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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include "lint.h"
28#include "mtlib.h"
29#include <ctype.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <sys/types.h>
34#include <unistd.h>
35#include <sys/mman.h>
36#include <langinfo.h>
37#include "libc.h"
38#include "_loc_path.h"
39#include "msgfmt.h"
40#include "gettext.h"
41
42#ifdef GETTEXT_DEBUG
43#include "plural_parser.h"
44#include <stdarg.h>
45#endif
46
47static const char	*category_name[] = {
48	"LC_CTYPE",
49	"LC_NUMERIC",
50	"LC_TIME",
51	"LC_COLLATE",
52	"LC_MONETARY",
53	"LC_MESSAGES"
54};
55
56static const int	category_name_len[] = {
57	8,
58	10,
59	7,
60	10,
61	11,
62	11
63};
64
65/*
66 * mk_msgfile
67 *
68 * INPUT
69 * mp -	uses the following members:
70 * 	msgfile  - buffer to store the pathname to the message file
71 *	binding  - directory pathname bound to specified domain
72 *	cblen    - length of binding
73 *	locale   - locale name
74 *	domain   - domain name
75 *	category - category
76 *	domain_len - length of domain name
77 *
78 * OUTPUT
79 * mp->msgfile - pathname to the message file is stored
80 *
81 * RETURN
82 * mp->msgfile is returned
83 */
84char *
85mk_msgfile(struct msg_pack *mp)
86{
87	const char *q;
88	char	*p;
89	const char	*catstr;
90	uint32_t	cblen, loclen, catlen, totallen;
91
92#ifdef GETTEXT_DEBUG
93	gprintf(0, "*************** mk_msgfile(0x%p)\n", (void *)mp);
94	printmp(mp, 1);
95#endif
96
97	p = mp->msgfile;
98	q = mp->binding;
99	while (*p = *q++)
100		p++;
101	cblen = (uint32_t)(p - mp->msgfile);
102	if (*(p - 1) != '/') {
103		/*
104		 * if the last character of binding
105		 * isn't a '/', adding '/'.
106		 */
107		if (cblen + 1 >= MAXPATHLEN) {
108			/* MAXPATHLEN includes a null termination */
109			return (NULL);
110		}
111		*p++ = '/';
112		cblen++;
113	}
114
115	loclen = strlen(mp->locale);
116	catstr = category_name[mp->category];
117	catlen = (uint32_t)category_name_len[mp->category];
118	/*
119	 * totallen is the length of the msgfile
120	 * pathname excluding a null termination.
121	 */
122
123	totallen = cblen + loclen + 1 + catlen + 1 +
124	    mp->domain_len + MSGFILESUFFIXLEN;
125	if (totallen >= MAXPATHLEN)
126		return (NULL);
127
128	q = mp->locale;
129	while (*p++ = *q++)
130		;
131	*(p - 1) = '/';
132	while (*p++ = *catstr++)
133		;
134	*(p - 1) = '/';
135	q = mp->domain;
136	while (*p = *q++)
137		p++;
138	q = MSGFILESUFFIX;
139	while (*p++ = *q++)
140		;
141
142#ifdef GETTEXT_DEBUG
143	gprintf(0, "*************** Exiting mk_msgfile\n");
144	gprintf(0, "mp->msgfile: \"%s\"\n", mp->msgfile);
145#endif
146
147	return (mp->msgfile);
148}
149
150/*
151 * check_cache
152 *
153 * INPUT
154 * mp - may use the following members:
155 *	msgfile - pathname to the message catalog file
156 *	hash_domain - hash id of this domain
157 *
158 * RETURN
159 * non-NULL
160 *	pointer to the Msg_node object of the current message catalog
161 *	found in the cache
162 *
163 * NULL
164 *	this message catalog does not exist in the cache
165 */
166Msg_node *
167check_cache(struct msg_pack *mp)
168{
169	Msg_node	*cur_msg, *mnp;
170	Gettext_t	*gt = global_gt;
171
172#ifdef GETTEXT_DEBUG
173	gprintf(0, "*************** check_cache(0x%p)\n", mp);
174	printmp(mp, 1);
175#endif
176
177	cur_msg = gt->c_m_node;		/* current Msg_node */
178	if (cur_msg &&
179	    cur_msg->hashid == mp->hash_domain &&
180	    strcmp(cur_msg->path, mp->msgfile) == 0) {
181		/*
182		 * msgfile is the same as the previous message file
183		 */
184#ifdef GETTEXT_DEBUG
185		gprintf(0, "*** cache found\n");
186		gprintf(0, "************* exiting check_cache\n");
187		printmnp(cur_msg, 1);
188#endif
189		return (cur_msg);
190	}
191	mnp = gt->m_node;
192	while (mnp) {
193#ifdef GETTEXT_DEBUG
194		gprintf(0, "========== descending the list\n");
195		gprintf(0, "  hashid: %d, hash_domain: %d\n",
196		    mnp->hashid, mp->hash_domain);
197		printmnp(mnp, 1);
198#endif
199		if (mnp->hashid == mp->hash_domain &&
200		    strcmp(mnp->path, mp->msgfile) == 0) {
201#ifdef GETTEXT_DEBUG
202			gprintf(0, "*** cache found\n");
203			gprintf(0, "******* exiting check_cache\n");
204			printmnp(mnp, 1);
205#endif
206			gt->c_m_node = mnp;
207			return (mnp);
208		}
209		mnp = mnp->next;
210	}
211
212#ifdef GETTEXT_DEBUG
213	gprintf(0, "*** cache not found\n");
214	gprintf(0, "******* exiting check_cache\n");
215#endif
216	return (NULL);
217}
218
219char *
220get_codeset(const char *domain)
221{
222	char	*codeset;
223
224#ifdef GETTEXT_DEBUG
225	gprintf(0, "*************** get_codeset(\"%s\")\n",
226	    domain ? domain : "(null)");
227#endif
228
229	codeset = _real_bindtextdomain_u(domain, NULL, TP_CODESET);
230	if (codeset == NULL) {
231		/* no codeset is bound to this domain */
232		codeset = nl_langinfo(CODESET);
233	}
234#ifdef GETTEXT_DEBUG
235	gprintf(0, "*************** existing get_codeset(\"%s\")\n",
236	    domain ? domain : "(null)");
237	gprintf(0, "                = \"%s\"\n", codeset);
238#endif
239
240	return (codeset);
241}
242
243/*
244 * get_hashid (hashpjw)
245 *
246 * Calculates the hash value from the specified string.
247 * Actual hashid will be mod(hash value, PRIME_NUMBER).
248 *
249 * Ref: Compilers - Principles, Techniques, and Tools
250 * Aho, Sethi, and Ullman
251 */
252uint32_t
253get_hashid(const char *str, uint32_t *len)
254{
255	const unsigned char	*p = (unsigned char *)str;
256	uint32_t	h = 0;
257	uint32_t	g;
258
259	for (; *p; p++) {
260		h = (h << 4) + *p;
261		g = h & 0xf0000000;
262		if (g) {
263			h = h ^ (g >> 24);
264			h = h ^ g;
265		}
266	}
267
268	if (len)
269		*len = (uint32_t)(p - (unsigned char *)str);
270	return (h);
271}
272
273uint32_t
274doswap32(uint32_t n)
275{
276	uint32_t	r;
277
278	r = (n << 24) | ((n & 0xff00) << 8) |
279	    ((n >> 8) & 0xff00) | (n >> 24);
280	return (r);
281}
282
283#ifdef GETTEXT_DEBUG
284static uint32_t
285search_msg(Msg_g_node *p, const char *id, uint32_t hash_val,
286    struct gnu_msg_ent *m)
287{
288	char	*base = (char *)p->msg_file_info;
289	uint32_t	hash_size, num_of_str, i, idx, inc;
290	char	*ms;
291
292	num_of_str = p->num_of_str;
293	hash_size = p->hash_size;
294	idx = hash_val % hash_size;
295	inc = 1 + (hash_val % (hash_size - 2));
296
297	while ((i = p->hash_table[idx]) != 0) {
298		ms = (i <= num_of_str) ?
299		    base + SWAP(p, m[i-1].offset) :
300		    p->mchunk + p->d_msg[MSGID][i-num_of_str-1].offset;
301		if (strcmp(id, ms) == 0) {
302			/* found */
303			return (i);
304		}
305		idx = (idx + inc) % hash_size;
306	}
307	/* not found */
308	return (0);
309}
310
311void
312print_rev1_info(Msg_g_node *p)
313{
314	char	*base = (char *)p->msg_file_info;
315	struct gnu_msg_info	*header = p->msg_file_info;
316	struct gnu_msg_ent	*m;
317	uint32_t	hv, hidx;
318	char	*ms;
319	enum gnu_msgidstr	v;
320	int	x;
321
322#ifdef	GETTEXT_DEBUG_DYMMSG
323	gprintf(0, "******** dynamic msgid/msgstr\n");
324	for (v = MSGID; v <= MSGSTR; v++) {
325		for (x = 0; x < p->num_of_d_str; x++) {
326			gprintf(0, "len: %u\n", p->d_msg[v][x].len);
327			gprintf(0, "str: \"%s\"\n",
328			    p->mchunk + p->d_msg[v][x].offset);
329		}
330	}
331#endif
332#ifdef	GETTEXT_DEBUG_HASHTBL
333	gprintf(0, "******** dynamic hash table\n");
334	for (x = 0; x < p->hash_size; x++) {
335		gprintf(0, "%d: %u\n", x, p->hash_table[x]);
336	}
337#endif
338#ifdef	GETTEXT_DEBUG_CHECK_STMSGID
339	gprintf(0, "******** sanity check of static msgid\n");
340	m = (struct gnu_msg_ent *)(uintptr_t)
341	    (base + SWAP(p, header->off_msgid_tbl));
342	for (x = 0; x < p->num_of_str; x++) {
343		ms = base + SWAP(p, m[x].offset);
344		gprintf(0, "\"%s\"\n", ms);
345		hv = get_hashid(ms, NULL);
346		hidx = search_msg(p, ms, hv, m);
347		if (hidx == 0) {
348			gprintf(0,
349			    "failed to find this msg in the hash table\n");
350		} else {
351			if (hidx != x + 1) {
352				gprintf(0, "hash table mismatch\n");
353			}
354		}
355	}
356#endif
357#ifdef	GETTEXT_DEBUG_CHECK_DYMMSGID
358	gprintf(0, "******* sanity check of dynamic msgid\n");
359	m = (struct gnu_msg_ent *)(uintptr_t)
360	    (base + SWAP(p, header->off_msgid_tbl));
361	for (x = 0; x < p->num_of_d_str; x++) {
362		ms = p->mchunk + p->d_msg[MSGID][x].offset;
363		gprintf(0, "\"%s\"\n", ms);
364		hv = get_hashid(ms, NULL);
365		hidx = search_msg(p, ms, hv, m);
366		if (hidx == 0) {
367			gprintf(0,
368			    "failed to find this msg in the hash table\n");
369		} else {
370			if (hidx != x + p->num_of_str + 1) {
371				gprintf(0, "hash table mismatch\n");
372			}
373		}
374	}
375#endif
376}
377
378void
379gprintf(int level, const char *format, ...)
380{
381	va_list	ap;
382
383	va_start(ap, format);
384
385	while (level-- > 0) {
386		(void) fputs("   ", stdout);
387	}
388	(void) vprintf(format, ap);
389	va_end(ap);
390
391	(void) fflush(stdout);
392}
393
394void
395printlist(void)
396{
397	struct domain_binding	*ppp;
398	Gettext_t	*gt = global_gt;
399
400	gprintf(0, "=== Printing default list and regural list\n");
401	gprintf(0, "   Default domain=<%s>, binding=<%s>\n",
402	    DEFAULT_DOMAIN, defaultbind);
403
404	ppp = FIRSTBIND(gt);
405	while (ppp) {
406		gprintf(0, "   domain=<%s>, binding=<%s>, codeset=<%s>\n",
407		    ppp->domain ? ppp->domain : "(null)",
408		    ppp->binding ? ppp->binding : "(null)",
409		    ppp->codeset ? ppp->codeset : "(null)");
410		ppp = ppp->next;
411	}
412	(void) fflush(stdout);
413}
414
415void
416printmp(struct msg_pack *mp, int level)
417{
418	gprintf(level, "=== mp ===\n");
419	gprintf(level, "   msgid1: \"%s\"\n",
420	    mp->msgid1 ? mp->msgid1 : "(null)");
421	gprintf(level, "   msgid2: \"%s\"\n",
422	    mp->msgid2 ? mp->msgid2 : "(null)");
423	gprintf(level, "   msgfile: \"%s\"\n",
424	    mp->msgfile ? mp->msgfile : "(null)");
425	gprintf(level, "   domain: \"%s\"\n",
426	    mp->domain ? mp->domain : "(null)");
427	gprintf(level, "   binding: \"%s\"\n",
428	    mp->binding ? mp->binding : "(null)");
429	gprintf(level, "   locale: \"%s\"\n",
430	    mp->locale ? mp->locale : "(null)");
431	gprintf(level, "   language: \"%s\"\n",
432	    mp->language ? mp->language : "(null)");
433	gprintf(level, "   addr: 0x%p\n", mp->addr);
434	gprintf(level, "   fsz: %d\n", mp->fsz);
435	gprintf(level, "   hash_domain: %d\n", mp->hash_domain);
436	gprintf(level, "   domain_len: %d\n", mp->domain_len);
437	gprintf(level, "   n: %d\n", mp->n);
438	gprintf(level, "   category: \"%s\"\n",
439	    category_name[mp->category]);
440	gprintf(level, "   plural: %d\n", mp->plural);
441	gprintf(level, "   nlsp: %d\n", mp->nlsp);
442	gprintf(level, "   trusted: %d\n", mp->trusted);
443	gprintf(level, "   status: %d\n", mp->status);
444}
445
446void
447printsunmsg(Msg_s_node *smnp, int level)
448{
449	gprintf(level, "=== sunmsg ===\n");
450	gprintf(level, "   msg_file_info: 0x%p\n",
451	    (void *)smnp->msg_file_info);
452	gprintf(level, "      msg_mid: %d\n",
453	    smnp->msg_file_info->msg_mid);
454	gprintf(level, "      msg_count: %d\n",
455	    smnp->msg_file_info->msg_count);
456	gprintf(level, "      str_count_msgid: %d\n",
457	    smnp->msg_file_info->str_count_msgid);
458	gprintf(level, "      str_count_msgstr: %d\n",
459	    smnp->msg_file_info->str_count_msgstr);
460	gprintf(level, "      msg_struct_size: %d\n",
461	    smnp->msg_file_info->msg_struct_size);
462	gprintf(level, "   msg_list: 0x%p\n",
463	    (void *)smnp->msg_list);
464	gprintf(level, "   msg_ids: 0x%p\n",
465	    (void *)smnp->msg_ids);
466	gprintf(level, "   msg_strs: 0x%p\n",
467	    (void *)smnp->msg_strs);
468}
469
470void
471printgnumsg(Msg_g_node *gmnp, int level)
472{
473	gprintf(level, "=== gnumsg ===\n");
474	gprintf(level, "   msg_file_info: 0x%p\n", gmnp->msg_file_info);
475	gprintf(level, "      magic: 0x%x\n",
476	    gmnp->msg_file_info->magic);
477	gprintf(level, "      revision: %d\n",
478	    SWAP(gmnp, gmnp->msg_file_info->revision));
479	gprintf(level, "      num_of_str: %d\n",
480	    SWAP(gmnp, gmnp->msg_file_info->num_of_str));
481	gprintf(level, "      off_msgid_tbl: %d\n",
482	    SWAP(gmnp, gmnp->msg_file_info->off_msgid_tbl));
483	gprintf(level, "      off_msgstr_tbl: %d\n",
484	    SWAP(gmnp, gmnp->msg_file_info->off_msgstr_tbl));
485	gprintf(level, "      sz_hashtbl: %d\n",
486	    SWAP(gmnp, gmnp->msg_file_info->sz_hashtbl));
487	gprintf(level, "      off_hashtbl: %d\n",
488	    SWAP(gmnp, gmnp->msg_file_info->off_hashtbl));
489	if (gmnp->flag & ST_REV1) {
490		struct gnu_msg_rev1_info	*a =
491		    (struct gnu_msg_rev1_info *)(uintptr_t)
492		    ((char *)gmnp->msg_file_info +
493		    sizeof (struct gnu_msg_info));
494		gprintf(level, "      num_of_dynamic_macro: %d\n",
495		    SWAP(gmnp, a->num_of_dynamic_macro));
496		gprintf(level, "      off_dynamic_macro: %d\n",
497		    SWAP(gmnp, a->off_dynamic_macro));
498		gprintf(level, "      num_of_dynamic_str: %d\n",
499		    SWAP(gmnp, a->num_of_dynamic_str));
500		gprintf(level, "      off_dynamic_msgid_tbl: %d\n",
501		    SWAP(gmnp, a->off_dynamic_msgid_tbl));
502		gprintf(level, "      off_dynamic_msgstr_tbl: %d\n",
503		    SWAP(gmnp, a->off_dynamic_msgstr_tbl));
504	}
505	gprintf(level, "   fsize: %lu\n", gmnp->fsize);
506	gprintf(level, "   flag: %08x\n", gmnp->flag);
507	gprintf(level, "   num_of_str: %u\n", gmnp->num_of_str);
508	gprintf(level, "   num_of_d_str: %u\n", gmnp->num_of_d_str);
509	gprintf(level, "   hash_size: %u\n", gmnp->hash_size);
510	gprintf(level, "   hash_table: 0x%p\n", (void *)gmnp->hash_table);
511	gprintf(level, "   d_msgid: 0x%p\n", (void *)gmnp->d_msg[MSGID]);
512	gprintf(level, "   d_msgstr: 0x%p\n", (void *)gmnp->d_msg[MSGSTR]);
513	gprintf(level, "   mchunk: 0x%p\n", (void *)gmnp->mchunk);
514
515	gprintf(level, "   src_encoding: \"%s\"\n",
516	    gmnp->src_encoding ? gmnp->src_encoding : "(null)");
517	gprintf(level, "   dst_encoding: \"%s\"\n",
518	    gmnp->dst_encoding ? gmnp->dst_encoding : "(null)");
519	gprintf(level, "   nplurals: %d\n",
520	    gmnp->nplurals);
521	gprintf(level, "   plural: 0x%p\n",
522	    (void *)gmnp->plural);
523	if (gmnp->plural)
524		printexpr(gmnp->plural, level+1);
525	gprintf(level, "   fd: 0x%p\n", (void *)gmnp->fd);
526	gprintf(level, "   conv_msgstr: 0x%p\n",
527	    (void *)gmnp->conv_msgstr);
528}
529
530void
531printexpr(struct expr *e, int level)
532{
533	static const char	*op_name[] = {
534	    "NULL", "INIT", "EXP",
535	    "NUM", "VAR", "?", ":", "||",
536	    "&&", "==", "!=", ">", "<",
537	    ">=", "<=", "+", "-", "*", "/",
538	    "%", "!", "(", ")", "ERR"
539	};
540	switch (GETOPNUM(e->op)) {
541	case 0:
542		switch (GETTYPE(e->op)) {
543		case T_NUM:
544			gprintf(level, "NUM(%d)\n", e->num);
545			break;
546		case T_VAR:
547			gprintf(level, "VAR(n)\n");
548			break;
549		}
550		break;
551	case 1:
552		gprintf(level, "OP: !\n");
553		printexpr(e->nodes[0], level+1);
554		break;
555	case 2:
556		gprintf(level, "OP: %s\n", op_name[GETTYPE(e->op)]);
557		printexpr(e->nodes[0], level+1);
558		printexpr(e->nodes[1], level+1);
559		break;
560	case 3:
561		gprintf(level, "OP: ?\n");
562
563		printexpr(e->nodes[0], level+1);
564		printexpr(e->nodes[1], level+1);
565		printexpr(e->nodes[2], level+1);
566		break;
567	}
568}
569
570
571void
572printmnp(Msg_node *mnp, int level)
573{
574	gprintf(level, "=== mnp ===\n");
575
576	gprintf(level, "   hashid: %d\n", mnp->hashid);
577	gprintf(level, "   type: \"%s\"\n",
578	    mnp->type == T_ILL_MO ? "T_ILL_MO" :
579	    mnp->type == T_SUN_MO ? "T_SUN_MO" :
580	    mnp->type == T_GNU_MO ? "T_GNU_MO" :
581	    "UNKNOWN TYPE");
582	gprintf(level, "   path: \"%s\"\n",
583	    mnp->path ? mnp->path : "(null)");
584	gprintf(level, "   msg_file_trusted: %d\n",
585	    mnp->trusted);
586	if (mnp->type == T_SUN_MO)
587		printsunmsg(mnp->msg.sunmsg, level+1);
588	else if (mnp->type == T_GNU_MO)
589		printgnumsg(mnp->msg.gnumsg, level+1);
590	gprintf(level, "   next: 0x%p\n", (void *)mnp->next);
591}
592
593void
594printnls(Nls_node *n, int level)
595{
596	gprintf(level, "=== nls ===\n");
597	gprintf(level, "   domain: \"%s\"\n", n->domain ? n->domain : "NULL");
598	gprintf(level, "   locale: \"%s\"\n", n->locale ? n->locale : "NULL");
599	gprintf(level, "   nlspath: \"%s\"\n", n->nlspath ? n->nlspath :
600	    "NULL");
601	gprintf(level, "   next: 0x%p\n", n->next);
602}
603
604void
605printdbind(Dbinding *d, int level)
606{
607	gprintf(level, "=== dbind ===\n");
608	gprintf(level, "   domain: \"%s\"\n", d->domain ? d->domain : "NULL");
609	gprintf(level, "   binding: \"%s\"\n", d->binding ? d->binding :
610	    "NULL");
611	gprintf(level, "   codeset: \"%s\"\n", d->codeset ? d->codeset :
612	    "NULL");
613	gprintf(level, "   next: 0x%p\n", d->next);
614}
615
616void
617printgt(Gettext_t *gt, int level)
618{
619	gprintf(level, "=== gt ===\n");
620	gprintf(level, "   cur_domain: \"%s\"\n", gt->cur_domain);
621	if (gt->dbind) {
622		printdbind(gt->dbind, level+1);
623	} else {
624		gprintf(level, "   dbind: NULL\n");
625	}
626	if (gt->m_node) {
627		printmnp(gt->m_node, level + 1);
628	} else {
629		gprintf(level, "   m_node: NULL\n");
630	}
631	if (gt->n_node) {
632		printnls(gt->n_node, level + 1);
633	} else {
634		gprintf(level, "   n_node: NULL\n");
635	}
636	if (gt->c_m_node) {
637		printmnp(gt->c_m_node, level + 1);
638	} else {
639		gprintf(level, "   c_m_node: NULL\n");
640	}
641	if (gt->c_n_node) {
642		printnls(gt->c_n_node, level + 1);
643	} else {
644		gprintf(level, "   c_n_node: NULL\n");
645	}
646}
647
648#endif
649