/* execute a multicharacter command. */ void exec_mca(void) { char *p, *fn, *patt; *cp = '\0'; CMD_EXEC; switch (mca) { case A_F_SEARCH: (void)search(1, cmdbuf, number, wsearch); break; case A_B_SEARCH: (void)search(0, cmdbuf, number, wsearch); break; case A_EXAMINE: for (p = cmdbuf; isspace(*p); ++p) ; edit(glob(p)); break; case A_TAGFILE: for (p = cmdbuf; isspace(*p); ++p); findtag(p, &fn, &patt); if (fn == NULL) break; if (edit(fn)) (void)tagsearch(patt); break; } }
/* * Handler for -t option. */ void opt_t(int type, char *s) { IFILE save_ifile; off_t pos; switch (type) { case INIT: tagoption = s; /* Do the rest in main() */ break; case TOGGLE: if (secure) { error("tags support is not available", NULL_PARG); break; } findtag(skipsp(s)); save_ifile = save_curr_ifile(); /* * Try to open the file containing the tag * and search for the tag in that file. */ if (edit_tagfile() || (pos = tagsearch()) == -1) { /* Failed: reopen the old file. */ reedit_ifile(save_ifile); break; } unsave_ifile(save_ifile); jump_loc(pos, jump_sline); break; } }
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:ifgGIlL:noOpPqrstTuvVx", 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 'g': gflag++; setcom(optchar); break; case 'G': Gflag++; break; case 'i': iflag++; break; case 'I': Iflag++; setcom(optchar); break; case 'l': lflag++; break; case 'L': file_list = optarg; 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 't': tflag++; break; case 'T': Tflag++; break; case 'u': uflag++; setcom(optchar); break; case 'v': vflag++; break; case 'V': Vflag++; break; case 'x': xflag++; break; case ENCODE_PATH: if (strlen(optarg) > 255) die("too many encode chars."); if (strchr(optarg, '/') || strchr(optarg, '.')) die("cannot encode '/' and '.' in the path."); set_encode_chars((unsigned char *)optarg); break; case 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 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 = *argv; /* * global -g pattern [files ...] * av argv */ if (gflag && av) argv++; } if (show_version) version(av, vflag); /* * 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(); /* * 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)); /* * 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) { if (Iflag) completion_idutils(dbpath, root, av); else 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. */ 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 (rflag && sflag) db = GRTAGS + GSYMS; else 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, 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; }
/* * Main command processor. * Accept and execute commands until a quit command. */ void commands(void) { int c = 0; int action; char *cbuf; int newaction; int save_search_type; char *extra; char tbuf[2]; PARG parg; IFILE old_ifile; IFILE new_ifile; char *tagfile; search_type = SRCH_FORW; wscroll = (sc_height + 1) / 2; newaction = A_NOACTION; for (;;) { mca = 0; cmd_accept(); number = 0; curropt = NULL; /* * See if any signals need processing. */ if (sigs) { psignals(); if (quitting) quit(QUIT_SAVED_STATUS); } /* * Display prompt and accept a character. */ cmd_reset(); prompt(); if (sigs) continue; if (newaction == A_NOACTION) c = getcc(); again: if (sigs) continue; if (newaction != A_NOACTION) { action = newaction; newaction = A_NOACTION; } else { /* * If we are in a multicharacter command, call mca_char. * Otherwise we call fcmd_decode to determine the * action to be performed. */ if (mca) switch (mca_char(c)) { case MCA_MORE: /* * Need another character. */ c = getcc(); goto again; case MCA_DONE: /* * Command has been handled by mca_char. * Start clean with a prompt. */ continue; case NO_MCA: /* * Not a multi-char command * (at least, not anymore). */ break; } /* * Decode the command character and decide what to do. */ if (mca) { /* * We're in a multichar command. * Add the character to the command buffer * and display it on the screen. * If the user backspaces past the start * of the line, abort the command. */ if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0) continue; cbuf = get_cmdbuf(); } else { /* * Don't use cmd_char if we're starting fresh * at the beginning of a command, because we * don't want to echo the command until we know * it is a multichar command. We also don't * want erase_char/kill_char to be treated * as line editing characters. */ tbuf[0] = (char)c; tbuf[1] = '\0'; cbuf = tbuf; } extra = NULL; action = fcmd_decode(cbuf, &extra); /* * If an "extra" string was returned, * process it as a string of command characters. */ if (extra != NULL) ungetsc(extra); } /* * Clear the cmdbuf string. * (But not if we're in the prefix of a command, * because the partial command string is kept there.) */ if (action != A_PREFIX) cmd_reset(); switch (action) { case A_DIGIT: /* * First digit of a number. */ start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE); goto again; case A_F_WINDOW: /* * Forward one window (and set the window size). */ if (number > 0) swindow = (int)number; /* FALLTHRU */ case A_F_SCREEN: /* * Forward one screen. */ if (number <= 0) number = get_swindow(); cmd_exec(); if (show_attn) set_attnpos(bottompos); forward((int)number, 0, 1); break; case A_B_WINDOW: /* * Backward one window (and set the window size). */ if (number > 0) swindow = (int)number; /* FALLTHRU */ case A_B_SCREEN: /* * Backward one screen. */ if (number <= 0) number = get_swindow(); cmd_exec(); backward((int)number, 0, 1); break; case A_F_LINE: /* * Forward N (default 1) line. */ if (number <= 0) number = 1; cmd_exec(); if (show_attn == OPT_ONPLUS && number > 1) set_attnpos(bottompos); forward((int)number, 0, 0); break; case A_B_LINE: /* * Backward N (default 1) line. */ if (number <= 0) number = 1; cmd_exec(); backward((int)number, 0, 0); break; case A_F_SKIP: /* * Skip ahead one screen, and then number lines. */ if (number <= 0) { number = get_swindow(); } else { number += get_swindow(); } cmd_exec(); if (show_attn == OPT_ONPLUS) set_attnpos(bottompos); forward((int)number, 0, 1); break; case A_FF_LINE: /* * Force forward N (default 1) line. */ if (number <= 0) number = 1; cmd_exec(); if (show_attn == OPT_ONPLUS && number > 1) set_attnpos(bottompos); forward((int)number, 1, 0); break; case A_BF_LINE: /* * Force backward N (default 1) line. */ if (number <= 0) number = 1; cmd_exec(); backward((int)number, 1, 0); break; case A_FF_SCREEN: /* * Force forward one screen. */ if (number <= 0) number = get_swindow(); cmd_exec(); if (show_attn == OPT_ONPLUS) set_attnpos(bottompos); forward((int)number, 1, 0); break; case A_F_FOREVER: /* * Forward forever, ignoring EOF. */ newaction = forw_loop(0); break; case A_F_UNTIL_HILITE: newaction = forw_loop(1); break; case A_F_SCROLL: /* * Forward N lines * (default same as last 'd' or 'u' command). */ if (number > 0) wscroll = (int)number; cmd_exec(); if (show_attn == OPT_ONPLUS) set_attnpos(bottompos); forward(wscroll, 0, 0); break; case A_B_SCROLL: /* * Forward N lines * (default same as last 'd' or 'u' command). */ if (number > 0) wscroll = (int)number; cmd_exec(); backward(wscroll, 0, 0); break; case A_FREPAINT: /* * Flush buffers, then repaint screen. * Don't flush the buffers on a pipe! */ clear_buffers(); /* FALLTHRU */ case A_REPAINT: /* * Repaint screen. */ cmd_exec(); repaint(); break; case A_GOLINE: /* * Go to line N, default beginning of file. */ if (number <= 0) number = 1; cmd_exec(); jump_back(number); break; case A_PERCENT: /* * Go to a specified percentage into the file. */ if (number < 0) { number = 0; fraction = 0; } if (number > 100) { number = 100; fraction = 0; } cmd_exec(); jump_percent((int)number, fraction); break; case A_GOEND: /* * Go to line N, default end of file. */ cmd_exec(); if (number <= 0) jump_forw(); else jump_back(number); break; case A_GOPOS: /* * Go to a specified byte position in the file. */ cmd_exec(); if (number < 0) number = 0; jump_line_loc((off_t) number, jump_sline); break; case A_STAT: /* * Print file name, etc. */ if (ch_getflags() & CH_HELPFILE) break; cmd_exec(); parg.p_string = eq_message(); error("%s", &parg); break; case A_VERSION: /* * Print version number, without the "@(#)". */ cmd_exec(); dispversion(); break; case A_QUIT: /* * Exit. */ if (curr_ifile != NULL_IFILE && ch_getflags() & CH_HELPFILE) { /* * Quit while viewing the help file * just means return to viewing the * previous file. */ hshift = save_hshift; if (edit_prev(1) == 0) break; } if (extra != NULL) quit(*extra); quit(QUIT_OK); break; /* * Define abbreviation for a commonly used sequence below. */ #define DO_SEARCH() \ if (number <= 0) number = 1; \ mca_search(); \ cmd_exec(); \ multi_search(NULL, (int)number); case A_F_SEARCH: /* * Search forward for a pattern. * Get the first char of the pattern. */ search_type = SRCH_FORW; if (number <= 0) number = 1; mca_search(); c = getcc(); goto again; case A_B_SEARCH: /* * Search backward for a pattern. * Get the first char of the pattern. */ search_type = SRCH_BACK; if (number <= 0) number = 1; mca_search(); c = getcc(); goto again; case A_FILTER: search_type = SRCH_FORW | SRCH_FILTER; mca_search(); c = getcc(); goto again; case A_AGAIN_SEARCH: /* * Repeat previous search. */ DO_SEARCH(); break; case A_T_AGAIN_SEARCH: /* * Repeat previous search, multiple files. */ search_type |= SRCH_PAST_EOF; DO_SEARCH(); break; case A_REVERSE_SEARCH: /* * Repeat previous search, in reverse direction. */ save_search_type = search_type; search_type = SRCH_REVERSE(search_type); DO_SEARCH(); search_type = save_search_type; break; case A_T_REVERSE_SEARCH: /* * Repeat previous search, * multiple files in reverse direction. */ save_search_type = search_type; search_type = SRCH_REVERSE(search_type); search_type |= SRCH_PAST_EOF; DO_SEARCH(); search_type = save_search_type; break; case A_UNDO_SEARCH: undo_search(); break; case A_HELP: /* * Help. */ if (ch_getflags() & CH_HELPFILE) break; if (ungot != NULL || unget_end) { error(less_is_more ? "Invalid option -p h" : "Invalid option ++h", NULL_PARG); break; } cmd_exec(); save_hshift = hshift; hshift = 0; (void) edit(FAKE_HELPFILE); break; case A_EXAMINE: /* * Edit a new file. Get the filename. */ if (secure) { error("Command not available", NULL_PARG); break; } start_mca(A_EXAMINE, "Examine: ", ml_examine, 0); c = getcc(); goto again; case A_VISUAL: /* * Invoke an editor on the input file. */ if (secure) { error("Command not available", NULL_PARG); break; } if (ch_getflags() & CH_HELPFILE) break; if (strcmp(get_filename(curr_ifile), "-") == 0) { error("Cannot edit standard input", NULL_PARG); break; } if (curr_altfilename != NULL) { error("WARNING: This file was viewed via " "LESSOPEN", NULL_PARG); } start_mca(A_SHELL, "!", ml_shell, 0); /* * Expand the editor prototype string * and pass it to the system to execute. * (Make sure the screen is displayed so the * expansion of "+%lm" works.) */ make_display(); cmd_exec(); lsystem(pr_expand(editproto, 0), NULL); break; case A_NEXT_FILE: /* * Examine next file. */ if (ntags()) { error("No next file", NULL_PARG); break; } if (number <= 0) number = 1; if (edit_next((int)number)) { if (get_quit_at_eof() && eof_displayed() && !(ch_getflags() & CH_HELPFILE)) quit(QUIT_OK); parg.p_string = (number > 1) ? "(N-th) " : ""; error("No %snext file", &parg); } break; case A_PREV_FILE: /* * Examine previous file. */ if (ntags()) { error("No previous file", NULL_PARG); break; } if (number <= 0) number = 1; if (edit_prev((int)number)) { parg.p_string = (number > 1) ? "(N-th) " : ""; error("No %sprevious file", &parg); } break; case A_NEXT_TAG: if (number <= 0) number = 1; tagfile = nexttag((int)number); if (tagfile == NULL) { error("No next tag", NULL_PARG); break; } if (edit(tagfile) == 0) { off_t pos = tagsearch(); if (pos != -1) jump_loc(pos, jump_sline); } break; case A_PREV_TAG: if (number <= 0) number = 1; tagfile = prevtag((int)number); if (tagfile == NULL) { error("No previous tag", NULL_PARG); break; } if (edit(tagfile) == 0) { off_t pos = tagsearch(); if (pos != -1) jump_loc(pos, jump_sline); } break; case A_INDEX_FILE: /* * Examine a particular file. */ if (number <= 0) number = 1; if (edit_index((int)number)) error("No such file", NULL_PARG); break; case A_REMOVE_FILE: if (ch_getflags() & CH_HELPFILE) break; old_ifile = curr_ifile; new_ifile = getoff_ifile(curr_ifile); if (new_ifile == NULL_IFILE) { ring_bell(); break; } if (edit_ifile(new_ifile) != 0) { reedit_ifile(old_ifile); break; } del_ifile(old_ifile); break; case A_OPT_TOGGLE: optflag = OPT_TOGGLE; optgetname = FALSE; mca_opt_toggle(); c = getcc(); goto again; case A_DISP_OPTION: /* * Report a flag setting. */ optflag = OPT_NO_TOGGLE; optgetname = FALSE; mca_opt_toggle(); c = getcc(); goto again; case A_FIRSTCMD: /* * Set an initial command for new files. */ start_mca(A_FIRSTCMD, "+", NULL, 0); c = getcc(); goto again; case A_SHELL: /* * Shell escape. */ if (secure) { error("Command not available", NULL_PARG); break; } start_mca(A_SHELL, "!", ml_shell, 0); c = getcc(); goto again; case A_SETMARK: /* * Set a mark. */ if (ch_getflags() & CH_HELPFILE) break; start_mca(A_SETMARK, "mark: ", (void*)NULL, 0); c = getcc(); if (c == erase_char || c == erase2_char || c == kill_char || c == '\n' || c == '\r') break; setmark(c); break; case A_GOMARK: /* * Go to a mark. */ start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0); c = getcc(); if (c == erase_char || c == erase2_char || c == kill_char || c == '\n' || c == '\r') break; cmd_exec(); gomark(c); break; case A_PIPE: if (secure) { error("Command not available", NULL_PARG); break; } start_mca(A_PIPE, "|mark: ", (void*)NULL, 0); c = getcc(); if (c == erase_char || c == erase2_char || c == kill_char) break; if (c == '\n' || c == '\r') c = '.'; if (badmark(c)) break; pipec = c; start_mca(A_PIPE, "!", ml_shell, 0); c = getcc(); goto again; case A_B_BRACKET: case A_F_BRACKET: start_mca(action, "Brackets: ", (void*)NULL, 0); c = getcc(); goto again; case A_LSHIFT: if (number > 0) shift_count = number; else number = (shift_count > 0) ? shift_count : sc_width / 2; if (number > hshift) number = hshift; hshift -= number; screen_trashed = 1; break; case A_RSHIFT: if (number > 0) shift_count = number; else number = (shift_count > 0) ? shift_count : sc_width / 2; hshift += number; screen_trashed = 1; break; case A_PREFIX: /* * The command is incomplete (more chars are needed). * Display the current char, so the user knows * what's going on, and get another character. */ if (mca != A_PREFIX) { cmd_reset(); start_mca(A_PREFIX, " ", (void*)NULL, CF_QUIT_ON_ERASE); (void) cmd_char(c); } c = getcc(); goto again; case A_NOACTION: break; default: ring_bell(); break; } } }
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; }