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