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