From d067ebcacd55472f720b2765ec686a69b25c9a90 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 7 Dec 2014 16:24:19 +0000 Subject: 33876: etc.: Separate errors and keyboards interrupts Combination of 12 commits from interrupt_abort branch. Basic strategy is to introduce bits to errflag and to set and reset them separately. Remove interrupt status on return to main keymap. Turn off ERRFLAG_INT for always block. Restore bit thereafter: we probably need a new variable in order to allow user interrupts to be reset in the always block. Add TRY_BLOCK_INTERRUPT This works the same as TRY_BLOCK_ERROR, but for a SIGINT, too. Ensure propagation of SIGINT from exited job. If received by foreground job, shell uses ERRFLAG_INT, not ERRFLAG_ERROR, to set the new state. Reset errflag before precmd() Add always block in _main_completion to fix ZLS_COLORS Ensures we get the right state of $ZLS_COLORS at the end of _main_complete even if there's an interrupt. However, the "right state" is a bit messy as it depends on styles. --- Src/params.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'Src/params.c') diff --git a/Src/params.c b/Src/params.c index 61edc5d08..79088d162 100644 --- a/Src/params.c +++ b/Src/params.c @@ -331,6 +331,7 @@ IPDEF5("SHLVL", &shlvl, varinteger_gsu), #define IPDEF6(A,B,F) {{NULL,A,PM_INTEGER|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(F),10,0,NULL,NULL,NULL,0} IPDEF6("OPTIND", &zoptind, varinteger_gsu), IPDEF6("TRY_BLOCK_ERROR", &try_errflag, varinteger_gsu), +IPDEF6("TRY_BLOCK_INTERRUPT", &try_interrupt, varinteger_gsu), #define IPDEF7(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0} #define IPDEF7U(A,B) {{NULL,A,PM_SCALAR|PM_SPECIAL|PM_UNSET},BR((void *)B),GSU(varscalar_gsu),0,0,NULL,NULL,NULL,0} @@ -2654,7 +2655,7 @@ assignsparam(char *s, char *val, int flags) if (!isident(s)) { zerr("not an identifier: %s", s); zsfree(val); - errflag = 1; + errflag |= ERRFLAG_ERROR; return NULL; } queue_signals(); @@ -2783,7 +2784,7 @@ assignaparam(char *s, char **val, int flags) if (!isident(s)) { zerr("not an identifier: %s", s); freearray(val); - errflag = 1; + errflag |= ERRFLAG_ERROR; return NULL; } queue_signals(); @@ -2799,7 +2800,7 @@ assignaparam(char *s, char **val, int flags) zerr("%s: attempt to set slice of associative array", v->pm->node.nam); freearray(val); - errflag = 1; + errflag |= ERRFLAG_ERROR; return NULL; } v = NULL; @@ -2870,13 +2871,13 @@ sethparam(char *s, char **val) if (!isident(s)) { zerr("not an identifier: %s", s); freearray(val); - errflag = 1; + errflag |= ERRFLAG_ERROR; return NULL; } if (strchr(s, '[')) { freearray(val); zerr("nested associative arrays not yet supported"); - errflag = 1; + errflag |= ERRFLAG_ERROR; return NULL; } if (unset(EXECOPT)) @@ -2916,7 +2917,7 @@ setnparam(char *s, mnumber val) if (!isident(s)) { zerr("not an identifier: %s", s); - errflag = 1; + errflag |= ERRFLAG_ERROR; return NULL; } if (unset(EXECOPT)) -- cgit v1.2.3 From f3cb9a77543125d4b478f43e068d2a8272d69a0e Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 18 Dec 2014 19:36:03 +0000 Subject: 34006: unmetafy anything put into the environment --- ChangeLog | 3 +++ Src/params.c | 12 +++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 4529bfc48..ff802b874 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2014-12-18 Peter Stephenson + * 34006: Src/params.c: unmetafy anything put into the + environment. + * Lokesh Mandvekar: 33999: Completion/Linux/Command/_docker: remove completion as the version supplied with docker is authoritative. diff --git a/Src/params.c b/Src/params.c index 79088d162..b87598a02 100644 --- a/Src/params.c +++ b/Src/params.c @@ -4357,7 +4357,18 @@ arrfixenv(char *s, char **t) int zputenv(char *str) { + char *ptr; DPUTS(!str, "Attempt to put null string into environment."); + /* + * The environment uses NULL-terminated strings, so just + * unmetafy and ignore the length. + */ + for (ptr = str; *ptr && *ptr != Meta; ptr++) + ; + if (*ptr == Meta) { + str = dupstring(str); + unmetafy(str, NULL); + } #ifdef USE_SET_UNSET_ENV /* * If we are using unsetenv() to remove values from the @@ -4366,7 +4377,6 @@ zputenv(char *str) * Unfortunately this is a slightly different interface * from what zputenv() assumes. */ - char *ptr; int ret; for (ptr = str; *ptr && *ptr != '='; ptr++) -- cgit v1.2.3 From ecef922df15c9c315be48871a59b311bd2477f3c Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 18 Dec 2014 19:55:53 +0000 Subject: 34008: metafy the environment on arrival in the shell --- ChangeLog | 3 +++ Src/params.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index ff802b874..d4c27f50d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2014-12-18 Peter Stephenson + * 34008: Src/params.c: also metafy anything arriving from the + environment. + * 34006: Src/params.c: unmetafy anything put into the environment. diff --git a/Src/params.c b/Src/params.c index b87598a02..1c51afd7a 100644 --- a/Src/params.c +++ b/Src/params.c @@ -641,7 +641,7 @@ split_env_string(char *env, char **name, char **value) if (!env || !name || !value) return 0; - tenv = strcpy(zhalloc(strlen(env) + 1), env); + tenv = metafy(env, strlen(env), META_HEAPDUP); for (str = tenv; *str && *str != '='; str++) ; if (str != tenv && *str == '=') { -- cgit v1.2.3 From 89012cf94caa6e782b928d0eacfbf840244ffb6b Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 19 Dec 2014 22:15:24 +0000 Subject: 34015: disallow strange environment variable names. These are ones with the top bit set in any character. Don't import them, and don't export them. --- ChangeLog | 4 ++++ Src/params.c | 39 +++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 16 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 81e800d61..c11e2dee3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2014-12-19 Peter Stephenson + * 34015: Src/params.c: back off yesterday's mess and + disallow environment variables with characters with the + top bit set. + * 34005: Src/Zle/zle_refresh.c: Zero uninitialised memory when realloc'ing region_highlights. diff --git a/Src/params.c b/Src/params.c index 1c51afd7a..b8e0c429b 100644 --- a/Src/params.c +++ b/Src/params.c @@ -641,9 +641,17 @@ split_env_string(char *env, char **name, char **value) if (!env || !name || !value) return 0; - tenv = metafy(env, strlen(env), META_HEAPDUP); - for (str = tenv; *str && *str != '='; str++) - ; + tenv = strcpy(zhalloc(strlen(env) + 1), env); + for (str = tenv; *str && *str != '='; str++) { + if (STOUC(*str) >= 128) { + /* + * We'll ignore environment variables with names not + * from the portable character set since we don't + * know of a good reason to accept them. + */ + return 0; + } + } if (str != tenv && *str == '=') { *str = '\0'; *name = tenv; @@ -4357,18 +4365,7 @@ arrfixenv(char *s, char **t) int zputenv(char *str) { - char *ptr; DPUTS(!str, "Attempt to put null string into environment."); - /* - * The environment uses NULL-terminated strings, so just - * unmetafy and ignore the length. - */ - for (ptr = str; *ptr && *ptr != Meta; ptr++) - ; - if (*ptr == Meta) { - str = dupstring(str); - unmetafy(str, NULL); - } #ifdef USE_SET_UNSET_ENV /* * If we are using unsetenv() to remove values from the @@ -4377,11 +4374,21 @@ zputenv(char *str) * Unfortunately this is a slightly different interface * from what zputenv() assumes. */ + char *ptr; int ret; - for (ptr = str; *ptr && *ptr != '='; ptr++) + for (ptr = str; *ptr && STOUC(*ptr) < 128 && *ptr != '='; ptr++) ; - if (*ptr) { + if (STOUC(*ptr) >= 128) { + /* + * Environment variables not in the portable character + * set are non-standard and we don't really know of + * a use for them. + * + * We'll disable until someone complains. + */ + return 1; + } else if (*ptr) { *ptr = '\0'; ret = setenv(str, ptr+1, 1); *ptr = '='; -- cgit v1.2.3 From c6c9f5daf2e196e6ab7346dfbf5f5166a1d87f0c Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 18 Jan 2015 22:38:57 +0000 Subject: 34322: bug with interface to parsestr() etc. Was showing up in places like ${(e)...} where command substitution could reallocate the token string, but actually there was never any guarantee that the lexer wouldn't do that, so this was always a bit iffy. --- ChangeLog | 6 ++++++ Src/Zle/compctl.c | 4 ++-- Src/Zle/compresult.c | 3 ++- Src/exec.c | 9 +++++---- Src/init.c | 11 +++++++---- Src/lex.c | 30 +++++++++++++++++++++--------- Src/params.c | 3 ++- Src/prompt.c | 2 +- Src/subst.c | 8 +++++--- Src/utils.c | 2 +- Test/D04parameter.ztst | 7 +++++++ 11 files changed, 59 insertions(+), 26 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 30068eb8b..5d52f6693 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2015-01-18 Peter Stephenson + * 34322: Src/Zle/compctl.c, Src/Zle/compresult.c, Src/exec.c, + Src/init.c, Src/lex.c, Src/params.c, Src/prompt.c, Src/subst.c, + Src/utils.c, Test/D04parameter.ztst: update interface to + parsestr()/parsestrnoerr() to ensure correct token string + is passed back. + * 34320: Src/hist.c, Src/lex.c: alias expansion in history of command substitution. diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 43dd4e2a9..189582d22 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -3853,7 +3853,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) yaptr = get_user_var(uv); if ((tt = cc->explain)) { tt = dupstring(tt); - if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) { + if ((cc->mask & CC_EXPANDEXPL) && !parsestr(&tt)) { singsub(&tt); untokenize(tt); } @@ -3873,7 +3873,7 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) } } else if ((tt = cc->explain)) { tt = dupstring(tt); - if ((cc->mask & CC_EXPANDEXPL) && !parsestr(tt)) { + if ((cc->mask & CC_EXPANDEXPL) && !parsestr(&tt)) { singsub(&tt); untokenize(tt); } diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index dbef7f841..9f383f4b8 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -1090,7 +1090,8 @@ do_single(Cmatch m) } if (tryit) { noerrs = 1; - parsestr(p); + p = dupstring(p); + parsestr(&p); singsub(&p); errflag &= ~ERRFLAG_ERROR; noerrs = ne; diff --git a/Src/exec.c b/Src/exec.c index 7b6495113..f42fb2b9b 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3772,19 +3772,20 @@ gethere(char **strp, int typ) *bptr++ = '\n'; } *t = '\0'; + s = buf; + buf = dupstring(buf); + zfree(s, bsiz); if (!qt) { int ef = errflag; - parsestr(buf); + parsestr(&buf); if (!errflag) { /* Retain any user interrupt error */ errflag = ef | (errflag & ERRFLAG_INT); } } - s = dupstring(buf); - zfree(buf, bsiz); - return s; + return buf; } /* open here string fd */ diff --git a/Src/init.c b/Src/init.c index e7d86feac..3e41fb9dd 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1197,10 +1197,13 @@ run_init_scripts(void) if (islogin) sourcehome(".profile"); noerrs = 2; - if (s && !parsestr(s)) { - singsub(&s); - noerrs = 0; - source(s); + if (s) { + s = dupstring(s); + if (!parsestr(&s)) { + singsub(&s); + noerrs = 0; + source(s); + } } noerrs = 0; } else diff --git a/Src/lex.c b/Src/lex.c index e4dfdfaca..433c27fbb 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -1503,17 +1503,27 @@ dquote_parse(char endchar, int sub) return err; } -/* Tokenize a string given in s. Parsing is done as in double * - * quotes. This is usually called before singsub(). */ +/* + * Tokenize a string given in s. Parsing is done as in double + * quotes. This is usually called before singsub(). + * + * parsestr() is noisier, reporting an error if the parse failed. + * + * On entry, *s must point to a string allocated from the stack of + * exactly the right length, i.e. strlen(*s) + 1, as the string + * is used as the lexical token string whose memory management + * demands this. Usually the input string will therefore be + * the result of an immediately preceding dupstring(). + */ /**/ mod_export int -parsestr(char *s) +parsestr(char **s) { int err; if ((err = parsestrnoerr(s))) { - untokenize(s); + untokenize(*s); if (err > 32 && err < 127) zerr("parse error near `%c'", err); else @@ -1524,18 +1534,20 @@ parsestr(char *s) /**/ mod_export int -parsestrnoerr(char *s) +parsestrnoerr(char **s) { - int l = strlen(s), err; + int l = strlen(*s), err; zcontext_save(); - untokenize(s); - inpush(dupstring(s), 0, NULL); + untokenize(*s); + inpush(dupstring(*s), 0, NULL); strinbeg(0); lexbuf.len = 0; - lexbuf.ptr = tokstr = s; + lexbuf.ptr = tokstr = *s; lexbuf.siz = l + 1; err = dquote_parse('\0', 1); + if (tokstr) + *s = tokstr; *lexbuf.ptr = '\0'; strinend(); inpop(); diff --git a/Src/params.c b/Src/params.c index b8e0c429b..64c78bd63 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1260,7 +1260,8 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, if (ishash && (keymatch || !rev)) remnulargs(s); if (needtok) { - if (parsestr(s)) + s = dupstring(s); + if (parsestr(&s)) return 0; singsub(&s); } else if (rev) diff --git a/Src/prompt.c b/Src/prompt.c index 3552575f3..ffc1d0df2 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -183,7 +183,7 @@ promptexpand(char *s, int ns, char *rs, char *Rs, unsigned int *txtchangep) int oldval = lastval; s = dupstring(s); - if (!parsestr(s)) + if (!parsestr(&s)) singsub(&s); /* * We don't need the special Nularg hack here and we're diff --git a/Src/subst.c b/Src/subst.c index 5f993d6fd..a2bb6483a 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1306,7 +1306,7 @@ get_intarg(char **s, int *delmatchp) p = dupstring(*s + arglen); *s = t + arglen; *t = sav; - if (parsestr(p)) + if (parsestr(&p)) return -1; singsub(&p); if (errflag) @@ -1329,7 +1329,8 @@ subst_parse_str(char **sp, int single, int err) *sp = s = dupstring(*sp); - if (!(err ? parsestr(s) : parsestrnoerr(s))) { + if (!(err ? parsestr(&s) : parsestrnoerr(&s))) { + *sp = s; if (!single) { int qt = 0; @@ -1439,7 +1440,8 @@ check_colon_subscript(char *str, char **endp) } sav = **endp; **endp = '\0'; - if (parsestr(str = dupstring(str))) + str = dupstring(str); + if (parsestr(&str)) return NULL; singsub(&str); remnulargs(str); diff --git a/Src/utils.c b/Src/utils.c index f8d239458..4561b5e9a 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1515,7 +1515,7 @@ checkmailpath(char **s) setunderscore(*s); u = dupstring(u); - if (! parsestr(u)) { + if (!parsestr(&u)) { singsub(&u); zputs(u, shout); fputc('\n', shout); diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 94d15f205..cf639fa8c 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -1663,3 +1663,10 @@ 0:SHLVL appears sensible when about to exit shell >2 >2 + +# The following tests the return behaviour of parsestr/parsestrnoerr + alias param-test-alias='print $'\''\x45xpanded in substitution'\' + param='$(param-test-alias)' + print ${(e)param} +0:Alias expansion in command substitution in parameter evaluation +>Expanded in substitution -- cgit v1.2.3 From bc8491c3dc939411546d0b5ee5fe25551bce424f Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 29 Jan 2015 21:05:17 +0000 Subject: 34430: parameter fixes for gdbm tied hash. Probably fix the issue with correct parameter hiding or not hiding. A little extra safety checking. Possibly fixed a memory leak with untying. --- ChangeLog | 7 ++++++- Src/Modules/db_gdbm.c | 17 +++++++++++------ Src/params.c | 27 +++++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 9 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 1fe592b07..ee6860c0c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,12 @@ +2015-01-29 Peter Stephenson + + * 34430: Src/Modules/db_gdbm.c, Src/params.c: various bug fixes + and safety additions for gdbm tied hashes. + 2015-01-28 Barton E. Schaefer * unposted: Doc/Zsh/builtins.yo: fix other typeset doc errors - + * 34421: Doc/Zsh/builtins.yo: clean up typeset documentation 2015-01-28 Jun-ichi Takimoto diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c index d75af980b..a6027deaf 100644 --- a/Src/Modules/db_gdbm.c +++ b/Src/Modules/db_gdbm.c @@ -105,7 +105,6 @@ static int bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func)) { Param pm; - GDBM_FILE dbf; char *pmname; int ret = 0; @@ -116,12 +115,18 @@ bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func)) ret = 1; continue; } + if (pm->gsu.h != &gdbm_hash_gsu) { + zwarnnam(nam, "not a tied gdbm hash: %s", pmname); + ret = 1; + continue; + } - dbf = (GDBM_FILE)(pm->u.hash->tmpdata); - gdbm_close(dbf); - /* free(pm->u.hash->tmpdata); */ - pm->u.hash->tmpdata = NULL; - paramtab->removenode(paramtab, pm->node.nam); + queue_signals(); + if (unsetparam_pm(pm, 0, 1)) { + /* assume already reported */ + ret = 1; + } + unqueue_signals(); } return ret; diff --git a/Src/params.c b/Src/params.c index 64c78bd63..e8a90104c 100644 --- a/Src/params.c +++ b/Src/params.c @@ -927,7 +927,15 @@ shempty(void) { } -/* Create a simple special hash parameter. */ +/* + * Create a simple special hash parameter. + * + * This is for hashes added internally --- it's not possible to add + * special hashes from shell commands. It's currently used + * - by addparamdef() for special parameters in the zsh/parameter + * module + * - by ztie for special parameters tied to databases. + */ /**/ mod_export Param @@ -939,7 +947,22 @@ createspecialhash(char *name, GetNodeFunc get, ScanTabFunc scan, int flags) if (!(pm = createparam(name, PM_SPECIAL|PM_HASHED|flags))) return NULL; - pm->level = pm->old ? locallevel : 0; + /* + * If there's an old parameter, we'll put the new one at + * the current locallevel, so that the old parameter is + * exposed again after leaving the function. Otherwise, + * we'll leave it alone. Usually this means the parameter + * will stay in place until explicitly unloaded, however + * if the parameter was previously unset within a function + * we'll inherit the level of that function and follow the + * standard convention that the parameter remains local + * even if unset. + * + * These semantics are similar to those of a normal parameter set + * within a function without a local definition. + */ + if (pm->old) + pm->level = locallevel; pm->gsu.h = (flags & PM_READONLY) ? &stdhash_gsu : &nullsethash_gsu; pm->u.hash = ht = newhashtable(0, name, NULL); -- cgit v1.2.3 From bf258a1c07a9cf1119f83d1d15310b02b94f4d67 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Wed, 29 Apr 2015 15:54:49 +0100 Subject: 34992: POSIX fix for readonly variables. With POSIXBUILTINS, variables can be marked readonly if unset. Also, variables can't have the readonly flag removed. --- ChangeLog | 7 +++++++ Doc/Zsh/builtins.yo | 7 +++++++ Src/builtin.c | 37 +++++++++++++++++++++++++++++++++---- Src/params.c | 6 +++++- Test/B02typeset.ztst | 17 +++++++++++++++++ 5 files changed, 69 insertions(+), 5 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index a5987b764..2d2fb1b5a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2015-04-29 Peter Stephenson + + * 34992: Doc/Zsh/builtins.yo, Src/builtin.c, Src/params.c, + Test/B02typeset.ztst: With POSXIBUILTINS, parameters can be + marked readonly if unset and in any case can't subsequently be + marked not readonly. + 2015-04-28 Peter Stephenson * 34989: Src/exec.c: AUTOCD needs to pass -- to cd to avoid diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index dd5a80fe8..985d19e11 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -1933,6 +1933,13 @@ item(tt(-r))( The given var(name)s are marked readonly. Note that if var(name) is a special parameter, the readonly attribute can be turned on, but cannot then be turned off. + +If the tt(POSIX_BUILTINS) option is set, the readonly attribute is +more restrictive: unset variables can be marked readonly and cannot then +be set; furthermore, the readonly attribute cannot be removed from any +variable. Note that in zsh (unlike other shells) it is still possible +to create a local variable of the same name as this is considered a +different variable (though this variable, too, can be marked readonly). ) item(tt(-t))( Tags the named parameters. Tags have no special meaning to the shell. diff --git a/Src/builtin.c b/Src/builtin.c index de0101405..0a57489ea 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -1931,8 +1931,12 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), * locallevel as an unset one we use the pm struct anyway: that's * handled in createparam(). Here we just avoid using it for the * present tests if it's unset. + * + * POSIXBUILTINS horror: we need to retain the 'readonly' flag + * of an unset parameter. */ - usepm = pm && !(pm->node.flags & PM_UNSET); + usepm = pm && (!(pm->node.flags & PM_UNSET) || + (isset(POSIXBUILTINS) && (pm->node.flags & PM_READONLY))); /* * We need to compare types with an existing pm if special, @@ -2032,6 +2036,20 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), else if (newspecial != NS_NONE && strcmp(pname, "SECONDS") == 0) newspecial = NS_SECONDS; + if (isset(POSIXBUILTINS)) { + /* + * Stricter rules about retaining readonly attribute in this case. + */ + if ((on & PM_READONLY) && (!usepm || (pm->node.flags & PM_UNSET)) && + !value) + on |= PM_UNSET; + else if (usepm && (pm->node.flags & PM_READONLY) && + !(on & PM_READONLY)) { + zerr("read-only variable: %s", pm->node.nam); + return NULL; + } + } + /* * A parameter will be local if * 1. we are re-using an existing local parameter @@ -2078,9 +2096,15 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), } if (usepm == 2) /* do not change the PM_UNSET flag */ pm->node.flags = (pm->node.flags | (on & ~PM_READONLY)) & ~off; - else + else { + /* + * Keep unset if using readonly in POSIX mode. + */ + if (!(on & PM_READONLY) || !isset(POSIXBUILTINS)) + off |= PM_UNSET; pm->node.flags = (pm->node.flags | - (on & ~PM_READONLY)) & ~(off | PM_UNSET); + (on & ~PM_READONLY)) & ~off; + } if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) { if (typeset_setwidth(cname, pm, ops, on, 0)) return NULL; @@ -2256,7 +2280,12 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), * readonly flag */ pm = createparam(pname, on & ~PM_READONLY); - DPUTS(!pm, "BUG: parameter not created"); + if (!pm) { + if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z | + PM_INTEGER | PM_EFLOAT | PM_FFLOAT)) + zerrnam(cname, "can't change variable attribute: %s", pname); + return NULL; + } if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) { if (typeset_setwidth(cname, pm, ops, on, 0)) return NULL; diff --git a/Src/params.c b/Src/params.c index e8a90104c..d53b6ca7e 100644 --- a/Src/params.c +++ b/Src/params.c @@ -874,10 +874,14 @@ createparam(char *name, int flags) DPUTS(oldpm && oldpm->level > locallevel, "BUG: old local parameter not deleted"); if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) { + if (isset(POSIXBUILTINS) && (oldpm->node.flags & PM_READONLY)) { + zerr("read-only variable: %s", name); + return NULL; + } if (!(oldpm->node.flags & PM_UNSET) || (oldpm->node.flags & PM_SPECIAL)) { oldpm->node.flags &= ~PM_UNSET; if ((oldpm->node.flags & PM_SPECIAL) && oldpm->ename) { - Param altpm = + Param altpm = (Param) paramtab->getnode(paramtab, oldpm->ename); if (altpm) altpm->node.flags &= ~PM_UNSET; diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index 51ebc6535..f4fb8ecb9 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -468,3 +468,20 @@ 0:retying arrays to same array works >foo bar >goo car + + ( + setopt POSIXBUILTINS + readonly pbro + print ${+pbro} >&2 + (typeset pbro=3) + (pbro=4) + typeset -r pbro # idempotent (no error)... + print ${+pbro} >&2 # ...so still readonly... + typeset +r pbro # ...can't turn it off + ) +1:Readonly with POSIX_BUILTINS +?0 +?(eval):5: read-only variable: pbro +?(eval):6: read-only variable: pbro +?0 +?(eval):9: read-only variable: pbro -- cgit v1.2.3