Index: gtags/gtags.c
===================================================================
RCS file: /cvsroot/global/global/gtags/gtags.c,v
retrieving revision 1.128
diff -u -r1.128 gtags.c
--- gtags/gtags.c	31 Jul 2005 12:51:53 -0000	1.128
+++ gtags/gtags.c	31 Jul 2005 15:02:05 -0000
@@ -94,6 +94,7 @@
 int secure_mode;
 const char *extra_options;
 const char *info_string;
+const char *file_list;
 
 int extractmethod;
 int total;
@@ -130,6 +131,7 @@
 	{"convert", no_argument, &do_convert, 1},
 	{"debug", no_argument, &debug, 1},
 	{"expand", required_argument, &do_expand, 1},
+	{"file", required_argument, NULL, 0},
 	{"find", no_argument, &do_find, 1},
 	{"gtagsconf", required_argument, &gtagsconf, 1},
 	{"gtagslabel", required_argument, &gtagslabel, 1},
@@ -220,6 +222,8 @@
 				}
 				set_env(name, value);
 				gtagsconf = gtagslabel = 0;
+			} else if (!strcmp(p, "file")) {
+				file_list = optarg;
 			}
 			break;
 		case 'c':
@@ -684,7 +688,11 @@
 	/*
 	 * make add list and update list.
 	 */
-	for (find_open(NULL); (path = find_read()) != NULL; ) {
+	if (file_list)
+		filelist_open(file_list, root);
+	else
+		find_open(NULL);
+	while ((path = file_list ? filelist_read() : find_read()) != NULL) {
 		const char *fid;
 
 		/* a blank at the head of path means 'NOT SOURCE'. */
@@ -701,7 +709,10 @@
 			idset_add(deleteset, atoi(fid));
 		}
 	}
-	find_close();
+	if (file_list)
+		filelist_close();
+	else
+		find_close();
 	/*
 	 * make delete list.
 	 */
@@ -925,6 +936,7 @@
 	int arg_count = 0;
 	STRBUF *path_list = strbuf_open(MAXPATHLEN);
 	int path_list_max;
+	static int gpath_created;
 
 	/*
 	 * get tag command.
@@ -973,7 +985,14 @@
 	 * the source file as a lot as possible to decrease the invoking
 	 * frequency of the parser.
 	 */
