[comp.sources.unix] v20i041: Perfect hash generator for sets of key words, Part02/05

rsalz@uunet.uu.net (Rich Salz) (10/20/89)

Submitted-by: "Douglas C. Schmidt" <schmidt@glacier.ics.uci.edu>
Posting-number: Volume 20, Issue 41
Archive-name: gperf/part02

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 5)."
# Contents:  cperf/ChangeLog cperf/src/Makefile cperf/src/getopt.c
#   cperf/src/hashtable.c cperf/src/listnode.c cperf/src/options.h
#   cperf/src/perfect.c
# Wrapped by schmidt@crimee.ics.uci.edu on Wed Oct 18 11:43:32 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cperf/ChangeLog' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cperf/ChangeLog'\"
else
echo shar: Extracting \"'cperf/ChangeLog'\" \(8012 characters\)
sed "s/^X//" >'cperf/ChangeLog' <<'END_OF_FILE'
XMon Oct 16 19:58:08 1989  Doug Schmidt  (schmidt at glacier.ics.uci.edu)
X
X        * Fixed a number of small bugs kindly brought to my attention by
X          Adam de Boor (bsw!adam@uunet.UU.NET).  Thanks Adam!  In particular,
X          changed the behavior for the -a (ANSI) option so that the
X          generated prototypes use int rather than size_t for the LEN 
X          parameter.  It was too ugly having to #include <stddef.h> all
X          over the place...
X
X        * Added a majorly neat hack to Bool_Array, suggested by rfg.
X          The basic idea was to throw away the Ullman array technique.
X          The Ullman array was used to remove the need to reinitialize all 
X          the Bool_Array elements to zero everytime we needed to determine
X          whether there were duplicate hash values in the keyword list.  
X          The current trick uses an `iteration number' scheme, which takes
X          about 1/3 the space and reduces the overall program running a 
X          time by about 20 percent for large input!  The hack works as 
X          follows:
X          
X          1. Dynamically allocation 1 boolean array of size k.
X          2. Initialize the boolean array to zeros, and consider the first
X             iteration to be iteration 1.
X          2. Then on all subsequent iterations we `reset' the bool array by
X             kicking the iteration count by 1. 
X          3. When it comes time to check whether a hash value is currently
X             in the boolean array we simply check its index location.  If
X             the value stored there is *not* equal to the current iteration
X             number then the item is clearly *not* in the set.  In that
X             case we assign the iteration number to that array's index
X             location for future reference.  Otherwise, if the item at
X             the index location *is* equal to the iteration number we've
X             found a duplicate.  No muss, no fuss!
X
XThu Oct 12 18:08:43 1989  Doug Schmidt  (schmidt at zola.ics.uci.edu)
X
X        * Updated the version number to 1.9.
X
X        * Added support for the -C option.  This makes the contents of
X          all generated tables `readonly'.
X
X        * Changed the handling of generated switches so that there is
X          only one call to str[n]?cmp.  This *greatly* reduces the size of
X          the generated assembly code on all compilers I've seen.
X
X        * Fixed a subtle bug that occurred when the -l and -S option
X          was given.  Code produced looked something like:
X
X          if (len != key_len || !strcmp (s1, resword->name)) return resword;
X
X          which doesn't make any sense.  Clearly, this should be:
X
X          if (len == key_len && !strcmp (s1, resword->name)) return resword;
X
XSat Sep 30 12:55:24 1989  Doug Schmidt  (schmidt at glacier.ics.uci.edu)
X
X        * Fixed a stupid bug in Key_List::print_hash_function that manifested
X          itself if the `-k$' option was given (i.e., only use the key[length]
X          character in the hash function).
X
XMon Jul 24 17:09:46 1989  Doug Schmidt  (schmidt at glacier.ics.uci.edu)
X
X        * Fixed a bug in PRINT_MIN_MAX that resulted in MAX_INT being printed
X          for the MIN_KEY_LEN if there was only 1 keyword in the input file
X          (yeah, that's a pretty unlikely occurrence, I agree!).
X
X        * Fixed PRINT_HASH_FUNCTION and PRINT_LOOKUP_FUNCTION in keylist.c
X          so that the generated functions take an unsigned int length argument.
X          If -a is enabled the prototype is (const char *str, size_t len).
X
XFri Jul 21 13:06:15 1989  Doug Schmidt  (schmidt at zola.ics.uci.edu)
X
X        * Fixed a horrible typo in PRINT_KEYWORD_TABLE in keylist.cc
X          that prevented links from being printed correctly.
X
XSun Jul  9 17:53:28 1989  Doug Schmidt  (schmidt at glacier.ics.uci.edu)
X
X        * Changed the ./tests subdirectory Makefile so that it 
X          uses $(CC) instead of gcc.
X
XSun Jul  2 12:14:04 1989  Doug Schmidt  (schmidt at glacier.ics.uci.edu)
X
X        * Moved comment handling from keylist.c to readline.c.  This
X          simplifies the code and reduces the number of malloc calls.
X
X        * Fixed a number of subtle bugs that occurred when -S was
X          combined with various and sundry options.
X
X        * Added the -G option, that makes the generated keyword table
X          a global static variable, rather than hiding it inside
X          the lookup function.  This allows other functions to directly
X          access the contents in this table.
X
XSat Jul  1 10:12:21 1989  Doug Schmidt  (schmidt at crimee.ics.uci.edu)
X
X        * Added the "#" feature, that allows comments inside the keyword
X          list from the input file.
X          
X        * Also added the -H option (user can give the name of the hash
X          function) and the -T option (prevents the transfer of the type decl
X          to the output file, which is useful if the type is already defined
X          elsewhere).
X
XFri Jun 30 18:22:35 1989  Doug Schmidt  (schmidt at crimee.ics.uci.edu)
X
X        * Added Adam de Boor's changes.  Created an UNSET_OPTION macro.
X
XSat Jun 17 10:56:00 1989  Doug Schmidt  (schmidt at glacier.ics.uci.edu)
X
X        * Modified option.h and option.c so that all mixed operations
X          between integers and enumerals are cast correctly to int.
X          This prevents errors in some brain-damaged C compilers.
X
XFri Jun 16 14:13:15 1989  Doug Schmidt  (schmidt at crimee.ics.uci.edu)
X
X        * Modified the -f (FAST) option.  This now takes an argument.
X          The argument corresponds to the number of iterations used
X          to resolve collisions.  -f 0 uses the length of the
X          keyword list (which is what -f did before).  This makes
X          life much easier when dealing with large keyword files.
X
XWed Jun  7 23:07:13 1989  Doug Schmidt  (schmidt at zola.ics.uci.edu)
X
X        * Updated to version 1.8 in preparation to release to Doug Lea
X          and FSF.
X
X        * Added the -c (comparison) option.  Enabling this
X          will use the strncmp function for string comparisons.
X          The default is to use strcmp.
X
XTue Jun  6 16:32:09 1989  Doug Schmidt  (schmidt at zola.ics.uci.edu)
X
X        * Fixed another stupid typo in xmalloc.c (XMALLOC).  I accidentally
X          left the ANSI-fied prototype in place.  This obviously
X          fails on old-style C compilers.
X
X        * Fixed stupid typos in PRINT_SWITCH from the keylist.c.  This
X          caused the -D option to produce incorrect output when used
X          in conjunction with -p and -t.
X          
X        * Replaced the use of STRCMP with STRNCMP for the generated
X          C output code.          
X
XFri Jun  2 23:16:01 1989  Doug Schmidt  (schmidt at trinite.ics.uci.edu)
X
X        * Added a new function (XMALLOC) and file (xmalloc.c).  All
X          calls to MALLOC were replaced by calls to XMALLOC.  This 
X          will complain when virtual memory runs out (don't laugh, 
X          this has happened!)
X
XThu Jun  1 21:10:10 1989  Doug Schmidt  (schmidt at zola.ics.uci.edu)
X
X        * Fixed a typo in options.c that prevented the -f option
X          from being given on the command-line.
X
XWed May  3 17:48:02 1989  Doug Schmidt  (schmidt at zola.ics.uci.edu)
X
X        * Updated to version 1.7.  This reflects the recent major changes
X          and the new C port.
X
X        * Fixed a typo in perfect.c perfect_destroy that prevented the actual 
X          maximum hash table size from being printed.
X
X        * Added support for the -f option.  This generates the perfect
X          hash function ``fast.''  It reduces the execution time of
X          gperf, at the cost of minimizing the range of hash values.
X
XTue May  2 16:23:29 1989  Doug Schmidt  (schmidt at crimee.ics.uci.edu)
X
X        * Enabled the diagnostics dump if the debugging option is enabled.
X        
X        * Removed all calls to FREE (silly to do this at program termination).
X
X        * Ported gperf to C.  From now on both K&R C and GNU G++ versions
X          will be supported.
X
END_OF_FILE
if test 8012 -ne `wc -c <'cperf/ChangeLog'`; then
    echo shar: \"'cperf/ChangeLog'\" unpacked with wrong size!
fi
# end of 'cperf/ChangeLog'
fi
if test -f 'cperf/src/Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cperf/src/Makefile'\"
else
echo shar: Extracting \"'cperf/src/Makefile'\" \(3414 characters\)
sed "s/^X//" >'cperf/src/Makefile' <<'END_OF_FILE'
X# Copyright (C) 1989 Free Software Foundation, Inc.
X# written by Douglas C. Schmidt (schmidt@ics.uci.edu)
X# 
X# This file is part of GNU GPERF.
X# 
X# GNU GPERF is free software; you can redistribute it and/or modify
X# it under the terms of the GNU General Public License as published by
X# the Free Software Foundation; either version 1, or (at your option)
X# any later version.
X# 
X# GNU GPERF is distributed in the hope that it will be useful,
X# but WITHOUT ANY WARRANTY; without even the implied warranty of
X# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X# GNU General Public License for more details.
X# 
X# You should have received a copy of the GNU General Public License
X# along with GNU GPERF; see the file COPYING.  If not, write to
X# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
X
XCC = cc
XCFLAGS= -O # -g -fstrength-reduce -finline-functions # gcc options 
XOBJS = options.o iterator.o main.o perfect.o keylist.o listnode.o xmalloc.o \
X       hashtable.o boolarray.o readline.o stderr.o version.o getopt.o
XSOURCES = options.c iterator.c main.c perfect.c keylist.c listnode.c xmalloc.c \
X       hashtable.c boolarray.c readline.c stderr.c version.c getopt.c
X
Xall: gperf
X
Xgperf: $(OBJS) 
X	$(CC) $(CFLAGS) -o gperf $(OBJS) $(LIBS)
X
Xclean: 
X	-rm -f *.o core *~ #*#
X
Xrealclean: clean
X	-rm -f gperf
X
X# dependencies
X# DO NOT DELETE THIS LINE -- mkdep uses it.
X# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY.
X
Xboolarray.o: boolarray.c /usr/include/stdio.h boolarray.h prototype.h options.h
Xboolarray.o: /usr/include/stdio.h prototype.h
Xgetopt.o: getopt.c /usr/include/stdio.h
Xhashtable.o: hashtable.c /usr/include/stdio.h hashtable.h keylist.h
Xhashtable.o: /usr/include/stdio.h listnode.h prototype.h prototype.h options.h
Xhashtable.o: /usr/include/stdio.h prototype.h
Xiterator.o: iterator.c /usr/include/stdio.h /usr/include/ctype.h iterator.h
Xiterator.o: prototype.h
Xkeylist.o: keylist.c /usr/include/assert.h /usr/include/stdio.h options.h
Xkeylist.o: /usr/include/stdio.h prototype.h readline.h prototype.h keylist.h
Xkeylist.o: /usr/include/stdio.h listnode.h prototype.h hashtable.h keylist.h
Xkeylist.o: prototype.h stderr.h prototype.h /usr/include/varargs.h
Xlistnode.o: listnode.c /usr/include/stdio.h options.h /usr/include/stdio.h
Xlistnode.o: prototype.h listnode.h prototype.h stderr.h prototype.h
Xlistnode.o: /usr/include/varargs.h
Xmain.o: main.c /usr/include/stdio.h stderr.h prototype.h /usr/include/varargs.h
Xmain.o: options.h /usr/include/stdio.h prototype.h perfect.h prototype.h
Xmain.o: keylist.h /usr/include/stdio.h listnode.h prototype.h boolarray.h
Xmain.o: prototype.h
Xoptions.o: options.c /usr/include/stdio.h /usr/include/assert.h options.h
Xoptions.o: /usr/include/stdio.h prototype.h iterator.h prototype.h stderr.h
Xoptions.o: prototype.h /usr/include/varargs.h
Xperfect.o: perfect.c /usr/include/stdio.h /usr/include/assert.h
Xperfect.o: /usr/include/ctype.h options.h /usr/include/stdio.h prototype.h
Xperfect.o: perfect.h prototype.h keylist.h /usr/include/stdio.h listnode.h
Xperfect.o: prototype.h boolarray.h prototype.h stderr.h prototype.h
Xperfect.o: /usr/include/varargs.h
Xreadline.o: readline.c /usr/include/stdio.h readline.h prototype.h
Xstderr.o: stderr.c /usr/include/stdio.h stderr.h prototype.h
Xstderr.o: /usr/include/varargs.h
Xversion.o: version.c
Xxmalloc.o: xmalloc.c /usr/include/stdio.h
X
X# IF YOU PUT ANYTHING HERE IT WILL GO AWAY
END_OF_FILE
if test 3414 -ne `wc -c <'cperf/src/Makefile'`; then
    echo shar: \"'cperf/src/Makefile'\" unpacked with wrong size!
fi
# end of 'cperf/src/Makefile'
fi
if test -f 'cperf/src/getopt.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cperf/src/getopt.c'\"
else
echo shar: Extracting \"'cperf/src/getopt.c'\" \(12436 characters\)
sed "s/^X//" >'cperf/src/getopt.c' <<'END_OF_FILE'
X/* Getopt for GNU.
X   Copyright (C) 1987, 1989 Free Software Foundation, Inc.
X
X   This program is free software; you can redistribute it and/or modify
X   it under the terms of the GNU General Public License as published by
X   the Free Software Foundation; either version 1, or (at your option)
X   any later version.
X
X   This program is distributed in the hope that it will be useful,
X   but WITHOUT ANY WARRANTY; without even the implied warranty of
X   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
X   GNU General Public License for more details.
X
X   You should have received a copy of the GNU General Public License
X   along with this program; if not, write to the Free Software
X   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
X
X
X
X/* This version of `getopt' appears to the caller like standard Unix `getopt'
X   but it behaves differently for the user, since it allows the user
X   to intersperse the options with the other arguments.
X
X   As `getopt' works, it permutes the elements of `argv' so that,
X   when it is done, all the options precede everything else.  Thus
X   all application programs are extended to handle flexible argument order.
X
X   Setting the environment variable _POSIX_OPTION_ORDER disables permutation.
X   Then the behavior is completely standard.
X
X   GNU application programs can use a third alternative mode in which
X   they can distinguish the relative order of options and other arguments.  */
X
X#include <stdio.h>
X
X#ifdef sparc
X#include <alloca.h>
X#endif
X#ifdef USG
X#define bcopy(s, d, l) memcpy((d), (s), (l))
X#endif
X
X/* For communication from `getopt' to the caller.
X   When `getopt' finds an option that takes an argument,
X   the argument value is returned here.
X   Also, when `ordering' is RETURN_IN_ORDER,
X   each non-option ARGV-element is returned here.  */
X
Xchar *optarg = 0;
X
X/* Index in ARGV of the next element to be scanned.
X   This is used for communication to and from the caller
X   and for communication between successive calls to `getopt'.
X
X   On entry to `getopt', zero means this is the first call; initialize.
X
X   When `getopt' returns EOF, this is the index of the first of the
X   non-option elements that the caller should itself scan.
X
X   Otherwise, `optind' communicates from one call to the next
X   how much of ARGV has been scanned so far.  */
X
Xint optind = 0;
X
X/* The next char to be scanned in the option-element
X   in which the last option character we returned was found.
X   This allows us to pick up the scan where we left off.
X
X   If this is zero, or a null string, it means resume the scan
X   by advancing to the next ARGV-element.  */
X
Xstatic char *nextchar;
X
X/* Callers store zero here to inhibit the error message
X   for unrecognized options.  */
X
Xint opterr = 1;
X
X/* Describe how to deal with options that follow non-option ARGV-elements.
X
X   UNSPECIFIED means the caller did not specify anything;
X   the default is then REQUIRE_ORDER if the environment variable
X   _OPTIONS_FIRST is defined, PERMUTE otherwise.
X
X   REQUIRE_ORDER means don't recognize them as options.
X   Stop option processing when the first non-option is seen.
X   This is what Unix does.
X
X   PERMUTE is the default.  We permute the contents of `argv' as we scan,
X   so that eventually all the options are at the end.  This allows options
X   to be given in any order, even with programs that were not written to
X   expect this.
X
X   RETURN_IN_ORDER is an option available to programs that were written
X   to expect options and other ARGV-elements in any order and that care about
X   the ordering of the two.  We describe each non-option ARGV-element
X   as if it were the argument of an option with character code zero.
X   Using `-' as the first character of the list of option characters
X   requests this mode of operation.
X
X   The special argument `--' forces an end of option-scanning regardless
X   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
X   `--' can cause `getopt' to return EOF with `optind' != ARGC.  */
X
Xstatic enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering;
X
X/* Handle permutation of arguments.  */
X
X/* Describe the part of ARGV that contains non-options that have
X   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
X   `last_nonopt' is the index after the last of them.  */
X
Xstatic int first_nonopt;
Xstatic int last_nonopt;
X
X/* Exchange two adjacent subsequences of ARGV.
X   One subsequence is elements [first_nonopt,last_nonopt)
X    which contains all the non-options that have been skipped so far.
X   The other is elements [last_nonopt,optind), which contains all
X    the options processed since those non-options were skipped.
X
X   `first_nonopt' and `last_nonopt' are relocated so that they describe
X    the new indices of the non-options in ARGV after they are moved.  */
X
Xstatic void
Xexchange (argv)
X     char **argv;
X{
X  int nonopts_size
X    = (last_nonopt - first_nonopt) * sizeof (char *);
X  char **temp = (char **) alloca (nonopts_size);
X
X  /* Interchange the two blocks of data in argv.  */
X
X  bcopy (&argv[first_nonopt], temp, nonopts_size);
X  bcopy (&argv[last_nonopt], &argv[first_nonopt],
X	 (optind - last_nonopt) * sizeof (char *));
X  bcopy (temp, &argv[first_nonopt + optind - last_nonopt],
X	 nonopts_size);
X
X  /* Update records for the slots the non-options now occupy.  */
X
X  first_nonopt += (optind - last_nonopt);
X  last_nonopt = optind;
X}
X
X/* Scan elements of ARGV (whose length is ARGC) for option characters
X   given in OPTSTRING.
X
X   If an element of ARGV starts with '-', and is not exactly "-" or "--",
X   then it is an option element.  The characters of this element
X   (aside from the initial '-') are option characters.  If `getopt'
X   is called repeatedly, it returns successively each of theoption characters
X   from each of the option elements.
X
X   If `getopt' finds another option character, it returns that character,
X   updating `optind' and `nextchar' so that the next call to `getopt' can
X   resume the scan with the following option character or ARGV-element.
X
X   If there are no more option characters, `getopt' returns `EOF'.
X   Then `optind' is the index in ARGV of the first ARGV-element
X   that is not an option.  (The ARGV-elements have been permuted
X   so that those that are not options now come last.)
X
X   OPTSTRING is a string containing the legitimate option characters.
X   A colon in OPTSTRING means that the previous character is an option
X   that wants an argument.  The argument is taken from the rest of the
X   current ARGV-element, or from the following ARGV-element,
X   and returned in `optarg'.
X
X   If an option character is seen that is not listed in OPTSTRING,
X   return '?' after printing an error message.  If you set `opterr' to
X   zero, the error message is suppressed but we still return '?'.
X
X   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
X   so the following text in the same ARGV-element, or the text of the following
X   ARGV-element, is returned in `optarg.  Two colons mean an option that
X   wants an optional arg; if there is text in the current ARGV-element,
X   it is returned in `optarg'.
X
X   If OPTSTRING starts with `-', it requests a different method of handling the
X   non-option ARGV-elements.  See the comments about RETURN_IN_ORDER, above.  */
X
Xint
Xgetopt (argc, argv, optstring)
X     int argc;
X     char **argv;
X     char *optstring;
X{
X  /* Initialize the internal data when the first call is made.
X     Start processing options with ARGV-element 1 (since ARGV-element 0
X     is the program name); the sequence of previously skipped
X     non-option ARGV-elements is empty.  */
X
X  if (optind == 0)
X    {
X      first_nonopt = last_nonopt = optind = 1;
X
X      nextchar = 0;
X
X      /* Determine how to handle the ordering of options and nonoptions.  */
X
X      if (optstring[0] == '-')
X	ordering = RETURN_IN_ORDER;
X      else if (getenv ("_POSIX_OPTION_ORDER") != 0)
X	ordering = REQUIRE_ORDER;
X      else
X	ordering = PERMUTE;
X    }
X
X  if (nextchar == 0 || *nextchar == 0)
X    {
X      if (ordering == PERMUTE)
X	{
X	  /* If we have just processed some options following some non-options,
X	     exchange them so that the options come first.  */
X
X	  if (first_nonopt != last_nonopt && last_nonopt != optind)
X	    exchange (argv);
X	  else if (last_nonopt != optind)
X	    first_nonopt = optind;
X
X	  /* Now skip any additional non-options
X	     and extend the range of non-options previously skipped.  */
X
X	  while (optind < argc
X		 && (argv[optind][0] != '-'
X		     || argv[optind][1] == 0))
X	    optind++;
X	  last_nonopt = optind;
X	}
X
X      /* Special ARGV-element `--' means premature end of options.
X	 Skip it like a null option,
X	 then exchange with previous non-options as if it were an option,
X	 then skip everything else like a non-option.  */
X
X      if (optind != argc && !strcmp (argv[optind], "--"))
X	{
X	  optind++;
X
X	  if (first_nonopt != last_nonopt && last_nonopt != optind)
X	    exchange (argv);
X	  else if (first_nonopt == last_nonopt)
X	    first_nonopt = optind;
X	  last_nonopt = argc;
X
X	  optind = argc;
X	}
X
X      /* If we have done all the ARGV-elements, stop the scan
X	 and back over any non-options that we skipped and permuted.  */
X
X      if (optind == argc)
X	{
X	  /* Set the next-arg-index to point at the non-options
X	     that we previously skipped, so the caller will digest them.  */
X	  if (first_nonopt != last_nonopt)
X	    optind = first_nonopt;
X	  return EOF;
X	}
X	 
X      /* If we have come to a non-option and did not permute it,
X	 either stop the scan or describe it to the caller and pass it by.  */
X
X      if (argv[optind][0] != '-' || argv[optind][1] == 0)
X	{
X	  if (ordering == REQUIRE_ORDER)
X	    return EOF;
X	  optarg = argv[optind++];
X	  return 0;
X	}
X
X      /* We have found another option-ARGV-element.
X	 Start decoding its characters.  */
X
X      nextchar = argv[optind] + 1;
X    }
X
X  /* Look at and handle the next option-character.  */
X
X  {
X    char c = *nextchar++;
X    char *temp = (char *) index (optstring, c);
X
X    /* Increment `optind' when we start to process its last character.  */
X    if (*nextchar == 0)
X      optind++;
X
X    if (temp == 0 || c == ':')
X      {
X	if (opterr != 0)
X	  {
X	    if (c < 040 || c >= 0177)
X	      fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
X		       argv[0], c);
X	    else
X	      fprintf (stderr, "%s: unrecognized option `-%c'\n",
X		       argv[0], c);
X	  }
X	return '?';
X      }
X    if (temp[1] == ':')
X      {
X	if (temp[2] == ':')
X	  {
X	    /* This is an option that accepts an argument optionally.  */
X	    if (*nextchar != 0)
X	      {
X	        optarg = nextchar;
X		optind++;
X	      }
X	    else
X	      optarg = 0;
X	    nextchar = 0;
X	  }
X	else
X	  {
X	    /* This is an option that requires an argument.  */
X	    if (*nextchar != 0)
X	      {
X		optarg = nextchar;
X		/* If we end this ARGV-element by taking the rest as an arg,
X		   we must advance to the next element now.  */
X		optind++;
X	      }
X	    else if (optind == argc)
X	      {
X		if (opterr != 0)
X		  fprintf (stderr, "%s: no argument for `-%c' option\n",
X			   argv[0], c);
X		c = '?';
X	      }
X	    else
X	      /* We already incremented `optind' once;
X		 increment it again when taking next ARGV-elt as argument.  */
X	      optarg = argv[optind++];
X	    nextchar = 0;
X	  }
X      }
X    return c;
X  }
X}
X
X#ifdef TEST
X
X/* Compile with -DTEST to make an executable for use in testing
X   the above definition of `getopt'.  */
X
Xint
Xmain (argc, argv)
X     int argc;
X     char **argv;
X{
X  char c;
X  int digit_optind = 0;
X
X  while (1)
X    {
X      int this_option_optind = optind;
X      if ((c = getopt (argc, argv, "abc:d:0123456789")) == EOF)
X	break;
X
X      switch (c)
X	{
X	case '0':
X	case '1':
X	case '2':
X	case '3':
X	case '4':
X	case '5':
X	case '6':
X	case '7':
X	case '8':
X	case '9':
X	  if (digit_optind != 0 && digit_optind != this_option_optind)
X	    printf ("digits occur in two different argv-elements.\n");
X	  digit_optind = this_option_optind;
X	  printf ("option %c\n", c);
X	  break;
X
X	case 'a':
X	  printf ("option a\n");
X	  break;
X
X	case 'b':
X	  printf ("option b\n");
X	  break;
X
X	case 'c':
X	  printf ("option c with value `%s'\n", optarg);
X	  break;
X
X	case '?':
X	  break;
X
X	default:
X	  printf ("?? getopt returned character code 0%o ??\n", c);
X	}
X    }
X
X  if (optind < argc)
X    {
X      printf ("non-option ARGV-elements: ");
X      while (optind < argc)
X	printf ("%s ", argv[optind++]);
X      printf ("\n");
X    }
X
X  return 0;
X}
X
X#endif /* TEST */
END_OF_FILE
if test 12436 -ne `wc -c <'cperf/src/getopt.c'`; then
    echo shar: \"'cperf/src/getopt.c'\" unpacked with wrong size!
