gettxt.c revision 7257d1b4d25bfac0c802847390e98a464fd787ac
/*
* 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"
#pragma weak _gettxt = gettxt
#include "lint.h"
#include "libc.h"
#include <mtlib.h>
#include <ctype.h>
#include <string.h>
#include <locale.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <pfmt.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <thread.h>
#include "../i18n/_locale.h"
#include "../i18n/_loc_path.h"
#define MESSAGES "/LC_MESSAGES/"
#define DB_NAME_LEN 15
#define handle_return(s) \
((char *)((s) != NULL && *(s) != '\0' ? (s) : not_found))
extern char cur_cat[];
extern rwlock_t _rw_cur_cat;
static mutex_t gettxt_lock = DEFAULTMUTEX;
static const char *not_found = "Message not found!!\n";
static const char *loc_C = "C";
struct db_list {
char db_name[DB_NAME_LEN]; /* name of the message file */
uintptr_t addr; /* virtual memory address */
struct db_list *next;
};
struct db_cache {
char *loc;
struct db_list *info;
struct db_cache *next;
};
static struct db_cache *db_cache;
char *
gettxt(const char *msg_id, const char *dflt_str)
{
struct db_cache *dbc;
struct db_list *dbl;
char msgfile[DB_NAME_LEN]; /* name of static shared library */
int msgnum; /* message number */
char pathname[PATH_MAX]; /* full pathname to message file */
int fd;
struct stat64 sb;
void *addr;
char *tokp;
size_t name_len;
char *curloc;
if ((msg_id == NULL) || (*msg_id == '\0')) {
return (handle_return(dflt_str));
}
/* parse msg_id */
if (((tokp = strchr(msg_id, ':')) == NULL) || *(tokp+1) == '\0')
return (handle_return(dflt_str));
if ((name_len = (tokp - msg_id)) >= DB_NAME_LEN)
return (handle_return(dflt_str));
if (name_len > 0) {
(void) strncpy(msgfile, msg_id, name_len);
msgfile[name_len] = '\0';
} else {
lrw_rdlock(&_rw_cur_cat);
if (cur_cat == NULL || *cur_cat == '\0') {
lrw_unlock(&_rw_cur_cat);
return (handle_return(dflt_str));
}
/*
* We know the following strcpy is safe.
*/
(void) strcpy(msgfile, cur_cat);
lrw_unlock(&_rw_cur_cat);
}
while (*++tokp) {
if (!isdigit((unsigned char)*tokp))
return (handle_return(dflt_str));
}
msgnum = atoi(msg_id + name_len + 1);
curloc = setlocale(LC_MESSAGES, NULL);
lmutex_lock(&gettxt_lock);
try_C:
dbc = db_cache;
while (dbc) {
if (strcmp(curloc, dbc->loc) == 0) {
dbl = dbc->info;
while (dbl) {
if (strcmp(msgfile, dbl->db_name) == 0) {
/* msgfile found */
lmutex_unlock(&gettxt_lock);
goto msgfile_found;
}
dbl = dbl->next;
}
/* not found */
break;
}
dbc = dbc->next;
}
if (dbc == NULL) {
/* new locale */
if ((dbc = lmalloc(sizeof (struct db_cache))) == NULL) {
lmutex_unlock(&gettxt_lock);
return (handle_return(dflt_str));
}
if ((dbc->loc = lmalloc(strlen(curloc) + 1)) == NULL) {
lfree(dbc, sizeof (struct db_cache));
lmutex_unlock(&gettxt_lock);
return (handle_return(dflt_str));
}
dbc->info = NULL;
(void) strcpy(dbc->loc, curloc);
/* connect dbc to the dbc list */
dbc->next = db_cache;
db_cache = dbc;
}
if ((dbl = lmalloc(sizeof (struct db_list))) == NULL) {
lmutex_unlock(&gettxt_lock);
return (handle_return(dflt_str));
}
if (snprintf(pathname, sizeof (pathname),
_DFLT_LOC_PATH "%s" MESSAGES "%s", dbc->loc, msgfile) >=
sizeof (pathname)) {
lfree(dbl, sizeof (struct db_list));
lmutex_unlock(&gettxt_lock);
return (handle_return(dflt_str));
}
if ((fd = open(pathname, O_RDONLY)) == -1 ||
fstat64(fd, &sb) == -1 ||
(addr = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_SHARED,
fd, 0L)) == MAP_FAILED) {
if (fd != -1)
(void) close(fd);
lfree(dbl, sizeof (struct db_list));
if (strcmp(dbc->loc, "C") == 0) {
lmutex_unlock(&gettxt_lock);
return (handle_return(dflt_str));
}
/* Change locale to C */
curloc = (char *)loc_C;
goto try_C;
}
(void) close(fd);
/* save file name, memory address, fd and size */
(void) strcpy(dbl->db_name, msgfile);
dbl->addr = (uintptr_t)addr;
/* connect dbl to the dbc->info list */
dbl->next = dbc->info;
dbc->info = dbl;
lmutex_unlock(&gettxt_lock);
msgfile_found:
/* check if msgnum out of domain */
if (msgnum <= 0 || msgnum > *(int *)dbl->addr)
return (handle_return(dflt_str));
/* return pointer to message */
return ((char *)(dbl->addr +
*(int *)(dbl->addr + msgnum * sizeof (int))));
}