[PATCH] var: Call setlocale when LC variables are set
Herbert Xu <[email protected]> Sat, 13 Jun 2026 20:25:27 +0800
| Newsgroups | org.kernel.vger.dash |
|---|---|
| Message-ID | <[email protected]> |
Vladimir Kudrya <[email protected]> wrote: > Hi! > It seems that this commit c5bf9702 changed behavior of ${var#?} under > LC_ALL=C, which should consider characters to be one byte long and cut > accordingly. > > This code: > > var=联; > LC_ALL=C > while [ -n "$var" ]; do > cut_var=${var#?} > byte=${var%"$cut_var"} > printf '>%02x<\n' "'$byte" > var=$cut_var > done > unset LC_ALL > > should output: > > >e8< > >81< > >94< > > It works reliably in sh 0.5.12, also ash, bash, zsh (both in POSIX and > native modes), but is broken in dash 0.5.13. > > More context can be found at: > https://github.com/Vladimir-csp/app2unit/issues/17 Yes this is a consequence of dash only calling setlocale on startup. This patch should fix the problem: ---8<--- When any shell-relevant locale environment variable changes, call putenv followed by setlocale so that the change is effective for the current shell. Reported-by: Vladimir Kudrya <[email protected]> Fixes: cb669294463e ("shell: Call setlocale") Signed-off-by: Herbert Xu <[email protected]> diff --git a/src/main.c b/src/main.c index 5d25b8d..2ab506c 100644 --- a/src/main.c +++ b/src/main.c @@ -32,7 +32,6 @@ * SUCH DAMAGE. */ -#include <locale.h> #include <stdio.h> #include <signal.h> #include <sys/stat.h> @@ -104,8 +103,6 @@ main(int argc, char **argv) monitor(4, etext, profile_buf, sizeof profile_buf, 50); #endif - setlocale(LC_ALL, ""); - state = 0; if (unlikely(setjmp(main_handler.loc))) { int e; diff --git a/src/var.c b/src/var.c index 90fc896..e76abe3 100644 --- a/src/var.c +++ b/src/var.c @@ -32,6 +32,7 @@ * SUCH DAMAGE. */ +#include <locale.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> @@ -81,6 +82,12 @@ MKINIT char defoptindvar[] = "OPTIND=1"; int lineno; char linenovar[sizeof("LINENO=")+sizeof(int)*CHAR_BIT/3+1] = "LINENO="; +static void changelocale(const char *val) +{ + putenv((char *)val); + setlocale(LC_ALL, nullstr); +} + /* Some macros in var.h depend on the order, add new variables to the end. */ struct var varinit[] = { #if ATTY @@ -101,6 +108,11 @@ struct var varinit[] = { { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM\0", 0 }, { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE\0", sethistsize }, #endif + { 0, VSTRFIXED|VTEXTFIXED|VFULL|VUNSET, "LC_ALL\0", changelocale }, + { 0, VSTRFIXED|VTEXTFIXED|VFULL|VUNSET, "LC_COLLATE\0", changelocale }, + { 0, VSTRFIXED|VTEXTFIXED|VFULL|VUNSET, "LC_CTYPE\0", changelocale }, + { 0, VSTRFIXED|VTEXTFIXED|VFULL|VUNSET, "LC_NUMERIC\0", changelocale }, + { 0, VSTRFIXED|VTEXTFIXED|VFULL|VUNSET, "LANG\0", changelocale }, }; STATIC struct var *vartab[VTABSIZE]; @@ -160,6 +172,19 @@ static char *varnull(const char *s) return strchrnul(s, '=') + 1; } +static void varfunc(struct var *vp) +{ + const char *s; + + if (!vp->func) + return; + + s = vp->text; + if (!(vp->flags & VFULL)) + s = varnull(s); + (*vp->func)(s); +} + /* * This routine initializes the builtin variables. It is called when the * shell is initialized. @@ -299,8 +324,8 @@ out_free: vp->text = s; vp->flags = flags; - if (vp->func && (flags & VNOFUNC) == 0) - (*vp->func)(varnull(s)); + if (!(flags & VNOFUNC)) + varfunc(vp); out: return vp; @@ -536,8 +561,8 @@ poplocalvars(void) ckfree(vp->text); vp->flags = lvp->flags; vp->text = lvp->text; - if (vp->func && !(vp->flags & VNOFUNC)) - (*vp->func)(varnull(vp->text)); + if (!(vp->flags & VNOFUNC)) + varfunc(vp); } ckfree(lvp); } diff --git a/src/var.h b/src/var.h index f6fb320..3d837a7 100644 --- a/src/var.h +++ b/src/var.h @@ -48,7 +48,7 @@ #define VSTACK 0x10 /* text is allocated on the stack */ #define VUNSET 0x20 /* the variable is not set */ #define VNOFUNC 0x40 /* don't call the callback function */ -/* #define VNOSET 0x80 do not set variable - just readonly test */ +#define VFULL 0x80 /* pass value suitable for putenv */ #define VNOSAVE 0x100 /* when text is on the heap before setvareq */ -- Email: Herbert Xu <[email protected]> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt