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