]> git.saurik.com Git - apple/libc.git/blame - nls/FreeBSD/msgcat.c
Libc-763.13.tar.gz
[apple/libc.git] / nls / FreeBSD / msgcat.c
CommitLineData
9385eb3d
A
1/***********************************************************
2Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
3
4 All Rights Reserved
5
6Permission to use, copy, modify, and distribute this software and its
7documentation for any purpose and without fee is hereby granted,
8provided that the above copyright notice appear in all copies and that
9both that copyright notice and this permission notice appear in
10supporting documentation, and that Alfalfa's name not be used in
11advertising or publicity pertaining to distribution of the software
12without specific, written prior permission.
13
14ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20SOFTWARE.
21
22If you make any modifications, bugfixes or other changes to this software
23we'd appreciate it if you could send a copy to us so we can keep things
24up-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
67static nl_catd loadCat(__const char *);
68static int loadSet(MCCatT *, MCSetT *);
69static void __nls_free_resources(MCCatT *, int);
9385eb3d
A
70
71nl_catd
1f2f436a 72catopen(__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
240static MCSetT *
1f2f436a 241MCGetSet(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
254static MCMsgT *
1f2f436a 255MCGetMsg(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
266char *
1f2f436a 267catgets(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
283int
1f2f436a 284catclose(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 */
304static 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
322static 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
338static nl_catd
1f2f436a 339loadCat(__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
410static int
1f2f436a 411loadSet(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}