diff options
Diffstat (limited to 'contrib/gperf/src/key-list.cc')
-rw-r--r-- | contrib/gperf/src/key-list.cc | 1957 |
1 files changed, 1957 insertions, 0 deletions
diff --git a/contrib/gperf/src/key-list.cc b/contrib/gperf/src/key-list.cc new file mode 100644 index 0000000..38341f3 --- /dev/null +++ b/contrib/gperf/src/key-list.cc @@ -0,0 +1,1957 @@ +/* Routines for building, ordering, and printing the keyword list. + 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 <stdio.h> +#include <string.h> /* declares strncpy(), strchr() */ +#include <stdlib.h> /* declares malloc(), free(), abs(), exit(), abort() */ +#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; + } +} + +/* 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 = new List_Node (ptr, strcspn (ptr, delimiter)); + + for (temp = head; + (ptr = Read_Line::get_line ()) && strcmp (ptr, "%%"); + temp = temp->next) + { + temp->next = new List_Node (ptr, strcspn (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); + + /* 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 (temp, 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) + { + 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, ptr->key, temp->char_set); + } + else + trail = temp; + + /* Update minimum and maximum keyword length, if needed. */ + if (max_key_len < temp->length) + max_key_len = temp->length; + if (min_key_len > temp->length) + min_key_len = temp->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); + } + } + 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; + + for (const char *temp = ptr->char_set; *temp; temp++) + value += occurrences[(unsigned char)(*temp)]; + + 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");) + for (const char *temp = ptr->char_set; *temp; temp++) + determined[(unsigned char)(*temp)] = 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; + + for (const char *temp = ptr->char_set; is_determined && *temp; temp++) + is_determined = determined[(unsigned char)(*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 +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 and double quote characters. */ + +static void +output_string (const char *key) +{ + T (Trace t ("output_string");) + char c; + + putchar ('"'); + while (c = *key++, c != '\0') + { + if (c == '"' || c == '\\') + putchar ('\\'); + putchar (c); + } + 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)"); +} + +/* ------------------------------------------------------------------------- */ + +/* 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" + "#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->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->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); + if (option[TYPE]) + printf (",%s}", temp->rest); + 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) +{ + const int 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 ("{\"\"}"); + 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, 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, 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, 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); + + 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->length, + indent, ""); + indent += 4; + } + printf ("%*sresword = ", + indent, ""); + if (option[TYPE]) + printf ("&%s[%d]", option.get_wordlist_name (), list->index); + else + output_string (list->key); + 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, ""); + } + 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" + "%*s}\n", + indent, "", indent, "", indent, "", 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[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->length, ptr->index, + field_width, ptr->char_set, 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; +} + |