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