1 /***********************************************************
2 Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
3 Copyright 2010, Gabor Kovesdan <gabor@FreeBSD.org>
7 Permission to use, copy, modify, and distribute this software and its
8 documentation for any purpose and without fee is hereby granted,
9 provided that the above copyright notice appear in all copies and that
10 both that copyright notice and this permission notice appear in
11 supporting documentation, and that Alfalfa's name not be used in
12 advertising or publicity pertaining to distribution of the software
13 without specific, written prior permission.
15 ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
16 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
17 ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
18 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
19 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
20 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
23 If you make any modifications, bugfixes or other changes to this software
24 we'd appreciate it if you could send a copy to us so we can keep things
25 up-to-date. Many thanks.
27 Alfalfa Software, Inc.
29 Cambridge, MA 02139 USA
32 ******************************************************************/
36 #include "namespace.h"
37 #include <sys/types.h>
40 #include <sys/queue.h>
42 #include <arpa/inet.h> /* for ntohl() */
43 #include <machine/atomic.h>
55 #include "un-namespace.h"
57 #include "../locale/xlocale_private.h"
58 #include "libc_private.h"
60 #define _DEFAULT_NLS_PATH "/usr/share/nls/%L/%N.cat:/usr/share/nls/%N/%L:" \
61 _PATH_LOCALBASE "/share/nls/%L/%N.cat:" \
62 _PATH_LOCALBASE "/share/nls/%N/%L"
64 #define RLOCK(fail) { int ret; \
66 ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) { \
70 #define WLOCK(fail) { int ret; \
72 ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) { \
76 #define UNLOCK { if (__isthreaded) \
77 _pthread_rwlock_unlock(&rwlock); }
79 #define NLERR ((nl_catd) -1)
80 #define NLRETERR(errc) { errno = errc; return (NLERR); }
81 #define SAVEFAIL(n, l, e) { np = calloc(1, sizeof(struct catentry)); \
83 np->name = strdup(n); \
85 np->lang = (l == NULL) ? NULL : \
88 if (np->name == NULL || \
89 (l != NULL && np->lang == NULL)) { \
95 SLIST_INSERT_HEAD(&cache, np, \
103 static nl_catd load_msgcat(const char *, const char *, const char *);
105 static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
108 SLIST_ENTRY(catentry) list;
117 SLIST_HEAD(listhead, catentry) cache =
118 SLIST_HEAD_INITIALIZER(cache);
121 catopen(const char *name, int type)
123 return (__catopen_l(name, type, __get_locale()));
127 __catopen_l(const char *name, int type, locale_t locale)
131 char *base, *cptr, *cptr1, *nlspath, *pathP, *pcode;
134 const char *lang, *tmpptr;
137 /* sanity checking */
138 if (name == NULL || *name == '\0')
141 if (strchr(name, '/') != NULL)
142 /* have a pathname */
145 if (type == NL_CAT_LOCALE)
146 lang = querylocale(LC_MESSAGES_MASK, locale);
148 lang = getenv("LANG");
150 if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
152 (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
153 strchr(lang, '/') != NULL)
157 /* Try to get it from the cache first */
159 SLIST_FOREACH(np, &cache, list) {
160 if ((strcmp(np->name, name) == 0) &&
161 ((lang != NULL && np->lang != NULL &&
162 strcmp(np->lang, lang) == 0) || (np->lang == lang))) {
163 if (np->caterrno != 0) {
164 /* Found cached failing entry */
166 NLRETERR(np->caterrno);
168 /* Found cached successful entry */
169 atomic_add_int(&np->refcount, 1);
177 /* is it absolute path ? if yes, load immediately */
178 if (strchr(name, '/') != NULL)
179 return (load_msgcat(name, name, lang));
181 /* sanity checking */
182 if ((plang = cptr1 = strdup(lang)) == NULL)
184 if ((cptr = strchr(cptr1, '@')) != NULL)
187 if ((cptr = strchr(cptr1, '_')) != NULL) {
191 if ((cptr = strchr(cptr1, '.')) != NULL) {
196 if ((nlspath = secure_getenv("NLSPATH")) == NULL)
197 nlspath = _DEFAULT_NLS_PATH;
199 if ((base = cptr = strdup(nlspath)) == NULL) {
206 while ((nlspath = strsep(&cptr, ":")) != NULL) {
209 for (; *nlspath; ++nlspath) {
210 if (*nlspath == '%') {
211 switch (*(nlspath + 1)) {
225 tmpptr = (char *)name;
234 *(pathP++) = *nlspath;
239 spcleft = sizeof(path) -
241 if (strlcpy(pathP, tmpptr, spcleft) >=
246 SAVEFAIL(name, lang, ENAMETOOLONG);
247 NLRETERR(ENAMETOOLONG);
249 pathP += strlen(tmpptr);
251 if (pathP - path >= sizeof(path) - 1)
253 *(pathP++) = *nlspath;
257 if (stat(path, &sbuf) == 0) {
260 return (load_msgcat(path, name, lang));
263 tmpptr = (char *)name;
270 SAVEFAIL(name, lang, ENOENT);
275 catgets(nl_catd catd, int set_id, int msg_id, const char *s)
277 struct _nls_cat_hdr *cat_hdr;
278 struct _nls_msg_hdr *msg_hdr;
279 struct _nls_set_hdr *set_hdr;
282 if (catd == NULL || catd == NLERR) {
284 /* LINTED interface problem */
288 cat_hdr = (struct _nls_cat_hdr *)catd->__data;
289 set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data +
290 sizeof(struct _nls_cat_hdr));
292 /* binary search, see knuth algorithm b */
294 u = ntohl((u_int32_t)cat_hdr->__nsets) - 1;
297 r = set_id - ntohl((u_int32_t)set_hdr[i].__setno);
300 msg_hdr = (struct _nls_msg_hdr *)
301 (void *)((char *)catd->__data +
302 sizeof(struct _nls_cat_hdr) +
303 ntohl((u_int32_t)cat_hdr->__msg_hdr_offset));
305 l = ntohl((u_int32_t)set_hdr[i].__index);
306 u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1;
310 ntohl((u_int32_t)msg_hdr[i].__msgno);
312 return ((char *) catd->__data +
313 sizeof(struct _nls_cat_hdr) +
315 cat_hdr->__msg_txt_offset) +
317 msg_hdr[i].__offset));
338 /* LINTED interface problem */
343 catfree(struct catentry *np)
346 if (np->catd != NULL && np->catd != NLERR) {
347 munmap(np->catd->__data, (size_t)np->catd->__size);
350 SLIST_REMOVE(&cache, np, catentry, list);
358 catclose(nl_catd catd)
362 /* sanity checking */
363 if (catd == NULL || catd == NLERR) {
368 /* Remove from cache if not referenced any more */
370 SLIST_FOREACH(np, &cache, list) {
371 if (catd == np->catd) {
372 if (atomic_fetchadd_int(&np->refcount, -1) == 1)
382 * Internal support functions
386 load_msgcat(const char *path, const char *name, const char *lang)
392 char *copy_path, *copy_name, *copy_lang;
395 /* path/name will never be NULL here */
398 * One more try in cache; if it was not found by name,
399 * it might still be found by absolute path.
402 SLIST_FOREACH(np, &cache, list) {
403 if ((np->path != NULL) && (strcmp(np->path, path) == 0)) {
404 atomic_add_int(&np->refcount, 1);
411 if ((fd = _open(path, O_RDONLY | O_CLOEXEC)) == -1) {
412 SAVEFAIL(name, lang, errno);
416 if (_fstat(fd, &st) != 0) {
418 SAVEFAIL(name, lang, EFTYPE);
423 * If the file size cannot be held in size_t we cannot mmap()
424 * it to the memory. Probably, this will not be a problem given
425 * that catalog files are usually small.
427 if (st.st_size > SIZE_T_MAX) {
429 SAVEFAIL(name, lang, EFBIG);
433 if ((data = mmap(0, (size_t)st.st_size, PROT_READ,
434 MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) {
435 int saved_errno = errno;
437 SAVEFAIL(name, lang, saved_errno);
438 NLRETERR(saved_errno);
442 if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) !=
444 munmap(data, (size_t)st.st_size);
445 SAVEFAIL(name, lang, EFTYPE);
449 copy_name = strdup(name);
450 copy_path = strdup(path);
451 copy_lang = (lang == NULL) ? NULL : strdup(lang);
452 catd = malloc(sizeof (*catd));
453 np = calloc(1, sizeof(struct catentry));
455 if (copy_name == NULL || copy_path == NULL ||
456 (lang != NULL && copy_lang == NULL) ||
457 catd == NULL || np == NULL) {
463 munmap(data, (size_t)st.st_size);
464 SAVEFAIL(name, lang, ENOMEM);
469 catd->__size = (int)st.st_size;
471 /* Caching opened catalog */
472 np->name = copy_name;
473 np->path = copy_path;
475 np->lang = copy_lang;
476 atomic_store_int(&np->refcount, 1);
478 SLIST_INSERT_HEAD(&cache, np, list);