/** * updatetags: update tag file. * * @param[in] dbpath directory in which tag file exist * @param[in] root root directory of source tree * @param[in] deleteset bit array of fid of deleted or modified files * @param[in] addlist @CODE{\\0} separated list of added or modified files */ void updatetags(const char *dbpath, const char *root, IDSET *deleteset, STRBUF *addlist) { struct put_func_data data; int seqno, flags; const char *path, *start, *end; if (vflag) fprintf(stderr, "[%s] Updating '%s' and '%s'.\n", now(), dbname(GTAGS), dbname(GRTAGS)); /* * Open tag files. */ data.gtop[GTAGS] = gtags_open(dbpath, root, GTAGS, GTAGS_MODIFY, 0); if (test("f", makepath(dbpath, dbname(GRTAGS), NULL))) { data.gtop[GRTAGS] = gtags_open(dbpath, root, GRTAGS, GTAGS_MODIFY, 0); } else { /* * If you set NULL to data.gtop[GRTAGS], parse_file() doesn't write to * GRTAGS. See put_syms(). */ data.gtop[GRTAGS] = NULL; } /* * Delete tags from GTAGS. */ if (!idset_empty(deleteset)) { if (vflag) { char fid[MAXFIDLEN]; int total = idset_count(deleteset); unsigned int id; seqno = 1; for (id = idset_first(deleteset); id != END_OF_ID; id = idset_next(deleteset)) { snprintf(fid, sizeof(fid), "%d", id); path = gpath_fid2path(fid, NULL); if (path == NULL) die("GPATH is corrupted."); fprintf(stderr, " [%d/%d] deleting tags of %s\n", seqno++, total, path + 2); } } gtags_delete(data.gtop[GTAGS], deleteset); if (data.gtop[GRTAGS] != NULL) gtags_delete(data.gtop[GRTAGS], deleteset); } /* * Set flags. */ data.gtop[GTAGS]->flags = 0; if (extractmethod) data.gtop[GTAGS]->flags |= GTAGS_EXTRACTMETHOD; data.gtop[GRTAGS]->flags = data.gtop[GTAGS]->flags; flags = 0; if (vflag) flags |= PARSER_VERBOSE; if (debug) flags |= PARSER_DEBUG; if (wflag) flags |= PARSER_WARNING; /* * Add tags to GTAGS and GRTAGS. */ start = strbuf_value(addlist); end = start + strbuf_getlen(addlist); seqno = 0; for (path = start; path < end; path += strlen(path) + 1) { gpath_put(path, GPATH_SOURCE); data.fid = gpath_path2fid(path, NULL); if (data.fid == NULL) die("GPATH is corrupted.('%s' not found)", path); if (vflag) fprintf(stderr, " [%d/%d] extracting tags of %s\n", ++seqno, total, path + 2); parse_file(path, flags, put_syms, &data); gtags_flush(data.gtop[GTAGS], data.fid); if (data.gtop[GRTAGS] != NULL) gtags_flush(data.gtop[GRTAGS], data.fid); } parser_exit(); gtags_close(data.gtop[GTAGS]); if (data.gtop[GRTAGS] != NULL) gtags_close(data.gtop[GRTAGS]); }
/** * makedefineindex: make definition index (including alphabetic index) * * @param[in] file definition index file * @param[in] total definitions total * @param[out] defines \@defines * @par Globals used (input): * tag cache XXX: should this be global output, not input? */ int makedefineindex(const char *file, int total, STRBUF *defines) { int count = 0; int alpha_count = 0; FILEOP *fileop_MAP = NULL, *fileop_DEFINES, *fileop_ALPHA = NULL; FILE *MAP = NULL; FILE *DEFINES, *STDOUT, *TAGS, *ALPHA = NULL; STRBUF *sb = strbuf_open(0); STRBUF *url = strbuf_open(0); /* Index link */ const char *target = (Fflag) ? "mains" : "_top"; const char *indexlink; const char *index_string = "Index Page"; char command[1024], buf[1024], alpha[32], alpha_f[32], *_; if (!aflag && !Fflag) indexlink = "mains"; else if (Fflag) indexlink = "../defines"; else indexlink = "../mains"; if (map_file) { fileop_MAP = open_output_file(makepath(distpath, "MAP", NULL), 0); MAP = get_descripter(fileop_MAP); } fileop_DEFINES = open_output_file(makepath(distpath, file, NULL), 0); DEFINES = get_descripter(fileop_DEFINES); fputs_nl(gen_page_begin(title_define_index, TOPDIR), DEFINES); fputs_nl(body_begin, DEFINES); fputs(header_begin, DEFINES); if (Fflag) fputs(gen_href_begin(NULL, "defines", normal_suffix, NULL), DEFINES); fputs(title_define_index, DEFINES); if (Fflag) fputs(gen_href_end(), DEFINES); fputs_nl(header_end, DEFINES); if (!aflag && !Fflag) { fputs(gen_href_begin_with_title(NULL, indexlink, normal_suffix, NULL, index_string), DEFINES); if (Iflag) fputs(gen_image(CURRENT, back_icon, ".."), DEFINES); else fputs("[..]", DEFINES); fputs_nl(gen_href_end(), DEFINES); } if (!aflag) { if (!no_order_list) fputs_nl(list_begin, DEFINES); } /* * map DEFINES to STDOUT. */ STDOUT = DEFINES; snprintf(command, sizeof(command), PQUOTE "%s -c" PQUOTE, quote_shell(global_path)); if ((TAGS = popen(command, "r")) == NULL) die("cannot fork."); alpha[0] = '\0'; while ((_ = strbuf_fgets(sb, TAGS, STRBUF_NOCRLF)) != NULL) { const char *tag, *line; char guide[1024], url_for_map[1024]; count++; tag = _; message(" [%d/%d] adding %s", count, total, tag); if (aflag && (alpha[0] == '\0' || !locatestring(tag, alpha, MATCH_AT_FIRST))) { const char *msg = (alpha_count == 1) ? "definition" : "definitions"; int c; if (alpha[0]) { char tmp[128]; snprintf(tmp, sizeof(tmp), "%d %s", alpha_count, msg); strbuf_puts(defines, gen_href_begin_with_title("defines", alpha_f, HTML, NULL, tmp)); strbuf_sprintf(defines, "[%s]", alpha); strbuf_puts_nl(defines, gen_href_end()); alpha_count = 0; if (!no_order_list) fputs_nl(list_end, ALPHA); else fputs_nl(br, ALPHA); fputs(gen_href_begin_with_title(NULL, indexlink, normal_suffix, NULL, index_string), ALPHA); if (Iflag) fputs(gen_image(PARENT, back_icon, ".."), ALPHA); else fputs("[..]", ALPHA); fputs_nl(gen_href_end(), ALPHA); fputs_nl(body_end, ALPHA); fputs_nl(gen_page_end(), ALPHA); close_file(fileop_ALPHA); html_count++; } /* * setup index char (for example, 'a' of '[a]'). * alpha is used for display. * alpha_f is used for part of path. */ c = (unsigned char)*tag; if (c > 127) { int i2 = *(tag + 1) & 0xff; /* * for multi-byte(EUC) code. */ alpha[0] = *tag; alpha[1] = *(tag + 1); alpha[2] = '\0'; snprintf(alpha_f, sizeof(alpha_f), "%03d%03d", c, i2); } else if (isalpha(c) || c == '_') { alpha[0] = *tag; alpha[1] = '\0'; /* * for CD9660 or FAT file system */ if (islower(c)) { alpha_f[0] = 'l'; alpha_f[1] = *tag; alpha_f[2] = '\0'; } else { alpha_f[0] = *tag; alpha_f[1] = '\0'; } } else { alpha[0] = *tag; alpha[1] = '\0'; snprintf(alpha_f, sizeof(alpha_f), "%03d", c); } snprintf(buf, sizeof(buf), "%s/defines/%s.%s", distpath, alpha_f, HTML); fileop_ALPHA = open_output_file(buf, cflag); ALPHA = get_descripter(fileop_ALPHA); snprintf(buf, sizeof(buf), "[%s]", alpha); fputs_nl(gen_page_begin(buf, SUBDIR), ALPHA); fputs_nl(body_begin, ALPHA); fprintf(ALPHA, "%s[%s]%s\n", header_begin, alpha, header_end); fputs(gen_href_begin_with_title(NULL, indexlink, normal_suffix, NULL, index_string), ALPHA); if (Iflag) fputs(gen_image(PARENT, back_icon, ".."), ALPHA); else fputs("[..]", ALPHA); fputs_nl(gen_href_end(), ALPHA); if (!no_order_list) fputs_nl(list_begin, ALPHA); else fprintf(ALPHA, "%s%s\n", br, br); STDOUT = ALPHA; } alpha_count++; /* * generating url for function definition. */ line = cache_get(GTAGS, tag); strbuf_reset(url); if (line == NULL) die("internal error in makedefineindex()."); /* * About the format of 'line', please see the head comment of cache.c. */ if (*line == ' ') { const char *fid = line + 1; const char *enumber = nextstring(fid); snprintf(url_for_map, sizeof(url_for_map), "%s/%s.%s", DEFS, fid, HTML); if (dynamic) { if (*action != '/' && aflag) strbuf_puts(url, "../"); strbuf_puts(url, action); strbuf_sprintf(url, "?pattern=%s%stype=definitions", tag, quote_amp); } else { if (aflag) strbuf_puts(url, "../"); strbuf_sprintf(url, "%s/%s.%s", DEFS, fid, HTML); } snprintf(guide, sizeof(guide), "Multiple defined in %s places.", enumber); } else { const char *lno = line; const char *fid = nextstring(line); const char *path = gpath_fid2path(fid, NULL); path += 2; /* remove './' */ snprintf(url_for_map, sizeof(url_for_map), "%s/%s.%s#L%s", SRCS, fid, HTML, lno); if (aflag) strbuf_puts(url, "../"); strbuf_sprintf(url, "%s/%s.%s#L%s", SRCS, fid, HTML, lno); snprintf(guide, sizeof(guide), "Defined at %s in %s.", lno, path); } if (!no_order_list) fputs(item_begin, STDOUT); fputs(gen_href_begin_with_title_target(NULL, strbuf_value(url), NULL, NULL, guide, target), STDOUT); fputs(tag, STDOUT); fputs(gen_href_end(), STDOUT); if (!no_order_list) fputs(item_end, STDOUT); else fputs(br, STDOUT); fputc('\n', STDOUT); if (map_file) fprintf(MAP, "%s\t%s\n", tag, url_for_map); } if (pclose(TAGS) != 0) die("'%s' failed.", command); if (aflag && alpha[0]) { char tmp[128]; const char *msg = (alpha_count == 1) ? "definition" : "definitions"; snprintf(tmp, sizeof(tmp), "%d %s", alpha_count, msg); strbuf_puts(defines, gen_href_begin_with_title("defines", alpha_f, HTML, NULL, tmp)); strbuf_sprintf(defines, "[%s]", alpha); strbuf_puts_nl(defines, gen_href_end()); if (!no_order_list) fputs_nl(list_end, ALPHA); else fputs_nl(br, ALPHA); fputs(gen_href_begin_with_title(NULL, indexlink, normal_suffix, NULL, index_string), ALPHA); if (Iflag) fputs(gen_image(PARENT, back_icon, ".."), ALPHA); else fputs("[..]", ALPHA); fputs_nl(gen_href_end(), ALPHA); fputs_nl(body_end, ALPHA); fputs_nl(gen_page_end(), ALPHA); close_file(fileop_ALPHA); html_count++; fputs(strbuf_value(defines), DEFINES); } if (!no_order_list && !aflag) fputs_nl(list_end, DEFINES); if (!aflag && !Fflag) { fputs(gen_href_begin_with_title(NULL, "mains", normal_suffix, NULL, index_string), DEFINES); if (Iflag) fputs(gen_image(CURRENT, back_icon, ".."), DEFINES); else fputs("[..]", DEFINES); fputs_nl(gen_href_end(), DEFINES); } fputs_nl(body_end, DEFINES); fputs_nl(gen_page_end(), DEFINES); close_file(fileop_DEFINES); html_count++; if (map_file) close_file(fileop_MAP); strbuf_close(sb); strbuf_close(url); return count; }
/** * incremental: incremental update * * @param[in] dbpath dbpath directory * @param[in] root root directory of source tree * @return 0: not updated, 1: updated */ int incremental(const char *dbpath, const char *root) { STATISTICS_TIME *tim; struct stat statp; time_t gtags_mtime; STRBUF *addlist = strbuf_open(0); STRBUF *deletelist = strbuf_open(0); STRBUF *addlist_other = strbuf_open(0); IDSET *deleteset, *findset; int updated = 0; const char *path; unsigned int id, limit; tim = statistics_time_start("Time of inspecting %s and %s.", dbname(GTAGS), dbname(GRTAGS)); if (vflag) { fprintf(stderr, " Tag found in '%s'.\n", dbpath); fprintf(stderr, " Incremental updating.\n"); } /* * get modified time of GTAGS. */ path = makepath(dbpath, dbname(GTAGS), NULL); if (stat(path, &statp) < 0) die("stat failed '%s'.", path); gtags_mtime = statp.st_mtime; if (gpath_open(dbpath, 2) < 0) die("GPATH not found."); /* * deleteset: * The list of the path name which should be deleted from GPATH. * findset: * The list of the path name which exists in the current project. * A project is limited by the --file option. */ deleteset = idset_open(gpath_nextkey()); findset = idset_open(gpath_nextkey()); total = 0; /* * Make add list and delete list for update. */ if (single_update) { int type; const char *fid; if (skipthisfile(single_update)) goto exit; if (test("b", single_update)) goto exit; fid = gpath_path2fid(single_update, &type); if (fid == NULL) { /* new file */ type = issourcefile(single_update) ? GPATH_SOURCE : GPATH_OTHER; if (type == GPATH_OTHER) strbuf_puts0(addlist_other, single_update); else { strbuf_puts0(addlist, single_update); total++; } } else { /* update file */ if (type == GPATH_OTHER) goto exit; idset_add(deleteset, atoi(fid)); strbuf_puts0(addlist, single_update); total++; } } else { if (file_list) find_open_filelist(file_list, root); else find_open(NULL); while ((path = find_read()) != NULL) { const char *fid; int n_fid = 0; int other = 0; /* a blank at the head of path means 'NOT SOURCE'. */ if (*path == ' ') { if (test("b", ++path)) continue; other = 1; } if (stat(path, &statp) < 0) die("stat failed '%s'.", path); fid = gpath_path2fid(path, NULL); if (fid) { n_fid = atoi(fid); idset_add(findset, n_fid); } if (other) { if (fid == NULL) strbuf_puts0(addlist_other, path); } else { if (fid == NULL) { strbuf_puts0(addlist, path); total++; } else if (gtags_mtime < statp.st_mtime) { strbuf_puts0(addlist, path); total++; idset_add(deleteset, n_fid); } } } find_close(); /* * make delete list. */ limit = gpath_nextkey(); for (id = 1; id < limit; id++) { char fid[MAXFIDLEN]; int type; snprintf(fid, sizeof(fid), "%d", id); /* * This is a hole of GPATH. The hole increases if the deletion * and the addition are repeated. */ if ((path = gpath_fid2path(fid, &type)) == NULL) continue; /* * The file which does not exist in the findset is treated * assuming that it does not exist in the file system. */ if (type == GPATH_OTHER) { if (!idset_contains(findset, id) || !test("f", path) || test("b", path)) strbuf_puts0(deletelist, path); } else { if (!idset_contains(findset, id) || !test("f", path)) { strbuf_puts0(deletelist, path); idset_add(deleteset, id); } } } } statistics_time_end(tim); /* * execute updating. */ if ((!idset_empty(deleteset) || strbuf_getlen(addlist) > 0) || (strbuf_getlen(deletelist) + strbuf_getlen(addlist_other) > 0)) { int db; updated = 1; tim = statistics_time_start("Time of updating %s and %s.", dbname(GTAGS), dbname(GRTAGS)); if (!idset_empty(deleteset) || strbuf_getlen(addlist) > 0) updatetags(dbpath, root, deleteset, addlist); if (strbuf_getlen(deletelist) + strbuf_getlen(addlist_other) > 0) { const char *start, *end, *p; if (vflag) fprintf(stderr, "[%s] Updating '%s'.\n", now(), dbname(GPATH)); /* gpath_open(dbpath, 2); */ if (strbuf_getlen(deletelist) > 0) { start = strbuf_value(deletelist); end = start + strbuf_getlen(deletelist); for (p = start; p < end; p += strlen(p) + 1) gpath_delete(p); } if (strbuf_getlen(addlist_other) > 0) { start = strbuf_value(addlist_other); end = start + strbuf_getlen(addlist_other); for (p = start; p < end; p += strlen(p) + 1) { gpath_put(p, GPATH_OTHER); } } /* gpath_close(); */ } /* * Update modification time of tag files * because they may have no definitions. */ for (db = GTAGS; db < GTAGLIM; db++) utime(makepath(dbpath, dbname(db), NULL), NULL); statistics_time_end(tim); } exit: if (vflag) { if (updated) fprintf(stderr, " Global databases have been modified.\n"); else fprintf(stderr, " Global databases are up to date.\n"); fprintf(stderr, "[%s] Done.\n", now()); } strbuf_close(addlist); strbuf_close(deletelist); strbuf_close(addlist_other); gpath_close(); idset_close(deleteset); idset_close(findset); return updated; }
/** * gtags_first: return first record * * @param[in] gtop #GTOP structure * @param[in] pattern tag name <br> * - may be regular expression * - may be @VAR{NULL} * @param[in] flags #GTOP_PREFIX: prefix read <br> * #GTOP_KEY: read key only <br> * #GTOP_PATH: read path only <br> * #GTOP_NOREGEX: don't use regular expression. <br> * #GTOP_IGNORECASE: ignore case distinction. <br> * #GTOP_BASICREGEX: use basic regular expression. <br> * #GTOP_NOSORT: don't sort * @return record */ GTP * gtags_first(GTOP *gtop, const char *pattern, int flags) { int regflags = 0; static regex_t reg; const char *tagline; STATIC_STRBUF(regex); strbuf_clear(regex); gtop->preg = ® gtop->key = NULL; gtop->prefix = NULL; gtop->flags = flags; gtop->dbflags = 0; gtop->readcount = 1; /* Settlement for last time if any */ if (gtop->path_hash) { strhash_close(gtop->path_hash); gtop->path_hash = NULL; } if (gtop->path_array) { free(gtop->path_array); gtop->path_array = NULL; } if (flags & GTOP_KEY) gtop->dbflags |= DBOP_KEY; if (!(flags & GTOP_BASICREGEX)) regflags |= REG_EXTENDED; /* * decide a read method */ if (pattern == NULL) gtop->preg = NULL; else if (pattern[0] == 0) return NULL; else if (!strcmp(pattern, ".*") || !strcmp(pattern, "^.*$") || !strcmp(pattern, "^") || !strcmp(pattern, "$") || !strcmp(pattern, "^.*") || !strcmp(pattern, ".*$")) { /* * Since these regular expressions match to any record, * we take sequential read method. */ gtop->preg = NULL; } else if (flags & GTOP_IGNORECASE) { regflags |= REG_ICASE; if (!isregex(pattern) || flags & GTOP_NOREGEX) { gtop->prefix = get_prefix(pattern, flags); if (gtop->openflags & GTAGS_DEBUG) if (gtop->prefix != NULL) fprintf(stderr, "Using prefix: %s\n", gtop->prefix); if (gtop->prefix == NULL) die("gtags_first: impossible (1)."); strbuf_putc(regex, '^'); strbuf_puts(regex, pattern); if (!(flags & GTOP_PREFIX)) strbuf_putc(regex, '$'); } else if (*pattern == '^' && (gtop->prefix = get_prefix(pattern, flags)) != NULL) { if (gtop->openflags & GTAGS_DEBUG) fprintf(stderr, "Using prefix: %s\n", gtop->prefix); strbuf_puts(regex, pattern); } else { strbuf_puts(regex, pattern); } } else { if (!isregex(pattern) || flags & GTOP_NOREGEX) { if (flags & GTOP_PREFIX) gtop->dbflags |= DBOP_PREFIX; gtop->key = pattern; gtop->preg = NULL; } else if (*pattern == '^' && (gtop->key = get_prefix(pattern, flags)) != NULL) { if (gtop->openflags & GTAGS_DEBUG) fprintf(stderr, "Using prefix: %s\n", gtop->key); gtop->dbflags |= DBOP_PREFIX; gtop->preg = NULL; } else { strbuf_puts(regex, pattern); } } if (gtop->prefix) { if (gtop->key) die("gtags_first: impossible (2)."); gtop->key = gtop->prefix; gtop->dbflags |= DBOP_PREFIX; } if (strbuf_getlen(regex) > 0) { if (gtop->preg == NULL) die("gtags_first: impossible (3)."); if (regcomp(gtop->preg, strbuf_value(regex), regflags) != 0) die("invalid regular expression."); } /* * If GTOP_PATH is set, at first, we collect all path names in a pool and * sort them. gtags_first() and gtags_next() returns one of the pool. */ if (gtop->flags & GTOP_PATH) { struct sh_entry *entry; char *p; const char *cp; unsigned long i; gtop->path_hash = strhash_open(HASHBUCKETS); /* * Pool path names. * * fid path name * +-------------------------- * |100 ./aaa/a.c * |105 ./aaa/b.c * ... */ again0: for (tagline = dbop_first(gtop->dbop, gtop->key, gtop->preg, gtop->dbflags); tagline != NULL; tagline = dbop_next(gtop->dbop)) { VIRTUAL_GRTAGS_GSYMS_PROCESSING(gtop); /* extract file id */ p = locatestring(tagline, " ", MATCH_FIRST); if (p == NULL) die("Invalid tag record. '%s'\n", tagline); *p = '\0'; entry = strhash_assign(gtop->path_hash, tagline, 1); /* new entry: get path name and set. */ if (entry->value == NULL) { cp = gpath_fid2path(tagline, NULL); if (cp == NULL) die("GPATH is corrupted.(file id '%s' not found)", tagline); entry->value = strhash_strdup(gtop->path_hash, cp, 0); } } if (gtop->prefix && gtags_restart(gtop)) goto again0; /* * Sort path names. * * fid path name path_array (sort) * +-------------------------- +---+ * |100 ./aaa/a.c <-------* | * |105 ./aaa/b.c <-------* | * ... ... */ gtop->path_array = (char **)check_malloc(gtop->path_hash->entries * sizeof(char *)); i = 0; for (entry = strhash_first(gtop->path_hash); entry != NULL; entry = strhash_next(gtop->path_hash)) gtop->path_array[i++] = entry->value; if (i != gtop->path_hash->entries) die("Something is wrong. 'i = %lu, entries = %lu'" , i, gtop->path_hash->entries); if (!(gtop->flags & GTOP_NOSORT)) qsort(gtop->path_array, gtop->path_hash->entries, sizeof(char *), compare_path); gtop->path_count = gtop->path_hash->entries; gtop->path_index = 0; if (gtop->path_index >= gtop->path_count) return NULL; gtop->gtp.path = gtop->path_array[gtop->path_index++]; return >op->gtp; } else if (gtop->flags & GTOP_KEY) { again1: for (gtop->gtp.tag = dbop_first(gtop->dbop, gtop->key, gtop->preg, gtop->dbflags); gtop->gtp.tag != NULL; gtop->gtp.tag = dbop_next(gtop->dbop)) { VIRTUAL_GRTAGS_GSYMS_PROCESSING(gtop); break; } if (gtop->gtp.tag == NULL) { if (gtop->prefix && gtags_restart(gtop)) goto again1; } return gtop->gtp.tag ? >op->gtp : NULL; } else { if (gtop->vb == NULL) gtop->vb = varray_open(sizeof(GTP), 200); else varray_reset(gtop->vb); if (gtop->segment_pool == NULL) gtop->segment_pool = pool_open(); else pool_reset(gtop->segment_pool); if (gtop->path_hash == NULL) gtop->path_hash = strhash_open(HASHBUCKETS); else strhash_reset(gtop->path_hash); again2: tagline = dbop_first(gtop->dbop, gtop->key, gtop->preg, gtop->dbflags); if (tagline == NULL) { if (gtop->prefix && gtags_restart(gtop)) goto again2; return NULL; } /* * Dbop_next() wil read the same record again. */ dbop_unread(gtop->dbop); /* * Read a tag segment with sorting. */ segment_read(gtop); return >op->gtp_array[gtop->gtp_index++]; } }