xref: /netbsd-src/lib/libintl/gettext.c (revision bb42e65bfecc3a76253c9b8889051cfe941a4b9a)
1*bb42e65bSitojun /*	$NetBSD: gettext.c,v 1.1.1.1 2000/10/31 10:45:04 itojun Exp $	*/
2*bb42e65bSitojun 
3*bb42e65bSitojun /*-
4*bb42e65bSitojun  * Copyright (c) 2000 Citrus Project,
5*bb42e65bSitojun  * All rights reserved.
6*bb42e65bSitojun  *
7*bb42e65bSitojun  * Redistribution and use in source and binary forms, with or without
8*bb42e65bSitojun  * modification, are permitted provided that the following conditions
9*bb42e65bSitojun  * are met:
10*bb42e65bSitojun  * 1. Redistributions of source code must retain the above copyright
11*bb42e65bSitojun  *    notice, this list of conditions and the following disclaimer.
12*bb42e65bSitojun  * 2. Redistributions in binary form must reproduce the above copyright
13*bb42e65bSitojun  *    notice, this list of conditions and the following disclaimer in the
14*bb42e65bSitojun  *    documentation and/or other materials provided with the distribution.
15*bb42e65bSitojun  *
16*bb42e65bSitojun  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*bb42e65bSitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*bb42e65bSitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*bb42e65bSitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*bb42e65bSitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*bb42e65bSitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*bb42e65bSitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*bb42e65bSitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*bb42e65bSitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*bb42e65bSitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*bb42e65bSitojun  * SUCH DAMAGE.
27*bb42e65bSitojun  */
28*bb42e65bSitojun 
29*bb42e65bSitojun #include <sys/cdefs.h>
30*bb42e65bSitojun #if defined(LIBC_SCCS) && !defined(lint)
31*bb42e65bSitojun __RCSID("$NetBSD: gettext.c,v 1.1.1.1 2000/10/31 10:45:04 itojun Exp $");
32*bb42e65bSitojun #endif /* LIBC_SCCS and not lint */
33*bb42e65bSitojun 
34*bb42e65bSitojun #include <sys/types.h>
35*bb42e65bSitojun #include <sys/param.h>
36*bb42e65bSitojun #include <sys/stat.h>
37*bb42e65bSitojun #include <sys/mman.h>
38*bb42e65bSitojun #include <sys/uio.h>
39*bb42e65bSitojun 
40*bb42e65bSitojun #include <fcntl.h>
41*bb42e65bSitojun #include <stdio.h>
42*bb42e65bSitojun #include <stdlib.h>
43*bb42e65bSitojun #include <unistd.h>
44*bb42e65bSitojun #include <string.h>
45*bb42e65bSitojun #if 0
46*bb42e65bSitojun #include <util.h>
47*bb42e65bSitojun #endif
48*bb42e65bSitojun #include <libintl.h>
49*bb42e65bSitojun #include <locale.h>
50*bb42e65bSitojun #include "libintl_local.h"
51*bb42e65bSitojun #include "pathnames.h"
52*bb42e65bSitojun 
53*bb42e65bSitojun static struct mohandle mohandle;
54*bb42e65bSitojun 
55*bb42e65bSitojun static const char *lookup_category __P((int));
56*bb42e65bSitojun static const char *split_locale __P((const char *));
57*bb42e65bSitojun static const char *lookup_mofile __P((char *, size_t, const char *,
58*bb42e65bSitojun 	char *, const char *, const char *));
59*bb42e65bSitojun static u_int32_t flip __P((u_int32_t, u_int32_t));
60*bb42e65bSitojun static int validate __P((void *));
61*bb42e65bSitojun static int mapit __P((const char *));
62*bb42e65bSitojun static int unmapit __P((void));
63*bb42e65bSitojun static const char *lookup_hash __P((const char *));
64*bb42e65bSitojun static const char *lookup_bsearch __P((const char *));
65*bb42e65bSitojun static const char *lookup __P((const char *));
66*bb42e65bSitojun 
67*bb42e65bSitojun /*
68*bb42e65bSitojun  * shortcut functions.  the main implementation resides in dcngettext().
69*bb42e65bSitojun  */
70*bb42e65bSitojun char *
71*bb42e65bSitojun gettext(msgid)
72*bb42e65bSitojun 	const char *msgid;
73*bb42e65bSitojun {
74*bb42e65bSitojun 
75*bb42e65bSitojun 	return dcngettext(NULL, msgid, NULL, 1UL, LC_MESSAGES);
76*bb42e65bSitojun }
77*bb42e65bSitojun 
78*bb42e65bSitojun char *
79*bb42e65bSitojun dgettext(domainname, msgid)
80*bb42e65bSitojun 	const char *domainname;
81*bb42e65bSitojun 	const char *msgid;
82*bb42e65bSitojun {
83*bb42e65bSitojun 
84*bb42e65bSitojun 	return dcngettext(domainname, msgid, NULL, 1UL, LC_MESSAGES);
85*bb42e65bSitojun }
86*bb42e65bSitojun 
87*bb42e65bSitojun char *
88*bb42e65bSitojun dcgettext(domainname, msgid, category)
89*bb42e65bSitojun 	const char *domainname;
90*bb42e65bSitojun 	const char *msgid;
91*bb42e65bSitojun 	int category;
92*bb42e65bSitojun {
93*bb42e65bSitojun 
94*bb42e65bSitojun 	return dcngettext(domainname, msgid, NULL, 1UL, category);
95*bb42e65bSitojun }
96*bb42e65bSitojun 
97*bb42e65bSitojun char *
98*bb42e65bSitojun ngettext(msgid1, msgid2, n)
99*bb42e65bSitojun 	const char *msgid1;
100*bb42e65bSitojun 	const char *msgid2;
101*bb42e65bSitojun 	unsigned long int n;
102*bb42e65bSitojun {
103*bb42e65bSitojun 
104*bb42e65bSitojun 	return dcngettext(NULL, msgid1, msgid2, n, LC_MESSAGES);
105*bb42e65bSitojun }
106*bb42e65bSitojun 
107*bb42e65bSitojun char *
108*bb42e65bSitojun dngettext(domainname, msgid1, msgid2, n)
109*bb42e65bSitojun 	const char *domainname;
110*bb42e65bSitojun 	const char *msgid1;
111*bb42e65bSitojun 	const char *msgid2;
112*bb42e65bSitojun 	unsigned long int n;
113*bb42e65bSitojun {
114*bb42e65bSitojun 
115*bb42e65bSitojun 	return dcngettext(domainname, msgid1, msgid2, n, LC_MESSAGES);
116*bb42e65bSitojun }
117*bb42e65bSitojun 
118*bb42e65bSitojun /*
119*bb42e65bSitojun  * dcngettext() -
120*bb42e65bSitojun  * lookup internationalized message on database locale/category/domainname
121*bb42e65bSitojun  * (like ja_JP.eucJP/LC_MESSAGES/domainname).
122*bb42e65bSitojun  * if n equals to 1, internationalized message will be looked up for msgid1.
123*bb42e65bSitojun  * otherwise, message will be looked up for msgid2.
124*bb42e65bSitojun  * if the lookup fails, the function will return msgid1 or msgid2 as is.
125*bb42e65bSitojun  *
126*bb42e65bSitojun  * Even though the return type is "char *", caller should not rewrite the
127*bb42e65bSitojun  * region pointed to by the return value (should be "const char *", but can't
128*bb42e65bSitojun  * change it for compatibility with other implementations).
129*bb42e65bSitojun  *
130*bb42e65bSitojun  * by default (if domainname == NULL), domainname is taken from the value set
131*bb42e65bSitojun  * by textdomain().  usually name of the application (like "ls") is used as
132*bb42e65bSitojun  * domainname.  category is usually LC_MESSAGES.
133*bb42e65bSitojun  *
134*bb42e65bSitojun  * the code reads in *.mo files generated by GNU gettext.  *.mo is a host-
135*bb42e65bSitojun  * endian encoded file.  both endians are supported here, as the files are in
136*bb42e65bSitojun  * /usr/share/locale! (or we should move those files into /usr/libdata)
137*bb42e65bSitojun  */
138*bb42e65bSitojun 
139*bb42e65bSitojun static const char *
140*bb42e65bSitojun lookup_category(category)
141*bb42e65bSitojun 	int category;
142*bb42e65bSitojun {
143*bb42e65bSitojun 
144*bb42e65bSitojun 	switch (category) {
145*bb42e65bSitojun 	case LC_COLLATE:	return "LC_COLLATE";
146*bb42e65bSitojun 	case LC_CTYPE:		return "LC_CTYPE";
147*bb42e65bSitojun 	case LC_MONETARY:	return "LC_MONETARY";
148*bb42e65bSitojun 	case LC_NUMERIC:	return "LC_NUMERIC";
149*bb42e65bSitojun 	case LC_TIME:		return "LC_TIME";
150*bb42e65bSitojun 	case LC_MESSAGES:	return "LC_MESSAGES";
151*bb42e65bSitojun 	}
152*bb42e65bSitojun 	return NULL;
153*bb42e65bSitojun }
154*bb42e65bSitojun 
155*bb42e65bSitojun /*
156*bb42e65bSitojun  * XPG syntax: language[_territory[.codeset]][@modifier]
157*bb42e65bSitojun  * XXX boundary check on "result" is lacking
158*bb42e65bSitojun  */
159*bb42e65bSitojun static const char *
160*bb42e65bSitojun split_locale(lname)
161*bb42e65bSitojun 	const char *lname;
162*bb42e65bSitojun {
163*bb42e65bSitojun 	char buf[BUFSIZ], tmp[BUFSIZ];
164*bb42e65bSitojun 	char *l, *t, *c, *m;
165*bb42e65bSitojun 	static char result[BUFSIZ];
166*bb42e65bSitojun 
167*bb42e65bSitojun 	memset(result, 0, sizeof(result));
168*bb42e65bSitojun 
169*bb42e65bSitojun 	if (strlen(lname) + 1 > sizeof(buf)) {
170*bb42e65bSitojun fail:
171*bb42e65bSitojun 		return lname;
172*bb42e65bSitojun 	}
173*bb42e65bSitojun 
174*bb42e65bSitojun 	strlcpy(buf, lname, sizeof(buf));
175*bb42e65bSitojun 	m = strrchr(buf, '@');
176*bb42e65bSitojun 	if (m)
177*bb42e65bSitojun 		*m++ = '\0';
178*bb42e65bSitojun 	c = strrchr(buf, '.');
179*bb42e65bSitojun 	if (c)
180*bb42e65bSitojun 		*c++ = '\0';
181*bb42e65bSitojun 	t = strrchr(buf, '_');
182*bb42e65bSitojun 	if (t)
183*bb42e65bSitojun 		*t++ = '\0';
184*bb42e65bSitojun 	l = buf;
185*bb42e65bSitojun 	if (strlen(l) == 0)
186*bb42e65bSitojun 		goto fail;
187*bb42e65bSitojun 	if (c && !t)
188*bb42e65bSitojun 		goto fail;
189*bb42e65bSitojun 
190*bb42e65bSitojun 	if (m) {
191*bb42e65bSitojun 		if (t) {
192*bb42e65bSitojun 			if (c) {
193*bb42e65bSitojun 				snprintf(tmp, sizeof(tmp), "%s_%s.%s@%s",
194*bb42e65bSitojun 				    l, t, c, m);
195*bb42e65bSitojun 				strlcat(result, tmp, sizeof(result));
196*bb42e65bSitojun 				strlcat(result, ":", sizeof(result));
197*bb42e65bSitojun 			}
198*bb42e65bSitojun 			snprintf(tmp, sizeof(tmp), "%s_%s@%s", l, t, m);
199*bb42e65bSitojun 			strlcat(result, tmp, sizeof(result));
200*bb42e65bSitojun 			strlcat(result, ":", sizeof(result));
201*bb42e65bSitojun 		}
202*bb42e65bSitojun 		snprintf(tmp, sizeof(tmp), "%s@%s", l, m);
203*bb42e65bSitojun 		strlcat(result, tmp, sizeof(result));
204*bb42e65bSitojun 		strlcat(result, ":", sizeof(result));
205*bb42e65bSitojun 	}
206*bb42e65bSitojun 	if (t) {
207*bb42e65bSitojun 		if (c) {
208*bb42e65bSitojun 			snprintf(tmp, sizeof(tmp), "%s_%s.%s", l, t, c);
209*bb42e65bSitojun 			strlcat(result, tmp, sizeof(result));
210*bb42e65bSitojun 			strlcat(result, ":", sizeof(result));
211*bb42e65bSitojun 		}
212*bb42e65bSitojun 		strlcat(result, tmp, sizeof(result));
213*bb42e65bSitojun 		strlcat(result, ":", sizeof(result));
214*bb42e65bSitojun 	}
215*bb42e65bSitojun 	strlcat(result, l, sizeof(result));
216*bb42e65bSitojun 
217*bb42e65bSitojun 	return result;
218*bb42e65bSitojun }
219*bb42e65bSitojun 
220*bb42e65bSitojun static const char *
221*bb42e65bSitojun lookup_mofile(buf, len, dir, lpath, category, domainname)
222*bb42e65bSitojun 	char *buf;
223*bb42e65bSitojun 	size_t len;
224*bb42e65bSitojun 	const char *dir;
225*bb42e65bSitojun 	char *lpath;	/* list of locales to be tried */
226*bb42e65bSitojun 	const char *category;
227*bb42e65bSitojun 	const char *domainname;
228*bb42e65bSitojun {
229*bb42e65bSitojun 	struct stat st;
230*bb42e65bSitojun 	char *p, *q;
231*bb42e65bSitojun 
232*bb42e65bSitojun 	q = lpath;
233*bb42e65bSitojun 	while (1) {
234*bb42e65bSitojun 		p = strsep(&q, ":");
235*bb42e65bSitojun 		if (!p)
236*bb42e65bSitojun 			break;
237*bb42e65bSitojun 		if (!*p)
238*bb42e65bSitojun 			continue;
239*bb42e65bSitojun 
240*bb42e65bSitojun 		/* don't mess with default locales */
241*bb42e65bSitojun 		if (strcmp(p, "C") == 0 || strcmp(p, "POSIX") == 0)
242*bb42e65bSitojun 			return NULL;
243*bb42e65bSitojun 
244*bb42e65bSitojun 		/* validate pathname */
245*bb42e65bSitojun 		if (strchr(p, '/') || strchr(category, '/'))
246*bb42e65bSitojun 			continue;
247*bb42e65bSitojun #if 1	/*?*/
248*bb42e65bSitojun 		if (strchr(domainname, '/'))
249*bb42e65bSitojun 			continue;
250*bb42e65bSitojun #endif
251*bb42e65bSitojun 
252*bb42e65bSitojun 		snprintf(buf, len, "%s/%s/%s/%s.mo", dir, p,
253*bb42e65bSitojun 		    category, domainname);
254*bb42e65bSitojun 		if (stat(buf, &st) < 0)
255*bb42e65bSitojun 			continue;
256*bb42e65bSitojun 		if ((st.st_mode & S_IFMT) != S_IFREG)
257*bb42e65bSitojun 			continue;
258*bb42e65bSitojun 
259*bb42e65bSitojun 		if (mapit(buf) == 0)
260*bb42e65bSitojun 			return buf;
261*bb42e65bSitojun 	}
262*bb42e65bSitojun 
263*bb42e65bSitojun 	return NULL;
264*bb42e65bSitojun }
265*bb42e65bSitojun 
266*bb42e65bSitojun static u_int32_t
267*bb42e65bSitojun flip(v, magic)
268*bb42e65bSitojun 	u_int32_t v;
269*bb42e65bSitojun 	u_int32_t magic;
270*bb42e65bSitojun {
271*bb42e65bSitojun 
272*bb42e65bSitojun 	if (magic == MO_MAGIC)
273*bb42e65bSitojun 		return v;
274*bb42e65bSitojun 	else if (magic == MO_MAGIC_SWAPPED) {
275*bb42e65bSitojun 		v = ((v >> 24) & 0xff) | ((v >> 8) & 0xff00) |
276*bb42e65bSitojun 		    ((v << 8) & 0xff0000) | ((v << 24) & 0xff000000);
277*bb42e65bSitojun 		return v;
278*bb42e65bSitojun 	} else {
279*bb42e65bSitojun 		abort();
280*bb42e65bSitojun 		/*NOTREACHED*/
281*bb42e65bSitojun 	}
282*bb42e65bSitojun }
283*bb42e65bSitojun 
284*bb42e65bSitojun static int
285*bb42e65bSitojun validate(arg)
286*bb42e65bSitojun 	void *arg;
287*bb42e65bSitojun {
288*bb42e65bSitojun 	char *p;
289*bb42e65bSitojun 
290*bb42e65bSitojun 	p = (char *)arg;
291*bb42e65bSitojun 	if (p < (char *)mohandle.addr ||
292*bb42e65bSitojun 	    p > (char *)mohandle.addr + mohandle.len)
293*bb42e65bSitojun 		return 0;
294*bb42e65bSitojun 	else
295*bb42e65bSitojun 		return 1;
296*bb42e65bSitojun }
297*bb42e65bSitojun 
298*bb42e65bSitojun int
299*bb42e65bSitojun mapit(path)
300*bb42e65bSitojun 	const char *path;
301*bb42e65bSitojun {
302*bb42e65bSitojun 	int fd;
303*bb42e65bSitojun 	struct stat st;
304*bb42e65bSitojun 	char *base;
305*bb42e65bSitojun 	u_int32_t magic, revision;
306*bb42e65bSitojun 	struct moentry *otable, *ttable;
307*bb42e65bSitojun 	struct moentry_h *p;
308*bb42e65bSitojun 	struct mo *mo;
309*bb42e65bSitojun 	size_t l;
310*bb42e65bSitojun 	int i;
311*bb42e65bSitojun 	char *v;
312*bb42e65bSitojun 
313*bb42e65bSitojun 	if (mohandle.addr && strcmp(path, mohandle.path) == 0)
314*bb42e65bSitojun 		return 0;	/*already opened*/
315*bb42e65bSitojun 
316*bb42e65bSitojun 	unmapit();
317*bb42e65bSitojun 
318*bb42e65bSitojun #if 0
319*bb42e65bSitojun 	if (secure_path(path) != 0)
320*bb42e65bSitojun 		goto fail;
321*bb42e65bSitojun #endif
322*bb42e65bSitojun 	if (stat(path, &st) < 0)
323*bb42e65bSitojun 		goto fail;
324*bb42e65bSitojun 	if ((st.st_mode & S_IFMT) != S_IFREG || st.st_size > GETTEXT_MMAP_MAX)
325*bb42e65bSitojun 		goto fail;
326*bb42e65bSitojun 	fd = open(path, O_RDONLY);
327*bb42e65bSitojun 	if (fd < 0)
328*bb42e65bSitojun 		goto fail;
329*bb42e65bSitojun 	if (read(fd, &magic, sizeof(magic)) < 0 ||
330*bb42e65bSitojun 	    (magic != MO_MAGIC && magic != MO_MAGIC_SWAPPED)) {
331*bb42e65bSitojun 		close(fd);
332*bb42e65bSitojun 		goto fail;
333*bb42e65bSitojun 	}
334*bb42e65bSitojun 	if (read(fd, &revision, sizeof(revision)) < 0 ||
335*bb42e65bSitojun 	    flip(revision, magic) != MO_REVISION) {
336*bb42e65bSitojun 		close(fd);
337*bb42e65bSitojun 		goto fail;
338*bb42e65bSitojun 	}
339*bb42e65bSitojun 	mohandle.addr = mmap(NULL, st.st_size, PROT_READ, MAP_FILE | MAP_SHARED,
340*bb42e65bSitojun 	    fd, (off_t)0);
341*bb42e65bSitojun 	if (!mohandle.addr) {
342*bb42e65bSitojun 		close(fd);
343*bb42e65bSitojun 		goto fail;
344*bb42e65bSitojun 	}
345*bb42e65bSitojun 	close(fd);
346*bb42e65bSitojun 	mohandle.len = st.st_size;
347*bb42e65bSitojun 	strlcpy(mohandle.path, path, sizeof(mohandle.path));
348*bb42e65bSitojun 
349*bb42e65bSitojun 	base = mohandle.addr;
350*bb42e65bSitojun 	mo = (struct mo *)mohandle.addr;
351*bb42e65bSitojun 
352*bb42e65bSitojun 	/* flip endian.  do not flip magic number! */
353*bb42e65bSitojun 	mohandle.mo.mo_magic = mo->mo_magic;
354*bb42e65bSitojun 	mohandle.mo.mo_revision = flip(mo->mo_revision, magic);
355*bb42e65bSitojun 	mohandle.mo.mo_nstring = flip(mo->mo_nstring, magic);
356*bb42e65bSitojun 
357*bb42e65bSitojun 	/* validate otable/ttable */
358*bb42e65bSitojun 	otable = (struct moentry *)(base + flip(mo->mo_otable, magic));
359*bb42e65bSitojun 	ttable = (struct moentry *)(base + flip(mo->mo_ttable, magic));
360*bb42e65bSitojun 	if (!validate(otable) || !validate(&otable[mohandle.mo.mo_nstring])) {
361*bb42e65bSitojun 		unmapit();
362*bb42e65bSitojun 		goto fail;
363*bb42e65bSitojun 	}
364*bb42e65bSitojun 	if (!validate(ttable) || !validate(&ttable[mohandle.mo.mo_nstring])) {
365*bb42e65bSitojun 		unmapit();
366*bb42e65bSitojun 		goto fail;
367*bb42e65bSitojun 	}
368*bb42e65bSitojun 
369*bb42e65bSitojun 	/* allocate [ot]table, and convert to normal pointer representation. */
370*bb42e65bSitojun 	l = sizeof(struct moentry_h) * mohandle.mo.mo_nstring;
371*bb42e65bSitojun 	mohandle.mo.mo_otable = (struct moentry_h *)malloc(l);
372*bb42e65bSitojun 	if (!mohandle.mo.mo_otable) {
373*bb42e65bSitojun 		unmapit();
374*bb42e65bSitojun 		goto fail;
375*bb42e65bSitojun 	}
376*bb42e65bSitojun 	mohandle.mo.mo_ttable = (struct moentry_h *)malloc(l);
377*bb42e65bSitojun 	if (!mohandle.mo.mo_ttable) {
378*bb42e65bSitojun 		unmapit();
379*bb42e65bSitojun 		goto fail;
380*bb42e65bSitojun 	}
381*bb42e65bSitojun 	p = mohandle.mo.mo_otable;
382*bb42e65bSitojun 	for (i = 0; i < mohandle.mo.mo_nstring; i++) {
383*bb42e65bSitojun 		p[i].len = flip(otable[i].len, magic);
384*bb42e65bSitojun 		p[i].off = base + flip(otable[i].off, magic);
385*bb42e65bSitojun 
386*bb42e65bSitojun 		if (!validate(p[i].off) || !validate(p[i].off + p[i].len + 1)) {
387*bb42e65bSitojun 			unmapit();
388*bb42e65bSitojun 			goto fail;
389*bb42e65bSitojun 		}
390*bb42e65bSitojun 	}
391*bb42e65bSitojun 	p = mohandle.mo.mo_ttable;
392*bb42e65bSitojun 	for (i = 0; i < mohandle.mo.mo_nstring; i++) {
393*bb42e65bSitojun 		p[i].len = flip(ttable[i].len, magic);
394*bb42e65bSitojun 		p[i].off = base + flip(ttable[i].off, magic);
395*bb42e65bSitojun 
396*bb42e65bSitojun 		if (!validate(p[i].off) || !validate(p[i].off + p[i].len + 1)) {
397*bb42e65bSitojun 			unmapit();
398*bb42e65bSitojun 			goto fail;
399*bb42e65bSitojun 		}
400*bb42e65bSitojun 	}
401*bb42e65bSitojun 
402*bb42e65bSitojun 	/* grab MIME-header and charset field */
403*bb42e65bSitojun 	mohandle.mo.mo_header = lookup("");
404*bb42e65bSitojun 	if (mohandle.mo.mo_header)
405*bb42e65bSitojun 		v = strstr(mohandle.mo.mo_header, "charset=");
406*bb42e65bSitojun 	else
407*bb42e65bSitojun 		v = NULL;
408*bb42e65bSitojun 	if (v) {
409*bb42e65bSitojun 		mohandle.mo.mo_charset = strdup(v + 8);
410*bb42e65bSitojun 		v = strchr(mohandle.mo.mo_charset, '\n');
411*bb42e65bSitojun 		if (v)
412*bb42e65bSitojun 			*v = '\0';
413*bb42e65bSitojun 	}
414*bb42e65bSitojun 
415*bb42e65bSitojun 	/*
416*bb42e65bSitojun 	 * XXX check charset, reject it if we are unable to support the charset
417*bb42e65bSitojun 	 * with the current locale.
418*bb42e65bSitojun 	 * for example, if we are using euc-jp locale and we are looking at
419*bb42e65bSitojun 	 * *.mo file encoded by euc-kr (charset=euc-kr), we should reject
420*bb42e65bSitojun 	 * the *.mo file as we cannot support it.
421*bb42e65bSitojun 	 */
422*bb42e65bSitojun 
423*bb42e65bSitojun 	return 0;
424*bb42e65bSitojun 
425*bb42e65bSitojun fail:
426*bb42e65bSitojun 	return -1;
427*bb42e65bSitojun }
428*bb42e65bSitojun 
429*bb42e65bSitojun static int
430*bb42e65bSitojun unmapit()
431*bb42e65bSitojun {
432*bb42e65bSitojun 
433*bb42e65bSitojun 	/* unmap if there's already mapped region */
434*bb42e65bSitojun 	if (mohandle.addr)
435*bb42e65bSitojun 		munmap(mohandle.addr, mohandle.len);
436*bb42e65bSitojun 	mohandle.addr = NULL;
437*bb42e65bSitojun 	mohandle.path[0] = '\0';
438*bb42e65bSitojun 	if (mohandle.mo.mo_otable)
439*bb42e65bSitojun 		free(mohandle.mo.mo_otable);
440*bb42e65bSitojun 	if (mohandle.mo.mo_ttable)
441*bb42e65bSitojun 		free(mohandle.mo.mo_ttable);
442*bb42e65bSitojun 	if (mohandle.mo.mo_charset)
443*bb42e65bSitojun 		free(mohandle.mo.mo_charset);
444*bb42e65bSitojun 	memset(&mohandle.mo, 0, sizeof(mohandle.mo));
445*bb42e65bSitojun 	return 0;
446*bb42e65bSitojun }
447*bb42e65bSitojun 
448*bb42e65bSitojun static const char *
449*bb42e65bSitojun lookup_hash(msgid)
450*bb42e65bSitojun 	const char *msgid;
451*bb42e65bSitojun {
452*bb42e65bSitojun 
453*bb42e65bSitojun 	/*
454*bb42e65bSitojun 	 * XXX should try a hashed lookup here, but to do so, we need to
455*bb42e65bSitojun 	 * look inside the GPL'ed *.c and re-implement...
456*bb42e65bSitojun 	 */
457*bb42e65bSitojun 	return NULL;
458*bb42e65bSitojun }
459*bb42e65bSitojun 
460*bb42e65bSitojun static const char *
461*bb42e65bSitojun lookup_bsearch(msgid)
462*bb42e65bSitojun 	const char *msgid;
463*bb42e65bSitojun {
464*bb42e65bSitojun 	size_t l;
465*bb42e65bSitojun 	int top, bottom, middle, omiddle;
466*bb42e65bSitojun 	int n;
467*bb42e65bSitojun 
468*bb42e65bSitojun 	l = strlen(msgid);
469*bb42e65bSitojun 
470*bb42e65bSitojun 	top = 0;
471*bb42e65bSitojun 	bottom = mohandle.mo.mo_nstring;
472*bb42e65bSitojun 	omiddle = -1;
473*bb42e65bSitojun 	while (1) {
474*bb42e65bSitojun 		if (top > bottom)
475*bb42e65bSitojun 			return NULL;
476*bb42e65bSitojun 		middle = (top + bottom) / 2;
477*bb42e65bSitojun 		/* avoid possible infinite loop, when the data is not sorted */
478*bb42e65bSitojun 		if (omiddle == middle)
479*bb42e65bSitojun 			return NULL;
480*bb42e65bSitojun 		if (middle < 0 || middle >= mohandle.mo.mo_nstring)
481*bb42e65bSitojun 			return NULL;
482*bb42e65bSitojun 
483*bb42e65bSitojun 		n = strcmp(msgid, mohandle.mo.mo_otable[middle].off);
484*bb42e65bSitojun 		if (n == 0)
485*bb42e65bSitojun 			return (const char *)mohandle.mo.mo_ttable[middle].off;
486*bb42e65bSitojun 		else if (n < 0)
487*bb42e65bSitojun 			bottom = middle;
488*bb42e65bSitojun 		else
489*bb42e65bSitojun 			top = middle;
490*bb42e65bSitojun 		omiddle = middle;
491*bb42e65bSitojun 	}
492*bb42e65bSitojun 
493*bb42e65bSitojun 	return NULL;
494*bb42e65bSitojun }
495*bb42e65bSitojun 
496*bb42e65bSitojun static const char *
497*bb42e65bSitojun lookup(msgid)
498*bb42e65bSitojun 	const char *msgid;
499*bb42e65bSitojun {
500*bb42e65bSitojun 	const char *v;
501*bb42e65bSitojun 
502*bb42e65bSitojun 	v = lookup_hash(msgid);
503*bb42e65bSitojun 	if (v)
504*bb42e65bSitojun 		return v;
505*bb42e65bSitojun 
506*bb42e65bSitojun 	return lookup_bsearch(msgid);
507*bb42e65bSitojun }
508*bb42e65bSitojun 
509*bb42e65bSitojun char *
510*bb42e65bSitojun dcngettext(domainname, msgid1, msgid2, n, category)
511*bb42e65bSitojun 	const char *domainname;
512*bb42e65bSitojun 	const char *msgid1;
513*bb42e65bSitojun 	const char *msgid2;
514*bb42e65bSitojun 	unsigned long int n;
515*bb42e65bSitojun 	int category;
516*bb42e65bSitojun {
517*bb42e65bSitojun 	const char *msgid;
518*bb42e65bSitojun 	char path[PATH_MAX];
519*bb42e65bSitojun 	static char lpath[PATH_MAX];
520*bb42e65bSitojun 	static char olpath[PATH_MAX];
521*bb42e65bSitojun 	const char *locale;
522*bb42e65bSitojun 	const char *language;
523*bb42e65bSitojun 	const char *cname;
524*bb42e65bSitojun 	const char *v;
525*bb42e65bSitojun 
526*bb42e65bSitojun 	msgid = (n == 1) ? msgid1 : msgid2;
527*bb42e65bSitojun 
528*bb42e65bSitojun 	if (!domainname)
529*bb42e65bSitojun 		domainname = __domainname;
530*bb42e65bSitojun 	cname = lookup_category(category);
531*bb42e65bSitojun 	if (!domainname || !cname)
532*bb42e65bSitojun 		goto fail;
533*bb42e65bSitojun 
534*bb42e65bSitojun 	language = getenv("LANGUAGE");
535*bb42e65bSitojun 	locale = setlocale(LC_MESSAGES, NULL);	/*XXX*/
536*bb42e65bSitojun 	if (locale)
537*bb42e65bSitojun 		locale = split_locale(locale);
538*bb42e65bSitojun 	if (language && locale) {
539*bb42e65bSitojun 		if (strlen(language) + strlen(locale) + 2 > sizeof(lpath))
540*bb42e65bSitojun 			goto fail;
541*bb42e65bSitojun 		snprintf(lpath, sizeof(lpath), "%s:%s", language, locale);
542*bb42e65bSitojun 	} else if (language) {
543*bb42e65bSitojun 		if (strlen(language) + 1 > sizeof(lpath))
544*bb42e65bSitojun 			goto fail;
545*bb42e65bSitojun 		strlcpy(lpath, language, sizeof(lpath));
546*bb42e65bSitojun 	} else if (locale) {
547*bb42e65bSitojun 		if (strlen(locale) + 1 > sizeof(lpath))
548*bb42e65bSitojun 			goto fail;
549*bb42e65bSitojun 		strlcpy(lpath, locale, sizeof(lpath));
550*bb42e65bSitojun 	} else
551*bb42e65bSitojun 		goto fail;
552*bb42e65bSitojun 
553*bb42e65bSitojun 	/* don't bother looking it up if the values are the same */
554*bb42e65bSitojun 	if (strcmp(lpath, olpath) == 0)
555*bb42e65bSitojun 		goto found;
556*bb42e65bSitojun 
557*bb42e65bSitojun 	strlcpy(olpath, lpath, sizeof(olpath));
558*bb42e65bSitojun 
559*bb42e65bSitojun 	/* try to find appropriate file, from $LANGUAGE */
560*bb42e65bSitojun 	if (lookup_mofile(path, sizeof(path), __domainpath, lpath, cname,
561*bb42e65bSitojun 	    domainname) != NULL)
562*bb42e65bSitojun 		goto found;
563*bb42e65bSitojun 
564*bb42e65bSitojun 	goto fail;
565*bb42e65bSitojun 
566*bb42e65bSitojun found:
567*bb42e65bSitojun 	v = lookup(msgid);
568*bb42e65bSitojun 	if (v) {
569*bb42e65bSitojun 		/*
570*bb42e65bSitojun 		 * XXX call iconv() here, if translated text is encoded
571*bb42e65bSitojun 		 * differently from currently-selected encoding (locale).
572*bb42e65bSitojun 		 * look at Content-type header in *.mo file, in string obtained
573*bb42e65bSitojun 		 * by gettext("").
574*bb42e65bSitojun 		 */
575*bb42e65bSitojun 
576*bb42e65bSitojun 		/*
577*bb42e65bSitojun 		 * Given the amount of printf-format security issues, it may
578*bb42e65bSitojun 		 * be a good idea to validate if the original msgid and the
579*bb42e65bSitojun 		 * translated message format string carry the same printf-like
580*bb42e65bSitojun 		 * format identifiers.
581*bb42e65bSitojun 		 */
582*bb42e65bSitojun 
583*bb42e65bSitojun 		msgid = v;
584*bb42e65bSitojun 	}
585*bb42e65bSitojun 
586*bb42e65bSitojun fail:
587*bb42e65bSitojun 	/* LINTED const cast */
588*bb42e65bSitojun 	return (char *)msgid;
589*bb42e65bSitojun }
590