tags.c revision 195941
1266423Sjfv/* 2266423Sjfv * Copyright (C) 1984-2009 Mark Nudelman 3279033Sjfv * 4266423Sjfv * You may distribute under the terms of either the GNU General Public 5266423Sjfv * License or the Less License, as specified in the README file. 6266423Sjfv * 7266423Sjfv * For more information about less, or for information on how to 8266423Sjfv * contact the author, see the README file. 9266423Sjfv */ 10266423Sjfv 11266423Sjfv 12266423Sjfv#include "less.h" 13266423Sjfv 14266423Sjfv#define WHITESP(c) ((c)==' ' || (c)=='\t') 15266423Sjfv 16266423Sjfv#if TAGS 17266423Sjfv 18266423Sjfvpublic char *tags = "tags"; 19266423Sjfv 20266423Sjfvstatic int total; 21266423Sjfvstatic int curseq; 22266423Sjfv 23266423Sjfvextern int linenums; 24266423Sjfvextern int sigs; 25266423Sjfv 26266423Sjfvenum tag_result { 27266423Sjfv TAG_FOUND, 28266423Sjfv TAG_NOFILE, 29266423Sjfv TAG_NOTAG, 30266423Sjfv TAG_NOTYPE, 31266423Sjfv TAG_INTR 32266423Sjfv}; 33266423Sjfv 34266423Sjfv/* 35266423Sjfv * Tag type 36266423Sjfv */ 37266423Sjfvenum { 38266423Sjfv T_CTAGS, /* 'tags': standard and extended format (ctags) */ 39266423Sjfv T_CTAGS_X, /* stdin: cross reference format (ctags) */ 40279033Sjfv T_GTAGS, /* 'GTAGS': function defenition (global) */ 41266423Sjfv T_GRTAGS, /* 'GRTAGS': function reference (global) */ 42266423Sjfv T_GSYMS, /* 'GSYMS': other symbols (global) */ 43266423Sjfv T_GPATH /* 'GPATH': path name (global) */ 44266423Sjfv}; 45266423Sjfv 46266423Sjfvstatic enum tag_result findctag(); 47266423Sjfvstatic enum tag_result findgtag(); 48270346Sjfvstatic char *nextgtag(); 49266423Sjfvstatic char *prevgtag(); 50266423Sjfvstatic POSITION ctagsearch(); 51266423Sjfvstatic POSITION gtagsearch(); 52266423Sjfvstatic int getentry(); 53266423Sjfv 54266423Sjfv/* 55266423Sjfv * The list of tags generated by the last findgtag() call. 56266423Sjfv * 57266423Sjfv * Use either pattern or line number. 58266423Sjfv * findgtag() always uses line number, so pattern is always NULL. 59266423Sjfv * findctag() uses either pattern (in which case line number is 0), 60266423Sjfv * or line number (in which case pattern is NULL). 61266423Sjfv */ 62266423Sjfvstruct taglist { 63270346Sjfv struct tag *tl_first; 64284049Sjfv struct tag *tl_last; 65279033Sjfv}; 66284049Sjfv#define TAG_END ((struct tag *) &taglist) 67303967Ssbrunostatic struct taglist taglist = { TAG_END, TAG_END }; 68303967Ssbrunostruct tag { 69266423Sjfv struct tag *next, *prev; /* List links */ 70266423Sjfv char *tag_file; /* Source file containing the tag */ 71303967Ssbruno LINENUM tag_linenum; /* Appropriate line number in source file */ 72303967Ssbruno char *tag_pattern; /* Pattern used to find the tag */ 73303967Ssbruno char tag_endline; /* True if the pattern includes '$' */ 74303967Ssbruno}; 75303967Ssbrunostatic struct tag *curtag; 76303967Ssbruno 77303967Ssbruno#define TAG_INS(tp) \ 78303967Ssbruno (tp)->next = TAG_END; \ 79303967Ssbruno (tp)->prev = taglist.tl_last; \ 80303967Ssbruno taglist.tl_last->next = (tp); \ 81303967Ssbruno taglist.tl_last = (tp); 82303967Ssbruno 83303967Ssbruno#define TAG_RM(tp) \ 84303967Ssbruno (tp)->next->prev = (tp)->prev; \ 85266423Sjfv (tp)->prev->next = (tp)->next; 86266423Sjfv 87266423Sjfv/* 88266423Sjfv * Delete tag structures. 89266423Sjfv */ 90266423Sjfv public void 91266423Sjfvcleantags() 92266423Sjfv{ 93266423Sjfv register struct tag *tp; 94266423Sjfv 95266423Sjfv /* 96266423Sjfv * Delete any existing tag list. 97266423Sjfv * {{ Ideally, we wouldn't do this until after we know that we 98266423Sjfv * can load some other tag information. }} 99266423Sjfv */ 100266423Sjfv while ((tp = taglist.tl_first) != TAG_END) 101266423Sjfv { 102266423Sjfv TAG_RM(tp); 103284049Sjfv free(tp); 104284049Sjfv } 105284049Sjfv curtag = NULL; 106284049Sjfv total = curseq = 0; 107299545Serj} 108284049Sjfv 109284049Sjfv/* 110284049Sjfv * Create a new tag entry. 111284049Sjfv */ 112284049Sjfv static struct tag * 113284049Sjfvmaketagent(name, file, linenum, pattern, endline) 114284049Sjfv char *name; 115284049Sjfv char *file; 116284049Sjfv LINENUM linenum; 117284049Sjfv char *pattern; 118284049Sjfv int endline; 119284049Sjfv{ 120284049Sjfv register struct tag *tp; 121284049Sjfv 122284049Sjfv tp = (struct tag *) ecalloc(sizeof(struct tag), 1); 123284049Sjfv tp->tag_file = (char *) ecalloc(strlen(file) + 1, sizeof(char)); 124284049Sjfv strcpy(tp->tag_file, file); 125284049Sjfv tp->tag_linenum = linenum; 126284049Sjfv tp->tag_endline = endline; 127284049Sjfv if (pattern == NULL) 128284049Sjfv tp->tag_pattern = NULL; 129284049Sjfv else 130284049Sjfv { 131284049Sjfv tp->tag_pattern = (char *) ecalloc(strlen(pattern) + 1, sizeof(char)); 132284049Sjfv strcpy(tp->tag_pattern, pattern); 133284049Sjfv } 134284049Sjfv return (tp); 135284049Sjfv} 136284049Sjfv 137284049Sjfv/* 138284049Sjfv * Get tag mode. 139284049Sjfv */ 140284049Sjfv public int 141284049Sjfvgettagtype() 142284049Sjfv{ 143284049Sjfv int f; 144284049Sjfv 145284049Sjfv if (strcmp(tags, "GTAGS") == 0) 146284049Sjfv return T_GTAGS; 147284049Sjfv if (strcmp(tags, "GRTAGS") == 0) 148284049Sjfv return T_GRTAGS; 149284049Sjfv if (strcmp(tags, "GSYMS") == 0) 150284049Sjfv return T_GSYMS; 151284049Sjfv if (strcmp(tags, "GPATH") == 0) 152284049Sjfv return T_GPATH; 153284049Sjfv if (strcmp(tags, "-") == 0) 154284049Sjfv return T_CTAGS_X; 155284049Sjfv f = open(tags, OPEN_READ); 156284049Sjfv if (f >= 0) 157284049Sjfv { 158284049Sjfv close(f); 159284049Sjfv return T_CTAGS; 160284049Sjfv } 161284049Sjfv return T_GTAGS; 162284049Sjfv} 163284049Sjfv 164284049Sjfv/* 165284049Sjfv * Find tags in tag file. 166284049Sjfv * Find a tag in the "tags" file. 167299545Serj * Sets "tag_file" to the name of the file containing the tag, 168284049Sjfv * and "tagpattern" to the search pattern which should be used 169284049Sjfv * to find the tag. 170284049Sjfv */ 171284049Sjfv public void 172284049Sjfvfindtag(tag) 173284049Sjfv register char *tag; 174284049Sjfv{ 175284049Sjfv int type = gettagtype(); 176284049Sjfv enum tag_result result; 177284049Sjfv 178284049Sjfv if (type == T_CTAGS) 179284049Sjfv result = findctag(tag); 180284049Sjfv else 181284049Sjfv result = findgtag(tag, type); 182284049Sjfv switch (result) 183284049Sjfv { 184284049Sjfv case TAG_FOUND: 185284049Sjfv case TAG_INTR: 186284049Sjfv break; 187284049Sjfv case TAG_NOFILE: 188284049Sjfv error("No tags file", NULL_PARG); 189284049Sjfv break; 190284049Sjfv case TAG_NOTAG: 191284049Sjfv error("No such tag in tags file", NULL_PARG); 192284049Sjfv break; 193284049Sjfv case TAG_NOTYPE: 194284049Sjfv error("unknown tag type", NULL_PARG); 195284049Sjfv break; 196284049Sjfv } 197284049Sjfv} 198284049Sjfv 199284049Sjfv/* 200284049Sjfv * Search for a tag. 201284049Sjfv */ 202284049Sjfv public POSITION 203284049Sjfvtagsearch() 204284049Sjfv{ 205284049Sjfv if (curtag == NULL) 206284049Sjfv return (NULL_POSITION); /* No gtags loaded! */ 207284049Sjfv if (curtag->tag_linenum != 0) 208284049Sjfv return gtagsearch(); 209284049Sjfv else 210284049Sjfv return ctagsearch(); 211284049Sjfv} 212284049Sjfv 213284049Sjfv/* 214284049Sjfv * Go to the next tag. 215284049Sjfv */ 216284049Sjfv public char * 217284049Sjfvnexttag(n) 218284049Sjfv int n; 219284049Sjfv{ 220284049Sjfv char *tagfile = (char *) NULL; 221284049Sjfv 222284049Sjfv while (n-- > 0) 223284049Sjfv tagfile = nextgtag(); 224284049Sjfv return tagfile; 225284049Sjfv} 226284049Sjfv 227284049Sjfv/* 228284049Sjfv * Go to the previous tag. 229284049Sjfv */ 230284049Sjfv public char * 231284049Sjfvprevtag(n) 232284049Sjfv int n; 233284049Sjfv{ 234284049Sjfv char *tagfile = (char *) NULL; 235284049Sjfv 236284049Sjfv while (n-- > 0) 237284049Sjfv tagfile = prevgtag(); 238284049Sjfv return tagfile; 239284049Sjfv} 240284049Sjfv 241284049Sjfv/* 242284049Sjfv * Return the total number of tags. 243284049Sjfv */ 244284049Sjfv public int 245284049Sjfvntags() 246284049Sjfv{ 247284049Sjfv return total; 248284049Sjfv} 249284049Sjfv 250284049Sjfv/* 251284049Sjfv * Return the sequence number of current tag. 252284049Sjfv */ 253284049Sjfv public int 254284049Sjfvcurr_tag() 255284049Sjfv{ 256284049Sjfv return curseq; 257284049Sjfv} 258284049Sjfv 259284049Sjfv/***************************************************************************** 260284049Sjfv * ctags 261284049Sjfv */ 262284049Sjfv 263284049Sjfv/* 264284049Sjfv * Find tags in the "tags" file. 265284049Sjfv * Sets curtag to the first tag entry. 266284049Sjfv */ 267284049Sjfv static enum tag_result 268284049Sjfvfindctag(tag) 269284049Sjfv register char *tag; 270284049Sjfv{ 271284049Sjfv char *p; 272284049Sjfv register FILE *f; 273284049Sjfv register int taglen; 274284049Sjfv LINENUM taglinenum; 275284049Sjfv char *tagfile; 276284049Sjfv char *tagpattern; 277284049Sjfv int tagendline; 278284049Sjfv int search_char; 279284049Sjfv int err; 280284049Sjfv char tline[TAGLINE_SIZE]; 281284049Sjfv struct tag *tp; 282284049Sjfv 283284049Sjfv p = shell_unquote(tags); 284284049Sjfv f = fopen(p, "r"); 285284049Sjfv free(p); 286284049Sjfv if (f == NULL) 287284049Sjfv return TAG_NOFILE; 288284049Sjfv 289284049Sjfv cleantags(); 290284049Sjfv total = 0; 291284049Sjfv taglen = strlen(tag); 292284049Sjfv 293284049Sjfv /* 294284049Sjfv * Search the tags file for the desired tag. 295284049Sjfv */ 296284049Sjfv while (fgets(tline, sizeof(tline), f) != NULL) 297284049Sjfv { 298284049Sjfv if (tline[0] == '!') 299284049Sjfv /* Skip header of extended format. */ 300284049Sjfv continue; 301284049Sjfv if (strncmp(tag, tline, taglen) != 0 || !WHITESP(tline[taglen])) 302284049Sjfv continue; 303284049Sjfv 304284049Sjfv /* 305284049Sjfv * Found it. 306284049Sjfv * The line contains the tag, the filename and the 307284049Sjfv * location in the file, separated by white space. 308284049Sjfv * The location is either a decimal line number, 309266423Sjfv * or a search pattern surrounded by a pair of delimiters. 310266423Sjfv * Parse the line and extract these parts. 311266423Sjfv */ 312266423Sjfv tagpattern = NULL; 313266423Sjfv 314269198Sjfv /* 315266423Sjfv * Skip over the whitespace after the tag name. 316266423Sjfv */ 317266423Sjfv p = skipsp(tline+taglen); 318266423Sjfv if (*p == '\0') 319269198Sjfv /* File name is missing! */ 320266423Sjfv continue; 321266423Sjfv 322269198Sjfv /* 323277082Sjfv * Save the file name. 324277082Sjfv * Skip over the whitespace after the file name. 325266423Sjfv */ 326266423Sjfv tagfile = p; 327266423Sjfv while (!WHITESP(*p) && *p != '\0') 328266423Sjfv p++; 329266423Sjfv *p++ = '\0'; 330266423Sjfv p = skipsp(p); 331277082Sjfv if (*p == '\0') 332277082Sjfv /* Pattern is missing! */ 333277082Sjfv continue; 334277082Sjfv 335266423Sjfv /* 336277082Sjfv * First see if it is a line number. 337277082Sjfv */ 338266423Sjfv tagendline = 0; 339277082Sjfv taglinenum = getnum(&p, 0, &err); 340277082Sjfv if (err) 341266423Sjfv { 342277082Sjfv /* 343277082Sjfv * No, it must be a pattern. 344266423Sjfv * Delete the initial "^" (if present) and 345266423Sjfv * the final "$" from the pattern. 346266423Sjfv * Delete any backslash in the pattern. 347269198Sjfv */ 348269198Sjfv taglinenum = 0; 349277082Sjfv search_char = *p++; 350277082Sjfv if (*p == '^') 351277082Sjfv p++; 352277082Sjfv tagpattern = p; 353277082Sjfv while (*p != search_char && *p != '\0') 354277082Sjfv { 355277082Sjfv if (*p == '\\') 356277082Sjfv p++; 357299546Serj p++; 358277082Sjfv } 359299546Serj tagendline = (p[-1] == '$'); 360303967Ssbruno if (tagendline) 361277082Sjfv p--; 362303967Ssbruno *p = '\0'; 363277082Sjfv } 364299546Serj tp = maketagent(tag, tagfile, taglinenum, tagpattern, tagendline); 365299546Serj TAG_INS(tp); 366299546Serj total++; 367299546Serj } 368303967Ssbruno fclose(f); 369299546Serj if (total == 0) 370299546Serj return TAG_NOTAG; 371299546Serj curtag = taglist.tl_first; 372266423Sjfv curseq = 1; 373266423Sjfv return TAG_FOUND; 374266423Sjfv} 375266423Sjfv 376266423Sjfv/* 377266423Sjfv * Edit current tagged file. 378266423Sjfv */ 379266423Sjfv public int 380266423Sjfvedit_tagfile() 381266423Sjfv{ 382266423Sjfv if (curtag == NULL) 383266423Sjfv return (1); 384266423Sjfv return (edit(curtag->tag_file)); 385284049Sjfv} 386284049Sjfv 387284049Sjfv/* 388284049Sjfv * Search for a tag. 389284049Sjfv * This is a stripped-down version of search(). 390284049Sjfv * We don't use search() for several reasons: 391284049Sjfv * - We don't want to blow away any search string we may have saved. 392266423Sjfv * - The various regular-expression functions (from different systems: 393266423Sjfv * regcmp vs. re_comp) behave differently in the presence of 394266423Sjfv * parentheses (which are almost always found in a tag). 395266423Sjfv */ 396266423Sjfv static POSITION 397266423Sjfvctagsearch() 398266423Sjfv{ 399266423Sjfv POSITION pos, linepos; 400266423Sjfv LINENUM linenum; 401266423Sjfv int len; 402266423Sjfv char *line; 403266423Sjfv 404266423Sjfv pos = ch_zero(); 405266423Sjfv linenum = find_linenum(pos); 406266423Sjfv 407266423Sjfv for (;;) 408266423Sjfv { 409266423Sjfv /* 410266423Sjfv * Get lines until we find a matching one or 411266423Sjfv * until we hit end-of-file. 412266423Sjfv */ 413266423Sjfv if (ABORT_SIGS()) 414266423Sjfv return (NULL_POSITION); 415266423Sjfv 416266423Sjfv /* 417266423Sjfv * Read the next line, and save the 418266423Sjfv * starting position of that line in linepos. 419266423Sjfv */ 420303967Ssbruno linepos = pos; 421303967Ssbruno pos = forw_raw_line(pos, &line, (int *)NULL); 422303967Ssbruno if (linenum != 0) 423303967Ssbruno linenum++; 424303967Ssbruno 425303967Ssbruno if (pos == NULL_POSITION) 426303967Ssbruno { 427303967Ssbruno /* 428303967Ssbruno * We hit EOF without a match. 429303967Ssbruno */ 430303967Ssbruno error("Tag not found", NULL_PARG); 431303967Ssbruno return (NULL_POSITION); 432303967Ssbruno } 433303967Ssbruno 434303967Ssbruno /* 435303967Ssbruno * If we're using line numbers, we might as well 436303967Ssbruno * remember the information we have now (the position 437303967Ssbruno * and line number of the current line). 438303967Ssbruno */ 439303967Ssbruno if (linenums) 440303967Ssbruno add_lnum(linenum, pos); 441303967Ssbruno 442303967Ssbruno /* 443303967Ssbruno * Test the line to see if we have a match. 444303967Ssbruno * Use strncmp because the pattern may be 445303967Ssbruno * truncated (in the tags file) if it is too long. 446303967Ssbruno * If tagendline is set, make sure we match all 447303967Ssbruno * the way to end of line (no extra chars after the match). 448303967Ssbruno */ 449303967Ssbruno len = strlen(curtag->tag_pattern); 450303967Ssbruno if (strncmp(curtag->tag_pattern, line, len) == 0 && 451303967Ssbruno (!curtag->tag_endline || line[len] == '\0' || line[len] == '\r')) 452303967Ssbruno { 453303967Ssbruno curtag->tag_linenum = find_linenum(linepos); 454303967Ssbruno break; 455303967Ssbruno } 456303967Ssbruno } 457303967Ssbruno 458303967Ssbruno return (linepos); 459303967Ssbruno} 460303967Ssbruno 461303967Ssbruno/******************************************************************************* 462303967Ssbruno * gtags 463303967Ssbruno */ 464303967Ssbruno 465303967Ssbruno/* 466303967Ssbruno * Find tags in the GLOBAL's tag file. 467303967Ssbruno * The findgtag() will try and load information about the requested tag. 468303967Ssbruno * It does this by calling "global -x tag" and storing the parsed output 469303967Ssbruno * for future use by gtagsearch(). 470303967Ssbruno * Sets curtag to the first tag entry. 471303967Ssbruno */ 472303967Ssbruno static enum tag_result 473303967Ssbrunofindgtag(tag, type) 474303967Ssbruno char *tag; /* tag to load */ 475303967Ssbruno int type; /* tags type */ 476303967Ssbruno{ 477303967Ssbruno char buf[256]; 478303967Ssbruno FILE *fp; 479303967Ssbruno struct tag *tp; 480303967Ssbruno 481303967Ssbruno if (type != T_CTAGS_X && tag == NULL) 482303967Ssbruno return TAG_NOFILE; 483303967Ssbruno 484303967Ssbruno cleantags(); 485303967Ssbruno total = 0; 486303967Ssbruno 487303967Ssbruno /* 488303967Ssbruno * If type == T_CTAGS_X then read ctags's -x format from stdin 489303967Ssbruno * else execute global(1) and read from it. 490303967Ssbruno */ 491303967Ssbruno if (type == T_CTAGS_X) 492303967Ssbruno { 493303967Ssbruno fp = stdin; 494303967Ssbruno /* Set tag default because we cannot read stdin again. */ 495303967Ssbruno tags = "tags"; 496303967Ssbruno } else 497303967Ssbruno { 498303967Ssbruno#if !HAVE_POPEN 499303967Ssbruno return TAG_NOFILE; 500303967Ssbruno#else 501303967Ssbruno char *command; 502303967Ssbruno char *flag; 503303967Ssbruno char *qtag; 504303967Ssbruno char *cmd = lgetenv("LESSGLOBALTAGS"); 505303967Ssbruno 506303967Ssbruno if (cmd == NULL || *cmd == '\0') 507303967Ssbruno return TAG_NOFILE; 508303967Ssbruno /* Get suitable flag value for global(1). */ 509303967Ssbruno switch (type) 510303967Ssbruno { 511303967Ssbruno case T_GTAGS: 512303967Ssbruno flag = "" ; 513303967Ssbruno break; 514303967Ssbruno case T_GRTAGS: 515303967Ssbruno flag = "r"; 516303967Ssbruno break; 517303967Ssbruno case T_GSYMS: 518303967Ssbruno flag = "s"; 519303967Ssbruno break; 520303967Ssbruno case T_GPATH: 521303967Ssbruno flag = "P"; 522303967Ssbruno break; 523303967Ssbruno default: 524303967Ssbruno return TAG_NOTYPE; 525303967Ssbruno } 526303967Ssbruno 527303967Ssbruno /* Get our data from global(1). */ 528303967Ssbruno qtag = shell_quote(tag); 529303967Ssbruno if (qtag == NULL) 530303967Ssbruno qtag = tag; 531303967Ssbruno command = (char *) ecalloc(strlen(cmd) + strlen(flag) + 532303967Ssbruno strlen(qtag) + 5, sizeof(char)); 533303967Ssbruno sprintf(command, "%s -x%s %s", cmd, flag, qtag); 534303967Ssbruno if (qtag != tag) 535303967Ssbruno free(qtag); 536303967Ssbruno fp = popen(command, "r"); 537303967Ssbruno free(command); 538303967Ssbruno#endif 539303967Ssbruno } 540303967Ssbruno if (fp != NULL) 541303967Ssbruno { 542303967Ssbruno while (fgets(buf, sizeof(buf), fp)) 543303967Ssbruno { 544303967Ssbruno char *name, *file, *line; 545303967Ssbruno int len; 546303967Ssbruno 547303967Ssbruno if (sigs) 548303967Ssbruno { 549303967Ssbruno#if HAVE_POPEN 550303967Ssbruno if (fp != stdin) 551303967Ssbruno pclose(fp); 552303967Ssbruno#endif 553303967Ssbruno return TAG_INTR; 554303967Ssbruno } 555303967Ssbruno len = strlen(buf); 556303967Ssbruno if (len > 0 && buf[len-1] == '\n') 557303967Ssbruno buf[len-1] = '\0'; 558303967Ssbruno else 559303967Ssbruno { 560303967Ssbruno int c; 561303967Ssbruno do { 562303967Ssbruno c = fgetc(fp); 563303967Ssbruno } while (c != '\n' && c != EOF); 564303967Ssbruno } 565303967Ssbruno 566303967Ssbruno if (getentry(buf, &name, &file, &line)) 567303967Ssbruno { 568303967Ssbruno /* 569303967Ssbruno * Couldn't parse this line for some reason. 570303967Ssbruno * We'll just pretend it never happened. 571303967Ssbruno */ 572303967Ssbruno break; 573303967Ssbruno } 574303967Ssbruno 575303967Ssbruno /* Make new entry and add to list. */ 576303967Ssbruno tp = maketagent(name, file, (LINENUM) atoi(line), NULL, 0); 577303967Ssbruno TAG_INS(tp); 578266423Sjfv total++; 579266423Sjfv } 580266423Sjfv if (fp != stdin) 581266423Sjfv { 582266423Sjfv if (pclose(fp)) 583266423Sjfv { 584266423Sjfv curtag = NULL; 585266423Sjfv total = curseq = 0; 586266423Sjfv return TAG_NOFILE; 587266423Sjfv } 588266423Sjfv } 589266423Sjfv } 590266423Sjfv 591266423Sjfv /* Check to see if we found anything. */ 592266423Sjfv tp = taglist.tl_first; 593266423Sjfv if (tp == TAG_END) 594266423Sjfv return TAG_NOTAG; 595266423Sjfv curtag = tp; 596266423Sjfv curseq = 1; 597266423Sjfv return TAG_FOUND; 598266423Sjfv} 599266423Sjfv 600266423Sjfvstatic int circular = 0; /* 1: circular tag structure */ 601266423Sjfv 602266423Sjfv/* 603266423Sjfv * Return the filename required for the next gtag in the queue that was setup 604266423Sjfv * by findgtag(). The next call to gtagsearch() will try to position at the 605266423Sjfv * appropriate tag. 606266423Sjfv */ 607266423Sjfv static char * 608266423Sjfvnextgtag() 609266423Sjfv{ 610266423Sjfv struct tag *tp; 611266423Sjfv 612266423Sjfv if (curtag == NULL) 613266423Sjfv /* No tag loaded */ 614266423Sjfv return NULL; 615266423Sjfv 616266423Sjfv tp = curtag->next; 617266423Sjfv if (tp == TAG_END) 618266423Sjfv { 619266423Sjfv if (!circular) 620266423Sjfv return NULL; 621266423Sjfv /* Wrapped around to the head of the queue */ 622266423Sjfv curtag = taglist.tl_first; 623266423Sjfv curseq = 1; 624266423Sjfv } else 625266423Sjfv { 626266423Sjfv curtag = tp; 627266423Sjfv curseq++; 628266423Sjfv } 629266423Sjfv return (curtag->tag_file); 630266423Sjfv} 631266423Sjfv 632266423Sjfv/* 633266423Sjfv * Return the filename required for the previous gtag in the queue that was 634266423Sjfv * setup by findgtat(). The next call to gtagsearch() will try to position 635266423Sjfv * at the appropriate tag. 636266423Sjfv */ 637266423Sjfv static char * 638266423Sjfvprevgtag() 639266423Sjfv{ 640266423Sjfv struct tag *tp; 641266423Sjfv 642266423Sjfv if (curtag == NULL) 643266423Sjfv /* No tag loaded */ 644266423Sjfv return NULL; 645266423Sjfv 646266423Sjfv tp = curtag->prev; 647266423Sjfv if (tp == TAG_END) 648266423Sjfv { 649266423Sjfv if (!circular) 650266423Sjfv return NULL; 651266423Sjfv /* Wrapped around to the tail of the queue */ 652266423Sjfv curtag = taglist.tl_last; 653266423Sjfv curseq = total; 654266423Sjfv } else 655266423Sjfv { 656266423Sjfv curtag = tp; 657266423Sjfv curseq--; 658266423Sjfv } 659266423Sjfv return (curtag->tag_file); 660266423Sjfv} 661266423Sjfv 662266423Sjfv/* 663266423Sjfv * Position the current file at at what is hopefully the tag that was chosen 664266423Sjfv * using either findtag() or one of nextgtag() and prevgtag(). Returns -1 665266423Sjfv * if it was unable to position at the tag, 0 if successful. 666266423Sjfv */ 667266423Sjfv static POSITION 668266423Sjfvgtagsearch() 669266423Sjfv{ 670266423Sjfv if (curtag == NULL) 671266423Sjfv return (NULL_POSITION); /* No gtags loaded! */ 672266423Sjfv return (find_pos(curtag->tag_linenum)); 673266423Sjfv} 674266423Sjfv 675266423Sjfv/* 676266423Sjfv * The getentry() parses both standard and extended ctags -x format. 677266423Sjfv * 678266423Sjfv * [standard format] 679266423Sjfv * <tag> <lineno> <file> <image> 680266423Sjfv * +------------------------------------------------ 681266423Sjfv * |main 30 main.c main(argc, argv) 682266423Sjfv * |func 21 subr.c func(arg) 683266423Sjfv * 684266423Sjfv * The following commands write this format. 685266423Sjfv * o Traditinal Ctags with -x option 686266423Sjfv * o Global with -x option 687266423Sjfv * See <http://www.gnu.org/software/global/global.html> 688266423Sjfv * 689266423Sjfv * [extended format] 690266423Sjfv * <tag> <type> <lineno> <file> <image> 691266423Sjfv * +---------------------------------------------------------- 692266423Sjfv * |main function 30 main.c main(argc, argv) 693266423Sjfv * |func function 21 subr.c func(arg) 694266423Sjfv * 695266423Sjfv * The following commands write this format. 696266423Sjfv * o Exuberant Ctags with -x option 697266423Sjfv * See <http://ctags.sourceforge.net> 698266423Sjfv * 699266423Sjfv * Returns 0 on success, -1 on error. 700266423Sjfv * The tag, file, and line will each be NUL-terminated pointers 701266423Sjfv * into buf. 702266423Sjfv */ 703266423Sjfv static int 704266423Sjfvgetentry(buf, tag, file, line) 705266423Sjfv char *buf; /* standard or extended ctags -x format data */ 706266423Sjfv char **tag; /* name of the tag we actually found */ 707266423Sjfv char **file; /* file in which to find this tag */ 708266423Sjfv char **line; /* line number of file where this tag is found */ 709266423Sjfv{ 710266423Sjfv char *p = buf; 711266423Sjfv 712266423Sjfv for (*tag = p; *p && !IS_SPACE(*p); p++) /* tag name */ 713266423Sjfv ; 714266423Sjfv if (*p == 0) 715266423Sjfv return (-1); 716266423Sjfv *p++ = 0; 717266423Sjfv for ( ; *p && IS_SPACE(*p); p++) /* (skip blanks) */ 718266423Sjfv ; 719266423Sjfv if (*p == 0) 720266423Sjfv return (-1); 721266423Sjfv /* 722266423Sjfv * If the second part begin with other than digit, 723266423Sjfv * it is assumed tag type. Skip it. 724266423Sjfv */ 725266423Sjfv if (!IS_DIGIT(*p)) 726266423Sjfv { 727266423Sjfv for ( ; *p && !IS_SPACE(*p); p++) /* (skip tag type) */ 728266423Sjfv ; 729266423Sjfv for (; *p && IS_SPACE(*p); p++) /* (skip blanks) */ 730266423Sjfv ; 731266423Sjfv } 732266423Sjfv if (!IS_DIGIT(*p)) 733266423Sjfv return (-1); 734266423Sjfv *line = p; /* line number */ 735266423Sjfv for (*line = p; *p && !IS_SPACE(*p); p++) 736266423Sjfv ; 737266423Sjfv if (*p == 0) 738266423Sjfv return (-1); 739266423Sjfv *p++ = 0; 740266423Sjfv for ( ; *p && IS_SPACE(*p); p++) /* (skip blanks) */ 741303967Ssbruno ; 742266423Sjfv if (*p == 0) 743266423Sjfv return (-1); 744266423Sjfv *file = p; /* file name */ 745266423Sjfv for (*file = p; *p && !IS_SPACE(*p); p++) 746266423Sjfv ; 747266423Sjfv if (*p == 0) 748266423Sjfv return (-1); 749266423Sjfv *p = 0; 750266423Sjfv 751266423Sjfv /* value check */ 752266423Sjfv if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0) 753266423Sjfv return (0); 754266423Sjfv return (-1); 755266423Sjfv} 756266423Sjfv 757266423Sjfv#endif 758266423Sjfv