xref: /illumos-gate/usr/src/lib/libc/port/i18n/gettext_real.c (revision f13ac6397da869bb7b7bc8a9f0b2e8fff530c346)
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 "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 
56 char *
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
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
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 *
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 *
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 *
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 *
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
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
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 *
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 *
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