"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 }