Index: gtags/gtags.c
===================================================================
RCS file: /cvsroot/global/global/gtags/gtags.c,v
retrieving revision 1.131
diff -u -r1.131 gtags.c
--- gtags/gtags.c	19 Aug 2005 05:08:14 -0000	1.131
+++ gtags/gtags.c	19 Aug 2005 06:35:46 -0000
@@ -60,7 +60,6 @@
 static void help(void);
 void signal_setup(void);
 void onintr(int);
-int match(const char *, const char *);
 int main(int, char **);
 int incremental(const char *, const char *);
 void updatetags(const char *, const char *, IDSET *, STRBUF *, int, int);
@@ -82,7 +81,6 @@
 int show_config;
 int do_convert;
 int do_find;
-int do_sort;
 int do_relative;
 int do_absolute;
 int cxref;
@@ -138,7 +136,6 @@
 	{"other", no_argument, &other_files, 1},
 	{"relative", no_argument, &do_relative, 1},
 	{"secure", no_argument, &secure_mode, 1},
-	{"sort", no_argument, &do_sort, 1},
 	{"version", no_argument, &show_version, 1},
 	{"help", no_argument, &show_help, 1},
 	{ 0 }
@@ -173,21 +170,6 @@
 }
 
 int
-match(curtag, line)
-	const char *curtag;
-	const char *line;
-{
-	const char *p, *q = line;
-
-	for (p = curtag; *p; p++)
-		if (*p != *q++)
-			return 0;
-	if (!isspace((unsigned char)*q))
-		return 0;
-	return 1;
-}
-
-int
 main(argc, argv)
 	int argc;
 	char *argv[];
@@ -416,48 +398,6 @@
 			fputc('\n', stdout);
 		}
 		vfind_close();
