[BACK]Return to tags.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

Diff for /src/usr.bin/less/tags.c between version 1.1.1.1 and 1.1.1.2

version 1.1.1.1, 1996/09/21 05:39:43 version 1.1.1.2, 2003/04/13 18:21:21
Line 1 
Line 1 
 /*  /*
  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman   * Copyright (C) 1984-2002  Mark Nudelman
  * All rights reserved.  
  *   *
  * Redistribution and use in source and binary forms, with or without   * You may distribute under the terms of either the GNU General Public
  * modification, are permitted provided that the following conditions   * License or the Less License, as specified in the README file.
  * are met:  
  * 1. Redistributions of source code must retain the above copyright  
  *    notice, this list of conditions and the following disclaimer.  
  * 2. Redistributions in binary form must reproduce the above copyright  
  *    notice in the documentation and/or other materials provided with  
  *    the distribution.  
  *   *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY   * For more information about less, or for information on how to
  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE   * contact the author, see the README file.
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  
  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE  
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR  
  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN  
  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
  */   */
   
   
Line 31 
Line 15 
   
 #if TAGS  #if TAGS
   
 public char *tagfile;  
 public char *tags = "tags";  public char *tags = "tags";
   
 static char *tagpattern;  static int total;
 static int taglinenum;  static int curseq;
   
 extern int linenums;  extern int linenums;
 extern int sigs;  extern int sigs;
 extern int jump_sline;  
   
   enum tag_result {
           TAG_FOUND,
           TAG_NOFILE,
           TAG_NOTAG,
           TAG_NOTYPE,
           TAG_INTR
   };
   
 /*  /*
    * Tag type
    */
   enum {
           T_CTAGS,        /* 'tags': standard and extended format (ctags) */
           T_CTAGS_X,      /* stdin: cross reference format (ctags) */
           T_GTAGS,        /* 'GTAGS': function defenition (global) */
           T_GRTAGS,       /* 'GRTAGS': function reference (global) */
           T_GSYMS,        /* 'GSYMS': other symbols (global) */
           T_GPATH         /* 'GPATH': path name (global) */
   };
   
   static enum tag_result findctag();
   static enum tag_result findgtag();
   static char *nextgtag();
   static char *prevgtag();
   static POSITION ctagsearch();
   static POSITION gtagsearch();
   static int getentry();
   
   /*
    * The list of tags generated by the last findgtag() call.
    *
    * Use either pattern or line number.
    * findgtag() always uses line number, so pattern is always NULL.
    * findctag() usually either pattern (in which case line number is 0),
    * or line number (in which case pattern is NULL).
    */
   struct taglist {
           struct tag *tl_first;
           struct tag *tl_last;
   };
   #define TAG_END  ((struct tag *) &taglist)
   static struct taglist taglist = { TAG_END, TAG_END };
   struct tag {
           struct tag *next, *prev; /* List links */
           char *tag_file;         /* Source file containing the tag */
           LINENUM tag_linenum;    /* Appropriate line number in source file */
           char *tag_pattern;      /* Pattern used to find the tag */
           char tag_endline;       /* True if the pattern includes '$' */
   };
   static struct tag *curtag;
   
   #define TAG_INS(tp) \
           (tp)->next = taglist.tl_first; \
           (tp)->prev = TAG_END; \
           taglist.tl_first->prev = (tp); \
           taglist.tl_first = (tp);
   
   #define TAG_RM(tp) \
           (tp)->next->prev = (tp)->prev; \
           (tp)->prev->next = (tp)->next;
   
   /*
    * Delete tag structures.
    */
           public void
   cleantags()
   {
           register struct tag *tp;
   
           /*
            * Delete any existing tag list.
            * {{ Ideally, we wouldn't do this until after we know that we
            *    can load some other tag information. }}
            */
           while ((tp = taglist.tl_first) != TAG_END)
           {
                   TAG_RM(tp);
                   free(tp);
           }
           curtag = NULL;
           total = curseq = 0;
   }
   
   /*
    * Create a new tag entry.
    */
           static struct tag *
   maketagent(name, file, linenum, pattern, endline)
           char *name;
           char *file;
           LINENUM linenum;
           char *pattern;
           int endline;
   {
           register struct tag *tp;
   
           tp = (struct tag *) ecalloc(sizeof(struct tag), 1);
           tp->tag_file = (char *) ecalloc(strlen(file) + 1, sizeof(char));
           strcpy(tp->tag_file, file);
           tp->tag_linenum = linenum;
           tp->tag_endline = endline;
           if (pattern == NULL)
                   tp->tag_pattern = NULL;
           else
           {
                   tp->tag_pattern = (char *) ecalloc(strlen(pattern) + 1, sizeof(char));
                   strcpy(tp->tag_pattern, pattern);
           }
           return (tp);
   }
   
   /*
    * Get tag mode.
    */
           public int
   gettagtype()
   {
           int f;
   
           if (strcmp(tags, "GTAGS") == 0)
                   return T_GTAGS;
           if (strcmp(tags, "GRTAGS") == 0)
                   return T_GRTAGS;
           if (strcmp(tags, "GSYMS") == 0)
                   return T_GSYMS;
           if (strcmp(tags, "GPATH") == 0)
                   return T_GPATH;
           if (strcmp(tags, "-") == 0)
                   return T_CTAGS_X;
           f = open(tags, OPEN_READ);
           if (f >= 0)
           {
                   close(f);
                   return T_CTAGS;
           }
           return T_GTAGS;
   }
   
   /*
    * Find tags in tag file.
  * Find a tag in the "tags" file.   * Find a tag in the "tags" file.
  * Sets "tagfile" to the name of the file containing the tag,   * Sets "tag_file" to the name of the file containing the tag,
  * and "tagpattern" to the search pattern which should be used   * and "tagpattern" to the search pattern which should be used
  * to find the tag.   * to find the tag.
  */   */