fi
# end of 'cperf/src/getopt.c'
fi
if test -f 'cperf/src/hashtable.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cperf/src/hashtable.c'\"
else
echo shar: Extracting \"'cperf/src/hashtable.c'\" \(3281 characters\)
sed "s/^X//" >'cperf/src/hashtable.c' <<'END_OF_FILE'
X/* Hash table for checking keyword links.  Implemented using double hashing.
X   Copyright (C) 1989 Free Software Foundation, Inc.
X   written by Douglas C. Schmidt (schmidt@ics.uci.edu)
X
XThis file is part of GNU GPERF.
X
XGNU GPERF is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU GPERF is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU GPERF; see the file COPYING.  If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
X
X#include <stdio.h>
X#include "hashtable.h"
X#include "options.h"
X
X/* Locally visible hash table. */
Xstatic HASH_TABLE hash_table;
X
X#define POW(X) ((!X)?1:(X-=1,X|=X>>1,X|=X>>2,X|=X>>4,X|=X>>8,X|=X>>16,(++X)))
X
Xstatic unsigned
Xhash_pjw (str)
X     char *str;
X{
X  char    *temp;
X  unsigned g, h = 0;
X   
X  for (temp = str; *temp; temp++) 
X    {
X      h = (h << 4) + (*temp * 13);
X      if (g = h & 0xf0000000) 
X        {
X          h ^= (g >> 24);
X          h ^= g;
X        }
X    }
X
X  return h;
X}
X
X/* The size of the hash table is always the smallest power of 2 >= the size
X   indicated by the user.  this allows several optimizations, including
X   the use of double hashing and elimination of the mod instruction.
X   note that the size had better be larger than the number of items
X   in the hash table, else there's trouble!!! */
X
Xvoid
Xhash_table_init (s)
X     int s;
X{
X	char *xmalloc ();
X  hash_table.size  = POW (s);
X  hash_table.table = (LIST_NODE **) xmalloc (hash_table.size * sizeof (*hash_table.table));
X  bzero ((char *) hash_table.table, hash_table.size * sizeof *hash_table.table);
X}
X
X/* Frees the dynamically allocated table. */
X
Xvoid
Xhash_table_destroy ()
X{ 
X  if (OPTION_ENABLED (option, DEBUG))
X    {
X      int i;
X
X      fprintf (stderr, "\ndumping the hash table\n");
X    
X      for (i = hash_table.size - 1; i >= 0; i--)
X        if (hash_table.table[i])
X          fprintf (stderr, "location[%d] = key set %s, key %s\n",
X                   i, hash_table.table[i]->key_set, hash_table.table[i]->key);
X
X      fprintf (stderr, "end dumping hash table\n\n");
X    }
X}
X
X/* If the ITEM is already in the hash table return the item found
X   in the table.  Otherwise inserts the ITEM, and returns FALSE.
X   Uses double hashing. */
X
XLIST_NODE *
Xretrieve (item, ignore_length)
X     LIST_NODE *item;
X     int        ignore_length;
X{
X  unsigned hash_val  = hash_pjw (item->key_set);
X  int      probe     = hash_val & hash_table.size - 1;
X  int      increment = (hash_val ^ item->length | 1) & hash_table.size - 1;
X  
X  while (hash_table.table[probe]
X         && (strcmp (hash_table.table[probe]->key_set, item->key_set)
X             || (!ignore_length && hash_table.table[probe]->length != item->length)))
X    probe = probe + increment & hash_table.size - 1;
X  
X  if (hash_table.table[probe])
X    return hash_table.table[probe];
X  else
X    {
X      hash_table.table[probe] = item;
X      return 0;
X    }
X}
X
X
END_OF_FILE
if test 3281 -ne `wc -c <'cperf/src/hashtable.c'`; then
    echo shar: \"'cperf/src/hashtable.c'\" unpacked with wrong size!
