/* */
This source file includes following definitions.
- usage
- help
- setcom
- decide_tag_by_context
- main
- completion_tags
- completion
- completion_idutils
- completion_path
- print_count
- idutils
- grep
- pathlist
- put_syms
- parsefile
- search
- tagsearch
- encode
1 /*
2 * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006,
3 * 2007, 2008, 2010, 2011, 2012 Tama Communications Corporation
4 *
5 * This file is part of GNU GLOBAL.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <errno.h>
27
28 #include <ctype.h>
29 #include <stdio.h>
30 #ifdef STDC_HEADERS
31 #include <stdlib.h>
32 #endif
33 #ifdef HAVE_STRING_H
34 #include <string.h>
35 #else
36 #include <strings.h>
37 #endif
38 #ifdef HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #include "getopt.h"
42
43 #include "global.h"
44 #include "parser.h"
45 #include "regex.h"
46 #include "const.h"
47 #include "literal.h"
48
49 /**
50 * @file global.c
51 * @NAME{global} - print locations of the specified object.
52 */
53 static void usage(void);
54 static void help(void);
55 static void setcom(int);
56 int decide_tag_by_context(const char *, const char *, int);
57 int main(int, char **);
58 int completion_tags(const char *, const char *, const char *, int);
59 void completion(const char *, const char *, const char *, int);
60 void completion_idutils(const char *, const char *, const char *);
61 void completion_path(const char *, const char *);
62 void idutils(const char *, const char *);
63 void grep(const char *, char *const *, const char *);
64 void pathlist(const char *, const char *);
65 void parsefile(char *const *, const char *, const char *, const char *, int);
66 int search(const char *, const char *, const char *, const char *, int);
67 void tagsearch(const char *, const char *, const char *, const char *, int);
68 void encode(char *, int, const char *);
69
70 const char *localprefix; /**< local prefix */
71 int aflag; /* [option] */
72 int cflag; /* command */
73 int dflag; /* command */
74 int fflag; /* command */
75 int gflag; /* command */
76 int Gflag; /* [option] */
77 int iflag; /* [option] */
78 int Iflag; /* command */
79 int lflag; /* [option] */
80 int Lflag; /* [option] */
81 int nflag; /* [option] */
82 int oflag; /* [option] */
83 int Oflag; /* [option] */
84 int pflag; /* command */
85 int Pflag; /* command */
86 int qflag; /* [option] */
87 int rflag; /* [option] */
88 int sflag; /* [option] */
89 int tflag; /* [option] */
90 int Tflag; /* [option] */
91 int uflag; /* command */
92 int vflag; /* [option] */
93 int Vflag; /* [option] */
94 int xflag; /* [option] */
95 int show_version;
96 int show_help;
97 int nofilter;
98 int nosource; /**< undocumented command */
99 int debug;
100 int literal; /**< 1: literal search */
101 int print0; /**< @OPTION{-print0} option */
102 int format;
103 int type; /**< path conversion type */
104 int match_part; /**< match part only */
105 int abslib; /**< absolute path only in library project */
106 const char *cwd; /**< current directory */
107 const char *root; /**< root of source tree */
108 const char *dbpath; /**< dbpath directory */
109 char *context_file;
110 char *context_lineno;
111 char *file_list;
112 char *encode_chars;
113 char *single_update;
114 char *path_style;
115
116 static void
117 usage(void)
118 {
119 if (!qflag)
120 fputs(usage_const, stderr);
121 exit(2);
122 }
123 static void
124 help(void)
125 {
126 fputs(usage_const, stdout);
127 fputs(help_const, stdout);
128 exit(0);
129 }
130
131 #define RESULT 128
132 #define FROM_HERE 129
133 #define ENCODE_PATH 130
134 #define MATCH_PART 131
135 #define SINGLE_UPDATE 132
136 #define PATH_STYLE 133
137 #define SORT_FILTER 1
138 #define PATH_FILTER 2
139 #define BOTH_FILTER (SORT_FILTER|PATH_FILTER)
140 #define MATCH_PART_FIRST 1
141 #define MATCH_PART_LAST 2
142 #define MATCH_PART_ALL 3
143
144 static struct option const long_options[] = {
145 {"absolute", no_argument, NULL, 'a'},
146 {"completion", no_argument, NULL, 'c'},
147 {"definition", no_argument, NULL, 'd'},
148 {"regexp", required_argument, NULL, 'e'},
149 {"file", no_argument, NULL, 'f'},
150 {"local", no_argument, NULL, 'l'},
151 {"file-list", required_argument, NULL, 'L'},
152 {"nofilter", optional_argument, NULL, 'n'},
153 {"grep", no_argument, NULL, 'g'},
154 {"basic-regexp", no_argument, NULL, 'G'},
155 {"ignore-case", no_argument, NULL, 'i'},
156 {"idutils", no_argument, NULL, 'I'},
157 {"other", no_argument, NULL, 'o'},
158 {"only-other", no_argument, NULL, 'O'},
159 {"print-dbpath", no_argument, NULL, 'p'},
160 {"path", no_argument, NULL, 'P'},
161 {"quiet", no_argument, NULL, 'q'},
162 {"reference", no_argument, NULL, 'r'},
163 {"rootdir", no_argument, NULL, 'r'},
164 {"symbol", no_argument, NULL, 's'},
165 {"tags", no_argument, NULL, 't'},
166 {"through", no_argument, NULL, 'T'},
167 {"update", no_argument, NULL, 'u'},
168 {"verbose", no_argument, NULL, 'v'},
169 {"invert-match", optional_argument, NULL, 'V'},
170 {"cxref", no_argument, NULL, 'x'},
171
172 /* long name only */
173 {"encode-path", required_argument, NULL, ENCODE_PATH},
174 {"from-here", required_argument, NULL, FROM_HERE},
175 {"debug", no_argument, &debug, 1},
176 {"literal", no_argument, &literal, 1},
177 {"match-part", required_argument, NULL, MATCH_PART},
178 {"path-style", required_argument, NULL, PATH_STYLE},
179 {"print0", no_argument, &print0, 1},
180 {"version", no_argument, &show_version, 1},
181 {"help", no_argument, &show_help, 1},
182 {"result", required_argument, NULL, RESULT},
183 {"nosource", no_argument, &nosource, 1},
184 {"single-update", required_argument, NULL, SINGLE_UPDATE},
185 { 0 }
186 };
187
188 static int command;
189 static void
190 setcom(int c)
191 {
192 if (command == 0)
193 command = c;
194 else if (c == 'c' && (command == 'I' || command == 'P'))
195 command = c;
196 else if (command == 'c' && (c == 'I' || c == 'P'))
197 ;
198 else if (command != c)
199 usage();
200 }
201 /**
202 * @fn int decide_tag_by_context(const char *tag, const char *file, int lineno)
203 *
204 * decide_tag_by_context: decide tag type by context
205 *
206 * @param[in] tag tag name
207 * @param[in] file context file
208 * @param[in] lineno context lineno
209 * @return #GTAGS, #GRTAGS, #GSYMS
210 */
211 #define NEXT_NUMBER(p) do { \
212 for (n = 0; isdigit(*p); p++) \
213 n = n * 10 + (*p - '0'); \
214 } while (0)
215 int
216 decide_tag_by_context(const char *tag, const char *file, int lineno)
217 {
218 STRBUF *sb = NULL;
219 char path[MAXPATHLEN], s_fid[MAXFIDLEN];
220 const char *tagline, *p;
221 DBOP *dbop;
222 int db = GSYMS;
223 int iscompline = 0;
224
225 if (normalize(file, get_root_with_slash(), cwd, path, sizeof(path)) == NULL)
226 die("'%s' is out of the source project.", file);
227 /*
228 * get file id
229 */
230 if (gpath_open(dbpath, 0) < 0)
231 die("GPATH not found.");
232 if ((p = gpath_path2fid(path, NULL)) == NULL)
233 die("path name in the context is not found.");
234 strlimcpy(s_fid, p, sizeof(s_fid));
235 gpath_close();
236 /*
237 * read btree records directly to avoid the overhead.
238 */
239 dbop = dbop_open(makepath(dbpath, dbname(GTAGS), NULL), 0, 0, 0);
240 if (dbop == NULL)
241 die("cannot open GTAGS.");
242 if (dbop_getoption(dbop, COMPLINEKEY))
243 iscompline = 1;
244 tagline = dbop_first(dbop, tag, NULL, 0);
245 if (tagline) {
246 db = GTAGS;
247 for (; tagline; tagline = dbop_next(dbop)) {
248 /*
249 * examine whether the definition record include the context.
250 */
251 p = locatestring(tagline, s_fid, MATCH_AT_FIRST);
252 if (p != NULL && *p == ' ') {
253 for (p++; *p && *p != ' '; p++)
254 ;
255 if (*p++ != ' ' || !isdigit(*p))
256 die("Impossible! decide_tag_by_context(1)");
257 /*
258 * Standard format n <blank> <image>$
259 * Compact format d,d,d,d$
260 */
261 if (!iscompline) { /* Standard format */
262 if (atoi(p) == lineno) {
263 db = GRTAGS;
264 goto finish;
265 }
266 } else { /* Compact format */
267 int n, cur, last = 0;
268
269 do {
270 if (!isdigit(*p))
271 die("Impossible! decide_tag_by_context(2)");
272 NEXT_NUMBER(p);
273 cur = last + n;
274 if (cur == lineno) {
275 db = GRTAGS;
276 goto finish;
277 }
278 last = cur;
279 if (*p == '-') {
280 if (!isdigit(*++p))
281 die("Impossible! decide_tag_by_context(3)");
282 NEXT_NUMBER(p);
283 cur = last + n;
284 if (lineno >= last && lineno <= cur) {
285 db = GRTAGS;
286 goto finish;
287 }
288 last = cur;
289 }
290 if (*p) {
291 if (*p == ',')
292 p++;
293 else
294 die("Impossible! decide_tag_by_context(4)");
295 }
296 } while (*p);
297 }
298 }
299 }
300 }
301 finish:
302 dbop_close(dbop);
303 if (db == GSYMS && getenv("GTAGSLIBPATH")) {
304 char libdbpath[MAXPATHLEN];
305 char *libdir = NULL, *nextp = NULL;
306
307 sb = strbuf_open(0);
308 strbuf_puts(sb, getenv("GTAGSLIBPATH"));
309 for (libdir = strbuf_value(sb); libdir; libdir = nextp) {
310 if ((nextp = locatestring(libdir, PATHSEP, MATCH_FIRST)) != NULL)
311 *nextp++ = 0;
312 if (!gtagsexist(libdir, libdbpath, sizeof(libdbpath), 0))
313 continue;
314 if (!strcmp(dbpath, libdbpath))
315 continue;
316 dbop = dbop_open(makepath(libdbpath, dbname(GTAGS), NULL), 0, 0, 0);
317 if (dbop == NULL)
318 continue;
319 tagline = dbop_first(dbop, tag, NULL, 0);
320 dbop_close(dbop);
321 if (tagline != NULL) {
322 db = GTAGS;
323 break;
324 }
325 }
326 strbuf_close(sb);
327 }
328 return db;
329 }
330 int
331 main(int argc, char **argv)
332 {
333 const char *av = NULL;
334 int db;
335 int optchar;
336 int option_index = 0;
337
338 logging_arguments(argc, argv);
339 while ((optchar = getopt_long(argc, argv, "acde:ifgGIlL:noOpPqrstTuvVx", long_options, &option_index)) != EOF) {
340 switch (optchar) {
341 case 0:
342 break;
343 case 'a':
344 aflag++;
345 break;
346 case 'c':
347 cflag++;
348 setcom(optchar);
349 break;
350 case 'd':
351 dflag++;
352 break;
353 case 'e':
354 av = optarg;
355 break;
356 case 'f':
357 fflag++;
358 xflag++;
359 setcom(optchar);
360 break;
361 case 'g':
362 gflag++;
363 setcom(optchar);
364 break;
365 case 'G':
366 Gflag++;
367 break;
368 case 'i':
369 iflag++;
370 break;
371 case 'I':
372 Iflag++;
373 setcom(optchar);
374 break;
375 case 'l':
376 lflag++;
377 break;
378 case 'L':
379 file_list = optarg;
380 break;
381 case 'n':
382 nflag++;
383 if (optarg) {
384 if (!strcmp(optarg, "sort"))
385 nofilter |= SORT_FILTER;
386 else if (!strcmp(optarg, "path"))
387 nofilter |= PATH_FILTER;
388 } else {
389 nofilter = BOTH_FILTER;
390 }
391 break;
392 case 'o':
393 oflag++;
394 break;
395 case 'O':
396 Oflag++;
397 break;
398 case 'p':
399 pflag++;
400 setcom(optchar);
401 break;
402 case 'P':
403 Pflag++;
404 setcom(optchar);
405 break;
406 case 'q':
407 qflag++;
408 setquiet();
409 break;
410 case 'r':
411 rflag++;
412 break;
413 case 's':
414 sflag++;
415 break;
416 case 't':
417 tflag++;
418 break;
419 case 'T':
420 Tflag++;
421 break;
422 case 'u':
423 uflag++;
424 setcom(optchar);
425 break;
426 case 'v':
427 vflag++;
428 setverbose();
429 break;
430 case 'V':
431 Vflag++;
432 break;
433 case 'x':
434 xflag++;
435 break;
436 case ENCODE_PATH:
437 encode_chars = optarg;
438 break;
439 case FROM_HERE:
440 {
441 char *p = optarg;
442 const char *usage = "usage: global --from-here=lineno:path.";
443
444 context_lineno = p;
445 while (*p && isdigit(*p))
446 p++;
447 if (*p != ':')
448 die_with_code(2, usage);
449 *p++ = '\0';
450 if (!*p)
451 die_with_code(2, usage);
452 context_file = p;
453 }
454 break;
455 case MATCH_PART:
456 if (!strcmp(optarg, "first"))
457 match_part = MATCH_PART_FIRST;
458 else if (!strcmp(optarg, "last"))
459 match_part = MATCH_PART_LAST;
460 else if (!strcmp(optarg, "all"))
461 match_part = MATCH_PART_ALL;
462 else
463 die_with_code(2, "unknown part type for the --match-part option.");
464 break;
465 case PATH_STYLE:
466 path_style = optarg;
467 break;
468 case RESULT:
469 if (!strcmp(optarg, "ctags-x"))
470 format = FORMAT_CTAGS_X;
471 else if (!strcmp(optarg, "ctags-xid"))
472 format = FORMAT_CTAGS_XID;
473 else if (!strcmp(optarg, "ctags"))
474 format = FORMAT_CTAGS;
475 else if (!strcmp(optarg, "ctags-mod"))
476 format = FORMAT_CTAGS_MOD;
477 else if (!strcmp(optarg, "path"))
478 format = FORMAT_PATH;
479 else if (!strcmp(optarg, "grep"))
480 format = FORMAT_GREP;
481 else if (!strcmp(optarg, "cscope"))
482 format = FORMAT_CSCOPE;
483 else
484 die_with_code(2, "unknown format type for the --result option.");
485 break;
486 case SINGLE_UPDATE:
487 single_update = optarg;
488 break;
489 default:
490 usage();
491 break;
492 }
493 }
494 /*
495 * decide format.
496 * The --result option is given to priority more than the -t and -x option.
497 */
498 if (format == 0) {
499 if (tflag) { /* ctags format */
500 format = FORMAT_CTAGS;
501 } else if (xflag) { /* print details */
502 format = FORMAT_CTAGS_X;
503 } else { /* print just a file name */
504 format = FORMAT_PATH;
505 }
506 }
507 /*
508 * GTAGSBLANKENCODE will be used in less(1).
509 */
510 switch (format) {
511 case FORMAT_CTAGS_X:
512 case FORMAT_CTAGS_XID:
513 if (encode_chars == NULL && getenv("GTAGSBLANKENCODE"))
514 encode_chars = " \t";
515 break;
516 }
517 if (encode_chars) {
518 if (strlen(encode_chars) > 255)
519 die("too many encode chars.");
520 if (strchr(encode_chars, '/') || strchr(encode_chars, '.'))
521 warning("cannot encode '/' and '.' in the path. Ignored.");
522 set_encode_chars((unsigned char *)encode_chars);
523 }
524 if (getenv("GTAGSTHROUGH"))
525 Tflag++;
526 if (qflag)
527 vflag = 0;
528 if (show_help)
529 help();
530
531 argc -= optind;
532 argv += optind;
533 /*
534 * At first, we pickup pattern from -e option. If it is not found
535 * then use argument which is not option.
536 */
537 if (!av) {
538 av = *argv;
539 /*
540 * global -g pattern [files ...]
541 * av argv
542 */
543 if (gflag && av)
544 argv++;
545 }
546 if (show_version)
547 version(av, vflag);
548 if (single_update) {
549 if (command == 0) {
550 uflag++;
551 command = 'u';
552 } else if (command != 'u') {
553 ; /* ignored */
554 }
555 }
556 /*
557 * only -c, -u, -P and -p allows no argument.
558 */
559 if (!av) {
560 switch (command) {
561 case 'c':
562 case 'u':
563 case 'p':
564 case 'P':
565 break;
566 case 'f':
567 if (file_list)
568 break;
569 default:
570 usage();
571 break;
572 }
573 }
574 /*
575 * -u and -p cannot have any arguments.
576 */
577 if (av) {
578 switch (command) {
579 case 'u':
580 case 'p':
581 usage();
582 default:
583 break;
584 }
585 }
586 if (tflag)
587 xflag = 0;
588 if (nflag > 1)
589 nosource = 1; /* to keep compatibility */
590 if (print0)
591 set_print0();
592 if (cflag && match_part == 0)
593 match_part = MATCH_PART_ALL;
594 /*
595 * remove leading blanks.
596 */
597 if (!Iflag && !gflag && av)
598 for (; *av == ' ' || *av == '\t'; av++)
599 ;
600 if (cflag && !Pflag && av && isregex(av))
601 die_with_code(2, "only name char is allowed with -c option.");
602 /*
603 * get path of following directories.
604 * o current directory
605 * o root of source tree
606 * o dbpath directory
607 *
608 * if GTAGS not found, exit with an error message.
609 */
610 {
611 int status = setupdbpath(pflag && vflag);
612 if (status < 0)
613 die_with_code(-status, gtags_dbpath_error);
614 cwd = get_cwd();
615 root = get_root();
616 dbpath = get_dbpath();
617 }
618 /*
619 * print dbpath or rootdir.
620 */
621 if (pflag) {
622 fprintf(stdout, "%s\n", (rflag) ? root : dbpath);
623 exit(0);
624 }
625 /*
626 * incremental update of tag files.
627 */
628 if (uflag) {
629 STRBUF *sb = strbuf_open(0);
630 char *gtags_path = usable("gtags");
631
632 if (!gtags_path)
633 die("gtags command not found.");
634 if (chdir(root) < 0)
635 die("cannot change directory to '%s'.", root);
636 strbuf_puts(sb, quote_shell(gtags_path));
637 strbuf_puts(sb, " -i");
638 if (vflag)
639 strbuf_puts(sb, " -v");
640 if (single_update) {
641 if (!isabspath(single_update)) {
642 static char regular_path_name[MAXPATHLEN];
643
644 if (rel2abs(single_update, cwd, regular_path_name, sizeof(regular_path_name)) == NULL)
645 die("rel2abs failed.");
646 single_update = regular_path_name;
647 }
648 strbuf_puts(sb, " --single-update=");
649 strbuf_putc(sb, '"');
650 strbuf_puts(sb, single_update);
651 strbuf_putc(sb, '"');
652 }
653 strbuf_putc(sb, ' ');
654 strbuf_puts(sb, dbpath);
655 if (system(strbuf_value(sb)))
656 exit(1);
657 strbuf_close(sb);
658 exit(0);
659 }
660 /*
661 * decide tag type.
662 */
663 if (context_file) {
664 static char buf[MAXPATHLEN];
665
666 if (realpath(context_file, buf) == NULL)
667 die("cannot get real path name.");
668 context_file = buf;
669 if (isregex(av))
670 die_with_code(2, "regular expression is not allowed with the --from-here option.");
671 db = decide_tag_by_context(av, context_file, atoi(context_lineno));
672 } else {
673 if (dflag)
674 db = GTAGS;
675 else if (rflag && sflag)
676 db = GRTAGS + GSYMS;
677 else
678 db = (rflag) ? GRTAGS : ((sflag) ? GSYMS : GTAGS);
679 }
680 /*
681 * complete function name
682 */
683 if (cflag) {
684 if (Iflag)
685 completion_idutils(dbpath, root, av);
686 else if (Pflag)
687 completion_path(dbpath, av);
688 else
689 completion(dbpath, root, av, db);
690 exit(0);
691 }
692 /*
693 * make local prefix.
694 * local prefix must starts with './' and ends with '/'.
695 */
696 if (lflag) {
697 STRBUF *sb = strbuf_open(0);
698
699 strbuf_putc(sb, '.');
700 if (strcmp(root, cwd) != 0) {
701 const char *p = cwd + strlen(root);
702 if (*p != '/')
703 strbuf_putc(sb, '/');
704 strbuf_puts(sb, p);
705 }
706 strbuf_putc(sb, '/');
707 localprefix = check_strdup(strbuf_value(sb));
708 strbuf_close(sb);
709 #ifdef DEBUG
710 fprintf(stderr, "root=%s\n", root);
711 fprintf(stderr, "cwd=%s\n", cwd);
712 fprintf(stderr, "localprefix=%s\n", localprefix);
713 #endif
714 }
715 /*
716 * convert the file-list path into an absolute path.
717 */
718 if (file_list && strcmp(file_list, "-") && !isabspath(file_list)) {
719 static char buf[MAXPATHLEN];
720
721 if (realpath(file_list, buf) == NULL)
722 die("cannot get real path name.");
723 file_list = buf;
724 }
725 /*
726 * decide path conversion type.
727 */
728 if (nofilter & PATH_FILTER)
729 type = PATH_THROUGH;
730 else if (aflag)
731 type = PATH_ABSOLUTE;
732 else
733 type = PATH_RELATIVE;
734 if (path_style) {
735 if (!strcmp(path_style, "relative"))
736 type = PATH_RELATIVE;
737 else if (!strcmp(path_style, "absolute"))
738 type = PATH_ABSOLUTE;
739 else if (!strcmp(path_style, "through"))
740 type = PATH_THROUGH;
741 else if (!strcmp(path_style, "shorter"))
742 type = PATH_SHORTER;
743 else if (!strcmp(path_style, "abslib")) {
744 type = PATH_RELATIVE;
745 abslib++;
746 } else
747 die("illegal path style.");
748 }
749 /*
750 * exec lid(idutils).
751 */
752 if (Iflag) {
753 chdir(root);
754 idutils(av, dbpath);
755 }
756 /*
757 * search pattern (regular expression).
758 */
759 else if (gflag) {
760 chdir(root);
761 grep(av, argv, dbpath);
762 }
763 /*
764 * locate paths including the pattern.
765 */
766 else if (Pflag) {
767 chdir(root);
768 pathlist(av, dbpath);
769 }
770 /*
771 * parse source files.
772 */
773 else if (fflag) {
774 chdir(root);
775 parsefile(argv, cwd, root, dbpath, db);
776 }
777 /*
778 * tag search.
779 */
780 else {
781 tagsearch(av, cwd, root, dbpath, db);
782 }
783 return 0;
784 }
785 /**
786 * completion_tags: print completion list of specified @a prefix
787 *
788 * @param[in] dbpath dbpath directory
789 * @param[in] root root directory
790 * @param[in] prefix prefix of primary key
791 * @param[in] db #GTAGS,#GRTAGS,#GSYMS
792 * @return number of words
793 */
794 int
795 completion_tags(const char *dbpath, const char *root, const char *prefix, int db)
796 {
797 int flags = GTOP_KEY;
798 GTOP *gtop = gtags_open(dbpath, root, db, GTAGS_READ, 0);
799 GTP *gtp;
800 int count = 0;
801
802 if (prefix && isalpha(*prefix) && iflag) {
803 /*
804 * If the -i option is specified, we use both of regular
805 * expression and prefix read for performance. It is done
806 * by connecting two prefix reading.
807 */
808 STRBUF *sb = strbuf_open(0);
809 regex_t preg;
810 int i, firstchar[2];
811
812 flags |= GTOP_NOREGEX;
813 flags |= GTOP_PREFIX;
814 /*
815 * make regular expression.
816 */
817 strbuf_putc(sb, '^');
818 strbuf_puts(sb, prefix);
819 if (regcomp(&preg, strbuf_value(sb), REG_ICASE) != 0)
820 die("invalid regular expression.");
821 /*
822 * Two prefix reading:
823 *
824 * prefix = 'main'
825 * v
826 * firstchar[0] = 'M'; /^M/ the first time
827 * firstchar[1] = 'm'; /^m/ the second time
828 */
829 firstchar[0] = firstchar[1] = *prefix;
830 if (isupper(firstchar[0]))
831 firstchar[1] = tolower(firstchar[0]);
832 else
833 firstchar[0] = toupper(firstchar[0]);
834 for (i = 0; i < 2; i++) {
835 strbuf_reset(sb);
836 strbuf_putc(sb, firstchar[i]);
837 for (gtp = gtags_first(gtop, strbuf_value(sb), flags); gtp; gtp = gtags_next(gtop)) {
838 if (regexec(&preg, gtp->tag, 0, 0, 0) == 0) {
839 fputs(gtp->tag, stdout);
840 fputc('\n', stdout);
841 count++;
842 }
843 }
844 }
845 strbuf_close(sb);
846 } else {
847 flags |= GTOP_NOREGEX;
848 if (prefix)
849 flags |= GTOP_PREFIX;
850 for (gtp = gtags_first(gtop, prefix, flags); gtp; gtp = gtags_next(gtop)) {
851 fputs(gtp->tag, stdout);
852 fputc('\n', stdout);
853 count++;
854 }
855 }
856 gtags_close(gtop);
857 return count;
858 }
859 /**
860 * completion: print completion list of specified @a prefix
861 *
862 * @param[in] dbpath dbpath directory
863 * @param[in] root root directory
864 * @param[in] prefix prefix of primary key
865 * @param[in] db #GTAGS,#GRTAGS,#GSYMS
866 */
867 void
868 completion(const char *dbpath, const char *root, const char *prefix, int db)
869 {
870 int count, total = 0;
871 char libdbpath[MAXPATHLEN];
872
873 if (prefix && *prefix == 0) /* In the case global -c '' */
874 prefix = NULL;
875 count = completion_tags(dbpath, root, prefix, db);
876 /*
877 * search in library path.
878 */
879 if (db == GTAGS && getenv("GTAGSLIBPATH") && (count == 0 || Tflag) && !lflag) {
880 STRBUF *sb = strbuf_open(0);
881 char *libdir, *nextp = NULL;
882
883 strbuf_puts(sb, getenv("GTAGSLIBPATH"));
884 /*
885 * search for each tree in the library path.
886 */
887 for (libdir = strbuf_value(sb); libdir; libdir = nextp) {
888 if ((nextp = locatestring(libdir, PATHSEP, MATCH_FIRST)) != NULL)
889 *nextp++ = 0;
890 if (!gtagsexist(libdir, libdbpath, sizeof(libdbpath), 0))
891 continue;
892 if (!strcmp(dbpath, libdbpath))
893 continue;
894 if (!test("f", makepath(libdbpath, dbname(db), NULL)))
895 continue;
896 /*
897 * search again
898 */
899 count = completion_tags(libdbpath, libdir, prefix, db);
900 total += count;
901 if (count > 0 && !Tflag)
902 break;
903 }
904 strbuf_close(sb);
905 }
906 /* return total; */
907 }
908 /**
909 * completion_idutils: print completion list of specified @a prefix
910 *
911 * @param[in] dbpath dbpath directory
912 * @param[in] root root directory
913 * @param[in] prefix prefix of primary key
914 */
915 void
916 completion_idutils(const char *dbpath, const char *root, const char *prefix)
917 {
918 FILE *ip;
919 STRBUF *sb = strbuf_open(0);
920 const char *lid = usable("lid");
921 char *line, *p;
922
923 if (prefix && *prefix == 0) /* In the case global -c '' */
924 prefix = NULL;
925 /*
926 * make lid command line.
927 * Invoke lid with the --result=grep option to generate grep format.
928 */
929 if (!lid)
930 die("lid(idutils) not found.");
931 strbuf_puts(sb, lid);
932 strbuf_sprintf(sb, " --file='%s/ID'", dbpath);
933 strbuf_puts(sb, " --key=token");
934 if (iflag)
935 strbuf_puts(sb, " --ignore-case");
936 strbuf_putc(sb, ' ');
937 strbuf_putc(sb, '"');
938 strbuf_putc(sb, '^');
939 strbuf_puts(sb, prefix);
940 strbuf_putc(sb, '"');
941 if (debug)
942 fprintf(stderr, "completion_idutils: %s\n", strbuf_value(sb));
943 if (chdir(root) < 0)
944 die("cannot move to '%s' directory.", root);
945 if (!(ip = popen(strbuf_value(sb), "r")))
946 die("cannot execute '%s'.", strbuf_value(sb));
947 while ((line = strbuf_fgets(sb, ip, STRBUF_NOCRLF)) != NULL) {
948 for (p = line; *p && *p != ' '; p++)
949 ;
950 if (*p == '\0') {
951 warning("Illegal line: %s", line);
952 continue;
953 }
954 *p = '\0';
955 puts(line);
956 }
957 fclose(ip);
958 strbuf_close(sb);
959 }
960 /**
961 * completion_path: print candidate path list.
962 *
963 * @param[in] dbpath dbpath directory
964 * @param[in] prefix prefix of primary key
965 */
966 void
967 completion_path(const char *dbpath, const char *prefix)
968 {
969 GFIND *gp;
970 const char *localprefix = "./";
971 DBOP *dbop = dbop_open(NULL, 1, 0600, DBOP_RAW);
972 const char *path;
973 int prefix_length;
974 int target = GPATH_SOURCE;
975 int flags = (match_part == MATCH_PART_LAST) ? MATCH_LAST : MATCH_FIRST;
976
977 if (dbop == NULL)
978 die("cannot open temporary file.");
979 if (prefix && *prefix == 0) /* In the case global -c '' */
980 prefix = NULL;
981 prefix_length = (prefix == NULL) ? 0 : strlen(prefix);
982 if (oflag)
983 target = GPATH_BOTH;
984 if (Oflag)
985 target = GPATH_OTHER;
986 if (iflag || getconfb("icase_path"))
987 flags |= IGNORE_CASE;
988 gp = gfind_open(dbpath, localprefix, target);
989 while ((path = gfind_read(gp)) != NULL) {
990 path++; /* skip '.'*/
991 if (prefix == NULL) {
992 dbop_put(dbop, path + 1, "");
993 } else if (match_part == MATCH_PART_ALL) {
994 const char *p = path;
995
996 while ((p = locatestring(p, prefix, flags)) != NULL) {
997 dbop_put(dbop, p, "");
998 p += prefix_length;
999 }
1000 } else {
1001 const char *p = locatestring(path, prefix, flags);
1002 if (p != NULL) {
1003 dbop_put(dbop, p, "");
1004 }
1005 }
1006 }
1007 gfind_close(gp);
1008 for (path = dbop_first(dbop, NULL, NULL, DBOP_KEY); path != NULL; path = dbop_next(dbop)) {
1009 fputs(path, stdout);
1010 fputc('\n', stdout);
1011 }
1012 dbop_close(dbop);
1013 }
1014 /*
1015 * Output filter
1016 *
1017 * (1) Old architecture (- GLOBAL-4.7.8)
1018 *
1019 * process1 process2 process3
1020 * +=============+ +===========+ +===========+
1021 * |global(write)|->|sort filter|->|path filter|->[stdout]
1022 * +=============+ +===========+ +===========+
1023 *
1024 * (2) Recent architecture (GLOBAL-5.0 - 5.3)
1025 *
1026 * 1 process
1027 * +===========================================+
1028 * |global(write)->[sort filter]->[path filter]|->[stdout]
1029 * +===========================================+
1030 *
1031 * (3) Current architecture (GLOBAL-5.4 -)
1032 *
1033 * 1 process
1034 * +===========================================+
1035 * |[sort filter]->global(write)->[path filter]|->[stdout]
1036 * +===========================================+
1037 *
1038 * Sort filter is implemented in gtagsop module (libutil/gtagsop.c).
1039 * Path filter is implemented in pathconvert module (libutil/pathconvert.c).
1040 */
1041 /**
1042 * print number of object.
1043 *
1044 * This procedure is commonly used except for the @OPTION{-P} option.
1045 */
1046 void
1047 print_count(int number)
1048 {
1049 const char *target = format == FORMAT_PATH ? "file" : "object";
1050
1051 switch (number) {
1052 case 0:
1053 fprintf(stderr, "object not found");
1054 break;
1055 case 1:
1056 fprintf(stderr, "1 %s located", target);
1057 break;
1058 default:
1059 fprintf(stderr, "%d %ss located", number, target);
1060 break;
1061 }
1062 }
1063 /**
1064 * idutils: @NAME{lid}(@NAME{idutils}) pattern
1065 *
1066 * @param[in] pattern @NAME{POSIX} regular expression
1067 * @param[in] dbpath @FILE{GTAGS} directory
1068 */
1069 void
1070 idutils(const char *pattern, const char *dbpath)
1071 {
1072 FILE *ip;
1073 CONVERT *cv;
1074 STRBUF *ib = strbuf_open(0);
1075 char encoded_pattern[IDENTLEN];
1076 char path[MAXPATHLEN];
1077 const char *lid;
1078 int linenum, count;
1079 char *p, *q, *grep;
1080
1081 lid = usable("lid");
1082 if (!lid)
1083 die("lid(idutils) not found.");
1084 /*
1085 * convert spaces into %FF format.
1086 */
1087 encode(encoded_pattern, sizeof(encoded_pattern), pattern);
1088 /*
1089 * make lid command line.
1090 * Invoke lid with the --result=grep option to generate grep format.
1091 */
1092 strbuf_puts(ib, lid);
1093 strbuf_sprintf(ib, " --file='%s/ID'", dbpath);
1094 strbuf_puts(ib, " --separator=newline");
1095 if (format == FORMAT_PATH)
1096 strbuf_puts(ib, " --result=filenames --key=none");
1097 else
1098 strbuf_puts(ib, " --result=grep");
1099 if (iflag)
1100 strbuf_puts(ib, " --ignore-case");
1101 strbuf_putc(ib, ' ');
1102 strbuf_puts(ib, quote_string(pattern));
1103 if (debug)
1104 fprintf(stderr, "idutils: %s\n", strbuf_value(ib));
1105 if (!(ip = popen(strbuf_value(ib), "r")))
1106 die("cannot execute '%s'.", strbuf_value(ib));
1107 cv = convert_open(type, format, root, cwd, dbpath, stdout, NOTAGS);
1108 count = 0;
1109 strcpy(path, "./");
1110 while ((grep = strbuf_fgets(ib, ip, STRBUF_NOCRLF)) != NULL) {
1111 q = path + 2;
1112 /* extract path name */
1113 if (*grep == '/')
1114 die("The path in the output of lid is assumed absolute. '%s'", grep);
1115 p = grep;
1116 while (*p && *p != ':')
1117 *q++ = *p++;
1118 *q = '\0';
1119 if ((xflag || tflag) && !*p)
1120 die("invalid lid(idutils) output format(1). '%s'", grep);
1121 p++;
1122 if (lflag) {
1123 if (!locatestring(path, localprefix, MATCH_AT_FIRST))
1124 continue;
1125 }
1126 count++;
1127 switch (format) {
1128 case FORMAT_PATH:
1129 convert_put_path(cv, path);
1130 break;
1131 default:
1132 /* extract line number */
1133 while (*p && isspace(*p))
1134 p++;
1135 linenum = 0;
1136 for (linenum = 0; *p && isdigit(*p); linenum = linenum * 10 + (*p++ - '0'))
1137 ;
1138 if (*p != ':')
1139 die("invalid lid(idutils) output format(2). '%s'", grep);
1140 if (linenum <= 0)
1141 die("invalid lid(idutils) output format(3). '%s'", grep);
1142 p++;
1143 /*
1144 * print out.
1145 */
1146 convert_put_using(cv, encoded_pattern, path, linenum, p, NULL);
1147 break;
1148 }
1149 }
1150 if (pclose(ip) < 0)
1151 die("terminated abnormally.");
1152 convert_close(cv);
1153 strbuf_close(ib);
1154 if (vflag) {
1155 print_count(count);
1156 fprintf(stderr, " (using idutils index in '%s').\n", dbpath);
1157 }
1158 }
1159 /**
1160 * grep: @NAME{grep} pattern
1161 *
1162 * @param[in] pattern @NAME{POSIX} regular expression
1163 * @param argv
1164 * @param dbpath
1165 */
1166 void
1167 grep(const char *pattern, char *const *argv, const char *dbpath)
1168 {
1169 FILE *fp;
1170 CONVERT *cv;
1171 GFIND *gp = NULL;
1172 STRBUF *ib = strbuf_open(MAXBUFLEN);
1173 const char *path;
1174 char encoded_pattern[IDENTLEN];
1175 const char *buffer;
1176 int linenum, count;
1177 int flags = 0;
1178 int target = GPATH_SOURCE;
1179 regex_t preg;
1180 int user_specified = 1;
1181
1182 /*
1183 * convert spaces into %FF format.
1184 */
1185 encode(encoded_pattern, sizeof(encoded_pattern), pattern);
1186 /*
1187 * literal search available?
1188 */
1189 if (!literal) {
1190 const char *p = pattern;
1191 int normal = 1;
1192
1193 for (; *p; p++) {
1194 if (!(isalpha(*p) || isdigit(*p) || isblank(*p) || *p == '_')) {
1195 normal = 0;
1196 break;
1197 }
1198 }
1199 if (normal)
1200 literal = 1;
1201 }
1202 if (oflag)
1203 target = GPATH_BOTH;
1204 if (Oflag)
1205 target = GPATH_OTHER;
1206 if (literal) {
1207 literal_comple(pattern);
1208 } else {
1209 if (!Gflag)
1210 flags |= REG_EXTENDED;
1211 if (iflag)
1212 flags |= REG_ICASE;
1213 if (regcomp(&preg, pattern, flags) != 0)
1214 die("invalid regular expression.");
1215 }
1216 cv = convert_open(type, format, root, cwd, dbpath, stdout, NOTAGS);
1217 count = 0;
1218
1219 if (*argv && file_list)
1220 args_open_both(argv, file_list);
1221 else if (*argv)
1222 args_open(argv);
1223 else if (file_list)
1224 args_open_filelist(file_list);
1225 else {
1226 args_open_gfind(gp = gfind_open(dbpath, localprefix, target));
1227 user_specified = 0;
1228 }
1229 while ((path = args_read()) != NULL) {
1230 if (user_specified) {
1231 static char buf[MAXPATHLEN];
1232
1233 if (normalize(path, get_root_with_slash(), cwd, buf, sizeof(buf)) == NULL) {
1234 warning("'%s' is out of the source project.", path);
1235 continue;
1236 }
1237 if (test("d", buf)) {
1238 warning("'%s' is a directory. Ignored.", path);
1239 continue;
1240 }
1241 if (!test("f", buf)) {
1242 warning("'%s' not found. Ignored.", path);
1243 continue;
1244 }
1245 path = buf;
1246 }
1247 if (lflag && !locatestring(path, localprefix, MATCH_AT_FIRST))
1248 continue;
1249 if (literal) {
1250 literal_search(cv, path);
1251 } else {
1252 if (!(fp = fopen(path, "r")))
1253 die("cannot open file '%s'.", path);
1254 linenum = 0;
1255 while ((buffer = strbuf_fgets(ib, fp, STRBUF_NOCRLF)) != NULL) {
1256 int result = regexec(&preg, buffer, 0, 0, 0);
1257 linenum++;
1258 if ((!Vflag && result == 0) || (Vflag && result != 0)) {
1259 count++;
1260 if (format == FORMAT_PATH) {
1261 convert_put_path(cv, path);
1262 break;
1263 } else {
1264 convert_put_using(cv, encoded_pattern, path, linenum, buffer,
1265 (user_specified) ? NULL : gp->dbop->lastdat);
1266 }
1267 }
1268 }
1269 fclose(fp);
1270 }
1271 }
1272 args_close();
1273 convert_close(cv);
1274 strbuf_close(ib);
1275 if (literal == 0)
1276 regfree(&preg);
1277 if (vflag) {
1278 print_count(count);
1279 fprintf(stderr, " (no index used).\n");
1280 }
1281 }
1282 /**
1283 * pathlist: print candidate path list.
1284 *
1285 * @param[in] pattern
1286 * @param[in] dbpath
1287 */
1288 void
1289 pathlist(const char *pattern, const char *dbpath)
1290 {
1291 GFIND *gp;
1292 CONVERT *cv;
1293 const char *path, *p;
1294 regex_t preg;
1295 int count;
1296 int target = GPATH_SOURCE;
1297
1298 if (oflag)
1299 target = GPATH_BOTH;
1300 if (Oflag)
1301 target = GPATH_OTHER;
1302 if (pattern) {
1303 int flags = 0;
1304 char edit[IDENTLEN];
1305
1306 if (!Gflag)
1307 flags |= REG_EXTENDED;
1308 if (iflag || getconfb("icase_path"))
1309 flags |= REG_ICASE;
1310 #ifdef _WIN32
1311 flags |= REG_ICASE;
1312 #endif /* _WIN32 */
1313 /*
1314 * We assume '^aaa' as '^/aaa'.
1315 */
1316 if (*pattern == '^' && *(pattern + 1) != '/') {
1317 snprintf(edit, sizeof(edit), "^/%s", pattern + 1);
1318 pattern = edit;
1319 }
1320 if (regcomp(&preg, pattern, flags) != 0)
1321 die("invalid regular expression.");
1322 }
1323 if (!localprefix)
1324 localprefix = "./";
1325 cv = convert_open(type, format, root, cwd, dbpath, stdout, GPATH);
1326 count = 0;
1327
1328 gp = gfind_open(dbpath, localprefix, target);
1329 while ((path = gfind_read(gp)) != NULL) {
1330 /*
1331 * skip localprefix because end-user doesn't see it.
1332 */
1333 p = path + strlen(localprefix) - 1;
1334 if (pattern) {
1335 int result = regexec(&preg, p, 0, 0, 0);
1336
1337 if ((!Vflag && result != 0) || (Vflag && result == 0))
1338 continue;
1339 } else if (Vflag)
1340 continue;
1341 if (format == FORMAT_PATH)
1342 convert_put_path(cv, path);
1343 else
1344 convert_put_using(cv, "path", path, 1, " ", gp->dbop->lastdat);
1345 count++;
1346 }
1347 gfind_close(gp);
1348 convert_close(cv);
1349 if (pattern)
1350 regfree(&preg);
1351 if (vflag) {
1352 switch (count) {
1353 case 0:
1354 fprintf(stderr, "file not found");
1355 break;
1356 case 1:
1357 fprintf(stderr, "1 file located");
1358 break;
1359 default:
1360 fprintf(stderr, "%d files located", count);
1361 break;
1362 }
1363 fprintf(stderr, " (using '%s').\n", makepath(dbpath, dbname(GPATH), NULL));
1364 }
1365 }
1366 /**
1367 * @fn void parsefile(char *const *argv, const char *cwd, const char *root, const char *dbpath, int db)
1368 *
1369 * parsefile: parse file to pick up tags.
1370 *
1371 * @param[in] argv
1372 * @param[in] cwd current directory
1373 * @param[in] root root directory of source tree
1374 * @param[in] dbpath dbpath
1375 * @param[in] db type of parse
1376 */
1377 #define TARGET_DEF (1 << GTAGS)
1378 #define TARGET_REF (1 << GRTAGS)
1379 #define TARGET_SYM (1 << GSYMS)
1380 struct parsefile_data {
1381 CONVERT *cv;
1382 DBOP *dbop;
1383 int target;
1384 int extractmethod;
1385 int count;
1386 const char *fid; /**< fid of the file under processing */
1387 };
1388 static void
1389 put_syms(int type, const char *tag, int lno, const char *path, const char *line_image, void *arg)
1390 {
1391 struct parsefile_data *data = arg;
1392 const char *key;
1393
1394 if (format == FORMAT_PATH && data->count > 0)
1395 return;
1396 switch (type) {
1397 case PARSER_DEF:
1398 if (!(data->target & TARGET_DEF))
1399 return;
1400 break;
1401 case PARSER_REF_SYM:
1402 if (!(data->target & (TARGET_REF | TARGET_SYM)))
1403 return;
1404 /*
1405 * extract method when class method definition.
1406 *
1407 * Ex: Class::method(...)
1408 *
1409 * key = 'method'
1410 * data = 'Class::method 103 ./class.cpp ...'
1411 */
1412 if (data->extractmethod) {
1413 if ((key = locatestring(tag, ".", MATCH_LAST)) != NULL)
1414 key++;
1415 else if ((key = locatestring(tag, "::", MATCH_LAST)) != NULL)
1416 key += 2;
1417 else
1418 key = tag;
1419 } else {
1420 key = tag;
1421 }
1422 if (data->target == TARGET_REF || data->target == TARGET_SYM) {
1423 if (dbop_get(data->dbop, key) != NULL) {
1424 if (!(data->target & TARGET_REF))
1425 return;
1426 } else {
1427 if (!(data->target & TARGET_SYM))
1428 return;
1429 }
1430 }
1431 break;
1432 default:
1433 return;
1434 }
1435 convert_put_using(data->cv, tag, path, lno, line_image, data->fid);
1436 data->count++;
1437 }
1438 void
1439 parsefile(char *const *argv, const char *cwd, const char *root, const char *dbpath, int db)
1440 {
1441 int count = 0;
1442 int flags = 0;
1443 STRBUF *sb = strbuf_open(0);
1444 char *langmap;
1445 const char *plugin_parser, *av;
1446 char path[MAXPATHLEN];
1447 struct parsefile_data data;
1448
1449 flags = 0;
1450 if (vflag)
1451 flags |= PARSER_VERBOSE;
1452 if (debug)
1453 flags |= PARSER_DEBUG;
1454 /*
1455 if (wflag)
1456 flags |= PARSER_WARNING;
1457 */
1458 if (db == GRTAGS + GSYMS)
1459 data.target = TARGET_REF|TARGET_SYM;
1460 else
1461 data.target = 1 << db;
1462 data.extractmethod = getconfb("extractmethod");
1463 if (getconfs("langmap", sb))
1464 langmap = check_strdup(strbuf_value(sb));
1465 else
1466 langmap = NULL;
1467 strbuf_reset(sb);
1468 if (getconfs("gtags_parser", sb))
1469 plugin_parser = strbuf_value(sb);
1470 else
1471 plugin_parser = NULL;
1472 data.cv = convert_open(type, format, root, cwd, dbpath, stdout, db);
1473 if (gpath_open(dbpath, 0) < 0)
1474 die("GPATH not found.");
1475 if (data.target == TARGET_REF || data.target == TARGET_SYM) {
1476 data.dbop = dbop_open(makepath(dbpath, dbname(GTAGS), NULL), 0, 0, 0);
1477 if (data.dbop == NULL)
1478 die("%s not found.", dbname(GTAGS));
1479 } else {
1480 data.dbop = NULL;
1481 }
1482 data.fid = NULL;
1483 parser_init(langmap, plugin_parser);
1484 if (langmap != NULL)
1485 free(langmap);
1486
1487 if (*argv && file_list)
1488 args_open_both(argv, file_list);
1489 else if (*argv)
1490 args_open(argv);
1491 else if (file_list)
1492 args_open_filelist(file_list);
1493 else
1494 args_open_nop();
1495 while ((av = args_read()) != NULL) {
1496 /*
1497 * convert the path into relative to the root directory of source tree.
1498 */
1499 if (normalize(av, get_root_with_slash(), cwd, path, sizeof(path)) == NULL) {
1500 if (!qflag)
1501 die("'%s' is out of the source project.", av);
1502 continue;
1503 }
1504 if (!test("f", makepath(root, path, NULL))) {
1505 if (!qflag) {
1506 if (test("d", NULL))
1507 die("'%s' is not a source file.", av);
1508 else
1509 die("'%s' not found.", av);
1510 }
1511 continue;
1512 }
1513 /*
1514 * Memorize the file id of the path. This is used in put_syms().
1515 */
1516 {
1517 static char s_fid[MAXFIDLEN];
1518 int type = 0;
1519 const char *p = gpath_path2fid(path, &type);
1520
1521 if (!p || type != GPATH_SOURCE) {
1522 if (!qflag)
1523 die("'%s' is not a source file.", av);
1524 continue;
1525 }
1526 strlimcpy(s_fid, p, sizeof(s_fid));
1527 data.fid = s_fid;
1528 }
1529 if (lflag && !locatestring(path, localprefix, MATCH_AT_FIRST))
1530 continue;
1531 data.count = 0;
1532 parse_file(path, flags, put_syms, &data);
1533 count += data.count;
1534 }
1535 args_close();
1536 parser_exit();
1537 /*
1538 * Settlement
1539 */
1540 if (data.dbop != NULL)
1541 dbop_close(data.dbop);
1542 gpath_close();
1543 convert_close(data.cv);
1544 strbuf_close(sb);
1545 if (vflag) {
1546 print_count(count);
1547 fprintf(stderr, " (no index used).\n");
1548 }
1549 }
1550 /**
1551 * @fn int search(const char *pattern, const char *root, const char *cwd, const char *dbpath, int db)
1552 *
1553 * search: search specified function
1554 *
1555 * @param[in] pattern search pattern
1556 * @param[in] root root of source tree
1557 * @param[in] cwd current directory
1558 * @param[in] dbpath database directory
1559 * @param[in] db #GTAGS,#GRTAGS,#GSYMS
1560 * @return count of output lines
1561 */
1562 /** get next number and seek to the next character */
1563 #define GET_NEXT_NUMBER(p) do { \
1564 if (!isdigit(*p)) \
1565 p++; \
1566 for (n = 0; isdigit(*p); p++) \
1567 n = n * 10 + (*p - '0'); \
1568 } while (0)
1569 int
1570 search(const char *pattern, const char *root, const char *cwd, const char *dbpath, int db)
1571 {
1572 CONVERT *cv;
1573 int count = 0;
1574 GTOP *gtop;
1575 GTP *gtp;
1576 int flags = 0;
1577 STRBUF *sb = NULL, *ib = NULL;
1578 char curpath[MAXPATHLEN], curtag[IDENTLEN];
1579 FILE *fp = NULL;
1580 const char *src = "";
1581 int lineno, last_lineno;
1582
1583 lineno = last_lineno = 0;
1584 curpath[0] = curtag[0] = '\0';
1585 /*
1586 * open tag file.
1587 */
1588 gtop = gtags_open(dbpath, root, db, GTAGS_READ, 0);
1589 cv = convert_open(type, format, root, cwd, dbpath, stdout, db);
1590 /*
1591 * search through tag file.
1592 */
1593 if (nofilter & SORT_FILTER)
1594 flags |= GTOP_NOSORT;
1595 if (iflag) {
1596 if (!isregex(pattern)) {
1597 sb = strbuf_open(0);
1598 strbuf_putc(sb, '^');
1599 strbuf_puts(sb, pattern);
1600 strbuf_putc(sb, '$');
1601 pattern = strbuf_value(sb);
1602 }
1603 flags |= GTOP_IGNORECASE;
1604 }
1605 if (Gflag)
1606 flags |= GTOP_BASICREGEX;
1607 if (format == FORMAT_PATH)
1608 flags |= GTOP_PATH;
1609 if (gtop->format & GTAGS_COMPACT)
1610 ib = strbuf_open(0);
1611 for (gtp = gtags_first(gtop, pattern, flags); gtp; gtp = gtags_next(gtop)) {
1612 if (lflag && !locatestring(gtp->path, localprefix, MATCH_AT_FIRST))
1613 continue;
1614 if (format == FORMAT_PATH) {
1615 convert_put_path(cv, gtp->path);
1616 count++;
1617 } else if (gtop->format & GTAGS_COMPACT) {
1618 /*
1619 * Compact format:
1620 * a b
1621 * tagline = <file id> <tag name> <line no>,...
1622 */
1623 char *p = (char *)gtp->tagline;
1624 const char *fid, *tagname;
1625 int n = 0;
1626
1627 fid = p;
1628 while (*p != ' ')
1629 p++;
1630 *p++ = '\0'; /* a */
1631 tagname = p;
1632 while (*p != ' ')
1633 p++;
1634 *p++ = '\0'; /* b */
1635 /*
1636 * Reopen or rewind source file.
1637 */
1638 if (!nosource) {
1639 if (strcmp(gtp->path, curpath) != 0) {
1640 if (curpath[0] != '\0' && fp != NULL)
1641 fclose(fp);
1642 strlimcpy(curtag, tagname, sizeof(curtag));
1643 strlimcpy(curpath, gtp->path, sizeof(curpath));
1644 /*
1645 * Use absolute path name to support GTAGSROOT
1646 * environment variable.
1647 */
1648 fp = fopen(makepath(root, curpath, NULL), "r");
1649 if (fp == NULL)
1650 warning("source file '%s' is not available.", curpath);
1651 last_lineno = lineno = 0;
1652 } else if (strcmp(gtp->tag, curtag) != 0) {
1653 strlimcpy(curtag, gtp->tag, sizeof(curtag));
1654 if (atoi(p) < last_lineno && fp != NULL) {
1655 rewind(fp);
1656 lineno = 0;
1657 }
1658 last_lineno = 0;
1659 }
1660 }
1661 /*
1662 * Unfold compact format.
1663 */
1664 if (!isdigit(*p))
1665 die("illegal compact format.");
1666 if (gtop->format & GTAGS_COMPLINE) {
1667 /*
1668 *
1669 * If GTAGS_COMPLINE flag is set, each line number is expressed as
1670 * the difference from the previous line number except for the head.
1671 * Please see flush_pool() in libutil/gtagsop.c for the details.
1672 */
1673 int last = 0, cont = 0;
1674
1675 while (*p || cont > 0) {
1676 if (cont > 0) {
1677 n = last + 1;
1678 if (n > cont) {
1679 cont = 0;
1680 continue;
1681 }
1682 } else if (isdigit(*p)) {
1683 GET_NEXT_NUMBER(p);
1684 } else if (*p == '-') {
1685 GET_NEXT_NUMBER(p);
1686 cont = n + last;
1687 n = last + 1;
1688 } else if (*p == ',') {
1689 GET_NEXT_NUMBER(p);
1690 n += last;
1691 }
1692 if (last_lineno != n && fp) {
1693 while (lineno < n) {
1694 if (!(src = strbuf_fgets(ib, fp, STRBUF_NOCRLF))) {
1695 src = "";
1696 fclose(fp);
1697 fp = NULL;
1698 break;
1699 }
1700 lineno++;
1701 }
1702 }
1703 if (gtop->format & GTAGS_COMPNAME)
1704 tagname = (char *)uncompress(tagname, gtp->tag);
1705 convert_put_using(cv, tagname, gtp->path, n, src, fid);
1706 count++;
1707 last_lineno = last = n;
1708 }
1709 } else {
1710 /*
1711 * In fact, when GTAGS_COMPACT is set, GTAGS_COMPLINE is allways set.
1712 * Therefore, the following code are not actually used.
1713 * However, it is left for some test.
1714 */
1715 while (*p) {
1716 for (n = 0; isdigit(*p); p++)
1717 n = n * 10 + *p - '0';
1718 if (*p == ',')
1719 p++;
1720 if (last_lineno == n)
1721 continue;
1722 if (last_lineno != n && fp) {
1723 while (lineno < n) {
1724 if (!(src = strbuf_fgets(ib, fp, STRBUF_NOCRLF))) {
1725 src = "";
1726 fclose(fp);
1727 fp = NULL;
1728 break;
1729 }
1730 lineno++;
1731 }
1732 }
1733 if (gtop->format & GTAGS_COMPNAME)
1734 tagname = (char *)uncompress(tagname, gtp->tag);
1735 convert_put_using(cv, tagname, gtp->path, n, src, fid);
1736 count++;
1737 last_lineno = n;
1738 }
1739 }
1740 } else {
1741 /*
1742 * Standard format:
1743 * a b c
1744 * tagline = <file id> <tag name> <line no> <line image>
1745 */
1746 char *p = (char *)gtp->tagline;
1747 char namebuf[IDENTLEN];
1748 const char *fid, *tagname, *image;
1749
1750 fid = p;
1751 while (*p != ' ')
1752 p++;
1753 *p++ = '\0'; /* a */
1754 tagname = p;
1755 while (*p != ' ')
1756 p++;
1757 *p++ = '\0'; /* b */
1758 if (gtop->format & GTAGS_COMPNAME) {
1759 strlimcpy(namebuf, (char *)uncompress(tagname, gtp->tag), sizeof(namebuf));
1760 tagname = namebuf;
1761 }
1762 if (nosource) {
1763 image = " ";
1764 } else {
1765 while (*p != ' ')
1766 p++;
1767 image = p + 1; /* c + 1 */
1768 if (gtop->format & GTAGS_COMPRESS)
1769 image = (char *)uncompress(image, gtp->tag);
1770 }
1771 convert_put_using(cv, tagname, gtp->path, gtp->lineno, image, fid);
1772 count++;
1773 }
1774 }
1775 convert_close(cv);
1776 if (sb)
1777 strbuf_close(sb);
1778 if (ib)
1779 strbuf_close(ib);
1780 if (fp)
1781 fclose(fp);
1782 gtags_close(gtop);
1783 return count;
1784 }
1785 /**
1786 * tagsearch: execute tag search
1787 *
1788 * @param[in] pattern search pattern
1789 * @param[in] cwd current directory
1790 * @param[in] root root of source tree
1791 * @param[in] dbpath database directory
1792 * @param[in] db #GTAGS,#GRTAGS,#GSYMS
1793 */
1794 void
1795 tagsearch(const char *pattern, const char *cwd, const char *root, const char *dbpath, int db)
1796 {
1797 int count, total = 0;
1798 char libdbpath[MAXPATHLEN];
1799
1800 /*
1801 * search in current source tree.
1802 */
1803 count = search(pattern, root, cwd, dbpath, db);
1804 total += count;
1805 /*
1806 * search in library path.
1807 */
1808 if (abslib)
1809 type = PATH_ABSOLUTE;
1810 if (db == GTAGS && getenv("GTAGSLIBPATH") && (count == 0 || Tflag) && !lflag) {
1811 STRBUF *sb = strbuf_open(0);
1812 char *libdir, *nextp = NULL;
1813
1814 strbuf_puts(sb, getenv("GTAGSLIBPATH"));
1815 /*
1816 * search for each tree in the library path.
1817 */
1818 for (libdir = strbuf_value(sb); libdir; libdir = nextp) {
1819 if ((nextp = locatestring(libdir, PATHSEP, MATCH_FIRST)) != NULL)
1820 *nextp++ = 0;
1821 if (!gtagsexist(libdir, libdbpath, sizeof(libdbpath), 0))
1822 continue;
1823 if (!strcmp(dbpath, libdbpath))
1824 continue;
1825 if (!test("f", makepath(libdbpath, dbname(db), NULL)))
1826 continue;
1827 /*
1828 * search again
1829 */
1830 count = search(pattern, libdir, cwd, libdbpath, db);
1831 total += count;
1832 if (count > 0 && !Tflag) {
1833 /* for verbose message */
1834 dbpath = libdbpath;
1835 break;
1836 }
1837 }
1838 strbuf_close(sb);
1839 }
1840 if (vflag) {
1841 print_count(total);
1842 if (!Tflag)
1843 fprintf(stderr, " (using '%s')", makepath(dbpath, dbname(db), NULL));
1844 fputs(".\n", stderr);
1845 }
1846 }
1847 /**
1848 * encode: string copy with converting blank chars into @CODE{\%ff} format.
1849 *
1850 * @param[out] to result
1851 * @param[in] size size of @a to buffer
1852 * @param[in] from string
1853 */
1854 void
1855 encode(char *to, int size, const char *from)
1856 {
1857 const char *p;
1858 char *e = to;
1859
1860 for (p = from; *p; p++) {
1861 if (*p == '%' || *p == ' ' || *p == '\t') {
1862 if (size <= 3)
1863 break;
1864 snprintf(e, size, "%%%02x", *p);
1865 e += 3;
1866 size -= 3;
1867 } else {
1868 if (size <= 1)
1869 break;
1870 *e++ = *p;
1871 size--;
1872 }
1873 }
1874 *e = 0;
1875 }
/* */