bug-global
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC] Suggestion of new tag format


From: Shigio YAMAGUCHI
Subject: [RFC] Suggestion of new tag format
Date: Tue, 21 Feb 2006 10:13:29 +0900

Hi all,

I have a plan to make new tag record format.

This change allows path name including blanks, and decrease
disk space used. In this suggestion, any user visible change
is not intended.

I have already made the first version of it.
Since it takes time to assess the code, I would like to commit
it to the CVS repository for the test like the -cc option.
After that, you can make tag files using new format like follows:

        $ gtags -ccc                    <= three 'c' option

This code is written so that we can remove it easily.

What do you think?
Is there any problem?

--------------------------------------------------------------------
Suggestion of new tag format (I play, for GLOBAL-5.0)

Purpose
=======

o Allows path name including blanks.
o Decrease disk space used.
o Make tag format simpler.
o Realize stable performance.

Specification
=============

STANDARD5 format (for GTAGS)

        <file id> <tag name> <line number> <line image>

                * Separator is single blank.

        [example]
        +------------------------------------
        |110 func 10 int func(int a)
        |110 func 30 func(int a1, int a2)

COMPACT5 format (for GRTAGS, GSYMS)

        <file id> <tag name> <line number>,...

                * Separator is single blank.

        [example]
        +------------------------------------
        |110 func 10,30

Description
===========

o STANDARD5 format is applied to GTAGS, and COMPACT5 format is applied
  to GRTAGS and GSYMS by default.
  Because GRTAGS was growing too much, COMPACT5 format might be suitable.
  (The -c (--compact) option might be removed.)
o Separator is single blank.
  This decrease disk space used a little, and make it easy to parse
  tag record.
o Use file id instead of path name.
  This decreases disk space used, and allows blanks in path name
  at least in tag files.
o Put file id at the head of tag record.
  We can access file id without string processing.
  This is advantageous for deleting tag record when incremental updating.
o Format version of tag file is incremented (4).
  This enables us to keep upper compatibility of tag files.
  (Whether to keep it, a further examination is necessary though.)
  For the detail of 'format version', please see the comment in
  libutil/gtagsop.c.

Test data
=========

Here is a test data when applying to FreeBSD kernel source code.

        Format    Command line   Making time Updating time Total size
                                 (sec)       (sec)         (bytes)
        -------------------------------------------------------------
        standard    gtags        1137        6676           131391488
        compact     gtags -c      330          98            36569088
        pathindex   gtags -cc     948        4254           109953024
        new format  gtags -ccc    392         112            43040768
        -------------------------------------------------------------

Patch
=====

diff -rcN global/gtags/gtags.c global.5/gtags/gtags.c
*** global/gtags/gtags.c        Thu Feb  9 21:56:48 2006
--- global.5/gtags/gtags.c      Tue Feb 21 08:16:54 2006
***************
*** 814,827 ****
         * non: STANDARD format
         * -c:  COMPACT format + PATHINDEX option
         * -cc: STANDARD format + PATHINDEX option
!        * Ths -cc is undocumented.
!        * In the future, it may become the standard format of GLOBAL.
         */
        flags = 0;
        if (cflag) {
                flags |= GTAGS_PATHINDEX;
                if (cflag == 1)
                        flags |= GTAGS_COMPACT;
        }
        if (vflag > 1)
                fprintf(stderr, " using tag command '%s <path>'.\n", 
strbuf_value(comline));
--- 814,829 ----
         * non: STANDARD format
         * -c:  COMPACT format + PATHINDEX option
         * -cc: STANDARD format + PATHINDEX option
!        * -ccc: TEST format for version 5.0
!        * Ths -cc and -ccc is undocumented.
         */
        flags = 0;
        if (cflag) {
                flags |= GTAGS_PATHINDEX;
                if (cflag == 1)
                        flags |= GTAGS_COMPACT;
+               if (cflag >= 3)
+                       flags = GTAGS_FORMAT5;
        }
        if (vflag > 1)
                fprintf(stderr, " using tag command '%s <path>'.\n", 
strbuf_value(comline));
diff -rcN global/libutil/Makefile.am global.5/libutil/Makefile.am
*** global/libutil/Makefile.am  Mon Jan 23 16:31:48 2006
--- global.5/libutil/Makefile.am        Tue Feb 21 08:16:54 2006
***************
*** 17,30 ****
  gtagsop.h locatestring.h makepath.h path.h gpathop.h strbuf.h \
  strmake.h tab.h test.h token.h usable.h version.h is_unixy.h abs2rel.h \
  split.h strlimcpy.h linetable.h env.h char.h date.h langmap.h \
