10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
52552Scraigm * Common Development and Distribution License (the "License").
62552Scraigm * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
21*6812Sraf
220Sstevel@tonic-gate /*
23*6812Sraf * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
240Sstevel@tonic-gate * Use is subject to license terms.
250Sstevel@tonic-gate */
260Sstevel@tonic-gate
270Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */
280Sstevel@tonic-gate /* All Rights Reserved */
290Sstevel@tonic-gate
30*6812Sraf #pragma ident "%Z%%M% %I% %E% SMI"
310Sstevel@tonic-gate
320Sstevel@tonic-gate /* __gtxt(): Common part to gettxt() and pfmt() */
330Sstevel@tonic-gate
34*6812Sraf #pragma weak _setcat = setcat
352552Scraigm
36*6812Sraf #include "lint.h"
370Sstevel@tonic-gate #include "libc.h"
380Sstevel@tonic-gate #include <mtlib.h>
390Sstevel@tonic-gate #include <sys/types.h>
400Sstevel@tonic-gate #include <string.h>
410Sstevel@tonic-gate #include <locale.h>
420Sstevel@tonic-gate #include <fcntl.h>
430Sstevel@tonic-gate #include <sys/types.h>
440Sstevel@tonic-gate #include <sys/stat.h>
450Sstevel@tonic-gate #include <sys/mman.h>
460Sstevel@tonic-gate #include <stdlib.h>
470Sstevel@tonic-gate #include <synch.h>
480Sstevel@tonic-gate #include <pfmt.h>
490Sstevel@tonic-gate #include <thread.h>
500Sstevel@tonic-gate #include <unistd.h>
510Sstevel@tonic-gate #include <errno.h>
520Sstevel@tonic-gate #include <limits.h>
530Sstevel@tonic-gate #include "../i18n/_locale.h"
540Sstevel@tonic-gate #include "../i18n/_loc_path.h"
550Sstevel@tonic-gate
560Sstevel@tonic-gate #define MESSAGES "/LC_MESSAGES/"
570Sstevel@tonic-gate static const char *def_locale = "C";
580Sstevel@tonic-gate static const char *not_found = "Message not found!!\n";
590Sstevel@tonic-gate static struct db_info *db_info;
600Sstevel@tonic-gate static int db_count, maxdb;
610Sstevel@tonic-gate
620Sstevel@tonic-gate struct db_info {
630Sstevel@tonic-gate char db_name[DB_NAME_LEN]; /* Name of the message file */
640Sstevel@tonic-gate uintptr_t addr; /* Virtual memory address */
650Sstevel@tonic-gate size_t length;
660Sstevel@tonic-gate char *saved_locale;
670Sstevel@tonic-gate char flag;
680Sstevel@tonic-gate };
690Sstevel@tonic-gate
700Sstevel@tonic-gate #define DB_EXIST 1 /* The catalogue exists */
710Sstevel@tonic-gate #define DB_OPEN 2 /* Already tried to open */
720Sstevel@tonic-gate
730Sstevel@tonic-gate /* Minimum number of open catalogues */
740Sstevel@tonic-gate #define MINDB 3
750Sstevel@tonic-gate
764447Ssp149894 char cur_cat[DB_NAME_LEN];
774447Ssp149894 rwlock_t _rw_cur_cat = DEFAULTRWLOCK;
780Sstevel@tonic-gate
790Sstevel@tonic-gate
800Sstevel@tonic-gate /*
810Sstevel@tonic-gate * setcat(cat): Specify the default catalogue.
820Sstevel@tonic-gate * Return a pointer to the local copy of the default catalogue
830Sstevel@tonic-gate */
840Sstevel@tonic-gate const char *
setcat(const char * cat)850Sstevel@tonic-gate setcat(const char *cat)
860Sstevel@tonic-gate {
870Sstevel@tonic-gate lrw_wrlock(&_rw_cur_cat);
880Sstevel@tonic-gate if (cat) {
890Sstevel@tonic-gate if (((strchr(cat, '/') != NULL)) ||
900Sstevel@tonic-gate ((strchr(cat, ':') != NULL))) {
910Sstevel@tonic-gate cur_cat[0] = '\0';
920Sstevel@tonic-gate goto out;
930Sstevel@tonic-gate }
940Sstevel@tonic-gate (void) strncpy(cur_cat, cat, sizeof (cur_cat) - 1);
950Sstevel@tonic-gate cur_cat[sizeof (cur_cat) - 1] = '\0';
960Sstevel@tonic-gate }
970Sstevel@tonic-gate out:
980Sstevel@tonic-gate lrw_unlock(&_rw_cur_cat);
990Sstevel@tonic-gate return (cur_cat[0] ? cur_cat : NULL);
1000Sstevel@tonic-gate }
1010Sstevel@tonic-gate
1020Sstevel@tonic-gate /*
1030Sstevel@tonic-gate * load a message catalog which specified with current locale,
1040Sstevel@tonic-gate * and catalog name.
1050Sstevel@tonic-gate */
1060Sstevel@tonic-gate static struct db_info *
load_db(const char * curloc,const char * catname,int * err)1070Sstevel@tonic-gate load_db(const char *curloc, const char *catname, int *err)
1080Sstevel@tonic-gate {
1090Sstevel@tonic-gate char pathname[PATH_MAX];
1100Sstevel@tonic-gate struct stat64 sb;
1110Sstevel@tonic-gate caddr_t addr;
1120Sstevel@tonic-gate struct db_info *db;
1130Sstevel@tonic-gate int fd;
1140Sstevel@tonic-gate int i;
1150Sstevel@tonic-gate
1160Sstevel@tonic-gate *err = 0;
1170Sstevel@tonic-gate
1180Sstevel@tonic-gate /* First time called, allocate space */
1190Sstevel@tonic-gate if (!db_info) {
1200Sstevel@tonic-gate if ((db_info =
1210Sstevel@tonic-gate libc_malloc(MINDB * sizeof (struct db_info))) == NULL) {
1220Sstevel@tonic-gate *err = 1;
1230Sstevel@tonic-gate return (NULL);
1240Sstevel@tonic-gate }
1250Sstevel@tonic-gate maxdb = MINDB;
1260Sstevel@tonic-gate }
1270Sstevel@tonic-gate
1280Sstevel@tonic-gate for (i = 0; i < db_count; i++) {
1290Sstevel@tonic-gate if (db_info[i].flag == 0)
1300Sstevel@tonic-gate break;
1310Sstevel@tonic-gate }
1320Sstevel@tonic-gate /* New catalogue */
1330Sstevel@tonic-gate if (i == db_count) {
1340Sstevel@tonic-gate if (db_count == maxdb) {
1350Sstevel@tonic-gate if ((db = libc_realloc(db_info,
1360Sstevel@tonic-gate ++maxdb * sizeof (struct db_info))) == NULL) {
1370Sstevel@tonic-gate *err = 1;
1380Sstevel@tonic-gate return (NULL);
1390Sstevel@tonic-gate }
1400Sstevel@tonic-gate db_info = db;
1410Sstevel@tonic-gate }
1420Sstevel@tonic-gate db_count++;
1430Sstevel@tonic-gate }
1440Sstevel@tonic-gate db = &db_info[i];
1450Sstevel@tonic-gate db->flag = 0;
1460Sstevel@tonic-gate (void) strcpy(db->db_name, catname);
1470Sstevel@tonic-gate db->saved_locale = libc_strdup(curloc);
1480Sstevel@tonic-gate if (db->saved_locale == NULL) {
1490Sstevel@tonic-gate *err = 1;
1500Sstevel@tonic-gate return (NULL);
1510Sstevel@tonic-gate }
1520Sstevel@tonic-gate db->flag = DB_OPEN;
1530Sstevel@tonic-gate if (snprintf(pathname, sizeof (pathname),
1544447Ssp149894 _DFLT_LOC_PATH "%s" MESSAGES "%s",
1554447Ssp149894 db->saved_locale, db->db_name) >= sizeof (pathname)) {
1560Sstevel@tonic-gate /*
1570Sstevel@tonic-gate * We won't set err here, because an invalid locale is not
1580Sstevel@tonic-gate * the fatal condition, but we can fall back to "C"
1590Sstevel@tonic-gate * locale.
1600Sstevel@tonic-gate */
1610Sstevel@tonic-gate return (NULL);
1620Sstevel@tonic-gate }
1630Sstevel@tonic-gate if ((fd = open(pathname, O_RDONLY)) != -1 &&
1644447Ssp149894 fstat64(fd, &sb) != -1 &&
1654447Ssp149894 (addr = mmap(0, (size_t)sb.st_size, PROT_READ, MAP_SHARED,
1664447Ssp149894 fd, 0)) != MAP_FAILED) {
1670Sstevel@tonic-gate db->flag |= DB_EXIST;
1680Sstevel@tonic-gate db->addr = (uintptr_t)addr;
1690Sstevel@tonic-gate db->length = (size_t)sb.st_size;
1700Sstevel@tonic-gate }
1710Sstevel@tonic-gate if (fd != -1)
1720Sstevel@tonic-gate (void) close(fd);
1730Sstevel@tonic-gate return (db);
1740Sstevel@tonic-gate }
1750Sstevel@tonic-gate
1760Sstevel@tonic-gate /*
1770Sstevel@tonic-gate * unmap the message catalog, and release the db_info slot.
1780Sstevel@tonic-gate */
1790Sstevel@tonic-gate static void
unload_db(struct db_info * db)1800Sstevel@tonic-gate unload_db(struct db_info *db)
1810Sstevel@tonic-gate {
1820Sstevel@tonic-gate if ((db->flag & (DB_OPEN|DB_EXIST)) ==
1834447Ssp149894 (DB_OPEN|DB_EXIST)) {
1840Sstevel@tonic-gate (void) munmap((caddr_t)db->addr, db->length);
1850Sstevel@tonic-gate }
1860Sstevel@tonic-gate db->flag = 0;
1870Sstevel@tonic-gate if (db->saved_locale)
1880Sstevel@tonic-gate libc_free(db->saved_locale);
1890Sstevel@tonic-gate db->saved_locale = NULL;
1900Sstevel@tonic-gate }
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate /*
1930Sstevel@tonic-gate * go through the db_info, and find out a db_info slot regarding
1940Sstevel@tonic-gate * the given current locale and catalog name.
1950Sstevel@tonic-gate * If db is not NULL, then search will start from top of the array,
1960Sstevel@tonic-gate * otherwise it will start from the next of given db.
1970Sstevel@tonic-gate * If curloc is set to NULL, then return a cache without regards of
1980Sstevel@tonic-gate * locale.
1990Sstevel@tonic-gate */
2000Sstevel@tonic-gate static struct db_info *
lookup_cache(struct db_info * db,const char * curloc,const char * catname)2010Sstevel@tonic-gate lookup_cache(struct db_info *db, const char *curloc, const char *catname)
2020Sstevel@tonic-gate {
2030Sstevel@tonic-gate if (db_info == NULL)
2040Sstevel@tonic-gate return (NULL);
2050Sstevel@tonic-gate
2060Sstevel@tonic-gate if (db == NULL)
2070Sstevel@tonic-gate db = db_info;
2080Sstevel@tonic-gate else
2090Sstevel@tonic-gate db++;
2100Sstevel@tonic-gate
2110Sstevel@tonic-gate for (; db < &db_info[db_count]; db++) {
2120Sstevel@tonic-gate if (db->flag == 0)
2130Sstevel@tonic-gate continue;
2140Sstevel@tonic-gate if (strcmp(db->db_name, catname) == 0) {
2150Sstevel@tonic-gate if (curloc == NULL ||
2164447Ssp149894 (db->saved_locale != NULL &&
2174447Ssp149894 strcmp(db->saved_locale, curloc) == 0)) {
2180Sstevel@tonic-gate return (db);
2190Sstevel@tonic-gate }
2200Sstevel@tonic-gate }
2210Sstevel@tonic-gate }
2220Sstevel@tonic-gate return (NULL);
2230Sstevel@tonic-gate }
2240Sstevel@tonic-gate
2250Sstevel@tonic-gate static int
valid_msg(struct db_info * db,int id)2260Sstevel@tonic-gate valid_msg(struct db_info *db, int id)
2270Sstevel@tonic-gate {
2280Sstevel@tonic-gate if (db == NULL || (db->flag & DB_EXIST) == 0)
2290Sstevel@tonic-gate return (0);
2300Sstevel@tonic-gate
2310Sstevel@tonic-gate /* catalog has been loaded */
2320Sstevel@tonic-gate if (id != 0 && id <= *(int *)(db->addr))
2330Sstevel@tonic-gate return (1);
2340Sstevel@tonic-gate
2350Sstevel@tonic-gate /* not a valid id */
2360Sstevel@tonic-gate return (0);
2370Sstevel@tonic-gate }
2380Sstevel@tonic-gate
2390Sstevel@tonic-gate static char *
msg(struct db_info * db,int id)2400Sstevel@tonic-gate msg(struct db_info *db, int id)
2410Sstevel@tonic-gate {
2420Sstevel@tonic-gate return ((char *)(db->addr + *(int *)(db->addr +
2434447Ssp149894 id * sizeof (int))));
2440Sstevel@tonic-gate }
2450Sstevel@tonic-gate
2460Sstevel@tonic-gate /*
2470Sstevel@tonic-gate * __gtxt(catname, id, dflt): Return a pointer to a message.
2480Sstevel@tonic-gate * catname is the name of the catalog. If null, the default catalog is
2490Sstevel@tonic-gate * used.
2500Sstevel@tonic-gate * id is the numeric id of the message in the catalogue
2510Sstevel@tonic-gate * dflt is the default message.
2520Sstevel@tonic-gate *
2530Sstevel@tonic-gate * Information about non-existent catalogues is kept in db_info, in
2540Sstevel@tonic-gate * such a way that subsequent calls with the same catalogue do not
2550Sstevel@tonic-gate * try to open the catalogue again.
2560Sstevel@tonic-gate */
2570Sstevel@tonic-gate const char *
__gtxt(const char * catname,int id,const char * dflt)2580Sstevel@tonic-gate __gtxt(const char *catname, int id, const char *dflt)
2590Sstevel@tonic-gate {
2600Sstevel@tonic-gate char *curloc;
2610Sstevel@tonic-gate struct db_info *db;
2620Sstevel@tonic-gate int err;
2630Sstevel@tonic-gate
2640Sstevel@tonic-gate /* Check for invalid message id */
2650Sstevel@tonic-gate if (id < 0)
2660Sstevel@tonic-gate return (not_found);
2670Sstevel@tonic-gate if (id == 0)
2680Sstevel@tonic-gate return ((dflt && *dflt) ? dflt : not_found);
2690Sstevel@tonic-gate
2700Sstevel@tonic-gate /*
2710Sstevel@tonic-gate * If catalogue is unspecified, use default catalogue.
2720Sstevel@tonic-gate * No catalogue at all is an error
2730Sstevel@tonic-gate */
2740Sstevel@tonic-gate if (!catname || !*catname) {
2750Sstevel@tonic-gate lrw_rdlock(&_rw_cur_cat);
2760Sstevel@tonic-gate if (cur_cat == NULL || !*cur_cat) {
2770Sstevel@tonic-gate lrw_unlock(&_rw_cur_cat);
2780Sstevel@tonic-gate return (not_found);
2790Sstevel@tonic-gate }
2800Sstevel@tonic-gate catname = cur_cat;
2810Sstevel@tonic-gate lrw_unlock(&_rw_cur_cat);
2820Sstevel@tonic-gate }
2830Sstevel@tonic-gate
2840Sstevel@tonic-gate curloc = setlocale(LC_MESSAGES, NULL);
2850Sstevel@tonic-gate
2860Sstevel@tonic-gate /* First look up the cache */
2870Sstevel@tonic-gate db = lookup_cache(NULL, curloc, catname);
2880Sstevel@tonic-gate if (db != NULL) {
2890Sstevel@tonic-gate /*
2900Sstevel@tonic-gate * The catalog has been loaded, and if id seems valid,
2910Sstevel@tonic-gate * then just return.
2920Sstevel@tonic-gate */
2930Sstevel@tonic-gate if (valid_msg(db, id))
2940Sstevel@tonic-gate return (msg(db, id));
2950Sstevel@tonic-gate
2960Sstevel@tonic-gate /*
2970Sstevel@tonic-gate * seems given id is out of bound or does not exist. In this
2980Sstevel@tonic-gate * case, we need to look up a message for the "C" locale as
2990Sstevel@tonic-gate * documented in the man page.
3000Sstevel@tonic-gate */
3010Sstevel@tonic-gate db = lookup_cache(NULL, def_locale, catname);
3020Sstevel@tonic-gate if (db == NULL) {
3030Sstevel@tonic-gate /*
3040Sstevel@tonic-gate * Even the message catalog for the "C" has not been
3050Sstevel@tonic-gate * loaded.
3060Sstevel@tonic-gate */
3070Sstevel@tonic-gate db = load_db(def_locale, catname, &err);
3080Sstevel@tonic-gate if (err)
3090Sstevel@tonic-gate return (not_found);
3100Sstevel@tonic-gate }
3110Sstevel@tonic-gate if (valid_msg(db, id))
3120Sstevel@tonic-gate return (msg(db, id));
3130Sstevel@tonic-gate /* no message found */
3140Sstevel@tonic-gate return ((dflt && *dflt) ? dflt : not_found);
3150Sstevel@tonic-gate }
3160Sstevel@tonic-gate
3170Sstevel@tonic-gate /*
3180Sstevel@tonic-gate * The catalog has not been loaded or even has not
3190Sstevel@tonic-gate * attempted to be loaded, invalidate all caches related to
3200Sstevel@tonic-gate * the catname for possibly different locale.
3210Sstevel@tonic-gate */
3220Sstevel@tonic-gate db = NULL;
3230Sstevel@tonic-gate while ((db = lookup_cache(db, NULL, catname)) != NULL)
3240Sstevel@tonic-gate unload_db(db);
3250Sstevel@tonic-gate
3260Sstevel@tonic-gate /*
3270Sstevel@tonic-gate * load a message catalog for the requested locale.
3280Sstevel@tonic-gate */
3290Sstevel@tonic-gate db = load_db(curloc, catname, &err);
3300Sstevel@tonic-gate if (err)
3310Sstevel@tonic-gate return (not_found);
3320Sstevel@tonic-gate if (valid_msg(db, id))
3330Sstevel@tonic-gate return (msg(db, id));
3340Sstevel@tonic-gate
3350Sstevel@tonic-gate /*
3360Sstevel@tonic-gate * If the requested catalog is either not exist or message
3370Sstevel@tonic-gate * id is invalid, then try to load from "C" locale.
3380Sstevel@tonic-gate */
3390Sstevel@tonic-gate db = load_db(def_locale, catname, &err);
3400Sstevel@tonic-gate if (err)
3410Sstevel@tonic-gate return (not_found);
3420Sstevel@tonic-gate
3430Sstevel@tonic-gate if (valid_msg(db, id))
3440Sstevel@tonic-gate return (msg(db, id));
3450Sstevel@tonic-gate
3460Sstevel@tonic-gate /* no message found */
3470Sstevel@tonic-gate return ((dflt && *dflt) ? dflt : not_found);
3480Sstevel@tonic-gate }
349