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 * 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++) != '\0') 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++) != '\0') 130 ; 131 *(p - 1) = '/'; 132 while ((*p++ = *catstr++) != '\0') 133 ; 134 *(p - 1) = '/'; 135 q = mp->domain; 136 while ((*p = *q++) != '\0') 137 p++; 138 q = MSGFILESUFFIX; 139 while ((*p++ = *q++) != '\0') 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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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