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"
58 #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"
60 #define RLOCK(fail) { int ret; \
62 ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) { \
66 #define WLOCK(fail) { int ret; \
68 ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) { \
72 #define UNLOCK { if (__isthreaded) \
73 _pthread_rwlock_unlock(&rwlock); }
75 #define NLERR ((nl_catd) -1)
76 #define NLRETERR(errc) { errno = errc; return (NLERR); }
77 #define SAVEFAIL(n, l, e) { WLOCK(NLERR); \
78 np = malloc(sizeof(struct catentry)); \
80 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
, *nlspath
, *pathP
, *pcode
;
118 const char *lang
, *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
= querylocale(LC_MESSAGES_MASK
, __get_locale());
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 catfree(struct catentry
*np
)
330 if (np
->catd
!= NULL
&& np
->catd
!= NLERR
) {
331 munmap(np
->catd
->__data
, (size_t)np
->catd
->__size
);
334 SLIST_REMOVE(&cache
, np
, catentry
, list
);
342 catclose(nl_catd catd
)
346 /* sanity checking */
347 if (catd
== NULL
|| catd
== NLERR
) {
352 /* Remove from cache if not referenced any more */
354 SLIST_FOREACH(np
, &cache
, list
) {
355 if (catd
== np
->catd
) {
357 if (np
->refcount
== 0)
367 * Internal support functions
371 load_msgcat(const char *path
, const char *name
, const char *lang
)
379 /* path/name will never be NULL here */
382 * One more try in cache; if it was not found by name,
383 * it might still be found by absolute path.
386 SLIST_FOREACH(np
, &cache
, list
) {
387 if ((np
->path
!= NULL
) && (strcmp(np
->path
, path
) == 0)) {
395 if ((fd
= _open(path
, O_RDONLY
| O_CLOEXEC
)) == -1) {
396 SAVEFAIL(name
, lang
, errno
);
400 if (_fstat(fd
, &st
) != 0) {
402 SAVEFAIL(name
, lang
, EFTYPE
);
407 * If the file size cannot be held in size_t we cannot mmap()
408 * it to the memory. Probably, this will not be a problem given
409 * that catalog files are usually small.
411 if (st
.st_size
> SIZE_T_MAX
) {
413 SAVEFAIL(name
, lang
, EFBIG
);
417 if ((data
= mmap(0, (size_t)st
.st_size
, PROT_READ
,
418 MAP_FILE
|MAP_SHARED
, fd
, (off_t
)0)) == MAP_FAILED
) {
419 int saved_errno
= errno
;
421 SAVEFAIL(name
, lang
, saved_errno
);
422 NLRETERR(saved_errno
);
426 if (ntohl((u_int32_t
)((struct _nls_cat_hdr
*)data
)->__magic
) !=
428 munmap(data
, (size_t)st
.st_size
);
429 SAVEFAIL(name
, lang
, EFTYPE
);
433 if ((catd
= malloc(sizeof (*catd
))) == NULL
) {
434 munmap(data
, (size_t)st
.st_size
);
435 SAVEFAIL(name
, lang
, ENOMEM
);
440 catd
->__size
= (int)st
.st_size
;
442 /* Caching opened catalog */
444 if ((np
= malloc(sizeof(struct catentry
))) != NULL
) {
445 np
->name
= strdup(name
);
446 np
->path
= strdup(path
);
448 np
->lang
= (lang
== NULL
) ? NULL
: strdup(lang
);
451 SLIST_INSERT_HEAD(&cache
, np
, list
);