Author: jilles
Date: Wed May  5 21:48:40 2010
New Revision: 207678
URL: http://svn.freebsd.org/changeset/base/207678

Log:
  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.

Added:
  head/tools/regression/bin/sh/builtins/locale1.0   (contents, props changed)
Modified:
  head/bin/sh/eval.c
  head/bin/sh/var.c
  head/bin/sh/var.h

Modified: head/bin/sh/eval.c
==============================================================================
--- head/bin/sh/eval.c  Wed May  5 21:24:18 2010        (r207677)
+++ head/bin/sh/eval.c  Wed May  5 21:48:40 2010        (r207678)
@@ -937,6 +937,8 @@ evalcommand(union node *cmd, int flags, 
                        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, 
                exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
                flushall();
 cmddone:
+               if (argc > 0)
+                       bltinunsetlocale();
                cmdenviron = NULL;
                out1 = &output;
                out2 = &errout;

Modified: head/bin/sh/var.c
==============================================================================
--- head/bin/sh/var.c   Wed May  5 21:24:18 2010        (r207677)
+++ head/bin/sh/var.c   Wed May  5 21:48:40 2010        (r207678)
@@ -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
 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

Modified: head/bin/sh/var.h
==============================================================================
--- head/bin/sh/var.h   Wed May  5 21:24:18 2010        (r207677)
+++ head/bin/sh/var.h   Wed May  5 21:48:40 2010        (r207678)
@@ -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 **);

Added: head/tools/regression/bin/sh/builtins/locale1.0
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/tools/regression/bin/sh/builtins/locale1.0     Wed May  5 21:48:40 
2010        (r207678)
@@ -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))
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to