Index: libutil/gtagsop.c
===================================================================
RCS file: /cvsroot/global/global/libutil/gtagsop.c,v
retrieving revision 1.70
diff -u -r1.70 gtagsop.c
--- libutil/gtagsop.c	6 Jul 2005 12:01:16 -0000	1.70
+++ libutil/gtagsop.c	14 Jul 2005 12:08:16 -0000
@@ -27,6 +27,7 @@
 #include <stdio.h>
 #ifdef STDC_HEADERS
 #include <stdlib.h>
+#include <stddef.h>
 #endif
 #ifdef HAVE_STRING_H
 #include <string.h>
@@ -53,6 +54,11 @@
 #include "strmake.h"
 #include "tab.h"
 
+#define HASHBUCKETS	256
+
+static unsigned int hashfunc(const char *);
+static int compare_lno(const void *, const void *);
+static void gtop_flush_htab(GTOP *);
 static const char *unpack_pathindex(const char *);
 static const char *genrecord(GTOP *);
 static regex_t reg;
@@ -387,6 +393,37 @@
 	line = ptable.part[1].start;
 	path = ptable.part[2].start;
 	/*
+	 * If gtags-parser is used, register each record into the hash table.
+	 * duplicated records will be combined.
+	 */
+	if (gtop->htab) {
+		struct gtop_compact_entry *entry, **prev;
+		int *lno;
+
+		if (gtop->prev_path[0] && strcmp(gtop->prev_path, path))
+			gtop_flush_htab(gtop);
+		strlimcpy(gtop->prev_path, path, sizeof(gtop->prev_path));
+		for (prev = gtop->htab + hashfunc(tag) % HASHBUCKETS;
+		     (entry = *prev) != NULL;
+		     prev = &entry->next) {
+			if (strcmp(entry->tag, tag) == 0)
+				break;
+		}
+		if (entry == NULL) {
+			entry = malloc(offsetof(struct gtop_compact_entry, tag) + strlen(tag) + 1);
+			if (entry == NULL)
+				die("short of memory.");
+			entry->next = NULL;
+			entry->vb = varray_open(sizeof(int), 100);
+			strcpy(entry->tag, tag);
+			*prev = entry;
+		}
+		lno = varray_append(entry->vb);
+		*lno = atoi(line);
+		recover(&ptable);
+		return;
+	}
+	/*
 	 * First time, it occurs, because 'prev_tag' and 'prev_path' are NULL.
 	 */
 	if (strcmp(gtop->prev_tag, tag) || strcmp(gtop->prev_path, path)) {
@@ -419,6 +456,88 @@
 	recover(&ptable);
 }
 /*
+ * hashfunc: calculate hash value for given string.
+ *
+ *	i)	string
+ *	r)	hash value
+ */
+static unsigned int
+hashfunc(const char *s)
+{
+	unsigned int h, g;
+
+	h = strlen(s);
+	while (*s != '\0') {
+		h <<= 4;
+		h += (unsigned char)*s++;
+		g = h & 0xf0000000;
+		if (g != 0)
+			h = h ^ (g >> 24) ^ g;
+	}
+
+	return h;
+}
+/*
+ * compare_lno: compare function for sorting line number.
+ */
+static int
+compare_lno(s1, s2)
+	const void *s1;
+	const void *s2;
+{
+	return *(const int *)s1 - *(const int *)s2;
+}
+/*
+ * gtop_flush_htab: register each record into the tag database and delete from the hash table.
+ *
+ *	i)	gtop	descripter of GTOP
+ */
+static void
+gtop_flush_htab(GTOP *gtop)
+{
+	const char *path;
+	struct gtop_compact_entry *entry, *next;
+	int *lno_array;
+	int i, j, savelen;
+
+	path = gtop->prev_path;
+	if (gtop->format & GTAGS_PATHINDEX) {
+		path = gpath_path2fid(gtop->prev_path);
+		if (path == NULL)
+			die("GPATH is corrupted.('%s' not found)", gtop->prev_path);
+	}
+
+	for (i = 0; i < HASHBUCKETS; i++) {
+		for (entry = gtop->htab[i]; entry != NULL; entry = next) {
+			lno_array = varray_assign(entry->vb, 0, 0);
+			qsort(lno_array, entry->vb->length, sizeof(int), compare_lno); 
+			strbuf_reset(gtop->sb);
+			strbuf_puts(gtop->sb, entry->tag);
+			strbuf_putc(gtop->sb, ' ');
+			strbuf_puts(gtop->sb, path);
+			strbuf_putc(gtop->sb, ' ');
+			savelen = strbuf_getlen(gtop->sb);
+			for (j = 0; j < entry->vb->length; j++) {
+				if (gtop->unique && j && lno_array[j - 1] == lno_array[j])
+					continue;
+				if (strbuf_getlen(gtop->sb) > savelen)
+					strbuf_putc(gtop->sb, ',');
+				strbuf_putn(gtop->sb, lno_array[j]);
+				if (strbuf_getlen(gtop->sb) > DBOP_PAGESIZE / 4) {
+					dbop_put(gtop->dbop, entry->tag, strbuf_value(gtop->sb));
+					strbuf_setlen(gtop->sb, savelen);
+				}
+			}
+			if (strbuf_getlen(gtop->sb) > savelen)
+				dbop_put(gtop->dbop, entry->tag, strbuf_value(gtop->sb));
+			varray_close(entry->vb);
+			next = entry->next;
+			free(entry);
+		}
+		gtop->htab[i] = NULL;
+	}
+}
+/*
  * gtags_add: add tags belonging to the path list into tag file.
  *
  *	i)	gtop	descripter of GTOP
@@ -456,9 +575,23 @@
 	 * Compact format.
 	 */
 	if (gtop->format & GTAGS_COMPACT) {
-		strbuf_puts(sb, "| gnusort -k3,3 -k 1,1 -k 2,2n");
-		if (flags & GTAGS_UNIQUE)
-			strbuf_puts(sb, " -u");
+		/*
+		 * If gtags-parser is used, sorting by path is not necessary.
+		 * Therefore, it is easy to perform sorting for compact form on gtags.
+		 */
+		if (locatestring(comline, "gtags-parser", MATCH_FIRST)) {
+			if (gtop->htab == NULL) {
+				gtop->htab = calloc(sizeof(struct gtop_compact_entry *), HASHBUCKETS);
+				if (gtop->htab == NULL)
+					die("short of memory.");
+			}
+			if (flags & GTAGS_UNIQUE)
+				gtop->unique = 1;
+		} else {
+			strbuf_puts(sb, "| gnusort -k3,3 -k 1,1 -k 2,2n");
+			if (flags & GTAGS_UNIQUE)
+				strbuf_puts(sb, " -u");
+		}
 	}
 #ifdef DEBUG
 	if (flags & GTAGS_DEBUG)
