xref: /illumos-gate/usr/src/lib/libc/port/gen/catopen.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
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 
53 static char *
54 replace_nls_option(char *, char *, char *, char *, char *, char *, char *);
55 static nl_catd file_open(const char *, int);
56 static nl_catd process_nls_path(char *, int);
57 
58 nl_catd
59 catopen(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  */
88 static nl_catd
89 process_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++ = 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