tags.c revision 128345
160786Sps/* 2128345Stjr * Copyright (C) 1984-2002 Mark Nudelman 360786Sps * 460786Sps * You may distribute under the terms of either the GNU General Public 560786Sps * License or the Less License, as specified in the README file. 660786Sps * 760786Sps * For more information about less, or for information on how to 860786Sps * contact the author, see the README file. 960786Sps */ 1060786Sps 1160786Sps 1260786Sps#include "less.h" 1360786Sps 1460786Sps#define WHITESP(c) ((c)==' ' || (c)=='\t') 1560786Sps 1660786Sps#if TAGS 1760786Sps 1860786Spspublic char *tags = "tags"; 1960786Sps 2089019Spsstatic int total; 2189019Spsstatic int curseq; 2260786Sps 2360786Spsextern int linenums; 2460786Spsextern int sigs; 2560786Sps 2689019Spsenum tag_result { 2789019Sps TAG_FOUND, 2889019Sps TAG_NOFILE, 2989019Sps TAG_NOTAG, 3089019Sps TAG_NOTYPE, 3189019Sps TAG_INTR 3289019Sps}; 3389019Sps 3460786Sps/* 3589019Sps * Tag type 3689019Sps */ 3789019Spsenum { 3889019Sps T_CTAGS, /* 'tags': standard and extended format (ctags) */ 3989019Sps T_CTAGS_X, /* stdin: cross reference format (ctags) */ 4089019Sps T_GTAGS, /* 'GTAGS': function defenition (global) */ 4189019Sps T_GRTAGS, /* 'GRTAGS': function reference (global) */ 4289019Sps T_GSYMS, /* 'GSYMS': other symbols (global) */ 4389019Sps T_GPATH /* 'GPATH': path name (global) */ 4489019Sps}; 4589019Sps 4689019Spsstatic enum tag_result findctag(); 4789019Spsstatic enum tag_result findgtag(); 4889019Spsstatic char *nextgtag(); 4989019Spsstatic char *prevgtag(); 5089019Spsstatic POSITION ctagsearch(); 5189019Spsstatic POSITION gtagsearch(); 5289019Spsstatic int getentry(); 5389019Sps 5489019Sps/* 5589019Sps * The list of tags generated by the last findgtag() call. 5689019Sps * 5789019Sps * Use either pattern or line number. 5889019Sps * findgtag() always uses line number, so pattern is always NULL. 5989019Sps * findctag() usually either pattern (in which case line number is 0), 6089019Sps * or line number (in which case pattern is NULL). 6189019Sps */ 6289019Spsstruct taglist { 6389019Sps struct tag *tl_first; 6489019Sps struct tag *tl_last; 6589019Sps}; 6689019Sps#define TAG_END ((struct tag *) &taglist) 6789019Spsstatic struct taglist taglist = { TAG_END, TAG_END }; 6889019Spsstruct tag { 6989019Sps struct tag *next, *prev; /* List links */ 7089019Sps char *tag_file; /* Source file containing the tag */ 71128345Stjr LINENUM tag_linenum; /* Appropriate line number in source file */ 7289019Sps char *tag_pattern; /* Pattern used to find the tag */ 7389019Sps char tag_endline; /* True if the pattern includes '$' */ 7489019Sps}; 7589019Spsstatic struct tag *curtag; 7689019Sps 7789019Sps#define TAG_INS(tp) \ 7889019Sps (tp)->next = taglist.tl_first; \ 7989019Sps (tp)->prev = TAG_END; \ 8089019Sps taglist.tl_first->prev = (tp); \ 8189019Sps taglist.tl_first = (tp); 8289019Sps 8389019Sps#define TAG_RM(tp) \ 8489019Sps (tp)->next->prev = (tp)->prev; \ 8589019Sps (tp)->prev->next = (tp)->next; 8689019Sps 8789019Sps/* 8889019Sps * Delete tag structures. 8989019Sps */ 9089019Sps public void 9189019Spscleantags() 9289019Sps{ 9389019Sps register struct tag *tp; 9489019Sps 9589019Sps /* 9689019Sps * Delete any existing tag list. 9789019Sps * {{ Ideally, we wouldn't do this until after we know that we 9889019Sps * can load some other tag information. }} 9989019Sps */ 10089019Sps while ((tp = taglist.tl_first) != TAG_END) 10189019Sps { 10289019Sps TAG_RM(tp); 10389019Sps free(tp); 10489019Sps } 10589019Sps curtag = NULL; 10689019Sps total = curseq = 0; 10789019Sps} 10889019Sps 10989019Sps/* 11089019Sps * Create a new tag entry. 11189019Sps */ 11289019Sps static struct tag * 11389019Spsmaketagent(name, file, linenum, pattern, endline) 11489019Sps char *name; 11589019Sps char *file; 116128345Stjr LINENUM linenum; 11789019Sps char *pattern; 11889019Sps int endline; 11989019Sps{ 12089019Sps register struct tag *tp; 12189019Sps 12289019Sps tp = (struct tag *) ecalloc(sizeof(struct tag), 1); 12389019Sps tp->tag_file = (char *) ecalloc(strlen(file) + 1, sizeof(char)); 12489019Sps strcpy(tp->tag_file, file); 12589019Sps tp->tag_linenum = linenum; 12689019Sps tp->tag_endline = endline; 12789019Sps if (pattern == NULL) 12889019Sps tp->tag_pattern = NULL; 12989019Sps else 13089019Sps { 13189019Sps tp->tag_pattern = (char *) ecalloc(strlen(pattern) + 1, sizeof(char)); 13289019Sps strcpy(tp->tag_pattern, pattern); 13389019Sps } 13489019Sps return (tp); 13589019Sps} 13689019Sps 13789019Sps/* 13889019Sps * Get tag mode. 13989019Sps */ 14089019Sps public int 14189019Spsgettagtype() 14289019Sps{ 14389019Sps int f; 14489019Sps 14589019Sps if (strcmp(tags, "GTAGS") == 0) 14689019Sps return T_GTAGS; 14789019Sps if (strcmp(tags, "GRTAGS") == 0) 14889019Sps return T_GRTAGS; 14989019Sps if (strcmp(tags, "GSYMS") == 0) 15089019Sps return T_GSYMS; 15189019Sps if (strcmp(tags, "GPATH") == 0) 15289019Sps return T_GPATH; 15389019Sps if (strcmp(tags, "-") == 0) 15489019Sps return T_CTAGS_X; 15589019Sps f = open(tags, OPEN_READ); 15689019Sps if (f >= 0) 15789019Sps { 15889019Sps close(f); 15989019Sps return T_CTAGS; 16089019Sps } 16189019Sps return T_GTAGS; 16289019Sps} 16389019Sps 16489019Sps/* 16589019Sps * Find tags in tag file. 16660786Sps * Find a tag in the "tags" file. 16789019Sps * Sets "tag_file" to the name of the file containing the tag, 16860786Sps * and "tagpattern" to the search pattern which should be used 16960786Sps * to find the tag. 17060786Sps */ 17160786Sps public void 17260786Spsfindtag(tag) 17360786Sps register char *tag; 17460786Sps{ 17589019Sps int type = gettagtype(); 17689019Sps enum tag_result result; 17789019Sps 17889019Sps if (type == T_CTAGS) 17989019Sps result = findctag(tag); 18089019Sps else 18189019Sps result = findgtag(tag, type); 18289019Sps switch (result) 18389019Sps { 18489019Sps case TAG_FOUND: 18589019Sps case TAG_INTR: 18689019Sps break; 18789019Sps case TAG_NOFILE: 18889019Sps error("No tags file", NULL_PARG); 18989019Sps break; 19089019Sps case TAG_NOTAG: 19189019Sps error("No such tag in tags file", NULL_PARG); 19289019Sps break; 19389019Sps case TAG_NOTYPE: 19489019Sps error("unknown tag type", NULL_PARG); 19589019Sps break; 19689019Sps } 19789019Sps} 19889019Sps 19989019Sps/* 20089019Sps * Search for a tag. 20189019Sps */ 20289019Sps public POSITION 20389019Spstagsearch() 20489019Sps{ 20589019Sps if (curtag == NULL) 20689019Sps return (NULL_POSITION); /* No gtags loaded! */ 20789019Sps if (curtag->tag_linenum != 0) 20889019Sps return gtagsearch(); 20989019Sps else 21089019Sps return ctagsearch(); 21189019Sps} 21289019Sps 21389019Sps/* 21489019Sps * Go to the next tag. 21589019Sps */ 21689019Sps public char * 21789019Spsnexttag(n) 21889019Sps int n; 21989019Sps{ 220128345Stjr char *tagfile = (char *) NULL; 22189019Sps 22289019Sps while (n-- > 0) 22389019Sps tagfile = nextgtag(); 22489019Sps return tagfile; 22589019Sps} 22689019Sps 22789019Sps/* 22889019Sps * Go to the previous tag. 22989019Sps */ 23089019Sps public char * 23189019Spsprevtag(n) 23289019Sps int n; 23389019Sps{ 234128345Stjr char *tagfile = (char *) NULL; 23589019Sps 23689019Sps while (n-- > 0) 23789019Sps tagfile = prevgtag(); 23889019Sps return tagfile; 23989019Sps} 24089019Sps 24189019Sps/* 24289019Sps * Return the total number of tags. 24389019Sps */ 24489019Sps public int 24589019Spsntags() 24689019Sps{ 24789019Sps return total; 24889019Sps} 24989019Sps 25089019Sps/* 25189019Sps * Return the sequence number of current tag. 25289019Sps */ 25389019Sps public int 25489019Spscurr_tag() 25589019Sps{ 25689019Sps return curseq; 25789019Sps} 25889019Sps 25989019Sps/***************************************************************************** 26089019Sps * ctags 26189019Sps */ 26289019Sps 26389019Sps/* 26489019Sps * Find tags in the "tags" file. 26589019Sps * Sets curtag to the first tag entry. 26689019Sps */ 26789019Sps static enum tag_result 26889019Spsfindctag(tag) 26989019Sps register char *tag; 27089019Sps{ 27160786Sps char *p; 27260786Sps register FILE *f; 27360786Sps register int taglen; 274128345Stjr LINENUM taglinenum; 27589019Sps char *tagfile; 27689019Sps char *tagpattern; 27789019Sps int tagendline; 27860786Sps int search_char; 27960786Sps int err; 28060786Sps char tline[TAGLINE_SIZE]; 28189019Sps struct tag *tp; 28260786Sps 283128345Stjr p = shell_unquote(tags); 28460786Sps f = fopen(p, "r"); 28560786Sps free(p); 28660786Sps if (f == NULL) 28789019Sps return TAG_NOFILE; 28860786Sps 28989019Sps cleantags(); 29089019Sps total = 0; 29160786Sps taglen = strlen(tag); 29260786Sps 29360786Sps /* 29460786Sps * Search the tags file for the desired tag. 29560786Sps */ 29660786Sps while (fgets(tline, sizeof(tline), f) != NULL) 29760786Sps { 29889019Sps if (tline[0] == '!') 29989019Sps /* Skip header of extended format. */ 30089019Sps continue; 30160786Sps if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen])) 30260786Sps continue; 30360786Sps 30460786Sps /* 30560786Sps * Found it. 30660786Sps * The line contains the tag, the filename and the 30760786Sps * location in the file, separated by white space. 30860786Sps * The location is either a decimal line number, 30960786Sps * or a search pattern surrounded by a pair of delimiters. 31060786Sps * Parse the line and extract these parts. 31160786Sps */ 31289019Sps tagpattern = NULL; 31360786Sps 31460786Sps /* 31560786Sps * Skip over the whitespace after the tag name. 31660786Sps */ 31760786Sps p = skipsp(tline+taglen); 31860786Sps if (*p == '\0') 31960786Sps /* File name is missing! */ 32060786Sps continue; 32160786Sps 32260786Sps /* 32360786Sps * Save the file name. 32460786Sps * Skip over the whitespace after the file name. 32560786Sps */ 32660786Sps tagfile = p; 32760786Sps while (!WHITESP(*p) && *p != '\0') 32860786Sps p++; 32960786Sps *p++ = '\0'; 33060786Sps p = skipsp(p); 33160786Sps if (*p == '\0') 33260786Sps /* Pattern is missing! */ 33360786Sps continue; 33460786Sps 33560786Sps /* 33660786Sps * First see if it is a line number. 33760786Sps */ 338128345Stjr tagendline = 0; 33960786Sps taglinenum = getnum(&p, 0, &err); 34060786Sps if (err) 34160786Sps { 34260786Sps /* 34360786Sps * No, it must be a pattern. 34460786Sps * Delete the initial "^" (if present) and 34560786Sps * the final "$" from the pattern. 34660786Sps * Delete any backslash in the pattern. 34760786Sps */ 34860786Sps taglinenum = 0; 34960786Sps search_char = *p++; 35060786Sps if (*p == '^') 35160786Sps p++; 35289019Sps tagpattern = p; 35360786Sps while (*p != search_char && *p != '\0') 35460786Sps { 35560786Sps if (*p == '\\') 35660786Sps p++; 35789019Sps p++; 35860786Sps } 35989019Sps tagendline = (p[-1] == '$'); 36060786Sps if (tagendline) 36189019Sps p--; 36289019Sps *p = '\0'; 36360786Sps } 36489019Sps tp = maketagent(tag, tagfile, taglinenum, tagpattern, tagendline); 36589019Sps TAG_INS(tp); 36689019Sps total++; 36760786Sps } 36860786Sps fclose(f); 36989019Sps if (total == 0) 37089019Sps return TAG_NOTAG; 37189019Sps curtag = taglist.tl_first; 37289019Sps curseq = 1; 37389019Sps return TAG_FOUND; 37460786Sps} 37560786Sps 37689019Sps/* 37789019Sps * Edit current tagged file. 37889019Sps */ 37960786Sps public int 38060786Spsedit_tagfile() 38160786Sps{ 38289019Sps if (curtag == NULL) 38360786Sps return (1); 38489019Sps return (edit(curtag->tag_file)); 38560786Sps} 38660786Sps 38760786Sps/* 38860786Sps * Search for a tag. 38960786Sps * This is a stripped-down version of search(). 39060786Sps * We don't use search() for several reasons: 39160786Sps * - We don't want to blow away any search string we may have saved. 39260786Sps * - The various regular-expression functions (from different systems: 39360786Sps * regcmp vs. re_comp) behave differently in the presence of 39460786Sps * parentheses (which are almost always found in a tag). 39560786Sps */ 39689019Sps static POSITION 39789019Spsctagsearch() 39860786Sps{ 39960786Sps POSITION pos, linepos; 400128345Stjr LINENUM linenum; 40160786Sps int len; 40260786Sps char *line; 40360786Sps 40460786Sps pos = ch_zero(); 40560786Sps linenum = find_linenum(pos); 40660786Sps 40760786Sps for (;;) 40860786Sps { 40960786Sps /* 41060786Sps * Get lines until we find a matching one or 41160786Sps * until we hit end-of-file. 41260786Sps */ 41360786Sps if (ABORT_SIGS()) 41460786Sps return (NULL_POSITION); 41560786Sps 41660786Sps /* 41760786Sps * Read the next line, and save the 41860786Sps * starting position of that line in linepos. 41960786Sps */ 42060786Sps linepos = pos; 42160786Sps pos = forw_raw_line(pos, &line); 42260786Sps if (linenum != 0) 42360786Sps linenum++; 42460786Sps 42560786Sps if (pos == NULL_POSITION) 42660786Sps { 42760786Sps /* 42860786Sps * We hit EOF without a match. 42960786Sps */ 43060786Sps error("Tag not found", NULL_PARG); 43160786Sps return (NULL_POSITION); 43260786Sps } 43360786Sps 43460786Sps /* 43560786Sps * If we're using line numbers, we might as well 43660786Sps * remember the information we have now (the position 43760786Sps * and line number of the current line). 43860786Sps */ 43960786Sps if (linenums) 44060786Sps add_lnum(linenum, pos); 44160786Sps 44260786Sps /* 44360786Sps * Test the line to see if we have a match. 44460786Sps * Use strncmp because the pattern may be 44560786Sps * truncated (in the tags file) if it is too long. 44660786Sps * If tagendline is set, make sure we match all 44760786Sps * the way to end of line (no extra chars after the match). 44860786Sps */ 44989019Sps len = strlen(curtag->tag_pattern); 45089019Sps if (strncmp(curtag->tag_pattern, line, len) == 0 && 45189019Sps (!curtag->tag_endline || line[len] == '\0' || line[len] == '\r')) 45289019Sps { 45389019Sps curtag->tag_linenum = find_linenum(linepos); 45460786Sps break; 45589019Sps } 45660786Sps } 45760786Sps 45860786Sps return (linepos); 45960786Sps} 46060786Sps 46189019Sps/******************************************************************************* 46289019Sps * gtags 46389019Sps */ 46489019Sps 46589019Sps/* 46689019Sps * Find tags in the GLOBAL's tag file. 46789019Sps * The findgtag() will try and load information about the requested tag. 46889019Sps * It does this by calling "global -x tag" and storing the parsed output 46989019Sps * for future use by gtagsearch(). 47089019Sps * Sets curtag to the first tag entry. 47189019Sps */ 47289019Sps static enum tag_result 47389019Spsfindgtag(tag, type) 47489019Sps char *tag; /* tag to load */ 47589019Sps int type; /* tags type */ 47689019Sps{ 47789019Sps char buf[256]; 47889019Sps FILE *fp; 47989019Sps struct tag *tp; 48089019Sps 48189019Sps if (type != T_CTAGS_X && tag == NULL) 48289019Sps return TAG_NOFILE; 48389019Sps 48489019Sps cleantags(); 48589019Sps total = 0; 48689019Sps 48789019Sps /* 48889019Sps * If type == T_CTAGS_X then read ctags's -x format from stdin 48989019Sps * else execute global(1) and read from it. 49089019Sps */ 49189019Sps if (type == T_CTAGS_X) 49289019Sps { 49389019Sps fp = stdin; 49489019Sps /* Set tag default because we cannot read stdin again. */ 49589019Sps tags = "tags"; 49689019Sps } else 49789019Sps { 49889019Sps#if !HAVE_POPEN 49989019Sps return TAG_NOFILE; 50089019Sps#else 50189019Sps char command[512]; 50289019Sps char *flag; 503128345Stjr char *qtag; 50489019Sps char *cmd = lgetenv("LESSGLOBALTAGS"); 50589019Sps 50689019Sps if (cmd == NULL || *cmd == '\0') 50789019Sps return TAG_NOFILE; 50889019Sps /* Get suitable flag value for global(1). */ 50989019Sps switch (type) 51089019Sps { 51189019Sps case T_GTAGS: 51289019Sps flag = "" ; 51389019Sps break; 51489019Sps case T_GRTAGS: 51589019Sps flag = "r"; 51689019Sps break; 51789019Sps case T_GSYMS: 51889019Sps flag = "s"; 51989019Sps break; 52089019Sps case T_GPATH: 52189019Sps flag = "P"; 52289019Sps break; 52389019Sps default: 52489019Sps return TAG_NOTYPE; 52589019Sps } 52689019Sps 52789019Sps /* Get our data from global(1). */ 528128345Stjr qtag = shell_quote(tag); 529128345Stjr if (qtag == NULL) 530128345Stjr qtag = tag; 531128345Stjr sprintf(command, "%s -x%s %s", cmd, flag, qtag); 532128345Stjr if (qtag != tag) 533128345Stjr free(qtag); 53489019Sps fp = popen(command, "r"); 53560786Sps#endif 53689019Sps } 53789019Sps if (fp != NULL) 53889019Sps { 53989019Sps while (fgets(buf, sizeof(buf), fp)) 54089019Sps { 54189019Sps char *name, *file, *line; 54289019Sps 54389019Sps if (sigs) 54489019Sps { 54589019Sps#if HAVE_POPEN 54689019Sps if (fp != stdin) 54789019Sps pclose(fp); 54889019Sps#endif 54989019Sps return TAG_INTR; 55089019Sps } 55189019Sps if (buf[strlen(buf) - 1] == '\n') 55289019Sps buf[strlen(buf) - 1] = 0; 55389019Sps else 55489019Sps { 55589019Sps int c; 55689019Sps do { 55789019Sps c = fgetc(fp); 55889019Sps } while (c != '\n' && c != EOF); 55989019Sps } 56089019Sps 56189019Sps if (getentry(buf, &name, &file, &line)) 56289019Sps { 56389019Sps /* 56489019Sps * Couldn't parse this line for some reason. 56589019Sps * We'll just pretend it never happened. 56689019Sps */ 56789019Sps break; 56889019Sps } 56989019Sps 57089019Sps /* Make new entry and add to list. */ 571128345Stjr tp = maketagent(name, file, (LINENUM) atoi(line), NULL, 0); 57289019Sps TAG_INS(tp); 57389019Sps total++; 57489019Sps } 57589019Sps if (fp != stdin) 57689019Sps { 57789019Sps if (pclose(fp)) 57889019Sps { 57989019Sps curtag = NULL; 58089019Sps total = curseq = 0; 58189019Sps return TAG_NOFILE; 58289019Sps } 58389019Sps } 58489019Sps } 58589019Sps 58689019Sps /* Check to see if we found anything. */ 58789019Sps tp = taglist.tl_first; 58889019Sps if (tp == TAG_END) 58989019Sps return TAG_NOTAG; 59089019Sps curtag = tp; 59189019Sps curseq = 1; 59289019Sps return TAG_FOUND; 59389019Sps} 59489019Sps 59589019Spsstatic int circular = 0; /* 1: circular tag structure */ 59689019Sps 59789019Sps/* 59889019Sps * Return the filename required for the next gtag in the queue that was setup 59989019Sps * by findgtag(). The next call to gtagsearch() will try to position at the 60089019Sps * appropriate tag. 60189019Sps */ 60289019Sps static char * 60389019Spsnextgtag() 60489019Sps{ 60589019Sps struct tag *tp; 60689019Sps 60789019Sps if (curtag == NULL) 60889019Sps /* No tag loaded */ 60989019Sps return NULL; 61089019Sps 61189019Sps tp = curtag->next; 61289019Sps if (tp == TAG_END) 61389019Sps { 61489019Sps if (!circular) 61589019Sps return NULL; 61689019Sps /* Wrapped around to the head of the queue */ 61789019Sps curtag = taglist.tl_first; 61889019Sps curseq = 1; 61989019Sps } else 62089019Sps { 62189019Sps curtag = tp; 62289019Sps curseq++; 62389019Sps } 62489019Sps return (curtag->tag_file); 62589019Sps} 62689019Sps 62789019Sps/* 62889019Sps * Return the filename required for the previous gtag in the queue that was 62989019Sps * setup by findgtat(). The next call to gtagsearch() will try to position 63089019Sps * at the appropriate tag. 63189019Sps */ 63289019Sps static char * 63389019Spsprevgtag() 63489019Sps{ 63589019Sps struct tag *tp; 63689019Sps 63789019Sps if (curtag == NULL) 63889019Sps /* No tag loaded */ 63989019Sps return NULL; 64089019Sps 64189019Sps tp = curtag->prev; 64289019Sps if (tp == TAG_END) 64389019Sps { 64489019Sps if (!circular) 64589019Sps return NULL; 64689019Sps /* Wrapped around to the tail of the queue */ 64789019Sps curtag = taglist.tl_last; 64889019Sps curseq = total; 64989019Sps } else 65089019Sps { 65189019Sps curtag = tp; 65289019Sps curseq--; 65389019Sps } 65489019Sps return (curtag->tag_file); 65589019Sps} 65689019Sps 65789019Sps/* 65889019Sps * Position the current file at at what is hopefully the tag that was chosen 65989019Sps * using either findtag() or one of nextgtag() and prevgtag(). Returns -1 66089019Sps * if it was unable to position at the tag, 0 if succesful. 66189019Sps */ 66289019Sps static POSITION 66389019Spsgtagsearch() 66489019Sps{ 66589019Sps if (curtag == NULL) 66689019Sps return (NULL_POSITION); /* No gtags loaded! */ 66789019Sps return (find_pos(curtag->tag_linenum)); 66889019Sps} 66989019Sps 67089019Sps/* 67189019Sps * The getentry() parses both standard and extended ctags -x format. 67289019Sps * 67389019Sps * [standard format] 67489019Sps * <tag> <lineno> <file> <image> 67589019Sps * +------------------------------------------------ 67689019Sps * |main 30 main.c main(argc, argv) 67789019Sps * |func 21 subr.c func(arg) 67889019Sps * 67989019Sps * The following commands write this format. 68089019Sps * o Traditinal Ctags with -x option 68189019Sps * o Global with -x option 68289019Sps * See <http://www.gnu.org/software/global/global.html> 68389019Sps * 68489019Sps * [extended format] 68589019Sps * <tag> <type> <lineno> <file> <image> 68689019Sps * +---------------------------------------------------------- 68789019Sps * |main function 30 main.c main(argc, argv) 68889019Sps * |func function 21 subr.c func(arg) 68989019Sps * 69089019Sps * The following commands write this format. 69189019Sps * o Exuberant Ctags with -x option 69289019Sps * See <http://ctags.sourceforge.net> 69389019Sps * 69489019Sps * Returns 0 on success, -1 on error. 69589019Sps * The tag, file, and line will each be NUL-terminated pointers 69689019Sps * into buf. 69789019Sps */ 69889019Sps 69989019Sps#ifndef isspace 70089019Sps#define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' || (c) == '\f') 70189019Sps#endif 70289019Sps#ifndef isdigit 70389019Sps#define isdigit(c) ((c) >= '0' && (c <= '9')) 70489019Sps#endif 70589019Sps 70689019Sps static int 70789019Spsgetentry(buf, tag, file, line) 70889019Sps char *buf; /* standard or extended ctags -x format data */ 70989019Sps char **tag; /* name of the tag we actually found */ 71089019Sps char **file; /* file in which to find this tag */ 71189019Sps char **line; /* line number of file where this tag is found */ 71289019Sps{ 71389019Sps char *p = buf; 71489019Sps 71589019Sps for (*tag = p; *p && !isspace(*p); p++) /* tag name */ 71689019Sps ; 71789019Sps if (*p == 0) 71889019Sps return (-1); 71989019Sps *p++ = 0; 72089019Sps for ( ; *p && isspace(*p); p++) /* (skip blanks) */ 72189019Sps ; 72289019Sps if (*p == 0) 72389019Sps return (-1); 72489019Sps /* 72589019Sps * If the second part begin with other than digit, 72689019Sps * it is assumed tag type. Skip it. 72789019Sps */ 72889019Sps if (!isdigit(*p)) 72989019Sps { 73089019Sps for ( ; *p && !isspace(*p); p++) /* (skip tag type) */ 73189019Sps ; 73289019Sps for (; *p && isspace(*p); p++) /* (skip blanks) */ 73389019Sps ; 73489019Sps } 73589019Sps if (!isdigit(*p)) 73689019Sps return (-1); 73789019Sps *line = p; /* line number */ 73889019Sps for (*line = p; *p && !isspace(*p); p++) 73989019Sps ; 74089019Sps if (*p == 0) 74189019Sps return (-1); 74289019Sps *p++ = 0; 74389019Sps for ( ; *p && isspace(*p); p++) /* (skip blanks) */ 74489019Sps ; 74589019Sps if (*p == 0) 74689019Sps return (-1); 74789019Sps *file = p; /* file name */ 74889019Sps for (*file = p; *p && !isspace(*p); p++) 74989019Sps ; 75089019Sps if (*p == 0) 75189019Sps return (-1); 75289019Sps *p = 0; 75389019Sps 75489019Sps /* value check */ 75589019Sps if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0) 75689019Sps return (0); 75789019Sps return (-1); 75889019Sps} 75989019Sps 76089019Sps#endif 761