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