]>
Commit | Line | Data |
---|---|---|
9385eb3d A |
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> | |
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 |
73 | static nl_catd loadCat(__const char *); |
74 | static int loadSet(MCCatT *, MCSetT *); | |
75 | static void __nls_free_resources(MCCatT *, int); | |
9385eb3d A |
76 | |
77 | nl_catd | |
1f2f436a | 78 | catopen(__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 | ||
246 | static MCSetT * | |
1f2f436a | 247 | MCGetSet(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 | ||
260 | static MCMsgT * | |
1f2f436a | 261 | MCGetMsg(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 | ||
272 | char * | |
1f2f436a | 273 | catgets(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 | ||
289 | int | |
1f2f436a | 290 | catclose(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 */ | |
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 | |
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 | ||
344 | static nl_catd | |
1f2f436a | 345 | loadCat(__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 | ||
427 | static int | |
1f2f436a | 428 | loadSet(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 | } |