fi
# end of 'cperf/src/hashtable.c'
fi
if test -f 'cperf/src/listnode.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cperf/src/listnode.c'\"
else
echo shar: Extracting \"'cperf/src/listnode.c'\" \(4294 characters\)
sed "s/^X//" >'cperf/src/listnode.c' <<'END_OF_FILE'
X/* Creates and initializes a new list node.
X   Copyright (C) 1989 Free Software Foundation, Inc.
X   written by Douglas C. Schmidt (schmidt@ics.uci.edu)
X
XThis file is part of GNU GPERF.
X
XGNU GPERF is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU GPERF is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU GPERF; see the file COPYING.  If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
X
X#include <stdio.h>
X#include "options.h"
X#include "listnode.h"
X#include "stderr.h"
X
X/* See comments in perfect.cc. */
Xextern int occurrences[ALPHABET_SIZE]; 
X
X/* Sorts the key set alphabetically to speed up subsequent operations.
X   Uses insertion sort since the set is probably quite small. */
X
Xstatic void 
Xset_sort (base, len)
X     char *base;
X     int len;
X{
X  int i, j;
X
X  for (i = 0, j = len - 1; i < j; i++)
X    {
X      char curr, tmp;
X      
X      for (curr = i + 1, tmp = base[curr]; curr > 0 && tmp < base[curr-1]; curr--)
X        base[curr] = base[curr - 1];
X
X      base[curr] = tmp;
X
X    }
X}
X
X/* Initializes a List_Node.  This requires obtaining memory for the KEY_SET
X   and UNIQ_SET, initializing them using the information stored in the
X   KEY_POSITIONS array in Options, and checking for simple errors.
X   It's important to note that KEY and REST are both pointers to
X   the different offsets into the same block of dynamic memory pointed to
X   by parameter K. The data member REST is used to store any additional fields 
X   of the input file (it is set to the "" string if Option[TYPE] is not enabled).
X   This is useful if the user wishes to incorporate a lookup structure,
X   rather than just an array of keys. */
X
XLIST_NODE *
Xmake_list_node (k, len)
X     char *k;
X     int len;
X{
X	char *xmalloc ();
X  LIST_NODE *temp = (LIST_NODE *) xmalloc (sizeof (LIST_NODE));
X  int positions  = 1 + (OPTION_ENABLED (option, ALLCHARS) ? len : TOTAL_POSITIONS (option) + 1);
X  char *ptr, *ptr1, *ptr2;
X
X  temp->key_set  = (char *) xmalloc (positions + positions); /* Save 1 call to new. */
X  temp->uniq_set = temp->key_set + positions;
X  ptr            = temp->key_set;
X  k[len]         = '\0';        /* Null terminate KEY to separate it from REST. */
X  temp->key      = k;
X  temp->next     = 0;
X  temp->index    = 0;
X  temp->length   = len;
X  temp->link     = 0;
X  temp->rest     = OPTION_ENABLED (option, TYPE) ? k + len + 1 : "";
X
X  if (OPTION_ENABLED (option, ALLCHARS)) /* Use all the character position in the KEY. */
X
X    for (; *k; k++, ptr++)
X      ++occurrences[*ptr = *k];
X
X  else                          /* Only use those character positions specified by the user. */
X    {                           
X      int i;
X
X      /* Iterate thru the list of key_positions, initializing occurrences table
X         and temp->key_set (via char * pointer ptr). */
X
X      for(RESET (option); (i = GET (option)) != EOS; )
X        {
X          if (i == WORD_END)    /* Special notation for last KEY position, i.e. '$'. */
X            *ptr = temp->key[len - 1];
X          else if (i <= len)    /* Within range of KEY length, so we'll keep it. */
X            *ptr = temp->key[i - 1];
X          else                  /* Out of range of KEY length, so we'll just skip it. */
X            continue;
X          ++occurrences[*ptr++];
X        }
X
X      if (ptr == temp->key_set) /* Didn't get any hits, i.e., no usable positions. */
X        report_error ("can't hash keyword %s with chosen key positions\n%a", temp->key);
X    }
X
X  *ptr = '\0';                  /* Terminate this bastard.... */
X  set_sort (temp->key_set, ptr - temp->key_set); /* Sort the KEY_SET items alphabetically. */
X
X  /* Eliminate UNIQ_SET duplicates, this saves time later on.... */
X
X  for (ptr1 = temp->key_set, ptr2 = temp->uniq_set; *ptr1; ptr1++)
X    if (*ptr1 != ptr1[1])
X      *ptr2++ = *ptr1;
X
X  *ptr2 = '\0';                 /* NULL terminate the UNIQ_SET. */
X  return temp;
X}
END_OF_FILE
if test 4294 -ne `wc -c <'cperf/src/listnode.c'`; then
    echo shar: \"'cperf/src/listnode.c'\" unpacked with wrong size!