-		exit(0);
-	} else if (do_sort) {
-		/*
-		 * This code and the makedupindex() in htags(1) compose
-		 * a pipeline 'global -x ".*" | gtags --sort'.
-		 * The 'gtags --sort' is equivalent with 'sort -k 1,1 -k 3,3 -k 2,2n -u'
-		 * but the latter is ineffective and needs a lot of temporary
-		 * files when applied to a huge file. (According to circumstances,
-		 * hundreds of files are generated.)
-		 *
-		 * Utilizing the feature that the output of 'global -x ".*"'
-		 * is already sorted in alphabetical order by tag name,
-		 * we splited the output into relatively small unit and
-		 * execute sort for each unit.
-		 *
-		 * It is not certain whether the present unit value is the best.
-		 */
-		int unit = 1500;
-		STRBUF *ib = strbuf_open(MAXBUFLEN);
-		const char *ctags_x = strbuf_fgets(ib, stdin, 0);
-
-		while (ctags_x != NULL) {
-			int count = 0;
-			FILE *op = popen("gnusort -k 1,1 -k 3,3 -k 2,2n -u", "w");
-			do {
-				fputs(ctags_x, op);
-			} while ((ctags_x = strbuf_fgets(ib, stdin, 0)) != NULL && ++count < unit);
-			if (ctags_x) {
-				/* curtag = current tag name */
-				STRBUF *curtag = strbuf_open(0);
-				const char *p = ctags_x;
-				while (!isspace((unsigned char)*p))
-					strbuf_putc(curtag, *p++);
-				/* read until next tag name */
-				do {
-					fputs(ctags_x, op);
-				} while ((ctags_x = strbuf_fgets(ib, stdin, 0)) != NULL
-					&& match(strbuf_value(curtag), ctags_x));
-			}
-			if (pclose(op) != 0)
-				die("terminated abnormally.");
-		}
 		exit(0);
 	} else if (do_relative || do_absolute) {
 		/*
Index: htags/anchor.c
===================================================================
RCS file: /cvsroot/global/global/htags/anchor.c,v
retrieving revision 1.16
diff -u -r1.16 anchor.c
--- htags/anchor.c	10 May 2005 05:17:52 -0000	1.16
+++ htags/anchor.c	19 Aug 2005 06:35:46 -0000
@@ -34,6 +34,15 @@
 #include "anchor.h"
 #include "htags.h"
 
+struct anchor_input {
+	STRBUF *command;
+	FILE *ip;
+	STRBUF *ib;
+	char *ctags_x;
+	SPLIT ptable;
+};
+
+static struct anchor_input anchor_input[GTAGLIM - GTAGS];
 static struct anchor *table;
 static VARRAY *vb;
 
@@ -57,6 +66,88 @@
 static struct anchor *CURRENTDEF;
 
 /*
+ * anchor_pathlist_limit: determine the maximum length of the list of paths.
+ */
+int
+anchor_pathlist_limit()
+{
+	int limit, db, max = 0;
+	STRBUF *comline = strbuf_open(0);
+
+	limit = exec_line_limit();
+	for (db = GTAGS; db < GTAGLIM; db++) {
+		if (!symbol && db == GSYMS)
+			continue;
+		strbuf_reset(comline);
+		if (!getconfs(dbname(db), comline))
+			die("cannot get parser for %s.", dbname(db));
+		if (strbuf_getlen(comline) > max)
+			max = strbuf_getlen(comline);
+	}
+	strbuf_close(comline);
+	limit -= max + 40;
+	if (limit < 0)
+		limit = 0;
+	return limit;
+}
+/*
+ * anchor_prepare: setup input stream.
+ *
+ *	i)	path_list	\0 separated list of paths
+ */
+void
+anchor_prepare(path_list)
+	STRBUF *path_list;
+{
+	int db;
+	struct anchor_input *input;
+	STRBUF *comline = strbuf_open(0);
+
+	for (db = GTAGS; db < GTAGLIM; db++) {
+		if (!symbol && db == GSYMS)
+			continue;
+		input = &anchor_input[db - GTAGS];
+		input->command = strbuf_open(0);
+		input->ib = strbuf_open(MAXBUFLEN);
+		strbuf_reset(comline);
+		if (!getconfs(dbname(db), comline))
+			die("cannot get parser for %s.", dbname(db));
+		makecommand(strbuf_value(comline), path_list, input->command);
+		/*
+		 * We assume that the output of gtags-parser is sorted by the path.
+		 * About the other parsers, it is not guaranteed, so we sort it
+		 * using external sort command (gnusort).
+		 */
+		if (locatestring(strbuf_value(comline), "gtags-parser", MATCH_FIRST) == NULL)
+			strbuf_puts(input->command, "| gnusort -k 3,3");
+		input->ip = popen(strbuf_value(input->command), "r");
+		if (input->ip == NULL)
+			die("cannot execute command '%s'.", strbuf_value(input->command));
+		if (input->ctags_x != NULL) {
+			recover(&input->ptable);
+			die("The output of parser is illegal.\n%s", input->ctags_x);
+		}
+		input->ctags_x = strbuf_fgets(input->ib, input->ip, STRBUF_NOCRLF);
+		if (input->ctags_x == NULL) {
+			if (pclose(input->ip) != 0)
+				die("command '%s' failed.", strbuf_value(input->command));
+			strbuf_close(input->ib);
+			strbuf_close(input->command);
+			continue;
+		}
+		if (split(input->ctags_x, 4, &input->ptable) < 4) {
+			recover(&input->ptable);
+			die("too small number of parts in anchor_prepare().\n'%s'", input->ctags_x);
+		}
+		if (input->ptable.part[PART_PATH].start[0] != '.'
+		 || input->ptable.part[PART_PATH].start[1] != '/') {
+			recover(&input->ptable);
+			die("The output of parser is illegal.\n%s", input->ctags_x);
+		}
+	}
+	strbuf_close(comline);
+}
+/*
  * anchor_load: load anchor table
  *
  *	i)	file	file name
@@ -65,10 +156,7 @@
 anchor_load(file)
 	const char *file;
 {
-	char command[MAXFILLEN];
-	const char *options[] = {NULL, "", "r", "s"};
-	STRBUF *sb = strbuf_open(0);
-	FILE *ip;
+	struct anchor_input *input;
 	int i, db;
 
 	FIRST = LAST = 0;
@@ -80,34 +168,26 @@
 		varray_reset(vb);
 
 	for (db = GTAGS; db < GTAGLIM; db++) {
-		char *ctags_x;
-
 		if (!symbol && db == GSYMS)
 			continue;
+		input = &anchor_input[db - GTAGS];
 		/*
-		 * Setup input stream.
+		 * Read from input stream until it reaches end of file
+		 * or the line of another file appears.
 		 */
-		snprintf(command, sizeof(command), "global -fn%s \"%s\"", options[db], file);
-		ip = popen(command, "r");
-		if (ip == NULL)
-			die("cannot execute command '%s'.", command);
-		while ((ctags_x = strbuf_fgets(sb, ip, STRBUF_NOCRLF)) != NULL) {
-			SPLIT ptable;
+		while (input->ctags_x != NULL
+		       && strcmp(input->ptable.part[PART_PATH].start + 2, file) == 0) {
 			struct anchor *a;
 			int type;
 
-			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) {
 				const char *p;
 
-				for (p = ptable.part[PART_LINE].start; *p && isspace((unsigned char)*p); p++)
+				for (p = input->ptable.part[PART_LINE].start; *p && isspace((unsigned char)*p); p++)
 					;
 				if (!*p) {
-					recover(&ptable);
-					die("The output of global is illegal.\n%s", p);
+					recover(&input->ptable);
+					die("The output of parser is illegal.\n%s", input->ctags_x);
 				}
 				type = 'D';
 				if (*p == '#') {
@@ -127,16 +207,30 @@
 				type = 'Y';
 			/* allocate an entry */
 			a = varray_append(vb);
-			a->lineno = atoi(ptable.part[PART_LNO].start);
+			a->lineno = atoi(input->ptable.part[PART_LNO].start);
 			a->type = type;
 			a->done = 0;
-			settag(a, ptable.part[PART_TAG].start);
-			recover(&ptable);
+			settag(a, input->ptable.part[PART_TAG].start);
+			recover(&input->ptable);
+			input->ctags_x = strbuf_fgets(input->ib, input->ip, STRBUF_NOCRLF);
+			if (input->ctags_x == NULL) {
+				if (pclose(input->ip) != 0)
+					die("command '%s' failed.", strbuf_value(input->command));
+				strbuf_close(input->ib);
+				strbuf_close(input->command);
+				break;
+			}
+			if (split(input->ctags_x, 4, &input->ptable) < 4) {
+				recover(&input->ptable);
+				die("too small number of parts in anchor_load().\n'%s'", input->ctags_x);
+			}
+			if (input->ptable.part[PART_PATH].start[0] != '.'
+			 || input->ptable.part[PART_PATH].start[1] != '/') {
+				recover(&input->ptable);
+				die("The output of parser is illegal.\n%s", input->ctags_x);
+			}
 		}
-		if (pclose(ip) != 0)
-			die("command '%s' failed.", command);
 	}
-	strbuf_close(sb);
 	if (vb->length == 0) {
 		table = NULL;
 	} else {
Index: htags/anchor.h
===================================================================
RCS file: /cvsroot/global/global/htags/anchor.h,v
retrieving revision 1.9
diff -u -r1.9 anchor.h
--- htags/anchor.h	10 May 2005 05:17:52 -0000	1.9
+++ htags/anchor.h	19 Aug 2005 06:35:46 -0000
@@ -65,6 +65,8 @@
 #define A_HELP		7
 #define A_LIMIT		8
 
+int anchor_pathlist_limit(void);
+void anchor_prepare(STRBUF *);
 void anchor_load(const char *);
 void anchor_unload(void);
 struct anchor *anchor_first();
Index: htags/dupindex.c
===================================================================
RCS file: /cvsroot/global/global/htags/dupindex.c,v
retrieving revision 1.22
diff -u -r1.22 dupindex.c
--- htags/dupindex.c	18 Aug 2005 15:06:27 -0000	1.22
+++ htags/dupindex.c	19 Aug 2005 06:35:46 -0000
@@ -22,6 +22,7 @@
 #include <config.h>
 #endif
 #include <stdio.h>
+#include <stdlib.h>
 #ifdef HAVE_STRING_H
 #include <string.h>
 #else
@@ -42,6 +43,14 @@
 static const char *options[] = {NULL, "",           "r",         "s"};
 
 static char command[MAXFILLEN];
+static char srcdir[MAXPATHLEN];
+
+struct dup_entry {
+	int offset;
+	SPLIT ptable;
+	int lineno;
+	int skip;
+};
 
 /*
  * Open duplicate object index file.
@@ -87,6 +96,114 @@
 	}
 }
 /*
+ * put_single: put single entry to tag cache.
+ *
+ *	i)	db	GTAGS,GRTAGS,GSYMS
+ *	i)	ctags_x	ctags -x image
+ */
+static void
+put_single(db, ctags_x)
+	int db;
+	char *ctags_x;
+{
+	SPLIT ptable;
+	char buf[1024];
+
+	if (split(ctags_x, 4, &ptable) < 4) {
+		recover(&ptable);
+		die("too small number of parts in put_single().\n'%s'", ctags_x);
+	}
+	snprintf(buf, sizeof(buf), "%s %s", ptable.part[PART_LNO].start, ptable.part[PART_PATH].start);
+	cache_put(db, ptable.part[PART_TAG].start, buf);
+	recover(&ptable);
+}
+/*
+ * compare_dup_entry: compare function for sorting.
+ */
+static int
+compare_dup_entry(v1, v2)
+	const void *v1;
+	const void *v2;
+{
+	const struct dup_entry *e1 = v1, *e2 = v2;
+	int ret;
+
+	if ((ret = strcmp(e1->ptable.part[PART_PATH].start,
+			  e2->ptable.part[PART_PATH].start)) != 0)
+		return ret;
+	return e1->lineno - e2->lineno;
+}
+/*
+ * put_duplicated: 
+ *
+ *	i)	db	GTAGS,GRTAGS,GSYMS
+ *	i)	tag	tag name
+ *	i)	file_id	id of duplicate object
+ *	i)	lines	\0 separated ctags -x images
+ *	i)	entries	array of duplicate entries
+ *	i)	entry_count	number of duplicate entries
+ */
+static void
+put_duplicated(db, tag, file_id, lines, entries, entry_count)
+	int db;
+	const char *tag;
+	int file_id;
+	char *lines;
+	struct dup_entry *entries;
+	int entry_count;
+{
+	int i;
+	char buf[1024];
+	int skipped_lines = 0;
+
+	if (!dynamic || db == GSYMS) {
+		for (i = 0; i < entry_count; i++) {
+			char *ctags_x = lines + entries[i].offset;
+			SPLIT *ptable = &entries[i].ptable;
+
+			if (split(ctags_x, 4, ptable) < 4) {
+				recover(ptable);
+				die("too small number of parts in put_duplicated().\n'%s'", ctags_x);
+			}
+			entries[i].lineno = atoi(ptable->part[PART_LNO].start);
+			entries[i].skip = 0;
+		}
+		qsort(entries, entry_count, sizeof(struct dup_entry), compare_dup_entry); 
+		for (i = 1; i < entry_count; i++) {
+			if (entries[i].lineno == entries[i - 1].lineno
+			 && strcmp(entries[i].ptable.part[PART_PATH].start,
+				   entries[i - 1].ptable.part[PART_PATH].start) == 0) {
+				entries[i].skip = 1;
+				skipped_lines++;
+			}
+		}
+		for (i = 0; i < entry_count; i++)
+			recover(&entries[i].ptable);
+	}
+	if (!dynamic) {
+		FILE *op = open_dup_file(db, file_id);
+
+		fputs_nl(gen_page_begin(tag, SUBDIR), op);
+		fputs_nl(body_begin, op);
+		fputs_nl(gen_list_begin(), op);
+		for (i = 0; i < entry_count; i++) {
+			if (entries[i].skip)
+				continue;
+			fputs_nl(gen_list_body(srcdir, lines + entries[i].offset), op);
+		}
+		fputs_nl(gen_list_end(), op);
+		fputs_nl(body_end, op);
+		fputs_nl(gen_page_end(), op);
+		close_dup_file(op);
+		file_count++;
+	}
+	/*
+	 * cache record: " <file id> <entry number>"
+	 */
+	snprintf(buf, sizeof(buf), " %d %d", file_id, entry_count - skipped_lines);
+	cache_put(db, tag, buf);
+}
+/*
  * Make duplicate object index.
  *
  * If referred tag is only one, direct link which points the tag is generated.
@@ -96,129 +213,70 @@
 int
 makedupindex(void)
 {
-	STRBUF *sb = strbuf_open(0);
+	STRBUF *ib = strbuf_open(MAXBUFLEN);
+	STRBUF *sb = strbuf_open(MAXBUFLEN);
+	VARRAY *vb = varray_open(sizeof(struct dup_entry), 100);
 	int definition_count = 0;
-	char srcdir[MAXPATHLEN];
 	int db;
-	char buf[1024];
-	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;
-		char *ctags_x, tag[IDENTLEN], prev[IDENTLEN], first_line[MAXBUFLEN], command[MAXFILLEN];
+		char *ctags_x, prev[IDENTLEN], command[MAXFILLEN];
+		FILE *ip;
 
 		if (!symbol && db == GSYMS)
 			continue;
-		prev[0] = 0;
-		first_line[0] = 0;
-		snprintf(command, sizeof(command), "global -xn%s%s \".*\"%s",
-			 dynamic ? "n" : "", option,
-			 (!dynamic || db == GSYMS) ? " | gtags --sort" : "");
+		prev[0] = '\0';
+		snprintf(command, sizeof(command), "global -xn%s%s \".*\"", dynamic ? "n" : "", option);
 		if ((ip = popen(command, "r")) == NULL)
 			die("cannot execute command '%s'.", command);
-		while ((ctags_x = strbuf_fgets(sb, ip, STRBUF_NOCRLF)) != NULL) {
+		while ((ctags_x = strbuf_fgets(ib, ip, STRBUF_NOCRLF)) != NULL) {
+			char *tag;
+			struct dup_entry *entry;
 			SPLIT ptable;
 
 			if (split(ctags_x, 2, &ptable) < 2) {
 				recover(&ptable);
-				die("too small number of parts.(1)\n'%s'", ctags_x);
+				die("too small number of parts in makedupindex().\n'%s'", ctags_x);
 			}
-			strlimcpy(tag, ptable.part[PART_TAG].start, sizeof(tag));
-			recover(&ptable);
-
-			if (strcmp(prev, tag)) {
+			tag = ptable.part[PART_TAG].start;
+			if (prev[0] == '\0' || strcmp(prev, tag) != 0) {
+				if (prev[0] != '\0') {
+					if (vb->length == 1)
+						put_single(db, strbuf_value(sb));
+					else 
+						put_duplicated(db, prev, count, strbuf_value(sb),
+							varray_assign(vb, 0, 0), vb->length);
+				}
 				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_dup_file(op);
-						file_count++;
-					}
-					writing = 0;
-					/*
-					 * cache record: " <file id> <entry number>"
-					 */
-					snprintf(buf, sizeof(buf), " %d %d", count - 1, entry_count);
-					cache_put(db, prev, buf);
-				}				
-				/* single entry */
-				if (first_line[0]) {
-					if (split(first_line, 4, &ptable) < 4) {
-						recover(&ptable);
-						die("too small number of parts.(2)\n'%s'", ctags_x);
-					}
-					snprintf(buf, sizeof(buf), "%s %s", ptable.part[PART_LNO].start, ptable.part[PART_PATH].start);
-					cache_put(db, prev, buf);
-					recover(&ptable);
-				}
-				/*
-				 * Chop the tail of the line. It is not important.
-				 * strlimcpy(first_line, ctags_x, sizeof(first_line));
-				 */
-				strncpy(first_line, ctags_x, 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]) {
-					if (!dynamic) {
-						op = open_dup_file(db, count);
-						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, first_line), op);
-					}
-					writing = 1;
-					entry_count++;
-					first_line[0] = 0;
-				}
-				if (!dynamic) {
-					fputs_nl(gen_list_body(srcdir, ctags_x), op);
-				}
-				entry_count++;
+				strbuf_reset(sb);
+				varray_reset(vb);
 			}
