]> git.saurik.com Git - apple/libc.git/blob - nls/FreeBSD/msgcat.c
fd9e820fd299d6dc47c6cc0873cf537e38c8415d
[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.45 2002/10/27 17:44:33 wollman 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
115 #ifndef __NETBSD_SYSCALLS
116 || issetugid()
117 #endif
118 )
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 < PARENT->NUM) { \
218 cur = ID - 1; \
219 hi = ID; \
220 } else { \
221 hi = PARENT->NUM; \
222 cur = (hi - lo) / 2; \
223 } \
224 while (TRUE) { \
225 CHILD = PARENT->SET + cur; \
226 if (CHILD->ID == ID) \
227 break; \
228 if (CHILD->ID < ID) { \
229 lo = cur + 1; \
230 if (hi > cur + (ID - CHILD->ID) + 1) \
231 hi = cur + (ID - 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(cat, setId)
248 MCCatT *cat;
249 int setId;
250 {
251 MCSetT *set;
252 long lo, hi, cur, dir;
253
254 if (cat == NULL || setId <= 0)
255 return (NULL);
256 LOOKUP(cat, set, setId, numSets, sets);
257 if (set->invalid && loadSet(cat, set) <= 0)
258 return (NULL);
259 return (set);
260 }
261
262 static MCMsgT *
263 MCGetMsg(set, msgId)
264 MCSetT *set;
265 int msgId;
266 {
267 MCMsgT *msg;
268 long lo, hi, cur, dir;
269
270 if (set == NULL || set->invalid || msgId <= 0)
271 return (NULL);
272 LOOKUP(set, msg, msgId, numMsgs, u.msgs);
273 return (msg);
274 }
275
276 char *
277 catgets(catd, setId, msgId, dflt)
278 nl_catd catd;
279 int setId;
280 int msgId;
281 __const char *dflt;
282 {
283 MCMsgT *msg;
284 MCCatT *cat = (MCCatT *)catd;
285 __const char *cptr;
286
287 if (catd == NULL || catd == NLERR)
288 return ((char *)dflt);
289 msg = MCGetMsg(MCGetSet(cat, setId), msgId);
290 if (msg != NULL)
291 cptr = msg->msg.str;
292 else
293 cptr = dflt;
294 return ((char *)cptr);
295 }
296
297 int
298 catclose(catd)
299 nl_catd catd;
300 {
301 MCCatT *cat = (MCCatT *)catd;
302
303 if (catd == NULL || catd == NLERR) {
304 errno = EBADF;
305 return (-1);
306 }
307 #if 0
308 if (cat->loadType != MCLoadAll)
309 #endif
310 (void)fclose(cat->fp);
311 __nls_free_resources(cat, cat->numSets);
312 free(cat);
313 return (0);
314 }
315
316 /*
317 * Internal routines
318 */
319
320 /* Note that only malloc failures are allowed to return an error */
321 static char *_errowner = "Message Catalog System";
322
323 #define CORRUPT() { \
324 (void)fclose(cat->fp); \
325 (void)fprintf(stderr, "%s: corrupt file.", _errowner); \
326 free(cat); \
327 NLRETERR(EFTYPE); \
328 }
329
330 #define NOSPACE() { \
331 saverr = errno; \
332 (void)fclose(cat->fp); \
333 (void)fprintf(stderr, "%s: no more memory.", _errowner); \
334 free(cat); \
335 errno = saverr; \
336 return (NLERR); \
337 }
338
339 static void
340 __nls_free_resources(cat, i)
341 MCCatT *cat;
342 int i;
343 {
344 MCSetT *set;
345 int j;
346
347 for (j = 0; j < i; j++) {
348 set = cat->sets + j;
349 if (!set->invalid) {
350 free(set->data.str);
351 free(set->u.msgs);
352 }
353 }
354 free(cat->sets);
355 }
356
357 static nl_catd
358 loadCat(catpath)
359 __const char *catpath;
360 {
361 MCHeaderT header;
362 MCCatT *cat;
363 MCSetT *set;
364 long i;
365 off_t nextSet;
366 int saverr;
367
368 if ((cat = (MCCatT *)malloc(sizeof(MCCatT))) == NULL)
369 return (NLERR);
370 cat->loadType = MCLoadBySet;
371
372 if ((cat->fp = fopen(catpath, "r")) == NULL) {
373 saverr = errno;
374 free(cat);
375 errno = saverr;
376 return (NLERR);
377 }
378 (void)_fcntl(fileno(cat->fp), F_SETFD, FD_CLOEXEC);
379
380 if (fread(&header, sizeof(header), 1, cat->fp) != 1 ||
381 strncmp(header.magic, MCMagic, MCMagicLen) != 0)
382 CORRUPT();
383
384 if (header.majorVer != MCMajorVer) {
385 (void)fclose(cat->fp);
386 free(cat);
387 (void)fprintf(stderr, "%s: %s is version %ld, we need %ld.\n",
388 _errowner, catpath, header.majorVer, MCMajorVer);
389 NLRETERR(EFTYPE);
390 }
391 if (header.numSets <= 0) {
392 (void)fclose(cat->fp);
393 free(cat);
394 (void)fprintf(stderr, "%s: %s has %ld sets!\n",
395 _errowner, catpath, header.numSets);
396 NLRETERR(EFTYPE);
397 }
398
399 cat->numSets = header.numSets;
400 if ((cat->sets = (MCSetT *)malloc(sizeof(MCSetT) * header.numSets)) ==
401 NULL)
402 NOSPACE();
403
404 nextSet = header.firstSet;
405 for (i = 0; i < cat->numSets; ++i) {
406 if (fseeko(cat->fp, nextSet, SEEK_SET) == -1) {
407 __nls_free_resources(cat, i);
408 CORRUPT();
409 }
410
411 /* read in the set header */
412 set = cat->sets + i;
413 if (fread(set, sizeof(*set), 1, cat->fp) != 1) {
414 __nls_free_resources(cat, i);
415 CORRUPT();
416 }
417
418 /* if it's invalid, skip over it (and backup 'i') */
419 if (set->invalid) {
420 --i;
421 nextSet = set->nextSet;
422 continue;
423 }
424 #if 0
425 if (cat->loadType == MCLoadAll) {
426 int res;
427
428 if ((res = loadSet(cat, set)) <= 0) {
429 saverr = errno;
430 __nls_free_resources(cat, i);
431 errno = saverr;
432 if (res < 0)
433 NOSPACE();
434 CORRUPT();
435 }
436 } else
437 #endif
438 set->invalid = TRUE;
439 nextSet = set->nextSet;
440 }
441 #if 0
442 if (cat->loadType == MCLoadAll) {
443 (void)fclose(cat->fp);
444 cat->fp = NULL;
445 }
446 #endif
447 return ((nl_catd) cat);
448 }
449
450 static int
451 loadSet(cat, set)
452 MCCatT *cat;
453 MCSetT *set;
454 {
455 MCMsgT *msg;
456 int i;
457 int saverr;
458
459 /* Get the data */
460 if (fseeko(cat->fp, set->data.off, SEEK_SET) == -1)
461 return (0);
462 if ((set->data.str = malloc(set->dataLen)) == NULL)
463 return (-1);
464 if (fread(set->data.str, set->dataLen, 1, cat->fp) != 1) {
465 saverr = errno;
466 free(set->data.str);
467 errno = saverr;
468 return (0);
469 }
470
471 /* Get the messages */
472 if (fseeko(cat->fp, set->u.firstMsg, SEEK_SET) == -1) {
473 saverr = errno;
474 free(set->data.str);
475 errno = saverr;
476 return (0);
477 }
478 if ((set->u.msgs = (MCMsgT *)malloc(sizeof(MCMsgT) * set->numMsgs)) ==
479 NULL) {
480 saverr = errno;
481 free(set->data.str);
482 errno = saverr;
483 return (-1);
484 }
485
486 for (i = 0; i < set->numMsgs; ++i) {
487 msg = set->u.msgs + i;
488 if (fread(msg, sizeof(*msg), 1, cat->fp) != 1) {
489 saverr = errno;
490 free(set->u.msgs);
491 free(set->data.str);
492 errno = saverr;
493 return (0);
494 }
495 if (msg->invalid) {
496 --i;
497 continue;
498 }
499 msg->msg.str = (char *)(set->data.str + msg->msg.off);
500 }
501 set->invalid = FALSE;
502 return (1);
503 }