📄 gtags.c
字号:
/* * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005 * Tama Communications Corporation * * This file is part of GNU GLOBAL. * * GNU GLOBAL 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 2, or (at your option) * any later version. * * GNU GLOBAL 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, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <sys/types.h>#include <sys/stat.h>#include <errno.h>#include <ctype.h>#include <utime.h>#include <signal.h>#include <stdio.h>#if TIME_WITH_SYS_TIME#include <sys/time.h>#include <time.h>#else#if HAVE_SYS_TIME_H#include <sys/time.h>#else#include <time.h>#endif#endif#ifdef STDC_HEADERS#include <stdlib.h>#endif#ifdef HAVE_STRING_H#include <string.h>#else#include <strings.h>#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#include "getopt.h"#include "global.h"#include "const.h"struct dup_entry { int offset; SPLIT ptable; int lineno; int skip;};static void usage(void);static void help(void);void signal_setup(void);void onintr(int);static int compare_dup_entry(const void *, const void *);static void put_lines(char *, struct dup_entry *, int);int main(int, char **);int incremental(const char *, const char *);void updatetags(const char *, const char *, IDSET *, STRBUF *, int, int);void createtags(const char *, const char *, int);int printconf(const char *);void set_base_directory(const char *, const char *);void put_converting(const char *, int, int);int cflag; /* compact format */int iflag; /* incremental update */int Iflag; /* make id-utils index */int oflag; /* suppress making GSYMS */int qflag; /* quiet mode */int wflag; /* warning message */int vflag; /* verbose mode */int max_args;int show_version;int show_help;int show_config;int do_convert;int do_find;int do_sort;int do_relative;int do_absolute;int cxref;int do_expand;int gtagsconf;int gtagslabel;int other_files;int debug;int secure_mode;const char *extra_options;const char *info_string;const char *file_list;int extractmethod;int total;static voidusage(void){ if (!qflag) fputs(usage_const, stderr); exit(2);}static voidhelp(void){ fputs(usage_const, stdout); fputs(help_const, stdout); exit(0);}static struct option const long_options[] = { {"compact", no_argument, NULL, 'c'}, {"file", required_argument, NULL, 'f'}, {"idutils", no_argument, NULL, 'I'}, {"incremental", no_argument, NULL, 'i'}, {"max-args", required_argument, NULL, 'n'}, {"omit-gsyms", no_argument, NULL, 'o'}, {"quiet", no_argument, NULL, 'q'}, {"verbose", no_argument, NULL, 'v'}, {"warning", no_argument, NULL, 'w'}, /* long name only */ {"absolute", no_argument, &do_absolute, 1}, {"config", optional_argument, &show_config, 1}, {"convert", no_argument, &do_convert, 1}, {"cxref", no_argument, &cxref, 1}, {"debug", no_argument, &debug, 1}, {"expand", required_argument, &do_expand, 1}, {"find", no_argument, &do_find, 1}, {"gtagsconf", required_argument, >agsconf, 1}, {"gtagslabel", required_argument, >agslabel, 1}, {"other", no_argument, &other_files, 1}, {"relative", no_argument, &do_relative, 1}, {"secure", no_argument, &secure_mode, 1}, {"sort", no_argument, &do_sort, 1}, {"version", no_argument, &show_version, 1}, {"help", no_argument, &show_help, 1}, { 0 }};/* * Gtags catch signal even if the parent ignore it. */int exitflag = 0;static const char *langmap = DEFAULTLANGMAP;voidonintr(signo) int signo;{ signo = 0; /* to satisfy compiler */ exitflag = 1;}voidsignal_setup(void){ signal(SIGINT, onintr); signal(SIGTERM, onintr);#ifdef SIGHUP signal(SIGHUP, onintr);#endif#ifdef SIGQUIT signal(SIGQUIT, onintr);#endif}/* * compare_dup_entry: compare function for sorting. */static intcompare_dup_entry(v1, v2) const void *v1; const void *v2;{ const struct dup_entry *e1 = v1, *e2 = v2; int ret; if ((ret = strcmp(e1->ptable.part[PART_PATH].start, e2->ptable.part[PART_PATH].start)) != 0) return ret; return e1->lineno - e2->lineno;}/* * put_lines: sort and print duplicate lines */static voidput_lines(lines, entries, entry_count) char *lines; struct dup_entry *entries; int entry_count;{ int i; for (i = 0; i < entry_count; i++) { char *ctags_x = lines + entries[i].offset; SPLIT *ptable = &entries[i].ptable; if (split(ctags_x, 4, ptable) < 4) { recover(ptable); die("too small number of parts.\n'%s'", ctags_x); } entries[i].lineno = atoi(ptable->part[PART_LNO].start); entries[i].skip = 0; } qsort(entries, entry_count, sizeof(struct dup_entry), compare_dup_entry); for (i = 1; i < entry_count; i++) { if (entries[i].lineno == entries[i - 1].lineno && strcmp(entries[i].ptable.part[PART_PATH].start, entries[i - 1].ptable.part[PART_PATH].start) == 0) entries[i].skip = 1; } for (i = 0; i < entry_count; i++) { recover(&entries[i].ptable); if (!entries[i].skip) puts(lines + entries[i].offset); }}intmain(argc, argv) int argc; char *argv[];{ char root[MAXPATHLEN+1]; char dbpath[MAXPATHLEN+1]; char cwd[MAXPATHLEN+1]; STRBUF *sb = strbuf_open(0); const char *p; int db; int optchar; int option_index = 0; while ((optchar = getopt_long(argc, argv, "cf:iIn:oqvwse", long_options, &option_index)) != EOF) { switch (optchar) { case 0: p = long_options[option_index].name; if (!strcmp(p, "expand")) { settabs(atoi(optarg + 1)); } else if (!strcmp(p, "config")) { if (optarg) info_string = optarg; } else if (gtagsconf || gtagslabel) { char value[MAXPATHLEN+1]; const char *name = (gtagsconf) ? "GTAGSCONF" : "GTAGSLABEL"; if (gtagsconf) { if (realpath(optarg, value) == NULL) die("%s not found.", optarg); } else { strlimcpy(value, optarg, sizeof(value)); } set_env(name, value); gtagsconf = gtagslabel = 0; } break; case 'c': cflag++; break; case 'f': file_list = optarg; break; case 'i': iflag++; break; case 'I': Iflag++; break; case 'n': max_args = atoi(optarg); if (max_args <= 0) die("--max-args option requires number > 0."); break; case 'o': oflag++; break; case 'q': qflag++; setquiet(); break; case 'w': wflag++; break; case 'v': vflag++; break; /* for compatibility */ case 's': case 'e': break; default: usage(); break; } } if (qflag) vflag = 0; if (show_version) version(NULL, vflag); if (show_help) help(); argc -= optind; argv += optind; if (show_config) { if (!info_string && argc) info_string = argv[0]; if (info_string) { printconf(info_string); } else { fprintf(stdout, "%s\n", getconfline()); } exit(0); } else if (do_convert) { STRBUF *ib = strbuf_open(MAXBUFLEN); const char *fid; char *p, *q; int c; /* * [Job] * * Read line from stdin and replace " ./<file name> " * with the file number like this. * * <a href='http://xxx/global/S/ ./main.c .html#110'>main</a>\n * | * v * <a href='http://xxx/global/S/39.html#110'>main</a>\n * * If the file name is not found in GPATH, change into the path to CGI script. * <a href='http://xxx/global/S/ ./README .html#9'>main</a>\n * | * v * <a href='http://xxx/global/cgi-bin/global.cgi?pattern=README&type=source#9'>main</a>\n */ if (gpath_open(".", 0) < 0) die("GPATH not found."); while (strbuf_fgets(ib, stdin, 0) != NULL) { p = strbuf_value(ib); if (!locatestring(p, "<a ", MATCH_AT_FIRST)) continue; q = locatestring(p, "/S/ ", MATCH_FIRST); if (q == NULL) { printf("%s: ERROR(1): %s", progname, strbuf_value(ib)); continue; } /* Print just before "/S/ " and skip "/S/ ". */ for (; p < q; p++) putc(*p, stdout); for (; *p && *p != ' '; p++) ; /* Extract path name. */ for (q = ++p; *q && *q != ' '; q++) ; if (*q == '\0') { printf("%s: ERROR(2): %s", progname, strbuf_value(ib)); continue; } *q++ = '\0'; /* * Convert path name into URL. * The output of 'global -xgo' may include lines about * files other than source code. In this case, file id * doesn't exist in GPATH. */ fid = gpath_path2fid(p); if (fid) { fputs("/S/", stdout); fputs(fid, stdout); fputs(q, stdout); } else { fputs("/cgi-bin/global.cgi?pattern=", stdout); p += 2; while ((c = (unsigned char)*p++) != '\0') { if (isalnum(c)) putc(c, stdout); else printf("%%%02x", c); } fputs("&type=source", stdout); for (; *q && *q != '#'; q++) ; if (*q == '\0') { printf("%s: ERROR(2): %s", progname, strbuf_value(ib)); continue; } fputs(q, stdout); } } gpath_close(); strbuf_close(ib); exit(0); } else if (do_expand) { /* * The 'gtags --expand' is nearly equivalent with 'expand'. * We made this command to decrease dependency to external * command. But now, the --secure option use this command * positively. */ FILE *ip; STRBUF *ib = strbuf_open(MAXBUFLEN); if (argc) { if (secure_mode) { char buf[MAXPATHLEN+1], *path; char rootdir[MAXPATHLEN+1]; getdbpath(cwd, root, dbpath, 0); snprintf(rootdir, sizeof(rootdir), "%s/", root); path = realpath(argv[0], buf); if (path == NULL) die("realpath(%s, buf) failed. (errno=%d).", argv[0], errno); if (!isabspath(path)) die("realpath(3) is not compatible with BSD version."); if (!locatestring(path, rootdir, MATCH_AT_FIRST)) die("'%s' is out of source tree.", path); } ip = fopen(argv[0], "r"); if (ip == NULL) exit(1); } else ip = stdin; while (strbuf_fgets(ib, ip, STRBUF_NOCRLF) != NULL) detab(stdout, strbuf_value(ib)); strbuf_close(ib); exit(0); } else if (do_find) { /* * This code is used by htags(1) to traverse file system. * * If the --other option is not specified, 'gtags --find' * read GPATH instead of traversing file. But if the option * is specified, it traverse file system every time. * It is because gtags doesn't record the paths other than * source file in GPATH. * Since it is slow, gtags should record not only source * files but also other files in GPATH in the future. * But it needs adding a new format version. */ const char *path; const char *local = (argc) ? argv[0] : NULL; for (vfind_open(local, other_files); (path = vfind_read()) != NULL; ) { fputs(path, stdout); fputc('\n', stdout); } vfind_close(); exit(0); } else if (do_sort) { /* * This code and the makedupindex() in htags(1) compose * a pipeline 'global -x ".*" | gtags --sort'. * The 'gtags --sort' is equivalent with 'sort -k 1,1 -k 3,3 -k 2,2n -u' * but the latter is ineffective and needs a lot of temporary * files when applied to a huge file. (According to circumstances, * hundreds of files are generated.) * * Utilizing the feature that the output of 'global -x ".*"' * is already sorted in alphabetical order by tag name, * we splited the output into relatively small unit and * execute sort for each unit. */ STRBUF *ib = strbuf_open(MAXBUFLEN); STRBUF *sb = strbuf_open(MAXBUFLEN); VARRAY *vb = varray_open(sizeof(struct dup_entry), 100); char *ctags_x, prev[IDENTLEN]; prev[0] = '\0'; while ((ctags_x = strbuf_fgets(ib, stdin, STRBUF_NOCRLF)) != NULL) { const char *tag; struct dup_entry *entry; SPLIT ptable; if (split(ctags_x, 2, &ptable) < 2) { recover(&ptable); die("too small number of parts.\n'%s'", ctags_x); } tag = ptable.part[PART_TAG].start; if (prev[0] == '\0' || strcmp(prev, tag) != 0) { if (prev[0] != '\0') { if (vb->length == 1) puts(strbuf_value(sb)); else put_lines(strbuf_value(sb), varray_assign(vb, 0, 0), vb->length); } strlimcpy(prev, tag, sizeof(prev)); strbuf_reset(sb); varray_reset(vb); } entry = varray_append(vb); entry->offset = strbuf_getlen(sb); recover(&ptable); strbuf_puts0(sb, ctags_x); } if (prev[0] != '\0') { if (vb->length == 1) puts(strbuf_value(sb)); else put_lines(strbuf_value(sb), varray_assign(vb, 0, 0), vb->length); } strbuf_close(ib); strbuf_close(sb); varray_close(vb); exit(0); } else if (do_relative || do_absolute) { /* * This is the main body of path filter. * This code extract path name from tag line and * replace it with the relative or the absolute path name. * * By default, if we are in src/ directory, the output * should be converted like follws: * * main 10 ./src/main.c main(argc, argv)\n * main 22 ./libc/func.c main(argc, argv)\n * v * main 10 main.c main(argc, argv)\n * main 22 ../libc/func.c main(argc, argv)\n * * Similarly, the --absolute option specified, then * v * main 10 /prj/xxx/src/main.c main(argc, argv)\n * main 22 /prj/xxx/libc/func.c main(argc, argv)\n */ STRBUF *ib = strbuf_open(MAXBUFLEN); const char *root = argv[0]; const char *cwd = argv[1]; const char *ctags_x; if (argc < 2) die("do_relative: 2 arguments needed."); set_base_directory(root, cwd); while ((ctags_x = strbuf_fgets(ib, stdin, 0)) != NULL) put_converting(ctags_x, do_absolute ? 1 : 0, cxref); strbuf_close(ib); exit(0); } else if (Iflag) { if (!usable("mkid")) die("mkid not found."); } /* * If the file_list other than "-" is given, it must be readable file. */ if (file_list && strcmp(file_list, "-")) { if (test("d", file_list)) die("'%s' is a directory.", file_list); else if (!test("f", file_list)) die("'%s' not found.", file_list); else if (!test("r", file_list)) die("'%s' is not readable.", file_list); } if (!getcwd(cwd, MAXPATHLEN)) die("cannot get current directory."); canonpath(cwd); /* * Decide directory (dbpath) in which gtags make tag files. * * Gtags create tag files at current directory by default. * If dbpath is specified as an argument then use it. * If the -i option specified and both GTAGS and GRTAGS exists * at one of the candedite directories then gtags use existing * tag files. */ if (iflag) { if (argc > 0) realpath(*argv, dbpath); else if (!gtagsexist(cwd, dbpath, MAXPATHLEN, vflag)) strlimcpy(dbpath, cwd, sizeof(dbpath)); } else { if (argc > 0) realpath(*argv, dbpath); else strlimcpy(dbpath, cwd, sizeof(dbpath)); } if (iflag && (!test("f", makepath(dbpath, dbname(GTAGS), NULL)) || !test("f", makepath(dbpath, dbname(GPATH), NULL)))) { if (wflag) warning("GTAGS or GPATH not found. -i option ignored."); iflag = 0; } if (!test("d", dbpath)) die("directory '%s' not found.", dbpath); if (vflag) fprintf(stderr, "[%s] Gtags started.\n", now()); /* * load .globalrc or /etc/gtags.conf */ openconf();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -