grrr...fix reverse chronological order
[dragonfly.git] / lib / libcr / 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/libcr/nls/Attic/msgcat.c,v 1.3 2003/08/22 19:31:21 asmodai 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 <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/syslimits.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <locale.h>
48 #include <nl_types.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53
54 #include "msgcat.h"
55 #include "../locale/setlocale.h"        /* for ENCODING_LEN */
56
57 #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"
58
59 #define TRUE    1
60 #define FALSE   0
61
62 #define NLERR           ((nl_catd) -1)
63 #define NLRETERR(errc)  { errno = errc; return (NLERR); }
64
65 static nl_catd  loadCat();
66 static int      loadSet();
67 static void     __nls_free_resources();
68
69 nl_catd
70 catopen(name, type)
71         const char      *name;
72         int             type;
73 {
74         int             spcleft, saverr;
75         char            path[PATH_MAX];
76         char            *nlspath, *lang, *base, *cptr, *pathP, *tmpptr;
77         char            *cptr1, *plang, *pter, *pcode;
78         struct stat     sbuf;
79
80         if (name == NULL || *name == '\0')
81                 NLRETERR(EINVAL);
82
83         /* is it absolute path ? if yes, load immediately */
84         if (strchr(name, '/') != NULL)
85                 return (loadCat(name));
86
87         if (type == NL_CAT_LOCALE)
88                 lang = setlocale(LC_MESSAGES, NULL);
89         else
90                 lang = getenv("LANG");
91
92         if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
93             (lang[0] == '.' &&
94              (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
95             strchr(lang, '/') != NULL)
96                 lang = "C";
97
98         if ((plang = cptr1 = strdup(lang)) == NULL) {
99                 errno = ENOMEM;
100                 return (NLERR);
101         }
102         if ((cptr = strchr(cptr1, '@')) != NULL)
103                 *cptr = '\0';
104         pter = pcode = "";
105         if ((cptr = strchr(cptr1, '_')) != NULL) {
106                 *cptr++ = '\0';
107                 pter = cptr1 = cptr;
108         }
109         if ((cptr = strchr(cptr1, '.')) != NULL) {
110                 *cptr++ = '\0';
111                 pcode = cptr;
112         }
113
114         if ((nlspath = getenv("NLSPATH")) == NULL
115 #ifndef __NETBSD_SYSCALLS
116             || issetugid()
117 #endif
118            )
119                 nlspath = _DEFAULT_NLS_PATH;
120
121         if ((base = cptr = strdup(nlspath)) == NULL) {
122                 free(plang);
123                 errno = ENOMEM;
124                 return (NLERR);
125         }
126
127         while ((nlspath = strsep(&cptr, ":")) != NULL) {
128                 pathP = path;
129                 if (*nlspath) {
130                         for (; *nlspath; ++nlspath) {
131                                 if (*nlspath == '%') {
132                                         switch (*(nlspath + 1)) {
133                                         case 'l':
134                                                 tmpptr = plang;
135                                                 break;
136                                         case 't':
137                                                 tmpptr = pter;
138                                                 break;
139                                         case 'c':
140                                                 tmpptr = pcode;
141                                                 break;
142                                         case 'L':
143                                                 tmpptr = lang;
144                                                 break;
145                                         case 'N':
146                                                 tmpptr = (char *)name;
147                                                 break;
148                                         case '%':
149                                                 ++nlspath;
150                                                 /* fallthrough */
151                                         default:
152                                                 if (pathP - path >=
153                                                     sizeof(path) - 1)
154                                                         goto too_long;
155                                                 *(pathP++) = *nlspath;
156                                                 continue;
157                                         }
158                                         ++nlspath;
159                         put_tmpptr:
160                                         spcleft = sizeof(path) -
161                                                   (pathP - path) - 1;
162                                         if (strlcpy(pathP, tmpptr, spcleft) >=
163                                             spcleft) {
164                                 too_long:
165                                                 free(plang);
166                                                 free(base);
167                                                 NLRETERR(ENAMETOOLONG);
168                                         }
169                                         pathP += strlen(tmpptr);
170                                 } else {
171                                         if (pathP - path >= sizeof(path) - 1)
172                                                 goto too_long;
173                                         *(pathP++) = *nlspath;
174                                 }
175                         }
176                         *pathP = '\0';
177                         if (stat(path, &sbuf) == 0) {
178                                 free(plang);
179                                 free(base);
180                                 return (loadCat(path));
181                         }
182                 } else {
183                         tmpptr = (char *)name;
184                         --nlspath;
185                         goto put_tmpptr;
186                 }
187         }
188         free(plang);
189         free(base);
190         NLRETERR(ENOENT);
191 }
192
193 /*
194  * We've got an odd situation here.  The odds are real good that the
195  * number we are looking for is almost the same as the index.  We could
196  * use the index, check the difference and do something intelligent, but
197  * I haven't quite figured out what's intelligent.
198  *
199  * Here's a start.
200  *      Take an id N.  If there are > N items in the list, then N cannot
201  *      be more than N items from the start, since otherwise there would
202  *      have to be duplicate items.  So we can safely set the top to N+1
203  *      (after taking into account that ids start at 1, and arrays at 0)
204  *
205  *      Let's say we are at position P, and we are looking for N, but have
206  *      V.  If N > V, then the furthest away that N could be is
207  *      P + (N-V).  So we can safely set hi to P+(N-V)+1.  For example:
208  *              We are looking for 10, but have 8
209  *              8       ?       ?       ?       ?
210  *                      >=9     >=10    >=11
211  *
212  */
213
214 #define LOOKUP(PARENT, CHILD, ID, NUM, SET) {                    \
215         lo = 0;                                                  \
216         if (ID - 1 < PARENT->NUM) {                              \
217                 cur = ID - 1;                                    \
218                 hi = ID;                                         \
219         } else {                                                 \
220                 hi = PARENT->NUM;                                \
221                 cur = (hi - lo) / 2;                             \
222         }                                                        \
223         while (TRUE) {                                           \
224                 CHILD = PARENT->SET + cur;                       \
225                 if (CHILD->ID == ID)                             \
226                         break;                                   \
227                 if (CHILD->ID < ID) {                            \
228                         lo = cur + 1;                            \
229                         if (hi > cur + (ID - CHILD->ID) + 1)     \
230                                 hi = cur + (ID - CHILD->ID) + 1; \
231                         dir = 1;                                 \
232                 } else {                                         \
233                         hi = cur;                                \
234                         dir = -1;                                \
235                 }                                                \
236                 if (lo >= hi)                                    \
237                         return (NULL);                           \
238                 if (hi - lo == 1)                                \
239                         cur += dir;                              \
240                 else                                             \
241                         cur += ((hi - lo) / 2) * dir;            \
242         }                                                        \
243 }
244
245 static MCSetT *
246 MCGetSet(cat, setId)
247         MCCatT  *cat;
248         int     setId;
249 {
250         MCSetT  *set;
251         long    lo, hi, cur, dir;
252
253         if (cat == NULL || setId <= 0)
254                 return (NULL);
255         LOOKUP(cat, set, setId, numSets, sets);
256         if (set->invalid && loadSet(cat, set) <= 0)
257                 return (NULL);
258         return (set);
259 }
260
261 static MCMsgT *
262 MCGetMsg(set, msgId)
263         MCSetT  *set;
264         int     msgId;
265 {
266         MCMsgT  *msg;
267         long    lo, hi, cur, dir;
268
269         if (set == NULL || set->invalid || msgId <= 0)
270                 return (NULL);
271         LOOKUP(set, msg, msgId, numMsgs, u.msgs);
272         return (msg);
273 }
274
275 char *
276 catgets(catd, setId, msgId, dflt)
277         nl_catd         catd;
278         int             setId;
279         int             msgId;
280         const char      *dflt;
281 {
282         MCMsgT          *msg;
283         MCCatT          *cat = (MCCatT *)catd;
284         const char      *cptr;
285
286         if (catd == NULL || catd == NLERR)
287                 return ((char *)dflt);
288         msg = MCGetMsg(MCGetSet(cat, setId), msgId);
289         if (msg != NULL)
290                 cptr = msg->msg.str;
291         else
292                 cptr = dflt;
293         return ((char *)cptr);
294 }
295
296 int
297 catclose(catd)
298         nl_catd catd;
299 {
300         MCCatT  *cat = (MCCatT *)catd;
301
302         if (catd == NULL || catd == NLERR) {
303                 errno = EBADF;
304                 return (-1);
305         }
306 #if 0
307         if (cat->loadType != MCLoadAll)
308 #endif
309                 (void)fclose(cat->fp);
310         __nls_free_resources(cat, cat->numSets);
311         free(cat);
312         return (0);
313 }
314
315 /*
316  * Internal routines
317  */
318
319 /* Note that only malloc failures are allowed to return an error */
320 static char     *_errowner = "Message Catalog System";
321
322 #define CORRUPT() {                                            \
323         (void)fclose(cat->fp);                                 \
324         (void)fprintf(stderr, "%s: corrupt file.", _errowner); \
325         free(cat);                                             \
326         NLRETERR(EFTYPE);                                      \
327 }
328
329 #define NOSPACE() {                                              \
330         (void)fclose(cat->fp);                                   \
331         (void)fprintf(stderr, "%s: no more memory.", _errowner); \
332         free(cat);                                               \
333         errno = ENOMEM;                                          \
334         return (NLERR);                                          \
335 }
336
337 static void
338 __nls_free_resources(cat, i)
339         MCCatT  *cat;
340         int     i;
341 {
342         MCSetT  *set;
343         int     j;
344
345         for (j = 0; j < i; j++) {
346                 set = cat->sets + j;
347                 if (!set->invalid) {
348                         free(set->data.str);
349                         free(set->u.msgs);
350                 }
351         }
352         free(cat->sets);
353 }
354
355 static nl_catd
356 loadCat(catpath)
357         const char      *catpath;
358 {
359         MCHeaderT       header;
360         MCCatT          *cat;
361         MCSetT          *set;
362         long            i;
363         off_t           nextSet;
364         int             saverr;
365
366         if ((cat = (MCCatT *)malloc(sizeof(MCCatT))) == NULL) {
367                 errno = ENOMEM;
368                 return (NLERR);
369         }
370         cat->loadType = MCLoadBySet;
371
372         if ((cat->fp = fopen(catpath, "r")) == NULL) {
373                 saverr = errno;
374                 free(cat);
375                 errno = saverr;
376                 return (NLERR);
377         }
378         (void)_fcntl(fileno(cat->fp), F_SETFD, FD_CLOEXEC);
379
380         if (fread(&header, sizeof(header), 1, cat->fp) != 1 ||
381             strncmp(header.magic, MCMagic, MCMagicLen) != 0)
382                 CORRUPT();
383
384         if (header.majorVer != MCMajorVer) {
385                 (void)fclose(cat->fp);
386                 free(cat);
387                 (void)fprintf(stderr, "%s: %s is version %ld, we need %ld.\n",
388                     _errowner, catpath, header.majorVer, MCMajorVer);
389                 NLRETERR(EFTYPE);
390         }
391         if (header.numSets <= 0) {
392                 (void)fclose(cat->fp);
393                 free(cat);
394                 (void)fprintf(stderr, "%s: %s has %ld sets!\n",
395                     _errowner, catpath, header.numSets);
396                 NLRETERR(EFTYPE);
397         }
398
399         cat->numSets = header.numSets;
400         if ((cat->sets = (MCSetT *)malloc(sizeof(MCSetT) * header.numSets)) ==
401             NULL)
402                 NOSPACE();
403
404         nextSet = header.firstSet;
405         for (i = 0; i < cat->numSets; ++i) {
406                 if (fseeko(cat->fp, nextSet, SEEK_SET) == -1) {
407                         __nls_free_resources(cat, i);
408                         CORRUPT();
409                 }
410
411                 /* read in the set header */
412                 set = cat->sets + i;
413                 if (fread(set, sizeof(*set), 1, cat->fp) != 1) {
414                         __nls_free_resources(cat, i);
415                         CORRUPT();
416                 }
417
418                 /* if it's invalid, skip over it (and backup 'i') */
419                 if (set->invalid) {
420                         --i;
421                         nextSet = set->nextSet;
422                         continue;
423                 }
424 #if 0
425                 if (cat->loadType == MCLoadAll) {
426                         int     res;
427
428                         if ((res = loadSet(cat, set)) <= 0) {
429                                 __nls_free_resources(cat, i);
430                                 if (res < 0)
431                                         NOSPACE();
432                                 CORRUPT();
433                         }
434                 } else
435 #endif
436                         set->invalid = TRUE;
437                 nextSet = set->nextSet;
438         }
439 #if 0
440         if (cat->loadType == MCLoadAll) {
441                 (void)fclose(cat->fp);
442                 cat->fp = NULL;
443         }
444 #endif
445         return ((nl_catd) cat);
446 }
447
448 static int
449 loadSet(cat, set)
450         MCCatT  *cat;
451         MCSetT  *set;
452 {
453         MCMsgT  *msg;
454         int     i;
455         int     saverr;
456
457         /* Get the data */
458         if (fseeko(cat->fp, set->data.off, SEEK_SET) == -1)
459                 return (0);
460         if ((set->data.str = malloc(set->dataLen)) == NULL) {
461                 errno = ENOMEM;
462                 return (-1);
463         }
464         if (fread(set->data.str, set->dataLen, 1, cat->fp) != 1) {
465                 saverr = errno;
466                 free(set->data.str);
467                 errno = saverr;
468                 return (0);
469         }
470
471         /* Get the messages */
472         if (fseeko(cat->fp, set->u.firstMsg, SEEK_SET) == -1) {
473                 saverr = errno;
474                 free(set->data.str);
475                 errno = saverr;
476                 return (0);
477         }
478         if ((set->u.msgs = (MCMsgT *)malloc(sizeof(MCMsgT) * set->numMsgs)) ==
479             NULL) {
480                 free(set->data.str);
481                 errno = ENOMEM;
482                 return (-1);
483         }
484
485         for (i = 0; i < set->numMsgs; ++i) {
486                 msg = set->u.msgs + i;
487                 if (fread(msg, sizeof(*msg), 1, cat->fp) != 1) {
488                         saverr = errno;
489                         free(set->u.msgs);
490                         free(set->data.str);
491                         errno = saverr;
492                         return (0);
493                 }
494                 if (msg->invalid) {
495                         --i;
496                         continue;
497                 }
498                 msg->msg.str = (char *)(set->data.str + msg->msg.off);
499         }
500         set->invalid = FALSE;
501         return (1);
502 }