@@ -662,14 +795,18 @@
 gtags_close(gtop)
 	GTOP *gtop;
 {
-	if (gtop->format & GTAGS_PATHINDEX || gtop->mode != GTAGS_READ)
-		gpath_close();
+	if (gtop->htab && gtop->prev_path[0])
+		gtop_flush_htab(gtop);
 	if (gtop->sb && gtop->prev_tag[0])
 		dbop_put(gtop->dbop, gtop->prev_tag, strbuf_value(gtop->sb));
+	if (gtop->htab)
+		free(gtop->htab);
 	if (gtop->sb)
 		strbuf_close(gtop->sb);
 	if (gtop->ib)
 		strbuf_close(gtop->ib);
+	if (gtop->format & GTAGS_PATHINDEX || gtop->mode != GTAGS_READ)
+		gpath_close();
 	dbop_close(gtop->dbop);
 	free(gtop);
 }
Index: libutil/gtagsop.h
===================================================================
RCS file: /cvsroot/global/global/libutil/gtagsop.h,v
retrieving revision 1.24
diff -u -r1.24 gtagsop.h
--- libutil/gtagsop.h	6 Jul 2005 12:01:16 -0000	1.24
+++ libutil/gtagsop.h	14 Jul 2005 12:08:16 -0000
@@ -28,6 +28,7 @@
 #include "dbop.h"
 #include "idset.h"
 #include "strbuf.h"
+#include "varray.h"
 
 #define VERSIONKEY	" __.VERSION"
 #define COMPACTKEY	" __.COMPACT"
@@ -59,6 +60,12 @@
 #define GTOP_IGNORECASE		16	/* ignore case distinction */
 #define GTOP_BASICREGEX		32	/* use basic regular expression */
 
+struct gtop_compact_entry {
+	struct gtop_compact_entry *next;
+	VARRAY *vb;			/* array of line numbers */
+	char tag[1];
+};
+
 typedef struct {
 	DBOP *dbop;			/* descripter of DBOP */
 	int format_version;		/* format version */
@@ -82,6 +89,8 @@
 	FILE *fp;			/* descriptor of 'path' */
 	const char *lnop;		/* current line number */
 	int lno;			/* integer value of 'lnop' */
+	struct gtop_compact_entry **htab;/* hash table */
+	int unique;			/* flags & GTAGS_UNIQUE */
 } GTOP;
 
 const char *dbname(int);