+			entry = varray_append(vb);
+			entry->offset = strbuf_getlen(sb);
+			recover(&ptable);
+			strbuf_puts0(sb, ctags_x);
 		}
 		if (db == GTAGS)
 			definition_count = count;
 		if (pclose(ip) != 0)
 			die("'%s' failed.", command);
-		if (writing) {
-			if (!dynamic) {
-				fputs_nl(gen_list_end(), op);
-				fputs_nl(body_end, op);
-				fputs_nl(gen_page_end(), op);
-				close_dup_file(op);
-				file_count++;
-			}
-			/*
-			 * cache record: " <file id> <entry number>"
-			 */
-			snprintf(buf, sizeof(buf), " %d %d", count, entry_count);
-			cache_put(db, prev, buf);
-		}
-		if (first_line[0]) {
-			SPLIT ptable;
-
-			if (split(first_line, 4, &ptable) < 4) {
-				recover(&ptable);
-				die("too small number of parts.(3)\n'%s'", ctags_x);
-			}
-			snprintf(buf, sizeof(buf), "%s %s", ptable.part[PART_LNO].start, ptable.part[PART_PATH].start);
-			cache_put(db, prev, buf);
-			recover(&ptable);
+		if (prev[0] != '\0') {
+			if (vb->length == 1)
+				put_single(db, strbuf_value(sb));
+			else 
+				put_duplicated(db, prev, count, strbuf_value(sb),
+					varray_assign(vb, 0, 0), vb->length);
 		}
 	}