Line 51 
Line 172 
 findtag(tag)  findtag(tag)
         register char *tag;          register char *tag;
 {  {
           int type = gettagtype();
           enum tag_result result;
   
           if (type == T_CTAGS)
                   result = findctag(tag);
           else
                   result = findgtag(tag, type);
           switch (result)
           {
           case TAG_FOUND:
           case TAG_INTR:
                   break;
           case TAG_NOFILE:
                   error("No tags file", NULL_PARG);
                   break;
           case TAG_NOTAG:
                   error("No such tag in tags file", NULL_PARG);
                   break;
           case TAG_NOTYPE:
                   error("unknown tag type", NULL_PARG);
                   break;
           }
   }
   
   /*
    * Search for a tag.
    */
           public POSITION
   tagsearch()
   {
           if (curtag == NULL)
                   return (NULL_POSITION);  /* No gtags loaded! */
           if (curtag->tag_linenum != 0)
                   return gtagsearch();
           else
                   return ctagsearch();
   }
   
   /*
    * Go to the next tag.
    */
           public char *
   nexttag(n)
           int n;
   {
           char *tagfile = (char *) NULL;
   
           while (n-- > 0)
                   tagfile = nextgtag();
           return tagfile;
   }
   
   /*
    * Go to the previous tag.
    */
           public char *
   prevtag(n)
           int n;
   {
           char *tagfile = (char *) NULL;
   
           while (n-- > 0)
                   tagfile = prevgtag();
           return tagfile;
   }
   
   /*
    * Return the total number of tags.
    */
           public int
   ntags()
   {
           return total;
   }
   
   /*
    * Return the sequence number of current tag.
    */
           public int
   curr_tag()
   {
           return curseq;
   }
   
   /*****************************************************************************
    * ctags
    */
   
   /*
    * Find tags in the "tags" file.
    * Sets curtag to the first tag entry.
    */
           static enum tag_result
   findctag(tag)
           register char *tag;
   {
         char *p;          char *p;
         char *q;  
         register FILE *f;          register FILE *f;
         register int taglen;          register int taglen;
           LINENUM taglinenum;
           char *tagfile;
           char *tagpattern;
           int tagendline;
         int search_char;          int search_char;
         int err;          int err;
         static char tline[200];          char tline[TAGLINE_SIZE];
           struct tag *tp;
   
         if ((f = fopen(tags, "r")) == NULL)          p = shell_unquote(tags);
         {          f = fopen(p, "r");
                 error("No tags file", NULL_PARG);          free(p);
                 tagfile = NULL;          if (f == NULL)
                 return;                  return TAG_NOFILE;
         }  
   
           cleantags();
           total = 0;
         taglen = strlen(tag);          taglen = strlen(tag);
   
         /*          /*
Line 73 
Line 295 
          */           */
         while (fgets(tline, sizeof(tline), f) != NULL)          while (fgets(tline, sizeof(tline), f) != NULL)
         {          {
                   if (tline[0] == '!')
                           /* Skip header of extended format. */
                           continue;
                 if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))                  if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen]))
                         continue;                          continue;
   
