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) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include "lint.h"
2N/A#include "mtlib.h"
2N/A#include <ctype.h>
2N/A#include <locale.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <sys/types.h>
2N/A#include <sys/mman.h>
2N/A#include <sys/param.h>
2N/A#include <sys/stat.h>
2N/A#include <libintl.h>
2N/A#include <thread.h>
2N/A#include <synch.h>
2N/A#include <limits.h>
2N/A#include <unistd.h>
2N/A#include "libc.h"
2N/A#include "_loc_path.h"
2N/A#include "msgfmt.h"
2N/A#include "gettext.h"
2N/A#include "nlspath_checks.h"
2N/A
2N/A#define GETTEXT_ALTERNATIVE_LOCALES_NOT_CALLED (-9)
2N/A
2N/Astatic int process_nlspath(const char *, const char *,
2N/A const char *, Nlstmp **, char **, int *, int *);
2N/Astatic char *replace_nls_option(char *, const char *, char *,
2N/A char *, char *, char *, char *, int *, size_t *);
2N/A
2N/Achar *
2N/A_real_gettext_u(const char *domain, const char *msgid1, const char *msgid2,
2N/A unsigned long int ln, int category, int plural)
2N/A{
2N/A char msgfile[MAXPATHLEN]; /* 1024 */
2N/A char mydomain[TEXTDOMAINMAX + 1]; /* 256 + 1 */
2N/A Nlstmp *cur_binding; /* points to current binding in list */
2N/A char *cur_locale, *cur_domain, *result, *nlspath;
2N/A char *msgloc, *cur_domain_binding;
2N/A char *language;
2N/A unsigned int n = (unsigned int)ln; /* we don't need long for n */
2N/A uint32_t cur_domain_len;
2N/A uint32_t hash_domain;
2N/A struct msg_pack *mp, omp;
2N/A char *canonical;
2N/A int start;
2N/A int end;
2N/A
2N/A#ifdef GETTEXT_DEBUG
2N/A gprintf(0, "*************** _real_gettext_u(\"%s\", \"%s\", "
2N/A "\"%s\", %d, %d, %d)\n",
2N/A domain ? domain : "NULL", msgid1 ? msgid1 : "NULL",
2N/A msgid2 ? msgid2 : "NULL", n, category, plural);
2N/A gprintf(0, "***************** global_gt: 0x%p\n", global_gt);
2N/A printgt(global_gt, 1);
2N/A#endif
2N/A
2N/A if (msgid1 == NULL)
2N/A return (NULL);
2N/A
2N/A mp = memset(&omp, 0, sizeof (omp)); /* msg pack */
2N/A
2N/A /*
2N/A * category may be LC_MESSAGES or LC_TIME
2N/A * locale contains the value of 'category'
2N/A */
2N/A cur_locale = setlocale(category, NULL);
2N/A
2N/A language = getenv("LANGUAGE"); /* for GNU */
2N/A if (language) {
2N/A if (!*language || strchr(language, '/') != NULL) {
2N/A /*
2N/A * LANGUAGE is an empty string or
2N/A * LANGUAGE contains '/'.
2N/A * Ignore it.
2N/A */
2N/A language = NULL;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Query the current domain if domain argument is NULL pointer
2N/A */
2N/A mydomain[0] = '\0';
2N/A if (domain == NULL) {
2N/A /*
2N/A * if NULL is specified for domainname,
2N/A * use the currently bound domain.
2N/A */
2N/A cur_domain = _textdomain_u(NULL, mydomain);
2N/A } else if (!*domain) {
2N/A /*
2N/A * if an empty string is specified
2N/A */
2N/A cur_domain = DEFAULT_DOMAIN;
2N/A } else {
2N/A cur_domain = (char *)domain;
2N/A }
2N/A
2N/A hash_domain = get_hashid(cur_domain, &cur_domain_len);
2N/A if (cur_domain_len > TEXTDOMAINMAX) {
2N/A /* domain is invalid, return msg_id */
2N/A DFLTMSG(result, msgid1, msgid2, n, plural);
2N/A return (result);
2N/A }
2N/A
2N/A start = GETTEXT_ALTERNATIVE_LOCALES_NOT_CALLED;
2N/A
2N/A nlspath = getenv("NLSPATH"); /* get the content of NLSPATH */
2N/A if (nlspath == NULL || !*nlspath) {
2N/A /* no NLSPATH is defined in the environ */
2N/A if ((*cur_locale == 'C') && (*(cur_locale + 1) == '\0')) {
2N/A /*
2N/A * If C locale,
2N/A * return the original msgid immediately.
2N/A */
2N/A DFLTMSG(result, msgid1, msgid2, n, plural);
2N/A return (result);
2N/A }
2N/A nlspath = NULL;
2N/A } else {
2N/A /* NLSPATH is set */
2N/A int ret;
2N/A
2N/A msgloc = setlocale(LC_MESSAGES, NULL);
2N/A
2N/A ret = process_nlspath(cur_domain, msgloc,
2N/A (const char *)nlspath, &cur_binding, &canonical,
2N/A &start, &end);
2N/A if (ret == -1) {
2N/A /* error occurred */
2N/A DFLTMSG(result, msgid1, msgid2, n, plural);
2N/A return (result);
2N/A } else if (ret == 0) {
2N/A nlspath = NULL;
2N/A }
2N/A }
2N/A
2N/A cur_domain_binding = _real_bindtextdomain_u(cur_domain,
2N/A NULL, TP_BINDING);
2N/A if (cur_domain_binding == NULL) {
2N/A DFLTMSG(result, msgid1, msgid2, n, plural);
2N/A return (result);
2N/A }
2N/A
2N/A mp->msgid1 = msgid1;
2N/A mp->msgid2 = msgid2;
2N/A mp->domain = cur_domain;
2N/A mp->binding = cur_domain_binding;
2N/A mp->locale = cur_locale;
2N/A mp->language = language;
2N/A mp->domain_len = cur_domain_len;
2N/A mp->n = n;
2N/A mp->category = category;
2N/A mp->plural = plural;
2N/A mp->hash_domain = hash_domain;
2N/A
2N/A /*
2N/A * Spec1170 requires that we use NLSPATH if it's defined, to
2N/A * override any system default variables. If NLSPATH is not
2N/A * defined or if a message catalog is not found in any of the
2N/A * components (bindings) specified by NLSPATH, dcgettext_u() will
2N/A * search for the message catalog in either a) the binding path set
2N/A * by any previous application calls to bindtextdomain() or
2N/A * b) the default binding path (/usr/lib/locale). Save the original
2N/A * binding path so that we can search it if the message catalog
2N/A * is not found via NLSPATH. The original binding is restored before
2N/A * returning from this routine because the gettext routines should
2N/A * not change the binding set by the application. This allows
2N/A * bindtextdomain() to be called once for all gettext() calls in the
2N/A * application.
2N/A */
2N/A
2N/A /*
2N/A * First, examine NLSPATH
2N/A */
2N/A if (nlspath) {
2N/A /*
2N/A * NLSPATH binding has been successfully built
2N/A */
2N/A#ifdef GETTEXT_DEBUG
2N/A gprintf(0, "************************** examining NLSPATH\n");
2N/A gprintf(0, " cur_binding: \"0x%p\"\n",
2N/A (void *)cur_binding);
2N/A#endif
2N/A
2N/A mp->nlsp = 1;
2N/A
2N/A while (cur_binding != NULL) {
2N/A if (cur_binding->len > MAXPATHLEN) {
2N/A DFLTMSG(result, msgid1, msgid2, n, plural);
2N/A return (result);
2N/A }
2N/A
2N/A mp->msgfile = cur_binding->pathname;
2N/A
2N/A result = handle_mo(mp);
2N/A if (result != NULL)
2N/A return (result);
2N/A
2N/A cur_binding = cur_binding->next;
2N/A }
2N/A }
2N/A
2N/A mp->msgfile = msgfile;
2N/A mp->nlsp = 0;
2N/A mp->binding = cur_domain_binding;
2N/A /*
2N/A * Next, examine LANGUAGE
2N/A */
2N/A if (language) {
2N/A char *ret_msg;
2N/A ret_msg = handle_lang(mp);
2N/A if (ret_msg != NULL) {
2N/A /* valid msg found in GNU MO */
2N/A return (ret_msg);
2N/A }
2N/A /*
2N/A * handle_lang() may have overridden locale
2N/A */
2N/A mp->locale = cur_locale;
2N/A mp->status = 0;
2N/A }
2N/A
2N/A /*
2N/A * Handle a single binding.
2N/A */
2N/A#ifdef GETTEXT_DEBUG
2N/A *mp->msgfile = '\0';
2N/A#endif
2N/A if (mk_msgfile(mp) == NULL) {
2N/A DFLTMSG(result, msgid1, msgid2, n, plural);
2N/A return (result);
2N/A }
2N/A
2N/A result = handle_mo(mp);
2N/A if (result) {
2N/A return (result);
2N/A }
2N/A
2N/A /*
2N/A * Lastly, check with obsoleted and canonical locale names.
2N/A */
2N/A if (start == GETTEXT_ALTERNATIVE_LOCALES_NOT_CALLED ||
2N/A category != LC_MESSAGES)
2N/A alternative_locales(cur_locale, &canonical, &start, &end, 1);
2N/A
2N/A if (start != -1) {
2N/A for (; start <= end; start++) {
2N/A mp->locale = (char *)__lc_obs_msg_lc_list[start];
2N/A
2N/A if (mk_msgfile(mp) == NULL) {
2N/A DFLTMSG(result, msgid1, msgid2, n, plural);
2N/A return (result);
2N/A }
2N/A
2N/A result = handle_mo(mp);
2N/A if (result != NULL)
2N/A return (result);
2N/A }
2N/A }
2N/A
2N/A if (canonical != NULL) {
2N/A mp->locale = canonical;
2N/A
2N/A if (mk_msgfile(mp) == NULL) {
2N/A DFLTMSG(result, msgid1, msgid2, n, plural);
2N/A return (result);
2N/A }
2N/A
2N/A result = handle_mo(mp);
2N/A if (result != NULL)
2N/A return (result);
2N/A }
2N/A
2N/A DFLTMSG(result, msgid1, msgid2, n, plural);
2N/A return (result);
2N/A} /* _real_gettext_u */
2N/A
2N/Astatic void
2N/Afree_Nlstmp(Nlstmp *nlstmp)
2N/A{
2N/A Nlstmp *tp;
2N/A
2N/A while (nlstmp) {
2N/A tp = nlstmp;
2N/A nlstmp = nlstmp->next;
2N/A if (tp->pathname)
2N/A free(tp->pathname);
2N/A free(tp);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Populate templates as a linked list, nt, based on NLSPATH, locale, and
2N/A * domain values.
2N/A */
2N/Astatic int
2N/Apopulate_templates(Nlstmp **nt, Nlstmp **tail, const char *domain,
2N/A size_t domain_len, const char *loc, const char *nlspath, int *ltc_defined)
2N/A{
2N/A char *l;
2N/A char *t;
2N/A char *c;
2N/A char *s;
2N/A char path[MAXPATHLEN];
2N/A Nlstmp *tnt;
2N/A size_t len;
2N/A
2N/A l = s = strdup(loc);
2N/A if (l == NULL)
2N/A return (0);
2N/A
2N/A t = c = NULL;
2N/A
2N/A /*
2N/A * Collect language, territory, and codeset from the locale name
2N/A * based on the following locale naming convention:
2N/A *
2N/A * language[_territory][.codeset][@modifier]
2N/A */
2N/A while (*s != '\0') {
2N/A if (*s == '_') {
2N/A *s++ = '\0';
2N/A t = s;
2N/A continue;
2N/A } else if (*s == '.') {
2N/A *s++ = '\0';
2N/A c = s;
2N/A continue;
2N/A } else if (*s == '@') {
2N/A *s = '\0';
2N/A break;
2N/A }
2N/A
2N/A s++;
2N/A }
2N/A
2N/A /*
2N/A * Process NLSPATH and populate templates.
2N/A *
2N/A * The domain_len and the len used at below includes '\0'.
2N/A */
2N/A
2N/A s = (char *)nlspath;
2N/A while (*s) {
2N/A if (*s == ':') {
2N/A if (domain_len <= 1) {
2N/A s++;
2N/A continue;
2N/A }
2N/A
2N/A tnt = malloc(sizeof (Nlstmp));
2N/A if (tnt == NULL) {
2N/A free(l);
2N/A free_Nlstmp(*nt);
2N/A return (0);
2N/A }
2N/A
2N/A tnt->pathname = malloc(domain_len);
2N/A if (tnt->pathname == NULL) {
2N/A free(l);
2N/A free(tnt);
2N/A free_Nlstmp(*nt);
2N/A return (0);
2N/A }
2N/A
2N/A (void) memcpy(tnt->pathname, domain, domain_len);
2N/A tnt->len = domain_len;
2N/A tnt->next = NULL;
2N/A
2N/A if (*tail == NULL) {
2N/A *nt = *tail = tnt;
2N/A } else {
2N/A (*tail)->next = tnt;
2N/A *tail = tnt;
2N/A }
2N/A
2N/A s++;
2N/A continue;
2N/A }
2N/A
2N/A s = replace_nls_option(s, domain, path, (char *)loc,
2N/A l, t, c, ltc_defined, &len);
2N/A if (s == NULL) {
2N/A free(l);
2N/A free_Nlstmp(*nt);
2N/A return (0);
2N/A }
2N/A
2N/A if (*path != '\0') {
2N/A tnt = malloc(sizeof (Nlstmp));
2N/A if (tnt == NULL) {
2N/A free(l);
2N/A free_Nlstmp(*nt);
2N/A return (0);
2N/A }
2N/A
2N/A tnt->pathname = malloc(len);
2N/A if (tnt->pathname == NULL) {
2N/A free(l);
2N/A free(tnt);
2N/A free_Nlstmp(*nt);
2N/A return (0);
2N/A }
2N/A
2N/A (void) memcpy(tnt->pathname, path, len);
2N/A tnt->len = len;
2N/A tnt->next = NULL;
2N/A
2N/A if (*tail == NULL) {
2N/A *nt = *tail = tnt;
2N/A } else {
2N/A (*tail)->next = tnt;
2N/A *tail = tnt;
2N/A }
2N/A }
2N/A
2N/A if (*s != '\0')
2N/A s++;
2N/A }
2N/A
2N/A free(l);
2N/A
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * process_nlspath(): process the NLSPATH environment variable.
2N/A *
2N/A * This routine looks at NLSPATH in the environment,
2N/A * and will try to build up the binding list based
2N/A * on the settings of NLSPATH. It will not only use
2N/A * the locale name supplied but also, if available,
2N/A * obsoleted Solaris locale names based on the supplied
2N/A * locale name and also canonical locale name in case
2N/A * the supplied is an alias.
2N/A *
2N/A * RETURN:
2N/A * -1: Error occurred
2N/A * 0: No error, but no binding list has been built
2N/A * 1: No error, and a binding list has been built or found
2N/A *
2N/A */
2N/Astatic int
2N/Aprocess_nlspath(const char *domain, const char *locale, const char *nlspath,
2N/A Nlstmp **binding, char **canonical, int *start, int *end)
2N/A{
2N/A size_t len;
2N/A Nlstmp *nthead;
2N/A Nlstmp *nttail;
2N/A Nls_node *cur_nls;
2N/A Nls_node *nnp;
2N/A int ltc_defined;
2N/A int i;
2N/A
2N/A#ifdef GETTEXT_DEBUG
2N/A gprintf(0, "*************** process_nlspath(%s, %s, "
2N/A "%s, 0x%p)\n", domain,
2N/A locale, nlspath, (void *)binding);
2N/A#endif
2N/A
2N/A cur_nls = global_gt->c_n_node;
2N/A if (cur_nls &&
2N/A (strcmp(cur_nls->domain, domain) == 0 &&
2N/A strcmp(cur_nls->locale, locale) == 0 &&
2N/A strcmp(cur_nls->nlspath, nlspath) == 0)) {
2N/A *binding = cur_nls->ppaths;
2N/A return (1);
2N/A }
2N/A
2N/A nnp = global_gt->n_node;
2N/A while (nnp) {
2N/A if (strcmp(nnp->domain, domain) == 0 &&
2N/A strcmp(nnp->locale, locale) == 0 &&
2N/A strcmp(nnp->nlspath, nlspath) == 0) {
2N/A /* found */
2N/A global_gt->c_n_node = nnp;
2N/A *binding = nnp->ppaths;
2N/A return (1);
2N/A }
2N/A nnp = nnp->next;
2N/A }
2N/A /* not found */
2N/A
2N/A nthead = NULL;
2N/A nttail = NULL;
2N/A ltc_defined = 0;
2N/A len = strlen(domain) + 1;
2N/A
2N/A if (populate_templates(&nthead, &nttail, domain, len, locale,
2N/A nlspath, &ltc_defined) == 0)
2N/A return (-1);
2N/A
2N/A if (ltc_defined != 0) {
2N/A alternative_locales((char *)locale, canonical, start, end, 1);
2N/A
2N/A if (*start != -1) {
2N/A for (i = *start; i <= *end; i++) {
2N/A if (populate_templates(&nthead, &nttail,
2N/A domain, len, __lc_obs_msg_lc_list[i],
2N/A nlspath, &ltc_defined) == 0)
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A if (*canonical != NULL) {
2N/A if (populate_templates(&nthead, &nttail, domain, len,
2N/A *canonical, nlspath, &ltc_defined) == 0)
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A if (nthead == NULL)
2N/A return (0);
2N/A
2N/A nnp = malloc(sizeof (Nls_node));
2N/A if (nnp == NULL) {
2N/A free_Nlstmp(nthead);
2N/A return (-1);
2N/A }
2N/A
2N/A nnp->domain = malloc(len);
2N/A if (nnp->domain == NULL) {
2N/A free_Nlstmp(nthead);
2N/A free(nnp);
2N/A return (-1);
2N/A } else {
2N/A (void) memcpy(nnp->domain, domain, len);
2N/A }
2N/A
2N/A len = strlen(locale) + 1;
2N/A nnp->locale = malloc(len);
2N/A if (nnp->locale == NULL) {
2N/A free_Nlstmp(nthead);
2N/A free(nnp->domain);
2N/A free(nnp);
2N/A return (-1);
2N/A } else {
2N/A (void) memcpy(nnp->locale, locale, len);
2N/A }
2N/A
2N/A len = strlen(nlspath) + 1;
2N/A nnp->nlspath = malloc(len);
2N/A if (nnp->nlspath == NULL) {
2N/A free_Nlstmp(nthead);
2N/A free(nnp->domain);
2N/A free(nnp->locale);
2N/A free(nnp);
2N/A return (-1);
2N/A } else {
2N/A (void) memcpy(nnp->nlspath, nlspath, len);
2N/A }
2N/A nnp->ppaths = nthead;
2N/A
2N/A nnp->next = global_gt->n_node;
2N/A global_gt->n_node = nnp;
2N/A global_gt->c_n_node = nnp;
2N/A
2N/A *binding = nthead;
2N/A return (1);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * This routine will replace substitution parameters in NLSPATH
2N/A * with appropiate values.
2N/A */
2N/Astatic char *
2N/Areplace_nls_option(char *s, const char *name, char *pathname, char *locale,
2N/A char *lang, char *territory, char *codeset, int *ltc_defined, size_t *len)
2N/A{
2N/A char *t, *u;
2N/A char *limit;
2N/A
2N/A t = pathname;
2N/A limit = pathname + MAXPATHLEN - 1;
2N/A
2N/A while (*s && *s != ':') {
2N/A if (t < limit) {
2N/A /*
2N/A * %% is considered a single % character (XPG).
2N/A * %L : LC_MESSAGES (XPG4) LANG(XPG3)
2N/A * %l : The language element from the current locale.
2N/A * (XPG3, XPG4)
2N/A */
2N/A if (*s != '%')
2N/A *t++ = *s;
2N/A else if (*++s == 'N') {
2N/A if (name) {
2N/A u = (char *)name;
2N/A while (*u && (t < limit))
2N/A *t++ = *u++;
2N/A }
2N/A } else if (*s == 'L') {
2N/A if (locale) {
2N/A u = locale;
2N/A while (*u && (t < limit))
2N/A *t++ = *u++;
2N/A }
2N/A *ltc_defined = 1;
2N/A } else if (*s == 'l') {
2N/A if (lang) {
2N/A u = lang;
2N/A while (*u && (*u != '_') &&
2N/A (t < limit))
2N/A *t++ = *u++;
2N/A }
2N/A *ltc_defined = 1;
2N/A } else if (*s == 't') {
2N/A if (territory) {
2N/A u = territory;
2N/A while (*u && (*u != '.') &&
2N/A (t < limit))
2N/A *t++ = *u++;
2N/A }
2N/A *ltc_defined = 1;
2N/A } else if (*s == 'c') {
2N/A if (codeset) {
2N/A u = codeset;
2N/A while (*u && (t < limit))
2N/A *t++ = *u++;
2N/A }
2N/A *ltc_defined = 1;
2N/A } else {
2N/A if (t < limit)
2N/A *t++ = *s;
2N/A }
2N/A } else {
2N/A /* too long pathname */
2N/A return (NULL);
2N/A }
2N/A ++s;
2N/A }
2N/A
2N/A *t = '\0';
2N/A *len = t - pathname + 1;
2N/A
2N/A return (s);
2N/A}
2N/A
2N/A
2N/Achar *
2N/A_real_bindtextdomain_u(const char *domain, const char *binding,
2N/A int type)
2N/A{
2N/A struct domain_binding *bind, *prev;
2N/A Gettext_t *gt = global_gt;
2N/A char **binding_addr;
2N/A
2N/A#ifdef GETTEXT_DEBUG
2N/A gprintf(0, "*************** _real_bindtextdomain_u(\"%s\", "
2N/A "\"%s\", \"%s\")\n",
2N/A (domain ? domain : ""),
2N/A (binding ? binding : ""),
2N/A (type == TP_BINDING) ? "TP_BINDING" : "TP_CODESET");
2N/A#endif
2N/A
2N/A /*
2N/A * If domain is a NULL pointer, no change will occur regardless
2N/A * of binding value. Just return NULL.
2N/A */
2N/A if (domain == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * Global Binding is not supported any more.
2N/A * Just return NULL if domain is NULL string.
2N/A */
2N/A if (*domain == '\0') {
2N/A return (NULL);
2N/A }
2N/A
2N/A /* linear search for binding, rebind if found, add if not */
2N/A bind = FIRSTBIND(gt);
2N/A prev = NULL; /* Two pointers needed for pointer operations */
2N/A
2N/A while (bind) {
2N/A if (strcmp(domain, bind->domain) == 0) {
2N/A /*
2N/A * Domain found.
2N/A */
2N/A binding_addr = (type == TP_BINDING) ? &(bind->binding) :
2N/A &(bind->codeset);
2N/A if (binding == NULL) {
2N/A /*
2N/A * if binding is null, then query
2N/A */
2N/A return (*binding_addr);
2N/A }
2N/A /* replace existing binding with new binding */
2N/A if (*binding_addr) {
2N/A free(*binding_addr);
2N/A }
2N/A if ((*binding_addr = strdup(binding)) == NULL) {
2N/A return (NULL);
2N/A }
2N/A#ifdef GETTEXT_DEBUG
2N/A printlist();
2N/A#endif
2N/A return (*binding_addr);
2N/A }
2N/A prev = bind;
2N/A bind = bind->next;
2N/A } /* while (bind) */
2N/A
2N/A /* domain has not been found in the list at this point */
2N/A if (binding) {
2N/A /*
2N/A * domain is not found, but binding is not NULL.
2N/A * Then add a new node to the end of linked list.
2N/A */
2N/A
2N/A if ((bind = malloc(sizeof (Dbinding))) == NULL) {
2N/A return (NULL);
2N/A }
2N/A if ((bind->domain = strdup(domain)) == NULL) {
2N/A free(bind);
2N/A return (NULL);
2N/A }
2N/A bind->binding = NULL;
2N/A bind->codeset = NULL;
2N/A binding_addr = (type == TP_BINDING) ? &(bind->binding) :
2N/A &(bind->codeset);
2N/A if ((*binding_addr = strdup(binding)) == NULL) {
2N/A free(bind->domain);
2N/A free(bind);
2N/A return (NULL);
2N/A }
2N/A bind->next = NULL;
2N/A
2N/A if (prev) {
2N/A /* reached the end of list */
2N/A prev->next = bind;
2N/A } else {
2N/A /* list was empty */
2N/A FIRSTBIND(gt) = bind;
2N/A }
2N/A
2N/A#ifdef GETTEXT_DEBUG
2N/A printlist();
2N/A#endif
2N/A return (*binding_addr);
2N/A } else {
2N/A /*
2N/A * Query of domain which is not found in the list
2N/A * for bindtextdomain, returns defaultbind
2N/A * for bind_textdomain_codeset, returns NULL
2N/A */
2N/A if (type == TP_BINDING) {
2N/A return ((char *)defaultbind);
2N/A } else {
2N/A return (NULL);
2N/A }
2N/A } /* if (binding) */
2N/A
2N/A /* Must not reach here */
2N/A
2N/A} /* _real_bindtextdomain_u */
2N/A
2N/A
2N/Achar *
2N/A_textdomain_u(const char *domain, char *result)
2N/A{
2N/A char *p;
2N/A size_t domain_len;
2N/A Gettext_t *gt = global_gt;
2N/A
2N/A#ifdef GETTEXT_DEBUG
2N/A gprintf(0, "*************** _textdomain_u(\"%s\", 0x%p)\n",
2N/A (domain ? domain : ""), (void *)result);
2N/A#endif
2N/A
2N/A /* Query is performed for NULL domain pointer */
2N/A if (domain == NULL) {
2N/A (void) strcpy(result, CURRENT_DOMAIN(gt));
2N/A return (result);
2N/A }
2N/A
2N/A /* check for error. */
2N/A /*
2N/A * domain is limited to TEXTDOMAINMAX bytes
2N/A * excluding a null termination.
2N/A */
2N/A domain_len = strlen(domain);
2N/A if (domain_len > TEXTDOMAINMAX) {
2N/A /* too long */
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * Calling textdomain() with a null domain string sets
2N/A * the domain to the default domain.
2N/A * If non-null string is passwd, current domain is changed
2N/A * to the new domain.
2N/A */
2N/A
2N/A /* actually this if clause should be protected from signals */
2N/A if (*domain == '\0') {
2N/A if (CURRENT_DOMAIN(gt) != default_domain) {
2N/A free(CURRENT_DOMAIN(gt));
2N/A CURRENT_DOMAIN(gt) = (char *)default_domain;
2N/A }
2N/A } else {
2N/A p = malloc(domain_len + 1);
2N/A if (p == NULL)
2N/A return (NULL);
2N/A (void) strcpy(p, domain);
2N/A if (CURRENT_DOMAIN(gt) != default_domain)
2N/A free(CURRENT_DOMAIN(gt));
2N/A CURRENT_DOMAIN(gt) = p;
2N/A }
2N/A
2N/A (void) strcpy(result, CURRENT_DOMAIN(gt));
2N/A return (result);
2N/A} /* _textdomain_u */
2N/A
2N/A/*
2N/A * key_2_text() translates msd_id into target string.
2N/A */
2N/Astatic char *
2N/Akey_2_text(Msg_s_node *messages, const char *key_string)
2N/A{
2N/A int val;
2N/A char *msg_id_str;
2N/A unsigned char kc = *(unsigned char *)key_string;
2N/A struct msg_struct *check_msg_list;
2N/A
2N/A#ifdef GETTEXT_DEBUG
2N/A gprintf(0, "*************** key_2_text(0x%p, \"%s\")\n",
2N/A (void *)messages, key_string ? key_string : "(null)");
2N/A printsunmsg(messages, 1);
2N/A#endif
2N/A
2N/A check_msg_list = messages->msg_list +
2N/A messages->msg_file_info->msg_mid;
2N/A for (;;) {
2N/A msg_id_str = messages->msg_ids +
2N/A check_msg_list->msgid_offset;
2N/A /*
2N/A * To maintain the compatibility with Zeus mo file,
2N/A * msg_id's are stored in descending order.
2N/A * If the ascending order is desired, change "msgfmt.c"
2N/A * and switch msg_id_str and key_string in the following
2N/A * strcmp() statement.
2N/A */
2N/A val = *(unsigned char *)msg_id_str - kc;
2N/A if ((val == 0) &&
2N/A (val = strcmp(msg_id_str, key_string)) == 0) {
2N/A return (messages->msg_strs
2N/A + check_msg_list->msgstr_offset);
2N/A } else if (val < 0) {
2N/A if (check_msg_list->less != LEAFINDICATOR) {
2N/A check_msg_list = messages->msg_list +
2N/A check_msg_list->less;
2N/A continue;
2N/A }
2N/A return ((char *)key_string);
2N/A } else {
2N/A /* val > 0 */
2N/A if (check_msg_list->more != LEAFINDICATOR) {
2N/A check_msg_list = messages->msg_list +
2N/A check_msg_list->more;
2N/A continue;
2N/A }
2N/A return ((char *)key_string);
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * sun_setmsg
2N/A *
2N/A * INPUT
2N/A * mnp - message node
2N/A * addr - address to the mmapped file
2N/A * size - size of the file
2N/A *
2N/A * RETURN
2N/A * 0 - either T_SUN_MO or T_ILL_MO has been set
2N/A * 1 - not a valid sun mo file
2N/A * -1 - failed
2N/A */
2N/Astatic int
2N/Asun_setmsg(Msg_node *mnp, char *addr, size_t size)
2N/A{
2N/A struct msg_info *sun_header;
2N/A Msg_s_node *p;
2N/A uint32_t first_4bytes;
2N/A int mid, count;
2N/A int struct_size, struct_size_old;
2N/A int msg_struct_size;
2N/A
2N/A if (size < sizeof (struct msg_info)) {
2N/A /* invalid mo file */
2N/A mnp->type = T_ILL_MO;
2N/A#ifdef GETTEXT_DEBUG
2N/A gprintf(0, "********* exiting sun_setmsg\n");
2N/A printmnp(mnp, 1);
2N/A#endif
2N/A return (0);
2N/A }
2N/A
2N/A first_4bytes = *((uint32_t *)(uintptr_t)addr);
2N/A if (first_4bytes > INT_MAX) {
2N/A /*
2N/A * Not a valid sun mo file
2N/A */
2N/A return (1);
2N/A }
2N/A
2N/A /* candidate for sun mo */
2N/A
2N/A sun_header = (struct msg_info *)(uintptr_t)addr;
2N/A mid = sun_header->msg_mid;
2N/A count = sun_header->msg_count;
2N/A msg_struct_size = sun_header->msg_struct_size;
2N/A struct_size_old = (int)(OLD_MSG_STRUCT_SIZE * count);
2N/A struct_size = (int)(MSG_STRUCT_SIZE * count);
2N/A
2N/A if ((((count - 1) / 2) != mid) ||
2N/A ((msg_struct_size != struct_size_old) &&
2N/A (msg_struct_size != struct_size))) {
2N/A /* invalid mo file */
2N/A mnp->type = T_ILL_MO;
2N/A#ifdef GETTEXT_DEBUG
2N/A gprintf(0, "********* exiting sun_setmsg\n");
2N/A printmnp(mnp, 1);
2N/A#endif
2N/A return (0);
2N/A }
2N/A /* valid sun mo file */
2N/A
2N/A p = malloc(sizeof (Msg_s_node));
2N/A if (p == NULL) {
2N/A return (-1);
2N/A }
2N/A
2N/A p->msg_file_info = sun_header;
2N/A p->msg_list = (struct msg_struct *)(uintptr_t)
2N/A (addr + sizeof (struct msg_info));
2N/A p->msg_ids = (char *)(addr + sizeof (struct msg_info) +
2N/A struct_size);
2N/A p->msg_strs = (char *)(addr + sizeof (struct msg_info) +
2N/A struct_size + sun_header->str_count_msgid);
2N/A
2N/A mnp->msg.sunmsg = p;
2N/A mnp->type = T_SUN_MO;
2N/A#ifdef GETTEXT_DEBUG
2N/A gprintf(0, "******** exiting sun_setmsg\n");
2N/A printmnp(mnp, 1);
2N/A#endif
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * setmsg
2N/A *
2N/A * INPUT
2N/A * mnp - message node
2N/A * addr - address to the mmapped file
2N/A * size - size of the file
2N/A *
2N/A * RETURN
2N/A * 0 - succeeded
2N/A * -1 - failed
2N/A */
2N/Astatic int
2N/Asetmsg(Msg_node *mnp, char *addr, size_t size)
2N/A{
2N/A int ret;
2N/A if ((ret = sun_setmsg(mnp, addr, size)) <= 0)
2N/A return (ret);
2N/A
2N/A return (gnu_setmsg(mnp, addr, size));
2N/A}
2N/A
2N/Astatic char *
2N/Ahandle_type_mo(Msg_node *mnp, struct msg_pack *mp)
2N/A{
2N/A char *result;
2N/A
2N/A switch (mnp->type) {
2N/A case T_ILL_MO:
2N/A /* invalid MO */
2N/A return (NULL);
2N/A case T_SUN_MO:
2N/A /* Sun MO found */
2N/A mp->status |= ST_SUN_MO_FOUND;
2N/A
2N/A if (mp->plural) {
2N/A /*
2N/A * *ngettext is called against
2N/A * Sun MO file
2N/A */
2N/A int exp = (mp->n == 1);
2N/A result = (char *)mp->msgid1;
2N/A if (!exp)
2N/A result = (char *)mp->msgid2;
2N/A return (result);
2N/A }
2N/A result = key_2_text(mnp->msg.sunmsg, mp->msgid1);
2N/A if (!mnp->trusted) {
2N/A result = check_format(mp->msgid1, result, 0);
2N/A }
2N/A return (result);
2N/A case T_GNU_MO:
2N/A /* GNU MO found */
2N/A mp->status |= ST_GNU_MO_FOUND;
2N/A
2N/A result = gnu_key_2_text(mnp->msg.gnumsg,
2N/A get_codeset(mp->domain), mp);
2N/A
2N/A if (result == mp->msgid1 || result == mp->msgid2) {
2N/A /* no valid msg found */
2N/A return (result);
2N/A }
2N/A
2N/A /* valid msg found */
2N/A mp->status |= ST_GNU_MSG_FOUND;
2N/A
2N/A if (!mnp->trusted) {
2N/A result = check_format(mp->msgid1, result, 0);
2N/A if (result == mp->msgid1) {
2N/A DFLTMSG(result, mp->msgid1, mp->msgid2,
2N/A mp->n, mp->plural);
2N/A }
2N/A }
2N/A return (result);
2N/A default:
2N/A /* this should never happen */
2N/A DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
2N/A return (result);
2N/A }
2N/A /* NOTREACHED */
2N/A}
2N/A
2N/A/*
2N/A * handle_mo() returns NULL if invalid MO found.
2N/A */
2N/Achar *
2N/Ahandle_mo(struct msg_pack *mp)
2N/A{
2N/A int fd;
2N/A char *result;
2N/A struct stat64 statbuf;
2N/A Msg_node *mnp;
2N/A Gettext_t *gt = global_gt;
2N/A
2N/A#define CONNECT_ENTRY \
2N/A mnp->next = gt->m_node; \
2N/A gt->m_node = mnp; \
2N/A gt->c_m_node = mnp
2N/A
2N/A#ifdef GETTEXT_DEBUG
2N/A gprintf(0, "*************** handle_mo(0x%p)\n", (void *)mp);
2N/A printmp(mp, 1);
2N/A#endif
2N/A
2N/A mnp = check_cache(mp);
2N/A
2N/A if (mnp != NULL) {
2N/A /* cache found */
2N/A return (handle_type_mo(mnp, mp));
2N/A }
2N/A
2N/A /*
2N/A * Valid entry not found in the cache
2N/A */
2N/A mnp = calloc(1, sizeof (Msg_node));
2N/A if (mnp == NULL) {
2N/A DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
2N/A return (result);
2N/A }
2N/A mnp->hashid = mp->hash_domain;
2N/A mnp->path = strdup(mp->msgfile);
2N/A if (mnp->path == NULL) {
2N/A free(mnp);
2N/A DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
2N/A return (result);
2N/A }
2N/A
2N/A fd = nls_safe_open(mp->msgfile, &statbuf, &mp->trusted, !mp->nlsp);
2N/A if ((fd == -1) || (statbuf.st_size > LONG_MAX)) {
2N/A if (fd != -1)
2N/A (void) close(fd);
2N/A mnp->type = T_ILL_MO;
2N/A CONNECT_ENTRY;
2N/A return (NULL);
2N/A }
2N/A mp->fsz = (size_t)statbuf.st_size;
2N/A mp->addr = mmap(NULL, mp->fsz, PROT_READ, MAP_SHARED, fd, 0);
2N/A (void) close(fd);
2N/A
2N/A if (mp->addr == MAP_FAILED) {
2N/A free(mnp->path);
2N/A free(mnp);
2N/A DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
2N/A return (result);
2N/A }
2N/A
2N/A if (setmsg(mnp, (char *)mp->addr, mp->fsz) == -1) {
2N/A free(mnp->path);
2N/A free(mnp);
2N/A (void) munmap(mp->addr, mp->fsz);
2N/A DFLTMSG(result, mp->msgid1, mp->msgid2, mp->n, mp->plural);
2N/A return (result);
2N/A }
2N/A mnp->trusted = mp->trusted;
2N/A CONNECT_ENTRY;
2N/A
2N/A return (handle_type_mo(mnp, mp));
2N/A}