+	strbuf_close(ib);
 	strbuf_close(sb);
+	varray_close(vb);
 	return definition_count;
 }
Index: htags/htags.c
===================================================================
RCS file: /cvsroot/global/global/htags/htags.c,v
retrieving revision 1.66
diff -u -r1.66 htags.c
--- htags/htags.c	16 May 2005 13:48:31 -0000	1.66
+++ htags/htags.c	19 Aug 2005 06:35:46 -0000
@@ -43,6 +43,7 @@
 #include "getopt.h"
 #include "regex.h"
 #include "global.h"
+#include "anchor.h"
 #include "cache.h"
 #include "common.h"
 #include "htags.h"
@@ -754,6 +755,32 @@
 	fclose(op);
 }
 /*
+ * Wrapper of src2html()
+ */
+static int
+do_src2html(path_list, count, total)
+	STRBUF *path_list;
+	int count;
+	int total;
+{
+	int notsource;
+	char path[MAXPATHLEN];
+	const char *_ = strbuf_value(path_list);
+	const char *end = _ + strbuf_getlen(path_list);
+	const char *p;
+
+	while (_ < end) {
+		notsource = *_++;
+		count++;
+		message(" [%d/%d] converting %s", count, total, _);
+		p = path2fid(_);
+		snprintf(path, sizeof(path), "%s/%s/%s.%s", distpath, SRCS, p, HTML);
+		src2html(_, path, notsource);
+		_ += strlen(_) + 1;
+	}
+	return count;
+}
+/*
  * makehtml: make html files
  *
  *	i)	total	number of files.
@@ -765,8 +792,11 @@
 	FILE *ip;
 	const char *_;
 	int count = 0;
-	char command[MAXFILLEN], path[MAXPATHLEN];
+	char command[MAXFILLEN];
 	STRBUF *sb = strbuf_open(0);
+	STRBUF *alllist = strbuf_open(0);
+	STRBUF *srclist = strbuf_open(0);
+	int path_list_max = anchor_pathlist_limit();
 
 	if (other_files && !dynamic)
 		snprintf(command, sizeof(command), "%s --other | gnusort -t / -k 2", findcom);
@@ -777,7 +807,6 @@
 		die("cannot execute command '%s'.", command);
 	while ((_ = strbuf_fgets(sb, ip, STRBUF_NOCRLF)) != NULL) {
 		int notsource = 0;
-		const char *p;
 
 		if (*_ == ' ') {
 			if (!other_files)
@@ -789,18 +818,40 @@
 				continue;
 			}
 			notsource = 1;
+		} else {
+			/*
+			 * Execute parser when path name collects enough.
+			 * Though the path_list is \0 separated list of string,
+			 * we can think its length equals to the length of
+			 * argument string because each \0 can be replaced
+			 * with a blank.
+			 */
+			if (strbuf_getlen(srclist)) {
+				if (strbuf_getlen(srclist) + strlen(_) > path_list_max) {
+					anchor_prepare(srclist);
+					count = do_src2html(alllist, count, total);
+					strbuf_reset(srclist);
+					strbuf_reset(alllist);
+				}
+			}
+			/*
+			 * Add a path to the path list.
+			 */
+			strbuf_puts0(srclist, _);
 		}
