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); \
86 np->lang = (l == NULL) ? NULL : \
89 SLIST_INSERT_HEAD(&cache, np, list); \
95 static nl_catd load_msgcat(const char *, const char *, const char *);
97 static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
100 SLIST_ENTRY(catentry) list;
109 SLIST_HEAD(listhead, catentry) cache =
110 SLIST_HEAD_INITIALIZER(cache);
113 catopen(const char *name, int type)
117 char *base, *cptr, *cptr1, *lang, *nlspath, *pathP, *pcode;
118 char *plang, *pter, *tmpptr;
122 /* sanity checking */
123 if (name == NULL || *name == '\0')
126 if (strchr(name, '/') != NULL)
127 /* have a pathname */
130 if (type == NL_CAT_LOCALE)
131 lang = setlocale(LC_MESSAGES, NULL);
133 lang = getenv("LANG");
135 if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
137 (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
138 strchr(lang, '/') != NULL)
142 /* Try to get it from the cache first */
144 SLIST_FOREACH(np, &cache, list) {
145 if ((strcmp(np->name, name) == 0) &&
146 ((lang != NULL && np->lang != NULL &&
147 strcmp(np->lang, lang) == 0) || (np->lang == lang))) {
148 if (np->caterrno != 0) {
149 /* Found cached failing entry */
151 NLRETERR(np->caterrno);
153 /* Found cached successful entry */
162 /* is it absolute path ? if yes, load immediately */
163 if (strchr(name, '/') != NULL)
164 return (load_msgcat(name, name, lang));
166 /* sanity checking */
167 if ((plang = cptr1 = strdup(lang)) == NULL)
169 if ((cptr = strchr(cptr1, '@')) != NULL)
172 if ((cptr = strchr(cptr1, '_')) != NULL) {
176 if ((cptr = strchr(cptr1, '.')) != NULL) {
181 if ((nlspath = getenv("NLSPATH")) == NULL || issetugid())
182 nlspath = _DEFAULT_NLS_PATH;
184 if ((base = cptr = strdup(nlspath)) == NULL) {
191 while ((nlspath = strsep(&cptr, ":")) != NULL) {
194 for (; *nlspath; ++nlspath) {
195 if (*nlspath == '%') {
196 switch (*(nlspath + 1)) {
210 tmpptr = (char *)name;
219 *(pathP++) = *nlspath;
224 spcleft = sizeof(path) -
226 if (strlcpy(pathP, tmpptr, spcleft) >=
231 SAVEFAIL(name, lang, ENAMETOOLONG);
232 NLRETERR(ENAMETOOLONG);
234 pathP += strlen(tmpptr);
236 if (pathP - path >= sizeof(path) - 1)
238 *(pathP++) = *nlspath;
242 if (stat(path, &sbuf) == 0) {
245 return (load_msgcat(path, name, lang));
248 tmpptr = (char *)name;
255 SAVEFAIL(name, lang, ENOENT);
260 catgets(nl_catd catd, int set_id, int msg_id, const char *s)
262 struct _nls_cat_hdr *cat_hdr;
263 struct _nls_msg_hdr *msg_hdr;
264 struct _nls_set_hdr *set_hdr;
267 if (catd == NULL || catd == NLERR) {
269 /* LINTED interface problem */
273 cat_hdr = (struct _nls_cat_hdr *)catd->__data;
274 set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data +
275 sizeof(struct _nls_cat_hdr));
277 /* binary search, see knuth algorithm b */
279 u = ntohl((u_int32_t)cat_hdr->__nsets) - 1;
282 r = set_id - ntohl((u_int32_t)set_hdr[i].__setno);
285 msg_hdr = (struct _nls_msg_hdr *)
286 (void *)((char *)catd->__data +
287 sizeof(struct _nls_cat_hdr) +
288 ntohl((u_int32_t)cat_hdr->__msg_hdr_offset));
290 l = ntohl((u_int32_t)set_hdr[i].__index);
291 u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1;
295 ntohl((u_int32_t)msg_hdr[i].__msgno);
297 return ((char *) catd->__data +
298 sizeof(struct _nls_cat_hdr) +
300 cat_hdr->__msg_txt_offset) +
302 msg_hdr[i].__offset));
323 /* LINTED interface problem */
328 catclose(nl_catd catd)
332 /* sanity checking */
333 if (catd == NULL || catd == NLERR) {
338 /* Remove from cache if not referenced any more */
340 SLIST_FOREACH(np, &cache, list) {
341 if (catd == np->catd) {
343 if (np->refcount == 0) {
344 munmap(catd->__data, (size_t)catd->__size);
346 SLIST_REMOVE(&cache, np, catentry, list);
360 * Internal support functions
364 load_msgcat(const char *path, const char *name, const char *lang)
372 /* path/name will never be NULL here */
375 * One more try in cache; if it was not found by name,
376 * it might still be found by absolute path.
379 SLIST_FOREACH(np, &cache, list) {
380 if ((np->path != NULL) && (strcmp(np->path, path) == 0)) {
388 if ((fd = _open(path, O_RDONLY)) == -1) {
389 SAVEFAIL(name, lang, errno);
393 if (_fstat(fd, &st) != 0) {
395 SAVEFAIL(name, lang, EFTYPE);
400 * If the file size cannot be held in size_t we cannot mmap()
401 * it to the memory. Probably, this will not be a problem given
402 * that catalog files are usually small.
404 if (st.st_size > SIZE_T_MAX) {
406 SAVEFAIL(name, lang, EFBIG);
410 if ((data = mmap(0, (size_t)st.st_size, PROT_READ,
411 MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) {
412 int saved_errno = errno;
414 SAVEFAIL(name, lang, saved_errno);
415 NLRETERR(saved_errno);
419 if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) !=
421 munmap(data, (size_t)st.st_size);
422 SAVEFAIL(name, lang, EFTYPE);
426 if ((catd = malloc(sizeof (*catd))) == NULL) {
427 munmap(data, (size_t)st.st_size);
428 SAVEFAIL(name, lang, ENOMEM);
433 catd->__size = (int)st.st_size;
435 /* Caching opened catalog */
437 if ((np = malloc(sizeof(struct catentry))) != NULL) {
438 np->name = strdup(name);
439 np->path = strdup(path);
441 np->lang = (lang == NULL) ? NULL : strdup(lang);
444 SLIST_INSERT_HEAD(&cache, np, list);