usr/src/lib/libc/port/gen/gtxt.c
author Jon Tibble <meths@btinternet.com>
Thu, 09 Dec 2010 22:32:39 +0100
changeset 13255 4afa820d78b9
parent 6812 febeba71273d
permissions -rw-r--r--
298 SPARC build fails in smt_pause.o 478 Build needs fixing for pkgdepend flag day Reviewed by: [email protected] Reviewed by: [email protected] Reviewed by: [email protected] Approved by: [email protected]

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1988 AT&T	*/
/*	  All Rights Reserved  	*/

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/* __gtxt(): Common part to gettxt() and pfmt()	*/

#pragma	weak _setcat = setcat

#include "lint.h"
#include "libc.h"
#include <mtlib.h>
#include <sys/types.h>
#include <string.h>
#include <locale.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <synch.h>
#include <pfmt.h>
#include <thread.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include "../i18n/_locale.h"
#include "../i18n/_loc_path.h"

#define	MESSAGES "/LC_MESSAGES/"
static const char *def_locale = "C";
static const char *not_found = "Message not found!!\n";
static struct db_info *db_info;
static int db_count, maxdb;

struct db_info {
	char	db_name[DB_NAME_LEN];	/* Name of the message file */
	uintptr_t	addr;		/* Virtual memory address   */
	size_t	length;
	char	*saved_locale;
	char	flag;
};

#define	DB_EXIST	1		/* The catalogue exists	   */
#define	DB_OPEN		2		/* Already tried to open   */

/* Minimum number of open catalogues */
#define	MINDB		3

char cur_cat[DB_NAME_LEN];
rwlock_t _rw_cur_cat = DEFAULTRWLOCK;


/*
 * setcat(cat): Specify the default catalogue.
 * Return a pointer to the local copy of the default catalogue
 */
const char *
setcat(const char *cat)
{
	lrw_wrlock(&_rw_cur_cat);
	if (cat) {
		if (((strchr(cat, '/') != NULL)) ||
		    ((strchr(cat, ':') != NULL))) {
			cur_cat[0] = '\0';
			goto out;
		}
		(void) strncpy(cur_cat, cat, sizeof (cur_cat) - 1);
		cur_cat[sizeof (cur_cat) - 1] = '\0';
	}
out:
	lrw_unlock(&_rw_cur_cat);
	return (cur_cat[0] ? cur_cat : NULL);
}

/*
 * load a message catalog which specified with current locale,
 * and catalog name.
 */
static struct db_info *
load_db(const char *curloc, const char *catname, int *err)
{
	char pathname[PATH_MAX];
	struct	stat64 sb;
	caddr_t	addr;
	struct db_info *db;
	int fd;
	int i;

	*err = 0;

	/* First time called, allocate space */
	if (!db_info) {
		if ((db_info =
		    libc_malloc(MINDB * sizeof (struct db_info))) == NULL) {
			*err = 1;
			return (NULL);
		}
		maxdb = MINDB;
	}

	for (i = 0; i < db_count; i++) {
		if (db_info[i].flag == 0)
			break;
	}
	/* New catalogue */
	if (i == db_count) {
		if (db_count == maxdb) {
			if ((db = libc_realloc(db_info,
			    ++maxdb * sizeof (struct db_info))) == NULL) {
				*err = 1;
				return (NULL);
			}
			db_info = db;
		}
		db_count++;
	}
	db = &db_info[i];
	db->flag = 0;
	(void) strcpy(db->db_name, catname);
	db->saved_locale = libc_strdup(curloc);
	if (db->saved_locale == NULL) {
		*err = 1;
		return (NULL);
	}
	db->flag = DB_OPEN;
	if (snprintf(pathname, sizeof (pathname),
	    _DFLT_LOC_PATH "%s" MESSAGES "%s",
	    db->saved_locale, db->db_name) >= sizeof (pathname)) {
		/*
		 * We won't set err here, because an invalid locale is not
		 * the fatal condition, but we can fall back to "C"
		 * locale.
		 */
		return (NULL);
	}
	if ((fd = open(pathname, O_RDONLY)) != -1 &&
	    fstat64(fd, &sb) != -1 &&
	    (addr = mmap(0, (size_t)sb.st_size, PROT_READ, MAP_SHARED,
	    fd, 0)) != MAP_FAILED) {
		db->flag |= DB_EXIST;
		db->addr = (uintptr_t)addr;
		db->length = (size_t)sb.st_size;
	}
	if (fd != -1)
		(void) close(fd);
	return (db);
}

/*
 * unmap the message catalog, and release the db_info slot.
 */
