xref: /illumos-gate/usr/src/lib/libc/port/gen/catopen.c (revision afab0816ecb604f0099a09ad8ee398f0d7b77b1c)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * catopen.c
31  *
32  */
33 
34 #pragma weak _catopen = catopen
35 #pragma weak _catclose = catclose
36 
37 #include "lint.h"
38 #include "libc.h"
39 #include <sys/types.h>
40 #include <unistd.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/stat.h>
44 #include <sys/mman.h>
45 #include <nl_types.h>
46 #include <locale.h>
47 #include <limits.h>
48 #include <errno.h>
49 #include "../i18n/_loc_path.h"
50 #include "nlspath_checks.h"
51 
52 #define	SAFE_F		1
53 #define	UNSAFE_F	0
54 
55 static char *
56 replace_nls_option(char *, char *, char *, char *, char *, char *, char *);
57 static nl_catd file_open(const char *, int);
58 static nl_catd process_nls_path(char *, int);
59 
60 nl_catd
61 catopen(const char *name, int oflag)
62 {
63 	nl_catd p;
64 
65 	if (!name) {				/* Null pointer */
66 		errno = EFAULT;
67 		return ((nl_catd)-1);
68 	} else if (!*name) {		/* Empty string */
69 		errno = ENOENT;
70 		return ((nl_catd)-1);
71 	} else if (strchr(name, '/') != NULL) {
72 		/* If name contains '/', then it is complete file name */
73 		p = file_open(name, SAFE_F);
74 	} else {				/* Normal case */
75 		p = process_nls_path((char *)name, oflag);
76 	}
77 
78 	if (p == NULL) {  /* Opening catalog file failed */
79 		return ((nl_catd)-1);
80 	} else {
81 		return (p);
82 	}
83 }
84 
85 
86 /*
87  * This routine will process NLSPATH environment variable.
88  * It will return catd id whenever it finds valid catalog.
89  */
90 static nl_catd
91 process_nls_path(char *name, int oflag)
92 {
93 	char	*s, *s1, *s2, *t;
94 	char	*nlspath, *lang, *territory, *codeset, *locale;
95 	char	pathname[PATH_MAX + 1];
96 	nl_catd	p;
97 
98 	/*
99 	 * locale=language_territory.codeset
100 	 * XPG4 uses LC_MESSAGES.
101 	 * XPG3 uses LANG.
102 	 * From the following two lines, choose one depending on XPG3 or 4.
103 	 *
104 	 * Chose XPG4. If oflag == NL_CAT_LOCALE, use LC_MESSAGES.
105 	 */
106 	if (oflag == NL_CAT_LOCALE)
107 		locale = setlocale(LC_MESSAGES, NULL);
108 	else
109 		locale = getenv("LANG");
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++ = NULL;
130 				} else if (*s == '.') {
131 					s2 = s;
132 					*s2++ = NULL;
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 = NULL;
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  */
218 static char *
219 replace_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 = NULL;
273 	return (s);
274 }
275 
276 /*
277  * This routine will open file, mmap it, and return catd id.
278  */
279 static nl_catd
280 file_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 
322 int
323 catclose(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