diff options
Diffstat (limited to 'src')
54 files changed, 10273 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 000000000000..05f59a4bdabb --- /dev/null +++ b/src/Makefile @@ -0,0 +1,77 @@ +# Copyright (C) 1989 Free Software Foundation, Inc. +# written by Douglas C. Schmidt (schmidt@ics.uci.edu) +# +# This file is part of GNU GPERF. +# +# GNU GPERF 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 1, or (at your option) +# any later version. +# +# GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +CC = gcc +DFLAGS= -DLO_CAL -DGATHER_STATISTICS #-DRLIMIT_STACK +OFLAGS= -O -p -g -fstrength-reduce -fomit-frame-pointer -fdelayed-branch -finline-functions # gcc options +CFLAGS= $(DFLAGS) $(OFLAGS) +OBJS = options.o iterator.o main.o perfect.o keylist.o listnode.o xmalloc.o \ + hashtable.o boolarray.o readline.o stderr.o version.o getopt.o +SOURCES = options.c iterator.c main.c perfect.c keylist.c listnode.c xmalloc.c \ + hashtable.c boolarray.c readline.c stderr.c version.c getopt.c + +all: gperf + +gperf: $(OBJS) + $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) + +clean: + -rm -f *.o core *~ #*# + +realclean: clean + -rm -f gperf + +# dependencies +# DO NOT DELETE THIS LINE -- mkdep uses it. +# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY. + +boolarray.o: boolarray.c /usr/include/stdio.h boolarray.h prototype.h options.h +boolarray.o: /usr/include/stdio.h prototype.h +getopt.o: getopt.c /usr/include/stdio.h +hashtable.o: hashtable.c /usr/include/stdio.h hashtable.h keylist.h +hashtable.o: /usr/include/stdio.h listnode.h prototype.h prototype.h options.h +hashtable.o: /usr/include/stdio.h prototype.h +iterator.o: iterator.c /usr/include/stdio.h /usr/include/ctype.h iterator.h +iterator.o: prototype.h +keylist.o: keylist.c /usr/include/assert.h /usr/include/stdio.h options.h +keylist.o: /usr/include/stdio.h prototype.h readline.h prototype.h keylist.h +keylist.o: /usr/include/stdio.h listnode.h prototype.h hashtable.h keylist.h +keylist.o: prototype.h stderr.h prototype.h /usr/include/varargs.h +listnode.o: listnode.c /usr/include/stdio.h options.h /usr/include/stdio.h +listnode.o: prototype.h listnode.h prototype.h stderr.h prototype.h +listnode.o: /usr/include/varargs.h +main.o: main.c /usr/include/stdio.h stderr.h prototype.h /usr/include/varargs.h +main.o: options.h /usr/include/stdio.h prototype.h perfect.h prototype.h +main.o: keylist.h /usr/include/stdio.h listnode.h prototype.h boolarray.h +main.o: prototype.h +options.o: options.c /usr/include/stdio.h /usr/include/assert.h options.h +options.o: /usr/include/stdio.h prototype.h iterator.h prototype.h stderr.h +options.o: prototype.h /usr/include/varargs.h +perfect.o: perfect.c /usr/include/stdio.h /usr/include/assert.h +perfect.o: /usr/include/ctype.h options.h /usr/include/stdio.h prototype.h +perfect.o: perfect.h prototype.h keylist.h /usr/include/stdio.h listnode.h +perfect.o: prototype.h boolarray.h prototype.h stderr.h prototype.h +perfect.o: /usr/include/varargs.h +readline.o: readline.c /usr/include/stdio.h readline.h prototype.h +stderr.o: stderr.c /usr/include/stdio.h stderr.h prototype.h +stderr.o: /usr/include/varargs.h +version.o: version.c +xmalloc.o: xmalloc.c /usr/include/stdio.h + +# IF YOU PUT ANYTHING HERE IT WILL GO AWAY diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 000000000000..60f73c735a34 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,146 @@ +# Makefile for gperf/src + +# Copyright (C) 1989, 1992, 1993, 1998, 2000 Free Software Foundation, Inc. +# written by Douglas C. Schmidt (schmidt@ics.uci.edu) +# +# This file is part of GNU GPERF. +# +# GNU GPERF 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 1, or (at your option) +# any later version. +# +# GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +# Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. + +#### Start of system configuration section. #### + +# Directories used by "make": +srcdir = @srcdir@ + +# Directories used by "make install": +prefix = @prefix@ +local_prefix = /usr/local +exec_prefix = @exec_prefix@ +bindir = @bindir@ + +# Programs used by "make": +# C compiler +CC = @CC@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +# C++ compiler +CXX = @CXX@ +CXXFLAGS = @CXXFLAGS@ +CXXCPP = @CXXCPP@ +# Both C and C++ compiler +LDFLAGS = @LDFLAGS@ +# Other +MV = mv +LN = ln +RM = rm -f +@SET_MAKE@ + +# Programs used by "make install": +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +MKINSTALLDIRS = $(SHELL) $(srcdir)/../mkinstalldirs + +#### End of system configuration section. #### + +SHELL = /bin/sh + +VPATH = $(srcdir) + +OBJECTS = new.o options.o iterator.o main.o gen-perf.o key-list.o list-node.o \ + hash-table.o bool-array.o read-line.o trace.o vectors.o version.o +LIBS = ../lib/libgp.a -lm +CPPFLAGS = -I. -I$(srcdir)/../lib + +TARGETPROG = gperf + +all : $(TARGETPROG) + +$(TARGETPROG): $(OBJECTS) + $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBS) + +# Don't use implicit rules, since AIX "make" and OSF/1 "make" don't always +# expand $< correctly in this context. +# +#%.o : %.c +# $(CC) $(CFLAGS) $(CPPFLAGS) -c $< +# +#%.o : %.cc +# $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< + +# Dependencies. +CONFIG_H = config.h +VERSION_H = version.h +VECTORS_H = vectors.h +TRACE_H = trace.h +READ_LINE_H = read-line.h read-line.icc $(TRACE_H) +OPTIONS_H = options.h options.icc $(TRACE_H) +LIST_NODE_H = list-node.h $(VECTORS_H) +KEY_LIST_H = key-list.h $(LIST_NODE_H) $(VECTORS_H) $(READ_LINE_H) +ITERATOR_H = iterator.h +HASH_TABLE_H = hash-table.h $(LIST_NODE_H) +BOOL_ARRAY_H = bool-array.h bool-array.icc $(TRACE_H) $(OPTIONS_H) +GEN_PERF_H = gen-perf.h $(KEY_LIST_H) $(BOOL_ARRAY_H) + +bool-array.o : bool-array.cc $(BOOL_ARRAY_H) $(OPTIONS_H) $(TRACE_H) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(srcdir)/bool-array.cc +gen-perf.o : gen-perf.cc $(GEN_PERF_H) $(OPTIONS_H) $(TRACE_H) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(srcdir)/gen-perf.cc +hash-table.o : hash-table.cc $(HASH_TABLE_H) $(OPTIONS_H) $(TRACE_H) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(srcdir)/hash-table.cc +iterator.o : iterator.cc $(ITERATOR_H) $(TRACE_H) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(srcdir)/iterator.cc +key-list.o : key-list.cc $(KEY_LIST_H) $(OPTIONS_H) $(READ_LINE_H) $(HASH_TABLE_H) $(TRACE_H) $(VERSION_H) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(srcdir)/key-list.cc +list-node.o : list-node.cc $(LIST_NODE_H) $(OPTIONS_H) $(TRACE_H) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(srcdir)/list-node.cc +main.o : main.cc $(OPTIONS_H) $(GEN_PERF_H) $(TRACE_H) $(CONFIG_H) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(srcdir)/main.cc +new.o : new.cc $(TRACE_H) $(CONFIG_H) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(srcdir)/new.cc +options.o : options.cc $(OPTIONS_H) $(ITERATOR_H) $(TRACE_H) $(VECTORS_H) $(VERSION_H) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(srcdir)/options.cc +read-line.o : read-line.cc $(READ_LINE_H) $(OPTIONS_H) $(TRACE_H) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(srcdir)/read-line.cc +trace.o : trace.cc $(TRACE_H) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(srcdir)/trace.cc +vectors.o : vectors.cc $(VECTORS_H) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(srcdir)/vectors.cc +version.o : version.cc $(VERSION_H) + $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(srcdir)/version.cc + +install : all force + $(MKINSTALLDIRS) $(DESTDIR)$(bindir) + $(INSTALL_PROGRAM) $(TARGETPROG) $(DESTDIR)$(bindir)/$(TARGETPROG) + +installdirs : force + $(MKINSTALLDIRS) $(DESTDIR)$(bindir) + +uninstall : force + $(RM) $(DESTDIR)$(bindir)/$(TARGETPROG) + +check : all + +mostlyclean : clean + +clean : force + $(RM) *~ *.s *.o *.a $(TARGETPROG) core + +distclean : clean + $(RM) config.status config.log config.cache Makefile config.h + +maintainer-clean : distclean + +force : diff --git a/src/bool-array.cc b/src/bool-array.cc new file mode 100644 index 000000000000..0774b2d65265 --- /dev/null +++ b/src/bool-array.cc @@ -0,0 +1,49 @@ +/* Fast lookup table abstraction implemented as an Iteration Number Array + Copyright (C) 1989-1998 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "bool-array.h" + +#include <stdio.h> +#include <string.h> +#include "options.h" +#include "trace.h" + +STORAGE_TYPE * Bool_Array::storage_array; +STORAGE_TYPE Bool_Array::iteration_number; +unsigned int Bool_Array::size; + +/* Prints out debugging diagnostics. */ + +Bool_Array::~Bool_Array (void) +{ + T (Trace t ("Bool_Array::~Bool_Array");) + if (option[DEBUG]) + fprintf (stderr, "\ndumping boolean array information\n" + "size = %d\niteration number = %d\nend of array dump\n", + size, iteration_number); +} + +#ifndef __OPTIMIZE__ + +#define INLINE /* not inline */ +#include "bool-array.icc" +#undef INLINE + +#endif /* not defined __OPTIMIZE__ */ diff --git a/src/bool-array.h b/src/bool-array.h new file mode 100644 index 000000000000..8330fcd22019 --- /dev/null +++ b/src/bool-array.h @@ -0,0 +1,66 @@ +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Simple lookup table abstraction implemented as an Iteration Number Array. + + Copyright (C) 1989-1998 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Define and implement a simple boolean array abstraction, + uses an Iteration Numbering implementation to save on initialization time. */ + +#ifndef bool_array_h +#define bool_array_h 1 + +#include "trace.h" + +#ifdef LO_CAL +/* If we are on a memory diet then we'll only make these use a limited + amount of storage space. */ +typedef unsigned short STORAGE_TYPE; +#else +typedef unsigned int STORAGE_TYPE; +#endif + +class Bool_Array +{ +private: + static STORAGE_TYPE *storage_array; /* Initialization of the index space. */ + static STORAGE_TYPE iteration_number; /* Keep track of the current iteration. */ + static unsigned int size; /* Keep track of array size. */ + +public: + Bool_Array (void); + ~Bool_Array (void); + static void init (STORAGE_TYPE *buffer, unsigned int s); + static int find (int hash_value); + static void reset (void); +}; + +#ifdef __OPTIMIZE__ /* efficiency hack! */ + +#include <stdio.h> +#include <string.h> +#include "options.h" +#define INLINE inline +#include "bool-array.icc" +#undef INLINE + +#endif + +#endif diff --git a/src/bool-array.icc b/src/bool-array.icc new file mode 100644 index 000000000000..6de6f236e712 --- /dev/null +++ b/src/bool-array.icc @@ -0,0 +1,85 @@ +/* Inline Functions for bool-array.{h,cc}. + + Copyright (C) 1989-1998 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +// This needs: +//#include <stdio.h> +//#include <string.h> +//#include "options.h" +//#include "trace.h" + +INLINE +Bool_Array::Bool_Array (void) +{ + T (Trace t ("Bool_Array::Bool_Array");) + storage_array = 0; + iteration_number = size = 0; +} + +INLINE void +Bool_Array::init (STORAGE_TYPE *buffer, unsigned int s) +{ + T (Trace t ("Bool_Array::init");) + size = s; + iteration_number = 1; + storage_array = buffer; + memset (storage_array, 0, s * sizeof (*storage_array)); + if (option[DEBUG]) + fprintf (stderr, "\nbool array size = %d, total bytes = %d\n", + size, (unsigned int) (size * sizeof (*storage_array))); +} + +INLINE int +Bool_Array::find (int index) +{ + T (Trace t ("Bool_Array::find");) + if (storage_array[index] == iteration_number) + return 1; + else + { + storage_array[index] = iteration_number; + return 0; + } +} + +INLINE void +Bool_Array::reset (void) +{ + T (Trace t ("Bool_Array::reset");) + /* If we wrap around it's time to zero things out again! However, this only + occurs once about every 2^31 or 2^15 iterations, so it should probably + never happen! */ + + if (++iteration_number == 0) + { + if (option[DEBUG]) + { + fprintf (stderr, "(re-initializing bool_array)..."); + fflush (stderr); + } + iteration_number = 1; + memset (storage_array, 0, size * sizeof (*storage_array)); + if (option[DEBUG]) + { + fprintf (stderr, "done\n"); + fflush (stderr); + } + } +} diff --git a/src/boolarray.c b/src/boolarray.c new file mode 100644 index 000000000000..890613499200 --- /dev/null +++ b/src/boolarray.c @@ -0,0 +1,90 @@ +/* Fast lookup table abstraction implemented as a Guilmette Array + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include "boolarray.h" +#include "options.h" + +/* Locally visible BOOL_ARRAY object. */ + +static BOOL_ARRAY bool_array; + +/* Prints out debugging diagnostics. */ + +void +bool_array_destroy () +{ + if (OPTION_ENABLED (option, DEBUG)) + fprintf (stderr, "\ndumping boolean array information\niteration number = %d\nend of array dump\n", + bool_array.iteration_number); + free ((char *) bool_array.storage_array); +} + +void +bool_array_init (size) + int size; +{ + STORAGE_TYPE *xmalloc (); + bool_array.iteration_number = 1; + bool_array.size = size; + bool_array.storage_array = xmalloc (size * sizeof *bool_array.storage_array); + bzero (bool_array.storage_array, size * sizeof *bool_array.storage_array); + if (OPTION_ENABLED (option, DEBUG)) + fprintf (stderr, "\nbool array size = %d, total bytes = %d\n", + bool_array.size, bool_array.size * sizeof *bool_array.storage_array); +} + +bool +lookup (index) + int index; +{ + if (bool_array.storage_array[index] == bool_array.iteration_number) + return 1; + else + { + bool_array.storage_array[index] = bool_array.iteration_number; + return 0; + } +} + +/* Simple enough to reset, eh?! */ + +void +bool_array_reset () +{ + /* If we wrap around it's time to zero things out again! */ + + + if (++bool_array.iteration_number == 0) + { + if (OPTION_ENABLED (option, DEBUG)) + { + fprintf (stderr, "(re-initializing bool_array)..."); + fflush (stderr); + } + bool_array.iteration_number = 1; + bzero (bool_array.storage_array, bool_array.size * sizeof *bool_array.storage_array); + if (OPTION_ENABLED (option, DEBUG)) + { + fprintf (stderr, "done\n"); + fflush (stderr); + } + } +} diff --git a/src/boolarray.h b/src/boolarray.h new file mode 100644 index 000000000000..48339755060a --- /dev/null +++ b/src/boolarray.h @@ -0,0 +1,48 @@ +/* Simple lookup table abstraction implemented as a Guilmette Array. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Define and implement a simple boolean array abstraction, + uses a Guilmette array implementation to save on initialization time. */ + +#ifndef _boolarray_h +#define _boolarray_h +#include "prototype.h" + +#ifdef LO_CAL +/* If we are on a memory diet then we'll only make these use a limited + amount of storage space. */ +typedef unsigned short STORAGE_TYPE; +#else +typedef int STORAGE_TYPE; +#endif +typedef struct bool_array +{ + STORAGE_TYPE *storage_array; /* Initialization of the index space. */ + STORAGE_TYPE iteration_number; /* Keep track of the current iteration. */ + int size; /* Size of the entire array (dynamically initialized). */ +} BOOL_ARRAY; + +extern void bool_array_init P ((int size)); +extern void bool_array_destroy P ((void)); +extern bool lookup P ((int hash_value)); +extern void bool_array_reset P ((void)); + +#endif /* _boolarray_h */ diff --git a/src/config.h.in b/src/config.h.in new file mode 100644 index 000000000000..4d3d76212d6c --- /dev/null +++ b/src/config.h.in @@ -0,0 +1,19 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define if the C++ compiler supports "throw ()" declarations. */ +#undef HAVE_THROW_DECL + +/* Define if you have the getrlimit function. */ +#undef HAVE_GETRLIMIT + +/* Define if you have the setrlimit function. */ +#undef HAVE_SETRLIMIT + +/* Define if you have the <sys/resource.h> header file. */ +#undef HAVE_SYS_RESOURCE_H + +/* Define if you have the <sys/time.h> header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H diff --git a/src/configure b/src/configure new file mode 100755 index 000000000000..edd1fd7f0520 --- /dev/null +++ b/src/configure @@ -0,0 +1,1660 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.13 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.13" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=gen-perf.cc + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +ac_exeext= +ac_objext=o +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:527: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:556: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:586: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_prog_rejected=no + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -z "$CC"; then + case "`uname -s`" in + *win32* | *WIN32*) + # Extract the first word of "cl", so it can be a program name with args. +set dummy cl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:637: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="cl" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + ;; + esac + fi + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:669: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext << EOF + +#line 680 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:685: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:711: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:716: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <<EOF +#ifdef __GNUC__ + yes; +#endif +EOF +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:725: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes +else + GCC= +fi + +ac_test_CFLAGS="${CFLAGS+set}" +ac_save_CFLAGS="$CFLAGS" +CFLAGS= +echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:744: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi + + echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:776: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext <<EOF +#line 791 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:797: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext <<EOF +#line 808 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:814: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -nologo -E" + cat > conftest.$ac_ext <<EOF +#line 825 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:831: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + + for ac_prog in $CCC c++ g++ gcc CC cxx cc++ cl +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:860: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CXX="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CXX="$ac_cv_prog_CXX" +if test -n "$CXX"; then + echo "$ac_t""$CXX" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$CXX" && break +done +test -n "$CXX" || CXX="gcc" + + +echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:892: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) works" >&5 + +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + +cat > conftest.$ac_ext << EOF + +#line 903 "configure" +#include "confdefs.h" + +int main(){return(0);} +EOF +if { (eval echo configure:908: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cxx_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cxx_cross=no + else + ac_cv_prog_cxx_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cxx_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cxx_works" 1>&6 +if test $ac_cv_prog_cxx_works = no; then + { echo "configure: error: installation or configuration problem: C++ compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:934: checking whether the C++ compiler ($CXX $CXXFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cxx_cross" 1>&6 +cross_compiling=$ac_cv_prog_cxx_cross + +echo $ac_n "checking whether we are using GNU C++""... $ac_c" 1>&6 +echo "configure:939: checking whether we are using GNU C++" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gxx'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.C <<EOF +#ifdef __GNUC__ + yes; +#endif +EOF +if { ac_try='${CXX-g++} -E conftest.C'; { (eval echo configure:948: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gxx=yes +else + ac_cv_prog_gxx=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gxx" 1>&6 + +if test $ac_cv_prog_gxx = yes; then + GXX=yes +else + GXX= +fi + +ac_test_CXXFLAGS="${CXXFLAGS+set}" +ac_save_CXXFLAGS="$CXXFLAGS" +CXXFLAGS= +echo $ac_n "checking whether ${CXX-g++} accepts -g""... $ac_c" 1>&6 +echo "configure:967: checking whether ${CXX-g++} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cxx_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.cc +if test -z "`${CXX-g++} -g -c conftest.cc 2>&1`"; then + ac_cv_prog_cxx_g=yes +else + ac_cv_prog_cxx_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cxx_g" 1>&6 +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS="$ac_save_CXXFLAGS" +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi + + echo $ac_n "checking how to run the C++ preprocessor""... $ac_c" 1>&6 +echo "configure:999: checking how to run the C++ preprocessor" >&5 +if test -z "$CXXCPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CXXCPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + CXXCPP="${CXX-g++} -E" + cat > conftest.$ac_ext <<EOF +#line 1012 "configure" +#include "confdefs.h" +#include <stdlib.h> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1017: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CXXCPP=/lib/cpp +fi +rm -f conftest* + ac_cv_prog_CXXCPP="$CXXCPP" +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross +fi +fi +CXXCPP="$ac_cv_prog_CXXCPP" +echo "$ac_t""$CXXCPP" 1>&6 + + # Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:1052: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'cl_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + for ac_prog in ginstall installbsd scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + # OSF/1 installbsd also uses dspmsg, but is usable. + : + else + if test $ac_prog = installbsd && + grep src/bos $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX installbsd doesn't work without option "-g". + : + else + cl_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + fi + done + ;; + esac + done + IFS="$ac_save_ifs" + # As a last resort, use cp. + test -z "$cl_cv_path_install" && cl_cv_path_install="cp" + +fi + INSTALL="$cl_cv_path_install" +fi +echo "$ac_t""$INSTALL" 1>&6 +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='$(INSTALL)' +if test -z "$INSTALL_DATA"; then + case "$INSTALL" in + cp | */cp ) INSTALL_DATA='$(INSTALL)' ;; + * ) INSTALL_DATA='$(INSTALL) -m 644' ;; + esac +fi + + echo $ac_n "checking for working throw()""... $ac_c" 1>&6 +echo "configure:1105: checking for working throw()" >&5 +if eval "test \"`echo '$''{'gp_cv_cxx_throw_decl'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + +ac_ext=C +# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cxx_cross + +cat > conftest.$ac_ext <<EOF +#line 1119 "configure" +#include "confdefs.h" +#include <stdlib.h> +void operator delete (void* ptr) throw() {} +int main() { + +; return 0; } +EOF +if { (eval echo configure:1127: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + gp_cv_cxx_throw_decl=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + gp_cv_cxx_throw_decl=no +fi +rm -f conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + + +fi + +echo "$ac_t""$gp_cv_cxx_throw_decl" 1>&6 +if test $gp_cv_cxx_throw_decl = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_THROW_DECL 1 +EOF + +fi +for ac_hdr in unistd.h sys/time.h sys/resource.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1158: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1163 "configure" +#include "confdefs.h" +#include <$ac_hdr> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1168: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <<EOF +#define $ac_tr_hdr 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + if test $ac_cv_header_sys_resource_h = yes; then +for ac_func in getrlimit +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1198: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1203 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1226: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + if test $ac_cv_func_getrlimit = yes; then +for ac_func in setrlimit +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1254: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1259 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1282: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + fi +fi +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS <<EOF +#! /bin/sh +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# This directory was configured as follows, +# on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.13" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir + +trap 'rm -fr `echo "Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS <<EOF + +# Protect against being on the right side of a sed subst in config.status. +sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g; + s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@SHELL@%$SHELL%g +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@FFLAGS@%$FFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@SET_MAKE@%$SET_MAKE%g +s%@CC@%$CC%g +s%@CPP@%$CPP%g +s%@CXX@%$CXX%g +s%@CXXCPP@%$CXXCPP%g +s%@INSTALL@%$INSTALL%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <<EOF + +CONFIG_FILES=\${CONFIG_FILES-"Makefile"} +EOF +cat >> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <<EOF + CONFIG_HEADERS="config.h" +EOF +cat >> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + +EOF +cat >> $CONFIG_STATUS <<EOF + +EOF +cat >> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/src/configure.in b/src/configure.in new file mode 100644 index 000000000000..e8880d507f85 --- /dev/null +++ b/src/configure.in @@ -0,0 +1,71 @@ +dnl autoconf configuration for gperf/src + +dnl Copyright (C) 1998, 2000 Free Software Foundation, Inc. +dnl written by Douglas C. Schmidt (schmidt@ics.uci.edu) +dnl +dnl This file is part of GNU GPERF. +dnl +dnl GNU GPERF is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 1, or (at your option) +dnl any later version. +dnl +dnl GNU GPERF is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with GNU GPERF; see the file COPYING. If not, write to the +dnl Free Software Foundation, 59 Temple Place - Suite 330, Boston, +dnl MA 02111-1307, USA. + +AC_INIT(gen-perf.cc) +AC_CONFIG_HEADER(config.h) +AC_PROG_MAKE_SET +dnl +dnl checks for programs +dnl +AC_PROG_CC + dnl sets variable CC +AC_PROG_CPP + dnl sets variable CPP +AC_PROG_CXX + dnl sets variable CXX +AC_PROG_CXXCPP + dnl sets variable CXXCPP +CL_PROG_INSTALL + dnl sets variables INSTALL, INSTALL_DATA, INSTALL_PROGRAM +dnl +dnl checks for compiler characteristics +dnl +AC_MSG_CHECKING([for working throw()]) +AC_CACHE_VAL(gp_cv_cxx_throw_decl,[ +AC_LANG_SAVE() +AC_LANG_CPLUSPLUS() +AC_TRY_COMPILE([#include <stdlib.h> +void operator delete (void* ptr) throw() {}], [], +gp_cv_cxx_throw_decl=yes, gp_cv_cxx_throw_decl=no) +AC_LANG_RESTORE() +]) +AC_MSG_RESULT([$]gp_cv_cxx_throw_decl) +if test [$]gp_cv_cxx_throw_decl = yes; then + AC_DEFINE(HAVE_THROW_DECL) +fi +dnl +dnl checks for functions and declarations +dnl +AC_CHECK_HEADERS(unistd.h sys/time.h sys/resource.h) + dnl DEFs HAVE_UNISTD_H, HAVE_SYS_TIME_H, HAVE_SYS_RESOURCE_H +if test $ac_cv_header_sys_resource_h = yes; then +AC_CHECK_FUNCS(getrlimit) + dnl DEFS HAVE_GETRLIMIT +if test $ac_cv_func_getrlimit = yes; then +AC_CHECK_FUNCS(setrlimit) + dnl DEFS HAVE_SETRLIMIT +fi +fi +dnl +dnl That's it. +dnl +AC_OUTPUT(Makefile) diff --git a/src/gen-perf.cc b/src/gen-perf.cc new file mode 100644 index 000000000000..0b5109d4ff4d --- /dev/null +++ b/src/gen-perf.cc @@ -0,0 +1,359 @@ +/* Provides high-level routines to manipulate the keywork list + structures the code generation output. + Copyright (C) 1989-1998, 2000 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <stdio.h> +#include <stdlib.h> /* declares rand(), srand() */ +#include <time.h> /* declares time() */ +#include "options.h" +#include "gen-perf.h" +#include "trace.h" + +/* Efficiently returns the least power of two greater than or equal to X! */ +#define POW(X) ((!X)?1:(X-=1,X|=X>>1,X|=X>>2,X|=X>>4,X|=X>>8,X|=X>>16,(++X))) + +/* Reads input keys, possibly applies the reordering heuristic, sets the + maximum associated value size (rounded up to the nearest power of 2), + may initialize the associated values array, and determines the maximum + hash table size. Note: using the random numbers is often helpful, + though not as deterministic, of course! */ + +Gen_Perf::Gen_Perf (void) +{ + T (Trace t ("Gen_Perf::Gen_Perf");) + int asso_value_max; + int non_linked_length; + + Key_List::read_keys (); + if (option[ORDER]) + reorder (); + asso_value_max = option.get_asso_max (); + non_linked_length = Key_List::keyword_list_length (); + num_done = 1; + fewest_collisions = 0; + if (asso_value_max == 0) + asso_value_max = non_linked_length; + else if (asso_value_max > 0) + asso_value_max *= non_linked_length; + else /* if (asso_value_max < 0) */ + asso_value_max = non_linked_length / -asso_value_max; + option.set_asso_max (POW (asso_value_max)); + + if (option[RANDOM]) + { + srand ((long) time (0)); + + for (int i = 0; i < ALPHA_SIZE; i++) + asso_values[i] = (rand () & asso_value_max - 1); + } + else + { + int asso_value = option.initial_value (); + + if (asso_value) /* Initialize array if user requests non-zero default. */ + for (int i = ALPHA_SIZE - 1; i >= 0; i--) + asso_values[i] = asso_value & option.get_asso_max () - 1; + } + max_hash_value = Key_List::max_key_length () + option.get_asso_max () * + option.get_max_keysig_size (); + + if (option[DEBUG]) + fprintf (stderr, "total non-linked keys = %d\nmaximum associated value is %d" + "\nmaximum size of generated hash table is %d\n", + non_linked_length, asso_value_max, max_hash_value); +} + +/* Merge two disjoint hash key multisets to form the ordered disjoint union of the sets. + (In a multiset, an element can occur multiple times.) + Precondition: both set_1 and set_2 must be ordered. Returns the length + of the combined set. */ + +inline int +Gen_Perf::compute_disjoint_union (const char *set_1, int size_1, const char *set_2, int size_2, char *set_3) +{ + T (Trace t ("Gen_Perf::compute_disjoint_union");) + char *base = set_3; + + while (size_1 > 0 && size_2 > 0) + if (*set_1 == *set_2) + set_1++, size_1--, set_2++, size_2--; + else + { + char next; + if (*set_1 < *set_2) + next = *set_1++, size_1--; + else + next = *set_2++, size_2--; + if (set_3 == base || next != set_3[-1]) + *set_3++ = next; + } + + while (size_1 > 0) + { + char next; + next = *set_1++, size_1--; + if (set_3 == base || next != set_3[-1]) + *set_3++ = next; + } + + while (size_2 > 0) + { + char next; + next = *set_2++, size_2--; + if (set_3 == base || next != set_3[-1]) + *set_3++ = next; + } + return set_3 - base; +} + +/* Sort the UNION_SET in increasing frequency of occurrence. + This speeds up later processing since we may assume the resulting + set (Set_3, in this case), is ordered. Uses insertion sort, since + the UNION_SET is typically short. */ + +inline void +Gen_Perf::sort_set (char *union_set, int len) +{ + T (Trace t ("Gen_Perf::sort_set");) + int i, j; + + for (i = 0, j = len - 1; i < j; i++) + { + int curr; + char tmp; + + for (curr = i + 1, tmp = union_set[curr]; + curr > 0 && occurrences[(unsigned char)tmp] < occurrences[(unsigned char)(union_set[curr-1])]; + curr--) + union_set[curr] = union_set[curr - 1]; + + union_set[curr] = tmp; + } +} + +/* Generate a key set's hash value. */ + +inline int +Gen_Perf::hash (List_Node *key_node) +{ + T (Trace t ("Gen_Perf::hash");) + int sum = option[NOLENGTH] ? 0 : key_node->key_length; + + const char *p = key_node->char_set; + int i = key_node->char_set_length; + for (; i > 0; p++, i--) + sum += asso_values[(unsigned char)(*p)]; + + return key_node->hash_value = sum; +} + +/* Find out how character value change affects successfully hashed items. + Returns FALSE if no other hash values are affected, else returns TRUE. + Note that because Option.Get_Asso_Max is a power of two we can guarantee + that all legal Asso_Values are visited without repetition since + Option.Get_Jump was forced to be an odd value! */ + +inline int +Gen_Perf::affects_prev (char c, List_Node *curr) +{ + T (Trace t ("Gen_Perf::affects_prev");) + int original_char = asso_values[(unsigned char)c]; + int total_iterations = !option[FAST] + ? option.get_asso_max () : option.get_iterations () ? option.get_iterations () : keyword_list_length (); + + /* Try all legal associated values. */ + + for (int i = total_iterations - 1; i >= 0; i--) + { + int collisions = 0; + + asso_values[(unsigned char)c] = + (asso_values[(unsigned char)c] + (option.get_jump () ? option.get_jump () : rand ())) + & (option.get_asso_max () - 1); + + /* Iteration Number array is a win, O(1) intialization time! */ + reset (); + + /* See how this asso_value change affects previous keywords. If + it does better than before we'll take it! */ + + for (List_Node *ptr = head; + !Bool_Array::find (hash (ptr)) || ++collisions < fewest_collisions; + ptr = ptr->next) + if (ptr == curr) + { + fewest_collisions = collisions; + if (option[DEBUG]) + fprintf (stderr, "- resolved after %d iterations", total_iterations - i); + return 0; + } + } + + /* Restore original values, no more tries. */ + asso_values[(unsigned char)c] = original_char; + /* If we're this far it's time to try the next character.... */ + return 1; +} + +/* Change a character value, try least-used characters first. */ + +void +Gen_Perf::change (List_Node *prior, List_Node *curr) +{ + T (Trace t ("Gen_Perf::change");) + static char *union_set; + int union_set_length; + + if (!union_set) + union_set = new char [2 * option.get_max_keysig_size ()]; + + if (option[DEBUG]) + { + fprintf (stderr, "collision on keyword #%d, prior = \"%.*s\", curr = \"%.*s\" hash = %d\n", + num_done, + prior->key_length, prior->key, + curr->key_length, curr->key, + curr->hash_value); + fflush (stderr); + } + union_set_length = compute_disjoint_union (prior->char_set, prior->char_set_length, curr->char_set, curr->char_set_length, union_set); + sort_set (union_set, union_set_length); + + /* Try changing some values, if change doesn't alter other values continue normal action. */ + fewest_collisions++; + + const char *p = union_set; + int i = union_set_length; + for (; i > 0; p++, i--) + if (!affects_prev (*p, curr)) + { + if (option[DEBUG]) + { + fprintf (stderr, " by changing asso_value['%c'] (char #%d) to %d\n", + *p, p - union_set + 1, asso_values[(unsigned char)(*p)]); + fflush (stderr); + } + return; /* Good, doesn't affect previous hash values, we'll take it. */ + } + + for (List_Node *ptr = head; ptr != curr; ptr = ptr->next) + hash (ptr); + + hash (curr); + + if (option[DEBUG]) + { + fprintf (stderr, "** collision not resolved after %d iterations, %d duplicates remain, continuing...\n", + !option[FAST] ? option.get_asso_max () : option.get_iterations () ? option.get_iterations () : keyword_list_length (), + fewest_collisions + total_duplicates); + fflush (stderr); + } +} + +/* Does the hard stuff.... + Initializes the Iteration Number array, and attempts to find a perfect + function that will hash all the key words without getting any + duplications. This is made much easier since we aren't attempting + to generate *minimum* functions, only perfect ones. + If we can't generate a perfect function in one pass *and* the user + hasn't enabled the DUP option, we'll inform the user to try the + randomization option, use -D, or choose alternative key positions. + The alternatives (e.g., back-tracking) are too time-consuming, i.e, + exponential in the number of keys. */ + +int +Gen_Perf::operator() (void) +{ + T (Trace t ("Gen_Perf::operator()");) +#if LARGE_STACK_ARRAYS + STORAGE_TYPE buffer[max_hash_value + 1]; +#else + // Note: we don't use new, because that invokes a custom operator new. + STORAGE_TYPE *buffer + = (STORAGE_TYPE*) malloc (sizeof(STORAGE_TYPE) * (max_hash_value + 1)); + if (buffer == NULL) + abort (); +#endif + + Bool_Array::init (buffer, max_hash_value + 1); + + List_Node *curr; + for (curr = head; curr; curr = curr->next) + { + hash (curr); + + for (List_Node *ptr = head; ptr != curr; ptr = ptr->next) + if (ptr->hash_value == curr->hash_value) + { + change (ptr, curr); + break; + } + num_done++; + } + + /* Make one final check, just to make sure nothing weird happened.... */ + + Bool_Array::reset (); + + for (curr = head; curr; curr = curr->next) + if (Bool_Array::find (hash (curr))) + if (option[DUP]) /* Keep track of this number... */ + total_duplicates++; + else /* Yow, big problems. we're outta here! */ + { + fprintf (stderr, "\nInternal error, duplicate value %d:\n" + "try options -D or -r, or use new key positions.\n\n", hash (curr)); +#if !LARGE_STACK_ARRAYS + free ((char *) buffer); +#endif + return 1; + } + + /* Sorts the key word list by hash value, and then outputs the list. + The generated hash table code is only output if the early stage of + processing turned out O.K. */ + + sort (); + output (); +#if !LARGE_STACK_ARRAYS + free ((char *) buffer); +#endif + return 0; +} + +/* Prints out some diagnostics upon completion. */ + +Gen_Perf::~Gen_Perf (void) +{ + T (Trace t ("Gen_Perf::~Gen_Perf");) + if (option[DEBUG]) + { + fprintf (stderr, "\ndumping occurrence and associated values tables\n"); + + for (int i = 0; i < ALPHA_SIZE; i++) + if (occurrences[i]) + fprintf (stderr, "asso_values[%c] = %6d, occurrences[%c] = %6d\n", + i, asso_values[i], i, occurrences[i]); + + fprintf (stderr, "end table dumping\n"); + + } +} + diff --git a/src/gen-perf.h b/src/gen-perf.h new file mode 100644 index 000000000000..602d160131d3 --- /dev/null +++ b/src/gen-perf.h @@ -0,0 +1,50 @@ +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Provides high-level routines to manipulate the keyword list + structures the code generation output. + + Copyright (C) 1989-1998, 2000 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef gen_perf_h +#define gen_perf_h 1 + +#include "key-list.h" +#include "bool-array.h" + +class Gen_Perf : private Key_List, private Bool_Array +{ +private: + int max_hash_value; /* Maximum possible hash value. */ + int fewest_collisions; /* Records fewest # of collisions for asso value. */ + int num_done; /* Number of keywords processed without a collision. */ + + void change (List_Node *prior, List_Node *curr); + int affects_prev (char c, List_Node *curr); + static int hash (List_Node *key_node); + static int compute_disjoint_union (const char *set_1, int size_1, const char *set_2, int size_2, char *set_3); + static void sort_set (char *union_set, int len); + +public: + Gen_Perf (void); + ~Gen_Perf (void); + int operator () (void); +}; + +#endif diff --git a/src/getopt.c b/src/getopt.c new file mode 100644 index 000000000000..4eb3c2090887 --- /dev/null +++ b/src/getopt.c @@ -0,0 +1,413 @@ +/* Getopt for GNU. + Copyright (C) 1987, 1989 Free Software Foundation, Inc. + + 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of `argv' so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable _POSIX_OPTION_ORDER disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include <stdio.h> + +#ifdef sparc +#include <alloca.h> +#endif +#ifdef USG +#define bcopy(s, d, l) memcpy((d), (s), (l)) +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + UNSPECIFIED means the caller did not specify anything; + the default is then REQUIRE_ORDER if the environment variable + _OPTIONS_FIRST is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options. + Stop option processing when the first non-option is seen. + This is what Unix does. + + PERMUTE is the default. We permute the contents of `argv' as we scan, + so that eventually all the options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code zero. + Using `-' as the first character of the list of option characters + requests this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (argv) + char **argv; +{ + int nonopts_size + = (last_nonopt - first_nonopt) * sizeof (char *); + char **temp = (char **) alloca (nonopts_size); + + /* Interchange the two blocks of data in argv. */ + + bcopy (&argv[first_nonopt], temp, nonopts_size); + bcopy (&argv[last_nonopt], &argv[first_nonopt], + (optind - last_nonopt) * sizeof (char *)); + bcopy (temp, &argv[first_nonopt + optind - last_nonopt], + nonopts_size); + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of theoption characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + A colon in OPTSTRING means that the previous character is an option + that wants an argument. The argument is taken from the rest of the + current ARGV-element, or from the following ARGV-element, + and returned in `optarg'. + + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg'. + + If OPTSTRING starts with `-', it requests a different method of handling the + non-option ARGV-elements. See the comments about RETURN_IN_ORDER, above. */ + +int +getopt (argc, argv, optstring) + int argc; + char **argv; + char *optstring; +{ + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = 0; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + ordering = RETURN_IN_ORDER; + else if (getenv ("_POSIX_OPTION_ORDER") != 0) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == 0 || *nextchar == 0) + { + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange (argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' + || argv[optind][1] == 0)) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange (argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (argv[optind][0] != '-' || argv[optind][1] == 0) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 0; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = argv[optind] + 1; + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = (char *) index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == 0) + optind++; + + if (temp == 0 || c == ':') + { + if (opterr != 0) + { + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", + argv[0], c); + } + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != 0) + { + optarg = nextchar; + optind++; + } + else + optarg = 0; + nextchar = 0; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != 0) + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr != 0) + fprintf (stderr, "%s: no argument for `-%c' option\n", + argv[0], c); + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = 0; + } + } + return c; + } +} + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + char c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind; + if ((c = getopt (argc, argv, "abc:d:0123456789")) == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + return 0; +} + +#endif /* TEST */ diff --git a/src/gperf-to-do b/src/gperf-to-do new file mode 100644 index 000000000000..05caecca9dbd --- /dev/null +++ b/src/gperf-to-do @@ -0,0 +1,22 @@ +1. provide output diagnostics that explain how many input keys total, + how many after dealing with static links, and finally, after the + algorithm is complete, how many dynamic duplicates do we now + have. +2. fix up GATHER_STATISTICS for all instrumentation. +3. Useful idea: + + a. Generate the wordlist as a contiguous block of keywords, as before. + This wordlist *must* be sorted by hash value. + + b. generate the lookup_array, which are an array of signed {chars,shorts,ints}, + which ever allows full coverage of the wordlist dimensions. If the + value v, where v = lookup_array[hash(str,len)], is >= 0, then we + simply use this result as a direct access into wordlist to snag + the keyword for comparison. + + c. Otherwise, if v is < 0 this is an indication that we'll need to + search through some number of duplicates hash values. Using a + hash linking scheme we'd then index into a duplicate_address + table that would provide the starting index and total length of + the duplicate entries to consider sequentially. + diff --git a/src/hash-table.cc b/src/hash-table.cc new file mode 100644 index 000000000000..a147674b3074 --- /dev/null +++ b/src/hash-table.cc @@ -0,0 +1,95 @@ +/* Hash table for checking keyword links. Implemented using double hashing. + Copyright (C) 1989-1998, 2000 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "hash-table.h" + +#include <stdio.h> +#include <string.h> /* declares memset(), strcmp() */ +#include <hash.h> +#include "options.h" +#include "trace.h" + +/* The size of the hash table is always the smallest power of 2 >= the size + indicated by the user. This allows several optimizations, including + the use of double hashing and elimination of the mod instruction. + Note that the size had better be larger than the number of items + in the hash table, else there's trouble!!! Note that the memory + for the hash table is allocated *outside* the intialization routine. + This compromises information hiding somewhat, but greatly reduces + memory fragmentation, since we can now use alloca! */ + +Hash_Table::Hash_Table (List_Node **table_ptr, int s, int ignore_len): + table (table_ptr), size (s), collisions (0), ignore_length (ignore_len) +{ + T (Trace t ("Hash_Table::Hash_Table");) + memset ((char *) table, 0, size * sizeof (*table)); +} + +Hash_Table::~Hash_Table (void) +{ + T (Trace t ("Hash_Table::~Hash_Table");) + if (option[DEBUG]) + { + int field_width = option.get_max_keysig_size (); + + fprintf (stderr, + "\ndumping the hash table\n" + "total available table slots = %d, total bytes = %d, total collisions = %d\n" + "location, %*s, keyword\n", + size, size * (int) sizeof (*table), collisions, + field_width, "keysig"); + + for (int i = size - 1; i >= 0; i--) + if (table[i]) + fprintf (stderr, "%8d, %*.*s, %.*s\n", + i, + field_width, table[i]->char_set_length, table[i]->char_set, + table[i]->key_length, table[i]->key); + + fprintf (stderr, "\nend dumping hash table\n\n"); + } +} + +/* If the ITEM is already in the hash table return the item found + in the table. Otherwise inserts the ITEM, and returns FALSE. + Uses double hashing. */ + +List_Node * +Hash_Table::insert (List_Node *item) +{ + T (Trace t ("Hash_Table::operator()");) + unsigned hash_val = hashpjw (item->char_set, item->char_set_length); + int probe = hash_val & (size - 1); + int increment = ((hash_val ^ item->key_length) | 1) & (size - 1); + + while (table[probe]) + { + if (table[probe]->char_set_length == item->char_set_length + && memcmp (table[probe]->char_set, item->char_set, item->char_set_length) == 0 + && (ignore_length || table[probe]->key_length == item->key_length)) + return table[probe]; + + collisions++; + probe = (probe + increment) & (size - 1); + } + + table[probe] = item; + return (List_Node *) 0; +} diff --git a/src/hash-table.h b/src/hash-table.h new file mode 100644 index 000000000000..86438d00f7d6 --- /dev/null +++ b/src/hash-table.h @@ -0,0 +1,43 @@ +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Hash table used to check for duplicate keyword entries. + + Copyright (C) 1989-1998, 2000 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef hash_table_h +#define hash_table_h 1 + +#include "list-node.h" + +class Hash_Table +{ +private: + List_Node **table; /* Vector of pointers to linked lists of List_Node's. */ + int size; /* Size of the vector. */ + int collisions; /* Find out how well our double hashing is working! */ + int ignore_length; + +public: + Hash_Table (List_Node **t, int s, int ignore_len); + ~Hash_Table (void); + List_Node *insert (List_Node *item); +}; + +#endif diff --git a/src/hashtable.c b/src/hashtable.c new file mode 100644 index 000000000000..c256addd307c --- /dev/null +++ b/src/hashtable.c @@ -0,0 +1,132 @@ +/* Hash table for checking keyword links. Implemented using double hashing. + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include "hashtable.h" +#include "options.h" + +#ifdef GATHER_STATISTICS +/* Find out how well our double hashing is working! */ +static collisions = 0; +#endif + +/* Locally visible hash table. */ +static HASH_TABLE hash_table; + +/* Basically the algorithm from the Dragon book. */ + +static unsigned +hash_pjw (str) + char *str; +{ + char *temp; + unsigned g, h = 0; + + for (temp = str; *temp; temp++) + { + h = (h << 4) + (*temp * 13); + if (g = h & 0xf0000000) + { + h ^= (g >> 24); + h ^= g; + } + } + + return h; +} + +/* The size of the hash table is always the smallest power of 2 >= the size + indicated by the user. This allows several optimizations, including + the use of double hashing and elimination of the mod instruction. + Note that the size had better be larger than the number of items + in the hash table, else there's trouble!!! Note that the memory + for the hash table is allocated *outside* the intialization routine. + This compromises information hiding somewhat, but greatly reduces + memory fragmentation, since we can now use alloca! */ + +void +hash_table_init (table, s) + LIST_NODE **table; + int s; +{ + hash_table.size = s; + hash_table.table = table; + bzero ((char *) hash_table.table, hash_table.size * sizeof *hash_table.table); +} + +/* Frees the dynamically allocated table. Note that since we don't + really need this space anymore, and since it is potentially quite + big it is best to return it when we are done. */ + +void +hash_table_destroy () +{ + if (OPTION_ENABLED (option, DEBUG)) + { + int i; + + fprintf (stderr, "\ndumping the hash table\ntotal elements = %d, bytes = %d\n", + hash_table.size, hash_table.size * sizeof *hash_table.table); + + for (i = hash_table.size - 1; i >= 0; i--) + if (hash_table.table[i]) + fprintf (stderr, "location[%d] has charset \"%s\" and keyword \"%s\"\n", + i, hash_table.table[i]->char_set, hash_table.table[i]->key); + +#ifdef GATHER_STATISTICS + fprintf (stderr, "\ntotal collisions during hashing = %d\n", collisions); +#endif + fprintf (stderr, "end dumping hash table\n\n"); + } +} + +/* If the ITEM is already in the hash table return the item found + in the table. Otherwise inserts the ITEM, and returns FALSE. + Uses double hashing. */ + +LIST_NODE * +retrieve (item, ignore_length) + LIST_NODE *item; + int ignore_length; +{ + unsigned hash_val = hash_pjw (item->char_set); + int probe = hash_val & hash_table.size - 1; + int increment = (hash_val ^ item->length | 1) & hash_table.size - 1; + + while (hash_table.table[probe] + && (strcmp (hash_table.table[probe]->char_set, item->char_set) + || (!ignore_length && hash_table.table[probe]->length != item->length))) + { +#ifdef GATHER_STATISTICS + collisions++; +#endif + probe = probe + increment & hash_table.size - 1; + } + + if (hash_table.table[probe]) + return hash_table.table[probe]; + else + { + hash_table.table[probe] = item; + return 0; + } +} + + diff --git a/src/hashtable.h b/src/hashtable.h new file mode 100644 index 000000000000..218e9874a1d1 --- /dev/null +++ b/src/hashtable.h @@ -0,0 +1,37 @@ +/* Hash table used to check for duplicate keyword entries. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _hashtable_h +#define _hashtable_h +#include "keylist.h" +#include "prototype.h" + +typedef struct hash_table +{ + LIST_NODE **table; /* Vector of pointers to linked lists of List_Node's. */ + int size; /* Size of the vector. */ +} HASH_TABLE; + +extern void hash_table_init P ((LIST_NODE **table, int size)); +extern void hash_table_destroy P ((void)); +extern LIST_NODE *retrieve P ((LIST_NODE *item, int ignore_length)); + +#endif /* _hashtable_h */ diff --git a/src/iterator.c b/src/iterator.c new file mode 100644 index 000000000000..b5930f089bb2 --- /dev/null +++ b/src/iterator.c @@ -0,0 +1,106 @@ +/* Provides an Iterator for keyword characters. + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include <ctype.h> +#include "iterator.h" + +/* Locally visible ITERATOR object. */ + +ITERATOR iterator; + +/* Constructor for ITERATOR. */ + +void +iterator_init (s, lo, hi, word_end, bad_val, key_end) + char *s; + int lo; + int hi; + int word_end; + int bad_val; + int key_end; +{ + iterator.end = key_end; + iterator.error_value = bad_val; + iterator.end_word = word_end; + iterator.str = s; + iterator.hi_bound = hi; + iterator.lo_bound = lo; +} + +/* Define several useful macros to clarify subsequent code. */ +#define ISPOSDIGIT(X) ((X)<='9'&&(X)>'0') +#define TODIGIT(X) ((X)-'0') + +/* Provide an Iterator, returning the ``next'' value from + the list of valid values given in the constructor. */ + +int +next () +{ +/* Variables to record the Iterator's status when handling ranges, e.g., 3-12. */ + + static int size; + static int curr_value; + static int upper_bound; + + if (size) + { + if (++curr_value >= upper_bound) + size = 0; + return curr_value; + } + else + { + while (*iterator.str) + { + if (*iterator.str == ',') + iterator.str++; + else if (*iterator.str == '$') + { + iterator.str++; + return iterator.end_word; + } + else if (ISPOSDIGIT (*iterator.str)) + { + + for (curr_value = 0; isdigit (*iterator.str); iterator.str++) + curr_value = curr_value * 10 + *iterator.str - '0'; + + if (*iterator.str == '-') + { + + for (size = 1, upper_bound = 0; + isdigit (*++iterator.str); + upper_bound = upper_bound * 10 + *iterator.str - '0'); + + if (upper_bound <= curr_value || upper_bound > iterator.hi_bound) + return iterator.error_value; + } + return curr_value >= iterator.lo_bound && curr_value <= iterator.hi_bound + ? curr_value : iterator.error_value; + } + else + return iterator.error_value; + } + + return iterator.end; + } +} diff --git a/src/iterator.cc b/src/iterator.cc new file mode 100644 index 000000000000..ca66bbb8aca4 --- /dev/null +++ b/src/iterator.cc @@ -0,0 +1,87 @@ +/* Provides an Iterator for keyword characters. + Copyright (C) 1989-1998, 2000 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "iterator.h" + +#include <ctype.h> +#include "trace.h" + +/* Constructor for Iterator. */ + +Iterator::Iterator (const char *s, int lo, int hi, int word_end, int bad_val, int key_end) +{ + T (Trace t ("Iterator::Iterator");) + end = key_end; + error_value = bad_val; + end_word = word_end; + str = s; + hi_bound = hi; + lo_bound = lo; +} + +/* Provide an Iterator, returning the ``next'' value from + the list of valid values given in the constructor. */ + +int +Iterator::operator() (void) +{ + T (Trace t ("Iterator::operator()");) +/* Variables to record the Iterator's status when handling ranges, e.g., 3-12. */ + + static int size; + static int curr_value; + static int upper_bound; + + if (size) + { + if (++curr_value >= upper_bound) + size = 0; + return curr_value; + } + else + { + while (*str) + switch (*str) + { + default: return error_value; + case ',': str++; break; + case '$': str++; return end_word; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + for (curr_value = 0; isdigit ((unsigned char)(*str)); str++) + curr_value = curr_value * 10 + (*str - '0'); + + if (*str == '-') + { + + for (size = 1, upper_bound = 0; + isdigit ((unsigned char)(*++str)); + upper_bound = upper_bound * 10 + (*str - '0')); + + if (upper_bound <= curr_value || upper_bound > hi_bound) + return error_value; + } + return curr_value >= lo_bound && curr_value <= hi_bound + ? curr_value : error_value; + } + + return end; + } +} diff --git a/src/iterator.h b/src/iterator.h new file mode 100644 index 000000000000..d5138ab99447 --- /dev/null +++ b/src/iterator.h @@ -0,0 +1,51 @@ +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Provides an Iterator for keyword characters. + + Copyright (C) 1989-1998 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* Provides an Iterator that expands and decodes a control string containing digits + and ranges, returning an integer every time the generator function is called. + This is used to decode the user's key position requests. For example: + "-k 1,2,5-10,$" will return 1, 2, 5, 6, 7, 8, 9, 10, and 0 ( representing + the abstract ``last character of the key'' on successive calls to the + member function operator (). + No errors are handled in these routines, they are passed back to the + calling routines via a user-supplied Error_Value */ + +#ifndef iterator_h +#define iterator_h 1 + +class Iterator +{ +private: + const char *str; /* A pointer to the string provided by the user. */ + int end; /* Value returned after last key is processed. */ + int end_word; /* A value marking the abstract ``end of word'' ( usually '$'). */ + int error_value; /* Error value returned when input is syntactically erroneous. */ + int hi_bound; /* Greatest possible value, inclusive. */ + int lo_bound; /* Smallest possible value, inclusive. */ + +public: + Iterator (const char *s, int lo, int hi, int word_end, int bad_val, int key_end); + int operator () (void); +}; + +#endif diff --git a/src/key-list.cc b/src/key-list.cc new file mode 100644 index 000000000000..1c941a453579 --- /dev/null +++ b/src/key-list.cc @@ -0,0 +1,2184 @@ +/* Routines for building, ordering, and printing the keyword list. + Copyright (C) 1989-1998, 2000 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <stdio.h> +#include <string.h> /* declares strncpy(), strchr() */ +#include <stdlib.h> /* declares malloc(), free(), abs(), exit(), abort() */ +#include <ctype.h> /* declares isprint() */ +#include <assert.h> /* defines assert() */ +#include <limits.h> /* defines SCHAR_MAX etc. */ +#include "options.h" +#include "read-line.h" +#include "hash-table.h" +#include "key-list.h" +#include "trace.h" +#include "version.h" + +/* Make the hash table 8 times larger than the number of keyword entries. */ +static const int TABLE_MULTIPLE = 10; + +/* Efficiently returns the least power of two greater than or equal to X! */ +#define POW(X) ((!X)?1:(X-=1,X|=X>>1,X|=X>>2,X|=X>>4,X|=X>>8,X|=X>>16,(++X))) + +int Key_List::determined[MAX_ALPHA_SIZE]; + +/* Destructor dumps diagnostics during debugging. */ + +Key_List::~Key_List (void) +{ + T (Trace t ("Key_List::~Key_List");) + if (option[DEBUG]) + { + fprintf (stderr, "\nDumping key list information:\ntotal non-static linked keywords = %d" + "\ntotal keywords = %d\ntotal duplicates = %d\nmaximum key length = %d\n", + list_len, total_keys, total_duplicates, max_key_len); + dump (); + fprintf (stderr, "End dumping list.\n\n"); + } +} + +/* Gathers the input stream into a buffer until one of two things occur: + + 1. We read a '%' followed by a '%' + 2. We read a '%' followed by a '}' + + The first symbolizes the beginning of the keyword list proper, + The second symbolizes the end of the C source code to be generated + verbatim in the output file. + + I assume that the keys are separated from the optional preceding struct + declaration by a consecutive % followed by either % or } starting in + the first column. The code below uses an expandible buffer to scan off + and return a pointer to all the code (if any) appearing before the delimiter. */ + +const char * +Key_List::get_special_input (char delimiter) +{ + T (Trace t ("Key_List::get_special_input");) + int size = 80; + char *buf = new char[size]; + int c, i; + + for (i = 0; (c = getchar ()) != EOF; i++) + { + if (c == '%') + { + if ((c = getchar ()) == delimiter) + { + + while ((c = getchar ()) != '\n') + ; /* discard newline */ + + if (i == 0) + return ""; + else + { + buf[delimiter == '%' && buf[i - 2] == ';' ? i - 2 : i - 1] = '\0'; + return buf; + } + } + else + buf[i++] = '%'; + } + else if (i >= size) /* Yikes, time to grow the buffer! */ + { + char *temp = new char[size *= 2]; + int j; + + for (j = 0; j < i; j++) + temp[j] = buf[j]; + + buf = temp; + } + buf[i] = c; + } + + return 0; /* Problem here. */ +} + +/* Stores any C text that must be included verbatim into the + generated code output. */ + +const char * +Key_List::save_include_src (void) +{ + T (Trace t ("Key_List::save_include_src");) + int c; + + if ((c = getchar ()) != '%') + ungetc (c, stdin); + else if ((c = getchar ()) != '{') + { + fprintf (stderr, "internal error, %c != '{' on line %d in file %s", c, __LINE__, __FILE__); + exit (1); + } + else + return get_special_input ('}'); + return ""; +} + +/* Determines from the input file whether the user wants to build a table + from a user-defined struct, or whether the user is content to simply + use the default array of keys. */ + +const char * +Key_List::get_array_type (void) +{ + T (Trace t ("Key_List::get_array_type");) + return get_special_input ('%'); +} + +/* strcspn - find length of initial segment of S consisting entirely + of characters not from REJECT (borrowed from Henry Spencer's + ANSI string package, when GNU libc comes out I'll replace this...). */ + +#ifndef strcspn +inline int +Key_List::strcspn (const char *s, const char *reject) +{ + T (Trace t ("Key_List::strcspn");) + const char *scan; + const char *rej_scan; + int count = 0; + + for (scan = s; *scan; scan++) + { + + for (rej_scan = reject; *rej_scan; rej_scan++) + if (*scan == *rej_scan) + return count; + + count++; + } + + return count; +} +#endif + +/* Sets up the Return_Type, the Struct_Tag type and the Array_Type + based upon various user Options. */ + +void +Key_List::set_output_types (void) +{ + T (Trace t ("Key_List::set_output_types");) + if (option[TYPE]) + { + array_type = get_array_type (); + if (!array_type) + /* Something's wrong, but we'll catch it later on, in read_keys()... */ + return; + /* Yow, we've got a user-defined type... */ + int i = strcspn (array_type, "{\n\0"); + /* Remove trailing whitespace. */ + while (i > 0 && strchr (" \t", array_type[i-1])) + i--; + int struct_tag_length = i; + + /* Set `struct_tag' to a naked "struct something". */ + char *structtag = new char[struct_tag_length + 1]; + strncpy (structtag, array_type, struct_tag_length); + structtag[struct_tag_length] = '\0'; + struct_tag = structtag; + + /* The return type of the lookup function is "struct something *". + No "const" here, because if !option[CONST], some user code might want + to modify the structure. */ + char *rettype = new char[struct_tag_length + 3]; + strncpy (rettype, array_type, struct_tag_length); + rettype[struct_tag_length] = ' '; + rettype[struct_tag_length + 1] = '*'; + rettype[struct_tag_length + 2] = '\0'; + return_type = rettype; + } +} + +/* Extracts a key from an input line and creates a new List_Node for it. */ + +static List_Node * +parse_line (const char *line, const char *delimiters) +{ + if (*line == '"') + { + /* Parse a string in ANSI C syntax. */ + char *key = new char[strlen(line)]; + char *kp = key; + const char *lp = line + 1; + + for (; *lp;) + { + char c = *lp; + + if (c == '\0') + { + fprintf (stderr, "unterminated string: %s\n", line); + exit (1); + } + else if (c == '\\') + { + c = *++lp; + switch (c) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + { + int code = 0; + int count = 0; + while (count < 3 && *lp >= '0' && *lp <= '7') + { + code = (code << 3) + (*lp - '0'); + lp++; + count++; + } + if (code > UCHAR_MAX) + fprintf (stderr, "octal escape out of range: %s\n", line); + *kp = (char) code; + break; + } + case 'x': + { + int code = 0; + int count = 0; + lp++; + while ((*lp >= '0' && *lp <= '9') + || (*lp >= 'A' && *lp <= 'F') + || (*lp >= 'a' && *lp <= 'f')) + { + code = (code << 4) + + (*lp >= 'A' && *lp <= 'F' ? *lp - 'A' + 10 : + *lp >= 'a' && *lp <= 'f' ? *lp - 'a' + 10 : + *lp - '0'); + lp++; + count++; + } + if (count == 0) + fprintf (stderr, "hexadecimal escape without any hex digits: %s\n", line); + if (code > UCHAR_MAX) + fprintf (stderr, "hexadecimal escape out of range: %s\n", line); + *kp = (char) code; + break; + } + case '\\': case '\'': case '"': + *kp = c; + lp++; + break; + case 'n': + *kp = '\n'; + lp++; + break; + case 't': + *kp = '\t'; + lp++; + break; + case 'r': + *kp = '\r'; + lp++; + break; + case 'f': + *kp = '\f'; + lp++; + break; + case 'b': + *kp = '\b'; + lp++; + break; + case 'a': + *kp = '\a'; + lp++; + break; + case 'v': + *kp = '\v'; + lp++; + break; + default: + fprintf (stderr, "invalid escape sequence in string: %s\n", line); + exit (1); + } + } + else if (c == '"') + break; + else + { + *kp = c; + lp++; + } + kp++; + } + lp++; + if (*lp != '\0') + { + if (strchr (delimiters, *lp) == NULL) + { + fprintf (stderr, "string not followed by delimiter: %s\n", line); + exit (1); + } + lp++; + } + return new List_Node (key, kp - key, option[TYPE] ? lp : ""); + } + else + { + /* Not a string. Look for the delimiter. */ + int len = strcspn (line, delimiters); + const char *rest; + + if (line[len] == '\0') + rest = ""; + else + /* Skip the first delimiter. */ + rest = &line[len + 1]; + return new List_Node (line, len, option[TYPE] ? rest : ""); + } +} + +/* Reads in all keys from standard input and creates a linked list pointed + to by Head. This list is then quickly checked for ``links,'' i.e., + unhashable elements possessing identical key sets and lengths. */ + +void +Key_List::read_keys (void) +{ + T (Trace t ("Key_List::read_keys");) + char *ptr; + + include_src = save_include_src (); + set_output_types (); + + /* Oops, problem with the input file. */ + if (! (ptr = Read_Line::get_line ())) + { + fprintf (stderr, "No words in input file, did you forget to prepend %s or use -t accidentally?\n", "%%"); + exit (1); + } + + /* Read in all the keywords from the input file. */ + else + { + const char *delimiter = option.get_delimiter (); + List_Node *temp, *trail = 0; + + head = parse_line (ptr, delimiter); + + for (temp = head; + (ptr = Read_Line::get_line ()) && strcmp (ptr, "%%"); + temp = temp->next) + { + temp->next = parse_line (ptr, delimiter); + total_keys++; + } + + /* See if any additional C code is included at end of this file. */ + if (ptr) + additional_code = 1; + + /* Hash table this number of times larger than keyword number. */ + int table_size = (list_len = total_keys) * TABLE_MULTIPLE; + +#if LARGE_STACK_ARRAYS + /* By allocating the memory here we save on dynamic allocation overhead. + Table must be a power of 2 for the hash function scheme to work. */ + List_Node *table[POW (table_size)]; +#else + // Note: we don't use new, because that invokes a custom operator new. + int malloc_size = POW (table_size) * sizeof(List_Node*); + if (malloc_size == 0) malloc_size = 1; + List_Node **table = (List_Node**)malloc(malloc_size); + if (table == NULL) + abort (); +#endif + + /* Make large hash table for efficiency. */ + Hash_Table found_link (table, table_size, option[NOLENGTH]); + + /* Test whether there are any links and also set the maximum length of + an identifier in the keyword list. */ + + for (temp = head; temp; temp = temp->next) + { + List_Node *ptr = found_link.insert (temp); + + /* Check for links. We deal with these by building an equivalence class + of all duplicate values (i.e., links) so that only 1 keyword is + representative of the entire collection. This *greatly* simplifies + processing during later stages of the program. */ + + if (ptr) + { + total_duplicates++; + list_len--; + trail->next = temp->next; + temp->link = ptr->link; + ptr->link = temp; + + /* Complain if user hasn't enabled the duplicate option. */ + if (!option[DUP] || option[DEBUG]) + fprintf (stderr, "Key link: \"%.*s\" = \"%.*s\", with key set \"%.*s\".\n", + temp->key_length, temp->key, + ptr->key_length, ptr->key, + temp->char_set_length, temp->char_set); + } + else + trail = temp; + + /* Update minimum and maximum keyword length, if needed. */ + if (max_key_len < temp->key_length) + max_key_len = temp->key_length; + if (min_key_len > temp->key_length) + min_key_len = temp->key_length; + } + +#if !LARGE_STACK_ARRAYS + free ((char *) table); +#endif + + /* Exit program if links exists and option[DUP] not set, since we can't continue */ + if (total_duplicates) + { + if (option[DUP]) + fprintf (stderr, "%d input keys have identical hash values, examine output carefully...\n", + total_duplicates); + else + { + fprintf (stderr, "%d input keys have identical hash values,\ntry different key positions or use option -D.\n", + total_duplicates); + exit (1); + } + } + /* Exit program if an empty string is used as key, since the comparison + expressions don't work correctly for looking up an empty string. */ + if (min_key_len == 0) + { + fprintf (stderr, "Empty input key is not allowed.\nTo recognize an empty input key, your code should check for\nlen == 0 before calling the gperf generated lookup function.\n"); + exit (1); + } + if (option[ALLCHARS]) + option.set_keysig_size (max_key_len); + } +} + +/* Recursively merges two sorted lists together to form one sorted list. The + ordering criteria is by frequency of occurrence of elements in the key set + or by the hash value. This is a kludge, but permits nice sharing of + almost identical code without incurring the overhead of a function + call comparison. */ + +List_Node * +Key_List::merge (List_Node *list1, List_Node *list2) +{ + T (Trace t ("Key_List::merge");) + List_Node *result; + List_Node **resultp = &result; + for (;;) + { + if (!list1) + { + *resultp = list2; + break; + } + if (!list2) + { + *resultp = list1; + break; + } + if (occurrence_sort && list1->occurrence < list2->occurrence + || hash_sort && list1->hash_value > list2->hash_value) + { + *resultp = list2; + resultp = &list2->next; list2 = list1; list1 = *resultp; + } + else + { + *resultp = list1; + resultp = &list1->next; list1 = *resultp; + } + } + return result; +} + +/* Applies the merge sort algorithm to recursively sort the key list by + frequency of occurrence of elements in the key set. */ + +List_Node * +Key_List::merge_sort (List_Node *head) +{ + T (Trace t ("Key_List::merge_sort");) + if (!head || !head->next) + return head; + else + { + List_Node *middle = head; + List_Node *temp = head->next->next; + + while (temp) + { + temp = temp->next; + middle = middle->next; + if (temp) + temp = temp->next; + } + + temp = middle->next; + middle->next = 0; + return merge (merge_sort (head), merge_sort (temp)); + } +} + +/* Returns the frequency of occurrence of elements in the key set. */ + +inline int +Key_List::get_occurrence (List_Node *ptr) +{ + T (Trace t ("Key_List::get_occurrence");) + int value = 0; + + const char *p = ptr->char_set; + unsigned int i = ptr->char_set_length; + for (; i > 0; p++, i--) + value += occurrences[(unsigned char)(*p)]; + + return value; +} + +/* Enables the index location of all key set elements that are now + determined. */ + +inline void +Key_List::set_determined (List_Node *ptr) +{ + T (Trace t ("Key_List::set_determined");) + + const char *p = ptr->char_set; + unsigned int i = ptr->char_set_length; + for (; i > 0; p++, i--) + determined[(unsigned char)(*p)] = 1; +} + +/* Returns TRUE if PTR's key set is already completely determined. */ + +inline int +Key_List::already_determined (List_Node *ptr) +{ + T (Trace t ("Key_List::already_determined");) + int is_determined = 1; + + const char *p = ptr->char_set; + unsigned int i = ptr->char_set_length; + for (; is_determined && i > 0; p++, i--) + is_determined = determined[(unsigned char)(*p)]; + + return is_determined; +} + +/* Reorders the table by first sorting the list so that frequently occuring + keys appear first, and then the list is reorded so that keys whose values + are already determined will be placed towards the front of the list. This + helps prune the search time by handling inevitable collisions early in the + search process. See Cichelli's paper from Jan 1980 JACM for details.... */ + +void +Key_List::reorder (void) +{ + T (Trace t ("Key_List::reorder");) + List_Node *ptr; + for (ptr = head; ptr; ptr = ptr->next) + ptr->occurrence = get_occurrence (ptr); + + hash_sort = 0; + occurrence_sort = 1; + + for (ptr = head = merge_sort (head); ptr->next; ptr = ptr->next) + { + set_determined (ptr); + + if (already_determined (ptr->next)) + continue; + else + { + List_Node *trail_ptr = ptr->next; + List_Node *run_ptr = trail_ptr->next; + + for (; run_ptr; run_ptr = trail_ptr->next) + { + + if (already_determined (run_ptr)) + { + trail_ptr->next = run_ptr->next; + run_ptr->next = ptr->next; + ptr = ptr->next = run_ptr; + } + else + trail_ptr = run_ptr; + } + } + } +} + +/* ============================ Output routines ============================ */ + +/* The "const " qualifier. */ +static const char *const_always; + +/* The "const " qualifier, for read-only arrays. */ +static const char *const_readonly_array; + +/* The "const " qualifier, for the array type. */ +static const char *const_for_struct; + +/* Returns the smallest unsigned C type capable of holding integers up to N. */ + +static const char * +smallest_integral_type (int n) +{ + if (n <= UCHAR_MAX) return "unsigned char"; + if (n <= USHRT_MAX) return "unsigned short"; + return "unsigned int"; +} + +/* Returns the smallest signed C type capable of holding integers + from MIN to MAX. */ + +static const char * +smallest_integral_type (int min, int max) +{ + if (option[ANSIC] | option[CPLUSPLUS]) + if (min >= SCHAR_MIN && max <= SCHAR_MAX) return "signed char"; + if (min >= SHRT_MIN && max <= SHRT_MAX) return "short"; + return "int"; +} + +/* A cast from `char' to a valid array index. */ +static const char *char_to_index; + +/* ------------------------------------------------------------------------- */ + +/* Computes the maximum and minimum hash values. Since the + list is already sorted by hash value all we need to do is + find the final item! */ + +void +Key_List::compute_min_max (void) +{ + T (Trace t ("Key_List::compute_min_max");) + List_Node *temp; + for (temp = head; temp->next; temp = temp->next) + ; + + min_hash_value = head->hash_value; + max_hash_value = temp->hash_value; +} + +/* ------------------------------------------------------------------------- */ + +/* Returns the number of different hash values. */ + +int +Key_List::num_hash_values (void) +{ + T (Trace t ("Key_List::num_hash_values");) + int count = 1; + List_Node *temp; + int value; + + for (temp = head, value = temp->hash_value; temp->next; ) + { + temp = temp->next; + if (value != temp->hash_value) + { + value = temp->hash_value; + count++; + } + } + return count; +} + +/* -------------------- Output_Constants and subclasses -------------------- */ + +/* This class outputs an enumeration defining some constants. */ + +struct Output_Constants +{ + virtual void output_start () = 0; + virtual void output_item (const char *name, int value) = 0; + virtual void output_end () = 0; + Output_Constants () {} + virtual ~Output_Constants () {} +}; + +/* This class outputs an enumeration in #define syntax. */ + +struct Output_Defines : public Output_Constants +{ + virtual void output_start (); + virtual void output_item (const char *name, int value); + virtual void output_end (); + Output_Defines () {} + virtual ~Output_Defines () {} +}; + +void Output_Defines::output_start () +{ + T (Trace t ("Output_Defines::output_start");) + printf ("\n"); +} + +void Output_Defines::output_item (const char *name, int value) +{ + T (Trace t ("Output_Defines::output_item");) + printf ("#define %s %d\n", name, value); +} + +void Output_Defines::output_end () +{ + T (Trace t ("Output_Defines::output_end");) +} + +/* This class outputs an enumeration using `enum'. */ + +struct Output_Enum : public Output_Constants +{ + virtual void output_start (); + virtual void output_item (const char *name, int value); + virtual void output_end (); + Output_Enum (const char *indent) : indentation (indent) {} + virtual ~Output_Enum () {} +private: + const char *indentation; + int pending_comma; +}; + +void Output_Enum::output_start () +{ + T (Trace t ("Output_Enum::output_start");) + printf ("%senum\n" + "%s {\n", + indentation, indentation); + pending_comma = 0; +} + +void Output_Enum::output_item (const char *name, int value) +{ + T (Trace t ("Output_Enum::output_item");) + if (pending_comma) + printf (",\n"); + printf ("%s %s = %d", indentation, name, value); + pending_comma = 1; +} + +void Output_Enum::output_end () +{ + T (Trace t ("Output_Enum::output_end");) + if (pending_comma) + printf ("\n"); + printf ("%s };\n\n", indentation); +} + +/* Outputs the maximum and minimum hash values etc. */ + +void +Key_List::output_constants (struct Output_Constants& style) +{ + T (Trace t ("Key_List::output_constants");) + + style.output_start (); + style.output_item ("TOTAL_KEYWORDS", total_keys); + style.output_item ("MIN_WORD_LENGTH", min_key_len); + style.output_item ("MAX_WORD_LENGTH", max_key_len); + style.output_item ("MIN_HASH_VALUE", min_hash_value); + style.output_item ("MAX_HASH_VALUE", max_hash_value); + style.output_end (); +} + +/* ------------------------------------------------------------------------- */ + +/* Outputs a keyword, as a string: enclosed in double quotes, escaping + backslashes, double quote and unprintable characters. */ + +static void +output_string (const char *key, int len) +{ + T (Trace t ("output_string");) + + putchar ('"'); + for (; len > 0; len--) + { + unsigned char c = (unsigned char) *key++; + if (isprint (c)) + { + if (c == '"' || c == '\\') + putchar ('\\'); + putchar (c); + } + else + { + /* Use octal escapes, not hexadecimal escapes, because some old + C compilers didn't understand hexadecimal escapes, and because + hexadecimal escapes are not limited to 2 digits, thus needing + special care if the following character happens to be a digit. */ + putchar ('\\'); + putchar ('0' + ((c >> 6) & 7)); + putchar ('0' + ((c >> 3) & 7)); + putchar ('0' + (c & 7)); + } + } + putchar ('"'); +} + +/* ------------------------------------------------------------------------- */ + +/* Outputs a type and a const specifier. + The output is terminated with a space. */ + +static void +output_const_type (const char *const_string, const char *type_string) +{ + if (type_string[strlen(type_string)-1] == '*') + printf ("%s %s", type_string, const_string); + else + printf ("%s%s ", const_string, type_string); +} + +/* ----------------------- Output_Expr and subclasses ----------------------- */ + +/* This class outputs a general expression. */ + +struct Output_Expr +{ + virtual void output_expr () const = 0; + Output_Expr () {} + virtual ~Output_Expr () {} +}; + +/* This class outputs an expression formed by a single string. */ + +struct Output_Expr1 : public Output_Expr +{ + virtual void output_expr () const; + Output_Expr1 (const char *piece1) : p1 (piece1) {} + virtual ~Output_Expr1 () {} +private: + const char *p1; +}; + +void Output_Expr1::output_expr () const +{ + T (Trace t ("Output_Expr1::output_expr");) + printf ("%s", p1); +} + +#if 0 /* unused */ + +/* This class outputs an expression formed by the concatenation of two + strings. */ + +struct Output_Expr2 : public Output_Expr +{ + virtual void output_expr () const; + Output_Expr2 (const char *piece1, const char *piece2) + : p1 (piece1), p2 (piece2) {} + virtual ~Output_Expr2 () {} +private: + const char *p1; + const char *p2; +}; + +void Output_Expr2::output_expr () const +{ + T (Trace t ("Output_Expr2::output_expr");) + printf ("%s%s", p1, p2); +} + +#endif + +/* --------------------- Output_Compare and subclasses --------------------- */ + +/* This class outputs a comparison expression. */ + +struct Output_Compare +{ + virtual void output_comparison (const Output_Expr& expr1, + const Output_Expr& expr2) const = 0; + Output_Compare () {} + virtual ~Output_Compare () {} +}; + +/* This class outputs a comparison using strcmp. */ + +struct Output_Compare_Strcmp : public Output_Compare +{ + virtual void output_comparison (const Output_Expr& expr1, + const Output_Expr& expr2) const; + Output_Compare_Strcmp () {} + virtual ~Output_Compare_Strcmp () {} +}; + +void Output_Compare_Strcmp::output_comparison (const Output_Expr& expr1, + const Output_Expr& expr2) const +{ + T (Trace t ("Output_Compare_Strcmp::output_comparison");) + printf ("*"); + expr1.output_expr (); + printf (" == *"); + expr2.output_expr (); + printf (" && !strcmp ("); + expr1.output_expr (); + printf (" + 1, "); + expr2.output_expr (); + printf (" + 1)"); +} + +/* This class outputs a comparison using strncmp. + Note that the length of expr1 will be available through the local variable + `len'. */ + +struct Output_Compare_Strncmp : public Output_Compare +{ + virtual void output_comparison (const Output_Expr& expr1, + const Output_Expr& expr2) const; + Output_Compare_Strncmp () {} + virtual ~Output_Compare_Strncmp () {} +}; + +void Output_Compare_Strncmp::output_comparison (const Output_Expr& expr1, + const Output_Expr& expr2) const +{ + T (Trace t ("Output_Compare_Strncmp::output_comparison");) + printf ("*"); + expr1.output_expr (); + printf (" == *"); + expr2.output_expr (); + printf (" && !strncmp ("); + expr1.output_expr (); + printf (" + 1, "); + expr2.output_expr (); + printf (" + 1, len - 1) && "); + expr2.output_expr (); + printf ("[len] == '\\0'"); +} + +/* This class outputs a comparison using memcmp. + Note that the length of expr1 (available through the local variable `len') + must be verified to be equal to the length of expr2 prior to this + comparison. */ + +struct Output_Compare_Memcmp : public Output_Compare +{ + virtual void output_comparison (const Output_Expr& expr1, + const Output_Expr& expr2) const; + Output_Compare_Memcmp () {} + virtual ~Output_Compare_Memcmp () {} +}; + +void Output_Compare_Memcmp::output_comparison (const Output_Expr& expr1, + const Output_Expr& expr2) const +{ + T (Trace t ("Output_Compare_Memcmp::output_comparison");) + printf ("*"); + expr1.output_expr (); + printf (" == *"); + expr2.output_expr (); + printf (" && !memcmp ("); + expr1.output_expr (); + printf (" + 1, "); + expr2.output_expr (); + printf (" + 1, len - 1)"); +} + +/* ------------------------------------------------------------------------- */ + +/* Generates C code for the hash function that returns the + proper encoding for each key word. */ + +void +Key_List::output_hash_function (void) +{ + T (Trace t ("Key_List::output_hash_function");) + const int max_column = 10; + int field_width; + + /* Calculate maximum number of digits required for MAX_HASH_VALUE. */ + field_width = 2; + for (int trunc = max_hash_value; (trunc /= 10) > 0;) + field_width++; + + /* Output the function's head. */ + if (option[CPLUSPLUS]) + printf ("inline "); + else if (option[KRC] | option[C] | option[ANSIC]) + printf ("#ifdef __GNUC__\n" + "__inline\n" + "#else\n" + "#ifdef __cplusplus\n" + "inline\n" + "#endif\n" + "#endif\n"); + + if (option[KRC] | option[C] | option[ANSIC]) + printf ("static "); + printf ("unsigned int\n"); + if (option[CPLUSPLUS]) + printf ("%s::", option.get_class_name ()); + printf ("%s ", option.get_hash_name ()); + printf (option[KRC] ? + "(str, len)\n" + " register char *str;\n" + " register unsigned int len;\n" : + option[C] ? + "(str, len)\n" + " register const char *str;\n" + " register unsigned int len;\n" : + option[ANSIC] | option[CPLUSPLUS] ? + "(register const char *str, register unsigned int len)\n" : + ""); + + /* Note that when the hash function is called, it has already been verified + that min_key_len <= len <= max_key_len. */ + + /* Output the function's body. */ + printf ("{\n"); + + /* First the asso_values array. */ + printf (" static %s%s asso_values[] =\n" + " {", + const_readonly_array, + smallest_integral_type (max_hash_value + 1)); + + for (int count = 0; count < ALPHA_SIZE; count++) + { + if (count > 0) + printf (","); + if (!(count % max_column)) + printf ("\n "); + printf ("%*d", field_width, + occurrences[count] ? asso_values[count] : max_hash_value + 1); + } + + printf ("\n" + " };\n"); + + /* Optimize special case of ``-k 1,$'' */ + if (option[DEFAULTCHARS]) + printf (" return %sasso_values[%sstr[len - 1]] + asso_values[%sstr[0]];\n", + option[NOLENGTH] ? "" : "len + ", + char_to_index, char_to_index); + else + { + int key_pos; + + option.reset (); + + /* Get first (also highest) key position. */ + key_pos = option.get (); + + if (!option[ALLCHARS] && (key_pos == WORD_END || key_pos <= min_key_len)) + { + /* We can perform additional optimizations here: + Write it out as a single expression. Note that the values + are added as `int's even though the asso_values array may + contain `unsigned char's or `unsigned short's. */ + + printf (" return %s", + option[NOLENGTH] ? "" : "len + "); + + for (; key_pos != WORD_END; ) + { + printf ("asso_values[%sstr[%d]]", char_to_index, key_pos - 1); + if ((key_pos = option.get ()) != EOS) + printf (" + "); + else + break; + } + + if (key_pos == WORD_END) + printf ("asso_values[%sstr[len - 1]]", char_to_index); + + printf (";\n"); + } + else + { + /* We've got to use the correct, but brute force, technique. */ + printf (" register int hval = %s;\n\n" + " switch (%s)\n" + " {\n" + " default:\n", + option[NOLENGTH] ? "0" : "len", + option[NOLENGTH] ? "len" : "hval"); + + /* User wants *all* characters considered in hash. */ + if (option[ALLCHARS]) + { + for (int i = max_key_len; i > 0; i--) + printf (" case %d:\n" + " hval += asso_values[%sstr[%d]];\n", + i, char_to_index, i - 1); + + printf (" break;\n" + " }\n" + " return hval;\n"); + } + else /* do the hard part... */ + { + while (key_pos != WORD_END && key_pos > max_key_len) + if ((key_pos = option.get ()) == EOS) + break; + + if (key_pos != EOS && key_pos != WORD_END) + { + int i = key_pos; + do + { + for ( ; i >= key_pos; i--) + printf (" case %d:\n", i); + + printf (" hval += asso_values[%sstr[%d]];\n", + char_to_index, key_pos - 1); + + key_pos = option.get (); + } + while (key_pos != EOS && key_pos != WORD_END); + + for ( ; i >= min_key_len; i--) + printf (" case %d:\n", i); + } + + printf (" break;\n" + " }\n" + " return hval"); + if (key_pos == WORD_END) + printf (" + asso_values[%sstr[len - 1]]", char_to_index); + printf (";\n"); + } + } + } + printf ("}\n\n"); +} + +/* ------------------------------------------------------------------------- */ + +/* Prints out a table of keyword lengths, for use with the + comparison code in generated function ``in_word_set''. */ + +void +Key_List::output_keylength_table (void) +{ + T (Trace t ("Key_List::output_keylength_table");) + const int columns = 14; + int index; + int column; + const char *indent = option[GLOBAL] ? "" : " "; + List_Node *temp; + + printf ("%sstatic %s%s lengthtable[] =\n%s {", + indent, const_readonly_array, + smallest_integral_type (max_key_len), + indent); + + /* Generate an array of lengths, similar to output_keyword_table. */ + + column = 0; + for (temp = head, index = 0; temp; temp = temp->next) + { + if (option[SWITCH] && !option[TYPE] + && !(temp->link + || (temp->next && temp->hash_value == temp->next->hash_value))) + continue; + + if (index < temp->hash_value && !option[SWITCH] && !option[DUP]) + { + /* Some blank entries. */ + for ( ; index < temp->hash_value; index++) + { + if (index > 0) + printf (","); + if ((column++ % columns) == 0) + printf ("\n%s ", indent); + printf ("%3d", 0); + } + } + + if (index > 0) + printf (","); + if ((column++ % columns) == 0) + printf("\n%s ", indent); + printf ("%3d", temp->key_length); + + /* Deal with links specially. */ + if (temp->link) // implies option[DUP] + for (List_Node *links = temp->link; links; links = links->link) + { + ++index; + printf (","); + if ((column++ % columns) == 0) + printf("\n%s ", indent); + printf ("%3d", links->key_length); + } + + index++; + } + + printf ("\n%s };\n", indent); + if (option[GLOBAL]) + printf ("\n"); +} + +/* ------------------------------------------------------------------------- */ + +static void +output_keyword_entry (List_Node *temp, const char *indent) +{ + printf ("%s ", indent); + if (option[TYPE]) + printf ("{"); + output_string (temp->key, temp->key_length); + if (option[TYPE]) + { + if (strlen (temp->rest) > 0) + printf (",%s", temp->rest); + printf ("}"); + } + if (option[DEBUG]) + printf (" /* hash value = %d, index = %d */", + temp->hash_value, temp->index); +} + +static void +output_keyword_blank_entries (int count, const char *indent) +{ + int columns; + if (option[TYPE]) + { + columns = 58 / (6 + strlen (option.get_initializer_suffix())); + if (columns == 0) + columns = 1; + } + else + { + columns = 9; + } + int column = 0; + for (int i = 0; i < count; i++) + { + if ((column % columns) == 0) + { + if (i > 0) + printf (",\n"); + printf ("%s ", indent); + } + else + { + if (i > 0) + printf (", "); + } + if (option[TYPE]) + printf ("{\"\"%s}", option.get_initializer_suffix()); + else + printf ("\"\""); + column++; + } +} + +/* Prints out the array containing the key words for the hash function. */ + +void +Key_List::output_keyword_table (void) +{ + T (Trace t ("Key_List::output_keyword_table");) + const char *indent = option[GLOBAL] ? "" : " "; + int index; + List_Node *temp; + + printf ("%sstatic ", + indent); + output_const_type (const_readonly_array, struct_tag); + printf ("%s[] =\n" + "%s {\n", + option.get_wordlist_name (), + indent); + + /* Generate an array of reserved words at appropriate locations. */ + + for (temp = head, index = 0; temp; temp = temp->next) + { + if (option[SWITCH] && !option[TYPE] + && !(temp->link + || (temp->next && temp->hash_value == temp->next->hash_value))) + continue; + + if (index > 0) + printf (",\n"); + + if (index < temp->hash_value && !option[SWITCH] && !option[DUP]) + { + /* Some blank entries. */ + output_keyword_blank_entries (temp->hash_value - index, indent); + printf (",\n"); + index = temp->hash_value; + } + + temp->index = index; + + output_keyword_entry (temp, indent); + + /* Deal with links specially. */ + if (temp->link) // implies option[DUP] + for (List_Node *links = temp->link; links; links = links->link) + { + links->index = ++index; + printf (",\n"); + output_keyword_entry (links, indent); + } + + index++; + } + if (index > 0) + printf ("\n"); + + printf ("%s };\n\n", indent); +} + +/* ------------------------------------------------------------------------- */ + +/* Generates the large, sparse table that maps hash values into + the smaller, contiguous range of the keyword table. */ + +void +Key_List::output_lookup_array (void) +{ + T (Trace t ("Key_List::output_lookup_array");) + if (option[DUP]) + { + const int DEFAULT_VALUE = -1; + + /* Because of the way output_keyword_table works, every duplicate set is + stored contiguously in the wordlist array. */ + struct duplicate_entry + { + int hash_value; /* Hash value for this particular duplicate set. */ + int index; /* Index into the main keyword storage array. */ + int count; /* Number of consecutive duplicates at this index. */ + }; + +#if LARGE_STACK_ARRAYS + duplicate_entry duplicates[total_duplicates]; + int lookup_array[max_hash_value + 1 + 2*total_duplicates]; +#else + // Note: we don't use new, because that invokes a custom operator new. + duplicate_entry *duplicates = (duplicate_entry *) + malloc (total_duplicates * sizeof(duplicate_entry) + 1); + int *lookup_array = (int *) + malloc ((max_hash_value + 1 + 2*total_duplicates) * sizeof(int)); + if (duplicates == NULL || lookup_array == NULL) + abort(); +#endif + int lookup_array_size = max_hash_value + 1; + duplicate_entry *dup_ptr = &duplicates[0]; + int *lookup_ptr = &lookup_array[max_hash_value + 1 + 2*total_duplicates]; + + while (lookup_ptr > lookup_array) + *--lookup_ptr = DEFAULT_VALUE; + + /* Now dup_ptr = &duplicates[0] and lookup_ptr = &lookup_array[0]. */ + + for (List_Node *temp = head; temp; temp = temp->next) + { + int hash_value = temp->hash_value; + lookup_array[hash_value] = temp->index; + if (option[DEBUG]) + fprintf (stderr, "keyword = %.*s, index = %d\n", + temp->key_length, temp->key, temp->index); + if (temp->link + || (temp->next && hash_value == temp->next->hash_value)) + { + /* Start a duplicate entry. */ + dup_ptr->hash_value = hash_value; + dup_ptr->index = temp->index; + dup_ptr->count = 1; + + for (;;) + { + for (List_Node *ptr = temp->link; ptr; ptr = ptr->link) + { + dup_ptr->count++; + if (option[DEBUG]) + fprintf (stderr, + "static linked keyword = %.*s, index = %d\n", + ptr->key_length, ptr->key, ptr->index); + } + + if (!(temp->next && hash_value == temp->next->hash_value)) + break; + + temp = temp->next; + + dup_ptr->count++; + if (option[DEBUG]) + fprintf (stderr, "dynamic linked keyword = %.*s, index = %d\n", + temp->key_length, temp->key, temp->index); + } + assert (dup_ptr->count >= 2); + dup_ptr++; + } + } + + while (dup_ptr > duplicates) + { + dup_ptr--; + + if (option[DEBUG]) + fprintf (stderr, + "dup_ptr[%d]: hash_value = %d, index = %d, count = %d\n", + dup_ptr - duplicates, + dup_ptr->hash_value, dup_ptr->index, dup_ptr->count); + + int i; + /* Start searching for available space towards the right part + of the lookup array. */ + for (i = dup_ptr->hash_value; i < lookup_array_size-1; i++) + if (lookup_array[i] == DEFAULT_VALUE + && lookup_array[i + 1] == DEFAULT_VALUE) + goto found_i; + /* If we didn't find it to the right look to the left instead... */ + for (i = dup_ptr->hash_value-1; i >= 0; i--) + if (lookup_array[i] == DEFAULT_VALUE + && lookup_array[i + 1] == DEFAULT_VALUE) + goto found_i; + /* Append to the end of lookup_array. */ + i = lookup_array_size; + lookup_array_size += 2; + found_i: + /* Put in an indirection from dup_ptr->hash_value to i. + At i and i+1 store dup_ptr->index and dup_ptr->count. */ + assert (lookup_array[dup_ptr->hash_value] == dup_ptr->index); + lookup_array[dup_ptr->hash_value] = - 1 - total_keys - i; + lookup_array[i] = - total_keys + dup_ptr->index; + lookup_array[i + 1] = - dup_ptr->count; + /* All these three values are <= -2, distinct from DEFAULT_VALUE. */ + } + + /* The values of the lookup array are now known. */ + + int min = INT_MAX; + int max = INT_MIN; + lookup_ptr = lookup_array + lookup_array_size; + while (lookup_ptr > lookup_array) + { + int val = *--lookup_ptr; + if (min > val) + min = val; + if (max < val) + max = val; + } + + const char *indent = option[GLOBAL] ? "" : " "; + printf ("%sstatic %s%s lookup[] =\n" + "%s {", + indent, const_readonly_array, smallest_integral_type (min, max), + indent); + + int field_width; + /* Calculate maximum number of digits required for MIN..MAX. */ + { + field_width = 2; + for (int trunc = max; (trunc /= 10) > 0;) + field_width++; + } + if (min < 0) + { + int neg_field_width = 2; + for (int trunc = -min; (trunc /= 10) > 0;) + neg_field_width++; + neg_field_width++; /* account for the minus sign */ + if (field_width < neg_field_width) + field_width = neg_field_width; + } + + const int columns = 42 / field_width; + int column; + + column = 0; + for (int i = 0; i < lookup_array_size; i++) + { + if (i > 0) + printf (","); + if ((column++ % columns) == 0) + printf("\n%s ", indent); + printf ("%*d", field_width, lookup_array[i]); + } + printf ("\n%s };\n\n", indent); + +#if !LARGE_STACK_ARRAYS + free ((char *) duplicates); + free ((char *) lookup_array); +#endif + } +} + +/* ------------------------------------------------------------------------- */ + +/* Generate all the tables needed for the lookup function. */ + +void +Key_List::output_lookup_tables (void) +{ + T (Trace t ("Key_List::output_lookup_tables");) + + if (option[SWITCH]) + { + /* Use the switch in place of lookup table. */ + if (option[LENTABLE] && (option[DUP] && total_duplicates > 0)) + output_keylength_table (); + if (option[TYPE] || (option[DUP] && total_duplicates > 0)) + output_keyword_table (); + } + else + { + /* Use the lookup table, in place of switch. */ + if (option[LENTABLE]) + output_keylength_table (); + output_keyword_table (); + output_lookup_array (); + } +} + +/* ------------------------------------------------------------------------- */ + +/* Output a single switch case (including duplicates). Advance list. */ + +static List_Node * +output_switch_case (List_Node *list, int indent, int *jumps_away) +{ + T (Trace t ("output_switch_case");) + + if (option[DEBUG]) + printf ("%*s/* hash value = %4d, keyword = \"%.*s\" */\n", + indent, "", list->hash_value, list->key_length, list->key); + + if (option[DUP] + && (list->link + || (list->next && list->hash_value == list->next->hash_value))) + { + if (option[LENTABLE]) + printf ("%*slengthptr = &lengthtable[%d];\n", + indent, "", list->index); + printf ("%*swordptr = &%s[%d];\n", + indent, "", option.get_wordlist_name (), list->index); + + int count = 0; + for (List_Node *temp = list; ; temp = temp->next) + { + for (List_Node *links = temp; links; links = links->link) + count++; + if (!(temp->next && temp->hash_value == temp->next->hash_value)) + break; + } + + printf ("%*swordendptr = wordptr + %d;\n" + "%*sgoto multicompare;\n", + indent, "", count, + indent, ""); + *jumps_away = 1; + } + else + { + if (option[LENTABLE]) + { + printf ("%*sif (len == %d)\n" + "%*s {\n", + indent, "", list->key_length, + indent, ""); + indent += 4; + } + printf ("%*sresword = ", + indent, ""); + if (option[TYPE]) + printf ("&%s[%d]", option.get_wordlist_name (), list->index); + else + output_string (list->key, list->key_length); + printf (";\n"); + printf ("%*sgoto compare;\n", + indent, ""); + if (option[LENTABLE]) + { + indent -= 4; + printf ("%*s }\n", + indent, ""); + } + else + *jumps_away = 1; + } + + while (list->next && list->hash_value == list->next->hash_value) + list = list->next; + list = list->next; + return list; +} + +/* Output a total of size cases, grouped into num_switches switch statements, + where 0 < num_switches <= size. */ + +static void +output_switches (List_Node *list, int num_switches, int size, int min_hash_value, int max_hash_value, int indent) +{ + T (Trace t ("output_switches");) + + if (option[DEBUG]) + printf ("%*s/* know %d <= key <= %d, contains %d cases */\n", + indent, "", min_hash_value, max_hash_value, size); + + if (num_switches > 1) + { + int part1 = num_switches / 2; + int part2 = num_switches - part1; + int size1 = (int)((double)size / (double)num_switches * (double)part1 + 0.5); + int size2 = size - size1; + + List_Node *temp = list; + for (int count = size1; count > 0; count--) + { + while (temp->hash_value == temp->next->hash_value) + temp = temp->next; + temp = temp->next; + } + + printf ("%*sif (key < %d)\n" + "%*s {\n", + indent, "", temp->hash_value, + indent, ""); + + output_switches (list, part1, size1, min_hash_value, temp->hash_value-1, indent+4); + + printf ("%*s }\n" + "%*selse\n" + "%*s {\n", + indent, "", indent, "", indent, ""); + + output_switches (temp, part2, size2, temp->hash_value, max_hash_value, indent+4); + + printf ("%*s }\n", + indent, ""); + } + else + { + /* Output a single switch. */ + int lowest_case_value = list->hash_value; + if (size == 1) + { + int jumps_away = 0; + assert (min_hash_value <= lowest_case_value); + assert (lowest_case_value <= max_hash_value); + if (min_hash_value == max_hash_value) + output_switch_case (list, indent, &jumps_away); + else + { + printf ("%*sif (key == %d)\n" + "%*s {\n", + indent, "", lowest_case_value, + indent, ""); + output_switch_case (list, indent+4, &jumps_away); + printf ("%*s }\n", + indent, ""); + } + } + else + { + if (lowest_case_value == 0) + printf ("%*sswitch (key)\n", indent, ""); + else + printf ("%*sswitch (key - %d)\n", indent, "", lowest_case_value); + printf ("%*s {\n", + indent, ""); + for (; size > 0; size--) + { + int jumps_away = 0; + printf ("%*s case %d:\n", + indent, "", list->hash_value - lowest_case_value); + list = output_switch_case (list, indent+6, &jumps_away); + if (!jumps_away) + printf ("%*s break;\n", + indent, ""); + } + printf ("%*s }\n", + indent, ""); + } + } +} + +/* Generates C code to perform the keyword lookup. */ + +void +Key_List::output_lookup_function_body (const Output_Compare& comparison) +{ + T (Trace t ("Key_List::output_lookup_function_body");) + + printf (" if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)\n" + " {\n" + " register int key = %s (str, len);\n\n", + option.get_hash_name ()); + + if (option[SWITCH]) + { + int switch_size = num_hash_values (); + int num_switches = option.get_total_switches (); + if (num_switches > switch_size) + num_switches = switch_size; + + printf (" if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE)\n" + " {\n"); + if (option[DUP]) + { + if (option[LENTABLE]) + printf (" register %s%s *lengthptr;\n", + const_always, smallest_integral_type (max_key_len)); + printf (" register "); + output_const_type (const_readonly_array, struct_tag); + printf ("*wordptr;\n"); + printf (" register "); + output_const_type (const_readonly_array, struct_tag); + printf ("*wordendptr;\n"); + } + if (option[TYPE]) + { + printf (" register "); + output_const_type (const_readonly_array, struct_tag); + printf ("*resword;\n\n"); + } + else + printf (" register %sresword;\n\n", + struct_tag); + + output_switches (head, num_switches, switch_size, min_hash_value, max_hash_value, 10); + + if (option[DUP]) + { + int indent = 8; + printf ("%*s return 0;\n" + "%*smulticompare:\n" + "%*s while (wordptr < wordendptr)\n" + "%*s {\n", + indent, "", indent, "", indent, "", indent, ""); + if (option[LENTABLE]) + { + printf ("%*s if (len == *lengthptr)\n" + "%*s {\n", + indent, "", indent, ""); + indent += 4; + } + printf ("%*s register %schar *s = ", + indent, "", const_always); + if (option[TYPE]) + printf ("wordptr->%s", option.get_key_name ()); + else + printf ("*wordptr"); + printf (";\n\n" + "%*s if (", + indent, ""); + comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s")); + printf (")\n" + "%*s return %s;\n", + indent, "", + option[TYPE] ? "wordptr" : "s"); + if (option[LENTABLE]) + { + indent -= 4; + printf ("%*s }\n", + indent, ""); + } + if (option[LENTABLE]) + printf ("%*s lengthptr++;\n", + indent, ""); + printf ("%*s wordptr++;\n" + "%*s }\n", + indent, "", indent, ""); + } + printf (" return 0;\n" + " compare:\n"); + if (option[TYPE]) + { + printf (" {\n" + " register %schar *s = resword->%s;\n\n" + " if (", + const_always, option.get_key_name ()); + comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s")); + printf (")\n" + " return resword;\n" + " }\n"); + } + else + { + printf (" if ("); + comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("resword")); + printf (")\n" + " return resword;\n"); + } + printf (" }\n"); + } + else + { + printf (" if (key <= MAX_HASH_VALUE && key >= 0)\n"); + + if (option[DUP]) + { + int indent = 8; + printf ("%*s{\n" + "%*s register int index = lookup[key];\n\n" + "%*s if (index >= 0)\n", + indent, "", indent, "", indent, ""); + if (option[LENTABLE]) + { + printf ("%*s {\n" + "%*s if (len == lengthtable[index])\n", + indent, "", indent, ""); + indent += 4; + } + printf ("%*s {\n" + "%*s register %schar *s = %s[index]", + indent, "", + indent, "", const_always, option.get_wordlist_name ()); + if (option[TYPE]) + printf (".%s", option.get_key_name ()); + printf (";\n\n" + "%*s if (", + indent, ""); + comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s")); + printf (")\n" + "%*s return ", + indent, ""); + if (option[TYPE]) + printf ("&%s[index]", option.get_wordlist_name ()); + else + printf ("s"); + printf (";\n" + "%*s }\n", + indent, ""); + if (option[LENTABLE]) + { + indent -= 4; + printf ("%*s }\n", indent, ""); + } + if (total_duplicates > 0) + { + printf ("%*s else if (index < -TOTAL_KEYWORDS)\n" + "%*s {\n" + "%*s register int offset = - 1 - TOTAL_KEYWORDS - index;\n", + indent, "", indent, "", indent, ""); + if (option[LENTABLE]) + printf ("%*s register %s%s *lengthptr = &lengthtable[TOTAL_KEYWORDS + lookup[offset]];\n", + indent, "", const_always, smallest_integral_type (max_key_len)); + printf ("%*s register ", + indent, ""); + output_const_type (const_readonly_array, struct_tag); + printf ("*wordptr = &%s[TOTAL_KEYWORDS + lookup[offset]];\n", + option.get_wordlist_name ()); + printf ("%*s register ", + indent, ""); + output_const_type (const_readonly_array, struct_tag); + printf ("*wordendptr = wordptr + -lookup[offset + 1];\n\n"); + printf ("%*s while (wordptr < wordendptr)\n" + "%*s {\n", + indent, "", indent, ""); + if (option[LENTABLE]) + { + printf ("%*s if (len == *lengthptr)\n" + "%*s {\n", + indent, "", indent, ""); + indent += 4; + } + printf ("%*s register %schar *s = ", + indent, "", const_always); + if (option[TYPE]) + printf ("wordptr->%s", option.get_key_name ()); + else + printf ("*wordptr"); + printf (";\n\n" + "%*s if (", + indent, ""); + comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s")); + printf (")\n" + "%*s return %s;\n", + indent, "", + option[TYPE] ? "wordptr" : "s"); + if (option[LENTABLE]) + { + indent -= 4; + printf ("%*s }\n", + indent, ""); + } + if (option[LENTABLE]) + printf ("%*s lengthptr++;\n", + indent, ""); + printf ("%*s wordptr++;\n" + "%*s }\n" + "%*s }\n", + indent, "", indent, "", indent, ""); + } + printf ("%*s}\n", + indent, ""); + } + else + { + int indent = 8; + if (option[LENTABLE]) + { + printf ("%*sif (len == lengthtable[key])\n", + indent, ""); + indent += 2; + } + + printf ("%*s{\n" + "%*s register %schar *s = %s[key]", + indent, "", + indent, "", const_always, option.get_wordlist_name ()); + + if (option[TYPE]) + printf (".%s", option.get_key_name ()); + + printf (";\n\n" + "%*s if (", + indent, ""); + comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s")); + printf (")\n" + "%*s return ", + indent, ""); + if (option[TYPE]) + printf ("&%s[key]", option.get_wordlist_name ()); + else + printf ("s"); + printf (";\n" + "%*s}\n", + indent, ""); + } + } + printf (" }\n" + " return 0;\n"); +} + +/* Generates C code for the lookup function. */ + +void +Key_List::output_lookup_function (void) +{ + T (Trace t ("Key_List::output_lookup_function");) + + /* Output the function's head. */ + if (option[KRC] | option[C] | option[ANSIC]) + printf ("#ifdef __GNUC__\n" + "__inline\n" + "#endif\n"); + + printf ("%s%s\n", + const_for_struct, return_type); + if (option[CPLUSPLUS]) + printf ("%s::", option.get_class_name ()); + printf ("%s ", option.get_function_name ()); + printf (option[KRC] ? + "(str, len)\n" + " register char *str;\n" + " register unsigned int len;\n" : + option[C] ? + "(str, len)\n" + " register const char *str;\n" + " register unsigned int len;\n" : + option[ANSIC] | option[CPLUSPLUS] ? + "(register const char *str, register unsigned int len)\n" : + ""); + + /* Output the function's body. */ + printf ("{\n"); + + if (option[ENUM] && !option[GLOBAL]) + { + Output_Enum style (" "); + output_constants (style); + } + + if (!option[GLOBAL]) + output_lookup_tables (); + + if (option[LENTABLE]) + output_lookup_function_body (Output_Compare_Memcmp ()); + else + { + if (option[COMP]) + output_lookup_function_body (Output_Compare_Strncmp ()); + else + output_lookup_function_body (Output_Compare_Strcmp ()); + } + + printf ("}\n"); +} + +/* ------------------------------------------------------------------------- */ + +/* Generates the hash function and the key word recognizer function + based upon the user's Options. */ + +void +Key_List::output (void) +{ + T (Trace t ("Key_List::output");) + + compute_min_max (); + + if (option[C] | option[ANSIC] | option[CPLUSPLUS]) + { + const_always = "const "; + const_readonly_array = (option[CONST] ? "const " : ""); + const_for_struct = ((option[CONST] && option[TYPE]) ? "const " : ""); + } + else + { + const_always = ""; + const_readonly_array = ""; + const_for_struct = ""; + } + + if (!option[TYPE]) + { + return_type = (const_always[0] ? "const char *" : "char *"); + struct_tag = (const_always[0] ? "const char *" : "char *"); + } + + char_to_index = (option[SEVENBIT] ? "" : "(unsigned char)"); + + printf ("/* "); + if (option[KRC]) + printf ("KR-C"); + else if (option[C]) + printf ("C"); + else if (option[ANSIC]) + printf ("ANSI-C"); + else if (option[CPLUSPLUS]) + printf ("C++"); + printf (" code produced by gperf version %s */\n", version_string); + Options::print_options (); + + printf ("%s\n", include_src); + + if (option[TYPE] && !option[NOTYPE]) /* Output type declaration now, reference it later on.... */ + printf ("%s;\n", array_type); + + if (option[INCLUDE]) + printf ("#include <string.h>\n"); /* Declare strlen(), strcmp(), strncmp(). */ + + if (!option[ENUM]) + { + Output_Defines style; + output_constants (style); + } + else if (option[GLOBAL]) + { + Output_Enum style (""); + output_constants (style); + } + + printf ("/* maximum key range = %d, duplicates = %d */\n\n", + max_hash_value - min_hash_value + 1, total_duplicates); + + if (option[CPLUSPLUS]) + printf ("class %s\n" + "{\n" + "private:\n" + " static inline unsigned int %s (const char *str, unsigned int len);\n" + "public:\n" + " static %s%s%s (const char *str, unsigned int len);\n" + "};\n" + "\n", + option.get_class_name (), option.get_hash_name (), + const_for_struct, return_type, option.get_function_name ()); + + output_hash_function (); + + if (option[GLOBAL]) + output_lookup_tables (); + + output_lookup_function (); + + if (additional_code) + for (int c; (c = getchar ()) != EOF; putchar (c)) + ; + + fflush (stdout); +} + +/* ========================= End of Output routines ========================= */ + +/* Sorts the keys by hash value. */ + +void +Key_List::sort (void) +{ + T (Trace t ("Key_List::sort");) + hash_sort = 1; + occurrence_sort = 0; + + head = merge_sort (head); +} + +/* Dumps the key list to stderr stream. */ + +void +Key_List::dump () +{ + T (Trace t ("Key_List::dump");) + int field_width = option.get_max_keysig_size (); + + fprintf (stderr, "\nList contents are:\n(hash value, key length, index, %*s, keyword):\n", + field_width, "char_set"); + + for (List_Node *ptr = head; ptr; ptr = ptr->next) + fprintf (stderr, "%11d,%11d,%6d, %*.*s, %.*s\n", + ptr->hash_value, ptr->key_length, ptr->index, + field_width, ptr->char_set_length, ptr->char_set, + ptr->key_length, ptr->key); +} + +/* Simple-minded constructor action here... */ + +Key_List::Key_List (void) +{ + T (Trace t ("Key_List::Key_List");) + total_keys = 1; + max_key_len = INT_MIN; + min_key_len = INT_MAX; + array_type = 0; + return_type = 0; + struct_tag = 0; + head = 0; + total_duplicates = 0; + additional_code = 0; +} + +/* Returns the length of entire key list. */ + +int +Key_List::keyword_list_length (void) +{ + T (Trace t ("Key_List::keyword_list_length");) + return list_len; +} + +/* Returns length of longest key read. */ + +int +Key_List::max_key_length (void) +{ + T (Trace t ("Key_List::max_key_length");) + return max_key_len; +} + diff --git a/src/key-list.h b/src/key-list.h new file mode 100644 index 000000000000..98b8fa5e0c84 --- /dev/null +++ b/src/key-list.h @@ -0,0 +1,96 @@ +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Data and function member declarations for the keyword list class. + + Copyright (C) 1989-1998 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +/* The key word list is a useful abstraction that keeps track of + various pieces of information that enable that fast generation + of the Gen_Perf.hash function. A Key_List is a singly-linked + list of List_Nodes. */ + +#ifndef key_list_h +#define key_list_h 1 + +#include "list-node.h" +#include "vectors.h" +#include "read-line.h" + +/* OSF/1 cxx needs these forward declarations. */ +struct Output_Constants; +struct Output_Compare; + +class Key_List : private Read_Line, public Vectors +{ +private: + const char *array_type; /* Pointer to the type for word list. */ + const char *return_type; /* Pointer to return type for lookup function. */ + const char *struct_tag; /* Shorthand for user-defined struct tag type. */ + const char *include_src; /* C source code to be included verbatim. */ + int max_key_len; /* Maximum length of the longest keyword. */ + int min_key_len; /* Minimum length of the shortest keyword. */ + int min_hash_value; /* Minimum hash value for all keywords. */ + int max_hash_value; /* Maximum hash value for all keywords. */ + int occurrence_sort; /* True if sorting by occurrence. */ + int hash_sort; /* True if sorting by hash value. */ + int additional_code; /* True if any additional C code is included. */ + int list_len; /* Length of head's Key_List, not counting duplicates. */ + int total_keys; /* Total number of keys, counting duplicates. */ + static int determined[MAX_ALPHA_SIZE]; /* Used in function reorder, below. */ + static int get_occurrence (List_Node *ptr); +#ifndef strcspn + static int strcspn (const char *s, const char *reject); +#endif + static int already_determined (List_Node *ptr); + static void set_determined (List_Node *ptr); + void compute_min_max (void); + int num_hash_values (void); + void output_constants (struct Output_Constants&); + void output_hash_function (void); + void output_keylength_table (void); + void output_keyword_table (void); + void output_lookup_array (void); + void output_lookup_tables (void); + void output_lookup_function_body (const struct Output_Compare&); + void output_lookup_function (void); + void set_output_types (void); + void dump (void); + const char *get_array_type (void); + const char *save_include_src (void); + const char *get_special_input (char delimiter); + List_Node *merge (List_Node *list1, List_Node *list2); + List_Node *merge_sort (List_Node *head); + +protected: + List_Node *head; /* Points to the head of the linked list. */ + int total_duplicates; /* Total number of duplicate hash values. */ + +public: + Key_List (void); + ~Key_List (void); + int keyword_list_length (void); + int max_key_length (void); + void reorder (void); + void sort (void); + void read_keys (void); + void output (void); +}; + +#endif diff --git a/src/keylist.c b/src/keylist.c new file mode 100644 index 000000000000..f92d97549694 --- /dev/null +++ b/src/keylist.c @@ -0,0 +1,1033 @@ +/* Routines for building, ordering, and printing the keyword list. + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <assert.h> +#include <stdio.h> +#include "options.h" +#include "readline.h" +#include "keylist.h" +#include "hashtable.h" +#include "stderr.h" +#ifdef sparc +#include <alloca.h> +#endif + +/* Current release version. */ +extern char *version_string; + +/* See comments in perfect.cc. */ +extern int occurrences[ALPHABET_SIZE]; + +/* Ditto. */ +extern int asso_values[ALPHABET_SIZE]; + +/* Used in function reorder, below. */ +static bool determined[ALPHABET_SIZE]; + +/* Default type for generated code. */ +static char *default_array_type = "char *"; + +/* Generated function ``in_word_set'' default return type. */ +static char *default_return_type = "char *"; + +/* Largest positive integer value. */ +#define MAX_INT ((~(unsigned)0)>>1) + +/* Most negative integer value. */ +#define NEG_MAX_INT ((~(unsigned)0)^((~(unsigned)0)>>1)) + +/* Maximum value an unsigned char can take. */ +#define MAX_UNSIGNED_CHAR 256 + +/* Maximum value an unsigned short can take. */ +#define MAX_UNSIGNED_SHORT 65536 + +/* Make the hash table 5 times larger than the number of keyword entries. */ +#define TABLE_MULTIPLE 5 + +/* Efficiently returns the least power of two greater than or equal to X! */ +#define POW(X) ((!X)?1:(X-=1,X|=X>>1,X|=X>>2,X|=X>>4,X|=X>>8,X|=X>>16,(++X))) + +/* How wide the printed field width must be to contain the maximum hash value. */ +static int field_width = 2; + +/* Globally visible KEY_LIST object. */ + +KEY_LIST key_list; + +/* Gathers the input stream into a buffer until one of two things occur: + + 1. We read a '%' followed by a '%' + 2. We read a '%' followed by a '}' + + The first symbolizes the beginning of the keyword list proper, + The second symbolizes the end of the C source code to be generated + verbatim in the output file. + + I assume that the keys are separated from the optional preceding struct + declaration by a consecutive % followed by either % or } starting in + the first column. The code below uses an expandible buffer to scan off + and return a pointer to all the code (if any) appearing before the delimiter. */ + +static char * +get_special_input (delimiter) + char delimiter; +{ + char *xmalloc (); + int size = 80; + char *buf = xmalloc (size); + int c, i; + + for (i = 0; (c = getchar ()) != EOF; i++) + { + if (c == '%') + { + if ((c = getchar ()) == delimiter) + { + + while ((c = getchar ()) != '\n') + ; /* Discard newline. */ + + if (i == 0) + return ""; + else + { + buf[delimiter == '%' && buf[i - 2] == ';' ? i - 2 : i - 1] = '\0'; + return buf; + } + } + else + ungetc (c, stdin); + } + else if (i >= size) /* Yikes, time to grow the buffer! */ + { + char *temp = xmalloc (size *= 2); + int j; + + for (j = 0; j < i; j++) + temp[j] = buf[j]; + + free (buf); + buf = temp; + } + buf[i] = c; + } + + return NULL; /* Problem here. */ +} + +/* Stores any C text that must be included verbatim into the + generated code output. */ + +static char * +save_include_src () +{ + int c; + + if ((c = getchar ()) != '%') + { + ungetc (c, stdin); + return ""; + } + else if ((c = getchar ()) != '{') + report_error ("internal error, %c != '{' on line %d in file %s%a", c, __LINE__, __FILE__); + /*NOT REACHED*/ + else + return get_special_input ('}'); +} + +/* strcspn - find length of initial segment of s consisting entirely + of characters not from reject (borrowed from Henry Spencer's + ANSI string package). */ + +static int +strcspn (s, reject) + char *s; + char *reject; +{ + char *scan; + char *rej_scan; + int count = 0; + + for (scan = s; *scan; scan++) + { + + for (rej_scan = reject; *rej_scan;) + if (*scan == *rej_scan++) + return count; + + count++; + } + + return count; +} + +/* Determines from the input file whether the user wants to build a table + from a user-defined struct, or whether the user is content to simply + use the default array of keys. */ + +static char * +get_array_type () +{ + return get_special_input ('%'); +} + +/* Sets up the Return_Type, the Struct_Tag type and the Array_Type + based upon various user Options. */ + +static void +set_output_types () +{ + char *xmalloc (); + + if (OPTION_ENABLED (option, TYPE) && !(key_list.array_type = get_array_type ())) + return; /* Something's wrong, bug we'll catch it later on.... */ + else if (OPTION_ENABLED (option, TYPE)) /* Yow, we've got a user-defined type... */ + { + int struct_tag_length = strcspn (key_list.array_type, "{\n\0"); + + if (OPTION_ENABLED (option, POINTER)) /* And it must return a pointer... */ + { + key_list.return_type = xmalloc (struct_tag_length + 2); + strncpy (key_list.return_type, key_list.array_type, struct_tag_length); + key_list.return_type[struct_tag_length] = '\0'; + strcat (key_list.return_type, "*"); + } + + key_list.struct_tag = (char *) xmalloc (struct_tag_length + 1); + strncpy (key_list.struct_tag, key_list.array_type, struct_tag_length); + key_list.struct_tag[struct_tag_length] = '\0'; + } + else if (OPTION_ENABLED (option, POINTER)) /* Return a char *. */ + key_list.return_type = default_array_type; +} + +/* Reads in all keys from standard input and creates a linked list pointed + to by Head. This list is then quickly checked for ``links,'' i.e., + unhashable elements possessing identical key sets and lengths. */ + +void +read_keys () +{ + char *ptr; + + key_list.include_src = save_include_src (); + set_output_types (); + + /* Oops, problem with the input file. */ + if (! (ptr = read_line ())) + report_error ("No words in input file, did you forget\ + to prepend %s or use -t accidentally?\n%a", "%%"); + + /* Read in all the keywords from the input file. */ + else + { + LIST_NODE *temp, *trail; + char *delimiter = GET_DELIMITER (option); + + for (temp = key_list.head = make_list_node (ptr, strcspn (ptr, delimiter)); + (ptr = read_line ()) && strcmp (ptr, "%%"); + key_list.total_keys++, temp = temp->next) + temp->next = make_list_node (ptr, strcspn (ptr, delimiter)); + + /* See if any additional C code is included at end of this file. */ + if (ptr) + key_list.additional_code = TRUE; + { + /* If this becomes TRUE we've got a link. */ + bool link = FALSE; + + /* Make large hash table for efficiency. */ + int table_size = (key_list.list_len = key_list.total_keys) * TABLE_MULTIPLE; + + /* By allocating the memory here we save on dynamic allocation overhead. + Table must be a power of 2 for the hash function scheme to work. */ + LIST_NODE **table = (LIST_NODE **) alloca (POW (table_size) * sizeof (LIST_NODE *)); + + hash_table_init (table, table_size); + + /* Test whether there are any links and also set the maximum length of + an identifier in the keyword list. */ + + for (temp = key_list.head, trail = NULL; temp; temp = temp->next) + { + LIST_NODE *ptr = retrieve (temp, OPTION_ENABLED (option, NOLENGTH)); + + /* Check for links. We deal with these by building an equivalence class + of all duplicate values (i.e., links) so that only 1 keyword is + representative of the entire collection. This *greatly* simplifies + processing during later stages of the program. */ + + if (ptr) + { + key_list.list_len--; + trail->next = temp->next; + temp->link = ptr->link; + ptr->link = temp; + link = TRUE; + + /* Complain if user hasn't enabled the duplicate option. */ + if (!OPTION_ENABLED (option, DUP)) + fprintf (stderr, "Key link: \"%s\" = \"%s\", with key set \"%s\".\n", + temp->key, ptr->key, temp->char_set); + else if (OPTION_ENABLED (option, DEBUG)) + fprintf (stderr, "Key link: \"%s\" = \"%s\", with key set \"%s\".\n", + temp->key, ptr->key, temp->char_set); + } + else + trail = temp; + + /* Update minimum and maximum keyword length, if needed. */ + if (temp->length > key_list.max_key_len) + key_list.max_key_len = temp->length; + if (temp->length < key_list.min_key_len) + key_list.min_key_len = temp->length; + } + + /* Free up the dynamic memory used in the hash table. */ + hash_table_destroy (); + + /* Exit program if links exists and option[DUP] not set, since we can't continue safely. */ + if (link) + report_error (OPTION_ENABLED (option, DUP) + ? "Some input keys have identical hash values, examine output carefully...\n" + : "Some input keys have identical hash values,\ntry different key positions or use option -D.\n%a"); + } + if (OPTION_ENABLED (option, ALLCHARS)) + SET_CHARSET_SIZE (option, key_list.max_key_len); + } +} + +/* Recursively merges two sorted lists together to form one sorted list. The + ordering criteria is by frequency of occurrence of elements in the key set + or by the hash value. This is a kludge, but permits nice sharing of + almost identical code without incurring the overhead of a function + call comparison. */ + +static LIST_NODE * +merge (list1, list2) + LIST_NODE *list1; + LIST_NODE *list2; +{ + if (!list1) + return list2; + else if (!list2) + return list1; + else if (key_list.occurrence_sort && list1->occurrence < list2->occurrence + || key_list.hash_sort && list1->hash_value > list2->hash_value) + { + list2->next = merge (list2->next, list1); + return list2; + } + else + { + list1->next = merge (list1->next, list2); + return list1; + } +} + +/* Applies the merge sort algorithm to recursively sort the key list by + frequency of occurrence of elements in the key set. */ + +static LIST_NODE * +merge_sort (head) + LIST_NODE *head; +{ + if (!head || !head->next) + return head; + else + { + LIST_NODE *middle = head; + LIST_NODE *temp = head->next->next; + + while (temp) + { + temp = temp->next; + middle = middle->next; + if (temp) + temp = temp->next; + } + + temp = middle->next; + middle->next = NULL; + return merge (merge_sort (head), merge_sort (temp)); + } +} + +/* Returns the frequency of occurrence of elements in the key set. */ + +static int +get_occurrence (ptr) + LIST_NODE *ptr; +{ + int value = 0; + char *temp; + + for (temp = ptr->char_set; *temp; temp++) + value += occurrences[*temp]; + + return value; +} + +/* Enables the index location of all key set elements that are now + determined. */ + +static void +set_determined (ptr) + LIST_NODE *ptr; +{ + char *temp; + + for (temp = ptr->char_set; *temp; temp++) + determined[*temp] = TRUE; + +} + +/* Returns TRUE if PTR's key set is already completely determined. */ + +static bool +already_determined (ptr) + LIST_NODE *ptr; +{ + bool is_determined = TRUE; + char *temp; + + for (temp = ptr->char_set; is_determined && *temp; temp++) + is_determined = determined[*temp]; + + return is_determined; +} + +/* Reorders the table by first sorting the list so that frequently occuring + keys appear first, and then the list is reorded so that keys whose values + are already determined will be placed towards the front of the list. This + helps prune the search time by handling inevitable collisions early in the + search process. See Cichelli's paper from Jan 1980 JACM for details.... */ + +void +reorder () +{ + LIST_NODE *ptr; + + for (ptr = key_list.head; ptr; ptr = ptr->next) + ptr->occurrence = get_occurrence (ptr); + + key_list.hash_sort = FALSE; + key_list.occurrence_sort = TRUE; + + for (ptr = key_list.head = merge_sort (key_list.head); ptr->next; ptr = ptr->next) + { + set_determined (ptr); + + if (already_determined (ptr->next)) + continue; + else + { + LIST_NODE *trail_ptr = ptr->next; + LIST_NODE *run_ptr = trail_ptr->next; + + for (; run_ptr; run_ptr = trail_ptr->next) + { + + if (already_determined (run_ptr)) + { + trail_ptr->next = run_ptr->next; + run_ptr->next = ptr->next; + ptr = ptr->next = run_ptr; + } + else + trail_ptr = run_ptr; + } + } + } +} + +/* Determines the maximum and minimum hash values. One notable feature is + Ira Pohl's optimal algorithm to calculate both the maximum and minimum + items in a list in O(3n/2) time (faster than the O (2n) method). + Returns the maximum hash value encountered. */ + +static int +print_min_max () +{ + int min_hash_value; + int max_hash_value; + LIST_NODE *temp; + + if (ODD (key_list.list_len)) /* Pre-process first item, list now has an even length. */ + { + min_hash_value = max_hash_value = key_list.head->hash_value; + temp = key_list.head->next; + } + else /* List is already even length, no extra work necessary. */ + { + min_hash_value = MAX_INT; + max_hash_value = NEG_MAX_INT; + temp = key_list.head; + } + + for ( ; temp; temp = temp->next) /* Find max and min in optimal o(3n/2) time. */ + { + static int i; + int key_2, key_1 = temp->hash_value; + temp = temp->next; + key_2 = temp->hash_value; + i++; + + if (key_1 < key_2) + { + if (key_1 < min_hash_value) + min_hash_value = key_1; + if (key_2 > max_hash_value) + max_hash_value = key_2; + } + else + { + if (key_2 < min_hash_value) + min_hash_value = key_2; + if (key_1 > max_hash_value) + max_hash_value = key_1; + } + } + + printf ("\n#define MIN_WORD_LENGTH %d\n#define MAX_WORD_LENGTH %d\ +\n#define MIN_HASH_VALUE %d\n#define MAX_HASH_VALUE %d\ +\n/*\n%5d keywords\n%5d is the maximum key range\n*/\n\n", + key_list.min_key_len == MAX_INT ? key_list.max_key_len : key_list.min_key_len, + key_list.max_key_len, min_hash_value, max_hash_value, + key_list.total_keys, (max_hash_value - min_hash_value + 1)); + return max_hash_value; +} + +/* Generates the output using a C switch. This trades increased search + time for decreased table space (potentially *much* less space for + sparse tables). It the user has specified their own struct in the + keyword file *and* they enable the POINTER option we have extra work to + do. The solution here is to maintain a local static array of user + defined struct's, as with the Print_Lookup_Function. Then we use for + switch statements to perform a strcmp or strncmp, returning 0 if the str + fails to match, and otherwise returning a pointer to appropriate index + location in the local static array. */ + +static void +print_switch () +{ + char *comp_buffer; + LIST_NODE *curr = key_list.head; + int pointer_and_type_enabled = OPTION_ENABLED (option, POINTER) && OPTION_ENABLED (option, TYPE); + int total_switches = GET_TOTAL_SWITCHES (option); + int switch_size = keyword_list_length () / total_switches; + + if (pointer_and_type_enabled) + { + comp_buffer = (char *) alloca (strlen ("*str == *resword->%s && !strncmp (str + 1, resword->%s + 1, len - 1)") + + 2 * strlen (GET_KEY_NAME (option)) + 1); + sprintf (comp_buffer, OPTION_ENABLED (option, COMP) + ? "*str == *resword->%s && !strncmp (str + 1, resword->%s + 1, len - 1)" + : "*str == *resword->%s && !strcmp (str + 1, resword->%s + 1)", + GET_KEY_NAME (option), GET_KEY_NAME (option)); + } + else + comp_buffer = OPTION_ENABLED (option, COMP) + ? "*str == *resword && !strncmp (str + 1, resword + 1, len - 1)" + : "*str == *resword && !strcmp (str + 1, resword + 1)"; + + printf (" if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)\n {\n\ + register int key = %s (str, len);\n\n\ + if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE)\n {\n", GET_HASH_NAME (option)); + + /* Properly deal with user's who request multiple switch statements. */ + + while (curr) + { + LIST_NODE *temp = curr; + int lowest_case_value = curr->hash_value; + int number_of_cases = 0; + + /* Figure out a good cut point to end this switch. */ + + for (; temp && ++number_of_cases < switch_size; temp = temp->next) + if (temp->next && temp->hash_value == temp->next->hash_value) + while (temp->next && temp->hash_value == temp->next->hash_value) + temp = temp->next; + + if (temp) + printf (" if (key <= %d)\n {\n", temp->hash_value); + else + printf (" {\n"); + + /* Output each keyword as part of a switch statement indexed by hash value. */ + + if (OPTION_ENABLED (option, POINTER) || OPTION_ENABLED (option, DUP)) + { + int i = 0; + + printf (" %s%s *resword; %s\n\n", + OPTION_ENABLED (option, CONST) ? "const " : "", + pointer_and_type_enabled ? key_list.struct_tag : "char", + OPTION_ENABLED (option, LENTABLE) && !OPTION_ENABLED (option, DUP) ? "int key_len;" : ""); + printf (" switch (key - %d)\n {\n", lowest_case_value); + + for (temp = curr; temp && ++i <= number_of_cases; temp = temp->next) + { + printf (" case %*d:", field_width, temp->hash_value - lowest_case_value); + if (OPTION_ENABLED (option, DEBUG)) + printf (" /* hash value = %4d, keyword = \"%s\" */", temp->hash_value, temp->key); + putchar ('\n'); + + /* Handle `natural links,' i.e., those that occur statically. */ + + if (temp->link) + { + LIST_NODE *links; + + for (links = temp; links; links = links->link) + { + if (pointer_and_type_enabled) + printf (" resword = &wordlist[%d];\n", links->index); + else + printf (" resword = \"%s\";\n", links->key); + printf (" if (%s) return resword;\n", comp_buffer); + } + } + /* Handle unresolved duplicate hash values. These are guaranteed + to be adjacent since we sorted the keyword list by increasing + hash values. */ + if (temp->next && temp->hash_value == temp->next->hash_value) + { + + for ( ; temp->next && temp->hash_value == temp->next->hash_value; + temp = temp->next) + { + if (pointer_and_type_enabled) + printf (" resword = &wordlist[%d];\n", temp->index); + else + printf (" resword = \"%s\";\n", temp->key); + printf (" if (%s) return resword;\n", comp_buffer); + } + if (pointer_and_type_enabled) + printf (" resword = &wordlist[%d];\n", temp->index); + else + printf (" resword = \"%s\";\n", temp->key); + printf (" return %s ? resword : 0;\n", comp_buffer); + } + else if (temp->link) + printf (" return 0;\n"); + else + { + if (pointer_and_type_enabled) + printf (" resword = &wordlist[%d];", temp->index); + else + printf (" resword = \"%s\";", temp->key); + if (OPTION_ENABLED (option, LENTABLE) && !OPTION_ENABLED (option, DUP)) + printf (" key_len = %d;", temp->length); + printf (" break;\n"); + } + } + printf (" default: return 0;\n }\n"); + printf (OPTION_ENABLED (option, LENTABLE) && !OPTION_ENABLED (option, DUP) + ? " if (len == key_len && %s)\n return resword;\n" + : " if (%s)\n return resword;\n", comp_buffer); + printf (" return 0;\n }\n"); + curr = temp; + } + else /* Nothing special required here. */ + { + int i = 0; + printf (" char *s;\n\n switch (key - %d)\n {\n", + lowest_case_value); + + for (temp = curr; temp && ++i <= number_of_cases; temp = temp->next) + if (OPTION_ENABLED (option, LENTABLE)) + printf (" case %*d: if (len == %d) s = \"%s\"; else return 0; break;\n", + field_width, temp->hash_value - lowest_case_value, + temp->length, temp->key); + else + printf (" case %*d: s = \"%s\"; break;\n", + field_width, temp->hash_value - lowest_case_value, temp->key); + + printf (" default: return 0;\n }\n "); + printf ("return *s == *str && !%s;\n }\n", + OPTION_ENABLED (option, COMP) + ? "strncmp (s + 1, str + 1, len - 1)" : "strcmp (s + 1, str + 1)"); + curr = temp; + } + } + printf (" }\n }\n return 0;\n}\n"); +} + +/* Prints out a table of keyword lengths, for use with the + comparison code in generated function ``in_word_set.'' */ + +static void +print_keylength_table () +{ + int max_column = 15; + int index = 0; + int column = 0; + char *indent = OPTION_ENABLED (option, GLOBAL) ? "" : " "; + LIST_NODE *temp; + + if (!OPTION_ENABLED (option, DUP) && !OPTION_ENABLED (option, SWITCH)) + { + printf ("\n%sstatic %sunsigned %s lengthtable[] =\n%s%s{\n ", + indent, OPTION_ENABLED (option, CONST) ? "const " : "", + key_list.max_key_len < MAX_UNSIGNED_CHAR ? "char" : + (key_list.max_key_len < MAX_UNSIGNED_SHORT ? "short" : "long"), + indent, indent); + + for (temp = key_list.head; temp; temp = temp->next, index++) + { + + if (index < temp->hash_value) + { + + for ( ; index < temp->hash_value; index++) + printf ("%3d%s", 0, ++column % (max_column - 1) ? "," : ",\n "); + } + + printf ("%3d%s", temp->length, ++column % (max_column - 1 ) ? "," : ",\n "); + } + + printf ("\n%s%s};\n\n", indent, indent); + } +} + +/* Prints out the array containing the key words for the Perfect + hash function. */ + +static void +print_keyword_table () +{ + char *l_brace = *key_list.head->rest ? "{" : ""; + char *r_brace = *key_list.head->rest ? "}," : ""; + int doing_switch = OPTION_ENABLED (option, SWITCH); + char *indent = OPTION_ENABLED (option, GLOBAL) ? "" : " "; + int index = 0; + LIST_NODE *temp; + + printf ("\n%sstatic %s%s wordlist[] =\n%s%s{\n", + indent, OPTION_ENABLED (option, CONST) ? "const " : "", + key_list.struct_tag, indent, indent); + + /* Generate an array of reserved words at appropriate locations. */ + + for (temp = key_list.head; temp; temp = temp->next, index++) + { + temp->index = index; + + if (!doing_switch && index < temp->hash_value) + { + int column; + + printf (" "); + + for (column = 1; index < temp->hash_value; index++, column++) + printf ("%s\"\",%s %s", l_brace, r_brace, column % 9 ? "" : "\n "); + + if (column % 10) + printf ("\n"); + else + { + printf ("%s\"%s\", %s%s\n", l_brace, temp->key, temp->rest, r_brace); + continue; + } + } + + printf (" %s\"%s\", %s%s\n", l_brace, temp->key, temp->rest, r_brace); + + /* Deal with links specially. */ + if (temp->link) + { + LIST_NODE *links; + + for (links = temp->link; links; links = links->link) + { + links->index = ++index; + printf (" %s\"%s\", %s%s\n", l_brace, links->key, links->rest, r_brace); + } + } + + } + + printf ("%s%s};\n\n", indent, indent); +} + +/* Generates C code for the hash function that returns the + proper encoding for each key word. */ + +static void +print_hash_function (max_hash_value) + int max_hash_value; +{ + int max_column = 10; + int count = max_hash_value; + + /* Calculate maximum number of digits required for MAX_HASH_VALUE. */ + + while ((count /= 10) > 0) + field_width++; + + if (OPTION_ENABLED (option, GNU)) + printf ("#ifdef __GNUC__\ninline\n#endif\n"); + + printf (OPTION_ENABLED (option, ANSI) + ? "static int\n%s (register const char *str, register int len)\n{\n static %sunsigned %s hash_table[] =\n {" + : "static int\n%s (str, len)\n register char *str;\n register unsigned int len;\n{\n static %sunsigned %s hash_table[] =\n {", + GET_HASH_NAME (option), OPTION_ENABLED (option, CONST) ? "const " : "", + max_hash_value < MAX_UNSIGNED_CHAR + ? "char" : (max_hash_value < MAX_UNSIGNED_SHORT ? "short" : "int")); + + for (count = 0; count < ALPHABET_SIZE; ++count) + { + if (!(count % max_column)) + printf ("\n "); + + printf ("%*d,", field_width, occurrences[count] ? asso_values[count] : max_hash_value); + } + + /* Optimize special case of ``-k 1,$'' */ + if (OPTION_ENABLED (option, DEFAULTCHARS)) + printf ("\n };\n return %s + hash_table[str[len - 1]] + hash_table[str[0]];\n}\n\n", + OPTION_ENABLED (option, NOLENGTH) ? "0" : "len"); + else + { + int key_pos; + + RESET (option); + + /* Get first (also highest) key position. */ + key_pos = GET (option); + + /* We can perform additional optimizations here. */ + if (!OPTION_ENABLED (option, ALLCHARS) && key_pos <= key_list.min_key_len) + { + printf ("\n };\n return %s", OPTION_ENABLED (option, NOLENGTH) ? "0" : "len"); + + for ( ; key_pos != EOS && key_pos != WORD_END; key_pos = GET (option)) + printf (" + hash_table[str[%d]]", key_pos - 1); + + printf ("%s;\n}\n\n", key_pos == WORD_END ? " + hash_table[str[len - 1]]" : ""); + } + + /* We've got to use the correct, but brute force, technique. */ + else + { + printf ("\n };\n register int hval = %s;\n\n switch (%s)\n {\n default:\n", + OPTION_ENABLED (option, NOLENGTH) + ? "0" : "len", OPTION_ENABLED (option, NOLENGTH) ? "len" : "hval"); + + /* User wants *all* characters considered in hash. */ + if (OPTION_ENABLED (option, ALLCHARS)) + { + int i; + + for (i = key_list.max_key_len; i > 0; i--) + printf (" case %d:\n hval += hash_table[str[%d]];\n", i, i - 1); + + printf (" }\n return hval;\n}\n\n"); + } + else /* do the hard part... */ + { + count = key_pos + 1; + + do + { + + while (--count > key_pos) + printf (" case %d:\n", count); + + printf (" case %d:\n hval += hash_table[str[%d]];\n", + key_pos, key_pos - 1); + } + while ((key_pos = GET (option)) != EOS && key_pos != WORD_END); + + printf (" }\n return hval%s ;\n}\n\n", key_pos == WORD_END + ? " + hash_table[str[len - 1]]" : ""); + } + } + } +} + +/* Generates C code to perform the keyword lookup. */ + +static void +print_lookup_function () +{ + printf (" if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)\n {\n\ + register int key = %s (str, len);\n\n\ + if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE)\n {\n\ + register %schar *s = wordlist[key]", + GET_HASH_NAME (option), OPTION_ENABLED (option, CONST) ? "const " : ""); + if (key_list.array_type != default_array_type) + printf (".%s", GET_KEY_NAME (option)); + + printf (";\n\n if (%s*s == *str && !%s)\n return %s", + OPTION_ENABLED (option, LENTABLE) ? "len == lengthtable[key]\n && " : "", + OPTION_ENABLED (option, COMP) ? "strncmp (str + 1, s + 1, len - 1)" : "strcmp (str + 1, s + 1)", + OPTION_ENABLED (option, TYPE) && OPTION_ENABLED (option, POINTER) ? "&wordlist[key]" : "s"); + printf (";\n }\n }\n return 0;\n}\n"); +} + +/* Generates the hash function and the key word recognizer function + based upon the user's Options. */ + +void +print_output () +{ + int global_table = OPTION_ENABLED (option, GLOBAL); + + printf ("%s\n", key_list.include_src); + + /* Potentially output type declaration now, reference it later on.... */ + if (OPTION_ENABLED (option, TYPE) && !OPTION_ENABLED (option, NOTYPE)) + printf ("%s;\n", key_list.array_type); + + print_hash_function (print_min_max ()); + + if (global_table) + if (OPTION_ENABLED (option, SWITCH)) + { + if (OPTION_ENABLED (option, LENTABLE) && OPTION_ENABLED (option, DUP)) + print_keylength_table (); + if (OPTION_ENABLED (option, POINTER) && OPTION_ENABLED (option, TYPE)) + print_keyword_table (); + } + else + { + if (OPTION_ENABLED (option, LENTABLE)) + print_keylength_table (); + print_keyword_table (); + } + /* Use the inline keyword to remove function overhead. */ + if (OPTION_ENABLED (option, GNU)) + printf ("#ifdef __GNUC__\ninline\n#endif\n"); + + /* Use ANSI function prototypes. */ + printf (OPTION_ENABLED (option, ANSI) + ? "%s%s\n%s (register const char *str, register int len)\n{\n" + : "%s%s\n%s (str, len)\n register char *str;\n register unsigned int len;\n{\n", + OPTION_ENABLED (option, CONST) ? "const " : "", + key_list.return_type, GET_FUNCTION_NAME (option)); + + /* Use the switch in place of lookup table. */ + if (OPTION_ENABLED (option, SWITCH)) + { + if (!global_table) + { + if (OPTION_ENABLED (option, LENTABLE) && OPTION_ENABLED (option, DUP)) + print_keylength_table (); + if (OPTION_ENABLED (option, POINTER) && OPTION_ENABLED (option, TYPE)) + print_keyword_table (); + } + print_switch (); + } + else /* Use the lookup table, in place of switch. */ + { + if (!global_table) + { + if (OPTION_ENABLED (option, LENTABLE)) + print_keylength_table (); + print_keyword_table (); + } + print_lookup_function (); + } + + if (key_list.additional_code) + { + int c; + + while ((c = getchar ()) != EOF) + putchar (c); + } + fflush (stdout); +} + +/* Sorts the keys by hash value. */ + +void +sort () +{ + key_list.hash_sort = TRUE; + key_list.occurrence_sort = FALSE; + + key_list.head = merge_sort (key_list.head); +} + +/* Dumps the key list to stderr stream. */ + +static void +dump () +{ + LIST_NODE *ptr; + + fprintf (stderr, "\nList contents are:\n(hash value, key length, index, key set, key):\n"); + + for (ptr = key_list.head; ptr; ptr = ptr->next) + fprintf (stderr, "%7d,%7d,%6d, %s, %s\n", + ptr->hash_value, ptr->length, ptr->index, + ptr->char_set, ptr->key); +} + +/* Simple-minded constructor action here... */ + +void +key_list_init () +{ + key_list.total_keys = 1; + key_list.max_key_len = NEG_MAX_INT; + key_list.min_key_len = MAX_INT; + key_list.return_type = default_return_type; + key_list.array_type = key_list.struct_tag = default_array_type; + key_list.head = NULL; + key_list.additional_code = FALSE; +} + +/* Returns the length of entire key list. */ + +int +keyword_list_length () +{ + return key_list.list_len; +} + +/* Returns length of longest key read. */ + +int +max_key_length () +{ + return key_list.max_key_len; +} + +/* DESTRUCTOR dumps diagnostics during debugging. */ + +void +key_list_destroy () +{ + if (OPTION_ENABLED (option, DEBUG)) + { + fprintf (stderr, "\nDumping key list information:\ntotal unique keywords = %d\ +\ntotal keywords = %d\nmaximum key length = %d.\n", + key_list.list_len, key_list.total_keys, key_list.max_key_len); + dump (); + fprintf (stderr, "End dumping list.\n\n"); + } +} + diff --git a/src/keylist.h b/src/keylist.h new file mode 100644 index 000000000000..38143b73cb14 --- /dev/null +++ b/src/keylist.h @@ -0,0 +1,54 @@ +/* Data and function member declarations for the keyword list class. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* The key word list is a useful abstraction that keeps track of + various pieces of information that enable that fast generation + of the Perfect.hash function. A Key_List is a singly-linked + list of List_Nodes. */ + +#ifndef _keylist_h +#define _keylist_h +#include <stdio.h> +#include "listnode.h" + +typedef struct key_list +{ + LIST_NODE *head; /* Points to the head of the linked list. */ + char *array_type; /* Pointer to the type for word list. */ + char *return_type; /* Pointer to return type for lookup function. */ + char *struct_tag; /* Shorthand for user-defined struct tag type. */ + char *include_src; /* C source code to be included verbatim. */ + int list_len; /* Length of head's Key_List, not counting duplicates. */ + int total_keys; /* Total number of keys, counting duplicates. */ + int max_key_len; /* Maximum length of the longest keyword. */ + int min_key_len; /* Minimum length of the shortest keyword. */ + bool occurrence_sort; /* True if sorting by occurrence. */ + bool hash_sort; /* True if sorting by hash value. */ + bool additional_code; /* True if any additional C code is included. */ +} KEY_LIST; + +extern void key_list_init P ((void)); +extern void key_list_destroy P ((void)); +extern void print_output P ((void)); +extern int keyword_list_length P ((void)); +extern int max_key_length P ((void)); +extern KEY_LIST key_list; +#endif /* _keylist_h */ diff --git a/src/list-node.cc b/src/list-node.cc new file mode 100644 index 000000000000..57a04a071fe4 --- /dev/null +++ b/src/list-node.cc @@ -0,0 +1,102 @@ +/* Creates and initializes a new list node. + Copyright (C) 1989-1998, 2000 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "list-node.h" + +#include <stdio.h> +#include <stdlib.h> /* declares exit() */ +#include "options.h" +#include "trace.h" + +/* Sorts the key set alphabetically to speed up subsequent operations. + Uses insertion sort since the set is probably quite small. */ + +inline void +List_Node::set_sort (char *base, int len) +{ + T (Trace t ("List_Node::set_sort");) + int i, j; + + for (i = 0, j = len - 1; i < j; i++) + { + char curr, tmp; + + for (curr = i + 1, tmp = base[curr]; curr > 0 && tmp < base[curr-1]; curr--) + base[curr] = base[curr - 1]; + + base[curr] = tmp; + + } +} + +/* Initializes a List_Node. This requires obtaining memory for the CHAR_SET + initializing them using the information stored in the KEY_POSITIONS array in Options, + and checking for simple errors. It's important to note that KEY and REST are + both pointers to the different offsets into the same block of dynamic memory pointed + to by parameter K. The data member REST is used to store any additional fields + of the input file (it is set to the "" string if Option[TYPE] is not enabled). + This is useful if the user wishes to incorporate a lookup structure, + rather than just an array of keys. Finally, KEY_NUMBER contains a count + of the total number of keys seen so far. This is used to initialize + the INDEX field to some useful value. */ + +List_Node::List_Node (const char *k, int len, const char *r): + link (0), next (0), key (k), key_length (len), rest (r), index (0) +{ + T (Trace t ("List_Node::List_Node");) + char *key_set = new char[(option[ALLCHARS] ? len : option.get_max_keysig_size ())]; + char *ptr = key_set; + int i; + + if (option[ALLCHARS]) /* Use all the character positions in the KEY. */ + for (i = len; i > 0; k++, ptr++, i--) + ++occurrences[(unsigned char)(*ptr = *k)]; + else /* Only use those character positions specified by the user. */ + { + /* Iterate through the list of key_positions, initializing occurrences table + and char_set (via char * pointer ptr). */ + + for (option.reset (); (i = option.get ()) != EOS; ) + { + if (i == WORD_END) /* Special notation for last KEY position, i.e. '$'. */ + *ptr = key[len - 1]; + else if (i <= len) /* Within range of KEY length, so we'll keep it. */ + *ptr = key[i - 1]; + else /* Out of range of KEY length, so we'll just skip it. */ + continue; + ++occurrences[(unsigned char)(*ptr++)]; + } + + /* Didn't get any hits and user doesn't want to consider the + keylength, so there are essentially no usable hash positions! */ + if (ptr == char_set && option[NOLENGTH]) + { + fprintf (stderr, "Can't hash keyword %.*s with chosen key positions.\n", + key_length, key); + exit (1); + } + } + + /* Sort the KEY_SET items alphabetically. */ + set_sort (key_set, ptr - key_set); + + char_set = key_set; + char_set_length = ptr - key_set; +} diff --git a/src/list-node.h b/src/list-node.h new file mode 100644 index 000000000000..3bd21b3359df --- /dev/null +++ b/src/list-node.h @@ -0,0 +1,46 @@ +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Data and function members for defining values and operations of a list node. + + Copyright (C) 1989-1998, 2000 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef list_node_h +#define list_node_h 1 + +#include "vectors.h" + +struct List_Node : private Vectors +{ + List_Node *link; /* TRUE if key has an identical KEY_SET as another key. */ + List_Node *next; /* Points to next element on the list. */ + const char *key; /* Each keyword string stored here. */ + int key_length; /* Length of the key. */ + const char *rest; /* Additional information for building hash function. */ + const char *char_set; /* Set of characters to hash, specified by user. */ + int char_set_length; /* Length of char_set. */ + int hash_value; /* Hash value for the key. */ + int occurrence; /* A metric for frequency of key set occurrences. */ + int index; /* Position of this node relative to other nodes. */ + + List_Node (const char *key, int len, const char *rest); + static void set_sort (char *base, int len); +}; + +#endif diff --git a/src/listnode.c b/src/listnode.c new file mode 100644 index 000000000000..2eec1a6f74f8 --- /dev/null +++ b/src/listnode.c @@ -0,0 +1,111 @@ +/* Creates and initializes a new list node. + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include "options.h" +#include "listnode.h" +#include "stderr.h" + +/* See comments in perfect.cc. */ +extern int occurrences[ALPHABET_SIZE]; + +/* Sorts the key set alphabetically to speed up subsequent operations. + Uses insertion sort since the set is probably quite small. */ + +static void +set_sort (base, len) + char *base; + int len; +{ + int i, j; + + for (i = 0, j = len - 1; i < j; i++) + { + char curr, tmp; + + for (curr = i + 1, tmp = base[curr]; curr > 0 && tmp < base[curr-1]; curr--) + base[curr] = base[curr - 1]; + + base[curr] = tmp; + + } +} + +/* Initializes a List_Node. This requires obtaining memory for the KEY_SET + initializing them using the information stored in the + KEY_POSITIONS array in Options, and checking for simple errors. + It's important to note that KEY and REST are both pointers to + the different offsets into the same block of dynamic memory pointed to + by parameter K. The data member REST is used to store any additional fields + of the input file (it is set to the "" string if Option[TYPE] is not enabled). + This is useful if the user wishes to incorporate a lookup structure, + rather than just an array of keys. */ + +LIST_NODE * +make_list_node (k, len) + char *k; + int len; +{ + LIST_NODE *buffered_malloc (); + int char_set_size = OPTION_ENABLED (option, ALLCHARS) ? len : GET_CHARSET_SIZE (option) + 1; + LIST_NODE *temp = buffered_malloc (sizeof (LIST_NODE) + char_set_size); + char *ptr = temp->char_set; + + k[len] = '\0'; /* Null terminate KEY to separate it from REST. */ + temp->key = k; + temp->next = 0; + temp->index = 0; + temp->length = len; + temp->link = 0; + temp->rest = OPTION_ENABLED (option, TYPE) ? k + len + 1 : ""; + + if (OPTION_ENABLED (option, ALLCHARS)) /* Use all the character position in the KEY. */ + + for (; *k; k++, ptr++) + ++occurrences[*ptr = *k]; + + else /* Only use those character positions specified by the user. */ + { + int i; + + /* Iterate thru the list of key_positions, initializing occurrences table + and temp->char_set (via char * pointer ptr). */ + + for(RESET (option); (i = GET (option)) != EOS; ) + { + if (i == WORD_END) /* Special notation for last KEY position, i.e. '$'. */ + *ptr = temp->key[len - 1]; + else if (i <= len) /* Within range of KEY length, so we'll keep it. */ + *ptr = temp->key[i - 1]; + else /* Out of range of KEY length, so we'll just skip it. */ + continue; + ++occurrences[*ptr++]; + } + + if (ptr == temp->char_set) /* Didn't get any hits, i.e., no usable positions. */ + report_error ("can't hash keyword %s with chosen key positions\n%a", temp->key); + } + + *ptr = '\0'; /* Terminate this bastard.... */ + /* Sort the KEY_SET items alphabetically. */ + set_sort (temp->char_set, ptr - temp->char_set); + + return temp; +} diff --git a/src/listnode.h b/src/listnode.h new file mode 100644 index 000000000000..3e64709f5c10 --- /dev/null +++ b/src/listnode.h @@ -0,0 +1,43 @@ +/* Data and function members for defining values and operations of a list node. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _listnode_h +#define _listnode_h +#include "prototype.h" + +#define ALPHABET_SIZE 128 + +typedef struct list_node +{ + struct list_node *link; /* TRUE if key has an identical KEY_SET as another key. */ + struct list_node *next; /* Points to next element on the list. */ + int length; /* Length of the key. */ + int hash_value; /* Hash value for the key. */ + int occurrence; /* A metric for frequency of key set occurrences. */ + int index; /* Position of this node relative to other nodes. */ + char *key; /* Key string. */ + char *rest; /* Additional information for building hash function. */ + char char_set[1]; /* Set of characters to hash, specified by user. */ +} LIST_NODE; + +extern LIST_NODE *make_list_node P ((char *k, int len)); + +#endif _listnode_h diff --git a/src/main.c b/src/main.c new file mode 100644 index 000000000000..a54c1dfc5069 --- /dev/null +++ b/src/main.c @@ -0,0 +1,96 @@ +/* Driver program for the Perfect hash function generator. + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Simple driver program for the Perfect.hash function generator. + Most of the hard work is done in class Perfect and its class methods. */ + +#include <stdio.h> +#include <sys/types.h> +#include <time.h> +#include "stderr.h" +#include "options.h" +#include "perfect.h" + +/* Calls the appropriate intialization routines for each + ADT. Note that certain initialization routines require + initialization *after* certain values are computed. Therefore, + they cannot be called here. */ + +static void +init_all (argc, argv) + int argc; + char *argv[]; +{ +#ifdef RLIMIT_STACK + /* Get rid of any avoidable limit on stack size. */ + { + struct rlimit rlim; + + /* Set the stack limit huge so that alloca does not fail. */ + getrlimit (RLIMIT_STACK, &rlim); + rlim.rlim_cur = rlim.rlim_max; + setrlimit (RLIMIT_STACK, &rlim); + } +#endif /* RLIMIT_STACK */ + + options_init (argc, argv); + key_list_init (); + perfect_init (); +} + +/* Calls appropriate destruction routines for each ADT. These + routines print diagnostics if the debugging option is enabled. */ + +static void +destroy_all () +{ + options_destroy (); + key_list_destroy (); + perfect_destroy (); +} + +/* Driver for perfect hash function generation. */ + +int +main (argc, argv) + int argc; + char *argv[]; +{ + struct tm *tm; + time_t clock; + int status; + + time (&clock); + tm = localtime (&clock); + + fprintf (stderr, "/* starting time is %d:%d:%d */\n", tm->tm_hour, tm->tm_min, tm->tm_sec); + /* Sets the options. */ + init_all (argc, argv); + + /* Generates the perfect hash table. + Also prints generated code neatly to the output. */ + status = perfect_generate (); + destroy_all (); + + time (&clock); + tm = localtime (&clock); + fprintf (stderr, "/* ending time is %d:%d:%d */\n", tm->tm_hour, tm->tm_min, tm->tm_sec); + return status; +} diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 000000000000..03b6c7ea242b --- /dev/null +++ b/src/main.cc @@ -0,0 +1,76 @@ +/* Driver program for the Gen_Perf hash function generator + Copyright (C) 1989-1998, 2000 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +/* Simple driver program for the Gen_Perf.hash function generator. + Most of the hard work is done in class Gen_Perf and its class methods. */ + +#include "config.h" +#include <sys/types.h> +#if LARGE_STACK_ARRAYS && defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef HAVE_SYS_RESOURCE_H +#include <sys/resource.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#endif + +#include <stdio.h> +#include "options.h" +#include "gen-perf.h" +#include "trace.h" + +int +main (int argc, char *argv[]) +{ + T (Trace t ("main");) + +#if LARGE_STACK_ARRAYS && defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) && defined(RLIMIT_STACK) + /* Get rid of any avoidable limit on stack size. */ + { + struct rlimit rlim; + if (getrlimit (RLIMIT_STACK, &rlim) == 0) + if (rlim.rlim_cur < rlim.rlim_max) + { + rlim.rlim_cur = rlim.rlim_max; + setrlimit (RLIMIT_STACK, &rlim); + } + } +#endif /* RLIMIT_STACK */ + + /* Sets the Options. */ + option (argc, argv); + + /* Initializes the key word list. */ + Gen_Perf generate_table; + + /* Generates and prints the Gen_Perf hash table. */ + int status = generate_table (); + + /* Check for write error on stdout. */ + if (fflush (stdout) || ferror (stdout)) + status = 1; + + /* Don't use exit() here, it skips the destructors. */ + return status; +} diff --git a/src/new.cc b/src/new.cc new file mode 100644 index 000000000000..8c6728ec4b2a --- /dev/null +++ b/src/new.cc @@ -0,0 +1,87 @@ +/* Defines a buffered memory allocation abstraction that reduces calls to + malloc. + Copyright (C) 1989-1998 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> /* declares malloc(), exit() */ +#include "trace.h" + +/* Determine default alignment. If your C++ compiler does not + like this then try something like #define DEFAULT_ALIGNMENT 8. */ +struct fooalign {char x; double d;}; +const int ALIGNMENT = ((char *)&((struct fooalign *) 0)->d - (char *)0); + +/* Provide an abstraction that cuts down on the number of + calls to NEW by buffering the memory pool from which + strings are allocated. */ + +void * +operator new (size_t size) +{ + T (Trace t ("operator new");) + static char *buf_start = 0; /* Large array used to reduce calls to NEW. */ + static char *buf_end = 0; /* Indicates end of BUF_START. */ + static size_t buf_size = 4096; /* Size of buffer pointed to by BUF_START. */ + char *temp; + + /* Align this on correct boundaries, just to be safe... */ + size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT; + + /* If we are about to overflow our buffer we'll just grab another + chunk of memory. Since we never free the original memory it + doesn't matter that no one points to the beginning of that + chunk. Note we use a heuristic that grows the buffer either by + size of the request or by twice the previous size, whichever is + larger. */ + + if (buf_start + size >= buf_end) + { + buf_size *= 2; + if (buf_size < size) + buf_size = size; + if ((buf_start = (char *)malloc (buf_size)) != (char *)0) + buf_end = buf_start + buf_size; + else + { + fprintf (stderr, "Virtual memory exhausted in `operator new'\n"); + exit (1); + } + } + + temp = buf_start; + buf_start += size; + return temp; +} + +/* We need this deletion operator in order to make the linker happy. + Because `operator new' and `operator delete' always come together. */ + +void +operator delete (void *ptr) +#ifdef HAVE_THROW_DECL + throw() +#endif +{ + T (Trace t ("operator delete");) + // We cannot call free here, as it doesn't match the mallocs. + // free ((char *) ptr); + (void) ptr; +} diff --git a/src/options.c b/src/options.c new file mode 100644 index 000000000000..40fdf0a6588e --- /dev/null +++ b/src/options.c @@ -0,0 +1,444 @@ +/* Handles parsing the Options provided to the user. + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include <assert.h> +#include "options.h" +#include "iterator.h" +#include "stderr.h" + +/* Current program version. */ +extern char *version_string; + +/* Size to jump on a collision. */ +#define DEFAULT_JUMP_VALUE 5 + +/* Default name for generated lookup function. */ +#define DEFAULT_NAME "in_word_set" + +/* Default name for the key component. */ +#define DEFAULT_KEY "name" + +/* Default name for generated hash function. */ +#define DEFAULT_HASH_NAME "hash" + +/* Globally visible OPTIONS object. */ +OPTIONS option; + +/* Default delimiters that separate keywords from their attributes. */ +#define DEFAULT_DELIMITERS ",\n" + +/* Prints program usage to standard error stream. */ + +void +usage () +{ + report_error ("usage: %n [-acCdDef[num]gGhH<hashname>i<init>jk<keys>\ +K<keyname>lnN<name>oprs<size>S<switches>tTv].\n(type %n -h for help)\n"); +} + +/* Sorts the key positions *IN REVERSE ORDER!!* + This makes further routines more efficient. Especially when generating code. + Uses a simple Insertion Sort since the set is probably ordered. + Returns 1 if there are no duplicates, 0 otherwise. */ + +static int +key_sort (base, len) + char *base; + int len; +{ + int i, j; + + for (i = 0, j = len - 1; i < j; i++) + { + int curr, tmp; + + for (curr = i + 1,tmp = base[curr]; curr > 0 && tmp >= base[curr - 1]; curr--) + if ((base[curr] = base[curr - 1]) == tmp) /* oh no, a duplicate!!! */ + return 0; + + base[curr] = tmp; + } + + return 1; +} + +/* Dumps option status when debug is set. */ + +void +options_destroy () +{ + if (OPTION_ENABLED (option, DEBUG)) + { + char *ptr; + + fprintf (stderr, "\ndumping Options:\nDEBUG is.......: %s\nORDER is.......: %s\ +\nANSI is........: %s\nTYPE is........: %s\nGNU is.........: %s\nRANDOM is......: %s\ +\nDEFAULTCHARS is: %s\nSWITCH is......: %s\nPOINTER is.....: %s\nNOLENGTH is....: %s\ +\nLENTABLE is....: %s\nDUP is.........: %s\nCOMP is........: %s\nFAST is........: %s\ +\nNOTYPE is......: %s\nGLOBAL is......: %s\nCONST is.......: %s\niterations = %d\ +\nlookup function name = %s\nhash function name = %s\nkey name = %s\ +\njump value = %d\nmax associcated value = %d\ninitial associated value = %d\ +\ndelimiters = %s\nnumber of switch statements = %d\napproximate switch statement size = %d\n", + OPTION_ENABLED (option, DEBUG) ? "enabled" : "disabled", + OPTION_ENABLED (option, ORDER) ? "enabled" : "disabled", + OPTION_ENABLED (option, ANSI) ? "enabled" : "disabled", + OPTION_ENABLED (option, TYPE) ? "enabled" : "disabled", + OPTION_ENABLED (option, GNU) ? "enabled" : "disabled", + OPTION_ENABLED (option, RANDOM) ? "enabled" : "disabled", + OPTION_ENABLED (option, DEFAULTCHARS) ? "enabled" : "disabled", + OPTION_ENABLED (option, SWITCH) ? "enabled" : "disabled", + OPTION_ENABLED (option, POINTER) ? "enabled" : "disabled", + OPTION_ENABLED (option, NOLENGTH) ? "enabled" : "disabled", + OPTION_ENABLED (option, LENTABLE) ? "enabled" : "disabled", + OPTION_ENABLED (option, DUP) ? "enabled" : "disabled", + OPTION_ENABLED (option, COMP) ? "enabled" : "disabled", + OPTION_ENABLED (option, FAST) ? "enabled" : "disabled", + OPTION_ENABLED (option, NOTYPE) ? "enabled" : "disabled", + OPTION_ENABLED (option, GLOBAL) ? "enabled" : "disabled", + OPTION_ENABLED (option, CONST) ? "enabled" : "disabled", + option.iterations, option.function_name, option.hash_name, + option.key_name, option.jump, option.size - 1, + option.initial_asso_value, option.delimiters, option.total_switches, + keyword_list_length () / option.total_switches); + + if (OPTION_ENABLED (option, ALLCHARS)) + fprintf (stderr, "all characters are used in the hash function\n"); + fprintf (stderr, "maximum charset size = %d\nkey positions are: \n", + option.total_charset_size); + + for (ptr = option.key_positions; *ptr != EOS; ptr++) + if (*ptr == WORD_END) + fprintf (stderr, "$\n"); + else + fprintf (stderr, "%d\n", *ptr); + + fprintf (stderr, "finished dumping Options\n"); + } +} + +/* Parses the command line Options and sets appropriate flags in option.option_word. */ + +void +options_init (argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + extern char *optarg; + int option_char; + + option.key_positions[0] = WORD_START; + option.key_positions[1] = WORD_END; + option.key_positions[2] = EOS; + option.total_charset_size = 2; + option.jump = DEFAULT_JUMP_VALUE; + option.option_word = (int) DEFAULTCHARS; + option.function_name = DEFAULT_NAME; + option.hash_name = DEFAULT_HASH_NAME; + option.key_name = DEFAULT_KEY; + option.delimiters = DEFAULT_DELIMITERS; + option.initial_asso_value = option.size = option.iterations = 0; + option.total_switches = 1; + option.argument_count = argc; + option.argument_vector = argv; + set_program_name (argv[0]); + + while ((option_char = getopt (argc, argv, "adcCDe:f:gGhH:i:j:k:K:lnN:oprs:S:tTv")) != EOF) + { + switch (option_char) + { + case 'a': /* Generated coded uses the ANSI prototype format. */ + { + SET_OPTION (option, ANSI); + break; + } + case 'c': /* Generate strncmp rather than strcmp. */ + { + SET_OPTION (option, COMP); + break; + } + case 'C': /* Make the generated tables readonly (const). */ + { + SET_OPTION (option, CONST); + break; + } + case 'd': /* Enable debugging option. */ + { + SET_OPTION (option, DEBUG); + report_error ("starting program %n, version %s, with debuggin on.\n", + version_string); + break; + } + case 'D': /* Enable duplicate option. */ + { + SET_OPTION (option, DUP); + break; + } + case 'e': /* Allows user to provide keyword/attribute separator */ + { + SET_DELIMITERS (option, optarg); + break; + } + case 'f': /* Generate the hash table ``fast.'' */ + { + SET_OPTION (option, FAST); + if ((option.iterations = atoi (optarg)) < 0) + { + report_error ("iterations value must not be negative, assuming 0\n"); + option.iterations = 0; + } + break; + } + case 'g': /* Use the ``inline'' keyword for generated sub-routines. */ + { + SET_OPTION (option, GNU); + break; + } + case 'G': /* Make the keyword table a global variable. */ + { + SET_OPTION (option, GLOBAL); + break; + } + case 'h': /* Displays a list of helpful Options to the user. */ + { + report_error ( +"-a\tGenerate ANSI standard C output code, i.e., function prototypes.\n\ +-c\tGenerate comparison code using strncmp rather than strcmp.\n\ +-C\tMake the contents of generated lookup tables constant, i.e., readonly.\n\ +-d\tEnables the debugging option (produces verbose output to Std_Err).\n\ +-D\tHandle keywords that hash to duplicate values. This is useful\n\ +\tfor certain highly redundant keyword sets. It enables the -S option.\n\ +-e\tAllow user to provide a string containing delimiters used to separate\n\ +\tkeywords from their attributes. Default is \",\\n\"\n\ +-f\tGenerate the perfect hash function ``fast.'' This decreases GPERF's\n\ +\trunning time at the cost of minimizing generated table-size.\n\ +\tThe numeric argument represents the number of times to iterate when\n\ +\tresolving a collision. `0' means ``iterate by the number of keywords''.\n\ +-g\tAssume a GNU compiler, e.g., g++ or gcc. This makes all generated\n\ +\troutines use the ``inline'' keyword to remove cost of function calls.\n\ +-G\tGenerate the static table of keywords as a static global variable,\n\ +\trather than hiding it inside of the lookup function (which is the\n\ +\tdefault behavior).\n\ +-h\tPrints this mesage.\n"); + report_error ( +"-H\tAllow user to specify name of generated hash function. Default is `hash'.\n\ +-i\tProvide an initial value for the associate values array. Default is 0.\n\ +\tSetting this value larger helps inflate the size of the final table.\n\ +-j\tAffects the ``jump value,'' i.e., how far to advance the associated\n\ +\tcharacter value upon collisions. Must be an odd number, default is %d.\n\ +-k\tAllows selection of the key positions used in the hash function.\n\ +\tThe allowable choices range between 1-%d, inclusive. The positions\n\ +\tare separated by commas, ranges may be used, and key positions may\n\ +\toccur in any order. Also, the meta-character '*' causes the generated\n\ +\thash function to consider ALL key positions, and $ indicates the\n\ +\t``final character'' of a key, e.g., $,1,2,4,6-10.\n\ +-K\tAllow user to select name of the keyword component in the keyword structure.\n\ +-l\tCompare key lengths before trying a string comparison. This helps\n\ +\tcut down on the number of string comparisons made during the lookup.\n\ +-n\tDo not include the length of the keyword when computing the hash function\n\ +-N\tAllow user to specify name of generated lookup function. Default\n\ +\tname is `in_word_set.'\n\ +-o\tReorders input keys by frequency of occurrence of the key sets.\n\ +\tThis should decrease the search time dramatically.\n\ +-p\tChanges the return value of the generated function ``in_word_set''\n\ +\tfrom its default boolean value (i.e., 0 or 1), to type ``pointer\n\ +\tto wordlist array'' This is most useful when the -t option, allowing\n\ +\tuser-defined structs, is used.\n", + DEFAULT_JUMP_VALUE, MAX_KEY_POS - 1); + report_error ( +"-r\tUtilizes randomness to initialize the associated values table.\n\ +-s\tAffects the size of the generated hash table. The numeric argument\n\ +\tfor this option indicates ``how many times larger'' the table range\n\ +\tshould be, in relationship to the number of keys, e.g. a value of 3\n\ +\tmeans ``make the table about 3 times larger than the number of input\n\ +\tkeys.'' A larger table should decrease the time required for an\n\ +\tunsuccessful search, at the expense of extra table space. Default\n\ +\tvalue is 1. This actual table size may vary somewhat.\n\ +-S\tCauses the generated C code to use a switch statement scheme, rather\n\ +\tthan an array lookup table. This can lead to a reduction in both\n\ +\ttime and space requirements for some keyfiles. The argument to\n\ +\tthis option determines how many switch statements are generated.\n\ +\tA value of 1 generates 1 switch containing all the elements, a value of 2\n\ +\tgenerates 2 tables with 1/2 the elements in each table, etc. This\n\ +\tis useful since many C compilers cannot correctly generate code for\n\ +\tlarge switch statements.\n\ +\tthe expense of longer time for each lookup. Mostly important for\n\ +\t*large* input sets, i.e., greater than around 100 items or so.\n\ +-t\tAllows the user to include a structured type declaration for \n\ +\tgenerated code. Any text before %%%% is consider part of the type\n\ +\tdeclaration. Key words and additional fields may follow this, one\n\ +\tgroup of fields per line.\n\ +-T\tPrevents the transfer of the type declaration to the output file.\n\ +\tUse this option if the type is already defined elsewhere.\n\ +-v\tPrints out the current version number\n%e%a\n", + usage); + } + case 'H': /* Sets the name for the hash function */ + { + option.hash_name = optarg; + break; + } + case 'i': /* Sets the initial value for the associated values array. */ + { + if ((option.initial_asso_value = atoi (optarg)) < 0) + report_error ("initial value %d must be non-zero, ignoring and continuing\n", + option.initial_asso_value); + if (OPTION_ENABLED (option, RANDOM)) + report_error ("warning, -r option superceeds -i, ignoring -i option and continuing\n"); + break; + } + case 'j': /* Sets the jump value, must be odd for later algorithms. */ + { + if ((option.jump = atoi (optarg)) < 0) + report_error ("jump value %d must be a positive number\n%e%a", + option.jump, usage); + else if (option.jump && EVEN (option.jump)) + report_error ("jump value %d should be odd, adding 1 and continuing...\n", + option.jump++); + break; + } + case 'k': /* Sets key positions used for hash function. */ + { + int BAD_VALUE = -1; + int value; + + iterator_init (optarg, 1, MAX_KEY_POS - 1, WORD_END, BAD_VALUE, EOS); + + if (*optarg == '*') /* Use all the characters for hashing!!!! */ + { + UNSET_OPTION (option, DEFAULTCHARS); + SET_OPTION (option, ALLCHARS); + } + else + { + char *key_pos; + + for (key_pos = option.key_positions; (value = next ()) != EOS; key_pos++) + if (value == BAD_VALUE) + report_error ("illegal key value or range, use 1,2,3-%d,'$' or '*'.\n%e%a", + (MAX_KEY_POS - 1),usage); + else + *key_pos = value;; + + *key_pos = EOS; + + if (! (option.total_charset_size = (key_pos - option.key_positions))) + report_error ("no keys selected\n%e%a", usage); + else if (! key_sort (option.key_positions, option.total_charset_size)) + report_error ("duplicate keys selected\n%e%a", usage); + + if (option.total_charset_size != 2 + || (option.key_positions[0] != 1 || option.key_positions[1] != WORD_END)) + UNSET_OPTION (option, DEFAULTCHARS); + } + break; + } + case 'K': /* Make this the keyname for the keyword component field. */ + { + option.key_name = optarg; + break; + } + case 'l': /* Create length table to avoid extra string compares. */ + { + SET_OPTION (option, LENTABLE); + break; + } + case 'n': /* Don't include the length when computing hash function. */ + { + SET_OPTION (option, NOLENGTH); + break; + } + case 'N': /* Make generated lookup function name be optarg */ + { + option.function_name = optarg; + break; + } + case 'o': /* Order input by frequency of key set occurrence. */ + { + SET_OPTION (option, ORDER); + break; + } + case 'p': /* Generated lookup function now a pointer instead of int. */ + { + SET_OPTION (option, POINTER); + break; + } + case 'r': /* Utilize randomness to initialize the associated values table. */ + { + SET_OPTION (option, RANDOM); + if (option.initial_asso_value != 0) + report_error ("warning, -r option superceeds -i, disabling -i option and continuing\n"); + break; + } + case 's': /* Range of associated values, determines size of final table. */ + { + if ((option.size = atoi (optarg)) <= 0) + report_error ("improper range argument %s\n%e%a", optarg, usage); + else if (option.size > 50) + report_error ("%d is excessive, did you really mean this?! (type %n -h for help)\n", + option.size); + break; + } + case 'S': /* Generate switch statement output, rather than lookup table. */ + { + SET_OPTION (option, SWITCH); + if ((option.total_switches = atoi (optarg)) <= 0) + report_error ("number of switches %s must be a positive number\n%e%a", optarg, usage); + break; + } + case 't': /* Enable the TYPE mode, allowing arbitrary user structures. */ + { + SET_OPTION (option, TYPE); + break; + } + case 'T': /* Don't print structure definition. */ + { + SET_OPTION (option, NOTYPE); + break; + } + case 'v': /* Print out the version and quit. */ + report_error ("%n: version %s\n%e%a\n", version_string, usage); + default: + report_error ("%e%a", usage); + } + } + + if (argv[optind] && ! freopen (argv[optind], "r", stdin)) + report_error ("unable to read key word file %s\n%e%a", argv[optind], usage); + + if (++optind < argc) + report_error ("extra trailing arguments to %n\n%e%a", usage); +} + +/* Output command-line Options. */ +void +print_options () +{ + int i; + + printf ("/* Command-line: "); + + for (i = 0; i < option.argument_count; i++) + printf ("%s ", option.argument_vector[i]); + + printf (" */\n\n"); +} + diff --git a/src/options.cc b/src/options.cc new file mode 100644 index 000000000000..d15e21cee8ce --- /dev/null +++ b/src/options.cc @@ -0,0 +1,727 @@ +/* Handles parsing the Options provided to the user. + Copyright (C) 1989-1998, 2000 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <stdio.h> +#include <stdlib.h> /* declares atoi(), abs(), exit() */ +#include <string.h> /* declares strcmp() */ +#include "getopt.h" +#include "options.h" +#include "iterator.h" +#include "trace.h" +#include "vectors.h" +#include "version.h" + +/* Global option coordinator for the entire program. */ +Options option; + +/* Records the program name. */ +const char *program_name; + +/* Size to jump on a collision. */ +static const int DEFAULT_JUMP_VALUE = 5; + +/* Default name for generated lookup function. */ +static const char *const DEFAULT_NAME = "in_word_set"; + +/* Default name for the key component. */ +static const char *const DEFAULT_KEY = "name"; + +/* Default struct initializer suffix. */ +static const char *const DEFAULT_INITIALIZER_SUFFIX = ""; + +/* Default name for the generated class. */ +static const char *const DEFAULT_CLASS_NAME = "Perfect_Hash"; + +/* Default name for generated hash function. */ +static const char *const DEFAULT_HASH_NAME = "hash"; + +/* Default name for generated hash table array. */ +static const char *const DEFAULT_WORDLIST_NAME = "wordlist"; + +/* Default delimiters that separate keywords from their attributes. */ +static const char *const DEFAULT_DELIMITERS = ",\n"; + +int Options::option_word; +int Options::total_switches; +int Options::total_keysig_size; +int Options::size; +int Options::key_pos; +int Options::jump; +int Options::initial_asso_value; +int Options::argument_count; +int Options::iterations; +char **Options::argument_vector; +const char *Options::function_name; +const char *Options::key_name; +const char *Options::initializer_suffix; +const char *Options::class_name; +const char *Options::hash_name; +const char *Options::wordlist_name; +const char *Options::delimiters; +char Options::key_positions[MAX_KEY_POS]; + +/* Prints program usage to given stream. */ + +void +Options::short_usage (FILE * strm) +{ + T (Trace t ("Options::short_usage");) + fprintf (strm, "Usage: %s [-cCdDef[num]F<initializers>GhH<hashname>i<init>Ijk<keys>K<keyname>lL<language>nN<function name>ors<size>S<switches>tTvW<wordlistname>Z<class name>7] [input-file]\n" + "Try `%s --help' for more information.\n", + program_name, program_name); +} + +void +Options::long_usage (FILE * strm) +{ + T (Trace t ("Options::long_usage");) + fprintf (strm, + "GNU `gperf' generates perfect hash functions.\n" + "\n" + "Usage: %s [OPTION]... [INPUT-FILE]\n" + "\n" + "If a long option shows an argument as mandatory, then it is mandatory\n" + "for the equivalent short option also.\n" + "\n" + "Input file interpretation:\n" + " -e, --delimiters=DELIMITER-LIST\n" + " Allow user to provide a string containing delimiters\n" + " used to separate keywords from their attributes.\n" + " Default is \",\\n\".\n" + " -t, --struct-type Allows the user to include a structured type\n" + " declaration for generated code. Any text before %%%%\n" + " is considered part of the type declaration. Key\n" + " words and additional fields may follow this, one\n" + " group of fields per line.\n" + "\n" + "Language for the output code:\n" + " -L, --language=LANGUAGE-NAME\n" + " Generates code in the specified language. Languages\n" + " handled are currently C++, ANSI-C, C, and KR-C. The\n" + " default is C.\n" + "\n" + "Details in the output code:\n" + " -K, --slot-name=NAME Select name of the keyword component in the keyword\n" + " structure.\n" + " -F, --initializer-suffix=INITIALIZERS\n" + " Initializers for additional components in the keyword\n" + " structure.\n" + " -H, --hash-fn-name=NAME\n" + " Specify name of generated hash function. Default is\n" + " `hash'.\n" + " -N, --lookup-fn-name=NAME\n" + " Specify name of generated lookup function. Default\n" + " name is `in_word_set'.\n" + " -Z, --class-name=NAME Specify name of generated C++ class. Default name is\n" + " `Perfect_Hash'.\n" + " -7, --seven-bit Assume 7-bit characters.\n" + " -c, --compare-strncmp Generate comparison code using strncmp rather than\n" + " strcmp.\n" + " -C, --readonly-tables Make the contents of generated lookup tables\n" + " constant, i.e., readonly.\n" + " -E, --enum Define constant values using an enum local to the\n" + " lookup function rather than with defines.\n" + " -I, --includes Include the necessary system include file <string.h>\n" + " at the beginning of the code.\n" + " -G, --global Generate the static table of keywords as a static\n" + " global variable, rather than hiding it inside of the\n" + " lookup function (which is the default behavior).\n" + " -W, --word-array-name=NAME\n" + " Specify name of word list array. Default name is\n" + " `wordlist'.\n" + " -S, --switch=COUNT Causes the generated C code to use a switch\n" + " statement scheme, rather than an array lookup table.\n" + " This can lead to a reduction in both time and space\n" + " requirements for some keyfiles. The COUNT argument\n" + " determines how many switch statements are generated.\n" + " A value of 1 generates 1 switch containing all the\n" + " elements, a value of 2 generates 2 tables with 1/2\n" + " the elements in each table, etc. If COUNT is very\n" + " large, say 1000000, the generated C code does a\n" + " binary search.\n" + " -T, --omit-struct-type\n" + " Prevents the transfer of the type declaration to the\n" + " output file. Use this option if the type is already\n" + " defined elsewhere.\n" + "\n" + "Algorithm employed by gperf:\n" + " -k, --key-positions=KEYS\n" + " Select the key positions used in the hash function.\n" + " The allowable choices range between 1-%d, inclusive.\n" + " The positions are separated by commas, ranges may be\n" + " used, and key positions may occur in any order.\n" + " Also, the meta-character '*' causes the generated\n" + " hash function to consider ALL key positions, and $\n" + " indicates the ``final character'' of a key, e.g.,\n" + " $,1,2,4,6-10.\n" + " -l, --compare-strlen Compare key lengths before trying a string\n" + " comparison. This helps cut down on the number of\n" + " string comparisons made during the lookup.\n" + " -D, --duplicates Handle keywords that hash to duplicate values. This\n" + " is useful for certain highly redundant keyword sets.\n" + " -f, --fast=ITERATIONS Generate the gen-perf.hash function ``fast''. This\n" + " decreases gperf's running time at the cost of\n" + " minimizing generated table size. The numeric\n" + " argument represents the number of times to iterate\n" + " when resolving a collision. `0' means ``iterate by\n" + " the number of keywords''.\n" + " -i, --initial-asso=N Provide an initial value for the associate values\n" + " array. Default is 0. Setting this value larger helps\n" + " inflate the size of the final table.\n" + " -j, --jump=JUMP-VALUE Affects the ``jump value'', i.e., how far to advance\n" + " the associated character value upon collisions. Must\n" + " be an odd number, default is %d.\n" + " -n, --no-strlen Do not include the length of the keyword when\n" + " computing the hash function.\n" + " -o, --occurrence-sort Reorders input keys by frequency of occurrence of\n" + " the key sets. This should decrease the search time\n" + " dramatically.\n" + " -r, --random Utilizes randomness to initialize the associated\n" + " values table.\n" + " -s, --size-multiple=N Affects the size of the generated hash table. The\n" + " numeric argument N indicates ``how many times larger\n" + " or smaller'' the associated value range should be,\n" + " in relationship to the number of keys, e.g. a value\n" + " of 3 means ``allow the maximum associated value to\n" + " be about 3 times larger than the number of input\n" + " keys.'' Conversely, a value of -3 means ``make the\n" + " maximum associated value about 3 times smaller than\n" + " the number of input keys. A larger table should\n" + " decrease the time required for an unsuccessful\n" + " search, at the expense of extra table space. Default\n" + " value is 1.\n" + "\n" + "Informative output:\n" + " -h, --help Print this message.\n" + " -v, --version Print the gperf version number.\n" + " -d, --debug Enables the debugging option (produces verbose\n" + " output to the standard error).\n" + "\n" + "Report bugs to <bug-gnu-utils@gnu.org>.\n" + , program_name, MAX_KEY_POS - 1, DEFAULT_JUMP_VALUE); +} + +/* Output command-line Options. */ + +void +Options::print_options (void) +{ + T (Trace t ("Options::print_options");) + int i; + + printf ("/* Command-line: "); + + for (i = 0; i < argument_count; i++) + { + const char *arg = argument_vector[i]; + + /* Escape arg if it contains shell metacharacters. */ + if (*arg == '-') + { + putchar (*arg); + arg++; + if (*arg >= 'A' && *arg <= 'Z' || *arg >= 'a' && *arg <= 'z') + { + putchar (*arg); + arg++; + } + } + if (strpbrk (arg, "\t\n !\"#$&'()*;<>?[\\]`{|}~") != NULL) + { + if (strchr (arg, '\'') != NULL) + { + putchar ('"'); + for (; *arg; arg++) + { + if (*arg == '\"' || *arg == '\\' || *arg == '$') + putchar ('\\'); + putchar (*arg); + } + putchar ('"'); + } + else + { + putchar ('\''); + for (; *arg; arg++) + { + if (*arg == '\\') + putchar ('\\'); + putchar (*arg); + } + putchar ('\''); + } + } + else + printf ("%s", arg); + + printf (" "); + } + + printf (" */"); +} + +/* Sorts the key positions *IN REVERSE ORDER!!* + This makes further routines more efficient. Especially when generating code. + Uses a simple Insertion Sort since the set is probably ordered. + Returns 1 if there are no duplicates, 0 otherwise. */ + +inline int +Options::key_sort (char *base, int len) +{ + T (Trace t ("Options::key_sort");) + int i, j; + + for (i = 0, j = len - 1; i < j; i++) + { + int curr, tmp; + + for (curr = i + 1,tmp = base[curr]; curr > 0 && tmp >= base[curr - 1]; curr--) + if ((base[curr] = base[curr - 1]) == tmp) /* oh no, a duplicate!!! */ + return 0; + + base[curr] = tmp; + } + + return 1; +} + +/* Sets the default Options. */ + +Options::Options (void) +{ + T (Trace t ("Options::Options");) + key_positions[0] = WORD_START; + key_positions[1] = WORD_END; + key_positions[2] = EOS; + total_keysig_size = 2; + delimiters = DEFAULT_DELIMITERS; + jump = DEFAULT_JUMP_VALUE; + option_word = DEFAULTCHARS | C; + function_name = DEFAULT_NAME; + key_name = DEFAULT_KEY; + initializer_suffix = DEFAULT_INITIALIZER_SUFFIX; + hash_name = DEFAULT_HASH_NAME; + wordlist_name = DEFAULT_WORDLIST_NAME; + class_name = DEFAULT_CLASS_NAME; + total_switches = size = 1; + initial_asso_value = iterations = 0; +} + +/* Dumps option status when debug is set. */ + +Options::~Options (void) +{ + T (Trace t ("Options::~Options");) + if (option_word & DEBUG) + { + char *ptr; + + fprintf (stderr, "\ndumping Options:" + "\nDEBUG is.......: %s" + "\nORDER is.......: %s" + "\nTYPE is........: %s" + "\nRANDOM is......: %s" + "\nDEFAULTCHARS is: %s" + "\nSWITCH is......: %s" + "\nNOLENGTH is....: %s" + "\nLENTABLE is....: %s" + "\nDUP is.........: %s" + "\nFAST is........: %s" + "\nCOMP is........: %s" + "\nNOTYPE is......: %s" + "\nGLOBAL is......: %s" + "\nCONST is.......: %s" + "\nKRC is.........: %s" + "\nC is...........: %s" + "\nANSIC is.......: %s" + "\nCPLUSPLUS is...: %s" + "\nENUM is........: %s" + "\nINCLUDE is.....: %s" + "\nSEVENBIT is....: %s" + "\niterations = %d" + "\nlookup function name = %s" + "\nhash function name = %s" + "\nword list name = %s" + "\nkey name = %s" + "\ninitializer suffix = %s" + "\njump value = %d" + "\nmax associated value = %d" + "\ninitial associated value = %d" + "\ndelimiters = %s" + "\nnumber of switch statements = %d\n", + option_word & DEBUG ? "enabled" : "disabled", + option_word & ORDER ? "enabled" : "disabled", + option_word & TYPE ? "enabled" : "disabled", + option_word & RANDOM ? "enabled" : "disabled", + option_word & DEFAULTCHARS ? "enabled" : "disabled", + option_word & SWITCH ? "enabled" : "disabled", + option_word & NOLENGTH ? "enabled" : "disabled", + option_word & LENTABLE ? "enabled" : "disabled", + option_word & DUP ? "enabled" : "disabled", + option_word & FAST ? "enabled" : "disabled", + option_word & COMP ? "enabled" : "disabled", + option_word & NOTYPE ? "enabled" : "disabled", + option_word & GLOBAL ? "enabled" : "disabled", + option_word & CONST ? "enabled" : "disabled", + option_word & KRC ? "enabled" : "disabled", + option_word & C ? "enabled" : "disabled", + option_word & ANSIC ? "enabled" : "disabled", + option_word & CPLUSPLUS ? "enabled" : "disabled", + option_word & ENUM ? "enabled" : "disabled", + option_word & INCLUDE ? "enabled" : "disabled", + option_word & SEVENBIT ? "enabled" : "disabled", + iterations, + function_name, hash_name, wordlist_name, key_name, + initializer_suffix, jump, size - 1, initial_asso_value, + delimiters, total_switches); + if (option_word & ALLCHARS) + fprintf (stderr, "all characters are used in the hash function\n"); + + fprintf (stderr, "maximum keysig size = %d\nkey positions are: \n", + total_keysig_size); + + for (ptr = key_positions; *ptr != EOS; ptr++) + if (*ptr == WORD_END) + fprintf (stderr, "$\n"); + else + fprintf (stderr, "%d\n", *ptr); + + fprintf (stderr, "finished dumping Options\n"); + } +} + + +/* Parses the command line Options and sets appropriate flags in option_word. */ + +static const struct option long_options[] = +{ + { "delimiters", required_argument, 0, 'e' }, + { "struct-type", no_argument, 0, 't' }, + { "language", required_argument, 0, 'L' }, + { "slot-name", required_argument, 0, 'K' }, + { "initializer-suffix", required_argument, 0, 'F' }, + { "hash-fn-name", required_argument, 0, 'H' }, + { "lookup-fn-name", required_argument, 0, 'N' }, + { "class-name", required_argument, 0, 'Z' }, + { "seven-bit", no_argument, 0, '7' }, + { "compare-strncmp", no_argument, 0, 'c' }, + { "readonly-tables", no_argument, 0, 'C' }, + { "enum", no_argument, 0, 'E' }, + { "includes", no_argument, 0, 'I' }, + { "global", no_argument, 0, 'G' }, + { "word-array-name", required_argument, 0, 'W' }, + { "switch", required_argument, 0, 'S' }, + { "omit-struct-type", no_argument, 0, 'T' }, + { "key-positions", required_argument, 0, 'k' }, + { "compare-strlen", no_argument, 0, 'l' }, + { "duplicates", no_argument, 0, 'D' }, + { "fast", required_argument, 0, 'f' }, + { "initial-asso", required_argument, 0, 'i' }, + { "jump", required_argument, 0, 'j' }, + { "no-strlen", no_argument, 0, 'n' }, + { "occurrence-sort", no_argument, 0, 'o' }, + { "random", no_argument, 0, 'r' }, + { "size-multiple", required_argument, 0, 's' }, + { "help", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'v' }, + { "debug", no_argument, 0, 'd' }, + { 0, no_argument, 0, 0 } +}; + +void +Options::operator() (int argc, char *argv[]) +{ + T (Trace t ("Options::operator()");) + int option_char; + + program_name = argv[0]; + argument_count = argc; + argument_vector = argv; + + while ((option_char = + getopt_long (argument_count, argument_vector, + "adcCDe:Ef:F:gGhH:i:Ij:k:K:lL:nN:oprs:S:tTvW:Z:7", + long_options, (int *)0)) + != -1) + { + switch (option_char) + { + case 'a': /* Generated code uses the ANSI prototype format. */ + break; /* This is now the default. */ + case 'c': /* Generate strncmp rather than strcmp. */ + { + option_word |= COMP; + break; + } + case 'C': /* Make the generated tables readonly (const). */ + { + option_word |= CONST; + break; + } + case 'd': /* Enable debugging option. */ + { + option_word |= DEBUG; + fprintf (stderr, "Starting program %s, version %s, with debugging on.\n", + program_name, version_string); + break; + } + case 'D': /* Enable duplicate option. */ + { + option_word |= DUP; + break; + } + case 'e': /* Allows user to provide keyword/attribute separator */ + { + option.delimiters = /*getopt*/optarg; + break; + } + case 'E': + { + option_word |= ENUM; + break; + } + case 'f': /* Generate the hash table ``fast.'' */ + { + option_word |= FAST; + if ((iterations = atoi (/*getopt*/optarg)) < 0) + { + fprintf (stderr, "iterations value must not be negative, assuming 0\n"); + iterations = 0; + } + break; + } + case 'F': + { + initializer_suffix = /*getopt*/optarg; + break; + } + case 'g': /* Use the ``inline'' keyword for generated sub-routines, ifdef __GNUC__. */ + break; /* This is now the default. */ + case 'G': /* Make the keyword table a global variable. */ + { + option_word |= GLOBAL; + break; + } + case 'h': /* Displays a list of helpful Options to the user. */ + { + long_usage (stdout); + exit (0); + } + case 'H': /* Sets the name for the hash function */ + { + hash_name = /*getopt*/optarg; + break; + } + case 'i': /* Sets the initial value for the associated values array. */ + { + if ((initial_asso_value = atoi (/*getopt*/optarg)) < 0) + fprintf (stderr, "Initial value %d should be non-zero, ignoring and continuing.\n", initial_asso_value); + if (option[RANDOM]) + fprintf (stderr, "warning, -r option superceeds -i, ignoring -i option and continuing\n"); + break; + } + case 'I': /* Enable #include statements. */ + { + option_word |= INCLUDE; + break; + } + case 'j': /* Sets the jump value, must be odd for later algorithms. */ + { + if ((jump = atoi (/*getopt*/optarg)) < 0) + { + fprintf (stderr, "Jump value %d must be a positive number.\n", jump); + short_usage (stderr); + exit (1); + } + else if (jump && ((jump % 2) == 0)) + fprintf (stderr, "Jump value %d should be odd, adding 1 and continuing...\n", jump++); + break; + } + case 'k': /* Sets key positions used for hash function. */ + { + const int BAD_VALUE = -1; + int value; + Iterator expand (/*getopt*/optarg, 1, MAX_KEY_POS - 1, WORD_END, BAD_VALUE, EOS); + + if (/*getopt*/optarg [0] == '*') /* Use all the characters for hashing!!!! */ + option_word = (option_word & ~DEFAULTCHARS) | ALLCHARS; + else + { + char *key_pos; + + for (key_pos = key_positions; (value = expand ()) != EOS; key_pos++) + if (value == BAD_VALUE) + { + fprintf (stderr, "Illegal key value or range, use 1,2,3-%d,'$' or '*'.\n", + MAX_KEY_POS - 1); + short_usage (stderr); + exit (1); + } + else + *key_pos = value;; + + *key_pos = EOS; + + if (! (total_keysig_size = (key_pos - key_positions))) + { + fprintf (stderr, "No keys selected.\n"); + short_usage (stderr); + exit (1); + } + else if (! key_sort (key_positions, total_keysig_size)) + { + fprintf (stderr, "Duplicate keys selected\n"); + short_usage (stderr); + exit (1); + } + + if (total_keysig_size != 2 + || (key_positions[0] != 1 || key_positions[1] != WORD_END)) + option_word &= ~DEFAULTCHARS; + } + break; + } + case 'K': /* Make this the keyname for the keyword component field. */ + { + key_name = /*getopt*/optarg; + break; + } + case 'l': /* Create length table to avoid extra string compares. */ + { + option_word |= LENTABLE; + break; + } + case 'L': /* Deal with different generated languages. */ + { + option_word &= ~(KRC | C | ANSIC | CPLUSPLUS); + if (!strcmp (/*getopt*/optarg, "KR-C")) + option_word |= KRC; + else if (!strcmp (/*getopt*/optarg, "C")) + option_word |= C; + else if (!strcmp (/*getopt*/optarg, "ANSI-C")) + option_word |= ANSIC; + else if (!strcmp (/*getopt*/optarg, "C++")) + option_word |= CPLUSPLUS; + else + { + fprintf (stderr, "unsupported language option %s, defaulting to C\n", /*getopt*/optarg); + option_word |= C; + } + break; + } + case 'n': /* Don't include the length when computing hash function. */ + { + option_word |= NOLENGTH; + break; + } + case 'N': /* Make generated lookup function name be optarg */ + { + function_name = /*getopt*/optarg; + break; + } + case 'o': /* Order input by frequency of key set occurrence. */ + { + option_word |= ORDER; + break; + } + case 'p': /* Generated lookup function a pointer instead of int. */ + break; /* This is now the default. */ + case 'r': /* Utilize randomness to initialize the associated values table. */ + { + option_word |= RANDOM; + if (option.initial_asso_value != 0) + fprintf (stderr, "warning, -r option superceeds -i, disabling -i option and continuing\n"); + break; + } + case 's': /* Range of associated values, determines size of final table. */ + { + if (abs (size = atoi (/*getopt*/optarg)) > 50) + fprintf (stderr, "%d is excessive, did you really mean this?! (try `%s --help' for help)\n", size, program_name); + break; + } + case 'S': /* Generate switch statement output, rather than lookup table. */ + { + option_word |= SWITCH; + if ((option.total_switches = atoi (/*getopt*/optarg)) <= 0) + { + fprintf (stderr, "number of switches %s must be a positive number\n", /*getopt*/optarg); + short_usage (stderr); + exit (1); + } + break; + } + case 't': /* Enable the TYPE mode, allowing arbitrary user structures. */ + { + option_word |= TYPE; + break; + } + case 'T': /* Don't print structure definition. */ + { + option_word |= NOTYPE; + break; + } + case 'v': /* Print out the version and quit. */ + fprintf (stdout, "GNU gperf %s\n", version_string); + exit (0); + case 'W': /* Sets the name for the hash table array */ + { + wordlist_name = /*getopt*/optarg; + break; + } + case 'Z': /* Set the class name. */ + { + class_name = /*getopt*/optarg; + break; + } + case '7': /* Assume 7-bit characters. */ + { + option_word |= SEVENBIT; + Vectors::ALPHA_SIZE = 128; + break; + } + default: + short_usage (stderr); + exit (1); + } + + } + + if (argv[/*getopt*/optind] && ! freopen (argv[/*getopt*/optind], "r", stdin)) + { + fprintf (stderr, "Cannot open keyword file `%s'\n", argv[/*getopt*/optind]); + short_usage (stderr); + exit (1); + } + + if (++/*getopt*/optind < argc) + { + fprintf (stderr, "Extra trailing arguments to %s.\n", program_name); + short_usage (stderr); + exit (1); + } +} + +#ifndef __OPTIMIZE__ + +#define INLINE /* not inline */ +#include "options.icc" +#undef INLINE + +#endif /* not defined __OPTIMIZE__ */ diff --git a/src/options.h b/src/options.h new file mode 100644 index 000000000000..9025c86f1732 --- /dev/null +++ b/src/options.h @@ -0,0 +1,159 @@ +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Handles parsing the Options provided to the user. + + Copyright (C) 1989-1998, 2000 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +/* This module provides a uniform interface to the various options available + to a user of the gperf hash function generator. In addition to the + run-time options, found in the Option_Type below, there is also the + hash table Size and the Keys to be used in the hashing. + The overall design of this module was an experiment in using C++ + classes as a mechanism to enhance centralization of option and + and error handling, which tend to get out of hand in a C program. */ + +#ifndef options_h +#define options_h 1 + +#include <stdio.h> + +/* Enumerate the potential debugging Options. */ + +enum Option_Type +{ + DEBUG = 01, /* Enable debugging (prints diagnostics to stderr). */ + ORDER = 02, /* Apply ordering heuristic to speed-up search time. */ + ALLCHARS = 04, /* Use all characters in hash function. */ + TYPE = 010, /* Handle user-defined type structured keyword input. */ + RANDOM = 020, /* Randomly initialize the associated values table. */ + DEFAULTCHARS = 040, /* Make default char positions be 1,$ (end of keyword). */ + SWITCH = 0100, /* Generate switch output to save space. */ + NOLENGTH = 0200, /* Don't include keyword length in hash computations. */ + LENTABLE = 0400, /* Generate a length table for string comparison. */ + DUP = 01000, /* Handle duplicate hash values for keywords. */ + FAST = 02000, /* Generate the hash function ``fast.'' */ + NOTYPE = 04000, /* Don't include user-defined type definition in output -- it's already defined elsewhere. */ + COMP = 010000, /* Generate strncmp rather than strcmp. */ + GLOBAL = 020000, /* Make the keyword table a global variable. */ + CONST = 040000, /* Make the generated tables readonly (const). */ + KRC = 0100000, /* Generate K&R C code: no prototypes, no const. */ + C = 0200000, /* Generate C code: no prototypes, but const (user can #define it away). */ + ANSIC = 0400000, /* Generate ISO/ANSI C code: prototypes and const, but no class. */ + CPLUSPLUS = 01000000, /* Generate C++ code: prototypes, const, class, inline, enum. */ + ENUM = 02000000, /* Use enum for constants. */ + INCLUDE = 04000000, /* Generate #include statements. */ + SEVENBIT = 010000000 /* Assume 7-bit, not 8-bit, characters. */ +}; + +/* Define some useful constants (these don't really belong here, but I'm + not sure where else to put them!). These should be consts, but g++ + doesn't seem to do the right thing with them at the moment... ;-( */ + +enum +{ + MAX_KEY_POS = 128 - 1, /* Max size of each word's key set. */ + WORD_START = 1, /* Signals the start of a word. */ + WORD_END = 0, /* Signals the end of a word. */ + EOS = MAX_KEY_POS /* Signals end of the key list. */ +}; + +/* Class manager for gperf program Options. */ + +class Options +{ +public: + Options (void); + ~Options (void); + int operator[] (Option_Type option); + void operator() (int argc, char *argv[]); + void operator= (enum Option_Type); + void operator!= (enum Option_Type); + static void print_options (void); + static void set_asso_max (int r); + static int get_asso_max (void); + static void reset (void); + static int get (void); + static int get_iterations (void); + static int get_max_keysig_size (void); + static void set_keysig_size (int); + static int get_jump (void); + static int initial_value (void); + static int get_total_switches (void); + static const char *get_function_name (void); + static const char *get_key_name (void); + static const char *get_initializer_suffix (void); + static const char *get_class_name (void); + static const char *get_hash_name (void); + static const char *get_wordlist_name (void); + static const char *get_delimiter (void); + +private: + static int option_word; /* Holds the user-specified Options. */ + static int total_switches; /* Number of switch statements to generate. */ + static int total_keysig_size; /* Total number of distinct key_positions. */ + static int size; /* Range of the hash table. */ + static int key_pos; /* Tracks current key position for Iterator. */ + static int jump; /* Jump length when trying alternative values. */ + static int initial_asso_value; /* Initial value for asso_values table. */ + static int argument_count; /* Records count of command-line arguments. */ + static int iterations; /* Amount to iterate when a collision occurs. */ + static char **argument_vector; /* Stores a pointer to command-line vector. */ + static const char *function_name; /* Names used for generated lookup function. */ + static const char *key_name; /* Name used for keyword key. */ + static const char *initializer_suffix; /* Suffix for empty struct initializers. */ + static const char *class_name; /* Name used for generated C++ class. */ + static const char *hash_name; /* Name used for generated hash function. */ + static const char *wordlist_name; /* Name used for hash table array. */ + static const char *delimiters; /* Separates keywords from other attributes. */ + static char key_positions[MAX_KEY_POS]; /* Contains user-specified key choices. */ + static int key_sort (char *base, int len); /* Sorts key positions in REVERSE order. */ + static void short_usage (FILE * strm); /* Prints proper program usage. */ + static void long_usage (FILE * strm); /* Prints proper program usage. */ +}; + +/* Global option coordinator for the entire program. */ +extern Options option; + +/* Set to 1 if your want to stack-allocate some large arrays. + This requires compiler support for variable-size arrays on the stack + (not ANSI). */ +#ifndef LARGE_STACK_ARRAYS +#if defined(__GNUG__) && !defined(__STRICT_ANSI__) +#define LARGE_STACK_ARRAYS 1 +#else +#define LARGE_STACK_ARRAYS 0 +#endif +#endif + +/* Set to 1 if the stack is large enough for holding a text line. */ +#ifndef LARGE_STACK +#define LARGE_STACK 1 +#endif + +#ifdef __OPTIMIZE__ + +#include "trace.h" +#define INLINE inline +#include "options.icc" +#undef INLINE + +#endif + +#endif diff --git a/src/options.icc b/src/options.icc new file mode 100644 index 000000000000..82fe5375c23f --- /dev/null +++ b/src/options.icc @@ -0,0 +1,183 @@ +/* Inline Functions for options.{h,cc}. + + Copyright (C) 1989-1998, 2000 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +// This needs: +//#include "trace.h" + +/* TRUE if option enable, else FALSE. */ +INLINE int +Options::operator[] (Option_Type option) +{ + T (Trace t ("Options::operator[]");) + return option_word & option; +} + +/* Enables option OPT. */ +INLINE void +Options::operator = (enum Option_Type opt) +{ + T (Trace t ("Options::operator=");) + option_word |= opt; +} + +/* Disables option OPT. */ +INLINE void +Options::operator != (enum Option_Type opt) +{ + T (Trace t ("Options::operator!=");) + option_word &= ~opt; +} + +/* Initializes the key Iterator. */ +INLINE void +Options::reset (void) +{ + T (Trace t ("Options::reset");) + key_pos = 0; +} + +/* Returns current key_position and advance index. */ +INLINE int +Options::get (void) +{ + T (Trace t ("Options::get");) + return key_positions[key_pos++]; +} + +/* Sets the size of the table size. */ +INLINE void +Options::set_asso_max (int r) +{ + T (Trace t ("Options::set_asso_max");) + size = r; +} + +/* Returns the size of the table size. */ +INLINE int +Options::get_asso_max (void) +{ + T (Trace t ("Options::get_asso_max");) + return size; +} + +/* Returns total distinct key positions. */ +INLINE int +Options::get_max_keysig_size (void) +{ + T (Trace t ("Options::get_max_keysig_size");) + return total_keysig_size; +} + +/* Sets total distinct key positions. */ +INLINE void +Options::set_keysig_size (int size) +{ + T (Trace t ("Options::set_keysig_size");) + total_keysig_size = size; +} + +/* Returns the jump value. */ +INLINE int +Options::get_jump (void) +{ + T (Trace t ("Options::get_jump");) + return jump; +} + +/* Returns the generated function name. */ +INLINE const char * +Options::get_function_name (void) +{ + T (Trace t ("Options::get_function_name");) + return function_name; +} + +/* Returns the keyword key name. */ +INLINE const char * +Options::get_key_name (void) +{ + T (Trace t ("Options::get_key_name");) + return key_name; +} + +/* Returns the struct initializer suffix. */ +INLINE const char * +Options::get_initializer_suffix (void) +{ + T (Trace t ("Options::get_initializer_suffix");) + return initializer_suffix; +} + +/* Returns the hash function name. */ +INLINE const char * +Options::get_hash_name (void) +{ + T (Trace t ("Options::get_hash_name");) + return hash_name; +} + +/* Returns the hash table array name. */ +INLINE const char * +Options::get_wordlist_name (void) +{ + T (Trace t ("Options::get_wordlist_name");) + return wordlist_name; +} + +/* Returns the generated class name. */ +INLINE const char * +Options::get_class_name (void) +{ + T (Trace t ("Options::get_class_name");) + return class_name; +} + +/* Returns the initial associated character value. */ +INLINE int +Options::initial_value (void) +{ + T (Trace t ("Options::initial_value");) + return initial_asso_value; +} + +/* Returns the iterations value. */ +INLINE int +Options::get_iterations (void) +{ + T (Trace t ("Options::get_iterations");) + return iterations; +} + +/* Returns the string used to delimit keywords from other attributes. */ +INLINE const char * +Options::get_delimiter () +{ + T (Trace t ("Options::get_delimiter");) + return delimiters; +} + +/* Gets the total number of switch statements to generate. */ +INLINE int +Options::get_total_switches () +{ + T (Trace t ("Options::get_total_switches");) + return total_switches; +} diff --git a/src/perfect.c b/src/perfect.c new file mode 100644 index 000000000000..25b958e2e44e --- /dev/null +++ b/src/perfect.c @@ -0,0 +1,350 @@ +/* Provides high-level routines to manipulate the keywork list + structures the code generation output. + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include <assert.h> +#include <ctype.h> +#include "options.h" +#include "perfect.h" +#include "stderr.h" + +/* Current release version. */ +extern char *version_string; + +/* Counts occurrences of each key set character. */ +int occurrences[ALPHABET_SIZE]; + +/* Value associated with each character. */ +int asso_values[ALPHABET_SIZE]; + +/* Locally visible PERFECT object. */ +PERFECT perfect; + +/* Efficiently returns the least power of two greater than or equal to X! */ +#define POW(X) ((!X)?1:(X-=1,X|=X>>1,X|=X>>2,X|=X>>4,X|=X>>8,X|=X>>16,(++X))) + +/* Reads input keys, possibly applies the reordering heuristic, sets the + maximum associated value size (rounded up to the nearest power of 2), + may initialize the associated values array, and determines the maximum + hash table size. Note: using the random numbers is often helpful, + though not as deterministic, of course! */ + +void +perfect_init () +{ + int asso_value_max; + int len; + + perfect.num_done = 1; + perfect.fewest_collisions = 0; + read_keys (); + if (OPTION_ENABLED (option, ORDER)) + reorder (); + asso_value_max = GET_ASSO_MAX (option); + len = keyword_list_length (); + asso_value_max = (asso_value_max ? asso_value_max * len : len); + SET_ASSO_MAX (option, POW (asso_value_max)); + + if (OPTION_ENABLED (option, RANDOM)) + { + int i; + + srandom (time (0)); + + for (i = 0; i < ALPHABET_SIZE; i++) + asso_values[i] = (random () & asso_value_max - 1); + } + else + { + int asso_value = INITIAL_VALUE (option); + if (asso_value) /* Initialize array if user requests non-zero default. */ + { + int i; + + for (i = ALPHABET_SIZE - 1; i >= 0; i--) + asso_values[i] = asso_value & GET_ASSO_MAX (option) - 1; + } + } + perfect.max_hash_value = max_key_length () + GET_ASSO_MAX (option) * + GET_CHARSET_SIZE (option); + + printf ("/* C code produced by gperf version %s */\n", version_string); + print_options (); + + if (OPTION_ENABLED (option, DEBUG)) + { + int i; + fprintf (stderr, "\nnumber of keys = %d\nmaximum associated value is %d\ +\nmaximum possible size of generated hash table is %d\n", + len, asso_value_max, perfect.max_hash_value); + } +} + +/* Merge two hash key multisets to form the ordered disjoint union of the sets. + (In a multiset, an element can occur multiple times). Precondition: both + set_1 and set_2 must be ordered. Returns the length of the combined set. */ + +static int +compute_disjoint_union (set_1, set_2, set_3) + char *set_1; + char *set_2; + char *set_3; +{ + char *base = set_3; + + while (*set_1 && *set_2) + if (*set_1 == *set_2) + set_1++, set_2++; + else + { + *set_3 = *set_1 < *set_2 ? *set_1++ : *set_2++; + if (set_3 == base || *set_3 != *(set_3-1)) set_3++; + } + + while (*set_1) + { + *set_3 = *set_1++; + if (set_3 == base || *set_3 != *(set_3-1)) set_3++; + } + + while (*set_2) + { + *set_3 = *set_2++; + if (set_3 == base || *set_3 != *(set_3-1)) set_3++; + } + *set_3 = '\0'; + return set_3 - base; +} + +/* Sort the UNION_SET in increasing frequency of occurrence. + This speeds up later processing since we may assume the resulting + set (Set_3, in this case), is ordered. Uses insertion sort, since + the UNION_SET is typically short. */ + +static void +sort_set (union_set, len) + char *union_set; + int len; +{ + int i, j; + + for (i = 0, j = len - 1; i < j; i++) + { + char curr, tmp; + + for (curr = i+1, tmp = union_set[curr]; + curr > 0 && occurrences[tmp] < occurrences[union_set[curr-1]]; + curr--) + union_set[curr] = union_set[curr - 1]; + + union_set[curr] = tmp; + } +} + +/* Generate a key set's hash value. */ + +static int +hash (key_node) + LIST_NODE *key_node; +{ + int sum = OPTION_ENABLED (option, NOLENGTH) ? 0 : key_node->length; + char *ptr; + + for (ptr = key_node->char_set; *ptr; ptr++) + sum += asso_values[*ptr]; + + return key_node->hash_value = sum; +} + +/* Find out how associated value changes affect successfully hashed items. + Returns FALSE if no other hash values are affected, else returns TRUE. + Note that because GET_ASSO_MAX (option) is a power of two we can guarantee + that all legal ASSO_VALUES are visited without repetition since + GET_JUMP (option) was forced to be an odd value! */ + +static bool +affects_prev (c, curr) + char c; + LIST_NODE *curr; +{ + int original_char = asso_values[c]; + int i = !OPTION_ENABLED (option, FAST) ? GET_ASSO_MAX (option) : + GET_ITERATIONS (option) == 0 ? key_list.list_len : GET_ITERATIONS (option); + + /* Try all asso_values. */ + + while (--i >= 0) + { + int collisions = 0; + LIST_NODE *ptr; + + asso_values[c] = asso_values[c] + (GET_JUMP (option) ? GET_JUMP (option) : random ()) + & GET_ASSO_MAX (option) - 1; + bool_array_reset (); + + /* See how this asso_value change affects previous keywords. If + it does better than before we'll take it! */ + + for (ptr = key_list.head; + !lookup (hash (ptr)) || ++collisions < perfect.fewest_collisions; + ptr = ptr->next) + if (ptr == curr) + { + perfect.fewest_collisions = collisions; + return FALSE; + } + } + + asso_values[c] = original_char; /* Restore original values, no more tries. */ + return TRUE; /* If we're this far it's time to try the next character.... */ +} + +/* Change a character value, try least-used characters first. */ + +static void +change (prior, curr) + LIST_NODE *prior; + LIST_NODE *curr; +{ + char *xmalloc (); + static char *union_set = 0; + char *temp; + LIST_NODE *ptr; + + if (!union_set) + union_set = xmalloc (2 * GET_CHARSET_SIZE (option) + 1); + + if (OPTION_ENABLED (option, DEBUG)) /* Very useful for debugging. */ + { + fprintf (stderr, "collision on keyword #%d, prior=\"%s\", curr=\"%s\", hash=%d\n", + perfect.num_done, prior->key, curr->key, curr->hash_value); + fflush (stderr); + } + sort_set (union_set, compute_disjoint_union (prior->char_set, curr->char_set, union_set)); + + /* Try changing some values, if change doesn't alter other values continue normal action. */ + + perfect.fewest_collisions++; + + for (temp = union_set; *temp; temp++) + if (!affects_prev (*temp, curr)) + { + if (OPTION_ENABLED (option, DEBUG)) + { + fprintf (stderr, "- resolved by changing asso_value['%c'] (char #%d) to %d\n", + *temp, temp - union_set + 1, asso_values[*temp]); + fflush (stderr); + } + return; /* Good, doesn't affect previous hash values, we'll take it. */ + } + + for (ptr = key_list.head; ptr != curr; ptr = ptr->next) + hash (ptr); + + hash (curr); + + if (OPTION_ENABLED (option, DEBUG)) + { + fprintf (stderr, "** collision not resolved, %d duplicates remain, continuing...\n", + perfect.fewest_collisions); + fflush (stderr); + } +} + +/* Does the hard stuff.... + Initializes the Iteration Number boolean array, and then trys to find a + perfect function that will hash all the key words without getting any + duplications. This is made much easier since we aren't attempting + to generate *minimum* functions, only perfect ones. + If we can't generate a perfect function in one pass *and* the user + hasn't enabled the DUP option, we'll inform the user to try the + randomization option, use -D, or choose alternative key positions. + The alternatives (e.g., back-tracking) are too time-consuming, i.e, + exponential in the number of keys. */ + +int +perfect_generate () +{ + LIST_NODE *curr; + bool_array_init (perfect.max_hash_value); + + for (curr = key_list.head; curr; curr = curr->next) + { + LIST_NODE *ptr; + hash (curr); + + for (ptr = key_list.head; ptr != curr; ptr = ptr->next) + if (ptr->hash_value == curr->hash_value) + { + change (ptr, curr); + break; + } + perfect.num_done++; + } + + + /* Make one final check, just to make sure nothing weird happened.... */ + bool_array_reset (); + + for (curr = key_list.head; curr; curr = curr->next) + if (lookup (hash (curr))) + if (OPTION_ENABLED (option, DUP)) /* We'll try to deal with this later..... */ + break; + else /* Yow, big problems. we're outta here! */ + { + report_error ("\nInternal error, duplicate value %d:\n\ +try options -D or -r, or use new key positions.\n\n", + hash (curr)); + return 1; + } + + bool_array_destroy (); + + /* First sorts the key word list by hash value, and the outputs the + list to the proper ostream. The generated hash table code is only + output if the early stage of processing turned out O.K. */ + + sort (); + print_output (); + return 0; +} + +/* Prints out some diagnostics upon completion. */ + +void +perfect_destroy () +{ + if (OPTION_ENABLED (option, DEBUG)) + { + int i; + + fprintf (stderr, "\ndumping occurrence and associated values tables\n"); + + for (i = 0; i < ALPHABET_SIZE; i++) + if (occurrences[i]) + fprintf (stderr, "asso_values[%c] = %3d, occurrences[%c] = %3d\n", + i, asso_values[i], i, occurrences[i]); + + fprintf (stderr, "end table dumping\n"); + + } +} + diff --git a/src/perfect.h b/src/perfect.h new file mode 100644 index 000000000000..c5b9443413d5 --- /dev/null +++ b/src/perfect.h @@ -0,0 +1,45 @@ +/* Provides high-level routines to manipulate the keyword list + structures the code generation output. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _perfect_h +#define _perfect_h + +#include "prototype.h" +#include "keylist.h" +#include "boolarray.h" + +typedef struct perfect +{ + KEY_LIST list; /* List of key words provided by the user. */ + BOOL_ARRAY duplicate; /* Speeds up check for redundant hash values. */ + int max_hash_value; /* Maximum possible hash value. */ + int fewest_collisions; /* Records fewest # of collisions for asso value. */ + int num_done; /* Number of keywords processed without a collision. */ +} PERFECT; + +extern void perfect_init P ((void)); +extern void perfect_destroy P ((void)); +extern int perfect_generate P ((void)); +extern void perfect_print P ((void)); +#endif /* _perfect_h */ + + diff --git a/src/prototype.h b/src/prototype.h new file mode 100644 index 000000000000..a6077b65c206 --- /dev/null +++ b/src/prototype.h @@ -0,0 +1,15 @@ +#ifndef _prototype_h +#define _prototype_h +#ifdef __STDC__ +#define P(X) X +#else +#define P(X) () +#endif + +typedef char bool; +#define FALSE 0 +#define TRUE 1 + +#define ODD(X) ((X) & 1) +#define EVEN(X) (!((X) & 1)) +#endif /* _prototype_h */ diff --git a/src/read-line.cc b/src/read-line.cc new file mode 100644 index 000000000000..8cb0971cb6b9 --- /dev/null +++ b/src/read-line.cc @@ -0,0 +1,97 @@ +/* Correctly reads an arbitrarily size string. + + Copyright (C) 1989-1998 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "read-line.h" + +#include <stdlib.h> +#include <string.h> /* declares memcpy() */ +#include "options.h" +#include "trace.h" + +/* Recursively fills up the buffer. */ + +#define CHUNK_SIZE 4096 + +/* CHUNKS is the number of chunks (each of size CHUNK_SIZE) which have + already been read and which are temporarily stored on the stack. + This function reads the remainder of the line, allocates a buffer + for the entire line, fills the part beyond &buffer[chunks*CHUNK_SIZE], + and returns &buffer[chunks*CHUNK_SIZE]. */ + +char * +Read_Line::readln_aux (int chunks) +{ + T (Trace t ("Read_Line::readln_aux");) +#if LARGE_STACK + char buf[CHUNK_SIZE]; +#else + // Note: we don't use new, because that invokes a custom operator new. + char *buf = (char*)malloc(CHUNK_SIZE); + if (buf == NULL) + abort (); +#endif + char *bufptr = buf; + char *ptr; + int c; + + while (c = getc (fp), c != EOF && c != '\n') /* fill the current buffer */ + { + *bufptr++ = c; + if (bufptr - buf == CHUNK_SIZE) + { + if ((ptr = readln_aux (chunks + 1)) != NULL) + + /* prepend remainder to ptr buffer */ + { + ptr -= CHUNK_SIZE; + memcpy (ptr, buf, CHUNK_SIZE); + } + + goto done; + } + } + if (c == EOF && bufptr == buf && chunks == 0) + ptr = NULL; + else + { + size_t s1 = chunks * CHUNK_SIZE; + size_t s2 = bufptr - buf; + + ptr = new char[s1+s2+1]; + ptr += s1; + ptr[s2] = '\0'; + memcpy (ptr, buf, s2); + } + done: +#if !LARGE_STACK + free (buf); +#endif + + return ptr; +} + +#ifndef __OPTIMIZE__ + +#define INLINE /* not inline */ +#include "read-line.icc" +#undef INLINE + +#endif /* not defined __OPTIMIZE__ */ diff --git a/src/read-line.h b/src/read-line.h new file mode 100644 index 000000000000..b243c84749b5 --- /dev/null +++ b/src/read-line.h @@ -0,0 +1,53 @@ +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Reads arbitrarily long string from input file, returning it as a + dynamically allocated buffer. + + Copyright (C) 1989-1998 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +/* Returns a pointer to an arbitrary length string. Returns NULL on error or EOF + The storage for the string is dynamically allocated by new. */ + +#ifndef read_line_h +#define read_line_h 1 + +#include <stdio.h> + +class Read_Line +{ +private: + char *readln_aux (int chunks); + FILE *fp; /* FILE pointer to the input stream. */ + +public: + Read_Line (FILE *stream = stdin) : fp (stream) {} + char *get_line (void); +}; + +#ifdef __OPTIMIZE__ + +#include "trace.h" +#define INLINE inline +#include "read-line.icc" +#undef INLINE + +#endif + +#endif diff --git a/src/read-line.icc b/src/read-line.icc new file mode 100644 index 000000000000..cdb5bf6f8c0a --- /dev/null +++ b/src/read-line.icc @@ -0,0 +1,47 @@ +/* Inline Functions for read-line.{h,cc}. + + Copyright (C) 1989-1998 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +// This needs: +//#include <stdio.h> +//#include "trace.h" + +/* Returns the ``next'' line, ignoring comments beginning with '#'. */ +INLINE char * +Read_Line::get_line (void) +{ + T (Trace t ("Read_Line::get_line");) + int c; + + while ((c = getc (fp)) == '#') + { + while (c = getc (fp), c != EOF && c != '\n') + ; + + if (c == EOF) + return (char *)0; + } + + if (c == EOF) + return (char *)0; + + ungetc (c, stdin); + return readln_aux (0); +} diff --git a/src/readline.c b/src/readline.c new file mode 100644 index 000000000000..19ac5e56366c --- /dev/null +++ b/src/readline.c @@ -0,0 +1,87 @@ +/* Correctly reads an arbitrarily size string. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include "readline.h" + +/* Size of each chunk. */ +#define CHUNK_SIZE BUFSIZ + +/* Recursively fills up the buffer. */ + +static char * +readln_aux (chunks) + int chunks; +{ + char *buffered_malloc (); + char buf[CHUNK_SIZE]; + register char *bufptr = buf; + register char *ptr; + int c; + + while ((c = getchar ()) != EOF && c != '\n') /* fill the current buffer */ + { + *bufptr++ = c; + if (bufptr - buf >= CHUNK_SIZE) /* prepend remainder to ptr buffer */ + { + if (ptr = readln_aux (chunks + 1)) + + for (; bufptr != buf; *--ptr = *--bufptr); + + return ptr; + } + } + + if (c == EOF && bufptr == buf) + return NULL; + + c = (chunks * CHUNK_SIZE + bufptr - buf) + 1; + + if (ptr = buffered_malloc (c)) + { + + for (*(ptr += (c - 1)) = '\0'; bufptr != buf; *--ptr = *--bufptr) + ; + + return ptr; + } + else + return NULL; +} + +/* Returns the ``next'' line, ignoring comments beginning with '#'. */ + +char *read_line () +{ + int c; + if ((c = getchar ()) == '#') + { + while ((c = getchar ()) != '\n' && c != EOF) + ; + + return c != EOF ? read_line () : NULL; + } + else + { + ungetc (c, stdin); + return readln_aux (0); + } +} diff --git a/src/readline.h b/src/readline.h new file mode 100644 index 000000000000..13164d902bb9 --- /dev/null +++ b/src/readline.h @@ -0,0 +1,31 @@ +/* Reads arbitrarily long string from input file, returning it as a dynamic buffer. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Returns a pointer to an arbitrary length string. Returns NULL on error or EOF + The storage for the string is dynamically allocated by new. */ + +#ifndef _readline_h +#define _readline_h +#include "prototype.h" + +extern char *read_line P ((void)); +#endif /* _readline_h */ + diff --git a/src/stderr.c b/src/stderr.c new file mode 100644 index 000000000000..3f00dd50eebb --- /dev/null +++ b/src/stderr.c @@ -0,0 +1,90 @@ +/* Provides a useful variable-length argument error handling abstraction. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include "stderr.h" + +/* Holds the name of the currently active program. */ +static char *program_name; + +/* Sets name of program. */ + +void +set_program_name (prog_name) + char *prog_name; +{ + program_name = prog_name; +} + +/* Valid Options (prefixed by '%', as in printf format strings) include: + 'a': exit the program at this point + 'c': print a character + 'd': print a decimal number + 'e': call the function pointed to by the corresponding argument + 'f','g': print a double + 'n': print the name of the program (NULL if not set in constructor or elsewhere) + 'p': print out the appropriate errno value from sys_errlist + 's': print out a character string + '%': print out a single percent sign, '%' */ + +void +report_error (va_alist) + va_dcl +{ + extern int errno, sys_nerr; + extern char *sys_errlist[]; + typedef void (*PTF)(); + typedef char *CHARP; + va_list argp; + int abort = 0; + char *format; + + va_start (argp); + + for (format = va_arg (argp, char *); *format; format++) + { + if (*format != '%') + putc (*format, stderr); + else + { + switch(*++format) + { + case '%' : putc ('%', stderr); break; + case 'a' : abort = 1; break; + case 'c' : putc (va_arg (argp, int), stderr); break; + case 'd' : fprintf (stderr, "%d", va_arg (argp, int)); break; + case 'e' : (*va_arg (argp, PTF))(); break; + case 'f' : fprintf (stderr, "%g", va_arg (argp, double)); break; + case 'n' : fputs (program_name ? program_name : "error", stderr); break; + case 'p' : + if (errno < sys_nerr) + fprintf (stderr, "%s: %s", va_arg (argp, CHARP), sys_errlist[errno]); + else + fprintf (stderr, "<unknown error> %d", errno); + break; + case 's' : fputs (va_arg (argp, CHARP), stderr); break; + } + } + if (abort) + exit (1); + } + va_end (argp); +} diff --git a/src/stderr.h b/src/stderr.h new file mode 100644 index 000000000000..a94255e4d004 --- /dev/null +++ b/src/stderr.h @@ -0,0 +1,29 @@ +/* Provides a useful variable-length argument error handling abstraction. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _stderr_h +#define _stderr_h +#include "prototype.h" +#include <varargs.h> + +extern void set_program_name P ((char *prog_name)); +extern void report_error (); +#endif /* _stderr_h */ diff --git a/src/trace.cc b/src/trace.cc new file mode 100644 index 000000000000..e571abae4239 --- /dev/null +++ b/src/trace.cc @@ -0,0 +1,35 @@ +/* Tracing function calls. + Copyright (C) 1989-1998 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "trace.h" + +#include <stdio.h> + +int Trace::nesting = 0; + +Trace::Trace (const char *n) +{ + fprintf (stderr, "%*scalling %s\n", 3 * nesting++, "", name = n); +} + +Trace::~Trace (void) +{ + fprintf (stderr, "%*sleaving %s\n", 3 * --nesting, "", name); +} diff --git a/src/trace.h b/src/trace.h new file mode 100644 index 000000000000..f16fcc5bd99e --- /dev/null +++ b/src/trace.h @@ -0,0 +1,40 @@ +/* Tracing function calls. + Copyright (C) 1989-1998 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef trace_h +#define trace_h 1 + +#ifdef TRACE +#define T(X) X +#else +#define T(X) +#endif + +class Trace +{ +private: + static int nesting; + const char *name; +public: + Trace (const char *n); + ~Trace (void); +}; + +#endif diff --git a/src/vectors.cc b/src/vectors.cc new file mode 100644 index 000000000000..1da014d4a31a --- /dev/null +++ b/src/vectors.cc @@ -0,0 +1,25 @@ +/* Static class data members that are shared between several classes. + Copyright (C) 1989-1998 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "vectors.h" + +int Vectors::ALPHA_SIZE = MAX_ALPHA_SIZE; +int Vectors::occurrences[MAX_ALPHA_SIZE]; +int Vectors::asso_values[MAX_ALPHA_SIZE]; diff --git a/src/vectors.h b/src/vectors.h new file mode 100644 index 000000000000..28a105397ebb --- /dev/null +++ b/src/vectors.h @@ -0,0 +1,37 @@ +/* This may look like C code, but it is really -*- C++ -*- */ + +/* Static class data members that are shared between several classes via + inheritance. + + Copyright (C) 1989-1998 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef vectors_h +#define vectors_h 1 + +static const int MAX_ALPHA_SIZE = 256; + +struct Vectors +{ + static int ALPHA_SIZE; /* Size of alphabet. */ + static int occurrences[MAX_ALPHA_SIZE]; /* Counts occurrences of each key set character. */ + static int asso_values[MAX_ALPHA_SIZE]; /* Value associated with each character. */ +}; + +#endif diff --git a/src/version.c b/src/version.c new file mode 100644 index 000000000000..7fa142cdcc90 --- /dev/null +++ b/src/version.c @@ -0,0 +1,22 @@ +/* Current program version number. + + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +char *version_string = "2.1 (K&R C version)"; diff --git a/src/version.cc b/src/version.cc new file mode 100644 index 000000000000..8f07c695d537 --- /dev/null +++ b/src/version.cc @@ -0,0 +1,22 @@ +/* Current program version number. + + Copyright (C) 1989-1998, 2000 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +const char *version_string = "2.7.2"; diff --git a/src/version.h b/src/version.h new file mode 100644 index 000000000000..4ffba2ec91a1 --- /dev/null +++ b/src/version.h @@ -0,0 +1,23 @@ +/* Current program version number. + + Copyright (C) 1989-1998 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +/* Current release version. */ +extern const char *version_string; diff --git a/src/xmalloc.c b/src/xmalloc.c new file mode 100644 index 000000000000..09cc0227564c --- /dev/null +++ b/src/xmalloc.c @@ -0,0 +1,78 @@ +/* Provide a useful malloc sanity checker and an efficient buffered memory + allocator that reduces calls to malloc. + Copyright (C) 1989 Free Software Foundation, Inc. + written by Douglas C. Schmidt (schmidt@ics.uci.edu) + +This file is part of GNU GPERF. + +GNU GPERF 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 1, or (at your option) +any later version. + +GNU GPERF 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 GNU GPERF; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> + +/* Grabs SIZE bytes of dynamic memory or dies trying! */ + +char * +xmalloc (size) + int size; +{ + char *malloc (); + char *temp = malloc (size); + + if (temp == 0) + { + fprintf (stderr, "out of virtual memory\n"); + exit (1); + } + return temp; +} + +/* Determine default alignment. If your C compiler does not + like this then try something like #define DEFAULT_ALIGNMENT 8. */ +struct fooalign {char x; double d;}; +#define ALIGNMENT ((char *)&((struct fooalign *) 0)->d - (char *)0) + +/* Provide an abstraction that cuts down on the number of + calls to MALLOC by buffering the memory pool from which + items are allocated. */ + +char * +buffered_malloc (size) + int size; +{ + char *temp; + static char *buf_start = 0; /* Large array used to reduce calls to NEW. */ + static char *buf_end = 0; /* Indicates end of BUF_START. */ + static int buf_size = 4 * BUFSIZ; /* Size of buffer pointed to by BUF_START. */ + + /* Align this on correct boundaries, just to be safe... */ + size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT; + + /* If we are about to overflow our buffer we'll just grab another + chunk of memory. Since we never free the original memory it + doesn't matter that no one points to the beginning of that + chunk. Furthermore, as a heuristic, we double the + size of the new buffer! */ + + if (buf_start + size >= buf_end) + { + buf_size = buf_size * 2 > size ? buf_size * 2 : size; + buf_start = xmalloc (buf_size); + buf_end = buf_start + buf_size; + } + + temp = buf_start; + buf_start += size; + return temp; +} |