/* */
This source file includes following definitions.
- compare_path
- compare_lineno
- compare_tags
- seekto
- is_defined_in_GTAGS
- dbname
- gtags_open
- gtags_put_using
- gtags_flush
- gtags_delete
- gtags_first
- gtags_next
- gtags_close
- flush_pool
- 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 = ®
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 >op->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 ? >op->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 >op->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 >op->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 ? >op->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 >op->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 }
/* */