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 <locale.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <sys/types.h> 37 #include <sys/mman.h> 38 #include <sys/param.h> 39 #include <sys/stat.h> 40 #include <libintl.h> 41 #include <thread.h> 42 #include <synch.h> 43 #include <limits.h> 44 #include <unistd.h> 45 #include "libc.h" 46 #include "_loc_path.h" 47 #include "msgfmt.h" 48 #include "gettext.h" 49 #include "nlspath_checks.h" 50 51 static int process_nlspath(const char *, const char *, 52 const char *, char **); 53 static char *replace_nls_option(char *, const char *, char *, 54 char *, char *, char *, char *); 55 56 char * 57 _real_gettext_u(const char *domain, const char *msgid1, const char *msgid2, 58 unsigned long int ln, int category, int plural) 59 { 60 char msgfile[MAXPATHLEN]; /* 1024 */ 61 char mydomain[TEXTDOMAINMAX + 1]; /* 256 + 1 */ 62 char *cur_binding; /* points to current binding in list */ 63 char *cur_locale, *cur_domain, *result, *nlspath; 64 char *msgloc, *cb, *cur_domain_binding; 65 char *language; 66 unsigned int n = (unsigned int)ln; /* we don't need long for n */ 67 uint32_t cur_domain_len, cblen; 68 uint32_t hash_domain; 69 struct msg_pack *mp, omp; 70 71 #ifdef GETTEXT_DEBUG 72 gprintf(0, "*************** _real_gettext_u(\"%s\", \"%s\", " 73 "\"%s\", %d, %d, %d)\n", 74 domain ? domain : "NULL", msgid1 ? msgid1 : "NULL", 75 msgid2 ? msgid2 : "NULL", n, category, plural); 76 gprintf(0, "***************** global_gt: 0x%p\n", global_gt); 77 printgt(global_gt, 1); 78 #endif 79 80 if (msgid1 == NULL) 81 return (NULL); 82 83 mp = memset(&omp, 0, sizeof (omp)); /* msg pack */ 84 85 /* 86 * category may be LC_MESSAGES or LC_TIME 87 * locale contains the value of 'category' 88 */ 89 cur_locale = setlocale(category, NULL); 90 91 language = getenv("LANGUAGE"); /* for GNU */ 92 if (language) { 93 if (!*language || strchr(language, '/') != NULL) { 94 /* 95 * LANGUAGE is an empty string or 96 * LANGUAGE contains '/'. 97 * Ignore it. 98 */ 99 language = NULL; 100 } 101 } 102 103 /* 104 * Query the current domain if domain argument is NULL pointer 105 */ 106 mydomain[0] = '\0'; 107 if (domain == NULL) { 108 /* 109 * if NULL is specified for domainname, 110 * use the currently bound domain. 111 */ 112 cur_domain = _textdomain_u(NULL, mydomain); 113 } else if (!*domain) { 114 /* 115 * if an empty string is specified 116 */ 117 cur_domain = DEFAULT_DOMAIN; 118 } else { 119 cur_domain = (char *)domain; 120 } 121 122 hash_domain = get_hashid(cur_domain, &cur_domain_len); 123 if (cur_domain_len > TEXTDOMAINMAX) { 124 /* domain is invalid, return msg_id */ 125 DFLTMSG(result, msgid1, msgid2, n, plural); 126 return (result); 127 } 128 129 nlspath = getenv("NLSPATH"); /* get the content of NLSPATH */ 130 if (nlspath == NULL || !*nlspath) { 131 /* no NLSPATH is defined in the environ */ 132 if ((*cur_locale == 'C') && (*(cur_locale + 1) == '\0')) { 133 /* 134 * If C locale, 135 * return the original msgid immediately. 136 */ 137 DFLTMSG(result, msgid1, msgid2, n, plural); 138 return (result); 139 } 140 nlspath = NULL; 141 } else { 142 /* NLSPATH is set */ 143 int ret; 144 145 msgloc = setlocale(LC_MESSAGES, NULL); 146 147 ret = process_nlspath(cur_domain, msgloc, 148 (const char *)nlspath, &cur_binding); 149 if (ret == -1) { 150 /* error occurred */ 151 DFLTMSG(result, msgid1, msgid2, n, plural); 152 return (result); 153 } else if (ret == 0) { 154 nlspath = NULL; 155 } 156 } 157 158 cur_domain_binding = _real_bindtextdomain_u(cur_domain, 159 NULL, TP_BINDING); 160 if (cur_domain_binding == NULL) { 161 DFLTMSG(result, msgid1, msgid2, n, plural); 162 return (result); 163 } 164 165 mp->msgid1 = msgid1; 166 mp->msgid2 = msgid2; 167 mp->msgfile = msgfile; 168 mp->domain = cur_domain; 169 mp->binding = cur_domain_binding; 170 mp->locale = cur_locale; 171 mp->language = language; 172 mp->domain_len = cur_domain_len; 173 mp->n = n; 174 mp->category = category; 175 mp->plural = plural; 176 mp->hash_domain = hash_domain; 177 178 /* 179 * Spec1170 requires that we use NLSPATH if it's defined, to 180 * override any system default variables. If NLSPATH is not 181 * defined or if a message catalog is not found in any of the 182 * components (bindings) specified by NLSPATH, dcgettext_u() will 183 * search for the message catalog in either a) the binding path set 184 * by any previous application calls to bindtextdomain() or 185 * b) the default binding path (/usr/lib/locale). Save the original 186 * binding path so that we can search it if the message catalog 187 * is not found via NLSPATH. The original binding is restored before 188 * returning from this routine because the gettext routines should 189 * not change the binding set by the application. This allows 190 * bindtextdomain() to be called once for all gettext() calls in the 191 * application. 192 */ 193 194 /* 195 * First, examine NLSPATH 196 */ 197 if (nlspath) { 198 /* 199 * NLSPATH binding has been successfully built 200 */ 201 #ifdef GETTEXT_DEBUG 202 gprintf(0, "************************** examining NLSPATH\n"); 203 gprintf(0, " cur_binding: \"%s\"\n", 204 cur_binding ? cur_binding : "(null)"); 205 #endif 206 207 mp->nlsp = 1; 208 /* 209 * cur_binding always ends with ':' before a null 210 * termination. 211 */ 212 while (*cur_binding) { 213 cb = cur_binding; 214 while (*cur_binding != ':') 215 cur_binding++; 216 cblen = cur_binding - cb; 217 cur_binding++; 218 if (cblen >= MAXPATHLEN) { 219 /* cur_binding too long */ 220 DFLTMSG(result, msgid1, msgid2, n, plural); 221 return (result); 222 } 223 224 (void) memcpy(mp->msgfile, cb, cblen); 225 *(mp->msgfile + cblen) = '\0'; 226 227 #ifdef GETTEXT_DEBUG 228 gprintf(0, "*******************" 229 "********************* \n"); 230 gprintf(0, " msgfile: \"%s\"\n", 231 msgfile ? msgfile : "(null)"); 232 gprintf(0, "*******************" 233 "********************* \n"); 234 #endif 235 result = handle_mo(mp); 236 if (result) { 237 return (result); 238 } 239 } 240 } 241 242 mp->nlsp = 0; 243 mp->binding = cur_domain_binding; 244 /* 245 * Next, examine LANGUAGE 246 */ 247 if (language) { 248 char *ret_msg; 249 ret_msg = handle_lang(mp); 250 if (ret_msg != NULL) { 251 /* valid msg found in GNU MO */ 252 return (ret_msg); 253 } 254 /* 255 * handle_lang() may have overridden locale 256 */ 257 mp->locale = cur_locale; 258 mp->status = 0; 259 } 260 261 /* 262 * Finally, handle a single binding 263 */ 264 #ifdef GETTEXT_DEBUG 265 *mp->msgfile = '\0'; 266 #endif 267 if (mk_msgfile(mp) == NULL) { 268 DFLTMSG(result, msgid1, msgid2, n, plural); 269 return (result); 270 } 271 272 result = handle_mo(mp); 273 if (result) { 274 return (result); 275 } 276 DFLTMSG(result, msgid1, msgid2, n, plural); 277 return (result); 278 } /* _real_gettext_u */ 279 280 #define ALLFREE \ 281 free_all(nlstmp, nnp, pathname, ppaths, lang) 282 283 static void 284 free_all(Nlstmp *nlstmp, Nls_node *nnp, char *pathname, 285 char *ppaths, char *lang) 286 { 287 Nlstmp *tp, *tq; 288 289 tp = nlstmp; 290 while (tp) { 291 tq = tp->next; 292 free(tp); 293 tp = tq; 294 } 295 if (nnp->locale) 296 free(nnp->locale); 297 if (nnp->domain) 298 free(nnp->domain); 299 if (pathname) 300 free(pathname); 301 if (ppaths) 302 free(ppaths); 303 if (lang) 304 free(lang); 305 free(nnp); 306 } 307 308 /* 309 * process_nlspath(): process the NLSPATH environment variable. 310 * 311 * this routine looks at NLSPATH in the environment, 312 * and will try to build up the binding list based 313 * on the settings of NLSPATH. 314 * 315 * RETURN: 316 * -1: Error occurred 317 * 0: No error, but no binding list has been built 318 * 1: No error, and a binding list has been built 319 * 320 */ 321 static int 322 process_nlspath(const char *cur_domain, const char *cur_msgloc, 323 const char *nlspath, char **binding) 324 { 325 char *s; /* generic string ptr */ 326 char *territory; /* our current territory element */ 327 char *codeset; /* our current codeset element */ 328 char *s1; /* for handling territory */ 329 char *s2; /* for handling codeset */ 330 char *lang = NULL; /* our current language element */ 331 char *ppaths = NULL; /* ptr to all of the templates */ 332 char *pathname = NULL; /* the full pathname to the file */ 333 size_t nlspath_len, domain_len, locale_len, path_len; 334 size_t ppaths_len = 0; 335 Nlstmp *nlstmp = NULL; 336 Nlstmp *pnlstmp, *qnlstmp; 337 Nls_node *cur_nls, *nnp; 338 Gettext_t *gt = global_gt; 339 340 #ifdef GETTEXT_DEBUG 341 gprintf(0, "*************** process_nlspath(%s, %s, " 342 "%s, 0x%p)\n", cur_domain, 343 cur_msgloc, nlspath, (void *)binding); 344 #endif 345 346 cur_nls = gt->c_n_node; 347 if (cur_nls && 348 (strcmp(cur_nls->domain, cur_domain) == 0 && 349 strcmp(cur_nls->locale, cur_msgloc) == 0 && 350 strcmp(cur_nls->nlspath, nlspath) == 0)) { 351 *binding = cur_nls->ppaths; 352 return (1); 353 } 354 355 nnp = gt->n_node; 356 while (nnp) { 357 if (strcmp(nnp->domain, cur_domain) == 0 && 358 strcmp(nnp->locale, cur_msgloc) == 0 && 359 strcmp(nnp->nlspath, nlspath) == 0) { 360 /* found */ 361 gt->c_n_node = nnp; 362 *binding = nnp->ppaths; 363 return (1); 364 } 365 nnp = nnp->next; 366 } 367 /* not found */ 368 369 nnp = calloc(1, sizeof (Nls_node)); 370 if (nnp == NULL) { 371 ALLFREE; 372 return (-1); 373 } 374 375 nlspath_len = strlen(nlspath); 376 locale_len = strlen(cur_msgloc); 377 domain_len = strlen(cur_domain); 378 379 lang = s = strdup(cur_msgloc); 380 if (lang == NULL) { 381 ALLFREE; 382 return (-1); 383 } 384 s1 = s2 = NULL; 385 while (*s) { 386 if (*s == '_') { 387 s1 = s; 388 *s1++ = '\0'; 389 } else if (*s == '.') { 390 s2 = s; 391 *s2++ = '\0'; 392 } 393 s++; 394 } 395 territory = s1; 396 codeset = s2; 397 398 /* 399 * now that we have the name (domain), we first look through NLSPATH, 400 * in an attempt to get the locale. A locale may be completely 401 * specified as "language_territory.codeset". NLSPATH consists 402 * of templates separated by ":" characters. The following are 403 * the substitution values within NLSPATH: 404 * %N = DEFAULT_DOMAIN 405 * %L = The value of the LC_MESSAGES category. 406 * %I = The language element from the LC_MESSAGES category. 407 * %t = The territory element from the LC_MESSAGES category. 408 * %c = The codeset element from the LC_MESSAGES category. 409 * %% = A single character. 410 * if we find one of these characters, we will carry out the 411 * appropriate substitution. 412 */ 413 pathname = malloc(MAXPATHLEN); 414 if (pathname == NULL) { 415 ALLFREE; 416 return (-1); 417 } 418 s = (char *)nlspath; /* s has a content of NLSPATH */ 419 while (*s) { /* march through NLSPATH */ 420 (void) memset(pathname, 0, MAXPATHLEN); 421 if (*s == ':') { 422 /* 423 * this loop only occurs if we have to replace 424 * ":" by "name". replace_nls_option() below 425 * will handle the subsequent ":"'s. 426 */ 427 pnlstmp = malloc(sizeof (Nlstmp)); 428 if (pnlstmp == NULL) { 429 ALLFREE; 430 return (-1); 431 } 432 433 (void) memcpy(pnlstmp->pathname, cur_domain, 434 domain_len + 1); 435 pnlstmp->len = domain_len; 436 ppaths_len += domain_len + 1; /* 1 for ':' */ 437 438 439 pnlstmp->next = NULL; 440 441 if (nlstmp == NULL) { 442 nlstmp = pnlstmp; 443 qnlstmp = pnlstmp; 444 } else { 445 qnlstmp->next = pnlstmp; 446 qnlstmp = pnlstmp; 447 } 448 449 ++s; 450 continue; 451 } 452 /* replace Substitution field */ 453 s = replace_nls_option(s, cur_domain, pathname, 454 (char *)cur_msgloc, lang, territory, codeset); 455 456 if (s == NULL) { 457 ALLFREE; 458 return (-1); 459 } 460 461 /* if we've found a valid file: */ 462 if (*pathname) { 463 /* add template to end of chain of pathnames: */ 464 pnlstmp = malloc(sizeof (Nlstmp)); 465 if (pnlstmp == NULL) { 466 ALLFREE; 467 return (-1); 468 } 469 470 path_len = strlen(pathname); 471 (void) memcpy(pnlstmp->pathname, pathname, 472 path_len + 1); 473 pnlstmp->len = path_len; 474 ppaths_len += path_len + 1; /* 1 for ':' */ 475 476 pnlstmp->next = NULL; 477 478 if (nlstmp == NULL) { 479 nlstmp = pnlstmp; 480 qnlstmp = pnlstmp; 481 } else { 482 qnlstmp->next = pnlstmp; 483 qnlstmp = pnlstmp; 484 } 485 } 486 if (*s) { 487 ++s; 488 } 489 } 490 /* 491 * now that we've handled the pathname templates, concatenate them 492 * all into the form "template1:template2:..." for _bindtextdomain_u() 493 */ 494 495 if (ppaths_len != 0) { 496 ppaths = malloc(ppaths_len + 1); 497 if (ppaths == NULL) { 498 ALLFREE; 499 return (-1); 500 } 501 *ppaths = '\0'; 502 } else { 503 ALLFREE; 504 return (0); 505 } 506 507 /* 508 * extract the path templates (fifo), and concatenate them 509 * all into a ":" separated string for _bindtextdomain_u() 510 */ 511 pnlstmp = nlstmp; 512 s = ppaths; 513 while (pnlstmp) { 514 (void) memcpy(s, pnlstmp->pathname, pnlstmp->len); 515 s += pnlstmp->len; 516 *s++ = ':'; 517 qnlstmp = pnlstmp->next; 518 free(pnlstmp); 519 pnlstmp = qnlstmp; 520 } 521 *s = '\0'; 522 nlstmp = NULL; 523 524 nnp->domain = malloc(domain_len + 1); 525 if (nnp->domain == NULL) { 526 ALLFREE; 527 return (-1); 528 } else { 529 (void) memcpy(nnp->domain, cur_domain, domain_len + 1); 530 } 531 nnp->locale = malloc(locale_len + 1); 532 if (nnp->locale == NULL) { 533 ALLFREE; 534 return (-1); 535 } else { 536 (void) memcpy(nnp->locale, cur_msgloc, locale_len + 1); 537 } 538 nnp->nlspath = malloc(nlspath_len + 1); 539 if (nnp->nlspath == NULL) { 540 ALLFREE; 541 return (-1); 542 } else { 543 (void) memcpy(nnp->nlspath, nlspath, nlspath_len + 1); 544 } 545 nnp->ppaths = ppaths; 546 547 nnp->next = gt->n_node; 548 gt->n_node = nnp; 549 gt->c_n_node = nnp; 550 551 free(pathname); 552 free(lang); 553 #ifdef GETTEXT_DEBUG 554 gprintf(0, "*************** existing process_nlspath with success\n"); 555 gprintf(0, " binding: \"%s\"\n", ppaths); 556 #endif 557 *binding = ppaths; 558 return (1); 559 } 560 561 562 /* 563 * This routine will replace substitution parameters in NLSPATH 564 * with appropiate values. 565 */ 566 static char * 567 replace_nls_option(char *s, const char *name, char *pathname, 568 char *locale, char *lang, char *territory, char *codeset) 569 { 570 char *t, *u; 571 char *limit; 572 573 t = pathname; 574 limit = pathname + MAXPATHLEN - 1; 575 576 while (*s && *s != ':') { 577 if (t < limit) { 578 /* 579 * %% is considered a single % character (XPG). 580 * %L : LC_MESSAGES (XPG4) LANG(XPG3) 581 * %l : The language element from the current locale. 582 * (XPG3, XPG4) 583 */ 584 if (*s != '%') 585 *t++ = *s; 586 else if (*++s == 'N') { 587 if (name) { 588 u = (char *)name; 589 while (*u && (t < limit)) 590 *t++ = *u++; 591 } 592 } else if (*s == 'L') { 593 if (locale) { 594 u = locale; 595 while (*u && (t < limit)) 596 *t++ = *u++; 597 } 598 } else if (*s == 'l') { 599 if (lang) { 600 u = lang; 601 while (*u && (*u != '_') && 602 (t < limit)) 603 *t++ = *u++; 604 } 605 } else if (*s == 't') { 606 if (territory) { 607 u = territory; 608 while (*u && (*u != '.') && 609 (t < limit)) 610 *t++ = *u++; 611 } 612 } else if (*s == 'c') { 613 if (codeset) { 614 u = codeset; 615 while (*u && (t < limit)) 616 *t++ = *u++; 617 } 618 } else { 619 if (t < limit) 620 *t++ = *s; 621 } 622 } else { 623 /* too long pathname */ 624 return (NULL); 625 } 626 ++s; 627 } 628 *t = '\0'; 629 return (s); 630 } 631 632 633 char * 634 _real_bindtextdomain_u(const char *domain, const char *binding, 635 int type) 636 { 637 struct domain_binding *bind, *prev; 638 Gettext_t *gt = global_gt; 639 char **binding_addr; 640 641 #ifdef GETTEXT_DEBUG 642 gprintf(0, "*************** _real_bindtextdomain_u(\"%s\", " 643 "\"%s\", \"%s\")\n", 644 (domain ? domain : ""), 645 (binding ? binding : ""), 646 (type == TP_BINDING) ? "TP_BINDING" : "TP_CODESET"); 647 #endif 648 649 /* 650 * If domain is a NULL pointer, no change will occur regardless 651 * of binding value. Just return NULL. 652 */ 653 if (domain == NULL) { 654 return (NULL); 655 } 656 657 /* 658 * Global Binding is not supported any more. 659 * Just return NULL if domain is NULL string. 660 */ 661 if (*domain == '\0') { 662 return (NULL); 663 } 664 665 /* linear search for binding, rebind if found, add if not */ 666 bind = FIRSTBIND(gt); 667 prev = NULL; /* Two pointers needed for pointer operations */ 668 669 while (bind) { 670 if (strcmp(domain, bind->domain) == 0) { 671 /* 672 * Domain found. 673 */ 674 binding_addr = (type == TP_BINDING) ? &(bind->binding) : 675 &(bind->codeset); 676 if (binding == NULL) { 677 /* 678 * if binding is null, then query 679 */ 680 return (*binding_addr); 681 } 682 /* replace existing binding with new binding */ 683 if (*binding_addr) { 684 free(*binding_addr); 685 } 686 if ((*binding_addr = strdup(binding)) == NULL) { 687 return (NULL); 688 } 689 #ifdef GETTEXT_DEBUG 690 printlist(); 691 #endif 692 return (*binding_addr); 693 } 694 prev = bind; 695 bind = bind->next; 696 } /* while (bind) */ 697 698 /* domain has not been found in the list at this point */ 699 if (binding) { 700 /* 701 * domain is not found, but binding is not NULL. 702 * Then add a new node to the end of linked list. 703 */ 704 705 if ((bind = malloc(sizeof (Dbinding))) == NULL) { 706 return (NULL); 707 } 708 if ((bind->domain = strdup(domain)) == NULL) { 709 free(bind); 710 return (NULL); 711 } 712 bind->binding = NULL; 713 bind->codeset = NULL; 714 binding_addr = (type == TP_BINDING) ? &(bind->binding) : 715 &(bind->codeset); 716 if ((*binding_addr = strdup(binding)) == NULL) { 717 free(bind->domain); 718 free(bind); 719 return (NULL); 720 } 721 bind->next = NULL; 722 723 if (prev) { 724 /* reached the end of list */ 725 prev->next = bind; 726 } else { 727 /* list was empty */ 728 FIRSTBIND(gt) = bind; 729 } 730 731 #ifdef GETTEXT_DEBUG 732 printlist(); 733 #endif 734 return (*binding_addr); 735 } else { 736 /* 737 * Query of domain which is not found in the list 738 * for bindtextdomain, returns defaultbind 739 * for bind_textdomain_codeset, returns NULL 740 */ 741 if (type == TP_BINDING) { 742 return ((char *)defaultbind); 743 } else { 744 return (NULL); 745 } 746 } /* if (binding) */ 747 748 /* Must not reach here */ 749 750 } /* _real_bindtextdomain_u */ 751 752 753 char * 754 _textdomain_u(const char *domain, char *result) 755 { 756 char *p; 757 size_t domain_len; 758 Gettext_t *gt = global_gt; 759 760 #ifdef GETTEXT_DEBUG 761 gprintf(0, "*************** _textdomain_u(\"%s\", 0x%p)\n", 762 (domain ? domain : ""), (void *)result); 763 #endif 764 765 /* Query is performed for NULL domain pointer */ 766 if (domain == NULL) { 767 (void) strcpy(result, CURRENT_DOMAIN(gt)); 768 return (result); 769 } 770 771 /* check for error. */ 772 /* 773 * domain is limited to TEXTDOMAINMAX bytes 774 * excluding a null termination. 775 */ 776 domain_len = strlen(domain); 777 if (domain_len > TEXTDOMAINMAX) { 778 /* too long */ 779 return (NULL); 780 } 781 782 /* 783 * Calling textdomain() with a null domain string sets 784 * the domain to the default domain. 785 * If non-null string is passwd, current domain is changed 786 * to the new domain. 787 */ 788 789 /* actually this if clause should be protected from signals */ 790 if (*domain == '\0') { 791 if (CURRENT_DOMAIN(gt) != default_domain) { 792 free(CURRENT_DOMAIN(gt)); 793 CURRENT_DOMAIN(gt) = (char *)default_domain; 794 } 795 } else { 796 p = malloc(domain_len + 1); 797 if (p == NULL) 798 return (NULL); 799 (void) strcpy(p, domain); 800 if (CURRENT_DOMAIN(gt) != default_domain) 801 free(CURRENT_DOMAIN(gt)); 802 CURRENT_DOMAIN(gt) = p; 803 } 804 805 (void) strcpy(result, CURRENT_DOMAIN(gt)); 806 return (result); 807 } /* _textdomain_u */ 808 809 /* 810 * key_2_text() translates msd_id into target string. 811 */ 812 static char * 813 key_2_text(Msg_s_node *messages, const char *key_string) 814 { 815 int val; 816 char *msg_id_str; 817 unsigned char kc = *(unsigned char *)key_string; 818 struct msg_struct *check_msg_list; 819 820 #ifdef GETTEXT_DEBUG 821 gprintf(0, "*************** key_2_text(0x%p, \"%s\")\n", 822 (void *)messages, key_string ? key_string : "(null)"); 823 printsunmsg(messages, 1); 824 #endif 825 826 check_msg_list = messages->msg_list + 827 messages->msg_file_info->msg_mid; 828 for (;;) { 829 msg_id_str = messages->msg_ids + 830 check_msg_list->msgid_offset; 831 /* 832 * To maintain the compatibility with Zeus mo file, 833 * msg_id's are stored in descending order. 834 * If the ascending order is desired, change "msgfmt.c" 835 * and switch msg_id_str and key_string in the following 836 * strcmp() statement. 837 */ 838 val = *(unsigned char *)msg_id_str - kc; 839 if ((val == 0) && 840 (val = strcmp(msg_id_str, key_string)) == 0) { 841 return (messages->msg_strs 842 + check_msg_list->msgstr_offset); 843 } else if (val < 0) { 844 if (check_msg_list->less != LEAFINDICATOR) { 845 check_msg_list = messages->msg_list + 846 check_msg_list->less; 847 continue; 848 } 849 return ((char *)key_string); 850 } else { 851 /* val > 0 */ 852 if (check_msg_list->more != LEAFINDICATOR) { 853 check_msg_list = messages->msg_list + 854 check_msg_list->more; 855 continue; 856 } 857 return ((char *)key_string); 858 } 859 } 860 } 861 862 /* 863 * sun_setmsg 864 * 865 * INPUT 866 * mnp - message node 867 * addr - address to the mmapped file 868 * size - size of the file 869 * 870 * RETURN 871 * 0 - either T_SUN_MO or T_ILL_MO has been set 872 * 1 - not a valid sun mo file 873 * -1 - failed 874 */ 875 static int 876 sun_setmsg(Msg_node *mnp, char *addr, size_t size) 877 { 878 struct msg_info *sun_header; 879 Msg_s_node *p; 880 uint32_t first_4bytes; 881 int mid, count; 882 int struct_size, struct_size_old; 883 int msg_struct_size; 884 885 if (size < sizeof (struct msg_info)) { 886 /* invalid mo file */ 887 mnp->type = T_ILL_MO; 888 #ifdef GETTEXT_DEBUG 889 gprintf(0, "********* exiting sun_setmsg\n"); 890 printmnp(mnp, 1); 891 #endif 892 return (0); 893 } 894 895 first_4bytes = *((uint32_t *)(uintptr_t)addr); 896 if (first_4bytes > INT_MAX) { 897 /* 898 * Not a valid sun mo file 899 */ 900 return (1); 901 } 902 903 /* candidate for sun mo */ 904 905 sun_header = (struct msg_info *)(uintptr_t)addr; 906 mid = sun_header->msg_mid; 907 count = sun_header->msg_count; 908 msg_struct_size = sun_header->msg_struct_size; 909 struct_size_old = (int)(OLD_MSG_STRUCT_SIZE * count); 910 struct_size = (int)(MSG_STRUCT_SIZE * count); 911 912 if ((((count - 1) / 2) != mid) || 913 ((msg_struct_size != struct_size_old) && 914 (msg_struct_size != struct_size))) { 915 /* invalid mo file */ 916 mnp->type = T_ILL_MO; 917 #ifdef GETTEXT_DEBUG 918 gprintf(0, "********* exiting sun_setmsg\n"); 919 printmnp(mnp, 1); 920 #endif 921 return (0); 922 } 923 /* valid sun mo file */ 924 925 p = malloc(sizeof (Msg_s_node)); 926 if (p == NULL) { 927 return (-1); 928 } 929 930 p->msg_file_info = sun_header; 931 p->msg_list = (struct msg_struct *)(uintptr_t) 932 (addr + sizeof (struct msg_info)); 933 p->msg_ids = (char *)(addr + sizeof (struct msg_info) + 934 struct_size); 935 p->msg_strs = (char *)(addr + sizeof (struct msg_info) + 936 struct_size + sun_header->str_count_msgid); 937 938 mnp->msg.sunmsg = p; 939 mnp->type = T_SUN_MO; 940 #ifdef GETTEXT_DEBUG 941 gprintf(0, "******** exiting sun_setmsg\n"); 942 printmnp(mnp, 1); 943 #endif 944 return (0); 945 } 946 947 /* 948 * setmsg 949 * 950 * INPUT 951 * mnp - message node 952 * addr - address to the mmapped file 953 * size - size of the file 954 * 955 * RETURN 956 * 0 - succeeded 957 * -1 - failed 958 */ 959 static int 960 setmsg(Msg_node *mnp, char *addr, size_t size) 961 { 962 int ret; 963 if ((ret = sun_setmsg(mnp, addr, size)) <= 0) 964 return (ret); 965 966 return (gnu_setmsg(mnp, addr, size)); 967 } 968 969 static char * 970 handle_type_mo(Msg_node *mnp, struct msg_pack *mp) 971 { 972 char *result; 973 974 switch (mnp->type) { 975 case T_ILL_MO: 976 /* invalid MO */ 977 return (NULL); 978 case T_SUN_MO: 979 /* Sun MO found */ 980 mp->status |= ST_SUN_MO_FOUND; 981 982 if (mp->plural) { 983 /* 984 * *ngettext is called against 985 * Sun MO file 986 */ 987 int exp = (mp->n == 1); 988 result = (char *)mp->msgid1; 989 if (!exp) 990 result = (char *)mp->msgid2; 991 return (result); 992 } 993 result = key_2_text(mnp->msg.sunmsg, mp->msgid1); 994 if (!mnp->trusted) { 995 result = check_format(mp->msgid1, result, 0); 996 } 997 return (result); 998 case T_GNU_MO: 999 /* GNU MO found */ 1000 mp->status |= ST_GNU_MO_FOUND; 1001 1002 result = gnu_key_2_text(mnp->msg.gnumsg, 1003 get_codeset(mp->domain), mp); 1004 1005 if (result == mp->msgid1 || result == mp->msgid2) { 1006 /* no valid msg found */ 1007 return (result); 1008 } 1009 1010 /* valid msg found */ 1011 mp->status |= ST_GNU_MSG_FOUND; 1012 1013 if (!mnp->trusted) { 1014 result = check_format(mp->msgid1, result, 0); 1015 if (result == mp->msgid1) { 1016 DFLTMSG(result, mp->msgid1, mp->msgid2, 1017 mp->n, mp->plural); 1018 } 1019 } 1020 return (result); 1021 default: 1022 /* this should never happen */ 1023 DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); 1024 return (result); 1025 } 1026 /* NOTREACHED */ 1027 } 1028 1029 /* 1030 * handle_mo() returns NULL if invalid MO found. 1031 */ 1032 char * 1033 handle_mo(struct msg_pack *mp) 1034 { 1035 int fd; 1036 char *result; 1037 struct stat64 statbuf; 1038 Msg_node *mnp; 1039 Gettext_t *gt = global_gt; 1040 1041 #define CONNECT_ENTRY \ 1042 mnp->next = gt->m_node; \ 1043 gt->m_node = mnp; \ 1044 gt->c_m_node = mnp 1045 1046 #ifdef GETTEXT_DEBUG 1047 gprintf(0, "*************** handle_mo(0x%p)\n", (void *)mp); 1048 printmp(mp, 1); 1049 #endif 1050 1051 mnp = check_cache(mp); 1052 1053 if (mnp != NULL) { 1054 /* cache found */ 1055 return (handle_type_mo(mnp, mp)); 1056 } 1057 1058 /* 1059 * Valid entry not found in the cache 1060 */ 1061 mnp = calloc(1, sizeof (Msg_node)); 1062 if (mnp == NULL) { 1063 DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); 1064 return (result); 1065 } 1066 mnp->hashid = mp->hash_domain; 1067 mnp->path = strdup(mp->msgfile); 1068 if (mnp->path == NULL) { 1069 free(mnp); 1070 DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); 1071 return (result); 1072 } 1073 1074 fd = nls_safe_open(mp->msgfile, &statbuf, &mp->trusted, !mp->nlsp); 1075 if ((fd == -1) || (statbuf.st_size > LONG_MAX)) { 1076 if (fd != -1) 1077 (void) close(fd); 1078 mnp->type = T_ILL_MO; 1079 CONNECT_ENTRY; 1080 return (NULL); 1081 } 1082 mp->fsz = (size_t)statbuf.st_size; 1083 mp->addr = mmap(NULL, mp->fsz, PROT_READ, MAP_SHARED, fd, 0); 1084 (void) close(fd); 1085 1086 if (mp->addr == MAP_FAILED) { 1087 free(mnp->path); 1088 free(mnp); 1089 DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); 1090 return (result); 1091 } 1092 1093 if (setmsg(mnp, (char *)mp->addr, mp->fsz) == -1) { 1094 free(mnp->path); 1095 free(mnp); 1096 (void) munmap(mp->addr, mp->fsz); 1097 DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural); 1098 return (result); 1099 } 1100 mnp->trusted = mp->trusted; 1101 CONNECT_ENTRY; 1102 1103 return (handle_type_mo(mnp, mp)); 1104 } 1105