hash.c revision 77298
160484Sobrien/* hash.c -- gas hash table code 277298Sobrien Copyright (C) 1987, 90, 91, 92, 93, 94, 95, 96, 98, 99, 2000 333965Sjdp Free Software Foundation, Inc. 433965Sjdp 533965Sjdp This file is part of GAS, the GNU Assembler. 633965Sjdp 733965Sjdp GAS is free software; you can redistribute it and/or modify 833965Sjdp it under the terms of the GNU General Public License as published by 933965Sjdp the Free Software Foundation; either version 2, or (at your option) 1033965Sjdp any later version. 1133965Sjdp 1233965Sjdp GAS is distributed in the hope that it will be useful, 1333965Sjdp but WITHOUT ANY WARRANTY; without even the implied warranty of 1433965Sjdp MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1533965Sjdp GNU General Public License for more details. 1633965Sjdp 1733965Sjdp You should have received a copy of the GNU General Public License 1860484Sobrien along with GAS; see the file COPYING. If not, write to the Free 1960484Sobrien Software Foundation, 59 Temple Place - Suite 330, Boston, MA 2060484Sobrien 02111-1307, USA. */ 2133965Sjdp 2260484Sobrien/* This version of the hash table code is a wholescale replacement of 2360484Sobrien the old hash table code, which was fairly bad. This is based on 2460484Sobrien the hash table code in BFD, but optimized slightly for the 2560484Sobrien asssembler. The assembler does not need to derive structures that 2660484Sobrien are stored in the hash table. Instead, it always stores a pointer. 2760484Sobrien The assembler uses the hash table mostly to store symbols, and we 2860484Sobrien don't need to confuse the symbol structure with a hash table 2960484Sobrien structure. */ 3033965Sjdp 3133965Sjdp#include "as.h" 3260484Sobrien#include "obstack.h" 3333965Sjdp 3460484Sobrien/* The default number of entries to use when creating a hash table. */ 3533965Sjdp 3660484Sobrien#define DEFAULT_SIZE (4051) 3733965Sjdp 3860484Sobrien/* An entry in a hash table. */ 3933965Sjdp 4077298Sobrienstruct hash_entry { 4160484Sobrien /* Next entry for this hash code. */ 4260484Sobrien struct hash_entry *next; 4360484Sobrien /* String being hashed. */ 4460484Sobrien const char *string; 4560484Sobrien /* Hash code. This is the full hash code, not the index into the 4660484Sobrien table. */ 4760484Sobrien unsigned long hash; 4860484Sobrien /* Pointer being stored in the hash table. */ 4960484Sobrien PTR data; 5033965Sjdp}; 5133965Sjdp 5260484Sobrien/* A hash table. */ 5333965Sjdp 5477298Sobrienstruct hash_control { 5560484Sobrien /* The hash array. */ 5660484Sobrien struct hash_entry **table; 5760484Sobrien /* The number of slots in the hash table. */ 5860484Sobrien unsigned int size; 5960484Sobrien /* An obstack for this hash table. */ 6060484Sobrien struct obstack memory; 6133965Sjdp 6260484Sobrien#ifdef HASH_STATISTICS 6360484Sobrien /* Statistics. */ 6460484Sobrien unsigned long lookups; 6560484Sobrien unsigned long hash_compares; 6660484Sobrien unsigned long string_compares; 6760484Sobrien unsigned long insertions; 6860484Sobrien unsigned long replacements; 6960484Sobrien unsigned long deletions; 7060484Sobrien#endif /* HASH_STATISTICS */ 7133965Sjdp}; 7233965Sjdp 7360484Sobrien/* Create a hash table. This return a control block. */ 7433965Sjdp 7560484Sobrienstruct hash_control * 7660484Sobrienhash_new () 7760484Sobrien{ 7860484Sobrien unsigned int size; 7960484Sobrien struct hash_control *ret; 8060484Sobrien unsigned int alloc; 8133965Sjdp 8260484Sobrien size = DEFAULT_SIZE; 8333965Sjdp 8460484Sobrien ret = (struct hash_control *) xmalloc (sizeof *ret); 8560484Sobrien obstack_begin (&ret->memory, chunksize); 8660484Sobrien alloc = size * sizeof (struct hash_entry *); 8760484Sobrien ret->table = (struct hash_entry **) obstack_alloc (&ret->memory, alloc); 8860484Sobrien memset (ret->table, 0, alloc); 8960484Sobrien ret->size = size; 9033965Sjdp 9160484Sobrien#ifdef HASH_STATISTICS 9260484Sobrien ret->lookups = 0; 9360484Sobrien ret->hash_compares = 0; 9460484Sobrien ret->string_compares = 0; 9560484Sobrien ret->insertions = 0; 9660484Sobrien ret->replacements = 0; 9760484Sobrien ret->deletions = 0; 9860484Sobrien#endif 9933965Sjdp 10077298Sobrien return ret; 10160484Sobrien} 10233965Sjdp 10360484Sobrien/* Delete a hash table, freeing all allocated memory. */ 10433965Sjdp 10560484Sobrienvoid 10660484Sobrienhash_die (table) 10760484Sobrien struct hash_control *table; 10860484Sobrien{ 10960484Sobrien obstack_free (&table->memory, 0); 11060484Sobrien free (table); 11160484Sobrien} 11233965Sjdp 11360484Sobrien/* Look up a string in a hash table. This returns a pointer to the 11460484Sobrien hash_entry, or NULL if the string is not in the table. If PLIST is 11560484Sobrien not NULL, this sets *PLIST to point to the start of the list which 11660484Sobrien would hold this hash entry. If PHASH is not NULL, this sets *PHASH 11760484Sobrien to the hash code for KEY. 11833965Sjdp 11960484Sobrien Each time we look up a string, we move it to the start of the list 12060484Sobrien for its hash code, to take advantage of referential locality. */ 12133965Sjdp 12260484Sobrienstatic struct hash_entry *hash_lookup PARAMS ((struct hash_control *, 12360484Sobrien const char *, 12460484Sobrien struct hash_entry ***, 12560484Sobrien unsigned long *)); 12633965Sjdp 12760484Sobrienstatic struct hash_entry * 12860484Sobrienhash_lookup (table, key, plist, phash) 12960484Sobrien struct hash_control *table; 13060484Sobrien const char *key; 13160484Sobrien struct hash_entry ***plist; 13260484Sobrien unsigned long *phash; 13360484Sobrien{ 13460484Sobrien register unsigned long hash; 13560484Sobrien unsigned int len; 13660484Sobrien register const unsigned char *s; 13760484Sobrien register unsigned int c; 13860484Sobrien unsigned int index; 13960484Sobrien struct hash_entry **list; 14060484Sobrien struct hash_entry *p; 14160484Sobrien struct hash_entry *prev; 14233965Sjdp 14360484Sobrien#ifdef HASH_STATISTICS 14460484Sobrien ++table->lookups; 14560484Sobrien#endif 14633965Sjdp 14760484Sobrien hash = 0; 14860484Sobrien len = 0; 14960484Sobrien s = (const unsigned char *) key; 15060484Sobrien while ((c = *s++) != '\0') 15160484Sobrien { 15260484Sobrien hash += c + (c << 17); 15360484Sobrien hash ^= hash >> 2; 15460484Sobrien ++len; 15560484Sobrien } 15660484Sobrien hash += len + (len << 17); 15760484Sobrien hash ^= hash >> 2; 15833965Sjdp 15960484Sobrien if (phash != NULL) 16060484Sobrien *phash = hash; 16133965Sjdp 16260484Sobrien index = hash % table->size; 16360484Sobrien list = table->table + index; 16433965Sjdp 16560484Sobrien if (plist != NULL) 16660484Sobrien *plist = list; 16733965Sjdp 16860484Sobrien prev = NULL; 16960484Sobrien for (p = *list; p != NULL; p = p->next) 17060484Sobrien { 17160484Sobrien#ifdef HASH_STATISTICS 17260484Sobrien ++table->hash_compares; 17360484Sobrien#endif 17433965Sjdp 17560484Sobrien if (p->hash == hash) 17633965Sjdp { 17760484Sobrien#ifdef HASH_STATISTICS 17860484Sobrien ++table->string_compares; 17933965Sjdp#endif 18033965Sjdp 18160484Sobrien if (strcmp (p->string, key) == 0) 18260484Sobrien { 18360484Sobrien if (prev != NULL) 18460484Sobrien { 18560484Sobrien prev->next = p->next; 18660484Sobrien p->next = *list; 18760484Sobrien *list = p; 18860484Sobrien } 18960484Sobrien 19060484Sobrien return p; 19160484Sobrien } 19233965Sjdp } 19360484Sobrien 19460484Sobrien prev = p; 19533965Sjdp } 19660484Sobrien 19760484Sobrien return NULL; 19833965Sjdp} 19960484Sobrien 20060484Sobrien/* Insert an entry into a hash table. This returns NULL on success. 20160484Sobrien On error, it returns a printable string indicating the error. It 20260484Sobrien is considered to be an error if the entry already exists in the 20360484Sobrien hash table. */ 20460484Sobrien 20560484Sobrienconst char * 20660484Sobrienhash_insert (table, key, value) 20760484Sobrien struct hash_control *table; 20860484Sobrien const char *key; 20933965Sjdp PTR value; 21033965Sjdp{ 21160484Sobrien struct hash_entry *p; 21260484Sobrien struct hash_entry **list; 21360484Sobrien unsigned long hash; 21433965Sjdp 21560484Sobrien p = hash_lookup (table, key, &list, &hash); 21660484Sobrien if (p != NULL) 21760484Sobrien return "exists"; 21860484Sobrien 21960484Sobrien#ifdef HASH_STATISTICS 22060484Sobrien ++table->insertions; 22160484Sobrien#endif 22260484Sobrien 22377298Sobrien p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p)); 22460484Sobrien p->string = key; 22560484Sobrien p->hash = hash; 22660484Sobrien p->data = value; 22760484Sobrien 22860484Sobrien p->next = *list; 22960484Sobrien *list = p; 23060484Sobrien 23160484Sobrien return NULL; 23233965Sjdp} 23333965Sjdp 23460484Sobrien/* Insert or replace an entry in a hash table. This returns NULL on 23560484Sobrien success. On error, it returns a printable string indicating the 23660484Sobrien error. If an entry already exists, its value is replaced. */ 23733965Sjdp 23833965Sjdpconst char * 23960484Sobrienhash_jam (table, key, value) 24060484Sobrien struct hash_control *table; 24160484Sobrien const char *key; 24233965Sjdp PTR value; 24333965Sjdp{ 24460484Sobrien struct hash_entry *p; 24560484Sobrien struct hash_entry **list; 24660484Sobrien unsigned long hash; 24733965Sjdp 24860484Sobrien p = hash_lookup (table, key, &list, &hash); 24960484Sobrien if (p != NULL) 25033965Sjdp { 25160484Sobrien#ifdef HASH_STATISTICS 25260484Sobrien ++table->replacements; 25360484Sobrien#endif 25460484Sobrien 25560484Sobrien p->data = value; 25633965Sjdp } 25760484Sobrien else 25833965Sjdp { 25960484Sobrien#ifdef HASH_STATISTICS 26060484Sobrien ++table->insertions; 26160484Sobrien#endif 26260484Sobrien 26377298Sobrien p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p)); 26460484Sobrien p->string = key; 26560484Sobrien p->hash = hash; 26660484Sobrien p->data = value; 26760484Sobrien 26860484Sobrien p->next = *list; 26960484Sobrien *list = p; 27033965Sjdp } 27160484Sobrien 27260484Sobrien return NULL; 27333965Sjdp} 27433965Sjdp 27560484Sobrien/* Replace an existing entry in a hash table. This returns the old 27660484Sobrien value stored for the entry. If the entry is not found in the hash 27760484Sobrien table, this does nothing and returns NULL. */ 27860484Sobrien 27960484SobrienPTR 28060484Sobrienhash_replace (table, key, value) 28160484Sobrien struct hash_control *table; 28260484Sobrien const char *key; 28360484Sobrien PTR value; 28433965Sjdp{ 28560484Sobrien struct hash_entry *p; 28660484Sobrien PTR ret; 28733965Sjdp 28860484Sobrien p = hash_lookup (table, key, NULL, NULL); 28960484Sobrien if (p == NULL) 29060484Sobrien return NULL; 29160484Sobrien 29260484Sobrien#ifdef HASH_STATISTICS 29360484Sobrien ++table->replacements; 29433965Sjdp#endif 29533965Sjdp 29660484Sobrien ret = p->data; 29733965Sjdp 29860484Sobrien p->data = value; 29933965Sjdp 30060484Sobrien return ret; 30160484Sobrien} 30233965Sjdp 30360484Sobrien/* Find an entry in a hash table, returning its value. Returns NULL 30460484Sobrien if the entry is not found. */ 30560484Sobrien 30660484SobrienPTR 30760484Sobrienhash_find (table, key) 30860484Sobrien struct hash_control *table; 30960484Sobrien const char *key; 31033965Sjdp{ 31160484Sobrien struct hash_entry *p; 31233965Sjdp 31360484Sobrien p = hash_lookup (table, key, NULL, NULL); 31460484Sobrien if (p == NULL) 31560484Sobrien return NULL; 31660484Sobrien 31760484Sobrien return p->data; 31833965Sjdp} 31960484Sobrien 32060484Sobrien/* Delete an entry from a hash table. This returns the value stored 32160484Sobrien for that entry, or NULL if there is no such entry. */ 32260484Sobrien 32333965SjdpPTR 32460484Sobrienhash_delete (table, key) 32560484Sobrien struct hash_control *table; 32660484Sobrien const char *key; 32733965Sjdp{ 32860484Sobrien struct hash_entry *p; 32960484Sobrien struct hash_entry **list; 33033965Sjdp 33160484Sobrien p = hash_lookup (table, key, &list, NULL); 33260484Sobrien if (p == NULL) 33333965Sjdp return NULL; 33460484Sobrien 33560484Sobrien if (p != *list) 33660484Sobrien abort (); 33760484Sobrien 33860484Sobrien#ifdef HASH_STATISTICS 33960484Sobrien ++table->deletions; 34060484Sobrien#endif 34160484Sobrien 34260484Sobrien *list = p->next; 34360484Sobrien 34460484Sobrien /* Note that we never reclaim the memory for this entry. If gas 34560484Sobrien ever starts deleting hash table entries in a big way, this will 34660484Sobrien have to change. */ 34760484Sobrien 34860484Sobrien return p->data; 34933965Sjdp} 35033965Sjdp 35160484Sobrien/* Traverse a hash table. Call the function on every entry in the 35260484Sobrien hash table. */ 35333965Sjdp 35460484Sobrienvoid 35560484Sobrienhash_traverse (table, pfn) 35660484Sobrien struct hash_control *table; 35760484Sobrien void (*pfn) PARAMS ((const char *key, PTR value)); 35833965Sjdp{ 35960484Sobrien unsigned int i; 36033965Sjdp 36160484Sobrien for (i = 0; i < table->size; ++i) 36233965Sjdp { 36360484Sobrien struct hash_entry *p; 36433965Sjdp 36560484Sobrien for (p = table->table[i]; p != NULL; p = p->next) 36660484Sobrien (*pfn) (p->string, p->data); 36733965Sjdp } 36833965Sjdp} 36960484Sobrien 37060484Sobrien/* Print hash table statistics on the specified file. NAME is the 37160484Sobrien name of the hash table, used for printing a header. */ 37260484Sobrien 37333965Sjdpvoid 37460484Sobrienhash_print_statistics (f, name, table) 37560484Sobrien FILE *f ATTRIBUTE_UNUSED; 37660484Sobrien const char *name ATTRIBUTE_UNUSED; 37760484Sobrien struct hash_control *table ATTRIBUTE_UNUSED; 37833965Sjdp{ 37960484Sobrien#ifdef HASH_STATISTICS 38060484Sobrien unsigned int i; 38160484Sobrien unsigned long total; 38260484Sobrien unsigned long empty; 38333965Sjdp 38460484Sobrien fprintf (f, "%s hash statistics:\n", name); 38560484Sobrien fprintf (f, "\t%lu lookups\n", table->lookups); 38660484Sobrien fprintf (f, "\t%lu hash comparisons\n", table->hash_compares); 38760484Sobrien fprintf (f, "\t%lu string comparisons\n", table->string_compares); 38860484Sobrien fprintf (f, "\t%lu insertions\n", table->insertions); 38960484Sobrien fprintf (f, "\t%lu replacements\n", table->replacements); 39060484Sobrien fprintf (f, "\t%lu deletions\n", table->deletions); 39133965Sjdp 39260484Sobrien total = 0; 39360484Sobrien empty = 0; 39460484Sobrien for (i = 0; i < table->size; ++i) 39560484Sobrien { 39660484Sobrien struct hash_entry *p; 39733965Sjdp 39860484Sobrien if (table->table[i] == NULL) 39960484Sobrien ++empty; 40060484Sobrien else 40160484Sobrien { 40260484Sobrien for (p = table->table[i]; p != NULL; p = p->next) 40360484Sobrien ++total; 40460484Sobrien } 40560484Sobrien } 40633965Sjdp 40760484Sobrien fprintf (f, "\t%g average chain length\n", (double) total / table->size); 40860484Sobrien fprintf (f, "\t%lu empty slots\n", empty); 40960484Sobrien#endif 41033965Sjdp} 41133965Sjdp 41233965Sjdp#ifdef TEST 41333965Sjdp 41460484Sobrien/* This test program is left over from the old hash table code. */ 41560484Sobrien 41677298Sobrien/* Number of hash tables to maintain (at once) in any testing. */ 41777298Sobrien#define TABLES (6) 41833965Sjdp 41977298Sobrien/* We can have 12 statistics. */ 42077298Sobrien#define STATBUFSIZE (12) 42177298Sobrien 42277298Sobrien/* Display statistics here. */ 42377298Sobrienint statbuf[STATBUFSIZE]; 42477298Sobrien 42577298Sobrien/* Human farts here. */ 42677298Sobrienchar answer[100]; 42777298Sobrien 42877298Sobrien/* We test many hash tables at once. */ 42977298Sobrienchar *hashtable[TABLES]; 43077298Sobrien 43177298Sobrien/* Points to curent hash_control. */ 43277298Sobrienchar *h; 43333965Sjdpchar **pp; 43433965Sjdpchar *p; 43533965Sjdpchar *name; 43633965Sjdpchar *value; 43733965Sjdpint size; 43833965Sjdpint used; 43933965Sjdpchar command; 44033965Sjdp 44177298Sobrien/* Number 0:TABLES-1 of current hashed symbol table. */ 44277298Sobrienint number; 44377298Sobrien 44460484Sobrienint 44533965Sjdpmain () 44633965Sjdp{ 44733965Sjdp void applicatee (); 44833965Sjdp void destroy (); 44933965Sjdp char *what (); 45033965Sjdp int *ip; 45133965Sjdp 45233965Sjdp number = 0; 45333965Sjdp h = 0; 45433965Sjdp printf ("type h <RETURN> for help\n"); 45533965Sjdp for (;;) 45633965Sjdp { 45733965Sjdp printf ("hash_test command: "); 45833965Sjdp gets (answer); 45933965Sjdp command = answer[0]; 46033965Sjdp if (isupper (command)) 46177298Sobrien command = tolower (command); /* Ecch! */ 46233965Sjdp switch (command) 46333965Sjdp { 46433965Sjdp case '#': 46533965Sjdp printf ("old hash table #=%d.\n", number); 46633965Sjdp whattable (); 46733965Sjdp break; 46833965Sjdp case '?': 46933965Sjdp for (pp = hashtable; pp < hashtable + TABLES; pp++) 47033965Sjdp { 47177298Sobrien printf ("address of hash table #%d control block is %xx\n", 47277298Sobrien pp - hashtable, *pp); 47333965Sjdp } 47433965Sjdp break; 47533965Sjdp case 'a': 47660484Sobrien hash_traverse (h, applicatee); 47733965Sjdp break; 47833965Sjdp case 'd': 47960484Sobrien hash_traverse (h, destroy); 48033965Sjdp hash_die (h); 48133965Sjdp break; 48233965Sjdp case 'f': 48333965Sjdp p = hash_find (h, name = what ("symbol")); 48433965Sjdp printf ("value of \"%s\" is \"%s\"\n", name, p ? p : "NOT-PRESENT"); 48533965Sjdp break; 48633965Sjdp case 'h': 48733965Sjdp printf ("# show old, select new default hash table number\n"); 48833965Sjdp printf ("? display all hashtable control block addresses\n"); 48933965Sjdp printf ("a apply a simple display-er to each symbol in table\n"); 49033965Sjdp printf ("d die: destroy hashtable\n"); 49133965Sjdp printf ("f find value of nominated symbol\n"); 49233965Sjdp printf ("h this help\n"); 49333965Sjdp printf ("i insert value into symbol\n"); 49433965Sjdp printf ("j jam value into symbol\n"); 49533965Sjdp printf ("n new hashtable\n"); 49633965Sjdp printf ("r replace a value with another\n"); 49733965Sjdp printf ("s say what %% of table is used\n"); 49833965Sjdp printf ("q exit this program\n"); 49933965Sjdp printf ("x delete a symbol from table, report its value\n"); 50033965Sjdp break; 50133965Sjdp case 'i': 50233965Sjdp p = hash_insert (h, name = what ("symbol"), value = what ("value")); 50333965Sjdp if (p) 50433965Sjdp { 50533965Sjdp printf ("symbol=\"%s\" value=\"%s\" error=%s\n", name, value, 50633965Sjdp p); 50733965Sjdp } 50833965Sjdp break; 50933965Sjdp case 'j': 51033965Sjdp p = hash_jam (h, name = what ("symbol"), value = what ("value")); 51133965Sjdp if (p) 51233965Sjdp { 51333965Sjdp printf ("symbol=\"%s\" value=\"%s\" error=%s\n", name, value, p); 51433965Sjdp } 51533965Sjdp break; 51633965Sjdp case 'n': 51733965Sjdp h = hashtable[number] = (char *) hash_new (); 51833965Sjdp break; 51933965Sjdp case 'q': 52033965Sjdp exit (EXIT_SUCCESS); 52133965Sjdp case 'r': 52233965Sjdp p = hash_replace (h, name = what ("symbol"), value = what ("value")); 52333965Sjdp printf ("old value was \"%s\"\n", p ? p : "{}"); 52433965Sjdp break; 52533965Sjdp case 's': 52633965Sjdp hash_say (h, statbuf, STATBUFSIZE); 52733965Sjdp for (ip = statbuf; ip < statbuf + STATBUFSIZE; ip++) 52833965Sjdp { 52933965Sjdp printf ("%d ", *ip); 53033965Sjdp } 53133965Sjdp printf ("\n"); 53233965Sjdp break; 53333965Sjdp case 'x': 53433965Sjdp p = hash_delete (h, name = what ("symbol")); 53533965Sjdp printf ("old value was \"%s\"\n", p ? p : "{}"); 53633965Sjdp break; 53733965Sjdp default: 53833965Sjdp printf ("I can't understand command \"%c\"\n", command); 53933965Sjdp break; 54033965Sjdp } 54133965Sjdp } 54233965Sjdp} 54333965Sjdp 54433965Sjdpchar * 54533965Sjdpwhat (description) 54633965Sjdp char *description; 54733965Sjdp{ 54833965Sjdp char *retval; 54933965Sjdp char *malloc (); 55033965Sjdp 55133965Sjdp printf (" %s : ", description); 55233965Sjdp gets (answer); 55377298Sobrien /* Will one day clean up answer here. */ 55433965Sjdp retval = malloc (strlen (answer) + 1); 55533965Sjdp if (!retval) 55633965Sjdp { 55733965Sjdp error ("room"); 55833965Sjdp } 55933965Sjdp (void) strcpy (retval, answer); 56033965Sjdp return (retval); 56133965Sjdp} 56233965Sjdp 56333965Sjdpvoid 56433965Sjdpdestroy (string, value) 56533965Sjdp char *string; 56633965Sjdp char *value; 56733965Sjdp{ 56833965Sjdp free (string); 56933965Sjdp free (value); 57033965Sjdp} 57133965Sjdp 57233965Sjdpvoid 57333965Sjdpapplicatee (string, value) 57433965Sjdp char *string; 57533965Sjdp char *value; 57633965Sjdp{ 57733965Sjdp printf ("%.20s-%.20s\n", string, value); 57833965Sjdp} 57933965Sjdp 58077298Sobrien/* Determine number: what hash table to use. 58177298Sobrien Also determine h: points to hash_control. */ 58277298Sobrien 58360484Sobrienvoid 58477298Sobrienwhattable () 58533965Sjdp{ 58633965Sjdp for (;;) 58733965Sjdp { 58833965Sjdp printf (" what hash table (%d:%d) ? ", 0, TABLES - 1); 58933965Sjdp gets (answer); 59033965Sjdp sscanf (answer, "%d", &number); 59133965Sjdp if (number >= 0 && number < TABLES) 59233965Sjdp { 59333965Sjdp h = hashtable[number]; 59433965Sjdp if (!h) 59533965Sjdp { 59633965Sjdp printf ("warning: current hash-table-#%d. has no hash-control\n", number); 59733965Sjdp } 59833965Sjdp return; 59933965Sjdp } 60033965Sjdp else 60133965Sjdp { 60233965Sjdp printf ("invalid hash table number: %d\n", number); 60333965Sjdp } 60433965Sjdp } 60533965Sjdp} 60633965Sjdp 60777298Sobrien#endif /* TEST */ 608