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