xref: /illumos-gate/usr/src/lib/libc/port/i18n/gettext_util.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
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 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "synonyms.h"
30 #include "mtlib.h"
31 #include <ctype.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <thread.h>
37 #include <synch.h>
38 #include <limits.h>
39 #include <unistd.h>
40 #include <sys/mman.h>
41 #include <langinfo.h>
42 #include "libc.h"
43 #include "_loc_path.h"
44 #include "msgfmt.h"
45 #include "gettext.h"
46 
47 #ifdef GETTEXT_DEBUG
48 #include "plural_parser.h"
49 #include <stdarg.h>
50 #endif
51 
52 static const char	*category_name[] = {
53 	"LC_CTYPE",
54 	"LC_NUMERIC",
55 	"LC_TIME",
56 	"LC_COLLATE",
57 	"LC_MONETARY",
58 	"LC_MESSAGES"
59 };
60 
61 static const int	category_name_len[] = {
62 	8,
63 	10,
64 	7,
65 	10,
66 	11,
67 	11
68 };
69 
70 static int
71 setmo(Msg_node *mnp, char *addr, int type)
72 {
73 #ifdef GETTEXT_DEBUG
74 	(void) printf("*************** setmo(0x%p, %d)\n",
75 		(void *)mnp, ret);
76 	printmnp(mnp, 0);
77 #endif
78 
79 	switch (type) {
80 	case T_ILL_MO:
81 		/* invalid MO */
82 		mnp->type = T_ILL_MO;
83 #ifdef GETTEXT_DEBUG
84 		(void) printf("*************** exiting setmo\n");
85 		printmnp(mnp, 0);
86 #endif
87 		return (0);
88 	case T_SUN_MO:
89 		{
90 			struct msg_info	*sun_header;
91 			int	struct_size, info_size, count;
92 			Msg_s_node	*p;
93 			p = (Msg_s_node *)calloc(1, sizeof (Msg_s_node));
94 			if (!p) {
95 				return (-1);
96 			}
97 			/* LINTED */
98 			sun_header = (struct msg_info *)addr;
99 			count = sun_header->msg_count;
100 			struct_size = (int)(MSG_STRUCT_SIZE * count);
101 			info_size = (int)(sizeof (struct msg_info));
102 			p->msg_file_info = sun_header;
103 			/* LINTED */
104 			p->msg_list = (struct msg_struct *)(((char *)addr) +
105 				info_size);
106 			p->msg_ids = (char *)(((char *)addr) + info_size +
107 				struct_size);
108 			p->msg_strs = (char *)(((char *)addr) + info_size +
109 				struct_size + sun_header->str_count_msgid);
110 
111 			mnp->msg.sunmsg = p;
112 			mnp->type = T_SUN_MO;
113 #ifdef GETTEXT_DEBUG
114 			(void) printf("*************** exiting setmo\n");
115 			printmnp(mnp, 0);
116 #endif
117 			return (0);
118 		}
119 		/* NOTREACHED */
120 	case T_GNU_MO:
121 	case T_GNU_SWAPPED_MO:
122 		{
123 			struct gnu_msg_info	*gnu_header;
124 			Msg_g_node	*p;
125 			p = (Msg_g_node *)calloc(1, sizeof (Msg_g_node));
126 			if (!p) {
127 				return (-1);
128 			}
129 			/* LINTED */
130 			gnu_header = (struct gnu_msg_info *)addr;
131 			p->msg_file_info = gnu_header;
132 			if (type == T_GNU_SWAPPED_MO) {
133 				/*
134 				 * This MO file has been created on
135 				 * the reversed endian system
136 				 */
137 				p->flag |= ST_SWP;
138 			}
139 			/* LINTED */
140 			p->hash_table = (unsigned int *)(((char *)addr) +
141 				SWAP(p, gnu_header->off_hashtbl));
142 
143 			mnp->msg.gnumsg = p;
144 			mnp->type = T_GNU_MO;
145 #ifdef GETTEXT_DEBUG
146 			(void) printf("*************** exiting setmo\n");
147 			printmnp(mnp, 0);
148 #endif
149 			return (0);
150 		}
151 		/* NOTREACHED */
152 	}
153 	/* NOTREACHED */
154 	return (0);	/* keep gcc happy */
155 }
156 
157 /*
158  * setmsg
159  *
160  * INPUT
161  *   mnp  - message node
162  *   addr -	address to the mmapped file
163  *   size - size of the file
164  *
165  * RETURN
166  *   0   - succeeded
167  *  -1   - failed
168  */
169 int
170 setmsg(Msg_node *mnp, char *addr, size_t size)
171 {
172 	struct msg_info	*sun_header;
173 	struct gnu_msg_info	*gnu_header;
174 	unsigned int	first_4bytes;
175 	int	mid, count;
176 	int	struct_size, struct_size_old;
177 	int	msg_struct_size;
178 
179 	if (size < sizeof (struct msg_info)) {
180 		/* invalid mo file */
181 		return (setmo(mnp, addr, T_ILL_MO));
182 	}
183 
184 	/* LINTED */
185 	first_4bytes = *((unsigned int *)addr);
186 	if (first_4bytes <= INT_MAX) {
187 		/* candidate for sun mo */
188 		/* LINTED */
189 		sun_header = (struct msg_info *)addr;
190 		mid = sun_header->msg_mid;
191 		count = sun_header->msg_count;
192 		msg_struct_size = sun_header->msg_struct_size;
193 		struct_size_old = (int)(OLD_MSG_STRUCT_SIZE * count);
194 		struct_size = (int)(MSG_STRUCT_SIZE * count);
195 
196 		if ((((count - 1) / 2) == mid) &&
197 			((msg_struct_size == struct_size_old) ||
198 			(msg_struct_size == struct_size))) {
199 			/* valid sun mo file */
200 			return (setmo(mnp, addr, T_SUN_MO));
201 		}
202 		/* invalid mo file */
203 		return (setmo(mnp, addr, T_ILL_MO));
204 	}
205 
206 	/* checks the GNU MAGIC number */
207 	if (size < sizeof (struct gnu_msg_info)) {
208 		/* invalid mo file */
209 		return (setmo(mnp, addr, T_ILL_MO));
210 	}
211 
212 	/* LINTED */
213 	gnu_header = (struct gnu_msg_info *)addr;
214 	if ((gnu_header->magic == GNU_MAGIC) &&
215 		(gnu_header->revision == GNU_REVISION)) {
216 		/* GNU mo file */
217 		return (setmo(mnp, addr, T_GNU_MO));
218 	} else if ((gnu_header->magic == GNU_MAGIC_SWAPPED) &&
219 		(gnu_header->revision == GNU_REVISION_SWAPPED)) {
220 		/* endian-swapped GNU mo file */
221 		return (setmo(mnp, addr, T_GNU_SWAPPED_MO));
222 	}
223 
224 	/* invalid mo file */
225 	return (setmo(mnp, addr, T_ILL_MO));
226 }
227 
228 /*
229  * mk_msgfile
230  *
231  * INPUT
232  * mp -	uses the following members:
233  * 	msgfile  - buffer to store the pathname to the message file
234  *	binding  - directory pathname bound to specified domain
235  *	cblen    - length of binding
236  *	locale   - locale name
237  *	domain   - domain name
238  *	category - category
239  *	locale_len - length of locale name
240  *	domain_len - length of domain name
241  *
242  * OUTPUT
243  * mp->msgfile - pathname to the message file is stored
244  * mp->msgfile_len - length of mp->msgfile without null termination
245  *
246  * RETURN
247  * mp->msgfile is returned
248  */
249 char *
250 mk_msgfile(struct msg_pack *mp)
251 {
252 	char	*p, *q;
253 	const char	*catstr;
254 	size_t	cblen, catlen, totallen;
255 
256 #ifdef GETTEXT_DEBUG
257 	(void) printf("*************** mk_msgfile(0x%p)\n",
258 		(void *)mp);
259 	printmp(mp, 0);
260 #endif
261 
262 	p = mp->msgfile;
263 	q = mp->binding;
264 	while (*p = *q++)
265 		p++;
266 	cblen = (size_t)(p - mp->msgfile);
267 	if (*(p - 1) != '/') {
268 		/*
269 		 * if the last character of binding
270 		 * isn't a '/', adding '/'.
271 		 */
272 		if (cblen + 1 >= MAXPATHLEN) {
273 			/* MAXPATHLEN includes a null termination */
274 			return (NULL);
275 		}
276 		*p++ = '/';
277 		cblen++;
278 	}
279 
280 	catstr = category_name[mp->category];
281 	catlen = (size_t)category_name_len[mp->category];
282 	/*
283 	 * totallen is the length of the msgfile
284 	 * pathname excluding a null termination.
285 	 */
286 
287 	totallen = cblen + mp->locale_len + 1 + catlen + 1 +
288 		mp->domain_len + MSGFILESUFFIXLEN;
289 	if (totallen >= MAXPATHLEN)
290 		return (NULL);
291 
292 	q = mp->locale;
293 	while (*p++ = *q++)
294 		;
295 	*(p - 1) = '/';
296 	while (*p++ = *catstr++)
297 		;
298 	*(p - 1) = '/';
299 	q = mp->domain;
300 	while (*p++ = *q++)
301 		;
302 	*(p - 1) = '.';
303 	*p = 'm';
304 	*(p + 1) = 'o';
305 	*(p + 2) = '\0';
306 
307 	mp->msgfile_len = totallen;
308 
309 #ifdef GETTEXT_DEBUG
310 	(void) printf("*************** Exiting mk_msgfile\n");
311 	(void) printf("mp->msgfile: \"%s\"\n", mp->msgfile);
312 #endif
313 
314 	return (mp->msgfile);
315 }
316 
317 
318 /*
319  * check_cache
320  *
321  * INPUT
322  * cp - may use the following members:
323  *	node_hash - pointer to the Cache_node object having this locale
324  *
325  * mp - may use the following members:
326  *	msgfile - pathname to the message catalog file
327  *	hash_locale - hash id of this locale
328  *
329  * OUTPUT
330  * cp - may update the following members:
331  *	mnp - pointer to a Msg_node object
332  *	cnp - pointer to a Cache_node object
333  *	node_hash - pointer to the Cache_node object having this locale
334  *	cacheline - flag to show if the Cache_node for this locale exists
335  *
336  * RETURN
337  * 1 - Entry for this message catalog exists in the cache
338  * 0 - Entry for this message catalog doesn't exist in the cache
339  */
340 int
341 check_cache(struct cache_pack *cp, struct msg_pack *mp)
342 {
343 	Msg_node	*cur_msg;
344 	Gettext_t	*gt = global_gt;
345 
346 #ifdef GETTEXT_DEBUG
347 	{
348 		int level = 0;
349 
350 		(void) printf("*************** check_cache(0x%p, 0x%p)\n",
351 			(void *)cp, (void *)mp);
352 		printcp(cp, 0);
353 		printmp(mp, 0);
354 	}
355 #endif
356 
357 	cur_msg = gt->c_m_node;
358 	if (cur_msg &&
359 		(strcmp(cur_msg->path, mp->msgfile) == 0)) {
360 		/*
361 		 * msgfile is the same as the previous message file
362 		 */
363 		cp->mnp = cur_msg;
364 		cp->cnp = gt->c_node;
365 		cp->cacheline = 1;
366 #ifdef GETTEXT_DEBUG
367 		(void) printf("************* exiting check_cache\n");
368 		(void) printf("cache found\n");
369 		printmnp(cp->mnp, 0);
370 #endif
371 		return (1);
372 	}
373 	if (cp->node_hash) {
374 		/*
375 		 * already cache_node having the same
376 		 * hash id found
377 		 */
378 		cp->cnp = cp->node_hash;
379 		cp->mnp = cp->cnp->m_node;
380 		cp->cacheline = 1;
381 		while (cp->mnp) {
382 			if (strcmp(cp->mnp->path, mp->msgfile) == 0) {
383 #ifdef GETTEXT_DEBUG
384 		(void) printf("************* exiting check_cache\n");
385 		(void) printf("cache found\n");
386 		printmnp(cp->mnp, 0);
387 #endif
388 				return (1);
389 			}
390 			cp->mnp = cp->mnp->next;
391 		}
392 #ifdef GETTEXT_DEBUG
393 		(void) printf("************* exiting check_cache\n");
394 		(void) printf("cache not found\n");
395 #endif
396 		return (0);
397 	}
398 	/* search the cache list */
399 	cp->cnp = gt->c_node;
400 	cp->mnp = NULL;
401 	while (cp->cnp) {
402 		if (cp->cnp->hashid == mp->hash_locale) {
403 			cp->node_hash = cp->cnp;
404 			cp->mnp = cp->cnp->m_node;
405 			cp->cacheline = 1;
406 			while (cp->mnp) {
407 				if (strcmp(cp->mnp->path, mp->msgfile) == 0) {
408 					/*
409 					 * msgfile found in the cache
410 					 */
411 #ifdef GETTEXT_DEBUG
412 		(void) printf("************* exiting check_cache\n");
413 		(void) printf("cache found\n");
414 		printmnp(cp->mnp, 0);
415 #endif
416 					return (1);
417 				}
418 				cp->mnp = cp->mnp->next;
419 			}
420 #ifdef GETTEXT_DEBUG
421 		(void) printf("************* exiting check_cache\n");
422 		(void) printf("cache not found\n");
423 #endif
424 			return (0);
425 		} else {
426 			cp->cnp = cp->cnp->next;
427 		}
428 	}
429 	cp->cacheline = 0;
430 #ifdef GETTEXT_DEBUG
431 		(void) printf("************* exiting check_cache\n");
432 		(void) printf("cache not found\n");
433 #endif
434 	return (0);
435 }
436 
437 char *
438 get_codeset(const char *domain)
439 {
440 	char	*codeset;
441 
442 #ifdef GETTEXT_DEBUG
443 	(void) printf("*************** get_codeset(\"%s\")\n",
444 		domain ? domain : "(null)");
445 #endif
446 
447 	codeset = _real_bindtextdomain_u(domain, NULL, TP_CODESET);
448 	if (!codeset) {
449 		/* no codeset is bound to this domain */
450 		codeset = nl_langinfo(CODESET);
451 	}
452 #ifdef GETTEXT_DEBUG
453 	(void) printf("*************** existing get_codeset(\"%s\")\n",
454 		domain ? domain : "(null)");
455 	(void) printf("                = \"%s\"\n", codeset);
456 #endif
457 
458 	return (codeset);
459 }
460 
461 void
462 connect_entry(struct cache_pack *cp)
463 {
464 	Gettext_t	*gt = global_gt;
465 
466 #ifdef GETTEXT_DEBUG
467 	(void) printf("*************** connect_entry(0x%p)\n",
468 		(void *)cp);
469 	printcp(cp, 0);
470 #endif
471 
472 	if (cp->cacheline) {
473 		if (cp->cnp->m_last)
474 			cp->cnp->m_last->next = cp->mnp;
475 		else
476 			cp->cnp->m_node = cp->mnp;
477 		cp->cnp->m_last = cp->mnp;
478 	} else {
479 		if (gt->c_last)
480 			gt->c_last->next = cp->cnp;
481 		else
482 			gt->c_node = cp->cnp;
483 		gt->c_last = cp->cnp;
484 	}
485 	gt->c_m_node = cp->mnp;
486 }
487 
488 int
489 connect_invalid_entry(struct cache_pack *cp, struct msg_pack *mp)
490 {
491 #ifdef GETTEXT_DEBUG
492 	(void) printf("*************** connect_invalid_entry(0x%p, 0x%p)\n",
493 		(void *)cp, (void *)mp);
494 	printcp(cp, 0);
495 	printmp(mp, 0);
496 #endif
497 
498 	cp->mnp = create_mnp(mp);
499 	if (!cp->mnp) {
500 		return (-1);
501 	}
502 
503 	if (!cp->cacheline) {
504 		cp->cnp = create_cnp(cp->mnp, mp);
505 		if (!cp->cnp) {
506 			free_mnp_mp(cp->mnp, mp);
507 			return (-1);
508 		}
509 	}
510 	cp->mnp->type = T_ILL_MO;
511 	connect_entry(cp);
512 
513 	return (0);
514 }
515 
516 Msg_node *
517 create_mnp(struct msg_pack *mp)
518 {
519 	Msg_node	*mnp;
520 	char	*s;
521 	size_t	msglen;
522 
523 #ifdef GETTEXT_DEBUG
524 	(void) printf("*************** create_mnp(0x%p)\n", (void *)mp);
525 	printmp(mp, 0);
526 #endif
527 
528 	mnp = (Msg_node *)calloc(1, sizeof (Msg_node));
529 	if (!mnp) {
530 		return (NULL);
531 	}
532 	msglen = mp->msgfile_len;
533 	s = (char *)malloc(msglen + 1);
534 	if (!s) {
535 		free(mnp);
536 		return (NULL);
537 	}
538 	(void) memcpy(s, mp->msgfile, msglen + 1);
539 	mnp->path = s;
540 	return (mnp);
541 }
542 
543 Cache_node *
544 create_cnp(Msg_node *mnp, struct msg_pack *mp)
545 {
546 	Cache_node	*cnp;
547 
548 #ifdef GETTEXT_DEBUG
549 	(void) printf("*************** create_cnp(0x%p, 0x%p)\n",
550 		(void *)mnp, (void *)mp);
551 	printmnp(mnp, 0);
552 	printmp(mp, 0);
553 #endif
554 
555 	cnp = (Cache_node *)calloc(1, sizeof (Cache_node));
556 	if (!cnp) {
557 		return (NULL);
558 	}
559 	cnp->hashid = mp->hash_locale;
560 	cnp->m_node = mnp;
561 	cnp->m_last = mnp;
562 
563 	return (cnp);
564 }
565 
566 void
567 free_mnp_mp(Msg_node *mnp, struct msg_pack *mp)
568 {
569 #ifdef GETTEXT_DEBUG
570 	(void) printf("*************** free_mnp_mp(0x%p, 0x%p)\n",
571 		(void *)mnp, (void *)mp);
572 	printmnp(mnp, 0);
573 	printmp(mp, 0);
574 #endif
575 
576 	if (mnp) {
577 		if (mnp->path)
578 			free(mnp->path);
579 		switch (mnp->type) {
580 		case T_SUN_MO:
581 			free(mnp->msg.sunmsg);
582 			break;
583 		case T_GNU_MO:
584 			free(mnp->msg.gnumsg);
585 			break;
586 		}
587 		free(mnp);
588 	}
589 	if (mp->addr != (caddr_t)-1) {
590 		(void) munmap(mp->addr, mp->fsz);
591 	}
592 }
593 
594 /*
595  * get_hashid
596  *
597  * Calculates the hash value from the specified string.
598  * Actual hashid will be mod(hash value, PRIME_NUMBER).
599  *
600  * hashpjw
601  * Ref: Compilers - Principles, Techniques, and Tools
602  * Aho, Sethi, and Ullman
603  */
604 unsigned int
605 get_hashid(const char *str, size_t *len)
606 {
607 	const char	*p;
608 	unsigned int	h = 0, g;
609 
610 	for (p = str; *p; p++) {
611 		h = (h << 4) + *p;
612 		g = h & 0xf0000000;
613 		if (g) {
614 			h = h ^ (g >> 24);
615 			h = h ^ g;
616 		}
617 	}
618 	if (len)
619 		*len = (size_t)(p - str);
620 	return (h);
621 }
622 
623 unsigned int
624 doswap32(unsigned int n)
625 {
626 	unsigned int	r;
627 
628 	r = (n << 24) | ((n & 0xff00) << 8) |
629 		((n >> 8) & 0xff00) | (n >> 24);
630 	return (r);
631 }
632 
633 #ifdef GETTEXT_DEBUG
634 void
635 gprintf(int level, const char *format, ...)
636 {
637 	va_list	ap;
638 
639 	va_start(ap, format);
640 
641 	while (level-- >= 0) {
642 		(void) fputs("   ", stdout);
643 	}
644 	(void) vprintf(format, ap);
645 	va_end(ap);
646 }
647 
648 void
649 printlist(void)
650 {
651 	struct domain_binding	*ppp;
652 	Gettext_t	*gt = global_gt;
653 
654 	(void) printf("=== Printing default list and regural list\n");
655 	(void) printf("   Default domain=<%s>, binding=<%s>\n",
656 	    DEFAULT_DOMAIN, defaultbind);
657 
658 	ppp = FIRSTBIND(gt);
659 	while (ppp) {
660 		(void) printf(
661 			"   domain=<%s>, binding=<%s>, codeset=<%s>\n",
662 		    ppp->domain ? ppp->domain : "(null)",
663 			ppp->binding ? ppp->binding : "(null)",
664 			ppp->codeset ? ppp->codeset : "(null)");
665 		ppp = ppp->next;
666 	}
667 }
668 
669 void
670 printmp(struct msg_pack *mp, int level)
671 {
672 	gprintf(level, "=== mp ===\n");
673 	gprintf(level, "   msgid1: \"%s\"\n",
674 		mp->msgid1 ? mp->msgid1 : "(null)");
675 	gprintf(level, "   msgid2: \"%s\"\n",
676 		mp->msgid2 ? mp->msgid2 : "(null)");
677 	gprintf(level, "   n: %d\n", mp->n);
678 	gprintf(level, "   plural: %d\n", mp->plural);
679 	gprintf(level, "   category: \"%s\"\n",
680 		category_name[mp->category]);
681 	gprintf(level, "   domain: \"%s\"\n",
682 		mp->domain ? mp->domain : "(null)");
683 	gprintf(level, "   binding: \"%s\"\n",
684 		mp->binding ? mp->binding : "(null)");
685 	gprintf(level, "   msgfile: \"%s\"\n",
686 		mp->msgfile ? mp->msgfile : "(null)");
687 	gprintf(level, "   locale: \"%s\"\n",
688 		mp->locale ? mp->locale : "(null)");
689 	gprintf(level, "   language: \"%s\"\n",
690 		mp->language ? mp->language : "(null)");
691 	gprintf(level, "   hash_locale: %d\n", mp->hash_locale);
692 	gprintf(level, "   addr: 0x%p\n", mp->addr);
693 	gprintf(level, "   fsz: %d\n", mp->fsz);
694 	gprintf(level, "   trusted: %d\n", mp->trusted);
695 }
696 
697 void
698 printsunmsg(Msg_s_node *smnp, int level)
699 {
700 	gprintf(level, "=== sunmsg ===\n");
701 	gprintf(level, "   msg_file_info: 0x%p\n",
702 		(void *)smnp->msg_file_info);
703 	gprintf(level, "      msg_mid: %d\n",
704 		smnp->msg_file_info->msg_mid);
705 	gprintf(level, "      msg_count: %d\n",
706 		smnp->msg_file_info->msg_count);
707 	gprintf(level, "      str_count_msgid: %d\n",
708 		smnp->msg_file_info->str_count_msgid);
709 	gprintf(level, "      str_count_msgstr: %d\n",
710 		smnp->msg_file_info->str_count_msgstr);
711 	gprintf(level, "      msg_struct_size: %d\n",
712 		smnp->msg_file_info->msg_struct_size);
713 	gprintf(level, "   msg_list: 0x%p\n",
714 		(void *)smnp->msg_list);
715 	gprintf(level, "   msg_ids: 0x%p\n",
716 		(void *)smnp->msg_ids);
717 	gprintf(level, "   msg_strs: 0x%p\n",
718 		(void *)smnp->msg_strs);
719 }
720 
721 void
722 printgnumsg(Msg_g_node *gmnp, int level)
723 {
724 	gprintf(level, "=== gnumsg ===\n");
725 	gprintf(level, "   msg_file_info: 0x%p\n",
726 		(void *)gmnp->msg_file_info);
727 	gprintf(level, "      magic: 0x%x\n",
728 		gmnp->msg_file_info->magic);
729 	gprintf(level, "      revision: %d\n",
730 		SWAP(gmnp, gmnp->msg_file_info->revision));
731 	gprintf(level, "      num_of_str: %d\n",
732 		SWAP(gmnp, gmnp->msg_file_info->num_of_str));
733 	gprintf(level, "      off_msgid_tbl: %d\n",
734 		SWAP(gmnp, gmnp->msg_file_info->off_msgid_tbl));
735 	gprintf(level, "      off_msgstr_tbl: %d\n",
736 		SWAP(gmnp, gmnp->msg_file_info->off_msgstr_tbl));
737 	gprintf(level, "      sz_hashtbl: %d\n",
738 		SWAP(gmnp, gmnp->msg_file_info->sz_hashtbl));
739 	gprintf(level, "      off_hashtbl: %d\n",
740 		SWAP(gmnp, gmnp->msg_file_info->off_hashtbl));
741 	gprintf(level, "   hash_table: 0x%p\n",
742 		(void *)gmnp->hash_table);
743 	gprintf(level, "   header_flag: %08x\n",
744 		gmnp->header_flag);
745 	gprintf(level, "   src_encoding: \"%s\"\n",
746 		gmnp->src_encoding ? gmnp->src_encoding : "(null)");
747 	gprintf(level, "   dst_encoding: \"%s\"\n",
748 		gmnp->dst_encoding ? gmnp->dst_encoding : "(null)");
749 	gprintf(level, "   nplurals: %d\n",
750 		gmnp->nplurals);
751 	gprintf(level, "   plural: 0x%p\n",
752 		(void *)gmnp->plural);
753 	if (gmnp->plural)
754 		printexpr(gmnp->plural, level+1);
755 	gprintf(level, "   fd: 0x%p\n", (void *)gmnp->fd);
756 	gprintf(level, "   conv_msgstr: 0x%p\n",
757 		(void *)gmnp->conv_msgstr);
758 }
759 
760 void
761 printexpr(struct expr *e, int level)
762 {
763 	static const char	*op_name[] = {
764 		"NULL", "INIT", "EXP",
765 		"NUM", "VAR", "?", ":", "||",
766 		"&&", "==", "!=", ">", "<",
767 		">=", "<=", "+", "-", "*", "/",
768 		"%", "!", "(", ")", "ERR"
769 	};
770 	switch (GETOPNUM(e->op)) {
771 	case 0:
772 		switch (GETTYPE(e->op)) {
773 		case T_NUM:
774 			gprintf(level, "NUM(%d)\n", e->num);
775 			break;
776 		case T_VAR:
777 			gprintf(level, "VAR(n)\n");
778 			break;
779 		}
780 		break;
781 	case 1:
782 		gprintf(level, "OP: !\n");
783 		printexpr(e->nodes[0], level+1);
784 		break;
785 	case 2:
786 		gprintf(level, "OP: %s\n", op_name[GETTYPE(e->op)]);
787 		printexpr(e->nodes[0], level+1);
788 		printexpr(e->nodes[1], level+1);
789 		break;
790 	case 3:
791 		gprintf(level, "OP: ?\n");
792 
793 		printexpr(e->nodes[0], level+1);
794 		printexpr(e->nodes[1], level+1);
795 		printexpr(e->nodes[2], level+1);
796 		break;
797 	}
798 }
799 
800 
801 void
802 printmnp(Msg_node *mnp, int level)
803 {
804 	gprintf(level, "=== mnp ===\n");
805 
806 	gprintf(level, "   type: \"%s\"\n",
807 		mnp->type == T_ILL_MO ? "T_ILL_MO" :
808 		mnp->type == T_SUN_MO ? "T_SUN_MO" :
809 		mnp->type == T_GNU_MO ? "T_GNU_MO" :
810 		"UNKNOWN TYPE");
811 	gprintf(level, "   path: \"%s\"\n",
812 		mnp->path ? mnp->path : "(null)");
813 	gprintf(level, "   msg_file_trusted: %d\n",
814 		mnp->trusted);
815 	if (mnp->type == T_SUN_MO)
816 		printsunmsg(mnp->msg.sunmsg, level+1);
817 	else if (mnp->type == T_GNU_MO)
818 		printgnumsg(mnp->msg.gnumsg, level+1);
819 	gprintf(level, "   next: 0x%p\n", (void *)mnp->next);
820 }
821 
822 void
823 printcnp(Cache_node *cnp, int level)
824 {
825 	gprintf(level, "=== cnp ===\n");
826 
827 	gprintf(level, "   hashid: %d\n", cnp->hashid);
828 	gprintf(level, "   m_node: 0x%p\n", (void *)cnp->m_node);
829 	if (cnp->m_node)
830 		printmnp(cnp->m_node, level+1);
831 	gprintf(level, "   m_last: 0x%p\n", (void *)cnp->m_last);
832 	if (cnp->m_last)
833 		printmnp(cnp->m_last, level+1);
834 	gprintf(level, "   n_node: 0x%p\n", (void *)cnp->n_node);
835 	gprintf(level, "   next: 0x%p\n", (void *)cnp->next);
836 }
837 
838 void
839 printcp(struct cache_pack *cp, int level)
840 {
841 	gprintf(level, "=== cp ===\n");
842 	gprintf(level, "   cacheline: %d\n", cp->cacheline);
843 	gprintf(level, "   mnp: 0x%p\n", (void *)cp->mnp);
844 	if (cp->mnp)
845 		printmnp(cp->mnp, level+1);
846 	gprintf(level, "   cnp: 0x%p\n", (void *)cp->cnp);
847 	if (cp->cnp)
848 		printcnp(cp->cnp, level+1);
849 	gprintf(level, "   node_hash: 0x%p\n", (void *)cp->node_hash);
850 	if (cp->node_hash)
851 		printcnp(cp->node_hash, level+1);
852 }
853 #endif
854