xref: /freebsd/lib/libc/nls/msgcat.c (revision 8e6b01171e30297084bb0b4457c4183c2746aacc)
1 /*	$Id: msgcat.c,v 1.3 1995/06/17 03:02:21 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, *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:/usr/local/share/nls/%L/%N.cat:/usr/local/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     int		i;
264 
265     if (!cat) return -1;
266 
267     if (cat->loadType != MCLoadAll) close(cat->fd);
268     for (i = 0; i < cat->numSets; ++i) {
269 	set = cat->sets + i;
270 	if (!set->invalid) {
271 	    free(set->data.str);
272 	    free(set->u.msgs);
273 	}
274     }
275     free(cat->sets);
276     free(cat);
277 
278     return 0;
279 }
280 
281 /*
282  * Internal routines
283  */
284 
285 /* Note that only malloc failures are allowed to return an error */
286 #define ERRNAME	"Message Catalog System"
287 #define CORRUPT() {fprintf(stderr, "%s: corrupt file.\n", ERRNAME); return(0);}
288 #define NOSPACE() {fprintf(stderr, "%s: no more memory.\n", ERRNAME); return(NLERR);}
289 
290 static nl_catd loadCat( catpath, type)
291 __const char *catpath;
292 int type;
293 {
294     MCHeaderT	header;
295     MCCatT	*cat;
296     MCSetT	*set;
297     long	i;
298     off_t	nextSet;
299 
300     cat = (MCCatT *) malloc(sizeof(MCCatT));
301     if (!cat) return(NLERR);
302     cat->loadType = type;
303 
304     if ((cat->fd = open(catpath, O_RDONLY)) < 0) {
305 	return(0);
306     }
307 
308     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 	fprintf(stderr, "%s: %s is version %d, we need %d.\n", ERRNAME,
316 		catpath, header.majorVer, MCMajorVer);
317 	return(0);
318     }
319 
320     if (header.numSets <= 0) {
321 	fprintf(stderr, "%s: %s has %d sets!\n", ERRNAME, catpath,
322 		header.numSets);
323 	return(0);
324     }
325 
326     cat->numSets = header.numSets;
327     cat->sets = (MCSetT *) malloc(sizeof(MCSetT) * header.numSets);
328     if (!cat->sets) NOSPACE();
329 
330     nextSet = header.firstSet;
331     for (i = 0; i < cat->numSets; ++i) {
332 	if (lseek(cat->fd, nextSet, 0) == -1) CORRUPT();
333 
334 	/* read in the set header */
335 	set = cat->sets + i;
336 	if (read(cat->fd, set, sizeof(*set)) != sizeof(*set)) CORRUPT();
337 
338 	/* if it's invalid, skip over it (and backup 'i') */
339 
340 	if (set->invalid) {
341 	    --i;
342 	    nextSet = set->nextSet;
343 	    continue;
344 	}
345 
346 	if (cat->loadType == MCLoadAll) {
347 	    nl_catd	res;
348 	    if ((res = loadSet(cat, set)) <= 0) {
349 		if (res == -1) NOSPACE();
350 		CORRUPT();
351 	    }
352 	} else set->invalid = True;
353 	nextSet = set->nextSet;
354     }
355     if (cat->loadType == MCLoadAll) {
356 	close(cat->fd);
357 	cat->fd = -1;
358     }
359     return((nl_catd) cat);
360 }
361 
362 static nl_catd loadSet( cat, set)
363 MCCatT *cat;
364 MCSetT *set;
365 {
366     MCMsgT	*msg;
367     int		i;
368 
369     /* Get the data */
370     if (lseek(cat->fd, set->data.off, 0) == -1) return(0);
371     if ((set->data.str = (char *) malloc(set->dataLen)) == NULL) return(-1);
372     if (read(cat->fd, set->data.str, set->dataLen) != set->dataLen) return(0);
373 
374     /* Get the messages */
375     if (lseek(cat->fd, set->u.firstMsg, 0) == -1) return(0);
376     if ((set->u.msgs = (MCMsgT *) malloc(sizeof(MCMsgT) * set->numMsgs)) == NULL) return(-1);
377 
378     for (i = 0; i < set->numMsgs; ++i) {
379 	msg = set->u.msgs + i;
380 	if (read(cat->fd, msg, sizeof(*msg)) != sizeof(*msg)) return(0);
381 	if (msg->invalid) {
382 	    --i;
383 	    continue;
384 	}
385 	msg->msg.str = (char *) (set->data.str + msg->msg.off);
386     }
387     set->invalid = False;
388     return(1);
389 }
390 
391 
392 
393 
394 
395