hash.c revision 78828
160484Sobrien/* hash.c -- gas hash table code 278828Sobrien Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 378828Sobrien 2000 433965Sjdp Free Software Foundation, Inc. 533965Sjdp 633965Sjdp This file is part of GAS, the GNU Assembler. 733965Sjdp 833965Sjdp GAS is free software; you can redistribute it and/or modify 933965Sjdp it under the terms of the GNU General Public License as published by 1033965Sjdp the Free Software Foundation; either version 2, or (at your option) 1133965Sjdp any later version. 1233965Sjdp 1333965Sjdp GAS is distributed in the hope that it will be useful, 1433965Sjdp but WITHOUT ANY WARRANTY; without even the implied warranty of 1533965Sjdp MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1633965Sjdp GNU General Public License for more details. 1733965Sjdp 1833965Sjdp You should have received a copy of the GNU General Public License 1960484Sobrien along with GAS; see the file COPYING. If not, write to the Free 2060484Sobrien Software Foundation, 59 Temple Place - Suite 330, Boston, MA 2160484Sobrien 02111-1307, USA. */ 2233965Sjdp 2360484Sobrien/* This version of the hash table code is a wholescale replacement of 2460484Sobrien the old hash table code, which was fairly bad. This is based on 2560484Sobrien the hash table code in BFD, but optimized slightly for the 2660484Sobrien asssembler. The assembler does not need to derive structures that 2760484Sobrien are stored in the hash table. Instead, it always stores a pointer. 2860484Sobrien The assembler uses the hash table mostly to store symbols, and we 2960484Sobrien don't need to confuse the symbol structure with a hash table 3060484Sobrien structure. */ 3133965Sjdp 3233965Sjdp#include "as.h" 3360484Sobrien#include "obstack.h" 3433965Sjdp 3560484Sobrien/* The default number of entries to use when creating a hash table. */ 3633965Sjdp 3760484Sobrien#define DEFAULT_SIZE (4051) 3833965Sjdp 3960484Sobrien/* An entry in a hash table. */ 4033965Sjdp 4177298Sobrienstruct hash_entry { 4260484Sobrien /* Next entry for this hash code. */ 4360484Sobrien struct hash_entry *next; 4460484Sobrien /* String being hashed. */ 4560484Sobrien const char *string; 4660484Sobrien /* Hash code. This is the full hash code, not the index into the 4760484Sobrien table. */ 4860484Sobrien unsigned long hash; 4960484Sobrien /* Pointer being stored in the hash table. */ 5060484Sobrien PTR data; 5133965Sjdp}; 5233965Sjdp 5360484Sobrien/* A hash table. */ 5433965Sjdp 5577298Sobrienstruct hash_control { 5660484Sobrien /* The hash array. */ 5760484Sobrien struct hash_entry **table; 5860484Sobrien /* The number of slots in the hash table. */ 5960484Sobrien unsigned int size; 6060484Sobrien /* An obstack for this hash table. */ 6160484Sobrien struct obstack memory; 6233965Sjdp 6360484Sobrien#ifdef HASH_STATISTICS 6460484Sobrien /* Statistics. */ 6560484Sobrien unsigned long lookups; 6660484Sobrien unsigned long hash_compares; 6760484Sobrien unsigned long string_compares; 6860484Sobrien unsigned long insertions; 6960484Sobrien unsigned long replacements; 7060484Sobrien unsigned long deletions; 7160484Sobrien#endif /* HASH_STATISTICS */ 7233965Sjdp}; 7333965Sjdp 7460484Sobrien/* Create a hash table. This return a control block. */ 7533965Sjdp 7660484Sobrienstruct hash_control * 7760484Sobrienhash_new () 7860484Sobrien{ 7960484Sobrien unsigned int size; 8060484Sobrien struct hash_control *ret; 8160484Sobrien unsigned int alloc; 8233965Sjdp 8360484Sobrien size = DEFAULT_SIZE; 8433965Sjdp 8560484Sobrien ret = (struct hash_control *) xmalloc (sizeof *ret); 8660484Sobrien obstack_begin (&ret->memory, chunksize); 8760484Sobrien alloc = size * sizeof (struct hash_entry *); 8860484Sobrien ret->table = (struct hash_entry **) obstack_alloc (&ret->memory, alloc); 8960484Sobrien memset (ret->table, 0, alloc); 9060484Sobrien ret->size = size; 9133965Sjdp 9260484Sobrien#ifdef HASH_STATISTICS 9360484Sobrien ret->lookups = 0; 9460484Sobrien ret->hash_compares = 0; 9560484Sobrien ret->string_compares = 0; 9660484Sobrien ret->insertions = 0; 9760484Sobrien ret->replacements = 0; 9860484Sobrien ret->deletions = 0; 9960484Sobrien#endif 10033965Sjdp 10177298Sobrien return ret; 10260484Sobrien} 10333965Sjdp 10460484Sobrien/* Delete a hash table, freeing all allocated memory. */ 10533965Sjdp 10660484Sobrienvoid 10760484Sobrienhash_die (table) 10860484Sobrien struct hash_control *table; 10960484Sobrien{ 11060484Sobrien obstack_free (&table->memory, 0); 11160484Sobrien free (table); 11260484Sobrien} 11333965Sjdp 11460484Sobrien/* Look up a string in a hash table. This returns a pointer to the 11560484Sobrien hash_entry, or NULL if the string is not in the table. If PLIST is 11660484Sobrien not NULL, this sets *PLIST to point to the start of the list which 11760484Sobrien would hold this hash entry. If PHASH is not NULL, this sets *PHASH 11860484Sobrien to the hash code for KEY. 11933965Sjdp 12060484Sobrien Each time we look up a string, we move it to the start of the list 12160484Sobrien for its hash code, to take advantage of referential locality. */ 12233965Sjdp 12360484Sobrienstatic struct hash_entry *hash_lookup PARAMS ((struct hash_control *, 12460484Sobrien const char *, 12560484Sobrien struct hash_entry ***, 12660484Sobrien unsigned long *)); 12733965Sjdp 12860484Sobrienstatic struct hash_entry * 12960484Sobrienhash_lookup (table, key, plist, phash) 13060484Sobrien struct hash_control *table; 13160484Sobrien const char *key; 13260484Sobrien struct hash_entry ***plist; 13360484Sobrien unsigned long *phash; 13460484Sobrien{ 13560484Sobrien register unsigned long hash; 13660484Sobrien unsigned int len; 13760484Sobrien register const unsigned char *s; 13860484Sobrien register unsigned int c; 13960484Sobrien unsigned int index; 14060484Sobrien struct hash_entry **list; 14160484Sobrien struct hash_entry *p; 14260484Sobrien struct hash_entry *prev; 14333965Sjdp 14460484Sobrien#ifdef HASH_STATISTICS 14560484Sobrien ++table->lookups; 14660484Sobrien#endif 14733965Sjdp 14860484Sobrien hash = 0; 14960484Sobrien len = 0; 15060484Sobrien s = (const unsigned char *) key; 15160484Sobrien while ((c = *s++) != '\0') 15260484Sobrien { 15360484Sobrien hash += c + (c << 17); 15460484Sobrien hash ^= hash >> 2; 15560484Sobrien ++len; 15660484Sobrien } 15760484Sobrien hash += len + (len << 17); 15860484Sobrien hash ^= hash >> 2; 15933965Sjdp 16060484Sobrien if (phash != NULL) 16160484Sobrien *phash = hash; 16233965Sjdp 16360484Sobrien index = hash % table->size; 16460484Sobrien list = table->table + index; 16533965Sjdp 16660484Sobrien if (plist != NULL) 16760484Sobrien *plist = list; 16833965Sjdp 16960484Sobrien prev = NULL; 17060484Sobrien for (p = *list; p != NULL; p = p->next) 17160484Sobrien { 17260484Sobrien#ifdef HASH_STATISTICS 17360484Sobrien ++table->hash_compares; 17460484Sobrien#endif 17533965Sjdp 17660484Sobrien if (p->hash == hash) 17733965Sjdp { 17860484Sobrien#ifdef HASH_STATISTICS 17960484Sobrien ++table->string_compares; 18033965Sjdp#endif 18133965Sjdp 18260484Sobrien if (strcmp (p->string, key) == 0) 18360484Sobrien { 18460484Sobrien if (prev != NULL) 18560484Sobrien { 18660484Sobrien prev->next = p->next; 18760484Sobrien p->next = *list; 18860484Sobrien *list = p; 18960484Sobrien } 19060484Sobrien 19160484Sobrien return p; 19260484Sobrien } 19333965Sjdp } 19460484Sobrien 19560484Sobrien prev = p; 19633965Sjdp } 19760484Sobrien 19860484Sobrien return NULL; 19933965Sjdp} 20060484Sobrien 20160484Sobrien/* Insert an entry into a hash table. This returns NULL on success. 20260484Sobrien On error, it returns a printable string indicating the error. It 20360484Sobrien is considered to be an error if the entry already exists in the 20460484Sobrien hash table. */ 20560484Sobrien 20660484Sobrienconst char * 20760484Sobrienhash_insert (table, key, value) 20860484Sobrien struct hash_control *table; 20960484Sobrien const char *key; 21033965Sjdp PTR value; 21133965Sjdp{ 21260484Sobrien struct hash_entry *p; 21360484Sobrien struct hash_entry **list; 21460484Sobrien unsigned long hash; 21533965Sjdp 21660484Sobrien p = hash_lookup (table, key, &list, &hash); 21760484Sobrien if (p != NULL) 21860484Sobrien return "exists"; 21960484Sobrien 22060484Sobrien#ifdef HASH_STATISTICS 22160484Sobrien ++table->insertions; 22260484Sobrien#endif 22360484Sobrien 22477298Sobrien p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p)); 22560484Sobrien p->string = key; 22660484Sobrien p->hash = hash; 22760484Sobrien p->data = value; 22860484Sobrien 22960484Sobrien p->next = *list; 23060484Sobrien *list = p; 23160484Sobrien 23260484Sobrien return NULL; 23333965Sjdp} 23433965Sjdp 23560484Sobrien/* Insert or replace an entry in a hash table. This returns NULL on 23660484Sobrien success. On error, it returns a printable string indicating the 23760484Sobrien error. If an entry already exists, its value is replaced. */ 23833965Sjdp 23933965Sjdpconst char * 24060484Sobrienhash_jam (table, key, value) 24160484Sobrien struct hash_control *table; 24260484Sobrien const char *key; 24333965Sjdp PTR value; 24433965Sjdp{ 24560484Sobrien struct hash_entry *p; 24660484Sobrien struct hash_entry **list; 24760484Sobrien unsigned long hash; 24833965Sjdp 24960484Sobrien p = hash_lookup (table, key, &list, &hash); 25060484Sobrien if (p != NULL) 25133965Sjdp { 25260484Sobrien#ifdef HASH_STATISTICS 25360484Sobrien ++table->replacements; 25460484Sobrien#endif 25560484Sobrien 25660484Sobrien p->data = value; 25733965Sjdp } 25860484Sobrien else 25933965Sjdp { 26060484Sobrien#ifdef HASH_STATISTICS 26160484Sobrien ++table->insertions; 26260484Sobrien#endif 26360484Sobrien 26477298Sobrien p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p)); 26560484Sobrien p->string = key; 26660484Sobrien p->hash = hash; 26760484Sobrien p->data = value; 26860484Sobrien 26960484Sobrien p->next = *list; 27060484Sobrien *list = p; 27133965Sjdp } 27260484Sobrien 27360484Sobrien return NULL; 27433965Sjdp} 27533965Sjdp 27660484Sobrien/* Replace an existing entry in a hash table. This returns the old 27760484Sobrien value stored for the entry. If the entry is not found in the hash 27860484Sobrien table, this does nothing and returns NULL. */ 27960484Sobrien 28060484SobrienPTR 28160484Sobrienhash_replace (table, key, value) 28260484Sobrien struct hash_control *table; 28360484Sobrien const char *key; 28460484Sobrien PTR value; 28533965Sjdp{ 28660484Sobrien struct hash_entry *p; 28760484Sobrien PTR ret; 28833965Sjdp 28960484Sobrien p = hash_lookup (table, key, NULL, NULL); 29060484Sobrien if (p == NULL) 29160484Sobrien return NULL; 29260484Sobrien 29360484Sobrien#ifdef HASH_STATISTICS 29460484Sobrien ++table->replacements; 29533965Sjdp#endif 29633965Sjdp 29760484Sobrien ret = p->data; 29833965Sjdp 29960484Sobrien p->data = value; 30033965Sjdp 30160484Sobrien return ret; 30260484Sobrien} 30333965Sjdp 30460484Sobrien/* Find an entry in a hash table, returning its value. Returns NULL 30560484Sobrien if the entry is not found. */ 30660484Sobrien 30760484SobrienPTR 30860484Sobrienhash_find (table, key) 30960484Sobrien struct hash_control *table; 31060484Sobrien const char *key; 31133965Sjdp{ 31260484Sobrien struct hash_entry *p; 31333965Sjdp 31460484Sobrien p = hash_lookup (table, key, NULL, NULL); 31560484Sobrien if (p == NULL) 31660484Sobrien return NULL; 31760484Sobrien 31860484Sobrien return p->data; 31933965Sjdp} 32060484Sobrien 32160484Sobrien/* Delete an entry from a hash table. This returns the value stored 32260484Sobrien for that entry, or NULL if there is no such entry. */ 32360484Sobrien 32433965SjdpPTR 32560484Sobrienhash_delete (table, key) 32660484Sobrien struct hash_control *table; 32760484Sobrien const char *key; 32833965Sjdp{ 32960484Sobrien struct hash_entry *p; 33060484Sobrien struct hash_entry **list; 33133965Sjdp 33260484Sobrien p = hash_lookup (table, key, &list, NULL); 33360484Sobrien if (p == NULL) 33433965Sjdp return NULL; 33560484Sobrien 33660484Sobrien if (p != *list) 33760484Sobrien abort (); 33860484Sobrien 33960484Sobrien#ifdef HASH_STATISTICS 34060484Sobrien ++table->deletions; 34160484Sobrien#endif 34260484Sobrien 34360484Sobrien *list = p->next; 34460484Sobrien 34560484Sobrien /* Note that we never reclaim the memory for this entry. If gas 34660484Sobrien ever starts deleting hash table entries in a big way, this will 34760484Sobrien have to change. */ 34860484Sobrien 34960484Sobrien return p->data; 35033965Sjdp} 35133965Sjdp 35260484Sobrien/* Traverse a hash table. Call the function on every entry in the 35360484Sobrien hash table. */ 35433965Sjdp 35560484Sobrienvoid 35660484Sobrienhash_traverse (table, pfn) 35760484Sobrien struct hash_control *table; 35860484Sobrien void (*pfn) PARAMS ((const char *key, PTR value)); 35933965Sjdp{ 36060484Sobrien unsigned int i; 36133965Sjdp 36260484Sobrien for (i = 0; i < table->size; ++i) 36333965Sjdp { 36460484Sobrien struct hash_entry *p; 36533965Sjdp 36660484Sobrien for (p = table->table[i]; p != NULL; p = p->next) 36760484Sobrien (*pfn) (p->string, p->data); 36833965Sjdp } 36933965Sjdp} 37060484Sobrien 37160484Sobrien/* Print hash table statistics on the specified file. NAME is the 37260484Sobrien name of the hash table, used for printing a header. */ 37360484Sobrien 37433965Sjdpvoid 37560484Sobrienhash_print_statistics (f, name, table) 37660484Sobrien FILE *f ATTRIBUTE_UNUSED; 37760484Sobrien const char *name ATTRIBUTE_UNUSED; 37860484Sobrien struct hash_control *table ATTRIBUTE_UNUSED; 37933965Sjdp{ 38060484Sobrien#ifdef HASH_STATISTICS 38160484Sobrien unsigned int i; 38260484Sobrien unsigned long total; 38360484Sobrien unsigned long empty; 38433965Sjdp 38560484Sobrien fprintf (f, "%s hash statistics:\n", name); 38660484Sobrien fprintf (f, "\t%lu lookups\n", table->lookups); 38760484Sobrien fprintf (f, "\t%lu hash comparisons\n", table->hash_compares); 38860484Sobrien fprintf (f, "\t%lu string comparisons\n", table->string_compares); 38960484Sobrien fprintf (f, "\t%lu insertions\n", table->insertions); 39060484Sobrien fprintf (f, "\t%lu replacements\n", table->replacements); 39160484Sobrien fprintf (f, "\t%lu deletions\n", table->deletions); 39233965Sjdp 39360484Sobrien total = 0; 39460484Sobrien empty = 0; 39560484Sobrien for (i = 0; i < table->size; ++i) 39660484Sobrien { 39760484Sobrien struct hash_entry *p; 39833965Sjdp 39960484Sobrien if (table->table[i] == NULL) 40060484Sobrien ++empty; 40160484Sobrien else 40260484Sobrien { 40360484Sobrien for (p = table->table[i]; p != NULL; p = p->next) 40460484Sobrien ++total; 40560484Sobrien } 40660484Sobrien } 40733965Sjdp 40860484Sobrien fprintf (f, "\t%g average chain length\n", (double) total / table->size); 40960484Sobrien fprintf (f, "\t%lu empty slots\n", empty); 41060484Sobrien#endif 41133965Sjdp} 41233965Sjdp 41333965Sjdp#ifdef TEST 41433965Sjdp 41560484Sobrien/* This test program is left over from the old hash table code. */ 41660484Sobrien 41777298Sobrien/* Number of hash tables to maintain (at once) in any testing. */ 41877298Sobrien#define TABLES (6) 41933965Sjdp 42077298Sobrien/* We can have 12 statistics. */ 42177298Sobrien#define STATBUFSIZE (12) 42277298Sobrien 42377298Sobrien/* Display statistics here. */ 42477298Sobrienint statbuf[STATBUFSIZE]; 42577298Sobrien 42677298Sobrien/* Human farts here. */ 42777298Sobrienchar answer[100]; 42877298Sobrien 42977298Sobrien/* We test many hash tables at once. */ 43077298Sobrienchar *hashtable[TABLES]; 43177298Sobrien 43277298Sobrien/* Points to curent hash_control. */ 43377298Sobrienchar *h; 43433965Sjdpchar **pp; 43533965Sjdpchar *p; 43633965Sjdpchar *name; 43733965Sjdpchar *value; 43833965Sjdpint size; 43933965Sjdpint used; 44033965Sjdpchar command; 44133965Sjdp 44277298Sobrien/* Number 0:TABLES-1 of current hashed symbol table. */ 44377298Sobrienint number; 44477298Sobrien 44560484Sobrienint 44633965Sjdpmain () 44733965Sjdp{ 44833965Sjdp void applicatee (); 44933965Sjdp void destroy (); 45033965Sjdp char *what (); 45133965Sjdp int *ip; 45233965Sjdp 45333965Sjdp number = 0; 45433965Sjdp h = 0; 45533965Sjdp printf ("type h <RETURN> for help\n"); 45633965Sjdp for (;;) 45733965Sjdp { 45833965Sjdp printf ("hash_test command: "); 45933965Sjdp gets (answer); 46033965Sjdp command = answer[0]; 46133965Sjdp if (isupper (command)) 46277298Sobrien command = tolower (command); /* Ecch! */ 46333965Sjdp switch (command) 46433965Sjdp { 46533965Sjdp case '#': 46633965Sjdp printf ("old hash table #=%d.\n", number); 46733965Sjdp whattable (); 46833965Sjdp break; 46933965Sjdp case '?': 47033965Sjdp for (pp = hashtable; pp < hashtable + TABLES; pp++) 47133965Sjdp { 47277298Sobrien printf ("address of hash table #%d control block is %xx\n", 47377298Sobrien pp - hashtable, *pp); 47433965Sjdp } 47533965Sjdp break; 47633965Sjdp case 'a': 47760484Sobrien hash_traverse (h, applicatee); 47833965Sjdp break; 47933965Sjdp case 'd': 48060484Sobrien hash_traverse (h, destroy); 48133965Sjdp hash_die (h); 48233965Sjdp break; 48333965Sjdp case 'f': 48433965Sjdp p = hash_find (h, name = what ("symbol")); 48533965Sjdp printf ("value of \"%s\" is \"%s\"\n", name, p ? p : "NOT-PRESENT"); 48633965Sjdp break; 48733965Sjdp case 'h': 48833965Sjdp printf ("# show old, select new default hash table number\n"); 48933965Sjdp printf ("? display all hashtable control block addresses\n"); 49033965Sjdp printf ("a apply a simple display-er to each symbol in table\n"); 49133965Sjdp printf ("d die: destroy hashtable\n"); 49233965Sjdp printf ("f find value of nominated symbol\n"); 49333965Sjdp printf ("h this help\n"); 49433965Sjdp printf ("i insert value into symbol\n"); 49533965Sjdp printf ("j jam value into symbol\n"); 49633965Sjdp printf ("n new hashtable\n"); 49733965Sjdp printf ("r replace a value with another\n"); 49833965Sjdp printf ("s say what %% of table is used\n"); 49933965Sjdp printf ("q exit this program\n"); 50033965Sjdp printf ("x delete a symbol from table, report its value\n"); 50133965Sjdp break; 50233965Sjdp case 'i': 50333965Sjdp p = hash_insert (h, name = what ("symbol"), value = what ("value")); 50433965Sjdp if (p) 50533965Sjdp { 50633965Sjdp printf ("symbol=\"%s\" value=\"%s\" error=%s\n", name, value, 50733965Sjdp p); 50833965Sjdp } 50933965Sjdp break; 51033965Sjdp case 'j': 51133965Sjdp p = hash_jam (h, name = what ("symbol"), value = what ("value")); 51233965Sjdp if (p) 51333965Sjdp { 51433965Sjdp printf ("symbol=\"%s\" value=\"%s\" error=%s\n", name, value, p); 51533965Sjdp } 51633965Sjdp break; 51733965Sjdp case 'n': 51833965Sjdp h = hashtable[number] = (char *) hash_new (); 51933965Sjdp break; 52033965Sjdp case 'q': 52133965Sjdp exit (EXIT_SUCCESS); 52233965Sjdp case 'r': 52333965Sjdp p = hash_replace (h, name = what ("symbol"), value = what ("value")); 52433965Sjdp printf ("old value was \"%s\"\n", p ? p : "{}"); 52533965Sjdp break; 52633965Sjdp case 's': 52733965Sjdp hash_say (h, statbuf, STATBUFSIZE); 52833965Sjdp for (ip = statbuf; ip < statbuf + STATBUFSIZE; ip++) 52933965Sjdp { 53033965Sjdp printf ("%d ", *ip); 53133965Sjdp } 53233965Sjdp printf ("\n"); 53333965Sjdp break; 53433965Sjdp case 'x': 53533965Sjdp p = hash_delete (h, name = what ("symbol")); 53633965Sjdp printf ("old value was \"%s\"\n", p ? p : "{}"); 53733965Sjdp break; 53833965Sjdp default: 53933965Sjdp printf ("I can't understand command \"%c\"\n", command); 54033965Sjdp break; 54133965Sjdp } 54233965Sjdp } 54333965Sjdp} 54433965Sjdp 54533965Sjdpchar * 54633965Sjdpwhat (description) 54733965Sjdp char *description; 54833965Sjdp{ 54933965Sjdp char *retval; 55033965Sjdp char *malloc (); 55133965Sjdp 55233965Sjdp printf (" %s : ", description); 55333965Sjdp gets (answer); 55477298Sobrien /* Will one day clean up answer here. */ 55533965Sjdp retval = malloc (strlen (answer) + 1); 55633965Sjdp if (!retval) 55733965Sjdp { 55833965Sjdp error ("room"); 55933965Sjdp } 56033965Sjdp (void) strcpy (retval, answer); 56133965Sjdp return (retval); 56233965Sjdp} 56333965Sjdp 56433965Sjdpvoid 56533965Sjdpdestroy (string, value) 56633965Sjdp char *string; 56733965Sjdp char *value; 56833965Sjdp{ 56933965Sjdp free (string); 57033965Sjdp free (value); 57133965Sjdp} 57233965Sjdp 57333965Sjdpvoid 57433965Sjdpapplicatee (string, value) 57533965Sjdp char *string; 57633965Sjdp char *value; 57733965Sjdp{ 57833965Sjdp printf ("%.20s-%.20s\n", string, value); 57933965Sjdp} 58033965Sjdp 58177298Sobrien/* Determine number: what hash table to use. 58277298Sobrien Also determine h: points to hash_control. */ 58377298Sobrien 58460484Sobrienvoid 58577298Sobrienwhattable () 58633965Sjdp{ 58733965Sjdp for (;;) 58833965Sjdp { 58933965Sjdp printf (" what hash table (%d:%d) ? ", 0, TABLES - 1); 59033965Sjdp gets (answer); 59133965Sjdp sscanf (answer, "%d", &number); 59233965Sjdp if (number >= 0 && number < TABLES) 59333965Sjdp { 59433965Sjdp h = hashtable[number]; 59533965Sjdp if (!h) 59633965Sjdp { 59733965Sjdp printf ("warning: current hash-table-#%d. has no hash-control\n", number); 59833965Sjdp } 59933965Sjdp return; 60033965Sjdp } 60133965Sjdp else 60233965Sjdp { 60333965Sjdp printf ("invalid hash table number: %d\n", number); 60433965Sjdp } 60533965Sjdp } 60633965Sjdp} 60733965Sjdp 60877298Sobrien#endif /* TEST */ 609