xref: /netbsd-src/lib/libintl/gettext.c (revision 9b2f4799723719bf87ce8aa35a73fb5ab025996d)
1*9b2f4799Schristos /*	$NetBSD: gettext.c,v 1.33 2024/08/18 17:46:24 christos Exp $	*/
2bb42e65bSitojun 
3bb42e65bSitojun /*-
43bad25dcSminoura  * Copyright (c) 2000, 2001 Citrus Project,
5bb42e65bSitojun  * All rights reserved.
6bb42e65bSitojun  *
7bb42e65bSitojun  * Redistribution and use in source and binary forms, with or without
8bb42e65bSitojun  * modification, are permitted provided that the following conditions
9bb42e65bSitojun  * are met:
10bb42e65bSitojun  * 1. Redistributions of source code must retain the above copyright
11bb42e65bSitojun  *    notice, this list of conditions and the following disclaimer.
12bb42e65bSitojun  * 2. Redistributions in binary form must reproduce the above copyright
13bb42e65bSitojun  *    notice, this list of conditions and the following disclaimer in the
14bb42e65bSitojun  *    documentation and/or other materials provided with the distribution.
15bb42e65bSitojun  *
16bb42e65bSitojun  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17bb42e65bSitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18bb42e65bSitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19bb42e65bSitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20bb42e65bSitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21bb42e65bSitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22bb42e65bSitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23bb42e65bSitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24bb42e65bSitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25bb42e65bSitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26bb42e65bSitojun  * SUCH DAMAGE.
27782b5c88Syamt  *
28782b5c88Syamt  * $Citrus: xpg4dl/FreeBSD/lib/libintl/gettext.c,v 1.31 2001/09/27 15:18:45 yamt Exp $
29bb42e65bSitojun  */
30bb42e65bSitojun 
31bb42e65bSitojun #include <sys/cdefs.h>
32*9b2f4799Schristos __RCSID("$NetBSD: gettext.c,v 1.33 2024/08/18 17:46:24 christos Exp $");
33bb42e65bSitojun 
34bb42e65bSitojun #include <sys/param.h>
35bb42e65bSitojun #include <sys/stat.h>
36bb42e65bSitojun #include <sys/mman.h>
37bb42e65bSitojun #include <sys/uio.h>
38bb42e65bSitojun 
3929be49b4Stshiozak #include <assert.h>
40bb42e65bSitojun #include <fcntl.h>
41bb42e65bSitojun #include <stdio.h>
42bb42e65bSitojun #include <stdlib.h>
43bb42e65bSitojun #include <unistd.h>
44bb42e65bSitojun #include <string.h>
45bb42e65bSitojun #if 0
46bb42e65bSitojun #include <util.h>
47bb42e65bSitojun #endif
48bb42e65bSitojun #include <libintl.h>
49bb42e65bSitojun #include <locale.h>
50bb42e65bSitojun #include "libintl_local.h"
51dd416aa8Stshiozak #include "plural_parser.h"
52bb42e65bSitojun #include "pathnames.h"
53bb42e65bSitojun 
545297022aSchristos /* GNU gettext added a hack to add some context to messages. If a message is
555297022aSchristos  * used in multiple locations, it needs some amount of context to make the
565297022aSchristos  * translation clear to translators. GNU gettext, rather than modifying the
575297022aSchristos  * message format, concatenates the context, \004 and the message id.
585297022aSchristos  */
595297022aSchristos #define	MSGCTXT_ID_SEPARATOR	'\004'
605297022aSchristos 
615297022aSchristos static const char *pgettext_impl(const char *, const char *, const char *,
625297022aSchristos 				const char *, unsigned long int, int);
635297022aSchristos static char *concatenate_ctxt_id(const char *, const char *);
644271bf2eSjunyoung static const char *lookup_category(int);
654271bf2eSjunyoung static const char *split_locale(const char *);
664271bf2eSjunyoung static const char *lookup_mofile(char *, size_t, const char *, const char *,
674271bf2eSjunyoung 				 const char *, const char *,
684271bf2eSjunyoung 				 struct domainbinding *);
694271bf2eSjunyoung static uint32_t flip(uint32_t, uint32_t);
704271bf2eSjunyoung static int validate(void *, struct mohandle *);
714271bf2eSjunyoung static int mapit(const char *, struct domainbinding *);
724271bf2eSjunyoung static int unmapit(struct domainbinding *);
734271bf2eSjunyoung static const char *lookup_hash(const char *, struct domainbinding *, size_t *);
744271bf2eSjunyoung static const char *lookup_bsearch(const char *, struct domainbinding *,
754271bf2eSjunyoung 				  size_t *);
764271bf2eSjunyoung static const char *lookup(const char *, struct domainbinding *, size_t *);
774271bf2eSjunyoung static const char *get_lang_env(const char *);
78bb42e65bSitojun 
79bb42e65bSitojun /*
80bb42e65bSitojun  * shortcut functions.  the main implementation resides in dcngettext().
81bb42e65bSitojun  */
82bb42e65bSitojun char *
834271bf2eSjunyoung gettext(const char *msgid)
84bb42e65bSitojun {
85bb42e65bSitojun 
86bb42e65bSitojun 	return dcngettext(NULL, msgid, NULL, 1UL, LC_MESSAGES);
87bb42e65bSitojun }
88bb42e65bSitojun 
89bb42e65bSitojun char *
904271bf2eSjunyoung dgettext(const char *domainname, const char *msgid)
91bb42e65bSitojun {
92bb42e65bSitojun 
93bb42e65bSitojun 	return dcngettext(domainname, msgid, NULL, 1UL, LC_MESSAGES);
94bb42e65bSitojun }
95bb42e65bSitojun 
96bb42e65bSitojun char *
974271bf2eSjunyoung dcgettext(const char *domainname, const char *msgid, int category)
98bb42e65bSitojun {
99bb42e65bSitojun 
100bb42e65bSitojun 	return dcngettext(domainname, msgid, NULL, 1UL, category);
101bb42e65bSitojun }
102bb42e65bSitojun 
103bb42e65bSitojun char *
1044271bf2eSjunyoung ngettext(const char *msgid1, const char *msgid2, unsigned long int n)
105bb42e65bSitojun {
106bb42e65bSitojun 
107bb42e65bSitojun 	return dcngettext(NULL, msgid1, msgid2, n, LC_MESSAGES);
108bb42e65bSitojun }
109bb42e65bSitojun 
110bb42e65bSitojun char *
1114271bf2eSjunyoung dngettext(const char *domainname, const char *msgid1, const char *msgid2,
1124271bf2eSjunyoung 	  unsigned long int n)
113bb42e65bSitojun {
114bb42e65bSitojun 
115bb42e65bSitojun 	return dcngettext(domainname, msgid1, msgid2, n, LC_MESSAGES);
116bb42e65bSitojun }
117bb42e65bSitojun 
1185297022aSchristos const char *
1195297022aSchristos pgettext(const char *msgctxt, const char *msgid)
1205297022aSchristos {
1215297022aSchristos 
1225297022aSchristos 	return pgettext_impl(NULL, msgctxt, msgid, NULL, 1UL, LC_MESSAGES);
1235297022aSchristos }
1245297022aSchristos 
1255297022aSchristos const char *
1265297022aSchristos dpgettext(const char *domainname, const char *msgctxt, const char *msgid)
1275297022aSchristos {
1285297022aSchristos 
1295297022aSchristos 	return pgettext_impl(domainname, msgctxt, msgid, NULL, 1UL, LC_MESSAGES);
1305297022aSchristos }
1315297022aSchristos 
1325297022aSchristos const char *
1335297022aSchristos dcpgettext(const char *domainname, const char *msgctxt, const char *msgid,
1345297022aSchristos 	int category)
1355297022aSchristos {
1365297022aSchristos 
1375297022aSchristos 	return pgettext_impl(domainname, msgctxt, msgid, NULL, 1UL, category);
1385297022aSchristos }
1395297022aSchristos 
1405297022aSchristos const char *
1415297022aSchristos npgettext(const char *msgctxt, const char *msgid1, const char *msgid2,
1425297022aSchristos 	unsigned long int n)
1435297022aSchristos {
1445297022aSchristos 
1455297022aSchristos 	return pgettext_impl(NULL, msgctxt, msgid1, msgid2, n, LC_MESSAGES);
1465297022aSchristos }
1475297022aSchristos 
1485297022aSchristos const char *
1495297022aSchristos dnpgettext(const char *domainname, const char *msgctxt, const char *msgid1,
1505297022aSchristos 	const char *msgid2, unsigned long int n)
1515297022aSchristos {
1525297022aSchristos 
1535297022aSchristos 	return pgettext_impl(domainname, msgctxt, msgid1, msgid2, n, LC_MESSAGES);
1545297022aSchristos }
1555297022aSchristos 
1565297022aSchristos const char *
1575297022aSchristos dcnpgettext(const char *domainname, const char *msgctxt, const char *msgid1,
1585297022aSchristos 	const char *msgid2, unsigned long int n, int category)
1595297022aSchristos {
1605297022aSchristos 
1615297022aSchristos 	return pgettext_impl(domainname, msgctxt, msgid1, msgid2, n, category);
1625297022aSchristos }
1635297022aSchristos 
1645297022aSchristos static const char *
1655297022aSchristos pgettext_impl(const char *domainname, const char *msgctxt, const char *msgid1,
1665297022aSchristos 	const char *msgid2, unsigned long int n, int category)
1675297022aSchristos {
1685297022aSchristos 	char *msgctxt_id;
1695297022aSchristos 	char *translation;
1705297022aSchristos 	char *p;
1715297022aSchristos 
1725297022aSchristos 	if ((msgctxt_id = concatenate_ctxt_id(msgctxt, msgid1)) == NULL)
1735297022aSchristos 		return msgid1;
1745297022aSchristos 
1755297022aSchristos 	translation = dcngettext(domainname, msgctxt_id,
1765297022aSchristos 		msgid2, n, category);
177*9b2f4799Schristos 
178*9b2f4799Schristos 	if (translation == msgctxt_id) {
1795297022aSchristos 		free(msgctxt_id);
18028614955Schristos 		return msgid1;
181*9b2f4799Schristos 	}
18228614955Schristos 
183*9b2f4799Schristos 	free(msgctxt_id);
1845297022aSchristos 	p = strchr(translation, '\004');
1855297022aSchristos 	if (p)
1865297022aSchristos 		return p + 1;
1875297022aSchristos 	return translation;
1885297022aSchristos }
1895297022aSchristos 
190bb42e65bSitojun /*
191bb42e65bSitojun  * dcngettext() -
192bb42e65bSitojun  * lookup internationalized message on database locale/category/domainname
193bb42e65bSitojun  * (like ja_JP.eucJP/LC_MESSAGES/domainname).
194bb42e65bSitojun  * if n equals to 1, internationalized message will be looked up for msgid1.
195bb42e65bSitojun  * otherwise, message will be looked up for msgid2.
196bb42e65bSitojun  * if the lookup fails, the function will return msgid1 or msgid2 as is.
197bb42e65bSitojun  *
198bb42e65bSitojun  * Even though the return type is "char *", caller should not rewrite the
199bb42e65bSitojun  * region pointed to by the return value (should be "const char *", but can't
200bb42e65bSitojun  * change it for compatibility with other implementations).
201bb42e65bSitojun  *
202bb42e65bSitojun  * by default (if domainname == NULL), domainname is taken from the value set
203bb42e65bSitojun  * by textdomain().  usually name of the application (like "ls") is used as
204bb42e65bSitojun  * domainname.  category is usually LC_MESSAGES.
205bb42e65bSitojun  *
206bb42e65bSitojun  * the code reads in *.mo files generated by GNU gettext.  *.mo is a host-
207bb42e65bSitojun  * endian encoded file.  both endians are supported here, as the files are in
208bb42e65bSitojun  * /usr/share/locale! (or we should move those files into /usr/libdata)
209bb42e65bSitojun  */
210bb42e65bSitojun 
2115297022aSchristos static char *
2125297022aSchristos concatenate_ctxt_id(const char *msgctxt, const char *msgid)
2135297022aSchristos {
2145297022aSchristos 	char *ret;
2155297022aSchristos 
2165297022aSchristos 	if (asprintf(&ret, "%s%c%s", msgctxt, MSGCTXT_ID_SEPARATOR, msgid) == -1)
2175297022aSchristos 		return NULL;
2185297022aSchristos 
2195297022aSchristos 	return ret;
2205297022aSchristos }
2215297022aSchristos 
222bb42e65bSitojun static const char *
2234271bf2eSjunyoung lookup_category(int category)
224bb42e65bSitojun {
225bb42e65bSitojun 
226bb42e65bSitojun 	switch (category) {
227bb42e65bSitojun 	case LC_COLLATE:	return "LC_COLLATE";
228bb42e65bSitojun 	case LC_CTYPE:		return "LC_CTYPE";
229bb42e65bSitojun 	case LC_MONETARY:	return "LC_MONETARY";
230bb42e65bSitojun 	case LC_NUMERIC:	return "LC_NUMERIC";
231bb42e65bSitojun 	case LC_TIME:		return "LC_TIME";
232bb42e65bSitojun 	case LC_MESSAGES:	return "LC_MESSAGES";
233bb42e65bSitojun 	}
234bb42e65bSitojun 	return NULL;
235bb42e65bSitojun }
236bb42e65bSitojun 
237b490ea0cSchristos #define MAXBUFLEN	1024
238bb42e65bSitojun /*
239bb42e65bSitojun  * XPG syntax: language[_territory[.codeset]][@modifier]
240bb42e65bSitojun  * XXX boundary check on "result" is lacking
241bb42e65bSitojun  */
242bb42e65bSitojun static const char *
2434271bf2eSjunyoung split_locale(const char *lname)
244bb42e65bSitojun {
245b490ea0cSchristos 	char buf[MAXBUFLEN], tmp[2 * MAXBUFLEN];
246bb42e65bSitojun 	char *l, *t, *c, *m;
247b490ea0cSchristos 	static char result[4 * MAXBUFLEN];
248bb42e65bSitojun 
249bb42e65bSitojun 	memset(result, 0, sizeof(result));
250bb42e65bSitojun 
251bb42e65bSitojun 	if (strlen(lname) + 1 > sizeof(buf)) {
252bb42e65bSitojun fail:
253bb42e65bSitojun 		return lname;
254bb42e65bSitojun 	}
255bb42e65bSitojun 
256bb42e65bSitojun 	strlcpy(buf, lname, sizeof(buf));
257bb42e65bSitojun 	m = strrchr(buf, '@');
258bb42e65bSitojun 	if (m)
259bb42e65bSitojun 		*m++ = '\0';
260bb42e65bSitojun 	c = strrchr(buf, '.');
261bb42e65bSitojun 	if (c)
262bb42e65bSitojun 		*c++ = '\0';
263bb42e65bSitojun 	t = strrchr(buf, '_');
264bb42e65bSitojun 	if (t)
265bb42e65bSitojun 		*t++ = '\0';
266bb42e65bSitojun 	l = buf;
267bb42e65bSitojun 	if (strlen(l) == 0)
268bb42e65bSitojun 		goto fail;
269bb42e65bSitojun 	if (c && !t)
270bb42e65bSitojun 		goto fail;
271bb42e65bSitojun 
272bb42e65bSitojun 	if (m) {
273bb42e65bSitojun 		if (t) {
274bb42e65bSitojun 			if (c) {
275bb42e65bSitojun 				snprintf(tmp, sizeof(tmp), "%s_%s.%s@%s",
276bb42e65bSitojun 				    l, t, c, m);
277bb42e65bSitojun 				strlcat(result, tmp, sizeof(result));
278bb42e65bSitojun 				strlcat(result, ":", sizeof(result));
279bb42e65bSitojun 			}
280bb42e65bSitojun 			snprintf(tmp, sizeof(tmp), "%s_%s@%s", l, t, m);
281bb42e65bSitojun 			strlcat(result, tmp, sizeof(result));
282bb42e65bSitojun 			strlcat(result, ":", sizeof(result));
283bb42e65bSitojun 		}
284bb42e65bSitojun 		snprintf(tmp, sizeof(tmp), "%s@%s", l, m);
285bb42e65bSitojun 		strlcat(result, tmp, sizeof(result));
286bb42e65bSitojun 		strlcat(result, ":", sizeof(result));
287bb42e65bSitojun 	}
288bb42e65bSitojun 	if (t) {
289bb42e65bSitojun 		if (c) {
290bb42e65bSitojun 			snprintf(tmp, sizeof(tmp), "%s_%s.%s", l, t, c);
291bb42e65bSitojun 			strlcat(result, tmp, sizeof(result));
292bb42e65bSitojun 			strlcat(result, ":", sizeof(result));
293bb42e65bSitojun 		}
2947cd60a32Syamt 		snprintf(tmp, sizeof(tmp), "%s_%s", l, t);
295bb42e65bSitojun 		strlcat(result, tmp, sizeof(result));
296bb42e65bSitojun 		strlcat(result, ":", sizeof(result));
297bb42e65bSitojun 	}
298bb42e65bSitojun 	strlcat(result, l, sizeof(result));
299bb42e65bSitojun 
300bb42e65bSitojun 	return result;
301bb42e65bSitojun }
302bb42e65bSitojun 
303bb42e65bSitojun static const char *
3044271bf2eSjunyoung lookup_mofile(char *buf, size_t len, const char *dir, const char *lpath,
3054271bf2eSjunyoung 	      const char *category, const char *domainname,
3064271bf2eSjunyoung 	      struct domainbinding *db)
307bb42e65bSitojun {
308bb42e65bSitojun 	struct stat st;
309bb42e65bSitojun 	char *p, *q;
310782b5c88Syamt 	char lpath_tmp[BUFSIZ];
311bb42e65bSitojun 
3125c3967c0Syamt 	/*
3135c3967c0Syamt 	 * LANGUAGE is a colon separated list of locale names.
3145c3967c0Syamt 	 */
3155c3967c0Syamt 
316782b5c88Syamt 	strlcpy(lpath_tmp, lpath, sizeof(lpath_tmp));
317782b5c88Syamt 	q = lpath_tmp;
3183bad25dcSminoura 	/* CONSTCOND */
319bb42e65bSitojun 	while (1) {
320bb42e65bSitojun 		p = strsep(&q, ":");
321bb42e65bSitojun 		if (!p)
322bb42e65bSitojun 			break;
323bb42e65bSitojun 		if (!*p)
324bb42e65bSitojun 			continue;
325bb42e65bSitojun 
326bb42e65bSitojun 		/* don't mess with default locales */
327bb42e65bSitojun 		if (strcmp(p, "C") == 0 || strcmp(p, "POSIX") == 0)
328bb42e65bSitojun 			return NULL;
329bb42e65bSitojun 
330bb42e65bSitojun 		/* validate pathname */
331bb42e65bSitojun 		if (strchr(p, '/') || strchr(category, '/'))
332bb42e65bSitojun 			continue;
333bb42e65bSitojun #if 1	/*?*/
334bb42e65bSitojun 		if (strchr(domainname, '/'))
335bb42e65bSitojun 			continue;
336bb42e65bSitojun #endif
337bb42e65bSitojun 
33882974f22Smrg 		int rv = snprintf(buf, len, "%s/%s/%s/%s.mo", dir, p,
339bb42e65bSitojun 		    category, domainname);
34082974f22Smrg 		if (rv > (int)len)
34182974f22Smrg 			return NULL;
342bb42e65bSitojun 		if (stat(buf, &st) < 0)
343bb42e65bSitojun 			continue;
344bb42e65bSitojun 		if ((st.st_mode & S_IFMT) != S_IFREG)
345bb42e65bSitojun 			continue;
346bb42e65bSitojun 
3473bad25dcSminoura 		if (mapit(buf, db) == 0)
348bb42e65bSitojun 			return buf;
349bb42e65bSitojun 	}
350bb42e65bSitojun 
351bb42e65bSitojun 	return NULL;
352bb42e65bSitojun }
353bb42e65bSitojun 
3544271bf2eSjunyoung static uint32_t
3554271bf2eSjunyoung flip(uint32_t v, uint32_t magic)
356bb42e65bSitojun {
357bb42e65bSitojun 
358bb42e65bSitojun 	if (magic == MO_MAGIC)
359bb42e65bSitojun 		return v;
360bb42e65bSitojun 	else if (magic == MO_MAGIC_SWAPPED) {
361bb42e65bSitojun 		v = ((v >> 24) & 0xff) | ((v >> 8) & 0xff00) |
362bb42e65bSitojun 		    ((v << 8) & 0xff0000) | ((v << 24) & 0xff000000);
363bb42e65bSitojun 		return v;
364bb42e65bSitojun 	} else {
365bb42e65bSitojun 		abort();
366bb42e65bSitojun 		/*NOTREACHED*/
367bb42e65bSitojun 	}
368bb42e65bSitojun }
369bb42e65bSitojun 
370bb42e65bSitojun static int
3714271bf2eSjunyoung validate(void *arg, struct mohandle *mohandle)
372bb42e65bSitojun {
373bb42e65bSitojun 	char *p;
374bb42e65bSitojun 
375bb42e65bSitojun 	p = (char *)arg;
3763bad25dcSminoura 	if (p < (char *)mohandle->addr ||
3773bad25dcSminoura 	    p > (char *)mohandle->addr + mohandle->len)
378bb42e65bSitojun 		return 0;
379bb42e65bSitojun 	else
380bb42e65bSitojun 		return 1;
381bb42e65bSitojun }
382bb42e65bSitojun 
383922c0d00Stshiozak /*
384922c0d00Stshiozak  * calculate the step value if the hash value is conflicted.
385922c0d00Stshiozak  */
3864271bf2eSjunyoung static __inline uint32_t
3874271bf2eSjunyoung calc_collision_step(uint32_t hashval, uint32_t hashsize)
388922c0d00Stshiozak {
389922c0d00Stshiozak 	_DIAGASSERT(hashsize>2);
390922c0d00Stshiozak 	return (hashval % (hashsize - 2)) + 1;
391922c0d00Stshiozak }
392922c0d00Stshiozak 
393922c0d00Stshiozak /*
394922c0d00Stshiozak  * calculate the next index while conflicting.
395922c0d00Stshiozak  */
3964271bf2eSjunyoung static __inline uint32_t
3974271bf2eSjunyoung calc_next_index(uint32_t curidx, uint32_t hashsize, uint32_t step)
398922c0d00Stshiozak {
399922c0d00Stshiozak 	return curidx+step - (curidx >= hashsize-step ? hashsize : 0);
400922c0d00Stshiozak }
401922c0d00Stshiozak 
402922c0d00Stshiozak static int
4034271bf2eSjunyoung get_sysdep_string_table(struct mosysdepstr_h **table_h, uint32_t *ofstable,
4044271bf2eSjunyoung 			uint32_t nstrings, uint32_t magic, char *base)
405922c0d00Stshiozak {
406e1a2f47fSmatt 	unsigned int i;
407e1a2f47fSmatt 	int j, count;
408922c0d00Stshiozak 	size_t l;
409922c0d00Stshiozak 	struct mosysdepstr *table;
410922c0d00Stshiozak 
411922c0d00Stshiozak 	for (i=0; i<nstrings; i++) {
412922c0d00Stshiozak 		/* get mosysdepstr record */
413922c0d00Stshiozak 		/* LINTED: ignore the alignment problem. */
414922c0d00Stshiozak 		table = (struct mosysdepstr *)(base + flip(ofstable[i], magic));
415922c0d00Stshiozak 		/* count number of segments */
416922c0d00Stshiozak 		count = 0;
417922c0d00Stshiozak 		while (flip(table->segs[count++].ref, magic) != MO_LASTSEG)
418922c0d00Stshiozak 			;
419922c0d00Stshiozak 		/* get table */
420922c0d00Stshiozak 		l = sizeof(struct mosysdepstr_h) +
421922c0d00Stshiozak 		    sizeof(struct mosysdepsegentry_h) * (count-1);
422922c0d00Stshiozak 		table_h[i] = (struct mosysdepstr_h *)malloc(l);
423922c0d00Stshiozak 		if (!table_h[i])
424922c0d00Stshiozak 			return -1;
425922c0d00Stshiozak 		memset(table_h[i], 0, l);
426922c0d00Stshiozak 		table_h[i]->off = (const char *)(base + flip(table->off, magic));
427922c0d00Stshiozak 		for (j=0; j<count; j++) {
428922c0d00Stshiozak 			table_h[i]->segs[j].len =
429922c0d00Stshiozak 			    flip(table->segs[j].len, magic);
430922c0d00Stshiozak 			table_h[i]->segs[j].ref =
431922c0d00Stshiozak 			    flip(table->segs[j].ref, magic);
432922c0d00Stshiozak 		}
433922c0d00Stshiozak 		/* LINTED: ignore the alignment problem. */
434922c0d00Stshiozak 		table = (struct mosysdepstr *)&table->segs[count];
435922c0d00Stshiozak 	}
436922c0d00Stshiozak 	return 0;
437922c0d00Stshiozak }
438922c0d00Stshiozak 
439922c0d00Stshiozak static int
440922c0d00Stshiozak expand_sysdep(struct mohandle *mohandle, struct mosysdepstr_h *str)
441922c0d00Stshiozak {
442922c0d00Stshiozak 	int i;
443922c0d00Stshiozak 	const char *src;
444922c0d00Stshiozak 	char *dst;
445922c0d00Stshiozak 
446922c0d00Stshiozak 	/* check whether already expanded */
447922c0d00Stshiozak 	if (str->expanded)
448922c0d00Stshiozak 		return 0;
449922c0d00Stshiozak 
450922c0d00Stshiozak 	/* calc total length */
451922c0d00Stshiozak 	str->expanded_len = 1;
452922c0d00Stshiozak 	for (i=0; /*CONSTCOND*/1; i++) {
453922c0d00Stshiozak 		str->expanded_len += str->segs[i].len;
454922c0d00Stshiozak 		if (str->segs[i].ref == MO_LASTSEG)
455922c0d00Stshiozak 			break;
456922c0d00Stshiozak 		str->expanded_len +=
457922c0d00Stshiozak 		    mohandle->mo.mo_sysdep_segs[str->segs[i].ref].len;
458922c0d00Stshiozak 	}
459922c0d00Stshiozak 	/* expand */
460922c0d00Stshiozak 	str->expanded = malloc(str->expanded_len);
461922c0d00Stshiozak 	if (!str->expanded)
462922c0d00Stshiozak 		return -1;
463922c0d00Stshiozak 	src = str->off;
464922c0d00Stshiozak 	dst = str->expanded;
465922c0d00Stshiozak 	for (i=0; /*CONSTCOND*/1; i++) {
466922c0d00Stshiozak 		memcpy(dst, src, str->segs[i].len);
467922c0d00Stshiozak 		src += str->segs[i].len;
468922c0d00Stshiozak 		dst += str->segs[i].len;
469922c0d00Stshiozak 		if (str->segs[i].ref == MO_LASTSEG)
470922c0d00Stshiozak 			break;
471922c0d00Stshiozak 		memcpy(dst, mohandle->mo.mo_sysdep_segs[str->segs[i].ref].str,
472922c0d00Stshiozak 		       mohandle->mo.mo_sysdep_segs[str->segs[i].ref].len);
473922c0d00Stshiozak 		dst += mohandle->mo.mo_sysdep_segs[str->segs[i].ref].len;
474922c0d00Stshiozak 	}
475922c0d00Stshiozak 	*dst = '\0';
476922c0d00Stshiozak 
477922c0d00Stshiozak 	return 0;
478922c0d00Stshiozak }
479922c0d00Stshiozak 
480922c0d00Stshiozak static void
4814271bf2eSjunyoung insert_to_hash(uint32_t *htable, uint32_t hsize, const char *str, uint32_t ref)
482922c0d00Stshiozak {
4834271bf2eSjunyoung 	uint32_t hashval, idx, step;
484922c0d00Stshiozak 
485922c0d00Stshiozak 	hashval = __intl_string_hash(str);
486922c0d00Stshiozak 	step = calc_collision_step(hashval, hsize);
487922c0d00Stshiozak 	idx = hashval % hsize;
488922c0d00Stshiozak 
489922c0d00Stshiozak 	while (htable[idx])
490922c0d00Stshiozak 		idx = calc_next_index(idx, hsize, step);
491922c0d00Stshiozak 
492922c0d00Stshiozak 	htable[idx] = ref;
493922c0d00Stshiozak }
494922c0d00Stshiozak 
495922c0d00Stshiozak static int
496922c0d00Stshiozak setup_sysdep_stuffs(struct mo *mo, struct mohandle *mohandle, char *base)
497922c0d00Stshiozak {
4984271bf2eSjunyoung 	uint32_t magic;
499922c0d00Stshiozak 	struct moentry *stable;
500922c0d00Stshiozak 	size_t l;
501e1a2f47fSmatt 	unsigned int i;
502922c0d00Stshiozak 	char *v;
5034271bf2eSjunyoung 	uint32_t *ofstable;
504922c0d00Stshiozak 
505922c0d00Stshiozak 	magic = mo->mo_magic;
506922c0d00Stshiozak 
507922c0d00Stshiozak 	mohandle->mo.mo_sysdep_nsegs = flip(mo->mo_sysdep_nsegs, magic);
508922c0d00Stshiozak 	mohandle->mo.mo_sysdep_nstring = flip(mo->mo_sysdep_nstring, magic);
509922c0d00Stshiozak 
510922c0d00Stshiozak 	if (mohandle->mo.mo_sysdep_nstring == 0)
511922c0d00Stshiozak 		return 0;
512922c0d00Stshiozak 
513922c0d00Stshiozak 	/* check hash size */
514922c0d00Stshiozak 	if (mohandle->mo.mo_hsize <= 2 ||
515922c0d00Stshiozak 	    mohandle->mo.mo_hsize <
516922c0d00Stshiozak 	    (mohandle->mo.mo_nstring + mohandle->mo.mo_sysdep_nstring))
517922c0d00Stshiozak 		return -1;
518922c0d00Stshiozak 
519922c0d00Stshiozak 	/* get sysdep segments */
5204882631eSyamt 	l = sizeof(struct mosysdepsegs_h) * mohandle->mo.mo_sysdep_nsegs;
521922c0d00Stshiozak 	mohandle->mo.mo_sysdep_segs = (struct mosysdepsegs_h *)malloc(l);
522922c0d00Stshiozak 	if (!mohandle->mo.mo_sysdep_segs)
523922c0d00Stshiozak 		return -1;
524922c0d00Stshiozak 	/* LINTED: ignore the alignment problem. */
525922c0d00Stshiozak 	stable = (struct moentry *)(base + flip(mo->mo_sysdep_segoff, magic));
526922c0d00Stshiozak 	for (i=0; i<mohandle->mo.mo_sysdep_nsegs; i++) {
527922c0d00Stshiozak 		v = base + flip(stable[i].off, magic);
528922c0d00Stshiozak 		mohandle->mo.mo_sysdep_segs[i].str =
529922c0d00Stshiozak 		    __intl_sysdep_get_string_by_tag(
530922c0d00Stshiozak 			    v,
531922c0d00Stshiozak 			    &mohandle->mo.mo_sysdep_segs[i].len);
532922c0d00Stshiozak 	}
533922c0d00Stshiozak 
534922c0d00Stshiozak 	/* get sysdep string table */
535922c0d00Stshiozak 	mohandle->mo.mo_sysdep_otable =
536922c0d00Stshiozak 	    (struct mosysdepstr_h **)calloc(mohandle->mo.mo_sysdep_nstring,
537922c0d00Stshiozak 					    sizeof(struct mosysdepstr_h *));
538922c0d00Stshiozak 	if (!mohandle->mo.mo_sysdep_otable)
539922c0d00Stshiozak 		return -1;
540922c0d00Stshiozak 	/* LINTED: ignore the alignment problem. */
5414271bf2eSjunyoung 	ofstable = (uint32_t *)(base + flip(mo->mo_sysdep_otable, magic));
542922c0d00Stshiozak 	if (get_sysdep_string_table(mohandle->mo.mo_sysdep_otable, ofstable,
543922c0d00Stshiozak 				    mohandle->mo.mo_sysdep_nstring, magic,
544922c0d00Stshiozak 				    base))
545922c0d00Stshiozak 		return -1;
546922c0d00Stshiozak 	mohandle->mo.mo_sysdep_ttable =
547922c0d00Stshiozak 	    (struct mosysdepstr_h **)calloc(mohandle->mo.mo_sysdep_nstring,
548922c0d00Stshiozak 					    sizeof(struct mosysdepstr_h *));
549922c0d00Stshiozak 	if (!mohandle->mo.mo_sysdep_ttable)
550922c0d00Stshiozak 		return -1;
551922c0d00Stshiozak 	/* LINTED: ignore the alignment problem. */
5524271bf2eSjunyoung 	ofstable = (uint32_t *)(base + flip(mo->mo_sysdep_ttable, magic));
553922c0d00Stshiozak 	if (get_sysdep_string_table(mohandle->mo.mo_sysdep_ttable, ofstable,
554922c0d00Stshiozak 				    mohandle->mo.mo_sysdep_nstring, magic,
555922c0d00Stshiozak 				    base))
556922c0d00Stshiozak 		return -1;
557922c0d00Stshiozak 
558922c0d00Stshiozak 	/* update hash */
559922c0d00Stshiozak 	for (i=0; i<mohandle->mo.mo_sysdep_nstring; i++) {
560922c0d00Stshiozak 		if (expand_sysdep(mohandle, mohandle->mo.mo_sysdep_otable[i]))
561922c0d00Stshiozak 			return -1;
562922c0d00Stshiozak 		insert_to_hash(mohandle->mo.mo_htable,
563922c0d00Stshiozak 			       mohandle->mo.mo_hsize,
564922c0d00Stshiozak 			       mohandle->mo.mo_sysdep_otable[i]->expanded,
565922c0d00Stshiozak 			       (i+1) | MO_HASH_SYSDEP_MASK);
566922c0d00Stshiozak 	}
567922c0d00Stshiozak 
568922c0d00Stshiozak 	return 0;
569922c0d00Stshiozak }
570922c0d00Stshiozak 
571bb42e65bSitojun int
5724271bf2eSjunyoung mapit(const char *path, struct domainbinding *db)
573bb42e65bSitojun {
574bb42e65bSitojun 	int fd;
575bb42e65bSitojun 	struct stat st;
576bb42e65bSitojun 	char *base;
5774271bf2eSjunyoung 	uint32_t magic, revision, flags = 0;
578bb42e65bSitojun 	struct moentry *otable, *ttable;
5794271bf2eSjunyoung 	const uint32_t *htable;
580bb42e65bSitojun 	struct moentry_h *p;
581bb42e65bSitojun 	struct mo *mo;
582dd416aa8Stshiozak 	size_t l, headerlen;
583e1a2f47fSmatt 	unsigned int i;
584bb42e65bSitojun 	char *v;
5853bad25dcSminoura 	struct mohandle *mohandle = &db->mohandle;
586bb42e65bSitojun 
5873bad25dcSminoura 	if (mohandle->addr && mohandle->addr != MAP_FAILED &&
5883bad25dcSminoura 	    mohandle->mo.mo_magic)
589bb42e65bSitojun 		return 0;	/*already opened*/
590bb42e65bSitojun 
5913bad25dcSminoura 	unmapit(db);
592bb42e65bSitojun 
593bb42e65bSitojun #if 0
594bb42e65bSitojun 	if (secure_path(path) != 0)
595bb42e65bSitojun 		goto fail;
596bb42e65bSitojun #endif
597bb42e65bSitojun 	if (stat(path, &st) < 0)
598bb42e65bSitojun 		goto fail;
599bb42e65bSitojun 	if ((st.st_mode & S_IFMT) != S_IFREG || st.st_size > GETTEXT_MMAP_MAX)
600bb42e65bSitojun 		goto fail;
601bb42e65bSitojun 	fd = open(path, O_RDONLY);
602bb42e65bSitojun 	if (fd < 0)
603bb42e65bSitojun 		goto fail;
604435b1e30Sitojun 	if (read(fd, &magic, sizeof(magic)) != sizeof(magic) ||
605bb42e65bSitojun 	    (magic != MO_MAGIC && magic != MO_MAGIC_SWAPPED)) {
606bb42e65bSitojun 		close(fd);
607bb42e65bSitojun 		goto fail;
608bb42e65bSitojun 	}
60929be49b4Stshiozak 	if (read(fd, &revision, sizeof(revision)) != sizeof(revision)) {
61029be49b4Stshiozak 		close(fd);
61129be49b4Stshiozak 		goto fail;
61229be49b4Stshiozak 	}
61329be49b4Stshiozak 	switch (flip(revision, magic)) {
61429be49b4Stshiozak 	case MO_MAKE_REV(0, 0):
615922c0d00Stshiozak 		break;
61629be49b4Stshiozak 	case MO_MAKE_REV(0, 1):
61729be49b4Stshiozak 	case MO_MAKE_REV(1, 1):
618922c0d00Stshiozak 		flags |= MO_F_SYSDEP;
61929be49b4Stshiozak 		break;
62029be49b4Stshiozak 	default:
621bb42e65bSitojun 		close(fd);
622bb42e65bSitojun 		goto fail;
623bb42e65bSitojun 	}
6243bad25dcSminoura 	mohandle->addr = mmap(NULL, (size_t)st.st_size, PROT_READ,
625099fdc2aSitojun 	    MAP_FILE | MAP_SHARED, fd, (off_t)0);
6263bad25dcSminoura 	if (!mohandle->addr || mohandle->addr == MAP_FAILED) {
627bb42e65bSitojun 		close(fd);
628bb42e65bSitojun 		goto fail;
629bb42e65bSitojun 	}
630bb42e65bSitojun 	close(fd);
6313bad25dcSminoura 	mohandle->len = (size_t)st.st_size;
632bb42e65bSitojun 
6333bad25dcSminoura 	base = mohandle->addr;
6343bad25dcSminoura 	mo = (struct mo *)mohandle->addr;
635bb42e65bSitojun 
636bb42e65bSitojun 	/* flip endian.  do not flip magic number! */
6373bad25dcSminoura 	mohandle->mo.mo_magic = mo->mo_magic;
6383bad25dcSminoura 	mohandle->mo.mo_revision = flip(mo->mo_revision, magic);
6393bad25dcSminoura 	mohandle->mo.mo_nstring = flip(mo->mo_nstring, magic);
64029be49b4Stshiozak 	mohandle->mo.mo_hsize = flip(mo->mo_hsize, magic);
641922c0d00Stshiozak 	mohandle->mo.mo_flags = flags;
642bb42e65bSitojun 
643bb42e65bSitojun 	/* validate otable/ttable */
64429be49b4Stshiozak 	/* LINTED: ignore the alignment problem. */
645bb42e65bSitojun 	otable = (struct moentry *)(base + flip(mo->mo_otable, magic));
64629be49b4Stshiozak 	/* LINTED: ignore the alignment problem. */
647bb42e65bSitojun 	ttable = (struct moentry *)(base + flip(mo->mo_ttable, magic));
6483bad25dcSminoura 	if (!validate(otable, mohandle) ||
6493bad25dcSminoura 	    !validate(&otable[mohandle->mo.mo_nstring], mohandle)) {
6503bad25dcSminoura 		unmapit(db);
651bb42e65bSitojun 		goto fail;
652bb42e65bSitojun 	}
6533bad25dcSminoura 	if (!validate(ttable, mohandle) ||
6543bad25dcSminoura 	    !validate(&ttable[mohandle->mo.mo_nstring], mohandle)) {
6553bad25dcSminoura 		unmapit(db);
656bb42e65bSitojun 		goto fail;
657bb42e65bSitojun 	}
658bb42e65bSitojun 
659bb42e65bSitojun 	/* allocate [ot]table, and convert to normal pointer representation. */
6603bad25dcSminoura 	l = sizeof(struct moentry_h) * mohandle->mo.mo_nstring;
6613bad25dcSminoura 	mohandle->mo.mo_otable = (struct moentry_h *)malloc(l);
6623bad25dcSminoura 	if (!mohandle->mo.mo_otable) {
6633bad25dcSminoura 		unmapit(db);
664bb42e65bSitojun 		goto fail;
665bb42e65bSitojun 	}
6663bad25dcSminoura 	mohandle->mo.mo_ttable = (struct moentry_h *)malloc(l);
6673bad25dcSminoura 	if (!mohandle->mo.mo_ttable) {
6683bad25dcSminoura 		unmapit(db);
669bb42e65bSitojun 		goto fail;
670bb42e65bSitojun 	}
6713bad25dcSminoura 	p = mohandle->mo.mo_otable;
6723bad25dcSminoura 	for (i = 0; i < mohandle->mo.mo_nstring; i++) {
673bb42e65bSitojun 		p[i].len = flip(otable[i].len, magic);
674bb42e65bSitojun 		p[i].off = base + flip(otable[i].off, magic);
675bb42e65bSitojun 
6763bad25dcSminoura 		if (!validate(p[i].off, mohandle) ||
6773bad25dcSminoura 		    !validate(p[i].off + p[i].len + 1, mohandle)) {
6783bad25dcSminoura 			unmapit(db);
679bb42e65bSitojun 			goto fail;
680bb42e65bSitojun 		}
681bb42e65bSitojun 	}
6823bad25dcSminoura 	p = mohandle->mo.mo_ttable;
6833bad25dcSminoura 	for (i = 0; i < mohandle->mo.mo_nstring; i++) {
684bb42e65bSitojun 		p[i].len = flip(ttable[i].len, magic);
685bb42e65bSitojun 		p[i].off = base + flip(ttable[i].off, magic);
686bb42e65bSitojun 
6873bad25dcSminoura 		if (!validate(p[i].off, mohandle) ||
6883bad25dcSminoura 		    !validate(p[i].off + p[i].len + 1, mohandle)) {
6893bad25dcSminoura 			unmapit(db);
690bb42e65bSitojun 			goto fail;
691bb42e65bSitojun 		}
692bb42e65bSitojun 	}
69329be49b4Stshiozak 	/* allocate htable, and convert it to the host order. */
69429be49b4Stshiozak 	if (mohandle->mo.mo_hsize > 2) {
6954271bf2eSjunyoung 		l = sizeof(uint32_t) * mohandle->mo.mo_hsize;
6964271bf2eSjunyoung 		mohandle->mo.mo_htable = (uint32_t *)malloc(l);
69729be49b4Stshiozak 		if (!mohandle->mo.mo_htable) {
69829be49b4Stshiozak 			unmapit(db);
69929be49b4Stshiozak 			goto fail;
70029be49b4Stshiozak 		}
70129be49b4Stshiozak 		/* LINTED: ignore the alignment problem. */
7024271bf2eSjunyoung 		htable = (const uint32_t *)(base+flip(mo->mo_hoffset, magic));
70329be49b4Stshiozak 		for (i=0; i < mohandle->mo.mo_hsize; i++) {
70429be49b4Stshiozak 			mohandle->mo.mo_htable[i] = flip(htable[i], magic);
70529be49b4Stshiozak 			if (mohandle->mo.mo_htable[i] >=
70629be49b4Stshiozak 			    mohandle->mo.mo_nstring+1) {
70729be49b4Stshiozak 				/* illegal string number. */
70829be49b4Stshiozak 				unmapit(db);
70929be49b4Stshiozak 				goto fail;
71029be49b4Stshiozak 			}
71129be49b4Stshiozak 		}
71229be49b4Stshiozak 	}
713bb42e65bSitojun 	/* grab MIME-header and charset field */
714dd416aa8Stshiozak 	mohandle->mo.mo_header = lookup("", db, &headerlen);
7153bad25dcSminoura 	if (mohandle->mo.mo_header)
7163bad25dcSminoura 		v = strstr(mohandle->mo.mo_header, "charset=");
717bb42e65bSitojun 	else
718bb42e65bSitojun 		v = NULL;
719bb42e65bSitojun 	if (v) {
7203bad25dcSminoura 		mohandle->mo.mo_charset = strdup(v + 8);
7213bad25dcSminoura 		if (!mohandle->mo.mo_charset)
7223280f611Sitojun 			goto fail;
7233bad25dcSminoura 		v = strchr(mohandle->mo.mo_charset, '\n');
724bb42e65bSitojun 		if (v)
725bb42e65bSitojun 			*v = '\0';
726bb42e65bSitojun 	}
727dc7498dfStnozaki 	if (!mohandle->mo.mo_header ||
728dc7498dfStnozaki 	    _gettext_parse_plural(&mohandle->mo.mo_plural,
729dd416aa8Stshiozak 				  &mohandle->mo.mo_nplurals,
730dd416aa8Stshiozak 				  mohandle->mo.mo_header, headerlen))
731dd416aa8Stshiozak 		mohandle->mo.mo_plural = NULL;
732bb42e65bSitojun 
733bb42e65bSitojun 	/*
734bb42e65bSitojun 	 * XXX check charset, reject it if we are unable to support the charset
735bb42e65bSitojun 	 * with the current locale.
736bb42e65bSitojun 	 * for example, if we are using euc-jp locale and we are looking at
737bb42e65bSitojun 	 * *.mo file encoded by euc-kr (charset=euc-kr), we should reject
738bb42e65bSitojun 	 * the *.mo file as we cannot support it.
739bb42e65bSitojun 	 */
740bb42e65bSitojun 
741922c0d00Stshiozak 	/* system dependent string support */
742922c0d00Stshiozak 	if ((mohandle->mo.mo_flags & MO_F_SYSDEP) != 0) {
743922c0d00Stshiozak 		if (setup_sysdep_stuffs(mo, mohandle, base)) {
744922c0d00Stshiozak 			unmapit(db);
745922c0d00Stshiozak 			goto fail;
746922c0d00Stshiozak 		}
747922c0d00Stshiozak 	}
748922c0d00Stshiozak 
749bb42e65bSitojun 	return 0;
750bb42e65bSitojun 
751bb42e65bSitojun fail:
752bb42e65bSitojun 	return -1;
753bb42e65bSitojun }
754bb42e65bSitojun 
755922c0d00Stshiozak static void
7564271bf2eSjunyoung free_sysdep_table(struct mosysdepstr_h **table, uint32_t nstring)
757922c0d00Stshiozak {
758922c0d00Stshiozak 
7595297022aSchristos 	if (! table)
7605297022aSchristos 		return;
7615297022aSchristos 
7625297022aSchristos 	for (uint32_t i = 0; i < nstring; i++) {
763922c0d00Stshiozak 		if (table[i]) {
764922c0d00Stshiozak 			free(table[i]->expanded);
765922c0d00Stshiozak 			free(table[i]);
766922c0d00Stshiozak 		}
767922c0d00Stshiozak 	}
768922c0d00Stshiozak 	free(table);
769922c0d00Stshiozak }
770922c0d00Stshiozak 
771bb42e65bSitojun static int
7724271bf2eSjunyoung unmapit(struct domainbinding *db)
773bb42e65bSitojun {
7743bad25dcSminoura 	struct mohandle *mohandle = &db->mohandle;
775bb42e65bSitojun 
776bb42e65bSitojun 	/* unmap if there's already mapped region */
7773bad25dcSminoura 	if (mohandle->addr && mohandle->addr != MAP_FAILED)
7783bad25dcSminoura 		munmap(mohandle->addr, mohandle->len);
7793bad25dcSminoura 	mohandle->addr = NULL;
7803bad25dcSminoura 	free(mohandle->mo.mo_otable);
7813bad25dcSminoura 	free(mohandle->mo.mo_ttable);
7823bad25dcSminoura 	free(mohandle->mo.mo_charset);
78329be49b4Stshiozak 	free(mohandle->mo.mo_htable);
784922c0d00Stshiozak 	free(mohandle->mo.mo_sysdep_segs);
785922c0d00Stshiozak 	free_sysdep_table(mohandle->mo.mo_sysdep_otable,
786922c0d00Stshiozak 	    mohandle->mo.mo_sysdep_nstring);
787922c0d00Stshiozak 	free_sysdep_table(mohandle->mo.mo_sysdep_ttable,
788922c0d00Stshiozak 	    mohandle->mo.mo_sysdep_nstring);
789dd416aa8Stshiozak 	_gettext_free_plural(mohandle->mo.mo_plural);
7903bad25dcSminoura 	memset(&mohandle->mo, 0, sizeof(mohandle->mo));
791bb42e65bSitojun 	return 0;
792bb42e65bSitojun }
793bb42e65bSitojun 
7943bad25dcSminoura /* ARGSUSED */
795bb42e65bSitojun static const char *
7964271bf2eSjunyoung lookup_hash(const char *msgid, struct domainbinding *db, size_t *rlen)
797bb42e65bSitojun {
79829be49b4Stshiozak 	struct mohandle *mohandle = &db->mohandle;
7994271bf2eSjunyoung 	uint32_t idx, hashval, step, strno;
80029be49b4Stshiozak 	size_t len;
801922c0d00Stshiozak 	struct mosysdepstr_h *sysdep_otable, *sysdep_ttable;
802bb42e65bSitojun 
80329be49b4Stshiozak 	if (mohandle->mo.mo_hsize <= 2 || mohandle->mo.mo_htable == NULL)
804bb42e65bSitojun 		return NULL;
80529be49b4Stshiozak 
80629be49b4Stshiozak 	hashval = __intl_string_hash(msgid);
80729be49b4Stshiozak 	step = calc_collision_step(hashval, mohandle->mo.mo_hsize);
80829be49b4Stshiozak 	idx = hashval % mohandle->mo.mo_hsize;
80929be49b4Stshiozak 	len = strlen(msgid);
81029be49b4Stshiozak 	while (/*CONSTCOND*/1) {
81129be49b4Stshiozak 		strno = mohandle->mo.mo_htable[idx];
81229be49b4Stshiozak 		if (strno == 0) {
81329be49b4Stshiozak 			/* unexpected miss */
81429be49b4Stshiozak 			return NULL;
81529be49b4Stshiozak 		}
81629be49b4Stshiozak 		strno--;
817922c0d00Stshiozak 		if ((strno & MO_HASH_SYSDEP_MASK) == 0) {
818922c0d00Stshiozak 			/* system independent strings */
81929be49b4Stshiozak 			if (len <= mohandle->mo.mo_otable[strno].len &&
82029be49b4Stshiozak 			    !strcmp(msgid, mohandle->mo.mo_otable[strno].off)) {
82129be49b4Stshiozak 				/* hit */
822dd416aa8Stshiozak 				if (rlen)
823dd416aa8Stshiozak 					*rlen =
824dd416aa8Stshiozak 					    mohandle->mo.mo_ttable[strno].len;
82529be49b4Stshiozak 				return mohandle->mo.mo_ttable[strno].off;
82629be49b4Stshiozak 			}
827922c0d00Stshiozak 		} else {
828922c0d00Stshiozak 			/* system dependent strings */
829922c0d00Stshiozak 			strno &= ~MO_HASH_SYSDEP_MASK;
830922c0d00Stshiozak 			sysdep_otable = mohandle->mo.mo_sysdep_otable[strno];
831922c0d00Stshiozak 			sysdep_ttable = mohandle->mo.mo_sysdep_ttable[strno];
832922c0d00Stshiozak 			if (len <= sysdep_otable->expanded_len &&
833922c0d00Stshiozak 			    !strcmp(msgid, sysdep_otable->expanded)) {
834922c0d00Stshiozak 				/* hit */
835922c0d00Stshiozak 				if (expand_sysdep(mohandle, sysdep_ttable))
836922c0d00Stshiozak 					/* memory exhausted */
837922c0d00Stshiozak 					return NULL;
838dd416aa8Stshiozak 				if (rlen)
839dd416aa8Stshiozak 					*rlen = sysdep_ttable->expanded_len;
840922c0d00Stshiozak 				return sysdep_ttable->expanded;
841922c0d00Stshiozak 			}
842922c0d00Stshiozak 		}
84329be49b4Stshiozak 		idx = calc_next_index(idx, mohandle->mo.mo_hsize, step);
84429be49b4Stshiozak 	}
84529be49b4Stshiozak 	/*NOTREACHED*/
846bb42e65bSitojun }
847bb42e65bSitojun 
848bb42e65bSitojun static const char *
8494271bf2eSjunyoung lookup_bsearch(const char *msgid, struct domainbinding *db, size_t *rlen)
850bb42e65bSitojun {
851bb42e65bSitojun 	int top, bottom, middle, omiddle;
852bb42e65bSitojun 	int n;
8533bad25dcSminoura 	struct mohandle *mohandle = &db->mohandle;
854bb42e65bSitojun 
855bb42e65bSitojun 	top = 0;
8563bad25dcSminoura 	bottom = mohandle->mo.mo_nstring;
857bb42e65bSitojun 	omiddle = -1;
8583bad25dcSminoura 	/* CONSTCOND */
859bb42e65bSitojun 	while (1) {
860bb42e65bSitojun 		if (top > bottom)
861099fdc2aSitojun 			break;
862bb42e65bSitojun 		middle = (top + bottom) / 2;
863bb42e65bSitojun 		/* avoid possible infinite loop, when the data is not sorted */
864bb42e65bSitojun 		if (omiddle == middle)
865099fdc2aSitojun 			break;
866e1a2f47fSmatt 		if ((size_t)middle >= mohandle->mo.mo_nstring)
867099fdc2aSitojun 			break;
868bb42e65bSitojun 
8693bad25dcSminoura 		n = strcmp(msgid, mohandle->mo.mo_otable[middle].off);
870dd416aa8Stshiozak 		if (n == 0) {
871dd416aa8Stshiozak 			if (rlen)
872dd416aa8Stshiozak 				*rlen = mohandle->mo.mo_ttable[middle].len;
8733bad25dcSminoura 			return (const char *)mohandle->mo.mo_ttable[middle].off;
874dd416aa8Stshiozak 		}
875bb42e65bSitojun 		else if (n < 0)
876bb42e65bSitojun 			bottom = middle;
877bb42e65bSitojun 		else
878bb42e65bSitojun 			top = middle;
879bb42e65bSitojun 		omiddle = middle;
880bb42e65bSitojun 	}
881bb42e65bSitojun 
882bb42e65bSitojun 	return NULL;
883bb42e65bSitojun }
884bb42e65bSitojun 
885bb42e65bSitojun static const char *
8864271bf2eSjunyoung lookup(const char *msgid, struct domainbinding *db, size_t *rlen)
887bb42e65bSitojun {
888bb42e65bSitojun 	const char *v;
889bb42e65bSitojun 
890dd416aa8Stshiozak 	v = lookup_hash(msgid, db, rlen);
891bb42e65bSitojun 	if (v)
892bb42e65bSitojun 		return v;
893bb42e65bSitojun 
894dd416aa8Stshiozak 	return lookup_bsearch(msgid, db, rlen);
895bb42e65bSitojun }
896bb42e65bSitojun 
8970169ab0aSitojun static const char *
8980169ab0aSitojun get_lang_env(const char *category_name)
899782b5c88Syamt {
900782b5c88Syamt 	const char *lang;
901782b5c88Syamt 
9025c3967c0Syamt 	/*
9035c3967c0Syamt 	 * 1. see LANGUAGE variable first.
9045c3967c0Syamt 	 *
9055c3967c0Syamt 	 * LANGUAGE is a GNU extension.
9065c3967c0Syamt 	 * It's a colon separated list of locale names.
9075c3967c0Syamt 	 */
908782b5c88Syamt 	lang = getenv("LANGUAGE");
909782b5c88Syamt 	if (lang)
910782b5c88Syamt 		return lang;
911782b5c88Syamt 
9125c3967c0Syamt 	/*
9135c3967c0Syamt 	 * 2. if LANGUAGE isn't set, see LC_ALL, LC_xxx, LANG.
9145c3967c0Syamt 	 *
9155c3967c0Syamt 	 * It's essentially setlocale(LC_xxx, NULL).
9165c3967c0Syamt 	 */
917782b5c88Syamt 	lang = getenv("LC_ALL");
918782b5c88Syamt 	if (!lang)
9196c208635Syamt 		lang = getenv(category_name);
9206c208635Syamt 	if (!lang)
921782b5c88Syamt 		lang = getenv("LANG");
922782b5c88Syamt 
923782b5c88Syamt 	if (!lang)
924782b5c88Syamt 		return 0; /* error */
925782b5c88Syamt 
926782b5c88Syamt 	return split_locale(lang);
927782b5c88Syamt }
928782b5c88Syamt 
929dd416aa8Stshiozak static const char *
930dd416aa8Stshiozak get_indexed_string(const char *str, size_t len, unsigned long idx)
931dd416aa8Stshiozak {
932dd416aa8Stshiozak 	while (idx > 0) {
933dd416aa8Stshiozak 		if (len <= 1)
934dd416aa8Stshiozak 			return str;
935dd416aa8Stshiozak 		if (*str == '\0')
936dd416aa8Stshiozak 			idx--;
937dd416aa8Stshiozak 		if (len > 0) {
938dd416aa8Stshiozak 			str++;
939dd416aa8Stshiozak 			len--;
940dd416aa8Stshiozak 		}
941dd416aa8Stshiozak 	}
942dd416aa8Stshiozak 	return str;
943dd416aa8Stshiozak }
944dd416aa8Stshiozak 
94562de7e0cSyamt #define	_NGETTEXT_DEFAULT(msgid1, msgid2, n)	\
94662de7e0cSyamt 	((char *)__UNCONST((n) == 1 ? (msgid1) : (msgid2)))
94762de7e0cSyamt 
948bb42e65bSitojun char *
9494271bf2eSjunyoung dcngettext(const char *domainname, const char *msgid1, const char *msgid2,
9504271bf2eSjunyoung 	   unsigned long int n, int category)
951bb42e65bSitojun {
952bb42e65bSitojun 	const char *msgid;
95382974f22Smrg 	char path[PATH_MAX+1];
954782b5c88Syamt 	const char *lpath;
955bb42e65bSitojun 	static char olpath[PATH_MAX];
9563280f611Sitojun 	const char *cname = NULL;
957bb42e65bSitojun 	const char *v;
9583280f611Sitojun 	static char *ocname = NULL;
9593280f611Sitojun 	static char *odomainname = NULL;
960a09524d4Sitojun 	struct domainbinding *db;
9619f3f3adfSlukem 	unsigned long plural_index = 0;
962dd416aa8Stshiozak 	size_t len;
963bb42e65bSitojun 
964bb42e65bSitojun 	if (!domainname)
9653bad25dcSminoura 		domainname = __current_domainname;
966bb42e65bSitojun 	cname = lookup_category(category);
967bb42e65bSitojun 	if (!domainname || !cname)
968bb42e65bSitojun 		goto fail;
969bb42e65bSitojun 
970782b5c88Syamt 	lpath = get_lang_env(cname);
971782b5c88Syamt 	if (!lpath)
972bb42e65bSitojun 		goto fail;
973bb42e65bSitojun 
9743bad25dcSminoura 	for (db = __bindings; db; db = db->next)
975a09524d4Sitojun 		if (strcmp(db->domainname, domainname) == 0)
976a09524d4Sitojun 			break;
9773bad25dcSminoura 	if (!db) {
9783bad25dcSminoura 		if (!bindtextdomain(domainname, _PATH_TEXTDOMAIN))
9793bad25dcSminoura 			goto fail;
9803bad25dcSminoura 		db = __bindings;
9813bad25dcSminoura 	}
982a09524d4Sitojun 
9830c36fb14Syamt 	/* resolve relative path */
9840c36fb14Syamt 	/* XXX not necessary? */
9850c36fb14Syamt 	if (db->path[0] != '/') {
9860c36fb14Syamt 		char buf[PATH_MAX];
9870c36fb14Syamt 
9880c36fb14Syamt 		if (getcwd(buf, sizeof(buf)) == 0)
9890c36fb14Syamt 			goto fail;
9900c36fb14Syamt 		if (strlcat(buf, "/", sizeof(buf)) >= sizeof(buf))
9910c36fb14Syamt 			goto fail;
9920c36fb14Syamt 		if (strlcat(buf, db->path, sizeof(buf)) >= sizeof(buf))
9930c36fb14Syamt 			goto fail;
994edffd458Sitojun 		strlcpy(db->path, buf, sizeof(db->path));
9950c36fb14Syamt 	}
9960c36fb14Syamt 
997bb42e65bSitojun 	/* don't bother looking it up if the values are the same */
998a09524d4Sitojun 	if (odomainname && strcmp(domainname, odomainname) == 0 &&
9993bad25dcSminoura 	    ocname && strcmp(cname, ocname) == 0 && strcmp(lpath, olpath) == 0 &&
10003bad25dcSminoura 	    db->mohandle.mo.mo_magic)
1001bb42e65bSitojun 		goto found;
1002bb42e65bSitojun 
1003bb42e65bSitojun 	/* try to find appropriate file, from $LANGUAGE */
1004a09524d4Sitojun 	if (lookup_mofile(path, sizeof(path), db->path, lpath, cname,
10053bad25dcSminoura 	    domainname, db) == NULL)
1006bb42e65bSitojun 		goto fail;
1007bb42e65bSitojun 
1008a09524d4Sitojun 	free(odomainname);
1009a09524d4Sitojun 	free(ocname);
10105297022aSchristos 
10113280f611Sitojun 	odomainname = strdup(domainname);
1012a09524d4Sitojun 	ocname = strdup(cname);
10133280f611Sitojun 	if (!odomainname || !ocname) {
10143280f611Sitojun 		free(odomainname);
10153280f611Sitojun 		free(ocname);
10165297022aSchristos 
10173280f611Sitojun 		odomainname = ocname = NULL;
10183280f611Sitojun 	}
1019782b5c88Syamt 	else
1020a09524d4Sitojun 		strlcpy(olpath, lpath, sizeof(olpath));
1021a09524d4Sitojun 
1022bb42e65bSitojun found:
1023dd416aa8Stshiozak 	if (db->mohandle.mo.mo_plural) {
1024dd416aa8Stshiozak 		plural_index =
1025dd416aa8Stshiozak 		    _gettext_calculate_plural(db->mohandle.mo.mo_plural, n);
1026dd416aa8Stshiozak 		if (plural_index >= db->mohandle.mo.mo_nplurals)
1027dd416aa8Stshiozak 			plural_index = 0;
1028dd416aa8Stshiozak 		msgid = msgid1;
1029dd416aa8Stshiozak 	} else
103062de7e0cSyamt 		msgid = _NGETTEXT_DEFAULT(msgid1, msgid2, n);
1031dd416aa8Stshiozak 
1032dd416aa8Stshiozak 	if (msgid == NULL)
1033dd416aa8Stshiozak 		return NULL;
1034dd416aa8Stshiozak 
1035dd416aa8Stshiozak 	v = lookup(msgid, db, &len);
1036bb42e65bSitojun 	if (v) {
1037dd416aa8Stshiozak 		if (db->mohandle.mo.mo_plural)
1038dd416aa8Stshiozak 			v = get_indexed_string(v, len, plural_index);
1039bb42e65bSitojun 		/*
1040f3ec2d57Syamt 		 * convert the translated message's encoding.
1041f3ec2d57Syamt 		 *
1042f3ec2d57Syamt 		 * special case:
1043f3ec2d57Syamt 		 *	a result of gettext("") shouldn't need any conversion.
1044bb42e65bSitojun 		 */
1045f3ec2d57Syamt 		if (msgid[0])
1046f3ec2d57Syamt 			v = __gettext_iconv(v, db);
1047bb42e65bSitojun 
1048bb42e65bSitojun 		/*
1049bb42e65bSitojun 		 * Given the amount of printf-format security issues, it may
1050bb42e65bSitojun 		 * be a good idea to validate if the original msgid and the
1051bb42e65bSitojun 		 * translated message format string carry the same printf-like
1052bb42e65bSitojun 		 * format identifiers.
1053bb42e65bSitojun 		 */
1054bb42e65bSitojun 
1055bb42e65bSitojun 		msgid = v;
1056bb42e65bSitojun 	}
1057bb42e65bSitojun 
105829be49b4Stshiozak 	return (char *)__UNCONST(msgid);
105962de7e0cSyamt 
106062de7e0cSyamt fail:
106162de7e0cSyamt 	return _NGETTEXT_DEFAULT(msgid1, msgid2, n);
1062bb42e65bSitojun }
1063