! varray.h idset.h strhash.h xargs.h tagsort.h format.h pathconvert.h
  
  libgloutil_a_SOURCES = \
  conf.c dbop.c defined.c die.c find.c getdbpath.c gtagsop.c locatestring.c \
  makepath.c path.c gpathop.c strbuf.c strmake.c tab.c test.c \
  token.c usable.c version.c is_unixy.c abs2rel.c split.c strlimcpy.c 
linetable.c \
  env.c char.c date.c langmap.c varray.c idset.c strhash.c xargs.c tagsort.c \
! pathconvert.c
  
  AM_CFLAGS = -DBINDIR='"$(bindir)"' -DDATADIR='"$(datadir)"'
  
--- 17,31 ----
  gtagsop.h locatestring.h makepath.h path.h gpathop.h strbuf.h \
  strmake.h tab.h test.h token.h usable.h version.h is_unixy.h abs2rel.h \
  split.h strlimcpy.h linetable.h env.h char.h date.h langmap.h \
! varray.h idset.h strhash.h xargs.h tagsort.h format.h pathconvert.h \
! gtagsop5.h
  
  libgloutil_a_SOURCES = \
  conf.c dbop.c defined.c die.c find.c getdbpath.c gtagsop.c locatestring.c \
  makepath.c path.c gpathop.c strbuf.c strmake.c tab.c test.c \
  token.c usable.c version.c is_unixy.c abs2rel.c split.c strlimcpy.c 
linetable.c \
  env.c char.c date.c langmap.c varray.c idset.c strhash.c xargs.c tagsort.c \
! pathconvert.c gtagsop5.c
  
  AM_CFLAGS = -DBINDIR='"$(bindir)"' -DDATADIR='"$(datadir)"'
  
diff -rcN global/libutil/format.h global.5/libutil/format.h
*** global/libutil/format.h     Thu Feb  9 21:43:55 2006
--- global.5/libutil/format.h   Tue Feb 21 08:16:54 2006
***************
*** 81,86 ****
--- 81,100 ----
   */
  #define PART_PATH_COMP  1
  #define PART_LNO_COMP   2
+ /*
+  * Standard5 format
+  * PART_FID5 PART_TAG5 PART_LNO5 PART_LINE5
+  * +----------------------------------------------------
+  * |100 main 227 main(int argc, argv **char)
+  *
+  * Compact5 format
+  * PART_FID5 PART_TAG5 PART_LNO5
+  * +----------------------------------------------------
+  * |100 main 227,230,245,260
+  */
+ #define PART_FID5 0
+ #define PART_TAG5 1
+ #define PART_LNO5 2
  
  /*
   * Path name type
diff -rcN global/libutil/gtagsop.c global.5/libutil/gtagsop.c
*** global/libutil/gtagsop.c    Thu Feb  9 07:49:26 2006
--- global.5/libutil/gtagsop.c  Tue Feb 21 08:16:55 2006
***************
*** 44,49 ****
--- 44,50 ----
  #include "format.h"
  #include "gparam.h"
  #include "gtagsop.h"
+ #include "gtagsop5.h"
  #include "locatestring.h"
  #include "makepath.h"
  #include "path.h"
***************
*** 257,262 ****
--- 258,265 ----
                        die("cannot make %s.", dbname(db));
                die("%s not found.", dbname(db));
        }
+       if (gtop->mode == GTAGS_CREATE && flags & GTAGS_FORMAT5)
+               return gtags_open5(gtop, dbpath, root);
        /*
         * decide format version.
         */
***************
*** 303,309 ****
                        gtop->format_version = atoi(p);
                }
                if (gtop->format_version > support_version)
