From d24ab95469fd5514e308fcb4926a218abe492082 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Thu, 9 Jun 2022 13:30:55 -0700 Subject: 50351: "functions -c" can set signal traps --- Src/builtin.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'Src/builtin.c') diff --git a/Src/builtin.c b/Src/builtin.c index 1cef7cce8..7f00d9d29 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -3274,6 +3274,7 @@ bin_functions(char *name, char **argv, Options ops, int func) if (OPT_ISSET(ops,'c')) { Shfunc newsh; + char *s = argv[1]; if (!*argv || !argv[1] || argv[2]) { zwarnnam(name, "-c: requires two arguments"); return 1; @@ -3305,7 +3306,21 @@ bin_functions(char *name, char **argv, Options ops, int func) newsh->redir->nref++; if (shf->sticky) newsh->sticky = sticky_emulation_dup(sticky, 0); - shfunctab->addnode(shfunctab, ztrdup(argv[1]), &newsh->node); + /* 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)) { + freeeprog(newsh->funcdef); + dircache_set(&newsh->filename, NULL); + zfree(newsh, sizeof(*newsh)); + return 1; + } + /* Remove any old node explicitly */ + removetrapnode(signum); + } + } + shfunctab->addnode(shfunctab, ztrdup(s), &newsh->node); return 0; } -- cgit v1.2.3 From d4955bc0f9403551503012c3f36c841210ce0cd5 Mon Sep 17 00:00:00 2001 From: Matthew Martin Date: Thu, 9 Jun 2022 13:37:51 -0700 Subject: 50359: fix bad sticky-emulation in "functions -c" --- ChangeLog | 4 ++++ Src/builtin.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 742e7d716..dc2ec1e89 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2022-06-09 Matthew Martin + + * 50359: Src/builtin.c: fix bad sticky-emulation in "functions -c" + 2022-06-09 Bart Schaefer * 50351: Src/builtin.c: "functions -c" can set signal traps diff --git a/Src/builtin.c b/Src/builtin.c index 7f00d9d29..a7b7755a7 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -3305,7 +3305,7 @@ bin_functions(char *name, char **argv, Options ops, int func) if (newsh->redir) newsh->redir->nref++; if (shf->sticky) - newsh->sticky = sticky_emulation_dup(sticky, 0); + 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); -- 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/builtin.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 2701ab161df1f259b8292a650a4ea5cebd668d81 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Tue, 13 Dec 2022 20:12:06 +0900 Subject: 51207: fix for read -d when the delimiter is a byte >= 0x80 --- ChangeLog | 3 +++ Src/builtin.c | 7 ++++--- Test/B04read.ztst | 6 ++++++ 3 files changed, 13 insertions(+), 3 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 996704135..5b0af2135 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-12-16 Oliver Kiddle + * Jun T.: 51207: Src/builtin.c, Test/B04read.ztst: + fix for read -d when the delimiter is a byte >= 0x80 + * 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, diff --git a/Src/builtin.c b/Src/builtin.c index db83313d6..951970138 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -6286,7 +6286,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) char *laststart; size_t ret; #else - char delim = '\n'; + int delim = '\n'; #endif if (OPT_HASARG(ops,c='k')) { @@ -6413,10 +6413,11 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) if (wi != WEOF) delim = (wchar_t)wi; else - delim = (wchar_t)((delimstr[0] == Meta) ? + delim = (wchar_t) (unsigned char) ((delimstr[0] == Meta) ? delimstr[1] ^ 32 : delimstr[0]); #else - delim = (delimstr[0] == Meta) ? delimstr[1] ^ 32 : delimstr[0]; + delim = (unsigned char) ((delimstr[0] == Meta) ? + delimstr[1] ^ 32 : delimstr[0]); #endif if (SHTTY != -1) { struct ttyinfo ti; diff --git a/Test/B04read.ztst b/Test/B04read.ztst index 25c3d4173..96adf51c7 100644 --- a/Test/B04read.ztst +++ b/Test/B04read.ztst @@ -82,6 +82,12 @@ >Testing the >null hypothesis + print -n $'first line\x80second line\x80' | + while read -d $'\x80' line; do print $line; done +0:read with a delimiter >= 0x80 +>first line +>second line + # Note that trailing NULLs are not stripped even if they are in # $IFS; only whitespace characters contained in $IFS are stripped. print -n $'Aaargh, I hate nulls.\0\0\0' | read line -- cgit v1.2.3 From 35a2f155c3b92e67957325e1f49e409546378e3e Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sat, 17 Dec 2022 00:09:37 +0100 Subject: 51214: handle read -d and a delimiter that can't be decoded into a character Terminate input at the raw byte value of the delimiter. Also document and test the use of an empty string as a way to specify NUL as the delimiter. --- ChangeLog | 4 ++++ Doc/Zsh/builtins.yo | 3 ++- Src/builtin.c | 7 +++++-- Test/B04read.ztst | 4 ++++ Test/D07multibyte.ztst | 14 ++++++++++++++ 5 files changed, 29 insertions(+), 3 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 5b0af2135..130bec319 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2022-12-16 Oliver Kiddle + * 51214: Doc/Zsh/builtins.yo, Src/builtin.c, Test/B04read.ztst, + Test/D07multibyte.ztst: with read -d and a delimiter that can't be + decoded into a character terminate input at the raw byte value + * Jun T.: 51207: Src/builtin.c, Test/B04read.ztst: fix for read -d when the delimiter is a byte >= 0x80 diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index b6217f66d..56428a714 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -1589,7 +1589,8 @@ Input is read from the coprocess. ) item(tt(-d) var(delim))( Input is terminated by the first character of var(delim) instead of -by newline. +by newline. For compatibility with other shells, if var(delim) is an +empty string, input is terminated at the first NUL. ) item(tt(-t) [ var(num) ])( Test if input is available before attempting to read. If var(num) diff --git a/Src/builtin.c b/Src/builtin.c index 951970138..70a950666 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -6282,6 +6282,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) long izle_timeout = 0; #ifdef MULTIBYTE_SUPPORT wchar_t delim = L'\n', wc; + int rawbyte = 0; mbstate_t mbs; char *laststart; size_t ret; @@ -6412,9 +6413,11 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) wi = WEOF; if (wi != WEOF) delim = (wchar_t)wi; - else + else { delim = (wchar_t) (unsigned char) ((delimstr[0] == Meta) ? delimstr[1] ^ 32 : delimstr[0]); + rawbyte = 1; + } #else delim = (unsigned char) ((delimstr[0] == Meta) ? delimstr[1] ^ 32 : delimstr[0]); @@ -6842,7 +6845,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) break; } *bptr = (char)c; - if (isset(MULTIBYTE)) { + if (isset(MULTIBYTE) && !rawbyte) { ret = mbrtowc(&wc, bptr, 1, &mbs); if (!ret) /* NULL */ ret = 1; diff --git a/Test/B04read.ztst b/Test/B04read.ztst index 96adf51c7..14bdaeef5 100644 --- a/Test/B04read.ztst +++ b/Test/B04read.ztst @@ -82,6 +82,10 @@ >Testing the >null hypothesis + read -ed '' <<<$'one\0two' +0:empty delimiter terminates at nulls +>one + print -n $'first line\x80second line\x80' | while read -d $'\x80' line; do print $line; done 0:read with a delimiter >= 0x80 diff --git a/Test/D07multibyte.ztst b/Test/D07multibyte.ztst index 6909346cb..413c4fe73 100644 --- a/Test/D07multibyte.ztst +++ b/Test/D07multibyte.ztst @@ -212,6 +212,20 @@ >first >second + read -ed £ +0:read with multibyte delimiter where bytes of delimiter also occur in input +one¤twoãthree + + read -ed $'\xa0' <<<$'first\xa0second' +0:read delimited by a byte that isn't a valid multibyte character +>first + + read -ed $'\xc2' +0:read delimited by a single byte terminates if the byte is part of a multibyte character +one + (IFS=« read -d » -A array print -l $array) -- cgit v1.2.3 From 667ead3a64e590ac758e9f0a053849c7aaccec66 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Tue, 10 Jan 2023 20:53:17 +0100 Subject: 51258, 51272: refactor handling of terminal attributes, removing OFF flags in zattr --- ChangeLog | 8 ++ Src/Modules/watch.c | 64 +++++----- Src/Zle/complist.c | 60 ++++----- Src/Zle/zle.h | 6 +- Src/Zle/zle_main.c | 19 +-- Src/Zle/zle_refresh.c | 316 ++++++++++++---------------------------------- Src/Zle/zle_tricky.c | 48 +++---- Src/Zle/zle_utils.c | 2 +- Src/builtin.c | 4 +- Src/init.c | 3 +- Src/input.c | 2 +- Src/loop.c | 2 +- Src/prompt.c | 254 +++++++++++++++++++++++++------------ Src/subst.c | 11 +- Src/utils.c | 7 +- Src/zsh.h | 45 ++----- Test/D01prompt.ztst | 13 ++ Test/X04zlehighlight.ztst | 14 +- 18 files changed, 400 insertions(+), 478 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 773971c5a..b3518f1bf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2023-01-10 Oliver Kiddle + * 51258, 51272: Src/Modules/watch.c, Src/Zle/complist.c, + Src/Zle/zle.h, Src/Zle/zle_main.c, Src/Zle/zle_refresh.c, + Src/Zle/zle_tricky.c, Src/Zle/zle_utils.c, Src/builtin.c, + Src/init.c, Src/input.c, Src/loop.c, Src/prompt.c, + Src/subst.c, Src/utils.c, Src/zsh.h, Test/D01prompt.ztst, + Test/X04zlehighlight.ztst: refactor handling of terminal + attributes, removing OFF flags in zattr + * Nathan Houghton: 51276: Completion/Unix/Type/_diff_options: Fix diff completion for non GNU / FreeBSD platforms diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c index d45c3cf3d..0de8cbf9a 100644 --- a/Src/Modules/watch.c +++ b/Src/Modules/watch.c @@ -255,8 +255,10 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) while (*fmt) if (*fmt == '\\') { if (*++fmt) { - if (prnt) + if (prnt) { + applytextattributes(TSC_RAW); putchar(*fmt); + } ++fmt; } else if (fini) return fmt; @@ -266,8 +268,10 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) else if (*fmt == fini) return ++fmt; else if (*fmt != '%') { - if (prnt) + if (prnt) { + applytextattributes(TSC_RAW); putchar(*fmt); + } ++fmt; } else { if (*++fmt == BEGIN3) @@ -277,12 +281,15 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) else switch (*(fm2 = fmt++)) { case 'n': + applytextattributes(TSC_RAW); printf("%.*s", (int)sizeof(u->ut_name), u->ut_name); break; case 'a': + applytextattributes(TSC_RAW); printf("%s", (!inout) ? "logged off" : "logged on"); break; case 'l': + applytextattributes(TSC_RAW); if (!strncmp(u->ut_line, "tty", 3)) printf("%.*s", (int)sizeof(u->ut_line) - 3, u->ut_line + 3); else @@ -290,6 +297,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) break; # ifdef WATCH_UTMP_UT_HOST case 'm': + applytextattributes(TSC_RAW); for (p = u->ut_host, i = sizeof(u->ut_host); i && *p; i--, p++) { if (*p == '.' && !idigit(p[1])) break; @@ -297,6 +305,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) } break; case 'M': + applytextattributes(TSC_RAW); printf("%.*s", (int)sizeof(u->ut_host), u->ut_host); break; # endif /* WATCH_UTMP_UT_HOST */ @@ -343,9 +352,11 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) len = ztrftime(buf, 40, fm2, tm, 0L); if (len > 0) metafy(buf, len, META_NOALLOC); + applytextattributes(TSC_RAW); printf("%s", (*buf == ' ') ? buf + 1 : buf); break; case '%': + applytextattributes(TSC_RAW); putchar('%'); break; case 'F': @@ -354,16 +365,13 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) atr = match_colour((const char**)&fmt, 1, 0); if (*fmt == '}') fmt++; - if (!(atr & (TXT_ERROR | TXTNOFGCOLOUR))) { - txtunset(TXT_ATTR_FG_COL_MASK); - txtset(atr & TXT_ATTR_FG_ON_MASK); - set_colour_attribute(atr, COL_SEQ_FG, TSC_RAW); + if (atr && atr != TXT_ERROR) { + tsetattrs(atr); + break; } - } - break; + } /* fall-through */ case 'f': - txtunset(TXT_ATTR_FG_ON_MASK); - set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_RAW); + tunsetattrs(TXTFGCOLOUR); break; case 'K': if (*fmt == '{') { @@ -371,49 +379,43 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) atr = match_colour((const char**)&fmt, 0, 0); if (*fmt == '}') fmt++; - if (!(atr & (TXT_ERROR | TXTNOBGCOLOUR))) { - txtunset(TXT_ATTR_BG_COL_MASK); - txtset(atr & TXT_ATTR_BG_ON_MASK); - set_colour_attribute(atr, COL_SEQ_BG, TSC_RAW); + if (atr && atr != TXT_ERROR) { + tsetattrs(atr); + break; } - } - break; + } /* fall-through */ case 'k': - txtunset(TXT_ATTR_BG_ON_MASK); - set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_RAW); + tunsetattrs(TXTBGCOLOUR); break; case 'S': - txtset(TXTSTANDOUT); - tsetcap(TCSTANDOUTBEG, TSC_RAW); + tsetattrs(TXTSTANDOUT); break; case 's': - txtunset(TXTSTANDOUT); - tsetcap(TCSTANDOUTEND, TSC_RAW|TSC_DIRTY); + tunsetattrs(TXTSTANDOUT); break; case 'B': - txtset(TXTBOLDFACE); - tsetcap(TCBOLDFACEBEG, TSC_RAW|TSC_DIRTY); + tsetattrs(TXTBOLDFACE); break; case 'b': - txtunset(TXTBOLDFACE); - tsetcap(TCALLATTRSOFF, TSC_RAW|TSC_DIRTY); + tunsetattrs(TXTBOLDFACE); break; case 'U': - txtset(TXTUNDERLINE); - tsetcap(TCUNDERLINEBEG, TSC_RAW); + tsetattrs(TXTUNDERLINE); break; case 'u': - txtunset(TXTUNDERLINE); - tsetcap(TCUNDERLINEEND, TSC_RAW|TSC_DIRTY); + tunsetattrs(TXTUNDERLINE); break; default: + applytextattributes(TSC_RAW); putchar('%'); putchar(*fm2); break; } } - if (prnt) + if (prnt) { + applytextattributes(TSC_RAW); putchar('\n'); + } return fmt; } diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 6e0eac31f..8bdf1bb29 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -1072,7 +1072,7 @@ static int compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) { char *p, nc[2*DIGBUFSIZE + 12], nbuf[2*DIGBUFSIZE + 12]; - int l = 0, cc = 0, b = 0, s = 0, u = 0, m, ask, beg, stat; + int l = 0, cc = 0, m, ask, beg, stat; if ((stat = !fmt)) { if (mlbeg >= 0) { @@ -1118,48 +1118,46 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) m = 0; switch (cchar) { case ZWC('%'): - if (dopr == 1) + if (dopr == 1) { + applytextattributes(0); putc('%', shout); + } cc++; break; case ZWC('n'): if (!stat) { sprintf(nc, "%d", n); - if (dopr == 1) + if (dopr == 1) { + applytextattributes(0); fputs(nc, shout); + } /* everything here is ASCII... */ cc += strlen(nc); } break; case ZWC('B'): - b = 1; if (dopr) - tcout(TCBOLDFACEBEG); + tsetattrs(TXTBOLDFACE); break; case ZWC('b'): - b = 0; m = 1; if (dopr) - tcout(TCALLATTRSOFF); + tunsetattrs(TXTBOLDFACE); break; case ZWC('S'): - s = 1; if (dopr) - tcout(TCSTANDOUTBEG); + tsetattrs(TXTSTANDOUT); break; case ZWC('s'): - s = 0; m = 1; if (dopr) - tcout(TCSTANDOUTEND); + tunsetattrs(TXTSTANDOUT); break; case ZWC('U'): - u = 1; if (dopr) - tcout(TCUNDERLINEBEG); + tsetattrs(TXTUNDERLINE); break; case ZWC('u'): - u = 0; m = 1; if (dopr) - tcout(TCUNDERLINEEND); + tunsetattrs(TXTUNDERLINE); break; case ZWC('F'): case ZWC('K'): @@ -1173,20 +1171,21 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) } else atr = match_colour(NULL, is_fg, arg); if (atr != TXT_ERROR && dopr) - set_colour_attribute(atr, is_fg ? COL_SEQ_FG : - COL_SEQ_BG, 0); + tsetattrs(atr); break; case ZWC('f'): if (dopr) - set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, 0); + tunsetattrs(TXTFGCOLOUR); break; case ZWC('k'): if (dopr) - set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, 0); + tunsetattrs(TXTBGCOLOUR); break; case ZWC('{'): if (arg) cc += arg; + if (dopr) + applytextattributes(0); for (; *p && (*p != '%' || p[1] != '}'); p++) if (dopr) putc(*p == Meta ? *++p ^ 32 : *p, shout); @@ -1197,7 +1196,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) if (stat) { sprintf(nc, "%d/%d", (n ? mlastm : mselect), listdat.nlist); - m = 2; + m = 1; } break; case ZWC('M'): @@ -1205,20 +1204,20 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) sprintf(nbuf, "%d/%d", (n ? mlastm : mselect), listdat.nlist); sprintf(nc, "%-9s", nbuf); - m = 2; + m = 1; } break; case ZWC('l'): if (stat) { sprintf(nc, "%d/%d", ml + 1, listdat.nlines); - m = 2; + m = 1; } break; case ZWC('L'): if (stat) { sprintf(nbuf, "%d/%d", ml + 1, listdat.nlines); sprintf(nc, "%-9s", nbuf); - m = 2; + m = 1; } break; case ZWC('p'): @@ -1230,7 +1229,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) ((ml + 1) * 100) / listdat.nlines); else strcpy(nc, "Top"); - m = 2; + m = 1; } break; case ZWC('P'): @@ -1242,25 +1241,19 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) ((ml + 1) * 100) / listdat.nlines); else strcpy(nc, "Top "); - m = 2; + m = 1; } break; } - if (m == 2 && dopr == 1) { + if (m && dopr) { /* nc only contains ASCII text */ int l = strlen(nc); if (l + cc > zterm_columns - 2) nc[l -= l + cc - (zterm_columns - 2)] = '\0'; + applytextattributes(0); fputs(nc, shout); cc += l; - } else if (dopr && m == 1) { - if (b) - tcout(TCBOLDFACEBEG); - if (s) - tcout(TCSTANDOUTBEG); - if (u) - tcout(TCUNDERLINEBEG); } } else break; @@ -1276,6 +1269,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) cc = 0; } if (dopr == 1) { + applytextattributes(0); if (ml == mlend - 1 && (cc % zterm_columns) == zterm_columns - 1) { dopr = 0; diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index 97cc7d797..1a3e4c241 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -490,11 +490,7 @@ typedef struct { */ REFRESH_CHAR chr; /* - * Its attributes. 'On' attributes (TXT_ATTR_ON_MASK) are - * applied before the character, 'off' attributes (TXT_ATTR_OFF_MASK) - * after it. 'On' attributes are present for all characters that - * need the effect; 'off' attributes are only present for the - * last character in the sequence. + * Its attributes. */ zattr atr; } REFRESH_ELEMENT; diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 40b902901..39be33939 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -1230,9 +1230,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) char *pptbuf; int pptlen; - pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL, - &pmpt_attr), + pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL), &pptlen); + pmpt_attr = txtcurrentattrs; write_loop(2, pptbuf, pptlen); free(pptbuf); return shingetline(); @@ -1267,10 +1267,11 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) fetchttyinfo = 0; trashedzle = 0; raw_lp = lp; - lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL, &pmpt_attr); + lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL); + pmpt_attr = txtcurrentattrs; raw_rp = rp; - rpmpt_attr = pmpt_attr; - rpromptbuf = promptexpand(rp ? *rp : NULL, 1, NULL, NULL, &rpmpt_attr); + rpromptbuf = promptexpand(rp ? *rp : NULL, 1, NULL, NULL); + rpmpt_attr = txtcurrentattrs; free_prepostdisplay(); zlereadflags = flags; @@ -2009,8 +2010,8 @@ reexpandprompt(void) char *new_lprompt, *new_rprompt; looping = reexpanding; - new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL, - &pmpt_attr); + new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL); + pmpt_attr = txtcurrentattrs; free(lpromptbuf); lpromptbuf = new_lprompt; @@ -2018,8 +2019,8 @@ reexpandprompt(void) continue; rpmpt_attr = pmpt_attr; - new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL, - &rpmpt_attr); + new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL); + rpmpt_attr = txtcurrentattrs; free(rpromptbuf); rpromptbuf = new_rprompt; } while (looping != reexpanding); diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index 2db5f0642..ae8e5c109 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -208,7 +208,7 @@ int predisplaylen, postdisplaylen; * displayed on screen. */ -static zattr default_atr_on, special_atr_on; +static zattr default_attr, special_attr; /* * Array of region highlights, no special termination. @@ -245,12 +245,12 @@ char *tcout_func_name; int cost; # define SELECT_ADD_COST(X) (cost += X) -# define zputc(a) (zwcputc(a, NULL), cost++) +# define zputc(a) (zwcputc(a), cost++) # define zwrite(a, b) (zwcwrite((a), (b)), \ cost += ((b) * ZLE_CHAR_SIZE)) #else # define SELECT_ADD_COST(X) -# define zputc(a) zwcputc(a, NULL) +# define zputc(a) zwcputc(a) # define zwrite(a, b) zwcwrite((a), (b)) #endif @@ -316,14 +316,14 @@ static void zle_set_highlight(void) { char **atrs = getaparam("zle_highlight"); - int special_atr_on_set = 0; - int region_atr_on_set = 0; - int isearch_atr_on_set = 0; - int suffix_atr_on_set = 0; - int paste_atr_on_set = 0; + int special_attr_set = 0; + int region_attr_set = 0; + int isearch_attr_set = 0; + int suffix_attr_set = 0; + int paste_attr_set = 0; struct region_highlight *rhp; - special_atr_on = default_atr_on = 0; + special_attr = default_attr = 0; if (!region_highlights) { region_highlights = (struct region_highlight *) zshcalloc(N_SPECIAL_HIGHLIGHTS*sizeof(struct region_highlight)); @@ -340,41 +340,41 @@ zle_set_highlight(void) for (; *atrs; atrs++) { if (!strcmp(*atrs, "none")) { /* reset attributes for consistency... usually unnecessary */ - special_atr_on = default_atr_on = 0; - special_atr_on_set = 1; - paste_atr_on_set = region_atr_on_set = - isearch_atr_on_set = suffix_atr_on_set = 1; + special_attr = default_attr = 0; + special_attr_set = 1; + paste_attr_set = region_attr_set = + isearch_attr_set = suffix_attr_set = 1; } else if (strpfx("default:", *atrs)) { - match_highlight(*atrs + 8, &default_atr_on); + match_highlight(*atrs + 8, &default_attr); } else if (strpfx("special:", *atrs)) { - match_highlight(*atrs + 8, &special_atr_on); - special_atr_on_set = 1; + match_highlight(*atrs + 8, &special_attr); + special_attr_set = 1; } else if (strpfx("region:", *atrs)) { match_highlight(*atrs + 7, ®ion_highlights[0].atr); - region_atr_on_set = 1; + region_attr_set = 1; } else if (strpfx("isearch:", *atrs)) { match_highlight(*atrs + 8, &(region_highlights[1].atr)); - isearch_atr_on_set = 1; + isearch_attr_set = 1; } else if (strpfx("suffix:", *atrs)) { match_highlight(*atrs + 7, &(region_highlights[2].atr)); - suffix_atr_on_set = 1; + suffix_attr_set = 1; } else if (strpfx("paste:", *atrs)) { match_highlight(*atrs + 6, &(region_highlights[3].atr)); - paste_atr_on_set = 1; + paste_attr_set = 1; } } } /* Defaults */ - if (!special_atr_on_set) - special_atr_on = TXTSTANDOUT; - if (!region_atr_on_set) + if (!special_attr_set) + special_attr = TXTSTANDOUT; + if (!region_attr_set) region_highlights[0].atr = TXTSTANDOUT; - if (!isearch_atr_on_set) + if (!isearch_attr_set) region_highlights[1].atr = TXTUNDERLINE; - if (!suffix_atr_on_set) + if (!suffix_attr_set) region_highlights[2].atr = TXTBOLDFACE; - if (!paste_atr_on_set) + if (!paste_attr_set) region_highlights[3].atr = TXTSTANDOUT; allocate_colour_buffer(); @@ -571,22 +571,6 @@ unset_region_highlight(Param pm, int exp) } -/* The last attributes that were on. */ -static zattr lastatr; - -/* - * Clear the last attributes that we set: used when we're going - * to be outputting stuff that shouldn't show up as text. - */ -static void -clearattributes(void) -{ - if (lastatr) { - settextattributes(TXT_ATTR_OFF_FROM_ON(lastatr)); - lastatr = 0; - } -} - /* * Output a termcap capability, clearing any text attributes so * as not to mess up the display. @@ -595,7 +579,7 @@ clearattributes(void) static void tcoutclear(int cap) { - clearattributes(); + cleartextattributes(0); tcout(cap); } @@ -603,47 +587,20 @@ tcoutclear(int cap) * Output the character. This must come from the new video * buffer, nbuf, since we access the multiword buffer nmwbuf * directly. - * - * curatrp may be NULL, otherwise points to an integer specifying - * what attributes were turned on for a character output immediately - * before, in order to optimise output of attribute changes. */ /**/ void -zwcputc(const REFRESH_ELEMENT *c, zattr *curatrp) +zwcputc(const REFRESH_ELEMENT *c) { - /* - * Safety: turn attributes off if last heard of turned on. - * This differs from *curatrp, which is an optimisation for - * writing lots of stuff at once. - */ #ifdef MULTIBYTE_SUPPORT mbstate_t mbstate; int i; VARARR(char, mbtmp, MB_CUR_MAX + 1); #endif - if (lastatr & ~c->atr) { - /* Stuff on we don't want, turn it off */ - settextattributes(TXT_ATTR_OFF_FROM_ON(lastatr & ~c->atr)); - lastatr = 0; - } - - /* - * Don't output "on" attributes in a string of characters with - * the same attributes. Be careful in case a different colour - * needs setting. - */ - if ((c->atr & TXT_ATTR_ON_MASK) && - (!curatrp || - ((*curatrp & TXT_ATTR_ON_VALUES_MASK) != - (c->atr & TXT_ATTR_ON_VALUES_MASK)))) { - /* Record just the control flags we might need to turn off... */ - lastatr = c->atr & TXT_ATTR_ON_MASK; - /* ...but set including the values for colour attributes */ - settextattributes(c->atr & TXT_ATTR_ON_VALUES_MASK); - } + treplaceattrs(c->atr); + applytextattributes(0); #ifdef MULTIBYTE_SUPPORT if (c->atr & TXT_MULTIWORD_MASK) { @@ -664,35 +621,15 @@ zwcputc(const REFRESH_ELEMENT *c, zattr *curatrp) #else fputc(c->chr, shout); #endif - - /* - * Always output "off" attributes since we only turn off at - * the end of a chunk of highlighted text. - */ - if (c->atr & TXT_ATTR_OFF_MASK) { - settextattributes(c->atr & TXT_ATTR_OFF_MASK); - lastatr &= ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT); - } - if (curatrp) { - /* - * Remember the current attributes: those that are turned - * on, less those that are turned off again. Include - * colour attributes here in case the colour changes to - * another non-default one. - */ - *curatrp = (c->atr & TXT_ATTR_ON_VALUES_MASK) & - ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT); - } } static int zwcwrite(const REFRESH_STRING s, size_t i) { size_t j; - zattr curatr = 0; for (j = 0; j < i; j++) - zwcputc(s + j, &curatr); + zwcputc(s + j); return i; /* TODO something better for error indication */ } @@ -939,29 +876,6 @@ snextline(Rparams rpms) rpms->sen = rpms->s + winw; } - -/**/ -static void -settextattributes(zattr atr) -{ - if (txtchangeisset(atr, TXTNOBOLDFACE)) - tsetcap(TCALLATTRSOFF, 0); - if (txtchangeisset(atr, TXTNOSTANDOUT)) - tsetcap(TCSTANDOUTEND, 0); - if (txtchangeisset(atr, TXTNOUNDERLINE)) - tsetcap(TCUNDERLINEEND, 0); - if (txtchangeisset(atr, TXTBOLDFACE)) - tsetcap(TCBOLDFACEBEG, 0); - if (txtchangeisset(atr, TXTSTANDOUT)) - tsetcap(TCSTANDOUTBEG, 0); - if (txtchangeisset(atr, TXTUNDERLINE)) - tsetcap(TCUNDERLINEBEG, 0); - if (txtchangeisset(atr, TXTFGCOLOUR|TXTNOFGCOLOUR)) - set_colour_attribute(atr, COL_SEQ_FG, 0); - if (txtchangeisset(atr, TXTBGCOLOUR|TXTNOBGCOLOUR)) - set_colour_attribute(atr, COL_SEQ_BG, 0); -} - #ifdef MULTIBYTE_SUPPORT /* * Add a multiword glyph at the screen location base. @@ -1043,7 +957,6 @@ zrefresh(void) int tmppos; /* t - tmpline */ int tmpalloced; /* flag to free tmpline when finished */ int remetafy; /* flag that zle line is metafied */ - zattr txtchange; /* attributes set after prompts */ int rprompt_off = 1; /* Offset of rprompt from right of screen */ struct rparams rpms; #ifdef MULTIBYTE_SUPPORT @@ -1194,7 +1107,7 @@ zrefresh(void) tsetcap(TCALLATTRSOFF, 0); tsetcap(TCSTANDOUTEND, 0); tsetcap(TCUNDERLINEEND, 0); - txtattrmask = 0; + txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0; if (trashedzle && !clearflag) reexpandprompt(); @@ -1219,8 +1132,8 @@ zrefresh(void) if (lpromptwof == winw) zputs("\n", shout); /* works with both hasam and !hasam */ } else { - txtchange = pmpt_attr; - settextattributes(txtchange); + treplaceattrs(pmpt_attr); + applytextattributes(0); } if (clearflag) { zputc(&zr_cr); @@ -1264,8 +1177,8 @@ zrefresh(void) rpms.sen = *nbuf + winw; for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) { unsigned ireg; - zattr base_atr_on = default_atr_on, base_atr_off = 0; - zattr all_atr_on, all_atr_off; + zattr base_attr = default_attr; + zattr all_attr; struct region_highlight *rhp; /* * Calculate attribute based on region. @@ -1282,26 +1195,21 @@ zrefresh(void) tmppos < rhp->end + offset) { if (rhp->atr & (TXTFGCOLOUR|TXTBGCOLOUR)) { /* override colour with later entry */ - base_atr_on = (base_atr_on & ~TXT_ATTR_ON_VALUES_MASK) | - rhp->atr; + base_attr = rhp->atr; } else { /* no colour set yet */ - base_atr_on |= rhp->atr; + base_attr |= rhp->atr; } - if (tmppos == rhp->end + offset - 1 || - tmppos == tmpll - 1) - base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr); } } - if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) { + if (special_attr & (TXTFGCOLOUR|TXTBGCOLOUR)) { /* keep colours from special attributes */ - all_atr_on = special_atr_on | - (base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK); + all_attr = special_attr | + (base_attr & ~TXT_ATTR_COLOUR_MASK); } else { /* keep colours from standard attributes */ - all_atr_on = special_atr_on | base_atr_on; + all_attr = special_attr | base_attr; } - all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on); if (t == scs) /* if cursor is here, remember it */ rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln]; @@ -1319,10 +1227,9 @@ zrefresh(void) } else { do { rpms.s->chr = ZWC(' '); - rpms.s->atr = base_atr_on; + rpms.s->atr = base_attr; rpms.s++; } while ((++t0) & 7); - rpms.s[-1].atr |= base_atr_off; } } #ifdef MULTIBYTE_SUPPORT @@ -1341,11 +1248,9 @@ zrefresh(void) rpms.s->chr = ZWC(' '); if (!started) started = 1; - rpms.s->atr = all_atr_on; + rpms.s->atr = all_attr; rpms.s++; } while (rpms.s < rpms.sen); - if (started) - rpms.s[-1].atr |= all_atr_off; if (nextline(&rpms, 1)) break; if (t == scs) { @@ -1369,7 +1274,7 @@ zrefresh(void) * occurrence. */ rpms.s->chr = ZWC('?'); - rpms.s->atr = all_atr_on | all_atr_off; + rpms.s->atr = all_attr; rpms.s++; } else { /* We can fit it without reaching the end of the line. */ @@ -1377,7 +1282,7 @@ zrefresh(void) * As we don't actually output the WEOF, we attach * any off attributes to the character itself. */ - rpms.s->atr = base_atr_on | base_atr_off; + rpms.s->atr = base_attr; if (ichars > 1) { /* * Glyph includes combining characters. @@ -1393,7 +1298,7 @@ zrefresh(void) while (--width > 0) { rpms.s->chr = WEOF; /* Not used, but be consistent... */ - rpms.s->atr = base_atr_on | base_atr_off; + rpms.s->atr = base_attr; rpms.s++; } } @@ -1410,17 +1315,16 @@ zrefresh(void) #endif ) { /* other control character */ rpms.s->chr = ZWC('^'); - rpms.s->atr = all_atr_on; + rpms.s->atr = all_attr; rpms.s++; if (rpms.s == rpms.sen) { /* text wrapped */ - rpms.s[-1].atr |= all_atr_off; if (nextline(&rpms, 1)) break; } rpms.s->chr = (((unsigned int)*t & ~0x80u) > 31) ? ZWC('?') : (*t | ZWC('@')); - rpms.s->atr = all_atr_on | all_atr_off; + rpms.s->atr = all_attr; rpms.s++; } #ifdef MULTIBYTE_SUPPORT @@ -1432,7 +1336,6 @@ zrefresh(void) char dispchars[11]; char *dispptr = dispchars; wchar_t wc; - int started = 0; #ifdef __STDC_ISO_10646__ if (ZSH_INVALID_WCHAR_TEST(*t)) { @@ -1449,31 +1352,23 @@ zrefresh(void) if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */) { rpms.s->chr = wc; - if (!started) - started = 1; - rpms.s->atr = all_atr_on; + rpms.s->atr = all_attr; rpms.s++; if (rpms.s == rpms.sen) { /* text wrapped */ - if (started) { - rpms.s[-1].atr |= all_atr_off; - started = 0; - } if (nextline(&rpms, 1)) break; } } dispptr++; } - if (started) - rpms.s[-1].atr |= all_atr_off; if (*dispptr) /* nextline said stop processing */ break; } #else else { /* normal character */ rpms.s->chr = *t; - rpms.s->atr = base_atr_on | base_atr_off; + rpms.s->atr = base_attr; rpms.s++; } #endif @@ -1499,13 +1394,12 @@ zrefresh(void) if (statusline) { int outll, outsz; - zattr all_atr_on, all_atr_off; + zattr all_attr; char *statusdup = ztrdup(statusline); ZLE_STRING_T outputline = stringaszleline(statusdup, 0, &outll, &outsz, NULL); - all_atr_on = special_atr_on; - all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on); + all_attr = special_attr; rpms.tosln = rpms.ln + 1; nbuf[rpms.ln][winw + 1] = zr_zr; /* text not wrapped */ @@ -1525,7 +1419,7 @@ zrefresh(void) } if (width > rpms.sen - rpms.s) { rpms.s->chr = ZWC('?'); - rpms.s->atr = all_atr_on | all_atr_off; + rpms.s->atr = all_attr; rpms.s++; } else { rpms.s->chr = *u; @@ -1542,7 +1436,7 @@ zrefresh(void) #endif if (ZC_icntrl(*u)) { /* simplified processing in the status line */ rpms.s->chr = ZWC('^'); - rpms.s->atr = all_atr_on; + rpms.s->atr = all_attr; rpms.s++; if (rpms.s == rpms.sen) { nbuf[rpms.ln][winw + 1] = zr_nl;/* text wrapped */ @@ -1550,7 +1444,7 @@ zrefresh(void) } rpms.s->chr = (((unsigned int)*u & ~0x80u) > 31) ? ZWC('?') : (*u | ZWC('@')); - rpms.s->atr = all_atr_on | all_atr_off; + rpms.s->atr = all_attr; rpms.s++; } else { rpms.s->chr = *u; @@ -1725,7 +1619,6 @@ zrefresh(void) /* output the right-prompt if appropriate */ if (put_rpmpt && !iln && !oput_rpmpt) { - zattr attrchange; moveto(0, winw - rprompt_off - rpromptw); zputs(rpromptbuf, shout); @@ -1735,39 +1628,9 @@ zrefresh(void) zputc(&zr_cr); vcs = 0; } - /* reset character attributes to that set by the main prompt */ - txtchange = pmpt_attr; - /* - * Keep attributes that have actually changed, - * which are ones off in rpmpt_attr and on in - * pmpt_attr, and vice versa. - */ - attrchange = txtchange & - (TXT_ATTR_OFF_FROM_ON(rpmpt_attr) | - TXT_ATTR_ON_FROM_OFF(rpmpt_attr)); - /* - * Careful in case the colour changed. - */ - if (txtchangeisset(txtchange, TXTFGCOLOUR) && - (!txtchangeisset(rpmpt_attr, TXTFGCOLOUR) || - ((txtchange ^ rpmpt_attr) & TXT_ATTR_FG_COL_MASK))) - { - attrchange |= - txtchange & (TXTFGCOLOUR | TXT_ATTR_FG_COL_MASK); - } - if (txtchangeisset(txtchange, TXTBGCOLOUR) && - (!txtchangeisset(rpmpt_attr, TXTBGCOLOUR) || - ((txtchange ^ rpmpt_attr) & TXT_ATTR_BG_COL_MASK))) - { - attrchange |= - txtchange & (TXTBGCOLOUR | TXT_ATTR_BG_COL_MASK); - } - /* - * Now feed these changes into the usual function, - * if necessary. - */ - if (attrchange) - settextattributes(attrchange); + /* reset character attributes to that set by the main prompt */ + treplaceattrs(pmpt_attr); + applytextattributes(0); } } @@ -1782,8 +1645,8 @@ individually */ /* reset character attributes */ if (clearf && postedit) { - if ((txtchange = pmpt_attr ? pmpt_attr : rpmpt_attr)) - settextattributes(txtchange); + treplaceattrs(pmpt_attr ? pmpt_attr : rpmpt_attr); + applytextattributes(0); } clearf = 0; oput_rpmpt = put_rpmpt; @@ -1984,8 +1847,6 @@ refreshline(int ln) /* 3: main display loop - write out the buffer using whatever tricks we can */ for (;;) { - zattr now_off; - #ifdef MULTIBYTE_SUPPORT if ((!nl->chr || nl->chr != WEOF) && (!ol->chr || ol->chr != WEOF)) { #endif @@ -2087,7 +1948,7 @@ refreshline(int ln) * deletions, so turn off text attributes. */ if (first) { - clearattributes(); + cleartextattributes(0); first = 0; } tc_delchars(i); @@ -2176,13 +2037,8 @@ refreshline(int ln) break; do { #endif - /* - * If an attribute was on here but isn't any more, - * output the sequence to turn it off. - */ - now_off = ol->atr & ~nl->atr & TXT_ATTR_ON_MASK; - if (now_off) - settextattributes(TXT_ATTR_OFF_FROM_ON(now_off)); + treplaceattrs(nl->atr); + applytextattributes(0); /* * This is deliberately called if nl->chr is WEOF @@ -2560,8 +2416,8 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) for (t0 = 0; t0 < tmpll; t0++) { unsigned ireg; - zattr base_atr_on = 0, base_atr_off = 0; - zattr all_atr_on, all_atr_off; + zattr base_attr = 0; + zattr all_attr; struct region_highlight *rhp; /* * Calculate attribute based on region. @@ -2576,38 +2432,33 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) offset = predisplaylen; /* increment over it */ if (rhp->start + offset <= t0 && t0 < rhp->end + offset) { - if (base_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) { + if (base_attr & (TXTFGCOLOUR|TXTBGCOLOUR)) { /* keep colour already set */ - base_atr_on |= rhp->atr & ~TXT_ATTR_COLOUR_ON_MASK; + base_attr |= rhp->atr & ~TXT_ATTR_COLOUR_MASK; } else { /* no colour set yet */ - base_atr_on |= rhp->atr; + base_attr |= rhp->atr; } - if (t0 == rhp->end + offset - 1 || - t0 == tmpll - 1) - base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr); } } - if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) { + if (special_attr & (TXTFGCOLOUR|TXTBGCOLOUR)) { /* keep colours from special attributes */ - all_atr_on = special_atr_on | - (base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK); + all_attr = special_attr | + (base_attr & ~TXT_ATTR_COLOUR_MASK); } else { /* keep colours from standard attributes */ - all_atr_on = special_atr_on | base_atr_on; + all_attr = special_attr | base_attr; } - all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on); if (tmpline[t0] == ZWC('\t')) { for (*vp++ = zr_sp; (vp - vbuf) & 7; ) *vp++ = zr_sp; - vp[-1].atr |= base_atr_off; } else if (tmpline[t0] == ZWC('\n')) { vp->chr = ZWC('\\'); - vp->atr = all_atr_on; + vp->atr = all_attr; vp++; vp->chr = ZWC('n'); - vp->atr = all_atr_on | all_atr_off; + vp->atr = all_attr; vp++; #ifdef MULTIBYTE_SUPPORT } else if (WC_ISPRINT(tmpline[t0]) && @@ -2623,7 +2474,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) } } else ichars = 1; - vp->atr = base_atr_on | base_atr_off; + vp->atr = base_attr; if (ichars > 1) addmultiword(vp, tmpline+t0, ichars); else @@ -2631,7 +2482,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) vp++; while (--width > 0) { vp->chr = WEOF; - vp->atr = base_atr_on | base_atr_off; + vp->atr = base_attr; vp++; } t0 += ichars - 1; @@ -2644,11 +2495,11 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) ZLE_INT_T t = tmpline[++t0]; vp->chr = ZWC('^'); - vp->atr = all_atr_on; + vp->atr = all_attr; vp++; vp->chr = (((unsigned int)t & ~0x80u) > 31) ? ZWC('?') : (t | ZWC('@')); - vp->atr = all_atr_on | all_atr_off; + vp->atr = all_attr; vp++; } #ifdef MULTIBYTE_SUPPORT @@ -2656,7 +2507,6 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) char dispchars[11]; char *dispptr = dispchars; wchar_t wc; - int started = 0; if ((unsigned)tmpline[t0] > 0xffffU) { sprintf(dispchars, "<%.08x>", (unsigned)tmpline[t0]); @@ -2666,20 +2516,16 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) while (*dispptr) { if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */) { vp->chr = wc; - if (!started) - started = 1; - vp->atr = all_atr_on; + vp->atr = all_attr; vp++; } dispptr++; } - if (started) - vp[-1].atr |= all_atr_off; } #else else { vp->chr = tmpline[t0]; - vp->atr = base_atr_on | base_atr_off; + vp->atr = base_attr; vp++; } #endif diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index fdd168763..f94bfce3c 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -2426,7 +2426,7 @@ mod_export int printfmt(char *fmt, int n, int dopr, int doesc) { char *p = fmt, nc[DIGBUFSIZE]; - int l = 0, cc = 0, b = 0, s = 0, u = 0, m; + int l = 0, cc = 0; MB_METACHARINIT(); for (; *p; ) { @@ -2437,48 +2437,45 @@ printfmt(char *fmt, int n, int dopr, int doesc) if (idigit(*++p)) arg = zstrtol(p, &p, 10); if (*p) { - m = 0; switch (*p) { case '%': - if (dopr) + if (dopr) { + applytextattributes(0); putc('%', shout); + } cc++; break; case 'n': sprintf(nc, "%d", n); - if (dopr) + if (dopr) { + applytextattributes(0); fputs(nc, shout); + } cc += MB_METASTRWIDTH(nc); break; case 'B': - b = 1; if (dopr) - tcout(TCBOLDFACEBEG); + tsetattrs(TXTBOLDFACE); break; case 'b': - b = 0; m = 1; if (dopr) - tcout(TCALLATTRSOFF); + tunsetattrs(TXTBOLDFACE); break; case 'S': - s = 1; if (dopr) - tcout(TCSTANDOUTBEG); + tsetattrs(TXTSTANDOUT); break; case 's': - s = 0; m = 1; if (dopr) - tcout(TCSTANDOUTEND); + tunsetattrs(TXTSTANDOUT); break; case 'U': - u = 1; if (dopr) - tcout(TCUNDERLINEBEG); + tsetattrs(TXTUNDERLINE); break; case 'u': - u = 0; m = 1; if (dopr) - tcout(TCUNDERLINEEND); + tunsetattrs(TXTUNDERLINE); break; case 'F': case 'K': @@ -2491,18 +2488,19 @@ printfmt(char *fmt, int n, int dopr, int doesc) } else atr = match_colour(NULL, is_fg, arg); if (atr != TXT_ERROR) - set_colour_attribute(atr, is_fg ? COL_SEQ_FG : - COL_SEQ_BG, 0); + tsetattrs(atr); break; case 'f': - set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, 0); + tunsetattrs(TXTFGCOLOUR); break; case 'k': - set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, 0); + tunsetattrs(TXTBGCOLOUR); break; case '{': if (arg) cc += arg; + if (dopr) + applytextattributes(0); for (p++; *p && (*p != '%' || p[1] != '}'); p++) { if (*p == Meta) { p++; @@ -2518,14 +2516,6 @@ printfmt(char *fmt, int n, int dopr, int doesc) p--; break; } - if (dopr && m) { - if (b) - tcout(TCBOLDFACEBEG); - if (s) - tcout(TCSTANDOUTBEG); - if (u) - tcout(TCUNDERLINEBEG); - } } else break; p++; @@ -2533,6 +2523,7 @@ printfmt(char *fmt, int n, int dopr, int doesc) if (*p == '\n') { cc++; if (dopr) { + applytextattributes(0); if (tccan(TCCLEAREOL)) tcout(TCCLEAREOL); else { @@ -2551,6 +2542,7 @@ printfmt(char *fmt, int n, int dopr, int doesc) convchar_t cchar; int clen = MB_METACHARLENCONV(p, &cchar); if (dopr) { + applytextattributes(0); while (clen--) { if (*p == Meta) { p++; diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 2536e9faa..1a580a9e6 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -1250,7 +1250,7 @@ getzlequery(void) REFRESH_ELEMENT re; re.chr = c; re.atr = 0; - zwcputc(&re, NULL); + zwcputc(&re); } return c == ZWC('y'); } diff --git a/Src/builtin.c b/Src/builtin.c index 70a950666..4c295d11f 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -4603,6 +4603,8 @@ bin_print(char *name, char **args, Options ops, int func) /* compute lengths, and interpret according to -P, -D, -e, etc. */ argc = arrlen(args); len = (int *) hcalloc(argc * sizeof(int)); + if (OPT_ISSET(ops, 'P')) + txtunknownattrs = TXT_ATTR_ALL; for (n = 0; n < argc; n++) { /* first \ sequences */ if (fmt || @@ -4633,7 +4635,7 @@ bin_print(char *name, char **args, Options ops, int func) */ char *str = unmetafy( promptexpand(metafy(args[n], len[n], META_NOALLOC), - 0, NULL, NULL, NULL), + 0, NULL, NULL), &len[n]); args[n] = dupstrpfx(str, len[n]); free(str); diff --git a/Src/init.c b/Src/init.c index 9981d059a..75ef2e094 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1654,8 +1654,7 @@ VA_DCL lp = va_arg(ap, char **); - pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL, - NULL), + pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL), &pptlen); write_loop(2, pptbuf, pptlen); free(pptbuf); diff --git a/Src/input.c b/Src/input.c index d55b05696..5a612669b 100644 --- a/Src/input.c +++ b/Src/input.c @@ -402,7 +402,7 @@ inputline(void) char *pptbuf; int pptlen; pptbuf = unmetafy(promptexpand(ingetcpmptl ? *ingetcpmptl : NULL, - 0, NULL, NULL, NULL), &pptlen); + 0, NULL, NULL), &pptlen); write_loop(2, pptbuf, pptlen); free(pptbuf); } diff --git a/Src/loop.c b/Src/loop.c index 88c55dd1a..7df379ecf 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -282,7 +282,7 @@ execselect(Estate state, UNUSED(int do_exec)) /* Keep any user interrupt error status */ errflag = oef | (errflag & ERRFLAG_INT); } else { - str = promptexpand(prompt3, 0, NULL, NULL, NULL); + str = promptexpand(prompt3, 0, NULL, NULL); zputs(str, stderr); free(str); fflush(stderr); diff --git a/Src/prompt.c b/Src/prompt.c index 3cb95039c..880194f87 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -30,10 +30,20 @@ #include "zsh.mdh" #include "prompt.pro" -/* text attribute mask */ +/* current text attributes */ /**/ -mod_export zattr txtattrmask; +mod_export zattr txtcurrentattrs; + +/* pending changes for attributes */ + +/**/ +mod_export zattr txtpendingattrs; + +/* mask of attributes with an unknown state */ + +/**/ +mod_export zattr txtunknownattrs; /* the command stack for use with %_ in prompts */ @@ -160,15 +170,11 @@ promptpath(char *p, int npath, int tilde) * between spacing and non-spacing parts of the prompt, and * Nularg, which (in a non-spacing sequence) indicates a * `glitch' space. - * - * txtchangep gives an integer controlling the attributes of - * the prompt. This is for use in zle to maintain the attributes - * consistently. Other parts of the shell should not need to use it. */ /**/ mod_export char * -promptexpand(char *s, int ns, char *rs, char *Rs, zattr *txtchangep) +promptexpand(char *s, int ns, char *rs, char *Rs) { struct buf_vars new_vars; @@ -212,7 +218,7 @@ promptexpand(char *s, int ns, char *rs, char *Rs, zattr *txtchangep) new_vars.bp1 = NULL; new_vars.truncwidth = 0; - putpromptchar(1, '\0', txtchangep); + putpromptchar(1, '\0'); addbufspc(2); if (new_vars.dontcount) *new_vars.bp++ = Outpar; @@ -253,7 +259,7 @@ parsecolorchar(zattr arg, int is_fg) *ep = '\0'; /* expand the contents of the argument so you can use * %v for example */ - coll = col = promptexpand(bv->fm, 0, NULL, NULL, NULL); + coll = col = promptexpand(bv->fm, 0, NULL, NULL); *ep = oc; arg = match_colour((const char **)&coll, is_fg, 0); free(col); @@ -278,7 +284,7 @@ parsecolorchar(zattr arg, int is_fg) /**/ static int -putpromptchar(int doprint, int endchar, zattr *txtchangep) +putpromptchar(int doprint, int endchar) { char *ss, *hostnam; int t0, arg, test, sep, j, numjobs, len; @@ -430,10 +436,9 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep) /* Don't do the current truncation until we get back */ otruncwidth = bv->truncwidth; bv->truncwidth = 0; - if (!putpromptchar(test == 1 && doprint, sep, - txtchangep) || !*++bv->fm || - !putpromptchar(test == 0 && doprint, ')', - txtchangep)) { + if (!putpromptchar(test == 1 && doprint, sep) || + !*++bv->fm || + !putpromptchar(test == 0 && doprint, ')')) { bv->truncwidth = otruncwidth; return 0; } @@ -519,71 +524,57 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep) unqueue_signals(); break; case 'S': - txtchangeset(txtchangep, TXTSTANDOUT, TXTNOSTANDOUT); - txtset(TXTSTANDOUT); - tsetcap(TCSTANDOUTBEG, TSC_PROMPT); + tsetattrs(TXTSTANDOUT); + applytextattributes(TSC_PROMPT); break; case 's': - txtchangeset(txtchangep, TXTNOSTANDOUT, TXTSTANDOUT); - txtunset(TXTSTANDOUT); - tsetcap(TCSTANDOUTEND, TSC_PROMPT|TSC_DIRTY); + tunsetattrs(TXTSTANDOUT); + applytextattributes(TSC_PROMPT); break; case 'B': - txtchangeset(txtchangep, TXTBOLDFACE, TXTNOBOLDFACE); - txtset(TXTBOLDFACE); - tsetcap(TCBOLDFACEBEG, TSC_PROMPT|TSC_DIRTY); + tsetattrs(TXTBOLDFACE); + applytextattributes(TSC_PROMPT); break; case 'b': - txtchangeset(txtchangep, TXTNOBOLDFACE, TXTBOLDFACE); - txtunset(TXTBOLDFACE); - tsetcap(TCALLATTRSOFF, TSC_PROMPT|TSC_DIRTY); + tunsetattrs(TXTBOLDFACE); + applytextattributes(TSC_PROMPT); break; case 'U': - txtchangeset(txtchangep, TXTUNDERLINE, TXTNOUNDERLINE); - txtset(TXTUNDERLINE); - tsetcap(TCUNDERLINEBEG, TSC_PROMPT); + tsetattrs(TXTUNDERLINE); + applytextattributes(TSC_PROMPT); break; case 'u': - txtchangeset(txtchangep, TXTNOUNDERLINE, TXTUNDERLINE); - txtunset(TXTUNDERLINE); - tsetcap(TCUNDERLINEEND, TSC_PROMPT|TSC_DIRTY); + tunsetattrs(TXTUNDERLINE); + applytextattributes(TSC_PROMPT); break; case 'F': atr = parsecolorchar(arg, 1); - if (!(atr & (TXT_ERROR | TXTNOFGCOLOUR))) { - txtchangeset(txtchangep, atr & TXT_ATTR_FG_ON_MASK, - TXTNOFGCOLOUR | TXT_ATTR_FG_COL_MASK); - txtunset(TXT_ATTR_FG_COL_MASK); - txtset(atr & TXT_ATTR_FG_ON_MASK); - set_colour_attribute(atr, COL_SEQ_FG, TSC_PROMPT); + if (atr && atr != TXT_ERROR) { + tsetattrs(atr); + applytextattributes(TSC_PROMPT); break; } /* else FALLTHROUGH */ case 'f': - txtchangeset(txtchangep, TXTNOFGCOLOUR, TXT_ATTR_FG_ON_MASK); - txtunset(TXT_ATTR_FG_ON_MASK); - set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_PROMPT); + tunsetattrs(TXTFGCOLOUR); + applytextattributes(TSC_PROMPT); break; case 'K': atr = parsecolorchar(arg, 0); - if (!(atr & (TXT_ERROR | TXTNOBGCOLOUR))) { - txtchangeset(txtchangep, atr & TXT_ATTR_BG_ON_MASK, - TXTNOBGCOLOUR | TXT_ATTR_BG_COL_MASK); - txtunset(TXT_ATTR_BG_COL_MASK); - txtset(atr & TXT_ATTR_BG_ON_MASK); - set_colour_attribute(atr, COL_SEQ_BG, TSC_PROMPT); + if (atr && atr != TXT_ERROR) { + tsetattrs(atr); + applytextattributes(TSC_PROMPT); break; } /* else FALLTHROUGH */ case 'k': - txtchangeset(txtchangep, TXTNOBGCOLOUR, TXT_ATTR_BG_ON_MASK); - txtunset(TXT_ATTR_BG_ON_MASK); - set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_PROMPT); + tunsetattrs(TXTBGCOLOUR); + applytextattributes(TSC_PROMPT); break; case '[': if (idigit(*++bv->fm)) arg = zstrtol(bv->fm, &bv->fm, 10); - if (!prompttrunc(arg, ']', doprint, endchar, txtchangep)) + if (!prompttrunc(arg, ']', doprint, endchar)) return *bv->fm; break; case '<': @@ -596,7 +587,7 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep) if (arg <= 0) arg = 1; } - if (!prompttrunc(arg, *bv->fm, doprint, endchar, txtchangep)) + if (!prompttrunc(arg, *bv->fm, doprint, endchar)) return *bv->fm; break; case '{': /*}*/ @@ -1015,7 +1006,7 @@ tsetcap(int cap, int flags) { if (tccan(cap) && !isset(SINGLELINEZLE) && !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) { - switch (flags & TSC_OUTPUT_MASK) { + switch (flags) { case TSC_RAW: tputs(tcstr[cap], 1, putraw); break; @@ -1045,20 +1036,6 @@ tsetcap(int cap, int flags) } break; } - - if (flags & TSC_DIRTY) { - flags &= ~TSC_DIRTY; - if (txtisset(TXTBOLDFACE) && cap != TCBOLDFACEBEG) - tsetcap(TCBOLDFACEBEG, flags); - if (txtisset(TXTSTANDOUT)) - tsetcap(TCSTANDOUTBEG, flags); - if (txtisset(TXTUNDERLINE)) - tsetcap(TCUNDERLINEBEG, flags); - if (txtisset(TXTFGCOLOUR)) - set_colour_attribute(txtattrmask, COL_SEQ_FG, flags); - if (txtisset(TXTBGCOLOUR)) - set_colour_attribute(txtattrmask, COL_SEQ_BG, flags); - } } } @@ -1219,8 +1196,7 @@ countprompt(char *str, int *wp, int *hp, int overf) /**/ static int -prompttrunc(int arg, int truncchar, int doprint, int endchar, - zattr *txtchangep) +prompttrunc(int arg, int truncchar, int doprint, int endchar) { if (arg > 0) { char ch = *bv->fm, *ptr, *truncstr; @@ -1267,7 +1243,7 @@ prompttrunc(int arg, int truncchar, int doprint, int endchar, w = bv->bp - bv->buf; bv->fm++; bv->trunccount = bv->dontcount; - putpromptchar(doprint, endchar, txtchangep); + putpromptchar(doprint, endchar); bv->trunccount = 0; ptr = bv->buf + w; /* putpromptchar() may have realloc()'d */ *bv->bp = '\0'; @@ -1547,7 +1523,7 @@ prompttrunc(int arg, int truncchar, int doprint, int endchar, * With bv->truncwidth set to zero, we always reach endchar * * (or the terminating NULL) this time round. * */ - if (!putpromptchar(doprint, endchar, txtchangep)) + if (!putpromptchar(doprint, endchar)) return 0; } /* Now we have to trick it into matching endchar again */ @@ -1585,6 +1561,122 @@ cmdpop(void) cmdsp--; } +/* functions for handling attributes */ + +/**/ +mod_export void +applytextattributes(int flags) +{ + zattr change = txtcurrentattrs ^ txtpendingattrs; + zattr keepon = ~change & txtpendingattrs & TXT_ATTR_ALL; + zattr turnoff = change & ~txtpendingattrs & TXT_ATTR_ALL; + int keepcount, turncount = 0; + + /* bail out early if we wouldn't do anything */ + if (!change) + return; + + if (txtunknownattrs) { + txtunknownattrs &= ~change; /* changes cease to be unknown */ + /* can't turn unknown attrs back on so avoid wiping them */ + keepcount = 1; + } else { + /* If we want to turn off more attributes than we want to keep on + * then it takes fewer termcap sequences to just turn off all the + * attributes. */ + for (keepcount = 0; keepon; keepcount++) /* count bits */ + keepon &= keepon - 1; + for (; turnoff; turncount++) + turnoff &= turnoff - 1; + } + + if (keepcount < turncount || (change & ~txtpendingattrs & TXTBOLDFACE)) { + tsetcap(TCALLATTRSOFF, flags); + /* this cleared all attributes, may need to restore some */ + change = txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs; + txtunknownattrs = 0; + } else { + if (change & ~txtpendingattrs & TXTSTANDOUT) { + tsetcap(TCSTANDOUTEND, flags); + /* in some cases, that clears all attributes */ + change = (txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs) | + (TXTUNDERLINE & change); + } + if (change & ~txtpendingattrs & TXTUNDERLINE) { + tsetcap(TCUNDERLINEEND, flags); + /* in some cases, that clears all attributes */ + change = txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs; + } + } + if (change & txtpendingattrs & TXTBOLDFACE) + tsetcap(TCBOLDFACEBEG, flags); + if (change & txtpendingattrs & TXTSTANDOUT) + tsetcap(TCSTANDOUTBEG, flags); + if (change & txtpendingattrs & TXTUNDERLINE) + tsetcap(TCUNDERLINEBEG, flags); + + if (change & TXT_ATTR_FG_MASK) + set_colour_attribute(txtpendingattrs, COL_SEQ_FG, flags); + if (change & TXT_ATTR_BG_MASK) + set_colour_attribute(txtpendingattrs, COL_SEQ_BG, flags); + + txtcurrentattrs = txtpendingattrs; +} + +/**/ +mod_export void +cleartextattributes(int flags) +{ + treplaceattrs(0); + applytextattributes(flags); +} + +/**/ +mod_export void +treplaceattrs(zattr newattrs) +{ + if (txtunknownattrs) { + /* Set current attributes to the opposite of the new ones + * for any that are unknown so that applytextattributes() + * detects them as changed. */ + txtcurrentattrs &= ~txtunknownattrs; + txtcurrentattrs |= txtunknownattrs & ~newattrs; + } + + txtpendingattrs = newattrs; +} + +/**/ +mod_export void +tsetattrs(zattr newattrs) +{ + /* assume any unknown attributes that we're now setting were unset */ + txtcurrentattrs &= ~(newattrs & txtunknownattrs); + + txtpendingattrs |= newattrs & TXT_ATTR_ALL; + if (newattrs & TXTFGCOLOUR) { + txtpendingattrs &= ~TXT_ATTR_FG_MASK; + txtpendingattrs |= newattrs & TXT_ATTR_FG_MASK; + } + if (newattrs & TXTBGCOLOUR) { + txtpendingattrs &= ~TXT_ATTR_BG_MASK; + txtpendingattrs |= newattrs & TXT_ATTR_BG_MASK; + } +} + +/**/ +mod_export void +tunsetattrs(zattr newattrs) +{ + /* assume any unknown attributes that we're now unsetting were set */ + txtcurrentattrs |= newattrs & txtunknownattrs; + + txtpendingattrs &= ~(newattrs & TXT_ATTR_ALL); + if (newattrs & TXTFGCOLOUR) + txtpendingattrs &= ~TXT_ATTR_FG_MASK; + if (newattrs & TXTBGCOLOUR) + txtpendingattrs &= ~TXT_ATTR_BG_MASK; +} /***************************************************************************** * Utilities dealing with colour and other forms of highlighting. @@ -1607,7 +1699,7 @@ struct highlight { }; static const struct highlight highlights[] = { - { "none", 0, TXT_ATTR_ON_MASK }, + { "none", 0, TXT_ATTR_ALL }, { "bold", TXTBOLDFACE, 0 }, { "standout", TXTSTANDOUT, 0 }, { "underline", TXTUNDERLINE, 0 }, @@ -1645,8 +1737,8 @@ match_named_colour(const char **teststrp) * Match just the colour part of a highlight specification. * If teststrp is NULL, use the already parsed numeric colour. * Return the attributes to set in the attribute variable. - * Return -1 for out of range. Does not check the character - * following the colour specification. + * Return TXT_ERROR for out of range. Does not check the + * character following the colour specification. */ /**/ @@ -1693,10 +1785,8 @@ match_colour(const char **teststrp, int is_fg, int colour) } } else if ((named = ialpha(**teststrp))) { colour = match_named_colour(teststrp); - if (colour == 8) { - /* default */ - return is_fg ? TXTNOFGCOLOUR : TXTNOBGCOLOUR; - } + if (colour == 8) /* default */ + return 0; if (colour < 0) return TXT_ERROR; } @@ -2024,13 +2114,13 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) if (fg_bg == COL_SEQ_FG) { colour = txtchangeget(atr, TXT_ATTR_FG_COL); tc = TCFGCOLOUR; - def = txtchangeisset(atr, TXTNOFGCOLOUR); - use_truecolor = txtchangeisset(atr, TXT_ATTR_FG_24BIT); + def = !(atr & TXTFGCOLOUR); + use_truecolor = atr & TXT_ATTR_FG_24BIT; } else { colour = txtchangeget(atr, TXT_ATTR_BG_COL); tc = TCBGCOLOUR; - def = txtchangeisset(atr, TXTNOBGCOLOUR); - use_truecolor = txtchangeisset(atr, TXT_ATTR_BG_24BIT); + def = !(atr & TXTBGCOLOUR); + use_truecolor = atr & TXT_ATTR_BG_24BIT; } /* Test if current zle_highlight settings are customized, or diff --git a/Src/subst.c b/Src/subst.c index b8e4023e1..897188862 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -3716,6 +3716,8 @@ colonsubscript: if (presc) { int ops = opts[PROMPTSUBST], opb = opts[PROMPTBANG]; int opp = opts[PROMPTPERCENT]; + zattr savecurrent = txtcurrentattrs; + zattr saveunknown = txtunknownattrs; if (presc < 2) { opts[PROMPTPERCENT] = 1; @@ -3738,7 +3740,8 @@ colonsubscript: for (; *ap; ap++) { char *tmps; untokenize(*ap); - tmps = promptexpand(*ap, 0, NULL, NULL, NULL); + txtunknownattrs = TXT_ATTR_ALL; + tmps = promptexpand(*ap, 0, NULL, NULL); *ap = dupstring(tmps); free(tmps); } @@ -3747,10 +3750,14 @@ colonsubscript: if (!copied) val = dupstring(val), copied = 1; untokenize(val); - tmps = promptexpand(val, 0, NULL, NULL, NULL); + txtunknownattrs = TXT_ATTR_ALL; + tmps = promptexpand(val, 0, NULL, NULL); val = dupstring(tmps); free(tmps); } + + txtpendingattrs = txtcurrentattrs = savecurrent; + txtunknownattrs = saveunknown; opts[PROMPTSUBST] = ops; opts[PROMPTBANG] = opb; opts[PROMPTPERCENT] = opp; diff --git a/Src/utils.c b/Src/utils.c index 32492a93b..55f2d1ab0 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1543,7 +1543,8 @@ preprompt(void) if (!eolmark) eolmark = "%B%S%#%s%b"; opts[PROMPTPERCENT] = 1; - str = promptexpand(eolmark, 1, NULL, NULL, NULL); + txtunknownattrs = TXT_ATTR_ALL; + str = promptexpand(eolmark, 1, NULL, NULL); countprompt(str, &w, 0, -1); opts[PROMPTPERCENT] = percents; zputs(str, shout); @@ -1713,7 +1714,7 @@ printprompt4(void) opts[XTRACE] = 0; unmetafy(s, &l); s = unmetafy(promptexpand(metafy(s, l, META_NOALLOC), - 0, NULL, NULL, NULL), &l); + 0, NULL, NULL), &l); opts[XTRACE] = t; fprintf(xtrerr, "%s", s); @@ -3211,7 +3212,7 @@ spckword(char **s, int hist, int cmd, int ask) x = 'n'; } else if (shout) { char *pptbuf; - pptbuf = promptexpand(sprompt, 0, best, guess, NULL); + pptbuf = promptexpand(sprompt, 0, best, guess); zputs(pptbuf, shout); free(pptbuf); fflush(shout); diff --git a/Src/zsh.h b/Src/zsh.h index b035a1184..35ae033e3 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -2681,25 +2681,8 @@ struct ttyinfo { #define TXTFGCOLOUR 0x0008 #define TXTBGCOLOUR 0x0010 -#define TXT_ATTR_ON_MASK 0x001F - -#define txtisset(X) (txtattrmask & (X)) -#define txtset(X) (txtattrmask |= (X)) -#define txtunset(X) (txtattrmask &= ~(X)) - -#define TXTNOBOLDFACE 0x0020 -#define TXTNOSTANDOUT 0x0040 -#define TXTNOUNDERLINE 0x0080 -#define TXTNOFGCOLOUR 0x0100 -#define TXTNOBGCOLOUR 0x0200 - -#define TXT_ATTR_OFF_MASK 0x03E0 -/* Bits to shift off right to get on */ -#define TXT_ATTR_OFF_ON_SHIFT 5 -#define TXT_ATTR_OFF_FROM_ON(attr) \ - (((attr) & TXT_ATTR_ON_MASK) << TXT_ATTR_OFF_ON_SHIFT) -#define TXT_ATTR_ON_FROM_OFF(attr) \ - (((attr) & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT) +#define TXT_ATTR_ALL 0x001F + /* * Indicates to zle_refresh.c that the character entry is an * index into the list of multiword symbols. @@ -2707,7 +2690,7 @@ struct ttyinfo { #define TXT_MULTIWORD_MASK 0x0400 /* used when, e.g an invalid colour is specified */ -#define TXT_ERROR 0x0800 +#define TXT_ERROR 0xF00000F000000800 /* Mask for colour to use in foreground */ #define TXT_ATTR_FG_COL_MASK 0x000000FFFFFF0000 @@ -2723,26 +2706,19 @@ struct ttyinfo { /* Flag to indicate that background is a 24-bit colour */ #define TXT_ATTR_BG_24BIT 0x8000 -/* Things to turn on, including values for the colour elements */ -#define TXT_ATTR_ON_VALUES_MASK \ - (TXT_ATTR_ON_MASK|TXT_ATTR_FG_COL_MASK|TXT_ATTR_BG_COL_MASK|\ - TXT_ATTR_FG_24BIT|TXT_ATTR_BG_24BIT) - /* Mask out everything to do with setting a foreground colour */ -#define TXT_ATTR_FG_ON_MASK \ +#define TXT_ATTR_FG_MASK \ (TXTFGCOLOUR|TXT_ATTR_FG_COL_MASK|TXT_ATTR_FG_24BIT) /* Mask out everything to do with setting a background colour */ -#define TXT_ATTR_BG_ON_MASK \ +#define TXT_ATTR_BG_MASK \ (TXTBGCOLOUR|TXT_ATTR_BG_COL_MASK|TXT_ATTR_BG_24BIT) /* Mask out everything to do with activating colours */ -#define TXT_ATTR_COLOUR_ON_MASK \ - (TXT_ATTR_FG_ON_MASK|TXT_ATTR_BG_ON_MASK) +#define TXT_ATTR_COLOUR_MASK \ + (TXT_ATTR_FG_MASK|TXT_ATTR_BG_MASK) -#define txtchangeisset(T,X) ((T) & (X)) #define txtchangeget(T,A) (((T) & A ## _MASK) >> A ## _SHIFT) -#define txtchangeset(T, X, Y) ((void)(T && (*T &= ~(Y), *T |= (X)))) /* * For outputting sequences to change colour: specify foreground @@ -2750,7 +2726,6 @@ struct ttyinfo { */ #define COL_SEQ_FG (0) #define COL_SEQ_BG (1) -#define COL_SEQ_COUNT (2) struct color_rgb { unsigned int red, green, blue; @@ -2766,11 +2741,7 @@ enum { /* Raw output: use stdout rather than shout */ TSC_RAW = 0x0001, /* Output to current prompt buffer: only used when assembling prompt */ - TSC_PROMPT = 0x0002, - /* Mask to get the output mode */ - TSC_OUTPUT_MASK = 0x0003, - /* Change needs reset of other attributes */ - TSC_DIRTY = 0x0004 + TSC_PROMPT = 0x0002 }; /****************************************/ diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst index 6879e6fd1..a0abb7e1d 100644 --- a/Test/D01prompt.ztst +++ b/Test/D01prompt.ztst @@ -258,6 +258,19 @@ fi 0:Equivalence of terminal colour settings (background colour) + A1=${(%):-%s} + A2=${(%):-%u} + A3=${(%):-%s%u%s} + [[ $A3 = $A1$A2 && -n $A1 && -n $A2 ]] +0:Attribute optimisation - preserve initial disabling of attribute but drop useless later one + + : ${(%):-%K{blue}} + A1="${(%):-%b}x" + : ${(%):-%k} + A2="${(%):-%b}x" + [[ $A1 = $A2 && -n $A1 && -n $A2 ]] +0:Don't restore attributes from earlier substitution after disabling bold + (RPS1=foo; echo $RPS1 $RPROMPT) (RPS2=bar; echo $RPS2 $RPROMPT2) -fD:RPS1 and RPROMPT are aliases (regression from 5.0.6) (workers/49600) diff --git a/Test/X04zlehighlight.ztst b/Test/X04zlehighlight.ztst index f84c02505..6d9ca4a48 100644 --- a/Test/X04zlehighlight.ztst +++ b/Test/X04zlehighlight.ztst @@ -79,7 +79,7 @@ zpty_line 1 p # the line of interest, preserving escapes ("p") zpty_stop 0:region highlight - standout overlapping on other region_highlight entry ->0m27m24mtr7mu27me word2 word3 +>0m27m24mtr7mu0me word2 word3 zpty_start zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=green" ); }' @@ -90,7 +90,7 @@ zpty_line 1 p # the line of interest, preserving escapes ("p") zpty_stop 0:basic region_highlight with 8 colors ->0m27m24mCDE|32|trueCDE|39| +>0m27m24mCDE|32|true0m zpty_start zpty_input 'rh_widget() { region_highlight+=( "0 4 fg=green memo=someplugin" ); typeset -p region_highlight }' @@ -145,7 +145,7 @@ zpty_line 1 p # the line of interest, preserving escapes ("p") zpty_stop 0:basic region_highlight with true-color (hex-triplets) ->0m27m24m38;2;4;8;16mtrueCDE|39| +>0m27m24m38;2;4;8;16mtrue0m zpty_start zpty_input 'zmodload zsh/nearcolor' @@ -157,7 +157,7 @@ zpty_line 1 p # the line of interest, preserving escapes ("p") zpty_stop 0:basic region_highlight with near-color (hex-triplets at input) ->0m27m24mCDE|3232|trueCDE|39| +>0m27m24mCDE|3232|true0m zpty_start zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=green" ); rh2; }' @@ -169,7 +169,7 @@ zpty_line 1 p # the line of interest, preserving escapes ("p") zpty_stop 0:overlapping region_highlight with 8 colors ->0m27m24mCDE|32|tCDE|31|rCDE|39|CDE|32|ueCDE|39| +>0m27m24mCDE|32|tCDE|31|rCDE|32|ue0m zpty_start zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=#00cc00" ); rh2; }' @@ -181,7 +181,7 @@ zpty_line 1 p # the line of interest, preserving escapes ("p") zpty_stop 0:overlapping region_highlight with true-color ->0m27m24m38;2;0;204;0mt38;2;204;0;0mrCDE|39|38;2;0;204;0mueCDE|39| +>0m27m24m38;2;0;204;0mt38;2;204;0;0mr38;2;0;204;0mue0m zpty_start zpty_input 'zmodload zsh/nearcolor' @@ -194,7 +194,7 @@ zpty_line 1 p # the line of interest, preserving escapes ("p") zpty_stop 0:overlapping region_highlight with near-color (hex-triplets at input) ->0m27m24mCDE|340|tCDE|3160|rCDE|39|CDE|340|ueCDE|39| +>0m27m24mCDE|340|tCDE|3160|rCDE|340|ue0m zpty_start zpty_input 'f () { zle clear-screen; zle g -f nolast; BUFFER=": ${(q)LASTWIDGET}" }; zle -N f' -- 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/builtin.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 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/builtin.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 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/builtin.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 d2768f2f886c34dafa535b46796948b6a860d974 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 20 Feb 2023 10:32:40 -0800 Subject: 51431: "typeset -p" shouldn't change parameter flags --- ChangeLog | 4 ++++ Src/builtin.c | 46 +++++++++++++++++++++++++++++++++------------- 2 files changed, 37 insertions(+), 13 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 8cca5d1e4..52491f4b5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-02-20 Bart Schaefer + + * 51431: Src/builtin.c: "typeset -p" shouldn't change parameter flags + 2023-02-19 Oliver Kiddle * 51456: Completion/Unix/Command/_git: complete only modified diff --git a/Src/builtin.c b/Src/builtin.c index 47b337edc..11c1ab3a4 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2106,15 +2106,27 @@ typeset_single(char *cname, char *pname, Param pm, int func, return NULL; } tc = 1; - usepm = 0; + if (OPT_MINUS(ops,'p')) + usepm = (on & pm->node.flags); + else if (OPT_PLUS(ops,'p')) + usepm = (off & pm->node.flags); + else + usepm = 0; } else if (usepm || newspecial != NS_NONE) { int chflags = ((off & pm->node.flags) | (on & ~pm->node.flags)) & (PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED| PM_ARRAY|PM_TIED|PM_AUTOLOAD); /* keep the parameter if just switching between floating types */ - if ((tc = chflags && chflags != (PM_EFLOAT|PM_FFLOAT))) - usepm = 0; + if ((tc = chflags && chflags != (PM_EFLOAT|PM_FFLOAT))) { + if (OPT_MINUS(ops,'p')) + usepm = (on & pm->node.flags); + else if (OPT_PLUS(ops,'p')) + usepm = (off & pm->node.flags); + else + usepm = 0; + } + } /* @@ -2166,13 +2178,16 @@ typeset_single(char *cname, char *pname, Param pm, int func, } if (err) { - zerrnam(cname, "%s: can't change type of a special parameter", - pname); + if (!OPT_ISSET(ops,'p')) + zerrnam(cname, + "%s: can't change type of a special parameter", + pname); return NULL; } } else if (pm->node.flags & PM_AUTOLOAD) { - zerrnam(cname, "%s: can't change type of autoloaded parameter", - pname); + if (!OPT_ISSET(ops,'p')) + zerrnam(cname, "%s: can't change type of autoloaded parameter", + pname); return NULL; } } @@ -2208,6 +2223,10 @@ typeset_single(char *cname, char *pname, Param pm, int func, * ii. we are creating a new local parameter */ if (usepm) { + if (OPT_MINUS(ops,'p') && on && !(on & pm->node.flags)) + return NULL; + else if (OPT_PLUS(ops,'p') && off && !(off & pm->node.flags)) + return NULL; if ((asg->flags & ASG_ARRAY) ? !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) : (asg->value.scalar && (PM_TYPE(pm->node.flags & @@ -2254,6 +2273,10 @@ typeset_single(char *cname, char *pname, Param pm, int func, arrfixenv(pm->node.nam, x); } } + if (OPT_ISSET(ops,'p')) { + paramtab->printnode(&pm->node, PRINT_TYPESET); + return pm; + } if (usepm == 2) /* do not change the PM_UNSET flag */ pm->node.flags = (pm->node.flags | (on & ~PM_READONLY)) & ~off; else { @@ -2262,9 +2285,8 @@ typeset_single(char *cname, char *pname, Param pm, int func, */ if (!(on & PM_READONLY) || !isset(POSIXBUILTINS)) off |= PM_UNSET; - if (!OPT_ISSET(ops, 'p')) - pm->node.flags = (pm->node.flags | - (on & ~PM_READONLY)) & ~off; + 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)) @@ -2310,8 +2332,6 @@ typeset_single(char *cname, char *pname, Param pm, int func, if (errflag) return NULL; pm->node.flags |= (on & PM_READONLY); - if (OPT_ISSET(ops,'p')) - paramtab->printnode(&pm->node, PRINT_TYPESET); return pm; } @@ -2328,7 +2348,7 @@ typeset_single(char *cname, char *pname, Param pm, int func, * or we're converting the type of a parameter. In the * last case only, we need to delete the old parameter. */ - if (tc) { + if (tc && !OPT_ISSET(ops,'p')) { /* Maintain existing readonly/exported status... */ on |= ~off & (PM_READONLY|PM_EXPORTED) & pm->node.flags; /* ...but turn off existing readonly so we can delete it */ -- 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/builtin.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 4b7a9fd0ecb750646cac76c41383f8391cd0fdd9 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 14 Mar 2023 20:51:15 -0700 Subject: 51573: additional "typset -p -m" fix for namespaces The "-m pattern" option is supposed to enable printing namespaces, but that didn't work when combined with -p. The -p option could also cause an unset parameter to become set if a named reference pointed at it. --- ChangeLog | 4 ++++ Src/builtin.c | 8 +++++--- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 7ecc36a67..f16bd7811 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-03-14 Bart Schaefer + + * 51573: Src/builtin.c: additional "typset -p -m" fix for namespaces + 2023-03-13 Bart Schaefer * 51572: Functions/Misc/run-help: fix error when running standalone diff --git a/Src/builtin.c b/Src/builtin.c index d99802f5f..f38a54936 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2236,12 +2236,12 @@ typeset_single(char *cname, char *pname, Param pm, int func, } on &= ~PM_LOCAL; if (!on && !roff && !ASG_VALUEP(asg)) { + int with_ns = OPT_ISSET(ops,'m') ? PRINT_WITH_NAMESPACE : 0; if (OPT_ISSET(ops,'p')) - paramtab->printnode(&pm->node, PRINT_TYPESET); + paramtab->printnode(&pm->node, PRINT_TYPESET|with_ns); else if (!OPT_ISSET(ops,'g') && (unset(TYPESETSILENT) || OPT_ISSET(ops,'m'))) - paramtab->printnode(&pm->node, - PRINT_INCLUDEVALUE|PRINT_WITH_NAMESPACE); + paramtab->printnode(&pm->node, PRINT_INCLUDEVALUE|with_ns); return pm; } if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) { @@ -2502,6 +2502,8 @@ typeset_single(char *cname, char *pname, Param pm, int func, "%s: inconsistent array element or slice assignment", pname); return NULL; } + } else if (!pm && OPT_ISSET(ops,'p')) { + return NULL; } /* * As we can hide existing parameters, we allow a name if -- cgit v1.2.3 From 6763f45e77b130fdcecd244440552095b5ee23b5 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Wed, 22 Mar 2023 10:24:11 +0000 Subject: 58586: print "%s" with invalid multibyte character Treat each byte that is invalid or part of an incopmlete set as a single byte. --- ChangeLog | 6 ++++++ Src/builtin.c | 27 ++++++++++++++------------- 2 files changed, 20 insertions(+), 13 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index ec32f1029..c575a0b3c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2023-03-22 Peter Stephenson + + * 51586: Src/builtin.c: When printf "%s" encounters a byte + that's not part of a vaild multibyte character it should handle + it a single byte at a time. + 2023-03-16 Oliver Kiddle * 51583: Completion/Unix/Command/_git: update completion of diff --git a/Src/builtin.c b/Src/builtin.c index f38a54936..e4f356803 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -5329,20 +5329,21 @@ bin_print(char *name, char **args, Options ops, int func) #ifdef MULTIBYTE_SUPPORT if (isset(MULTIBYTE)) { chars = mbrlen(ptr, lleft, &mbs); - if (chars < 0) { - /* - * Invalid/incomplete character at this - * point. Assume all the rest are a - * single byte. That's about the best we - * can do. - */ - lchars += lleft; - lbytes = (ptr - b) + lleft; - break; - } else if (chars == 0) { - /* NUL, handle as real character */ + /* + * chars <= 0 means one of + * + * 0: NUL, handle as real character + * + * -1: MB_INVALID: Assume this is + * a single character as we do + * elsewhere in the code. + * + * -2: MB_INCOMPLETE: We're not waiting + * for input on this occasion, so + * just treat this as invalid. + */ + if (chars <= 0) chars = 1; - } } else /* use the non-multibyte code below */ #endif -- cgit v1.2.3 From 5ead24c8819efb3cd0d24a6ff4f178e1a5defee9 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 9 Jul 2023 19:28:28 -0700 Subject: 51890: fix "whence -wa" for multiple arguments --- ChangeLog | 4 ++++ Src/builtin.c | 1 + 2 files changed, 5 insertions(+) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 6a2801e9b..531503312 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-07-09 Bart Schaefer + + * 51890: Src/builtin.c: fix "whence -wa" for multiple arguments + 2023-06-26 Jun-ichi Takimoto * 51889: Src/Modules/zftp.c, Src/mkbltnmlst.sh: enable loading a diff --git a/Src/builtin.c b/Src/builtin.c index e4f356803..669a47092 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -4046,6 +4046,7 @@ bin_whence(char *nam, char **argv, Options ops, int func) /* Take arguments literally -- do not glob */ queue_signals(); for (; *argv; argv++) { + informed = 0; if (!OPT_ISSET(ops,'p') && !allmatched) { char *suf; -- cgit v1.2.3 From aa85564319f4d511fae04a3cdf7a0b1fba1f67fe Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 26 Jul 2023 19:54:30 -0700 Subject: 51969: read -d and -s should not reset terminal state when stdin is redirected --- ChangeLog | 3 +++ Src/builtin.c | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 74496543f..a02e25218 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-07-26 Bart Schaefer + * 51969: Src/builtin.c: read -d and -s should not reset terminal + state when stdin is redirected + * 51949 (tweak per 51950): Src/Zle/zle_main.c: correct Thingy refcount in raw_getbyte() diff --git a/Src/builtin.c b/Src/builtin.c index 669a47092..1568cf44c 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -6483,7 +6483,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) } else readfd = izle = 0; - if (OPT_ISSET(ops,'s') && SHTTY != -1) { + if (OPT_ISSET(ops,'s') && SHTTY == readfd) { struct ttyinfo ti; gettyinfo(&ti); saveti = ti; @@ -6531,7 +6531,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) delim = (unsigned char) ((delimstr[0] == Meta) ? delimstr[1] ^ 32 : delimstr[0]); #endif - if (SHTTY != -1) { + if (SHTTY == readfd) { struct ttyinfo ti; gettyinfo(&ti); if (! resettty) { @@ -6691,7 +6691,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) /* dispose of result appropriately, etc. */ if (isem) while (val > 0 && read(SHTTY, &d, 1) == 1 && d != '\n'); - else { + else if (resettty) { settyinfo(&shttyinfo); resettty = 0; } -- 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/builtin.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 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/builtin.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 3406089647393c7593e37ee0661a179a9d848c49 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 24 Jan 2024 17:27:41 -0800 Subject: 52468: save and restore state of correct TTY when using read -s / -d --- ChangeLog | 5 +++++ Src/builtin.c | 30 +++++++++++++++--------------- Src/utils.c | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 15 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 043539756..b64c62830 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-01-24 Bart Schaefer + + * 52468: Src/builtin.c, Src/utils.c: save and restore state of + correct TTY when using read -s / -d + 2024-01-14 Matthew Martin * github #109: Wu Zhenyu: Completion/Linux/Command/_valgrind: Fix diff --git a/Src/builtin.c b/Src/builtin.c index 9e08a1dbc..5c5adb9d3 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -6506,10 +6506,10 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) } else readfd = izle = 0; - if (OPT_ISSET(ops,'s') && SHTTY == readfd) { + if (OPT_ISSET(ops,'s') && isatty(readfd)) { struct ttyinfo ti; memset(&ti, 0, sizeof(struct ttyinfo)); - gettyinfo(&ti); + fdgettyinfo(readfd, &ti); saveti = ti; resettty = 1; #ifdef HAS_TIO @@ -6517,7 +6517,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) #else ti.sgttyb.sg_flags &= ~ECHO; #endif - settyinfo(&ti); + fdsettyinfo(readfd, &ti); } /* handle prompt */ @@ -6555,9 +6555,9 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) delim = (unsigned char) ((delimstr[0] == Meta) ? delimstr[1] ^ 32 : delimstr[0]); #endif - if (SHTTY == readfd) { + if (isatty(readfd)) { struct ttyinfo ti; - gettyinfo(&ti); + fdgettyinfo(readfd, &ti); if (! resettty) { saveti = ti; resettty = 1; @@ -6569,7 +6569,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) #else ti.sgttyb.sg_flags |= CBREAK; #endif - settyinfo(&ti); + fdsettyinfo(readfd, &ti); } } if (OPT_ISSET(ops,'t')) { @@ -6604,8 +6604,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) timeout)) { if (keys && !zleactive && !isem) settyinfo(&shttyinfo); - else if (resettty && SHTTY != -1) - settyinfo(&saveti); + else if (resettty) + fdsettyinfo(readfd, &saveti); if (haso) { if (shout) fclose(shout); @@ -6717,7 +6717,7 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) if (isem) while (val > 0 && read(SHTTY, &d, 1) == 1 && d != '\n'); else if (resettty) { - settyinfo(&shttyinfo); + fdsettyinfo(readfd, &saveti); resettty = 0; } if (haso) { @@ -6746,8 +6746,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) setsparam(reply, metafy(buf, bptr - buf, META_REALLOC)); else zfree(buf, bptr - buf + 1); - if (resettty && SHTTY != -1) - settyinfo(&saveti); + if (resettty) + fdsettyinfo(readfd, &saveti); return eof; } @@ -6957,8 +6957,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) *pp++ = NULL; setaparam(reply, p); } - if (resettty && SHTTY != -1) - settyinfo(&saveti); + if (resettty) + fdsettyinfo(readfd, &saveti); return c == EOF; } buf = bptr = (char *)zalloc(bsiz = 64); @@ -7086,8 +7086,8 @@ bin_read(char *name, char **args, Options ops, UNUSED(int func)) break; } *bptr = '\0'; - if (resettty && SHTTY != -1) - settyinfo(&saveti); + if (resettty) + fdsettyinfo(readfd, &saveti); /* final assignment of reply, etc. */ if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E')) { zputs(buf, stdout); diff --git a/Src/utils.c b/Src/utils.c index 0f66984cd..1a4f4c14b 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1730,6 +1730,13 @@ freestr(void *a) /**/ mod_export void gettyinfo(struct ttyinfo *ti) +{ + fdgettyinfo(SHTTY, ti); +} + +/**/ +mod_export void +fdgettyinfo(int SHTTY, struct ttyinfo *ti) { if (SHTTY != -1) { #ifdef HAVE_TERMIOS_H @@ -1755,6 +1762,13 @@ gettyinfo(struct ttyinfo *ti) /**/ mod_export void settyinfo(struct ttyinfo *ti) +{ + fdsettyinfo(SHTTY, ti); +} + +/**/ +mod_export void +fdsettyinfo(int SHTTY, struct ttyinfo *ti) { if (SHTTY != -1) { #ifdef HAVE_TERMIOS_H -- cgit v1.2.3 From 8c59638522d8ed06cb962d41c11d1fade27abaa9 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 17 Feb 2024 20:27:56 -0800 Subject: 52556: fix crash when changing type of unset referent via named reference --- ChangeLog | 5 +++++ Src/builtin.c | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index e9bc0d9c5..11cc8dd6b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-02-17 Bart Schaefer + + * 52556: Src/builtin.c: fix crash when applying a type change via + a named reference when the referent has been declared but unset + 2024-02-16 Mikael Magnusson * 52546: Functions/Zle/incarg: incarg: avoid unneeded subshell diff --git a/Src/builtin.c b/Src/builtin.c index 5c5adb9d3..dd352c146 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2031,8 +2031,10 @@ typeset_single(char *cname, char *pname, Param pm, int func, char *subscript; if (pm && (pm->node.flags & PM_NAMEREF) && !((off|on) & PM_NAMEREF)) { - if (!(off & PM_NAMEREF)) - pm = (Param)resolve_nameref(pm, NULL); + if (!(off & PM_NAMEREF)) { + 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. * -- cgit v1.2.3 From 4c0ebc155ebab1e2babda2a218cba1131cf74109 Mon Sep 17 00:00:00 2001 From: Stephane Chazelas Date: Sat, 24 Feb 2024 20:41:33 -0800 Subject: 52591: printf builtin must pass metafied strings to math evaluation --- ChangeLog | 5 +++++ Src/builtin.c | 10 ++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index d520ec8c5..00a3c65ab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-02-24 Bart Schaefer + + * Stephane: 52591: Src/builtin.c: printf builtin must pass + metafied strings to math evaluation + 2024-02-23 Bart Schaefer * 52583: Src/params.c, Test/V10private.ztst: do an extra check diff --git a/Src/builtin.c b/Src/builtin.c index dd352c146..f72d14da4 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -5247,7 +5247,8 @@ bin_print(char *name, char **args, Options ops, int func) } } if (*argp) { - width = (int)mathevali(*argp++); + width = (int)mathevali(metafy(*argp, len[argp - args], META_USEHEAP)); + argp++; if (errflag) { errflag &= ~ERRFLAG_ERROR; ret = 1; @@ -5281,7 +5282,8 @@ bin_print(char *name, char **args, Options ops, int func) } if (*argp) { - prec = (int)mathevali(*argp++); + prec = (int)mathevali(metafy(*argp, len[argp - args], META_USEHEAP)); + argp++; if (errflag) { errflag &= ~ERRFLAG_ERROR; ret = 1; @@ -5465,7 +5467,7 @@ bin_print(char *name, char **args, Options ops, int func) *d++ = 'l'; #endif *d++ = 'l', *d++ = *c, *d = '\0'; - zlongval = (curarg) ? mathevali(curarg) : 0; + zlongval = (curarg) ? mathevali(metafy(curarg, curlen, META_HEAPDUP)) : 0; if (errflag) { zlongval = 0; errflag &= ~ERRFLAG_ERROR; @@ -5516,7 +5518,7 @@ bin_print(char *name, char **args, Options ops, int func) if (!curarg) zulongval = (zulong)0; else if (!zstrtoul_underscore(curarg, &zulongval)) - zulongval = mathevali(curarg); + zulongval = mathevali(metafy(curarg, curlen, META_HEAPDUP)); if (errflag) { zulongval = 0; errflag &= ~ERRFLAG_ERROR; -- 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/builtin.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 13f73d84d3a69085df148e7cfe03ed6448a1238b Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Fri, 1 Mar 2024 15:43:50 -0800 Subject: 52645: unset through a nameref keep up-scope parameters declared unset Othewise unset of a reference to a global wipes out all parameters of the same name. --- ChangeLog | 5 +++++ Src/builtin.c | 6 ++++++ 2 files changed, 11 insertions(+) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 9ab0218c9..31e90dfd3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-03-01 Bart Schaefer + + * 52645: Src/builtin.c: unset through a nameref keep up-scope + parameters declared, and not wipe out the entire parameter stack + 2024-02-28 Bart Schaefer * 52619 (plus tests): Src/params.c, Test/A06assign.ztst: there diff --git a/Src/builtin.c b/Src/builtin.c index 44dfed651..83144677b 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -3932,8 +3932,14 @@ bin_unset(char *name, char **argv, Options ops, int func) } } else { if (!OPT_ISSET(ops,'n')) { + int ref = (pm->node.flags & PM_NAMEREF); if (!(pm = (Param)resolve_nameref(pm, NULL))) continue; + if (ref && pm->level < locallevel) { + /* Just mark unset, do not remove from table */ + pm->node.flags |= PM_DECLARED|PM_UNSET; + continue; + } } if (unsetparam_pm(pm, 0, 1)) returnval = 1; -- 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/builtin.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 0848b7534ed918503c36a4b217ab5f6053805763 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 4 Mar 2024 21:21:20 -0800 Subject: 52659: Fix crash on unset-through-nameref, add regression test --- ChangeLog | 3 +++ Src/builtin.c | 6 ++++-- Test/K01nameref.ztst | 25 +++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 2d4d9922e..bd8eeafcf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-03-04 Bart Schaefer + * 52659: Src/builtin.c, Test/K01nameref.ztst: Fix crash when unset + was called on a named referece, add regression test + * 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, diff --git a/Src/builtin.c b/Src/builtin.c index ba9cb03c2..0aae1fcde 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -3935,9 +3935,11 @@ bin_unset(char *name, char **argv, Options ops, int func) int ref = (pm->node.flags & PM_NAMEREF); if (!(pm = (Param)resolve_nameref(pm, NULL))) continue; - if (ref && pm->level < locallevel) { + if (ref && pm->level < locallevel && + !(pm->node.flags & PM_READONLY)) { /* Just mark unset, do not remove from table */ - pm->node.flags |= PM_DECLARED|PM_UNSET; + stdunsetfn(pm, 0); + pm->node.flags |= PM_DECLARED; continue; } } diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index 47d32697c..e45b922e2 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -711,6 +711,31 @@ F:Checking for a bug in zmodload that affects later tests >typeset -n ref=two >typeset -n ref=var + typeset -g .K01.scalar='RW' + typeset -gA .K01.assoc=(x y) + typeset -ga .K01.array=(z) + typeset -gi .K01.integer=0 + typeset -gE .K01.double=0.0 + typeset -gF .K01.float=0.0 + typeset -gr .K01.readonly='RO' + typeset -n gref + for gref in ARGC .K01.{scalar,assoc,array,integer,double,float,readonly} + do + { unset gref } always { TRY_BLOCK_ERROR=0 } + done + typeset -p .K01.{scalar,assoc,array,integer,double,float,readonly} + unset .K01.{scalar,assoc,array,integer,double,float} +0:unset various types via nameref, including a readonly special +>typeset -g .K01.scalar +>typeset -g -A .K01.assoc +>typeset -g -a .K01.array +>typeset -g -i .K01.integer +>typeset -g -E .K01.double +>typeset -g -F .K01.float +>typeset -g -r .K01.readonly=RO +*?*read-only variable: ARGC +*?*read-only variable: .K01.readonly + unset -n ref unset one typeset -n ref -- cgit v1.2.3 From c0a392b3924951a316f7183e89afd302c5f680e8 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 4 Mar 2024 21:39:29 -0800 Subject: unposted (cf. 52615): use META_NOALLOC for 52591 --- ChangeLog | 2 ++ Src/builtin.c | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 6dc3e32ee..b4ebfedbc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2024-03-04 Bart Schaefer + * unposted (cf. 52615): Src/builtin.c: use META_NOALLOC for 52591 + * unposted (cf. 52617): Src/params.c: only scalars can instantiate a declared named reference diff --git a/Src/builtin.c b/Src/builtin.c index 0aae1fcde..6f98990f9 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -5255,7 +5255,7 @@ bin_print(char *name, char **args, Options ops, int func) } } if (*argp) { - width = (int)mathevali(metafy(*argp, len[argp - args], META_USEHEAP)); + width = (int)mathevali(metafy(*argp, len[argp - args], META_NOALLOC)); argp++; if (errflag) { errflag &= ~ERRFLAG_ERROR; @@ -5290,7 +5290,7 @@ bin_print(char *name, char **args, Options ops, int func) } if (*argp) { - prec = (int)mathevali(metafy(*argp, len[argp - args], META_USEHEAP)); + prec = (int)mathevali(metafy(*argp, len[argp - args], META_NOALLOC)); argp++; if (errflag) { errflag &= ~ERRFLAG_ERROR; @@ -5403,7 +5403,7 @@ bin_print(char *name, char **args, Options ops, int func) break; case 'q': stringval = curarg ? - quotestring(metafy(curarg, curlen, META_USEHEAP), + quotestring(metafy(curarg, curlen, META_NOALLOC), QT_BACKSLASH_SHOWNULL) : &nullstr; *d = 's'; print_val(unmetafy(stringval, &curlen)); @@ -5475,7 +5475,7 @@ bin_print(char *name, char **args, Options ops, int func) *d++ = 'l'; #endif *d++ = 'l', *d++ = *c, *d = '\0'; - zlongval = (curarg) ? mathevali(metafy(curarg, curlen, META_HEAPDUP)) : 0; + zlongval = (curarg) ? mathevali(metafy(curarg, curlen, META_NOALLOC)) : 0; if (errflag) { zlongval = 0; errflag &= ~ERRFLAG_ERROR; @@ -5526,7 +5526,7 @@ bin_print(char *name, char **args, Options ops, int func) if (!curarg) zulongval = (zulong)0; else if (!zstrtoul_underscore(curarg, &zulongval)) - zulongval = mathevali(metafy(curarg, curlen, META_HEAPDUP)); + zulongval = mathevali(metafy(curarg, curlen, META_NOALLOC)); if (errflag) { zulongval = 0; errflag &= ~ERRFLAG_ERROR; -- 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/builtin.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 7c875adb0968b308e9b989ae5ac8d245b58810f2 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 12 Mar 2024 18:00:19 -0700 Subject: 52742: fix bad interactions of "typeset -p" with GLOBAL_EXPORT --- ChangeLog | 5 +++++ Src/builtin.c | 35 +++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 8 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index f136bc6b4..c8935fb61 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-03-12 Bart Schaefer + + * 52742: Src/builtin.c: fix bad interactions of "typeset -p" with + GLOBAL_EXPORT, plus some other inconsistencies. + 2024-03-13 Oliver Kiddle * 52724: Src/Modules/hlgroup.c: fix .zle.sgr for empty sequences diff --git a/Src/builtin.c b/Src/builtin.c index 829b899f8..7bfb1ce1d 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2059,7 +2059,7 @@ typeset_single(char *cname, char *pname, Param pm, int func, * POSIXBUILTINS horror: we need to retain the 'readonly' or 'export' * flags of an unset parameter. */ - usepm = pm && (!(pm->node.flags & PM_UNSET) || + usepm = pm && (!(pm->node.flags & PM_UNSET) || OPT_ISSET(ops, 'p') || (isset(POSIXBUILTINS) && (pm->node.flags & (PM_READONLY|PM_EXPORTED)))); @@ -2195,7 +2195,7 @@ typeset_single(char *cname, char *pname, Param pm, int func, else if (newspecial != NS_NONE && strcmp(pname, "SECONDS") == 0) newspecial = NS_SECONDS; - if (isset(POSIXBUILTINS)) { + if (isset(POSIXBUILTINS) && !OPT_ISSET(ops,'p')) { /* * Stricter rules about retaining readonly attribute in this case. */ @@ -2224,7 +2224,8 @@ typeset_single(char *cname, char *pname, Param pm, int func, * ii. we are creating a new local parameter */ if (usepm) { - if (OPT_MINUS(ops,'p') && on && !(on & pm->node.flags)) + if (OPT_MINUS(ops,'p') && on && + !((on & pm->node.flags) || ((on & PM_LOCAL) && pm->level))) return NULL; else if (OPT_PLUS(ops,'p') && off && !(off & pm->node.flags)) return NULL; @@ -2339,7 +2340,8 @@ typeset_single(char *cname, char *pname, Param pm, int func, return NULL; pm->node.flags |= (on & PM_READONLY); return pm; - } + } else if (OPT_ISSET(ops,'p')) + return NULL; /* Nothing to print */ if ((asg->flags & ASG_ARRAY) ? !(on & (PM_ARRAY|PM_HASHED)) : @@ -2686,6 +2688,15 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) if (func == BIN_READONLY && isset(POSIXBUILTINS) && !OPT_PLUS(ops, 'g')) ops->ind['g'] = 1; +#if 0 + /* "local" rejects -m, this should too ... what about +m ? */ + if (locallevel && OPT_MINUS(ops, 'm') && + !(OPT_MINUS(ops, 'g') || OPT_ISSET(ops, 'p'))) { + zerrnam(name, "bad option: -m"); + return 1; + } +#endif + /* Translate the options into PM_* flags. * * Unfortunately, this depends on the order * * these flags are defined in zsh.h */ @@ -2788,10 +2799,18 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) return 0; } - if (!(OPT_ISSET(ops,'g') || OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m')) || - OPT_PLUS(ops,'g') || *name == 'l' || - (!isset(GLOBALEXPORT) && !OPT_ISSET(ops,'g'))) - on |= PM_LOCAL; + /* Using *name here is cheating, "local" allows no -g option */ + if ((*name == 'l' || OPT_PLUS(ops,'g'))) + on |= PM_LOCAL; + else if (!OPT_ISSET(ops,'g')) { + if (OPT_MINUS(ops, 'x')) { + if (isset(GLOBALEXPORT)) + ops->ind['g'] = 1; + else if (locallevel) + on |= PM_LOCAL; + } else if (!(OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m'))) + on |= PM_LOCAL; + } if ((on & PM_TIED) && !OPT_ISSET(ops, 'p')) { Param apm; -- cgit v1.2.3 From a3b56d4f03e493985aa652248e9476c1f8181e4e Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Thu, 13 Jun 2024 11:12:41 +0900 Subject: 52951: make sure to close memstream for 'print -v' --- ChangeLog | 4 ++++ Src/builtin.c | 5 ++--- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 29546095d..455cf38c3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-06-13 Jun-ichi Takimoto + + * 52951: Src/builtin.c: make sure to close memstream for 'print -v' + 2024-06-08 Mikael Magnusson * 52946: Completion/compdump, Completion/compinit: Revert 52768 diff --git a/Src/builtin.c b/Src/builtin.c index 7bfb1ce1d..cd0ee7522 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -5455,9 +5455,8 @@ bin_print(char *name, char **args, Options ops, int func) } zwarnnam(name, "%s: invalid directive", start); if (*c) c[1] = save; - /* Why do we care about a clean close here? */ - if (!CLOSE_CLEANLY(fout)) - zwarnnam(name, "write error: %e", errno); + if (fout != stdout) + fclose(fout); #ifdef HAVE_OPEN_MEMSTREAM if (buf) free(buf); -- cgit v1.2.3 From f7b5cc431bdda1f7123aca740bf7c535b98ca616 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Thu, 30 Jan 2025 12:30:40 +0100 Subject: 53332, 53334: Avoid strlen calls after sprintf --- ChangeLog | 6 ++++++ Src/Modules/stat.c | 3 +-- Src/Zle/compresult.c | 10 +++++----- Src/Zle/zle_misc.c | 3 +-- Src/builtin.c | 3 +-- Src/prompt.c | 35 +++++++++++++---------------------- 6 files changed, 27 insertions(+), 33 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index d826e6c8b..f96e4f00c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2025-01-30 Oliver Kiddle + + * 53332, 53334: Src/builtin.c, Src/prompt.c, Src/Modules/stat.c, + Src/Zle/compresult.c, Src/Zle/zle_misc.c: + Avoid strlen calls after sprintf + 2025-01-27 Oliver Kiddle * 53329: Src/Modules/hlgroup.c: adapt .zle.sgr for CSI sequences diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c index c9f851974..5bf201dd3 100644 --- a/Src/Modules/stat.c +++ b/Src/Modules/stat.c @@ -236,9 +236,8 @@ statprint(struct stat *sbuf, char *outbuf, char *fname, int iwhich, int flags) char *optr = outbuf; if (flags & STF_NAME) { - sprintf(outbuf, (flags & (STF_PICK|STF_ARRAY)) ? + optr += sprintf(outbuf, (flags & (STF_PICK|STF_ARRAY)) ? "%s " : "%-8s", statelts[iwhich]); - optr += strlen(outbuf); } *optr = '\0'; diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index cd8c7dd64..7dbc5676a 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -489,7 +489,7 @@ static char * build_pos_string(LinkList list) { LinkNode node; - int l; + int l, buflen; char buf[40], *s; long p; @@ -499,12 +499,12 @@ build_pos_string(LinkList list) /* This could be used to put an extra colon before the end-of-word * position if there is nothing missing. */ if (p < 0) - sprintf(buf, ":%ld", -p); + buflen = sprintf(buf, ":%ld", -p); else #endif - sprintf(buf, "%ld", p); - setdata(node, dupstring(buf)); - l += 1 + strlen(buf); + buflen = sprintf(buf, "%ld", p); + setdata(node, dupstring_wlen(buf, buflen)); + l += 1 + buflen; } s = (char *) zalloc(l * sizeof(char)); *s = 0; diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c index eba28d1ec..e17a08d53 100644 --- a/Src/Zle/zle_misc.c +++ b/Src/Zle/zle_misc.c @@ -841,9 +841,8 @@ whatcursorposition(UNUSED(char **args)) strcpy(s, mbstr); s += len; } - sprintf(s, " (0%o, %u, 0x%x)", (unsigned int)c, + s += sprintf(s, " (0%o, %u, 0x%x)", (unsigned int)c, (unsigned int)c, (unsigned int)c); - s += strlen(s); } sprintf(s, " point %d of %d(%d%%) column %d", zlecs+1, zlell+1, zlell ? 100 * zlecs / zlell : 0, zlecs - bol); diff --git a/Src/builtin.c b/Src/builtin.c index cd0ee7522..18d74b09e 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -4809,9 +4809,8 @@ bin_print(char *name, char **args, Options ops, int func) if (d) { int dirlen = strlen(d->dir); char *arg = zhalloc(len[n] - dirlen + strlen(d->node.nam) + 2); - sprintf(arg, "~%s%s", d->node.nam, args[n] + dirlen); + len[n] = sprintf(arg, "~%s%s", d->node.nam, args[n] + dirlen); args[n] = arg; - len[n] = strlen(args[n]); } unqueue_signals(); } diff --git a/Src/prompt.c b/Src/prompt.c index f36aba9d3..7467cdfb9 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -555,8 +555,7 @@ putpromptchar(int doprint, int endchar) if (jobtab[j].stat && jobtab[j].procs && !(jobtab[j].stat & STAT_NOPRINT)) numjobs++; addbufspc(DIGBUFSIZE); - sprintf(bv->bp, "%d", numjobs); - bv->bp += strlen(bv->bp); + bv->bp += sprintf(bv->bp, "%d", numjobs); break; case 'M': queue_signals(); @@ -782,20 +781,18 @@ putpromptchar(int doprint, int endchar) case 'L': addbufspc(DIGBUFSIZE); #if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) - sprintf(bv->bp, "%lld", shlvl); + bv->bp += sprintf(bv->bp, "%lld", shlvl); #else - sprintf(bv->bp, "%ld", (long)shlvl); + bv->bp += sprintf(bv->bp, "%ld", (long)shlvl); #endif - bv->bp += strlen(bv->bp); break; case '?': addbufspc(DIGBUFSIZE); #if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) - sprintf(bv->bp, "%lld", lastval); + bv->bp += sprintf(bv->bp, "%lld", lastval); #else - sprintf(bv->bp, "%ld", (long)lastval); + bv->bp += sprintf(bv->bp, "%ld", (long)lastval); #endif - bv->bp += strlen(bv->bp); break; case '%': case ')': @@ -886,8 +883,7 @@ putpromptchar(int doprint, int endchar) fsptr = fsptr->prev; } addbufspc(DIGBUFSIZE); - sprintf(bv->bp, "%d", depth); - bv->bp += strlen(bv->bp); + bv->bp += sprintf(bv->bp, "%d", depth); break; } case 'I': @@ -904,11 +900,10 @@ putpromptchar(int doprint, int endchar) lineno--; addbufspc(DIGBUFSIZE); #if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) - sprintf(bv->bp, "%lld", flineno); + bv->bp += sprintf(bv->bp, "%lld", flineno); #else - sprintf(bv->bp, "%ld", (long)flineno); + bv->bp += sprintf(bv->bp, "%ld", (long)flineno); #endif - bv->bp += strlen(bv->bp); break; } /* else we're in a file and lineno is already correct */ @@ -916,11 +911,10 @@ putpromptchar(int doprint, int endchar) case 'i': addbufspc(DIGBUFSIZE); #if defined(ZLONG_IS_LONG_LONG) && defined(PRINTF_HAS_LLD) - sprintf(bv->bp, "%lld", lineno); + bv->bp += sprintf(bv->bp, "%lld", lineno); #else - sprintf(bv->bp, "%ld", (long)lineno); + bv->bp += sprintf(bv->bp, "%ld", (long)lineno); #endif - bv->bp += strlen(bv->bp); break; case 'x': if (funcstack && funcstack->tp != FS_SOURCE && @@ -1990,7 +1984,7 @@ match_highlight(const char *teststr, zattr *on_var, int *layer) static int output_colour(int colour, int fg_bg, int truecol, char *buf) { - int atrlen = 3, len; + int atrlen = 3; char *ptr = buf; if (buf) { strcpy(ptr, fg_bg == COL_SEQ_FG ? "fg=" : "bg="); @@ -2008,14 +2002,11 @@ output_colour(int colour, int fg_bg, int truecol, char *buf) */ } else if (colour > 7) { char digbuf[DIGBUFSIZE]; - sprintf(digbuf, "%d", colour); - len = strlen(digbuf); - atrlen += len; + atrlen += sprintf(digbuf, "%d", colour); if (buf) strcpy(ptr, digbuf); } else { - len = strlen(ansi_colours[colour]); - atrlen += len; + atrlen += strlen(ansi_colours[colour]); if (buf) strcpy(ptr, ansi_colours[colour]); } -- cgit v1.2.3 From 7a54b36fa88aa35f44c42715503f716a1612e3b7 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 12 Feb 2025 20:03:07 -0800 Subject: 53348: Revise handling of incompatible typeset options when used with -n --- ChangeLog | 5 +++++ Doc/Zsh/builtins.yo | 10 ++++++---- Src/builtin.c | 15 ++++++++++++--- Test/K01nameref.ztst | 2 +- 4 files changed, 24 insertions(+), 8 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 59a04e638..929abce32 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2025-02-12 Bart Schaefer + + * 53348: Doc/Zsh/builtins.yo, Src/builtin.c, Test/K01nameref.ztst: + Revise handling of incompatible typeset options when used with -n + 2025-02-04 Jun-ichi Takimoto * unposted: Doc/Makefile.in, Etc/Makefile.in: remove a few more diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 7a9684ac8..69bb86a0f 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -2052,8 +2052,10 @@ 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), 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 +created. Only the tt(-H), tt(-g), and tt(-r) flags may be used in +conjunction with tt(-n), having their usual meanings. The tt(-u) +flag is special and may be applied to alter the scope of the reference. +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 @@ -2286,7 +2288,7 @@ special tt(PATH) parameter is not altered in any way. It is also possible to create a local parameter using `tt(typeset +h )var(special)', where the local copy of var(special) will retain its special properties regardless of having the tt(-h) attribute. Global special parameters loaded from shell -modules (currently those in tt(zsh/mapfile) and tt(zsh/parameter)) are +modules (for example, those in tt(zsh/mapfile) and tt(zsh/parameter)) are automatically given the tt(-h) attribute to avoid name clashes. ) item(tt(-H))( @@ -2349,7 +2351,7 @@ flag has a different meaning when used with tt(-f); see above. item(tt(-u))( Convert the result to upper case whenever the parameter is expanded. The value is em(not) converted when assigned. -This flag has a different meaning when used with tt(-f); see above. +This flag has different meanings when used with tt(-f) or tt(-n); see above. ) item(tt(-x))( Mark for automatic export to the environment of subsequently diff --git a/Src/builtin.c b/Src/builtin.c index 18d74b09e..2fab73b24 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2707,10 +2707,18 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) on |= bit; else if (OPT_PLUS(ops,optval)) off |= bit; + else + continue; + if (OPT_MINUS(ops,'n')) { + if ((on|off) & ~(PM_READONLY|PM_UPPER|PM_HIDEVAL)) { + zwarnnam(name, "-%c not allowed with -n", optval); + /* return 1; */ + } + } } if (OPT_MINUS(ops,'n')) { - if ((on|off) & ~(PM_READONLY|PM_UPPER)) { - zwarnnam(name, "no other attributes allowed with -n"); + if ((on|off) & ~(PM_READONLY|PM_UPPER|PM_HIDEVAL)) { + /* zwarnnam(name, "no other attributes allowed with -n"); */ return 1; } on |= PM_NAMEREF; @@ -3049,7 +3057,8 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) /* It's generally unwise to mass-change the types of * parameters, but for namerefs it would be fatal */ unqueue_signals(); - zerrnam(name, "invalid reference"); + zerrnam(name, "%cm not allowed with -n", + (OPT_PLUS(ops,'m') ? '+' : '-')); return 1; } if (!(on|roff)) diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index bb0d11821..bacc3ade2 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -853,7 +853,7 @@ F:previously this could create an infinite recursion and crash typeset -nm foo=bar 1:create nameref by pattern match not allowed -*?*typeset:1: invalid reference +*?*typeset:1: -m not allowed with -n # # The following tests are run in interactive mode, using PS1 as an -- cgit v1.2.3 From 8701313c615394654f47586c9a4961ac8893bf5b Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 15 Feb 2025 14:29:51 -0800 Subject: 53363: permit "typeset -n +m pattern" Also fix spurious error printing the value of a read-only named reference --- ChangeLog | 7 +++++++ Src/builtin.c | 7 +++---- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 990509ae1..05aa89c88 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2025-02-15 Bart Schaefer + + * 53363: Src/builtin.c: permit "typeset -n +m pattern" + + * unposted: Src/builtin.c: fix spurious error printing the value + of a read-only named reference + 2025-02-13 Oliver Kiddle * 53358: Completion/Zsh/Command/_typeset: adapt completion to diff --git a/Src/builtin.c b/Src/builtin.c index 2fab73b24..6bdaddff0 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2254,7 +2254,7 @@ typeset_single(char *cname, char *pname, Param pm, int func, /* 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)))) { + (asg && (pm->node.flags & PM_NAMEREF))) && !OPT_ISSET(ops,'p')) { zerrnam(cname, "%s: read-only %s", pname, (pm->node.flags & PM_NAMEREF) ? "reference" : "variable"); return NULL; @@ -3053,12 +3053,11 @@ 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) { + if ((on & PM_NAMEREF) && OPT_MINUS(ops,'m')) { /* It's generally unwise to mass-change the types of * parameters, but for namerefs it would be fatal */ unqueue_signals(); - zerrnam(name, "%cm not allowed with -n", - (OPT_PLUS(ops,'m') ? '+' : '-')); + zerrnam(name, "-m not allowed with -n"); return 1; } if (!(on|roff)) -- cgit v1.2.3 From 435cb1b748ce1f2f5c38edc1d64f4ee2424f9b3a Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Mon, 3 Mar 2025 01:24:46 +0100 Subject: 53399: don't load ZLE early to set keymap When zsh is run with -o vi / -o emacs, instead of loading ZLE to force the change, only record the option state. Then when ZLE starts, the vi option determines the default keymap. --- ChangeLog | 3 +++ Src/Zle/zle_keymap.c | 5 ++++- Src/builtin.c | 2 +- Src/init.c | 30 +++++++----------------------- Src/options.c | 5 +++-- 5 files changed, 18 insertions(+), 27 deletions(-) (limited to 'Src/builtin.c') diff --git a/ChangeLog b/ChangeLog index 926f36d58..a37a0f2ed 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2025-03-03 Oliver Kiddle + * 53399: Src/Zle/zle_keymap.c, Src/builtin.c, Src/init.c, + Src/options.c: don't load ZLE early to set keymap + * 53394: Src/Zle/zle_keymap.c: drop OSC and DCS sequences in key input as they are likely to be terminal query responses not keys diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index ce52abe66..7f31f837c 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -1452,7 +1452,10 @@ default_bindings(void) linkkeymap(oppmap, "viopp", 0); linkkeymap(vismap, "visual", 0); linkkeymap(smap, ".safe", 1); - linkkeymap(emap, "main", 0); + if (isset(VIMODE)) + linkkeymap(vmap, "main", 0); + else + linkkeymap(emap, "main", 0); /* the .safe map cannot be modified or deleted */ smap->flags |= KM_IMMUTABLE; diff --git a/Src/builtin.c b/Src/builtin.c index 6bdaddff0..5563bdba9 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -6333,7 +6333,7 @@ bin_emulate(char *nam, char **argv, Options ops, UNUSED(int func)) savehackchar = keyboardhackchar; emulate(shname, opt_R, &new_emulation, new_opts); optlist = newlinklist(); - if (parseopts(nam, &argv, new_opts, &cmd, optlist, 0, NULL)) { + if (parseopts(nam, &argv, new_opts, &cmd, optlist, 0)) { ret = 1; goto restore; } diff --git a/Src/init.c b/Src/init.c index 75d9dd136..76de0b449 100644 --- a/Src/init.c +++ b/Src/init.c @@ -255,8 +255,7 @@ static char *argv0; /**/ static void -parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr, - int *needkeymap) +parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr) { char **x; LinkList paramlist; @@ -273,7 +272,7 @@ parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr, * matched by code at the end of the present function. */ - if (parseopts(zsh_name, &argv, opts, cmdptr, NULL, flags, needkeymap)) + if (parseopts(zsh_name, &argv, opts, cmdptr, NULL, flags)) exit(1); /* @@ -384,7 +383,7 @@ static void parseopts_setemulate(char *nam, int flags) /**/ mod_export int parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp, - LinkList optlist, int flags, int *needkeymap) + LinkList optlist, int flags) { int optionbreak = 0; int action, optno; @@ -490,14 +489,8 @@ parseopts(char *nam, char ***argvp, char *new_opts, char **cmdp, return 1; } else if (optno == RESTRICTED && toplevel) { restricted = action; - } else if ((optno == EMACSMODE || optno == VIMODE) - && (!toplevel || needkeymap)){ - if (!toplevel) { - WARN_OPTION("can't change option: %s", *argv); - } else { - /* Need to wait for modules to be loadable */ - *needkeymap = optno; - } + } else if ((optno == EMACSMODE || optno == VIMODE) && !toplevel) { + WARN_OPTION("can't change option: %s", *argv); } else { if (dosetopt(optno, action, toplevel, new_opts) && !toplevel) { @@ -1864,7 +1857,7 @@ zsh_main(UNUSED(int argc), char **argv) { char **t, *runscript = NULL, *zsh_name; char *cmd; /* argument to -c */ - int t0, needkeymap = 0; + int t0; #ifdef USE_LOCALE setlocale(LC_ALL, ""); #endif @@ -1910,7 +1903,7 @@ zsh_main(UNUSED(int argc), char **argv) createoptiontable(); /* sets emulation, LOGINSHELL, PRIVILEGED, ZLE, INTERACTIVE, * SHINSTDIN and SINGLECOMMAND */ - parseargs(zsh_name, argv, &runscript, &cmd, &needkeymap); + parseargs(zsh_name, argv, &runscript, &cmd); SHTTY = -1; init_io(cmd); @@ -1919,15 +1912,6 @@ zsh_main(UNUSED(int argc), char **argv) init_signals(); init_bltinmods(); init_builtins(); - - if (needkeymap) - { - /* Saved for after module system initialisation */ - zleentry(ZLE_CMD_SET_KEYMAP, needkeymap); - opts[needkeymap] = 1; - opts[needkeymap == EMACSMODE ? VIMODE : EMACSMODE] = 0; - } - run_init_scripts(); setupshin(runscript); init_misc(cmd, zsh_name); diff --git a/Src/options.c b/Src/options.c index 696ab5c69..8b13f0c5d 100644 --- a/Src/options.c +++ b/Src/options.c @@ -895,8 +895,9 @@ dosetopt(int optno, int value, int force, char *new_opts) */ if (sticky && sticky->emulation) return opts[optno] ? 0 : -1; - zleentry(ZLE_CMD_SET_KEYMAP, optno); - new_opts[(optno == EMACSMODE) ? VIMODE : EMACSMODE] = 0; + if (zle_load_state == 1) + zleentry(ZLE_CMD_SET_KEYMAP, optno); + new_opts[optno ^ EMACSMODE ^ VIMODE] = 0; } else if (optno == SUNKEYBOARDHACK) { /* for backward compatibility */ keyboardhackchar = (value ? '`' : '\0'); -- cgit v1.2.3