diff options
author | Bart Schaefer <schaefer@zsh.org> | 2023-02-12 11:51:41 -0800 |
---|---|---|
committer | Bart Schaefer <schaefer@zsh.org> | 2023-02-12 11:51:41 -0800 |
commit | 3eed6f70cdfea63cfdc380a4df8382fff38af55d (patch) | |
tree | e044d367f65abaa8e63294c4c3cddbb2456397fe /Src/params.c | |
parent | e807ac1157015581c1466407cbe722179244be37 (diff) | |
download | zsh-3eed6f70cdfea63cfdc380a4df8382fff38af55d.tar.gz zsh-3eed6f70cdfea63cfdc380a4df8382fff38af55d.tar.xz zsh-3eed6f70cdfea63cfdc380a4df8382fff38af55d.zip |
51402: Some ksh/bash features, additional sanity checking
* Add "unset -n" * Allow and enforce "typeset -n -r" for read-only references * "can't change type via subscript reference" error * Better checking for self-referential declarations/assignments * Ksh-style "foo=bar; typeset -n foo" creates foo=bar reference * Support "typeset -n ref; for ref in ..." * Subscripted references use NO_EXEC for safety * References assigned in called scopes reset scope at end * Allow named references to $! $? $$ $- $0 $_
Diffstat (limited to 'Src/params.c')
-rw-r--r-- | Src/params.c | 92 |
1 files changed, 80 insertions, 12 deletions
diff --git a/Src/params.c b/Src/params.c index 98950d88f..4910d65fe 100644 --- a/Src/params.c +++ b/Src/params.c @@ -997,7 +997,7 @@ createparam(char *name, int flags) paramtab->getnode(paramtab, name)); if (oldpm && (oldpm->node.flags & PM_NAMEREF) && - !(flags & PM_NAMEREF)) { + !(flags & PM_NAMEREF) && (oldpm = upscope(oldpm, oldpm->base))) { Param lastpm; struct asgment stop; stop.flags = PM_NAMEREF | (flags & PM_LOCAL); @@ -1467,10 +1467,14 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, if (ishash && (keymatch || !rev)) remnulargs(s); if (needtok) { + char exe = opts[EXECOPT]; s = dupstring(s); if (parsestr(&s)) return 0; + if (flags & SCANPM_NOEXEC) + opts[EXECOPT] = 0; singsub(&s); + opts[EXECOPT] = exe; } else if (rev) remnulargs(s); /* This is probably always a no-op, but ... */ if (!rev) { @@ -2153,9 +2157,12 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) ((pm->node.flags & PM_UNSET) && !(pm->node.flags & PM_DECLARED))) return NULL; - if (ss) + if (ss) { + flags |= SCANPM_NOEXEC; *ss = sav; - s = dyncat(ss,*pptr); + s = dyncat(ss,*pptr); + } else + s = *pptr; } if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) { /* Overload v->isarr as the flag bits for hashed arrays. */ @@ -5782,7 +5789,8 @@ scanendscope(HashNode hn, UNUSED(int flags)) export_param(pm); } else unsetparam_pm(pm, 0, 0); - } + } else if ((pm->node.flags & PM_NAMEREF) && pm->base > pm->level) + pm->base = locallevel; } @@ -6109,10 +6117,10 @@ resolve_nameref(Param pm, const Asgment stop) if (pm && (pm->node.flags & (PM_UNSET|PM_TAGGED))) { /* Semaphore with createparam() */ pm->node.flags &= ~PM_UNSET; - /* See V10private.ztst end is in scope but private: - if (pm->node.flags & PM_SPECIAL) + if (pm->node.flags & PM_NEWREF) /* See setloopvar() */ return NULL; - */ + if (pm->u.str && *(pm->u.str) && (pm->node.flags & PM_TAGGED)) + pm->node.flags |= PM_SELFREF; /* See setscope() */ return (HashNode) pm; } else if (pm->u.str) { if ((pm->node.flags & PM_TAGGED) || @@ -6157,7 +6165,7 @@ resolve_nameref(Param pm, const Asgment stop) if (pm) pm->node.flags &= ~PM_TAGGED; } else if (stop && (stop->flags & PM_NAMEREF)) - hn = (HashNode)pm; + hn = (pm && (pm->node.flags & PM_NEWREF)) ? NULL : (HashNode)pm; unqueue_signals(); } @@ -6165,6 +6173,22 @@ resolve_nameref(Param pm, const Asgment stop) } /**/ +mod_export void +setloopvar(char *name, char *value) +{ + Param pm = (Param) gethashnode2(realparamtab, name); + + if (pm && (pm->node.flags & PM_NAMEREF)) { + pm->base = pm->width = 0; + pm->u.str = ztrdup(value); + pm->node.flags |= PM_NEWREF; + setscope(pm); + pm->node.flags &= ~PM_NEWREF; + } else + setsparam(name, value); +} + +/**/ static void setscope(Param pm) { @@ -6188,9 +6212,50 @@ setscope(Param pm) pm->width = t - pm->u.str; *t = '['; } - if (basepm) - pm->base = ((basepm->node.flags & PM_NAMEREF) ? - basepm->base : basepm->level); + if (basepm) { + if (basepm->node.flags & PM_NAMEREF) { + if (pm == basepm) { + if (pm->node.flags & PM_SELFREF) { + /* Loop signalled by resolve_nameref() */ + if (upscope(pm, pm->base) == pm) { + zerr("%s: invalid self reference", pm->u.str); + unsetparam_pm(pm, 0, 1); + return; + } + pm->node.flags &= ~PM_SELFREF; + } else if (pm->base == pm->level) { + if (pm->u.str && *(pm->u.str) && + strcmp(pm->node.nam, pm->u.str) == 0) { + zerr("%s: invalid self reference", pm->u.str); + unsetparam_pm(pm, 0, 1); + return; + } + } + } else if (basepm->u.str) { + if (basepm->base <= basepm->level && + strcmp(pm->node.nam, basepm->u.str) == 0) { + zerr("%s: invalid self reference", pm->u.str); + unsetparam_pm(pm, 0, 1); + return; + } + } + } else + pm->base = basepm->level; + } + if (pm->base > pm->level) { + if (EMULATION(EMULATE_KSH)) { + zerr("%s: global reference cannot refer to local variable", + pm->node.nam); + unsetparam_pm(pm, 0, 1); + } else if (isset(WARNNESTEDVAR)) + zwarn("%s: global reference to local variable: %s", + pm->node.nam, pm->u.str); + } + if (pm->u.str && upscope(pm, pm->base) == pm && + strcmp(pm->node.nam, pm->u.str) == 0) { + zerr("%s: invalid self reference", pm->u.str); + unsetparam_pm(pm, 0, 1); + } } } @@ -6217,7 +6282,10 @@ valid_refname(char *val) if (*t == '[') { tokenize(t = dupstring(t+1)); t = parse_subscript(t, 0, ']'); - } else { + } else if (t[1] || !(*t == '!' || *t == '?' || + *t == '$' || *t == '-' || + *t == '0' || *t == '_')) { + /* Skipping * @ # because of doshfunc() implementation */ t = NULL; } } |