nrelease - fix/improve livecd
[dragonfly.git] / lib / libc / nls / msgcat.c
CommitLineData
0d5acd74
JM
1/***********************************************************
2Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
3Copyright 2010, Gabor Kovesdan <gabor@FreeBSD.org>
4
5 All Rights Reserved
6
7Permission to use, copy, modify, and distribute this software and its
8documentation for any purpose and without fee is hereby granted,
9provided that the above copyright notice appear in all copies and that
10both that copyright notice and this permission notice appear in
11supporting documentation, and that Alfalfa's name not be used in
12advertising or publicity pertaining to distribution of the software
13without specific, written prior permission.
14
15ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
16ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
17ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
18ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
19WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
20ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
21SOFTWARE.
22
23If you make any modifications, bugfixes or other changes to this software
24we'd appreciate it if you could send a copy to us so we can keep things
25up-to-date. Many thanks.
26 Kee Hinckley
27 Alfalfa Software, Inc.
28 267 Allston St., #3
29 Cambridge, MA 02139 USA
30 nazgul@alfalfa.com
31
4bbf0fc0 32$FreeBSD: head/lib/libc/nls/msgcat.c 304755 2016-08-24 16:44:27Z ache $
0d5acd74
JM
33******************************************************************/
34
35#define _NLS_PRIVATE
36
37#include "namespace.h"
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <sys/mman.h>
41#include <sys/queue.h>
42
43#include <arpa/inet.h> /* for ntohl() */
44
45#include <errno.h>
46#include <fcntl.h>
47#include <limits.h>
0d5acd74
JM
48#include <nl_types.h>
49#include <pthread.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <unistd.h>
54#include "un-namespace.h"
55
21a0e97d 56#include "../locale/xlocale_private.h"
8ad5bb66 57#include "libc_private.h"
0d5acd74
JM
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 RLOCK(fail) { int ret; \
62 if (__isthreaded && \
63 ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) { \
64 errno = ret; \
65 return (fail); \
66 }}
67#define WLOCK(fail) { int ret; \
68 if (__isthreaded && \
69 ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) { \
70 errno = ret; \
71 return (fail); \
72 }}
73#define UNLOCK { if (__isthreaded) \
74 _pthread_rwlock_unlock(&rwlock); }
75
76#define NLERR ((nl_catd) -1)
77#define NLRETERR(errc) { errno = errc; return (NLERR); }
78#define SAVEFAIL(n, l, e) { WLOCK(NLERR); \
79 np = malloc(sizeof(struct catentry)); \
80 if (np != NULL) { \
81 np->name = strdup(n); \
82 np->path = NULL; \
83 np->catd = NLERR; \
4bbf0fc0 84 np->refcount = 0; \
0d5acd74
JM
85 np->lang = (l == NULL) ? NULL : \
86 strdup(l); \
87 np->caterrno = e; \
88 SLIST_INSERT_HEAD(&cache, np, list); \
89 } \
90 UNLOCK; \
91 errno = e; \
92 }
93
94static nl_catd load_msgcat(const char *, const char *, const char *);
95
96static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
97
98struct catentry {
99 SLIST_ENTRY(catentry) list;
100 char *name;
101 char *path;
102 int caterrno;
103 nl_catd catd;
104 char *lang;
105 int refcount;
106};
107
108SLIST_HEAD(listhead, catentry) cache =
109 SLIST_HEAD_INITIALIZER(cache);
110
111nl_catd
112catopen(const char *name, int type)
8ad5bb66
SW
113{
114 return (__catopen_l(name, type, __get_locale()));
115}
116
117nl_catd
118__catopen_l(const char *name, int type, locale_t locale)
0d5acd74
JM
119{
120 struct stat sbuf;
121 struct catentry *np;
21a0e97d
SW
122 char *base, *cptr, *cptr1, *nlspath, *pathP, *pcode;
123 char *plang, *pter;
0d5acd74 124 int saverr, spcleft;
21a0e97d 125 const char *lang, *tmpptr;
0d5acd74
JM
126 char path[PATH_MAX];
127
128 /* sanity checking */
129 if (name == NULL || *name == '\0')
130 NLRETERR(EINVAL);
131
132 if (strchr(name, '/') != NULL)
133 /* have a pathname */
134 lang = NULL;
135 else {
136 if (type == NL_CAT_LOCALE)
8ad5bb66 137 lang = querylocale(LC_MESSAGES_MASK, locale);
0d5acd74
JM
138 else
139 lang = getenv("LANG");
140
141 if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
142 (lang[0] == '.' &&
143 (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
144 strchr(lang, '/') != NULL)
145 lang = "C";
146 }
147
148 /* Try to get it from the cache first */
149 RLOCK(NLERR);
150 SLIST_FOREACH(np, &cache, list) {
151 if ((strcmp(np->name, name) == 0) &&
152 ((lang != NULL && np->lang != NULL &&
153 strcmp(np->lang, lang) == 0) || (np->lang == lang))) {
154 if (np->caterrno != 0) {
155 /* Found cached failing entry */
156 UNLOCK;
157 NLRETERR(np->caterrno);
158 } else {
159 /* Found cached successful entry */
160 np->refcount++;
161 UNLOCK;
162 return (np->catd);
163 }
164 }
165 }
166 UNLOCK;
167
168 /* is it absolute path ? if yes, load immediately */
169 if (strchr(name, '/') != NULL)
170 return (load_msgcat(name, name, lang));
171
172 /* sanity checking */
173 if ((plang = cptr1 = strdup(lang)) == NULL)
174 return (NLERR);
175 if ((cptr = strchr(cptr1, '@')) != NULL)
176 *cptr = '\0';
177 pter = pcode = "";
178 if ((cptr = strchr(cptr1, '_')) != NULL) {
179 *cptr++ = '\0';
180 pter = cptr1 = cptr;
181 }
182 if ((cptr = strchr(cptr1, '.')) != NULL) {
183 *cptr++ = '\0';
184 pcode = cptr;
185 }
186
187 if ((nlspath = getenv("NLSPATH")) == NULL || issetugid())
188 nlspath = _DEFAULT_NLS_PATH;
189
190 if ((base = cptr = strdup(nlspath)) == NULL) {
191 saverr = errno;
192 free(plang);
193 errno = saverr;
194 return (NLERR);
195 }
196
197 while ((nlspath = strsep(&cptr, ":")) != NULL) {
198 pathP = path;
199 if (*nlspath) {
200 for (; *nlspath; ++nlspath) {
201 if (*nlspath == '%') {
202 switch (*(nlspath + 1)) {
203 case 'l':
204 tmpptr = plang;
205 break;
206 case 't':
207 tmpptr = pter;
208 break;
209 case 'c':
210 tmpptr = pcode;
211 break;
212 case 'L':
213 tmpptr = lang;
214 break;
215 case 'N':
216 tmpptr = (char *)name;
217 break;
218 case '%':
219 ++nlspath;
220 /* FALLTHROUGH */
221 default:
222 if (pathP - path >=
223 sizeof(path) - 1)
224 goto too_long;
225 *(pathP++) = *nlspath;
226 continue;
227 }
228 ++nlspath;
229 put_tmpptr:
230 spcleft = sizeof(path) -
231 (pathP - path) - 1;
232 if (strlcpy(pathP, tmpptr, spcleft) >=
233 spcleft) {
234 too_long:
235 free(plang);
236 free(base);
237 SAVEFAIL(name, lang, ENAMETOOLONG);
238 NLRETERR(ENAMETOOLONG);
239 }
240 pathP += strlen(tmpptr);
241 } else {
242 if (pathP - path >= sizeof(path) - 1)
243 goto too_long;
244 *(pathP++) = *nlspath;
245 }
246 }
247 *pathP = '\0';
248 if (stat(path, &sbuf) == 0) {
249 free(plang);
250 free(base);
251 return (load_msgcat(path, name, lang));
252 }
253 } else {
254 tmpptr = (char *)name;
255 --nlspath;
256 goto put_tmpptr;
257 }
258 }
259 free(plang);
260 free(base);
261 SAVEFAIL(name, lang, ENOENT);
262 NLRETERR(ENOENT);
263}
264
265char *
266catgets(nl_catd catd, int set_id, int msg_id, const char *s)
267{
268 struct _nls_cat_hdr *cat_hdr;
269 struct _nls_msg_hdr *msg_hdr;
270 struct _nls_set_hdr *set_hdr;
271 int i, l, r, u;
272
273 if (catd == NULL || catd == NLERR) {
274 errno = EBADF;
275 /* LINTED interface problem */
276 return ((char *)s);
277 }
278
279 cat_hdr = (struct _nls_cat_hdr *)catd->__data;
280 set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data +
281 sizeof(struct _nls_cat_hdr));
282
283 /* binary search, see knuth algorithm b */
284 l = 0;
285 u = ntohl((u_int32_t)cat_hdr->__nsets) - 1;
286 while (l <= u) {
287 i = (l + u) / 2;
288 r = set_id - ntohl((u_int32_t)set_hdr[i].__setno);
289
290 if (r == 0) {
291 msg_hdr = (struct _nls_msg_hdr *)
292 (void *)((char *)catd->__data +
293 sizeof(struct _nls_cat_hdr) +
294 ntohl((u_int32_t)cat_hdr->__msg_hdr_offset));
295
296 l = ntohl((u_int32_t)set_hdr[i].__index);
297 u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1;
298 while (l <= u) {
299 i = (l + u) / 2;
300 r = msg_id -
301 ntohl((u_int32_t)msg_hdr[i].__msgno);
302 if (r == 0) {
303 return ((char *) catd->__data +
304 sizeof(struct _nls_cat_hdr) +
305 ntohl((u_int32_t)
306 cat_hdr->__msg_txt_offset) +
307 ntohl((u_int32_t)
308 msg_hdr[i].__offset));
309 } else if (r < 0) {
310 u = i - 1;
311 } else {
312 l = i + 1;
313 }
314 }
315
316 /* not found */
317 goto notfound;
318
319 } else if (r < 0) {
320 u = i - 1;
321 } else {
322 l = i + 1;
323 }
324 }
325
326notfound:
327 /* not found */
328 errno = ENOMSG;
329 /* LINTED interface problem */
330 return ((char *)s);
331}
332
4bbf0fc0 333static void
334catfree(struct catentry *np)
335{
336
337 if (np->catd != NULL && np->catd != NLERR) {
338 munmap(np->catd->__data, (size_t)np->catd->__size);
339 free(np->catd);
340 }
341 SLIST_REMOVE(&cache, np, catentry, list);
342 free(np->name);
343 free(np->path);
344 free(np->lang);
345 free(np);
346}
347
0d5acd74
JM
348int
349catclose(nl_catd catd)
350{
351 struct catentry *np;
352
353 /* sanity checking */
354 if (catd == NULL || catd == NLERR) {
355 errno = EBADF;
356 return (-1);
357 }
358
359 /* Remove from cache if not referenced any more */
360 WLOCK(-1);
361 SLIST_FOREACH(np, &cache, list) {
362 if (catd == np->catd) {
363 np->refcount--;
4bbf0fc0 364 if (np->refcount == 0)
365 catfree(np);
0d5acd74
JM
366 break;
367 }
368 }
369 UNLOCK;
370 return (0);
371}
372
373/*
374 * Internal support functions
375 */
376
377static nl_catd
378load_msgcat(const char *path, const char *name, const char *lang)
379{
380 struct stat st;
381 nl_catd catd;
382 struct catentry *np;
383 void *data;
384 int fd;
385
386 /* path/name will never be NULL here */
387
388 /*
389 * One more try in cache; if it was not found by name,
390 * it might still be found by absolute path.
391 */
392 RLOCK(NLERR);
393 SLIST_FOREACH(np, &cache, list) {
394 if ((np->path != NULL) && (strcmp(np->path, path) == 0)) {
395 np->refcount++;
396 UNLOCK;
397 return (np->catd);
398 }
399 }
400 UNLOCK;
401
402 if ((fd = _open(path, O_RDONLY | O_CLOEXEC)) == -1) {
403 SAVEFAIL(name, lang, errno);
404 NLRETERR(errno);
405 }
406
407 if (_fstat(fd, &st) != 0) {
408 _close(fd);
409 SAVEFAIL(name, lang, EFTYPE);
410 NLRETERR(EFTYPE);
411 }
412
413 /*
414 * If the file size cannot be held in size_t we cannot mmap()
415 * it to the memory. Probably, this will not be a problem given
416 * that catalog files are usually small.
417 */
418 if (st.st_size > SIZE_T_MAX) {
419 _close(fd);
420 SAVEFAIL(name, lang, EFBIG);
421 NLRETERR(EFBIG);
422 }
423
424 if ((data = mmap(0, (size_t)st.st_size, PROT_READ,
425 MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) {
426 int saved_errno = errno;
427 _close(fd);
428 SAVEFAIL(name, lang, saved_errno);
429 NLRETERR(saved_errno);
430 }
431 _close(fd);
432
433 if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) !=
434 _NLS_MAGIC) {
435 munmap(data, (size_t)st.st_size);
436 SAVEFAIL(name, lang, EFTYPE);
437 NLRETERR(EFTYPE);
438 }
439
440 if ((catd = malloc(sizeof (*catd))) == NULL) {
441 munmap(data, (size_t)st.st_size);
442 SAVEFAIL(name, lang, ENOMEM);
443 NLRETERR(ENOMEM);
444 }
445
446 catd->__data = data;
447 catd->__size = (int)st.st_size;
448
449 /* Caching opened catalog */
450 WLOCK(NLERR);
451 if ((np = malloc(sizeof(struct catentry))) != NULL) {
452 np->name = strdup(name);
453 np->path = strdup(path);
454 np->catd = catd;
455 np->lang = (lang == NULL) ? NULL : strdup(lang);
456 np->refcount = 1;
457 np->caterrno = 0;
458 SLIST_INSERT_HEAD(&cache, np, list);
459 }
460 UNLOCK;
461 return (catd);
462}