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 $FreeBSD: head/lib/libc/nls/msgcat.c 304755 2016-08-24 16:44:27Z ache $
33 ******************************************************************/
37 #include "namespace.h"
38 #include <sys/types.h>
41 #include <sys/queue.h>
43 #include <arpa/inet.h> /* for ntohl() */
54 #include "un-namespace.h"
56 #include "../locale/xlocale_private.h"
57 #include "libc_private.h"
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"
61 #define RLOCK(fail) { int ret; \
63 ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) { \
67 #define WLOCK(fail) { int ret; \
69 ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) { \
73 #define UNLOCK { if (__isthreaded) \
74 _pthread_rwlock_unlock(&rwlock); }
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)); \
81 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)
114 return (__catopen_l(name, type, __get_locale()));
118 __catopen_l(const char *name, int type, locale_t locale)
122 char *base, *cptr, *cptr1, *nlspath, *pathP, *pcode;
125 const char *lang, *tmpptr;
128 /* sanity checking */
129 if (name == NULL || *name == '\0')
132 if (strchr(name, '/') != NULL)
133 /* have a pathname */
136 if (type == NL_CAT_LOCALE)
137 lang = querylocale(LC_MESSAGES_MASK, locale);
139 lang = getenv("LANG");
141 if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
143 (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
144 strchr(lang, '/') != NULL)
148 /* Try to get it from the cache first */
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 */
157 NLRETERR(np->caterrno);
159 /* Found cached successful entry */
168 /* is it absolute path ? if yes, load immediately */
169 if (strchr(name, '/') != NULL)
170 return (load_msgcat(name, name, lang));
172 /* sanity checking */
173 if ((plang = cptr1 = strdup(lang)) == NULL)
175 if ((cptr = strchr(cptr1, '@')) != NULL)
178 if ((cptr = strchr(cptr1, '_')) != NULL) {
182 if ((cptr = strchr(cptr1, '.')) != NULL) {
187 if ((nlspath = getenv("NLSPATH")) == NULL || issetugid())
188 nlspath = _DEFAULT_NLS_PATH;
190 if ((base = cptr = strdup(nlspath)) == NULL) {
197 while ((nlspath = strsep(&cptr, ":")) != NULL) {
200 for (; *nlspath; ++nlspath) {
201 if (*nlspath == '%') {
202 switch (*(nlspath + 1)) {
216 tmpptr = (char *)name;
225 *(pathP++) = *nlspath;
230 spcleft = sizeof(path) -
232 if (strlcpy(pathP, tmpptr, spcleft) >=
237 SAVEFAIL(name, lang, ENAMETOOLONG);
238 NLRETERR(ENAMETOOLONG);
240 pathP += strlen(tmpptr);
242 if (pathP - path >= sizeof(path) - 1)
244 *(pathP++) = *nlspath;
248 if (stat(path, &sbuf) == 0) {
251 return (load_msgcat(path, name, lang));
254 tmpptr = (char *)name;
261 SAVEFAIL(name, lang, ENOENT);
266 catgets(nl_catd catd, int set_id, int msg_id, const char *s)
268 struct _nls_cat_hdr *cat_hdr;
269 struct _nls_msg_hdr *msg_hdr;
270 struct _nls_set_hdr *set_hdr;
273 if (catd == NULL || catd == NLERR) {
275 /* LINTED interface problem */
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));
283 /* binary search, see knuth algorithm b */
285 u = ntohl((u_int32_t)cat_hdr->__nsets) - 1;
288 r = set_id - ntohl((u_int32_t)set_hdr[i].__setno);
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));
296 l = ntohl((u_int32_t)set_hdr[i].__index);
297 u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1;
301 ntohl((u_int32_t)msg_hdr[i].__msgno);
303 return ((char *) catd->__data +
304 sizeof(struct _nls_cat_hdr) +
306 cat_hdr->__msg_txt_offset) +
308 msg_hdr[i].__offset));
329 /* LINTED interface problem */
334 catfree(struct catentry *np)
337 if (np->catd != NULL && np->catd != NLERR) {
338 munmap(np->catd->__data, (size_t)np->catd->__size);
341 SLIST_REMOVE(&cache, np, catentry, list);
349 catclose(nl_catd catd)
353 /* sanity checking */
354 if (catd == NULL || catd == NLERR) {
359 /* Remove from cache if not referenced any more */
361 SLIST_FOREACH(np, &cache, list) {
362 if (catd == np->catd) {
364 if (np->refcount == 0)
374 * Internal support functions
378 load_msgcat(const char *path, const char *name, const char *lang)
386 /* path/name will never be NULL here */
389 * One more try in cache; if it was not found by name,
390 * it might still be found by absolute path.
393 SLIST_FOREACH(np, &cache, list) {
394 if ((np->path != NULL) && (strcmp(np->path, path) == 0)) {
402 if ((fd = _open(path, O_RDONLY | O_CLOEXEC)) == -1) {
403 SAVEFAIL(name, lang, errno);
407 if (_fstat(fd, &st) != 0) {
409 SAVEFAIL(name, lang, EFTYPE);
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.
418 if (st.st_size > SIZE_T_MAX) {
420 SAVEFAIL(name, lang, EFBIG);
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;
428 SAVEFAIL(name, lang, saved_errno);
429 NLRETERR(saved_errno);
433 if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) !=
435 munmap(data, (size_t)st.st_size);
436 SAVEFAIL(name, lang, EFTYPE);
440 if ((catd = malloc(sizeof (*catd))) == NULL) {
441 munmap(data, (size_t)st.st_size);
442 SAVEFAIL(name, lang, ENOMEM);
447 catd->__size = (int)st.st_size;
449 /* Caching opened catalog */
451 if ((np = malloc(sizeof(struct catentry))) != NULL) {
452 np->name = strdup(name);
453 np->path = strdup(path);
455 np->lang = (lang == NULL) ? NULL : strdup(lang);
458 SLIST_INSERT_HEAD(&cache, np, list);