xref: /freebsd/lib/libc/nls/msgcat.c (revision 1176390d2d2bbb1e207c840d1f7a66a6ac1096ff)
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