Line 84 
Line 309 
                  * or a search pattern surrounded by a pair of delimiters.                   * or a search pattern surrounded by a pair of delimiters.
                  * Parse the line and extract these parts.                   * Parse the line and extract these parts.
                  */                   */
                 tagfile = tagpattern = NULL;                  tagpattern = NULL;
                 taglinenum = 0;  
   
                 /*                  /*
                  * Skip over the whitespace after the tag name.                   * Skip over the whitespace after the tag name.
Line 111 
Line 335 
                 /*                  /*
                  * First see if it is a line number.                   * First see if it is a line number.
                  */                   */
                   tagendline = 0;
                 taglinenum = getnum(&p, 0, &err);                  taglinenum = getnum(&p, 0, &err);
                 if (err)                  if (err)
                 {                  {
Line 124 
Line 349 
                         search_char = *p++;                          search_char = *p++;
                         if (*p == '^')                          if (*p == '^')
                                 p++;                                  p++;
                         tagpattern = q = p;                          tagpattern = p;
                         while (*p != search_char && *p != '\0')                          while (*p != search_char && *p != '\0')
                         {                          {
                                 if (*p == '\\')                                  if (*p == '\\')
                                         p++;                                          p++;
                                 *q++ = *p++;                                  p++;
                         }                          }
                         if (q[-1] == '$')                          tagendline = (p[-1] == '$');
                                 q--;                          if (tagendline)
                         *q = '\0';                                  p--;
                           *p = '\0';
                 }                  }
                   tp = maketagent(tag, tagfile, taglinenum, tagpattern, tagendline);
                 fclose(f);                  TAG_INS(tp);
                 return;                  total++;
         }          }
         fclose(f);          fclose(f);
         error("No such tag in tags file", NULL_PARG);          if (total == 0)
         tagfile = NULL;                  return TAG_NOTAG;
           curtag = taglist.tl_first;
           curseq = 1;
           return TAG_FOUND;
 }  }
   
 /*  /*
    * Edit current tagged file.
    */
           public int
   edit_tagfile()
   {
           if (curtag == NULL)
                   return (1);
           return (edit(curtag->tag_file));
   }
   
   /*
  * Search for a tag.   * Search for a tag.
  * This is a stripped-down version of search().   * This is a stripped-down version of search().
  * We don't use search() for several reasons:   * We don't use search() for several reasons:
Line 153 
Line 393 
  *      regcmp vs. re_comp) behave differently in the presence of   *      regcmp vs. re_comp) behave differently in the presence of
  *      parentheses (which are almost always found in a tag).   *      parentheses (which are almost always found in a tag).
  */   */
         public POSITION          static POSITION
 tagsearch()  ctagsearch()
 {  {
         POSITION pos, linepos;          POSITION pos, linepos;
         int linenum;          LINENUM linenum;
           int len;
         char *line;          char *line;
   
         /*  
          * If we have the line number of the tag instead of the pattern,  
          * just use find_pos.  
          */  
         if (taglinenum)  
                 return (find_pos(taglinenum));  
   
         pos = ch_zero();          pos = ch_zero();
         linenum = find_linenum(pos);          linenum = find_linenum(pos);
   
Line 209 
Line 443 
                  * Test the line to see if we have a match.                   * Test the line to see if we have a match.
                  * Use strncmp because the pattern may be                   * Use strncmp because the pattern may be
                  * truncated (in the tags file) if it is too long.                   * truncated (in the tags file) if it is too long.
                    * If tagendline is set, make sure we match all
                    * the way to end of line (no extra chars after the match).
                  */                   */
                 if (strncmp(tagpattern, line, strlen(tagpattern)) == 0)                  len = strlen(curtag->tag_pattern);
                   if (strncmp(curtag->tag_pattern, line, len) == 0 &&
                       (!curtag->tag_endline || line[len] == '\0' || line[len] == '\r'))
                   {
                           curtag->tag_linenum = find_linenum(linepos);
                         break;                          break;
                   }
         }          }
   
         return (linepos);          return (linepos);
 }  }
   
   /*******************************************************************************
    * gtags
    */
   
   /*
    * Find tags in the GLOBAL's tag file.
    * The findgtag() will try and load information about the requested tag.
    * It does this by calling "global -x tag" and storing the parsed output
    * for future use by gtagsearch().
    * Sets curtag to the first tag entry.
    */
           static enum tag_result
   findgtag(tag, type)
           char *tag;              /* tag to load */
           int type;               /* tags type */
   {
           char buf[256];
           FILE *fp;
           struct tag *tp;
   
           if (type != T_CTAGS_X && tag == NULL)
                   return TAG_NOFILE;
   
           cleantags();
           total = 0;
   
           /*
            * If type == T_CTAGS_X then read ctags's -x format from stdin
            * else execute global(1) and read from it.
            */
           if (type == T_CTAGS_X)
           {
                   fp = stdin;
                   /* Set tag default because we cannot read stdin again. */
                   tags = "tags";
           } else
           {
   #if !HAVE_POPEN
                   return TAG_NOFILE;
   #else
                   char command[512];
                   char *flag;
                   char *qtag;
                   char *cmd = lgetenv("LESSGLOBALTAGS");
   
                   if (cmd == NULL || *cmd == '\0')
                           return TAG_NOFILE;
                   /* Get suitable flag value for global(1). */
                   switch (type)
                   {
                   case T_GTAGS:
                           flag = "" ;
                           break;
                   case T_GRTAGS:
                           flag = "r";
                           break;
                   case T_GSYMS:
                           flag = "s";
                           break;
                   case T_GPATH:
                           flag = "P";
                           break;
                   default:
                           return TAG_NOTYPE;
                   }
   
                   /* Get our data from global(1). */
                   qtag = shell_quote(tag);
                   if (qtag == NULL)
                           qtag = tag;
                   sprintf(command, "%s -x%s %s", cmd, flag, qtag);
                   if (qtag != tag)
                           free(qtag);
                   fp = popen(command, "r");
   #endif
           }
           if (fp != NULL)
           {
                   while (fgets(buf, sizeof(buf), fp))
                   {
                           char *name, *file, *line;
   
                           if (sigs)
                           {
   #if HAVE_POPEN
                                   if (fp != stdin)
                                           pclose(fp);
   #endif
                                   return TAG_INTR;
                           }
                           if (buf[strlen(buf) - 1] == '\n')
                                   buf[strlen(buf) - 1] = 0;
                           else
                           {
                                   int c;
                                   do {
                                           c = fgetc(fp);
                                   } while (c != '\n' && c != EOF);
                           }
   
                           if (getentry(buf, &name, &file, &line))
                           {
                                   /*
                                    * Couldn't parse this line for some reason.
                                    * We'll just pretend it never happened.
                                    */
                                   break;
                           }
   
                           /* Make new entry and add to list. */
                           tp = maketagent(name, file, (LINENUM) atoi(line), NULL, 0);
                           TAG_INS(tp);
                           total++;
                   }
                   if (fp != stdin)
                   {
                           if (pclose(fp))
                           {
                                   curtag = NULL;
                                   total = curseq = 0;
                                   return TAG_NOFILE;
                           }
                   }
           }
   
           /* Check to see if we found anything. */
           tp = taglist.tl_first;
           if (tp == TAG_END)
                   return TAG_NOTAG;
           curtag = tp;
           curseq = 1;
           return TAG_FOUND;
   }
   
   static int circular = 0;        /* 1: circular tag structure */
   
   /*
    * Return the filename required for the next gtag in the queue that was setup
    * by findgtag().  The next call to gtagsearch() will try to position at the
    * appropriate tag.
    */
           static char *
   nextgtag()
   {
           struct tag *tp;
   
           if (curtag == NULL)
                   /* No tag loaded */
                   return NULL;
   
           tp = curtag->next;
           if (tp == TAG_END)
           {
                   if (!circular)
                           return NULL;
                   /* Wrapped around to the head of the queue */
                   curtag = taglist.tl_first;
                   curseq = 1;
           } else
           {
                   curtag = tp;
                   curseq++;
           }
           return (curtag->tag_file);
   }
   
   /*
    * Return the filename required for the previous gtag in the queue that was
    * setup by findgtat().  The next call to gtagsearch() will try to position
    * at the appropriate tag.
    */
           static char *
   prevgtag()
   {
           struct tag *tp;
   
           if (curtag == NULL)
                   /* No tag loaded */
                   return NULL;
   
           tp = curtag->prev;
           if (tp == TAG_END)
           {
                   if (!circular)
                           return NULL;
                   /* Wrapped around to the tail of the queue */
                   curtag = taglist.tl_last;
                   curseq = total;
           } else
           {
                   curtag = tp;
                   curseq--;
           }
           return (curtag->tag_file);
   }
   
   /*
    * Position the current file at at what is hopefully the tag that was chosen
    * using either findtag() or one of nextgtag() and prevgtag().  Returns -1
    * if it was unable to position at the tag, 0 if succesful.
    */
           static POSITION
   gtagsearch()
   {
           if (curtag == NULL)
                   return (NULL_POSITION);  /* No gtags loaded! */
           return (find_pos(curtag->tag_linenum));
   }
   
   /*
    * The getentry() parses both standard and extended ctags -x format.
    *
    * [standard format]
    * <tag>   <lineno>  <file>         <image>
    * +------------------------------------------------
    * |main     30      main.c         main(argc, argv)
    * |func     21      subr.c         func(arg)
    *
    * The following commands write this format.
    *      o Traditinal Ctags with -x option
    *      o Global with -x option
    *              See <http://www.gnu.org/software/global/global.html>
    *
    * [extended format]
    * <tag>   <type>  <lineno>   <file>        <image>
    * +----------------------------------------------------------
    * |main     function 30      main.c         main(argc, argv)
    * |func     function 21      subr.c         func(arg)
    *
    * The following commands write this format.
    *      o Exuberant Ctags with -x option
    *              See <http://ctags.sourceforge.net>
    *
    * Returns 0 on success, -1 on error.
    * The tag, file, and line will each be NUL-terminated pointers
    * into buf.
    */
   
   #ifndef isspace
   #define isspace(c)      ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' || (c) == '\f')
   #endif
   #ifndef isdigit
   #define isdigit(c)      ((c) >= '0' && (c <= '9'))
   #endif
   
           static int
   getentry(buf, tag, file, line)
           char *buf;      /* standard or extended ctags -x format data */
           char **tag;     /* name of the tag we actually found */
           char **file;    /* file in which to find this tag */
           char **line;    /* line number of file where this tag is found */
   {
           char *p = buf;
   
           for (*tag = p;  *p && !isspace(*p);  p++)       /* tag name */
                   ;
           if (*p == 0)
                   return (-1);
           *p++ = 0;
           for ( ;  *p && isspace(*p);  p++)               /* (skip blanks) */
                   ;
           if (*p == 0)
                   return (-1);
           /*
            * If the second part begin with other than digit,
            * it is assumed tag type. Skip it.
            */
           if (!isdigit(*p))
           {
                   for ( ;  *p && !isspace(*p);  p++)      /* (skip tag type) */
                           ;
                   for (;  *p && isspace(*p);  p++)        /* (skip blanks) */
                           ;
           }
           if (!isdigit(*p))
                   return (-1);
           *line = p;                                      /* line number */
           for (*line = p;  *p && !isspace(*p);  p++)
                   ;
           if (*p == 0)
                   return (-1);
           *p++ = 0;
           for ( ; *p && isspace(*p);  p++)                /* (skip blanks) */
                   ;
           if (*p == 0)
                   return (-1);
           *file = p;                                      /* file name */
           for (*file = p;  *p && !isspace(*p);  p++)
                   ;
           if (*p == 0)
                   return (-1);
           *p = 0;
   
           /* value check */
           if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0)
                   return (0);
           return (-1);
   }
   
 #endif  #endif

Legend:
Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.2