xref: /dflybsd-src/lib/libc/nls/msgcat.c (revision 8ad5bb66841bad6639afa5a7a92e2ce2bb173f17)
10d5acd74SJohn Marino /***********************************************************
20d5acd74SJohn Marino Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts.
30d5acd74SJohn Marino Copyright 2010, Gabor Kovesdan <gabor@FreeBSD.org>
40d5acd74SJohn Marino 
50d5acd74SJohn Marino                         All Rights Reserved
60d5acd74SJohn Marino 
70d5acd74SJohn Marino Permission to use, copy, modify, and distribute this software and its
80d5acd74SJohn Marino documentation for any purpose and without fee is hereby granted,
90d5acd74SJohn Marino provided that the above copyright notice appear in all copies and that
100d5acd74SJohn Marino both that copyright notice and this permission notice appear in
110d5acd74SJohn Marino supporting documentation, and that Alfalfa's name not be used in
120d5acd74SJohn Marino advertising or publicity pertaining to distribution of the software
130d5acd74SJohn Marino without specific, written prior permission.
140d5acd74SJohn Marino 
150d5acd74SJohn Marino ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
160d5acd74SJohn Marino ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
170d5acd74SJohn Marino ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
180d5acd74SJohn Marino ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
190d5acd74SJohn Marino WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
200d5acd74SJohn Marino ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
210d5acd74SJohn Marino SOFTWARE.
220d5acd74SJohn Marino 
230d5acd74SJohn Marino If you make any modifications, bugfixes or other changes to this software
240d5acd74SJohn Marino we'd appreciate it if you could send a copy to us so we can keep things
250d5acd74SJohn Marino up-to-date.  Many thanks.
260d5acd74SJohn Marino 				Kee Hinckley
270d5acd74SJohn Marino 				Alfalfa Software, Inc.
280d5acd74SJohn Marino 				267 Allston St., #3
290d5acd74SJohn Marino 				Cambridge, MA 02139  USA
300d5acd74SJohn Marino 				nazgul@alfalfa.com
310d5acd74SJohn Marino 
324bbf0fc0Szrj $FreeBSD: head/lib/libc/nls/msgcat.c 304755 2016-08-24 16:44:27Z ache $
330d5acd74SJohn Marino ******************************************************************/
340d5acd74SJohn Marino 
350d5acd74SJohn Marino #define _NLS_PRIVATE
360d5acd74SJohn Marino 
370d5acd74SJohn Marino #include "namespace.h"
380d5acd74SJohn Marino #include <sys/types.h>
390d5acd74SJohn Marino #include <sys/stat.h>
400d5acd74SJohn Marino #include <sys/mman.h>
410d5acd74SJohn Marino #include <sys/queue.h>
420d5acd74SJohn Marino 
430d5acd74SJohn Marino #include <arpa/inet.h>		/* for ntohl() */
440d5acd74SJohn Marino 
450d5acd74SJohn Marino #include <errno.h>
460d5acd74SJohn Marino #include <fcntl.h>
470d5acd74SJohn Marino #include <limits.h>
480d5acd74SJohn Marino #include <nl_types.h>
490d5acd74SJohn Marino #include <pthread.h>
500d5acd74SJohn Marino #include <stdio.h>
510d5acd74SJohn Marino #include <stdlib.h>
520d5acd74SJohn Marino #include <string.h>
530d5acd74SJohn Marino #include <unistd.h>
540d5acd74SJohn Marino #include "un-namespace.h"
550d5acd74SJohn Marino 
5621a0e97dSSascha Wildner #include "../locale/xlocale_private.h"
57*8ad5bb66SSascha Wildner #include "libc_private.h"
580d5acd74SJohn Marino 
590d5acd74SJohn Marino #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"
600d5acd74SJohn Marino 
610d5acd74SJohn Marino #define RLOCK(fail)	{ int ret;						\
620d5acd74SJohn Marino 			  if (__isthreaded &&					\
630d5acd74SJohn Marino 			      ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) {	\
640d5acd74SJohn Marino 				  errno = ret;					\
650d5acd74SJohn Marino 				  return (fail);				\
660d5acd74SJohn Marino 			  }}
670d5acd74SJohn Marino #define WLOCK(fail)	{ int ret;						\
680d5acd74SJohn Marino 			  if (__isthreaded &&					\
690d5acd74SJohn Marino 			      ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) {	\
700d5acd74SJohn Marino 				  errno = ret;					\
710d5acd74SJohn Marino 				  return (fail);				\
720d5acd74SJohn Marino 			  }}
730d5acd74SJohn Marino #define UNLOCK		{ if (__isthreaded)					\
740d5acd74SJohn Marino 			      _pthread_rwlock_unlock(&rwlock); }
750d5acd74SJohn Marino 
760d5acd74SJohn Marino #define	NLERR		((nl_catd) -1)
770d5acd74SJohn Marino #define NLRETERR(errc)  { errno = errc; return (NLERR); }
780d5acd74SJohn Marino #define SAVEFAIL(n, l, e)	{ WLOCK(NLERR);					\
790d5acd74SJohn Marino 				  np = malloc(sizeof(struct catentry));		\
800d5acd74SJohn Marino 				  if (np != NULL) {				\
810d5acd74SJohn Marino 				  	np->name = strdup(n);			\
820d5acd74SJohn Marino 					np->path = NULL;			\
830d5acd74SJohn Marino 					np->catd = NLERR;			\
844bbf0fc0Szrj 					np->refcount = 0;			\
850d5acd74SJohn Marino 					np->lang = (l == NULL) ? NULL :		\
860d5acd74SJohn Marino 					    strdup(l);				\
870d5acd74SJohn Marino 					np->caterrno = e;			\
880d5acd74SJohn Marino 				  	SLIST_INSERT_HEAD(&cache, np, list);	\
890d5acd74SJohn Marino 				  }						\
900d5acd74SJohn Marino 				  UNLOCK;					\
910d5acd74SJohn Marino 				  errno = e;					\
920d5acd74SJohn Marino 				}
930d5acd74SJohn Marino 
940d5acd74SJohn Marino static nl_catd load_msgcat(const char *, const char *, const char *);
950d5acd74SJohn Marino 
960d5acd74SJohn Marino static pthread_rwlock_t		 rwlock = PTHREAD_RWLOCK_INITIALIZER;
970d5acd74SJohn Marino 
980d5acd74SJohn Marino struct catentry {
990d5acd74SJohn Marino 	SLIST_ENTRY(catentry)	 list;
1000d5acd74SJohn Marino 	char			*name;
1010d5acd74SJohn Marino 	char			*path;
1020d5acd74SJohn Marino 	int			 caterrno;
1030d5acd74SJohn Marino 	nl_catd			 catd;
1040d5acd74SJohn Marino 	char			*lang;
1050d5acd74SJohn Marino 	int			 refcount;
1060d5acd74SJohn Marino };
1070d5acd74SJohn Marino 
1080d5acd74SJohn Marino SLIST_HEAD(listhead, catentry) cache =
1090d5acd74SJohn Marino     SLIST_HEAD_INITIALIZER(cache);
1100d5acd74SJohn Marino 
1110d5acd74SJohn Marino nl_catd
catopen(const char * name,int type)1120d5acd74SJohn Marino catopen(const char *name, int type)
1130d5acd74SJohn Marino {
114*8ad5bb66SSascha Wildner 	return (__catopen_l(name, type, __get_locale()));
115*8ad5bb66SSascha Wildner }
116*8ad5bb66SSascha Wildner 
117*8ad5bb66SSascha Wildner nl_catd
__catopen_l(const char * name,int type,locale_t locale)118*8ad5bb66SSascha Wildner __catopen_l(const char *name, int type, locale_t locale)
119*8ad5bb66SSascha Wildner {
1200d5acd74SJohn Marino 	struct stat sbuf;
1210d5acd74SJohn Marino 	struct catentry *np;
12221a0e97dSSascha Wildner 	char *base, *cptr, *cptr1, *nlspath, *pathP, *pcode;
12321a0e97dSSascha Wildner 	char *plang, *pter;
1240d5acd74SJohn Marino 	int saverr, spcleft;
12521a0e97dSSascha Wildner 	const char *lang, *tmpptr;
1260d5acd74SJohn Marino 	char path[PATH_MAX];
1270d5acd74SJohn Marino 
1280d5acd74SJohn Marino 	/* sanity checking */
1290d5acd74SJohn Marino 	if (name == NULL || *name == '\0')
1300d5acd74SJohn Marino 		NLRETERR(EINVAL);
1310d5acd74SJohn Marino 
1320d5acd74SJohn Marino 	if (strchr(name, '/') != NULL)
1330d5acd74SJohn Marino 		/* have a pathname */
1340d5acd74SJohn Marino 		lang = NULL;
1350d5acd74SJohn Marino 	else {
1360d5acd74SJohn Marino 		if (type == NL_CAT_LOCALE)
137*8ad5bb66SSascha Wildner 			lang = querylocale(LC_MESSAGES_MASK, locale);
1380d5acd74SJohn Marino 		else
1390d5acd74SJohn Marino 			lang = getenv("LANG");
1400d5acd74SJohn Marino 
1410d5acd74SJohn Marino 		if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN ||
1420d5acd74SJohn Marino 		    (lang[0] == '.' &&
1430d5acd74SJohn Marino 		    (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) ||
1440d5acd74SJohn Marino 		    strchr(lang, '/') != NULL)
1450d5acd74SJohn Marino 			lang = "C";
1460d5acd74SJohn Marino 	}
1470d5acd74SJohn Marino 
1480d5acd74SJohn Marino 	/* Try to get it from the cache first */
1490d5acd74SJohn Marino 	RLOCK(NLERR);
1500d5acd74SJohn Marino 	SLIST_FOREACH(np, &cache, list) {
1510d5acd74SJohn Marino 		if ((strcmp(np->name, name) == 0) &&
1520d5acd74SJohn Marino 		    ((lang != NULL && np->lang != NULL &&
1530d5acd74SJohn Marino 		    strcmp(np->lang, lang) == 0) || (np->lang == lang))) {
1540d5acd74SJohn Marino 			if (np->caterrno != 0) {
1550d5acd74SJohn Marino 				/* Found cached failing entry */
1560d5acd74SJohn Marino 				UNLOCK;
1570d5acd74SJohn Marino 				NLRETERR(np->caterrno);
1580d5acd74SJohn Marino 			} else {
1590d5acd74SJohn Marino 				/* Found cached successful entry */
1600d5acd74SJohn Marino 				np->refcount++;
1610d5acd74SJohn Marino 				UNLOCK;
1620d5acd74SJohn Marino 				return (np->catd);
1630d5acd74SJohn Marino 			}
1640d5acd74SJohn Marino 		}
1650d5acd74SJohn Marino 	}
1660d5acd74SJohn Marino 	UNLOCK;
1670d5acd74SJohn Marino 
1680d5acd74SJohn Marino 	/* is it absolute path ? if yes, load immediately */
1690d5acd74SJohn Marino 	if (strchr(name, '/') != NULL)
1700d5acd74SJohn Marino 		return (load_msgcat(name, name, lang));
1710d5acd74SJohn Marino 
1720d5acd74SJohn Marino 	/* sanity checking */
1730d5acd74SJohn Marino 	if ((plang = cptr1 = strdup(lang)) == NULL)
1740d5acd74SJohn Marino 		return (NLERR);
1750d5acd74SJohn Marino 	if ((cptr = strchr(cptr1, '@')) != NULL)
1760d5acd74SJohn Marino 		*cptr = '\0';
1770d5acd74SJohn Marino 	pter = pcode = "";
1780d5acd74SJohn Marino 	if ((cptr = strchr(cptr1, '_')) != NULL) {
1790d5acd74SJohn Marino 		*cptr++ = '\0';
1800d5acd74SJohn Marino 		pter = cptr1 = cptr;
1810d5acd74SJohn Marino 	}
1820d5acd74SJohn Marino 	if ((cptr = strchr(cptr1, '.')) != NULL) {
1830d5acd74SJohn Marino 		*cptr++ = '\0';
1840d5acd74SJohn Marino 		pcode = cptr;
1850d5acd74SJohn Marino 	}
1860d5acd74SJohn Marino 
1870d5acd74SJohn Marino 	if ((nlspath = getenv("NLSPATH")) == NULL || issetugid())
1880d5acd74SJohn Marino 		nlspath = _DEFAULT_NLS_PATH;
1890d5acd74SJohn Marino 
1900d5acd74SJohn Marino 	if ((base = cptr = strdup(nlspath)) == NULL) {
1910d5acd74SJohn Marino 		saverr = errno;
1920d5acd74SJohn Marino 		free(plang);
1930d5acd74SJohn Marino 		errno = saverr;
1940d5acd74SJohn Marino 		return (NLERR);
1950d5acd74SJohn Marino 	}
1960d5acd74SJohn Marino 
1970d5acd74SJohn Marino 	while ((nlspath = strsep(&cptr, ":")) != NULL) {
1980d5acd74SJohn Marino 		pathP = path;
1990d5acd74SJohn Marino 		if (*nlspath) {
2000d5acd74SJohn Marino 			for (; *nlspath; ++nlspath) {
2010d5acd74SJohn Marino 				if (*nlspath == '%') {
2020d5acd74SJohn Marino 					switch (*(nlspath + 1)) {
2030d5acd74SJohn Marino 					case 'l':
2040d5acd74SJohn Marino 						tmpptr = plang;
2050d5acd74SJohn Marino 						break;
2060d5acd74SJohn Marino 					case 't':
2070d5acd74SJohn Marino 						tmpptr = pter;
2080d5acd74SJohn Marino 						break;
2090d5acd74SJohn Marino 					case 'c':
2100d5acd74SJohn Marino 						tmpptr = pcode;
2110d5acd74SJohn Marino 						break;
2120d5acd74SJohn Marino 					case 'L':
2130d5acd74SJohn Marino 						tmpptr = lang;
2140d5acd74SJohn Marino 						break;
2150d5acd74SJohn Marino 					case 'N':
2160d5acd74SJohn Marino 						tmpptr = (char *)name;
2170d5acd74SJohn Marino 						break;
2180d5acd74SJohn Marino 					case '%':
2190d5acd74SJohn Marino 						++nlspath;
2200d5acd74SJohn Marino 						/* FALLTHROUGH */
2210d5acd74SJohn Marino 					default:
2220d5acd74SJohn Marino 						if (pathP - path >=
2230d5acd74SJohn Marino 						    sizeof(path) - 1)
2240d5acd74SJohn Marino 							goto too_long;
2250d5acd74SJohn Marino 						*(pathP++) = *nlspath;
2260d5acd74SJohn Marino 						continue;
2270d5acd74SJohn Marino 					}
2280d5acd74SJohn Marino 					++nlspath;
2290d5acd74SJohn Marino 			put_tmpptr:
2300d5acd74SJohn Marino 					spcleft = sizeof(path) -
2310d5acd74SJohn Marino 						  (pathP - path) - 1;
2320d5acd74SJohn Marino 					if (strlcpy(pathP, tmpptr, spcleft) >=
2330d5acd74SJohn Marino 					    spcleft) {
2340d5acd74SJohn Marino 			too_long:
2350d5acd74SJohn Marino 						free(plang);
2360d5acd74SJohn Marino 						free(base);
2370d5acd74SJohn Marino 						SAVEFAIL(name, lang, ENAMETOOLONG);
2380d5acd74SJohn Marino 						NLRETERR(ENAMETOOLONG);
2390d5acd74SJohn Marino 					}
2400d5acd74SJohn Marino 					pathP += strlen(tmpptr);
2410d5acd74SJohn Marino 				} else {
2420d5acd74SJohn Marino 					if (pathP - path >= sizeof(path) - 1)
2430d5acd74SJohn Marino 						goto too_long;
2440d5acd74SJohn Marino 					*(pathP++) = *nlspath;
2450d5acd74SJohn Marino 				}
2460d5acd74SJohn Marino 			}
2470d5acd74SJohn Marino 			*pathP = '\0';
2480d5acd74SJohn Marino 			if (stat(path, &sbuf) == 0) {
2490d5acd74SJohn Marino 				free(plang);
2500d5acd74SJohn Marino 				free(base);
2510d5acd74SJohn Marino 				return (load_msgcat(path, name, lang));
2520d5acd74SJohn Marino 			}
2530d5acd74SJohn Marino 		} else {
2540d5acd74SJohn Marino 			tmpptr = (char *)name;
2550d5acd74SJohn Marino 			--nlspath;
2560d5acd74SJohn Marino 			goto put_tmpptr;
2570d5acd74SJohn Marino 		}
2580d5acd74SJohn Marino 	}
2590d5acd74SJohn Marino 	free(plang);
2600d5acd74SJohn Marino 	free(base);
2610d5acd74SJohn Marino 	SAVEFAIL(name, lang, ENOENT);
2620d5acd74SJohn Marino 	NLRETERR(ENOENT);
2630d5acd74SJohn Marino }
2640d5acd74SJohn Marino 
2650d5acd74SJohn Marino char *
catgets(nl_catd catd,int set_id,int msg_id,const char * s)2660d5acd74SJohn Marino catgets(nl_catd catd, int set_id, int msg_id, const char *s)
2670d5acd74SJohn Marino {
2680d5acd74SJohn Marino 	struct _nls_cat_hdr *cat_hdr;
2690d5acd74SJohn Marino 	struct _nls_msg_hdr *msg_hdr;
2700d5acd74SJohn Marino 	struct _nls_set_hdr *set_hdr;
2710d5acd74SJohn Marino 	int i, l, r, u;
2720d5acd74SJohn Marino 
2730d5acd74SJohn Marino 	if (catd == NULL || catd == NLERR) {
2740d5acd74SJohn Marino 		errno = EBADF;
2750d5acd74SJohn Marino 		/* LINTED interface problem */
2760d5acd74SJohn Marino 		return ((char *)s);
2770d5acd74SJohn Marino 	}
2780d5acd74SJohn Marino 
2790d5acd74SJohn Marino 	cat_hdr = (struct _nls_cat_hdr *)catd->__data;
2800d5acd74SJohn Marino 	set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data +
2810d5acd74SJohn Marino 	    sizeof(struct _nls_cat_hdr));
2820d5acd74SJohn Marino 
2830d5acd74SJohn Marino 	/* binary search, see knuth algorithm b */
2840d5acd74SJohn Marino 	l = 0;
2850d5acd74SJohn Marino 	u = ntohl((u_int32_t)cat_hdr->__nsets) - 1;
2860d5acd74SJohn Marino 	while (l <= u) {
2870d5acd74SJohn Marino 		i = (l + u) / 2;
2880d5acd74SJohn Marino 		r = set_id - ntohl((u_int32_t)set_hdr[i].__setno);
2890d5acd74SJohn Marino 
2900d5acd74SJohn Marino 		if (r == 0) {
2910d5acd74SJohn Marino 			msg_hdr = (struct _nls_msg_hdr *)
2920d5acd74SJohn Marino 			    (void *)((char *)catd->__data +
2930d5acd74SJohn Marino 			    sizeof(struct _nls_cat_hdr) +
2940d5acd74SJohn Marino 			    ntohl((u_int32_t)cat_hdr->__msg_hdr_offset));
2950d5acd74SJohn Marino 
2960d5acd74SJohn Marino 			l = ntohl((u_int32_t)set_hdr[i].__index);
2970d5acd74SJohn Marino 			u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1;
2980d5acd74SJohn Marino 			while (l <= u) {
2990d5acd74SJohn Marino 				i = (l + u) / 2;
3000d5acd74SJohn Marino 				r = msg_id -
3010d5acd74SJohn Marino 				    ntohl((u_int32_t)msg_hdr[i].__msgno);
3020d5acd74SJohn Marino 				if (r == 0) {
3030d5acd74SJohn Marino 					return ((char *) catd->__data +
3040d5acd74SJohn Marino 					    sizeof(struct _nls_cat_hdr) +
3050d5acd74SJohn Marino 					    ntohl((u_int32_t)
3060d5acd74SJohn Marino 					    cat_hdr->__msg_txt_offset) +
3070d5acd74SJohn Marino 					    ntohl((u_int32_t)
3080d5acd74SJohn Marino 					    msg_hdr[i].__offset));
3090d5acd74SJohn Marino 				} else if (r < 0) {
3100d5acd74SJohn Marino 					u = i - 1;
3110d5acd74SJohn Marino 				} else {
3120d5acd74SJohn Marino 					l = i + 1;
3130d5acd74SJohn Marino 				}
3140d5acd74SJohn Marino 			}
3150d5acd74SJohn Marino 
3160d5acd74SJohn Marino 			/* not found */
3170d5acd74SJohn Marino 			goto notfound;
3180d5acd74SJohn Marino 
3190d5acd74SJohn Marino 		} else if (r < 0) {
3200d5acd74SJohn Marino 			u = i - 1;
3210d5acd74SJohn Marino 		} else {
3220d5acd74SJohn Marino 			l = i + 1;
3230d5acd74SJohn Marino 		}
3240d5acd74SJohn Marino 	}
3250d5acd74SJohn Marino 
3260d5acd74SJohn Marino notfound:
3270d5acd74SJohn Marino 	/* not found */
3280d5acd74SJohn Marino 	errno = ENOMSG;
3290d5acd74SJohn Marino 	/* LINTED interface problem */
3300d5acd74SJohn Marino 	return ((char *)s);
3310d5acd74SJohn Marino }
3320d5acd74SJohn Marino 
3334bbf0fc0Szrj static void
catfree(struct catentry * np)3344bbf0fc0Szrj catfree(struct catentry *np)
3354bbf0fc0Szrj {
3364bbf0fc0Szrj 
3374bbf0fc0Szrj 	if (np->catd != NULL && np->catd != NLERR) {
3384bbf0fc0Szrj 		munmap(np->catd->__data, (size_t)np->catd->__size);
3394bbf0fc0Szrj 		free(np->catd);
3404bbf0fc0Szrj 	}
3414bbf0fc0Szrj 	SLIST_REMOVE(&cache, np, catentry, list);
3424bbf0fc0Szrj 	free(np->name);
3434bbf0fc0Szrj 	free(np->path);
3444bbf0fc0Szrj 	free(np->lang);
3454bbf0fc0Szrj 	free(np);
3464bbf0fc0Szrj }
3474bbf0fc0Szrj 
3480d5acd74SJohn Marino int
catclose(nl_catd catd)3490d5acd74SJohn Marino catclose(nl_catd catd)
3500d5acd74SJohn Marino {
3510d5acd74SJohn Marino 	struct catentry *np;
3520d5acd74SJohn Marino 
3530d5acd74SJohn Marino 	/* sanity checking */
3540d5acd74SJohn Marino 	if (catd == NULL || catd == NLERR) {
3550d5acd74SJohn Marino 		errno = EBADF;
3560d5acd74SJohn Marino 		return (-1);
3570d5acd74SJohn Marino 	}
3580d5acd74SJohn Marino 
3590d5acd74SJohn Marino 	/* Remove from cache if not referenced any more */
3600d5acd74SJohn Marino 	WLOCK(-1);
3610d5acd74SJohn Marino 	SLIST_FOREACH(np, &cache, list) {
3620d5acd74SJohn Marino 		if (catd == np->catd) {
3630d5acd74SJohn Marino 			np->refcount--;
3644bbf0fc0Szrj 			if (np->refcount == 0)
3654bbf0fc0Szrj 				catfree(np);
3660d5acd74SJohn Marino 			break;
3670d5acd74SJohn Marino 		}
3680d5acd74SJohn Marino 	}
3690d5acd74SJohn Marino 	UNLOCK;
3700d5acd74SJohn Marino 	return (0);
3710d5acd74SJohn Marino }
3720d5acd74SJohn Marino 
3730d5acd74SJohn Marino /*
3740d5acd74SJohn Marino  * Internal support functions
3750d5acd74SJohn Marino  */
3760d5acd74SJohn Marino 
3770d5acd74SJohn Marino static nl_catd
load_msgcat(const char * path,const char * name,const char * lang)3780d5acd74SJohn Marino load_msgcat(const char *path, const char *name, const char *lang)
3790d5acd74SJohn Marino {
3800d5acd74SJohn Marino 	struct stat st;
3810d5acd74SJohn Marino 	nl_catd	catd;
3820d5acd74SJohn Marino 	struct catentry *np;
3830d5acd74SJohn Marino 	void *data;
3840d5acd74SJohn Marino 	int fd;
3850d5acd74SJohn Marino 
3860d5acd74SJohn Marino 	/* path/name will never be NULL here */
3870d5acd74SJohn Marino 
3880d5acd74SJohn Marino 	/*
3890d5acd74SJohn Marino 	 * One more try in cache; if it was not found by name,
3900d5acd74SJohn Marino 	 * it might still be found by absolute path.
3910d5acd74SJohn Marino 	 */
3920d5acd74SJohn Marino 	RLOCK(NLERR);
3930d5acd74SJohn Marino 	SLIST_FOREACH(np, &cache, list) {
3940d5acd74SJohn Marino 		if ((np->path != NULL) && (strcmp(np->path, path) == 0)) {
3950d5acd74SJohn Marino 			np->refcount++;
3960d5acd74SJohn Marino 			UNLOCK;
3970d5acd74SJohn Marino 			return (np->catd);
3980d5acd74SJohn Marino 		}
3990d5acd74SJohn Marino 	}
4000d5acd74SJohn Marino 	UNLOCK;
4010d5acd74SJohn Marino 
4020d5acd74SJohn Marino 	if ((fd = _open(path, O_RDONLY | O_CLOEXEC)) == -1) {
4030d5acd74SJohn Marino 		SAVEFAIL(name, lang, errno);
4040d5acd74SJohn Marino 		NLRETERR(errno);
4050d5acd74SJohn Marino 	}
4060d5acd74SJohn Marino 
4070d5acd74SJohn Marino 	if (_fstat(fd, &st) != 0) {
4080d5acd74SJohn Marino 		_close(fd);
4090d5acd74SJohn Marino 		SAVEFAIL(name, lang, EFTYPE);
4100d5acd74SJohn Marino 		NLRETERR(EFTYPE);
4110d5acd74SJohn Marino 	}
4120d5acd74SJohn Marino 
4130d5acd74SJohn Marino 	/*
4140d5acd74SJohn Marino 	 * If the file size cannot be held in size_t we cannot mmap()
4150d5acd74SJohn Marino 	 * it to the memory.  Probably, this will not be a problem given
4160d5acd74SJohn Marino 	 * that catalog files are usually small.
4170d5acd74SJohn Marino 	 */
4180d5acd74SJohn Marino 	if (st.st_size > SIZE_T_MAX) {
4190d5acd74SJohn Marino 		_close(fd);
4200d5acd74SJohn Marino 		SAVEFAIL(name, lang, EFBIG);
4210d5acd74SJohn Marino 		NLRETERR(EFBIG);
4220d5acd74SJohn Marino 	}
4230d5acd74SJohn Marino 
4240d5acd74SJohn Marino 	if ((data = mmap(0, (size_t)st.st_size, PROT_READ,
4250d5acd74SJohn Marino 	    MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) {
4260d5acd74SJohn Marino 		int saved_errno = errno;
4270d5acd74SJohn Marino 		_close(fd);
4280d5acd74SJohn Marino 		SAVEFAIL(name, lang, saved_errno);
4290d5acd74SJohn Marino 		NLRETERR(saved_errno);
4300d5acd74SJohn Marino 	}
4310d5acd74SJohn Marino 	_close(fd);
4320d5acd74SJohn Marino 
4330d5acd74SJohn Marino 	if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) !=
4340d5acd74SJohn Marino 	    _NLS_MAGIC) {
4350d5acd74SJohn Marino 		munmap(data, (size_t)st.st_size);
4360d5acd74SJohn Marino 		SAVEFAIL(name, lang, EFTYPE);
4370d5acd74SJohn Marino 		NLRETERR(EFTYPE);
4380d5acd74SJohn Marino 	}
4390d5acd74SJohn Marino 
4400d5acd74SJohn Marino 	if ((catd = malloc(sizeof (*catd))) == NULL) {
4410d5acd74SJohn Marino 		munmap(data, (size_t)st.st_size);
4420d5acd74SJohn Marino 		SAVEFAIL(name, lang, ENOMEM);
4430d5acd74SJohn Marino 		NLRETERR(ENOMEM);
4440d5acd74SJohn Marino 	}
4450d5acd74SJohn Marino 
4460d5acd74SJohn Marino 	catd->__data = data;
4470d5acd74SJohn Marino 	catd->__size = (int)st.st_size;
4480d5acd74SJohn Marino 
4490d5acd74SJohn Marino 	/* Caching opened catalog */
4500d5acd74SJohn Marino 	WLOCK(NLERR);
4510d5acd74SJohn Marino 	if ((np = malloc(sizeof(struct catentry))) != NULL) {
4520d5acd74SJohn Marino 		np->name = strdup(name);
4530d5acd74SJohn Marino 		np->path = strdup(path);
4540d5acd74SJohn Marino 		np->catd = catd;
4550d5acd74SJohn Marino 		np->lang = (lang == NULL) ? NULL : strdup(lang);
4560d5acd74SJohn Marino 		np->refcount = 1;
4570d5acd74SJohn Marino 		np->caterrno = 0;
4580d5acd74SJohn Marino 		SLIST_INSERT_HEAD(&cache, np, list);
4590d5acd74SJohn Marino 	}
4600d5acd74SJohn Marino 	UNLOCK;
4610d5acd74SJohn Marino 	return (catd);
4620d5acd74SJohn Marino }
463