1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * catopen.c
29 *
30 */
31
32#pragma weak _catopen = catopen
33#pragma weak _catclose = catclose
34
35#include "lint.h"
36#include "libc.h"
37#include <sys/types.h>
38#include <unistd.h>
39#include <stdlib.h>
40#include <string.h>
41#include <sys/stat.h>
42#include <sys/mman.h>
43#include <nl_types.h>
44#include <locale.h>
45#include <limits.h>
46#include <errno.h>
47#include "../i18n/_loc_path.h"
48#include "nlspath_checks.h"
49
50#define	SAFE_F		1
51#define	UNSAFE_F	0
52
53static char *
54replace_nls_option(char *, char *, char *, char *, char *, char *, char *);
55static nl_catd file_open(const char *, int);
56static nl_catd process_nls_path(char *, int);
57
58nl_catd
59catopen(const char *name, int oflag)
60{
61	nl_catd p;
62
63	if (!name) {				/* Null pointer */
64		errno = EFAULT;
65		return ((nl_catd)-1);
66	} else if (!*name) {		/* Empty string */
67		errno = ENOENT;
68		return ((nl_catd)-1);
69	} else if (strchr(name, '/') != NULL) {
70		/* If name contains '/', then it is complete file name */
71		p = file_open(name, SAFE_F);
72	} else {				/* Normal case */
73		p = process_nls_path((char *)name, oflag);
74	}
75
76	if (p == NULL) {  /* Opening catalog file failed */
77		return ((nl_catd)-1);
78	} else {
79		return (p);
80	}
81}
82
83
84/*
85 * This routine will process NLSPATH environment variable.
86 * It will return catd id whenever it finds valid catalog.
87 */
88static nl_catd
89process_nls_path(char *name, int oflag)
90{
91	char	*s, *s1, *s2, *t;
92	char	*nlspath, *lang, *territory, *codeset, *locale;
93	char	pathname[PATH_MAX + 1];
94	nl_catd	p;
95
96	/*
97	 * locale=language_territory.codeset
98	 * XPG4 uses LC_MESSAGES.
99	 * XPG3 uses LANG.
100	 * From the following two lines, choose one depending on XPG3 or 4.
101	 *
102	 * Chose XPG4. If oflag == NL_CAT_LOCALE, use LC_MESSAGES.
103	 */
104	if (oflag == NL_CAT_LOCALE) {
105		locale_t loc = uselocale(NULL);
106		locale = current_locale(loc, LC_MESSAGES);
107	} else {
108		locale = getenv("LANG");
109	}
110
111	nlspath = getenv("NLSPATH");
112	lang = NULL;
113	if (nlspath) {
114		territory = NULL;
115		codeset = NULL;
116		/*
117		 * extract lang, territory and codeset from locale name
118		 */
119		if (locale) {
120			lang = s = libc_strdup(locale);
121			if (!lang) {
122				/* strdup failed */
123				return (NULL);
124			}
125			s1 = s2 = NULL;
126			while (s && *s) {
127				if (*s == '_') {
128					s1 = s;
129					*s1++ = '\0';
130				} else if (*s == '.') {
131					s2 = s;
132					*s2++ = '\0';
133				}
134				s++;
135			}
136			territory = s1;
137			codeset   = s2;
138		} /* if (locale) */
139
140		/*
141		 * March through NLSPATH until finds valid cat file
142		 */
143		s = nlspath;
144		while (*s) {
145			if (*s == ':') {
146				/* unqualified pathname is unsafe */
147				p = file_open(name, UNSAFE_F);
148				if (p != NULL) {
149					if (lang)
150						libc_free(lang);
151					return (p);
152				}
153				++s;
154				continue;
155			}
156
157			/* replace Substitution field */
158			s = replace_nls_option(s, name, pathname, locale,
159			    lang, territory, codeset);
160
161			p = file_open(pathname, UNSAFE_F);
162			if (p != NULL) {
163				if (lang)
164					libc_free(lang);
165				return (p);
166			}
167			if (*s)
168				++s;
169		} /* while */
170	} /* if (nlspath) */
171
172	/* lang is not used any more, free it */
173	if (lang)
174		libc_free(lang);
175
176	/*
177	 * Implementation dependent default location of XPG3.
178	 * We use /usr/lib/locale/<locale>/LC_MESSAGES/%N.
179	 * If C locale, do not translate message.
180	 */
181	if (locale == NULL) {
182		return (NULL);
183	} else if (locale[0] == 'C' && locale[1] == '\0') {
184		p = libc_malloc(sizeof (struct _nl_catd_struct));
185		if (p == NULL) {
186			/* malloc failed */
187			return (NULL);
188		}
189		p->__content = NULL;
190		p->__size = 0;
191		p->__trust = 1;
192		return (p);
193	}
194
195	s = _DFLT_LOC_PATH;
196	t = pathname;
197	while (*t++ = *s++)
198		continue;
199	t--;
200	s = locale;
201	while (*s && t < pathname + PATH_MAX)
202		*t++ = *s++;
203	s = "/LC_MESSAGES/";
204	while (*s && t < pathname + PATH_MAX)
205		*t++ = *s++;
206	s = name;
207	while (*s && t < pathname + PATH_MAX)
208		*t++ = *s++;
209	*t = '\0';
210	return (file_open(pathname, SAFE_F));
211}
212
213
214/*
215 * This routine will replace substitution parameters in NLSPATH
216 * with appropiate values. Returns expanded pathname.
217 */
218static char *
219replace_nls_option(char *s, char *name, char *pathname, char *locale,
220	char *lang, char *territory, char *codeset)
221{
222	char	*t, *u;
223
224	t = pathname;
225	while (*s && *s != ':') {
226		if (t < pathname + PATH_MAX) {
227			/*
228			 * %% is considered a single % character (XPG).
229			 * %L : LC_MESSAGES (XPG4) LANG(XPG3)
230			 * %l : The language element from the current locale.
231			 *	(XPG3, XPG4)
232			 */
233			if (*s != '%')
234				*t++ = *s;
235			else if (*++s == 'N') {
236				u = name;
237				while (*u && t < pathname + PATH_MAX)
238					*t++ = *u++;
239			} else if (*s == 'L') {
240				if (locale) {
241					u = locale;
242					while (*u && t < pathname + PATH_MAX)
243						*t++ = *u++;
244				}
245			} else if (*s == 'l') {
246				if (lang) {
247					u = lang;
248					while (*u && *u != '_' &&
249					    t < pathname + PATH_MAX)
250						*t++ = *u++;
251				}
252			} else if (*s == 't') {
253				if (territory) {
254					u = territory;
255					while (*u && *u != '.' &&
256					    t < pathname + PATH_MAX)
257						*t++ = *u++;
258				}
259			} else if (*s == 'c') {
260				if (codeset) {
261					u = codeset;
262					while (*u && t < pathname + PATH_MAX)
263						*t++ = *u++;
264				}
265			} else {
266				if (t < pathname + PATH_MAX)
267					*t++ = *s;
268			}
269		}
270		++s;
271	}
272	*t = '\0';
273	return (s);
274}
275
276/*
277 * This routine will open file, mmap it, and return catd id.
278 */
279static nl_catd
280file_open(const char *name, int safe)
281{
282	int		fd;
283	struct stat64	statbuf;
284	void		*addr;
285	struct _cat_hdr	*tmp;
286	nl_catd		tmp_catd;
287	int		trust;
288
289	fd = nls_safe_open(name, &statbuf, &trust, safe);
290
291	if (fd == -1) {
292		return (NULL);
293	}
294
295	addr = mmap(0, (size_t)statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
296	(void) close(fd);
297
298	if (addr == MAP_FAILED) {
299		return (NULL);
300	}
301
302	/* check MAGIC number of catalogue file */
303	tmp = (struct _cat_hdr *)addr;
304	if (tmp->__hdr_magic != _CAT_MAGIC) {
305		(void) munmap(addr, (size_t)statbuf.st_size);
306		return (NULL);
307	}
308
309	tmp_catd = libc_malloc(sizeof (struct _nl_catd_struct));
310	if (tmp_catd == NULL) {
311		/* malloc failed */
312		(void) munmap(addr, statbuf.st_size);
313		return (NULL);
314	}
315	tmp_catd->__content = addr;
316	tmp_catd->__size = (int)statbuf.st_size;
317	tmp_catd->__trust = trust;
318
319	return (tmp_catd);
320}
321
322int
323catclose(nl_catd catd)
324{
325	if (catd &&
326	    catd != (nl_catd)-1) {
327		if (catd->__content) {
328			(void) munmap(catd->__content, catd->__size);
329			catd->__content = NULL;
330		}
331		catd->__size = 0;
332		catd->__trust = 0;
333		libc_free(catd);
334	}
335	return (0);
336}
337