#include "lint.h"
#include "mtlib.h"
#include <ctype.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <libintl.h>
#include <thread.h>
#include <synch.h>
#include <limits.h>
#include <unistd.h>
#include "libc.h"
#include "_loc_path.h"
#include "msgfmt.h"
#include "gettext.h"
#include "nlspath_checks.h"
static int process_nlspath(const char *, const char *,
const char *, char **);
static char *replace_nls_option(char *, const char *, char *,
char *, char *, char *, char *);
char *
_real_gettext_u(const char *domain, const char *msgid1, const char *msgid2,
unsigned long int ln, int category, int plural, locale_t loc)
{
char msgfile[MAXPATHLEN];
char mydomain[TEXTDOMAINMAX + 1];
char *cur_binding;
const char *cur_locale;
char *cur_domain, *result, *nlspath;
char *msgloc, *cb, *cur_domain_binding;
char *language;
unsigned int n = (unsigned int)ln;
uint32_t cur_domain_len, cblen;
uint32_t hash_domain;
struct msg_pack *mp, omp;
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** _real_gettext_u(\"%s\", \"%s\", "
"\"%s\", %d, %d, %d)\n",
domain ? domain : "NULL", msgid1 ? msgid1 : "NULL",
msgid2 ? msgid2 : "NULL", n, category, plural);
gprintf(0, "***************** global_gt: 0x%p\n", global_gt);
printgt(global_gt, 1);
#endif
if (msgid1 == NULL)
return (NULL);
mp = memset(&omp, 0, sizeof (omp));
if (loc == NULL)
loc = uselocale(NULL);
cur_locale = current_locale(loc, category);
language = getenv("LANGUAGE");
if (language) {
if (!*language || strchr(language, '/') != NULL) {
language = NULL;
}
}
mydomain[0] = '\0';
if (domain == NULL) {
cur_domain = _textdomain_u(NULL, mydomain);
} else if (!*domain) {
cur_domain = DEFAULT_DOMAIN;
} else {
cur_domain = (char *)domain;
}
hash_domain = get_hashid(cur_domain, &cur_domain_len);
if (cur_domain_len > TEXTDOMAINMAX) {
DFLTMSG(result, msgid1, msgid2, n, plural);
return (result);
}
nlspath = getenv("NLSPATH");
if (nlspath == NULL || !*nlspath) {
if ((*cur_locale == 'C') && (*(cur_locale + 1) == '\0')) {
DFLTMSG(result, msgid1, msgid2, n, plural);
return (result);
}
nlspath = NULL;
} else {
int ret;
msgloc = current_locale(loc, LC_MESSAGES);
ret = process_nlspath(cur_domain, msgloc,
(const char *)nlspath, &cur_binding);
if (ret == -1) {
DFLTMSG(result, msgid1, msgid2, n, plural);
return (result);
} else if (ret == 0) {
nlspath = NULL;
}
}
cur_domain_binding = _real_bindtextdomain_u(cur_domain,
NULL, TP_BINDING);
if (cur_domain_binding == NULL) {
DFLTMSG(result, msgid1, msgid2, n, plural);
return (result);
}
mp->msgid1 = msgid1;
mp->msgid2 = msgid2;
mp->msgfile = msgfile;
mp->domain = cur_domain;
mp->binding = cur_domain_binding;
mp->locale = cur_locale;
mp->language = language;
mp->domain_len = cur_domain_len;
mp->n = n;
mp->category = category;
mp->plural = plural;
mp->hash_domain = hash_domain;
if (nlspath) {
#ifdef GETTEXT_DEBUG
gprintf(0, "************************** examining NLSPATH\n");
gprintf(0, " cur_binding: \"%s\"\n",
cur_binding ? cur_binding : "(null)");
#endif
mp->nlsp = 1;
while (*cur_binding) {
cb = cur_binding;
while (*cur_binding != ':')
cur_binding++;
cblen = cur_binding - cb;
cur_binding++;
if (cblen >= MAXPATHLEN) {
DFLTMSG(result, msgid1, msgid2, n, plural);
return (result);
}
(void) memcpy(mp->msgfile, cb, cblen);
*(mp->msgfile + cblen) = '\0';
#ifdef GETTEXT_DEBUG
gprintf(0, "*******************"
"********************* \n");
gprintf(0, " msgfile: \"%s\"\n",
msgfile ? msgfile : "(null)");
gprintf(0, "*******************"
"********************* \n");
#endif
result = handle_mo(mp);
if (result) {
return (result);
}
}
}
mp->nlsp = 0;
mp->binding = cur_domain_binding;
if (language) {
char *ret_msg;
ret_msg = handle_lang(mp);
if (ret_msg != NULL) {
return (ret_msg);
}
mp->locale = cur_locale;
mp->status = 0;
}
#ifdef GETTEXT_DEBUG
*mp->msgfile = '\0';
#endif
if (mk_msgfile(mp) == NULL) {
DFLTMSG(result, msgid1, msgid2, n, plural);
return (result);
}
result = handle_mo(mp);
if (result) {
return (result);
}
DFLTMSG(result, msgid1, msgid2, n, plural);
return (result);
}
#define ALLFREE \
free_all(nlstmp, nnp, pathname, ppaths, lang)
static void
free_all(Nlstmp *nlstmp, Nls_node *nnp, char *pathname,
char *ppaths, char *lang)
{
Nlstmp *tp, *tq;
tp = nlstmp;
while (tp) {
tq = tp->next;
free(tp);
tp = tq;
}
if (nnp->locale)
free(nnp->locale);
if (nnp->domain)
free(nnp->domain);
if (pathname)
free(pathname);
if (ppaths)
free(ppaths);
if (lang)
free(lang);
free(nnp);
}
static int
process_nlspath(const char *cur_domain, const char *cur_msgloc,
const char *nlspath, char **binding)
{
char *s;
char *territory;
char *codeset;
char *s1;
char *s2;
char *lang = NULL;
char *ppaths = NULL;
char *pathname = NULL;
size_t nlspath_len, domain_len, locale_len, path_len;
size_t ppaths_len = 0;
Nlstmp *nlstmp = NULL;
Nlstmp *pnlstmp, *qnlstmp;
Nls_node *cur_nls, *nnp;
Gettext_t *gt = global_gt;
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** process_nlspath(%s, %s, "
"%s, 0x%p)\n", cur_domain,
cur_msgloc, nlspath, (void *)binding);
#endif
cur_nls = gt->c_n_node;
if (cur_nls &&
(strcmp(cur_nls->domain, cur_domain) == 0 &&
strcmp(cur_nls->locale, cur_msgloc) == 0 &&
strcmp(cur_nls->nlspath, nlspath) == 0)) {
*binding = cur_nls->ppaths;
return (1);
}
nnp = gt->n_node;
while (nnp) {
if (strcmp(nnp->domain, cur_domain) == 0 &&
strcmp(nnp->locale, cur_msgloc) == 0 &&
strcmp(nnp->nlspath, nlspath) == 0) {
gt->c_n_node = nnp;
*binding = nnp->ppaths;
return (1);
}
nnp = nnp->next;
}
nnp = calloc(1, sizeof (Nls_node));
if (nnp == NULL) {
ALLFREE;
return (-1);
}
nlspath_len = strlen(nlspath);
locale_len = strlen(cur_msgloc);
domain_len = strlen(cur_domain);
lang = s = strdup(cur_msgloc);
if (lang == NULL) {
ALLFREE;
return (-1);
}
s1 = s2 = NULL;
while (*s) {
if (*s == '_') {
s1 = s;
*s1++ = '\0';
} else if (*s == '.') {
s2 = s;
*s2++ = '\0';
}
s++;
}
territory = s1;
codeset = s2;
pathname = malloc(MAXPATHLEN);
if (pathname == NULL) {
ALLFREE;
return (-1);
}
s = (char *)nlspath;
while (*s) {
(void) memset(pathname, 0, MAXPATHLEN);
if (*s == ':') {
pnlstmp = malloc(sizeof (Nlstmp));
if (pnlstmp == NULL) {
ALLFREE;
return (-1);
}
(void) memcpy(pnlstmp->pathname, cur_domain,
domain_len + 1);
pnlstmp->len = domain_len;
ppaths_len += domain_len + 1;
pnlstmp->next = NULL;
if (nlstmp == NULL) {
nlstmp = pnlstmp;
qnlstmp = pnlstmp;
} else {
qnlstmp->next = pnlstmp;
qnlstmp = pnlstmp;
}
++s;
continue;
}
s = replace_nls_option(s, cur_domain, pathname,
(char *)cur_msgloc, lang, territory, codeset);
if (s == NULL) {
ALLFREE;
return (-1);
}
if (*pathname) {
pnlstmp = malloc(sizeof (Nlstmp));
if (pnlstmp == NULL) {
ALLFREE;
return (-1);
}
path_len = strlen(pathname);
(void) memcpy(pnlstmp->pathname, pathname,
path_len + 1);
pnlstmp->len = path_len;
ppaths_len += path_len + 1;
pnlstmp->next = NULL;
if (nlstmp == NULL) {
nlstmp = pnlstmp;
qnlstmp = pnlstmp;
} else {
qnlstmp->next = pnlstmp;
qnlstmp = pnlstmp;
}
}
if (*s) {
++s;
}
}
if (ppaths_len != 0) {
ppaths = malloc(ppaths_len + 1);
if (ppaths == NULL) {
ALLFREE;
return (-1);
}
*ppaths = '\0';
} else {
ALLFREE;
return (0);
}
pnlstmp = nlstmp;
s = ppaths;
while (pnlstmp) {
(void) memcpy(s, pnlstmp->pathname, pnlstmp->len);
s += pnlstmp->len;
*s++ = ':';
qnlstmp = pnlstmp->next;
free(pnlstmp);
pnlstmp = qnlstmp;
}
*s = '\0';
nlstmp = NULL;
nnp->domain = malloc(domain_len + 1);
if (nnp->domain == NULL) {
ALLFREE;
return (-1);
} else {
(void) memcpy(nnp->domain, cur_domain, domain_len + 1);
}
nnp->locale = malloc(locale_len + 1);
if (nnp->locale == NULL) {
ALLFREE;
return (-1);
} else {
(void) memcpy(nnp->locale, cur_msgloc, locale_len + 1);
}
nnp->nlspath = malloc(nlspath_len + 1);
if (nnp->nlspath == NULL) {
ALLFREE;
return (-1);
} else {
(void) memcpy(nnp->nlspath, nlspath, nlspath_len + 1);
}
nnp->ppaths = ppaths;
nnp->next = gt->n_node;
gt->n_node = nnp;
gt->c_n_node = nnp;
free(pathname);
free(lang);
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** existing process_nlspath with success\n");
gprintf(0, " binding: \"%s\"\n", ppaths);
#endif
*binding = ppaths;
return (1);
}
static char *
replace_nls_option(char *s, const char *name, char *pathname,
char *locale, char *lang, char *territory, char *codeset)
{
char *t, *u;
char *limit;
t = pathname;
limit = pathname + MAXPATHLEN - 1;
while (*s && *s != ':') {
if (t < limit) {
if (*s != '%')
*t++ = *s;
else if (*++s == 'N') {
if (name) {
u = (char *)name;
while (*u && (t < limit))
*t++ = *u++;
}
} else if (*s == 'L') {
if (locale) {
u = locale;
while (*u && (t < limit))
*t++ = *u++;
}
} else if (*s == 'l') {
if (lang) {
u = lang;
while (*u && (*u != '_') &&
(t < limit))
*t++ = *u++;
}
} else if (*s == 't') {
if (territory) {
u = territory;
while (*u && (*u != '.') &&
(t < limit))
*t++ = *u++;
}
} else if (*s == 'c') {
if (codeset) {
u = codeset;
while (*u && (t < limit))
*t++ = *u++;
}
} else {
if (t < limit)
*t++ = *s;
}
} else {
return (NULL);
}
++s;
}
*t = '\0';
return (s);
}
char *
_real_bindtextdomain_u(const char *domain, const char *binding,
int type)
{
struct domain_binding *bind, *prev;
Gettext_t *gt = global_gt;
char **binding_addr;
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** _real_bindtextdomain_u(\"%s\", "
"\"%s\", \"%s\")\n",
(domain ? domain : ""),
(binding ? binding : ""),
(type == TP_BINDING) ? "TP_BINDING" : "TP_CODESET");
#endif
if (domain == NULL) {
return (NULL);
}
if (*domain == '\0') {
return (NULL);
}
bind = FIRSTBIND(gt);
prev = NULL;
while (bind) {
if (strcmp(domain, bind->domain) == 0) {
binding_addr = (type == TP_BINDING) ? &(bind->binding) :
&(bind->codeset);
if (binding == NULL) {
return (*binding_addr);
}
char *new_binding = strdup(binding);
if (new_binding == NULL) {
return (NULL);
}
free(*binding_addr);
*binding_addr = new_binding;
#ifdef GETTEXT_DEBUG
printlist();
#endif
return (*binding_addr);
}
prev = bind;
bind = bind->next;
}
if (binding) {
if ((bind = malloc(sizeof (Dbinding))) == NULL) {
return (NULL);
}
if ((bind->domain = strdup(domain)) == NULL) {
free(bind);
return (NULL);
}
bind->binding = NULL;
bind->codeset = NULL;
binding_addr = (type == TP_BINDING) ? &(bind->binding) :
&(bind->codeset);
if ((*binding_addr = strdup(binding)) == NULL) {
free(bind->domain);
free(bind);
return (NULL);
}
bind->next = NULL;
if (prev) {
prev->next = bind;
} else {
FIRSTBIND(gt) = bind;
}
#ifdef GETTEXT_DEBUG
printlist();
#endif
return (*binding_addr);
} else {
if (type == TP_BINDING) {
return ((char *)defaultbind);
} else {
return (NULL);
}
}
}
char *
_textdomain_u(const char *domain, char *result)
{
char *p;
size_t domain_len;
Gettext_t *gt = global_gt;
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** _textdomain_u(\"%s\", 0x%p)\n",
(domain ? domain : ""), (void *)result);
#endif
if (domain == NULL) {
(void) strcpy(result, CURRENT_DOMAIN(gt));
return (result);
}
domain_len = strlen(domain);
if (domain_len > TEXTDOMAINMAX) {
return (NULL);
}
if (*domain == '\0') {
if (CURRENT_DOMAIN(gt) != default_domain) {
free(CURRENT_DOMAIN(gt));
CURRENT_DOMAIN(gt) = (char *)default_domain;
}
} else {
p = malloc(domain_len + 1);
if (p == NULL)
return (NULL);
(void) strcpy(p, domain);
if (CURRENT_DOMAIN(gt) != default_domain)
free(CURRENT_DOMAIN(gt));
CURRENT_DOMAIN(gt) = p;
}
(void) strcpy(result, CURRENT_DOMAIN(gt));
return (result);
}
static char *
key_2_text(Msg_s_node *messages, const char *key_string)
{
int val;
char *msg_id_str;
unsigned char kc = *(unsigned char *)key_string;
struct msg_struct *check_msg_list;
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** key_2_text(0x%p, \"%s\")\n",
(void *)messages, key_string ? key_string : "(null)");
printsunmsg(messages, 1);
#endif
check_msg_list = messages->msg_list +
messages->msg_file_info->msg_mid;
for (;;) {
msg_id_str = messages->msg_ids +
check_msg_list->msgid_offset;
val = *(unsigned char *)msg_id_str - kc;
if ((val == 0) &&
(val = strcmp(msg_id_str, key_string)) == 0) {
return (messages->msg_strs
+ check_msg_list->msgstr_offset);
} else if (val < 0) {
if (check_msg_list->less != LEAFINDICATOR) {
check_msg_list = messages->msg_list +
check_msg_list->less;
continue;
}
return ((char *)key_string);
} else {
if (check_msg_list->more != LEAFINDICATOR) {
check_msg_list = messages->msg_list +
check_msg_list->more;
continue;
}
return ((char *)key_string);
}
}
}
static int
sun_setmsg(Msg_node *mnp, char *addr, size_t size)
{
struct msg_info *sun_header;
Msg_s_node *p;
uint32_t first_4bytes;
int mid, count;
int struct_size, struct_size_old;
int msg_struct_size;
if (size < sizeof (struct msg_info)) {
mnp->type = T_ILL_MO;
#ifdef GETTEXT_DEBUG
gprintf(0, "********* exiting sun_setmsg\n");
printmnp(mnp, 1);
#endif
return (0);
}
first_4bytes = *((uint32_t *)(uintptr_t)addr);
if (first_4bytes > INT_MAX) {
return (1);
}
sun_header = (struct msg_info *)(uintptr_t)addr;
mid = sun_header->msg_mid;
count = sun_header->msg_count;
msg_struct_size = sun_header->msg_struct_size;
struct_size_old = (int)(OLD_MSG_STRUCT_SIZE * count);
struct_size = (int)(MSG_STRUCT_SIZE * count);
if ((((count - 1) / 2) != mid) ||
((msg_struct_size != struct_size_old) &&
(msg_struct_size != struct_size))) {
mnp->type = T_ILL_MO;
#ifdef GETTEXT_DEBUG
gprintf(0, "********* exiting sun_setmsg\n");
printmnp(mnp, 1);
#endif
return (0);
}
p = malloc(sizeof (Msg_s_node));
if (p == NULL) {
return (-1);
}
p->msg_file_info = sun_header;
p->msg_list = (struct msg_struct *)(uintptr_t)
(addr + sizeof (struct msg_info));
p->msg_ids = (char *)(addr + sizeof (struct msg_info) +
struct_size);
p->msg_strs = (char *)(addr + sizeof (struct msg_info) +
struct_size + sun_header->str_count_msgid);
mnp->msg.sunmsg = p;
mnp->type = T_SUN_MO;
#ifdef GETTEXT_DEBUG
gprintf(0, "******** exiting sun_setmsg\n");
printmnp(mnp, 1);
#endif
return (0);
}
static int
setmsg(Msg_node *mnp, char *addr, size_t size)
{
int ret;
if ((ret = sun_setmsg(mnp, addr, size)) <= 0)
return (ret);
return (gnu_setmsg(mnp, addr, size));
}
static char *
handle_type_mo(Msg_node *mnp, struct msg_pack *mp)
{
char *result;
switch (mnp->type) {
case T_ILL_MO:
return (NULL);
case T_SUN_MO:
mp->status |= ST_SUN_MO_FOUND;
if (mp->plural) {
int exp = (mp->n == 1);
result = (char *)mp->msgid1;
if (!exp)
result = (char *)mp->msgid2;
return (result);
}
result = key_2_text(mnp->msg.sunmsg, mp->msgid1);
if (!mnp->trusted) {
result = check_format(mp->msgid1, result, 0);
}
return (result);
case T_GNU_MO:
mp->status |= ST_GNU_MO_FOUND;
result = gnu_key_2_text(mnp->msg.gnumsg,
get_codeset(mp->domain), mp);
if (result == mp->msgid1 || result == mp->msgid2) {
return (result);
}
mp->status |= ST_GNU_MSG_FOUND;
if (!mnp->trusted) {
result = check_format(mp->msgid1, result, 0);
if (result == mp->msgid1) {
DFLTMSG(result, mp->msgid1, mp->msgid2,
mp->n, mp->plural);
}
}
return (result);
default:
DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
return (result);
}
}
char *
handle_mo(struct msg_pack *mp)
{
int fd;
char *result;
struct stat64 statbuf;
Msg_node *mnp;
Gettext_t *gt = global_gt;
#define CONNECT_ENTRY \
mnp->next = gt->m_node; \
gt->m_node = mnp; \
gt->c_m_node = mnp
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** handle_mo(0x%p)\n", (void *)mp);
printmp(mp, 1);
#endif
mnp = check_cache(mp);
if (mnp != NULL) {
return (handle_type_mo(mnp, mp));
}
mnp = calloc(1, sizeof (Msg_node));
if (mnp == NULL) {
DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
return (result);
}
mnp->hashid = mp->hash_domain;
mnp->path = strdup(mp->msgfile);
if (mnp->path == NULL) {
free(mnp);
DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
return (result);
}
fd = nls_safe_open(mp->msgfile, &statbuf, &mp->trusted, !mp->nlsp);
if ((fd == -1) || (statbuf.st_size > LONG_MAX)) {
if (fd != -1)
(void) close(fd);
mnp->type = T_ILL_MO;
CONNECT_ENTRY;
return (NULL);
}
mp->fsz = (size_t)statbuf.st_size;
mp->addr = mmap(NULL, mp->fsz, PROT_READ, MAP_SHARED, fd, 0);
(void) close(fd);
if (mp->addr == MAP_FAILED) {
free(mnp->path);
free(mnp);
DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
return (result);
}
if (setmsg(mnp, (char *)mp->addr, mp->fsz) == -1) {
free(mnp->path);
free(mnp);
(void) munmap(mp->addr, mp->fsz);
DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
return (result);
}
mnp->trusted = mp->trusted;
CONNECT_ENTRY;
return (handle_type_mo(mnp, mp));
}