160484Sobrien/* hash.c -- gas hash table code 278828Sobrien Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 3218822Sdim 2000, 2001, 2002, 2003, 2005 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 20218822Sdim Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 21218822Sdim 02110-1301, 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 26130561Sobrien assembler. 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" 3389857Sobrien#include "safe-ctype.h" 3460484Sobrien#include "obstack.h" 3533965Sjdp 3660484Sobrien/* An entry in a hash table. */ 3733965Sjdp 3877298Sobrienstruct hash_entry { 3960484Sobrien /* Next entry for this hash code. */ 4060484Sobrien struct hash_entry *next; 4160484Sobrien /* String being hashed. */ 4260484Sobrien const char *string; 4360484Sobrien /* Hash code. This is the full hash code, not the index into the 4460484Sobrien table. */ 4560484Sobrien unsigned long hash; 4660484Sobrien /* Pointer being stored in the hash table. */ 4760484Sobrien PTR data; 4833965Sjdp}; 4933965Sjdp 5060484Sobrien/* A hash table. */ 5133965Sjdp 5277298Sobrienstruct hash_control { 5360484Sobrien /* The hash array. */ 5460484Sobrien struct hash_entry **table; 5560484Sobrien /* The number of slots in the hash table. */ 5660484Sobrien unsigned int size; 5760484Sobrien /* An obstack for this hash table. */ 5860484Sobrien struct obstack memory; 5933965Sjdp 6060484Sobrien#ifdef HASH_STATISTICS 6160484Sobrien /* Statistics. */ 6260484Sobrien unsigned long lookups; 6360484Sobrien unsigned long hash_compares; 6460484Sobrien unsigned long string_compares; 6560484Sobrien unsigned long insertions; 6660484Sobrien unsigned long replacements; 6760484Sobrien unsigned long deletions; 6860484Sobrien#endif /* HASH_STATISTICS */ 6933965Sjdp}; 7033965Sjdp 71218822Sdim/* The default number of entries to use when creating a hash table. 72218822Sdim Note this value can be reduced to 4051 by using the command line 73218822Sdim switch --reduce-memory-overheads, or set to other values by using 74218822Sdim the --hash-size=<NUMBER> switch. */ 75218822Sdim 76218822Sdimstatic unsigned long gas_hash_table_size = 65537; 77218822Sdim 78218822Sdimvoid 79218822Sdimset_gas_hash_table_size (unsigned long size) 80218822Sdim{ 81218822Sdim gas_hash_table_size = size; 82218822Sdim} 83218822Sdim 84218822Sdim/* FIXME: This function should be amalgmated with bfd/hash.c:bfd_hash_set_default_size(). */ 85218822Sdimstatic unsigned long 86218822Sdimget_gas_hash_table_size (void) 87218822Sdim{ 88218822Sdim /* Extend this prime list if you want more granularity of hash table size. */ 89218822Sdim static const unsigned long hash_size_primes[] = 90218822Sdim { 91218822Sdim 1021, 4051, 8599, 16699, 65537 92218822Sdim }; 93218822Sdim unsigned int index; 94218822Sdim 95218822Sdim /* Work out the best prime number near the hash_size. 96218822Sdim FIXME: This could be a more sophisticated algorithm, 97218822Sdim but is it really worth implementing it ? */ 98218822Sdim for (index = 0; index < ARRAY_SIZE (hash_size_primes) - 1; ++index) 99218822Sdim if (gas_hash_table_size <= hash_size_primes[index]) 100218822Sdim break; 101218822Sdim 102218822Sdim return hash_size_primes[index]; 103218822Sdim} 104218822Sdim 10560484Sobrien/* Create a hash table. This return a control block. */ 10633965Sjdp 10760484Sobrienstruct hash_control * 108130561Sobrienhash_new (void) 10960484Sobrien{ 110218822Sdim unsigned long size; 111218822Sdim unsigned long alloc; 11260484Sobrien struct hash_control *ret; 11333965Sjdp 114218822Sdim size = get_gas_hash_table_size (); 11533965Sjdp 116218822Sdim ret = xmalloc (sizeof *ret); 11760484Sobrien obstack_begin (&ret->memory, chunksize); 11860484Sobrien alloc = size * sizeof (struct hash_entry *); 119218822Sdim ret->table = obstack_alloc (&ret->memory, alloc); 12060484Sobrien memset (ret->table, 0, alloc); 12160484Sobrien ret->size = size; 12233965Sjdp 12360484Sobrien#ifdef HASH_STATISTICS 12460484Sobrien ret->lookups = 0; 12560484Sobrien ret->hash_compares = 0; 12660484Sobrien ret->string_compares = 0; 12760484Sobrien ret->insertions = 0; 12860484Sobrien ret->replacements = 0; 12960484Sobrien ret->deletions = 0; 13060484Sobrien#endif 13133965Sjdp 13277298Sobrien return ret; 13360484Sobrien} 13433965Sjdp 13560484Sobrien/* Delete a hash table, freeing all allocated memory. */ 13633965Sjdp 13760484Sobrienvoid 138130561Sobrienhash_die (struct hash_control *table) 13960484Sobrien{ 14060484Sobrien obstack_free (&table->memory, 0); 14160484Sobrien free (table); 14260484Sobrien} 14333965Sjdp 14460484Sobrien/* Look up a string in a hash table. This returns a pointer to the 14560484Sobrien hash_entry, or NULL if the string is not in the table. If PLIST is 14660484Sobrien not NULL, this sets *PLIST to point to the start of the list which 14760484Sobrien would hold this hash entry. If PHASH is not NULL, this sets *PHASH 14860484Sobrien to the hash code for KEY. 14933965Sjdp 15060484Sobrien Each time we look up a string, we move it to the start of the list 15160484Sobrien for its hash code, to take advantage of referential locality. */ 15233965Sjdp 15360484Sobrienstatic struct hash_entry * 154218822Sdimhash_lookup (struct hash_control *table, const char *key, size_t len, 155130561Sobrien struct hash_entry ***plist, unsigned long *phash) 15660484Sobrien{ 157218822Sdim unsigned long hash; 158218822Sdim size_t n; 159218822Sdim unsigned int c; 16060484Sobrien unsigned int index; 16160484Sobrien struct hash_entry **list; 16260484Sobrien struct hash_entry *p; 16360484Sobrien struct hash_entry *prev; 16433965Sjdp 16560484Sobrien#ifdef HASH_STATISTICS 16660484Sobrien ++table->lookups; 16760484Sobrien#endif 16833965Sjdp 16960484Sobrien hash = 0; 170218822Sdim for (n = 0; n < len; n++) 17160484Sobrien { 172218822Sdim c = key[n]; 17360484Sobrien hash += c + (c << 17); 17460484Sobrien hash ^= hash >> 2; 17560484Sobrien } 17660484Sobrien hash += len + (len << 17); 17760484Sobrien hash ^= hash >> 2; 17833965Sjdp 17960484Sobrien if (phash != NULL) 18060484Sobrien *phash = hash; 18133965Sjdp 18260484Sobrien index = hash % table->size; 18360484Sobrien list = table->table + index; 18433965Sjdp 18560484Sobrien if (plist != NULL) 18660484Sobrien *plist = list; 18733965Sjdp 18860484Sobrien prev = NULL; 18960484Sobrien for (p = *list; p != NULL; p = p->next) 19060484Sobrien { 19160484Sobrien#ifdef HASH_STATISTICS 19260484Sobrien ++table->hash_compares; 19360484Sobrien#endif 19433965Sjdp 19560484Sobrien if (p->hash == hash) 19633965Sjdp { 19760484Sobrien#ifdef HASH_STATISTICS 19860484Sobrien ++table->string_compares; 19933965Sjdp#endif 20033965Sjdp 201218822Sdim if (strncmp (p->string, key, len) == 0 && p->string[len] == '\0') 20260484Sobrien { 20360484Sobrien if (prev != NULL) 20460484Sobrien { 20560484Sobrien prev->next = p->next; 20660484Sobrien p->next = *list; 20760484Sobrien *list = p; 20860484Sobrien } 20960484Sobrien 21060484Sobrien return p; 21160484Sobrien } 21233965Sjdp } 21360484Sobrien 21460484Sobrien prev = p; 21533965Sjdp } 21660484Sobrien 21760484Sobrien return NULL; 21833965Sjdp} 21960484Sobrien 22060484Sobrien/* Insert an entry into a hash table. This returns NULL on success. 22160484Sobrien On error, it returns a printable string indicating the error. It 22260484Sobrien is considered to be an error if the entry already exists in the 22360484Sobrien hash table. */ 22460484Sobrien 22560484Sobrienconst char * 226130561Sobrienhash_insert (struct hash_control *table, const char *key, PTR value) 22733965Sjdp{ 22860484Sobrien struct hash_entry *p; 22960484Sobrien struct hash_entry **list; 23060484Sobrien unsigned long hash; 23133965Sjdp 232218822Sdim p = hash_lookup (table, key, strlen (key), &list, &hash); 23360484Sobrien if (p != NULL) 23460484Sobrien return "exists"; 23560484Sobrien 23660484Sobrien#ifdef HASH_STATISTICS 23760484Sobrien ++table->insertions; 23860484Sobrien#endif 23960484Sobrien 24077298Sobrien p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p)); 24160484Sobrien p->string = key; 24260484Sobrien p->hash = hash; 24360484Sobrien p->data = value; 24460484Sobrien 24560484Sobrien p->next = *list; 24660484Sobrien *list = p; 24760484Sobrien 24860484Sobrien return NULL; 24933965Sjdp} 25033965Sjdp 25160484Sobrien/* Insert or replace an entry in a hash table. This returns NULL on 25260484Sobrien success. On error, it returns a printable string indicating the 25360484Sobrien error. If an entry already exists, its value is replaced. */ 25433965Sjdp 25533965Sjdpconst char * 256130561Sobrienhash_jam (struct hash_control *table, const char *key, PTR value) 25733965Sjdp{ 25860484Sobrien struct hash_entry *p; 25960484Sobrien struct hash_entry **list; 26060484Sobrien unsigned long hash; 26133965Sjdp 262218822Sdim p = hash_lookup (table, key, strlen (key), &list, &hash); 26360484Sobrien if (p != NULL) 26433965Sjdp { 26560484Sobrien#ifdef HASH_STATISTICS 26660484Sobrien ++table->replacements; 26760484Sobrien#endif 26860484Sobrien 26960484Sobrien p->data = value; 27033965Sjdp } 27160484Sobrien else 27233965Sjdp { 27360484Sobrien#ifdef HASH_STATISTICS 27460484Sobrien ++table->insertions; 27560484Sobrien#endif 27660484Sobrien 27777298Sobrien p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p)); 27860484Sobrien p->string = key; 27960484Sobrien p->hash = hash; 28060484Sobrien p->data = value; 28160484Sobrien 28260484Sobrien p->next = *list; 28360484Sobrien *list = p; 28433965Sjdp } 28560484Sobrien 28660484Sobrien return NULL; 28733965Sjdp} 28833965Sjdp 28960484Sobrien/* Replace an existing entry in a hash table. This returns the old 29060484Sobrien value stored for the entry. If the entry is not found in the hash 29160484Sobrien table, this does nothing and returns NULL. */ 29260484Sobrien 29360484SobrienPTR 294130561Sobrienhash_replace (struct hash_control *table, const char *key, PTR value) 29533965Sjdp{ 29660484Sobrien struct hash_entry *p; 29760484Sobrien PTR ret; 29833965Sjdp 299218822Sdim p = hash_lookup (table, key, strlen (key), NULL, NULL); 30060484Sobrien if (p == NULL) 30160484Sobrien return NULL; 30260484Sobrien 30360484Sobrien#ifdef HASH_STATISTICS 30460484Sobrien ++table->replacements; 30533965Sjdp#endif 30633965Sjdp 30760484Sobrien ret = p->data; 30833965Sjdp 30960484Sobrien p->data = value; 31033965Sjdp 31160484Sobrien return ret; 31260484Sobrien} 31333965Sjdp 31460484Sobrien/* Find an entry in a hash table, returning its value. Returns NULL 31560484Sobrien if the entry is not found. */ 31660484Sobrien 31760484SobrienPTR 318130561Sobrienhash_find (struct hash_control *table, const char *key) 31933965Sjdp{ 32060484Sobrien struct hash_entry *p; 32133965Sjdp 322218822Sdim p = hash_lookup (table, key, strlen (key), NULL, NULL); 32360484Sobrien if (p == NULL) 32460484Sobrien return NULL; 32560484Sobrien 32660484Sobrien return p->data; 32733965Sjdp} 32860484Sobrien 329218822Sdim/* As hash_find, but KEY is of length LEN and is not guaranteed to be 330218822Sdim NUL-terminated. */ 331218822Sdim 332218822SdimPTR 333218822Sdimhash_find_n (struct hash_control *table, const char *key, size_t len) 334218822Sdim{ 335218822Sdim struct hash_entry *p; 336218822Sdim 337218822Sdim p = hash_lookup (table, key, len, NULL, NULL); 338218822Sdim if (p == NULL) 339218822Sdim return NULL; 340218822Sdim 341218822Sdim return p->data; 342218822Sdim} 343218822Sdim 34460484Sobrien/* Delete an entry from a hash table. This returns the value stored 34560484Sobrien for that entry, or NULL if there is no such entry. */ 34660484Sobrien 34733965SjdpPTR 348130561Sobrienhash_delete (struct hash_control *table, const char *key) 34933965Sjdp{ 35060484Sobrien struct hash_entry *p; 35160484Sobrien struct hash_entry **list; 35233965Sjdp 353218822Sdim p = hash_lookup (table, key, strlen (key), &list, NULL); 35460484Sobrien if (p == NULL) 35533965Sjdp return NULL; 35660484Sobrien 35760484Sobrien if (p != *list) 35860484Sobrien abort (); 35960484Sobrien 36060484Sobrien#ifdef HASH_STATISTICS 36160484Sobrien ++table->deletions; 36260484Sobrien#endif 36360484Sobrien 36460484Sobrien *list = p->next; 36560484Sobrien 36660484Sobrien /* Note that we never reclaim the memory for this entry. If gas 36760484Sobrien ever starts deleting hash table entries in a big way, this will 36860484Sobrien have to change. */ 36960484Sobrien 37060484Sobrien return p->data; 37133965Sjdp} 37233965Sjdp 37360484Sobrien/* Traverse a hash table. Call the function on every entry in the 37460484Sobrien hash table. */ 37533965Sjdp 37660484Sobrienvoid 377130561Sobrienhash_traverse (struct hash_control *table, 378130561Sobrien void (*pfn) (const char *key, PTR value)) 37933965Sjdp{ 38060484Sobrien unsigned int i; 38133965Sjdp 38260484Sobrien for (i = 0; i < table->size; ++i) 38333965Sjdp { 38460484Sobrien struct hash_entry *p; 38533965Sjdp 38660484Sobrien for (p = table->table[i]; p != NULL; p = p->next) 38760484Sobrien (*pfn) (p->string, p->data); 38833965Sjdp } 38933965Sjdp} 39060484Sobrien 39160484Sobrien/* Print hash table statistics on the specified file. NAME is the 39260484Sobrien name of the hash table, used for printing a header. */ 39360484Sobrien 39433965Sjdpvoid 395130561Sobrienhash_print_statistics (FILE *f ATTRIBUTE_UNUSED, 396130561Sobrien const char *name ATTRIBUTE_UNUSED, 397130561Sobrien struct hash_control *table ATTRIBUTE_UNUSED) 39833965Sjdp{ 39960484Sobrien#ifdef HASH_STATISTICS 40060484Sobrien unsigned int i; 40160484Sobrien unsigned long total; 40260484Sobrien unsigned long empty; 40333965Sjdp 40460484Sobrien fprintf (f, "%s hash statistics:\n", name); 40560484Sobrien fprintf (f, "\t%lu lookups\n", table->lookups); 40660484Sobrien fprintf (f, "\t%lu hash comparisons\n", table->hash_compares); 40760484Sobrien fprintf (f, "\t%lu string comparisons\n", table->string_compares); 40860484Sobrien fprintf (f, "\t%lu insertions\n", table->insertions); 40960484Sobrien fprintf (f, "\t%lu replacements\n", table->replacements); 41060484Sobrien fprintf (f, "\t%lu deletions\n", table->deletions); 41133965Sjdp 41260484Sobrien total = 0; 41360484Sobrien empty = 0; 41460484Sobrien for (i = 0; i < table->size; ++i) 41560484Sobrien { 41660484Sobrien struct hash_entry *p; 41733965Sjdp 41860484Sobrien if (table->table[i] == NULL) 41960484Sobrien ++empty; 42060484Sobrien else 42160484Sobrien { 42260484Sobrien for (p = table->table[i]; p != NULL; p = p->next) 42360484Sobrien ++total; 42460484Sobrien } 42560484Sobrien } 42633965Sjdp 42760484Sobrien fprintf (f, "\t%g average chain length\n", (double) total / table->size); 42860484Sobrien fprintf (f, "\t%lu empty slots\n", empty); 42960484Sobrien#endif 43033965Sjdp} 43133965Sjdp 43233965Sjdp#ifdef TEST 43333965Sjdp 43460484Sobrien/* This test program is left over from the old hash table code. */ 43560484Sobrien 43677298Sobrien/* Number of hash tables to maintain (at once) in any testing. */ 43777298Sobrien#define TABLES (6) 43833965Sjdp 43977298Sobrien/* We can have 12 statistics. */ 44077298Sobrien#define STATBUFSIZE (12) 44177298Sobrien 44277298Sobrien/* Display statistics here. */ 44377298Sobrienint statbuf[STATBUFSIZE]; 44477298Sobrien 44577298Sobrien/* Human farts here. */ 44677298Sobrienchar answer[100]; 44777298Sobrien 44877298Sobrien/* We test many hash tables at once. */ 44977298Sobrienchar *hashtable[TABLES]; 45077298Sobrien 451130561Sobrien/* Points to current hash_control. */ 45277298Sobrienchar *h; 45333965Sjdpchar **pp; 45433965Sjdpchar *p; 45533965Sjdpchar *name; 45633965Sjdpchar *value; 45733965Sjdpint size; 45833965Sjdpint used; 45933965Sjdpchar command; 46033965Sjdp 46177298Sobrien/* Number 0:TABLES-1 of current hashed symbol table. */ 46277298Sobrienint number; 46377298Sobrien 46460484Sobrienint 46533965Sjdpmain () 46633965Sjdp{ 46733965Sjdp void applicatee (); 46833965Sjdp void destroy (); 46933965Sjdp char *what (); 47033965Sjdp int *ip; 47133965Sjdp 47233965Sjdp number = 0; 47333965Sjdp h = 0; 47433965Sjdp printf ("type h <RETURN> for help\n"); 47533965Sjdp for (;;) 47633965Sjdp { 47733965Sjdp printf ("hash_test command: "); 47833965Sjdp gets (answer); 47933965Sjdp command = answer[0]; 48089857Sobrien command = TOLOWER (command); /* Ecch! */ 48133965Sjdp switch (command) 48233965Sjdp { 48333965Sjdp case '#': 48433965Sjdp printf ("old hash table #=%d.\n", number); 48533965Sjdp whattable (); 48633965Sjdp break; 48733965Sjdp case '?': 48833965Sjdp for (pp = hashtable; pp < hashtable + TABLES; pp++) 48933965Sjdp { 49077298Sobrien printf ("address of hash table #%d control block is %xx\n", 49177298Sobrien pp - hashtable, *pp); 49233965Sjdp } 49333965Sjdp break; 49433965Sjdp case 'a': 49560484Sobrien hash_traverse (h, applicatee); 49633965Sjdp break; 49733965Sjdp case 'd': 49860484Sobrien hash_traverse (h, destroy); 49933965Sjdp hash_die (h); 50033965Sjdp break; 50133965Sjdp case 'f': 50233965Sjdp p = hash_find (h, name = what ("symbol")); 50333965Sjdp printf ("value of \"%s\" is \"%s\"\n", name, p ? p : "NOT-PRESENT"); 50433965Sjdp break; 50533965Sjdp case 'h': 50633965Sjdp printf ("# show old, select new default hash table number\n"); 50733965Sjdp printf ("? display all hashtable control block addresses\n"); 50833965Sjdp printf ("a apply a simple display-er to each symbol in table\n"); 50933965Sjdp printf ("d die: destroy hashtable\n"); 51033965Sjdp printf ("f find value of nominated symbol\n"); 51133965Sjdp printf ("h this help\n"); 51233965Sjdp printf ("i insert value into symbol\n"); 51333965Sjdp printf ("j jam value into symbol\n"); 51433965Sjdp printf ("n new hashtable\n"); 51533965Sjdp printf ("r replace a value with another\n"); 51633965Sjdp printf ("s say what %% of table is used\n"); 51733965Sjdp printf ("q exit this program\n"); 51833965Sjdp printf ("x delete a symbol from table, report its value\n"); 51933965Sjdp break; 52033965Sjdp case 'i': 52133965Sjdp p = hash_insert (h, name = what ("symbol"), value = what ("value")); 52233965Sjdp if (p) 52333965Sjdp { 52433965Sjdp printf ("symbol=\"%s\" value=\"%s\" error=%s\n", name, value, 52533965Sjdp p); 52633965Sjdp } 52733965Sjdp break; 52833965Sjdp case 'j': 52933965Sjdp p = hash_jam (h, name = what ("symbol"), value = what ("value")); 53033965Sjdp if (p) 53133965Sjdp { 53233965Sjdp printf ("symbol=\"%s\" value=\"%s\" error=%s\n", name, value, p); 53333965Sjdp } 53433965Sjdp break; 53533965Sjdp case 'n': 53633965Sjdp h = hashtable[number] = (char *) hash_new (); 53733965Sjdp break; 53833965Sjdp case 'q': 53933965Sjdp exit (EXIT_SUCCESS); 54033965Sjdp case 'r': 54133965Sjdp p = hash_replace (h, name = what ("symbol"), value = what ("value")); 54233965Sjdp printf ("old value was \"%s\"\n", p ? p : "{}"); 54333965Sjdp break; 54433965Sjdp case 's': 54533965Sjdp hash_say (h, statbuf, STATBUFSIZE); 54633965Sjdp for (ip = statbuf; ip < statbuf + STATBUFSIZE; ip++) 54733965Sjdp { 54833965Sjdp printf ("%d ", *ip); 54933965Sjdp } 55033965Sjdp printf ("\n"); 55133965Sjdp break; 55233965Sjdp case 'x': 55333965Sjdp p = hash_delete (h, name = what ("symbol")); 55433965Sjdp printf ("old value was \"%s\"\n", p ? p : "{}"); 55533965Sjdp break; 55633965Sjdp default: 55733965Sjdp printf ("I can't understand command \"%c\"\n", command); 55833965Sjdp break; 55933965Sjdp } 56033965Sjdp } 56133965Sjdp} 56233965Sjdp 56333965Sjdpchar * 56433965Sjdpwhat (description) 56533965Sjdp char *description; 56633965Sjdp{ 56733965Sjdp printf (" %s : ", description); 56833965Sjdp gets (answer); 569104834Sobrien return xstrdup (answer); 57033965Sjdp} 57133965Sjdp 57233965Sjdpvoid 57333965Sjdpdestroy (string, value) 57433965Sjdp char *string; 57533965Sjdp char *value; 57633965Sjdp{ 57733965Sjdp free (string); 57833965Sjdp free (value); 57933965Sjdp} 58033965Sjdp 58133965Sjdpvoid 58233965Sjdpapplicatee (string, value) 58333965Sjdp char *string; 58433965Sjdp char *value; 58533965Sjdp{ 58633965Sjdp printf ("%.20s-%.20s\n", string, value); 58733965Sjdp} 58833965Sjdp 58977298Sobrien/* Determine number: what hash table to use. 59077298Sobrien Also determine h: points to hash_control. */ 59177298Sobrien 59260484Sobrienvoid 59377298Sobrienwhattable () 59433965Sjdp{ 59533965Sjdp for (;;) 59633965Sjdp { 59733965Sjdp printf (" what hash table (%d:%d) ? ", 0, TABLES - 1); 59833965Sjdp gets (answer); 59933965Sjdp sscanf (answer, "%d", &number); 60033965Sjdp if (number >= 0 && number < TABLES) 60133965Sjdp { 60233965Sjdp h = hashtable[number]; 60333965Sjdp if (!h) 60433965Sjdp { 60533965Sjdp printf ("warning: current hash-table-#%d. has no hash-control\n", number); 60633965Sjdp } 60733965Sjdp return; 60833965Sjdp } 60933965Sjdp else 61033965Sjdp { 61133965Sjdp printf ("invalid hash table number: %d\n", number); 61233965Sjdp } 61333965Sjdp } 61433965Sjdp} 61533965Sjdp 61677298Sobrien#endif /* TEST */ 617