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