]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/nls/msgcat.c
This commit was generated by cvs2svn to compensate for changes in r57429,
[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))
332         CORRUPT();
333
334     if (strncmp(header.magic, MCMagic, MCMagicLen) != 0) CORRUPT();
335
336     if (header.majorVer != MCMajorVer) {
337         free(cat);
338         fprintf(stderr, "%s: %s is version %ld, we need %ld.\n", ERRNAME,
339                 catpath, header.majorVer, MCMajorVer);
340         errno = EINVAL;
341         return(NLERR);
342     }
343
344     if (header.numSets <= 0) {
345         free(cat);
346         fprintf(stderr, "%s: %s has %ld sets!\n", ERRNAME, catpath,
347                 header.numSets);
348         errno = EINVAL;
349         return(NLERR);
350     }
351
352     cat->numSets = header.numSets;
353     cat->sets = (MCSetT *) malloc(sizeof(MCSetT) * header.numSets);
354     if (!cat->sets) NOSPACE();
355
356     nextSet = header.firstSet;
357     for (i = 0; i < cat->numSets; ++i) {
358         if (lseek(cat->fd, nextSet, 0) == -1) {
359                 for (j = 0; j < i; j++) {
360                         set = cat->sets + j;
361                         if (!set->invalid) {
362                             free(set->data.str);
363                             free(set->u.msgs);
364                         }
365                 }
366                 free(cat->sets);
367                 CORRUPT();
368         }
369
370         /* read in the set header */
371         set = cat->sets + i;
372         if (_read(cat->fd, set, sizeof(*set)) != sizeof(*set)) {
373                 for (j = 0; j < i; j++) {
374                         set = cat->sets + j;
375                         if (!set->invalid) {
376                             free(set->data.str);
377                             free(set->u.msgs);
378                         }
379                 }
380                 free(cat->sets);
381                 CORRUPT();
382         }
383
384         /* if it's invalid, skip over it (and backup 'i') */
385
386         if (set->invalid) {
387             --i;
388             nextSet = set->nextSet;
389             continue;
390         }
391
392         if (cat->loadType == MCLoadAll) {
393             int res;
394
395             if ((res = loadSet(cat, set)) <= 0) {
396                 for (j = 0; j < i; j++) {
397                         set = cat->sets + j;
398                         if (!set->invalid) {
399                             free(set->data.str);
400                             free(set->u.msgs);
401                         }
402                 }
403                 free(cat->sets);
404                 if (res < 0) NOSPACE();
405                 CORRUPT();
406             }
407         } else set->invalid = True;
408         nextSet = set->nextSet;
409     }
410     if (cat->loadType == MCLoadAll) {
411         _close(cat->fd);
412         cat->fd = -1;
413     }
414     return((nl_catd) cat);
415 }
416
417 static int loadSet(cat, set)
418 MCCatT *cat;
419 MCSetT *set;
420 {
421     MCMsgT      *msg;
422     int         i;
423
424     /* Get the data */
425     if (lseek(cat->fd, set->data.off, 0) == -1) return(0);
426     if ((set->data.str = malloc(set->dataLen)) == NULL) return(-1);
427     if (_read(cat->fd, set->data.str, set->dataLen) != set->dataLen) {
428         free(set->data.str); return(0);
429     }
430
431     /* Get the messages */
432     if (lseek(cat->fd, set->u.firstMsg, 0) == -1) {
433         free(set->data.str); return(0);
434     }
435     if ((set->u.msgs = (MCMsgT *) malloc(sizeof(MCMsgT) * set->numMsgs)) == NULL) {
436         free(set->data.str); return(-1);
437     }
438
439     for (i = 0; i < set->numMsgs; ++i) {
440         msg = set->u.msgs + i;
441         if (_read(cat->fd, msg, sizeof(*msg)) != sizeof(*msg)) {
442             free(set->u.msgs); free(set->data.str); return(0);
443         }
444         if (msg->invalid) {
445             --i;
446             continue;
447         }
448         msg->msg.str = (char *) (set->data.str + msg->msg.off);
449     }
450     set->invalid = False;
451     return(1);
452 }