#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];
uintptr_t addr;
size_t length;
char *saved_locale;
char flag;
};
#define DB_EXIST 1
#define DB_OPEN 2
#define MINDB 3
char cur_cat[DB_NAME_LEN];
rwlock_t _rw_cur_cat = DEFAULTRWLOCK;
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);
}
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;
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;
}
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)) {
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);
}
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;
}
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);
if (id != 0 && id <= *(int *)(db->addr))
return (1);
return (0);
}
static char *
msg(struct db_info *db, int id)
{
return ((char *)(db->addr + *(int *)(db->addr +
id * sizeof (int))));
}
const char *
__gtxt(const char *catname, int id, const char *dflt)
{
char *curloc;
struct db_info *db;
int err;
locale_t loc;
if (id < 0)
return (not_found);
if (id == 0)
return ((dflt && *dflt) ? dflt : not_found);
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);
}
loc = uselocale(NULL);
curloc = current_locale(loc, LC_MESSAGES);
db = lookup_cache(NULL, curloc, catname);
if (db != NULL) {
if (valid_msg(db, id))
return (msg(db, id));
db = lookup_cache(NULL, def_locale, catname);
if (db == NULL) {
db = load_db(def_locale, catname, &err);
if (err)
return (not_found);
}
if (valid_msg(db, id))
return (msg(db, id));
return ((dflt && *dflt) ? dflt : not_found);
}
db = NULL;
while ((db = lookup_cache(db, NULL, catname)) != NULL)
unload_db(db);
db = load_db(curloc, catname, &err);
if (err)
return (not_found);
if (valid_msg(db, id))
return (msg(db, id));
db = load_db(def_locale, catname, &err);
if (err)
return (not_found);
if (valid_msg(db, id))
return (msg(db, id));
return ((dflt && *dflt) ? dflt : not_found);
}