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