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 5*64e3e6f9Scraigm * Common Development and Distribution License (the "License"). 6*64e3e6f9Scraigm * 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 */ 217c478bd9Sstevel@tonic-gate /* 22*64e3e6f9Scraigm * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */ 297c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 307c478bd9Sstevel@tonic-gate 317c478bd9Sstevel@tonic-gate 327c478bd9Sstevel@tonic-gate /* __gtxt(): Common part to gettxt() and pfmt() */ 337c478bd9Sstevel@tonic-gate 34*64e3e6f9Scraigm #pragma weak setcat = _setcat 35*64e3e6f9Scraigm 367c478bd9Sstevel@tonic-gate #include "synonyms.h" 377c478bd9Sstevel@tonic-gate #include "libc.h" 387c478bd9Sstevel@tonic-gate #include <mtlib.h> 397c478bd9Sstevel@tonic-gate #include <sys/types.h> 407c478bd9Sstevel@tonic-gate #include <string.h> 417c478bd9Sstevel@tonic-gate #include <locale.h> 427c478bd9Sstevel@tonic-gate #include <fcntl.h> 437c478bd9Sstevel@tonic-gate #include <sys/types.h> 447c478bd9Sstevel@tonic-gate #include <sys/stat.h> 457c478bd9Sstevel@tonic-gate #include <sys/mman.h> 467c478bd9Sstevel@tonic-gate #include <stdlib.h> 477c478bd9Sstevel@tonic-gate #include <synch.h> 487c478bd9Sstevel@tonic-gate #include <pfmt.h> 497c478bd9Sstevel@tonic-gate #include <thread.h> 507c478bd9Sstevel@tonic-gate #include <unistd.h> 517c478bd9Sstevel@tonic-gate #include <errno.h> 527c478bd9Sstevel@tonic-gate #include <limits.h> 537c478bd9Sstevel@tonic-gate #include "../i18n/_locale.h" 547c478bd9Sstevel@tonic-gate #include "../i18n/_loc_path.h" 557c478bd9Sstevel@tonic-gate 567c478bd9Sstevel@tonic-gate #define MESSAGES "/LC_MESSAGES/" 577c478bd9Sstevel@tonic-gate static const char *def_locale = "C"; 587c478bd9Sstevel@tonic-gate static const char *not_found = "Message not found!!\n"; 597c478bd9Sstevel@tonic-gate static struct db_info *db_info; 607c478bd9Sstevel@tonic-gate static int db_count, maxdb; 617c478bd9Sstevel@tonic-gate 627c478bd9Sstevel@tonic-gate struct db_info { 637c478bd9Sstevel@tonic-gate char db_name[DB_NAME_LEN]; /* Name of the message file */ 647c478bd9Sstevel@tonic-gate uintptr_t addr; /* Virtual memory address */ 657c478bd9Sstevel@tonic-gate size_t length; 667c478bd9Sstevel@tonic-gate char *saved_locale; 677c478bd9Sstevel@tonic-gate char flag; 687c478bd9Sstevel@tonic-gate }; 697c478bd9Sstevel@tonic-gate 707c478bd9Sstevel@tonic-gate #define DB_EXIST 1 /* The catalogue exists */ 717c478bd9Sstevel@tonic-gate #define DB_OPEN 2 /* Already tried to open */ 727c478bd9Sstevel@tonic-gate 737c478bd9Sstevel@tonic-gate /* Minimum number of open catalogues */ 747c478bd9Sstevel@tonic-gate #define MINDB 3 757c478bd9Sstevel@tonic-gate 767c478bd9Sstevel@tonic-gate static char cur_cat[DB_NAME_LEN]; 777c478bd9Sstevel@tonic-gate static rwlock_t _rw_cur_cat = DEFAULTRWLOCK; 787c478bd9Sstevel@tonic-gate 797c478bd9Sstevel@tonic-gate 807c478bd9Sstevel@tonic-gate /* 817c478bd9Sstevel@tonic-gate * setcat(cat): Specify the default catalogue. 827c478bd9Sstevel@tonic-gate * Return a pointer to the local copy of the default catalogue 837c478bd9Sstevel@tonic-gate */ 847c478bd9Sstevel@tonic-gate const char * 857c478bd9Sstevel@tonic-gate setcat(const char *cat) 867c478bd9Sstevel@tonic-gate { 877c478bd9Sstevel@tonic-gate lrw_wrlock(&_rw_cur_cat); 887c478bd9Sstevel@tonic-gate if (cat) { 897c478bd9Sstevel@tonic-gate if (((strchr(cat, '/') != NULL)) || 907c478bd9Sstevel@tonic-gate ((strchr(cat, ':') != NULL))) { 917c478bd9Sstevel@tonic-gate cur_cat[0] = '\0'; 927c478bd9Sstevel@tonic-gate goto out; 937c478bd9Sstevel@tonic-gate } 947c478bd9Sstevel@tonic-gate (void) strncpy(cur_cat, cat, sizeof (cur_cat) - 1); 957c478bd9Sstevel@tonic-gate cur_cat[sizeof (cur_cat) - 1] = '\0'; 967c478bd9Sstevel@tonic-gate } 977c478bd9Sstevel@tonic-gate out: 987c478bd9Sstevel@tonic-gate lrw_unlock(&_rw_cur_cat); 997c478bd9Sstevel@tonic-gate return (cur_cat[0] ? cur_cat : NULL); 1007c478bd9Sstevel@tonic-gate } 1017c478bd9Sstevel@tonic-gate 1027c478bd9Sstevel@tonic-gate /* 1037c478bd9Sstevel@tonic-gate * load a message catalog which specified with current locale, 1047c478bd9Sstevel@tonic-gate * and catalog name. 1057c478bd9Sstevel@tonic-gate */ 1067c478bd9Sstevel@tonic-gate static struct db_info * 1077c478bd9Sstevel@tonic-gate load_db(const char *curloc, const char *catname, int *err) 1087c478bd9Sstevel@tonic-gate { 1097c478bd9Sstevel@tonic-gate char pathname[PATH_MAX]; 1107c478bd9Sstevel@tonic-gate struct stat64 sb; 1117c478bd9Sstevel@tonic-gate caddr_t addr; 1127c478bd9Sstevel@tonic-gate struct db_info *db; 1137c478bd9Sstevel@tonic-gate int fd; 1147c478bd9Sstevel@tonic-gate int i; 1157c478bd9Sstevel@tonic-gate 1167c478bd9Sstevel@tonic-gate *err = 0; 1177c478bd9Sstevel@tonic-gate 1187c478bd9Sstevel@tonic-gate /* First time called, allocate space */ 1197c478bd9Sstevel@tonic-gate if (!db_info) { 1207c478bd9Sstevel@tonic-gate if ((db_info = 1217c478bd9Sstevel@tonic-gate libc_malloc(MINDB * sizeof (struct db_info))) == NULL) { 1227c478bd9Sstevel@tonic-gate *err = 1; 1237c478bd9Sstevel@tonic-gate return (NULL); 1247c478bd9Sstevel@tonic-gate } 1257c478bd9Sstevel@tonic-gate maxdb = MINDB; 1267c478bd9Sstevel@tonic-gate } 1277c478bd9Sstevel@tonic-gate 1287c478bd9Sstevel@tonic-gate for (i = 0; i < db_count; i++) { 1297c478bd9Sstevel@tonic-gate if (db_info[i].flag == 0) 1307c478bd9Sstevel@tonic-gate break; 1317c478bd9Sstevel@tonic-gate } 1327c478bd9Sstevel@tonic-gate /* New catalogue */ 1337c478bd9Sstevel@tonic-gate if (i == db_count) { 1347c478bd9Sstevel@tonic-gate if (db_count == maxdb) { 1357c478bd9Sstevel@tonic-gate if ((db = libc_realloc(db_info, 1367c478bd9Sstevel@tonic-gate ++maxdb * sizeof (struct db_info))) == NULL) { 1377c478bd9Sstevel@tonic-gate *err = 1; 1387c478bd9Sstevel@tonic-gate return (NULL); 1397c478bd9Sstevel@tonic-gate } 1407c478bd9Sstevel@tonic-gate db_info = db; 1417c478bd9Sstevel@tonic-gate } 1427c478bd9Sstevel@tonic-gate db_count++; 1437c478bd9Sstevel@tonic-gate } 1447c478bd9Sstevel@tonic-gate db = &db_info[i]; 1457c478bd9Sstevel@tonic-gate db->flag = 0; 1467c478bd9Sstevel@tonic-gate (void) strcpy(db->db_name, catname); 1477c478bd9Sstevel@tonic-gate db->saved_locale = libc_strdup(curloc); 1487c478bd9Sstevel@tonic-gate if (db->saved_locale == NULL) { 1497c478bd9Sstevel@tonic-gate *err = 1; 1507c478bd9Sstevel@tonic-gate return (NULL); 1517c478bd9Sstevel@tonic-gate } 1527c478bd9Sstevel@tonic-gate db->flag = DB_OPEN; 1537c478bd9Sstevel@tonic-gate if (snprintf(pathname, sizeof (pathname), 1547c478bd9Sstevel@tonic-gate _DFLT_LOC_PATH "%s" MESSAGES "%s", 1557c478bd9Sstevel@tonic-gate db->saved_locale, db->db_name) >= sizeof (pathname)) { 1567c478bd9Sstevel@tonic-gate /* 1577c478bd9Sstevel@tonic-gate * We won't set err here, because an invalid locale is not 1587c478bd9Sstevel@tonic-gate * the fatal condition, but we can fall back to "C" 1597c478bd9Sstevel@tonic-gate * locale. 1607c478bd9Sstevel@tonic-gate */ 1617c478bd9Sstevel@tonic-gate return (NULL); 1627c478bd9Sstevel@tonic-gate } 1637c478bd9Sstevel@tonic-gate if ((fd = open(pathname, O_RDONLY)) != -1 && 1647c478bd9Sstevel@tonic-gate fstat64(fd, &sb) != -1 && 1657c478bd9Sstevel@tonic-gate (addr = mmap(0, (size_t)sb.st_size, PROT_READ, MAP_SHARED, 1667c478bd9Sstevel@tonic-gate fd, 0)) != MAP_FAILED) { 1677c478bd9Sstevel@tonic-gate db->flag |= DB_EXIST; 1687c478bd9Sstevel@tonic-gate db->addr = (uintptr_t)addr; 1697c478bd9Sstevel@tonic-gate db->length = (size_t)sb.st_size; 1707c478bd9Sstevel@tonic-gate } 1717c478bd9Sstevel@tonic-gate if (fd != -1) 1727c478bd9Sstevel@tonic-gate (void) close(fd); 1737c478bd9Sstevel@tonic-gate return (db); 1747c478bd9Sstevel@tonic-gate } 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gate /* 1777c478bd9Sstevel@tonic-gate * unmap the message catalog, and release the db_info slot. 1787c478bd9Sstevel@tonic-gate */ 1797c478bd9Sstevel@tonic-gate static void 1807c478bd9Sstevel@tonic-gate unload_db(struct db_info *db) 1817c478bd9Sstevel@tonic-gate { 1827c478bd9Sstevel@tonic-gate if ((db->flag & (DB_OPEN|DB_EXIST)) == 1837c478bd9Sstevel@tonic-gate (DB_OPEN|DB_EXIST)) { 1847c478bd9Sstevel@tonic-gate (void) munmap((caddr_t)db->addr, db->length); 1857c478bd9Sstevel@tonic-gate } 1867c478bd9Sstevel@tonic-gate db->flag = 0; 1877c478bd9Sstevel@tonic-gate if (db->saved_locale) 1887c478bd9Sstevel@tonic-gate libc_free(db->saved_locale); 1897c478bd9Sstevel@tonic-gate db->saved_locale = NULL; 1907c478bd9Sstevel@tonic-gate } 1917c478bd9Sstevel@tonic-gate 1927c478bd9Sstevel@tonic-gate /* 1937c478bd9Sstevel@tonic-gate * go through the db_info, and find out a db_info slot regarding 1947c478bd9Sstevel@tonic-gate * the given current locale and catalog name. 1957c478bd9Sstevel@tonic-gate * If db is not NULL, then search will start from top of the array, 1967c478bd9Sstevel@tonic-gate * otherwise it will start from the next of given db. 1977c478bd9Sstevel@tonic-gate * If curloc is set to NULL, then return a cache without regards of 1987c478bd9Sstevel@tonic-gate * locale. 1997c478bd9Sstevel@tonic-gate */ 2007c478bd9Sstevel@tonic-gate static struct db_info * 2017c478bd9Sstevel@tonic-gate lookup_cache(struct db_info *db, const char *curloc, const char *catname) 2027c478bd9Sstevel@tonic-gate { 2037c478bd9Sstevel@tonic-gate if (db_info == NULL) 2047c478bd9Sstevel@tonic-gate return (NULL); 2057c478bd9Sstevel@tonic-gate 2067c478bd9Sstevel@tonic-gate if (db == NULL) 2077c478bd9Sstevel@tonic-gate db = db_info; 2087c478bd9Sstevel@tonic-gate else 2097c478bd9Sstevel@tonic-gate db++; 2107c478bd9Sstevel@tonic-gate 2117c478bd9Sstevel@tonic-gate for (; db < &db_info[db_count]; db++) { 2127c478bd9Sstevel@tonic-gate if (db->flag == 0) 2137c478bd9Sstevel@tonic-gate continue; 2147c478bd9Sstevel@tonic-gate if (strcmp(db->db_name, catname) == 0) { 2157c478bd9Sstevel@tonic-gate if (curloc == NULL || 2167c478bd9Sstevel@tonic-gate (db->saved_locale != NULL && 2177c478bd9Sstevel@tonic-gate strcmp(db->saved_locale, curloc) == 0)) { 2187c478bd9Sstevel@tonic-gate return (db); 2197c478bd9Sstevel@tonic-gate } 2207c478bd9Sstevel@tonic-gate } 2217c478bd9Sstevel@tonic-gate } 2227c478bd9Sstevel@tonic-gate return (NULL); 2237c478bd9Sstevel@tonic-gate } 2247c478bd9Sstevel@tonic-gate 2257c478bd9Sstevel@tonic-gate static int 2267c478bd9Sstevel@tonic-gate valid_msg(struct db_info *db, int id) 2277c478bd9Sstevel@tonic-gate { 2287c478bd9Sstevel@tonic-gate if (db == NULL || (db->flag & DB_EXIST) == 0) 2297c478bd9Sstevel@tonic-gate return (0); 2307c478bd9Sstevel@tonic-gate 2317c478bd9Sstevel@tonic-gate /* catalog has been loaded */ 2327c478bd9Sstevel@tonic-gate if (id != 0 && id <= *(int *)(db->addr)) 2337c478bd9Sstevel@tonic-gate return (1); 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate /* not a valid id */ 2367c478bd9Sstevel@tonic-gate return (0); 2377c478bd9Sstevel@tonic-gate } 2387c478bd9Sstevel@tonic-gate 2397c478bd9Sstevel@tonic-gate static char * 2407c478bd9Sstevel@tonic-gate msg(struct db_info *db, int id) 2417c478bd9Sstevel@tonic-gate { 2427c478bd9Sstevel@tonic-gate return ((char *)(db->addr + *(int *)(db->addr + 2437c478bd9Sstevel@tonic-gate id * sizeof (int)))); 2447c478bd9Sstevel@tonic-gate } 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate /* 2477c478bd9Sstevel@tonic-gate * __gtxt(catname, id, dflt): Return a pointer to a message. 2487c478bd9Sstevel@tonic-gate * catname is the name of the catalog. If null, the default catalog is 2497c478bd9Sstevel@tonic-gate * used. 2507c478bd9Sstevel@tonic-gate * id is the numeric id of the message in the catalogue 2517c478bd9Sstevel@tonic-gate * dflt is the default message. 2527c478bd9Sstevel@tonic-gate * 2537c478bd9Sstevel@tonic-gate * Information about non-existent catalogues is kept in db_info, in 2547c478bd9Sstevel@tonic-gate * such a way that subsequent calls with the same catalogue do not 2557c478bd9Sstevel@tonic-gate * try to open the catalogue again. 2567c478bd9Sstevel@tonic-gate */ 2577c478bd9Sstevel@tonic-gate const char * 2587c478bd9Sstevel@tonic-gate __gtxt(const char *catname, int id, const char *dflt) 2597c478bd9Sstevel@tonic-gate { 2607c478bd9Sstevel@tonic-gate char *curloc; 2617c478bd9Sstevel@tonic-gate struct db_info *db; 2627c478bd9Sstevel@tonic-gate int err; 2637c478bd9Sstevel@tonic-gate 2647c478bd9Sstevel@tonic-gate /* Check for invalid message id */ 2657c478bd9Sstevel@tonic-gate if (id < 0) 2667c478bd9Sstevel@tonic-gate return (not_found); 2677c478bd9Sstevel@tonic-gate if (id == 0) 2687c478bd9Sstevel@tonic-gate return ((dflt && *dflt) ? dflt : not_found); 2697c478bd9Sstevel@tonic-gate 2707c478bd9Sstevel@tonic-gate /* 2717c478bd9Sstevel@tonic-gate * If catalogue is unspecified, use default catalogue. 2727c478bd9Sstevel@tonic-gate * No catalogue at all is an error 2737c478bd9Sstevel@tonic-gate */ 2747c478bd9Sstevel@tonic-gate if (!catname || !*catname) { 2757c478bd9Sstevel@tonic-gate lrw_rdlock(&_rw_cur_cat); 2767c478bd9Sstevel@tonic-gate if (cur_cat == NULL || !*cur_cat) { 2777c478bd9Sstevel@tonic-gate lrw_unlock(&_rw_cur_cat); 2787c478bd9Sstevel@tonic-gate return (not_found); 2797c478bd9Sstevel@tonic-gate } 2807c478bd9Sstevel@tonic-gate catname = cur_cat; 2817c478bd9Sstevel@tonic-gate lrw_unlock(&_rw_cur_cat); 2827c478bd9Sstevel@tonic-gate } 2837c478bd9Sstevel@tonic-gate 2847c478bd9Sstevel@tonic-gate curloc = setlocale(LC_MESSAGES, NULL); 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