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