From 1be52186b4aed40eb9fe295691932037ecf6c6ab Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Fri, 2 Dec 2022 19:19:27 +0900 Subject: 51079: metafy sep in array subscript flag (s:sep:) this enable sep to contain \0 etc. --- Src/params.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'Src/params.c') diff --git a/Src/params.c b/Src/params.c index 27ea82298..f1fe38955 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1262,7 +1262,6 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, /* first parse any subscription flags */ if (v->pm && (*s == '(' || *s == Inpar)) { int escapes = 0; - int waste; for (s++; *s != ')' && *s != Outpar && s != *str; s++) { switch (*s) { case 'r': @@ -1339,8 +1338,13 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, sav = *t; *t = '\0'; s += arglen; - sep = escapes ? getkeystring(s, &waste, GETKEYS_SEP, NULL) - : dupstring(s); + if (escapes) { + int len; + sep = getkeystring(s, &len, GETKEYS_SEP, NULL); + sep = metafy(sep, len, META_HREALLOC); + } + else + sep = dupstring(s); *t = sav; s = t + arglen - 1; break; -- cgit v1.2.3 From a73c705b0c864a9ce042fca6e72e0c92d4ad8237 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Fri, 16 Dec 2022 23:22:33 +0100 Subject: 51212: remove STOUC() macro This served as a workaround for ancient compilers where casts to unsigned char were broken. --- ChangeLog | 12 +++++++ Etc/zsh-development-guide | 3 -- Src/Modules/curses.c | 4 +-- Src/Modules/stat.c | 2 +- Src/Modules/zftp.c | 12 +++---- Src/Modules/zpty.c | 2 +- Src/Modules/zutil.c | 24 +++++++------- Src/Zle/compcore.c | 6 ++-- Src/Zle/complete.c | 2 +- Src/Zle/complist.c | 12 +++---- Src/Zle/zle.h | 2 +- Src/Zle/zle_keymap.c | 22 ++++++------- Src/Zle/zle_main.c | 4 +-- Src/Zle/zle_thingy.c | 4 +-- Src/Zle/zle_utils.c | 4 +-- Src/builtin.c | 20 ++++++------ Src/exec.c | 2 +- Src/glob.c | 4 +-- Src/hist.c | 6 ++-- Src/init.c | 10 +++--- Src/input.c | 8 ++--- Src/lex.c | 10 +++--- Src/math.c | 2 +- Src/module.c | 2 +- Src/params.c | 25 ++++++++------- Src/parse.c | 6 ++-- Src/pattern.c | 81 ++++++++++++++++++++++++----------------------- Src/prompt.c | 2 +- Src/sort.c | 4 +-- Src/subst.c | 6 ++-- Src/utils.c | 68 +++++++++++++++++++-------------------- Src/zsh.h | 13 -------- Src/ztype.h | 2 +- configure.ac | 10 ------ 34 files changed, 192 insertions(+), 204 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index e9e572957..996704135 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,17 @@ 2022-12-16 Oliver Kiddle + * 51212: Etc/zsh-development-guide, Src/Modules/curses.c, + Src/Modules/stat.c, Src/Modules/zftp.c, Src/Modules/zpty.c, + Src/Modules/zutil.c, Src/Zle/compcore.c, Src/Zle/complete.c, + Src/Zle/complist.c, Src/Zle/zle.h, Src/Zle/zle_keymap.c, + Src/Zle/zle_main.c, Src/Zle/zle_thingy.c, Src/Zle/zle_utils.c, + Src/builtin.c, Src/exec.c, Src/glob.c, Src/hist.c, Src/init.c, + Src/input.c, Src/lex.c, Src/math.c, Src/module.c, Src/params.c, + Src/parse.c, Src/pattern.c, Src/prompt.c, Src/sort.c, Src/subst.c, + Src/utils.c, Src/zsh.h, Src/ztype.h, configure.ac: remove STOUC() + macro which served as a workaround for ancient compilers where + casts to unsigned char were broken + * 51215: Src/Zle/zle_keymap.c, Test/X03zlebindkey.ztst, Test/X02zlevi.ztst: consume whole CSI sequences from the input even where they aren't explicitly bound diff --git a/Etc/zsh-development-guide b/Etc/zsh-development-guide index e8c292cfd..565b0b1d9 100644 --- a/Etc/zsh-development-guide +++ b/Etc/zsh-development-guide @@ -223,9 +223,6 @@ C coding style * Do not use space between the function name and the opening parenthesis. Use space after if/for/while. Use space after type casts. -* Do not use (unsigned char) casts since some compilers do not handle - them properly. Use the provided STOUC(X) macro instead. - * If you use emacs 19.30 or newer you can put the following line to your ~/.emacs file to make these formatting rules the default: diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c index e46903916..ad17ed65f 100644 --- a/Src/Modules/curses.c +++ b/Src/Modules/curses.c @@ -1426,10 +1426,10 @@ zccmd_querychar(const char *nam, char **args) inc &= A_CHARTEXT; if (imeta(inc)) { instr[0] = Meta; - instr[1] = STOUC(inc ^ 32); + instr[1] = (unsigned char) (inc ^ 32); instr[2] = '\0'; } else { - instr[0] = STOUC(inc); + instr[0] = (unsigned char) inc; instr[1] = '\0'; } attrs = inc; diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c index 0df9b35b7..c9f851974 100644 --- a/Src/Modules/stat.c +++ b/Src/Modules/stat.c @@ -406,7 +406,7 @@ bin_stat(char *name, char **args, Options ops, UNUSED(int func)) } else { for (; *arg; arg++) { if (strchr("glLnNorstT", *arg)) - ops->ind[STOUC(*arg)] = 1; + ops->ind[(unsigned char) *arg] = 1; else if (*arg == 'A') { if (arg[1]) { arrnam = arg+1; diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index e8e239e76..49b3ffa89 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -944,9 +944,9 @@ zfopendata(char *name, union tcp_sockaddr *zdsockp, int *is_passivep) return 1; } for (i = 0; i < 4; i++) - iaddr[i] = STOUC(nums[i]); - iport[0] = STOUC(nums[4]); - iport[1] = STOUC(nums[5]); + iaddr[i] = (unsigned char) nums[i]; + iport[0] = (unsigned char) nums[4]; + iport[1] = (unsigned char) nums[5]; memcpy(&zdsockp->in.sin_addr, iaddr, sizeof(iaddr)); memcpy(&zdsockp->in.sin_port, iport, sizeof(iport)); @@ -2438,7 +2438,7 @@ zftp_type(char *name, char **args, int flags) fflush(stdout); return 0; } else { - nt = toupper(STOUC(*str)); + nt = toupper((unsigned char) *str); /* * RFC959 specifies other types, but these are the only * ones we know what to do with. @@ -2472,7 +2472,7 @@ zftp_mode(char *name, char **args, UNUSED(int flags)) fflush(stdout); return 0; } - nt = str[0] = toupper(STOUC(*str)); + nt = str[0] = toupper((unsigned char) *str); if (str[1] || (nt != 'S' && nt != 'B')) { zwarnnam(name, "transfer mode %s not recognised", str); return 1; @@ -3075,7 +3075,7 @@ bin_zftp(char *name, char **args, UNUSED(Options ops), UNUSED(int func)) if ((prefs = getsparam_u("ZFTP_PREFS"))) { zfprefs = 0; for (ptr = prefs; *ptr; ptr++) { - switch (toupper(STOUC(*ptr))) { + switch (toupper((unsigned char) *ptr)) { case 'S': /* sendport */ zfprefs |= ZFPF_SNDP; diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index dfd2a2a7a..c2656698c 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -638,7 +638,7 @@ ptyread(char *nam, Ptycmd cmd, char **args, int noblock, int mustmatch) readchar = cmd->read; cmd->read = -1; } else - readchar = STOUC(buf[used]); + readchar = (unsigned char) buf[used]; if (imeta(readchar)) { buf[used++] = Meta; buf[used++] = (char) (readchar ^ 32); diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 2f17c03f1..8a7d0a4c5 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -795,11 +795,11 @@ static char *zformat_substring(char* instr, char **specs, char **outp, if (idigit(*s)) { for (min = 0; idigit(*s); s++) - min = (min * 10) + (int) STOUC(*s) - '0'; + min = (min * 10) + (int) (unsigned char) *s - '0'; } /* Ternary expressions */ - testit = (STOUC(*s) == '('); + testit = ((unsigned char) *s == '('); if (testit && s[1] == '-') { /* Allow %(-1... etc. */ @@ -808,25 +808,25 @@ static char *zformat_substring(char* instr, char **specs, char **outp, } if ((*s == '.' || testit) && idigit(s[1])) { for (max = 0, s++; idigit(*s); s++) - max = (max * 10) + (int) STOUC(*s) - '0'; + max = (max * 10) + (int) (unsigned char) *s - '0'; } else if (*s == '.' || testit) s++; - if (testit && STOUC(*s)) { + if (testit && (unsigned char) *s) { int actval, testval, endcharl; /* Only one number is useful for ternary expressions. */ testval = (min >= 0) ? min : (max >= 0) ? max : 0; - if (specs[STOUC(*s)] && *specs[STOUC(*s)]) { + if (specs[(unsigned char) *s] && *specs[(unsigned char) *s]) { if (presence) { if (testval) #ifdef MULTIBYTE_SUPPORT if (isset(MULTIBYTE)) - actval = MB_METASTRWIDTH(specs[STOUC(*s)]); + actval = MB_METASTRWIDTH(specs[(unsigned char) *s]); else #endif - actval = strlen(specs[STOUC(*s)]); + actval = strlen(specs[(unsigned char) *s]); else actval = 1; actval = right ? (testval < actval) : (testval >= actval); @@ -834,7 +834,7 @@ static char *zformat_substring(char* instr, char **specs, char **outp, if (right) /* put the sign back */ testval *= -1; /* zero means values are equal, i.e. true */ - actval = (int)mathevali(specs[STOUC(*s)]) - testval; + actval = (int) mathevali(specs[(unsigned char) *s]) - testval; } } else actval = presence ? !right : testval; @@ -855,7 +855,7 @@ static char *zformat_substring(char* instr, char **specs, char **outp, return NULL; } else if (skip) { continue; - } else if ((spec = specs[STOUC(*s)])) { + } else if ((spec = specs[(unsigned char) *s])) { int len; if ((len = strlen(spec)) > max && max >= 0) @@ -950,7 +950,7 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) zwarnnam(nam, "invalid argument: %s", *ap); return 1; } - specs[STOUC(ap[0][0])] = ap[0] + 2; + specs[(unsigned char) ap[0][0]] = ap[0] + 2; } out = (char *) zhalloc(olen = 128); @@ -1864,7 +1864,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) d->vals = d->last = NULL; opt_descs = d; if (!o[1]) - sopts[STOUC(*o)] = d; + sopts[(unsigned char) *o] = d; if ((flags & ZOF_MAP) && !map_opt_desc(d)) { zwarnnam(nam, "cyclic option mapping: %s", args[-1]); return 1; @@ -1888,7 +1888,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } if (!(d = lookup_opt(o + 1))) { while (*++o) { - if (!(d = sopts[STOUC(*o)])) { + if (!(d = sopts[(unsigned char) *o])) { if (fail) { if (*o != '-') zwarnnam(nam, "bad option: -%c", *o); diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 4ac5d089f..64a860fa3 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -2898,9 +2898,9 @@ add_match_data(int alt, char *str, char *orig, Cline line, *t++ = '$'; *t++ = '\''; *t++ = '\\'; - *t++ = '0' + ((STOUC(curchar) >> 6) & 7); - *t++ = '0' + ((STOUC(curchar) >> 3) & 7); - *t++ = '0' + (STOUC(curchar) & 7); + *t++ = '0' + (((unsigned char) curchar >> 6) & 7); + *t++ = '0' + (((unsigned char) curchar >> 3) & 7); + *t++ = '0' + ((unsigned char) curchar & 7); *t++ = '\''; } while (cnt == MB_INCOMPLETE && fs < fe); /* Scanning restarts from the spot after the char we skipped. */ diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c index 67a60963e..96ad7b3f1 100644 --- a/Src/Zle/complete.c +++ b/Src/Zle/complete.c @@ -518,7 +518,7 @@ parse_class(Cpattern p, char *iptr) ch = range_type((char *)iptr, nptr-iptr); iptr = nptr + 2; if (ch != PP_UNKWN) - *optr++ = STOUC(Meta) + ch; + *optr++ = (unsigned char) Meta + ch; } else { /* characters stay metafied */ char *ptr1 = iptr; diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 0dc64db6a..6e0eac31f 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -291,12 +291,12 @@ getcolval(char *s, int multi) case '?': *p = '\177'; break; default: if (*s >= '0' && *s <= '7') { - int i = STOUC(*s); + int i = (unsigned char) *s; if (*++s >= '0' && *s <= '7') { - i = (i * 8) + STOUC(*s); + i = (i * 8) + (unsigned char) *s; if (*++s >= '0' && *s <= '7') - i = (i * 8) + STOUC(*s); + i = (i * 8) + (unsigned char) *s; } *p = (char) i; } else @@ -305,7 +305,7 @@ getcolval(char *s, int multi) } else if (*s == '^') { if ((s[1] >= '@' && s[1] <= '_') || (s[1] >= 'a' && s[1] <= 'z')) - *p = (char) (STOUC(*s) & ~0x60); + *p = (char) ((unsigned char) *s & ~0x60); else if (s[1] == '?') *p = '\177'; else { @@ -794,7 +794,7 @@ clnicezputs(int do_colors, char *s, int ml) */ for (t = sptr; *t; t++) { /* Input is metafied... */ - int nc = (*t == Meta) ? STOUC(*++t ^ 32) : STOUC(*t); + int nc = (*t == Meta) ? (unsigned char) (*++t ^ 32) : (unsigned char) *t; /* Is the screen full? */ if (ml == mlend - 1 && col == zterm_columns - 1) { mlprinted = ml - oml; @@ -852,7 +852,7 @@ clnicezputs(int do_colors, char *s, int ml) cc = *s++ ^ 32; for (t = nicechar(cc); *t; t++) { - int nc = (*t == Meta) ? STOUC(*++t ^ 32) : STOUC(*t); + int nc = (*t == Meta) ? (unsigned char) (*++t ^ 32) : (unsigned char) *t; if (ml == mlend - 1 && col == zterm_columns - 1) { mlprinted = ml - oml; return 0; diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index f59545397..97cc7d797 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -526,7 +526,7 @@ typedef REFRESH_ELEMENT *REFRESH_STRING; ((int)((unsigned)(x) - ZSH_INVALID_WCHAR_BASE)) /* Turn a single byte character into a private wide character */ #define ZSH_CHAR_TO_INVALID_WCHAR(x) \ - ((wchar_t)(STOUC(x) + ZSH_INVALID_WCHAR_BASE)) + ((wchar_t)((unsigned char) x + ZSH_INVALID_WCHAR_BASE)) #endif diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index 48691e8d0..ec8dd031e 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -404,7 +404,7 @@ static void scankeys(HashNode hn, UNUSED(int flags)) { Key k = (Key) hn; - int f = k->nam[0] == Meta ? STOUC(k->nam[1])^32 : STOUC(k->nam[0]); + int f = k->nam[0] == Meta ? (unsigned char) k->nam[1]^32 : (unsigned char) k->nam[0]; char m[3]; while(skm_last < f) { @@ -566,7 +566,7 @@ mod_export int bindkey(Keymap km, const char *seq, Thingy bind, char *str) { Key k; - int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]); + int f = seq[0] == Meta ? (unsigned char) seq[1]^32 : (unsigned char) seq[0]; char *buf, *ptr; if(km->flags & KM_IMMUTABLE) @@ -661,7 +661,7 @@ keybind(Keymap km, char *seq, char **strp) Key k; if(ztrlen(seq) == 1) { - int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]); + int f = seq[0] == Meta ? (unsigned char) seq[1]^32 : (unsigned char) seq[0]; Thingy bind = km->first[f]; if(bind) @@ -687,7 +687,7 @@ keyisprefix(Keymap km, char *seq) if(!*seq) return 1; if(ztrlen(seq) == 1) { - int f = seq[0] == Meta ? STOUC(seq[1])^32 : STOUC(seq[0]); + int f = seq[0] == Meta ? (unsigned char) seq[1]^32 : (unsigned char) seq[0]; if(km->first[f]) return 0; @@ -764,10 +764,10 @@ bin_bindkey(char *name, char **argv, Options ops, UNUSED(int func)) int n; /* select operation and ensure no clashing arguments */ - for(op = opns; op->o && !OPT_ISSET(ops,STOUC(op->o)); op++) ; + for(op = opns; op->o && !OPT_ISSET(ops,(unsigned char) op->o); op++) ; if(op->o) for(opp = op; (++opp)->o; ) - if(OPT_ISSET(ops,STOUC(opp->o))) { + if(OPT_ISSET(ops,(unsigned char) opp->o)) { zwarnnam(name, "incompatible operation selection options"); return 1; } @@ -1049,7 +1049,7 @@ bin_bindkey_bind(char *name, char *kmname, Keymap km, char **argv, Options ops, char m[3]; if(len < 2 || len > 2 + (bseq[1] == '-') || - (first = STOUC(bseq[0])) > (last = STOUC(bseq[len - 1]))) { + (first = (unsigned char) bseq[0]) > (last = (unsigned char) bseq[len - 1])) { zwarnnam(name, "malformed key range `%s'", useq); ret = 1; } else { @@ -1149,8 +1149,8 @@ scanbindlist(char *seq, Thingy bind, char *str, void *magic) if(bind == bs->bind && (bind || !strcmp(str, bs->str)) && ztrlen(seq) == 1 && ztrlen(bs->lastseq) == 1) { int l = bs->lastseq[1] ? - STOUC(bs->lastseq[1]) ^ 32 : STOUC(bs->lastseq[0]); - int t = seq[1] ? STOUC(seq[1]) ^ 32 : STOUC(seq[0]); + (unsigned char) bs->lastseq[1] ^ 32 : (unsigned char) bs->lastseq[0]; + int t = seq[1] ? (unsigned char) seq[1] ^ 32 : (unsigned char) seq[0]; if(t == l + 1) { zsfree(bs->lastseq); @@ -1526,10 +1526,10 @@ getrestchar_keybuf(void) */ while (1) { if (bufind < buflen) { - c = STOUC(keybuf[bufind++]); + c = (unsigned char) keybuf[bufind++]; if (c == Meta) { DPUTS(bufind == buflen, "Meta at end of keybuf"); - c = STOUC(keybuf[bufind++]) ^ 32; + c = (unsigned char) keybuf[bufind++] ^ 32; } } else { /* diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 9edf30e01..40b902901 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -876,7 +876,7 @@ getbyte(long do_keytmout, int *timeout, int full) #endif if (kungetct) - ret = STOUC(kungetbuf[--kungetct]); + ret = (unsigned char) kungetbuf[--kungetct]; else { for (;;) { int q = queue_signal_level(); @@ -940,7 +940,7 @@ getbyte(long do_keytmout, int *timeout, int full) else if (cc == '\n') cc = '\r'; - ret = STOUC(cc); + ret = (unsigned char) cc; } /* * curvichg.buf is raw bytes, not wide characters, so is dealt diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index cd3f2c356..1b036a8a0 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -366,10 +366,10 @@ bin_zle(char *name, char **args, Options ops, UNUSED(int func)) int n; /* select operation and ensure no clashing arguments */ - for(op = opns; op->o && !OPT_ISSET(ops,STOUC(op->o)); op++) ; + for(op = opns; op->o && !OPT_ISSET(ops, (unsigned char) op->o); op++) ; if(op->o) for(opp = op; (++opp)->o; ) - if(OPT_ISSET(ops,STOUC(opp->o))) { + if(OPT_ISSET(ops, (unsigned char) opp->o)) { zwarnnam(name, "incompatible operation selection options"); return 1; } diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 3d9017dcf..2536e9faa 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -1265,7 +1265,7 @@ bindztrdup(char *str) char *buf, *ptr, *ret; for(ptr = str; *ptr; ptr++) { - c = *ptr == Meta ? STOUC(*++ptr) ^ 32 : STOUC(*ptr); + c = *ptr == Meta ? (unsigned char) *++ptr ^ 32 : (unsigned char) *ptr; if(c & 0x80) { len += 3; c &= 0x7f; @@ -1279,7 +1279,7 @@ bindztrdup(char *str) } ptr = buf = zalloc(len); for(; *str; str++) { - c = *str == Meta ? STOUC(*++str) ^ 32 : STOUC(*str); + c = *str == Meta ? (unsigned char) *++str ^ 32 : (unsigned char) *str; if(c & 0x80) { *ptr++ = '\\'; *ptr++ = 'M'; diff --git a/Src/builtin.c b/Src/builtin.c index a7b7755a7..db83313d6 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2634,7 +2634,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) * these flags are defined in zsh.h */ for (; *optstr; optstr++, bit <<= 1) { - int optval = STOUC(*optstr); + int optval = (unsigned char) *optstr; if (OPT_MINUS(ops,optval)) on |= bit; else if (OPT_PLUS(ops,optval)) @@ -4752,7 +4752,7 @@ bin_print(char *name, char **args, Options ops, int func) */ if (*aptr == '\033' || *aptr == '\233') { for (aptr++, l--; - l && !isalpha(STOUC(*aptr)); + l && !isalpha((unsigned char) (*aptr)); aptr++, l--) ; aptr++; @@ -5313,9 +5313,9 @@ bin_print(char *name, char **args, Options ops, int func) else cc = WEOF; if (cc == WEOF) - cc = (curlen > 1) ? STOUC(curarg[1]) : 0; + cc = (curlen > 1) ? (unsigned char) (curarg[1]) : 0; #else - cc = (curlen > 1) ? STOUC(curarg[1]) : 0; + cc = (curlen > 1) ? (unsigned char) (curarg[1]) : 0; #endif if (type == 2) { doubleval = cc; @@ -6685,7 +6685,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) continue; first = 0; } - if (imeta(STOUC(*bptr))) { + if (imeta((unsigned char) *bptr)) { bptr[1] = bptr[0] ^ 32; bptr[0] = Meta; bptr += 2; @@ -6878,7 +6878,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) if (bslash) continue; } - if (imeta(STOUC(*bptr))) { + if (imeta((unsigned char) *bptr)) { bptr[1] = bptr[0] ^ 32; bptr[0] = Meta; bptr += 2; @@ -7000,14 +7000,14 @@ zread(int izle, int *readchar, long izle_timeout) buffer. This may be a null byte to indicate EOF. If reading from the buffer, move on the buffer pointer. */ if (*zbuf == Meta) - return zbuf++, STOUC(*zbuf++ ^ 32); + return zbuf++, (unsigned char) (*zbuf++ ^ 32); else - return (*zbuf) ? STOUC(*zbuf++) : EOF; + return (*zbuf) ? (unsigned char) *zbuf++ : EOF; } if (*readchar >= 0) { cc = *readchar; *readchar = -1; - return STOUC(cc); + return (unsigned char) cc; } for (;;) { /* read a character from readfd */ @@ -7015,7 +7015,7 @@ zread(int izle, int *readchar, long izle_timeout) switch (ret) { case 1: /* return the character read */ - return STOUC(cc); + return (unsigned char) cc; case -1: #if defined(EAGAIN) || defined(EWOULDBLOCK) if (!retry && readfd == 0 && ( diff --git a/Src/exec.c b/Src/exec.c index 2b7e0c7c5..c8eb71b34 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -603,7 +603,7 @@ zexecve(char *pth, char **argv, char **newenvp) isbinary = 1; hasletter = 0; for (ptr = execvebuf; ptr < ptr2; ptr++) { - if (islower(STOUC(*ptr)) || *ptr == '$' || *ptr == '`') + if (islower((unsigned char) *ptr) || *ptr == '$' || *ptr == '`') hasletter = 1; if (hasletter && *ptr == '\n') { isbinary = 0; diff --git a/Src/glob.c b/Src/glob.c index 490bafc37..63f8a5fa7 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -2418,11 +2418,11 @@ xpandbraces(LinkList list, LinkNode *np) memset(ccl, 0, sizeof(ccl) / sizeof(ccl[0])); for (p = str + 1; p < str2;) { if (itok(c1 = *p++)) - c1 = ztokens[c1 - STOUC(Pound)]; + c1 = ztokens[c1 - (unsigned char) Pound]; if ((char) c1 == Meta) c1 = 32 ^ *p++; if (itok(c2 = *p)) - c2 = ztokens[c2 - STOUC(Pound)]; + c2 = ztokens[c2 - (unsigned char) Pound]; if ((char) c2 == Meta) c2 = 32 ^ p[1]; if (IS_DASH((char)c1) && lastch >= 0 && diff --git a/Src/hist.c b/Src/hist.c index bff0abe61..82d03a840 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -2235,7 +2235,7 @@ casemodify(char *str, int how) char *mbptr; for (mbptr = mbstr; mbptr < mbstr + len2; mbptr++) { - if (imeta(STOUC(*mbptr))) { + if (imeta((unsigned char) *mbptr)) { *ptr2++ = Meta; *ptr2++ = *mbptr ^ 32; } else @@ -2254,10 +2254,10 @@ casemodify(char *str, int how) int c; int mod = 0; if (*str == Meta) { - c = STOUC(str[1] ^ 32); + c = (unsigned char) (str[1] ^ 32); str += 2; } else - c = STOUC(*str++); + c = (unsigned char) *str++; switch (how) { case CASMOD_LOWER: if (isupper(c)) { diff --git a/Src/init.c b/Src/init.c index 871d46b12..9981d059a 100644 --- a/Src/init.c +++ b/Src/init.c @@ -500,10 +500,10 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp, } } break; - } else if (isspace(STOUC(**argv))) { + } else if (isspace((unsigned char) **argv)) { /* zsh's typtab not yet set, have to use ctype */ while (*++*argv) - if (!isspace(STOUC(**argv))) { + if (!isspace((unsigned char) **argv)) { badoptionstring: WARN_OPTION("bad option string: '%s'", args); return 1; @@ -1724,9 +1724,9 @@ zsh_main(UNUSED(int argc), char **argv) * interactive */ typtab['\0'] |= IMETA; - typtab[STOUC(Meta) ] |= IMETA; - typtab[STOUC(Marker)] |= IMETA; - for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(Nularg); t0++) + typtab[(unsigned char) Meta ] |= IMETA; + typtab[(unsigned char) Marker] |= IMETA; + for (t0 = (int) (unsigned char) Pound; t0 <= (int) (unsigned char) Nularg; t0++) typtab[t0] |= ITOK | IMETA; for (t = argv; *t; *t = metafy(*t, -1, META_ALLOC), t++); diff --git a/Src/input.c b/Src/input.c index 9898a7177..d55b05696 100644 --- a/Src/input.c +++ b/Src/input.c @@ -220,7 +220,7 @@ shingetchar(void) int nread, rsize = isset(SHINSTDIN) ? 1 : SHINBUFSIZE; if (shinbufptr < shinbufendptr) - return STOUC(*shinbufptr++); + return (unsigned char) *shinbufptr++; shinbufreset(); #ifdef USE_LSEEK @@ -242,7 +242,7 @@ shingetchar(void) zerr("lseek(%d, %d): %e", SHIN, -(nread - rsize), errno); } else shinbufendptr = shinbuffer + nread; - return STOUC(*shinbufptr++); + return (unsigned char) *shinbufptr++; } #endif for (;;) { @@ -259,7 +259,7 @@ shingetchar(void) } if (shinbufendptr == shinbuffer) return -1; - return STOUC(*shinbufptr++); + return (unsigned char) *shinbufptr++; } /* Read a line from SHIN. Convert tokens and * @@ -328,7 +328,7 @@ ingetc(void) if (inbufleft) { inbufleft--; inbufct--; - if (itok(lastc = STOUC(*inbufptr++))) + if (itok(lastc = (unsigned char) *inbufptr++)) continue; if (((inbufflags & INP_LINENO) || !strin) && lastc == '\n') lineno++; diff --git a/Src/lex.c b/Src/lex.c index e2f8bcfb1..15da85a93 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -423,7 +423,7 @@ initlextabs(void) for (t0 = 0; lx2[t0]; t0++) lexact2[(int)lx2[t0]] = t0; lexact2['&'] = LX2_BREAK; - lexact2[STOUC(Meta)] = LX2_META; + lexact2[(unsigned char) Meta] = LX2_META; lextok2['*'] = Star; lextok2['?'] = Quest; lextok2['{'] = Inbrace; @@ -722,7 +722,7 @@ gettok(void) } return peek; } - switch (lexact1[STOUC(c)]) { + switch (lexact1[(unsigned char) c]) { case LX1_BKSLASH: d = hgetc(); if (d == '\n') @@ -960,8 +960,8 @@ gettokstr(int c, int sub) if (inbl && !in_brace_param && !pct) act = LX2_BREAK; else { - act = lexact2[STOUC(c)]; - c = lextok2[STOUC(c)]; + act = lexact2[(unsigned char) c]; + c = lextok2[(unsigned char) c]; } switch (act) { case LX2_BREAK: @@ -1263,7 +1263,7 @@ gettokstr(int c, int sub) continue; } else { add(Bnull); - if (c == STOUC(Meta)) { + if (c == (unsigned char) Meta) { c = hgetc(); #ifdef DEBUG if (lexstop) { diff --git a/Src/math.c b/Src/math.c index 777ad9c31..12c8d6f6b 100644 --- a/Src/math.c +++ b/Src/math.c @@ -955,7 +955,7 @@ getcvar(char *s) } } #endif - mn.u.l = STOUC(*t == Meta ? t[1] ^ 32 : *t); + mn.u.l = (unsigned char) (*t == Meta ? t[1] ^ 32 : *t); } unqueue_signals(); return mn; diff --git a/Src/module.c b/Src/module.c index bab4d8d73..6cf442270 100644 --- a/Src/module.c +++ b/Src/module.c @@ -2474,7 +2474,7 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func)) return 1; } for (fp = fonly; *fp; fp++) { - if (OPT_ISSET(ops,STOUC(*fp)) && !OPT_ISSET(ops,'F')) { + if (OPT_ISSET(ops,(unsigned char) *fp) && !OPT_ISSET(ops,'F')) { zwarnnam(nam, "-%c is only allowed with -F", *fp); return 1; } diff --git a/Src/params.c b/Src/params.c index f1fe38955..2e4a6eae2 100644 --- a/Src/params.c +++ b/Src/params.c @@ -732,7 +732,7 @@ split_env_string(char *env, char **name, char **value) tenv = strcpy(zhalloc(strlen(env) + 1), env); for (str = tenv; *str && *str != '='; str++) { - if (STOUC(*str) >= 128) { + if ((unsigned char) *str >= 128) { /* * We'll ignore environment variables with names not * from the portable character set since we don't @@ -4123,7 +4123,8 @@ char * tiedarrgetfn(Param pm) { struct tieddata *dptr = (struct tieddata *)pm->u.data; - return *dptr->arrptr ? zjoin(*dptr->arrptr, STOUC(dptr->joinchar), 1) : ""; + return *dptr->arrptr ? + zjoin(*dptr->arrptr, (unsigned char) dptr->joinchar, 1) : ""; } /**/ @@ -4819,12 +4820,12 @@ keyboardhacksetfn(UNUSED(Param pm), char *x) zwarn("Only one KEYBOARD_HACK character can be defined"); /* could be changed if needed */ } for (i = 0; i < len; i++) { - if (!isascii(STOUC(x[i]))) { + if (!isascii((unsigned char) x[i])) { zwarn("KEYBOARD_HACK can only contain ASCII characters"); return; } } - keyboardhackchar = len ? STOUC(x[0]) : '\0'; + keyboardhackchar = len ? (unsigned char) x[0] : '\0'; free(x); } else keyboardhackchar = '\0'; @@ -4858,14 +4859,14 @@ histcharssetfn(UNUSED(Param pm), char *x) if (len > 3) len = 3; for (i = 0; i < len; i++) { - if (!isascii(STOUC(x[i]))) { + if (!isascii((unsigned char) x[i])) { zwarn("HISTCHARS can only contain ASCII characters"); return; } } - bangchar = len ? STOUC(x[0]) : '\0'; - hatchar = len > 1 ? STOUC(x[1]) : '\0'; - hashchar = len > 2 ? STOUC(x[2]) : '\0'; + bangchar = len ? (unsigned char) x[0] : '\0'; + hatchar = len > 1 ? (unsigned char) x[1] : '\0'; + hashchar = len > 2 ? (unsigned char) x[2] : '\0'; free(x); } else { bangchar = '!'; @@ -5087,7 +5088,7 @@ arrfixenv(char *s, char **t) if (pm->node.flags & PM_SPECIAL) joinchar = ':'; else - joinchar = STOUC(((struct tieddata *)pm->u.data)->joinchar); + joinchar = (unsigned char) ((struct tieddata *)pm->u.data)->joinchar; addenv(pm, t ? zjoin(t, joinchar, 1) : ""); } @@ -5109,9 +5110,9 @@ zputenv(char *str) char *ptr; int ret; - for (ptr = str; *ptr && STOUC(*ptr) < 128 && *ptr != '='; ptr++) + for (ptr = str; *ptr && (unsigned char) *ptr < 128 && *ptr != '='; ptr++) ; - if (STOUC(*ptr) >= 128) { + if ((unsigned char) *ptr >= 128) { /* * Environment variables not in the portable character * set are non-standard and we don't really know of @@ -6022,7 +6023,7 @@ printparamnode(HashNode hn, int printflags) * append the join char for tied parameters if different from colon * for typeset -p output. */ - unsigned char joinchar = STOUC(((struct tieddata *)peer->u.data)->joinchar); + unsigned char joinchar = (unsigned char) ((struct tieddata *)peer->u.data)->joinchar; if (joinchar != ':') { char buf[2]; buf[0] = joinchar; diff --git a/Src/parse.c b/Src/parse.c index 2fac5c89c..283225b74 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -433,9 +433,9 @@ ecstrcode(char *s) t = has_token(s); wordcode c = (t ? 3 : 2); switch (l) { - case 4: c |= ((wordcode) STOUC(s[2])) << 19; - case 3: c |= ((wordcode) STOUC(s[1])) << 11; - case 2: c |= ((wordcode) STOUC(s[0])) << 3; break; + case 4: c |= ((wordcode) (unsigned char) s[2]) << 19; + case 3: c |= ((wordcode) (unsigned char) s[1]) << 11; + case 2: c |= ((wordcode) (unsigned char) s[0]) << 3; break; case 1: c = (t ? 7 : 6); break; } return c; diff --git a/Src/pattern.c b/Src/pattern.c index e947d1216..3edda1772 100644 --- a/Src/pattern.c +++ b/Src/pattern.c @@ -239,7 +239,7 @@ typedef unsigned long zrange_t; * a bit tricky... */ #define WCHAR_INVALID(ch) \ - ((wchar_t) (0xDC00 + STOUC(ch))) + ((wchar_t) (0xDC00 + (unsigned char) ch)) #endif /* MULTIBYTE_SUPPORT */ /* @@ -346,7 +346,7 @@ metacharinc(char **x) * set doesn't have the property that all bytes with the 8th * bit clear are single characters then we are stuffed. */ - if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*inptr) & 0x80)) + if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) *inptr & 0x80)) { if (itok(*inptr)) inchar = ztokens[*inptr++ - Pound]; @@ -357,7 +357,7 @@ metacharinc(char **x) inchar = *inptr++; } *x = inptr; - return (wchar_t)STOUC(inchar); + return (wchar_t)(unsigned char) inchar; } while (*inptr) { @@ -1181,8 +1181,8 @@ pattern_range_to_string(char *rangestr, char *outstr) int len = 0; while (*rangestr) { - if (imeta(STOUC(*rangestr))) { - int swtype = STOUC(*rangestr) - STOUC(Meta); + if (imeta((unsigned char) *rangestr)) { + int swtype = (unsigned char) *rangestr - (unsigned char) Meta; if (swtype == 0) { /* Ordindary metafied character */ @@ -1278,17 +1278,17 @@ patcomppiece(int *flagp, int paren) kshchar = '\0'; if (*patparse && patparse[1] == Inpar) { if (*patparse == zpc_special[ZPC_KSH_PLUS]) - kshchar = STOUC('+'); + kshchar = (unsigned char) '+'; else if (*patparse == zpc_special[ZPC_KSH_BANG]) - kshchar = STOUC('!'); + kshchar = (unsigned char) '!'; else if (*patparse == zpc_special[ZPC_KSH_BANG2]) - kshchar = STOUC('!'); + kshchar = (unsigned char) '!'; else if (*patparse == zpc_special[ZPC_KSH_AT]) - kshchar = STOUC('@'); + kshchar = (unsigned char) '@'; else if (*patparse == zpc_special[ZPC_KSH_STAR]) - kshchar = STOUC('*'); + kshchar = (unsigned char) '*'; else if (*patparse == zpc_special[ZPC_KSH_QUEST]) - kshchar = STOUC('?'); + kshchar = (unsigned char) '?'; } /* @@ -1468,7 +1468,8 @@ patcomppiece(int *flagp, int paren) ch = range_type(patparse, len); patparse = nptr + 2; if (ch != PP_UNKWN) - patadd(NULL, STOUC(Meta) + ch, 1, PA_NOALIGN); + patadd(NULL, (unsigned char) Meta + ch, 1, + PA_NOALIGN); continue; } charstart = patparse; @@ -1476,10 +1477,10 @@ patcomppiece(int *flagp, int paren) if (*patparse == Dash && patparse[1] && patparse[1] != Outbrack) { - patadd(NULL, STOUC(Meta)+PP_RANGE, 1, PA_NOALIGN); + patadd(NULL, (unsigned char) Meta+PP_RANGE, 1, PA_NOALIGN); if (itok(*charstart)) { - patadd(0, STOUC(ztokens[*charstart - Pound]), 1, - PA_NOALIGN); + patadd(0, (unsigned char) ztokens[*charstart - Pound], + 1, PA_NOALIGN); } else { patadd(charstart, 0, patparse-charstart, PA_NOALIGN); } @@ -1487,7 +1488,7 @@ patcomppiece(int *flagp, int paren) METACHARINC(patparse); } if (itok(*charstart)) { - patadd(0, STOUC(ztokens[*charstart - Pound]), 1, + patadd(0, (unsigned char) ztokens[*charstart - Pound], 1, PA_NOALIGN); } else { patadd(charstart, 0, patparse-charstart, PA_NOALIGN); @@ -1910,8 +1911,8 @@ charref(char *x, char *y, int *zmb_ind) wchar_t wc; size_t ret; - if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*x) & 0x80)) - return (wchar_t) STOUC(*x); + if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) *x & 0x80)) + return (wchar_t) (unsigned char) *x; ret = mbrtowc(&wc, x, y-x, &shiftstate); @@ -1937,7 +1938,7 @@ charnext(char *x, char *y) wchar_t wc; size_t ret; - if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(*x) & 0x80)) + if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) *x & 0x80)) return x + 1; ret = mbrtowc(&wc, x, y-x, &shiftstate); @@ -1965,8 +1966,8 @@ charrefinc(char **x, char *y, int *z) wchar_t wc; size_t ret; - if (!(patglobflags & GF_MULTIBYTE) || !(STOUC(**x) & 0x80)) - return (wchar_t) STOUC(*(*x)++); + if (!(patglobflags & GF_MULTIBYTE) || !((unsigned char) **x & 0x80)) + return (wchar_t) (unsigned char) *(*x)++; ret = mbrtowc(&wc, *x, y-*x, &shiftstate); @@ -2025,13 +2026,13 @@ charsub(char *x, char *y) #else /* no MULTIBYTE_SUPPORT */ /* Get a character from the start point in a string */ -#define CHARREF(x, y) (STOUC(*(x))) +#define CHARREF(x, y) ((unsigned char) (*(x))) /* Get a pointer to the next character */ #define CHARNEXT(x, y) ((x)+1) /* Increment a pointer past the current character. */ #define CHARINC(x, y) ((x)++) /* Get a character and increment */ -#define CHARREFINC(x, y, z) (STOUC(*(x)++)) +#define CHARREFINC(x, y, z) ((unsigned char) (*(x)++)) /* Counter the number of characters between two pointers, smaller first */ #define CHARSUB(x,y) ((y) - (x)) @@ -2890,7 +2891,7 @@ patmatch(Upat prog) } if (!no && P_OP(next) == P_EXACTLY && (!P_LS_LEN(next) || - !idigit(STOUC(*P_LS_STR(next)))) && + !idigit((unsigned char) (*P_LS_STR(next)))) && !(patglobflags & 0xff)) return 0; patinput = --save; @@ -3600,8 +3601,8 @@ mb_patmatchrange(char *range, wchar_t ch, int zmb_ind, wint_t *indptr, int *mtp) * ranges specially. */ while (*range) { - if (imeta(STOUC(*range))) { - int swtype = STOUC(*range++) - STOUC(Meta); + if (imeta((unsigned char) *range)) { + int swtype = (unsigned char) *range++ - (unsigned char) Meta; if (mtp) *mtp = swtype; switch (swtype) { @@ -3753,8 +3754,8 @@ mb_patmatchindex(char *range, wint_t ind, wint_t *chr, int *mtp) *mtp = 0; while (*range) { - if (imeta(STOUC(*range))) { - int swtype = STOUC(*range++) - STOUC(Meta); + if (imeta((unsigned char) *range)) { + int swtype = (unsigned char) *range++ - (unsigned char) Meta; switch (swtype) { case 0: range--; @@ -3845,13 +3846,13 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp) * ranges specially. */ for (; *range; range++) { - if (imeta(STOUC(*range))) { - int swtype = STOUC(*range) - STOUC(Meta); + if (imeta((unsigned char) *range)) { + int swtype = (unsigned char) *range - (unsigned char) Meta; if (mtp) *mtp = swtype; switch (swtype) { case 0: - if (STOUC(*++range ^ 32) == ch) + if ((unsigned char) (*++range ^ 32) == ch) return 1; break; case PP_ALPHA: @@ -3931,9 +3932,9 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp) break; case PP_RANGE: range++; - r1 = STOUC(UNMETA(range)); + r1 = (unsigned char) UNMETA(range); METACHARINC(range); - r2 = STOUC(UNMETA(range)); + r2 = (unsigned char) UNMETA(range); if (*range == Meta) range++; if (r1 <= ch && ch <= r2) { @@ -3955,7 +3956,7 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp) DPUTS(1, "BUG: unknown metacharacter in range."); break; } - } else if (STOUC(*range) == ch) { + } else if ((unsigned char) *range == ch) { if (mtp) *mtp = 0; return 1; @@ -3989,12 +3990,12 @@ patmatchindex(char *range, int ind, int *chr, int *mtp) *mtp = 0; for (; *range; range++) { - if (imeta(STOUC(*range))) { - int swtype = STOUC(*range) - STOUC(Meta); + if (imeta((unsigned char) *range)) { + int swtype = (unsigned char) *range - (unsigned char) Meta; switch (swtype) { case 0: /* ordinary metafied character */ - rchr = STOUC(*++range) ^ 32; + rchr = (unsigned char) *++range ^ 32; if (!ind) { *chr = rchr; return 1; @@ -4028,9 +4029,9 @@ patmatchindex(char *range, int ind, int *chr, int *mtp) case PP_RANGE: range++; - r1 = STOUC(UNMETA(range)); + r1 = (unsigned char) UNMETA(range); METACHARINC(range); - r2 = STOUC(UNMETA(range)); + r2 = (unsigned char) UNMETA(range); if (*range == Meta) range++; rdiff = r2 - r1; @@ -4050,7 +4051,7 @@ patmatchindex(char *range, int ind, int *chr, int *mtp) } } else { if (!ind) { - *chr = STOUC(*range); + *chr = (unsigned char) *range; return 1; } } diff --git a/Src/prompt.c b/Src/prompt.c index 092de63a4..3cb95039c 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -1666,7 +1666,7 @@ match_colour(const char **teststrp, int is_fg, int colour) tc = TCBGCOLOUR; } if (teststrp) { - if (**teststrp == '#' && isxdigit(STOUC((*teststrp)[1]))) { + if (**teststrp == '#' && isxdigit((unsigned char) (*teststrp)[1])) { struct color_rgb color; char *end; zlong col = zstrtol(*teststrp+1, &end, 16); diff --git a/Src/sort.c b/Src/sort.c index 26949ad9c..ce2b4bbc3 100644 --- a/Src/sort.c +++ b/Src/sort.c @@ -138,7 +138,7 @@ eltpcmp(const void *a, const void *b) int mul = 0; for (; *as == *bs && *as; as++, bs++); #ifndef HAVE_STRCOLL - cmp = (int)STOUC(*as) - (int)STOUC(*bs); + cmp = (int) (unsigned char) *as - (int) (unsigned char) *bs; #endif if (sortnumeric < 0) { if (*as == '-' && idigit(as[1]) && idigit(*bs)) { @@ -159,7 +159,7 @@ eltpcmp(const void *a, const void *b) bs++; for (; idigit(*as) && *as == *bs; as++, bs++); if (idigit(*as) || idigit(*bs)) { - cmp = mul * ((int)STOUC(*as) - (int)STOUC(*bs)); + cmp = mul * ((int) (unsigned char) *as - (int) (unsigned char) *bs); while (idigit(*as) && idigit(*bs)) as++, bs++; if (idigit(*as) && !idigit(*bs)) diff --git a/Src/subst.c b/Src/subst.c index 0f98e6ea3..b8e4023e1 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -556,7 +556,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep, for ( ; *x; x += l) { char c = (l = *x == Meta) ? x[1] ^ 32 : *x; l++; - if (!iwsep(STOUC(c))) + if (!iwsep((unsigned char) c)) break; *ms_flags |= MULTSUB_WS_AT_START; } @@ -573,7 +573,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep, convchar_t c; if (*x == Dash) *x = '-'; - if (itok(STOUC(*x))) { + if (itok((unsigned char) *x)) { /* token, can't be separator, must be single byte */ rawc = *x; l = 1; @@ -582,7 +582,7 @@ multsub(char **s, int pf_flags, char ***a, int *isarr, char *sep, if (!inq && !inp && WC_ZISTYPE(c, ISEP)) { *x = '\0'; for (x += l; *x; x += l) { - if (itok(STOUC(*x))) { + if (itok((unsigned char) *x)) { /* as above */ rawc = *x; l = 1; diff --git a/Src/utils.c b/Src/utils.c index edf5d3df7..32492a93b 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -86,7 +86,7 @@ set_widearray(char *mb_array, Widechar_array wca) while (*mb_array) { int mblen; - if (STOUC(*mb_array) <= 0x7f) { + if ((unsigned char) *mb_array <= 0x7f) { mb_array++; *wcptr++ = (wchar_t)*mb_array; continue; @@ -2920,7 +2920,7 @@ read1char(int echo) restore_queue_signals(q); if (echo) write_loop(SHTTY, &c, 1); - return STOUC(c); + return (unsigned char) c; } /**/ @@ -4123,20 +4123,20 @@ inittyptab(void) #endif /* typtab['.'] |= IIDENT; */ /* Allow '.' in variable names - broken */ typtab['_'] = IIDENT | IUSER; - typtab['-'] = typtab['.'] = typtab[STOUC(Dash)] = IUSER; + typtab['-'] = typtab['.'] = typtab[(unsigned char) Dash] = IUSER; typtab[' '] |= IBLANK | INBLANK; typtab['\t'] |= IBLANK | INBLANK; typtab['\n'] |= INBLANK; typtab['\0'] |= IMETA; - typtab[STOUC(Meta) ] |= IMETA; - typtab[STOUC(Marker)] |= IMETA; - for (t0 = (int)STOUC(Pound); t0 <= (int)STOUC(LAST_NORMAL_TOK); t0++) + typtab[(unsigned char) Meta ] |= IMETA; + typtab[(unsigned char) Marker] |= IMETA; + for (t0 = (int) (unsigned char) Pound; t0 <= (int) (unsigned char) LAST_NORMAL_TOK; t0++) typtab[t0] |= ITOK | IMETA; - for (t0 = (int)STOUC(Snull); t0 <= (int)STOUC(Nularg); t0++) + for (t0 = (int) (unsigned char) Snull; t0 <= (int) (unsigned char) Nularg; t0++) typtab[t0] |= ITOK | IMETA | INULL; for (s = ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ? DEFAULT_IFS_SH : DEFAULT_IFS; *s; s++) { - int c = STOUC(*s == Meta ? *++s ^ 32 : *s); + int c = (unsigned char) (*s == Meta ? *++s ^ 32 : *s); #ifdef MULTIBYTE_SUPPORT if (!isascii(c)) { /* see comment for wordchars below */ @@ -4152,7 +4152,7 @@ inittyptab(void) typtab[c] |= ISEP; } for (s = wordchars ? wordchars : DEFAULT_WORDCHARS; *s; s++) { - int c = STOUC(*s == Meta ? *++s ^ 32 : *s); + int c = (unsigned char) (*s == Meta ? *++s ^ 32 : *s); #ifdef MULTIBYTE_SUPPORT if (!isascii(c)) { /* @@ -4173,16 +4173,16 @@ inittyptab(void) DEFAULT_IFS_SH : DEFAULT_IFS, &ifs_wide); #endif for (s = SPECCHARS; *s; s++) - typtab[STOUC(*s)] |= ISPECIAL; + typtab[(unsigned char) *s] |= ISPECIAL; if (typtab_flags & ZTF_SP_COMMA) - typtab[STOUC(',')] |= ISPECIAL; + typtab[(unsigned char) ','] |= ISPECIAL; if (isset(BANGHIST) && bangchar && (typtab_flags & ZTF_INTERACT)) { typtab_flags |= ZTF_BANGCHAR; typtab[bangchar] |= ISPECIAL; } else typtab_flags &= ~ZTF_BANGCHAR; for (s = PATCHARS; *s; s++) - typtab[STOUC(*s)] |= IPATTERN; + typtab[(unsigned char) *s] |= IPATTERN; unqueue_signals(); } @@ -4193,10 +4193,10 @@ makecommaspecial(int yesno) { if (yesno != 0) { typtab_flags |= ZTF_SP_COMMA; - typtab[STOUC(',')] |= ISPECIAL; + typtab[(unsigned char) ','] |= ISPECIAL; } else { typtab_flags &= ~ZTF_SP_COMMA; - typtab[STOUC(',')] &= ~ISPECIAL; + typtab[(unsigned char) ','] &= ~ISPECIAL; } } @@ -4336,7 +4336,7 @@ itype_end(const char *ptr, int itype, int once) if (wc == WEOF) { /* invalid, treat as single character */ - int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr); + int chr = (unsigned char) (*ptr == Meta ? ptr[1] ^ 32 : *ptr); /* in this case non-ASCII characters can't match */ if (chr > 127 || !zistype(chr,itype)) break; @@ -4375,7 +4375,7 @@ itype_end(const char *ptr, int itype, int once) } else #endif for (;;) { - int chr = STOUC(*ptr == Meta ? ptr[1] ^ 32 : *ptr); + int chr = (unsigned char) (*ptr == Meta ? ptr[1] ^ 32 : *ptr); if (!zistype(chr,itype)) break; ptr += (*ptr == Meta) ? 2 : 1; @@ -4983,11 +4983,11 @@ unmeta_one(const char *in, int *sz) *sz = mb_metacharlenconv_r(in, &wc, &wstate); #else if (in[0] == Meta) { - *sz = 2; - wc = STOUC(in[1] ^ 32); + *sz = 2; + wc = (unsigned char) (in[1] ^ 32); } else { - *sz = 1; - wc = STOUC(in[0]); + *sz = 1; + wc = (unsigned char) in[0]; } #endif return wc; @@ -5022,11 +5022,11 @@ ztrcmp(char const *s1, char const *s2) if(!(c1 = *s1)) c1 = -1; - else if(c1 == STOUC(Meta)) + else if(c1 == (unsigned char) Meta) c1 = *++s1 ^ 32; if(!(c2 = *s2)) c2 = -1; - else if(c2 == STOUC(Meta)) + else if(c2 == (unsigned char) Meta) c2 = *++s2 ^ 32; if(c1 == c2) @@ -5458,7 +5458,7 @@ mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp) const char *ptr; wchar_t wc; - if (STOUC(*s) <= 0x7f) { + if ((unsigned char) *s <= 0x7f) { if (wcp) *wcp = (wint_t)*s; return 1; @@ -5516,10 +5516,10 @@ mb_metacharlenconv_r(const char *s, wint_t *wcp, mbstate_t *mbsp) mod_export int mb_metacharlenconv(const char *s, wint_t *wcp) { - if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) { + if (!isset(MULTIBYTE) || (unsigned char) *s <= 0x7f) { /* treat as single byte, possibly metafied */ if (wcp) - *wcp = (wint_t)STOUC(*s == Meta ? s[1] ^ 32 : *s); + *wcp = (wint_t)(unsigned char) (*s == Meta ? s[1] ^ 32 : *s); return 1 + (*s == Meta); } /* @@ -5581,7 +5581,7 @@ mb_metastrlenend(char *ptr, int width, char *eptr) inchar = *ptr; ptr++; - if (complete && STOUC(inchar) <= STOUC(0x7f)) { + if (complete && (unsigned char) inchar <= (unsigned char) 0x7f) { /* * We rely on 7-bit US-ASCII as a subset, so skip * multibyte handling if we have such a character. @@ -5657,7 +5657,7 @@ mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp) const char *ptr; wchar_t wc; - if (slen && STOUC(*s) <= 0x7f) { + if (slen && (unsigned char) *s <= 0x7f) { if (wcp) *wcp = (wint_t)*s; return 1; @@ -5698,7 +5698,7 @@ mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp) mod_export int mb_charlenconv(const char *s, int slen, wint_t *wcp) { - if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) { + if (!isset(MULTIBYTE) || (unsigned char) *s <= 0x7f) { if (wcp) *wcp = (wint_t)*s; return 1; @@ -5717,7 +5717,7 @@ mod_export int metacharlenconv(const char *x, int *c) { /* - * Here we don't use STOUC() on the chars since they + * Here we don't use an (unsigned char) cast on the chars since they * may be compared against other chars and this will fail * if chars are signed and the high bit is set. */ @@ -5779,7 +5779,7 @@ sb_niceformat(const char *s, FILE *stream, char **outstrp, int flags) eptr = ptr + umlen; while (ptr < eptr) { - int c = STOUC(*ptr); + int c = (unsigned char) *ptr; if (c == '\'' && (flags & NICEFLAG_QUOTE)) { fmt = "\\'"; newl = 2; @@ -5996,9 +5996,9 @@ addunprintable(char *v, const char *u, const char *uend) */ int c; if (*u == Meta) - c = STOUC(*++u ^ 32); + c = (unsigned char) (*++u ^ 32); else - c = STOUC(*u); + c = (unsigned char) *u; switch (c) { case '\0': *v++ = '\\'; @@ -7104,7 +7104,7 @@ getkeystring(char *s, int *len, int how, int *misc) continue; #ifdef MULTIBYTE_SUPPORT } else if ((how & GETKEY_SINGLE_CHAR) && - isset(MULTIBYTE) && STOUC(*s) > 127) { + isset(MULTIBYTE) && (unsigned char) *s > 127) { wint_t wc; int len; len = mb_metacharlenconv(s, &wc); @@ -7207,7 +7207,7 @@ getkeystring(char *s, int *len, int how, int *misc) t = tbuf; } if ((how & GETKEY_SINGLE_CHAR) && t != tmp) { - *misc = STOUC(tmp[0]); + *misc = (unsigned char) tmp[0]; return s + 1; } } diff --git a/Src/zsh.h b/Src/zsh.h index 6f68df6a4..b035a1184 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -135,19 +135,6 @@ struct mathfunc { #define STRMATHFUNC(name, func, id) \ { NULL, name, MFF_STR, NULL, func, NULL, 0, 0, id } -/* Character tokens are sometimes casted to (unsigned char)'s. * - * Unfortunately, some compilers don't correctly cast signed to * - * unsigned promotions; i.e. (int)(unsigned char)((char) -1) evaluates * - * to -1, instead of 255 like it should. We circumvent the troubles * - * of such shameful delinquency by casting to a larger unsigned type * - * then back down to unsigned char. */ - -#ifdef BROKEN_SIGNED_TO_UNSIGNED_CASTING -# define STOUC(X) ((unsigned char)(unsigned short)(X)) -#else -# define STOUC(X) ((unsigned char)(X)) -#endif - /* Meta together with the character following Meta denotes the character * * which is the exclusive or of 32 and the character following Meta. * * This is used to represent characters which otherwise has special * diff --git a/Src/ztype.h b/Src/ztype.h index 5c85b0cd7..8757fc733 100644 --- a/Src/ztype.h +++ b/Src/ztype.h @@ -43,7 +43,7 @@ #define IWSEP (1 << 13) #define INULL (1 << 14) #define IPATTERN (1 << 15) -#define zistype(X,Y) (typtab[STOUC(X)] & Y) +#define zistype(X,Y) (typtab[(unsigned char) (X)] & Y) #define idigit(X) zistype(X,IDIGIT) #define ialnum(X) zistype(X,IALNUM) #define iblank(X) zistype(X,IBLANK) /* blank, not including \n */ diff --git a/configure.ac b/configure.ac index 074141d38..f340d2993 100644 --- a/configure.ac +++ b/configure.ac @@ -582,16 +582,6 @@ if test x$zsh_cv_c_have_union_init = xyes; then AC_DEFINE(HAVE_UNION_INIT) fi -dnl Checking if compiler correctly cast signed to unsigned. -AC_CACHE_CHECK(if signed to unsigned casting is broken, -zsh_cv_c_broken_signed_to_unsigned_casting, -[AC_RUN_IFELSE([AC_LANG_SOURCE([[int main(){return((int)(unsigned char)((char) -1) == 255);}]])],[zsh_cv_c_broken_signed_to_unsigned_casting=yes],[zsh_cv_c_broken_signed_to_unsigned_casting=no],[zsh_cv_c_broken_signed_to_unsigned_casting=no])]) -AH_TEMPLATE([BROKEN_SIGNED_TO_UNSIGNED_CASTING], -[Define to 1 if compiler incorrectly cast signed to unsigned.]) -if test x$zsh_cv_c_broken_signed_to_unsigned_casting = xyes; then - AC_DEFINE(BROKEN_SIGNED_TO_UNSIGNED_CASTING) -fi - dnl Checking if the compiler supports variable-length arrays AC_CACHE_CHECK(if the compiler supports variable-length arrays, zsh_cv_c_variable_length_arrays, -- cgit v1.2.3 From 03292bceecba1ca39745f82fe37f321d08b138e4 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 16 Jan 2023 11:10:02 +0000 Subject: 51278: make (i) subscript flag for zero-length string consistent --- ChangeLog | 6 ++++++ Src/params.c | 2 +- Test/D06subscript.ztst | 9 +++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 23baf278b..69ea1ab53 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2023-01-16 Peter Stephenson + + * 51278: Src/params.c, Test/D06subscript.ztst: result of (i) + subscript flag with zero-length string was inconsistent with + other cases. + 2023-01-11 Oliver Kiddle * 51297: Test/X04zlehighlight.ztst: update expected test results diff --git a/Src/params.c b/Src/params.c index 2e4a6eae2..6362b382c 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1669,7 +1669,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, /* Searching characters */ int slen; d = getstrvalue(v); - if (!d || !*d) + if (!d) return 0; /* * beg and len are character counts, not raw offsets. diff --git a/Test/D06subscript.ztst b/Test/D06subscript.ztst index 21127e641..57cdc027c 100644 --- a/Test/D06subscript.ztst +++ b/Test/D06subscript.ztst @@ -299,3 +299,12 @@ F:In math, (($i)) should be the same as ((i)), see workers/47748. echo ${string[(pws:\0:)1]} 0:Word splitting by NUL >foo + + string="a" + print ${string[(i)x]} + string="" + print ${string[(i)x]} +0:Can check off end of zero length string +F:Regression test for inconsistency of failed (i) on zero-length string +>2 +>1 -- cgit v1.2.3 From 511e020c68955f737036b7febd360615517a3637 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 12 Feb 2023 11:21:23 -0800 Subject: 51360: Initial implementation of named references. --- ChangeLog | 4 + Src/Modules/param_private.c | 17 +++- Src/Modules/parameter.c | 4 +- Src/builtin.c | 56 ++++++++++++- Src/params.c | 190 ++++++++++++++++++++++++++++++++++++++++++-- Src/subst.c | 3 +- Src/zsh.h | 8 +- 7 files changed, 266 insertions(+), 16 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 7f467a8ac..aecb1efcf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-02-12 Bart Schaefer + * 51360: Src/Modules/param_private.c, Src/Modules/parameter.c, + Src/builtin.c, Src/params.c, Src/subst.c, Src/zsh.h: Initial + implementation of named references. + * 51404: Src/jobs.c: Nullify filelist after deleting (fix segfault) 2023-02-09 Oliver Kiddle diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index 065fa63d2..70f36ceb1 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -512,9 +512,16 @@ static GetNodeFunc getparamnode; static HashNode getprivatenode(HashTable ht, const char *nam) { - HashNode hn = getparamnode(ht, nam); + /* getparamnode() would follow namerefs, we must not do that here */ + HashNode hn = gethashnode2(ht, nam); Param pm = (Param) hn; + /* autoload has precedence over nameref, so getparamnode() */ + if (pm && (pm->node.flags & PM_AUTOLOAD)) { + hn = getparamnode(ht, nam); + pm = (Param) hn; + /* how would an autoloaded private behave? return here? */ + } while (!fakelevel && pm && locallevel > pm->level && is_private(pm)) { if (!(pm->node.flags & PM_UNSET)) { /* @@ -533,6 +540,12 @@ getprivatenode(HashTable ht, const char *nam) } pm = pm->old; } + + /* resolve nameref after skipping private parameters */ + if (pm && (pm->node.flags & PM_NAMEREF) && + (pm->u.str || (pm->node.flags & PM_UNSET))) + pm = (Param) resolve_nameref(pm, NULL); + return (HashNode)pm; } @@ -571,7 +584,7 @@ printprivatenode(HashNode hn, int printflags) static struct builtin bintab[] = { /* Copied from BUILTIN("local"), "P" added */ - BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lmprtux", "P") + BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lnmprtux", "P") }; static struct features module_features = { diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index dbb61e474..5bf675e2a 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -49,13 +49,15 @@ paramtypestr(Param pm) if (pm->node.flags & PM_AUTOLOAD) return dupstring("undefined"); - switch (PM_TYPE(f)) { + /* For simplicity we treat PM_NAMEREF as PM_TYPE(PM_SCALAR) */ + switch (PM_TYPE(f)|(f & PM_NAMEREF)) { case PM_SCALAR: val = "scalar"; break; case PM_ARRAY: val = "array"; break; case PM_INTEGER: val = "integer"; break; case PM_EFLOAT: case PM_FFLOAT: val = "float"; break; case PM_HASHED: val = "association"; break; + case PM_NAMEREF: val = "nameref"; break; } DPUTS(!val, "BUG: type not handled in parameter"); val = dupstring(val); diff --git a/Src/builtin.c b/Src/builtin.c index 4c295d11f..8039b644e 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -55,7 +55,7 @@ static struct builtin builtins[] = BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL), BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL), BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL), - BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmp:%rtuxz", NULL), + BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmnp:%rtuxz", NULL), BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL), BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL), BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL), @@ -88,7 +88,7 @@ static struct builtin builtins[] = BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL), BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL), BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL), - BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lp:%rtux", NULL), + BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lnp:%rtux", NULL), BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL), #if defined(ZSH_MEM) & defined(ZSH_MEM_DEBUG) @@ -121,7 +121,7 @@ static struct builtin builtins[] = BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL), BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL), BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsSw", "v"), - BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmz", NULL), + BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmnz", NULL), BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL), BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL), BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"), @@ -2030,6 +2030,19 @@ typeset_single(char *cname, char *pname, Param pm, int func, int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly, dont_set = 0; char *subscript; + if (pm && (pm->node.flags & PM_NAMEREF) && !((off|on) & PM_NAMEREF)) { + if (!(off & PM_NAMEREF)) + pm = (Param)resolve_nameref(pm, NULL); + if (pm && (pm->node.flags & PM_NAMEREF) && + (on & ~(PM_NAMEREF|PM_LOCAL))) { + /* Changing type of PM_SPECIAL|PM_AUTOLOAD is a fatal error. * + * Should this be a fatal error as well, rather than warning? */ + zwarnnam(cname, "%s: can't change type of a named reference", + pname); + return NULL; + } + } + /* * Do we use the existing pm? Note that this isn't the end of the * story, because if we try and create a new pm at the same @@ -2406,6 +2419,11 @@ typeset_single(char *cname, char *pname, Param pm, int func, return NULL; } } else if ((subscript = strchr(pname, '['))) { + if (on & PM_NAMEREF) { + zerrnam(cname, + "%s: reference variable cannot be an array", pname); + return NULL; + } if (on & PM_READONLY) { zerrnam(cname, "%s: can't create readonly array elements", pname); @@ -2640,6 +2658,14 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) else if (OPT_PLUS(ops,optval)) off |= bit; } + if (OPT_MINUS(ops,'n')) { + if (on|off) { + zwarnnam(name, "no other attributes allowed with -n"); + return 1; + } + on |= PM_NAMEREF; + } else if (OPT_PLUS(ops,'n')) + off |= PM_NAMEREF; roff = off; /* Sanity checks on the options. Remove conflicting options. */ @@ -3022,6 +3048,27 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) } continue; } + + if (on & PM_NAMEREF) { + if (asg->value.scalar && + (strcmp(asg->name, asg->value.scalar) == 0 || + ((pm = (Param)resolve_nameref((Param)hn, asg)) && + (pm->node.flags & PM_NAMEREF)))) { + if (pm->node.flags & PM_SPECIAL) + zwarnnam(name, "%s: invalid reference", pm->node.nam); + else + zwarnnam(name, "%s: invalid self reference", asg->name); + returnval = 1; + continue; + } + if (hn) { + /* namerefs always start over fresh */ + if (((Param)hn)->level >= locallevel) + unsetparam_pm((Param)hn, 0, 1); + hn = NULL; + } + } + if (!typeset_single(name, asg->name, (Param)hn, func, on, off, roff, asg, NULL, ops, 0)) @@ -3805,7 +3852,8 @@ bin_unset(char *name, char **argv, Options ops, int func) returnval = 1; } } else { - if (unsetparam_pm(pm, 0, 1)) + if ((pm = (Param)resolve_nameref(pm, NULL)) && + unsetparam_pm(pm, 0, 1)) returnval = 1; } if (ss) diff --git a/Src/params.c b/Src/params.c index 6362b382c..69b7f484f 100644 --- a/Src/params.c +++ b/Src/params.c @@ -536,6 +536,9 @@ getparamnode(HashTable ht, const char *nam) nam); } } + + if (hn && ht == realparamtab) + hn = resolve_nameref(pm, NULL); return hn; } @@ -993,6 +996,34 @@ createparam(char *name, int flags) gethashnode2(paramtab, name) : paramtab->getnode(paramtab, name)); + if (oldpm && (oldpm->node.flags & PM_NAMEREF) && + !(flags & PM_NAMEREF)) { + Param lastpm; + struct asgment stop; + stop.flags = PM_NAMEREF | (flags & PM_LOCAL); + stop.name = oldpm->node.nam; + stop.value.scalar = oldpm->u.str; + lastpm = (Param)resolve_nameref(oldpm, &stop); + if (lastpm) { + if (lastpm->node.flags & PM_NAMEREF) { + if (lastpm->u.str && *(lastpm->u.str)) { + name = lastpm->u.str; + oldpm = NULL; + } else { + if (!(lastpm->node.flags & PM_READONLY)) + lastpm->node.flags |= PM_UNSET; + return lastpm; + } + } else { + /* nameref pointing to an unset local */ + DPUTS(!(lastpm->node.flags & PM_UNSET), + "BUG: local parameter is not unset"); + oldpm = lastpm; + } + } else + flags |= PM_NAMEREF; + } + DPUTS(oldpm && oldpm->level > locallevel, "BUG: old local parameter not deleted"); if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) { @@ -2109,6 +2140,23 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) memset(v, 0, sizeof(*v)); else v = (Value) hcalloc(sizeof *v); + if ((pm->node.flags & PM_NAMEREF) && pm->u.str && *(pm->u.str)) { + /* only happens for namerefs pointing to array elements */ + char *ref = dupstring(pm->u.str); + char *ss = pm->width ? ref + pm->width : NULL; + if (ss) { + sav = *ss; + *ss = 0; + } + Param p1 = (Param)gethashnode2(paramtab, ref); + if (!(p1 && (pm = upscope(p1, pm->base))) || + ((pm->node.flags & PM_UNSET) && + !(pm->node.flags & PM_DECLARED))) + return NULL; + if (ss) + *ss = sav; + s = ss; + } if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) { /* Overload v->isarr as the flag bits for hashed arrays. */ v->isarr = flags | (isvarat ? SCANPM_ISVAR_AT : 0); @@ -2677,6 +2725,7 @@ assignstrvalue(Value v, char *val, int flags) } break; } + setscope(v->pm); if ((!v->pm->env && !(v->pm->node.flags & PM_EXPORTED) && !(isset(ALLEXPORT) && !(v->pm->node.flags & PM_HASHELEM))) || (v->pm->node.flags & PM_ARRAY) || v->pm->ename) @@ -3084,11 +3133,20 @@ assignsparam(char *s, char *val, int flags) } } if (!v && !(v = getvalue(&vbuf, &t, 1))) { - unqueue_signals(); zsfree(val); + unqueue_signals(); /* errflag |= ERRFLAG_ERROR; */ return NULL; } + if (*val && (v->pm->node.flags & PM_NAMEREF)) { + if (!valid_refname(val)) { + zerr("invalid variable name: %s", val); + zsfree(val); + unqueue_signals(); + errflag |= ERRFLAG_ERROR; + return NULL; + } + } if (flags & ASSPM_WARN) check_warn_pm(v->pm, "scalar", created, 1); v->pm->node.flags &= ~PM_DEFAULTED; @@ -3115,8 +3173,8 @@ assignsparam(char *s, char *val, int flags) lhs.u.l = lhs.u.l + (zlong)rhs.u.d; } setnumvalue(v, lhs); - unqueue_signals(); zsfree(val); + unqueue_signals(); return v->pm; /* avoid later setstrvalue() call */ case PM_ARRAY: if (unset(KSHARRAYS)) { @@ -3141,9 +3199,9 @@ assignsparam(char *s, char *val, int flags) case PM_INTEGER: case PM_EFLOAT: case PM_FFLOAT: + zsfree(val); unqueue_signals(); zerr("attempt to add to slice of a numeric variable"); - zsfree(val); return NULL; case PM_ARRAY: kshappend: @@ -3602,7 +3660,8 @@ unsetparam(char *s) if ((pm = (Param) (paramtab == realparamtab ? /* getnode2() to avoid autoloading */ paramtab->getnode2(paramtab, s) : - paramtab->getnode(paramtab, s)))) + paramtab->getnode(paramtab, s))) && + !(pm->node.flags & PM_NAMEREF)) unsetparam_pm(pm, 0, 1); unqueue_signals(); } @@ -5783,7 +5842,8 @@ static const struct paramtypes pmtypes[] = { { PM_TAGGED, "tagged", 't', 0}, { PM_EXPORTED, "exported", 'x', 0}, { PM_UNIQUE, "unique", 'U', 0}, - { PM_TIED, "tied", 'T', 0} + { PM_TIED, "tied", 'T', 0}, + { PM_NAMEREF, "namref", 'n', 0} }; #define PMTYPES_SIZE ((int)(sizeof(pmtypes)/sizeof(struct paramtypes))) @@ -6037,3 +6097,123 @@ printparamnode(HashNode hn, int printflags) else if (!(printflags & PRINT_KV_PAIR)) putchar('\n'); } + +/**/ +mod_export HashNode +resolve_nameref(Param pm, const Asgment stop) +{ + HashNode hn = (HashNode)pm; + const char *seek = stop ? stop->value.scalar : NULL; + + if (pm && (pm->node.flags & PM_NAMEREF)) { + 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) + return NULL; + */ + return (HashNode) pm; + } else if (pm->u.str) { + if ((pm->node.flags & PM_TAGGED) || + (stop && strcmp(pm->u.str, stop->name) == 0)) { + /* zwarnnam(pm->u.str, "invalid self reference"); */ + return stop ? (HashNode)pm : NULL; + } + if (*(pm->u.str)) + seek = pm->u.str; + } + } + else if (pm && !(stop && (stop->flags & PM_NAMEREF))) + return (HashNode)pm; + if (seek) { + queue_signals(); + /* pm->width is the offset of any subscript */ + if (pm && (pm->node.flags & PM_NAMEREF) && pm->width) { + if (stop) { + if (stop->flags & PM_NAMEREF) + hn = (HashNode)pm; + else + hn = NULL; + } else { + /* this has to be the end of any chain */ + hn = (HashNode)pm; /* see fetchvalue() */ + } + } else if ((hn = gethashnode2(realparamtab, seek))) { + if (pm) { + if (!(stop && (stop->flags & (PM_LOCAL)))) + hn = (HashNode)upscope((Param)hn, + ((pm->node.flags & PM_NAMEREF) ? + pm->base : ((Param)hn)->level)); + /* user can't tag a nameref, safe for loop detection */ + pm->node.flags |= PM_TAGGED; + } + if (hn) { + if (hn->flags & PM_AUTOLOAD) + hn = getparamnode(realparamtab, seek); + if (!(hn->flags & PM_UNSET)) + hn = resolve_nameref((Param)hn, stop); + } + if (pm) + pm->node.flags &= ~PM_TAGGED; + } else if (stop && (stop->flags & PM_NAMEREF)) + hn = (HashNode)pm; + unqueue_signals(); + } + + return hn; +} + +/**/ +static void +setscope(Param pm) +{ + if (pm->node.flags & PM_NAMEREF) { + Param basepm; + char *t = pm->u.str ? itype_end(pm->u.str, IIDENT, 0) : NULL; + + /* Temporarily change nameref to array parameter itself */ + if (t && *t == '[') + *t = 0; + else + t = 0; + basepm = (Param)resolve_nameref(pm, NULL); + if (t) { + pm->width = t - pm->u.str; + *t = '['; + } + if (basepm) + pm->base = ((basepm->node.flags & PM_NAMEREF) ? + basepm->base : basepm->level); + } +} + +/**/ +mod_export Param +upscope(Param pm, int reflevel) +{ + Param up = pm->old; + while (pm && up && up->level >= reflevel) { + pm = up; + if (up) + up = up->old; + } + return pm; +} + +/**/ +mod_export int +valid_refname(char *val) +{ + char *t = itype_end(val, IIDENT, 0); + + if (*t != 0) { + if (*t == '[') { + tokenize(t = dupstring(t+1)); + t = parse_subscript(t, 0, ']'); + } else { + t = NULL; + } + } + return !!t; +} diff --git a/Src/subst.c b/Src/subst.c index 3dd920e87..05bfcc03b 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -2573,13 +2573,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, !(v->pm->node.flags & PM_UNSET))) { int f = v->pm->node.flags; - switch (PM_TYPE(f)) { + switch (PM_TYPE(f)|(f & PM_NAMEREF)) { case PM_SCALAR: val = "scalar"; break; case PM_ARRAY: val = "array"; break; case PM_INTEGER: val = "integer"; break; case PM_EFLOAT: case PM_FFLOAT: val = "float"; break; case PM_HASHED: val = "association"; break; + case PM_NAMEREF: val = "nameref"; break; } val = dupstring(val); if (v->pm->level) diff --git a/Src/zsh.h b/Src/zsh.h index f82e76e4b..1e35bd33e 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1852,8 +1852,9 @@ struct param { GsuHash h; } gsu; - int base; /* output base or floating point prec */ - int width; /* field width */ + int base; /* output base or floating point prec or */ + /* for namerefs, locallevel of reference */ + int width; /* field width or nameref subscript idx */ char *env; /* location in environment, if exported */ char *ename; /* name of corresponding environment var */ Param old; /* old struct for use with local */ @@ -1932,9 +1933,10 @@ struct tieddata { */ #define PM_HASHELEM (1<<28) /* is a hash-element */ #define PM_NAMEDDIR (1<<29) /* has a corresponding nameddirtab entry */ +#define PM_NAMEREF (1<<30) /* pointer to a different parameter */ /* The option string corresponds to the first of the variables above */ -#define TYPESET_OPTSTR "aiEFALRZlurtxUhHTkz" +#define TYPESET_OPTSTR "aiEFALRZlurtxUhHT" /* These typeset options take an optional numeric argument */ #define TYPESET_OPTNUM "LRZiEF" -- cgit v1.2.3 From 3e55a135c10d3582af22a3e6dc616f57ea212df8 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 12 Feb 2023 11:29:10 -0800 Subject: 51374: Expose named references in $parameters, fix substitution error. --- ChangeLog | 4 ++ Src/Modules/parameter.c | 11 +++-- Src/params.c | 10 ++++- Test/K01nameref.ztst | 105 ++++++++++++++++++++++++++++++++++++++++-------- Test/README | 1 + 5 files changed, 110 insertions(+), 21 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 221b9838d..af448c5e3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-02-12 Bart Schaefer + * 51374: Src/Modules/parameter.c, Src/params.c, Test/README, + Test/K01nameref.ztst: Expose named references in $parameters, + fix substitution error. + * 51362: Doc/Zsh/builtins.yo, Doc/Zsh/expn.yo, Doc/Zsh/params.yo, Doc/Zsh/mod_parameter.yo: Begin documentation for named references. diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index 5bf675e2a..a659300fd 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -105,10 +105,15 @@ getpmparameter(UNUSED(HashTable ht), const char *name) pm->node.nam = dupstring(name); pm->node.flags = PM_SCALAR | PM_READONLY; pm->gsu.s = &nullsetscalar_gsu; - if ((rpm = (Param) realparamtab->getnode(realparamtab, name)) && - !(rpm->node.flags & PM_UNSET)) + if ((rpm = (Param) realparamtab->getnode2(realparamtab, name)) && + !(rpm->node.flags & PM_UNSET)) { pm->u.str = paramtypestr(rpm); - else { + if ((rpm->node.flags & PM_NAMEREF) && + (rpm = (Param) realparamtab->getnode(realparamtab, name)) && + !(rpm->node.flags & PM_UNSET)) { + pm->u.str = zhtricat(pm->u.str, "-", paramtypestr(rpm)); + } + } else { pm->u.str = dupstring(""); pm->node.flags |= (PM_UNSET|PM_SPECIAL); } diff --git a/Src/params.c b/Src/params.c index 69b7f484f..98950d88f 100644 --- a/Src/params.c +++ b/Src/params.c @@ -2155,7 +2155,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) return NULL; if (ss) *ss = sav; - s = ss; + s = dyncat(ss,*pptr); } if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) { /* Overload v->isarr as the flag bits for hashed arrays. */ @@ -6170,6 +6170,7 @@ setscope(Param pm) { if (pm->node.flags & PM_NAMEREF) { Param basepm; + struct asgment stop; char *t = pm->u.str ? itype_end(pm->u.str, IIDENT, 0) : NULL; /* Temporarily change nameref to array parameter itself */ @@ -6177,7 +6178,12 @@ setscope(Param pm) *t = 0; else t = 0; - basepm = (Param)resolve_nameref(pm, NULL); + stop.name = ""; + stop.value.scalar = NULL; + stop.flags = PM_NAMEREF; + if (locallevel) + stop.flags |= PM_LOCAL; + basepm = (Param)resolve_nameref(pm, &stop); if (t) { pm->width = t - pm->u.str; *t = '['; diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index b38831100..a663194a7 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -1,11 +1,11 @@ -# Tests for the zsh/param/private module +# Tests for named references %prep # Required in order to declare an unset hash for substitution test setopt TYPESET_TO_UNSET - : ${ZTST_continue:=1} + : ${ZTST_continue::=1} %test @@ -53,7 +53,13 @@ F:Other type changes are fatal errors, should this also be? typeset var=value typeset -n ptr=var print $ptr -0:basic nameref expansion +0:basic nameref expansion, no braces +>value + + typeset var=value + typeset -n ptr=var + print ${ptr} +0:basic nameref expansion, braces >value typeset var=(val1 val2) @@ -115,9 +121,10 @@ F:Other type changes are fatal errors, should this also be? >typeset -n ptr=var >typeset -a var=( new1 new2 ) - typeset -p ptr1 ptr2 var + typeset -p ptr ptr1 ptr2 var 1:check state of paramtab ONE F:unexpected side-effects of previous tests +*?*no such variable: ptr *?*no such variable: ptr1 *?*no such variable: ptr2 *?*no such variable: var @@ -247,13 +254,24 @@ F:unexpected side-effects of previous tests typeset -n ptr2='path[2]' print -r -- $ptr2 -0d:nameref to array element +0q:nameref to array element, no braces +>${path[2]} + + typeset -n ptr2='path[2]' + print -r -- ${ptr2} +0q:nameref to array element, with braces >${path[2]} typeset -A hash=(x MISS y HIT) typeset -n ptr1='hash[y]' print -r -- $ptr1 -0:nameref to hash element +0:nameref to hash element, no braces +>HIT + + typeset -A hash=(x MISS y HIT) + typeset -n ptr1='hash[y]' + print -r -- ${ptr1} +0:nameref to hash element, with braces >HIT typeset -a ary=(1 2) @@ -362,16 +380,16 @@ F:unexpected side-effects of previous tests >typeset -n ptr=lval >typeset -n ptr=gval + typeset -A var=(myself outside) () { - zmodload -u zsh/parameter - typeset -n myself=parameters[myself] - local -h parameters + typeset -n myself=var[myself] + local -h var print -r -- $myself - typeset -p parameters + typeset -p var } -0:up-reference part 3, autoloading with hidden special ->nameref-local ->typeset parameters +0:up-reference part 3, hidden global +>outside +>typeset var () { typeset notdef @@ -401,7 +419,7 @@ F:unexpected side-effects of previous tests 1:up-reference part 5, stacked namerefs, end not in scope F:What is the correct behavior for the scope of ptr1? >typeset -n ptr1=ptr2 ->typeset -n ptr2='' +>typeset -n ptr2 >ptr1=ptr2 >ptr2=val >ptr1=LOCAL @@ -427,13 +445,68 @@ F:What is the correct behavior for the scope of ptr1? 0:up-reference part 6, stacked namerefs, end is in scope F:Same test, should part 5 output look like this? >typeset -n ptr1=ptr2 ->typeset -n ptr2='' +>typeset -n ptr2 >ptr1=ptr2 ->ptr2='' +>ptr2 >ptr1=val >ptr2= >typeset -n ptr1=ptr2 >typeset -n ptr2='' >typeset ptr2=val + if zmodload zsh/parameter; then + () { + zmodload -u zsh/parameter + typeset -n myself=parameters[myself] + local -h parameters + print -r -- $myself + typeset -p parameters + } + else ZTST_skip='Cannot zmodload zsh/parameter, skipping autoload test' + fi +0:up-reference part 3, autoloading with hidden special +>nameref-local-nameref-local +>typeset parameters + + typeset ptr2=var2 + typeset var2=GLOBAL + () { + typeset -n ptr1=ptr2 + typeset ptr2=var1 + typeset var1=VAR1 + typeset var2=VAR2 + print -r -- ${(P)ptr1} + } +0: +>VAR2 + + ary=(one two three four) + typeset -n ptr=ary + print -r ${(j.:.)ptr//o/0} +0:expansion flags and string replacement +>0ne:tw0:three:f0ur + + var=value + typeset -n ptr=var + myscalar=ptr + echo ${(P)myscalar} +0:named references with (P), as ${(P)name_of_nameref} +>value + + var=value + myscalar=var + typeset -n ptr=myscalar + echo ${(P)ptr} +0:named references with (P), as ${(P)nameref} +>value + + ary=( 'bry[1]' 'bry[2]' ) + bry=( lorem ipsum ) + typeset -n ptr='ary[2]' + print -r -- ${ptr} + print -r -- ${(P)ptr} +0:named references with (P), array element to array element +>bry[2] +>ipsum + %clean diff --git a/Test/README b/Test/README index 670434ac3..b9d393d7c 100644 --- a/Test/README +++ b/Test/README @@ -6,6 +6,7 @@ scripts names: C: shell commands with special syntax D: substititution E: options + K: features adopted from ksh P: privileged (needs super-user privileges) V: modules W: builtin interactive commands and constructs -- cgit v1.2.3 From 3eed6f70cdfea63cfdc380a4df8382fff38af55d Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 12 Feb 2023 11:51:41 -0800 Subject: 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 $_ --- ChangeLog | 5 ++++ Src/builtin.c | 49 +++++++++++++++++++++++-------- Src/loop.c | 6 ++-- Src/params.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- Src/zsh.h | 4 +++ 5 files changed, 129 insertions(+), 27 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 1353de45d..2b2f2a08b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2023-02-12 Bart Schaefer + * 51402: Src/builtin.c, Src/loop.c, Src/params.c, Src/zsh.h: + Add ksh/bash features (unset -n, for ref), readonly refs, + better error checking and messages, code injection safety, + allow references to $! $? $$ $- $0 $_ + * 51375: Doc/Zsh/builtins.yo, Doc/Zsh/expn.yo, Doc/Zsh/params.yo, Doc/Zsh/mod_parameter.yo: Clarify, fix typos, add indexing. diff --git a/Src/builtin.c b/Src/builtin.c index 8039b644e..cf7e9d9fe 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -126,7 +126,7 @@ static struct builtin builtins[] = BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL), BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"), BUILTIN("unhash", 0, bin_unhash, 1, -1, BIN_UNHASH, "adfms", NULL), - BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, BIN_UNSET, "fmv", NULL), + BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, BIN_UNSET, "fmvn", NULL), BUILTIN("unsetopt", 0, bin_setopt, 0, -1, BIN_UNSETOPT, NULL, NULL), BUILTIN("wait", 0, bin_fg, 0, -1, BIN_WAIT, NULL, NULL), BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSwx:", NULL), @@ -2034,11 +2034,16 @@ typeset_single(char *cname, char *pname, Param pm, int func, if (!(off & PM_NAMEREF)) pm = (Param)resolve_nameref(pm, NULL); if (pm && (pm->node.flags & PM_NAMEREF) && - (on & ~(PM_NAMEREF|PM_LOCAL))) { + (on & ~(PM_NAMEREF|PM_LOCAL|PM_READONLY))) { /* Changing type of PM_SPECIAL|PM_AUTOLOAD is a fatal error. * * Should this be a fatal error as well, rather than warning? */ - zwarnnam(cname, "%s: can't change type of a named reference", - pname); + if (pm->width) + zwarnnam(cname, + "%s: can't change type via subscript reference", + pm->u.str); + else + zwarnnam(cname, "%s: can't change type of a named reference", + pname); return NULL; } } @@ -2223,6 +2228,11 @@ typeset_single(char *cname, char *pname, Param pm, int func, zerrnam(cname, "%s: restricted", pname); return pm; } + if ((pm->node.flags & PM_READONLY) && + (pm->node.flags & PM_NAMEREF & off)) { + zerrnam(cname, "%s: read-only reference", pname); + return pm; + } if ((on & PM_UNIQUE) && !(pm->node.flags & PM_READONLY & ~off)) { Param apm; char **x; @@ -2659,7 +2669,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) off |= bit; } if (OPT_MINUS(ops,'n')) { - if (on|off) { + if ((on & ~PM_READONLY)|off) { zwarnnam(name, "no other attributes allowed with -n"); return 1; } @@ -3051,9 +3061,8 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) if (on & PM_NAMEREF) { if (asg->value.scalar && - (strcmp(asg->name, asg->value.scalar) == 0 || - ((pm = (Param)resolve_nameref((Param)hn, asg)) && - (pm->node.flags & PM_NAMEREF)))) { + ((pm = (Param)resolve_nameref((Param)hn, asg)) && + (pm->node.flags & PM_NAMEREF))) { if (pm->node.flags & PM_SPECIAL) zwarnnam(name, "%s: invalid reference", pm->node.nam); else @@ -3063,8 +3072,12 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) } if (hn) { /* namerefs always start over fresh */ - if (((Param)hn)->level >= locallevel) + if (((Param)hn)->level >= locallevel) { + Param oldpm = (Param)hn; + if (!asg->value.scalar && oldpm->u.str) + asg->value.scalar = dupstring(oldpm->u.str); unsetparam_pm((Param)hn, 0, 1); + } hn = NULL; } } @@ -3762,7 +3775,11 @@ bin_unset(char *name, char **argv, Options ops, int func) if ((!(pm->node.flags & PM_RESTRICTED) || unset(RESTRICTED)) && pattry(pprog, pm->node.nam)) { - unsetparam_pm(pm, 0, 1); + if (!OPT_ISSET(ops,'n') && + (pm->node.flags & PM_NAMEREF) && pm->u.str) + unsetparam(pm->u.str); + else + unsetparam_pm(pm, 0, 1); match++; } } @@ -3814,6 +3831,11 @@ bin_unset(char *name, char **argv, Options ops, int func) zerrnam(name, "%s: restricted", pm->node.nam); returnval = 1; } else if (ss) { + if ((pm->node.flags & PM_NAMEREF) && + (!(pm = (Param)resolve_nameref(pm, NULL)) || pm->width)) { + /* warning? */ + continue; + } if (PM_TYPE(pm->node.flags) == PM_HASHED) { HashTable tht = paramtab; if ((paramtab = pm->gsu.h->getfn(pm))) @@ -3852,8 +3874,11 @@ bin_unset(char *name, char **argv, Options ops, int func) returnval = 1; } } else { - if ((pm = (Param)resolve_nameref(pm, NULL)) && - unsetparam_pm(pm, 0, 1)) + if (!OPT_ISSET(ops,'n')) { + if (!(pm = (Param)resolve_nameref(pm, NULL))) + continue; + } + if (unsetparam_pm(pm, 0, 1)) returnval = 1; } if (ss) diff --git a/Src/loop.c b/Src/loop.c index 7df379ecf..0f3847541 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -53,7 +53,7 @@ execfor(Estate state, int do_exec) wordcode code = state->pc[-1]; int iscond = (WC_FOR_TYPE(code) == WC_FOR_COND), ctok = 0, atok = 0; int last = 0; - char *name, *str, *cond = NULL, *advance = NULL; + char *str, *cond = NULL, *advance = NULL; zlong val = 0; LinkList vars = NULL, args = NULL; int old_simple_pline = simple_pline; @@ -151,7 +151,7 @@ execfor(Estate state, int do_exec) int count = 0; for (node = firstnode(vars); node; incnode(node)) { - name = (char *)getdata(node); + char *name = (char *)getdata(node); if (!args || !(str = (char *) ugetnode(args))) { if (count) { @@ -165,7 +165,7 @@ execfor(Estate state, int do_exec) fprintf(xtrerr, "%s=%s\n", name, str); fflush(xtrerr); } - setsparam(name, ztrdup(str)); + setloopvar(name, ztrdup(str)); count++; } if (!count) 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,13 +6165,29 @@ 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(); } return hn; } +/**/ +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; } } diff --git a/Src/zsh.h b/Src/zsh.h index 1e35bd33e..96b4b06bd 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1935,6 +1935,9 @@ struct tieddata { #define PM_NAMEDDIR (1<<29) /* has a corresponding nameddirtab entry */ #define PM_NAMEREF (1<<30) /* pointer to a different parameter */ +#define PM_SELFREF PM_UNIQUE /* Overload when namerefs resolved */ +#define PM_NEWREF PM_SINGLE /* Overload in for-loop namerefs */ + /* The option string corresponds to the first of the variables above */ #define TYPESET_OPTSTR "aiEFALRZlurtxUhHT" @@ -1959,6 +1962,7 @@ struct tieddata { * elements */ #define SCANPM_CHECKING (1<<10) /* Check if set, no need to create */ +#define SCANPM_NOEXEC (1<<11) /* No command substitutions, etc. */ /* "$foo[@]"-style substitution * Only sign bit is significant */ -- cgit v1.2.3 From f4c706f0c84bddb7777d38635c4ba1d43703e2e5 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 12 Feb 2023 12:20:33 -0800 Subject: 51417: Check subscripts in named reference values more rigorously. --- ChangeLog | 3 +++ Src/params.c | 11 ++++++++++- Test/K01nameref.ztst | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 0cc33c7f1..6dc453f7f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-02-12 Bart Schaefer + * 51417: Src/params.c, Test/K01nameref.ztst: Check subscripts + in named reference values more rigorously. + * 51403: Doc/Zsh/builtins.yo, Doc/Zsh/expn.yo, Doc/Zsh/func.yo, Doc/Zsh/grammar.yo, Doc/Zsh/params.yo, Test/K01nameref.ztst: Tests and documentation for 51402, clean up some other tests. diff --git a/Src/params.c b/Src/params.c index 4910d65fe..f61374b87 100644 --- a/Src/params.c +++ b/Src/params.c @@ -6281,7 +6281,16 @@ valid_refname(char *val) if (*t != 0) { if (*t == '[') { tokenize(t = dupstring(t+1)); - t = parse_subscript(t, 0, ']'); + while ((t = parse_subscript(t, 0, ']')) && *t++ == Outbrack) { + if (*t == Inbrack) + ++t; + else + break; + } + if (t && *t) { + /* zwarn("%s: stuff after subscript: %s", val, t); */ + t = NULL; + } } else if (t[1] || !(*t == '!' || *t == '?' || *t == '$' || *t == '-' || *t == '0' || *t == '_')) { diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index 61c2b006a..d2abdd391 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -350,9 +350,9 @@ F:ksh93 does not implement this either >typeset -A hash=( [y]=HIT ) unset -n ptr1 - typeset -n ptr1='not good' + typeset -n ptr1='not[2]good' 1:invalid nameref -*?*invalid variable name: not good +*?*invalid variable name: not\[2\]good unset -n ptr1 unset hash -- cgit v1.2.3 From 03887bb03fbca246fa94b5b5f2266572c0b6d038 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 13 Feb 2023 18:20:11 -0800 Subject: 51430: Misc. problems with typeset and $parameters * Fix and test for regression of assignment when using typeset command * Fix output of typeset +m and $parameters[ref] * Prevent segfault in typeset --- ChangeLog | 7 +++++++ Src/Modules/parameter.c | 2 +- Src/builtin.c | 16 ++++++++++------ Src/params.c | 2 +- Test/K01nameref.ztst | 14 ++++++++++++++ 5 files changed, 33 insertions(+), 8 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 6dc453f7f..be0d8d5e1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2023-02-13 Bart Schaefer + + * 51430: Src/Modules/parameter.c, Src/builtin.c, Src/params.c, + Test/K01nameref.ztst: Fix and test for regression of assignment + when using typeset command, fix output of typeset +m and + $parameters[ref], prevent segfault in typeset. + 2023-02-12 Bart Schaefer * 51417: Src/params.c, Test/K01nameref.ztst: Check subscripts diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index a659300fd..96a211c69 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -108,7 +108,7 @@ getpmparameter(UNUSED(HashTable ht), const char *name) if ((rpm = (Param) realparamtab->getnode2(realparamtab, name)) && !(rpm->node.flags & PM_UNSET)) { pm->u.str = paramtypestr(rpm); - if ((rpm->node.flags & PM_NAMEREF) && + if ((rpm->node.flags & PM_NAMEREF) && rpm->u.str && *(rpm->u.str) && (rpm = (Param) realparamtab->getnode(realparamtab, name)) && !(rpm->node.flags & PM_UNSET)) { pm->u.str = zhtricat(pm->u.str, "-", paramtypestr(rpm)); diff --git a/Src/builtin.c b/Src/builtin.c index cf7e9d9fe..47b337edc 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2262,8 +2262,9 @@ typeset_single(char *cname, char *pname, Param pm, int func, */ if (!(on & PM_READONLY) || !isset(POSIXBUILTINS)) off |= PM_UNSET; - pm->node.flags = (pm->node.flags | - (on & ~PM_READONLY)) & ~off; + if (!OPT_ISSET(ops, 'p')) + pm->node.flags = (pm->node.flags | + (on & ~PM_READONLY)) & ~off; } if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) { if (typeset_setwidth(cname, pm, ops, on, 0)) @@ -3063,12 +3064,15 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) if (asg->value.scalar && ((pm = (Param)resolve_nameref((Param)hn, asg)) && (pm->node.flags & PM_NAMEREF))) { - if (pm->node.flags & PM_SPECIAL) + if (pm->node.flags & PM_SPECIAL) { zwarnnam(name, "%s: invalid reference", pm->node.nam); - else + returnval = 1; + continue; + } else if (pm->u.str && strcmp(pm->u.str, asg->name) == 0) { zwarnnam(name, "%s: invalid self reference", asg->name); - returnval = 1; - continue; + returnval = 1; + continue; + } } if (hn) { /* namerefs always start over fresh */ diff --git a/Src/params.c b/Src/params.c index f61374b87..92cbecf63 100644 --- a/Src/params.c +++ b/Src/params.c @@ -5851,7 +5851,7 @@ static const struct paramtypes pmtypes[] = { { PM_EXPORTED, "exported", 'x', 0}, { PM_UNIQUE, "unique", 'U', 0}, { PM_TIED, "tied", 'T', 0}, - { PM_NAMEREF, "namref", 'n', 0} + { PM_NAMEREF, "nameref", 'n', 0} }; #define PMTYPES_SIZE ((int)(sizeof(pmtypes)/sizeof(struct paramtypes))) diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index d2abdd391..d240e4917 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -25,6 +25,20 @@ 0:assign nameref placeholder >ptr=var + unset ptr + typeset -n ptr + typeset -n ptr=var + typeset -n +0:assign placeholder with new typeset +>ptr=var + + typeset -n ptr1 + typeset -n ptr2=ptr1 + typeset -n +0:chain ending in placeholder +>ptr1 +>ptr2=ptr1 + typeset ptr=var typeset -n ptr typeset -n -- cgit v1.2.3 From 32cceefa95903190829af26723b7ad51c163040c Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 14 Feb 2023 17:54:42 -0800 Subject: 51437: Fix incorrectly-passed test case, masked by unrelated bug. A bug with zmodload when unloading/reloading a static module caused the state of the shell options to change during K01 test. Worked around it. Also changed warnnestedvar messages to look more like other such. --- ChangeLog | 5 +++++ Src/params.c | 5 +++-- Test/K01nameref.ztst | 23 +++++++++++++++++++++-- 3 files changed, 29 insertions(+), 4 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index b490b0ee8..9e7608293 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-02-14 Bart Schaefer + + * 51437: Src/params.c, Test/K01nameref.ztst: Fix incorrectly-passed + test case, masked by unrelated bug. Improve warnnestedvar message. + 2023-02-14 Peter Stephenson * 51425: Src/exec.c, Test/E01OPTIONS: $(<...) shouldn't try to diff --git a/Src/params.c b/Src/params.c index 92cbecf63..e940d7995 100644 --- a/Src/params.c +++ b/Src/params.c @@ -3068,7 +3068,7 @@ check_warn_pm(Param pm, const char *pmtype, int created, } else return; - if (pm->node.flags & PM_SPECIAL) + if (pm->node.flags & (PM_SPECIAL|PM_NAMEREF)) return; for (i = funcstack; i; i = i->prev) { @@ -6181,6 +6181,7 @@ setloopvar(char *name, char *value) if (pm && (pm->node.flags & PM_NAMEREF)) { pm->base = pm->width = 0; pm->u.str = ztrdup(value); + pm->node.flags &= ~PM_UNSET; pm->node.flags |= PM_NEWREF; setscope(pm); pm->node.flags &= ~PM_NEWREF; @@ -6248,7 +6249,7 @@ setscope(Param pm) pm->node.nam); unsetparam_pm(pm, 0, 1); } else if (isset(WARNNESTEDVAR)) - zwarn("%s: global reference to local variable: %s", + zwarn("reference %s in enclosing scope set to local variable %s", pm->node.nam, pm->u.str); } if (pm->u.str && upscope(pm, pm->base) == pm && diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index d240e4917..6a5e767df 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -532,6 +532,13 @@ F:Same test, should part 5 output look like this? >nameref-local-nameref-local >typeset parameters + if [[ $options[typesettounset] != on ]]; then + ZTST_skip='Ignoring zmodload bug that resets TYPESET_TO_UNSET' + setopt typesettounset + fi +0:options reloaded +F:Checking for a bug in zmodload that affects later tests + typeset ptr2=var2 typeset var2=GLOBAL () { @@ -541,7 +548,7 @@ F:Same test, should part 5 output look like this? typeset var2=VAR2 print -r -- ${(P)ptr1} } -0: +0:Order of evaluation with ${(P)...} >VAR2 ary=(one two three four) @@ -666,7 +673,19 @@ F:Same test, should part 5 output look like this? > >scalar-local > -*?*ref: global reference to local variable: one +*?*reference ref*to local variable one + + unset -n ref + typeset -n ref + () { + setopt localoptions warn_nested_var + typeset inner + ref=inner + } + typeset -p ref +0:Global variable is a reference, warning +>typeset -n ref=inner +*?*reference ref*to local variable inner typeset -n ptr='ary[$(echo 2)]' typeset -a ary=(one two three) -- cgit v1.2.3 From 82f307bddfaff0f6e45bf315ff43ea4192529cad Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 21 Feb 2023 12:16:40 +0000 Subject: Fix access to autoloaded parameter. Namerefef resolution needs to happen on the parameter after autoload. --- ChangeLog | 4 ++++ Src/params.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 52491f4b5..0b7d4dad1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-02-21 Peter Stephenson + + * 51466: Src/params.c: fix access to autoloaded parameter. + 2023-02-20 Bart Schaefer * 51431: Src/builtin.c: "typeset -p" shouldn't change parameter flags diff --git a/Src/params.c b/Src/params.c index e940d7995..90302b1b0 100644 --- a/Src/params.c +++ b/Src/params.c @@ -538,7 +538,7 @@ getparamnode(HashTable ht, const char *nam) } if (hn && ht == realparamtab) - hn = resolve_nameref(pm, NULL); + hn = resolve_nameref((Param)hn, NULL); return hn; } -- cgit v1.2.3 From a9ba1662165823a0303a03fdeddb2ce4ca3814e5 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 5 Mar 2023 14:03:42 -0800 Subject: 51483: Enable assignment and expansion of parameters with ksh-like namespace prefixes. --- ChangeLog | 5 +++++ Src/Zle/compcore.c | 4 ++-- Src/Zle/zle_tricky.c | 6 +++--- Src/lex.c | 2 +- Src/params.c | 11 ++++++----- Src/subst.c | 15 +++++++++------ Src/utils.c | 18 ++++++++++++++++-- Src/zsh.h | 2 ++ Src/ztype.h | 1 + 9 files changed, 45 insertions(+), 19 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index e344e8878..38a0395d6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2023-03-05 Bart Schaefer + * 51483: Src/Zle/compcore.c, Src/Zle/zle_tricky.c, Src/lex.c, + Src/params.c, Src/subst.c, Src/utils.c, Src/zsh.h, Src/ztype.h: + Enable assignment and expansion of parameters with ksh-like + namespace prefixes. + * unposted: Src/Modules/param_private.c: coverity memory leak 2023-02-28 Mikael Magnusson diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 64a860fa3..77fce66e8 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -1230,14 +1230,14 @@ check_param(char *s, int set, int test) else if (idigit(*e)) while (idigit(*e)) e++; - else if ((ie = itype_end(e, IIDENT, 0)) != e) { + else if ((ie = itype_end(e, INAMESPC, 0)) != e) { do { e = ie; if (comppatmatch && *comppatmatch && (*e == Star || *e == Quest)) ie = e + 1; else - ie = itype_end(e, IIDENT, 0); + ie = itype_end(e, INAMESPC, 0); } while (ie != e); } diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 07fac7144..690cf6efb 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -576,7 +576,7 @@ parambeg(char *s) while (idigit(*e)) e++; else - e = itype_end(e, IIDENT, 0); + e = itype_end(e, INAMESPC, 0); /* Now make sure that the cursor is inside the name. */ if (offs <= e - s && offs >= b - s && n <= 0) { @@ -765,7 +765,7 @@ docomplete(int lst) else if (idigit(*q)) do q++; while (idigit(*q)); else - q = itype_end(q, IIDENT, 0); + q = itype_end(q, INAMESPC, 0); sav = *q; *q = '\0'; if (zlemetacs - wb == q - s && @@ -1497,7 +1497,7 @@ get_comp_string(void) if (varq) tt = clwords[clwpos]; - s = itype_end(tt, IIDENT, 0); + s = itype_end(tt, INAMESPC, 0); sav = *s; *s = '\0'; zsfree(varname); diff --git a/Src/lex.c b/Src/lex.c index 15da85a93..2f7937410 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -1230,7 +1230,7 @@ gettokstr(int c, int sub) else { int sav = *lexbuf.ptr; *lexbuf.ptr = '\0'; - t = itype_end(t, IIDENT, 0); + t = itype_end(t, INAMESPC, 0); if (t < lexbuf.ptr) { skipparens(Inbrack, Outbrack, &t); } else { diff --git a/Src/params.c b/Src/params.c index 90302b1b0..d3b6a7d43 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1223,7 +1223,7 @@ isident(char *s) break; } else { /* Find the first character in `s' not in the iident type table */ - ss = itype_end(s, IIDENT, 0); + ss = itype_end(s, INAMESPC, 0); } /* If the next character is not [, then it is * @@ -2086,6 +2086,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) char *s, *t, *ie; char sav, c; int ppar = 0; + int itype = (flags & SCANPM_NONAMESPC) ? IIDENT : INAMESPC; s = t = *pptr; @@ -2095,7 +2096,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) else ppar = *s++ - '0'; } - else if ((ie = itype_end(s, IIDENT, 0)) != s) + else if ((ie = itype_end(s, itype, 0)) != s) s = ie; else if (c == Quest) *s++ = '?'; @@ -2183,7 +2184,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) return v; } } else if (!(flags & SCANPM_ASSIGNING) && v->isarr && - itype_end(t, IIDENT, 1) != t && isset(KSHARRAYS)) + itype_end(t, INAMESPC, 1) != t && isset(KSHARRAYS)) v->end = 1, v->isarr = 0; } if (!bracks && *s) @@ -6196,7 +6197,7 @@ setscope(Param pm) if (pm->node.flags & PM_NAMEREF) { Param basepm; struct asgment stop; - char *t = pm->u.str ? itype_end(pm->u.str, IIDENT, 0) : NULL; + char *t = pm->u.str ? itype_end(pm->u.str, INAMESPC, 0) : NULL; /* Temporarily change nameref to array parameter itself */ if (t && *t == '[') @@ -6277,7 +6278,7 @@ upscope(Param pm, int reflevel) mod_export int valid_refname(char *val) { - char *t = itype_end(val, IIDENT, 0); + char *t = itype_end(val, INAMESPC, 0); if (*t != 0) { if (*t == '[') { diff --git a/Src/subst.c b/Src/subst.c index 05bfcc03b..7a4b433bc 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1870,7 +1870,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * these later on, too. */ c = *s; - if (itype_end(s, IIDENT, 1) == s && *s != '#' && c != Pound && + if (itype_end(s, INAMESPC, 1) == s && *s != '#' && c != Pound && !IS_DASH(c) && c != '!' && c != '$' && c != String && c != Qstring && c != '?' && c != Quest && @@ -2332,7 +2332,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, } } else if ((c == '#' || c == Pound) && (inbrace || !isset(POSIXIDENTIFIERS)) && - (itype_end(s+1, IIDENT, 0) != s + 1 + (itype_end(s+1, INAMESPC, 0) != s + 1 || (cc = s[1]) == '*' || cc == Star || cc == '@' || cc == '?' || cc == Quest || cc == '$' || cc == String || cc == Qstring @@ -2369,8 +2369,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * Try to handle this when parameter is named * by (P) (second part of test). */ - if (itype_end(s+1, IIDENT, 0) != s+1 || (aspar && isstring(s[1]) && - (s[2] == Inbrace || s[2] == Inpar))) + if (itype_end(s+1, INAMESPC, 0) != s+1 || + (aspar && isstring(s[1]) && + (s[2] == Inbrace || s[2] == Inpar))) chkset = 1, s++; else if (!inbrace) { /* Special case for `$+' on its own --- leave unmodified */ @@ -2531,6 +2532,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, scanflags |= SCANPM_DQUOTED; if (chkset) scanflags |= SCANPM_CHECKING; + if (!inbrace) + scanflags |= SCANPM_NONAMESPC; /* * Second argument: decide whether to use the subexpression or * the string next on the line as the parameter name. @@ -3211,7 +3214,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, shortest = 0; ++s; } - if (*itype_end(s, IIDENT, 0)) { + if (*itype_end(s, INAMESPC, 0)) { untokenize(s); zerr("not an identifier: %s", s); return NULL; @@ -3271,7 +3274,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, int intersect = (*s == '*' || *s == Star); char **compare, **ap, **apsrc; ++s; - if (*itype_end(s, IIDENT, 0)) { + if (*itype_end(s, INAMESPC, 0)) { untokenize(s); zerr("not an identifier: %s", s); return NULL; diff --git a/Src/utils.c b/Src/utils.c index 55f2d1ab0..1393ecb13 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -3123,7 +3123,7 @@ spckword(char **s, int hist, int cmd, int ask) if (**s == String && !*t) { guess = *s + 1; - if (itype_end(guess, IIDENT, 1) == guess) + if (itype_end(guess, INAMESPC, 1) == guess) return; ic = String; d = 100; @@ -4310,13 +4310,27 @@ wcsitype(wchar_t c, int itype) * If "once" is set, just test the first character, i.e. (outptr != * inptr) tests whether the first character is valid in an identifier. * - * Currently this is only called with itype IIDENT, IUSER or ISEP. + * Currently called only with itype INAMESPC, IIDENT, IUSER or ISEP. */ /**/ mod_export char * itype_end(const char *ptr, int itype, int once) { + if (itype == INAMESPC) { + itype = IIDENT; + if (once == 0 && !isset(POSIXIDENTIFIERS)) { + /* Special case for names containing ".", ksh93 namespaces */ + char *t = itype_end(ptr + (*ptr == '.'), itype, 0); + if (t > ptr+1) { + if (*t == '.') + return itype_end(t+1, itype, 0); + else + return t; + } + } + } + #ifdef MULTIBYTE_SUPPORT if (isset(MULTIBYTE) && (itype != IIDENT || !isset(POSIXIDENTIFIERS))) { diff --git a/Src/zsh.h b/Src/zsh.h index 96b4b06bd..0de1f7afb 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1963,6 +1963,8 @@ struct tieddata { */ #define SCANPM_CHECKING (1<<10) /* Check if set, no need to create */ #define SCANPM_NOEXEC (1<<11) /* No command substitutions, etc. */ +#define SCANPM_NONAMESPC (1<<12) /* namespace syntax not allowed */ + /* "$foo[@]"-style substitution * Only sign bit is significant */ diff --git a/Src/ztype.h b/Src/ztype.h index 8757fc733..4675f73a9 100644 --- a/Src/ztype.h +++ b/Src/ztype.h @@ -43,6 +43,7 @@ #define IWSEP (1 << 13) #define INULL (1 << 14) #define IPATTERN (1 << 15) +#define INAMESPC (1 << 16) #define zistype(X,Y) (typtab[(unsigned char) (X)] & Y) #define idigit(X) zistype(X,IDIGIT) #define ialnum(X) zistype(X,IALNUM) -- cgit v1.2.3 From 4bc1f6e0d2b60976b9c0412d72097ee1c812139b Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 5 Mar 2023 14:06:25 -0800 Subject: 51484: Extend named reference handling for special parameters, improve doc. --- ChangeLog | 3 ++ Doc/Zsh/builtins.yo | 2 + Src/params.c | 108 +++++++++++++++++++++++++++++++--------------------- 3 files changed, 69 insertions(+), 44 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 38a0395d6..30e9850e5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-03-05 Bart Schaefer + * 51484: Src/builtins.yo Src/params.c: Extend named reference + handling for special parameters, improve doc. + * 51483: Src/Zle/compcore.c, Src/Zle/zle_tricky.c, Src/lex.c, Src/params.c, Src/subst.c, Src/utils.c, Src/zsh.h, Src/ztype.h: Enable assignment and expansion of parameters with ksh-like diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 92917c06c..5393cb149 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -1924,6 +1924,8 @@ redef(SPACES)(0)(tt(ifztexi(NOTRANS(@ @ @ @ @ @ @ @ ))ifnztexi( ))) xitem(tt(typeset )[ {tt(PLUS())|tt(-)}tt(AHUaghlmrtux) ] \ [ {tt(PLUS())|tt(-)}tt(EFLRZip) [ var(n) ] ]) xitem(SPACES()[ tt(+) ] [ var(name)[tt(=)var(value)] ... ]) +xitem(tt(typeset )[ {tt(PLUS())|tt(-)}tt(n) ] \ +[ tt(-gr) ] [ var(name)[tt(=)var(value)] ... ]) xitem(tt(typeset )tt(-T) [ {tt(PLUS())|tt(-)}tt(Uglrux) ] [ {tt(PLUS())|tt(-)}tt(LRZp) [ var(n) ] ]) xitem(SPACES()[ tt(+) | var(SCALAR)[tt(=)var(value)] var(array)[tt(=LPAR())var(value) ...tt(RPAR())] [ var(sep) ] ]) item(tt(typeset) tt(-f) [ {tt(PLUS())|tt(-)}tt(TUkmtuz) ] [ tt(+) ] [ var(name) ... ])( diff --git a/Src/params.c b/Src/params.c index d3b6a7d43..c9f4b3017 100644 --- a/Src/params.c +++ b/Src/params.c @@ -475,6 +475,15 @@ static initparam argvparam_pm = IPDEF9("", &pparams, NULL, \ ((V) && (!(V)->pm || ((V)->pm->node.flags & PM_UNSET) || \ !(V)->pm->node.nam || !*(V)->pm->node.nam)) +/* + * For named references. Simple named references are just like scalars + * for efficiency, but special named references need get/set functions. + */ +#define GETREFNAME(PM) (((PM)->node.flags & PM_SPECIAL) ? \ + (PM)->gsu.s->getfn(PM) : (PM)->u.str) +#define SETREFNAME(PM,S) (((PM)->node.flags & PM_SPECIAL) ? \ + (PM)->gsu.s->setfn(PM,(S)) : ((PM)->u.str = (S))) + static Param argvparam; /* "parameter table" - hash table containing the parameters @@ -520,7 +529,7 @@ getparamnode(HashTable ht, const char *nam) HashNode hn = gethashnode2(ht, nam); Param pm = (Param) hn; - if (pm && pm->u.str && (pm->node.flags & PM_AUTOLOAD)) { + if (pm && (pm->node.flags & PM_AUTOLOAD) && pm->u.str) { char *mn = dupstring(pm->u.str); (void)ensurefeature(mn, "p:", (pm->node.flags & PM_AUTOALL) ? NULL : @@ -1002,12 +1011,13 @@ createparam(char *name, int flags) struct asgment stop; stop.flags = PM_NAMEREF | (flags & PM_LOCAL); stop.name = oldpm->node.nam; - stop.value.scalar = oldpm->u.str; + stop.value.scalar = GETREFNAME(oldpm); lastpm = (Param)resolve_nameref(oldpm, &stop); if (lastpm) { if (lastpm->node.flags & PM_NAMEREF) { - if (lastpm->u.str && *(lastpm->u.str)) { - name = lastpm->u.str; + char *refname = GETREFNAME(lastpm); + if (refname && *refname) { + name = refname; oldpm = NULL; } else { if (!(lastpm->node.flags & PM_READONLY)) @@ -2145,25 +2155,28 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) memset(v, 0, sizeof(*v)); else v = (Value) hcalloc(sizeof *v); - if ((pm->node.flags & PM_NAMEREF) && pm->u.str && *(pm->u.str)) { - /* only happens for namerefs pointing to array elements */ - char *ref = dupstring(pm->u.str); - char *ss = pm->width ? ref + pm->width : NULL; - if (ss) { - sav = *ss; - *ss = 0; + if (pm->node.flags & PM_NAMEREF) { + char *refname = GETREFNAME(pm); + if (refname && *refname) { + /* only happens for namerefs pointing to array elements */ + char *ref = dupstring(refname); + char *ss = pm->width ? ref + pm->width : NULL; + if (ss) { + sav = *ss; + *ss = 0; + } + Param p1 = (Param)gethashnode2(paramtab, ref); + if (!(p1 && (pm = upscope(p1, pm->base))) || + ((pm->node.flags & PM_UNSET) && + !(pm->node.flags & PM_DECLARED))) + return NULL; + if (ss) { + flags |= SCANPM_NOEXEC; + *ss = sav; + s = dyncat(ss,*pptr); + } else + s = *pptr; } - Param p1 = (Param)gethashnode2(paramtab, ref); - if (!(p1 && (pm = upscope(p1, pm->base))) || - ((pm->node.flags & PM_UNSET) && - !(pm->node.flags & PM_DECLARED))) - return NULL; - if (ss) { - flags |= SCANPM_NOEXEC; - *ss = sav; - 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. */ @@ -3648,7 +3661,7 @@ mod_export Param setiparam_no_convert(char *s, zlong val) { /* - * If the target is already an integer, thisgets converted + * If the target is already an integer, this gets converted * back. Low technology rules. */ char buf[BDIGBUFSIZE]; @@ -6115,22 +6128,23 @@ resolve_nameref(Param pm, const Asgment stop) const char *seek = stop ? stop->value.scalar : NULL; if (pm && (pm->node.flags & PM_NAMEREF)) { - if (pm && (pm->node.flags & (PM_UNSET|PM_TAGGED))) { + char *refname = GETREFNAME(pm); + if (pm->node.flags & (PM_UNSET|PM_TAGGED)) { /* Semaphore with createparam() */ pm->node.flags &= ~PM_UNSET; if (pm->node.flags & PM_NEWREF) /* See setloopvar() */ return NULL; - if (pm->u.str && *(pm->u.str) && (pm->node.flags & PM_TAGGED)) + if (refname && *refname && (pm->node.flags & PM_TAGGED)) pm->node.flags |= PM_SELFREF; /* See setscope() */ return (HashNode) pm; - } else if (pm->u.str) { + } else if (refname) { if ((pm->node.flags & PM_TAGGED) || - (stop && strcmp(pm->u.str, stop->name) == 0)) { - /* zwarnnam(pm->u.str, "invalid self reference"); */ + (stop && strcmp(refname, stop->name) == 0)) { + /* zwarnnam(refname, "invalid self reference"); */ return stop ? (HashNode)pm : NULL; } - if (*(pm->u.str)) - seek = pm->u.str; + if (*refname) + seek = refname; } } else if (pm && !(stop && (stop->flags & PM_NAMEREF))) @@ -6180,8 +6194,13 @@ setloopvar(char *name, char *value) Param pm = (Param) gethashnode2(realparamtab, name); if (pm && (pm->node.flags & PM_NAMEREF)) { + if (pm->node.flags & PM_READONLY) { + /* Bash error is: "%s: readonly variable" */ + zerr("read-only reference: %s", pm->node.nam); + return; + } pm->base = pm->width = 0; - pm->u.str = ztrdup(value); + SETREFNAME(pm, ztrdup(value)); pm->node.flags &= ~PM_UNSET; pm->node.flags |= PM_NEWREF; setscope(pm); @@ -6197,7 +6216,8 @@ setscope(Param pm) if (pm->node.flags & PM_NAMEREF) { Param basepm; struct asgment stop; - char *t = pm->u.str ? itype_end(pm->u.str, INAMESPC, 0) : NULL; + char *refname = GETREFNAME(pm); + char *t = refname ? itype_end(refname, INAMESPC, 0) : NULL; /* Temporarily change nameref to array parameter itself */ if (t && *t == '[') @@ -6211,7 +6231,7 @@ setscope(Param pm) stop.flags |= PM_LOCAL; basepm = (Param)resolve_nameref(pm, &stop); if (t) { - pm->width = t - pm->u.str; + pm->width = t - refname; *t = '['; } if (basepm) { @@ -6220,23 +6240,23 @@ setscope(Param pm) 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); + zerr("%s: invalid self reference", refname); 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); + if (refname && *refname && + strcmp(pm->node.nam, refname) == 0) { + zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); return; } } - } else if (basepm->u.str) { + } else if ((t = GETREFNAME(basepm))) { if (basepm->base <= basepm->level && - strcmp(pm->node.nam, basepm->u.str) == 0) { - zerr("%s: invalid self reference", pm->u.str); + strcmp(pm->node.nam, t) == 0) { + zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); return; } @@ -6251,11 +6271,11 @@ setscope(Param pm) unsetparam_pm(pm, 0, 1); } else if (isset(WARNNESTEDVAR)) zwarn("reference %s in enclosing scope set to local variable %s", - pm->node.nam, pm->u.str); + pm->node.nam, refname); } - 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); + if (refname && upscope(pm, pm->base) == pm && + strcmp(pm->node.nam, refname) == 0) { + zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); } } -- cgit v1.2.3 From 0562be0af8127bb728774de47e4e8851461bd8e2 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 6 Mar 2023 19:54:48 -0800 Subject: 51509 (+ fix typo): Add ${(!)name} for the referred-to name of a named reference Extend ${!name} in ksh emulation for same --- ChangeLog | 4 ++++ Src/params.c | 7 +++++-- Src/subst.c | 38 +++++++++++++++++++++++++++++++------- Src/zsh.h | 1 + 4 files changed, 41 insertions(+), 9 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 618475dd4..809466c0e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-03-06 Bart Schaefer + * 51509 (+ fix typo): Src/params.c, Src/subst.c, Src/zsh.h: Add + ${(!)name} for the referred-to parameter of a named reference, + and extend ${!name} in ksh emulation for same + * 51524: Src/Modules/ksh93.mdd: dependency on zsh/zle for linkage 2023-03-05 Bart Schaefer diff --git a/Src/params.c b/Src/params.c index c9f4b3017..85eaee609 100644 --- a/Src/params.c +++ b/Src/params.c @@ -2144,7 +2144,10 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) int isvarat; isvarat = (t[0] == '@' && !t[1]); - pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t); + if (flags & SCANPM_NONAMEREF) + pm = (Param) paramtab->getnode2(paramtab, *t == '0' ? "0" : t); + else + pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t); if (sav) *s = sav; *pptr = s; @@ -2155,7 +2158,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) memset(v, 0, sizeof(*v)); else v = (Value) hcalloc(sizeof *v); - if (pm->node.flags & PM_NAMEREF) { + if ((pm->node.flags & PM_NAMEREF) && !(flags & SCANPM_NONAMEREF)) { char *refname = GETREFNAME(pm); if (refname && *refname) { /* only happens for namerefs pointing to array elements */ diff --git a/Src/subst.c b/Src/subst.c index 7a4b433bc..974d6171e 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1818,14 +1818,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * Use for the (k) flag. Goes down into the parameter code, * sometimes. */ - char hkeys = 0; + int hkeys = 0; /* * Used for the (v) flag, ditto. Not quite sure why they're * separate, but the tradition seems to be that things only * get combined when that makes the result more obscure rather * than less. */ - char hvals = 0; + int hvals = 0; /* * Whether we had to evaluate a subexpression, i.e. an * internal ${...} or $(...) or plain $pm. We almost don't @@ -1870,8 +1870,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * these later on, too. */ c = *s; - if (itype_end(s, INAMESPC, 1) == s && *s != '#' && c != Pound && - !IS_DASH(c) && + if (itype_end(s, (c == Inbrace ? INAMESPC : IIDENT), 1) == s && + *s != '#' && c != Pound && !IS_DASH(c) && c != '!' && c != '$' && c != String && c != Qstring && c != '?' && c != Quest && c != '*' && c != Star && c != '@' && c != '{' && @@ -1891,15 +1891,30 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, s++; /* * In ksh emulation a leading `!' is a special flag working - * sort of like our (k). + * sort of like our (k). This is true only for arrays or + * associative arrays and only with subscripts [*] or [@], + * so zsh's implementation is approximate. For namerefs + * in ksh, ${!ref} substitues the parameter name at the + * end of any chain of references, rather than the value. + * * TODO: this is one of very few cases tied directly to * the emulation mode rather than an option. Since ksh * doesn't have parameter flags it might be neater to * handle this with the ^, =, ~ stuff, below. */ if ((c = *s) == '!' && s[1] != Outbrace && EMULATION(EMULATE_KSH)) { - hkeys = SCANPM_WANTKEYS; + hkeys = SCANPM_WANTKEYS|SCANPM_NONAMEREF; s++; + /* There's a slew of other special bash meanings of parameter + * references that start with "!": + * ${!name} == ${(P)name} (when name is not a nameref) + * ${!name*} == ${(k)parameters[(I)name*]} + * ${!name@} == ${(@k)parameters[(I)name*]} + * ${!name[*]} == ${(k)name} (but indexes of ordinary arrays, too) + * ${!name[@]} == ${(@k)name} (ditto, as noted above for ksh) + * + * See also workers/34390, workers/34397, workers/34408. + */ } else if (c == '(' || c == Inpar) { char *t, sav; int tt = 0; @@ -2154,10 +2169,19 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, escapes = 1; break; + case '!': + if ((hkeys|hvals) & ~SCANPM_NONAMEREF) + goto flagerr; + hkeys = SCANPM_NONAMEREF; + break; case 'k': + if (hkeys & ~SCANPM_WANTKEYS) + goto flagerr; hkeys = SCANPM_WANTKEYS; break; case 'v': + if (hvals & ~SCANPM_WANTVALS) + goto flagerr; hvals = SCANPM_WANTVALS; break; @@ -2308,7 +2332,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, /* * Look for special unparenthesised flags. * TODO: could make these able to appear inside parentheses, too, - * i.e. ${(^)...} etc. + * i.e. ${(^)...} etc., but ${(~)...} already has another meaning. */ for (;;) { if ((c = *s) == '^' || c == Hat) { diff --git a/Src/zsh.h b/Src/zsh.h index 0de1f7afb..f3a777045 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1964,6 +1964,7 @@ struct tieddata { #define SCANPM_CHECKING (1<<10) /* Check if set, no need to create */ #define SCANPM_NOEXEC (1<<11) /* No command substitutions, etc. */ #define SCANPM_NONAMESPC (1<<12) /* namespace syntax not allowed */ +#define SCANPM_NONAMEREF (1<<13) /* named references are not followed */ /* "$foo[@]"-style substitution * Only sign bit is significant -- cgit v1.2.3 From 8d009d35a9eeacb1bbe9399316d2649a47102014 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 6 Mar 2023 20:01:04 -0800 Subject: 51510: Skip namespaces in "set"/"typeset" output, add tests, fix bug --- ChangeLog | 5 +++ Src/builtin.c | 14 +++++-- Src/params.c | 4 ++ Src/utils.c | 8 ++-- Src/zsh.h | 1 + Test/K02parameter.ztst | 106 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 Test/K02parameter.ztst (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 809466c0e..96adb9c57 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2023-03-06 Bart Schaefer + * 51510: Src/builtin.c, Src/params.c, Src/utils.c, Src/zsh.h, + Test/K02parameter.ztst: parameters with a leading namespace are + skipped in output of "set" and "typeset", add tests for ksh-like + parameter handling and fix a bug thus revealed + * 51509 (+ fix typo): Src/params.c, Src/subst.c, Src/zsh.h: Add ${(!)name} for the referred-to parameter of a named reference, and extend ${!name} in ksh emulation for same diff --git a/Src/builtin.c b/Src/builtin.c index 11c1ab3a4..d99802f5f 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2240,7 +2240,8 @@ typeset_single(char *cname, char *pname, Param pm, int func, paramtab->printnode(&pm->node, PRINT_TYPESET); else if (!OPT_ISSET(ops,'g') && (unset(TYPESETSILENT) || OPT_ISSET(ops,'m'))) - paramtab->printnode(&pm->node, PRINT_INCLUDEVALUE); + paramtab->printnode(&pm->node, + PRINT_INCLUDEVALUE|PRINT_WITH_NAMESPACE); return pm; } if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) { @@ -2274,7 +2275,7 @@ typeset_single(char *cname, char *pname, Param pm, int func, } } if (OPT_ISSET(ops,'p')) { - paramtab->printnode(&pm->node, PRINT_TYPESET); + paramtab->printnode(&pm->node, PRINT_TYPESET|PRINT_WITH_NAMESPACE); return pm; } if (usepm == 2) /* do not change the PM_UNSET flag */ @@ -2662,7 +2663,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) char *optstr = TYPESET_OPTSTR; int on = 0, off = 0, roff, bit = PM_ARRAY; int i; - int returnval = 0, printflags = 0; + int returnval = 0, printflags = PRINT_WITH_NAMESPACE; int hasargs = *argv != NULL || (assigns && firstnode(assigns)); /* POSIXBUILTINS is set for bash/ksh and both ignore -p with args */ @@ -2730,7 +2731,6 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) queue_signals(); - /* Given no arguments, list whatever the options specify. */ if (OPT_ISSET(ops,'p')) { if (isset(POSIXBUILTINS) && SHELL_EMULATION() != EMULATE_KSH) { @@ -2756,8 +2756,14 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) /* -p0 treated as -p for consistency */ } } + + /* Given no arguments, list whatever the options specify. */ if (!hasargs) { int exclude = 0; + + if (!OPT_ISSET(ops,'m')) + printflags &= ~PRINT_WITH_NAMESPACE; + if (!OPT_ISSET(ops,'p')) { if (!(on|roff)) printflags |= PRINT_TYPE; diff --git a/Src/params.c b/Src/params.c index 85eaee609..021d341e8 100644 --- a/Src/params.c +++ b/Src/params.c @@ -5966,6 +5966,10 @@ printparamnode(HashNode hn, int printflags) Param p = (Param) hn; Param peer = NULL; + if (!(p->node.flags & PM_HASHELEM) && + !(printflags & PRINT_WITH_NAMESPACE) && *(p->node.nam) == '.') + return; + if (p->node.flags & PM_UNSET) { if ((printflags & (PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT) && p->node.flags & (PM_READONLY|PM_EXPORTED)) || diff --git a/Src/utils.c b/Src/utils.c index 8ce9a175d..14ff0ed47 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -4319,13 +4319,13 @@ itype_end(const char *ptr, int itype, int once) { if (itype == INAMESPC) { itype = IIDENT; - if (once == 0 && (!isset(POSIXIDENTIFIERS) || EMULATION(EMULATE_KSH))) { + if (!isset(POSIXIDENTIFIERS) || EMULATION(EMULATE_KSH)) { /* Special case for names containing ".", ksh93 namespaces */ char *t = itype_end(ptr + (*ptr == '.'), itype, 0); - if (t > ptr+1) { + if (t > ptr + (*ptr == '.')) { if (*t == '.') - return itype_end(t+1, itype, 0); - else + ptr = t + 1; /* Fall through */ + else if (!once) return t; } } diff --git a/Src/zsh.h b/Src/zsh.h index f3a777045..a0243e98e 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -2184,6 +2184,7 @@ typedef groupset *Groupset; #define PRINT_LINE (1<<6) #define PRINT_POSIX_EXPORT (1<<7) #define PRINT_POSIX_READONLY (1<<8) +#define PRINT_WITH_NAMESPACE (1<<9) /* flags for printing for the whence builtin */ #define PRINT_WHENCE_CSH (1<<7) diff --git a/Test/K02parameter.ztst b/Test/K02parameter.ztst new file mode 100644 index 000000000..8a1be1e36 --- /dev/null +++ b/Test/K02parameter.ztst @@ -0,0 +1,106 @@ +# Test parameter expansion with namespace syntax +# (heavily borrowed from D04parameter.ztst) + +%prep + +%test + + .k02.foo='the first parameter' + .k02.bar='the second parameter' + print -l $.k02.foo ${.k02.bar} +0:Basic scalars with namespace +F:Braces are required +>$.k02.foo +>the second parameter + + typeset .k02.bar='the second parameter' + print -l ${.k02.bar} +0:Scalar but with typeset +>the second parameter + + .k02.array1=(the first array) + .k02.array2=(the second array) + print -l $.k02.array1 ${.k02.array2} +0:Basic arrays with namespace +>$.k02.array1 +>the +>second +>array + + typeset -a .k02.array2=(the second array) + print -l ${.k02.array2} +0:Array but with typeset +>the +>second +>array + + setopt ksharrays + print -l ${.k02.array2} + unsetopt ksharrays +0:Basic ksharray with namespace +>the + + setopt shwordsplit + print -l ${.k02.foo} ${==.k02.bar} + unsetopt shwordsplit +0:Basic shwordsplit with namespace +>the +>first +>parameter +>the second parameter + + print ${+.k02.foo} ${+.k02.notappearinginthistest} +0:$+... and namespace +>1 0 + + .k02.x=() + print ${+.k02.x} ${+.k02.x[1]} ${+.k02.x[(r)foo]} ${+.k02.x[(r)bar]} + .k02.x=(foo) + print ${+.k02.x} ${+.k02.x[1]} ${+.k02.x[(r)foo]} ${+.k02.x[(r)bar]} +0:$+... with arrays and namespace +>1 0 0 0 +>1 1 1 0 + + # See D04 for complete explanation. + # For K02 we're just testing that flag syntax works. + .k02.foo=' {six} (seven) >eight< }nine{ |forty-two| $many$ )ten( more' + .k02.array=(${(z).k02.foo}) + print -l ${(Q).k02.array} +0:${(z)...} and ${(Q)...} for some hard to parse cases +>< +>five +>> +>{six} +>( +>seven +>) +>> +>eight +>< +>}nine{ +>| +>forty-two +>| +>$many$ +>) +>ten( more + + .k02.array=(characters in an array) + print ${(c)#.k02.array} +0:${(c)#...} +>22 + + () { + typeset -n .k02.ref=.k02.array + emulate -L ksh + print -l ${!.k02.ref} ${(!).k02.ref} ${.k02.ref} + } +0:namerefs with namespaces +>.k02.array +>.k02.array +>characters + + k.2=test + print ${k.2} +0:Parse without leading dot (future proofing) +>test -- cgit v1.2.3 From 4345eed1fe5dd6c881b948331cfa8f4a48beda42 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Thu, 22 Jun 2023 13:36:40 -0700 Subject: 51887: namespaces recognized in math, incorrect usages rejected. --- ChangeLog | 3 +++ Src/math.c | 10 +++++++--- Src/params.c | 27 +++++++++++++++++++++++++- Test/K02parameter.ztst | 52 ++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 86 insertions(+), 6 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index e48073e80..0011cc947 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-06-22 Bart Schaefer + * 51887: Src/math.c, Src/params.c, Test/K02parameter.ztst: + namespaces recognized in math, incorrect usages rejected. + * Marlon Richert: 51860: Completion/Base/Completer/_prefix, Test/Y01completion.ztst, Test/comptest: simplify suffix handling in _prefix to remove longstanding and less accurate hack; tests. diff --git a/Src/math.c b/Src/math.c index 12c8d6f6b..a060181ed 100644 --- a/Src/math.c +++ b/Src/math.c @@ -641,7 +641,9 @@ zzlex(void) return MINUSEQ; } if (unary) { - if (idigit(*ptr) || *ptr == '.') { + if (idigit(*ptr) || + (*ptr == '.' && + (idigit(ptr[1]) || !itype_end(ptr, INAMESPC, 0)))) { int ctype = lexconstant(); if (ctype == NUM) { @@ -835,7 +837,9 @@ zzlex(void) case Dnull: break; default: - if (idigit(*--ptr) || *ptr == '.') + if (idigit(*--ptr) || + (*ptr == '.' && + (idigit(ptr[1]) || !itype_end(ptr, INAMESPC, 0)))) return lexconstant(); if (*ptr == '#') { if (*++ptr == '\\' || *ptr == '#') { @@ -857,7 +861,7 @@ zzlex(void) } cct = 1; } - if ((ie = itype_end(ptr, IIDENT, 0)) != ptr) { + if ((ie = itype_end(ptr, INAMESPC, 0)) != ptr) { int func = 0; char *p; diff --git a/Src/params.c b/Src/params.c index 021d341e8..2b0837e03 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1226,6 +1226,26 @@ isident(char *s) if (!*s) /* empty string is definitely not valid */ return 0; + /* This partly duplicates code in itype_end(), but we need to + * distinguish the leading namespace at this point to check the + * correctness of the identifier that follows + */ + if (*s == '.') { + if (idigit(s[1])) + return 0; /* Namespace must not start with a digit */ + /* Reject identifiers beginning with a digit in namespaces. + * Move this out below this block to also reject v.1x form. + */ + if ((ss = itype_end(s + (*s == '.'), IIDENT, 0))) { + if (*ss == '.') { + if (!ss[1]) + return 0; + if (idigit(ss[1])) + s = ss + 1; + } + } + } + if (idigit(*s)) { /* If the first character is `s' is a digit, then all must be */ for (ss = ++s; *ss; ss++) @@ -2148,7 +2168,12 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) pm = (Param) paramtab->getnode2(paramtab, *t == '0' ? "0" : t); else pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t); - if (sav) + if (!pm && *t == '.' && !isident(t)) { + /* badly formed namespace reference */ + if (sav) + *s = sav; + return NULL; + } else if (sav) *s = sav; *pptr = s; if (!pm || ((pm->node.flags & PM_UNSET) && diff --git a/Test/K02parameter.ztst b/Test/K02parameter.ztst index 8a1be1e36..0b1a8dd4a 100644 --- a/Test/K02parameter.ztst +++ b/Test/K02parameter.ztst @@ -100,7 +100,55 @@ F:Braces are required >.k02.array >characters + k.=empty k.2=test - print ${k.2} + print ${k.} ${k.2} 0:Parse without leading dot (future proofing) ->test +>empty test + + .k=OK + print ${.k} +0:Bare namespace is usable (ksh compatibility) +>OK + + .k.=empty +1:Namespace must precede an identifier, assignment +?(eval):1: not an identifier: .k. + + typeset .k.=empty +1:Namespace must precede an identifier, typeset +?(eval):typeset:1: not valid in this context: .k. + + print ${.k.} +1:Namespace must precede an identifier, reference +?(eval):1: bad substitution + + .2.b=not +1:Namespace identifier must not begin with a digit, assignment +?(eval):1: not an identifier: .2.b + + typeset .2.b=not +1:Namespace identifier must not begin with a digit, typeset +?(eval):typeset:1: not valid in this context: .2.b + + print ${.2.b} +1:Namespace identifier must not begin with a digit, reference +?(eval):1: bad substitution + + .not.2b=question +1:Identifier starting with a digit must be all digits, assignment +?(eval):1: not an identifier: .not.2b + + typeset .not.2b=question +1:Identifier starting with a digit must be all digits, typeset +?(eval):typeset:1: not valid in this context: .not.2b + + print ${.not.2b} +1:Identifier starting with a digit must be all digits, reference +?(eval):1: bad substitution + + integer .var.d=0 + float .var.f=.2 + print $((.var.x = ++.var.d - -.var.f)) +0:Namespaces in math context +>1.2 -- cgit v1.2.3 From 1b9bc3441ca0e6d155243084d6e7b98925dc02cb Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Mon, 26 Jun 2023 16:52:40 +0900 Subject: 51884: reset IFS if it contains invalid characters This happens only if MULTIBYTE option is on. --- ChangeLog | 6 ++++++ Doc/Zsh/params.yo | 7 +++++-- Src/params.c | 3 +++ Src/utils.c | 42 ++++++++++++++++++++++++++---------------- Test/D04parameter.ztst | 21 +++++++++++++++++++++ 5 files changed, 61 insertions(+), 18 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 0011cc947..51a091aff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2023-06-26 Jun-ichi Takimoto + + * 51884: Doc/Zsh/params.yo, Src/params.c, Src/utils.c, + Test/D04parameter.ztst: if MULTIBYTE option is on and IFS contains + invalid bytes in curret locale then reset it to default + 2023-06-22 Bart Schaefer * 51887: Src/math.c, Src/params.c, Test/K02parameter.ztst: diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 57d10b8bd..e0410d673 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -1325,15 +1325,18 @@ Internal field separators (by default space, tab, newline and NUL), that are used to separate words which result from command or parameter expansion and words read by the tt(read) builtin. Any characters from the set space, tab and -newline that appear in the IFS are called em(IFS white space). +newline that appear in the tt(IFS) are called em(IFS white space). One or more IFS white space characters or one non-IFS white space character together with any adjacent IFS white space character delimit a field. If an IFS white space character appears twice consecutively -in the IFS, this character is treated as if it were not an IFS white +in the tt(IFS), this character is treated as if it were not an IFS white space character. If the parameter is unset, the default is used. Note this has a different effect from setting the parameter to an empty string. + +If tt(MULTIBYTE) option is on and tt(IFS) contains invalid characters in +the current locale, it is reset to the default. ) vindex(KEYBOARD_HACK) item(tt(KEYBOARD_HACK))( diff --git a/Src/params.c b/Src/params.c index 2b0837e03..f5750a4b4 100644 --- a/Src/params.c +++ b/Src/params.c @@ -4748,6 +4748,7 @@ setlang(char *x) if ((x = getsparam_u(ln->name)) && *x) setlocale(ln->category, x); unqueue_signals(); + inittyptab(); } /**/ @@ -4771,6 +4772,7 @@ lc_allsetfn(Param pm, char *x) else { setlocale(LC_ALL, unmeta(x)); clear_mbstate(); + inittyptab(); } } @@ -4809,6 +4811,7 @@ lcsetfn(Param pm, char *x) } unqueue_signals(); clear_mbstate(); /* LC_CTYPE may have changed */ + inittyptab(); } #endif /* USE_LOCALE */ diff --git a/Src/utils.c b/Src/utils.c index f13e3a79d..94a33453f 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -74,9 +74,6 @@ set_widearray(char *mb_array, Widechar_array wca) } wca->len = 0; - if (!isset(MULTIBYTE)) - return; - if (mb_array) { VARARR(wchar_t, tmpwcs, strlen(mb_array)); wchar_t *wcptr = tmpwcs; @@ -87,8 +84,7 @@ set_widearray(char *mb_array, Widechar_array wca) int mblen; if ((unsigned char) *mb_array <= 0x7f) { - mb_array++; - *wcptr++ = (wchar_t)*mb_array; + *wcptr++ = (wchar_t)*mb_array++; continue; } @@ -4121,8 +4117,9 @@ inittyptab(void) * having IIDENT here is a good idea at all, but this code * should disappear into history... */ - for (t0 = 0240; t0 != 0400; t0++) - typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD; + if isset(MULTIBYTE) + for (t0 = 0240; t0 != 0400; t0++) + typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD; #endif /* typtab['.'] |= IIDENT; */ /* Allow '.' in variable names - broken */ typtab['_'] = IIDENT | IUSER; @@ -4137,11 +4134,24 @@ inittyptab(void) typtab[t0] |= ITOK | IMETA; for (t0 = (int) (unsigned char) Snull; t0 <= (int) (unsigned char) Nularg; t0++) typtab[t0] |= ITOK | IMETA | INULL; - for (s = ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ? - DEFAULT_IFS_SH : DEFAULT_IFS; *s; s++) { + /* ifs */ +#define CURRENT_DEFAULT_IFS (EMULATION(EMULATE_KSH|EMULATE_SH) ? \ + DEFAULT_IFS_SH : DEFAULT_IFS) +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) { + set_widearray(ifs ? ifs : CURRENT_DEFAULT_IFS, &ifs_wide); + if (ifs && !ifs_wide.chars) { + zwarn("IFS has an invalid character; resetting IFS to default"); + zsfree(ifs); + ifs = ztrdup(CURRENT_DEFAULT_IFS); + set_widearray(ifs, &ifs_wide); + } + } +#endif + for (s = ifs ? ifs : CURRENT_DEFAULT_IFS; *s; s++) { int c = (unsigned char) (*s == Meta ? *++s ^ 32 : *s); #ifdef MULTIBYTE_SUPPORT - if (!isascii(c)) { + if (isset(MULTIBYTE) && !isascii(c)) { /* see comment for wordchars below */ continue; } @@ -4154,10 +4164,15 @@ inittyptab(void) } typtab[c] |= ISEP; } + /* wordchars */ +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) + set_widearray(wordchars, &wordchars_wide); +#endif for (s = wordchars ? wordchars : DEFAULT_WORDCHARS; *s; s++) { int c = (unsigned char) (*s == Meta ? *++s ^ 32 : *s); #ifdef MULTIBYTE_SUPPORT - if (!isascii(c)) { + if (isset(MULTIBYTE) && !isascii(c)) { /* * If we have support for multibyte characters, we don't * handle non-ASCII characters here; instead, we turn @@ -4170,11 +4185,6 @@ inittyptab(void) #endif typtab[c] |= IWORD; } -#ifdef MULTIBYTE_SUPPORT - set_widearray(wordchars, &wordchars_wide); - set_widearray(ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ? - DEFAULT_IFS_SH : DEFAULT_IFS, &ifs_wide); -#endif for (s = SPECCHARS; *s; s++) typtab[(unsigned char) *s] |= ISPECIAL; if (typtab_flags & ZTF_SP_COMMA) diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 2fd2f975f..0d44558a7 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -2280,6 +2280,27 @@ F:We do not care what $OLDPWD is, as long as it does not cause an error F:As of this writing, var=$@ and var="$@" with null IFS have unspecified F:behavior, see http://austingroupbugs.net/view.php?id=888 + ( + IFS=$'\x80' + if [[ $IFS = $' \t\n\0' ]]; then + echo OK # if $'\x80' is illegal (e.g. Linux) + else # otherwise (e.g. macOS), it should work as a separator + s=$'foo\x80\bar' + [[ ${${=s}[1]} = foo ]] && echo OK + fi + ) +0D:reset IFS to default if it contains illegal character +>OK + + ( + unsetopt multibyte + IFS=$'\xc3\xa9' + s=$'foo\xc3bar\xa9boo' + echo ${${=s}[2]} + ) +0:eight bit chars in IFS should work if multibute option is off +>bar + () { setopt localoptions extendedglob [[ $- = [[:alnum:]]## ]] || print Failed 1 -- cgit v1.2.3 From baa19d2a85758d6b6bcbcd8b78f065a3be262fb3 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 26 Jul 2023 20:15:21 -0700 Subject: 51945: assorted documentation improvements, bug fixes, and new test 1) Document the behavior of "typeset -n existing_var" (via Jun T. comment) 2) Prohibit "typeset -nm pattern" because, well, it's insane. Add test. 3) Improve doc for ${(!)ref} including ${{t!)ref} (Jun T.) 4) Fix doc for how-to unset of a named ref (Jun T.) 5) Allow "typeset +r -n ref" and "typeset +r +n ref" (Jun T.) 6) Fix "typeset -r -n ref=param" to create readonly references 7) Avoid accidental removal of PM_UNSET flag (Jun T.) and update test 8) Fix "typeset -gn ref=value" and add a test for it 9) Add tests for read-only reference behavior 10) Fix infinite recursion when resolving scope of an unset local named reference, add test. --- ChangeLog | 5 ++++ Doc/Zsh/builtins.yo | 8 +++++- Doc/Zsh/expn.yo | 8 +++++- Doc/Zsh/params.yo | 4 +-- Src/builtin.c | 41 +++++++++++++++++++++++------- Src/params.c | 14 ++++++++--- Test/K01nameref.ztst | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++- 7 files changed, 132 insertions(+), 18 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index d725239f3..f0b6e8c4b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2023-07-26 Bart Schaefer + * 51945: Doc/Zsh/builtins.yo, Doc/Zsh/expn.yo, Doc/Zsh/params.yo, + Src/builtin.c, Src/params.c, Test/K01nameref.ztst: improve named + references documentation, fixes for typeset -r and -g behavior, + fix unset reference behavior including scoping crash, more tests + * Shohei YOSHIDA: 51979: Completion/Linux/Command/_free: Update free completion for procps-ng version 4.0.3 diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 5393cb149..33b13ac16 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -2060,6 +2060,11 @@ function unless `tt(-g -n)' is specified, and any local parameter (of any type) with the same var(name) supplants a named reference from a surrounding scope. +A scalar parameter, including an existing named reference, may be +converted to a new named reference by `tt(typeset -n )var(name)', so +the `tt(-p)' option must be included to display the value of a +specific named reference var(name). + If no attribute flags are given, and either no var(name) arguments are present or the flag tt(+m) is used, then each parameter name printed is preceded by a list of the attributes of that parameter (tt(array), @@ -2104,7 +2109,8 @@ is not used in this case). If the tt(+g) flag is combined with tt(-m), a new local parameter is created for every matching parameter that is not already local. Otherwise -tt(-m) applies all other flags or assignments to the existing parameters. +tt(-m) applies all other flags or assignments to the existing parameters, +except that the tt(-n) option cannot create named references in this way. Except when assignments are made with var(name)tt(=)var(value), using tt(+m) forces the matching parameters and their attributes to be printed, diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 7bc736470..f87832e75 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -987,6 +987,11 @@ means the same thing as the more readable `(tt(%%qqq))'. The following flags are supported: startitem() +item(tt(!))( +When the parameter being expanded is a named reference, the reference +itself is examined and thus is em(not) resolved to its referent. In +ksh emulation, the parens around this flag are optional. +) item(tt(#))( Evaluate the resulting words as numeric expressions and interpret these as character codes. Output the corresponding characters. Note @@ -1245,7 +1250,8 @@ item(tt(hideval))( for parameters with the `hideval' flag (tt(-H)) ) item(tt(nameref))( -for named references having an empty value (tt(-n)) +for named references (tt(typeset -n)) either having an empty value or +when combined with `tt(!)' as in `tt(${LPAR()!t)tt(RPAR()var(rname)})' ) item(tt(special))( for special parameters defined by the shell diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index e0410d673..5653b3bc9 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -672,9 +672,9 @@ of var(pname) in assignments and expansions instead assign to or expand var(rname). This also applies to `tt(unset )var(pname)' and to most subsequent uses of `tt(typeset)' with the exception of `tt(typeset -n)' and `tt(typeset +n)', so to remove a named reference, -use either `tt(unset -n )var(pname)' or one of: +use either `tt(unset -n )var(pname)' (preferred) or one of: ifzman() -example(tt(typeset -n )var(pname) +example(tt(typeset -n )var(pname=) tt(typeset +n )var(pname)) followed by diff --git a/Src/builtin.c b/Src/builtin.c index 1568cf44c..31af66c7c 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2248,10 +2248,14 @@ typeset_single(char *cname, char *pname, Param pm, int func, zerrnam(cname, "%s: restricted", pname); return pm; } - if ((pm->node.flags & PM_READONLY) && - (pm->node.flags & PM_NAMEREF & off)) { - zerrnam(cname, "%s: read-only reference", pname); - return pm; + if ((pm->node.flags & PM_READONLY) && !(off & PM_READONLY) && + /* It seems as though these checks should not be specific to + * PM_NAMEREF, but changing that changes historic behavior */ + ((on & PM_NAMEREF) != (pm->node.flags & PM_NAMEREF) || + (asg && (pm->node.flags & PM_NAMEREF)))) { + zerrnam(cname, "%s: read-only %s", pname, + (pm->node.flags & PM_NAMEREF) ? "reference" : "variable"); + return NULL; } if ((on & PM_UNIQUE) && !(pm->node.flags & PM_READONLY & ~off)) { Param apm; @@ -2693,7 +2697,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) off |= bit; } if (OPT_MINUS(ops,'n')) { - if ((on & ~PM_READONLY)|off) { + if ((on|off) & ~PM_READONLY) { zwarnnam(name, "no other attributes allowed with -n"); return 1; } @@ -3021,6 +3025,13 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) /* With the -m option, treat arguments as glob patterns */ if (OPT_ISSET(ops,'m')) { if (!OPT_ISSET(ops,'p')) { + if (on & PM_NAMEREF) { + /* It's generally unwise to mass-change the types of + * parameters, but for namerefs it would be fatal */ + unqueue_signals(); + zerrnam(name, "invalid reference"); + return 1; + } if (!(on|roff)) printflags |= PRINT_TYPE; if (!on) @@ -3104,13 +3115,25 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) } if (hn) { /* namerefs always start over fresh */ - if (((Param)hn)->level >= locallevel) { + if (((Param)hn)->level >= locallevel || + (!(on & PM_LOCAL) && ((Param)hn)->level < locallevel)) { Param oldpm = (Param)hn; - if (!asg->value.scalar && oldpm->u.str) + if (!asg->value.scalar && + PM_TYPE(oldpm->node.flags) == PM_SCALAR && + oldpm->u.str) asg->value.scalar = dupstring(oldpm->u.str); - unsetparam_pm((Param)hn, 0, 1); + /* Defer read-only error to typeset_single() */ + if (!(hn->flags & PM_READONLY)) + unsetparam_pm(oldpm, 0, 1); } - hn = NULL; + /* Passing a NULL pm to typeset_single() makes the + * nameref read-only before assignment, which breaks + * typeset -rn ref=var + * so this is special-cased to permit that action + * like assign-at-create for other parameter types. + */ + if (!(hn->flags & PM_READONLY)) + hn = NULL; } } diff --git a/Src/params.c b/Src/params.c index f5750a4b4..5841308d7 100644 --- a/Src/params.c +++ b/Src/params.c @@ -546,7 +546,7 @@ getparamnode(HashTable ht, const char *nam) } } - if (hn && ht == realparamtab) + if (hn && ht == realparamtab && !(hn->flags & PM_UNSET)) hn = resolve_nameref((Param)hn, NULL); return hn; } @@ -3729,7 +3729,9 @@ unsetparam_pm(Param pm, int altflag, int exp) char *altremove; if ((pm->node.flags & PM_READONLY) && pm->level <= locallevel) { - zerr("read-only variable: %s", pm->node.nam); + zerr("read-only %s: %s", + (pm->node.flags & PM_NAMEREF) ? "reference" : "variable", + pm->node.nam); return 1; } if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) { @@ -6182,8 +6184,12 @@ resolve_nameref(Param pm, const Asgment stop) seek = refname; } } - else if (pm && !(stop && (stop->flags & PM_NAMEREF))) - return (HashNode)pm; + else if (pm) { + if (!(stop && (stop->flags & PM_NAMEREF))) + return (HashNode)pm; + if (!(pm->node.flags & PM_NAMEREF)) + return (pm->level < locallevel ? NULL : (HashNode)pm); + } if (seek) { queue_signals(); /* pm->width is the offset of any subscript */ diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index 6a5e767df..d8c098a98 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -515,7 +515,7 @@ F:Same test, should part 5 output look like this? >ptr1=val >ptr2= >typeset -n ptr1=ptr2 ->typeset -n ptr2='' +>typeset -n ptr2 >typeset ptr2=val if zmodload zsh/parameter; then @@ -694,4 +694,72 @@ F:Checking for a bug in zmodload that affects later tests F:runs in `setopt noexec` so $(...) returns nothing *?*bad math expression: empty string + unset -n ref + typeset -n ref=GLOBAL + () { + typeset -gn ref=RESET + } + typeset -p ref +0:reset global reference within function +>typeset -n ref=RESET + + unset -n ref + typeset -rn ref=RO + typeset -p ref + (typeset -n ref=RW) + print status: $? expected: 1 + typeset +r -n ref + typeset -p ref + typeset -r +n ref + typeset -p ref + (typeset -rn ref) + print status: $? expected: 1 + typeset +r -n ref=RW # Assignment occurs after type change, + typeset -p ref RO # so RO=RW here. Potentially confusing. + typeset -r -n ref=RX # No type change, so referent changes ... + typeset -p ref RO # ... and previous refererent does not. + typeset +rn ref=RW # Here ref=RW, again type changed first. + typeset -p ref +0:add and remove readonly attribute with references +>typeset -rn ref=RO +*?*: ref: read-only reference +>status: 1 expected: 1 +>typeset -n ref=RO +>typeset -r ref=RO +*?*: ref: read-only variable +>status: 1 expected: 1 +>typeset -n ref=RO +>typeset -g RO=RW +>typeset -rn ref=RX +>typeset -g RO=RW +>typeset ref=RW + + () { + typeset -n r1 r2= + typeset -p r1 r2 + print -- ${(!)r1-unset} + print -- ${+r1} + typeset -p r1 + } +0:unset nameref remains unset when resolved +F:relies on global TYPESET_TO_UNSET in %prep +>typeset -n r1 +>typeset -n r2='' +>unset +>0 +>typeset -n r1 + + bar=xx + typeset -n foo=bar + () { typeset -n foo; foo=zz; foo=zz; print $bar $zz } + () { typeset -n foo; foo=zz; local zz; foo=zz; print $bar $zz } +0:regression: local nameref may not in-scope a global parameter +F:previously this could create an infinite recursion and crash +>xx +>xx zz + + typeset -nm foo=bar +1:create nameref by pattern match not allowed +*?*typeset:1: invalid reference + %clean -- cgit v1.2.3 From 29644f12e742883ec9502205cffb318e446d7ca3 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 1 Oct 2023 11:34:33 -0700 Subject: 52193: handle UTF8-encoded USERNAME and therefore home directory in zcompile Includes one unposted thinko fix ztrdup -> dupstring --- ChangeLog | 7 +++++++ Src/init.c | 4 ++-- Src/params.c | 2 +- Src/parse.c | 12 +++++++++--- Src/utils.c | 2 +- 5 files changed, 20 insertions(+), 7 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 4c0a27878..840f6c5a1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2023-10-01 Bart Schaefer + + * 52193: Src/init.c, Src/params.c, Src/utils.c: metafy USERNAME + (mostly for Cygwin compatibilty with UTF8 encodings) + + * 52193: Src/parse.c: unmetafy file paths in zcompile + 2023-09-27 Jun-ichi Takimoto * 52188: Test/D04parameter.ztst: skip tests that fail if diff --git a/Src/init.c b/Src/init.c index ffb017e22..799ad19f6 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1212,8 +1212,8 @@ setupvals(char *cmd, char *runscript, char *zsh_name) #ifdef USE_GETPWUID if ((pswd = getpwuid(cached_uid))) { if (EMULATION(EMULATE_ZSH)) - home = metafy(pswd->pw_dir, -1, META_DUP); - cached_username = ztrdup(pswd->pw_name); + home = ztrdup_metafy(pswd->pw_dir); + cached_username = ztrdup_metafy(pswd->pw_name); } else #endif /* USE_GETPWUID */ diff --git a/Src/params.c b/Src/params.c index 5841308d7..50e8627d1 100644 --- a/Src/params.c +++ b/Src/params.c @@ -4561,7 +4561,7 @@ usernamesetfn(UNUSED(Param pm), char *x) zwarn("failed to change user ID: %e", errno); else { zsfree(cached_username); - cached_username = ztrdup(pswd->pw_name); + cached_username = ztrdup_metafy(pswd->pw_name); cached_uid = pswd->pw_uid; } } diff --git a/Src/parse.c b/Src/parse.c index a07a6cc71..f7285c2ed 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -3217,12 +3217,14 @@ bin_zcompile(char *nam, char **args, Options ops, UNUSED(int func)) if (!args[1] && !(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a'))) { queue_signals(); - ret = build_dump(nam, dyncat(*args, FD_EXT), args, OPT_ISSET(ops,'U'), + dump = unmetafy(dyncat(*args, FD_EXT), NULL); + ret = build_dump(nam, dump, args, OPT_ISSET(ops,'U'), map, flags); unqueue_signals(); return ret; } - dump = (strsfx(FD_EXT, *args) ? *args : dyncat(*args, FD_EXT)); + dump = (strsfx(FD_EXT, *args) ? dupstring(*args) : dyncat(*args, FD_EXT)); + unmetafy(dump, NULL); queue_signals(); ret = ((OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) ? @@ -3400,6 +3402,7 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) for (hlen = FD_PRELEN, tlen = 0; *files; files++) { struct stat st; + char *fnam; if (check_cond(*files, "k")) { flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_KSHLOAD; @@ -3408,7 +3411,8 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_ZSHLOAD; continue; } - if ((fd = open(*files, O_RDONLY)) < 0 || + fnam = unmeta(*files); + if ((fd = open(fnam, O_RDONLY)) < 0 || fstat(fd, &st) != 0 || !S_ISREG(st.st_mode) || (flen = lseek(fd, 0, 2)) == -1) { if (fd >= 0) @@ -3417,8 +3421,10 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) zwarnnam(nam, "can't open file: %s", *files); noaliases = ona; unlink(dump); + zsfree(fnam); return 1; } + zsfree(fnam); file = (char *) zalloc(flen + 1); file[flen] = '\0'; lseek(fd, 0, 0); diff --git a/Src/utils.c b/Src/utils.c index 7028c155f..790625379 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1069,7 +1069,7 @@ get_username(void) cached_uid = current_uid; zsfree(cached_username); if ((pswd = getpwuid(current_uid))) - cached_username = ztrdup(pswd->pw_name); + cached_username = ztrdup_metafy(pswd->pw_name); else cached_username = ztrdup(""); } -- cgit v1.2.3 From 0f0ba0539e9fdc063c73e3ac6751f77395c193ec Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 1 Oct 2023 13:38:25 -0700 Subject: 52195: cached_username is already metafied when initializing LOGNAME --- ChangeLog | 3 +++ Src/params.c | 7 +++---- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 840f6c5a1..d08adc031 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-10-01 Bart Schaefer + * 52195: Src/params.c: cached_username is already metafied when + initializing LOGNAME + * 52193: Src/init.c, Src/params.c, Src/utils.c: metafy USERNAME (mostly for Cygwin compatibilty with UTF8 encodings) diff --git a/Src/params.c b/Src/params.c index 50e8627d1..957656e3f 100644 --- a/Src/params.c +++ b/Src/params.c @@ -850,12 +850,11 @@ createparamtable(void) setsparam("HOST", ztrdup_metafy(hostnam)); zfree(hostnam, 256); - setsparam("LOGNAME", ztrdup_metafy( + setsparam("LOGNAME", #ifndef DISABLE_DYNAMIC_NSS - (str = getlogin()) && *str ? str : + (str = getlogin()) && *str ? ztrdup_metafy(str) : #endif - cached_username - )); + ztrdup(cached_username)); #if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV) /* Copy the environment variables we are inheriting to dynamic * -- cgit v1.2.3 From 98a6892cb138a53dc4a265e29e60dbbd813f3d73 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Thu, 26 Oct 2023 08:27:18 -0700 Subject: 52244: Fix a batch of minor defects reported by Coverity. Coverity defects 1547831, 1547826 (remove unused function), 1521551, 1500752, 1500747, 1401549, 1372423, 1270645, 1255799, 1255792, 1255789, 1255787, 1255782, 1255750 --- ChangeLog | 9 +++++++++ Src/Modules/zutil.c | 6 +++--- Src/Zle/compcore.c | 5 +++-- Src/Zle/compresult.c | 12 ++++-------- Src/builtin.c | 4 +++- Src/glob.c | 3 ++- Src/hist.c | 15 ++++++++++----- Src/input.c | 12 ------------ Src/params.c | 5 ++--- Src/utils.c | 4 ++-- 10 files changed, 38 insertions(+), 37 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index c3d68a6a2..ebfd2731f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2023-10-25 Bart Schaefer + + * 52244: Src/Modules/zutil.c, Src/Zle/compcore.c, + Src/Zle/compresult.c, Src/builtin.c, Src/glob.c, Src/hist.c, + Src/input.c, Src/params.c, Src/utils.c: Coverity defects 1547831, + 1547826 (remove unused function), 1521551, 1500752, 1500747, + 1401549, 1372423, 1270645, 1255799, 1255792, 1255789, 1255787, + 1255782, 1255750 + 2023-10-24 Matthew Martin * github #103: Christian Heusel: Completion/Unix/Command/_zfs: fix diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 8a7d0a4c5..8b863d5c8 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -1378,11 +1378,11 @@ rmatch(RParseResult *sm, char *subj, char *var1, char *var2, int comp) "zregexparse-guard"), !lastval))) { LinkNode aln; char **mend; - int len; + int len = 0; queue_signals(); - mend = getaparam("mend"); - len = atoi(mend[0]); + if ((mend = getaparam("mend"))) + len = atoi(mend[0]); unqueue_signals(); for (i = len; i; i--) diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 77fce66e8..9b87cad93 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -2249,8 +2249,9 @@ addmatches(Cadata dat, char **argv) llpl = strlen(lpre); llsl = strlen(lsuf); - if (llpl + (int)strlen(compqiprefix) + (int)strlen(lipre) != origlpre - || llsl + (int)strlen(compqisuffix) + (int)strlen(lisuf) != origlsuf) + /* This used to reference compqiprefix and compqisuffix, why? */ + if (llpl + (int)strlen(qipre) + (int)strlen(lipre) != origlpre + || llsl + (int)strlen(qisuf) + (int)strlen(lisuf) != origlsuf) lenchanged = 1; /* Test if there is an existing -P prefix. */ diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index 57789c0f3..cd8c7dd64 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -897,7 +897,7 @@ void do_allmatches(UNUSED(int end)) { int first = 1, nm = nmatches - 1, omc = menucmp, oma = menuacc, e; - Cmatch *mc; + Cmatch *mc = 0; struct menuinfo mi; char *p = (brbeg ? ztrdup(lastbrbeg->str) : NULL); @@ -915,10 +915,10 @@ do_allmatches(UNUSED(int end)) #endif } + if (minfo.group) + mc = (minfo.group)->matches; - mc = (minfo.group)->matches; - - while (1) { + while (mc) { if (!((*mc)->flags & CMF_ALL)) { if (!first) accept_last(); @@ -1731,8 +1731,6 @@ calclist(int showall) width < zterm_columns && nth < g->dcount; nth++, tcol++) { - m = *p; - if (tcol == tcols) { tcol = 0; tlines++; @@ -1994,7 +1992,6 @@ printlist(int over, CLPrintFunc printm, int showall) (listdat.onlyexpl & ((*e)->always > 0 ? 2 : 1)))) { if (pnl) { putc('\n', shout); - pnl = 0; ml++; if (cl >= 0 && --cl <= 1) { cl = -1; @@ -2087,7 +2084,6 @@ printlist(int over, CLPrintFunc printm, int showall) (showall || !(m->flags & (CMF_HIDE|CMF_NOLIST)))) { if (pnl) { putc('\n', shout); - pnl = 0; ml++; if (cl >= 0 && --cl <= 1) { cl = -1; diff --git a/Src/builtin.c b/Src/builtin.c index 31af66c7c..9e08a1dbc 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -6508,6 +6508,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) if (OPT_ISSET(ops,'s') && SHTTY == readfd) { struct ttyinfo ti; + memset(&ti, 0, sizeof(struct ttyinfo)); gettyinfo(&ti); saveti = ti; resettty = 1; @@ -6606,7 +6607,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) else if (resettty && SHTTY != -1) settyinfo(&saveti); if (haso) { - fclose(shout); + if (shout) + fclose(shout); shout = oshout; SHTTY = -1; } diff --git a/Src/glob.c b/Src/glob.c index 63f8a5fa7..bd199ace3 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -1317,7 +1317,8 @@ zglob(LinkList list, LinkNode np, int nountok) sense = 0; if (qualct) { qn = (struct qual *)hcalloc(sizeof *qn); - qo->or = qn; + if (qo) + qo->or = qn; qo = qn; qualorct++; qualct = 0; diff --git a/Src/hist.c b/Src/hist.c index bfbcd6ede..448dfddbc 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -1359,7 +1359,8 @@ putoldhistentryontop(short keep_going) do { if (max_unique_ct-- <= 0 || he == hist_ring) { max_unique_ct = 0; - he = hist_ring->down; + if (hist_ring) + he = hist_ring->down; next = hist_ring; break; } @@ -1367,12 +1368,16 @@ putoldhistentryontop(short keep_going) next = he->down; } while (!(he->node.flags & HIST_DUP)); } - if (he != hist_ring->down) { + /* Is it really possible for hist_ring to be NULL here? */ + if (he && (!hist_ring || he != hist_ring->down)) { he->up->down = he->down; he->down->up = he->up; he->up = hist_ring; - he->down = hist_ring->down; - hist_ring->down = he->down->up = he; + if (hist_ring) { + he->down = hist_ring->down; + hist_ring->down = he; + } + he->down->up = he; } hist_ring = he; } @@ -1468,7 +1473,7 @@ should_ignore_line(Eprog prog) mod_export int hend(Eprog prog) { - int flag, hookret, stack_pos = histsave_stack_pos; + int flag, hookret = 0, stack_pos = histsave_stack_pos; /* * save: * 0: don't save diff --git a/Src/input.c b/Src/input.c index dd8f2edc7..d8ac2c0e7 100644 --- a/Src/input.c +++ b/Src/input.c @@ -643,18 +643,6 @@ zstuff(char **out, const char *fn) return len; } -/**/ -char * -ztuff(const char *fn) -{ - char *buf; - off_t len = zstuff(&buf, fn); - if (len > 0) - return buf; - else - return NULL; -} - /* stuff a whole file into the input queue and print it */ /**/ diff --git a/Src/params.c b/Src/params.c index 957656e3f..9f0cbcd67 100644 --- a/Src/params.c +++ b/Src/params.c @@ -6326,10 +6326,9 @@ mod_export Param upscope(Param pm, int reflevel) { Param up = pm->old; - while (pm && up && up->level >= reflevel) { + while (up && up->level >= reflevel) { pm = up; - if (up) - up = up->old; + up = up->old; } return pm; } diff --git a/Src/utils.c b/Src/utils.c index 790625379..0f66984cd 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -7523,8 +7523,8 @@ restoredir(struct dirsav *d) else if (d->level < 0) err = -1; if (d->dev || d->ino) { - stat(".", &sbuf); - if (sbuf.st_ino != d->ino || sbuf.st_dev != d->dev) + if (stat(".", &sbuf) < 0 || + sbuf.st_ino != d->ino || sbuf.st_dev != d->dev) err = -2; } return err; -- cgit v1.2.3 From 8801665e5b241c3adac9c36b6135d057c5ab2a59 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 3 Feb 2024 12:07:14 -0800 Subject: 52513: fixes and doc for using nofork substitutions with private parameters Also fixes a crash bug with {fd}>&N redirections and private parameters --- ChangeLog | 7 +++ Doc/Zsh/mod_private.yo | 9 +++- Src/Modules/param_private.c | 53 ++++++++++++-------- Src/params.c | 17 +++++-- Test/V10private.ztst | 118 +++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 170 insertions(+), 34 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 46cd45647..fbfae8589 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2024-02-03 Bart Schaefer + + * 52513: Doc/Zsh/mod_private.yo, Src/Modules/param_private.c, + Src/params.c, Test/v10private.ztst: nofork substitutions can + use private parameters; fix crash bug on {privateFD}>&N; add + tests and documentation + 2024-01-28 Bart Schaefer * 52510: Doc/Zsh/expn.yo, Doc/Zsh/mod_private.yo: document how diff --git a/Doc/Zsh/mod_private.yo b/Doc/Zsh/mod_private.yo index 69a5f58be..08ac4cafe 100644 --- a/Doc/Zsh/mod_private.yo +++ b/Doc/Zsh/mod_private.yo @@ -84,9 +84,14 @@ created outside the local scope when it was not previously declared.) itemiz(An exported private remains in the environment of inner scopes but appears unset for the current shell in those scopes. Generally, exporting private parameters should be avoided.) +itemiz(Current shell command substitutions such as `tt(${|)...tt(})', +`tt(${|)var(var)tt(|)...tt(})' and `tt(${ )...tt( })' may read and assign +private parameters from the enclosing function.) itemiz(Declaring a private parameter in a current shell command substitution -such as `tt(${ )...tt( })' limits the parameter to the scope of the command -substitution, just as if the parameter were declared in a function.) +limits that parameter to the scope of the command substitution, just as if +the parameter were declared in a function. This also prevents access by +any enclosed current shell command substitutions, but other substitutions +may use the private parameter because those have the same calling scope.) enditemize() Note that this differs from the static scope defined by compiled languages diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index 7ef6633da..5003d4627 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -298,7 +298,7 @@ pps_setfn(Param pm, char *x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s); GsuScalar gsu = (GsuScalar)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -338,7 +338,7 @@ ppi_setfn(Param pm, zlong x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i); GsuInteger gsu = (GsuInteger)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -378,7 +378,7 @@ ppf_setfn(Param pm, double x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f); GsuFloat gsu = (GsuFloat)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -419,7 +419,7 @@ ppa_setfn(Param pm, char **x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a); GsuArray gsu = (GsuArray)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -461,7 +461,7 @@ pph_setfn(Param pm, HashTable x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h); GsuHash gsu = (GsuHash)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -539,19 +539,20 @@ static struct funcwrap wrapper[] = { WRAPDEF(wrap_private) }; +/**/ +static int private_wraplevel = 0; + /**/ static int wrap_private(Eprog prog, FuncWrap w, char *name) { - static int wraplevel = 0; - - if (wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) { - int owl = wraplevel; - wraplevel = locallevel; + if (private_wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) { + int owl = private_wraplevel; + private_wraplevel = locallevel; scanhashtable(paramtab, 0, 0, 0, scopeprivate, PM_UNSET); runshfunc(prog, w, name); scanhashtable(paramtab, 0, 0, 0, scopeprivate, 0); - wraplevel = owl; + private_wraplevel = owl; return 0; } return 1; @@ -573,22 +574,32 @@ getprivatenode(HashTable ht, const char *nam) pm = (Param) hn; /* how would an autoloaded private behave? return here? */ } - while (!fakelevel && pm && locallevel > pm->level && is_private(pm)) { + while (!fakelevel && pm && is_private(pm) && locallevel > pm->level) { + if (pm->level == private_wraplevel + 1) { + /* Variable is in the current function scope */ + break; + } +#if 0 if (!(pm->node.flags & PM_UNSET)) { /* * private parameters are always marked PM_UNSET before we - * increment locallevel, so the only way we get here is - * when createparam() wants a new parameter that is not at - * the current locallevel and it has therefore cleared the - * PM_UNSET flag. + * increment locallevel, so there are three possible ways + * to get here: + * 1) createparam() wants a new parameter that is not at + * the current locallevel and it has therefore cleared the + * PM_UNSET flag + * 2) locallevel has been incremented (startparamscope()) + * outside the usual function call stack (private_wraplevel) + * 3) dynamic scoping is fetching a value from a surrounding + * scope, we don't know if that's for assign or just expand + * The first of those is now caught in createparam() when + * testing PM_RO_BY_DESIGN and the second occurs only in + * nofork substitution or handling of ZLE specials. If the + * third is an assignment, the GSU setfn rejects it. */ DPUTS(pm->old, "BUG: PM_UNSET cleared in wrong scope"); - setfn_error(pm); - /* - * TODO: instead of throwing an error here, create a global - * parameter, insert as pm->old, handle WARN_CREATE_GLOBAL. - */ } +#endif pm = pm->old; } diff --git a/Src/params.c b/Src/params.c index 9f0cbcd67..a722a20f6 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1049,7 +1049,7 @@ createparam(char *name, int flags) /* POSIXBUILTINS horror: we need to retain 'export' flags */ (isset(POSIXBUILTINS) && (oldpm->node.flags & PM_EXPORTED))) { if (oldpm->node.flags & PM_RO_BY_DESIGN) { - zerr("%s: can't change parameter attribute", + zerr("%s: can't modify read-only parameter", name); return NULL; } @@ -3615,9 +3615,18 @@ assignnparam(char *s, mnumber val, int flags) pm = createparam(t, ss ? PM_ARRAY : isset(POSIXIDENTIFIERS) ? PM_SCALAR : (val.type & MN_INTEGER) ? PM_INTEGER : PM_FFLOAT); - if (!pm) - pm = (Param) paramtab->getnode(paramtab, t); - DPUTS(!pm, "BUG: parameter not created"); + if (errflag) { + /* assume error message already output */ + unqueue_signals(); + return NULL; + } + if (!pm && !(pm = (Param) paramtab->getnode(paramtab, t))) { + DPUTS(!pm, "BUG: parameter not created"); + if (!errflag) + zerr("%s: parameter not found", t); + unqueue_signals(); + return NULL; + } if (ss) { *ss = '['; } else if (val.type & MN_INTEGER) { diff --git a/Test/V10private.ztst b/Test/V10private.ztst index d902cac56..9eeda0f47 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -10,6 +10,8 @@ sed -e 's,# test_zsh_param_private,zmodload zsh/param/private,' < $ZTST_srcdir/B02typeset.ztst > private.TMP/B02 fi + setopt TYPESET_TO_UNSET + %test (zmodload -u zsh/param/private && zmodload zsh/param/private) @@ -246,7 +248,7 @@ F:note "typeset" rather than "private" in output from outer 1:privates are not visible in anonymous functions, part 3 >X top level >array_test not set -?(anon):4: array_test: can't change parameter attribute +?(anon):4: array_test: can't modify read-only parameter F:future revision will create a global with this assignment typeset -a array_test @@ -311,11 +313,30 @@ F:future revision will create a global with this assignment () { typeset -n ptr1=ptr2 - private -n ptr2 + private -n ptr2 # TYPESET_TO_UNSET makes this not a "placeholder" typeset -p ptr1 ptr2 typeset val=LOCAL () { - ptr1=val + ptr1=val # Test dies here as ptr2 is private and unset + typeset -n + printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" + } + typeset -p ptr1 ptr2 + } + typeset -p ptr2 +1:up-reference for private namerefs, end unset and not in scope +F:See K01nameref.ztst up-reference part 5 +F:Here ptr1 finds private ptr2 by scope mismatch +>typeset -n ptr1=ptr2 +*?*read-only variable: ptr2 + + () { + typeset -n ptr1=ptr2 + private -n ptr2= # Assignment makes this a placeholder, not unset + typeset -p ptr1 ptr2 + typeset val=LOCAL + () { + ptr1=val # This is a silent no-op, why? typeset -n printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" } @@ -323,8 +344,9 @@ F:future revision will create a global with this assignment } typeset -p ptr2 1:up-reference for private namerefs, end not in scope -F:See K01typeset.ztst up-reference part 5 -F:Here ptr1 finds private ptr2 by scope mismatch, assignment silently fails +F:See K01nameref.ztst up-reference part 5 +F:Here ptr1 finds private ptr2 by scope mismatch +F:Assignment silently fails, is that correct? >typeset -n ptr1=ptr2 >ptr1=ptr2 >ptr1= @@ -335,11 +357,11 @@ F:Here ptr1 finds private ptr2 by scope mismatch, assignment silently fails typeset ptr2 () { typeset -n ptr1=ptr2 - private -n ptr2 + private -n ptr2 # Set/unset is irrelevant, not referenced typeset -p ptr1 ptr2 typeset val=LOCAL () { - ptr1=val; + ptr1=val typeset -n printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" } @@ -401,6 +423,88 @@ F:Should we allow "public" namerefs to private parameters? 1:private may not change parameter type ?(anon):private:2: can't change type of private param: x + () { + private fd1 fd2 + exec {fd1}>&1 + print OK + () { exec {fd2}>&2 } + print BAD $fd2 + } +1:redirection cannot assign private in wrong scope +F:Better if caught in checkclobberparam() but exec.c doesn't know scope +>OK +?(anon): fd2: can't modify read-only parameter + + () { + private z=outer + print ${(t)z} $z + print ${| + print ${(t)z} $z + REPLY=$z + } + } +0:nofork may read private in calling function +>scalar-local-hide-special outer +>scalar-local-hide-special outer +>outer + + () { + private z=outer + print ${(t)z} $z + print ${| REPLY=${|z| z=nofork} } + print ${(t)z} $z + } +0:nofork may write to private in calling function +>scalar-local-hide-special outer +>nofork +>scalar-local-hide-special nofork + + () { + local q=outer + print ${| + private q=nofork + REPLY=${| REPLY=$q} + } + } +0:nofork cannot see private in surrounding nofork +>outer + + () { + private z=outer + print ${(t)z} $z + print ${|z| + private q + z=${|q| q=nofork} + } + print ${(t)z} $z + } +1:nofork may not change private in surrounding nofork +>scalar-local-hide-special outer +*?*: q: can't modify read-only parameter + + () { + private q=outer + print ${| + () { REPLY="{$q}" } + } + print ${|q| + () { q=nofork } + } + } +1:function may not access private from inside nofork +>{} +*?*: q: can't modify read-only parameter + + () { + print ${| + private q + () { q=nofork } + } + } +1:function may not access private declared in nofork +*?*: q: can't modify read-only parameter + %clean + unsetopt TYPESET_TO_UNSET rm -r private.TMP -- cgit v1.2.3 From 336249e7eae1439a7d96e6aec413af1c78624859 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 18 Feb 2024 12:19:25 -0800 Subject: unposted: referent of named reference cannot start with digits This duplicates ksh behavior and doesn't change useful functionality. --- Src/params.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Src/params.c') diff --git a/Src/params.c b/Src/params.c index a722a20f6..fce3af940 100644 --- a/Src/params.c +++ b/Src/params.c @@ -6348,6 +6348,8 @@ valid_refname(char *val) { char *t = itype_end(val, INAMESPC, 0); + if (idigit(*val)) + return 0; if (*t != 0) { if (*t == '[') { tokenize(t = dupstring(t+1)); -- cgit v1.2.3 From 6b21e5c0e201876f6659b563b56da9a993ae6c03 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 20 Feb 2024 20:16:03 -0800 Subject: 52559: revise "typeset -p" with respect to local readonly special parameters Update doc and tests to describe handling of global readonly specials and to account for side-effects on zsh/param/private. --- ChangeLog | 9 +++++++++ Doc/Zsh/builtins.yo | 8 ++++++++ Doc/Zsh/mod_private.yo | 7 ++++--- Src/Modules/param_private.c | 2 +- Src/params.c | 24 ++++++++++++++++++++++-- Test/B02typeset.ztst | 14 ++++++++++++++ Test/K01nameref.ztst | 4 ++-- Test/V10private.ztst | 13 +++++++++---- 8 files changed, 69 insertions(+), 12 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index e3762340a..090644e8b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2024-02-20 Bart Schaefer + + * 52559: Doc/Zsh/builtins.yo, Doc/Zsh/mod_private.yo, + Src/Modules/param_private.c, Src/params.c, Test/B02typeset.ztst, + Test/K01nameref.ztst, Test/V10private.ztst: revise "typeset -p" + with respect to local readonly special parameters; update doc + and tests to describe handling of global readonly specials and + to account for side-effects on zsh/param/private. + 2024-02-19 Peter Stephenson * 52549: Doc/Zsh/builtins.yo: document that return already works diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 7a8654f27..784089594 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -2136,6 +2136,14 @@ tt(-p) may be followed by an optional integer argument. Currently only the value tt(1) is supported. In this case arrays and associative arrays are printed with newlines between indented elements for readability. + +The names and values of readonly special parameters +(most of the parameters marked `' in +ifzman(zmanref(zshparam))ifnzman(noderef(Parameters Set By The Shell)), +except those documented as settable) +are not printed with `tt(-)tt(p)' because to execute those typeset commands +would cause errors. However, these parameters are printed when they +have been made local to the scope where `tt(typeset -p)' is run. ) item(tt(-T) [ var(scalar)[tt(=)var(value)] var(array)[tt(=LPAR())var(value) ...tt(RPAR())] [ var(sep) ] ])( This flag has a different meaning when used with tt(-f); see below. diff --git a/Doc/Zsh/mod_private.yo b/Doc/Zsh/mod_private.yo index 08ac4cafe..24c099f38 100644 --- a/Doc/Zsh/mod_private.yo +++ b/Doc/Zsh/mod_private.yo @@ -16,9 +16,10 @@ The tt(private) builtin accepts all the same options and arguments as tt(local) (ifzman(zmanref(zshbuiltins))ifnzman(noderef(Shell Builtin Commands))) except for the `tt(-)tt(T)' option. Tied parameters may not be made private. -The `tt(-)tt(p)' option is presently a no-op because the state of -private parameters cannot reliably be reloaded. This also applies -to printing private parameters with `tt(typeset -p)'. +The `tt(-)tt(p)' option is presently disabled because the state of +private parameters cannot reliably be reloaded. When `tt(typeset -)tt(p)' +outputs a private parameter, it is treated as a local with the +`tt(-)tt(h)' (hide) option enabled. If used at the top level (outside a function scope), tt(private) creates a normal parameter in the same manner as tt(declare) or tt(typeset). A diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index 5003d4627..044617190 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -646,7 +646,7 @@ printprivatenode(HashNode hn, int printflags) static struct builtin bintab[] = { /* Copied from BUILTIN("local"), "P" added */ - BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lnmprtux", "P") + BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lnmrtux", "P") }; static struct features module_features = { diff --git a/Src/params.c b/Src/params.c index fce3af940..b329d2079 100644 --- a/Src/params.c +++ b/Src/params.c @@ -5896,6 +5896,7 @@ static const struct paramtypes pmtypes[] = { { PM_ARRAY, "array", 'a', 0}, { PM_HASHED, "association", 'A', 0}, { 0, "local", 0, PMTF_TEST_LEVEL}, + { PM_HIDE, "hide", 'h', 0 }, { PM_LEFT, "left justified", 'L', PMTF_USE_WIDTH}, { PM_RIGHT_B, "right justified", 'R', PMTF_USE_WIDTH}, { PM_RIGHT_Z, "zero filled", 'Z', PMTF_USE_WIDTH}, @@ -6025,13 +6026,21 @@ printparamnode(HashNode hn, int printflags) printflags |= PRINT_NAMEONLY; if (printflags & (PRINT_TYPESET|PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT)) { - if (p->node.flags & (PM_RO_BY_DESIGN|PM_AUTOLOAD)) { + if (p->node.flags & PM_AUTOLOAD) { /* * It's not possible to restore the state of * these, so don't output. */ return; } + if (p->node.flags & PM_RO_BY_DESIGN) { + /* + * Compromise: cannot be restored out of context, + * but show anyway if printed in scope of declaration + */ + if (p->level != locallevel || p->level == 0) + return; + } /* * The zsh variants of export -p/readonly -p also report other * flags to indicate other attributes or scope. The POSIX variants @@ -6064,8 +6073,19 @@ printparamnode(HashNode hn, int printflags) for (pmptr = pmtypes, i = 0; i < PMTYPES_SIZE; i++, pmptr++) { int doprint = 0; if (pmptr->flags & PMTF_TEST_LEVEL) { - if (p->level) + if (p->level) { + /* + if ((p->node.flags & PM_SPECIAL) && + (p->node.flags & PM_LOCAL) && + !(p->node.flags & PM_HIDE)) { + if (doneminus) + putchar(' '); + printf("+h "); + doneminus = 0; + } + */ doprint = 1; + } } else if ((pmptr->binflag != PM_EXPORTED || p->level || (p->node.flags & (PM_LOCAL|PM_ARRAY|PM_HASHED))) && (p->node.flags & pmptr->binflag)) diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index 8b3988151..d90f17d13 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -959,6 +959,20 @@ > [three]='' >) + () { + local -h status + typeset -p status + } +0:parameter hiding preserved by "typeset -p" +>typeset -h status='' + + () { + local status + typeset -p status + } +0:read-only special params are output when localized +>typeset -i10 -r status=0 + (export PATH MANPATH path=(/bin) MANPATH=/ diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index ebb70dd92..ff48e2289 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -464,7 +464,7 @@ F:unexpected side-effects of previous tests } 0:up-reference part 3, hidden global >outside ->typeset var +>typeset -h var () { typeset notdef @@ -541,7 +541,7 @@ F:Same test, should part 5 output look like this? fi 0:up-reference part 3, autoloading with hidden special >nameref-local-nameref-local ->typeset parameters +>typeset -h parameters if [[ $options[typesettounset] != on ]]; then ZTST_skip='Ignoring zmodload bug that resets TYPESET_TO_UNSET' diff --git a/Test/V10private.ztst b/Test/V10private.ztst index 9eeda0f47..4140d4e96 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -28,7 +28,7 @@ print $scalar_test 0:basic scope hiding >toplevel ->local scalar_test +>local hide scalar_test >0 >toplevel @@ -54,7 +54,7 @@ print $+unset_test 0:variable defined only in scope >0 ->local unset_test +>local hide unset_test >setme >0 @@ -70,7 +70,7 @@ } print $array_test 0:nested scope with different type, correctly restored ->local array_test +>local hide array_test >in function >top level @@ -113,7 +113,7 @@ typeset -a hash_test=(top level) typeset -p hash_test inner () { - private -p hash_test + typeset -p hash_test print ${(t)hash_test} ${(kv)hash_test} } outer () { @@ -328,6 +328,7 @@ F:future revision will create a global with this assignment F:See K01nameref.ztst up-reference part 5 F:Here ptr1 finds private ptr2 by scope mismatch >typeset -n ptr1=ptr2 +>typeset -hn ptr2 *?*read-only variable: ptr2 () { @@ -348,10 +349,12 @@ F:See K01nameref.ztst up-reference part 5 F:Here ptr1 finds private ptr2 by scope mismatch F:Assignment silently fails, is that correct? >typeset -n ptr1=ptr2 +>typeset -hn ptr2='' >ptr1=ptr2 >ptr1= >ptr2= >typeset -n ptr1=ptr2 +>typeset -hn ptr2='' *?*no such variable: ptr2 typeset ptr2 @@ -372,11 +375,13 @@ F:Assignment silently fails, is that correct? F:See K01typeset.ztst up-reference part 5 F:Here ptr1 points to global ptr2 so assignment succeeds >typeset -n ptr1=ptr2 +>typeset -hn ptr2 >ptr1=ptr2 >ptr2=val >ptr1=val >ptr2=val >typeset -n ptr1=ptr2 +>typeset -hn ptr2 >typeset ptr2=val () { -- cgit v1.2.3 From 4b9cd6b8bd5f67500e716f8485aebd31a9f7cf47 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Fri, 23 Feb 2024 09:51:06 -0800 Subject: 52583: extra check for proper scope and existence of readonly specials --- ChangeLog | 6 ++++++ Src/params.c | 25 +++++++++++++++++++++++-- Test/V10private.ztst | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 91503f01d..d520ec8c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2024-02-23 Bart Schaefer + + * 52583: Src/params.c, Test/V10private.ztst: do an extra check + for proper scope and parameter existence when assigning to a + non-local name that resolves to a readonly special. + 2024-02-22 Oliver Kiddle * 52552: Completion/Unix/Command/_java: newer Java supports diff --git a/Src/params.c b/Src/params.c index b329d2079..225acb8a1 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1004,8 +1004,29 @@ createparam(char *name, int flags) gethashnode2(paramtab, name) : paramtab->getnode(paramtab, name)); - if (oldpm && (oldpm->node.flags & PM_NAMEREF) && - !(flags & PM_NAMEREF) && (oldpm = upscope(oldpm, oldpm->base))) { + if (oldpm && (oldpm->node.flags & PM_RO_BY_DESIGN)) { + if (!(flags & PM_LOCAL)) { + /* Must call the API for namerefs and specials to work */ + pm = (Param) paramtab->getnode2(paramtab, oldpm->node.nam); + if (!pm || ((pm->node.flags & PM_NAMEREF) && + pm->level != locallevel)) { + zerr("%s: can't modify read-only parameter", name); + return NULL; + } + } + /** + * Implementation note: In the case of a readonly nameref, + * the right thing might be to insert a new global into + * the paramtab and point the local pm->old at it, rather + * than error. That is why gethashnode2() is called + * first, to avoid skipping up the stack prematurely. + **/ + } + + if (oldpm && !(flags & PM_NAMEREF) && + (!(oldpm->node.flags & PM_RO_BY_DESIGN) || !(flags & PM_LOCAL)) && + (oldpm->node.flags & PM_NAMEREF) && + (oldpm = upscope(oldpm, oldpm->base))) { Param lastpm; struct asgment stop; stop.flags = PM_NAMEREF | (flags & PM_LOCAL); diff --git a/Test/V10private.ztst b/Test/V10private.ztst index 4140d4e96..efa346002 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -311,6 +311,47 @@ F:future revision will create a global with this assignment >TOP >UP: + () { + typeset -a ary + local -P -n ref=ary + { + (){ + ref=XX # Should be an error + typeset -p ary ref + } + } always { + TRY_BLOCK_ERROR=0 + typeset -p ary ref + } + } + typeset -p ary +1:assignment to private nameref in wrong scope, part 1 +>typeset -a ary +>typeset -hn ref=ary +*?*ref: can't modify read-only parameter +*?*no such variable: ary + + () { + typeset -a ary + local -P -n ref=ary + { + (){ + typeset ref=XX # Should create a local + typeset -p ary ref + } + } always { + TRY_BLOCK_ERROR=0 + typeset -p ary ref + } + } + typeset -p ary +1:assignment to private nameref in wrong scope, part 2 +>typeset -g -a ary +>typeset ref=XX +>typeset -a ary +>typeset -hn ref=ary +*?*no such variable: ary + () { typeset -n ptr1=ptr2 private -n ptr2 # TYPESET_TO_UNSET makes this not a "placeholder" -- cgit v1.2.3 From 5331ff11c64d9d292f4fe817723af6e0a067fa1f Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 28 Feb 2024 00:21:11 +0100 Subject: 52594: support for POSIX real-time signals with kill and trap Also add new -L option to kill for a more verbose listing of signals --- ChangeLog | 9 ++++ Completion/Zsh/Command/_kill | 9 ++-- Doc/Zsh/builtins.yo | 6 ++- Doc/Zsh/params.yo | 5 +- Src/Modules/parameter.c | 2 +- Src/builtin.c | 30 ++++++------ Src/exec.c | 2 +- Src/hashtable.c | 20 ++++---- Src/init.c | 3 ++ Src/jobs.c | 113 +++++++++++++++++++++++++++++++++++++------ Src/params.c | 14 +++++- Src/signals.c | 84 +++++++++++++++++++++++++++++--- Src/signals.h | 9 ++++ Src/signames2.awk | 6 +-- 14 files changed, 249 insertions(+), 63 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 684820f92..53038c221 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2024-02-28 Oliver Kiddle + + * 52594: Completion/Zsh/Command/_kill, Doc/Zsh/builtins.yo, + Doc/Zsh/params.yo, Src/Modules/parameter.c, Src/builtin.c, + Src/exec.c, Src/hashtable.c, Src/init.c, Src/jobs.c, Src/params.c, + Src/signals.c, Src/signals.h, Src/signames2.awk: support for + POSIX real-time signals with kill and trap and add -L option to + kill for more verbose listing of signals + 2024-02-24 Bart Schaefer * 52597: Src/math.c: fix multibyte and metafied character counts diff --git a/Completion/Zsh/Command/_kill b/Completion/Zsh/Command/_kill index 084cf42c8..3b5c02151 100644 --- a/Completion/Zsh/Command/_kill +++ b/Completion/Zsh/Command/_kill @@ -4,10 +4,11 @@ local curcontext="$curcontext" line state ret=1 typeset -A opt_args _arguments -C \ - '(-s -l 1)-n[specify signal number]:signal number' \ - '(-l)-q[send the specified integer with the signal using sigqueue]:value' \ - '(-n -l 1)-s[specify signal name]:signal:_signals -s' \ - '(-n -s)-l[list signal names or numbers of specified signals]:*:signal:_signals' \ + '(-s -l -L 1)-n[specify signal number]:signal number' \ + '(-l -L)-q[send the specified integer with the signal using sigqueue]:value' \ + '(-n -l -L 1)-s[specify signal name]:signal:_signals -s' \ + '-l[list signal names or numbers of specified signals]:*:signal:_signals' \ + '(- *)-L[list each signal and corresponding number]' \ '(-n -s -l)1::signal:_signals -p -s' \ '*:processes:->processes' && ret=0 diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 784089594..6318053d8 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -1144,7 +1144,8 @@ cindex(killing jobs) cindex(jobs, killing) xitem(tt(kill) [ tt(-s) var(signal_name) | tt(-n) var(signal_number) | \ tt(-)var(sig) ] [ tt(-q) var(value) ] var(job) ...) -item(tt(kill) tt(-l) [ var(sig) ... ])( +xitem(tt(kill) tt(-l) [ var(sig) ... ]) +item(tt(kill) tt(-L))( Sends either tt(SIGTERM) or the specified signal to the given jobs or processes. Signals are given by number or by names, with or without the `tt(SIG)' @@ -1158,7 +1159,8 @@ specified the signal names are listed. Otherwise, for each var(sig) that is a name, the corresponding signal number is listed. For each var(sig) that is a signal number or a number representing the exit status of a process which was terminated or -stopped by a signal the name of the signal is printed. +stopped by a signal the name of the signal is printed. The final +form with tt(-L) lists each signal name with its corresponding number. On some systems, alternative signal names are allowed for a few signals. Typical examples are tt(SIGCHLD) and tt(SIGCLD) or tt(SIGPOLL) and diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index a6fbe6723..8c5e67e70 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -953,7 +953,10 @@ has index 1, the signals are offset by 1 from the signal number used by the operating system. For example, on typical Unix-like systems tt(HUP) is signal number 1, but is referred to as tt($signals[2]). This is because of tt(EXIT) at position 1 in the array, which is used -internally by zsh but is not known to the operating system. +internally by zsh but is not known to the operating system. On many systems +there is a block of reserved or unused signal numbers before the POSIX +real-time signals so the array index can't be used as an accurate indicator +of their signal number. Use, for example, tt(kill -l SIGRTMIN) instead. ) vindex(TRY_BLOCK_ERROR) item(tt(TRY_BLOCK_ERROR) )( diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index a05ea2fe4..7441c30b8 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -309,7 +309,7 @@ setfunction(char *name, char *val, int dis) shfunc_set_sticky(shf); if (!strncmp(name, "TRAP", 4) && - (sn = getsignum(name + 4)) != -1) { + (sn = getsigidx(name + 4)) != -1) { if (settrap(sn, NULL, ZSIG_FUNC)) { freeeprog(shf->funcdef); zfree(shf, sizeof(*shf)); diff --git a/Src/builtin.c b/Src/builtin.c index f72d14da4..44dfed651 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -3425,16 +3425,16 @@ bin_functions(char *name, char **argv, Options ops, int func) newsh->sticky = sticky_emulation_dup(shf->sticky, 0); /* is newsh a signal trap? (adapted from exec.c) */ if (!strncmp(s, "TRAP", 4)) { - int signum = getsignum(s + 4); - if (signum != -1) { - if (settrap(signum, NULL, ZSIG_FUNC)) { + int sigidx = getsigidx(s + 4); + if (sigidx != -1) { + if (settrap(sigidx, NULL, ZSIG_FUNC)) { freeeprog(newsh->funcdef); dircache_set(&newsh->filename, NULL); zfree(newsh, sizeof(*newsh)); return 1; } /* Remove any old node explicitly */ - removetrapnode(signum); + removetrapnode(sigidx); } } shfunctab->addnode(shfunctab, ztrdup(s), &newsh->node); @@ -3713,15 +3713,15 @@ bin_functions(char *name, char **argv, Options ops, int func) /* no flags, so just print */ printshfuncexpand(&shf->node, pflags, expand); } else if (on & PM_UNDEFINED) { - int signum = -1, ok = 1; + int sigidx = -1, ok = 1; if (!strncmp(*argv, "TRAP", 4) && - (signum = getsignum(*argv + 4)) != -1) { + (sigidx = getsigidx(*argv + 4)) != -1) { /* * Because of the possibility of alternative names, * we must remove the trap explicitly. */ - removetrapnode(signum); + removetrapnode(sigidx); } if (**argv == '/') { @@ -3757,8 +3757,8 @@ bin_functions(char *name, char **argv, Options ops, int func) shfunc_set_sticky(shf); add_autoload_function(shf, *argv); - if (signum != -1) { - if (settrap(signum, NULL, ZSIG_FUNC)) { + if (sigidx != -1) { + if (settrap(sigidx, NULL, ZSIG_FUNC)) { shfunctab->removenode(shfunctab, *argv); shfunctab->freenode(&shf->node); returnval = 1; @@ -7346,7 +7346,7 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) /* If given no arguments, list all currently-set traps */ if (!*argv) { queue_signals(); - for (sig = 0; sig < VSIGCOUNT; sig++) { + for (sig = 0; sig < TRAPCOUNT; sig++) { if (sigtrapped[sig] & ZSIG_FUNC) { HashNode hn; @@ -7372,13 +7372,13 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) /* If we have a signal number, unset the specified * * signals. With only -, remove all traps. */ - if ((getsignum(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) { + if ((getsigidx(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) { if (!*argv) { - for (sig = 0; sig < VSIGCOUNT; sig++) + for (sig = 0; sig < TRAPCOUNT; sig++) unsettrap(sig); } else { for (; *argv; argv++) { - sig = getsignum(*argv); + sig = getsigidx(*argv); if (sig == -1) { zwarnnam(name, "undefined signal: %s", *argv); break; @@ -7403,12 +7403,12 @@ bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) Eprog t; int flags; - sig = getsignum(*argv); + sig = getsigidx(*argv); if (sig == -1) { zwarnnam(name, "undefined signal: %s", *argv); break; } - if (idigit(**argv) || + if (idigit(**argv) || (sig >= VSIGCOUNT) || !strcmp(sigs[sig], *argv) || (!strncmp("SIG", *argv, 3) && !strcmp(sigs[sig], *argv+3))) { /* The signal was specified by number or by canonical name (with diff --git a/Src/exec.c b/Src/exec.c index 0750738ce..d85adbea5 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -5427,7 +5427,7 @@ execfuncdef(Estate state, Eprog redir_prog) } else { /* is this shell function a signal trap? */ if (!strncmp(s, "TRAP", 4) && - (signum = getsignum(s + 4)) != -1) { + (signum = getsigidx(s + 4)) != -1) { if (settrap(signum, NULL, ZSIG_FUNC)) { freeeprog(shf->funcdef); dircache_set(&shf->filename, NULL); diff --git a/Src/hashtable.c b/Src/hashtable.c index bb165505e..75b06c4ad 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -836,10 +836,10 @@ static HashNode removeshfuncnode(UNUSED(HashTable ht), const char *nam) { HashNode hn; - int signum; + int sigidx; - if (!strncmp(nam, "TRAP", 4) && (signum = getsignum(nam + 4)) != -1) - hn = removetrap(signum); + if (!strncmp(nam, "TRAP", 4) && (sigidx = getsigidx(nam + 4)) != -1) + hn = removetrap(sigidx); else hn = removehashnode(shfunctab, nam); @@ -856,10 +856,10 @@ disableshfuncnode(HashNode hn, UNUSED(int flags)) { hn->flags |= DISABLED; if (!strncmp(hn->nam, "TRAP", 4)) { - int signum = getsignum(hn->nam + 4); - if (signum != -1) { - sigtrapped[signum] &= ~ZSIG_FUNC; - unsettrap(signum); + int sigidx = getsigidx(hn->nam + 4); + if (sigidx != -1) { + sigtrapped[sigidx] &= ~ZSIG_FUNC; + unsettrap(sigidx); } } } @@ -876,9 +876,9 @@ enableshfuncnode(HashNode hn, UNUSED(int flags)) shf->node.flags &= ~DISABLED; if (!strncmp(shf->node.nam, "TRAP", 4)) { - int signum = getsignum(shf->node.nam + 4); - if (signum != -1) { - settrap(signum, NULL, ZSIG_FUNC); + int sigidx = getsigidx(shf->node.nam + 4); + if (sigidx != -1) { + settrap(sigidx, NULL, ZSIG_FUNC); } } } diff --git a/Src/init.c b/Src/init.c index 83b79d3d4..ec21521b1 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1382,6 +1382,9 @@ setupshin(char *runscript) void init_signals(void) { + sigtrapped = (int *) hcalloc(TRAPCOUNT * sizeof(int)); + siglists = (Eprog *) hcalloc(TRAPCOUNT * sizeof(Eprog)); + if (interact) { int i; signal_setmask(signal_mask(0)); diff --git a/Src/jobs.c b/Src/jobs.c index 118c5e61b..49decc661 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -1073,6 +1073,21 @@ should_report_time(Job j) return 0; } +/**/ +char * +sigmsg(int sig) +{ + static char *unknown = "unknown signal"; +#if defined(SIGRTMIN) && defined(SIGRTMAX) + static char rtmsg[] = "real-time event XXX"; + if (sig >= SIGRTMIN && sig <= SIGRTMAX) { + sprintf(rtmsg + sizeof(rtmsg) - 4, "%u", sig - SIGRTMIN + 1); + return rtmsg; + } +#endif + return sig <= SIGCOUNT ? sig_msg[sig] : unknown; +} + /* !(lng & 3) means jobs * * (lng & 1) means jobs -l * * (lng & 2) means jobs -p @@ -2694,7 +2709,7 @@ static const struct { int bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) { - int sig = SIGTERM; + int status, sig = SIGTERM; int returnval = 0; #ifdef HAVE_SIGQUEUE union sigval sigqueue_info; @@ -2740,23 +2755,29 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) if ((*argv)[1] == 'l' && (*argv)[2] == '\0') { if (argv[1]) { while (*++argv) { - sig = zstrtol(*argv, &signame, 10); + status = zstrtol(*argv, &signame, 10); if (signame == *argv) { + signame = casemodify(signame, CASMOD_UPPER); if (!strncmp(signame, "SIG", 3)) signame += 3; for (sig = 1; sig <= SIGCOUNT; sig++) - if (!strcasecmp(sigs[sig], signame)) + if (!strcmp(sigs[sig], signame)) break; if (sig > SIGCOUNT) { int i; for (i = 0; alt_sigs[i].name; i++) - if (!strcasecmp(alt_sigs[i].name, signame)) + if (!strcmp(alt_sigs[i].name, signame)) { sig = alt_sigs[i].num; break; } } +#if defined(SIGRTMIN) && defined(SIGRTMAX) + if (sig > SIGCOUNT && (sig = rtsigno(signame))) { + printf("%d\n", sig); + } else +#endif if (sig > SIGCOUNT) { zwarnnam(nam, "unknown signal: SIG%s", signame); @@ -2769,14 +2790,15 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) signame); returnval++; } else { - if (WIFSIGNALED(sig)) - sig = WTERMSIG(sig); - else if (WIFSTOPPED(sig)) - sig = WSTOPSIG(sig); + sig = status & ~0200; if (1 <= sig && sig <= SIGCOUNT) printf("%s\n", sigs[sig]); +#if defined(SIGRTMIN) && defined(SIGRTMAX) + else if ((signame = rtsigname(sig, 0))) + printf("%s\n", signame); +#endif else - printf("%d\n", sig); + printf("%d\n", status); } } } @@ -2785,10 +2807,42 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) printf("%s", sigs[1]); for (sig = 2; sig <= SIGCOUNT; sig++) printf(" %s", sigs[sig]); +#if defined(SIGRTMIN) && defined(SIGRTMAX) + for (sig = SIGRTMIN; sig <= SIGRTMAX; sig++) + printf(" %s", rtsigname(sig, 0)); +#endif putchar('\n'); return 0; } + /* with argument "-L" list signals with their numbers in a table */ + if ((*argv)[1] == 'L' && (*argv)[2] == '\0') { +#if defined(SIGRTMIN) && defined(SIGRTMAX) + const int width = SIGRTMAX >= 100 ? 3 : 2; +#else + const int width = SIGCOUNT >= 100 ? 3 : 2; +#endif + for (sig = 1; sig < SIGCOUNT +#if defined(SIGRTMIN) && defined(SIGRTMAX) + + 1 +#endif + ; sig++) + { + printf("%*d) %-10s%c", width, sig, sigs[sig], + sig % 5 ? ' ' : '\n'); + } +#if defined(SIGRTMIN) && defined(SIGRTMAX) + for (sig = SIGRTMIN; sig < SIGRTMAX; sig++) { + printf("%*d) %-10s%c", width, sig, rtsigname(sig, 0), + (sig - SIGRTMIN + SIGCOUNT + 1) % 5 ? ' ' : '\n'); + } + printf("%*d) RTMAX\n", width, sig); +#else + printf("%*d) %s\n", width, sig, sigs[sig]); +#endif + return 0; + } + if ((*argv)[1] == 'n' && (*argv)[2] == '\0') { char *endp; @@ -2833,9 +2887,13 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) break; } } - if (sig > SIGCOUNT) { + if (sig > SIGCOUNT +#if defined(SIGRTMIN) && defined(SIGRTMAX) + && !(sig = rtsigno(signame)) +#endif + ) { zwarnnam(nam, "unknown signal: SIG%s", signame); - zwarnnam(nam, "type kill -l for a list of signals"); + zwarnnam(nam, "type kill -L for a list of signals"); return 1; } } @@ -2916,18 +2974,19 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) return returnval < 126 ? returnval : 1; } -/* Get a signal number from a string */ + +/* Get index into table of traps from a string describing a signal */ /**/ mod_export int -getsignum(const char *s) +getsigidx(const char *s) { int x, i; /* check for a signal specified by number */ x = atoi(s); - if (idigit(*s) && x >= 0 && x < VSIGCOUNT) - return x; + if (idigit(*s) && x >= 0) + return SIGIDX(x); /* search for signal by name */ if (!strncmp(s, "SIG", 3)) @@ -2943,11 +3002,16 @@ getsignum(const char *s) return alt_sigs[i].num; } +#if defined(SIGRTMIN) && defined(SIGRTMAX) + if ((x = rtsigno(s))) + return SIGIDX(x); +#endif + /* no matching signal */ return -1; } -/* Get the name for a signal. */ +/* Get the name for a signal given the index into the traps table. */ /**/ mod_export const char * @@ -2961,6 +3025,11 @@ getsigname(int sig) return alt_sigs[i].name; } else +#if defined(SIGRTMIN) && defined(SIGRTMAX) + if (sig >= VSIGCOUNT) + return rtsigname(SIGNUM(sig), 0); + else +#endif return sigs[sig]; /* shouldn't reach here */ @@ -2985,10 +3054,22 @@ gettrapnode(int sig, int ignoredisable) else getptr = shfunctab->getnode; +#if defined(SIGRTMIN) && defined(SIGRTMAX) + if (sig >= VSIGCOUNT) + sprintf(fname, "TRAP%s", rtsigname(SIGNUM(sig), 0)); + else +#endif sprintf(fname, "TRAP%s", sigs[sig]); if ((hn = getptr(shfunctab, fname))) return hn; +#if defined(SIGRTMIN) && defined(SIGRTMAX) + if (sig >= VSIGCOUNT) { + sprintf(fname, "TRAP%s", rtsigname(SIGNUM(sig), 1)); + return getptr(shfunctab, fname); + } +#endif + for (i = 0; alt_sigs[i].name; i++) { if (alt_sigs[i].num == sig) { sprintf(fname, "TRAP%s", alt_sigs[i].name); diff --git a/Src/params.c b/Src/params.c index 225acb8a1..7c5e9d8ff 100644 --- a/Src/params.c +++ b/Src/params.c @@ -946,8 +946,18 @@ createparamtable(void) setsparam("ZSH_ARGZERO", ztrdup(posixzero)); setsparam("ZSH_VERSION", ztrdup_metafy(ZSH_VERSION)); setsparam("ZSH_PATCHLEVEL", ztrdup_metafy(ZSH_PATCHLEVEL)); - setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *))); - for (t = sigs; (*sigptr++ = ztrdup_metafy(*t++)); ); + setaparam("signals", sigptr = zalloc((TRAPCOUNT + 1) * sizeof(char *))); + t = sigs; +#if defined(SIGRTMIN) && defined(SIGRTMAX) + while (t - sigs <= SIGCOUNT) + *sigptr++ = ztrdup_metafy(*t++); + { + int sig; + for (sig = SIGRTMIN; sig <= SIGRTMAX; sig++) + *sigptr++ = ztrdup_metafy(rtsigname(sig, 0)); + } +#endif + while ((*sigptr++ = ztrdup_metafy(*t++))) /* empty */ ; noerrs = 0; } diff --git a/Src/signals.c b/Src/signals.c index b1a843e2c..d28853ea6 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -31,10 +31,12 @@ #include "signals.pro" /* Array describing the state of each signal: an element contains * - * 0 for the default action or some ZSIG_* flags ored together. */ + * 0 for the default action or some ZSIG_* flags ored together. * + * Contains TRAPCOUNT elements but can't be allocated statically * + * because that's a dynamic value on Linux */ /**/ -mod_export int sigtrapped[VSIGCOUNT]; +mod_export int *sigtrapped; /* * Trap programme lists for each signal. @@ -48,7 +50,7 @@ mod_export int sigtrapped[VSIGCOUNT]; */ /**/ -mod_export Eprog siglists[VSIGCOUNT]; +mod_export Eprog *siglists; /* Total count of trapped signals */ @@ -892,7 +894,7 @@ dosavetrap(int sig, int level) * Set a trap: note this does not handle manipulation of * the function table for TRAPNAL functions. * - * sig is the signal number. + * sig is index into the table of trapped signals. * * l is the list to be eval'd for a trap defined with the "trap" * builtin and should be NULL for a function trap. @@ -931,6 +933,10 @@ settrap(int sig, Eprog l, int flags) #endif sig != SIGCHLD) signal_ignore(sig); +#if defined(SIGRTMIN) && defined(SIGRTMAX) + else if (sig >= VSIGCOUNT && sig < TRAPCOUNT) + signal_ignore(SIGNUM(sig)); +#endif } else { nsigtrapped++; sigtrapped[sig] = ZSIG_TRAPPED; @@ -940,6 +946,10 @@ settrap(int sig, Eprog l, int flags) #endif sig != SIGCHLD) install_handler(sig); +#if defined(SIGRTMIN) && defined(SIGRTMAX) + if (sig >= VSIGCOUNT && sig < TRAPCOUNT) + install_handler(SIGNUM(sig)); +#endif } sigtrapped[sig] |= flags; /* @@ -1019,6 +1029,11 @@ removetrap(int sig) #endif sig != SIGCHLD) signal_default(sig); +#if defined(SIGRTMIN) && defined(SIGRTMAX) + else if (sig >= VSIGCOUNT && sig < TRAPCOUNT) + signal_default(SIGNUM(sig)); +#endif + if (sig == SIGEXIT) exit_trap_posix = 0; @@ -1172,7 +1187,7 @@ endtrapscope(void) static int handletrap(int sig) { - if (!sigtrapped[sig]) + if (!sigtrapped[SIGIDX(sig)]) return 0; if (trap_queueing_enabled) @@ -1189,7 +1204,7 @@ handletrap(int sig) return 1; } - dotrap(sig); + dotrap(SIGIDX(sig)); if (sig == SIGALRM) { @@ -1481,3 +1496,60 @@ dotrap(int sig) restore_queue_signals(q); } + +#if defined(SIGRTMIN) && defined(SIGRTMAX) + +/* Realtime signals, these are a contiguous block that can + * be separated from the other signals with an unused gap. */ + +/**/ +int +rtsigno(const char* signame) +{ + const int maxofs = SIGRTMAX - SIGRTMIN; + const char *end = signame + 5; + int offset; + struct rtdir { int sig; int dir; char op; } x = { 0, 0, 0 }; + if (!strncmp(signame, "RTMIN", 5)) { + x = (struct rtdir) { SIGRTMIN, 1, '+' }; + } else if (!strncmp(signame, "RTMAX", 5)) { + x = (struct rtdir) { SIGRTMAX, -1, '-' }; + } else + return 0; + + if (signame[5] == x.op) { + if ((offset = strtol(signame + 6, (char **) &end, 10)) > maxofs) + return 0; + x.sig += offset * x.dir; + } + if (*end) + return 0; + + return x.sig; +} + +/**/ +char * +rtsigname(int signo, int alt) +{ + char* buf = (char *) zhalloc(10); + int minofs = signo - SIGRTMIN; + int maxofs = SIGRTMAX - signo; + int offset; + int form = alt ^ (maxofs < minofs); + + if (signo < SIGRTMIN || signo > SIGRTMAX) + return NULL; + + strcpy(buf, "RT"); + strcpy(buf+2, form ? "MAX-" : "MIN+"); + offset = form ? maxofs : minofs; + if (offset) { + snprintf(buf + 6, 4, "%d", offset); + } else { + buf[5] = '\0'; + } + return buf; +} + +#endif diff --git a/Src/signals.h b/Src/signals.h index 41ac88cce..391f11fed 100644 --- a/Src/signals.h +++ b/Src/signals.h @@ -36,6 +36,15 @@ #define SIGZERR (SIGCOUNT+1) #define SIGDEBUG (SIGCOUNT+2) #define VSIGCOUNT (SIGCOUNT+3) +#if defined(SIGRTMIN) && defined(SIGRTMAX) +# define TRAPCOUNT (VSIGCOUNT + SIGRTMAX - SIGRTMIN + 1) +# define SIGNUM(x) ((x) >= VSIGCOUNT ? (x) - VSIGCOUNT + SIGRTMIN : (x)) +# define SIGIDX(x) ((x) >= SIGRTMIN && (x) <= SIGRTMAX ? (x) - SIGRTMIN + VSIGCOUNT : (x)) +#else +# define TRAPCOUNT VSIGCOUNT +# define SIGNUM(x) (x) +# define SIGIDX(x) (x) +#endif #define SIGEXIT 0 #ifdef SV_BSDSIG diff --git a/Src/signames2.awk b/Src/signames2.awk index 4d1557cd8..5738030c6 100644 --- a/Src/signames2.awk +++ b/Src/signames2.awk @@ -15,7 +15,7 @@ if (signam == "CHLD" && sig[signum] == "CLD") sig[signum] = "" if (signam == "POLL" && sig[signum] == "IO") sig[signum] = "" if (signam == "ABRT" && sig[signum] == "IOT") sig[signum] = "" - if (sig[signum] == "") { + if (signam !~ /RTM(IN|AX)/ && sig[signum] == "") { sig[signum] = signam if (0 + max < 0 + signum && signum < 60) max = signum @@ -66,10 +66,6 @@ END { printf "#include %czsh.mdh%c\n", 34, 34 printf "\n" printf "/**/\n" - printf "#define sigmsg(sig) ((sig) <= SIGCOUNT ? sig_msg[sig]" - printf " : %c%s%c)", 34, "unknown signal", 34 - printf "\n" - printf "/**/\n" printf "mod_export char *sig_msg[SIGCOUNT+2] = {\n" printf "\t%c%s%c,\n", 34, "done", 34 -- cgit v1.2.3 From 85172998f499cb789570714ffdd43f1ca3b53e58 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 28 Feb 2024 20:40:26 -0800 Subject: 52619 (plus tests): no empty element when appending array to unset scalar --- ChangeLog | 7 ++++++- Src/params.c | 2 +- Test/A06assign.ztst | 21 +++++++++++++++++---- 3 files changed, 24 insertions(+), 6 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index e836a3853..9ab0218c9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-02-28 Bart Schaefer + + * 52619 (plus tests): Src/params.c, Test/A06assign.ztst: there + is no empty element when appending array to unset scalar + 2024-02-28 Oliver Kiddle * 52622 (tweaked, c.f. 52626): Src/jobs.c: adjust number of columns @@ -54,7 +59,7 @@ x2024-02-19 Jun-ichi Takimoto * 52544: Completion/Unix/Type/_diff_options: support macOS Ventura or newer -2024-02-18 Bart Schaefer +2024-02-18 Bart Schaefer * 52558: Etc/FAQ.yo: make note of word splitting differences with nofork substitutions; update ToC; minor formatting fixes diff --git a/Src/params.c b/Src/params.c index 7c5e9d8ff..064dbd2bc 100644 --- a/Src/params.c +++ b/Src/params.c @@ -3355,7 +3355,7 @@ assignaparam(char *s, char **val, int flags) } else if (!(PM_TYPE(v->pm->node.flags) & (PM_ARRAY|PM_HASHED)) && !(v->pm->node.flags & (PM_SPECIAL|PM_TIED))) { int uniq = v->pm->node.flags & PM_UNIQUE; - if (flags & ASSPM_AUGMENT) { + if ((flags & ASSPM_AUGMENT) && !(v->pm->node.flags & PM_UNSET)) { /* insert old value at the beginning of the val array */ char **new; int lv = arrlen(val); diff --git a/Test/A06assign.ztst b/Test/A06assign.ztst index f89edb888..3eff5331a 100644 --- a/Test/A06assign.ztst +++ b/Test/A06assign.ztst @@ -296,13 +296,26 @@ # tests of var+=(array) + a= + a+=(1 2 3) + print "${(q@)a}" +0:add array to empty parameter +>'' 1 2 3 + unset a a+=(1 2 3) - print -l $a + print "${(q@)a}" 0:add array to unset parameter ->1 ->2 ->3 +>1 2 3 + + () { + setopt localoptions typeset_to_unset + typeset a + a+=(1 2 3) + print "${(q@)a}" + } +0:add array to declared unset parameter +>1 2 3 a=(a) a+=(b) -- cgit v1.2.3 From d1ff06f99185bb14554c6a48e0466aee6466ecac Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 2 Mar 2024 21:37:25 -0800 Subject: 52652: fix obscure bug unsetting the array part of a tied parameter pair --- ChangeLog | 3 +++ Src/params.c | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 7273034ac..9718d0cae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-03-02 Bart Schaefer + * 52652: Src/params.c, Test/D04parameter.ztst: fix obscure bug + unsetting the array part of a tied parameter pair, update test + * JunT.: 52635: Test/runtests.zsh: show file name when crashed * 52612: Src/parse.c, Src/subst.c, Src/utils.c: change the %l diff --git a/Src/params.c b/Src/params.c index 064dbd2bc..e83e4aa5e 100644 --- a/Src/params.c +++ b/Src/params.c @@ -3813,12 +3813,15 @@ unsetparam_pm(Param pm, int altflag, int exp) /* fudge things so removenode isn't called */ altpm->level = 1; } - unsetparam_pm(altpm, 1, exp); + unsetparam_pm(altpm, 1, exp); /* This resets pm to empty */ + pm->node.flags |= PM_UNSET; /* so we must repeat this */ } zsfree(altremove); - if (!(pm->node.flags & PM_SPECIAL)) + if (!(pm->node.flags & PM_SPECIAL)) { pm->gsu.s = &stdscalar_gsu; + pm->node.flags &= ~PM_ARRAY; + } } /* -- cgit v1.2.3 From 610b18875ad9f4498a57e9af6903bcac3b14ff46 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 4 Mar 2024 21:07:01 -0800 Subject: 52650 plus minor fixes: add -u for named references pointing to "upper" scope --- ChangeLog | 9 +++++++++ Doc/Zsh/builtins.yo | 8 ++++++-- Doc/Zsh/expn.yo | 19 +++++++++++++++++++ Doc/Zsh/func.yo | 13 ++++++++----- Doc/Zsh/mod_ksh93.yo | 2 +- Etc/FAQ.yo | 7 +++++++ Src/Modules/ksh93.c | 2 +- Src/builtin.c | 2 +- Src/exec.c | 22 +++++++++++++++++++++- Src/params.c | 32 +++++++++++++++++++------------ Test/K01nameref.ztst | 53 +++++++++++++++++++++++++++++++++++++++++++++++++--- Test/V10private.ztst | 4 ++-- 12 files changed, 145 insertions(+), 28 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 0dd7268d1..2d4d9922e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2024-03-04 Bart Schaefer + + * 52650 plus minor fixes: Doc/Zsh/builtins.yo, Doc/Zsh/expn.yo, + Doc/Zsh/func.yo, Doc/Zsh/mod_ksh93.yo, Etc/FAQ.yo, + Src/Modules/ksh93.c, Src/builtin.c, Src/exec.c, Src/params.c, + Test/D04parameter.ztst, Test/K01nameref.ztst, + Test/V10private.ztst: add -u option for named references that + point to the "upper" scope, failed assignments have status 1 + 2024-03-05 Oliver Kiddle * 52646: Completion/Zsh/Type/_ps1234, Doc/Zsh/compsys.yo, diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 6318053d8..7a9684ac8 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -2052,13 +2052,17 @@ cindex(named reference) cindex(reference, named) The flag tt(-n) creates a em(named reference) to another parameter. The second parameter need not exist at the time the reference is -created. Only tt(-g) and tt(-r) may be used in conjunction with +created. Only tt(-g), tt(-u), and tt(-r) may be used in conjunction with tt(-n). The var(name) so created may not be an array element nor use a subscript, but the var(value) assigned may be any valid parameter name syntax, even a subscripted array element (including an associative array element) or an array slice, which is evaluated when the named reference is expanded. It is an error for a named reference to refer -to itself, even indirectly through a chain of references. +to itself, even indirectly through a chain of references. When tt(-u) +is applied to a named reference, the parameter identified by var(value) +is always found in the calling function scope rather than the current +local scope. In this case, if there is no such parameter in the calling +scope, assignments to the named reference may fail, setting tt($?) to 1. See ifzman(zmanref(zshexpn))ifnzman(noderef(Parameter Expansion)) and ifzman(zmanref(zshparam))ifnzman(noderef(Parameters)) for details of the behavior of named references. diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 2acfd08c9..183ca6e03 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -1600,6 +1600,25 @@ example(tt(before local: OUTER) tt(by reference: OUTER) tt(after func: RESULT)) +To force a named reference to refer to the outer scope, even if a local +has already been declared, add the tt(-u) option when declaring the +named reference. In this case var(rname) should already exist in the +outer scope, otherwise the behavior of assignment through var(pname) +is not defined and may change the scope of the reference or fail with +a status of 1. Example of correct usage: +ifzman() +example(tt(caller=OUTER) +tt(func LPAR()RPAR() {) +tt( print before local: $caller) +tt( local caller=INNER) +tt( print after local: $caller) +tt( typeset -n -u outer=$1) +tt( print by reference: $outer) +tt( outer=RESULT) +tt(}) +tt(func caller) +tt(print after func: $caller)) + Note, however, that named references to em(special) parameters acquire the behavior of the special parameter, regardless of the scope where the reference is declared. diff --git a/Doc/Zsh/func.yo b/Doc/Zsh/func.yo index d4914df7a..7b71e34e9 100644 --- a/Doc/Zsh/func.yo +++ b/Doc/Zsh/func.yo @@ -31,10 +31,12 @@ referent parameter is in scope, and as early as possible in the function if the reference is to a parameter in a calling scope. A typical use of named references is to pass the name -of the referent as a positional parameter. For example, +of the referent as a positional parameter. In this case it is +good practice to use the tt(-u) option to reference the calling +scope. For example, ifzman() example(pop+LPAR()RPAR() { - local -n ref=$1 + local -nu ref=$1 local last=$ref[$#ref] ref[$#ref]=LPAR()RPAR() print -r -- $last @@ -43,9 +45,10 @@ array=LPAR() a list of five values RPAR() pop array) prints the word `tt(values)' and shortens `tt($array)' to -`tt(LPAR() a list of five RPAR())'. There are no local parameters in -tt(pop) at the time `tt(ref=$1)' is assigned, so `tt(ref)' becomes a -reference to `tt(array)' in the caller. +`tt(LPAR() a list of five RPAR())'. With tt(-nu), `tt(ref)' becomes a +reference to `tt(array)' in the caller. There are no local parameters in +tt(pop) at the time `tt(ref=$1)' is assigned, so in this example tt(-u) +could have been omitted, but it makes the intention clear. Functions execute in the same process as the caller and share all files diff --git a/Doc/Zsh/mod_ksh93.yo b/Doc/Zsh/mod_ksh93.yo index 9cd708d10..7508758aa 100644 --- a/Doc/Zsh/mod_ksh93.yo +++ b/Doc/Zsh/mod_ksh93.yo @@ -12,7 +12,7 @@ The single builtin provided by this module is: startitem() findex(nameref) cindex(named references, creating) -item(tt(nameref) [ tt(-r) ] var(pname)[tt(=)var(rname)])( +item(tt(nameref) [ tt(-gur) ] var(pname)[tt(=)var(rname)])( Equivalent to tt(typeset -n )var(pname)tt(=)var(rname) However, tt(nameref) is a builtin command rather than a reserved word, diff --git a/Etc/FAQ.yo b/Etc/FAQ.yo index 145ef02c9..4a86050e6 100644 --- a/Etc/FAQ.yo +++ b/Etc/FAQ.yo @@ -1025,6 +1025,13 @@ label(210) HIT:SPOT ) + Dynamic scoping applies to named references, so for example a named + reference declared in global scope may be used in function scopes. + In ksh, local parameters have static scope, so named references in + zsh may have side-effects that do not occur in ksh. To limit those + effects, mytt(zmodload zsh/param/private) and declare all named + references mytt(private). + Named references may be used in zsh versions later than 5.9. sect(What is zsh's support for non-forking command substitution?) diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c index 9af5e1d69..6760cbca0 100644 --- a/Src/Modules/ksh93.c +++ b/Src/Modules/ksh93.c @@ -38,7 +38,7 @@ */ static struct builtin bintab[] = { - BUILTIN("nameref", BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "gr", "n") + BUILTIN("nameref", BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "gur", "n") }; #include "zsh.mdh" diff --git a/Src/builtin.c b/Src/builtin.c index 83144677b..ba9cb03c2 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2699,7 +2699,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) off |= bit; } if (OPT_MINUS(ops,'n')) { - if ((on|off) & ~PM_READONLY) { + if ((on|off) & ~(PM_READONLY|PM_UPPER)) { zwarnnam(name, "no other attributes allowed with -n"); return 1; } diff --git a/Src/exec.c b/Src/exec.c index d85adbea5..0231bc361 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2604,6 +2604,17 @@ addvars(Estate state, Wordcode pc, int addflags) opts[ALLEXPORT] = allexp; } else pm = assignsparam(name, val, myflags); + if (!pm) { + lastval = 1; + /* + * This is cheating but some exec functions propagate + * assignment status only from command substitution + * + * zerr("%s: assignment failed", name); + */ + if (!cmdoutval) + cmdoutval = 1; + } if (errflag) { state->pc = opc; return; @@ -2628,7 +2639,16 @@ addvars(Estate state, Wordcode pc, int addflags) } fprintf(xtrerr, ") "); } - assignaparam(name, arr, myflags); + if (!assignaparam(name, arr, myflags)) { + lastval = 1; + /* + * See above RE "cheating" + * + * zerr("%s: array assignment failed", name); + */ + if (!cmdoutval) + cmdoutval = 1; + } if (errflag) { state->pc = opc; return; diff --git a/Src/params.c b/Src/params.c index e83e4aa5e..263cd0c52 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1060,8 +1060,7 @@ createparam(char *name, int flags) "BUG: local parameter is not unset"); oldpm = lastpm; } - } else - flags |= PM_NAMEREF; + } } DPUTS(oldpm && oldpm->level > locallevel, @@ -6267,10 +6266,12 @@ resolve_nameref(Param pm, const Asgment stop) } } else if ((hn = gethashnode2(realparamtab, seek))) { if (pm) { - if (!(stop && (stop->flags & (PM_LOCAL)))) - hn = (HashNode)upscope((Param)hn, - ((pm->node.flags & PM_NAMEREF) ? - pm->base : ((Param)hn)->level)); + if (!(stop && (stop->flags & (PM_LOCAL)))) { + int scope = ((pm->node.flags & PM_NAMEREF) ? + ((pm->node.flags & PM_UPPER) ? -1 : + pm->base) : ((Param)hn)->level); + hn = (HashNode)upscope((Param)hn, scope); + } /* user can't tag a nameref, safe for loop detection */ pm->node.flags |= PM_TAGGED; } @@ -6316,11 +6317,13 @@ setloopvar(char *name, char *value) static void setscope(Param pm) { - if (pm->node.flags & PM_NAMEREF) { + queue_signals(); + if (pm->node.flags & PM_NAMEREF) do { Param basepm; struct asgment stop; char *refname = GETREFNAME(pm); char *t = refname ? itype_end(refname, INAMESPC, 0) : NULL; + int q = queue_signal_level(); /* Temporarily change nameref to array parameter itself */ if (t && *t == '[') @@ -6330,9 +6333,11 @@ setscope(Param pm) stop.name = ""; stop.value.scalar = NULL; stop.flags = PM_NAMEREF; - if (locallevel) + if (locallevel && !(pm->node.flags & PM_UPPER)) stop.flags |= PM_LOCAL; + dont_queue_signals(); /* Prevent unkillable loops */ basepm = (Param)resolve_nameref(pm, &stop); + restore_queue_signals(q); if (t) { pm->width = t - refname; *t = '['; @@ -6345,7 +6350,7 @@ setscope(Param pm) if (upscope(pm, pm->base) == pm) { zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); - return; + break; } pm->node.flags &= ~PM_SELFREF; } else if (pm->base == pm->level) { @@ -6353,7 +6358,7 @@ setscope(Param pm) strcmp(pm->node.nam, refname) == 0) { zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); - return; + break; } } } else if ((t = GETREFNAME(basepm))) { @@ -6361,7 +6366,7 @@ setscope(Param pm) strcmp(pm->node.nam, t) == 0) { zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); - return; + break; } } } else @@ -6381,7 +6386,8 @@ setscope(Param pm) zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); } - } + } while (0); + unqueue_signals(); } /**/ @@ -6393,6 +6399,8 @@ upscope(Param pm, int reflevel) pm = up; up = up->old; } + if (reflevel < 0 && locallevel > 0) + return pm->level == locallevel ? up : pm; return pm; } diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index ff48e2289..47d32697c 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -492,7 +492,6 @@ F:unexpected side-effects of previous tests } typeset -p ptr2 1:up-reference part 5, stacked namerefs, end not in scope -F:What is the correct behavior for the scope of ptr1? >typeset -n ptr1=ptr2 >typeset -n ptr2 >ptr1=ptr2 @@ -529,6 +528,49 @@ F:Same test, should part 5 output look like this? >typeset -n ptr2 >typeset ptr2=val + () { + () { + local var + typeset -nu ptr1=var + ptr1=outer && print -u2 assignment expected to fail + typeset -n ptr2=var + ptr2=inner + typeset -n + printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" + } + typeset -p var + } + typeset -p var +1:up-reference part 7, upscope namerefs, end not in scope +>ptr1=var +>ptr2=var +>ptr1= +>ptr2=inner +*?*typeset*: no such variable: var +*?*typeset*: no such variable: var + + typeset var + () { + () { + local var + typeset -nu ptr1=var + ptr1=outer || print -u2 assignment expected to succeed + typeset -n ptr2=var + ptr2=inner + typeset -n + printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" + } + typeset -p var + } + typeset -p var +0:up-reference part 8, upscope namerefs, end in scope +>ptr1=var +>ptr2=var +>ptr1=outer +>ptr2=inner +>typeset -g var=outer +>typeset var=outer + if zmodload zsh/parameter; then () { zmodload -u zsh/parameter @@ -539,7 +581,7 @@ F:Same test, should part 5 output look like this? } else ZTST_skip='Cannot zmodload zsh/parameter, skipping autoload test' fi -0:up-reference part 3, autoloading with hidden special +0:up-reference part 9, autoloading with hidden special >nameref-local-nameref-local >typeset -h parameters @@ -762,12 +804,17 @@ F:relies on global TYPESET_TO_UNSET in %prep bar=xx typeset -n foo=bar - () { typeset -n foo; foo=zz; foo=zz; print $bar $zz } + () { + typeset -n foo; foo=zz + foo=zz || print -u2 foo: assignment failed + print $bar $zz + } () { typeset -n foo; foo=zz; local zz; foo=zz; print $bar $zz } 0:regression: local nameref may not in-scope a global parameter F:previously this could create an infinite recursion and crash >xx >xx zz +*?*foo: assignment failed typeset -nm foo=bar 1:create nameref by pattern match not allowed diff --git a/Test/V10private.ztst b/Test/V10private.ztst index efa346002..ed51316f3 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -378,7 +378,7 @@ F:Here ptr1 finds private ptr2 by scope mismatch typeset -p ptr1 ptr2 typeset val=LOCAL () { - ptr1=val # This is a silent no-op, why? + ptr1=val || print -u2 ptr1: assignment failed typeset -n printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" } @@ -388,7 +388,6 @@ F:Here ptr1 finds private ptr2 by scope mismatch 1:up-reference for private namerefs, end not in scope F:See K01nameref.ztst up-reference part 5 F:Here ptr1 finds private ptr2 by scope mismatch -F:Assignment silently fails, is that correct? >typeset -n ptr1=ptr2 >typeset -hn ptr2='' >ptr1=ptr2 @@ -396,6 +395,7 @@ F:Assignment silently fails, is that correct? >ptr2= >typeset -n ptr1=ptr2 >typeset -hn ptr2='' +*?*ptr1: assignment failed *?*no such variable: ptr2 typeset ptr2 -- cgit v1.2.3 From d27ea2ae02275b255f9efbf929d1dc7932aebc57 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 4 Mar 2024 21:36:45 -0800 Subject: unposted (cf. 52617): only scalars can instantiate a declared named reference --- ChangeLog | 3 +++ Src/params.c | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 16ede3665..6dc3e32ee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-03-04 Bart Schaefer + * unposted (cf. 52617): Src/params.c: only scalars can instantiate + a declared named reference + * 52659: Src/builtin.c, Test/K01nameref.ztst: Fix crash when unset was called on a named referece, add regression test diff --git a/Src/params.c b/Src/params.c index 263cd0c52..4bcf41c22 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1050,8 +1050,14 @@ createparam(char *name, int flags) name = refname; oldpm = NULL; } else { - if (!(lastpm->node.flags & PM_READONLY)) - lastpm->node.flags |= PM_UNSET; + if (!(lastpm->node.flags & PM_READONLY)) { + if (flags) { + /* Only plain scalar assignment allowed */ + zerr("%s: can't change type of named reference", + name); /* Differs from ksh93u+ */ + return NULL; + } + } return lastpm; } } else { -- cgit v1.2.3 From 330821de01ebf1115079222f719c9a28cc26ff57 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 5 Mar 2024 21:13:33 -0800 Subject: 52692: local typeset of the name of a named reference hides the reference --- ChangeLog | 7 +++++++ Doc/Zsh/params.yo | 5 +++-- Src/builtin.c | 15 ++++++++------- Src/params.c | 3 ++- Test/K01nameref.ztst | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 68 insertions(+), 13 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 68f826342..a3cec14ab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2024-03-05 Bart Schaefer + + * 52692: Doc/Zsh/params.yo, Src/builtin.c, Src/params.c, + Test/K01nameref.ztst: local declaration (typeset) of the + name of a named reference hides the reference rather than + following it. Also fix two related crash bugs. + 2024-03-05 Stephane Chazelas * 52685: Doc/Zsh/restricted.yo: fix typo in the name of bash's diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 8c5e67e70..d179a0d1d 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -670,8 +670,9 @@ This manual was generated with Zsh tt(version()). When a em(named reference) is created with `tt(typeset -n)', all uses of var(pname) in assignments and expansions instead assign to or expand var(rname). This also applies to `tt(unset )var(pname)' and to -most subsequent uses of `tt(typeset)' with the exception of -`tt(typeset -n)' and `tt(typeset +n)', so to remove a named reference, +most subsequent uses of `tt(typeset)' with the exceptions of declaring +a local in a called function, or updating a current-scope parameter with +`tt(typeset -n)' or `tt(typeset +n)'. Thus to remove a named reference, use either `tt(unset -n )var(pname)' (preferred) or one of: ifzman() example(tt(typeset -n )var(pname=) diff --git a/Src/builtin.c b/Src/builtin.c index 6f98990f9..829b899f8 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2030,11 +2030,10 @@ typeset_single(char *cname, char *pname, Param pm, int func, int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly, dont_set = 0; char *subscript; - if (pm && (pm->node.flags & PM_NAMEREF) && !((off|on) & PM_NAMEREF)) { - if (!(off & PM_NAMEREF)) { - if ((pm = (Param)resolve_nameref(pm, NULL))) - pname = pm->node.nam; - } + if (pm && (pm->node.flags & PM_NAMEREF) && !((off|on) & PM_NAMEREF) && + (pm->level == locallevel || !(on & PM_LOCAL))) { + if ((pm = (Param)resolve_nameref(pm, NULL))) + pname = pm->node.nam; if (pm && (pm->node.flags & PM_NAMEREF) && (on & ~(PM_NAMEREF|PM_LOCAL|PM_READONLY))) { /* Changing type of PM_SPECIAL|PM_AUTOLOAD is a fatal error. * @@ -3125,8 +3124,10 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) oldpm->u.str) asg->value.scalar = dupstring(oldpm->u.str); /* Defer read-only error to typeset_single() */ - if (!(hn->flags & PM_READONLY)) + if (!(hn->flags & PM_READONLY)) { unsetparam_pm(oldpm, 0, 1); + hn = NULL; + } } /* Passing a NULL pm to typeset_single() makes the * nameref read-only before assignment, which breaks @@ -3134,7 +3135,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) * so this is special-cased to permit that action * like assign-at-create for other parameter types. */ - if (!(hn->flags & PM_READONLY)) + if (hn && !(hn->flags & PM_READONLY)) hn = NULL; } } diff --git a/Src/params.c b/Src/params.c index 4bcf41c22..973df3fe5 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1034,7 +1034,8 @@ createparam(char *name, int flags) } if (oldpm && !(flags & PM_NAMEREF) && - (!(oldpm->node.flags & PM_RO_BY_DESIGN) || !(flags & PM_LOCAL)) && + (oldpm->level == locallevel ? + !(oldpm->node.flags & PM_RO_BY_DESIGN) : !(flags & PM_LOCAL)) && (oldpm->node.flags & PM_NAMEREF) && (oldpm = upscope(oldpm, oldpm->base))) { Param lastpm; diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index e45b922e2..bb0d11821 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -51,9 +51,19 @@ 0:remove nameref attribute >typeset ptr=var - typeset -n ptr - typeset -t ptr - typeset -p ptr + typeset -n ptr=gvar + () { + local ptr + typeset -p ptr + } + typeset -p ptr +0:Local non-reference hides outside reference +>typeset ptr +>typeset -n ptr=gvar + + typeset -n ptr + typeset -t ptr + typeset -p ptr 0:change type of a placeholder F:Other type changes are fatal errors, should this also be? >typeset -n ptr='' @@ -845,4 +855,39 @@ F:previously this could create an infinite recursion and crash 1:create nameref by pattern match not allowed *?*typeset:1: invalid reference +# +# The following tests are run in interactive mode, using PS1 as an +# assignable special with side-effects. This crashed at one time. +# + + # Note bypassing TYPESET_TO_UNSET here + $ZTST_testdir/../Src/zsh -fis <<<$' + typeset -n p=PS1 + () { + typeset -p p + local p + typeset -p p + p=xx + typeset -p p + } + ' +0:regression: assign to local that shadows global named reference +>typeset -g -n p=PS1 +>typeset p='' +>typeset p=xx +*?* + + # Note bypassing TYPESET_TO_UNSET here + $ZTST_testdir/../Src/zsh -fis <<<$' + () { + typeset p=PS1 + typeset -n p + p=zz + } + typeset -p PS1 + ' +0:regression - converting a string into a named reference +>typeset PS1=zz +*?* + %clean -- cgit v1.2.3 From 326e8635fe01239ddf14c09785eeca2394e32b95 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Thu, 14 Mar 2024 13:11:31 -0700 Subject: 52752: typeset -p fixes for local exports and "export -x" / "readonly -r" output. --- ChangeLog | 5 +++++ Src/params.c | 21 ++++++++++++++++++--- Test/B02typeset.ztst | 14 +++++++------- 3 files changed, 30 insertions(+), 10 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index ab59d83d7..5eca27d43 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-03-14 Bart Schaefer + + * 52752: Src/params.c, Test/B02typeset.ztst: more typeset -p fixes + for local exports and improper "export -x" / "readonly -r" output. + 2024-03-13 Bart Schaefer * 52753: Doc/Zsh/grammar.yo: Clarify "nocorrect" when introducing diff --git a/Src/params.c b/Src/params.c index 973df3fe5..f65aa1e80 100644 --- a/Src/params.c +++ b/Src/params.c @@ -6044,6 +6044,7 @@ printparamnode(HashNode hn, int printflags) { Param p = (Param) hn; Param peer = NULL; + int altname = 0; if (!(p->node.flags & PM_HASHELEM) && !(printflags & PRINT_WITH_NAMESPACE) && *(p->node.nam) == '.') @@ -6089,16 +6090,26 @@ printparamnode(HashNode hn, int printflags) if (printflags & PRINT_POSIX_EXPORT) { if (!(p->node.flags & PM_EXPORTED)) return; + altname = 'x'; printf("export "); } else if (printflags & PRINT_POSIX_READONLY) { if (!(p->node.flags & PM_READONLY)) return; + altname = 'r'; printf("readonly "); - } else if (locallevel && p->level >= locallevel) { - printf("typeset "); /* printf("local "); */ } else if ((p->node.flags & PM_EXPORTED) && !(p->node.flags & (PM_ARRAY|PM_HASHED))) { - printf("export "); + if (p->level && p->level >= locallevel) + printf("local "); + else { + altname = 'x'; + printf("export "); + } + } else if (locallevel && p->level >= locallevel) { + if (p->node.flags & PM_EXPORTED) + printf("local "); + else + printf("typeset "); /* printf("local "); */ } else if (locallevel) { printf("typeset -g "); } else @@ -6112,6 +6123,10 @@ printparamnode(HashNode hn, int printflags) for (pmptr = pmtypes, i = 0; i < PMTYPES_SIZE; i++, pmptr++) { int doprint = 0; + + if (altname && altname == pmptr->typeflag) + continue; + if (pmptr->flags & PMTF_TEST_LEVEL) { if (p->level) { /* diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index d90f17d13..914eea92b 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -311,7 +311,7 @@ print $OUTER 0:Export of tied parameters >i:n:n:e:r ->typeset -xT OUTER outer=( i n n e r ) +>local -xT OUTER outer=( i n n e r ) >typeset -aT OUTER outer=( i n n e r ) >OUTER=i:n:n:e:r >outer=( i n n e r ) @@ -1099,12 +1099,12 @@ } 0: no array/hash in POSIX export/readonly -p >zsh: ->typeset -arx zsh_exported_readonly_array=( 2 ) ->typeset -Arx zsh_exported_readonly_hash=( [3]=3 ) ->typeset -rx zsh_exported_readonly_scalar=1 ->typeset -arx zsh_exported_readonly_array=( 2 ) ->typeset -Arx zsh_exported_readonly_hash=( [3]=3 ) ->typeset -rx zsh_exported_readonly_scalar=1 +>local -arx zsh_exported_readonly_array=( 2 ) +>local -Arx zsh_exported_readonly_hash=( [3]=3 ) +>local -rx zsh_exported_readonly_scalar=1 +>local -arx zsh_exported_readonly_array=( 2 ) +>local -Arx zsh_exported_readonly_hash=( [3]=3 ) +>local -rx zsh_exported_readonly_scalar=1 >sh: >export zsh_exported_readonly_scalar=1 >readonly zsh_exported_readonly_scalar=1 -- cgit v1.2.3 From 65da4674410abac23e41f89f29f6613d74858854 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 5 Aug 2024 15:49:15 -0700 Subject: 53023: fix memory leak of unset private parameter --- ChangeLog | 4 ++++ Src/params.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 41204dfd6..56f6d0f35 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-08-05 Bart Schaefer + + * 53023: Src/params.c: fix memory leak of unset private parameter + 2024-08-04 Bart Schaefer * Philippe Altherr: 53005: Src/exec.c: off-by-one error when diff --git a/Src/params.c b/Src/params.c index f65aa1e80..83bdb785d 100644 --- a/Src/params.c +++ b/Src/params.c @@ -3790,7 +3790,7 @@ unsetparam_pm(Param pm, int altflag, int exp) altremove = NULL; pm->node.flags &= ~PM_DECLARED; /* like ksh, not like bash */ - if (!(pm->node.flags & PM_UNSET)) + if (!(pm->node.flags & PM_UNSET) || (pm->node.flags & PM_REMOVABLE)) pm->gsu.s->unsetfn(pm, exp); if (pm->env) delenv(pm); -- cgit v1.2.3 From 72751bfe1f9e37145c12e244ebb1729c27aff901 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 5 Aug 2024 15:59:48 -0700 Subject: 53025: fix memory leaks and pointer errors upon named reference self-reference --- ChangeLog | 3 +++ Src/loop.c | 2 +- Src/params.c | 12 +++++++----- 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 56f6d0f35..2fa2f51a3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-08-05 Bart Schaefer + * 53025: Src/loop.c, Src/params.c: fix two memory leaks and two + pointer errors when encountering a named reference self-reference + * 53023: Src/params.c: fix memory leak of unset private parameter 2024-08-04 Bart Schaefer diff --git a/Src/loop.c b/Src/loop.c index 0f3847541..84dc66476 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -165,7 +165,7 @@ execfor(Estate state, int do_exec) fprintf(xtrerr, "%s=%s\n", name, str); fflush(xtrerr); } - setloopvar(name, ztrdup(str)); + setloopvar(name, str); count++; } if (!count) diff --git a/Src/params.c b/Src/params.c index 83bdb785d..f143a790f 100644 --- a/Src/params.c +++ b/Src/params.c @@ -2811,9 +2811,10 @@ assignstrvalue(Value v, char *val, int flags) break; } setscope(v->pm); - if ((!v->pm->env && !(v->pm->node.flags & PM_EXPORTED) && - !(isset(ALLEXPORT) && !(v->pm->node.flags & PM_HASHELEM))) || - (v->pm->node.flags & PM_ARRAY) || v->pm->ename) + if (errflag || + ((!v->pm->env && !(v->pm->node.flags & PM_EXPORTED) && + !(isset(ALLEXPORT) && !(v->pm->node.flags & PM_HASHELEM))) || + (v->pm->node.flags & PM_ARRAY) || v->pm->ename)) return; export_param(v->pm); } @@ -6330,9 +6331,10 @@ setloopvar(char *name, char *value) pm->node.flags &= ~PM_UNSET; pm->node.flags |= PM_NEWREF; setscope(pm); - pm->node.flags &= ~PM_NEWREF; + if (!errflag) + pm->node.flags &= ~PM_NEWREF; } else - setsparam(name, value); + setsparam(name, ztrdup(value)); } /**/ -- cgit v1.2.3 From b2f24ff0d2a9631f331c1841491ce60e69c20ca6 Mon Sep 17 00:00:00 2001 From: "Jun. T" Date: Tue, 6 Aug 2024 14:37:39 -0700 Subject: 53026: fix failure to free old value when setting new value of reference --- ChangeLog | 5 +++++ Src/params.c | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 2fa2f51a3..667f17177 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-08-06 Bart Schaefer + + * Jun.T: 53026: Src/params.c: fix failure to free old value when + setting new value of reference + 2024-08-05 Bart Schaefer * 53025: Src/loop.c, Src/params.c: fix two memory leaks and two diff --git a/Src/params.c b/Src/params.c index f143a790f..acd577527 100644 --- a/Src/params.c +++ b/Src/params.c @@ -482,7 +482,8 @@ static initparam argvparam_pm = IPDEF9("", &pparams, NULL, \ #define GETREFNAME(PM) (((PM)->node.flags & PM_SPECIAL) ? \ (PM)->gsu.s->getfn(PM) : (PM)->u.str) #define SETREFNAME(PM,S) (((PM)->node.flags & PM_SPECIAL) ? \ - (PM)->gsu.s->setfn(PM,(S)) : ((PM)->u.str = (S))) + (PM)->gsu.s->setfn(PM,(S)) : \ + (zsfree((PM)->u.str), (PM)->u.str = (S))) static Param argvparam; -- cgit v1.2.3 From f282ff579284c7dd918438bc8e30fba4f6776c8c Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 5 Nov 2024 13:50:51 -0800 Subject: 53209 + tests: do not unmetafy via string pointers into global parameter table --- ChangeLog | 5 +++++ Src/params.c | 3 ++- Test/D04parameter.ztst | 8 ++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index 977e25159..35b01f95e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-11-05 Bart Schaefer + + * 53209 + comments + test: Src/params.c, Test/D04parameter.ztst: + do not unmetafy via string pointers into global parameter table + 2024-10-16 Bart Schaefer * 53146: Src/exec.c: trace flags cannot be copied from an diff --git a/Src/params.c b/Src/params.c index acd577527..6f137585b 100644 --- a/Src/params.c +++ b/Src/params.c @@ -3064,8 +3064,9 @@ getsparam(char *s) mod_export char * getsparam_u(char *s) { + /* getsparam() returns pointer into global params table, so ... */ if ((s = getsparam(s))) - return unmetafy(s, NULL); + return unmeta(s); /* returns static pointer to copy */ return s; } diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 0e2a04eb5..7953827d6 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -2834,3 +2834,11 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888 fi 1:${(#X)...}: array of out-of-range characters ?(eval):4: character not in range + + ( + export ZDOTDIR=${ echo $PWD/'\0360\0237\0224\0256' } + $ZTST_testdir/../Src/zsh -c 'echo $ZDOTDIR' + ) +0:regression for workers/53179 unicode ZDOTDIR +F:output ignorable as long as not an error +*>* -- cgit v1.2.3 From 6bb792dba89016c250bc9f2581c9c267dd322254 Mon Sep 17 00:00:00 2001 From: dana Date: Thu, 26 Dec 2024 09:36:45 -0600 Subject: 53257: use monotonic clock where appropriate update the following features to use the monotonic clock for calculating time deltas and intervals: * MAILCHECK parameter * PERIOD parameter * SECONDS parameter * %(nS.t.f) prompt-expansion sequence * time built-in's elapsed time and cpu % values * zsh/zftp ZFTP_TMOUT parameter * zsh/zprof timings also use CLOCK_MONOTONIC_RAW instead of CLOCK_MONOTONIC on macOS --- ChangeLog | 9 ++++++ Doc/Zsh/params.yo | 18 ++++++++---- Doc/Zsh/prompt.yo | 3 +- Src/Modules/zftp.c | 4 +-- Src/Modules/zprof.c | 19 ++++++------ Src/compat.c | 12 ++++++++ Src/exec.c | 21 +++++++------- Src/hist.c | 6 ++-- Src/init.c | 7 ++--- Src/jobs.c | 78 ++++++++++++++++++++++++++++++++++---------------- Src/params.c | 38 ++++++++++++------------ Src/prompt.c | 2 +- Src/signals.c | 3 +- Src/utils.c | 25 ++++++++++++---- Src/zsh.h | 4 +-- Test/A08time.ztst | 39 +++++++++++++++++++++++-- Test/D01prompt.ztst | 7 +++++ Test/D04parameter.ztst | 22 ++++++++++++++ 18 files changed, 225 insertions(+), 92 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index a58002d71..989cc0aa3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2024-12-26 dana + + * 53257: Doc/Zsh/params.yo, Doc/Zsh/prompt.yo, + Src/Modules/zftp.c, Src/Modules/zprof.c, Src/compat.c, + Src/exec.c, Src/hist.c, Src/init.c, Src/jobs.c, Src/params.c, + Src/prompt.c, Src/signals.c, Src/utils.c, Src/zsh.h, + Test/A08time.ztst, Test/D01prompt.ztst, Test/D04parameter.ztst: + use monotonic clock where appropriate + 2024-12-16 dana * 53251: Completion/Unix/Command/_man: fix page completion on diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 02ce796a9..69298855f 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -926,7 +926,9 @@ referenced or seeded in the parent shell in between subshell invocations. ) vindex(SECONDS) item(tt(SECONDS) )( -The number of seconds since shell invocation. If this parameter +The number of seconds since shell invocation. On most platforms, this +is a monotonic value, so it is not affected by NTP time jumps or other +clock changes (though it may be affected by slewing). If this parameter is assigned a value, then the value returned upon reference will be the value that was assigned plus the number of seconds since the assignment. @@ -936,8 +938,10 @@ be changed using the tt(typeset) command. The type may be changed only to one of the floating point types or back to integer. For example, `tt(typeset -F SECONDS)' causes the value to be reported as a floating point number. The -value is available to microsecond accuracy, although the shell may -show more or fewer digits depending on the use of tt(typeset). See +value is nominally available to nanosecond precision, although this +varies by platform (and probably isn't accurate to 1 ns regardless), +and the shell may show more or fewer digits depending on the +use of tt(typeset). See the documentation for the builtin tt(typeset) in ifzman(zmanref(zshbuiltins))\ ifnzman(noderef(Shell Builtin Commands)) for more details. @@ -1735,8 +1739,12 @@ A star may be inserted between the percent sign and flags printing time (e.g., `tt(%*E)'); this causes the time to be printed in `var(hh)tt(:)var(mm)tt(:)var(ss)tt(.)var(ttt)' format (hours and minutes are only printed if they are not zero). -Alternatively, `tt(m)' or `tt(u)' may be used (e.g., `tt(%mE)') to produce -time output in milliseconds or microseconds, respectively. +Alternatively, `tt(m)', `tt(u)', or `tt(n)' may be used (e.g., +`tt(%mE)') to produce time output in milliseconds, microseconds, or +nanoseconds, respectively. Note that some timings on some platforms +are not actually nanosecond-precise (nor accurate to 1 ns when +they are); in fact on many systems user and kernel times are not +even microsecond-precise. ) vindex(TMOUT) item(tt(TMOUT))( diff --git a/Doc/Zsh/prompt.yo b/Doc/Zsh/prompt.yo index de988ab7c..108cb62e5 100644 --- a/Doc/Zsh/prompt.yo +++ b/Doc/Zsh/prompt.yo @@ -195,7 +195,8 @@ sitem(tt(%K))(the hour of the day on the 24-hour clock) sitem(tt(%L))(the hour of the day on the 12-hour clock) endsitem() -In addition, if the system supports the POSIX tt(gettimeofday) system +In addition, if the system supports the POSIX tt(clock_gettime) +or tt(gettimeofday) system call, tt(%.) provides decimal fractions of a second since the epoch with leading zeroes. By default three decimal places are provided, but a number of digits up to 9 may be given following the tt(%); hence tt(%6.) diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index b60e5bf31..230ad86f6 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -397,7 +397,7 @@ zfalarm(int tmout) signal(SIGALRM, zfhandler); oalremain = alarm(tmout); if (oalremain) - oaltime = time(NULL); + oaltime = zmonotime(NULL); /* * We'll leave sigtrapped, sigfuncs and TRAPXXX as they are; since the * shell's handler doesn't get the signal, they don't matter. @@ -431,7 +431,7 @@ zfunalarm(void) * I love the way alarm() uses unsigned int while time_t * is probably something completely different. */ - unsigned int tdiff = time(NULL) - oaltime; + time_t tdiff = zmonotime(NULL) - oaltime; alarm(oalremain < tdiff ? 1 : oalremain - tdiff); } else alarm(0); diff --git a/Src/Modules/zprof.c b/Src/Modules/zprof.c index 171a15b90..f5a50effc 100644 --- a/Src/Modules/zprof.c +++ b/Src/Modules/zprof.c @@ -239,8 +239,7 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name) struct sfunc sf, *sp; Pfunc f = NULL; Parc a = NULL; - struct timeval tv; - struct timezone dummy; + struct timespec ts; double prev = 0, now; char *name_for_lookups; @@ -278,19 +277,19 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name) stack = &sf; f->calls++; - tv.tv_sec = tv.tv_usec = 0; - gettimeofday(&tv, &dummy); - sf.beg = prev = ((((double) tv.tv_sec) * 1000.0) + - (((double) tv.tv_usec) / 1000.0)); + ts.tv_sec = ts.tv_nsec = 0; + zgettime_monotonic_if_available(&ts); + sf.beg = prev = ((((double) ts.tv_sec) * 1000.0) + + (((double) ts.tv_nsec) / 1000000.0)); } runshfunc(prog, w, name); if (active) { if (zprof_module && !(zprof_module->node.flags & MOD_UNLOAD)) { - tv.tv_sec = tv.tv_usec = 0; - gettimeofday(&tv, &dummy); + ts.tv_sec = ts.tv_nsec = 0; + zgettime_monotonic_if_available(&ts); - now = ((((double) tv.tv_sec) * 1000.0) + - (((double) tv.tv_usec) / 1000.0)); + now = ((((double) ts.tv_sec) * 1000.0) + + (((double) ts.tv_nsec) / 1000000.0)); f->self += now - sf.beg; for (sp = sf.prev; sp && sp->p != f; sp = sp->prev); if (!sp) diff --git a/Src/compat.c b/Src/compat.c index 8b31ad9f4..918d98e69 100644 --- a/Src/compat.c +++ b/Src/compat.c @@ -136,7 +136,19 @@ zgettime_monotonic_if_available(struct timespec *ts) #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) struct timespec dts; + +/* + * On at least some versions of macOS it appears that CLOCK_MONOTONIC is not + * actually monotonic -- there are reports that it can go backwards. + * CLOCK_MONOTONIC_RAW does not have this problem. On top of that, it is faster + * to read and it has nanosecond precision. We could use it on other systems + * too, but on Linux at least it seems that CLOCK_MONOTONIC is preferred + */ +#if defined(__APPLE__) && defined(CLOCK_MONOTONIC_RAW) + if (clock_gettime(CLOCK_MONOTONIC_RAW, &dts) < 0) { +#else if (clock_gettime(CLOCK_MONOTONIC, &dts) < 0) { +#endif zwarn("unable to retrieve CLOCK_MONOTONIC time: %e", errno); ret--; } else { diff --git a/Src/exec.c b/Src/exec.c index bc07e8c39..874ff41f7 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -348,10 +348,9 @@ setlimits(char *nam) /**/ static pid_t -zfork(struct timeval *tv) +zfork(struct timespec *ts) { pid_t pid; - struct timezone dummy_tz; /* * Is anybody willing to explain this test? @@ -360,8 +359,8 @@ zfork(struct timeval *tv) zerr("job table full"); return -1; } - if (tv) - gettimeofday(tv, &dummy_tz); + if (ts) + zgettime_monotonic_if_available(ts); /* * Queueing signals is necessary on Linux because fork() * manipulates mutexes, leading to deadlock in memory @@ -460,7 +459,7 @@ zfork(struct timeval *tv) int list_pipe = 0, simple_pline = 0; static pid_t list_pipe_pid; -static struct timeval list_pipe_start; +static struct timespec list_pipe_start; static int nowait, pline_level = 0; static int list_pipe_child = 0, list_pipe_job; static char list_pipe_text[JOBTEXTSIZE]; @@ -1863,7 +1862,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) (jobtab[list_pipe_job].stat & STAT_STOPPED)))) { pid_t pid = 0; int synch[2]; - struct timeval bgtime; + struct timespec bgtime; /* * A pipeline with the shell handling the right @@ -2284,7 +2283,7 @@ closemn(struct multio **mfds, int fd, int type) char buf[TCBUFSIZE]; int len, i; pid_t pid; - struct timeval bgtime; + struct timespec bgtime; /* * We need to block SIGCHLD in case the process @@ -2829,7 +2828,7 @@ execcmd_fork(Estate state, int how, int type, Wordcode varspc, pid_t pid; int synch[2], flags; struct entersubsh_ret esret; - struct timeval bgtime; + struct timespec bgtime; child_block(); esret.gleader = -1; @@ -2947,7 +2946,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, * for the "time" keyword */ child_times_t shti, chti; - struct timeval then; + struct timespec then; if (how & Z_TIMED) shelltime(&shti, &chti, &then, 0); @@ -5060,7 +5059,7 @@ getproc(char *cmd, char **eptr) int out = *cmd == Inang; char *pnam; pid_t pid; - struct timeval bgtime; + struct timespec bgtime; #ifndef PATH_DEV_FD int fd; @@ -5149,7 +5148,7 @@ getpipe(char *cmd, int nullexec) Eprog prog; int pipes[2], out = *cmd == Inang; pid_t pid; - struct timeval bgtime; + struct timespec bgtime; char *ends; if (!(prog = parsecmd(cmd, &ends))) diff --git a/Src/hist.c b/Src/hist.c index 1a00c30ed..d0960a284 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -2891,9 +2891,9 @@ flockhistfile(char *fn, int keep_trying) /* * Timeout is ten seconds. */ - end_time = time(NULL) + (time_t)10; + end_time = zmonotime(NULL) + (time_t)10; while (fcntl(flock_fd, F_SETLKW, &lck) == -1) { - if (!keep_trying || time(NULL) >= end_time || + if (!keep_trying || zmonotime(NULL) >= end_time || /* * Randomise wait to minimise clashes with shells exiting at * the same time. @@ -3137,7 +3137,7 @@ static int lockhistct; static int checklocktime(char *lockfile, long *sleep_usp, time_t then) { - time_t now = time(NULL); + time_t now = zmonotime(NULL); if (now + 10 < then) { /* File is more than 10 seconds in the future? */ diff --git a/Src/init.c b/Src/init.c index 61f759ded..378aee348 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1022,7 +1022,6 @@ setupvals(char *cmd, char *runscript, char *zsh_name) #ifdef USE_GETPWUID struct passwd *pswd; #endif - struct timezone dummy_tz; char *ptr; int i, j; #if defined(SITEFPATH_DIR) || defined(FPATH_DIR) || defined (ADDITIONAL_FPATH) || defined(FIXED_FPATH_DIR) @@ -1109,8 +1108,8 @@ setupvals(char *cmd, char *runscript, char *zsh_name) hatchar = '^'; termflags = TERM_UNKNOWN; curjob = prevjob = coprocin = coprocout = -1; - gettimeofday(&shtimer, &dummy_tz); /* init $SECONDS */ - srand((unsigned int)(shtimer.tv_sec + shtimer.tv_usec)); /* seed $RANDOM */ + zgettime_monotonic_if_available(&shtimer); /* init $SECONDS */ + srand((unsigned int)(shtimer.tv_sec + shtimer.tv_nsec)); /* seed $RANDOM */ /* Set default path */ path = (char **) zalloc(sizeof(*path) * 5); @@ -1297,7 +1296,7 @@ setupvals(char *cmd, char *runscript, char *zsh_name) #endif breaks = loops = 0; - lastmailcheck = time(NULL); + lastmailcheck = zmonotime(NULL); locallevel = sourcelevel = 0; sfcontext = SFC_NONE; trap_return = 0; diff --git a/Src/jobs.c b/Src/jobs.c index 39c664388..ad14f6312 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -136,7 +136,7 @@ int numpipestats, pipestats[MAX_PIPESTATS]; /**/ static struct timeval * -dtime(struct timeval *dt, struct timeval *t1, struct timeval *t2) +dtime_tv(struct timeval *dt, struct timeval *t1, struct timeval *t2) { dt->tv_sec = t2->tv_sec - t1->tv_sec; dt->tv_usec = t2->tv_usec - t1->tv_usec; @@ -147,6 +147,21 @@ dtime(struct timeval *dt, struct timeval *t1, struct timeval *t2) return dt; } +/* As above, but with timespecs */ + +/**/ +static struct timespec * +dtime_ts(struct timespec *dt, struct timespec *t1, struct timespec *t2) +{ + dt->tv_sec = t2->tv_sec - t1->tv_sec; + dt->tv_nsec = t2->tv_nsec - t1->tv_nsec; + if (dt->tv_nsec < 0) { + dt->tv_nsec += 1000000000.0; + dt->tv_sec -= 1.0; + } + return dt; +} + /* change job table entry from stopped to running */ /**/ @@ -349,7 +364,6 @@ get_usage(void) void update_process(Process pn, int status) { - struct timezone dummy_tz; #ifdef HAVE_GETRUSAGE struct timeval childs = child_usage.ru_stime; struct timeval childu = child_usage.ru_utime; @@ -360,12 +374,12 @@ update_process(Process pn, int status) /* get time-accounting info */ get_usage(); - gettimeofday(&pn->endtime, &dummy_tz); /* record time process exited */ + zgettime_monotonic_if_available(&pn->endtime); /* record time process exited */ pn->status = status; /* save the status returned by WAIT */ #ifdef HAVE_GETRUSAGE - dtime(&pn->ti.ru_stime, &childs, &child_usage.ru_stime); - dtime(&pn->ti.ru_utime, &childu, &child_usage.ru_utime); + dtime_tv(&pn->ti.ru_stime, &childs, &child_usage.ru_stime); + dtime_tv(&pn->ti.ru_utime, &childu, &child_usage.ru_utime); #else pn->ti.st = shtms.tms_cstime - childs; /* compute process system space time */ pn->ti.ut = shtms.tms_cutime - childu; /* compute process user space time */ @@ -753,7 +767,7 @@ printhhmmss(double secs) } static void -printtime(struct timeval *real, child_times_t *ti, char *desc) +printtime(struct timespec *real, child_times_t *ti, char *desc) { char *s; double elapsed_time, user_time, system_time; @@ -774,21 +788,21 @@ printtime(struct timeval *real, child_times_t *ti, char *desc) } /* go ahead and compute these, since almost every TIMEFMT will have them */ - elapsed_time = real->tv_sec + real->tv_usec / 1000000.0; + elapsed_time = real->tv_sec + real->tv_nsec / 1000000000.0; #ifdef HAVE_GETRUSAGE user_time = ti->ru_utime.tv_sec + ti->ru_utime.tv_usec / 1000000.0; system_time = ti->ru_stime.tv_sec + ti->ru_stime.tv_usec / 1000000.0; total_time = user_time + system_time; percent = 100.0 * total_time - / (real->tv_sec + real->tv_usec / 1000000.0); + / (real->tv_sec + real->tv_nsec / 1000000000.0); #else { long clktck = get_clktck(); user_time = ti->ut / (double) clktck; system_time = ti->st / (double) clktck; percent = 100.0 * (ti->ut + ti->st) - / (clktck * real->tv_sec + clktck * real->tv_usec / 1000000.0); + / (clktck * real->tv_sec + clktck * real->tv_nsec / 1000000000.0); } #endif @@ -844,6 +858,23 @@ printtime(struct timeval *real, child_times_t *ti, char *desc) break; } break; + case 'n': + switch (*++s) { + case 'E': + fprintf(stderr, "%0.fns", elapsed_time * 1000000000.0); + break; + case 'U': + fprintf(stderr, "%0.fns", user_time * 1000000000.0); + break; + case 'S': + fprintf(stderr, "%0.fns", system_time * 1000000000.0); + break; + default: + fprintf(stderr, "%%n"); + s--; + break; + } + break; case '*': switch (*++s) { case 'E': @@ -991,12 +1022,12 @@ static void dumptime(Job jn) { Process pn; - struct timeval dtimeval; + struct timespec dtimespec; if (!jn->procs) return; for (pn = jn->procs; pn; pn = pn->next) - printtime(dtime(&dtimeval, &pn->bgtime, &pn->endtime), &pn->ti, + printtime(dtime_ts(&dtimespec, &pn->bgtime, &pn->endtime), &pn->ti, pn->text); } @@ -1506,7 +1537,7 @@ deletejob(Job jn, int disowning) /**/ void -addproc(pid_t pid, char *text, int aux, struct timeval *bgtime, +addproc(pid_t pid, char *text, int aux, struct timespec *bgtime, int gleader, int list_pipe_job_used) { Process pn, *pnlist; @@ -1894,16 +1925,15 @@ spawnjob(void) /**/ void -shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int delta) +shelltime(child_times_t *shell, child_times_t *kids, struct timespec *then, int delta) { - struct timezone dummy_tz; - struct timeval dtimeval, now; + struct timespec dtimespec, now; child_times_t ti; #ifndef HAVE_GETRUSAGE struct tms buf; #endif - gettimeofday(&now, &dummy_tz); + zgettime_monotonic_if_available(&now); #ifdef HAVE_GETRUSAGE getrusage(RUSAGE_SELF, &ti); @@ -1916,8 +1946,8 @@ shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int d if (shell) { if (delta) { #ifdef HAVE_GETRUSAGE - dtime(&ti.ru_utime, &shell->ru_utime, &ti.ru_utime); - dtime(&ti.ru_stime, &shell->ru_stime, &ti.ru_stime); + dtime_tv(&ti.ru_utime, &shell->ru_utime, &ti.ru_utime); + dtime_tv(&ti.ru_stime, &shell->ru_stime, &ti.ru_stime); #else ti.ut -= shell->ut; ti.st -= shell->st; @@ -1926,15 +1956,15 @@ shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int d *shell = ti; } if (delta) - dtime(&dtimeval, then, &now); + dtime_ts(&dtimespec, then, &now); else { if (then) *then = now; - dtime(&dtimeval, &shtimer, &now); + dtime_ts(&dtimespec, &shtimer, &now); } if (!delta == !shell) - printtime(&dtimeval, &ti, "shell"); + printtime(&dtimespec, &ti, "shell"); #ifdef HAVE_GETRUSAGE getrusage(RUSAGE_CHILDREN, &ti); @@ -1945,8 +1975,8 @@ shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int d if (kids) { if (delta) { #ifdef HAVE_GETRUSAGE - dtime(&ti.ru_utime, &kids->ru_utime, &ti.ru_utime); - dtime(&ti.ru_stime, &kids->ru_stime, &ti.ru_stime); + dtime_tv(&ti.ru_utime, &kids->ru_utime, &ti.ru_utime); + dtime_tv(&ti.ru_stime, &kids->ru_stime, &ti.ru_stime); #else ti.ut -= shell->ut; ti.st -= shell->st; @@ -1955,7 +1985,7 @@ shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int d *kids = ti; } if (!delta == !kids) - printtime(&dtimeval, &ti, "children"); + printtime(&dtimespec, &ti, "children"); } /* see if jobs need printing */ diff --git a/Src/params.c b/Src/params.c index 6f137585b..d1c06b893 100644 --- a/Src/params.c +++ b/Src/params.c @@ -137,11 +137,11 @@ unsigned char hatchar, hashchar; unsigned char keyboardhackchar = '\0'; /* $SECONDS = now.tv_sec - shtimer.tv_sec - * + (now.tv_usec - shtimer.tv_usec) / 1000000.0 + * + (now.tv_nsec - shtimer.tv_nsec) / 1000000000.0 * (rounded to an integer if the parameter is not set to float) */ /**/ -struct timeval shtimer; +struct timespec shtimer; /* 0 if this $TERM setup is usable, otherwise it contains TERM_* flags */ @@ -4496,13 +4496,12 @@ randomsetfn(UNUSED(Param pm), zlong v) zlong intsecondsgetfn(UNUSED(Param pm)) { - struct timeval now; - struct timezone dummy_tz; + struct timespec now; - gettimeofday(&now, &dummy_tz); + zgettime_monotonic_if_available(&now); return (zlong)(now.tv_sec - shtimer.tv_sec - - (now.tv_usec < shtimer.tv_usec ? 1 : 0)); + (now.tv_nsec < shtimer.tv_nsec ? 1 : 0)); } /* Function to set value of special parameter `SECONDS' */ @@ -4511,48 +4510,47 @@ intsecondsgetfn(UNUSED(Param pm)) void intsecondssetfn(UNUSED(Param pm), zlong x) { - struct timeval now; - struct timezone dummy_tz; + struct timespec now; zlong diff; - gettimeofday(&now, &dummy_tz); + zgettime_monotonic_if_available(&now); + diff = (zlong)now.tv_sec - x; shtimer.tv_sec = diff; if ((zlong)shtimer.tv_sec != diff) zwarn("SECONDS truncated on assignment"); - shtimer.tv_usec = now.tv_usec; + shtimer.tv_nsec = now.tv_nsec; } /**/ double floatsecondsgetfn(UNUSED(Param pm)) { - struct timeval now; - struct timezone dummy_tz; + struct timespec now; - gettimeofday(&now, &dummy_tz); + zgettime_monotonic_if_available(&now); return (double)(now.tv_sec - shtimer.tv_sec) + - (double)(now.tv_usec - shtimer.tv_usec) / 1000000.0; + (double)(now.tv_nsec - shtimer.tv_nsec) / 1000000000.0; } /**/ void floatsecondssetfn(UNUSED(Param pm), double x) { - struct timeval now; - struct timezone dummy_tz; + struct timespec now; + + zgettime_monotonic_if_available(&now); - gettimeofday(&now, &dummy_tz); shtimer.tv_sec = now.tv_sec - (zlong)x; - shtimer.tv_usec = now.tv_usec - (zlong)((x - (zlong)x) * 1000000.0); + shtimer.tv_nsec = now.tv_nsec - (zlong)((x - (zlong)x) * 1000000000.0); } /**/ double getrawseconds(void) { - return (double)shtimer.tv_sec + (double)shtimer.tv_usec / 1000000.0; + return (double)shtimer.tv_sec + (double)shtimer.tv_nsec / 1000000000.0; } /**/ @@ -4560,7 +4558,7 @@ void setrawseconds(double x) { shtimer.tv_sec = (zlong)x; - shtimer.tv_usec = (zlong)((x - (zlong)x) * 1000000.0); + shtimer.tv_nsec = (zlong)((x - (zlong)x) * 1000000000.0); } /**/ diff --git a/Src/prompt.c b/Src/prompt.c index e10b05215..f36aba9d3 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -469,7 +469,7 @@ putpromptchar(int doprint, int endchar) test = 1; break; case 'S': - if (time(NULL) - shtimer.tv_sec >= arg) + if (zmonotime(NULL) - shtimer.tv_sec >= arg) test = 1; break; case 'v': diff --git a/Src/signals.c b/Src/signals.c index 86f1a49f6..de42f302d 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -342,8 +342,7 @@ wait_for_processes(void) zwarn("job can't be suspended"); } else { #if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE) - struct timezone dummy_tz; - gettimeofday(&pn->endtime, &dummy_tz); + zgettime_monotonic_if_available(&pn->endtime); #ifdef WIFCONTINUED if (WIFCONTINUED(status)) pn->status = SP_RUNNING; diff --git a/Src/utils.c b/Src/utils.c index ce4e875fd..5c91dfcda 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1570,14 +1570,14 @@ preprompt(void) /* If 1) the parameter PERIOD exists, 2) a hook function for * * "periodic" exists, 3) it's been greater than PERIOD since we * * executed any such hook, then execute it now. */ - if (period && ((zlong)time(NULL) > (zlong)lastperiodic + period) && + if (period && ((zlong)zmonotime(NULL) > (zlong)lastperiodic + period) && !callhookfunc("periodic", NULL, 1, NULL)) - lastperiodic = time(NULL); + lastperiodic = zmonotime(NULL); if (errflag) return; /* Check mail */ - currentmailcheck = time(NULL); + currentmailcheck = zmonotime(NULL); if (mailcheck && (zlong) difftime(currentmailcheck, lastmailcheck) > mailcheck) { char *mailfile; @@ -2761,6 +2761,19 @@ timespec_diff_us(const struct timespec *t1, const struct timespec *t2) return (reverse ? LONG_MIN : LONG_MAX); } +/* Like time(), but uses the monotonic clock */ + +/**/ +mod_export int +zmonotime(time_t *tloc) +{ + struct timespec ts; + zgettime_monotonic_if_available(&ts); + if (tloc) + *tloc = ts.tv_sec; + return ts.tv_sec; +} + /* * Sleep for the given number of microseconds --- must be within * range of a long at the moment, but this is only used for @@ -2794,7 +2807,9 @@ zsleep(long us) /** * Sleep for time (fairly) randomly up to max_us microseconds. - * Don't let the wallclock time extend beyond end_time. + * Don't let the time extend beyond end_time. end_time is compared to + * the current *monotonic* clock time, so do NOT base it on e.g. time(2); + * use zmonotime() or zgettime_monotonic_if_available(). * Return 1 if that seemed to work, else 0. * * For best results max_us should be a multiple of 2**16 or large @@ -2806,7 +2821,7 @@ int zsleep_random(long max_us, time_t end_time) { long r; - time_t now = time(NULL); + time_t now = zmonotime(NULL); /* * Randomish backoff. Doesn't need to be fundamentally diff --git a/Src/zsh.h b/Src/zsh.h index 090abf8f5..85b5c9bdc 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1115,8 +1115,8 @@ struct process { char text[JOBTEXTSIZE]; /* text to print when 'jobs' is run */ int status; /* return code from waitpid/wait3() */ child_times_t ti; - struct timeval bgtime; /* time job was spawned */ - struct timeval endtime; /* time job exited */ + struct timespec bgtime; /* time job was spawned */ + struct timespec endtime; /* time job exited */ }; struct execstack { diff --git a/Test/A08time.ztst b/Test/A08time.ztst index 22a460f5e..4a41cc76a 100644 --- a/Test/A08time.ztst +++ b/Test/A08time.ztst @@ -11,9 +11,44 @@ (time cat) >&/dev/null 0:`time' keyword (status only) - ( TIMEFMT='%E %mE %uE %* %m%mm %u%uu'; time (:) ) + ( TIMEFMT='%E %mE %uE %nE %* %m%mm %u%uu %n%nn'; time (:) ) 0:`time' keyword with custom TIMEFMT -*?[0-9]##.[0-9](#c2)s [0-9]##ms [0-9]##us %\* %m%mm %u%uu +*?[0-9]##.[0-9](#c2)s [0-9]##ms [0-9]##us [0-9]##ns %\* %m%mm %u%uu %n%nn + + ( TIMEFMT='x %U %S %E'; time (:) ) +0:TIMEFMT %[USE] use centisecond precision +*?x( <0-9>.<00-99>s)(#c3) + + ( TIMEFMT='x %*U %*S %*E'; time (:) ) +0:TIMEFMT %*[USE] use millisecond precision +*?x( <0-9>.<000-999>)(#c3) + + ( TIMEFMT='%nU %nS'; time (read -k3 -t0.1) ) +1:TIMEFMT %nU and %nS are limited to microsecond precision +*?[1-9][0-9]#000ns [1-9][0-9]#000ns + +# SECONDS (after - before) must be greater than the elapsed time, but not much +# greater. 25% was picked arbitrarily as something that hopefully will prevent +# the test from failing on slow machines + ( + typeset -F SECONDS + TIMEFMT=%nE + a=$SECONDS + t=$( (time (read -k3 -t0.1)) 2>&1 ) + b=$SECONDS + s=$(( b - a )) + t=$(( ${t%ns}.0 / 10**9 )) + echo $s $t $(( s > t )) $(( t > s - (s * 0.25) )) + ) +0:`time' elapsed time matches SECONDS +*>[0-9.]## [0-9.]## 1 1 + +# Again, the wide range here is an attempt to prevent this test from failing on +# slow machines. We don't care about the exact time, just that it's vaguely sane +# and that each representation has the same basis + ( TIMEFMT='%E %mE %uE %nE %*E'; time (read -k3 -t0.1) ) +1:TIMEFMT elapsed time values +*?0.<10-50>s <10-500>ms <100000-500000>us <100000000-500000000>ns 0.<100-500> time x=1 0:`time' simple assignment diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst index 55861cca1..f42e19714 100644 --- a/Test/D01prompt.ztst +++ b/Test/D01prompt.ztst @@ -68,6 +68,13 @@ print -P '%(?.true.false)' 0:ternary prompt escapes >true +>false + + sec=$SECONDS + eval "print -P '%(${sec}S.true.false)'" + eval "print -P '%($((sec+30))S.true.false)'" +0:ternary prompt escape with test character S +>true >false print -P 'start %10<...1 >1 + # Integer + a=$SECONDS + sleep 1 + b=$SECONDS + print -r - $a $b $(( b > a )) + # Float + typeset -F SECONDS + a=$SECONDS + repeat 10 : + b=$SECONDS + print -r - $a $b $(( b > a )) + # Assignment + a=$SECONDS + SECONDS=8888 + repeat 10 : + b=$SECONDS + print -r - $(( a < 8888 )) $(( b > 8888 )) +0:SECONDS +*>[0-9]## [0-9]## 1 +*>[0-9]##.[0-9]## [0-9]##.[0-9]## 1 +*>1 1 + foo=("|" "?") [[ "|" = ${(j.|.)foo} ]] && print yes || print no [[ "|" = ${(j.|.)~foo} ]] && print yes || print no -- cgit v1.2.3 From e7163e69d90f8dcd5cdeea054df929b635f89260 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 31 Mar 2025 15:25:34 -0700 Subject: 53431: fix assignment via named reference to parameters in outer scopes --- ChangeLog | 5 ++++- Src/params.c | 6 +++++- Test/K01nameref.ztst | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) (limited to 'Src/params.c') diff --git a/ChangeLog b/ChangeLog index ed79feb18..af75d0ea7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,7 @@ -2025-03-31 Bart Schaefer +2025-03-31 Bart Schaefer + + * 53431: Src/params.c, Test/K01nameref.ztst: fix assignment via + named reference to parameters in outer scopes; add tests * Frank Dana: 53414: Functions/Prompts/prompt_restore_setup: attempting to preview the "restore" keyword is nonsensical, diff --git a/Src/params.c b/Src/params.c index d1c06b893..c10236a0d 100644 --- a/Src/params.c +++ b/Src/params.c @@ -6395,7 +6395,9 @@ setscope(Param pm) } } else pm->base = basepm->level; - } + } else if (pm->base < locallevel && refname && + (basepm = (Param)getparamnode(realparamtab, refname))) + pm->base = basepm->level; if (pm->base > pm->level) { if (EMULATION(EMULATE_KSH)) { zerr("%s: global reference cannot refer to local variable", @@ -6420,6 +6422,8 @@ upscope(Param pm, int reflevel) { Param up = pm->old; while (up && up->level >= reflevel) { + if (reflevel < 0 && up->level < locallevel) + break; pm = up; up = up->old; } diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index bacc3ade2..1603ab1b9 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -595,6 +595,53 @@ F:Same test, should part 5 output look like this? >nameref-local-nameref-local >typeset -h parameters + ( + inner() { local -n var="${1:?}"; var=(alpha beta gamma); } + outer() { local -a foo=(outer); inner foo; typeset -p foo; } + foo=3 ; { outer foo } always { typeset -p foo } + ) +0:up-reference part 10, assignment to enclosing scope, types match +>typeset -a foo=( alpha beta gamma ) +>typeset -g foo=3 + + ( + inner() { local -n var="${1:?}"; var=(alpha beta gamma); } + outer() { local foo=outer; inner foo; typeset -p foo; } + foo=3 ; { outer foo } always { typeset -p foo } + ) +1:up-reference part 11, assignment to enclosing scope, type mismatch +>typeset -g foo=3 +?inner: foo: attempt to assign array value to non-array + + ( + inner() { local -n var="${1:?}"; unset var; var=(alpha beta gamma); } + outer() { local foo=outer; inner foo; typeset -p foo; } + foo=3 ; { outer foo } always { typeset -p foo } + ) +0:up-reference part 12, assignment to enclosing scope, unset by reference +>typeset -a foo=( alpha beta gamma ) +>typeset -g foo=3 + + ( + inner() { local "${1:?}"; local -nu var="$1"; var=(alpha beta gamma); } + outer() { local -a foo=(outer); inner foo; typeset -p foo; } + foo=3 ; { outer foo } always { typeset -p foo } + ) +0:up-reference part 13, assignment to enclosing scope, skip local +>typeset -a foo=( alpha beta gamma ) +>typeset -g foo=3 + + ( + inner() { local "${1:?}"; local -nu var="$1"; + typeset -g var=(alpha beta gamma); } + outer() { local -a foo=(outer); inner foo; typeset -p foo; } + foo=3 ; { outer foo } always { typeset -p foo } + ) +0f:up-reference part 14, typeset -g to enclosing scope, skip local +F:typeset cannot bypass a name in the local scope, even via nameref +>typeset -a foo=( alpha beta gamma ) +>typeset -g foo=3 + if [[ $options[typesettounset] != on ]]; then ZTST_skip='Ignoring zmodload bug that resets TYPESET_TO_UNSET' setopt typesettounset -- cgit v1.2.3