"Fossies" - the Fresh Open Source Software Archive 
Member "global-6.6.5/gtags/gtags.c" (3 Sep 2020, 28108 Bytes) of package /linux/misc/global-6.6.5.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "gtags.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
6.6.4_vs_6.6.5.
1 /*
2 * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2008,
3 * 2009, 2010, 2012, 2014, 2015, 2016, 2018, 2020
4 * Tama Communications Corporation
5 *
6 * This file is part of GNU GLOBAL.
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <ctype.h>
28 #include <utime.h>
29 #include <signal.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #if TIME_WITH_SYS_TIME
33 #include <sys/time.h>
34 #include <time.h>
35 #else
36 #if HAVE_SYS_TIME_H
37 #include <sys/time.h>
38 #else
39 #include <time.h>
40 #endif
41 #endif
42 #ifdef STDC_HEADERS
43 #include <stdlib.h>
44 #endif
45 #ifdef HAVE_STRING_H
46 #include <string.h>
47 #else
48 #include <strings.h>
49 #endif
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53 #include "getopt.h"
54
55 #include "global.h"
56 #include "parser.h"
57 #include "const.h"
58
59 /*
60 * enable [set] globbing, if available
61 */
62 #ifdef __CRT_GLOB_BRACKET_GROUPS__
63 int _CRT_glob = __CRT_GLOB_USE_MINGW__ | __CRT_GLOB_BRACKET_GROUPS__;
64 #endif
65
66 /*
67 gtags - create tag files for global.
68 */
69
70 static void usage(void);
71 static void help(void);
72 int printconf(const char *);
73 int main(int, char **);
74 int incremental(const char *, const char *);
75 void updatetags(const char *, const char *, IDSET *, STRBUF *);
76 void createtags(const char *, const char *);
77
78 int cflag; /**< compact format */
79 int iflag; /**< incremental update */
80 int Iflag; /**< make idutils index */
81 int Oflag; /**< use objdir */
82 int qflag; /**< quiet mode */
83 int wflag; /**< warning message */
84 int vflag; /**< verbose mode */
85 int show_version;
86 int show_help;
87 int show_config;
88 int skip_unreadable;
89 int skip_symlink;
90 int accept_dotfiles;
91 char *gtagsconf;
92 char *gtagslabel;
93 int debug;
94 const char *config_name;
95 const char *file_list;
96 const char *dump_target;
97 char *single_update;
98 int statistics = STATISTICS_STYLE_NONE;
99 int explain;
100 #ifdef USE_SQLITE3
101 int use_sqlite3;
102 #endif
103
104 #define GTAGSFILES "gtags.files"
105
106 int extractmethod;
107 int total;
108
109 static void
110 usage(void)
111 {
112 if (!qflag)
113 fputs(usage_const, stderr);
114 exit(2);
115 }
116 static void
117 help(void)
118 {
119 fputs(usage_const, stdout);
120 fputs(help_const, stdout);
121 exit(0);
122 }
123 /**
124 * printconf: print configuration data.
125 *
126 * @param[in] name label of config data
127 * @return exit code
128 */
129 int
130 printconf(const char *name)
131 {
132 int num;
133 int exist = 1;
134
135 if (getconfn(name, &num))
136 fprintf(stdout, "%d\n", num);
137 else if (getconfb(name))
138 fprintf(stdout, "1\n");
139 else {
140 STRBUF *sb = strbuf_open(0);
141 if (getconfs(name, sb))
142 fprintf(stdout, "%s\n", strbuf_value(sb));
143 else
144 exist = 0;
145 strbuf_close(sb);
146 }
147 return exist;
148 }
149
150 const char *short_options = "cC:d:f:iIn:oOqvwse";
151 struct option const long_options[] = {
152 /*
153 * These options have long name and short name.
154 * We throw them to the processing of short options.
155 *
156 * Though the -o(--omit-gsyms) was removed, this code
157 * is left for compatibility.
158 */
159 {"compact", no_argument, NULL, 'c'},
160 {"directory", required_argument, NULL, 'C'},
161 {"dump", required_argument, NULL, 'd'},
162 {"file", required_argument, NULL, 'f'},
163 {"idutils", no_argument, NULL, 'I'},
164 {"incremental", no_argument, NULL, 'i'},
165 {"max-args", required_argument, NULL, 'n'},
166 {"omit-gsyms", no_argument, NULL, 'o'}, /* removed */
167 {"objdir", no_argument, NULL, 'O'},
168 {"quiet", no_argument, NULL, 'q'},
169 {"verbose", no_argument, NULL, 'v'},
170 {"warning", no_argument, NULL, 'w'},
171
172 /*
173 * The following are long name only.
174 */
175 #define OPT_CONFIG 128
176 #define OPT_PATH 129
177 #define OPT_SINGLE_UPDATE 130
178 #define OPT_ACCEPT_DOTFILES 131
179 #define OPT_SKIP_UNREADABLE 132
180 #define OPT_GTAGSSKIP_SYMLINK 133
181 /* flag value */
182 {"accept-dotfiles", no_argument, NULL, OPT_ACCEPT_DOTFILES},
183 {"debug", no_argument, &debug, 1},
184 {"explain", no_argument, &explain, 1},
185 #ifdef USE_SQLITE3
186 {"sqlite3", no_argument, &use_sqlite3, 1},
187 #endif
188 {"skip-unreadable", no_argument, NULL, OPT_SKIP_UNREADABLE},
189 {"statistics", no_argument, &statistics, STATISTICS_STYLE_TABLE},
190 {"version", no_argument, &show_version, 1},
191 {"help", no_argument, &show_help, 1},
192
193 /* accept value */
194 {"config", optional_argument, NULL, OPT_CONFIG},
195 {"gtagsconf", required_argument, NULL, OPT_GTAGSCONF},
196 {"gtagslabel", required_argument, NULL, OPT_GTAGSLABEL},
197 {"skip-symlink", optional_argument, NULL, OPT_GTAGSSKIP_SYMLINK},
198 {"path", required_argument, NULL, OPT_PATH},
199 {"single-update", required_argument, NULL, OPT_SINGLE_UPDATE},
200 { 0 }
201 };
202
203 static const char *langmap = DEFAULTLANGMAP; /**< langmap */
204 static const char *gtags_parser; /**< gtags_parser */
205 /**
206 * load configuration variables.
207 */
208 static void
209 configuration()
210 {
211 STRBUF *sb = strbuf_open(0);
212
213 if (getconfb("extractmethod"))
214 extractmethod = 1;
215 strbuf_reset(sb);
216 if (getconfs("langmap", sb))
217 langmap = check_strdup(strbuf_value(sb));
218 strbuf_reset(sb);
219 if (getconfs("gtags_parser", sb))
220 gtags_parser = check_strdup(strbuf_value(sb));
221 strbuf_close(sb);
222 }
223 int
224 main(int argc, char **argv)
225 {
226 char dbpath[MAXPATHLEN];
227 char cwd[MAXPATHLEN];
228 STRBUF *sb = strbuf_open(0);
229 int optchar;
230 int option_index = 0;
231 STATISTICS_TIME *tim;
232
233 /*
234 * pick up --gtagsconf, --gtagslabel and --directory (-C).
235 */
236 if (preparse_options(argc, argv) < 0)
237 usage();
238 /*
239 * Get the project root directory.
240 */
241 if (!vgetcwd(cwd, MAXPATHLEN))
242 die("cannot get current directory.");
243 canonpath(cwd);
244 /*
245 * Load configuration file.
246 */
247 openconf(cwd);
248 configuration();
249 setenv_from_config();
250 {
251 char *env = getenv("GTAGS_OPTIONS");
252 if (env && *env)
253 argv = prepend_options(&argc, argv, env);
254 }
255 /*
256 * Execute gtags_hook before the jobs.
257 * The hook is not executed when the following options are specified.
258 * --help, --dump, --config, --version
259 * These are informational options only.
260 */
261 {
262 int skip_hook = 0;
263
264 /* Make a decision whether to execute gtags_hook. */
265 while ((optchar = getopt_long(argc, argv,
266 short_options, long_options, &option_index)) != EOF) {
267 switch (optchar) {
268 case OPT_CONFIG:
269 case 'd':
270 skip_hook++;
271 break;
272 default:
273 break;
274 }
275 }
276 optind = 1; /* Reset getopt(3) library. */
277 if (show_version || show_help)
278 skip_hook++;
279 if (skip_hook) {
280 if (debug)
281 fprintf(stderr, "Gtags_hook is skipped.\n");
282 } else if (getconfs("gtags_hook", sb)) {
283 char *p = serialize_options(argc, argv);
284 set_env("GTAGS_COMMANDLINE", p);
285 free(p);
286 if (system(strbuf_value(sb)))
287 fprintf(stderr, "gtags-hook failed: %s\n", strbuf_value(sb));
288 }
289 }
290 logging_arguments(argc, argv);
291 while ((optchar = getopt_long(argc, argv, short_options, long_options, &option_index)) != EOF) {
292 switch (optchar) {
293 case 0:
294 /* already flags set */
295 break;
296 case OPT_CONFIG:
297 show_config = 1;
298 if (optarg)
299 config_name = optarg;
300 break;
301 case OPT_GTAGSCONF:
302 case OPT_GTAGSLABEL:
303 case 'C':
304 /* These options are already parsed in preparse_options(). */
305 break;
306 case OPT_SINGLE_UPDATE:
307 iflag++;
308 single_update = optarg;
309 break;
310 case OPT_ACCEPT_DOTFILES:
311 accept_dotfiles = 1;
312 break;
313 case OPT_SKIP_UNREADABLE:
314 skip_unreadable = 1;
315 break;
316 case OPT_GTAGSSKIP_SYMLINK:
317 skip_symlink = SKIP_SYMLINK_FOR_ALL;
318 if (optarg) {
319 if (!strcmp(optarg, "f"))
320 skip_symlink = SKIP_SYMLINK_FOR_FILE;
321 else if (!strcmp(optarg, "d"))
322 skip_symlink = SKIP_SYMLINK_FOR_DIR;
323 else if (!strcmp(optarg, "a"))
324 skip_symlink = SKIP_SYMLINK_FOR_ALL;
325 else
326 die("--skip-symlink: %s: unknown type.", optarg);
327 }
328 break;
329 case 'c':
330 cflag++;
331 break;
332 case 'd':
333 dump_target = optarg;
334 break;
335 case 'f':
336 file_list = optarg;
337 break;
338 case 'i':
339 iflag++;
340 break;
341 case 'I':
342 Iflag++;
343 break;
344 case 'o':
345 /*
346 * Though the -o(--omit-gsyms) was removed, this code
347 * is left for compatibility.
348 */
349 break;
350 case 'O':
351 Oflag++;
352 break;
353 case 'q':
354 qflag++;
355 break;
356 case 'w':
357 wflag++;
358 break;
359 case 'v':
360 vflag++;
361 break;
362 default:
363 usage();
364 break;
365 }
366 }
367 if (skip_symlink)
368 set_skip_symlink(skip_symlink);
369 if (qflag) {
370 vflag = 0;
371 setquiet();
372 }
373 if (vflag)
374 setverbose();
375 if (wflag)
376 set_langmap_wflag();
377 if (show_version)
378 version(NULL, vflag);
379 if (show_help)
380 help();
381
382 argc -= optind;
383 argv += optind;
384
385 /* If dbpath is specified, -O(--objdir) option is ignored. */
386 if (argc > 0)
387 Oflag = 0;
388 if (show_config) {
389 openconf(setupdbpath(0) == 0 ? get_root() : NULL);
390 if (config_name)
391 printconf(config_name);
392 else
393 fprintf(stdout, "%s\n", getconfline());
394 exit(0);
395 } else if (dump_target) {
396 /*
397 * Dump a tag file.
398 */
399 DBOP *dbop = NULL;
400 const char *dat = 0;
401 int is_gpath = 0;
402
403 if (!test("f", dump_target))
404 die("file '%s' not found.", dump_target);
405 if ((dbop = dbop_open(dump_target, 0, 0, DBOP_RAW)) == NULL)
406 die("file '%s' is not a tag file.", dump_target);
407 /*
408 * The file which has a NEXTKEY record is GPATH.
409 */
410 if (dbop_get(dbop, NEXTKEY))
411 is_gpath = 1;
412 for (dat = dbop_first(dbop, NULL, NULL, 0); dat != NULL; dat = dbop_next(dbop)) {
413 const char *flag = is_gpath ? dbop_getflag(dbop) : "";
414
415 if (*flag)
416 printf("%s\t%s\t%s\n", dbop->lastkey, dat, flag);
417 else
418 printf("%s\t%s\n", dbop->lastkey, dat);
419 }
420 dbop_close(dbop);
421 exit(0);
422 } else if (Iflag) {
423 #define REQUIRED_MKID_VERSION "4.5"
424 char *p;
425
426 if (!usable("mkid"))
427 die("mkid not found.");
428 if (read_first_line("mkid --version", sb))
429 die("mkid cannot executed.");
430 p = strrchr(strbuf_value(sb), ' ');
431 if (p == NULL)
432 die("invalid version string of mkid: %s", strbuf_value(sb));
433 switch (check_version(p + 1, REQUIRED_MKID_VERSION)
434 #ifdef _WIN32
435 | strcmp(p + 1, "3.2.99") == 0
436 #endif
437 ) {
438 case 1: break; /* OK */
439 case 0: die("mkid version %s or later is required.", REQUIRED_MKID_VERSION);
440 default: die("invalid version string of mkid: %s", strbuf_value(sb));
441 }
442 }
443
444 /*
445 * If 'gtags.files' exists, use it as a file list.
446 * If the file_list other than "-" is given, it must be readable file.
447 */
448 if (file_list == NULL && test("f", GTAGSFILES))
449 file_list = GTAGSFILES;
450 if (file_list && strcmp(file_list, "-")) {
451 if (test("d", file_list))
452 die("'%s' is a directory.", file_list);
453 else if (!test("f", file_list))
454 die("'%s' not found.", file_list);
455 else if (!test("r", file_list))
456 die("'%s' is not readable.", file_list);
457 }
458 /*
459 * Regularize the path name for single updating (--single-update).
460 */
461 if (single_update) {
462 static char regular_path_name[MAXPATHLEN];
463 char *p = single_update;
464
465 #if _WIN32 || __DJGPP__
466 for (; *p; p++)
467 if (*p == '\\')
468 *p = '/';
469 p = single_update;
470 #define LOCATEFLAG MATCH_AT_FIRST|IGNORE_CASE
471 #else
472 #define LOCATEFLAG MATCH_AT_FIRST
473 #endif
474 if (isabspath(p)) {
475 char *q = locatestring(p, cwd, LOCATEFLAG);
476
477 if (q && *q == '/')
478 snprintf(regular_path_name, MAXPATHLEN, "./%s", q + 1);
479 else
480 die("path '%s' is out of the project.", p);
481
482 } else {
483 if (p[0] == '.' && p[1] == '/')
484 snprintf(regular_path_name, MAXPATHLEN, "%s", p);
485 else
486 snprintf(regular_path_name, MAXPATHLEN, "./%s", p);
487 }
488 single_update = regular_path_name;
489 }
490 /*
491 * Decide directory (dbpath) in which gtags make tag files.
492 *
493 * Gtags create tag files at current directory by default.
494 * If dbpath is specified as an argument then use it.
495 * If the -i option specified and both GTAGS and GRTAGS exists
496 * at one of the candidate directories then gtags use existing
497 * tag files.
498 */
499 if (iflag) {
500 if (argc > 0)
501 realpath(*argv, dbpath);
502 else if (!gtagsexist(cwd, dbpath, MAXPATHLEN, vflag))
503 strlimcpy(dbpath, cwd, sizeof(dbpath));
504 } else {
505 if (argc > 0)
506 realpath(*argv, dbpath);
507 else if (Oflag) {
508 char *objdir = getobjdir(cwd, vflag);
509
510 if (objdir == NULL)
511 die("Objdir not found.");
512 strlimcpy(dbpath, objdir, sizeof(dbpath));
513 } else
514 strlimcpy(dbpath, cwd, sizeof(dbpath));
515 }
516 if (iflag && (!test("f", makepath(dbpath, dbname(GTAGS), NULL)) ||
517 !test("f", makepath(dbpath, dbname(GRTAGS), NULL)) ||
518 !test("f", makepath(dbpath, dbname(GPATH), NULL)))) {
519 if (wflag)
520 warning("GTAGS, GRTAGS or GPATH not found. -i option ignored.");
521 iflag = 0;
522 }
523 if (!test("d", dbpath))
524 die("directory '%s' not found.", dbpath);
525 /*
526 * Start processing.
527 */
528 if (accept_dotfiles)
529 set_accept_dotfiles();
530 if (skip_unreadable)
531 set_skip_unreadable();
532 if (vflag) {
533 const char *config_path = getconfigpath();
534 const char *config_label = getconfiglabel();
535
536 fprintf(stderr, "[%s] Gtags started.\n", now());
537 if (config_path)
538 fprintf(stderr, " Using configuration file '%s'.\n", config_path);
539 else {
540 fprintf(stderr, " Using default configuration.\n");
541 if (getenv("GTAGSLABEL"))
542 fprintf(stderr, " GTAGSLABEL(--gtagslabel) ignored since configuration file not found.\n");
543 }
544 if (config_label)
545 fprintf(stderr, " Using configuration label '%s'.\n", config_label);
546 if (file_list)
547 fprintf(stderr, " Using '%s' as a file list.\n", file_list);
548 }
549 /*
550 * initialize parser.
551 */
552 if (vflag && gtags_parser)
553 fprintf(stderr, " Using plug-in parser.\n");
554 parser_init(langmap, gtags_parser);
555 /*
556 * Start statistics.
557 */
558 init_statistics();
559 /*
560 * incremental update.
561 */
562 if (iflag) {
563 /*
564 * Version check. If existing tag files are old enough
565 * gtagsopen() abort with error message.
566 */
567 GTOP *gtop = gtags_open(dbpath, cwd, GTAGS, GTAGS_MODIFY, 0);
568 gtags_close(gtop);
569 /*
570 * GPATH is needed for incremental updating.
571 * Gtags check whether or not GPATH exist, since it may be
572 * removed by mistake.
573 */
574 if (!test("f", makepath(dbpath, dbname(GPATH), NULL)))
575 die("Old version tag file found. Please remake it.");
576 (void)incremental(dbpath, cwd);
577 print_statistics(statistics);
578 exit(0);
579 }
580 /*
581 * create GTAGS and GRTAGS
582 */
583 createtags(dbpath, cwd);
584 /*
585 * create idutils index.
586 */
587 if (Iflag) {
588 FILE *op;
589 GFIND *gp;
590 const char *path;
591
592 tim = statistics_time_start("Time of creating ID");
593 if (vflag)
594 fprintf(stderr, "[%s] Creating indexes for idutils.\n", now());
595 strbuf_reset(sb);
596 /*
597 * Since idutils stores the value of PWD in ID file, we need to
598 * force idutils to follow our style.
599 */
600 #if _WIN32 || __DJGPP__
601 strbuf_puts(sb, "mkid --files0-from=-");
602 #else
603 strbuf_sprintf(sb, "PWD=%s mkid --files0-from=-", quote_shell(cwd));
604 #endif
605 if (vflag)
606 strbuf_puts(sb, " -v");
607 strbuf_sprintf(sb, " --file=%s/ID", quote_shell(dbpath));
608 if (vflag) {
609 #ifdef __DJGPP__
610 if (is_unixy()) /* test for 4DOS as well? */
611 #endif
612 strbuf_puts(sb, " 1>&2");
613 } else {
614 strbuf_puts(sb, " >" NULL_DEVICE);
615 #ifdef __DJGPP__
616 if (is_unixy()) /* test for 4DOS as well? */
617 #endif
618 strbuf_puts(sb, " 2>&1");
619 }
620 if (debug)
621 fprintf(stderr, "executing mkid like: %s\n", strbuf_value(sb));
622 op = popen(strbuf_value(sb), "w");
623 if (op == NULL)
624 die("cannot execute '%s'.", strbuf_value(sb));
625 gp = gfind_open(dbpath, NULL, GPATH_BOTH, 0);
626 while ((path = gfind_read(gp)) != NULL) {
627 fputs(path, op);
628 fputc('\0', op);
629 }
630 gfind_close(gp);
631 if (pclose(op) != 0)
632 die("terminated abnormally '%s' (errno = %d).", strbuf_value(sb), errno);
633 if (test("f", makepath(dbpath, "ID", NULL)))
634 if (chmod(makepath(dbpath, "ID", NULL), 0644) < 0)
635 die("cannot chmod ID file.");
636 statistics_time_end(tim);
637 }
638 if (vflag)
639 fprintf(stderr, "[%s] Done.\n", now());
640 closeconf();
641 strbuf_close(sb);
642 print_statistics(statistics);
643 return 0;
644 }
645 /**
646 * incremental: incremental update
647 *
648 * @param[in] dbpath dbpath directory
649 * @param[in] root root directory of source tree
650 * @return 0: not updated, 1: updated
651 */
652 int
653 incremental(const char *dbpath, const char *root)
654 {
655 STATISTICS_TIME *tim;
656 struct stat statp;
657 time_t gtags_mtime;
658 STRBUF *addlist = strbuf_open(0);
659 STRBUF *deletelist = strbuf_open(0);
660 STRBUF *addlist_other = strbuf_open(0);
661 IDSET *deleteset, *findset;
662 int updated = 0;
663 const char *path;
664 unsigned int id, limit;
665
666 tim = statistics_time_start("Time of inspecting %s and %s.", dbname(GTAGS), dbname(GRTAGS));
667 if (vflag) {
668 fprintf(stderr, " Tag found in '%s'.\n", dbpath);
669 fprintf(stderr, " Incremental updating.\n");
670 }
671 /*
672 * get modified time of GTAGS.
673 */
674 path = makepath(dbpath, dbname(GTAGS), NULL);
675 if (stat(path, &statp) < 0)
676 die("stat failed '%s'.", path);
677 gtags_mtime = statp.st_mtime;
678
679 if (gpath_open(dbpath, 2) < 0)
680 die("GPATH not found.");
681 /*
682 * deleteset:
683 * The list of the path name which should be deleted from GPATH.
684 * findset:
685 * The list of the path name which exists in the current project.
686 * A project is limited by the --file option.
687 */
688 deleteset = idset_open(gpath_nextkey());
689 findset = idset_open(gpath_nextkey());
690 total = 0;
691 /*
692 * Make add list and delete list for update.
693 */
694 if (single_update) {
695 int type;
696 const char *fid;
697
698 if (skipthisfile(single_update))
699 goto exit;
700 if (test("b", single_update))
701 goto exit;
702 fid = gpath_path2fid(single_update, &type);
703 if (fid == NULL) {
704 /* new file */
705 if (!test("f", single_update))
706 die("'%s' not found.", single_update);
707 type = issourcefile(single_update) ? GPATH_SOURCE : GPATH_OTHER;
708 if (type == GPATH_OTHER)
709 strbuf_puts0(addlist_other, single_update);
710 else {
711 strbuf_puts0(addlist, single_update);
712 total++;
713 }
714 } else if (!test("f", single_update)) {
715 /* delete */
716 if (type != GPATH_OTHER) {
717 idset_add(deleteset, atoi(fid));
718 total++;
719 }
720 strbuf_puts0(deletelist, single_update);
721 } else {
722 /* update */
723 if (type == GPATH_OTHER)
724 goto exit;
725 idset_add(deleteset, atoi(fid));
726 strbuf_puts0(addlist, single_update);
727 total++;
728 }
729 } else {
730 if (file_list)
731 find_open_filelist(file_list, root, explain);
732 else
733 find_open(NULL, explain);
734 while ((path = find_read()) != NULL) {
735 const char *fid;
736 int n_fid = 0;
737 int other = 0;
738
739 /* a blank at the head of path means 'NOT SOURCE'. */
740 if (*path == ' ') {
741 if (test("b", ++path))
742 continue;
743 other = 1;
744 }
745 if (stat(path, &statp) < 0)
746 die("stat failed '%s'.", path);
747 fid = gpath_path2fid(path, NULL);
748 if (fid) {
749 n_fid = atoi(fid);
750 idset_add(findset, n_fid);
751 }
752 if (other) {
753 if (fid == NULL)
754 strbuf_puts0(addlist_other, path);
755 } else {
756 if (fid == NULL) {
757 strbuf_puts0(addlist, path);
758 total++;
759 } else if (gtags_mtime < statp.st_mtime) {
760 strbuf_puts0(addlist, path);
761 total++;
762 idset_add(deleteset, n_fid);
763 }
764 }
765 }
766 find_close();
767 /*
768 * make delete list.
769 */
770 limit = gpath_nextkey();
771 for (id = 1; id < limit; id++) {
772 char fid[MAXFIDLEN];
773 int type;
774
775 snprintf(fid, sizeof(fid), "%d", id);
776 /*
777 * This is a hole of GPATH. The hole increases if the deletion
778 * and the addition are repeated.
779 */
780 if ((path = gpath_fid2path(fid, &type)) == NULL)
781 continue;
782 /*
783 * The file which does not exist in the findset is treated
784 * assuming that it does not exist in the file system.
785 */
786 if (type == GPATH_OTHER) {
787 if (!idset_contains(findset, id) || !test("f", path) || test("b", path))
788 strbuf_puts0(deletelist, path);
789 } else {
790 if (!idset_contains(findset, id) || !test("f", path)) {
791 strbuf_puts0(deletelist, path);
792 idset_add(deleteset, id);
793 }
794 }
795 }
796 }
797 statistics_time_end(tim);
798 /*
799 * execute updating.
800 */
801 if ((!idset_empty(deleteset) || strbuf_getlen(addlist) > 0) ||
802 (strbuf_getlen(deletelist) + strbuf_getlen(addlist_other) > 0))
803 {
804 int db;
805 updated = 1;
806 tim = statistics_time_start("Time of updating %s and %s.", dbname(GTAGS), dbname(GRTAGS));
807 if (!idset_empty(deleteset) || strbuf_getlen(addlist) > 0)
808 updatetags(dbpath, root, deleteset, addlist);
809 if (strbuf_getlen(deletelist) + strbuf_getlen(addlist_other) > 0) {
810 const char *start, *end, *p;
811
812 if (vflag)
813 fprintf(stderr, "[%s] Updating '%s'.\n", now(), dbname(GPATH));
814 /* gpath_open(dbpath, 2); */
815 if (strbuf_getlen(deletelist) > 0) {
816 start = strbuf_value(deletelist);
817 end = start + strbuf_getlen(deletelist);
818
819 for (p = start; p < end; p += strlen(p) + 1)
820 gpath_delete(p);
821 }
822 if (strbuf_getlen(addlist_other) > 0) {
823 start = strbuf_value(addlist_other);
824 end = start + strbuf_getlen(addlist_other);
825
826 for (p = start; p < end; p += strlen(p) + 1) {
827 gpath_put(p, GPATH_OTHER);
828 }
829 }
830 /* gpath_close(); */
831 }
832 /*
833 * Update modification time of tag files
834 * because they may have no definitions.
835 */
836 for (db = GTAGS; db < GTAGLIM; db++)
837 utime(makepath(dbpath, dbname(db), NULL), NULL);
838 statistics_time_end(tim);
839 }
840 exit:
841 if (vflag) {
842 if (updated)
843 fprintf(stderr, " Global databases have been modified.\n");
844 else
845 fprintf(stderr, " Global databases are up to date.\n");
846 fprintf(stderr, "[%s] Done.\n", now());
847 }
848 strbuf_close(addlist);
849 strbuf_close(deletelist);
850 strbuf_close(addlist_other);
851 gpath_close();
852 idset_close(deleteset);
853 idset_close(findset);
854
855 return updated;
856 }
857 /**
858 * static void put_syms(int type, const char *tag, int lno, const char *path, const char *line_image, void *arg)
859 *
860 * callback functions for built-in parser
861 */
862 struct put_func_data {
863 GTOP *gtop[GTAGLIM];
864 const char *fid;
865 };
866 static void
867 put_syms(int type, const char *tag, int lno, const char *path, const char *line_image, void *arg)
868 {
869 const struct put_func_data *data = arg;
870 GTOP *gtop;
871 const char *p;
872
873 /*
874 * sanity checks
875 * These checks are required, because there is no telling what kind of string
876 * comes as 'a symbol' from external plug-in parsers.
877 */
878 for (p = tag; *p; p++) {
879 if (isspace(*p)) {
880 if (wflag)
881 warning("symbol name includs a space character. (Ignored) [+%d %s]", lno, path);
882 return;
883 }
884 }
885 if (p == tag) {
886 if (wflag)
887 warning("symbol name is null. (Ignored) [+%d %s]", lno, path);
888 return;
889 }
890 if (p - tag >= IDENTLEN) {
891 if (wflag)
892 warning("symbol name is too long. (Ignored) [+%d %s]", lno, path);
893 return;
894 }
895 switch (type) {
896 case PARSER_DEF:
897 gtop = data->gtop[GTAGS];
898 break;
899 case PARSER_REF_SYM:
900 gtop = data->gtop[GRTAGS];
901 if (gtop == NULL)
902 return;
903 break;
904 default:
905 return;
906 }
907 gtags_put_using(gtop, tag, lno, data->fid, line_image);
908 }
909 /**
910 * updatetags: update tag file.
911 *
912 * @param[in] dbpath directory in which tag file exist
913 * @param[in] root root directory of source tree
914 * @param[in] deleteset bit array of fid of deleted or modified files
915 * @param[in] addlist '\0' separated list of added or modified files
916 */
917 void
918 updatetags(const char *dbpath, const char *root, IDSET *deleteset, STRBUF *addlist)
919 {
920 struct put_func_data data;
921 int seqno, flags;
922 const char *path, *start, *end;
923
924 if (vflag)
925 fprintf(stderr, "[%s] Updating '%s' and '%s'.\n", now(), dbname(GTAGS), dbname(GRTAGS));
926 /*
927 * Open tag files.
928 */
929 data.gtop[GTAGS] = gtags_open(dbpath, root, GTAGS, GTAGS_MODIFY, 0);
930 if (test("f", makepath(dbpath, dbname(GRTAGS), NULL))) {
931 data.gtop[GRTAGS] = gtags_open(dbpath, root, GRTAGS, GTAGS_MODIFY, 0);
932 } else {
933 /*
934 * If you set NULL to data.gtop[GRTAGS], parse_file() doesn't write to
935 * GRTAGS. See put_syms().
936 */
937 data.gtop[GRTAGS] = NULL;
938 }
939 /*
940 * Delete tags from GTAGS.
941 */
942 if (!idset_empty(deleteset)) {
943 if (vflag) {
944 char fid[MAXFIDLEN];
945 int total = idset_count(deleteset);
946 unsigned int id;
947
948 seqno = 1;
949 for (id = idset_first(deleteset); id != END_OF_ID; id = idset_next(deleteset)) {
950 snprintf(fid, sizeof(fid), "%d", id);
951 path = gpath_fid2path(fid, NULL);
952 if (path == NULL)
953 die("GPATH is corrupted.");
954 fprintf(stderr, " [%d/%d] deleting tags of %s\n", seqno++, total, path + 2);
955 }
956 }
957 gtags_delete(data.gtop[GTAGS], deleteset);
958 if (data.gtop[GRTAGS] != NULL)
959 gtags_delete(data.gtop[GRTAGS], deleteset);
960 }
961 /*
962 * Set flags.
963 */
964 data.gtop[GTAGS]->flags = 0;
965 if (extractmethod)
966 data.gtop[GTAGS]->flags |= GTAGS_EXTRACTMETHOD;
967 data.gtop[GRTAGS]->flags = data.gtop[GTAGS]->flags;
968 flags = 0;
969 if (vflag)
970 flags |= PARSER_VERBOSE;
971 if (debug)
972 flags |= PARSER_DEBUG;
973 if (wflag)
974 flags |= PARSER_WARNING;
975 if (explain)
976 flags |= PARSER_EXPLAIN;
977 if (getenv("GTAGSFORCEENDBLOCK"))
978 flags |= PARSER_END_BLOCK;
979 /*
980 * Add tags to GTAGS and GRTAGS.
981 */
982 start = strbuf_value(addlist);
983 end = start + strbuf_getlen(addlist);
984 seqno = 0;
985 for (path = start; path < end; path += strlen(path) + 1) {
986 gpath_put(path, GPATH_SOURCE);
987 data.fid = gpath_path2fid(path, NULL);
988 if (data.fid == NULL)
989 die("GPATH is corrupted.('%s' not found)", path);
990 if (vflag)
991 fprintf(stderr, " [%d/%d] extracting tags of %s\n", ++seqno, total, path + 2);
992 parse_file(path, flags, put_syms, &data);
993 gtags_flush(data.gtop[GTAGS], data.fid);
994 if (data.gtop[GRTAGS] != NULL)
995 gtags_flush(data.gtop[GRTAGS], data.fid);
996 }
997 parser_exit();
998 gtags_close(data.gtop[GTAGS]);
999 if (data.gtop[GRTAGS] != NULL)
1000 gtags_close(data.gtop[GRTAGS]);
1001 }
1002 /**
1003 * createtags: create tags file
1004 *
1005 * @param[in] dbpath dbpath directory
1006 * @param[in] root root directory of source tree
1007 */
1008 void
1009 createtags(const char *dbpath, const char *root)
1010 {
1011 STATISTICS_TIME *tim;
1012 STRBUF *sb = strbuf_open(0);
1013 struct put_func_data data;
1014 int openflags, flags, seqno;
1015 const char *path;
1016
1017 tim = statistics_time_start("Time of creating %s and %s.", dbname(GTAGS), dbname(GRTAGS));
1018 if (vflag)
1019 fprintf(stderr, "[%s] Creating '%s' and '%s'.\n", now(), dbname(GTAGS), dbname(GRTAGS));
1020 openflags = cflag ? GTAGS_COMPACT : 0;
1021 #ifdef USE_SQLITE3
1022 if (use_sqlite3)
1023 openflags |= GTAGS_SQLITE3;
1024 #endif
1025 data.gtop[GTAGS] = gtags_open(dbpath, root, GTAGS, GTAGS_CREATE, openflags);
1026 data.gtop[GTAGS]->flags = 0;
1027 if (extractmethod)
1028 data.gtop[GTAGS]->flags |= GTAGS_EXTRACTMETHOD;
1029 data.gtop[GRTAGS] = gtags_open(dbpath, root, GRTAGS, GTAGS_CREATE, openflags);
1030 data.gtop[GRTAGS]->flags = data.gtop[GTAGS]->flags;
1031 flags = 0;
1032 if (vflag)
1033 flags |= PARSER_VERBOSE;
1034 if (debug)
1035 flags |= PARSER_DEBUG;
1036 if (wflag)
1037 flags |= PARSER_WARNING;
1038 if (explain)
1039 flags |= PARSER_EXPLAIN;
1040 if (getenv("GTAGSFORCEENDBLOCK"))
1041 flags |= PARSER_END_BLOCK;
1042 /*
1043 * Add tags to GTAGS and GRTAGS.
1044 */
1045 if (file_list)
1046 find_open_filelist(file_list, root, explain);
1047 else
1048 find_open(NULL, explain);
1049 seqno = 0;
1050 while ((path = find_read()) != NULL) {
1051 if (*path == ' ') {
1052 path++;
1053 if (!test("b", path))
1054 gpath_put(path, GPATH_OTHER);
1055 continue;
1056 }
1057 gpath_put(path, GPATH_SOURCE);
1058 data.fid = gpath_path2fid(path, NULL);
1059 if (data.fid == NULL)
1060 die("GPATH is corrupted.('%s' not found)", path);
1061 seqno++;
1062 if (vflag)
1063 fprintf(stderr, " [%d] extracting tags of %s\n", seqno, path + 2);
1064 parse_file(path, flags, put_syms, &data);
1065 gtags_flush(data.gtop[GTAGS], data.fid);
1066 gtags_flush(data.gtop[GRTAGS], data.fid);
1067 }
1068 total = seqno;
1069 parser_exit();
1070 find_close();
1071 statistics_time_end(tim);
1072 tim = statistics_time_start("Time of flushing B-tree cache");
1073 gtags_close(data.gtop[GTAGS]);
1074 gtags_close(data.gtop[GRTAGS]);
1075 statistics_time_end(tim);
1076 strbuf_reset(sb);
1077 if (getconfs("GTAGS_extra", sb)) {
1078 tim = statistics_time_start("Time of executing GTAGS_extra command");
1079 if (system(strbuf_value(sb)))
1080 fprintf(stderr, "GTAGS_extra command failed: %s\n", strbuf_value(sb));
1081 statistics_time_end(tim);
1082 }
1083 strbuf_reset(sb);
1084 if (getconfs("GRTAGS_extra", sb)) {
1085 tim = statistics_time_start("Time of executing GRTAGS_extra command");
1086 if (system(strbuf_value(sb)))
1087 fprintf(stderr, "GRTAGS_extra command failed: %s\n", strbuf_value(sb));
1088 statistics_time_end(tim);
1089 }
1090 strbuf_close(sb);
1091 }