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
47 static const char *category_name[] = {
48 "LC_CTYPE",
49 "LC_NUMERIC",
50 "LC_TIME",
51 "LC_COLLATE",
52 "LC_MONETARY",
53 "LC_MESSAGES"
54 };
55
56 static 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 */
84 char *
mk_msgfile(struct msg_pack * mp)85 mk_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 */
166 Msg_node *
check_cache(struct msg_pack * mp)167 check_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
219 char *
get_codeset(const char * domain)220 get_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 */
252 uint32_t
get_hashid(const char * str,uint32_t * len)253 get_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
273 uint32_t
doswap32(uint32_t n)274 doswap32(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
284 static uint32_t
search_msg(Msg_g_node * p,const char * id,uint32_t hash_val,struct gnu_msg_ent * m)285 search_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
311 void
print_rev1_info(Msg_g_node * p)312 print_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
378 void
gprintf(int level,const char * format,...)379 gprintf(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
394 void
printlist(void)395 printlist(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
415 void
printmp(struct msg_pack * mp,int level)416 printmp(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
446 void
printsunmsg(Msg_s_node * smnp,int level)447 printsunmsg(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
470 void
printgnumsg(Msg_g_node * gmnp,int level)471 printgnumsg(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
530 void
printexpr(struct expr * e,int level)531 printexpr(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
571 void
printmnp(Msg_node * mnp,int level)572 printmnp(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
593 void
printnls(Nls_node * n,int level)594 printnls(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
604 void
printdbind(Dbinding * d,int level)605 printdbind(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
616 void
printgt(Gettext_t * gt,int level)617 printgt(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