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