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
catopen(const char * name,int oflag)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
process_nls_path(char * name,int oflag)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 *
replace_nls_option(char * s,char * name,char * pathname,char * locale,char * lang,char * territory,char * codeset)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
file_open(const char * name,int safe)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
catclose(nl_catd catd)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