xref: /illumos-gate/usr/src/lib/libtecla/common/hash.c (revision fc910014e8a32a65612105835a10995f2c13d942)
1 /*
2  * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
3  *
4  * All rights reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, and/or sell copies of the Software, and to permit persons
11  * to whom the Software is furnished to do so, provided that the above
12  * copyright notice(s) and this permission notice appear in all copies of
13  * the Software and that both the above copyright notice(s) and this
14  * permission notice appear in supporting documentation.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
19  * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20  * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
21  * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
22  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
23  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
24  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25  *
26  * Except as contained in this notice, the name of a copyright holder
27  * shall not be used in advertising or otherwise to promote the sale, use
28  * or other dealings in this Software without prior written authorization
29  * of the copyright holder.
30  */
31 
32 /*
33  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
34  * Use is subject to license terms.
35  */
36 
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <errno.h>
42 
43 #include "hash.h"
44 #include "strngmem.h"
45 #include "freelist.h"
46 
47 /*
48  * The following container object contains free-lists to be used
49  * for allocation of HashTable containers and nodes.
50  */
51 struct HashMemory {
52   FreeList *hash_memory;    /* HashTable free-list */
53   FreeList *node_memory;    /* HashNode free-list */
54   StringMem *string_memory; /* Memory used to allocate hash strings */
55 };
56 
57 /*
58  * Define a hash symbol-table entry.
59  * See symbol.h for the definition of the Symbol container type.
60  */
61 typedef struct HashNode HashNode;
62 struct HashNode {
63   Symbol symbol;       /* The symbol stored in the hash-entry */
64   HashNode *next;      /* The next hash-table entry in a bucket list */
65 };
66 
67 /*
68  * Each hash-table bucket contains a linked list of entries that
69  * hash to the same bucket.
70  */
71 typedef struct {
72   HashNode *head;   /* The head of the bucket hash-node list */
73   int count;        /* The number of entries in the list */
74 } HashBucket;
75 
76 /*
77  * A hash-table consists of 'size' hash buckets.
78  * Note that the HashTable typedef for this struct is contained in hash.h.
79  */
80 struct HashTable {
81   HashMemory *mem;         /* HashTable free-list */
82   int internal_mem;        /* True if 'mem' was allocated by _new_HashTable() */
83   int case_sensitive;      /* True if case is significant in lookup keys */
84   int size;                /* The number of hash buckets */
85   HashBucket *bucket;      /* An array of 'size' hash buckets */
86   int (*keycmp)(const char *, const char *); /* Key comparison function */
87   void *app_data;          /* Application-provided data */
88   HASH_DEL_FN(*del_fn);    /* Application-provided 'app_data' destructor */
89 };
90 
91 static HashNode *_del_HashNode(HashTable *hash, HashNode *node);
92 static HashNode *_new_HashNode(HashTable *hash, const char *name, int code,
93 		      void (*fn)(void), void *data, SYM_DEL_FN(*del_fn));
94 static HashNode *_find_HashNode(HashTable *hash, HashBucket *bucket,
95 				const char *name, HashNode **prev);
96 static HashBucket *_find_HashBucket(HashTable *hash, const char *name);
97 static int _ht_lower_strcmp(const char *node_key, const char *look_key);
98 static int _ht_strcmp(const char *node_key, const char *look_key);
99 
100 /*.......................................................................
101  * Allocate a free-list for use in allocating hash tables and their nodes.
102  *
103  * Input:
104  *  list_count    int    The number of HashTable containers per free-list
105  *                       block.
106  *  node_count    int    The number of HashTable nodes per free-list block.
107  * Output:
108  *  return HashMemory *  The new free-list for use in allocating hash tables
109  *                       and their nodes.
110  */
111 HashMemory *_new_HashMemory(int hash_count, int node_count)
112 {
113   HashMemory *mem;
114 /*
115  * Allocate the free-list container.
116  */
117   mem = (HashMemory *) malloc(sizeof(HashMemory));
118   if(!mem) {
119     errno = ENOMEM;
120     return NULL;
121   };
122 /*
123  * Initialize the container at least up to the point at which it can
124  * safely be passed to _del_HashMemory().
125  */
126   mem->hash_memory = NULL;
127   mem->node_memory = NULL;
128   mem->string_memory = NULL;
129 /*
130  * Allocate the two free-lists.
131  */
132   mem->hash_memory = _new_FreeList(sizeof(HashTable), hash_count);
133   if(!mem->hash_memory)
134     return _del_HashMemory(mem, 1);
135   mem->node_memory = _new_FreeList(sizeof(HashNode), node_count);
136   if(!mem->node_memory)
137     return _del_HashMemory(mem, 1);
138   mem->string_memory = _new_StringMem(64);
139   if(!mem->string_memory)
140     return _del_HashMemory(mem, 1);
141 /*
142  * Return the free-list container.
143  */
144   return mem;
145 }
146 
147 /*.......................................................................
148  * Delete a HashTable free-list. An error will be displayed if the list is
149  * still in use and the deletion will be aborted.
150  *
151  * Input:
152  *  mem    HashMemory *  The free-list container to be deleted.
153  *  force         int    If force==0 then _del_HashMemory() will complain
154  *                        and refuse to delete the free-list if any
155  *                        of nodes have not been returned to the free-list.
156  *                       If force!=0 then _del_HashMemory() will not check
157  *                        whether any nodes are still in use and will
158  *                        always delete the list.
159  * Output:
160  *  return HashMemory *  Always NULL (even if the memory could not be
161  *                       deleted).
162  */
163 HashMemory *_del_HashMemory(HashMemory *mem, int force)
164 {
165   if(mem) {
166     if(!force && (_busy_FreeListNodes(mem->hash_memory) > 0 ||
167 		  _busy_FreeListNodes(mem->node_memory) > 0)) {
168       errno = EBUSY;
169       return NULL;
170     };
171     mem->hash_memory = _del_FreeList(mem->hash_memory, force);
172     mem->node_memory = _del_FreeList(mem->node_memory, force);
173     mem->string_memory = _del_StringMem(mem->string_memory, force);
174     free(mem);
175   };
176   return NULL;
177 }
178 
179 /*.......................................................................
180  * Create a new hash table.
181  *
182  * Input:
183  *  mem       HashMemory *  An optional free-list for use in allocating
184  *                          HashTable containers and nodes. See explanation
185  *                          in hash.h. If you are going to allocate more
186  *                          than one hash table, then it will be more
187  *                          efficient to allocate a single free-list for
188  *                          all of them than to force each hash table
189  *                          to allocate its own private free-list.
190  *  size             int    The size of the hash table. Best performance
191  *                          will be acheived if this is a prime number.
192  *  hcase       HashCase    Specify how symbol case is considered when
193  *                          looking up symbols, from:
194  *                           IGNORE_CASE - Upper and lower case versions
195  *                                         of a letter are treated as
196  *                                         being identical.
197  *                           HONOUR_CASE - Upper and lower case versions
198  *                                         of a letter are treated as
199  *                                         being distinct.
200  *                          characters in a lookup name is significant.
201  *  app_data        void *  Optional application data to be registered
202  *                          to the table. This is presented to user
203  *                          provided SYM_DEL_FN() symbol destructors along
204  *                          with the symbol data.
205  *  del_fn() HASH_DEL_FN(*) If you want app_data to be free'd when the
206  *                          hash-table is destroyed, register a suitable
207  *                          destructor function here.
208  * Output:
209  *  return HashTable *  The new hash table, or NULL on error.
210  */
211 HashTable *_new_HashTable(HashMemory *mem, int size, HashCase hcase,
212 			 void *app_data, HASH_DEL_FN(*del_fn))
213 {
214   HashTable *hash;         /* The table to be returned */
215   int allocate_mem = !mem; /* True if mem should be internally allocated */
216   int i;
217 /*
218  * Check arguments.
219  */
220   if(size <= 0) {
221     errno = EINVAL;
222     return NULL;
223   };
224 /*
225  * Allocate an internal free-list?
226  */
227   if(allocate_mem) {
228     mem = _new_HashMemory(1, 100);
229     if(!mem)
230       return NULL;
231   };
232 /*
233  * Allocate the container.
234  */
235   hash = (HashTable *) _new_FreeListNode(mem->hash_memory);
236   if(!hash) {
237     errno = ENOMEM;
238     if(allocate_mem)
239       mem = _del_HashMemory(mem, 1);
240     return NULL;
241   };
242 /*
243  * Before attempting any operation that might fail, initialize
244  * the container at least up to the point at which it can safely
245  * be passed to _del_HashTable().
246  */
247   hash->mem = mem;
248   hash->internal_mem = allocate_mem;
249   hash->case_sensitive = hcase==HONOUR_CASE;
250   hash->size = size;
251   hash->bucket = NULL;
252   hash->keycmp = hash->case_sensitive ? _ht_strcmp : _ht_lower_strcmp;
253   hash->app_data = app_data;
254   hash->del_fn = del_fn;
255 /*
256  * Allocate the array of 'size' hash buckets.
257  */
258   hash->bucket = (HashBucket *) malloc(sizeof(HashBucket) * size);
259   if(!hash->bucket) {
260     errno = ENOMEM;
261     return _del_HashTable(hash);
262   };
263 /*
264  * Initialize the bucket array.
265  */
266   for(i=0; i<size; i++) {
267     HashBucket *b = hash->bucket + i;
268     b->head = NULL;
269     b->count = 0;
270   };
271 /*
272  * The table is ready for use - albeit currently empty.
273  */
274   return hash;
275 }
276 
277 /*.......................................................................
278  * Delete a hash-table.
279  *
280  * Input:
281  *  hash   HashTable *  The hash table to be deleted.
282  * Output:
283  *  return HashTable *  The deleted hash table (always NULL).
284  */
285 HashTable *_del_HashTable(HashTable *hash)
286 {
287   if(hash) {
288 /*
289  * Clear and delete the bucket array.
290  */
291     if(hash->bucket) {
292       _clear_HashTable(hash);
293       free(hash->bucket);
294       hash->bucket = NULL;
295     };
296 /*
297  * Delete application data.
298  */
299     if(hash->del_fn)
300       hash->del_fn(hash->app_data);
301 /*
302  * If the hash table was allocated from an internal free-list, delete
303  * it and the hash table by deleting the free-list. Otherwise just
304  * return the hash-table to the external free-list.
305  */
306     if(hash->internal_mem)
307       _del_HashMemory(hash->mem, 1);
308     else
309       hash = (HashTable *) _del_FreeListNode(hash->mem->hash_memory, hash);
310   };
311   return NULL;
312 }
313 
314 /*.......................................................................
315  * Create and install a new entry in a hash table. If an entry with the
316  * same name already exists, replace its contents with the new data.
317  *
318  * Input:
319  *  hash   HashTable *  The hash table to insert the symbol into.
320  *  name  const char *  The name to tag the entry with.
321  *  code         int    An application-specific code to be stored in
322  *                      the entry.
323  *  fn  void (*)(void)  An application-specific function to be stored
324  *                      in the entry.
325  *  data        void *  An application-specific pointer to data to be
326  *                      associated with the entry, or NULL if not
327  *                      relevant.
328  *  del_fn SYM_DEL_FN(*) An optional destructor function. When the
329  *                      symbol is deleted this function will be called
330  *                      with the 'code' and 'data' arguments given
331  *                      above. Any application data that was registered
332  *                      to the table via the app_data argument of
333  *                      _new_HashTable() will also be passed.
334  * Output:
335  *  return  HashNode *  The new entry, or NULL if there was insufficient
336  *                      memory or the arguments were invalid.
337  */
338 Symbol *_new_HashSymbol(HashTable *hash, const char *name, int code,
339 			void (*fn)(void), void *data, SYM_DEL_FN(*del_fn))
340 {
341   HashBucket *bucket;  /* The hash-bucket associated with the name */
342   HashNode *node;      /* The new node */
343 /*
344  * Check arguments.
345  */
346   if(!hash || !name) {
347     errno = EINVAL;
348     return NULL;
349   };
350 /*
351  * Get the hash bucket of the specified name.
352  */
353   bucket = _find_HashBucket(hash, name);
354 /*
355  * See if a node with the same name already exists.
356  */
357   node = _find_HashNode(hash, bucket, name, NULL);
358 /*
359  * If found, delete its contents by calling the user-supplied
360  * destructor function, if provided.
361  */
362   if(node) {
363     if(node->symbol.data && node->symbol.del_fn) {
364       node->symbol.data = node->symbol.del_fn(hash->app_data, node->symbol.code,
365 					      node->symbol.data);
366     };
367 /*
368  * Allocate a new node if necessary.
369  */
370   } else {
371     node = _new_HashNode(hash, name, code, fn, data, del_fn);
372     if(!node)
373       return NULL;
374   };
375 /*
376  * Install the node at the head of the hash-bucket list.
377  */
378   node->next = bucket->head;
379   bucket->head = node;
380   bucket->count++;
381   return &node->symbol;
382 }
383 
384 /*.......................................................................
385  * Remove and delete a given hash-table entry.
386  *
387  * Input:
388  *  hash   HashTable *  The hash table to find the symbol in.
389  *  name  const char *  The name of the entry.
390  * Output:
391  *  return  HashNode *  The deleted hash node (always NULL).
392  */
393 Symbol *_del_HashSymbol(HashTable *hash, const char *name)
394 {
395   if(hash && name) {
396     HashBucket *bucket = _find_HashBucket(hash, name);
397     HashNode *prev;   /* The node preceding the located node */
398     HashNode *node = _find_HashNode(hash, bucket, name, &prev);
399 /*
400  * Node found?
401  */
402     if(node) {
403 /*
404  * Remove the node from the bucket list.
405  */
406       if(prev) {
407 	prev->next = node->next;
408       } else {
409 	bucket->head = node->next;
410       };
411 /*
412  * Record the loss of a node.
413  */
414       bucket->count--;
415 /*
416  * Delete the node.
417  */
418       (void) _del_HashNode(hash, node);
419     };
420   };
421   return NULL;
422 }
423 
424 /*.......................................................................
425  * Look up a symbol in the hash table.
426  *
427  * Input:
428  *  hash   HashTable *   The table to look up the string in.
429  *  name  const char *   The name of the symbol to look up.
430  * Output:
431  *  return    Symbol *   The located hash-table symbol, or NULL if not
432  *                       found.
433  */
434 Symbol *_find_HashSymbol(HashTable *hash, const char *name)
435 {
436   HashBucket *bucket;  /* The hash-table bucket associated with name[] */
437   HashNode *node;      /* The hash-table node of the requested symbol */
438 /*
439  * Check arguments.
440  */
441   if(!hash)
442     return NULL;
443 /*
444  * Nothing to lookup?
445  */
446   if(!name)
447     return NULL;
448 /*
449  * Hash the name to a hash-table bucket.
450  */
451   bucket = _find_HashBucket(hash, name);
452 /*
453  * Find the bucket entry that exactly matches the name.
454  */
455   node = _find_HashNode(hash, bucket, name, NULL);
456   if(!node)
457     return NULL;
458   return &node->symbol;
459 }
460 
461 /*.......................................................................
462  * Private function used to allocate a hash-table node.
463  * The caller is responsible for checking that the specified symbol
464  * is unique and for installing the returned entry in the table.
465  *
466  * Input:
467  *  hash     HashTable *  The table to allocate the node for.
468  *  name    const char *  The name of the new entry.
469  *  code           int    A user-supplied context code.
470  *  fn  void (*)(void)    A user-supplied function pointer.
471  *  data          void *  A user-supplied data pointer.
472  *  del_fn  SYM_DEL_FN(*) An optional 'data' destructor function.
473  * Output:
474  *  return    HashNode *  The new node, or NULL on error.
475  */
476 static HashNode *_new_HashNode(HashTable *hash, const char *name, int code,
477 			      void (*fn)(void), void *data, SYM_DEL_FN(*del_fn))
478 {
479   HashNode *node;  /* The new node */
480   size_t len;
481 /*
482  * Allocate the new node from the free list.
483  */
484   node = (HashNode *) _new_FreeListNode(hash->mem->node_memory);
485   if(!node)
486     return NULL;
487 /*
488  * Before attempting any operation that might fail, initialize the
489  * contents of 'node' at least up to the point at which it can be
490  * safely passed to _del_HashNode().
491  */
492   node->symbol.name = NULL;
493   node->symbol.code = code;
494   node->symbol.fn = fn;
495   node->symbol.data = data;
496   node->symbol.del_fn = del_fn;
497   node->next = NULL;
498 /*
499  * Allocate a copy of 'name'.
500  */
501   len = strlen(name) + 1;
502   node->symbol.name = _new_StringMemString(hash->mem->string_memory, len);
503   if(!node->symbol.name)
504     return _del_HashNode(hash, node);
505 /*
506  * If character-case is insignificant in the current table, convert the
507  * name to lower case while copying it.
508  */
509   if(hash->case_sensitive) {
510     strlcpy(node->symbol.name, name, len);
511   } else {
512     const char *src = name;
513     char *dst = node->symbol.name;
514     for( ; *src; src++,dst++)
515       *dst = tolower(*src);
516     *dst = '\0';
517   };
518   return node;
519 }
520 
521 /*.......................................................................
522  * Private function used to delete a hash-table node.
523  * The node must have been removed from its list before calling this
524  * function.
525  *
526  * Input:
527  *  hash   HashTable *  The table for which the node was originally
528  *                      allocated.
529  *  node    HashNode *  The node to be deleted.
530  * Output:
531  *  return  HashNode *  The deleted node (always NULL).
532  */
533 static HashNode *_del_HashNode(HashTable *hash, HashNode *node)
534 {
535   if(node) {
536     node->symbol.name = _del_StringMemString(hash->mem->string_memory,
537 					    node->symbol.name);
538 /*
539  * Call the user-supplied data-destructor if provided.
540  */
541     if(node->symbol.data && node->symbol.del_fn)
542       node->symbol.data = node->symbol.del_fn(hash->app_data,
543 					      node->symbol.code,
544 					      node->symbol.data);
545 /*
546  * Return the node to the free-list.
547  */
548     node->next = NULL;
549     node = (HashNode *) _del_FreeListNode(hash->mem->node_memory, node);
550   };
551   return NULL;
552 }
553 
554 /*.......................................................................
555  * Private function to locate the hash bucket associated with a given
556  * name.
557  *
558  * This uses a hash-function described in the dragon-book
559  * ("Compilers - Principles, Techniques and Tools", by Aho, Sethi and
560  *  Ullman; pub. Adison Wesley) page 435.
561  *
562  * Input:
563  *  hash    HashTable *   The table to look up the string in.
564  *  name   const char *   The name of the symbol to look up.
565  * Output:
566  *  return HashBucket *   The located hash-bucket.
567  */
568 static HashBucket *_find_HashBucket(HashTable *hash, const char *name)
569 {
570   unsigned const char *kp;
571   unsigned long h = 0L;
572   if(hash->case_sensitive) {
573     for(kp=(unsigned const char *) name; *kp; kp++)
574       h = 65599UL * h + *kp;  /* 65599 is a prime close to 2^16 */
575   } else {
576     for(kp=(unsigned const char *) name; *kp; kp++)
577       h = 65599UL * h + tolower((int)*kp);  /* 65599 is a prime close to 2^16 */
578   };
579   return hash->bucket + (h % hash->size);
580 }
581 
582 /*.......................................................................
583  * Search for a given name in the entries of a given bucket.
584  *
585  * Input:
586  *  hash     HashTable *  The hash-table being searched.
587  *  bucket  HashBucket *  The bucket to search (use _find_HashBucket()).
588  *  name    const char *  The name to search for.
589  * Output:
590  *  prev      HashNode ** If prev!=NULL then the pointer to the node
591  *                        preceding the located node in the list will
592  *                        be recorded in *prev. This will be NULL either
593  *                        if the name is not found or the located node is
594  *                        at the head of the list of entries.
595  * return     HashNode *  The located hash-table node, or NULL if not
596  *                        found.
597  */
598 static HashNode *_find_HashNode(HashTable *hash, HashBucket *bucket,
599 			       const char *name, HashNode **prev)
600 {
601   HashNode *last;  /* The previously searched node */
602   HashNode *node;  /* The node that is being searched */
603 /*
604  * Search the list for a node containing the specified name.
605  */
606   for(last=NULL, node=bucket->head;
607       node && hash->keycmp(node->symbol.name, name)!=0;
608       last = node, node=node->next)
609     ;
610   if(prev)
611     *prev = node ? last : NULL;
612   return node;
613 }
614 
615 /*.......................................................................
616  * When hash->case_sensitive is zero this function is called
617  * in place of strcmp(). In such cases the hash-table names are stored
618  * as lower-case versions of the original strings so this function
619  * performs the comparison against lower-case copies of the characters
620  * of the string being compared.
621  *
622  * Input:
623  *  node_key   const char *  The lower-case hash-node key being compared
624  *                           against.
625  *  look_key   const char *  The lookup key.
626  * Output:
627  *  return            int    <0 if node_key < look_key.
628  *                            0 if node_key == look_key.
629  *                           >0 if node_key > look_key.
630  */
631 static int _ht_lower_strcmp(const char *node_key, const char *look_key)
632 {
633   int cn;  /* The latest character from node_key[] */
634   int cl;  /* The latest character from look_key[] */
635   do {
636     cn = *node_key++;
637     cl = *look_key++;
638   } while(cn && cn==tolower(cl));
639   return cn - tolower(cl);
640 }
641 
642 /*.......................................................................
643  * This is a wrapper around strcmp for comparing hash-keys in a case
644  * sensitive manner. The reason for having this wrapper, instead of using
645  * strcmp() directly, is to make some C++ compilers happy. The problem
646  * is that when the library is compiled with a C++ compiler, the
647  * declaration of the comparison function is a C++ declaration, whereas
648  * strcmp() is a pure C function and thus although it appears to have the
649  * same declaration, the compiler disagrees.
650  *
651  * Input:
652  *  node_key   char *  The lower-case hash-node key being compared against.
653  *  look_key   char *  The lookup key.
654  * Output:
655  *  return      int    <0 if node_key < look_key.
656  *                      0 if node_key == look_key.
657  *                     >0 if node_key > look_key.
658  */
659 static int _ht_strcmp(const char *node_key, const char *look_key)
660 {
661   return strcmp(node_key, look_key);
662 }
663 
664 /*.......................................................................
665  * Empty a hash-table by deleting all of its entries.
666  *
667  * Input:
668  *  hash    HashTable *  The hash table to clear.
669  * Output:
670  *  return        int    0 - OK.
671  *                       1 - Invalid arguments.
672  */
673 int _clear_HashTable(HashTable *hash)
674 {
675   int i;
676 /*
677  * Check the arguments.
678  */
679   if(!hash)
680     return 1;
681 /*
682  * Clear the contents of the bucket array.
683  */
684   for(i=0; i<hash->size; i++) {
685     HashBucket *bucket = hash->bucket + i;
686 /*
687  * Delete the list of active hash nodes from the bucket.
688  */
689     HashNode *node = bucket->head;
690     while(node) {
691       HashNode *next = node->next;
692       (void) _del_HashNode(hash, node);
693       node = next;
694     };
695 /*
696  * Mark the bucket as empty.
697  */
698     bucket->head = NULL;
699     bucket->count = 0;
700   };
701   return 0;
702 }
703 
704 /*.......................................................................
705  * Execute a given function on each entry of a hash table, returning
706  * before completion if the the specified function returns non-zero.
707  *
708  * Input:
709  *  hash       HashTable *    The table to traverse.
710  *  scan_fn HASH_SCAN_FN(*)   The function to call.
711  *  context         void *    Optional caller-specific context data
712  *                            to be passed to scan_fn().
713  * Output:
714  *  return           int      0 - OK.
715  *                            1 - Either the arguments were invalid, or
716  *                                scan_fn() returned non-zero at some
717  *                                point.
718  */
719 int _scan_HashTable(HashTable *hash, HASH_SCAN_FN(*scan_fn), void *context)
720 {
721   int i;
722 /*
723  * Check the arguments.
724  */
725   if(!hash || !scan_fn)
726     return 1;
727 /*
728  * Iterate through the buckets of the table.
729  */
730   for(i=0; i<hash->size; i++) {
731     HashBucket *bucket = hash->bucket + i;
732     HashNode *node;
733 /*
734  * Iterate through the list of symbols that fall into bucket i,
735  * passing each one to the caller-specified function.
736  */
737     for(node=bucket->head; node; node=node->next) {
738       if(scan_fn(&node->symbol, context))
739 	return 1;
740     };
741   };
742   return 0;
743 }
744