-		count++;
-		_ += 2;
-		p = _;
-		message(" [%d/%d] converting %s", count, total, p);
-		p = path2fid(p);
-		snprintf(path, sizeof(path), "%s/%s/%s.%s", distpath, SRCS, p, HTML);
-		src2html(_, path, notsource);
+		strbuf_putc(alllist, notsource);
+		strbuf_puts0(alllist, _ + 2);
+	}
+	if (strbuf_getlen(alllist)) {
+		if (strbuf_getlen(srclist))
+			anchor_prepare(srclist);
+		do_src2html(alllist, count, total);
 	}
 	if (pclose(ip) != 0)
 		die("cannot traverse directory.(%s)", command);
 	strbuf_close(sb);
+	strbuf_close(alllist);
+	strbuf_close(srclist);
 }
 /*
  * copy file.
Index: htags/lexcommon.h
===================================================================
RCS file: /cvsroot/global/global/htags/lexcommon.h,v
retrieving revision 1.14
diff -u -r1.14 lexcommon.h
--- htags/lexcommon.h	10 May 2005 05:17:52 -0000	1.14
+++ htags/lexcommon.h	19 Aug 2005 06:35:46 -0000
@@ -56,6 +56,39 @@
  */
 static int newline_terminate_string = 0;
 
+/*
+ * Convert tabs to spaces.
+ */
+static int dest_column;
+static int left_spaces;
+
+#define YY_INPUT(buf, result, max_size) do {				\
+	int n = 0;							\
+	while (n < max_size) {						\
+		int c;							\
+		if (left_spaces > 0) {					\
+			left_spaces--;					\
+			c = ' ';					\
+		} else {						\
+			c = getc(LEXIN);				\
+			if (c == EOF) {					\
+				if (ferror(LEXIN))			\
+					die("read error.");		\
+				break;					\
+			}						\
+			if (c == '\t') {				\
+				left_spaces = tabs - dest_column % tabs;\
+				continue;				\
+			}						\
+		}							\
+		buf[n++] = c;						\
+		dest_column++;						\
+		if (c == '\n')						\
+			dest_column = 0;				\
+	}								\
+	result = n;							\
+} while (0)
+
 #define LINENO lexcommon_lineno
 
 #define DEFAULT_BEGIN_OF_FILE_ACTION {					\
