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); \
85 np->lang = (l == NULL) ? NULL : \
88 SLIST_INSERT_HEAD(&cache, np, list); \
94 static nl_catd load_msgcat(const char *, const char *, const char *);
96 static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
99 SLIST_ENTRY(catentry) list;
108 SLIST_HEAD(listhead, catentry) cache =
109 SLIST_HEAD_INITIALIZER(cache);
112 catopen(const char *name, int type)
116 char *base, *cptr, *cptr1, *lang, *nlspath, *pathP, *pcode;
117 char *plang, *pter, *tmpptr;
121 /* sanity checking */
122 if (name == NULL || *name == '\0')
125 if (strchr(name, '/') != NULL)
126 /* have a pathname */
129 if (type == NL_CAT_LOCALE)
130 lang = setlocale(LC_MESSAGES, NULL);
132 lang = getenv("LANG");
134 if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
136 (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
137 strchr(lang, '/') != NULL)
141 /* Try to get it from the cache first */
143 SLIST_FOREACH(np, &cache, list) {
144 if ((strcmp(np->name, name) == 0) &&
145 ((lang != NULL && np->lang != NULL &&
146 strcmp(np->lang, lang) == 0) || (np->lang == lang))) {
147 if (np->caterrno != 0) {
148 /* Found cached failing entry */
150 NLRETERR(np->caterrno);
152 /* Found cached successful entry */
161 /* is it absolute path ? if yes, load immediately */
162 if (strchr(name, '/') != NULL)
163 return (load_msgcat(name, name, lang));
165 /* sanity checking */
166 if ((plang = cptr1 = strdup(lang)) == NULL)
168 if ((cptr = strchr(cptr1, '@')) != NULL)
171 if ((cptr = strchr(cptr1, '_')) != NULL) {
175 if ((cptr = strchr(cptr1, '.')) != NULL) {
180 if ((nlspath = getenv("NLSPATH")) == NULL || issetugid())
181 nlspath = _DEFAULT_NLS_PATH;
183 if ((base = cptr = strdup(nlspath)) == NULL) {
190 while ((nlspath = strsep(&cptr, ":")) != NULL) {
193 for (; *nlspath; ++nlspath) {
194 if (*nlspath == '%') {
195 switch (*(nlspath + 1)) {
209 tmpptr = (char *)name;
218 *(pathP++) = *nlspath;
223 spcleft = sizeof(path) -
225 if (strlcpy(pathP, tmpptr, spcleft) >=
230 SAVEFAIL(name, lang, ENAMETOOLONG);
231 NLRETERR(ENAMETOOLONG);
233 pathP += strlen(tmpptr);
235 if (pathP - path >= sizeof(path) - 1)
237 *(pathP++) = *nlspath;
241 if (stat(path, &sbuf) == 0) {
244 return (load_msgcat(path, name, lang));
247 tmpptr = (char *)name;
254 SAVEFAIL(name, lang, ENOENT);
259 catgets(nl_catd catd, int set_id, int msg_id, const char *s)
261 struct _nls_cat_hdr *cat_hdr;
262 struct _nls_msg_hdr *msg_hdr;
263 struct _nls_set_hdr *set_hdr;
266 if (catd == NULL || catd == NLERR) {
268 /* LINTED interface problem */
272 cat_hdr = (struct _nls_cat_hdr *)catd->__data;
273 set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data +
274 sizeof(struct _nls_cat_hdr));
276 /* binary search, see knuth algorithm b */
278 u = ntohl((u_int32_t)cat_hdr->__nsets) - 1;
281 r = set_id - ntohl((u_int32_t)set_hdr[i].__setno);
284 msg_hdr = (struct _nls_msg_hdr *)
285 (void *)((char *)catd->__data +
286 sizeof(struct _nls_cat_hdr) +
287 ntohl((u_int32_t)cat_hdr->__msg_hdr_offset));
289 l = ntohl((u_int32_t)set_hdr[i].__index);
290 u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1;
294 ntohl((u_int32_t)msg_hdr[i].__msgno);
296 return ((char *) catd->__data +
297 sizeof(struct _nls_cat_hdr) +
299 cat_hdr->__msg_txt_offset) +
301 msg_hdr[i].__offset));
322 /* LINTED interface problem */
327 catclose(nl_catd catd)
331 /* sanity checking */
332 if (catd == NULL || catd == NLERR) {
337 /* Remove from cache if not referenced any more */
339 SLIST_FOREACH(np, &cache, list) {
340 if (catd == np->catd) {
342 if (np->refcount == 0) {
343 munmap(catd->__data, (size_t)catd->__size);
345 SLIST_REMOVE(&cache, np, catentry, list);
359 * Internal support functions
363 load_msgcat(const char *path, const char *name, const char *lang)
371 /* path/name will never be NULL here */
374 * One more try in cache; if it was not found by name,
375 * it might still be found by absolute path.
378 SLIST_FOREACH(np, &cache, list) {
379 if ((np->path != NULL) && (strcmp(np->path, path) == 0)) {
387 if ((fd = _open(path, O_RDONLY)) == -1) {
388 SAVEFAIL(name, lang, errno);
392 if (_fstat(fd, &st) != 0) {
394 SAVEFAIL(name, lang, EFTYPE);
399 * If the file size cannot be held in size_t we cannot mmap()
400 * it to the memory. Probably, this will not be a problem given
401 * that catalog files are usually small.
403 if (st.st_size > SIZE_T_MAX) {
405 SAVEFAIL(name, lang, EFBIG);
409 if ((data = mmap(0, (size_t)st.st_size, PROT_READ,
410 MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) {
411 int saved_errno = errno;
413 SAVEFAIL(name, lang, saved_errno);
414 NLRETERR(saved_errno);
418 if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) !=
420 munmap(data, (size_t)st.st_size);
421 SAVEFAIL(name, lang, EFTYPE);
425 if ((catd = malloc(sizeof (*catd))) == NULL) {
426 munmap(data, (size_t)st.st_size);
427 SAVEFAIL(name, lang, ENOMEM);
432 catd->__size = (int)st.st_size;
434 /* Caching opened catalog */
436 if ((np = malloc(sizeof(struct catentry))) != NULL) {
437 np->name = strdup(name);
438 np->path = strdup(path);
440 np->lang = (lang == NULL) ? NULL : strdup(lang);
443 SLIST_INSERT_HEAD(&cache, np, list);