Index: gtags.conf.in
===================================================================
RCS file: /sources/global/global/gtags.conf.in,v
retrieving revision 1.20
diff -u -p -r1.20 gtags.conf.in
--- gtags.conf.in	30 Oct 2009 16:20:26 -0000	1.20
+++ gtags.conf.in	13 Nov 2009 13:17:53 -0000
@@ -52,6 +52,8 @@ gtags:\
 #
 # 2-pass mode (experimental)
 #
+2pass:\
+	:tc=gtags-2pass:tc=htags:
 gtags-2pass:\
 	:tc=common:\
 	:GTAGS=gtags-parser %s:\
Index: global/global.c
===================================================================
RCS file: /sources/global/global/global/global.c,v
retrieving revision 1.200
diff -u -p -r1.200 global.c
--- global/global.c	12 Nov 2009 12:36:43 -0000	1.200
+++ global/global.c	13 Nov 2009 13:17:53 -0000
@@ -54,6 +54,7 @@ void idutils(const char *, const char *)
 void grep(const char *, const char *);
 void pathlist(const char *, const char *);
 void parsefile(int, char *const *, const char *, const char *, const char *, int);
+void parsefile_filelist(const char *, const char *, const char *, const char *, int);
 int search(const char *, const char *, const char *, const char *, int);
 void tagsearch(const char *, const char *, const char *, const char *, int);
 void encode(char *, int, const char *);
@@ -62,6 +63,7 @@ const char *localprefix;		/* local prefi
 int aflag;				/* [option]		*/
 int cflag;				/* command		*/
 int fflag;				/* command		*/
+int Fflag;				/* command		*/
 int gflag;				/* command		*/
 int Gflag;				/* [option]		*/
 int iflag;				/* [option]		*/
