16eaef618SJordan K. Hubbard /***********************************************************
26eaef618SJordan K. Hubbard Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
3f978ef93SGabor Kovesdan Copyright 2010, Gabor Kovesdan <gabor@FreeBSD.org>
46eaef618SJordan K. Hubbard
56eaef618SJordan K. Hubbard All Rights Reserved
66eaef618SJordan K. Hubbard
76eaef618SJordan K. Hubbard Permission to use, copy, modify, and distribute this software and its
86eaef618SJordan K. Hubbard documentation for any purpose and without fee is hereby granted,
96eaef618SJordan K. Hubbard provided that the above copyright notice appear in all copies and that
106eaef618SJordan K. Hubbard both that copyright notice and this permission notice appear in
116eaef618SJordan K. Hubbard supporting documentation, and that Alfalfa's name not be used in
126eaef618SJordan K. Hubbard advertising or publicity pertaining to distribution of the software
136eaef618SJordan K. Hubbard without specific, written prior permission.
146eaef618SJordan K. Hubbard
156eaef618SJordan K. Hubbard ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
166eaef618SJordan K. Hubbard ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
176eaef618SJordan K. Hubbard ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
186eaef618SJordan K. Hubbard ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
196eaef618SJordan K. Hubbard WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
206eaef618SJordan K. Hubbard ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
216eaef618SJordan K. Hubbard SOFTWARE.
226eaef618SJordan K. Hubbard
236eaef618SJordan K. Hubbard If you make any modifications, bugfixes or other changes to this software
246eaef618SJordan K. Hubbard we'd appreciate it if you could send a copy to us so we can keep things
256eaef618SJordan K. Hubbard up-to-date. Many thanks.
266eaef618SJordan K. Hubbard Kee Hinckley
276eaef618SJordan K. Hubbard Alfalfa Software, Inc.
286eaef618SJordan K. Hubbard 267 Allston St., #3
296eaef618SJordan K. Hubbard Cambridge, MA 02139 USA
306eaef618SJordan K. Hubbard nazgul@alfalfa.com
316eaef618SJordan K. Hubbard
326eaef618SJordan K. Hubbard ******************************************************************/
336eaef618SJordan K. Hubbard
34fd5cf7c0SAlexey Zelkin #define _NLS_PRIVATE
356eaef618SJordan K. Hubbard
36d201fe46SDaniel Eischen #include "namespace.h"
376eaef618SJordan K. Hubbard #include <sys/types.h>
386eaef618SJordan K. Hubbard #include <sys/stat.h>
39fd5cf7c0SAlexey Zelkin #include <sys/mman.h>
40f978ef93SGabor Kovesdan #include <sys/queue.h>
41fd5cf7c0SAlexey Zelkin
42fd5cf7c0SAlexey Zelkin #include <arpa/inet.h> /* for ntohl() */
434188ba1aSXin LI #include <machine/atomic.h>
44688dfe45SGarrett Wollman
45dfb1b7aeSAndrey A. Chernov #include <errno.h>
466eaef618SJordan K. Hubbard #include <fcntl.h>
47688dfe45SGarrett Wollman #include <limits.h>
48a8a87cc6SAlexey Zelkin #include <nl_types.h>
491f474190SStefan Eßer #include <paths.h>
50f978ef93SGabor Kovesdan #include <pthread.h>
516eaef618SJordan K. Hubbard #include <stdio.h>
526eaef618SJordan K. Hubbard #include <stdlib.h>
536eaef618SJordan K. Hubbard #include <string.h>
546eaef618SJordan K. Hubbard #include <unistd.h>
55d201fe46SDaniel Eischen #include "un-namespace.h"
566eaef618SJordan K. Hubbard
5795f97094SAndrey A. Chernov #include "../locale/xlocale_private.h"
58675079b1SKonstantin Belousov #include "libc_private.h"
59a8a87cc6SAlexey Zelkin
601f474190SStefan Eßer #define _DEFAULT_NLS_PATH "/usr/share/nls/%L/%N.cat:/usr/share/nls/%N/%L:" \
611f474190SStefan Eßer _PATH_LOCALBASE "/share/nls/%L/%N.cat:" \
621f474190SStefan Eßer _PATH_LOCALBASE "/share/nls/%N/%L"
6349b8dd0dSAlexey Zelkin
64f978ef93SGabor Kovesdan #define RLOCK(fail) { int ret; \
65f978ef93SGabor Kovesdan if (__isthreaded && \
66f978ef93SGabor Kovesdan ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) { \
67f978ef93SGabor Kovesdan errno = ret; \
68f978ef93SGabor Kovesdan return (fail); \
69f978ef93SGabor Kovesdan }}
70f978ef93SGabor Kovesdan #define WLOCK(fail) { int ret; \
71f978ef93SGabor Kovesdan if (__isthreaded && \
72f978ef93SGabor Kovesdan ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) { \
73f978ef93SGabor Kovesdan errno = ret; \
74f978ef93SGabor Kovesdan return (fail); \
75f978ef93SGabor Kovesdan }}
76f978ef93SGabor Kovesdan #define UNLOCK { if (__isthreaded) \
77f978ef93SGabor Kovesdan _pthread_rwlock_unlock(&rwlock); }
78f978ef93SGabor Kovesdan
796eaef618SJordan K. Hubbard #define NLERR ((nl_catd) -1)
8097c54f77SAndrey A. Chernov #define NLRETERR(errc) { errno = errc; return (NLERR); }
814188ba1aSXin LI #define SAVEFAIL(n, l, e) { np = calloc(1, sizeof(struct catentry)); \
82f978ef93SGabor Kovesdan if (np != NULL) { \
83f978ef93SGabor Kovesdan np->name = strdup(n); \
8449a8f2aaSEitan Adler np->catd = NLERR; \
8529c27159SGabor Kovesdan np->lang = (l == NULL) ? NULL : \
8629c27159SGabor Kovesdan strdup(l); \
87f978ef93SGabor Kovesdan np->caterrno = e; \
884188ba1aSXin LI if (np->name == NULL || \
894188ba1aSXin LI (l != NULL && np->lang == NULL)) { \
904188ba1aSXin LI free(np->name); \
914188ba1aSXin LI free(np->lang); \
924188ba1aSXin LI free(np); \
934188ba1aSXin LI } else { \
944188ba1aSXin LI WLOCK(NLERR); \
954188ba1aSXin LI SLIST_INSERT_HEAD(&cache, np, \
964188ba1aSXin LI list); \
97f978ef93SGabor Kovesdan UNLOCK; \
984188ba1aSXin LI } \
994188ba1aSXin LI } \
100fe0aca73SGabor Kovesdan errno = e; \
101f978ef93SGabor Kovesdan }
1026eaef618SJordan K. Hubbard
103f978ef93SGabor Kovesdan static nl_catd load_msgcat(const char *, const char *, const char *);
104f978ef93SGabor Kovesdan
105fe0aca73SGabor Kovesdan static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
106f978ef93SGabor Kovesdan
107f978ef93SGabor Kovesdan struct catentry {
108f978ef93SGabor Kovesdan SLIST_ENTRY(catentry) list;
109f978ef93SGabor Kovesdan char *name;
110f978ef93SGabor Kovesdan char *path;
111f978ef93SGabor Kovesdan int caterrno;
112f978ef93SGabor Kovesdan nl_catd catd;
113f978ef93SGabor Kovesdan char *lang;
114f978ef93SGabor Kovesdan int refcount;
115f978ef93SGabor Kovesdan };
116f978ef93SGabor Kovesdan
117f978ef93SGabor Kovesdan SLIST_HEAD(listhead, catentry) cache =
118f978ef93SGabor Kovesdan SLIST_HEAD_INITIALIZER(cache);
1196eaef618SJordan K. Hubbard
12049b8dd0dSAlexey Zelkin nl_catd
catopen(const char * name,int type)121fd5cf7c0SAlexey Zelkin catopen(const char *name, int type)
1226eaef618SJordan K. Hubbard {
123675079b1SKonstantin Belousov return (__catopen_l(name, type, __get_locale()));
124675079b1SKonstantin Belousov }
125675079b1SKonstantin Belousov
126675079b1SKonstantin Belousov nl_catd
__catopen_l(const char * name,int type,locale_t locale)127675079b1SKonstantin Belousov __catopen_l(const char *name, int type, locale_t locale)
128675079b1SKonstantin Belousov {
1296eaef618SJordan K. Hubbard struct stat sbuf;
130f978ef93SGabor Kovesdan struct catentry *np;
13195f97094SAndrey A. Chernov char *base, *cptr, *cptr1, *nlspath, *pathP, *pcode;
13295f97094SAndrey A. Chernov char *plang, *pter;
133336831f0SGabor Kovesdan int saverr, spcleft;
13495f97094SAndrey A. Chernov const char *lang, *tmpptr;
135336831f0SGabor Kovesdan char path[PATH_MAX];
1366eaef618SJordan K. Hubbard
137fe0aca73SGabor Kovesdan /* sanity checking */
13897c54f77SAndrey A. Chernov if (name == NULL || *name == '\0')
139*1176390dSKonstantin Belousov NLRETERR(ENOENT);
1406eaef618SJordan K. Hubbard
14197c54f77SAndrey A. Chernov if (strchr(name, '/') != NULL)
142fe0aca73SGabor Kovesdan /* have a pathname */
143f978ef93SGabor Kovesdan lang = NULL;
144f978ef93SGabor Kovesdan else {
145c8970622SAndrey A. Chernov if (type == NL_CAT_LOCALE)
146675079b1SKonstantin Belousov lang = querylocale(LC_MESSAGES_MASK, locale);
147460b9262SAndrey A. Chernov else
148460b9262SAndrey A. Chernov lang = getenv("LANG");
14997c54f77SAndrey A. Chernov
15097c54f77SAndrey A. Chernov if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
15197c54f77SAndrey A. Chernov (lang[0] == '.' &&
15297c54f77SAndrey A. Chernov (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
15397c54f77SAndrey A. Chernov strchr(lang, '/') != NULL)
154016de69dSAndrey A. Chernov lang = "C";
155f978ef93SGabor Kovesdan }
156f978ef93SGabor Kovesdan
157f978ef93SGabor Kovesdan /* Try to get it from the cache first */
158f978ef93SGabor Kovesdan RLOCK(NLERR);
159f978ef93SGabor Kovesdan SLIST_FOREACH(np, &cache, list) {
160fe0aca73SGabor Kovesdan if ((strcmp(np->name, name) == 0) &&
161fe0aca73SGabor Kovesdan ((lang != NULL && np->lang != NULL &&
162fe0aca73SGabor Kovesdan strcmp(np->lang, lang) == 0) || (np->lang == lang))) {
163f978ef93SGabor Kovesdan if (np->caterrno != 0) {
164f978ef93SGabor Kovesdan /* Found cached failing entry */
165f978ef93SGabor Kovesdan UNLOCK;
166f978ef93SGabor Kovesdan NLRETERR(np->caterrno);
167fe0aca73SGabor Kovesdan } else {
168f978ef93SGabor Kovesdan /* Found cached successful entry */
1694188ba1aSXin LI atomic_add_int(&np->refcount, 1);
170f978ef93SGabor Kovesdan UNLOCK;
171f978ef93SGabor Kovesdan return (np->catd);
172f978ef93SGabor Kovesdan }
173f978ef93SGabor Kovesdan }
174f978ef93SGabor Kovesdan }
175f978ef93SGabor Kovesdan UNLOCK;
176f978ef93SGabor Kovesdan
177f978ef93SGabor Kovesdan /* is it absolute path ? if yes, load immediately */
178f978ef93SGabor Kovesdan if (strchr(name, '/') != NULL)
179f978ef93SGabor Kovesdan return (load_msgcat(name, name, lang));
180f3ed9135SAndrey A. Chernov
181fe0aca73SGabor Kovesdan /* sanity checking */
1827e3c1a3cSAndrey A. Chernov if ((plang = cptr1 = strdup(lang)) == NULL)
183f3ed9135SAndrey A. Chernov return (NLERR);
184f3ed9135SAndrey A. Chernov if ((cptr = strchr(cptr1, '@')) != NULL)
185f3ed9135SAndrey A. Chernov *cptr = '\0';
186f3ed9135SAndrey A. Chernov pter = pcode = "";
187f3ed9135SAndrey A. Chernov if ((cptr = strchr(cptr1, '_')) != NULL) {
188f3ed9135SAndrey A. Chernov *cptr++ = '\0';
189f3ed9135SAndrey A. Chernov pter = cptr1 = cptr;
190f3ed9135SAndrey A. Chernov }
191f3ed9135SAndrey A. Chernov if ((cptr = strchr(cptr1, '.')) != NULL) {
192f3ed9135SAndrey A. Chernov *cptr++ = '\0';
193f3ed9135SAndrey A. Chernov pcode = cptr;
194f3ed9135SAndrey A. Chernov }
195f3ed9135SAndrey A. Chernov
19668ca8363SMark Johnston if ((nlspath = secure_getenv("NLSPATH")) == NULL)
19749b8dd0dSAlexey Zelkin nlspath = _DEFAULT_NLS_PATH;
1986eaef618SJordan K. Hubbard
199f3ed9135SAndrey A. Chernov if ((base = cptr = strdup(nlspath)) == NULL) {
2007e3c1a3cSAndrey A. Chernov saverr = errno;
201f3ed9135SAndrey A. Chernov free(plang);
2027e3c1a3cSAndrey A. Chernov errno = saverr;
203f3ed9135SAndrey A. Chernov return (NLERR);
204f3ed9135SAndrey A. Chernov }
2056eaef618SJordan K. Hubbard
206f3ed9135SAndrey A. Chernov while ((nlspath = strsep(&cptr, ":")) != NULL) {
207f3ed9135SAndrey A. Chernov pathP = path;
208f3ed9135SAndrey A. Chernov if (*nlspath) {
209f3ed9135SAndrey A. Chernov for (; *nlspath; ++nlspath) {
2106eaef618SJordan K. Hubbard if (*nlspath == '%') {
21149b8dd0dSAlexey Zelkin switch (*(nlspath + 1)) {
212f3ed9135SAndrey A. Chernov case 'l':
213f3ed9135SAndrey A. Chernov tmpptr = plang;
214f3ed9135SAndrey A. Chernov break;
215f3ed9135SAndrey A. Chernov case 't':
216f3ed9135SAndrey A. Chernov tmpptr = pter;
217f3ed9135SAndrey A. Chernov break;
218f3ed9135SAndrey A. Chernov case 'c':
219f3ed9135SAndrey A. Chernov tmpptr = pcode;
220f3ed9135SAndrey A. Chernov break;
22149b8dd0dSAlexey Zelkin case 'L':
22249b8dd0dSAlexey Zelkin tmpptr = lang;
22349b8dd0dSAlexey Zelkin break;
22449b8dd0dSAlexey Zelkin case 'N':
22549b8dd0dSAlexey Zelkin tmpptr = (char *)name;
22649b8dd0dSAlexey Zelkin break;
227f3ed9135SAndrey A. Chernov case '%':
228f3ed9135SAndrey A. Chernov ++nlspath;
22929c27159SGabor Kovesdan /* FALLTHROUGH */
23049b8dd0dSAlexey Zelkin default:
23197c54f77SAndrey A. Chernov if (pathP - path >=
23297c54f77SAndrey A. Chernov sizeof(path) - 1)
23386797bf9SAndrey A. Chernov goto too_long;
23449b8dd0dSAlexey Zelkin *(pathP++) = *nlspath;
23549b8dd0dSAlexey Zelkin continue;
23684b578d5SKris Kennaway }
2376eaef618SJordan K. Hubbard ++nlspath;
238f3ed9135SAndrey A. Chernov put_tmpptr:
23997c54f77SAndrey A. Chernov spcleft = sizeof(path) -
24097c54f77SAndrey A. Chernov (pathP - path) - 1;
241d0509082SJacques Vidrine if (strlcpy(pathP, tmpptr, spcleft) >=
24297c54f77SAndrey A. Chernov spcleft) {
24386797bf9SAndrey A. Chernov too_long:
244f3ed9135SAndrey A. Chernov free(plang);
245a367b970SAlexey Zelkin free(base);
246fe0aca73SGabor Kovesdan SAVEFAIL(name, lang, ENAMETOOLONG);
24749b8dd0dSAlexey Zelkin NLRETERR(ENAMETOOLONG);
24884b578d5SKris Kennaway }
24949b8dd0dSAlexey Zelkin pathP += strlen(tmpptr);
25086797bf9SAndrey A. Chernov } else {
25186797bf9SAndrey A. Chernov if (pathP - path >= sizeof(path) - 1)
25286797bf9SAndrey A. Chernov goto too_long;
25349b8dd0dSAlexey Zelkin *(pathP++) = *nlspath;
2546eaef618SJordan K. Hubbard }
25586797bf9SAndrey A. Chernov }
2566eaef618SJordan K. Hubbard *pathP = '\0';
2576eaef618SJordan K. Hubbard if (stat(path, &sbuf) == 0) {
258f3ed9135SAndrey A. Chernov free(plang);
25949b8dd0dSAlexey Zelkin free(base);
260f978ef93SGabor Kovesdan return (load_msgcat(path, name, lang));
2616eaef618SJordan K. Hubbard }
262f3ed9135SAndrey A. Chernov } else {
263f3ed9135SAndrey A. Chernov tmpptr = (char *)name;
264f3ed9135SAndrey A. Chernov --nlspath;
265f3ed9135SAndrey A. Chernov goto put_tmpptr;
2666eaef618SJordan K. Hubbard }
2676eaef618SJordan K. Hubbard }
268f3ed9135SAndrey A. Chernov free(plang);
2696eaef618SJordan K. Hubbard free(base);
270fe0aca73SGabor Kovesdan SAVEFAIL(name, lang, ENOENT);
27149b8dd0dSAlexey Zelkin NLRETERR(ENOENT);
2726eaef618SJordan K. Hubbard }
2736eaef618SJordan K. Hubbard
27449b8dd0dSAlexey Zelkin char *
catgets(nl_catd catd,int set_id,int msg_id,const char * s)275fd5cf7c0SAlexey Zelkin catgets(nl_catd catd, int set_id, int msg_id, const char *s)
2766eaef618SJordan K. Hubbard {
277fd5cf7c0SAlexey Zelkin struct _nls_cat_hdr *cat_hdr;
278fd5cf7c0SAlexey Zelkin struct _nls_msg_hdr *msg_hdr;
279336831f0SGabor Kovesdan struct _nls_set_hdr *set_hdr;
280336831f0SGabor Kovesdan int i, l, r, u;
2816eaef618SJordan K. Hubbard
282fd5cf7c0SAlexey Zelkin if (catd == NULL || catd == NLERR) {
283fd5cf7c0SAlexey Zelkin errno = EBADF;
284fd5cf7c0SAlexey Zelkin /* LINTED interface problem */
285f978ef93SGabor Kovesdan return ((char *)s);
286fd5cf7c0SAlexey Zelkin }
287fd5cf7c0SAlexey Zelkin
288fd5cf7c0SAlexey Zelkin cat_hdr = (struct _nls_cat_hdr *)catd->__data;
289f978ef93SGabor Kovesdan set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data +
290f978ef93SGabor Kovesdan sizeof(struct _nls_cat_hdr));
291fd5cf7c0SAlexey Zelkin
292fd5cf7c0SAlexey Zelkin /* binary search, see knuth algorithm b */
293fd5cf7c0SAlexey Zelkin l = 0;
294fd5cf7c0SAlexey Zelkin u = ntohl((u_int32_t)cat_hdr->__nsets) - 1;
295fd5cf7c0SAlexey Zelkin while (l <= u) {
296fd5cf7c0SAlexey Zelkin i = (l + u) / 2;
297fd5cf7c0SAlexey Zelkin r = set_id - ntohl((u_int32_t)set_hdr[i].__setno);
298fd5cf7c0SAlexey Zelkin
299fd5cf7c0SAlexey Zelkin if (r == 0) {
300fd5cf7c0SAlexey Zelkin msg_hdr = (struct _nls_msg_hdr *)
301fd5cf7c0SAlexey Zelkin (void *)((char *)catd->__data +
302fd5cf7c0SAlexey Zelkin sizeof(struct _nls_cat_hdr) +
303fd5cf7c0SAlexey Zelkin ntohl((u_int32_t)cat_hdr->__msg_hdr_offset));
304fd5cf7c0SAlexey Zelkin
305fd5cf7c0SAlexey Zelkin l = ntohl((u_int32_t)set_hdr[i].__index);
306fd5cf7c0SAlexey Zelkin u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1;
307fd5cf7c0SAlexey Zelkin while (l <= u) {
308fd5cf7c0SAlexey Zelkin i = (l + u) / 2;
309fd5cf7c0SAlexey Zelkin r = msg_id -
310fd5cf7c0SAlexey Zelkin ntohl((u_int32_t)msg_hdr[i].__msgno);
311fd5cf7c0SAlexey Zelkin if (r == 0) {
312fd5cf7c0SAlexey Zelkin return ((char *) catd->__data +
313fd5cf7c0SAlexey Zelkin sizeof(struct _nls_cat_hdr) +
314fd5cf7c0SAlexey Zelkin ntohl((u_int32_t)
315fd5cf7c0SAlexey Zelkin cat_hdr->__msg_txt_offset) +
316fd5cf7c0SAlexey Zelkin ntohl((u_int32_t)
317fd5cf7c0SAlexey Zelkin msg_hdr[i].__offset));
318fd5cf7c0SAlexey Zelkin } else if (r < 0) {
319fd5cf7c0SAlexey Zelkin u = i - 1;
320fd5cf7c0SAlexey Zelkin } else {
321fd5cf7c0SAlexey Zelkin l = i + 1;
322fd5cf7c0SAlexey Zelkin }
323fd5cf7c0SAlexey Zelkin }
324fd5cf7c0SAlexey Zelkin
325fd5cf7c0SAlexey Zelkin /* not found */
326fd5cf7c0SAlexey Zelkin goto notfound;
327fd5cf7c0SAlexey Zelkin
328fd5cf7c0SAlexey Zelkin } else if (r < 0) {
329fd5cf7c0SAlexey Zelkin u = i - 1;
330fd5cf7c0SAlexey Zelkin } else {
331fd5cf7c0SAlexey Zelkin l = i + 1;
332fd5cf7c0SAlexey Zelkin }
333fd5cf7c0SAlexey Zelkin }
334fd5cf7c0SAlexey Zelkin
335fd5cf7c0SAlexey Zelkin notfound:
336fd5cf7c0SAlexey Zelkin /* not found */
337fd5cf7c0SAlexey Zelkin errno = ENOMSG;
338fd5cf7c0SAlexey Zelkin /* LINTED interface problem */
339f978ef93SGabor Kovesdan return ((char *)s);
3406eaef618SJordan K. Hubbard }
3416eaef618SJordan K. Hubbard
342b231dc0aSBryan Drewery static void
catfree(struct catentry * np)343b231dc0aSBryan Drewery catfree(struct catentry *np)
344b231dc0aSBryan Drewery {
345b231dc0aSBryan Drewery
346b231dc0aSBryan Drewery if (np->catd != NULL && np->catd != NLERR) {
347b231dc0aSBryan Drewery munmap(np->catd->__data, (size_t)np->catd->__size);
348b231dc0aSBryan Drewery free(np->catd);
349b231dc0aSBryan Drewery }
350b231dc0aSBryan Drewery SLIST_REMOVE(&cache, np, catentry, list);
351b231dc0aSBryan Drewery free(np->name);
352b231dc0aSBryan Drewery free(np->path);
353b231dc0aSBryan Drewery free(np->lang);
354b231dc0aSBryan Drewery free(np);
355b231dc0aSBryan Drewery }
356b231dc0aSBryan Drewery
35749b8dd0dSAlexey Zelkin int
catclose(nl_catd catd)358f24d446aSAlexey Zelkin catclose(nl_catd catd)
3596eaef618SJordan K. Hubbard {
360f978ef93SGabor Kovesdan struct catentry *np;
361f978ef93SGabor Kovesdan
362fe0aca73SGabor Kovesdan /* sanity checking */
363dfb1b7aeSAndrey A. Chernov if (catd == NULL || catd == NLERR) {
36497c54f77SAndrey A. Chernov errno = EBADF;
36597c54f77SAndrey A. Chernov return (-1);
366dfb1b7aeSAndrey A. Chernov }
367f24d446aSAlexey Zelkin
368f978ef93SGabor Kovesdan /* Remove from cache if not referenced any more */
369f978ef93SGabor Kovesdan WLOCK(-1);
370f978ef93SGabor Kovesdan SLIST_FOREACH(np, &cache, list) {
371fe0aca73SGabor Kovesdan if (catd == np->catd) {
3724188ba1aSXin LI if (atomic_fetchadd_int(&np->refcount, -1) == 1)
373b231dc0aSBryan Drewery catfree(np);
374f978ef93SGabor Kovesdan break;
375f978ef93SGabor Kovesdan }
376f978ef93SGabor Kovesdan }
377f978ef93SGabor Kovesdan UNLOCK;
37849b8dd0dSAlexey Zelkin return (0);
3796eaef618SJordan K. Hubbard }
3806eaef618SJordan K. Hubbard
3816eaef618SJordan K. Hubbard /*
382fd5cf7c0SAlexey Zelkin * Internal support functions
3836eaef618SJordan K. Hubbard */
3846eaef618SJordan K. Hubbard
38549b8dd0dSAlexey Zelkin static nl_catd
load_msgcat(const char * path,const char * name,const char * lang)386f978ef93SGabor Kovesdan load_msgcat(const char *path, const char *name, const char *lang)
3876eaef618SJordan K. Hubbard {
388fd5cf7c0SAlexey Zelkin struct stat st;
389fd5cf7c0SAlexey Zelkin nl_catd catd;
390f978ef93SGabor Kovesdan struct catentry *np;
391fd5cf7c0SAlexey Zelkin void *data;
3924188ba1aSXin LI char *copy_path, *copy_name, *copy_lang;
393*1176390dSKonstantin Belousov int fd, saved_errno;
3946eaef618SJordan K. Hubbard
395f978ef93SGabor Kovesdan /* path/name will never be NULL here */
396fd5cf7c0SAlexey Zelkin
39729c27159SGabor Kovesdan /*
39829c27159SGabor Kovesdan * One more try in cache; if it was not found by name,
399fe0aca73SGabor Kovesdan * it might still be found by absolute path.
400fe0aca73SGabor Kovesdan */
401f978ef93SGabor Kovesdan RLOCK(NLERR);
402f978ef93SGabor Kovesdan SLIST_FOREACH(np, &cache, list) {
403fe0aca73SGabor Kovesdan if ((np->path != NULL) && (strcmp(np->path, path) == 0)) {
4044188ba1aSXin LI atomic_add_int(&np->refcount, 1);
405f978ef93SGabor Kovesdan UNLOCK;
406f978ef93SGabor Kovesdan return (np->catd);
407f978ef93SGabor Kovesdan }
408f978ef93SGabor Kovesdan }
409f978ef93SGabor Kovesdan UNLOCK;
410f978ef93SGabor Kovesdan
41105eb11cbSJilles Tjoelker if ((fd = _open(path, O_RDONLY | O_CLOEXEC)) == -1) {
412fe0aca73SGabor Kovesdan SAVEFAIL(name, lang, errno);
413fe0aca73SGabor Kovesdan NLRETERR(errno);
414f978ef93SGabor Kovesdan }
4156eaef618SJordan K. Hubbard
416fd5cf7c0SAlexey Zelkin if (_fstat(fd, &st) != 0) {
417*1176390dSKonstantin Belousov saved_errno = errno;
418fd5cf7c0SAlexey Zelkin _close(fd);
419*1176390dSKonstantin Belousov SAVEFAIL(name, lang, saved_errno);
420*1176390dSKonstantin Belousov NLRETERR(saved_errno);
421*1176390dSKonstantin Belousov }
422*1176390dSKonstantin Belousov
423*1176390dSKonstantin Belousov /* The file is too small to contain a _NLS_MAGIC. */
424*1176390dSKonstantin Belousov if (st.st_size < sizeof(u_int32_t)) {
425*1176390dSKonstantin Belousov _close(fd);
426*1176390dSKonstantin Belousov SAVEFAIL(name, lang, ENOENT);
427*1176390dSKonstantin Belousov NLRETERR(ENOENT);
4286eaef618SJordan K. Hubbard }
4296eaef618SJordan K. Hubbard
43029c27159SGabor Kovesdan /*
43129c27159SGabor Kovesdan * If the file size cannot be held in size_t we cannot mmap()
432fe0aca73SGabor Kovesdan * it to the memory. Probably, this will not be a problem given
433fe0aca73SGabor Kovesdan * that catalog files are usually small.
434fe0aca73SGabor Kovesdan */
435fe0aca73SGabor Kovesdan if (st.st_size > SIZE_T_MAX) {
436fd5cf7c0SAlexey Zelkin _close(fd);
437*1176390dSKonstantin Belousov SAVEFAIL(name, lang, ENOENT);
438*1176390dSKonstantin Belousov NLRETERR(ENOENT);
439f978ef93SGabor Kovesdan }
440fd5cf7c0SAlexey Zelkin
441*1176390dSKonstantin Belousov data = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
442*1176390dSKonstantin Belousov if (data == MAP_FAILED) {
443*1176390dSKonstantin Belousov saved_errno = errno;
444fe0aca73SGabor Kovesdan _close(fd);
445fe0aca73SGabor Kovesdan SAVEFAIL(name, lang, saved_errno);
446fe0aca73SGabor Kovesdan NLRETERR(saved_errno);
447fe0aca73SGabor Kovesdan }
448fe0aca73SGabor Kovesdan _close(fd);
449fe0aca73SGabor Kovesdan
450fd5cf7c0SAlexey Zelkin if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) !=
451fd5cf7c0SAlexey Zelkin _NLS_MAGIC) {
452fd5cf7c0SAlexey Zelkin munmap(data, (size_t)st.st_size);
453*1176390dSKonstantin Belousov SAVEFAIL(name, lang, ENOENT);
454*1176390dSKonstantin Belousov NLRETERR(ENOENT);
4556eaef618SJordan K. Hubbard }
4566eaef618SJordan K. Hubbard
4574188ba1aSXin LI copy_name = strdup(name);
4584188ba1aSXin LI copy_path = strdup(path);
4594188ba1aSXin LI copy_lang = (lang == NULL) ? NULL : strdup(lang);
4604188ba1aSXin LI catd = malloc(sizeof (*catd));
4614188ba1aSXin LI np = calloc(1, sizeof(struct catentry));
4624188ba1aSXin LI
4634188ba1aSXin LI if (copy_name == NULL || copy_path == NULL ||
4644188ba1aSXin LI (lang != NULL && copy_lang == NULL) ||
4654188ba1aSXin LI catd == NULL || np == NULL) {
4664188ba1aSXin LI free(copy_name);
4674188ba1aSXin LI free(copy_path);
4684188ba1aSXin LI free(copy_lang);
4694188ba1aSXin LI free(catd);
4704188ba1aSXin LI free(np);
471fd5cf7c0SAlexey Zelkin munmap(data, (size_t)st.st_size);
472fe0aca73SGabor Kovesdan SAVEFAIL(name, lang, ENOMEM);
473fe0aca73SGabor Kovesdan NLRETERR(ENOMEM);
47494658e74SAndrey A. Chernov }
4756eaef618SJordan K. Hubbard
476fd5cf7c0SAlexey Zelkin catd->__data = data;
477fd5cf7c0SAlexey Zelkin catd->__size = (int)st.st_size;
478f978ef93SGabor Kovesdan
479f978ef93SGabor Kovesdan /* Caching opened catalog */
4804188ba1aSXin LI np->name = copy_name;
4814188ba1aSXin LI np->path = copy_path;
482f978ef93SGabor Kovesdan np->catd = catd;
4834188ba1aSXin LI np->lang = copy_lang;
4844188ba1aSXin LI atomic_store_int(&np->refcount, 1);
4854188ba1aSXin LI WLOCK(NLERR);
486f978ef93SGabor Kovesdan SLIST_INSERT_HEAD(&cache, np, list);
487f978ef93SGabor Kovesdan UNLOCK;
488fd5cf7c0SAlexey Zelkin return (catd);
48994658e74SAndrey A. Chernov }
490