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 *
_real_gettext_u(const char * domain,const char * msgid1,const char * msgid2,unsigned long int ln,int category,int plural)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
free_all(Nlstmp * nlstmp,Nls_node * nnp,char * pathname,char * ppaths,char * lang)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
process_nlspath(const char * cur_domain,const char * cur_msgloc,const char * nlspath,char ** binding)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 *
replace_nls_option(char * s,const char * name,char * pathname,char * locale,char * lang,char * territory,char * codeset)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 *
_real_bindtextdomain_u(const char * domain,const char * binding,int type)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 *
_textdomain_u(const char * domain,char * result)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 *
key_2_text(Msg_s_node * messages,const char * key_string)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
sun_setmsg(Msg_node * mnp,char * addr,size_t size)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
setmsg(Msg_node * mnp,char * addr,size_t size)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 *
handle_type_mo(Msg_node * mnp,struct msg_pack * mp)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 *
handle_mo(struct msg_pack * mp)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