root/global/global.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. usage
  2. help
  3. setcom
  4. decide_tag_by_context
  5. main
  6. completion_tags
  7. completion
  8. completion_idutils
  9. completion_path
  10. print_count
  11. idutils
  12. grep
  13. pathlist
  14. put_syms
  15. parsefile
  16. search
  17. tagsearch
  18. 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 }

/* [previous][next][first][last][top][bottom][index][help] */