/* * for debugging. */ void abbrev_dump(void) { struct abbrmap *ab; int i, limit = sizeof(ab2name) / sizeof(struct abbrmap); if (!name2ab) { fprintf(stderr, "name2ab is NULL.\n"); return; } fprintf(stderr, "ab2name: %d entries\n", limit); for (i = 0; i < limit; i++) { if (ab2name[i].c != 0) { fprintf(stderr, "ab2name[%d].c = %c\n", i, ab2name[i].c); fprintf(stderr, "ab2name[%d].name = %s\n", i, ab2name[i].name); } } ab = (struct abbrmap *)varray_assign(name2ab, 0, 0); limit = name2ab->length; fprintf(stderr, "name2ab: %d entries\n", limit); for (i = 0; i < limit; i++) { if (ab[i].c != 0) { fprintf(stderr, "name2ab[%d].c = %c\n", i, ab[i].c); fprintf(stderr, "name2ab[%d].name = %s\n", i, ab[i].name); } } }
/* * find_open: start iterator without GPATH. * * i) start start directory * If NULL, assumed '.' directory. */ void find_open(const char *start) { struct stack_entry *curp; assert(find_mode == 0); find_mode = FIND_OPEN; /* * This is temporary measures. It doesn't decide how to become final. */ if (getenv("GTAGSALLOWBLANK")) allow_blank = 1; if (!start) start = "./"; /* * setup stack. */ stack = varray_open(sizeof(struct stack_entry), 50); current_entry = 0; curp = varray_assign(stack, current_entry, 1); strlimcpy(dir, start, sizeof(dir)); curp->dirp = dir + strlen(dir); curp->sb = strbuf_open(0); if (getdirs(dir, curp->sb) < 0) die("cannot open '.' directory."); curp->start = curp->p = strbuf_value(curp->sb); curp->end = curp->start + strbuf_getlen(curp->sb); /* * prepare regular expressions. */ prepare_source(); prepare_skip(); }
/** * Read a tag segment with sorting. * * @param[in] gtop #GTOP structure <br> * Output: @CODE{gtop->gtp_array} segment table <br> * Output: @CODE{gtop->gtp_count} segment table size <br> * Output: @CODE{gtop->gtp_index} segment table index (initial value = 0) <br> * Output: @CODE{gtop->cur_tagname} current tag name * * A segment is a set of tag records which have same tag name. <br> * This function read a segment from tag file, sort it and put it on segment table. <br> * This function can treat both of standard format and compact format. * * Sorting is done by three keys. * - 1st key: tag name * - 2nd key: file name * - 3rd key: line number * * Since all records in a segment have same tag name, you need not think about 1st key. */ void segment_read(GTOP *gtop) { const char *tagline, *fid, *path, *lineno; GTP *gtp; struct sh_entry *sh; /* * Save tag lines. */ gtop->cur_tagname[0] = '\0'; while ((tagline = dbop_next(gtop->dbop)) != NULL) { VIRTUAL_GRTAGS_GSYMS_PROCESSING(gtop); /* * get tag name and line number. * * tagline = <file id> <tag name> <line number> */ if (gtop->cur_tagname[0] == '\0') { strlimcpy(gtop->cur_tagname, gtop->dbop->lastkey, sizeof(gtop->cur_tagname)); } else if (strcmp(gtop->cur_tagname, gtop->dbop->lastkey) != 0) { /* * Dbop_next() wil read the same record again. */ dbop_unread(gtop->dbop); break; } gtp = varray_append(gtop->vb); gtp->tagline = pool_strdup(gtop->segment_pool, tagline, 0); gtp->tag = (const char *)gtop->cur_tagname; /* * convert fid into hashed path name to save memory. */ fid = (const char *)strmake(tagline, " "); path = gpath_fid2path(fid, NULL); if (path == NULL) die("gtags_first: path not found. (fid=%s)", fid); sh = strhash_assign(gtop->path_hash, path, 1); gtp->path = sh->name; lineno = seekto(gtp->tagline, SEEKTO_LINENO); if (lineno == NULL) die("illegal tag record.\n%s", tagline); gtp->lineno = atoi(lineno); } /* * Sort tag lines. */ gtop->gtp_array = varray_assign(gtop->vb, 0, 0); gtop->gtp_count = gtop->vb->length; gtop->gtp_index = 0; if (!(gtop->flags & GTOP_NOSORT)) qsort(gtop->gtp_array, gtop->gtp_count, sizeof(GTP), compare_tags); }
/** * gfind_open: start iterator using GPATH. * * @param[in] dbpath dbpath * @param[in] local local prefix, * if NULL specified, it assumes "./"; * @param[in] target GPATH_SOURCE: only source file, * GPATH_OTHER: only other file, * GPATH_BOTH: source file + other file * @param[in] flags GPATH_NEARSORT * @return GFIND structure */ GFIND * gfind_open(const char *dbpath, const char *local, int target, int flags) { GFIND *gfind = (GFIND *)check_calloc(sizeof(GFIND), 1); gfind->dbop = dbop_open(makepath(dbpath, dbname(GPATH), NULL), 0, 0, 0); if (gfind->dbop == NULL) die("GPATH not found."); gfind->path = NULL; gfind->prefix = check_strdup(local ? local : "./"); gfind->first = 1; gfind->eod = 0; gfind->target = target; gfind->type = GPATH_SOURCE; gfind->flags = flags; gfind->path_array = NULL; gfind->version = dbop_getversion(gfind->dbop); if (gfind->version > support_version) die("GPATH seems new format. Please install the latest GLOBAL."); else if (gfind->version < support_version) die("GPATH seems older format. Please remake tag files."); /* * Nearness sort. * In fact, this timing of sort is not good for performance. * Reconsideration is needed later. */ if (gfind->flags & GPATH_NEARSORT) { const char *path = NULL; VARRAY *varray = varray_open(sizeof(char *), 100); POOL *pool = pool_open(); while ((path = gfind_read(gfind)) != NULL) { char **a = varray_append(varray); *a = pool_strdup(pool, path, 0); } if ((nearbase = get_nearbase_path()) == NULL) die("cannot get nearbase path."); qsort(varray_assign(varray, 0, 0), varray->length, sizeof(char *), compare_nearpath); gfind->path_array = varray; gfind->pool = pool; gfind->index = 0; } return gfind; }
/** * gfind_read: read path using GPATH. * * @param[in] gfind GFIND structure * @return path */ const char * gfind_read(GFIND *gfind) { const char *flag; if (gfind->path_array) { char **a = NULL; if (gfind->index >= gfind->path_array->length) return NULL; a = varray_assign(gfind->path_array, gfind->index++, 0); return *a; } gfind->type = GPATH_SOURCE; if (gfind->eod) return NULL; for (;;) { if (gfind->first) { gfind->first = 0; gfind->path = dbop_first(gfind->dbop, gfind->prefix, NULL, DBOP_KEY | DBOP_PREFIX); } else { gfind->path = dbop_next(gfind->dbop); } if (gfind->path == NULL) { gfind->eod = 1; break; } /* * if gfind->target == 0, return only source files. * *flag == 'o' means 'other files' like README. */ flag = dbop_getflag(gfind->dbop); if (flag == NULL) flag = ""; gfind->type = (*flag == 'o') ? GPATH_OTHER : GPATH_SOURCE; if (gfind->type & gfind->target) break; } return gfind->path; }
/** * anchor_load: load anchor table * * @param[in] path path name */ void anchor_load(const char *path) { int db, current_fid; /* Get fid of the path */ { const char *p = path2fid(path); if (p == NULL) die("anchor_load: internal error. file '%s' not found in GPATH.", path); current_fid = atoi(p); } FIRST = LAST = 0; end = CURRENT = NULL; if (vb == NULL) vb = varray_open(sizeof(struct anchor), 1000); else varray_reset(vb); for (db = GTAGS; db < GTAGLIM; db++) { XARGS *xp; char *ctags_xid; if ((xp = anchor_input[db]) == NULL) continue; /* * Read from input stream until it reaches end of file * or the line of another file appears. */ while ((ctags_xid = xargs_read(xp)) != NULL) { SPLIT ptable; struct anchor *a; int type, fid; const char *ctags_x = parse_xid(ctags_xid, NULL, &fid); /* * It is the following file. */ if (current_fid != fid) { xargs_unread(xp); break; } if (split(ctags_x, 4, &ptable) < 4) { recover(&ptable); die("too small number of parts in anchor_load().\n'%s'", ctags_x); } if (db == GTAGS) { char *p = ptable.part[PART_LINE].start; for (; *p && isspace((unsigned char)*p); p++) ; if (!*p) { recover(&ptable); die("The output of parser is illegal.\n%s", ctags_x); } /* * Function header is applied only to the anchor whoes type is 'D'. * (D: function, M: macro, T: type) */ type = 'T'; if (*p == '#') type = 'M'; else if (locatestring(p, "typedef", MATCH_AT_FIRST)) type = 'T'; else if ((p = locatestring(p, ptable.part[PART_TAG].start, MATCH_FIRST)) != NULL) { /* skip a tag and the following blanks */ p += strlen(ptable.part[PART_TAG].start); for (; *p && isspace((unsigned char)*p); p++) ; if (*p == '(') type = 'D'; } } else if (db == GRTAGS) type = 'R'; else type = 'Y'; /* allocate an entry */ a = varray_append(vb); a->lineno = atoi(ptable.part[PART_LNO].start); a->type = type; a->done = 0; settag(a, ptable.part[PART_TAG].start); recover(&ptable); } if (ctags_xid == NULL) { xargs_close(anchor_input[db]); anchor_input[db] = NULL; } } if (vb->length == 0) { table = NULL; } else { int i, used = vb->length; /* * Sort by lineno. */ table = varray_assign(vb, 0, 0); qsort(table, used, sizeof(struct anchor), cmp); /* * Setup some lineno. */ for (i = 0; i < used; i++) if (table[i].type == 'D') break; if (i < used) FIRST = table[i].lineno; for (i = used - 1; i >= 0; i--) if (table[i].type == 'D') break; if (i >= 0) LAST = table[i].lineno; } /* * Setup loop range. */ start = table; curp = NULL; end = &table[vb->length]; /* anchor_dump(stderr, 0);*/ }
/** * flush_pool: flush the pool and write is as compact format. * * @param[in] gtop descripter of #GTOP * @param[in] s_fid */ static void flush_pool(GTOP *gtop, const char *s_fid) { struct sh_entry *entry; int header_offset; int i, last; if (s_fid == NULL && (s_fid = gpath_path2fid(gtop->cur_path, NULL)) == NULL) die("GPATH is corrupted.('%s' not found)", gtop->cur_path); /* * Write records as compact format and free line number table * for each entry in the pool. */ for (entry = strhash_first(gtop->path_hash); entry; entry = strhash_next(gtop->path_hash)) { 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_lineno); strbuf_reset(gtop->sb); strbuf_puts(gtop->sb, s_fid); strbuf_putc(gtop->sb, ' '); if (gtop->format & GTAGS_COMPNAME) { strbuf_puts(gtop->sb, compress(entry->name, key)); } else { strbuf_puts(gtop->sb, entry->name); } strbuf_putc(gtop->sb, ' '); header_offset = strbuf_getlen(gtop->sb); /* * If GTAGS_COMPLINE flag is set, each line number is expressed as the * difference from the previous line number except for the head. * GTAGS_COMPLINE is set by default in format version 5. */ if (gtop->format & GTAGS_COMPLINE) { int cont = 0; last = 0; /* line 0 doesn't exist */ for (i = 0; i < vb->length; i++) { int n = lno_array[i]; if (n == last) continue; if (last > 0 && n == last + 1) { if (!cont) { /* * Don't use range expression at the head. */ if (strbuf_getlen(gtop->sb) == header_offset) strbuf_putn(gtop->sb, n); else cont = last; } } else { /* * Range expression. ex: 10-2 means 10 11 12 */ if (cont) { strbuf_putc(gtop->sb, '-'); strbuf_putn(gtop->sb, last - cont); cont = 0; } if (strbuf_getlen(gtop->sb) > header_offset) { strbuf_putc(gtop->sb, ','); strbuf_putn(gtop->sb, n - last); } else { 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, header_offset); } } last = n; } if (cont) { strbuf_putc(gtop->sb, '-'); strbuf_putn(gtop->sb, last - cont); } } else { /* * This code is to support older format (version 4). */ last = 0; /* line 0 doesn't exist */ for (i = 0; i < vb->length; i++) { int n = lno_array[i]; if (n == last) continue; if (strbuf_getlen(gtop->sb) > header_offset) 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, header_offset); } last = n; } } if (strbuf_getlen(gtop->sb) > header_offset) { dbop_put(gtop->dbop, key, strbuf_value(gtop->sb)); } /* Free line number table */ varray_close(vb); } }
/* * compress source line. * * i) in source line * i) name replaced string * r) compressed string */ char * compress(const char *in, const char *name) { STATIC_STRBUF(sb); const char *p = in; int length = strlen(name); int spaces = 0; strbuf_clear(sb); while (*p) { if (*p == ' ') { spaces++; p++; continue; } if (spaces > 0) { if (spaces >= 10) { strbuf_putc(sb, '@'); strbuf_putc(sb, '{'); strbuf_putn(sb, spaces); strbuf_putc(sb, '}'); } else if (spaces > 3) { strbuf_putc(sb, '@'); strbuf_putn(sb, spaces); } else { strbuf_nputc(sb, ' ', spaces); } } spaces = 0; if (*p == '@') { strbuf_puts(sb, "@@"); p++; } else if (!strncmp(p, name, length)) { strbuf_puts(sb, "@n"); p += length; } else if (name2ab) { int i, limit = name2ab->length; struct abbrmap *ab = (struct abbrmap *)varray_assign(name2ab, 0, 0); for (i = 0; i < limit; i++) { if (!strncmp(p, ab[i].name, ab[i].length)) { strbuf_putc(sb, '@'); strbuf_putc(sb, ab[i].c); p += ab[i].length; break; } } if (i >= limit) { strbuf_putc(sb, *p); p++; } } else { strbuf_putc(sb, *p); p++; } } if (spaces > 0) { if (spaces < 4) { strbuf_nputc(sb, ' ', spaces); } else if (spaces < 10) { strbuf_putc(sb, '@'); strbuf_putn(sb, spaces); } else { strbuf_putc(sb, '@'); strbuf_putc(sb, '{'); strbuf_putn(sb, spaces); strbuf_putc(sb, '}'); } } return strbuf_value(sb); }
/* * find_read_traverse: read path without GPATH. * * r) path */ char * find_read_traverse(void) { static char val[MAXPATHLEN]; char path[MAXPATHLEN]; struct stack_entry *curp = varray_assign(stack, current_entry, 1); for (;;) { while (curp->p < curp->end) { char type = *(curp->p); const char *unit = curp->p + 1; curp->p += strlen(curp->p) + 1; /* * Skip files described in the skip list. */ /* makepath() returns unsafe module local area. */ strlimcpy(path, makepath(dir, unit, NULL), sizeof(path)); if (type == 'd') strcat(path, "/"); if (skipthisfile(path)) continue; if (type == 'f') { /* * Skip the following: * o directory * o file which does not exist * o dead symbolic link */ if (!test("f", path)) { if (test("d", path)) warning("'%s' is a directory. (Ignored)", path); else warning("'%s' not found. (Ignored)", path); continue; } /* * GLOBAL cannot treat path which includes blanks. * It will be improved in the future. */ if (!allow_blank && locatestring(path, " ", MATCH_FIRST)) { warning("'%s' ignored, because it includes blank.", &path[2]); continue; } /* * A blank at the head of path means * other than source file. */ if (regexec(suff, path, 0, 0, 0) == 0) { /* source file */ strlimcpy(val, path, sizeof(val)); } else { /* other file like 'Makefile' */ val[0] = ' '; strlimcpy(&val[1], path, sizeof(val) - 1); } val[sizeof(val) - 1] = '\0'; return val; } if (type == 'd') { STRBUF *sb = strbuf_open(0); char *dirp = curp->dirp; strcat(dirp, unit); strcat(dirp, "/"); if (getdirs(dir, sb) < 0) { warning("cannot open directory '%s'. (Ignored)", dir); strbuf_close(sb); *(curp->dirp) = 0; continue; } /* * Push stack. */ curp = varray_assign(stack, ++current_entry, 1); curp->dirp = dirp + strlen(dirp); curp->sb = sb; curp->start = curp->p = strbuf_value(sb); curp->end = curp->start + strbuf_getlen(sb); } } strbuf_close(curp->sb); curp->sb = NULL; if (current_entry == 0) break; /* * Pop stack. */ curp = varray_assign(stack, --current_entry, 0); *(curp->dirp) = 0; } find_eof = 1; return NULL; }
/** * varray_append: append varray entry. * * @param[in] vb #VARRAY structure * @return pointer of the entry * * This procedure @EMPH{doesn't} operate the contents of the array. */ void * varray_append(VARRAY *vb) { return varray_assign(vb, vb->length, 1); }