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 ******************************************************************/
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
39 #include "namespace.h"
40 #include <sys/types.h>
43 #include <sys/queue.h>
45 #include <arpa/inet.h> /* for ntohl() */
57 #include "un-namespace.h"
59 #include "../locale/setlocale.h" /* for ENCODING_LEN */
61 #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"
63 #define RLOCK(fail) { int ret; \
65 ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) { \
69 #define WLOCK(fail) { int ret; \
71 ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) { \
75 #define UNLOCK { if (__isthreaded) \
76 _pthread_rwlock_unlock(&rwlock); }
78 #define NLERR ((nl_catd) -1)
79 #define NLRETERR(errc) { errno = errc; return (NLERR); }
80 #define SAVEFAIL(n, l, e) { WLOCK(NLERR); \
81 np = malloc(sizeof(struct catentry)); \
83 np->name = strdup(n); \
87 np->lang = (l == NULL) ? NULL : \
90 SLIST_INSERT_HEAD(&cache, np, list); \
96 static nl_catd load_msgcat(const char *, const char *, const char *);
98 static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
101 SLIST_ENTRY(catentry) list;
110 SLIST_HEAD(listhead, catentry) cache =
111 SLIST_HEAD_INITIALIZER(cache);
114 catopen(const char *name, int type)
118 char *base, *cptr, *cptr1, *lang, *nlspath, *pathP, *pcode;
119 char *plang, *pter, *tmpptr;
123 /* sanity checking */
124 if (name == NULL || *name == '\0')
127 if (strchr(name, '/') != NULL)
128 /* have a pathname */
131 if (type == NL_CAT_LOCALE)
132 lang = setlocale(LC_MESSAGES, NULL);
134 lang = getenv("LANG");
136 if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
138 (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
139 strchr(lang, '/') != NULL)
143 /* Try to get it from the cache first */
145 SLIST_FOREACH(np, &cache, list) {
146 if ((strcmp(np->name, name) == 0) &&
147 ((lang != NULL && np->lang != NULL &&
148 strcmp(np->lang, lang) == 0) || (np->lang == lang))) {
149 if (np->caterrno != 0) {
150 /* Found cached failing entry */
152 NLRETERR(np->caterrno);
154 /* Found cached successful entry */
163 /* is it absolute path ? if yes, load immediately */
164 if (strchr(name, '/') != NULL)
165 return (load_msgcat(name, name, lang));
167 /* sanity checking */
168 if ((plang = cptr1 = strdup(lang)) == NULL)
170 if ((cptr = strchr(cptr1, '@')) != NULL)
173 if ((cptr = strchr(cptr1, '_')) != NULL) {
177 if ((cptr = strchr(cptr1, '.')) != NULL) {
182 if ((nlspath = getenv("NLSPATH")) == NULL || issetugid())
183 nlspath = _DEFAULT_NLS_PATH;
185 if ((base = cptr = strdup(nlspath)) == NULL) {
192 while ((nlspath = strsep(&cptr, ":")) != NULL) {
195 for (; *nlspath; ++nlspath) {
196 if (*nlspath == '%') {
197 switch (*(nlspath + 1)) {
211 tmpptr = (char *)name;
220 *(pathP++) = *nlspath;
225 spcleft = sizeof(path) -
227 if (strlcpy(pathP, tmpptr, spcleft) >=
232 SAVEFAIL(name, lang, ENAMETOOLONG);
233 NLRETERR(ENAMETOOLONG);
235 pathP += strlen(tmpptr);
237 if (pathP - path >= sizeof(path) - 1)
239 *(pathP++) = *nlspath;
243 if (stat(path, &sbuf) == 0) {
246 return (load_msgcat(path, name, lang));
249 tmpptr = (char *)name;
256 SAVEFAIL(name, lang, ENOENT);
261 catgets(nl_catd catd, int set_id, int msg_id, const char *s)
263 struct _nls_cat_hdr *cat_hdr;
264 struct _nls_msg_hdr *msg_hdr;
265 struct _nls_set_hdr *set_hdr;
268 if (catd == NULL || catd == NLERR) {
270 /* LINTED interface problem */
274 cat_hdr = (struct _nls_cat_hdr *)catd->__data;
275 set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data +
276 sizeof(struct _nls_cat_hdr));
278 /* binary search, see knuth algorithm b */
280 u = ntohl((u_int32_t)cat_hdr->__nsets) - 1;
283 r = set_id - ntohl((u_int32_t)set_hdr[i].__setno);
286 msg_hdr = (struct _nls_msg_hdr *)
287 (void *)((char *)catd->__data +
288 sizeof(struct _nls_cat_hdr) +
289 ntohl((u_int32_t)cat_hdr->__msg_hdr_offset));
291 l = ntohl((u_int32_t)set_hdr[i].__index);
292 u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1;
296 ntohl((u_int32_t)msg_hdr[i].__msgno);
298 return ((char *) catd->__data +
299 sizeof(struct _nls_cat_hdr) +
301 cat_hdr->__msg_txt_offset) +
303 msg_hdr[i].__offset));
324 /* LINTED interface problem */
329 catclose(nl_catd catd)
333 /* sanity checking */
334 if (catd == NULL || catd == NLERR) {
339 /* Remove from cache if not referenced any more */
341 SLIST_FOREACH(np, &cache, list) {
342 if (catd == np->catd) {
344 if (np->refcount == 0) {
345 munmap(catd->__data, (size_t)catd->__size);
347 SLIST_REMOVE(&cache, np, catentry, list);
361 * Internal support functions
365 load_msgcat(const char *path, const char *name, const char *lang)
373 /* path/name will never be NULL here */
376 * One more try in cache; if it was not found by name,
377 * it might still be found by absolute path.
380 SLIST_FOREACH(np, &cache, list) {
381 if ((np->path != NULL) && (strcmp(np->path, path) == 0)) {
389 if ((fd = _open(path, O_RDONLY | O_CLOEXEC)) == -1) {
390 SAVEFAIL(name, lang, errno);
394 if (_fstat(fd, &st) != 0) {
396 SAVEFAIL(name, lang, EFTYPE);
401 * If the file size cannot be held in size_t we cannot mmap()
402 * it to the memory. Probably, this will not be a problem given
403 * that catalog files are usually small.
405 if (st.st_size > SIZE_T_MAX) {
407 SAVEFAIL(name, lang, EFBIG);
411 if ((data = mmap(0, (size_t)st.st_size, PROT_READ,
412 MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) {
413 int saved_errno = errno;
415 SAVEFAIL(name, lang, saved_errno);
416 NLRETERR(saved_errno);
420 if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) !=
422 munmap(data, (size_t)st.st_size);
423 SAVEFAIL(name, lang, EFTYPE);
427 if ((catd = malloc(sizeof (*catd))) == NULL) {
428 munmap(data, (size_t)st.st_size);
429 SAVEFAIL(name, lang, ENOMEM);
434 catd->__size = (int)st.st_size;
436 /* Caching opened catalog */
438 if ((np = malloc(sizeof(struct catentry))) != NULL) {
439 np->name = strdup(name);
440 np->path = strdup(path);
442 np->lang = (lang == NULL) ? NULL : strdup(lang);
445 SLIST_INSERT_HEAD(&cache, np, list);