diff options
author | Greg McGary <greg@mcgary.org> | 1997-04-18 06:43:32 +0000 |
---|---|---|
committer | Greg McGary <greg@mcgary.org> | 1997-04-18 06:43:32 +0000 |
commit | 916418ea1284e6aa64f50eba077e48ced5944acc (patch) | |
tree | 8e519e3a48ae80c8c17da3ba5acccd91530b22ff /libidu | |
parent | a560adff07afe7c1f35e4585e0694e89c842b245 (diff) | |
download | idutils-916418ea1284e6aa64f50eba077e48ced5944acc.tar.gz idutils-916418ea1284e6aa64f50eba077e48ced5944acc.tar.bz2 idutils-916418ea1284e6aa64f50eba077e48ced5944acc.zip |
Initial revision
Diffstat (limited to 'libidu')
-rw-r--r-- | libidu/Makefile.am | 22 | ||||
-rw-r--r-- | libidu/Makefile.in | 320 | ||||
-rw-r--r-- | libidu/dynvec.c | 59 | ||||
-rw-r--r-- | libidu/dynvec.h | 35 | ||||
-rw-r--r-- | libidu/fnprint.c | 138 | ||||
-rw-r--r-- | libidu/hash.c | 294 | ||||
-rw-r--r-- | libidu/hash.h | 144 | ||||
-rw-r--r-- | libidu/id-lang.map | 88 | ||||
-rw-r--r-- | libidu/idfile.c | 137 | ||||
-rw-r--r-- | libidu/idfile.h | 223 | ||||
-rw-r--r-- | libidu/idread.c | 280 | ||||
-rw-r--r-- | libidu/idwrite.c | 169 | ||||
-rw-r--r-- | libidu/scanners.c | 1212 | ||||
-rw-r--r-- | libidu/scanners.h | 70 | ||||
-rw-r--r-- | libidu/tokflags.h | 35 | ||||
-rw-r--r-- | libidu/walker.c | 1130 | ||||
-rw-r--r-- | libidu/xnls.h | 41 |
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_ */ |