NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: bin/58609 (sh(1) ignores interactive locale changes)
Synopsis: sh(1) ignores interactive locale changes
State-Changed-From-To: suspended->analyzed
State-Changed-By: riastradh%NetBSD.org@localhost
State-Changed-When: Wed, 13 May 2026 19:16:22 +0000
State-Changed-Why:
I applied the following changes on top of a netbsd-11 tree, and then
applied the following patch extracted from the test hg repository
(tweaked to fix a trailing whitespace merge conflict around line 1208
of bin/sh/var.c), and then made dependall install in lib/libedit and
bin/sh. The result was:
1. If started with LC_CTYPE=C.UTF-8, entering £ works fine.
2. If started with LC_CTYPE=C, entering £ moves the cursor to the
beginning of the line but does not delete it:
$ echo
^
transitions, on entering £ (i.e., octets 0xc2 0xa3), to
$ echo
^
where ^ indicates the position of the cursor on the line above.
Further insertion maintains `echo' on the command line:
$ asdfecho
^
And if I hit return I get:
sh: asdfecho: not found
(This is a change from before, when the input was apparently just
ignored.)
3. If started with LC_CTYPE=C and then interactively setting
LC_CTYPE=C.UTF-8, entering £ works fine.
So while (2) is weird, the important ones, (1) and (3), seem to work
fine with the patch (which includes some other changes that have since
been committed on HEAD).
Dependencies:
https://mail-index.netbsd.org/source-changes/2025/12/14/msg159526.html
https://mail-index.netbsd.org/source-changes/2025/12/16/msg159550.html
Patch:
# HG changeset patch
# User Robert Elz <kre%NetBSD.org@localhost>
# Date 1762139127 -25200
# Mon Nov 03 10:05:27 2025 +0700
# Branch trunk
# Node ID d12208786ee4c8f9bde73ec33f18830ba622c450
# Parent 8cdcca3e3510391c74825e9d7b67abc5a9967efb
PR bin/58609: Handle locale changes while sh is running
Previously, sh internally used the locale settings as they were when
it started, and changed to the LC_* vars (and LANG) were made available
(if exported) to commands run by the shell, but not to the shell itself
(nor its builtin commands).
Now the shell deals with changes to those variables, and updates the
locale settings (setlocale(3)) when they occur. Whether or not all
the cases are handled ideally might be a matter for some debate, and
perhaps later changes, but at least something is being done now.
To do this, several internal changes were needed - now the callback
function called when a variable is set has a third parameter - a pointer
to the struct var involved (in addition to the new value, and setting
flags). This allows comparisons between the new and old values (and more).
In addition a new flag has been added to the variable state (used only
when a var setting callback function exists) to determine whether that
callback function is called before, or after, the variable value has been
updated (different callback functions need one or the other - many simply
don't care).
Note that these changes require libedit.so.3.2 (or later).
This also allows the "setenv("TERM", ...)" hack to be removed.
diff -r 8cdcca3e3510 -r d12208786ee4 bin/sh/exec.c
--- a/bin/sh/exec.c Mon Nov 03 09:37:25 2025 +0700
+++ b/bin/sh/exec.c Mon Nov 03 10:05:27 2025 +0700
@@ -844,15 +844,14 @@ hashcd(void)
*/
void
-changepath(char *newval, int flags __unused)
+changepath(const char *new, int flags __unused, struct var *vp __unused)
{
- const char *old, *new;
+ const char *old;
int idx;
int firstchange;
int bltin;
old = pathval();
- new = newval;
firstchange = 9999; /* assume no change */
idx = 0;
bltin = -1;
diff -r 8cdcca3e3510 -r d12208786ee4 bin/sh/exec.h
--- a/bin/sh/exec.h Mon Nov 03 09:37:25 2025 +0700
+++ b/bin/sh/exec.h Mon Nov 03 10:05:27 2025 +0700
@@ -63,13 +63,15 @@ struct cmdentry {
extern const char *pathopt; /* set by padvance */
+struct var;
+
void shellexec(char **, char **, const char *, int, int) __dead;
char *padvance(const char **, const char *, int);
void find_command(char *, struct cmdentry *, int, const char *);
int (*find_builtin(char *))(int, char **);
int (*find_splbltin(char *))(int, char **);
void hashcd(void);
-void changepath(char *, int);
+void changepath(const char *, int, struct var *);
void deletefuncs(void);
void getcmdentry(char *, struct cmdentry *);
union node;
diff -r 8cdcca3e3510 -r d12208786ee4 bin/sh/histedit.c
--- a/bin/sh/histedit.c Mon Nov 03 09:37:25 2025 +0700
+++ b/bin/sh/histedit.c Mon Nov 03 10:05:27 2025 +0700
@@ -99,6 +99,18 @@ static unsigned char sh_complete(EditLin
static FILE *Hist_File_Open(const char *);
/*
+ * a getenv(3) lookalike function for libedit to
+ * use so it can access current values of sh variables
+ * so there is no need to keep doing setenv() of anything
+ * it might want to lookup.
+ */
+static char *
+el_getenv(const char *name)
+{
+ return bltinlookup(name, 1);
+}
+
+/*
* Set history and editing status. Called whenever the status may
* have changed (figures out what to do).
*/
@@ -122,8 +134,10 @@ histedit(void)
INTON;
if (hist != NULL) {
- sethistsize(histsizeval(), histsizeflags());
- sethistfile(histfileval(), histfileflags());
+ sethistsize(histsizeval(),
+ histsizeflags(), NULL);
+ sethistfile(histfileval(),
+ histfileflags(), NULL);
} else
out2str("sh: can't initialize history\n");
}
@@ -131,8 +145,6 @@ histedit(void)
/*
* turn editing on
*/
- char *term;
-
INTOFF;
if (el_in == NULL)
el_in = fdopen(0, "r");
@@ -145,28 +157,6 @@ histedit(void)
if (tracefile)
el_err = tracefile;
#endif
- /*
- * This odd piece of code doesn't affect the shell
- * at all, the environment modified here is the
- * stuff accessed via "environ" (the incoming
- * environment to the shell) which is only ever
- * touched at sh startup time (long before we get
- * here) and ignored thereafter.
- *
- * But libedit calls getenv() to discover TERM
- * and that searches the "environ" environment,
- * not the shell's internal variable data struct,
- * so we need to make sure that TERM in there is
- * correct.
- *
- * This sequence copies TERM from the shell into
- * the old "environ" environment.
- */
- term = lookupvar("TERM");
- if (term)
- setenv("TERM", term, 1);
- else
- unsetenv("TERM");
el = el_init("sh", el_in, el_out, el_err);
VTRACE(DBG_HISTORY, ("el_init() %sed\n",
el != NULL ? "succeed" : "fail"));
@@ -174,7 +164,9 @@ histedit(void)
if (hist)
el_set(el, EL_HIST, history, hist);
- set_prompt_lit(lookupvar("PSlit"), 0);
+ set_prompt_lit(lookupvar("PSlit"), 0, NULL);
+
+ el_set(el, EL_GETENV, el_getenv);
el_set(el, EL_SIGNAL, 1);
el_set(el, EL_SAFEREAD, 1);
el_set(el, EL_ALIAS_TEXT, alias_text, NULL);
@@ -221,7 +213,7 @@ histedit(void)
}
void
-set_prompt_lit(char *lit_ch, int flags __unused)
+set_prompt_lit(const char *lit_ch, int flags, struct var *vp __unused)
{
wchar_t wc;
@@ -236,7 +228,7 @@ set_prompt_lit(char *lit_ch, int flags _
mbtowc(&wc, NULL, 1); /* state init */
INTOFF;
- if (mbtowc(&wc, lit_ch, strlen(lit_ch)) <= 0)
+ if ((flags & VUNSET) || mbtowc(&wc, lit_ch, strlen(lit_ch)) <= 0)
el_set(el, EL_PROMPT, getprompt);
else
el_set(el, EL_PROMPT_ESC, getprompt, (int)wc);
@@ -244,7 +236,7 @@ set_prompt_lit(char *lit_ch, int flags _
}
void
-set_editrc(char *fname, int flags)
+set_editrc(const char *fname, int flags, struct var *vp __unused)
{
INTOFF;
if (iflag && editing && el && !(flags & VUNSET))
@@ -253,7 +245,7 @@ set_editrc(char *fname, int flags)
}
void
-sethistsize(char *hs, int flags)
+sethistsize(const char *hs, int flags, struct var *vp __unused)
{
int histsize;
HistEvent he;
@@ -278,7 +270,7 @@ sethistsize(char *hs, int flags)
}
void
-sethistfile(char *hs, int flags)
+sethistfile(const char *hs, int flags, struct var *vp __unused)
{
const char *file;
HistEvent he;
@@ -330,14 +322,14 @@ sethistfile(char *hs, int flags)
HistFileOpen = "";
sethistappend((histappflags() & VUNSET) ? NULL : histappval(),
- ~VUNSET & 0xFFFF);
+ ~VUNSET & 0xFFFF, NULL);
INTON;
}
}
void
-sethistappend(char *s, int flags __diagused)
+sethistappend(const char *s, int flags, struct var *vp __unused)
{
CTRACE(DBG_HISTORY, ("Set HISTAPPEND=%s [%x] %s ",
(s == NULL ? "''" : s), flags, "!hist" + (hist != NULL)));
@@ -558,7 +550,7 @@ save_sh_history(void)
}
void
-setterm(char *term, int flags __unused)
+setterm(const char *term, int flags __unused, struct var *vp __unused)
{
INTOFF;
if (el != NULL && term != NULL && *term != '\0')
diff -r 8cdcca3e3510 -r d12208786ee4 bin/sh/myhistedit.h
--- a/bin/sh/myhistedit.h Mon Nov 03 09:37:25 2025 +0700
+++ b/bin/sh/myhistedit.h Mon Nov 03 10:05:27 2025 +0700
@@ -39,15 +39,17 @@ extern int displayhist;
#include <filecomplete.h>
+struct var;
+
void histedit(void);
-void sethistsize(char *, int);
-void sethistfile(char *, int);
-void sethistappend(char *, int);
+void sethistsize(const char *, int, struct var *);
+void sethistfile(const char *, int, struct var *);
+void sethistappend(const char *, int, struct var *);
void save_sh_history(void);
-void setterm(char *, int);
+void setterm(const char *, int, struct var *);
int inputrc(int, char **);
-void set_editrc(char *, int);
-void set_prompt_lit(char *, int);
+void set_editrc(const char *, int, struct var *);
+void set_prompt_lit(const char *, int, struct var *);
#include <stdio.h>
extern FILE *HistFP;
diff -r 8cdcca3e3510 -r d12208786ee4 bin/sh/options.c
--- a/bin/sh/options.c Mon Nov 03 09:37:25 2025 +0700
+++ b/bin/sh/options.c Mon Nov 03 10:05:27 2025 +0700
@@ -530,7 +530,7 @@ setcmd(int argc, char **argv)
void
-getoptsreset(char *value, int flags __unused)
+getoptsreset(const char *value, int flags __unused, struct var *vp __unused)
{
/*
* This is just to detect the case where OPTIND=1
@@ -573,7 +573,8 @@ getoptscmd(int argc, char **argv)
}
STATIC int
-getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, char **optpptr)
+getopts(char *optstr, char *optvar, char **optfirst,
+ char ***optnext, char **optpptr)
{
char *p, *q;
char c = '?';
@@ -588,7 +589,7 @@ getopts(char *optstr, char *optvar, char
return 1;
p = **optnext;
if (p == NULL || *p != '-' || *++p == '\0') {
-atend:
+ atend:;
ind = *optnext - optfirst + 1;
*optnext = NULL;
p = NULL;
@@ -642,11 +643,11 @@ atend:
ind = *optnext - optfirst + 1;
goto out;
-bad:
+ bad:;
ind = 1;
*optnext = NULL;
p = NULL;
-out:
+ out:;
*optpptr = p;
fmtstr(s, sizeof(s), "%d", ind);
err |= setvarsafe("OPTIND", s, VNOFUNC);
diff -r 8cdcca3e3510 -r d12208786ee4 bin/sh/options.h
--- a/bin/sh/options.h Mon Nov 03 09:37:25 2025 +0700
+++ b/bin/sh/options.h Mon Nov 03 10:05:27 2025 +0700
@@ -64,9 +64,11 @@ extern char **argptr; /* argument list
extern char *optionarg; /* set by nextopt */
extern char *optptr; /* used by nextopt */
+struct var;
+
void procargs(int, char **);
void optschanged(void);
void setparam(char **);
void freeparam(volatile struct shparam *);
int nextopt(const char *);
-void getoptsreset(char *, int);
+void getoptsreset(const char *, int, struct var *);
diff -r 8cdcca3e3510 -r d12208786ee4 bin/sh/parser.c
--- a/bin/sh/parser.c Mon Nov 03 09:37:25 2025 +0700
+++ b/bin/sh/parser.c Mon Nov 03 10:05:27 2025 +0700
@@ -2244,7 +2244,7 @@ parsesub: {
if (c == '#') {
if ((c = pgetc_linecont()) == CLOSEBRACE)
c = '#';
- else if (is_name(c) || isdigit(c))
+ else if (is_name(c) || is_digit(c))
subtype = VSLENGTH;
else if (is_special(c)) {
/*
@@ -2703,13 +2703,13 @@ getprompt(void *unused)
* behaviour.
*/
static const char *
-expandonstack(char *ps, int cmdsub, int lineno)
+expandonstack(const char *ps, int cmdsub, int lineno)
{
union node n;
struct jmploc jmploc;
struct jmploc *const savehandler = handler;
struct parsefile *const savetopfile = getcurrentfile();
- char * const save_ps = ps;
+ const char * const save_ps = ps;
const int save_x = xflag;
const int save_e_s = errors_suppressed;
struct parse_state new_state = init_parse_state;
@@ -2767,7 +2767,7 @@ expandonstack(char *ps, int cmdsub, int
}
const char *
-expandstr(char *ps, int lineno)
+expandstr(const char *ps, int lineno)
{
const char *result = NULL;
struct stackmark smark;
@@ -2842,7 +2842,7 @@ expandstr(char *ps, int lineno)
*/
const char *
-expandvar(char *var, int flags)
+expandvar(const char *var, int flags)
{
const char *result = NULL;
struct stackmark smark;
@@ -2918,7 +2918,7 @@ expandvar(char *var, int flags)
* Simply return the result, even if it is on the stack
*/
const char *
-expandenv(char *arg)
+expandenv(const char *arg)
{
return expandonstack(arg, 0, 0);
}
diff -r 8cdcca3e3510 -r d12208786ee4 bin/sh/parser.h
--- a/bin/sh/parser.h Mon Nov 03 09:37:25 2025 +0700
+++ b/bin/sh/parser.h Mon Nov 03 10:05:27 2025 +0700
@@ -78,9 +78,9 @@ void fixredir(union node *, const char *
int goodname(const char *);
int isassignment(const char *);
const char *getprompt(void *);
-const char *expandstr(char *, int);
-const char *expandvar(char *, int);
-const char *expandenv(char *);
+const char *expandstr(const char *, int);
+const char *expandvar(const char *, int);
+const char *expandenv(const char *);
struct HereDoc;
union node;
diff -r 8cdcca3e3510 -r d12208786ee4 bin/sh/var.c
--- a/bin/sh/var.c Mon Nov 03 09:37:25 2025 +0700
+++ b/bin/sh/var.c Mon Nov 03 10:05:27 2025 +0700
@@ -51,6 +51,9 @@ static char sccsid[] = "@(#)var.c 8.3 (B
#include <pwd.h>
#include <fcntl.h>
#include <inttypes.h>
+#ifndef SMALL
+#include <locale.h>
+#endif
/*
* Shell variables.
@@ -101,6 +104,9 @@ char *get_hostname(struct var *);
char *get_seconds(struct var *);
char *get_euser(struct var *);
char *get_random(struct var *);
+
+STATIC void set_locale_var(const char *, int, struct var *);
+void init_locale_vars(void);
#endif
struct localvar *localvars;
@@ -131,6 +137,15 @@ struct var euname;
struct var random_num;
intmax_t sh_start_time;
+
+struct var lc_all;
+struct var lc_collate;
+struct var lc_ctype;
+struct var lc_messages;
+struct var lc_monetary;
+struct var lc_numeric;
+struct var lc_time;
+struct var lc_lang;
#endif
struct var line_num;
@@ -173,6 +188,22 @@ const struct varinit varinit[] = {
{ .set_func= set_editrc } },
{ &ps_lit, VSTRFIXED|VTEXTFIXED|VUNSET, "PSlit=",
{ .set_func= set_prompt_lit } },
+ { &lc_all, VSTRFIXED|VTEXTFIXED|VUNSET|VFUNCPOST, "LC_ALL=",
+ { .set_func= set_locale_var } },
+ { &lc_collate, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_COLLATE=",
+ { .set_func= set_locale_var } },
+ { &lc_ctype, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE=",
+ { .set_func= set_locale_var } },
+ { &lc_messages, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_MESSAGES=",
+ { .set_func= set_locale_var } },
+ { &lc_monetary, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_MONETARY=",
+ { .set_func= set_locale_var } },
+ { &lc_numeric, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_NUMERIC=",
+ { .set_func= set_locale_var } },
+ { &lc_time, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_TIME=",
+ { .set_func= set_locale_var } },
+ { &lc_lang, VSTRFIXED|VTEXTFIXED|VUNSET|VFUNCPOST, "LANG=",
+ { .set_func= set_locale_var } },
#endif
{ &voptind, VSTRFIXED|VTEXTFIXED|VNOFUNC, "OPTIND=1",
{ .set_func= getoptsreset } },
@@ -214,6 +245,7 @@ INCLUDE "var.h"
INCLUDE "version.h"
MKINIT char **environ;
MKINIT void setvareqsafe(char *, int);
+MKINIT void init_locale_vars(void);
INIT {
char **envp;
char buf[64];
@@ -257,6 +289,8 @@ INIT {
#ifndef SMALL
snprintf(buf, sizeof(buf), "%jd", sh_start_time);
setvar("START_TIME", buf, VTEXTFIXED);
+
+ init_locale_vars();
#endif
setvar("NETBSD_SHELL", NETBSD_SHELL
@@ -505,8 +539,9 @@ setvareq(char *s, int flags)
INTOFF;
- if (vp->func && !(vp->flags & VFUNCREF) && !(flags & VNOFUNC))
- (*vp->func)(s + vp->name_len + 1, flags);
+ if (vp->func && !(vp->flags & (VFUNCREF|VFUNCPOST)) &&
+ !(flags & VNOFUNC))
+ (*vp->func)(s + vp->name_len + 1, flags, vp);
if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
ckfree(vp->text);
@@ -528,6 +563,11 @@ setvareq(char *s, int flags)
vp->flags |= flags & ~(VNOFUNC | VDOEXPORT);
vp->text = s;
+ if (vp->func &&
+ (vp->flags & (VFUNCREF|VFUNCPOST)) == VFUNCPOST &&
+ !(flags & VNOFUNC))
+ (*vp->func)(s + vp->name_len + 1, flags, vp);
+
/*
* We could roll this to a function, to handle it as
* a regular variable function callback, but why bother?
@@ -1208,7 +1248,7 @@ poplocalvars(void)
} else {
if (lvp->func && (lvp->flags & (VNOFUNC|VFUNCREF)) == 0)
(*lvp->func)(lvp->text + vp->name_len + 1,
- lvp->flags);
+ lvp->flags, lvp->vp);
if ((vp->flags & VTEXTFIXED) == 0)
ckfree(vp->text);
vp->flags = lvp->flags;
@@ -1735,4 +1775,201 @@ specialvarcmd(int argc, char **argv)
return res;
}
+struct lc_vars {
+ const char *name;
+ int category;
+ struct var *vp;
+};
+
+const struct lc_vars lc_vars[] = {
+ { .name= "LC_ALL", .category= LC_ALL, .vp= &lc_all },
+
+ { .name= "LC_COLLATE", .category= LC_COLLATE, .vp= &lc_collate },
+ { .name= "LC_CTYPE", .category= LC_CTYPE, .vp= &lc_ctype },
+ { .name= "LC_MESSAGES", .category= LC_MESSAGES, .vp= &lc_messages },
+ { .name= "LC_MONETARY", .category= LC_MONETARY, .vp= &lc_monetary },
+ { .name= "LC_NUMERIC", .category= LC_NUMERIC, .vp= &lc_numeric },
+ { .name= "LC_TIME", .category= LC_TIME, .vp= &lc_time },
+
+ { .name= "LANG", .category= LC_ALL, .vp= &lc_lang },
+
+ { .name= NULL, .category= 0, .vp= NULL }
+};
+
+STATIC void
+set_locale_var(const char *val, int flags, struct var *vp)
+{
+ const struct lc_vars *lv;
+
+ if (flags & VUNSAFE) /* parsing the environment */
+ return; /* do nothing until later */
+
+ for (lv = lc_vars; lv->name != NULL; lv++) {
+ /* Find the var being altered */
+ if (lv->vp != vp)
+ continue;
+
+ if (flags & VUNSET) {
+ /*
+ * If we are unsetting one of these, then
+ * we need to set the locale for the category
+ * back to a default value
+ */
+
+ if (lv->category == LC_ALL) {
+ /*
+ * nb: the LC_ALLL vars call this func
+ * after their value has changed, ie:
+ * since this is an unset, the var already
+ * is unset. This simplifies things.
+ */
+
+ /*
+ * LANG is just the default to use when
+ * nothing more specific is set, if it is
+ * unset, there is nothing to do.
+ */
+ if (lv->vp == &lc_lang)
+ return;
+
+ /*
+ * Otherwise LC_ALL must be being unset,
+ * in that case, simply set everything to
+ * the values obtained from the other
+ * LC_xxx vars (with LANG as default)
+ */
+ init_locale_vars();
+ return;
+ }
+
+ /*
+ * For the category vars, this func is called before
+ * the sh var is updated
+ */
+
+ /* If a category var previously was unset - easy case */
+ if (lv->vp->flags & VUNSET)
+ return;
+
+ /*
+ * One of the specific categories is being
+ * unset, in that case we fall back upon
+ * the value of LC_ALL if that is set, or
+ * otherwise the value of LANG, if that is set,
+ * or just set the "C" locale for this category
+ */
+ vp = NULL;
+ if (!(lc_all.flags & VUNSET))
+ vp = &lc_all;
+ else if (!(lc_lang.flags & VUNSET))
+ vp = &lc_lang;
+
+ if (vp != NULL)
+ val = vp->text + vp->name_len + 1;
+ else
+ val = "C";
+
+ set_locale_var(val, 0, lv->vp);
+ } else {
+ char *old;
+
+ if (lv->category == LC_ALL) {
+ /*
+ * post call, so this var's value is
+ * already established, just go use
+ * it (and the others) to set all the
+ * categories
+ */
+ init_locale_vars();
+ return;
+ }
+
+ /*
+ * Potentially changing the value of a specific
+ * category.
+ *
+ * If the value isn't actually changing, do nothing
+ */
+ old = setlocale(lv->category, NULL);
+ if (old != NULL && strcmp(old, val) != 0) {
+ /*
+ * Otherwise update the shell's locale
+ * to match the new setting
+ */
+ if (setlocale(lv->category, val) == NULL) {
+ /*XXX NetBSD! */ if (lv->category != LC_COLLATE)
+ outfmt(out2,
+ "Setting %s to '%s' failed\n",
+ lv->name, val);
+ } else if (lv->category == LC_CTYPE) {
+ /*
+ * and if that worded, and we're
+ * changing the char encodings,
+ * go re-init libedit (if in use)
+ */
+ if (iflag && (Eflag || Vflag)) {
+ int oE = Eflag, oV = Vflag;
+
+ /*
+ * re-init editing if
+ * LC_CTYPE changes
+ *
+ * easy way: disable, then
+ * enable again
+ */
+ Eflag = Vflag = 0;
+ histedit();
+ Eflag = oE, Vflag=oV;
+ histedit();
+ }
+ }
+ }
+ }
+ return;
+ }
+}
+
+void
+init_locale_vars(void)
+{
+ const struct lc_vars *lv;
+ const char *defval;
+
+ if (!(lc_all.flags & VUNSET)) {
+ /*
+ * If LC_ALL is set, just use it, ignore others
+ * Simply set all categories to the value of LC_ALL
+ *
+ * nb: strlen("LC_ALL") + 1 == 7
+ */
+ for (lv = lc_vars; lv->name != NULL; lv++) {
+ if (lv->category == LC_ALL)
+ continue;
+ set_locale_var(lc_all.text + 7, 0, lv->vp);
+ }
+ return;
+ }
+
+ /*
+ * Otherwise, for each specific category, set the value
+ * of its variable for that category, if that variable is
+ * set, otherwise we need a default for that category.
+ * If LANG is set, that gives the default, otherwise it is "C"
+ */
+ if (lc_lang.flags & VUNSET)
+ defval = "C";
+ else
+ defval = lc_lang.text + 5; /* strlen("LANG") + 1 == 5 */
+
+ for (lv = lc_vars; lv->name != NULL; lv++) {
+ /* ignore LC_ALL and LANG */
+ if (lv->category == LC_ALL)
+ continue;
+ /* otherwise set the category to the appropriate value */
+ set_locale_var( (lv->vp->flags & VUNSET ? defval :
+ lv->vp->text + lv->vp->name_len + 1),
+ 0, lv->vp);
+ }
+}
+
#endif /* SMALL */
diff -r 8cdcca3e3510 -r d12208786ee4 bin/sh/var.h
--- a/bin/sh/var.h Mon Nov 03 09:37:25 2025 +0700
+++ b/bin/sh/var.h Mon Nov 03 10:05:27 2025 +0700
@@ -53,6 +53,7 @@
#define VNOFUNC 0x0100 /* don't call the callback function */
#define VFUNCREF 0x0200 /* the function is called on ref, not set */
+#define VFUNCPOST 0x0400 /* call callback func after setting var */
#define VSPECIAL 0x1000 /* magic properties not lost when set */
#define VDOEXPORT 0x2000 /* obey VEXPORT even if VNOEXPORT */
@@ -62,7 +63,8 @@
struct var;
union var_func_union { /* function to be called when: */
- void (*set_func)(char *, int); /* variable gets set/unset */
+ /* variable set/unset */
+ void (*set_func)(const char *, int, struct var *);
char*(*ref_func)(struct var *); /* variable is referenced */
};
@@ -106,6 +108,14 @@ extern struct var ps_lit;
extern struct var euname;
extern struct var random_num;
extern intmax_t sh_start_time;
+extern struct var lc_all;
+extern struct var lc_collate;
+extern struct var lc_ctype;
+extern struct var lc_messages;
+extern struct var lc_monetary;
+extern struct var lc_numeric;
+extern struct var lc_time;
+extern struct var lc_lang;
#endif
extern int line_number;
Home |
Main Index |
Thread Index |
Old Index