root/libutil/gtagsop.c

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

DEFINITIONS

This source file includes following definitions.
  1. compare_path
  2. compare_lineno
  3. compare_tags
  4. seekto
  5. is_defined_in_GTAGS
  6. dbname
  7. gtags_open
  8. gtags_put_using
  9. gtags_flush
  10. gtags_delete
  11. gtags_first
  12. gtags_next
  13. gtags_close
  14. flush_pool
  15. segment_read

   1 /*
   2  * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2005, 2006,
   3  *      2007, 2009, 2010 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 <assert.h>
  25 #include <ctype.h>
  26 #include <stdio.h>
  27 #ifdef STDC_HEADERS
  28 #include <stdlib.h>
  29 #endif
  30 #ifdef HAVE_STRING_H
  31 #include <string.h>
  32 #else
  33 #include <strings.h>
  34 #endif
  35 #ifdef HAVE_UNISTD_H
  36 #include <unistd.h>
  37 #endif
  38 
  39 #include "char.h"
  40 #include "checkalloc.h"
  41 #include "conf.h"
  42 #include "compress.h"
  43 #include "dbop.h"
  44 #include "die.h"
  45 #include "format.h"
  46 #include "gparam.h"
  47 #include "gtagsop.h"
  48 #include "locatestring.h"
  49 #include "makepath.h"
  50 #include "path.h"
  51 #include "gpathop.h"
  52 #include "split.h"
  53 #include "strbuf.h"
  54 #include "strhash.h"
  55 #include "strlimcpy.h"
  56 #include "strmake.h"
  57 #include "varray.h"
  58 
  59 #define HASHBUCKETS     2048
  60 
  61 static int compare_path(const void *, const void *);
  62 static int compare_lineno(const void *, const void *);
  63 static int compare_tags(const void *, const void *);
  64 static const char *seekto(const char *, int);
  65 static int is_defined_in_GTAGS(GTOP *, const char *);
  66 static void flush_pool(GTOP *, const char *);
  67 static void segment_read(GTOP *);
  68 
  69 /**
  70  * compare_path: compare function for sorting path names.
  71  */
  72 static int
  73 compare_path(const void *s1, const void *s2)
  74 {
  75         return strcmp(*(char **)s1, *(char **)s2);
  76 }
  77 /**
  78  * compare_lineno: compare function for sorting line number.
  79  */
  80 static int
  81 compare_lineno(const void *s1, const void *s2)
  82 {
  83         return *(const int *)s1 - *(const int *)s2;
  84 }
  85 /**
  86  * compare_tags: compare function for sorting tags.
  87  */
  88 static int
  89 compare_tags(const void *v1, const void *v2)
  90 {
  91         const GTP *e1 = v1, *e2 = v2;
  92         int ret;
  93 
  94         if ((ret = strcmp(e1->path, e2->path)) != 0)
  95                 return ret;
  96         return e1->lineno - e2->lineno;
  97 }
  98 /**
  99  * @fn static const char *seekto(const char *string, int n)
 100  * seekto: seek to the specified item of tag record.
 101  *
 102  * @par Usage:
 103  * @code
 104  *           0         1          2
 105  * tagline = <file id> <tag name> <line number>
 106  *
 107  * <file id>     = seekto(tagline, SEEKTO_FILEID);
 108  * <tag name>    = seekto(tagline, SEEKTO_TAGNAME);
 109  * <line number> = seekto(tagline, SEEKTO_LINENO);
 110  * @endcode
 111  */
 112 #define SEEKTO_FILEID   0
 113 #define SEEKTO_TAGNAME  1
 114 #define SEEKTO_LINENO   2
 115 
 116 static const char *
 117 seekto(const char *string, int n)
 118 {
 119         const char *p = string;
 120         while (n--) {
 121                 p = strchr(p, ' ');
 122                 if (p == NULL)
 123                         return NULL;
 124                 p++;
 125         }
 126         return p;
 127 }
 128 /**
 129  * Tag format
 130  *
 131  * [@EMPH{Specification of format version 6}]
 132  * 
 133  * @par Standard format:
 134  *
 135  *      This format is the default format of #GTAGS.
 136  * 
 137  * @par
 138  * @code{.txt}
 139  *         <file id> <tag name> <line number> <line image>
 140  * @endcode
 141  * 
 142  * @par
 143  *                 * Separator is single blank.
 144  * 
 145  * @par
 146  * @code{.txt}
 147  *         [example]
 148  *         +------------------------------------
 149  *         |110 func 10 int func(int a)
 150  *         |110 func 30 func(int a1, int a2)
 151  * @endcode
 152  * 
 153  * @par
 154  *         Line image might be compressed (#GTAGS_COMPRESS). <br>
 155  *         Tag name might be compressed (#GTAGS_COMPNAME).
 156  *
 157  * @par Compact format:
 158  * 
 159  * @par
 160  *      This format is the default format of #GRTAGS. <br>
 161  *      It is used for #GTAGS with the @OPTION{-c} option.
 162  *
 163  * @par
 164  * @code{.txt}
 165  *         <file id> <tag name> <line number>,...
 166  * @endcode
 167  * 
 168  * @par
 169  *                 * Separator is single blank.
 170  * 
 171  * @par
 172  * @code{.txt}
 173  *         [example]
 174  *         +------------------------------------
 175  *         |110 func 10,30
 176  * @endcode
 177  * 
 178  * @par
 179  *         Line numbers are sorted in a line. <br>
 180  *         Each line number might be expressed as difference from the previous
 181  *         line number except for the head (#GTAGS_COMPLINE). <br>
 182  *           ex: 10,3,2 means '10 13 15'. <br>
 183  *         In addition,successive line numbers are expressed as a range. <br>
 184  *           ex: 10-3 means '10 11 12 13'.
 185  *
 186  * @par [Description]
 187  * 
 188  * - Standard format is applied to #GTAGS, and compact format is applied
 189  *     to #GRTAGS by default.
 190  * - #GSYMS is not used any longer. It is virtually included by GRTAGS.
 191  * - Above two formats are same to the first line number. So, we can use
 192  *     common function to sort them.
 193  * - Separator is single blank.
 194  *     This decrease disk space used a little, and make it easy to parse
 195  *     tag record.
 196  * - Use file id instead of path name.
 197  *     This allows blanks in path name at least in tag files.
 198  * - Put file id at the head of tag record.
 199  *     We can access file id without string processing.
 200  *     This is advantageous for deleting tag record when incremental updating.
 201  * 
 202  * @par [Concept of format version]
 203  *
 204  * Since @NAME{GLOBAL}'s tag files are machine independent, they can be distributed
 205  * apart from @NAME{GLOBAL} itself. For example, if some network file system available,
 206  * client may execute global using server's tag files. In this case, both
 207  * @NAME{GLOBAL} are not necessarily the same version. So, we should assume that
 208  * older version of @NAME{GLOBAL} might access the tag files which generated
 209  * by new @NAME{GLOBAL}. To deal in such case, we decided to buried a version number
 210  * to both @XREF{global,1} and tag files. The conclete procedure is like follows:
 211  *
 212  * @par
 213  * -# @XREF{gtags,1} bury the version number in tag files. <br>
 214  * -# @XREF{global,1} pick up the version number from a tag file. If the number
 215  *      is larger than its acceptable version number then global give up work
 216  *      any more and display error message. <br>
 217  * -# If version number is not found then it assumes version 1.
 218  *
 219  * @par [History of format version]
 220  *
 221  @verbatim
 222   GLOBAL-1.0 - 1.8     no idea about format version.
 223   GLOBAL-1.9 - 2.24    understand format version.
 224                        support format version 1 (default).
 225                        if (format > 1) then print error message.
 226   GLOBAL-3.0 - 4.5     support format version 1 and 2.
 227                        if (format > 2) then print error message.
 228   GLOBAL-4.5.1 - 4.8.7 support format version 1, 2 and 3.
 229                        if (format > 3) then print error message.
 230   GLOBAL-5.0 -  5.3     support format version only 4.
 231                        if (format !=  4) then print error message.
 232   GLOBAL-5.4 - 5.8.2    support format version 4 and 5
 233                        if (format > 5 || format < 4) then print error message.
 234   GLOBAL-5.9 -          support only format version 6
 235                        if (format > 6 || format < 6) then print error message.
 236  @endverbatim
 237  *
 238  * In @NAME{GLOBAL-5.0}, we threw away the compatibility with the past formats.
 239  * Though we could continue the support for older formats, it seemed
 240  * not to be worthy. Because keeping maintaining the all formats hinders
 241  * new optimization and the function addition in the future.
 242  * Instead, the following error messages are displayed in a wrong usage.
 243  * @code{.sh}
 244  *       [older global and new tag file]
 245  *       $ global -x main
 246  *       GTAGS seems new format. Please install the latest GLOBAL.
 247  *       [new global and older tag file]
 248  *       $ global -x main
 249  *       GTAGS seems older format. Please remake tag files.
 250  * @endcode
 251  */
 252 static int new_format_version = 6;      /**< new format version */
 253 static int upper_bound_version = 6;     /**< acceptable format version (upper bound) */
 254 static int lower_bound_version = 6;     /**< acceptable format version (lower bound) */
 255 static const char *const tagslist[] = {"GPATH", "GTAGS", "GRTAGS", "GSYMS"};
 256 /**
 257  * Virtual #GRTAGS, #GSYMS processing:
 258  *
 259  * We use a real @NAME{GRTAGS} as virtual @NAME{GRTAGS} and @NAME{GSYMS}.
 260  * In fact, @NAME{GSYMS} tag file doesn't exist.
 261  *
 262  * @code{.txt}
 263  * Real tag file        virtual tag file
 264  * --------------------------------------
 265  * GTAGS =============> GTAGS
 266  *
 267  * GRTAGS ============> GRTAGS + GSYMS
 268  *            +=======> GRTAGS  tags which is defined in GTAGS
 269  *            +=======> GSYMS   tags which is not defined in GTAGS
 270  * @endcode
 271  */
 272 #define VIRTUAL_GRTAGS_GSYMS_PROCESSING(gtop)                                           \
 273         if (gtop->db == GRTAGS || gtop->db == GSYMS) {                                  \
 274                 int defined = is_defined_in_GTAGS(gtop, gtop->dbop->lastkey);           \
 275                 if ((gtop->db == GRTAGS && !defined) || (gtop->db == GSYMS && defined)) \
 276                         continue;                                                       \
 277         }
 278 /**
 279  * is_defined_in_GTAGS: whether or not the name is defined in #GTAGS.
 280  *
 281  *      @param[in]      gtop
 282  *      @param[in]      name    tag name
 283  *      @return         0: not defined, 1: defined
 284  *
 285  * @note It is assumed that the input stream is sorted by the tag name.
 286  */
 287 static int
 288 is_defined_in_GTAGS(GTOP *gtop, const char *name)
 289 {
 290         static char prev_name[MAXTOKEN+1];
 291         static int prev_result;
 292 
 293         if (!strcmp(name, prev_name))
 294                 return prev_result;
 295         strlimcpy(prev_name, name, sizeof(prev_name));
 296         return prev_result = dbop_get(gtop->gtags, prev_name) ? 1 : 0;
 297 }
 298 /**
 299  * dbname: return db name
 300  *
 301  *      @param[in]      db      0: #GPATH, 1: #GTAGS, 2: #GRTAGS, 3: #GSYMS
 302  *      @return         dbname
 303  */
 304 const char *
 305 dbname(int db)
 306 {
 307         if (db == GRTAGS + GSYMS)
 308                 db = GRTAGS;
 309         assert(db >= 0 && db < GTAGLIM);
 310         return tagslist[db];
 311 }
 312 /**
 313  * gtags_open: open global tag.
 314  *
 315  *      @param[in]      dbpath  dbpath directory
 316  *      @param[in]      root    root directory (needed when compact format)
 317  *      @param[in]      db      #GTAGS, #GRTAGS, #GSYMS
 318  *      @param[in]      mode    #GTAGS_READ: read only <br>
 319  *                      #GTAGS_CREATE: create tag <br>
 320  *                      #GTAGS_MODIFY: modify tag
 321  *      @param[in]      flags   #GTAGS_COMPACT: compact format
 322  *      @return         #GTOP structure
 323  *
 324  * @note when error occurred, @NAME{gtags_open()} doesn't return.
 325  */
 326 GTOP *
 327 gtags_open(const char *dbpath, const char *root, int db, int mode, int flags)
 328 {
 329         GTOP *gtop;
 330         char tagfile[MAXPATHLEN];
 331         int dbmode;
 332 
 333         gtop = (GTOP *)check_calloc(sizeof(GTOP), 1);
 334         gtop->db = db;
 335         gtop->mode = mode;
 336         gtop->openflags = flags;
 337         /*
 338          * Open tag file allowing duplicate records.
 339          */
 340         switch (gtop->mode) {
 341         case GTAGS_READ:
 342                 dbmode = 0;
 343                 break;
 344         case GTAGS_CREATE:
 345                 dbmode = 1;
 346                 break;
 347         case GTAGS_MODIFY:
 348                 dbmode = 2;
 349                 break;
 350         default:
 351                 assert(0);
 352         }
 353         /*
 354          * GRTAGS and GSYMS are virtual tag file. They are included in a real GRTAGS file.
 355          * In fact, GSYMS doesn't exist now.
 356          *
 357          * GRTAGS:      tags which belongs to GRTAGS, and are defined in GTAGS.
 358          * GSYMS:       tags which belongs to GRTAGS, and is not defined in GTAGS.
 359          */
 360         strlimcpy(tagfile, makepath(dbpath, dbname(db == GSYMS ? GRTAGS : db), NULL), sizeof(tagfile));
 361         gtop->dbop = dbop_open(tagfile, dbmode, 0644, DBOP_DUP|DBOP_SORTED_WRITE);
 362         if (gtop->dbop == NULL) {
 363                 if (dbmode == 1)
 364                         die("cannot make %s.", dbname(db));
 365                 die("%s not found.", dbname(db));
 366         }
 367         if (gtop->mode == GTAGS_READ && db != GTAGS) {
 368                 const char *gtags = makepath(dbpath, dbname(GTAGS), NULL);
 369                 int format_version;
 370 
 371                 gtop->gtags = dbop_open(gtags, 0, 0, 0);
 372                 if (gtop->gtags == NULL)
 373                         die("GTAGS not found.");
 374                 format_version = dbop_getversion(gtop->dbop);
 375                 if (format_version > upper_bound_version)
 376                         die("%s seems new format. Please install the latest GLOBAL.", gtags);
 377                 else if (format_version < lower_bound_version)
 378                         die("%s seems older format. Please remake tag files.", gtags);
 379         }
 380         if (gtop->mode == GTAGS_CREATE) {
 381                 /*
 382                  * Decide format.
 383                  */
 384                 gtop->format = 0;
 385                 gtop->format_version = new_format_version;
 386                 /*
 387                  * GRTAGS and GSYSM always use compact format.
 388                  * GTAGS uses compact format only when the -c option specified.
 389                  */
 390                 if (gtop->db == GRTAGS || gtop->db == GSYMS || gtop->openflags & GTAGS_COMPACT) {
 391                         gtop->format |= GTAGS_COMPACT;
 392                         gtop->format |= GTAGS_COMPLINE;
 393                 } else {
 394                         /* standard format */
 395                         gtop->format |= GTAGS_COMPRESS;
 396                 }
 397                 gtop->format |= GTAGS_COMPNAME;
 398                 if (gtop->format & GTAGS_COMPACT)
 399                         dbop_putoption(gtop->dbop, COMPACTKEY, NULL);
 400                 if (gtop->format & GTAGS_COMPRESS) {
 401                         dbop_putoption(gtop->dbop, COMPRESSKEY, DEFAULT_ABBREVIATION);
 402                         abbrev_open(DEFAULT_ABBREVIATION);
 403                 }
 404                 if (gtop->format & GTAGS_COMPLINE)
 405                         dbop_putoption(gtop->dbop, COMPLINEKEY, NULL);
 406                 if (gtop->format & GTAGS_COMPNAME)
 407                         dbop_putoption(gtop->dbop, COMPNAMEKEY, NULL);
 408                 dbop_putversion(gtop->dbop, gtop->format_version); 
 409         } else {
 410                 /*
 411                  * recognize format version of GTAGS. 'format version record'
 412                  * is saved as a META record in GTAGS and GRTAGS.
 413                  * if 'format version record' is not found, it's assumed
 414                  * version 1.
 415                  */
 416                 const char *p;
 417                 /*
 418                  * check format version.
 419                  */
 420                 gtop->format_version = dbop_getversion(gtop->dbop);
 421                 if (gtop->format_version > upper_bound_version)
 422                         die("%s seems new format. Please install the latest GLOBAL.", tagfile);
 423                 else if (gtop->format_version < lower_bound_version)
 424                         die("%s seems older format. Please remake tag files.", tagfile);
 425                 gtop->format = 0;
 426                 if (dbop_getoption(gtop->dbop, COMPACTKEY) != NULL)
 427                         gtop->format |= GTAGS_COMPACT;
 428                 if ((p = dbop_getoption(gtop->dbop, COMPRESSKEY)) != NULL) {
 429                         abbrev_open(p);
 430                         gtop->format |= GTAGS_COMPRESS;
 431                 }
 432                 if (dbop_getoption(gtop->dbop, COMPLINEKEY) != NULL)
 433                         gtop->format |= GTAGS_COMPLINE;
 434                 if (dbop_getoption(gtop->dbop, COMPNAMEKEY) != NULL)
 435                         gtop->format |= GTAGS_COMPNAME;
 436         }
 437         if (gpath_open(dbpath, dbmode) < 0) {
 438                 if (dbmode == 1)
 439                         die("cannot create GPATH.");
 440                 else
 441                         die("GPATH not found.");
 442         }
 443         if (gtop->mode != GTAGS_READ)
 444                 gtop->sb = strbuf_open(0);      /* This buffer is used for working area. */
 445         /*
 446          * Stuff for compact format.
 447          */
 448         if (gtop->format & GTAGS_COMPACT) {
 449                 assert(root != NULL);
 450                 strlimcpy(gtop->root, root, sizeof(gtop->root));
 451                 if (gtop->mode != GTAGS_READ)
 452                         gtop->path_hash = strhash_open(HASHBUCKETS);
 453         }
 454         return gtop;
 455 }
 456 /**
 457  * gtags_put_using: put tag record with packing.
 458  *
 459  *      @param[in]      gtop    descripter of #GTOP
 460  *      @param[in]      tag     tag name
 461  *      @param[in]      lno     line number
 462  *      @param[in]      fid     file id
 463  *      @param[in]      img     line image
 464  */
 465 void
 466 gtags_put_using(GTOP *gtop, const char *tag, int lno, const char *fid, const char *img)
 467 {
 468         const char *key;
 469 
 470         if (gtop->format & GTAGS_COMPACT) {
 471                 struct sh_entry *entry;
 472 
 473                 /*
 474                  * Register each record into the pool.
 475                  *
 476                  * Pool image:
 477                  *
 478                  * tagname   lno
 479                  * ------------------------------
 480                  * "funcA"   | 1| 3| 7|23|11| 2|...
 481                  * "funcB"   |34| 2| 5|66| 3|...
 482                  * ...
 483                  */
 484                 entry = strhash_assign(gtop->path_hash, tag, 1);
 485                 if (entry->value == NULL)
 486                         entry->value = varray_open(sizeof(int), 100);
 487                 *(int *)varray_append((VARRAY *)entry->value) = lno;
 488                 return;
 489         }
 490         /*
 491          * extract method when class method definition.
 492          *
 493          * Ex: Class::method(...)
 494          *
 495          * key  = 'method'
 496          * data = 'Class::method  103 ./class.cpp ...'
 497          */
 498         if (gtop->flags & GTAGS_EXTRACTMETHOD) {
 499                 if ((key = locatestring(tag, ".", MATCH_LAST)) != NULL)
 500                         key++;
 501                 else if ((key = locatestring(tag, "::", MATCH_LAST)) != NULL)
 502                         key += 2;
 503                 else
 504                         key = tag;
 505         } else {
 506                 key = tag;
 507         }
 508         strbuf_reset(gtop->sb);
 509         strbuf_puts(gtop->sb, fid);
 510         strbuf_putc(gtop->sb, ' ');
 511         strbuf_puts(gtop->sb, (gtop->format & GTAGS_COMPNAME) ? compress(tag, key) : tag);
 512         strbuf_putc(gtop->sb, ' ');
 513         strbuf_putn(gtop->sb, lno);
 514         strbuf_putc(gtop->sb, ' ');
 515         strbuf_puts(gtop->sb, (gtop->format & GTAGS_COMPRESS) ? compress(img, key) : img);
 516         dbop_put(gtop->dbop, key, strbuf_value(gtop->sb));
 517 }
 518 /**
 519  * gtags_flush: Flush the pool for compact format.
 520  *
 521  *      @param[in]      gtop    descripter of #GTOP
 522  *      @param[in]      fid     file id
 523  */
 524 void
 525 gtags_flush(GTOP *gtop, const char *fid)
 526 {
 527         if (gtop->format & GTAGS_COMPACT) {
 528                 flush_pool(gtop, fid);
 529                 strhash_reset(gtop->path_hash);
 530         }
 531 }
 532 /**
 533  * gtags_delete: delete records belong to set of fid.
 534  *
 535  *      @param[in]      gtop    #GTOP structure
 536  *      @param[in]      deleteset bit array of fid
 537  */
 538 void
 539 gtags_delete(GTOP *gtop, IDSET *deleteset)
 540 {
 541         const char *tagline;
 542         int fid;
 543 
 544         for (tagline = dbop_first(gtop->dbop, NULL, NULL, 0); tagline; tagline = dbop_next(gtop->dbop)) {
 545                 /*
 546                  * Extract path from the tag line.
 547                  */
 548                 fid = atoi(tagline);
 549                 /*
 550                  * If the file id exists in the deleteset, delete the tagline.
 551                  */
 552                 if (idset_contains(deleteset, fid))
 553                         dbop_delete(gtop->dbop, NULL);
 554         }
 555 }
 556 /**
 557  * gtags_first: return first record
 558  *
 559  *      @param[in]      gtop    #GTOP structure
 560  *      @param[in]      pattern tag name <br>
 561  *              - may be regular expression
 562  *              - may be @VAR{NULL}
 563  *      @param[in]      flags   #GTOP_PREFIX:   prefix read <br>
 564  *                      #GTOP_KEY:      read key only <br>
 565  *                      #GTOP_PATH:     read path only <br>
 566  *                      #GTOP_NOREGEX:  don't use regular expression. <br>
 567  *                      #GTOP_IGNORECASE:       ignore case distinction. <br>
 568  *                      #GTOP_BASICREGEX:       use basic regular expression. <br>
 569  *                      #GTOP_NOSORT:   don't sort
 570  *      @return         record
 571  */
 572 GTP *
 573 gtags_first(GTOP *gtop, const char *pattern, int flags)
 574 {
 575         int dbflags = 0;
 576         int regflags = 0;
 577         char prefix[IDENTLEN];
 578         static regex_t reg;
 579         regex_t *preg = &reg;
 580         const char *key = NULL;
 581         const char *tagline;
 582 
 583         /* Settlement for last time if any */
 584         if (gtop->path_hash) {
 585                 strhash_close(gtop->path_hash);
 586                 gtop->path_hash = NULL;
 587         }
 588         if (gtop->path_array) {
 589                 free(gtop->path_array);
 590                 gtop->path_array = NULL;
 591         }
 592 
 593         gtop->flags = flags;
 594         if (flags & GTOP_PREFIX && pattern != NULL)
 595                 dbflags |= DBOP_PREFIX;
 596         if (flags & GTOP_KEY)
 597                 dbflags |= DBOP_KEY;
 598 
 599         if (!(flags & GTOP_BASICREGEX))
 600                 regflags |= REG_EXTENDED;
 601         if (flags & GTOP_IGNORECASE)
 602                 regflags |= REG_ICASE;
 603         /*
 604          * Get key and compiled regular expression for dbop_xxxx().
 605          */
 606         if (flags & GTOP_NOREGEX) {
 607                 key = pattern;
 608                 preg = NULL;
 609         } else if (pattern == NULL || !strcmp(pattern, ".*")) {
 610                 /*
 611                  * Since the regular expression '.*' matches to any record,
 612                  * we take sequential read method.
 613                  */
 614                 key = NULL;
 615                 preg = NULL;
 616         } else if (isregex(pattern) && regcomp(preg, pattern, regflags) == 0) {
 617                 const char *p;
 618                 /*
 619                  * If the pattern include '^' + some non regular expression
 620                  * characters like '^aaa[0-9]', we take prefix read method
 621                  * with the non regular expression part as the prefix.
 622                  */
 623                 if (!(flags & GTOP_IGNORECASE) && *pattern == '^' && *(p = pattern + 1) && !isregexchar(*p)) {
 624                         int i = 0;
 625 
 626                         while (*p && !isregexchar(*p) && i < IDENTLEN)
 627                                 prefix[i++] = *p++;
 628                         prefix[i] = '\0';
 629                         key = prefix;
 630                         dbflags |= DBOP_PREFIX;
 631                 } else {
 632                         key = NULL;
 633                 }
 634         } else {
 635                 key = pattern;
 636                 preg = NULL;
 637         }
 638         /*
 639          * If GTOP_PATH is set, at first, we collect all path names in a pool and
 640          * sort them. gtags_first() and gtags_next() returns one of the pool.
 641          */
 642         if (gtop->flags & GTOP_PATH) {
 643                 struct sh_entry *entry;
 644                 char *p;
 645                 const char *cp;
 646                 unsigned long i;
 647 
 648                 gtop->path_hash = strhash_open(HASHBUCKETS);
 649                 /*
 650                  * Pool path names.
 651                  *
 652                  * fid          path name
 653                  * +--------------------------
 654                  * |100         ./aaa/a.c
 655                  * |105         ./aaa/b.c
 656                  *  ...
 657                  */
 658                 for (tagline = dbop_first(gtop->dbop, key, preg, dbflags);
 659                      tagline != NULL;
 660                      tagline = dbop_next(gtop->dbop))
 661                 {
 662                         VIRTUAL_GRTAGS_GSYMS_PROCESSING(gtop);
 663                         /* extract file id */
 664                         p = locatestring(tagline, " ", MATCH_FIRST);
 665                         if (p == NULL)
 666                                 die("Illegal tag record. '%s'\n", tagline);
 667                         *p = '\0';
 668                         entry = strhash_assign(gtop->path_hash, tagline, 1);
 669                         /* new entry: get path name and set. */
 670                         if (entry->value == NULL) {
 671                                 cp = gpath_fid2path(tagline, NULL);
 672                                 if (cp == NULL)
 673                                         die("GPATH is corrupted.(file id '%s' not found)", tagline);
 674                                 entry->value = strhash_strdup(gtop->path_hash, cp, 0);
 675                         }
 676                 }
 677                 /*
 678                  * Sort path names.
 679                  *
 680                  * fid          path name       path_array (sort)
 681                  * +--------------------------  +---+
 682                  * |100         ./aaa/a.c <-------* |
 683                  * |105         ./aaa/b.c <-------* |
 684                  *  ...                         ...
 685                  */
 686                 gtop->path_array = (char **)check_malloc(gtop->path_hash->entries * sizeof(char *));
 687                 i = 0;
 688                 for (entry = strhash_first(gtop->path_hash); entry != NULL; entry = strhash_next(gtop->path_hash))
 689                         gtop->path_array[i++] = entry->value;
 690                 if (i != gtop->path_hash->entries)
 691                         die("Something is wrong. 'i = %lu, entries = %lu'" , i, gtop->path_hash->entries);
 692                 if (!(gtop->flags & GTOP_NOSORT))
 693                         qsort(gtop->path_array, gtop->path_hash->entries, sizeof(char *), compare_path);
 694                 gtop->path_count = gtop->path_hash->entries;
 695                 gtop->path_index = 0;
 696 
 697                 if (gtop->path_index >= gtop->path_count)
 698                         return NULL;
 699                 gtop->gtp.path = gtop->path_array[gtop->path_index++];
 700                 return &gtop->gtp;
 701         } else if (gtop->flags & GTOP_KEY) {
 702                 for (gtop->gtp.tag = dbop_first(gtop->dbop, key, preg, dbflags);
 703                      gtop->gtp.tag != NULL;
 704                      gtop->gtp.tag = dbop_next(gtop->dbop))
 705                 {
 706                         VIRTUAL_GRTAGS_GSYMS_PROCESSING(gtop);
 707                         break;
 708                 }
 709                 return gtop->gtp.tag ? &gtop->gtp : NULL;
 710         } else {
 711                 if (gtop->vb == NULL)
 712                         gtop->vb = varray_open(sizeof(GTP), 200);
 713                 else
 714                         varray_reset(gtop->vb);
 715                 if (gtop->segment_pool == NULL)
 716                         gtop->segment_pool = pool_open();
 717                 else
 718                         pool_reset(gtop->segment_pool);
 719                 if (gtop->path_hash == NULL)
 720                         gtop->path_hash = strhash_open(HASHBUCKETS);
 721                 else
 722                         strhash_reset(gtop->path_hash);
 723                 tagline = dbop_first(gtop->dbop, key, preg, dbflags);
 724                 if (tagline == NULL)
 725                         return NULL;
 726                 /*
 727                  * Dbop_next() wil read the same record again.
 728                  */
 729                 dbop_unread(gtop->dbop);
 730                 /*
 731                  * Read a tag segment with sorting.
 732                  */
 733                 segment_read(gtop);
 734                 return  &gtop->gtp_array[gtop->gtp_index++];
 735         }
 736 }
 737 /**
 738  * gtags_next: return next record.
 739  *
 740  *      @param[in]      gtop    #GTOP structure
 741  *      @return         record
 742  *                      @VAR{NULL} end of tag
 743  */
 744 GTP *
 745 gtags_next(GTOP *gtop)
 746 {
 747         if (gtop->flags & GTOP_PATH) {
 748                 if (gtop->path_index >= gtop->path_count)
 749                         return NULL;
 750                 gtop->gtp.path = gtop->path_array[gtop->path_index++];
 751                 return &gtop->gtp;
 752         } else if (gtop->flags & GTOP_KEY) {
 753                 for (gtop->gtp.tag = dbop_next(gtop->dbop);
 754                      gtop->gtp.tag != NULL;
 755                      gtop->gtp.tag = dbop_next(gtop->dbop))
 756                 {
 757                         VIRTUAL_GRTAGS_GSYMS_PROCESSING(gtop);
 758                         break;
 759                 }
 760                 return gtop->gtp.tag ? &gtop->gtp : NULL;
 761         } else {
 762                 /*
 763                  * End of segment.
 764                  * Reset resources and read new segment again.
 765                  */
 766                 if (gtop->gtp_index >= gtop->gtp_count) {
 767                         varray_reset(gtop->vb);
 768                         pool_reset(gtop->segment_pool);
 769                         /* strhash_reset(gtop->path_hash); */
 770                         segment_read(gtop);
 771                 }
 772                 if (gtop->gtp_index >= gtop->gtp_count)
 773                         return NULL;
 774                 return &gtop->gtp_array[gtop->gtp_index++];
 775         }
 776 }
 777 /**
 778  * gtags_close: close tag file
 779  *
 780  *      @param[in]      gtop    #GTOP structure
 781  */
 782 void
 783 gtags_close(GTOP *gtop)
 784 {
 785         if (gtop->format & GTAGS_COMPRESS)
 786                 abbrev_close();
 787         if (gtop->format & GTAGS_COMPACT && gtop->cur_path[0])
 788                 flush_pool(gtop, NULL);
 789         if (gtop->segment_pool)
 790                 pool_close(gtop->segment_pool);
 791         if (gtop->path_array)
 792                 free(gtop->path_array);
 793         if (gtop->sb)
 794                 strbuf_close(gtop->sb);
 795         if (gtop->vb)
 796                 varray_close(gtop->vb);
 797         if (gtop->path_hash)
 798                 strhash_close(gtop->path_hash);
 799         gpath_close();
 800         dbop_close(gtop->dbop);
 801         if (gtop->gtags)
 802                 dbop_close(gtop->gtags);
 803         free(gtop);
 804 }
 805 /**
 806  * flush_pool: flush the pool and write is as compact format.
 807  *
 808  *      @param[in]      gtop    descripter of #GTOP
 809  *      @param[in]      s_fid
 810  */
 811 static void
 812 flush_pool(GTOP *gtop, const char *s_fid)
 813 {
 814         struct sh_entry *entry;
 815         int header_offset;
 816         int i, last;
 817 
 818         if (s_fid == NULL && (s_fid = gpath_path2fid(gtop->cur_path, NULL)) == NULL)
 819                 die("GPATH is corrupted.('%s' not found)", gtop->cur_path);
 820         /*
 821          * Write records as compact format and free line number table
 822          * for each entry in the pool.
 823          */
 824         for (entry = strhash_first(gtop->path_hash); entry; entry = strhash_next(gtop->path_hash)) {
 825                 VARRAY *vb = (VARRAY *)entry->value;
 826                 int *lno_array = varray_assign(vb, 0, 0);
 827                 const char *key = entry->name;
 828 
 829                 /*
 830                  * extract method when class method definition.
 831                  *
 832                  * Ex: Class::method(...)
 833                  *
 834                  * key  = 'method'
 835                  * data = 'Class::method  103 ./class.cpp ...'
 836                  */
 837                 if (gtop->flags & GTAGS_EXTRACTMETHOD) {
 838                         if ((key = locatestring(entry->name, ".", MATCH_LAST)) != NULL)
 839                                 key++;
 840                         else if ((key = locatestring(entry->name, "::", MATCH_LAST)) != NULL)
 841                                 key += 2;
 842                         else
 843                                 key = entry->name;
 844                 }
 845                 /* Sort line number table */
 846                 qsort(lno_array, vb->length, sizeof(int), compare_lineno); 
 847 
 848                 strbuf_reset(gtop->sb);
 849                 strbuf_puts(gtop->sb, s_fid);
 850                 strbuf_putc(gtop->sb, ' ');
 851                 if (gtop->format & GTAGS_COMPNAME) {
 852                         strbuf_puts(gtop->sb, compress(entry->name, key));
 853                 } else {
 854                         strbuf_puts(gtop->sb, entry->name);
 855                 }
 856                 strbuf_putc(gtop->sb, ' ');
 857                 header_offset = strbuf_getlen(gtop->sb);
 858                 /*
 859                  * If GTAGS_COMPLINE flag is set, each line number is expressed as the
 860                  * difference from the previous line number except for the head.
 861                  * GTAGS_COMPLINE is set by default in format version 5.
 862                  */
 863                 if (gtop->format & GTAGS_COMPLINE) {
 864                         int cont = 0;
 865 
 866                         last = 0;                       /* line 0 doesn't exist */
 867                         for (i = 0; i < vb->length; i++) {
 868                                 int n = lno_array[i];
 869 
 870                                 if (n == last)
 871                                         continue;
 872                                 if (last > 0 && n == last + 1) {
 873                                         if (!cont) {
 874                                                 /*
 875                                                  * Don't use range expression at the head.
 876                                                  */
 877                                                 if (strbuf_getlen(gtop->sb) == header_offset)
 878                                                         strbuf_putn(gtop->sb, n);
 879                                                 else
 880                                                         cont = last;
 881                                         }
 882                                 } else {
 883                                         /*
 884                                          * Range expression. ex: 10-2 means 10 11 12
 885                                          */
 886                                         if (cont) {
 887                                                 strbuf_putc(gtop->sb, '-');
 888                                                 strbuf_putn(gtop->sb, last - cont);
 889                                                 cont = 0;
 890                                         }
 891                                         if (strbuf_getlen(gtop->sb) > header_offset) {
 892                                                 strbuf_putc(gtop->sb, ',');
 893                                                 strbuf_putn(gtop->sb, n - last);
 894                                         } else {
 895                                                 strbuf_putn(gtop->sb, n);
 896                                         }
 897                                         if (strbuf_getlen(gtop->sb) > DBOP_PAGESIZE / 4) {
 898                                                 dbop_put(gtop->dbop, key, strbuf_value(gtop->sb));
 899                                                 strbuf_setlen(gtop->sb, header_offset);
 900                                         }
 901                                 }
 902                                 last = n;
 903                         }
 904                         if (cont) {
 905                                 strbuf_putc(gtop->sb, '-');
 906                                 strbuf_putn(gtop->sb, last - cont);
 907                         }
 908                 } else {
 909                         /*
 910                          * This code is to support older format (version 4).
 911                          */
 912                         last = 0;                       /* line 0 doesn't exist */
 913                         for (i = 0; i < vb->length; i++) {
 914                                 int n = lno_array[i];
 915 
 916                                 if (n == last)
 917                                         continue;
 918                                 if (strbuf_getlen(gtop->sb) > header_offset)
 919                                         strbuf_putc(gtop->sb, ',');
 920                                 strbuf_putn(gtop->sb, n);
 921                                 if (strbuf_getlen(gtop->sb) > DBOP_PAGESIZE / 4) {
 922                                         dbop_put(gtop->dbop, key, strbuf_value(gtop->sb));
 923                                         strbuf_setlen(gtop->sb, header_offset);
 924                                 }
 925                                 last = n;
 926                         }
 927                 }
 928                 if (strbuf_getlen(gtop->sb) > header_offset) {
 929                         dbop_put(gtop->dbop, key, strbuf_value(gtop->sb));
 930                 }
 931                 /* Free line number table */
 932                 varray_close(vb);
 933         }
 934 }
 935 /**
 936  * Read a tag segment with sorting.
 937  *
 938  *      @param[in]      gtop    #GTOP structure <br>
 939  *              Output: @CODE{gtop->gtp_array}          segment table <br>
 940  *              Output: @CODE{gtop->gtp_count}          segment table size <br>
 941  *              Output: @CODE{gtop->gtp_index}          segment table index (initial value = 0) <br>
 942  *              Output: @CODE{gtop->cur_tagname}        current tag name
 943  *
 944  * A segment is a set of tag records which have same tag name. <br>
 945  * This function read a segment from tag file, sort it and put it on segment table. <br>
 946  * This function can treat both of standard format and compact format.
 947  *
 948  * Sorting is done by three keys.
 949  *      - 1st key: tag name
 950  *      - 2nd key: file name
 951  *      - 3rd key: line number
 952  *
 953  * Since all records in a segment have same tag name, you need not think about 1st key.
 954  */
 955 void
 956 segment_read(GTOP *gtop)
 957 {
 958         const char *tagline, *fid, *path, *lineno;
 959         GTP *gtp;
 960         struct sh_entry *sh;
 961 
 962         /*
 963          * Save tag lines.
 964          */
 965         gtop->cur_tagname[0] = '\0';
 966         while ((tagline = dbop_next(gtop->dbop)) != NULL) {
 967                 VIRTUAL_GRTAGS_GSYMS_PROCESSING(gtop);
 968                 /*
 969                  * get tag name and line number.
 970                  *
 971                  * tagline = <file id> <tag name> <line number>
 972                  */
 973                 if (gtop->cur_tagname[0] == '\0') {
 974                         strlimcpy(gtop->cur_tagname, gtop->dbop->lastkey, sizeof(gtop->cur_tagname));
 975                 } else if (strcmp(gtop->cur_tagname, gtop->dbop->lastkey) != 0) {
 976                         /*
 977                          * Dbop_next() wil read the same record again.
 978                          */
 979                         dbop_unread(gtop->dbop);
 980                         break;
 981                 }
 982                 gtp = varray_append(gtop->vb);
 983                 gtp->tagline = pool_strdup(gtop->segment_pool, tagline, 0);
 984                 gtp->tag = (const char *)gtop->cur_tagname;
 985                 /*
 986                  * convert fid into hashed path name to save memory.
 987                  */
 988                 fid = (const char *)strmake(tagline, " ");
 989                 path = gpath_fid2path(fid, NULL);
 990                 if (path == NULL)
 991                         die("gtags_first: path not found. (fid=%s)", fid);
 992                 sh = strhash_assign(gtop->path_hash, path, 1);
 993                 gtp->path = sh->name;
 994                 lineno = seekto(gtp->tagline, SEEKTO_LINENO);
 995                 if (lineno == NULL)
 996                         die("illegal tag record.\n%s", tagline);
 997                 gtp->lineno = atoi(lineno);
 998         }
 999         /*
1000          * Sort tag lines.
1001          */
1002         gtop->gtp_array = varray_assign(gtop->vb, 0, 0);
1003         gtop->gtp_count = gtop->vb->length;
1004         gtop->gtp_index = 0;
1005         if (!(gtop->flags & GTOP_NOSORT))
1006                 qsort(gtop->gtp_array, gtop->gtp_count, sizeof(GTP), compare_tags);
1007 }

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