xref: /freebsd/lib/libc/nls/msgcat.c (revision 952d112864d8008aa87278a30a539d888a8493cd)
1 /*	$Id: msgcat.c,v 1.8 1997/03/24 06:15:07 imp 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, *tmppath = NULL;
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 	/* XXX Should really be issetguid(), but we don't have that */
105 	if ((nlspath = (char *) getenv("NLSPATH")) == NULL ||
106 		getuid() != geteuid() || getgid() != getegid()) {
107 	    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";
108 	}
109 
110 	len = strlen(nlspath);
111 	base = cptr = (char *) malloc(len + 2);
112 	if (!base) return(NLERR);
113 	strcpy(cptr, nlspath);
114 	cptr[len] = ':';
115 	cptr[len+1] = '\0';
116 
117 	for (nlspath = cptr; *cptr; ++cptr) {
118 	    if (*cptr == ':') {
119 		*cptr = '\0';
120 		for (pathP = path; *nlspath; ++nlspath) {
121 		    if (*nlspath == '%') {
122 			if (*(nlspath + 1) == 'L') {
123 			    ++nlspath;
124 			    strcpy(pathP, lang);
125 			    pathP += strlen(lang);
126 			} else if (*(nlspath + 1) == 'N') {
127 			    ++nlspath;
128 			    strcpy(pathP, name);
129 			    pathP += strlen(name);
130 			} else *(pathP++) = *nlspath;
131 		    } else *(pathP++) = *nlspath;
132 		}
133 		*pathP = '\0';
134 		if (stat(path, &sbuf) == 0) {
135 		    catpath = path;
136 		    break;
137 		}
138 		nlspath = cptr+1;
139 	    }
140 	}
141 	free(base);
142 	if (tmppath) free(tmppath);
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); return(0);}
291 #define NOSPACE() {fprintf(stderr, "%s: no more memory.\n", ERRNAME); 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;
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 	return(0);
309     }
310 
311     fcntl(cat->fd, F_SETFD, FD_CLOEXEC);
312 
313     if (read(cat->fd, &header, sizeof(header)) != sizeof(header)) CORRUPT();
314 
315     if (strncmp(header.magic, MCMagic, MCMagicLen) != 0) CORRUPT();
316 
317     if (header.majorVer != MCMajorVer) {
318 	fprintf(stderr, "%s: %s is version %ld, we need %ld.\n", ERRNAME,
319 		catpath, header.majorVer, MCMajorVer);
320 	return(0);
321     }
322 
323     if (header.numSets <= 0) {
324 	fprintf(stderr, "%s: %s has %ld sets!\n", ERRNAME, catpath,
325 		header.numSets);
326 	return(0);
327     }
328 
329     cat->numSets = header.numSets;
330     cat->sets = (MCSetT *) malloc(sizeof(MCSetT) * header.numSets);
331     if (!cat->sets) NOSPACE();
332 
333     nextSet = header.firstSet;
334     for (i = 0; i < cat->numSets; ++i) {
335 	if (lseek(cat->fd, nextSet, 0) == -1) CORRUPT();
336 
337 	/* read in the set header */
338 	set = cat->sets + i;
339 	if (read(cat->fd, set, sizeof(*set)) != sizeof(*set)) CORRUPT();
340 
341 	/* if it's invalid, skip over it (and backup 'i') */
342 
343 	if (set->invalid) {
344 	    --i;
345 	    nextSet = set->nextSet;
346 	    continue;
347 	}
348 
349 	if (cat->loadType == MCLoadAll) {
350 	    nl_catd	res;
351 	    if ((res = loadSet(cat, set)) <= 0) {
352 		if (res == -1) NOSPACE();
353 		CORRUPT();
354 	    }
355 	} else set->invalid = True;
356 	nextSet = set->nextSet;
357     }
358     if (cat->loadType == MCLoadAll) {
359 	close(cat->fd);
360 	cat->fd = -1;
361     }
362     return((nl_catd) cat);
363 }
364 
365 static nl_catd loadSet( cat, set)
366 MCCatT *cat;
367 MCSetT *set;
368 {
369     MCMsgT	*msg;
370     int		i;
371 
372     /* Get the data */
373     if (lseek(cat->fd, set->data.off, 0) == -1) return(0);
374     if ((set->data.str = (char *) malloc(set->dataLen)) == NULL) return(-1);
375     if (read(cat->fd, set->data.str, set->dataLen) != set->dataLen) return(0);
376 
377     /* Get the messages */
378     if (lseek(cat->fd, set->u.firstMsg, 0) == -1) return(0);
379     if ((set->u.msgs = (MCMsgT *) malloc(sizeof(MCMsgT) * set->numMsgs)) == NULL) return(-1);
380 
381     for (i = 0; i < set->numMsgs; ++i) {
382 	msg = set->u.msgs + i;
383 	if (read(cat->fd, msg, sizeof(*msg)) != sizeof(*msg)) return(0);
384 	if (msg->invalid) {
385 	    --i;
386 	    continue;
387 	}
388 	msg->msg.str = (char *) (set->data.str + msg->msg.off);
389     }
390     set->invalid = False;
391     return(1);
392 }
393 
394 
395 
396 
397 
398