2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 1993, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/* Copyright (c) 1988 AT&T */
2N/A/* All Rights Reserved */
2N/A
2N/A/* __gtxt(): Common part to gettxt() and pfmt() */
2N/A
2N/A#pragma weak _setcat = setcat
2N/A
2N/A#include "lint.h"
2N/A#include "libc.h"
2N/A#include <mtlib.h>
2N/A#include <sys/types.h>
2N/A#include <string.h>
2N/A#include <locale.h>
2N/A#include <fcntl.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/mman.h>
2N/A#include <stdlib.h>
2N/A#include <synch.h>
2N/A#include <pfmt.h>
2N/A#include <thread.h>
2N/A#include <unistd.h>
2N/A#include <errno.h>
2N/A#include <limits.h>
2N/A#include "../i18n/_locale.h"
2N/A#include "../i18n/_loc_path.h"
2N/A
2N/A#define MESSAGES "/LC_MESSAGES/"
2N/Astatic const char *def_locale = "C";
2N/Astatic const char *not_found = "Message not found!!\n";
2N/Astatic struct db_info *db_info;
2N/Astatic int db_count, maxdb;
2N/A
2N/Astruct db_info {
2N/A char db_name[DB_NAME_LEN]; /* Name of the message file */
2N/A uintptr_t addr; /* Virtual memory address */
2N/A size_t length;
2N/A char *saved_locale;
2N/A char flag;
2N/A};
2N/A
2N/A#define DB_EXIST 1 /* The catalogue exists */
2N/A#define DB_OPEN 2 /* Already tried to open */
2N/A
2N/A/* Minimum number of open catalogues */
2N/A#define MINDB 3
2N/A
2N/Achar cur_cat[DB_NAME_LEN];
2N/Arwlock_t _rw_cur_cat = DEFAULTRWLOCK;
2N/A
2N/A
2N/A/*
2N/A * setcat(cat): Specify the default catalogue.
2N/A * Return a pointer to the local copy of the default catalogue
2N/A */
2N/Aconst char *
2N/Asetcat(const char *cat)
2N/A{
2N/A lrw_wrlock(&_rw_cur_cat);
2N/A if (cat) {
2N/A if (((strchr(cat, '/') != NULL)) ||
2N/A ((strchr(cat, ':') != NULL))) {
2N/A cur_cat[0] = '\0';
2N/A goto out;
2N/A }
2N/A (void) strncpy(cur_cat, cat, sizeof (cur_cat) - 1);
2N/A cur_cat[sizeof (cur_cat) - 1] = '\0';
2N/A }
2N/Aout:
2N/A lrw_unlock(&_rw_cur_cat);
2N/A return (cur_cat[0] ? cur_cat : NULL);
2N/A}
2N/A
2N/A/*
2N/A * load a message catalog which specified with current locale,
2N/A * and catalog name.
2N/A */
2N/Astatic struct db_info *
2N/Aload_db(const char *curloc, const char *catname, int *err)
2N/A{
2N/A char pathname[PATH_MAX];
2N/A struct stat64 sb;
2N/A caddr_t addr;
2N/A struct db_info *db;
2N/A int fd;
2N/A int i;
2N/A
2N/A *err = 0;
2N/A
2N/A /* First time called, allocate space */
2N/A if (!db_info) {
2N/A if ((db_info =
2N/A libc_malloc(MINDB * sizeof (struct db_info))) == NULL) {
2N/A *err = 1;
2N/A return (NULL);
2N/A }
2N/A maxdb = MINDB;
2N/A }
2N/A
2N/A for (i = 0; i < db_count; i++) {
2N/A if (db_info[i].flag == 0)
2N/A break;
2N/A }
2N/A /* New catalogue */
2N/A if (i == db_count) {
2N/A if (db_count == maxdb) {
2N/A if ((db = libc_realloc(db_info,
2N/A ++maxdb * sizeof (struct db_info))) == NULL) {
2N/A *err = 1;
2N/A return (NULL);
2N/A }
2N/A db_info = db;
2N/A }
2N/A db_count++;
2N/A }
2N/A db = &db_info[i];
2N/A db->flag = 0;
2N/A (void) strcpy(db->db_name, catname);
2N/A db->saved_locale = libc_strdup(curloc);
2N/A if (db->saved_locale == NULL) {
2N/A *err = 1;
2N/A return (NULL);
2N/A }
2N/A db->flag = DB_OPEN;
2N/A if (snprintf(pathname, sizeof (pathname),
2N/A _DFLT_LOC_PATH "%s" MESSAGES "%s",
2N/A db->saved_locale, db->db_name) >= sizeof (pathname)) {
2N/A /*
2N/A * We won't set err here, because an invalid locale is not
2N/A * the fatal condition, but we can fall back to "C"
2N/A * locale.
2N/A */
2N/A return (NULL);
2N/A }
2N/A if ((fd = open(pathname, O_RDONLY)) != -1 &&
2N/A fstat64(fd, &sb) != -1 &&
2N/A (addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0L)) !=
2N/A MAP_FAILED) {
2N/A db->flag |= DB_EXIST;
2N/A db->addr = (uintptr_t)addr;
2N/A db->length = (size_t)sb.st_size;
2N/A }
2N/A if (fd != -1)
2N/A (void) close(fd);
2N/A return (db);
2N/A}
2N/A
2N/A/*
2N/A * unmap the message catalog, and release the db_info slot.
2N/A */
2N/Astatic void
2N/Aunload_db(struct db_info *db)
2N/A{
2N/A if ((db->flag & (DB_OPEN|DB_EXIST)) ==
2N/A (DB_OPEN|DB_EXIST)) {
2N/A (void) munmap((caddr_t)db->addr, db->length);
2N/A }
2N/A db->flag = 0;
2N/A if (db->saved_locale)
2N/A libc_free(db->saved_locale);
2N/A db->saved_locale = NULL;
2N/A}
2N/A
2N/A/*
2N/A * go through the db_info, and find out a db_info slot regarding
2N/A * the given current locale and catalog name.
2N/A * If db is not NULL, then search will start from top of the array,
2N/A * otherwise it will start from the next of given db.
2N/A * If curloc is set to NULL, then return a cache without regards of
2N/A * locale.
2N/A */
2N/Astatic struct db_info *
2N/Alookup_cache(struct db_info *db, const char *curloc, const char *catname)
2N/A{
2N/A if (db_info == NULL)
2N/A return (NULL);
2N/A
2N/A if (db == NULL)
2N/A db = db_info;
2N/A else
2N/A db++;
2N/A
2N/A for (; db < &db_info[db_count]; db++) {
2N/A if (db->flag == 0)
2N/A continue;
2N/A if (strcmp(db->db_name, catname) == 0) {
2N/A if (curloc == NULL ||
2N/A (db->saved_locale != NULL &&
2N/A strcmp(db->saved_locale, curloc) == 0)) {
2N/A return (db);
2N/A }
2N/A }
2N/A }
2N/A return (NULL);
2N/A}
2N/A
2N/Astatic int
2N/Avalid_msg(struct db_info *db, int id)
2N/A{
2N/A if (db == NULL || (db->flag & DB_EXIST) == 0)
2N/A return (0);
2N/A
2N/A /* catalog has been loaded */
2N/A if (id != 0 && id <= *(int *)(db->addr))
2N/A return (1);
2N/A
2N/A /* not a valid id */
2N/A return (0);
2N/A}
2N/A
2N/Astatic char *
2N/Amsg(struct db_info *db, int id)
2N/A{
2N/A return ((char *)(db->addr + *(int *)(db->addr +
2N/A id * sizeof (int))));
2N/A}
2N/A
2N/A/*
2N/A * __gtxt(catname, id, dflt): Return a pointer to a message.
2N/A * catname is the name of the catalog. If null, the default catalog is
2N/A * used.
2N/A * id is the numeric id of the message in the catalogue
2N/A * dflt is the default message.
2N/A *
2N/A * Information about non-existent catalogues is kept in db_info, in
2N/A * such a way that subsequent calls with the same catalogue do not
2N/A * try to open the catalogue again.
2N/A */
2N/Aconst char *
2N/A__gtxt(const char *catname, int id, const char *dflt)
2N/A{
2N/A char *curloc;
2N/A struct db_info *db;
2N/A int err;
2N/A
2N/A /* Check for invalid message id */
2N/A if (id < 0)
2N/A return (not_found);
2N/A if (id == 0)
2N/A return ((dflt && *dflt) ? dflt : not_found);
2N/A
2N/A /*
2N/A * If catalogue is unspecified, use default catalogue.
2N/A * No catalogue at all is an error
2N/A */
2N/A if (!catname || !*catname) {
2N/A lrw_rdlock(&_rw_cur_cat);
2N/A if (cur_cat == NULL || !*cur_cat) {
2N/A lrw_unlock(&_rw_cur_cat);
2N/A return (not_found);
2N/A }
2N/A catname = cur_cat;
2N/A lrw_unlock(&_rw_cur_cat);
2N/A }
2N/A
2N/A curloc = setlocale(LC_MESSAGES, NULL);
2N/A
2N/A /* First look up the cache */
2N/A db = lookup_cache(NULL, curloc, catname);
2N/A if (db != NULL) {
2N/A /*
2N/A * The catalog has been loaded, and if id seems valid,
2N/A * then just return.
2N/A */
2N/A if (valid_msg(db, id))
2N/A return (msg(db, id));
2N/A
2N/A /*
2N/A * seems given id is out of bound or does not exist. In this
2N/A * case, we need to look up a message for the "C" locale as
2N/A * documented in the man page.
2N/A */
2N/A db = lookup_cache(NULL, def_locale, catname);
2N/A if (db == NULL) {
2N/A /*
2N/A * Even the message catalog for the "C" has not been
2N/A * loaded.
2N/A */
2N/A db = load_db(def_locale, catname, &err);
2N/A if (err)
2N/A return (not_found);
2N/A }
2N/A if (valid_msg(db, id))
2N/A return (msg(db, id));
2N/A /* no message found */
2N/A return ((dflt && *dflt) ? dflt : not_found);
2N/A }
2N/A
2N/A /*
2N/A * The catalog has not been loaded or even has not
2N/A * attempted to be loaded, invalidate all caches related to
2N/A * the catname for possibly different locale.
2N/A */
2N/A db = NULL;
2N/A while ((db = lookup_cache(db, NULL, catname)) != NULL)
2N/A unload_db(db);
2N/A
2N/A /*
2N/A * load a message catalog for the requested locale.
2N/A */
2N/A db = load_db(curloc, catname, &err);
2N/A if (err)
2N/A return (not_found);
2N/A if (valid_msg(db, id))
2N/A return (msg(db, id));
2N/A
2N/A /*
2N/A * If the requested catalog is either not exist or message
2N/A * id is invalid, then try to load from "C" locale.
2N/A */
2N/A db = load_db(def_locale, catname, &err);
2N/A if (err)
2N/A return (not_found);
2N/A
2N/A if (valid_msg(db, id))
2N/A return (msg(db, id));
2N/A
2N/A /* no message found */
2N/A return ((dflt && *dflt) ? dflt : not_found);
2N/A}