@@ -63,6 +96,8 @@
         LEXRESTART(LEXIN);						\
         LINENO = 1;							\
         begin_line = 1;							\
+	dest_column = 0;						\
+	left_spaces = 0;						\
 }
 
 #define DEFAULT_YY_USER_ACTION {					\
Index: htags/src2html.c
===================================================================
RCS file: /cvsroot/global/global/htags/src2html.c,v
retrieving revision 1.40
diff -u -r1.40 src2html.c
--- htags/src2html.c	11 May 2005 01:50:12 -0000	1.40
+++ htags/src2html.c	19 Aug 2005 06:35:47 -0000
@@ -133,13 +133,11 @@
 open_input_file(file)
 	const char *file;
 {
-	char command[MAXFILLEN];
 	FILE *ip;
 
-	snprintf(command, sizeof(command), "gtags --expand -%d < %s", tabs, file);
-	ip = popen(command, "r");
+	ip = fopen(file, "r");
 	if (!ip)
-		die("cannot execute '%s'.", command);
+		die("cannot open file '%s'.", file);
 	curpfile = file;
 	warned = 0;
 	return ip;
@@ -151,8 +149,7 @@
 close_input_file(ip)
 	FILE *ip;
 {
-	if (pclose(ip) != 0)
-		die("command 'gtags --expand -%d' failed.", tabs);
+	fclose(ip);
 }
 /*
  * Open HTML file.
@@ -756,18 +753,27 @@
 		fputs_nl(verbatim_begin, out);
 		last_lineno = 0;
 		while ((_ = strbuf_fgets(sb, in, STRBUF_NOCRLF)) != NULL) {
+			int dst = 0;
+
 			fputs(gen_name_number(++last_lineno), out);
 			for (; *_; _++) {
 				int c = *_;
 
-				if (c == '&')
-					fputs(quote_amp, out);
-				else if (c == '<')
-					fputs(quote_little, out);
-				else if (c == '>')
-					fputs(quote_great, out);
-				else
-					fputc(c, out);
+				if (c == '\t') {
+					do {
+						putc(' ', out);
+					} while (++dst % tabs);
+				} else {
+					if (c == '&')
+						fputs(quote_amp, out);
+					else if (c == '<')
+						fputs(quote_little, out);
+					else if (c == '>')
+						fputs(quote_great, out);
+					else
+						fputc(c, out);
+					dst++;
+				}
 			}
 			fputc('\n', out);
 		}
