Build aicasm as host program, not via world's compiler.
[dragonfly.git] / lib / libc / nls / msgcat.c
CommitLineData
984263bc
MD
1/***********************************************************
2Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
3
4 All Rights Reserved
5
6Permission to use, copy, modify, and distribute this software and its
7documentation for any purpose and without fee is hereby granted,
8provided that the above copyright notice appear in all copies and that
9both that copyright notice and this permission notice appear in
10supporting documentation, and that Alfalfa's name not be used in
11advertising or publicity pertaining to distribution of the software
12without specific, written prior permission.
13
14ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20SOFTWARE.
21
22If you make any modifications, bugfixes or other changes to this software
23we'd appreciate it if you could send a copy to us so we can keep things
24up-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******************************************************************/
1de703da
MD
32/*
33 * $FreeBSD: src/lib/libc/nls/msgcat.c,v 1.21.2.6 2002/08/12 11:23:54 ache Exp $
17ea2221 34 * $DragonFly: src/lib/libc/nls/Attic/msgcat.c,v 1.4 2005/01/31 22:29:36 dillon Exp $
1de703da 35 */
984263bc
MD
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
17ea2221 42#include "namespace.h"
984263bc
MD
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>
17ea2221 54#include "un-namespace.h"
984263bc
MD
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
67static nl_catd loadCat();
68static int loadSet();
69static void __nls_free_resources();
70
71nl_catd
72catopen(name, type)
9bd47c1d 73 const char *name;
984263bc
MD
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
247static MCSetT *
248MCGetSet(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
263static MCMsgT *
264MCGetMsg(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
277char *
278catgets(catd, setId, msgId, dflt)
279 nl_catd catd;
280 int setId;
281 int msgId;
9bd47c1d 282 const char *dflt;
984263bc
MD
283{
284 MCMsgT *msg;
285 MCCatT *cat = (MCCatT *)catd;
9bd47c1d 286 const char *cptr;
984263bc
MD
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
298int
299catclose(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 */
322static 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
339static 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
357static nl_catd
358loadCat(catpath)
9bd47c1d 359 const char *catpath;
984263bc
MD
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
450static int
451loadSet(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}