msgcat.c revision 106053
1193326Sed/***********************************************************
2193326SedCopyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
3193326Sed
4193326Sed                        All Rights Reserved
5193326Sed
6193326SedPermission to use, copy, modify, and distribute this software and its
7193326Seddocumentation for any purpose and without fee is hereby granted,
8193326Sedprovided that the above copyright notice appear in all copies and that
9239462Sdimboth that copyright notice and this permission notice appear in
10239462Sdimsupporting documentation, and that Alfalfa's name not be used in
11239462Sdimadvertising or publicity pertaining to distribution of the software
12239462Sdimwithout specific, written prior permission.
13239462Sdim
14193326SedALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15193326SedALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16193326SedALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17193326SedANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18193326SedWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19249423SdimARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20193326SedSOFTWARE.
21193326Sed
22263508SdimIf you make any modifications, bugfixes or other changes to this software
23193326Sedwe'd appreciate it if you could send a copy to us so we can keep things
24205219Srdivackyup-to-date.  Many thanks.
25193326Sed				Kee Hinckley
26218893Sdim				Alfalfa Software, Inc.
27198092Srdivacky				267 Allston St., #3
28193326Sed				Cambridge, MA 02139  USA
29193326Sed				nazgul@alfalfa.com
30193326Sed
31193326Sed******************************************************************/
32193326Sed
33193326Sed#include <sys/cdefs.h>
34193326Sed__FBSDID("$FreeBSD: head/lib/libc/nls/msgcat.c 106053 2002-10-27 17:44:33Z wollman $");
35193326Sed
36193326Sed/*
37193326Sed * We need a better way of handling errors than printing text.  I need
38193326Sed * to add an error handling routine.
39193326Sed */
40193326Sed
41239462Sdim#include "namespace.h"
42193326Sed#include <sys/types.h>
43198092Srdivacky#include <sys/stat.h>
44198092Srdivacky
45239462Sdim#include <errno.h>
46239462Sdim#include <fcntl.h>
47193326Sed#include <limits.h>
48193326Sed#include <locale.h>
49198092Srdivacky#include <nl_types.h>
50193326Sed#include <stdio.h>
51226633Sdim#include <stdlib.h>
52193326Sed#include <string.h>
53193326Sed#include <unistd.h>
54193326Sed#include "un-namespace.h"
55218893Sdim
56193326Sed#include "msgcat.h"
57243830Sdim#include "../locale/setlocale.h"        /* for ENCODING_LEN */
58193326Sed
59226633Sdim#define _DEFAULT_NLS_PATH "/usr/share/nls/%L/%N.cat:/usr/share/nls/%N/%L:/usr/local/share/nls/%L/%N.cat:/usr/local/share/nls/%N/%L"
60193326Sed
61193326Sed#define	TRUE	1
62193326Sed#define	FALSE	0
63234353Sdim
64234353Sdim#define	NLERR		((nl_catd) -1)
65234353Sdim#define NLRETERR(errc)  { errno = errc; return (NLERR); }
66234353Sdim
67212904Sdimstatic nl_catd  loadCat();
68212904Sdimstatic int      loadSet();
69234353Sdimstatic void     __nls_free_resources();
70234353Sdim
71234353Sdimnl_catd
72239462Sdimcatopen(name, type)
73234353Sdim	__const char    *name;
74243830Sdim	int             type;
75243830Sdim{
76193326Sed	int             spcleft, saverr;
77193326Sed	char            path[PATH_MAX];
78198092Srdivacky	char            *nlspath, *lang, *base, *cptr, *pathP, *tmpptr;
79243830Sdim	char            *cptr1, *plang, *pter, *pcode;
80243830Sdim	struct stat     sbuf;
81193326Sed
82198092Srdivacky	if (name == NULL || *name == '\0')
83218893Sdim		NLRETERR(EINVAL);
84193326Sed
85193326Sed	/* is it absolute path ? if yes, load immediately */
86193326Sed	if (strchr(name, '/') != NULL)
87198092Srdivacky		return (loadCat(name));
88239462Sdim
89239462Sdim	if (type == NL_CAT_LOCALE)
90193326Sed		lang = setlocale(LC_MESSAGES, NULL);
91193326Sed	else
92193326Sed		lang = getenv("LANG");
93198398Srdivacky
94193326Sed	if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
95198092Srdivacky	    (lang[0] == '.' &&
96239462Sdim	     (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
97239462Sdim	    strchr(lang, '/') != NULL)
98193326Sed		lang = "C";
99198398Srdivacky
100193326Sed	if ((plang = cptr1 = strdup(lang)) == NULL)
101193326Sed		return (NLERR);
102193326Sed	if ((cptr = strchr(cptr1, '@')) != NULL)
103198092Srdivacky		*cptr = '\0';
104193326Sed	pter = pcode = "";
105193326Sed	if ((cptr = strchr(cptr1, '_')) != NULL) {
106210299Sed		*cptr++ = '\0';
107210299Sed		pter = cptr1 = cptr;
108193326Sed	}
109198092Srdivacky	if ((cptr = strchr(cptr1, '.')) != NULL) {
110239462Sdim		*cptr++ = '\0';
111193326Sed		pcode = cptr;
112193326Sed	}
113193326Sed
114193326Sed	if ((nlspath = getenv("NLSPATH")) == NULL
115193326Sed#ifndef __NETBSD_SYSCALLS
116198092Srdivacky	    || issetugid()
117193326Sed#endif
118193326Sed	   )
119210299Sed		nlspath = _DEFAULT_NLS_PATH;
120210299Sed
121198398Srdivacky	if ((base = cptr = strdup(nlspath)) == NULL) {
122193326Sed		saverr = errno;
123198092Srdivacky		free(plang);
124239462Sdim		errno = saverr;
125226633Sdim		return (NLERR);
126226633Sdim	}
127198398Srdivacky
128198398Srdivacky	while ((nlspath = strsep(&cptr, ":")) != NULL) {
129239462Sdim		pathP = path;
130193326Sed		if (*nlspath) {
131193326Sed			for (; *nlspath; ++nlspath) {
132193326Sed				if (*nlspath == '%') {
133193326Sed					switch (*(nlspath + 1)) {
134193326Sed					case 'l':
135198092Srdivacky						tmpptr = plang;
136193326Sed						break;
137243830Sdim					case 't':
138193326Sed						tmpptr = pter;
139243830Sdim						break;
140243830Sdim					case 'c':
141193326Sed						tmpptr = pcode;
142243830Sdim						break;
143193326Sed					case 'L':
144243830Sdim						tmpptr = lang;
145243830Sdim						break;
146243830Sdim					case 'N':
147243830Sdim						tmpptr = (char *)name;
148243830Sdim						break;
149243830Sdim					case '%':
150198092Srdivacky						++nlspath;
151263508Sdim						/* fallthrough */
152193326Sed					default:
153193326Sed						if (pathP - path >=
154193326Sed						    sizeof(path) - 1)
155198092Srdivacky							goto too_long;
156212904Sdim						*(pathP++) = *nlspath;
157212904Sdim						continue;
158212904Sdim					}
159212904Sdim					++nlspath;
160212904Sdim			put_tmpptr:
161212904Sdim					spcleft = sizeof(path) -
162212904Sdim						  (pathP - path) - 1;
163212904Sdim					if (strlcpy(pathP, tmpptr, spcleft) >=
164212904Sdim					    spcleft) {
165212904Sdim				too_long:
166212904Sdim						free(plang);
167212904Sdim						free(base);
168212904Sdim						NLRETERR(ENAMETOOLONG);
169212904Sdim					}
170212904Sdim					pathP += strlen(tmpptr);
171239462Sdim				} else {
172239462Sdim					if (pathP - path >= sizeof(path) - 1)
173193326Sed						goto too_long;
174193326Sed					*(pathP++) = *nlspath;
175198092Srdivacky				}
176239462Sdim			}
177239462Sdim			*pathP = '\0';
178239462Sdim			if (stat(path, &sbuf) == 0) {
179193326Sed				free(plang);
180198092Srdivacky				free(base);
181193326Sed				return (loadCat(path));
182193326Sed			}
183193326Sed		} else {
184193326Sed			tmpptr = (char *)name;
185193326Sed			--nlspath;
186193326Sed			goto put_tmpptr;
187263508Sdim		}
188263508Sdim	}
189263508Sdim	free(plang);
190193326Sed	free(base);
191198092Srdivacky	NLRETERR(ENOENT);
192193326Sed}
193198092Srdivacky
194193326Sed/*
195193326Sed * We've got an odd situation here.  The odds are real good that the
196193326Sed * number we are looking for is almost the same as the index.  We could
197193326Sed * use the index, check the difference and do something intelligent, but
198193326Sed * I haven't quite figured out what's intelligent.
199198092Srdivacky *
200193326Sed * Here's a start.
201193326Sed *	Take an id N.  If there are > N items in the list, then N cannot
202193326Sed *	be more than N items from the start, since otherwise there would
203193326Sed *	have to be duplicate items.  So we can safely set the top to N+1
204193326Sed *	(after taking into account that ids start at 1, and arrays at 0)
205193326Sed *
206193326Sed *	Let's say we are at position P, and we are looking for N, but have
207193326Sed *	V.  If N > V, then the furthest away that N could be is
208193326Sed *	P + (N-V).  So we can safely set hi to P+(N-V)+1.  For example:
209193326Sed *		We are looking for 10, but have 8
210193326Sed *		8	?	?	?	?
211193326Sed *			>=9	>=10	>=11
212193326Sed *
213193326Sed */
214193326Sed
215193326Sed#define LOOKUP(PARENT, CHILD, ID, NUM, SET) {                    \
216193326Sed	lo = 0;                                                  \
217198092Srdivacky	if (ID - 1 < PARENT->NUM) {                              \
218226633Sdim		cur = ID - 1;                                    \
219226633Sdim		hi = ID;                                         \
220226633Sdim	} else {                                                 \
221226633Sdim		hi = PARENT->NUM;                                \
222226633Sdim		cur = (hi - lo) / 2;                             \
223226633Sdim	}                                                        \
224226633Sdim	while (TRUE) {                                           \
225226633Sdim		CHILD = PARENT->SET + cur;                       \
226226633Sdim		if (CHILD->ID == ID)                             \
227226633Sdim			break;                                   \
228226633Sdim		if (CHILD->ID < ID) {                            \
229226633Sdim			lo = cur + 1;                            \
230226633Sdim			if (hi > cur + (ID - CHILD->ID) + 1)     \
231193326Sed				hi = cur + (ID - CHILD->ID) + 1; \
232193326Sed			dir = 1;                                 \
233193326Sed		} else {                                         \
234193326Sed			hi = cur;                                \
235193326Sed			dir = -1;                                \
236193326Sed		}                                                \
237193326Sed		if (lo >= hi)                                    \
238193326Sed			return (NULL);                           \
239193326Sed		if (hi - lo == 1)                                \
240198092Srdivacky			cur += dir;                              \
241263508Sdim		else                                             \
242193326Sed			cur += ((hi - lo) / 2) * dir;            \
243198092Srdivacky	}                                                        \
244193326Sed}
245193326Sed
246193326Sedstatic MCSetT *
247193326SedMCGetSet(cat, setId)
248193326Sed	MCCatT  *cat;
249193326Sed	int     setId;
250193326Sed{
251193326Sed	MCSetT  *set;
252193326Sed	long    lo, hi, cur, dir;
253193326Sed
254193326Sed	if (cat == NULL || setId <= 0)
255193326Sed		return (NULL);
256193326Sed	LOOKUP(cat, set, setId, numSets, sets);
257193326Sed	if (set->invalid && loadSet(cat, set) <= 0)
258193326Sed		return (NULL);
259193326Sed	return (set);
260193326Sed}
261263508Sdim
262263508Sdimstatic MCMsgT *
263263508SdimMCGetMsg(set, msgId)
264263508Sdim	MCSetT  *set;
265263508Sdim	int     msgId;
266193326Sed{
267198092Srdivacky	MCMsgT  *msg;
268263508Sdim	long    lo, hi, cur, dir;
269212904Sdim
270212904Sdim	if (set == NULL || set->invalid || msgId <= 0)
271212904Sdim		return (NULL);
272234353Sdim	LOOKUP(set, msg, msgId, numMsgs, u.msgs);
273212904Sdim	return (msg);
274234353Sdim}
275234353Sdim
276234353Sdimchar *
277234353Sdimcatgets(catd, setId, msgId, dflt)
278234353Sdim	nl_catd         catd;
279234353Sdim	int             setId;
280234353Sdim	int             msgId;
281234353Sdim	__const char    *dflt;
282234353Sdim{
283234353Sdim	MCMsgT          *msg;
284234353Sdim	MCCatT          *cat = (MCCatT *)catd;
285234353Sdim	__const char    *cptr;
286234353Sdim
287234353Sdim	if (catd == NULL || catd == NLERR)
288234353Sdim		return ((char *)dflt);
289234353Sdim	msg = MCGetMsg(MCGetSet(cat, setId), msgId);
290234353Sdim	if (msg != NULL)
291234353Sdim		cptr = msg->msg.str;
292234353Sdim	else
293234353Sdim		cptr = dflt;
294234353Sdim	return ((char *)cptr);
295234353Sdim}
296234353Sdim
297234353Sdimint
298234353Sdimcatclose(catd)
299234353Sdim	nl_catd catd;
300263508Sdim{
301234353Sdim	MCCatT  *cat = (MCCatT *)catd;
302234353Sdim
303263508Sdim	if (catd == NULL || catd == NLERR) {
304234353Sdim		errno = EBADF;
305234353Sdim		return (-1);
306234353Sdim	}
307234353Sdim#if 0
308234353Sdim	if (cat->loadType != MCLoadAll)
309234353Sdim#endif
310234353Sdim		(void)fclose(cat->fp);
311234353Sdim	__nls_free_resources(cat, cat->numSets);
312193326Sed	free(cat);
313263508Sdim	return (0);
314263508Sdim}
315263508Sdim
316193326Sed/*
317193326Sed * Internal routines
318193326Sed */
319193326Sed
320193326Sed/* Note that only malloc failures are allowed to return an error */
321193326Sedstatic char     *_errowner = "Message Catalog System";
322234353Sdim
323234353Sdim#define CORRUPT() {                                            \
324193326Sed	(void)fclose(cat->fp);                                 \
325193326Sed	(void)fprintf(stderr, "%s: corrupt file.", _errowner); \
326193326Sed	free(cat);                                             \
327263508Sdim	NLRETERR(EFTYPE);                                      \
328263508Sdim}
329263508Sdim
330263508Sdim#define NOSPACE() {                                              \
331221345Sdim	saverr = errno;                                          \
332221345Sdim	(void)fclose(cat->fp);                                   \
333221345Sdim	(void)fprintf(stderr, "%s: no more memory.", _errowner); \
334221345Sdim	free(cat);                                               \
335221345Sdim	errno = saverr;                                          \
336221345Sdim	return (NLERR);                                          \
337221345Sdim}
338221345Sdim
339221345Sdimstatic void
340221345Sdim__nls_free_resources(cat, i)
341221345Sdim	MCCatT  *cat;
342221345Sdim	int     i;
343221345Sdim{
344221345Sdim	MCSetT  *set;
345221345Sdim	int     j;
346221345Sdim
347218893Sdim	for (j = 0; j < i; j++) {
348218893Sdim		set = cat->sets + j;
349218893Sdim		if (!set->invalid) {
350218893Sdim			free(set->data.str);
351218893Sdim			free(set->u.msgs);
352218893Sdim		}
353218893Sdim	}
354218893Sdim	free(cat->sets);
355218893Sdim}
356218893Sdim
357218893Sdimstatic nl_catd
358218893SdimloadCat(catpath)
359243830Sdim	__const char    *catpath;
360243830Sdim{
361218893Sdim	MCHeaderT       header;
362218893Sdim	MCCatT          *cat;
363218893Sdim	MCSetT          *set;
364218893Sdim	long            i;
365218893Sdim	off_t           nextSet;
366218893Sdim	int             saverr;
367218893Sdim
368218893Sdim	if ((cat = (MCCatT *)malloc(sizeof(MCCatT))) == NULL)
369218893Sdim		return (NLERR);
370218893Sdim	cat->loadType = MCLoadBySet;
371218893Sdim
372226633Sdim	if ((cat->fp = fopen(catpath, "r")) == NULL) {
373226633Sdim		saverr = errno;
374218893Sdim		free(cat);
375218893Sdim		errno = saverr;
376263508Sdim		return (NLERR);
377193326Sed	}
378193326Sed	(void)_fcntl(fileno(cat->fp), F_SETFD, FD_CLOEXEC);
379193326Sed
380198092Srdivacky	if (fread(&header, sizeof(header), 1, cat->fp) != 1 ||
381263508Sdim	    strncmp(header.magic, MCMagic, MCMagicLen) != 0)
382263508Sdim		CORRUPT();
383263508Sdim
384263508Sdim	if (header.majorVer != MCMajorVer) {
385263508Sdim		(void)fclose(cat->fp);
386226633Sdim		free(cat);
387218893Sdim		(void)fprintf(stderr, "%s: %s is version %ld, we need %ld.\n",
388218893Sdim		    _errowner, catpath, header.majorVer, MCMajorVer);
389218893Sdim		NLRETERR(EFTYPE);
390218893Sdim	}
391218893Sdim	if (header.numSets <= 0) {
392218893Sdim		(void)fclose(cat->fp);
393218893Sdim		free(cat);
394218893Sdim		(void)fprintf(stderr, "%s: %s has %ld sets!\n",
395218893Sdim		    _errowner, catpath, header.numSets);
396218893Sdim		NLRETERR(EFTYPE);
397218893Sdim	}
398251662Sdim
399198092Srdivacky	cat->numSets = header.numSets;
400193326Sed	if ((cat->sets = (MCSetT *)malloc(sizeof(MCSetT) * header.numSets)) ==
401193326Sed	    NULL)
402193326Sed		NOSPACE();
403193326Sed
404193326Sed	nextSet = header.firstSet;
405193326Sed	for (i = 0; i < cat->numSets; ++i) {
406193326Sed		if (fseeko(cat->fp, nextSet, SEEK_SET) == -1) {
407193326Sed			__nls_free_resources(cat, i);
408193326Sed			CORRUPT();
409193326Sed		}
410193326Sed
411193326Sed		/* read in the set header */
412193326Sed		set = cat->sets + i;
413193326Sed		if (fread(set, sizeof(*set), 1, cat->fp) != 1) {
414239462Sdim			__nls_free_resources(cat, i);
415239462Sdim			CORRUPT();
416239462Sdim		}
417239462Sdim
418239462Sdim		/* if it's invalid, skip over it (and backup 'i') */
419193326Sed		if (set->invalid) {
420193326Sed			--i;
421193326Sed			nextSet = set->nextSet;
422193326Sed			continue;
423193326Sed		}
424198092Srdivacky#if 0
425193326Sed		if (cat->loadType == MCLoadAll) {
426193326Sed			int     res;
427193326Sed
428239462Sdim			if ((res = loadSet(cat, set)) <= 0) {
429239462Sdim				saverr = errno;
430193326Sed				__nls_free_resources(cat, i);
431193326Sed				errno = saverr;
432198092Srdivacky				if (res < 0)
433193326Sed					NOSPACE();
434193326Sed				CORRUPT();
435193326Sed			}
436193326Sed		} else
437193326Sed#endif
438218893Sdim			set->invalid = TRUE;
439218893Sdim		nextSet = set->nextSet;
440218893Sdim	}
441218893Sdim#if 0
442218893Sdim	if (cat->loadType == MCLoadAll) {
443193326Sed		(void)fclose(cat->fp);
444193326Sed		cat->fp = NULL;
445193326Sed	}
446198092Srdivacky#endif
447239462Sdim	return ((nl_catd) cat);
448239462Sdim}
449226633Sdim
450193326Sedstatic int
451205219SrdivackyloadSet(cat, set)
452198092Srdivacky	MCCatT  *cat;
453193326Sed	MCSetT  *set;
454193326Sed{
455198092Srdivacky	MCMsgT  *msg;
456193326Sed	int     i;
457193326Sed	int     saverr;
458205219Srdivacky
459193326Sed	/* Get the data */
460193326Sed	if (fseeko(cat->fp, set->data.off, SEEK_SET) == -1)
461193326Sed		return (0);
462193326Sed	if ((set->data.str = malloc(set->dataLen)) == NULL)
463193326Sed		return (-1);
464193326Sed	if (fread(set->data.str, set->dataLen, 1, cat->fp) != 1) {
465193326Sed		saverr = errno;
466193326Sed		free(set->data.str);
467193326Sed		errno = saverr;
468193326Sed		return (0);
469193326Sed	}
470193326Sed
471193326Sed	/* Get the messages */
472193326Sed	if (fseeko(cat->fp, set->u.firstMsg, SEEK_SET) == -1) {
473193326Sed		saverr = errno;
474193326Sed		free(set->data.str);
475193326Sed		errno = saverr;
476193326Sed		return (0);
477198092Srdivacky	}
478226633Sdim	if ((set->u.msgs = (MCMsgT *)malloc(sizeof(MCMsgT) * set->numMsgs)) ==
479212904Sdim	    NULL) {
480212904Sdim		saverr = errno;
481226633Sdim		free(set->data.str);
482212904Sdim		errno = saverr;
483212904Sdim		return (-1);
484212904Sdim	}
485212904Sdim
486212904Sdim	for (i = 0; i < set->numMsgs; ++i) {
487193326Sed		msg = set->u.msgs + i;
488212904Sdim		if (fread(msg, sizeof(*msg), 1, cat->fp) != 1) {
489212904Sdim			saverr = errno;
490212904Sdim			free(set->u.msgs);
491226633Sdim			free(set->data.str);
492193326Sed			errno = saverr;
493224145Sdim			return (0);
494198092Srdivacky		}
495193326Sed		if (msg->invalid) {
496212904Sdim			--i;
497198092Srdivacky			continue;
498212904Sdim		}
499212904Sdim		msg->msg.str = (char *)(set->data.str + msg->msg.off);
500212904Sdim	}
501212904Sdim	set->invalid = FALSE;
502193326Sed	return (1);
503212904Sdim}
504212904Sdim