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