xref: /openbsd/gnu/usr.bin/binutils/gas/hash.c (revision 007c2a45)
1b305b0f1Sespie /* hash.c -- gas hash table code
2b55d4692Sfgsch    Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999,
3c074d1c9Sdrahn    2000, 2001, 2002
40c6d0228Sniklas    Free Software Foundation, Inc.
52159047fSniklas 
62159047fSniklas    This file is part of GAS, the GNU Assembler.
72159047fSniklas 
82159047fSniklas    GAS is free software; you can redistribute it and/or modify
92159047fSniklas    it under the terms of the GNU General Public License as published by
102159047fSniklas    the Free Software Foundation; either version 2, or (at your option)
112159047fSniklas    any later version.
122159047fSniklas 
132159047fSniklas    GAS is distributed in the hope that it will be useful,
142159047fSniklas    but WITHOUT ANY WARRANTY; without even the implied warranty of
152159047fSniklas    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
162159047fSniklas    GNU General Public License for more details.
172159047fSniklas 
182159047fSniklas    You should have received a copy of the GNU General Public License
19b305b0f1Sespie    along with GAS; see the file COPYING.  If not, write to the Free
20b305b0f1Sespie    Software Foundation, 59 Temple Place - Suite 330, Boston, MA
21b305b0f1Sespie    02111-1307, USA.  */
222159047fSniklas 
23b305b0f1Sespie /* This version of the hash table code is a wholescale replacement of
24b305b0f1Sespie    the old hash table code, which was fairly bad.  This is based on
25b305b0f1Sespie    the hash table code in BFD, but optimized slightly for the
26*007c2a45Smiod    assembler.  The assembler does not need to derive structures that
27b305b0f1Sespie    are stored in the hash table.  Instead, it always stores a pointer.
28b305b0f1Sespie    The assembler uses the hash table mostly to store symbols, and we
29b305b0f1Sespie    don't need to confuse the symbol structure with a hash table
30b305b0f1Sespie    structure.  */
312159047fSniklas 
322159047fSniklas #include "as.h"
33c074d1c9Sdrahn #include "safe-ctype.h"
34b305b0f1Sespie #include "obstack.h"
352159047fSniklas 
36b305b0f1Sespie /* The default number of entries to use when creating a hash table.  */
372159047fSniklas 
38b305b0f1Sespie #define DEFAULT_SIZE (4051)
392159047fSniklas 
40b305b0f1Sespie /* An entry in a hash table.  */
412159047fSniklas 
42b55d4692Sfgsch struct hash_entry {
43b305b0f1Sespie   /* Next entry for this hash code.  */
44b305b0f1Sespie   struct hash_entry *next;
45b305b0f1Sespie   /* String being hashed.  */
46b305b0f1Sespie   const char *string;
47b305b0f1Sespie   /* Hash code.  This is the full hash code, not the index into the
48b305b0f1Sespie      table.  */
49b305b0f1Sespie   unsigned long hash;
50b305b0f1Sespie   /* Pointer being stored in the hash table.  */
51b305b0f1Sespie   PTR data;
52191aa565Sniklas };
53191aa565Sniklas 
54b305b0f1Sespie /* A hash table.  */
55b305b0f1Sespie 
56b55d4692Sfgsch struct hash_control {
57b305b0f1Sespie   /* The hash array.  */
58b305b0f1Sespie   struct hash_entry **table;
59b305b0f1Sespie   /* The number of slots in the hash table.  */
60b305b0f1Sespie   unsigned int size;
61b305b0f1Sespie   /* An obstack for this hash table.  */
62b305b0f1Sespie   struct obstack memory;
63b305b0f1Sespie 
64b305b0f1Sespie #ifdef HASH_STATISTICS
65b305b0f1Sespie   /* Statistics.  */
66b305b0f1Sespie   unsigned long lookups;
67b305b0f1Sespie   unsigned long hash_compares;
68b305b0f1Sespie   unsigned long string_compares;
69b305b0f1Sespie   unsigned long insertions;
70b305b0f1Sespie   unsigned long replacements;
71b305b0f1Sespie   unsigned long deletions;
72b305b0f1Sespie #endif /* HASH_STATISTICS */
73191aa565Sniklas };
742159047fSniklas 
75b305b0f1Sespie /* Create a hash table.  This return a control block.  */
762159047fSniklas 
772159047fSniklas struct hash_control *
hash_new(void)78*007c2a45Smiod hash_new (void)
792159047fSniklas {
80b305b0f1Sespie   unsigned int size;
81b305b0f1Sespie   struct hash_control *ret;
82b305b0f1Sespie   unsigned int alloc;
832159047fSniklas 
84b305b0f1Sespie   size = DEFAULT_SIZE;
852159047fSniklas 
86b305b0f1Sespie   ret = (struct hash_control *) xmalloc (sizeof *ret);
87b305b0f1Sespie   obstack_begin (&ret->memory, chunksize);
88b305b0f1Sespie   alloc = size * sizeof (struct hash_entry *);
89b305b0f1Sespie   ret->table = (struct hash_entry **) obstack_alloc (&ret->memory, alloc);
90b305b0f1Sespie   memset (ret->table, 0, alloc);
91b305b0f1Sespie   ret->size = size;
922159047fSniklas 
93b305b0f1Sespie #ifdef HASH_STATISTICS
94b305b0f1Sespie   ret->lookups = 0;
95b305b0f1Sespie   ret->hash_compares = 0;
96b305b0f1Sespie   ret->string_compares = 0;
97b305b0f1Sespie   ret->insertions = 0;
98b305b0f1Sespie   ret->replacements = 0;
99b305b0f1Sespie   ret->deletions = 0;
100b305b0f1Sespie #endif
101b305b0f1Sespie 
102b305b0f1Sespie   return ret;
1032159047fSniklas }
1042159047fSniklas 
105b305b0f1Sespie /* Delete a hash table, freeing all allocated memory.  */
106b305b0f1Sespie 
1072159047fSniklas void
hash_die(struct hash_control * table)108*007c2a45Smiod hash_die (struct hash_control *table)
1092159047fSniklas {
110b305b0f1Sespie   obstack_free (&table->memory, 0);
111b305b0f1Sespie   free (table);
1122159047fSniklas }
1132159047fSniklas 
114b305b0f1Sespie /* Look up a string in a hash table.  This returns a pointer to the
115b305b0f1Sespie    hash_entry, or NULL if the string is not in the table.  If PLIST is
116b305b0f1Sespie    not NULL, this sets *PLIST to point to the start of the list which
117b305b0f1Sespie    would hold this hash entry.  If PHASH is not NULL, this sets *PHASH
118b305b0f1Sespie    to the hash code for KEY.
119b305b0f1Sespie 
120b305b0f1Sespie    Each time we look up a string, we move it to the start of the list
121b305b0f1Sespie    for its hash code, to take advantage of referential locality.  */
122b305b0f1Sespie 
123*007c2a45Smiod static struct hash_entry *hash_lookup (struct hash_control *,
124b305b0f1Sespie 				       const char *,
125b305b0f1Sespie 				       struct hash_entry ***,
126*007c2a45Smiod 				       unsigned long *);
127b305b0f1Sespie 
128b305b0f1Sespie static struct hash_entry *
hash_lookup(struct hash_control * table,const char * key,struct hash_entry *** plist,unsigned long * phash)129*007c2a45Smiod hash_lookup (struct hash_control *table, const char *key,
130*007c2a45Smiod 	     struct hash_entry ***plist, unsigned long *phash)
1312159047fSniklas {
132b305b0f1Sespie   register unsigned long hash;
133b305b0f1Sespie   unsigned int len;
134b305b0f1Sespie   register const unsigned char *s;
135b305b0f1Sespie   register unsigned int c;
136b305b0f1Sespie   unsigned int index;
137b305b0f1Sespie   struct hash_entry **list;
138b305b0f1Sespie   struct hash_entry *p;
139b305b0f1Sespie   struct hash_entry *prev;
140b305b0f1Sespie 
141b305b0f1Sespie #ifdef HASH_STATISTICS
142b305b0f1Sespie   ++table->lookups;
1432159047fSniklas #endif
1442159047fSniklas 
145b305b0f1Sespie   hash = 0;
146b305b0f1Sespie   len = 0;
147b305b0f1Sespie   s = (const unsigned char *) key;
148b305b0f1Sespie   while ((c = *s++) != '\0')
1492159047fSniklas     {
150b305b0f1Sespie       hash += c + (c << 17);
151b305b0f1Sespie       hash ^= hash >> 2;
1522159047fSniklas       ++len;
1532159047fSniklas     }
154b305b0f1Sespie   hash += len + (len << 17);
155b305b0f1Sespie   hash ^= hash >> 2;
156b305b0f1Sespie 
157b305b0f1Sespie   if (phash != NULL)
158b305b0f1Sespie     *phash = hash;
159b305b0f1Sespie 
160b305b0f1Sespie   index = hash % table->size;
161b305b0f1Sespie   list = table->table + index;
162b305b0f1Sespie 
163b305b0f1Sespie   if (plist != NULL)
164b305b0f1Sespie     *plist = list;
165b305b0f1Sespie 
166b305b0f1Sespie   prev = NULL;
167b305b0f1Sespie   for (p = *list; p != NULL; p = p->next)
168b305b0f1Sespie     {
169b305b0f1Sespie #ifdef HASH_STATISTICS
170b305b0f1Sespie       ++table->hash_compares;
171b305b0f1Sespie #endif
172b305b0f1Sespie 
173b305b0f1Sespie       if (p->hash == hash)
174b305b0f1Sespie 	{
175b305b0f1Sespie #ifdef HASH_STATISTICS
176b305b0f1Sespie 	  ++table->string_compares;
177b305b0f1Sespie #endif
178b305b0f1Sespie 
179b305b0f1Sespie 	  if (strcmp (p->string, key) == 0)
180b305b0f1Sespie 	    {
181b305b0f1Sespie 	      if (prev != NULL)
182b305b0f1Sespie 		{
183b305b0f1Sespie 		  prev->next = p->next;
184b305b0f1Sespie 		  p->next = *list;
185b305b0f1Sespie 		  *list = p;
186b305b0f1Sespie 		}
187b305b0f1Sespie 
188b305b0f1Sespie 	      return p;
189b305b0f1Sespie 	    }
190b305b0f1Sespie 	}
191b305b0f1Sespie 
192b305b0f1Sespie       prev = p;
193b305b0f1Sespie     }
194b305b0f1Sespie 
195b305b0f1Sespie   return NULL;
196b305b0f1Sespie }
197b305b0f1Sespie 
198b305b0f1Sespie /* Insert an entry into a hash table.  This returns NULL on success.
199b305b0f1Sespie    On error, it returns a printable string indicating the error.  It
200b305b0f1Sespie    is considered to be an error if the entry already exists in the
201b305b0f1Sespie    hash table.  */
202b305b0f1Sespie 
203b305b0f1Sespie const char *
hash_insert(struct hash_control * table,const char * key,PTR value)204*007c2a45Smiod hash_insert (struct hash_control *table, const char *key, PTR value)
205b305b0f1Sespie {
206b305b0f1Sespie   struct hash_entry *p;
207b305b0f1Sespie   struct hash_entry **list;
208b305b0f1Sespie   unsigned long hash;
209b305b0f1Sespie 
210b305b0f1Sespie   p = hash_lookup (table, key, &list, &hash);
211b305b0f1Sespie   if (p != NULL)
212b305b0f1Sespie     return "exists";
213b305b0f1Sespie 
214b305b0f1Sespie #ifdef HASH_STATISTICS
215b305b0f1Sespie   ++table->insertions;
216b305b0f1Sespie #endif
217b305b0f1Sespie 
218b55d4692Sfgsch   p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p));
219b305b0f1Sespie   p->string = key;
220b305b0f1Sespie   p->hash = hash;
221b305b0f1Sespie   p->data = value;
222b305b0f1Sespie 
223b305b0f1Sespie   p->next = *list;
224b305b0f1Sespie   *list = p;
225b305b0f1Sespie 
226b305b0f1Sespie   return NULL;
227b305b0f1Sespie }
228b305b0f1Sespie 
229b305b0f1Sespie /* Insert or replace an entry in a hash table.  This returns NULL on
230b305b0f1Sespie    success.  On error, it returns a printable string indicating the
231b305b0f1Sespie    error.  If an entry already exists, its value is replaced.  */
232b305b0f1Sespie 
233b305b0f1Sespie const char *
hash_jam(struct hash_control * table,const char * key,PTR value)234*007c2a45Smiod hash_jam (struct hash_control *table, const char *key, PTR value)
235b305b0f1Sespie {
236b305b0f1Sespie   struct hash_entry *p;
237b305b0f1Sespie   struct hash_entry **list;
238b305b0f1Sespie   unsigned long hash;
239b305b0f1Sespie 
240b305b0f1Sespie   p = hash_lookup (table, key, &list, &hash);
241b305b0f1Sespie   if (p != NULL)
242b305b0f1Sespie     {
243b305b0f1Sespie #ifdef HASH_STATISTICS
244b305b0f1Sespie       ++table->replacements;
245b305b0f1Sespie #endif
246b305b0f1Sespie 
247b305b0f1Sespie       p->data = value;
248b305b0f1Sespie     }
249b305b0f1Sespie   else
250b305b0f1Sespie     {
251b305b0f1Sespie #ifdef HASH_STATISTICS
252b305b0f1Sespie       ++table->insertions;
253b305b0f1Sespie #endif
254b305b0f1Sespie 
255b55d4692Sfgsch       p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p));
256b305b0f1Sespie       p->string = key;
257b305b0f1Sespie       p->hash = hash;
258b305b0f1Sespie       p->data = value;
259b305b0f1Sespie 
260b305b0f1Sespie       p->next = *list;
261b305b0f1Sespie       *list = p;
262b305b0f1Sespie     }
263b305b0f1Sespie 
264b305b0f1Sespie   return NULL;
265b305b0f1Sespie }
266b305b0f1Sespie 
267b305b0f1Sespie /* Replace an existing entry in a hash table.  This returns the old
268b305b0f1Sespie    value stored for the entry.  If the entry is not found in the hash
269b305b0f1Sespie    table, this does nothing and returns NULL.  */
270b305b0f1Sespie 
271b305b0f1Sespie PTR
hash_replace(struct hash_control * table,const char * key,PTR value)272*007c2a45Smiod hash_replace (struct hash_control *table, const char *key, PTR value)
273b305b0f1Sespie {
274b305b0f1Sespie   struct hash_entry *p;
275b305b0f1Sespie   PTR ret;
276b305b0f1Sespie 
277b305b0f1Sespie   p = hash_lookup (table, key, NULL, NULL);
278b305b0f1Sespie   if (p == NULL)
279b305b0f1Sespie     return NULL;
280b305b0f1Sespie 
281b305b0f1Sespie #ifdef HASH_STATISTICS
282b305b0f1Sespie   ++table->replacements;
283b305b0f1Sespie #endif
284b305b0f1Sespie 
285b305b0f1Sespie   ret = p->data;
286b305b0f1Sespie 
287b305b0f1Sespie   p->data = value;
288b305b0f1Sespie 
289b305b0f1Sespie   return ret;
290b305b0f1Sespie }
291b305b0f1Sespie 
292b305b0f1Sespie /* Find an entry in a hash table, returning its value.  Returns NULL
293b305b0f1Sespie    if the entry is not found.  */
294b305b0f1Sespie 
295b305b0f1Sespie PTR
hash_find(struct hash_control * table,const char * key)296*007c2a45Smiod hash_find (struct hash_control *table, const char *key)
297b305b0f1Sespie {
298b305b0f1Sespie   struct hash_entry *p;
299b305b0f1Sespie 
300b305b0f1Sespie   p = hash_lookup (table, key, NULL, NULL);
301b305b0f1Sespie   if (p == NULL)
302b305b0f1Sespie     return NULL;
303b305b0f1Sespie 
304b305b0f1Sespie   return p->data;
305b305b0f1Sespie }
306b305b0f1Sespie 
307b305b0f1Sespie /* Delete an entry from a hash table.  This returns the value stored
308b305b0f1Sespie    for that entry, or NULL if there is no such entry.  */
309b305b0f1Sespie 
310b305b0f1Sespie PTR
hash_delete(struct hash_control * table,const char * key)311*007c2a45Smiod hash_delete (struct hash_control *table, const char *key)
312b305b0f1Sespie {
313b305b0f1Sespie   struct hash_entry *p;
314b305b0f1Sespie   struct hash_entry **list;
315b305b0f1Sespie 
316b305b0f1Sespie   p = hash_lookup (table, key, &list, NULL);
317b305b0f1Sespie   if (p == NULL)
318b305b0f1Sespie     return NULL;
319b305b0f1Sespie 
320b305b0f1Sespie   if (p != *list)
321b305b0f1Sespie     abort ();
322b305b0f1Sespie 
323b305b0f1Sespie #ifdef HASH_STATISTICS
324b305b0f1Sespie   ++table->deletions;
325b305b0f1Sespie #endif
326b305b0f1Sespie 
327b305b0f1Sespie   *list = p->next;
328b305b0f1Sespie 
329b305b0f1Sespie   /* Note that we never reclaim the memory for this entry.  If gas
330b305b0f1Sespie      ever starts deleting hash table entries in a big way, this will
331b305b0f1Sespie      have to change.  */
332b305b0f1Sespie 
333b305b0f1Sespie   return p->data;
334b305b0f1Sespie }
335b305b0f1Sespie 
336b305b0f1Sespie /* Traverse a hash table.  Call the function on every entry in the
337b305b0f1Sespie    hash table.  */
338b305b0f1Sespie 
339b305b0f1Sespie void
hash_traverse(struct hash_control * table,void (* pfn)(const char * key,PTR value))340*007c2a45Smiod hash_traverse (struct hash_control *table,
341*007c2a45Smiod 	       void (*pfn) (const char *key, PTR value))
342b305b0f1Sespie {
343b305b0f1Sespie   unsigned int i;
344b305b0f1Sespie 
345b305b0f1Sespie   for (i = 0; i < table->size; ++i)
346b305b0f1Sespie     {
347b305b0f1Sespie       struct hash_entry *p;
348b305b0f1Sespie 
349b305b0f1Sespie       for (p = table->table[i]; p != NULL; p = p->next)
350b305b0f1Sespie 	(*pfn) (p->string, p->data);
351b305b0f1Sespie     }
352b305b0f1Sespie }
353b305b0f1Sespie 
354b305b0f1Sespie /* Print hash table statistics on the specified file.  NAME is the
355b305b0f1Sespie    name of the hash table, used for printing a header.  */
356b305b0f1Sespie 
357b305b0f1Sespie void
hash_print_statistics(FILE * f ATTRIBUTE_UNUSED,const char * name ATTRIBUTE_UNUSED,struct hash_control * table ATTRIBUTE_UNUSED)358*007c2a45Smiod hash_print_statistics (FILE *f ATTRIBUTE_UNUSED,
359*007c2a45Smiod 		       const char *name ATTRIBUTE_UNUSED,
360*007c2a45Smiod 		       struct hash_control *table ATTRIBUTE_UNUSED)
361b305b0f1Sespie {
362b305b0f1Sespie #ifdef HASH_STATISTICS
363b305b0f1Sespie   unsigned int i;
364b305b0f1Sespie   unsigned long total;
365b305b0f1Sespie   unsigned long empty;
366b305b0f1Sespie 
367b305b0f1Sespie   fprintf (f, "%s hash statistics:\n", name);
368b305b0f1Sespie   fprintf (f, "\t%lu lookups\n", table->lookups);
369b305b0f1Sespie   fprintf (f, "\t%lu hash comparisons\n", table->hash_compares);
370b305b0f1Sespie   fprintf (f, "\t%lu string comparisons\n", table->string_compares);
371b305b0f1Sespie   fprintf (f, "\t%lu insertions\n", table->insertions);
372b305b0f1Sespie   fprintf (f, "\t%lu replacements\n", table->replacements);
373b305b0f1Sespie   fprintf (f, "\t%lu deletions\n", table->deletions);
374b305b0f1Sespie 
375b305b0f1Sespie   total = 0;
376b305b0f1Sespie   empty = 0;
377b305b0f1Sespie   for (i = 0; i < table->size; ++i)
378b305b0f1Sespie     {
379b305b0f1Sespie       struct hash_entry *p;
380b305b0f1Sespie 
381b305b0f1Sespie       if (table->table[i] == NULL)
382b305b0f1Sespie 	++empty;
383b305b0f1Sespie       else
384b305b0f1Sespie 	{
385b305b0f1Sespie 	  for (p = table->table[i]; p != NULL; p = p->next)
386b305b0f1Sespie 	    ++total;
387b305b0f1Sespie 	}
388b305b0f1Sespie     }
389b305b0f1Sespie 
390b305b0f1Sespie   fprintf (f, "\t%g average chain length\n", (double) total / table->size);
391b305b0f1Sespie   fprintf (f, "\t%lu empty slots\n", empty);
3922159047fSniklas #endif
3932159047fSniklas }
3942159047fSniklas 
3952159047fSniklas #ifdef TEST
3962159047fSniklas 
397b305b0f1Sespie /* This test program is left over from the old hash table code.  */
398b305b0f1Sespie 
399b55d4692Sfgsch /* Number of hash tables to maintain (at once) in any testing.  */
400b55d4692Sfgsch #define TABLES (6)
4012159047fSniklas 
402b55d4692Sfgsch /* We can have 12 statistics.  */
403b55d4692Sfgsch #define STATBUFSIZE (12)
404b55d4692Sfgsch 
405b55d4692Sfgsch /* Display statistics here.  */
406b55d4692Sfgsch int statbuf[STATBUFSIZE];
407b55d4692Sfgsch 
408b55d4692Sfgsch /* Human farts here.  */
409b55d4692Sfgsch char answer[100];
410b55d4692Sfgsch 
411b55d4692Sfgsch /* We test many hash tables at once.  */
412b55d4692Sfgsch char *hashtable[TABLES];
413b55d4692Sfgsch 
414*007c2a45Smiod /* Points to current hash_control.  */
415b55d4692Sfgsch char *h;
4162159047fSniklas char **pp;
4172159047fSniklas char *p;
4182159047fSniklas char *name;
4192159047fSniklas char *value;
4202159047fSniklas int size;
4212159047fSniklas int used;
4222159047fSniklas char command;
423b55d4692Sfgsch 
424b55d4692Sfgsch /* Number 0:TABLES-1 of current hashed symbol table.  */
425b55d4692Sfgsch int number;
4262159047fSniklas 
427b305b0f1Sespie int
main()4282159047fSniklas main ()
4292159047fSniklas {
430191aa565Sniklas   void applicatee ();
431191aa565Sniklas   void destroy ();
4322159047fSniklas   char *what ();
4332159047fSniklas   int *ip;
4342159047fSniklas 
4352159047fSniklas   number = 0;
4362159047fSniklas   h = 0;
4372159047fSniklas   printf ("type h <RETURN> for help\n");
4382159047fSniklas   for (;;)
4392159047fSniklas     {
4402159047fSniklas       printf ("hash_test command: ");
4412159047fSniklas       gets (answer);
4422159047fSniklas       command = answer[0];
443c074d1c9Sdrahn       command = TOLOWER (command);	/* Ecch!  */
4442159047fSniklas       switch (command)
4452159047fSniklas 	{
4462159047fSniklas 	case '#':
4472159047fSniklas 	  printf ("old hash table #=%d.\n", number);
4482159047fSniklas 	  whattable ();
4492159047fSniklas 	  break;
4502159047fSniklas 	case '?':
4512159047fSniklas 	  for (pp = hashtable; pp < hashtable + TABLES; pp++)
4522159047fSniklas 	    {
453b55d4692Sfgsch 	      printf ("address of hash table #%d control block is %xx\n",
454b55d4692Sfgsch 		      pp - hashtable, *pp);
4552159047fSniklas 	    }
4562159047fSniklas 	  break;
4572159047fSniklas 	case 'a':
458b305b0f1Sespie 	  hash_traverse (h, applicatee);
4592159047fSniklas 	  break;
4602159047fSniklas 	case 'd':
461b305b0f1Sespie 	  hash_traverse (h, destroy);
4622159047fSniklas 	  hash_die (h);
4632159047fSniklas 	  break;
4642159047fSniklas 	case 'f':
4652159047fSniklas 	  p = hash_find (h, name = what ("symbol"));
4662159047fSniklas 	  printf ("value of \"%s\" is \"%s\"\n", name, p ? p : "NOT-PRESENT");
4672159047fSniklas 	  break;
4682159047fSniklas 	case 'h':
4692159047fSniklas 	  printf ("# show old, select new default hash table number\n");
4702159047fSniklas 	  printf ("? display all hashtable control block addresses\n");
4712159047fSniklas 	  printf ("a apply a simple display-er to each symbol in table\n");
4722159047fSniklas 	  printf ("d die: destroy hashtable\n");
4732159047fSniklas 	  printf ("f find value of nominated symbol\n");
4742159047fSniklas 	  printf ("h this help\n");
4752159047fSniklas 	  printf ("i insert value into symbol\n");
4762159047fSniklas 	  printf ("j jam value into symbol\n");
4772159047fSniklas 	  printf ("n new hashtable\n");
4782159047fSniklas 	  printf ("r replace a value with another\n");
4792159047fSniklas 	  printf ("s say what %% of table is used\n");
4802159047fSniklas 	  printf ("q exit this program\n");
4812159047fSniklas 	  printf ("x delete a symbol from table, report its value\n");
4822159047fSniklas 	  break;
4832159047fSniklas 	case 'i':
4842159047fSniklas 	  p = hash_insert (h, name = what ("symbol"), value = what ("value"));
4852159047fSniklas 	  if (p)
4862159047fSniklas 	    {
4872159047fSniklas 	      printf ("symbol=\"%s\"  value=\"%s\"  error=%s\n", name, value,
4882159047fSniklas 		      p);
4892159047fSniklas 	    }
4902159047fSniklas 	  break;
4912159047fSniklas 	case 'j':
4922159047fSniklas 	  p = hash_jam (h, name = what ("symbol"), value = what ("value"));
4932159047fSniklas 	  if (p)
4942159047fSniklas 	    {
4952159047fSniklas 	      printf ("symbol=\"%s\"  value=\"%s\"  error=%s\n", name, value, p);
4962159047fSniklas 	    }
4972159047fSniklas 	  break;
4982159047fSniklas 	case 'n':
4992159047fSniklas 	  h = hashtable[number] = (char *) hash_new ();
5002159047fSniklas 	  break;
5012159047fSniklas 	case 'q':
5022159047fSniklas 	  exit (EXIT_SUCCESS);
5032159047fSniklas 	case 'r':
5042159047fSniklas 	  p = hash_replace (h, name = what ("symbol"), value = what ("value"));
5052159047fSniklas 	  printf ("old value was \"%s\"\n", p ? p : "{}");
5062159047fSniklas 	  break;
5072159047fSniklas 	case 's':
5082159047fSniklas 	  hash_say (h, statbuf, STATBUFSIZE);
5092159047fSniklas 	  for (ip = statbuf; ip < statbuf + STATBUFSIZE; ip++)
5102159047fSniklas 	    {
5112159047fSniklas 	      printf ("%d ", *ip);
5122159047fSniklas 	    }
5132159047fSniklas 	  printf ("\n");
5142159047fSniklas 	  break;
5152159047fSniklas 	case 'x':
5162159047fSniklas 	  p = hash_delete (h, name = what ("symbol"));
5172159047fSniklas 	  printf ("old value was \"%s\"\n", p ? p : "{}");
5182159047fSniklas 	  break;
5192159047fSniklas 	default:
5202159047fSniklas 	  printf ("I can't understand command \"%c\"\n", command);
5212159047fSniklas 	  break;
5222159047fSniklas 	}
5232159047fSniklas     }
5242159047fSniklas }
5252159047fSniklas 
5262159047fSniklas char *
what(description)5272159047fSniklas what (description)
5282159047fSniklas      char *description;
5292159047fSniklas {
5302159047fSniklas   printf ("   %s : ", description);
5312159047fSniklas   gets (answer);
532c074d1c9Sdrahn   return xstrdup (answer);
5332159047fSniklas }
5342159047fSniklas 
535191aa565Sniklas void
destroy(string,value)5362159047fSniklas destroy (string, value)
5372159047fSniklas      char *string;
5382159047fSniklas      char *value;
5392159047fSniklas {
5402159047fSniklas   free (string);
5412159047fSniklas   free (value);
5422159047fSniklas }
5432159047fSniklas 
544191aa565Sniklas void
applicatee(string,value)5452159047fSniklas applicatee (string, value)
5462159047fSniklas      char *string;
5472159047fSniklas      char *value;
5482159047fSniklas {
5492159047fSniklas   printf ("%.20s-%.20s\n", string, value);
5502159047fSniklas }
5512159047fSniklas 
552b55d4692Sfgsch /* Determine number: what hash table to use.
553b55d4692Sfgsch    Also determine h: points to hash_control.  */
5542159047fSniklas 
555b55d4692Sfgsch void
whattable()556b55d4692Sfgsch whattable ()
557b55d4692Sfgsch {
5582159047fSniklas   for (;;)
5592159047fSniklas     {
5602159047fSniklas       printf ("   what hash table (%d:%d) ?  ", 0, TABLES - 1);
5612159047fSniklas       gets (answer);
5622159047fSniklas       sscanf (answer, "%d", &number);
5632159047fSniklas       if (number >= 0 && number < TABLES)
5642159047fSniklas 	{
5652159047fSniklas 	  h = hashtable[number];
5662159047fSniklas 	  if (!h)
5672159047fSniklas 	    {
5682159047fSniklas 	      printf ("warning: current hash-table-#%d. has no hash-control\n", number);
5692159047fSniklas 	    }
5702159047fSniklas 	  return;
5712159047fSniklas 	}
5722159047fSniklas       else
5732159047fSniklas 	{
5742159047fSniklas 	  printf ("invalid hash table number: %d\n", number);
5752159047fSniklas 	}
5762159047fSniklas     }
5772159047fSniklas }
5782159047fSniklas 
579b55d4692Sfgsch #endif /* TEST */
580