Example #1
0
/**
 * 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]);
}
Example #2
0
/**
 * 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;
}
Example #3
0
/**
 * 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;
}
Example #4
0
/**
 * 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 = &reg;
	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 &gtop->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 ? &gtop->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  &gtop->gtp_array[gtop->gtp_index++];
	}
}