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