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