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