fi
# end of 'cperf/src/listnode.c'
fi
if test -f 'cperf/src/options.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cperf/src/options.h'\"
else
echo shar: Extracting \"'cperf/src/options.h'\" \(5762 characters\)
sed "s/^X//" >'cperf/src/options.h' <<'END_OF_FILE'
X/* Handles parsing the Options provided to the user.
X
X   Copyright (C) 1989 Free Software Foundation, Inc.
X   written by Douglas C. Schmidt (schmidt@ics.uci.edu)
X
XThis file is part of GNU GPERF.
X
XGNU GPERF is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU GPERF is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU GPERF; see the file COPYING.  If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
X
X/* This module provides a uniform interface to the various Options available
X   to a user of the Perfect.hash function generator.  In addition to the
X   run-time Options, found in the Option_Type below, there is also the
X   hash table Size and the Keys to be used in the hashing.
X   The overall design of this module was an experiment in using C++
X   classes as a mechanism to enhance centralization of option and
X   and error handling, which tend to get out of hand in a C program. */
X
X#ifndef _options_h
X#define _options_h
X
X#include <stdio.h>
X#include "prototype.h"
X
X/* Enumerate the potential debugging Options. */
X
Xenum option_type 
X{
X  DEBUG        = 01,            /* Enable debugging (prints diagnostics to Std_Err). */
X  ORDER        = 02,            /* Apply ordering heuristic to speed-up search time. */
X  ANSI         = 04,            /* Generate ANSI prototypes. */
X  ALLCHARS     = 010,           /* Use all characters in hash function. */
X  GNU          = 020,           /* Assume GNU extensions (primarily function inline). */
X  TYPE         = 040,           /* Handle user-defined type structured keyword input. */
X  RANDOM       = 0100,          /* Randomly initialize the associated values table. */
X  DEFAULTCHARS = 0200,          /* Make default char positions be 1,$ (end of keyword). */
X  SWITCH       = 0400,          /* Generate switch output to save space. */
X  POINTER      = 01000,         /* Have in_word_set function return pointer, not boolean. */
X  NOLENGTH     = 02000,         /* Don't include keyword length in hash computations. */
X  LENTABLE     = 04000,         /* Generate a length table for string comparison. */
X  DUP          = 010000,        /* Handle duplicate hash values for keywords. */
X  FAST         = 020000,        /* Generate the hash function ``fast.'' */
X  NOTYPE       = 040000,	/* Don't include user-defined type definition
X				 * in output -- it's already defined elsewhere. */
X  COMP         = 0100000,       /* Generate strncmp rather than strcmp. */
X  GLOBAL       = 0200000,       /* Make the keyword table a global variable. */
X  CONST        = 0400000,       /* Make the generated tables readonly (const). */
X};
X
X/* Define some useful constants. */
X
X/* Max size of each word's key set. */
X#define MAX_KEY_POS (128 - 1)
X
X/* Signals the start of a word. */
X#define WORD_START 1           
X
X/* Signals the end of a word. */
X#define WORD_END 0             
X
X/* Signals end of the key list. */
X#define EOS MAX_KEY_POS        
X
X/* Returns TRUE if option O is enabled. */
X#define OPTION_ENABLED(OW,O) (OW.option_word & (int)O)
X
X/* Enables option O in OPTION_WORD. */
X#define SET_OPTION(OW,O) (OW.option_word |= (int)O)
X
X/* Disable option O in OPTION_WORD. */
X#define UNSET_OPTION(OW,O) (OW.option_word &= ~(int)(O))
X
X/* Returns total distinct key positions. */
X#define TOTAL_POSITIONS(O) (O.total_key_positions)
X
X/* Initializes the key Iterator. */
X#define RESET(O) (O.key_pos = 0)
X
X/* Returns current key_position and advances index. */
X#define GET(O) (O.key_positions[O.key_pos++])
X
X/* Sets the size of the table size. */
X#define SET_ASSO_MAX(O,R) (O.size = (R))
X
X/* Returns the size of the table size. */
X#define GET_ASSO_MAX(O) (O.size)
X
X/* Returns the jump value. */
X#define GET_JUMP(O) (O.jump)
X
X/* Returns the iteration value. */
X#define GET_ITERATIONS(O) (O.iterations)
X
X/* Returns the lookup function name. */
X#define GET_FUNCTION_NAME(O) (O.function_name)
X
X/* Returns the keyword key name. */
X#define GET_KEY_NAME(O) (O.key_name)
X
X/* Returns the hash function name. */
X#define GET_HASH_NAME(O) (O.hash_name)
X
X/* Returns the initial associated character value. */
X#define INITIAL_VALUE(O) (O.initial_asso_value)
X
X/* Class manager for gperf program options. */
X
Xtypedef struct options
X{
X  int    option_word;           /* Holds the user-specified Options. */
X  int    total_key_positions;   /* Total number of distinct key_positions. */
X  int    size;                  /* Range of the hash table. */
X  int    key_pos;               /* Tracks current key position for Iterator. */
X  int    jump;                  /* Jump length when trying alternative values. */
X  int    initial_asso_value;    /* Initial value for asso_values table. */
X  int    argument_count;        /* Records count of command-line arguments. */
X  int    iterations;            /* Amount to iterate when a collision occurs. */
X  char **argument_vector;       /* Stores a pointer to command-line vector. */
X  char  *function_name;         /* Name used for generated lookup function. */
X  char  *key_name;              /* Name used for keyword key. */
X  char  *hash_name;             /* Name used for generated hash function. */
X  char   key_positions[MAX_KEY_POS]; /* Contains user-specified key choices. */
X} OPTIONS;
X
Xextern void    options_init P ((int argc, char *argv[]));
Xextern void    options_destroy P ((void));
Xextern OPTIONS option;       
X#endif /* _options_h */
END_OF_FILE
if test 5762 -ne `wc -c <'cperf/src/options.h'`; then
    echo shar: \"'cperf/src/options.h'\" unpacked with wrong size!
