[Top][All Lists]
[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 = ®
+ 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, >op->path[2]);
+ else
+ snprintf(path, sizeof(path), "%s",
>op->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
- [RFC] Suggestion of new tag format,
Shigio YAMAGUCHI <=