xref: /freebsd/lib/libc/nls/msgcat.c (revision 33b77e2decd50e53798014b70bf7ca3bdc4c0c7e)
1 /*	$Id: msgcat.c,v 1.11 1997/05/10 04:40:40 ache Exp $ */
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 <fcntl.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <unistd.h>
64 
65 #ifndef True
66 # define True	~0
67 # define False	0
68 #endif
69 
70 /* take care of sysv diffs */
71 #ifndef MAXPATHLEN
72 #define MAXPATHLEN 1024
73 #endif
74 
75 #ifndef FD_CLOEXEC
76 #define FD_CLOEXEC 1
77 #endif
78 
79 #define	NLERR	((nl_catd) -1)
80 
81 static nl_catd loadCat();
82 static nl_catd loadSet();
83 
84 nl_catd 	_catopen( name, type)
85 __const char *name;
86 int type;
87 {
88     char	path[MAXPATHLEN];
89     __const char *catpath = NULL;
90     char        *nlspath;
91     char	*lang;
92     long	len;
93     char	*base, *cptr, *pathP;
94     struct stat	sbuf;
95 
96     if (!name || !*name) return(NLERR);
97 
98     if (strchr(name, '/')) {
99 	catpath = name;
100 	if (stat(catpath, &sbuf)) return(0);
101     } else {
102 	if ((lang = (char *) getenv("LANG")) == NULL)
103 		lang = "C";
104 	if ((nlspath = (char *) getenv("NLSPATH")) == NULL
105 #ifndef __NETBSD_SYSCALLS
106 	    || issetugid()
107 #endif
108 	    )
109 	    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";
110 
111 	len = strlen(nlspath);
112 	base = cptr = (char *) malloc(len + 2);
113 	if (!base) return(NLERR);
114 	strcpy(cptr, nlspath);
115 	cptr[len] = ':';
116 	cptr[len+1] = '\0';
117 
118 	for (nlspath = cptr; *cptr; ++cptr) {
119 	    if (*cptr == ':') {
120 		*cptr = '\0';
121 		for (pathP = path; *nlspath; ++nlspath) {
122 		    if (*nlspath == '%') {
123 			if (*(nlspath + 1) == 'L') {
124 			    ++nlspath;
125 			    strcpy(pathP, lang);
126 			    pathP += strlen(lang);
127 			} else if (*(nlspath + 1) == 'N') {
128 			    ++nlspath;
129 			    strcpy(pathP, name);
130 			    pathP += strlen(name);
131 			} else *(pathP++) = *nlspath;
132 		    } else *(pathP++) = *nlspath;
133 		}
134 		*pathP = '\0';
135 		if (stat(path, &sbuf) == 0) {
136 		    catpath = path;
137 		    break;
138 		}
139 		nlspath = cptr+1;
140 	    }
141 	}
142 	free(base);
143 
144 	if (!catpath) return(0);
145     }
146 
147     return(loadCat(catpath, type));
148 }
149 
150 /*
151  * We've got an odd situation here.  The odds are real good that the
152  * number we are looking for is almost the same as the index.  We could
153  * use the index, check the difference and do something intelligent, but
154  * I haven't quite figured out what's intelligent.
155  *
156  * Here's a start.
157  *	Take an id N.  If there are > N items in the list, then N cannot
158  *	be more than N items from the start, since otherwise there would
159  *	have to be duplicate items.  So we can safely set the top to N+1
160  *	(after taking into account that ids start at 1, and arrays at 0)
161  *
162  *	Let's say we are at position P, and we are looking for N, but have
163  *	V.  If N > V, then the furthest away that N could be is
164  *	P + (N-V).  So we can safely set hi to P+(N-V)+1.  For example:
165  *		We are looking for 10, but have 8
166  *		8	?	?	?	?
167  *			>=9	>=10	>=11
168  *
169  */
170 static MCSetT	*MCGetSet( cat, setId)
171 MCCatT *cat;
172 int setId;
173 {
174     MCSetT	*set;
175     long	lo, hi, cur, dir;
176 
177     if (!cat || setId <= 0) return(NULL);
178 
179     lo = 0;
180     if (setId - 1 < cat->numSets) {
181 	cur = setId - 1;
182 	hi = setId;
183     } else {
184 	hi = cat->numSets;
185 	cur = (hi - lo) / 2;
186     }
187 
188     while (True) {
189 	set = cat->sets + cur;
190 	if (set->setId == setId) break;
191 	if (set->setId < setId) {
192 	    lo = cur+1;
193 	    if (hi > cur + (setId - set->setId) + 1) hi = cur+(setId-set->setId)+1;
194 	    dir = 1;
195 	} else {
196 	    hi = cur;
197 	    dir = -1;
198 	}
199 	if (lo >= hi) return(NULL);
200 	if (hi - lo == 1) cur += dir;
201 	else cur += ((hi - lo) / 2) * dir;
202     }
203     if (set->invalid) loadSet(cat, set);
204     return(set);
205 }
206 
207 
208 static MCMsgT	*MCGetMsg( set, msgId)
209 MCSetT *set;
210 int msgId;
211 {
212     MCMsgT	*msg;
213     long	lo, hi, cur, dir;
214 
215     if (!set || set->invalid || msgId <= 0) return(NULL);
216 
217     lo = 0;
218     if (msgId - 1 < set->numMsgs) {
219 	cur = msgId - 1;
220 	hi = msgId;
221     } else {
222 	hi = set->numMsgs;
223 	cur = (hi - lo) / 2;
224     }
225 
226     while (True) {
227 	msg = set->u.msgs + cur;
228 	if (msg->msgId == msgId) break;
229 	if (msg->msgId < msgId) {
230 	    lo = cur+1;
231 	    if (hi > cur + (msgId - msg->msgId) + 1) hi = cur+(msgId-msg->msgId)+1;
232 	    dir = 1;
233 	} else {
234 	    hi = cur;
235 	    dir = -1;
236 	}
237 	if (lo >= hi) return(NULL);
238 	if (hi - lo == 1) cur += dir;
239 	else cur += ((hi - lo) / 2) * dir;
240     }
241     return(msg);
242 }
243 
244 char	*_catgets( catd, setId, msgId, dflt)
245 nl_catd catd;
246 int setId;
247 int msgId;
248 char *dflt;
249 {
250     MCMsgT	*msg;
251     MCCatT	*cat = (MCCatT *) catd;
252     char	*cptr;
253 
254     msg = MCGetMsg(MCGetSet(cat, setId), msgId);
255     if (msg) cptr = msg->msg.str;
256     else cptr = dflt;
257     return(cptr);
258 }
259 
260 
261 int		_catclose( catd)
262 nl_catd catd;
263 {
264     MCCatT	*cat = (MCCatT *) catd;
265     MCSetT	*set;
266     int		i;
267 
268     if (!cat) return -1;
269 
270     if (cat->loadType != MCLoadAll) close(cat->fd);
271     for (i = 0; i < cat->numSets; ++i) {
272 	set = cat->sets + i;
273 	if (!set->invalid) {
274 	    free(set->data.str);
275 	    free(set->u.msgs);
276 	}
277     }
278     free(cat->sets);
279     free(cat);
280 
281     return 0;
282 }
283 
284 /*
285  * Internal routines
286  */
287 
288 /* Note that only malloc failures are allowed to return an error */
289 #define ERRNAME	"Message Catalog System"
290 #define CORRUPT() {fprintf(stderr, "%s: corrupt file.\n", ERRNAME); free(cat); return(0);}
291 #define NOSPACE() {fprintf(stderr, "%s: no more memory.\n", ERRNAME); free(cat); return(NLERR);}
292 
293 static nl_catd loadCat( catpath, type)
294 __const char *catpath;
295 int type;
296 {
297     MCHeaderT	header;
298     MCCatT	*cat;
299     MCSetT	*set;
300     long        i, j;
301     off_t	nextSet;
302 
303     cat = (MCCatT *) malloc(sizeof(MCCatT));
304     if (!cat) return(NLERR);
305     cat->loadType = type;
306 
307     if ((cat->fd = open(catpath, O_RDONLY)) < 0) {
308 	free(cat);
309 	return(0);
310     }
311 
312     (void)fcntl(cat->fd, F_SETFD, FD_CLOEXEC);
313 
314     if (read(cat->fd, &header, sizeof(header)) != sizeof(header)) CORRUPT();
315 
316     if (strncmp(header.magic, MCMagic, MCMagicLen) != 0) CORRUPT();
317 
318     if (header.majorVer != MCMajorVer) {
319 	free(cat);
320 	fprintf(stderr, "%s: %s is version %ld, we need %ld.\n", ERRNAME,
321 		catpath, header.majorVer, MCMajorVer);
322 	return(0);
323     }
324 
325     if (header.numSets <= 0) {
326 	free(cat);
327 	fprintf(stderr, "%s: %s has %ld sets!\n", ERRNAME, catpath,
328 		header.numSets);
329 	return(0);
330     }
331 
332     cat->numSets = header.numSets;
333     cat->sets = (MCSetT *) malloc(sizeof(MCSetT) * header.numSets);
334     if (!cat->sets) NOSPACE();
335 
336     nextSet = header.firstSet;
337     for (i = 0; i < cat->numSets; ++i) {
338 	if (lseek(cat->fd, nextSet, 0) == -1) {
339 		for (j = 0; j < i; j++) {
340 			set = cat->sets + j;
341 			if (!set->invalid) {
342 			    free(set->data.str);
343 			    free(set->u.msgs);
344 			}
345 		}
346 		free(cat->sets);
347 		CORRUPT();
348 	}
349 
350 	/* read in the set header */
351 	set = cat->sets + i;
352 	if (read(cat->fd, set, sizeof(*set)) != sizeof(*set)) {
353 		for (j = 0; j < i; j++) {
354 			set = cat->sets + j;
355 			if (!set->invalid) {
356 			    free(set->data.str);
357 			    free(set->u.msgs);
358 			}
359 		}
360 		free(cat->sets);
361 		CORRUPT();
362 	}
363 
364 	/* if it's invalid, skip over it (and backup 'i') */
365 
366 	if (set->invalid) {
367 	    --i;
368 	    nextSet = set->nextSet;
369 	    continue;
370 	}
371 
372 	if (cat->loadType == MCLoadAll) {
373 	    nl_catd	res;
374 
375 	    if ((res = loadSet(cat, set)) <= 0) {
376 		for (j = 0; j < i; j++) {
377 			set = cat->sets + j;
378 			if (!set->invalid) {
379 			    free(set->data.str);
380 			    free(set->u.msgs);
381 			}
382 		}
383 		free(cat->sets);
384 		if (res == -1) NOSPACE();
385 		CORRUPT();
386 	    }
387 	} else set->invalid = True;
388 	nextSet = set->nextSet;
389     }
390     if (cat->loadType == MCLoadAll) {
391 	close(cat->fd);
392 	cat->fd = -1;
393     }
394     return((nl_catd) cat);
395 }
396 
397 static nl_catd loadSet( cat, set)
398 MCCatT *cat;
399 MCSetT *set;
400 {
401     MCMsgT	*msg;
402     int		i;
403 
404     /* Get the data */
405     if (lseek(cat->fd, set->data.off, 0) == -1) return(0);
406     if ((set->data.str = (char *) malloc(set->dataLen)) == NULL) return(-1);
407     if (read(cat->fd, set->data.str, set->dataLen) != set->dataLen) {
408 	free(set->data.str); return(0);
409     }
410 
411     /* Get the messages */
412     if (lseek(cat->fd, set->u.firstMsg, 0) == -1) {
413 	free(set->data.str); return(0);
414     }
415     if ((set->u.msgs = (MCMsgT *) malloc(sizeof(MCMsgT) * set->numMsgs)) == NULL) {
416 	free(set->data.str); return(-1);
417     }
418 
419     for (i = 0; i < set->numMsgs; ++i) {
420 	msg = set->u.msgs + i;
421 	if (read(cat->fd, msg, sizeof(*msg)) != sizeof(*msg)) {
422 	    free(set->u.msgs); free(set->data.str); return(0);
423 	}
424 	if (msg->invalid) {
425 	    --i;
426 	    continue;
427 	}
428 	msg->msg.str = (char *) (set->data.str + msg->msg.off);
429     }
430     set->invalid = False;
431     return(1);
432 }
433