xref: /onnv-gate/usr/src/lib/libc/port/gen/gtxt.c (revision 6812:febeba71273d)
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