sh: Apply locale vars on builtins, recognize LC_MESSAGES as a locale var.
This allows doing things like LC_ALL=C some_builtin to run a builtin under a different locale, just like is possible with external programs. The immediate reason is that this allows making printf(1) a builtin without breaking things like LC_NUMERIC=C printf '%f\n' 1.2 This change also affects special builtins, as even though the assignment is persistent, the export is only to the builtin (unless the variable was already exported). Note: for this to work for builtins that also exist as external programs such as /bin/test, the setlocale() call must be under #ifndef SHELL. The shell will do the setlocale() calls which may not agree with the environment variables.
This commit is contained in:
parent
d18129ea1d
commit
e4b50334ec
@ -937,6 +937,8 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
|
||||
cmdentry.special = 1;
|
||||
if (cmdentry.special)
|
||||
listsetvar(cmdenviron);
|
||||
if (argc > 0)
|
||||
bltinsetlocale();
|
||||
commandname = argv[0];
|
||||
argptr = argv + 1;
|
||||
nextopt_optptr = NULL; /* initialize nextopt */
|
||||
@ -944,6 +946,8 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
|
||||
exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
|
||||
flushall();
|
||||
cmddone:
|
||||
if (argc > 0)
|
||||
bltinunsetlocale();
|
||||
cmdenviron = NULL;
|
||||
out1 = &output;
|
||||
out2 = &errout;
|
||||
|
75
bin/sh/var.c
75
bin/sh/var.c
@ -122,6 +122,14 @@ STATIC const struct varinit varinit[] = {
|
||||
|
||||
STATIC struct var *vartab[VTABSIZE];
|
||||
|
||||
STATIC const char *const locale_names[7] = {
|
||||
"LC_COLLATE", "LC_CTYPE", "LC_MONETARY",
|
||||
"LC_NUMERIC", "LC_TIME", "LC_MESSAGES", NULL
|
||||
};
|
||||
STATIC const int locale_categories[7] = {
|
||||
LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES, 0
|
||||
};
|
||||
|
||||
STATIC struct var **hashvar(const char *);
|
||||
STATIC int varequal(const char *, const char *);
|
||||
STATIC int localevar(const char *);
|
||||
@ -258,11 +266,7 @@ setvar(const char *name, const char *val, int flags)
|
||||
STATIC int
|
||||
localevar(const char *s)
|
||||
{
|
||||
static const char *lnames[7] = {
|
||||
"ALL", "COLLATE", "CTYPE", "MONETARY",
|
||||
"NUMERIC", "TIME", NULL
|
||||
};
|
||||
const char **ss;
|
||||
const char *const *ss;
|
||||
|
||||
if (*s != 'L')
|
||||
return 0;
|
||||
@ -270,8 +274,10 @@ localevar(const char *s)
|
||||
return 1;
|
||||
if (strncmp(s + 1, "C_", 2) != 0)
|
||||
return 0;
|
||||
for (ss = lnames; *ss ; ss++)
|
||||
if (varequal(s + 3, *ss))
|
||||
if (varequal(s + 3, "ALL"))
|
||||
return 1;
|
||||
for (ss = locale_names; *ss ; ss++)
|
||||
if (varequal(s + 3, *ss + 3))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
@ -437,6 +443,61 @@ bltinlookup(const char *name, int doall)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set up locale for a builtin (LANG/LC_* assignments).
|
||||
*/
|
||||
void
|
||||
bltinsetlocale(void)
|
||||
{
|
||||
struct strlist *lp;
|
||||
int act = 0;
|
||||
char *loc, *locdef;
|
||||
int i;
|
||||
|
||||
for (lp = cmdenviron ; lp ; lp = lp->next) {
|
||||
if (localevar(lp->text)) {
|
||||
act = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!act)
|
||||
return;
|
||||
loc = bltinlookup("LC_ALL", 0);
|
||||
INTOFF;
|
||||
if (loc != NULL) {
|
||||
setlocale(LC_ALL, loc);
|
||||
INTON;
|
||||
return;
|
||||
}
|
||||
locdef = bltinlookup("LANG", 0);
|
||||
for (i = 0; locale_names[i] != NULL; i++) {
|
||||
loc = bltinlookup(locale_names[i], 0);
|
||||
if (loc == NULL)
|
||||
loc = locdef;
|
||||
if (loc != NULL)
|
||||
setlocale(locale_categories[i], loc);
|
||||
}
|
||||
INTON;
|
||||
}
|
||||
|
||||
/*
|
||||
* Undo the effect of bltinlocaleset().
|
||||
*/
|
||||
void
|
||||
bltinunsetlocale(void)
|
||||
{
|
||||
struct strlist *lp;
|
||||
|
||||
INTOFF;
|
||||
for (lp = cmdenviron ; lp ; lp = lp->next) {
|
||||
if (localevar(lp->text)) {
|
||||
setlocale(LC_ALL, "");
|
||||
return;
|
||||
}
|
||||
}
|
||||
INTON;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generate a list of exported variables. This routine is used to construct
|
||||
|
@ -107,6 +107,8 @@ struct strlist;
|
||||
void listsetvar(struct strlist *);
|
||||
char *lookupvar(const char *);
|
||||
char *bltinlookup(const char *, int);
|
||||
void bltinsetlocale(void);
|
||||
void bltinunsetlocale(void);
|
||||
char **environment(void);
|
||||
int showvarscmd(int, char **);
|
||||
int exportcmd(int, char **);
|
||||
|
133
tools/regression/bin/sh/builtins/locale1.0
Normal file
133
tools/regression/bin/sh/builtins/locale1.0
Normal file
@ -0,0 +1,133 @@
|
||||
# $FreeBSD$
|
||||
# Note: this test depends on strerror() using locale.
|
||||
|
||||
failures=0
|
||||
|
||||
check() {
|
||||
if ! eval "[ $1 ]"; then
|
||||
echo "Failed: $1 at $2"
|
||||
: $((failures += 1))
|
||||
fi
|
||||
}
|
||||
|
||||
unset LANG LC_ALL LC_COLLATE LC_CTYPE LC_MONETARY LC_NUMERIC LC_TIME LC_MESSAGES
|
||||
|
||||
msgeng="No such file or directory"
|
||||
msgdut="Bestand of map niet gevonden"
|
||||
|
||||
# Verify C locale error message.
|
||||
case $(command . /var/empty/foo 2>&1) in
|
||||
*"$msgeng"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
# Various locale variables that should not affect the message.
|
||||
case $(LC_ALL=C command . /var/empty/foo 2>&1) in
|
||||
*"$msgeng"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
case $(LC_ALL=C LANG=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in
|
||||
*"$msgeng"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
case $(LC_ALL=C LC_MESSAGES=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in
|
||||
*"$msgeng"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
case $(LC_CTYPE=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in
|
||||
*"$msgeng"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
# Verify Dutch message.
|
||||
case $(export LANG=nl_NL.ISO8859-1; command . /var/empty/foo 2>&1) in
|
||||
*"$msgdut"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
case $(export LC_MESSAGES=nl_NL.ISO8859-1; command . /var/empty/foo 2>&1) in
|
||||
*"$msgdut"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
case $(export LC_ALL=nl_NL.ISO8859-1; command . /var/empty/foo 2>&1) in
|
||||
*"$msgdut"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
case $(LANG=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in
|
||||
*"$msgdut"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
case $(LC_MESSAGES=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in
|
||||
*"$msgdut"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
case $(LC_ALL=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in
|
||||
*"$msgdut"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
# Verify that command assignments do not set the locale persistently.
|
||||
case $(command . /var/empty/foo 2>&1) in
|
||||
*"$msgeng"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
case $(LANG=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1; command . /var/empty/foo 2>&1) in
|
||||
*"$msgdut"*"$msgeng"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
case $(LC_MESSAGES=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1; command . /var/empty/foo 2>&1) in
|
||||
*"$msgdut"*"$msgeng"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
case $(LC_ALL=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1; command . /var/empty/foo 2>&1) in
|
||||
*"$msgdut"*"$msgeng"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
# Check special builtin; add colon invocation to avoid depending on certain fix.
|
||||
case $(LC_ALL=nl_NL.ISO8859-1 . /var/empty/foo 2>&1; :) in
|
||||
*"$msgdut"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
# Assignments on special builtins are exported to that builtin; the export
|
||||
# is not persistent.
|
||||
case $(LC_ALL=nl_NL.ISO8859-1 . /dev/null; . /var/empty/foo 2>&1) in
|
||||
*"$msgeng"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
case $(export LC_ALL; LC_ALL=nl_NL.ISO8859-1 . /dev/null; . /var/empty/foo 2>&1) in
|
||||
*"$msgdut"*) ok=1 ;;
|
||||
*) ok=0 ;;
|
||||
esac
|
||||
check '$ok -eq 1' $LINENO
|
||||
|
||||
exit $((failures > 0))
|
Loading…
Reference in New Issue
Block a user