/* * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, * 2007 Tama Communications Corporation * * This file is part of GNU GLOBAL. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #ifdef STDC_HEADERS #include #endif #ifdef HAVE_STRING_H #include #else #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "getopt.h" #include "global.h" #include "regex.h" #include "const.h" static void usage(void); static void help(void); static void setcom(int); int main(int, char **); void completion(const char *, const char *, const char *); void idutils(const char *, const char *); void grep(const char *, const char *); void pathlist(const char *, const char *); void parsefile(int, char **, const char *, const char *, const char *, int); int search(const char *, const char *, const char *, const char *, int); void tagsearch(const char *, const char *, const char *, const char *, int); void encode(char *, int, const char *); const char *localprefix; /* local prefix */ int aflag; /* [option] */ int cflag; /* command */ int fflag; /* command */ int gflag; /* command */ int Gflag; /* [option] */ int iflag; /* [option] */ int Iflag; /* command */ int lflag; /* [option] */ int nflag; /* [option] */ int oflag; /* [option] */ int Oflag; /* [option] */ int pflag; /* command */ int Pflag; /* command */ int qflag; /* [option] */ int rflag; /* [option] */ int sflag; /* [option] */ int tflag; /* [option] */ int Tflag; /* [option] */ int uflag; /* command */ int vflag; /* [option] */ int xflag; /* [option] */ int show_version; int show_help; int nofilter; int nosource; /* undocumented command */ int debug; int format; int type; /* path conversion type */ char cwd[MAXPATHLEN+1]; /* current directory */ char root[MAXPATHLEN+1]; /* root of source tree */ char dbpath[MAXPATHLEN+1]; /* dbpath directory */ static void usage(void) { if (!qflag) fputs(usage_const, stderr); exit(2); } static void help(void) { fputs(usage_const, stdout); fputs(help_const, stdout); exit(0); } #define RESULT 128 #define SORT_FILTER 1 #define PATH_FILTER 2 #define BOTH_FILTER (SORT_FILTER|PATH_FILTER) static struct option const long_options[] = { {"absolute", no_argument, NULL, 'a'}, {"completion", no_argument, NULL, 'c'}, {"regexp", required_argument, NULL, 'e'}, {"file", no_argument, NULL, 'f'}, {"local", no_argument, NULL, 'l'}, {"nofilter", optional_argument, NULL, 'n'}, {"grep", no_argument, NULL, 'g'}, {"basic-regexp", no_argument, NULL, 'G'}, {"ignore-case", no_argument, NULL, 'i'}, {"idutils", no_argument, NULL, 'I'}, {"other", no_argument, NULL, 'o'}, {"only-other", no_argument, NULL, 'O'}, {"print-dbpath", no_argument, NULL, 'p'}, {"path", no_argument, NULL, 'P'}, {"quiet", no_argument, NULL, 'q'}, {"reference", no_argument, NULL, 'r'}, {"rootdir", no_argument, NULL, 'r'}, {"symbol", no_argument, NULL, 's'}, {"tags", no_argument, NULL, 't'}, {"through", no_argument, NULL, 'T'}, {"update", no_argument, NULL, 'u'}, {"verbose", no_argument, NULL, 'v'}, {"cxref", no_argument, NULL, 'x'}, /* long name only */ {"debug", no_argument, &debug, 1}, {"version", no_argument, &show_version, 1}, {"help", no_argument, &show_help, 1}, {"result", required_argument, NULL, RESULT}, {"nosource", no_argument, &nosource, 1}, { 0 } }; static int command; static void setcom(int c) { if (command == 0) command = c; else if (command != c) usage(); } int main(int argc, char **argv) { const char *av = NULL; int db; int optchar; int option_index = 0; while ((optchar = getopt_long(argc, argv, "ace:ifgGIlnoOpPqrstTuvx", long_options, &option_index)) != EOF) { switch (optchar) { case 0: break; case 'a': aflag++; break; case 'c': cflag++; setcom(optchar); break; case 'e': av = optarg; break; case 'f': fflag++; xflag++; setcom(optchar); break; case 'l': lflag++; break; case 'n': nflag++; if (optarg) { if (!strcmp(optarg, "sort")) nofilter |= SORT_FILTER; else if (!strcmp(optarg, "path")) nofilter |= PATH_FILTER; } else { nofilter = BOTH_FILTER; } break; case 'g': gflag++; setcom(optchar); break; case 'G': Gflag++; break; case 'i': iflag++; break; case 'I': Iflag++; setcom(optchar); break; case 'o': oflag++; break; case 'O': Oflag++; break; case 'p': pflag++; setcom(optchar); break; case 'P': Pflag++; setcom(optchar); break; case 'q': qflag++; setquiet(); break; case 'r': rflag++; break; case 's': sflag++; break; case 't': tflag++; break; case 'T': Tflag++; break; case 'u': uflag++; setcom(optchar); break; case 'v': vflag++; break; case 'x': xflag++; break; case RESULT: if (!strcmp(optarg, "ctags-x")) format = FORMAT_CTAGS_X; else if (!strcmp(optarg, "ctags-xid")) format = FORMAT_CTAGS_XID; else if (!strcmp(optarg, "ctags")) format = FORMAT_CTAGS; else if (!strcmp(optarg, "path")) format = FORMAT_PATH; else if (!strcmp(optarg, "grep")) format = FORMAT_GREP; else if (!strcmp(optarg, "cscope")) format = FORMAT_CSCOPE; else die_with_code(2, "unknown format type for the --result option."); break; default: usage(); break; } } if (qflag) vflag = 0; if (show_help) help(); argc -= optind; argv += optind; /* * At first, we pickup pattern from -e option. If it is not found * then use argument which is not option. */ if (!av) av = (argc > 0) ? *argv : NULL; if (show_version) version(av, vflag); /* * invalid options. */ if (sflag && rflag) die_with_code(2, "both of -s and -r are not allowed."); /* * only -c, -u, -P and -p allows no argument. */ if (!av) { switch (command) { case 'c': case 'u': case 'p': case 'P': break; default: usage(); break; } } /* * -u and -p cannot have any arguments. */ if (av) { switch (command) { case 'u': case 'p': usage(); default: break; } } if (tflag) xflag = 0; if (nflag > 1) nosource = 1; /* to keep compatibility */ /* * remove leading blanks. */ if (!Iflag && !gflag && av) for (; *av == ' ' || *av == '\t'; av++) ; if (cflag && av && isregex(av)) die_with_code(2, "only name char is allowed with -c option."); /* * get path of following directories. * o current directory * o root of source tree * o dbpath directory * * if GTAGS not found, getdbpath doesn't return. */ getdbpath(cwd, root, dbpath, (pflag && vflag)); if (Iflag && !test("f", makepath(root, "ID", NULL))) die("You must have idutils's index at the root of source tree."); /* * print dbpath or rootdir. */ if (pflag) { fprintf(stdout, "%s\n", (rflag) ? root : dbpath); exit(0); } /* * incremental update of tag files. */ if (uflag) { STRBUF *sb = strbuf_open(0); char *gtags = usable("gtags"); if (!gtags) die("gtags command not found."); if (chdir(root) < 0) die("cannot change directory to '%s'.", root); strbuf_puts(sb, gtags); strbuf_puts(sb, " -i"); if (vflag) strbuf_putc(sb, 'v'); strbuf_putc(sb, ' '); strbuf_puts(sb, dbpath); if (system(strbuf_value(sb))) exit(1); strbuf_close(sb); exit(0); } /* * complete function name */ if (cflag) { completion(dbpath, root, av); exit(0); } /* * make local prefix. * local prefix must starts with './' and ends with '/'. */ if (lflag) { STRBUF *sb = strbuf_open(0); strbuf_putc(sb, '.'); if (strcmp(root, cwd) != 0) { char *p = cwd + strlen(root); if (*p != '/') strbuf_putc(sb, '/'); strbuf_puts(sb, p); } strbuf_putc(sb, '/'); localprefix = check_strdup(strbuf_value(sb)); strbuf_close(sb); #ifdef DEBUG fprintf(stderr, "root=%s\n", root); fprintf(stderr, "cwd=%s\n", cwd); fprintf(stderr, "localprefix=%s\n", localprefix); #endif } /* * decide tag type. */ db = (rflag) ? GRTAGS : ((sflag) ? GSYMS : GTAGS); /* * decide format. * The --result option is given to priority more than the -t and -x option. */ if (format == 0) { if (tflag) { /* ctags format */ format = FORMAT_CTAGS; } else if (xflag) { /* print details */ format = FORMAT_CTAGS_X; } else { /* print just a file name */ format = FORMAT_PATH; } } /* * decide path conversion type. */ if (nofilter & PATH_FILTER) type = PATH_THROUGH; else if (aflag) type = PATH_ABSOLUTE; else type = PATH_RELATIVE; /* * exec lid(idutils). */ if (Iflag) { chdir(root); idutils(av, dbpath); } /* * search pattern (regular expression). */ else if (gflag) { chdir(root); grep(av, dbpath); } /* * locate paths including the pattern. */ else if (Pflag) { chdir(root); pathlist(av, dbpath); } /* * parse source files. */ else if (fflag) { parsefile(argc, argv, cwd, root, dbpath, db); } /* * tag search. */ else { tagsearch(av, cwd, root, dbpath, db); } return 0; } /* * completion: print completion list of specified prefix * * i) dbpath dbpath directory * i) root root directory * i) prefix prefix of primary key */ void completion(const char *dbpath, const char *root, const char *prefix) { int flags = GTOP_KEY; GTOP *gtop; GTP *gtp; int db; flags |= GTOP_NOREGEX; if (prefix && *prefix == 0) /* In the case global -c '' */ prefix = NULL; if (prefix) flags |= GTOP_PREFIX; db = (sflag) ? GSYMS : GTAGS; gtop = gtags_open(dbpath, root, db, GTAGS_READ); for (gtp = gtags_first(gtop, prefix, flags); gtp; gtp = gtags_next(gtop)) { fputs(gtp->tag, stdout); fputc('\n', stdout); } gtags_close(gtop); } /* * Output filter * * (1) Old architecture (- GLOBAL-4.7.8) * * process1 process2 process3 * +=============+ +===========+ +===========+ * |global(write)|->|sort filter|->|path filter|->[stdout] * +=============+ +===========+ +===========+ * * (2) Recent architecture (GLOBAL-5.0 - 5.3) * * 1 process * +===========================================+ * |global(write)->[sort filter]->[path filter]|->[stdout] * +===========================================+ * * (3) Current architecture (GLOBAL-5.4 -) * * 1 process * +===========================================+ * |[sort filter]->global(write)->[path filter]|->[stdout] * +===========================================+ * * Sort filter is implemented in gtagsop module (libutil/gtagsop.c). * Path filter is implemented in pathconvert module (libutil/pathconvert.c). */ /* * print number of object. */ void print_count(int number) { if (format == FORMAT_PATH) { switch (number) { case 0: fprintf(stderr, "object not found"); break; case 1: fprintf(stderr, "1 file located"); break; default: fprintf(stderr, "%d files located", number); break; } } else { switch (number) { case 0: fprintf(stderr, "object not found"); break; case 1: fprintf(stderr, "1 object located"); break; default: fprintf(stderr, "%d objects located", number); break; } } } /* * idutils: lid(idutils) pattern * * i) pattern POSIX regular expression * i) dbpath GTAGS directory */ void idutils(const char *pattern, const char *dbpath) { FILE *ip; CONVERT *cv; STRBUF *ib = strbuf_open(0); char edit[IDENTLEN+1]; const char *path, *lno, *lid; int linenum, count; char *p, *grep; lid = usable("lid"); if (!lid) die("lid(idutils) not found."); /* * convert spaces into %FF format. */ encode(edit, sizeof(edit), pattern); /* * make lid command line. * Invoke lid with the --result=grep option to generate grep format. */ strbuf_puts(ib, lid); strbuf_puts(ib, " --separator=newline"); if (!tflag && !xflag) strbuf_puts(ib, " --result=filenames --key=none"); else strbuf_puts(ib, " --result=grep"); if (iflag) strbuf_puts(ib, " --ignore-case"); strbuf_putc(ib, ' '); strbuf_puts(ib, quote_string(pattern)); if (debug) fprintf(stderr, "idutils: %s\n", strbuf_value(ib)); if (!(ip = popen(strbuf_value(ib), "r"))) die("cannot execute '%s'.", strbuf_value(ib)); cv = convert_open(type, format, root, cwd, dbpath, stdout); count = 0; while ((grep = strbuf_fgets(ib, ip, STRBUF_NOCRLF)) != NULL) { p = grep; /* extract path name */ path = p; while (*p && *p != ':') p++; if ((xflag || tflag) && !*p) die("invalid lid(idutils) output format. '%s'", grep); *p++ = 0; /* normalize path name */ path = makepath(".", path, NULL); if (lflag) { if (!locatestring(path, localprefix, MATCH_AT_FIRST)) continue; } count++; switch (format) { case FORMAT_PATH: convert_put(cv, path); break; default: /* extract line number */ while (*p && isspace(*p)) p++; lno = p; while (*p && isdigit(*p)) p++; if (*p != ':') die("invalid lid(idutils) output format. '%s'", grep); *p++ = 0; linenum = atoi(lno); if (linenum <= 0) die("invalid lid(idutils) output format. '%s'", grep); /* * print out. */ convert_put_using(cv, edit, path, linenum, p); break; } } if (pclose(ip) < 0) die("terminated abnormally."); convert_close(cv); strbuf_close(ib); if (vflag) { print_count(count); fprintf(stderr, " (using idutils index in '%s').\n", dbpath); } } /* * grep: grep pattern * * i) pattern POSIX regular expression */ void grep(const char *pattern, const char *dbpath) { FILE *fp; CONVERT *cv; GFIND *gp; STRBUF *ib = strbuf_open(MAXBUFLEN); const char *path; char edit[IDENTLEN+1]; const char *buffer; int linenum, count; int flags = 0; int target = GPATH_SOURCE; regex_t preg; /* * convert spaces into %FF format. */ encode(edit, sizeof(edit), pattern); if (oflag) target = GPATH_BOTH; if (Oflag) target = GPATH_OTHER; if (!Gflag) flags |= REG_EXTENDED; if (iflag) flags |= REG_ICASE; if (regcomp(&preg, pattern, flags) != 0) die("invalid regular expression."); cv = convert_open(type, format, root, cwd, dbpath, stdout); count = 0; gp = gfind_open(dbpath, localprefix, target); while ((path = gfind_read(gp)) != NULL) { if (!(fp = fopen(path, "r"))) die("'%s' not found. Please remake tag files by invoking gtags(1).", path); linenum = 0; while ((buffer = strbuf_fgets(ib, fp, STRBUF_NOCRLF)) != NULL) { linenum++; if (regexec(&preg, buffer, 0, 0, 0) == 0) { count++; if (format == FORMAT_PATH) { convert_put(cv, path); break; } else { convert_put_using(cv, edit, path, linenum, buffer); } } } fclose(fp); } gfind_close(gp); convert_close(cv); strbuf_close(ib); regfree(&preg); if (vflag) { print_count(count); fprintf(stderr, " (no index used).\n"); } } /* * pathlist: print candidate path list. * * i) dbpath */ void pathlist(const char *pattern, const char *dbpath) { GFIND *gp; CONVERT *cv; const char *path, *p; regex_t preg; int count; int target = GPATH_SOURCE; if (oflag) target = GPATH_BOTH; if (Oflag) target = GPATH_OTHER; if (pattern) { int flags = 0; char edit[IDENTLEN+1]; if (!Gflag) flags |= REG_EXTENDED; if (iflag || getconfb("icase_path")) flags |= REG_ICASE; #ifdef _WIN32 flags |= REG_ICASE; #endif /* _WIN32 */ /* * We assume '^aaa' as '^/aaa'. */ if (*pattern == '^' && *(pattern + 1) != '/') { snprintf(edit, sizeof(edit), "^/%s", pattern + 1); pattern = edit; } if (regcomp(&preg, pattern, flags) != 0) die("invalid regular expression."); } if (!localprefix) localprefix = "./"; cv = convert_open(type, format, root, cwd, dbpath, stdout); count = 0; gp = gfind_open(dbpath, localprefix, target); while ((path = gfind_read(gp)) != NULL) { /* * skip localprefix because end-user doesn't see it. */ p = path + strlen(localprefix) - 1; if (pattern && regexec(&preg, p, 0, 0, 0) != 0) continue; if (format == FORMAT_PATH) convert_put(cv, path); else convert_put_using(cv, "path", path, 1, " "); count++; } gfind_close(gp); convert_close(cv); if (pattern) regfree(&preg); if (vflag) { switch (count) { case 0: fprintf(stderr, "file not found"); break; case 1: fprintf(stderr, "%d file located", count); break; default: fprintf(stderr, "%d files located", count); break; } fprintf(stderr, " (using '%s').\n", makepath(dbpath, dbname(GPATH), NULL)); } } /* * parsefile: parse file to pick up tags. * * i) argc * i) argv * i) cwd current directory * i) root root directory of source tree * i) dbpath dbpath * i) db type of parse */ void parsefile(int argc, char **argv, const char *cwd, const char *root, const char *dbpath, int db) { CONVERT *cv; char rootdir[MAXPATHLEN+1]; char buf[MAXPATHLEN+1], *path; const char *basename; STATIC_STRBUF(dir); int count = 0; STRBUF *comline = strbuf_open(0); STRBUF *path_list = strbuf_open(MAXPATHLEN); XARGS *xp; char *ctags_x; snprintf(rootdir, sizeof(rootdir), "%s/", root); /* * teach parser where is dbpath. */ set_env("GTAGSDBPATH", dbpath); /* * teach parser language mapping. */ { STRBUF *sb = strbuf_open(0); if (getconfs("langmap", sb)) set_env("GTAGSLANGMAP", strbuf_value(sb)); strbuf_close(sb); } /* * get parser. */ if (!getconfs(dbname(db), comline)) die("cannot get parser for %s.", dbname(db)); cv = convert_open(type, format, root, cwd, dbpath, stdout); if (gpath_open(dbpath, 0) < 0) die("GPATH not found."); /* * Make a path list while checking the validity of path name. */ for (; argc > 0; argv++, argc--) { const char *av = argv[0]; if (!test("f", av)) { if (test("d", av)) { if (!qflag) fprintf(stderr, "'%s' is a directory.\n", av); } else { if (!qflag) fprintf(stderr, "'%s' not found.\n", av); } continue; } /* * convert path into relative from root directory of source tree. */ strbuf_clear(dir); basename = locatestring(av, "/", MATCH_LAST); if (basename != NULL) { strbuf_nputs(dir, av, basename - av); basename++; } else { strbuf_putc(dir, '.'); basename = av; } /* * Expand symlink only in directory part. */ path = realpath(strbuf_value(dir), buf); if (path == NULL) die("realpath(%s, buf) failed. (errno=%d).", strbuf_value(dir), errno); if (!isabspath(path)) die("realpath(3) is not compatible with BSD version."); if (strcmp(path, "/") != 0) strcat(path, "/"); strcat(path, basename); /* * Remove the root part of path and insert './'. * rootdir /a/b/ * path /a/b/c/d.c -> c/d.c -> ./c/d.c */ path = locatestring(path, rootdir, MATCH_AT_FIRST); if (path == NULL) { if (!qflag) fprintf(stderr, "'%s' is out of source tree.\n", buf); continue; } /* normalize path name */ path -= 2; *path = '.'; if (!gpath_path2fid(path, NULL)) { if (!qflag) fprintf(stderr, "'%s' not found in GPATH.\n", path); continue; } if (lflag && !locatestring(path, localprefix, MATCH_AT_FIRST)) continue; /* * Add a path to the path list. */ strbuf_puts0(path_list, path); } /* * Execute parser in the root directory of source tree. */ if (chdir(root) < 0) die("cannot move to '%s' directory.", root); xp = xargs_open_with_strbuf(strbuf_value(comline), 0, path_list); if (format == FORMAT_PATH) { SPLIT ptable; char curpath[MAXPATHLEN+1]; curpath[0] = '\0'; while ((ctags_x = xargs_read(xp)) != NULL) { if (split((char *)ctags_x, 4, &ptable) < 4) { recover(&ptable); die("too small number of parts.\n'%s'", ctags_x); } if (strcmp(curpath, ptable.part[PART_PATH].start)) { strlimcpy(curpath, ptable.part[PART_PATH].start, sizeof(curpath)); convert_put(cv, curpath); count++; } } } else { while ((ctags_x = xargs_read(xp)) != NULL) { convert_put(cv, ctags_x); count++; } } xargs_close(xp); if (chdir(cwd) < 0) die("cannot move to '%s' directory.", cwd); /* * Settlement */ gpath_close(); convert_close(cv); strbuf_close(comline); strbuf_close(path_list); if (vflag) { print_count(count); fprintf(stderr, " (no index used).\n"); } } /* * search: search specified function * * i) pattern search pattern * i) root root of source tree * i) cwd current directory * i) dbpath database directory * i) db GTAGS,GRTAGS,GSYMS * r) count of output lines */ /* get next number and seek to the next character */ #define GET_NEXT_NUMBER(p) do { \ if (!isdigit(*p)) \ p++; \ for (n = 0; isdigit(*p); p++) \ n = n * 10 + (*p - '0'); \ } while (0) int search(const char *pattern, const char *root, const char *cwd, const char *dbpath, int db) { CONVERT *cv; int count = 0; GTOP *gtop; GTP *gtp; int flags = 0; STRBUF *sb = NULL, *ib = NULL; char curpath[MAXPATHLEN+1], curtag[IDENTLEN+1]; FILE *fp = NULL; const char *src = ""; int lineno, last_lineno; lineno = last_lineno = 0; curpath[0] = curtag[0] = '\0'; /* * open tag file. */ gtop = gtags_open(dbpath, root, db, GTAGS_READ); cv = convert_open(type, format, root, cwd, dbpath, stdout); /* * search through tag file. */ if (nofilter & SORT_FILTER) flags |= GTOP_NOSORT; if (iflag) { if (!isregex(pattern)) { sb = strbuf_open(0); strbuf_putc(sb, '^'); strbuf_puts(sb, pattern); strbuf_putc(sb, '$'); pattern = strbuf_value(sb); } flags |= GTOP_IGNORECASE; } if (Gflag) flags |= GTOP_BASICREGEX; if (format == FORMAT_PATH) flags |= GTOP_PATH; if (gtop->format & GTAGS_COMPACT) ib = strbuf_open(0); for (gtp = gtags_first(gtop, pattern, flags); gtp; gtp = gtags_next(gtop)) { if (lflag && !locatestring(gtp->path, localprefix, MATCH_AT_FIRST)) continue; if (format == FORMAT_PATH) { convert_put(cv, gtp->path); count++; } else if (gtop->format & GTAGS_COMPACT) { /* * a b * tagline = ,... */ char *p = (char *)gtp->tagline; const char *tagname; int n = 0; while (*p != ' ') p++; *p++ = '\0'; /* a */ tagname = p; while (*p != ' ') p++; *p++ = '\0'; /* b */ /* * Reopen or rewind source file. */ if (!nosource) { if (strcmp(gtp->path, curpath) != 0) { if (curpath[0] != '\0' && fp != NULL) fclose(fp); strlimcpy(curtag, tagname, sizeof(curtag)); strlimcpy(curpath, gtp->path, sizeof(curpath)); /* * Use absolute path name to support GTAGSROOT * environment variable. */ fp = fopen(makepath(root, curpath, NULL), "r"); if (fp == NULL) warning("source file '%s' is not available.", curpath); last_lineno = lineno = 0; } else if (strcmp(gtp->tag, curtag) != 0) { strlimcpy(curtag, gtp->tag, sizeof(curtag)); if (atoi(p) < last_lineno && fp != NULL) { rewind(fp); lineno = 0; } last_lineno = 0; } } /* * Unfold compact format. * * If GTAGS_COMPLINE flag is set, each line number is expressed as * the difference from the previous line number except for the head. * Please see flush_pool() in libutil/gtagsop.c for the details. */ if (!isdigit(*p)) die("illegal compact format."); if (gtop->format & GTAGS_COMPLINE) { int last = 0, cont = 0; while (*p || cont > 0) { if (cont > 0) { n = last + 1; if (n > cont) { cont = 0; continue; } } else if (isdigit(*p)) { GET_NEXT_NUMBER(p); } else if (*p == '-') { GET_NEXT_NUMBER(p); cont = n + last; n = last + 1; } else if (*p == ',') { GET_NEXT_NUMBER(p); n += last; } if (last_lineno != n && fp) { while (lineno < n) { if (!(src = strbuf_fgets(ib, fp, STRBUF_NOCRLF))) die("unexpected end of file. '%s: %d/%d'", gtp->path, lineno, n); lineno++; } } if (gtop->format & GTAGS_COMPNAME) tagname = (char *)uncompress(tagname, gtp->tag); convert_put_using(cv, tagname, gtp->path, n, src); count++; last_lineno = last = n; } } else { while (*p) { for (n = 0; isdigit(*p); p++) n = n * 10 + *p - '0'; if (*p == ',') p++; if (last_lineno == n) continue; if (last_lineno != n && fp) { while (lineno < n) { if (!(src = strbuf_fgets(ib, fp, STRBUF_NOCRLF))) die("unexpected end of file. '%s: %d/%d'", gtp->path, lineno, n); lineno++; } } if (gtop->format & GTAGS_COMPNAME) tagname = (char *)uncompress(tagname, gtp->tag); convert_put_using(cv, tagname, gtp->path, n, src); count++; last_lineno = n; } } } else { /* * a b c * tagline = */ char *p = (char *)gtp->tagline; char namebuf[IDENTLEN+1]; const char *tagname, *image; while (*p != ' ') p++; *p++ = '\0'; /* a */ tagname = p; while (*p != ' ') p++; *p++ = '\0'; /* b */ if (gtop->format & GTAGS_COMPNAME) { strlimcpy(namebuf, (char *)uncompress(tagname, gtp->tag), sizeof(namebuf)); tagname = namebuf; } if (nosource) { image = " "; } else { while (*p != ' ') p++; image = p + 1; /* c + 1 */ if (gtop->format & GTAGS_COMPRESS) image = (char *)uncompress(image, gtp->tag); } convert_put_using(cv, tagname, gtp->path, gtp->lineno, image); count++; } } convert_close(cv); if (sb) strbuf_close(sb); if (ib) strbuf_close(ib); if (fp) fclose(fp); gtags_close(gtop); return count; } /* * tagsearch: execute tag search * * i) pattern search pattern * i) cwd current directory * i) root root of source tree * i) dbpath database directory * i) db GTAGS,GRTAGS,GSYMS */ void tagsearch(const char *pattern, const char *cwd, const char *root, const char *dbpath, int db) { int count, total = 0; char libdbpath[MAXPATHLEN+1]; /* * search in current source tree. */ count = search(pattern, root, cwd, dbpath, db); total += count; /* * search in library path. */ if (db == GTAGS && getenv("GTAGSLIBPATH") && (count == 0 || Tflag) && !lflag) { STRBUF *sb = strbuf_open(0); char *libdir, *nextp = NULL; strbuf_puts(sb, getenv("GTAGSLIBPATH")); /* * search for each tree in the library path. */ for (libdir = strbuf_value(sb); libdir; libdir = nextp) { if ((nextp = locatestring(libdir, PATHSEP, MATCH_FIRST)) != NULL) *nextp++ = 0; if (!gtagsexist(libdir, libdbpath, sizeof(libdbpath), 0)) continue; if (!strcmp(dbpath, libdbpath)) continue; if (!test("f", makepath(libdbpath, dbname(db), NULL))) continue; /* * search again */ count = search(pattern, libdir, cwd, libdbpath, db); total += count; if (count > 0 && !Tflag) { /* for verbose message */ dbpath = libdbpath; break; } } strbuf_close(sb); } if (vflag) { print_count(total); if (!Tflag) fprintf(stderr, " (using '%s')", makepath(dbpath, dbname(db), NULL)); fputs(".\n", stderr); } } /* * encode: string copy with converting blank chars into %ff format. * * o) to result * i) size size of 'to' buffer * i) from string */ void encode(char *to, int size, const char *from) { const char *p; char *e = to; for (p = from; *p; p++) { if (*p == '%' || *p == ' ' || *p == '\t') { if (size <= 3) break; snprintf(e, size, "%%%02x", *p); e += 3; size -= 3; } else { if (size <= 1) break; *e++ = *p; size--; } } *e = 0; }