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