static void load_notfunction(const char *filename) { FILE *ip; STRBUF *sb = strbuf_open(0); STRBUF *ib = strbuf_open(0); char *p; int i; if ((ip = fopen(filename, "r")) == NULL) die("'%s' cannot read.", filename); for (tablesize = 0; (p = strbuf_fgets(ib, ip, STRBUF_NOCRLF)) != NULL; tablesize++) strbuf_puts0(sb, p); fclose(ip); words = (struct words *)check_malloc(sizeof(struct words) * tablesize); /* * Don't free *p. */ p = (char *)check_malloc(strbuf_getlen(sb) + 1); memcpy(p, strbuf_value(sb), strbuf_getlen(sb) + 1); for (i = 0; i < tablesize; i++) { words[i].name = p; p += strlen(p) + 1; } qsort(words, tablesize, sizeof(struct words), cmp); strbuf_close(sb); strbuf_close(ib); }
/* * gpath_put: put path name * * i) path path name * i) type path type * GPATH_SOURCE: source file * GPATH_OTHER: other file */ void gpath_put(const char *path, int type) { char fid[MAXFIDLEN]; STATIC_STRBUF(sb); assert(opened > 0); if (_mode == 1 && created) return; if (dbop_get(dbop, path) != NULL) return; /* * generate new file id for the path. */ snprintf(fid, sizeof(fid), "%d", _nextkey++); /* * path => fid mapping. */ strbuf_clear(sb); strbuf_puts0(sb, fid); if (type == GPATH_OTHER) strbuf_puts0(sb, "o"); dbop_put_withlen(dbop, path, strbuf_value(sb), strbuf_getlen(sb)); /* * fid => path mapping. */ strbuf_clear(sb); strbuf_puts0(sb, path); if (type == GPATH_OTHER) strbuf_puts0(sb, "o"); dbop_put_withlen(dbop, fid, strbuf_value(sb), strbuf_getlen(sb)); }
/* * convertpath: convert source file into hypertext path. * * i) dbpath dbpath * i) htmldir HTML directory made by htags(1) * i) path source file path * o) sb string buffer * r) 0: normal, -1: error */ int convertpath(const char *dbpath, const char *htmldir, const char *path, STRBUF *sb) { static const char *suffix[] = {".html", ".htm"}; static const char *gz = ".gz"; int i, lim = sizeof(suffix)/sizeof(char *); const char *p; strbuf_reset(sb); strbuf_puts(sb, htmldir); strbuf_puts(sb, "/S/"); /* * new style. */ if (gpath_open(dbpath, 0) == 0) { int tag1 = strbuf_getlen(sb); p = gpath_path2fid(path, NULL); if (p == NULL) { gpath_close(); return -1; } gpath_close(); strbuf_puts(sb, p); for (i = 0; i < lim; i++) { int tag2 = strbuf_getlen(sb); strbuf_puts(sb, suffix[i]); if (test("f", strbuf_value(sb))) return 0; strbuf_puts(sb, gz); if (test("f", strbuf_value(sb))) return 0; strbuf_setlen(sb, tag2); } strbuf_setlen(sb, tag1); } /* * old style. */ for (p = path + 1; *p; p++) strbuf_putc(sb, (*p == '/') ? ' ' : *p); for (i = 0; i < lim; i++) { int tag = strbuf_getlen(sb); strbuf_puts(sb, suffix[i]); if (test("f", strbuf_value(sb))) return 0; strbuf_puts(sb, gz); if (test("f", strbuf_value(sb))) return 0; strbuf_setlen(sb, tag); } return -1; }
/* * convert_open: open convert filter * * i) type PATH_ABSOLUTE, PATH_RELATIVE, PATH_THROUGH * i) format tag record format * i) root root directory of source tree * i) cwd current directory * i) dbpath dbpath directory * i) op output file */ CONVERT * convert_open(int type, int format, const char *root, const char *cwd, const char *dbpath, FILE *op) { CONVERT *cv = (CONVERT *)check_calloc(sizeof(CONVERT), 1); /* * set base directory. */ cv->abspath = strbuf_open(MAXPATHLEN); strbuf_puts(cv->abspath, root); strbuf_unputc(cv->abspath, '/'); cv->start_point = strbuf_getlen(cv->abspath); /* * copy elements. */ if (strlen(cwd) > MAXPATHLEN) die("current directory name too long."); strlimcpy(cv->basedir, cwd, sizeof(cv->basedir)); cv->type = type; cv->format = format; cv->op = op; /* * open GPATH. */ if (gpath_open(dbpath, 0) < 0) die("GPATH not found."); return cv; }
/** * Load text from file with replacing @CODE{\@PARENT_DIR\@} macro. * Macro @CODE{\@PARENT_DIR\@} is replaced with the parent directory * of the @FILE{HTML} directory. */ static const char * sed(FILE *ip, int place) { STATIC_STRBUF(sb); const char *parent_dir = (place == SUBDIR) ? "../.." : ".."; int c, start_position = -1; strbuf_clear(sb); while ((c = fgetc(ip)) != EOF) { strbuf_putc(sb, c); if (c == '@') { int curpos = strbuf_getlen(sb); if (start_position == -1) { start_position = curpos - 1; } else { if (!strncmp("@PARENT_DIR@", strbuf_value(sb) + start_position, curpos - start_position)) { strbuf_setlen(sb, start_position); strbuf_puts(sb, parent_dir); start_position = -1; } else { start_position = curpos - 1; } } } else if (!isalpha(c) && c != '_') { if (start_position != -1) start_position = -1; } } return strbuf_value(sb); }
/* * 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(); }
/** * xargs_open_with_strbuf: open xargs stream using string buffer * * @param[in] command command skeleton. * @param[in] max_args 0: no limit, \>0: max argument * @param[in] sb string buffer * @return xargs structure * * The @CODE{'\%s'} in the command skeleton is replaced with given arguments. <br> * If @CODE{'\%s'} doesn't exist, the arguments is appended to the tail of the * skeleton. */ XARGS * xargs_open_with_strbuf(const char *command, int max_args, STRBUF *sb) { XARGS *xp = xargs_open_generic(command, max_args); xp->type = XARGS_STRBUF; xp->curp = strbuf_value(sb); xp->endp = xp->curp + strbuf_getlen(sb); return xp; }
/** * strtrim: make string from original string with deleting blanks. * * @param[in] p original string. * @param[in] flag TRIM_HEAD: from only head, * TRIM_TAIL: from only tail, * TRIM_BOTH: from head and tail, * TRIM_ALL: from all * @param[out] len length of result string, * if len == NULL then nothing returned. * @return result string * * Usage: * strtrim(" # define ", TRIM_HEAD, NULL) => "# define " * strtrim(" # define ", TRIM_TAIL, NULL) => " # define" * strtrim(" # define ", TRIM_BOTH, NULL) => "# define" * strtrim(" # define ", TRIM_ALL, NULL) => "#define" * * [Note] The result string area is function local. So, following call * to this function may destroy the area. */ const char * strtrim(const char *p, int flag, int *len) { STATIC_STRBUF(sb); int cut_off = -1; strbuf_clear(sb); /* * Delete blanks of the head. */ if (flag != TRIM_TAIL) SKIP_BLANKS(p); /* * Copy string. */ for (; *p; p++) { if (isspace(*p)) { if (flag != TRIM_ALL) { if (cut_off == -1 && flag != TRIM_HEAD) cut_off = strbuf_getlen(sb); strbuf_putc(sb,*p); } } else { strbuf_putc(sb,*p); cut_off = -1; } } /* * Delete blanks of the tail. */ if (cut_off != -1) strbuf_setlen(sb, cut_off); if (len) *len = strbuf_getlen(sb); return strbuf_value(sb); }
static int single_select_callback(void *v, int argc, char **argv, char **colname) { STATIC_STRBUF(sb); DBOP *dbop = (DBOP *)v; if (argc > 0) { strbuf_clear(sb); strbuf_puts(sb, argv[0]); dbop->lastsize = strbuf_getlen(sb); if (argv[1]) { strbuf_putc(sb, '\0'); strbuf_puts(sb, argv[1]); } dbop->lastdat = strbuf_value(sb); dbop->lastflag = argv[1] ? dbop->lastdat + dbop->lastsize + 1 : NULL; } else { dbop->lastdat = NULL; dbop->lastflag = NULL; dbop->lastsize = 0; } return SQLITE_OK; }
/** * dbop_put_path: put data and flag by a key. * * @param[in] dbop descripter * @param[in] name key * @param[in] data data * @param[in] flag flag * * [Note] This function doesn't support sorted writing. */ void dbop_put_path(DBOP *dbop, const char *name, const char *data, const char *flag) { STATIC_STRBUF(sb); DB *db = dbop->db; DBT key, dat; int status; int len; #ifdef USE_SQLITE3 if (dbop->openflags & DBOP_SQLITE3) { dbop3_put(dbop, name, data, flag); return; } #endif if (!(len = strlen(name))) die("primary key size == 0."); if (len > MAXKEYLEN) die("primary key too long."); strbuf_clear(sb); strbuf_puts0(sb, data); if (flag) strbuf_puts0(sb, flag); key.data = (char *)name; key.size = len+1; dat.data = strbuf_value(sb); dat.size = strbuf_getlen(sb); status = (*db->put)(db, &key, &dat, 0); switch (status) { case RET_SUCCESS: break; case RET_ERROR: case RET_SPECIAL: die("%s", dbop->put_errmsg ? dbop->put_errmsg : "dbop_put_path failed."); } }
/* * incremental: incremental update * * i) dbpath dbpath directory * i) root root directory of source tree * r) 0: not updated, 1: updated */ int incremental(const char *dbpath, const char *root) { STATISTICS_TIME *tim; struct stat statp; 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 (gpath_open(dbpath, 0) < 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 = gpath_path2fid(single_update, &type); /* * The --single-update=file supports only updating. * If it is new file, this option is ignored, and the processing is * automatically switched to the normal procedure. */ if (fid == NULL) { if (vflag) fprintf(stderr, " --single-update option ignored, because '%s' is new file.\n", single_update); goto normal_update; } /* * If type != GPATH_SOURCE then we have nothing to do, and you will see * a message 'Global databases are up to date.'. */ if (type == GPATH_SOURCE) { strbuf_puts0(addlist, single_update); idset_add(deleteset, atoi(fid)); total++; } } else { normal_update: 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 (gpath_mtime(NULL, fid) < statp.st_mtime) { if (uflag) { printf("%s\n", path); } else { strbuf_puts0(addlist, path); total++; idset_add(deleteset, n_fid); } } } } find_close(); /* * make delete list. */ if (remove_lost) { limit = gpath_nextkey(); } else { limit = 0; } 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); } } } } gpath_close(); 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); } 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); idset_close(deleteset); idset_close(findset); return updated; }
/* * 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; }
/** * 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++]; } }
/* * updatetags: update tag file. * * i) dbpath directory in which tag file exist * i) root root directory of source tree * i) deleteset bit array of fid of deleted or modified files * i) addlist \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 (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); if (debug) fprintf(stderr, "[%s]\n", 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]); }
/** * execute_command * * @param[in] xp xargs structure * @return !=NULL: file pointer <br> * ==NULL: end of argument * * This function constructs command line from the following, <br> * @STRONG{command}: @CODE{xp-\>command} <br> * @STRONG{argument}: each argument provider <br> * execute it on a pipe line, and return the file pointer. */ static FILE * execute_command(XARGS *xp) { int limit; STRBUF *comline = strbuf_open(0); int count = 0; int length; FILE *pipe = NULL; char *p, *meta_p; #if defined(_WIN32) && !defined(__CYGWIN__) /* * If the command starts with a quote, CMD.EXE requires the entire * command line to be quoted. */ if (*xp->command == '"') strbuf_putc(comline, '"'); #endif /* * Copy the part before '%s' of the command skeleton. * The '%s' in the skeleton is replaced with given arguments. */ meta_p = locatestring(xp->command, "%s", MATCH_FIRST); if (meta_p) { strbuf_nputs(comline, xp->command, meta_p - xp->command); limit = exec_line_limit(strlen(meta_p + 2)); } else { strbuf_puts(comline, xp->command); limit = exec_line_limit(0); } /* * Append arguments as many as possible. */ switch (xp->type) { case XARGS_FILE: for ( /* initial */ fseek(xp->ip, xp->fptr, SEEK_SET) ; /* continuation condition */ (LT_MAX && ((p = (strbuf_getlen(xp->path) > 0 ? strbuf_value(xp->path) : strbuf_fgets(xp->path, xp->ip, STRBUF_NOCRLF))) != NULL)) ; /* preparation */ strbuf_reset(xp->path) ) APPEND_ARGUMENT(p); xp->fptr = ftell(xp->ip); break; case XARGS_ARGV: for (; LT_MAX && xp->argc > 0; xp->argc--, xp->argv++) APPEND_ARGUMENT(xp->argv[0]) break; case XARGS_STRBUF: for (; LT_MAX && xp->curp < xp->endp; xp->curp += length + 1) APPEND_ARGUMENT(xp->curp) break; case XARGS_FIND: for (; LT_MAX && (p = repeat_find_read()) != NULL; repeat_find_next()) APPEND_ARGUMENT(p) break; } /* * Copy the left part of the command skeleton. */ if (meta_p) { strbuf_putc(comline, ' '); strbuf_puts(comline, meta_p + 2); } #if defined(_WIN32) && !defined(__CYGWIN__) if (*xp->command == '"') strbuf_putc(comline, '"'); #endif if (count > 0) { pipe = popen(strbuf_value(comline), "r"); if (pipe == NULL) die("cannot execute command '%s'.", strbuf_value(comline)); } strbuf_close(comline); return pipe; }
/* * Make duplicate object index. * * If referred tag is only one, direct link which points the tag is generated. * Else if two or more tag exists, indirect link which points the tag list * is generated. */ int makedupindex(void) { STRBUF *sb = strbuf_open(0); STRBUF *tmp = strbuf_open(0); STRBUF *command = strbuf_open(0); int definition_count = 0; char srcdir[MAXPATHLEN]; int db; FILEOP *fileop = NULL; FILE *op = NULL; FILE *ip = NULL; snprintf(srcdir, sizeof(srcdir), "../%s", SRCS); for (db = GTAGS; db < GTAGLIM; db++) { const char *kind = kinds[db]; const char *option = options[db]; int writing = 0; int count = 0; int entry_count = 0; const char *ctags_xid, *ctags_x; char tag[IDENTLEN], prev[IDENTLEN], first_line[MAXBUFLEN]; if (gtags_exist[db] == 0) continue; prev[0] = 0; first_line[0] = 0; /* * construct command line. */ strbuf_reset(command); strbuf_sprintf(command, "%s -x%s --result=ctags-xid --encode-path=\" \t\" --nofilter=path", quote_shell(global_path), option); /* * Optimization when the --dynamic option is specified. */ if (dynamic) { strbuf_puts(command, " --nosource"); if (db != GSYMS) strbuf_puts(command, " --nofilter=sort"); } strbuf_puts(command, " \".*\""); if ((ip = popen(strbuf_value(command), "r")) == NULL) die("cannot execute command '%s'.", strbuf_value(command)); while ((ctags_xid = strbuf_fgets(sb, ip, STRBUF_NOCRLF)) != NULL) { char fid[MAXFIDLEN]; ctags_x = parse_xid(ctags_xid, fid, NULL); /* tag name */ (void)strcpy_withterm(tag, ctags_x, sizeof(tag), ' '); if (strcmp(prev, tag)) { count++; if (vflag) fprintf(stderr, " [%d] adding %s %s\n", count, kind, tag); if (writing) { if (!dynamic) { fputs_nl(gen_list_end(), op); fputs_nl(body_end, op); fputs_nl(gen_page_end(), op); close_file(fileop); html_count++; } writing = 0; /* * cache record: " <fid>\0<entry number>\0" */ strbuf_reset(tmp); strbuf_putc(tmp, ' '); strbuf_putn(tmp, count - 1); strbuf_putc(tmp, '\0'); strbuf_putn(tmp, entry_count); cache_put(db, prev, strbuf_value(tmp), strbuf_getlen(tmp) + 1); } /* single entry */ if (first_line[0]) { char fid[MAXFIDLEN]; const char *ctags_x = parse_xid(first_line, fid, NULL); const char *lno = nextelement(ctags_x); strbuf_reset(tmp); strbuf_puts_withterm(tmp, lno, ' '); strbuf_putc(tmp, '\0'); strbuf_puts(tmp, fid); cache_put(db, prev, strbuf_value(tmp), strbuf_getlen(tmp) + 1); } /* * Chop the tail of the line. It is not important. * strlimcpy(first_line, ctags_x, sizeof(first_line)); */ strncpy(first_line, ctags_xid, sizeof(first_line)); first_line[sizeof(first_line) - 1] = '\0'; strlimcpy(prev, tag, sizeof(prev)); entry_count = 0; } else { /* duplicate entry */ if (first_line[0]) { char fid[MAXFIDLEN]; const char *ctags_x = parse_xid(first_line, fid, NULL); if (!dynamic) { char path[MAXPATHLEN]; snprintf(path, sizeof(path), "%s/%s/%d.%s", distpath, dirs[db], count, HTML); fileop = open_output_file(path, cflag); op = get_descripter(fileop); fputs_nl(gen_page_begin(tag, SUBDIR), op); fputs_nl(body_begin, op); fputs_nl(gen_list_begin(), op); fputs_nl(gen_list_body(srcdir, ctags_x, fid), op); } writing = 1; entry_count++; first_line[0] = 0; } if (!dynamic) { fputs_nl(gen_list_body(srcdir, ctags_x, fid), op); } entry_count++; } } if (db == GTAGS) definition_count = count; if (pclose(ip) != 0) die("'%s' failed.", strbuf_value(command)); if (writing) { if (!dynamic) { fputs_nl(gen_list_end(), op); fputs_nl(body_end, op); fputs_nl(gen_page_end(), op); close_file(fileop); html_count++; } /* * cache record: " <fid>\0<entry number>\0" */ strbuf_reset(tmp); strbuf_putc(tmp, ' '); strbuf_putn(tmp, count); strbuf_putc(tmp, '\0'); strbuf_putn(tmp, entry_count); cache_put(db, prev, strbuf_value(tmp), strbuf_getlen(tmp) + 1); } if (first_line[0]) { char fid[MAXFIDLEN]; const char *ctags_x = parse_xid(first_line, fid, NULL); const char *lno = nextelement(ctags_x); strbuf_reset(tmp); strbuf_puts_withterm(tmp, lno, ' '); strbuf_putc(tmp, '\0'); strbuf_puts(tmp, fid); cache_put(db, prev, strbuf_value(tmp), strbuf_getlen(tmp) + 1); } } strbuf_close(sb); strbuf_close(tmp); strbuf_close(command); return definition_count; }
const char * dbop3_next(DBOP *dbop) { int rc; char *key, *dat; /* * 0: rowid * 1: key * 2: dat * 3: flags */ for (;;) { /* Once it receives SQLITE_DONE, do not never return value */ if (dbop->done) return NULL; rc = sqlite3_step(dbop->stmt); if (rc == SQLITE_DONE) goto finish; else if (rc == SQLITE_ROW) { dbop->readcount++; dbop->lastrowid = sqlite3_column_int64(dbop->stmt, 0); key = (char *)sqlite3_column_text(dbop->stmt, 1); dat = (char *)sqlite3_column_text(dbop->stmt, 2); /* skip meta records */ if (!(dbop->openflags & DBOP_RAW)) { if (dbop->ioflags & DBOP_KEY && ismeta(key)) continue; else if (ismeta(dat)) continue; } if (dbop->ioflags & DBOP_KEY) { if (!strcmp(dbop->prev, key)) continue; if (strlen(key) > MAXKEYLEN) die("primary key too long."); strlimcpy(dbop->prev, key, sizeof(dbop->prev)); } if (dbop->ioflags & DBOP_PREFIX) { if (strncmp(key, dbop->key, dbop->keylen)) goto finish; } else if (dbop->keylen) { if (strcmp(key, dbop->key)) goto finish; } if (dbop->preg && regexec(dbop->preg, key, 0, 0, 0) != 0) continue; break; } else { die("dbop3_next: something is wrong (rc = %d).", rc); } } strbuf_clear(dbop->sb); strbuf_puts0(dbop->sb, (char *)sqlite3_column_text(dbop->stmt, 2)); dbop->lastsize = strbuf_getlen(dbop->sb) - 1; dbop->lastflag = (char *)sqlite3_column_text(dbop->stmt, 3); if (dbop->lastflag) strbuf_puts(dbop->sb, dbop->lastflag); dbop->lastdat = strbuf_value(dbop->sb); if (dbop->lastflag) dbop->lastflag = dbop->lastdat + dbop->lastsize + 1; dbop->lastkey = key; dbop->lastkeysize = strlen(dbop->lastkey); if (dbop->ioflags & DBOP_KEY) { strlimcpy(dbop->prev, key, sizeof(dbop->prev)); return key; } return dbop->lastdat; finish: dbop->done = 1; dbop->lastdat = NULL; dbop->lastsize = 0; return dbop->lastdat; }
const char * dbop3_first(DBOP *dbop, const char *name, regex_t *preg, int flags) { int rc; char *key; STRBUF *sql = strbuf_open_tempbuf(); dbop->done = 0; /* This is turned on when it receives SQLITE_DONE. */ strbuf_puts(sql, "select rowid, * from "); strbuf_puts(sql, dbop->tblname); if (name) { strbuf_puts(sql, " where key "); if (dbop->ioflags & DBOP_PREFIX) { /* * In sqlite3, 'like' ignores case. 'glob' does not ignore case. */ strbuf_puts(sql, "glob '"); strbuf_puts(sql, name); strbuf_puts(sql, "*'"); } else { strbuf_puts(sql, "= '"); strbuf_puts(sql, name); strbuf_puts(sql, "'"); } strlimcpy(dbop->key, name, sizeof(dbop->key)); dbop->keylen = strlen(name); } strbuf_puts(sql, " order by key"); if (dbop->stmt) { rc = sqlite3_finalize(dbop->stmt); if (rc != SQLITE_OK) die("dbop3_finalize failed. (rc = %d)", rc); dbop->stmt = NULL; } rc = sqlite3_prepare_v2(dbop->db3, strbuf_value(sql), -1, &dbop->stmt, NULL); if (rc != SQLITE_OK) die("dbop3_first: sqlite3_prepare_v2 failed. (rc = %d)", rc); /* * 0: rowid * 1: key * 2: dat * 3: flags */ for (;;) { /* Once it receives SQLITE_DONE, do not never return value */ if (dbop->done) return NULL; rc = sqlite3_step(dbop->stmt); if (rc == SQLITE_DONE) goto finish; else if (rc == SQLITE_ROW) { dbop->readcount++; dbop->lastrowid = sqlite3_column_int64(dbop->stmt, 0); key = (char *)sqlite3_column_text(dbop->stmt, 1); if (name) { if (dbop->ioflags & DBOP_PREFIX) { if (strncmp(key, dbop->key, dbop->keylen)) goto finish; } else { if (strcmp(key, dbop->key)) goto finish; } if (dbop->preg && regexec(dbop->preg, key, 0, 0, 0) != 0) continue; } else { /* skip meta records */ if (ismeta(key) && !(dbop->openflags & DBOP_RAW)) continue; if (dbop->preg && regexec(dbop->preg, key, 0, 0, 0) != 0) continue; } break; } else { die("dbop3_first: something is wrong (rc = %d).", rc); } } strbuf_clear(dbop->sb); strbuf_puts0(dbop->sb, (char *)sqlite3_column_text(dbop->stmt, 2)); dbop->lastsize = strbuf_getlen(dbop->sb) - 1; dbop->lastflag = (char *)sqlite3_column_text(dbop->stmt, 3); if (dbop->lastflag) strbuf_puts(dbop->sb, dbop->lastflag); dbop->lastdat = strbuf_value(dbop->sb); if (dbop->lastflag) dbop->lastflag = dbop->lastdat + dbop->lastsize + 1; dbop->lastkey = key; dbop->lastkeysize = strlen(dbop->lastkey); strbuf_release_tempbuf(sql); if (flags & DBOP_KEY) { strlimcpy(dbop->prev, key, sizeof(dbop->prev)); return key; } return dbop->lastdat; finish: strbuf_release_tempbuf(sql); dbop->done = 1; dbop->lastdat = NULL; dbop->lastsize = 0; dbop->lastflag = NULL; return dbop->lastdat; }
/** * 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); } }
/* * Execute retrieval command. * * i) command 0: Find this C symbol * 1: Find this definition * 2: Find functions called by this function (not supported) * 3: Find functions calling this function * 4: Find this text string * 6: Find this egrep pattern * 7: Find this file * 8: Find files #including this file * i) arg argument * * Unsupported command prints "cscope: 0 lines\n". */ static void search(int com, char *arg) { static STRBUF *sb; char buf[1024], *p; int count = 0, opt = 0; if (sb == NULL) sb = strbuf_open(1024 * 1024); else strbuf_reset(sb); /* * Convert from cscope command to global command. */ switch (com) { case '0': /* Find this C symbol */ case '1': /* Find this definition */ break; case '2': /* Find functions called by this function */ /* * <symbol>:<line number>:<path> */ for (p = arg; *p && *p != ':'; p++) ; *p++ = '\0'; context = p; opt = FROM_HERE; break; case '3': /* Find functions calling this function */ opt = 'r'; break; case '4': /* Find this text string */ opt = 'g'; strlimcpy(buf, quote_string(arg), sizeof(buf)); arg = buf; break; case '5': /* Change this text string */ opt = NA; break; case '6': /* Find this egrep pattern */ opt = 'g'; break; case '7': /* Find this file */ opt = 'P'; break; case '8': /* Find files #including this file */ opt = 'g'; arg = include_pattern(arg); break; } /* * Execute global(1). */ if (opt == NA) { fprintf(stdout, "cscope: 0 lines\n"); return; } if (com == '0') { count += execute_command(sb, com, 0, arg); count += execute_command(sb, com, (count > 0) ? 'r' : 's', arg); } else { count += execute_command(sb, com, opt, arg); } /* * Output format: * cscope: <n> lines * ****************** ... 1 * ****************** ... 2 * ... * ****************** ... n * * Example: * cscope: 3 lines * global/global.c main 158 main(int argc, char **argv) * gozilla/gozilla.c main 155 main(int argc, char **argv) * gscope/gscope.c main 108 main(int argc, char **argv) */ fprintf(stdout, "cscope: %d lines\n", count); if (count > 0) fwrite(strbuf_value(sb), 1, strbuf_getlen(sb), stdout); fflush(stdout); }