]> git.saurik.com Git - apple/libc.git/blob - nls/msgcat-fbsd.c
Libc-763.13.tar.gz
[apple/libc.git] / nls / msgcat-fbsd.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 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD: src/lib/libc/nls/msgcat.c,v 1.49 2005/02/01 16:04:55 phantom Exp $");
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>
48 #include <xlocale.h>
49 #include <nl_types.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <machine/endian.h>
55 #include <libkern/OSByteOrder.h>
56 #include "un-namespace.h"
57
58 #include "msgcat.h"
59 #include "setlocale.h" /* for ENCODING_LEN */
60
61 #ifndef ntohll
62 #define ntohll(x) OSSwapBigToHostInt64(x)
63 #endif
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
73 static nl_catd loadCat(__const char *);
74 static int loadSet(MCCatT *, MCSetT *);
75 static void __nls_free_resources(MCCatT *, int);
76
77 nl_catd
78 catopen(__const char *name, int type)
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)
94 lang = (char *)querylocale(LC_MESSAGES_MASK, NULL);
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
118 if ((nlspath = getenv("NLSPATH")) == NULL || issetugid())
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) {
165 too_long:
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; \
217 if (ID - 1 < NUM) { \
218 cur = ID - 1; \
219 hi = ID; \
220 } else { \
221 hi = NUM; \
222 cur = (hi - lo) / 2; \
223 } \
224 while (TRUE) { \
225 CHILD = PARENT->SET + cur; \
226 if (ntohl(CHILD->ID) == ID) \
227 break; \
228 if (ntohl(CHILD->ID) < ID) { \
229 lo = cur + 1; \
230 if (hi > cur + (ID - ntohl(CHILD->ID)) + 1) \
231 hi = cur + (ID - ntohl(CHILD->ID)) + 1; \
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
246 static MCSetT *
247 MCGetSet(MCCatT *cat, int setId)
248 {
249 MCSetT *set;
250 int32_t lo, hi, cur, dir;
251
252 if (cat == NULL || setId <= 0)
253 return (NULL);
254 LOOKUP(cat, set, setId, cat->numSets, sets);
255 if (set->invalid && loadSet(cat, set) <= 0)
256 return (NULL);
257 return (set);
258 }
259
260 static MCMsgT *
261 MCGetMsg(MCSetT *set, int msgId)
262 {
263 MCMsgT *msg;
264 int32_t lo, hi, cur, dir;
265
266 if (set == NULL || set->invalid || msgId <= 0)
267 return (NULL);
268 LOOKUP(set, msg, msgId, ntohl(set->numMsgs), u.msgs);
269 return (msg);
270 }
271
272 char *
273 catgets(nl_catd catd, int setId, int msgId, __const char *dflt)
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
289 int
290 catclose(nl_catd catd)
291 {
292 MCCatT *cat = (MCCatT *)catd;
293
294 if (catd == NULL || catd == NLERR) {
295 errno = EBADF;
296 return (-1);
297 }
298
299 (void)fclose(cat->fp);
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 */
310 static 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
328 static void
329 __nls_free_resources(MCCatT *cat, int i)
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
344 static nl_catd
345 loadCat(__const char *catpath)
346 {
347 MCHeaderT header;
348 MCCatT *cat;
349 MCSetT *set;
350 int32_t i;
351 off_t nextSet;
352 int saverr;
353
354 if ((cat = (MCCatT *)malloc(sizeof(MCCatT))) == NULL)
355 return (NLERR);
356
357 if ((cat->fp = fopen(catpath, "r")) == NULL) {
358 saverr = errno;
359 free(cat);
360 errno = saverr;
361 return (NLERR);
362 }
363 (void)_fcntl(fileno(cat->fp), F_SETFD, FD_CLOEXEC);
364
365 if (fread(&header, sizeof(header), 1, cat->fp) != 1 ||
366 strncmp(header.magic, MCMagic, MCMagicLen) != 0)
367 CORRUPT();
368
369 if (ntohl(header.majorVer) != MCMajorVer) {
370 (void)fclose(cat->fp);
371 free(cat);
372 if (OSSwapInt32(ntohl(header.majorVer)) == MCMajorVer) {
373 (void)fprintf(stderr, "%s: %s is the wrong byte ordering.\n", _errowner, catpath);
374 } else {
375 (void)fprintf(stderr, "%s: %s is version %d, we need %d.\n", _errowner, catpath, (int)ntohl(header.majorVer), MCMajorVer);
376 }
377 NLRETERR(EFTYPE);
378 }
379 if (ntohl(header.numSets) <= 0) {
380 (void)fclose(cat->fp);
381 free(cat);
382 (void)fprintf(stderr, "%s: %s has %d sets!\n",
383 _errowner, catpath, (int)ntohl(header.numSets));
384 NLRETERR(EFTYPE);
385 }
386
387 cat->numSets = ntohl(header.numSets);
388 if ((cat->sets = (MCSetT *)malloc(sizeof(MCSetT) * cat->numSets)) ==
389 NULL)
390 NOSPACE();
391
392 nextSet = ntohll(header.firstSet);
393 for (i = 0; i < cat->numSets; ++i) {
394 if (fseeko(cat->fp, nextSet, SEEK_SET) == -1) {
395 __nls_free_resources(cat, i);
396 CORRUPT();
397 }
398
399 /* read in the set header */
400 set = cat->sets + i;
401 if (fread(set, sizeof(*set), 1, cat->fp) != 1) {
402 __nls_free_resources(cat, i);
403 CORRUPT();
404 }
405
406 /* if it's invalid, skip over it (and backup 'i') */
407 if (set->invalid) {
408 --i;
409 nextSet = ntohll(set->nextSet);
410 continue;
411 }
412 set->invalid = TRUE;
413 nextSet = ntohll(set->nextSet);
414 }
415
416 return ((nl_catd) cat);
417 }
418
419 static int
420 loadSet(MCCatT *cat, MCSetT *set)
421 {
422 MCMsgT *msg;
423 int i;
424 int saverr;
425
426 /* Get the data */
427 if (fseeko(cat->fp, ntohll(set->data.off), SEEK_SET) == -1)
428 return (0);
429 if ((set->data.str = malloc(ntohl(set->dataLen))) == NULL)
430 return (-1);
431 if (fread(set->data.str, ntohl(set->dataLen), 1, cat->fp) != 1) {
432 saverr = errno;
433 free(set->data.str);
434 errno = saverr;
435 return (0);
436 }
437
438 /* Get the messages */
439 if (fseeko(cat->fp, ntohll(set->u.firstMsg), SEEK_SET) == -1) {
440 saverr = errno;
441 free(set->data.str);
442 errno = saverr;
443 return (0);
444 }
445 if ((set->u.msgs = (MCMsgT *)malloc(sizeof(MCMsgT) * ntohl(set->numMsgs))) ==
446 NULL) {
447 saverr = errno;
448 free(set->data.str);
449 errno = saverr;
450 return (-1);
451 }
452
453 for (i = 0; i < ntohl(set->numMsgs); ++i) {
454 msg = set->u.msgs + i;
455 if (fread(msg, sizeof(*msg), 1, cat->fp) != 1) {
456 saverr = errno;
457 free(set->u.msgs);
458 free(set->data.str);
459 errno = saverr;
460 return (0);
461 }
462 if (msg->invalid) {
463 --i;
464 continue;
465 }
466 msg->msg.str = (char *)(set->data.str + ntohll(msg->msg.off));
467 }
468 set->invalid = FALSE;
469 return (1);
470 }