@@ -119,6 +121,7 @@ static struct option const long_options[
 	{"completion", no_argument, NULL, 'c'},
 	{"regexp", required_argument, NULL, 'e'},
 	{"file", no_argument, NULL, 'f'},
+	{"file-list", no_argument, NULL, 'F'},
 	{"local", no_argument, NULL, 'l'},
 	{"nofilter", optional_argument, NULL, 'n'},
 	{"grep", no_argument, NULL, 'g'},
@@ -219,7 +222,7 @@ main(int argc, char **argv)
 	int optchar;
 	int option_index = 0;
 
-	while ((optchar = getopt_long(argc, argv, "ace:ifgGIlnoOpPqrstTuvx", long_options, &option_index)) != EOF) {
+	while ((optchar = getopt_long(argc, argv, "ace:ifFgGIlnoOpPqrstTuvx", long_options, &option_index)) != EOF) {
 		switch (optchar) {
 		case 0:
 			break;
@@ -238,6 +241,11 @@ main(int argc, char **argv)
 			xflag++;
 			setcom(optchar);
 			break;
+		case 'F':
+			Fflag++;
+			xflag++;
+			setcom(optchar);
+			break;
 		case 'l':
 			lflag++;
 			break;
@@ -398,11 +406,19 @@ main(int argc, char **argv)
 	/*
 	 * remove leading blanks.
 	 */
-	if (!Iflag && !gflag && av)
+	if (!Iflag && !gflag && !Fflag && av)
 		for (; *av == ' ' || *av == '\t'; av++)
 			;
 	if (cflag && av && isregex(av))
 		die_with_code(2, "only name char is allowed with -c option.");
+	if (Fflag && strcmp(av, "-") != 0) {
+		if (test("d", av))
+			die("'%s' is a directory.", av);
+		else if (!test("f", av))
+			die("'%s' not found.", av);
+		else if (!test("r", av))
+			die("'%s' is not readable.", av);
+	}
 	/*
 	 * get path of following directories.
 	 *	o current directory
@@ -533,6 +549,12 @@ main(int argc, char **argv)
 		parsefile(argc, argv, cwd, root, dbpath, db);
 	}
 	/*
+	 * parse source files in file list.
+	 */
+	else if (Fflag) {
+		parsefile_filelist(av, cwd, root, dbpath, db);
+	}
+	/*
 	 * tag search.
 	 */
 	else {
@@ -855,23 +877,20 @@ pathlist(const char *pattern, const char
 	}
 }
 /*
- * parsefile: parse file to pick up tags.
+ * parsefile_common: common part for -f and -F
  *
- *	i)	argc
- *	i)	argv
- *	i)	cwd	current directory
- *	i)	root	root directory of source tree
- *	i)	dbpath	dbpath
- *	i)	db	type of parse
+ *	i)	path_list	list of path relative to the root directory
+ *	i)	cwd		current directory
+ *	i)	root		root directory of source tree
+ *	i)	dbpath		dbpath
+ *	i)	db		type of parse
  */
-void
-parsefile(int argc, char *const *argv, const char *cwd, const char *root, const char *dbpath, int db)
+static void
+parsefile_common(void *path_list, const char *cwd, const char *root, const char *dbpath, int db)
 {
-	CONVERT *cv;
 	int count = 0;
-	int file_count = 0;
+	CONVERT *cv;
 	STRBUF *comline = strbuf_open(0);
-	STRBUF *path_list = strbuf_open(MAXPATHLEN);
 	XARGS *xp;
 	char *ctags_x;
 	int skip_defined = 0, skip_undefined = 0;
@@ -904,17 +923,92 @@ parsefile(int argc, char *const *argv, c
 	if (!getconfs(dbname(db), comline))
 		die("cannot get parser for %s.", dbname(db));
 	cv = convert_open(type, format, root, cwd, dbpath, stdout);
-	if (gpath_open(dbpath, 0) < 0)
-		die("GPATH not found.");
+	/*
+	 * Execute parser in the root directory of source tree.
+	 */
+	if (chdir(root) < 0)
+		die("cannot move to '%s' directory.", root);
+	if (fflag)
+		xp = xargs_open_with_strbuf(strbuf_value(comline), 0, path_list);
+	else
+		xp = xargs_open_with_file(strbuf_value(comline), 0, path_list);
+	if (format == FORMAT_PATH) {
+		SPLIT ptable;
+		char curpath[MAXPATHLEN+1];
+
+		curpath[0] = '\0';
+		while ((ctags_x = xargs_read(xp)) != NULL) {
+			if (split((char *)ctags_x, 4, &ptable) < 4) {
+				recover(&ptable);
+				die("too small number of parts.\n'%s'", ctags_x);
+			}
+			if (skip_defined || skip_undefined) {
+				if (defined(ptable.part[PART_TAG].start) ? skip_defined : skip_undefined)
+					continue;
+			}
+			if (strcmp(curpath, ptable.part[PART_PATH].start)) {
+				strlimcpy(curpath, ptable.part[PART_PATH].start, sizeof(curpath));
+				convert_put(cv, curpath);
+				count++;
+			}
+		}
+	} else {
+		while ((ctags_x = xargs_read(xp)) != NULL) {
+			if (skip_defined || skip_undefined) {
+				SPLIT ptable;
+
+				if (split((char *)ctags_x, 4, &ptable) < 4) {
+					recover(&ptable);
+					die("too small number of parts.\n'%s'", ctags_x);
+				}
+				if (defined(ptable.part[PART_TAG].start) ? skip_defined : skip_undefined)
+					continue;
+				recover(&ptable);
+			}
+			convert_put(cv, ctags_x);
+			count++;
+		}
+	}
+	xargs_close(xp);
+	if (chdir(cwd) < 0)
+		die("cannot move to '%s' directory.", cwd);
+	/*
+	 * Settlement
+	 */
+	convert_close(cv);
+	strbuf_close(comline);
+	if (vflag) {
+		print_count(count);
+		fprintf(stderr, " (no index used).\n");
+	}
+}
+/*
+ * parsefile: parse file to pick up tags.
+ *
+ *	i)	argc
+ *	i)	argv
+ *	i)	cwd	current directory
+ *	i)	root	root directory of source tree
+ *	i)	dbpath	dbpath
+ *	i)	db	type of parse
+ */
+void
+parsefile(int argc, char *const *argv, const char *cwd, const char *root, const char *dbpath, int db)
+{
+	int file_count = 0;
+	STRBUF *path_list = strbuf_open(MAXPATHLEN);
+
 	/*
 	 * Make a path list while checking the validity of path name.
 	 */
+	if (gpath_open(dbpath, 0) < 0)
+		die("GPATH not found.");
 	for (; argc > 0; argv++, argc--) {
 		const char *av = argv[0];
 		char path[MAXPATHLEN+1];
 
 		/*
-		 * convert the path into relative from the root directory of source tree.
+		 * convert the path into relative to the root directory of source tree.
 		 */
 		if (normalize(av, get_root_with_slash(), cwd, path, sizeof(path)) == NULL)
 			if (!qflag)
@@ -942,67 +1036,91 @@ parsefile(int argc, char *const *argv, c
 		strbuf_puts0(path_list, path);
 		file_count++;
 	}
-	if (file_count > 0) {
+	gpath_close();
+
+	if (file_count == 0)
+		die("file not found.");
+	parsefile_common(path_list, cwd, root, dbpath, db);
+
+	strbuf_close(path_list);
+}
+/*
+ * parsefile_filelist: parse files in list to pick up tags.
+ *
+ *	i)	filename	name of file incluing a list of path name
+ *	i)	cwd		current directory
+ *	i)	root		root directory of source tree
+ *	i)	dbpath		dbpath
+ *	i)	db		type of parse
+ */
+void
+parsefile_filelist(const char *filename, const char *cwd, const char *root, const char *dbpath, int db)
+{
+	int file_count = 0;
+	FILE *ip, *path_list;
+	STRBUF *ib;
+	char *p;
+	char path[MAXPATHLEN + 1];
+
+	/*
+	 * Make a path list while checking the validity of path name.
+	 */
+	path_list = tmpfile();
+	if (path_list == NULL)
+		die("cannot make temporary file.");
+	if (gpath_open(dbpath, 0) < 0)
+		die("GPATH not found.");
+	if (strcmp(filename, "-") == 0) {
+		ip = stdin;
+	} else {
+		ip = fopen(filename, "r");
+		if (ip == NULL)
+			 die("cannot open '%s'.", filename);
+	}
+	ib = strbuf_open(0);
+	while ((p = strbuf_fgets(ib, ip, STRBUF_NOCRLF)) != NULL) {
+		/* skip empty line. */
+		if (*p == '\0')
+			continue;
 		/*
-		 * Execute parser in the root directory of source tree.
+		 * convert the path into relative to the root directory of source tree.
 		 */
-		if (chdir(root) < 0)
-			die("cannot move to '%s' directory.", root);
-		xp = xargs_open_with_strbuf(strbuf_value(comline), 0, path_list);
-		if (format == FORMAT_PATH) {
-			SPLIT ptable;
-			char curpath[MAXPATHLEN+1];
-
-			curpath[0] = '\0';
-			while ((ctags_x = xargs_read(xp)) != NULL) {
-				if (split((char *)ctags_x, 4, &ptable) < 4) {
-					recover(&ptable);
-					die("too small number of parts.\n'%s'", ctags_x);
-				}
-				if (skip_defined || skip_undefined) {
-					if (defined(ptable.part[PART_TAG].start) ? skip_defined : skip_undefined)
-						continue;
-				}
-				if (strcmp(curpath, ptable.part[PART_PATH].start)) {
-					strlimcpy(curpath, ptable.part[PART_PATH].start, sizeof(curpath));
-					convert_put(cv, curpath);
-					count++;
-				}
-			}
-		} else {
-			while ((ctags_x = xargs_read(xp)) != NULL) {
-				if (skip_defined || skip_undefined) {
-					SPLIT ptable;
-
-					if (split((char *)ctags_x, 4, &ptable) < 4) {
-						recover(&ptable);
-						die("too small number of parts.\n'%s'", ctags_x);
-					}
-					if (defined(ptable.part[PART_TAG].start) ? skip_defined : skip_undefined)
-						continue;
-					recover(&ptable);
-				}
-				convert_put(cv, ctags_x);
-				count++;
+		if (normalize(p, get_root_with_slash(), cwd, path, sizeof(path)) == NULL)
+			if (!qflag)
+				fprintf(stderr, "'%s' is out of source tree.\n", path + 2);
+		if (!gpath_path2fid(path, NULL)) {
+			if (!qflag)
+				fprintf(stderr, "'%s' not found in GPATH.\n", path + 2);
+			continue;
+		}
+		if (!test("f", makepath(root, path, NULL))) {
+			if (test("d", NULL)) {
+				if (!qflag)
+					fprintf(stderr, "'%s' is a directory.\n", p);
+			} else {
+				if (!qflag)
+					fprintf(stderr, "'%s' not found.\n", p);
 			}
+			continue;
 		}
-		xargs_close(xp);
-		if (chdir(cwd) < 0)
-			die("cannot move to '%s' directory.", cwd);
+		if (lflag && !locatestring(path, localprefix, MATCH_AT_FIRST))
+			continue;
+		/*
+		 * Add a path to the path list.
+		 */
+		fputs(path, path_list);
+		putc('\n', path_list);
+		file_count++;
 	}
-	/*
-	 * Settlement
-	 */
+	strbuf_close(ib);
+	if (ip != stdin)
+		fclose(ip);
 	gpath_close();
-	convert_close(cv);
-	strbuf_close(comline);
-	strbuf_close(path_list);
-	if (file_count == 0)
-		die("file not found.");
-	if (vflag) {
-		print_count(count);
-		fprintf(stderr, " (no index used).\n");
-	}
+
+	if (file_count > 0)
+		parsefile_common(path_list, cwd, root, dbpath, db);
+
+	fclose(path_list);
 }
 /*
  * search: search specified function 
Index: global/manual.in
===================================================================
RCS file: /sources/global/global/global/manual.in,v
retrieving revision 1.52
diff -u -p -r1.52 manual.in
--- global/manual.in	25 Feb 2009 00:41:14 -0000	1.52
+++ global/manual.in	13 Nov 2009 13:17:53 -0000
@@ -28,6 +28,7 @@
 	@name{global} [-aGilnqrstTvx][-e] @arg{pattern}
 	@name{global} -c[qrsv] @arg{prefix}
 	@name{global} -f[anqrstvx] @arg{files}
+	@name{global} -F[anqrstvx] @arg{file}
 	@name{global} -g[aGilnoOqtvx][-e] @arg{pattern}
 	@name{global} -I[ailnqtvx][-e] @arg{pattern}
 	@name{global} -P[aGilnoOqtvx][-e] @arg{pattern}
@@ -59,6 +60,12 @@
 	@item{@option{-f}, @option{--file} @arg{files}}
 		Print all tags in the @arg{files}.
 		This option implies the -x option.
+	@item{@option{-F}, @option{--file-list} @arg{file}}
+		Read from @arg{file} a list of file names and print all tags
+		in each file in the list.
+		If @arg{file} is @file{-}, read from standard input.
+		File names must be separated by newline.
+		This option implies the -x option.
 	@item{@option{-g}, @option{--grep} @arg{pattern}}
 		Print all lines which match to the @arg{pattern}.
 	@item{@option{--help}}
Index: htags/anchor.c
===================================================================
RCS file: /sources/global/global/htags/anchor.c,v
retrieving revision 1.34
diff -u -p -r1.34 anchor.c
--- htags/anchor.c	12 Nov 2009 12:36:44 -0000	1.34
+++ htags/anchor.c	13 Nov 2009 13:17:53 -0000
@@ -32,10 +32,18 @@
 #ifdef HAVE_CTYPE_H
 #include <ctype.h>
 #endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
 #include "global.h"
 #include "anchor.h"
 #include "htags.h"
 
+#if (defined(_WIN32) && !defined(__CYGWIN__))
+#define mkstemp(p) open(_mktemp(p), _O_CREAT | _O_SHORT_LIVED | _O_EXCL)
+#endif
+
+static char pathlist_name[MAXPATHLEN+1];
 static XARGS *anchor_input[GTAGLIM];
 static struct anchor *table;
 static VARRAY *vb;
@@ -60,41 +68,66 @@ static struct anchor *CURRENTDEF;
 
 /*
  * anchor_prepare: setup input stream.
- *
- *	i)	anchor_stream	file pointer of path list
  */
 void
-anchor_prepare(FILE *anchor_stream)
+anchor_prepare(void)
 {
-	/*
-	 * Option table:
-	 * We use blank string as null option instead of null string("")
-	 * not to change the command length. This length influences
-	 * the number of arguments in the xargs processing.
-	 */
-	static const char *const options[] = {NULL, " ", "r", "s"};
+	static const char *const options[] = {NULL, "", "r", "s"};
 	char comline[MAXFILLEN];
 	int db;
+	static char *const argv[2] = {pathlist_name, NULL};
+	int fd;
+	GFIND *gp;
+	const char *path;
+	FILE *fp;
+
+	/* Create the list of file which should be parsed. */
+	snprintf(pathlist_name, sizeof(pathlist_name), "%s/pathlist.XXXXXX", tmpdir);
+	fd = mkstemp(pathlist_name);
+	if (fd == -1) {
+		pathlist_name[0] = '\0';
+		die("cannot make temporary file.");
+	}
+	fp = fdopen(fd, "w");
+	if (fp == NULL)
+		die("fdopen failed.");
+	gp = gfind_open(dbpath, NULL, GPATH_SOURCE);
+	while ((path = gfind_read(gp)) != NULL) {
+		fputs(path, fp);
+		putc('\n', fp);
+	}
+	gfind_close(gp);
+	fclose(fp);
 
 	for (db = GTAGS; db < GTAGLIM; db++) {
-		anchor_input[db] = NULL;
+		if (!gtags_exist[db])
+			continue;
 		/*
 		 * Htags(1) should not use gtags-parser(1) directly;
-		 * it should use global(1) with the -f option instead.
+		 * it should use global(1) with the -F option instead.
 		 * Because gtags-parser is part of the implementation of
 		 * gtags(1) and global(1), and htags is only an application
 		 * program which uses global(1). If htags depends on
 		 * gtags-parser, it will become difficult to change the
 		 * implementation of gtags and global.
 		 *
-		 * Though the output of global -f is not necessarily sorted
+		 * Though the output of global -F is not necessarily sorted
 		 * by the path, it is guaranteed that the records concerning
 		 * the same file are consecutive.
 		 */
-		if (gtags_exist[db] == 1) {
-			snprintf(comline, sizeof(comline), "%s -f%s --nofilter=path", global_path, options[db]);
-			anchor_input[db] = xargs_open_with_file(comline, 0, anchor_stream);
-		}
+		snprintf(comline, sizeof(comline), "%s -F%s --nofilter=path", global_path, options[db]);
+		anchor_input[db] = xargs_open_with_argv(comline, 0, 1, argv);
+	}
+}
+/*
+ * anchor_close: remove temporary file
+ */
+void
+anchor_close(void)
+{
+	if (pathlist_name[0] != '\0') {
+		unlink(pathlist_name);
+		pathlist_name[0] = '\0';
 	}
 }
 /*
Index: htags/anchor.h
===================================================================
RCS file: /sources/global/global/htags/anchor.h,v
retrieving revision 1.16
diff -u -p -r1.16 anchor.h
--- htags/anchor.h	5 Jul 2007 06:52:00 -0000	1.16
+++ htags/anchor.h	13 Nov 2009 13:17:53 -0000
@@ -63,7 +63,8 @@ struct anchor {
 #define A_HELP		7
 #define A_LIMIT		8
 
-void anchor_prepare(FILE *);
+void anchor_prepare(void);
+void anchor_close(void);
 void anchor_load(const char *);
 void anchor_unload(void);
 struct anchor *anchor_first(void);
Index: htags/htags.c
===================================================================
RCS file: /sources/global/global/htags/htags.c,v
retrieving revision 1.133
diff -u -p -r1.133 htags.c
--- htags/htags.c	12 Nov 2009 12:36:44 -0000	1.133
+++ htags/htags.c	13 Nov 2009 13:17:53 -0000
@@ -331,6 +331,7 @@ clean(void)
 {
 	unload_gpath();
 	cache_close();
+	anchor_close();
 }
 /*
  * Signal handler.
@@ -815,26 +816,13 @@ static void
 makehtml(int total)
 {
 	GFIND *gp;
-	FILE *anchor_stream;
 	const char *path;
 	int count = 0;
 
 	/*
-	 * Create anchor stream for anchor_load().
-	 */
-	anchor_stream = tmpfile();
-	gp = gfind_open(dbpath, NULL, other_files ? GPATH_BOTH : GPATH_SOURCE);
-	while ((path = gfind_read(gp)) != NULL) {
-		if (gp->type == GPATH_OTHER)
-			fputc(' ', anchor_stream);
-		fputs(path, anchor_stream);
-		fputc('\n', anchor_stream);
-	}
-	gfind_close(gp);
-	/*
 	 * Prepare anchor stream for anchor_load().
 	 */
-	anchor_prepare(anchor_stream);
+	anchor_prepare();
 	/*
 	 * For each path in GPATH, convert the path into HTML file.
 	 */
