#include "config.h"
#include "bashtypes.h"
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#if HAVE_LANGINFO_CODESET
# include <langinfo.h>
#endif
#include "bashintl.h"
#include "bashansi.h"
#include <stdio.h>
#include "chartypes.h"
#include <errno.h>
#include "shell.h"
#include "input.h"
#ifndef errno
extern int errno;
#endif
#if defined (HAVE_LOCALE_CHARSET)
extern const char *locale_charset (void);
#endif
extern void init_notfound_str (void);
extern int dump_translatable_strings, dump_po_strings;
int locale_utf8locale;
int locale_mb_cur_max;
int locale_shiftstates = 0;
int singlequote_translations = 0;
static char *default_locale;
static char *default_domain;
static char *default_dir;
static char *lc_all;
static char *lang;
static int reset_locale_vars (void);
static void locale_setblanks (void);
static int locale_isutf8 (char *);
void
set_default_locale (void)
{
#if defined (HAVE_SETLOCALE)
default_locale = setlocale (LC_ALL, "");
if (default_locale)
default_locale = savestring (default_locale);
#else
default_locale = savestring ("C");
#endif
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
locale_mb_cur_max = MB_CUR_MAX;
locale_utf8locale = locale_isutf8 (default_locale);
#if defined (HANDLE_MULTIBYTE)
locale_shiftstates = mblen ((char *)NULL, 0);
#else
locale_shiftstates = 0;
#endif
}
void
set_default_locale_vars (void)
{
char *val;
#if defined (HAVE_SETLOCALE)
# if defined (LC_CTYPE)
val = get_string_value ("LC_CTYPE");
if (val == 0 && lc_all && *lc_all)
{
setlocale (LC_CTYPE, lc_all);
locale_mb_cur_max = MB_CUR_MAX;
locale_utf8locale = locale_isutf8 (lc_all);
locale_setblanks ();
# if defined (HANDLE_MULTIBYTE)
locale_shiftstates = mblen ((char *)NULL, 0);
# else
locale_shiftstates = 0;
# endif
u32reset ();
}
# endif
# if defined (LC_COLLATE)
val = get_string_value ("LC_COLLATE");
if (val == 0 && lc_all && *lc_all)
setlocale (LC_COLLATE, lc_all);
# endif
# if defined (LC_MESSAGES)
val = get_string_value ("LC_MESSAGES");
if (val == 0 && lc_all && *lc_all)
setlocale (LC_MESSAGES, lc_all);
# endif
# if defined (LC_NUMERIC)
val = get_string_value ("LC_NUMERIC");
if (val == 0 && lc_all && *lc_all)
setlocale (LC_NUMERIC, lc_all);
# endif
# if defined (LC_TIME)
val = get_string_value ("LC_TIME");
if (val == 0 && lc_all && *lc_all)
setlocale (LC_TIME, lc_all);
# endif
#endif
val = get_string_value ("TEXTDOMAIN");
if (val && *val)
{
FREE (default_domain);
default_domain = savestring (val);
if (default_dir && *default_dir)
bindtextdomain (default_domain, default_dir);
}
val = get_string_value ("TEXTDOMAINDIR");
if (val && *val)
{
FREE (default_dir);
default_dir = savestring (val);
if (default_domain && *default_domain)
bindtextdomain (default_domain, default_dir);
}
init_notfound_str ();
}
int
set_locale_var (const char *var, const char *value)
{
int r;
char *x;
x = "";
errno = 0;
if (var[0] == 'T' && var[10] == 0)
{
FREE (default_domain);
default_domain = value ? savestring (value) : (char *)NULL;
if (default_dir && *default_dir)
bindtextdomain (default_domain, default_dir);
return (1);
}
else if (var[0] == 'T')
{
FREE (default_dir);
default_dir = value ? savestring (value) : (char *)NULL;
if (default_domain && *default_domain)
bindtextdomain (default_domain, default_dir);
return (1);
}
else if (var[3] == 'A')
{
FREE (lc_all);
if (value)
lc_all = savestring (value);
else
{
lc_all = (char *)xmalloc (1);
lc_all[0] = '\0';
}
#if defined (HAVE_SETLOCALE)
r = *lc_all ? ((x = setlocale (LC_ALL, lc_all)) != 0) : reset_locale_vars ();
if (x == 0)
{
if (errno == 0)
internal_warning ("setlocale: LC_ALL: %s (%s)", _("cannot change locale"), lc_all);
else
internal_warning ("setlocale: LC_ALL: %s (%s): %s", _("cannot change locale"), lc_all, strerror (errno));
}
locale_mb_cur_max = MB_CUR_MAX;
if (*lc_all && x)
locale_utf8locale = locale_isutf8 (lc_all);
locale_setblanks ();
# if defined (HANDLE_MULTIBYTE)
locale_shiftstates = mblen ((char *)NULL, 0);
# else
locale_shiftstates = 0;
# endif
u32reset ();
return r;
#else
return (1);
#endif
}
#if defined (HAVE_SETLOCALE)
else if (var[3] == 'C' && var[4] == 'T')
{
# if defined (LC_CTYPE)
if (lc_all == 0 || *lc_all == '\0')
{
x = setlocale (LC_CTYPE, get_locale_var ("LC_CTYPE"));
locale_mb_cur_max = MB_CUR_MAX;
if (x)
locale_utf8locale = locale_isutf8 (x);
locale_setblanks ();
#if defined (HANDLE_MULTIBYTE)
locale_shiftstates = mblen ((char *)NULL, 0);
#else
locale_shiftstates = 0;
#endif
u32reset ();
}
# endif
}
else if (var[3] == 'C' && var[4] == 'O')
{
# if defined (LC_COLLATE)
if (lc_all == 0 || *lc_all == '\0')
x = setlocale (LC_COLLATE, get_locale_var ("LC_COLLATE"));
# endif
}
else if (var[3] == 'M' && var[4] == 'E')
{
# if defined (LC_MESSAGES)
if (lc_all == 0 || *lc_all == '\0')
x = setlocale (LC_MESSAGES, get_locale_var ("LC_MESSAGES"));
# endif
}
else if (var[3] == 'N' && var[4] == 'U')
{
# if defined (LC_NUMERIC)
if (lc_all == 0 || *lc_all == '\0')
x = setlocale (LC_NUMERIC, get_locale_var ("LC_NUMERIC"));
# endif
}
else if (var[3] == 'T' && var[4] == 'I')
{
# if defined (LC_TIME)
if (lc_all == 0 || *lc_all == '\0')
x = setlocale (LC_TIME, get_locale_var ("LC_TIME"));
# endif
}
#endif
if (x == 0)
{
if (errno == 0)
internal_warning("setlocale: %s: %s (%s)", var, _("cannot change locale"), get_locale_var (var));
else
internal_warning("setlocale: %s: %s (%s): %s", var, _("cannot change locale"), get_locale_var (var), strerror (errno));
}
return (x != 0);
}
int
set_lang (const char *var, const char *value)
{
FREE (lang);
if (value)
lang = savestring (value);
else
{
lang = (char *)xmalloc (1);
lang[0] = '\0';
}
return ((lc_all == 0 || *lc_all == 0) ? reset_locale_vars () : 0);
}
void
set_default_lang (void)
{
char *v;
v = get_string_value ("LC_ALL");
set_locale_var ("LC_ALL", v);
v = get_string_value ("LANG");
set_lang ("LANG", v);
}
char *
get_locale_var (const char *var)
{
char *locale;
locale = lc_all;
if (locale == 0 || *locale == 0)
locale = get_string_value (var);
if (locale == 0 || *locale == 0)
locale = lang;
if (locale == 0 || *locale == 0)
#if 0#else
locale = "";
#endif
return (locale);
}
static int
reset_locale_vars (void)
{
char *t, *x;
int retval;
#if defined (HAVE_SETLOCALE)
if (lang == 0 || *lang == '\0')
maybe_make_export_env ();
if (setlocale (LC_ALL, lang ? lang : "") == 0)
return 0;
retval = 1;
x = 0;
# if defined (LC_CTYPE)
x = setlocale (LC_CTYPE, get_locale_var ("LC_CTYPE"));
if (x == 0)
retval = 0;
# endif
# if defined (LC_COLLATE)
t = setlocale (LC_COLLATE, get_locale_var ("LC_COLLATE"));
if (t == 0)
retval = 0;
# endif
# if defined (LC_MESSAGES)
t = setlocale (LC_MESSAGES, get_locale_var ("LC_MESSAGES"));
if (t == 0)
retval = 0;
# endif
# if defined (LC_NUMERIC)
t = setlocale (LC_NUMERIC, get_locale_var ("LC_NUMERIC"));
if (t == 0)
retval = 0;
# endif
# if defined (LC_TIME)
t = setlocale (LC_TIME, get_locale_var ("LC_TIME"));
if (t == 0)
retval = 0;
# endif
locale_mb_cur_max = MB_CUR_MAX;
if (x)
locale_utf8locale = locale_isutf8 (x);
locale_setblanks ();
# if defined (HANDLE_MULTIBYTE)
locale_shiftstates = mblen ((char *)NULL, 0);
# else
locale_shiftstates = 0;
# endif
u32reset ();
#endif
return retval;
}
#if defined (TRANSLATABLE_STRINGS)
char *
localetrans (const char *string, int len, size_t *lenp)
{
char *locale, *t;
char *translated;
size_t tlen;
if (string == 0 || *string == 0)
{
if (lenp)
*lenp = 0;
return ((char *)NULL);
}
locale = get_locale_var ("LC_MESSAGES");
if (locale == 0 || locale[0] == '\0' ||
(locale[0] == 'C' && locale[1] == '\0') || STREQ (locale, "POSIX"))
{
t = (char *)xmalloc (len + 1);
strcpy (t, string);
if (lenp)
*lenp = len;
return (t);
}
if (default_domain && *default_domain)
translated = dgettext (default_domain, string);
else
translated = (char *)string;
if (translated == string)
{
t = (char *)xmalloc (len + 1);
strcpy (t, string);
if (lenp)
*lenp = len;
}
else
{
tlen = strlen (translated);
t = (char *)xmalloc (tlen + 1);
strcpy (t, translated);
if (lenp)
*lenp = tlen;
}
return (t);
}
char *
mk_msgstr (char *string, int *foundnlp)
{
register int c, len;
char *result, *r, *s;
for (len = 0, s = string; s && *s; s++)
{
len++;
if (*s == '"' || *s == '\\')
len++;
else if (*s == '\n')
len += 5;
}
r = result = (char *)xmalloc (len + 3);
*r++ = '"';
for (s = string; s && (c = *s); s++)
{
if (c == '\n')
{
*r++ = '\\';
*r++ = 'n';
*r++ = '"';
*r++ = '\n';
*r++ = '"';
if (foundnlp)
*foundnlp = 1;
continue;
}
if (c == '"' || c == '\\')
*r++ = '\\';
*r++ = c;
}
*r++ = '"';
*r++ = '\0';
return result;
}
char *
locale_expand (const char *string, int start, int end, int lineno, size_t *lenp)
{
int tlen, foundnl;
char *temp, *t, *t2;
size_t len;
temp = (char *)xmalloc (end - start + 1);
for (tlen = 0, len = start; len < end; )
temp[tlen++] = string[len++];
temp[tlen] = '\0';
if (dump_translatable_strings)
{
if (dump_po_strings)
{
foundnl = 0;
t = mk_msgstr (temp, &foundnl);
t2 = foundnl ? "\"\"\n" : "";
printf ("#: %s:%d\nmsgid %s%s\nmsgstr \"\"\n",
yy_input_name (), lineno, t2, t);
free (t);
}
else
printf ("\"%s\"\n", temp);
if (lenp)
*lenp = tlen;
return (temp);
}
else if (*temp)
{
t = localetrans (temp, tlen, &len);
free (temp);
if (lenp)
*lenp = len;
return (t);
}
else
{
if (lenp)
*lenp = 0;
return (temp);
}
}
#endif
#define locale_isblank(c) \
((locale_utf8locale == 0 || ((c) & 0x80) == 0) && isblank ((unsigned char)(c)))
static void
locale_setblanks (void)
{
int x;
for (x = 0; x < sh_syntabsiz; x++)
{
if (locale_isblank (x))
sh_syntaxtab[x] |= CSHBRK|CBLANK;
else if (member (x, shell_break_chars))
{
sh_syntaxtab[x] |= CSHBRK;
sh_syntaxtab[x] &= ~CBLANK;
}
else
sh_syntaxtab[x] &= ~(CSHBRK|CBLANK);
}
}
static int
locale_isutf8 (char *lspec)
{
#if HAVE_LOCALE_CHARSET
const char *cp;
#else
char *cp, *encoding;
#endif
#if HAVE_LANGINFO_CODESET
cp = nl_langinfo (CODESET);
return (STREQ (cp, "UTF-8") || STREQ (cp, "utf8"));
#elif HAVE_LOCALE_CHARSET
cp = locale_charset ();
return (STREQ (cp, "UTF-8") || STREQ (cp, "utf8"));
#else
for (cp = lspec; *cp && *cp != '@' && *cp != '+' && *cp != ','; cp++)
{
if (*cp == '.')
{
for (encoding = ++cp; *cp && *cp != '@' && *cp != '+' && *cp != ','; cp++)
;
if ((cp - encoding == 5 && STREQN (encoding, "UTF-8", 5)) ||
(cp - encoding == 4 && STREQN (encoding, "utf8", 4)))
return 1;
else
return 0;
}
}
return 0;
#endif
}
#if defined (HAVE_LOCALECONV)
int
locale_decpoint (void)
{
struct lconv *lv;
lv = localeconv ();
return (lv && lv->decimal_point && lv->decimal_point[0]) ? lv->decimal_point[0] : '.';
}
#else
# undef locale_decpoint
int
locale_decpoint (void)
{
return '.';
}
#endif