1238730Sdelphij/* 2330571Sdelphij * Copyright (C) 1984-2017 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 17294286Sdelphijpublic char ztags[] = "tags"; 18294286Sdelphijpublic char *tags = ztags; 1960786Sps 2089019Spsstatic int total; 2189019Spsstatic int curseq; 2260786Sps 2360786Spsextern int linenums; 2460786Spsextern int sigs; 25330571Sdelphijextern int ctldisp; 2660786Sps 2789019Spsenum tag_result { 2889019Sps TAG_FOUND, 2989019Sps TAG_NOFILE, 3089019Sps TAG_NOTAG, 3189019Sps TAG_NOTYPE, 3289019Sps TAG_INTR 3389019Sps}; 3489019Sps 3560786Sps/* 3689019Sps * Tag type 3789019Sps */ 3889019Spsenum { 3989019Sps T_CTAGS, /* 'tags': standard and extended format (ctags) */ 4089019Sps T_CTAGS_X, /* stdin: cross reference format (ctags) */ 4189019Sps T_GTAGS, /* 'GTAGS': function defenition (global) */ 4289019Sps T_GRTAGS, /* 'GRTAGS': function reference (global) */ 4389019Sps T_GSYMS, /* 'GSYMS': other symbols (global) */ 4489019Sps T_GPATH /* 'GPATH': path name (global) */ 4589019Sps}; 4689019Sps 4789019Spsstatic enum tag_result findctag(); 4889019Spsstatic enum tag_result findgtag(); 4989019Spsstatic char *nextgtag(); 5089019Spsstatic char *prevgtag(); 5189019Spsstatic POSITION ctagsearch(); 5289019Spsstatic POSITION gtagsearch(); 5389019Spsstatic int getentry(); 5489019Sps 5589019Sps/* 5689019Sps * The list of tags generated by the last findgtag() call. 5789019Sps * 5889019Sps * Use either pattern or line number. 5989019Sps * findgtag() always uses line number, so pattern is always NULL. 60170256Sdelphij * findctag() uses either pattern (in which case line number is 0), 6189019Sps * or line number (in which case pattern is NULL). 6289019Sps */ 6389019Spsstruct taglist { 6489019Sps struct tag *tl_first; 6589019Sps struct tag *tl_last; 6689019Sps}; 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}; 74330571Sdelphij#define TAG_END ((struct tag *) &taglist) 75330571Sdelphijstatic struct taglist taglist = { TAG_END, TAG_END }; 7689019Spsstatic struct tag *curtag; 7789019Sps 7889019Sps#define TAG_INS(tp) \ 79170256Sdelphij (tp)->next = TAG_END; \ 80170256Sdelphij (tp)->prev = taglist.tl_last; \ 81170256Sdelphij taglist.tl_last->next = (tp); \ 82170256Sdelphij taglist.tl_last = (tp); 8389019Sps 8489019Sps#define TAG_RM(tp) \ 8589019Sps (tp)->next->prev = (tp)->prev; \ 8689019Sps (tp)->prev->next = (tp)->next; 8789019Sps 8889019Sps/* 8989019Sps * Delete tag structures. 9089019Sps */ 9189019Sps public void 9289019Spscleantags() 9389019Sps{ 94330571Sdelphij struct tag *tp; 9589019Sps 9689019Sps /* 9789019Sps * Delete any existing tag list. 9889019Sps * {{ Ideally, we wouldn't do this until after we know that we 9989019Sps * can load some other tag information. }} 10089019Sps */ 10189019Sps while ((tp = taglist.tl_first) != TAG_END) 10289019Sps { 10389019Sps TAG_RM(tp); 104330571Sdelphij free(tp->tag_file); 105330571Sdelphij free(tp->tag_pattern); 10689019Sps free(tp); 10789019Sps } 10889019Sps curtag = NULL; 10989019Sps total = curseq = 0; 11089019Sps} 11189019Sps 11289019Sps/* 11389019Sps * Create a new tag entry. 11489019Sps */ 11589019Sps static struct tag * 11689019Spsmaketagent(name, file, linenum, pattern, endline) 11789019Sps char *name; 11889019Sps char *file; 119128345Stjr LINENUM linenum; 12089019Sps char *pattern; 12189019Sps int endline; 12289019Sps{ 123330571Sdelphij struct tag *tp; 12489019Sps 12589019Sps tp = (struct tag *) ecalloc(sizeof(struct tag), 1); 12689019Sps tp->tag_file = (char *) ecalloc(strlen(file) + 1, sizeof(char)); 12789019Sps strcpy(tp->tag_file, file); 12889019Sps tp->tag_linenum = linenum; 12989019Sps tp->tag_endline = endline; 13089019Sps if (pattern == NULL) 13189019Sps tp->tag_pattern = NULL; 13289019Sps else 13389019Sps { 13489019Sps tp->tag_pattern = (char *) ecalloc(strlen(pattern) + 1, sizeof(char)); 13589019Sps strcpy(tp->tag_pattern, pattern); 13689019Sps } 13789019Sps return (tp); 13889019Sps} 13989019Sps 14089019Sps/* 14189019Sps * Get tag mode. 14289019Sps */ 14389019Sps public int 14489019Spsgettagtype() 14589019Sps{ 14689019Sps int f; 14789019Sps 14889019Sps if (strcmp(tags, "GTAGS") == 0) 14989019Sps return T_GTAGS; 15089019Sps if (strcmp(tags, "GRTAGS") == 0) 15189019Sps return T_GRTAGS; 15289019Sps if (strcmp(tags, "GSYMS") == 0) 15389019Sps return T_GSYMS; 15489019Sps if (strcmp(tags, "GPATH") == 0) 15589019Sps return T_GPATH; 15689019Sps if (strcmp(tags, "-") == 0) 15789019Sps return T_CTAGS_X; 15889019Sps f = open(tags, OPEN_READ); 15989019Sps if (f >= 0) 16089019Sps { 16189019Sps close(f); 16289019Sps return T_CTAGS; 16389019Sps } 16489019Sps return T_GTAGS; 16589019Sps} 16689019Sps 16789019Sps/* 16889019Sps * Find tags in tag file. 16960786Sps * Find a tag in the "tags" file. 17089019Sps * Sets "tag_file" to the name of the file containing the tag, 17160786Sps * and "tagpattern" to the search pattern which should be used 17260786Sps * to find the tag. 17360786Sps */ 17460786Sps public void 17560786Spsfindtag(tag) 176330571Sdelphij char *tag; 17760786Sps{ 17889019Sps int type = gettagtype(); 17989019Sps enum tag_result result; 18089019Sps 18189019Sps if (type == T_CTAGS) 18289019Sps result = findctag(tag); 18389019Sps else 18489019Sps result = findgtag(tag, type); 18589019Sps switch (result) 18689019Sps { 18789019Sps case TAG_FOUND: 18889019Sps case TAG_INTR: 18989019Sps break; 19089019Sps case TAG_NOFILE: 19189019Sps error("No tags file", NULL_PARG); 19289019Sps break; 19389019Sps case TAG_NOTAG: 19489019Sps error("No such tag in tags file", NULL_PARG); 19589019Sps break; 19689019Sps case TAG_NOTYPE: 19789019Sps error("unknown tag type", NULL_PARG); 19889019Sps break; 19989019Sps } 20089019Sps} 20189019Sps 20289019Sps/* 20389019Sps * Search for a tag. 20489019Sps */ 20589019Sps public POSITION 20689019Spstagsearch() 20789019Sps{ 20889019Sps if (curtag == NULL) 20989019Sps return (NULL_POSITION); /* No gtags loaded! */ 21089019Sps if (curtag->tag_linenum != 0) 21189019Sps return gtagsearch(); 21289019Sps else 21389019Sps return ctagsearch(); 21489019Sps} 21589019Sps 21689019Sps/* 21789019Sps * Go to the next tag. 21889019Sps */ 21989019Sps public char * 22089019Spsnexttag(n) 22189019Sps int n; 22289019Sps{ 223128345Stjr char *tagfile = (char *) NULL; 22489019Sps 22589019Sps while (n-- > 0) 22689019Sps tagfile = nextgtag(); 22789019Sps return tagfile; 22889019Sps} 22989019Sps 23089019Sps/* 23189019Sps * Go to the previous tag. 23289019Sps */ 23389019Sps public char * 23489019Spsprevtag(n) 23589019Sps int n; 23689019Sps{ 237128345Stjr char *tagfile = (char *) NULL; 23889019Sps 23989019Sps while (n-- > 0) 24089019Sps tagfile = prevgtag(); 24189019Sps return tagfile; 24289019Sps} 24389019Sps 24489019Sps/* 24589019Sps * Return the total number of tags. 24689019Sps */ 24789019Sps public int 24889019Spsntags() 24989019Sps{ 25089019Sps return total; 25189019Sps} 25289019Sps 25389019Sps/* 25489019Sps * Return the sequence number of current tag. 25589019Sps */ 25689019Sps public int 25789019Spscurr_tag() 25889019Sps{ 25989019Sps return curseq; 26089019Sps} 26189019Sps 26289019Sps/***************************************************************************** 26389019Sps * ctags 26489019Sps */ 26589019Sps 26689019Sps/* 26789019Sps * Find tags in the "tags" file. 26889019Sps * Sets curtag to the first tag entry. 26989019Sps */ 27089019Sps static enum tag_result 27189019Spsfindctag(tag) 272330571Sdelphij char *tag; 27389019Sps{ 27460786Sps char *p; 275330571Sdelphij FILE *f; 276330571Sdelphij int taglen; 277128345Stjr LINENUM taglinenum; 27889019Sps char *tagfile; 27989019Sps char *tagpattern; 28089019Sps int tagendline; 28160786Sps int search_char; 28260786Sps int err; 28360786Sps char tline[TAGLINE_SIZE]; 28489019Sps struct tag *tp; 28560786Sps 286128345Stjr p = shell_unquote(tags); 28760786Sps f = fopen(p, "r"); 28860786Sps free(p); 28960786Sps if (f == NULL) 29089019Sps return TAG_NOFILE; 29160786Sps 29289019Sps cleantags(); 29389019Sps total = 0; 294294286Sdelphij taglen = (int) strlen(tag); 29560786Sps 29660786Sps /* 29760786Sps * Search the tags file for the desired tag. 29860786Sps */ 29960786Sps while (fgets(tline, sizeof(tline), f) != NULL) 30060786Sps { 30189019Sps if (tline[0] == '!') 30289019Sps /* Skip header of extended format. */ 30389019Sps continue; 30460786Sps if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen])) 30560786Sps continue; 30660786Sps 30760786Sps /* 30860786Sps * Found it. 30960786Sps * The line contains the tag, the filename and the 31060786Sps * location in the file, separated by white space. 31160786Sps * The location is either a decimal line number, 31260786Sps * or a search pattern surrounded by a pair of delimiters. 31360786Sps * Parse the line and extract these parts. 31460786Sps */ 31589019Sps tagpattern = NULL; 31660786Sps 31760786Sps /* 31860786Sps * Skip over the whitespace after the tag name. 31960786Sps */ 32060786Sps p = skipsp(tline+taglen); 32160786Sps if (*p == '\0') 32260786Sps /* File name is missing! */ 32360786Sps continue; 32460786Sps 32560786Sps /* 32660786Sps * Save the file name. 32760786Sps * Skip over the whitespace after the file name. 32860786Sps */ 32960786Sps tagfile = p; 33060786Sps while (!WHITESP(*p) && *p != '\0') 33160786Sps p++; 33260786Sps *p++ = '\0'; 33360786Sps p = skipsp(p); 33460786Sps if (*p == '\0') 33560786Sps /* Pattern is missing! */ 33660786Sps continue; 33760786Sps 33860786Sps /* 33960786Sps * First see if it is a line number. 34060786Sps */ 341128345Stjr tagendline = 0; 34260786Sps taglinenum = getnum(&p, 0, &err); 34360786Sps if (err) 34460786Sps { 34560786Sps /* 34660786Sps * No, it must be a pattern. 34760786Sps * Delete the initial "^" (if present) and 34860786Sps * the final "$" from the pattern. 34960786Sps * Delete any backslash in the pattern. 35060786Sps */ 35160786Sps taglinenum = 0; 35260786Sps search_char = *p++; 35360786Sps if (*p == '^') 35460786Sps p++; 35589019Sps tagpattern = p; 35660786Sps while (*p != search_char && *p != '\0') 35760786Sps { 35860786Sps if (*p == '\\') 35960786Sps p++; 36089019Sps p++; 36160786Sps } 36289019Sps tagendline = (p[-1] == '$'); 36360786Sps if (tagendline) 36489019Sps p--; 36589019Sps *p = '\0'; 36660786Sps } 36789019Sps tp = maketagent(tag, tagfile, taglinenum, tagpattern, tagendline); 36889019Sps TAG_INS(tp); 36989019Sps total++; 37060786Sps } 37160786Sps fclose(f); 37289019Sps if (total == 0) 37389019Sps return TAG_NOTAG; 37489019Sps curtag = taglist.tl_first; 37589019Sps curseq = 1; 37689019Sps return TAG_FOUND; 37760786Sps} 37860786Sps 37989019Sps/* 38089019Sps * Edit current tagged file. 38189019Sps */ 38260786Sps public int 38360786Spsedit_tagfile() 38460786Sps{ 38589019Sps if (curtag == NULL) 38660786Sps return (1); 38789019Sps return (edit(curtag->tag_file)); 38860786Sps} 38960786Sps 390330571Sdelphij static int 391330571Sdelphijcurtag_match(char const *line, POSITION linepos) 392330571Sdelphij{ 393330571Sdelphij /* 394330571Sdelphij * Test the line to see if we have a match. 395330571Sdelphij * Use strncmp because the pattern may be 396330571Sdelphij * truncated (in the tags file) if it is too long. 397330571Sdelphij * If tagendline is set, make sure we match all 398330571Sdelphij * the way to end of line (no extra chars after the match). 399330571Sdelphij */ 400330571Sdelphij int len = (int) strlen(curtag->tag_pattern); 401330571Sdelphij if (strncmp(curtag->tag_pattern, line, len) == 0 && 402330571Sdelphij (!curtag->tag_endline || line[len] == '\0' || line[len] == '\r')) 403330571Sdelphij { 404330571Sdelphij curtag->tag_linenum = find_linenum(linepos); 405330571Sdelphij return 1; 406330571Sdelphij } 407330571Sdelphij return 0; 408330571Sdelphij} 409330571Sdelphij 41060786Sps/* 41160786Sps * Search for a tag. 41260786Sps * This is a stripped-down version of search(). 41360786Sps * We don't use search() for several reasons: 41460786Sps * - We don't want to blow away any search string we may have saved. 41560786Sps * - The various regular-expression functions (from different systems: 41660786Sps * regcmp vs. re_comp) behave differently in the presence of 41760786Sps * parentheses (which are almost always found in a tag). 41860786Sps */ 41989019Sps static POSITION 42089019Spsctagsearch() 42160786Sps{ 42260786Sps POSITION pos, linepos; 423128345Stjr LINENUM linenum; 424330571Sdelphij int line_len; 42560786Sps char *line; 426330571Sdelphij int found; 42760786Sps 42860786Sps pos = ch_zero(); 42960786Sps linenum = find_linenum(pos); 43060786Sps 431330571Sdelphij for (found = 0; !found;) 43260786Sps { 43360786Sps /* 43460786Sps * Get lines until we find a matching one or 43560786Sps * until we hit end-of-file. 43660786Sps */ 43760786Sps if (ABORT_SIGS()) 43860786Sps return (NULL_POSITION); 43960786Sps 44060786Sps /* 44160786Sps * Read the next line, and save the 44260786Sps * starting position of that line in linepos. 44360786Sps */ 44460786Sps linepos = pos; 445330571Sdelphij pos = forw_raw_line(pos, &line, &line_len); 44660786Sps if (linenum != 0) 44760786Sps linenum++; 44860786Sps 44960786Sps if (pos == NULL_POSITION) 45060786Sps { 45160786Sps /* 45260786Sps * We hit EOF without a match. 45360786Sps */ 45460786Sps error("Tag not found", NULL_PARG); 45560786Sps return (NULL_POSITION); 45660786Sps } 45760786Sps 45860786Sps /* 45960786Sps * If we're using line numbers, we might as well 46060786Sps * remember the information we have now (the position 46160786Sps * and line number of the current line). 46260786Sps */ 46360786Sps if (linenums) 46460786Sps add_lnum(linenum, pos); 46560786Sps 466330571Sdelphij if (ctldisp != OPT_ONPLUS) 46789019Sps { 468330571Sdelphij if (curtag_match(line, linepos)) 469330571Sdelphij found = 1; 470330571Sdelphij } else 471330571Sdelphij { 472330571Sdelphij int cvt_ops = CVT_ANSI; 473330571Sdelphij int cvt_len = cvt_length(line_len, cvt_ops); 474330571Sdelphij int *chpos = cvt_alloc_chpos(cvt_len); 475330571Sdelphij char *cline = (char *) ecalloc(1, cvt_len); 476330571Sdelphij cvt_text(cline, line, chpos, &line_len, cvt_ops); 477330571Sdelphij if (curtag_match(cline, linepos)) 478330571Sdelphij found = 1; 479330571Sdelphij free(chpos); 480330571Sdelphij free(cline); 48189019Sps } 48260786Sps } 48360786Sps 48460786Sps return (linepos); 48560786Sps} 48660786Sps 48789019Sps/******************************************************************************* 48889019Sps * gtags 48989019Sps */ 49089019Sps 49189019Sps/* 49289019Sps * Find tags in the GLOBAL's tag file. 49389019Sps * The findgtag() will try and load information about the requested tag. 49489019Sps * It does this by calling "global -x tag" and storing the parsed output 49589019Sps * for future use by gtagsearch(). 49689019Sps * Sets curtag to the first tag entry. 49789019Sps */ 49889019Sps static enum tag_result 49989019Spsfindgtag(tag, type) 50089019Sps char *tag; /* tag to load */ 50189019Sps int type; /* tags type */ 50289019Sps{ 50389019Sps char buf[256]; 50489019Sps FILE *fp; 50589019Sps struct tag *tp; 50689019Sps 50789019Sps if (type != T_CTAGS_X && tag == NULL) 50889019Sps return TAG_NOFILE; 50989019Sps 51089019Sps cleantags(); 51189019Sps total = 0; 51289019Sps 51389019Sps /* 51489019Sps * If type == T_CTAGS_X then read ctags's -x format from stdin 51589019Sps * else execute global(1) and read from it. 51689019Sps */ 51789019Sps if (type == T_CTAGS_X) 51889019Sps { 51989019Sps fp = stdin; 52089019Sps /* Set tag default because we cannot read stdin again. */ 521294286Sdelphij tags = ztags; 52289019Sps } else 52389019Sps { 52489019Sps#if !HAVE_POPEN 52589019Sps return TAG_NOFILE; 52689019Sps#else 527161475Sdelphij char *command; 52889019Sps char *flag; 529128345Stjr char *qtag; 53089019Sps char *cmd = lgetenv("LESSGLOBALTAGS"); 53189019Sps 53289019Sps if (cmd == NULL || *cmd == '\0') 53389019Sps return TAG_NOFILE; 53489019Sps /* Get suitable flag value for global(1). */ 53589019Sps switch (type) 53689019Sps { 53789019Sps case T_GTAGS: 53889019Sps flag = "" ; 53989019Sps break; 54089019Sps case T_GRTAGS: 54189019Sps flag = "r"; 54289019Sps break; 54389019Sps case T_GSYMS: 54489019Sps flag = "s"; 54589019Sps break; 54689019Sps case T_GPATH: 54789019Sps flag = "P"; 54889019Sps break; 54989019Sps default: 55089019Sps return TAG_NOTYPE; 55189019Sps } 55289019Sps 55389019Sps /* Get our data from global(1). */ 554128345Stjr qtag = shell_quote(tag); 555128345Stjr if (qtag == NULL) 556128345Stjr qtag = tag; 557161475Sdelphij command = (char *) ecalloc(strlen(cmd) + strlen(flag) + 558161475Sdelphij strlen(qtag) + 5, sizeof(char)); 559128345Stjr sprintf(command, "%s -x%s %s", cmd, flag, qtag); 560128345Stjr if (qtag != tag) 561128345Stjr free(qtag); 56289019Sps fp = popen(command, "r"); 563161475Sdelphij free(command); 56460786Sps#endif 56589019Sps } 56689019Sps if (fp != NULL) 56789019Sps { 56889019Sps while (fgets(buf, sizeof(buf), fp)) 56989019Sps { 57089019Sps char *name, *file, *line; 571161475Sdelphij int len; 57289019Sps 57389019Sps if (sigs) 57489019Sps { 57589019Sps#if HAVE_POPEN 57689019Sps if (fp != stdin) 57789019Sps pclose(fp); 57889019Sps#endif 57989019Sps return TAG_INTR; 58089019Sps } 581294286Sdelphij len = (int) strlen(buf); 582161475Sdelphij if (len > 0 && buf[len-1] == '\n') 583161475Sdelphij buf[len-1] = '\0'; 58489019Sps else 58589019Sps { 58689019Sps int c; 58789019Sps do { 58889019Sps c = fgetc(fp); 58989019Sps } while (c != '\n' && c != EOF); 59089019Sps } 59189019Sps 59289019Sps if (getentry(buf, &name, &file, &line)) 59389019Sps { 59489019Sps /* 59589019Sps * Couldn't parse this line for some reason. 59689019Sps * We'll just pretend it never happened. 59789019Sps */ 59889019Sps break; 59989019Sps } 60089019Sps 60189019Sps /* Make new entry and add to list. */ 602128345Stjr tp = maketagent(name, file, (LINENUM) atoi(line), NULL, 0); 60389019Sps TAG_INS(tp); 60489019Sps total++; 60589019Sps } 60689019Sps if (fp != stdin) 60789019Sps { 60889019Sps if (pclose(fp)) 60989019Sps { 61089019Sps curtag = NULL; 61189019Sps total = curseq = 0; 61289019Sps return TAG_NOFILE; 61389019Sps } 61489019Sps } 61589019Sps } 61689019Sps 61789019Sps /* Check to see if we found anything. */ 61889019Sps tp = taglist.tl_first; 61989019Sps if (tp == TAG_END) 62089019Sps return TAG_NOTAG; 62189019Sps curtag = tp; 62289019Sps curseq = 1; 62389019Sps return TAG_FOUND; 62489019Sps} 62589019Sps 62689019Spsstatic int circular = 0; /* 1: circular tag structure */ 62789019Sps 62889019Sps/* 62989019Sps * Return the filename required for the next gtag in the queue that was setup 63089019Sps * by findgtag(). The next call to gtagsearch() will try to position at the 63189019Sps * appropriate tag. 63289019Sps */ 63389019Sps static char * 63489019Spsnextgtag() 63589019Sps{ 63689019Sps struct tag *tp; 63789019Sps 63889019Sps if (curtag == NULL) 63989019Sps /* No tag loaded */ 64089019Sps return NULL; 64189019Sps 64289019Sps tp = curtag->next; 64389019Sps if (tp == TAG_END) 64489019Sps { 64589019Sps if (!circular) 64689019Sps return NULL; 64789019Sps /* Wrapped around to the head of the queue */ 64889019Sps curtag = taglist.tl_first; 64989019Sps curseq = 1; 65089019Sps } else 65189019Sps { 65289019Sps curtag = tp; 65389019Sps curseq++; 65489019Sps } 65589019Sps return (curtag->tag_file); 65689019Sps} 65789019Sps 65889019Sps/* 65989019Sps * Return the filename required for the previous gtag in the queue that was 66089019Sps * setup by findgtat(). The next call to gtagsearch() will try to position 66189019Sps * at the appropriate tag. 66289019Sps */ 66389019Sps static char * 66489019Spsprevgtag() 66589019Sps{ 66689019Sps struct tag *tp; 66789019Sps 66889019Sps if (curtag == NULL) 66989019Sps /* No tag loaded */ 67089019Sps return NULL; 67189019Sps 67289019Sps tp = curtag->prev; 67389019Sps if (tp == TAG_END) 67489019Sps { 67589019Sps if (!circular) 67689019Sps return NULL; 67789019Sps /* Wrapped around to the tail of the queue */ 67889019Sps curtag = taglist.tl_last; 67989019Sps curseq = total; 68089019Sps } else 68189019Sps { 68289019Sps curtag = tp; 68389019Sps curseq--; 68489019Sps } 68589019Sps return (curtag->tag_file); 68689019Sps} 68789019Sps 68889019Sps/* 68989019Sps * Position the current file at at what is hopefully the tag that was chosen 69089019Sps * using either findtag() or one of nextgtag() and prevgtag(). Returns -1 691173682Sdelphij * if it was unable to position at the tag, 0 if successful. 69289019Sps */ 69389019Sps static POSITION 69489019Spsgtagsearch() 69589019Sps{ 69689019Sps if (curtag == NULL) 69789019Sps return (NULL_POSITION); /* No gtags loaded! */ 69889019Sps return (find_pos(curtag->tag_linenum)); 69989019Sps} 70089019Sps 70189019Sps/* 70289019Sps * The getentry() parses both standard and extended ctags -x format. 70389019Sps * 70489019Sps * [standard format] 70589019Sps * <tag> <lineno> <file> <image> 70689019Sps * +------------------------------------------------ 70789019Sps * |main 30 main.c main(argc, argv) 70889019Sps * |func 21 subr.c func(arg) 70989019Sps * 71089019Sps * The following commands write this format. 71189019Sps * o Traditinal Ctags with -x option 71289019Sps * o Global with -x option 71389019Sps * See <http://www.gnu.org/software/global/global.html> 71489019Sps * 71589019Sps * [extended format] 71689019Sps * <tag> <type> <lineno> <file> <image> 71789019Sps * +---------------------------------------------------------- 71889019Sps * |main function 30 main.c main(argc, argv) 71989019Sps * |func function 21 subr.c func(arg) 72089019Sps * 72189019Sps * The following commands write this format. 72289019Sps * o Exuberant Ctags with -x option 72389019Sps * See <http://ctags.sourceforge.net> 72489019Sps * 72589019Sps * Returns 0 on success, -1 on error. 72689019Sps * The tag, file, and line will each be NUL-terminated pointers 72789019Sps * into buf. 72889019Sps */ 72989019Sps static int 73089019Spsgetentry(buf, tag, file, line) 73189019Sps char *buf; /* standard or extended ctags -x format data */ 73289019Sps char **tag; /* name of the tag we actually found */ 73389019Sps char **file; /* file in which to find this tag */ 73489019Sps char **line; /* line number of file where this tag is found */ 73589019Sps{ 73689019Sps char *p = buf; 73789019Sps 738161475Sdelphij for (*tag = p; *p && !IS_SPACE(*p); p++) /* tag name */ 73989019Sps ; 74089019Sps if (*p == 0) 74189019Sps return (-1); 74289019Sps *p++ = 0; 743161475Sdelphij for ( ; *p && IS_SPACE(*p); p++) /* (skip blanks) */ 74489019Sps ; 74589019Sps if (*p == 0) 74689019Sps return (-1); 74789019Sps /* 74889019Sps * If the second part begin with other than digit, 74989019Sps * it is assumed tag type. Skip it. 75089019Sps */ 751161475Sdelphij if (!IS_DIGIT(*p)) 75289019Sps { 753161475Sdelphij for ( ; *p && !IS_SPACE(*p); p++) /* (skip tag type) */ 75489019Sps ; 755161475Sdelphij for (; *p && IS_SPACE(*p); p++) /* (skip blanks) */ 75689019Sps ; 75789019Sps } 758161475Sdelphij if (!IS_DIGIT(*p)) 75989019Sps return (-1); 76089019Sps *line = p; /* line number */ 761161475Sdelphij for (*line = p; *p && !IS_SPACE(*p); p++) 76289019Sps ; 76389019Sps if (*p == 0) 76489019Sps return (-1); 76589019Sps *p++ = 0; 766161475Sdelphij for ( ; *p && IS_SPACE(*p); p++) /* (skip blanks) */ 76789019Sps ; 76889019Sps if (*p == 0) 76989019Sps return (-1); 77089019Sps *file = p; /* file name */ 771161475Sdelphij for (*file = p; *p && !IS_SPACE(*p); p++) 77289019Sps ; 77389019Sps if (*p == 0) 77489019Sps return (-1); 77589019Sps *p = 0; 77689019Sps 77789019Sps /* value check */ 77889019Sps if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0) 77989019Sps return (0); 78089019Sps return (-1); 78189019Sps} 78289019Sps 78389019Sps#endif 784