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