/* * Execute global(1) and write the output to the 'sb' string buffer. * * o) sb output * i) com cscope command (0-8) * i) opt option for global(1) * i) arg argument for global(1) * r) number of output */ static int execute_command(STRBUF *sb, const int com, const int opt, const char *arg) { #ifdef _WIN32 #define QUOTE '"' #else #define QUOTE '\'' #endif STATIC_STRBUF(command); STATIC_STRBUF(ib); FILE *ip; int count = 0; strbuf_clear(command); strbuf_puts(command, global_path); strbuf_puts(command, " --result=cscope"); if (opt == FROM_HERE) { strbuf_puts(command, " --from-here="); strbuf_puts(command, context); } else if (opt) { strbuf_puts(command, " -"); strbuf_putc(command, opt); } if (ignore_case) strbuf_puts(command, " --ignore-case"); strbuf_putc(command, ' '); strbuf_putc(command, QUOTE); strbuf_puts(command, arg); strbuf_putc(command, QUOTE); if (!(ip = popen(strbuf_value(command), "r"))) die("cannot execute '%s'.", strbuf_value(command)); if (vflag) fprintf(stderr, "gscope: %s\n", strbuf_value(command)); /* * Copy records with little modification. */ strbuf_clear(ib); while (strbuf_fgets(ib, ip, 0)) { count++; if (opt == 0) { strbuf_puts(sb, strbuf_value(ib)); } else { char *p = strbuf_value(ib); /* path name */ while (*p && *p != ' ') strbuf_putc(sb, *p++); if (*p != ' ') die("illegal cscope format. (%s)", strbuf_value(ib)); strbuf_putc(sb, *p++); /* replace pattern with "<unknown>" or "<global>" */ while (*p && *p != ' ') p++; if (*p != ' ') die("illegal cscope format. (%s)", strbuf_value(ib)); if (com == '8') strbuf_puts(sb, "<global>"); else strbuf_puts(sb, "<unknown>"); strbuf_putc(sb, *p++); /* line number */ while (*p && *p != ' ') strbuf_putc(sb, *p++); if (*p != ' ') die("illegal cscope format. (%s)", strbuf_value(ib)); strbuf_putc(sb, *p++); /* line image */ if (*p == '\n') strbuf_puts(sb, "<unknown>\n"); else strbuf_puts(sb, p); } } if (pclose(ip) < 0) die("terminated abnormally."); return count; }
/** * execute_command * * @param[in] xp xargs structure * @return !=NULL: file pointer <br> * ==NULL: end of argument * * This function constructs command line from the following, <br> * @STRONG{command}: @CODE{xp-\>command} <br> * @STRONG{argument}: each argument provider <br> * execute it on a pipe line, and return the file pointer. */ static FILE * execute_command(XARGS *xp) { int limit; STRBUF *comline = strbuf_open(0); int count = 0; int length; FILE *pipe = NULL; char *p, *meta_p; #if defined(_WIN32) && !defined(__CYGWIN__) /* * If the command starts with a quote, CMD.EXE requires the entire * command line to be quoted. */ if (*xp->command == '"') strbuf_putc(comline, '"'); #endif /* * Copy the part before '%s' of the command skeleton. * The '%s' in the skeleton is replaced with given arguments. */ meta_p = locatestring(xp->command, "%s", MATCH_FIRST); if (meta_p) { strbuf_nputs(comline, xp->command, meta_p - xp->command); limit = exec_line_limit(strlen(meta_p + 2)); } else { strbuf_puts(comline, xp->command); limit = exec_line_limit(0); } /* * Append arguments as many as possible. */ switch (xp->type) { case XARGS_FILE: for ( /* initial */ fseek(xp->ip, xp->fptr, SEEK_SET) ; /* continuation condition */ (LT_MAX && ((p = (strbuf_getlen(xp->path) > 0 ? strbuf_value(xp->path) : strbuf_fgets(xp->path, xp->ip, STRBUF_NOCRLF))) != NULL)) ; /* preparation */ strbuf_reset(xp->path) ) APPEND_ARGUMENT(p); xp->fptr = ftell(xp->ip); break; case XARGS_ARGV: for (; LT_MAX && xp->argc > 0; xp->argc--, xp->argv++) APPEND_ARGUMENT(xp->argv[0]) break; case XARGS_STRBUF: for (; LT_MAX && xp->curp < xp->endp; xp->curp += length + 1) APPEND_ARGUMENT(xp->curp) break; case XARGS_FIND: for (; LT_MAX && (p = repeat_find_read()) != NULL; repeat_find_next()) APPEND_ARGUMENT(p) break; } /* * Copy the left part of the command skeleton. */ if (meta_p) { strbuf_putc(comline, ' '); strbuf_puts(comline, meta_p + 2); } #if defined(_WIN32) && !defined(__CYGWIN__) if (*xp->command == '"') strbuf_putc(comline, '"'); #endif if (count > 0) { pipe = popen(strbuf_value(comline), "r"); if (pipe == NULL) die("cannot execute command '%s'.", strbuf_value(comline)); } strbuf_close(comline); return pipe; }
int main(int argc, char **argv) { const char *av = NULL; int db; int optchar; int option_index = 0; int status = 0; /* * get path of following directories. * o current directory * o root of source tree * o dbpath directory * * if GTAGS not found, exit with an error message. */ status = setupdbpath(0); if (status == 0) { cwd = get_cwd(); root = get_root(); dbpath = get_dbpath(); } /* * Setup GTAGSCONF and GTAGSLABEL environment variable * according to the --gtagsconf and --gtagslabel option. */ preparse_options(argc, argv); /* * Open configuration file. */ openconf(root); setenv_from_config(); logging_arguments(argc, argv); while ((optchar = getopt_long(argc, argv, "acde:EifFgGIlL:MnoOpPqrsS:tTuvVx", long_options, &option_index)) != EOF) { switch (optchar) { case 0: break; case 'a': aflag++; break; case 'c': cflag++; setcom(optchar); break; case 'd': dflag++; break; case 'e': av = optarg; break; case 'E': Gflag = 0; break; case 'f': fflag++; xflag++; setcom(optchar); break; case 'F': Tflag = 0; break; case 'g': gflag++; setcom(optchar); break; case 'G': Gflag++; break; case 'i': iflag++; break; case 'I': Iflag++; setcom(optchar); break; case 'l': Sflag++; scope = "."; break; case 'L': file_list = optarg; break; case 'M': Mflag++; iflag = 0; 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 '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 'S': Sflag++; scope = optarg; break; case 't': tflag++; break; case 'T': Tflag++; break; case 'u': uflag++; setcom(optchar); break; case 'v': vflag++; setverbose(); break; case 'V': Vflag++; break; case 'x': xflag++; break; case OPT_USE_COLOR: if (optarg) { if (!strcmp(optarg, "never")) use_color = 0; else if (!strcmp(optarg, "always")) use_color = 1; else if (!strcmp(optarg, "auto")) use_color = 2; else die_with_code(2, "unknown color type."); } else { use_color = 2; } break; case OPT_ENCODE_PATH: encode_chars = optarg; break; case OPT_FROM_HERE: { char *p = optarg; const char *usage = "usage: global --from-here=lineno:path."; context_lineno = p; while (*p && isdigit(*p)) p++; if (*p != ':') die_with_code(2, usage); *p++ = '\0'; if (!*p) die_with_code(2, usage); context_file = p; } break; case OPT_GTAGSCONF: case OPT_GTAGSLABEL: /* These options are already parsed in preparse_options() */ break; case OPT_MATCH_PART: if (!strcmp(optarg, "first")) match_part = MATCH_PART_FIRST; else if (!strcmp(optarg, "last")) match_part = MATCH_PART_LAST; else if (!strcmp(optarg, "all")) match_part = MATCH_PART_ALL; else die_with_code(2, "unknown part type for the --match-part option."); break; case OPT_PATH_CONVERT: do_path = 1; if (!strcmp("absolute", optarg)) convert_type = PATH_ABSOLUTE; else if (!strcmp("relative", optarg)) convert_type = PATH_RELATIVE; else if (!strcmp("through", optarg)) convert_type = PATH_THROUGH; else die("Unknown path type."); break; case OPT_PATH_STYLE: path_style = optarg; break; case OPT_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, "ctags-mod")) format = FORMAT_CTAGS_MOD; 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; case OPT_SINGLE_UPDATE: single_update = optarg; break; default: usage(); break; } } if (qflag) vflag = 0; if (show_version) version(av, vflag); if (show_help) help(); if (dbpath == NULL) die_with_code(-status, gtags_dbpath_error); /* * 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; } } /* * GTAGSBLANKENCODE will be used in less(1). */ switch (format) { case FORMAT_CTAGS_X: case FORMAT_CTAGS_XID: if (encode_chars == NULL && getenv("GTAGSBLANKENCODE")) encode_chars = " \t"; break; } if (encode_chars) { if (strlen(encode_chars) > 255) die("too many encode chars."); if (strchr(encode_chars, '/') || strchr(encode_chars, '.')) warning("cannot encode '/' and '.' in the path. Ignored."); set_encode_chars((unsigned char *)encode_chars); } if (getenv("GTAGSTHROUGH")) Tflag++; if (use_color) { #if defined(_WIN32) && !defined(__CYGWIN__) if (!(getenv("ANSICON") || LoadLibrary("ANSI32.dll")) && use_color == 2) use_color = 0; #endif if (use_color == 2 && !isatty(1)) use_color = 0; if (Vflag) use_color = 0; } argc -= optind; argv += optind; /* * Path filter */ if (do_path) { /* * This code is needed for globash.rc. * 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 follows: * * 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 --path-convert=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); CONVERT *cv; char *ctags_x; if (argc < 3) die("global --path-convert: 3 arguments needed."); cv = convert_open(convert_type, FORMAT_CTAGS_X, argv[0], argv[1], argv[2], stdout, NOTAGS); while ((ctags_x = strbuf_fgets(ib, stdin, STRBUF_NOCRLF)) != NULL) convert_put(cv, ctags_x); convert_close(cv); strbuf_close(ib); exit(0); } /* * At first, we pickup pattern from -e option. If it is not found * then use argument which is not option. */ if (!av) { av = *argv; /* * global -g pattern [files ...] * av argv */ if (gflag && av) argv++; } if (single_update) { if (command == 0) { uflag++; command = 'u'; } else if (command != 'u') { ; /* ignored */ } } /* * only -c, -u, -P and -p allows no argument. */ if (!av) { switch (command) { case 'c': case 'u': case 'p': case 'P': break; case 'f': if (file_list) 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 */ if (print0) set_print0(); if (cflag && match_part == 0) match_part = MATCH_PART_ALL; /* * remove leading blanks. */ if (!Iflag && !gflag && av) for (; *av == ' ' || *av == '\t'; av++) ; if (cflag && !Pflag && av && isregex(av)) die_with_code(2, "only name char is allowed with -c option."); /* * 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_path = usable("gtags"); if (!gtags_path) die("gtags command not found."); if (chdir(root) < 0) die("cannot change directory to '%s'.", root); #if defined(_WIN32) && !defined(__CYGWIN__) /* * Get around CMD.EXE's weird quoting rules by sticking another * perceived whitespace in front (also works with Take Command). */ strbuf_putc(sb, ';'); #endif strbuf_puts(sb, quote_shell(gtags_path)); strbuf_puts(sb, " -i"); if (vflag) strbuf_puts(sb, " -v"); if (single_update) { if (!isabspath(single_update)) { static char regular_path_name[MAXPATHLEN]; if (rel2abs(single_update, cwd, regular_path_name, sizeof(regular_path_name)) == NULL) die("rel2abs failed."); single_update = regular_path_name; } strbuf_puts(sb, " --single-update "); strbuf_puts(sb, quote_shell(single_update)); } strbuf_putc(sb, ' '); strbuf_puts(sb, quote_shell(dbpath)); if (system(strbuf_value(sb))) exit(1); strbuf_close(sb); exit(0); } /* * decide tag type. */ if (context_file) { if (isregex(av)) die_with_code(2, "regular expression is not allowed with the --from-here option."); db = decide_tag_by_context(av, context_file, atoi(context_lineno)); } else { if (dflag) db = GTAGS; else if (rflag && sflag) db = GRTAGS + GSYMS; else db = (rflag) ? GRTAGS : ((sflag) ? GSYMS : GTAGS); } /* * complete function name */ if (cflag) { if (Iflag) completion_idutils(dbpath, root, av); else if (Pflag) completion_path(dbpath, av); else completion(dbpath, root, av, db); exit(0); } /* * make local prefix. * local prefix must starts with './' and ends with '/'. */ if (Sflag) { STRBUF *sb = strbuf_open(0); static char buf[MAXPATHLEN]; const char *path = scope; /* * normalize the path of scope directory. */ if (!test("d", path)) die("'%s' not found or not a directory.", scope); if (!isabspath(path)) path = makepath(cwd, path, NULL); if (realpath(path, buf) == NULL) die("cannot get real path of '%s'.", scope); if (!in_the_project(buf)) die("'%s' is out of the source project.", scope); scope = buf; /* * make local prefix. */ strbuf_putc(sb, '.'); if (strcmp(root, scope) != 0) { const char *p = scope + 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 } /* * convert the file-list path into an absolute path. */ if (file_list && strcmp(file_list, "-") && !isabspath(file_list)) { static char buf[MAXPATHLEN]; if (realpath(file_list, buf) == NULL) die("'%s' not found.", file_list); file_list = buf; } /* * decide path conversion type. */ if (nofilter & PATH_FILTER) type = PATH_THROUGH; else if (aflag) type = PATH_ABSOLUTE; else type = PATH_RELATIVE; if (path_style) { if (!strcmp(path_style, "relative")) type = PATH_RELATIVE; else if (!strcmp(path_style, "absolute")) type = PATH_ABSOLUTE; else if (!strcmp(path_style, "through")) type = PATH_THROUGH; else if (!strcmp(path_style, "shorter")) type = PATH_SHORTER; else if (!strcmp(path_style, "abslib")) { type = PATH_RELATIVE; abslib++; } else die("invalid path style."); } /* * exec lid(idutils). */ if (Iflag) { chdir(root); idutils(av, dbpath); } /* * search pattern (regular expression). */ else if (gflag) { chdir(root); grep(av, argv, dbpath); } /* * locate paths including the pattern. */ else if (Pflag) { chdir(root); pathlist(av, dbpath); } /* * parse source files. */ else if (fflag) { chdir(root); parsefile(argv, cwd, root, dbpath, db); } /* * tag search. */ else { tagsearch(av, cwd, root, dbpath, db); } return 0; }
int decide_tag_by_context(const char *tag, const char *file, int lineno) { STRBUF *sb = NULL; char path[MAXPATHLEN], s_fid[MAXFIDLEN]; const char *tagline, *p; DBOP *dbop; int db = GSYMS; int iscompline = 0; if (normalize(file, get_root_with_slash(), cwd, path, sizeof(path)) == NULL) die("'%s' is out of the source project.", file); /* * get file id */ if (gpath_open(dbpath, 0) < 0) die("GPATH not found."); if ((p = gpath_path2fid(path, NULL)) == NULL) die("path name in the context is not found."); strlimcpy(s_fid, p, sizeof(s_fid)); gpath_close(); /* * read btree records directly to avoid the overhead. */ dbop = dbop_open(makepath(dbpath, dbname(GTAGS), NULL), 0, 0, 0); if (dbop == NULL) die("cannot open GTAGS."); if (dbop_getoption(dbop, COMPLINEKEY)) iscompline = 1; tagline = dbop_first(dbop, tag, NULL, 0); if (tagline) { db = GTAGS; for (; tagline; tagline = dbop_next(dbop)) { /* * examine whether the definition record include the context. */ p = locatestring(tagline, s_fid, MATCH_AT_FIRST); if (p != NULL && *p == ' ') { for (p++; *p && *p != ' '; p++) ; if (*p++ != ' ' || !isdigit(*p)) die("Impossible! decide_tag_by_context(1)"); /* * Standard format n <blank> <image>$ * Compact format d,d,d,d$ */ if (!iscompline) { /* Standard format */ if (atoi(p) == lineno) { db = GRTAGS; goto finish; } } else { /* Compact format */ int n, cur, last = 0; do { if (!isdigit(*p)) die("Impossible! decide_tag_by_context(2)"); NEXT_NUMBER(p); cur = last + n; if (cur == lineno) { db = GRTAGS; goto finish; } last = cur; if (*p == '-') { if (!isdigit(*++p)) die("Impossible! decide_tag_by_context(3)"); NEXT_NUMBER(p); cur = last + n; if (lineno >= last && lineno <= cur) { db = GRTAGS; goto finish; } last = cur; } if (*p) { if (*p == ',') p++; else die("Impossible! decide_tag_by_context(4)"); } } while (*p); } } } } finish: dbop_close(dbop); if (db == GSYMS && getenv("GTAGSLIBPATH")) { char libdbpath[MAXPATHLEN]; char *libdir = NULL, *nextp = NULL; sb = strbuf_open(0); strbuf_puts(sb, getenv("GTAGSLIBPATH")); 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; dbop = dbop_open(makepath(libdbpath, dbname(GTAGS), NULL), 0, 0, 0); if (dbop == NULL) continue; tagline = dbop_first(dbop, tag, NULL, 0); dbop_close(dbop); if (tagline != NULL) { db = GTAGS; break; } } strbuf_close(sb); } return db; }
/** * tagsearch: execute tag search * * @param[in] pattern search pattern * @param[in] cwd current directory * @param[in] root root of source tree * @param[in] dbpath database directory * @param[in] 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 buffer[IDENTLEN], *p = buffer; char libdbpath[MAXPATHLEN]; /* * trim pattern (^<no regex>$ => <no regex>) */ if (pattern) { strlimcpy(p, pattern, sizeof(buffer)); if (*p++ == '^') { char *q = p + strlen(p); if (*--q == '$') { *q = 0; if (*p == 0 || !isregex(p)) pattern = p; } } } /* * search in current source tree. */ count = search(pattern, root, cwd, dbpath, db); total += count; /* * search in library path. */ if (abslib) type = PATH_ABSOLUTE; if (db == GTAGS && getenv("GTAGSLIBPATH") && (count == 0 || Tflag) && !Sflag) { 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); } }
/** * idutils: @NAME{lid}(@NAME{idutils}) pattern * * @param[in] pattern @NAME{POSIX} regular expression * @param[in] dbpath @FILE{GTAGS} directory */ void idutils(const char *pattern, const char *dbpath) { FILE *ip; CONVERT *cv; STRBUF *ib = strbuf_open(0); char encoded_pattern[IDENTLEN]; char path[MAXPATHLEN]; const char *lid; int linenum, count; char *p, *q, *grep; lid = usable("lid"); if (!lid) die("lid(idutils) not found."); if (!test("f", makepath(dbpath, "ID", NULL))) die("ID file not found."); /* * convert spaces into %FF format. */ encode(encoded_pattern, sizeof(encoded_pattern), pattern); /* * make lid command line. * Invoke lid with the --result=grep option to generate grep format. */ #if _WIN32 || __DJGPP__ strbuf_puts(ib, lid); #else strbuf_sprintf(ib, "PWD=%s %s", quote_shell(root), lid); #endif strbuf_sprintf(ib, " --file=%s/ID", quote_shell(dbpath)); strbuf_puts(ib, " --separator=newline"); if (format == FORMAT_PATH) strbuf_puts(ib, " --result=filenames --key=none"); else strbuf_puts(ib, " --result=grep"); if (iflag) strbuf_puts(ib, " --ignore-case"); if (isregex(pattern)) strbuf_puts(ib, " --regexp"); strbuf_putc(ib, ' '); strbuf_puts(ib, quote_shell(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, NOTAGS); cv->tag_for_display = encoded_pattern; count = 0; strcpy(path, "./"); while ((grep = strbuf_fgets(ib, ip, STRBUF_NOCRLF)) != NULL) { q = path + 2; /* extract path name */ if (*grep == '/') die("The path in the output of lid is assumed relative.\n'%s'", grep); p = grep; while (*p && *p != ':') *q++ = *p++; *q = '\0'; if ((xflag || tflag) && !*p) die("invalid lid(idutils) output format(1). '%s'", grep); p++; if (Sflag) { if (!locatestring(path, localprefix, MATCH_AT_FIRST)) continue; } count++; switch (format) { case FORMAT_PATH: convert_put_path(cv, NULL, path); break; default: /* extract line number */ while (*p && isspace(*p)) p++; linenum = 0; for (linenum = 0; *p && isdigit(*p); linenum = linenum * 10 + (*p++ - '0')) ; if (*p != ':') die("invalid lid(idutils) output format(2). '%s'", grep); if (linenum <= 0) die("invalid lid(idutils) output format(3). '%s'", grep); p++; /* * print out. */ convert_put_using(cv, pattern, path, linenum, p, NULL); break; } } if (pclose(ip) != 0) die("terminated abnormally (errno = %d).", errno); convert_close(cv); strbuf_close(ib); if (vflag) { print_count(count); fprintf(stderr, " (using idutils index in '%s').\n", dbpath); } }
/** * Cpp: read C++ file and pickup tag entries. */ void Cpp(const struct parser_param *param) { int c, cc; int savelevel; int startclass, startthrow, startmacro, startsharp, startequal; char classname[MAXTOKEN]; char completename[MAXCOMPLETENAME]; char *completename_limit = &completename[sizeof(completename)]; int classlevel; struct { char *classname; char *terminate; int level; } stack[MAXCLASSSTACK]; const char *interested = "{}=;~"; STRBUF *sb = strbuf_open(0); *classname = *completename = 0; stack[0].classname = completename; stack[0].terminate = completename; stack[0].level = 0; level = classlevel = piflevel = namespacelevel = 0; savelevel = -1; startclass = startthrow = startmacro = startsharp = startequal = 0; if (!opentoken(param->file)) die("'%s' cannot open.", param->file); cmode = 1; /* allow token like '#xxx' */ crflag = 1; /* require '\n' as a token */ cppmode = 1; /* treat '::' as a token */ while ((cc = nexttoken(interested, cpp_reserved_word)) != EOF) { if (cc == '~' && level == stack[classlevel].level) continue; switch (cc) { case SYMBOL: /* symbol */ if (startclass || startthrow) { PUT(PARSER_REF_SYM, token, lineno, sp); } else if (peekc(0) == '('/* ) */) { if (param->isnotfunction(token)) { PUT(PARSER_REF_SYM, token, lineno, sp); } else if (level > stack[classlevel].level || startequal || startmacro) { PUT(PARSER_REF_SYM, token, lineno, sp); } else if (level == stack[classlevel].level && !startmacro && !startsharp && !startequal) { char savetok[MAXTOKEN], *saveline; int savelineno = lineno; strlimcpy(savetok, token, sizeof(savetok)); strbuf_reset(sb); strbuf_puts(sb, sp); saveline = strbuf_value(sb); if (function_definition(param)) { /* ignore constructor */ if (strcmp(stack[classlevel].classname, savetok)) PUT(PARSER_DEF, savetok, savelineno, saveline); } else { PUT(PARSER_REF_SYM, savetok, savelineno, saveline); } } } else { PUT(PARSER_REF_SYM, token, lineno, sp); } break; case CPP_USING: crflag = 0; /* * using namespace name; * using ...; */ if ((c = nexttoken(interested, cpp_reserved_word)) == CPP_NAMESPACE) { if ((c = nexttoken(interested, cpp_reserved_word)) == SYMBOL) { PUT(PARSER_REF_SYM, token, lineno, sp); } else { if (param->flags & PARSER_WARNING) warning("missing namespace name. [+%d %s].", lineno, curfile); pushbacktoken(); } } else if (c == SYMBOL) { char savetok[MAXTOKEN], *saveline; int savelineno = lineno; strlimcpy(savetok, token, sizeof(savetok)); strbuf_reset(sb); strbuf_puts(sb, sp); saveline = strbuf_value(sb); if ((c = nexttoken(interested, cpp_reserved_word)) == '=') { PUT(PARSER_DEF, savetok, savelineno, saveline); } else { PUT(PARSER_REF_SYM, savetok, savelineno, saveline); while (c == SYMBOL) { PUT(PARSER_REF_SYM, token, lineno, sp); c = nexttoken(interested, cpp_reserved_word); } } } else { pushbacktoken(); } crflag = 1; break; case CPP_NAMESPACE: crflag = 0; /* * namespace name = ...; * namespace [name] { ... } */ if ((c = nexttoken(interested, cpp_reserved_word)) == SYMBOL) { PUT(PARSER_DEF, token, lineno, sp); if ((c = nexttoken(interested, cpp_reserved_word)) == '=') { crflag = 1; break; } } /* * Namespace block doesn't have any influence on level. */ if (c == '{') { /* } */ namespacelevel++; } else { if (param->flags & PARSER_WARNING) warning("missing namespace block. [+%d %s](0x%x).", lineno, curfile, c); } crflag = 1; break; case CPP_EXTERN: /* for 'extern "C"/"C++"' */ if (peekc(0) != '"') /* " */ continue; /* If does not start with '"', continue. */ while ((c = nexttoken(interested, cpp_reserved_word)) == '\n') ; /* * 'extern "C"/"C++"' block is a kind of namespace block. * (It doesn't have any influence on level.) */ if (c == '{') /* } */ namespacelevel++; else pushbacktoken(); break; case CPP_STRUCT: case CPP_CLASS: DBG_PRINT(level, cc == CPP_CLASS ? "class" : "struct"); while ((c = nexttoken(NULL, cpp_reserved_word)) == CPP___ATTRIBUTE__ || c == '\n') if (c == CPP___ATTRIBUTE__) process_attribute(param); if (c == SYMBOL) { char *saveline; int savelineno; do { if (c == SYMBOL) { savelineno = lineno; strbuf_reset(sb); strbuf_puts(sb, sp); saveline = strbuf_value(sb); strlimcpy(classname, token, sizeof(classname)); } c = nexttoken(NULL, cpp_reserved_word); if (c == SYMBOL) PUT(PARSER_REF_SYM, classname, savelineno, saveline); else if (c == '<') { int templates = 1; for (;;) { c = nexttoken(NULL, cpp_reserved_word); if (c == SYMBOL) PUT(PARSER_REF_SYM, token, lineno, sp); if (c == '<') { if (peekc(1) == '<') throwaway_nextchar(); else ++templates; } else if (c == '>') { if (--templates == 0) break; } else if (c == EOF) { if (param->flags & PARSER_WARNING) warning("failed to parse template [+%d %s].", savelineno, curfile); goto finish; } } c = nexttoken(NULL, cpp_reserved_word); } } while (c == SYMBOL || c == '\n'); if (c == ':' || c == '{') { /* } */ startclass = 1; PUT(PARSER_DEF, classname, savelineno, saveline); } else PUT(PARSER_REF_SYM, classname, savelineno, saveline); } pushbacktoken(); break; case '{': /* } */ DBG_PRINT(level, "{"); /* } */ ++level; if ((param->flags & PARSER_BEGIN_BLOCK) && atfirst) { if ((param->flags & PARSER_WARNING) && level != 1) warning("forced level 1 block start by '{' at column 0 [+%d %s].", lineno, curfile); /* } */ level = 1; } if (startclass) { char *p = stack[classlevel].terminate; char *q = classname; if (++classlevel >= MAXCLASSSTACK) die("class stack over flow.[%s]", curfile); if (classlevel > 1 && p < completename_limit) *p++ = '.'; stack[classlevel].classname = p; while (*q && p < completename_limit) *p++ = *q++; stack[classlevel].terminate = p; stack[classlevel].level = level; *p++ = 0; } startclass = startthrow = 0; break; /* { */ case '}': if (--level < 0) { if (namespacelevel > 0) namespacelevel--; else if (param->flags & PARSER_WARNING) warning("missing left '{' [+%d %s].", lineno, curfile); /* } */ level = 0; } if ((param->flags & PARSER_END_BLOCK) && atfirst) { if ((param->flags & PARSER_WARNING) && level != 0) /* { */ warning("forced level 0 block end by '}' at column 0 [+%d %s].", lineno, curfile); level = 0; } if (level < stack[classlevel].level) *(stack[--classlevel].terminate) = 0; /* { */ DBG_PRINT(level, "}"); break; case '=': /* dirty hack. Don't mimic this. */ if (peekc(0) == '=') { throwaway_nextchar(); } else { startequal = 1; } break; case ';': startthrow = startequal = 0; break; case '\n': if (startmacro && level != savelevel) { if (param->flags & PARSER_WARNING) warning("different level before and after #define macro. reseted. [+%d %s].", lineno, curfile); level = savelevel; } startmacro = startsharp = 0; break; /* * #xxx */ case SHARP_DEFINE: case SHARP_UNDEF: startmacro = 1; savelevel = level; if ((c = nexttoken(interested, cpp_reserved_word)) != SYMBOL) { pushbacktoken(); break; } if (peekc(1) == '('/* ) */) { PUT(PARSER_DEF, token, lineno, sp); while ((c = nexttoken("()", cpp_reserved_word)) != EOF && c != '\n' && c != /* ( */ ')') if (c == SYMBOL) PUT(PARSER_REF_SYM, token, lineno, sp); if (c == '\n') pushbacktoken(); } else { PUT(PARSER_DEF, token, lineno, sp); } break; case SHARP_IMPORT: case SHARP_INCLUDE: case SHARP_INCLUDE_NEXT: case SHARP_ERROR: case SHARP_LINE: case SHARP_PRAGMA: case SHARP_WARNING: case SHARP_IDENT: case SHARP_SCCS: while ((c = nexttoken(interested, cpp_reserved_word)) != EOF && c != '\n') ; break; case SHARP_IFDEF: case SHARP_IFNDEF: case SHARP_IF: case SHARP_ELIF: case SHARP_ELSE: case SHARP_ENDIF: condition_macro(param, cc); break; case SHARP_SHARP: /* ## */ (void)nexttoken(interested, cpp_reserved_word); break; case CPP_NEW: if ((c = nexttoken(interested, cpp_reserved_word)) == SYMBOL) PUT(PARSER_REF_SYM, token, lineno, sp); break; case CPP_ENUM: case CPP_UNION: while ((c = nexttoken(interested, cpp_reserved_word)) == CPP___ATTRIBUTE__) process_attribute(param); while (c == '\n') c = nexttoken(interested, cpp_reserved_word); if (c == SYMBOL) { if (peekc(0) == '{') { /* } */ PUT(PARSER_DEF, token, lineno, sp); } else { PUT(PARSER_REF_SYM, token, lineno, sp); } c = nexttoken(interested, cpp_reserved_word); } while (c == '\n') c = nexttoken(interested, cpp_reserved_word); if (c == '{' /* } */ && cc == CPP_ENUM) { enumerator_list(param); } else { pushbacktoken(); } break; case CPP_TEMPLATE: { int level = 0; while ((c = nexttoken("<>", cpp_reserved_word)) != EOF) { if (c == '<') ++level; else if (c == '>') { if (--level == 0) break; } else if (c == SYMBOL) { PUT(PARSER_REF_SYM, token, lineno, sp); } } if (c == EOF && (param->flags & PARSER_WARNING)) warning("template <...> isn't closed. [+%d %s].", lineno, curfile); } break; case CPP_OPERATOR: while ((c = nexttoken(";{", /* } */ cpp_reserved_word)) != EOF) { if (c == '{') { /* } */ pushbacktoken(); break; } else if (c == ';') { break; } else if (c == SYMBOL) { PUT(PARSER_REF_SYM, token, lineno, sp); } } if (c == EOF && (param->flags & PARSER_WARNING)) warning("'{' doesn't exist after 'operator'. [+%d %s].", lineno, curfile); /* } */ break; /* control statement check */ case CPP_THROW: startthrow = 1; case CPP_BREAK: case CPP_CASE: case CPP_CATCH: case CPP_CONTINUE: case CPP_DEFAULT: case CPP_DELETE: case CPP_DO: case CPP_ELSE: case CPP_FOR: case CPP_GOTO: case CPP_IF: case CPP_RETURN: case CPP_SWITCH: case CPP_TRY: case CPP_WHILE: if ((param->flags & PARSER_WARNING) && !startmacro && level == 0) warning("Out of function. %8s [+%d %s]", token, lineno, curfile); break; case CPP_TYPEDEF: { /* * This parser is too complex to maintain. * We should rewrite the whole. */ char savetok[MAXTOKEN]; int savelineno = 0; int typedef_savelevel = level; int templates = 0; savetok[0] = 0; /* skip CV qualifiers */ do { c = nexttoken("{}(),;", cpp_reserved_word); } while (IS_CV_QUALIFIER(c) || c == '\n'); if ((param->flags & PARSER_WARNING) && c == EOF) { warning("unexpected eof. [+%d %s]", lineno, curfile); break; } else if (c == CPP_ENUM || c == CPP_STRUCT || c == CPP_UNION) { char *interest_enum = "{},;"; int c_ = c; while ((c = nexttoken(interest_enum, cpp_reserved_word)) == CPP___ATTRIBUTE__) process_attribute(param); while (c == '\n') c = nexttoken(interest_enum, cpp_reserved_word); /* read tag name if exist */ if (c == SYMBOL) { if (peekc(0) == '{') { /* } */ PUT(PARSER_DEF, token, lineno, sp); } else { PUT(PARSER_REF_SYM, token, lineno, sp); } c = nexttoken(interest_enum, cpp_reserved_word); } while (c == '\n') c = nexttoken(interest_enum, cpp_reserved_word); if (c_ == CPP_ENUM) { if (c == '{') /* } */ c = enumerator_list(param); else pushbacktoken(); } else { for (; c != EOF; c = nexttoken(interest_enum, cpp_reserved_word)) { switch (c) { case SHARP_IFDEF: case SHARP_IFNDEF: case SHARP_IF: case SHARP_ELIF: case SHARP_ELSE: case SHARP_ENDIF: condition_macro(param, c); continue; default: break; } if (c == ';' && level == typedef_savelevel) { if (savetok[0]) PUT(PARSER_DEF, savetok, savelineno, sp); break; } else if (c == '{') level++; else if (c == '}') { if (--level == typedef_savelevel) break; } else if (c == SYMBOL) { PUT(PARSER_REF_SYM, token, lineno, sp); /* save lastest token */ strlimcpy(savetok, token, sizeof(savetok)); savelineno = lineno; } } if (c == ';') break; } if ((param->flags & PARSER_WARNING) && c == EOF) { warning("unexpected eof. [+%d %s]", lineno, curfile); break; } } else if (c == SYMBOL) { PUT(PARSER_REF_SYM, token, lineno, sp); } savetok[0] = 0; while ((c = nexttoken("()<>,;", cpp_reserved_word)) != EOF) { switch (c) { case SHARP_IFDEF: case SHARP_IFNDEF: case SHARP_IF: case SHARP_ELIF: case SHARP_ELSE: case SHARP_ENDIF: condition_macro(param, c); continue; default: break; } if (c == '(') level++; else if (c == ')') level--; else if (c == '<') templates++; else if (c == '>') templates--; else if (c == SYMBOL) { if (level > typedef_savelevel) { PUT(PARSER_REF_SYM, token, lineno, sp); } else { /* put latest token if any */ if (savetok[0]) { PUT(PARSER_REF_SYM, savetok, savelineno, sp); } /* save lastest token */ strlimcpy(savetok, token, sizeof(savetok)); savelineno = lineno; } } else if (c == ',' || c == ';') { if (savetok[0]) { PUT(templates ? PARSER_REF_SYM : PARSER_DEF, savetok, lineno, sp); savetok[0] = 0; } } if (level == typedef_savelevel && c == ';') break; } if (param->flags & PARSER_WARNING) { if (c == EOF) warning("unexpected eof. [+%d %s]", lineno, curfile); else if (level != typedef_savelevel) warning("unmatched () block. (last at level %d.)[+%d %s]", level, lineno, curfile); } } break; case CPP___ATTRIBUTE__: process_attribute(param); break; default: break; } } finish: strbuf_close(sb); if (param->flags & PARSER_WARNING) { if (level != 0) warning("unmatched {} block. (last at level %d.)[+%d %s]", level, lineno, curfile); if (piflevel != 0) warning("unmatched #if block. (last at level %d.)[+%d %s]", piflevel, lineno, curfile); } closetoken(); }