Build aicasm as host program, not via world's compiler.
[dragonfly.git] / lib / libc / nls / msgcat.c
1 /***********************************************************
2 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
3
4                         All Rights Reserved
5
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted,
8 provided that the above copyright notice appear in all copies and that
9 both that copyright notice and this permission notice appear in
10 supporting documentation, and that Alfalfa's name not be used in
11 advertising or publicity pertaining to distribution of the software
12 without specific, written prior permission.
13
14 ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16 ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 SOFTWARE.
21
22 If you make any modifications, bugfixes or other changes to this software
23 we'd appreciate it if you could send a copy to us so we can keep things
24 up-to-date.  Many thanks.
25                                 Kee Hinckley
26                                 Alfalfa Software, Inc.
27                                 267 Allston St., #3
28                                 Cambridge, MA 02139  USA
29                                 nazgul@alfalfa.com
30
31 ******************************************************************/
32 /*
33  * $FreeBSD: src/lib/libc/nls/msgcat.c,v 1.21.2.6 2002/08/12 11:23:54 ache Exp $
34  * $DragonFly: src/lib/libc/nls/Attic/msgcat.c,v 1.4 2005/01/31 22:29:36 dillon Exp $
35  */
36
37 /*
38  * We need a better way of handling errors than printing text.  I need
39  * to add an error handling routine.
40  */
41
42 #include "namespace.h"
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <sys/syslimits.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <locale.h>
49 #include <nl_types.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include "un-namespace.h"
55
56 #include "msgcat.h"
57 #include "../locale/setlocale.h"        /* for ENCODING_LEN */
58
59 #define _DEFAULT_NLS_PATH "/usr/share/nls/%L/%N.cat:/usr/share/nls/%N/%L:/usr/local/share/nls/%L/%N.cat:/usr/local/share/nls/%N/%L"
60
61 #define TRUE    1
62 #define FALSE   0
63
64 #define NLERR           ((nl_catd) -1)
65 #define NLRETERR(errc)  { errno = errc; return (NLERR); }
66
67 static nl_catd  loadCat();
68 static int      loadSet();
69 static void     __nls_free_resources();
70
71 nl_catd
72 catopen(name, type)
73         const char      *name;
74         int             type;
75 {
76         int             spcleft, saverr;
77         char            path[PATH_MAX];
78         char            *nlspath, *lang, *base, *cptr, *pathP, *tmpptr;
79         char            *cptr1, *plang, *pter, *pcode;
80         struct stat     sbuf;
81
82         if (name == NULL || *name == '\0')
83                 NLRETERR(EINVAL);
84
85         /* is it absolute path ? if yes, load immediately */
86         if (strchr(name, '/') != NULL)
87                 return (loadCat(name));
88
89         if (type == NL_CAT_LOCALE)
90                 lang = setlocale(LC_MESSAGES, NULL);
91         else
92                 lang = getenv("LANG");
93
94         if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
95             (lang[0] == '.' &&
96              (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
97             strchr(lang, '/') != NULL)
98                 lang = "C";
99
100         if ((plang = cptr1 = strdup(lang)) == NULL) {
101                 errno = ENOMEM;
102                 return (NLERR);
103         }
104         if ((cptr = strchr(cptr1, '@')) != NULL)
105                 *cptr = '\0';
106         pter = pcode = "";
107         if ((cptr = strchr(cptr1, '_')) != NULL) {
108                 *cptr++ = '\0';
109                 pter = cptr1 = cptr;
110         }
111         if ((cptr = strchr(cptr1, '.')) != NULL) {
112                 *cptr++ = '\0';
113                 pcode = cptr;
114         }
115
116         if ((nlspath = getenv("NLSPATH")) == NULL
117 #ifndef __NETBSD_SYSCALLS
118             || issetugid()
119 #endif
120            )
121                 nlspath = _DEFAULT_NLS_PATH;
122
123         if ((base = cptr = strdup(nlspath)) == NULL) {
124                 free(plang);
125                 errno = ENOMEM;
126                 return (NLERR);
127         }
128
129         while ((nlspath = strsep(&cptr, ":")) != NULL) {
130                 pathP = path;
131                 if (*nlspath) {
132                         for (; *nlspath; ++nlspath) {
133                                 if (*nlspath == '%') {
134                                         switch (*(nlspath + 1)) {
135                                         case 'l':
136                                                 tmpptr = plang;
137                                                 break;
138                                         case 't':
139                                                 tmpptr = pter;
140                                                 break;
141                                         case 'c':
142                                                 tmpptr = pcode;
143                                                 break;
144                                         case 'L':
145                                                 tmpptr = lang;
146                                                 break;
147                                         case 'N':
148                                                 tmpptr = (char *)name;
149                                                 break;
150                                         case '%':
151                                                 ++nlspath;
152                                                 /* fallthrough */
153                                         default:
154                                                 if (pathP - path >=
155                                                     sizeof(path) - 1)
156                                                         goto too_long;
157                                                 *(pathP++) = *nlspath;
158                                                 continue;
159                                         }
160                                         ++nlspath;
161                         put_tmpptr:
162                                         spcleft = sizeof(path) -
163                                                   (pathP - path) - 1;
164                                         if (strlcpy(pathP, tmpptr, spcleft) >=
165                                             spcleft) {
166                                 too_long:
167                                                 free(plang);
168                                                 free(base);
169                                                 NLRETERR(ENAMETOOLONG);
170                                         }
171                                         pathP += strlen(tmpptr);
172                                 } else {
173                                         if (pathP - path >= sizeof(path) - 1)
174                                                 goto too_long;
175                                         *(pathP++) = *nlspath;
176                                 }
177                         }
178                         *pathP = '\0';
179                         if (stat(path, &sbuf) == 0) {
180                                 free(plang);
181                                 free(base);
182                                 return (loadCat(path));
183                         }
184                 } else {
185                         tmpptr = (char *)name;
186                         --nlspath;
187                         goto put_tmpptr;
188                 }
189         }
190         free(plang);
191         free(base);
192         NLRETERR(ENOENT);
193 }
194
195 /*
196  * We've got an odd situation here.  The odds are real good that the
197  * number we are looking for is almost the same as the index.  We could
198  * use the index, check the difference and do something intelligent, but
199  * I haven't quite figured out what's intelligent.
200  *
201  * Here's a start.
202  *      Take an id N.  If there are > N items in the list, then N cannot
203  *      be more than N items from the start, since otherwise there would
204  *      have to be duplicate items.  So we can safely set the top to N+1
205  *      (after taking into account that ids start at 1, and arrays at 0)
206  *
207  *      Let's say we are at position P, and we are looking for N, but have
208  *      V.  If N > V, then the furthest away that N could be is
209  *      P + (N-V).  So we can safely set hi to P+(N-V)+1.  For example:
210  *              We are looking for 10, but have 8
211  *              8       ?       ?       ?       ?
212  *                      >=9     >=10    >=11
213  *
214  */
215
216 #define LOOKUP(PARENT, CHILD, ID, NUM, SET) {                    \
217         lo = 0;                                                  \
218         if (ID - 1 < PARENT->NUM) {                              \
219                 cur = ID - 1;                                    \
220                 hi = ID;                                         \
221         } else {                                                 \
222                 hi = PARENT->NUM;                                \
223                 cur = (hi - lo) / 2;                             \
224         }                                                        \
225         while (TRUE) {                                           \
226                 CHILD = PARENT->SET + cur;                       \
227                 if (CHILD->ID == ID)                             \
228                         break;                                   \
229                 if (CHILD->ID < ID) {                            \
230                         lo = cur + 1;                            \
231                         if (hi > cur + (ID - CHILD->ID) + 1)     \
232                                 hi = cur + (ID - CHILD->ID) + 1; \
233                         dir = 1;                                 \
234                 } else {                                         \
235                         hi = cur;                                \
236                         dir = -1;                                \
237                 }                                                \
238                 if (lo >= hi)                                    \
239                         return (NULL);                           \
240                 if (hi - lo == 1)                                \
241                         cur += dir;                              \
242                 else                                             \
243                         cur += ((hi - lo) / 2) * dir;            \
244         }                                                        \
245 }
246
247 static MCSetT *
248 MCGetSet(cat, setId)
249         MCCatT  *cat;
250         int     setId;
251 {
252         MCSetT  *set;
253         long    lo, hi, cur, dir;
254
255         if (cat == NULL || setId <= 0)
256                 return (NULL);
257         LOOKUP(cat, set, setId, numSets, sets);
258         if (set->invalid && loadSet(cat, set) <= 0)
259                 return (NULL);
260         return (set);
261 }
262
263 static MCMsgT *
264 MCGetMsg(set, msgId)
265         MCSetT  *set;
266         int     msgId;
267 {
268         MCMsgT  *msg;
269         long    lo, hi, cur, dir;
270
271         if (set == NULL || set->invalid || msgId <= 0)
272                 return (NULL);
273         LOOKUP(set, msg, msgId, numMsgs, u.msgs);
274         return (msg);
275 }
276
277 char *
278 catgets(catd, setId, msgId, dflt)
279         nl_catd         catd;
280         int             setId;
281         int             msgId;
282         const char      *dflt;
283 {
284         MCMsgT          *msg;
285         MCCatT          *cat = (MCCatT *)catd;
286         const char      *cptr;
287
288         if (catd == NULL || catd == NLERR)
289                 return ((char *)dflt);
290         msg = MCGetMsg(MCGetSet(cat, setId), msgId);
291         if (msg != NULL)
292                 cptr = msg->msg.str;
293         else
294                 cptr = dflt;
295         return ((char *)cptr);
296 }
297
298 int
299 catclose(catd)
300         nl_catd catd;
301 {
302         MCCatT  *cat = (MCCatT *)catd;
303
304         if (catd == NULL || catd == NLERR) {
305                 errno = EBADF;
306                 return (-1);
307         }
308 #if 0
309         if (cat->loadType != MCLoadAll)
310 #endif
311                 (void)fclose(cat->fp);
312         __nls_free_resources(cat, cat->numSets);
313         free(cat);
314         return (0);
315 }
316
317 /*
318  * Internal routines
319  */
320
321 /* Note that only malloc failures are allowed to return an error */
322 static char     *_errowner = "Message Catalog System";
323
324 #define CORRUPT() {                                            \
325         (void)fclose(cat->fp);                                 \
326         (void)fprintf(stderr, "%s: corrupt file.", _errowner); \
327         free(cat);                                             \
328         NLRETERR(EFTYPE);                                      \
329 }
330
331 #define NOSPACE() {                                              \
332         (void)fclose(cat->fp);                                   \
333         (void)fprintf(stderr, "%s: no more memory.", _errowner); \
334         free(cat);                                               \
335         errno = ENOMEM;                                          \
336         return (NLERR);                                          \
337 }
338
339 static void
340 __nls_free_resources(cat, i)
341         MCCatT  *cat;
342         int     i;
343 {
344         MCSetT  *set;
345         int     j;
346
347         for (j = 0; j < i; j++) {
348                 set = cat->sets + j;
349                 if (!set->invalid) {
350                         free(set->data.str);
351                         free(set->u.msgs);
352                 }
353         }
354         free(cat->sets);
355 }
356
357 static nl_catd
358 loadCat(catpath)
359         const char      *catpath;
360 {
361         MCHeaderT       header;
362         MCCatT          *cat;
363         MCSetT          *set;
364         long            i;
365         off_t           nextSet;
366         int             saverr;
367
368         if ((cat = (MCCatT *)malloc(sizeof(MCCatT))) == NULL) {
369                 errno = ENOMEM;
370                 return (NLERR);
371         }
372         cat->loadType = MCLoadBySet;
373
374         if ((cat->fp = fopen(catpath, "r")) == NULL) {
375                 saverr = errno;
376                 free(cat);
377                 errno = saverr;
378                 return (NLERR);
379         }
380         (void)_fcntl(fileno(cat->fp), F_SETFD, FD_CLOEXEC);
381
382         if (fread(&header, sizeof(header), 1, cat->fp) != 1 ||
383             strncmp(header.magic, MCMagic, MCMagicLen) != 0)
384                 CORRUPT();
385
386         if (header.majorVer != MCMajorVer) {
387                 (void)fclose(cat->fp);
388                 free(cat);
389                 (void)fprintf(stderr, "%s: %s is version %ld, we need %ld.\n",
390                     _errowner, catpath, header.majorVer, MCMajorVer);
391                 NLRETERR(EFTYPE);
392         }
393         if (header.numSets <= 0) {
394                 (void)fclose(cat->fp);
395                 free(cat);
396                 (void)fprintf(stderr, "%s: %s has %ld sets!\n",
397                     _errowner, catpath, header.numSets);
398                 NLRETERR(EFTYPE);
399         }
400
401         cat->numSets = header.numSets;
402         if ((cat->sets = (MCSetT *)malloc(sizeof(MCSetT) * header.numSets)) ==
403             NULL)
404                 NOSPACE();
405
406         nextSet = header.firstSet;
407         for (i = 0; i < cat->numSets; ++i) {
408                 if (fseeko(cat->fp, nextSet, SEEK_SET) == -1) {
409                         __nls_free_resources(cat, i);
410                         CORRUPT();
411                 }
412
413                 /* read in the set header */
414                 set = cat->sets + i;
415                 if (fread(set, sizeof(*set), 1, cat->fp) != 1) {
416                         __nls_free_resources(cat, i);
417                         CORRUPT();
418                 }
419
420                 /* if it's invalid, skip over it (and backup 'i') */
421                 if (set->invalid) {
422                         --i;
423                         nextSet = set->nextSet;
424                         continue;
425                 }
426 #if 0
427                 if (cat->loadType == MCLoadAll) {
428                         int     res;
429
430                         if ((res = loadSet(cat, set)) <= 0) {
431                                 __nls_free_resources(cat, i);
432                                 if (res < 0)
433                                         NOSPACE();
434                                 CORRUPT();
435                         }
436                 } else
437 #endif
438                         set->invalid = TRUE;
439                 nextSet = set->nextSet;
440         }
441 #if 0
442         if (cat->loadType == MCLoadAll) {
443                 (void)fclose(cat->fp);
444                 cat->fp = NULL;
445         }
446 #endif
447         return ((nl_catd) cat);
448 }
449
450 static int
451 loadSet(cat, set)
452         MCCatT  *cat;
453         MCSetT  *set;
454 {
455         MCMsgT  *msg;
456         int     i;
457         int     saverr;
458
459         /* Get the data */
460         if (fseeko(cat->fp, set->data.off, SEEK_SET) == -1)
461                 return (0);
462         if ((set->data.str = malloc(set->dataLen)) == NULL) {
463                 errno = ENOMEM;
464                 return (-1);
465         }
466         if (fread(set->data.str, set->dataLen, 1, cat->fp) != 1) {
467                 saverr = errno;
468                 free(set->data.str);
469                 errno = saverr;
470                 return (0);
471         }
472
473         /* Get the messages */
474         if (fseeko(cat->fp, set->u.firstMsg, SEEK_SET) == -1) {
475                 saverr = errno;
476                 free(set->data.str);
477                 errno = saverr;
478                 return (0);
479         }
480         if ((set->u.msgs = (MCMsgT *)malloc(sizeof(MCMsgT) * set->numMsgs)) ==
481             NULL) {
482                 free(set->data.str);
483                 errno = ENOMEM;
484                 return (-1);
485         }
486
487         for (i = 0; i < set->numMsgs; ++i) {
488                 msg = set->u.msgs + i;
489                 if (fread(msg, sizeof(*msg), 1, cat->fp) != 1) {
490                         saverr = errno;
491                         free(set->u.msgs);
492                         free(set->data.str);
493                         errno = saverr;
494                         return (0);
495                 }
496                 if (msg->invalid) {
497                         --i;
498                         continue;
499                 }
500                 msg->msg.str = (char *)(set->data.str + msg->msg.off);
501         }
502         set->invalid = FALSE;
503         return (1);
504 }