17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 564e3e6f9Scraigm * Common Development and Distribution License (the "License"). 664e3e6f9Scraigm * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217257d1b4Sraf 227c478bd9Sstevel@tonic-gate /* 237257d1b4Sraf * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */ 287c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate /* __gtxt(): Common part to gettxt() and pfmt() */ 317c478bd9Sstevel@tonic-gate 327257d1b4Sraf #pragma weak _setcat = setcat 3364e3e6f9Scraigm 347257d1b4Sraf #include "lint.h" 357c478bd9Sstevel@tonic-gate #include "libc.h" 367c478bd9Sstevel@tonic-gate #include <mtlib.h> 377c478bd9Sstevel@tonic-gate #include <sys/types.h> 387c478bd9Sstevel@tonic-gate #include <string.h> 397c478bd9Sstevel@tonic-gate #include <locale.h> 407c478bd9Sstevel@tonic-gate #include <fcntl.h> 417c478bd9Sstevel@tonic-gate #include <sys/types.h> 427c478bd9Sstevel@tonic-gate #include <sys/stat.h> 437c478bd9Sstevel@tonic-gate #include <sys/mman.h> 447c478bd9Sstevel@tonic-gate #include <stdlib.h> 457c478bd9Sstevel@tonic-gate #include <synch.h> 467c478bd9Sstevel@tonic-gate #include <pfmt.h> 477c478bd9Sstevel@tonic-gate #include <thread.h> 487c478bd9Sstevel@tonic-gate #include <unistd.h> 497c478bd9Sstevel@tonic-gate #include <errno.h> 507c478bd9Sstevel@tonic-gate #include <limits.h> 517c478bd9Sstevel@tonic-gate #include "../i18n/_locale.h" 527c478bd9Sstevel@tonic-gate #include "../i18n/_loc_path.h" 537c478bd9Sstevel@tonic-gate 547c478bd9Sstevel@tonic-gate #define MESSAGES "/LC_MESSAGES/" 557c478bd9Sstevel@tonic-gate static const char *def_locale = "C"; 567c478bd9Sstevel@tonic-gate static const char *not_found = "Message not found!!\n"; 577c478bd9Sstevel@tonic-gate static struct db_info *db_info; 587c478bd9Sstevel@tonic-gate static int db_count, maxdb; 597c478bd9Sstevel@tonic-gate 607c478bd9Sstevel@tonic-gate struct db_info { 617c478bd9Sstevel@tonic-gate char db_name[DB_NAME_LEN]; /* Name of the message file */ 627c478bd9Sstevel@tonic-gate uintptr_t addr; /* Virtual memory address */ 637c478bd9Sstevel@tonic-gate size_t length; 647c478bd9Sstevel@tonic-gate char *saved_locale; 657c478bd9Sstevel@tonic-gate char flag; 667c478bd9Sstevel@tonic-gate }; 677c478bd9Sstevel@tonic-gate 687c478bd9Sstevel@tonic-gate #define DB_EXIST 1 /* The catalogue exists */ 697c478bd9Sstevel@tonic-gate #define DB_OPEN 2 /* Already tried to open */ 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gate /* Minimum number of open catalogues */ 727c478bd9Sstevel@tonic-gate #define MINDB 3 737c478bd9Sstevel@tonic-gate 74e53d4db1Ssp149894 char cur_cat[DB_NAME_LEN]; 75e53d4db1Ssp149894 rwlock_t _rw_cur_cat = DEFAULTRWLOCK; 767c478bd9Sstevel@tonic-gate 777c478bd9Sstevel@tonic-gate 787c478bd9Sstevel@tonic-gate /* 797c478bd9Sstevel@tonic-gate * setcat(cat): Specify the default catalogue. 807c478bd9Sstevel@tonic-gate * Return a pointer to the local copy of the default catalogue 817c478bd9Sstevel@tonic-gate */ 827c478bd9Sstevel@tonic-gate const char * 837c478bd9Sstevel@tonic-gate setcat(const char *cat) 847c478bd9Sstevel@tonic-gate { 857c478bd9Sstevel@tonic-gate lrw_wrlock(&_rw_cur_cat); 867c478bd9Sstevel@tonic-gate if (cat) { 877c478bd9Sstevel@tonic-gate if (((strchr(cat, '/') != NULL)) || 887c478bd9Sstevel@tonic-gate ((strchr(cat, ':') != NULL))) { 897c478bd9Sstevel@tonic-gate cur_cat[0] = '\0'; 907c478bd9Sstevel@tonic-gate goto out; 917c478bd9Sstevel@tonic-gate } 927c478bd9Sstevel@tonic-gate (void) strncpy(cur_cat, cat, sizeof (cur_cat) - 1); 937c478bd9Sstevel@tonic-gate cur_cat[sizeof (cur_cat) - 1] = '\0'; 947c478bd9Sstevel@tonic-gate } 957c478bd9Sstevel@tonic-gate out: 967c478bd9Sstevel@tonic-gate lrw_unlock(&_rw_cur_cat); 977c478bd9Sstevel@tonic-gate return (cur_cat[0] ? cur_cat : NULL); 987c478bd9Sstevel@tonic-gate } 997c478bd9Sstevel@tonic-gate 1007c478bd9Sstevel@tonic-gate /* 1017c478bd9Sstevel@tonic-gate * load a message catalog which specified with current locale, 1027c478bd9Sstevel@tonic-gate * and catalog name. 1037c478bd9Sstevel@tonic-gate */ 1047c478bd9Sstevel@tonic-gate static struct db_info * 1057c478bd9Sstevel@tonic-gate load_db(const char *curloc, const char *catname, int *err) 1067c478bd9Sstevel@tonic-gate { 1077c478bd9Sstevel@tonic-gate char pathname[PATH_MAX]; 1087c478bd9Sstevel@tonic-gate struct stat64 sb; 1097c478bd9Sstevel@tonic-gate caddr_t addr; 1107c478bd9Sstevel@tonic-gate struct db_info *db; 1117c478bd9Sstevel@tonic-gate int fd; 1127c478bd9Sstevel@tonic-gate int i; 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate *err = 0; 1157c478bd9Sstevel@tonic-gate 1167c478bd9Sstevel@tonic-gate /* First time called, allocate space */ 1177c478bd9Sstevel@tonic-gate if (!db_info) { 1187c478bd9Sstevel@tonic-gate if ((db_info = 1197c478bd9Sstevel@tonic-gate libc_malloc(MINDB * sizeof (struct db_info))) == NULL) { 1207c478bd9Sstevel@tonic-gate *err = 1; 1217c478bd9Sstevel@tonic-gate return (NULL); 1227c478bd9Sstevel@tonic-gate } 1237c478bd9Sstevel@tonic-gate maxdb = MINDB; 1247c478bd9Sstevel@tonic-gate } 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate for (i = 0; i < db_count; i++) { 1277c478bd9Sstevel@tonic-gate if (db_info[i].flag == 0) 1287c478bd9Sstevel@tonic-gate break; 1297c478bd9Sstevel@tonic-gate } 1307c478bd9Sstevel@tonic-gate /* New catalogue */ 1317c478bd9Sstevel@tonic-gate if (i == db_count) { 1327c478bd9Sstevel@tonic-gate if (db_count == maxdb) { 1337c478bd9Sstevel@tonic-gate if ((db = libc_realloc(db_info, 1347c478bd9Sstevel@tonic-gate ++maxdb * sizeof (struct db_info))) == NULL) { 1357c478bd9Sstevel@tonic-gate *err = 1; 1367c478bd9Sstevel@tonic-gate return (NULL); 1377c478bd9Sstevel@tonic-gate } 1387c478bd9Sstevel@tonic-gate db_info = db; 1397c478bd9Sstevel@tonic-gate } 1407c478bd9Sstevel@tonic-gate db_count++; 1417c478bd9Sstevel@tonic-gate } 1427c478bd9Sstevel@tonic-gate db = &db_info[i]; 1437c478bd9Sstevel@tonic-gate db->flag = 0; 1447c478bd9Sstevel@tonic-gate (void) strcpy(db->db_name, catname); 1457c478bd9Sstevel@tonic-gate db->saved_locale = libc_strdup(curloc); 1467c478bd9Sstevel@tonic-gate if (db->saved_locale == NULL) { 1477c478bd9Sstevel@tonic-gate *err = 1; 1487c478bd9Sstevel@tonic-gate return (NULL); 1497c478bd9Sstevel@tonic-gate } 1507c478bd9Sstevel@tonic-gate db->flag = DB_OPEN; 1517c478bd9Sstevel@tonic-gate if (snprintf(pathname, sizeof (pathname), 1527c478bd9Sstevel@tonic-gate _DFLT_LOC_PATH "%s" MESSAGES "%s", 1537c478bd9Sstevel@tonic-gate db->saved_locale, db->db_name) >= sizeof (pathname)) { 1547c478bd9Sstevel@tonic-gate /* 1557c478bd9Sstevel@tonic-gate * We won't set err here, because an invalid locale is not 1567c478bd9Sstevel@tonic-gate * the fatal condition, but we can fall back to "C" 1577c478bd9Sstevel@tonic-gate * locale. 1587c478bd9Sstevel@tonic-gate */ 1597c478bd9Sstevel@tonic-gate return (NULL); 1607c478bd9Sstevel@tonic-gate } 1617c478bd9Sstevel@tonic-gate if ((fd = open(pathname, O_RDONLY)) != -1 && 1627c478bd9Sstevel@tonic-gate fstat64(fd, &sb) != -1 && 1637c478bd9Sstevel@tonic-gate (addr = mmap(0, (size_t)sb.st_size, PROT_READ, MAP_SHARED, 1647c478bd9Sstevel@tonic-gate fd, 0)) != MAP_FAILED) { 1657c478bd9Sstevel@tonic-gate db->flag |= DB_EXIST; 1667c478bd9Sstevel@tonic-gate db->addr = (uintptr_t)addr; 1677c478bd9Sstevel@tonic-gate db->length = (size_t)sb.st_size; 1687c478bd9Sstevel@tonic-gate } 1697c478bd9Sstevel@tonic-gate if (fd != -1) 1707c478bd9Sstevel@tonic-gate (void) close(fd); 1717c478bd9Sstevel@tonic-gate return (db); 1727c478bd9Sstevel@tonic-gate } 1737c478bd9Sstevel@tonic-gate 1747c478bd9Sstevel@tonic-gate /* 1757c478bd9Sstevel@tonic-gate * unmap the message catalog, and release the db_info slot. 1767c478bd9Sstevel@tonic-gate */ 1777c478bd9Sstevel@tonic-gate static void 1787c478bd9Sstevel@tonic-gate unload_db(struct db_info *db) 1797c478bd9Sstevel@tonic-gate { 1807c478bd9Sstevel@tonic-gate if ((db->flag & (DB_OPEN|DB_EXIST)) == 1817c478bd9Sstevel@tonic-gate (DB_OPEN|DB_EXIST)) { 1827c478bd9Sstevel@tonic-gate (void) munmap((caddr_t)db->addr, db->length); 1837c478bd9Sstevel@tonic-gate } 1847c478bd9Sstevel@tonic-gate db->flag = 0; 1857c478bd9Sstevel@tonic-gate if (db->saved_locale) 1867c478bd9Sstevel@tonic-gate libc_free(db->saved_locale); 1877c478bd9Sstevel@tonic-gate db->saved_locale = NULL; 1887c478bd9Sstevel@tonic-gate } 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate /* 1917c478bd9Sstevel@tonic-gate * go through the db_info, and find out a db_info slot regarding 1927c478bd9Sstevel@tonic-gate * the given current locale and catalog name. 1937c478bd9Sstevel@tonic-gate * If db is not NULL, then search will start from top of the array, 1947c478bd9Sstevel@tonic-gate * otherwise it will start from the next of given db. 1957c478bd9Sstevel@tonic-gate * If curloc is set to NULL, then return a cache without regards of 1967c478bd9Sstevel@tonic-gate * locale. 1977c478bd9Sstevel@tonic-gate */ 1987c478bd9Sstevel@tonic-gate static struct db_info * 1997c478bd9Sstevel@tonic-gate lookup_cache(struct db_info *db, const char *curloc, const char *catname) 2007c478bd9Sstevel@tonic-gate { 2017c478bd9Sstevel@tonic-gate if (db_info == NULL) 2027c478bd9Sstevel@tonic-gate return (NULL); 2037c478bd9Sstevel@tonic-gate 2047c478bd9Sstevel@tonic-gate if (db == NULL) 2057c478bd9Sstevel@tonic-gate db = db_info; 2067c478bd9Sstevel@tonic-gate else 2077c478bd9Sstevel@tonic-gate db++; 2087c478bd9Sstevel@tonic-gate 2097c478bd9Sstevel@tonic-gate for (; db < &db_info[db_count]; db++) { 2107c478bd9Sstevel@tonic-gate if (db->flag == 0) 2117c478bd9Sstevel@tonic-gate continue; 2127c478bd9Sstevel@tonic-gate if (strcmp(db->db_name, catname) == 0) { 2137c478bd9Sstevel@tonic-gate if (curloc == NULL || 2147c478bd9Sstevel@tonic-gate (db->saved_locale != NULL && 2157c478bd9Sstevel@tonic-gate strcmp(db->saved_locale, curloc) == 0)) { 2167c478bd9Sstevel@tonic-gate return (db); 2177c478bd9Sstevel@tonic-gate } 2187c478bd9Sstevel@tonic-gate } 2197c478bd9Sstevel@tonic-gate } 2207c478bd9Sstevel@tonic-gate return (NULL); 2217c478bd9Sstevel@tonic-gate } 2227c478bd9Sstevel@tonic-gate 2237c478bd9Sstevel@tonic-gate static int 2247c478bd9Sstevel@tonic-gate valid_msg(struct db_info *db, int id) 2257c478bd9Sstevel@tonic-gate { 2267c478bd9Sstevel@tonic-gate if (db == NULL || (db->flag & DB_EXIST) == 0) 2277c478bd9Sstevel@tonic-gate return (0); 2287c478bd9Sstevel@tonic-gate 2297c478bd9Sstevel@tonic-gate /* catalog has been loaded */ 2307c478bd9Sstevel@tonic-gate if (id != 0 && id <= *(int *)(db->addr)) 2317c478bd9Sstevel@tonic-gate return (1); 2327c478bd9Sstevel@tonic-gate 2337c478bd9Sstevel@tonic-gate /* not a valid id */ 2347c478bd9Sstevel@tonic-gate return (0); 2357c478bd9Sstevel@tonic-gate } 2367c478bd9Sstevel@tonic-gate 2377c478bd9Sstevel@tonic-gate static char * 2387c478bd9Sstevel@tonic-gate msg(struct db_info *db, int id) 2397c478bd9Sstevel@tonic-gate { 2407c478bd9Sstevel@tonic-gate return ((char *)(db->addr + *(int *)(db->addr + 2417c478bd9Sstevel@tonic-gate id * sizeof (int)))); 2427c478bd9Sstevel@tonic-gate } 2437c478bd9Sstevel@tonic-gate 2447c478bd9Sstevel@tonic-gate /* 2457c478bd9Sstevel@tonic-gate * __gtxt(catname, id, dflt): Return a pointer to a message. 2467c478bd9Sstevel@tonic-gate * catname is the name of the catalog. If null, the default catalog is 2477c478bd9Sstevel@tonic-gate * used. 2487c478bd9Sstevel@tonic-gate * id is the numeric id of the message in the catalogue 2497c478bd9Sstevel@tonic-gate * dflt is the default message. 2507c478bd9Sstevel@tonic-gate * 2517c478bd9Sstevel@tonic-gate * Information about non-existent catalogues is kept in db_info, in 2527c478bd9Sstevel@tonic-gate * such a way that subsequent calls with the same catalogue do not 2537c478bd9Sstevel@tonic-gate * try to open the catalogue again. 2547c478bd9Sstevel@tonic-gate */ 2557c478bd9Sstevel@tonic-gate const char * 2567c478bd9Sstevel@tonic-gate __gtxt(const char *catname, int id, const char *dflt) 2577c478bd9Sstevel@tonic-gate { 2587c478bd9Sstevel@tonic-gate char *curloc; 2597c478bd9Sstevel@tonic-gate struct db_info *db; 2607c478bd9Sstevel@tonic-gate int err; 261*b599bd93SRobert Mustacchi locale_t loc; 2627c478bd9Sstevel@tonic-gate 2637c478bd9Sstevel@tonic-gate /* Check for invalid message id */ 2647c478bd9Sstevel@tonic-gate if (id < 0) 2657c478bd9Sstevel@tonic-gate return (not_found); 2667c478bd9Sstevel@tonic-gate if (id == 0) 2677c478bd9Sstevel@tonic-gate return ((dflt && *dflt) ? dflt : not_found); 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate /* 2707c478bd9Sstevel@tonic-gate * If catalogue is unspecified, use default catalogue. 2717c478bd9Sstevel@tonic-gate * No catalogue at all is an error 2727c478bd9Sstevel@tonic-gate */ 2737c478bd9Sstevel@tonic-gate if (!catname || !*catname) { 2747c478bd9Sstevel@tonic-gate lrw_rdlock(&_rw_cur_cat); 2757c478bd9Sstevel@tonic-gate if (cur_cat == NULL || !*cur_cat) { 2767c478bd9Sstevel@tonic-gate lrw_unlock(&_rw_cur_cat); 2777c478bd9Sstevel@tonic-gate return (not_found); 2787c478bd9Sstevel@tonic-gate } 2797c478bd9Sstevel@tonic-gate catname = cur_cat; 2807c478bd9Sstevel@tonic-gate lrw_unlock(&_rw_cur_cat); 2817c478bd9Sstevel@tonic-gate } 2827c478bd9Sstevel@tonic-gate 283*b599bd93SRobert Mustacchi loc = uselocale(NULL); 284*b599bd93SRobert Mustacchi curloc = current_locale(loc, LC_MESSAGES); 2857c478bd9Sstevel@tonic-gate 2867c478bd9Sstevel@tonic-gate /* First look up the cache */ 2877c478bd9Sstevel@tonic-gate db = lookup_cache(NULL, curloc, catname); 2887c478bd9Sstevel@tonic-gate if (db != NULL) { 2897c478bd9Sstevel@tonic-gate /* 2907c478bd9Sstevel@tonic-gate * The catalog has been loaded, and if id seems valid, 2917c478bd9Sstevel@tonic-gate * then just return. 2927c478bd9Sstevel@tonic-gate */ 2937c478bd9Sstevel@tonic-gate if (valid_msg(db, id)) 2947c478bd9Sstevel@tonic-gate return (msg(db, id)); 2957c478bd9Sstevel@tonic-gate 2967c478bd9Sstevel@tonic-gate /* 2977c478bd9Sstevel@tonic-gate * seems given id is out of bound or does not exist. In this 2987c478bd9Sstevel@tonic-gate * case, we need to look up a message for the "C" locale as 2997c478bd9Sstevel@tonic-gate * documented in the man page. 3007c478bd9Sstevel@tonic-gate */ 3017c478bd9Sstevel@tonic-gate db = lookup_cache(NULL, def_locale, catname); 3027c478bd9Sstevel@tonic-gate if (db == NULL) { 3037c478bd9Sstevel@tonic-gate /* 3047c478bd9Sstevel@tonic-gate * Even the message catalog for the "C" has not been 3057c478bd9Sstevel@tonic-gate * loaded. 3067c478bd9Sstevel@tonic-gate */ 3077c478bd9Sstevel@tonic-gate db = load_db(def_locale, catname, &err); 3087c478bd9Sstevel@tonic-gate if (err) 3097c478bd9Sstevel@tonic-gate return (not_found); 3107c478bd9Sstevel@tonic-gate } 3117c478bd9Sstevel@tonic-gate if (valid_msg(db, id)) 3127c478bd9Sstevel@tonic-gate return (msg(db, id)); 3137c478bd9Sstevel@tonic-gate /* no message found */ 3147c478bd9Sstevel@tonic-gate return ((dflt && *dflt) ? dflt : not_found); 3157c478bd9Sstevel@tonic-gate } 3167c478bd9Sstevel@tonic-gate 3177c478bd9Sstevel@tonic-gate /* 3187c478bd9Sstevel@tonic-gate * The catalog has not been loaded or even has not 3197c478bd9Sstevel@tonic-gate * attempted to be loaded, invalidate all caches related to 3207c478bd9Sstevel@tonic-gate * the catname for possibly different locale. 3217c478bd9Sstevel@tonic-gate */ 3227c478bd9Sstevel@tonic-gate db = NULL; 3237c478bd9Sstevel@tonic-gate while ((db = lookup_cache(db, NULL, catname)) != NULL) 3247c478bd9Sstevel@tonic-gate unload_db(db); 3257c478bd9Sstevel@tonic-gate 3267c478bd9Sstevel@tonic-gate /* 3277c478bd9Sstevel@tonic-gate * load a message catalog for the requested locale. 3287c478bd9Sstevel@tonic-gate */ 3297c478bd9Sstevel@tonic-gate db = load_db(curloc, catname, &err); 3307c478bd9Sstevel@tonic-gate if (err) 3317c478bd9Sstevel@tonic-gate return (not_found); 3327c478bd9Sstevel@tonic-gate if (valid_msg(db, id)) 3337c478bd9Sstevel@tonic-gate return (msg(db, id)); 3347c478bd9Sstevel@tonic-gate 3357c478bd9Sstevel@tonic-gate /* 3367c478bd9Sstevel@tonic-gate * If the requested catalog is either not exist or message 3377c478bd9Sstevel@tonic-gate * id is invalid, then try to load from "C" locale. 3387c478bd9Sstevel@tonic-gate */ 3397c478bd9Sstevel@tonic-gate db = load_db(def_locale, catname, &err); 3407c478bd9Sstevel@tonic-gate if (err) 3417c478bd9Sstevel@tonic-gate return (not_found); 3427c478bd9Sstevel@tonic-gate 3437c478bd9Sstevel@tonic-gate if (valid_msg(db, id)) 3447c478bd9Sstevel@tonic-gate return (msg(db, id)); 3457c478bd9Sstevel@tonic-gate 3467c478bd9Sstevel@tonic-gate /* no message found */ 3477c478bd9Sstevel@tonic-gate return ((dflt && *dflt) ? dflt : not_found); 3487c478bd9Sstevel@tonic-gate } 349