fi
# end of 'cperf/src/options.h'
fi
if test -f 'cperf/src/perfect.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cperf/src/perfect.c'\"
else
echo shar: Extracting \"'cperf/src/perfect.c'\" \(9830 characters\)
sed "s/^X//" >'cperf/src/perfect.c' <<'END_OF_FILE'
X/* Provides high-level routines to manipulate the keywork list 
X   structures the code generation output.
X   Copyright (C) 1989 Free Software Foundation, Inc.
X   written by Douglas C. Schmidt (schmidt@ics.uci.edu)
X
XThis file is part of GNU GPERF.
X
XGNU GPERF is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU GPERF is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU GPERF; see the file COPYING.  If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
X
X#include <stdio.h>
X#include <assert.h>
X#include <ctype.h>
X#include "options.h"
X#include "perfect.h"
X#include "stderr.h"
X
Xint occurrences[ALPHABET_SIZE]; /* Counts occurrences of each key set character. */
Xint asso_values[ALPHABET_SIZE]; /* Value associated with each character. */
X
X/* Locally visible PERFECT object. */
X
XPERFECT perfect;
X
X/* Efficiently returns the least power of two greater than or equal to X! */
X#define POW(X) ((!X)?1:(X-=1,X|=X>>1,X|=X>>2,X|=X>>4,X|=X>>8,X|=X>>16,(++X)))
X
X/* Reads input keys, possibly applies the reordering heuristic, sets the
X   maximum associated value size (rounded up to the nearest power of 2),
X   may initialize the associated values array, and determines the maximum
X   hash table size.  Note: using the random numbers is often helpful,
X   though not as deterministic, of course! */
X
Xvoid
Xperfect_init ()
X{
X  int asso_value_max;
X  int len;
X
X  perfect.best_char_choice = 0;
X  perfect.best_asso_value = 0;
X  read_keys ();
X  if (OPTION_ENABLED (option, ORDER))
X    reorder ();
X
X  asso_value_max = GET_ASSO_MAX (option);
X  len            = length ();
X  
X  asso_value_max = (asso_value_max ? asso_value_max * len : len);
X  SET_ASSO_MAX (option, POW (asso_value_max));
X  
X  if (OPTION_ENABLED (option, RANDOM))
X    {
X      int i;
X
X      srandom (time (0));
X      
X      for (i = 0; i < ALPHABET_SIZE; i++)
X        asso_values[i] = (random () & asso_value_max - 1);
X      
X    }
X  else
X    {
X      int asso_value = INITIAL_VALUE (option);
X      
X      if (asso_value)           /* Initialize array if user requests non-zero default. */
X        {
X          int i;
X
X          for (i = ALPHABET_SIZE - 1; i >= 0; i--)
X            asso_values[i] = asso_value & GET_ASSO_MAX (option) - 1;
X
X        }
X    }
X  perfect.max_hash_value = max_key_length () + GET_ASSO_MAX (option) * 
X    (OPTION_ENABLED (option, ALLCHARS) ? max_key_length () : TOTAL_POSITIONS (option));
X  
X  if (OPTION_ENABLED (option, DEBUG))
X    fprintf (stderr, "number of keys = %d\nmaximum associated value is %d\
X\nmaximum size of hash table is %d\n", len, asso_value_max, perfect.max_hash_value);
X}
X
X/* Merge two disjoint hash key sets to form the ordered union of the sets.
X   Precondition: both set_1 and set_2 must be ordered. Returns the length
X   of the combined set. */
X
Xstatic int 
Xmerge_sets (set_1, set_2, set_3)
X     char *set_1;
X     char *set_2;
X     char *set_3;
X{
X  char *base = set_3;
X  
X  while (*set_1 && *set_2)
X    if (*set_1 == *set_2)
X      *set_3++ = *set_1++, set_2++; 
X    else
X      *set_3++ = *set_1 < *set_2 ? *set_1++ : *set_2++;
X  
X  while (*set_1)
X    *set_3++ = *set_1++; 
X  
X  while (*set_2)
X    *set_3++ = *set_2++; 
X  
X  *set_3 = '\0';
X  return set_3 - base;
X}
X
X/* Sort the UNION_SET in increasing frequency of occurrence.
X   This speeds up later processing since we may assume the resulting
X   set (Set_3, in this case), is ordered. Uses insertion sort, since
X   the UNION_SET is typically short. */
X  
Xstatic void 
Xsort_set (union_set, len)
X     char *union_set;
X     int   len;
X{
X  int i, j;
X  
X  for (i = 0, j = len - 1; i < j; i++)
X    {
X      char curr, tmp;
X
X      for (curr = i+1, tmp = union_set[curr]; 
X           curr>0 && occurrences[tmp] < occurrences[union_set[curr-1]]; 
X           curr--)
X        union_set[curr] = union_set[curr - 1];
X      
X      union_set[curr] = tmp;
X    }
X}
X
X/* Generate a key set's hash value. */
X
Xstatic int 
Xhash (key_node)
X     LIST_NODE *key_node;
X{                             
X  int   sum = OPTION_ENABLED (option, NOLENGTH) ? 0 : key_node->length;
X  char *ptr;
X
X  for (ptr = key_node->key_set; *ptr; ptr++)
X      sum += asso_values[*ptr];
X  
X  return key_node->hash_value = sum;
X}
X
X/* Find out how character value change affects successfully hashed items.
X   Returns FALSE if no other hash values are affected, else returns TRUE.
X   Note that because Option.Get_Asso_Max is a power of two we can guarantee
X   that all legal Asso_Values are visited without repetition since
X   Option.Get_Jump was forced to be an odd value! */
X
Xstatic bool 
Xaffects_prev (c, curr)
X     char c;
X     LIST_NODE *curr;
X{
X  int original_char = asso_values[c];
X  int i = !OPTION_ENABLED (option, FAST) ? GET_ASSO_MAX (option) :
X    GET_ITERATIONS (option) == 0 ? key_list.list_len : GET_ITERATIONS (option);
X
X  /* Try all legal asso_values. */
X
X  while (--i >= 0)
X    { 
X      int        number_of_hits = 0;
X      LIST_NODE *ptr;
X
X      asso_values[c] = asso_values[c] + (OPTION_ENABLED (option, FAST) ? random () : GET_JUMP (option))
X        & GET_ASSO_MAX (option) - 1;
X      bool_array_reset ();       /* Ullman's array is a win, O(1) intialization time! */
X      
X      for (ptr = key_list.head; ; ptr = ptr->next)
X        {
X          if (lookup (hash (ptr)) && ++number_of_hits >= perfect.fewest_hits)
X            goto bottom_of_main_loop;
X          if (ptr == curr)
X            break;
X        }    
X      
X      perfect.best_asso_value  = asso_values[perfect.best_char_choice = c];
X      if ((perfect.fewest_hits = number_of_hits) == 0) /* Use if 0 hits for this value. */
X        return FALSE;        
X      
X    bottom_of_main_loop: ;
X    }
X  
X  asso_values[c] = original_char; /* Restore original values, no more tries. */
X  return TRUE; /* If we're this far it's time to try the next character.... */
X}
X
X#ifdef sparc
X#include <alloca.h>
X#endif
X
X/* Change a character value, try least-used characters first. */
X
Xstatic void 
Xchange (prior, curr)
X     LIST_NODE *prior;
X     LIST_NODE *curr;
X{
X  char      *union_set = (char *) alloca (2 * TOTAL_POSITIONS (option) + 1);
X  int        i = 1;
X  char      *temp;
X  LIST_NODE *ptr;
X
X  if (OPTION_ENABLED (option, DEBUG))        /* Very useful for debugging. */
X    fprintf (stderr, "collision, prior->key = %s, curr->key = %s, hash_value = %d\n",
X             prior->key, curr->key, curr->hash_value);
X  sort_set (union_set, merge_sets (prior->uniq_set, curr->uniq_set, union_set));
X  
X  /* Try changing some values, if change doesn't alter other values continue normal action. */
X  
X  perfect.fewest_hits = length ();
X  
X  for (temp = union_set; *temp; temp++)
X    if (!affects_prev (*temp, curr))
X      return; /* Good, doesn't affect previous hash values, we'll take it. */
X  
X  asso_values[perfect.best_char_choice] = perfect.best_asso_value; /* If none work take best value. */
X  
X  for (ptr = key_list.head; ; ptr = ptr->next, i++)
X    {
X      hash (ptr);
X      if (ptr == curr)
X        break;
X    }           
X  
X  if (OPTION_ENABLED (option, DEBUG))
X    fprintf (stderr, "changes on %d hash values still produce duplicates, continuing...\n", i);
X}
X
X/* Does the hard stuff....
X   Initializes the Ullman_Array, and then attempts to find a Perfect
X   function that will hash all the key words without getting any
X   duplications.  This is made much easier since we aren't attempting
X   to generate *minimum* functions, only Perfect ones.
X   If we can't generate a Perfect function in one pass *and* the user
X   hasn't enabled the DUP option, we'll inform the user to try the
X   randomization option, use -D, or choose alternative key positions.  
X   The alternatives (e.g., back-tracking) are too time-consuming, i.e,
X   exponential in the number of keys. */
X
Xint
Xperfect_generate ()
X{
X  LIST_NODE *curr;
X  bool_array_init (perfect.max_hash_value);
X  hash (key_list.head);
X  
X  for (curr = key_list.head->next; curr; curr = curr->next)
X    {
X      LIST_NODE *ptr;
X      hash (curr);
X      
X      for (ptr = key_list.head; ptr != curr; ptr = ptr->next)
X        if (ptr->hash_value == curr->hash_value)
X          {
X            change (ptr, curr);
X            break;
X          }
X    } 
X  
X  /* Make one final check, just to make sure nothing weird happened.... */
X  
X  for (bool_array_reset (), curr = key_list.head; curr; curr = curr->next)
X    if (lookup (hash (curr)))
X      if (OPTION_ENABLED (option, DUP)) /* We'll try to deal with this later..... */
X        break;
X      else /* Yow, big problems.  we're outta here! */
X        { 
X          report_error ("\nInternal error, duplicate value %d:\ntry options -D or -r, or use new key positions.\n\n", 
X                        hash (curr));
X          return 1;
X        }
X
X  /* First sorts the key word list by hash value, and the outputs the
X     list to the proper ostream. The generated hash table code is only 
X     output if the early stage of processing turned out O.K. */
X
X  sort ();
X  print_output ();
X  return 0;
X}
X
X/* Prints out some diagnostics upon completion. */
X
Xvoid 
Xperfect_destroy ()
X{                             
X  if (OPTION_ENABLED (option, DEBUG))
X    {
X      int i;
X
X      fprintf (stderr, "\ndumping occurrence and associated values tables\n");
X      
X      for (i = 0; i < ALPHABET_SIZE; i++)
X        if (occurrences[i])
X          fprintf (stderr, "asso_values[%c] = %3d, occurrences[%c] = %3d\n",
X                   i, asso_values[i], i, occurrences[i]);
X      
X      fprintf (stderr, "end table dumping\n");
X      
X    }
X}
X
END_OF_FILE
if test 9830 -ne `wc -c <'cperf/src/perfect.c'`; then
    echo shar: \"'cperf/src/perfect.c'\" unpacked with wrong size!
fi
# end of 'cperf/src/perfect.c'
fi
echo shar: End of archive 2 \(of 5\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 5 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 5 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.