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