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