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 * 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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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