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