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