xref: /dragonfly/lib/libc/nls/msgcat.c (revision 6e285212)
1 /***********************************************************
2 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
3 
4                         All Rights Reserved
5 
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted,
8 provided that the above copyright notice appear in all copies and that
9 both that copyright notice and this permission notice appear in
10 supporting documentation, and that Alfalfa's name not be used in
11 advertising or publicity pertaining to distribution of the software
12 without specific, written prior permission.
13 
14 ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16 ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 SOFTWARE.
21 
22 If you make any modifications, bugfixes or other changes to this software
23 we'd appreciate it if you could send a copy to us so we can keep things
24 up-to-date.  Many thanks.
25 				Kee Hinckley
26 				Alfalfa Software, Inc.
27 				267 Allston St., #3
28 				Cambridge, MA 02139  USA
29 				nazgul@alfalfa.com
30 
31 ******************************************************************/
32 /*
33  * $FreeBSD: src/lib/libc/nls/msgcat.c,v 1.21.2.6 2002/08/12 11:23:54 ache Exp $
34  * $DragonFly: src/lib/libc/nls/Attic/msgcat.c,v 1.2 2003/06/17 04:26:44 dillon Exp $
35  */
36 
37 /*
38  * We need a better way of handling errors than printing text.  I need
39  * to add an error handling routine.
40  */
41 
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/syslimits.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <locale.h>
48 #include <nl_types.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53 
54 #include "msgcat.h"
55 #include "../locale/setlocale.h"        /* for ENCODING_LEN */
56 
57 #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"
58 
59 #define	TRUE	1
60 #define	FALSE	0
61 
62 #define	NLERR		((nl_catd) -1)
63 #define NLRETERR(errc)  { errno = errc; return (NLERR); }
64 
65 static nl_catd  loadCat();
66 static int      loadSet();
67 static void     __nls_free_resources();
68 
69 nl_catd
70 catopen(name, type)
71 	__const char    *name;
72 	int             type;
73 {
74 	int             spcleft, saverr;
75 	char            path[PATH_MAX];
76 	char            *nlspath, *lang, *base, *cptr, *pathP, *tmpptr;
77 	char            *cptr1, *plang, *pter, *pcode;
78 	struct stat     sbuf;
79 
80 	if (name == NULL || *name == '\0')
81 		NLRETERR(EINVAL);
82 
83 	/* is it absolute path ? if yes, load immediately */
84 	if (strchr(name, '/') != NULL)
85 		return (loadCat(name));
86 
87 	if (type == NL_CAT_LOCALE)
88 		lang = setlocale(LC_MESSAGES, NULL);
89 	else
90 		lang = getenv("LANG");
91 
92 	if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
93 	    (lang[0] == '.' &&
94 	     (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
95 	    strchr(lang, '/') != NULL)
96 		lang = "C";
97 
98 	if ((plang = cptr1 = strdup(lang)) == NULL) {
99 		errno = ENOMEM;
100 		return (NLERR);
101 	}
102 	if ((cptr = strchr(cptr1, '@')) != NULL)
103 		*cptr = '\0';
104 	pter = pcode = "";
105 	if ((cptr = strchr(cptr1, '_')) != NULL) {
106 		*cptr++ = '\0';
107 		pter = cptr1 = cptr;
108 	}
109 	if ((cptr = strchr(cptr1, '.')) != NULL) {
110 		*cptr++ = '\0';
111 		pcode = cptr;
112 	}
113 
114 	if ((nlspath = getenv("NLSPATH")) == NULL
115 #ifndef __NETBSD_SYSCALLS
116 	    || issetugid()
117 #endif
118 	   )
119 		nlspath = _DEFAULT_NLS_PATH;
120 
121 	if ((base = cptr = strdup(nlspath)) == NULL) {
122 		free(plang);
123 		errno = ENOMEM;
124 		return (NLERR);
125 	}
126 
127 	while ((nlspath = strsep(&cptr, ":")) != NULL) {
128 		pathP = path;
129 		if (*nlspath) {
130 			for (; *nlspath; ++nlspath) {
131 				if (*nlspath == '%') {
132 					switch (*(nlspath + 1)) {
133 					case 'l':
134 						tmpptr = plang;
135 						break;
136 					case 't':
137 						tmpptr = pter;
138 						break;
139 					case 'c':
140 						tmpptr = pcode;
141 						break;
142 					case 'L':
143 						tmpptr = lang;
144 						break;
145 					case 'N':
146 						tmpptr = (char *)name;
147 						break;
148 					case '%':
149 						++nlspath;
150 						/* fallthrough */
151 					default:
152 						if (pathP - path >=
153 						    sizeof(path) - 1)
154 							goto too_long;
155 						*(pathP++) = *nlspath;
156 						continue;
157 					}
158 					++nlspath;
159 			put_tmpptr:
160 					spcleft = sizeof(path) -
161 						  (pathP - path) - 1;
162 					if (strlcpy(pathP, tmpptr, spcleft) >=
163 					    spcleft) {
164 				too_long:
165 						free(plang);
166 						free(base);
167 						NLRETERR(ENAMETOOLONG);
168 					}
169 					pathP += strlen(tmpptr);
170 				} else {
171 					if (pathP - path >= sizeof(path) - 1)
172 						goto too_long;
173 					*(pathP++) = *nlspath;
174 				}
175 			}
176 			*pathP = '\0';
177 			if (stat(path, &sbuf) == 0) {
178 				free(plang);
179 				free(base);
180 				return (loadCat(path));
181 			}
182 		} else {
183 			tmpptr = (char *)name;
184 			--nlspath;
185 			goto put_tmpptr;
186 		}
187 	}
188 	free(plang);
189 	free(base);
190 	NLRETERR(ENOENT);
191 }
192 
193 /*
194  * We've got an odd situation here.  The odds are real good that the
195  * number we are looking for is almost the same as the index.  We could
196  * use the index, check the difference and do something intelligent, but
197  * I haven't quite figured out what's intelligent.
198  *
199  * Here's a start.
200  *	Take an id N.  If there are > N items in the list, then N cannot
201  *	be more than N items from the start, since otherwise there would
202  *	have to be duplicate items.  So we can safely set the top to N+1
203  *	(after taking into account that ids start at 1, and arrays at 0)
204  *
205  *	Let's say we are at position P, and we are looking for N, but have
206  *	V.  If N > V, then the furthest away that N could be is
207  *	P + (N-V).  So we can safely set hi to P+(N-V)+1.  For example:
208  *		We are looking for 10, but have 8
209  *		8	?	?	?	?
210  *			>=9	>=10	>=11
211  *
212  */
213 
214 #define LOOKUP(PARENT, CHILD, ID, NUM, SET) {                    \
215 	lo = 0;                                                  \
216 	if (ID - 1 < PARENT->NUM) {                              \
217 		cur = ID - 1;                                    \
218 		hi = ID;                                         \
219 	} else {                                                 \
220 		hi = PARENT->NUM;                                \
221 		cur = (hi - lo) / 2;                             \
222 	}                                                        \
223 	while (TRUE) {                                           \
224 		CHILD = PARENT->SET + cur;                       \
225 		if (CHILD->ID == ID)                             \
226 			break;                                   \
227 		if (CHILD->ID < ID) {                            \
228 			lo = cur + 1;                            \
229 			if (hi > cur + (ID - CHILD->ID) + 1)     \
230 				hi = cur + (ID - CHILD->ID) + 1; \
231 			dir = 1;                                 \
232 		} else {                                         \
233 			hi = cur;                                \
234 			dir = -1;                                \
235 		}                                                \
236 		if (lo >= hi)                                    \
237 			return (NULL);                           \
238 		if (hi - lo == 1)                                \
239 			cur += dir;                              \
240 		else                                             \
241 			cur += ((hi - lo) / 2) * dir;            \
242 	}                                                        \
243 }
244 
245 static MCSetT *
246 MCGetSet(cat, setId)
247 	MCCatT  *cat;
248 	int     setId;
249 {
250 	MCSetT  *set;
251 	long    lo, hi, cur, dir;
252 
253 	if (cat == NULL || setId <= 0)
254 		return (NULL);
255 	LOOKUP(cat, set, setId, numSets, sets);
256 	if (set->invalid && loadSet(cat, set) <= 0)
257 		return (NULL);
258 	return (set);
259 }
260 
261 static MCMsgT *
262 MCGetMsg(set, msgId)
263 	MCSetT  *set;
264 	int     msgId;
265 {
266 	MCMsgT  *msg;
267 	long    lo, hi, cur, dir;
268 
269 	if (set == NULL || set->invalid || msgId <= 0)
270 		return (NULL);
271 	LOOKUP(set, msg, msgId, numMsgs, u.msgs);
272 	return (msg);
273 }
274 
275 char *
276 catgets(catd, setId, msgId, dflt)
277 	nl_catd         catd;
278 	int             setId;
279 	int             msgId;
280 	__const char    *dflt;
281 {
282 	MCMsgT          *msg;
283 	MCCatT          *cat = (MCCatT *)catd;
284 	__const char    *cptr;
285 
286 	if (catd == NULL || catd == NLERR)
287 		return ((char *)dflt);
288 	msg = MCGetMsg(MCGetSet(cat, setId), msgId);
289 	if (msg != NULL)
290 		cptr = msg->msg.str;
291 	else
292 		cptr = dflt;
293 	return ((char *)cptr);
294 }
295 
296 int
297 catclose(catd)
298 	nl_catd catd;
299 {
300 	MCCatT  *cat = (MCCatT *)catd;
301 
302 	if (catd == NULL || catd == NLERR) {
303 		errno = EBADF;
304 		return (-1);
305 	}
306 #if 0
307 	if (cat->loadType != MCLoadAll)
308 #endif
309 		(void)fclose(cat->fp);
310 	__nls_free_resources(cat, cat->numSets);
311 	free(cat);
312 	return (0);
313 }
314 
315 /*
316  * Internal routines
317  */
318 
319 /* Note that only malloc failures are allowed to return an error */
320 static char     *_errowner = "Message Catalog System";
321 
322 #define CORRUPT() {                                            \
323 	(void)fclose(cat->fp);                                 \
324 	(void)fprintf(stderr, "%s: corrupt file.", _errowner); \
325 	free(cat);                                             \
326 	NLRETERR(EFTYPE);                                      \
327 }
328 
329 #define NOSPACE() {                                              \
330 	(void)fclose(cat->fp);                                   \
331 	(void)fprintf(stderr, "%s: no more memory.", _errowner); \
332 	free(cat);                                               \
333 	errno = ENOMEM;                                          \
334 	return (NLERR);                                          \
335 }
336 
337 static void
338 __nls_free_resources(cat, i)
339 	MCCatT  *cat;
340 	int     i;
341 {
342 	MCSetT  *set;
343 	int     j;
344 
345 	for (j = 0; j < i; j++) {
346 		set = cat->sets + j;
347 		if (!set->invalid) {
348 			free(set->data.str);
349 			free(set->u.msgs);
350 		}
351 	}
352 	free(cat->sets);
353 }
354 
355 static nl_catd
356 loadCat(catpath)
357 	__const char    *catpath;
358 {
359 	MCHeaderT       header;
360 	MCCatT          *cat;
361 	MCSetT          *set;
362 	long            i;
363 	off_t           nextSet;
364 	int             saverr;
365 
366 	if ((cat = (MCCatT *)malloc(sizeof(MCCatT))) == NULL) {
367 		errno = ENOMEM;
368 		return (NLERR);
369 	}
370 	cat->loadType = MCLoadBySet;
371 
372 	if ((cat->fp = fopen(catpath, "r")) == NULL) {
373 		saverr = errno;
374 		free(cat);
375 		errno = saverr;
376 		return (NLERR);
377 	}
378 	(void)_fcntl(fileno(cat->fp), F_SETFD, FD_CLOEXEC);
379 
380 	if (fread(&header, sizeof(header), 1, cat->fp) != 1 ||
381 	    strncmp(header.magic, MCMagic, MCMagicLen) != 0)
382 		CORRUPT();
383 
384 	if (header.majorVer != MCMajorVer) {
385 		(void)fclose(cat->fp);
386 		free(cat);
387 		(void)fprintf(stderr, "%s: %s is version %ld, we need %ld.\n",
388 		    _errowner, catpath, header.majorVer, MCMajorVer);
389 		NLRETERR(EFTYPE);
390 	}
391 	if (header.numSets <= 0) {
392 		(void)fclose(cat->fp);
393 		free(cat);
394 		(void)fprintf(stderr, "%s: %s has %ld sets!\n",
395 		    _errowner, catpath, header.numSets);
396 		NLRETERR(EFTYPE);
397 	}
398 
399 	cat->numSets = header.numSets;
400 	if ((cat->sets = (MCSetT *)malloc(sizeof(MCSetT) * header.numSets)) ==
401 	    NULL)
402 		NOSPACE();
403 
404 	nextSet = header.firstSet;
405 	for (i = 0; i < cat->numSets; ++i) {
406 		if (fseeko(cat->fp, nextSet, SEEK_SET) == -1) {
407 			__nls_free_resources(cat, i);
408 			CORRUPT();
409 		}
410 
411 		/* read in the set header */
412 		set = cat->sets + i;
413 		if (fread(set, sizeof(*set), 1, cat->fp) != 1) {
414 			__nls_free_resources(cat, i);
415 			CORRUPT();
416 		}
417 
418 		/* if it's invalid, skip over it (and backup 'i') */
419 		if (set->invalid) {
420 			--i;
421 			nextSet = set->nextSet;
422 			continue;
423 		}
424 #if 0
425 		if (cat->loadType == MCLoadAll) {
426 			int     res;
427 
428 			if ((res = loadSet(cat, set)) <= 0) {
429 				__nls_free_resources(cat, i);
430 				if (res < 0)
431 					NOSPACE();
432 				CORRUPT();
433 			}
434 		} else
435 #endif
436 			set->invalid = TRUE;
437 		nextSet = set->nextSet;
438 	}
439 #if 0
440 	if (cat->loadType == MCLoadAll) {
441 		(void)fclose(cat->fp);
442 		cat->fp = NULL;
443 	}
444 #endif
445 	return ((nl_catd) cat);
446 }
447 
448 static int
449 loadSet(cat, set)
450 	MCCatT  *cat;
451 	MCSetT  *set;
452 {
453 	MCMsgT  *msg;
454 	int     i;
455 	int     saverr;
456 
457 	/* Get the data */
458 	if (fseeko(cat->fp, set->data.off, SEEK_SET) == -1)
459 		return (0);
460 	if ((set->data.str = malloc(set->dataLen)) == NULL) {
461 		errno = ENOMEM;
462 		return (-1);
463 	}
464 	if (fread(set->data.str, set->dataLen, 1, cat->fp) != 1) {
465 		saverr = errno;
466 		free(set->data.str);
467 		errno = saverr;
468 		return (0);
469 	}
470 
471 	/* Get the messages */
472 	if (fseeko(cat->fp, set->u.firstMsg, SEEK_SET) == -1) {
473 		saverr = errno;
474 		free(set->data.str);
475 		errno = saverr;
476 		return (0);
477 	}
478 	if ((set->u.msgs = (MCMsgT *)malloc(sizeof(MCMsgT) * set->numMsgs)) ==
479 	    NULL) {
480 		free(set->data.str);
481 		errno = ENOMEM;
482 		return (-1);
483 	}
484 
485 	for (i = 0; i < set->numMsgs; ++i) {
486 		msg = set->u.msgs + i;
487 		if (fread(msg, sizeof(*msg), 1, cat->fp) != 1) {
488 			saverr = errno;
489 			free(set->u.msgs);
490 			free(set->data.str);
491 			errno = saverr;
492 			return (0);
493 		}
494 		if (msg->invalid) {
495 			--i;
496 			continue;
497 		}
498 		msg->msg.str = (char *)(set->data.str + msg->msg.off);
499 	}
500 	set->invalid = FALSE;
501 	return (1);
502 }
503