xref: /dflybsd-src/contrib/binutils-2.34/gas/hash.c (revision b52ef7118d1621abed722c5bbbd542210290ecef)
1*fae548d3Szrj /* hash.c -- gas hash table code
2*fae548d3Szrj    Copyright (C) 1987-2020 Free Software Foundation, Inc.
3*fae548d3Szrj 
4*fae548d3Szrj    This file is part of GAS, the GNU Assembler.
5*fae548d3Szrj 
6*fae548d3Szrj    GAS is free software; you can redistribute it and/or modify
7*fae548d3Szrj    it under the terms of the GNU General Public License as published by
8*fae548d3Szrj    the Free Software Foundation; either version 3, or (at your option)
9*fae548d3Szrj    any later version.
10*fae548d3Szrj 
11*fae548d3Szrj    GAS is distributed in the hope that it will be useful,
12*fae548d3Szrj    but WITHOUT ANY WARRANTY; without even the implied warranty of
13*fae548d3Szrj    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*fae548d3Szrj    GNU General Public License for more details.
15*fae548d3Szrj 
16*fae548d3Szrj    You should have received a copy of the GNU General Public License
17*fae548d3Szrj    along with GAS; see the file COPYING.  If not, write to the Free
18*fae548d3Szrj    Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
19*fae548d3Szrj    02110-1301, USA.  */
20*fae548d3Szrj 
21*fae548d3Szrj /* This version of the hash table code is a wholescale replacement of
22*fae548d3Szrj    the old hash table code, which was fairly bad.  This is based on
23*fae548d3Szrj    the hash table code in BFD, but optimized slightly for the
24*fae548d3Szrj    assembler.  The assembler does not need to derive structures that
25*fae548d3Szrj    are stored in the hash table.  Instead, it always stores a pointer.
26*fae548d3Szrj    The assembler uses the hash table mostly to store symbols, and we
27*fae548d3Szrj    don't need to confuse the symbol structure with a hash table
28*fae548d3Szrj    structure.  */
29*fae548d3Szrj 
30*fae548d3Szrj #include "as.h"
31*fae548d3Szrj #include "safe-ctype.h"
32*fae548d3Szrj #include "obstack.h"
33*fae548d3Szrj 
34*fae548d3Szrj /* An entry in a hash table.  */
35*fae548d3Szrj 
36*fae548d3Szrj struct hash_entry {
37*fae548d3Szrj   /* Next entry for this hash code.  */
38*fae548d3Szrj   struct hash_entry *next;
39*fae548d3Szrj   /* String being hashed.  */
40*fae548d3Szrj   const char *string;
41*fae548d3Szrj   /* Hash code.  This is the full hash code, not the index into the
42*fae548d3Szrj      table.  */
43*fae548d3Szrj   unsigned long hash;
44*fae548d3Szrj   /* Pointer being stored in the hash table.  */
45*fae548d3Szrj   void *data;
46*fae548d3Szrj };
47*fae548d3Szrj 
48*fae548d3Szrj /* A hash table.  */
49*fae548d3Szrj 
50*fae548d3Szrj struct hash_control {
51*fae548d3Szrj   /* The hash array.  */
52*fae548d3Szrj   struct hash_entry **table;
53*fae548d3Szrj   /* The number of slots in the hash table.  */
54*fae548d3Szrj   unsigned int size;
55*fae548d3Szrj   /* An obstack for this hash table.  */
56*fae548d3Szrj   struct obstack memory;
57*fae548d3Szrj 
58*fae548d3Szrj #ifdef HASH_STATISTICS
59*fae548d3Szrj   /* Statistics.  */
60*fae548d3Szrj   unsigned long lookups;
61*fae548d3Szrj   unsigned long hash_compares;
62*fae548d3Szrj   unsigned long string_compares;
63*fae548d3Szrj   unsigned long insertions;
64*fae548d3Szrj   unsigned long replacements;
65*fae548d3Szrj   unsigned long deletions;
66*fae548d3Szrj #endif /* HASH_STATISTICS */
67*fae548d3Szrj };
68*fae548d3Szrj 
69*fae548d3Szrj /* The default number of entries to use when creating a hash table.
70*fae548d3Szrj    Note this value can be reduced to 4051 by using the command line
71*fae548d3Szrj    switch --reduce-memory-overheads, or set to other values by using
72*fae548d3Szrj    the --hash-size=<NUMBER> switch.  */
73*fae548d3Szrj 
74*fae548d3Szrj static unsigned long gas_hash_table_size = 65537;
75*fae548d3Szrj 
76*fae548d3Szrj void
set_gas_hash_table_size(unsigned long size)77*fae548d3Szrj set_gas_hash_table_size (unsigned long size)
78*fae548d3Szrj {
79*fae548d3Szrj   gas_hash_table_size = bfd_hash_set_default_size (size);
80*fae548d3Szrj }
81*fae548d3Szrj 
82*fae548d3Szrj /* Create a hash table.  This return a control block.  */
83*fae548d3Szrj 
84*fae548d3Szrj struct hash_control *
hash_new_sized(unsigned long size)85*fae548d3Szrj hash_new_sized (unsigned long size)
86*fae548d3Szrj {
87*fae548d3Szrj   unsigned long alloc;
88*fae548d3Szrj   struct hash_control *ret;
89*fae548d3Szrj 
90*fae548d3Szrj   ret = XNEW (struct hash_control);
91*fae548d3Szrj   obstack_begin (&ret->memory, chunksize);
92*fae548d3Szrj   alloc = size * sizeof (struct hash_entry *);
93*fae548d3Szrj   ret->table = (struct hash_entry **) obstack_alloc (&ret->memory, alloc);
94*fae548d3Szrj   memset (ret->table, 0, alloc);
95*fae548d3Szrj   ret->size = size;
96*fae548d3Szrj 
97*fae548d3Szrj #ifdef HASH_STATISTICS
98*fae548d3Szrj   ret->lookups = 0;
99*fae548d3Szrj   ret->hash_compares = 0;
100*fae548d3Szrj   ret->string_compares = 0;
101*fae548d3Szrj   ret->insertions = 0;
102*fae548d3Szrj   ret->replacements = 0;
103*fae548d3Szrj   ret->deletions = 0;
104*fae548d3Szrj #endif
105*fae548d3Szrj 
106*fae548d3Szrj   return ret;
107*fae548d3Szrj }
108*fae548d3Szrj 
109*fae548d3Szrj struct hash_control *
hash_new(void)110*fae548d3Szrj hash_new (void)
111*fae548d3Szrj {
112*fae548d3Szrj   return hash_new_sized (gas_hash_table_size);
113*fae548d3Szrj }
114*fae548d3Szrj 
115*fae548d3Szrj /* Delete a hash table, freeing all allocated memory.  */
116*fae548d3Szrj 
117*fae548d3Szrj void
hash_die(struct hash_control * table)118*fae548d3Szrj hash_die (struct hash_control *table)
119*fae548d3Szrj {
120*fae548d3Szrj   obstack_free (&table->memory, 0);
121*fae548d3Szrj   free (table);
122*fae548d3Szrj }
123*fae548d3Szrj 
124*fae548d3Szrj /* Look up a string in a hash table.  This returns a pointer to the
125*fae548d3Szrj    hash_entry, or NULL if the string is not in the table.  If PLIST is
126*fae548d3Szrj    not NULL, this sets *PLIST to point to the start of the list which
127*fae548d3Szrj    would hold this hash entry.  If PHASH is not NULL, this sets *PHASH
128*fae548d3Szrj    to the hash code for KEY.
129*fae548d3Szrj 
130*fae548d3Szrj    Each time we look up a string, we move it to the start of the list
131*fae548d3Szrj    for its hash code, to take advantage of referential locality.  */
132*fae548d3Szrj 
133*fae548d3Szrj static struct hash_entry *
hash_lookup(struct hash_control * table,const char * key,size_t len,struct hash_entry *** plist,unsigned long * phash)134*fae548d3Szrj hash_lookup (struct hash_control *table, const char *key, size_t len,
135*fae548d3Szrj 	     struct hash_entry ***plist, unsigned long *phash)
136*fae548d3Szrj {
137*fae548d3Szrj   unsigned long hash;
138*fae548d3Szrj   size_t n;
139*fae548d3Szrj   unsigned int c;
140*fae548d3Szrj   unsigned int hindex;
141*fae548d3Szrj   struct hash_entry **list;
142*fae548d3Szrj   struct hash_entry *p;
143*fae548d3Szrj   struct hash_entry *prev;
144*fae548d3Szrj 
145*fae548d3Szrj #ifdef HASH_STATISTICS
146*fae548d3Szrj   ++table->lookups;
147*fae548d3Szrj #endif
148*fae548d3Szrj 
149*fae548d3Szrj   hash = 0;
150*fae548d3Szrj   for (n = 0; n < len; n++)
151*fae548d3Szrj     {
152*fae548d3Szrj       c = key[n];
153*fae548d3Szrj       hash += c + (c << 17);
154*fae548d3Szrj       hash ^= hash >> 2;
155*fae548d3Szrj     }
156*fae548d3Szrj   hash += len + (len << 17);
157*fae548d3Szrj   hash ^= hash >> 2;
158*fae548d3Szrj 
159*fae548d3Szrj   if (phash != NULL)
160*fae548d3Szrj     *phash = hash;
161*fae548d3Szrj 
162*fae548d3Szrj   hindex = hash % table->size;
163*fae548d3Szrj   list = table->table + hindex;
164*fae548d3Szrj 
165*fae548d3Szrj   if (plist != NULL)
166*fae548d3Szrj     *plist = list;
167*fae548d3Szrj 
168*fae548d3Szrj   prev = NULL;
169*fae548d3Szrj   for (p = *list; p != NULL; p = p->next)
170*fae548d3Szrj     {
171*fae548d3Szrj #ifdef HASH_STATISTICS
172*fae548d3Szrj       ++table->hash_compares;
173*fae548d3Szrj #endif
174*fae548d3Szrj 
175*fae548d3Szrj       if (p->hash == hash)
176*fae548d3Szrj 	{
177*fae548d3Szrj #ifdef HASH_STATISTICS
178*fae548d3Szrj 	  ++table->string_compares;
179*fae548d3Szrj #endif
180*fae548d3Szrj 
181*fae548d3Szrj 	  if (strncmp (p->string, key, len) == 0 && p->string[len] == '\0')
182*fae548d3Szrj 	    {
183*fae548d3Szrj 	      if (prev != NULL)
184*fae548d3Szrj 		{
185*fae548d3Szrj 		  prev->next = p->next;
186*fae548d3Szrj 		  p->next = *list;
187*fae548d3Szrj 		  *list = p;
188*fae548d3Szrj 		}
189*fae548d3Szrj 
190*fae548d3Szrj 	      return p;
191*fae548d3Szrj 	    }
192*fae548d3Szrj 	}
193*fae548d3Szrj 
194*fae548d3Szrj       prev = p;
195*fae548d3Szrj     }
196*fae548d3Szrj 
197*fae548d3Szrj   return NULL;
198*fae548d3Szrj }
199*fae548d3Szrj 
200*fae548d3Szrj /* Insert an entry into a hash table.  This returns NULL on success.
201*fae548d3Szrj    On error, it returns a printable string indicating the error.  It
202*fae548d3Szrj    is considered to be an error if the entry already exists in the
203*fae548d3Szrj    hash table.  */
204*fae548d3Szrj 
205*fae548d3Szrj const char *
hash_insert(struct hash_control * table,const char * key,void * val)206*fae548d3Szrj hash_insert (struct hash_control *table, const char *key, void *val)
207*fae548d3Szrj {
208*fae548d3Szrj   struct hash_entry *p;
209*fae548d3Szrj   struct hash_entry **list;
210*fae548d3Szrj   unsigned long hash;
211*fae548d3Szrj 
212*fae548d3Szrj   p = hash_lookup (table, key, strlen (key), &list, &hash);
213*fae548d3Szrj   if (p != NULL)
214*fae548d3Szrj     return "exists";
215*fae548d3Szrj 
216*fae548d3Szrj #ifdef HASH_STATISTICS
217*fae548d3Szrj   ++table->insertions;
218*fae548d3Szrj #endif
219*fae548d3Szrj 
220*fae548d3Szrj   p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p));
221*fae548d3Szrj   p->string = key;
222*fae548d3Szrj   p->hash = hash;
223*fae548d3Szrj   p->data = val;
224*fae548d3Szrj 
225*fae548d3Szrj   p->next = *list;
226*fae548d3Szrj   *list = p;
227*fae548d3Szrj 
228*fae548d3Szrj   return NULL;
229*fae548d3Szrj }
230*fae548d3Szrj 
231*fae548d3Szrj /* Insert or replace an entry in a hash table.  This returns NULL on
232*fae548d3Szrj    success.  On error, it returns a printable string indicating the
233*fae548d3Szrj    error.  If an entry already exists, its value is replaced.  */
234*fae548d3Szrj 
235*fae548d3Szrj const char *
hash_jam(struct hash_control * table,const char * key,void * val)236*fae548d3Szrj hash_jam (struct hash_control *table, const char *key, void *val)
237*fae548d3Szrj {
238*fae548d3Szrj   struct hash_entry *p;
239*fae548d3Szrj   struct hash_entry **list;
240*fae548d3Szrj   unsigned long hash;
241*fae548d3Szrj 
242*fae548d3Szrj   p = hash_lookup (table, key, strlen (key), &list, &hash);
243*fae548d3Szrj   if (p != NULL)
244*fae548d3Szrj     {
245*fae548d3Szrj #ifdef HASH_STATISTICS
246*fae548d3Szrj       ++table->replacements;
247*fae548d3Szrj #endif
248*fae548d3Szrj 
249*fae548d3Szrj       p->data = val;
250*fae548d3Szrj     }
251*fae548d3Szrj   else
252*fae548d3Szrj     {
253*fae548d3Szrj #ifdef HASH_STATISTICS
254*fae548d3Szrj       ++table->insertions;
255*fae548d3Szrj #endif
256*fae548d3Szrj 
257*fae548d3Szrj       p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p));
258*fae548d3Szrj       p->string = key;
259*fae548d3Szrj       p->hash = hash;
260*fae548d3Szrj       p->data = val;
261*fae548d3Szrj 
262*fae548d3Szrj       p->next = *list;
263*fae548d3Szrj       *list = p;
264*fae548d3Szrj     }
265*fae548d3Szrj 
266*fae548d3Szrj   return NULL;
267*fae548d3Szrj }
268*fae548d3Szrj 
269*fae548d3Szrj /* Replace an existing entry in a hash table.  This returns the old
270*fae548d3Szrj    value stored for the entry.  If the entry is not found in the hash
271*fae548d3Szrj    table, this does nothing and returns NULL.  */
272*fae548d3Szrj 
273*fae548d3Szrj void *
hash_replace(struct hash_control * table,const char * key,void * value)274*fae548d3Szrj hash_replace (struct hash_control *table, const char *key, void *value)
275*fae548d3Szrj {
276*fae548d3Szrj   struct hash_entry *p;
277*fae548d3Szrj   void *ret;
278*fae548d3Szrj 
279*fae548d3Szrj   p = hash_lookup (table, key, strlen (key), NULL, NULL);
280*fae548d3Szrj   if (p == NULL)
281*fae548d3Szrj     return NULL;
282*fae548d3Szrj 
283*fae548d3Szrj #ifdef HASH_STATISTICS
284*fae548d3Szrj   ++table->replacements;
285*fae548d3Szrj #endif
286*fae548d3Szrj 
287*fae548d3Szrj   ret = p->data;
288*fae548d3Szrj 
289*fae548d3Szrj   p->data = value;
290*fae548d3Szrj 
291*fae548d3Szrj   return ret;
292*fae548d3Szrj }
293*fae548d3Szrj 
294*fae548d3Szrj /* Find an entry in a hash table, returning its value.  Returns NULL
295*fae548d3Szrj    if the entry is not found.  */
296*fae548d3Szrj 
297*fae548d3Szrj void *
hash_find(struct hash_control * table,const char * key)298*fae548d3Szrj hash_find (struct hash_control *table, const char *key)
299*fae548d3Szrj {
300*fae548d3Szrj   struct hash_entry *p;
301*fae548d3Szrj 
302*fae548d3Szrj   p = hash_lookup (table, key, strlen (key), NULL, NULL);
303*fae548d3Szrj   if (p == NULL)
304*fae548d3Szrj     return NULL;
305*fae548d3Szrj 
306*fae548d3Szrj   return p->data;
307*fae548d3Szrj }
308*fae548d3Szrj 
309*fae548d3Szrj /* As hash_find, but KEY is of length LEN and is not guaranteed to be
310*fae548d3Szrj    NUL-terminated.  */
311*fae548d3Szrj 
312*fae548d3Szrj void *
hash_find_n(struct hash_control * table,const char * key,size_t len)313*fae548d3Szrj hash_find_n (struct hash_control *table, const char *key, size_t len)
314*fae548d3Szrj {
315*fae548d3Szrj   struct hash_entry *p;
316*fae548d3Szrj 
317*fae548d3Szrj   p = hash_lookup (table, key, len, NULL, NULL);
318*fae548d3Szrj   if (p == NULL)
319*fae548d3Szrj     return NULL;
320*fae548d3Szrj 
321*fae548d3Szrj   return p->data;
322*fae548d3Szrj }
323*fae548d3Szrj 
324*fae548d3Szrj /* Delete an entry from a hash table.  This returns the value stored
325*fae548d3Szrj    for that entry, or NULL if there is no such entry.  */
326*fae548d3Szrj 
327*fae548d3Szrj void *
hash_delete(struct hash_control * table,const char * key,int freeme)328*fae548d3Szrj hash_delete (struct hash_control *table, const char *key, int freeme)
329*fae548d3Szrj {
330*fae548d3Szrj   struct hash_entry *p;
331*fae548d3Szrj   struct hash_entry **list;
332*fae548d3Szrj 
333*fae548d3Szrj   p = hash_lookup (table, key, strlen (key), &list, NULL);
334*fae548d3Szrj   if (p == NULL)
335*fae548d3Szrj     return NULL;
336*fae548d3Szrj 
337*fae548d3Szrj   if (p != *list)
338*fae548d3Szrj     abort ();
339*fae548d3Szrj 
340*fae548d3Szrj #ifdef HASH_STATISTICS
341*fae548d3Szrj   ++table->deletions;
342*fae548d3Szrj #endif
343*fae548d3Szrj 
344*fae548d3Szrj   *list = p->next;
345*fae548d3Szrj 
346*fae548d3Szrj   if (freeme)
347*fae548d3Szrj     obstack_free (&table->memory, p);
348*fae548d3Szrj 
349*fae548d3Szrj   return p->data;
350*fae548d3Szrj }
351*fae548d3Szrj 
352*fae548d3Szrj /* Traverse a hash table.  Call the function on every entry in the
353*fae548d3Szrj    hash table.  */
354*fae548d3Szrj 
355*fae548d3Szrj void
hash_traverse(struct hash_control * table,void (* pfn)(const char * key,void * value))356*fae548d3Szrj hash_traverse (struct hash_control *table,
357*fae548d3Szrj 	       void (*pfn) (const char *key, void *value))
358*fae548d3Szrj {
359*fae548d3Szrj   unsigned int i;
360*fae548d3Szrj 
361*fae548d3Szrj   for (i = 0; i < table->size; ++i)
362*fae548d3Szrj     {
363*fae548d3Szrj       struct hash_entry *p;
364*fae548d3Szrj 
365*fae548d3Szrj       for (p = table->table[i]; p != NULL; p = p->next)
366*fae548d3Szrj 	(*pfn) (p->string, p->data);
367*fae548d3Szrj     }
368*fae548d3Szrj }
369*fae548d3Szrj 
370*fae548d3Szrj /* Print hash table statistics on the specified file.  NAME is the
371*fae548d3Szrj    name of the hash table, used for printing a header.  */
372*fae548d3Szrj 
373*fae548d3Szrj void
hash_print_statistics(FILE * f ATTRIBUTE_UNUSED,const char * name ATTRIBUTE_UNUSED,struct hash_control * table ATTRIBUTE_UNUSED)374*fae548d3Szrj hash_print_statistics (FILE *f ATTRIBUTE_UNUSED,
375*fae548d3Szrj 		       const char *name ATTRIBUTE_UNUSED,
376*fae548d3Szrj 		       struct hash_control *table ATTRIBUTE_UNUSED)
377*fae548d3Szrj {
378*fae548d3Szrj #ifdef HASH_STATISTICS
379*fae548d3Szrj   unsigned int i;
380*fae548d3Szrj   unsigned long total;
381*fae548d3Szrj   unsigned long empty;
382*fae548d3Szrj 
383*fae548d3Szrj   fprintf (f, "%s hash statistics:\n", name);
384*fae548d3Szrj   fprintf (f, "\t%lu lookups\n", table->lookups);
385*fae548d3Szrj   fprintf (f, "\t%lu hash comparisons\n", table->hash_compares);
386*fae548d3Szrj   fprintf (f, "\t%lu string comparisons\n", table->string_compares);
387*fae548d3Szrj   fprintf (f, "\t%lu insertions\n", table->insertions);
388*fae548d3Szrj   fprintf (f, "\t%lu replacements\n", table->replacements);
389*fae548d3Szrj   fprintf (f, "\t%lu deletions\n", table->deletions);
390*fae548d3Szrj 
391*fae548d3Szrj   total = 0;
392*fae548d3Szrj   empty = 0;
393*fae548d3Szrj   for (i = 0; i < table->size; ++i)
394*fae548d3Szrj     {
395*fae548d3Szrj       struct hash_entry *p;
396*fae548d3Szrj 
397*fae548d3Szrj       if (table->table[i] == NULL)
398*fae548d3Szrj 	++empty;
399*fae548d3Szrj       else
400*fae548d3Szrj 	{
401*fae548d3Szrj 	  for (p = table->table[i]; p != NULL; p = p->next)
402*fae548d3Szrj 	    ++total;
403*fae548d3Szrj 	}
404*fae548d3Szrj     }
405*fae548d3Szrj 
406*fae548d3Szrj   fprintf (f, "\t%g average chain length\n", (double) total / table->size);
407*fae548d3Szrj   fprintf (f, "\t%lu empty slots\n", empty);
408*fae548d3Szrj #endif
409*fae548d3Szrj }
410*fae548d3Szrj 
411*fae548d3Szrj #ifdef TEST
412*fae548d3Szrj 
413*fae548d3Szrj /* This test program is left over from the old hash table code.  */
414*fae548d3Szrj 
415*fae548d3Szrj /* Number of hash tables to maintain (at once) in any testing.  */
416*fae548d3Szrj #define TABLES (6)
417*fae548d3Szrj 
418*fae548d3Szrj /* We can have 12 statistics.  */
419*fae548d3Szrj #define STATBUFSIZE (12)
420*fae548d3Szrj 
421*fae548d3Szrj /* Display statistics here.  */
422*fae548d3Szrj int statbuf[STATBUFSIZE];
423*fae548d3Szrj 
424*fae548d3Szrj /* Human farts here.  */
425*fae548d3Szrj char answer[100];
426*fae548d3Szrj 
427*fae548d3Szrj /* We test many hash tables at once.  */
428*fae548d3Szrj char *hashtable[TABLES];
429*fae548d3Szrj 
430*fae548d3Szrj /* Points to current hash_control.  */
431*fae548d3Szrj char *h;
432*fae548d3Szrj char **pp;
433*fae548d3Szrj char *p;
434*fae548d3Szrj char *name;
435*fae548d3Szrj char *value;
436*fae548d3Szrj int size;
437*fae548d3Szrj int used;
438*fae548d3Szrj char command;
439*fae548d3Szrj 
440*fae548d3Szrj /* Number 0:TABLES-1 of current hashed symbol table.  */
441*fae548d3Szrj int number;
442*fae548d3Szrj 
443*fae548d3Szrj int
main()444*fae548d3Szrj main ()
445*fae548d3Szrj {
446*fae548d3Szrj   void applicatee ();
447*fae548d3Szrj   void destroy ();
448*fae548d3Szrj   char *what ();
449*fae548d3Szrj   int *ip;
450*fae548d3Szrj 
451*fae548d3Szrj   number = 0;
452*fae548d3Szrj   h = 0;
453*fae548d3Szrj   printf ("type h <RETURN> for help\n");
454*fae548d3Szrj   for (;;)
455*fae548d3Szrj     {
456*fae548d3Szrj       printf ("hash_test command: ");
457*fae548d3Szrj       gets (answer);
458*fae548d3Szrj       command = answer[0];
459*fae548d3Szrj       command = TOLOWER (command);	/* Ecch!  */
460*fae548d3Szrj       switch (command)
461*fae548d3Szrj 	{
462*fae548d3Szrj 	case '#':
463*fae548d3Szrj 	  printf ("old hash table #=%d.\n", number);
464*fae548d3Szrj 	  whattable ();
465*fae548d3Szrj 	  break;
466*fae548d3Szrj 	case '?':
467*fae548d3Szrj 	  for (pp = hashtable; pp < hashtable + TABLES; pp++)
468*fae548d3Szrj 	    {
469*fae548d3Szrj 	      printf ("address of hash table #%d control block is %xx\n",
470*fae548d3Szrj 		      pp - hashtable, *pp);
471*fae548d3Szrj 	    }
472*fae548d3Szrj 	  break;
473*fae548d3Szrj 	case 'a':
474*fae548d3Szrj 	  hash_traverse (h, applicatee);
475*fae548d3Szrj 	  break;
476*fae548d3Szrj 	case 'd':
477*fae548d3Szrj 	  hash_traverse (h, destroy);
478*fae548d3Szrj 	  hash_die (h);
479*fae548d3Szrj 	  break;
480*fae548d3Szrj 	case 'f':
481*fae548d3Szrj 	  p = hash_find (h, name = what ("symbol"));
482*fae548d3Szrj 	  printf ("value of \"%s\" is \"%s\"\n", name, p ? p : "NOT-PRESENT");
483*fae548d3Szrj 	  break;
484*fae548d3Szrj 	case 'h':
485*fae548d3Szrj 	  printf ("# show old, select new default hash table number\n");
486*fae548d3Szrj 	  printf ("? display all hashtable control block addresses\n");
487*fae548d3Szrj 	  printf ("a apply a simple display-er to each symbol in table\n");
488*fae548d3Szrj 	  printf ("d die: destroy hashtable\n");
489*fae548d3Szrj 	  printf ("f find value of nominated symbol\n");
490*fae548d3Szrj 	  printf ("h this help\n");
491*fae548d3Szrj 	  printf ("i insert value into symbol\n");
492*fae548d3Szrj 	  printf ("j jam value into symbol\n");
493*fae548d3Szrj 	  printf ("n new hashtable\n");
494*fae548d3Szrj 	  printf ("r replace a value with another\n");
495*fae548d3Szrj 	  printf ("s say what %% of table is used\n");
496*fae548d3Szrj 	  printf ("q exit this program\n");
497*fae548d3Szrj 	  printf ("x delete a symbol from table, report its value\n");
498*fae548d3Szrj 	  break;
499*fae548d3Szrj 	case 'i':
500*fae548d3Szrj 	  p = hash_insert (h, name = what ("symbol"), value = what ("value"));
501*fae548d3Szrj 	  if (p)
502*fae548d3Szrj 	    {
503*fae548d3Szrj 	      printf ("symbol=\"%s\"  value=\"%s\"  error=%s\n", name, value,
504*fae548d3Szrj 		      p);
505*fae548d3Szrj 	    }
506*fae548d3Szrj 	  break;
507*fae548d3Szrj 	case 'j':
508*fae548d3Szrj 	  p = hash_jam (h, name = what ("symbol"), value = what ("value"));
509*fae548d3Szrj 	  if (p)
510*fae548d3Szrj 	    {
511*fae548d3Szrj 	      printf ("symbol=\"%s\"  value=\"%s\"  error=%s\n", name, value, p);
512*fae548d3Szrj 	    }
513*fae548d3Szrj 	  break;
514*fae548d3Szrj 	case 'n':
515*fae548d3Szrj 	  h = hashtable[number] = (char *) hash_new ();
516*fae548d3Szrj 	  break;
517*fae548d3Szrj 	case 'q':
518*fae548d3Szrj 	  exit (EXIT_SUCCESS);
519*fae548d3Szrj 	case 'r':
520*fae548d3Szrj 	  p = hash_replace (h, name = what ("symbol"), value = what ("value"));
521*fae548d3Szrj 	  printf ("old value was \"%s\"\n", p ? p : "{}");
522*fae548d3Szrj 	  break;
523*fae548d3Szrj 	case 's':
524*fae548d3Szrj 	  hash_say (h, statbuf, STATBUFSIZE);
525*fae548d3Szrj 	  for (ip = statbuf; ip < statbuf + STATBUFSIZE; ip++)
526*fae548d3Szrj 	    {
527*fae548d3Szrj 	      printf ("%d ", *ip);
528*fae548d3Szrj 	    }
529*fae548d3Szrj 	  printf ("\n");
530*fae548d3Szrj 	  break;
531*fae548d3Szrj 	case 'x':
532*fae548d3Szrj 	  p = hash_delete (h, name = what ("symbol"));
533*fae548d3Szrj 	  printf ("old value was \"%s\"\n", p ? p : "{}");
534*fae548d3Szrj 	  break;
535*fae548d3Szrj 	default:
536*fae548d3Szrj 	  printf ("I can't understand command \"%c\"\n", command);
537*fae548d3Szrj 	  break;
538*fae548d3Szrj 	}
539*fae548d3Szrj     }
540*fae548d3Szrj }
541*fae548d3Szrj 
542*fae548d3Szrj char *
what(description)543*fae548d3Szrj what (description)
544*fae548d3Szrj      char *description;
545*fae548d3Szrj {
546*fae548d3Szrj   printf ("   %s : ", description);
547*fae548d3Szrj   gets (answer);
548*fae548d3Szrj   return xstrdup (answer);
549*fae548d3Szrj }
550*fae548d3Szrj 
551*fae548d3Szrj void
destroy(string,value)552*fae548d3Szrj destroy (string, value)
553*fae548d3Szrj      char *string;
554*fae548d3Szrj      char *value;
555*fae548d3Szrj {
556*fae548d3Szrj   free (string);
557*fae548d3Szrj   free (value);
558*fae548d3Szrj }
559*fae548d3Szrj 
560*fae548d3Szrj void
applicatee(string,value)561*fae548d3Szrj applicatee (string, value)
562*fae548d3Szrj      char *string;
563*fae548d3Szrj      char *value;
564*fae548d3Szrj {
565*fae548d3Szrj   printf ("%.20s-%.20s\n", string, value);
566*fae548d3Szrj }
567*fae548d3Szrj 
568*fae548d3Szrj /* Determine number: what hash table to use.
569*fae548d3Szrj    Also determine h: points to hash_control.  */
570*fae548d3Szrj 
571*fae548d3Szrj void
whattable()572*fae548d3Szrj whattable ()
573*fae548d3Szrj {
574*fae548d3Szrj   for (;;)
575*fae548d3Szrj     {
576*fae548d3Szrj       printf ("   what hash table (%d:%d) ?  ", 0, TABLES - 1);
577*fae548d3Szrj       gets (answer);
578*fae548d3Szrj       sscanf (answer, "%d", &number);
579*fae548d3Szrj       if (number >= 0 && number < TABLES)
580*fae548d3Szrj 	{
581*fae548d3Szrj 	  h = hashtable[number];
582*fae548d3Szrj 	  if (!h)
583*fae548d3Szrj 	    {
584*fae548d3Szrj 	      printf ("warning: current hash-table-#%d. has no hash-control\n", number);
585*fae548d3Szrj 	    }
586*fae548d3Szrj 	  return;
587*fae548d3Szrj 	}
588*fae548d3Szrj       else
589*fae548d3Szrj 	{
590*fae548d3Szrj 	  printf ("invalid hash table number: %d\n", number);
591*fae548d3Szrj 	}
592*fae548d3Szrj     }
593*fae548d3Szrj }
594*fae548d3Szrj 
595*fae548d3Szrj #endif /* TEST */
596