diff options
Diffstat (limited to 'ACE/apps/gperf/src/Key_List.cpp')
-rw-r--r-- | ACE/apps/gperf/src/Key_List.cpp | 1948 |
1 files changed, 1948 insertions, 0 deletions
diff --git a/ACE/apps/gperf/src/Key_List.cpp b/ACE/apps/gperf/src/Key_List.cpp new file mode 100644 index 00000000000..e954c075e8a --- /dev/null +++ b/ACE/apps/gperf/src/Key_List.cpp @@ -0,0 +1,1948 @@ +// -*- C++ -*- + +// $Id$ + +// Copyright (C) 1989 Free Software Foundation, Inc. +// written by Douglas C. Schmidt (schmidt@cs.wustl.edu) + +// This file is part of GNU GPERF. + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "Key_List.h" + +ACE_RCSID(src, Key_List, "$Id$") + +#if defined (ACE_HAS_GPERF) + +#include "ace/Read_Buffer.h" +#include "Hash_Table.h" +#include "ace/OS_Memory.h" +#include "ace/OS_NS_stdio.h" +#include "ace/OS_NS_string.h" + +// Default type for generated code. +const char *const Key_List::default_array_type = "char *"; + +// in_word_set return type, by default. +const char *const Key_List::default_return_type = "char *"; + +// How wide the printed field width must be to contain the maximum +// hash value. +int Key_List::field_width = 0; +int Key_List::determined_[ACE_STANDARD_CHARACTER_SET_SIZE]; + +// Destructor dumps diagnostics during debugging. + +Key_List::~Key_List (void) +{ + if (option[DEBUGGING]) + this->dump (); + + // Free up all the nodes in the list. + while (this->head != 0) + { + List_Node *temp; + + // Make sure to delete the linked nodes, as well. + for (List_Node *ptr = this->head->link; + ptr != 0; + ptr = temp) + { + temp = ptr->link; + delete ptr; + } + + temp = this->head->next; + delete this->head; + this->head = temp; + } +} + +// 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. + +char * +Key_List::special_input (char delimiter) +{ + int size = 80; + char *buf = 0; + ACE_NEW_RETURN (buf, + char[size], + 0); + int c; + + for (int i = 0; (c = getchar ()) != EOF; i++) + { + if (c == '%') + { + c = getchar (); + if (c == delimiter) + { + // Discard newline... + while ((c = getchar ()) != '\n') + continue; + + if (i == 0) + { + buf[0] = '\0'; + return buf; + } + 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 = 0; + ACE_NEW_RETURN (temp, + char[size *= 2], + 0); + for (int j = 0; j < i; j++) + temp[j] = buf[j]; + + delete [] buf; + buf = temp; + } + buf[i] = static_cast<char> (c); + } + + return 0; +} + +// Stores any C/C++ source code that must be included verbatim into +// the generated code output. + +char * +Key_List::save_include_src (void) +{ + int c = getchar (); + + if (c != '%') + ungetc (c, stdin); + else if ((c = getchar ()) != '{') + ACE_ERROR_RETURN ((LM_ERROR, + "internal error, %c != '{' on line %l in file %N", + c), + 0); + else + return special_input ('}'); + return (char *) ""; +} + +// 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. + +char * +Key_List::array_type (void) +{ + return special_input ('%'); +} + +// Sets up the Return_Type, the Struct_Tag type and the Array_Type +// based upon various user Options. + +int +Key_List::output_types (void) +{ + if (option[TYPE]) + { + array_type_ = array_type (); + if (array_type_ == 0) + // Something's wrong, but we'll catch it later on.... + return -1; + else + { + // Yow, we've got a user-defined type... + size_t struct_tag_length = ACE_OS::strcspn (array_type_, + "{\n\0"); + if (option[POINTER]) // And it must return a pointer... + { + ACE_NEW_RETURN (return_type, + char[struct_tag_length + 2], + -1); + ACE_OS::strncpy (return_type, + array_type_, + struct_tag_length); + return_type[struct_tag_length] = '*'; + return_type[struct_tag_length + 1] = '\0'; + } + + ACE_NEW_RETURN (struct_tag, + char[struct_tag_length + 2], + -1); + ACE_OS::strncpy (struct_tag, + array_type_, + struct_tag_length); + if (struct_tag[struct_tag_length] != ' ') + { + struct_tag[struct_tag_length] = ' '; + struct_tag_length++; + } + struct_tag[struct_tag_length] = '\0'; + } + } + else if (option[POINTER]) // Return a char *. + return_type = (char *) Key_List::default_array_type; + return 0; +} + +// 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. + +int +Key_List::read_keys (void) +{ + this->include_src = this->save_include_src (); + if (this->include_src == 0) + return -1; + else if (this->output_types () == -1) + return -1; + else + { + ACE_Read_Buffer input (stdin); + + char *buffer = input.read ('\n'); + + if (buffer == 0) + ACE_ERROR_RETURN ((LM_ERROR, + "No words in input file, did you forget to prepend %%%%" + " or use -t accidentally?\n"), + -1); + // Read in all the keywords from the input file. + else + { + List_Node *temp = 0; + const char *delimiter = option.delimiter (); + ACE_NEW_RETURN (this->head, + List_Node (buffer, + static_cast<int> (ACE_OS::strcspn (buffer, + delimiter))), + -1); + for (temp = this->head; + (0 != (buffer = input.read ('\n'))) + && ACE_OS::strcmp (buffer, "%%"); + temp = temp->next) + { + ACE_NEW_RETURN (temp->next, + List_Node (buffer, + static_cast<int> (ACE_OS::strcspn (buffer, + delimiter))), + -1); + this->total_keys++; + } + + // See if any additional source code is included at end of + // this file. + if (buffer) + additional_code = 1; + + this->list_len = this->total_keys; + + // Make large hash table for efficiency. + Hash_Table table (this->list_len * Key_List::TABLE_MULTIPLE); + List_Node *trail = 0; + + // Test whether there are any links and also set the maximum + // length an identifier in the keyword list. + + for (temp = head; + temp != 0; + temp = temp->next) + { + List_Node *ptr = table.find (temp, option[NOLENGTH]); + + // Check for static key 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 == 0) + trail = temp; + else + { + 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[DEBUGGING]) + ACE_ERROR ((LM_ERROR, + "Static key link: \"%s\" = \"%s\", with key set \"%s\".\n", + temp->key, + ptr->key, + temp->keysig)); + } + + // 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; + } + } + + // Exit program if links exists and option[DUP] not set, since + // we can't continue. + if (total_duplicates) + { + if (option[DUP]) + { + if (!option[MUTE]) + ACE_ERROR_RETURN ((LM_ERROR, + "%d input keysigs have identical hash values, examine output carefully...\n", + total_duplicates), + 0); + } + else + ACE_ERROR_RETURN ((LM_ERROR, + "%d input keysigs have identical hash values,\ntry different key positions or use option -D.\n", + total_duplicates), + -1); + } + if (option[ALLCHARS]) + option.keysig_size (max_key_len); + } + + return 0; +} + +// 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) +{ + if (!list1) + return list2; + else if (!list2) + return list1; + else if (occurrence_sort && list1->occurrence < list2->occurrence + || hash_sort && list1->hash_value > list2->hash_value + || key_sort && ACE_OS::strcmp (list1->key, list2->key) >= 0) + { + list2->next = merge (list2->next, list1); + return list2; + } + else + { + list1->next = merge (list1->next, list2); + return list1; + } +} + +// Applies the merge sort algorithm to recursively sort the key list +// by frequency of occurrence of elements in the key set. + +List_Node * +Key_List::merge_sort (List_Node *a_head) +{ + if (!a_head || !a_head->next) + return a_head; + else + { + List_Node *middle = a_head; + List_Node *temp = a_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 (a_head), merge_sort (temp)); + } +} + +// Returns the frequency of occurrence of elements in the key set. + +inline int +Key_List::occurrence (List_Node *ptr) +{ + int value = 0; + + for (char *temp = ptr->keysig; *temp; temp++) + value += Vectors::occurrences[(int) *temp]; + + return value; +} + +// Sets the index location for all keysig characters that are now +// determined. + +inline void +Key_List::determined (List_Node *ptr) +{ + for (char *temp = ptr->keysig; *temp; temp++) + Key_List::determined_[(int) *temp] = 1; +} + +// Returns TRUE if PTR's key set is already completely determined. + +inline int +Key_List::already_determined (List_Node *ptr) +{ + int is_determined = 1; + + for (char *temp = ptr->keysig; is_determined && *temp; temp++) + is_determined = determined_[(int) *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) +{ + List_Node *ptr; + + for (ptr = head; ptr; ptr = ptr->next) + ptr->occurrence = occurrence (ptr); + + // Switch to sorting by occurrence. + hash_sort = 0; + occurrence_sort = 1; + + for (ptr = head = merge_sort (head); ptr->next; ptr = ptr->next) + { + 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; + } + } + } +} + +// Outputs 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::output_min_max (void) +{ + List_Node *temp; + for (temp = head; temp->next; temp = temp->next) + continue; + + min_hash_value = head->hash_value; + max_hash_value = temp->hash_value; + + if (!option[ENUM]) + ACE_OS::printf ("\n#define TOTAL_KEYWORDS %d\n#define MIN_WORD_LENGTH %d" + "\n#define MAX_WORD_LENGTH %d\n#define MIN_HASH_VALUE %d" + "\n#define MAX_HASH_VALUE %d\n#define HASH_VALUE_RANGE %d" + "\n#define DUPLICATES %d\n#define WORDLIST_SIZE %d\n\n", + total_keys, min_key_len, max_key_len, min_hash_value, + max_hash_value, max_hash_value - min_hash_value + 1, + total_duplicates ? total_duplicates + 1 : 0, total_keys + min_hash_value); + else if (option[GLOBAL]) + ACE_OS::printf ("enum\n{\n" + " TOTAL_KEYWORDS = %d,\n" + " MIN_WORD_LENGTH = %d,\n" + " MAX_WORD_LENGTH = %d,\n" + " MIN_HASH_VALUE = %d,\n" + " MAX_HASH_VALUE = %d,\n" + " HASH_VALUE_RANGE = %d,\n" + " DUPLICATES = %d\n" + " WORDLIST_SIZE = %d};\n\n", + total_keys, min_key_len, max_key_len, min_hash_value, + max_hash_value, max_hash_value - min_hash_value + 1, + total_duplicates ? total_duplicates + 1 : 0, total_keys + min_hash_value); +} + +// Generates the output using a C switch. This trades increased +// search time for decreased table space (potentially *much* less +// space for sparse tables). It the user has specified their own +// struct in the keyword file *and* they enable the POINTER option we +// have extra work to do. The solution here is to maintain a local +// static array of user defined struct's, as with the +// Output_Lookup_Function. Then we use for switch statements to +// perform either a strcmp or strncmp, returning 0 if the str fails to +// match, and otherwise returning a pointer to appropriate index +// location in the local static array. + +void +Key_List::output_switch (int use_keyword_table) +{ + if (!option[GLOBAL] && use_keyword_table == 0) + { + if (option[LENTABLE] && option[DUP]) + output_keylength_table (); + if (option[POINTER] && option[TYPE]) + output_keyword_table (); + } + + char *comp_buffer; + List_Node *curr = head; + int pointer_and_type_enabled = option[POINTER] && option[TYPE]; + int total_switches = option.total_switches (); + int switch_size = keyword_list_length () / total_switches; + + if (pointer_and_type_enabled) + { + // Keep track of the longest string we'll need! + const char *s = "charmap[*str] == *resword->%s && !strncasecmp (str + 1, resword->%s + 1, len - 1)"; + comp_buffer = + new char [ACE_OS::strlen (s) + 2 * ACE_OS::strlen (option.key_name ()) + 1]; + if (option[COMP]) + sprintf (comp_buffer, "%s == *resword->%s && !%s (str + 1, resword->%s + 1, len - 1)", + option[STRCASECMP] ? "charmap[*str]" : "*str", option.key_name (), + option[STRCASECMP] ? "strncasecmp" : "strncmp", option.key_name ()); + else + sprintf (comp_buffer, "%s == *resword->%s && !%s (str + 1, resword->%s + 1)", + option[STRCASECMP] ? "charmap[*str]" : "*str", option.key_name (), + option[STRCASECMP] ? "strcasecmp" : "strcmp", option.key_name ()); + } + else + { + if (option[COMP]) + comp_buffer = option[STRCASECMP] + ? (char *) "charmap[*str] == *resword && !strncasecmp (str + 1, resword + 1, len - 1)" + : (char *) "*str == *resword && !strncmp (str + 1, resword + 1, len - 1)"; + else + comp_buffer = option[STRCASECMP] + ? (char *) "charmap[*str] == *resword && !strncasecmp (str + 1, resword + 1, len - 1)" + : (char *) "*str == *resword && !strcmp (str + 1, resword + 1)"; + } + if (!option[OPTIMIZE]) + ACE_OS::printf (" if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)\n {\n"); + ACE_OS::printf (" unsigned int key = %s (str, len);\n\n", option.hash_name ()); + if (!option[OPTIMIZE]) + ACE_OS::printf (" if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE)\n"); + + ACE_OS::printf (" {\n"); + + // Properly deal with user's who request multiple switch statements. + + while (curr) + { + List_Node *temp = curr; + int lowest_case_value = curr->hash_value; + int number_of_cases = 0; + + // Figure out a good cut point to end this switch. + + for (; temp && ++number_of_cases < switch_size; temp = temp->next) + if (temp->next && temp->hash_value == temp->next->hash_value) + while (temp->next && temp->hash_value == temp->next->hash_value) + temp = temp->next; + + if (temp && total_switches != 1) + ACE_OS::printf (" if (key <= %d)\n {\n", temp->hash_value); + else + ACE_OS::printf (" {\n"); + + // Output each keyword as part of a switch statement indexed by + // hash value. + + if (option[POINTER] || option[DUP] || use_keyword_table) + { + int i = 0; + + ACE_OS::printf (" %s%s *resword; %s\n\n", + option[CONSTANT] || pointer_and_type_enabled == 0 ? "const " : "", + pointer_and_type_enabled ? struct_tag : "char", + option[LENTABLE] && !option[DUP] ? "unsigned int key_len;" : ""); + if (total_switches == 1) + { + ACE_OS::printf (" switch (key)\n {\n"); + lowest_case_value = 0; + } + else + ACE_OS::printf (" switch (key - %d)\n {\n", lowest_case_value); + + for (temp = curr; temp && ++i <= number_of_cases; temp = temp->next) + { + ACE_OS::printf (" case %*d:\n", + Key_List::field_width, + temp->hash_value - lowest_case_value); + + // Handle `static links,' i.e., those that occur during + // the initial preprocessing. + + if (temp->link == 0) + { + if (option[DEBUGGING]) + ACE_OS::printf (" /* hash value = %4d, keyword = \"%s\" */\n", + temp->hash_value, + temp->key); + } + else + { + List_Node *links; + + for (links = temp; links; links = links->link) + { + if (option[DEBUGGING]) + ACE_OS::printf (" /* hash value = %4d, keyword = \"%s\" */\n", + temp->hash_value, + links->key); + if (pointer_and_type_enabled) + ACE_OS::printf (" resword = &wordlist[%d];\n", links->slot); + else if (use_keyword_table) + ACE_OS::printf (" resword = wordlist[%d];\n", links->slot); + else + ACE_OS::printf (" resword = \"%s\";\n", links->key); + ACE_OS::printf (" if (%s) return resword;\n", comp_buffer); + } + } + + // Handle unresolved duplicate hash values. These are + // guaranteed to be adjacent since we sorted the keyword + // list by increasing hash values. + if (temp->next && temp->hash_value == temp->next->hash_value) + { + + for ( ; temp->next && temp->hash_value == temp->next->hash_value; + temp = temp->next) + { + if (pointer_and_type_enabled) + ACE_OS::printf (" resword = &wordlist[%d];\n", temp->slot); + else if (use_keyword_table) + ACE_OS::printf (" resword = wordlist[%d];", temp->slot); + else + ACE_OS::printf (" resword = \"%s\";\n", temp->key); + ACE_OS::printf (" if (%s) return resword;\n", comp_buffer); + } + if (pointer_and_type_enabled) + ACE_OS::printf (" resword = &wordlist[%d];\n", temp->slot); + else if (use_keyword_table) + ACE_OS::printf (" resword = wordlist[%d];", temp->slot); + else + ACE_OS::printf (" resword = \"%s\";\n", temp->key); + ACE_OS::printf (" return %s ? resword : 0;\n", comp_buffer); + } + else if (temp->link) + ACE_OS::printf (" return 0;\n"); + else + { + if (pointer_and_type_enabled) + ACE_OS::printf (" resword = &wordlist[%d];", temp->slot); + else if (use_keyword_table) + ACE_OS::printf (" resword = wordlist[%d];", temp->slot); + else + ACE_OS::printf (" resword = \"%s\";", temp->key); + if (option[LENTABLE] && !option[DUP]) + ACE_OS::printf (" key_len = %d;", temp->length); + ACE_OS::printf (" break;\n"); + } + } + ACE_OS::printf (" default: return 0;\n }\n"); + if (option[OPTIMIZE]) + ACE_OS::printf (" return resword;\n"); + else + { + ACE_OS::printf (option[LENTABLE] && !option[DUP] + ? " if (len == key_len && %s)\n return resword;\n" + : " if (%s)\n return resword;\n", comp_buffer); + ACE_OS::printf (" return 0;\n"); + } + ACE_OS::printf (" }\n"); + curr = temp; + } + else // Nothing special required here. + { + int i = 0; + ACE_OS::printf (" char *s;\n\n switch (key - %d)\n {\n", + lowest_case_value); + + for (temp = curr; temp && ++i <= number_of_cases; temp = temp->next) + if (option[LENTABLE]) + ACE_OS::printf (" case %*d: if (len == %d) s = \"%s\"; else return 0; break;\n", + Key_List::field_width, + temp->hash_value - lowest_case_value, + temp->length, + temp->key); + else + ACE_OS::printf (" case %*d: s = \"%s\"; break;\n", + Key_List::field_width, + temp->hash_value - lowest_case_value, + temp->key); + + ACE_OS::printf (" default: return 0;\n }\n "); + if (option[COMP]) + ACE_OS::printf ("return %s == *s && !%s;\n }\n", + option[STRCASECMP] ? "charmap[*str]" : "*str", + option[STRCASECMP] ? "strncasecmp (s + 1, str + 1, len - 1)" : "strcmp (s + 1, str + 1)"); + else + ACE_OS::printf ("return %s == *s && !%s;\n }\n", + option[STRCASECMP] ? "charmap[*str]" : "*str", + option[STRCASECMP] ? "strcasecmp (s + 1, str + 1, len - 1)" : "strcmp (s + 1, str + 1)"); + curr = temp; + } + } + ACE_OS::printf (" }\n %s\n}\n", option[OPTIMIZE] ? "" : "}\n return 0;"); +} + +// 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) +{ + const int max_column = 15; + int slot = 0; + int column = 0; + const char *indent = option[GLOBAL] ? "" : " "; + List_Node *temp; + + if (!option[DUP] && !option[SWITCH]) + { + ACE_OS::printf ("\n%sstatic %sunsigned %s lengthtable[] =\n%s%s{\n ", + indent, + option[CONSTANT] ? "const " : "", + max_key_len <= ((int) UCHAR_MAX) ? "char" : (max_key_len <= ((int) USHRT_MAX) ? "short" : "long"), + indent, + indent); + + for (temp = head; temp; temp = temp->next, slot++) + { + + if (slot < temp->hash_value) + for ( ; slot < temp->hash_value; slot++) + ACE_OS::printf ("%3d,%s", 0, ++column % (max_column - 1) ? "" : "\n "); + + ACE_OS::printf ("%3d,%s", temp->length, ++column % (max_column - 1 ) ? "" : "\n "); + } + + ACE_OS::printf ("\n%s%s};\n", + indent, + indent); + } +} + +// Prints out the array containing the key words for the Gen_Perf hash +// function. + +void +Key_List::output_keyword_table (void) +{ + const char *l_brace = *head->rest ? "{" : ""; + const char *r_brace = *head->rest ? "}," : ""; + const char *indent = option[GLOBAL] ? "" : " "; + int slot = 0; + List_Node *temp; + + int pointer_and_type_enabled = option[POINTER] && option[TYPE]; + ACE_OS::printf ("%sstatic %s%swordlist[] =\n%s%s{\n", + indent, + option[CONSTANT] || pointer_and_type_enabled == 0 ? "const " : "", + struct_tag, + indent, + indent); + + // Skip over leading blank entries if there are no duplicates. + + if (0 < head->hash_value) + ACE_OS::printf (" "); + + + int column; + + for (column = 1; slot < head->hash_value; column++) + { + ACE_OS::printf ("%s\"\",%s%s%s", + l_brace, + option.fill_default (), + r_brace, + column % 9 ? "" : "\n "); + slot++; + } + + if (0 < head->hash_value && column % 10) + ACE_OS::printf ("\n"); + + // Generate an array of reserved words at appropriate locations. + + for (temp = head ; temp; temp = temp->next, slot++) + { + temp->slot = slot; + + if (!option[SWITCH] && (total_duplicates == 0 || !option[DUP]) && slot < temp->hash_value) + { + int column; + + ACE_OS::printf (" "); + + for (column = 1; slot < temp->hash_value; slot++, column++) + ACE_OS::printf ("%s\"\",%s%s%s", + l_brace, + option.fill_default (), + r_brace, + column % 9 ? "" : "\n "); + + if (column % 10) + ACE_OS::printf ("\n"); + else + { + ACE_OS::printf ("%s\"%s\", %s%s", l_brace, temp->key, temp->rest, r_brace); + if (option[DEBUGGING]) + ACE_OS::printf (" /* hash value = %d, slot = %d */", + temp->hash_value, + temp->slot); + putchar ('\n'); + continue; + } + } + + ACE_OS::printf (" %s\"%s\", %s%s", l_brace, temp->key, temp->rest, r_brace); + if (option[DEBUGGING]) + ACE_OS::printf (" /* hash value = %d, slot = %d */", + temp->hash_value, + temp->slot); + putchar ('\n'); + + // Deal with links specially. + if (temp->link) + for (List_Node *links = temp->link; links; links = links->link) + { + links->slot = ++slot; + ACE_OS::printf (" %s\"%s\", %s%s", l_brace, links->key, links->rest, r_brace); + if (option[DEBUGGING]) + ACE_OS::printf (" /* hash value = %d, slot = %d */", + links->hash_value, + links->slot); + putchar ('\n'); + } + + } + ACE_OS::printf ("%s%s};\n\n", indent, indent); +} + +// Generates C code for the binary search algorithm that returns +// the proper encoding for each key word + +int +Key_List::output_binary_search_function (void) +{ + ACE_OS::printf ("%s\n", include_src); + + // Get prototype for strncmp() and strcmp(). + if (!option[SKIPSTRINGH]) + ACE_OS::printf ("#include <string.h>\n"); + + // Output type declaration now, reference it later on.... + if (option[TYPE] && !option[NOTYPE]) + ACE_OS::printf ("%s;\n", + array_type_); + + output_min_max (); + + if (option[STRCASECMP]) + output_strcasecmp (); + + // Class definition if -M is *not* enabled. + if (option[CPLUSPLUS] && !option[SKIPCLASS]) + ACE_OS::printf ("class %s {\npublic:\n" + " static %s%s%s (const char *str);\n};\n\n", + option.class_name (), + option[CONSTANT] ? "const " : "", + return_type, + option.function_name ()); + + // Use the inline keyword to remove function overhead. + if (option[INLINE]) + ACE_OS::printf ("inline\n"); + + ACE_OS::printf ("%s%s\n", option[CONSTANT] ? "const " : "", return_type); + if (option[CPLUSPLUS]) + ACE_OS::printf ("%s::", option.class_name ()); + + ACE_OS::printf (option[ANSI] + ? "%s (const char *str)\n{\n" + : "%s (str)\n char *str;\n{\n", + option.function_name ()); + +// Use the switch in place of lookup table. + + if (option[SWITCH]) + output_switch (); + + // Use the lookup table, in place of switch. + else + { + if (!option[GLOBAL]) + { + if (option[LENTABLE]) + output_keylength_table (); + output_keyword_table (); + } + } + + // Logic to handle the Binary Search. + + ACE_OS::printf ("int first = 0, last = 0, middle = 0;\n"); + + if (option[DUP] && total_duplicates > 0) + { + ACE_OS::printf ("%s*base = 0;\n",struct_tag); + } + + ACE_OS::printf ("\nlast = %d;\n",total_keys - 1); + ACE_OS::printf ("while (last >= first)\n"); + ACE_OS::printf ("\t{\n"); + ACE_OS::printf ("\t middle = (last + first) / 2;\n"); + ACE_OS::printf ("\t if (strcmp (wordlist[middle].%s, str) == 0)\n break;\n", option.key_name()); + ACE_OS::printf ("\t if (strcmp (wordlist[middle].%s, str) < 0)\n first = middle + 1;\n", option.key_name()); + ACE_OS::printf ("\t else last = middle - 1;\n"); + ACE_OS::printf ("\t}\n"); + ACE_OS::printf ("if (last < first)\n return 0;\n"); + ACE_OS::printf ("else\n return (&wordlist[middle]);\n}\n"); + + if (additional_code) + { + for (;;) + { + int c = getchar (); + + if (c == EOF) + break; + else + putchar (c); + } + } + + fflush(stdout); + + return 0; + +} + +// Generates C code for the linear search algorithm that returns +// the proper encoding for each key word + +int +Key_List::output_linear_search_function (void) +{ + ACE_OS::printf ("%s\n", include_src); + + // Get prototype for strncmp() and strcmp(). + if (!option[SKIPSTRINGH]) + ACE_OS::printf ("#include <string.h>\n"); + + // Output type declaration now, reference it later on.... + if (option[TYPE] && !option[NOTYPE]) + ACE_OS::printf ("%s;\n", + array_type_); + + output_min_max (); + + if (option[STRCASECMP]) + output_strcasecmp (); + + // Class definition if -M is *not* enabled. + if (option[CPLUSPLUS] && !option[SKIPCLASS]) + ACE_OS::printf ("class %s {\npublic:\n" + " static %s%s%s (const char *str);\n};\n\n", + option.class_name (), + option[CONSTANT] ? "const " : "", + return_type, + option.function_name ()); + + // Use the inline keyword to remove function overhead. + if (option[INLINE]) + ACE_OS::printf ("inline\n"); + + ACE_OS::printf ("%s%s\n", + option[CONSTANT] ? "const " : "", + return_type); + if (option[CPLUSPLUS]) + ACE_OS::printf ("%s::", option.class_name ()); + + ACE_OS::printf (option[ANSI] + ? "%s (const char *str)\n{\n" + : "%s (str)\n char *str;\n{\n", + option.function_name ()); + + // Use the switch in place of lookup table. + + if (option[SWITCH]) + output_switch (); + // Use the lookup table, in place of switch. + else + { + if (!option[GLOBAL]) + { + if (option[LENTABLE]) + output_keylength_table (); + output_keyword_table (); + } + } + + // Logic to handle the Linear Search. + + ACE_OS::printf ("for (int i=0; i<=%d; i++)",total_keys-1); + ACE_OS::printf ("\t{\n"); + ACE_OS::printf ("\t if (strcmp (wordlist[i].%s, str) == 0)\n", option.key_name()); + ACE_OS::printf ("\t return &wordlist[i];\n"); + ACE_OS::printf ("\t}\n"); + ACE_OS::printf ("return 0;\n}\n"); + + if (additional_code) + { + for (;;) + { + int c = getchar (); + + if (c == EOF) + break; + else + putchar (c); + } + } + + ACE_OS::fflush (stdout); + + return 0; + +} +// Generates C code for the hash function that returns the proper +// encoding for each key word. + +void +Key_List::output_hash_function (void) +{ + const int max_column = 10; + int count = max_hash_value; + + // Lookup table for converting ASCII to EBCDIC. + static const int ascii_to_ebcdic[ACE_ASCII_SIZE] = + { + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, + + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D, + + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + 0xA7, 0xA8, 0xA9, 0xC0, 0x6A, 0xD0, 0xA1, 0x07}; + + int ebcdic_to_ascii[ACE_EBCDIC_SIZE]; + int target; + + // Calculate maximum number of digits required for MAX_HASH_VALUE. + + for (Key_List::field_width = 2; + (count /= 10) > 0; + Key_List::field_width++) + continue; + + if (option[INLINE]) + ACE_OS::printf ("inline\n"); + + if (option[C]) + ACE_OS::printf ("static "); + ACE_OS::printf ("unsigned int\n"); + if (option[CPLUSPLUS]) + ACE_OS::printf ("%s::", option.class_name ()); + + ACE_OS::printf (option[ANSI] + ? "%s (const char *str, unsigned int len)\n{\n" + : "%s (str, len)\n char *str;\n unsigned int len;\n{\n", + option.hash_name ()); + + // Generate the asso_values table. + ACE_OS::printf (" static %sunsigned %s asso_values[] =\n {", + option[CONSTANT] ? "const " : "", + max_hash_value <= ((int) UCHAR_MAX) ? "char" : (max_hash_value <= ((int) USHRT_MAX) ? "short" : "int")); + + ACE_OS::printf ("\n#if defined (ACE_MVS)"); +#if ACE_STANDARD_CHARACTER_SET_SIZE == ACE_EBCDIC_SIZE + { + // We are running in EBCDIC environment. + for (count = 0; count < ACE_EBCDIC_SIZE; ++count) + { + if (!(count % max_column)) + ACE_OS::printf ("\n "); + + ACE_OS::printf ("%*d,", + Key_List::field_width, + Vectors::occurrences[count] ? Vectors::asso_values[count] : max_hash_value + 1); + } + + ACE_OS::printf ("\n#else"); + + for (count = 0; count < ACE_ASCII_SIZE; ++count) + { + if (!(count % max_column)) + ACE_OS::printf ("\n "); + + target = ascii_to_ebcdic[count]; + ACE_OS::printf ("%*d,", + Key_List::field_width, + Vectors::occurrences[target] ? Vectors::asso_values[target] : max_hash_value + 1); + } + } +# else + { + // We are running in ASCII environment. + for (count = 0; count < ACE_EBCDIC_SIZE; ++count) + ebcdic_to_ascii[count] = 0; + + for (count = 0; count < ACE_ASCII_SIZE; ++count) + { + target = ascii_to_ebcdic[count]; + ebcdic_to_ascii[target] = count; + } + + for (count = 0; count < ACE_EBCDIC_SIZE; ++count) + { + if (!(count % max_column)) + ACE_OS::printf ("\n "); + + target = ebcdic_to_ascii[count]; + ACE_OS::printf ("%*d,", + Key_List::field_width, + Vectors::occurrences[target] ? Vectors::asso_values[target] : max_hash_value + 1); + } + ACE_OS::printf ("\n#else"); + + for (count = 0; count < ACE_ASCII_SIZE; ++count) + { + if (!(count % max_column)) + ACE_OS::printf ("\n "); + + ACE_OS::printf ("%*d,", + Key_List::field_width, + Vectors::occurrences[count] ? Vectors::asso_values[count] : max_hash_value + 1); + } + } +#endif /* ACE_STANDARD_CHARACTER_SET_SIZE == ACE_EBCDIC_SIZE */ + ACE_OS::printf ("\n#endif /* ACE_MVS */"); + + // Optimize special case of ``-k 1,$'' + if (option[DEFAULTCHARS]) + { + if (option[STRCASECMP]) + ACE_OS::printf ("\n };\n return %sasso_values[(int) charmap[str[len - 1]]] + asso_values[(int) charmap[str[0]]];\n}\n\n", + option[NOLENGTH] ? "" : "len + "); + else + ACE_OS::printf ("\n };\n return %sasso_values[(int) str[len - 1]] + asso_values[(int) str[0]];\n}\n\n", + option[NOLENGTH] ? "" : "len + "); + } + else + { + int key_pos; + + option.reset (); + + // Get first (also highest) key position. + key_pos = option.get (); + + // We can perform additional optimizations here. + if (!option[ALLCHARS] && key_pos <= min_key_len) + { + ACE_OS::printf ("\n };\n return %s", option[NOLENGTH] ? "" : "len + "); + + for (; key_pos != WORD_END; ) + { + ACE_OS::printf (option[STRCASECMP] ? "asso_values[(int) charmap[str[%d]]]" : "asso_values[(int) str[%d]]", key_pos - 1); + if ((key_pos = option.get ()) != EOS) + ACE_OS::printf (" + "); + else + break; + } + + ACE_OS::printf ("%s;\n}\n\n", key_pos == WORD_END + ? (option[STRCASECMP] ? "asso_values[(int) charmap[str[len - 1]]]" : "asso_values[(int) str[len - 1]]") + : ""); + } + + // We've got to use the correct, but brute force, technique. + else + { + ACE_OS::printf ("\n };\n unsigned 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]) + { + int i; + + // Break these options up for speed (gee, is this misplaced efficiency or what?! + if (option[STRCASECMP]) + + for (i = max_key_len; i > 0; i--) + ACE_OS::printf (" case %d:\n hval += asso_values[(int) charmap[(int) str[%d]]];\n", i, i - 1); + + else + + for (i = max_key_len; i > 0; i--) + ACE_OS::printf (" case %d:\n hval += asso_values[(int) str[%d]];\n", i, i - 1); + + ACE_OS::printf (" }\n return hval;\n}\n\n"); + } + else // do the hard part... + { + count = key_pos + 1; + + do + { + + while (--count > key_pos) + ACE_OS::printf (" case %d:\n", count); + + ACE_OS::printf (option[STRCASECMP] + ? " case %d:\n hval += asso_values[(int) charmap[(int) str[%d]]];\n" + : " case %d:\n hval += asso_values[(int) str[%d]];\n", + key_pos, key_pos - 1); + } + while ((key_pos = option.get ()) != EOS && key_pos != WORD_END); + + ACE_OS::printf (" }\n return hval%s;\n}\n\n", + key_pos == WORD_END + ? (option[STRCASECMP] ? " + asso_values[(int) charmap[(int) str[len - 1]]]" : " + asso_values[(int) str[len - 1]]") + : ""); + } + } + } +} + +int +Key_List::count_duplicates (List_Node *link, + const char *type) +{ + int count = 0; + + // Count the number of "static" duplicates for this hash value. + for (List_Node *ptr = link; + ptr != 0; + ptr = ptr->link) + { + count++; + + if (option[DEBUGGING]) + ACE_DEBUG ((LM_DEBUG, + "%s linked keyword = %s, slot = %d, hash_value = %d\n", + type, + ptr->key, + ptr->slot, + ptr->hash_value)); + } + + return count; +} + +void +Key_List::update_lookup_array (int lookup_array[], + int i1, + int i2, + Duplicate_Entry *dup_ptr, + int value) +{ + lookup_array[i1] = -dup_ptr->slot; + lookup_array[i2] = -dup_ptr->count; + lookup_array[dup_ptr->hash_value] = value; +} + +// Generates the large, sparse table that maps hash values in the +// smaller, contiguous range of the keyword table. + +int +Key_List::output_lookup_array (void) +{ + if (total_duplicates > 0) + { + const int DEFAULT_VALUE = -1; + + Duplicate_Entry *duplicates = 0; + ACE_NEW_RETURN (duplicates, + Duplicate_Entry[total_duplicates], + -1); + + int *lookup_array = 0; + ACE_NEW_RETURN (lookup_array, + int[max_hash_value + 1], + -1); + + Duplicate_Entry *dup_ptr = duplicates; + int *lookup_ptr = lookup_array + max_hash_value + 1; + + // Initialize the lookup array to the DEFAULT_VALUE (-1). + while (lookup_ptr > lookup_array) + *--lookup_ptr = DEFAULT_VALUE; + + // Iterate through the keylist and handle the static and dynamic + // duplicate entries. + for (List_Node *temp = head; temp; temp = temp->next) + { + int hash_value = temp->hash_value; + // Store the keyword's slot location into the + // <lookup_array> at the <hash_value>. If this is a + // non-duplicate, then this value will point directly to the + // keyword. + lookup_array[hash_value] = temp->slot; + + if (option[DEBUGGING]) + ACE_DEBUG ((LM_DEBUG, + "keyword = %s, slot = %d, hash_value = %d, lookup_array[hash_value] = %d\n", + temp->key, + temp->slot, + temp->hash_value, + lookup_array[temp->hash_value])); + + if (temp->link == 0 && + (temp->next == 0 || hash_value != temp->next->hash_value)) + // This isn't a duplicate. Note that we know this because + // we sorted the keys by their hash value. + continue; + else + { + // We'll handle the duplicates here. + dup_ptr->hash_value = hash_value; + dup_ptr->slot = temp->slot; + dup_ptr->count = 1; + + // Count the number of "static" duplicates, i.e., + // keywords that had the same keysig when the keyfile + // was first read. + dup_ptr->count += this->count_duplicates (temp->link, + "static"); + + // Count the number of "dynamic" duplicates, i.e., + // keywords that ended up with the same hash value as a + // result of the <asso_values> contents. + for (; + temp->next && hash_value == temp->next->hash_value; + temp = temp->next) + dup_ptr->count += this->count_duplicates (temp->next, + "dynamic"); + dup_ptr++; + } + } + + // Compute the values in the lookup array. + while (--dup_ptr >= duplicates) + { + if (option[DEBUGGING]) + ACE_DEBUG ((LM_DEBUG, + "dup_ptr[%d]: hash_value = %d, slot = %d, count = %d\n", + dup_ptr - duplicates, + dup_ptr->hash_value, + dup_ptr->slot, + dup_ptr->count)); + int i; + + // Look to the left first. + for (i = dup_ptr->hash_value; i > 0; i--) + if (lookup_array[i] == DEFAULT_VALUE && lookup_array[i - 1] == DEFAULT_VALUE) + { + this->update_lookup_array (lookup_array, + i - 1, + i, + dup_ptr, + -(max_hash_value + (dup_ptr->hash_value - i + 1))); + break; + } + + // If we didn't find it to the left look to the right + // instead... + if (i == 0) + { + for (i = dup_ptr->hash_value; i < max_hash_value; i++) + if (lookup_array[i] == DEFAULT_VALUE && lookup_array[i + 1] == DEFAULT_VALUE) + { + this->update_lookup_array (lookup_array, + i, + i + 1, + dup_ptr, + max_hash_value + (i - dup_ptr->hash_value)); + break; + } + + // If this happens, we can't use the output array scheme... + if (i >= max_hash_value) + { + option = SWITCH; + ACE_DEBUG ((LM_DEBUG, + "GPERF: Automatically changing to -S1 switch option\n")); + // Since we've already generated the keyword table + // we need to use it! + this->output_switch (1); + return 1; // 1 indicates that we've changed our mind... + } + } + } + + lookup_ptr = lookup_array + max_hash_value + 1; + int max = INT_MIN; + + while (lookup_ptr > lookup_array) + { + int val = abs (*--lookup_ptr); + if (max < val) + max = val; + } + + const char *indent = option[GLOBAL] ? "" : " "; + + ACE_OS::printf ("%sstatic %ssigned %s lookup[] =\n%s%s{\n%s", indent, option[CONSTANT] ? "const " : "", + max <= SCHAR_MAX ? "char" : (max <= SHRT_MAX ? "short" : "int"), + indent, indent, option[DEBUGGING] ? "" : " "); + + int count = max; + + // Calculate maximum number of digits required for LOOKUP_ARRAY_SIZE. + + for (Key_List::field_width = 2; (count /= 10) > 0; Key_List::field_width++) + continue; + + const int max_column = 15; + int column = 0; + + for (lookup_ptr = lookup_array; + lookup_ptr < lookup_array + max_hash_value + 1; + lookup_ptr++) + { + if (option[DEBUGGING]) + ACE_OS::printf (" %*d, /* slot = %d */\n", + Key_List::field_width, + *lookup_ptr, + (int)(lookup_ptr - lookup_array)); + else + ACE_OS::printf ("%*d, %s", + Key_List::field_width, + *lookup_ptr, + ++column % (max_column - 1) ? "" : "\n "); + } + ACE_OS::printf ("%s%s%s};\n\n", option[DEBUGGING] ? "" : "\n", indent, indent); + + delete [] duplicates; + delete [] lookup_array; + } + return 0; +} + +// Generates C code to perform the keyword lookup. + +void +Key_List::output_lookup_function (void) +{ + if (!option[OPTIMIZE]) + ACE_OS::printf (" if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)\n {\n"); + ACE_OS::printf (" unsigned int key = %s (str, len);\n\n", option.hash_name ()); + if (!option[OPTIMIZE]) + ACE_OS::printf (" if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE)\n"); + ACE_OS::printf (" {\n"); + + if (option[DUP] && total_duplicates > 0) + { + int pointer_and_type_enabled = option[POINTER] && option[TYPE]; + + ACE_OS::printf (" int slot = lookup[key];\n\n" + " if (slot >= 0 && slot < WORDLIST_SIZE)\n"); + if (option[OPTIMIZE]) + ACE_OS::printf (" return %swordlist[slot];\n", option[TYPE] && option[POINTER] ? "&" : ""); + else + { + ACE_OS::printf (" {\n" + " %schar *s = wordlist[slot]", option[CONSTANT] || pointer_and_type_enabled == 0 ? "const " : ""); + if (array_type_ != Key_List::default_array_type) + ACE_OS::printf (".%s", option.key_name ()); + + ACE_OS::printf (";\n\n if (%s%s == *s && !%s)\n return %s;\n }\n", + option[LENTABLE] ? "len == lengthtable[key]\n && " : "", + option[STRCASECMP] ? "charmap[*str]" : "*str", + option[COMP] ? (option[STRCASECMP] ? "strncasecmp (str + 1, s + 1, len - 1)" : "strncmp (str + 1, s + 1, len - 1)") + : (option[STRCASECMP] ? "strcasecmp (str + 1, s + 1)" : "strcmp (str + 1, s + 1)"), + option[TYPE] && option[POINTER] ? "&wordlist[slot]" : "s"); + ACE_OS::printf (" else if (slot < 0 && slot >= -MAX_HASH_VALUE)\n" + " return 0;\n"); + } + ACE_OS::printf (" else\n {\n" + " unsigned int offset = key + slot + (slot > 0 ? -MAX_HASH_VALUE : MAX_HASH_VALUE);\n" + " %s%s*base = &wordlist[-lookup[offset]];\n" + " %s%s*ptr = base + -lookup[offset + 1];\n\n" + " while (--ptr >= base)\n ", + option[CONSTANT] || pointer_and_type_enabled == 0 ? "const " : "", struct_tag, + option[CONSTANT] || pointer_and_type_enabled == 0 ? "const " : "", struct_tag); + if (array_type_ != Key_List::default_array_type) + { + if (option[COMP]) + ACE_OS::printf ("if (%s == *ptr->%s && !%s (str + 1, ptr->%s + 1, len - 1", + option[STRCASECMP] ? "charmap[*str]" : "*str", option.key_name (), + option[STRCASECMP] ? "strncasecmp" : "strncmp", option.key_name ()); + else + ACE_OS::printf ("if (%s == *ptr->%s && !%s (str + 1, ptr->%s + 1", + option[STRCASECMP] ? "charmap[*str]" : "*str", option.key_name (), + option[STRCASECMP] ? "strcasecmp" : "strcmp", option.key_name ()); + } + else + ACE_OS::printf (option[STRCASECMP] ? "if (charmap[*str] == **ptr && !%s" : "if (*str == **ptr && !%s", + option[COMP] + ? (option[STRCASECMP] ? "strncasecmp (str + 1, *ptr + 1, len - 1" : "strncmp (str + 1, *ptr + 1, len - 1") + : (option[STRCASECMP] ? "strcasecmp (str + 1, *ptr + 1" : "strcmp (str + 1, *ptr + 1")); + ACE_OS::printf ("))\n return %sptr;" + "\n }\n }\n %s\n}\n", array_type_ == + Key_List::default_array_type ? "*" : "", option[OPTIMIZE] ? "" : "}\n return 0;"); + } + else + { + if (option[OPTIMIZE]) + ACE_OS::printf (" return %swordlist[key]", option[TYPE] && option[POINTER] ? "&" : ""); + else + { + int pointer_and_type_enabled = option[POINTER] && option[TYPE]; + + ACE_OS::printf (" %schar *s = wordlist[key]", option[CONSTANT] || pointer_and_type_enabled == 0 ? "const " : ""); + + if (array_type_ != Key_List::default_array_type) + ACE_OS::printf (".%s", option.key_name ()); + + ACE_OS::printf (";\n\n if (%s%s == *s && !%s)\n return %s", + option[LENTABLE] ? "len == lengthtable[key]\n && " : "", + option[STRCASECMP] ? "charmap[*str]" : "*str", + option[COMP] + ? (option[STRCASECMP] ? "strncasecmp (str + 1, s + 1, len - 1)" : "strncmp (str + 1, s + 1, len - 1)") + : (option[STRCASECMP] ? "strcasecmp (str + 1, s + 1)" : "strcmp (str + 1, s + 1)"), + option[TYPE] && option[POINTER] ? "&wordlist[key]" : "s"); + } + ACE_OS::printf (";\n }\n %s\n}\n", option[OPTIMIZE] ? "" : "}\n return 0;"); + } +} + +// Output the table and the functions that map upper case into lower case! + +void +Key_List::output_strcasecmp (void) +{ + ACE_OS::printf ("%s", + "/* This array is designed for mapping upper and lower case letter\n" + " * together for a case independent comparison. The mappings are\n" + " * based upon ascii character sequences.\n */" + "static char charmap[] = {\n" + " '\\000', '\\001', '\\002', '\\003', '\\004', '\\005', '\\006', '\\007',\n" + " '\\010', '\\011', '\\012', '\\013', '\\014', '\\015', '\\016', '\\017',\n" + " '\\020', '\\021', '\\022', '\\023', '\\024', '\\025', '\\026', '\\027',\n" + " '\\030', '\\031', '\\032', '\\033', '\\034', '\\035', '\\036', '\\037',\n" + " '\\040', '\\041', '\\042', '\\043', '\\044', '\\045', '\\046', '\\047',\n" + " '\\050', '\\051', '\\052', '\\053', '\\054', '\\055', '\\056', '\\057',\n" + " '\\060', '\\061', '\\062', '\\063', '\\064', '\\065', '\\066', '\\067',\n" + " '\\070', '\\071', '\\072', '\\073', '\\074', '\\075', '\\076', '\\077',\n" + " '\\100', '\\141', '\\142', '\\143', '\\144', '\\145', '\\146', '\\147',\n" + " '\\150', '\\151', '\\152', '\\153', '\\154', '\\155', '\\156', '\\157',\n" + " '\\160', '\\161', '\\162', '\\163', '\\164', '\\165', '\\166', '\\167',\n" + " '\\170', '\\171', '\\172', '\\133', '\\134', '\\135', '\\136', '\\137',\n" + " '\\140', '\\141', '\\142', '\\143', '\\144', '\\145', '\\146', '\\147',\n" + " '\\150', '\\151', '\\152', '\\153', '\\154', '\\155', '\\156', '\\157',\n" + " '\\160', '\\161', '\\162', '\\163', '\\164', '\\165', '\\166', '\\167',\n" + " '\\170', '\\171', '\\172', '\\173', '\\174', '\\175', '\\176', '\\177',\n" + " '\\200', '\\201', '\\202', '\\203', '\\204', '\\205', '\\206', '\\207',\n" + " '\\210', '\\211', '\\212', '\\213', '\\214', '\\215', '\\216', '\\217',\n" + " '\\220', '\\221', '\\222', '\\223', '\\224', '\\225', '\\226', '\\227',\n" + " '\\230', '\\231', '\\232', '\\233', '\\234', '\\235', '\\236', '\\237',\n" + " '\\240', '\\241', '\\242', '\\243', '\\244', '\\245', '\\246', '\\247',\n" + " '\\250', '\\251', '\\252', '\\253', '\\254', '\\255', '\\256', '\\257',\n" + " '\\260', '\\261', '\\262', '\\263', '\\264', '\\265', '\\266', '\\267',\n" + " '\\270', '\\271', '\\272', '\\273', '\\274', '\\275', '\\276', '\\277',\n" + " '\\300', '\\341', '\\342', '\\343', '\\344', '\\345', '\\346', '\\347',\n" + " '\\350', '\\351', '\\352', '\\353', '\\354', '\\355', '\\356', '\\357',\n" + " '\\360', '\\361', '\\362', '\\363', '\\364', '\\365', '\\366', '\\367',\n" + " '\\370', '\\371', '\\372', '\\333', '\\334', '\\335', '\\336', '\\337',\n" + " '\\340', '\\341', '\\342', '\\343', '\\344', '\\345', '\\346', '\\347',\n" + " '\\350', '\\351', '\\352', '\\353', '\\354', '\\355', '\\356', '\\357',\n" + " '\\360', '\\361', '\\362', '\\363', '\\364', '\\365', '\\366', '\\367',\n" + " '\\370', '\\371', '\\372', '\\373', '\\374', '\\375', '\\376', '\\377',\n};\n\nstatic int\n"); + if (option[COMP]) + { + ACE_OS::printf ("%s", option[ANSI] + ? "strncasecmp (char *s1, char *s2, int n)" + : "strncasecmp (s1, s2, n)\n char *s1, *s2;\n int n;"); + ACE_OS::printf ("\n{\n char *cm = charmap;\n\n while (--n >= 0 && cm[*s1] == cm[*s2++])\n" + " if (*s1++ == '\\0')\n return 0;\n" + "\n return n < 0 ? 0 : cm[*s1] - cm[*--s2];\n}\n\n"); + } + else + { + ACE_OS::printf ("%s", option[ANSI] + ? "strcasecmp (char *s1, char *s2)" + : "strcasecmp (s1, s2)\n char *s1, *s2;"); + ACE_OS::printf ("\n{\n char *cm = charmap;\n\n while (cm[*s1] == cm[*s2++])\n" + " if (*s1++ == '\\0')\n return 0;\n" + "\n return cm[*s1] - cm[*--s2];\n}\n\n"); + } +} + +// Generates the hash function and the key word recognizer function +// based upon the user's Options. + +int +Key_List::output (void) +{ + if (option[BINARYSEARCH]) + // Generate code binary search. + this->output_binary_search_function (); + else if (option[LINEARSEARCH]) + // Generate code for linear search. + this->output_linear_search_function (); + else + { + // Generate the usual GPERF things. + ACE_OS::printf ("%s\n", include_src); + + // Get prototype for strncmp() and strcmp(). + if (!option[SKIPSTRINGH]) + ACE_OS::printf ("#include <string.h>\n"); + + // Output type declaration now, reference it later on.... + if (option[TYPE] && !option[NOTYPE]) + ACE_OS::printf ("%s;\n", + array_type_); + + output_min_max (); + + if (option[STRCASECMP]) + output_strcasecmp (); + + // Class definition if -M is *not* enabled. + if (option[CPLUSPLUS] && !option[SKIPCLASS]) + ACE_OS::printf ("class %s\n{\nprivate:\n" + " static unsigned int %s (const char *str, unsigned int len);\npublic:\n" + " static %s%s%s (const char *str, unsigned int len);\n};\n\n", + option.class_name (), + option.hash_name (), + option[CONSTANT] ? "const " : "", + return_type, + option.function_name ()); + + output_hash_function (); + + if (option[GLOBAL]) + if (option[SWITCH]) + { + if (option[LENTABLE] && option[DUP]) + output_keylength_table (); + if (option[POINTER] && option[TYPE]) + output_keyword_table (); + } + else + { + if (option[LENTABLE]) + output_keylength_table (); + output_keyword_table (); + if (output_lookup_array () == -1) + ACE_ERROR_RETURN ((LM_DEBUG, + "%p\n", + "output_lookup_array"), + -1); + } + + // Use the inline keyword to remove function overhead. + if (option[INLINE]) + ACE_OS::printf ("inline\n"); + + int pointer_and_type_enabled = option[POINTER] && option[TYPE]; + + ACE_OS::printf ("%s%s\n", + option[CONSTANT] || pointer_and_type_enabled == 0 ? "const " : "", + return_type); + if (option[CPLUSPLUS]) + ACE_OS::printf ("%s::", option.class_name ()); + + ACE_OS::printf (option[ANSI] + ? "%s (const char *str, unsigned int len)\n{\n" + : "%s (str, len)\n char *str;\n unsigned int len;\n{\n", + option.function_name ()); + + if (option[ENUM] && !option[GLOBAL]) + ACE_OS::printf (" enum\n {\n" + " TOTAL_KEYWORDS = %d,\n" + " MIN_WORD_LENGTH = %d,\n" + " MAX_WORD_LENGTH = %d,\n" + " MIN_HASH_VALUE = %d,\n" + " MAX_HASH_VALUE = %d,\n" + " HASH_VALUE_RANGE = %d,\n" + " DUPLICATES = %d,\n" + " WORDLIST_SIZE = %d\n };\n\n", + total_keys, min_key_len, max_key_len, min_hash_value, + max_hash_value, max_hash_value - min_hash_value + 1, + total_duplicates ? total_duplicates + 1 : 0, total_keys + min_hash_value); + // Use the switch in place of lookup table. + if (option[SWITCH]) + output_switch (); + // Use the lookup table, in place of switch. + else + { + if (!option[GLOBAL]) + { + if (option[LENTABLE]) + output_keylength_table (); + output_keyword_table (); + } + if (!option[GLOBAL]) + { + switch (output_lookup_array ()) + { + case -1: + ACE_ERROR_RETURN ((LM_DEBUG, + "%p\n", + "output_lookup_array"), + -1); + /* NOTREACHED */ + case 0: + output_lookup_function (); + break; + /* NOTREACHED */ + default: + break; + /* NOTREACHED */ + } + } + else + output_lookup_function (); + } + + if (additional_code) + { + for (;;) + { + int c = getchar (); + + if (c == EOF) + break; + else + putchar (c); + } + } + fflush (stdout); + } + return 0; + } + +// Sorts the keys by hash value. + +void +Key_List::sort (void) +{ + // By default, we sort via hashing. + hash_sort = 1; + occurrence_sort = 0; + + this->head = merge_sort (this->head); +} + +// Sorts the keys by normal strcmp. +void +Key_List::string_sort (void) +{ + + // Flatten the equivalence class list to a linear list. + + List_Node *ptr; + for(ptr=head;ptr;ptr=ptr->next) + { + List_Node *curr; + if(ptr->link) + { + List_Node *last_node = 0; + + for(curr = ptr->link; curr; curr = curr->link) + { + // Chnage the link to next pointer. + curr->next = curr->link; + + // Save the pointer for the last node. + if (curr->link == 0) + last_node = curr; + } + + // Set the pointers, correctly. + last_node->next = ptr->next; + ptr->next = ptr->link; + ptr = last_node; + } + } + + // Set all links to Null. + + for(ptr=head;ptr;ptr=ptr->next) + { + ptr->link = 0; + } + + // Set the sorting options. + + key_sort = 1; + hash_sort = 0; + occurrence_sort = 0; + + // Sort. + + this->head = merge_sort (head); + key_sort = 0; +} + + +// Dumps the key list to stderr stream. + +void +Key_List::dump (void) +{ + ACE_DEBUG ((LM_DEBUG, + "\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 ? total_duplicates + 1 : 0, + max_key_len)); + + u_int keysig_width = option.max_keysig_size () > ACE_OS::strlen ("keysig") + ? option.max_keysig_size () + : static_cast<u_int> (ACE_OS::strlen ("keysig")); + + size_t key_length = this->max_key_length (); + size_t keyword_width = key_length > ACE_OS::strlen ("keysig") + ? key_length + : ACE_OS::strlen ("keysig"); + + ACE_DEBUG ((LM_DEBUG, + "\nList contents are:\n(hash value, key length, slot, %*s, %*s, duplicates):\n", + keysig_width, + "keysig", + keyword_width, + "keyword")); + + for (List_Node *ptr = head; ptr; ptr = ptr->next) + { + ACE_DEBUG ((LM_DEBUG, + "%11d,%11d,%6d, %*s, %*s", + ptr->hash_value, + ptr->length, + ptr->slot, + keysig_width, + ptr->keysig, + keyword_width, + ptr->key)); + + List_Node *dup = ptr->link; + if (dup) + { + for (; + dup != 0; + dup = dup->link) + ACE_DEBUG ((LM_DEBUG, + " %s", + dup->key)); + } + ACE_DEBUG ((LM_DEBUG, + "\n")); + } + ACE_DEBUG ((LM_DEBUG, + "End dumping list.\n\n")); +} + +// Simple-minded constructor action here... + +Key_List::Key_List (void) + : head (0), + total_duplicates (0), + array_type_ ((char *) Key_List::default_array_type), + return_type ((char *) Key_List::default_return_type), + struct_tag ((char *) Key_List::default_array_type), + max_key_len (INT_MIN), + min_key_len (INT_MAX), + key_sort (0), + additional_code (0), + total_keys (1) +{ +} + +// Returns the length of entire key list. + +int +Key_List::keyword_list_length (void) +{ + return list_len; +} + +// Returns length of longest key read. + +int +Key_List::max_key_length (void) +{ + return max_key_len; +} + +#endif /* ACE_HAS_GPERF */ |