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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 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 /* Copyright (c) 1988 AT&T */ 30 /* All Rights Reserved */ 31 32 33 /* __gtxt(): Common part to gettxt() and pfmt() */ 34 35 #include "synonyms.h" 36 #include "libc.h" 37 #include <mtlib.h> 38 #include <sys/types.h> 39 #include <string.h> 40 #include <locale.h> 41 #include <fcntl.h> 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #include <sys/mman.h> 45 #include <stdlib.h> 46 #include <synch.h> 47 #include <pfmt.h> 48 #include <thread.h> 49 #include <unistd.h> 50 #include <errno.h> 51 #include <limits.h> 52 #include "../i18n/_locale.h" 53 #include "../i18n/_loc_path.h" 54 55 #define MESSAGES "/LC_MESSAGES/" 56 static const char *def_locale = "C"; 57 static const char *not_found = "Message not found!!\n"; 58 static struct db_info *db_info; 59 static int db_count, maxdb; 60 61 struct db_info { 62 char db_name[DB_NAME_LEN]; /* Name of the message file */ 63 uintptr_t addr; /* Virtual memory address */ 64 size_t length; 65 char *saved_locale; 66 char flag; 67 }; 68 69 #define DB_EXIST 1 /* The catalogue exists */ 70 #define DB_OPEN 2 /* Already tried to open */ 71 72 /* Minimum number of open catalogues */ 73 #define MINDB 3 74 75 static char cur_cat[DB_NAME_LEN]; 76 static rwlock_t _rw_cur_cat = DEFAULTRWLOCK; 77 78 79 /* 80 * setcat(cat): Specify the default catalogue. 81 * Return a pointer to the local copy of the default catalogue 82 */ 83 const char * 84 setcat(const char *cat) 85 { 86 lrw_wrlock(&_rw_cur_cat); 87 if (cat) { 88 if (((strchr(cat, '/') != NULL)) || 89 ((strchr(cat, ':') != NULL))) { 90 cur_cat[0] = '\0'; 91 goto out; 92 } 93 (void) strncpy(cur_cat, cat, sizeof (cur_cat) - 1); 94 cur_cat[sizeof (cur_cat) - 1] = '\0'; 95 } 96 out: 97 lrw_unlock(&_rw_cur_cat); 98 return (cur_cat[0] ? cur_cat : NULL); 99 } 100 101 /* 102 * load a message catalog which specified with current locale, 103 * and catalog name. 104 */ 105 static struct db_info * 106 load_db(const char *curloc, const char *catname, int *err) 107 { 108 char pathname[PATH_MAX]; 109 struct stat64 sb; 110 caddr_t addr; 111 struct db_info *db; 112 int fd; 113 int i; 114 115 *err = 0; 116 117 /* First time called, allocate space */ 118 if (!db_info) { 119 if ((db_info = 120 libc_malloc(MINDB * sizeof (struct db_info))) == NULL) { 121 *err = 1; 122 return (NULL); 123 } 124 maxdb = MINDB; 125 } 126 127 for (i = 0; i < db_count; i++) { 128 if (db_info[i].flag == 0) 129 break; 130 } 131 /* New catalogue */ 132 if (i == db_count) { 133 if (db_count == maxdb) { 134 if ((db = libc_realloc(db_info, 135 ++maxdb * sizeof (struct db_info))) == NULL) { 136 *err = 1; 137 return (NULL); 138 } 139 db_info = db; 140 } 141 db_count++; 142 } 143 db = &db_info[i]; 144 db->flag = 0; 145 (void) strcpy(db->db_name, catname); 146 db->saved_locale = libc_strdup(curloc); 147 if (db->saved_locale == NULL) { 148 *err = 1; 149 return (NULL); 150 } 151 db->flag = DB_OPEN; 152 if (snprintf(pathname, sizeof (pathname), 153 _DFLT_LOC_PATH "%s" MESSAGES "%s", 154 db->saved_locale, db->db_name) >= sizeof (pathname)) { 155 /* 156 * We won't set err here, because an invalid locale is not 157 * the fatal condition, but we can fall back to "C" 158 * locale. 159 */ 160 return (NULL); 161 } 162 if ((fd = open(pathname, O_RDONLY)) != -1 && 163 fstat64(fd, &sb) != -1 && 164 (addr = mmap(0, (size_t)sb.st_size, PROT_READ, MAP_SHARED, 165 fd, 0)) != MAP_FAILED) { 166 db->flag |= DB_EXIST; 167 db->addr = (uintptr_t)addr; 168 db->length = (size_t)sb.st_size; 169 } 170 if (fd != -1) 171 (void) close(fd); 172 return (db); 173 } 174 175 /* 176 * unmap the message catalog, and release the db_info slot. 177 */ 178 static void 179 unload_db(struct db_info *db) 180 { 181 if ((db->flag & (DB_OPEN|DB_EXIST)) == 182 (DB_OPEN|DB_EXIST)) { 183 (void) munmap((caddr_t)db->addr, db->length); 184 } 185 db->flag = 0; 186 if (db->saved_locale) 187 libc_free(db->saved_locale); 188 db->saved_locale = NULL; 189 } 190 191 /* 192 * go through the db_info, and find out a db_info slot regarding 193 * the given current locale and catalog name. 194 * If db is not NULL, then search will start from top of the array, 195 * otherwise it will start from the next of given db. 196 * If curloc is set to NULL, then return a cache without regards of 197 * locale. 198 */ 199 static struct db_info * 200 lookup_cache(struct db_info *db, const char *curloc, const char *catname) 201 { 202 if (db_info == NULL) 203 return (NULL); 204 205 if (db == NULL) 206 db = db_info; 207 else 208 db++; 209 210 for (; db < &db_info[db_count]; db++) { 211 if (db->flag == 0) 212 continue; 213 if (strcmp(db->db_name, catname) == 0) { 214 if (curloc == NULL || 215 (db->saved_locale != NULL && 216 strcmp(db->saved_locale, curloc) == 0)) { 217 return (db); 218 } 219 } 220 } 221 return (NULL); 222 } 223 224 static int 225 valid_msg(struct db_info *db, int id) 226 { 227 if (db == NULL || (db->flag & DB_EXIST) == 0) 228 return (0); 229 230 /* catalog has been loaded */ 231 if (id != 0 && id <= *(int *)(db->addr)) 232 return (1); 233 234 /* not a valid id */ 235 return (0); 236 } 237 238 static char * 239 msg(struct db_info *db, int id) 240 { 241 return ((char *)(db->addr + *(int *)(db->addr + 242 id * sizeof (int)))); 243 } 244 245 /* 246 * __gtxt(catname, id, dflt): Return a pointer to a message. 247 * catname is the name of the catalog. If null, the default catalog is 248 * used. 249 * id is the numeric id of the message in the catalogue 250 * dflt is the default message. 251 * 252 * Information about non-existent catalogues is kept in db_info, in 253 * such a way that subsequent calls with the same catalogue do not 254 * try to open the catalogue again. 255 */ 256 const char * 257 __gtxt(const char *catname, int id, const char *dflt) 258 { 259 char *curloc; 260 struct db_info *db; 261 int err; 262 263 /* Check for invalid message id */ 264 if (id < 0) 265 return (not_found); 266 if (id == 0) 267 return ((dflt && *dflt) ? dflt : not_found); 268 269 /* 270 * If catalogue is unspecified, use default catalogue. 271 * No catalogue at all is an error 272 */ 273 if (!catname || !*catname) { 274 lrw_rdlock(&_rw_cur_cat); 275 if (cur_cat == NULL || !*cur_cat) { 276 lrw_unlock(&_rw_cur_cat); 277 return (not_found); 278 } 279 catname = cur_cat; 280 lrw_unlock(&_rw_cur_cat); 281 } 282 283 curloc = setlocale(LC_MESSAGES, NULL); 284 285 /* First look up the cache */ 286 db = lookup_cache(NULL, curloc, catname); 287 if (db != NULL) { 288 /* 289 * The catalog has been loaded, and if id seems valid, 290 * then just return. 291 */ 292 if (valid_msg(db, id)) 293 return (msg(db, id)); 294 295 /* 296 * seems given id is out of bound or does not exist. In this 297 * case, we need to look up a message for the "C" locale as 298 * documented in the man page. 299 */ 300 db = lookup_cache(NULL, def_locale, catname); 301 if (db == NULL) { 302 /* 303 * Even the message catalog for the "C" has not been 304 * loaded. 305 */ 306 db = load_db(def_locale, catname, &err); 307 if (err) 308 return (not_found); 309 } 310 if (valid_msg(db, id)) 311 return (msg(db, id)); 312 /* no message found */ 313 return ((dflt && *dflt) ? dflt : not_found); 314 } 315 316 /* 317 * The catalog has not been loaded or even has not 318 * attempted to be loaded, invalidate all caches related to 319 * the catname for possibly different locale. 320 */ 321 db = NULL; 322 while ((db = lookup_cache(db, NULL, catname)) != NULL) 323 unload_db(db); 324 325 /* 326 * load a message catalog for the requested locale. 327 */ 328 db = load_db(curloc, catname, &err); 329 if (err) 330 return (not_found); 331 if (valid_msg(db, id)) 332 return (msg(db, id)); 333 334 /* 335 * If the requested catalog is either not exist or message 336 * id is invalid, then try to load from "C" locale. 337 */ 338 db = load_db(def_locale, catname, &err); 339 if (err) 340 return (not_found); 341 342 if (valid_msg(db, id)) 343 return (msg(db, id)); 344 345 /* no message found */ 346 return ((dflt && *dflt) ? dflt : not_found); 347 } 348