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