summaryrefslogtreecommitdiffstats
path: root/libidu
diff options
context:
space:
mode:
authorGreg McGary <greg@mcgary.org>1997-04-18 06:43:32 +0000
committerGreg McGary <greg@mcgary.org>1997-04-18 06:43:32 +0000
commit916418ea1284e6aa64f50eba077e48ced5944acc (patch)
tree8e519e3a48ae80c8c17da3ba5acccd91530b22ff /libidu
parenta560adff07afe7c1f35e4585e0694e89c842b245 (diff)
downloadidutils-916418ea1284e6aa64f50eba077e48ced5944acc.tar.gz
idutils-916418ea1284e6aa64f50eba077e48ced5944acc.tar.bz2
idutils-916418ea1284e6aa64f50eba077e48ced5944acc.zip
Initial revision
Diffstat (limited to 'libidu')
-rw-r--r--libidu/Makefile.am22
-rw-r--r--libidu/Makefile.in320
-rw-r--r--libidu/dynvec.c59
-rw-r--r--libidu/dynvec.h35
-rw-r--r--libidu/fnprint.c138
-rw-r--r--libidu/hash.c294
-rw-r--r--libidu/hash.h144
-rw-r--r--libidu/id-lang.map88
-rw-r--r--libidu/idfile.c137
-rw-r--r--libidu/idfile.h223
-rw-r--r--libidu/idread.c280
-rw-r--r--libidu/idwrite.c169
-rw-r--r--libidu/scanners.c1212
-rw-r--r--libidu/scanners.h70
-rw-r--r--libidu/tokflags.h35
-rw-r--r--libidu/walker.c1130
-rw-r--r--libidu/xnls.h41
17 files changed, 4397 insertions, 0 deletions
diff --git a/libidu/Makefile.am b/libidu/Makefile.am
new file mode 100644
index 0000000..5fde198
--- /dev/null
+++ b/libidu/Makefile.am
@@ -0,0 +1,22 @@
+## Process this file with automake to create Makefile.in
+
+AUTOMAKE_OPTIONS = ansi2knr
+
+noinst_LIBRARIES = idu
+
+idu_SOURCES = dynvec.c hash.c idfile.c idread.c idwrite.c fnprint.c \
+ scanners.c walker.c
+
+idudir = $(prefix)/share
+idu_DATA = id-lang.map
+
+noinst_HEADERS = dynvec.h hash.h idfile.h scanners.h tokflags.h xnls.h
+
+INCLUDES = -I. -I$(srcdir) \
+ -I../lib -I$(top_srcdir)/lib \
+ -I../intl -I$(top_srcdir)/intl \
+ -I.. -I$(top_srcdir)
+DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
+
+ansi2knr.c: $(top_srcdir)/src/ansi2knr.c
+ ln $(top_srcdir)/src/ansi2knr.c .
diff --git a/libidu/Makefile.in b/libidu/Makefile.in
new file mode 100644
index 0000000..10c086f
--- /dev/null
+++ b/libidu/Makefile.in
@@ -0,0 +1,320 @@
+# Makefile.in generated automatically by automake 1.0 from Makefile.am
+
+# Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+
+SHELL = /bin/sh
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+AUTOMAKE_OPTIONS = ansi2knr
+
+noinst_LIBRARIES = idu
+
+idu_SOURCES = dynvec.c hash.c idfile.c idread.c idwrite.c fnprint.c \
+ scanners.c walker.c
+
+idudir = $(prefix)/share
+idu_DATA = id-lang.map
+
+noinst_HEADERS = dynvec.h hash.h idfile.h scanners.h tokflags.h xnls.h
+
+INCLUDES = -I. -I$(srcdir) \
+ -I../lib -I$(top_srcdir)/lib \
+ -I../intl -I$(top_srcdir)/intl \
+ -I.. -I$(top_srcdir)
+DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
+mkinstalldirs = $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../config.h
+LIBRARIES = $(noinst_LIBRARIES)
+
+noinst_LIBFILES = libidu.a
+
+CC = @CC@
+LEX = @LEX@
+YACC = @YACC@
+CPPFLAGS = @CPPFLAGS@
+CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)
+LINK = $(CC) $(LDFLAGS) -o $@
+
+ANSI2KNR = @ANSI2KNR@
+o = .@U@o
+idu_LIBADD =
+idu_OBJECTS = dynvec$o hash$o idfile$o idread$o idwrite$o fnprint$o \
+scanners$o walker$o
+EXTRA_idu_SOURCES =
+LIBFILES = libidu.a
+AR = ar
+RANLIB = @RANLIB@
+DATA = $(idu_DATA)
+
+HEADERS = $(noinst_HEADERS)
+
+DIST_COMMON = Makefile.am Makefile.in
+
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(BUILT_SOURCES) $(HEADERS) \
+ $(TEXINFOS) $(INFOS) $(MANS) $(EXTRA_DIST) $(DATA)
+DEP_DISTFILES = $(DIST_COMMON) $(SOURCES) $(BUILT_SOURCES) $(HEADERS) \
+ $(TEXINFOS) $(INFO_DEPS) $(MANS) $(EXTRA_DIST) $(DATA)
+
+TAR = tar
+SOURCES = $(idu_SOURCES)
+OBJECTS = $(idu_OBJECTS)
+
+default: all
+
+
+$(srcdir)/Makefile.in: @MAINT@Makefile.am $(top_srcdir)/configure.in
+ cd $(top_srcdir) && automake $(subdir)/Makefile
+
+Makefile: $(top_builddir)/config.status Makefile.in
+ cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= ./config.status
+
+mostlyclean-noinstLIBRARIES:
+
+clean-noinstLIBRARIES:
+ rm -f $(noinst_LIBFILES)
+
+distclean-noinstLIBRARIES:
+
+maintainer-clean-noinstLIBRARIES:
+
+.c.o:
+ $(COMPILE) $<
+
+mostlyclean-compile:
+ rm -f *.o core
+
+clean-compile:
+
+distclean-compile:
+ rm -f *.tab.c
+
+maintainer-clean-compile:
+
+.c._c:
+ $(ANSI2KNR) $< > $*.tmp && mv $*.tmp $@
+
+._c._o:
+ @echo $(COMPILE) $<
+ @rm -f _$*.c
+ @ln $< _$*.c && $(COMPILE) _$*.c && mv _$*.o $@ && rm _$*.c
+
+.c._o:
+ $(ANSI2KNR) $< > $*.tmp && mv $*.tmp $*._c
+ @echo $(COMPILE) $*._c
+ @rm -f _$*.c
+ @ln $*._c _$*.c && $(COMPILE) _$*.c && mv _$*.o $@ && rm _$*.c
+
+ansi2knr: ansi2knr.o
+ $(LINK) ansi2knr.o $(LIBS)
+
+$(OBJECTS): $(ANSI2KNR)
+ansi2knr.o: $(CONFIG_HEADER)
+
+mostlyclean-kr:
+ rm -f *._o *._c _*.c _*.o
+
+clean-kr:
+
+distclean-kr:
+ rm -f ansi2knr
+
+maintainer-clean-kr:
+$(idu_OBJECTS): ../config.h
+
+libidu.a: $(idu_OBJECTS) $(idu_LIBADD)
+ rm -f libidu.a
+ $(AR) cru libidu.a $(idu_OBJECTS) $(idu_LIBADD)
+ $(RANLIB) libidu.a
+
+install-iduDATA: $(idu_DATA)
+ $(mkinstalldirs) $(idudir)
+ list="$(idu_DATA)"; for p in $$list; do \
+ if test -f $(srcdir)/$$p; then \
+ $(INSTALL_DATA) $(srcdir)/$$p $(idudir)/$$p; \
+ else if test -f $$p; then \
+ $(INSTALL_DATA) $$p $(idudir)/$$p; \
+ fi; fi; \
+ done
+
+uninstall-iduDATA:
+ list="$(idu_DATA)"; for p in $$list; do \
+ rm -f $(idudir)/$$p; \
+ done
+
+ID: $(HEADERS) $(SOURCES)
+ here=`pwd` && cd $(srcdir) && mkid -f$$here/ID $(SOURCES) $(HEADERS)
+
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES)
+ here=`pwd` && cd $(srcdir) && etags $(ETAGS_ARGS) $(SOURCES) $(HEADERS) -o $$here/TAGS
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+ rm -f TAGS ID
+
+maintainer-clean-tags:
+
+subdir = libidu
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+distdir: $(DEP_DISTFILES)
+ @for file in `cd $(srcdir) && echo $(DISTFILES)`; do \
+ test -f $(distdir)/$$file \
+ || ln $(srcdir)/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $(srcdir)/$$file $(distdir)/$$file; \
+ done
+dynvec$o: ../libidu/dynvec.c ../libidu/dynvec.h \
+ ../lib/xmalloc.h ../lib/xstring.h
+fnprint$o: ../libidu/fnprint.c ../lib/xstdlib.h \
+ ../lib/xalloca.h ../lib/xunistd.h ../libidu/idfile.h \
+ ../lib/xobstack.h ../lib/xmalloc.h ../lib/xstring.h \
+ ../libidu/hash.h ../libidu/dynvec.h ../libidu/tokflags.h \
+ ../libidu/xnls.h ../lib/pathmax.h ../lib/error.h
+hash$o: ../libidu/hash.c ../lib/xstdlib.h ../libidu/hash.h \
+ ../libidu/xnls.h ../lib/xmalloc.h ../lib/xstring.h \
+ ../lib/error.h
+idfile$o: ../libidu/idfile.c ../lib/xstdlib.h \
+ ../lib/xstring.h ../lib/xsysstat.h ../libidu/xnls.h \
+ ../lib/xobstack.h ../lib/xmalloc.h ../libidu/idfile.h \
+ ../libidu/hash.h ../libidu/dynvec.h ../libidu/tokflags.h \
+ ../lib/error.h
+idread$o: ../libidu/idread.c ../libidu/idfile.h \
+ ../lib/xobstack.h ../lib/xmalloc.h ../lib/xstring.h \
+ ../libidu/hash.h ../libidu/dynvec.h ../libidu/tokflags.h \
+ ../lib/xstddef.h ../lib/error.h ../libidu/xnls.h
+idwrite$o: ../libidu/idwrite.c ../libidu/idfile.h \
+ ../lib/xobstack.h ../lib/xmalloc.h ../lib/xstring.h \
+ ../libidu/hash.h ../libidu/dynvec.h ../libidu/tokflags.h \
+ ../libidu/xnls.h ../lib/error.h
+scanners$o: ../libidu/scanners.c ../lib/xstdlib.h \
+ ../lib/xstddef.h ../lib/xunistd.h ../lib/xsysstat.h \
+ ../lib/xstring.h ../lib/xmalloc.h ../libidu/xnls.h \
+ ../lib/error.h ../libidu/scanners.h ../lib/xobstack.h \
+ ../libidu/tokflags.h
+walker$o: ../libidu/walker.c ../lib/xsysstat.h \
+ ../lib/xstdlib.h ../lib/xstddef.h ../lib/xunistd.h \
+ ../lib/xstring.h ../lib/xfnmatch.h ../lib/xdirent.h \
+ ../libidu/xnls.h ../libidu/idfile.h ../lib/xobstack.h \
+ ../lib/xmalloc.h ../libidu/hash.h ../libidu/dynvec.h \
+ ../libidu/tokflags.h ../lib/error.h ../libidu/scanners.h \
+ ../lib/pathmax.h ../lib/xalloca.h
+
+info:
+
+dvi:
+
+check: all
+
+installcheck:
+
+install-exec:
+
+install-data: install-iduDATA
+
+install: install-exec install-data all
+ @:
+
+uninstall: uninstall-iduDATA
+
+all: $(LIBFILES) $(DATA) $(HEADERS) Makefile
+
+install-strip:
+ $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install
+installdirs:
+ $(mkinstalldirs) $(idudir)
+
+
+mostlyclean-generic:
+ test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
+
+clean-generic:
+ test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ rm -f Makefile $(DISTCLEANFILES)
+ rm -f config.cache config.log $(CONFIG_HEADER) stamp-h
+
+maintainer-clean-generic:
+ test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+ test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+mostlyclean: mostlyclean-noinstLIBRARIES mostlyclean-compile \
+ mostlyclean-kr mostlyclean-tags mostlyclean-generic
+
+clean: clean-noinstLIBRARIES clean-compile clean-kr clean-tags \
+ clean-generic mostlyclean
+
+distclean: distclean-noinstLIBRARIES distclean-compile distclean-kr \
+ distclean-tags distclean-generic clean
+ rm -f config.status
+
+maintainer-clean: maintainer-clean-noinstLIBRARIES \
+ maintainer-clean-compile maintainer-clean-kr \
+ maintainer-clean-tags maintainer-clean-generic \
+ distclean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+.PHONY: default mostlyclean-noinstLIBRARIES distclean-noinstLIBRARIES \
+clean-noinstLIBRARIES maintainer-clean-noinstLIBRARIES \
+mostlyclean-compile distclean-compile clean-compile \
+maintainer-clean-compile mostlyclean-kr distclean-kr clean-kr \
+maintainer-clean-kr uninstall-iduDATA install-iduDATA tags \
+mostlyclean-tags distclean-tags clean-tags maintainer-clean-tags \
+distdir info dvi check installcheck install-exec install-data install \
+uninstall all installdirs mostlyclean-generic distclean-generic \
+clean-generic maintainer-clean-generic clean mostlyclean distclean \
+maintainer-clean
+
+
+ansi2knr.c: $(top_srcdir)/src/ansi2knr.c
+ ln $(top_srcdir)/src/ansi2knr.c .
+.SUFFIXES:
+.SUFFIXES: .c .o ._c ._o
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/libidu/dynvec.c b/libidu/dynvec.c
new file mode 100644
index 0000000..29844b0
--- /dev/null
+++ b/libidu/dynvec.c
@@ -0,0 +1,59 @@
+/* dynvec.c -- dynamically growable vectors
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ This program 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.
+
+ This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <config.h>
+#include "dynvec.h"
+#include "xmalloc.h"
+
+struct dynvec *
+make_dynvec (int n)
+{
+ struct dynvec *dv = MALLOC (struct dynvec, 1);
+ dv->dv_vec = MALLOC (void *, n);
+ dv->dv_capacity = n;
+ dv->dv_fill = 0;
+ return dv;
+}
+
+void
+dynvec_free (struct dynvec *dv)
+{
+ free (dv->dv_vec);
+ free (dv);
+}
+
+void
+dynvec_freeze (struct dynvec *dv)
+{
+ if (dv->dv_fill == dv->dv_capacity)
+ return;
+ dv->dv_capacity = dv->dv_fill;
+ dv->dv_vec = REALLOC (dv->dv_vec, void *, dv->dv_capacity);
+}
+
+void
+dynvec_append (struct dynvec *dv, void *element)
+{
+ if (dv->dv_fill == dv->dv_capacity)
+ {
+ dv->dv_capacity *= 2;
+ dv->dv_vec = REALLOC (dv->dv_vec, void *, dv->dv_capacity);
+ }
+ dv->dv_vec[dv->dv_fill++] = element;
+}
diff --git a/libidu/dynvec.h b/libidu/dynvec.h
new file mode 100644
index 0000000..55fc0c2
--- /dev/null
+++ b/libidu/dynvec.h
@@ -0,0 +1,35 @@
+/* dynvec.h -- declarations for dynamically growable vectors
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ This program 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.
+
+ This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _dynvec_h_
+#define _dynvec_h_
+
+struct dynvec
+{
+ void **dv_vec;
+ int dv_capacity;
+ int dv_fill;
+};
+
+struct dynvec *make_dynvec __P((int n));
+void dynvec_free __P((struct dynvec *dv));
+void dynvec_freeze __P((struct dynvec *dv));
+void dynvec_append __P((struct dynvec *dv, void *element));
+
+#endif /* not _dynvec_h_ */
diff --git a/libidu/fnprint.c b/libidu/fnprint.c
new file mode 100644
index 0000000..ea5ea88
--- /dev/null
+++ b/libidu/fnprint.c
@@ -0,0 +1,138 @@
+/* fnprint.c -- print a list of file names
+ Copyright (C) 1986, 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ This program 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.
+
+ This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <config.h>
+#include <stdio.h>
+#include "xstdlib.h"
+#include "xalloca.h"
+#include "xunistd.h"
+#include "idfile.h"
+#include "xstring.h"
+#include "xnls.h"
+#include "pathmax.h"
+#include "error.h"
+
+char const *root_name __P((char const *path));
+char const *suff_name __P((char const *path));
+int common_prefix_suffix __P((struct file_link const *flink_1, struct file_link const *flink_2));
+
+extern void usage __P((void));
+extern struct file_link *cw_dlink;
+
+/* Return the file name with the suffix stripped off. */
+
+char const *
+root_name (char const *file_name)
+{
+ static char file_name_buffer[BUFSIZ];
+ char const *dot = strrchr (file_name, '.');
+
+ if (dot)
+ {
+ int length = dot - file_name;
+ strncpy (file_name_buffer, file_name, length);
+ file_name_buffer[length] = '\0';
+ }
+ else
+ strcpy (file_name_buffer, file_name);
+ return file_name_buffer;
+}
+
+/* Return the suffix, including the dot, or an empty string if there
+ is no suffix. */
+
+char const *
+suff_name (char const *file_name)
+{
+ char const *dot = strrchr (file_name, '.');
+ return dot ? dot : "";
+}
+
+/* common_prefix_suffix returns non-zero if two file names have a
+ fully common directory prefix and a common suffix (i.e., they're
+ eligible for coalescing with brace notation. */
+
+int
+common_prefix_suffix (struct file_link const *flink_1, struct file_link const *flink_2)
+{
+ return (flink_1->fl_parent == flink_2->fl_parent
+ && strequ (suff_name (flink_1->fl_name), suff_name (flink_2->fl_name)));
+}
+
+void
+print_filenames (struct file_link **flinkv, enum separator_style separator_style)
+{
+ struct file_link const *arg;
+ struct file_link const *dlink;
+ int brace_is_open = 0;
+
+ while (*flinkv)
+ {
+ arg = *flinkv++;
+ if (*flinkv && (separator_style == ss_braces)
+ && common_prefix_suffix (arg, *flinkv))
+ {
+ if (brace_is_open)
+ printf (",%s", root_name (arg->fl_name));
+ else
+ {
+ dlink = arg->fl_parent;
+ if (dlink && dlink != cw_dlink)
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ maybe_relative_file_name (file_name, dlink, cw_dlink);
+ fputs (file_name, stdout);
+ putchar ('/');
+ }
+ printf ("{%s", root_name (arg->fl_name));
+ }
+ brace_is_open = 1;
+ }
+ else
+ {
+ if (brace_is_open)
+ printf (",%s}%s", root_name (arg->fl_name), suff_name (arg->fl_name));
+ else
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ maybe_relative_file_name (file_name, arg, cw_dlink);
+ fputs (file_name, stdout);
+ }
+ brace_is_open = 0;
+ if (*flinkv)
+ {
+ if (separator_style == ss_newline)
+ putchar ('\n');
+ else
+ putchar (' ');
+ }
+ }
+ }
+ putchar ('\n');
+}
+
+enum separator_style
+parse_separator_style (char const *arg)
+{
+ MAYBE_RETURN_PREFIX_MATCH (arg, "braces", ss_braces);
+ MAYBE_RETURN_PREFIX_MATCH (arg, "space", ss_space);
+ MAYBE_RETURN_PREFIX_MATCH (arg, "newline", ss_newline);
+ error (0, 0, _("invalid `--separator' style: `%s'"), arg);
+ usage ();
+ return ss_bogus;
+}
diff --git a/libidu/hash.c b/libidu/hash.c
new file mode 100644
index 0000000..cda26a0
--- /dev/null
+++ b/libidu/hash.c
@@ -0,0 +1,294 @@
+/* hash.c -- hash table maintenance
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ This program 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.
+
+ This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <config.h>
+#include <stdio.h>
+#include "xstdlib.h"
+#include "hash.h"
+#include "xnls.h"
+#include "xmalloc.h"
+#include "error.h"
+
+static void hash_rehash __P((struct hash_table* ht));
+static unsigned long round_up_2 __P((unsigned long rough));
+
+/* Implement double hashing with open addressing. The table size is
+ always a power of two. The secondary (`increment') hash function
+ is forced to return an odd-value, in order to be relatively prime
+ to the table size. This guarantees that the increment can
+ potentially hit every slot in the table during collision
+ resolution. */
+
+void *hash_deleted_item = &hash_deleted_item;
+
+/* Force the table size to be a power of two, possibly rounding up the
+ given size. */
+
+void
+hash_init (struct hash_table* ht, unsigned long size,
+ hash_func_t hash_1, hash_func_t hash_2, hash_cmp_func_t hash_cmp)
+{
+ ht->ht_size = round_up_2 (size);
+ if (ht->ht_size > (128 * 1024)) /* prevent size from getting out of hand */
+ ht->ht_size /= 2;
+ ht->ht_vec = (void**) CALLOC (struct token *, ht->ht_size);
+ if (ht->ht_vec == 0)
+ error (1, 0, _("can't allocate %ld bytes for hash table: memory exhausted"),
+ ht->ht_size * sizeof(struct token *));
+ ht->ht_capacity = ht->ht_size * 15 / 16; /* 93.75% loading factor */
+ ht->ht_fill = 0;
+ ht->ht_collisions = 0;
+ ht->ht_lookups = 0;
+ ht->ht_rehashes = 0;
+ ht->ht_hash_1 = hash_1;
+ ht->ht_hash_2 = hash_2;
+ ht->ht_compare = hash_cmp;
+}
+
+/* Load an array of items into `ht'. */
+
+void
+hash_load (struct hash_table* ht, void *item_table, unsigned long cardinality, unsigned long size)
+{
+ char *items = (char *) item_table;
+ while (cardinality--)
+ {
+ hash_insert (ht, items);
+ items += size;
+ }
+}
+
+/* Returns the address of the table slot matching `key'. If `key' is
+ not found, return the address of an empty slot suitable for
+ inserting `key'. The caller is responsible for incrementing
+ ht_fill on insertion. */
+
+void **
+hash_find_slot (struct hash_table* ht, void const *key)
+{
+ void **slot;
+ void **deleted_slot = 0;
+ unsigned int hash_2 = 0;
+ unsigned int hash_1 = (*ht->ht_hash_1) (key);
+
+ ht->ht_lookups++;
+ for (;;)
+ {
+ hash_1 %= ht->ht_size;
+ slot = &ht->ht_vec[hash_1];
+
+ if (*slot == 0)
+ return slot;
+ if (*slot == hash_deleted_item)
+ {
+ if (deleted_slot == 0)
+ deleted_slot = slot;
+ }
+ else
+ {
+ if (key == *slot)
+ return slot;
+ if ((*ht->ht_compare) (key, *slot) == 0)
+ return slot;
+ ht->ht_collisions++;
+ }
+ if (!hash_2)
+ hash_2 = (*ht->ht_hash_2) (key) | 1;
+ hash_1 += hash_2;
+ }
+}
+
+void *
+hash_find_item (struct hash_table* ht, void const *key)
+{
+ void **slot = hash_find_slot (ht, key);
+ return ((HASH_VACANT (*slot)) ? 0 : *slot);
+}
+
+void *
+hash_insert (struct hash_table* ht, void *item)
+{
+ void **slot = hash_find_slot (ht, item);
+ return hash_insert_at (ht, item, slot);
+}
+
+void *
+hash_insert_at (struct hash_table* ht, void *item, void const *slot)
+{
+ void *old_item = *(void **) slot;
+ if (HASH_VACANT (old_item))
+ {
+ ht->ht_fill++;
+ old_item = item;
+ }
+ *(void const **) slot = item;
+ if (ht->ht_fill >= ht->ht_capacity)
+ hash_rehash (ht);
+ return old_item;
+}
+
+void *
+hash_delete (struct hash_table* ht, void const *item)
+{
+ void **slot = hash_find_slot (ht, item);
+ return hash_delete_at (ht, slot);
+}
+
+void *
+hash_delete_at (struct hash_table* ht, void const *slot)
+{
+ void *item = *(void **) slot;
+ if (!HASH_VACANT (item))
+ {
+ *(void const **) slot = hash_deleted_item;
+ ht->ht_fill--;
+ return item;
+ }
+ else
+ return 0;
+}
+
+void
+hash_free_items (struct hash_table* ht)
+{
+ void **vec = ht->ht_vec;
+ void **end = &vec[ht->ht_size];
+ for (; vec < end; vec++)
+ {
+ void *item = *vec;
+ if (!HASH_VACANT (item))
+ free (item);
+ *vec = 0;
+ }
+ ht->ht_fill = 0;
+}
+
+void
+hash_delete_items (struct hash_table* ht)
+{
+ void **vec = ht->ht_vec;
+ void **end = &vec[ht->ht_size];
+ for (; vec < end; vec++)
+ *vec = 0;
+ ht->ht_fill = 0;
+ ht->ht_collisions = 0;
+ ht->ht_lookups = 0;
+ ht->ht_rehashes = 0;
+}
+
+void
+hash_free (struct hash_table* ht, int free_items)
+{
+ if (free_items)
+ hash_free_items (ht);
+ free (ht->ht_vec);
+ ht->ht_vec = 0;
+ ht->ht_fill = 0;
+ ht->ht_capacity = 0;
+}
+
+void
+hash_map (struct hash_table *ht, hash_map_func_t map)
+{
+ void **slot;
+ void **end = &ht->ht_vec[ht->ht_size];
+
+ for (slot = ht->ht_vec; slot < end; slot++)
+ {
+ if (!HASH_VACANT (*slot))
+ (*map) (*slot);
+ }
+}
+
+/* Double the size of the hash table in the event of overflow... */
+
+static void
+hash_rehash (struct hash_table* ht)
+{
+ unsigned long old_ht_size = ht->ht_size;
+ void **old_vec = ht->ht_vec;
+ void **ovp;
+ void **slot;
+
+ ht->ht_size *= 2;
+ ht->ht_rehashes++;
+ ht->ht_capacity = ht->ht_size - (ht->ht_size >> 4);
+ ht->ht_vec = (void **) CALLOC (struct token *, ht->ht_size);
+
+ for (ovp = old_vec; ovp < &old_vec[old_ht_size]; ovp++)
+ {
+ if (*ovp == 0)
+ continue;
+ slot = hash_find_slot (ht, *ovp);
+ *slot = *ovp;
+ }
+ free (old_vec);
+}
+
+void
+hash_print_stats (struct hash_table *ht, FILE *out_FILE)
+{
+ fprintf (out_FILE, _("Load=%ld/%ld=%.0f%%, "), ht->ht_fill, ht->ht_size,
+ 100.0 * (double) ht->ht_fill / (double) ht->ht_size);
+ fprintf (out_FILE, _("Rehash=%d, "), ht->ht_rehashes);
+ fprintf (out_FILE, _("Collisions=%ld/%ld=%.0f%%"), ht->ht_collisions, ht->ht_lookups,
+ (ht->ht_lookups
+ ? (100.0 * (double) ht->ht_collisions / (double) ht->ht_lookups)
+ : 0));
+}
+
+/* Dump all items into a NULL-terminated vector. Use the
+ user-supplied vector, or malloc one. */
+
+void**
+hash_dump (struct hash_table *ht, void **vector_0, qsort_cmp_t compare)
+{
+ void **vector;
+ void **slot;
+ void **end = &ht->ht_vec[ht->ht_size];
+
+ if (vector_0 == 0)
+ vector_0 = MALLOC (void *, ht->ht_fill + 1);
+ vector = vector_0;
+
+ for (slot = ht->ht_vec; slot < end; slot++)
+ if (!HASH_VACANT (*slot))
+ *vector++ = *slot;
+ *vector = 0;
+
+ if (compare)
+ qsort (vector_0, ht->ht_fill, sizeof (void *), compare);
+ return vector_0;
+}
+
+/* Round a given number up to the nearest power of 2. */
+
+static unsigned long
+round_up_2 (unsigned long rough)
+{
+ int round;
+
+ round = 1;
+ while (rough)
+ {
+ round <<= 1;
+ rough >>= 1;
+ }
+ return round;
+}
diff --git a/libidu/hash.h b/libidu/hash.h
new file mode 100644
index 0000000..5661893
--- /dev/null
+++ b/libidu/hash.h
@@ -0,0 +1,144 @@
+/* hash.h -- decls for hash table
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ This program 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.
+
+ This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _hash_h_
+#define _hash_h_
+
+#include <stdio.h>
+
+typedef unsigned long (*hash_func_t) __P((void const *key));
+typedef int (*hash_cmp_func_t) __P((void const *x, void const *y));
+typedef void (*hash_map_func_t) __P((void const *item));
+
+struct hash_table
+{
+ void **ht_vec;
+ unsigned long ht_size; /* total number of slots (power of 2) */
+ unsigned long ht_capacity; /* usable slots, limited by loading-factor */
+ unsigned long ht_fill; /* items in table */
+ unsigned long ht_collisions; /* # of failed calls to comparison function */
+ unsigned long ht_lookups; /* # of queries */
+ unsigned int ht_rehashes; /* # of times we've expanded table */
+ hash_func_t ht_hash_1; /* primary hash function */
+ hash_func_t ht_hash_2; /* secondary hash function */
+ hash_cmp_func_t ht_compare; /* comparison function */
+};
+
+typedef int (*qsort_cmp_t) __P((void const *, void const *));
+
+void hash_init __P((struct hash_table *ht, unsigned long size,
+ hash_func_t hash_1, hash_func_t hash_2, hash_cmp_func_t hash_cmp));
+void hash_load __P((struct hash_table *ht, void *item_table,
+ unsigned long cardinality, unsigned long size));
+void **hash_find_slot __P((struct hash_table *ht, void const *key));
+void *hash_find_item __P((struct hash_table *ht, void const *key));
+void *hash_insert __P((struct hash_table *ht, void *item));
+void *hash_insert_at __P((struct hash_table *ht, void *item, void const *slot));
+void *hash_delete __P((struct hash_table *ht, void const *item));
+void *hash_delete_at __P((struct hash_table *ht, void const *slot));
+void hash_delete_items __P((struct hash_table *ht));
+void hash_free_items __P((struct hash_table *ht));
+void hash_free __P((struct hash_table *ht, int free_items));
+void hash_map __P((struct hash_table *ht, hash_map_func_t map));
+void hash_print_stats __P((struct hash_table *ht, FILE *out_FILE));
+void **hash_dump __P((struct hash_table *ht, void **vector_0, qsort_cmp_t compare));
+
+extern void *hash_deleted_item;
+#define HASH_VACANT(item) ((item) == 0 || (void *) (item) == hash_deleted_item)
+
+
+/* hash and comparison macros for string keys. */
+
+#define STRING_HASH_1(_key_, _result_) { \
+ unsigned char const *kk = (unsigned char const *) (_key_) - 1; \
+ while (*++kk) \
+ (_result_) += (*kk << (kk[1] & 0xf)); \
+} while (0)
+#define return_STRING_HASH_1(_key_) do { \
+ unsigned long result = 0; \
+ STRING_HASH_1 ((_key_), result); \
+ return result; \
+} while (0)
+
+#define STRING_HASH_2(_key_, _result_) do { \
+ unsigned char const *kk = (unsigned char const *) (_key_) - 1; \
+ while (*++kk) \
+ (_result_) += (*kk << (kk[1] & 0x7)); \
+} while (0)
+#define return_STRING_HASH_2(_key_) do { \
+ unsigned long result = 0; \
+ STRING_HASH_2 ((_key_), result); \
+ return result; \
+} while (0)
+
+#define STRING_COMPARE(_x_, _y_, _result_) do { \
+ unsigned char const *xx = (unsigned char const *) (_x_) - 1; \
+ unsigned char const *yy = (unsigned char const *) (_y_) - 1; \
+ do { \
+ if (*++xx == '\0') { \
+ yy++; \
+ break; \
+ } \
+ } while (*xx == *++yy); \
+ (_result_) = *xx - *yy; \
+} while (0)
+#define return_STRING_COMPARE(_x_, _y_) do { \
+ int result; \
+ STRING_COMPARE (_x_, _y_, result); \
+ return result; \
+} while (0)
+
+/* hash and comparison macros for integer keys. */
+
+#define INTEGER_HASH_1(_key_, _result_) do { \
+ (_result_) += ((unsigned long)(_key_)); \
+} while (0)
+#define return_INTEGER_HASH_1(_key_) do { \
+ unsigned long result = 0; \
+ INTEGER_HASH_1 ((_key_), result); \
+ return result; \
+} while (0)
+
+#define INTEGER_HASH_2(_key_, _result_) do { \
+ (_result_) += ~((unsigned long)(_key_)); \
+} while (0)
+#define return_INTEGER_HASH_2(_key_) do { \
+ unsigned long result = 0; \
+ INTEGER_HASH_2 ((_key_), result); \
+ return result; \
+} while (0)
+
+#define INTEGER_COMPARE(_x_, _y_, _result_) do { \
+ (_result_) = _x_ - _y_; \
+} while (0)
+#define return_INTEGER_COMPARE(_x_, _y_) do { \
+ int result; \
+ INTEGER_COMPARE (_x_, _y_, result); \
+ return result; \
+} while (0)
+
+/* hash and comparison macros for address keys. */
+
+#define ADDRESS_HASH_1(_key_, _result_) INTEGER_HASH_1 (((unsigned long)(_key_)) >> 3, (_result_))
+#define ADDRESS_HASH_2(_key_, _result_) INTEGER_HASH_2 (((unsigned long)(_key_)) >> 3, (_result_))
+#define ADDRESS_COMPARE(_x_, _y_, _result_) INTEGER_COMPARE ((_x_), (_y_), (_result_))
+#define return_ADDRESS_HASH_1(_key_) return_INTEGER_HASH_1 (((unsigned long)(_key_)) >> 3)
+#define return_ADDRESS_HASH_2(_key_) return_INTEGER_HASH_2 (((unsigned long)(_key_)) >> 3)
+#define return_ADDRESS_COMPARE(_x_, _y_) return_INTEGER_COMPARE ((_x_), (_y_))
+
+#endif /* not _hash_h_ */
diff --git a/libidu/id-lang.map b/libidu/id-lang.map
new file mode 100644
index 0000000..e230cb6
--- /dev/null
+++ b/libidu/id-lang.map
@@ -0,0 +1,88 @@
+# Welcome to the mkid language mapper.
+#
+# The format of each line is:
+#
+# <pattern> <language> [options]
+#
+# Filenames are matched top-to-bottom against the patterns, and the
+# first match is chosen. The special language `IGNORE' means that
+# this file should be ignored by mkid. The options are
+# language-specific command-line options to mkid.
+#
+# If a file name doesn't match any pattern, it is assigned the default
+# language. The default language may be specified here with the
+# special pattern `**', or overridden from the mkid command-line with
+# the `--default-lang=LANG' option.
+#
+# The special pattern `***' means to include the named file that
+# immediately follows. If no file is named, then the default system
+# language mapper file (i.e., this file) is included.
+
+# Default language
+** IGNORE # Although this is listed first,
+ # the default language pattern is
+ # logically matched last.
+
+# Backup files
+*~ IGNORE
+*.bak IGNORE
+*.bk[0-9] IGNORE
+
+# SCCS files
+[sp].* IGNORE
+
+# C dependencies created by automake
+*/.deps/* IGNORE
+
+*.h C
+*.h.in C
+*.H C++
+*.hh C++
+*.hpp C++
+*.hxx C++
+
+*.l C
+*.lex C
+*.y C
+*.yacc C
+
+*.c C
+*.C C++
+*.cc C++
+*.cpp C++
+*.cxx C++
+
+ChangeLog* Cdoc
+
+*.[sS] asm --comment=;
+*.asm asm --comment=;
+
+# [nt]roff
+*.[0-9] roff
+*.ms roff
+*.me roff
+*.mm roff
+
+*.tex TeX
+*.ltx TeX
+*.texi texinfo
+*.texinfo texinfo
+
+# portable object (i18n)
+*.po po
+
+*.el elisp
+
+*.am make
+Makefile make
+Makefile.* make
+
+*.doc text
+*.txt text
+
+*.m4 m4
+
+*.pl perl
+
+*.gz FILTER gzip -d <%s
+*.Z FILTER gzip -d <%s
diff --git a/libidu/idfile.c b/libidu/idfile.c
new file mode 100644
index 0000000..e377145
--- /dev/null
+++ b/libidu/idfile.c
@@ -0,0 +1,137 @@
+/* idfile.c -- read & write mkid database file header
+ Copyright (C) 1986, 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ This program 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.
+
+ This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <config.h>
+#include <stdio.h>
+#include <errno.h>
+#include "xstdlib.h"
+#include "xstring.h"
+#include "xsysstat.h"
+#include "xnls.h"
+#include "xobstack.h"
+#include "idfile.h"
+#include "error.h"
+
+int io_size __P((FILE *, void *, unsigned int size, int));
+
+
+/****************************************************************************/
+
+/* Discover the name of the ID file. If ARG is NULL, consult $IDPATH.
+ If $IDPATH is undefined, default to "ID". If the candidate file
+ name is relative, search successive ancestor directories until the
+ file is found or we reach the root. If we find it, return the
+ relative file name, otherwise return NULL. */
+
+char const *
+locate_id_file_name (char const *arg)
+{
+ static char file_name_buffer[BUFSIZ];
+ char *buf = file_name_buffer;
+ char *id_path = 0;
+ struct stat rootb;
+ struct stat statb;
+
+ if (arg == 0)
+ {
+ id_path = getenv ("IDPATH");
+ if (id_path)
+ {
+ id_path = strdup (id_path);
+ arg = strtok (id_path, ":");
+ /* FIXME: handle multiple ID file names */
+ }
+ }
+ if (arg == 0)
+ arg = DEFAULT_ID_FILE_NAME;
+
+ /* if we got absolute name, just use it. */
+ if (arg[0] == '/')
+ return arg;
+ /* if the name we were give exists, don't bother searching */
+ if (stat (arg, &statb) == 0)
+ return arg;
+ /* search up the tree until we find a directory where this
+ * relative file name is visible.
+ * (or we run out of tree to search by hitting root).
+ */
+
+ if (stat ("/", &rootb) != 0)
+ return NULL;
+ do
+ {
+ strcpy (buf, "../");
+ buf += 3;
+ strcpy (buf, arg);
+ if (stat (file_name_buffer, &statb) == 0)
+ return file_name_buffer;
+ *buf = '\0';
+ if (stat (file_name_buffer, &statb) != 0)
+ return NULL;
+ }
+ while (!((statb.st_ino == rootb.st_ino) ||
+ (statb.st_dev == rootb.st_dev)));
+ return NULL;
+}
+
+
+/****************************************************************************/
+
+int
+sizeof_idhead ()
+{
+ return io_idhead (0, io_size, 0);
+}
+
+int
+io_size (FILE *ignore_FILE, void *ignore_addr, unsigned int size, int io_type)
+{
+ if (io_type == IO_TYPE_STR)
+ error (0, 0, _("can't determine the io_size of a string!"));
+ return size;
+}
+
+/* The sizes of the fields must be hard-coded. They aren't
+ necessarily the sizes of the struct members, because some
+ architectures don't have any way to declare 4-byte integers
+ (e.g., Cray) */
+
+int
+io_idhead (FILE *fp, io_func_t iof, struct idhead *idhp)
+{
+ unsigned int size = 0;
+ unsigned char pad = 0;
+ if (fp)
+ fseek (fp, 0L, 0);
+ size += iof (fp, idhp->idh_magic, 2, IO_TYPE_FIX);
+ size += iof (fp, &pad, 1, IO_TYPE_FIX);
+ size += iof (fp, &idhp->idh_version, 1, IO_TYPE_FIX);
+ size += iof (fp, &idhp->idh_flags, 2, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_file_links, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_files, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_tokens, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_buf_size, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_vec_size, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_tokens_offset, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_flinks_offset, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_end_offset, 4, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_max_link, 2, IO_TYPE_INT);
+ size += iof (fp, &idhp->idh_max_path, 2, IO_TYPE_INT);
+ return size;
+}
diff --git a/libidu/idfile.h b/libidu/idfile.h
new file mode 100644
index 0000000..fb108a8
--- /dev/null
+++ b/libidu/idfile.h
@@ -0,0 +1,223 @@
+/* idfile.h -- decls for ID file header and constituent file names
+ Copyright (C) 1986, 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ This program 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.
+
+ This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _idfile_h_
+#define _idfile_h_ 1
+
+#include <config.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <stdio.h>
+#include "xobstack.h"
+#include "hash.h"
+#include "dynvec.h"
+#include "tokflags.h"
+
+
+/****************************************************************************/
+
+/* The ID file header is the nexus of all ID file information. This
+ is an in-core structure, only some of which is read/written to disk. */
+
+struct idhead
+{
+ unsigned char idh_magic[2];
+#define IDH_MAGIC_0 ('I'|0x80)
+#define IDH_MAGIC_1 ('D'|0x80)
+ unsigned char idh_version;
+#define IDH_VERSION 4
+ unsigned short idh_flags;
+#define IDH_COUNTS (1<<0) /* include occurrence counts for each token */
+#define IDH_FOLLOW_SL (1<<1) /* follow symlinks to directories */
+#define IDH_COMMENTS (1<<2) /* include tokens found in comments */
+#define IDH_LOCALS (1<<3) /* include names of formal params & local vars */
+#define IDH_DECL_DEFN_USE (1<<4) /* include decl/defn/use info */
+#define IDH_L_R_VALUE (1<<5) /* include lvalue/rvalue info */
+#define IDH_CALL_ER_EE (1<<6) /* include caller/callee relationship info */
+ unsigned long idh_file_links; /* total # of file links */
+ unsigned long idh_files; /* total # of constituent source files */
+ unsigned long idh_tokens; /* total # of constituent tokens */
+ /* idh_*_size: max buffer-sizes for ID file reading programs */
+ unsigned long idh_buf_size; /* # of bytes in longest entry */
+ unsigned long idh_vec_size; /* # of hits in longest entry */
+ /* idh_*_offset: ID file offsets for start of various sections */
+ long idh_tokens_offset; /* constituent tokens section */
+ long idh_flinks_offset; /* constituent file & directory names section */
+ long idh_end_offset; /* end of tokens section */
+ unsigned short idh_max_link; /* longest file name component */
+ unsigned short idh_max_path; /* largest # of file name components */
+
+ /* The following are run-time variables and are not stored on disk */
+ char const *idh_file_name;
+ struct hash_table idh_member_file_table;
+ struct hash_table idh_file_link_table;
+ struct obstack idh_member_file_obstack;
+ struct obstack idh_file_link_obstack;
+#if HAVE_LINK
+ struct hash_table idh_dev_ino_table; /* for detecting file name aliases */
+ struct obstack idh_dev_ino_obstack;
+#endif
+ FILE *idh_FILE;
+};
+
+/* idhead input/output definitions */
+
+#define IO_TYPE_INT 0 /* integer */
+#define IO_TYPE_STR 1 /* NUL terminated string */
+#define IO_TYPE_FIX 2 /* fix-sized */
+
+
+/****************************************************************************/
+
+/* A file_link represents a single component (file or directory) in a
+ file name. It has a name, a parent file_link and some flags. */
+
+struct file_link
+{
+ union {
+ struct file_link *u_parent;
+#define fl_parent fl_u.u_parent
+ unsigned long u_index;
+#define fl_index fl_u.u_index
+#define FL_PARENT_INDEX_BYTES 3
+#define IS_ROOT_FILE_LINK(flink) ((flink)->fl_parent == (flink))
+ } fl_u;
+ unsigned char fl_flags;
+#define FL_CMD_LINE_ARG (1<<0)
+#define FL_USED (1<<1)
+#define FL_MEMBER (1<<2) /* has a corresponding member_file entry */
+#define FL_SCAN_ME (1<<3)
+#define FL_SYM_LINK (1<<4)
+#define FL_TYPE_MASK (FL_TYPE_DIR|FL_TYPE_FILE)
+# define FL_TYPE_DIR (1<<5)
+# define FL_IS_DIR(_f_) (((_f_) & FL_TYPE_MASK) == FL_TYPE_DIR)
+# define FL_TYPE_FILE (1<<6)
+# define FL_IS_FILE(_f_) (((_f_) & FL_TYPE_MASK) == FL_TYPE_FILE)
+#define FL_PRUNE (1<<7)
+ char fl_name[1];
+};
+
+/* A member_file represents a source file that is treated by mkid. */
+
+struct member_file
+{
+ struct file_link *mf_link;
+ struct lang_args const *mf_lang_args;
+ short mf_index; /* order in ID file */
+};
+
+#if HAVE_LINK
+
+/* On systems that support multiple names for a single file (via hard
+ and/or soft links), dev_ino records information needed to detect
+ such aliasing. */
+
+struct dev_ino
+{
+ dev_t di_dev;
+ ino_t di_ino;
+ struct file_link *di_link;
+};
+
+extern struct hash_table dev_ino_table;
+
+#endif /* HAVE_LINK */
+
+
+/******************************************************************************/
+/* token flags (struct token is defined in scanners.h) */
+
+#define token_string(buf) (buf)
+extern unsigned int token_flags __P((char const *buf));
+extern unsigned short token_count __P((char const *buf));
+extern unsigned char const *token_hits_addr __P((char const *buf));
+
+#define MAYBE_RETURN_PREFIX_MATCH(arg, str, val) do { \
+ char const *_s_ = (str); \
+ if (strstr (_s_, (arg)) == _s_) \
+ return (val); \
+ } while (0)
+
+enum separator_style
+{
+ ss_bogus,
+ ss_contextual,
+ ss_braces,
+ ss_space,
+ ss_newline
+};
+
+#ifndef DEFAULT_SEPARATOR_STYLE
+#define DEFAULT_SEPARATOR_STYLE ss_braces
+#endif
+
+typedef int (*io_func_t) __P((FILE *, void *, unsigned int, int));
+
+extern struct file_link **read_id_file __P((char const *id_file_name, struct idhead *idhp));
+extern struct file_link **maybe_read_id_file __P((char const *id_file_name, struct idhead *idhp));
+extern int read_idhead __P((struct idhead *idhp));
+extern int write_idhead __P((struct idhead *idhp));
+extern int sizeof_idhead __P((void));
+struct file_link *init_walker __P((struct idhead *idhp));
+extern void init_idh_obstacks __P((struct idhead *idhp));
+extern void init_idh_tables __P((struct idhead *idhp));
+
+extern int io_write __P((FILE *output_FILE, void *addr, unsigned int size, int io_type));
+extern int io_read __P((FILE *input_FILE, void *addr, unsigned int size, int io_type));
+extern int io_idhead __P((FILE *fp, io_func_t iof, struct idhead *idhp));
+
+extern struct file_link *get_current_dir_link __P((void));
+extern struct file_link **deserialize_file_links __P((struct idhead *idhp));
+extern void serialize_file_links __P((struct idhead *idhp));
+
+extern void mark_member_file_links __P((struct idhead *idhp));
+extern int member_file_qsort_compare __P((void const *x, void const *y));
+extern struct file_link *parse_file_name __P((char *file_name,
+ struct file_link *relative_dir_link));
+extern void print_filenames __P((struct file_link **flinkv,
+ enum separator_style separator_style));
+extern enum separator_style parse_separator_style __P((char const *arg));
+
+extern void walk_flink __P((struct file_link *flink, struct dynvec *sub_dirs_vec));
+extern int chdir_to_link __P((struct file_link* dir_link));
+void prune_file_names __P((char *str, struct file_link *from_link));
+char **vectorize_string __P((char *string, char *delimiter_class));
+void include_languages __P((char *lang_names));
+void exclude_languages __P((char *lang_names));
+
+extern char *absolute_file_name __P((char *buffer, struct file_link const *flink));
+extern char *maybe_relative_file_name __P((char *buffer, struct file_link const *to_link,
+ struct file_link const *from_link));
+extern char const *locate_id_file_name __P((char const *arg));
+
+extern int tree8_count_levels __P((unsigned int cardinality));
+extern int gets_past_00 __P((char *tok, FILE *input_FILE));
+extern int skip_past_00 __P((FILE *input_FILE));
+
+extern int links_depth __P((struct file_link const *flink));
+#if HAVE_LINK
+extern struct member_file *find_member_file __P((struct file_link const *flink));
+#endif
+
+extern struct idhead idh;
+
+#define DEFAULT_ID_FILE_NAME "ID"
+
+#endif /* not _idfile_h_ */
diff --git a/libidu/idread.c b/libidu/idread.c
new file mode 100644
index 0000000..c725f9f
--- /dev/null
+++ b/libidu/idread.c
@@ -0,0 +1,280 @@
+/* idread.c -- functions to read ID database files
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ This program 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.
+
+ This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <config.h>
+#include <stdio.h>
+#include "idfile.h"
+#include "xstddef.h"
+#include "hash.h"
+#include "error.h"
+#include "xobstack.h"
+#include "xmalloc.h"
+#include "xnls.h"
+
+int fgets0 __P((char *buf0, int size, FILE *in_FILE));
+
+
+/****************************************************************************/
+
+/* read_id_file opens the ID file, reads header fields into idh,
+ verifies the magic number and version, and reads the constituent
+ file names. Any errors are considered fatal and cause an exit. */
+
+struct file_link **
+read_id_file (char const *id_file_name, struct idhead *idhp)
+{
+ struct file_link **flinkv = maybe_read_id_file (id_file_name, idhp);
+ if (flinkv)
+ return flinkv;
+ error (1, errno, _("can't open `%s'"), id_file_name);
+ return NULL;
+}
+
+/* maybe_read_id_file does everything that read_id_file does, but is
+ tolerant of errors opening the ID file, returning NULL in this case
+ (this is called from mkid where an ID might or might not already
+ exist). All other errors are considered fatal. */
+
+struct file_link **
+maybe_read_id_file (char const *id_file_name, struct idhead *idhp)
+{
+ obstack_init (&idhp->idh_file_link_obstack);
+ idhp->idh_FILE = fopen (id_file_name, "r");
+ if (idhp->idh_FILE == 0)
+ return 0;
+
+ read_idhead (idhp);
+ if (idhp->idh_magic[0] != IDH_MAGIC_0 || idhp->idh_magic[1] != IDH_MAGIC_1)
+ error (1, 0, _("`%s' is not an ID file! (bad magic #)"), id_file_name);
+ if (idhp->idh_version != IDH_VERSION)
+ error (1, 0, _("`%s' is version %d, but I only grok version %d"),
+ id_file_name, idhp->idh_version, IDH_VERSION);
+
+ fseek (idhp->idh_FILE, idhp->idh_flinks_offset, 0);
+ return deserialize_file_links (idhp);
+}
+
+
+/****************************************************************************/
+
+/* Read and reconstruct a serialized file_link hierarchy. */
+
+struct file_link **
+deserialize_file_links (struct idhead *idhp)
+{
+ struct file_link **flinks_0 = MALLOC (struct file_link *, idhp->idh_file_links);
+ struct file_link **flinks = flinks_0;
+ struct file_link **members_0 = MALLOC (struct file_link *, idhp->idh_files + 1);
+ struct file_link **members = members_0;
+ struct file_link *flink;
+ struct file_link **slot;
+ int i;
+
+ for (i = 0; i < idhp->idh_file_links; i++)
+ {
+ unsigned long parent_index;
+ int c;
+
+ obstack_blank (&idhp->idh_file_link_obstack, offsetof (struct file_link, fl_name));
+ if (obstack_room (&idhp->idh_file_link_obstack) >= idhp->idh_max_link)
+ do
+ {
+ c = getc (idhp->idh_FILE);
+ obstack_1grow_fast (&idhp->idh_file_link_obstack, c);
+ }
+ while (c);
+ else
+ do
+ {
+ c = getc (idhp->idh_FILE);
+ obstack_1grow (&idhp->idh_file_link_obstack, c);
+ }
+ while (c);
+ flink = (struct file_link *) obstack_finish (&idhp->idh_file_link_obstack);
+ *flinks = flink;
+ io_read (idhp->idh_FILE, &flink->fl_flags, sizeof (flink->fl_flags), IO_TYPE_INT);
+ io_read (idhp->idh_FILE, &parent_index, FL_PARENT_INDEX_BYTES, IO_TYPE_INT);
+ flink->fl_parent = flinks_0[parent_index];
+ slot = (struct file_link **) hash_find_slot (&idhp->idh_file_link_table, flink);
+ if (HASH_VACANT (*slot))
+ hash_insert_at (&idhp->idh_file_link_table, flink, slot);
+ else
+ {
+ obstack_free (&idhp->idh_file_link_obstack, flink);
+ (*slot)->fl_flags = flink->fl_flags;
+ flink = *flinks = *slot;
+ }
+ flinks++;
+ if (flink->fl_flags & FL_MEMBER)
+ *members++ = flink;
+ }
+ free (flinks_0);
+ *members = 0;
+ return members_0;
+}
+
+
+/****************************************************************************/
+
+int
+read_idhead (struct idhead *idhp)
+{
+ return io_idhead (idhp->idh_FILE, io_read, idhp);
+}
+
+/* This is like fgets(3s), except that lines are delimited by NULs
+ rather than newlines. Also, we return the number of characters
+ read rather than the address of buf0. */
+
+int
+fgets0 (char *buf0, int size, FILE * in_FILE)
+{
+ char *buf;
+ int c;
+ char *end;
+
+ buf = buf0;
+ end = &buf[size];
+ while ((c = getc (in_FILE)) > 0 && buf < end)
+ *buf++ = c;
+ *buf = '\0';
+ return (buf - buf0);
+}
+
+int
+io_read (FILE *input_FILE, void *addr, unsigned int size, int io_type)
+{
+ if (io_type == IO_TYPE_INT || size == 1)
+ {
+ switch (size)
+ {
+ case 4:
+ *(unsigned long *)addr = getc (input_FILE);
+ *(unsigned long *)addr += getc (input_FILE) << 010;
+ *(unsigned long *)addr += getc (input_FILE) << 020;
+ *(unsigned long *)addr += getc (input_FILE) << 030;
+ break;
+ case 3:
+ *(unsigned long *)addr = getc (input_FILE);
+ *(unsigned long *)addr += getc (input_FILE) << 010;
+ *(unsigned long *)addr += getc (input_FILE) << 020;
+ break;
+ case 2:
+ *(unsigned short *)addr = getc (input_FILE);
+ *(unsigned short *)addr += getc (input_FILE) << 010;
+ break;
+ case 1:
+ *(unsigned char *)addr = getc (input_FILE);
+ break;
+ default:
+ error (1, 0, _("unsupported size in io_read (): %d"), size);
+ }
+ }
+ else if (io_type == IO_TYPE_STR)
+ fgets0 (addr, size, input_FILE);
+ else if (io_type == IO_TYPE_FIX)
+ fread (addr, size, 1, input_FILE);
+ else
+ error (0, 0, _("unknown I/O type: %d"), io_type);
+ return size;
+}
+
+
+/****************************************************************************/
+
+unsigned int
+token_flags (char const *buf)
+{
+ return *(unsigned char const *)&buf[strlen (buf) + 1];
+}
+
+#define TOK_COUNT_ADDR(buf) ((unsigned char const *)(TOK_FLAGS_ADDR (buf) + 1))
+#define TOK_HITS_ADDR(buf) ((unsigned char const *)(TOK_COUNT_ADDR (buf) + 2))
+
+unsigned short
+token_count (char const *buf)
+{
+ unsigned char const *flags = (unsigned char const *)&buf[strlen (buf) + 1];
+ unsigned char const *addr = flags + 1;
+ unsigned short count = *addr;
+ if (*flags & TOK_SHORT_COUNT)
+ count += (*++addr << 8);
+ return count;
+}
+
+unsigned char const *
+token_hits_addr (char const *buf)
+{
+ unsigned char const *flags = (unsigned char const *)&buf[strlen (buf) + 1];
+ unsigned char const *addr = flags + 2;
+ if (*flags & TOK_SHORT_COUNT)
+ addr++;
+ return addr;
+}
+
+
+
+/****************************************************************************/
+
+int
+tree8_count_levels (unsigned int cardinality)
+{
+ int levels = 1;
+ cardinality--;
+ while (cardinality >>= 3)
+ ++levels;
+ return levels;
+}
+
+int
+gets_past_00 (char *tok, FILE *input_FILE)
+{
+ int got = 0;
+ int c;
+ do
+ {
+ do
+ {
+ got++;
+ c = getc (input_FILE);
+ *tok++ = c;
+ }
+ while (c > 0);
+ got++;
+ c = getc (input_FILE);
+ *tok++ = c;
+ }
+ while (c > 0);
+ return got - 2;
+}
+
+int
+skip_past_00 (FILE *input_FILE)
+{
+ int skipped = 0;
+ do
+ {
+ do
+ skipped++;
+ while (getc (input_FILE) > 0);
+ skipped++;
+ }
+ while (getc (input_FILE) > 0);
+ return skipped;
+}
diff --git a/libidu/idwrite.c b/libidu/idwrite.c
new file mode 100644
index 0000000..6257302
--- /dev/null
+++ b/libidu/idwrite.c
@@ -0,0 +1,169 @@
+/* idwrite.c -- functions to write ID database files
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ This program 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.
+
+ This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <config.h>
+#include "idfile.h"
+#include "hash.h"
+#include "xobstack.h"
+#include "xmalloc.h"
+#include "xnls.h"
+#include "error.h"
+
+int file_link_qsort_compare __P((void const *x, void const *y));
+
+
+/****************************************************************************/
+/* Serialize and write a file_link hierarchy. */
+
+void
+serialize_file_links (struct idhead *idhp)
+{
+ struct file_link **flinks_0;
+ struct file_link **flinks;
+ struct file_link **end;
+ struct file_link **parents_0;
+ struct file_link **parents;
+ unsigned long parent_index = 0;
+ int max_link = 0;
+
+ flinks_0 = (struct file_link **) hash_dump (&idhp->idh_file_link_table,
+ 0, file_link_qsort_compare);
+ end = &flinks_0[idhp->idh_file_link_table.ht_fill];
+ parents = parents_0 = MALLOC (struct file_link *, idhp->idh_file_link_table.ht_fill);
+ for (flinks = flinks_0; flinks < end; flinks++)
+ {
+ struct file_link *flink = *flinks;
+ int name_length;
+
+ if (!(flink->fl_flags & FL_USED))
+ break;
+ name_length = strlen (flink->fl_name);
+ if (name_length > max_link)
+ max_link = name_length;
+ io_write (idhp->idh_FILE, flink->fl_name, 0, IO_TYPE_STR);
+ io_write (idhp->idh_FILE, &flink->fl_flags, sizeof (flink->fl_flags), IO_TYPE_INT);
+ io_write (idhp->idh_FILE, (IS_ROOT_FILE_LINK (flink)
+ ? &parent_index : &flink->fl_parent->fl_index),
+ FL_PARENT_INDEX_BYTES, IO_TYPE_INT);
+ *parents++ = flink->fl_parent; /* save parent link before clobbering */
+ flink->fl_index = parent_index++;
+ }
+ /* restore parent links */
+ for ((flinks = flinks_0), (parents = parents_0); flinks < end; flinks++)
+ {
+ struct file_link *flink = *flinks;
+ if (!(flink->fl_flags & FL_USED))
+ break;
+ flink->fl_parent = *parents++;
+ }
+ free (parents_0);
+ free (flinks_0);
+ idhp->idh_max_link = max_link + 1;
+ idhp->idh_file_links = parent_index;
+ idhp->idh_files = idhp->idh_member_file_table.ht_fill;
+}
+
+/* Collation sequence:
+ - Used before unused.
+ - Among used: breadth-first (dirs before files, parent dirs before children)
+ - Among files: collate by mf_index. */
+
+int
+file_link_qsort_compare (void const *x, void const *y)
+{
+ struct file_link const *flx = *(struct file_link const *const *) x;
+ struct file_link const *fly = *(struct file_link const *const *) y;
+ unsigned int x_flags = flx->fl_flags;
+ unsigned int y_flags = fly->fl_flags;
+ int result;
+
+ result = (y_flags & FL_USED) - (x_flags & FL_USED);
+ if (result)
+ return result;
+ if (!(x_flags & FL_USED)) /* If neither link is used, we don't care... */
+ return 0;
+ result = (y_flags & FL_TYPE_DIR) - (x_flags & FL_TYPE_DIR);
+ if (result)
+ return result;
+ result = (y_flags & FL_TYPE_MASK) - (x_flags & FL_TYPE_MASK);
+ if (result)
+ return result;
+ if (FL_IS_FILE (x_flags))
+ {
+ struct member_file *x_member = find_member_file (flx);
+ struct member_file *y_member = find_member_file (fly);
+ return x_member->mf_index - y_member->mf_index;
+ }
+ else
+ {
+ int x_depth = links_depth (flx);
+ int y_depth = links_depth (fly);
+ return (x_depth - y_depth);
+ }
+}
+
+
+/****************************************************************************/
+
+int
+write_idhead (struct idhead *idhp)
+{
+ return io_idhead (idhp->idh_FILE, io_write, idhp);
+}
+
+int
+io_write (FILE *output_FILE, void *addr, unsigned int size, int io_type)
+{
+ if (io_type == IO_TYPE_INT || size == 1)
+ {
+ switch (size)
+ {
+ case 4:
+ putc (*(unsigned long *)addr, output_FILE);
+ putc (*(unsigned long *)addr >> 010, output_FILE);
+ putc (*(unsigned long *)addr >> 020, output_FILE);
+ putc (*(unsigned long *)addr >> 030, output_FILE);
+ break;
+ case 3:
+ putc (*(unsigned long *)addr, output_FILE);
+ putc (*(unsigned long *)addr >> 010, output_FILE);
+ putc (*(unsigned long *)addr >> 020, output_FILE);
+ break;
+ case 2:
+ putc (*(unsigned short *)addr, output_FILE);
+ putc (*(unsigned short *)addr >> 010, output_FILE);
+ break;
+ case 1:
+ putc (*(unsigned char *)addr, output_FILE);
+ break;
+ default:
+ error (1, 0, _("unsupported size in io_write (): %d"), size);
+ }
+ }
+ else if (io_type == IO_TYPE_STR)
+ {
+ fputs (addr, output_FILE);
+ putc ('\0', output_FILE);
+ }
+ else if (io_type == IO_TYPE_FIX)
+ fwrite (addr, size, 1, output_FILE);
+ else
+ error (0, 0, _("unknown I/O type: %d"), io_type);
+ return size;
+}
diff --git a/libidu/scanners.c b/libidu/scanners.c
new file mode 100644
index 0000000..af48c9f
--- /dev/null
+++ b/libidu/scanners.c
@@ -0,0 +1,1212 @@
+/* scanners.c -- file & directory name manipulations
+ Copyright (C) 1986, 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ This program 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.
+
+ This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <config.h>
+#include <stdio.h>
+#include <ctype.h>
+#if HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#include <getopt.h>
+#include "xstdlib.h"
+#include "xstddef.h"
+#include "xunistd.h"
+#include "xsysstat.h"
+#include "xstring.h"
+#include "xmalloc.h"
+#include "xnls.h"
+#include "error.h"
+#include "scanners.h"
+#include "tokflags.h"
+
+#define DEBUG(args) /* printf args */
+
+struct obstack lang_args_obstack;
+struct lang_args *lang_args_default = 0;
+struct lang_args *lang_args_list = 0;
+struct obstack tokens_obstack;
+
+extern void usage __P((void));
+extern char *program_name;
+
+/****************************************************************************/
+
+struct lang_args **parse_language_map_file __P((char const *file_name, struct lang_args **next_ptr));
+char *read_language_map_file __P((char const *file_name));
+void tokenize_args_string __P((char *args_string, int *argcp, char ***argvp));
+
+static struct token *get_token_c __P((FILE *in_FILE, void const *args, int *flags));
+static void *parse_args_c __P((char **argv, int argc));
+static void help_me_c __P((void));
+
+static struct token *get_token_asm __P((FILE *in_FILE, void const *args, int *flags));
+static void *parse_args_asm __P((char **argv, int argc));
+static void help_me_asm __P((void));
+
+static struct token *get_token_text __P((FILE *in_FILE, void const *args, int *flags));
+static void *parse_args_text __P((char **argv, int argc));
+static void help_me_text __P((void));
+
+struct language languages_0[] =
+{
+ { "C", parse_args_c, get_token_c, help_me_c },
+ { "asm", parse_args_asm, get_token_asm, help_me_asm },
+ { "text", parse_args_text, get_token_text, help_me_text },
+};
+struct language const *languages_N = &languages_0[cardinalityof (languages_0)];
+
+void
+language_help_me (void)
+{
+ struct language *lang;
+ for (lang = languages_0; lang < languages_N; lang++)
+ {
+ putchar ('\n');
+ (*lang->lg_help_me) ();
+ }
+}
+
+void
+language_save_arg (char *arg)
+{
+ static char horizontal_space[] = " \t";
+ char *lang_name = strtok (arg, ":");
+ struct language *lang = get_language (lang_name);
+
+ if (lang == 0)
+ {
+ error (0, 0, _("unrecognized language: `%s'"), lang_name);
+ usage ();
+ }
+ if (lang->lg_argc == 0)
+ lang->lg_argv[lang->lg_argc++] = program_name;
+ lang->lg_argv[lang->lg_argc++] = strtok (0, horizontal_space);
+}
+
+void
+language_getopt (void)
+{
+ struct language *lang;
+
+ for (lang = languages_0; lang < languages_N; lang++)
+ if (lang->lg_argc)
+ lang->lg_parse_args (lang->lg_argv, lang->lg_argc);
+}
+
+struct language *
+get_language (char const *lang_name)
+{
+ struct language *lang;
+
+ for (lang = languages_0; lang < languages_N; lang++)
+ if (strequ (lang_name, lang->lg_name))
+ {
+ DEBUG (("lang=%s", lang_name));
+ return lang;
+ }
+ DEBUG (("!lang=%s", lang_name));
+ return 0;
+}
+
+/****************************************************************************/
+
+int lang_args_index = 0;
+
+void
+set_default_language (char const *lang_name)
+{
+}
+
+void
+parse_language_map (char const *file_name)
+{
+ if (obstack_init (&lang_args_obstack) == 0)
+ error (1, 0, _("can't allocate language args obstack: memory exhausted"));
+ if (file_name == 0)
+ file_name = LANGUAGE_MAP_FILE;
+ parse_language_map_file (file_name, &lang_args_list);
+}
+
+struct lang_args **
+parse_language_map_file (char const *file_name, struct lang_args **next_ptr)
+{
+ static char white_space[] = " \t\r\n\v\f";
+ static char horizontal_space[] = " \t";
+ static char vertical_space[] = "\r\n\v\f";
+ char *lang_map_buffer;
+ char *lmp;
+
+ lmp = lang_map_buffer = read_language_map_file (file_name);
+ for (;;)
+ {
+ struct lang_args *new_args;
+ struct language const *lang;
+ int pattern_size;
+ char *lang_name;
+ int space;
+
+ /* Skip leading white space and full-line comments */
+ while (*lmp)
+ {
+ lmp += strspn (lmp, white_space);
+ if (*lmp != '#')
+ break;
+ lmp += strcspn (lmp, vertical_space);
+ }
+ if (*lmp == '\0')
+ break;
+
+ pattern_size = strcspn (lmp, white_space);
+ if (pattern_size == 3 && strnequ (lmp, "***", 3))
+ {
+ lmp += pattern_size;
+ lmp += strspn (lmp, horizontal_space);
+ if (isspace (*lmp))
+ next_ptr = parse_language_map_file (LANGUAGE_MAP_FILE, next_ptr);
+ else
+ {
+ char *end = lmp + strcspn (lmp, white_space);
+ *end = '\0';
+ next_ptr = parse_language_map_file (lmp, next_ptr);
+ lmp = end + 1;
+ }
+ continue;
+ }
+
+ new_args = OBSTACK_ALLOC (&lang_args_obstack, struct lang_args, 1);
+ if (new_args == 0)
+ error (1, 0, _("can't allocate language args: memory exhausted"));
+ new_args->la_pattern = obstack_copy0 (&lang_args_obstack, lmp, pattern_size);
+ new_args->la_args_string = 0;
+ new_args->la_next = 0;
+ lmp += pattern_size;
+ lmp += strspn (lmp, horizontal_space);
+ if (isspace (*lmp))
+ {
+ error (0, 0, _("language name expected following `%s' in file `%s'"),
+ new_args->la_pattern, file_name);
+ obstack_free (&lang_args_obstack, new_args);
+ continue;
+ }
+ lang_name = lmp;
+ lmp += strcspn (lmp, white_space);
+ space = *lmp;
+ *lmp++ = '\0';
+ lmp += strspn (lmp, horizontal_space);
+ lang = new_args->la_language = get_language (lang_name);
+
+ if (*lmp == '#')
+ lmp += strcspn (lmp, vertical_space);
+ else if (!isspace (*lmp) && (space == ' ' || space == '\t'))
+ {
+ int args_size = strcspn (lmp, vertical_space);
+ new_args->la_args_string = obstack_copy0 (&lang_args_obstack, lmp, args_size);
+ lmp += args_size;
+ }
+ new_args->la_args_digested = (lang
+ ? lang->lg_parse_args (&new_args->la_args_string, 0)
+ : 0);
+ if (pattern_size == 2 && strnequ (new_args->la_pattern, "**", 2))
+ {
+ if (lang_args_default)
+ {
+ obstack_free (&lang_args_obstack, new_args);
+ continue;
+ }
+ lang_args_default = new_args;
+ DEBUG ((", <default>"));
+ }
+ else
+ {
+ new_args->la_index = lang_args_index++;
+ *next_ptr = new_args;
+ next_ptr = &new_args->la_next;
+ }
+ DEBUG ((", pat=%s\n", new_args->la_pattern));
+ }
+ free (lang_map_buffer);
+ return next_ptr;
+}
+
+char *
+read_language_map_file (char const *file_name)
+{
+ int map_fd;
+ char *lang_map_buffer;
+ struct stat st;
+ int bytes;
+
+ map_fd = open (file_name, O_RDONLY);
+ if (map_fd < 0)
+ error (1, errno, _("can't open language map file `%s'"), file_name);
+ if (fstat (map_fd, &st) < 0)
+ error (1, errno, _("can't get size of map file `%s'"), file_name);
+
+ lang_map_buffer = MALLOC (char, st.st_size + 2);
+ if (lang_map_buffer == 0)
+ error (1, 0, _("can't allocate language args: memory exhausted"));
+ lang_map_buffer[st.st_size] = '\n';
+ lang_map_buffer[st.st_size+1] = '\0';
+
+ bytes = read (map_fd, lang_map_buffer, st.st_size);
+ if (bytes < 0)
+ error (1, errno, _("can't read language map file `%s'"), file_name);
+ /* FIXME: handle interrupted & partial reads */
+ if (bytes != st.st_size)
+ error (1, errno, _("can't read entire language map file `%s'"), file_name);
+
+ close (map_fd);
+ return lang_map_buffer;
+}
+
+/****************************************************************************/
+
+void
+tokenize_args_string (char *args_string, int *argcp, char ***argvp)
+{
+ static char horizontal_space[] = " \t";
+ char **argv_0 = MALLOC (char *, strlen (args_string) / 2);
+ char **argv = argv_0;
+ char *arg;
+
+ *argv++ = program_name;
+ arg = strtok (args_string, horizontal_space);
+ while (arg)
+ {
+ *argv++ = arg;
+ arg = strtok (0, horizontal_space);
+ }
+ *argcp = argv - argv_0;
+ *argvp = REALLOC (argv_0, char *, *argcp);
+}
+
+static void
+set_ushort_ctype (unsigned short *ctype, char const *chars, int type)
+{
+ unsigned short *rct = &ctype[1];
+
+ while (*chars)
+ rct[*chars++] |= type;
+}
+
+static void
+clear_ushort_ctype (unsigned short *ctype, char const *chars, int type)
+{
+ unsigned short *rct = &ctype[1];
+
+ while (*chars)
+ rct[*chars++] &= ~type;
+}
+
+static void
+set_uchar_ctype (unsigned char *ctype, char const *chars, int type)
+{
+ unsigned char *rct = &ctype[1];
+
+ while (*chars)
+ rct[*chars++] |= type;
+}
+
+static void
+clear_uchar_ctype (unsigned char *ctype, char const *chars, int type)
+{
+ unsigned char *rct = &ctype[1];
+
+ while (*chars)
+ rct[*chars++] &= ~type;
+}
+
+/*************** C & C++ ****************************************************/
+
+#define I1 0x0001 /* 1st char of an identifier [a-zA-Z_] */
+#define DG 0x0002 /* decimal digit [0-9] */
+#define NM 0x0004 /* extra chars in a hex or long number [a-fA-FxXlL] */
+#define C1 0x0008 /* C comment introduction char: / */
+#define C2 0x0010 /* C comment termination char: * */
+#define Q1 0x0020 /* single quote: ' */
+#define Q2 0x0040 /* double quote: " */
+#define ES 0x0080 /* escape char: \ */
+#define NL 0x0100 /* newline: \n */
+#define EF 0x0200 /* EOF */
+#define SK 0x0400 /* Make these chars valid for names within strings */
+#define VH 0x0800 /* VHIL comment introduction char: # */
+#define WS 0x1000 /* White space characters */
+
+/* character class membership macros: */
+
+#define ISDIGIT(c) ((rct)[c] & (DG)) /* digit */
+#define ISNUMBER(c) ((rct)[c] & (DG|NM)) /* legal in a number */
+#define ISEOF(c) ((rct)[c] & (EF)) /* EOF */
+#define ISID1ST(c) ((rct)[c] & (I1)) /* 1st char of an identifier */
+#define ISIDREST(c) ((rct)[c] & (I1|DG)) /* rest of an identifier */
+#define ISSTRKEEP(c) ((rct)[c] & (I1|DG|SK)) /* keep contents of string */
+#define ISSPACE(c) ((rct)[c] & (WS)) /* white space character */
+
+/* The `BORING' classes should be skipped over until something
+ interesting comes along... */
+
+#define ISBORING(c) (!((rct)[c] & (EF|NL|I1|DG|Q1|Q2|C1|VH))) /* fluff */
+#define ISCBORING(c) (!((rct)[c] & (EF|C2))) /* comment fluff */
+#define ISCCBORING(c) (!((rct)[c] & (EF|NL))) /* C++ // comment fluff */
+#define ISQ1BORING(c) (!((rct)[c] & (EF|NL|Q1|ES))) /* char const fluff */
+#define ISQ2BORING(c) (!((rct)[c] & (EF|NL|Q2|ES))) /* quoted str fluff */
+
+static unsigned short ctype_c[257] =
+{
+ EF,
+/* 0 1 2 3 4 5 6 7 */
+/* ----- ----- ----- ----- ----- ----- ----- ----- */
+/*000*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*010*/ 0, 0, NL, 0, 0, 0, 0, 0,
+/*020*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*030*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*040*/ 0, 0, Q2, 0, 0, 0, 0, Q1,
+/*050*/ 0, 0, C2, 0, 0, 0, 0, C1,
+/*060*/ DG, DG, DG, DG, DG, DG, DG, DG,
+/*070*/ DG, DG, 0, 0, 0, 0, 0, 0,
+/*100*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*110*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*120*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*130*/ I1|NM, I1, I1, 0, ES, 0, 0, I1,
+/*140*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*150*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*160*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*170*/ I1|NM, I1, I1, 0, 0, 0, 0, 0,
+ /* FIXME: latin-1 */
+};
+
+struct args_c
+{
+ int strip_underscore;
+ unsigned short *ctype;
+};
+
+static struct args_c args_c = { 0, ctype_c };
+
+static struct option const long_options_c[] =
+{
+ { "keep", required_argument, 0, 'k' },
+ { "ignore", required_argument, 0, 'i' },
+ { "strip-underscore", no_argument, 0, 'u' },
+ { 0 }
+};
+
+static void
+help_me_c (void)
+{
+ printf (_("\
+C language:\n\
+ -k,--keep=CHARS Allow CHARS in single-token strings, keep the result\n\
+ -i,--ignore=CHARS Allow CHARS in single-token strings, toss the result\n\
+ -u,--strip-underscore Strip a leading underscore from single-token strings\n\
+"));
+}
+
+static void *
+parse_args_c (char **argv, int argc)
+{
+ char *tmp_string = 0;
+ struct args_c *args;
+
+ if (argv == 0 || *argv == 0)
+ return &args_c;
+
+ if (argc)
+ args = &args_c;
+ else
+ {
+ tmp_string = strdup (*argv);
+ tokenize_args_string (tmp_string, &argc, &argv);
+ args = MALLOC (struct args_c, 1);
+ args->strip_underscore = 0;
+ args->ctype = ctype_c;
+ }
+
+ optind = 0;
+ for (;;)
+ {
+ int optc = getopt_long (argc, argv, "k:i:u",
+ long_options_c, (int *) 0);
+ if (optc < 0)
+ break;
+ if ((optc == 'k' || optc == 'i') && args->ctype == ctype_c)
+ args->ctype = CLONE (ctype_c, unsigned short, cardinalityof (ctype_c));
+ switch (optc)
+ {
+ case 'k':
+ set_ushort_ctype (args->ctype, optarg, SK);
+ break;
+
+ case 'i':
+ clear_ushort_ctype (args->ctype, optarg, SK);
+ break;
+
+ case 'u':
+ args->strip_underscore = 1;
+ break;
+
+ default:
+ usage ();
+ }
+ }
+ if (tmp_string)
+ {
+ free (argv);
+ free (tmp_string);
+ }
+ return args;
+}
+
+
+/* Grab the next identifier from the C source file. This state
+ machine is built for speed, not elegance. */
+
+static struct token *
+get_token_c (FILE *in_FILE, void const *args, int *flags)
+{
+#define ARGS ((struct args_c const *) args)
+ static int new_line = 1;
+ unsigned short const *rct = &ARGS->ctype[1];
+ char id_0[BUFSIZ];
+ char *id = id_0;
+ int c;
+
+ obstack_blank (&tokens_obstack, offsetof (struct token, tok_name));
+
+top:
+ c = getc (in_FILE);
+ if (new_line)
+ {
+ new_line = 0;
+ if (c != '#')
+ goto next;
+ c = getc (in_FILE);
+ while (ISBORING (c))
+ c = getc (in_FILE);
+ if (!ISID1ST (c))
+ goto next;
+ id = id_0;
+ *id++ = c;
+ while (ISIDREST (c = getc (in_FILE)))
+ *id++ = c;
+ *id = '\0';
+ if (strequ (id_0, "include"))
+ {
+ while (c == ' ' || c == '\t')
+ c = getc (in_FILE);
+ if (c == '\n')
+ {
+ new_line = 1;
+ goto top;
+ }
+ id = id_0;
+ if (c == '"')
+ {
+ c = getc (in_FILE);
+ while (c != '\n' && c != EOF && c != '"')
+ {
+ *id++ = c;
+ c = getc (in_FILE);
+ }
+ *flags = TOK_STRING;
+ }
+ else if (c == '<')
+ {
+ c = getc (in_FILE);
+ while (c != '\n' && c != EOF && c != '>')
+ {
+ *id++ = c;
+ c = getc (in_FILE);
+ }
+ *flags = TOK_STRING;
+ }
+ else if (ISID1ST (c))
+ {
+ *id++ = c;
+ while (ISIDREST (c = getc (in_FILE)))
+ *id++ = c;
+ *flags = TOK_NAME;
+ }
+ else
+ {
+ while (c != '\n' && c != EOF)
+ c = getc (in_FILE);
+ new_line = 1;
+ goto top;
+ }
+ while (c != '\n' && c != EOF)
+ c = getc (in_FILE);
+ new_line = 1;
+ obstack_grow0 (&tokens_obstack, id_0, id - id_0);
+ return (struct token *) obstack_finish (&tokens_obstack);
+ }
+ if (strnequ (id_0, "if", 2)
+ || strequ (id_0, "define")
+ || strequ (id_0, "elif") /* ansi C */
+ || strequ (id_0, "undef"))
+ goto next;
+ while ((c != '\n') && (c != EOF))
+ c = getc (in_FILE);
+ new_line = 1;
+ goto top;
+ }
+
+next:
+ while (ISBORING (c))
+ c = getc (in_FILE);
+
+ switch (c)
+ {
+ case '"':
+ id = id_0;
+ *id++ = c = getc (in_FILE);
+ for (;;)
+ {
+ while (ISQ2BORING (c))
+ *id++ = c = getc (in_FILE);
+ if (c == '\\')
+ {
+ *id++ = c = getc (in_FILE);
+ continue;
+ }
+ else if (c != '"')
+ goto next;
+ break;
+ }
+ *--id = '\0';
+ id = id_0;
+ while (ISSTRKEEP (*id))
+ id++;
+ if (*id || id == id_0)
+ {
+ c = getc (in_FILE);
+ goto next;
+ }
+ *flags = TOK_STRING;
+ if (ARGS->strip_underscore && id_0[0] == '_' && id_0[1])
+ obstack_grow0 (&tokens_obstack, id_0 + 1, id - id_0 - 1);
+ else
+ obstack_grow0 (&tokens_obstack, id_0, id - id_0);
+ return (struct token *) obstack_finish (&tokens_obstack);
+
+ case '\'':
+ c = getc (in_FILE);
+ for (;;)
+ {
+ while (ISQ1BORING (c))
+ c = getc (in_FILE);
+ if (c == '\\')
+ {
+ c = getc (in_FILE);
+ continue;
+ }
+ else if (c == '\'')
+ c = getc (in_FILE);
+ goto next;
+ }
+
+ case '/':
+ c = getc (in_FILE);
+ if (c == '/')
+ { /* Cope with C++ comment */
+ while (ISCCBORING (c))
+ c = getc (in_FILE);
+ new_line = 1;
+ goto top;
+ }
+ else if (c != '*')
+ goto next;
+ c = getc (in_FILE);
+ for (;;)
+ {
+ while (ISCBORING (c))
+ c = getc (in_FILE);
+ c = getc (in_FILE);
+ if (c == '/')
+ {
+ c = getc (in_FILE);
+ goto next;
+ }
+ else if (ISEOF (c))
+ {
+ new_line = 1;
+ obstack_free (&tokens_obstack, obstack_finish (&tokens_obstack));
+ return 0;
+ }
+ }
+
+ case '\n':
+ new_line = 1;
+ goto top;
+
+ default:
+ if (ISEOF (c))
+ {
+ new_line = 1;
+ obstack_free (&tokens_obstack, obstack_finish (&tokens_obstack));
+ return 0;
+ }
+ id = id_0;
+ *id++ = c;
+ if (ISID1ST (c))
+ {
+ *flags = TOK_NAME;
+ while (ISIDREST (c = getc (in_FILE)))
+ *id++ = c;
+ }
+ else if (ISDIGIT (c))
+ {
+ *flags = TOK_NUMBER;
+ while (ISNUMBER (c = getc (in_FILE)))
+ *id++ = c;
+ }
+ else
+ {
+ if (isprint (c))
+ fprintf (stderr, _("junk: `%c'"), c);
+ else
+ fprintf (stderr, _("junk: `\\%03o'"), c);
+ }
+ ungetc (c, in_FILE);
+ *flags |= TOK_LITERAL;
+ obstack_grow0 (&tokens_obstack, id_0, id - id_0);
+ return (struct token *) obstack_finish (&tokens_obstack);
+ }
+#undef ARGS
+}
+
+#undef I1
+#undef DG
+#undef NM
+#undef C1
+#undef C2
+#undef Q1
+#undef Q2
+#undef ES
+#undef NL
+#undef EF
+#undef SK
+#undef VH
+#undef WS
+#undef ISDIGIT
+#undef ISNUMBER
+#undef ISEOF
+#undef ISID1ST
+#undef ISIDREST
+#undef ISSTRKEEP
+#undef ISSPACE
+#undef ISBORING
+#undef ISCBORING
+#undef ISCCBORING
+#undef ISQ1BORING
+#undef ISQ2BORING
+
+/*************** Assembly ***************************************************/
+
+#define I1 0x01 /* 1st char of an identifier [a-zA-Z_] */
+#define NM 0x02 /* digit [0-9a-fA-FxX] */
+#define NL 0x04 /* newline: \n */
+#define CM 0x08 /* assembler comment char: usually # or | */
+#define IG 0x10 /* ignore `identifiers' with these chars in them */
+#define C1 0x20 /* C comment introduction char: / */
+#define C2 0x40 /* C comment termination char: * */
+#define EF 0x80 /* EOF */
+
+/* Assembly Language character classes */
+#define ISID1ST(c) ((rct)[c] & (I1))
+#define ISIDREST(c) ((rct)[c] & (I1|NM))
+#define ISNUMBER(c) ((rct)[c] & (NM))
+#define ISEOF(c) ((rct)[c] & (EF))
+#define ISCOMMENT(c) ((rct)[c] & (CM))
+#define ISBORING(c) (!((rct)[c] & (EF|NL|I1|NM|CM|C1)))
+#define ISCBORING(c) (!((rct)[c] & (EF|NL)))
+#define ISCCBORING(c) (!((rct)[c] & (EF|C2)))
+#define ISIGNORE(c) ((rct)[c] & (IG))
+
+static unsigned char ctype_asm[257] =
+{
+ EF,
+/* 0 1 2 3 4 5 6 7 */
+/* ----- ----- ----- ----- ----- ----- ----- ----- */
+/*000*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*010*/ 0, 0, NL, 0, 0, 0, 0, 0,
+/*020*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*030*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*040*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*050*/ 0, 0, C2, 0, 0, 0, 0, C1,
+/*060*/ NM, NM, NM, NM, NM, NM, NM, NM,
+/*070*/ NM, NM, 0, 0, 0, 0, 0, 0,
+/*100*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*110*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*120*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*130*/ I1|NM, I1, I1, 0, 0, 0, 0, I1,
+/*140*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*150*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*160*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*170*/ I1|NM, I1, I1, 0, 0, 0, 0, 0,
+
+};
+
+struct args_asm
+{
+ int handle_cpp;
+ int strip_underscore;
+ unsigned char *ctype;
+};
+
+static struct args_asm args_asm = { 1, 0, ctype_asm };
+
+static struct option const long_options_asm[] =
+{
+ { "comment", required_argument, 0, 'c' },
+ { "keep", required_argument, 0, 'k' },
+ { "ignore", required_argument, 0, 'i' },
+ { "strip-underscore", no_argument, 0, 'u' },
+ { "no-cpp", no_argument, 0, 'p' },
+ { 0 }
+};
+
+static void
+help_me_asm (void)
+{
+ printf (_("\
+Assembly language:\n\
+ -c,--comment=CHARS Any of CHARS starts a comment until end-of-line\n\
+ -k,--keep=CHARS Allow CHARS in tokens, and keep the result\n\
+ -i,--ignore=CHARS Allow CHARS in tokens, and toss the result\n\
+ -u,--strip-underscore Strip a leading underscore from tokens\n\
+ -n,--no-cpp Don't handle C pre-processor directives\n\
+"));
+}
+
+static void *
+parse_args_asm (char **argv, int argc)
+{
+ char *tmp_string = 0;
+ struct args_asm *args;
+
+ if (argv == 0 || *argv == 0)
+ return &args_asm;
+
+ if (argc)
+ args = &args_asm;
+ else
+ {
+ tmp_string = strdup (*argv);
+ tokenize_args_string (tmp_string, &argc, &argv);
+ args = MALLOC (struct args_asm, 1);
+ args->strip_underscore = 0;
+ args->ctype = ctype_asm;
+ args->handle_cpp = 1;
+ }
+
+ optind = 0;
+ for (;;)
+ {
+ int optc = getopt_long (argc, argv, "c:k:i:un",
+ long_options_asm, (int *) 0);
+ if (optc < 0)
+ break;
+ if ((optc == 'k' || optc == 'i' || optc == 'c')
+ && args->ctype == ctype_asm)
+ args->ctype = CLONE (ctype_asm, unsigned char, cardinalityof (ctype_asm));
+ switch (optc)
+ {
+ case 'c':
+ set_uchar_ctype (args->ctype, optarg, CM);
+ break;
+
+ case 'k':
+ set_uchar_ctype (args->ctype, optarg, I1);
+ break;
+
+ case 'i':
+ set_uchar_ctype (args->ctype, optarg, I1 | IG);
+ break;
+
+ case 'u':
+ args->strip_underscore = 1;
+ break;
+
+ case 'n':
+ args->handle_cpp = 0;
+ break;
+
+ default:
+ usage ();
+ }
+ }
+ if (tmp_string)
+ {
+ free (argv);
+ free (tmp_string);
+ }
+ return args;
+}
+
+/* Grab the next identifier the assembly language source file. This
+ state machine is built for speed, not elegance. */
+
+static struct token *
+get_token_asm (FILE *in_FILE, void const *args, int *flags)
+{
+#define ARGS ((struct args_asm const *) args)
+ static int new_line = 1;
+ unsigned char const *rct = &ARGS->ctype[1];
+ char id_0[BUFSIZ];
+ char *id = id_0;
+ int c;
+
+ obstack_blank (&tokens_obstack, offsetof (struct token, tok_name));
+
+top:
+ c = getc (in_FILE);
+ if (ARGS->handle_cpp > 0 && new_line)
+ {
+ new_line = 0;
+ if (c != '#')
+ goto next;
+ while (ISBORING (c))
+ c = getc (in_FILE);
+ if (!ISID1ST (c))
+ goto next;
+ id = id_0;
+ *id++ = c;
+ while (ISIDREST (c = getc (in_FILE)))
+ *id++ = c;
+ *id = '\0';
+ if (strequ (id_0, "include"))
+ {
+ while (c != '"' && c != '<')
+ c = getc (in_FILE);
+ id = id_0;
+ *id++ = c = getc (in_FILE);
+ while ((c = getc (in_FILE)) != '"' && c != '>')
+ *id++ = c;
+ *flags = TOK_STRING;
+ obstack_grow0 (&tokens_obstack, id_0, id - id_0);
+ return (struct token *) obstack_finish (&tokens_obstack);
+ }
+ if (strnequ (id_0, "if", 2)
+ || strequ (id_0, "define")
+ || strequ (id_0, "undef"))
+ goto next;
+ while (c != '\n')
+ c = getc (in_FILE);
+ new_line = 1;
+ goto top;
+ }
+
+next:
+ while (ISBORING (c))
+ c = getc (in_FILE);
+
+ if (ISCOMMENT (c))
+ {
+ while (ISCBORING (c))
+ c = getc (in_FILE);
+ new_line = 1;
+ }
+
+ if (ISEOF (c))
+ {
+ new_line = 1;
+ obstack_free (&tokens_obstack, obstack_finish (&tokens_obstack));
+ return 0;
+ }
+
+ if (c == '\n')
+ {
+ new_line = 1;
+ goto top;
+ }
+
+ if (c == '/')
+ {
+ if ((c = getc (in_FILE)) != '*')
+ goto next;
+ c = getc (in_FILE);
+ for (;;)
+ {
+ while (ISCCBORING (c))
+ c = getc (in_FILE);
+ c = getc (in_FILE);
+ if (c == '/')
+ {
+ c = getc (in_FILE);
+ break;
+ }
+ else if (ISEOF (c))
+ {
+ new_line = 1;
+ obstack_free (&tokens_obstack, obstack_finish (&tokens_obstack));
+ return 0;
+ }
+ }
+ goto next;
+ }
+
+ id = id_0;
+ if (ARGS->strip_underscore && c == '_' && !ISID1ST (c = getc (in_FILE)))
+ {
+ obstack_grow0 (&tokens_obstack, "_", 1);
+ return (struct token *) obstack_finish (&tokens_obstack);
+ }
+ *id++ = c;
+ if (ISID1ST (c))
+ {
+ *flags = TOK_NAME;
+ while (ISIDREST (c = getc (in_FILE)))
+ *id++ = c;
+ }
+ else if (ISNUMBER (c))
+ {
+ *flags = TOK_NUMBER;
+ while (ISNUMBER (c = getc (in_FILE)))
+ *id++ = c;
+ }
+ else
+ {
+ if (isprint (c))
+ fprintf (stderr, _("junk: `%c'"), c);
+ else
+ fprintf (stderr, _("junk: `\\%03o'"), c);
+ goto next;
+ }
+
+ *id = '\0';
+ for (id = id_0; *id; id++)
+ if (ISIGNORE (*id))
+ goto next;
+ ungetc (c, in_FILE);
+ *flags |= TOK_LITERAL;
+ obstack_grow0 (&tokens_obstack, id_0, id - id_0);
+ return (struct token *) obstack_finish (&tokens_obstack);
+#undef ARGS
+}
+
+#undef I1
+#undef NM
+#undef NL
+#undef CM
+#undef IG
+#undef C1
+#undef C2
+#undef EF
+#undef ISID1ST
+#undef ISIDREST
+#undef ISNUMBER
+#undef ISEOF
+#undef ISCOMMENT
+#undef ISBORING
+#undef ISCBORING
+#undef ISCCBORING
+#undef ISIGNORE
+
+/*************** Text *******************************************************/
+
+#define I1 0x01 /* 1st char of an identifier [a-zA-Z_] */
+#define NM 0x02 /* digit [0-9a-fA-FxX] */
+#define SQ 0x04 /* squeeze these out (.,',-) */
+#define EF 0x80 /* EOF */
+
+/* Text character classes */
+#define ISID1ST(c) ((rct)[c] & (I1))
+#define ISIDREST(c) ((rct)[c] & (I1|NM|SQ))
+#define ISNUMBER(c) ((rct)[c] & (NM))
+#define ISEOF(c) ((rct)[c] & (EF))
+#define ISBORING(c) (!((rct)[c] & (I1|NM|EF)))
+#define ISIDSQUEEZE(c) ((rct)[c] & (SQ))
+
+static unsigned char ctype_text[257] =
+{
+ EF,
+/* 0 1 2 3 4 5 6 7 */
+/* ----- ----- ----- ----- ----- ----- ----- ----- */
+/*000*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*010*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*020*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*030*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*040*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*050*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*060*/ NM, NM, NM, NM, NM, NM, NM, NM,
+/*070*/ NM, NM, 0, 0, 0, 0, 0, 0,
+/*100*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*110*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*120*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*130*/ I1|NM, I1, I1, 0, 0, 0, 0, I1,
+/*140*/ 0, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1|NM, I1,
+/*150*/ I1, I1, I1, I1, I1|NM, I1, I1, I1,
+/*160*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*170*/ I1|NM, I1, I1, 0, 0, 0, 0, 0,
+/*200*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*210*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*220*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*230*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*240*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*250*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*260*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*270*/ 0, 0, 0, 0, 0, 0, 0, 0,
+/*300*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*310*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*320*/ I1, I1, I1, I1, I1, I1, I1, 0,
+/*330*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*340*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*350*/ I1, I1, I1, I1, I1, I1, I1, I1,
+/*360*/ I1, I1, I1, I1, I1, I1, I1, 0,
+/*370*/ I1, I1, I1, I1, I1, I1, I1, I1,
+};
+
+struct args_text
+{
+ unsigned char *ctype;
+};
+
+static struct args_text args_text = { ctype_text };
+
+static struct option const long_options_text[] =
+{
+ { "include", required_argument, 0, 'i' },
+ { "exclude", required_argument, 0, 'x' },
+ { 0 }
+};
+
+static void
+help_me_text (void)
+{
+ printf (_("\
+Text language:\n\
+ -i,--include=CHAR-CLASS Treat characters of CHAR-CLASS as token constituents\n\
+ -x,--exclude=CHAR-CLASS Treat characters of CHAR-CLASS as token delimiters\n\
+"));
+}
+
+static void *
+parse_args_text (char **argv, int argc)
+{
+ char *tmp_string = 0;
+ struct args_text *args;
+
+ if (argv == 0 || *argv == 0)
+ return &args_text;
+
+ if (argc)
+ args = &args_text;
+ else
+ {
+ tmp_string = strdup (*argv);
+ tokenize_args_string (tmp_string, &argc, &argv);
+ args = MALLOC (struct args_text, 1);
+ args->ctype = ctype_text;
+ }
+
+ optind = 0;
+ for (;;)
+ {
+ int optc = getopt_long (argc, argv, "i:x:",
+ long_options_text, (int *) 0);
+ if (optc < 0)
+ break;
+ if ((optc == 'k' || optc == 'i') && args->ctype == ctype_text)
+ args->ctype = CLONE (ctype_text, unsigned char, cardinalityof (ctype_text));
+ switch (optc)
+ {
+ case 'i':
+ set_uchar_ctype (args->ctype, optarg, I1);
+ break;
+
+ case 'x':
+ clear_uchar_ctype (args->ctype, optarg, I1);
+ break;
+
+ default:
+ usage ();
+ }
+ }
+ if (tmp_string)
+ {
+ free (argv);
+ free (tmp_string);
+ }
+ return args;
+}
+
+/* Grab the next identifier the text source file. This state machine
+ is built for speed, not elegance. */
+
+static struct token *
+get_token_text (FILE *in_FILE, void const *args, int *flags)
+{
+#define ARGS ((struct args_text const *) args)
+ static char id_0[BUFSIZ];
+ unsigned char const *rct = &ARGS->ctype[1];
+ int c;
+ char *id = id_0;
+
+ obstack_blank (&tokens_obstack, offsetof (struct token, tok_name));
+
+top:
+ c = getc (in_FILE);
+ while (ISBORING (c))
+ c = getc (in_FILE);
+ if (ISEOF (c))
+ {
+ obstack_free (&tokens_obstack, obstack_finish (&tokens_obstack));
+ return 0;
+ }
+ id = id_0;
+ *id++ = c;
+ if (ISID1ST (c))
+ {
+ *flags = TOK_NAME;
+ while (ISIDREST (c = getc (in_FILE)))
+ if (!ISIDSQUEEZE (c))
+ *id++ = c;
+ }
+ else if (ISNUMBER (c))
+ {
+ *flags = TOK_NUMBER;
+ while (ISNUMBER (c = getc (in_FILE)))
+ *id++ = c;
+ }
+ else
+ {
+ if (isprint (c))
+ fprintf (stderr, _("junk: `%c'"), c);
+ else
+ fprintf (stderr, _("junk: `\\%03o'"), c);
+ goto top;
+ }
+
+ ungetc (c, in_FILE);
+ *flags |= TOK_LITERAL;
+ obstack_grow0 (&tokens_obstack, id_0, id - id_0);
+ return (struct token *) obstack_finish (&tokens_obstack);
+#undef ARGS
+}
+
+#undef I1
+#undef NM
+#undef SQ
+#undef EF
+#undef ISID1ST
+#undef ISIDREST
+#undef ISNUMBER
+#undef ISEOF
+#undef ISBORING
+#undef ISIDSQUEEZE
diff --git a/libidu/scanners.h b/libidu/scanners.h
new file mode 100644
index 0000000..ca95310
--- /dev/null
+++ b/libidu/scanners.h
@@ -0,0 +1,70 @@
+/* scanners.h -- defs for interface to scanners.c
+ Copyright (C) 1986, 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ This program 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.
+
+ This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _scanners_h_
+#define _scanners_h_
+
+#include "xobstack.h"
+
+#define MAX_LEVELS 5 /* log_8 of the max # of files: log_8 (32768) == 5 */
+
+struct token
+{
+ unsigned short tok_count;
+ unsigned char tok_flags;
+ unsigned char tok_hits[MAX_LEVELS];
+ char tok_name[1];
+};
+
+typedef struct token *(*get_token_func_t) __P((FILE *in_FILE, void const *args, int *flags));
+typedef void *(*parse_args_func_t) __P((char **argv, int argc));
+typedef void (*help_me_func_t) __P((void));
+
+struct language
+{
+ char const *lg_name;
+ parse_args_func_t lg_parse_args;
+ get_token_func_t lg_get_token;
+ help_me_func_t lg_help_me;
+ int lg_argc;
+ char *lg_argv[16];
+};
+
+struct lang_args
+{
+ struct language const *la_language;
+ char const *la_pattern; /* fnmatch(3) pattern */
+ char *la_args_string; /* human-readable scanner args */
+ void const *la_args_digested; /* pre-parsed scanner args */
+ int la_index;
+ struct lang_args *la_next;
+};
+
+extern void language_help_me __P((void));
+extern void language_getopt __P((void));
+extern void language_save_arg __P((char *arg));
+extern struct language *get_language __P((char const *lang_name));
+extern void parse_language_map __P((char const *file_name));
+extern void set_default_language __P((char const *lang_name));
+
+extern struct lang_args *lang_args_default;
+extern struct lang_args *lang_args_list;
+
+extern struct obstack tokens_obstack;
+
+#endif /* not _scanners_h_ */
diff --git a/libidu/tokflags.h b/libidu/tokflags.h
new file mode 100644
index 0000000..a246ec5
--- /dev/null
+++ b/libidu/tokflags.h
@@ -0,0 +1,35 @@
+/* tokflags.h -- decls for per-token flags
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ This program 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.
+
+ This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _tokflags_h_
+#define _tokflags_h_
+
+#define TOK_VECTOR 0x01 /* 1 = hits are stored as a vector
+ 0 = hits are stored as a 8-way tree of bits
+ mkid chooses whichever is more compact.
+ vector is more compact for tokens with few hits */
+#define TOK_NUMBER 0x02 /* occurs as a number */
+#define TOK_NAME 0x04 /* occurs as a name */
+#define TOK_STRING 0x08 /* occurs in a string */
+#define TOK_LITERAL 0x10 /* occurs as a literal */
+#define TOK_COMMENT 0x20 /* occurs in a comment */
+#define TOK_UNUSED_1 0x40
+#define TOK_SHORT_COUNT 0x80 /* count is two bytes */
+
+#endif /* not _tokflags_h_ */
diff --git a/libidu/walker.c b/libidu/walker.c
new file mode 100644
index 0000000..0ba2d89
--- /dev/null
+++ b/libidu/walker.c
@@ -0,0 +1,1130 @@
+/* walker.c -- nifty file-tree walker
+ Copyright (C) 1986, 1995, 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ This program 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.
+
+ This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <config.h>
+#include "xsysstat.h"
+#include <stdio.h>
+#include "xstdlib.h"
+#include "xstddef.h"
+#include "xunistd.h"
+#include "xstring.h"
+#include "xfnmatch.h"
+#include "xdirent.h"
+#include "xnls.h"
+#include "idfile.h"
+#include "error.h"
+#include "xmalloc.h"
+#include "dynvec.h"
+#include "scanners.h"
+#include "pathmax.h"
+#include "xalloca.h"
+
+int walk_dir __P((struct file_link *dir_link));
+struct member_file *get_member_file __P((struct file_link *flink));
+struct lang_args *get_lang_args __P((struct file_link const *flink));
+int walk_sub_dirs __P((struct dynvec *sub_dirs_vec));
+void reparent_children __P((struct file_link *dlink, struct file_link *slink));
+int classify_link __P((struct file_link *flink, struct stat *stp));
+struct file_link *get_link_from_dirent __P((struct dirent *dirent, struct file_link *parent));
+struct file_link *make_link_from_dirent __P((struct dirent *dirent, struct file_link *parent));
+struct file_link *get_link_from_string __P((char const *name, struct file_link *parent));
+struct file_link *make_link_from_string __P((char const *name, struct file_link *parent));
+int lang_wanted __P((char const *lang_name));
+char **append_strings_to_vector __P((char **vector_0, char *string, char *delimiter_class));
+int vector_length __P((char **vector));
+int string_in_vector __P((char const *string, char **vector));
+static int same_as_dot __P((char const *cwd));
+struct file_link const **fill_link_vector __P((struct file_link const **vec_buf, struct file_link const *flink));
+struct file_link const **fill_link_vector_1 __P((struct file_link const **vec_buf, struct file_link const *flink));
+char *fill_dot_dots __P((char *buf, int levels));
+static char *absolute_file_name_1 __P((char *buffer, struct file_link const *flink));
+unsigned long member_file_hash_1 __P((void const *key));
+unsigned long member_file_hash_2 __P((void const *key));
+int member_file_hash_compare __P((void const *x, void const *y));
+unsigned long file_link_hash_1 __P((void const *key));
+unsigned long file_link_hash_2 __P((void const *key));
+int file_link_hash_compare __P((void const *x, void const *y));
+unsigned long dev_ino_hash_1 __P((void const *key));
+unsigned long dev_ino_hash_2 __P((void const *key));
+int dev_ino_hash_compare __P((void const *x, void const *y));
+int symlink_ancestry __P((struct file_link *flink));
+
+#if HAVE_LINK
+struct file_link *find_alias_link __P((struct file_link *flink, struct stat *stp));
+struct member_file *maybe_get_member_file __P((struct file_link *flink, struct stat *stp));
+#endif
+
+#define IS_DOT(s) ((s)[0] == '.' && (s)[1] == '\0')
+#define IS_DOT_DOT(s) ((s)[0] == '.' && (s)[1] == '.' && (s)[2] == '\0')
+#define IS_DOT_or_DOT_DOT(s) \
+ (((s)[0] == '.') && (((s)[1] == '\0') || ((s)[1] == '.' && (s)[2] == '\0')))
+
+static struct file_link *current_dir_link = 0;
+
+static char white_space[] = " \t\r\n\v\f";
+
+char* xgetcwd __P((void));
+
+
+/****************************************************************************/
+/* Walk the file-system tree rooted at `dir_link', looking for files
+ that are eligible for scanning. */
+
+int
+walk_dir (struct file_link *dir_link)
+{
+ int scannable_files;
+ struct dynvec *sub_dirs_vec;
+ DIR *dirp;
+
+ if (!chdir_to_link (dir_link))
+ return 0;
+ dirp = opendir (".");
+ if (dirp == 0)
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ absolute_file_name (file_name, dir_link);
+ error (0, errno, _("can't read directory `%s' (`.' from `%s')"), file_name, xgetcwd ());
+ return 0;
+ }
+ sub_dirs_vec = make_dynvec (32);
+ scannable_files = 0;
+ for (;;)
+ {
+ struct file_link *flink;
+ struct dirent *dirent = readdir (dirp);
+
+ if (dirent == 0)
+ break;
+ if (IS_DOT_or_DOT_DOT (dirent->d_name))
+ continue;
+
+ flink = get_link_from_dirent (dirent, dir_link);
+ if (!(flink->fl_flags & FL_PRUNE))
+ walk_flink (flink, sub_dirs_vec);
+ }
+ closedir (dirp);
+
+ scannable_files += walk_sub_dirs (sub_dirs_vec);
+ dynvec_free (sub_dirs_vec);
+ return scannable_files;
+}
+
+/* Walk the directories found by walk_dir, calling walk_dir
+ recursively for each directory. */
+
+int
+walk_sub_dirs (struct dynvec *sub_dirs_vec)
+{
+ struct file_link **sub_dirs;
+ struct file_link **sub_dirs_end;
+ int total_scannable_files = 0;
+
+ dynvec_freeze (sub_dirs_vec);
+ sub_dirs_end = (struct file_link **)
+ &sub_dirs_vec->dv_vec[sub_dirs_vec->dv_fill];
+ sub_dirs = (struct file_link **) sub_dirs_vec->dv_vec;
+ for ( ; sub_dirs < sub_dirs_end; sub_dirs++)
+ {
+ struct file_link *sub_dir_link = *sub_dirs;
+ int scannable_files = walk_dir (sub_dir_link);
+ if (scannable_files)
+ total_scannable_files += scannable_files;
+ }
+ return total_scannable_files;
+}
+
+void
+walk_flink (struct file_link *flink, struct dynvec *sub_dirs_vec)
+{
+ struct stat st;
+ unsigned int old_flags;
+ unsigned int new_flags;
+
+ new_flags = classify_link (flink, &st);
+ if (new_flags == 0)
+ return;
+
+ old_flags = flink->fl_flags;
+ if ((old_flags & FL_TYPE_MASK)
+ && (old_flags & FL_TYPE_MASK) != (new_flags & FL_TYPE_MASK))
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ absolute_file_name (file_name, flink);
+ error (0, 0, _("notice: `%s' was a %s, but is now a %s!"), file_name,
+ (FL_IS_FILE (old_flags) ? _("file") : _("directory")),
+ (FL_IS_FILE (new_flags) ? _("file") : _("directory")));
+ }
+
+
+ flink->fl_flags = (old_flags & ~(FL_TYPE_MASK|FL_SYM_LINK)) | new_flags;
+ if (FL_IS_DIR (new_flags))
+ {
+ struct file_link *alias_link = find_alias_link (flink, &st);
+
+ if (alias_link)
+ {
+ if (!(new_flags & FL_SYM_LINK))
+ reparent_children (flink, alias_link);
+ }
+ else if (sub_dirs_vec == 0)
+ walk_dir (flink);
+ else
+ dynvec_append (sub_dirs_vec, flink);
+ }
+ else
+ {
+ struct member_file *member;
+#if HAVE_LINK
+ member = maybe_get_member_file (flink, &st);
+#else
+ member = get_member_file (flink);
+#endif
+ if (member == 0)
+ return;
+ }
+}
+
+/* Take child file_link nodes from a symlinked directory and give them
+ to a hard linked directory. This is something of a pain since a
+ file_link's parent node is part of its hash-table key. We must
+ search the entire hash-table for the children. With each child, we
+ must delete it, reset its parent link, then reinsert. */
+
+void
+reparent_children (struct file_link *dlink, struct file_link *slink)
+{
+ void **slot = idh.idh_file_link_table.ht_vec;
+ void **end = &idh.idh_file_link_table.ht_vec[idh.idh_file_link_table.ht_size];
+
+ for ( ; slot < end; slot++)
+ {
+ if (!HASH_VACANT (*slot))
+ {
+ struct file_link *child = (struct file_link *) *slot;
+ if (child->fl_parent == slink)
+ {
+ void **new_slot;
+ *slot = hash_deleted_item;
+ child->fl_parent = dlink;
+ new_slot = hash_find_slot (&idh.idh_file_link_table, child);
+ *new_slot = child;
+ }
+ }
+ }
+}
+
+
+/****************************************************************************/
+/* Separate the wheat from the chaff. Mark those file_links that are
+ components in member files. */
+
+void
+mark_member_file_links (struct idhead *idhp)
+{
+ struct member_file **members_0
+ = (struct member_file **) hash_dump (&idhp->idh_member_file_table,
+ 0, member_file_qsort_compare);
+ struct member_file **end = &members_0[idhp->idh_member_file_table.ht_fill];
+ struct member_file **members;
+ int new_index = 0;
+
+ for (members = members_0; members < end; members++)
+ {
+ struct member_file *member = *members;
+ struct file_link *flink;
+ member->mf_index = new_index++;
+ for (flink = member->mf_link;
+ !(flink->fl_flags & FL_USED); flink = flink->fl_parent)
+ flink->fl_flags |= FL_USED;
+ }
+ free (members_0);
+}
+
+
+#if HAVE_LINK
+
+/****************************************************************************/
+/* Return a `member_file' for this `flink' *if* the filename matches
+ some scan pattern, and no alias for the file takes precedence ([1]
+ hard-links dominate symbolic-links; [2] for two hard-links: first
+ come, first served). */
+
+struct member_file *
+maybe_get_member_file (struct file_link *flink, struct stat *stp)
+{
+ struct file_link *alias_link;
+ struct member_file *member;
+ struct member_file *alias_member = 0;
+
+ member = get_member_file (flink);
+ alias_link = find_alias_link (flink, stp);
+ if (alias_link)
+ alias_member = find_member_file (alias_link);
+
+ if (member && alias_member)
+ {
+ int ancestry = symlink_ancestry (flink);
+ int alias_ancestry = symlink_ancestry (alias_link);
+ if (member->mf_lang_args != alias_member->mf_lang_args)
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ char *alias_file_name = ALLOCA (char, PATH_MAX);
+ absolute_file_name (file_name, flink);
+ absolute_file_name (alias_file_name, alias_link);
+ error (0, 0, _("warning: `%s' and `%s' are the same file, but yield different scans!"),
+ file_name, alias_file_name);
+ }
+ else if (alias_ancestry > ancestry)
+ {
+ hash_delete (&idh.idh_member_file_table, member);
+ member->mf_link->fl_flags &= ~FL_MEMBER;
+ return 0;
+ }
+ else
+ {
+ hash_delete (&idh.idh_member_file_table, alias_member);
+ alias_member->mf_link->fl_flags &= ~FL_MEMBER;
+ }
+ }
+ return member;
+}
+
+/* Return a previously registered alias for `flink', if any. */
+
+struct file_link *
+find_alias_link (struct file_link *flink, struct stat *stp)
+{
+ struct dev_ino *dev_ino;
+ struct dev_ino **slot;
+
+ dev_ino = (struct dev_ino *) obstack_alloc (&idh.idh_dev_ino_obstack, sizeof (struct dev_ino));
+ dev_ino->di_dev = stp->st_dev;
+ dev_ino->di_ino = stp->st_ino;
+ slot = (struct dev_ino **) hash_find_slot (&idh.idh_dev_ino_table, dev_ino);
+ if (HASH_VACANT (*slot))
+ {
+ dev_ino->di_link = flink;
+ hash_insert_at (&idh.idh_dev_ino_table, dev_ino, slot);
+ return 0;
+ }
+ else
+ {
+ obstack_free (&idh.idh_dev_ino_obstack, dev_ino);
+ return (*slot)->di_link;
+ }
+}
+
+/* Return the distance from `flink' to a symbolic-link ancestor
+ directory. PATH_MAX is considered an infinite distance (e.g.,
+ there are no symlinks between `flink' and the root). */
+
+int
+symlink_ancestry (struct file_link *flink)
+{
+ int ancestry = 0;
+ while (!IS_ROOT_FILE_LINK (flink))
+ {
+ if (flink->fl_flags & FL_SYM_LINK)
+ return ancestry;
+ ancestry++;
+ flink = flink->fl_parent;
+ }
+ return PATH_MAX;
+}
+
+#endif /* HAVE_LINK */
+
+struct member_file *
+get_member_file (struct file_link *flink)
+{
+ struct member_file *member;
+ struct member_file **slot;
+ struct lang_args const *args;
+
+ args = get_lang_args (flink);
+ if (args == 0)
+ return 0;
+ if (!lang_wanted (args->la_language->lg_name))
+ return 0;
+
+ member = (struct member_file *) obstack_alloc (&idh.idh_member_file_obstack,
+ sizeof (struct member_file));
+ member->mf_link = flink;
+ slot = (struct member_file **) hash_find_slot (&idh.idh_member_file_table, member);
+ if (HASH_VACANT (*slot))
+ {
+ member->mf_index = -1;
+ hash_insert_at (&idh.idh_member_file_table, member, slot);
+ flink->fl_flags |= FL_MEMBER;
+ }
+ else
+ {
+ obstack_free (&idh.idh_member_file_obstack, member);
+#if 0
+ if (member->mf_lang_args != args)
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ absolute_file_name (file_name, flink);
+ error (0, 0, _("notice: scan parameters changed for `%s'"), file_name);
+ member->mf_old_index = -1;
+ }
+#endif
+ member = *slot;
+ }
+ member->mf_lang_args = args;
+ return *slot;
+}
+
+struct member_file *
+find_member_file (struct file_link const *flink)
+{
+ struct member_file key;
+ struct member_file **slot;
+
+ key.mf_link = (struct file_link *) flink;
+ slot = (struct member_file **) hash_find_slot (&idh.idh_member_file_table, &key);
+ if (HASH_VACANT (*slot))
+ return 0;
+ return *slot;
+}
+
+/* March down the list of lang_args, and return the first one whose
+ pattern matches FLINK. Return the matching lang_args, if a
+ scanner exists for that language, otherwise return 0. */
+
+struct lang_args *
+get_lang_args (struct file_link const *flink)
+{
+ struct lang_args *args = lang_args_list;
+
+ while (args)
+ {
+ if (strchr (args->la_pattern, SLASH_CHAR))
+ {
+ char *file_name = ALLOCA (char, PATH_MAX);
+ absolute_file_name (file_name, flink);
+ if (fnmatch (args->la_pattern, file_name, MAYBE_FNM_CASEFOLD | FNM_FILE_NAME) == 0)
+ return (args->la_language ? args : 0);
+ }
+ else
+ {
+ if (fnmatch (args->la_pattern, flink->fl_name, MAYBE_FNM_CASEFOLD) == 0)
+ return (args->la_language ? args : 0);
+ }
+ args = args->la_next;
+ }
+ return ((lang_args_default && lang_args_default->la_language)
+ ? lang_args_default : 0);
+}
+
+
+/****************************************************************************/
+
+static char **langs_included;
+static char **langs_excluded;
+
+int
+lang_wanted (char const *lang_name)
+{
+ if (langs_excluded)
+ return !string_in_vector (lang_name, langs_excluded);
+ else if (langs_included)
+ return string_in_vector (lang_name, langs_included);
+ else
+ return 1;
+}
+
+void
+include_languages (char *lang_names)
+{
+ if (langs_excluded)
+ error (1, 0, "can't mix --include and --exclude options");
+ langs_included = append_strings_to_vector (langs_included, lang_names, white_space);
+}
+
+void
+exclude_languages (char *lang_names)
+{
+ if (langs_excluded)
+ error (1, 0, "can't mix --include and --exclude options");
+ langs_excluded = append_strings_to_vector (langs_excluded, lang_names, white_space);
+}
+
+char **
+append_strings_to_vector (char **vector_0, char *string, char *delimiter_class)
+{
+ char **vector;
+ if (vector_0)
+ {
+ int length = vector_length (vector_0);
+ vector_0 = REALLOC (vector_0, char *,
+ length + 2 + strlen (string) / 2);
+ vector = &vector_0[length];
+ }
+ else
+ vector = vector_0 = MALLOC (char *, 2 + strlen (string) / 2);
+
+ *vector = strtok (string, delimiter_class);
+ while (*vector)
+ *++vector = strtok (0, delimiter_class);
+ return REALLOC (vector_0, char *, ++vector - vector_0);
+}
+
+int
+vector_length (char **vector)
+{
+ int length = 0;
+ while (*vector++)
+ length++;
+ return length;
+}
+
+int
+string_in_vector (char const *string, char **vector)
+{
+ while (*vector)
+ if (strequ (string, *vector++))
+ return 1;
+ return 0;
+}
+
+
+/****************************************************************************/
+/* Convert a file name string to an absolute chain of `file_link's. */
+
+struct file_link *
+parse_file_name (char *file_name, struct file_link *relative_dir_link)
+{
+ struct file_link *flink;
+ char **links_0;
+ char **links;
+
+ if (IS_ABSOLUTE (file_name))
+ {
+#if HAVE_LINK
+ flink = get_link_from_string (SLASH_STRING, 0);
+#else
+ flink = 0;
+#endif
+ }
+ else if (relative_dir_link)
+ flink = relative_dir_link;
+ else if (current_dir_link)
+ flink = current_dir_link;
+ else
+ flink = get_current_dir_link ();
+
+ links = links_0 = vectorize_string (file_name, SLASH_STRING);
+ while (*links)
+ {
+ char const* link_name = *links++;
+ if (*link_name == '\0' || IS_DOT (link_name))
+ ;
+ else if (IS_DOT_DOT (link_name))
+ flink = flink->fl_parent;
+ else
+ {
+ struct stat st;
+ flink = get_link_from_string (link_name, flink);
+ if (!flink->fl_flags)
+ {
+ flink->fl_flags = classify_link (flink, &st);
+ if (!flink->fl_flags)
+ return 0;
+ }
+ }
+ }
+ free (links_0);
+ return flink;
+}
+
+/* Return an absolute chain of `file_link's representing the current
+ working directory. */
+
+struct file_link *
+get_current_dir_link (void)
+{
+ struct file_link *dir_link;
+ char *cwd_0;
+ char *cwd;
+ char *xcwd = 0;
+ char **links_0;
+ char **links;
+
+ if (current_dir_link)
+ return current_dir_link;
+
+ cwd_0 = getenv ("PWD");
+ if (cwd_0)
+ cwd_0 = strdup (cwd_0);
+ if (!same_as_dot (cwd_0))
+ cwd_0 = xcwd = xgetcwd ();
+ if (cwd_0 == 0)
+ error (1, errno, _("can't get working directory"));
+ cwd = cwd_0;
+#if HAVE_LINK
+ dir_link = get_link_from_string (SLASH_STRING, 0);
+ dir_link->fl_flags = (dir_link->fl_flags & ~FL_TYPE_MASK) | FL_TYPE_DIR;
+#else
+ dir_link = 0;
+#endif
+ links = links_0 = vectorize_string (cwd, SLASH_STRING);
+ while (*links)
+ {
+ struct stat st;
+ char const* link_name = *links++;
+ dir_link = get_link_from_string (link_name, dir_link);
+ if (!dir_link->fl_flags)
+ dir_link->fl_flags = classify_link (dir_link, &st);
+ }
+ chdir_to_link (dir_link);
+ free (links_0);
+ if (xcwd)
+ free (xcwd);
+ current_dir_link = dir_link;
+ return dir_link;
+}
+
+static int
+same_as_dot (char const *cwd)
+{
+ struct stat cwd_st;
+ struct stat dot_st;
+
+ if (cwd == 0 || *cwd != '/'
+ || stat (cwd, &cwd_st) < 0
+ || stat (".", &dot_st) < 0)
+ return 0;
+ return ((cwd_st.st_ino == dot_st.st_ino) && (cwd_st.st_dev == dot_st.st_dev));
+}
+
+/* Change the working directory to the directory represented by
+ `dir_link'. Choose the shortest path to the destination based on
+ the cached value of the current directory. */
+
+int
+chdir_to_link (struct file_link *dir_link)
+{
+ char *to_dir_name = ALLOCA (char, PATH_MAX);
+
+ if (current_dir_link == dir_link)
+ return 1;
+
+ if (current_dir_link)
+ maybe_relative_file_name (to_dir_name, dir_link, current_dir_link);
+ else
+ absolute_file_name (to_dir_name, dir_link);
+ if (chdir (to_dir_name) < 0)
+ {
+ if (IS_ABSOLUTE (to_dir_name))
+ error (0, errno, _("can't chdir to `%s'"), to_dir_name);
+ else
+ {
+ char *from_dir_name = ALLOCA (char, PATH_MAX);
+ absolute_file_name (from_dir_name, current_dir_link);
+ error (0, errno, _("can't chdir to `%s' from `%s'"), to_dir_name, from_dir_name);
+ }
+ return 0;
+ }
+ else
+ {
+ current_dir_link = dir_link;
+ return 1;
+ }
+}
+
+char **
+vectorize_string (char *string, char *delimiter_class)
+{
+ char **vector_0 = MALLOC (char *, 2 + strlen (string) / 2);
+ char **vector = vector_0;
+
+ *vector = strtok (string, delimiter_class);
+ while (*vector)
+ *++vector = strtok (0, delimiter_class);
+ return REALLOC (vector_0, char *, ++vector - vector_0);
+}
+
+void
+prune_file_names (char *str, struct file_link *from_link)
+{
+ char **file_names_0 = vectorize_string (str, white_space);
+ char **file_names = file_names_0;
+
+ while (*file_names)
+ {
+ struct file_link *flink = parse_file_name (*file_names++, from_link);
+ if (flink)
+ flink->fl_flags |= FL_PRUNE;
+ }
+ free (file_names_0);
+}
+
+
+/****************************************************************************/
+/* Gather information about the link at `flink'. If it's a good
+ file or directory, return its mod-time and type. */
+
+int
+classify_link (struct file_link *flink, struct stat *stp)
+{
+ unsigned int flags = 0;
+
+ if (!chdir_to_link (flink->fl_parent))
+ return 0;
+
+#ifdef S_IFLNK
+ if (lstat (flink->fl_name, stp) < 0)
+ {
+ error (0, errno, _("can't lstat `%s' from `%s'"), flink->fl_name, xgetcwd ());
+ return 0;
+ }
+ if (S_ISLNK (stp->st_mode))
+ {
+#endif
+ if (stat (flink->fl_name, stp) < 0)
+ {
+ error (0, errno, _("can't stat `%s' from `%s'"), flink->fl_name, xgetcwd ());
+ return 0;
+ }
+#ifdef S_IFLNK
+ flags |= FL_SYM_LINK;
+ }
+#endif
+ if (S_ISDIR (stp->st_mode))
+ flags |= FL_TYPE_DIR;
+ else if (S_ISREG (stp->st_mode))
+ flags |= FL_TYPE_FILE;
+ else
+ return 0;
+ return flags;
+}
+
+
+/****************************************************************************/
+/* Retrieve an existing flink; or if none exists, create one. */
+
+struct file_link *
+get_link_from_dirent (struct dirent *dirent, struct file_link *parent)
+{
+ struct file_link **slot;
+ struct file_link *new_link;
+
+ new_link = make_link_from_dirent (dirent, parent);
+ slot = (struct file_link **) hash_find_slot (&idh.idh_file_link_table, new_link);
+ if (HASH_VACANT (*slot))
+ hash_insert_at (&idh.idh_file_link_table, new_link, slot);
+ else
+ obstack_free (&idh.idh_file_link_obstack, new_link);
+ return *slot;
+}
+
+struct file_link *
+get_link_from_string (char const *name, struct file_link *parent)
+{
+ struct file_link **slot;
+ struct file_link *new_link;
+
+ new_link = make_link_from_string (name, parent);
+ slot = (struct file_link **) hash_find_slot (&idh.idh_file_link_table, new_link);
+ if (HASH_VACANT (*slot))
+ hash_insert_at (&idh.idh_file_link_table, new_link, slot);
+ else
+ obstack_free (&idh.idh_file_link_obstack, new_link);
+ return *slot;
+}
+
+struct file_link *
+make_link_from_dirent (struct dirent* dirent, struct file_link *parent)
+{
+ struct file_link *flink;
+
+ flink = (struct file_link *) obstack_alloc (&idh.idh_file_link_obstack,
+ sizeof (struct file_link) + strlen (dirent->d_name));
+ strcpy (flink->fl_name, dirent->d_name);
+ flink->fl_parent = parent ? parent : flink;
+ flink->fl_flags = 0;
+
+ return flink;
+}
+
+struct file_link *
+make_link_from_string (char const* name, struct file_link *parent)
+{
+ struct file_link *flink;
+
+ flink = (struct file_link *) obstack_alloc (&idh.idh_file_link_obstack,
+ sizeof (struct file_link) + strlen (name));
+ strcpy (flink->fl_name, name);
+ flink->fl_parent = parent ? parent : flink;
+ flink->fl_flags = 0;
+
+ return flink;
+}
+
+
+/****************************************************************************/
+/* Convert a `file_link' chain to a vector of component `file_link's,
+ with the root at [0]. Return a pointer beyond the final component. */
+
+struct file_link const **
+fill_link_vector (struct file_link const **vec_buf, struct file_link const *flink)
+{
+ vec_buf = fill_link_vector_1 (vec_buf, flink);
+ *vec_buf = 0;
+ return vec_buf;
+}
+
+struct file_link const **
+fill_link_vector_1 (struct file_link const **vec_buf, struct file_link const *flink)
+{
+ if (!IS_ROOT_FILE_LINK (flink))
+ vec_buf = fill_link_vector_1 (vec_buf, flink->fl_parent);
+ *vec_buf++ = flink;
+ return vec_buf;
+}
+
+
+/****************************************************************************/
+/* Fill BUF_0 with a path to TO_LINK. If a relative path from
+ FROM_LINK is possible (i.e., no intervening symbolic-links) and
+ shorter, return the relative path; otherwise, return an absolute
+ path. */
+
+char *
+maybe_relative_file_name (char *buf_0, struct file_link const *to_link, struct file_link const *from_link)
+{
+ struct file_link const **to_link_vec_0 = ALLOCA (struct file_link const *, PATH_MAX/2);
+ struct file_link const **from_link_vec_0 = ALLOCA (struct file_link const *, PATH_MAX/2);
+ struct file_link const **to_link_vec = to_link_vec_0;
+ struct file_link const **from_link_vec = from_link_vec_0;
+ struct file_link const **from_link_end;
+ struct file_link const **from_links;
+ int alloc_me = (buf_0 == 0);
+ char *buf;
+ int levels;
+
+ if (from_link == 0)
+ from_link = current_dir_link;
+
+ if (alloc_me)
+ buf_0 = MALLOC (char, PATH_MAX);
+
+ /* Optimize common cases. */
+ if (to_link == from_link)
+ strcpy (buf_0, ".");
+ else if (to_link->fl_parent == from_link)
+ strcpy (buf_0, to_link->fl_name);
+ else if (from_link->fl_flags & FL_SYM_LINK)
+ absolute_file_name (buf_0, to_link);
+ else if (to_link == from_link->fl_parent)
+ strcpy (buf_0, "..");
+ else if (to_link->fl_parent == from_link->fl_parent)
+ {
+ strcpy (buf_0, DOT_DOT_SLASH);
+ strcpy (&buf_0[3], to_link->fl_name);
+ }
+ else
+ {
+ from_link_end = fill_link_vector (from_link_vec, from_link);
+ fill_link_vector (to_link_vec, to_link);
+ while (*to_link_vec == *from_link_vec)
+ {
+ if (*to_link_vec == 0)
+ {
+ strcpy (buf_0, ".");
+ goto out;
+ }
+ to_link_vec++;
+ from_link_vec++;
+ }
+ levels = from_link_end - from_link_vec;
+ if (levels >= (from_link_vec - from_link_vec_0))
+ {
+ absolute_file_name (buf_0, to_link);
+ goto out;
+ }
+ for (from_links = from_link_vec; *from_links; from_links++)
+ if ((*from_links)->fl_flags & FL_SYM_LINK)
+ {
+ absolute_file_name (buf_0, to_link);
+ goto out;
+ }
+ buf = fill_dot_dots (buf_0, levels);
+ if (*to_link_vec == 0)
+ *--buf = '\0';
+ else
+ {
+ do
+ {
+ strcpy (buf, (*to_link_vec)->fl_name);
+ buf += strlen (buf);
+ if ((*to_link_vec)->fl_name[0] != SLASH_CHAR && *++to_link_vec)
+ *buf++ = SLASH_CHAR;
+ }
+ while (*to_link_vec);
+ }
+ }
+out:
+ if (alloc_me)
+ buf_0 = REALLOC (buf_0, char, 1 + strlen (buf_0));
+ return buf_0;
+}
+
+/* Fill `buf' with sequences of "../" in order to ascend so many
+ `levels' in a directory tree. */
+
+char *
+fill_dot_dots (char *buf, int levels)
+{
+ while (levels--)
+ {
+ strcpy (buf, DOT_DOT_SLASH);
+ buf += 3;
+ }
+ return buf;
+}
+
+
+/****************************************************************************/
+/* Fill `buffer' with the absolute path to `flink'. */
+
+char *
+absolute_file_name (char *buf_0, struct file_link const *flink)
+{
+ char *end;
+ int alloc_me = (buf_0 == 0);
+
+ if (alloc_me)
+ buf_0 = MALLOC (char, PATH_MAX);
+ end = absolute_file_name_1 (buf_0, flink);
+ /* Clip the trailing slash. */
+#if HAVE_LINK
+ if (end > &buf_0[1])
+ end--;
+#else
+ if (end > &buf_0[3])
+ end--;
+#endif
+ *end++ = '\0';
+ if (alloc_me)
+ buf_0 = REALLOC (buf_0, char, end - buf_0);
+ return buf_0;
+}
+
+static char *
+absolute_file_name_1 (char *buf, struct file_link const *flink)
+{
+ char *end;
+ if (IS_ROOT_FILE_LINK (flink))
+ end = buf;
+ else
+ end = absolute_file_name_1 (buf, flink->fl_parent);
+ strcpy (end, flink->fl_name);
+ if (*end == SLASH_CHAR)
+ end++;
+ else
+ {
+ end += strlen (end);
+ *end++ = SLASH_CHAR;
+ }
+ return end;
+}
+
+
+/****************************************************************************/
+/* Hash stuff for `struct member_file'. */
+
+unsigned long
+member_file_hash_1 (void const *key)
+{
+ return_ADDRESS_HASH_1 (((struct member_file const *) key)->mf_link);
+}
+
+unsigned long
+member_file_hash_2 (void const *key)
+{
+ return_ADDRESS_HASH_2 (((struct member_file const *) key)->mf_link);
+}
+
+int
+member_file_hash_compare (void const *x, void const *y)
+{
+ return_ADDRESS_COMPARE (((struct member_file const *) x)->mf_link,
+ ((struct member_file const *) y)->mf_link);
+}
+
+/* Collating sequence:
+ - language.map index
+ - mf_link: breadth-first, alphabetical */
+
+int
+member_file_qsort_compare (void const *x, void const *y)
+{
+ struct member_file const *mfx = *(struct member_file const *const *) x;
+ struct member_file const *mfy = *(struct member_file const *const *) y;
+ int result;
+
+ INTEGER_COMPARE (mfx->mf_lang_args->la_index, mfy->mf_lang_args->la_index, result);
+ if (result)
+ return result;
+ else
+ {
+ struct file_link const *flx = mfx->mf_link;
+ struct file_link const *fly = mfy->mf_link;
+ if (flx->fl_parent == fly->fl_parent)
+ return strcmp (flx->fl_name, fly->fl_name);
+ result = (links_depth (flx) - links_depth (fly));
+ if (result)
+ return result;
+ while (flx->fl_parent != fly->fl_parent)
+ {
+ flx = flx->fl_parent;
+ fly = fly->fl_parent;
+ }
+ return strcmp (flx->fl_name, fly->fl_name);
+ }
+}
+
+/****************************************************************************/
+/* Hash stuff for `struct file_link'. */
+
+unsigned long
+file_link_hash_1 (void const *key)
+{
+ unsigned long result = 0;
+ struct file_link const *parent = (IS_ROOT_FILE_LINK (((struct file_link const *) key))
+ ? 0 : ((struct file_link const *) key)->fl_parent);
+ STRING_HASH_1 (((struct file_link const *) key)->fl_name, result);
+ ADDRESS_HASH_1 (parent, result);
+ return result;
+}
+
+unsigned long
+file_link_hash_2 (void const *key)
+{
+ unsigned long result = 0;
+ struct file_link const *parent = (IS_ROOT_FILE_LINK (((struct file_link const *) key))
+ ? 0 : ((struct file_link const *) key)->fl_parent);
+ STRING_HASH_2 (((struct file_link const *) key)->fl_name, result);
+ ADDRESS_HASH_2 (parent, result);
+ return result;
+}
+
+int
+file_link_hash_compare (void const *x, void const *y)
+{
+ int result;
+ struct file_link const *x_parent = (IS_ROOT_FILE_LINK (((struct file_link const *) x))
+ ? 0 : ((struct file_link const *) x)->fl_parent);
+ struct file_link const *y_parent = (IS_ROOT_FILE_LINK (((struct file_link const *) y))
+ ? 0 : ((struct file_link const *) y)->fl_parent);
+ ADDRESS_COMPARE (x_parent, y_parent, result);
+ if (result)
+ return result;
+ STRING_COMPARE (((struct file_link const *) x)->fl_name,
+ ((struct file_link const *) y)->fl_name, result);
+ return result;
+}
+
+/* Count directory components between flink and its root. */
+
+int
+links_depth (struct file_link const *flink)
+{
+ int depth = 0;
+ while (!IS_ROOT_FILE_LINK (flink))
+ {
+ depth++;
+ flink = flink->fl_parent;
+ }
+ return depth;
+}
+
+#if HAVE_LINK
+
+/****************************************************************************/
+/* Hash stuff for `struct dev_ino'. */
+
+unsigned long
+dev_ino_hash_1 (void const *key)
+{
+ unsigned long result = 0;
+ INTEGER_HASH_1 (((struct dev_ino const *) key)->di_dev, result);
+ INTEGER_HASH_1 (((struct dev_ino const *) key)->di_ino, result);
+ return result;
+}
+
+unsigned long
+dev_ino_hash_2 (void const *key)
+{
+ unsigned long result = 0;
+ INTEGER_HASH_2 (((struct dev_ino const *) key)->di_dev, result);
+ INTEGER_HASH_2 (((struct dev_ino const *) key)->di_ino, result);
+ return result;
+}
+
+int
+dev_ino_hash_compare (void const *x, void const *y)
+{
+ int result;
+ INTEGER_COMPARE (((struct dev_ino const *) x)->di_ino,
+ ((struct dev_ino const *) y)->di_ino, result);
+ if (result)
+ return result;
+ INTEGER_COMPARE (((struct dev_ino const *) x)->di_dev,
+ ((struct dev_ino const *) y)->di_dev, result);
+ return result;
+}
+
+#endif
+
+/*******************************************************************/
+
+struct file_link *
+init_walker (struct idhead *idhp)
+{
+ init_idh_obstacks (idhp);
+ init_idh_tables (idhp);
+ return get_current_dir_link ();
+}
+
+void
+init_idh_obstacks (struct idhead *idhp)
+{
+ obstack_init (&idhp->idh_member_file_obstack);
+ obstack_init (&idhp->idh_file_link_obstack);
+#if HAVE_LINK
+ obstack_init (&idhp->idh_dev_ino_obstack);
+#endif
+}
+
+void
+init_idh_tables (struct idhead *idhp)
+{
+ hash_init (&idhp->idh_member_file_table, 16*1024,
+ member_file_hash_1, member_file_hash_2, member_file_hash_compare);
+ hash_init (&idhp->idh_file_link_table, 16*1024,
+ file_link_hash_1, file_link_hash_2, file_link_hash_compare);
+#if HAVE_LINK
+ hash_init (&idhp->idh_dev_ino_table, 16*1024,
+ dev_ino_hash_1, dev_ino_hash_2, dev_ino_hash_compare);
+#endif
+}
diff --git a/libidu/xnls.h b/libidu/xnls.h
new file mode 100644
index 0000000..f7f5b45
--- /dev/null
+++ b/libidu/xnls.h
@@ -0,0 +1,41 @@
+/* xnls.h -- NLS declarations
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Greg McGary <gkm@gnu.ai.mit.edu>
+
+ This program 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.
+
+ This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _i18n_h_
+#define _i18n_h_
+
+/* Take care of NLS matters. */
+
+#if HAVE_LOCALE_H
+# include <locale.h>
+#endif
+#if !HAVE_SETLOCALE
+# define setlocale(Category, Locale) /* empty */
+#endif
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(Text) gettext (Text)
+#else
+# define bindtextdomain(Domain, Directory) /* empty */
+# define textdomain(Domain) /* empty */
+# define _(Text) Text
+#endif
+
+#endif /* not _i18n_h_ */