/*
* 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
* 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 2015 Joyent, Inc.
*/
#include "lint.h"
#include "mtlib.h"
#include <ctype.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.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 *
{
const char *cur_locale;
char *language;
unsigned int n = (unsigned int)ln; /* we don't need long for n */
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** _real_gettext_u(\"%s\", \"%s\", "
"\"%s\", %d, %d, %d)\n",
#endif
return (NULL);
/*
* category may be LC_MESSAGES or LC_TIME
* locale contains the value of 'category'
*/
if (language) {
/*
* LANGUAGE is an empty string or
* LANGUAGE contains '/'.
* Ignore it.
*/
}
}
/*
* Query the current domain if domain argument is NULL pointer
*/
mydomain[0] = '\0';
/*
* if NULL is specified for domainname,
* use the currently bound domain.
*/
} else if (!*domain) {
/*
* if an empty string is specified
*/
} else {
cur_domain = (char *)domain;
}
if (cur_domain_len > TEXTDOMAINMAX) {
/* domain is invalid, return msg_id */
return (result);
}
/* no NLSPATH is defined in the environ */
/*
* If C locale,
* return the original msgid immediately.
*/
return (result);
}
} else {
/* NLSPATH is set */
int ret;
(const char *)nlspath, &cur_binding);
if (ret == -1) {
/* error occurred */
return (result);
} else if (ret == 0) {
}
}
NULL, TP_BINDING);
if (cur_domain_binding == NULL) {
return (result);
}
mp->n = n;
/*
* Spec1170 requires that we use NLSPATH if it's defined, to
* override any system default variables. If NLSPATH is not
* defined or if a message catalog is not found in any of the
* components (bindings) specified by NLSPATH, dcgettext_u() will
* search for the message catalog in either a) the binding path set
* by any previous application calls to bindtextdomain() or
* binding path so that we can search it if the message catalog
* is not found via NLSPATH. The original binding is restored before
* returning from this routine because the gettext routines should
* not change the binding set by the application. This allows
* bindtextdomain() to be called once for all gettext() calls in the
* application.
*/
/*
* First, examine NLSPATH
*/
if (nlspath) {
/*
* NLSPATH binding has been successfully built
*/
#ifdef GETTEXT_DEBUG
gprintf(0, "************************** examining NLSPATH\n");
gprintf(0, " cur_binding: \"%s\"\n",
#endif
/*
* cur_binding always ends with ':' before a null
* termination.
*/
while (*cur_binding) {
cb = cur_binding;
while (*cur_binding != ':')
cur_binding++;
cur_binding++;
if (cblen >= MAXPATHLEN) {
/* cur_binding too long */
return (result);
}
#ifdef GETTEXT_DEBUG
gprintf(0, "*******************"
"********************* \n");
gprintf(0, " msgfile: \"%s\"\n",
gprintf(0, "*******************"
"********************* \n");
#endif
if (result) {
return (result);
}
}
}
/*
* Next, examine LANGUAGE
*/
if (language) {
char *ret_msg;
/* valid msg found in GNU MO */
return (ret_msg);
}
/*
* handle_lang() may have overridden locale
*/
}
/*
* Finally, handle a single binding
*/
#ifdef GETTEXT_DEBUG
#endif
return (result);
}
if (result) {
return (result);
}
return (result);
} /* _real_gettext_u */
#define ALLFREE \
static void
{
while (tp) {
}
if (pathname)
if (ppaths)
if (lang)
}
/*
* process_nlspath(): process the NLSPATH environment variable.
*
* this routine looks at NLSPATH in the environment,
* and will try to build up the binding list based
* on the settings of NLSPATH.
*
* RETURN:
* -1: Error occurred
* 0: No error, but no binding list has been built
* 1: No error, and a binding list has been built
*
*/
static int
{
char *s; /* generic string ptr */
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** process_nlspath(%s, %s, "
"%s, 0x%p)\n", cur_domain,
#endif
if (cur_nls &&
return (1);
}
while (nnp) {
/* found */
return (1);
}
}
/* not found */
return (-1);
}
return (-1);
}
while (*s) {
if (*s == '_') {
s1 = s;
*s1++ = '\0';
} else if (*s == '.') {
s2 = s;
*s2++ = '\0';
}
s++;
}
/*
* now that we have the name (domain), we first look through NLSPATH,
* in an attempt to get the locale. A locale may be completely
* specified as "language_territory.codeset". NLSPATH consists
* of templates separated by ":" characters. The following are
* the substitution values within NLSPATH:
* %N = DEFAULT_DOMAIN
* %L = The value of the LC_MESSAGES category.
* %I = The language element from the LC_MESSAGES category.
* %t = The territory element from the LC_MESSAGES category.
* %c = The codeset element from the LC_MESSAGES category.
* %% = A single character.
* if we find one of these characters, we will carry out the
* appropriate substitution.
*/
return (-1);
}
s = (char *)nlspath; /* s has a content of NLSPATH */
while (*s) { /* march through NLSPATH */
if (*s == ':') {
/*
* this loop only occurs if we have to replace
* ":" by "name". replace_nls_option() below
* will handle the subsequent ":"'s.
*/
return (-1);
}
domain_len + 1);
} else {
}
++s;
continue;
}
/* replace Substitution field */
if (s == NULL) {
return (-1);
}
/* if we've found a valid file: */
if (*pathname) {
/* add template to end of chain of pathnames: */
return (-1);
}
path_len + 1);
} else {
}
}
if (*s) {
++s;
}
}
/*
* now that we've handled the pathname templates, concatenate them
* all into the form "template1:template2:..." for _bindtextdomain_u()
*/
if (ppaths_len != 0) {
return (-1);
}
*ppaths = '\0';
} else {
return (0);
}
/*
* extract the path templates (fifo), and concatenate them
* all into a ":" separated string for _bindtextdomain_u()
*/
s = ppaths;
while (pnlstmp) {
*s++ = ':';
}
*s = '\0';
return (-1);
} else {
}
return (-1);
} else {
}
return (-1);
} else {
}
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** existing process_nlspath with success\n");
#endif
return (1);
}
/*
* This routine will replace substitution parameters in NLSPATH
* with appropiate values.
*/
static char *
{
char *t, *u;
char *limit;
t = pathname;
while (*s && *s != ':') {
if (t < limit) {
/*
* %% is considered a single % character (XPG).
* %L : LC_MESSAGES (XPG4) LANG(XPG3)
* %l : The language element from the current locale.
* (XPG3, XPG4)
*/
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 {
/* too long pathname */
return (NULL);
}
++s;
}
*t = '\0';
return (s);
}
char *
int type)
{
char **binding_addr;
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** _real_bindtextdomain_u(\"%s\", "
"\"%s\", \"%s\")\n",
#endif
/*
* If domain is a NULL pointer, no change will occur regardless
* of binding value. Just return NULL.
*/
return (NULL);
}
/*
* Global Binding is not supported any more.
* Just return NULL if domain is NULL string.
*/
if (*domain == '\0') {
return (NULL);
}
/* linear search for binding, rebind if found, add if not */
while (bind) {
/*
* Domain found.
*/
/*
* if binding is null, then query
*/
return (*binding_addr);
}
/* replace existing binding with new binding */
if (*binding_addr) {
free(*binding_addr);
}
return (NULL);
}
#ifdef GETTEXT_DEBUG
printlist();
#endif
return (*binding_addr);
}
} /* while (bind) */
/* domain has not been found in the list at this point */
if (binding) {
/*
* domain is not found, but binding is not NULL.
* Then add a new node to the end of linked list.
*/
return (NULL);
}
return (NULL);
}
return (NULL);
}
if (prev) {
/* reached the end of list */
} else {
/* list was empty */
}
#ifdef GETTEXT_DEBUG
printlist();
#endif
return (*binding_addr);
} else {
/*
* Query of domain which is not found in the list
* for bindtextdomain, returns defaultbind
* for bind_textdomain_codeset, returns NULL
*/
if (type == TP_BINDING) {
return ((char *)defaultbind);
} else {
return (NULL);
}
} /* if (binding) */
/* Must not reach here */
} /* _real_bindtextdomain_u */
char *
{
char *p;
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** _textdomain_u(\"%s\", 0x%p)\n",
#endif
/* Query is performed for NULL domain pointer */
return (result);
}
/* check for error. */
/*
* domain is limited to TEXTDOMAINMAX bytes
* excluding a null termination.
*/
if (domain_len > TEXTDOMAINMAX) {
/* too long */
return (NULL);
}
/*
* Calling textdomain() with a null domain string sets
* the domain to the default domain.
* If non-null string is passwd, current domain is changed
* to the new domain.
*/
/* actually this if clause should be protected from signals */
if (*domain == '\0') {
}
} else {
if (p == NULL)
return (NULL);
CURRENT_DOMAIN(gt) = p;
}
return (result);
} /* _textdomain_u */
/*
* key_2_text() translates msd_id into target string.
*/
static char *
{
int val;
char *msg_id_str;
#ifdef GETTEXT_DEBUG
gprintf(0, "*************** key_2_text(0x%p, \"%s\")\n",
#endif
for (;;) {
/*
* To maintain the compatibility with Zeus mo file,
* msg_id's are stored in descending order.
* If the ascending order is desired, change "msgfmt.c"
* and switch msg_id_str and key_string in the following
* strcmp() statement.
*/
if ((val == 0) &&
} else if (val < 0) {
continue;
}
return ((char *)key_string);
} else {
/* val > 0 */
continue;
}
return ((char *)key_string);
}
}
}
/*
* sun_setmsg
*
* INPUT
* mnp - message node
* addr - address to the mmapped file
* size - size of the file
*
* RETURN
* 0 - either T_SUN_MO or T_ILL_MO has been set
* 1 - not a valid sun mo file
* -1 - failed
*/
static int
{
Msg_s_node *p;
int msg_struct_size;
/* invalid mo file */
#ifdef GETTEXT_DEBUG
gprintf(0, "********* exiting sun_setmsg\n");
#endif
return (0);
}
if (first_4bytes > INT_MAX) {
/*
* Not a valid sun mo file
*/
return (1);
}
/* candidate for sun mo */
((msg_struct_size != struct_size_old) &&
(msg_struct_size != struct_size))) {
/* invalid mo file */
#ifdef GETTEXT_DEBUG
gprintf(0, "********* exiting sun_setmsg\n");
#endif
return (0);
}
/* valid sun mo file */
p = malloc(sizeof (Msg_s_node));
if (p == NULL) {
return (-1);
}
p->msg_file_info = sun_header;
#ifdef GETTEXT_DEBUG
gprintf(0, "******** exiting sun_setmsg\n");
#endif
return (0);
}
/*
* setmsg
*
* INPUT
* mnp - message node
* addr - address to the mmapped file
* size - size of the file
*
* RETURN
* 0 - succeeded
* -1 - failed
*/
static int
{
int ret;
return (ret);
}
static char *
{
char *result;
case T_ILL_MO:
/* invalid MO */
return (NULL);
case T_SUN_MO:
/* Sun MO found */
/*
* *ngettext is called against
* Sun MO file
*/
if (!exp)
return (result);
}
}
return (result);
case T_GNU_MO:
/* GNU MO found */
/* no valid msg found */
return (result);
}
/* valid msg found */
}
}
return (result);
default:
/* this should never happen */
return (result);
}
/* NOTREACHED */
}
/*
* handle_mo() returns NULL if invalid MO found.
*/
char *
{
int fd;
char *result;
#define CONNECT_ENTRY \
#ifdef GETTEXT_DEBUG
#endif
/* cache found */
}
/*
* Valid entry not found in the cache
*/
return (result);
}
return (result);
}
if (fd != -1)
return (NULL);
}
return (result);
}
return (result);
}
}