-	for (find_open(NULL); (path = find_read()) != NULL; ) {
+	if (gpath_created)
+		gfind_open(dbpath, NULL);
+	else if (file_list)
+		filelist_open(file_list, root);
+	else
+		find_open(NULL);
+	while ((path = gpath_created ? gfind_read() :
+			file_list ? filelist_read() : find_read()) != NULL) {
 		int skip = 0;
 
 		/* a blank at the head of path means 'NOT SOURCE'. */
@@ -1028,7 +1047,13 @@
 	if (strbuf_getlen(path_list))
 		gtags_add(gtop, strbuf_value(comline), path_list, gflags);
 	total = count;				/* save total count */
-	find_close();
+	if (gpath_created)
+		gfind_close();
+	else if (file_list)
+		filelist_close();
+	else
+		find_close();
+	gpath_created = 1;
 	gtags_close(gtop);
 	strbuf_close(comline);
 	strbuf_close(path_list);
Index: gtags/manual.in
===================================================================
RCS file: /cvsroot/global/global/gtags/manual.in,v
retrieving revision 1.48
diff -u -r1.48 manual.in
--- gtags/manual.in	30 Jul 2005 11:34:13 -0000	1.48
+++ gtags/manual.in	31 Jul 2005 15:02:05 -0000
@@ -55,6 +55,10 @@
 	@item{@option{--config} @arg{name}}
 		Show the value of config variable @arg{name}.
 		If @arg{name} is not specified then show whole of config entry.
+	@item{@option{--files} @arg{file}}
+		Read from @arg{file} a list of file names which should be parsed.
+		If @arg{file} is @file{-}, read from standard input.
+		File names must be separated by newline.
 	@item{@option{--gtagsconf} @arg{file}}
 		Load user's configuration from @arg{file}.
 	@item{@option{--gtagslabel} @arg{label}}
Index: libutil/Makefile.am
===================================================================
RCS file: /cvsroot/global/global/libutil/Makefile.am,v
retrieving revision 1.20
diff -u -r1.20 Makefile.am
--- libutil/Makefile.am	5 Jul 2005 22:31:20 -0000	1.20
+++ libutil/Makefile.am	31 Jul 2005 15:02:05 -0000
@@ -23,7 +23,7 @@
 conf.c dbop.c defined.c die.c find.c getdbpath.c gtagsop.c locatestring.c \
 makepath.c path.c gpathop.c strbuf.c strmake.c tab.c test.c \
 token.c usable.c version.c is_unixy.c abs2rel.c split.c strlimcpy.c linetable.c \
-env.c char.c vfind.c date.c langmap.c varray.c idset.c
+env.c char.c vfind.c date.c langmap.c varray.c idset.c filelist.c
 
 AM_CFLAGS = -DBINDIR='"$(bindir)"' -DDATADIR='"$(datadir)"'
 
Index: libutil/filelist.c
===================================================================
RCS file: libutil/filelist.c
diff -N libutil/filelist.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ libutil/filelist.c	31 Jul 2005 15:02:05 -0000
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 2005 Tama Communications Corporation
+ *
+ * This file is part of GNU GLOBAL.
+ *
+ * GNU GLOBAL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU GLOBAL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <assert.h>
+#include <ctype.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <stdio.h>
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+#include "gparam.h"
+#include "regex.h"
+
+#include "char.h"
+#include "conf.h"
+#include "die.h"
+#include "locatestring.h"
+#include "path.h"
+#include "strbuf.h"
+#include "strlimcpy.h"
+#include "test.h"
+#include "filelist.h"
+
+extern int qflag;
+#ifdef DEBUG
+extern int debug;
+#endif
+
+static int opened;
+static FILE *ip;
+static STRBUF *ib;
+static char root[MAXPATHLEN + 1];
+static size_t rootlen;
+static char buf[MAXPATHLEN + 1];
+static regex_t skip_area;
+static regex_t *skip;			/* regex for skipping units */
+static int list_count;
+static char **listarray;		/* list for skipping full path */
+static regex_t suff_area;
+static regex_t *suff = &suff_area;	/* regex for suffixes */
+
+static void trim(char *);
+static void prepare_skip(void);
+static int skipthisfile(const char *);
+static void prepare_source(void);
+
+/*
+ * trim: remove blanks and '\'.
+ */
+static void
+trim(s)
+	char *s;
+{
+	char *p;
+
+	for (p = s; *s; s++) {
+		if (isspace((unsigned char)*s))
+			continue;	
+		if (*s == '\\' && *(s + 1))
+			s++;
+		*p++ = *s;
+	}
+	*p = 0;
+}
+/*
+ * prepare_skip: prepare skipping files.
+ *
+ *	go)	skip	regular expression for skip files.
+ *	go)	listarry[] skip list.
+ *	go)	list_count count of skip list.
+ */
+static void
+prepare_skip(void)
+{
+	char *skiplist;
+	STRBUF *reg = strbuf_open(0);
+	int reg_count = 0;
+	char *p, *q;
+	int flags = REG_EXTENDED|REG_NEWLINE;
+	STATIC_STRBUF(list);
+
+	/*
+	 * load icase_path option.
+	 */
+	if (getconfb("icase_path"))
+		flags |= REG_ICASE;
+#if defined(_WIN32) || defined(__DJGPP__)
+	flags |= REG_ICASE;
+#endif
+	/*
+	 * initinalize common data.
+	 */
+	strbuf_clear(list);
+	list_count = 0;
+	if (listarray)
+		free(listarray);
+	listarray = NULL;
+	/*
+	 * load skip data.
+	 */
+	if (!getconfs("skip", reg)) {
+		strbuf_close(reg);
+		return;
+	}
+	skiplist = strdup(strbuf_value(reg));
+	if (!skiplist)
+		die("short of memory.");
+	trim(skiplist);
+	strbuf_reset(reg);
+	/*
+	 * construct regular expression.
+	 */
+	strbuf_putc(reg, '(');	/* ) */
+	for (p = skiplist; p; ) {
+		char *skipf = p;
+		if ((p = locatestring(p, ",", MATCH_FIRST)) != NULL)
+			*p++ = 0;
+		if (*skipf == '/') {
+			list_count++;
+			strbuf_puts0(list, skipf);
+		} else {
+			reg_count++;
+			strbuf_putc(reg, '/');
+			for (q = skipf; *q; q++) {
+				if (isregexchar(*q))
+					strbuf_putc(reg, '\\');
+				strbuf_putc(reg, *q);
+			}
+			if (*(q - 1) != '/')
+				strbuf_putc(reg, '$');
+			if (p)
+				strbuf_putc(reg, '|');
+		}
+	}
+	strbuf_unputc(reg, '|');
+	strbuf_putc(reg, ')');
+	if (reg_count > 0) {
+		/*
+		 * compile regular expression.
+		 */
+		skip = &skip_area;
+#ifdef DEBUG
+		if (debug)
+			fprintf(stderr, "skip regex: %s\n", strbuf_value(reg));
+#endif
+		if (regcomp(skip, strbuf_value(reg), flags))
+			die("cannot compile regular expression.");
+	} else {
+		skip = NULL;
+	}
+	if (list_count > 0) {
+		int i;
+		listarray = (char **)malloc(sizeof(char *) * list_count);
+		if (listarray == NULL)
+			die("short of memory.");
+		p = strbuf_value(list);
+#ifdef DEBUG
+		if (debug)
+			fprintf(stderr, "skip list: ");
+#endif
+		for (i = 0; i < list_count; i++) {
+#ifdef DEBUG
+			if (debug) {
+				fprintf(stderr, "%s", p);
+				if (i + 1 < list_count)
+					fputc(',', stderr);
+			}
+#endif
+			listarray[i] = p;
+			p += strlen(p) + 1;
+		}
+#ifdef DEBUG
+		if (debug)
+			fputc('\n', stderr);
+#endif
+	}
+	strbuf_close(reg);
+	free(skiplist);
+}
+/*
+ * skipthisfile: check whether or not we accept this file.
+ *
+ *	i)	path	path name (must start with ./)
+ *	r)		1: skip, 0: dont skip
+ */
+static int
+skipthisfile(path)
+	const char *path;
+{
+	const char *first, *last;
+	int i;
+
+	/*
+	 * unit check.
+	 */
+	if (skip && regexec(skip, path, 0, 0, 0) == 0)
+		return 1;
+	/*
+	 * list check.
+	 */
+	for (i = 0; i < list_count; i++) {
+		first = listarray[i];
+		last = first + strlen(first);
+		/*
+		 * the path must start with "./".
+		 */
+		if (*(last - 1) == '/') {	/* it's a directory */
+			if (!strncmp(path + 1, first, last - first))
+				return 1;
+		} else {
+			if (!strcmp(path + 1, first))
+				return 1;
+		}
+	}
+	return 0;
+}
+/*
+ * prepare_source: preparing regular expression.
+ *
+ *	i)	flags	flags for regcomp.
+ *	go)	suff	regular expression for source files.
+ */
+static void
+prepare_source(void)
+{
+	STRBUF *sb = strbuf_open(0);
+	char *sufflist = NULL;
+	int flags = REG_EXTENDED;
+
+	/*
+	 * load icase_path option.
+	 */
+	if (getconfb("icase_path"))
+		flags |= REG_ICASE;
+#if defined(_WIN32) || defined(__DJGPP__)
+	flags |= REG_ICASE;
+#endif
+	strbuf_reset(sb);
+	if (!getconfs("suffixes", sb))
+		die("cannot get suffixes data.");
+	sufflist = strdup(strbuf_value(sb));
+	if (!sufflist)
+		die("short of memory.");
+	trim(sufflist);
+	{
+		const char *suffp;
+
+		strbuf_reset(sb);
+		strbuf_puts(sb, "\\.(");       /* ) */
+		for (suffp = sufflist; suffp; ) {
+			const char *p;
+
+			for (p = suffp; *p && *p != ','; p++) {
+				if (!isalnum((unsigned char)*p))
+					strbuf_putc(sb, '\\');
+				strbuf_putc(sb, *p);
+			}
+			if (!*p)
+				break;
+			assert(*p == ',');
+			strbuf_putc(sb, '|');
+			suffp = ++p;
+		}
+		strbuf_puts(sb, ")$");
+		/*
+		 * compile regular expression.
+		 */
+#ifdef DEBUG
+		if (debug)
+			fprintf(stderr, "find regex: %s\n", strbuf_value(sb));
+#endif
+		if (regcomp(suff, strbuf_value(sb), flags) != 0)
+			die("cannot compile regular expression.");
+	}
+	strbuf_close(sb);
+	if (sufflist)
+		free(sufflist);
+}
+/*
+ * filelist_open: find_open like interface for handling output of find(1).
+ *
+ *	i)	filename	file including list of file names.
+ *				When "-" is specified, read from standard input.
+ *	i)	rootdir		root directory of source tree
+ */
+void
+filelist_open(filename, rootdir)
+	const char *filename;
+	const char *rootdir;
+{
+	assert(opened == 0);
+	ib = strbuf_open(MAXBUFLEN);
+	if (filename[0] == '-' && filename[1] == '\0') {
+		ip = stdin;
+	} else {
+		if (test("d", filename))
+			die("'%s' is a directory.", filename);
+		if (!test("f", filename))
+			die("'%s' not found.", filename);
+		if (!test("r", filename))
+			die("'%s' is not readable.", filename);
+		ip = fopen(filename, "r");
+		if (ip == NULL)
+			die("cannot open '%s'.", filename);
+	}
+	strlimcpy(root, rootdir, sizeof(root));
+	rootlen = strlen(root);
+	prepare_skip();
+	prepare_source();
+	opened = 1;
+}
+/*
+ * filelist_read: read path from file
+ *
+ *	r)		path
+ */
+const char *
+filelist_read()
+{
+	char *path;
+
+	assert(opened != 0);
+	for (;;) {
+		path = strbuf_fgets(ib, ip, STRBUF_NOCRLF);
+		if (path == NULL) {
+			/* EOF */
+			return NULL;
+		}
+		if (*path == '\0') {
+			/* skip empty line.  */
+			continue;
+		}
+		if (!test("f", path)) {
+			if (!qflag) {
+				if (test("d", path))
+					fprintf(stderr, "'%s' is a directory.\n", path);
+				else
+					fprintf(stderr, "'%s' not found.\n", path);
+			}
+			continue;
+		}
+		if (realpath(path, buf) == NULL) {
+			if (!qflag)
+				fprintf(stderr, "realpath(\"%s\", buf) failed.\n", path);
+			continue;
+		}
+		if (!isabspath(buf))
+			die("realpath(3) is not compatible with BSD version.");
+		if (strncmp(buf, root, rootlen) || buf[rootlen] != '/') {
+			if (!qflag)
+				fprintf(stderr, "'%s' is out of source tree.\n", buf);
+			continue;
+		}
+		path = buf + rootlen - 1;
+		*path = '.';
+		/*
+		 * GLOBAL cannot treat path which includes blanks.
+		 * It will be improved in the future.
+		 */
+		if (locatestring(path, " ", MATCH_LAST)) {
+			if (!qflag)
+				fprintf(stderr, "'%s' ignored, because it includes blank.\n", path + 2);
+			continue;
+		}
+		if (skipthisfile(path))
+			continue;
+		/*
+		 * A blank at the head of path means
+		 * other than source file.
+		 */
+		if (regexec(suff, path, 0, 0, 0) != 0)
+			*--path = ' ';
+		return path;
+	}
+}
+/*
+ * filelist_close: close iterator.
+ */
+void
+filelist_close()
+{
+	assert(opened != 0);
+	regfree(suff);
+	if (skip)
+		regfree(skip);
+	if (ip != stdin)
+		fclose(ip);
+	strbuf_close(ib);
+	opened = 0;
+}
Index: libutil/filelist.h
===================================================================
RCS file: libutil/filelist.h
diff -N libutil/filelist.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ libutil/filelist.h	31 Jul 2005 15:02:06 -0000
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2005 Tama Communications Corporation
+ *
+ * This file is part of GNU GLOBAL.
+ *
+ * GNU GLOBAL is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * GNU GLOBAL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+ */
+#ifndef _FILELIST_H_
+#define _FILELIST_H_
+
+void filelist_open(const char *, const char *);
+const char *filelist_read(void);
+void filelist_close(void);
+
+#endif /* ! _FILELIST_H_ */
Index: libutil/global.h
===================================================================
RCS file: /cvsroot/global/global/libutil/global.h,v
retrieving revision 1.19
diff -u -r1.19 global.h
--- libutil/global.h	5 Jul 2005 22:31:20 -0000	1.19
+++ libutil/global.h	31 Jul 2005 15:02:06 -0000
@@ -31,6 +31,7 @@
 #include "defined.h"
 #include "die.h"
 #include "env.h"
+#include "filelist.h"
 #include "find.h"
 #include "getdbpath.h"
 #include "gtagsop.h"