!                       die("GTAGS seems new format. Please install the latest 
GLOBAL.");
                if (gtop->format_version > 1) {
                        if (dbop_get(gtop->dbop, COMPACTKEY) != NULL)
                                gtop->format |= GTAGS_COMPACT;
--- 306,312 ----
                        gtop->format_version = atoi(p);
                }
                if (gtop->format_version > support_version)
!                       return gtags_open5(gtop, dbpath, root);
                if (gtop->format_version > 1) {
                        if (dbop_get(gtop->dbop, COMPACTKEY) != NULL)
                                gtop->format |= GTAGS_COMPACT;
***************
*** 348,353 ****
--- 351,360 ----
  void
  gtags_put(GTOP *gtop, const char *tag, const char *ctags_x)   /* virtually 
const */
  {
+       if (gtop->format_version > support_version) {
+               gtags_put5(gtop, tag, ctags_x);
+               return;
+       }
        /*
         * Standard format.
         */
***************
*** 522,527 ****
--- 529,538 ----
        SPLIT ptable;
        int fid;
  
+       if (gtop->format_version > support_version) {
+               gtags_delete5(gtop, deleteset);
+               return;
+       }
        for (tagline = dbop_first(gtop->dbop, NULL, NULL, 0); tagline; tagline 
= dbop_next(gtop->dbop)) {
                /*
                 * Extract path from the tag line.
***************
*** 583,588 ****
--- 594,601 ----
        const char *key, *tagline;
        int regflags = 0;
  
+       if (gtop->format_version > support_version)
+               return gtags_first5(gtop, pattern, flags);
        gtop->flags = flags;
        if (flags & GTOP_PREFIX && pattern != NULL)
                dbflags |= DBOP_PREFIX;
***************
*** 664,669 ****
--- 677,685 ----
  gtags_next(GTOP *gtop)
  {
        const char *tagline;
+ 
+       if (gtop->format_version > support_version)
+               return gtags_next5(gtop);
        /*
         * Standard format (or only key)
         */
***************
*** 704,709 ****
--- 720,729 ----
  void
  gtags_close(GTOP *gtop)
  {
+       if (gtop->format_version > support_version) {
+               gtags_close5(gtop);
+               return;
+       }
        if (gtop->pool) {
                if (gtop->prev_path[0])
                        flush_pool(gtop);
diff -rcN global/libutil/gtagsop.h global.5/libutil/gtagsop.h
*** global/libutil/gtagsop.h    Tue Oct 25 21:18:21 2005
--- global.5/libutil/gtagsop.h  Tue Feb 21 08:16:55 2006
***************
*** 48,53 ****
--- 48,54 ----
  #define GTAGS_STANDARD                0       /* standard format */
  #define GTAGS_COMPACT         1       /* compact format */
  #define GTAGS_PATHINDEX               2       /* use path index */
+ #define GTAGS_FORMAT5         4       /* test format for version 5 */
  /* gtags_add() */
  #define GTAGS_UNIQUE          1       /* compress duplicate lines */
  #define GTAGS_EXTRACTMETHOD   2       /* extract method from class definition 
*/
diff -rcN global/libutil/gtagsop5.c global.5/libutil/gtagsop5.c
*** global/libutil/gtagsop5.c   Thu Jan  1 09:00:00 1970
--- global.5/libutil/gtagsop5.c Tue Feb 21 09:34:57 2006
***************
*** 0 ****
--- 1,619 ----
+ /*
+  * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2005, 2006
+  *    Tama Communications Corporation
+  *
+  * This file is part of GNU GLOBAL.
+  *
+  * GNU GLOBAL is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 2, or (at your option)
+  * any later version.
+  *
+  * GNU GLOBAL is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+  */
+ 
+ #ifdef HAVE_CONFIG_H
+ #include <config.h>
+ #endif
+ #include <assert.h>
+ #include <ctype.h>
+ #include <stdio.h>
+ #ifdef STDC_HEADERS
+ #include <stdlib.h>
+ #endif
+ #ifdef HAVE_STRING_H
+ #include <string.h>
+ #else
+ #include <strings.h>
+ #endif
+ #ifdef HAVE_UNISTD_H
+ #include <unistd.h>
+ #endif
+ 
+ #include "char.h"
+ #include "conf.h"
+ #include "dbop.h"
+ #include "die.h"
+ #include "format.h"
+ #include "gparam.h"
+ #include "gtagsop.h"
+ #include "gtagsop5.h"
+ #include "locatestring.h"
+ #include "makepath.h"
+ #include "path.h"
+ #include "gpathop.h"
+ #include "split.h"
+ #include "strbuf.h"
+ #include "strhash.h"
+ #include "strlimcpy.h"
+ #include "strmake.h"
+ #include "varray.h"
+ 
+ #define HASHBUCKETS   256
+ 
+ static int compare_lno(const void *, const void *);
+ static void flush_pool(GTOP *);
+ static const char *genrecord(GTOP *);
+ static regex_t reg;
+ 
+ /*
+  * Developing version of gtagsop module.
+  *
+  * You can test this module by gtags -ccc option.
+  *
+  * STANDARD5 format
+  *    <file id> <tag name> <line number> <line image>
+  * COMPACT5 format
+  *    <file id> <tag name> <line number>[,...]
+  */
+ static int support_version = 4;       /* acceptable format version   */
+ /*
+  * gtags_open5: open global tag.
+  *
+  *    i)      dbpath  dbpath directory
+  *    i)      root    root directory (needed when compact format)
+  *    i)      db      GTAGS, GRTAGS, GSYMS
+  *    i)      mode    GTAGS_READ: read only
+  *                    GTAGS_CREATE: create tag
+  *                    GTAGS_MODIFY: modify tag
+  *    i)      flags   GTAGS_COMPACT
+  *                    GTAGS_PATHINDEX
+  *    r)              GTOP structure
+  *
+  * when error occurred, gtagopen doesn't return.
+  */
+ GTOP *
+ gtags_open5(GTOP *gtop, const char *dbpath, const char *root)
+ {
+       int dbmode;
+ 
+       gtop->format_version = 4;
+       switch (gtop->db) {
+       case GTAGS:
+               gtop->format = GTAGS_STANDARD;
+               break;
+       case GRTAGS:
+       case GSYMS:
+               gtop->format = GTAGS_COMPACT;
+               break;
+       default:
+               break;
+       }
+       if (gtop->mode == GTAGS_CREATE) {
+               char buf[128];
+               /*
+                * Don't write format information in the tag file.
+                * dbop_put(gtop->dbop, COMPACTKEY, COMPACTKEY);
+                */
+               snprintf(buf, sizeof(buf), "%s %d", VERSIONKEY, 
gtop->format_version);
+               dbop_put(gtop->dbop, VERSIONKEY, buf);
+       } else {
+               /*
+                * recognize format version of GTAGS. 'format version record'
+                * is saved as a META record in GTAGS and GRTAGS.
+                * if 'format version record' is not found, it's assumed
+                * version 1.
+                */
+               const char *p;
+ 
+               if ((p = dbop_get(gtop->dbop, VERSIONKEY)) != NULL) {
+                       for (p += strlen(VERSIONKEY); *p && isspace((unsigned 
char)*p); p++)
+                               ;
+                       gtop->format_version = atoi(p);
+               }
+               if (gtop->format_version > support_version)
+                       die("GTAGS seems new format. Please install the latest 
GLOBAL.");
+               else if (gtop->format_version < support_version)
+                       die("GTAGS seems older format. Please remake tag 
files.");
+       }
+       switch (gtop->mode) {
+       case GTAGS_READ:
+               dbmode = 0;
+               break;
+       case GTAGS_CREATE:
+               dbmode = 1;
+               break;
+       case GTAGS_MODIFY:
+               dbmode = 2;
+               break;
+       default:
+               break;
+       }
+       if (gpath_open(dbpath, dbmode) < 0) {
+               if (dbmode == 1)
+                       die("cannot create GPATH.");
+               else
+                       die("GPATH not found.");
+       }
+       /*
+        * Stuff for compact format.
+        */
+       if (gtop->format & GTAGS_COMPACT) {
+               assert(root != NULL);
+               strlimcpy(gtop->root, root, sizeof(gtop->root));
+               if (gtop->mode == GTAGS_READ)
+                       gtop->ib = strbuf_open(MAXBUFLEN);
+               else {
+                       gtop->sb = strbuf_open(0);
+                       gtop->pool = strhash_open(HASHBUCKETS, NULL);
+               }
+       }
+       gtop->sb = strbuf_open(0);
+       return gtop;
+ }
+ /*
+  * gtags_put5: put tag record with packing.
+  *
+  *    i)      gtop    descripter of GTOP
+  *    i)      tag     tag name
+  *    i)      ctags_x ctags -x image
+  *
+  * NOTE: If format is GTAGS_COMPACT or GTAGS_PATHINDEX
+  *       then this function is destructive.
+  */
+ void
+ gtags_put5(GTOP *gtop, const char *tag, const char *ctags_x)  /* virtually 
const */
+ {
+       SPLIT ptable;
+       struct sh_entry *entry;
+       int *lno;
+ 
+       if (split((char *)ctags_x, 4, &ptable) != 4) {
+               recover(&ptable);
+               die("illegal tag format.\n'%s'", ctags_x);
+       }
+       if (gtop->format == GTAGS_STANDARD) {
+               const char *fid = gpath_path2fid(ptable.part[PART_PATH].start, 
NULL);
+ 
+               strbuf_reset(gtop->sb);
+               strbuf_puts(gtop->sb, fid);
+               strbuf_putc(gtop->sb, ' ');
+               strbuf_puts(gtop->sb, tag);
+               strbuf_putc(gtop->sb, ' ');
+               strbuf_puts(gtop->sb, ptable.part[PART_LNO].start);
+               strbuf_putc(gtop->sb, ' ');
+               strbuf_puts(gtop->sb, ptable.part[PART_LINE].start);
+               dbop_put(gtop->dbop, tag, strbuf_value(gtop->sb));
+       } else {        /* gtop->format == GTAGS_COMPACT */
+               /*
+                * Flush the pool when path is changed.
+                * Line numbers in the pool will be sorted and duplicated
+                * records will be combined.
+                *
+                * pool    "funcA"   | 1| 3| 7|23|11| 2|...
+                *           v
+                * output  funcA 33 1,2,3,7,11,23...
+                */
+               if (gtop->prev_path[0] && strcmp(gtop->prev_path, 
ptable.part[PART_PATH].start)) {
+                       flush_pool(gtop);
+                       strhash_reset(gtop->pool);
+               }
+               strlimcpy(gtop->prev_path, ptable.part[PART_PATH].start, 
sizeof(gtop->prev_path));
+               /*
+                * Register each record into the pool.
+                *
+                * Pool image:
+                *
+                * tagname   lno
+                * ------------------------------
+                * "funcA"   | 1| 3| 7|23|11| 2|...
+                * "funcB"   |34| 2| 5|66| 3|...
+                * ...
+                */
+               entry = strhash_assign(gtop->pool, ptable.part[PART_TAG].start, 
1);
+               if (entry->value == NULL)
+                       entry->value = varray_open(sizeof(int), 100);
+               lno = varray_append((VARRAY *)entry->value);
+               *lno = atoi(ptable.part[PART_LNO].start);
+       }
+       recover(&ptable);
+ }
+ /*
+  * compare_lno: compare function for sorting line number.
+  */
+ static int
+ compare_lno(const void *s1, const void *s2)
+ {
+       return *(const int *)s1 - *(const int *)s2;
+ }
+ /*
+  * flush_pool: flush the pool and write is as compact format.
+  *
+  *    i)      gtop    descripter of GTOP
+  */
+ static void
+ flush_pool(GTOP *gtop)
+ {
+       struct sh_entry *entry;
+       const char *fid = gpath_path2fid(gtop->prev_path, NULL);
+ 
+       if (fid == NULL)
+               die("GPATH is corrupted.('%s' not found)", gtop->prev_path);
+       /*
+        * Write records as compact format and free line number table
+        * for each entry in the pool.
+        */
+       for (entry = strhash_first(gtop->pool); entry; entry = 
strhash_next(gtop->pool)) {
+               VARRAY *vb = (VARRAY *)entry->value;
+               int *lno_array = varray_assign(vb, 0, 0);
+               const char *key = entry->name;
+ 
+               /*
+                * extract method when class method definition.
+                *
+                * Ex: Class::method(...)
+                *
+                * key  = 'method'
+                * data = 'Class::method  103 ./class.cpp ...'
+                */
+               if (gtop->flags & GTAGS_EXTRACTMETHOD) {
+                       if ((key = locatestring(entry->name, ".", MATCH_LAST)) 
!= NULL)
+                               key++;
+                       else if ((key = locatestring(entry->name, "::", 
MATCH_LAST)) != NULL)
+                               key += 2;
+                       else
+                               key = entry->name;
+               }
+               /* Sort line number table */
+               qsort(lno_array, vb->length, sizeof(int), compare_lno); 
+ 
+               strbuf_reset(gtop->sb);
+               strbuf_puts(gtop->sb, fid);
+               strbuf_putc(gtop->sb, ' ');
+               strbuf_puts(gtop->sb, entry->name);
+               strbuf_putc(gtop->sb, ' ');
+               {
+                       int savelen = strbuf_getlen(gtop->sb);
+                       int last = 0;           /* line 0 doesn't exist */
+                       int i;
+ 
+                       for (i = 0; i < vb->length; i++) {
+                               int n = lno_array[i];
+ 
+                               if ((gtop->flags & GTAGS_UNIQUE) && n == last)
+                                       continue;
+                               if (strbuf_getlen(gtop->sb) > savelen)
+                                       strbuf_putc(gtop->sb, ',');
+                               strbuf_putn(gtop->sb, n);
+                               if (strbuf_getlen(gtop->sb) > DBOP_PAGESIZE / 
4) {
+                                       dbop_put(gtop->dbop, key, 
strbuf_value(gtop->sb));
+                                       strbuf_setlen(gtop->sb, savelen);
+                               }
+                               last = n;
+                       }
+                       if (strbuf_getlen(gtop->sb) > savelen)
+                               dbop_put(gtop->dbop, key, 
strbuf_value(gtop->sb));
+               }
+               /* Free line number table */
+               varray_close(vb);
+       }
+ }
+ /*
+  * gtags_delete5: delete records belong to set of fid.
+  *
+  *    i)      gtop    GTOP structure
+  *    i)      deleteset bit array of fid
+  */
+ void
+ gtags_delete5(GTOP *gtop, IDSET *deleteset)
+ {
+       const char *tagline;
+       int fid;
+ 
+       for (tagline = dbop_first(gtop->dbop, NULL, NULL, 0); tagline; tagline 
= dbop_next(gtop->dbop)) {
+               /*
+                * Extract path from the tag line.
+                */
+               fid = atoi(tagline);
+               /*
+                * If the file id exists in the deleteset, delete the tagline.
+                */
+               if (idset_contains(deleteset, fid))
+                       dbop_delete(gtop->dbop, NULL);
+       }
+ }
+ static char output[MAXBUFLEN+1];
+ /*
+  * gtags_first5: return first record
+  *
+  *    i)      gtop    GTOP structure
+  *    i)      pattern tag name
+  *            o may be regular expression
+  *            o may be NULL
+  *    i)      flags   GTOP_PREFIX     prefix read
+  *                    GTOP_KEY        read key only
+  *                    GTOP_NOSOURCE   don't read source file(compact format)
+  *                    GTOP_NOREGEX    don't use regular expression.
+  *                    GTOP_IGNORECASE ignore case distinction.
+  *                    GTOP_BASICREGEX use basic regular expression.
+  *    r)              record
+  */
+ const char *
+ gtags_first5(GTOP *gtop, const char *pattern, int flags)
+ {
+       int dbflags = 0;
+       char prefix[IDENTLEN+1];
+       regex_t *preg = &reg;
+       const char *key, *tagline;
+       int regflags = 0;
+ 
+       gtop->flags = flags;
+       if (flags & GTOP_PREFIX && pattern != NULL)
+               dbflags |= DBOP_PREFIX;
+       if (flags & GTOP_KEY)
+               dbflags |= DBOP_KEY;
+ 
+       if (!(flags & GTOP_BASICREGEX))
+               regflags |= REG_EXTENDED;
+       if (flags & GTOP_IGNORECASE)
+               regflags |= REG_ICASE;
+ 
+       /*
+        * Get key and compiled regular expression for dbop_xxxx().
+        */
+       if (flags & GTOP_NOREGEX) {
+               key = pattern;
+               preg = NULL;
+       } else if (pattern == NULL || !strcmp(pattern, ".*")) {
+               /*
+                * Since the regular expression '.*' matches to any record,
+                * we take sequential read method.
+                */
+               key = NULL;
+               preg = NULL;
+       } else if (isregex(pattern) && regcomp(preg, pattern, regflags) == 0) {
+               const char *p;
+ 
+               /*
+                * If the pattern include '^' + some non regular expression
+                * characters like '^aaa[0-9]', we take prefix read method
+                * with the non regular expression part as the prefix.
+                */
+               if (!(flags & GTOP_IGNORECASE) && *pattern == '^' && *(p = 
pattern + 1) && !isregexchar(*p)) {
+                       int i = 0;
+ 
+                       while (*p && !isregexchar(*p) && i < IDENTLEN)
+                               prefix[i++] = *p++;
+                       prefix[i] = '\0';
+                       key = prefix;
+                       dbflags |= DBOP_PREFIX;
+               } else {
+                       key = NULL;
+               }
+       } else {
+               key = pattern;
+               preg = NULL;
+       }
+       tagline = dbop_first(gtop->dbop, key, preg, dbflags);
+       if (tagline) {
+               SPLIT ptable;
+               const char *path, *result;
+ 
+               if (gtop->flags & GTOP_KEY)
+                       return tagline;
+               split((char *)tagline, 3, &ptable);
+               if (ptable.npart < 3) {
+                       recover(&ptable);
+                       die("illegal tag format.'%s'\n", tagline);
+               }
+               path = gpath_fid2path(ptable.part[PART_FID5].start, NULL);
+               if (path == NULL)
+                       die("GPATH is corrupted.(file id '%s' not found)", 
ptable.part[PART_FID5].start);
+               /*
+                * Standard format
+                */
+               if (gtop->format == GTAGS_STANDARD) {
+                       char *p;
+ 
+                       for (p = ptable.part[PART_LNO5].start; *p && 
!isspace(*p); p++)
+                               ;
+                       p++;
+                       snprintf(output, sizeof(output), "%-16s %4d %-16s %s",
+                               ptable.part[PART_TAG5].start, 
atoi(ptable.part[PART_LNO5].start), path, p);
+                       recover(&ptable);
+                       result = output;
+               } else {
+                       gtop->line = (char *)tagline;
+                       gtop->opened = 0;
+                       recover(&ptable);
+                       result = genrecord(gtop);
+               }
+               return result;
+       }
+       return NULL;
+ }
+ /*
+  * gtags_next5: return followed record
+  *
+  *    i)      gtop    GTOP structure
+  *    r)              record
+  *                    NULL end of tag
+  */
+ const char *
+ gtags_next5(GTOP *gtop)
+ {
+       const char *tagline;
+ 
+       /*
+        * Standard format (or only key)
+        */
+       if (gtop->flags & GTOP_KEY) {
+               tagline = dbop_next(gtop->dbop);
+               return tagline;
+       } else if (gtop->format == GTAGS_STANDARD) {
+               tagline = dbop_next(gtop->dbop);
+               if (tagline) {
+                       SPLIT ptable;
+                       const char *path, *p;
+ 
+                       split((char *)tagline, 3, &ptable);
+                       if (ptable.npart < 3) {
+                               recover(&ptable);
+                               die("illegal tag format.'%s'\n", tagline);
+                       }
+                       path = gpath_fid2path(ptable.part[PART_FID5].start, 
NULL);
+                       if (path == NULL)
+                               die("GPATH is corrupted.(file id '%s' not 
found)", ptable.part[PART_FID5].start);
+                       for (p = ptable.part[PART_LNO5].start; *p && 
!isspace(*p); p++)
+                               ;
+                       p++;
+                       snprintf(output, sizeof(output), "%-16s %4d %-16s %s",
+                               ptable.part[PART_TAG5].start, 
atoi(ptable.part[PART_LNO5].start), path, p);
+                       recover(&ptable);
+                       return output;
+               }
+               return NULL;
+       }
+       /*
+        * Compact format
+        */
+       else {
+               const char *ctags_x;
+ 
+               /*
+                * If some unpacked record exists then return one of them
+                * else read the next tag line.
+                */
+               if ((ctags_x = genrecord(gtop)) != NULL)
+                       return ctags_x;
+               if ((tagline = dbop_next(gtop->dbop)) == NULL)
+                       return NULL;
+               gtop->line = (char *)tagline;           /* gtop->line = $0 */
+               gtop->opened = 0;
+               return genrecord(gtop);
+       }
+ }
+ /*
+  * gtags_close5: close tag file
+  *
+  *    i)      gtop    GTOP structure
+  */
+ void
+ gtags_close5(GTOP *gtop)
+ {
+       if (gtop->pool) {
+               if (gtop->prev_path[0])
+                       flush_pool(gtop);
+               strhash_close(gtop->pool);
+       }
+       if (gtop->sb)
+               strbuf_close(gtop->sb);
+       if (gtop->ib)
+               strbuf_close(gtop->ib);
+       gpath_close();
+       dbop_close(gtop->dbop);
+       free(gtop);
+ }
+ /*
+  * genrecord: generate original tag line from compact format.
+  *
+  *    io)     gtop    GTOP structure
+  *    r)              tag line
+  */
+ static const char *
+ genrecord(GTOP *gtop)
+ {
+       const char *lnop, *path;
+ 
+       if (!gtop->opened) {
+               SPLIT ptable;
+               const char *path;
+ 
+               gtop->opened = 1;
+               /*
+                * Compact format
+                */
+               split(gtop->line, 3, &ptable);
+               if (ptable.npart != 3) {
+                       recover(&ptable);
+                       die("illegal compact format. '%s'\n", gtop->line);
+               }
+               /* Tag name */
+               strlimcpy(gtop->tag, ptable.part[PART_TAG5].start, 
sizeof(gtop->tag));
+ 
+               /* Path name */
+               if ((path = gpath_fid2path(ptable.part[PART_FID5].start, NULL)) 
== NULL)
+                       die("GPATH is corrupted.('%s' not found)", 
ptable.part[PART_FID5].start);
+               strlimcpy(gtop->path, path, sizeof(gtop->path));
+ 
+               /* line number list */
+               gtop->lnop = ptable.part[PART_LNO5].start;
+               /*
+                * Open source file.
+                */
+               if (!(gtop->flags & GTOP_NOSOURCE)) {
+                       char path[MAXPATHLEN+1];
+ 
+                       if (gtop->root)
+                               snprintf(path, sizeof(path),
+                                       "%s/%s", gtop->root, &gtop->path[2]);
+                       else
+                               snprintf(path, sizeof(path), "%s", 
&gtop->path[2]);
+                       gtop->fp = fopen(path, "r");
+                       gtop->lno = 0;
+               }
+               recover(&ptable);
+       }
+ 
+       lnop = gtop->lnop;
+       if (*lnop >= '0' && *lnop <= '9') {
+               const char *src = "";
+               int lno;
+ 
+               /* get line number */
+               for (lno = 0; *lnop >= '0' && *lnop <= '9'; lnop++)
+                       lno = lno * 10 + *lnop - '0';
+               if (*lnop == ',')
+                       lnop++;
+               gtop->lnop = lnop;
+               if (gtop->fp) {
+                       /*
+                        * If it is duplicate line, return the previous line.
+                        */
+                       if (gtop->lno == lno)
+                               return output;
+                       while (gtop->lno < lno) {
+                               if (!(src = strbuf_fgets(gtop->ib, gtop->fp, 
STRBUF_NOCRLF)))
+                                       die("unexpected end of file. '%s'", 
gtop->path);
+                               gtop->lno++;
+                       }
+               }
+               snprintf(output, sizeof(output), "%-16s %4d %-16s %s",
+                       gtop->tag, lno, gtop->path, src);
+               return output;
+       } else {
+               if (gtop->opened && gtop->fp != NULL) {
+                       gtop->opened = 0;
+                       fclose(gtop->fp);
+                       gtop->fp = NULL;
+               }
+               return NULL;
+       }
+ }
diff -rcN global/libutil/gtagsop5.h global.5/libutil/gtagsop5.h
*** global/libutil/gtagsop5.h   Thu Jan  1 09:00:00 1970
--- global.5/libutil/gtagsop5.h Tue Feb 21 08:17:17 2006
***************
*** 0 ****
--- 1,41 ----
+ /*
+  * Copyright (c) 2006
+  *    Tama Communications Corporation
+  *
+  * This file is part of GNU GLOBAL.
+  *
+  * GNU GLOBAL is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+  * the Free Software Foundation; either version 2, or (at your option)
+  * any later version.
+  *
+  * GNU GLOBAL is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  * GNU General Public License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+  */
+ #ifndef _GTOP5_H_
+ #define _GTOP5_H_
+ 
+ #include <stdio.h>
+ #include <ctype.h>
+ 
+ #include "gparam.h"
+ #include "dbop.h"
+ #include "gtagsop.h"
+ #include "idset.h"
+ #include "strbuf.h"
+ #include "strhash.h"
+ 
+ GTOP *gtags_open5(GTOP *, const char *, const char *);
+ void gtags_put5(GTOP *, const char *, const char *);
+ void gtags_delete5(GTOP *, IDSET *);
+ const char *gtags_first5(GTOP *, const char *, int);
+ const char *gtags_next5(GTOP *);
+ void gtags_close5(GTOP *);
+ 
+ #endif /* ! _GTOP5_H_ */
--
Shigio YAMAGUCHI <address@hidden> - Tama Communications Corporation
PGP fingerprint: D1CB 0B89 B346 4AB6 5663  C4B6 3CA5 BBB3 57BE DDA3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]