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