import less(1)
[unleashed/tickless.git] / usr / src / lib / libc / port / i18n / gettext_real.c
blob116e050365f6134e332c441b251f0d3b4978d11a
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 * Copyright 2015 Joyent, Inc.
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"
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 *);
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)
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;
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
82 if (msgid1 == NULL)
83 return (NULL);
85 mp = memset(&omp, 0, sizeof (omp)); /* msg pack */
88 * category may be LC_MESSAGES or LC_TIME
89 * locale contains the value of 'category'
91 if (loc == NULL)
92 loc = uselocale(NULL);
93 cur_locale = current_locale(loc, category);
95 language = getenv("LANGUAGE"); /* for GNU */
96 if (language) {
97 if (!*language || strchr(language, '/') != NULL) {
99 * LANGUAGE is an empty string or
100 * LANGUAGE contains '/'.
101 * Ignore it.
103 language = NULL;
108 * Query the current domain if domain argument is NULL pointer
110 mydomain[0] = '\0';
111 if (domain == NULL) {
113 * if NULL is specified for domainname,
114 * use the currently bound domain.
116 cur_domain = _textdomain_u(NULL, mydomain);
117 } else if (!*domain) {
119 * if an empty string is specified
121 cur_domain = DEFAULT_DOMAIN;
122 } else {
123 cur_domain = (char *)domain;
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);
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')) {
138 * If C locale,
139 * return the original msgid immediately.
141 DFLTMSG(result, msgid1, msgid2, n, plural);
142 return (result);
144 nlspath = NULL;
145 } else {
146 /* NLSPATH is set */
147 int ret;
149 msgloc = current_locale(loc, LC_MESSAGES);
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;
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);
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;
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.
199 * First, examine NLSPATH
201 if (nlspath) {
203 * NLSPATH binding has been successfully built
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
211 mp->nlsp = 1;
213 * cur_binding always ends with ':' before a null
214 * termination.
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);
228 (void) memcpy(mp->msgfile, cb, cblen);
229 *(mp->msgfile + cblen) = '\0';
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);
246 mp->nlsp = 0;
247 mp->binding = cur_domain_binding;
249 * Next, examine LANGUAGE
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);
259 * handle_lang() may have overridden locale
261 mp->locale = cur_locale;
262 mp->status = 0;
266 * Finally, handle a single binding
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);
276 result = handle_mo(mp);
277 if (result) {
278 return (result);
280 DFLTMSG(result, msgid1, msgid2, n, plural);
281 return (result);
282 } /* _real_gettext_u */
284 #define ALLFREE \
285 free_all(nlstmp, nnp, pathname, ppaths, lang)
287 static void
288 free_all(Nlstmp *nlstmp, Nls_node *nnp, char *pathname,
289 char *ppaths, char *lang)
291 Nlstmp *tp, *tq;
293 tp = nlstmp;
294 while (tp) {
295 tq = tp->next;
296 free(tp);
297 tp = tq;
299 free(nnp->locale);
300 free(nnp->domain);
301 free(pathname);
302 free(ppaths);
303 free(lang);
304 free(nnp);
308 * process_nlspath(): process the NLSPATH environment variable.
310 * this routine looks at NLSPATH in the environment,
311 * and will try to build up the binding list based
312 * on the settings of NLSPATH.
314 * RETURN:
315 * -1: Error occurred
316 * 0: No error, but no binding list has been built
317 * 1: No error, and a binding list has been built
320 static int
321 process_nlspath(const char *cur_domain, const char *cur_msgloc,
322 const char *nlspath, char **binding)
324 char *s; /* generic string ptr */
325 char *territory; /* our current territory element */
326 char *codeset; /* our current codeset element */
327 char *s1; /* for handling territory */
328 char *s2; /* for handling codeset */
329 char *lang = NULL; /* our current language element */
330 char *ppaths = NULL; /* ptr to all of the templates */
331 char *pathname = NULL; /* the full pathname to the file */
332 size_t nlspath_len, domain_len, locale_len, path_len;
333 size_t ppaths_len = 0;
334 Nlstmp *nlstmp = NULL;
335 Nlstmp *pnlstmp, *qnlstmp;
336 Nls_node *cur_nls, *nnp;
337 Gettext_t *gt = global_gt;
339 #ifdef GETTEXT_DEBUG
340 gprintf(0, "*************** process_nlspath(%s, %s, "
341 "%s, 0x%p)\n", cur_domain,
342 cur_msgloc, nlspath, (void *)binding);
343 #endif
345 cur_nls = gt->c_n_node;
346 if (cur_nls &&
347 (strcmp(cur_nls->domain, cur_domain) == 0 &&
348 strcmp(cur_nls->locale, cur_msgloc) == 0 &&
349 strcmp(cur_nls->nlspath, nlspath) == 0)) {
350 *binding = cur_nls->ppaths;
351 return (1);
354 nnp = gt->n_node;
355 while (nnp) {
356 if (strcmp(nnp->domain, cur_domain) == 0 &&
357 strcmp(nnp->locale, cur_msgloc) == 0 &&
358 strcmp(nnp->nlspath, nlspath) == 0) {
359 /* found */
360 gt->c_n_node = nnp;
361 *binding = nnp->ppaths;
362 return (1);
364 nnp = nnp->next;
366 /* not found */
368 nnp = calloc(1, sizeof (Nls_node));
369 if (nnp == NULL) {
370 ALLFREE;
371 return (-1);
374 nlspath_len = strlen(nlspath);
375 locale_len = strlen(cur_msgloc);
376 domain_len = strlen(cur_domain);
378 lang = s = strdup(cur_msgloc);
379 if (lang == NULL) {
380 ALLFREE;
381 return (-1);
383 s1 = s2 = NULL;
384 while (*s) {
385 if (*s == '_') {
386 s1 = s;
387 *s1++ = '\0';
388 } else if (*s == '.') {
389 s2 = s;
390 *s2++ = '\0';
392 s++;
394 territory = s1;
395 codeset = s2;
398 * now that we have the name (domain), we first look through NLSPATH,
399 * in an attempt to get the locale. A locale may be completely
400 * specified as "language_territory.codeset". NLSPATH consists
401 * of templates separated by ":" characters. The following are
402 * the substitution values within NLSPATH:
403 * %N = DEFAULT_DOMAIN
404 * %L = The value of the LC_MESSAGES category.
405 * %I = The language element from the LC_MESSAGES category.
406 * %t = The territory element from the LC_MESSAGES category.
407 * %c = The codeset element from the LC_MESSAGES category.
408 * %% = A single character.
409 * if we find one of these characters, we will carry out the
410 * appropriate substitution.
412 pathname = malloc(MAXPATHLEN);
413 if (pathname == NULL) {
414 ALLFREE;
415 return (-1);
417 s = (char *)nlspath; /* s has a content of NLSPATH */
418 while (*s) { /* march through NLSPATH */
419 (void) memset(pathname, 0, MAXPATHLEN);
420 if (*s == ':') {
422 * this loop only occurs if we have to replace
423 * ":" by "name". replace_nls_option() below
424 * will handle the subsequent ":"'s.
426 pnlstmp = malloc(sizeof (Nlstmp));
427 if (pnlstmp == NULL) {
428 ALLFREE;
429 return (-1);
432 (void) memcpy(pnlstmp->pathname, cur_domain,
433 domain_len + 1);
434 pnlstmp->len = domain_len;
435 ppaths_len += domain_len + 1; /* 1 for ':' */
438 pnlstmp->next = NULL;
440 if (nlstmp == NULL) {
441 nlstmp = pnlstmp;
442 qnlstmp = pnlstmp;
443 } else {
444 qnlstmp->next = pnlstmp;
445 qnlstmp = pnlstmp;
448 ++s;
449 continue;
451 /* replace Substitution field */
452 s = replace_nls_option(s, cur_domain, pathname,
453 (char *)cur_msgloc, lang, territory, codeset);
455 if (s == NULL) {
456 ALLFREE;
457 return (-1);
460 /* if we've found a valid file: */
461 if (*pathname) {
462 /* add template to end of chain of pathnames: */
463 pnlstmp = malloc(sizeof (Nlstmp));
464 if (pnlstmp == NULL) {
465 ALLFREE;
466 return (-1);
469 path_len = strlen(pathname);
470 (void) memcpy(pnlstmp->pathname, pathname,
471 path_len + 1);
472 pnlstmp->len = path_len;
473 ppaths_len += path_len + 1; /* 1 for ':' */
475 pnlstmp->next = NULL;
477 if (nlstmp == NULL) {
478 nlstmp = pnlstmp;
479 qnlstmp = pnlstmp;
480 } else {
481 qnlstmp->next = pnlstmp;
482 qnlstmp = pnlstmp;
485 if (*s) {
486 ++s;
490 * now that we've handled the pathname templates, concatenate them
491 * all into the form "template1:template2:..." for _bindtextdomain_u()
494 if (ppaths_len != 0) {
495 ppaths = malloc(ppaths_len + 1);
496 if (ppaths == NULL) {
497 ALLFREE;
498 return (-1);
500 *ppaths = '\0';
501 } else {
502 ALLFREE;
503 return (0);
507 * extract the path templates (fifo), and concatenate them
508 * all into a ":" separated string for _bindtextdomain_u()
510 pnlstmp = nlstmp;
511 s = ppaths;
512 while (pnlstmp) {
513 (void) memcpy(s, pnlstmp->pathname, pnlstmp->len);
514 s += pnlstmp->len;
515 *s++ = ':';
516 qnlstmp = pnlstmp->next;
517 free(pnlstmp);
518 pnlstmp = qnlstmp;
520 *s = '\0';
521 nlstmp = NULL;
523 nnp->domain = malloc(domain_len + 1);
524 if (nnp->domain == NULL) {
525 ALLFREE;
526 return (-1);
527 } else {
528 (void) memcpy(nnp->domain, cur_domain, domain_len + 1);
530 nnp->locale = malloc(locale_len + 1);
531 if (nnp->locale == NULL) {
532 ALLFREE;
533 return (-1);
534 } else {
535 (void) memcpy(nnp->locale, cur_msgloc, locale_len + 1);
537 nnp->nlspath = malloc(nlspath_len + 1);
538 if (nnp->nlspath == NULL) {
539 ALLFREE;
540 return (-1);
541 } else {
542 (void) memcpy(nnp->nlspath, nlspath, nlspath_len + 1);
544 nnp->ppaths = ppaths;
546 nnp->next = gt->n_node;
547 gt->n_node = nnp;
548 gt->c_n_node = nnp;
550 free(pathname);
551 free(lang);
552 #ifdef GETTEXT_DEBUG
553 gprintf(0, "*************** existing process_nlspath with success\n");
554 gprintf(0, " binding: \"%s\"\n", ppaths);
555 #endif
556 *binding = ppaths;
557 return (1);
562 * This routine will replace substitution parameters in NLSPATH
563 * with appropiate values.
565 static char *
566 replace_nls_option(char *s, const char *name, char *pathname,
567 char *locale, char *lang, char *territory, char *codeset)
569 char *t, *u;
570 char *limit;
572 t = pathname;
573 limit = pathname + MAXPATHLEN - 1;
575 while (*s && *s != ':') {
576 if (t < limit) {
578 * %% is considered a single % character (XPG).
579 * %L : LC_MESSAGES (XPG4) LANG(XPG3)
580 * %l : The language element from the current locale.
581 * (XPG3, XPG4)
583 if (*s != '%')
584 *t++ = *s;
585 else if (*++s == 'N') {
586 if (name) {
587 u = (char *)name;
588 while (*u && (t < limit))
589 *t++ = *u++;
591 } else if (*s == 'L') {
592 if (locale) {
593 u = locale;
594 while (*u && (t < limit))
595 *t++ = *u++;
597 } else if (*s == 'l') {
598 if (lang) {
599 u = lang;
600 while (*u && (*u != '_') &&
601 (t < limit))
602 *t++ = *u++;
604 } else if (*s == 't') {
605 if (territory) {
606 u = territory;
607 while (*u && (*u != '.') &&
608 (t < limit))
609 *t++ = *u++;
611 } else if (*s == 'c') {
612 if (codeset) {
613 u = codeset;
614 while (*u && (t < limit))
615 *t++ = *u++;
617 } else {
618 if (t < limit)
619 *t++ = *s;
621 } else {
622 /* too long pathname */
623 return (NULL);
625 ++s;
627 *t = '\0';
628 return (s);
632 char *
633 _real_bindtextdomain_u(const char *domain, const char *binding,
634 int type)
636 struct domain_binding *bind, *prev;
637 Gettext_t *gt = global_gt;
638 char **binding_addr;
640 #ifdef GETTEXT_DEBUG
641 gprintf(0, "*************** _real_bindtextdomain_u(\"%s\", "
642 "\"%s\", \"%s\")\n",
643 (domain ? domain : ""),
644 (binding ? binding : ""),
645 (type == TP_BINDING) ? "TP_BINDING" : "TP_CODESET");
646 #endif
649 * If domain is a NULL pointer, no change will occur regardless
650 * of binding value. Just return NULL.
652 if (domain == NULL) {
653 return (NULL);
657 * Global Binding is not supported any more.
658 * Just return NULL if domain is NULL string.
660 if (*domain == '\0') {
661 return (NULL);
664 /* linear search for binding, rebind if found, add if not */
665 bind = FIRSTBIND(gt);
666 prev = NULL; /* Two pointers needed for pointer operations */
668 while (bind) {
669 if (strcmp(domain, bind->domain) == 0) {
671 * Domain found.
673 binding_addr = (type == TP_BINDING) ? &(bind->binding) :
674 &(bind->codeset);
675 if (binding == NULL) {
677 * if binding is null, then query
679 return (*binding_addr);
681 /* replace existing binding with new binding */
682 if (*binding_addr) {
683 free(*binding_addr);
685 if ((*binding_addr = strdup(binding)) == NULL) {
686 return (NULL);
688 #ifdef GETTEXT_DEBUG
689 printlist();
690 #endif
691 return (*binding_addr);
693 prev = bind;
694 bind = bind->next;
695 } /* while (bind) */
697 /* domain has not been found in the list at this point */
698 if (binding) {
700 * domain is not found, but binding is not NULL.
701 * Then add a new node to the end of linked list.
704 if ((bind = malloc(sizeof (Dbinding))) == NULL) {
705 return (NULL);
707 if ((bind->domain = strdup(domain)) == NULL) {
708 free(bind);
709 return (NULL);
711 bind->binding = NULL;
712 bind->codeset = NULL;
713 binding_addr = (type == TP_BINDING) ? &(bind->binding) :
714 &(bind->codeset);
715 if ((*binding_addr = strdup(binding)) == NULL) {
716 free(bind->domain);
717 free(bind);
718 return (NULL);
720 bind->next = NULL;
722 if (prev) {
723 /* reached the end of list */
724 prev->next = bind;
725 } else {
726 /* list was empty */
727 FIRSTBIND(gt) = bind;
730 #ifdef GETTEXT_DEBUG
731 printlist();
732 #endif
733 return (*binding_addr);
734 } else {
736 * Query of domain which is not found in the list
737 * for bindtextdomain, returns defaultbind
738 * for bind_textdomain_codeset, returns NULL
740 if (type == TP_BINDING) {
741 return ((char *)defaultbind);
742 } else {
743 return (NULL);
745 } /* if (binding) */
747 /* Must not reach here */
749 } /* _real_bindtextdomain_u */
752 char *
753 _textdomain_u(const char *domain, char *result)
755 char *p;
756 size_t domain_len;
757 Gettext_t *gt = global_gt;
759 #ifdef GETTEXT_DEBUG
760 gprintf(0, "*************** _textdomain_u(\"%s\", 0x%p)\n",
761 (domain ? domain : ""), (void *)result);
762 #endif
764 /* Query is performed for NULL domain pointer */
765 if (domain == NULL) {
766 (void) strcpy(result, CURRENT_DOMAIN(gt));
767 return (result);
770 /* check for error. */
772 * domain is limited to TEXTDOMAINMAX bytes
773 * excluding a null termination.
775 domain_len = strlen(domain);
776 if (domain_len > TEXTDOMAINMAX) {
777 /* too long */
778 return (NULL);
782 * Calling textdomain() with a null domain string sets
783 * the domain to the default domain.
784 * If non-null string is passwd, current domain is changed
785 * to the new domain.
788 /* actually this if clause should be protected from signals */
789 if (*domain == '\0') {
790 if (CURRENT_DOMAIN(gt) != default_domain) {
791 free(CURRENT_DOMAIN(gt));
792 CURRENT_DOMAIN(gt) = (char *)default_domain;
794 } else {
795 p = malloc(domain_len + 1);
796 if (p == NULL)
797 return (NULL);
798 (void) strcpy(p, domain);
799 if (CURRENT_DOMAIN(gt) != default_domain)
800 free(CURRENT_DOMAIN(gt));
801 CURRENT_DOMAIN(gt) = p;
804 (void) strcpy(result, CURRENT_DOMAIN(gt));
805 return (result);
806 } /* _textdomain_u */
809 * key_2_text() translates msd_id into target string.
811 static char *
812 key_2_text(Msg_s_node *messages, const char *key_string)
814 int val;
815 char *msg_id_str;
816 unsigned char kc = *(unsigned char *)key_string;
817 struct msg_struct *check_msg_list;
819 #ifdef GETTEXT_DEBUG
820 gprintf(0, "*************** key_2_text(0x%p, \"%s\")\n",
821 (void *)messages, key_string ? key_string : "(null)");
822 printsunmsg(messages, 1);
823 #endif
825 check_msg_list = messages->msg_list +
826 messages->msg_file_info->msg_mid;
827 for (;;) {
828 msg_id_str = messages->msg_ids +
829 check_msg_list->msgid_offset;
831 * To maintain the compatibility with Zeus mo file,
832 * msg_id's are stored in descending order.
833 * If the ascending order is desired, change "msgfmt.c"
834 * and switch msg_id_str and key_string in the following
835 * strcmp() statement.
837 val = *(unsigned char *)msg_id_str - kc;
838 if ((val == 0) &&
839 (val = strcmp(msg_id_str, key_string)) == 0) {
840 return (messages->msg_strs
841 + check_msg_list->msgstr_offset);
842 } else if (val < 0) {
843 if (check_msg_list->less != LEAFINDICATOR) {
844 check_msg_list = messages->msg_list +
845 check_msg_list->less;
846 continue;
848 return ((char *)key_string);
849 } else {
850 /* val > 0 */
851 if (check_msg_list->more != LEAFINDICATOR) {
852 check_msg_list = messages->msg_list +
853 check_msg_list->more;
854 continue;
856 return ((char *)key_string);
862 * sun_setmsg
864 * INPUT
865 * mnp - message node
866 * addr - address to the mmapped file
867 * size - size of the file
869 * RETURN
870 * 0 - either T_SUN_MO or T_ILL_MO has been set
871 * 1 - not a valid sun mo file
872 * -1 - failed
874 static int
875 sun_setmsg(Msg_node *mnp, char *addr, size_t size)
877 struct msg_info *sun_header;
878 Msg_s_node *p;
879 uint32_t first_4bytes;
880 int mid, count;
881 int struct_size, struct_size_old;
882 int msg_struct_size;
884 if (size < sizeof (struct msg_info)) {
885 /* invalid mo file */
886 mnp->type = T_ILL_MO;
887 #ifdef GETTEXT_DEBUG
888 gprintf(0, "********* exiting sun_setmsg\n");
889 printmnp(mnp, 1);
890 #endif
891 return (0);
894 first_4bytes = *((uint32_t *)(uintptr_t)addr);
895 if (first_4bytes > INT_MAX) {
897 * Not a valid sun mo file
899 return (1);
902 /* candidate for sun mo */
904 sun_header = (struct msg_info *)(uintptr_t)addr;
905 mid = sun_header->msg_mid;
906 count = sun_header->msg_count;
907 msg_struct_size = sun_header->msg_struct_size;
908 struct_size_old = (int)(OLD_MSG_STRUCT_SIZE * count);
909 struct_size = (int)(MSG_STRUCT_SIZE * count);
911 if ((((count - 1) / 2) != mid) ||
912 ((msg_struct_size != struct_size_old) &&
913 (msg_struct_size != struct_size))) {
914 /* invalid mo file */
915 mnp->type = T_ILL_MO;
916 #ifdef GETTEXT_DEBUG
917 gprintf(0, "********* exiting sun_setmsg\n");
918 printmnp(mnp, 1);
919 #endif
920 return (0);
922 /* valid sun mo file */
924 p = malloc(sizeof (Msg_s_node));
925 if (p == NULL) {
926 return (-1);
929 p->msg_file_info = sun_header;
930 p->msg_list = (struct msg_struct *)(uintptr_t)
931 (addr + sizeof (struct msg_info));
932 p->msg_ids = (char *)(addr + sizeof (struct msg_info) +
933 struct_size);
934 p->msg_strs = (char *)(addr + sizeof (struct msg_info) +
935 struct_size + sun_header->str_count_msgid);
937 mnp->msg.sunmsg = p;
938 mnp->type = T_SUN_MO;
939 #ifdef GETTEXT_DEBUG
940 gprintf(0, "******** exiting sun_setmsg\n");
941 printmnp(mnp, 1);
942 #endif
943 return (0);
947 * setmsg
949 * INPUT
950 * mnp - message node
951 * addr - address to the mmapped file
952 * size - size of the file
954 * RETURN
955 * 0 - succeeded
956 * -1 - failed
958 static int
959 setmsg(Msg_node *mnp, char *addr, size_t size)
961 int ret;
962 if ((ret = sun_setmsg(mnp, addr, size)) <= 0)
963 return (ret);
965 return (gnu_setmsg(mnp, addr, size));
968 static char *
969 handle_type_mo(Msg_node *mnp, struct msg_pack *mp)
971 char *result;
973 switch (mnp->type) {
974 case T_ILL_MO:
975 /* invalid MO */
976 return (NULL);
977 case T_SUN_MO:
978 /* Sun MO found */
979 mp->status |= ST_SUN_MO_FOUND;
981 if (mp->plural) {
983 * *ngettext is called against
984 * Sun MO file
986 int exp = (mp->n == 1);
987 result = (char *)mp->msgid1;
988 if (!exp)
989 result = (char *)mp->msgid2;
990 return (result);
992 result = key_2_text(mnp->msg.sunmsg, mp->msgid1);
993 if (!mnp->trusted) {
994 result = check_format(mp->msgid1, result, 0);
996 return (result);
997 case T_GNU_MO:
998 /* GNU MO found */
999 mp->status |= ST_GNU_MO_FOUND;
1001 result = gnu_key_2_text(mnp->msg.gnumsg,
1002 get_codeset(mp->domain), mp);
1004 if (result == mp->msgid1 || result == mp->msgid2) {
1005 /* no valid msg found */
1006 return (result);
1009 /* valid msg found */
1010 mp->status |= ST_GNU_MSG_FOUND;
1012 if (!mnp->trusted) {
1013 result = check_format(mp->msgid1, result, 0);
1014 if (result == mp->msgid1) {
1015 DFLTMSG(result, mp->msgid1, mp->msgid2,
1016 mp->n, mp->plural);
1019 return (result);
1020 default:
1021 /* this should never happen */
1022 DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
1023 return (result);
1025 /* NOTREACHED */
1029 * handle_mo() returns NULL if invalid MO found.
1031 char *
1032 handle_mo(struct msg_pack *mp)
1034 int fd;
1035 char *result;
1036 struct stat64 statbuf;
1037 Msg_node *mnp;
1038 Gettext_t *gt = global_gt;
1040 #define CONNECT_ENTRY \
1041 mnp->next = gt->m_node; \
1042 gt->m_node = mnp; \
1043 gt->c_m_node = mnp
1045 #ifdef GETTEXT_DEBUG
1046 gprintf(0, "*************** handle_mo(0x%p)\n", (void *)mp);
1047 printmp(mp, 1);
1048 #endif
1050 mnp = check_cache(mp);
1052 if (mnp != NULL) {
1053 /* cache found */
1054 return (handle_type_mo(mnp, mp));
1058 * Valid entry not found in the cache
1060 mnp = calloc(1, sizeof (Msg_node));
1061 if (mnp == NULL) {
1062 DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
1063 return (result);
1065 mnp->hashid = mp->hash_domain;
1066 mnp->path = strdup(mp->msgfile);
1067 if (mnp->path == NULL) {
1068 free(mnp);
1069 DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
1070 return (result);
1073 fd = nls_safe_open(mp->msgfile, &statbuf, &mp->trusted, !mp->nlsp);
1074 if ((fd == -1) || (statbuf.st_size > LONG_MAX)) {
1075 if (fd != -1)
1076 (void) close(fd);
1077 mnp->type = T_ILL_MO;
1078 CONNECT_ENTRY;
1079 return (NULL);
1081 mp->fsz = (size_t)statbuf.st_size;
1082 mp->addr = mmap(NULL, mp->fsz, PROT_READ, MAP_SHARED, fd, 0);
1083 (void) close(fd);
1085 if (mp->addr == MAP_FAILED) {
1086 free(mnp->path);
1087 free(mnp);
1088 DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
1089 return (result);
1092 if (setmsg(mnp, (char *)mp->addr, mp->fsz) == -1) {
1093 free(mnp->path);
1094 free(mnp);
1095 (void) munmap(mp->addr, mp->fsz);
1096 DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
1097 return (result);
1099 mnp->trusted = mp->trusted;
1100 CONNECT_ENTRY;
1102 return (handle_type_mo(mnp, mp));