mirror of
https://git.savannah.gnu.org/git/gperf.git
synced 2025-12-02 13:09:22 +00:00
2185 lines
64 KiB
C++
2185 lines
64 KiB
C++
/* Routines for building, ordering, and printing the keyword list.
|
|
Copyright (C) 1989-1998, 2000 Free Software Foundation, Inc.
|
|
written by Douglas C. Schmidt (schmidt@ics.uci.edu)
|
|
|
|
This file is part of GNU GPERF.
|
|
|
|
GNU GPERF is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 1, or (at your option)
|
|
any later version.
|
|
|
|
GNU GPERF is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU GPERF; see the file COPYING. If not, write to the Free
|
|
Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
|
|
|
|
#include <stdio.h>
|
|
#include <string.h> /* declares strncpy(), strchr() */
|
|
#include <stdlib.h> /* declares malloc(), free(), abs(), exit(), abort() */
|
|
#include <ctype.h> /* declares isprint() */
|
|
#include <assert.h> /* defines assert() */
|
|
#include <limits.h> /* defines SCHAR_MAX etc. */
|
|
#include "options.h"
|
|
#include "read-line.h"
|
|
#include "hash-table.h"
|
|
#include "key-list.h"
|
|
#include "trace.h"
|
|
#include "version.h"
|
|
|
|
/* Make the hash table 8 times larger than the number of keyword entries. */
|
|
static const int TABLE_MULTIPLE = 10;
|
|
|
|
/* Efficiently returns the least power of two greater than or equal to X! */
|
|
#define POW(X) ((!X)?1:(X-=1,X|=X>>1,X|=X>>2,X|=X>>4,X|=X>>8,X|=X>>16,(++X)))
|
|
|
|
int Key_List::determined[MAX_ALPHA_SIZE];
|
|
|
|
/* Destructor dumps diagnostics during debugging. */
|
|
|
|
Key_List::~Key_List (void)
|
|
{
|
|
T (Trace t ("Key_List::~Key_List");)
|
|
if (option[DEBUG])
|
|
{
|
|
fprintf (stderr, "\nDumping key list information:\ntotal non-static linked keywords = %d"
|
|
"\ntotal keywords = %d\ntotal duplicates = %d\nmaximum key length = %d\n",
|
|
list_len, total_keys, total_duplicates, max_key_len);
|
|
dump ();
|
|
fprintf (stderr, "End dumping list.\n\n");
|
|
}
|
|
}
|
|
|
|
/* Gathers the input stream into a buffer until one of two things occur:
|
|
|
|
1. We read a '%' followed by a '%'
|
|
2. We read a '%' followed by a '}'
|
|
|
|
The first symbolizes the beginning of the keyword list proper,
|
|
The second symbolizes the end of the C source code to be generated
|
|
verbatim in the output file.
|
|
|
|
I assume that the keys are separated from the optional preceding struct
|
|
declaration by a consecutive % followed by either % or } starting in
|
|
the first column. The code below uses an expandible buffer to scan off
|
|
and return a pointer to all the code (if any) appearing before the delimiter. */
|
|
|
|
const char *
|
|
Key_List::get_special_input (char delimiter)
|
|
{
|
|
T (Trace t ("Key_List::get_special_input");)
|
|
int size = 80;
|
|
char *buf = new char[size];
|
|
int c, i;
|
|
|
|
for (i = 0; (c = getchar ()) != EOF; i++)
|
|
{
|
|
if (c == '%')
|
|
{
|
|
if ((c = getchar ()) == delimiter)
|
|
{
|
|
|
|
while ((c = getchar ()) != '\n')
|
|
; /* discard newline */
|
|
|
|
if (i == 0)
|
|
return "";
|
|
else
|
|
{
|
|
buf[delimiter == '%' && buf[i - 2] == ';' ? i - 2 : i - 1] = '\0';
|
|
return buf;
|
|
}
|
|
}
|
|
else
|
|
buf[i++] = '%';
|
|
}
|
|
else if (i >= size) /* Yikes, time to grow the buffer! */
|
|
{
|
|
char *temp = new char[size *= 2];
|
|
int j;
|
|
|
|
for (j = 0; j < i; j++)
|
|
temp[j] = buf[j];
|
|
|
|
buf = temp;
|
|
}
|
|
buf[i] = c;
|
|
}
|
|
|
|
return 0; /* Problem here. */
|
|
}
|
|
|
|
/* Stores any C text that must be included verbatim into the
|
|
generated code output. */
|
|
|
|
const char *
|
|
Key_List::save_include_src (void)
|
|
{
|
|
T (Trace t ("Key_List::save_include_src");)
|
|
int c;
|
|
|
|
if ((c = getchar ()) != '%')
|
|
ungetc (c, stdin);
|
|
else if ((c = getchar ()) != '{')
|
|
{
|
|
fprintf (stderr, "internal error, %c != '{' on line %d in file %s", c, __LINE__, __FILE__);
|
|
exit (1);
|
|
}
|
|
else
|
|
return get_special_input ('}');
|
|
return "";
|
|
}
|
|
|
|
/* Determines from the input file whether the user wants to build a table
|
|
from a user-defined struct, or whether the user is content to simply
|
|
use the default array of keys. */
|
|
|
|
const char *
|
|
Key_List::get_array_type (void)
|
|
{
|
|
T (Trace t ("Key_List::get_array_type");)
|
|
return get_special_input ('%');
|
|
}
|
|
|
|
/* strcspn - find length of initial segment of S consisting entirely
|
|
of characters not from REJECT (borrowed from Henry Spencer's
|
|
ANSI string package, when GNU libc comes out I'll replace this...). */
|
|
|
|
#ifndef strcspn
|
|
inline int
|
|
Key_List::strcspn (const char *s, const char *reject)
|
|
{
|
|
T (Trace t ("Key_List::strcspn");)
|
|
const char *scan;
|
|
const char *rej_scan;
|
|
int count = 0;
|
|
|
|
for (scan = s; *scan; scan++)
|
|
{
|
|
|
|
for (rej_scan = reject; *rej_scan; rej_scan++)
|
|
if (*scan == *rej_scan)
|
|
return count;
|
|
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
#endif
|
|
|
|
/* Sets up the Return_Type, the Struct_Tag type and the Array_Type
|
|
based upon various user Options. */
|
|
|
|
void
|
|
Key_List::set_output_types (void)
|
|
{
|
|
T (Trace t ("Key_List::set_output_types");)
|
|
if (option[TYPE])
|
|
{
|
|
array_type = get_array_type ();
|
|
if (!array_type)
|
|
/* Something's wrong, but we'll catch it later on, in read_keys()... */
|
|
return;
|
|
/* Yow, we've got a user-defined type... */
|
|
int i = strcspn (array_type, "{\n\0");
|
|
/* Remove trailing whitespace. */
|
|
while (i > 0 && strchr (" \t", array_type[i-1]))
|
|
i--;
|
|
int struct_tag_length = i;
|
|
|
|
/* Set `struct_tag' to a naked "struct something". */
|
|
char *structtag = new char[struct_tag_length + 1];
|
|
strncpy (structtag, array_type, struct_tag_length);
|
|
structtag[struct_tag_length] = '\0';
|
|
struct_tag = structtag;
|
|
|
|
/* The return type of the lookup function is "struct something *".
|
|
No "const" here, because if !option[CONST], some user code might want
|
|
to modify the structure. */
|
|
char *rettype = new char[struct_tag_length + 3];
|
|
strncpy (rettype, array_type, struct_tag_length);
|
|
rettype[struct_tag_length] = ' ';
|
|
rettype[struct_tag_length + 1] = '*';
|
|
rettype[struct_tag_length + 2] = '\0';
|
|
return_type = rettype;
|
|
}
|
|
}
|
|
|
|
/* Extracts a key from an input line and creates a new List_Node for it. */
|
|
|
|
static List_Node *
|
|
parse_line (char *line, const char *delimiters)
|
|
{
|
|
if (*line == '"')
|
|
{
|
|
/* Parse a string in ANSI C syntax. */
|
|
char *key = new char[strlen(line)];
|
|
char *kp = key;
|
|
const char *lp = line + 1;
|
|
|
|
for (; *lp;)
|
|
{
|
|
char c = *lp;
|
|
|
|
if (c == '\0')
|
|
{
|
|
fprintf (stderr, "unterminated string: %s\n", line);
|
|
exit (1);
|
|
}
|
|
else if (c == '\\')
|
|
{
|
|
c = *++lp;
|
|
switch (c)
|
|
{
|
|
case '0': case '1': case '2': case '3':
|
|
case '4': case '5': case '6': case '7':
|
|
{
|
|
int code = 0;
|
|
int count = 0;
|
|
while (count < 3 && *lp >= '0' && *lp <= '7')
|
|
{
|
|
code = (code << 3) + (*lp - '0');
|
|
lp++;
|
|
count++;
|
|
}
|
|
if (code > UCHAR_MAX)
|
|
fprintf (stderr, "octal escape out of range: %s\n", line);
|
|
*kp = (char) code;
|
|
break;
|
|
}
|
|
case 'x':
|
|
{
|
|
int code = 0;
|
|
int count = 0;
|
|
lp++;
|
|
while ((*lp >= '0' && *lp <= '9')
|
|
|| (*lp >= 'A' && *lp <= 'F')
|
|
|| (*lp >= 'a' && *lp <= 'f'))
|
|
{
|
|
code = (code << 4)
|
|
+ (*lp >= 'A' && *lp <= 'F' ? *lp - 'A' + 10 :
|
|
*lp >= 'a' && *lp <= 'f' ? *lp - 'a' + 10 :
|
|
*lp - '0');
|
|
lp++;
|
|
count++;
|
|
}
|
|
if (count == 0)
|
|
fprintf (stderr, "hexadecimal escape without any hex digits: %s\n", line);
|
|
if (code > UCHAR_MAX)
|
|
fprintf (stderr, "hexadecimal escape out of range: %s\n", line);
|
|
*kp = (char) code;
|
|
break;
|
|
}
|
|
case '\\': case '\'': case '"':
|
|
*kp = c;
|
|
lp++;
|
|
break;
|
|
case 'n':
|
|
*kp = '\n';
|
|
lp++;
|
|
break;
|
|
case 't':
|
|
*kp = '\t';
|
|
lp++;
|
|
break;
|
|
case 'r':
|
|
*kp = '\r';
|
|
lp++;
|
|
break;
|
|
case 'f':
|
|
*kp = '\f';
|
|
lp++;
|
|
break;
|
|
case 'b':
|
|
*kp = '\b';
|
|
lp++;
|
|
break;
|
|
case 'a':
|
|
*kp = '\a';
|
|
lp++;
|
|
break;
|
|
case 'v':
|
|
*kp = '\v';
|
|
lp++;
|
|
break;
|
|
default:
|
|
fprintf (stderr, "invalid escape sequence in string: %s\n", line);
|
|
exit (1);
|
|
}
|
|
}
|
|
else if (c == '"')
|
|
break;
|
|
else
|
|
{
|
|
*kp = c;
|
|
lp++;
|
|
}
|
|
kp++;
|
|
}
|
|
lp++;
|
|
if (*lp != '\0')
|
|
{
|
|
if (strchr (delimiters, *lp) == NULL)
|
|
{
|
|
fprintf (stderr, "string not followed by delimiter: %s\n", line);
|
|
exit (1);
|
|
}
|
|
lp++;
|
|
}
|
|
return new List_Node (key, kp - key, option[TYPE] ? lp : "");
|
|
}
|
|
else
|
|
{
|
|
/* Not a string. Look for the delimiter. */
|
|
int len = strcspn (line, delimiters);
|
|
const char *rest;
|
|
|
|
if (line[len] == '\0')
|
|
rest = "";
|
|
else
|
|
{
|
|
/* Quick hack to separate the key from the rest, killing the first
|
|
delimiter. */
|
|
line[len] = '\0';
|
|
rest = &line[len + 1];
|
|
}
|
|
return new List_Node (line, len, option[TYPE] ? rest : "");
|
|
}
|
|
}
|
|
|
|
/* Reads in all keys from standard input and creates a linked list pointed
|
|
to by Head. This list is then quickly checked for ``links,'' i.e.,
|
|
unhashable elements possessing identical key sets and lengths. */
|
|
|
|
void
|
|
Key_List::read_keys (void)
|
|
{
|
|
T (Trace t ("Key_List::read_keys");)
|
|
char *ptr;
|
|
|
|
include_src = save_include_src ();
|
|
set_output_types ();
|
|
|
|
/* Oops, problem with the input file. */
|
|
if (! (ptr = Read_Line::get_line ()))
|
|
{
|
|
fprintf (stderr, "No words in input file, did you forget to prepend %s or use -t accidentally?\n", "%%");
|
|
exit (1);
|
|
}
|
|
|
|
/* Read in all the keywords from the input file. */
|
|
else
|
|
{
|
|
const char *delimiter = option.get_delimiter ();
|
|
List_Node *temp, *trail = 0;
|
|
|
|
head = parse_line (ptr, delimiter);
|
|
|
|
for (temp = head;
|
|
(ptr = Read_Line::get_line ()) && strcmp (ptr, "%%");
|
|
temp = temp->next)
|
|
{
|
|
temp->next = parse_line (ptr, delimiter);
|
|
total_keys++;
|
|
}
|
|
|
|
/* See if any additional C code is included at end of this file. */
|
|
if (ptr)
|
|
additional_code = 1;
|
|
|
|
/* Hash table this number of times larger than keyword number. */
|
|
int table_size = (list_len = total_keys) * TABLE_MULTIPLE;
|
|
|
|
#if LARGE_STACK_ARRAYS
|
|
/* By allocating the memory here we save on dynamic allocation overhead.
|
|
Table must be a power of 2 for the hash function scheme to work. */
|
|
List_Node *table[POW (table_size)];
|
|
#else
|
|
// Note: we don't use new, because that invokes a custom operator new.
|
|
int malloc_size = POW (table_size) * sizeof(List_Node*);
|
|
if (malloc_size == 0) malloc_size = 1;
|
|
List_Node **table = (List_Node**)malloc(malloc_size);
|
|
if (table == NULL)
|
|
abort ();
|
|
#endif
|
|
|
|
/* Make large hash table for efficiency. */
|
|
Hash_Table found_link (table, table_size, option[NOLENGTH]);
|
|
|
|
/* Test whether there are any links and also set the maximum length of
|
|
an identifier in the keyword list. */
|
|
|
|
for (temp = head; temp; temp = temp->next)
|
|
{
|
|
List_Node *ptr = found_link.insert (temp);
|
|
|
|
/* Check for links. We deal with these by building an equivalence class
|
|
of all duplicate values (i.e., links) so that only 1 keyword is
|
|
representative of the entire collection. This *greatly* simplifies
|
|
processing during later stages of the program. */
|
|
|
|
if (ptr)
|
|
{
|
|
total_duplicates++;
|
|
list_len--;
|
|
trail->next = temp->next;
|
|
temp->link = ptr->link;
|
|
ptr->link = temp;
|
|
|
|
/* Complain if user hasn't enabled the duplicate option. */
|
|
if (!option[DUP] || option[DEBUG])
|
|
fprintf (stderr, "Key link: \"%.*s\" = \"%.*s\", with key set \"%.*s\".\n",
|
|
temp->key_length, temp->key,
|
|
ptr->key_length, ptr->key,
|
|
temp->char_set_length, temp->char_set);
|
|
}
|
|
else
|
|
trail = temp;
|
|
|
|
/* Update minimum and maximum keyword length, if needed. */
|
|
if (max_key_len < temp->key_length)
|
|
max_key_len = temp->key_length;
|
|
if (min_key_len > temp->key_length)
|
|
min_key_len = temp->key_length;
|
|
}
|
|
|
|
#if !LARGE_STACK_ARRAYS
|
|
free ((char *) table);
|
|
#endif
|
|
|
|
/* Exit program if links exists and option[DUP] not set, since we can't continue */
|
|
if (total_duplicates)
|
|
{
|
|
if (option[DUP])
|
|
fprintf (stderr, "%d input keys have identical hash values, examine output carefully...\n",
|
|
total_duplicates);
|
|
else
|
|
{
|
|
fprintf (stderr, "%d input keys have identical hash values,\ntry different key positions or use option -D.\n",
|
|
total_duplicates);
|
|
exit (1);
|
|
}
|
|
}
|
|
/* Exit program if an empty string is used as key, since the comparison
|
|
expressions don't work correctly for looking up an empty string. */
|
|
if (min_key_len == 0)
|
|
{
|
|
fprintf (stderr, "Empty input key is not allowed.\nTo recognize an empty input key, your code should check for\nlen == 0 before calling the gperf generated lookup function.\n");
|
|
exit (1);
|
|
}
|
|
if (option[ALLCHARS])
|
|
option.set_keysig_size (max_key_len);
|
|
}
|
|
}
|
|
|
|
/* Recursively merges two sorted lists together to form one sorted list. The
|
|
ordering criteria is by frequency of occurrence of elements in the key set
|
|
or by the hash value. This is a kludge, but permits nice sharing of
|
|
almost identical code without incurring the overhead of a function
|
|
call comparison. */
|
|
|
|
List_Node *
|
|
Key_List::merge (List_Node *list1, List_Node *list2)
|
|
{
|
|
T (Trace t ("Key_List::merge");)
|
|
List_Node *result;
|
|
List_Node **resultp = &result;
|
|
for (;;)
|
|
{
|
|
if (!list1)
|
|
{
|
|
*resultp = list2;
|
|
break;
|
|
}
|
|
if (!list2)
|
|
{
|
|
*resultp = list1;
|
|
break;
|
|
}
|
|
if (occurrence_sort && list1->occurrence < list2->occurrence
|
|
|| hash_sort && list1->hash_value > list2->hash_value)
|
|
{
|
|
*resultp = list2;
|
|
resultp = &list2->next; list2 = list1; list1 = *resultp;
|
|
}
|
|
else
|
|
{
|
|
*resultp = list1;
|
|
resultp = &list1->next; list1 = *resultp;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Applies the merge sort algorithm to recursively sort the key list by
|
|
frequency of occurrence of elements in the key set. */
|
|
|
|
List_Node *
|
|
Key_List::merge_sort (List_Node *head)
|
|
{
|
|
T (Trace t ("Key_List::merge_sort");)
|
|
if (!head || !head->next)
|
|
return head;
|
|
else
|
|
{
|
|
List_Node *middle = head;
|
|
List_Node *temp = head->next->next;
|
|
|
|
while (temp)
|
|
{
|
|
temp = temp->next;
|
|
middle = middle->next;
|
|
if (temp)
|
|
temp = temp->next;
|
|
}
|
|
|
|
temp = middle->next;
|
|
middle->next = 0;
|
|
return merge (merge_sort (head), merge_sort (temp));
|
|
}
|
|
}
|
|
|
|
/* Returns the frequency of occurrence of elements in the key set. */
|
|
|
|
inline int
|
|
Key_List::get_occurrence (List_Node *ptr)
|
|
{
|
|
T (Trace t ("Key_List::get_occurrence");)
|
|
int value = 0;
|
|
|
|
const char *p = ptr->char_set;
|
|
unsigned int i = ptr->char_set_length;
|
|
for (; i > 0; p++, i--)
|
|
value += occurrences[(unsigned char)(*p)];
|
|
|
|
return value;
|
|
}
|
|
|
|
/* Enables the index location of all key set elements that are now
|
|
determined. */
|
|
|
|
inline void
|
|
Key_List::set_determined (List_Node *ptr)
|
|
{
|
|
T (Trace t ("Key_List::set_determined");)
|
|
|
|
const char *p = ptr->char_set;
|
|
unsigned int i = ptr->char_set_length;
|
|
for (; i > 0; p++, i--)
|
|
determined[(unsigned char)(*p)] = 1;
|
|
}
|
|
|
|
/* Returns TRUE if PTR's key set is already completely determined. */
|
|
|
|
inline int
|
|
Key_List::already_determined (List_Node *ptr)
|
|
{
|
|
T (Trace t ("Key_List::already_determined");)
|
|
int is_determined = 1;
|
|
|
|
const char *p = ptr->char_set;
|
|
unsigned int i = ptr->char_set_length;
|
|
for (; is_determined && i > 0; p++, i--)
|
|
is_determined = determined[(unsigned char)(*p)];
|
|
|
|
return is_determined;
|
|
}
|
|
|
|
/* Reorders the table by first sorting the list so that frequently occuring
|
|
keys appear first, and then the list is reorded so that keys whose values
|
|
are already determined will be placed towards the front of the list. This
|
|
helps prune the search time by handling inevitable collisions early in the
|
|
search process. See Cichelli's paper from Jan 1980 JACM for details.... */
|
|
|
|
void
|
|
Key_List::reorder (void)
|
|
{
|
|
T (Trace t ("Key_List::reorder");)
|
|
List_Node *ptr;
|
|
for (ptr = head; ptr; ptr = ptr->next)
|
|
ptr->occurrence = get_occurrence (ptr);
|
|
|
|
hash_sort = 0;
|
|
occurrence_sort = 1;
|
|
|
|
for (ptr = head = merge_sort (head); ptr->next; ptr = ptr->next)
|
|
{
|
|
set_determined (ptr);
|
|
|
|
if (already_determined (ptr->next))
|
|
continue;
|
|
else
|
|
{
|
|
List_Node *trail_ptr = ptr->next;
|
|
List_Node *run_ptr = trail_ptr->next;
|
|
|
|
for (; run_ptr; run_ptr = trail_ptr->next)
|
|
{
|
|
|
|
if (already_determined (run_ptr))
|
|
{
|
|
trail_ptr->next = run_ptr->next;
|
|
run_ptr->next = ptr->next;
|
|
ptr = ptr->next = run_ptr;
|
|
}
|
|
else
|
|
trail_ptr = run_ptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ============================ Output routines ============================ */
|
|
|
|
/* The "const " qualifier. */
|
|
static const char *const_always;
|
|
|
|
/* The "const " qualifier, for read-only arrays. */
|
|
static const char *const_readonly_array;
|
|
|
|
/* The "const " qualifier, for the array type. */
|
|
static const char *const_for_struct;
|
|
|
|
/* Returns the smallest unsigned C type capable of holding integers up to N. */
|
|
|
|
static const char *
|
|
smallest_integral_type (int n)
|
|
{
|
|
if (n <= UCHAR_MAX) return "unsigned char";
|
|
if (n <= USHRT_MAX) return "unsigned short";
|
|
return "unsigned int";
|
|
}
|
|
|
|
/* Returns the smallest signed C type capable of holding integers
|
|
from MIN to MAX. */
|
|
|
|
static const char *
|
|
smallest_integral_type (int min, int max)
|
|
{
|
|
if (option[ANSIC] | option[CPLUSPLUS])
|
|
if (min >= SCHAR_MIN && max <= SCHAR_MAX) return "signed char";
|
|
if (min >= SHRT_MIN && max <= SHRT_MAX) return "short";
|
|
return "int";
|
|
}
|
|
|
|
/* A cast from `char' to a valid array index. */
|
|
static const char *char_to_index;
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Computes the maximum and minimum hash values. Since the
|
|
list is already sorted by hash value all we need to do is
|
|
find the final item! */
|
|
|
|
void
|
|
Key_List::compute_min_max (void)
|
|
{
|
|
T (Trace t ("Key_List::compute_min_max");)
|
|
List_Node *temp;
|
|
for (temp = head; temp->next; temp = temp->next)
|
|
;
|
|
|
|
min_hash_value = head->hash_value;
|
|
max_hash_value = temp->hash_value;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Returns the number of different hash values. */
|
|
|
|
int
|
|
Key_List::num_hash_values (void)
|
|
{
|
|
T (Trace t ("Key_List::num_hash_values");)
|
|
int count = 1;
|
|
List_Node *temp;
|
|
int value;
|
|
|
|
for (temp = head, value = temp->hash_value; temp->next; )
|
|
{
|
|
temp = temp->next;
|
|
if (value != temp->hash_value)
|
|
{
|
|
value = temp->hash_value;
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/* -------------------- Output_Constants and subclasses -------------------- */
|
|
|
|
/* This class outputs an enumeration defining some constants. */
|
|
|
|
struct Output_Constants
|
|
{
|
|
virtual void output_start () = 0;
|
|
virtual void output_item (const char *name, int value) = 0;
|
|
virtual void output_end () = 0;
|
|
Output_Constants () {}
|
|
virtual ~Output_Constants () {}
|
|
};
|
|
|
|
/* This class outputs an enumeration in #define syntax. */
|
|
|
|
struct Output_Defines : public Output_Constants
|
|
{
|
|
virtual void output_start ();
|
|
virtual void output_item (const char *name, int value);
|
|
virtual void output_end ();
|
|
Output_Defines () {}
|
|
virtual ~Output_Defines () {}
|
|
};
|
|
|
|
void Output_Defines::output_start ()
|
|
{
|
|
T (Trace t ("Output_Defines::output_start");)
|
|
printf ("\n");
|
|
}
|
|
|
|
void Output_Defines::output_item (const char *name, int value)
|
|
{
|
|
T (Trace t ("Output_Defines::output_item");)
|
|
printf ("#define %s %d\n", name, value);
|
|
}
|
|
|
|
void Output_Defines::output_end ()
|
|
{
|
|
T (Trace t ("Output_Defines::output_end");)
|
|
}
|
|
|
|
/* This class outputs an enumeration using `enum'. */
|
|
|
|
struct Output_Enum : public Output_Constants
|
|
{
|
|
virtual void output_start ();
|
|
virtual void output_item (const char *name, int value);
|
|
virtual void output_end ();
|
|
Output_Enum (const char *indent) : indentation (indent) {}
|
|
virtual ~Output_Enum () {}
|
|
private:
|
|
const char *indentation;
|
|
int pending_comma;
|
|
};
|
|
|
|
void Output_Enum::output_start ()
|
|
{
|
|
T (Trace t ("Output_Enum::output_start");)
|
|
printf ("%senum\n"
|
|
"%s {\n",
|
|
indentation, indentation);
|
|
pending_comma = 0;
|
|
}
|
|
|
|
void Output_Enum::output_item (const char *name, int value)
|
|
{
|
|
T (Trace t ("Output_Enum::output_item");)
|
|
if (pending_comma)
|
|
printf (",\n");
|
|
printf ("%s %s = %d", indentation, name, value);
|
|
pending_comma = 1;
|
|
}
|
|
|
|
void Output_Enum::output_end ()
|
|
{
|
|
T (Trace t ("Output_Enum::output_end");)
|
|
if (pending_comma)
|
|
printf ("\n");
|
|
printf ("%s };\n\n", indentation);
|
|
}
|
|
|
|
/* Outputs the maximum and minimum hash values etc. */
|
|
|
|
void
|
|
Key_List::output_constants (struct Output_Constants& style)
|
|
{
|
|
T (Trace t ("Key_List::output_constants");)
|
|
|
|
style.output_start ();
|
|
style.output_item ("TOTAL_KEYWORDS", total_keys);
|
|
style.output_item ("MIN_WORD_LENGTH", min_key_len);
|
|
style.output_item ("MAX_WORD_LENGTH", max_key_len);
|
|
style.output_item ("MIN_HASH_VALUE", min_hash_value);
|
|
style.output_item ("MAX_HASH_VALUE", max_hash_value);
|
|
style.output_end ();
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Outputs a keyword, as a string: enclosed in double quotes, escaping
|
|
backslashes, double quote and unprintable characters. */
|
|
|
|
static void
|
|
output_string (const char *key, int len)
|
|
{
|
|
T (Trace t ("output_string");)
|
|
|
|
putchar ('"');
|
|
for (; len > 0; len--)
|
|
{
|
|
unsigned char c = (unsigned char) *key++;
|
|
if (isprint (c))
|
|
{
|
|
if (c == '"' || c == '\\')
|
|
putchar ('\\');
|
|
putchar (c);
|
|
}
|
|
else
|
|
{
|
|
putchar ('\\');
|
|
putchar ('0' + ((c >> 6) & 7));
|
|
putchar ('0' + ((c >> 3) & 7));
|
|
putchar ('0' + (c & 7));
|
|
}
|
|
}
|
|
putchar ('"');
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Outputs a type and a const specifier.
|
|
The output is terminated with a space. */
|
|
|
|
static void
|
|
output_const_type (const char *const_string, const char *type_string)
|
|
{
|
|
if (type_string[strlen(type_string)-1] == '*')
|
|
printf ("%s %s", type_string, const_string);
|
|
else
|
|
printf ("%s%s ", const_string, type_string);
|
|
}
|
|
|
|
/* ----------------------- Output_Expr and subclasses ----------------------- */
|
|
|
|
/* This class outputs a general expression. */
|
|
|
|
struct Output_Expr
|
|
{
|
|
virtual void output_expr () const = 0;
|
|
Output_Expr () {}
|
|
virtual ~Output_Expr () {}
|
|
};
|
|
|
|
/* This class outputs an expression formed by a single string. */
|
|
|
|
struct Output_Expr1 : public Output_Expr
|
|
{
|
|
virtual void output_expr () const;
|
|
Output_Expr1 (const char *piece1) : p1 (piece1) {}
|
|
virtual ~Output_Expr1 () {}
|
|
private:
|
|
const char *p1;
|
|
};
|
|
|
|
void Output_Expr1::output_expr () const
|
|
{
|
|
T (Trace t ("Output_Expr1::output_expr");)
|
|
printf ("%s", p1);
|
|
}
|
|
|
|
#if 0 /* unused */
|
|
|
|
/* This class outputs an expression formed by the concatenation of two
|
|
strings. */
|
|
|
|
struct Output_Expr2 : public Output_Expr
|
|
{
|
|
virtual void output_expr () const;
|
|
Output_Expr2 (const char *piece1, const char *piece2)
|
|
: p1 (piece1), p2 (piece2) {}
|
|
virtual ~Output_Expr2 () {}
|
|
private:
|
|
const char *p1;
|
|
const char *p2;
|
|
};
|
|
|
|
void Output_Expr2::output_expr () const
|
|
{
|
|
T (Trace t ("Output_Expr2::output_expr");)
|
|
printf ("%s%s", p1, p2);
|
|
}
|
|
|
|
#endif
|
|
|
|
/* --------------------- Output_Compare and subclasses --------------------- */
|
|
|
|
/* This class outputs a comparison expression. */
|
|
|
|
struct Output_Compare
|
|
{
|
|
virtual void output_comparison (const Output_Expr& expr1,
|
|
const Output_Expr& expr2) const = 0;
|
|
Output_Compare () {}
|
|
virtual ~Output_Compare () {}
|
|
};
|
|
|
|
/* This class outputs a comparison using strcmp. */
|
|
|
|
struct Output_Compare_Strcmp : public Output_Compare
|
|
{
|
|
virtual void output_comparison (const Output_Expr& expr1,
|
|
const Output_Expr& expr2) const;
|
|
Output_Compare_Strcmp () {}
|
|
virtual ~Output_Compare_Strcmp () {}
|
|
};
|
|
|
|
void Output_Compare_Strcmp::output_comparison (const Output_Expr& expr1,
|
|
const Output_Expr& expr2) const
|
|
{
|
|
T (Trace t ("Output_Compare_Strcmp::output_comparison");)
|
|
printf ("*");
|
|
expr1.output_expr ();
|
|
printf (" == *");
|
|
expr2.output_expr ();
|
|
printf (" && !strcmp (");
|
|
expr1.output_expr ();
|
|
printf (" + 1, ");
|
|
expr2.output_expr ();
|
|
printf (" + 1)");
|
|
}
|
|
|
|
/* This class outputs a comparison using strncmp.
|
|
Note that the length of expr1 will be available through the local variable
|
|
`len'. */
|
|
|
|
struct Output_Compare_Strncmp : public Output_Compare
|
|
{
|
|
virtual void output_comparison (const Output_Expr& expr1,
|
|
const Output_Expr& expr2) const;
|
|
Output_Compare_Strncmp () {}
|
|
virtual ~Output_Compare_Strncmp () {}
|
|
};
|
|
|
|
void Output_Compare_Strncmp::output_comparison (const Output_Expr& expr1,
|
|
const Output_Expr& expr2) const
|
|
{
|
|
T (Trace t ("Output_Compare_Strncmp::output_comparison");)
|
|
printf ("*");
|
|
expr1.output_expr ();
|
|
printf (" == *");
|
|
expr2.output_expr ();
|
|
printf (" && !strncmp (");
|
|
expr1.output_expr ();
|
|
printf (" + 1, ");
|
|
expr2.output_expr ();
|
|
printf (" + 1, len - 1) && ");
|
|
expr2.output_expr ();
|
|
printf ("[len] == '\\0'");
|
|
}
|
|
|
|
/* This class outputs a comparison using memcmp.
|
|
Note that the length of expr1 (available through the local variable `len')
|
|
must be verified to be equal to the length of expr2 prior to this
|
|
comparison. */
|
|
|
|
struct Output_Compare_Memcmp : public Output_Compare
|
|
{
|
|
virtual void output_comparison (const Output_Expr& expr1,
|
|
const Output_Expr& expr2) const;
|
|
Output_Compare_Memcmp () {}
|
|
virtual ~Output_Compare_Memcmp () {}
|
|
};
|
|
|
|
void Output_Compare_Memcmp::output_comparison (const Output_Expr& expr1,
|
|
const Output_Expr& expr2) const
|
|
{
|
|
T (Trace t ("Output_Compare_Memcmp::output_comparison");)
|
|
printf ("*");
|
|
expr1.output_expr ();
|
|
printf (" == *");
|
|
expr2.output_expr ();
|
|
printf (" && !memcmp (");
|
|
expr1.output_expr ();
|
|
printf (" + 1, ");
|
|
expr2.output_expr ();
|
|
printf (" + 1, len - 1)");
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Generates C code for the hash function that returns the
|
|
proper encoding for each key word. */
|
|
|
|
void
|
|
Key_List::output_hash_function (void)
|
|
{
|
|
T (Trace t ("Key_List::output_hash_function");)
|
|
const int max_column = 10;
|
|
int field_width;
|
|
|
|
/* Calculate maximum number of digits required for MAX_HASH_VALUE. */
|
|
field_width = 2;
|
|
for (int trunc = max_hash_value; (trunc /= 10) > 0;)
|
|
field_width++;
|
|
|
|
/* Output the function's head. */
|
|
if (option[CPLUSPLUS])
|
|
printf ("inline ");
|
|
else if (option[KRC] | option[C] | option[ANSIC])
|
|
printf ("#ifdef __GNUC__\n"
|
|
"__inline\n"
|
|
"#else\n"
|
|
"#ifdef __cplusplus\n"
|
|
"inline\n"
|
|
"#endif\n"
|
|
"#endif\n");
|
|
|
|
if (option[KRC] | option[C] | option[ANSIC])
|
|
printf ("static ");
|
|
printf ("unsigned int\n");
|
|
if (option[CPLUSPLUS])
|
|
printf ("%s::", option.get_class_name ());
|
|
printf ("%s ", option.get_hash_name ());
|
|
printf (option[KRC] ?
|
|
"(str, len)\n"
|
|
" register char *str;\n"
|
|
" register unsigned int len;\n" :
|
|
option[C] ?
|
|
"(str, len)\n"
|
|
" register const char *str;\n"
|
|
" register unsigned int len;\n" :
|
|
option[ANSIC] | option[CPLUSPLUS] ?
|
|
"(register const char *str, register unsigned int len)\n" :
|
|
"");
|
|
|
|
/* Note that when the hash function is called, it has already been verified
|
|
that min_key_len <= len <= max_key_len. */
|
|
|
|
/* Output the function's body. */
|
|
printf ("{\n");
|
|
|
|
/* First the asso_values array. */
|
|
printf (" static %s%s asso_values[] =\n"
|
|
" {",
|
|
const_readonly_array,
|
|
smallest_integral_type (max_hash_value + 1));
|
|
|
|
for (int count = 0; count < ALPHA_SIZE; count++)
|
|
{
|
|
if (count > 0)
|
|
printf (",");
|
|
if (!(count % max_column))
|
|
printf ("\n ");
|
|
printf ("%*d", field_width,
|
|
occurrences[count] ? asso_values[count] : max_hash_value + 1);
|
|
}
|
|
|
|
printf ("\n"
|
|
" };\n");
|
|
|
|
/* Optimize special case of ``-k 1,$'' */
|
|
if (option[DEFAULTCHARS])
|
|
printf (" return %sasso_values[%sstr[len - 1]] + asso_values[%sstr[0]];\n",
|
|
option[NOLENGTH] ? "" : "len + ",
|
|
char_to_index, char_to_index);
|
|
else
|
|
{
|
|
int key_pos;
|
|
|
|
option.reset ();
|
|
|
|
/* Get first (also highest) key position. */
|
|
key_pos = option.get ();
|
|
|
|
if (!option[ALLCHARS] && (key_pos == WORD_END || key_pos <= min_key_len))
|
|
{
|
|
/* We can perform additional optimizations here:
|
|
Write it out as a single expression. Note that the values
|
|
are added as `int's even though the asso_values array may
|
|
contain `unsigned char's or `unsigned short's. */
|
|
|
|
printf (" return %s",
|
|
option[NOLENGTH] ? "" : "len + ");
|
|
|
|
for (; key_pos != WORD_END; )
|
|
{
|
|
printf ("asso_values[%sstr[%d]]", char_to_index, key_pos - 1);
|
|
if ((key_pos = option.get ()) != EOS)
|
|
printf (" + ");
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (key_pos == WORD_END)
|
|
printf ("asso_values[%sstr[len - 1]]", char_to_index);
|
|
|
|
printf (";\n");
|
|
}
|
|
else
|
|
{
|
|
/* We've got to use the correct, but brute force, technique. */
|
|
printf (" register int hval = %s;\n\n"
|
|
" switch (%s)\n"
|
|
" {\n"
|
|
" default:\n",
|
|
option[NOLENGTH] ? "0" : "len",
|
|
option[NOLENGTH] ? "len" : "hval");
|
|
|
|
/* User wants *all* characters considered in hash. */
|
|
if (option[ALLCHARS])
|
|
{
|
|
for (int i = max_key_len; i > 0; i--)
|
|
printf (" case %d:\n"
|
|
" hval += asso_values[%sstr[%d]];\n",
|
|
i, char_to_index, i - 1);
|
|
|
|
printf (" break;\n"
|
|
" }\n"
|
|
" return hval;\n");
|
|
}
|
|
else /* do the hard part... */
|
|
{
|
|
while (key_pos != WORD_END && key_pos > max_key_len)
|
|
if ((key_pos = option.get ()) == EOS)
|
|
break;
|
|
|
|
if (key_pos != EOS && key_pos != WORD_END)
|
|
{
|
|
int i = key_pos;
|
|
do
|
|
{
|
|
for ( ; i >= key_pos; i--)
|
|
printf (" case %d:\n", i);
|
|
|
|
printf (" hval += asso_values[%sstr[%d]];\n",
|
|
char_to_index, key_pos - 1);
|
|
|
|
key_pos = option.get ();
|
|
}
|
|
while (key_pos != EOS && key_pos != WORD_END);
|
|
|
|
for ( ; i >= min_key_len; i--)
|
|
printf (" case %d:\n", i);
|
|
}
|
|
|
|
printf (" break;\n"
|
|
" }\n"
|
|
" return hval");
|
|
if (key_pos == WORD_END)
|
|
printf (" + asso_values[%sstr[len - 1]]", char_to_index);
|
|
printf (";\n");
|
|
}
|
|
}
|
|
}
|
|
printf ("}\n\n");
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Prints out a table of keyword lengths, for use with the
|
|
comparison code in generated function ``in_word_set''. */
|
|
|
|
void
|
|
Key_List::output_keylength_table (void)
|
|
{
|
|
T (Trace t ("Key_List::output_keylength_table");)
|
|
const int columns = 14;
|
|
int index;
|
|
int column;
|
|
const char *indent = option[GLOBAL] ? "" : " ";
|
|
List_Node *temp;
|
|
|
|
printf ("%sstatic %s%s lengthtable[] =\n%s {",
|
|
indent, const_readonly_array,
|
|
smallest_integral_type (max_key_len),
|
|
indent);
|
|
|
|
/* Generate an array of lengths, similar to output_keyword_table. */
|
|
|
|
column = 0;
|
|
for (temp = head, index = 0; temp; temp = temp->next)
|
|
{
|
|
if (option[SWITCH] && !option[TYPE]
|
|
&& !(temp->link
|
|
|| (temp->next && temp->hash_value == temp->next->hash_value)))
|
|
continue;
|
|
|
|
if (index < temp->hash_value && !option[SWITCH] && !option[DUP])
|
|
{
|
|
/* Some blank entries. */
|
|
for ( ; index < temp->hash_value; index++)
|
|
{
|
|
if (index > 0)
|
|
printf (",");
|
|
if ((column++ % columns) == 0)
|
|
printf ("\n%s ", indent);
|
|
printf ("%3d", 0);
|
|
}
|
|
}
|
|
|
|
if (index > 0)
|
|
printf (",");
|
|
if ((column++ % columns) == 0)
|
|
printf("\n%s ", indent);
|
|
printf ("%3d", temp->key_length);
|
|
|
|
/* Deal with links specially. */
|
|
if (temp->link) // implies option[DUP]
|
|
for (List_Node *links = temp->link; links; links = links->link)
|
|
{
|
|
++index;
|
|
printf (",");
|
|
if ((column++ % columns) == 0)
|
|
printf("\n%s ", indent);
|
|
printf ("%3d", links->key_length);
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
printf ("\n%s };\n", indent);
|
|
if (option[GLOBAL])
|
|
printf ("\n");
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static void
|
|
output_keyword_entry (List_Node *temp, const char *indent)
|
|
{
|
|
printf ("%s ", indent);
|
|
if (option[TYPE])
|
|
printf ("{");
|
|
output_string (temp->key, temp->key_length);
|
|
if (option[TYPE])
|
|
{
|
|
if (strlen (temp->rest) > 0)
|
|
printf (",%s", temp->rest);
|
|
printf ("}");
|
|
}
|
|
if (option[DEBUG])
|
|
printf (" /* hash value = %d, index = %d */",
|
|
temp->hash_value, temp->index);
|
|
}
|
|
|
|
static void
|
|
output_keyword_blank_entries (int count, const char *indent)
|
|
{
|
|
int columns;
|
|
if (option[TYPE])
|
|
{
|
|
columns = 58 / (6 + strlen (option.get_initializer_suffix()));
|
|
if (columns == 0)
|
|
columns = 1;
|
|
}
|
|
else
|
|
{
|
|
columns = 9;
|
|
}
|
|
int column = 0;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
if ((column % columns) == 0)
|
|
{
|
|
if (i > 0)
|
|
printf (",\n");
|
|
printf ("%s ", indent);
|
|
}
|
|
else
|
|
{
|
|
if (i > 0)
|
|
printf (", ");
|
|
}
|
|
if (option[TYPE])
|
|
printf ("{\"\"%s}", option.get_initializer_suffix());
|
|
else
|
|
printf ("\"\"");
|
|
column++;
|
|
}
|
|
}
|
|
|
|
/* Prints out the array containing the key words for the hash function. */
|
|
|
|
void
|
|
Key_List::output_keyword_table (void)
|
|
{
|
|
T (Trace t ("Key_List::output_keyword_table");)
|
|
const char *indent = option[GLOBAL] ? "" : " ";
|
|
int index;
|
|
List_Node *temp;
|
|
|
|
printf ("%sstatic ",
|
|
indent);
|
|
output_const_type (const_readonly_array, struct_tag);
|
|
printf ("%s[] =\n"
|
|
"%s {\n",
|
|
option.get_wordlist_name (),
|
|
indent);
|
|
|
|
/* Generate an array of reserved words at appropriate locations. */
|
|
|
|
for (temp = head, index = 0; temp; temp = temp->next)
|
|
{
|
|
if (option[SWITCH] && !option[TYPE]
|
|
&& !(temp->link
|
|
|| (temp->next && temp->hash_value == temp->next->hash_value)))
|
|
continue;
|
|
|
|
if (index > 0)
|
|
printf (",\n");
|
|
|
|
if (index < temp->hash_value && !option[SWITCH] && !option[DUP])
|
|
{
|
|
/* Some blank entries. */
|
|
output_keyword_blank_entries (temp->hash_value - index, indent);
|
|
printf (",\n");
|
|
index = temp->hash_value;
|
|
}
|
|
|
|
temp->index = index;
|
|
|
|
output_keyword_entry (temp, indent);
|
|
|
|
/* Deal with links specially. */
|
|
if (temp->link) // implies option[DUP]
|
|
for (List_Node *links = temp->link; links; links = links->link)
|
|
{
|
|
links->index = ++index;
|
|
printf (",\n");
|
|
output_keyword_entry (links, indent);
|
|
}
|
|
|
|
index++;
|
|
}
|
|
if (index > 0)
|
|
printf ("\n");
|
|
|
|
printf ("%s };\n\n", indent);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Generates the large, sparse table that maps hash values into
|
|
the smaller, contiguous range of the keyword table. */
|
|
|
|
void
|
|
Key_List::output_lookup_array (void)
|
|
{
|
|
T (Trace t ("Key_List::output_lookup_array");)
|
|
if (option[DUP])
|
|
{
|
|
const int DEFAULT_VALUE = -1;
|
|
|
|
/* Because of the way output_keyword_table works, every duplicate set is
|
|
stored contiguously in the wordlist array. */
|
|
struct duplicate_entry
|
|
{
|
|
int hash_value; /* Hash value for this particular duplicate set. */
|
|
int index; /* Index into the main keyword storage array. */
|
|
int count; /* Number of consecutive duplicates at this index. */
|
|
};
|
|
|
|
#if LARGE_STACK_ARRAYS
|
|
duplicate_entry duplicates[total_duplicates];
|
|
int lookup_array[max_hash_value + 1 + 2*total_duplicates];
|
|
#else
|
|
// Note: we don't use new, because that invokes a custom operator new.
|
|
duplicate_entry *duplicates = (duplicate_entry *)
|
|
malloc (total_duplicates * sizeof(duplicate_entry) + 1);
|
|
int *lookup_array = (int *)
|
|
malloc ((max_hash_value + 1 + 2*total_duplicates) * sizeof(int));
|
|
if (duplicates == NULL || lookup_array == NULL)
|
|
abort();
|
|
#endif
|
|
int lookup_array_size = max_hash_value + 1;
|
|
duplicate_entry *dup_ptr = &duplicates[0];
|
|
int *lookup_ptr = &lookup_array[max_hash_value + 1 + 2*total_duplicates];
|
|
|
|
while (lookup_ptr > lookup_array)
|
|
*--lookup_ptr = DEFAULT_VALUE;
|
|
|
|
/* Now dup_ptr = &duplicates[0] and lookup_ptr = &lookup_array[0]. */
|
|
|
|
for (List_Node *temp = head; temp; temp = temp->next)
|
|
{
|
|
int hash_value = temp->hash_value;
|
|
lookup_array[hash_value] = temp->index;
|
|
if (option[DEBUG])
|
|
fprintf (stderr, "keyword = %.*s, index = %d\n",
|
|
temp->key_length, temp->key, temp->index);
|
|
if (temp->link
|
|
|| (temp->next && hash_value == temp->next->hash_value))
|
|
{
|
|
/* Start a duplicate entry. */
|
|
dup_ptr->hash_value = hash_value;
|
|
dup_ptr->index = temp->index;
|
|
dup_ptr->count = 1;
|
|
|
|
for (;;)
|
|
{
|
|
for (List_Node *ptr = temp->link; ptr; ptr = ptr->link)
|
|
{
|
|
dup_ptr->count++;
|
|
if (option[DEBUG])
|
|
fprintf (stderr,
|
|
"static linked keyword = %.*s, index = %d\n",
|
|
ptr->key_length, ptr->key, ptr->index);
|
|
}
|
|
|
|
if (!(temp->next && hash_value == temp->next->hash_value))
|
|
break;
|
|
|
|
temp = temp->next;
|
|
|
|
dup_ptr->count++;
|
|
if (option[DEBUG])
|
|
fprintf (stderr, "dynamic linked keyword = %.*s, index = %d\n",
|
|
temp->key_length, temp->key, temp->index);
|
|
}
|
|
assert (dup_ptr->count >= 2);
|
|
dup_ptr++;
|
|
}
|
|
}
|
|
|
|
while (dup_ptr > duplicates)
|
|
{
|
|
dup_ptr--;
|
|
|
|
if (option[DEBUG])
|
|
fprintf (stderr,
|
|
"dup_ptr[%d]: hash_value = %d, index = %d, count = %d\n",
|
|
dup_ptr - duplicates,
|
|
dup_ptr->hash_value, dup_ptr->index, dup_ptr->count);
|
|
|
|
int i;
|
|
/* Start searching for available space towards the right part
|
|
of the lookup array. */
|
|
for (i = dup_ptr->hash_value; i < lookup_array_size-1; i++)
|
|
if (lookup_array[i] == DEFAULT_VALUE
|
|
&& lookup_array[i + 1] == DEFAULT_VALUE)
|
|
goto found_i;
|
|
/* If we didn't find it to the right look to the left instead... */
|
|
for (i = dup_ptr->hash_value-1; i >= 0; i--)
|
|
if (lookup_array[i] == DEFAULT_VALUE
|
|
&& lookup_array[i + 1] == DEFAULT_VALUE)
|
|
goto found_i;
|
|
/* Append to the end of lookup_array. */
|
|
i = lookup_array_size;
|
|
lookup_array_size += 2;
|
|
found_i:
|
|
/* Put in an indirection from dup_ptr->hash_value to i.
|
|
At i and i+1 store dup_ptr->index and dup_ptr->count. */
|
|
assert (lookup_array[dup_ptr->hash_value] == dup_ptr->index);
|
|
lookup_array[dup_ptr->hash_value] = - 1 - total_keys - i;
|
|
lookup_array[i] = - total_keys + dup_ptr->index;
|
|
lookup_array[i + 1] = - dup_ptr->count;
|
|
/* All these three values are <= -2, distinct from DEFAULT_VALUE. */
|
|
}
|
|
|
|
/* The values of the lookup array are now known. */
|
|
|
|
int min = INT_MAX;
|
|
int max = INT_MIN;
|
|
lookup_ptr = lookup_array + lookup_array_size;
|
|
while (lookup_ptr > lookup_array)
|
|
{
|
|
int val = *--lookup_ptr;
|
|
if (min > val)
|
|
min = val;
|
|
if (max < val)
|
|
max = val;
|
|
}
|
|
|
|
const char *indent = option[GLOBAL] ? "" : " ";
|
|
printf ("%sstatic %s%s lookup[] =\n"
|
|
"%s {",
|
|
indent, const_readonly_array, smallest_integral_type (min, max),
|
|
indent);
|
|
|
|
int field_width;
|
|
/* Calculate maximum number of digits required for MIN..MAX. */
|
|
{
|
|
field_width = 2;
|
|
for (int trunc = max; (trunc /= 10) > 0;)
|
|
field_width++;
|
|
}
|
|
if (min < 0)
|
|
{
|
|
int neg_field_width = 2;
|
|
for (int trunc = -min; (trunc /= 10) > 0;)
|
|
neg_field_width++;
|
|
neg_field_width++; /* account for the minus sign */
|
|
if (field_width < neg_field_width)
|
|
field_width = neg_field_width;
|
|
}
|
|
|
|
const int columns = 42 / field_width;
|
|
int column;
|
|
|
|
column = 0;
|
|
for (int i = 0; i < lookup_array_size; i++)
|
|
{
|
|
if (i > 0)
|
|
printf (",");
|
|
if ((column++ % columns) == 0)
|
|
printf("\n%s ", indent);
|
|
printf ("%*d", field_width, lookup_array[i]);
|
|
}
|
|
printf ("\n%s };\n\n", indent);
|
|
|
|
#if !LARGE_STACK_ARRAYS
|
|
free ((char *) duplicates);
|
|
free ((char *) lookup_array);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Generate all the tables needed for the lookup function. */
|
|
|
|
void
|
|
Key_List::output_lookup_tables (void)
|
|
{
|
|
T (Trace t ("Key_List::output_lookup_tables");)
|
|
|
|
if (option[SWITCH])
|
|
{
|
|
/* Use the switch in place of lookup table. */
|
|
if (option[LENTABLE] && (option[DUP] && total_duplicates > 0))
|
|
output_keylength_table ();
|
|
if (option[TYPE] || (option[DUP] && total_duplicates > 0))
|
|
output_keyword_table ();
|
|
}
|
|
else
|
|
{
|
|
/* Use the lookup table, in place of switch. */
|
|
if (option[LENTABLE])
|
|
output_keylength_table ();
|
|
output_keyword_table ();
|
|
output_lookup_array ();
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Output a single switch case (including duplicates). Advance list. */
|
|
|
|
static List_Node *
|
|
output_switch_case (List_Node *list, int indent, int *jumps_away)
|
|
{
|
|
T (Trace t ("output_switch_case");)
|
|
|
|
if (option[DEBUG])
|
|
printf ("%*s/* hash value = %4d, keyword = \"%.*s\" */\n",
|
|
indent, "", list->hash_value, list->key_length, list->key);
|
|
|
|
if (option[DUP]
|
|
&& (list->link
|
|
|| (list->next && list->hash_value == list->next->hash_value)))
|
|
{
|
|
if (option[LENTABLE])
|
|
printf ("%*slengthptr = &lengthtable[%d];\n",
|
|
indent, "", list->index);
|
|
printf ("%*swordptr = &%s[%d];\n",
|
|
indent, "", option.get_wordlist_name (), list->index);
|
|
|
|
int count = 0;
|
|
for (List_Node *temp = list; ; temp = temp->next)
|
|
{
|
|
for (List_Node *links = temp; links; links = links->link)
|
|
count++;
|
|
if (!(temp->next && temp->hash_value == temp->next->hash_value))
|
|
break;
|
|
}
|
|
|
|
printf ("%*swordendptr = wordptr + %d;\n"
|
|
"%*sgoto multicompare;\n",
|
|
indent, "", count,
|
|
indent, "");
|
|
*jumps_away = 1;
|
|
}
|
|
else
|
|
{
|
|
if (option[LENTABLE])
|
|
{
|
|
printf ("%*sif (len == %d)\n"
|
|
"%*s {\n",
|
|
indent, "", list->key_length,
|
|
indent, "");
|
|
indent += 4;
|
|
}
|
|
printf ("%*sresword = ",
|
|
indent, "");
|
|
if (option[TYPE])
|
|
printf ("&%s[%d]", option.get_wordlist_name (), list->index);
|
|
else
|
|
output_string (list->key, list->key_length);
|
|
printf (";\n");
|
|
printf ("%*sgoto compare;\n",
|
|
indent, "");
|
|
if (option[LENTABLE])
|
|
{
|
|
indent -= 4;
|
|
printf ("%*s }\n",
|
|
indent, "");
|
|
}
|
|
else
|
|
*jumps_away = 1;
|
|
}
|
|
|
|
while (list->next && list->hash_value == list->next->hash_value)
|
|
list = list->next;
|
|
list = list->next;
|
|
return list;
|
|
}
|
|
|
|
/* Output a total of size cases, grouped into num_switches switch statements,
|
|
where 0 < num_switches <= size. */
|
|
|
|
static void
|
|
output_switches (List_Node *list, int num_switches, int size, int min_hash_value, int max_hash_value, int indent)
|
|
{
|
|
T (Trace t ("output_switches");)
|
|
|
|
if (option[DEBUG])
|
|
printf ("%*s/* know %d <= key <= %d, contains %d cases */\n",
|
|
indent, "", min_hash_value, max_hash_value, size);
|
|
|
|
if (num_switches > 1)
|
|
{
|
|
int part1 = num_switches / 2;
|
|
int part2 = num_switches - part1;
|
|
int size1 = (int)((double)size / (double)num_switches * (double)part1 + 0.5);
|
|
int size2 = size - size1;
|
|
|
|
List_Node *temp = list;
|
|
for (int count = size1; count > 0; count--)
|
|
{
|
|
while (temp->hash_value == temp->next->hash_value)
|
|
temp = temp->next;
|
|
temp = temp->next;
|
|
}
|
|
|
|
printf ("%*sif (key < %d)\n"
|
|
"%*s {\n",
|
|
indent, "", temp->hash_value,
|
|
indent, "");
|
|
|
|
output_switches (list, part1, size1, min_hash_value, temp->hash_value-1, indent+4);
|
|
|
|
printf ("%*s }\n"
|
|
"%*selse\n"
|
|
"%*s {\n",
|
|
indent, "", indent, "", indent, "");
|
|
|
|
output_switches (temp, part2, size2, temp->hash_value, max_hash_value, indent+4);
|
|
|
|
printf ("%*s }\n",
|
|
indent, "");
|
|
}
|
|
else
|
|
{
|
|
/* Output a single switch. */
|
|
int lowest_case_value = list->hash_value;
|
|
if (size == 1)
|
|
{
|
|
int jumps_away = 0;
|
|
assert (min_hash_value <= lowest_case_value);
|
|
assert (lowest_case_value <= max_hash_value);
|
|
if (min_hash_value == max_hash_value)
|
|
output_switch_case (list, indent, &jumps_away);
|
|
else
|
|
{
|
|
printf ("%*sif (key == %d)\n"
|
|
"%*s {\n",
|
|
indent, "", lowest_case_value,
|
|
indent, "");
|
|
output_switch_case (list, indent+4, &jumps_away);
|
|
printf ("%*s }\n",
|
|
indent, "");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (lowest_case_value == 0)
|
|
printf ("%*sswitch (key)\n", indent, "");
|
|
else
|
|
printf ("%*sswitch (key - %d)\n", indent, "", lowest_case_value);
|
|
printf ("%*s {\n",
|
|
indent, "");
|
|
for (; size > 0; size--)
|
|
{
|
|
int jumps_away = 0;
|
|
printf ("%*s case %d:\n",
|
|
indent, "", list->hash_value - lowest_case_value);
|
|
list = output_switch_case (list, indent+6, &jumps_away);
|
|
if (!jumps_away)
|
|
printf ("%*s break;\n",
|
|
indent, "");
|
|
}
|
|
printf ("%*s }\n",
|
|
indent, "");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Generates C code to perform the keyword lookup. */
|
|
|
|
void
|
|
Key_List::output_lookup_function_body (const Output_Compare& comparison)
|
|
{
|
|
T (Trace t ("Key_List::output_lookup_function_body");)
|
|
|
|
printf (" if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)\n"
|
|
" {\n"
|
|
" register int key = %s (str, len);\n\n",
|
|
option.get_hash_name ());
|
|
|
|
if (option[SWITCH])
|
|
{
|
|
int switch_size = num_hash_values ();
|
|
int num_switches = option.get_total_switches ();
|
|
if (num_switches > switch_size)
|
|
num_switches = switch_size;
|
|
|
|
printf (" if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE)\n"
|
|
" {\n");
|
|
if (option[DUP])
|
|
{
|
|
if (option[LENTABLE])
|
|
printf (" register %s%s *lengthptr;\n",
|
|
const_always, smallest_integral_type (max_key_len));
|
|
printf (" register ");
|
|
output_const_type (const_readonly_array, struct_tag);
|
|
printf ("*wordptr;\n");
|
|
printf (" register ");
|
|
output_const_type (const_readonly_array, struct_tag);
|
|
printf ("*wordendptr;\n");
|
|
}
|
|
if (option[TYPE])
|
|
{
|
|
printf (" register ");
|
|
output_const_type (const_readonly_array, struct_tag);
|
|
printf ("*resword;\n\n");
|
|
}
|
|
else
|
|
printf (" register %sresword;\n\n",
|
|
struct_tag);
|
|
|
|
output_switches (head, num_switches, switch_size, min_hash_value, max_hash_value, 10);
|
|
|
|
if (option[DUP])
|
|
{
|
|
int indent = 8;
|
|
printf ("%*s return 0;\n"
|
|
"%*smulticompare:\n"
|
|
"%*s while (wordptr < wordendptr)\n"
|
|
"%*s {\n",
|
|
indent, "", indent, "", indent, "", indent, "");
|
|
if (option[LENTABLE])
|
|
{
|
|
printf ("%*s if (len == *lengthptr)\n"
|
|
"%*s {\n",
|
|
indent, "", indent, "");
|
|
indent += 4;
|
|
}
|
|
printf ("%*s register %schar *s = ",
|
|
indent, "", const_always);
|
|
if (option[TYPE])
|
|
printf ("wordptr->%s", option.get_key_name ());
|
|
else
|
|
printf ("*wordptr");
|
|
printf (";\n\n"
|
|
"%*s if (",
|
|
indent, "");
|
|
comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s"));
|
|
printf (")\n"
|
|
"%*s return %s;\n",
|
|
indent, "",
|
|
option[TYPE] ? "wordptr" : "s");
|
|
if (option[LENTABLE])
|
|
{
|
|
indent -= 4;
|
|
printf ("%*s }\n",
|
|
indent, "");
|
|
}
|
|
if (option[LENTABLE])
|
|
printf ("%*s lengthptr++;\n",
|
|
indent, "");
|
|
printf ("%*s wordptr++;\n"
|
|
"%*s }\n",
|
|
indent, "", indent, "");
|
|
}
|
|
printf (" return 0;\n"
|
|
" compare:\n");
|
|
if (option[TYPE])
|
|
{
|
|
printf (" {\n"
|
|
" register %schar *s = resword->%s;\n\n"
|
|
" if (",
|
|
const_always, option.get_key_name ());
|
|
comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s"));
|
|
printf (")\n"
|
|
" return resword;\n"
|
|
" }\n");
|
|
}
|
|
else
|
|
{
|
|
printf (" if (");
|
|
comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("resword"));
|
|
printf (")\n"
|
|
" return resword;\n");
|
|
}
|
|
printf (" }\n");
|
|
}
|
|
else
|
|
{
|
|
printf (" if (key <= MAX_HASH_VALUE && key >= 0)\n");
|
|
|
|
if (option[DUP])
|
|
{
|
|
int indent = 8;
|
|
printf ("%*s{\n"
|
|
"%*s register int index = lookup[key];\n\n"
|
|
"%*s if (index >= 0)\n",
|
|
indent, "", indent, "", indent, "");
|
|
if (option[LENTABLE])
|
|
{
|
|
printf ("%*s {\n"
|
|
"%*s if (len == lengthtable[index])\n",
|
|
indent, "", indent, "");
|
|
indent += 4;
|
|
}
|
|
printf ("%*s {\n"
|
|
"%*s register %schar *s = %s[index]",
|
|
indent, "",
|
|
indent, "", const_always, option.get_wordlist_name ());
|
|
if (option[TYPE])
|
|
printf (".%s", option.get_key_name ());
|
|
printf (";\n\n"
|
|
"%*s if (",
|
|
indent, "");
|
|
comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s"));
|
|
printf (")\n"
|
|
"%*s return ",
|
|
indent, "");
|
|
if (option[TYPE])
|
|
printf ("&%s[index]", option.get_wordlist_name ());
|
|
else
|
|
printf ("s");
|
|
printf (";\n"
|
|
"%*s }\n",
|
|
indent, "");
|
|
if (option[LENTABLE])
|
|
{
|
|
indent -= 4;
|
|
printf ("%*s }\n", indent, "");
|
|
}
|
|
if (total_duplicates > 0)
|
|
{
|
|
printf ("%*s else if (index < -TOTAL_KEYWORDS)\n"
|
|
"%*s {\n"
|
|
"%*s register int offset = - 1 - TOTAL_KEYWORDS - index;\n",
|
|
indent, "", indent, "", indent, "");
|
|
if (option[LENTABLE])
|
|
printf ("%*s register %s%s *lengthptr = &lengthtable[TOTAL_KEYWORDS + lookup[offset]];\n",
|
|
indent, "", const_always, smallest_integral_type (max_key_len));
|
|
printf ("%*s register ",
|
|
indent, "");
|
|
output_const_type (const_readonly_array, struct_tag);
|
|
printf ("*wordptr = &%s[TOTAL_KEYWORDS + lookup[offset]];\n",
|
|
option.get_wordlist_name ());
|
|
printf ("%*s register ",
|
|
indent, "");
|
|
output_const_type (const_readonly_array, struct_tag);
|
|
printf ("*wordendptr = wordptr + -lookup[offset + 1];\n\n");
|
|
printf ("%*s while (wordptr < wordendptr)\n"
|
|
"%*s {\n",
|
|
indent, "", indent, "");
|
|
if (option[LENTABLE])
|
|
{
|
|
printf ("%*s if (len == *lengthptr)\n"
|
|
"%*s {\n",
|
|
indent, "", indent, "");
|
|
indent += 4;
|
|
}
|
|
printf ("%*s register %schar *s = ",
|
|
indent, "", const_always);
|
|
if (option[TYPE])
|
|
printf ("wordptr->%s", option.get_key_name ());
|
|
else
|
|
printf ("*wordptr");
|
|
printf (";\n\n"
|
|
"%*s if (",
|
|
indent, "");
|
|
comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s"));
|
|
printf (")\n"
|
|
"%*s return %s;\n",
|
|
indent, "",
|
|
option[TYPE] ? "wordptr" : "s");
|
|
if (option[LENTABLE])
|
|
{
|
|
indent -= 4;
|
|
printf ("%*s }\n",
|
|
indent, "");
|
|
}
|
|
if (option[LENTABLE])
|
|
printf ("%*s lengthptr++;\n",
|
|
indent, "");
|
|
printf ("%*s wordptr++;\n"
|
|
"%*s }\n"
|
|
"%*s }\n",
|
|
indent, "", indent, "", indent, "");
|
|
}
|
|
printf ("%*s}\n",
|
|
indent, "");
|
|
}
|
|
else
|
|
{
|
|
int indent = 8;
|
|
if (option[LENTABLE])
|
|
{
|
|
printf ("%*sif (len == lengthtable[key])\n",
|
|
indent, "");
|
|
indent += 2;
|
|
}
|
|
|
|
printf ("%*s{\n"
|
|
"%*s register %schar *s = %s[key]",
|
|
indent, "",
|
|
indent, "", const_always, option.get_wordlist_name ());
|
|
|
|
if (option[TYPE])
|
|
printf (".%s", option.get_key_name ());
|
|
|
|
printf (";\n\n"
|
|
"%*s if (",
|
|
indent, "");
|
|
comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s"));
|
|
printf (")\n"
|
|
"%*s return ",
|
|
indent, "");
|
|
if (option[TYPE])
|
|
printf ("&%s[key]", option.get_wordlist_name ());
|
|
else
|
|
printf ("s");
|
|
printf (";\n"
|
|
"%*s}\n",
|
|
indent, "");
|
|
}
|
|
}
|
|
printf (" }\n"
|
|
" return 0;\n");
|
|
}
|
|
|
|
/* Generates C code for the lookup function. */
|
|
|
|
void
|
|
Key_List::output_lookup_function (void)
|
|
{
|
|
T (Trace t ("Key_List::output_lookup_function");)
|
|
|
|
/* Output the function's head. */
|
|
if (option[KRC] | option[C] | option[ANSIC])
|
|
printf ("#ifdef __GNUC__\n"
|
|
"__inline\n"
|
|
"#endif\n");
|
|
|
|
printf ("%s%s\n",
|
|
const_for_struct, return_type);
|
|
if (option[CPLUSPLUS])
|
|
printf ("%s::", option.get_class_name ());
|
|
printf ("%s ", option.get_function_name ());
|
|
printf (option[KRC] ?
|
|
"(str, len)\n"
|
|
" register char *str;\n"
|
|
" register unsigned int len;\n" :
|
|
option[C] ?
|
|
"(str, len)\n"
|
|
" register const char *str;\n"
|
|
" register unsigned int len;\n" :
|
|
option[ANSIC] | option[CPLUSPLUS] ?
|
|
"(register const char *str, register unsigned int len)\n" :
|
|
"");
|
|
|
|
/* Output the function's body. */
|
|
printf ("{\n");
|
|
|
|
if (option[ENUM] && !option[GLOBAL])
|
|
{
|
|
Output_Enum style (" ");
|
|
output_constants (style);
|
|
}
|
|
|
|
if (!option[GLOBAL])
|
|
output_lookup_tables ();
|
|
|
|
if (option[LENTABLE])
|
|
output_lookup_function_body (Output_Compare_Memcmp ());
|
|
else
|
|
{
|
|
if (option[COMP])
|
|
output_lookup_function_body (Output_Compare_Strncmp ());
|
|
else
|
|
output_lookup_function_body (Output_Compare_Strcmp ());
|
|
}
|
|
|
|
printf ("}\n");
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Generates the hash function and the key word recognizer function
|
|
based upon the user's Options. */
|
|
|
|
void
|
|
Key_List::output (void)
|
|
{
|
|
T (Trace t ("Key_List::output");)
|
|
|
|
compute_min_max ();
|
|
|
|
if (option[C] | option[ANSIC] | option[CPLUSPLUS])
|
|
{
|
|
const_always = "const ";
|
|
const_readonly_array = (option[CONST] ? "const " : "");
|
|
const_for_struct = ((option[CONST] && option[TYPE]) ? "const " : "");
|
|
}
|
|
else
|
|
{
|
|
const_always = "";
|
|
const_readonly_array = "";
|
|
const_for_struct = "";
|
|
}
|
|
|
|
if (!option[TYPE])
|
|
{
|
|
return_type = (const_always[0] ? "const char *" : "char *");
|
|
struct_tag = (const_always[0] ? "const char *" : "char *");
|
|
}
|
|
|
|
char_to_index = (option[SEVENBIT] ? "" : "(unsigned char)");
|
|
|
|
printf ("/* ");
|
|
if (option[KRC])
|
|
printf ("KR-C");
|
|
else if (option[C])
|
|
printf ("C");
|
|
else if (option[ANSIC])
|
|
printf ("ANSI-C");
|
|
else if (option[CPLUSPLUS])
|
|
printf ("C++");
|
|
printf (" code produced by gperf version %s */\n", version_string);
|
|
Options::print_options ();
|
|
|
|
printf ("%s\n", include_src);
|
|
|
|
if (option[TYPE] && !option[NOTYPE]) /* Output type declaration now, reference it later on.... */
|
|
printf ("%s;\n", array_type);
|
|
|
|
if (option[INCLUDE])
|
|
printf ("#include <string.h>\n"); /* Declare strlen(), strcmp(), strncmp(). */
|
|
|
|
if (!option[ENUM])
|
|
{
|
|
Output_Defines style;
|
|
output_constants (style);
|
|
}
|
|
else if (option[GLOBAL])
|
|
{
|
|
Output_Enum style ("");
|
|
output_constants (style);
|
|
}
|
|
|
|
printf ("/* maximum key range = %d, duplicates = %d */\n\n",
|
|
max_hash_value - min_hash_value + 1, total_duplicates);
|
|
|
|
if (option[CPLUSPLUS])
|
|
printf ("class %s\n"
|
|
"{\n"
|
|
"private:\n"
|
|
" static inline unsigned int %s (const char *str, unsigned int len);\n"
|
|
"public:\n"
|
|
" static %s%s%s (const char *str, unsigned int len);\n"
|
|
"};\n"
|
|
"\n",
|
|
option.get_class_name (), option.get_hash_name (),
|
|
const_for_struct, return_type, option.get_function_name ());
|
|
|
|
output_hash_function ();
|
|
|
|
if (option[GLOBAL])
|
|
output_lookup_tables ();
|
|
|
|
output_lookup_function ();
|
|
|
|
if (additional_code)
|
|
for (int c; (c = getchar ()) != EOF; putchar (c))
|
|
;
|
|
|
|
fflush (stdout);
|
|
}
|
|
|
|
/* ========================= End of Output routines ========================= */
|
|
|
|
/* Sorts the keys by hash value. */
|
|
|
|
void
|
|
Key_List::sort (void)
|
|
{
|
|
T (Trace t ("Key_List::sort");)
|
|
hash_sort = 1;
|
|
occurrence_sort = 0;
|
|
|
|
head = merge_sort (head);
|
|
}
|
|
|
|
/* Dumps the key list to stderr stream. */
|
|
|
|
void
|
|
Key_List::dump ()
|
|
{
|
|
T (Trace t ("Key_List::dump");)
|
|
int field_width = option.get_max_keysig_size ();
|
|
|
|
fprintf (stderr, "\nList contents are:\n(hash value, key length, index, %*s, keyword):\n",
|
|
field_width, "char_set");
|
|
|
|
for (List_Node *ptr = head; ptr; ptr = ptr->next)
|
|
fprintf (stderr, "%11d,%11d,%6d, %*.*s, %.*s\n",
|
|
ptr->hash_value, ptr->key_length, ptr->index,
|
|
field_width, ptr->char_set_length, ptr->char_set,
|
|
ptr->key_length, ptr->key);
|
|
}
|
|
|
|
/* Simple-minded constructor action here... */
|
|
|
|
Key_List::Key_List (void)
|
|
{
|
|
T (Trace t ("Key_List::Key_List");)
|
|
total_keys = 1;
|
|
max_key_len = INT_MIN;
|
|
min_key_len = INT_MAX;
|
|
array_type = 0;
|
|
return_type = 0;
|
|
struct_tag = 0;
|
|
head = 0;
|
|
total_duplicates = 0;
|
|
additional_code = 0;
|
|
}
|
|
|
|
/* Returns the length of entire key list. */
|
|
|
|
int
|
|
Key_List::keyword_list_length (void)
|
|
{
|
|
T (Trace t ("Key_List::keyword_list_length");)
|
|
return list_len;
|
|
}
|
|
|
|
/* Returns length of longest key read. */
|
|
|
|
int
|
|
Key_List::max_key_length (void)
|
|
{
|
|
T (Trace t ("Key_List::max_key_length");)
|
|
return max_key_len;
|
|
}
|
|
|