static void
unload_db(struct db_info *db)
{
	if ((db->flag & (DB_OPEN|DB_EXIST)) ==
	    (DB_OPEN|DB_EXIST)) {
		(void) munmap((caddr_t)db->addr, db->length);
	}
	db->flag = 0;
	if (db->saved_locale)
		libc_free(db->saved_locale);
	db->saved_locale = NULL;
}

/*
 * go through the db_info, and find out a db_info slot regarding
 * the given current locale and catalog name.
 * If db is not NULL, then search will start from top of the array,
 * otherwise it will start from the next of given db.
 * If curloc is set to NULL, then return a cache without regards of
 * locale.
 */
static struct db_info *
lookup_cache(struct db_info *db, const char *curloc, const char *catname)
{
	if (db_info == NULL)
		return (NULL);

	if (db == NULL)
		db = db_info;
	else
		db++;

	for (; db < &db_info[db_count]; db++) {
		if (db->flag == 0)
			continue;
		if (strcmp(db->db_name, catname) == 0) {
			if (curloc == NULL ||
			    (db->saved_locale != NULL &&
			    strcmp(db->saved_locale, curloc) == 0)) {
				return (db);
			}
		}
	}
	return (NULL);
}

static int
valid_msg(struct db_info *db, int id)
{
	if (db == NULL || (db->flag & DB_EXIST) == 0)
		return (0);

	/* catalog has been loaded */
	if (id != 0 && id <= *(int *)(db->addr))
		return (1);

	/* not a valid id */
	return (0);
}

static char *
msg(struct db_info *db, int id)
{
	return ((char *)(db->addr + *(int *)(db->addr +
	    id * sizeof (int))));
}

/*
 * __gtxt(catname, id, dflt): Return a pointer to a message.
 *	catname is the name of the catalog. If null, the default catalog is
 *		used.
 *	id is the numeric id of the message in the catalogue
 *	dflt is the default message.
 *
 *	Information about non-existent catalogues is kept in db_info, in
 *	such a way that subsequent calls with the same catalogue do not
 *	try to open the catalogue again.
 */
const char *
__gtxt(const char *catname, int id, const char *dflt)
{
	char	*curloc;
	struct db_info *db;
	int	err;

	/* Check for invalid message id */
	if (id < 0)
		return (not_found);
	if (id == 0)
		return ((dflt && *dflt) ? dflt : not_found);

	/*
	 * If catalogue is unspecified, use default catalogue.
	 * No catalogue at all is an error
	 */
	if (!catname || !*catname) {
		lrw_rdlock(&_rw_cur_cat);
		if (cur_cat == NULL || !*cur_cat) {
			lrw_unlock(&_rw_cur_cat);
			return (not_found);
		}
		catname = cur_cat;
		lrw_unlock(&_rw_cur_cat);
	}

	curloc = setlocale(LC_MESSAGES, NULL);

	/* First look up the cache */
	db = lookup_cache(NULL, curloc, catname);
	if (db != NULL) {
		/*
		 * The catalog has been loaded, and if id seems valid,
		 * then just return.
		 */
		if (valid_msg(db, id))
			return (msg(db, id));

		/*
		 * seems given id is out of bound or does not exist. In this
		 * case, we need to look up a message for the "C" locale as
		 * documented in the man page.
		 */
		db = lookup_cache(NULL, def_locale, catname);
		if (db == NULL) {
			/*
			 * Even the message catalog for the "C" has not been
			 * loaded.
			 */
			db = load_db(def_locale, catname, &err);
			if (err)
				return (not_found);
		}
		if (valid_msg(db, id))
			return (msg(db, id));
		/* no message found */
		return ((dflt && *dflt) ? dflt : not_found);
	}

	/*
	 * The catalog has not been loaded or even has not
	 * attempted to be loaded, invalidate all caches related to
	 * the catname for possibly different locale.
	 */
	db = NULL;
	while ((db = lookup_cache(db, NULL, catname)) != NULL)
		unload_db(db);

	/*
	 * load a message catalog for the requested locale.
	 */
	db = load_db(curloc, catname, &err);
	if (err)
		return (not_found);
	if (valid_msg(db, id))
		return (msg(db, id));

	/*
	 * If the requested catalog is either not exist or message
	 * id is invalid, then try to load from "C" locale.
	 */
	db = load_db(def_locale, catname, &err);
	if (err)
		return (not_found);

	if (valid_msg(db, id))
		return (msg(db, id));

	/* no message found */
	return ((dflt && *dflt) ? dflt : not_found);
}