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