1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <malloc.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <string.h>
32 #include "xlator.h"
33 #include "util.h"
34 #include "bucket.h"
35 #include "errlog.h"
36 
37 /* Statics: */
38 #define	TRUE	1
39 #define	FALSE	0
40 #define	NLISTS	50
41 #define	NPAR	25
42 
43 static bucket_t **Buckethead;
44 static int N_lists;
45 
46 static int Bc = -1; /* For iterators. */
47 static bucket_t *Bp;
48 
49 static void start_new_list(const bucket_t *);
50 static void grow_lists(void);
51 static bucket_t *new_bucket(const char *, int);
52 static void print_iface(const Interface *);
53 static void new_hashmap(void);
54 static int add_to_hashmap(const char *, const bucket_t *);
55 static bucket_t *find_in_hashmap(const char *);
56 /*
57  * initialization interfaces.
58  */
59 
60 /*
61  * create_lists -- initialize the bucket list and hash map.
62  */
63 void
create_lists(void)64 create_lists(void)
65 {
66 
67 	errlog(BEGIN, "create_lists() {");
68 	new_hashmap();
69 	if ((Buckethead = calloc(sizeof (bucket_t *), NLISTS)) == NULL) {
70 		errlog(FATAL, "out of memory creating initial "
71 			"list of versions");
72 
73 	}
74 	N_lists = NLISTS;
75 	errlog(END, "}");
76 }
77 
78 
79 /*
80  * data-loading interfaces -- adding buckets to lists and
81  *	interfaces to buckets.
82  */
83 
84 /*
85  * add_parent -- add a parent node. Returns TRUE or FALSE.
86  *
87  * 	if *version == NULL, then
88  * 		the bucket version (eg, SUNW_1.1) hasn't
89  * 		been parsed correctly.  Die.
90  * 	if *after == NULL, then this is the ``initial case'',
91  * 		where no predecessor (child) exists.  We start a new
92  * 		tree of buckets.
93  * 	if *after != NULL, we have the normal case, and
94  * 		add to an existing tree.
95  * 	if *after is not a version name found among the buckets,
96  * 		then something got misparsed or the versions file is
97  * 		malformed. Function will print problem and
98  * 		return 0 so caller can report location of error.
99  *      If either version or after is NULL, it's a programmer error.
100  */
101 int
add_parent(const char * version,const char * after,int weak)102 add_parent(const char *version, const char *after, int weak)
103 {
104 	bucket_t *new, *child;
105 
106 	/* Sanity-check parameters. */
107 	assert(version != NULL, "passed a null version to add_parent");
108 	assert(after != NULL, "passed a null after to add_parent");
109 	errlog(BEGIN, "add_parent(%s,%s,%d) {", version, after, weak);
110 	if ((new = find_in_hashmap(version)) == NULL) {
111 		/* We don't have one have one yet. */
112 		new = new_bucket(version, weak);
113 	}
114 	new->b_weak = weak;
115 	if (*after == '\0') {
116 		/*
117 		 * This is the ``initial case'', where no
118 		 * child exists.  We start a new tree of buckets.
119 		 */
120 		(void) add_to_hashmap(version, new);
121 		start_new_list(new);
122 	} else {
123 		if ((child = find_in_hashmap(after)) == NULL) {
124 			/*
125 			 * The version in the spec doesn't appear in the
126 			 * versions file.  One or the other is lying.
127 			 */
128 			errlog(WARNING, "set file: can't find version \"%s\","
129 			    "therefor can't add it's parent, \"%s\"",
130 			    after, version);
131 			errlog(END, "} /* add_parent */");
132 			return (FALSE);
133 		}
134 		(void) add_to_hashmap(version, new);
135 		child->b_parent = new;
136 	}
137 	errlog(END, "} /* add_parent */");
138 	return (TRUE);
139 }
140 
141 /*
142  * add_uncle -- adds an uncle node
143  */
144 int
add_uncle(const char * version,const char * after,int weak)145 add_uncle(const char *version, const char *after, int weak)
146 {
147 	bucket_t *new, *child;
148 	struct bucketlist *uncle;
149 
150 	/* Sanity-check parameters. */
151 	assert(version != NULL, "passed a null version to add_uncle");
152 	assert(after != NULL, "passed a null after to add_uncle");
153 	errlog(BEGIN, "add_uncle(%s,%s,%d) {", version, after, weak);
154 	if ((new = find_in_hashmap(version)) == NULL) {
155 		/* We don't have one have one yet. */
156 		new = new_bucket(version, weak);
157 	}
158 	if (*after == '\0') {
159 		/*
160 		 * This is the ``initial case'', where no
161 		 * child exists.  We start a new tree of buckets.
162 		 */
163 		(void) add_to_hashmap(version, new);
164 		start_new_list(new);
165 	} else {
166 		if ((child = find_in_hashmap(after)) == NULL) {
167 			/*
168 			 * The version in the spec doesn't appear in the
169 			 * versions file.  One or the other is lying.
170 			 */
171 			errlog(WARNING, "set file: can't find version \"%s\","
172 			    "therefor can't add it's uncle, \"%s\"",
173 			    after, version);
174 			errlog(END, "}");
175 			return (FALSE);
176 		}
177 		(void) add_to_hashmap(version, new);
178 		uncle =	malloc(sizeof (struct bucketlist));
179 		uncle->bl_next = child->b_uncles;
180 		uncle->bl_bucket = new;
181 		child->b_uncles = uncle;
182 	}
183 	errlog(END, "}");
184 	return (TRUE);
185 }
186 
187 /*
188  * set_weak -- set a version to be a weak version
189  */
190 void
set_weak(const char * version,int weak)191 set_weak(const char *version, int weak)
192 {
193 	bucket_t *v;
194 	if ((v = find_in_hashmap(version)) == NULL) {
195 		/* We don't have one have one yet. */
196 		errlog(ERROR|FATAL, "Unable to set weak. Version not found");
197 	}
198 	v->b_weak = weak;
199 }
200 
201 /*
202  * add_by_name -- look up bucket and add an interface to it.
203  *      Returns 0 for success or an errno.h value for failure.
204  *
205  * 	if *version is not among the buckets, then the
206  * 		version in the spec doesn't appear in the
207  * 		set file.  One or the other is lying. Function will
208  * 		report the problem and return ENOENT
209  * 		so the front end can report and exit (or
210  * 		continue if it wants).
211  * 	if interface ore version is NULL, then
212  * 		the begin line code should
213  * 		have caught it long before, to avoid passing
214  * 		a null pointer around. Die.
215  *
216  */
217 #define	ADD_EQUALS(str)	if (strchr(str, '=') == NULL) (void) strcat(str, " =")
218 
219 int
add_by_name(const char * version,const Interface * interface)220 add_by_name(const char *version, const Interface *interface)
221 {
222 	bucket_t *b;
223 	char buffer[1024];
224 
225 	assert(version != NULL, "passed a null version to add_by_name");
226 	assert(interface != NULL, "passed a null interface to add_by_name");
227 
228 	errlog(BEGIN, "add_by_name(%s,", version);
229 	print_iface(interface);
230 	errlog(TRACING, ");");
231 
232 	/* Sanity-check the parameters. */
233 	if ((b = find_in_hashmap(version)) == NULL) {
234 		/*
235 		 * The version in the spec doesn't appear in the
236 		 * versions file. Alas, this isn't an error.  It can
237 		 * happen whenever doing just sparc, just i386
238 		 * or the like.
239 		 */
240 		errlog(END, "}");
241 		return (ENOENT);
242 	}
243 	/*
244 	 * Add to bucket.
245 	 */
246 	(void) snprintf(buffer, sizeof (buffer), "%s", interface->IF_name);
247 
248 	if (interface->IF_filter && interface->IF_auxiliary) {
249 		errlog(FATAL, "Error: cannot set both FILTER and AUXILIARY "
250 		    "for an interface: %s", interface->IF_name);
251 	}
252 
253 	if (interface->IF_filter) {
254 		ADD_EQUALS(buffer);
255 		if (interface->IF_type == FUNCTION) {
256 			(void) strcat(buffer, " FUNCTION");
257 		} else if (interface->IF_type == DATA) {
258 			(void) strcat(buffer, " DATA");
259 		}
260 		(void) strcat(buffer, " FILTER ");
261 		(void) strcat(buffer, interface->IF_filter);
262 	} else if (interface->IF_auxiliary) {
263 		ADD_EQUALS(buffer);
264 		(void) strcat(buffer, " AUXILIARY ");
265 		(void) strcat(buffer, interface->IF_auxiliary);
266 	} else if (IsFilterLib) {
267 		/*
268 		 * For DATA types it is currently assumed that they are
269 		 * handled via a minimal C file, e.g. 'data.c', in the
270 		 * library's build.  Hence, we do not append '= DATA' here.
271 		 */
272 		if (interface->IF_type == FUNCTION) {
273 			ADD_EQUALS(buffer);
274 			(void) strcat(buffer, " FUNCTION");
275 		}
276 	}
277 
278 	switch (interface->IF_binding) {
279 	case DIRECT:
280 		ADD_EQUALS(buffer);
281 		(void) strcat(buffer, " DIRECT");
282 		break;
283 	case NODIRECT:
284 		ADD_EQUALS(buffer);
285 		(void) strcat(buffer, " NODIRECT");
286 		break;
287 	}
288 
289 	if (interface->IF_binding == PROTECTED) {
290 		/* Assign in case of realloc. */
291 		b->b_protected_table =
292 		    add_to_stringtable(b->b_protected_table, buffer);
293 		b->b_has_protecteds = 1;
294 		errlog(VERBOSE, "set has_protecteds on bucket 0x%p", b);
295 	} else {
296 		/* Assign in case of realloc. */
297 		b->b_global_table = add_to_stringtable(b->b_global_table,
298 			buffer);
299 	}
300 	errlog(END, "}");
301 	return (0);
302 }
303 
304 
305 /*
306  * Processing interfaces
307  */
308 
309 /*
310  * sort_buckets -- sort the interfaces within the buckets into
311  *      alphabetical order.
312  */
313 void
sort_buckets(void)314 sort_buckets(void)
315 {
316 	bucket_t *l, *b;
317 
318 	errlog(BEGIN, "sort_buckets() {");
319 	for (l = first_list(); l != NULL; l = next_list()) {
320 		errlog(VERBOSE, "l-bucket: %s", l->b_name);
321 		for (b = first_from_list(l); b != NULL; b = next_from_list()) {
322 			errlog(VERBOSE, "   b-bkt: %s", b->b_name);
323 			sort_stringtable(b->b_global_table);
324 			sort_stringtable(b->b_protected_table);
325 			if (b->b_uncles) {
326 
327 				if (b->b_uncles->bl_bucket) {
328 		sort_stringtable(b->b_uncles->bl_bucket->b_global_table);
329 		sort_stringtable(b->b_uncles->bl_bucket->b_protected_table);
330 				}
331 			}
332 		}
333 	}
334 	errlog(END, "}");
335 }
336 
337 
338 /*
339  * add_local -- set the local flag on the logically first bucket.
340  *     This decision may belong in the caller, as it is about
341  *     mapfiles, not inherent ordering or bucket contents...
342  */
343 void
add_local(void)344 add_local(void)
345 {
346 	bucket_t *b, *list;
347 	int	done = 0;
348 
349 	errlog(BEGIN, "add_local() {");
350 	/* Iterate across lists of buckets */
351 	for (list = first_list(); list != NULL; list = next_list()) {
352 		/* Traverse the list found. */
353 		for (b = list; b != NULL; b = b->b_parent) {
354 			if (b->b_weak != 1) {
355 				/* We've found the first non-weak. */
356 				b->b_has_locals = done = 1;
357 				errlog(VERBOSE,
358 				    "set has_locals on bucket 0x%p", b);
359 				break;
360 			}
361 		}
362 		if (b != NULL && b->b_has_locals == 1)
363 			break;
364 	}
365 	if (done == 0) {
366 		errlog(WARNING, "has_locals never set");
367 	}
368 	errlog(END, "}");
369 }
370 
371 
372 /*
373  * Output interfaces, mostly iterators
374  */
375 
376 
377 /*
378  * parents_of -- return a list of all parents.
379  */
380 char **
parents_of(const bucket_t * start)381 parents_of(const bucket_t *start)
382 {
383 	static char *a[NPAR] = {NULL};
384 	const bucket_t *b = start;
385 	char **p = &a[0];
386 
387 	assert(start != NULL, "passed a null start to parents_of");
388 	errlog(BEGIN, "parents_of() {");
389 	a[0] = '\0';
390 
391 	/* Go to parent, print it and all its uncle. */
392 	if (b->b_parent == NULL) {
393 		errlog(TRACING, "returning empty string");
394 		errlog(END, "}");
395 		return (a);
396 	}
397 	b = b->b_parent;
398 	*p++ = b->b_name;
399 	*p = '\0';
400 
401 	assert(p < &a[NPAR], "p fell off the end of a in parents_of");
402 	errlog(END, "}");
403 	return (a);
404 }
405 
406 /*
407  * first, next_from_bucket --iterators for bucket contents. Serially
408  *      reusable only.
409  */
410 int Ic = -1;
411 
412 /*
413  * debugging interfaces
414  */
415 void
print_bucket(const bucket_t * b)416 print_bucket(const bucket_t *b)
417 {
418 
419 	errlog(TRACING, "bucket_t at 0x%p {", (void *)b);
420 	errlog(TRACING, "    char   *b_name = \"%s\";", b->b_name);
421 	errlog(TRACING, "    struct bucket_t *b_parent = 0x%p;",
422 		(void *)b->b_parent);
423 	errlog(TRACING, "    struct bucketlist *b_uncles = 0x%p;",
424 		(void *)b->b_uncles);
425 	errlog(TRACING, "    struct bucket_t *b_thread = 0x%p;",
426 		(void *)b->b_thread);
427 	errlog(TRACING, "    int	b_has_locals = %d;",
428 		b->b_has_locals);
429 	errlog(TRACING, "    int	b_has_protecteds = %d;",
430 		b->b_has_protecteds);
431 	errlog(TRACING, "    int        b_was_printed = %d;",
432 		b->b_was_printed);
433 	errlog(TRACING, "    int        b_weak = %d;",
434 		b->b_weak);
435 	errlog(TRACING, "    table_t  *b_global_table = 0x%p;",
436 		(void *)b->b_global_table);
437 	errlog(TRACING, "    table_t  *b_protected_table = 0x%p;",
438 		(void *)b->b_protected_table);
439 	errlog(TRACING, "}");
440 }
441 
442 void
print_all_buckets(void)443 print_all_buckets(void)
444 {
445 	bucket_t *l, *b;
446 	int i = 0, j = 0;
447 	char **p;
448 
449 	for (i = 0, l = first_list(); l != NULL; l = next_list(), ++i) {
450 		errlog(TRACING, "list %d", i);
451 		for (j = 0, b = first_from_list(l);
452 		    b != NULL; b = next_from_list(), ++j) {
453 			errlog(TRACING, "bucket %d", j);
454 			print_bucket(b);
455 			errlog(TRACING, "global interfaces = {");
456 			print_stringtable(b->b_global_table);
457 			errlog(TRACING, "}");
458 			errlog(TRACING, "protected interfaces = {");
459 			print_stringtable(b->b_protected_table);
460 			errlog(TRACING, "}");
461 
462 			for (p = parents_of(b); p != NULL && *p != NULL; ++p) {
463 				errlog(TRACING, " %s", *p);
464 			}
465 			errlog(TRACING, ";");
466 
467 			if (b->b_uncles) {
468 				errlog(TRACING, " uncle bucket %d.1", j);
469 				print_bucket(b->b_uncles->bl_bucket);
470 				errlog(TRACING, "global interfaces = {");
471 				print_stringtable(
472 				    b->b_uncles->bl_bucket->b_global_table);
473 				errlog(TRACING, "}");
474 				errlog(TRACING, "protected interfaces = {");
475 				print_stringtable(
476 				    b->b_uncles->bl_bucket->b_protected_table);
477 				errlog(TRACING, "}");
478 			}
479 		}
480 	}
481 }
482 
483 
484 /*
485  * lower-level functions, not visible outside the file.
486  */
487 
488 /*
489  * new_bucket -- create a bucket for a given version. Must not fail.
490  */
491 static bucket_t *
new_bucket(const char * name,int weak)492 new_bucket(const char *name, int weak)
493 {
494 	bucket_t *b;
495 
496 	if ((b = (bucket_t *)calloc(1, sizeof (bucket_t))) == NULL) {
497 		errlog(FATAL, "out of memory creating a bucket "
498 			"to store interfaces in");
499 	}
500 	if ((b->b_name = strdup(name)) == NULL) {
501 		errlog(FATAL, "out of memory storing an interface "
502 			"in a version bucket");
503 	}
504 	b->b_uncles = NULL;
505 	b->b_global_table = create_stringtable(TABLE_INITIAL);
506 	b->b_protected_table = create_stringtable(TABLE_INITIAL);
507 	b->b_weak = weak;
508 	return (b);
509 }
510 
511 
512 /*
513  * start_new_list -- start a list of buckets.
514  */
515 static void
start_new_list(const bucket_t * b)516 start_new_list(const bucket_t *b)
517 {
518 	int i;
519 
520 	errlog(BEGIN, "start_new_list() {");
521 	assert(Buckethead != NULL, "Buckethead null in start_new_list");
522 	for (i = 0; Buckethead[i] != NULL && i < N_lists; ++i)
523 		continue;
524 	if (i >= N_lists) {
525 		grow_lists();
526 	}
527 	Buckethead[i] = (bucket_t *)b;
528 	errlog(END, "}");
529 }
530 
531 /*
532  * grow_list -- make more lists.  This should never occur...
533  */
534 static void
grow_lists(void)535 grow_lists(void)
536 {
537 	int i = N_lists;
538 
539 	errlog(BEGIN, "grow_lists() {");
540 	errlog(WARNING, "Warning: more than %d version lists "
541 	    "required (< %d is normal). Check sets file "
542 	    "to see why so many lines appear.",
543 	    N_lists, NLISTS);
544 
545 	N_lists *= 2;
546 	if ((Buckethead = realloc(Buckethead, sizeof (bucket_t *) * N_lists))
547 		== NULL) {
548 		errlog(FATAL, "out of memory growing list of "
549 			"version buckets");
550 	}
551 	for (; i < N_lists; ++i) {
552 		Buckethead[i] = NULL;
553 	}
554 }
555 
556 /*
557  * delete_lists -- clean up afterwards.
558  */
559 void
delete_lists(void)560 delete_lists(void)
561 {
562 	N_lists = 0;
563 	free(Buckethead);
564 	Buckethead = 0;
565 }
566 
567 /*
568  * first_list, next_list -- an iterator for lists themselves.  Serially
569  *      reusable only.
570  */
571 bucket_t *
first_list(void)572 first_list(void)
573 {
574 	Bc = 0;
575 	return (Buckethead[Bc]);
576 }
577 
578 bucket_t *
next_list(void)579 next_list(void)
580 {
581 	return (Buckethead[++Bc]);
582 }
583 
584 
585 /*
586  * first, next, last_from_list -- iterators for individual lists. Serially
587  *      reusable only.
588  */
589 bucket_t *
first_from_list(const bucket_t * l)590 first_from_list(const bucket_t *l)
591 {
592 	return (Bp = (bucket_t *)l);
593 }
594 
595 bucket_t *
next_from_list(void)596 next_from_list(void)
597 {
598 	return (Bp = Bp->b_parent);
599 }
600 
601 
602 
603 /*
604  * Iface print utility
605  */
606 static void
print_iface(const Interface * p)607 print_iface(const Interface * p)
608 {
609 
610 	errlog(TRACING, "%s (%s, %s, %s %d)", p->IF_name,
611 		(p->IF_type == FUNCTION) ? "function" :
612 		(p->IF_type == DATA) ? "data" : "unknown type",
613 		(p->IF_version) ? p->IF_version : "unknown version",
614 		(p->IF_class) ? p->IF_class : "unknown class",
615 		p->IF_binding);
616 }
617 
618 
619 
620 #define	HASHMAPSIZE	100
621 #define	ERR	(-1)
622 
623 static struct {
624 	hashmap_t *hh_map;
625 	int hh_map_size;
626 	int hh_mapC;
627 	hashmap_t *hh_last;
628 } Hashhead = {
629 	NULL, -1, -1, NULL
630 };
631 
632 static int checksum(const char *);
633 static void print_hashmap(const hashmap_t *);
634 
635 /*
636  * new_hashmap -- create the hash.
637  */
638 static void
new_hashmap(void)639 new_hashmap(void)
640 {
641 
642 	errlog(BEGIN, "new_hashmap() {");
643 	if ((Hashhead.hh_map = calloc(sizeof (hashmap_t), HASHMAPSIZE))
644 	    == NULL) {
645 		errlog(FATAL, "out of memory creating a hash-map of "
646 			"the versions");
647 	}
648 	Hashhead.hh_mapC = 0;
649 	errlog(END, "}");
650 }
651 
652 /*
653  * add_to_hashmap -- add a bucket to the map.  This is strictly for
654  *	use by add_parent()/add_uncle().
655  */
656 static int
add_to_hashmap(const char * version_name,const bucket_t * bucket)657 add_to_hashmap(const char *version_name, const bucket_t *bucket)
658 {
659 	hashmap_t *p;
660 
661 	assert(Hashhead.hh_map != NULL,
662 	    "Hashead.map was null in add_to_hashmap");
663 	assert(Hashhead.hh_mapC < HASHMAPSIZE,
664 	    "mapC too big in add_to_hashmap");
665 	errlog(BEGIN, "add_to_hashmap(%s, %s) {", version_name, bucket);
666 	if (find_in_hashmap(version_name) != NULL) {
667 		/* Seen for the second time. TBD... */
668 		errlog(END, "} /* add_to_hashmap */");
669 		return (ERR);
670 	}
671 	p = &Hashhead.hh_map[Hashhead.hh_mapC++];
672 	if ((p->h_version_name = strdup(version_name)) == NULL) {
673 		errlog(FATAL, "out of memory storing a version name");
674 
675 	}
676 	p->h_bucket = (bucket_t *)bucket;
677 	p->h_hash = checksum(version_name);
678 	Hashhead.hh_last = p;
679 	print_hashmap(p);
680 	errlog(END, "} /* add_to_hashmap */");
681 	return (0);
682 }
683 
684 
685 /*
686  * find_in_hashmap -- find a bucket by name.  Strictly for use by addByName().
687  */
688 static bucket_t *
find_in_hashmap(const char * version_name)689 find_in_hashmap(const char *version_name)
690 {
691 	hashmap_t *current;
692 	int hash = checksum(version_name);
693 
694 	assert(Hashhead.hh_map != NULL,
695 		"Hashhead.hh_map was null in find_in_hashmap");
696 	errlog(BEGIN, "find_in_hashmap(%s) {", version_name);
697 	if (Hashhead.hh_last != NULL && Hashhead.hh_last->h_hash == hash &&
698 	    strcmp(Hashhead.hh_last->h_version_name, version_name) == 0) {
699 		errlog(END, "}");
700 		return (Hashhead.hh_last->h_bucket);
701 	}
702 	for (current = Hashhead.hh_map;
703 		current->h_version_name != NULL; ++current) {
704 		if (current->h_hash == hash &&
705 			strcmp(current->h_version_name, version_name) == 0) {
706 			/* Found it */
707 			Hashhead.hh_last = current;
708 			errlog(END, "}");
709 			return (current->h_bucket);
710 		}
711 	}
712 	/* Doesn't exist, meaning version name is bogus. */
713 	errlog(END, "}");
714 	return (NULL);
715 }
716 
717 /*
718  * checksum -- from sum(1), algorithm 1.
719  */
720 static int
checksum(const char * p)721 checksum(const char *p)
722 {
723 	int sum;
724 
725 	for (sum = 0; *p != '\0'; ++p) {
726 		if (sum & 01)
727 			sum = (sum >> 1) + 0x8000;
728 		else
729 			sum >>= 1;
730 		sum += *p;
731 		sum &= 0xFFFF;
732 	}
733 	return (sum);
734 }
735 
736 static void
print_hashmap(const hashmap_t * h)737 print_hashmap(const hashmap_t *h)
738 {
739 	errlog(VERBOSE, "struct hashmap_t at 0x4.4x {", h);
740 	errlog(VERBOSE, "    int    h_hash = %d;", h->h_hash);
741 	errlog(VERBOSE, "    char   *h_version_name = \"%s\";",
742 		h->h_version_name);
743 	errlog(VERBOSE, "    bucket_t *h_bucket = 0x%p;;",
744 		(void *) h->h_bucket);
745 	errlog(VERBOSE, "}");
746 }
747