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