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 244358 2012-12-17 12:57:36Z eadler $
33 ******************************************************************/
37 #include "namespace.h"
38 #include <sys/types.h>
41 #include <sys/queue.h>
43 #include <arpa/inet.h> /* for ntohl() */
55 #include "un-namespace.h"
57 #include "../locale/setlocale.h" /* for ENCODING_LEN */
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); \
84 np->lang = (l == NULL) ? NULL : \
87 SLIST_INSERT_HEAD(&cache, np, list); \
93 static nl_catd
load_msgcat(const char *, const char *, const char *);
95 static pthread_rwlock_t rwlock
= PTHREAD_RWLOCK_INITIALIZER
;
98 SLIST_ENTRY(catentry
) list
;
107 SLIST_HEAD(listhead
, catentry
) cache
=
108 SLIST_HEAD_INITIALIZER(cache
);
111 catopen(const char *name
, int type
)
115 char *base
, *cptr
, *cptr1
, *lang
, *nlspath
, *pathP
, *pcode
;
116 char *plang
, *pter
, *tmpptr
;
120 /* sanity checking */
121 if (name
== NULL
|| *name
== '\0')
124 if (strchr(name
, '/') != NULL
)
125 /* have a pathname */
128 if (type
== NL_CAT_LOCALE
)
129 lang
= setlocale(LC_MESSAGES
, NULL
);
131 lang
= getenv("LANG");
133 if (lang
== NULL
|| *lang
== '\0' || strlen(lang
) > ENCODING_LEN
||
135 (lang
[1] == '\0' || (lang
[1] == '.' && lang
[2] == '\0'))) ||
136 strchr(lang
, '/') != NULL
)
140 /* Try to get it from the cache first */
142 SLIST_FOREACH(np
, &cache
, list
) {
143 if ((strcmp(np
->name
, name
) == 0) &&
144 ((lang
!= NULL
&& np
->lang
!= NULL
&&
145 strcmp(np
->lang
, lang
) == 0) || (np
->lang
== lang
))) {
146 if (np
->caterrno
!= 0) {
147 /* Found cached failing entry */
149 NLRETERR(np
->caterrno
);
151 /* Found cached successful entry */
160 /* is it absolute path ? if yes, load immediately */
161 if (strchr(name
, '/') != NULL
)
162 return (load_msgcat(name
, name
, lang
));
164 /* sanity checking */
165 if ((plang
= cptr1
= strdup(lang
)) == NULL
)
167 if ((cptr
= strchr(cptr1
, '@')) != NULL
)
170 if ((cptr
= strchr(cptr1
, '_')) != NULL
) {
174 if ((cptr
= strchr(cptr1
, '.')) != NULL
) {
179 if ((nlspath
= getenv("NLSPATH")) == NULL
|| issetugid())
180 nlspath
= _DEFAULT_NLS_PATH
;
182 if ((base
= cptr
= strdup(nlspath
)) == NULL
) {
189 while ((nlspath
= strsep(&cptr
, ":")) != NULL
) {
192 for (; *nlspath
; ++nlspath
) {
193 if (*nlspath
== '%') {
194 switch (*(nlspath
+ 1)) {
208 tmpptr
= (char *)name
;
217 *(pathP
++) = *nlspath
;
222 spcleft
= sizeof(path
) -
224 if (strlcpy(pathP
, tmpptr
, spcleft
) >=
229 SAVEFAIL(name
, lang
, ENAMETOOLONG
);
230 NLRETERR(ENAMETOOLONG
);
232 pathP
+= strlen(tmpptr
);
234 if (pathP
- path
>= sizeof(path
) - 1)
236 *(pathP
++) = *nlspath
;
240 if (stat(path
, &sbuf
) == 0) {
243 return (load_msgcat(path
, name
, lang
));
246 tmpptr
= (char *)name
;
253 SAVEFAIL(name
, lang
, ENOENT
);
258 catgets(nl_catd catd
, int set_id
, int msg_id
, const char *s
)
260 struct _nls_cat_hdr
*cat_hdr
;
261 struct _nls_msg_hdr
*msg_hdr
;
262 struct _nls_set_hdr
*set_hdr
;
265 if (catd
== NULL
|| catd
== NLERR
) {
267 /* LINTED interface problem */
271 cat_hdr
= (struct _nls_cat_hdr
*)catd
->__data
;
272 set_hdr
= (struct _nls_set_hdr
*)(void *)((char *)catd
->__data
+
273 sizeof(struct _nls_cat_hdr
));
275 /* binary search, see knuth algorithm b */
277 u
= ntohl((u_int32_t
)cat_hdr
->__nsets
) - 1;
280 r
= set_id
- ntohl((u_int32_t
)set_hdr
[i
].__setno
);
283 msg_hdr
= (struct _nls_msg_hdr
*)
284 (void *)((char *)catd
->__data
+
285 sizeof(struct _nls_cat_hdr
) +
286 ntohl((u_int32_t
)cat_hdr
->__msg_hdr_offset
));
288 l
= ntohl((u_int32_t
)set_hdr
[i
].__index
);
289 u
= l
+ ntohl((u_int32_t
)set_hdr
[i
].__nmsgs
) - 1;
293 ntohl((u_int32_t
)msg_hdr
[i
].__msgno
);
295 return ((char *) catd
->__data
+
296 sizeof(struct _nls_cat_hdr
) +
298 cat_hdr
->__msg_txt_offset
) +
300 msg_hdr
[i
].__offset
));
321 /* LINTED interface problem */
326 catclose(nl_catd catd
)
330 /* sanity checking */
331 if (catd
== NULL
|| catd
== NLERR
) {
336 /* Remove from cache if not referenced any more */
338 SLIST_FOREACH(np
, &cache
, list
) {
339 if (catd
== np
->catd
) {
341 if (np
->refcount
== 0) {
342 munmap(catd
->__data
, (size_t)catd
->__size
);
344 SLIST_REMOVE(&cache
, np
, catentry
, list
);
358 * Internal support functions
362 load_msgcat(const char *path
, const char *name
, const char *lang
)
370 /* path/name will never be NULL here */
373 * One more try in cache; if it was not found by name,
374 * it might still be found by absolute path.
377 SLIST_FOREACH(np
, &cache
, list
) {
378 if ((np
->path
!= NULL
) && (strcmp(np
->path
, path
) == 0)) {
386 if ((fd
= _open(path
, O_RDONLY
| O_CLOEXEC
)) == -1) {
387 SAVEFAIL(name
, lang
, errno
);
391 if (_fstat(fd
, &st
) != 0) {
393 SAVEFAIL(name
, lang
, EFTYPE
);
398 * If the file size cannot be held in size_t we cannot mmap()
399 * it to the memory. Probably, this will not be a problem given
400 * that catalog files are usually small.
402 if (st
.st_size
> SIZE_T_MAX
) {
404 SAVEFAIL(name
, lang
, EFBIG
);
408 if ((data
= mmap(0, (size_t)st
.st_size
, PROT_READ
,
409 MAP_FILE
|MAP_SHARED
, fd
, (off_t
)0)) == MAP_FAILED
) {
410 int saved_errno
= errno
;
412 SAVEFAIL(name
, lang
, saved_errno
);
413 NLRETERR(saved_errno
);
417 if (ntohl((u_int32_t
)((struct _nls_cat_hdr
*)data
)->__magic
) !=
419 munmap(data
, (size_t)st
.st_size
);
420 SAVEFAIL(name
, lang
, EFTYPE
);
424 if ((catd
= malloc(sizeof (*catd
))) == NULL
) {
425 munmap(data
, (size_t)st
.st_size
);
426 SAVEFAIL(name
, lang
, ENOMEM
);
431 catd
->__size
= (int)st
.st_size
;
433 /* Caching opened catalog */
435 if ((np
= malloc(sizeof(struct catentry
))) != NULL
) {
436 np
->name
= strdup(name
);
437 np
->path
= strdup(path
);
439 np
->lang
= (lang
== NULL
) ? NULL
: strdup(lang
);
442 SLIST_INSERT_HEAD(&cache
, np
, list
);