From 22b1a91c2a07e6d6a57975a1ab47d66f92aa21f2 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Fri, 3 Jun 2022 19:32:56 +0900 Subject: 50306: fix wait for child that was stopped/continued do not call addbgstatus() when child is stopped/continued --- Src/jobs.c | 20 ++++++++++++++++++-- Src/signals.c | 10 ++++------ 2 files changed, 22 insertions(+), 8 deletions(-) (limited to 'Src') diff --git a/Src/jobs.c b/Src/jobs.c index a91ef787f..aa32f4e80 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -414,7 +414,7 @@ storepipestats(Job jn, int inforeground, int fixlastval) jpipestats[i] = (WIFSIGNALED(p->status) ? 0200 | WTERMSIG(p->status) : (WIFSTOPPED(p->status) ? - 0200 | WEXITSTATUS(p->status) : + 0200 | WSTOPSIG(p->status) : WEXITSTATUS(p->status))); if (jpipestats[i]) pipefail = jpipestats[i]; @@ -471,7 +471,7 @@ update_job(Job jn) val = (WIFSIGNALED(pn->status) ? 0200 | WTERMSIG(pn->status) : (WIFSTOPPED(pn->status) ? - 0200 | WEXITSTATUS(pn->status) : + 0200 | WSTOPSIG(pn->status) : WEXITSTATUS(pn->status))); signalled = WIFSIGNALED(pn->status); } @@ -2221,6 +2221,7 @@ addbgstatus(pid_t pid, int status) { static long child_max; Bgstatus bgstatus_entry; + LinkNode node; if (!child_max) { #ifdef _SC_CHILD_MAX @@ -2244,6 +2245,21 @@ addbgstatus(pid_t pid, int status) if (!bgstatus_list) return; } +#ifdef DEBUG + /* See if an entry already exists for the pid */ + for (node = firstnode(bgstatus_list); node; incnode(node)) { + bgstatus_entry = (Bgstatus)getdata(node); + if (bgstatus_entry->pid == pid) { + /* In theory this should not happen because addbgstatus() is + * called only once when the process exits or gets killed. */ + dputs("addbgstatus called again: pid %d: status %d -> %d", + pid, bgstatus_entry->status, status); + bgstatus_entry->status = status; + return; + } + } +#endif + /* Add an entry for the pid */ if (bgstatus_count == child_max) { /* Overflow. List is in order, remove first */ rembgstatus(firstnode(bgstatus_list)); diff --git a/Src/signals.c b/Src/signals.c index 5c787e2a8..a61368554 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -576,12 +576,10 @@ wait_for_processes(void) */ if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) && jn - jobtab != thisjob) { - int val = (WIFSIGNALED(status) ? - 0200 | WTERMSIG(status) : - (WIFSTOPPED(status) ? - 0200 | WEXITSTATUS(status) : - WEXITSTATUS(status))); - addbgstatus(pid, val); + if (WIFEXITED(status)) + addbgstatus(pid, WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + addbgstatus(pid, 0200 | WTERMSIG(status)); } unqueue_signals(); -- cgit v1.2.3 From 3e3cfabcc74dc79d4d8717c4e5859d8d01be6c54 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Fri, 3 Jun 2022 20:08:15 -0700 Subject: 50325: revert 38150 and fix in calling function cfp_matcher_range() instead --- ChangeLog | 5 +++++ Src/Zle/compmatch.c | 2 +- Src/Zle/computil.c | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 780f967f6..839beb4f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,11 @@ * 50306: Src/jobs.c, Src/signals.c, Test/A05execution.ztst: fix wait builtin for child that has been stopped and continued. +2022-06-02 Bart Schaefer + + * 50325: Src/Zle/compmatch.c, Src/Zle/computil.c: revert 38150 and + fix in calling function cfp_matcher_range() instead + 2022-05-30 Bart Schaefer * Marlon Richert: 50307 (cf. PWS 50205): diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c index bb8359f1d..56e5509a4 100644 --- a/Src/Zle/compmatch.c +++ b/Src/Zle/compmatch.c @@ -1319,7 +1319,7 @@ pattern_match_equivalence(Cpattern lp, convchar_t wind, int wmtp, convchar_t lchr; int lmtp; - if (!PATMATCHINDEX(lp->u.str, wind, &lchr, &lmtp)) { + if (!PATMATCHINDEX(lp->u.str, wind-1, &lchr, &lmtp)) { /* * No equivalent. No possible match; give up. */ diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 59abb4cc4..77ccdebf7 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -4383,7 +4383,7 @@ cfp_matcher_range(Cmatcher *ms, char *add) * word pattern. */ if ((ind = pattern_match_equivalence - (m->word, ind, mt, addc)) != CHR_INVALID) { + (m->word, ind+1, mt, addc)) != CHR_INVALID) { if (ret) { if (imeta(ind)) { *p++ = Meta; -- cgit v1.2.3 From b26b6b3fe00b94a2d4370b1afd2644034947b6b8 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 7 Jun 2022 10:02:14 +0100 Subject: Tweaks to MULTI_FUNC_DEF Output multiple function definitions using "function" form. Note exceptions to errors with NO_MULTI_FUNC_DEF --- ChangeLog | 6 ++++++ Doc/Zsh/options.yo | 5 +++++ Src/text.c | 12 ++++++++++-- Test/C04funcdef.ztst | 20 ++++++++++++++++++++ 4 files changed, 41 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 5490385f7..71f879776 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2022-06-07 Peter Stephenson + + * 50339: Doc/Zsh/options.yo, Src/text.c, Test/C04funcdef.ztst: + Make multiple function output safer with NO_MULTI_FUNC_DEF and + document exceptions to errors raised by MULTI_FUNC_DEF. + 2022-06-04 Bart Schaefer * 50323: Completion/Base/Utility/_shadow (new file), diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo index 5e673eb5c..bf73664c9 100644 --- a/Doc/Zsh/options.yo +++ b/Doc/Zsh/options.yo @@ -1884,6 +1884,11 @@ fn2)var(...)tt(LPAR()RPAR())'; if the option is not set, this causes a parse error. Definition of multiple functions with the tt(function) keyword is always allowed. Multiple function definitions are not often used and can cause obscure errors. + +Note that no error is raised if multiple functions are defined as a +result of a set of names that were originally read as a single word on +the command line, for example `tt(TRAP{INT,QUIT})'. Although there are +no plans to change this behaviour at present, it is not guaranteed. ) pindex(MULTIOS) pindex(NO_MULTIOS) diff --git a/Src/text.c b/Src/text.c index 5cd7685fd..56127c457 100644 --- a/Src/text.c +++ b/Src/text.c @@ -578,11 +578,16 @@ gettext2(Estate state) Wordcode end = p + WC_FUNCDEF_SKIP(code); int nargs = *state->pc++; + if (nargs > 1) + taddstr("function "); taddlist(state, nargs); if (nargs) taddstr(" "); if (tjob) { - taddstr("() { ... }"); + if (nargs > 1) + taddstr("{ ... }"); + else + taddstr("() { ... }"); state->pc = end; if (!nargs) { /* @@ -594,7 +599,10 @@ gettext2(Estate state) } stack = 1; } else { - taddstr("() {"); + if (nargs > 1) + taddstr("{"); + else + taddstr("() {"); tindent++; taddnl(1); n = tpush(code, 1); diff --git a/Test/C04funcdef.ztst b/Test/C04funcdef.ztst index af469c527..b8509b25c 100644 --- a/Test/C04funcdef.ztst +++ b/Test/C04funcdef.ztst @@ -53,6 +53,26 @@ >b: redirection >a: redirection + define_multiple() { + fn1 fn2 fn3() { + print This is $0 + } + } + which -x2 define_multiple + define_multiple + fn1 + fn2 + fn3 +0: Safe output of multiple function definitions +>define_multiple () { +> function fn1 fn2 fn3 { +> print This is $0 +> } +>} +>This is fn1 +>This is fn2 +>This is fn3 + functions -M m1 m1() { (( $# )) } print $(( m1() )) -- cgit v1.2.3 From 734740a5ed52d236f3893cc738fb09c7203a5138 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 8 Jun 2022 20:48:42 -0700 Subject: 50341: disallow here-document markers containing newline --- ChangeLog | 2 ++ Src/parse.c | 3 +++ 2 files changed, 5 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index c13d8a163..b16668984 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2022-06-08 Bart Schaefer + * 50341: Src/parse.c: disallow here-doc markers containing newline + * 50335: Functions/Misc/zargs: simplify "wait" usage, fix signal handling for functions used as the command. diff --git a/Src/parse.c b/Src/parse.c index d612b7e17..5054e59d5 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -2248,6 +2248,9 @@ par_redir(int *rp, char *idstring) struct heredocs **hd; int htype = type; + if (strchr(tokstr, '\n')) + YYERROR(ecused); + /* * Add two here for the string to remember the HERE * terminator in raw and munged form. -- cgit v1.2.3 From c36068357b32b90cf034991bad7e9e1386c396c7 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Thu, 9 Jun 2022 15:08:39 +0900 Subject: 50342: fix test added by 50306 --- ChangeLog | 5 +++++ Src/jobs.c | 2 ++ Test/A05execution.ztst | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index b16668984..2dce207dc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-06-09 Jun-ichi Takimoto + + * 50342: Src/jobs.c, Test/A05execution.ztst: fix test added by + 50306 + 2022-06-08 Bart Schaefer * 50341: Src/parse.c: disallow here-doc markers containing newline diff --git a/Src/jobs.c b/Src/jobs.c index aa32f4e80..e0e453ed8 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -2221,7 +2221,9 @@ addbgstatus(pid_t pid, int status) { static long child_max; Bgstatus bgstatus_entry; +#ifdef DEBUG LinkNode node; +#endif if (!child_max) { #ifdef _SC_CHILD_MAX diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst index b257ddf2c..bcadc6d56 100644 --- a/Test/A05execution.ztst +++ b/Test/A05execution.ztst @@ -396,7 +396,7 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline # TBD: the 0 above is believed to be bogus and should also be turned # into 127 when the ccorresponding bug is fixed in the main shell. - sleep 1 & pid=$! + sleep 2 & pid=$! kill -STOP $pid sleep 1 kill -CONT $pid -- cgit v1.2.3 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 --- ChangeLog | 4 ++++ Src/builtin.c | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 2dce207dc..742e7d716 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2022-06-09 Bart Schaefer + + * 50351: Src/builtin.c: "functions -c" can set signal traps + 2022-06-09 Jun-ichi Takimoto * 50342: Src/jobs.c, Test/A05execution.ztst: fix test added by 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') 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 285b6c2538a3d5d427b13827679cb933a7e49c83 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Thu, 9 Jun 2022 15:10:43 -0700 Subject: 50363: avoid use of heap memory that depends on parameter scoping --- ChangeLog | 5 +++++ Src/Modules/param_private.c | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index dc2ec1e89..2995844ae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-06-09 Bart Schaefer + + * 50363: Src/Modules/param_private.c: avoid use of heap memory + that depends on parameter scoping + 2022-06-09 Matthew Martin * 50359: Src/builtin.c: fix bad sticky-emulation in "functions -c" diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index c53839152..065fa63d2 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -92,7 +92,7 @@ makeprivate(HashNode hn, UNUSED(int flags)) makeprivate_error = 1; return; } - struct gsu_closure *gsu = zhalloc(sizeof(struct gsu_closure)); + struct gsu_closure *gsu = zalloc(sizeof(struct gsu_closure)); switch (PM_TYPE(pm->node.flags)) { case PM_SCALAR: gsu->g = (void *)(pm->gsu.s); @@ -269,6 +269,8 @@ pps_unsetfn(Param pm, int explicit) gsu->unsetfn(pm, explicit); if (explicit) pm->gsu.s = (GsuScalar)c; + else + zfree(c, sizeof(struct gsu_closure)); } /**/ @@ -306,6 +308,8 @@ ppi_unsetfn(Param pm, int explicit) gsu->unsetfn(pm, explicit); if (explicit) pm->gsu.i = (GsuInteger)c; + else + zfree(c, sizeof(struct gsu_closure)); } /**/ @@ -343,6 +347,8 @@ ppf_unsetfn(Param pm, int explicit) gsu->unsetfn(pm, explicit); if (explicit) pm->gsu.f = (GsuFloat)c; + else + zfree(c, sizeof(struct gsu_closure)); } /**/ @@ -381,6 +387,8 @@ ppa_unsetfn(Param pm, int explicit) gsu->unsetfn(pm, explicit); if (explicit) pm->gsu.a = (GsuArray)c; + else + zfree(c, sizeof(struct gsu_closure)); } static HashTable emptytable; @@ -420,6 +428,8 @@ pph_unsetfn(Param pm, int explicit) gsu->unsetfn(pm, explicit); if (explicit) pm->gsu.h = (GsuHash)c; + else + zfree(c, sizeof(struct gsu_closure)); } /* -- cgit v1.2.3 From 61f35bb6264b04fc24e09144a2515227d5531826 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 11 Jun 2022 15:02:46 -0700 Subject: 50355: documentation and return status consistency in zsh/system module --- ChangeLog | 6 ++++++ Doc/Zsh/mod_system.yo | 16 +++++++++++++--- Src/Modules/system.c | 19 +++++++++++++++++-- 3 files changed, 36 insertions(+), 5 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 33e3a4b17..b5781f398 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2022-06-11 Bart Schaefer + + * 50355: Doc/Zsh/mod_system.yo, Src/Modules/system.c: make return + status values of sysopen consistent with other sys* functions, + make ERRNO values consistent for all, and update documentation + 2022-06-11 Jun-ichi Takimoto * 50356: Etc/FAQ.yo: work around a yodl bug (mishandling of \'e) diff --git a/Doc/Zsh/mod_system.yo b/Doc/Zsh/mod_system.yo index 884c3e753..e25201faa 100644 --- a/Doc/Zsh/mod_system.yo +++ b/Doc/Zsh/mod_system.yo @@ -74,6 +74,11 @@ truncate file to size 0 ) enditem() +A return status of 0 indicates the descriptor was successfully opened, +otherwise an error message is printed, and 1 is returned for an error +in the parameters to the command, or 2 is returned for a system error. +The parameter tt(ERRNO) is nonzero for system errors. + To close the file, use one of the following: example(tt(exec {)var(fd)tt(}<&-) @@ -123,11 +128,11 @@ error for which a message is printed to standard error. ) item(2)( There was an error on the read, or on polling the input file descriptor -for a timeout. The parameter tt(ERRNO) gives the error. +for a timeout. The parameter tt(ERRNO) identifies the error. ) item(3)( Data were successfully read, but there was an error writing them -to var(outfd). The parameter tt(ERRNO) gives the error. +to var(outfd). The parameter tt(ERRNO) identifies the error. ) item(4)( The attempt to read timed out. Note this does not set tt(ERRNO) as this @@ -147,6 +152,11 @@ expression. The tt(-u) option allows the file descriptor to be specified. By default the offset is specified relative to the start or the file but, with the tt(-w) option, it is possible to specify that the offset should be relative to the current position or the end of the file. + +The return status may be 0 for success, 1 for an error in the parameters +to the command, or 2 for an error on the seek; no error message is +printed in the last case, but the parameter tt(ERRNO) reflects +the error that occurred. ) item(tt(syswrite) [ tt(-c) var(countvar) ] [ tt(-o) var(outfd) ] var(data))( The data (a single string of bytes) are written to the file descriptor @@ -166,7 +176,7 @@ returning early. The return status may be 0 for success, 1 for an error in the parameters to the command, or 2 for an error on the write; no error message is -printed in the last case, but the parameter tt(ERRNO) will reflect +printed in the last case, but the parameter tt(ERRNO) reflects the error that occurred. ) xitem(tt(zsystem flock) [ tt(-t) var(timeout) ] [ tt(-i) var(interval) ] [ tt(-f) var(var) ] [tt(-er)] var(file)) diff --git a/Src/Modules/system.c b/Src/Modules/system.c index ea11ef037..929a8b002 100644 --- a/Src/Modules/system.c +++ b/Src/Modules/system.c @@ -74,6 +74,8 @@ bin_sysread(char *nam, char **args, Options ops, UNUSED(int func)) int infd = 0, outfd = -1, bufsize = SYSREAD_BUFSIZE, count; char *outvar = NULL, *countvar = NULL, *inbuf; + errno = 0; /* Distinguish non-system errors */ + /* -i: input file descriptor if not stdin */ if (OPT_ISSET(ops, 'i')) { infd = getposint(OPT_ARG(ops, 'i'), nam); @@ -238,6 +240,8 @@ bin_syswrite(char *nam, char **args, Options ops, UNUSED(int func)) int outfd = 1, len, count, totcount; char *countvar = NULL; + errno = 0; /* Distinguish non-system errors */ + /* -o: output file descriptor if not stdout */ if (OPT_ISSET(ops, 'o')) { outfd = getposint(OPT_ARG(ops, 'o'), nam); @@ -303,6 +307,13 @@ static struct { const char *name; int oflag; } openopts[] = { { "trunc", O_TRUNC } }; +/* + * Return values of bin_sysopen: + * 0 Success + * 1 Error in parameters to command + * 2 Error on open, ERRNO set by system + */ + /**/ static int bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func)) @@ -319,6 +330,8 @@ bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func)) int fdflags = 0; #endif + errno = 0; /* Distinguish non-system errors */ + if (!OPT_ISSET(ops, 'u')) { zwarnnam(nam, "file descriptor not specified"); return 1; @@ -374,12 +387,12 @@ bin_sysopen(char *nam, char **args, Options ops, UNUSED(int func)) if (fd == -1) { zwarnnam(nam, "can't open file %s: %e", *args, errno); - return 1; + return 2; } moved_fd = (explicit > -1) ? redup(fd, explicit) : movefd(fd); if (moved_fd == -1) { zwarnnam(nam, "can't open file %s", *args); - return 1; + return 2; } #ifdef FD_CLOEXEC @@ -423,6 +436,8 @@ bin_sysseek(char *nam, char **args, Options ops, UNUSED(int func)) char *whence; off_t pos; + errno = 0; /* Distinguish non-system errors */ + /* -u: file descriptor if not stdin */ if (OPT_ISSET(ops, 'u')) { fd = getposint(OPT_ARG(ops, 'u'), nam); -- cgit v1.2.3 From f7441b46456c3acee4a2bd500b8f6409c70f33a8 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 21 Jun 2022 17:58:57 -0700 Subject: 50368: adjust ztie'd bitflags so local variables cannot mess with database --- ChangeLog | 5 +++++ Src/Modules/db_gdbm.c | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index d8a057fcd..0cdd632f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-06-21 Bart Schaefer + + * 50368: Src/Modules/db_gdbm.c: adjust bitflags so local copies of + variables cannot mess with database file contents + 2022-06-16 Peter Stephenson * 50372: Etc/FAQ.yo: remove redundant references from days of diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c index 7e11ec939..3fefd412b 100644 --- a/Src/Modules/db_gdbm.c +++ b/Src/Modules/db_gdbm.c @@ -34,8 +34,8 @@ #include "db_gdbm.mdh" #include "db_gdbm.pro" -#ifndef PM_UPTODATE -#define PM_UPTODATE (1<<19) /* Parameter has up-to-date data (e.g. loaded from DB) */ +#ifndef PM_UPTODATE /* Parameter has up-to-date data (e.g. loaded from DB) */ +#define PM_UPTODATE PM_DONTIMPORT_SUID /* Safe PM_ bit to re-use */ #endif static Param createhash( char *name, int flags ); @@ -111,7 +111,7 @@ bin_ztie(char *nam, char **args, Options ops, UNUSED(int func)) struct gsu_scalar_ext *dbf_carrier; char *resource_name, *pmname; GDBM_FILE dbf = NULL; - int read_write = GDBM_SYNC, pmflags = PM_REMOVABLE; + int read_write = GDBM_SYNC, pmflags = PM_REMOVABLE|PM_SINGLE; Param tied_param; if(!OPT_ISSET(ops,'d')) { -- cgit v1.2.3 From cb59dfb3a6f6cce414c5b852c138d5f6bea6d563 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 21 Jun 2022 18:04:45 -0700 Subject: 50379/50380: fix off-by-one side-effect of workers/49906 that broke $(jobs -l) --- ChangeLog | 3 +++ Src/jobs.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 0cdd632f9..5220d2e83 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-06-21 Bart Schaefer + * 50379 (tweaked per 50380): Src/jobs.c: fix off-by-one + side-effect of workers/49906 that broke $(jobs -l) + * 50368: Src/Modules/db_gdbm.c: adjust bitflags so local copies of variables cannot mess with database file contents diff --git a/Src/jobs.c b/Src/jobs.c index e0e453ed8..707374297 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -2402,7 +2402,7 @@ bin_fg(char *name, char **argv, Options ops, int func) int curmaxjob, ignorejob; if (unset(MONITOR) && oldmaxjob) { jobptr = oldjobtab; - curmaxjob = oldmaxjob ? oldmaxjob - 1 : 0; + curmaxjob = oldmaxjob; ignorejob = 0; } else { jobptr = jobtab; -- cgit v1.2.3 From ac6257f1507af144f7c88d030bbf076790d86a42 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Sun, 24 Jul 2022 20:41:20 +0900 Subject: 50418: use setenv(3)/getenv(3) on newer macOS --- ChangeLog | 5 +++++ Src/zsh_system.h | 3 ++- configure.ac | 8 ++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index d6d3f98d2..ce2ec2dd3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-07-24 Jun-ichi Takimoto + + * 50418: Src/zsh_system.h, configure.ac: use setenv(3)/getenv(3) + on newer macOS + 2022-07-16 Bart Schaefer * users/27852: Completion/Unix/Command/_python: Make a local copy diff --git a/Src/zsh_system.h b/Src/zsh_system.h index 6f4efce96..16f724401 100644 --- a/Src/zsh_system.h +++ b/Src/zsh_system.h @@ -783,7 +783,8 @@ extern char **environ; * We always need setenv and unsetenv in pairs, because * we don't know how to do memory management on the values set. */ -#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) && !defined(__APPLE__) +#if defined(HAVE_SETENV) && defined(HAVE_UNSETENV) \ + && !defined(SETENV_MANGLES_EQUAL) # define USE_SET_UNSET_ENV #endif diff --git a/configure.ac b/configure.ac index 77e381f50..890ef8dd2 100644 --- a/configure.ac +++ b/configure.ac @@ -1515,6 +1515,14 @@ else zsh_cv_use_xattr=no fi]) +dnl We don't want to use setenv(3) on El Capitan or older OS X because it +dnl removes a leading '=' from the value of the environment variable +AH_TEMPLATE([SETENV_MANGLES_EQUAL], +[Define to 1 if setenv removes a leading =]) +case $host_os in + darwin1[0-5]*) AC_DEFINE(SETENV_MANGLES_EQUAL) ;; +esac + dnl ------------- dnl CHECK SIGNALS dnl ------------- -- cgit v1.2.3 From 1b421e4978440234fb73117c8505dad1ccc68d46 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Mon, 26 Sep 2022 10:52:50 +0900 Subject: 50658 + test: Enable to switch between C/UTF-8 locales in PCRE --- ChangeLog | 5 +++++ Src/Modules/pcre.c | 10 ++-------- Test/V07pcre.ztst | 11 +++++++++++ 3 files changed, 18 insertions(+), 8 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 48c65d01b..77345c050 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-09-26 Jun-ichi Takimoto + + * 50658 + test: Src/Modules/pcre.c, Test/V07pcre.ztst: Enable to + switch between C/UTF-8 locales in PCRE + 2022-09-25 Peter Stephenson * 50648: Functions/Misc/zcalc: Julian Prein: Use ZCALC_HISTFILE diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c index 6289e003e..46875a59b 100644 --- a/Src/Modules/pcre.c +++ b/Src/Modules/pcre.c @@ -47,8 +47,6 @@ zpcre_utf8_enabled(void) #if defined(MULTIBYTE_SUPPORT) && defined(HAVE_NL_LANGINFO) && defined(CODESET) static int have_utf8_pcre = -1; - /* value can toggle based on MULTIBYTE, so don't - * be too eager with caching */ if (have_utf8_pcre < -1) return 0; @@ -56,15 +54,11 @@ zpcre_utf8_enabled(void) return 0; if ((have_utf8_pcre == -1) && - (!strcmp(nl_langinfo(CODESET), "UTF-8"))) { - - if (pcre_config(PCRE_CONFIG_UTF8, &have_utf8_pcre)) + (pcre_config(PCRE_CONFIG_UTF8, &have_utf8_pcre))) { have_utf8_pcre = -2; /* erk, failed to ask */ } - if (have_utf8_pcre < 0) - return 0; - return have_utf8_pcre; + return (have_utf8_pcre == 1) && (!strcmp(nl_langinfo(CODESET), "UTF-8")); #else return 0; diff --git a/Test/V07pcre.ztst b/Test/V07pcre.ztst index ca13419e5..22a0b64c7 100644 --- a/Test/V07pcre.ztst +++ b/Test/V07pcre.ztst @@ -162,3 +162,14 @@ echo $match[2] ) 0:regression for segmentation fault, workers/38307 >test + + LANG_SAVE=$LANG + [[ é =~ '^.\z' ]]; echo $? + LANG=C + [[ é =~ '^..\z' ]]; echo $? + LANG=$LANG_SAVE + [[ é =~ '^.\z' ]]; echo $? +0:swich between C/UTF-8 locales +>0 +>0 +>0 -- cgit v1.2.3 From 33938ad489e6f3c280d431f92920db5a00458534 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Tue, 27 Sep 2022 15:20:24 +0900 Subject: 50668: treat 8bit chars correctly when multibyte is unset The problem was found in character range, but may have existed in other occasions --- ChangeLog | 5 +++++ Src/utils.c | 2 +- Test/D09brace.ztst | 7 +++++++ 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 597fd9093..8679bf019 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-09-27 Jun-ichi Takimoto + + * 50668: Src/utils.c, Test/D09brace.ztst: treat 8bit characters + in charcter range correctly when multibyte is unset + 2022-09-26 Jun-ichi Takimoto * 50662: Test/ztst.zsh: unset LC_* for all the tests diff --git a/Src/utils.c b/Src/utils.c index 62bd3e602..edf5d3df7 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -5519,7 +5519,7 @@ mb_metacharlenconv(const char *s, wint_t *wcp) if (!isset(MULTIBYTE) || STOUC(*s) <= 0x7f) { /* treat as single byte, possibly metafied */ if (wcp) - *wcp = (wint_t)(*s == Meta ? s[1] ^ 32 : *s); + *wcp = (wint_t)STOUC(*s == Meta ? s[1] ^ 32 : *s); return 1 + (*s == Meta); } /* diff --git a/Test/D09brace.ztst b/Test/D09brace.ztst index 580ed430f..961947b67 100644 --- a/Test/D09brace.ztst +++ b/Test/D09brace.ztst @@ -116,3 +116,10 @@ print -r {1..10}{.. 0:Unmatched braces after matched braces are left alone. >1{.. 2{.. 3{.. 4{.. 5{.. 6{.. 7{.. 8{.. 9{.. 10{.. + + () { + setopt localoptions no_multibyte + echo -E {$'\x80'..$'\x81'} + } +0:range of 8bit chars, multibyte option unset +>\M-^@ \M-^A -- cgit v1.2.3 From 727b493e2b782fca0f3933865faa9a0a6ab1a2c4 Mon Sep 17 00:00:00 2001 From: Wesley Schwengle Date: Mon, 17 Oct 2022 13:13:13 +0900 Subject: 50736: silence use-after-free warning (gcc-12.2) --- ChangeLog | 5 +++++ Src/Zle/compmatch.c | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 732721595..cb47acd1b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-10-17 Jun-ichi Takimoto + + * Wesley Schwengle: 50736: Src/Zle/compmatch.c: silence + use-after-free waring (gcc-12.2) + 2022-09-29 Jun-ichi Takimoto * 50671: Util/ztst-syntax.vim: enable spell check in *.ztst diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c index 56e5509a4..ddcecd589 100644 --- a/Src/Zle/compmatch.c +++ b/Src/Zle/compmatch.c @@ -2045,12 +2045,12 @@ join_strs(int la, char *sa, int lb, char *sb) zlelineasstring(line, mp->llen, 0, &convlen, NULL, 0); if (rr <= convlen) { - char *or = rs; + ptrdiff_t diff = rp - rs; int alloclen = (convlen > 20) ? convlen : 20; rs = realloc(rs, (rl += alloclen)); rr += alloclen; - rp += rs - or; + rp = rs + diff; } memcpy(rp, convstr, convlen); rp += convlen; @@ -2073,11 +2073,11 @@ join_strs(int la, char *sa, int lb, char *sb) } else { /* Same character, just take it. */ if (rr <= 1 /* HERE charlen */) { - char *or = rs; + ptrdiff_t diff = rp - rs; rs = realloc(rs, (rl += 20)); rr += 20; - rp += rs - or; + rp = rs + diff; } /* HERE: multibyte char */ *rp++ = *sa; -- cgit v1.2.3 From 5b1c204c54e3ba41411e583ea649532b12977845 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 23 Oct 2022 16:28:14 -0700 Subject: Unposted: Fix typo in comment --- Src/parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Src') diff --git a/Src/parse.c b/Src/parse.c index 5054e59d5..2fac5c89c 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -120,7 +120,7 @@ struct heredocs *hdocs; * - if not (type & Z_END), followed by next WC_LIST * * WC_SUBLIST - * - data contains type (&&, ||, END) and flags (coprog, not) + * - data contains type (&&, ||, END) and flags (coproc, not) * - followed by code for sublist * - if not (type == END), followed by next WC_SUBLIST * -- cgit v1.2.3 From f8d93888a8efd6c8142e74ece83b38632661de47 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Wed, 2 Nov 2022 16:27:27 +0900 Subject: 50851: restore typtab when necessary inittyptab() must be called when returning from a function with "setopt localoptions MULTIBYTE|BANGHIST|SHSTDIN", and also in function dosetopt() when setting these options (via $options, for example). We intentionally did not take account of the options EMACS/VI because these options are obsolete and their use is not recommended. --- ChangeLog | 6 ++++++ Doc/Zsh/options.yo | 16 ++++++++++------ Src/exec.c | 12 ++++++++++++ Src/options.c | 6 +++++- 4 files changed, 33 insertions(+), 7 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index c6daa5998..6146209cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2022-11-02 Jun-ichi Takimoto + + * 50851: Doc/Zsh/options.yo, Src/exec.c, Src/options.c: restore + state (such as typtab) when returning from a function with + localoptions (but do not take care of EMACS/VI options). + 2022-10-31 Bart Schaefer * 50855: Doc/Zsh/builtins.yo, Doc/Zsh/params.yo: Clarify how diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo index bf73664c9..445052617 100644 --- a/Doc/Zsh/options.yo +++ b/Doc/Zsh/options.yo @@ -2550,10 +2550,12 @@ pindex(NO_EMACS) pindex(NOEMACS) item(tt(EMACS))( If ZLE is loaded, turning on this option has the equivalent effect -of `tt(bindkey -e)'. In addition, the VI option is unset. +of `tt(bindkey -e)'. In addition, the tt(VI) option is unset. Turning it off has no effect. The option setting is -not guaranteed to reflect the current keymap. This option is -provided for compatibility; tt(bindkey) is the recommended interface. +not guaranteed to reflect the current keymap, and the tt(LOCALOPTIONS) +option does not work correctly. This option is provided only for +compatibility, and its use is highly discouraged. tt(bindkey) is the +recommended interface. ) pindex(OVERSTRIKE) pindex(NO_OVERSTRIKE) @@ -2582,10 +2584,12 @@ pindex(NO_VI) pindex(NOVI) item(tt(VI))( If ZLE is loaded, turning on this option has the equivalent effect -of `tt(bindkey -v)'. In addition, the EMACS option is unset. +of `tt(bindkey -v)'. In addition, the tt(EMACS) option is unset. Turning it off has no effect. The option setting is -not guaranteed to reflect the current keymap. This option is -provided for compatibility; tt(bindkey) is the recommended interface. +not guaranteed to reflect the current keymap, and the tt(LOCALOPTIONS) +option does not work correctly. This option is provided only for +compatibility, and its use is highly discouraged. tt(bindkey) is the +recommended interface. ) pindex(ZLE) pindex(NO_ZLE) diff --git a/Src/exec.c b/Src/exec.c index f2911807c..c8bcf4ee5 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -5961,11 +5961,23 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) emulation = funcsave->emulation; sticky = funcsave->sticky; } else if (isset(LOCALOPTIONS)) { + /* we need to call inittyptab() if these options change */ + int init_typtab = +#ifdef MULTIBYTE_SUPPORT + funcsave->opts[MULTIBYTE] != opts[MULTIBYTE] || +#endif + funcsave->opts[BANGHIST] != opts[BANGHIST] || + funcsave->opts[SHINSTDIN] != opts[SHINSTDIN]; + /* take care of SUNKEYBOARDHACK but not of EMACS/VI */ + if (funcsave->opts[SUNKEYBOARDHACK] != opts[SUNKEYBOARDHACK]) + keyboardhackchar = funcsave->opts[SUNKEYBOARDHACK] ? '`' : '\0'; /* restore all shell options except PRIVILEGED and RESTRICTED */ funcsave->opts[PRIVILEGED] = opts[PRIVILEGED]; funcsave->opts[RESTRICTED] = opts[RESTRICTED]; memcpy(opts, funcsave->opts, sizeof(opts)); emulation = funcsave->emulation; + if (init_typtab) + inittyptab(); } else { /* just restore a couple. */ opts[XTRACE] = funcsave->opts[XTRACE]; diff --git a/Src/options.c b/Src/options.c index a1fe918fc..a994b563e 100644 --- a/Src/options.c +++ b/Src/options.c @@ -904,7 +904,11 @@ dosetopt(int optno, int value, int force, char *new_opts) keyboardhackchar = (value ? '`' : '\0'); } new_opts[optno] = value; - if (optno == BANGHIST || optno == SHINSTDIN) + if ( +#ifdef MULTIBYTE_SUPPORT + optno == MULTIBYTE || +#endif + optno == BANGHIST || optno == SHINSTDIN) inittyptab(); return 0; } -- cgit v1.2.3 From 188c5cd518b512073d3fd1dbf09c91b7968efcaa Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 6 Nov 2022 11:25:47 -0800 Subject: 50874: fix handling of tty signals for jobs in the current shell when waiting for the right side of a pipeline. Reverts 15bf8ace (workers/50134). Thanks to Jun T. for debugging assistance. Issues came down to two things: 1. update_job() may be called on a process group leader even when a signal was NOT sent to any process in that process group. This caused jobs to be resumed or backgrounded incorrectly or in the wrong order. 2. When there is a current-shell complex command (in braces) on the right side of a pipeline, external processes within it have their own process groups, but a tty signal sent to such a process should be treated as if received by the whole complex command. This fixes: * Suspend/resume of a foreground pipeline within a shell function * Interrupt or suspend/resume of processes in a pipeline ending in { ... } * Interrupt of such a pipeline after exit of the last process in { ... } These affected interactive shells only (MONITOR set plus tty signals). --- ChangeLog | 5 +++++ Src/jobs.c | 24 +++++++++++++----------- 2 files changed, 18 insertions(+), 11 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 6146209cf..7016ca76e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-11-06 Bart Schaefer + + * 50874: Src/jobs.c: fix handling of tty signals for jobs in + the current shell when waiting for the right side of a pipeline. + 2022-11-02 Jun-ichi Takimoto * 50851: Doc/Zsh/options.yo, Src/exec.c, Src/options.c: restore diff --git a/Src/jobs.c b/Src/jobs.c index 707374297..76c762ee5 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -544,16 +544,14 @@ update_job(Job jn) if (isset(MONITOR)) { pid_t pgrp = gettygrp(); /* get process group of tty */ + int deadpgrp = (mypgrp != pgrp && inforeground && pgrp > 1 && + kill(-pgrp, 0) == -1 && errno == ESRCH); /* is this job in the foreground of an interactive shell? */ if (mypgrp != pgrp && inforeground && - (jn->gleader == pgrp || - (pgrp > 1 && - (kill(-pgrp, 0) == -1 && errno == ESRCH)))) { + ((jn->gleader == pgrp && signalled) || deadpgrp)) { if (list_pipe) { - if (somestopped || (pgrp > 1 && - kill(-pgrp, 0) == -1 && - errno == ESRCH)) { + if (somestopped || deadpgrp) { attachtty(mypgrp); /* check window size and adjust if necessary */ adjustwinsize(0); @@ -566,6 +564,12 @@ update_job(Job jn) * when the job is finally deleted. */ jn->stat |= STAT_ATTACH; + /* + * If we're in shell jobs on the right side of a pipeline + * we should treat it like a job in the current shell. + */ + if (inforeground == 2) + inforeground = 1; } /* If we have `foo|while true; (( x++ )); done', and hit * ^C, we have to stop the loop, too. */ @@ -1488,10 +1492,7 @@ addproc(pid_t pid, char *text, int aux, struct timeval *bgtime, * set it for that, too. */ if (gleader != -1) { - if (jobtab[thisjob].stat & STAT_CURSH) - jobtab[thisjob].gleader = gleader; - else - jobtab[thisjob].gleader = pid; + jobtab[thisjob].gleader = gleader; if (list_pipe_job_used != -1) jobtab[list_pipe_job_used].gleader = gleader; /* @@ -1500,7 +1501,7 @@ addproc(pid_t pid, char *text, int aux, struct timeval *bgtime, */ last_attached_pgrp = gleader; } else if (!jobtab[thisjob].gleader) - jobtab[thisjob].gleader = pid; + jobtab[thisjob].gleader = pid; /* attach this process to end of process list of current job */ pnlist = &jobtab[thisjob].procs; } @@ -2506,6 +2507,7 @@ bin_fg(char *name, char **argv, Options ops, int func) jobtab[job].stat &= ~STAT_CURSH; } if ((stopped = (jobtab[job].stat & STAT_STOPPED))) { + /* WIFCONTINUED will makerunning() again at killjb() */ makerunning(jobtab + job); if (func == BIN_BG) { /* Set $! to indicate this was backgrounded */ -- cgit v1.2.3 From 298919f43a565cc2e130c8cb3f67773f7a54fca1 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 8 Nov 2022 14:12:01 +0000 Subject: users/28338: command substitution with alias edge case. See added regression test. --- ChangeLog | 5 +++++ Src/lex.c | 8 ++++++++ Test/D08cmdsubst.ztst | 8 ++++++++ 3 files changed, 21 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 7016ca76e..3018706b8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-11-08 Peter Stephenson + + * users/28338: Src/lex.c, Test/D08cmdsubst.ztst: edge case of an + edge case in command expansion of alias. + 2022-11-06 Bart Schaefer * 50874: Src/jobs.c: fix handling of tty signals for jobs in diff --git a/Src/lex.c b/Src/lex.c index ece02659e..e2f8bcfb1 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -1429,10 +1429,18 @@ gettokstr(int c, int sub) peek == STRING && lexbuf.ptr[-1] == '}' && lexbuf.ptr[-2] != Bnull) { /* hack to get {foo} command syntax work */ + /* + * Alias expansion when parsing command substitution means that + * the case for raw lexical analysis may not be the same. + * (Just go with it, OK?) + */ + int lar = lex_add_raw; + lex_add_raw = lexbuf_raw.len > 0 && lexbuf_raw.ptr[-1] == '}'; lexbuf.ptr--; lexbuf.len--; lexstop = 0; hungetc('}'); + lex_add_raw = lar; } *lexbuf.ptr = '\0'; DPUTS(cmdsp != ocmdsp, "BUG: gettok: cmdstack changed."); diff --git a/Test/D08cmdsubst.ztst b/Test/D08cmdsubst.ztst index 04bf698aa..e415831a0 100644 --- a/Test/D08cmdsubst.ztst +++ b/Test/D08cmdsubst.ztst @@ -177,3 +177,11 @@ 0:Alias expansion needed in parsing substitutions >hi >bye + +# This should silently print a blank line; the original problem was +# a parse error as the last character of the unexpanded alias +# was erased, symptom: "command not found: W" + alias WI='while {false}' + eval 'echo $(WI blah)' +0:Aliases with braces in command substitution can cause havoc +> -- cgit v1.2.3 From d873ed6026d7b0c48d6e65ec06df491d015a4d59 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 8 Nov 2022 20:36:49 -0800 Subject: 50897: nonzero status of complex commands should trigger ERR_EXIT --- ChangeLog | 5 +++++ Src/exec.c | 2 +- Src/loop.c | 12 ++++++------ 3 files changed, 12 insertions(+), 7 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 3018706b8..76a8091e3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-11-08 Bart Schaefer + + * 50897: Src/exec.c, Src/loop.c: nonzero status of complex + commands should trigger ERR_EXIT + 2022-11-08 Peter Stephenson * users/28338: Src/lex.c, Test/D08cmdsubst.ztst: edge case of an diff --git a/Src/exec.c b/Src/exec.c index c8bcf4ee5..2422dae91 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -451,7 +451,7 @@ execcursh(Estate state, int do_exec) cmdpop(); state->pc = end; - this_noerrexit = 1; + this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); return lastval; } diff --git a/Src/loop.c b/Src/loop.c index db5b3e097..be5261369 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -208,7 +208,7 @@ execfor(Estate state, int do_exec) loops--; simple_pline = old_simple_pline; state->pc = end; - this_noerrexit = 1; + this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); return lastval; } @@ -336,7 +336,7 @@ execselect(Estate state, UNUSED(int do_exec)) loops--; simple_pline = old_simple_pline; state->pc = end; - this_noerrexit = 1; + this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); return lastval; } @@ -478,7 +478,7 @@ execwhile(Estate state, UNUSED(int do_exec)) popheap(); loops--; state->pc = end; - this_noerrexit = 1; + this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); return lastval; } @@ -532,7 +532,7 @@ execrepeat(Estate state, UNUSED(int do_exec)) loops--; simple_pline = old_simple_pline; state->pc = end; - this_noerrexit = 1; + this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); return lastval; } @@ -587,7 +587,7 @@ execif(Estate state, int do_exec) lastval = 0; } state->pc = end; - this_noerrexit = 1; + this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); return lastval; } @@ -701,7 +701,7 @@ execcase(Estate state, int do_exec) if (!anypatok) lastval = 0; - this_noerrexit = 1; + this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); return lastval; } -- cgit v1.2.3 From 61610ea4bdc3e2de11c258017f377db3d1d6d993 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 9 Nov 2022 20:24:57 -0800 Subject: 50922: fix additional cases of signals for current shell jobs on the right of a pipeline. Backs out part of 188c5cd5 (workers/50874). With this change, after a new subshell is forked upon suspend of the right side of a pipeline, the previous foreground subjob is resumed first and the new subshell remains stopped until that job finishes. --- ChangeLog | 6 ++++++ Src/exec.c | 8 ++++++-- Src/jobs.c | 6 ------ 3 files changed, 12 insertions(+), 8 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 76a8091e3..fe10e2982 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2022-11-09 Bart Schaefer + + * 50922: Src/exec.c, Src/jobs.c: fix additional cases of signals + for current shell jobs on the right of a pipeline. Backs out + part of 50874. + 2022-11-08 Bart Schaefer * 50897: Src/exec.c, Src/loop.c: nonzero status of complex diff --git a/Src/exec.c b/Src/exec.c index 2422dae91..d4e681887 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1899,8 +1899,12 @@ execpline(Estate state, wordcode slcode, int how, int last1) break; } } - else if (subsh && jn->stat & STAT_STOPPED) - thisjob = newjob; + else if (subsh && jn->stat & STAT_STOPPED) { + if (thisjob == newjob) + makerunning(jn); + else + thisjob = newjob; + } else break; } diff --git a/Src/jobs.c b/Src/jobs.c index 76c762ee5..4863962b9 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -564,12 +564,6 @@ update_job(Job jn) * when the job is finally deleted. */ jn->stat |= STAT_ATTACH; - /* - * If we're in shell jobs on the right side of a pipeline - * we should treat it like a job in the current shell. - */ - if (inforeground == 2) - inforeground = 1; } /* If we have `foo|while true; (( x++ )); done', and hit * ^C, we have to stop the loop, too. */ -- cgit v1.2.3 From 1ba8714a7ac665e661c1b3a716ffe2af73d1e443 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 9 Nov 2022 21:37:56 -0800 Subject: 50928: fix tests for 50897, mention behavior change in NEWS --- ChangeLog | 3 +++ NEWS | 9 +++++++++ Src/exec.c | 2 ++ Test/C03traps.ztst | 17 ++++++----------- 4 files changed, 20 insertions(+), 11 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index fe10e2982..d88fa4cbb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-11-09 Bart Schaefer + * 50928: News, Src/exec.c, Test/C03traps.ztst: fix tests for 50897, + mention behavior change in NEWS + * 50922: Src/exec.c, Src/jobs.c: fix additional cases of signals for current shell jobs on the right of a pipeline. Backs out part of 50874. diff --git a/NEWS b/NEWS index cdafd1ff5..9c28169bb 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,15 @@ CHANGES FROM PREVIOUS VERSIONS OF ZSH Note also the list of incompatibilities in the README file. +Changes since 5.9 +----------------- + +Handling of ERR_EXIT is corrected when the final status of a structured +command (for, select, while, repeat, if, case, or a list in braces) is +nonzero. To be compatible with other shells, "zsh -e" now exits in +those circumstances, whereas previous versions did not. This does not +affect the handling of nonzero status within conditional statements. + Changes since 5.8.1 ------------------- diff --git a/Src/exec.c b/Src/exec.c index d4e681887..eef40232e 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1442,6 +1442,8 @@ execlist(Estate state, int dont_change_job, int exiting) execsimple(state); else execpline(state, code, ltype, (ltype & Z_END) && exiting); + if (unset(ERRRETURN)) + this_noerrexit = noerrexit; state->pc = next; goto sublist_done; break; diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst index f120809a7..5cc45e2de 100644 --- a/Test/C03traps.ztst +++ b/Test/C03traps.ztst @@ -713,7 +713,7 @@ F:Must be tested with a top-level script rather than source or function fi } fn() { - setopt err_return + setopt localoptions err_return fn2 || true } fn @@ -726,8 +726,7 @@ F:Must be tested with a top-level script rather than source or function done print OK ) -0:ERR_EXIT not triggered by status 1 at end of for ->OK +1:ERR_EXIT triggered by status 1 at end of for (setopt err_exit integer x=0 @@ -736,8 +735,7 @@ F:Must be tested with a top-level script rather than source or function done print OK ) -0:ERR_EXIT not triggered by status 1 at end of while ->OK +1:ERR_EXIT triggered by status 1 at end of while (setopt err_exit repeat 1; do @@ -745,8 +743,7 @@ F:Must be tested with a top-level script rather than source or function done print OK ) -0:ERR_EXIT not triggered by status 1 at end of repeat ->OK +1:ERR_EXIT triggered by status 1 at end of repeat (setopt err_exit if true; then @@ -754,8 +751,7 @@ F:Must be tested with a top-level script rather than source or function fi print OK ) -0:ERR_EXIT not triggered by status 1 at end of if ->OK +1:ERR_EXIT triggered by status 1 at end of if (setopt err_exit { @@ -763,8 +759,7 @@ F:Must be tested with a top-level script rather than source or function } print OK ) -0:ERR_EXIT not triggered by status 1 at end of { } ->OK +1:ERR_EXIT triggered by status 1 at end of { } unsetopt err_exit err_return (setopt err_exit -- cgit v1.2.3 From 8839e969bf8f3f129d0efd8ecd51505610a1f01b Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 9 Nov 2022 21:48:46 -0800 Subject: 50929: fix handling of ERR_RETURN bent by 50928. --- ChangeLog | 2 ++ Src/exec.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index d88fa4cbb..6478f5480 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2022-11-09 Bart Schaefer + * 50929: Src/exec.c: fix handling of ERR_RETURN bent by 50928. + * 50928: News, Src/exec.c, Test/C03traps.ztst: fix tests for 50897, mention behavior change in NEWS diff --git a/Src/exec.c b/Src/exec.c index eef40232e..ce0c1f1ad 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1442,7 +1442,7 @@ execlist(Estate state, int dont_change_job, int exiting) execsimple(state); else execpline(state, code, ltype, (ltype & Z_END) && exiting); - if (unset(ERRRETURN)) + if (!locallevel || unset(ERRRETURN)) this_noerrexit = noerrexit; state->pc = next; goto sublist_done; -- cgit v1.2.3 From c4d557bb0a9cf6a7241f760ad466e2d91359ceb2 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Thu, 17 Nov 2022 20:05:12 +0100 Subject: 50934: use OSC 52 escape sequence when copying to "* or "+ vi buffers --- ChangeLog | 6 ++++++ Doc/Zsh/zle.yo | 11 ++++++++--- Src/Zle/zle.h | 3 +++ Src/Zle/zle_utils.c | 32 +++++++++++++++++++++++++++++++- Src/Zle/zle_vi.c | 9 ++++++--- 5 files changed, 54 insertions(+), 7 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 6478f5480..c42163434 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2022-11-17 Oliver Kiddle + + * 50934: Doc/Zsh/zle.yo, Src/Zle/zle.h, Src/Zle/zle_utils.c, + Src/Zle/zle_vi.c: use OSC 52 escape sequence when copying to + "* or "+ vi buffers + 2022-11-09 Bart Schaefer * 50929: Src/exec.c: fix handling of ERR_RETURN bent by 50928. diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo index 2d033a0a1..58700072a 100644 --- a/Doc/Zsh/zle.yo +++ b/Doc/Zsh/zle.yo @@ -2470,10 +2470,11 @@ command. tt(run-help) is normally aliased to tt(man). tindex(vi-set-buffer) item(tt(vi-set-buffer) (unbound) (tt(")) (unbound))( Specify a buffer to be used in the following command. -There are 37 buffers that can be specified: +There are 39 buffers that can be specified: the 26 `named' buffers tt("a) to tt("z), the `yank' buffer tt("0), -the nine `queued' buffers tt("1) to tt("9) and the `black hole' buffer -tt("_). The named buffers can also be specified as tt("A) to tt("Z). +the nine `queued' buffers tt("1) to tt("9), the `black hole' buffer +tt("_) and the system selection tt("*) and clipboard tt("+). +The named buffers can also be specified as tt("A) to tt("Z). When a buffer is specified for a cut, change or yank command, the text concerned replaces the previous contents of the specified buffer. If @@ -2482,6 +2483,10 @@ appended to the buffer instead of overwriting it. When using the tt("_) buffer, nothing happens. This can be useful for deleting text without affecting any buffers. +Updating the system clipboard relies on specific support from the terminal. +Reading it is not possible so a paste command with tt("*) or tt("+) will do +nothing. + If no buffer is specified for a cut or change command, tt("1) is used, and the contents of tt("1) to tt("8) are each shifted along one buffer; the contents of tt("9) is lost. If no buffer is specified for a yank diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index 391586c4a..f59545397 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -258,6 +258,9 @@ struct modifier { #define MOD_NULL (1<<5) /* throw away text for the vi cut buffer */ #define MOD_CHAR (1<<6) /* force character-wise movement */ #define MOD_LINE (1<<7) /* force line-wise movement */ +#define MOD_PRI (1<<8) /* OS primary selection for the vi cut buffer */ +#define MOD_CLIP (1<<9) /* OS clipboard for the vi cut buffer */ +#define MOD_OSSEL (MOD_PRI | MOD_CLIP) /* either system selection */ /* current modifier status */ diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 526216fa7..3d9017dcf 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -936,6 +936,28 @@ cut(int i, int ct, int flags) cuttext(zleline + i, ct, flags); } +static char* +base64_encode(const char *src, size_t len) { + static const char* base64_table = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + const unsigned char *end = (unsigned char *)src + len; + const unsigned char *in = (unsigned char *)src; + char *ret = zhalloc(1 + 4 * ((len + 2) / 3)); /* 4 bytes out for 3 in */ + char *cur = ret; + + for (; end - in > 0; in += 3, cur += 4) { + unsigned int n = *in << 16; + cur[3] = end - in > 2 ? base64_table[(n |= in[2]) & 0x3f] : '='; + cur[2] = end - in > 1 ? base64_table[((n |= in[1]<<8) >> 6) & 0x3f] : '='; + cur[1] = base64_table[(n >> 12) & 0x3f]; + cur[0] = base64_table[n >> 18]; + } + *cur = '\0'; + + return ret; +} + /* * As cut, but explicitly supply the text together with its length. */ @@ -948,7 +970,15 @@ cuttext(ZLE_STRING_T line, int ct, int flags) return; UNMETACHECK(); - if (zmod.flags & MOD_VIBUF) { + if (zmod.flags & MOD_OSSEL) { + int cutll; + char *mbcut = zlelineasstring(line, ct, 0, &cutll, NULL, 1); + unmetafy(mbcut, &cutll); + mbcut = base64_encode(mbcut, cutll); + + fprintf(shout, "\e]52;%c;%s\a", zmod.flags & MOD_CLIP ? 'c' : 'p', + mbcut); + } else if (zmod.flags & MOD_VIBUF) { struct cutbuffer *b = &vibuf[zmod.vibuf]; if (!(zmod.flags & MOD_VIAPP) || !b->buf) { diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c index 0f198d0e8..24d9de6ea 100644 --- a/Src/Zle/zle_vi.c +++ b/Src/Zle/zle_vi.c @@ -1014,6 +1014,9 @@ int visetbuffer(char **args) { ZLE_INT_T ch; + ZLE_CHAR_T *match = ZWS("_*+"); + int registermod[] = { MOD_NULL, MOD_PRI, MOD_CLIP }; + ZLE_CHAR_T *found; if (*args) { ch = **args; @@ -1022,12 +1025,12 @@ visetbuffer(char **args) } else { ch = getfullchar(0); } - if (ch == ZWC('_')) { - zmod.flags |= MOD_NULL; + if ((found = ZS_strchr(match, ch))) { + zmod.flags |= registermod[found - match]; prefixflag = 1; return 0; } else - zmod.flags &= ~MOD_NULL; + zmod.flags &= ~(MOD_NULL | MOD_OSSEL); if ((ch < ZWC('0') || ch > ZWC('9')) && (ch < ZWC('a') || ch > ZWC('z')) && (ch < ZWC('A') || ch > ZWC('Z'))) -- cgit v1.2.3 From 1be52186b4aed40eb9fe295691932037ecf6c6ab Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Fri, 2 Dec 2022 19:19:27 +0900 Subject: 51079: metafy sep in array subscript flag (s:sep:) this enable sep to contain \0 etc. --- ChangeLog | 5 +++++ Src/params.c | 10 +++++++--- Test/D06subscript.ztst | 5 +++++ 3 files changed, 17 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 2ae9ccade..27c6fee98 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-12-02 Jun-ichi Takimoto + + * 51079: Src/params.c, Test/D06subscript.ztst: metafy sep in the + array subscript flag (s:sep:) so that sep can contain \0 etc. + 2022-11-23 Daniel Shahaf * unposted (cf. 51016): Test/C01arith.ztst: Add a test case diff --git a/Src/params.c b/Src/params.c index 27ea82298..f1fe38955 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1262,7 +1262,6 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, /* first parse any subscription flags */ if (v->pm && (*s == '(' || *s == Inpar)) { int escapes = 0; - int waste; for (s++; *s != ')' && *s != Outpar && s != *str; s++) { switch (*s) { case 'r': @@ -1339,8 +1338,13 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, sav = *t; *t = '\0'; s += arglen; - sep = escapes ? getkeystring(s, &waste, GETKEYS_SEP, NULL) - : dupstring(s); + if (escapes) { + int len; + sep = getkeystring(s, &len, GETKEYS_SEP, NULL); + sep = metafy(sep, len, META_HREALLOC); + } + else + sep = dupstring(s); *t = sav; s = t + arglen - 1; break; diff --git a/Test/D06subscript.ztst b/Test/D06subscript.ztst index adbd398c4..21127e641 100644 --- a/Test/D06subscript.ztst +++ b/Test/D06subscript.ztst @@ -294,3 +294,8 @@ F:Regression test for workers/42297 [[ ${a[$i]} = ${a[i]} ]] 0f:Math evaluation of commas in array subscripts F:In math, (($i)) should be the same as ((i)), see workers/47748. + + string=$'foo\0bar' + echo ${string[(pws:\0:)1]} +0:Word splitting by NUL +>foo -- cgit v1.2.3 From 41b402d36d0aeac594cf424a9e46b5edb20c815d Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Fri, 2 Dec 2022 19:34:55 +0900 Subject: 51080: allow multibyte chars in glob qualifier (u:uname:) --- ChangeLog | 3 +++ Src/glob.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 27c6fee98..33f3f90de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-12-02 Jun-ichi Takimoto + * 51080: Src/glob.c: allow multibyte characters in glob qualifier + (u:uname:) + * 51079: Src/params.c, Test/D06subscript.ztst: metafy sep in the array subscript flag (s:sep:) so that sep can contain \0 etc. diff --git a/Src/glob.c b/Src/glob.c index 400be12d5..490bafc37 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -1481,7 +1481,7 @@ zglob(LinkList list, LinkNode np, int nountok) sav = *tt; *tt = '\0'; - if ((pw = getpwnam(s + arglen))) + if ((pw = getpwnam(unmeta(s + arglen)))) data = pw->pw_uid; else { zerr("unknown username '%s'", s + arglen); -- cgit v1.2.3 From 23dc19f005b6a9ac0740b46155f14dbcfa697421 Mon Sep 17 00:00:00 2001 From: Philippe Altherr Date: Sat, 3 Dec 2022 20:31:42 -0800 Subject: 51001: Reverts 8839e969b, most of 1ba8714a, and d873ed60. Also correct ChangeLog --- ChangeLog | 11 ++--------- NEWS | 9 --------- Src/exec.c | 4 +--- Src/loop.c | 12 ++++++------ Test/C03traps.ztst | 15 ++++++++++----- 5 files changed, 19 insertions(+), 32 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 33f3f90de..3b77f1023 100644 --- a/ChangeLog +++ b/ChangeLog @@ -31,20 +31,13 @@ 2022-11-09 Bart Schaefer - * 50929: Src/exec.c: fix handling of ERR_RETURN bent by 50928. - - * 50928: News, Src/exec.c, Test/C03traps.ztst: fix tests for 50897, - mention behavior change in NEWS + * 50928: Test/C03traps.ztst: scoping of ERR_RETURN in test + (most of this patch was not retained) * 50922: Src/exec.c, Src/jobs.c: fix additional cases of signals for current shell jobs on the right of a pipeline. Backs out part of 50874. -2022-11-08 Bart Schaefer - - * 50897: Src/exec.c, Src/loop.c: nonzero status of complex - commands should trigger ERR_EXIT - 2022-11-08 Peter Stephenson * users/28338: Src/lex.c, Test/D08cmdsubst.ztst: edge case of an diff --git a/NEWS b/NEWS index 9c28169bb..cdafd1ff5 100644 --- a/NEWS +++ b/NEWS @@ -4,15 +4,6 @@ CHANGES FROM PREVIOUS VERSIONS OF ZSH Note also the list of incompatibilities in the README file. -Changes since 5.9 ------------------ - -Handling of ERR_EXIT is corrected when the final status of a structured -command (for, select, while, repeat, if, case, or a list in braces) is -nonzero. To be compatible with other shells, "zsh -e" now exits in -those circumstances, whereas previous versions did not. This does not -affect the handling of nonzero status within conditional statements. - Changes since 5.8.1 ------------------- diff --git a/Src/exec.c b/Src/exec.c index ce0c1f1ad..b0f42ae67 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -451,7 +451,7 @@ execcursh(Estate state, int do_exec) cmdpop(); state->pc = end; - this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); + this_noerrexit = 1; return lastval; } @@ -1442,8 +1442,6 @@ execlist(Estate state, int dont_change_job, int exiting) execsimple(state); else execpline(state, code, ltype, (ltype & Z_END) && exiting); - if (!locallevel || unset(ERRRETURN)) - this_noerrexit = noerrexit; state->pc = next; goto sublist_done; break; diff --git a/Src/loop.c b/Src/loop.c index be5261369..db5b3e097 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -208,7 +208,7 @@ execfor(Estate state, int do_exec) loops--; simple_pline = old_simple_pline; state->pc = end; - this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); + this_noerrexit = 1; return lastval; } @@ -336,7 +336,7 @@ execselect(Estate state, UNUSED(int do_exec)) loops--; simple_pline = old_simple_pline; state->pc = end; - this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); + this_noerrexit = 1; return lastval; } @@ -478,7 +478,7 @@ execwhile(Estate state, UNUSED(int do_exec)) popheap(); loops--; state->pc = end; - this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); + this_noerrexit = 1; return lastval; } @@ -532,7 +532,7 @@ execrepeat(Estate state, UNUSED(int do_exec)) loops--; simple_pline = old_simple_pline; state->pc = end; - this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); + this_noerrexit = 1; return lastval; } @@ -587,7 +587,7 @@ execif(Estate state, int do_exec) lastval = 0; } state->pc = end; - this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); + this_noerrexit = 1; return lastval; } @@ -701,7 +701,7 @@ execcase(Estate state, int do_exec) if (!anypatok) lastval = 0; - this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); + this_noerrexit = 1; return lastval; } diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst index 5cc45e2de..a7a040d70 100644 --- a/Test/C03traps.ztst +++ b/Test/C03traps.ztst @@ -726,7 +726,8 @@ F:Must be tested with a top-level script rather than source or function done print OK ) -1:ERR_EXIT triggered by status 1 at end of for +0:ERR_EXIT not triggered by status 1 at end of for +>OK (setopt err_exit integer x=0 @@ -735,7 +736,8 @@ F:Must be tested with a top-level script rather than source or function done print OK ) -1:ERR_EXIT triggered by status 1 at end of while +0:ERR_EXIT not triggered by status 1 at end of while +>OK (setopt err_exit repeat 1; do @@ -743,7 +745,8 @@ F:Must be tested with a top-level script rather than source or function done print OK ) -1:ERR_EXIT triggered by status 1 at end of repeat +0:ERR_EXIT not triggered by status 1 at end of repeat +>OK (setopt err_exit if true; then @@ -751,7 +754,8 @@ F:Must be tested with a top-level script rather than source or function fi print OK ) -1:ERR_EXIT triggered by status 1 at end of if +0:ERR_EXIT not triggered by status 1 at end of if +>OK (setopt err_exit { @@ -759,7 +763,8 @@ F:Must be tested with a top-level script rather than source or function } print OK ) -1:ERR_EXIT triggered by status 1 at end of { } +0:ERR_EXIT not triggered by status 1 at end of { } +>OK unsetopt err_exit err_return (setopt err_exit -- cgit v1.2.3 From fda6fd9513ffdbd75c490e8e55ce33f370a9bd17 Mon Sep 17 00:00:00 2001 From: Philippe Altherr Date: Sat, 3 Dec 2022 20:35:58 -0800 Subject: 51001: fix for ERR_EXIT with "always" blocks; update tests --- Src/loop.c | 1 + Test/C03traps.ztst | 36 +++++++++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 7 deletions(-) (limited to 'Src') diff --git a/Src/loop.c b/Src/loop.c index db5b3e097..7c3e04b8a 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -793,6 +793,7 @@ exectry(Estate state, int do_exec) cmdpop(); popheap(); state->pc = end; + this_noerrexit = 1; return endval; } diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst index a7a040d70..4719dfd57 100644 --- a/Test/C03traps.ztst +++ b/Test/C03traps.ztst @@ -721,22 +721,19 @@ F:Must be tested with a top-level script rather than source or function >Good (setopt err_exit - for x in y; do - false && true - done + false && true print OK ) -0:ERR_EXIT not triggered by status 1 at end of for +0:ERR_EXIT not triggered by "false && true" >OK (setopt err_exit - integer x=0 - while (( ! x++ )); do + for x in y; do false && true done print OK ) -0:ERR_EXIT not triggered by status 1 at end of while +0:ERR_EXIT not triggered by status 1 at end of for >OK (setopt err_exit @@ -755,6 +752,31 @@ F:Must be tested with a top-level script rather than source or function print OK ) 0:ERR_EXIT not triggered by status 1 at end of if +>OK + + (setopt err_exit + loop=true + while print COND; $loop; do + loop=false + false && true + done + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of while +>COND +>COND +>OK + + (setopt err_exit + { + false && true + } always { + print ALWAYS + } + print OK + ) +0:ERR_EXIT not triggered by status 1 at end of always +>ALWAYS >OK (setopt err_exit -- cgit v1.2.3 From d47b8480f0eb883d54fcbef22999bf26d13d56a4 Mon Sep 17 00:00:00 2001 From: Philippe Altherr Date: Sat, 3 Dec 2022 20:42:13 -0800 Subject: 51001: fix for ERR_EXIT with pipeline negation ("!"); update tests --- Src/exec.c | 13 ++++++++----- Test/C03traps.ztst | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'Src') diff --git a/Src/exec.c b/Src/exec.c index b0f42ae67..d8501ca68 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -63,7 +63,10 @@ typedef struct funcsave *Funcsave; /**/ int noerrexit; -/* used to suppress ERREXIT or ERRRETURN for one occurrence: 0 or 1 */ +/* + * used to suppress ERREXIT and ERRRETURN for the command under evaluation. + * 0 or 1 + */ /**/ int this_noerrexit; @@ -1429,10 +1432,7 @@ execlist(Estate state, int dont_change_job, int exiting) if (!oldnoerrexit) noerrexit = isend ? 0 : NOERREXIT_EXIT | NOERREXIT_RETURN; if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT) { - /* suppress errexit for "! this_command" */ - if (isend) - this_noerrexit = 1; - /* suppress errexit for ! */ + /* suppress errexit for the commands in ! */ noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; } switch (WC_SUBLIST_TYPE(code)) { @@ -1443,6 +1443,9 @@ execlist(Estate state, int dont_change_job, int exiting) else execpline(state, code, ltype, (ltype & Z_END) && exiting); state->pc = next; + /* suppress errexit for the command "! ..." */ + if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT) + this_noerrexit = 1; goto sublist_done; break; case WC_SUBLIST_AND: diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst index 4719dfd57..08e24a32e 100644 --- a/Test/C03traps.ztst +++ b/Test/C03traps.ztst @@ -720,6 +720,21 @@ F:Must be tested with a top-level script rather than source or function 0:ERR_RETURN in "else" branch in nested function >Good + (setopt err_exit + ! true + print OK + ) +0:ERR_EXIT not triggered by "! true" +>OK + + (setopt err_exit + fn() { true } + ! fn + print OK + ) +0:ERR_EXIT not triggered by "! fn" +>OK + (setopt err_exit false && true print OK -- cgit v1.2.3 From dd3ba3d5991f5c99334742147fb2213b8c400a42 Mon Sep 17 00:00:00 2001 From: Philippe Altherr Date: Sat, 3 Dec 2022 20:44:10 -0800 Subject: 51001: fix for ERR_EXIT following shell function; update tests --- Src/exec.c | 10 +------ Test/C03traps.ztst | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 9 deletions(-) (limited to 'Src') diff --git a/Src/exec.c b/Src/exec.c index d8501ca68..43df8211a 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -5932,15 +5932,6 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) * This function is forced to return. */ retflag = 0; - /* - * The calling function isn't necessarily forced to return, - * but it should be made sensitive to ERR_EXIT and - * ERR_RETURN as the assumptions we made at the end of - * constructs within this function no longer apply. If - * there are cases where this is not true, they need adding - * to C03traps.ztst. - */ - this_noerrexit = 0; breaks = funcsave->breaks; } freearray(pparams); @@ -6010,6 +6001,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) trap_return++; ret = lastval; noerrexit = funcsave->noerrexit; + this_noerrexit = 0; if (noreturnval) { lastval = funcsave->lastval; numpipestats = funcsave->numpipestats; diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst index 08e24a32e..a8880673f 100644 --- a/Test/C03traps.ztst +++ b/Test/C03traps.ztst @@ -742,6 +742,15 @@ F:Must be tested with a top-level script rather than source or function 0:ERR_EXIT not triggered by "false && true" >OK + (setopt err_exit + fn() { + false && true + } + fn + print OK + ) +1:ERR_EXIT not triggered by "false && true" but by return from fn + (setopt err_exit for x in y; do false && true @@ -751,6 +760,17 @@ F:Must be tested with a top-level script rather than source or function 0:ERR_EXIT not triggered by status 1 at end of for >OK + (setopt err_exit + fn() { + for x in y; do + false && true + done + } + fn + print OK + ) +1:ERR_EXIT not triggered by status 1 at end of for but by return from fn + (setopt err_exit repeat 1; do false && true @@ -760,6 +780,17 @@ F:Must be tested with a top-level script rather than source or function 0:ERR_EXIT not triggered by status 1 at end of repeat >OK + (setopt err_exit + fn() { + repeat 1; do + false && true + done + } + fn + print OK + ) +1:ERR_EXIT not triggered by status 1 at end of repeat but by return from fn + (setopt err_exit if true; then false && true @@ -769,6 +800,17 @@ F:Must be tested with a top-level script rather than source or function 0:ERR_EXIT not triggered by status 1 at end of if >OK + (setopt err_exit + fn() { + if true; then + false && true + fi + } + fn + print OK + ) +1:ERR_EXIT not triggered by status 1 at end of if but by return from fn + (setopt err_exit loop=true while print COND; $loop; do @@ -782,6 +824,21 @@ F:Must be tested with a top-level script rather than source or function >COND >OK + (setopt err_exit + fn() { + loop=true + while print COND; $loop; do + loop=false + false && true + done + } + fn + print OK + ) +1:ERR_EXIT not triggered by status 1 at end of while but by return from fn +>COND +>COND + (setopt err_exit { false && true @@ -794,6 +851,20 @@ F:Must be tested with a top-level script rather than source or function >ALWAYS >OK + (setopt err_exit + fn() { + { + false && true + } always { + print ALWAYS + } + } + fn + print OK + ) +1:ERR_EXIT not triggered by status 1 at end of always but by return from fn +>ALWAYS + (setopt err_exit { false && true @@ -803,6 +874,17 @@ F:Must be tested with a top-level script rather than source or function 0:ERR_EXIT not triggered by status 1 at end of { } >OK + (setopt err_exit + fn() { + { + false && true + } + } + fn + print OK + ) +1:ERR_EXIT not triggered by status 1 at end of { } but by return from fn + unsetopt err_exit err_return (setopt err_exit for x in y; do -- cgit v1.2.3 From 259f1e944b96715fda25f7ba227da05bdb7e600f Mon Sep 17 00:00:00 2001 From: Philippe Altherr Date: Sat, 3 Dec 2022 21:03:36 -0800 Subject: 51071: fix ERR_RETURN for functions in conditional statements --- ChangeLog | 4 ++++ Src/exec.c | 12 +++++------- Test/C03traps.ztst | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index fdd9ac26c..215da1b19 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2022-12-03 Bart Schaefer + * Philippe Altherr: 51071: Src/exec.c, Test/C03traps.ztst: fix + ERR_RETURN when a function using && / || is called within another + statement using && / || + * Philippe Altherr: 51001: Src/exec.c, Src/loop.c, Test/C03traps.ztst: adjust handling of ERR_EXIT to more closely align with POSIX and with other shells; add corresponding tests diff --git a/Src/exec.c b/Src/exec.c index 43df8211a..711d8f374 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1427,14 +1427,12 @@ execlist(Estate state, int dont_change_job, int exiting) goto sublist_done; } while (wc_code(code) == WC_SUBLIST) { - int isend = (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END); + int isandor = WC_SUBLIST_TYPE(code) != WC_SUBLIST_END; + int isnot = WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT; next = state->pc + WC_SUBLIST_SKIP(code); - if (!oldnoerrexit) - noerrexit = isend ? 0 : NOERREXIT_EXIT | NOERREXIT_RETURN; - if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT) { - /* suppress errexit for the commands in ! */ + /* suppress errexit for commands before && and || and after ! */ + if (isandor || isnot) noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; - } switch (WC_SUBLIST_TYPE(code)) { case WC_SUBLIST_END: /* End of sublist; just execute, ignoring status. */ @@ -1444,7 +1442,7 @@ execlist(Estate state, int dont_change_job, int exiting) execpline(state, code, ltype, (ltype & Z_END) && exiting); state->pc = next; /* suppress errexit for the command "! ..." */ - if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT) + if (isnot) this_noerrexit = 1; goto sublist_done; break; diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst index a8880673f..b7132da81 100644 --- a/Test/C03traps.ztst +++ b/Test/C03traps.ztst @@ -670,6 +670,22 @@ F:Must be tested with a top-level script rather than source or function >before-out >before-in + (set -o err_return + fn() { + print before-in + { false; true } && true + print after-in + } + print before-out + fn && true + print after-out + ) +0:ERR_RETURN not triggered on LHS of "&&" in function on LHS of "&&" (regression test) +>before-out +>before-in +>after-in +>after-out + mkdir -p zdotdir print >zdotdir/.zshenv ' setopt norcs errreturn -- cgit v1.2.3 From f253ea6b9d14901e24fa6a376936effad9e6547b Mon Sep 17 00:00:00 2001 From: Philippe Altherr Date: Sat, 3 Dec 2022 21:14:26 -0800 Subject: 51076: fix ERR_EXIT when used with "eval" or "source"; documentary comments --- ChangeLog | 3 +++ Src/exec.c | 46 +++++++++++++++++++++++++++++++++++++++++++--- Test/C03traps.ztst | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 215da1b19..2355b4fa1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-12-03 Bart Schaefer + * Philippe Altherr: 51076: Src/exec.c, Test/C03traps.ztst: fix + ERR_EXIT when used with "eval" or "source"; documentary comments + * Philippe Altherr: 51071: Src/exec.c, Test/C03traps.ztst: fix ERR_RETURN when a function using && / || is called within another statement using && / || diff --git a/Src/exec.c b/Src/exec.c index 711d8f374..8ff6489ec 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -56,7 +56,17 @@ struct funcsave { typedef struct funcsave *Funcsave; /* - * used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT. + * Used to suppress ERREXIT and trapping of SIGZERR, SIGEXIT in the + * evaluation of sub-commands of the command under evaluation. The + * variable must be updated before the evaluation of the sub-commands + * starts and restored to its previous state right after that + * evaluation ends. The variable is read and acted upon in execlist. + * + * A good usage example can be found in execwhile in loop.c, which + * evaluates while statements. The variable is updated to disable + * ERREXIT just before evaluating the while's condition and restored + * to its previous state right after the evaluation of the condition. + * * Bits from noerrexit_bits. */ @@ -64,7 +74,36 @@ typedef struct funcsave *Funcsave; int noerrexit; /* - * used to suppress ERREXIT and ERRRETURN for the command under evaluation. + * Used to suppress ERREXIT and ERRRETURN for the command under + * evaluation. The variable must be enabled (set to 1) at the very + * end of the evaluation of the command. It must come after the + * evaluation of any sub-commands of the command under evaluation. The + * variable is read and acted upon in execlist, which also takes care + * of initialising and resetting it to 0. + * + * Unlike the variable noerrexit, whose state applies to the + * evaluation of whole sub-commands (and their direct and indirect + * sub-commands), the scope of the variable this_noerrexit is much + * more localized. ERREXIT and ERRRETURN are triggered at the end of + * the function execlist after the evaluation of some or all of the + * list's sub-commands. The role of the variable this_noerrexit is to + * give to the functions evaluating the list's sub-commands the + * possibility to tell the calling execlist not to trigger ERREXIT and + * ERRRETURN. In other words, the variable acts as an additional + * return value between the called evaluation functions and the + * calling execlist. For that reason the variable must always be set + * as late as possible and in particular after any sub-command + * evaluation. If the variable is set before the evaluation of a + * sub-command, if may affect the wrong execlist, if the sub-command + * evaluation involves another execlist call, and/or the variable may + * get modified by the sub-command evaluation and thus wouldn't return + * the desired value to the calling execlist. + * + * Good usage examples can be found in the exec functions in loop.c, + * which evaluate compound commands. The variable is enabled right + * before returning from the functions, after all the sub-commands of + * the compound commands have already been evaluated. + * * 0 or 1 */ @@ -1427,6 +1466,7 @@ execlist(Estate state, int dont_change_job, int exiting) goto sublist_done; } while (wc_code(code) == WC_SUBLIST) { + this_noerrexit = 0; int isandor = WC_SUBLIST_TYPE(code) != WC_SUBLIST_END; int isnot = WC_SUBLIST_FLAGS(code) & WC_SUBLIST_NOT; next = state->pc + WC_SUBLIST_SKIP(code); @@ -1582,6 +1622,7 @@ sublist_done: break; code = *state->pc++; } + this_noerrexit = 0; pline_level = old_pline_level; list_pipe = old_list_pipe; list_pipe_job = old_list_pipe_job; @@ -5999,7 +6040,6 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) trap_return++; ret = lastval; noerrexit = funcsave->noerrexit; - this_noerrexit = 0; if (noreturnval) { lastval = funcsave->lastval; numpipestats = funcsave->numpipestats; diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst index b7132da81..e0b6afb5f 100644 --- a/Test/C03traps.ztst +++ b/Test/C03traps.ztst @@ -954,6 +954,47 @@ F:Must be tested with a top-level script rather than source or function 1:ERR_EXIT triggered by status 1 at end of anon func >Still functioning + (setopt err_exit + loop=true; while print loop $? >&2; $loop; do loop=false; false && true; done + print done $? >&2 + ) +0: ERR_EXIT neither triggered inside loop nor triggered by while statement +?loop 0 +?loop 1 +?done 1 + + (setopt err_exit + { loop=true; while print loop $? >&2; $loop; do loop=false; false && true; done } || false + print done $? >&2 + ) +1: ERR_EXIT not triggered inside loop but triggered by rhs of || +?loop 0 +?loop 1 + + (setopt err_exit + eval 'loop=true; while print loop $? >&2; $loop; do loop=false; false && true; done' + print done $? >&2 + ) +1: ERR_EXIT not triggered inside loop but triggered by eval +?loop 0 +?loop 1 + + (setopt err_exit + source <(echo 'loop=true; while print loop $? >&2; $loop; do loop=false; false && true; done') + print done $? >&2 + ) +1: ERR_EXIT not triggered inside loop but triggered by source +?loop 0 +?loop 1 + + (setopt err_exit + v=$(loop=true; while print loop $? >&2; $loop; do loop=false; false && true; done) + print done $? >&2 + ) +1: ERR_EXIT not triggered inside loop but triggered by command substitution +?loop 0 +?loop 1 + if zmodload zsh/system 2>/dev/null; then ( trap 'echo TERM; exit 2' TERM -- cgit v1.2.3 From ab9c579ef9a1ad6482267719f5d031f6a5dbf24e Mon Sep 17 00:00:00 2001 From: Philippe Altherr Date: Sat, 3 Dec 2022 21:35:51 -0800 Subject: 51098: remove unreachable NOERREXIT_UNTIL_EXEC code and effects --- ChangeLog | 3 +++ Src/exec.c | 13 +------------ Src/loop.c | 15 +++------------ Src/zsh.h | 2 -- 4 files changed, 7 insertions(+), 26 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 2355b4fa1..91c8e69d3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-12-03 Bart Schaefer + * Philippe Altherr: 51098: Src/exec.c, Src/loop.c, Src/zsh.h: + remove unreachable NOERREXIT_UNTIL_EXEC code and effects + * Philippe Altherr: 51076: Src/exec.c, Test/C03traps.ztst: fix ERR_EXIT when used with "eval" or "source"; documentary comments diff --git a/Src/exec.c b/Src/exec.c index 8ff6489ec..1dd569019 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1559,14 +1559,7 @@ execlist(Estate state, int dont_change_job, int exiting) state->pc--; sublist_done: - /* - * See hairy code near the end of execif() for the - * following. "noerrexit " only applies until - * we hit execcmd on the way down. We're now - * on the way back up, so don't restore it. - */ - if (!(oldnoerrexit & NOERREXIT_UNTIL_EXEC)) - noerrexit = oldnoerrexit; + noerrexit = oldnoerrexit; if (sigtrapped[SIGDEBUG] && !isset(DEBUGBEFORECMD) && !donedebug) { /* @@ -3246,10 +3239,6 @@ execcmd_exec(Estate state, Execcmd_params eparams, } else preargs = NULL; - /* if we get this far, it is OK to pay attention to lastval again */ - if (noerrexit & NOERREXIT_UNTIL_EXEC) - noerrexit = 0; - /* Do prefork substitutions. * * Decide if we need "magic" handling of ~'s etc. in diff --git a/Src/loop.c b/Src/loop.c index 7c3e04b8a..61543ed73 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -569,23 +569,14 @@ execif(Estate state, int do_exec) s = 1; state->pc = next; } + noerrexit = olderrexit; if (run) { - /* we need to ignore lastval until we reach execcmd() */ - if (olderrexit || run == 2) - noerrexit = olderrexit; - else if (lastval) - noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN | NOERREXIT_UNTIL_EXEC; - else - noerrexit &= ~ (NOERREXIT_EXIT | NOERREXIT_RETURN); cmdpush(run == 2 ? CS_ELSE : (s ? CS_ELIFTHEN : CS_IFTHEN)); execlist(state, 1, do_exec); cmdpop(); - } else { - noerrexit = olderrexit; - if (!retflag && !errflag) - lastval = 0; - } + } else if (!retflag && !errflag) + lastval = 0; state->pc = end; this_noerrexit = 1; diff --git a/Src/zsh.h b/Src/zsh.h index 40f9ea537..703231ca2 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -2220,8 +2220,6 @@ enum noerrexit_bits { NOERREXIT_EXIT = 1, /* Suppress ERR_RETURN: per function call */ NOERREXIT_RETURN = 2, - /* NOERREXIT only needed on way down */ - NOERREXIT_UNTIL_EXEC = 4, /* Force exit on SIGINT */ NOERREXIT_SIGNAL = 8 }; -- cgit v1.2.3 From 8086f106159c2e9fc562b5ce88b8aefdb5fe5d23 Mon Sep 17 00:00:00 2001 From: Philippe Altherr Date: Sat, 3 Dec 2022 21:46:42 -0800 Subject: 51094: consistent use of bit-manipulation for noerrexit value changes --- ChangeLog | 3 +++ Src/exec.c | 6 +++--- Src/loop.c | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 91c8e69d3..8caeecd81 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-12-03 Bart Schaefer + * Philippe Altherr: 51094: Src/exec.c, Src/loop.c: consistent + use of bit-manipulation for noerrexit flag value changes + * Philippe Altherr: 51098: Src/exec.c, Src/loop.c, Src/zsh.h: remove unreachable NOERREXIT_UNTIL_EXEC code and effects diff --git a/Src/exec.c b/Src/exec.c index 1dd569019..1810fca5e 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1415,7 +1415,7 @@ execlist(Estate state, int dont_change_job, int exiting) int oerrexit_opt = opts[ERREXIT]; Param pm; opts[ERREXIT] = 0; - noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; + noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN; if (ltype & Z_SIMPLE) /* skip the line number */ pc2++; pm = assignsparam("ZSH_DEBUG_CMD", @@ -1472,7 +1472,7 @@ execlist(Estate state, int dont_change_job, int exiting) next = state->pc + WC_SUBLIST_SKIP(code); /* suppress errexit for commands before && and || and after ! */ if (isandor || isnot) - noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; + noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN; switch (WC_SUBLIST_TYPE(code)) { case WC_SUBLIST_END: /* End of sublist; just execute, ignoring status. */ @@ -1568,7 +1568,7 @@ sublist_done: */ int oerrexit_opt = opts[ERREXIT]; opts[ERREXIT] = 0; - noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; + noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN; exiting = donetrap; ret = lastval; dotrap(SIGDEBUG); diff --git a/Src/loop.c b/Src/loop.c index 61543ed73..88c55dd1a 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -428,7 +428,7 @@ execwhile(Estate state, UNUSED(int do_exec)) } else { for (;;) { state->pc = loop; - noerrexit = NOERREXIT_EXIT | NOERREXIT_RETURN; + noerrexit |= NOERREXIT_EXIT | NOERREXIT_RETURN; /* In case the test condition is a functional no-op, * make sure signal handlers recognize ^C to end the loop. */ -- cgit v1.2.3 From 2028539cb168d652dfa77e5b85b382c319b3a99a Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Fri, 9 Dec 2022 19:30:35 -0800 Subject: 51161: correct errno after closing xtrace FD --- ChangeLog | 2 ++ Src/exec.c | 3 +++ 2 files changed, 5 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 7472f056c..0a2a880f4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2022-12-09 Bart Schaefer + * 51161: Src/exec.c: correct errno after closing xtrace FD + * Shohei YOSHIDA: 51111: Completion/Unix/Command/_global: update completion for global to version 6.6.8 diff --git a/Src/exec.c b/Src/exec.c index 1810fca5e..a1059af5e 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -4336,10 +4336,13 @@ execcmd_exec(Estate state, Execcmd_params eparams, } } if (newxtrerr) { + int eno = errno; fil = fileno(newxtrerr); fclose(newxtrerr); xtrerr = oxtrerr; + /* Call zclose() to clean up internal tables, ignore EBADF */ zclose(fil); + errno = eno; } zsfree(STTYval); -- cgit v1.2.3 From 67d4bf5bb936a5b95160410b4790f2bf4113c75f Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 12 Dec 2022 10:30:13 +0000 Subject: 51134: ! return doesn't change the return status --- Src/exec.c | 2 +- Test/A01grammar.ztst | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/Src/exec.c b/Src/exec.c index a1059af5e..7001fd615 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1961,7 +1961,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) } else unqueue_signals(); - if ((slflags & WC_SUBLIST_NOT) && !errflag) + if ((slflags & WC_SUBLIST_NOT) && !errflag && !retflag) lastval = !lastval; } if (!pline_level) diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst index 0312fe94e..b3aea1055 100644 --- a/Test/A01grammar.ztst +++ b/Test/A01grammar.ztst @@ -970,3 +970,15 @@ F:its expectations. 0:Non-interactive shell command input is line buffered >Value is first >Value is second + + fn() { + ! false + } +0:! inverts the status of implicit return + + fn () { + false + ! return + } + fn +1:! does not affect return status of explicit return -- cgit v1.2.3 From 6d49734d46a66b572cf064f60dac8d9e0ad309d0 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 13 Dec 2022 21:11:33 -0800 Subject: 51210: Clear errflag before calling EXIT trap If this is not done, special cases such as failures in special builtins or errors in math expressions skip the trap execution. --- ChangeLog | 3 +++ Src/exec.c | 4 ++++ 2 files changed, 7 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index cea087a01..3536d0b1e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-12-13 Bart Schaefer + * 51210: Src/exec.c: Clear errflag before calling EXIT trap, + otherwise the trap is skipped for special-case errors in builtins + * Philippe Altherr: 51198: Doc/Zsh/options.yo: Clarify and expand ERR_EXIT and ERR_RETURN documentation to include updated behavior diff --git a/Src/exec.c b/Src/exec.c index 7001fd615..2b7e0c7c5 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1598,6 +1598,7 @@ sublist_done: (isset(ERRRETURN) && !errreturn)) && !(noerrexit & NOERREXIT_EXIT); if (errexit) { + errflag = 0; if (sigtrapped[SIGEXIT]) dotrap(SIGEXIT); if (mypid != getpid()) @@ -1630,9 +1631,12 @@ sublist_done: thisjob = cj; if (exiting && sigtrapped[SIGEXIT]) { + int eflag = errflag; + errflag = 0; /* Clear the context for trap */ dotrap(SIGEXIT); /* Make sure this doesn't get executed again. */ sigtrapped[SIGEXIT] = 0; + errflag = eflag; } unqueue_signals(); -- cgit v1.2.3 From 5c9713603d571fd228efc4c25c0efc9064d95a87 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Thu, 15 Dec 2022 20:38:08 +0000 Subject: unposted: zsh.h: lextok: Add an explanatory comment with a cross-reference. --- ChangeLog | 5 +++++ Src/zsh.h | 3 +++ 2 files changed, 8 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 3536d0b1e..bcf50f80c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-12-15 Daniel Shahaf + + * unposted: Src/zsh.h: lextok: Add an explanatory comment with + a cross-reference. + 2022-12-13 Bart Schaefer * 51210: Src/exec.c: Clear errflag before calling EXIT trap, diff --git a/Src/zsh.h b/Src/zsh.h index 703231ca2..b7eb39804 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -309,6 +309,9 @@ enum { /* * Lexical tokens: unlike the character tokens above, these never * appear in strings and don't necessarily represent a single character. + * + * See Src/lex.c:tokstrings[] for hints on what these mean. Note that + * SEPER or SEMI are both stringified as ";"./ */ enum lextok { -- cgit v1.2.3 From 56e7b147adc4404419b6295d25bc0ed179f6d889 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Thu, 15 Dec 2022 21:35:21 +0000 Subject: unposted: Follow-up to the last commit: Fix a typo in a comment. --- ChangeLog | 3 +++ Src/zsh.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index bcf50f80c..b0c3f79cb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-12-15 Daniel Shahaf + * unposted: Src/zsh.h: Follow-up to the last commit: Fix a typo + in a comment. + * unposted: Src/zsh.h: lextok: Add an explanatory comment with a cross-reference. diff --git a/Src/zsh.h b/Src/zsh.h index b7eb39804..6f68df6a4 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -311,7 +311,7 @@ enum { * appear in strings and don't necessarily represent a single character. * * See Src/lex.c:tokstrings[] for hints on what these mean. Note that - * SEPER or SEMI are both stringified as ";"./ + * SEPER or SEMI are both stringified as ";". */ enum lextok { -- cgit v1.2.3 From 7fb6c133bfdf445b4478897adc142ed7d07b5fd6 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Fri, 16 Dec 2022 23:12:54 +0100 Subject: 51215: consume whole CSI sequences from the input This affects CSI sequences that aren't explicitly bound but arrive within the usual KEYTIMEOUT time limits. A single undefined-key widget is run instead of unintended bindings for Escape and other characters in the sequence. --- ChangeLog | 6 ++++++ Src/Zle/zle_keymap.c | 27 +++++++++++++++++++++++++-- Test/X02zlevi.ztst | 7 +++++++ Test/X03zlebindkey.ztst | 22 ++++++++++++++++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index acde99fb4..e9e572957 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2022-12-16 Oliver Kiddle + + * 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 + 2022-12-15 Daniel Shahaf * unposted: Src/zsh.h: Follow-up to the last commit: Fix a typo diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index d90838f03..48691e8d0 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -1586,7 +1586,7 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp) Thingy func = t_undefinedkey; char *str = NULL; int lastlen = 0, lastc = lastchar; - int timeout = 0; + int timeout = 0, csi = 0, startcsi; keybuflen = 0; keybuf[0] = 0; @@ -1636,7 +1636,30 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp) } #endif } - if (!ispfx) + + /* CSI key sequences have a well defined structure so if we currently + * have an incomplete one, loop so the rest of it will be included in + * the key sequence if that arrives within the timeout. */ + if (keybuflen >= 3 && !csi) { + startcsi = keybuflen - 3; + csi = keybuf[startcsi] == '\033' && keybuf[keybuflen - 2] == '['; + } + if (csi) { + csi = keybuf[keybuflen - 2] != Meta && keybuf[keybuflen - 1] >= 0x20 + && keybuf[keybuflen - 1] <= 0x3f; + /* If we reach the end of a valid CSI sequence and the matched key + * binding is for part of the CSI introduction, select instead the + * undefined-key widget and consume the full sequence from the + * input buffer. */ + if (!csi && keybuf[keybuflen - 1] >= 0x40 && + keybuf[keybuflen - 1] <= 0x7e && lastlen > startcsi && + lastlen <= startcsi + 2) { + func = t_undefinedkey; + lastlen = keybuflen; + } + } + + if (!ispfx && !csi) break; } if(!lastlen && keybuflen) diff --git a/Test/X02zlevi.ztst b/Test/X02zlevi.ztst index 203c13c32..ccfb7b1c6 100644 --- a/Test/X02zlevi.ztst +++ b/Test/X02zlevi.ztst @@ -596,6 +596,13 @@ >BUFFER: 1ls `2` $(3) "4" $'5' ${6} >CURSOR: 0 + zpty_run 'bindkey -s -a "cw" "dwi"' + zletest $'one two\e0cwyksi' + zpty_run 'bindkey -r -a "cw"' +0:for a vi command, wait to allow a longer binding to be used +>BUFFER: yksitwo +>CURSOR: 4 + %clean zmodload -ui zsh/zpty diff --git a/Test/X03zlebindkey.ztst b/Test/X03zlebindkey.ztst index 5277332a7..1b63b3920 100644 --- a/Test/X03zlebindkey.ztst +++ b/Test/X03zlebindkey.ztst @@ -37,6 +37,28 @@ >"^Xy" "bar" >"^Xy" undefined-key + zpty_run 'bindkey -s "\e[" altbracket' + zletest $'$\C-A\e[17~' + zpty_run 'bindkey -r "\e["' +0:binding to CSI introduction is not used if a full sequence arrives +>BUFFER: $ +>CURSOR: 0 + + zpty_run 'bindkey -s "\e[1" altbracketone' + zletest $'$\C-A\e[17~' + zpty_run 'bindkey -r "\e[1"' +0:binding to longer prefix of a CSI sequence is used +# we assume the user knows what they're doing +>BUFFER: altbracketone7~$ +>CURSOR: 15 + + zpty_run 'bindkey -s "\e[" altbracket' + zletest $'$\C-A\e[177' + zpty_run 'bindkey -r "\e["' +0:use prefix binding where we don't have a CSI sequence +>BUFFER: altbracket177$ +>CURSOR: 13 + # As we're only looking at definitions here, we don't # bother using the pseudo-terminal; just test in the normal fashion. bindkey -e -- 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') 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') 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') 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 81684e334d34ca315938569f3154945d0bfb05e0 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Sun, 8 Jan 2023 13:09:34 +0000 Subject: unposted: In a comment, replace a C variables glob pattern with its matches, for greppability. --- ChangeLog | 5 +++++ Src/Zle/zle_refresh.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 09398077a..c6a15b9e6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-01-08 Daniel Shahaf + + * unposted: Src/Zle/zle_refresh.c: In a comment, replace a C + variables glob pattern with its matches, for greppability. + 2023-01-06 Daniel Shahaf * unposted (cf. users/28616): diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index 30b5d4447..2db5f0642 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -36,8 +36,8 @@ * non-zero width followed by an arbitrary (but typically small) * number of characters that have zero width (combining characters). * - * The allocated size for each array is given by ?mw_size; nmw_ind - * is the next free element, i.e. nmwbuf[nmw_ind] will be the next + * The allocated size for each array is given by omw_size and nmw_size; + * nmw_ind is the next free element, i.e. nmwbuf[nmw_ind] will be the next * element to be written (we never insert into omwbuf). We initialise * nmw_ind to 1 to avoid the index stored in the character looking like a * NULL. This wastees a word but it's safer than messing with pointers. -- 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') 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 c01479a2ede78b9b53057322e4b9f5bd0a103a00 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Tue, 10 Jan 2023 20:57:03 +0100 Subject: 51280: add support for italic and faint fonts in the line editor --- ChangeLog | 4 ++++ Doc/Zsh/zle.yo | 10 +++++++++- Src/Zle/zle_refresh.c | 26 +++----------------------- Src/init.c | 19 ++++++++++++++++++- Src/prompt.c | 39 ++++++++++++++++++++++++++++++++++++-- Src/zsh.h | 52 +++++++++++++++++++++++++++++---------------------- 6 files changed, 101 insertions(+), 49 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index b3518f1bf..9e2938bf8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-01-10 Oliver Kiddle + * 51280: Doc/Zsh/zle.yo, Src/Zle/zle_refresh.c, Src/init.c, + Src/prompt.c, Src/zsh.h: add support for italic and faint + fonts in the line editor + * 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, diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo index 58700072a..60254e828 100644 --- a/Doc/Zsh/zle.yo +++ b/Doc/Zsh/zle.yo @@ -2780,9 +2780,13 @@ This works similarly to the foreground colour, except the background is not usually affected by the bold attribute. ) item(tt(bold))( -The characters in the given context are shown in a bold font. +The characters in the given context are shown with a bold font weight. Not all terminals distinguish bold fonts. ) +item(tt(faint))( +The characters in the given context are shown with a faint font weight. +Not all terminals distinguish faint fonts. +) item(tt(standout))( The characters in the given context are shown in the terminal's standout mode. The actual effect is specific to the terminal; on many terminals it @@ -2796,6 +2800,10 @@ The characters in the given context are shown underlined. Some terminals show the foreground in a different colour instead; in this case whitespace will not be highlighted. ) +item(tt(italic))( +The characters in the given context are shown in a italic font. +Not all terminals support italic fonts. +) enditem() The characters described above as `special' are as follows. The diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index ae8e5c109..d40400886 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -1193,23 +1193,10 @@ zrefresh(void) offset = predisplaylen; /* increment over it */ if (rhp->start + offset <= tmppos && tmppos < rhp->end + offset) { - if (rhp->atr & (TXTFGCOLOUR|TXTBGCOLOUR)) { - /* override colour with later entry */ - base_attr = rhp->atr; - } else { - /* no colour set yet */ - base_attr |= rhp->atr; - } + base_attr = mixattrs(rhp->atr, base_attr); } } - if (special_attr & (TXTFGCOLOUR|TXTBGCOLOUR)) { - /* keep colours from special attributes */ - all_attr = special_attr | - (base_attr & ~TXT_ATTR_COLOUR_MASK); - } else { - /* keep colours from standard attributes */ - all_attr = special_attr | base_attr; - } + all_attr = mixattrs(special_attr, base_attr); if (t == scs) /* if cursor is here, remember it */ rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln]; @@ -2441,14 +2428,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) } } } - if (special_attr & (TXTFGCOLOUR|TXTBGCOLOUR)) { - /* keep colours from special attributes */ - all_attr = special_attr | - (base_attr & ~TXT_ATTR_COLOUR_MASK); - } else { - /* keep colours from standard attributes */ - all_attr = special_attr | base_attr; - } + all_attr = mixattrs(special_attr, base_attr); if (tmpline[t0] == ZWC('\t')) { for (*vp++ = zr_sp; (vp - vbuf) & 7; ) diff --git a/Src/init.c b/Src/init.c index 75ef2e094..68621a0ad 100644 --- a/Src/init.c +++ b/Src/init.c @@ -747,7 +747,7 @@ init_shout(void) static char *tccapnams[TC_COUNT] = { "cl", "le", "LE", "nd", "RI", "up", "UP", "do", "DO", "dc", "DC", "ic", "IC", "cd", "ce", "al", "dl", "ta", - "md", "so", "us", "me", "se", "ue", "ch", + "md", "mh", "so", "us", "ZH", "me", "se", "ue", "ZR", "ch", "ku", "kd", "kl", "kr", "sc", "rc", "bc", "AF", "AB" }; @@ -872,6 +872,23 @@ init_term(void) /* The following is an attempt at a heuristic, * but it fails in some cases */ /* rprompt_indent = ((hasam && !hasbw) || hasye || !tccan(TCLEFT)); */ + + /* if there's no termcap entry for italics, use CSI 3 m */ + if (!tccan(TCITALICSBEG)) { + zsfree(tcstr[TCITALICSBEG]); + tcstr[TCITALICSBEG] = ztrdup("\033[3m"); + tclen[TCITALICSBEG] = 4; + } + if (!tccan(TCITALICSEND)) { + zsfree(tcstr[TCITALICSEND]); + tcstr[TCITALICSEND] = ztrdup("\033[23m"); + tclen[TCITALICSEND] = 5; + } + if (!tccan(TCFAINTBEG)) { + zsfree(tcstr[TCFAINTBEG]); + tcstr[TCFAINTBEG] = ztrdup("\033[2m"); + tclen[TCFAINTBEG] = 4; + } } return 1; } diff --git a/Src/prompt.c b/Src/prompt.c index 880194f87..488a90d09 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -1590,7 +1590,15 @@ applytextattributes(int flags) turnoff &= turnoff - 1; } - if (keepcount < turncount || (change & ~txtpendingattrs & TXTBOLDFACE)) { + /* enabling bold can be relied upon to disable faint + * (the converse not so as that commonly does nothing at all) */ + if (txtcurrentattrs & TXTFAINT && txtpendingattrs & TXTBOLDFACE) { + --turncount; + change &= ~TXTFAINT; + } + + if (keepcount < turncount || + (change & ~txtpendingattrs & TXT_ATTR_FONT_WEIGHT)) { tsetcap(TCALLATTRSOFF, flags); /* this cleared all attributes, may need to restore some */ change = txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs; @@ -1607,13 +1615,19 @@ applytextattributes(int flags) /* in some cases, that clears all attributes */ change = txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs; } + if (change & ~txtpendingattrs & TXTITALIC) + tsetcap(TCITALICSEND, flags); } if (change & txtpendingattrs & TXTBOLDFACE) tsetcap(TCBOLDFACEBEG, flags); + if (change & txtpendingattrs & TXTFAINT) + tsetcap(TCFAINTBEG, flags); if (change & txtpendingattrs & TXTSTANDOUT) tsetcap(TCSTANDOUTBEG, flags); if (change & txtpendingattrs & TXTUNDERLINE) tsetcap(TCUNDERLINEBEG, flags); + if (change & txtpendingattrs & TXTITALIC) + tsetcap(TCITALICSBEG, flags); if (change & TXT_ATTR_FG_MASK) set_colour_attribute(txtpendingattrs, COL_SEQ_FG, flags); @@ -1678,6 +1692,25 @@ tunsetattrs(zattr newattrs) txtpendingattrs &= ~TXT_ATTR_BG_MASK; } +/* Merge two attribute sets. In an case where attributes might conflict + * choose those from the first parameter. Foreground and background + * colours are taken together - less likely to end up with unreadable + * combinations. */ + +/**/ +mod_export zattr +mixattrs(zattr primary, zattr secondary) +{ + zattr result = secondary; + /* take colours from primary */ + if (primary & (TXTFGCOLOUR|TXTBGCOLOUR)) + result &= ~TXT_ATTR_COLOUR_MASK; + /* take font weight from primary */ + if (primary & TXT_ATTR_FONT_WEIGHT) + result &= ~TXT_ATTR_FONT_WEIGHT; + return result | primary; +} + /***************************************************************************** * Utilities dealing with colour and other forms of highlighting. * @@ -1700,9 +1733,11 @@ struct highlight { static const struct highlight highlights[] = { { "none", 0, TXT_ATTR_ALL }, - { "bold", TXTBOLDFACE, 0 }, + { "bold", TXTBOLDFACE, TXTFAINT }, + { "faint", TXTFAINT, TXTBOLDFACE }, { "standout", TXTSTANDOUT, 0 }, { "underline", TXTUNDERLINE, 0 }, + { "italic", TXTITALIC, 0 }, { NULL, 0, 0 } }; diff --git a/Src/zsh.h b/Src/zsh.h index 35ae033e3..e834c7e06 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -2646,22 +2646,25 @@ struct ttyinfo { #define TCDELLINE 16 #define TCNEXTTAB 17 #define TCBOLDFACEBEG 18 -#define TCSTANDOUTBEG 19 -#define TCUNDERLINEBEG 20 -#define TCALLATTRSOFF 21 -#define TCSTANDOUTEND 22 -#define TCUNDERLINEEND 23 -#define TCHORIZPOS 24 -#define TCUPCURSOR 25 -#define TCDOWNCURSOR 26 -#define TCLEFTCURSOR 27 -#define TCRIGHTCURSOR 28 -#define TCSAVECURSOR 29 -#define TCRESTRCURSOR 30 -#define TCBACKSPACE 31 -#define TCFGCOLOUR 32 -#define TCBGCOLOUR 33 -#define TC_COUNT 34 +#define TCFAINTBEG 19 +#define TCSTANDOUTBEG 20 +#define TCUNDERLINEBEG 21 +#define TCITALICSBEG 22 +#define TCALLATTRSOFF 23 +#define TCSTANDOUTEND 24 +#define TCUNDERLINEEND 25 +#define TCITALICSEND 27 +#define TCHORIZPOS 27 +#define TCUPCURSOR 28 +#define TCDOWNCURSOR 29 +#define TCLEFTCURSOR 30 +#define TCRIGHTCURSOR 31 +#define TCSAVECURSOR 32 +#define TCRESTRCURSOR 33 +#define TCBACKSPACE 34 +#define TCFGCOLOUR 35 +#define TCBGCOLOUR 36 +#define TC_COUNT 37 #define tccan(X) (tclen[X]) @@ -2676,12 +2679,14 @@ struct ttyinfo { #endif #define TXTBOLDFACE 0x0001 -#define TXTSTANDOUT 0x0002 -#define TXTUNDERLINE 0x0004 -#define TXTFGCOLOUR 0x0008 -#define TXTBGCOLOUR 0x0010 +#define TXTFAINT 0x0002 +#define TXTSTANDOUT 0x0004 +#define TXTUNDERLINE 0x0008 +#define TXTITALIC 0x0010 +#define TXTFGCOLOUR 0x0020 +#define TXTBGCOLOUR 0x0040 -#define TXT_ATTR_ALL 0x001F +#define TXT_ATTR_ALL 0x007F /* * Indicates to zle_refresh.c that the character entry is an @@ -2690,7 +2695,10 @@ struct ttyinfo { #define TXT_MULTIWORD_MASK 0x0400 /* used when, e.g an invalid colour is specified */ -#define TXT_ERROR 0xF00000F000000800 +#define TXT_ERROR 0xF00000F000000003 + +/* Mask for font weight */ +#define TXT_ATTR_FONT_WEIGHT (TXTBOLDFACE|TXTFAINT) /* Mask for colour to use in foreground */ #define TXT_ATTR_FG_COL_MASK 0x000000FFFFFF0000 -- cgit v1.2.3 From 9d99a01367e85dc3d9cd8e711d78dc1cec6f018b Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Tue, 10 Jan 2023 21:02:16 +0100 Subject: 51281: keep track of attributes left on at the end of left and right prompts and reapply them explicitly as appropriate --- ChangeLog | 4 ++++ Src/Zle/zle_main.c | 7 ++++++- Src/Zle/zle_refresh.c | 41 ++++++++++++++++------------------------- Src/zsh.h | 3 ++- 4 files changed, 28 insertions(+), 27 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 9e2938bf8..9aac1d59b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-01-10 Oliver Kiddle + * 51281: Src/Zle/zle_main.c, Src/Zle/zle_refresh.c, Src/zsh.h: + keep track of attributes left on at the end of left and right + prompts and reapply them explicitly as appropriate + * 51280: Doc/Zsh/zle.yo, Src/Zle/zle_refresh.c, Src/init.c, Src/prompt.c, Src/zsh.h: add support for italic and faint fonts in the line editor diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 39be33939..686c6f5b4 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -1267,11 +1267,13 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) fetchttyinfo = 0; trashedzle = 0; raw_lp = lp; + txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0; lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL); pmpt_attr = txtcurrentattrs; raw_rp = rp; rpromptbuf = promptexpand(rp ? *rp : NULL, 1, NULL, NULL); rpmpt_attr = txtcurrentattrs; + prompt_attr = mixattrs(pmpt_attr, rpmpt_attr); free_prepostdisplay(); zlereadflags = flags; @@ -2010,6 +2012,7 @@ reexpandprompt(void) char *new_lprompt, *new_rprompt; looping = reexpanding; + txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0; new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL); pmpt_attr = txtcurrentattrs; free(lpromptbuf); @@ -2018,9 +2021,9 @@ reexpandprompt(void) if (looping != reexpanding) continue; - rpmpt_attr = pmpt_attr; new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL); rpmpt_attr = txtcurrentattrs; + prompt_attr = mixattrs(pmpt_attr, rpmpt_attr); free(rpromptbuf); rpromptbuf = new_rprompt; } while (looping != reexpanding); @@ -2067,6 +2070,8 @@ trashzle(void) zrefresh(); showinglist = sl; moveto(nlnct, 0); + treplaceattrs(prompt_attr); + applytextattributes(0); if (clearflag && tccan(TCCLEAREOD)) { tcout(TCCLEAREOD); clearflag = listshown = 0; diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index d40400886..ab84a1376 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -149,7 +149,7 @@ char *lpromptbuf, *rpromptbuf; /* Text attributes after displaying prompts */ /**/ -zattr pmpt_attr, rpmpt_attr; +zattr pmpt_attr, rpmpt_attr, prompt_attr; /* number of lines displayed */ @@ -254,13 +254,13 @@ int cost; # define zwrite(a, b) zwcwrite((a), (b)) #endif -static const REFRESH_ELEMENT zr_cr = { ZWC('\r'), 0 }; +static const REFRESH_ELEMENT zr_cr = { ZWC('\r'), TXT_ERROR }; #ifdef MULTIBYTE_SUPPORT -static const REFRESH_ELEMENT zr_dt = { ZWC('.'), 0 }; +static const REFRESH_ELEMENT zr_dt = { ZWC('.'), TXT_ERROR }; #endif -static const REFRESH_ELEMENT zr_nl = { ZWC('\n'), 0 }; -static const REFRESH_ELEMENT zr_sp = { ZWC(' '), 0 }; -static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), 0 }; +static const REFRESH_ELEMENT zr_nl = { ZWC('\n'), TXT_ERROR }; +static const REFRESH_ELEMENT zr_sp = { ZWC(' '), TXT_ERROR }; +static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), TXT_ERROR }; /* * Constant arrays to be copied into place: these are memcpy'd, @@ -599,8 +599,10 @@ zwcputc(const REFRESH_ELEMENT *c) VARARR(char, mbtmp, MB_CUR_MAX + 1); #endif - treplaceattrs(c->atr); - applytextattributes(0); + if (c->atr != TXT_ERROR) { + treplaceattrs(c->atr); + applytextattributes(0); + } #ifdef MULTIBYTE_SUPPORT if (c->atr & TXT_MULTIWORD_MASK) { @@ -1131,9 +1133,8 @@ zrefresh(void) zputs(lpromptbuf, shout); if (lpromptwof == winw) zputs("\n", shout); /* works with both hasam and !hasam */ - } else { - treplaceattrs(pmpt_attr); - applytextattributes(0); + /* lpromptbuf includes literal escapes so we need to update for it */ + txtcurrentattrs = txtpendingattrs = pmpt_attr; } if (clearflag) { zputc(&zr_cr); @@ -1177,7 +1178,7 @@ zrefresh(void) rpms.sen = *nbuf + winw; for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) { unsigned ireg; - zattr base_attr = default_attr; + zattr base_attr = mixattrs(default_attr, prompt_attr); zattr all_attr; struct region_highlight *rhp; /* @@ -1265,10 +1266,6 @@ zrefresh(void) rpms.s++; } else { /* We can fit it without reaching the end of the line. */ - /* - * As we don't actually output the WEOF, we attach - * any off attributes to the character itself. - */ rpms.s->atr = base_attr; if (ichars > 1) { /* @@ -1606,8 +1603,9 @@ zrefresh(void) /* output the right-prompt if appropriate */ if (put_rpmpt && !iln && !oput_rpmpt) { - moveto(0, winw - rprompt_off - rpromptw); + treplaceattrs(pmpt_attr); + applytextattributes(0); zputs(rpromptbuf, shout); if (rprompt_off) { vcs = winw - rprompt_off; @@ -1615,9 +1613,7 @@ zrefresh(void) zputc(&zr_cr); vcs = 0; } - /* reset character attributes to that set by the main prompt */ - treplaceattrs(pmpt_attr); - applytextattributes(0); + txtcurrentattrs = txtpendingattrs = rpmpt_attr; } } @@ -1630,11 +1626,6 @@ individually */ refreshline(iln); } -/* reset character attributes */ - if (clearf && postedit) { - treplaceattrs(pmpt_attr ? pmpt_attr : rpmpt_attr); - applytextattributes(0); - } clearf = 0; oput_rpmpt = put_rpmpt; diff --git a/Src/zsh.h b/Src/zsh.h index e834c7e06..073f8b71b 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -2694,7 +2694,8 @@ struct ttyinfo { */ #define TXT_MULTIWORD_MASK 0x0400 -/* used when, e.g an invalid colour is specified */ +/* Used when, e.g an invalid colour is specified. Also used in REFRESH_ELEMENT + * to indicate that attributes should remain unchanged. */ #define TXT_ERROR 0xF00000F000000003 /* Mask for font weight */ -- cgit v1.2.3 From cc672f1c3bfa9cdf4bbf100d85439340a479cb6b Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Tue, 10 Jan 2023 21:06:55 +0100 Subject: 51289: don't disable non-colour attributes in prompts for SINGLE_LINE_ZLE and remove superfluous extra escapes to disable attributes --- ChangeLog | 4 ++++ Src/Zle/zle_refresh.c | 2 -- Src/prompt.c | 3 +-- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 9aac1d59b..c3bc7b6e6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-01-10 Oliver Kiddle + * 51289: Src/Zle/zle_refresh.c, Src/prompt.c: don't disable + non-colour attributes in prompts for SINGLE_LINE_ZLE and remove + superfluous extra escapes to disable attributes + * 51281: Src/Zle/zle_main.c, Src/Zle/zle_refresh.c, Src/zsh.h: keep track of attributes left on at the end of left and right prompts and reapply them explicitly as appropriate diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index ab84a1376..8949a851c 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -1107,8 +1107,6 @@ zrefresh(void) #endif /* we probably should only have explicitly set attributes */ tsetcap(TCALLATTRSOFF, 0); - tsetcap(TCSTANDOUTEND, 0); - tsetcap(TCUNDERLINEEND, 0); txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0; if (trashedzle && !clearflag) diff --git a/Src/prompt.c b/Src/prompt.c index 488a90d09..4f29a6728 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -1004,8 +1004,7 @@ stradd(char *d) mod_export void tsetcap(int cap, int flags) { - if (tccan(cap) && !isset(SINGLELINEZLE) && - !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) { + if (tccan(cap) && !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) { switch (flags) { case TSC_RAW: tputs(tcstr[cap], 1, putraw); -- cgit v1.2.3 From 498b771a821c9ca7f77b9f64789dfdfb79fd0631 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Tue, 10 Jan 2023 21:09:54 +0100 Subject: 51290: fix display of control characters with SINGLE_LINE_ZLE set --- ChangeLog | 3 +++ Src/Zle/zle_refresh.c | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index c3bc7b6e6..1ed133a71 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-01-10 Oliver Kiddle + * 51290: Src/Zle/zle_refresh.c: fix display of control + characters with SINGLE_LINE_ZLE set + * 51289: Src/Zle/zle_refresh.c, Src/prompt.c: don't disable non-colour attributes in prompts for SINGLE_LINE_ZLE and remove superfluous extra escapes to disable attributes diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index 8949a851c..b196370dc 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -2419,6 +2419,8 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) } all_attr = mixattrs(special_attr, base_attr); + if (t0 == tmpcs) + nvcs = vp - vbuf; if (tmpline[t0] == ZWC('\t')) { for (*vp++ = zr_sp; (vp - vbuf) & 7; ) *vp++ = zr_sp; @@ -2461,7 +2463,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) && (unsigned)tmpline[t0] <= 0xffU #endif ) { - ZLE_INT_T t = tmpline[++t0]; + ZLE_INT_T t = tmpline[t0]; vp->chr = ZWC('^'); vp->atr = all_attr; @@ -2498,8 +2500,6 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) vp++; } #endif - if (t0 == tmpcs) - nvcs = vp - vbuf - 1; } if (t0 == tmpcs) nvcs = vp - vbuf; -- cgit v1.2.3 From be2c91bbc3361398a18ea4b77d493dded0a60e79 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Tue, 10 Jan 2023 21:13:52 +0100 Subject: 51291: support for highlighting ellipses in the line editor --- ChangeLog | 3 +++ Doc/Zsh/zle.yo | 3 +++ Src/Zle/zle_refresh.c | 60 ++++++++++++++++++++++++++++++--------------------- Src/prompt.c | 3 +++ 4 files changed, 44 insertions(+), 25 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 1ed133a71..e1d81845b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-01-10 Oliver Kiddle + * 51291: Doc/Zsh/zle.yo, Src/Zle/zle_refresh.c, Src/prompt.c: + support for highlighting ellipses in the line editor + * 51290: Src/Zle/zle_refresh.c: fix display of control characters with SINGLE_LINE_ZLE set diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo index 60254e828..c622483d7 100644 --- a/Doc/Zsh/zle.yo +++ b/Doc/Zsh/zle.yo @@ -2691,6 +2691,9 @@ for different completions. item(tt(paste))( Following a command to paste text, the characters that were inserted. ) +item(tt(ellipsis))( +Markers used to indicate where the text doesn't fit within the terminal. +) enditem() When tt(region_highlight) is set, the contexts that describe a region DASH()- diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index b196370dc..a587f696a 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -203,12 +203,12 @@ int predisplaylen, postdisplaylen; /* - * Attributes used by default on the command line, and - * attributes for highlighting special (unprintable) characters - * displayed on screen. + * Attributes used by default on the command line, + * for highlighting special (unprintable) characters displayed on screen, + * and for ellipsis continuation markers. */ -static zattr default_attr, special_attr; +static zattr default_attr, special_attr, ellipsis_attr; /* * Array of region highlights, no special termination. @@ -259,8 +259,8 @@ static const REFRESH_ELEMENT zr_cr = { ZWC('\r'), TXT_ERROR }; static const REFRESH_ELEMENT zr_dt = { ZWC('.'), TXT_ERROR }; #endif static const REFRESH_ELEMENT zr_nl = { ZWC('\n'), TXT_ERROR }; -static const REFRESH_ELEMENT zr_sp = { ZWC(' '), TXT_ERROR }; -static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), TXT_ERROR }; +static const REFRESH_ELEMENT zr_sp = { ZWC(' '), 0 }; +static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), 0 }; /* * Constant arrays to be copied into place: these are memcpy'd, @@ -269,10 +269,10 @@ static const REFRESH_ELEMENT zr_zr = { ZWC('\0'), TXT_ERROR }; static const REFRESH_ELEMENT zr_end_ellipsis[] = { { ZWC(' '), 0 }, { ZWC('<'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, { ZWC(' '), 0 }, }; #define ZR_END_ELLIPSIS_SIZE \ @@ -281,16 +281,16 @@ static const REFRESH_ELEMENT zr_end_ellipsis[] = { static const REFRESH_ELEMENT zr_mid_ellipsis1[] = { { ZWC(' '), 0 }, { ZWC('<'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, }; #define ZR_MID_ELLIPSIS1_SIZE \ ((int)(sizeof(zr_mid_ellipsis1)/sizeof(zr_mid_ellipsis1[0]))) static const REFRESH_ELEMENT zr_mid_ellipsis2[] = { - { ZWC('>'), 0 }, + { ZWC('>'), TXT_ERROR }, { ZWC(' '), 0 }, }; #define ZR_MID_ELLIPSIS2_SIZE \ @@ -298,10 +298,10 @@ static const REFRESH_ELEMENT zr_mid_ellipsis2[] = { static const REFRESH_ELEMENT zr_start_ellipsis[] = { { ZWC('>'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, - { ZWC('.'), 0 }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, + { ZWC('.'), TXT_ERROR }, }; #define ZR_START_ELLIPSIS_SIZE \ ((int)(sizeof(zr_start_ellipsis)/sizeof(zr_start_ellipsis[0]))) @@ -321,6 +321,7 @@ zle_set_highlight(void) int isearch_attr_set = 0; int suffix_attr_set = 0; int paste_attr_set = 0; + int ellipsis_attr_set = 0; struct region_highlight *rhp; special_attr = default_attr = 0; @@ -361,6 +362,9 @@ zle_set_highlight(void) } else if (strpfx("paste:", *atrs)) { match_highlight(*atrs + 6, &(region_highlights[3].atr)); paste_attr_set = 1; + } else if (strpfx("ellipsis:", *atrs)) { + match_highlight(*atrs + 9, &ellipsis_attr); + ellipsis_attr_set = 1; } } } @@ -376,6 +380,9 @@ zle_set_highlight(void) region_highlights[2].atr = TXTBOLDFACE; if (!paste_attr_set) region_highlights[3].atr = TXTSTANDOUT; + if (!ellipsis_attr_set) + ellipsis_attr = TXTBGCOLOUR | ((zattr) 3 << TXT_ATTR_BG_COL_SHIFT) | + TXTFGCOLOUR | ((zattr) 4 << TXT_ATTR_FG_COL_SHIFT); allocate_colour_buffer(); } @@ -599,10 +606,8 @@ zwcputc(const REFRESH_ELEMENT *c) VARARR(char, mbtmp, MB_CUR_MAX + 1); #endif - if (c->atr != TXT_ERROR) { - treplaceattrs(c->atr); - applytextattributes(0); - } + treplaceattrs(c->atr); + applytextattributes(0); #ifdef MULTIBYTE_SUPPORT if (c->atr & TXT_MULTIWORD_MASK) { @@ -1479,6 +1484,7 @@ zrefresh(void) } #endif ZR_memcpy(rpms.sen, zr_end_ellipsis, ZR_END_ELLIPSIS_SIZE); + rpms.sen[1].atr = ellipsis_attr; #ifdef MULTIBYTE_SUPPORT /* Extend to the end if we backed off for a wide character */ if (extra_ellipsis) { @@ -1514,6 +1520,7 @@ zrefresh(void) } #endif ZR_memcpy(rpms.sen, zr_mid_ellipsis1, ZR_MID_ELLIPSIS1_SIZE); + rpms.sen[1].atr = ellipsis_attr; rpms.sen += ZR_MID_ELLIPSIS1_SIZE; #ifdef MULTIBYTE_SUPPORT /* Extend if we backed off for a wide character */ @@ -1523,6 +1530,7 @@ zrefresh(void) } #endif ZR_memcpy(rpms.sen, zr_mid_ellipsis2, ZR_MID_ELLIPSIS2_SIZE); + rpms.sen[1].atr = prompt_attr; nbuf[rpms.tosln][winw] = nbuf[rpms.tosln][winw + 1] = zr_zr; } @@ -1555,7 +1563,9 @@ zrefresh(void) t0 = winw - lpromptw; t0 = t0 > ZR_START_ELLIPSIS_SIZE ? ZR_START_ELLIPSIS_SIZE : t0; ZR_memcpy(nbuf[0] + lpromptw, zr_start_ellipsis, t0); + (*nbuf + lpromptw)->atr = ellipsis_attr; ZR_memset(nbuf[0] + lpromptw + t0, zr_sp, winw - t0 - lpromptw); + (*nbuf + lpromptw + t0)->atr = prompt_attr; nbuf[0][winw] = nbuf[0][winw + 1] = zr_zr; } @@ -2514,11 +2524,11 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) } if (winpos) { vbuf[winpos].chr = ZWC('<'); /* line continues to the left */ - vbuf[winpos].atr = 0; + vbuf[winpos].atr = ellipsis_attr; } if ((int)ZR_strlen(vbuf + winpos) > (winw - hasam)) { vbuf[winpos + winw - hasam - 1].chr = ZWC('>'); /* line continues to right */ - vbuf[winpos + winw - hasam - 1].atr = 0; + vbuf[winpos + winw - hasam - 1].atr = ellipsis_attr; vbuf[winpos + winw - hasam] = zr_zr; } ZR_strcpy(nbuf[0], vbuf + winpos); diff --git a/Src/prompt.c b/Src/prompt.c index 4f29a6728..39fcf5eb7 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -1648,6 +1648,9 @@ cleartextattributes(int flags) mod_export void treplaceattrs(zattr newattrs) { + if (newattrs == TXT_ERROR) + return; + if (txtunknownattrs) { /* Set current attributes to the opposite of the new ones * for any that are unknown so that applytextattributes() -- cgit v1.2.3 From b513ca21c8aff35c76bb9b1cb30a9644e936833a Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Tue, 10 Jan 2023 21:17:24 +0100 Subject: 51292: fix dynamic updates of region_highlight to account for PREDISPLAY --- ChangeLog | 3 +++ Src/Zle/zle_utils.c | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index e1d81845b..ddee8700e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-01-10 Oliver Kiddle + * 51292: Src/Zle/zle_utils.c: fix dynamic updates of + region_highlight to account for PREDISPLAY + * 51291: Doc/Zsh/zle.yo, Src/Zle/zle_refresh.c, Src/prompt.c: support for highlighting ellipses in the line editor diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 1a580a9e6..45a82dd5e 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -866,13 +866,13 @@ shiftchars(int to, int cnt) if (rhp->start_meta - sub > to + cnt) rhp->start_meta -= cnt; else - rhp->start_meta = to; + rhp->start_meta = to + sub; } if (rhp->end_meta - sub > to) { if (rhp->end_meta - sub > to + cnt) rhp->end_meta -= cnt; else - rhp->end_meta = to; + rhp->end_meta = to + sub; } } } @@ -896,13 +896,13 @@ shiftchars(int to, int cnt) if (rhp->start - sub > to + cnt) rhp->start -= cnt; else - rhp->start = to; + rhp->start = to + sub; } if (rhp->end - sub > to) { if (rhp->end - sub > to + cnt) rhp->end -= cnt; else - rhp->end = to; + rhp->end = to + sub; } } } -- cgit v1.2.3 From 246b7c75055654dcc6186db1c4d52724dc522aa2 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 11 Jan 2023 15:58:45 +0100 Subject: 51295: where the end of a region coincides with the end of PREDISPLAY, don't extend it to include new text Also fix issue where an int was used for a copy of attributes which breaks if int is smaller than zattr. --- ChangeLog | 5 +++++ Src/Zle/zle_utils.c | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index ddee8700e..628fdcc63 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-01-11 Oliver Kiddle + + * 51295: Src/Zle/zle_utils.c: where the end of a region coincides + with the end of PREDISPLAY, don't extend it to include new text + 2023-01-10 Oliver Kiddle * 51292: Src/Zle/zle_utils.c: fix dynamic updates of diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 45a82dd5e..9ce91049c 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -580,7 +580,7 @@ struct zle_region; struct zle_region { struct zle_region *next; /* Entries of region_highlight, as needed */ - int atr; + zattr atr; int start; int end; int flags; @@ -799,7 +799,7 @@ spaceinline(int ct) if (rhp->start_meta - sub >= zlemetacs) { rhp->start_meta += ct; } - if (rhp->end_meta - sub >= zlemetacs) { + if (rhp->end_meta - sub >= zlemetacs && (!predisplaylen || zlecs)) { rhp->end_meta += ct; } } @@ -827,7 +827,7 @@ spaceinline(int ct) if (rhp->start - sub >= zlecs) { rhp->start += ct; } - if (rhp->end - sub >= zlecs) { + if (rhp->end - sub >= zlecs && (!predisplaylen || zlecs)) { rhp->end += ct; } } -- cgit v1.2.3 From e2a39513dcff152fdfe1d966c0eb3156cce260b6 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 11 Jan 2023 16:05:06 +0100 Subject: unposted: mention attributes changes, fix renumbering mistake and allow completion to offer prompt escapes for PROMPT_EOL_MARK --- ChangeLog | 4 ++++ Completion/Zsh/Type/_ps1234 | 2 +- NEWS | 6 ++++++ Src/zsh.h | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 628fdcc63..7213bf5f0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-01-11 Oliver Kiddle + * unposted: NEWS, Src/zsh.h, Completion/Zsh/Type/_ps1234: mention + attributes changes, fix renumbering mistake and allow completion + to offer prompt escapes for PROMPT_EOL_MARK + * 51295: Src/Zle/zle_utils.c: where the end of a region coincides with the end of PREDISPLAY, don't extend it to include new text diff --git a/Completion/Zsh/Type/_ps1234 b/Completion/Zsh/Type/_ps1234 index 0ea2cdda9..07dea5905 100644 --- a/Completion/Zsh/Type/_ps1234 +++ b/Completion/Zsh/Type/_ps1234 @@ -1,4 +1,4 @@ -#compdef -value-,PROMPT,-default- -value-,PROMPT2,-default- -value-,PROMPT3,-default- -value-,PROMPT4,-default- -value-,RPROMPT,-default- -value-,RPROMPT2,-default- -value-,PS1,-default- -value-,PS2,-default- -value-,PS3,-default- -value-,PS4,-default- -value-,RPS1,-default- -value-,RPS2,-default- -value-,SPROMPT,-default- +#compdef -value-,PROMPT,-default- -value-,PROMPT2,-default- -value-,PROMPT3,-default- -value-,PROMPT4,-default- -value-,RPROMPT,-default- -value-,RPROMPT2,-default- -value-,PS1,-default- -value-,PS2,-default- -value-,PS3,-default- -value-,PS4,-default- -value-,RPS1,-default- -value-,RPS2,-default- -value-,SPROMPT,-default- -value-,PROMPT_EOL_MARK,-default- local -a specs ccol local expl grp cols bs suf pre changed=1 ret=1 diff --git a/NEWS b/NEWS index e3a16a8bc..0e726699f 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,12 @@ Note also the list of incompatibilities in the README file. Changes since 5.9 ----------------- +In region_highlight and zle_highlight, italic and faint can be +specified as font attributes for terminals that support them. + +Ellipsis markers shown by the line editor to indicate where the line +doesn't fit in the terminal can be highlighted. + The ERR_EXIT and ERR_RETURN options were refined to be more self- consistent and better aligned with the POSIX-2017 specification of `set -e`. For details on what exactly changed, see the list of diff --git a/Src/zsh.h b/Src/zsh.h index 073f8b71b..f82e76e4b 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -2653,7 +2653,7 @@ struct ttyinfo { #define TCALLATTRSOFF 23 #define TCSTANDOUTEND 24 #define TCUNDERLINEEND 25 -#define TCITALICSEND 27 +#define TCITALICSEND 26 #define TCHORIZPOS 27 #define TCUPCURSOR 28 #define TCDOWNCURSOR 29 -- cgit v1.2.3 From 03292bceecba1ca39745f82fe37f321d08b138e4 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 16 Jan 2023 11:10:02 +0000 Subject: 51278: make (i) subscript flag for zero-length string consistent --- ChangeLog | 6 ++++++ Src/params.c | 2 +- Test/D06subscript.ztst | 9 +++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 23baf278b..69ea1ab53 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2023-01-16 Peter Stephenson + + * 51278: Src/params.c, Test/D06subscript.ztst: result of (i) + subscript flag with zero-length string was inconsistent with + other cases. + 2023-01-11 Oliver Kiddle * 51297: Test/X04zlehighlight.ztst: update expected test results diff --git a/Src/params.c b/Src/params.c index 2e4a6eae2..6362b382c 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1669,7 +1669,7 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, /* Searching characters */ int slen; d = getstrvalue(v); - if (!d || !*d) + if (!d) return 0; /* * beg and len are character counts, not raw offsets. diff --git a/Test/D06subscript.ztst b/Test/D06subscript.ztst index 21127e641..57cdc027c 100644 --- a/Test/D06subscript.ztst +++ b/Test/D06subscript.ztst @@ -299,3 +299,12 @@ F:In math, (($i)) should be the same as ((i)), see workers/47748. echo ${string[(pws:\0:)1]} 0:Word splitting by NUL >foo + + string="a" + print ${string[(i)x]} + string="" + print ${string[(i)x]} +0:Can check off end of zero length string +F:Regression test for inconsistency of failed (i) on zero-length string +>2 +>1 -- cgit v1.2.3 From f93ad02b94bd18c96a0861506127e3a246fb8eec Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 21 Jan 2023 19:18:15 -0800 Subject: 51310: zle -F handlers preserve LASTWIDGET Also fix email address in an old ChangeLog entry --- ChangeLog | 6 +++++- Src/Zle/zle_main.c | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 69ea1ab53..2143d9fcb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-01-21 Bart Schaefer + + * 51310: Src/Zle/zle_main.c: zle -F handlers preserve LASTWIDGET + 2023-01-16 Peter Stephenson * 51278: Src/params.c, Test/D06subscript.ztst: result of (i) @@ -627,7 +631,7 @@ * 49960: Doc/Zsh/compsys.yo: Sort lists in zshcompsys. -2022-04-05 Bart Schaefer +2022-04-05 Bart Schaefer * 49994: Src/glob.c: Single-byte equivalence of users/22601 and workers/40891 for matching against zero-length strings diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 686c6f5b4..cfa0a739d 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -737,6 +737,7 @@ raw_getbyte(long do_keytmout, char *cptr, int full) ) { /* Handle the fd. */ char *fdbuf; + Thingy save_lbindk = lbindk; { char buf[BDIGBUFSIZE]; convbase(buf, lwatch_fd->fd, 10); @@ -779,6 +780,7 @@ raw_getbyte(long do_keytmout, char *cptr, int full) */ errtry = 1; } + lbindk = save_lbindk; } } /* Function may have invalidated the display. */ -- cgit v1.2.3 From 21baad1037c1aa85384a81dd77a4661676336133 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 2 Feb 2023 10:09:21 +0000 Subject: 51307: Improve error on attempt to define function from aliased name --- ChangeLog | 6 ++++++ Src/input.c | 5 +++-- Src/parse.c | 3 +++ Test/A02alias.ztst | 7 ++++++- 4 files changed, 18 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 05c7d86c7..ee9a623d4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2023-02-02 Peter Stephenson + + * 51307: Src/input.c, Src/parse.c, Test/A02alias.ztst: error + on attempt to expand alias in function definition name didn't + find the original alias and printed an extra error. + 2023-01-31 Bart Schaefer * 51342: Etc/zsh-development-guide: update PARAMDEF description diff --git a/Src/input.c b/Src/input.c index 5a612669b..0da065e51 100644 --- a/Src/input.c +++ b/Src/input.c @@ -816,6 +816,7 @@ char *input_hasalias(void) { int flags = inbufflags; struct instacks *instackptr = instacktop; + char *alias_nam = NULL; for (;;) { @@ -824,9 +825,9 @@ char *input_hasalias(void) DPUTS(instackptr == instack, "BUG: continuation at bottom of instack"); instackptr--; if (instackptr->alias) - return instackptr->alias->node.nam; + alias_nam = instackptr->alias->node.nam; flags = instackptr->flags; } - return NULL; + return alias_nam; } diff --git a/Src/parse.c b/Src/parse.c index 283225b74..a07a6cc71 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -2055,6 +2055,9 @@ par_simple(int *cmplx, int nr) if (isset(EXECOPT) && hasalias && !isset(ALIASFUNCDEF) && argc && hasalias != input_hasalias()) { zwarn("defining function based on alias `%s'", hasalias); + herrflush(); + if (noerrs != 2) + errflag |= ERRFLAG_ERROR; YYERROR(oecused); } diff --git a/Test/A02alias.ztst b/Test/A02alias.ztst index ca415fa39..1c6969e74 100644 --- a/Test/A02alias.ztst +++ b/Test/A02alias.ztst @@ -123,7 +123,12 @@ eval 'badalias() { print does not work; }') 1:ALIAS_FUNC_DEF off by default. ?(eval):1: defining function based on alias `badalias' -?(eval):1: parse error near `()' + + (alias firstalias=notacommand + alias secondalias=firstalias + eval 'secondalias() { print does not work either; }') +1:ALIAS_FUNC_DEF reports original alias if multiple +?(eval):1: defining function based on alias `secondalias' (alias goodalias=isafunc setopt ALIAS_FUNC_DEF -- cgit v1.2.3 From 76d095df9de31d46b0ca042039855ffc286f5fdb Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 2 Feb 2023 10:12:17 +0000 Subject: 51306: error message in ${unset?error} should be expanded --- ChangeLog | 3 +++ Doc/Zsh/expn.yo | 4 +++- Src/subst.c | 6 +++++- Test/D04parameter.ztst | 5 +++++ 4 files changed, 16 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index ee9a623d4..6bdaeedbf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-02-02 Peter Stephenson + * 51306: Doc/Zsh/expn.yo, Src/subst.c, Test/D04parameter.ztst: + error message in ${unset?...} should be expanded. + * 51307: Src/input.c, Src/parse.c, Test/A02alias.ztst: error on attempt to expand alias in function definition name didn't find the original alias and printed an extra error. diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index ad55c24ba..fd5443b20 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -665,7 +665,9 @@ item(tt(${)var(name)tt(:?)var(word)tt(}))( In the first form, if var(name) is set, or in the second form if var(name) is both set and non-null, then substitute its value; otherwise, print var(word) and exit from the shell. Interactive shells instead return to -the prompt. If var(word) is omitted, then a standard message is printed. +the prompt. If var(word) is omitted, then a standard message is +printed. Note that var(word) is expanded even though its value +is not substituted onto the command line. ) enditem() diff --git a/Src/subst.c b/Src/subst.c index 897188862..4ad9fee1a 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -3076,7 +3076,11 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, if (vunset) { if (isset(EXECOPT)) { *idend = '\0'; - zerr("%s: %s", idbeg, *s ? s : "parameter not set"); + if (*s){ + singsub(&s); + zerr("%s: %s", idbeg, s); + } else + zerr("%s: %s", idbeg, "parameter not set"); /* * In interactive shell we need to return to * top-level prompt --- don't clear this error diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 29275f13f..a11652d1e 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -110,6 +110,11 @@ *>*foo:1: 1: no arguments given >reached + message="expand me and remove quotes" + (: ${UNSET_PARAM?$message}) +1:${...?....} performs expansion on the message +?(eval):2: UNSET_PARAM: expand me and remove quotes + print ${set1:+word1} ${set1+word2} ${null1:+word3} ${null1+word4} print ${unset1:+word5} ${unset1+word6} 0:${...:+...}, ${...+...} -- cgit v1.2.3 From d6b027c3c1203da5f3c6451bd6e2e0b81bd766b6 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 6 Feb 2023 14:16:17 +0000 Subject: 51350: ${(S)...//#%...} didn't match the whole string --- ChangeLog | 5 +++++ Src/subst.c | 3 +++ Test/D04parameter.ztst | 7 +++++++ 3 files changed, 15 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 1a66e94e9..2e63157de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-02-06 Peter Stephenson + + * 51350: Src/subst.c, Test/D04parameter.ztst: the combination + ${(S)...//#%...} needs to match as far as possible. + 2023-02-02 Daniel Shahaf * 51354: Doc/Zsh/params.yo: Fix markup in man page version diff --git a/Src/subst.c b/Src/subst.c index 4ad9fee1a..3dd920e87 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -2926,6 +2926,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, */ if (!(flags & (SUB_MATCH|SUB_REST|SUB_BIND|SUB_EIND|SUB_LEN))) flags |= SUB_REST; + /* If matching at start and end, don't stop early */ + if ((flags & (SUB_START|SUB_END)) == (SUB_START|SUB_END)) + flags |= SUB_LONG; /* * With ":" treat a value as unset if the variable is set but diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index a11652d1e..7990c2958 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -2307,6 +2307,13 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888 >x >y + a="string" + print ${(S)a//#%((#b)(*))/different} + print $match[1] +0:Fully anchored string must be fully searched +>different +>string + my_width=6 my_index=1 my_options=Option1 -- cgit v1.2.3 From 6502d05897a7d9441fcabfd24f203cffe4da59aa Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Thu, 9 Feb 2023 00:10:19 +0100 Subject: 51320, 51383: fixes to prevent later reappearance of old attributes Also associated test updates and a test fix for TERM=dumb. --- ChangeLog | 7 +++++++ Src/Zle/complist.c | 2 ++ Src/Zle/zle_main.c | 2 +- Src/Zle/zle_tricky.c | 2 ++ Test/D01prompt.ztst | 2 +- Test/X04zlehighlight.ztst | 2 +- 6 files changed, 14 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 2e63157de..bbe45a3f4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2023-02-09 Oliver Kiddle + + * 51320, 51383: Src/Zle/zle_main.c, Src/Zle/complist.c, + Src/Zle/zle_tricky.c, Test/D01prompt.ztst, + Test/X04zlehighlight.ztst: fixes to prevent later reappearance + of old attributes + 2023-02-06 Peter Stephenson * 51350: Src/subst.c, Test/D04parameter.ztst: the combination diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 8bdf1bb29..9cb89a60d 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -1305,6 +1305,8 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) } } if (dopr) { + treplaceattrs(0); + applytextattributes(0); if (!(cc % zterm_columns)) fputs(" \010", shout); cleareol(); diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index cfa0a739d..4a6c02133 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -2071,9 +2071,9 @@ trashzle(void) trashedzle = 1; zrefresh(); showinglist = sl; - moveto(nlnct, 0); treplaceattrs(prompt_attr); applytextattributes(0); + moveto(nlnct, 0); if (clearflag && tccan(TCCLEAREOD)) { tcout(TCCLEAREOD); clearflag = listshown = 0; diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index f94bfce3c..07fac7144 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -2560,6 +2560,8 @@ printfmt(char *fmt, int n, int dopr, int doesc) } } if (dopr) { + treplaceattrs(0); + applytextattributes(0); if (!(cc % zterm_columns)) fputs(" \010", shout); if (tccan(TCCLEAREOL)) diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst index a0abb7e1d..55861cca1 100644 --- a/Test/D01prompt.ztst +++ b/Test/D01prompt.ztst @@ -261,7 +261,7 @@ A1=${(%):-%s} A2=${(%):-%u} A3=${(%):-%s%u%s} - [[ $A3 = $A1$A2 && -n $A1 && -n $A2 ]] + [[ $A3 = $A1$A2 ]] 0:Attribute optimisation - preserve initial disabling of attribute but drop useless later one : ${(%):-%K{blue}} diff --git a/Test/X04zlehighlight.ztst b/Test/X04zlehighlight.ztst index 296635bf5..87a59fde5 100644 --- a/Test/X04zlehighlight.ztst +++ b/Test/X04zlehighlight.ztst @@ -40,7 +40,7 @@ # Fix e^Mexit - match ((?)\r(?)), if \2 == \3, then replace with \2 # otherwise replace with \1 stripped out of leading/trailing [[:space:]] REPLY=${REPLY//(#b)((?(#c0,1))$cm(?(#c0,1)))/${${${(M)match[2]:#${match[3]}}:+${match[2]}}:-${${match[1]##[[:space:]]##}%%[[:space:]]##}}} - [[ -n "$REPLY" ]] && print -r -- ${${REPLY%%[[:space:]]##}##[[:space:]]##} + [[ -n "$REPLY" ]] && print -r -- ${${REPLY%%${~cm}*}##[[:space:]]##} done } zpty_stop() { -- cgit v1.2.3 From d3edf318306e37d2d96c4e4ea442d10207722e94 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 12 Feb 2023 09:52:39 -0800 Subject: 51404: Nullify filelist after deleting (fix segfault) --- ChangeLog | 4 ++++ Src/jobs.c | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index bbe45a3f4..7f467a8ac 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-02-12 Bart Schaefer + + * 51404: Src/jobs.c: Nullify filelist after deleting (fix segfault) + 2023-02-09 Oliver Kiddle * 51320, 51383: Src/Zle/zle_main.c, Src/Zle/complist.c, diff --git a/Src/jobs.c b/Src/jobs.c index 4863962b9..59ddd952e 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -1372,8 +1372,10 @@ cleanfilelists(void) DPUTS(shell_exiting >= 0, "BUG: cleanfilelists() before exit"); - for (i = 1; i <= maxjob; i++) + for (i = 1; i <= maxjob; i++) { deletefilelist(jobtab[i].filelist, 0); + jobtab[i].filelist = 0; + } } /**/ @@ -1531,8 +1533,10 @@ havefiles(void) int i; for (i = 1; i <= maxjob; i++) - if (jobtab[i].stat && jobtab[i].filelist) + if (jobtab[i].stat && jobtab[i].filelist && + peekfirst(jobtab[i].filelist)) { return 1; + } return 0; } -- 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') diff --git a/ChangeLog b/ChangeLog index 7f467a8ac..aecb1efcf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-02-12 Bart Schaefer + * 51360: Src/Modules/param_private.c, Src/Modules/parameter.c, + Src/builtin.c, Src/params.c, Src/subst.c, Src/zsh.h: Initial + implementation of named references. + * 51404: Src/jobs.c: Nullify filelist after deleting (fix segfault) 2023-02-09 Oliver Kiddle diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index 065fa63d2..70f36ceb1 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -512,9 +512,16 @@ static GetNodeFunc getparamnode; static HashNode getprivatenode(HashTable ht, const char *nam) { - HashNode hn = getparamnode(ht, nam); + /* getparamnode() would follow namerefs, we must not do that here */ + HashNode hn = gethashnode2(ht, nam); Param pm = (Param) hn; + /* autoload has precedence over nameref, so getparamnode() */ + if (pm && (pm->node.flags & PM_AUTOLOAD)) { + hn = getparamnode(ht, nam); + pm = (Param) hn; + /* how would an autoloaded private behave? return here? */ + } while (!fakelevel && pm && locallevel > pm->level && is_private(pm)) { if (!(pm->node.flags & PM_UNSET)) { /* @@ -533,6 +540,12 @@ getprivatenode(HashTable ht, const char *nam) } pm = pm->old; } + + /* resolve nameref after skipping private parameters */ + if (pm && (pm->node.flags & PM_NAMEREF) && + (pm->u.str || (pm->node.flags & PM_UNSET))) + pm = (Param) resolve_nameref(pm, NULL); + return (HashNode)pm; } @@ -571,7 +584,7 @@ printprivatenode(HashNode hn, int printflags) static struct builtin bintab[] = { /* Copied from BUILTIN("local"), "P" added */ - BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lmprtux", "P") + BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lnmprtux", "P") }; static struct features module_features = { diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index dbb61e474..5bf675e2a 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -49,13 +49,15 @@ paramtypestr(Param pm) if (pm->node.flags & PM_AUTOLOAD) return dupstring("undefined"); - switch (PM_TYPE(f)) { + /* For simplicity we treat PM_NAMEREF as PM_TYPE(PM_SCALAR) */ + switch (PM_TYPE(f)|(f & PM_NAMEREF)) { case PM_SCALAR: val = "scalar"; break; case PM_ARRAY: val = "array"; break; case PM_INTEGER: val = "integer"; break; case PM_EFLOAT: case PM_FFLOAT: val = "float"; break; case PM_HASHED: val = "association"; break; + case PM_NAMEREF: val = "nameref"; break; } DPUTS(!val, "BUG: type not handled in parameter"); val = dupstring(val); diff --git a/Src/builtin.c b/Src/builtin.c index 4c295d11f..8039b644e 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -55,7 +55,7 @@ static struct builtin builtins[] = BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL), BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL), BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL), - BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmp:%rtuxz", NULL), + BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmnp:%rtuxz", NULL), BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL), BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL), BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL), @@ -88,7 +88,7 @@ static struct builtin builtins[] = BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL), BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL), BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL), - BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lp:%rtux", NULL), + BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lnp:%rtux", NULL), BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL), #if defined(ZSH_MEM) & defined(ZSH_MEM_DEBUG) @@ -121,7 +121,7 @@ static struct builtin builtins[] = BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL), BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL), BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsSw", "v"), - BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmz", NULL), + BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmnz", NULL), BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL), BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL), BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"), @@ -2030,6 +2030,19 @@ typeset_single(char *cname, char *pname, Param pm, int func, int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly, dont_set = 0; char *subscript; + if (pm && (pm->node.flags & PM_NAMEREF) && !((off|on) & PM_NAMEREF)) { + if (!(off & PM_NAMEREF)) + pm = (Param)resolve_nameref(pm, NULL); + if (pm && (pm->node.flags & PM_NAMEREF) && + (on & ~(PM_NAMEREF|PM_LOCAL))) { + /* Changing type of PM_SPECIAL|PM_AUTOLOAD is a fatal error. * + * Should this be a fatal error as well, rather than warning? */ + zwarnnam(cname, "%s: can't change type of a named reference", + pname); + return NULL; + } + } + /* * Do we use the existing pm? Note that this isn't the end of the * story, because if we try and create a new pm at the same @@ -2406,6 +2419,11 @@ typeset_single(char *cname, char *pname, Param pm, int func, return NULL; } } else if ((subscript = strchr(pname, '['))) { + if (on & PM_NAMEREF) { + zerrnam(cname, + "%s: reference variable cannot be an array", pname); + return NULL; + } if (on & PM_READONLY) { zerrnam(cname, "%s: can't create readonly array elements", pname); @@ -2640,6 +2658,14 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) else if (OPT_PLUS(ops,optval)) off |= bit; } + if (OPT_MINUS(ops,'n')) { + if (on|off) { + zwarnnam(name, "no other attributes allowed with -n"); + return 1; + } + on |= PM_NAMEREF; + } else if (OPT_PLUS(ops,'n')) + off |= PM_NAMEREF; roff = off; /* Sanity checks on the options. Remove conflicting options. */ @@ -3022,6 +3048,27 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) } continue; } + + if (on & PM_NAMEREF) { + if (asg->value.scalar && + (strcmp(asg->name, asg->value.scalar) == 0 || + ((pm = (Param)resolve_nameref((Param)hn, asg)) && + (pm->node.flags & PM_NAMEREF)))) { + if (pm->node.flags & PM_SPECIAL) + zwarnnam(name, "%s: invalid reference", pm->node.nam); + else + zwarnnam(name, "%s: invalid self reference", asg->name); + returnval = 1; + continue; + } + if (hn) { + /* namerefs always start over fresh */ + if (((Param)hn)->level >= locallevel) + unsetparam_pm((Param)hn, 0, 1); + hn = NULL; + } + } + if (!typeset_single(name, asg->name, (Param)hn, func, on, off, roff, asg, NULL, ops, 0)) @@ -3805,7 +3852,8 @@ bin_unset(char *name, char **argv, Options ops, int func) returnval = 1; } } else { - if (unsetparam_pm(pm, 0, 1)) + if ((pm = (Param)resolve_nameref(pm, NULL)) && + unsetparam_pm(pm, 0, 1)) returnval = 1; } if (ss) diff --git a/Src/params.c b/Src/params.c index 6362b382c..69b7f484f 100644 --- a/Src/params.c +++ b/Src/params.c @@ -536,6 +536,9 @@ getparamnode(HashTable ht, const char *nam) nam); } } + + if (hn && ht == realparamtab) + hn = resolve_nameref(pm, NULL); return hn; } @@ -993,6 +996,34 @@ createparam(char *name, int flags) gethashnode2(paramtab, name) : paramtab->getnode(paramtab, name)); + if (oldpm && (oldpm->node.flags & PM_NAMEREF) && + !(flags & PM_NAMEREF)) { + Param lastpm; + struct asgment stop; + stop.flags = PM_NAMEREF | (flags & PM_LOCAL); + stop.name = oldpm->node.nam; + stop.value.scalar = oldpm->u.str; + lastpm = (Param)resolve_nameref(oldpm, &stop); + if (lastpm) { + if (lastpm->node.flags & PM_NAMEREF) { + if (lastpm->u.str && *(lastpm->u.str)) { + name = lastpm->u.str; + oldpm = NULL; + } else { + if (!(lastpm->node.flags & PM_READONLY)) + lastpm->node.flags |= PM_UNSET; + return lastpm; + } + } else { + /* nameref pointing to an unset local */ + DPUTS(!(lastpm->node.flags & PM_UNSET), + "BUG: local parameter is not unset"); + oldpm = lastpm; + } + } else + flags |= PM_NAMEREF; + } + DPUTS(oldpm && oldpm->level > locallevel, "BUG: old local parameter not deleted"); if (oldpm && (oldpm->level == locallevel || !(flags & PM_LOCAL))) { @@ -2109,6 +2140,23 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) memset(v, 0, sizeof(*v)); else v = (Value) hcalloc(sizeof *v); + if ((pm->node.flags & PM_NAMEREF) && pm->u.str && *(pm->u.str)) { + /* only happens for namerefs pointing to array elements */ + char *ref = dupstring(pm->u.str); + char *ss = pm->width ? ref + pm->width : NULL; + if (ss) { + sav = *ss; + *ss = 0; + } + Param p1 = (Param)gethashnode2(paramtab, ref); + if (!(p1 && (pm = upscope(p1, pm->base))) || + ((pm->node.flags & PM_UNSET) && + !(pm->node.flags & PM_DECLARED))) + return NULL; + if (ss) + *ss = sav; + s = ss; + } if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) { /* Overload v->isarr as the flag bits for hashed arrays. */ v->isarr = flags | (isvarat ? SCANPM_ISVAR_AT : 0); @@ -2677,6 +2725,7 @@ assignstrvalue(Value v, char *val, int flags) } break; } + setscope(v->pm); if ((!v->pm->env && !(v->pm->node.flags & PM_EXPORTED) && !(isset(ALLEXPORT) && !(v->pm->node.flags & PM_HASHELEM))) || (v->pm->node.flags & PM_ARRAY) || v->pm->ename) @@ -3084,11 +3133,20 @@ assignsparam(char *s, char *val, int flags) } } if (!v && !(v = getvalue(&vbuf, &t, 1))) { - unqueue_signals(); zsfree(val); + unqueue_signals(); /* errflag |= ERRFLAG_ERROR; */ return NULL; } + if (*val && (v->pm->node.flags & PM_NAMEREF)) { + if (!valid_refname(val)) { + zerr("invalid variable name: %s", val); + zsfree(val); + unqueue_signals(); + errflag |= ERRFLAG_ERROR; + return NULL; + } + } if (flags & ASSPM_WARN) check_warn_pm(v->pm, "scalar", created, 1); v->pm->node.flags &= ~PM_DEFAULTED; @@ -3115,8 +3173,8 @@ assignsparam(char *s, char *val, int flags) lhs.u.l = lhs.u.l + (zlong)rhs.u.d; } setnumvalue(v, lhs); - unqueue_signals(); zsfree(val); + unqueue_signals(); return v->pm; /* avoid later setstrvalue() call */ case PM_ARRAY: if (unset(KSHARRAYS)) { @@ -3141,9 +3199,9 @@ assignsparam(char *s, char *val, int flags) case PM_INTEGER: case PM_EFLOAT: case PM_FFLOAT: + zsfree(val); unqueue_signals(); zerr("attempt to add to slice of a numeric variable"); - zsfree(val); return NULL; case PM_ARRAY: kshappend: @@ -3602,7 +3660,8 @@ unsetparam(char *s) if ((pm = (Param) (paramtab == realparamtab ? /* getnode2() to avoid autoloading */ paramtab->getnode2(paramtab, s) : - paramtab->getnode(paramtab, s)))) + paramtab->getnode(paramtab, s))) && + !(pm->node.flags & PM_NAMEREF)) unsetparam_pm(pm, 0, 1); unqueue_signals(); } @@ -5783,7 +5842,8 @@ static const struct paramtypes pmtypes[] = { { PM_TAGGED, "tagged", 't', 0}, { PM_EXPORTED, "exported", 'x', 0}, { PM_UNIQUE, "unique", 'U', 0}, - { PM_TIED, "tied", 'T', 0} + { PM_TIED, "tied", 'T', 0}, + { PM_NAMEREF, "namref", 'n', 0} }; #define PMTYPES_SIZE ((int)(sizeof(pmtypes)/sizeof(struct paramtypes))) @@ -6037,3 +6097,123 @@ printparamnode(HashNode hn, int printflags) else if (!(printflags & PRINT_KV_PAIR)) putchar('\n'); } + +/**/ +mod_export HashNode +resolve_nameref(Param pm, const Asgment stop) +{ + HashNode hn = (HashNode)pm; + const char *seek = stop ? stop->value.scalar : NULL; + + if (pm && (pm->node.flags & PM_NAMEREF)) { + if (pm && (pm->node.flags & (PM_UNSET|PM_TAGGED))) { + /* Semaphore with createparam() */ + pm->node.flags &= ~PM_UNSET; + /* See V10private.ztst end is in scope but private: + if (pm->node.flags & PM_SPECIAL) + return NULL; + */ + return (HashNode) pm; + } else if (pm->u.str) { + if ((pm->node.flags & PM_TAGGED) || + (stop && strcmp(pm->u.str, stop->name) == 0)) { + /* zwarnnam(pm->u.str, "invalid self reference"); */ + return stop ? (HashNode)pm : NULL; + } + if (*(pm->u.str)) + seek = pm->u.str; + } + } + else if (pm && !(stop && (stop->flags & PM_NAMEREF))) + return (HashNode)pm; + if (seek) { + queue_signals(); + /* pm->width is the offset of any subscript */ + if (pm && (pm->node.flags & PM_NAMEREF) && pm->width) { + if (stop) { + if (stop->flags & PM_NAMEREF) + hn = (HashNode)pm; + else + hn = NULL; + } else { + /* this has to be the end of any chain */ + hn = (HashNode)pm; /* see fetchvalue() */ + } + } else if ((hn = gethashnode2(realparamtab, seek))) { + if (pm) { + if (!(stop && (stop->flags & (PM_LOCAL)))) + hn = (HashNode)upscope((Param)hn, + ((pm->node.flags & PM_NAMEREF) ? + pm->base : ((Param)hn)->level)); + /* user can't tag a nameref, safe for loop detection */ + pm->node.flags |= PM_TAGGED; + } + if (hn) { + if (hn->flags & PM_AUTOLOAD) + hn = getparamnode(realparamtab, seek); + if (!(hn->flags & PM_UNSET)) + hn = resolve_nameref((Param)hn, stop); + } + if (pm) + pm->node.flags &= ~PM_TAGGED; + } else if (stop && (stop->flags & PM_NAMEREF)) + hn = (HashNode)pm; + unqueue_signals(); + } + + return hn; +} + +/**/ +static void +setscope(Param pm) +{ + if (pm->node.flags & PM_NAMEREF) { + Param basepm; + char *t = pm->u.str ? itype_end(pm->u.str, IIDENT, 0) : NULL; + + /* Temporarily change nameref to array parameter itself */ + if (t && *t == '[') + *t = 0; + else + t = 0; + basepm = (Param)resolve_nameref(pm, NULL); + if (t) { + pm->width = t - pm->u.str; + *t = '['; + } + if (basepm) + pm->base = ((basepm->node.flags & PM_NAMEREF) ? + basepm->base : basepm->level); + } +} + +/**/ +mod_export Param +upscope(Param pm, int reflevel) +{ + Param up = pm->old; + while (pm && up && up->level >= reflevel) { + pm = up; + if (up) + up = up->old; + } + return pm; +} + +/**/ +mod_export int +valid_refname(char *val) +{ + char *t = itype_end(val, IIDENT, 0); + + if (*t != 0) { + if (*t == '[') { + tokenize(t = dupstring(t+1)); + t = parse_subscript(t, 0, ']'); + } else { + t = NULL; + } + } + return !!t; +} diff --git a/Src/subst.c b/Src/subst.c index 3dd920e87..05bfcc03b 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -2573,13 +2573,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, !(v->pm->node.flags & PM_UNSET))) { int f = v->pm->node.flags; - switch (PM_TYPE(f)) { + switch (PM_TYPE(f)|(f & PM_NAMEREF)) { case PM_SCALAR: val = "scalar"; break; case PM_ARRAY: val = "array"; break; case PM_INTEGER: val = "integer"; break; case PM_EFLOAT: case PM_FFLOAT: val = "float"; break; case PM_HASHED: val = "association"; break; + case PM_NAMEREF: val = "nameref"; break; } val = dupstring(val); if (v->pm->level) diff --git a/Src/zsh.h b/Src/zsh.h index f82e76e4b..1e35bd33e 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1852,8 +1852,9 @@ struct param { GsuHash h; } gsu; - int base; /* output base or floating point prec */ - int width; /* field width */ + int base; /* output base or floating point prec or */ + /* for namerefs, locallevel of reference */ + int width; /* field width or nameref subscript idx */ char *env; /* location in environment, if exported */ char *ename; /* name of corresponding environment var */ Param old; /* old struct for use with local */ @@ -1932,9 +1933,10 @@ struct tieddata { */ #define PM_HASHELEM (1<<28) /* is a hash-element */ #define PM_NAMEDDIR (1<<29) /* has a corresponding nameddirtab entry */ +#define PM_NAMEREF (1<<30) /* pointer to a different parameter */ /* The option string corresponds to the first of the variables above */ -#define TYPESET_OPTSTR "aiEFALRZlurtxUhHTkz" +#define TYPESET_OPTSTR "aiEFALRZlurtxUhHT" /* These typeset options take an optional numeric argument */ #define TYPESET_OPTNUM "LRZiEF" -- cgit v1.2.3 From 3e55a135c10d3582af22a3e6dc616f57ea212df8 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 12 Feb 2023 11:29:10 -0800 Subject: 51374: Expose named references in $parameters, fix substitution error. --- ChangeLog | 4 ++ Src/Modules/parameter.c | 11 +++-- Src/params.c | 10 ++++- Test/K01nameref.ztst | 105 ++++++++++++++++++++++++++++++++++++++++-------- Test/README | 1 + 5 files changed, 110 insertions(+), 21 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 221b9838d..af448c5e3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-02-12 Bart Schaefer + * 51374: Src/Modules/parameter.c, Src/params.c, Test/README, + Test/K01nameref.ztst: Expose named references in $parameters, + fix substitution error. + * 51362: Doc/Zsh/builtins.yo, Doc/Zsh/expn.yo, Doc/Zsh/params.yo, Doc/Zsh/mod_parameter.yo: Begin documentation for named references. diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index 5bf675e2a..a659300fd 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -105,10 +105,15 @@ getpmparameter(UNUSED(HashTable ht), const char *name) pm->node.nam = dupstring(name); pm->node.flags = PM_SCALAR | PM_READONLY; pm->gsu.s = &nullsetscalar_gsu; - if ((rpm = (Param) realparamtab->getnode(realparamtab, name)) && - !(rpm->node.flags & PM_UNSET)) + if ((rpm = (Param) realparamtab->getnode2(realparamtab, name)) && + !(rpm->node.flags & PM_UNSET)) { pm->u.str = paramtypestr(rpm); - else { + if ((rpm->node.flags & PM_NAMEREF) && + (rpm = (Param) realparamtab->getnode(realparamtab, name)) && + !(rpm->node.flags & PM_UNSET)) { + pm->u.str = zhtricat(pm->u.str, "-", paramtypestr(rpm)); + } + } else { pm->u.str = dupstring(""); pm->node.flags |= (PM_UNSET|PM_SPECIAL); } diff --git a/Src/params.c b/Src/params.c index 69b7f484f..98950d88f 100644 --- a/Src/params.c +++ b/Src/params.c @@ -2155,7 +2155,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) return NULL; if (ss) *ss = sav; - s = ss; + s = dyncat(ss,*pptr); } if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) { /* Overload v->isarr as the flag bits for hashed arrays. */ @@ -6170,6 +6170,7 @@ setscope(Param pm) { if (pm->node.flags & PM_NAMEREF) { Param basepm; + struct asgment stop; char *t = pm->u.str ? itype_end(pm->u.str, IIDENT, 0) : NULL; /* Temporarily change nameref to array parameter itself */ @@ -6177,7 +6178,12 @@ setscope(Param pm) *t = 0; else t = 0; - basepm = (Param)resolve_nameref(pm, NULL); + stop.name = ""; + stop.value.scalar = NULL; + stop.flags = PM_NAMEREF; + if (locallevel) + stop.flags |= PM_LOCAL; + basepm = (Param)resolve_nameref(pm, &stop); if (t) { pm->width = t - pm->u.str; *t = '['; diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index b38831100..a663194a7 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -1,11 +1,11 @@ -# Tests for the zsh/param/private module +# Tests for named references %prep # Required in order to declare an unset hash for substitution test setopt TYPESET_TO_UNSET - : ${ZTST_continue:=1} + : ${ZTST_continue::=1} %test @@ -53,7 +53,13 @@ F:Other type changes are fatal errors, should this also be? typeset var=value typeset -n ptr=var print $ptr -0:basic nameref expansion +0:basic nameref expansion, no braces +>value + + typeset var=value + typeset -n ptr=var + print ${ptr} +0:basic nameref expansion, braces >value typeset var=(val1 val2) @@ -115,9 +121,10 @@ F:Other type changes are fatal errors, should this also be? >typeset -n ptr=var >typeset -a var=( new1 new2 ) - typeset -p ptr1 ptr2 var + typeset -p ptr ptr1 ptr2 var 1:check state of paramtab ONE F:unexpected side-effects of previous tests +*?*no such variable: ptr *?*no such variable: ptr1 *?*no such variable: ptr2 *?*no such variable: var @@ -247,13 +254,24 @@ F:unexpected side-effects of previous tests typeset -n ptr2='path[2]' print -r -- $ptr2 -0d:nameref to array element +0q:nameref to array element, no braces +>${path[2]} + + typeset -n ptr2='path[2]' + print -r -- ${ptr2} +0q:nameref to array element, with braces >${path[2]} typeset -A hash=(x MISS y HIT) typeset -n ptr1='hash[y]' print -r -- $ptr1 -0:nameref to hash element +0:nameref to hash element, no braces +>HIT + + typeset -A hash=(x MISS y HIT) + typeset -n ptr1='hash[y]' + print -r -- ${ptr1} +0:nameref to hash element, with braces >HIT typeset -a ary=(1 2) @@ -362,16 +380,16 @@ F:unexpected side-effects of previous tests >typeset -n ptr=lval >typeset -n ptr=gval + typeset -A var=(myself outside) () { - zmodload -u zsh/parameter - typeset -n myself=parameters[myself] - local -h parameters + typeset -n myself=var[myself] + local -h var print -r -- $myself - typeset -p parameters + typeset -p var } -0:up-reference part 3, autoloading with hidden special ->nameref-local ->typeset parameters +0:up-reference part 3, hidden global +>outside +>typeset var () { typeset notdef @@ -401,7 +419,7 @@ F:unexpected side-effects of previous tests 1:up-reference part 5, stacked namerefs, end not in scope F:What is the correct behavior for the scope of ptr1? >typeset -n ptr1=ptr2 ->typeset -n ptr2='' +>typeset -n ptr2 >ptr1=ptr2 >ptr2=val >ptr1=LOCAL @@ -427,13 +445,68 @@ F:What is the correct behavior for the scope of ptr1? 0:up-reference part 6, stacked namerefs, end is in scope F:Same test, should part 5 output look like this? >typeset -n ptr1=ptr2 ->typeset -n ptr2='' +>typeset -n ptr2 >ptr1=ptr2 ->ptr2='' +>ptr2 >ptr1=val >ptr2= >typeset -n ptr1=ptr2 >typeset -n ptr2='' >typeset ptr2=val + if zmodload zsh/parameter; then + () { + zmodload -u zsh/parameter + typeset -n myself=parameters[myself] + local -h parameters + print -r -- $myself + typeset -p parameters + } + else ZTST_skip='Cannot zmodload zsh/parameter, skipping autoload test' + fi +0:up-reference part 3, autoloading with hidden special +>nameref-local-nameref-local +>typeset parameters + + typeset ptr2=var2 + typeset var2=GLOBAL + () { + typeset -n ptr1=ptr2 + typeset ptr2=var1 + typeset var1=VAR1 + typeset var2=VAR2 + print -r -- ${(P)ptr1} + } +0: +>VAR2 + + ary=(one two three four) + typeset -n ptr=ary + print -r ${(j.:.)ptr//o/0} +0:expansion flags and string replacement +>0ne:tw0:three:f0ur + + var=value + typeset -n ptr=var + myscalar=ptr + echo ${(P)myscalar} +0:named references with (P), as ${(P)name_of_nameref} +>value + + var=value + myscalar=var + typeset -n ptr=myscalar + echo ${(P)ptr} +0:named references with (P), as ${(P)nameref} +>value + + ary=( 'bry[1]' 'bry[2]' ) + bry=( lorem ipsum ) + typeset -n ptr='ary[2]' + print -r -- ${ptr} + print -r -- ${(P)ptr} +0:named references with (P), array element to array element +>bry[2] +>ipsum + %clean diff --git a/Test/README b/Test/README index 670434ac3..b9d393d7c 100644 --- a/Test/README +++ b/Test/README @@ -6,6 +6,7 @@ scripts names: C: shell commands with special syntax D: substititution E: options + K: features adopted from ksh P: privileged (needs super-user privileges) V: modules W: builtin interactive commands and constructs -- cgit v1.2.3 From 3eed6f70cdfea63cfdc380a4df8382fff38af55d Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 12 Feb 2023 11:51:41 -0800 Subject: 51402: Some ksh/bash features, additional sanity checking * Add "unset -n" * Allow and enforce "typeset -n -r" for read-only references * "can't change type via subscript reference" error * Better checking for self-referential declarations/assignments * Ksh-style "foo=bar; typeset -n foo" creates foo=bar reference * Support "typeset -n ref; for ref in ..." * Subscripted references use NO_EXEC for safety * References assigned in called scopes reset scope at end * Allow named references to $! $? $$ $- $0 $_ --- ChangeLog | 5 ++++ Src/builtin.c | 49 +++++++++++++++++++++++-------- Src/loop.c | 6 ++-- Src/params.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- Src/zsh.h | 4 +++ 5 files changed, 129 insertions(+), 27 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 1353de45d..2b2f2a08b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2023-02-12 Bart Schaefer + * 51402: Src/builtin.c, Src/loop.c, Src/params.c, Src/zsh.h: + Add ksh/bash features (unset -n, for ref), readonly refs, + better error checking and messages, code injection safety, + allow references to $! $? $$ $- $0 $_ + * 51375: Doc/Zsh/builtins.yo, Doc/Zsh/expn.yo, Doc/Zsh/params.yo, Doc/Zsh/mod_parameter.yo: Clarify, fix typos, add indexing. diff --git a/Src/builtin.c b/Src/builtin.c index 8039b644e..cf7e9d9fe 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -126,7 +126,7 @@ static struct builtin builtins[] = BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL), BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"), BUILTIN("unhash", 0, bin_unhash, 1, -1, BIN_UNHASH, "adfms", NULL), - BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, BIN_UNSET, "fmv", NULL), + BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, BIN_UNSET, "fmvn", NULL), BUILTIN("unsetopt", 0, bin_setopt, 0, -1, BIN_UNSETOPT, NULL, NULL), BUILTIN("wait", 0, bin_fg, 0, -1, BIN_WAIT, NULL, NULL), BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSwx:", NULL), @@ -2034,11 +2034,16 @@ typeset_single(char *cname, char *pname, Param pm, int func, if (!(off & PM_NAMEREF)) pm = (Param)resolve_nameref(pm, NULL); if (pm && (pm->node.flags & PM_NAMEREF) && - (on & ~(PM_NAMEREF|PM_LOCAL))) { + (on & ~(PM_NAMEREF|PM_LOCAL|PM_READONLY))) { /* Changing type of PM_SPECIAL|PM_AUTOLOAD is a fatal error. * * Should this be a fatal error as well, rather than warning? */ - zwarnnam(cname, "%s: can't change type of a named reference", - pname); + if (pm->width) + zwarnnam(cname, + "%s: can't change type via subscript reference", + pm->u.str); + else + zwarnnam(cname, "%s: can't change type of a named reference", + pname); return NULL; } } @@ -2223,6 +2228,11 @@ typeset_single(char *cname, char *pname, Param pm, int func, zerrnam(cname, "%s: restricted", pname); return pm; } + if ((pm->node.flags & PM_READONLY) && + (pm->node.flags & PM_NAMEREF & off)) { + zerrnam(cname, "%s: read-only reference", pname); + return pm; + } if ((on & PM_UNIQUE) && !(pm->node.flags & PM_READONLY & ~off)) { Param apm; char **x; @@ -2659,7 +2669,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) off |= bit; } if (OPT_MINUS(ops,'n')) { - if (on|off) { + if ((on & ~PM_READONLY)|off) { zwarnnam(name, "no other attributes allowed with -n"); return 1; } @@ -3051,9 +3061,8 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) if (on & PM_NAMEREF) { if (asg->value.scalar && - (strcmp(asg->name, asg->value.scalar) == 0 || - ((pm = (Param)resolve_nameref((Param)hn, asg)) && - (pm->node.flags & PM_NAMEREF)))) { + ((pm = (Param)resolve_nameref((Param)hn, asg)) && + (pm->node.flags & PM_NAMEREF))) { if (pm->node.flags & PM_SPECIAL) zwarnnam(name, "%s: invalid reference", pm->node.nam); else @@ -3063,8 +3072,12 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) } if (hn) { /* namerefs always start over fresh */ - if (((Param)hn)->level >= locallevel) + if (((Param)hn)->level >= locallevel) { + Param oldpm = (Param)hn; + if (!asg->value.scalar && oldpm->u.str) + asg->value.scalar = dupstring(oldpm->u.str); unsetparam_pm((Param)hn, 0, 1); + } hn = NULL; } } @@ -3762,7 +3775,11 @@ bin_unset(char *name, char **argv, Options ops, int func) if ((!(pm->node.flags & PM_RESTRICTED) || unset(RESTRICTED)) && pattry(pprog, pm->node.nam)) { - unsetparam_pm(pm, 0, 1); + if (!OPT_ISSET(ops,'n') && + (pm->node.flags & PM_NAMEREF) && pm->u.str) + unsetparam(pm->u.str); + else + unsetparam_pm(pm, 0, 1); match++; } } @@ -3814,6 +3831,11 @@ bin_unset(char *name, char **argv, Options ops, int func) zerrnam(name, "%s: restricted", pm->node.nam); returnval = 1; } else if (ss) { + if ((pm->node.flags & PM_NAMEREF) && + (!(pm = (Param)resolve_nameref(pm, NULL)) || pm->width)) { + /* warning? */ + continue; + } if (PM_TYPE(pm->node.flags) == PM_HASHED) { HashTable tht = paramtab; if ((paramtab = pm->gsu.h->getfn(pm))) @@ -3852,8 +3874,11 @@ bin_unset(char *name, char **argv, Options ops, int func) returnval = 1; } } else { - if ((pm = (Param)resolve_nameref(pm, NULL)) && - unsetparam_pm(pm, 0, 1)) + if (!OPT_ISSET(ops,'n')) { + if (!(pm = (Param)resolve_nameref(pm, NULL))) + continue; + } + if (unsetparam_pm(pm, 0, 1)) returnval = 1; } if (ss) diff --git a/Src/loop.c b/Src/loop.c index 7df379ecf..0f3847541 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -53,7 +53,7 @@ execfor(Estate state, int do_exec) wordcode code = state->pc[-1]; int iscond = (WC_FOR_TYPE(code) == WC_FOR_COND), ctok = 0, atok = 0; int last = 0; - char *name, *str, *cond = NULL, *advance = NULL; + char *str, *cond = NULL, *advance = NULL; zlong val = 0; LinkList vars = NULL, args = NULL; int old_simple_pline = simple_pline; @@ -151,7 +151,7 @@ execfor(Estate state, int do_exec) int count = 0; for (node = firstnode(vars); node; incnode(node)) { - name = (char *)getdata(node); + char *name = (char *)getdata(node); if (!args || !(str = (char *) ugetnode(args))) { if (count) { @@ -165,7 +165,7 @@ execfor(Estate state, int do_exec) fprintf(xtrerr, "%s=%s\n", name, str); fflush(xtrerr); } - setsparam(name, ztrdup(str)); + setloopvar(name, ztrdup(str)); count++; } if (!count) diff --git a/Src/params.c b/Src/params.c index 98950d88f..4910d65fe 100644 --- a/Src/params.c +++ b/Src/params.c @@ -997,7 +997,7 @@ createparam(char *name, int flags) paramtab->getnode(paramtab, name)); if (oldpm && (oldpm->node.flags & PM_NAMEREF) && - !(flags & PM_NAMEREF)) { + !(flags & PM_NAMEREF) && (oldpm = upscope(oldpm, oldpm->base))) { Param lastpm; struct asgment stop; stop.flags = PM_NAMEREF | (flags & PM_LOCAL); @@ -1467,10 +1467,14 @@ getarg(char **str, int *inv, Value v, int a2, zlong *w, if (ishash && (keymatch || !rev)) remnulargs(s); if (needtok) { + char exe = opts[EXECOPT]; s = dupstring(s); if (parsestr(&s)) return 0; + if (flags & SCANPM_NOEXEC) + opts[EXECOPT] = 0; singsub(&s); + opts[EXECOPT] = exe; } else if (rev) remnulargs(s); /* This is probably always a no-op, but ... */ if (!rev) { @@ -2153,9 +2157,12 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) ((pm->node.flags & PM_UNSET) && !(pm->node.flags & PM_DECLARED))) return NULL; - if (ss) + if (ss) { + flags |= SCANPM_NOEXEC; *ss = sav; - s = dyncat(ss,*pptr); + s = dyncat(ss,*pptr); + } else + s = *pptr; } if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) { /* Overload v->isarr as the flag bits for hashed arrays. */ @@ -5782,7 +5789,8 @@ scanendscope(HashNode hn, UNUSED(int flags)) export_param(pm); } else unsetparam_pm(pm, 0, 0); - } + } else if ((pm->node.flags & PM_NAMEREF) && pm->base > pm->level) + pm->base = locallevel; } @@ -6109,10 +6117,10 @@ resolve_nameref(Param pm, const Asgment stop) if (pm && (pm->node.flags & (PM_UNSET|PM_TAGGED))) { /* Semaphore with createparam() */ pm->node.flags &= ~PM_UNSET; - /* See V10private.ztst end is in scope but private: - if (pm->node.flags & PM_SPECIAL) + if (pm->node.flags & PM_NEWREF) /* See setloopvar() */ return NULL; - */ + if (pm->u.str && *(pm->u.str) && (pm->node.flags & PM_TAGGED)) + pm->node.flags |= PM_SELFREF; /* See setscope() */ return (HashNode) pm; } else if (pm->u.str) { if ((pm->node.flags & PM_TAGGED) || @@ -6157,13 +6165,29 @@ resolve_nameref(Param pm, const Asgment stop) if (pm) pm->node.flags &= ~PM_TAGGED; } else if (stop && (stop->flags & PM_NAMEREF)) - hn = (HashNode)pm; + hn = (pm && (pm->node.flags & PM_NEWREF)) ? NULL : (HashNode)pm; unqueue_signals(); } return hn; } +/**/ +mod_export void +setloopvar(char *name, char *value) +{ + Param pm = (Param) gethashnode2(realparamtab, name); + + if (pm && (pm->node.flags & PM_NAMEREF)) { + pm->base = pm->width = 0; + pm->u.str = ztrdup(value); + pm->node.flags |= PM_NEWREF; + setscope(pm); + pm->node.flags &= ~PM_NEWREF; + } else + setsparam(name, value); +} + /**/ static void setscope(Param pm) @@ -6188,9 +6212,50 @@ setscope(Param pm) pm->width = t - pm->u.str; *t = '['; } - if (basepm) - pm->base = ((basepm->node.flags & PM_NAMEREF) ? - basepm->base : basepm->level); + if (basepm) { + if (basepm->node.flags & PM_NAMEREF) { + if (pm == basepm) { + if (pm->node.flags & PM_SELFREF) { + /* Loop signalled by resolve_nameref() */ + if (upscope(pm, pm->base) == pm) { + zerr("%s: invalid self reference", pm->u.str); + unsetparam_pm(pm, 0, 1); + return; + } + pm->node.flags &= ~PM_SELFREF; + } else if (pm->base == pm->level) { + if (pm->u.str && *(pm->u.str) && + strcmp(pm->node.nam, pm->u.str) == 0) { + zerr("%s: invalid self reference", pm->u.str); + unsetparam_pm(pm, 0, 1); + return; + } + } + } else if (basepm->u.str) { + if (basepm->base <= basepm->level && + strcmp(pm->node.nam, basepm->u.str) == 0) { + zerr("%s: invalid self reference", pm->u.str); + unsetparam_pm(pm, 0, 1); + return; + } + } + } else + pm->base = basepm->level; + } + if (pm->base > pm->level) { + if (EMULATION(EMULATE_KSH)) { + zerr("%s: global reference cannot refer to local variable", + pm->node.nam); + unsetparam_pm(pm, 0, 1); + } else if (isset(WARNNESTEDVAR)) + zwarn("%s: global reference to local variable: %s", + pm->node.nam, pm->u.str); + } + if (pm->u.str && upscope(pm, pm->base) == pm && + strcmp(pm->node.nam, pm->u.str) == 0) { + zerr("%s: invalid self reference", pm->u.str); + unsetparam_pm(pm, 0, 1); + } } } @@ -6217,7 +6282,10 @@ valid_refname(char *val) if (*t == '[') { tokenize(t = dupstring(t+1)); t = parse_subscript(t, 0, ']'); - } else { + } else if (t[1] || !(*t == '!' || *t == '?' || + *t == '$' || *t == '-' || + *t == '0' || *t == '_')) { + /* Skipping * @ # because of doshfunc() implementation */ t = NULL; } } diff --git a/Src/zsh.h b/Src/zsh.h index 1e35bd33e..96b4b06bd 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1935,6 +1935,9 @@ struct tieddata { #define PM_NAMEDDIR (1<<29) /* has a corresponding nameddirtab entry */ #define PM_NAMEREF (1<<30) /* pointer to a different parameter */ +#define PM_SELFREF PM_UNIQUE /* Overload when namerefs resolved */ +#define PM_NEWREF PM_SINGLE /* Overload in for-loop namerefs */ + /* The option string corresponds to the first of the variables above */ #define TYPESET_OPTSTR "aiEFALRZlurtxUhHT" @@ -1959,6 +1962,7 @@ struct tieddata { * elements */ #define SCANPM_CHECKING (1<<10) /* Check if set, no need to create */ +#define SCANPM_NOEXEC (1<<11) /* No command substitutions, etc. */ /* "$foo[@]"-style substitution * Only sign bit is significant */ -- cgit v1.2.3 From f4c706f0c84bddb7777d38635c4ba1d43703e2e5 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 12 Feb 2023 12:20:33 -0800 Subject: 51417: Check subscripts in named reference values more rigorously. --- ChangeLog | 3 +++ Src/params.c | 11 ++++++++++- Test/K01nameref.ztst | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 0cc33c7f1..6dc453f7f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-02-12 Bart Schaefer + * 51417: Src/params.c, Test/K01nameref.ztst: Check subscripts + in named reference values more rigorously. + * 51403: Doc/Zsh/builtins.yo, Doc/Zsh/expn.yo, Doc/Zsh/func.yo, Doc/Zsh/grammar.yo, Doc/Zsh/params.yo, Test/K01nameref.ztst: Tests and documentation for 51402, clean up some other tests. diff --git a/Src/params.c b/Src/params.c index 4910d65fe..f61374b87 100644 --- a/Src/params.c +++ b/Src/params.c @@ -6281,7 +6281,16 @@ valid_refname(char *val) if (*t != 0) { if (*t == '[') { tokenize(t = dupstring(t+1)); - t = parse_subscript(t, 0, ']'); + while ((t = parse_subscript(t, 0, ']')) && *t++ == Outbrack) { + if (*t == Inbrack) + ++t; + else + break; + } + if (t && *t) { + /* zwarn("%s: stuff after subscript: %s", val, t); */ + t = NULL; + } } else if (t[1] || !(*t == '!' || *t == '?' || *t == '$' || *t == '-' || *t == '0' || *t == '_')) { diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index 61c2b006a..d2abdd391 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -350,9 +350,9 @@ F:ksh93 does not implement this either >typeset -A hash=( [y]=HIT ) unset -n ptr1 - typeset -n ptr1='not good' + typeset -n ptr1='not[2]good' 1:invalid nameref -*?*invalid variable name: not good +*?*invalid variable name: not\[2\]good unset -n ptr1 unset hash -- cgit v1.2.3 From 03887bb03fbca246fa94b5b5f2266572c0b6d038 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 13 Feb 2023 18:20:11 -0800 Subject: 51430: Misc. problems with typeset and $parameters * Fix and test for regression of assignment when using typeset command * Fix output of typeset +m and $parameters[ref] * Prevent segfault in typeset --- ChangeLog | 7 +++++++ Src/Modules/parameter.c | 2 +- Src/builtin.c | 16 ++++++++++------ Src/params.c | 2 +- Test/K01nameref.ztst | 14 ++++++++++++++ 5 files changed, 33 insertions(+), 8 deletions(-) (limited to 'Src') 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 7e0c4406ceba1e021bf37680d6a6b8dcd3dd657f Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 14 Feb 2023 09:21:19 +0000 Subject: 51424: $(<...) shouldn't try to open a file with NO_EXEC --- ChangeLog | 5 +++++ Src/exec.c | 3 +++ Test/E01options.ztst | 3 +++ 3 files changed, 11 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index be0d8d5e1..b490b0ee8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-02-14 Peter Stephenson + + * 51425: Src/exec.c, Test/E01OPTIONS: $(<...) shouldn't try to + open a file with NO_EXEC. + 2023-02-13 Bart Schaefer * 51430: Src/Modules/parameter.c, Src/builtin.c, Src/params.c, diff --git a/Src/exec.c b/Src/exec.c index c8eb71b34..3330bbce8 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -4678,6 +4678,9 @@ getoutput(char *cmd, int qt) if (!prog) return NULL; + if (!isset(EXECOPT)) + return newlinklist(); + if ((s = simple_redir_name(prog, REDIR_READ))) { /* $(< word) */ int stream; diff --git a/Test/E01options.ztst b/Test/E01options.ztst index d38fbed74..533e08773 100644 --- a/Test/E01options.ztst +++ b/Test/E01options.ztst @@ -416,6 +416,9 @@ 1:NO_EXEC does recognize bad substitution syntax *?* bad substitution + (setopt noexec; : $( Date: Tue, 14 Feb 2023 17:54:42 -0800 Subject: 51437: Fix incorrectly-passed test case, masked by unrelated bug. A bug with zmodload when unloading/reloading a static module caused the state of the shell options to change during K01 test. Worked around it. Also changed warnnestedvar messages to look more like other such. --- ChangeLog | 5 +++++ Src/params.c | 5 +++-- Test/K01nameref.ztst | 23 +++++++++++++++++++++-- 3 files changed, 29 insertions(+), 4 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index b490b0ee8..9e7608293 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-02-14 Bart Schaefer + + * 51437: Src/params.c, Test/K01nameref.ztst: Fix incorrectly-passed + test case, masked by unrelated bug. Improve warnnestedvar message. + 2023-02-14 Peter Stephenson * 51425: Src/exec.c, Test/E01OPTIONS: $(<...) shouldn't try to diff --git a/Src/params.c b/Src/params.c index 92cbecf63..e940d7995 100644 --- a/Src/params.c +++ b/Src/params.c @@ -3068,7 +3068,7 @@ check_warn_pm(Param pm, const char *pmtype, int created, } else return; - if (pm->node.flags & PM_SPECIAL) + if (pm->node.flags & (PM_SPECIAL|PM_NAMEREF)) return; for (i = funcstack; i; i = i->prev) { @@ -6181,6 +6181,7 @@ setloopvar(char *name, char *value) if (pm && (pm->node.flags & PM_NAMEREF)) { pm->base = pm->width = 0; pm->u.str = ztrdup(value); + pm->node.flags &= ~PM_UNSET; pm->node.flags |= PM_NEWREF; setscope(pm); pm->node.flags &= ~PM_NEWREF; @@ -6248,7 +6249,7 @@ setscope(Param pm) pm->node.nam); unsetparam_pm(pm, 0, 1); } else if (isset(WARNNESTEDVAR)) - zwarn("%s: global reference to local variable: %s", + zwarn("reference %s in enclosing scope set to local variable %s", pm->node.nam, pm->u.str); } if (pm->u.str && upscope(pm, pm->base) == pm && diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index d240e4917..6a5e767df 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -532,6 +532,13 @@ F:Same test, should part 5 output look like this? >nameref-local-nameref-local >typeset parameters + if [[ $options[typesettounset] != on ]]; then + ZTST_skip='Ignoring zmodload bug that resets TYPESET_TO_UNSET' + setopt typesettounset + fi +0:options reloaded +F:Checking for a bug in zmodload that affects later tests + typeset ptr2=var2 typeset var2=GLOBAL () { @@ -541,7 +548,7 @@ F:Same test, should part 5 output look like this? typeset var2=VAR2 print -r -- ${(P)ptr1} } -0: +0:Order of evaluation with ${(P)...} >VAR2 ary=(one two three four) @@ -666,7 +673,19 @@ F:Same test, should part 5 output look like this? > >scalar-local > -*?*ref: global reference to local variable: one +*?*reference ref*to local variable one + + unset -n ref + typeset -n ref + () { + setopt localoptions warn_nested_var + typeset inner + ref=inner + } + typeset -p ref +0:Global variable is a reference, warning +>typeset -n ref=inner +*?*reference ref*to local variable inner typeset -n ptr='ary[$(echo 2)]' typeset -a ary=(one two three) -- cgit v1.2.3 From 6f4aa1d9496ed37e6ebbf70d86a35a9d5e1fb605 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Fri, 17 Feb 2023 23:38:14 +0100 Subject: 51447: silence compiler maybe-uninitialized warning by combining a couple of variables --- ChangeLog | 3 +++ Src/Zle/zle_keymap.c | 25 +++++++++++++------------ 2 files changed, 16 insertions(+), 12 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index d4701b99b..7472298d6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-02-17 Oliver Kiddle + * 51447: Src/Zle/zle_keymap.c: silence compiler maybe-uninitialized + warning by combining a couple of variables + * Øystein Walle: 51391: Completion/Unix/Command/_git: complete remote branch names respecting --delete for git push diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index ec8dd031e..a31ab22d7 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -1586,7 +1586,7 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp) Thingy func = t_undefinedkey; char *str = NULL; int lastlen = 0, lastc = lastchar; - int timeout = 0, csi = 0, startcsi; + int timeout = 0, csi = 0; keybuflen = 0; keybuf[0] = 0; @@ -1640,22 +1640,23 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp) /* CSI key sequences have a well defined structure so if we currently * have an incomplete one, loop so the rest of it will be included in * the key sequence if that arrives within the timeout. */ - if (keybuflen >= 3 && !csi) { - startcsi = keybuflen - 3; - csi = keybuf[startcsi] == '\033' && keybuf[keybuflen - 2] == '['; - } + if (!csi && keybuflen >= 3 && keybuf[keybuflen - 3] == '\033' && + keybuf[keybuflen - 2] == '[') + csi = keybuflen - 1; if (csi) { - csi = keybuf[keybuflen - 2] != Meta && keybuf[keybuflen - 1] >= 0x20 - && keybuf[keybuflen - 1] <= 0x3f; + if (keybuf[keybuflen - 2] == Meta || keybuf[keybuflen - 1] < 0x20 + || keybuf[keybuflen - 1] > 0x3f) { /* If we reach the end of a valid CSI sequence and the matched key * binding is for part of the CSI introduction, select instead the * undefined-key widget and consume the full sequence from the * input buffer. */ - if (!csi && keybuf[keybuflen - 1] >= 0x40 && - keybuf[keybuflen - 1] <= 0x7e && lastlen > startcsi && - lastlen <= startcsi + 2) { - func = t_undefinedkey; - lastlen = keybuflen; + if (keybuf[keybuflen - 1] >= 0x40 && + keybuf[keybuflen - 1] <= 0x7e && lastlen > csi - 2 && + lastlen <= csi) { + func = t_undefinedkey; + lastlen = keybuflen; + } + csi = 0; } } -- 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') 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 82f307bddfaff0f6e45bf315ff43ea4192529cad Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 21 Feb 2023 12:16:40 +0000 Subject: Fix access to autoloaded parameter. Namerefef resolution needs to happen on the parameter after autoload. --- ChangeLog | 4 ++++ Src/params.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 52491f4b5..0b7d4dad1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-02-21 Peter Stephenson + + * 51466: Src/params.c: fix access to autoloaded parameter. + 2023-02-20 Bart Schaefer * 51431: Src/builtin.c: "typeset -p" shouldn't change parameter flags diff --git a/Src/params.c b/Src/params.c index e940d7995..90302b1b0 100644 --- a/Src/params.c +++ b/Src/params.c @@ -538,7 +538,7 @@ getparamnode(HashTable ht, const char *nam) } if (hn && ht == realparamtab) - hn = resolve_nameref(pm, NULL); + hn = resolve_nameref((Param)hn, NULL); return hn; } -- cgit v1.2.3 From ec4bd3169d0734ca8ec74ccc52235e71d7e59166 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 26 Feb 2023 18:54:10 -0800 Subject: 51460: avoid crash on bad parameter autofeature --- ChangeLog | 4 ++++ Src/module.c | 17 +++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 1891b8b5a..2c07606c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-02-26 Bart Schaefer + + * 51460: Src/module.c: avoid crash on bad parameter autofeature + 2023-02-24 Oliver Kiddle * Shohei YOSHIDA: 51473: Completion/Unix/Command/_cal: diff --git a/Src/module.c b/Src/module.c index 6cf442270..a6005f30b 100644 --- a/Src/module.c +++ b/Src/module.c @@ -1198,6 +1198,7 @@ add_autoparam(const char *module, const char *pnam, int flags) { Param pm; int ret; + int ne = noerrs; queue_signals(); if ((ret = checkaddparam(pnam, (flags & FEAT_IGNORE)))) { @@ -1212,14 +1213,18 @@ add_autoparam(const char *module, const char *pnam, int flags) return ret == 2 ? 0 : -1; } - pm = setsparam(dupstring(pnam), ztrdup(module)); - - pm->node.flags |= PM_AUTOLOAD; - if (flags & FEAT_AUTOALL) - pm->node.flags |= PM_AUTOALL; + noerrs = 2; + if ((pm = setsparam(dupstring(pnam), ztrdup(module)))) { + pm->node.flags |= PM_AUTOLOAD; + if (flags & FEAT_AUTOALL) + pm->node.flags |= PM_AUTOALL; + ret = 0; + } else + ret = -1; + noerrs = ne; unqueue_signals(); - return 0; + return ret; } /* Remove a parameter added with add_autoparam() */ -- cgit v1.2.3 From 86a5278f9f5ab516ff2b099d64af2d5567983b5a Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Tue, 28 Feb 2023 14:35:52 +0100 Subject: 51491: Check should use zlemetacs instead of zlecs Coverity noticed that this first branch of the if statement has "meta" added to all the variable names except this zlecs at the end, so change it to match. --- ChangeLog | 5 +++++ Src/Zle/zle_utils.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 6ba598e4c..3fc73e413 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-02-28 Mikael Magnusson + + * 51491: Src/Zle/zle_utils.c: Check should use zlemetacs instead + of zlecs + 2023-02-26 Bart Schaefer * 51464: Util/printdefines: utility to interpret zsh.h constants diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 9ce91049c..454a877a9 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -799,7 +799,7 @@ spaceinline(int ct) if (rhp->start_meta - sub >= zlemetacs) { rhp->start_meta += ct; } - if (rhp->end_meta - sub >= zlemetacs && (!predisplaylen || zlecs)) { + if (rhp->end_meta - sub >= zlemetacs && (!predisplaylen || zlemetacs)) { rhp->end_meta += ct; } } -- cgit v1.2.3 From 806d096b0e7a64bf9712be1cb8159a1ef5b4bf81 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 5 Mar 2023 13:26:57 -0800 Subject: unposted: fix memory leak flagged by coverity --- ChangeLog | 4 ++++ Src/Modules/param_private.c | 1 + 2 files changed, 5 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 3fc73e413..e344e8878 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-03-05 Bart Schaefer + + * unposted: Src/Modules/param_private.c: coverity memory leak + 2023-02-28 Mikael Magnusson * 51491: Src/Zle/zle_utils.c: Check should use zlemetacs instead diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index 70f36ceb1..e43f0edb4 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -122,6 +122,7 @@ makeprivate(HashNode hn, UNUSED(int flags)) break; default: makeprivate_error = 1; + zfree(gsu, sizeof(struct gsu_closure)); break; } /* PM_HIDE so new parameters in deeper scopes do not shadow */ -- cgit v1.2.3 From a9ba1662165823a0303a03fdeddb2ce4ca3814e5 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 5 Mar 2023 14:03:42 -0800 Subject: 51483: Enable assignment and expansion of parameters with ksh-like namespace prefixes. --- ChangeLog | 5 +++++ Src/Zle/compcore.c | 4 ++-- Src/Zle/zle_tricky.c | 6 +++--- Src/lex.c | 2 +- Src/params.c | 11 ++++++----- Src/subst.c | 15 +++++++++------ Src/utils.c | 18 ++++++++++++++++-- Src/zsh.h | 2 ++ Src/ztype.h | 1 + 9 files changed, 45 insertions(+), 19 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index e344e8878..38a0395d6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2023-03-05 Bart Schaefer + * 51483: Src/Zle/compcore.c, Src/Zle/zle_tricky.c, Src/lex.c, + Src/params.c, Src/subst.c, Src/utils.c, Src/zsh.h, Src/ztype.h: + Enable assignment and expansion of parameters with ksh-like + namespace prefixes. + * unposted: Src/Modules/param_private.c: coverity memory leak 2023-02-28 Mikael Magnusson diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 64a860fa3..77fce66e8 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -1230,14 +1230,14 @@ check_param(char *s, int set, int test) else if (idigit(*e)) while (idigit(*e)) e++; - else if ((ie = itype_end(e, IIDENT, 0)) != e) { + else if ((ie = itype_end(e, INAMESPC, 0)) != e) { do { e = ie; if (comppatmatch && *comppatmatch && (*e == Star || *e == Quest)) ie = e + 1; else - ie = itype_end(e, IIDENT, 0); + ie = itype_end(e, INAMESPC, 0); } while (ie != e); } diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 07fac7144..690cf6efb 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -576,7 +576,7 @@ parambeg(char *s) while (idigit(*e)) e++; else - e = itype_end(e, IIDENT, 0); + e = itype_end(e, INAMESPC, 0); /* Now make sure that the cursor is inside the name. */ if (offs <= e - s && offs >= b - s && n <= 0) { @@ -765,7 +765,7 @@ docomplete(int lst) else if (idigit(*q)) do q++; while (idigit(*q)); else - q = itype_end(q, IIDENT, 0); + q = itype_end(q, INAMESPC, 0); sav = *q; *q = '\0'; if (zlemetacs - wb == q - s && @@ -1497,7 +1497,7 @@ get_comp_string(void) if (varq) tt = clwords[clwpos]; - s = itype_end(tt, IIDENT, 0); + s = itype_end(tt, INAMESPC, 0); sav = *s; *s = '\0'; zsfree(varname); diff --git a/Src/lex.c b/Src/lex.c index 15da85a93..2f7937410 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -1230,7 +1230,7 @@ gettokstr(int c, int sub) else { int sav = *lexbuf.ptr; *lexbuf.ptr = '\0'; - t = itype_end(t, IIDENT, 0); + t = itype_end(t, INAMESPC, 0); if (t < lexbuf.ptr) { skipparens(Inbrack, Outbrack, &t); } else { diff --git a/Src/params.c b/Src/params.c index 90302b1b0..d3b6a7d43 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1223,7 +1223,7 @@ isident(char *s) break; } else { /* Find the first character in `s' not in the iident type table */ - ss = itype_end(s, IIDENT, 0); + ss = itype_end(s, INAMESPC, 0); } /* If the next character is not [, then it is * @@ -2086,6 +2086,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) char *s, *t, *ie; char sav, c; int ppar = 0; + int itype = (flags & SCANPM_NONAMESPC) ? IIDENT : INAMESPC; s = t = *pptr; @@ -2095,7 +2096,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) else ppar = *s++ - '0'; } - else if ((ie = itype_end(s, IIDENT, 0)) != s) + else if ((ie = itype_end(s, itype, 0)) != s) s = ie; else if (c == Quest) *s++ = '?'; @@ -2183,7 +2184,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) return v; } } else if (!(flags & SCANPM_ASSIGNING) && v->isarr && - itype_end(t, IIDENT, 1) != t && isset(KSHARRAYS)) + itype_end(t, INAMESPC, 1) != t && isset(KSHARRAYS)) v->end = 1, v->isarr = 0; } if (!bracks && *s) @@ -6196,7 +6197,7 @@ setscope(Param pm) if (pm->node.flags & PM_NAMEREF) { Param basepm; struct asgment stop; - char *t = pm->u.str ? itype_end(pm->u.str, IIDENT, 0) : NULL; + char *t = pm->u.str ? itype_end(pm->u.str, INAMESPC, 0) : NULL; /* Temporarily change nameref to array parameter itself */ if (t && *t == '[') @@ -6277,7 +6278,7 @@ upscope(Param pm, int reflevel) mod_export int valid_refname(char *val) { - char *t = itype_end(val, IIDENT, 0); + char *t = itype_end(val, INAMESPC, 0); if (*t != 0) { if (*t == '[') { diff --git a/Src/subst.c b/Src/subst.c index 05bfcc03b..7a4b433bc 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1870,7 +1870,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * these later on, too. */ c = *s; - if (itype_end(s, IIDENT, 1) == s && *s != '#' && c != Pound && + if (itype_end(s, INAMESPC, 1) == s && *s != '#' && c != Pound && !IS_DASH(c) && c != '!' && c != '$' && c != String && c != Qstring && c != '?' && c != Quest && @@ -2332,7 +2332,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, } } else if ((c == '#' || c == Pound) && (inbrace || !isset(POSIXIDENTIFIERS)) && - (itype_end(s+1, IIDENT, 0) != s + 1 + (itype_end(s+1, INAMESPC, 0) != s + 1 || (cc = s[1]) == '*' || cc == Star || cc == '@' || cc == '?' || cc == Quest || cc == '$' || cc == String || cc == Qstring @@ -2369,8 +2369,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * Try to handle this when parameter is named * by (P) (second part of test). */ - if (itype_end(s+1, IIDENT, 0) != s+1 || (aspar && isstring(s[1]) && - (s[2] == Inbrace || s[2] == Inpar))) + if (itype_end(s+1, INAMESPC, 0) != s+1 || + (aspar && isstring(s[1]) && + (s[2] == Inbrace || s[2] == Inpar))) chkset = 1, s++; else if (!inbrace) { /* Special case for `$+' on its own --- leave unmodified */ @@ -2531,6 +2532,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, scanflags |= SCANPM_DQUOTED; if (chkset) scanflags |= SCANPM_CHECKING; + if (!inbrace) + scanflags |= SCANPM_NONAMESPC; /* * Second argument: decide whether to use the subexpression or * the string next on the line as the parameter name. @@ -3211,7 +3214,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, shortest = 0; ++s; } - if (*itype_end(s, IIDENT, 0)) { + if (*itype_end(s, INAMESPC, 0)) { untokenize(s); zerr("not an identifier: %s", s); return NULL; @@ -3271,7 +3274,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, int intersect = (*s == '*' || *s == Star); char **compare, **ap, **apsrc; ++s; - if (*itype_end(s, IIDENT, 0)) { + if (*itype_end(s, INAMESPC, 0)) { untokenize(s); zerr("not an identifier: %s", s); return NULL; diff --git a/Src/utils.c b/Src/utils.c index 55f2d1ab0..1393ecb13 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -3123,7 +3123,7 @@ spckword(char **s, int hist, int cmd, int ask) if (**s == String && !*t) { guess = *s + 1; - if (itype_end(guess, IIDENT, 1) == guess) + if (itype_end(guess, INAMESPC, 1) == guess) return; ic = String; d = 100; @@ -4310,13 +4310,27 @@ wcsitype(wchar_t c, int itype) * If "once" is set, just test the first character, i.e. (outptr != * inptr) tests whether the first character is valid in an identifier. * - * Currently this is only called with itype IIDENT, IUSER or ISEP. + * Currently called only with itype INAMESPC, IIDENT, IUSER or ISEP. */ /**/ mod_export char * itype_end(const char *ptr, int itype, int once) { + if (itype == INAMESPC) { + itype = IIDENT; + if (once == 0 && !isset(POSIXIDENTIFIERS)) { + /* Special case for names containing ".", ksh93 namespaces */ + char *t = itype_end(ptr + (*ptr == '.'), itype, 0); + if (t > ptr+1) { + if (*t == '.') + return itype_end(t+1, itype, 0); + else + return t; + } + } + } + #ifdef MULTIBYTE_SUPPORT if (isset(MULTIBYTE) && (itype != IIDENT || !isset(POSIXIDENTIFIERS))) { diff --git a/Src/zsh.h b/Src/zsh.h index 96b4b06bd..0de1f7afb 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1963,6 +1963,8 @@ struct tieddata { */ #define SCANPM_CHECKING (1<<10) /* Check if set, no need to create */ #define SCANPM_NOEXEC (1<<11) /* No command substitutions, etc. */ +#define SCANPM_NONAMESPC (1<<12) /* namespace syntax not allowed */ + /* "$foo[@]"-style substitution * Only sign bit is significant */ diff --git a/Src/ztype.h b/Src/ztype.h index 8757fc733..4675f73a9 100644 --- a/Src/ztype.h +++ b/Src/ztype.h @@ -43,6 +43,7 @@ #define IWSEP (1 << 13) #define INULL (1 << 14) #define IPATTERN (1 << 15) +#define INAMESPC (1 << 16) #define zistype(X,Y) (typtab[(unsigned char) (X)] & Y) #define idigit(X) zistype(X,IDIGIT) #define ialnum(X) zistype(X,IALNUM) -- cgit v1.2.3 From 4bc1f6e0d2b60976b9c0412d72097ee1c812139b Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 5 Mar 2023 14:06:25 -0800 Subject: 51484: Extend named reference handling for special parameters, improve doc. --- ChangeLog | 3 ++ Doc/Zsh/builtins.yo | 2 + Src/params.c | 108 +++++++++++++++++++++++++++++++--------------------- 3 files changed, 69 insertions(+), 44 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 38a0395d6..30e9850e5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-03-05 Bart Schaefer + * 51484: Src/builtins.yo Src/params.c: Extend named reference + handling for special parameters, improve doc. + * 51483: Src/Zle/compcore.c, Src/Zle/zle_tricky.c, Src/lex.c, Src/params.c, Src/subst.c, Src/utils.c, Src/zsh.h, Src/ztype.h: Enable assignment and expansion of parameters with ksh-like diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 92917c06c..5393cb149 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -1924,6 +1924,8 @@ redef(SPACES)(0)(tt(ifztexi(NOTRANS(@ @ @ @ @ @ @ @ ))ifnztexi( ))) xitem(tt(typeset )[ {tt(PLUS())|tt(-)}tt(AHUaghlmrtux) ] \ [ {tt(PLUS())|tt(-)}tt(EFLRZip) [ var(n) ] ]) xitem(SPACES()[ tt(+) ] [ var(name)[tt(=)var(value)] ... ]) +xitem(tt(typeset )[ {tt(PLUS())|tt(-)}tt(n) ] \ +[ tt(-gr) ] [ var(name)[tt(=)var(value)] ... ]) xitem(tt(typeset )tt(-T) [ {tt(PLUS())|tt(-)}tt(Uglrux) ] [ {tt(PLUS())|tt(-)}tt(LRZp) [ var(n) ] ]) xitem(SPACES()[ tt(+) | var(SCALAR)[tt(=)var(value)] var(array)[tt(=LPAR())var(value) ...tt(RPAR())] [ var(sep) ] ]) item(tt(typeset) tt(-f) [ {tt(PLUS())|tt(-)}tt(TUkmtuz) ] [ tt(+) ] [ var(name) ... ])( diff --git a/Src/params.c b/Src/params.c index d3b6a7d43..c9f4b3017 100644 --- a/Src/params.c +++ b/Src/params.c @@ -475,6 +475,15 @@ static initparam argvparam_pm = IPDEF9("", &pparams, NULL, \ ((V) && (!(V)->pm || ((V)->pm->node.flags & PM_UNSET) || \ !(V)->pm->node.nam || !*(V)->pm->node.nam)) +/* + * For named references. Simple named references are just like scalars + * for efficiency, but special named references need get/set functions. + */ +#define GETREFNAME(PM) (((PM)->node.flags & PM_SPECIAL) ? \ + (PM)->gsu.s->getfn(PM) : (PM)->u.str) +#define SETREFNAME(PM,S) (((PM)->node.flags & PM_SPECIAL) ? \ + (PM)->gsu.s->setfn(PM,(S)) : ((PM)->u.str = (S))) + static Param argvparam; /* "parameter table" - hash table containing the parameters @@ -520,7 +529,7 @@ getparamnode(HashTable ht, const char *nam) HashNode hn = gethashnode2(ht, nam); Param pm = (Param) hn; - if (pm && pm->u.str && (pm->node.flags & PM_AUTOLOAD)) { + if (pm && (pm->node.flags & PM_AUTOLOAD) && pm->u.str) { char *mn = dupstring(pm->u.str); (void)ensurefeature(mn, "p:", (pm->node.flags & PM_AUTOALL) ? NULL : @@ -1002,12 +1011,13 @@ createparam(char *name, int flags) struct asgment stop; stop.flags = PM_NAMEREF | (flags & PM_LOCAL); stop.name = oldpm->node.nam; - stop.value.scalar = oldpm->u.str; + stop.value.scalar = GETREFNAME(oldpm); lastpm = (Param)resolve_nameref(oldpm, &stop); if (lastpm) { if (lastpm->node.flags & PM_NAMEREF) { - if (lastpm->u.str && *(lastpm->u.str)) { - name = lastpm->u.str; + char *refname = GETREFNAME(lastpm); + if (refname && *refname) { + name = refname; oldpm = NULL; } else { if (!(lastpm->node.flags & PM_READONLY)) @@ -2145,25 +2155,28 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) memset(v, 0, sizeof(*v)); else v = (Value) hcalloc(sizeof *v); - if ((pm->node.flags & PM_NAMEREF) && pm->u.str && *(pm->u.str)) { - /* only happens for namerefs pointing to array elements */ - char *ref = dupstring(pm->u.str); - char *ss = pm->width ? ref + pm->width : NULL; - if (ss) { - sav = *ss; - *ss = 0; + if (pm->node.flags & PM_NAMEREF) { + char *refname = GETREFNAME(pm); + if (refname && *refname) { + /* only happens for namerefs pointing to array elements */ + char *ref = dupstring(refname); + char *ss = pm->width ? ref + pm->width : NULL; + if (ss) { + sav = *ss; + *ss = 0; + } + Param p1 = (Param)gethashnode2(paramtab, ref); + if (!(p1 && (pm = upscope(p1, pm->base))) || + ((pm->node.flags & PM_UNSET) && + !(pm->node.flags & PM_DECLARED))) + return NULL; + if (ss) { + flags |= SCANPM_NOEXEC; + *ss = sav; + s = dyncat(ss,*pptr); + } else + s = *pptr; } - Param p1 = (Param)gethashnode2(paramtab, ref); - if (!(p1 && (pm = upscope(p1, pm->base))) || - ((pm->node.flags & PM_UNSET) && - !(pm->node.flags & PM_DECLARED))) - return NULL; - if (ss) { - flags |= SCANPM_NOEXEC; - *ss = sav; - s = dyncat(ss,*pptr); - } else - s = *pptr; } if (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) { /* Overload v->isarr as the flag bits for hashed arrays. */ @@ -3648,7 +3661,7 @@ mod_export Param setiparam_no_convert(char *s, zlong val) { /* - * If the target is already an integer, thisgets converted + * If the target is already an integer, this gets converted * back. Low technology rules. */ char buf[BDIGBUFSIZE]; @@ -6115,22 +6128,23 @@ resolve_nameref(Param pm, const Asgment stop) const char *seek = stop ? stop->value.scalar : NULL; if (pm && (pm->node.flags & PM_NAMEREF)) { - if (pm && (pm->node.flags & (PM_UNSET|PM_TAGGED))) { + char *refname = GETREFNAME(pm); + if (pm->node.flags & (PM_UNSET|PM_TAGGED)) { /* Semaphore with createparam() */ pm->node.flags &= ~PM_UNSET; if (pm->node.flags & PM_NEWREF) /* See setloopvar() */ return NULL; - if (pm->u.str && *(pm->u.str) && (pm->node.flags & PM_TAGGED)) + if (refname && *refname && (pm->node.flags & PM_TAGGED)) pm->node.flags |= PM_SELFREF; /* See setscope() */ return (HashNode) pm; - } else if (pm->u.str) { + } else if (refname) { if ((pm->node.flags & PM_TAGGED) || - (stop && strcmp(pm->u.str, stop->name) == 0)) { - /* zwarnnam(pm->u.str, "invalid self reference"); */ + (stop && strcmp(refname, stop->name) == 0)) { + /* zwarnnam(refname, "invalid self reference"); */ return stop ? (HashNode)pm : NULL; } - if (*(pm->u.str)) - seek = pm->u.str; + if (*refname) + seek = refname; } } else if (pm && !(stop && (stop->flags & PM_NAMEREF))) @@ -6180,8 +6194,13 @@ setloopvar(char *name, char *value) Param pm = (Param) gethashnode2(realparamtab, name); if (pm && (pm->node.flags & PM_NAMEREF)) { + if (pm->node.flags & PM_READONLY) { + /* Bash error is: "%s: readonly variable" */ + zerr("read-only reference: %s", pm->node.nam); + return; + } pm->base = pm->width = 0; - pm->u.str = ztrdup(value); + SETREFNAME(pm, ztrdup(value)); pm->node.flags &= ~PM_UNSET; pm->node.flags |= PM_NEWREF; setscope(pm); @@ -6197,7 +6216,8 @@ setscope(Param pm) if (pm->node.flags & PM_NAMEREF) { Param basepm; struct asgment stop; - char *t = pm->u.str ? itype_end(pm->u.str, INAMESPC, 0) : NULL; + char *refname = GETREFNAME(pm); + char *t = refname ? itype_end(refname, INAMESPC, 0) : NULL; /* Temporarily change nameref to array parameter itself */ if (t && *t == '[') @@ -6211,7 +6231,7 @@ setscope(Param pm) stop.flags |= PM_LOCAL; basepm = (Param)resolve_nameref(pm, &stop); if (t) { - pm->width = t - pm->u.str; + pm->width = t - refname; *t = '['; } if (basepm) { @@ -6220,23 +6240,23 @@ setscope(Param pm) if (pm->node.flags & PM_SELFREF) { /* Loop signalled by resolve_nameref() */ if (upscope(pm, pm->base) == pm) { - zerr("%s: invalid self reference", pm->u.str); + zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); return; } pm->node.flags &= ~PM_SELFREF; } else if (pm->base == pm->level) { - if (pm->u.str && *(pm->u.str) && - strcmp(pm->node.nam, pm->u.str) == 0) { - zerr("%s: invalid self reference", pm->u.str); + if (refname && *refname && + strcmp(pm->node.nam, refname) == 0) { + zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); return; } } - } else if (basepm->u.str) { + } else if ((t = GETREFNAME(basepm))) { if (basepm->base <= basepm->level && - strcmp(pm->node.nam, basepm->u.str) == 0) { - zerr("%s: invalid self reference", pm->u.str); + strcmp(pm->node.nam, t) == 0) { + zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); return; } @@ -6251,11 +6271,11 @@ setscope(Param pm) unsetparam_pm(pm, 0, 1); } else if (isset(WARNNESTEDVAR)) zwarn("reference %s in enclosing scope set to local variable %s", - pm->node.nam, pm->u.str); + pm->node.nam, refname); } - if (pm->u.str && upscope(pm, pm->base) == pm && - strcmp(pm->node.nam, pm->u.str) == 0) { - zerr("%s: invalid self reference", pm->u.str); + if (refname && upscope(pm, pm->base) == pm && + strcmp(pm->node.nam, refname) == 0) { + zerr("%s: invalid self reference", refname); unsetparam_pm(pm, 0, 1); } } -- cgit v1.2.3 From ea0bd72dd84a783355969e0a9a1584ce7b1ea08b Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 5 Mar 2023 14:16:31 -0800 Subject: 51485: module for several ksh93 features, mostly enabled only in ksh emulation. --- ChangeLog | 4 + Doc/Makefile.in | 2 +- Doc/Zsh/mod_ksh93.yo | 211 ++++++++++++++++++++++++++++++++++++++++ Src/Modules/ksh93.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++ Src/Modules/ksh93.mdd | 8 ++ Src/utils.c | 2 +- 6 files changed, 490 insertions(+), 2 deletions(-) create mode 100644 Doc/Zsh/mod_ksh93.yo create mode 100644 Src/Modules/ksh93.c create mode 100644 Src/Modules/ksh93.mdd (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 30e9850e5..a6a1b2d8c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-03-05 Bart Schaefer + * 51485: Doc/Makefile.in, Doc/Zsh/mod_ksh93.yo, Src/utils.c, + Src/Modules/ksh93.c, Src/Modules/ksh93.mdd: module for several + ksh93 features, mostly enabled only in ksh emulation. + * 51484: Src/builtins.yo Src/params.c: Extend named reference handling for special parameters, improve doc. diff --git a/Doc/Makefile.in b/Doc/Makefile.in index 23e5fc7e2..136b080d6 100644 --- a/Doc/Makefile.in +++ b/Doc/Makefile.in @@ -63,7 +63,7 @@ Zsh/mod_compctl.yo Zsh/mod_complete.yo Zsh/mod_complist.yo \ Zsh/mod_computil.yo Zsh/mod_curses.yo \ Zsh/mod_datetime.yo Zsh/mod_db_gdbm.yo Zsh/mod_deltochar.yo \ Zsh/mod_example.yo Zsh/mod_files.yo Zsh/mod_langinfo.yo \ -Zsh/mod_mapfile.yo Zsh/mod_mathfunc.yo \ +Zsh/mod_ksh93.yo Zsh/mod_mapfile.yo Zsh/mod_mathfunc.yo \ Zsh/mod_nearcolor.yo Zsh/mod_newuser.yo \ Zsh/mod_parameter.yo Zsh/mod_pcre.yo Zsh/mod_private.yo \ Zsh/mod_regex.yo Zsh/mod_sched.yo Zsh/mod_socket.yo \ diff --git a/Doc/Zsh/mod_ksh93.yo b/Doc/Zsh/mod_ksh93.yo new file mode 100644 index 000000000..3ae644bd1 --- /dev/null +++ b/Doc/Zsh/mod_ksh93.yo @@ -0,0 +1,211 @@ +COMMENT(!MOD!zsh/ksh93 +Extended ksh93 compatibility for "emulate ksh" +!MOD!) +cindex(ksh93) +The tt(zsh/ksh93) module provides one builtin and several parameters to +improve compatibility with ksh93. As of this writing, several ksh93 +features are still missing. + +subsect(Ksh Builtins) +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)])( +Equivalent to tt(typeset -n )var(pname)tt(=)var(rname) + +However, tt(nameref) is a builtin command rather than a reserved word, +so when var(rname) uses subscript syntax it must be quoted against +globbing. Subscripts in referenced parameters are not supported in +ksh93, so this is not a significant compatibility issue. +) +enditem() + +subsect(Ksh Parameters) +cindex(parameters, ksh) +Parameters supplied by this module that are marked with `' below are +available only in ksh emulation. + +startitem() +vindex(.sh.command) +item(tt(.sh.command))( +A named reference to `tt(ZSH_DEBUG_CMD)' +) +vindex(.sh.edchar) +item(tt(.sh.edchar) )( +In a ZLE widget, equivalent to the `tt(KEYS)' special parameter. In a +future release, assignments to this parameter will affect the input +stream seen by ZLE. +) +vindex(.sh.edcol) +item(tt(.sh.edcol))( +A named reference to the ZLE special parameter `tt(CURSOR)'. +) +vindex(.sh.edmode) +item(tt(.sh.edmode) )( +In a ZLE widget, this parameter has the value tt(ESC) (tt($'\e')) if the +`tt(main)' keymap is selected, and the empty string otherwise. This is +intended for use with vi-mode key bindings (`tt(bindkey -v)'). In a +future revision, assigning `tt(.sh.edchar=${.sh.edmode})' is expected +to initiate `tt(vicmd)' mode when `tt(viins)' is active, and do +nothing when `tt(vicmd)' is already active. +) +vindex(.sh.edtext) +item(tt(.sh.edtext))( +A named reference to the `tt(BUFFER)' special ZLE parameter. +) +vindex(.sh.file) +item(tt(.sh.file))( +A named reference to the `tt(ZSH_SCRIPT)' parameter. +) +vindex(.sh.fun) +item(tt(.sh.fun) )( +In a shell function, the function's name. Usually the same as `tt($0)', +but not affected by tt(POSIX_ARGZERO) or tt(FUNCTION_ARGZERO) options. +) +vindex(.sh.level) +item(tt(.sh.level) )( +In a shell function, initially set to the depth of the call stack. This +may be assigned to change the apparent depth. However, such assignment +does not affect the scope of local parameters. +) +vindex(.sh.lineno) +item(tt(.sh.lineno))( +A named reference to the `tt(LINENO)' special parameter. +) +vindex(.sh.match) +item(tt(.sh.match))( +An array equivalent to the `tt(match)' parameter as assigned by the +`tt(LPAR()#b)tt(RPAR())' extended globbing flag. When the +tt(KSH_ARRAYS) option is set, `tt(${.sh.match[0]})' gives the value of +the `tt(MATCH)' parameter as set by the `tt(LPAR()#m)tt(RPAR())' extended +globbing flag. Currently, the tt(EXTENDED_GLOB) option must be enabled +and those flags must be explicitly used in a pattern in order for these +values of `tt(.sh.match)' to be set. +) +vindex(.sh.name) +item(tt(.sh.name) )( +When the `tt(vared)' command is used, this parameter may be used in +user-defined ZLE widgets to get the name of the variable being edited. +A future release is expected to use this parameter in the implementation +of "discipline functions". +) +vindex(.sh.subscript) +item(tt(.sh.subscript) )( +When `tt(vared)' has been used on an array element, this parameter holds +the array index (either a number, or an associative array key). +) +vindex(.sh.subshell) +item(tt(.sh.subshell))( +A named reference to the `tt(ZSH_SUBSHELL)' parameter. +) +vindex(.sh.value) +item(tt(.sh.value) )( +In `tt(vared)' this is a named reference to the ZLE special `tt(BUFFER)'. +A future release is expected to use this parameter in the implementation +of "discipline functions". +) +vindex(.sh.version) +item(tt(.sh.version))( +A named reference to `tt(ZSH_PATCHLEVEL)'. +) +enditem() + +subsect(Future Compatibility) + +The following features of ksh93 are not currently supported but may be +available in a future release. + +startitem() +item(var(pathdir)tt(/.paths))( +Each directory var(pathdir) in the tt(PATH) parameter may contain a +text file `tt(.paths)' which may define additional directories to +be searched for function definitions (tt(FPATH)), external executables +(tt(PATH)), and plugin files (similar to tt(MODULE_PATH)). + +em(THIS FEATURE IS UNLIKELY EVER TO BE IMPLEMENTED.) +) +item(tt(builtin -f )var(file))( +Installs a new shell builtin command dynamically linked from var(file), +where var(file) is found by a path search and the base name of the file +is the name of the builtin to be added. + +Similar to `tt(zmodload -F zsh/)var(file)tt( +b:)var(file)'. + +em(THIS FEATURE IS UNLIKELY EVER TO BE IMPLEMENTED.) +) +item(tt(namespace )var(ident)tt( { )var(list)tt( }))( +This reserved word executes the current shell compound command +tt({ )var(list)tt( }), with the special behavior that all functions +and parameters `var(name)' declared within var(list) are implicitly +prefixed to become `tt(.)var(ident)tt(.)var(name)', and similarly any +reference to a function or parameter `var(name)' is searched for as +`tt(.)var(ident)tt(.)var(name)' before falling back to `var(name)'. + +em(THIS FEATURE IS NOT YET IMPLEMENTED.) +) +item(tt(.sh.math) )( +This parameter is more accurately considered a namespace. A function +defintion of the form +ifzman() +indent(tt(function .sh.math.)var(name)tt( )var(ident)tt( ... { )var(list)tt( })) + +is equivalent to the native zsh definition +ifzman() +example(tt(function )var(name)tt( {) +tt( local )var(ident)tt(=$1 ...) +tt( )var(list) +tt(}) +tt(functions -M )var(name)tt( 1 3)) +ifzman() +Up to 3 var(ident) arguments, interpreted as floating point numbers, +may be provided for a function defined with tt(.sh.math) in this way. +The names (but not definitions) of all such functions are available +via tt(${.sh.math[@]}). + +em(THIS FEATURE IS NOT YET IMPLEMENTED.) +) +item(tt(var(name)tt(.get)))( +A shell function having this name, if defined, is invoked whenever the +parameter tt(${)var(name)tt(}) is referenced, including by commands +such as `tt(typeset -p)'. If the special variable `tt(.sh.value)' is +assigned by the function, that value is substituted instead of the +true value of var(name). This does not change the value of var(name), +but there is no way to access the actual value without first removing +the function. + +Additionally, an explicit reference to tt(${)var(name)tt(.get}) +calls the function var(name)tt(.get) even if there is no parameter +`var(name)' and substitutes the standard output of the function. + +em(THIS FEATURE IS NOT YET IMPLEMENTED.) +) +xitem(tt(var(name)tt(.set))) +item(tt(var(name)tt(.append)))( +Shell functions having these names are invoked when the parameter +var(name) is assigned or (for array types) has a new field appended. +The function may change the result of the operation by assigning to +the `tt(.sh.value)' special parameter, or block the change by +unsetting `tt(.sh.value)'. + +Explicit reference to tt(${)var(name)tt(.set}) or tt(${)var(name)tt(.append}) +substitutes the standard output of the function. + +em(THIS FEATURE IS NOT YET IMPLEMENTED.) +) +item(tt(var(name)tt(.unset)))( +When a function of this name is defined, it is called whenever the +parameter var(name) would be unset. The function must explicitly +`tt(unset )var(name)', otherwise the variable remains set. + +em(THIS FEATURE IS NOT YET IMPLEMENTED.) +) +item(tt(${ )var(list)tt(;}))( +Note the space after the opening brace (tt({)). This executes var(list) +in the current shell and substitutes its standard output in the manner +of `tt($LPAR())var(list)tt(RPAR())' but without forking. + +em(THIS FEATURE IS NOT YET IMPLEMENTED.) +) +enditem() diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c new file mode 100644 index 000000000..9dc75c93c --- /dev/null +++ b/Src/Modules/ksh93.c @@ -0,0 +1,265 @@ +/* + * ksh93.c - support for more ksh93 features + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 2022 Barton E. Schaefer + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Barton E. Schaefer or the Zsh Development + * Group be liable to any party for direct, indirect, special, incidental, or + * consequential damages arising out of the use of this software and its + * documentation, even if Barton E. Schaefer and the Zsh + * Development Group have been advised of the possibility of such damage. + * + * Barton E. Schaefer and the Zsh Development Group + * specifically disclaim any warranties, including, but not limited to, the + * implied warranties of merchantability and fitness for a particular purpose. + * The software provided hereunder is on an "as is" basis, and + * Barton E. Schaefer and the Zsh Development Group have no + * obligation to provide maintenance, support, updates, enhancements, or + * modifications. + * + */ + +#include "ksh93.mdh" +#include "ksh93.pro" + +/* Implementing "namespace" requires creating a new keword. Hrm. */ + +/* + * Standard module configuration/linkage + */ + +static struct builtin bintab[] = { + BUILTIN("nameref", BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "gr", "n") +}; + +#include "zsh.mdh" + +static void +edcharsetfn(Param pm, char *x) +{ + /* + * To make this work like ksh, we must intercept $KEYS before the widget + * is looked up, so that changing the key sequence causes a different + * widget to be substituted. Somewhat similar to "bindkey -s". + * + * Ksh93 adds SIGKEYBD to the trap list for this purpose. + */ + ; +} + +static char ** +matchgetfn(Param pm) +{ + char **zsh_match = getaparam("match"); + + /* For this to work accurately, ksh emulation should always imply + * that the (#m) and (#b) extendedglob operators are enabled. + * + * When we have a 0th element (ksharrays), it is $MATCH. Elements + * 1st and larger mirror the $match array. + */ + + if (pm->u.arr) + freearray(pm->u.arr); + if (zsh_match && *zsh_match) { + if (isset(KSHARRAYS)) { + char **ap = + (char **) zalloc(sizeof(char *) * (arrlen(zsh_match)+1)); + pm->u.arr = ap; + *ap++ = ztrdup(getsparam("MATCH")); + while (*zsh_match) + *ap = ztrdup(*zsh_match++); + } else + pm->u.arr = zarrdup(zsh_match); + } else if (isset(KSHARRAYS)) { + pm->u.arr = mkarray(ztrdup(getsparam("MATCH"))); + } else + pm->u.arr = NULL; + + return arrgetfn(pm); +} + +static const struct gsu_scalar constant_gsu = + { strgetfn, NULL, nullunsetfn }; + +static const struct gsu_scalar sh_edchar_gsu = + { strvargetfn, edcharsetfn, nullunsetfn }; +static const struct gsu_scalar sh_edmode_gsu = + { strgetfn, nullstrsetfn, nullunsetfn }; +static const struct gsu_array sh_match_gsu = + { matchgetfn, arrsetfn, stdunsetfn }; +static const struct gsu_scalar sh_name_gsu = + { strvargetfn, nullstrsetfn, nullunsetfn }; +static const struct gsu_scalar sh_subscript_gsu = + { strvargetfn, nullstrsetfn, nullunsetfn }; + +static char *sh_name; +static char *sh_subscript; +static char *sh_edchar; +static char sh_edmode[2]; + +/* + * Some parameters listed here do not appear in ksh93.mdd autofeatures + * because they are only instantiated by ksh93_wrapper() below. This + * obviously includes those commented out here. + */ +static struct paramdef partab[] = { + PARAMDEF(".sh.command", PM_NAMEREF|PM_READONLY, "ZSH_DEBUG_CMD", &constant_gsu), + PARAMDEF(".sh.edchar", PM_SCALAR|PM_SPECIAL, &sh_edchar, &sh_edchar_gsu), + PARAMDEF(".sh.edcol", PM_NAMEREF|PM_READONLY, "CURSOR", &constant_gsu), + PARAMDEF(".sh.edmode", PM_SCALAR|PM_READONLY|PM_SPECIAL, &sh_edmode, &sh_edmode_gsu), + PARAMDEF(".sh.edtext", PM_NAMEREF|PM_READONLY, "BUFFER", &constant_gsu), + PARAMDEF(".sh.file", PM_NAMEREF|PM_READONLY, "ZSH_SCRIPT", &constant_gsu), + /* PARAMDEF(".sh.fun", PM_SCALAR|PM_UNSET, NULL, &constant_gsu), */ + /* PARAMDEF(".sh.level", PM_INTEGER|PM_UNSET, NULL, &constant_gsu), */ + PARAMDEF(".sh.lineno", PM_NAMEREF|PM_READONLY, "LINENO", &constant_gsu), + PARAMDEF(".sh.match", PM_ARRAY|PM_READONLY, NULL, &sh_match_gsu), + PARAMDEF(".sh.name", PM_SCALAR|PM_READONLY|PM_SPECIAL, &sh_name, &sh_name_gsu), + PARAMDEF(".sh.subscript", PM_SCALAR|PM_READONLY|PM_SPECIAL, &sh_subscript, &sh_subscript_gsu), + PARAMDEF(".sh.subshell", PM_NAMEREF|PM_READONLY, "ZSH_SUBSHELL", &constant_gsu), + /* SPECIALPMDEF(".sh.value", 0, NULL, NULL, NULL), */ + PARAMDEF(".sh.version", PM_NAMEREF|PM_READONLY, "ZSH_PATCHLEVEL", &constant_gsu) +}; + +static struct features module_features = { + bintab, sizeof(bintab)/sizeof(*bintab), + NULL, 0, + NULL, 0, + partab, sizeof(partab)/sizeof(*partab), + 0 +}; + +/**/ +static int +ksh93_wrapper(Eprog prog, FuncWrap w, char *name) +{ + Funcstack f; + Param pm; + zlong num = funcstack->prev ? getiparam(".sh.level") : 0; + + if (!EMULATION(EMULATE_KSH)) + return 1; + + if (num == 0) + for (f = funcstack; f; f = f->prev, num++); + else + num++; + + queue_signals(); + ++locallevel; /* Make these local */ + if ((pm = createparam(".sh.level", PM_LOCAL|PM_UNSET))) { + pm->level = locallevel; /* Why is this necessary? */ + setiparam(".sh.level", num); + } + if ((pm = createparam(".sh.fun", PM_LOCAL|PM_UNSET))) { + pm->level = locallevel; + setsparam(".sh.fun", ztrdup(name)); + pm->node.flags |= PM_READONLY; + } + if (zleactive) { + extern mod_import_variable char *curkeymapname; /* XXX */ + extern mod_import_variable char *varedarg; /* XXX */ + /* How to distinguish emacs bindings? */ + if (curkeymapname && strcmp(curkeymapname, "main") == 0) + strcpy(sh_edmode, "\e"); + else + strcpy(sh_edmode, ""); + if (!sh_edchar) + sh_edchar = dupstring(getsparam("KEYS")); + if (varedarg) { + char *ie = itype_end((sh_name = dupstring(varedarg)), INAMESPC, 0); + if (ie && *ie) { + *ie++ = '\0'; + /* Assume bin_vared has validated subscript */ + sh_subscript = dupstring(ie); + ie = sh_subscript + strlen(sh_subscript); + *--ie = '\0'; + } else + sh_subscript = NULL; + if ((pm = createparam(".sh.value", PM_LOCAL|PM_NAMEREF|PM_UNSET))) { + pm->level = locallevel; + setloopvar(".sh.value", "BUFFER"); /* Hack */ + } + } else + sh_name = sh_subscript = NULL; + } else { + sh_edchar = sh_name = sh_subscript = NULL; + strcpy(sh_edmode, ""); + /* TODO: + * - disciplines + * - special handling of .sh.value in math + */ + } + --locallevel; + unqueue_signals(); + + return 1; +} + +static struct funcwrap wrapper[] = { + WRAPDEF(ksh93_wrapper), +}; + +/**/ +int +setup_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +features_(Module m, char ***features) +{ + *features = featuresarray(m, &module_features); + return 0; +} + +/**/ +int +enables_(Module m, int **enables) +{ + return handlefeatures(m, &module_features, enables); +} + +/**/ +int +boot_(Module m) +{ + return addwrapper(m, wrapper); +} + +/**/ +int +cleanup_(Module m) +{ + struct paramdef *p; + + deletewrapper(m, wrapper); + + /* Clean up namerefs, otherwise deleteparamdef() is confused */ + for (p = partab; p < partab + sizeof(partab)/sizeof(*partab); ++p) { + if (p->flags & PM_NAMEREF) { + HashNode hn = gethashnode2(paramtab, p->name); + if (hn) + ((Param)hn)->node.flags &= ~PM_NAMEREF; + } + } + return setfeatureenables(m, &module_features, NULL); +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + return 0; +} diff --git a/Src/Modules/ksh93.mdd b/Src/Modules/ksh93.mdd new file mode 100644 index 000000000..2759884a0 --- /dev/null +++ b/Src/Modules/ksh93.mdd @@ -0,0 +1,8 @@ +name=zsh/ksh93 +link=either +load=yes + +autofeatures="b:nameref" +autofeatures_emu="b:nameref p:.sh.command p:.sh.edcol p:.sh.edtext p:.sh.file p:.sh.lineno p:.sh.match p:.sh.subshell p:.sh.version" + +objects="ksh93.o" diff --git a/Src/utils.c b/Src/utils.c index 1393ecb13..8ce9a175d 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -4319,7 +4319,7 @@ itype_end(const char *ptr, int itype, int once) { if (itype == INAMESPC) { itype = IIDENT; - if (once == 0 && !isset(POSIXIDENTIFIERS)) { + if (once == 0 && (!isset(POSIXIDENTIFIERS) || EMULATION(EMULATE_KSH))) { /* Special case for names containing ".", ksh93 namespaces */ char *t = itype_end(ptr + (*ptr == '.'), itype, 0); if (t > ptr+1) { -- cgit v1.2.3 From ac1bf482ba43d4bf68708625e56a42860541d851 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 6 Mar 2023 14:54:28 -0800 Subject: 51524: dependency on zsh/zle for linkage --- ChangeLog | 4 ++++ Src/Modules/ksh93.mdd | 2 ++ 2 files changed, 6 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 301f048cf..618475dd4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-03-06 Bart Schaefer + + * 51524: Src/Modules/ksh93.mdd: dependency on zsh/zle for linkage + 2023-03-05 Bart Schaefer * 51486: Etc/zsh-development-guide: clarify module section diff --git a/Src/Modules/ksh93.mdd b/Src/Modules/ksh93.mdd index 2759884a0..85e35e9cb 100644 --- a/Src/Modules/ksh93.mdd +++ b/Src/Modules/ksh93.mdd @@ -5,4 +5,6 @@ load=yes autofeatures="b:nameref" autofeatures_emu="b:nameref p:.sh.command p:.sh.edcol p:.sh.edtext p:.sh.file p:.sh.lineno p:.sh.match p:.sh.subshell p:.sh.version" +moddeps="zsh/zle" + objects="ksh93.o" -- cgit v1.2.3 From 0562be0af8127bb728774de47e4e8851461bd8e2 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 6 Mar 2023 19:54:48 -0800 Subject: 51509 (+ fix typo): Add ${(!)name} for the referred-to name of a named reference Extend ${!name} in ksh emulation for same --- ChangeLog | 4 ++++ Src/params.c | 7 +++++-- Src/subst.c | 38 +++++++++++++++++++++++++++++++------- Src/zsh.h | 1 + 4 files changed, 41 insertions(+), 9 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 618475dd4..809466c0e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-03-06 Bart Schaefer + * 51509 (+ fix typo): Src/params.c, Src/subst.c, Src/zsh.h: Add + ${(!)name} for the referred-to parameter of a named reference, + and extend ${!name} in ksh emulation for same + * 51524: Src/Modules/ksh93.mdd: dependency on zsh/zle for linkage 2023-03-05 Bart Schaefer diff --git a/Src/params.c b/Src/params.c index c9f4b3017..85eaee609 100644 --- a/Src/params.c +++ b/Src/params.c @@ -2144,7 +2144,10 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) int isvarat; isvarat = (t[0] == '@' && !t[1]); - pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t); + if (flags & SCANPM_NONAMEREF) + pm = (Param) paramtab->getnode2(paramtab, *t == '0' ? "0" : t); + else + pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t); if (sav) *s = sav; *pptr = s; @@ -2155,7 +2158,7 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) memset(v, 0, sizeof(*v)); else v = (Value) hcalloc(sizeof *v); - if (pm->node.flags & PM_NAMEREF) { + if ((pm->node.flags & PM_NAMEREF) && !(flags & SCANPM_NONAMEREF)) { char *refname = GETREFNAME(pm); if (refname && *refname) { /* only happens for namerefs pointing to array elements */ diff --git a/Src/subst.c b/Src/subst.c index 7a4b433bc..974d6171e 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1818,14 +1818,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * Use for the (k) flag. Goes down into the parameter code, * sometimes. */ - char hkeys = 0; + int hkeys = 0; /* * Used for the (v) flag, ditto. Not quite sure why they're * separate, but the tradition seems to be that things only * get combined when that makes the result more obscure rather * than less. */ - char hvals = 0; + int hvals = 0; /* * Whether we had to evaluate a subexpression, i.e. an * internal ${...} or $(...) or plain $pm. We almost don't @@ -1870,8 +1870,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * these later on, too. */ c = *s; - if (itype_end(s, INAMESPC, 1) == s && *s != '#' && c != Pound && - !IS_DASH(c) && + if (itype_end(s, (c == Inbrace ? INAMESPC : IIDENT), 1) == s && + *s != '#' && c != Pound && !IS_DASH(c) && c != '!' && c != '$' && c != String && c != Qstring && c != '?' && c != Quest && c != '*' && c != Star && c != '@' && c != '{' && @@ -1891,15 +1891,30 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, s++; /* * In ksh emulation a leading `!' is a special flag working - * sort of like our (k). + * sort of like our (k). This is true only for arrays or + * associative arrays and only with subscripts [*] or [@], + * so zsh's implementation is approximate. For namerefs + * in ksh, ${!ref} substitues the parameter name at the + * end of any chain of references, rather than the value. + * * TODO: this is one of very few cases tied directly to * the emulation mode rather than an option. Since ksh * doesn't have parameter flags it might be neater to * handle this with the ^, =, ~ stuff, below. */ if ((c = *s) == '!' && s[1] != Outbrace && EMULATION(EMULATE_KSH)) { - hkeys = SCANPM_WANTKEYS; + hkeys = SCANPM_WANTKEYS|SCANPM_NONAMEREF; s++; + /* There's a slew of other special bash meanings of parameter + * references that start with "!": + * ${!name} == ${(P)name} (when name is not a nameref) + * ${!name*} == ${(k)parameters[(I)name*]} + * ${!name@} == ${(@k)parameters[(I)name*]} + * ${!name[*]} == ${(k)name} (but indexes of ordinary arrays, too) + * ${!name[@]} == ${(@k)name} (ditto, as noted above for ksh) + * + * See also workers/34390, workers/34397, workers/34408. + */ } else if (c == '(' || c == Inpar) { char *t, sav; int tt = 0; @@ -2154,10 +2169,19 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, escapes = 1; break; + case '!': + if ((hkeys|hvals) & ~SCANPM_NONAMEREF) + goto flagerr; + hkeys = SCANPM_NONAMEREF; + break; case 'k': + if (hkeys & ~SCANPM_WANTKEYS) + goto flagerr; hkeys = SCANPM_WANTKEYS; break; case 'v': + if (hvals & ~SCANPM_WANTVALS) + goto flagerr; hvals = SCANPM_WANTVALS; break; @@ -2308,7 +2332,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, /* * Look for special unparenthesised flags. * TODO: could make these able to appear inside parentheses, too, - * i.e. ${(^)...} etc. + * i.e. ${(^)...} etc., but ${(~)...} already has another meaning. */ for (;;) { if ((c = *s) == '^' || c == Hat) { diff --git a/Src/zsh.h b/Src/zsh.h index 0de1f7afb..f3a777045 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1964,6 +1964,7 @@ struct tieddata { #define SCANPM_CHECKING (1<<10) /* Check if set, no need to create */ #define SCANPM_NOEXEC (1<<11) /* No command substitutions, etc. */ #define SCANPM_NONAMESPC (1<<12) /* namespace syntax not allowed */ +#define SCANPM_NONAMEREF (1<<13) /* named references are not followed */ /* "$foo[@]"-style substitution * Only sign bit is significant -- cgit v1.2.3 From 8d009d35a9eeacb1bbe9399316d2649a47102014 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 6 Mar 2023 20:01:04 -0800 Subject: 51510: Skip namespaces in "set"/"typeset" output, add tests, fix bug --- ChangeLog | 5 +++ Src/builtin.c | 14 +++++-- Src/params.c | 4 ++ Src/utils.c | 8 ++-- Src/zsh.h | 1 + Test/K02parameter.ztst | 106 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 Test/K02parameter.ztst (limited to 'Src') 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 25dceb1dea4ee1c23dcc02f3f7f5c0bf345c492c Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 11 Mar 2023 13:20:21 -0800 Subject: 51557: Clarify availability of ksh-mode parameters, improve vi-mode detection. --- ChangeLog | 5 +++++ Doc/Zsh/mod_ksh93.yo | 8 +++++--- Src/Modules/ksh93.c | 5 +++-- 3 files changed, 13 insertions(+), 5 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index f15072191..cc2890b2f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-03-11 Bart Schaefer + + * 51557: Doc/zsh/mod_ksh93.yo, Src/Modules/ksh93.c: Clarify + availability of ksh-mode parameters, improve vi-mode detection. + 2023-03-07 Bart Schaefer * 51534: Util/printdefines: update for recent changes diff --git a/Doc/Zsh/mod_ksh93.yo b/Doc/Zsh/mod_ksh93.yo index d58b979b9..99dab385f 100644 --- a/Doc/Zsh/mod_ksh93.yo +++ b/Doc/Zsh/mod_ksh93.yo @@ -24,8 +24,10 @@ enditem() subsect(Ksh Parameters) cindex(parameters, ksh) -Parameters supplied by this module that are marked with `' below are -available only in ksh emulation. +Parameters supplied by this module that are marked with `' below +are available only when ksh emulation is active before entry to the +shell function, that is, `tt(emulate ksh)' locally to a function does +not populate these parameters for that function. startitem() vindex(.sh.command) @@ -45,7 +47,7 @@ A named reference to the ZLE special parameter `tt(CURSOR)'. vindex(.sh.edmode) item(tt(.sh.edmode) )( In a ZLE widget, this parameter has the value tt(ESC) (tt($'\e')) if the -`tt(main)' keymap is selected, and the empty string otherwise. This is +tt(VI) option is set and the `tt(main)' keymap is selected. This is intended for use with vi-mode key bindings (`tt(bindkey -v)'). In a future revision, assigning `tt(.sh.edchar=${.sh.edmode})' is expected to initiate `tt(vicmd)' mode when `tt(viins)' is active, and do diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c index 9dc75c93c..51999dd71 100644 --- a/Src/Modules/ksh93.c +++ b/Src/Modules/ksh93.c @@ -168,8 +168,9 @@ ksh93_wrapper(Eprog prog, FuncWrap w, char *name) if (zleactive) { extern mod_import_variable char *curkeymapname; /* XXX */ extern mod_import_variable char *varedarg; /* XXX */ - /* How to distinguish emacs bindings? */ - if (curkeymapname && strcmp(curkeymapname, "main") == 0) + /* bindkey -v forces VIMODE so this test is as good as any */ + if (curkeymapname && isset(VIMODE) && + strcmp(curkeymapname, "main") == 0) strcpy(sh_edmode, "\e"); else strcpy(sh_edmode, ""); -- 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') 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') 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 6d40d9b63b41188cc846918e19bbf2982b9305b9 Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Sun, 26 Mar 2023 10:26:25 +0200 Subject: 51602: Handle SIGIOT as an alias to SIGABRT if they are the same signal number --- ChangeLog | 5 +++++ Src/jobs.c | 5 +++++ Src/signames2.awk | 9 +++++---- 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 60642526b..9e8d45978 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-03-28 Mikael Magnusson + + * 51602: Src/jobs.c, Src/signames2.awk: Handle SIGIOT as an + alias to SIGABRT if they are the same signal number + 2023-03-27 Oliver Kiddle * Shohei YOSHIDA: 51589: Completion/Unix/Command/_nm: diff --git a/Src/jobs.c b/Src/jobs.c index 59ddd952e..15e2105eb 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -2646,6 +2646,11 @@ static const struct { { "IO", SIGIO }, #endif #endif +#if defined(SIGABRT) && defined(SIGIOT) +#if SIGABRT == SIGIOT + { "IOT", SIGIOT }, +#endif +#endif #if !defined(SIGERR) /* * If SIGERR is not defined by the operating system, use it diff --git a/Src/signames2.awk b/Src/signames2.awk index 4d15681d5..4d1557cd8 100644 --- a/Src/signames2.awk +++ b/Src/signames2.awk @@ -13,7 +13,8 @@ signam = substr(tmp[1], 4, 20) signum = tmp[2] if (signam == "CHLD" && sig[signum] == "CLD") sig[signum] = "" - if (signam == "POLL" && sig[signum] == "IO") sig[signum] = "" + if (signam == "POLL" && sig[signum] == "IO") sig[signum] = "" + if (signam == "ABRT" && sig[signum] == "IOT") sig[signum] = "" if (sig[signum] == "") { sig[signum] = signam if (0 + max < 0 + signum && signum < 60) @@ -33,9 +34,9 @@ if (signam == "IO") { msg[signum] = "i/o ready" } if (signam == "IOT") { msg[signum] = "IOT instruction" } if (signam == "KILL") { msg[signum] = "killed" } - if (signam == "LOST") { msg[signum] = "resource lost" } + if (signam == "LOST") { msg[signum] = "resource lost" } if (signam == "PIPE") { msg[signum] = "broken pipe" } - if (signam == "POLL") { msg[signum] = "pollable event occurred" } + if (signam == "POLL") { msg[signum] = "pollable event occurred" } if (signam == "PROF") { msg[signum] = "profile signal" } if (signam == "PWR") { msg[signum] = "power fail" } if (signam == "QUIT") { msg[signum] = "quit" } @@ -43,7 +44,7 @@ if (signam == "SYS") { msg[signum] = "invalid system call" } if (signam == "TERM") { msg[signum] = "terminated" } if (signam == "TRAP") { msg[signum] = "trace trap" } - if (signam == "URG") { msg[signum] = "urgent condition" } + if (signam == "URG") { msg[signum] = "urgent condition" } if (signam == "USR1") { msg[signum] = "user-defined signal 1" } if (signam == "USR2") { msg[signum] = "user-defined signal 2" } if (signam == "VTALRM") { msg[signum] = "virtual time alarm" } -- cgit v1.2.3 From 12e5db145b098a62ff11b88eea26f473ea2ecdcf Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Wed, 29 Mar 2023 10:52:05 +0100 Subject: 51608: Don't execute commands after "continue &&" Also ! continue || --- Src/exec.c | 4 ++-- Test/A01grammar.ztst | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/Src/exec.c b/Src/exec.c index 3330bbce8..4328975b9 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1491,7 +1491,7 @@ execlist(Estate state, int dont_change_job, int exiting) * we find a sublist followed by ORNEXT. */ if ((ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ? execsimple(state) : - execpline(state, code, Z_SYNC, 0)))) { + execpline(state, code, Z_SYNC, 0))) || breaks) { state->pc = next; code = *state->pc++; next = state->pc + WC_SUBLIST_SKIP(code); @@ -1524,7 +1524,7 @@ execlist(Estate state, int dont_change_job, int exiting) * we find a sublist followed by ANDNEXT. */ if (!(ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ? execsimple(state) : - execpline(state, code, Z_SYNC, 0)))) { + execpline(state, code, Z_SYNC, 0))) || breaks) { state->pc = next; code = *state->pc++; next = state->pc + WC_SUBLIST_SKIP(code); diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst index b3aea1055..d57085798 100644 --- a/Test/A01grammar.ztst +++ b/Test/A01grammar.ztst @@ -982,3 +982,39 @@ F:its expectations. } fn 1:! does not affect return status of explicit return + + msg=unset + for x in 1 2 3 4 5; do + continue && msg=set && print Not executed + print Not executed, neither. + done + print $msg +0:continue causes immediate continuation +>unset + + msg=unset + () { + return && msg=set && print Not executed + print Not executed, not nor neither. + } + print $msg +0:return causes immediate return +>unset + + msg=unset + for x in 1 2 3 4 5; do + ! continue || msg=set && print Not executed + print Not executed, neither. + done + print $msg +0:! continue causes immediate continuation +>unset + + msg=unset + () { + ! return || msg=set && print Not executed + print Not executed, not nor neither. + } + print $msg +0:! return causes immediate return +>unset -- cgit v1.2.3 From c006d7609703afcfb2162c36d4f745125df45879 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Thu, 30 Mar 2023 14:58:07 +0900 Subject: 51604: %M in TIMEFMT should report in kilobytes --- ChangeLog | 5 +++++ Src/jobs.c | 14 +++++++------- configure.ac | 9 +++++++++ 3 files changed, 21 insertions(+), 7 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 9e8d45978..526ae5265 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-03-30 Jun-ichi Takimoto + + * 51604: Src/jobs.c, configure.ac: %M in TIMEFMT should report + in kilobytes + 2023-03-28 Mikael Magnusson * 51602: Src/jobs.c, Src/signames2.awk: Handle SIGIOT as an diff --git a/Src/jobs.c b/Src/jobs.c index 15e2105eb..4d7172550 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -888,8 +888,13 @@ printtime(struct timeval *real, child_times_t *ti, char *desc) break; #endif #ifdef HAVE_STRUCT_RUSAGE_RU_MAXRSS +#ifdef RU_MAXRSS_IS_IN_BYTES +# define MAXRSS_IN_KB(x) ((x)/1024) +#else +# define MAXRSS_IN_KB(x) (x) +#endif case 'M': - fprintf(stderr, "%ld", ti->ru_maxrss / 1024); + fprintf(stderr, "%ld", MAXRSS_IN_KB(ti->ru_maxrss)); break; #endif #ifdef HAVE_STRUCT_RUSAGE_RU_MAJFLT @@ -1036,7 +1041,7 @@ should_report_time(Job j) #ifdef HAVE_GETRUSAGE if (reportmemory >= 0 && - j->procs->ti.ru_maxrss / 1024 > reportmemory) + MAXRSS_IN_KB(j->procs->ti.ru_maxrss) > reportmemory) return 1; #endif @@ -2646,11 +2651,6 @@ static const struct { { "IO", SIGIO }, #endif #endif -#if defined(SIGABRT) && defined(SIGIOT) -#if SIGABRT == SIGIOT - { "IOT", SIGIOT }, -#endif -#endif #if !defined(SIGERR) /* * If SIGERR is not defined by the operating system, use it diff --git a/configure.ac b/configure.ac index f340d2993..e6ced85d9 100644 --- a/configure.ac +++ b/configure.ac @@ -1965,6 +1965,15 @@ if test x$ac_cv_func_getrusage = xyes; then #endif #include ]) fi +dnl On some OSes (only macOS?) ru_maxrss is in bytes (not in kilobytes). +dnl Solaris uses pages as the unit, but ru_maxrss is set to zero anyway. +AH_TEMPLATE(RU_MAXRSS_IS_IN_BYTES, +[Define to 1 if ru_maxrss in struct rusage is in bytes.]) +case "$host_os" in + darwin*) + AC_DEFINE(RU_MAXRSS_IS_IN_BYTES) + ;; +esac dnl -------------------------------------------- -- cgit v1.2.3 From b411dc570217f337540c018f82797f248ec81754 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Mon, 3 Apr 2023 16:04:31 +0900 Subject: 51597: fix 'vared -c var' when var is unset --- ChangeLog | 4 ++++ Src/Zle/zle_utils.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 526ae5265..851c7e3ce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-04-03 Jun-ichi Takimoto + + * 51597: Src/Zle/zle_utils.c: fix 'vared -c var' when var is unset + 2023-03-30 Jun-ichi Takimoto * 51604: Src/jobs.c, configure.ac: %M in TIMEFMT should report diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 454a877a9..51bb43339 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -512,12 +512,13 @@ stringaszleline(char *instr, int incs, int *outll, int *outsz, int *outcs) *outcs = outptr - outstr; *outll = outptr - outstr; } else { + *outstr = ZWC('\0'); *outll = 0; if (outcs) *outcs = 0; } #else - memcpy(outstr, instr, ll); + strcpy(outstr, instr); *outll = ll; if (outcs) *outcs = incs; -- cgit v1.2.3 From 98b4d4bdca15393d3cce9e072867ee6526de5d2e Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Sun, 9 Apr 2023 20:33:32 +0900 Subject: 51632: nmetafy $_ when exporting it to child --- ChangeLog | 4 ++++ Src/exec.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 851c7e3ce..1eebe2660 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-04-09 Jun-ichi Takimoto + + * 51632: Src/exec.c: unmetafy $_ when exporting it to child + 2023-04-03 Jun-ichi Takimoto * 51597: Src/Zle/zle_utils.c: fix 'vared -c var' when var is unset diff --git a/Src/exec.c b/Src/exec.c index 4328975b9..3b3d1235e 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -519,7 +519,7 @@ zexecve(char *pth, char **argv, char **newenvp) if (*pth == '/') strcpy(buf + 2, pth); else - sprintf(buf + 2, "%s/%s", pwd, pth); + sprintf(buf + 2, "%s/%s", unmeta(pwd), pth); zputenv(buf); #ifndef FD_CLOEXEC closedumps(); -- cgit v1.2.3 From 8a9aea907ac4844e2ee7348c4fa6417ae9873991 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Sun, 9 Apr 2023 20:44:58 +0900 Subject: 51631: initialize $_ by copying it from environment --- ChangeLog | 3 +++ Doc/Zsh/params.yo | 5 ++++- Src/init.c | 9 ++++++--- 3 files changed, 13 insertions(+), 4 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 1eebe2660..32fd0780d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-04-09 Jun-ichi Takimoto + * 51631: Doc/Zsh/params.yo, Src/init.c: initialize $_ by copying + it from environment + * 51632: Src/exec.c: unmetafy $_ when exporting it to child 2023-04-03 Jun-ichi Takimoto diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 528c27f93..2db4210eb 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -779,7 +779,10 @@ last pipeline. ) vindex(_) item(tt(_) )( -The last argument of the previous command. +Initially, if tt(_) exists in the environment, then this parameter is set to +its value. This value may be the full pathname of the current zsh +executable or the script command file. +Later, this parameter is set to the last argument of the previous command. Also, this parameter is set in the environment of every command executed to the full pathname of the command. ) diff --git a/Src/init.c b/Src/init.c index 68621a0ad..7e98af44c 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1084,9 +1084,12 @@ setupvals(char *cmd, char *runscript, char *zsh_name) ztrdup(DEFAULT_IFS_SH) : ztrdup(DEFAULT_IFS); wordchars = ztrdup(DEFAULT_WORDCHARS); postedit = ztrdup(""); - zunderscore = (char *) zalloc(underscorelen = 32); - underscoreused = 1; - *zunderscore = '\0'; + /* If _ is set in environment then initialize our $_ by copying it */ + zunderscore = getenv("_"); + zunderscore = zunderscore ? metafy(zunderscore, -1, META_DUP) : ztrdup(""); + underscoreused = strlen(zunderscore) + 1; + underscorelen = (underscoreused + 31) & ~31; + zunderscore = (char *)zrealloc(zunderscore, underscorelen); zoptarg = ztrdup(""); zoptind = 1; -- cgit v1.2.3 From e5f8cc99f524db8db7c7fbe5957609db63869d0e Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Tue, 11 Apr 2023 21:43:15 +0900 Subject: 51639: new parameter ZSH_EXEPATH (full path of zsh executable) The full pathname is obatined by a reliable method on macOS and systems that support procfs. But on other systems (FreeBSD, OpenBSD, ...) it is guessed from argv[0], PWD and PATH. --- ChangeLog | 6 +++ Doc/Zsh/params.yo | 4 ++ Src/init.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++- configure.ac | 19 +++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 32fd0780d..b2c847da5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2023-04-11 Jun-ichi Takimoto + + * 51639: Doc/Zsh/params.yo, Src/init.c, configure.ac: add new + parameter ZSH_EXEPATH that is set to the full pathname of the + executable file of the current zsh + 2023-04-09 Jun-ichi Takimoto * 51631: Doc/Zsh/params.yo, Src/init.c: initialize $_ by copying diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 2db4210eb..57d10b8bd 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -1112,6 +1112,10 @@ item(tt(ZSH_EXECUTION_STRING))( If the shell was started with the option tt(-c), this contains the argument passed to the option. Otherwise it is not set. ) +vindex(ZSH_EXEPATH) +item(tt(ZSH_EXEPATH))( +Full pathname of the executable file of the current zsh process. +) vindex(ZSH_NAME) item(tt(ZSH_NAME))( Expands to the basename of the command used to invoke this instance diff --git a/Src/init.c b/Src/init.c index 7e98af44c..ffb017e22 100644 --- a/Src/init.c +++ b/Src/init.c @@ -246,6 +246,9 @@ loop(int toplevel, int justonce) static int restricted; +/* original argv[0]. This is already metafied */ +static char *argv0; + /**/ static void parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr, @@ -257,7 +260,7 @@ parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr, if (**argv == '-') flags |= PARSEARGS_LOGIN; - argzero = posixzero = *argv++; + argv0 = argzero = posixzero = *argv++; SHIN = 0; /* @@ -893,6 +896,106 @@ init_term(void) return 1; } +/* + * Get (or guess) the absolute pathname of the current zsh exeutable. + * Try OS-specific method, and if it fails, guess the absolute pathname + * from argv0, pwd, and PATH. 'name' and 'cwd' are unmetefied versions of + * argv0 and pwd. + * Returns a zalloc()ed string (not metafied), or NULL if failed. + */ +#ifdef __APPLE__ +#include +#endif + +/**/ +static char * +getmypath(const char *name, const char *cwd) +{ + char *buf; + int namelen; + + if (!name) + return NULL; + if (*name == '-') + ++name; + if ((namelen = strlen(name)) == 0) + return NULL; +#if defined(__APPLE__) + { + uint32_t n = PATH_MAX; + int ret; + buf = (char *)zalloc(PATH_MAX); + if ((ret = _NSGetExecutablePath(buf, &n)) < 0) { + /* try again with increased buffer size */ + buf = (char *)zrealloc(buf, n); + ret = _NSGetExecutablePath(buf, &n); + } + if (ret == 0 && strlen(buf) > 0) + return buf; + else + free(buf); + } +#elif defined(PROC_SELF_EXE) + { + ssize_t n; + buf = (char *)zalloc(PATH_MAX); + n = readlink(PROC_SELF_EXE, buf, PATH_MAX); + if (n > 0 && n < PATH_MAX) { + buf[n] = '\0'; + return buf; + } + else + free(buf); + } +#endif + /* guess the absolute pathname of 'name' */ + if (name[namelen-1] == '/') /* name should not end with '/' */ + return NULL; + else if (name[0] == '/') { + /* name is already an absolute pathname */ + return ztrdup(name); + } + else if (strchr(name, '/')) { + /* relative path */ + if (!cwd) + return NULL; + buf = (char *)zalloc(strlen(cwd) + namelen + 2); + sprintf(buf, "%s/%s", cwd, name); + return buf; + } +#ifdef HAVE_REALPATH + else { + /* search each dir in PARH */ + const char *path, *sep; + char *real, *try; + int pathlen, dirlen; + + path = getenv("PATH"); + if (!path || (pathlen = strlen(path)) == 0) + return NULL; + /* for simplicity, allocate buf even if REALPATH_ACCEPTS_NULL is on */ + buf = (char *)zalloc(PATH_MAX); + try = (char *)zalloc(pathlen + namelen + 2); + do { + sep = strchr(path, ':'); + dirlen = sep ? sep - path : strlen(path); + strncpy(try, path, dirlen); + try[dirlen] = '/'; + try[dirlen+1] = '\0'; + strcat(try, name); + real = realpath(try, buf); + if (sep) + path = sep + 1; + } while (!real && sep); + free(try); + if (!real) + free(buf); + return real; /* this may be NULL */ + } +#endif + return NULL; +} + /* Initialize lots of global variables and hash tables */ /**/ @@ -1195,6 +1298,18 @@ setupvals(char *cmd, char *runscript, char *zsh_name) /* Colour sequences for outputting colours in prompts and zle */ set_default_colour_sequences(); + /* ZSH_EXEPATH */ + { + char *mypath, *exename, *cwd; + exename = unmetafy(ztrdup(argv0), NULL); + cwd = pwd ? unmetafy(ztrdup(pwd), NULL) : NULL; + mypath = getmypath(exename, cwd); + free(exename); + free(cwd); + if (mypath) { + setsparam("ZSH_EXEPATH", metafy(mypath, -1, META_REALLOC)); + } + } if (cmd) setsparam("ZSH_EXECUTION_STRING", ztrdup(cmd)); if (runscript) diff --git a/configure.ac b/configure.ac index e6ced85d9..d8a17791a 100644 --- a/configure.ac +++ b/configure.ac @@ -2011,6 +2011,25 @@ if test x$zsh_cv_sys_path_dev_fd != xno; then AC_DEFINE_UNQUOTED(PATH_DEV_FD, "$zsh_cv_sys_path_dev_fd") fi +dnl ---------------------------------------------------- +dnl CHECK FOR SYMLINK TO THE CURRENT EXECUTABLE IN /proc +dnl ---------------------------------------------------- +dnl Linux: /proc/self/exe +dnl NetBSD: /proc/curproc/exe (or /proc/self/exe, but not /proc/curproc/file) +dnl DragonFly: /proc/curproc/file +dnl Solaris: /proc/self/path/a.out +AH_TEMPLATE([PROC_SELF_EXE], +[Define to the path of the symlink to the current executable file.]) +AC_CACHE_CHECK(for symlink to the current executable in /proc, +zsh_cv_proc_self_exe, +[for zsh_cv_proc_self_exe in /proc/self/exe /proc/curproc/exe \ + /proc/curproc/file /proc/self/path/a.out no; do + test -L $zsh_cv_proc_self_exe && break +done]) +if test x$zsh_cv_proc_self_exe != xno; then + AC_DEFINE_UNQUOTED(PROC_SELF_EXE, "$zsh_cv_proc_self_exe") +fi + dnl --------------------------------- dnl CHECK FOR RFS SUPERROOT DIRECTORY dnl --------------------------------- -- cgit v1.2.3 From 8f5fe841a63fecdab4416578653549689f2c592f Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 17 Apr 2023 09:30:34 +0100 Subject: 51652: fix running of TRAPEXIT explicitly. This is a special case where TRAPEXIT is unset within a TRAPEXIT as it should never run in a nested context, so just save the function structure temporarily on the heap. --- ChangeLog | 5 +++++ Src/exec.c | 34 +++++++++++++++++++++++++++++++++- Test/C03traps.ztst | 11 +++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 904d207de..981fdacd3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-04-17 Peter Stephenson + + * 51652 (plus typo correction): Src/exec.c, Test/C03traps.ztst: + fix running of TRAPEXIT explicitly. + 2023-04-11 Jun-ichi Takimoto * 51639: Doc/Zsh/params.yo, Src/init.c, configure.ac: add new diff --git a/Src/exec.c b/Src/exec.c index 3b3d1235e..8f9d5a885 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -5779,12 +5779,25 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) char *name = shfunc->node.nam; int flags = shfunc->node.flags; char *fname = dupstring(name); - Eprog prog; + Eprog prog, marked_prog; static int oflags; static int funcdepth; Heap funcheap; queue_signals(); /* Lots of memory and global state changes coming */ + /* + * In case this is a special function such as a trap, mark it + * as in use right now, so it doesn't get freed early. The + * worst that can happen is this hangs around in memory a little + * longer than strictly needed. + * + * Classic example of this happening is running TRAPEXIT directly. + * + * Because the shell function's contents may change, we'll ensure + * we use a consistent structure for use / free. + */ + marked_prog = shfunc->funcdef; + useeprog(marked_prog); NEWHEAPS(funcheap) { /* @@ -5818,6 +5831,22 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) memcpy(funcsave->pipestats, pipestats, bytes); } + if (!strcmp(fname, "TRAPEXIT")) { + /* + * If we are executing TRAPEXIT directly, starttrapscope() + * will pull the rug out from under us to ensure the + * exit trap isn't run inside the function. We just need + * the information locally here, so copy it on the heap. + * + * The funcdef is separately handled by reference counting. + */ + Shfunc shcopy = (Shfunc)zhalloc(sizeof(struct shfunc)); + memcpy(shcopy, shfunc, sizeof(struct shfunc)); + shcopy->node.nam = dupstring(shfunc->node.nam); + shfunc = shcopy; + name = shfunc->node.nam; + } + starttrapscope(); startpatternscope(); @@ -5942,6 +5971,8 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) funcsave->fstack.filename = getshfuncfile(shfunc); prog = shfunc->funcdef; + DPUTS1(!prog->nref, "function definition %s has zero reference count", + (fname && *fname) ? fname : ""); if (prog->flags & EF_RUN) { Shfunc shf; @@ -6046,6 +6077,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) } } OLDHEAPS; + freeeprog(marked_prog); unqueue_signals(); /* diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst index e0b6afb5f..de57765a0 100644 --- a/Test/C03traps.ztst +++ b/Test/C03traps.ztst @@ -1083,6 +1083,17 @@ F:Must be tested with a top-level script rather than source or function >trap1 # As of 5.7.1-test-2, the output was "out1 fn1 trap1 fn2" (on separate lines). + TRAPEXIT() { echo This is TRAPEXIT; } + TRAPEXIT + TRAPEXIT + TRAPEXIT +0:No memory problems with explicit call to TRAPEXIT. +>This is TRAPEXIT +>This is TRAPEXIT +>This is TRAPEXIT +>This is TRAPEXIT +# Three explicit calls, one implicit call at function exit. + %clean rm -f TRAPEXIT -- cgit v1.2.3 From 858b8de3d70fe76a3637c281bc2c8e3a6d961a2c Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 22 Apr 2023 14:40:23 -0700 Subject: 51670: prevent possible underflow in gettext() --- ChangeLog | 4 ++++ Src/text.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 01167b0c3..92a95ffaa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-04-22 Bart Schaefer + + * 51670: Src/text.c: prevent possible underflow in gettext() + 2023-04-18 Jun-ichi Takimoto * 51663: Completion/Unix/Command/_ssh: fix a typo in 51582 diff --git a/Src/text.c b/Src/text.c index 56127c457..8b1bd96b6 100644 --- a/Src/text.c +++ b/Src/text.c @@ -335,7 +335,7 @@ getjobtext(Eprog prog, Wordcode c) tlim = tptr + JOBTEXTSIZE - 1; tjob = 1; gettext2(&s); - if (tptr[-1] == Meta) + if (tptr > jbuf && tptr[-1] == Meta) --tptr; *tptr = '\0'; freeeprog(prog); /* mark as unused */ -- cgit v1.2.3 From 8943b5e4505faec8d02e8535417491a87fc74d4e Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Thu, 11 May 2023 12:37:52 -0700 Subject: users/29070: clean up tokens in cmdstr before compctl completion --- ChangeLog | 3 +++ Src/Zle/zle_tricky.c | 2 ++ 2 files changed, 5 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index d91ca40b6..130a37b8e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-05-11 Bart Schaefer + * users/29070: Src/Zle/zle_tricky.c: clean up tokens in cmdstr + before attempting completion (compctl only) + * Jim : 51609: Doc/Zsh/mod_zselect.yo: fix reference to select(2) diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 690cf6efb..6ceb5d87f 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -1315,6 +1315,8 @@ get_comp_string(void) ins = (tok == REPEAT ? 2 : (tok != STRING && tok != TYPESET)); zsfree(cmdstr); cmdstr = ztrdup(tokstr); + untokenize(cmdstr); + remnulargs(cmdstr); cmdtok = tok; /* * If everything before is a redirection, or anything -- cgit v1.2.3 From b62e911341c8ec7446378b477c47da4256053dc0 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sat, 13 May 2023 00:53:32 +0200 Subject: 51723: migrate pcre module to pcre2 --- ChangeLog | 3 + Src/Modules/pcre.c | 223 +++++++++++++++++++++-------------------------------- Test/V07pcre.ztst | 13 +++- configure.ac | 20 +++-- 4 files changed, 110 insertions(+), 149 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index f5c77f801..285b73b2c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-05-13 Oliver Kiddle + * 51723: Src/Modules/pcre.c, Test/V07pcre.ztst, configure.ac: + migrate pcre module to pcre2 + * Felipe Contreras: 50612: Misc/vcs_info-examples: fix typo * github #98: Vidhan Bhatt: Completion/Darwin/Command/_shortcuts: diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c index 46875a59b..079ecc2c5 100644 --- a/Src/Modules/pcre.c +++ b/Src/Modules/pcre.c @@ -34,11 +34,11 @@ #define CPCRE_PLAIN 0 /**/ -#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC) -#include +#if defined(HAVE_PCRE2_COMPILE_8) && defined(HAVE_PCRE2_H) +#define PCRE2_CODE_UNIT_WIDTH 8 +#include -static pcre *pcre_pattern; -static pcre_extra *pcre_hints; +static pcre2_code *pcre_pattern; /**/ static int @@ -54,8 +54,8 @@ zpcre_utf8_enabled(void) return 0; if ((have_utf8_pcre == -1) && - (pcre_config(PCRE_CONFIG_UTF8, &have_utf8_pcre))) { - have_utf8_pcre = -2; /* erk, failed to ask */ + (pcre2_config(PCRE2_CONFIG_UNICODE, &have_utf8_pcre))) { + have_utf8_pcre = -2; /* erk, failed to ask */ } return (have_utf8_pcre == 1) && (!strcmp(nl_langinfo(CODESET), "UTF-8")); @@ -69,115 +69,87 @@ zpcre_utf8_enabled(void) static int bin_pcre_compile(char *nam, char **args, Options ops, UNUSED(int func)) { - int pcre_opts = 0, pcre_errptr, target_len; - const char *pcre_error; + uint32_t pcre_opts = 0; + int target_len; + int pcre_error; + PCRE2_SIZE pcre_offset; char *target; - if(OPT_ISSET(ops,'a')) pcre_opts |= PCRE_ANCHORED; - if(OPT_ISSET(ops,'i')) pcre_opts |= PCRE_CASELESS; - if(OPT_ISSET(ops,'m')) pcre_opts |= PCRE_MULTILINE; - if(OPT_ISSET(ops,'x')) pcre_opts |= PCRE_EXTENDED; - if(OPT_ISSET(ops,'s')) pcre_opts |= PCRE_DOTALL; + if (OPT_ISSET(ops, 'a')) pcre_opts |= PCRE2_ANCHORED; + if (OPT_ISSET(ops, 'i')) pcre_opts |= PCRE2_CASELESS; + if (OPT_ISSET(ops, 'm')) pcre_opts |= PCRE2_MULTILINE; + if (OPT_ISSET(ops, 'x')) pcre_opts |= PCRE2_EXTENDED; + if (OPT_ISSET(ops, 's')) pcre_opts |= PCRE2_DOTALL; if (zpcre_utf8_enabled()) - pcre_opts |= PCRE_UTF8; - -#ifdef HAVE_PCRE_STUDY - if (pcre_hints) -#ifdef PCRE_CONFIG_JIT - pcre_free_study(pcre_hints); -#else - pcre_free(pcre_hints); -#endif - pcre_hints = NULL; -#endif + pcre_opts |= PCRE2_UTF; if (pcre_pattern) - pcre_free(pcre_pattern); + pcre2_code_free(pcre_pattern); pcre_pattern = NULL; target = ztrdup(*args); unmetafy(target, &target_len); - if ((int)strlen(target) != target_len) { - zwarnnam(nam, "embedded NULs in PCRE pattern terminate pattern"); - } - - pcre_pattern = pcre_compile(target, pcre_opts, &pcre_error, &pcre_errptr, NULL); + pcre_pattern = pcre2_compile((PCRE2_SPTR) target, (PCRE2_SIZE) target_len, + pcre_opts, &pcre_error, &pcre_offset, NULL); free(target); if (pcre_pattern == NULL) { - zwarnnam(nam, "error in regex: %s", pcre_error); + PCRE2_UCHAR buffer[256]; + pcre2_get_error_message(pcre_error, buffer, sizeof(buffer)); + zwarnnam(nam, "error in regex: %s", buffer); return 1; } return 0; } -/**/ -#ifdef HAVE_PCRE_STUDY - /**/ static int bin_pcre_study(char *nam, UNUSED(char **args), UNUSED(Options ops), UNUSED(int func)) { - const char *pcre_error; - if (pcre_pattern == NULL) { zwarnnam(nam, "no pattern has been compiled for study"); return 1; } - - if (pcre_hints) -#ifdef PCRE_CONFIG_JIT - pcre_free_study(pcre_hints); -#else - pcre_free(pcre_hints); -#endif - pcre_hints = NULL; - pcre_hints = pcre_study(pcre_pattern, 0, &pcre_error); - if (pcre_error != NULL) - { - zwarnnam(nam, "error while studying regex: %s", pcre_error); - return 1; + int jit = 0; + if (!pcre2_config(PCRE2_CONFIG_JIT, &jit) && jit) { + if (pcre2_jit_compile(pcre_pattern, PCRE2_JIT_COMPLETE) < 0) { + zwarnnam(nam, "error while studying regex"); + return 1; + } } return 0; } -/**/ -#else /* !HAVE_PCRE_STUDY */ - -# define bin_pcre_study bin_notavail - -/**/ -#endif /* !HAVE_PCRE_STUDY */ - -/**/ static int -zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar, - char *substravar, int want_offset_pair, int matchedinarr, - int want_begin_end) +zpcre_get_substrings(char *arg, pcre2_match_data *mdata, int captured_count, + char *matchvar, char *substravar, int want_offset_pair, + int matchedinarr, int want_begin_end) { - char **captures, *match_all, **matches; + PCRE2_SIZE *ovec; + char *match_all, **matches; char offset_all[50]; int capture_start = 1; if (matchedinarr) { - /* bash-style captures[0] entire-matched string in the array */ + /* bash-style ovec[0] entire-matched string in the array */ capture_start = 0; } - /* captures[0] will be entire matched string, [1] first substring */ - if (!pcre_get_substring_list(arg, ovec, captured_count, (const char ***)&captures)) { - int nelem = arrlen(captures)-1; + /* ovec[0] will be entire matched string, [1] first substring */ + ovec = pcre2_get_ovector_pointer(mdata); + if (ovec) { + int nelem = captured_count - 1; /* Set to the offsets of the complete match */ if (want_offset_pair) { - sprintf(offset_all, "%d %d", ovec[0], ovec[1]); + sprintf(offset_all, "%ld %ld", ovec[0], ovec[1]); setsparam("ZPCRE_OP", ztrdup(offset_all)); } /* @@ -186,7 +158,7 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar, * ovec is length 2*(1+capture_list_length) */ if (matchvar) { - match_all = metafy(captures[0], ovec[1] - ovec[0], META_DUP); + match_all = metafy(arg + ovec[0], ovec[1] - ovec[0], META_DUP); setsparam(matchvar, match_all); } /* @@ -201,16 +173,12 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar, */ if (substravar && (!want_begin_end || nelem)) { - char **x, **y; + char **x; int vec_off, i; - y = &captures[capture_start]; matches = x = (char **) zalloc(sizeof(char *) * (captured_count+1-capture_start)); - for (i = capture_start; i < captured_count; i++, y++) { + for (i = capture_start; i < captured_count; i++) { vec_off = 2*i; - if (*y) - *x++ = metafy(*y, ovec[vec_off+1]-ovec[vec_off], META_DUP); - else - *x++ = NULL; + *x++ = metafy(arg + ovec[vec_off], ovec[vec_off+1]-ovec[vec_off], META_DUP); } *x = NULL; setaparam(substravar, matches); @@ -247,7 +215,8 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar, setiparam("MEND", offs + !isset(KSHARRAYS) - 1); if (nelem) { char **mbegin, **mend, **bptr, **eptr; - int i, *ipair; + int i; + size_t *ipair; bptr = mbegin = zalloc(sizeof(char*)*(nelem+1)); eptr = mend = zalloc(sizeof(char*)*(nelem+1)); @@ -287,8 +256,6 @@ zpcre_get_substrings(char *arg, int *ovec, int captured_count, char *matchvar, setaparam("mend", mend); } } - - pcre_free_substring_list((const char **)captures); } return 0; @@ -314,7 +281,8 @@ getposint(char *instr, char *nam) static int bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) { - int ret, capcount, *ovec, ovecsize, c; + int ret, c; + pcre2_match_data *pcre_mdata = NULL; char *matched_portion = NULL; char *plaintext = NULL; char *receptacle = NULL; @@ -344,36 +312,30 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) /* For the entire match, 'Return' the offset byte positions instead of the matched string */ if(OPT_ISSET(ops,'b')) want_offset_pair = 1; - if ((ret = pcre_fullinfo(pcre_pattern, pcre_hints, PCRE_INFO_CAPTURECOUNT, &capcount))) - { - zwarnnam(nam, "error %d in fullinfo", ret); - return 1; - } - - ovecsize = (capcount+1)*3; - ovec = zalloc(ovecsize*sizeof(int)); - plaintext = ztrdup(*args); unmetafy(plaintext, &subject_len); if (offset_start > 0 && offset_start >= subject_len) - ret = PCRE_ERROR_NOMATCH; - else - ret = pcre_exec(pcre_pattern, pcre_hints, plaintext, subject_len, offset_start, 0, ovec, ovecsize); + ret = PCRE2_ERROR_NOMATCH; + else { + pcre_mdata = pcre2_match_data_create_from_pattern(pcre_pattern, NULL); + ret = pcre2_match(pcre_pattern, (PCRE2_SPTR) plaintext, subject_len, + offset_start, 0, pcre_mdata, NULL); + } if (ret==0) return_value = 0; - else if (ret==PCRE_ERROR_NOMATCH) /* no match */; + else if (ret == PCRE2_ERROR_NOMATCH) /* no match */; else if (ret>0) { - zpcre_get_substrings(plaintext, ovec, ret, matched_portion, receptacle, + zpcre_get_substrings(plaintext, pcre_mdata, ret, matched_portion, receptacle, want_offset_pair, 0, 0); return_value = 0; } else { - zwarnnam(nam, "error in pcre_exec [%d]", ret); + zwarnnam(nam, "error in pcre2_match [%d]", ret); } - if (ovec) - zfree(ovec, ovecsize*sizeof(int)); + if (pcre_mdata) + pcre2_match_data_free(pcre_mdata); zsfree(plaintext); return return_value; @@ -383,17 +345,19 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) static int cond_pcre_match(char **a, int id) { - pcre *pcre_pat; - const char *pcre_err; + pcre2_code *pcre_pat = NULL; + int pcre_err; + PCRE2_SIZE pcre_erroff; char *lhstr, *rhre, *lhstr_plain, *rhre_plain, *avar, *svar; - int r = 0, pcre_opts = 0, pcre_errptr, capcnt, *ov, ovsize; + int r = 0, pcre_opts = 0; + pcre2_match_data *pcre_mdata = NULL; int lhstr_plain_len, rhre_plain_len; int return_value = 0; if (zpcre_utf8_enabled()) - pcre_opts |= PCRE_UTF8; + pcre_opts |= PCRE2_UTF; if (isset(REMATCHPCRE) && !isset(CASEMATCH)) - pcre_opts |= PCRE_CASELESS; + pcre_opts |= PCRE2_CASELESS; lhstr = cond_str(a,0,0); rhre = cond_str(a,1,0); @@ -401,9 +365,6 @@ cond_pcre_match(char **a, int id) rhre_plain = ztrdup(rhre); unmetafy(lhstr_plain, &lhstr_plain_len); unmetafy(rhre_plain, &rhre_plain_len); - pcre_pat = NULL; - ov = NULL; - ovsize = 0; if (isset(BASHREMATCH)) { svar = NULL; @@ -415,27 +376,27 @@ cond_pcre_match(char **a, int id) switch(id) { case CPCRE_PLAIN: - if ((int)strlen(rhre_plain) != rhre_plain_len) { - zwarn("embedded NULs in PCRE pattern terminate pattern"); - } - pcre_pat = pcre_compile(rhre_plain, pcre_opts, &pcre_err, &pcre_errptr, NULL); - if (pcre_pat == NULL) { - zwarn("failed to compile regexp /%s/: %s", rhre, pcre_err); + if (!(pcre_pat = pcre2_compile((PCRE2_SPTR) rhre_plain, + (PCRE2_SIZE) rhre_plain_len, pcre_opts, + &pcre_err, &pcre_erroff, NULL))) + { + PCRE2_UCHAR buffer[256]; + pcre2_get_error_message(pcre_err, buffer, sizeof(buffer)); + zwarn("failed to compile regexp /%s/: %s", rhre, buffer); break; } - pcre_fullinfo(pcre_pat, NULL, PCRE_INFO_CAPTURECOUNT, &capcnt); - ovsize = (capcnt+1)*3; - ov = zalloc(ovsize*sizeof(int)); - r = pcre_exec(pcre_pat, NULL, lhstr_plain, lhstr_plain_len, 0, 0, ov, ovsize); - /* r < 0 => error; r==0 match but not enough size in ov + pcre_mdata = pcre2_match_data_create_from_pattern(pcre_pat, NULL); + r = pcre2_match(pcre_pat, (PCRE2_SPTR8) lhstr_plain, lhstr_plain_len, + 0, 0, pcre_mdata, NULL); + /* r < 0 => error; r==0 match but not enough size in match data * r > 0 => (r-1) substrings found; r==1 => no substrings */ if (r==0) { - zwarn("reportable zsh problem: pcre_exec() returned 0"); + zwarn("reportable zsh problem: pcre2_match() returned 0"); return_value = 1; break; } - else if (r==PCRE_ERROR_NOMATCH) { + else if (r == PCRE2_ERROR_NOMATCH) { return_value = 0; /* no match */ break; } @@ -444,7 +405,7 @@ cond_pcre_match(char **a, int id) break; } else if (r>0) { - zpcre_get_substrings(lhstr_plain, ov, r, svar, avar, 0, + zpcre_get_substrings(lhstr_plain, pcre_mdata, r, svar, avar, 0, isset(BASHREMATCH), !isset(BASHREMATCH)); return_value = 1; @@ -457,10 +418,10 @@ cond_pcre_match(char **a, int id) free(lhstr_plain); if(rhre_plain) free(rhre_plain); + if (pcre_mdata) + pcre2_match_data_free(pcre_mdata); if (pcre_pat) - pcre_free(pcre_pat); - if (ov) - zfree(ov, ovsize*sizeof(int)); + pcre2_code_free(pcre_pat); return return_value; } @@ -489,11 +450,11 @@ static struct builtin bintab[] = { static struct features module_features = { bintab, sizeof(bintab)/sizeof(*bintab), -#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC) +#if defined(HAVE_PCRE2_COMPILE_8) && defined(HAVE_PCRE2_H) cotab, sizeof(cotab)/sizeof(*cotab), -#else /* !(HAVE_PCRE_COMPILE && HAVE_PCRE_EXEC) */ +#else /* !(HAVE_PCRE2_COMPILE_8 && HAVE_PCRE2_H) */ NULL, 0, -#endif /* !(HAVE_PCRE_COMPILE && HAVE_PCRE_EXEC) */ +#endif /* !(HAVE_PCRE2_COMPILE_8 && HAVE_PCRE2_H) */ NULL, 0, NULL, 0, 0 @@ -540,19 +501,9 @@ cleanup_(Module m) int finish_(UNUSED(Module m)) { -#if defined(HAVE_PCRE_COMPILE) && defined(HAVE_PCRE_EXEC) -#ifdef HAVE_PCRE_STUDY - if (pcre_hints) -#ifdef PCRE_CONFIG_JIT - pcre_free_study(pcre_hints); -#else - pcre_free(pcre_hints); -#endif - pcre_hints = NULL; -#endif - +#if defined(HAVE_PCRE2_COMPILE_8) && defined(HAVE_PCRE2_H) if (pcre_pattern) - pcre_free(pcre_pattern); + pcre2_code_free(pcre_pattern); pcre_pattern = NULL; #endif diff --git a/Test/V07pcre.ztst b/Test/V07pcre.ztst index 22a0b64c7..6eb366964 100644 --- a/Test/V07pcre.ztst +++ b/Test/V07pcre.ztst @@ -117,12 +117,17 @@ >78884; ZPCRE_OP: 25 30 >90210; ZPCRE_OP: 31 36 -# Embedded NULs allowed in plaintext, but not in RE (although \0 as two-chars allowed) +# Embedded NULs allowed in plaintext, in RE, pcre supports \0 as two-chars [[ $'a\0bc\0d' =~ '^(a\0.)(.+)$' ]] print "${#MATCH}; ${#match[1]}; ${#match[2]}" 0:ensure ASCII NUL passes in and out of matched plaintext >6; 3; 3 +# PCRE2 supports NULs also in the RE + [[ $'a\0b\0c' =~ $'^(.\0)+' ]] && print "${#MATCH}; ${#match[1]}" +0:ensure ASCII NUL works also in the regex +>4; 2 + # Ensure the long-form infix operator works [[ foo -pcre-match ^f..$ ]] print $? @@ -169,7 +174,11 @@ [[ é =~ '^..\z' ]]; echo $? LANG=$LANG_SAVE [[ é =~ '^.\z' ]]; echo $? -0:swich between C/UTF-8 locales +0:switch between C/UTF-8 locales >0 >0 >0 + + [[ abc =~ 'a(d*)bc' ]] && print "$#MATCH; $#match; ${#match[1]}" +0:empty capture +>3; 1; 0 diff --git a/configure.ac b/configure.ac index d8a17791a..4710d1659 100644 --- a/configure.ac +++ b/configure.ac @@ -438,7 +438,7 @@ fi], dnl Do you want to look for pcre support? AC_ARG_ENABLE(pcre, -AS_HELP_STRING([--enable-pcre],[enable the search for the pcre library (may create run-time library dependencies)])) +AS_HELP_STRING([--enable-pcre],[enable the search for the pcre2 library (may create run-time library dependencies)])) dnl Do you want to look for capability support? AC_ARG_ENABLE(cap, @@ -652,13 +652,12 @@ AC_HEADER_SYS_WAIT oldcflags="$CFLAGS" if test x$enable_pcre = xyes; then -AC_CHECK_PROG([PCRECONF], pcre-config, pcre-config) -dnl Typically (meaning on this single RedHat 9 box in front of me) -dnl pcre-config --cflags produces a -I output which needs to go into +AC_CHECK_PROG([PCRECONF], pcre2-config, pcre2-config) +dnl pcre2-config --cflags may produce a -I output which needs to go into dnl CPPFLAGS else configure's preprocessor tests don't pick it up, dnl producing a warning. -if test "x$ac_cv_prog_PCRECONF" = xpcre-config; then - CPPFLAGS="$CPPFLAGS `pcre-config --cflags`" +if test "x$ac_cv_prog_PCRECONF" = xpcre2-config; then + CPPFLAGS="$CPPFLAGS `pcre2-config --cflags`" fi fi @@ -668,9 +667,10 @@ AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \ locale.h errno.h stdio.h stdarg.h varargs.h stdlib.h \ unistd.h sys/capability.h \ utmp.h utmpx.h sys/types.h pwd.h grp.h poll.h sys/mman.h \ - netinet/in_systm.h pcre.h langinfo.h wchar.h stddef.h \ + netinet/in_systm.h langinfo.h wchar.h stddef.h \ sys/stropts.h iconv.h ncurses.h ncursesw/ncurses.h \ ncurses/ncurses.h) +AC_CHECK_HEADERS([pcre2.h],,,[#define PCRE2_CODE_UNIT_WIDTH 8]) if test x$dynamic = xyes; then AC_CHECK_HEADERS(dlfcn.h) AC_CHECK_HEADERS(dl.h) @@ -948,9 +948,7 @@ if test "x$ac_found_iconv" = "xyes"; then fi if test x$enable_pcre = xyes; then -dnl pcre-config should probably be employed here -dnl AC_SEARCH_LIBS(pcre_compile, pcre) - LIBS="`$ac_cv_prog_PCRECONF --libs` $LIBS" + LIBS="`$ac_cv_prog_PCRECONF --libs8` $LIBS" fi dnl --------------------- @@ -1313,7 +1311,7 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \ pathconf sysconf \ tgetent tigetflag tigetnum tigetstr setupterm initscr resize_term \ getcchar setcchar waddwstr wget_wch win_wch use_default_colors \ - pcre_compile pcre_study pcre_exec \ + pcre2_compile_8 \ nl_langinfo \ erand48 open_memstream \ posix_openpt \ -- cgit v1.2.3 From f3f371deb376478176866fd770fbcf9bc0d0609f Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sat, 13 May 2023 00:56:48 +0200 Subject: 51728: assign pcre named capture groups to a hash --- ChangeLog | 3 +++ Doc/Zsh/mod_pcre.yo | 10 ++++++---- Src/Modules/pcre.c | 43 +++++++++++++++++++++++++++++++++---------- Test/V07pcre.ztst | 14 ++++++++++++++ 4 files changed, 56 insertions(+), 14 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 285b73b2c..2835a9405 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-05-13 Oliver Kiddle + * 51728: Doc/Zsh/mod_pcre.yo, Src/Modules/pcre.c, + Test/V07pcre.ztst: assign pcre named capture groups to a hash + * 51723: Src/Modules/pcre.c, Test/V07pcre.ztst, configure.ac: migrate pcre module to pcre2 diff --git a/Doc/Zsh/mod_pcre.yo b/Doc/Zsh/mod_pcre.yo index c2817f519..6d073985d 100644 --- a/Doc/Zsh/mod_pcre.yo +++ b/Doc/Zsh/mod_pcre.yo @@ -20,12 +20,12 @@ including those that indicate newline. ) findex(pcre_study) item(tt(pcre_study))( -Studies the previously-compiled PCRE which may result in faster -matching. +Requests JIT compilation for the previously-compiled PCRE which +may result in faster matching. ) findex(pcre_match) item(tt(pcre_match) [ tt(-v) var(var) ] [ tt(-a) var(arr) ] \ -[ tt(-n) var(offset) ] [ tt(-b) ] var(string))( +[ tt(-A) var(assoc) ] [ tt(-n) var(offset) ] [ tt(-b) ] var(string))( Returns successfully if tt(string) matches the previously-compiled PCRE. @@ -36,7 +36,9 @@ substrings, unless the tt(-a) option is given, in which case it will set the array var(arr). Similarly, the variable tt(MATCH) will be set to the entire matched portion of the string, unless the tt(-v) option is given, in which case the variable -var(var) will be set. +var(var) will be set. Furthermore, any named captures will +be stored in the associative array tt(.pcre.match) unless an +alternative is given with tt(-A). No variables are altered if there is no successful match. A tt(-n) option starts searching for a match from the byte var(offset) position in var(string). If the tt(-b) option is given, diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c index 079ecc2c5..6be1f76e2 100644 --- a/Src/Modules/pcre.c +++ b/Src/Modules/pcre.c @@ -129,14 +129,17 @@ bin_pcre_study(char *nam, UNUSED(char **args), UNUSED(Options ops), UNUSED(int f } static int -zpcre_get_substrings(char *arg, pcre2_match_data *mdata, int captured_count, - char *matchvar, char *substravar, int want_offset_pair, - int matchedinarr, int want_begin_end) +zpcre_get_substrings(pcre2_code *pat, char *arg, pcre2_match_data *mdata, + int captured_count, char *matchvar, char *substravar, char *namedassoc, + int want_offset_pair, int matchedinarr, int want_begin_end) { PCRE2_SIZE *ovec; char *match_all, **matches; char offset_all[50]; int capture_start = 1; + int vec_off; + PCRE2_SPTR ntable; /* table of named captures */ + uint32_t ncount, nsize; if (matchedinarr) { /* bash-style ovec[0] entire-matched string in the array */ @@ -174,7 +177,7 @@ zpcre_get_substrings(char *arg, pcre2_match_data *mdata, int captured_count, if (substravar && (!want_begin_end || nelem)) { char **x; - int vec_off, i; + int i; matches = x = (char **) zalloc(sizeof(char *) * (captured_count+1-capture_start)); for (i = capture_start; i < captured_count; i++) { vec_off = 2*i; @@ -184,6 +187,23 @@ zpcre_get_substrings(char *arg, pcre2_match_data *mdata, int captured_count, setaparam(substravar, matches); } + if (!pcre2_pattern_info(pat, PCRE2_INFO_NAMECOUNT, &ncount) && ncount + && !pcre2_pattern_info(pat, PCRE2_INFO_NAMEENTRYSIZE, &nsize) + && !pcre2_pattern_info(pat, PCRE2_INFO_NAMETABLE, &ntable)) + { + char **hash, **hashptr; + uint32_t nidx; + hashptr = hash = (char **)zshcalloc((ncount+1)*2*sizeof(char *)); + for (nidx = 0; nidx < ncount; nidx++) { + vec_off = (ntable[nsize * nidx] << 9) + 2 * ntable[nsize * nidx + 1]; + /* would metafy the key but pcre limits characters in the name */ + *hashptr++ = ztrdup((char *) ntable + nsize * nidx + 2); + *hashptr++ = metafy(arg + ovec[vec_off], + ovec[vec_off+1]-ovec[vec_off], META_DUP); + } + sethparam(namedassoc, hash); + } + if (want_begin_end) { /* * cond-infix rather than builtin; also not bash; so we set a bunch @@ -286,6 +306,7 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) char *matched_portion = NULL; char *plaintext = NULL; char *receptacle = NULL; + char *named = ".pcre.match"; int return_value = 1; /* The subject length and offset start are both int values in pcre_exec */ int subject_len; @@ -305,6 +326,9 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) if(OPT_HASARG(ops,c='v')) { matched_portion = OPT_ARG(ops,c); } + if (OPT_HASARG(ops, c='A')) { + named = OPT_ARG(ops, c); + } if(OPT_HASARG(ops,c='n')) { /* The offset position to start the search, in bytes. */ if ((offset_start = getposint(OPT_ARG(ops,c), nam)) < 0) return 1; @@ -326,8 +350,8 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) if (ret==0) return_value = 0; else if (ret == PCRE2_ERROR_NOMATCH) /* no match */; else if (ret>0) { - zpcre_get_substrings(plaintext, pcre_mdata, ret, matched_portion, receptacle, - want_offset_pair, 0, 0); + zpcre_get_substrings(pcre_pattern, plaintext, pcre_mdata, ret, matched_portion, + receptacle, named, want_offset_pair, 0, 0); return_value = 0; } else { @@ -405,9 +429,8 @@ cond_pcre_match(char **a, int id) break; } else if (r>0) { - zpcre_get_substrings(lhstr_plain, pcre_mdata, r, svar, avar, 0, - isset(BASHREMATCH), - !isset(BASHREMATCH)); + zpcre_get_substrings(pcre_pat, lhstr_plain, pcre_mdata, r, svar, avar, + ".pcre.match", 0, isset(BASHREMATCH), !isset(BASHREMATCH)); return_value = 1; break; } @@ -443,7 +466,7 @@ static struct conddef cotab[] = { static struct builtin bintab[] = { BUILTIN("pcre_compile", 0, bin_pcre_compile, 1, 1, 0, "aimxs", NULL), - BUILTIN("pcre_match", 0, bin_pcre_match, 1, 1, 0, "a:v:n:b", NULL), + BUILTIN("pcre_match", 0, bin_pcre_match, 1, 1, 0, "A:a:v:n:b", NULL), BUILTIN("pcre_study", 0, bin_pcre_study, 0, 0, 0, NULL, NULL) }; diff --git a/Test/V07pcre.ztst b/Test/V07pcre.ztst index 6eb366964..027fea3aa 100644 --- a/Test/V07pcre.ztst +++ b/Test/V07pcre.ztst @@ -182,3 +182,17 @@ [[ abc =~ 'a(d*)bc' ]] && print "$#MATCH; $#match; ${#match[1]}" 0:empty capture >3; 1; 0 + + [[ category/name-12345 =~ '(?x)^ + (? [^/]* ) / + (? + (? \w+ ) - + (? \d+ ))$' ]] + typeset -p1 .pcre.match +0:named captures +>typeset -g -A .pcre.match=( +> [category]=category +> [name]=name +> [package]=name-12345 +> [version]=12345 +>) -- cgit v1.2.3 From b4d1c756f50909b4a13e5c8fe5f26f71e9d54f63 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sat, 13 May 2023 00:59:00 +0200 Subject: 51738: support pcre's alternative DFA matching algorithm --- ChangeLog | 3 +++ Doc/Zsh/mod_pcre.yo | 6 +++++- Src/Modules/pcre.c | 53 ++++++++++++++++++++++++++++++++++++----------------- Test/V07pcre.ztst | 5 +++++ 4 files changed, 49 insertions(+), 18 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 2835a9405..18bc4a698 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-05-13 Oliver Kiddle + * 51738: Doc/Zsh/mod_pcre.yo, Src/Modules/pcre.c, + Test/V07pcre.ztst: support pcre's DFA matching algorithm + * 51728: Doc/Zsh/mod_pcre.yo, Src/Modules/pcre.c, Test/V07pcre.ztst: assign pcre named capture groups to a hash diff --git a/Doc/Zsh/mod_pcre.yo b/Doc/Zsh/mod_pcre.yo index 6d073985d..da73ac85a 100644 --- a/Doc/Zsh/mod_pcre.yo +++ b/Doc/Zsh/mod_pcre.yo @@ -25,7 +25,7 @@ may result in faster matching. ) findex(pcre_match) item(tt(pcre_match) [ tt(-v) var(var) ] [ tt(-a) var(arr) ] \ -[ tt(-A) var(assoc) ] [ tt(-n) var(offset) ] [ tt(-b) ] var(string))( +[ tt(-A) var(assoc) ] [ tt(-n) var(offset) ] [ tt(-bd) ] var(string))( Returns successfully if tt(string) matches the previously-compiled PCRE. @@ -69,6 +69,10 @@ print -l $accum) ) enditem() +The option tt(-d) uses the alternative breadth-first DFA search algorithm of +pcre. This sets tt(match), or the array given with tt(-a), to all the matches +found from the same start point in the subject. + The tt(zsh/pcre) module makes available the following test condition: startitem() diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c index 6be1f76e2..96f3c6e65 100644 --- a/Src/Modules/pcre.c +++ b/Src/Modules/pcre.c @@ -305,30 +305,29 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) pcre2_match_data *pcre_mdata = NULL; char *matched_portion = NULL; char *plaintext = NULL; - char *receptacle = NULL; - char *named = ".pcre.match"; + char *receptacle; + char *named = NULL; int return_value = 1; /* The subject length and offset start are both int values in pcre_exec */ int subject_len; int offset_start = 0; int want_offset_pair = 0; + int use_dfa = 0; if (pcre_pattern == NULL) { zwarnnam(nam, "no pattern has been compiled"); return 1; } - matched_portion = "MATCH"; - receptacle = "match"; - if(OPT_HASARG(ops,c='a')) { - receptacle = OPT_ARG(ops,c); - } - if(OPT_HASARG(ops,c='v')) { - matched_portion = OPT_ARG(ops,c); - } - if (OPT_HASARG(ops, c='A')) { - named = OPT_ARG(ops, c); + if (!(use_dfa = OPT_ISSET(ops, 'd'))) { + matched_portion = OPT_HASARG(ops, c='v') ? OPT_ARG(ops, c) : "MATCH"; + named = OPT_HASARG(ops, c='A') ? OPT_ARG(ops, c) : ".pcre.match"; + } else if (OPT_HASARG(ops, c='v') || OPT_HASARG(ops, c='A')) { + zwarnnam(nam, "-d cannot be combined with -%c", c); + return 1; } + receptacle = OPT_HASARG(ops, 'a') ? OPT_ARG(ops, 'a') : "match"; + if(OPT_HASARG(ops,c='n')) { /* The offset position to start the search, in bytes. */ if ((offset_start = getposint(OPT_ARG(ops,c), nam)) < 0) return 1; @@ -341,7 +340,25 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) if (offset_start > 0 && offset_start >= subject_len) ret = PCRE2_ERROR_NOMATCH; - else { + else if (use_dfa) { + PCRE2_SIZE old, wscount = 128, capcount = 128; + void *workspace = zhalloc(sizeof(int) * wscount); + pcre_mdata = pcre2_match_data_create(capcount, NULL); + do { + ret = pcre2_dfa_match(pcre_pattern, (PCRE2_SPTR) plaintext, subject_len, + offset_start, 0, pcre_mdata, NULL, (int *) workspace, wscount); + if (ret == PCRE2_ERROR_DFA_WSSIZE) { + old = wscount; + wscount += wscount / 2; + workspace = hrealloc(workspace, sizeof(int) * old, sizeof(int) * wscount); + } else if (ret == 0) { + capcount += capcount / 2; + pcre2_match_data_free(pcre_mdata); + pcre_mdata = pcre2_match_data_create(capcount, NULL); + } else + break; + } while(1); + } else { pcre_mdata = pcre2_match_data_create_from_pattern(pcre_pattern, NULL); ret = pcre2_match(pcre_pattern, (PCRE2_SPTR) plaintext, subject_len, offset_start, 0, pcre_mdata, NULL); @@ -350,12 +367,14 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) if (ret==0) return_value = 0; else if (ret == PCRE2_ERROR_NOMATCH) /* no match */; else if (ret>0) { - zpcre_get_substrings(pcre_pattern, plaintext, pcre_mdata, ret, matched_portion, - receptacle, named, want_offset_pair, 0, 0); + zpcre_get_substrings(pcre_pattern, plaintext, pcre_mdata, ret, + matched_portion, receptacle, named, want_offset_pair, use_dfa, 0); return_value = 0; } else { - zwarnnam(nam, "error in pcre2_match [%d]", ret); + PCRE2_UCHAR buffer[256]; + pcre2_get_error_message(ret, buffer, sizeof(buffer)); + zwarnnam(nam, "error in pcre matching for /%s/: %s", plaintext, buffer); } if (pcre_mdata) @@ -466,7 +485,7 @@ static struct conddef cotab[] = { static struct builtin bintab[] = { BUILTIN("pcre_compile", 0, bin_pcre_compile, 1, 1, 0, "aimxs", NULL), - BUILTIN("pcre_match", 0, bin_pcre_match, 1, 1, 0, "A:a:v:n:b", NULL), + BUILTIN("pcre_match", 0, bin_pcre_match, 1, 1, 0, "A:a:v:n:bd", NULL), BUILTIN("pcre_study", 0, bin_pcre_study, 0, 0, 0, NULL, NULL) }; diff --git a/Test/V07pcre.ztst b/Test/V07pcre.ztst index 027fea3aa..585698d05 100644 --- a/Test/V07pcre.ztst +++ b/Test/V07pcre.ztst @@ -196,3 +196,8 @@ > [package]=name-12345 > [version]=12345 >) + + pcre_compile 'cat(er(pillar)?)?' + pcre_match -d 'the caterpillar catchment' && print $match +0:pcre_match -d +>caterpillar cater cat -- cgit v1.2.3 From a95198e268ec1d432c37afc8dc4f8839acc0c8d0 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sat, 13 May 2023 21:49:07 +0100 Subject: 51722: Safety for extracting elements of $historywords --- ChangeLog | 5 +++++ Src/Modules/parameter.c | 11 +++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 18bc4a698..85fc1de96 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-05-13 Peter Stephenson + + * 51722: Src/Modules/parameter.c: Add safety to extracting + elements of $historywords. + 2023-05-13 Oliver Kiddle * 51738: Doc/Zsh/mod_pcre.yo, Src/Modules/pcre.c, diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index 96a211c69..a05ea2fe4 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -1233,9 +1233,16 @@ histwgetfn(UNUSED(Param pm)) pushnode(l, getdata(n)); while (he) { + char *hstr = he->node.nam; + int len = strlen(hstr); for (iw = he->nwords - 1; iw >= 0; iw--) { - h = he->node.nam + he->words[iw * 2]; - e = he->node.nam + he->words[iw * 2 + 1]; + int wbegin = he->words[iw * 2]; + int wend = he->words[iw * 2 + 1]; + + if (wbegin < 0 || wbegin >= len || wend < 0 || wend > len) + break; + h = hstr + wbegin; + e = hstr + wend; sav = *e; *e = '\0'; addlinknode(l, dupstring(h)); -- cgit v1.2.3 From f80ad32c3f4e6239d9d6853d14ff36e28154f075 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sun, 21 May 2023 03:36:21 +0200 Subject: 51769: fix compilation when HAVE_GETRUSAGE is not defined Also silence compiler warning when HAVE_SETUPTERM is not defined. --- ChangeLog | 3 +++ Src/jobs.c | 2 +- Src/utils.c | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index e43613070..0b2993f7d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-05-21 Oliver Kiddle + * 51769: Src/jobs.c, Src/utils.c: fix compilation when + HAVE_GETRUSAGE is not defined + * Marlon Richert: 51761: Completion/Zsh/Type/_parameters: Use zstyle verbose for _parameters descriptions diff --git a/Src/jobs.c b/Src/jobs.c index 4d7172550..dd7bba405 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -1032,7 +1032,7 @@ should_report_time(Job j) return 1; #else { - clktck = get_clktck(); + long clktck = get_clktck(); if ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime) return 1; } diff --git a/Src/utils.c b/Src/utils.c index 14ff0ed47..f13e3a79d 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -380,11 +380,13 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) fflush(file); } +#ifdef HAVE_SETUPTERM /* * Wrapper for setupterm() and del_curterm(). * These are called from terminfo.c and termcap.c. */ static int term_count; /* reference count of cur_term */ +#endif /**/ mod_export void -- cgit v1.2.3 From 88eeade0bcdba8beb4a94534312b8febbc608697 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 25 May 2023 15:47:23 +0100 Subject: 51739: detect invalid history word beginning --- ChangeLog | 4 ++++ Src/hist.c | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 0b2993f7d..1a703ec04 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-05-25 Peter Stephenson + + * 51739: Src/hist.c: detect invalid history word beginning. + 2023-05-21 Oliver Kiddle * 51769: Src/jobs.c, Src/utils.c: fix compilation when diff --git a/Src/hist.c b/Src/hist.c index 82d03a840..7e6394406 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -1643,12 +1643,17 @@ hend(Eprog prog) void ihwbegin(int offset) { + int pos = hptr - chline + offset; if (stophist == 2 || (histactive & HA_INWORD) || (inbufflags & (INP_ALIAS|INP_HIST)) == INP_ALIAS) return; if (chwordpos%2) chwordpos--; /* make sure we're on a word start, not end */ - chwords[chwordpos++] = hptr - chline + offset; + DPUTS1(pos < 0, "History word position < 0 in %s", + dupstrpfx(chline, hptr-chline)); + if (pos < 0) + pos = 0; + chwords[chwordpos++] = pos; } /* Abort current history word, not needed */ -- cgit v1.2.3 From 78102120b9c9e7485e7f537864fc2c24fbe0071a Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 6 Jun 2023 09:16:46 +0100 Subject: 51816: add :S history modifier with pattern match --- ChangeLog | 6 ++++++ Doc/Zsh/expn.yo | 18 ++++++++++++------ Src/hist.c | 17 ++++++++++++----- Src/subst.c | 12 +++++++++--- Test/D04parameter.ztst | 10 ++++++++++ 5 files changed, 49 insertions(+), 14 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 2c0f6287a..c760fccab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2023-06-06 Peter Stephenson + + * 51816: Doc/Zsh/expn.yo, Src/hist.c, Src/subst.c, + Test/D04parameter.ztst: add :S history modifier with pattern + match. + 2023-06-06 Jun-ichi Takimoto * Marlon Richert: 51779: Test/Y01completion.ztst: update diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 6f86d0c54..7bc736470 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -312,7 +312,8 @@ zero) that are neither `tt(.)' nor `tt(/)' and that continue to the end of the string. For example, the extension of `tt(foo.orig.c)' is `tt(.c)', and `tt(dir.c/foo)' has no extension. ) -item(tt(s/)var(l)tt(/)var(r)[tt(/)])( +xitem(tt(s/)var(l)tt(/)var(r)[tt(/)]) +item(tt(S/)var(l)tt(/)var(r)[tt(/)])( Substitute var(r) for var(l) as described below. The substitution is done only for the first string that matches var(l). For arrays and for filename @@ -324,13 +325,17 @@ perform global substitution, i.e. substitute every occurrence of var(r) for var(l). Note that the tt(g) or tt(:G) must appear in exactly the position shown. +The use of tt(S) instead of tt(s) is identical except that +the source is treated as a pattern, just as if the option +tt(HIST_SUBST_PATTERN) were set. + See further notes on this form of substitution below. ) item(tt(&))( -Repeat the previous tt(s) substitution. Like tt(s), may be preceded -immediately by a tt(g). In parameter expansion the tt(&) must appear -inside braces, and in filename generation it must be quoted with a -backslash. +Repeat the previous tt(s) or tt(S) substitution, whichever was most +recent. Like tt(s) and tt(S), may be preceded immediately by a tt(g). +In parameter expansion the tt(&) must appear inside braces, and in +filename generation it must be quoted with a backslash. ) item(tt(t) [ var(digits) ])( Remove all leading pathname components, leaving the final component (tail). @@ -377,7 +382,8 @@ substitutions or expansions are performed once at the time the qualifier is parsed, even before the `tt(:s)' expression itself is divided into var(l) and var(r) sides. -If the option tt(HIST_SUBST_PATTERN) is set, var(l) is treated as +If the option tt(HIST_SUBST_PATTERN) is set or the original substitution +was started with a capital tt(S), var(l) is treated as a pattern of the usual form described in ifzman(the section FILENAME GENERATION below)\ ifnzman(noderef(Filename Generation)). This can be used in diff --git a/Src/hist.c b/Src/hist.c index 7e6394406..b4dc53d90 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -163,6 +163,11 @@ char *hsubl; /**/ char *hsubr; +/* state of histsubstpattern at last substitution */ + +/**/ +int hsubpatopt; + /* pointer into the history line */ /**/ @@ -624,7 +629,7 @@ histsubchar(int c) return substfailed(); if (!hsubl) return -1; - if (subst(&sline, hsubl, hsubr, gbal)) + if (subst(&sline, hsubl, hsubr, gbal, 0)) return substfailed(); } else { /* Line doesn't begin ^foo^bar */ @@ -831,7 +836,7 @@ histsubchar(int c) if ((c = ingetc()) == 'g') { gbal = 1; c = ingetc(); - if (c != 's' && c != '&') { + if (c != 's' && c != 'S' && c != '&') { zerr("'s' or '&' modifier expected after 'g'"); return -1; } @@ -891,11 +896,13 @@ histsubchar(int c) } break; case 's': + case 'S': + hsubpatopt = (c == 'S'); if (getsubsargs(sline, &gbal, &cflag)) return -1; /* fall through */ case '&': if (hsubl && hsubr) { - if (subst(&sline, hsubl, hsubr, gbal)) + if (subst(&sline, hsubl, hsubr, gbal, hsubpatopt)) return substfailed(); } else { herrflush(); @@ -2315,7 +2322,7 @@ casemodify(char *str, int how) /**/ int -subst(char **strptr, char *in, char *out, int gbal) +subst(char **strptr, char *in, char *out, int gbal, int forcepat) { char *str = *strptr, *substcut, *sptr; int off, inlen, outlen; @@ -2323,7 +2330,7 @@ subst(char **strptr, char *in, char *out, int gbal) if (!*in) in = str, gbal = 0; - if (isset(HISTSUBSTPATTERN)) { + if (isset(HISTSUBSTPATTERN) || forcepat) { int fl = SUB_LONG|SUB_REST|SUB_RETFAIL; char *oldin = in; if (gbal) diff --git a/Src/subst.c b/Src/subst.c index 974d6171e..14947ae36 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -4351,6 +4351,8 @@ modify(char **str, char **ptr, int inbrace) break; case 's': + case 'S': + hsubpatopt = (**ptr == 'S'); c = **ptr; (*ptr)++; ptr1 = *ptr; @@ -4445,7 +4447,7 @@ modify(char **str, char **ptr, int inbrace) break; case '&': - c = 's'; + c = hsubpatopt ? 'S' : 's'; break; case 'g': @@ -4534,8 +4536,10 @@ modify(char **str, char **ptr, int inbrace) copy = casemodify(tt, CASMOD_UPPER); break; case 's': + case 'S': + hsubpatopt = (c == 'S'); if (hsubl && hsubr) - subst(©, hsubl, hsubr, gbal); + subst(©, hsubl, hsubr, gbal, hsubpatopt); break; case 'q': copy = quotestring(copy, QT_BACKSLASH_SHOWNULL); @@ -4620,8 +4624,10 @@ modify(char **str, char **ptr, int inbrace) *str = casemodify(*str, CASMOD_UPPER); break; case 's': + case 'S': + hsubpatopt = (c == 'S'); if (hsubl && hsubr) - subst(str, hsubl, hsubr, gbal); + subst(str, hsubl, hsubr, gbal, hsubpatopt); break; case 'q': *str = quotestring(*str, QT_BACKSLASH); diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 7990c2958..2fd2f975f 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -2754,3 +2754,13 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888 0:(users/28784 inspired this) substituting a single-quoted backslash, part #3: control >xfooy + spacestring="string with spaces" + print ${spacestring:gs/[[:space:]]/ /} + print ${spacestring:g&} + print ${spacestring:gS/[[:space:]]//} + print ${spacestring:g&} +0:Different behaviour of :s and :S modifiers +>string with spaces +>string with spaces +>stringwithspaces +>stringwithspaces -- cgit v1.2.3 From cd1a0a70972e717aa93c5fd6b36f476b5593c206 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Thu, 8 Jun 2023 15:36:31 +0900 Subject: 51826: correctly read metafied null character from history file --- ChangeLog | 3 +++ Src/hist.c | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 16ce32b54..ca290ea2a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-06-08 Jun-ichi Takimoto + * 51826: Src/hist.c: correctly handle metafied null character + when reading history file + * Stephane: 51817: Completion/BSD/Command/_rcctl: protect ':' in _rcctl (was in 51817 but missed in commit 0577daf) diff --git a/Src/hist.c b/Src/hist.c index b4dc53d90..bfbcd6ede 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -3803,8 +3803,14 @@ histsplitwords(char *lineptr, short **wordsp, int *nwordsp, int *nwordposp, zrealloc(words, nwords*sizeof(*words)); } words[nwordpos++] = lineptr - start; - while (*lineptr && !inblank(*lineptr)) - lineptr++; + while (*lineptr) { + if (*lineptr == Meta && lineptr[1]) + lineptr += 2; + else if (!inblank(*lineptr)) + lineptr++; + else + break; + } words[nwordpos++] = lineptr - start; } } while (*lineptr); -- cgit v1.2.3 From 10bdbd8b5b0b43445aff23dcd412f25cf6aa328a Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Tue, 20 Jun 2023 18:14:27 +0900 Subject: 51877: do not build pcre module if pcre2-config is not found --- ChangeLog | 5 +++++ Src/Modules/pcre.mdd | 2 +- configure.ac | 31 +++++++++++++++++++------------ 3 files changed, 25 insertions(+), 13 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 14349dcf2..e89ffee1b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-06-20 Jun-ichi Takimoto + + * 51877: Src/Modules/pcre.mdd, configure.ac: do not build pcre + module if pcre2-config is not available. + 2023-06-19 Jun-ichi Takimoto * 51862: Doc/Makefile.in, configure.ac: support texinfo-7.0 diff --git a/Src/Modules/pcre.mdd b/Src/Modules/pcre.mdd index 6eb3c691b..3e1579117 100644 --- a/Src/Modules/pcre.mdd +++ b/Src/Modules/pcre.mdd @@ -1,5 +1,5 @@ name=zsh/pcre -link=`if test x$enable_pcre = xyes && (pcre-config --version >/dev/null 2>/dev/null); then echo dynamic; else echo no; fi` +link=`if test x$enable_pcre = xyes; then echo dynamic; else echo no; fi` load=no autofeatures="b:pcre_compile b:pcre_study b:pcre_match" diff --git a/configure.ac b/configure.ac index ba76f9a60..c5263035e 100644 --- a/configure.ac +++ b/configure.ac @@ -440,6 +440,17 @@ dnl Do you want to look for pcre support? AC_ARG_ENABLE(pcre, AS_HELP_STRING([--enable-pcre],[enable the search for the pcre2 library (may create run-time library dependencies)])) +AC_ARG_VAR(PCRE_CONFIG, [pathname of pcre2-config if it is not in PATH]) +if test "x$enable_pcre" = xyes; then + AC_CHECK_PROG([PCRE_CONFIG], pcre2-config, pcre2-config) + if test "x$PCRE_CONFIG" = x; then + enable_pcre=no + AC_MSG_WARN([pcre2-config not found: pcre module is disabled.]) + AC_MSG_NOTICE( + [Set PCRE_CONFIG to pathname of pcre2-config if it is not in PATH.]) + fi +fi + dnl Do you want to look for capability support? AC_ARG_ENABLE(cap, AS_HELP_STRING([--enable-cap],[enable the search for POSIX capabilities (may require additional headers to be added by hand)])) @@ -655,15 +666,12 @@ AC_HEADER_DIRENT AC_HEADER_STAT AC_HEADER_SYS_WAIT -oldcflags="$CFLAGS" -if test x$enable_pcre = xyes; then -AC_CHECK_PROG([PCRECONF], pcre2-config, pcre2-config) dnl pcre2-config --cflags may produce a -I output which needs to go into dnl CPPFLAGS else configure's preprocessor tests don't pick it up, dnl producing a warning. -if test "x$ac_cv_prog_PCRECONF" = xpcre2-config; then - CPPFLAGS="$CPPFLAGS `pcre2-config --cflags`" -fi +if test "x$enable_pcre" = xyes; then + CPPFLAGS="`$PCRE_CONFIG --cflags` $CPPFLAGS" + AC_CHECK_HEADERS([pcre2.h],,,[#define PCRE2_CODE_UNIT_WIDTH 8]) fi AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \ @@ -675,7 +683,6 @@ AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \ netinet/in_systm.h langinfo.h wchar.h stddef.h \ sys/stropts.h iconv.h ncurses.h ncursesw/ncurses.h \ ncurses/ncurses.h) -AC_CHECK_HEADERS([pcre2.h],,,[#define PCRE2_CODE_UNIT_WIDTH 8]) if test x$dynamic = xyes; then AC_CHECK_HEADERS(dlfcn.h) AC_CHECK_HEADERS(dl.h) @@ -952,10 +959,6 @@ if test "x$ac_found_iconv" = "xyes"; then [Define as const if the declaration of iconv() needs const.]) fi -if test x$enable_pcre = xyes; then - LIBS="`$ac_cv_prog_PCRECONF --libs8` $LIBS" -fi - dnl --------------------- dnl CHECK TERMCAP LIBRARY dnl --------------------- @@ -1316,7 +1319,6 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \ pathconf sysconf \ tgetent tigetflag tigetnum tigetstr setupterm initscr resize_term \ getcchar setcchar waddwstr wget_wch win_wch use_default_colors \ - pcre2_compile_8 \ nl_langinfo \ erand48 open_memstream \ posix_openpt \ @@ -1371,6 +1373,11 @@ if test x$zsh_cv_func_realpath_accepts_null = xyes; then AC_DEFINE(REALPATH_ACCEPTS_NULL) fi +if test x$enable_pcre = xyes; then + LIBS="`$PCRE_CONFIG --libs8` $LIBS" + AC_CHECK_FUNCS(pcre2_compile_8) +fi + if test x$enable_cap = xyes; then AC_CHECK_FUNCS(cap_get_proc) fi -- cgit v1.2.3 From 4345eed1fe5dd6c881b948331cfa8f4a48beda42 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Thu, 22 Jun 2023 13:36:40 -0700 Subject: 51887: namespaces recognized in math, incorrect usages rejected. --- ChangeLog | 3 +++ Src/math.c | 10 +++++++--- Src/params.c | 27 +++++++++++++++++++++++++- Test/K02parameter.ztst | 52 ++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 86 insertions(+), 6 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index e48073e80..0011cc947 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-06-22 Bart Schaefer + * 51887: Src/math.c, Src/params.c, Test/K02parameter.ztst: + namespaces recognized in math, incorrect usages rejected. + * Marlon Richert: 51860: Completion/Base/Completer/_prefix, Test/Y01completion.ztst, Test/comptest: simplify suffix handling in _prefix to remove longstanding and less accurate hack; tests. diff --git a/Src/math.c b/Src/math.c index 12c8d6f6b..a060181ed 100644 --- a/Src/math.c +++ b/Src/math.c @@ -641,7 +641,9 @@ zzlex(void) return MINUSEQ; } if (unary) { - if (idigit(*ptr) || *ptr == '.') { + if (idigit(*ptr) || + (*ptr == '.' && + (idigit(ptr[1]) || !itype_end(ptr, INAMESPC, 0)))) { int ctype = lexconstant(); if (ctype == NUM) { @@ -835,7 +837,9 @@ zzlex(void) case Dnull: break; default: - if (idigit(*--ptr) || *ptr == '.') + if (idigit(*--ptr) || + (*ptr == '.' && + (idigit(ptr[1]) || !itype_end(ptr, INAMESPC, 0)))) return lexconstant(); if (*ptr == '#') { if (*++ptr == '\\' || *ptr == '#') { @@ -857,7 +861,7 @@ zzlex(void) } cct = 1; } - if ((ie = itype_end(ptr, IIDENT, 0)) != ptr) { + if ((ie = itype_end(ptr, INAMESPC, 0)) != ptr) { int func = 0; char *p; diff --git a/Src/params.c b/Src/params.c index 021d341e8..2b0837e03 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1226,6 +1226,26 @@ isident(char *s) if (!*s) /* empty string is definitely not valid */ return 0; + /* This partly duplicates code in itype_end(), but we need to + * distinguish the leading namespace at this point to check the + * correctness of the identifier that follows + */ + if (*s == '.') { + if (idigit(s[1])) + return 0; /* Namespace must not start with a digit */ + /* Reject identifiers beginning with a digit in namespaces. + * Move this out below this block to also reject v.1x form. + */ + if ((ss = itype_end(s + (*s == '.'), IIDENT, 0))) { + if (*ss == '.') { + if (!ss[1]) + return 0; + if (idigit(ss[1])) + s = ss + 1; + } + } + } + if (idigit(*s)) { /* If the first character is `s' is a digit, then all must be */ for (ss = ++s; *ss; ss++) @@ -2148,7 +2168,12 @@ fetchvalue(Value v, char **pptr, int bracks, int flags) pm = (Param) paramtab->getnode2(paramtab, *t == '0' ? "0" : t); else pm = (Param) paramtab->getnode(paramtab, *t == '0' ? "0" : t); - if (sav) + if (!pm && *t == '.' && !isident(t)) { + /* badly formed namespace reference */ + if (sav) + *s = sav; + return NULL; + } else if (sav) *s = sav; *pptr = s; if (!pm || ((pm->node.flags & PM_UNSET) && diff --git a/Test/K02parameter.ztst b/Test/K02parameter.ztst index 8a1be1e36..0b1a8dd4a 100644 --- a/Test/K02parameter.ztst +++ b/Test/K02parameter.ztst @@ -100,7 +100,55 @@ F:Braces are required >.k02.array >characters + k.=empty k.2=test - print ${k.2} + print ${k.} ${k.2} 0:Parse without leading dot (future proofing) ->test +>empty test + + .k=OK + print ${.k} +0:Bare namespace is usable (ksh compatibility) +>OK + + .k.=empty +1:Namespace must precede an identifier, assignment +?(eval):1: not an identifier: .k. + + typeset .k.=empty +1:Namespace must precede an identifier, typeset +?(eval):typeset:1: not valid in this context: .k. + + print ${.k.} +1:Namespace must precede an identifier, reference +?(eval):1: bad substitution + + .2.b=not +1:Namespace identifier must not begin with a digit, assignment +?(eval):1: not an identifier: .2.b + + typeset .2.b=not +1:Namespace identifier must not begin with a digit, typeset +?(eval):typeset:1: not valid in this context: .2.b + + print ${.2.b} +1:Namespace identifier must not begin with a digit, reference +?(eval):1: bad substitution + + .not.2b=question +1:Identifier starting with a digit must be all digits, assignment +?(eval):1: not an identifier: .not.2b + + typeset .not.2b=question +1:Identifier starting with a digit must be all digits, typeset +?(eval):typeset:1: not valid in this context: .not.2b + + print ${.not.2b} +1:Identifier starting with a digit must be all digits, reference +?(eval):1: bad substitution + + integer .var.d=0 + float .var.f=.2 + print $((.var.x = ++.var.d - -.var.f)) +0:Namespaces in math context +>1.2 -- cgit v1.2.3 From 1b9bc3441ca0e6d155243084d6e7b98925dc02cb Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Mon, 26 Jun 2023 16:52:40 +0900 Subject: 51884: reset IFS if it contains invalid characters This happens only if MULTIBYTE option is on. --- ChangeLog | 6 ++++++ Doc/Zsh/params.yo | 7 +++++-- Src/params.c | 3 +++ Src/utils.c | 42 ++++++++++++++++++++++++++---------------- Test/D04parameter.ztst | 21 +++++++++++++++++++++ 5 files changed, 61 insertions(+), 18 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 0011cc947..51a091aff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2023-06-26 Jun-ichi Takimoto + + * 51884: Doc/Zsh/params.yo, Src/params.c, Src/utils.c, + Test/D04parameter.ztst: if MULTIBYTE option is on and IFS contains + invalid bytes in curret locale then reset it to default + 2023-06-22 Bart Schaefer * 51887: Src/math.c, Src/params.c, Test/K02parameter.ztst: diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 57d10b8bd..e0410d673 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -1325,15 +1325,18 @@ Internal field separators (by default space, tab, newline and NUL), that are used to separate words which result from command or parameter expansion and words read by the tt(read) builtin. Any characters from the set space, tab and -newline that appear in the IFS are called em(IFS white space). +newline that appear in the tt(IFS) are called em(IFS white space). One or more IFS white space characters or one non-IFS white space character together with any adjacent IFS white space character delimit a field. If an IFS white space character appears twice consecutively -in the IFS, this character is treated as if it were not an IFS white +in the tt(IFS), this character is treated as if it were not an IFS white space character. If the parameter is unset, the default is used. Note this has a different effect from setting the parameter to an empty string. + +If tt(MULTIBYTE) option is on and tt(IFS) contains invalid characters in +the current locale, it is reset to the default. ) vindex(KEYBOARD_HACK) item(tt(KEYBOARD_HACK))( diff --git a/Src/params.c b/Src/params.c index 2b0837e03..f5750a4b4 100644 --- a/Src/params.c +++ b/Src/params.c @@ -4748,6 +4748,7 @@ setlang(char *x) if ((x = getsparam_u(ln->name)) && *x) setlocale(ln->category, x); unqueue_signals(); + inittyptab(); } /**/ @@ -4771,6 +4772,7 @@ lc_allsetfn(Param pm, char *x) else { setlocale(LC_ALL, unmeta(x)); clear_mbstate(); + inittyptab(); } } @@ -4809,6 +4811,7 @@ lcsetfn(Param pm, char *x) } unqueue_signals(); clear_mbstate(); /* LC_CTYPE may have changed */ + inittyptab(); } #endif /* USE_LOCALE */ diff --git a/Src/utils.c b/Src/utils.c index f13e3a79d..94a33453f 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -74,9 +74,6 @@ set_widearray(char *mb_array, Widechar_array wca) } wca->len = 0; - if (!isset(MULTIBYTE)) - return; - if (mb_array) { VARARR(wchar_t, tmpwcs, strlen(mb_array)); wchar_t *wcptr = tmpwcs; @@ -87,8 +84,7 @@ set_widearray(char *mb_array, Widechar_array wca) int mblen; if ((unsigned char) *mb_array <= 0x7f) { - mb_array++; - *wcptr++ = (wchar_t)*mb_array; + *wcptr++ = (wchar_t)*mb_array++; continue; } @@ -4121,8 +4117,9 @@ inittyptab(void) * having IIDENT here is a good idea at all, but this code * should disappear into history... */ - for (t0 = 0240; t0 != 0400; t0++) - typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD; + if isset(MULTIBYTE) + for (t0 = 0240; t0 != 0400; t0++) + typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD; #endif /* typtab['.'] |= IIDENT; */ /* Allow '.' in variable names - broken */ typtab['_'] = IIDENT | IUSER; @@ -4137,11 +4134,24 @@ inittyptab(void) typtab[t0] |= ITOK | IMETA; for (t0 = (int) (unsigned char) Snull; t0 <= (int) (unsigned char) Nularg; t0++) typtab[t0] |= ITOK | IMETA | INULL; - for (s = ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ? - DEFAULT_IFS_SH : DEFAULT_IFS; *s; s++) { + /* ifs */ +#define CURRENT_DEFAULT_IFS (EMULATION(EMULATE_KSH|EMULATE_SH) ? \ + DEFAULT_IFS_SH : DEFAULT_IFS) +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) { + set_widearray(ifs ? ifs : CURRENT_DEFAULT_IFS, &ifs_wide); + if (ifs && !ifs_wide.chars) { + zwarn("IFS has an invalid character; resetting IFS to default"); + zsfree(ifs); + ifs = ztrdup(CURRENT_DEFAULT_IFS); + set_widearray(ifs, &ifs_wide); + } + } +#endif + for (s = ifs ? ifs : CURRENT_DEFAULT_IFS; *s; s++) { int c = (unsigned char) (*s == Meta ? *++s ^ 32 : *s); #ifdef MULTIBYTE_SUPPORT - if (!isascii(c)) { + if (isset(MULTIBYTE) && !isascii(c)) { /* see comment for wordchars below */ continue; } @@ -4154,10 +4164,15 @@ inittyptab(void) } typtab[c] |= ISEP; } + /* wordchars */ +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) + set_widearray(wordchars, &wordchars_wide); +#endif for (s = wordchars ? wordchars : DEFAULT_WORDCHARS; *s; s++) { int c = (unsigned char) (*s == Meta ? *++s ^ 32 : *s); #ifdef MULTIBYTE_SUPPORT - if (!isascii(c)) { + if (isset(MULTIBYTE) && !isascii(c)) { /* * If we have support for multibyte characters, we don't * handle non-ASCII characters here; instead, we turn @@ -4170,11 +4185,6 @@ inittyptab(void) #endif typtab[c] |= IWORD; } -#ifdef MULTIBYTE_SUPPORT - set_widearray(wordchars, &wordchars_wide); - set_widearray(ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ? - DEFAULT_IFS_SH : DEFAULT_IFS, &ifs_wide); -#endif for (s = SPECCHARS; *s; s++) typtab[(unsigned char) *s] |= ISPECIAL; if (typtab_flags & ZTF_SP_COMMA) diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 2fd2f975f..0d44558a7 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -2280,6 +2280,27 @@ F:We do not care what $OLDPWD is, as long as it does not cause an error F:As of this writing, var=$@ and var="$@" with null IFS have unspecified F:behavior, see http://austingroupbugs.net/view.php?id=888 + ( + IFS=$'\x80' + if [[ $IFS = $' \t\n\0' ]]; then + echo OK # if $'\x80' is illegal (e.g. Linux) + else # otherwise (e.g. macOS), it should work as a separator + s=$'foo\x80\bar' + [[ ${${=s}[1]} = foo ]] && echo OK + fi + ) +0D:reset IFS to default if it contains illegal character +>OK + + ( + unsetopt multibyte + IFS=$'\xc3\xa9' + s=$'foo\xc3bar\xa9boo' + echo ${${=s}[2]} + ) +0:eight bit chars in IFS should work if multibute option is off +>bar + () { setopt localoptions extendedglob [[ $- = [[:alnum:]]## ]] || print Failed 1 -- cgit v1.2.3 From a84fdd7c8f77935ecce99ff2b0bdba738821ed79 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Mon, 26 Jun 2023 17:13:04 +0900 Subject: 51889: fix module loading problem with full RELRO If full RELRO (relocation read-only, one of the security enhancement methods for ELF-based systems) is used when building zsh (as in binary packages of most Linuxes), loading a module (e.g. zsh/zftp) fails unless all the modules it depends on are already loaded. With this patch the necessary modules are automatically loaded. --- ChangeLog | 4 ++++ Src/Modules/zftp.c | 2 +- Src/mkbltnmlst.sh | 24 ++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 51a091aff..6a2801e9b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-06-26 Jun-ichi Takimoto + * 51889: Src/Modules/zftp.c, Src/mkbltnmlst.sh: enable loading a + module (e.g. zftp) that depends on other modules even if zsh is + built with the full RELRO + * 51884: Doc/Zsh/params.yo, Src/params.c, Src/utils.c, Test/D04parameter.ztst: if MULTIBYTE option is on and IFS contains invalid bytes in curret locale then reset it to default diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index 49b3ffa89..47a5e9de9 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -3172,7 +3172,7 @@ static struct features module_features = { int setup_(UNUSED(Module m)) { - return (require_module("zsh/net/tcp", NULL, 0) == 1); + return 0; } /**/ diff --git a/Src/mkbltnmlst.sh b/Src/mkbltnmlst.sh index c4611d8b3..067ecdaf9 100644 --- a/Src/mkbltnmlst.sh +++ b/Src/mkbltnmlst.sh @@ -76,6 +76,30 @@ for x_mod in $x_mods; do test "x$linked" = xno && echo "#endif" done +# if dynamic module 'mod' with load=no has moddeps in its .mdd, +# then output add_dep(mod, dep) for each 'dep' in moddeps. +dyn_mods="`grep ' link=dynamic .* load=no ' $CFMOD | \ + sed -e '/^#/d' -e 's/ .*/ /' -e 's/^name=/ /'`" + +for mod in $dyn_mods; do + modfile="`grep '^name='$mod' ' $CFMOD | \ + sed -e 's/^.* modfile=//' -e 's/ .*//'`" + if test "x$modfile" = x; then + echo >&2 "WARNING: no name for \`$mod' in $CFMOD (ignored)" + continue + fi + unset moddeps + . $srcdir/../$modfile + if test -n "$moddeps"; then + echo '#ifdef DYNAMIC' + echo "/* non-linked-in known module \`$mod' */" + for dep in $moddeps; do + echo " add_dep(\"$mod\", \"$dep\");" + done + echo '#endif' + fi +done + echo done_mods=" " for bin_mod in $bin_mods; do -- 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') 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 03695f4b5819d5f20ad0ad241d9255ba8cbd8e91 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 20 Jul 2023 10:46:14 +0100 Subject: 51977: PIPEFAIL interaction with ERREXIT / ERRRETURN Ensure the list-level error handling code is executed if we detect pipe failure for a foreground job. Add tests. --- ChangeLog | 6 ++++++ Src/jobs.c | 16 ++++++++++----- Test/E01options.ztst | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 5 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 7d24a22dd..8ecc30f20 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2023-07-20 Peter Stephenson + + * 51977: Src/jobs.c, Test/E01options.ztst: Combination of + PIPEFAIL and ERRRETURN / ERREXIT options failed with complex + commands at end of pipeline. + 2023-07-19 dana * github #99: mirsella: Completion/Darwin/Command/_trash, diff --git a/Src/jobs.c b/Src/jobs.c index dd7bba405..a3b9f667a 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -427,11 +427,17 @@ storepipestats(Job jn, int inforeground, int fixlastval) } if (fixlastval) { - if (jn->stat & STAT_CURSH) { - if (!lastval && isset(PIPEFAIL)) - lastval = pipefail; - } else if (isset(PIPEFAIL)) - lastval = pipefail; + if (jn->stat & STAT_CURSH) { + if (!lastval && isset(PIPEFAIL)) { + if (inforeground) + this_noerrexit = 0; + lastval = pipefail; + } + } else if (isset(PIPEFAIL)) { + if (inforeground) + this_noerrexit = 0; + lastval = pipefail; + } } } diff --git a/Test/E01options.ztst b/Test/E01options.ztst index 533e08773..83f0371a1 100644 --- a/Test/E01options.ztst +++ b/Test/E01options.ztst @@ -1379,6 +1379,64 @@ F:Regression test for workers/41811 >1 >2 + pipefailfn1() { + emulate -L zsh + setopt errreturn pipefail + false | { true; } + print "Shouldn't get here, status $?" + } + pipefailfn1 +1:PIPE_FAIL causes ERR_RETURN with complex end of pipeline: braces + + pipefailfn2() { + emulate -L zsh + setopt errreturn pipefail + false | if true; then true; fi + print "Shouldn't get here, status $?" + } + pipefailfn2 || print Function failed, as expected +0:PIPE_FAIL causes ERR_RETURN with complex end of pipeline: if +>Function failed, as expected + + pipefailfn3() { + emulate -L zsh + setopt errreturn pipefail + false | while true; do break; done + print "Shouldn't get here, status $?" + } + pipefailfn3 || print Function failed, as expected +0:PIPE_FAIL causes ERR_RETURN with complex end of pipeline: while +>Function failed, as expected + + pipefailfn4() { + emulate -L zsh + setopt errreturn pipefail + false | true + print "Shouldn't get here, status $?" + } + pipefailfn4 +1:PIPE_FAIL causes ERR_RETURN in simple case + + pipefailfn5() { + emulate -L zsh + setopt errreturn pipefail + false | { true | true; } + print "Shouldn't get here, status $?" + } + pipefailfn5 || print Function failed as expected +0:PIPE_FAIL causes ERR_RETURN with nested successful pipe +>Function failed as expected + + pipefailfn6() { + emulate -L zsh + setopt errreturn pipefail + false | { false | true; } + print "Shouldn't get here, status $?" + } + pipefailfn6 || print Function failed as expected +0:PIPE_FAIL causes ERR_RETURN with nested failed pipe +>Function failed as expected + for (( i = 0; i < 10; i++ )); do () { print $i -- cgit v1.2.3 From fb5a6a871c718bbe6b841d1a2b6c5fd2d7859ce8 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 26 Jul 2023 19:49:50 -0700 Subject: 51950 (tweak per 51949): correct Thingy refcount in raw_getbyte() --- ChangeLog | 3 +++ Src/Zle/zle_main.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index ab329c767..a6b7905fe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-07-26 Bart Schaefer + * 51950 (tweak per 51949): Src/Zle/zle_main.c: correct Thingy + refcount in raw_getbyte() + * Shohei YOSHIDA: 51927: Completion/Unix/Command/_watch: Update procps watch completion for version 4.0.3 diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 4a6c02133..1afb1bf58 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -737,7 +737,7 @@ raw_getbyte(long do_keytmout, char *cptr, int full) ) { /* Handle the fd. */ char *fdbuf; - Thingy save_lbindk = lbindk; + Thingy save_lbindk = refthingy(lbindk); { char buf[BDIGBUFSIZE]; convbase(buf, lwatch_fd->fd, 10); @@ -780,6 +780,7 @@ raw_getbyte(long do_keytmout, char *cptr, int full) */ errtry = 1; } + unrefthingy(lbindk); lbindk = save_lbindk; } } -- 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') 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') 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 aa8e4a02904b3a1c4b3064eb7502d887f7de958b Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 1 Aug 2023 14:32:55 +0100 Subject: 52008: Pattern bug with branches + exclusion Add tests. --- ChangeLog | 5 +++++ Src/pattern.c | 22 ++++++++++++++++++++-- Test/D02glob.ztst | 26 ++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 372092a32..8e6e3fb18 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-08-01 Peter Stephenson + + * 52008: Src/pattern.c, Test/D02glob.ztst: Fix bug with branches + in patterns followed by an exculsion, and add tests. + 2023-07-31 dana * github #100: HexorCatZ: Completion/Unix/Command/_qemu: diff --git a/Src/pattern.c b/Src/pattern.c index 3edda1772..2a1a514fb 100644 --- a/Src/pattern.c +++ b/Src/pattern.c @@ -2987,14 +2987,15 @@ patmatch(Upat prog) case P_EXCSYNC: /* See the P_EXCLUDE code below for where syncptr comes from */ { - unsigned char *syncptr; + unsigned char *syncstart, *syncptr, *ptr; Upat after; after = P_OPERAND(scan); DPUTS(!P_ISEXCLUDE(after), "BUG: EXCSYNC not followed by EXCLUDE."); DPUTS(!P_OPERAND(after)->p, "BUG: EXCSYNC not handled by EXCLUDE"); - syncptr = P_OPERAND(after)->p + (patinput - patinstart); + syncstart = P_OPERAND(after)->p; + syncptr = syncstart + (patinput - patinstart); /* * If we already matched from here, this time we fail. * See WBRANCH code for story about error count. @@ -3009,6 +3010,23 @@ patmatch(Upat prog) * failed anyway. */ *syncptr = errsfound + 1; + /* + * Because of backtracking, any match before this point + * can't apply to the current branch we're on so is now + * a failure --- this can happen if, on a previous + * branch, we initially marked a success before failing + * on a later part of the pattern after marking up the + * P_EXCSYNC (even an end anchor will have this effect). + * To make sure we record the current match point + * correctly, mark those down now. + * + * This might have side effects on the efficiency of + * pathological cases involving nested branches. To + * fix that we'd probably need to record matches on + * different branches separately. + */ + for (ptr = syncstart; ptr < syncptr; ++ptr) + *ptr = 0; } break; case P_EXCEND: diff --git a/Test/D02glob.ztst b/Test/D02glob.ztst index 850a535e5..4d88e5c27 100644 --- a/Test/D02glob.ztst +++ b/Test/D02glob.ztst @@ -817,6 +817,32 @@ *>*/glob.tmp/(flip|flop) *>*/glob.tmp/(flip|flop)/trailing/components +# The following set test an obscure problem with branches followed by +# exclusions that shows up when the exclusion matches against +# something other than the complete test string, hence the complicated +# double negative. + [[ ab = (|a*)~^(*b) ]] +0:Regression test for exclusion after branches: empty first alternative + + [[ ab = (b|a*)~^(*b) ]] +0:Regression test for exclusion after branches: non-empty first alternative + + [[ ab = (b*|a*)~^(*b) ]] +0:Regression test for exclusion after branches: full length first alternative + +# Corresponding tests where the exclusion should succeed, so the +# match fails. It's hard to know how to provoke bugs here... + [[ abc = (|a*)~^(*b) ]] +1:Regression test for exclusion after branches: failure case 1 + + [[ abc = (b|a*)~^(*b) ]] +1:Regression test for exclusion after branches: failure case 2 + + [[ abc = (b*|a*)~^(*b) ]] +1:Regression test for exclusion after branches: failure case 3 + +# Careful: extendedglob off from this point. + unsetopt extendedglob print -r -- ${(*)=${(@s.+.):-A+B}/(#b)(?)/-${(L)match[1]} ${match[1]}} 0:the '*' qualfier enables extended_glob for pattern matching -- cgit v1.2.3 From 094f230e36a4de266921ed21c0d8f3f37e634073 Mon Sep 17 00:00:00 2001 From: Nojus Gudinavičius Date: Mon, 21 Aug 2023 15:48:06 +0300 Subject: users/29175: Don't need to forget zle edits if none --- ChangeLog | 6 ++++++ Src/Zle/zle_hist.c | 12 ++++++++++++ 2 files changed, 18 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index fd3c4e85d..3849db569 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2023-08-22 Peter Stephenson + + * Nojus Gudinavičius: users/29175: Src/Zle/zle_hist.c: don't + need to forget edits if none were made, avoiding loop over + entire history. + 2023-08-21 Jun-ichi Takimoto * Shohei YOSHIDA: 52059 (+52070): Completion/Unix/Command/_scons: diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c index cfaa70dae..0fdad70d9 100644 --- a/Src/Zle/zle_hist.c +++ b/Src/Zle/zle_hist.c @@ -68,6 +68,13 @@ Keymap isearch_keymap; */ #define GETZLETEXT(ent) ((ent)->zle_text ? (ent)->zle_text : (ent)->node.nam) +/* + * Flag that edits have been made to a zle line. + * If not set, nothing to forget. + */ +/**/ +int have_edits = 0; + /**/ void remember_edits(void) @@ -81,6 +88,7 @@ remember_edits(void) if (ent->zle_text) free(ent->zle_text); ent->zle_text = zlemetaline ? ztrdup(line) : line; + have_edits = 1; } else if (!zlemetaline) free(line); } @@ -90,6 +98,10 @@ remember_edits(void) void forget_edits(void) { + if (!have_edits) { + return; + } + have_edits = 0; Histent he; for (he = hist_ring; he; he = up_histent(he)) { -- cgit v1.2.3 From 97b4a30c4e5f4837bac7c5c67bd583d3aeaf7886 Mon Sep 17 00:00:00 2001 From: Robert Woods Date: Sun, 27 Aug 2023 15:05:08 -0700 Subject: 52053: whitelist capability CAP_WAKE_ALARM Since the systemd update v254 from July 28, 2023, the capability 'CAP_WAKE_ALARM' is passed by default to some user process (especially desktop managers). Since 'CAP_WAKE_ALARM' is very narrow in focus, it is preferable that zsh does not consider it as a 'privileged' capability. --- ChangeLog | 3 +++ Src/utils.c | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index a70adefdd..bb6afe127 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-08-27 Bart Schaefer + * Robert Woods: 52053: Src/utils.c: whitelist capability + CAP_WAKE_ALARM in 'privasserted' function + * Shohei YOSHIDA: 52034: Completion/Unix/Command/_sqlite: update for version 3.42.0 diff --git a/Src/utils.c b/Src/utils.c index 94a33453f..7040d0954 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -7551,9 +7551,9 @@ privasserted(void) /* POSIX doesn't define a way to test whether a capability set * * is empty or not. Typical. I hope this is conforming... */ cap_flag_value_t val; - cap_value_t n; - for(n = 0; !cap_get_flag(caps, n, CAP_EFFECTIVE, &val); n++) - if(val) { + cap_value_t cap; + for(cap = 0; !cap_get_flag(caps, cap, CAP_EFFECTIVE, &val); cap++) + if(val && cap != CAP_WAKE_ALARM) { cap_free(caps); return 1; } -- cgit v1.2.3 From 9ff1b2810e70658b2364b737478e9a996de0a644 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 3 Sep 2023 11:42:00 -0700 Subject: users/29220: fix bug with assignment to private following explicit unset --- ChangeLog | 5 +++++ Src/Modules/param_private.c | 29 ++++++++++++++++++----------- Test/V10private.ztst | 7 +++++++ 3 files changed, 30 insertions(+), 11 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 356d7a48c..1fc00562c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-09-03 Bart Schaefer + + * users/29220: Src/Modules/param_private.c, Test/V10private.ztst: + fix bug with assignment to private following explicit unset + 2023-08-28 Jun-ichi Takimoto * Shohei YOSHIDA: 52098(+comment), 52099, 52100, 52105(+52106): diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index e43f0edb4..8e04b2b95 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -230,7 +230,9 @@ setfn_error(Param pm) * calling the original unsetfn. This assures that if the old unsetfn * wants to use its getfn or setfn, they're unconditionally present. * The "explicit" flag indicates that "unset" was called, if zero the - * parameter is going out of scope (see params.c). + * parameter is going out of scope (see params.c). PM_DECLARED is + * asserted as if TYPESET_TO_UNSET were in use so that the private + * parameter is re-used rather than re-created when assigned again. * */ @@ -268,9 +270,10 @@ pps_unsetfn(Param pm, int explicit) pm->gsu.s = gsu; if (locallevel <= pm->level) gsu->unsetfn(pm, explicit); - if (explicit) + if (explicit) { + pm->node.flags |= PM_DECLARED; pm->gsu.s = (GsuScalar)c; - else + } else zfree(c, sizeof(struct gsu_closure)); } @@ -307,9 +310,10 @@ ppi_unsetfn(Param pm, int explicit) pm->gsu.i = gsu; if (locallevel <= pm->level) gsu->unsetfn(pm, explicit); - if (explicit) + if (explicit) { + pm->node.flags |= PM_DECLARED; pm->gsu.i = (GsuInteger)c; - else + } else zfree(c, sizeof(struct gsu_closure)); } @@ -346,9 +350,10 @@ ppf_unsetfn(Param pm, int explicit) pm->gsu.f = gsu; if (locallevel <= pm->level) gsu->unsetfn(pm, explicit); - if (explicit) + if (explicit) { + pm->node.flags |= PM_DECLARED; pm->gsu.f = (GsuFloat)c; - else + } else zfree(c, sizeof(struct gsu_closure)); } @@ -386,9 +391,10 @@ ppa_unsetfn(Param pm, int explicit) pm->gsu.a = gsu; if (locallevel <= pm->level) gsu->unsetfn(pm, explicit); - if (explicit) + if (explicit) { + pm->node.flags |= PM_DECLARED; pm->gsu.a = (GsuArray)c; - else + } else zfree(c, sizeof(struct gsu_closure)); } @@ -427,9 +433,10 @@ pph_unsetfn(Param pm, int explicit) pm->gsu.h = gsu; if (locallevel <= pm->level) gsu->unsetfn(pm, explicit); - if (explicit) + if (explicit) { + pm->node.flags |= PM_DECLARED; pm->gsu.h = (GsuHash)c; - else + } else zfree(c, sizeof(struct gsu_closure)); } diff --git a/Test/V10private.ztst b/Test/V10private.ztst index b191afcb7..b876f548d 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -377,6 +377,13 @@ F:Should we allow "public" namerefs to private parameters? *?*no such variable: ptr1 *?*no such variable: ptr2 + () { + private x=1 + unset x + x=2 + } +0:regression test for unset private + %clean rm -r private.TMP -- cgit v1.2.3 From e3c2af216b1f77202ef5240179dabcf4aab66f91 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 5 Sep 2023 18:04:09 -0700 Subject: 52115: permit repeated "private" declarations as long as types aren't changed --- ChangeLog | 5 +++++ Src/Modules/param_private.c | 49 ++++++++++++++++++++++++++++++++++++++++++--- Test/V10private.ztst | 17 ++++++++++++++++ 3 files changed, 68 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 0c782f617..a4d2dfe2f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-09-05 Bart Schaefer + + * 52115: Src/Modules/param_private.c, Test/V10private.ztst: permit + repeated "private" declarations as long as types aren't changed + 2023-09-04 Jun-ichi Takimoto * 52112: Completion/BSD/Command/_jexec, diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index 8e04b2b95..7ef6633da 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -87,9 +87,52 @@ makeprivate(HashNode hn, UNUSED(int flags)) ((pm->node.flags & (PM_SPECIAL|PM_REMOVABLE)) == PM_SPECIAL && /* typeset_single() line 2300 discards PM_REMOVABLE -- why? */ !is_private(pm->old))))) { - zwarnnam("private", "can't change scope of existing param: %s", - pm->node.nam); - makeprivate_error = 1; + if (is_private(pm->old)) { + if (pm->old->node.flags & PM_READONLY) { + zerr("read-only variable: %s", pm->node.nam); + makeprivate_error = 1; + } else if ((pm->node.flags | pm->old->node.flags) == + pm->old->node.flags) { + /* private called twice on same parameter */ + Param tpm = pm; + pm = pm->old; + --locallevel; + /* why have a union if we need this switch anyway? */ + switch (PM_TYPE(pm->node.flags)) { + case PM_SCALAR: + pm->gsu.s->setfn(pm, tpm->u.str); + tpm->u.str = NULL; + break; + case PM_INTEGER: + pm->gsu.i->setfn(pm, tpm->u.val); + break; + case PM_EFLOAT: + case PM_FFLOAT: + pm->gsu.f->setfn(pm, tpm->u.dval); + break; + case PM_ARRAY: + pm->gsu.a->setfn(pm, tpm->u.arr); + tpm->u.arr = NULL; + break; + case PM_HASHED: + pm->gsu.h->setfn(pm, tpm->u.hash); + tpm->u.hash = NULL; + break; + } + ++locallevel; + if (!(tpm->node.flags & PM_UNSET)) + pm->node.flags &= ~PM_UNSET; + } else { + zerrnam("private", + "can't change type of private param: %s", + pm->node.nam); + makeprivate_error = 1; + } + } else { + zerrnam("private", "can't change scope of existing param: %s", + pm->node.nam); + makeprivate_error = 1; + } return; } struct gsu_closure *gsu = zalloc(sizeof(struct gsu_closure)); diff --git a/Test/V10private.ztst b/Test/V10private.ztst index b876f548d..d902cac56 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -384,6 +384,23 @@ F:Should we allow "public" namerefs to private parameters? } 0:regression test for unset private + () { + private x=1 + unset x + private x=2 + print $x + } +0:private may be called twice +>2 + + () { + private x=1 + private -a x + print $x + } +1:private may not change parameter type +?(anon):private:2: can't change type of private param: x + %clean rm -r private.TMP -- cgit v1.2.3 From 96ce0abf6b7607ca2df64759c495d153132d07bd Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 10 Sep 2023 21:00:23 -0700 Subject: 52125: getoutput() must not free() after gettempname(..., 1) for heap --- ChangeLog | 4 ++++ Src/exec.c | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 72922feb4..c8bc5e30f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-09-10 Bart Schaefer + + * 52125: Src/exec.c: getoutput() must not free() gettempname() + 2023-09-09 Bart Schaefer * unposted: Completion/Base/Utility/_shadow: quoting for safety diff --git a/Src/exec.c b/Src/exec.c index 8f9d5a885..3a8b3e951 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -4897,7 +4897,6 @@ getoutputfile(char *cmd, char **eptr) if ((fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600)) < 0) { zerr("process substitution failed: %e", errno); - free(nam); if (!s) child_unblock(); return NULL; -- cgit v1.2.3 From 0eab788437d42afb290637441f88542fc496c307 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Wed, 13 Sep 2023 09:59:42 +0900 Subject: 52122 + 52129: fix (#) parameter expansion flag Without the X flag, null string "" is substituted for bad math expression. --- ChangeLog | 6 ++++++ Src/subst.c | 13 ++++++++++--- Test/D04parameter.ztst | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index c8bc5e30f..45cb416dc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2023-09-13 Jun-ichi Takimoto + + * 52122 + 52129: Src/subst.c, Test/D04parameter.ztst: fix (#) + parameter expansion flag for bad math expressions and out-of- + range characters + 2023-09-10 Bart Schaefer * 52125: Src/exec.c: getoutput() must not free() gettempname() diff --git a/Src/subst.c b/Src/subst.c index 14947ae36..d68159227 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1489,11 +1489,18 @@ subst_parse_str(char **sp, int single, int err) static char * substevalchar(char *ptr) { - zlong ires = mathevali(ptr); + zlong ires; int len = 0; + int saved_errflag = errflag; - if (errflag) - return NULL; + errflag = 0; + ires = mathevali(ptr); + + if (errflag) { /* not a valid numerical expression */ + errflag |= saved_errflag; + return noerrs ? dupstring(""): NULL; + } + errflag |= saved_errflag; #ifdef MULTIBYTE_SUPPORT if (isset(MULTIBYTE) && ires > 127) { /* '\\' + 'U' + 8 bytes of character + '\0' */ diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 0d44558a7..12ae1a446 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -2785,3 +2785,43 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888 >string with spaces >stringwithspaces >stringwithspaces + + : ${(#X):-@} +1:${(#X)...}: bad math expression +?(eval):1: bad math expression: illegal character: @ + + echo a${(#):-@}z +0:${(#)...}: bad math expression +>az + + printf "a%sz\n" ${(#):-@} +0:${(#)...}: bad math expression, printf +>az + + a=( '1 +' '@' ) + : ${(#X)a} +1:${(#X)...}: array of bad math expressions +?(eval):2: bad math expression: operand expected at end of string + + printf "a%sz\n" ${(#)a} +0:${(#)...}: array of bad math expressions, printf +>az + + : ${(#X):-0x80} +1:${(#X)...}: out-of-range character +?(eval):1: character not in range + + [[ ${(#):-0x80} = $'\x80' ]] && echo OK +0:${(#)...}: out-of-range character +>OK + + a=( 0x80 0x81 ) + : ${(#X)a} +1:${(#X)...}: array of out-of-range characters +?(eval):2: character not in range + + printf "%s\n" ${(#)a} | + while read x; do echo $(( #x )); done +0:${(#)...}: array of out-of-range characters +>128 +>129 -- cgit v1.2.3 From 355cfc1b95ca65e7ab9ada5b59a82ed4f651556f Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 16 Sep 2023 13:08:59 -0700 Subject: 52153: mapfile without HAVE_MMAP should not trim newlines --- ChangeLog | 5 +++++ Src/Modules/mapfile.c | 10 ++++------ Src/input.c | 44 +++++++++++++++++++++++++++++++++++++------- 3 files changed, 46 insertions(+), 13 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 978464688..27dcf0e58 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-09-16 Bart Schaefer + + * 52153: Src/input.c, Src/Modules/mapfile.c: $mapfile[fname] + should not trim newlines (only applies when not HAVE_MMAP) + 2023-09-15 Mikael Magnusson * 52142: Completion/Zsh/Context/_parameter, diff --git a/Src/Modules/mapfile.c b/Src/Modules/mapfile.c index dd86fb596..84cdfea18 100644 --- a/Src/Modules/mapfile.c +++ b/Src/Modules/mapfile.c @@ -170,6 +170,8 @@ get_contents(char *fname) #ifdef USE_MMAP caddr_t mmptr; struct stat sbuf; +#else + off_t size; #endif char *val; unmetafy(fname = ztrdup(fname), &fd); @@ -196,12 +198,8 @@ get_contents(char *fname) close(fd); #else /* don't USE_MMAP */ val = NULL; - if ((fd = open(fname, O_RDONLY | O_NOCTTY)) >= 0) { - LinkList ll; - - if ((ll = readoutput(fd, 1, 0))) - val = peekfirst(ll); - } + if ((size = zstuff(&val, fname)) > 0) + val = metafy(val, size, META_HEAPDUP); #endif /* USE_MMAP */ free(fname); return val; diff --git a/Src/input.c b/Src/input.c index 0da065e51..8d7f44d7c 100644 --- a/Src/input.c +++ b/Src/input.c @@ -610,11 +610,11 @@ inungetc(int c) } } -/* stuff a whole file into the input queue and print it */ +/* stuff a whole file into memory and return it */ /**/ -int -stuff(char *fn) +off_t +zstuff(char **out, const char *fn) { FILE *in; char *buf; @@ -622,20 +622,50 @@ stuff(char *fn) if (!(in = fopen(unmeta(fn), "r"))) { zerr("can't open %s", fn); - return 1; + return -1; } + queue_signals(); fseek(in, 0, SEEK_END); len = ftell(in); fseek(in, 0, SEEK_SET); buf = (char *)zalloc(len + 1); - if (!(fread(buf, len, 1, in))) { + if (len && !(fread(buf, len, 1, in))) { zerr("read error on %s", fn); fclose(in); - zfree(buf, len + 1); - return 1; + unqueue_signals(); + return -1; } fclose(in); buf[len] = '\0'; + *out = buf; + unqueue_signals(); + 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 */ + +/**/ +int +stuff(char *fn) +{ + char *buf; + off_t len = zstuff(&buf, fn); + + if (len < 0) + return 1; + fwrite(buf, len, 1, stderr); fflush(stderr); inputsetline(metafy(buf, len, META_REALLOC), INP_FREE); -- cgit v1.2.3 From 3aaef16569a6b9bd5ca0a2a323cc0643772f9c56 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 16 Sep 2023 17:34:39 -0700 Subject: 52154, 52155: Implement, document, and test non-forking command substitution. Comprises workers/51957, 51985, 51987, 51988, 51993, 52131, 52139, plus fixes for return values, parse errors, and trailing newlines (which were incorrectly removed) in ${ ... } --- ChangeLog | 6 +++ Doc/Zsh/expn.yo | 44 +++++++++++++--- Src/lex.c | 72 +++++++++++++++++++++----- Src/subst.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 254 insertions(+), 25 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 27dcf0e58..0390ea2b5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2023-09-16 Bart Schaefer + * 52155: Test/D10nofork.ztst: Tests for non-forking substitution. + + * 52154: Doc/Zsh/expn.yo, Src/lex.c, Src/subst.c: implement + and document non-forking command substitutions ${|...} and + ${ ... }. Based on Sebastian Gniazdowski: 51702. + * 52153: Src/input.c, Src/Modules/mapfile.c: $mapfile[fname] should not trim newlines (only applies when not HAVE_MMAP) diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index f87832e75..5be40bf25 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -1881,23 +1881,55 @@ sect(Command Substitution) cindex(command substitution) cindex(substitution, command) A command enclosed in parentheses preceded by a dollar sign, like -`tt($LPAR())...tt(RPAR())', or quoted with grave -accents, like `tt(`)...tt(`)', is replaced with its standard output, with -any trailing newlines deleted. -If the substitution is not enclosed in double quotes, the -output is broken into words using the tt(IFS) parameter. +`tt($LPAR())...tt(RPAR())', or quoted with grave accents, like +`tt(`)...tt(`)', is executed in a subshell and replaced by its +standard output, with any trailing newlines deleted. If the +substitution is not enclosed in double quotes, the output is broken +into words using the tt(IFS) parameter. vindex(IFS, use of) The substitution `tt($LPAR()cat) var(foo)tt(RPAR())' may be replaced by the faster `tt($LPAR()<)var(foo)tt(RPAR())'. In this case var(foo) undergoes single word shell expansions (em(parameter expansion), em(command substitution) and em(arithmetic expansion)), but not -filename generation. +filename generation. No subshell is created. If the option tt(GLOB_SUBST) is set, the result of any unquoted command substitution, including the special form just mentioned, is eligible for filename generation. +A command with a leading pipe character, enclosed in braces prefixed by +a dollar sign, as in `tt(${|)...tt(})', is executed in the current shell +context, rather than in a subshell, and is replaced by the value of the +parameter tt(REPLY) at the end of the command. There em(must not) be +any whitespace between the opening brace and the pipe character. Any +prior value of tt($REPLY) is saved and restored around this substitution, +in the manner of a function local parameter. Other parameters declared +within the substitution also behave as locals, as if in a function, +unless `tt(typeset -g)' is used. Trailing newlines are em(not) deleted +from the final replacement in this case, and it is subject to filename +generation in the same way as `tt($LPAR())...tt(RPAR())' but is em(not) +split on tt(IFS) unless the tt(SH_WORD_SPLIT) option is set. + +Substitutions of the form `tt(${|)var(param)tt(|)...tt(})' are similar, +except that the substitution is replaced by the value of the parameter +named by var(param). No implicit save or restore applies to var(param) +except as noted for tt(REPLY), and var(param) should em(not) be declared +within the command. If var(param) names an array, array expansion rules +apply. + +A command enclosed in braces preceded by a dollar sign, and set off from +the braces by whitespace, like `tt(${ )...tt( })', is replaced by its +standard output. Like `tt(${|)...tt(})' and unlike +`tt($LPAR())...tt(RPAR())', the command executes in the current shell +context with function local behaviors and does not create a subshell. + +Note that because the `tt(${|)...tt(})' and `tt(${ )...tt( })' forms +must be parsed at once as both string tokens and commands, all other +braces (`tt({)' or `tt(})') within the command either must be quoted, +or must appear in syntactically valid pairs, such as around complex +commands, function bodies, or parameter references. + texinode(Arithmetic Expansion)(Brace Expansion)(Command Substitution)(Expansion) sect(Arithmetic Expansion) cindex(arithmetic expansion) diff --git a/Src/lex.c b/Src/lex.c index 2f7937410..33b17cc95 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -937,7 +937,7 @@ static enum lextok gettokstr(int c, int sub) { int bct = 0, pct = 0, brct = 0, seen_brct = 0, fdpar = 0; - int intpos = 1, in_brace_param = 0; + int intpos = 1, in_brace_param = 0, cmdsubst = 0; int inquote, unmatched = 0; enum lextok peek; #ifdef DEBUG @@ -1135,7 +1135,7 @@ gettokstr(int c, int sub) c = Inpar; break; case LX2_INBRACE: - if (isset(IGNOREBRACES) || sub) + if ((isset(IGNOREBRACES) && !cmdsubst) || sub) c = '{'; else { if (!lexbuf.len && incmdpos) { @@ -1157,8 +1157,11 @@ gettokstr(int c, int sub) if (in_brace_param) { cmdpop(); } - if (bct-- == in_brace_param) - in_brace_param = 0; + if (bct-- == in_brace_param) { + if (cmdsubst) + cmdpop(); + in_brace_param = cmdsubst = 0; + } c = Outbrace; break; case LX2_COMMA: @@ -1405,16 +1408,24 @@ gettokstr(int c, int sub) } add(c); c = hgetc(); - if (intpos) + if (intpos) intpos--; - if (lexstop) + if (lexstop) break; + if (!cmdsubst && in_brace_param && act == LX2_STRING && + (c == '|' || c == Bar || inblank(c))) { + cmdsubst = in_brace_param; + cmdpush(CS_CURSH); + } } brk: if (errflag) { if (in_brace_param) { - while(bct-- >= in_brace_param) + while(bct >= in_brace_param) { + if (bct-- == cmdsubst) + cmdpop(); cmdpop(); + } } return LEXERR; } @@ -1422,8 +1433,11 @@ gettokstr(int c, int sub) if (unmatched && !(lexflags & LEXFLAGS_ACTIVE)) zerr("unmatched %c", unmatched); if (in_brace_param) { - while(bct-- >= in_brace_param) + while(bct >= in_brace_param) { + if (bct-- == cmdsubst) + cmdpop(); cmdpop(); + } zerr("closing brace expected"); } else if (unset(IGNOREBRACES) && !sub && lexbuf.len > 1 && peek == STRING && lexbuf.ptr[-1] == '}' && @@ -1459,8 +1473,8 @@ gettokstr(int c, int sub) static int dquote_parse(char endchar, int sub) { - int pct = 0, brct = 0, bct = 0, intick = 0, err = 0; - int c; + int pct = 0, brct = 0, bct = 0, intick = 0, err = 0, cmdsubst = 0; + int c, bskip = 0; int math = endchar == ')' || endchar == ']' || infor; int zlemath = math && zlemetacs > zlemetall + addedx - inbufct; @@ -1529,11 +1543,25 @@ dquote_parse(char endchar, int sub) c = Qstring; } break; + case '{': + if (cmdsubst && !intick) { + /* In nofork substitution, tokenize as if unquoted */ + c = Inbrace; + bskip++; + } + break; case '}': if (intick || !bct) break; c = Outbrace; - bct--; + if (bskip) { + bskip--; + break; + } + if (bct-- == cmdsubst) { + cmdsubst = 0; + cmdpop(); + } cmdpop(); break; case '`': @@ -1588,14 +1616,34 @@ dquote_parse(char endchar, int sub) if (err || lexstop) break; add(c); + if (!cmdsubst && c == Inbrace) { + /* Check for ${|...} nofork command substitution */ + if ((c = hgetc()) && !lexstop) { + if (c == '|' || inblank(c)) { + cmdsubst = bct; + cmdpush(CS_CURSH); + } + hungetc(c); + } + } } if (intick == 2) ALLOWHIST if (intick) { cmdpop(); } - while (bct--) + while (bct) { + if (bct-- == cmdsubst) { + /* + * You would think this is an error, but if we call it one, + * parsestrnoerr() returns nonzero to subst_parse_str() and + * subsequently "bad substitution" is not reported + */ + /* err = 1 */ + cmdpop(); + } cmdpop(); + } if (lexstop) err = intick || endchar || err; else if (err == 1) { diff --git a/Src/subst.c b/Src/subst.c index d68159227..52afd6484 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1867,6 +1867,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * joining the array into a string (for compatibility with ksh/bash). */ int quoted_array_with_offset = 0; + /* Indicates ${|...;} */ + char *rplyvar = NULL; + /* Indicates ${ ... ;} */ + char *rplytmp = NULL; *s++ = '\0'; /* @@ -1894,8 +1898,147 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * flags in parentheses, but also one ksh hack. */ if (c == Inbrace) { + /* The command string to be run by ${|...;} */ + char *cmdarg = NULL; + size_t slen = 0; inbrace = 1; s++; + + /* Short-path for the nofork command substitution ${|cmd;} + * See other comments about kludges for why this is here. + * + * The command string is extracted and executed, and the + * substitution assigned. There's no (...)-flags processing, + * i.e. no ${|(U)cmd;}, because it looks quite awful and + * should not be part of command substitution in any case. + * Use ${(U)${|cmd;}} as you would for ${(U)$(cmd;)}. + */ + if (*s == '|' || *s == Bar || inblank(*s)) { + char *outbracep = s; + char sav = *s; + *s = Inbrace; + if (skipparens(Inbrace, Outbrace, &outbracep) == 0) { + slen = outbracep - s - 1; + if ((*s = sav) != Bar) { + sav = *outbracep; + *outbracep = '\0'; + tokenize(s); + *outbracep = sav; + } + } + } + if (slen > 1) { + char *outbracep = s + slen; + if (*outbracep == Outbrace) { + if ((rplyvar = itype_end(s+1, INAMESPC, 0))) { + if (*rplyvar == Inbrack && + (rplyvar = parse_subscript(++rplyvar, 1, ']'))) + ++rplyvar; + } + if (rplyvar == s+1 && *rplyvar == Bar) { + /* Is ${||...} a subtitution error or a syntax error? + zerr("bad substitution"); + return NULL; + */ + rplyvar = NULL; + } + if (rplyvar && *rplyvar == Bar) { + cmdarg = dupstrpfx(rplyvar+1, outbracep-rplyvar-1); + rplyvar = dupstrpfx(s+1,rplyvar-s-1); + } else { + cmdarg = dupstrpfx(s+1, outbracep-s-1); + rplyvar = "REPLY"; + } + if (inblank(*s)) { + /* + * Admittedly a hack. Take advantage of the enforced + * locality of REPLY and the semantics of $(level = locallevel; + /* if (rplyval) setsparam("REPLY", ztrdup(rplyval)); */ + } + + if (rplyvar && cmdarg && *cmdarg) { + int obreaks = breaks; + Eprog cmdprog; + /* Execute the shell command */ + untokenize(cmdarg); + cmdprog = parse_string(cmdarg, 0); + if (cmdprog) { + execode(cmdprog, 1, 0, "cmdsubst"); + cmdoutval = lastval; + /* "return" behaves as if in a function */ + if (retflag) { + retflag = 0; + breaks = obreaks; /* Is this ever not zero? */ + } + } else /* parse error */ + errflag |= ERRFLAG_ERROR; + if (rplytmp && !errflag) { + int onoerrs = noerrs; + noerrs = 2; + if ((cmdarg = ztuff(rplytmp))) + setsparam("REPLY", cmdarg); + noerrs = onoerrs; + } + } + + if (rplytmp) + unlink(rplytmp); + if (rplyvar) { + if (strcmp(rplyvar, "REPLY") == 0) { + if ((val = dupstring(getsparam("REPLY")))) + vunset = 0; + else { + vunset = 1; + val = dupstring(""); + } + } else { + s = dyncat(rplyvar, s); + rplyvar = NULL; + } + endparamscope(); + if (exit_pending) { + if (mypid == getpid()) { + /* + * paranoia: don't check for jobs, but there + * shouldn't be any if not interactive. + */ + stopmsg = 1; + zexit(exit_val, ZEXIT_NORMAL); + } else + _exit(exit_val); + } + } + /* * In ksh emulation a leading `!' is a special flag working * sort of like our (k). This is true only for arrays or @@ -2590,14 +2733,14 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * we let fetchvalue set the main string pointer s to * the end of the bit it's fetched. */ - if (!(v = fetchvalue(&vbuf, (subexp ? &ov : &s), - (wantt ? -1 : - ((unset(KSHARRAYS) || inbrace) ? 1 : -1)), - scanflags)) || - (v->pm && (v->pm->node.flags & PM_UNSET)) || - (v->flags & VALFLAG_EMPTY)) + if (!rplyvar && + (!(v = fetchvalue(&vbuf, (subexp ? &ov : &s), + (wantt ? -1 : + ((unset(KSHARRAYS) || inbrace) ? 1 : -1)), + scanflags)) || + (v->pm && (v->pm->node.flags & PM_UNSET)) || + (v->flags & VALFLAG_EMPTY))) vunset = 1; - if (wantt) { /* * Handle the (t) flag: value now becomes the type -- cgit v1.2.3 From 1becbba0b614c9ef17ba28aa51c4e8e7d21f1e0f Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 16 Sep 2023 20:51:27 -0700 Subject: users/29160, workers/52156: Fix repetition of substitution modifier. --- ChangeLog | 4 ++++ Src/subst.c | 6 ++++-- Test/E01options.ztst | 2 ++ 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 23e7ef6d3..4356a564e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-09-16 Bart Schaefer + * 52156: Test/E01options.ztst: Test case for user/29160. + + * users/29160: Src/subst.c: Fix repetition of substituion modifier. + * Christoffer Lundell: 52082: Functions/Zle/edit-command-line: Enable linewise edit-command when in visual-line mode. diff --git a/Src/subst.c b/Src/subst.c index 52afd6484..dc2052ee0 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -4689,7 +4689,8 @@ modify(char **str, char **ptr, int inbrace) case 'S': hsubpatopt = (c == 'S'); if (hsubl && hsubr) - subst(©, hsubl, hsubr, gbal, hsubpatopt); + subst(©, dupstring(hsubl), dupstring(hsubr), + gbal, hsubpatopt); break; case 'q': copy = quotestring(copy, QT_BACKSLASH_SHOWNULL); @@ -4777,7 +4778,8 @@ modify(char **str, char **ptr, int inbrace) case 'S': hsubpatopt = (c == 'S'); if (hsubl && hsubr) - subst(str, hsubl, hsubr, gbal, hsubpatopt); + subst(str, dupstring(hsubl), dupstring(hsubr), + gbal, hsubpatopt); break; case 'q': *str = quotestring(*str, QT_BACKSLASH); diff --git a/Test/E01options.ztst b/Test/E01options.ztst index 83f0371a1..363846f5c 100644 --- a/Test/E01options.ztst +++ b/Test/E01options.ztst @@ -561,12 +561,14 @@ foo=(one.c two.c three.c) print ${foo:s/#%(#b)t(*).c/T${match[1]}.X/} print *(#q:s/#(#b)tmp(*e)/'scrunchy${match[1]}'/) + print ${${:-"left[({})]over"}:fs/(\\{\\}|\\(\\)|\\[\\])//} unsetopt histsubstpattern 0:HIST_SUBST_PATTERN option >TINGcd TINGfile1 TINGfile2 homedir >THUMPcd THUMPfile1 THUMPfile2 >one.c Two.X Three.X >homedir scrunchyfile1 scrunchyfile2 tmpcd +>leftover setopt ignorebraces echo X{a,b}Y -- cgit v1.2.3 From b35799269750de2801f8bf292f4374c2fa0f7a09 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Fri, 22 Sep 2023 20:29:40 -0500 Subject: 52176: metafy return from ${ ... } substitution --- ChangeLog | 5 +++++ Src/subst.c | 7 ++++--- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index e12ed92e0..e4965b804 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-09-22 Bart Schaefer + + * 52176: Src/subst.c: metafy return from ${ ... } substitution + (adapted from Jun T.: 52172) + 2023-09-20 Oliver Kiddle * Jörg Sommer: 51747: Functions/Misc/run-help-ip: diff --git a/Src/subst.c b/Src/subst.c index dc2052ee0..f37ae935e 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -2003,11 +2003,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, } else /* parse error */ errflag |= ERRFLAG_ERROR; if (rplytmp && !errflag) { - int onoerrs = noerrs; + int onoerrs = noerrs, rplylen; noerrs = 2; - if ((cmdarg = ztuff(rplytmp))) - setsparam("REPLY", cmdarg); + rplylen = zstuff(&cmdarg, rplytmp); noerrs = onoerrs; + if (rplylen >= 0) + setsparam("REPLY", metafy(cmdarg, rplylen, META_REALLOC)); } } -- cgit v1.2.3 From 02e33c54d85208c7d9b96d91a26d04069ff19ed2 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Wed, 27 Sep 2023 01:56:47 +0900 Subject: 52169: a few more improvements of (#) flag fix (#X) in C locale in FreeBSD, DragonFly, NetBSD. Negative values such as ${(#X):--1} are now error. UCS4 is limited to < 0x8000_0000 (in OSes without __STDC_ISO_10646__). --- ChangeLog | 4 ++ Src/subst.c | 15 ++-- Src/utils.c | 230 ++++++++++++++++++++++++++++++------------------------------ 3 files changed, 125 insertions(+), 124 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 0f3c9402d..506635756 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-09-27 Jun-ichi Takimoto + + * 52169: Src/subst.c, Src/utils.c: a few more improvemets of (#) + 2023-09-23 Bart Schaefer * 52180: Doc/Zsh/expn.yo: clarify array behavior of ${|var|...} diff --git a/Src/subst.c b/Src/subst.c index f37ae935e..cdbfc138a 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1501,16 +1501,15 @@ substevalchar(char *ptr) return noerrs ? dupstring(""): NULL; } errflag |= saved_errflag; + if (ires < 0) { + zerr("character not in range"); + } #ifdef MULTIBYTE_SUPPORT - if (isset(MULTIBYTE) && ires > 127) { - /* '\\' + 'U' + 8 bytes of character + '\0' */ - char buf[11]; - - /* inefficient: should separate out \U handling from getkeystring */ - sprintf(buf, "\\U%.8x", (unsigned int)ires & 0xFFFFFFFFu); - ptr = getkeystring(buf, &len, GETKEYS_BINDKEY, NULL); + else if (isset(MULTIBYTE) && ires > 127) { + ptr = zhalloc(MB_CUR_MAX); + len = ucs4tomb((unsigned int)ires & 0xffffffff, ptr); } - if (len == 0) + if (len <= 0) #endif { ptr = zhalloc(2); diff --git a/Src/utils.c b/Src/utils.c index 7040d0954..7028c155f 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -6672,11 +6672,14 @@ dquotedzputs(char const *s, FILE *stream) # if defined(HAVE_NL_LANGINFO) && defined(CODESET) && !defined(__STDC_ISO_10646__) /* Convert a character from UCS4 encoding to UTF-8 */ -static size_t +static int ucs4toutf8(char *dest, unsigned int wval) { - size_t len; + int len; + /* UCS4 is now equvalent to UTF-32 and limited to 0 - 0x10_FFFF. + * This function accepts 0 - 0x7FFF_FFFF (old range of UCS4) to be + * compatible with wctomb(3) (in UTF-8 locale) on Linux. */ if (wval < 0x80) len = 1; else if (wval < 0x800) @@ -6687,8 +6690,12 @@ ucs4toutf8(char *dest, unsigned int wval) len = 4; else if (wval < 0x4000000) len = 5; - else + else if (wval < 0x80000000) len = 6; + else { + zerr("character not in range"); + return -1; + } switch (len) { /* falls through except to the last case */ case 6: dest[5] = (wval & 0x3f) | 0x80; wval >>= 6; @@ -6705,30 +6712,89 @@ ucs4toutf8(char *dest, unsigned int wval) } #endif +/* Convert UCS4 to a multibyte character in current locale. + * Result is saved in buf (must be at least MB_CUR_MAX bytes long). + * Returns the number of bytes saved in buf, or -1 if conversion fails. */ -/* - * The following only occurs once or twice in the code, but in different - * places depending how character set conversion is implemented. - */ -#define CHARSET_FAILED() \ - if (how & GETKEY_DOLLAR_QUOTE) { \ - while ((*tdest++ = *++s)) { \ - if (how & GETKEY_UPDATE_OFFSET) { \ - if (s - sstart > *misc) \ - (*misc)++; \ - } \ - if (*s == Snull) { \ - *len = (s - sstart) + 1; \ - *tdest = '\0'; \ - return buf; \ - } \ - } \ - *len = tdest - buf; \ - return buf; \ - } \ - *t = '\0'; \ - *len = t - buf; \ - return buf +/**/ +int +ucs4tomb(unsigned int wval, char *buf) +{ +#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__) + int count = wctomb(buf, (wchar_t)wval); + if (count == -1) + zerr("character not in range"); + return count; +#else /* !(HAVE_WCHAR_H && HAVE_WCTOMB && __STDC_ISO_10646__) */ +# if defined(HAVE_NL_LANGINFO) && defined(CODESET) + if (!strcmp(nl_langinfo(CODESET), "UTF-8")) { + return ucs4toutf8(buf, wval); + } else { +# ifdef HAVE_ICONV + iconv_t cd; + char inbuf[4], *bsave = buf; + ICONV_CONST char *inptr = inbuf; + size_t inbytes = 4, outbytes = 6; + const char *codesetstr = nl_langinfo(CODESET); + size_t count; + int i; + + /* + * If the code set isn't handled, we'd better assume it's US-ASCII + * rather than just failing hopelessly. Solaris has a weird habit + * of returning 646. This is handled by the native iconv(), but + * not by GNU iconv; what's more, some versions of the native iconv + * don't handle standard names like ASCII. + * + * This should only be a problem if there's a mismatch between the + * NLS and the iconv in use, which probably only means if libiconv + * is in use. We checked at configure time if our libraries pulled + * in _libiconv_version, which should be a good test. + * + * It shouldn't ever be NULL, but while we're being paranoid... + */ +# ifdef ICONV_FROM_LIBICONV + if (!codesetstr || !*codesetstr) + codesetstr = "US-ASCII"; +# endif + cd = iconv_open(codesetstr, "UCS-4BE"); +# ifdef ICONV_FROM_LIBICONV + if (cd == (iconv_t)-1 && !strcmp(codesetstr, "646")) { + codesetstr = "US-ASCII"; + cd = iconv_open(codesetstr, "UCS-4BE"); + } +# endif + if (cd == (iconv_t)-1) { + zerr("cannot do charset conversion (iconv failed)"); + return -1; + } + + /* store value in big endian form */ + for (i=3; i>=0; i--) { + inbuf[i] = wval & 0xff; + wval >>= 8; + } + count = iconv(cd, &inptr, &inbytes, &buf, &outbytes); + iconv_close(cd); + if (count) { + /* -1 indicates error. Positive value means number of "invalid" + * (or "non-reversible") conversions, which we consider as + * "out-of-range" characters. */ + zerr("character not in range"); + return -1; + } + return buf - bsave; +# else /* !HAVE_ICONV */ + zerr("cannot do charset conversion (iconv not available)"); + return -1; +# endif /* HAVE_ICONV */ + } +# else /* !(HAVE_NL_LANGINFO && CODESET) */ + zerr("cannot do charset conversion (NLS not supported)"); + return -1; +# endif /* HAVE_NL_LANGINFO && CODESET */ +#endif /* HAVE_WCHAR_H && HAVE_WCTOMB && __STDC_ISO_10646__ */ +} /* * Decode a key string, turning it into the literal characters. @@ -6785,21 +6851,6 @@ getkeystring(char *s, int *len, int how, int *misc) char *t, *tdest = NULL, *u = NULL, *sstart = s, *tbuf = NULL; char svchar = '\0'; int meta = 0, control = 0, ignoring = 0; - int i; -#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__) - wint_t wval; - int count; -#else - unsigned int wval; -# if defined(HAVE_NL_LANGINFO) && defined(CODESET) -# if defined(HAVE_ICONV) - iconv_t cd; - char inbuf[4]; - size_t inbytes, outbytes; -# endif - size_t count; -# endif -#endif DPUTS((how & GETKEY_UPDATE_OFFSET) && (how & ~(GETKEYS_DOLLARS_QUOTE|GETKEY_UPDATE_OFFSET)), @@ -6864,7 +6915,8 @@ getkeystring(char *s, int *len, int how, int *misc) } for (; *s; s++) { if (*s == '\\' && s[1]) { - int miscadded; + int miscadded, count, i; + unsigned int wval; if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) { (*misc)--; miscadded = 1; @@ -6979,86 +7031,32 @@ getkeystring(char *s, int *len, int how, int *misc) *misc = wval; return s+1; } -#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__) - count = wctomb(t, (wchar_t)wval); + count = ucs4tomb(wval, t); if (count == -1) { - zerr("character not in range"); - CHARSET_FAILED(); + if (how & GETKEY_DOLLAR_QUOTE) { + while ((*tdest++ = *++s)) { + if (how & GETKEY_UPDATE_OFFSET) { + if (s - sstart > *misc) + (*misc)++; + } + if (*s == Snull) { + *len = (s - sstart) + 1; + *tdest = '\0'; + return buf; + } + } + *len = tdest - buf; + } + else { + *t = '\0'; + *len = t - buf; + } + return buf; } if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) (*misc) += count; t += count; -# else -# if defined(HAVE_NL_LANGINFO) && defined(CODESET) - if (!strcmp(nl_langinfo(CODESET), "UTF-8")) { - count = ucs4toutf8(t, wval); - t += count; - if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) - (*misc) += count; - } else { -# ifdef HAVE_ICONV - ICONV_CONST char *inptr = inbuf; - const char *codesetstr = nl_langinfo(CODESET); - inbytes = 4; - outbytes = 6; - /* store value in big endian form */ - for (i=3;i>=0;i--) { - inbuf[i] = wval & 0xff; - wval >>= 8; - } - /* - * If the code set isn't handled, we'd better - * assume it's US-ASCII rather than just failing - * hopelessly. Solaris has a weird habit of - * returning 646. This is handled by the - * native iconv(), but not by GNU iconv; what's - * more, some versions of the native iconv don't - * handle standard names like ASCII. - * - * This should only be a problem if there's a - * mismatch between the NLS and the iconv in use, - * which probably only means if libiconv is in use. - * We checked at configure time if our libraries - * pulled in _libiconv_version, which should be - * a good test. - * - * It shouldn't ever be NULL, but while we're - * being paranoid... - */ -#ifdef ICONV_FROM_LIBICONV - if (!codesetstr || !*codesetstr) - codesetstr = "US-ASCII"; -#endif - cd = iconv_open(codesetstr, "UCS-4BE"); -#ifdef ICONV_FROM_LIBICONV - if (cd == (iconv_t)-1 && !strcmp(codesetstr, "646")) { - codesetstr = "US-ASCII"; - cd = iconv_open(codesetstr, "UCS-4BE"); - } -#endif - if (cd == (iconv_t)-1) { - zerr("cannot do charset conversion (iconv failed)"); - CHARSET_FAILED(); - } - count = iconv(cd, &inptr, &inbytes, &t, &outbytes); - iconv_close(cd); - if (count == (size_t)-1) { - zerr("character not in range"); - CHARSET_FAILED(); - } - if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) - (*misc) += count; -# else - zerr("cannot do charset conversion (iconv not available)"); - CHARSET_FAILED(); -# endif - } -# else - zerr("cannot do charset conversion (NLS not supported)"); - CHARSET_FAILED(); -# endif -# endif if (how & GETKEY_DOLLAR_QUOTE) { char *t2; for (t2 = tbuf; t2 < t; t2++) { -- cgit v1.2.3 From 29644f12e742883ec9502205cffb318e446d7ca3 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 1 Oct 2023 11:34:33 -0700 Subject: 52193: handle UTF8-encoded USERNAME and therefore home directory in zcompile Includes one unposted thinko fix ztrdup -> dupstring --- ChangeLog | 7 +++++++ Src/init.c | 4 ++-- Src/params.c | 2 +- Src/parse.c | 12 +++++++++--- Src/utils.c | 2 +- 5 files changed, 20 insertions(+), 7 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 4c0a27878..840f6c5a1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2023-10-01 Bart Schaefer + + * 52193: Src/init.c, Src/params.c, Src/utils.c: metafy USERNAME + (mostly for Cygwin compatibilty with UTF8 encodings) + + * 52193: Src/parse.c: unmetafy file paths in zcompile + 2023-09-27 Jun-ichi Takimoto * 52188: Test/D04parameter.ztst: skip tests that fail if diff --git a/Src/init.c b/Src/init.c index ffb017e22..799ad19f6 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1212,8 +1212,8 @@ setupvals(char *cmd, char *runscript, char *zsh_name) #ifdef USE_GETPWUID if ((pswd = getpwuid(cached_uid))) { if (EMULATION(EMULATE_ZSH)) - home = metafy(pswd->pw_dir, -1, META_DUP); - cached_username = ztrdup(pswd->pw_name); + home = ztrdup_metafy(pswd->pw_dir); + cached_username = ztrdup_metafy(pswd->pw_name); } else #endif /* USE_GETPWUID */ diff --git a/Src/params.c b/Src/params.c index 5841308d7..50e8627d1 100644 --- a/Src/params.c +++ b/Src/params.c @@ -4561,7 +4561,7 @@ usernamesetfn(UNUSED(Param pm), char *x) zwarn("failed to change user ID: %e", errno); else { zsfree(cached_username); - cached_username = ztrdup(pswd->pw_name); + cached_username = ztrdup_metafy(pswd->pw_name); cached_uid = pswd->pw_uid; } } diff --git a/Src/parse.c b/Src/parse.c index a07a6cc71..f7285c2ed 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -3217,12 +3217,14 @@ bin_zcompile(char *nam, char **args, Options ops, UNUSED(int func)) if (!args[1] && !(OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a'))) { queue_signals(); - ret = build_dump(nam, dyncat(*args, FD_EXT), args, OPT_ISSET(ops,'U'), + dump = unmetafy(dyncat(*args, FD_EXT), NULL); + ret = build_dump(nam, dump, args, OPT_ISSET(ops,'U'), map, flags); unqueue_signals(); return ret; } - dump = (strsfx(FD_EXT, *args) ? *args : dyncat(*args, FD_EXT)); + dump = (strsfx(FD_EXT, *args) ? dupstring(*args) : dyncat(*args, FD_EXT)); + unmetafy(dump, NULL); queue_signals(); ret = ((OPT_ISSET(ops,'c') || OPT_ISSET(ops,'a')) ? @@ -3400,6 +3402,7 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) for (hlen = FD_PRELEN, tlen = 0; *files; files++) { struct stat st; + char *fnam; if (check_cond(*files, "k")) { flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_KSHLOAD; @@ -3408,7 +3411,8 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) flags = (flags & ~(FDHF_KSHLOAD | FDHF_ZSHLOAD)) | FDHF_ZSHLOAD; continue; } - if ((fd = open(*files, O_RDONLY)) < 0 || + fnam = unmeta(*files); + if ((fd = open(fnam, O_RDONLY)) < 0 || fstat(fd, &st) != 0 || !S_ISREG(st.st_mode) || (flen = lseek(fd, 0, 2)) == -1) { if (fd >= 0) @@ -3417,8 +3421,10 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) zwarnnam(nam, "can't open file: %s", *files); noaliases = ona; unlink(dump); + zsfree(fnam); return 1; } + zsfree(fnam); file = (char *) zalloc(flen + 1); file[flen] = '\0'; lseek(fd, 0, 0); diff --git a/Src/utils.c b/Src/utils.c index 7028c155f..790625379 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1069,7 +1069,7 @@ get_username(void) cached_uid = current_uid; zsfree(cached_username); if ((pswd = getpwuid(current_uid))) - cached_username = ztrdup(pswd->pw_name); + cached_username = ztrdup_metafy(pswd->pw_name); else cached_username = ztrdup(""); } -- cgit v1.2.3 From 0f0ba0539e9fdc063c73e3ac6751f77395c193ec Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 1 Oct 2023 13:38:25 -0700 Subject: 52195: cached_username is already metafied when initializing LOGNAME --- ChangeLog | 3 +++ Src/params.c | 7 +++---- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 840f6c5a1..d08adc031 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-10-01 Bart Schaefer + * 52195: Src/params.c: cached_username is already metafied when + initializing LOGNAME + * 52193: Src/init.c, Src/params.c, Src/utils.c: metafy USERNAME (mostly for Cygwin compatibilty with UTF8 encodings) diff --git a/Src/params.c b/Src/params.c index 50e8627d1..957656e3f 100644 --- a/Src/params.c +++ b/Src/params.c @@ -850,12 +850,11 @@ createparamtable(void) setsparam("HOST", ztrdup_metafy(hostnam)); zfree(hostnam, 256); - setsparam("LOGNAME", ztrdup_metafy( + setsparam("LOGNAME", #ifndef DISABLE_DYNAMIC_NSS - (str = getlogin()) && *str ? str : + (str = getlogin()) && *str ? ztrdup_metafy(str) : #endif - cached_username - )); + ztrdup(cached_username)); #if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV) /* Copy the environment variables we are inheriting to dynamic * -- cgit v1.2.3 From 86196843bd8a1d7afc81631881b2e98385e5c4a7 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 3 Oct 2023 21:21:54 -0700 Subject: 52198: put back incorrectly removed zfree() --- ChangeLog | 4 ++++ Src/input.c | 1 + 2 files changed, 5 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index d08adc031..dc8d96e24 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-10-03 Bart Schaefer + + * 52198: Src/input.c: put back incorrectly removed zfree() + 2023-10-01 Bart Schaefer * 52195: Src/params.c: cached_username is already metafied when diff --git a/Src/input.c b/Src/input.c index 8d7f44d7c..dd8f2edc7 100644 --- a/Src/input.c +++ b/Src/input.c @@ -632,6 +632,7 @@ zstuff(char **out, const char *fn) if (len && !(fread(buf, len, 1, in))) { zerr("read error on %s", fn); fclose(in); + zfree(buf, len + 1); unqueue_signals(); return -1; } -- cgit v1.2.3 From 1ffc6d0ef58bd0e6e4e0cb170af8c3325d77b376 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Thu, 5 Oct 2023 07:58:27 -0700 Subject: 52204: fix thinko, unmeta() buffer should not be freed --- ChangeLog | 4 ++++ Src/parse.c | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index dc8d96e24..b7d49ace2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-10-05 Bart Schaefer + + * 52204: Src/parse.c: fix unmeta() thinko from 52193 + 2023-10-03 Bart Schaefer * 52198: Src/input.c: put back incorrectly removed zfree() diff --git a/Src/parse.c b/Src/parse.c index f7285c2ed..c0a1e9f95 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -3421,10 +3421,8 @@ build_dump(char *nam, char *dump, char **files, int ali, int map, int flags) zwarnnam(nam, "can't open file: %s", *files); noaliases = ona; unlink(dump); - zsfree(fnam); return 1; } - zsfree(fnam); file = (char *) zalloc(flen + 1); file[flen] = '\0'; lseek(fd, 0, 0); -- cgit v1.2.3 From 83f8a71a7cc2137a1d4638228b3d83717c15af54 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sun, 8 Oct 2023 23:47:25 +0200 Subject: 52214: allow extra byte for nul terminator in allocation --- ChangeLog | 2 ++ Src/subst.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 3e9b6ffa0..9e0a94b10 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2023-10-10 Oliver Kiddle + * 52214: Src/subst.c: allow extra byte for nul in allocation + * unposted (cf. 52166): Functions/Misc/run-help-svk: remove obsolete helper for svk diff --git a/Src/subst.c b/Src/subst.c index cdbfc138a..60d850feb 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1506,7 +1506,7 @@ substevalchar(char *ptr) } #ifdef MULTIBYTE_SUPPORT else if (isset(MULTIBYTE) && ires > 127) { - ptr = zhalloc(MB_CUR_MAX); + ptr = zhalloc(MB_CUR_MAX+1); len = ucs4tomb((unsigned int)ires & 0xffffffff, ptr); } if (len <= 0) -- cgit v1.2.3 From 4878c2b1307d54cbdc218ee674403c03bc1e02c1 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Tue, 10 Oct 2023 23:46:15 +0200 Subject: 52216: metafy usernames to allow for them to be UTF-8 encoded --- ChangeLog | 3 +++ Src/Modules/watch.c | 26 ++++++++++++++++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 9e0a94b10..9a5204ce7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-10-10 Oliver Kiddle + * 52216: Src/Modules/watch.c: metafy usernames to allow for + them to be UTF-8 encoded + * 52214: Src/subst.c: allow extra byte for nul in allocation * unposted (cf. 52166): Functions/Misc/run-help-svk: diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c index 0de8cbf9a..97d4fa608 100644 --- a/Src/Modules/watch.c +++ b/Src/Modules/watch.c @@ -423,20 +423,23 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) /* See if the watch entry matches */ static int -watchlog_match(char *teststr, char *actual, int len) +watchlog_match(char *teststr, char *actual, size_t buflen) { int ret = 0; Patprog pprog; char *str = dupstring(teststr); + int len = strnlen(actual, buflen); + char *user = metafy(actual, len, + len == buflen ? META_HEAPDUP : META_USEHEAP); tokenize(str); if ((pprog = patcompile(str, PAT_STATIC, 0))) { queue_signals(); - if (pattry(pprog, actual)) + if (pattry(pprog, user)) ret = 1; unqueue_signals(); - } else if (!strncmp(actual, teststr, len)) + } else if (!strcmp(user, teststr)) ret = 1; return ret; } @@ -456,10 +459,17 @@ watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt) (void)watchlog2(inout, u, fmt, 1, 0); return; } - if (*w && !strcmp(*w, "notme") && - strncmp(u->ut_name, get_username(), sizeof(u->ut_name))) { - (void)watchlog2(inout, u, fmt, 1, 0); - return; + if (*w && !strcmp(*w, "notme")) { + int len = strnlen(u->ut_name, sizeof(u->ut_name)); + char *username = metafy(u->ut_name, len, + (len == sizeof(u->ut_name) ? + META_HEAPDUP /* allow for nul terminator */ : + META_USEHEAP)); + if (strcmp(username, get_username())) { + (void)watchlog2(inout, u, fmt, 1, 0); + return; + } + w++; } for (; *w; w++) { bad = 0; @@ -488,7 +498,7 @@ watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt) for (vv = ++v; *vv && *vv != '%'; vv++); sav = *vv; *vv = '\0'; - if (!watchlog_match(v, u->ut_host, strlen(v))) + if (!watchlog_match(v, u->ut_host, sizeof(u->ut_host))) bad = 1; *vv = sav; v = vv; -- cgit v1.2.3 From 70320635b4b50b1e84f70e17bf40f107d140bdcf Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Tue, 26 Sep 2023 23:08:54 +0200 Subject: 52189: ignore compadd -M if -U also specified as they don't make sense together This fixes df completion. --- ChangeLog | 4 ++++ Completion/Unix/Type/_umountable | 3 ++- Src/Zle/complete.c | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index cfc1a2841..a933a5186 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-10-10 Oliver Kiddle + * 52189: Completion/Unix/Type/_umountable, Src/Zle/complete.c: + ignore compadd -M if -U also specified as they don't make + sense together, this fixes df completion + * 52217: Completion/BSD/Command/_freebsd-update, Completion/BSD/Command/_ipfw, Completion/BSD/Command/_pfctl, Completion/BSD/Command/_sockstat, Completion/Unix/Command/_cmp, diff --git a/Completion/Unix/Type/_umountable b/Completion/Unix/Type/_umountable index 6e4988e2d..0111555b6 100644 --- a/Completion/Unix/Type/_umountable +++ b/Completion/Unix/Type/_umountable @@ -1,6 +1,6 @@ #autoload local tmp -local dev_tmp dpath_tmp mp_tmp mline +local -a dev_tmp dpath_tmp mp_tmp mline case "$OSTYPE" in linux*) @@ -15,6 +15,7 @@ irix*) ;; freebsd*|dragonfly*) /sbin/mount | while read mline; do + [[ $mline[(w)1] = map ]] && continue dev_tmp+=( $mline[(w)1] ) mp_tmp+=( $mline[(w)3] ) done diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c index 96ad7b3f1..342611f1f 100644 --- a/Src/Zle/complete.c +++ b/Src/Zle/complete.c @@ -829,7 +829,9 @@ bin_compadd(char *name, char **argv, UNUSED(Options ops), UNUSED(int func)) ca_args: - if (mstr && (match = parse_cmatcher(name, mstr)) == pcm_err) { + if (mstr && (dat.aflags & CAF_MATCH) && + (match = parse_cmatcher(name, mstr)) == pcm_err) + { zsfree(mstr); zfree(dat.dpar, dparsize); return 1; -- cgit v1.2.3 From 6b34f3dc14d5caf7f4e3ef0a6416224edf905c48 Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Tue, 28 Feb 2023 14:25:57 +0100 Subject: 51490: Use time_t for lastt which stores result of time(0) Coverity complained about this, and possibly some more people would in 15 years --- ChangeLog | 3 +++ Src/Zle/computil.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 5eb4876b8..2774ca119 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,9 @@ * 52056: Completion/Unix/Command/_sccs: _sccs: don't handle subcommands in main namespace + * 51490: Src/Zle/computil.c: Use time_t for lastt which stores + result of time(0) + 2023-10-15 Bart Schaefer * 52218: Etc/BUGS, NEWS, README: notes since 5.9 release diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 77ccdebf7..360667884 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -911,7 +911,7 @@ struct cadef { Caarg rest; /* the rest-argument */ char **defs; /* the original strings */ int ndefs; /* number of ... */ - int lastt; /* last time this was used */ + time_t lastt; /* last time this was used */ Caopt *single; /* array of single-letter options */ char *match; /* -M spec to use */ int argsactive; /* if normal arguments are still allowed */ @@ -2935,7 +2935,7 @@ struct cvdef { Cvval vals; /* value definitions */ char **defs; /* original strings */ int ndefs; /* number of ... */ - int lastt; /* last time used */ + time_t lastt; /* last time used */ int words; /* if to look at other words */ }; -- cgit v1.2.3 From 0c15cc8712b5b3c83d52b1c27f416db80d3426aa Mon Sep 17 00:00:00 2001 From: ErrrorMaxx Date: Sat, 21 Oct 2023 09:05:52 +0300 Subject: github #104: fix small typo --- ChangeLog | 2 ++ Src/exec.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index e37c0057c..c3d68a6a2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,8 @@ * github #103: Christian Heusel: Completion/Unix/Command/_zfs: fix completion for zpool upgrade + * github #104: ErrrorMaxx: Src/exec.c: fix small typo + 2023-10-16 Mikael Magnusson * 52056: Completion/Unix/Command/_sccs: _sccs: don't handle diff --git a/Src/exec.c b/Src/exec.c index 3a8b3e951..285d2c5ad 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2388,7 +2388,7 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag, /* fd will be over 10, don't touch mfds */ fd1 = movefd(fd2); if (fd1 == -1) { - zerr("cannot moved fd %d: %e", fd2, errno); + zerr("cannot move fd %d: %e", fd2, errno); return; } else { fdtable[fd1] = FDT_EXTERNAL; -- 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') 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 f36fccbb84e5d36ebdecede0c29e8e0c588336c9 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Thu, 26 Oct 2023 20:55:45 -0700 Subject: 50569 (Daniel Shahaf): main keymap defaults to emacs Tweaked to make the sample .zshrc code better match the former C code, and to remove the declaration of no-longer-used variable "ed". --- ChangeLog | 2 ++ Src/Zle/zle_keymap.c | 12 +++--------- 2 files changed, 5 insertions(+), 9 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index e67a33ea9..c7fa8a1ea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2023-10-25 Bart Schaefer + * Daniel Shahaf: 50569 (tweaked): main keymap defaults to emacs + * Sebastian Gniazdowski: 52240: Functions/MIME/zsh-mime-handler: use work-var $s not $suffix when setting flags diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index a31ab22d7..041682ee9 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -1315,7 +1315,7 @@ default_bindings(void) Keymap vismap = newkeymap(NULL, "visual"); Keymap smap = newkeymap(NULL, ".safe"); Keymap vimaps[2], vilmaps[2], kptr; - char buf[3], *ed; + char buf[3]; int i; /* vi insert mode and emacs mode: * @@ -1445,20 +1445,14 @@ default_bindings(void) } /* Put the keymaps in the right namespace. The "main" keymap * - * will be linked to the "emacs" keymap, except that if VISUAL * - * or EDITOR contain the string "vi" then it will be linked to * - * the "viins" keymap. */ + * will be linked to the "emacs" keymap. */ linkkeymap(vmap, "viins", 0); linkkeymap(emap, "emacs", 0); linkkeymap(amap, "vicmd", 0); linkkeymap(oppmap, "viopp", 0); linkkeymap(vismap, "visual", 0); linkkeymap(smap, ".safe", 1); - if (((ed = zgetenv("VISUAL")) && strstr(ed, "vi")) || - ((ed = zgetenv("EDITOR")) && strstr(ed, "vi"))) - linkkeymap(vmap, "main", 0); - else - linkkeymap(emap, "main", 0); + linkkeymap(emap, "main", 0); /* the .safe map cannot be modified or deleted */ smap->flags |= KM_IMMUTABLE; -- cgit v1.2.3 From 9f57ca4ac8ae071727b1d77cbb8c4c0d893b9099 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 1 Nov 2023 00:34:29 +0100 Subject: 52252: Coverity defect 1547827 --- ChangeLog | 2 ++ Src/Modules/pcre.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 96d8cab8a..10b7ab867 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2023-11-01 Oliver Kiddle + * 52252: Src/Modules/pcre.c: Coverity defect 1547827 + * Shohei YOSHIDA: 52179: Completion/Unix/Command/_gradle: Fix gradlew completion when it isn't in PATH diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c index 96f3c6e65..f5cda6d38 100644 --- a/Src/Modules/pcre.c +++ b/Src/Modules/pcre.c @@ -187,7 +187,8 @@ zpcre_get_substrings(pcre2_code *pat, char *arg, pcre2_match_data *mdata, setaparam(substravar, matches); } - if (!pcre2_pattern_info(pat, PCRE2_INFO_NAMECOUNT, &ncount) && ncount + if (namedassoc + && !pcre2_pattern_info(pat, PCRE2_INFO_NAMECOUNT, &ncount) && ncount && !pcre2_pattern_info(pat, PCRE2_INFO_NAMEENTRYSIZE, &nsize) && !pcre2_pattern_info(pat, PCRE2_INFO_NAMETABLE, &ntable)) { -- cgit v1.2.3 From de635b4ee56c188ccbaf0009027f9d1c0d42af0f Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Tue, 31 Oct 2023 01:04:19 +0100 Subject: 52253: support pcre callouts with shell evaluation of the callout string --- ChangeLog | 3 +++ Doc/Zsh/mod_pcre.yo | 5 +++++ Src/Modules/pcre.c | 34 ++++++++++++++++++++++++++++++++-- 3 files changed, 40 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 719222048..3f1014db3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-11-02 Oliver Kiddle + * 52253: Src/Modules/pcre.c: support pcre callouts with shell + evaluation of the callout string + * 52260: Completion/Unix/Command/_sudo: handle variable assignments before the command in sudo completion diff --git a/Doc/Zsh/mod_pcre.yo b/Doc/Zsh/mod_pcre.yo index da73ac85a..41fab4475 100644 --- a/Doc/Zsh/mod_pcre.yo +++ b/Doc/Zsh/mod_pcre.yo @@ -69,6 +69,11 @@ print -l $accum) ) enditem() +If the regular expression contains callouts, these are executed as shell code. +During the execution of the callout, the string the regular expression is +matching against is available in the parameter tt(.pcre.subject). If there is a +non-zero return status from the shell code, the callout does not match. + The option tt(-d) uses the alternative breadth-first DFA search algorithm of pcre. This sets tt(match), or the array given with tt(-a), to all the matches found from the same start point in the subject. diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c index f5cda6d38..e6b59831f 100644 --- a/Src/Modules/pcre.c +++ b/Src/Modules/pcre.c @@ -128,6 +128,31 @@ bin_pcre_study(char *nam, UNUSED(char **args), UNUSED(Options ops), UNUSED(int f return 0; } +static int +pcre_callout(pcre2_callout_block_8 *block, void *) +{ + Eprog prog; + int ret=0; + + if (!block->callout_number && + ((prog = parse_string((char *) block->callout_string, 0)))) + { + int ef = errflag, lv = lastval; + + setsparam(".pcre.subject", + metafy((char *) block->subject, block->subject_length, META_DUP)); + setiparam(".pcre.pos", block->current_position + 1); + execode(prog, 1, 0, "pcre"); + ret = lastval | errflag; + + /* Restore any user interrupt error status */ + errflag = ef | (errflag & ERRFLAG_INT); + lastval = lv; + } + + return ret; +} + static int zpcre_get_substrings(pcre2_code *pat, char *arg, pcre2_match_data *mdata, int captured_count, char *matchvar, char *substravar, char *namedassoc, @@ -339,6 +364,9 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) plaintext = ztrdup(*args); unmetafy(plaintext, &subject_len); + pcre2_match_context_8 *mcontext = pcre2_match_context_create(NULL); + pcre2_set_callout(mcontext, &pcre_callout, 0); + if (offset_start > 0 && offset_start >= subject_len) ret = PCRE2_ERROR_NOMATCH; else if (use_dfa) { @@ -347,7 +375,7 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) pcre_mdata = pcre2_match_data_create(capcount, NULL); do { ret = pcre2_dfa_match(pcre_pattern, (PCRE2_SPTR) plaintext, subject_len, - offset_start, 0, pcre_mdata, NULL, (int *) workspace, wscount); + offset_start, 0, pcre_mdata, mcontext, (int *) workspace, wscount); if (ret == PCRE2_ERROR_DFA_WSSIZE) { old = wscount; wscount += wscount / 2; @@ -362,7 +390,7 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) } else { pcre_mdata = pcre2_match_data_create_from_pattern(pcre_pattern, NULL); ret = pcre2_match(pcre_pattern, (PCRE2_SPTR) plaintext, subject_len, - offset_start, 0, pcre_mdata, NULL); + offset_start, 0, pcre_mdata, mcontext); } if (ret==0) return_value = 0; @@ -380,6 +408,8 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) if (pcre_mdata) pcre2_match_data_free(pcre_mdata); + if (mcontext) + pcre2_match_context_free(mcontext); zsfree(plaintext); return return_value; -- cgit v1.2.3 From bad8af1808e3809285fbb33a5a168f09247c4793 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 8 Nov 2023 02:32:23 +0100 Subject: 52271: use correct form for unused parameter --- ChangeLog | 4 ++++ Src/Modules/pcre.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 6c36d838e..0997ef5c7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-11-08 Oliver Kiddle + + * 52271: Src/Modules/pcre.c: use correct form for unused parameter + 2023-11-02 Oliver Kiddle * 52268: Completion/Linux/Command/_networkmanager: update to 1.42.2 diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c index e6b59831f..e48ae3ae5 100644 --- a/Src/Modules/pcre.c +++ b/Src/Modules/pcre.c @@ -129,7 +129,7 @@ bin_pcre_study(char *nam, UNUSED(char **args), UNUSED(Options ops), UNUSED(int f } static int -pcre_callout(pcre2_callout_block_8 *block, void *) +pcre_callout(pcre2_callout_block_8 *block, UNUSED(void *callout_data)) { Eprog prog; int ret=0; -- cgit v1.2.3 From e6ad117ccb548c8411610571c92fa4094f275460 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 15 Nov 2023 20:16:04 -0800 Subject: 52202: improve handling of quoting in ${var/pattern/replacement} --- ChangeLog | 6 ++++++ Src/lex.c | 53 ++++++++++++++++++++++++++++++-------------------- Src/subst.c | 7 +++++++ Test/D04parameter.ztst | 8 ++++---- 4 files changed, 49 insertions(+), 25 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 4f5c6b024..6baa171dc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2023-11-15 Bart Schaefer + + * 52202: Src/lex.c, Src/subst.c, Test/D04parameter.ztst: improve + handling of quoting in ${var/pattern/replacement}. Still not + perfect, e.g., deeply nested expansions in the pattern may fail. + 2023-11-14 Oliver Kiddle * github #106: Matt Koscica: Completion/Unix/Command/_tmux, diff --git a/Src/lex.c b/Src/lex.c index 33b17cc95..31b130b07 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -938,7 +938,7 @@ gettokstr(int c, int sub) { int bct = 0, pct = 0, brct = 0, seen_brct = 0, fdpar = 0; int intpos = 1, in_brace_param = 0, cmdsubst = 0; - int inquote, unmatched = 0; + int inquote, unmatched = 0, in_pattern = 0; enum lextok peek; #ifdef DEBUG int ocmdsp = cmdsp; @@ -1160,7 +1160,7 @@ gettokstr(int c, int sub) if (bct-- == in_brace_param) { if (cmdsubst) cmdpop(); - in_brace_param = cmdsubst = 0; + in_brace_param = cmdsubst = in_pattern = 0; } c = Outbrace; break; @@ -1309,7 +1309,8 @@ gettokstr(int c, int sub) lexbuf.ptr--, lexbuf.len--; else break; - } + } else if (in_pattern && c == '/') + add(Bnull); add(c); } ALLOWHIST @@ -1397,26 +1398,36 @@ gettokstr(int c, int sub) */ c = Dash; break; - case LX2_BANG: - /* - * Same logic as Dash, for ! to perform negation in range. - */ - if (seen_brct) - c = Bang; - else - c = '!'; - } - add(c); - c = hgetc(); - if (intpos) + case LX2_BANG: + /* + * Same logic as Dash, for ! to perform negation in range. + */ + if (seen_brct) + c = Bang; + else + c = '!'; + case LX2_OTHER: + if (in_brace_param) { + if (c == '/') { + if (in_pattern == 0) + in_pattern = 2; + else + --in_pattern; + } + } + } + add(c); + c = hgetc(); + if (intpos) intpos--; - if (lexstop) + if (lexstop) break; - if (!cmdsubst && in_brace_param && act == LX2_STRING && - (c == '|' || c == Bar || inblank(c))) { - cmdsubst = in_brace_param; - cmdpush(CS_CURSH); - } + if (!cmdsubst && in_brace_param && act == LX2_STRING && + (c == '|' || c == Bar || inblank(c))) { + cmdsubst = in_brace_param; + cmdpush(CS_CURSH); + } else if (in_pattern == 2 && c != '/') + in_pattern = 1; } brk: if (errflag) { diff --git a/Src/subst.c b/Src/subst.c index 60d850feb..4ef1d1269 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -3088,6 +3088,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, chuck(ptr); else ptr++; + } else if (c == Dnull) { + chuck(ptr); + while (*ptr && *ptr != c) + ptr++; + if (*ptr == Dnull) + chuck(ptr); + ptr--; /* Outer loop is about to increment */ } } replstr = (*ptr && ptr[1]) ? ptr+1 : ""; diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index c2008582c..69a4fd3ec 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -2762,13 +2762,13 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888 slash='/' print -r -- x${slash/'/'}y --Df:(users/28784) substituting a single-quoted backslash, part #1: slash +0:(users/28784) substituting a single-quoted backslash, part #1: slash >xy single_quote="'" - print -r -- x${single_quote/'/'}y --Df:(users/28784) substituting a single-quoted backslash, part #2: single quote ->x/y + print -r -- x${single_quote/$'/'}y +0:(users/28784) substituting a single-quoted backslash, part #2: single quote +>x'y control="foobar" print -r -- x${control/'bar'}y -- cgit v1.2.3 From 420d2c713f95cade62a8db77229c2649252e348d Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 15 Nov 2023 20:21:51 -0800 Subject: 52275: rationality in zgetdir() and zgetcwd() --- ChangeLog | 4 +++ Src/compat.c | 100 +++++++++++++++++++++++++++++++---------------------------- 2 files changed, 56 insertions(+), 48 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 6baa171dc..4c94edd3c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-11-15 Bart Schaefer + * 52275: Src/compat.c: rationality in zgetdir() and zgetcwd() to + avoid silently wandering out of the current directory when path + parents are inaccessible. + * 52202: Src/lex.c, Src/subst.c, Test/D04parameter.ztst: improve handling of quoting in ${var/pattern/replacement}. Still not perfect, e.g., deeply nested expansions in the pattern may fail. diff --git a/Src/compat.c b/Src/compat.c index 817bb4aaf..8b31ad9f4 100644 --- a/Src/compat.c +++ b/Src/compat.c @@ -342,36 +342,69 @@ zopenmax(void) mod_export char * zgetdir(struct dirsav *d) { - char nbuf[PATH_MAX+3]; char *buf; +#if defined(HAVE_GETCWD) || defined(__CYGWIN__) + char *cwdbuf; +#endif +#if defined(USE_GETCWD) || defined(__CYGWIN__) +#ifdef GETCWD_CALLS_MALLOC + if ((cwdbuf = getcwd(NULL, 0))) { + buf = dupstring(cwdbuf); + free(cwdbuf); + } else + buf = NULL; +#else + cwdbuf = zalloc(PATH_MAX+1); + if ((buf = getcwd(cwdbuf, PATH_MAX))) + buf = dupstring(buf); + zfree(cwdbuf, PATH_MAX+1); +#endif /* GETCWD_CALLS_MALLOC */ + if (d && buf) + return d->dirname = ztrdup(buf); + else + return buf; +#else /* !USE_GETCWD && !__CYGWIN__ */ int bufsiz, pos; - struct stat sbuf; - ino_t pino; - dev_t pdev; -#if !defined(__CYGWIN__) && !defined(USE_GETCWD) + char nbuf[PATH_MAX+3]; struct dirent *de; DIR *dir; - dev_t dev; - ino_t ino; + struct stat sbuf; + ino_t pino, ino; + dev_t pdev, dev; int len; -#endif + strcpy(nbuf, "../"); + if (stat(".", &sbuf) == 0) { + /* Record the initial inode and device */ + pino = sbuf.st_ino; + pdev = sbuf.st_dev; + if (d) + d->ino = pino, d->dev = pdev; + } +#ifdef HAVE_GETCWD + else { +#ifdef GETCWD_CALLS_MALLOC + if ((cwdbuf = getcwd(NULL, 0))) { + buf = dupstring(cwdbuf); + free(cwdbuf); + } else + buf = NULL; +#else + cwdbuf = zalloc(PATH_MAX+1); + if ((buf = getcwd(cwdbuf, PATH_MAX))) + buf = dupstring(buf); + zfree(cwdbuf, PATH_MAX+1); +#endif /* GETCWD_CALLS_MALLOC */ + return buf; /* NULL when stat() and getcwd() both failed */ + } +#endif + /* stat() succeeded */ buf = zhalloc(bufsiz = PATH_MAX+1); pos = bufsiz - 1; buf[pos] = '\0'; - strcpy(nbuf, "../"); - if (stat(".", &sbuf) < 0) { - return NULL; - } - /* Record the initial inode and device */ - pino = sbuf.st_ino; - pdev = sbuf.st_dev; - if (d) - d->ino = pino, d->dev = pdev; -#if !defined(__CYGWIN__) && !defined(USE_GETCWD) #ifdef HAVE_FCHDIR - else + if (!d) #endif holdintr(); @@ -487,18 +520,6 @@ zgetdir(struct dirsav *d) zchdir(buf + pos + 1); noholdintr(); -#else /* __CYGWIN__, USE_GETCWD cases */ - - if (!getcwd(buf, bufsiz)) { - if (d) { - return NULL; - } - } else { - if (d) { - return d->dirname = ztrdup(buf); - } - return buf; - } #endif /* @@ -526,23 +547,6 @@ mod_export char * zgetcwd(void) { char *ret = zgetdir(NULL); -#ifdef HAVE_GETCWD - if (!ret) { -#ifdef GETCWD_CALLS_MALLOC - char *cwd = getcwd(NULL, 0); - if (cwd) { - ret = dupstring(cwd); - free(cwd); - } -#else - char *cwdbuf = zalloc(PATH_MAX+1); - ret = getcwd(cwdbuf, PATH_MAX); - if (ret) - ret = dupstring(ret); - zfree(cwdbuf, PATH_MAX+1); -#endif /* GETCWD_CALLS_MALLOC */ - } -#endif /* HAVE_GETCWD */ if (!ret) ret = unmeta(pwd); if (!ret || *ret == '\0') -- cgit v1.2.3 From ddfc81a2b81e19c13f60bfff30f28ed520df8f57 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 15 Nov 2023 20:29:50 -0800 Subject: 52309: fix cases that hang with all signals blocked. * no job control inside <<(substition) * allow interrupt of multios reading from a terminal --- ChangeLog | 4 ++++ Src/exec.c | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 10631b9e3..0de5c2fcb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2023-11-15 Bart Schaefer + * 52309: Src/exec.c: suppress job control inside <<(substition) + to fix cases that hang with all signals blocked. Similarly, + allow interrupt of multios reading from a terminal. + * 52308: Etc/FAQ.yo: mention new features, fix some old answers * 52275: Src/compat.c: rationality in zgetdir() and zgetcwd() to diff --git a/Src/exec.c b/Src/exec.c index 285d2c5ad..97823760f 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2309,7 +2309,7 @@ closemn(struct multio **mfds, int fd, int type) for (i = 0; i < mn->ct; i++) while ((len = read(mn->fds[i], buf, TCBUFSIZE)) != 0) { if (len < 0) { - if (errno == EINTR) + if (errno == EINTR && !isatty(mn->fds[i])) continue; else break; @@ -5096,7 +5096,7 @@ getpipe(char *cmd, int nullexec) procsubstpid = pid; return pipes[!out]; } - entersubsh(ESUB_ASYNC|ESUB_PGRP, NULL); + entersubsh(ESUB_ASYNC|ESUB_PGRP|ESUB_NOMONITOR, NULL); redup(pipes[out], out); closem(FDT_UNUSED, 0); /* this closes pipes[!out] as well */ cmdpush(CS_CMDSUBST); -- cgit v1.2.3 From fcf080ab578de00c799eb287ef8bdc201c313fee Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 18 Nov 2023 16:22:24 -0800 Subject: 52313: Src/exec.c: multios are not interactive and check for write errors. --- ChangeLog | 5 +++++ Src/exec.c | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 09b854e54..feeae6068 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-11-18 Bart Schaefer + + * 52313: Src/exec.c: subshells implementing multio reads/writes + are not interactive and should check for write errors. + 2023-11-17 Oliver Kiddle * 52307: Completion/Linux/Command/_selinux, diff --git a/Src/exec.c b/Src/exec.c index 97823760f..7d8135266 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2290,6 +2290,8 @@ closemn(struct multio **mfds, int fd, int type) return; } /* pid == 0 */ + opts[INTERACTIVE] = 0; + dont_queue_signals(); child_unblock(); closeallelse(mn); if (mn->rflag) { @@ -2302,7 +2304,8 @@ closemn(struct multio **mfds, int fd, int type) break; } for (i = 0; i < mn->ct; i++) - write_loop(mn->fds[i], buf, len); + if (write_loop(mn->fds[i], buf, len) < 0) + break; } } else { /* cat process */ @@ -2314,7 +2317,8 @@ closemn(struct multio **mfds, int fd, int type) else break; } - write_loop(mn->pipe, buf, len); + if (write_loop(mn->pipe, buf, len) < 0) + break; } } _exit(0); -- cgit v1.2.3 From fbec213cc5ab0d0316c98bd9ddb883bae3bbdbc5 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Thu, 23 Nov 2023 13:23:55 -0800 Subject: 52325: Clarify doc for edge cases of named references and nofork substitution Unposted whitespace change avoids a parse error in ${ ... } with comments. --- ChangeLog | 9 +++++++++ Doc/Zsh/expn.yo | 8 +++++++- Doc/Zsh/mod_ksh93.yo | 7 ------- Doc/Zsh/params.yo | 10 +++++++--- Src/subst.c | 2 +- Test/D10nofork.ztst | 2 +- 6 files changed, 25 insertions(+), 13 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 8932d60d9..64155b175 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2023-11-23 Bart Schaefer + + * 52325: Doc/Zsh/expn.yo, Doc/Zsh/mod_ksh93.yo, Doc/Zsh/params.yo: + Clarify side-effects of $argv and named references to specials, + update ksh93 feature compatibility. + + unposted: Src/subst.c, Test/D10nofork.ztst: whitespace tweak to + avoid unexpected parse error when comments are used in ${ ... } + 2023-11-22 Oliver Kiddle * unposted: Completion/Unix/Command/_ri: fix missing closing brace diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 837a85db6..e5506d469 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -1600,6 +1600,10 @@ example(tt(before local: OUTER) tt(by reference: OUTER) tt(after func: RESULT)) +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. + When var(rname) includes an array subscript, the subscript expression is interpreted at the time tt(${)var(pname)tt(}) is expanded. Any form of subscript is allowed, including those that select individual @@ -1929,7 +1933,9 @@ Note that because the `tt(${|)...tt(})' and `tt(${ )...tt( })' forms must be parsed at once as both string tokens and commands, all other braces (`tt({)' or `tt(})') within the command either must be quoted, or must appear in syntactically valid pairs, such as around complex -commands, function bodies, or parameter references. +commands, function bodies, or parameter references. Furthermore, +comments are always recognized, even when tt(NO_INTERACTIVE_COMMENTS) +is in effect. texinode(Arithmetic Expansion)(Brace Expansion)(Command Substitution)(Expansion) sect(Arithmetic Expansion) diff --git a/Doc/Zsh/mod_ksh93.yo b/Doc/Zsh/mod_ksh93.yo index 99dab385f..9cd708d10 100644 --- a/Doc/Zsh/mod_ksh93.yo +++ b/Doc/Zsh/mod_ksh93.yo @@ -184,13 +184,6 @@ When a function of this name is defined, it is called whenever the parameter var(name) would be unset. The function must explicitly `tt(unset )var(name)', otherwise the variable remains set. -em(THIS FEATURE IS NOT YET IMPLEMENTED.) -) -item(tt(${ )var(list)tt(;}))( -Note the space after the opening brace (tt({)). This executes var(list) -in the current shell and substitutes its standard output in the manner -of `tt($LPAR())var(list)tt(RPAR())' but without forking. - em(THIS FEATURE IS NOT YET IMPLEMENTED.) ) item(tt(typeset -C )var(name)[tt(=)var(values)tt( ...)])( diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 5653b3bc9..68df4a16f 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -748,9 +748,13 @@ vindex(argv) item(tt(argv) )( Same as tt(*). Assigning to tt(argv) changes the local positional parameters, but tt(argv) is em(not) itself a local parameter. -Deleting tt(argv) with tt(unset) in any function deletes it everywhere, -although only the innermost positional parameter array is deleted (so -tt(*) and tt(@) in other scopes are not affected). +Deleting tt(argv) with tt(unset) in any function deletes it everywhere. +This can be avoided by declaring `tt(local +h argv)' before unsetting. +Even when not so declared, only the innermost positional parameter +array is deleted (so tt(*) and tt(@) in other scopes are not affected). + +A named reference to tt(argv) em(does not) permit a called function to +access the positional parameters of its caller. ) vindex(@) item(tt(@) )( diff --git a/Src/subst.c b/Src/subst.c index 4ef1d1269..3a1187350 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1956,7 +1956,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * Then fall through to the regular handling of $REPLY * to manage word splitting, expansion flags, etc. */ - char *outfmt = ">| %s { %s ;}"; /* 13 */ + char *outfmt = ">| %s {\n%s\n;}"; /* 13 */ if ((rplytmp = gettempname(NULL, 1))) { /* Prevent shenanigans with $TMPPREFIX */ char *tmpfile = quotestring(rplytmp, QT_BACKSLASH); diff --git a/Test/D10nofork.ztst b/Test/D10nofork.ztst index 024b8ffcc..d6a5588df 100644 --- a/Test/D10nofork.ztst +++ b/Test/D10nofork.ztst @@ -422,7 +422,7 @@ F:must do this before evaluating the next test block purr ${ { echo nested } } DONE 1:ignored braces, part 4 -?(eval):1: parse error near `}' +?(eval):3: parse error near `}' # "break" blocks function calls in outer loop # Could use print, but that might get fixed -- cgit v1.2.3 From 618f842b464c090f4a6fb02a9f4d217be9270466 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Tue, 5 Dec 2023 18:49:42 +0100 Subject: 52326, 52372: add -q option to kill for sigqueue --- ChangeLog | 3 +++ Completion/Zsh/Command/_kill | 1 + Doc/Zsh/builtins.yo | 5 ++++- Src/jobs.c | 36 ++++++++++++++++++++++++++++++++++-- configure.ac | 1 + 5 files changed, 43 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index aa995e0d2..cd3cd4e84 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-12-05 Oliver Kiddle + * 52326, 52372: configure.ac, Src/jobs.c, Doc/Zsh/builtins.yo, + Completion/Zsh/Command/_kill: add -q option to kill for sigqueue + * 52373: Completion/Base/Utility/_numbers, Completion/Solaris/Command/_dumpadm, Completion/Unix/Command/_xz: fix _numbers for suffixes containing % and update affected functions diff --git a/Completion/Zsh/Command/_kill b/Completion/Zsh/Command/_kill index b9dfde3f0..084cf42c8 100644 --- a/Completion/Zsh/Command/_kill +++ b/Completion/Zsh/Command/_kill @@ -5,6 +5,7 @@ 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' \ '(-n -s -l)1::signal:_signals -p -s' \ diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index cab265a25..1aa807633 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -1143,7 +1143,7 @@ findex(kill) cindex(killing jobs) cindex(jobs, killing) xitem(tt(kill) [ tt(-s) var(signal_name) | tt(-n) var(signal_number) | \ -tt(-)var(sig) ] var(job) ...) +tt(-)var(sig) ] [ tt(-q) var(value) ] var(job) ...) item(tt(kill) tt(-l) [ var(sig) ... ])( Sends either tt(SIGTERM) or the specified signal to the given jobs or processes. @@ -1170,6 +1170,9 @@ tt(kill -IO) and tt(kill -POLL) have the same effect. Many systems will allow process IDs to be negative to kill a process group or zero to kill the current process group. + +The tt(-q) option allows an integer value to be sent with the signal +on systems that support tt(sigqueue+LPAR()RPAR()). ) findex(let) item(tt(let) var(arg) ...)( diff --git a/Src/jobs.c b/Src/jobs.c index a3b9f667a..4e9767ee4 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -2677,9 +2677,35 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) { int sig = SIGTERM; int returnval = 0; +#ifdef HAVE_SIGQUEUE + union sigval sigqueue_info; +#endif + int use_sigqueue = 0, got_sig = 0; + + while (*argv && **argv == '-') { + if (!use_sigqueue && (*argv)[1] == 'q' && (*argv)[2] == '\0') { + char *endp; + + if (!*++argv) { + zwarnnam(nam, "-q: argument expected"); + return 1; + } +#ifdef HAVE_SIGQUEUE + sigqueue_info.sival_int = +#endif + zstrtol(*argv, &endp, 10); + if (*endp) { + zwarnnam(nam, "invalid number: %s", *argv); + return 1; + } + use_sigqueue = 1; + argv++; + continue; + } + if (got_sig) + break; /* check for, and interpret, a signal specifier */ - if (*argv && **argv == '-') { if (idigit((*argv)[1])) { char *endp; /* signal specified by number */ @@ -2796,6 +2822,7 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) } } argv++; + got_sig = 1; } /* Discard the standard "-" and "--" option breaks */ @@ -2844,7 +2871,12 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) returnval++; } else { int pid = atoi(*argv); - if (kill(pid, sig) == -1) { + if ( +#ifdef HAVE_SIGQUEUE + use_sigqueue ? sigqueue(pid, sig, sigqueue_info) : +#endif + kill(pid, sig) == -1) + { zwarnnam("kill", "kill %s failed: %e", *argv, errno); returnval++; } diff --git a/configure.ac b/configure.ac index c5263035e..9cb6e032b 100644 --- a/configure.ac +++ b/configure.ac @@ -1299,6 +1299,7 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \ mkfifo _mktemp mkstemp \ waitpid wait3 \ sigaction sigblock sighold sigrelse sigsetmask sigprocmask \ + sigqueue \ killpg setpgid setpgrp tcsetpgrp tcgetattr nice \ gethostname gethostbyname2 getipnodebyname \ inet_aton inet_pton inet_ntop \ -- cgit v1.2.3 From 0ecc456fb2a4dda75e9910bb019d381406f040e8 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 9 Dec 2023 19:36:47 -0800 Subject: 52365: record state of exited background jobs so as to be visible in TRAPCHLD --- ChangeLog | 5 +++++ Src/jobs.c | 19 +++++++++++++++++++ Src/signals.c | 16 +++------------- 3 files changed, 27 insertions(+), 13 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index cd3cd4e84..af62f6e2a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2023-12-09 Bart Schaefer + + * 52365: Src/jobs.c, Src/signals.c: record state of exited + background jobs sooner so as to be visible in TRAPCHLD + 2023-12-05 Oliver Kiddle * 52326, 52372: configure.ac, Src/jobs.c, Doc/Zsh/builtins.yo, diff --git a/Src/jobs.c b/Src/jobs.c index 4e9767ee4..118c5e61b 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -660,6 +660,25 @@ update_job(Job jn) } } +/**/ +void +update_bg_job(Job jn, pid_t pid, int status) +{ + /* + * Accumulate a list of older jobs. We only do this for + * background jobs, which is something in the job table + * that's not marked as in the current shell or as shell builtin + * and is not equal to the current foreground job. + */ + if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) && + jn - jobtab != thisjob) { + if (WIFEXITED(status)) + addbgstatus(pid, WEXITSTATUS(status)); + else if (WIFSIGNALED(status)) + addbgstatus(pid, 0200 | WTERMSIG(status)); + } +} + /* set the previous job to something reasonable */ /**/ diff --git a/Src/signals.c b/Src/signals.c index a61368554..b1a843e2c 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -556,9 +556,11 @@ wait_for_processes(void) jn->gleader = 0; } } + update_bg_job(jn, pid, status); update_job(jn); } else if (findproc(pid, &jn, &pn, 1)) { pn->status = status; + update_bg_job(jn, pid, status); update_job(jn); } else { /* If not found, update the shell record of time spent by @@ -567,19 +569,7 @@ wait_for_processes(void) * terminates. */ get_usage(); - } - /* - * Accumulate a list of older jobs. We only do this for - * background jobs, which is something in the job table - * that's not marked as in the current shell or as shell builtin - * and is not equal to the current foreground job. - */ - if (jn && !(jn->stat & (STAT_CURSH|STAT_BUILTIN)) && - jn - jobtab != thisjob) { - if (WIFEXITED(status)) - addbgstatus(pid, WEXITSTATUS(status)); - else if (WIFSIGNALED(status)) - addbgstatus(pid, 0200 | WTERMSIG(status)); + update_bg_job(jn, pid, status); } unqueue_signals(); -- cgit v1.2.3 From 4da0f689c42e5a60972a69255d5e7e4fdd41d0be Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 9 Dec 2023 19:48:48 -0800 Subject: unposted: Fix longstanding typo in comment --- Src/parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Src') diff --git a/Src/parse.c b/Src/parse.c index c0a1e9f95..859f4d0fc 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -601,7 +601,7 @@ clear_hdocs(void) * | sublist [ SEPER | AMPER | AMPERBANG ] * * cmdsubst indicates our event is part of a command-style - * substitution terminated by the token indicationg, usual closing + * substitution terminated by the token indicated, usually closing * parenthesis. In other cases endtok is ENDINPUT. */ -- cgit v1.2.3 From 25f5618b17cf5e9b5add518dffd512fe8503c6b2 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 13 Dec 2023 11:27:32 +0100 Subject: 52382: avoid the non-standard \e in C code, preferring \033 --- ChangeLog | 4 ++++ Src/Modules/ksh93.c | 2 +- Src/Zle/zle_utils.c | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 84334a0af..ca505ceea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2023-12-13 Oliver Kiddle + + * 52382: Src/Modules/ksh93.c, Src/Zle/zle_utils.c: avoid \e in C code + 2023-12-09 Bart Schaefer * 52366 + fix typo: Completion/Unix/Command/_ant: rename diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c index 51999dd71..9af5e1d69 100644 --- a/Src/Modules/ksh93.c +++ b/Src/Modules/ksh93.c @@ -171,7 +171,7 @@ ksh93_wrapper(Eprog prog, FuncWrap w, char *name) /* bindkey -v forces VIMODE so this test is as good as any */ if (curkeymapname && isset(VIMODE) && strcmp(curkeymapname, "main") == 0) - strcpy(sh_edmode, "\e"); + strcpy(sh_edmode, "\033"); else strcpy(sh_edmode, ""); if (!sh_edchar) diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 51bb43339..e2b86e863 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -977,7 +977,7 @@ cuttext(ZLE_STRING_T line, int ct, int flags) unmetafy(mbcut, &cutll); mbcut = base64_encode(mbcut, cutll); - fprintf(shout, "\e]52;%c;%s\a", zmod.flags & MOD_CLIP ? 'c' : 'p', + fprintf(shout, "\033]52;%c;%s\a", zmod.flags & MOD_CLIP ? 'c' : 'p', mbcut); } else if (zmod.flags & MOD_VIBUF) { struct cutbuffer *b = &vibuf[zmod.vibuf]; -- cgit v1.2.3 From 58840922eeefc90f3e688853ac6ec365fadc3445 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 13 Dec 2023 11:34:12 +0100 Subject: 52392: use octal escape to match = without error messages from awk --- ChangeLog | 3 +++ Src/makepro.awk | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index ca505ceea..7867d4926 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2023-12-13 Oliver Kiddle + * 52392: Src/makepro.awk: use octal escape to match = without + error messages from either GNU awk or Solaris awk + * 52382: Src/Modules/ksh93.c, Src/Zle/zle_utils.c: avoid \e in C code 2023-12-09 Bart Schaefer diff --git a/Src/makepro.awk b/Src/makepro.awk index f69660531..0d53c5850 100644 --- a/Src/makepro.awk +++ b/Src/makepro.awk @@ -121,7 +121,7 @@ BEGIN { # initialiser. dcltor = substr(line, 1, RLENGTH-1) line = substr(line, RLENGTH+1) - sub(/=.*$/, "", dcltor) + sub(/\075.*$/, "", dcltor) match(dcltor, /^([^_0-9A-Za-z]| const )*/) dcltor = substr(dcltor, 1, RLENGTH) "@+" substr(dcltor, RLENGTH+1) match(dcltor, /^.*@\+[_0-9A-Za-z]+/) -- 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') 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 c72b4a74ef3e5031a72cab13d7d6365e8d7ef0fc Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 24 Jan 2024 17:32:45 -0800 Subject: 52473: zstyle -q for testing existence of a zstyle setting --- ChangeLog | 2 ++ Doc/Zsh/mod_zutil.yo | 7 +++++++ Src/Modules/zutil.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index b64c62830..7b30da246 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2024-01-24 Bart Schaefer + * 52473: Doc/Zsh/mod_zutil.yo, Src/Modules/zutil.c: zstyle -q + * 52468: Src/builtin.c, Src/utils.c: save and restore state of correct TTY when using read -s / -d diff --git a/Doc/Zsh/mod_zutil.yo b/Doc/Zsh/mod_zutil.yo index 3cf9e5028..9946618d6 100644 --- a/Doc/Zsh/mod_zutil.yo +++ b/Doc/Zsh/mod_zutil.yo @@ -11,6 +11,7 @@ xitem(tt(zstyle) [ tt(-L) [ var(metapattern) [ var(style) ] ] ]) xitem(tt(zstyle) [ tt(-e) | tt(-) | tt(-)tt(-) ] var(pattern) var(style) var(string) ...) xitem(tt(zstyle -d) [ var(pattern) [ var(style) ... ] ]) xitem(tt(zstyle -g) var(name) [ var(pattern) [ var(style) ] ]) +xitem(tt(zstyle -q) var(context) var(style)) xitem(tt(zstyle -){tt(a)|tt(b)|tt(s)} var(context) var(style) var(name) [ var(sep) ]) xitem(tt(zstyle -){tt(T)|tt(t)} var(context) var(style) [ var(string) ... ]) item(tt(zstyle -m) var(context) var(style) var(pattern))( @@ -105,6 +106,12 @@ enditem() The other forms can be used to look up or test styles for a given context. startitem() +item(tt(zstyle -q) var(context) var(style))( +Return tt(0) if var(style) is defined in var(context). This does not +evaluate expressions defined by tt(zstyle -e) and does not examine any +values set by var(style). The expected use is to test whether a style +has been defined for var(context) before asserting a new style. +) item(tt(zstyle -s) var(context) var(style) var(name) [ var(sep) ])( The parameter var(name) is set to the value of the style interpreted as a string. If the value contains several strings they are concatenated with diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 8b863d5c8..293a62dcf 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -461,6 +461,28 @@ lookupstyle(char *ctxt, char *style) return found; } +static int +testforstyle(char *ctxt, char *style) +{ + Style s; + Stypat p; + int found = 0; + + s = (Style)zstyletab->getnode2(zstyletab, style); + if (s) { + MatchData match; + savematch(&match); + for (p = s->pats; p; p = p->next) + if (pattry(p->prog, ctxt)) { + found = 1; + break; + } + restorematch(&match); + } + + return !found; /* 0 == success */ +} + static int bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { @@ -570,6 +592,7 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) case 't': min = 2; max = -1; break; case 'T': min = 2; max = -1; break; case 'm': min = 3; max = 3; break; + case 'q': min = 2; max = 2; break; case 'g': min = 1; max = 3; break; default: zwarnnam(nam, "invalid option: %s", args[0]); @@ -723,6 +746,15 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) return 1; } break; + case 'q': + { + int success; + queue_signals(); /* Protect PAT_STATIC */ + success = testforstyle(args[1], args[2]); + unqueue_signals(); + return success; + } + break; case 'g': { int ret = 1; -- cgit v1.2.3 From 0459cc2eafa5041d1fff362a4aa8f6022b957c03 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 24 Jan 2024 17:36:18 -0800 Subject: 52477: fix "zcurses mouse delay ..." and one other typo --- ChangeLog | 2 ++ Src/Modules/curses.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 7b30da246..66397f10a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2024-01-24 Bart Schaefer + * 52477: Src/Modules/curses.c: fix "zcurses mouse delay ..." + * 52473: Doc/Zsh/mod_zutil.yo, Src/Modules/zutil.c: zstyle -q * 52468: Src/builtin.c, Src/utils.c: save and restore state of diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c index ad17ed65f..8950cc153 100644 --- a/Src/Modules/curses.c +++ b/Src/Modules/curses.c @@ -1302,7 +1302,7 @@ zccmd_mouse(const char *nam, char **args) zlong delay; if (!*++args || - ((delay = zstrtol(*args, &eptr, 10)), eptr != NULL)) { + ((delay = zstrtol(*args, &eptr, 10)), *eptr != '\0')) { zwarnnam(nam, "mouse delay requires an integer argument"); return 1; } @@ -1326,7 +1326,7 @@ zccmd_mouse(const char *nam, char **args) if (old_mask != zcurses_mouse_mask) zcurses_flags |= ZCF_MOUSE_MASK_CHANGED; } else { - zwarnnam(nam, "unrecognised mouse command: %s", *arg); + zwarnnam(nam, "unrecognised mouse command: %s", arg); return 1; } } -- cgit v1.2.3 From b3e763cc228b182019c22a3490312f7b17df4029 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 24 Jan 2024 17:56:21 -0800 Subject: 52482: strip trailing newlines in emulation modes of ${ command; } --- ChangeLog | 3 +++ Src/subst.c | 6 ++++++ 2 files changed, 9 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index d51d527b8..d46ce5fe5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-01-24 Bart Schaefer + * 52482: Src/subst.c: strip trailing newlines in emulation modes + of ${ command; }, for bash/ksh compatibility + * 52476 + cf. 52479: Etc/FAQ.yo: more about nofork substitution * 52477: Src/Modules/curses.c: fix "zcurses mouse delay ..." diff --git a/Src/subst.c b/Src/subst.c index 3a1187350..650c09de2 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -2005,6 +2005,12 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, int onoerrs = noerrs, rplylen; noerrs = 2; rplylen = zstuff(&cmdarg, rplytmp); + if (! EMULATION(EMULATE_ZSH)) { + /* bash and ksh strip trailing newlines here */ + while (rplylen > 0 && cmdarg[rplylen-1] == '\n') + rplylen--; + cmdarg[rplylen] = 0; + } noerrs = onoerrs; if (rplylen >= 0) setsparam("REPLY", metafy(cmdarg, rplylen, META_REALLOC)); -- cgit v1.2.3 From 1f861ceba1d5740798caa0a3f208f3047c6e3ff5 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 24 Jan 2024 18:00:16 -0800 Subject: 52492: prevent indexing error on recursive arithmetic in array subscript Operator returns error when operand returns error --- ChangeLog | 3 +++ Src/math.c | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index d46ce5fe5..b876cb6d1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-01-24 Bart Schaefer + * 52492: Src/math.c: prevent indexing error when using recursive + arithmetic in array subscript (operator stops on operand error) + * 52482: Src/subst.c: strip trailing newlines in emulation modes of ${ command; }, for bash/ksh compatibility diff --git a/Src/math.c b/Src/math.c index a060181ed..50b69d6a1 100644 --- a/Src/math.c +++ b/Src/math.c @@ -352,6 +352,8 @@ getmathparam(struct mathvalue *mptr) } return zero_mnumber; } + if (errflag) + return zero_mnumber; } result = getnumvalue(mptr->pval); if (isset(FORCEFLOAT) && result.type == MN_INTEGER) { @@ -1367,8 +1369,11 @@ op(int what) } spval = &stack[sp].val; - if (stack[sp].val.type == MN_UNSET) + if (stack[sp].val.type == MN_UNSET) { *spval = getmathparam(stack + sp); + if (errflag) + return; + } switch (what) { case NOT: if (spval->type & MN_FLOAT) { -- cgit v1.2.3 From 698af7bc1387462c8e87767d7eaeb7e30c6f0b2b Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Fri, 26 Jan 2024 07:33:38 +0100 Subject: 52405, 52502: add empty elements to $match for optional captures that don't match --- ChangeLog | 3 +++ Src/Modules/pcre.c | 5 ++++- Test/V07pcre.ztst | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 280eac2de..e73320081 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-01-26 Oliver Kiddle + * 52405, 52502: Src/Modules/pcre.c, Test/V07pcre.ztst: + add empty elements to $match for optional captures that don't match + * github #110: opensauce04: Completion/Redhat/Command/_dnf: Fix incorrect completion for `dnf --showduplicates` diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c index e48ae3ae5..a49d1a307 100644 --- a/Src/Modules/pcre.c +++ b/Src/Modules/pcre.c @@ -391,6 +391,8 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) pcre_mdata = pcre2_match_data_create_from_pattern(pcre_pattern, NULL); ret = pcre2_match(pcre_pattern, (PCRE2_SPTR) plaintext, subject_len, offset_start, 0, pcre_mdata, mcontext); + if (ret > 0) + ret = pcre2_get_ovector_count(pcre_mdata); } if (ret==0) return_value = 0; @@ -479,7 +481,8 @@ cond_pcre_match(char **a, int id) break; } else if (r>0) { - zpcre_get_substrings(pcre_pat, lhstr_plain, pcre_mdata, r, svar, avar, + uint32_t ovec_count = pcre2_get_ovector_count(pcre_mdata); + zpcre_get_substrings(pcre_pat, lhstr_plain, pcre_mdata, ovec_count, svar, avar, ".pcre.match", 0, isset(BASHREMATCH), !isset(BASHREMATCH)); return_value = 1; break; diff --git a/Test/V07pcre.ztst b/Test/V07pcre.ztst index 585698d05..b8cd31c96 100644 --- a/Test/V07pcre.ztst +++ b/Test/V07pcre.ztst @@ -108,6 +108,11 @@ >0 xo→t →t >0 Xo→t →t + [[ foo =~ (pre)?f(o*)(opt(i)onal)?(y)* ]] + typeset -p match +0:Empty string for optional captures that don't match +>typeset -g -a match=( '' oo '' '' '' ) + string="The following zip codes: 78884 90210 99513" pcre_compile -m "\d{5}" pcre_match -b -- $string && print "$MATCH; ZPCRE_OP: $ZPCRE_OP" -- cgit v1.2.3 From ce033e2b9cadf51b4d043b0737fd3acee9f5eef2 Mon Sep 17 00:00:00 2001 From: Jörg Sommer Date: Mon, 1 Jan 2024 19:10:16 +0100 Subject: 52440: zle.textobjects: Mark variables as const Because these variables are initialized with as constant string, they should be marked as *const* to make the compiler running with `-Wwrite-strings` more happy. --- ChangeLog | 4 ++++ Src/Zle/textobjects.c | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index e73320081..22875c7e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-01-28 Oliver Kiddle + + * Jörg Sommer: 52440: Src/Zle/textobjects.c: Mark variables as const + 2024-01-26 Oliver Kiddle * 52405, 52502: Src/Modules/pcre.c, Test/V07pcre.ztst: diff --git a/Src/Zle/textobjects.c b/Src/Zle/textobjects.c index c93777b65..a68c5296e 100644 --- a/Src/Zle/textobjects.c +++ b/Src/Zle/textobjects.c @@ -283,9 +283,9 @@ selectargument(UNUSED(char **args)) free(linein); if (IS_THINGY(bindk, selectinshellword)) { - ZLE_CHAR_T *match = ZWS("`\'\""); - ZLE_CHAR_T *lmatch = ZWS("\'({"), *rmatch = ZWS("\')}"); - ZLE_CHAR_T *ematch = match, *found; + const ZLE_CHAR_T *match = ZWS("`\'\""); + const ZLE_CHAR_T *lmatch = ZWS("\'({"), *rmatch = ZWS("\')}"); + const ZLE_CHAR_T *ematch = match, *found; int start, end = zlecs; /* for 'in' widget, don't include initial blanks ... */ while (mark < zlecs && ZC_iblank(zleline[mark])) -- cgit v1.2.3 From 98affe1115dfdab932432c09160f46ca3d269fb7 Mon Sep 17 00:00:00 2001 From: Jörg Sommer Date: Mon, 1 Jan 2024 19:10:18 +0100 Subject: 52441: zle_vi: Mark variables with const init as const Because these variables are initialized with as constant string, they should be marked as *const* to make the compiler running with `-Wwrite-strings` more happy. --- ChangeLog | 2 ++ Src/Zle/zle_vi.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 22875c7e9..16e012d5b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2024-01-28 Oliver Kiddle + * Jörg Sommer: 52441: Src/Zle/zle_vi.c: Mark variables as const + * Jörg Sommer: 52440: Src/Zle/textobjects.c: Mark variables as const 2024-01-26 Oliver Kiddle diff --git a/Src/Zle/zle_vi.c b/Src/Zle/zle_vi.c index 24d9de6ea..6692df830 100644 --- a/Src/Zle/zle_vi.c +++ b/Src/Zle/zle_vi.c @@ -1014,7 +1014,7 @@ int visetbuffer(char **args) { ZLE_INT_T ch; - ZLE_CHAR_T *match = ZWS("_*+"); + const ZLE_CHAR_T *match = ZWS("_*+"); int registermod[] = { MOD_NULL, MOD_PRI, MOD_CLIP }; ZLE_CHAR_T *found; -- cgit v1.2.3 From 4929910267a00b8f9958d9e1710580e30d1d5a18 Mon Sep 17 00:00:00 2001 From: Jörg Sommer Date: Mon, 1 Jan 2024 19:10:20 +0100 Subject: 52444: module: Mark name argument of some functions const --- ChangeLog | 3 +++ Src/module.c | 15 ++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 16e012d5b..8cdd990d6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-01-28 Oliver Kiddle + * Jörg Sommer: 52444: Src/module.c: Mark name argument of some + functions const + * Jörg Sommer: 52441: Src/Zle/zle_vi.c: Mark variables as const * Jörg Sommer: 52440: Src/Zle/textobjects.c: Mark variables as const diff --git a/Src/module.c b/Src/module.c index a6005f30b..b4b5d0a2c 100644 --- a/Src/module.c +++ b/Src/module.c @@ -356,7 +356,7 @@ finish_(UNUSED(Module m)) /**/ void -register_module(char *n, Module_void_func setup, +register_module(const char *n, Module_void_func setup, Module_features_func features, Module_enables_func enables, Module_void_func boot, @@ -846,7 +846,7 @@ Hookdef hooktab; /**/ Hookdef -gethookdef(char *n) +gethookdef(const char *n) { Hookdef p; @@ -974,7 +974,7 @@ deletehookdeffunc(Hookdef h, Hookfn f) /**/ mod_export int -deletehookfunc(char *n, Hookfn f) +deletehookfunc(const char *n, Hookfn f) { Hookdef h = gethookdef(n); @@ -1766,7 +1766,7 @@ dyn_finish_module(Module m) #else static Module_generic_func -module_func(Module m, char *name) +module_func(Module m, const char *name) { #ifdef DYNAMIC_NAME_CLASH_OK return (Module_generic_func) dlsym(m->u.handle, name); @@ -2443,7 +2443,7 @@ bin_zmodload(char *nam, char **args, Options ops, UNUSED(int func)) int ops_au = OPT_ISSET(ops,'a') || OPT_ISSET(ops,'u'); int ret = 1, autoopts; /* options only allowed with -F */ - char *fonly = "lP", *fp; + const char *fonly = "lP", *fp; if (ops_bcpf && !ops_au) { zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u"); @@ -3182,7 +3182,7 @@ bin_zmodload_features(const char *nam, char **args, Options ops) } else if (OPT_ISSET(ops, 'L')) printf("zmodload -F %s ", m->node.nam); for (fp = features, ep = enables; *fp; fp++, ep++) { - char *onoff; + const char *onoff; int term; if (*args) { char **argp; @@ -3452,7 +3452,8 @@ autofeatures(const char *cmdnam, const char *module, char **features, defm = NULL; for (; *features; features++) { - char *fnam, *typnam, *feature; + char *fnam, *feature; + const char *typnam; int add, fchar, flags = defflags; autofeaturefn_t fn; -- cgit v1.2.3 From fe276d3873c758ca17e06c31b3c05a3713cfe193 Mon Sep 17 00:00:00 2001 From: Jörg Sommer Date: Mon, 1 Jan 2024 19:10:21 +0100 Subject: 52442: mark hookdef.name as const At least *zle_main* uses const strings to initialize its structure *zlehooks*. --- ChangeLog | 2 ++ Src/zsh.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 8cdd990d6..c85715ef3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2024-01-28 Oliver Kiddle + * Jörg Sommer: 52442: Src/zsh.h: Mark hookdef.name as const + * Jörg Sommer: 52444: Src/module.c: Mark name argument of some functions const diff --git a/Src/zsh.h b/Src/zsh.h index a0243e98e..fae62b8d0 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1578,7 +1578,7 @@ typedef int (*Hookfn) _((Hookdef, void *)); struct hookdef { Hookdef next; - char *name; + const char *name; Hookfn def; int flags; LinkList funcs; -- cgit v1.2.3 From 8e622c25b24fb0b0d6e705fab97960351e123c65 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Mon, 22 Jan 2024 23:19:23 +0100 Subject: unposted: remove unused variable to silence compiler warning --- ChangeLog | 3 +++ Src/Modules/zutil.c | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index c85715ef3..240fb0581 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-01-28 Oliver Kiddle + * unposted: Src/Modules/zutil.c: remove unused variable to silence + compiler warning + * Jörg Sommer: 52442: Src/zsh.h: Mark hookdef.name as const * Jörg Sommer: 52444: Src/module.c: Mark name argument of some diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 293a62dcf..5eccea7a9 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -997,7 +997,7 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) case 'a': { char **ap, *cp; - int nbc = 0, colon = 0, pre = 0, suf = 0; + int nbc = 0, pre = 0, suf = 0; #ifdef MULTIBYTE_SUPPORT int prechars = 0; #endif /* MULTIBYTE_SUPPORT */ @@ -1012,7 +1012,6 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) int dchars = 0; #endif /* MULTIBYTE_SUPPORT */ - colon++; if ((d = cp - *ap - nbc) > pre) pre = d; #ifdef MULTIBYTE_SUPPORT -- cgit v1.2.3 From 3c5dacd503a2ac81059346b37d16ab5d1b6a1e04 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sun, 28 Jan 2024 00:34:05 +0100 Subject: 52499: support highlight groups These are defined in a .zle.hlgroups associative array and referenced using %H in prompt strings or hl= in zle_highlight/region_highlight. --- ChangeLog | 4 ++++ Src/prompt.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 240fb0581..2e0b085e5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2024-01-28 Oliver Kiddle + * 52499: Src/prompt.c: support highlight groups defined in a + .zle.hlgroups associative array and referenced using %H in + prompt strings or hl= in zle_highlight/region_highlight + * unposted: Src/Modules/zutil.c: remove unused variable to silence compiler warning diff --git a/Src/prompt.c b/Src/prompt.c index 39fcf5eb7..e4c213a13 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -241,6 +241,39 @@ promptexpand(char *s, int ns, char *rs, char *Rs) return new_vars.buf; } +/* Parse the argument for %H */ +static char * +parsehighlight(char *arg, char endchar, zattr *atr) +{ + static int entered = 0; + char *var = ".zle.hlgroups"; + struct value vbuf; + Value v; + char *ep, *attrs; + if ((ep = strchr(arg, endchar))) + *ep = '\0'; + if (!entered && (v = getvalue(&vbuf, &var, 0)) && + PM_TYPE(v->pm->node.flags) == PM_HASHED) + { + Param node; + HashTable ht = v->pm->gsu.h->getfn(v->pm); + if ((node = (Param) ht->getnode(ht, arg))) { + attrs = node->gsu.s->getfn(node); + entered = 1; + if (match_highlight(attrs, atr) == attrs) + *atr = TXT_ERROR; + } else + *atr = TXT_ERROR; + } else + *atr = TXT_ERROR; + if (ep) + *ep = endchar; + else + ep = strchr(arg, '\0') - 1; + entered = 0; + return ep; +} + /* Parse the argument for %F and %K */ static zattr parsecolorchar(zattr arg, int is_fg) @@ -571,6 +604,13 @@ putpromptchar(int doprint, int endchar) tunsetattrs(TXTBGCOLOUR); applytextattributes(TSC_PROMPT); break; + case 'H': + bv->fm = parsehighlight(bv->fm + 2, '}', &atr); + if (atr != TXT_ERROR) { + treplaceattrs(atr); + applytextattributes(TSC_PROMPT); + } + break; case '[': if (idigit(*++bv->fm)) arg = zstrtol(bv->fm, &bv->fm, 10); @@ -1856,11 +1896,17 @@ match_highlight(const char *teststr, zattr *on_var) *on_var = 0; while (found && *teststr) { const struct highlight *hl; + zattr atr = 0; found = 0; - if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) { + if (strpfx("hl=", teststr)) { + teststr += 3; + teststr = parsehighlight((char *)teststr, ',', &atr); + if (atr != TXT_ERROR) + *on_var = atr; + found = 1; + } else if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) { int is_fg = (teststr[0] == 'f'); - zattr atr; teststr += 3; atr = match_colour(&teststr, is_fg, 0); -- cgit v1.2.3 From 85545af42b8f9278136125324c37c1f88b461421 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sun, 28 Jan 2024 00:47:10 +0100 Subject: 52500: add layer token to zle attributes This provide control over the precedence of highlighting where different regions overlap. --- ChangeLog | 4 ++ Src/Zle/zle.h | 2 + Src/Zle/zle_refresh.c | 115 +++++++++++++++++++++++++++++++------------------- Src/prompt.c | 13 +++++- 4 files changed, 88 insertions(+), 46 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 2e0b085e5..7335590c9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2024-01-28 Oliver Kiddle + * 52500: Src/Zle/zle.h, Src/Zle/zle_refresh.c, Src/prompt.c: + add layer token to zle attributes to provide control over + the precedence of highlighting + * 52499: Src/prompt.c: support highlight groups defined in a .zle.hlgroups associative array and referenced using %H in prompt strings or hl= in zle_highlight/region_highlight diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index 1a3e4c241..010ead3d2 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -435,6 +435,8 @@ enum { struct region_highlight { /* Attributes turned on in the region */ zattr atr; + /* Priority for this region relative to others that overlap */ + int layer; /* Start of the region */ int start; /* Start of the region in metafied ZLE line */ diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index a587f696a..f076bdd61 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -210,6 +210,12 @@ int predisplaylen, postdisplaylen; static zattr default_attr, special_attr, ellipsis_attr; +/* + * Layer applied to highlighting for special characters + */ + +static int special_layer; + /* * Array of region highlights, no special termination. * The first N_SPECIAL_HIGHLIGHTS elements describe special uses of @@ -337,6 +343,13 @@ zle_set_highlight(void) } } + /* Default layers */ + region_highlights[0].layer = 20; /* region */ + region_highlights[1].layer = 20; /* isearch */ + region_highlights[2].layer = 10; /* suffix */ + region_highlights[3].layer = 15; /* paste */ + special_layer = 30; + if (atrs) { for (; *atrs; atrs++) { if (!strcmp(*atrs, "none")) { @@ -346,30 +359,34 @@ zle_set_highlight(void) paste_attr_set = region_attr_set = isearch_attr_set = suffix_attr_set = 1; } else if (strpfx("default:", *atrs)) { - match_highlight(*atrs + 8, &default_attr); + match_highlight(*atrs + 8, &default_attr, NULL); } else if (strpfx("special:", *atrs)) { - match_highlight(*atrs + 8, &special_attr); + match_highlight(*atrs + 8, &special_attr, &special_layer); special_attr_set = 1; } else if (strpfx("region:", *atrs)) { - match_highlight(*atrs + 7, ®ion_highlights[0].atr); + match_highlight(*atrs + 7, &(region_highlights[0].atr), + &(region_highlights[0].layer)); region_attr_set = 1; } else if (strpfx("isearch:", *atrs)) { - match_highlight(*atrs + 8, &(region_highlights[1].atr)); + match_highlight(*atrs + 8, &(region_highlights[1].atr), + &(region_highlights[1].layer)); isearch_attr_set = 1; } else if (strpfx("suffix:", *atrs)) { - match_highlight(*atrs + 7, &(region_highlights[2].atr)); + match_highlight(*atrs + 7, &(region_highlights[2].atr), + &(region_highlights[2].layer)); suffix_attr_set = 1; } else if (strpfx("paste:", *atrs)) { - match_highlight(*atrs + 6, &(region_highlights[3].atr)); + match_highlight(*atrs + 6, &(region_highlights[3].atr), + &(region_highlights[3].layer)); paste_attr_set = 1; } else if (strpfx("ellipsis:", *atrs)) { - match_highlight(*atrs + 9, &ellipsis_attr); + match_highlight(*atrs + 9, &ellipsis_attr, NULL); ellipsis_attr_set = 1; } } } - /* Defaults */ + /* Default attributes */ if (!special_attr_set) special_attr = TXTSTANDOUT; if (!region_attr_set) @@ -407,14 +424,13 @@ zle_free_highlight(void) char ** get_region_highlight(UNUSED(Param pm)) { - int arrsize = n_region_highlights; + int arrsize = n_region_highlights - N_SPECIAL_HIGHLIGHTS; char **retarr, **arrp; struct region_highlight *rhp; /* region_highlights may not have been set yet */ - if (!arrsize) + if (!n_region_highlights) return hmkarray(NULL); - arrsize -= N_SPECIAL_HIGHLIGHTS; DPUTS(arrsize < 0, "arrsize is negative from n_region_highlights"); arrp = retarr = (char **)zhalloc((arrsize+1)*sizeof(char *)); @@ -422,20 +438,18 @@ get_region_highlight(UNUSED(Param pm)) for (rhp = region_highlights + N_SPECIAL_HIGHLIGHTS; arrsize--; rhp++, arrp++) { - char digbuf1[DIGBUFSIZE], digbuf2[DIGBUFSIZE]; - int atrlen, alloclen; - const char memo_equals[] = "memo="; - - sprintf(digbuf1, "%d", rhp->start); - sprintf(digbuf2, "%d", rhp->end); - - atrlen = output_highlight(rhp->atr, NULL); - alloclen = atrlen + strlen(digbuf1) + strlen(digbuf2) + - 3; /* 2 spaces, 1 terminating NUL */ + char digbuf[2 * DIGBUFSIZE], layerbuf[7 + DIGBUFSIZE]; + int offset; + const char memo_equals[] = " memo="; + int alloclen = sprintf(digbuf, "%d %d", rhp->start, rhp->end) + + output_highlight(rhp->atr, NULL) + + 2; /* space and terminating NUL */ if (rhp->flags & ZRH_PREDISPLAY) - alloclen += 2; /* "P " */ + alloclen++; /* "P" */ + if (rhp->layer != 10) + alloclen += sprintf(layerbuf, ",layer=%d", rhp->layer); if (rhp->memo) - alloclen += 1 /* space */ + strlen(memo_equals) + strlen(rhp->memo); + alloclen += sizeof(memo_equals) - 1 + strlen(rhp->memo); *arrp = (char *)zhalloc(alloclen * sizeof(char)); /* * On input we allow a space after the flags. @@ -444,13 +458,14 @@ get_region_highlight(UNUSED(Param pm)) * into three words, and then check the first to * see if there are flags. However, it's arguable. */ - sprintf(*arrp, "%s%s %s ", + offset = sprintf(*arrp, "%s%s ", (rhp->flags & ZRH_PREDISPLAY) ? "P" : "", - digbuf1, digbuf2); - (void)output_highlight(rhp->atr, *arrp + strlen(*arrp)); + digbuf); + (void)output_highlight(rhp->atr, *arrp + offset); + if (rhp->layer != 10) + strcat(*arrp, layerbuf); if (rhp->memo) { - strcat(*arrp, " "); strcat(*arrp, memo_equals); strcat(*arrp, rhp->memo); } @@ -459,12 +474,10 @@ get_region_highlight(UNUSED(Param pm)) return retarr; } - /* * The parameter system requires the pm argument, but this * may be NULL if called directly. */ - /**/ void set_region_highlight(UNUSED(Param pm), char **aval) @@ -523,7 +536,8 @@ set_region_highlight(UNUSED(Param pm), char **aval) while (inblank(*strp)) strp++; - strp = (char*) match_highlight(strp, &rhp->atr); + rhp->layer = 10; /* default */ + strp = (char*) match_highlight(strp, &rhp->atr, &rhp->layer); while (inblank(*strp)) strp++; @@ -1180,27 +1194,40 @@ zrefresh(void) rpms.s = nbuf[rpms.ln = 0] + lpromptw; rpms.sen = *nbuf + winw; for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) { - unsigned ireg; zattr base_attr = mixattrs(default_attr, prompt_attr); zattr all_attr; struct region_highlight *rhp; + int layer, nextlayer = 0; /* * Calculate attribute based on region. */ - for (ireg = 0, rhp = region_highlights; - ireg < n_region_highlights; - ireg++, rhp++) { - int offset; - if (rhp->flags & ZRH_PREDISPLAY) - offset = 0; /* include predisplay in start end */ - else - offset = predisplaylen; /* increment over it */ - if (rhp->start + offset <= tmppos && - tmppos < rhp->end + offset) { - base_attr = mixattrs(rhp->atr, base_attr); + do { + unsigned ireg; + layer = nextlayer; + nextlayer = special_layer; + for (ireg = 0, rhp = region_highlights; + ireg < n_region_highlights; + ireg++, rhp++) { + if (rhp->layer == layer) { + int offset; + if (rhp->flags & ZRH_PREDISPLAY) + offset = 0; /* include predisplay in start end */ + else + offset = predisplaylen; /* increment over it */ + if (rhp->start + offset <= tmppos && + tmppos < rhp->end + offset) { + base_attr = mixattrs(rhp->atr, base_attr); + if (layer > special_layer) + all_attr = mixattrs(rhp->atr, all_attr); + } + } else if (rhp->layer > layer && rhp->layer < nextlayer) { + nextlayer = rhp->layer; + } } - } - all_attr = mixattrs(special_attr, base_attr); + if (special_layer == layer) { + all_attr = mixattrs(special_attr, base_attr); + } + } while (nextlayer > layer); if (t == scs) /* if cursor is here, remember it */ rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln]; diff --git a/Src/prompt.c b/Src/prompt.c index e4c213a13..ec79067cd 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -260,7 +260,7 @@ parsehighlight(char *arg, char endchar, zattr *atr) if ((node = (Param) ht->getnode(ht, arg))) { attrs = node->gsu.s->getfn(node); entered = 1; - if (match_highlight(attrs, atr) == attrs) + if (match_highlight(attrs, atr, 0) == attrs) *atr = TXT_ERROR; } else *atr = TXT_ERROR; @@ -1884,12 +1884,13 @@ match_colour(const char **teststrp, int is_fg, int colour) /* * Match a set of highlights in the given teststr. * Set *on_var to reflect the values found. + * Set *layer to the layer * Return a pointer to the first character not consumed. */ /**/ mod_export const char * -match_highlight(const char *teststr, zattr *on_var) +match_highlight(const char *teststr, zattr *on_var, int *layer) { int found = 1; @@ -1918,6 +1919,14 @@ match_highlight(const char *teststr, zattr *on_var) /* skip out of range colours but keep scanning attributes */ if (atr != TXT_ERROR) *on_var |= atr; + } else if (layer && strpfx("layer=", teststr)) { + teststr += 6; + *layer = (int) zstrtol(teststr, (char **) &teststr, 10); + if (*teststr == ',') + teststr++; + else if (*teststr && *teststr != ' ') + break; + found = 1; } else { for (hl = highlights; hl->name; hl++) { if (strpfx(hl->name, teststr)) { -- cgit v1.2.3 From d7cf4f25ebe2ec00b5e557e8202d74fa86a36062 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 28 Jan 2024 17:14:23 -0800 Subject: 52509: manage internals of stdio objects when performing redirections. --- ChangeLog | 5 +++++ Src/utils.c | 22 ++++++++++++++++++++++ configure.ac | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 7335590c9..6c52ccd22 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-01-28 Bart Schaefer + + * 52509: configure.ac, Src/utils.c: manage internals of stdio + objects when performing redirections. + 2024-01-28 Oliver Kiddle * 52500: Src/Zle/zle.h, Src/Zle/zle_refresh.c, Src/prompt.c: diff --git a/Src/utils.c b/Src/utils.c index 1a4f4c14b..0fda92709 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -2008,6 +2008,28 @@ redup(int x, int y) { int ret = y; +#ifdef HAVE_FPURGE + /* Make sure buffers are cleared when changing descriptor for a + * FILE object. No fflush() here because the only way anything + * can legitimately be left in the buffer is when an error has + * occurred, so attempting flush here would at best error again + * and at worst squirt out something unexpected. + */ + if (stdout && y == fileno(stdout)) + fpurge(stdout); + if (stderr && y == fileno(stderr)) + fpurge(stderr); + if (shout && y == fileno(shout)) + fpurge(shout); + if (xtrerr && y == fileno(xtrerr)) + fpurge(xtrerr); +#ifndef _IONBF + /* See init.c setupshin() -- stdin otherwise unbuffered */ + if (stdin && y == fileno(stdin)) + fpurge(stdin); +#endif +#endif + if(x < 0) zclose(y); else if (x != y) { diff --git a/configure.ac b/configure.ac index 2871dcb7c..175d90433 100644 --- a/configure.ac +++ b/configure.ac @@ -1295,7 +1295,7 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \ select poll \ readlink faccessx fchdir ftruncate \ fstat lstat lchown fchown fchmod \ - fseeko ftello \ + fpurge fseeko ftello \ mkfifo _mktemp mkstemp \ waitpid wait3 \ sigaction sigblock sighold sigrelse sigsetmask sigprocmask \ -- cgit v1.2.3 From 8801665e5b241c3adac9c36b6135d057c5ab2a59 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 3 Feb 2024 12:07:14 -0800 Subject: 52513: fixes and doc for using nofork substitutions with private parameters Also fixes a crash bug with {fd}>&N redirections and private parameters --- ChangeLog | 7 +++ Doc/Zsh/mod_private.yo | 9 +++- Src/Modules/param_private.c | 53 ++++++++++++-------- Src/params.c | 17 +++++-- Test/V10private.ztst | 118 +++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 170 insertions(+), 34 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 46cd45647..fbfae8589 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2024-02-03 Bart Schaefer + + * 52513: Doc/Zsh/mod_private.yo, Src/Modules/param_private.c, + Src/params.c, Test/v10private.ztst: nofork substitutions can + use private parameters; fix crash bug on {privateFD}>&N; add + tests and documentation + 2024-01-28 Bart Schaefer * 52510: Doc/Zsh/expn.yo, Doc/Zsh/mod_private.yo: document how diff --git a/Doc/Zsh/mod_private.yo b/Doc/Zsh/mod_private.yo index 69a5f58be..08ac4cafe 100644 --- a/Doc/Zsh/mod_private.yo +++ b/Doc/Zsh/mod_private.yo @@ -84,9 +84,14 @@ created outside the local scope when it was not previously declared.) itemiz(An exported private remains in the environment of inner scopes but appears unset for the current shell in those scopes. Generally, exporting private parameters should be avoided.) +itemiz(Current shell command substitutions such as `tt(${|)...tt(})', +`tt(${|)var(var)tt(|)...tt(})' and `tt(${ )...tt( })' may read and assign +private parameters from the enclosing function.) itemiz(Declaring a private parameter in a current shell command substitution -such as `tt(${ )...tt( })' limits the parameter to the scope of the command -substitution, just as if the parameter were declared in a function.) +limits that parameter to the scope of the command substitution, just as if +the parameter were declared in a function. This also prevents access by +any enclosed current shell command substitutions, but other substitutions +may use the private parameter because those have the same calling scope.) enditemize() Note that this differs from the static scope defined by compiled languages diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index 7ef6633da..5003d4627 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -298,7 +298,7 @@ pps_setfn(Param pm, char *x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.s); GsuScalar gsu = (GsuScalar)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -338,7 +338,7 @@ ppi_setfn(Param pm, zlong x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.i); GsuInteger gsu = (GsuInteger)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -378,7 +378,7 @@ ppf_setfn(Param pm, double x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.f); GsuFloat gsu = (GsuFloat)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -419,7 +419,7 @@ ppa_setfn(Param pm, char **x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.a); GsuArray gsu = (GsuArray)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -461,7 +461,7 @@ pph_setfn(Param pm, HashTable x) { struct gsu_closure *c = (struct gsu_closure *)(pm->gsu.h); GsuHash gsu = (GsuHash)(c->g); - if (locallevel == pm->level) + if (locallevel == pm->level || locallevel > private_wraplevel) gsu->setfn(pm, x); else setfn_error(pm); @@ -539,19 +539,20 @@ static struct funcwrap wrapper[] = { WRAPDEF(wrap_private) }; +/**/ +static int private_wraplevel = 0; + /**/ static int wrap_private(Eprog prog, FuncWrap w, char *name) { - static int wraplevel = 0; - - if (wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) { - int owl = wraplevel; - wraplevel = locallevel; + if (private_wraplevel < locallevel /* && strcmp(name, "(anon)") != 0 */) { + int owl = private_wraplevel; + private_wraplevel = locallevel; scanhashtable(paramtab, 0, 0, 0, scopeprivate, PM_UNSET); runshfunc(prog, w, name); scanhashtable(paramtab, 0, 0, 0, scopeprivate, 0); - wraplevel = owl; + private_wraplevel = owl; return 0; } return 1; @@ -573,22 +574,32 @@ getprivatenode(HashTable ht, const char *nam) pm = (Param) hn; /* how would an autoloaded private behave? return here? */ } - while (!fakelevel && pm && locallevel > pm->level && is_private(pm)) { + while (!fakelevel && pm && is_private(pm) && locallevel > pm->level) { + if (pm->level == private_wraplevel + 1) { + /* Variable is in the current function scope */ + break; + } +#if 0 if (!(pm->node.flags & PM_UNSET)) { /* * private parameters are always marked PM_UNSET before we - * increment locallevel, so the only way we get here is - * when createparam() wants a new parameter that is not at - * the current locallevel and it has therefore cleared the - * PM_UNSET flag. + * increment locallevel, so there are three possible ways + * to get here: + * 1) createparam() wants a new parameter that is not at + * the current locallevel and it has therefore cleared the + * PM_UNSET flag + * 2) locallevel has been incremented (startparamscope()) + * outside the usual function call stack (private_wraplevel) + * 3) dynamic scoping is fetching a value from a surrounding + * scope, we don't know if that's for assign or just expand + * The first of those is now caught in createparam() when + * testing PM_RO_BY_DESIGN and the second occurs only in + * nofork substitution or handling of ZLE specials. If the + * third is an assignment, the GSU setfn rejects it. */ DPUTS(pm->old, "BUG: PM_UNSET cleared in wrong scope"); - setfn_error(pm); - /* - * TODO: instead of throwing an error here, create a global - * parameter, insert as pm->old, handle WARN_CREATE_GLOBAL. - */ } +#endif pm = pm->old; } diff --git a/Src/params.c b/Src/params.c index 9f0cbcd67..a722a20f6 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1049,7 +1049,7 @@ createparam(char *name, int flags) /* POSIXBUILTINS horror: we need to retain 'export' flags */ (isset(POSIXBUILTINS) && (oldpm->node.flags & PM_EXPORTED))) { if (oldpm->node.flags & PM_RO_BY_DESIGN) { - zerr("%s: can't change parameter attribute", + zerr("%s: can't modify read-only parameter", name); return NULL; } @@ -3615,9 +3615,18 @@ assignnparam(char *s, mnumber val, int flags) pm = createparam(t, ss ? PM_ARRAY : isset(POSIXIDENTIFIERS) ? PM_SCALAR : (val.type & MN_INTEGER) ? PM_INTEGER : PM_FFLOAT); - if (!pm) - pm = (Param) paramtab->getnode(paramtab, t); - DPUTS(!pm, "BUG: parameter not created"); + if (errflag) { + /* assume error message already output */ + unqueue_signals(); + return NULL; + } + if (!pm && !(pm = (Param) paramtab->getnode(paramtab, t))) { + DPUTS(!pm, "BUG: parameter not created"); + if (!errflag) + zerr("%s: parameter not found", t); + unqueue_signals(); + return NULL; + } if (ss) { *ss = '['; } else if (val.type & MN_INTEGER) { diff --git a/Test/V10private.ztst b/Test/V10private.ztst index d902cac56..9eeda0f47 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -10,6 +10,8 @@ sed -e 's,# test_zsh_param_private,zmodload zsh/param/private,' < $ZTST_srcdir/B02typeset.ztst > private.TMP/B02 fi + setopt TYPESET_TO_UNSET + %test (zmodload -u zsh/param/private && zmodload zsh/param/private) @@ -246,7 +248,7 @@ F:note "typeset" rather than "private" in output from outer 1:privates are not visible in anonymous functions, part 3 >X top level >array_test not set -?(anon):4: array_test: can't change parameter attribute +?(anon):4: array_test: can't modify read-only parameter F:future revision will create a global with this assignment typeset -a array_test @@ -311,11 +313,30 @@ F:future revision will create a global with this assignment () { typeset -n ptr1=ptr2 - private -n ptr2 + private -n ptr2 # TYPESET_TO_UNSET makes this not a "placeholder" typeset -p ptr1 ptr2 typeset val=LOCAL () { - ptr1=val + ptr1=val # Test dies here as ptr2 is private and unset + typeset -n + printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" + } + typeset -p ptr1 ptr2 + } + typeset -p ptr2 +1:up-reference for private namerefs, end unset and not in scope +F:See K01nameref.ztst up-reference part 5 +F:Here ptr1 finds private ptr2 by scope mismatch +>typeset -n ptr1=ptr2 +*?*read-only variable: ptr2 + + () { + typeset -n ptr1=ptr2 + private -n ptr2= # Assignment makes this a placeholder, not unset + typeset -p ptr1 ptr2 + typeset val=LOCAL + () { + ptr1=val # This is a silent no-op, why? typeset -n printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" } @@ -323,8 +344,9 @@ F:future revision will create a global with this assignment } typeset -p ptr2 1:up-reference for private namerefs, end not in scope -F:See K01typeset.ztst up-reference part 5 -F:Here ptr1 finds private ptr2 by scope mismatch, assignment silently fails +F:See K01nameref.ztst up-reference part 5 +F:Here ptr1 finds private ptr2 by scope mismatch +F:Assignment silently fails, is that correct? >typeset -n ptr1=ptr2 >ptr1=ptr2 >ptr1= @@ -335,11 +357,11 @@ F:Here ptr1 finds private ptr2 by scope mismatch, assignment silently fails typeset ptr2 () { typeset -n ptr1=ptr2 - private -n ptr2 + private -n ptr2 # Set/unset is irrelevant, not referenced typeset -p ptr1 ptr2 typeset val=LOCAL () { - ptr1=val; + ptr1=val typeset -n printf "%s=%s\n" ptr1 "$ptr1" ptr2 "$ptr2" } @@ -401,6 +423,88 @@ F:Should we allow "public" namerefs to private parameters? 1:private may not change parameter type ?(anon):private:2: can't change type of private param: x + () { + private fd1 fd2 + exec {fd1}>&1 + print OK + () { exec {fd2}>&2 } + print BAD $fd2 + } +1:redirection cannot assign private in wrong scope +F:Better if caught in checkclobberparam() but exec.c doesn't know scope +>OK +?(anon): fd2: can't modify read-only parameter + + () { + private z=outer + print ${(t)z} $z + print ${| + print ${(t)z} $z + REPLY=$z + } + } +0:nofork may read private in calling function +>scalar-local-hide-special outer +>scalar-local-hide-special outer +>outer + + () { + private z=outer + print ${(t)z} $z + print ${| REPLY=${|z| z=nofork} } + print ${(t)z} $z + } +0:nofork may write to private in calling function +>scalar-local-hide-special outer +>nofork +>scalar-local-hide-special nofork + + () { + local q=outer + print ${| + private q=nofork + REPLY=${| REPLY=$q} + } + } +0:nofork cannot see private in surrounding nofork +>outer + + () { + private z=outer + print ${(t)z} $z + print ${|z| + private q + z=${|q| q=nofork} + } + print ${(t)z} $z + } +1:nofork may not change private in surrounding nofork +>scalar-local-hide-special outer +*?*: q: can't modify read-only parameter + + () { + private q=outer + print ${| + () { REPLY="{$q}" } + } + print ${|q| + () { q=nofork } + } + } +1:function may not access private from inside nofork +>{} +*?*: q: can't modify read-only parameter + + () { + print ${| + private q + () { q=nofork } + } + } +1:function may not access private declared in nofork +*?*: q: can't modify read-only parameter + %clean + unsetopt TYPESET_TO_UNSET rm -r private.TMP -- cgit v1.2.3 From ce8909b49428e260c15dce22d764f2831295645a Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 3 Feb 2024 19:52:39 -0800 Subject: unposted: Record as comments some notes about namespace usage exceptions. --- ChangeLog | 3 +++ Src/Zle/zle_tricky.c | 5 ++++- Src/parse.c | 2 ++ Src/pattern.c | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index fbfae8589..386ef3ab9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-02-03 Bart Schaefer + * unposted: Src/Zle/zle_tricky.c, Src/parse.c, Src/pattern.c: + Record as comments some notes about namespace usage exceptions. + * 52513: Doc/Zsh/mod_private.yo, Src/Modules/param_private.c, Src/params.c, Test/v10private.ztst: nofork substitutions can use private parameters; fix crash bug on {privateFD}>&N; add diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 6ceb5d87f..ea2a52390 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -1499,6 +1499,7 @@ get_comp_string(void) if (varq) tt = clwords[clwpos]; + /* The only place we complete namespaces, see IIDENT below */ s = itype_end(tt, INAMESPC, 0); sav = *s; *s = '\0'; @@ -1570,6 +1571,8 @@ get_comp_string(void) i = 0; MB_METACHARINIT(); + /* All further uses of IIDENT in this file should change to * + * INAMESPACE if this case is changed. Too ugly to risk now. */ if (itype_end(s, IIDENT, 1) == s) nnb = s + MB_METACHARLEN(s); else @@ -1643,7 +1646,7 @@ get_comp_string(void) } else { /* In mathematical expression, we complete parameter names * * (even if they don't have a `$' in front of them). So we * - * have to find that name. */ + * have to find that name. See above regarding INAMESPC */ char *cspos = zlemetaline + zlemetacs, *wptr, *cptr; we = itype_end(cspos, IIDENT, 0) - zlemetaline; diff --git a/Src/parse.c b/Src/parse.c index 859f4d0fc..2b7e003fc 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -1935,6 +1935,8 @@ par_simple(int *cmplx, int nr) if (*ptr == Outbrace && ptr > tokstr + 1) { + /* Should we allow namespace FDs, {.foo.bar}>&file ? * + * If so, change IIDENT to INAMESPACE here */ if (itype_end(tokstr+1, IIDENT, 0) >= ptr) { char *toksave = tokstr; diff --git a/Src/pattern.c b/Src/pattern.c index 2a1a514fb..1e0ae88d9 100644 --- a/Src/pattern.c +++ b/Src/pattern.c @@ -3691,6 +3691,7 @@ mb_patmatchrange(char *range, wchar_t ch, int zmb_ind, wint_t *indptr, int *mtp) return 1; break; case PP_IDENT: + /* Could use INAMESPC here? */ if (wcsitype(ch, IIDENT)) return 1; break; -- cgit v1.2.3 From c039a74e094bb6a4d965899f67fe34d2b84dda8d Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 3 Feb 2024 19:55:41 -0800 Subject: Typos in previous commit comments --- Src/Zle/zle_tricky.c | 2 +- Src/parse.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index ea2a52390..225ce8c74 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -1572,7 +1572,7 @@ get_comp_string(void) i = 0; MB_METACHARINIT(); /* All further uses of IIDENT in this file should change to * - * INAMESPACE if this case is changed. Too ugly to risk now. */ + * INAMESPC if this case is changed. Too ugly to risk now. */ if (itype_end(s, IIDENT, 1) == s) nnb = s + MB_METACHARLEN(s); else diff --git a/Src/parse.c b/Src/parse.c index 2b7e003fc..821812c78 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -1936,7 +1936,7 @@ par_simple(int *cmplx, int nr) if (*ptr == Outbrace && ptr > tokstr + 1) { /* Should we allow namespace FDs, {.foo.bar}>&file ? * - * If so, change IIDENT to INAMESPACE here */ + * If so, change IIDENT to INAMESPC here */ if (itype_end(tokstr+1, IIDENT, 0) >= ptr) { char *toksave = tokstr; -- cgit v1.2.3 From ec446a6f349c3f0a31ccd4cd21a257555bf53382 Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Sun, 4 Feb 2024 16:25:14 +0100 Subject: 52516: fix crash in %H when hlgroups is empty typeset -A .zle.hlgroups; print -P %H --- ChangeLog | 4 ++++ Src/prompt.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 4f059baa1..1804037f3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-02-04 Mikael Magnusson + + * 52516: Src/prompt.c: fix crash in %H when hlgroups is empty + 2024-02-03 Bart Schaefer * unposted: Util/printdefines: updates and fix omissions diff --git a/Src/prompt.c b/Src/prompt.c index ec79067cd..b1e5041bd 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -257,7 +257,7 @@ parsehighlight(char *arg, char endchar, zattr *atr) { Param node; HashTable ht = v->pm->gsu.h->getfn(v->pm); - if ((node = (Param) ht->getnode(ht, arg))) { + if (ht && (node = (Param) ht->getnode(ht, arg))) { attrs = node->gsu.s->getfn(node); entered = 1; if (match_highlight(attrs, atr, 0) == attrs) -- cgit v1.2.3 From 653be0823da9a6b085a0092ac3a7b8792afc142a Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Sun, 4 Feb 2024 16:44:21 +0100 Subject: 52517: ensure that %H is followed by { The previous code would accept any character after %H assuming it was a {, which was probably also a buffer overrun sometimes. --- ChangeLog | 2 ++ Src/prompt.c | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 1804037f3..bb619cf49 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,8 @@ * 52516: Src/prompt.c: fix crash in %H when hlgroups is empty + * 52517: Src/prompt.c: ensure that %H is followed by { + 2024-02-03 Bart Schaefer * unposted: Util/printdefines: updates and fix omissions diff --git a/Src/prompt.c b/Src/prompt.c index b1e5041bd..0d674ceab 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -605,10 +605,12 @@ putpromptchar(int doprint, int endchar) applytextattributes(TSC_PROMPT); break; case 'H': - bv->fm = parsehighlight(bv->fm + 2, '}', &atr); - if (atr != TXT_ERROR) { - treplaceattrs(atr); - applytextattributes(TSC_PROMPT); + if (bv->fm[1] == '{') { + bv->fm = parsehighlight(bv->fm + 2, '}', &atr); + if (atr != TXT_ERROR) { + treplaceattrs(atr); + applytextattributes(TSC_PROMPT); + } } break; case '[': -- cgit v1.2.3 From 04ae7dc64cde2fa6b7354f685596ff570404a2c9 Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Fri, 9 Feb 2024 19:26:54 +0100 Subject: 52526: metafy terminfo capabilities --- ChangeLog | 4 ++++ Src/Modules/terminfo.c | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 964eeef23..639f19369 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-02-09 Mikael Magnusson + + * 52526: Src/Modules/terminfo.c: metafy terminfo capabilities + 2024-02-04 Bart Schaefer * unposted (cf. users/29635): Doc/Zsh/arith.yo: additional detail diff --git a/Src/Modules/terminfo.c b/Src/Modules/terminfo.c index 4596b41d2..f9ab64fb3 100644 --- a/Src/Modules/terminfo.c +++ b/Src/Modules/terminfo.c @@ -160,7 +160,7 @@ getterminfo(UNUSED(HashTable ht), const char *name) pm->node.flags |= PM_SCALAR; pm->gsu.s = &nullsetscalar_gsu; } else if ((tistr = (char *)tigetstr(nameu)) != NULL && tistr != (char *)-1) { - pm->u.str = dupstring(tistr); + pm->u.str = metafy(tistr, -1, META_HEAPDUP); pm->node.flags |= PM_SCALAR; pm->gsu.s = &nullsetscalar_gsu; } else { @@ -280,7 +280,7 @@ scanterminfo(UNUSED(HashTable ht), ScanFunc func, int flags) for (capname = (char **)strnames; *capname; capname++) { if ((tistr = (char *)tigetstr(*capname)) != NULL && tistr != (char *)-1) { - pm->u.str = dupstring(tistr); + pm->u.str = metafy(tistr, -1, META_HEAPDUP); pm->node.nam = dupstring(*capname); func(&pm->node, flags); } -- cgit v1.2.3 From 14c230dc3216b7fe0f63d797347e14178d4ede2b Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Thu, 15 Feb 2024 14:48:04 +0100 Subject: 52533: add module to provide alternate readonly views of the content of .zle.hlgroups --- ChangeLog | 6 ++ Src/Modules/hlgroup.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++++ Src/Modules/hlgroup.mdd | 7 ++ Src/prompt.c | 28 +++++++ 4 files changed, 252 insertions(+) create mode 100644 Src/Modules/hlgroup.c create mode 100644 Src/Modules/hlgroup.mdd (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 639f19369..6d064eaf6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2024-02-15 Oliver Kiddle + + * 52533: Src/Modules/hlgroup.c, Src/Modules/hlgroup.mdd, + Src/prompt.c: add module to provide alternate readonly views of + the content of .zle.hlgroups + 2024-02-09 Mikael Magnusson * 52526: Src/Modules/terminfo.c: metafy terminfo capabilities diff --git a/Src/Modules/hlgroup.c b/Src/Modules/hlgroup.c new file mode 100644 index 000000000..6382f3216 --- /dev/null +++ b/Src/Modules/hlgroup.c @@ -0,0 +1,211 @@ +/* + * hlgroup.c - Supporting parameters for highlight groups + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 2024 Oliver Kiddle + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Oliver Kiddle or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Oliver Kiddle and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Oliver Kiddle and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Oliver Kiddle and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "hlgroup.mdh" +#include "hlgroup.pro" + +#define GROUPVAR ".zle.hlgroups" + +static const struct gsu_scalar pmesc_gsu = +{ strgetfn, nullstrsetfn, nullunsetfn }; + +/**/ +static char * +convertattr(char *attrstr, int sgr) +{ + zattr atr; + char *r, *s; + int len; + + match_highlight(attrstr, &atr, NULL); + s = zattrescape(atr, sgr ? NULL : &len); + + if (sgr) { + char *c = s, *t = s - 1; + + while (c[0] == '\033' && c[1] == '[') { + c += 2; + while (isdigit(*c) || *c == ';') + *++t = *c++; + t++; + if (*c != 'm') + break; + *t = ';'; + c++; + } + *t = '\0'; + len = t - s; + } + + r = dupstring_wlen(s, len); + free(s); + return r; +} + +/**/ +static HashNode +getgroup(const char *name, int sgr) +{ + Param pm = NULL; + HashNode hn; + HashTable hlg; + Value v; + struct value vbuf; + char *var = GROUPVAR; + + pm = (Param) hcalloc(sizeof(struct param)); + pm->gsu.s = &pmesc_gsu; + pm->node.nam = dupstring(name); + pm->node.flags = PM_SCALAR|PM_SPECIAL; + + if (!(v = getvalue(&vbuf, &var, 0)) || + PM_TYPE(v->pm->node.flags) != PM_HASHED || + !(hlg = v->pm->gsu.h->getfn(v->pm)) || + !(hn = gethashnode2(hlg, name))) + { + pm->u.str = dupstring(""); + pm->node.flags |= PM_UNSET; + } else { + pm->u.str = convertattr(((Param) hn)->u.str, sgr); + } + + return &pm->node; +} + +/**/ +static void +scangroup(ScanFunc func, int flags, int sgr) +{ + struct param pm; + int i; + HashNode hn; + HashTable hlg; + Value v; + struct value vbuf; + char *var = GROUPVAR; + + if (!(v = getvalue(&vbuf, &var, 0)) || + PM_TYPE(v->pm->node.flags) != PM_HASHED) + return; + hlg = v->pm->gsu.h->getfn(v->pm); + + memset((void *)&pm, 0, sizeof(struct param)); + pm.node.flags = PM_SCALAR; + pm.gsu.s = &pmesc_gsu; + + for (i = 0; i < hlg->hsize; i++) + for (hn = hlg->nodes[i]; hn; hn = hn->next) { + pm.u.str = convertattr(((Param) hn)->u.str, sgr); + pm.node.nam = hn->nam; + func(&pm.node, flags); + } +} +/**/ +static HashNode +getpmesc(UNUSED(HashTable ht), const char *name) +{ + return getgroup(name, 0); +} + +/**/ +static void +scanpmesc(UNUSED(HashTable ht), ScanFunc func, int flags) +{ + return scangroup(func, flags, 0); +} + +/**/ +static HashNode +getpmsgr(UNUSED(HashTable ht), const char *name) +{ + return getgroup(name, 1); +} + +/**/ +static void +scanpmsgr(UNUSED(HashTable ht), ScanFunc func, int flags) +{ + return scangroup(func, flags, 1); +} + +static struct paramdef partab[] = { + SPECIALPMDEF(".zle.esc", PM_READONLY_SPECIAL, 0, getpmesc, scanpmesc), + SPECIALPMDEF(".zle.sgr", PM_READONLY_SPECIAL, 0, getpmsgr, scanpmsgr) +}; + +static struct features module_features = { + NULL, 0, + NULL, 0, + NULL, 0, + partab, sizeof(partab)/sizeof(*partab), + 0 +}; + +/**/ +int +setup_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +features_(Module m, char ***features) +{ + *features = featuresarray(m, &module_features); + return 0; +} + +/**/ +int +enables_(Module m, int **enables) +{ + return handlefeatures(m, &module_features, enables); +} + +/**/ +int +boot_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +cleanup_(Module m) +{ + return setfeatureenables(m, &module_features, NULL); +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + return 0; +} diff --git a/Src/Modules/hlgroup.mdd b/Src/Modules/hlgroup.mdd new file mode 100644 index 000000000..ee3ba7260 --- /dev/null +++ b/Src/Modules/hlgroup.mdd @@ -0,0 +1,7 @@ +name=zsh/hlgroup +link=either +load=yes + +autofeatures="p:.zle.esc p:.zle.sgr" + +objects="hlgroup.o" diff --git a/Src/prompt.c b/Src/prompt.c index 0d674ceab..7acbe0e47 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -241,6 +241,34 @@ promptexpand(char *s, int ns, char *rs, char *Rs) return new_vars.buf; } +/* Get the escape sequence for a given attribute. */ +/**/ +mod_export char * +zattrescape(zattr atr, int *len) +{ + struct buf_vars new_vars; + zattr savecurrent = txtcurrentattrs; + zattr saveunknown = txtunknownattrs; + + memset(&new_vars, 0, sizeof(new_vars)); + new_vars.last = bv; + bv = &new_vars; + new_vars.bufspc = 256; + new_vars.bp = new_vars.bufline = new_vars.buf = zshcalloc(new_vars.bufspc); + new_vars.dontcount = 1; + + txtunknownattrs = 0; + treplaceattrs(atr); + applytextattributes(TSC_PROMPT); + + bv = new_vars.last; + + txtpendingattrs = txtcurrentattrs = savecurrent; + txtunknownattrs = saveunknown; + + return unmetafy(new_vars.buf, len); +} + /* Parse the argument for %H */ static char * parsehighlight(char *arg, char endchar, zattr *atr) -- 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') 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 b3cad1c24cb588f5b0cbb617fe6f7fc0fade11af Mon Sep 17 00:00:00 2001 From: Stephane Chazelas Date: Sun, 18 Feb 2024 18:22:37 +0000 Subject: 52515: (+ tests in 52527) avoid sh errors when running shebang-less scripts with paths starting with - or + --- ChangeLog | 5 +++++ Src/exec.c | 17 +++++++++++++++-- Test/A05execution.ztst | 16 ++++++++++++++-- 3 files changed, 34 insertions(+), 4 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 11cc8dd6b..df94007fe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-02-18 Stephane Chazelas + + * 52515: Src/exec.c (+ tests in 52527) avoid sh errors when + running shebang-less scripts with paths starting with - or + + 2024-02-17 Bart Schaefer * 52556: Src/builtin.c: fix crash when applying a type change via diff --git a/Src/exec.c b/Src/exec.c index 7d8135266..9c8bbb458 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -612,9 +612,22 @@ zexecve(char *pth, char **argv, char **newenvp) } } if (!isbinary) { - argv[-1] = "sh"; + char** args = argv; + if (argv[0][0] == '-' || argv[0][0] == '+') { + /* + * guard against +foo or -bar script paths being + * taken as options. POSIX says the script path + * must be passed as an *operand*. "--" would also + * make sure the next argument is treated as an + * operand with POSIX compliant sh implementations + * but "-" is more portable (to the Bourne shell in + * particular) and shorter. + */ + *--args = "-"; + } + *--args = "sh"; winch_unblock(); - execve("/bin/sh", argv - 1, newenvp); + execve("/bin/sh", args, newenvp); } } } else diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst index bcadc6d56..07a24f9c8 100644 --- a/Test/A05execution.ztst +++ b/Test/A05execution.ztst @@ -2,7 +2,7 @@ storepath=($path) - mkdir command.tmp command.tmp/dir1 command.tmp/dir2 + mkdir command.tmp command.tmp/dir{1,2} command.tmp/{+,-}dir cd command.tmp @@ -21,7 +21,10 @@ print '#!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxnyyy' >tstcmd-interp-too-long print "#!${sh}\necho should not execute; exit 1" >xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxn - chmod 755 tstcmd dir1/tstcmd dir2/tstcmd + print 'echo no shebang in -dir' > -dir/tstcmd + print 'echo no shebang in +dir' > +dir/tstcmd + + chmod 755 tstcmd dir{1,2}/tstcmd ./{-,+}dir/tstcmd chmod 755 tstcmd-slashless tstcmd-arg tstcmd-interp-too-long chmod 755 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxn @@ -422,3 +425,12 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline (exit 4); repeat 0 do done 0:'repeat 0' resets lastval + -dir/tstcmd + +dir/tstcmd + PATH=-dir tstcmd + PATH=+dir tstcmd +0:shebang-less scripts are to be run by sh even when their file paths start with - or + (workers/52515) +>no shebang in -dir +>no shebang in +dir +>no shebang in -dir +>no shebang in +dir -- cgit v1.2.3 From f1e7481b8690a6ef71a83853f05645cb774778ab Mon Sep 17 00:00:00 2001 From: Stephane Chazelas Date: Sun, 18 Feb 2024 18:52:50 +0000 Subject: 45837: fix process group restoration upon exit --- ChangeLog | 6 ++++++ Src/exec.c | 2 +- Src/init.c | 13 ++++++++----- Src/options.c | 7 ++++--- 4 files changed, 19 insertions(+), 9 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 9afb3e41c..cebc7c6ba 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2024-02-18 Stephane Chazelas + + * 45837: Src/exec.c, Src/init.c, Src/options.c: fix issue whereby + original process group is not restored properly upon exit when + exec {var} redirs are used or MONITOR is temporarily disabled. + 2024-02-18 Stephane Chazelas * 52515: Src/exec.c (+ tests in 52527) avoid sh errors when diff --git a/Src/exec.c b/Src/exec.c index 9c8bbb458..1565cb13e 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -3768,7 +3768,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, addfd(forked, save, mfds, fn->fd1, fil, 0, fn->varid); /* If this is 'exec < file', read from stdin, * * not terminal, unless `file' is a terminal. */ - if (nullexec == 1 && fn->fd1 == 0 && + if (nullexec == 1 && fn->fd1 == 0 && !fn->varid && isset(SHINSTDIN) && interact && !zleactive) init_io(NULL); break; diff --git a/Src/init.c b/Src/init.c index 799ad19f6..83b79d3d4 100644 --- a/Src/init.c +++ b/Src/init.c @@ -700,11 +700,14 @@ init_io(char *cmd) * process group leader. */ mypid = (zlong)getpid(); - if (opts[MONITOR] && (SHTTY != -1)) { - origpgrp = GETPGRP(); - acquire_pgrp(); /* might also clear opts[MONITOR] */ - } else - opts[MONITOR] = 0; + if (opts[MONITOR]) { + if (SHTTY == -1) + opts[MONITOR] = 0; + else if (!origpgrp) { + origpgrp = GETPGRP(); + acquire_pgrp(); /* might also clear opts[MONITOR] */ + } + } #else opts[MONITOR] = 0; #endif diff --git a/Src/options.c b/Src/options.c index a994b563e..a0e1aa024 100644 --- a/Src/options.c +++ b/Src/options.c @@ -881,11 +881,12 @@ dosetopt(int optno, int value, int force, char *new_opts) } else if (!force && optno == MONITOR && value) { if (new_opts[optno] == value) return 0; - if (SHTTY != -1) { + if (SHTTY == -1) + return -1; + if (!origpgrp) { origpgrp = GETPGRP(); acquire_pgrp(); - } else - return -1; + } #else } else if(optno == MONITOR && value) { return -1; -- cgit v1.2.3 From 336249e7eae1439a7d96e6aec413af1c78624859 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 18 Feb 2024 12:19:25 -0800 Subject: unposted: referent of named reference cannot start with digits This duplicates ksh behavior and doesn't change useful functionality. --- Src/params.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Src') diff --git a/Src/params.c b/Src/params.c index a722a20f6..fce3af940 100644 --- a/Src/params.c +++ b/Src/params.c @@ -6348,6 +6348,8 @@ valid_refname(char *val) { char *t = itype_end(val, INAMESPC, 0); + if (idigit(*val)) + return 0; if (*t != 0) { if (*t == '[') { tokenize(t = dupstring(t+1)); -- cgit v1.2.3 From 6b21e5c0e201876f6659b563b56da9a993ae6c03 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 20 Feb 2024 20:16:03 -0800 Subject: 52559: revise "typeset -p" with respect to local readonly special parameters Update doc and tests to describe handling of global readonly specials and to account for side-effects on zsh/param/private. --- ChangeLog | 9 +++++++++ Doc/Zsh/builtins.yo | 8 ++++++++ Doc/Zsh/mod_private.yo | 7 ++++--- Src/Modules/param_private.c | 2 +- Src/params.c | 24 ++++++++++++++++++++++-- Test/B02typeset.ztst | 14 ++++++++++++++ Test/K01nameref.ztst | 4 ++-- Test/V10private.ztst | 13 +++++++++---- 8 files changed, 69 insertions(+), 12 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index e3762340a..090644e8b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2024-02-20 Bart Schaefer + + * 52559: Doc/Zsh/builtins.yo, Doc/Zsh/mod_private.yo, + Src/Modules/param_private.c, Src/params.c, Test/B02typeset.ztst, + Test/K01nameref.ztst, Test/V10private.ztst: revise "typeset -p" + with respect to local readonly special parameters; update doc + and tests to describe handling of global readonly specials and + to account for side-effects on zsh/param/private. + 2024-02-19 Peter Stephenson * 52549: Doc/Zsh/builtins.yo: document that return already works diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 7a8654f27..784089594 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -2136,6 +2136,14 @@ tt(-p) may be followed by an optional integer argument. Currently only the value tt(1) is supported. In this case arrays and associative arrays are printed with newlines between indented elements for readability. + +The names and values of readonly special parameters +(most of the parameters marked `' in +ifzman(zmanref(zshparam))ifnzman(noderef(Parameters Set By The Shell)), +except those documented as settable) +are not printed with `tt(-)tt(p)' because to execute those typeset commands +would cause errors. However, these parameters are printed when they +have been made local to the scope where `tt(typeset -p)' is run. ) item(tt(-T) [ var(scalar)[tt(=)var(value)] var(array)[tt(=LPAR())var(value) ...tt(RPAR())] [ var(sep) ] ])( This flag has a different meaning when used with tt(-f); see below. diff --git a/Doc/Zsh/mod_private.yo b/Doc/Zsh/mod_private.yo index 08ac4cafe..24c099f38 100644 --- a/Doc/Zsh/mod_private.yo +++ b/Doc/Zsh/mod_private.yo @@ -16,9 +16,10 @@ The tt(private) builtin accepts all the same options and arguments as tt(local) (ifzman(zmanref(zshbuiltins))ifnzman(noderef(Shell Builtin Commands))) except for the `tt(-)tt(T)' option. Tied parameters may not be made private. -The `tt(-)tt(p)' option is presently a no-op because the state of -private parameters cannot reliably be reloaded. This also applies -to printing private parameters with `tt(typeset -p)'. +The `tt(-)tt(p)' option is presently disabled because the state of +private parameters cannot reliably be reloaded. When `tt(typeset -)tt(p)' +outputs a private parameter, it is treated as a local with the +`tt(-)tt(h)' (hide) option enabled. If used at the top level (outside a function scope), tt(private) creates a normal parameter in the same manner as tt(declare) or tt(typeset). A diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index 5003d4627..044617190 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -646,7 +646,7 @@ printprivatenode(HashNode hn, int printflags) static struct builtin bintab[] = { /* Copied from BUILTIN("local"), "P" added */ - BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lnmprtux", "P") + BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lnmrtux", "P") }; static struct features module_features = { diff --git a/Src/params.c b/Src/params.c index fce3af940..b329d2079 100644 --- a/Src/params.c +++ b/Src/params.c @@ -5896,6 +5896,7 @@ static const struct paramtypes pmtypes[] = { { PM_ARRAY, "array", 'a', 0}, { PM_HASHED, "association", 'A', 0}, { 0, "local", 0, PMTF_TEST_LEVEL}, + { PM_HIDE, "hide", 'h', 0 }, { PM_LEFT, "left justified", 'L', PMTF_USE_WIDTH}, { PM_RIGHT_B, "right justified", 'R', PMTF_USE_WIDTH}, { PM_RIGHT_Z, "zero filled", 'Z', PMTF_USE_WIDTH}, @@ -6025,13 +6026,21 @@ printparamnode(HashNode hn, int printflags) printflags |= PRINT_NAMEONLY; if (printflags & (PRINT_TYPESET|PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT)) { - if (p->node.flags & (PM_RO_BY_DESIGN|PM_AUTOLOAD)) { + if (p->node.flags & PM_AUTOLOAD) { /* * It's not possible to restore the state of * these, so don't output. */ return; } + if (p->node.flags & PM_RO_BY_DESIGN) { + /* + * Compromise: cannot be restored out of context, + * but show anyway if printed in scope of declaration + */ + if (p->level != locallevel || p->level == 0) + return; + } /* * The zsh variants of export -p/readonly -p also report other * flags to indicate other attributes or scope. The POSIX variants @@ -6064,8 +6073,19 @@ printparamnode(HashNode hn, int printflags) for (pmptr = pmtypes, i = 0; i < PMTYPES_SIZE; i++, pmptr++) { int doprint = 0; if (pmptr->flags & PMTF_TEST_LEVEL) { - if (p->level) + if (p->level) { + /* + if ((p->node.flags & PM_SPECIAL) && + (p->node.flags & PM_LOCAL) && + !(p->node.flags & PM_HIDE)) { + if (doneminus) + putchar(' '); + printf("+h "); + doneminus = 0; + } + */ doprint = 1; + } } else if ((pmptr->binflag != PM_EXPORTED || p->level || (p->node.flags & (PM_LOCAL|PM_ARRAY|PM_HASHED))) && (p->node.flags & pmptr->binflag)) diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index 8b3988151..d90f17d13 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -959,6 +959,20 @@ > [three]='' >) + () { + local -h status + typeset -p status + } +0:parameter hiding preserved by "typeset -p" +>typeset -h status='' + + () { + local status + typeset -p status + } +0:read-only special params are output when localized +>typeset -i10 -r status=0 + (export PATH MANPATH path=(/bin) MANPATH=/ diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index ebb70dd92..ff48e2289 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -464,7 +464,7 @@ F:unexpected side-effects of previous tests } 0:up-reference part 3, hidden global >outside ->typeset var +>typeset -h var () { typeset notdef @@ -541,7 +541,7 @@ F:Same test, should part 5 output look like this? fi 0:up-reference part 3, autoloading with hidden special >nameref-local-nameref-local ->typeset parameters +>typeset -h parameters if [[ $options[typesettounset] != on ]]; then ZTST_skip='Ignoring zmodload bug that resets TYPESET_TO_UNSET' diff --git a/Test/V10private.ztst b/Test/V10private.ztst index 9eeda0f47..4140d4e96 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -28,7 +28,7 @@ print $scalar_test 0:basic scope hiding >toplevel ->local scalar_test +>local hide scalar_test >0 >toplevel @@ -54,7 +54,7 @@ print $+unset_test 0:variable defined only in scope >0 ->local unset_test +>local hide unset_test >setme >0 @@ -70,7 +70,7 @@ } print $array_test 0:nested scope with different type, correctly restored ->local array_test +>local hide array_test >in function >top level @@ -113,7 +113,7 @@ typeset -a hash_test=(top level) typeset -p hash_test inner () { - private -p hash_test + typeset -p hash_test print ${(t)hash_test} ${(kv)hash_test} } outer () { @@ -328,6 +328,7 @@ F:future revision will create a global with this assignment F:See K01nameref.ztst up-reference part 5 F:Here ptr1 finds private ptr2 by scope mismatch >typeset -n ptr1=ptr2 +>typeset -hn ptr2 *?*read-only variable: ptr2 () { @@ -348,10 +349,12 @@ F:See K01nameref.ztst up-reference part 5 F:Here ptr1 finds private ptr2 by scope mismatch F:Assignment silently fails, is that correct? >typeset -n ptr1=ptr2 +>typeset -hn ptr2='' >ptr1=ptr2 >ptr1= >ptr2= >typeset -n ptr1=ptr2 +>typeset -hn ptr2='' *?*no such variable: ptr2 typeset ptr2 @@ -372,11 +375,13 @@ F:Assignment silently fails, is that correct? F:See K01typeset.ztst up-reference part 5 F:Here ptr1 points to global ptr2 so assignment succeeds >typeset -n ptr1=ptr2 +>typeset -hn ptr2 >ptr1=ptr2 >ptr2=val >ptr1=val >ptr2=val >typeset -n ptr1=ptr2 +>typeset -hn ptr2 >typeset ptr2=val () { -- cgit v1.2.3 From 4b9cd6b8bd5f67500e716f8485aebd31a9f7cf47 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Fri, 23 Feb 2024 09:51:06 -0800 Subject: 52583: extra check for proper scope and existence of readonly specials --- ChangeLog | 6 ++++++ Src/params.c | 25 +++++++++++++++++++++++-- Test/V10private.ztst | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 91503f01d..d520ec8c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2024-02-23 Bart Schaefer + + * 52583: Src/params.c, Test/V10private.ztst: do an extra check + for proper scope and parameter existence when assigning to a + non-local name that resolves to a readonly special. + 2024-02-22 Oliver Kiddle * 52552: Completion/Unix/Command/_java: newer Java supports diff --git a/Src/params.c b/Src/params.c index b329d2079..225acb8a1 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1004,8 +1004,29 @@ createparam(char *name, int flags) gethashnode2(paramtab, name) : paramtab->getnode(paramtab, name)); - if (oldpm && (oldpm->node.flags & PM_NAMEREF) && - !(flags & PM_NAMEREF) && (oldpm = upscope(oldpm, oldpm->base))) { + if (oldpm && (oldpm->node.flags & PM_RO_BY_DESIGN)) { + if (!(flags & PM_LOCAL)) { + /* Must call the API for namerefs and specials to work */ + pm = (Param) paramtab->getnode2(paramtab, oldpm->node.nam); + if (!pm || ((pm->node.flags & PM_NAMEREF) && + pm->level != locallevel)) { + zerr("%s: can't modify read-only parameter", name); + return NULL; + } + } + /** + * Implementation note: In the case of a readonly nameref, + * the right thing might be to insert a new global into + * the paramtab and point the local pm->old at it, rather + * than error. That is why gethashnode2() is called + * first, to avoid skipping up the stack prematurely. + **/ + } + + if (oldpm && !(flags & PM_NAMEREF) && + (!(oldpm->node.flags & PM_RO_BY_DESIGN) || !(flags & PM_LOCAL)) && + (oldpm->node.flags & PM_NAMEREF) && + (oldpm = upscope(oldpm, oldpm->base))) { Param lastpm; struct asgment stop; stop.flags = PM_NAMEREF | (flags & PM_LOCAL); diff --git a/Test/V10private.ztst b/Test/V10private.ztst index 4140d4e96..efa346002 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -311,6 +311,47 @@ F:future revision will create a global with this assignment >TOP >UP: + () { + typeset -a ary + local -P -n ref=ary + { + (){ + ref=XX # Should be an error + typeset -p ary ref + } + } always { + TRY_BLOCK_ERROR=0 + typeset -p ary ref + } + } + typeset -p ary +1:assignment to private nameref in wrong scope, part 1 +>typeset -a ary +>typeset -hn ref=ary +*?*ref: can't modify read-only parameter +*?*no such variable: ary + + () { + typeset -a ary + local -P -n ref=ary + { + (){ + typeset ref=XX # Should create a local + typeset -p ary ref + } + } always { + TRY_BLOCK_ERROR=0 + typeset -p ary ref + } + } + typeset -p ary +1:assignment to private nameref in wrong scope, part 2 +>typeset -g -a ary +>typeset ref=XX +>typeset -a ary +>typeset -hn ref=ary +*?*no such variable: ary + () { typeset -n ptr1=ptr2 private -n ptr2 # TYPESET_TO_UNSET makes this not a "placeholder" -- cgit v1.2.3 From 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') 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 69c0c646bbabb1caaeb62242e9323a62bf3cd57a Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 24 Feb 2024 20:45:29 -0800 Subject: 52596: metafy interpreter name for error message --- ChangeLog | 2 ++ Src/exec.c | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 00a3c65ab..9f7289cea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2024-02-24 Bart Schaefer + * 52596: Src/exec.c: metafy interpreter name for error message + * Stephane: 52591: Src/builtin.c: printf builtin must pass metafied strings to math evaluation diff --git a/Src/exec.c b/Src/exec.c index 1565cb13e..c75aa78d6 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -551,7 +551,7 @@ zexecve(char *pth, char **argv, char **newenvp) break; if (t0 == ct) zerr("%s: bad interpreter: %s: %e", pth, - execvebuf + 2, eno); + metafy(execvebuf + 2, -1, META_NOALLOC), eno); else { while (inblank(execvebuf[t0])) execvebuf[t0--] = '\0'; @@ -574,8 +574,8 @@ zexecve(char *pth, char **argv, char **newenvp) execve(pprog, argv - 2, newenvp); } } - zerr("%s: bad interpreter: %s: %e", pth, ptr2, - eno); + zerr("%s: bad interpreter: %s: %e", pth, + metafy(ptr2, -1, META_NOALLOC), eno); } else if (*ptr) { *ptr = '\0'; argv[-2] = ptr2; -- cgit v1.2.3 From 6c50d155625d83bde35dc78bc6920c9795462d58 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 24 Feb 2024 21:16:15 -0800 Subject: 52597: fix character counts in context of operator and operand errors --- ChangeLog | 3 +++ Src/math.c | 25 ++++++++++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 9f7289cea..684820f92 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-02-24 Bart Schaefer + * 52597: Src/math.c: fix multibyte and metafied character counts + when providing context for operator and operand errors + * 52596: Src/exec.c: metafy interpreter name for error message * Stephane: 52591: Src/builtin.c: printf builtin must pass diff --git a/Src/math.c b/Src/math.c index 50b69d6a1..d97dae238 100644 --- a/Src/math.c +++ b/Src/math.c @@ -1557,21 +1557,32 @@ checkunary(int mtokc, char *mptr) errmsg = 2; } if (errmsg) { - int len, over = 0; + int len = 0, over = 0; char *errtype = errmsg == 2 ? "operator" : "operand"; while (inblank(*mptr)) mptr++; - len = ztrlen(mptr); - if (len > 10) { - len = 10; - over = 1; + if (isset(MULTIBYTE)) + MB_CHARINIT(); + while (over < 10 && mptr[len]) { + if (isset(MULTIBYTE)) + len += MB_METACHARLEN(mptr+len); + else + len += (mptr[len] == Meta ? 2 : 1); + ++over; + } + if ((over = mptr[len])) { + mptr = dupstring(mptr); + if (mptr[len] == Meta) + mptr[len+1] = 0; + else + mptr[len] = 0; } if (!*mptr) zerr("bad math expression: %s expected at end of string", errtype); else - zerr("bad math expression: %s expected at `%l%s'", - errtype, mptr, len, over ? "..." : ""); + zerr("bad math expression: %s expected at `%s%s'", + errtype, mptr, over ? "..." : ""); } unary = !(tp & OP_OPF); } -- cgit v1.2.3 From b68002d927b1577bbed453d7bbbe39b55acf7bd0 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 24 Feb 2024 21:28:35 -0800 Subject: Fix META_NOALLOC to META_STATIC in 'bad interpreter' metafy --- Src/exec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/Src/exec.c b/Src/exec.c index c75aa78d6..0750738ce 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -551,7 +551,7 @@ zexecve(char *pth, char **argv, char **newenvp) break; if (t0 == ct) zerr("%s: bad interpreter: %s: %e", pth, - metafy(execvebuf + 2, -1, META_NOALLOC), eno); + metafy(execvebuf + 2, -1, META_STATIC), eno); else { while (inblank(execvebuf[t0])) execvebuf[t0--] = '\0'; @@ -575,7 +575,7 @@ zexecve(char *pth, char **argv, char **newenvp) } } zerr("%s: bad interpreter: %s: %e", pth, - metafy(ptr2, -1, META_NOALLOC), eno); + metafy(ptr2, -1, META_STATIC), eno); } else if (*ptr) { *ptr = '\0'; argv[-2] = ptr2; -- 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') 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 3078e07729930bfbff647b9b63452b58a54ecc70 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 28 Feb 2024 13:55:51 +0100 Subject: 52623: add some Solaris signal descriptions --- ChangeLog | 4 ++++ Src/signames2.awk | 3 +++ 2 files changed, 7 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 53038c221..7aaadfeb3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-02-28 Oliver Kiddle + + * 52623: Src/signames2.awk: add some Solaris signal descriptions + 2024-02-28 Oliver Kiddle * 52594: Completion/Zsh/Command/_kill, Doc/Zsh/builtins.yo, diff --git a/Src/signames2.awk b/Src/signames2.awk index 5738030c6..0b254f751 100644 --- a/Src/signames2.awk +++ b/Src/signames2.awk @@ -27,6 +27,7 @@ if (signam == "CONT") { msg[signum] = "continued" } if (signam == "EMT") { msg[signum] = "EMT instruction" } if (signam == "FPE") { msg[signum] = "floating point exception" } + if (signam == "FREEZE") { msg[signum] = "checkpoint freeze" } if (signam == "HUP") { msg[signum] = "hangup" } if (signam == "ILL") { msg[signum] = "illegal hardware instruction" } if (signam == "INFO") { msg[signum] = "status request from keyboard" } @@ -43,6 +44,7 @@ if (signam == "SEGV") { msg[signum] = "segmentation fault" } if (signam == "SYS") { msg[signum] = "invalid system call" } if (signam == "TERM") { msg[signum] = "terminated" } + if (signam == "THAW") { msg[signum] = "checkpoint thaw" } if (signam == "TRAP") { msg[signum] = "trace trap" } if (signam == "URG") { msg[signum] = "urgent condition" } if (signam == "USR1") { msg[signum] = "user-defined signal 1" } @@ -51,6 +53,7 @@ if (signam == "WINCH") { msg[signum] = "window size changed" } if (signam == "XCPU") { msg[signum] = "cpu limit exceeded" } if (signam == "XFSZ") { msg[signum] = "file size limit exceeded" } + if (signam == "XRES") { msg[signum] = "resource control exceeded" } } } -- cgit v1.2.3 From 69c58874611a586c68be14ce7965029dc00f41b7 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 28 Feb 2024 13:59:10 +0100 Subject: 52622 (tweaked, c.f. 52626): adjust number of columns and drop right-parenthesis in "kill -L" output --- ChangeLog | 3 +++ Src/jobs.c | 15 +++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 7aaadfeb3..e836a3853 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-02-28 Oliver Kiddle + * 52622 (tweaked, c.f. 52626): Src/jobs.c: adjust number of columns + and drop right-parenthesis in "kill -L" output + * 52623: Src/signames2.awk: add some Solaris signal descriptions 2024-02-28 Oliver Kiddle diff --git a/Src/jobs.c b/Src/jobs.c index 49decc661..07facc60b 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -2822,23 +2822,26 @@ bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) #else const int width = SIGCOUNT >= 100 ? 3 : 2; #endif + const int cols = zterm_columns >= 30 ? + (zterm_columns < 90 ? zterm_columns / 15 : 6) : 1; + for (sig = 1; sig < SIGCOUNT #if defined(SIGRTMIN) && defined(SIGRTMAX) + 1 #endif ; sig++) { - printf("%*d) %-10s%c", width, sig, sigs[sig], - sig % 5 ? ' ' : '\n'); + printf("%*d %-10s%c", width, sig, sigs[sig], + sig % cols ? ' ' : '\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 %-10s%c", width, sig, rtsigname(sig, 0), + (sig - SIGRTMIN + SIGCOUNT + 1) % cols ? ' ' : '\n'); } - printf("%*d) RTMAX\n", width, sig); + printf("%*d RTMAX\n", width, sig); #else - printf("%*d) %s\n", width, sig, sigs[sig]); + printf("%*d %s\n", width, sig, sigs[sig]); #endif return 0; } -- cgit v1.2.3 From 85172998f499cb789570714ffdd43f1ca3b53e58 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 28 Feb 2024 20:40:26 -0800 Subject: 52619 (plus tests): no empty element when appending array to unset scalar --- ChangeLog | 7 ++++++- Src/params.c | 2 +- Test/A06assign.ztst | 21 +++++++++++++++++---- 3 files changed, 24 insertions(+), 6 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index e836a3853..9ab0218c9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-02-28 Bart Schaefer + + * 52619 (plus tests): Src/params.c, Test/A06assign.ztst: there + is no empty element when appending array to unset scalar + 2024-02-28 Oliver Kiddle * 52622 (tweaked, c.f. 52626): Src/jobs.c: adjust number of columns @@ -54,7 +59,7 @@ x2024-02-19 Jun-ichi Takimoto * 52544: Completion/Unix/Type/_diff_options: support macOS Ventura or newer -2024-02-18 Bart Schaefer +2024-02-18 Bart Schaefer * 52558: Etc/FAQ.yo: make note of word splitting differences with nofork substitutions; update ToC; minor formatting fixes diff --git a/Src/params.c b/Src/params.c index 7c5e9d8ff..064dbd2bc 100644 --- a/Src/params.c +++ b/Src/params.c @@ -3355,7 +3355,7 @@ assignaparam(char *s, char **val, int flags) } else if (!(PM_TYPE(v->pm->node.flags) & (PM_ARRAY|PM_HASHED)) && !(v->pm->node.flags & (PM_SPECIAL|PM_TIED))) { int uniq = v->pm->node.flags & PM_UNIQUE; - if (flags & ASSPM_AUGMENT) { + if ((flags & ASSPM_AUGMENT) && !(v->pm->node.flags & PM_UNSET)) { /* insert old value at the beginning of the val array */ char **new; int lv = arrlen(val); diff --git a/Test/A06assign.ztst b/Test/A06assign.ztst index f89edb888..3eff5331a 100644 --- a/Test/A06assign.ztst +++ b/Test/A06assign.ztst @@ -296,13 +296,26 @@ # tests of var+=(array) + a= + a+=(1 2 3) + print "${(q@)a}" +0:add array to empty parameter +>'' 1 2 3 + unset a a+=(1 2 3) - print -l $a + print "${(q@)a}" 0:add array to unset parameter ->1 ->2 ->3 +>1 2 3 + + () { + setopt localoptions typeset_to_unset + typeset a + a+=(1 2 3) + print "${(q@)a}" + } +0:add array to declared unset parameter +>1 2 3 a=(a) a+=(b) -- cgit v1.2.3 From 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') 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 4fb96cc639f8f04a84dd488e61caacdecf11d65e Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 2 Mar 2024 09:22:43 -0800 Subject: 52612: %l replacment of zwarning() does literal string output --- ChangeLog | 6 ++++++ Src/parse.c | 9 ++++----- Src/subst.c | 4 +++- Src/utils.c | 22 ++++++++++++---------- 4 files changed, 25 insertions(+), 16 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 31e90dfd3..df90e441a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2024-03-02 Bart Schaefer + + * 52612: Src/parse.c, Src/subst.c, Src/utils.c: change the %l + replacment of zwarning() et al. to do literal string output; + change previous uses to %s and use new %l for ${var?$error} + 2024-03-01 Bart Schaefer * 52645: Src/builtin.c: unset through a nameref keep up-scope diff --git a/Src/parse.c b/Src/parse.c index 821812c78..40eb0ee0b 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -2730,11 +2730,10 @@ yyerror(int noerr) if (!t || !t[t0] || t[t0] == '\n') break; if (!(histdone & HISTFLAG_NOEXEC) && !(errflag & ERRFLAG_INT)) { - if (t0 == 20) - zwarn("parse error near `%l...'", t, 20); - else if (t0) - zwarn("parse error near `%l'", t, t0); - else + if (t0) { + t = metafy(t, t0, META_STATIC); + zwarn("parse error near `%s%s'", t, t0 == 20 ? "..." : ""); + } else zwarn("parse error"); } if (!noerr && noerrs != 2) diff --git a/Src/subst.c b/Src/subst.c index 650c09de2..49f7336bb 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -3271,8 +3271,10 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, if (isset(EXECOPT)) { *idend = '\0'; if (*s){ + int l; singsub(&s); - zerr("%s: %s", idbeg, s); + s = unmetafy(s, &l); + zerr("%s: %l", idbeg, s, l); } else zerr("%s: %s", idbeg, "parameter not set"); /* diff --git a/Src/utils.c b/Src/utils.c index 0fda92709..c8831c85e 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -122,14 +122,20 @@ set_widearray(char *mb_array, Widechar_array wca) (implemented by zerrmsg()): Code Argument types Prints - %s const char * C string (null terminated) - %l const char *, int C string of given length (null not required) + %s const char * C string (metafied, null terminated) + (output "nice") + %l const char *, int C string of given length (not metafied) + (output raw) %L long decimal value %d int decimal value %z zlong decimal value %% (none) literal '%' %c int character at that codepoint - %e int strerror() message (argument is typically 'errno') + %e int strerror() message (argument usually 'errno') + (output raw) + + For %s and %l, the caller is responsible for assuring end-of-string + is not in the middle of a metafy pair (%s) or a multibyte character. */ static void @@ -310,14 +316,9 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) nicezputs(str, file); break; case 'l': { - char *s; str = va_arg(ap, const char *); num = va_arg(ap, int); - num = metalen(str, num); - s = zhalloc(num + 1); - memcpy(s, str, num); - s[num] = '\0'; - nicezputs(s, file); + fwrite(str, num, 1, file); break; } case 'L': @@ -715,7 +716,8 @@ wcs_nicechar(wchar_t c, size_t *widthp, char **swidep) */ /**/ -mod_export int is_wcs_nicechar(wchar_t c) +mod_export int +is_wcs_nicechar(wchar_t c) { if (!WC_ISPRINT(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) { if (c == 0x7f || c == L'\n' || c == L'\t' || c < 0x20) -- cgit v1.2.3 From d1ff06f99185bb14554c6a48e0466aee6466ecac Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 2 Mar 2024 21:37:25 -0800 Subject: 52652: fix obscure bug unsetting the array part of a tied parameter pair --- ChangeLog | 3 +++ Src/params.c | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 7273034ac..9718d0cae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-03-02 Bart Schaefer + * 52652: Src/params.c, Test/D04parameter.ztst: fix obscure bug + unsetting the array part of a tied parameter pair, update test + * JunT.: 52635: Test/runtests.zsh: show file name when crashed * 52612: Src/parse.c, Src/subst.c, Src/utils.c: change the %l diff --git a/Src/params.c b/Src/params.c index 064dbd2bc..e83e4aa5e 100644 --- a/Src/params.c +++ b/Src/params.c @@ -3813,12 +3813,15 @@ unsetparam_pm(Param pm, int altflag, int exp) /* fudge things so removenode isn't called */ altpm->level = 1; } - unsetparam_pm(altpm, 1, exp); + unsetparam_pm(altpm, 1, exp); /* This resets pm to empty */ + pm->node.flags |= PM_UNSET; /* so we must repeat this */ } zsfree(altremove); - if (!(pm->node.flags & PM_SPECIAL)) + if (!(pm->node.flags & PM_SPECIAL)) { pm->gsu.s = &stdscalar_gsu; + pm->node.flags &= ~PM_ARRAY; + } } /* -- cgit v1.2.3 From 05c7b21e2b30873d002b50b37e2fbd3803d4b608 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Tue, 5 Mar 2024 00:11:02 +0100 Subject: 52646: extend support for highlight groups to completion explanation strings and WATCHFMT --- ChangeLog | 7 +++++-- Completion/Zsh/Type/_ps1234 | 21 +++++++++++++-------- Doc/Zsh/compsys.yo | 4 ++-- Doc/Zsh/compwid.yo | 3 ++- Src/Modules/watch.c | 9 ++++++++- Src/Zle/complist.c | 7 +++++++ Src/Zle/zle_tricky.c | 8 ++++++++ Src/prompt.c | 8 +++++--- 8 files changed, 50 insertions(+), 17 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 5c839cd19..0dd7268d1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2024-03-05 Oliver Kiddle + * 52646: Completion/Zsh/Type/_ps1234, Doc/Zsh/compsys.yo, + Doc/Zsh/compwid.yo, Src/Modules/watch.c, Src/Zle/complist.c, + Src/Zle/zle_tricky.c, Src/prompt.c: extend support for highlight + groups to completion explanation strings and WATCHFMT + * 52641: midchildan: Doc/Zsh/contrib.yo, Functions/Zle/incarg, Test/X05zleincarg.ztst: add a backward variant and make it repeatable @@ -31,8 +36,6 @@ * 52623: Src/signames2.awk: add some Solaris signal descriptions -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, diff --git a/Completion/Zsh/Type/_ps1234 b/Completion/Zsh/Type/_ps1234 index 07dea5905..e4391dc00 100644 --- a/Completion/Zsh/Type/_ps1234 +++ b/Completion/Zsh/Type/_ps1234 @@ -1,17 +1,18 @@ #compdef -value-,PROMPT,-default- -value-,PROMPT2,-default- -value-,PROMPT3,-default- -value-,PROMPT4,-default- -value-,RPROMPT,-default- -value-,RPROMPT2,-default- -value-,PS1,-default- -value-,PS2,-default- -value-,PS3,-default- -value-,PS4,-default- -value-,RPS1,-default- -value-,RPS2,-default- -value-,SPROMPT,-default- -value-,PROMPT_EOL_MARK,-default- -local -a specs ccol -local expl grp cols bs suf pre changed=1 ret=1 +local -a specs ccol suf +local expl grp cols bs pre changed=1 ret=1 local -A ansi [[ -z $compstate[quote] ]] && bs='\' +suf=( -S '' ) # first strip off any complete prompt specifications leaving only the # current, incomplete, one while (( changed )); do changed=0 - compset -P '%[DFK](\\|){[^}]#}' && changed=1 # formats with arg: %x{...} - compset -P '%[0-9-\\]#[^DFK(0-9-<>\\\[]' && changed=1 # normal formats + compset -P '%[DFHK](\\|){[^}]#}' && changed=1 # formats with arg: %x{...} + compset -P '%[0-9-\\]#[^DFHK(0-9-<>\\\[]' && changed=1 # normal formats compset -P '%[0-9-\\]#(<[^<]#<|>[^>]#>|\[[^\]]#\])' && changed=1 # truncations compset -P '%[0-9-\\]#(\\|)\([0-9-]#[^0-9]?|[^%]' && changed=1 # start of ternary compset -P '[^%]##' && changed=1 # sundry other characters @@ -41,15 +42,15 @@ if compset -P '%[FK]'; then grp="$expl[expl[(i)-J]+1]" print -v ccol -f "($grp)=%s=%s" ${(kv)ansi} _comp_colors+=( $ccol ) - compadd "$expl[@]" $suf $pre -k ansi && ret=0 - if (( $#suf )) && compset -P "(<->|%v)"; then + compadd "$expl[@]" "$suf[@]" $pre -k ansi && ret=0 + if [[ $ISUFFIX != (\\|)}* ]] && compset -P "(<->|%v)"; then _wanted ansi-colors expl 'closing brace' compadd -S '' \} && ret=0 elif (( $+terminfo[colors] )); then (( cols = $terminfo[colors] - 1 )) (( cols = cols > 255 ? 255 : cols )) _description -V terminal-colors expl 'terminal color' grp="$expl[expl[(i)-J]+1]" - compadd "$expl[@]" $suf $pre {0..$cols} + compadd "$expl[@]" "$suf[@]" $pre {0..$cols} for c in {0..$cols}; do _comp_colors+=( "($grp)=${c}=${${${(%):-%F{$c\}}#?\[}%m}" ) done @@ -93,11 +94,14 @@ elif compset -P '%[0-9-\\]#(\\|)\([0-9-]#'; then 'w:day of week (Sunday = 0)' ) [[ $IPREFIX != *- ]] && _describe -t ternary-prompt-expressions \ - 'ternary prompt format test character' specs $suf && ret=0 + 'ternary prompt format test character' specs "$suf[@]" && ret=0 _message -e numbers number elif compset -P '%D(\\|){'; then compset -S '(\\|)}*' _date_formats zsh && ret=0 +elif compset -P '%H(\\|){'; then + compset -S '(\\|)}*' || suf=( -S "$bs}" ) + _wanted highlight-groups expl 'highlight group' compadd "$suf[@]" -k .zle.hlgroups && ret=0 elif [[ -prefix '%' ]] || ! zstyle -t ":completion:${curcontext}:prompt-format-specifiers" prefix-needed then @@ -152,6 +156,7 @@ then 'B:start bold' 'b:stop bold' 'E:clear to end of line' + 'H{:use highlight group' 'U:start underline' 'u:stop underline' 'S:start standout' diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo index 3f708eb5a..f75298a1b 100644 --- a/Doc/Zsh/compsys.yo +++ b/Doc/Zsh/compsys.yo @@ -2023,8 +2023,8 @@ position shown as a percentage of the total length otherwise. In each case the form with the uppercase letter will be replaced by a string of fixed width, padded to the right with spaces, while the lowercase form will be replaced by a variable width string. As in other prompt strings, the -escape sequences `tt(%S)', `tt(%s)', `tt(%B)', `tt(%b)', `tt(%U)', -`tt(%u)' for entering and leaving the display modes +escape sequence `tt(%H)` along with `tt(%S)', `tt(%s)', `tt(%B)', `tt(%b)', +`tt(%U)', `tt(%u)' for entering and leaving the display modes standout, bold and underline, and `tt(%F)', `tt(%f)', `tt(%K)', `tt(%k)' for changing the foreground background colour, are also available, as is the form `tt(%{)...tt(%})' for enclosing escape sequences which display with zero diff --git a/Doc/Zsh/compwid.yo b/Doc/Zsh/compwid.yo index 9461ace17..b0c9b0a5f 100644 --- a/Doc/Zsh/compwid.yo +++ b/Doc/Zsh/compwid.yo @@ -597,7 +597,8 @@ ifnzman((see noderef(Prompt Expansion)))\ ifzman(as described in the section EXPANSION OF PROMPT SEQUENCES in zmanref(zshmisc)): `tt(%B)', `tt(%S)', `tt(%U)', `tt(%F)', `tt(%K)' and their lower case -counterparts, as well as `tt(%{)...tt(%})'. `tt(%F)', `tt(%K)' and +counterparts, as well as `tt(%H)' and `tt(%{)...tt(%})'. `tt(%F)', +`tt(%K)', `tt(%H)' and `tt(%{)...tt(%})' take arguments in the same form as prompt expansion. (Note that the sequence `tt(%G)' is not available; an argument to `tt(%{)' should be used instead.) The sequence `tt(%%)' diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c index 97d4fa608..ba17cf940 100644 --- a/Src/Modules/watch.c +++ b/Src/Modules/watch.c @@ -373,6 +373,13 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) case 'f': tunsetattrs(TXTFGCOLOUR); break; + case 'H': + if (*fmt == '{') { + fmt = parsehighlight(fmt + 1, '}', &atr); + if (atr && atr != TXT_ERROR) + treplaceattrs(atr); + } + break; case 'K': if (*fmt == '{') { fmt++; @@ -428,7 +435,7 @@ watchlog_match(char *teststr, char *actual, size_t buflen) int ret = 0; Patprog pprog; char *str = dupstring(teststr); - int len = strnlen(actual, buflen); + size_t len = strnlen(actual, buflen); char *user = metafy(actual, len, len == buflen ? META_HEAPDUP : META_USEHEAP); diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 9cb89a60d..5619160a9 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -1181,6 +1181,13 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) if (dopr) tunsetattrs(TXTBGCOLOUR); break; + case ZWC('H'): + if (*p == '{') { + p = parsehighlight(p + 1, '}', &atr); + if (atr != TXT_ERROR && dopr) + treplaceattrs(atr); + } + break; case ZWC('{'): if (arg) cc += arg; diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index 225ce8c74..aa3c71bc2 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -2501,6 +2501,14 @@ printfmt(char *fmt, int n, int dopr, int doesc) case 'k': tunsetattrs(TXTBGCOLOUR); break; + case 'H': + if (p[1] == '{') { + p = parsehighlight(p + 2, '}', &atr); + --p; + if (atr != TXT_ERROR) + treplaceattrs(atr); + } + break; case '{': if (arg) cc += arg; diff --git a/Src/prompt.c b/Src/prompt.c index 7acbe0e47..e10b05215 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -270,7 +270,8 @@ zattrescape(zattr atr, int *len) } /* Parse the argument for %H */ -static char * +/**/ +mod_export char * parsehighlight(char *arg, char endchar, zattr *atr) { static int entered = 0; @@ -295,9 +296,9 @@ parsehighlight(char *arg, char endchar, zattr *atr) } else *atr = TXT_ERROR; if (ep) - *ep = endchar; + *ep++ = endchar; else - ep = strchr(arg, '\0') - 1; + ep = strchr(arg, '\0'); entered = 0; return ep; } @@ -635,6 +636,7 @@ putpromptchar(int doprint, int endchar) case 'H': if (bv->fm[1] == '{') { bv->fm = parsehighlight(bv->fm + 2, '}', &atr); + --bv->fm; if (atr != TXT_ERROR) { treplaceattrs(atr); applytextattributes(TSC_PROMPT); -- 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') 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') 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 d27ea2ae02275b255f9efbf929d1dc7932aebc57 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 4 Mar 2024 21:36:45 -0800 Subject: unposted (cf. 52617): only scalars can instantiate a declared named reference --- ChangeLog | 3 +++ Src/params.c | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 16ede3665..6dc3e32ee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-03-04 Bart Schaefer + * unposted (cf. 52617): Src/params.c: only scalars can instantiate + a declared named reference + * 52659: Src/builtin.c, Test/K01nameref.ztst: Fix crash when unset was called on a named referece, add regression test diff --git a/Src/params.c b/Src/params.c index 263cd0c52..4bcf41c22 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1050,8 +1050,14 @@ createparam(char *name, int flags) name = refname; oldpm = NULL; } else { - if (!(lastpm->node.flags & PM_READONLY)) - lastpm->node.flags |= PM_UNSET; + if (!(lastpm->node.flags & PM_READONLY)) { + if (flags) { + /* Only plain scalar assignment allowed */ + zerr("%s: can't change type of named reference", + name); /* Differs from ksh93u+ */ + return NULL; + } + } return lastpm; } } else { -- cgit v1.2.3 From 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') 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') 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 47c7bc9b1493c7374f076b5471cfd57ee30f4ba5 Mon Sep 17 00:00:00 2001 From: Stephane Chazelas Date: Sat, 9 Mar 2024 11:45:46 +0000 Subject: 52721: fix metafication and regexp/subject confusion in pcre_match error message --- ChangeLog | 5 +++++ Src/Modules/pcre.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index ef6c9f02d..620c74fcf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-03-09 Stephane Chazelas + + * 52721: fix metafication and regexp/subject confusion in + pcre_match error message. + 2024-03-08 Stephane Chazelas * 52704: Doc/Zsh/params.yo, mention new ${ ... } and ${|...} diff --git a/Src/Modules/pcre.c b/Src/Modules/pcre.c index a49d1a307..67157cc01 100644 --- a/Src/Modules/pcre.c +++ b/Src/Modules/pcre.c @@ -405,7 +405,7 @@ bin_pcre_match(char *nam, char **args, Options ops, UNUSED(int func)) else { PCRE2_UCHAR buffer[256]; pcre2_get_error_message(ret, buffer, sizeof(buffer)); - zwarnnam(nam, "error in pcre matching for /%s/: %s", plaintext, buffer); + zwarnnam(nam, "error in pcre matching for %s: %s", *args, buffer); } if (pcre_mdata) -- cgit v1.2.3 From ea5a5d6ec430cad1a73f3179fcc018c90a315c35 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 9 Mar 2024 22:02:57 -0800 Subject: 52725: updated named reference semantics --- ChangeLog | 4 ++++ Src/Modules/ksh93.c | 44 ++++++++++++++++++++++++++++++++------------ 2 files changed, 36 insertions(+), 12 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 620c74fcf..a2609a1cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-03-09 Bart Schaefer + + * 52725: Src/Modules/ksh93.c: updated named reference semantics + 2024-03-09 Stephane Chazelas * 52721: fix metafication and regexp/subject confusion in diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c index 6760cbca0..8d10317dc 100644 --- a/Src/Modules/ksh93.c +++ b/Src/Modules/ksh93.c @@ -113,18 +113,17 @@ static char sh_edmode[2]; * obviously includes those commented out here. */ static struct paramdef partab[] = { - PARAMDEF(".sh.command", PM_NAMEREF|PM_READONLY, "ZSH_DEBUG_CMD", &constant_gsu), - PARAMDEF(".sh.edchar", PM_SCALAR|PM_SPECIAL, &sh_edchar, &sh_edchar_gsu), - PARAMDEF(".sh.edcol", PM_NAMEREF|PM_READONLY, "CURSOR", &constant_gsu), - PARAMDEF(".sh.edmode", PM_SCALAR|PM_READONLY|PM_SPECIAL, &sh_edmode, &sh_edmode_gsu), - PARAMDEF(".sh.edtext", PM_NAMEREF|PM_READONLY, "BUFFER", &constant_gsu), + PARAMDEF(".sh.edchar", PM_SCALAR|PM_SPECIAL, + &sh_edchar, &sh_edchar_gsu), + PARAMDEF(".sh.edmode", PM_SCALAR|PM_READONLY|PM_SPECIAL, + &sh_edmode, &sh_edmode_gsu), PARAMDEF(".sh.file", PM_NAMEREF|PM_READONLY, "ZSH_SCRIPT", &constant_gsu), - /* PARAMDEF(".sh.fun", PM_SCALAR|PM_UNSET, NULL, &constant_gsu), */ - /* PARAMDEF(".sh.level", PM_INTEGER|PM_UNSET, NULL, &constant_gsu), */ PARAMDEF(".sh.lineno", PM_NAMEREF|PM_READONLY, "LINENO", &constant_gsu), PARAMDEF(".sh.match", PM_ARRAY|PM_READONLY, NULL, &sh_match_gsu), - PARAMDEF(".sh.name", PM_SCALAR|PM_READONLY|PM_SPECIAL, &sh_name, &sh_name_gsu), - PARAMDEF(".sh.subscript", PM_SCALAR|PM_READONLY|PM_SPECIAL, &sh_subscript, &sh_subscript_gsu), + PARAMDEF(".sh.name", PM_SCALAR|PM_READONLY|PM_SPECIAL, + &sh_name, &sh_name_gsu), + PARAMDEF(".sh.subscript", PM_SCALAR|PM_READONLY|PM_SPECIAL, + &sh_subscript, &sh_subscript_gsu), PARAMDEF(".sh.subshell", PM_NAMEREF|PM_READONLY, "ZSH_SUBSHELL", &constant_gsu), /* SPECIALPMDEF(".sh.value", 0, NULL, NULL, NULL), */ PARAMDEF(".sh.version", PM_NAMEREF|PM_READONLY, "ZSH_PATCHLEVEL", &constant_gsu) @@ -156,15 +155,35 @@ ksh93_wrapper(Eprog prog, FuncWrap w, char *name) queue_signals(); ++locallevel; /* Make these local */ - if ((pm = createparam(".sh.level", PM_LOCAL|PM_UNSET))) { +#define LOCAL_NAMEREF (PM_LOCAL|PM_UNSET|PM_NAMEREF) + if ((pm = createparam(".sh.command", LOCAL_NAMEREF))) { pm->level = locallevel; /* Why is this necessary? */ - setiparam(".sh.level", num); + /* Force scoping by assignent hack */ + setloopvar(".sh.command", "ZSH_DEBUG_CMD"); + pm->node.flags |= PM_READONLY; } + /* .sh.edchar is in partab and below */ + if (zleactive && (pm = createparam(".sh.edcol", LOCAL_NAMEREF))) { + pm->level = locallevel; + setloopvar(".sh.edcol", "CURSOR"); + pm->node.flags |= (PM_NAMEREF|PM_READONLY); + } + /* .sh.edmode is in partab and below */ + if (zleactive && (pm = createparam(".sh.edtext", LOCAL_NAMEREF))) { + pm->level = locallevel; + setloopvar(".sh.edtext", "BUFFER"); + pm->node.flags |= PM_READONLY; + } + if ((pm = createparam(".sh.fun", PM_LOCAL|PM_UNSET))) { pm->level = locallevel; setsparam(".sh.fun", ztrdup(name)); pm->node.flags |= PM_READONLY; } + if ((pm = createparam(".sh.level", PM_LOCAL|PM_UNSET))) { + pm->level = locallevel; + setiparam(".sh.level", num); + } if (zleactive) { extern mod_import_variable char *curkeymapname; /* XXX */ extern mod_import_variable char *varedarg; /* XXX */ @@ -186,9 +205,10 @@ ksh93_wrapper(Eprog prog, FuncWrap w, char *name) *--ie = '\0'; } else sh_subscript = NULL; - if ((pm = createparam(".sh.value", PM_LOCAL|PM_NAMEREF|PM_UNSET))) { + if ((pm = createparam(".sh.value", LOCAL_NAMEREF))) { pm->level = locallevel; setloopvar(".sh.value", "BUFFER"); /* Hack */ + pm->node.flags |= PM_READONLY; } } else sh_name = sh_subscript = NULL; -- cgit v1.2.3 From 37f434498eb626a10e3009216afa306e5ae841c6 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 13 Mar 2024 00:35:10 +0100 Subject: 52724: fix .zle.sgr for empty sequences --- ChangeLog | 4 ++++ Src/Modules/hlgroup.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index a2609a1cf..f136bc6b4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-03-13 Oliver Kiddle + + * 52724: Src/Modules/hlgroup.c: fix .zle.sgr for empty sequences + 2024-03-09 Bart Schaefer * 52725: Src/Modules/ksh93.c: updated named reference semantics diff --git a/Src/Modules/hlgroup.c b/Src/Modules/hlgroup.c index 6382f3216..9c0aedcf8 100644 --- a/Src/Modules/hlgroup.c +++ b/Src/Modules/hlgroup.c @@ -59,6 +59,10 @@ convertattr(char *attrstr, int sgr) *t = ';'; c++; } + if (t <= s) { /* always return at least "0" */ + *s = '0'; + t = s + 1; + } *t = '\0'; len = t - s; } -- 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') 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 8adfbfc1f05200d566b9830232d0dabf82c84283 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 12 Mar 2024 18:02:38 -0700 Subject: unposted: "typeset -p" has problems with special parameters having NULL values --- ChangeLog | 3 +++ Src/Modules/ksh93.c | 15 ++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index c8935fb61..389054f14 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-03-12 Bart Schaefer + * unposted: Src/Modules/ksh93.c: "typeset -p" has problems with + special parameters having NULL values, use a dummy static instead. + * 52742: Src/builtin.c: fix bad interactions of "typeset -p" with GLOBAL_EXPORT, plus some other inconsistencies. diff --git a/Src/Modules/ksh93.c b/Src/Modules/ksh93.c index 8d10317dc..3206c11f3 100644 --- a/Src/Modules/ksh93.c +++ b/Src/Modules/ksh93.c @@ -102,9 +102,10 @@ static const struct gsu_scalar sh_name_gsu = static const struct gsu_scalar sh_subscript_gsu = { strvargetfn, nullstrsetfn, nullunsetfn }; -static char *sh_name; -static char *sh_subscript; -static char *sh_edchar; +static char sh_unsetval[2]; /* Dummy to treat as NULL */ +static char *sh_name = sh_unsetval; +static char *sh_subscript = sh_unsetval; +static char *sh_edchar = sh_unsetval; static char sh_edmode[2]; /* @@ -193,7 +194,7 @@ ksh93_wrapper(Eprog prog, FuncWrap w, char *name) strcpy(sh_edmode, "\033"); else strcpy(sh_edmode, ""); - if (!sh_edchar) + if (sh_edchar == sh_unsetval) sh_edchar = dupstring(getsparam("KEYS")); if (varedarg) { char *ie = itype_end((sh_name = dupstring(varedarg)), INAMESPC, 0); @@ -204,16 +205,16 @@ ksh93_wrapper(Eprog prog, FuncWrap w, char *name) ie = sh_subscript + strlen(sh_subscript); *--ie = '\0'; } else - sh_subscript = NULL; + sh_subscript = sh_unsetval; if ((pm = createparam(".sh.value", LOCAL_NAMEREF))) { pm->level = locallevel; setloopvar(".sh.value", "BUFFER"); /* Hack */ pm->node.flags |= PM_READONLY; } } else - sh_name = sh_subscript = NULL; + sh_name = sh_subscript = sh_unsetval; } else { - sh_edchar = sh_name = sh_subscript = NULL; + sh_edchar = sh_name = sh_subscript = sh_unsetval; strcpy(sh_edmode, ""); /* TODO: * - disciplines -- cgit v1.2.3 From 326e8635fe01239ddf14c09785eeca2394e32b95 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Thu, 14 Mar 2024 13:11:31 -0700 Subject: 52752: typeset -p fixes for local exports and "export -x" / "readonly -r" output. --- ChangeLog | 5 +++++ Src/params.c | 21 ++++++++++++++++++--- Test/B02typeset.ztst | 14 +++++++------- 3 files changed, 30 insertions(+), 10 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index ab59d83d7..5eca27d43 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-03-14 Bart Schaefer + + * 52752: Src/params.c, Test/B02typeset.ztst: more typeset -p fixes + for local exports and improper "export -x" / "readonly -r" output. + 2024-03-13 Bart Schaefer * 52753: Doc/Zsh/grammar.yo: Clarify "nocorrect" when introducing diff --git a/Src/params.c b/Src/params.c index 973df3fe5..f65aa1e80 100644 --- a/Src/params.c +++ b/Src/params.c @@ -6044,6 +6044,7 @@ printparamnode(HashNode hn, int printflags) { Param p = (Param) hn; Param peer = NULL; + int altname = 0; if (!(p->node.flags & PM_HASHELEM) && !(printflags & PRINT_WITH_NAMESPACE) && *(p->node.nam) == '.') @@ -6089,16 +6090,26 @@ printparamnode(HashNode hn, int printflags) if (printflags & PRINT_POSIX_EXPORT) { if (!(p->node.flags & PM_EXPORTED)) return; + altname = 'x'; printf("export "); } else if (printflags & PRINT_POSIX_READONLY) { if (!(p->node.flags & PM_READONLY)) return; + altname = 'r'; printf("readonly "); - } else if (locallevel && p->level >= locallevel) { - printf("typeset "); /* printf("local "); */ } else if ((p->node.flags & PM_EXPORTED) && !(p->node.flags & (PM_ARRAY|PM_HASHED))) { - printf("export "); + if (p->level && p->level >= locallevel) + printf("local "); + else { + altname = 'x'; + printf("export "); + } + } else if (locallevel && p->level >= locallevel) { + if (p->node.flags & PM_EXPORTED) + printf("local "); + else + printf("typeset "); /* printf("local "); */ } else if (locallevel) { printf("typeset -g "); } else @@ -6112,6 +6123,10 @@ printparamnode(HashNode hn, int printflags) for (pmptr = pmtypes, i = 0; i < PMTYPES_SIZE; i++, pmptr++) { int doprint = 0; + + if (altname && altname == pmptr->typeflag) + continue; + if (pmptr->flags & PMTF_TEST_LEVEL) { if (p->level) { /* diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst index d90f17d13..914eea92b 100644 --- a/Test/B02typeset.ztst +++ b/Test/B02typeset.ztst @@ -311,7 +311,7 @@ print $OUTER 0:Export of tied parameters >i:n:n:e:r ->typeset -xT OUTER outer=( i n n e r ) +>local -xT OUTER outer=( i n n e r ) >typeset -aT OUTER outer=( i n n e r ) >OUTER=i:n:n:e:r >outer=( i n n e r ) @@ -1099,12 +1099,12 @@ } 0: no array/hash in POSIX export/readonly -p >zsh: ->typeset -arx zsh_exported_readonly_array=( 2 ) ->typeset -Arx zsh_exported_readonly_hash=( [3]=3 ) ->typeset -rx zsh_exported_readonly_scalar=1 ->typeset -arx zsh_exported_readonly_array=( 2 ) ->typeset -Arx zsh_exported_readonly_hash=( [3]=3 ) ->typeset -rx zsh_exported_readonly_scalar=1 +>local -arx zsh_exported_readonly_array=( 2 ) +>local -Arx zsh_exported_readonly_hash=( [3]=3 ) +>local -rx zsh_exported_readonly_scalar=1 +>local -arx zsh_exported_readonly_array=( 2 ) +>local -Arx zsh_exported_readonly_hash=( [3]=3 ) +>local -rx zsh_exported_readonly_scalar=1 >sh: >export zsh_exported_readonly_scalar=1 >readonly zsh_exported_readonly_scalar=1 -- cgit v1.2.3 From 25182cc2e69ab1cfeeb3f0faa1d28d774393043b Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 17 Mar 2024 14:28:28 -0700 Subject: 52759: ${ ... } trims one trailing newline; "${ ... }" preserves that newline. --- ChangeLog | 5 +++++ Doc/Zsh/expn.yo | 3 +++ Etc/FAQ.yo | 21 ++++++++++++--------- Src/subst.c | 8 ++++++-- Test/D10nofork.ztst | 41 +++++++++++++++++++++++++++++++++++------ 5 files changed, 61 insertions(+), 17 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index c3f770477..7e5f68059 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2024-03-14 Bart Schaefer + * 52759: Doc/Zsh/expn.yo, Etc/FAQ.yo, Src/subst.c, + Test/D10nofork.ztst: change ${ ... } substitution to trim one + trailing newline; instead "${ ... }" (with quotes) preserves that + newline. All trailing newlines are still trimmed in emulations. + * unposted: Etc/BUGS: HIST_IGNORE_DUPS mishandles quoted whitespace. * 52752: Src/params.c, Test/B02typeset.ztst: more typeset -p fixes diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index 183ca6e03..0e121e784 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -1950,6 +1950,9 @@ the braces by whitespace, like `tt(${ )...tt( })', is replaced by its standard output. Like `tt(${|)...tt(})' and unlike `tt($LPAR())...tt(RPAR())', the command executes in the current shell context with function local behaviors and does not create a subshell. +Word splitting does not apply unless tt(SH_WORD_SPLIT) is set, but a +single trailing newline is stripped unless the substitution is enclosed +in double quotes. Note that because the `tt(${|)...tt(})' and `tt(${ )...tt( })' forms must be parsed at once as both string tokens and commands, all other diff --git a/Etc/FAQ.yo b/Etc/FAQ.yo index 4a86050e6..4d71c8f30 100644 --- a/Etc/FAQ.yo +++ b/Etc/FAQ.yo @@ -1091,20 +1091,23 @@ sect(Comparisons of forking and non-forking command substitution) mytt(set -- pos1 pos2 etc). Nothing that happens within mytt($(command)) affects the caller. - mytt($(command)) removes trailing newlines from the output of mytt(command) - when substituting, whereas mytt(${ command }) and its variants do not. - The latter is consistent with mytt(${|...}) from mksh but differs from - bash and ksh, so in emulation modes, newlines are stripped from command - output (not from tt(REPLY) assignments). - When not enclosed in double quotes, the expansion of mytt($(command)) is split on tt(IFS) into an array of words. In contrast, and unlike both bash and ksh, unquoted non-forking substitutions behave like parameter expansions with respect to the tt(SH_WORD_SPLIT) option. - When mytt(command) is myem(not) a builtin, mytt(${ command }) does fork, and - typically forks the same number of times as mytt($(command)), because in - the latter case zsh usually optimizes the final fork into an exec. + Both of the mytt(${|...}) formats retain any trailing newlines, + except as handled by the tt(SH_WORD_SPLIT) option, consistent with + mytt(${|...}) from mksh. mytt(${ command }) removes a single final + newline, but mytt("${ command }") retains it. This differs from + bash and ksh, so in emulation modes, newlines are stripped even from + quoted command output. In all cases, mytt($(command)) removes all + trailing newlines from the output of mytt(command). + + When mytt(command) is myem(not) a builtin, mytt(${ command }) does + fork, and typically forks the same number of times as + mytt($(command)), because in the latter case zsh usually optimizes + the final fork into an exec. Redirecting input from files has subtle differences: itemization( diff --git a/Src/subst.c b/Src/subst.c index 49f7336bb..9d20a2d0e 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1900,6 +1900,7 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, /* The command string to be run by ${|...;} */ char *cmdarg = NULL; size_t slen = 0; + int trim = (!EMULATION(EMULATE_ZSH)) ? 2 : !qt; inbrace = 1; s++; @@ -2005,10 +2006,13 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, int onoerrs = noerrs, rplylen; noerrs = 2; rplylen = zstuff(&cmdarg, rplytmp); - if (! EMULATION(EMULATE_ZSH)) { + if (trim) { /* bash and ksh strip trailing newlines here */ - while (rplylen > 0 && cmdarg[rplylen-1] == '\n') + while (rplylen > 0 && cmdarg[rplylen-1] == '\n') { rplylen--; + if (trim == 1) + break; + } cmdarg[rplylen] = 0; } noerrs = onoerrs; diff --git a/Test/D10nofork.ztst b/Test/D10nofork.ztst index d6a5588df..fc6b84613 100644 --- a/Test/D10nofork.ztst +++ b/Test/D10nofork.ztst @@ -86,9 +86,39 @@ F:setting option inside is too late for that substitution ?(eval):8: no matches found: f?* purr ${| REPLY=$'trailing newlines remain\n\n' } -0:newline removal should not occur +0:newline removal should not occur, part 1 >trailing newlines remain > +> + + purr ${ echo $'one trailing newline\nremoved\n\n\n' } +0:newline removal in ${ ... }, zsh mode +>one trailing newline +>removed +> +> +> + + () { + emulate -L ksh + purl ${ echo $'all trailing newlines\nremoved\n\n\n' } + purr "${ echo $'all trailing newlines\nremoved\n\n\n' }" + } +0:newline removal in ${ ... }, emulation mode, shwordsplit +>all +>trailing +>newlines +>removed +>all trailing newlines +>removed + + purr "${ echo $'no trailing newlines\nremoved\n\n\n' }" +0:newline removal should not occur, part 2 +>no trailing newlines +>removed +> +> +> > () { @@ -159,7 +189,7 @@ F:Why not use this error in the previous case as well? 1:unbalanced braces, part 4+ ?(eval):1: closing brace expected - purr ${ purr STDOUT } + purr "${ purr STDOUT }" 0:capture stdout >STDOUT > @@ -322,7 +352,7 @@ F:Fiddly here to get EOF past the test syntax 0:here-string behavior >in a here string - <<<${ purr $'stdout as a here string' } + <<<"${ purr $'stdout as a here string' }" 0:another capture stdout >stdout as a here string > @@ -331,7 +361,7 @@ F:Fiddly here to get EOF past the test syntax wrap=${ purr "capture in environment assignment" } typeset -p wrap 0:assignment context >typeset -g wrap='REPLY in environment assignment' ->typeset -g wrap=$'capture in environment assignment\n' +>typeset -g wrap='capture in environment assignment' # Repeat return and exit tests with stdout capture @@ -410,7 +440,7 @@ F:must do this before evaluating the next test block 0:ignored braces, part 1 >buried} - purr ${ purr ${REPLY:-buried}}} + purr "${ purr ${REPLY:-buried}}}" 0:ignored braces, part 2 >buried >} @@ -418,7 +448,6 @@ F:must do this before evaluating the next test block purr ${ { echo nested ;} } 0:ignored braces, part 3 >nested -> purr ${ { echo nested } } DONE 1:ignored braces, part 4 -- cgit v1.2.3 From 57248b88830ce56adc243a40c7773fb3825cab34 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Mon, 18 Mar 2024 20:02:34 +0100 Subject: 52750: remove ansi2knr support for old pre-ansi K&R compilers --- ChangeLog | 11 ++++++ Config/defs.mk.in | 3 +- Etc/zsh-development-guide | 2 +- Src/Makemod.in.in | 23 +++--------- Src/Modules/files.c | 4 +-- Src/Modules/watch.c | 2 +- Src/Modules/zftp.c | 2 +- Src/Modules/zprof.c | 6 ++-- Src/Zle/compcore.c | 4 +-- Src/Zle/zle.h | 4 +-- Src/Zle/zle_keymap.c | 2 +- Src/Zle/zle_thingy.c | 2 +- Src/exec.c | 2 +- Src/glob.c | 8 ++--- Src/hist.c | 14 ++++---- Src/makepro.awk | 4 +-- Src/mem.c | 8 ++--- Src/mkbltnmlst.sh | 12 +++---- Src/modentry.c | 10 +++--- Src/parse.c | 2 +- Src/prototypes.h | 54 ++++++++++++++-------------- Src/signals.h | 6 ++-- Src/utils.c | 7 +++- Src/zsh.h | 92 +++++++++++++++++++++++------------------------ Src/zsh_system.h | 8 +---- aclocal.m4 | 50 -------------------------- configure.ac | 41 +-------------------- 27 files changed, 144 insertions(+), 239 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 7e5f68059..290b1f1b2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2024-03-18 Oliver Kiddle + + * 52750: Config/defs.mk.in, Etc/zsh-development-guide, + Src/Makemod.in.in, Src/Modules/files.c, Src/Modules/watch.c, + Src/Modules/zftp.c, Src/Modules/zprof.c, Src/Zle/compcore.c, + Src/Zle/zle.h, Src/Zle/zle_keymap.c, Src/Zle/zle_thingy.c, + Src/exec.c, Src/glob.c, Src/hist.c, Src/makepro.awk, Src/mem.c, + Src/mkbltnmlst.sh, Src/modentry.c, Src/parse.c, Src/prototypes.h, + Src/signals.h, Src/utils.c, Src/zsh.h, Src/zsh_system.h, aclocal.m4, + configure.ac: remove ansi2knr support for old pre-ansi K&R compilers + 2024-03-14 Bart Schaefer * 52759: Doc/Zsh/expn.yo, Etc/FAQ.yo, Src/subst.c, diff --git a/Config/defs.mk.in b/Config/defs.mk.in index 2bc17482a..116875fb9 100644 --- a/Config/defs.mk.in +++ b/Config/defs.mk.in @@ -75,7 +75,6 @@ IMPOPT = @IMPOPT@ # utilities AWK = @AWK@ -ANSI2KNR = @ANSI2KNR@ YODL = @YODL@ @YODL_OPTIONS@ YODL2TXT = @YODL@2txt YODL2HTML = @YODL@2html @@ -100,7 +99,7 @@ LDFLAGS='$(LDFLAGS)' EXTRA_LDFLAGS='$(EXTRA_LDFLAGS)' \ DLCFLAGS='$(DLCFLAGS)' DLLDFLAGS='$(DLLDFLAGS)' \ LIBLDFLAGS='$(LIBLDFLAGS)' EXELDFLAGS='$(EXELDFLAGS)' \ LIBS='$(LIBS)' DL_EXT='$(DL_EXT)' DLLD='$(DLLD)' \ -AWK='$(AWK)' ANSI2KNR='$(ANSI2KNR)' \ +AWK='$(AWK)' \ YODL='$(YODL)' YODL2TXT='$(YODL2TXT)' YODL2HTML='$(YODL2HTML)' \ FUNCTIONS_INSTALL='$(FUNCTIONS_INSTALL)' tzsh='$(tzsh)' diff --git a/Etc/zsh-development-guide b/Etc/zsh-development-guide index 5cb542709..bdabe17d8 100644 --- a/Etc/zsh-development-guide +++ b/Etc/zsh-development-guide @@ -240,7 +240,7 @@ C coding style There must be an empty line, a line with "/**/", a line with the type of the function, and finally the name of the function with typed arguments. These lines must not be indented. The script generating - function prototypes and the ansi2knr program depend on this format. + function prototypes depends on this format. * Variable declarations must similarly be preceded by a line containing only "/**/", for the prototype generation script. diff --git a/Src/Makemod.in.in b/Src/Makemod.in.in index ea0cdc3a4..3343ae1d0 100644 --- a/Src/Makemod.in.in +++ b/Src/Makemod.in.in @@ -52,32 +52,17 @@ DLCOMPILE = $(CC) -c -I. -I$(dir_top)/Src -I$(sdir_top)/Src -I$(sdir_top)/Src/ LINK = $(CC) $(LDFLAGS) $(EXELDFLAGS) $(EXTRA_LDFLAGS) -o $@ DLLINK = $(DLLD) $(LDFLAGS) $(LIBLDFLAGS) $(DLLDFLAGS) -o $@ -KNR_OBJ=.o -KNROBJ=._foo_ +OBJ=.o -ANSIOBJ=.o -ANSI_OBJ=._foo_ +.SUFFIXES: .c .$(DL_EXT) ..o .o .syms .pro .epro -.SUFFIXES: .c .$(DL_EXT) ..o .._foo_ .o ._foo_ .syms .pro .epro - -.c$(ANSI@U@OBJ): +.c$(OBJ): $(COMPILE) -o $@ $< @rm -f $(dir_src)/stamp-modobjs -.c$(KNR@U@OBJ): - @ANSI2KNR@ $< > $@.c - $(COMPILE) -o $@ $@.c - rm -f $@.c - @rm -f $(dir_src)/stamp-modobjs - -.c.$(ANSI@U@OBJ): +.c.$(OBJ): $(DLCOMPILE) -o $@ $< -.c.$(KNR@U@OBJ): - @ANSI2KNR@ $< > $@.c - $(DLCOMPILE) -o $@ $@.c - rm -f $@.c - .c.syms: $(AWK) -f $(sdir_src)/makepro.awk $< $(subdir) > $@ diff --git a/Src/Modules/files.c b/Src/Modules/files.c index bf0e8f8a8..a3fec1daa 100644 --- a/Src/Modules/files.c +++ b/Src/Modules/files.c @@ -29,8 +29,8 @@ #include "files.mdh" -typedef int (*MoveFunc) _((char const *, char const *)); -typedef int (*RecurseFunc) _((char *, char *, struct stat const *, void *)); +typedef int (*MoveFunc) (char const *, char const *); +typedef int (*RecurseFunc) (char *, char *, struct stat const *, void *); struct recursivecmd; diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c index ba17cf940..acc499518 100644 --- a/Src/Modules/watch.c +++ b/Src/Modules/watch.c @@ -584,7 +584,7 @@ readwtab(WATCH_STRUCT_UTMP **head, int initial_sz) if (sz) qsort((void *) *head, sz, sizeof(WATCH_STRUCT_UTMP), - (int (*) _((const void *, const void *)))ucmp); + (int (*) (const void *, const void *))ucmp); return sz; } diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index 47a5e9de9..0c26828fd 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -127,7 +127,7 @@ typedef int (*readwrite_t)(int, char *, off_t, int); struct zftpcmd { const char *nam; - int (*fun) _((char *, char **, int)); + int (*fun) (char *, char **, int); int min, max, flags; }; diff --git a/Src/Modules/zprof.c b/Src/Modules/zprof.c index 56cdab888..171a15b90 100644 --- a/Src/Modules/zprof.c +++ b/Src/Modules/zprof.c @@ -163,9 +163,9 @@ bin_zprof(UNUSED(char *nam), UNUSED(char **args), Options ops, UNUSED(int func)) *ap = NULL; qsort(fs, ncalls, sizeof(f), - (int (*) _((const void *, const void *))) cmpsfuncs); + (int (*) (const void *, const void *)) cmpsfuncs); qsort(as, narcs, sizeof(a), - (int (*) _((const void *, const void *))) cmpparcs); + (int (*) (const void *, const void *)) cmpparcs); printf("num calls time self name\n-----------------------------------------------------------------------------------\n"); for (fp = fs, i = 1; *fp; fp++, i++) { @@ -179,7 +179,7 @@ bin_zprof(UNUSED(char *nam), UNUSED(char **args), Options ops, UNUSED(int func)) (*fp)->name); } qsort(fs, ncalls, sizeof(f), - (int (*) _((const void *, const void *))) cmptfuncs); + (int (*) (const void *, const void *)) cmptfuncs); for (fp = fs; *fp; fp++) { printf("\n-----------------------------------------------------------------------------------\n\n"); diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 9b87cad93..09282d42d 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -3253,7 +3253,7 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp) /* Now sort the array (it contains matches). */ matchorder = flags; qsort((void *) rp, n, sizeof(Cmatch), - (int (*) _((const void *, const void *)))matchcmp); + (int (*) (const void *, const void *))matchcmp); /* since the matches are sorted and the default is to remove * all duplicates, -1 (remove only consecutive dupes) is a no-op, @@ -3295,7 +3295,7 @@ makearray(LinkList l, int type, int flags, int *np, int *nlp, int *llp) sp = (Cmatch *) zhalloc((n + 1) * sizeof(Cmatch)); memcpy(sp, rp, (n + 1) * sizeof(Cmatch)); qsort((void *) sp, n, sizeof(Cmatch), - (int (*) _((const void *, const void *)))matchcmp); + (int (*) (const void *, const void *))matchcmp); for (asp = sp + 1; *asp; asp++) { Cmatch *ap = asp - 1, *bp = asp; if (matcheq(*ap, *bp)) { diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index 010ead3d2..5bb9e7a5e 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -186,7 +186,7 @@ typedef struct thingy *Thingy; /* widgets (ZLE functions) */ -typedef int (*ZleIntFunc) _((char **)); +typedef int (*ZleIntFunc) (char **); struct widget { int flags; /* flags (see below) */ @@ -319,7 +319,7 @@ struct vichange { typedef struct keymap *Keymap; -typedef void (*KeyScanFunc) _((char *, Thingy, char *, void *)); +typedef void (*KeyScanFunc) (char *, Thingy, char *, void *); #define invicmdmode() (!strcmp(curkeymapname, "vicmd")) diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index 041682ee9..5012917f5 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -745,7 +745,7 @@ bin_bindkey(char *name, char **argv, Options ops, UNUSED(int func)) static struct opn { char o; char selp; - int (*func) _((char *, char *, Keymap, char **, Options, char)); + int (*func) (char *, char *, Keymap, char **, Options, char); int min, max; } const opns[] = { { 'l', 0, bin_bindkey_lsmaps, 0, -1 }, diff --git a/Src/Zle/zle_thingy.c b/Src/Zle/zle_thingy.c index 1b036a8a0..f71435b01 100644 --- a/Src/Zle/zle_thingy.c +++ b/Src/Zle/zle_thingy.c @@ -344,7 +344,7 @@ bin_zle(char *name, char **args, Options ops, UNUSED(int func)) { static struct opn { char o; - int (*func) _((char *, char **, Options, char)); + int (*func) (char *, char **, Options, char); int min, max; } const opns[] = { { 'l', bin_zle_list, 0, -1 }, diff --git a/Src/exec.c b/Src/exec.c index 0231bc361..e955e85df 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -267,7 +267,7 @@ static char *blank_env[] = { NULL }; /* Execution functions. */ -static int (*execfuncs[WC_COUNT-WC_CURSH]) _((Estate, int)) = { +static int (*execfuncs[WC_COUNT-WC_CURSH]) (Estate, int) = { execcursh, exectime, NULL /* execfuncdef handled specially */, execfor, execselect, execwhile, execrepeat, execcase, execif, execcond, diff --git a/Src/glob.c b/Src/glob.c index bd199ace3..3e34f708e 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -133,7 +133,7 @@ typedef struct stat *Statptr; /* This makes the Ultrix compiler happy. Go figu #define TT_TERABYTES 5 -typedef int (*TestMatchFunc) _((char *, struct stat *, off_t, char *)); +typedef int (*TestMatchFunc) (char *, struct stat *, off_t, char *); struct qual { struct qual *next; /* Next qualifier, must match */ @@ -1264,7 +1264,7 @@ zglob(LinkList list, LinkNode np, int nountok) int sense, qualsfound; off_t data; char *sdata, *newcolonmod, *ptr; - int (*func) _((char *, Statptr, off_t, char *)); + int (*func) (char *, Statptr, off_t, char *); /* * Initialise state variables for current file pattern. @@ -1310,7 +1310,7 @@ zglob(LinkList list, LinkNode np, int nountok) if (*ptr == Dash) *ptr = '-'; while (*s && !newcolonmod) { - func = (int (*) _((char *, Statptr, off_t, char *)))0; + func = (int (*) (char *, Statptr, off_t, char *)) 0; if (*s == ',') { /* A comma separates alternative sets of qualifiers */ s++; @@ -1961,7 +1961,7 @@ zglob(LinkList list, LinkNode np, int nountok) /* Sort arguments in to lexical (and possibly numeric) order. * * This is reversed to facilitate insertion into the list. */ qsort((void *) & matchbuf[0], matchct, sizeof(struct gmatch), - (int (*) _((const void *, const void *)))gmatchcmp); + (int (*) (const void *, const void *)) gmatchcmp); } if (first < 0) { diff --git a/Src/hist.c b/Src/hist.c index 448dfddbc..1a00c30ed 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -34,25 +34,25 @@ * word control. */ /**/ -mod_export int (*hgetc) _((void)); +mod_export int (*hgetc) (void); /**/ -void (*hungetc) _((int)); +void (*hungetc) (int); /**/ -void (*hwaddc) _((int)); +void (*hwaddc) (int); /**/ -void (*hwbegin) _((int)); +void (*hwbegin) (int); /**/ -void (*hwabort) _((void)); +void (*hwabort) (void); /**/ -void (*hwend) _((void)); +void (*hwend) (void); /**/ -void (*addtoline) _((int)); +void (*addtoline) (int); /* != 0 means history substitution is turned off */ diff --git a/Src/makepro.awk b/Src/makepro.awk index 0d53c5850..56c4f4595 100644 --- a/Src/makepro.awk +++ b/Src/makepro.awk @@ -131,8 +131,8 @@ BEGIN { sub(/@-.*$/, "", dnam) # Put parens etc. back - gsub(/@[{]/, " _((", dcltor) - gsub(/@}/, "))", dcltor) + gsub(/@[{]/, " (", dcltor) + gsub(/@}/, ")", dcltor) gsub(/@/, ")", dcltor) gsub(/@!/, ",", dcltor) diff --git a/Src/mem.c b/Src/mem.c index fb4be47bf..0b6f76e46 100644 --- a/Src/mem.c +++ b/Src/mem.c @@ -1057,17 +1057,17 @@ zrealloc(void *ptr, size_t size) #if !defined(__hpux) && !defined(DGUX) && !defined(__osf__) # if defined(_BSD) # ifndef HAVE_BRK_PROTO - extern int brk _((caddr_t)); + extern int brk (caddr_t); # endif # ifndef HAVE_SBRK_PROTO - extern caddr_t sbrk _((int)); + extern caddr_t sbrk (int); # endif # else # ifndef HAVE_BRK_PROTO - extern int brk _((void *)); + extern int brk (void *); # endif # ifndef HAVE_SBRK_PROTO - extern void *sbrk _((int)); + extern void *sbrk (int); # endif # endif #endif diff --git a/Src/mkbltnmlst.sh b/Src/mkbltnmlst.sh index 067ecdaf9..1994ace60 100644 --- a/Src/mkbltnmlst.sh +++ b/Src/mkbltnmlst.sh @@ -122,12 +122,12 @@ for bin_mod in $bin_mods; do esac done echo " {" - echo " extern int setup_${q_bin_mod} _((Module));" - echo " extern int boot_${q_bin_mod} _((Module));" - echo " extern int features_${q_bin_mod} _((Module,char***));" - echo " extern int enables_${q_bin_mod} _((Module,int**));" - echo " extern int cleanup_${q_bin_mod} _((Module));" - echo " extern int finish_${q_bin_mod} _((Module));" + echo " extern int setup_${q_bin_mod} (Module);" + echo " extern int boot_${q_bin_mod} (Module);" + echo " extern int features_${q_bin_mod} (Module,char***);" + echo " extern int enables_${q_bin_mod} (Module,int**);" + echo " extern int cleanup_${q_bin_mod} (Module);" + echo " extern int finish_${q_bin_mod} (Module);" echo echo " register_module(\"$bin_mod\"," echo " setup_${q_bin_mod}," diff --git a/Src/modentry.c b/Src/modentry.c index 4d8217f43..23c499d94 100644 --- a/Src/modentry.c +++ b/Src/modentry.c @@ -1,10 +1,10 @@ #include "zsh.mdh" -int setup_ _((Module)); -int boot_ _((Module)); -int cleanup_ _((Module)); -int finish_ _((Module)); -int modentry _((int boot, Module m, void *ptr)); +int setup_ (Module); +int boot_ (Module); +int cleanup_ (Module); +int finish_ (Module); +int modentry (int boot, Module m, void *ptr); /**/ int diff --git a/Src/parse.c b/Src/parse.c index 40eb0ee0b..334365649 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -2392,7 +2392,7 @@ par_nl_wordlist(void) */ /**/ -void (*condlex) _((void)) = zshlex; +void (*condlex) (void) = zshlex; /* * cond : cond_1 { SEPER } [ DBAR { SEPER } cond ] diff --git a/Src/prototypes.h b/Src/prototypes.h index e3db4f5ee..3578482d0 100644 --- a/Src/prototypes.h +++ b/Src/prototypes.h @@ -28,9 +28,9 @@ */ #ifndef HAVE_STDLIB_H -char *malloc _((size_t)); -char *realloc _((void *, size_t)); -char *calloc _((size_t, size_t)); +char *malloc (size_t); +char *realloc (void *, size_t); +char *calloc (size_t, size_t); #endif #if !(defined(USES_TERMCAP_H) || defined(USES_TERM_H)) @@ -45,11 +45,11 @@ char *calloc _((size_t, size_t)); #else #define TC_CONST #endif -extern int tgetent _((char *bp, TC_CONST char *name)); -extern int tgetnum _((char *id)); -extern int tgetflag _((char *id)); -extern char *tgetstr _((char *id, char **area)); -extern int tputs _((TC_CONST char *cp, int affcnt, int (*outc) (int))); +extern int tgetent (char *bp, TC_CONST char *name); +extern int tgetnum (char *id); +extern int tgetflag (char *id); +extern char *tgetstr (char *id, char **area); +extern int tputs (TC_CONST char *cp, int affcnt, int (*outc) (int)); #undef TC_CONST #endif @@ -70,30 +70,30 @@ char *tgoto(const char *cap, int col, int row); #endif #ifdef __osf__ -char *mktemp _((char *)); +char *mktemp (char *); #endif #if defined(__osf__) && defined(__alpha) && defined(__GNUC__) /* Digital cc does not need these prototypes, gcc does need them */ # ifndef HAVE_IOCTL_PROTO -int ioctl _((int d, unsigned long request, void *argp)); +int ioctl (int d, unsigned long request, void *argp); # endif # ifndef HAVE_MKNOD_PROTO -int mknod _((const char *pathname, int mode, dev_t device)); +int mknod (const char *pathname, int mode, dev_t device); # endif -int nice _((int increment)); -int select _((int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout)); +int nice (int increment); +int select (int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout); #endif #if defined(DGUX) && defined(__STDC__) /* Just plain missing. */ -extern int getrlimit _((int resource, struct rlimit *rlp)); -extern int setrlimit _((int resource, const struct rlimit *rlp)); -extern int getrusage _((int who, struct rusage *rusage)); -extern int gettimeofday _((struct timeval *tv, struct timezone *tz)); -extern int wait3 _((union wait *wait_status, int options, struct rusage *rusage)); -extern int getdomainname _((char *name, int maxlength)); -extern int select _((int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout)); +extern int getrlimit (int resource, struct rlimit *rlp); +extern int setrlimit (int resource, const struct rlimit *rlp); +extern int getrusage (int who, struct rusage *rusage); +extern int gettimeofday (struct timeval *tv, struct timezone *tz); +extern int wait3 (union wait *wait_status, int options, struct rusage *rusage); +extern int getdomainname (char *name, int maxlength); +extern int select (int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout); #endif /* DGUX and __STDC__ */ #ifdef __NeXT__ @@ -101,34 +101,34 @@ extern pid_t getppid(void); #endif #if defined(__sun__) && !defined(__SVR4) /* SunOS */ -extern char *strerror _((int errnum)); +extern char *strerror (int errnum); #endif /**************************************************/ /*** prototypes for functions built in compat.c ***/ #ifndef HAVE_STRSTR -extern char *strstr _((const char *s, const char *t)); +extern char *strstr (const char *s, const char *t); #endif #ifndef HAVE_GETHOSTNAME -extern int gethostname _((char *name, size_t namelen)); +extern int gethostname (char *name, size_t namelen); #endif #ifndef HAVE_GETTIMEOFDAY -extern int gettimeofday _((struct timeval *tv, struct timezone *tz)); +extern int gettimeofday (struct timeval *tv, struct timezone *tz); #endif #ifndef HAVE_DIFFTIME -extern double difftime _((time_t t2, time_t t1)); +extern double difftime (time_t t2, time_t t1); #endif #ifndef HAVE_STRERROR -extern char *strerror _((int errnum)); +extern char *strerror (int errnum); #endif /*** end of prototypes for functions in compat.c ***/ /***************************************************/ #ifndef HAVE_MEMMOVE -extern void bcopy _((const void *, void *, size_t)); +extern void bcopy (const void *, void *, size_t); #endif diff --git a/Src/signals.h b/Src/signals.h index 391f11fed..7910f6b79 100644 --- a/Src/signals.h +++ b/Src/signals.h @@ -27,7 +27,7 @@ * */ -#define SIGNAL_HANDTYPE void (*)_((int)) +#define SIGNAL_HANDTYPE void (*)(int) #ifndef HAVE_KILLPG # define killpg(pgrp,sig) kill(-(pgrp),sig) @@ -145,7 +145,7 @@ #ifdef BSD_SIGNALS #define signal_block(S) sigblock(S) #else -extern sigset_t signal_block _((sigset_t)); +extern sigset_t signal_block (sigset_t); #endif /* BSD_SIGNALS */ -extern sigset_t signal_unblock _((sigset_t)); +extern sigset_t signal_unblock (sigset_t); diff --git a/Src/utils.c b/Src/utils.c index c8831c85e..ce4e875fd 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -5277,6 +5277,7 @@ nicedupstring(char const *s) } +/**/ #ifndef MULTIBYTE_SUPPORT /* Unmetafy and output a string, displaying special characters readably. */ @@ -5311,8 +5312,9 @@ niceztrlen(char const *s) } return l; } -#endif +/**/ +#endif /**/ #ifdef MULTIBYTE_SUPPORT @@ -7633,6 +7635,7 @@ mode_to_octal(mode_t mode) return m; } +/**/ #ifdef MAILDIR_SUPPORT /* * Stat a file. If it's a maildir, check all messages @@ -7756,4 +7759,6 @@ mailstat(char *path, struct stat *st) *st = st_ret_last = st_ret; return 0; } + +/**/ #endif diff --git a/Src/zsh.h b/Src/zsh.h index fae62b8d0..090abf8f5 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -618,7 +618,7 @@ union linkroot { /* Specific elements of linked lists */ /*************************************/ -typedef void (*voidvoidfnptr_t) _((void)); +typedef void (*voidvoidfnptr_t) (void); /* * Element of the prepromptfns list. @@ -678,7 +678,7 @@ struct timedfn { #define COND_MOD 18 #define COND_MODI 19 -typedef int (*CondHandler) _((char **, int)); +typedef int (*CondHandler) (char **, int); struct conddef { Conddef next; /* next in list */ @@ -1164,28 +1164,28 @@ struct dirsav { /* Definitions for Hash Tables */ /*******************************/ -typedef void *(*VFunc) _((void *)); -typedef void (*FreeFunc) _((void *)); +typedef void *(*VFunc) (void *); +typedef void (*FreeFunc) (void *); -typedef unsigned (*HashFunc) _((const char *)); -typedef void (*TableFunc) _((HashTable)); +typedef unsigned (*HashFunc) (const char *); +typedef void (*TableFunc) (HashTable); /* * Note that this is deliberately "char *", not "const char *", * since the AddNodeFunc is passed a pointer to a string that * will be stored and later freed. */ -typedef void (*AddNodeFunc) _((HashTable, char *, void *)); -typedef HashNode (*GetNodeFunc) _((HashTable, const char *)); -typedef HashNode (*RemoveNodeFunc) _((HashTable, const char *)); -typedef void (*FreeNodeFunc) _((HashNode)); -typedef int (*CompareFunc) _((const char *, const char *)); +typedef void (*AddNodeFunc) (HashTable, char *, void *); +typedef HashNode (*GetNodeFunc) (HashTable, const char *); +typedef HashNode (*RemoveNodeFunc) (HashTable, const char *); +typedef void (*FreeNodeFunc) (HashNode); +typedef int (*CompareFunc) (const char *, const char *); /* type of function that is passed to * * scanhashtable or scanmatchtable */ -typedef void (*ScanFunc) _((HashNode, int)); -typedef void (*ScanTabFunc) _((HashTable, ScanFunc, int)); +typedef void (*ScanFunc) (HashNode, int); +typedef void (*ScanTabFunc) (HashTable, ScanFunc, int); -typedef void (*PrintTableStats) _((HashTable)); +typedef void (*PrintTableStats) (HashTable); /* Hash table for standard open hashing. Instances of struct hashtable can be * * created only by newhashtable(). In fact, this function creates an instance * @@ -1352,7 +1352,7 @@ struct funcstack { /* node in list of function call wrappers */ -typedef int (*WrapFunc) _((Eprog, FuncWrap, char *)); +typedef int (*WrapFunc) (Eprog, FuncWrap, char *); struct funcwrap { FuncWrap next; @@ -1428,8 +1428,8 @@ enum { * builtin structure. */ -typedef int (*HandlerFunc) _((char *, char **, Options, int)); -typedef int (*HandlerFuncAssign) _((char *, char **, LinkList, Options, int)); +typedef int (*HandlerFunc) (char *, char **, Options, int); +typedef int (*HandlerFuncAssign) (char *, char **, LinkList, Options, int); #define NULLBINCMD ((HandlerFunc) 0) struct builtin { @@ -1526,10 +1526,10 @@ struct module { /* Module record is an alias */ #define MOD_ALIAS (1<<6) -typedef int (*Module_generic_func) _((void)); -typedef int (*Module_void_func) _((Module)); -typedef int (*Module_features_func) _((Module, char ***)); -typedef int (*Module_enables_func) _((Module, int **)); +typedef int (*Module_generic_func) (void); +typedef int (*Module_void_func) (Module); +typedef int (*Module_features_func) (Module, char ***); +typedef int (*Module_enables_func) (Module, int **); struct linkedmod { char *name; @@ -1574,7 +1574,7 @@ struct feature_enables { /* C-function hooks */ -typedef int (*Hookfn) _((Hookdef, void *)); +typedef int (*Hookfn) (Hookdef, void *); struct hookdef { Hookdef next; @@ -1789,33 +1789,33 @@ typedef const struct gsu_array *GsuArray; typedef const struct gsu_hash *GsuHash; struct gsu_scalar { - char *(*getfn) _((Param)); - void (*setfn) _((Param, char *)); - void (*unsetfn) _((Param, int)); + char *(*getfn) (Param); + void (*setfn) (Param, char *); + void (*unsetfn) (Param, int); }; struct gsu_integer { - zlong (*getfn) _((Param)); - void (*setfn) _((Param, zlong)); - void (*unsetfn) _((Param, int)); + zlong (*getfn) (Param); + void (*setfn) (Param, zlong); + void (*unsetfn) (Param, int); }; struct gsu_float { - double (*getfn) _((Param)); - void (*setfn) _((Param, double)); - void (*unsetfn) _((Param, int)); + double (*getfn) (Param); + void (*setfn) (Param, double); + void (*unsetfn) (Param, int); }; struct gsu_array { - char **(*getfn) _((Param)); - void (*setfn) _((Param, char **)); - void (*unsetfn) _((Param, int)); + char **(*getfn) (Param); + void (*setfn) (Param, char **); + void (*unsetfn) (Param, int); }; struct gsu_hash { - HashTable (*getfn) _((Param)); - void (*setfn) _((Param, HashTable)); - void (*unsetfn) _((Param, int)); + HashTable (*getfn) (Param); + void (*setfn) (Param, HashTable); + void (*unsetfn) (Param, int); }; @@ -2984,7 +2984,7 @@ enum errflag_bits { /* Sorting */ /***********/ -typedef int (*CompareFn) _((const void *, const void *)); +typedef int (*CompareFn) (const void *, const void *); enum { SORTIT_ANYOLDHOW = 0, /* Defaults */ @@ -3042,13 +3042,13 @@ struct hist_stack { short *chwords; int chwordlen; int chwordpos; - int (*hgetc) _((void)); - void (*hungetc) _((int)); - void (*hwaddc) _((int)); - void (*hwbegin) _((int)); - void (*hwabort) _((void)); - void (*hwend) _((void)); - void (*addtoline) _((int)); + int (*hgetc) (void); + void (*hungetc) (int); + void (*hwaddc) (int); + void (*hwbegin) (int); + void (*hwabort) (void); + void (*hwend) (void); + void (*addtoline) (int); unsigned char *cstack; int csp; int hist_keep_comment; @@ -3218,7 +3218,7 @@ enum { /* compctl entry point pointers */ -typedef int (*CompctlReadFn) _((char *, char **, Options, char *)); +typedef int (*CompctlReadFn) (char *, char **, Options, char *); /* ZLE entry point pointer */ diff --git a/Src/zsh_system.h b/Src/zsh_system.h index 16f724401..5c004d53e 100644 --- a/Src/zsh_system.h +++ b/Src/zsh_system.h @@ -82,12 +82,6 @@ */ #define _STRPTIME_DONTZERO -#ifdef PROTOTYPES -# define _(Args) Args -#else -# define _(Args) () -#endif - #ifndef HAVE_ALLOCA # define alloca zhalloc #else @@ -101,7 +95,7 @@ # pragma alloca # else # ifndef alloca -char *alloca _((size_t)); +char *alloca (size_t); # endif # endif # endif diff --git a/aclocal.m4 b/aclocal.m4 index c26e2d834..792d533f2 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -2,56 +2,6 @@ # Copyright (C) 1992, 1994 Free Software Foundation, Inc. # Francois Pinard , 1992. -# @defmac fp_PROG_CC_STDC -# @maindex PROG_CC_STDC -# @ovindex CC -# If the C compiler in not in ANSI C mode by default, try to add an option -# to output variable @code{CC} to make it so. This macro tries various -# options that select ANSI C on some system or another. It considers the -# compiler to be in ANSI C mode if it defines @code{__STDC__} to 1 and -# handles function prototypes correctly. -# -# If you use this macro, you should check after calling it whether the C -# compiler has been set to accept ANSI C; if not, the shell variable -# @code{fp_cv_prog_cc_stdc} is set to @samp{no}. If you wrote your source -# code in ANSI C, you can make an un-ANSIfied copy of it by using the -# program @code{ansi2knr}, which comes with Ghostscript. -# @end defmac - -define(fp_PROG_CC_STDC, -[AC_CACHE_CHECK(for ${CC-cc} option to accept ANSI C, -fp_cv_prog_cc_stdc, -[fp_cv_prog_cc_stdc=no -ac_save_CFLAGS="$CFLAGS" -# Don't try gcc -ansi; that turns off useful extensions and -# breaks some systems' header files. -# AIX -qlanglvl=ansi -# Ultrix and OSF/1 -std1 -# HP-UX -Ae or -Aa -D_HPUX_SOURCE -# SVR4 -Xc -# For HP-UX, we try -Ae first; this turns on ANSI but also extensions, -# as well as defining _HPUX_SOURCE, and we can then use long long. -# We keep the old version for backward compatibility. -for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" -Xc -do - CFLAGS="$ac_save_CFLAGS $ac_arg" - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ -[#ifndef __STDC__ -choke me -#endif -]], [[int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);};]])], -[fp_cv_prog_cc_stdc="$ac_arg"; break],[]) -done -CFLAGS="$ac_save_CFLAGS" -]) -case "x$fp_cv_prog_cc_stdc" in - x|xno) ;; - *) CC="$CC $fp_cv_prog_cc_stdc" ;; -esac -]) - AC_DEFUN(AC_PROG_LN, [AC_MSG_CHECKING(whether ln works) AC_CACHE_VAL(ac_cv_prog_LN, diff --git a/configure.ac b/configure.ac index 175d90433..ba3f2fe90 100644 --- a/configure.ac +++ b/configure.ac @@ -273,11 +273,6 @@ fi], AC_DEFINE(CONFIG_LOCALE) ) -dnl Do you want to compile as K&R C. -AC_ARG_ENABLE(ansi2knr, -AS_HELP_STRING([--enable-ansi2knr],[translate source to K&R C before compiling]), -[ansi2knr="$enableval"], [ansi2knr=default]) - ifdef([runhelpdir],[undefine([runhelpdir])])dnl AC_ARG_ENABLE(runhelpdir, AS_HELP_STRING([--enable-runhelpdir=DIR],[the directory in which to install run-help files]), @@ -483,7 +478,7 @@ fi dnl if the user hasn't specified CFLAGS, then dnl if compiler is gcc, then use -O2 and some warning flags dnl else use -O -if test -n "$auto_cflags" && test ."$ansi2knr" != .yes; then +if test -n "$auto_cflags"; then if test "${enable_zsh_debug}" = yes; then if test -n "$GCC"; then CFLAGS="$CFLAGS -Wall -Wmissing-prototypes -ggdb" @@ -558,28 +553,6 @@ case "$host_os" in esac fp_PROG_CC_STDC -AC_MSG_CHECKING([whether to use prototypes]) -if test ."$ansi2knr" = .yes || test ."$ansi2knr" = .no; then - msg="(overridden) " -else - msg= - if test ."$fp_cv_prog_cc_stdc" = .no; then - ansi2knr=yes - else - ansi2knr=no - fi -fi -AH_TEMPLATE([PROTOTYPES], -[Define to 1 if ANSI function prototypes are usable.]) -if test "$ansi2knr" = yes; then - AC_MSG_RESULT(${msg}no) - U=_ -else - AC_MSG_RESULT(${msg}yes) - AC_DEFINE(PROTOTYPES) - U= -fi -AC_SUBST(U) AC_FUNC_ALLOCA dnl Check how to get `alloca'. @@ -647,18 +620,6 @@ case "$LC_PAPER" in esac AC_SUBST(PAPERSIZE) -AC_CHECK_PROGS([ANSI2KNR], [ansi2knr], [: ansi2knr]) - -if test x"$ansi2knr" = xyes && test x"$ANSI2KNR" = x": ansi2knr"; then - echo "----------" - echo "configure fatal error:" - echo "ansi2knr was specified (--enable-ansi2knr) but the program could not be found." - echo "Either remove the configure option if it is not required or build the ansi2knr" - echo "program before reconfiguring Zsh. The source code for ansi2knr is also" - echo "available in the GPL directory on Zsh distribution sites." - exit 1 -fi - dnl ------------------ dnl CHECK HEADER FILES dnl ------------------ -- cgit v1.2.3 From 7139d3b286bba5874548eb6faca25cf9bb90f7be Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 20 Mar 2024 23:40:23 +0100 Subject: 52783: allow for unset hash element --- ChangeLog | 2 ++ Src/Modules/hlgroup.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index df8caca42..157abcc9e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2024-03-20 Oliver Kiddle + * 52783: Src/Modules/hlgroup.c: allow for unset hash element + * unposted: configure.ac: remove reference to fp_PROG_CC_STDC macro which was removed in 52750 diff --git a/Src/Modules/hlgroup.c b/Src/Modules/hlgroup.c index 9c0aedcf8..9b656c24e 100644 --- a/Src/Modules/hlgroup.c +++ b/Src/Modules/hlgroup.c @@ -91,7 +91,8 @@ getgroup(const char *name, int sgr) if (!(v = getvalue(&vbuf, &var, 0)) || PM_TYPE(v->pm->node.flags) != PM_HASHED || !(hlg = v->pm->gsu.h->getfn(v->pm)) || - !(hn = gethashnode2(hlg, name))) + !(hn = gethashnode2(hlg, name)) || + (((Param) hn)->node.flags & PM_UNSET)) { pm->u.str = dupstring(""); pm->node.flags |= PM_UNSET; -- cgit v1.2.3 From 017738cd60743b21832e5ee66b849b5adea1a28d Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Thu, 21 Mar 2024 09:25:07 +0000 Subject: 52780: unneccessary returns in hlgroup --- ChangeLog | 4 ++++ Src/Modules/hlgroup.c | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 157abcc9e..4cb7ca634 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-03-21 Peter Stephenson + + * 52780: Src/Modules/hlgroup.c: remove unnecessary returns. + 2024-03-20 Oliver Kiddle * 52783: Src/Modules/hlgroup.c: allow for unset hash element diff --git a/Src/Modules/hlgroup.c b/Src/Modules/hlgroup.c index 9b656c24e..082762623 100644 --- a/Src/Modules/hlgroup.c +++ b/Src/Modules/hlgroup.c @@ -142,7 +142,7 @@ getpmesc(UNUSED(HashTable ht), const char *name) static void scanpmesc(UNUSED(HashTable ht), ScanFunc func, int flags) { - return scangroup(func, flags, 0); + scangroup(func, flags, 0); } /**/ @@ -156,7 +156,7 @@ getpmsgr(UNUSED(HashTable ht), const char *name) static void scanpmsgr(UNUSED(HashTable ht), ScanFunc func, int flags) { - return scangroup(func, flags, 1); + scangroup(func, flags, 1); } static struct paramdef partab[] = { -- cgit v1.2.3 From 5ba43e58c269100a6e3adcfc118ae93346ba0165 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 1 Apr 2024 22:29:07 -0700 Subject: 52781: HIST IGNORE_DUPS + HIST_REDUCE_BLANKS treats whitespace as significant --- ChangeLog | 5 +++++ Src/hashtable.c | 8 ++++++++ 2 files changed, 13 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 62b332aae..63034fb5a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-04-01 Bart Schaefer + + * 52781 (and typo fix): Src/hashtable.c: HIST IGNORE_DUPS treats + whitespace as significant when HIST_REDUCE_BLANKS is also set. + 2024-04-01 Oliver Kiddle * github #115: OKURA Masafumi: Completion/Unix/Command/_ruby: diff --git a/Src/hashtable.c b/Src/hashtable.c index 75b06c4ad..96675a393 100644 --- a/Src/hashtable.c +++ b/Src/hashtable.c @@ -1397,6 +1397,14 @@ histstrcmp(const char *str1, const char *str2) { while (inblank(*str1)) str1++; while (inblank(*str2)) str2++; + + /* If insignificant whitespace has already been eliminated, + * there is no reason to expend similar effort here. Also, + * this is more accurate in cases of quoted whitespace. + */ + if (isset(HISTREDUCEBLANKS)) + return strcmp(str1, str2); + while (*str1 && *str2) { if (inblank(*str1)) { if (!inblank(*str2)) -- cgit v1.2.3 From 76019f71742fab725011e4fd0402e941544cf5ab Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 1 Apr 2024 22:35:33 -0700 Subject: 52864: Change ${|var|...} to ${{var} ...}, limit local REPLY to ${|...} --- ChangeLog | 4 ++ Src/lex.c | 2 +- Src/subst.c | 140 +++++++++++++++++++++++++++++++++++---------------- Test/D10nofork.ztst | 52 ++++++++++++++++--- Test/V10private.ztst | 8 +-- 5 files changed, 150 insertions(+), 56 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 63034fb5a..9476c50ab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2024-04-01 Bart Schaefer + * 52864: Src/lex.c, Src/subst.c, Test/D10nofork.ztst, + Test/V10private.ztst: Change ${|var|...} to ${{var} ...}, + limit local REPLY behavior to ${|...}, update tests. + * 52781 (and typo fix): Src/hashtable.c: HIST IGNORE_DUPS treats whitespace as significant when HIST_REDUCE_BLANKS is also set. diff --git a/Src/lex.c b/Src/lex.c index 31b130b07..700af2da1 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -1423,7 +1423,7 @@ gettokstr(int c, int sub) if (lexstop) break; if (!cmdsubst && in_brace_param && act == LX2_STRING && - (c == '|' || c == Bar || inblank(c))) { + (c == '|' || c == Bar || c == '{' || c == Inbrace || inblank(c))) { cmdsubst = in_brace_param; cmdpush(CS_CURSH); } else if (in_pattern == 2 && c != '/') diff --git a/Src/subst.c b/Src/subst.c index 9d20a2d0e..f0d6c7a7b 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -1866,10 +1866,11 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * joining the array into a string (for compatibility with ksh/bash). */ int quoted_array_with_offset = 0; - /* Indicates ${|...;} */ - char *rplyvar = NULL; - /* Indicates ${ ... ;} */ - char *rplytmp = NULL; + /* + * Nofork substitution controls + */ + char *rplyvar = NULL; /* Indicates ${|...;} or ${{var} ...;} */ + char *rplytmp = NULL; /* Indicates ${ ... ;} */ *s++ = '\0'; /* @@ -1897,14 +1898,16 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * flags in parentheses, but also one ksh hack. */ if (c == Inbrace) { - /* The command string to be run by ${|...;} */ - char *cmdarg = NULL; + /* For processing nofork command substitution string */ + char *cmdarg = NULL, *endvar = NULL, inchar = *++s; + char *outbracep = s, sav = *s; + Param rplypm = NULL; size_t slen = 0; int trim = (!EMULATION(EMULATE_ZSH)) ? 2 : !qt; - inbrace = 1; - s++; - /* Short-path for the nofork command substitution ${|cmd;} + inbrace = 1; /* Outer scope boolean, see above */ + + /* Handling for nofork command substitution e.g. ${|cmd;} * See other comments about kludges for why this is here. * * The command string is extracted and executed, and the @@ -1913,48 +1916,77 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, * should not be part of command substitution in any case. * Use ${(U)${|cmd;}} as you would for ${(U)$(cmd;)}. */ - if (*s == '|' || *s == Bar || inblank(*s)) { - char *outbracep = s; - char sav = *s; + if (inchar == '|' || inchar == Bar || inblank(inchar)) { *s = Inbrace; - if (skipparens(Inbrace, Outbrace, &outbracep) == 0) { + if (skipparens(Inbrace, Outbrace, &outbracep) == 0) slen = outbracep - s - 1; - if ((*s = sav) != Bar) { - sav = *outbracep; - *outbracep = '\0'; - tokenize(s); - *outbracep = sav; + *s = sav; + if (inchar == '|') + inchar = Bar; /* Simplify later compares */ + } else if (inchar == '{' || inchar == Inbrace) { + *s = Inbrace; + if ((outbracep = itype_end(s+1, INAMESPC, 0))) { + if (*outbracep == Inbrack && + (outbracep = parse_subscript(++outbracep, 1, ']'))) + ++outbracep; + } + + /* If we reached the first close brace, find the last */ + if (outbracep && *outbracep == Outbrace) { + char outchar = inchar == Inbrace ? Outbrace : '}'; + endvar = outbracep++; + + /* Require space to avoid ${{var}} typo for ${${var}} */ + if (!inblank(*outbracep)) { + zerr("bad substitution"); + return NULL; } + + *endvar = '|'; /* Almost anything but braces/brackets */ + outbracep = s; + if (skipparens(Inbrace, outchar, &outbracep) == 0) + *endvar = Outbrace; + else { /* Never happens? */ + *endvar = outchar; + outbracep = endvar + 1; + } + slen = outbracep - s - 1; + if (inchar != Inbrace) + outbracep[-1] = Outbrace; + *s = sav; + inchar = Inbrace; /* Simplify later compares */ + } else { + zerr("bad substitution"); + return NULL; } } if (slen > 1) { char *outbracep = s + slen; + if (!itok(*s) || inblank(inchar)) { + /* This tokenize() is important */ + char sav = *outbracep; + *outbracep = '\0'; + tokenize(s); + *outbracep = sav; + } if (*outbracep == Outbrace) { - if ((rplyvar = itype_end(s+1, INAMESPC, 0))) { - if (*rplyvar == Inbrack && - (rplyvar = parse_subscript(++rplyvar, 1, ']'))) - ++rplyvar; - } - if (rplyvar == s+1 && *rplyvar == Bar) { - /* Is ${||...} a subtitution error or a syntax error? - zerr("bad substitution"); - return NULL; - */ + if (endvar == s+1) { + /* For consistency with ${} we allow ${{}...} */ rplyvar = NULL; } - if (rplyvar && *rplyvar == Bar) { - cmdarg = dupstrpfx(rplyvar+1, outbracep-rplyvar-1); - rplyvar = dupstrpfx(s+1,rplyvar-s-1); + if (endvar && *endvar == Outbrace) { + cmdarg = dupstrpfx(endvar+1, outbracep-endvar-1); + rplyvar = dupstrpfx(s+1,endvar-s-1); } else { cmdarg = dupstrpfx(s+1, outbracep-s-1); rplyvar = "REPLY"; } - if (inblank(*s)) { + if (inblank(inchar)) { /* - * Admittedly a hack. Take advantage of the enforced - * locality of REPLY and the semantics of $(level = locallevel; - /* if (rplyval) setsparam("REPLY", ztrdup(rplyval)); */ + if (inchar == Bar) { + /* rplyvar should be REPLY at this point, but create + * hardwired name anyway to expose any bugs elsewhere + */ + rplypm = createparam("REPLY", PM_LOCAL|PM_UNSET|PM_HIDE); + if (rplypm) /* Shouldn't createparam() do this? */ + rplypm->level = locallevel; + /* Future? Expose global value of $REPLY if any? */ + /* if (rplyval) setsparam("REPLY", ztrdup(rplyval)); */ + } else if (inblank(inchar)) { + rplypm = createparam(".zsh.cmdsubst", + PM_LOCAL|PM_UNSET|PM_HIDE| + PM_READONLY_SPECIAL); + if (rplypm) + rplypm->level = locallevel; + } + if (inchar != Inbrace && !rplypm) { + zerr("failed to create scope for command substitution"); + return NULL; + } } if (rplyvar && cmdarg && *cmdarg) { int obreaks = breaks; Eprog cmdprog; /* Execute the shell command */ + queue_signals(); untokenize(cmdarg); cmdprog = parse_string(cmdarg, 0); if (cmdprog) { + /* exec.c handles dont_queue_signals() */ execode(cmdprog, 1, 0, "cmdsubst"); cmdoutval = lastval; /* "return" behaves as if in a function */ @@ -2002,6 +2051,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, } } else /* parse error */ errflag |= ERRFLAG_ERROR; + if (rplypm) + rplypm->node.flags &= ~PM_READONLY_SPECIAL; if (rplytmp && !errflag) { int onoerrs = noerrs, rplylen; noerrs = 2; @@ -2017,15 +2068,16 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, } noerrs = onoerrs; if (rplylen >= 0) - setsparam("REPLY", metafy(cmdarg, rplylen, META_REALLOC)); + setsparam(rplyvar, metafy(cmdarg, rplylen, META_REALLOC)); } + unqueue_signals(); } if (rplytmp) unlink(rplytmp); if (rplyvar) { - if (strcmp(rplyvar, "REPLY") == 0) { - if ((val = dupstring(getsparam("REPLY")))) + if (inchar != Inbrace) { + if ((val = dupstring(getsparam(rplyvar)))) vunset = 0; else { vunset = 1; diff --git a/Test/D10nofork.ztst b/Test/D10nofork.ztst index fc6b84613..5bb10266f 100644 --- a/Test/D10nofork.ztst +++ b/Test/D10nofork.ztst @@ -14,6 +14,28 @@ 0:Basic substitution and REPLY scoping >INNER OUTER + reply=(x OUTER x) + purl ${{reply} reply=(\{ INNER \})} $reply +0:Basic substitution, brace quoting, and array result +>{ +>INNER +>} +>{ +>INNER +>} + + () { + setopt localoptions ignorebraces + purl ${{reply} reply=({ INNER })} $reply + } +0:Basic substitution, ignorebraces, and array result +>{ +>INNER +>} +>{ +>INNER +>} + purr ${| REPLY=first}:${| REPLY=second}:$REPLY 0:re-scoping of REPLY in one statement >first:second:OUTER @@ -229,7 +251,7 @@ F:Why not use this error in the previous case as well? >26 unset reply - purl ${|reply| reply=(1 2 ${| REPLY=3 } 4) } + purl ${{reply} reply=(1 2 ${| REPLY=3 } 4) } typeset -p reply 0:array behavior with global assignment >1 @@ -315,7 +337,7 @@ F:status of "print" should hide return unset zz outer=GLOBAL - purr "${|zz| + purr "${{zz} local outer=LOCAL zz=NONLOCAL } $outer $?" @@ -440,7 +462,8 @@ F:must do this before evaluating the next test block 0:ignored braces, part 1 >buried} - purr "${ purr ${REPLY:-buried}}}" + # Global $REPLY still set from earlier test + purr "${ purr ${REPLY:+buried}}}" 0:ignored braces, part 2 >buried >} @@ -453,6 +476,7 @@ F:must do this before evaluating the next test block 1:ignored braces, part 4 ?(eval):3: parse error near `}' + unsetopt ignorebraces # "break" blocks function calls in outer loop # Could use print, but that might get fixed repeat 3 do purr ${ @@ -467,11 +491,25 @@ F:must do this before evaluating the next test block ?1 ?2 - print -u $ZTST_fd ${ZTST_testname}: TEST COMPLETE -0:make sure we got to the end -F:some tests might silently break the test harness + # Cannot "purr": break skips pending function calls + # Use "repeat" to avoid infinite loop on failure + repeat 3 do; echo ${|REPLY=x; break }; done + repeat 3 do; echo ${{x} x=y; break }; done + repeat 3 do; echo ${ echo z; break }; done +0:break after assignment completes the assignment +>x +>y +>z + + # Subshell because error exits + ( purr ${ echo ${unset?oops} } ) +1:error handling (without crashing) +*?*unset: oops + + purr ${ .zsh.cmdsubst=error } +1:reserved parameter name (without crashing) +*?*.zsh.cmdsubst: can't modify read-only parameter %clean unfunction purr purl - unsetopt ignorebraces diff --git a/Test/V10private.ztst b/Test/V10private.ztst index ed51316f3..26004a2dc 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -497,7 +497,7 @@ F:Better if caught in checkclobberparam() but exec.c doesn't know scope () { private z=outer print ${(t)z} $z - print ${| REPLY=${|z| z=nofork} } + print ${| REPLY=${{z} z=nofork} } print ${(t)z} $z } 0:nofork may write to private in calling function @@ -518,9 +518,9 @@ F:Better if caught in checkclobberparam() but exec.c doesn't know scope () { private z=outer print ${(t)z} $z - print ${|z| + print ${{z} private q - z=${|q| q=nofork} + z=${{q} q=nofork} } print ${(t)z} $z } @@ -533,7 +533,7 @@ F:Better if caught in checkclobberparam() but exec.c doesn't know scope print ${| () { REPLY="{$q}" } } - print ${|q| + print ${{q} () { q=nofork } } } -- cgit v1.2.3 From a66e92918568881af110a3e2e3018b317c054e4a Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Sat, 6 Apr 2024 00:19:53 +0200 Subject: 52878: Fix ${foo:^bar} where bar is an associative array --- ChangeLog | 5 +++++ Src/subst.c | 3 +++ 2 files changed, 8 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 643a33dac..df3f9b73c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-04-07 Mikael Magnusson + + * 52878: Src/subst.c: Fix ${foo:^bar} where bar is an associative + array + 2024-04-01 Bart Schaefer * 52865: Doc/Zsh/expn.yo, Doc/Zsh/params.yo, Etc/FAQ.yo: diff --git a/Src/subst.c b/Src/subst.c index f0d6c7a7b..a079672df 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -3474,6 +3474,9 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags, } else { char *sval; zip = getaparam(s); + if (!zip) { + zip = gethparam(s); + } if (!zip) { sval = getsparam(s); if (sval) -- cgit v1.2.3 From f6e005a9ef55b1fbc5291729faa2ab46bf3d9e8f Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 13 May 2024 11:26:06 +0100 Subject: Ooops, missed the actualy patch from the foregoing... --- Src/Modules/zftp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'Src') diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index 0c26828fd..b60e5bf31 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -3147,6 +3147,7 @@ zftp_cleanup(void) lastmsg = NULL; zfunsetparam("ZFTP_SESSION"); freelinklist(zfsessions, (FreeFunc) freesession); + zfsessions = NULL; zfree(zfstatusp, sizeof(int)*zfsesscnt); zfstatusp = NULL; } -- 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') 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 da733f5df691d01caff0a01addba234aad1b6864 Mon Sep 17 00:00:00 2001 From: Philippe Altherr Date: Fri, 28 Jun 2024 21:05:42 -0700 Subject: 52977: ERR_EXIT/ERR_RETURN are respected by the final command in && / || lists --- Src/exec.c | 1 + Test/C03traps.ztst | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'Src') diff --git a/Src/exec.c b/Src/exec.c index e955e85df..a473938ec 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1568,6 +1568,7 @@ execlist(Estate state, int dont_change_job, int exiting) } state->pc = next; code = *state->pc++; + noerrexit = oldnoerrexit; } state->pc--; sublist_done: diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst index de57765a0..87b7fd1f7 100644 --- a/Test/C03traps.ztst +++ b/Test/C03traps.ztst @@ -995,6 +995,33 @@ F:Must be tested with a top-level script rather than source or function ?loop 0 ?loop 1 + ( set -e; true && {false; echo NOT REACHED} ) + ( trap "print Trapped!" ERR; true && {false} ) + ( trap "print Trapped!" ERR; true && if true; then false; fi ) + ( trap "print Trapped!" ERR; true && {false} always {true} ) + ( true && (set -e; false; echo NOT REACHED) ) + ( true && (trap "print Trapped!" ERR; false) ) + ( true && { set -e; false; echo NOT REACHED } ) + ( true && { trap "print Trapped!" ERR; false } ) + ( set -e; true && (false; echo one) || echo two ) + ( set -e; true && { false; echo one; } || echo two ) +0:ERR_EXIT is triggered by last command in an AND-OR list +>Trapped! +>Trapped! +>Trapped! +>Trapped! +>Trapped! +>one +>one + + ( set -o ERR_RETURN; f() { false; echo NOT REACHED; }; f || true; echo OK ) + ( set -o ERR_RETURN; f() { true && false; echo NOT REACHED; }; f || true; echo OK ) + ( set -o ERR_RETURN; f() { true && { false }; echo NOT REACHED; }; f || true; echo OK ) +0:ERR_RETURN is triggered in function calls on the left of an AND-OR +>OK +>OK +>OK + if zmodload zsh/system 2>/dev/null; then ( trap 'echo TERM; exit 2' TERM -- cgit v1.2.3 From 2a54de167586c3b8b03019b5c52021c608bd310e Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Thu, 4 Jul 2024 11:51:09 +0900 Subject: 52985: avoid adding original param to restorelist --- ChangeLog | 4 ++++ Src/exec.c | 7 +++---- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 36fae8a51..d173df87e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-07-04 Jun-ichi Takimoto + + * 52985: Src/exec.c: avoid adding original param to restorelist + 2024-06-24 Jun-ichi Takimoto * 52968: Doc/Makefile.in: use pdfroff to create intro.pdf diff --git a/Src/exec.c b/Src/exec.c index a473938ec..097e0b368 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -4409,7 +4409,7 @@ save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p) while (wc_code(ac = *pc) == WC_ASSIGN) { s = ecrawstr(state->prog, pc + 1, NULL); if ((pm = (Param) paramtab->getnode(paramtab, s))) { - Param tpm; + Param tpm = NULL; if (pm->env) delenv(pm); if (!(pm->node.flags & PM_SPECIAL)) { @@ -4426,7 +4426,6 @@ save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p) tpm = (Param) zshcalloc(sizeof *tpm); tpm->node.nam = ztrdup(pm->node.nam); copyparam(tpm, pm, 0); - pm = tpm; } else if (!(pm->node.flags & PM_READONLY) && (unset(RESTRICTED) || !(pm->node.flags & PM_RESTRICTED))) { /* @@ -4437,10 +4436,10 @@ save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p) tpm = (Param) hcalloc(sizeof *tpm); tpm->node.nam = pm->node.nam; copyparam(tpm, pm, 1); - pm = tpm; } addlinknode(*remove_p, dupstring(s)); - addlinknode(*restore_p, pm); + if (tpm) + addlinknode(*restore_p, tpm); } else addlinknode(*remove_p, dupstring(s)); -- cgit v1.2.3 From 0bb140f9911851e9712dba311925f9c9ab521fd2 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 28 Jul 2024 20:33:07 +0100 Subject: 52999: import OLDPWD from environment if set --- ChangeLog | 5 +++++ Src/init.c | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 5c6270d05..22ccce77a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-07-28 Peter Stephenson + + * 52999: Franklin Yu (adapted): Src/init.c: Import OLDPWD from + the environment if set. + 2024-07-04 Jun-ichi Takimoto * 52985: Src/exec.c: avoid adding original param to restorelist diff --git a/Src/init.c b/Src/init.c index ec21521b1..0aecb5db9 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1245,7 +1245,11 @@ setupvals(char *cmd, char *runscript, char *zsh_name) pwd = metafy(zgetcwd(), -1, META_DUP); } - oldpwd = ztrdup(pwd); /* initialize `OLDPWD' = `PWD' */ + oldpwd = zgetenv("OLDPWD"); + if (oldpwd == NULL) + oldpwd = ztrdup(pwd); /* initialize `OLDPWD' = `PWD' */ + else + oldpwd = ztrdup(oldpwd); inittyptab(); /* initialize the ztypes table */ initlextabs(); /* initialize lexing tables */ -- cgit v1.2.3 From 4616ea398a5c1b04299dff9d25a8bc264c685540 Mon Sep 17 00:00:00 2001 From: Philippe Altherr Date: Sun, 4 Aug 2024 19:12:32 -0700 Subject: 53005: off-by-one error when resetting signals on subshell entrance --- ChangeLog | 5 +++++ Src/exec.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index f47690476..41204dfd6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-08-04 Bart Schaefer + + * Philippe Altherr: 53005: Src/exec.c: off-by-one error when + resetting signals on subshell entrance + 2024-08-03 Eric Cook * 52989: Completion/Unix/Command/_rsync, diff --git a/Src/exec.c b/Src/exec.c index 097e0b368..00278ac50 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1089,7 +1089,7 @@ entersubsh(int flags, struct entersubsh_ret *retp) int i, sig, monitor, job_control_ok; if (!(flags & ESUB_KEEPTRAP)) - for (sig = 0; sig < SIGCOUNT; sig++) + for (sig = 0; sig <= SIGCOUNT; sig++) if (!(sigtrapped[sig] & ZSIG_FUNC) && !(isset(POSIXTRAPS) && (sigtrapped[sig] & ZSIG_IGNORED))) unsettrap(sig); @@ -1203,7 +1203,7 @@ entersubsh(int flags, struct entersubsh_ret *retp) * Start loop at 1 because 0 is SIGEXIT */ if (intrap) - for (sig = 1; sig < SIGCOUNT; sig++) + for (sig = 1; sig <= SIGCOUNT; sig++) if (sigtrapped[sig] && sigtrapped[sig] != ZSIG_IGNORED) signal_unblock(signal_mask(sig)); if (!job_control_ok) -- cgit v1.2.3 From 65da4674410abac23e41f89f29f6613d74858854 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 5 Aug 2024 15:49:15 -0700 Subject: 53023: fix memory leak of unset private parameter --- ChangeLog | 4 ++++ Src/params.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 41204dfd6..56f6d0f35 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-08-05 Bart Schaefer + + * 53023: Src/params.c: fix memory leak of unset private parameter + 2024-08-04 Bart Schaefer * Philippe Altherr: 53005: Src/exec.c: off-by-one error when diff --git a/Src/params.c b/Src/params.c index f65aa1e80..83bdb785d 100644 --- a/Src/params.c +++ b/Src/params.c @@ -3790,7 +3790,7 @@ unsetparam_pm(Param pm, int altflag, int exp) altremove = NULL; pm->node.flags &= ~PM_DECLARED; /* like ksh, not like bash */ - if (!(pm->node.flags & PM_UNSET)) + if (!(pm->node.flags & PM_UNSET) || (pm->node.flags & PM_REMOVABLE)) pm->gsu.s->unsetfn(pm, exp); if (pm->env) delenv(pm); -- cgit v1.2.3 From 72751bfe1f9e37145c12e244ebb1729c27aff901 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 5 Aug 2024 15:59:48 -0700 Subject: 53025: fix memory leaks and pointer errors upon named reference self-reference --- ChangeLog | 3 +++ Src/loop.c | 2 +- Src/params.c | 12 +++++++----- 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 56f6d0f35..2fa2f51a3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-08-05 Bart Schaefer + * 53025: Src/loop.c, Src/params.c: fix two memory leaks and two + pointer errors when encountering a named reference self-reference + * 53023: Src/params.c: fix memory leak of unset private parameter 2024-08-04 Bart Schaefer diff --git a/Src/loop.c b/Src/loop.c index 0f3847541..84dc66476 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -165,7 +165,7 @@ execfor(Estate state, int do_exec) fprintf(xtrerr, "%s=%s\n", name, str); fflush(xtrerr); } - setloopvar(name, ztrdup(str)); + setloopvar(name, str); count++; } if (!count) diff --git a/Src/params.c b/Src/params.c index 83bdb785d..f143a790f 100644 --- a/Src/params.c +++ b/Src/params.c @@ -2811,9 +2811,10 @@ assignstrvalue(Value v, char *val, int flags) break; } setscope(v->pm); - if ((!v->pm->env && !(v->pm->node.flags & PM_EXPORTED) && - !(isset(ALLEXPORT) && !(v->pm->node.flags & PM_HASHELEM))) || - (v->pm->node.flags & PM_ARRAY) || v->pm->ename) + if (errflag || + ((!v->pm->env && !(v->pm->node.flags & PM_EXPORTED) && + !(isset(ALLEXPORT) && !(v->pm->node.flags & PM_HASHELEM))) || + (v->pm->node.flags & PM_ARRAY) || v->pm->ename)) return; export_param(v->pm); } @@ -6330,9 +6331,10 @@ setloopvar(char *name, char *value) pm->node.flags &= ~PM_UNSET; pm->node.flags |= PM_NEWREF; setscope(pm); - pm->node.flags &= ~PM_NEWREF; + if (!errflag) + pm->node.flags &= ~PM_NEWREF; } else - setsparam(name, value); + setsparam(name, ztrdup(value)); } /**/ -- cgit v1.2.3 From b2f24ff0d2a9631f331c1841491ce60e69c20ca6 Mon Sep 17 00:00:00 2001 From: "Jun. T" Date: Tue, 6 Aug 2024 14:37:39 -0700 Subject: 53026: fix failure to free old value when setting new value of reference --- ChangeLog | 5 +++++ Src/params.c | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 2fa2f51a3..667f17177 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-08-06 Bart Schaefer + + * Jun.T: 53026: Src/params.c: fix failure to free old value when + setting new value of reference + 2024-08-05 Bart Schaefer * 53025: Src/loop.c, Src/params.c: fix two memory leaks and two diff --git a/Src/params.c b/Src/params.c index f143a790f..acd577527 100644 --- a/Src/params.c +++ b/Src/params.c @@ -482,7 +482,8 @@ static initparam argvparam_pm = IPDEF9("", &pparams, NULL, \ #define GETREFNAME(PM) (((PM)->node.flags & PM_SPECIAL) ? \ (PM)->gsu.s->getfn(PM) : (PM)->u.str) #define SETREFNAME(PM,S) (((PM)->node.flags & PM_SPECIAL) ? \ - (PM)->gsu.s->setfn(PM,(S)) : ((PM)->u.str = (S))) + (PM)->gsu.s->setfn(PM,(S)) : \ + (zsfree((PM)->u.str), (PM)->u.str = (S))) static Param argvparam; -- cgit v1.2.3 From 5977d3cdd4b241ab7afff868ed58afc00b45c424 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Thu, 15 Aug 2024 18:06:19 -0700 Subject: unposted: fix parsing of Bang token in value side of array element assignment --- ChangeLog | 5 +++++ Src/lex.c | 3 ++- Test/A06assign.ztst | 7 +++++++ 3 files changed, 14 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 1ed2100f1..eec96a494 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-08-15 Bart Schaefer + + * unposted (see 53034): Src/lex.c, Test/A06assign.ztst: fix parsing + of Bang token in value side of array element assignment + 2024-08-13 Eric Cook * 53031: Christian Heusel: Completion/Unix/Command/_git: add diff --git a/Src/lex.c b/Src/lex.c index 700af2da1..efbb62b66 100644 --- a/Src/lex.c +++ b/Src/lex.c @@ -1402,10 +1402,11 @@ gettokstr(int c, int sub) /* * Same logic as Dash, for ! to perform negation in range. */ - if (seen_brct) + if (seen_brct && brct) c = Bang; else c = '!'; + break; case LX2_OTHER: if (in_brace_param) { if (c == '/') { diff --git a/Test/A06assign.ztst b/Test/A06assign.ztst index 3eff5331a..9f779b9a8 100644 --- a/Test/A06assign.ztst +++ b/Test/A06assign.ztst @@ -743,3 +743,10 @@ print $a 0:overwrite [2] character (string: "") with "xx" >xx + + ( sleep 1 & + x[1]=$! + typeset -p x + ) +0:regression workers/53033: assigning $! to array element +*>typeset -g -a x=\( <-> \) -- cgit v1.2.3 From 58bda5913007f53c91ae60cd22483dd222ea5618 Mon Sep 17 00:00:00 2001 From: Clinton Bunch Date: Fri, 30 Aug 2024 08:06:06 -0500 Subject: 53056: new zsh/random module defining an SRANDOM parameter and zrand_float() and zrand_int() math functions --- ChangeLog | 6 + Completion/Zsh/Type/_module_math_func | 2 +- Doc/Makefile.in | 2 +- Doc/Zsh/mod_random.yo | 56 ++++++ Src/Modules/random.c | 322 ++++++++++++++++++++++++++++++++++ Src/Modules/random.mdd | 7 + Src/Modules/random_real.c | 213 ++++++++++++++++++++++ configure.ac | 2 + 8 files changed, 608 insertions(+), 2 deletions(-) create mode 100644 Doc/Zsh/mod_random.yo create mode 100644 Src/Modules/random.c create mode 100644 Src/Modules/random.mdd create mode 100644 Src/Modules/random_real.c (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 753353153..51a859143 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2024-08-31 Oliver Kiddle + * Clinton Bunch: 53056: Completion/Zsh/Type/_module_math_func, + Doc/Makefile.in, Doc/Zsh/mod_random.yo, Src/Modules/random.c, + Src/Modules/random.mdd, Src/Modules/random_real.c, configure.ac: + new zsh/random module defining an SRANDOM parameter and + zrand_float() and zrand_int() math functions + * github #120: Semnodime: Completion/Unix/Command/_git: update _git to reflect `--recursive` being an alias diff --git a/Completion/Zsh/Type/_module_math_func b/Completion/Zsh/Type/_module_math_func index 5044bdf4c..e92b78b71 100644 --- a/Completion/Zsh/Type/_module_math_func +++ b/Completion/Zsh/Type/_module_math_func @@ -2,7 +2,7 @@ local mod local -a funcs alts -local -a modules=( example mathfunc system ) +local -a modules=( example mathfunc system random ) for mod in $modules; do funcs=( ${${${(f)"$(zmodload -Fl zsh/$mod 2>/dev/null)"}:#^+f:*}##+f:} ) diff --git a/Doc/Makefile.in b/Doc/Makefile.in index 401fb942b..fa2a336ad 100644 --- a/Doc/Makefile.in +++ b/Doc/Makefile.in @@ -68,7 +68,7 @@ Zsh/mod_hlgroup.yo Zsh/mod_langinfo.yo \ Zsh/mod_ksh93.yo Zsh/mod_mapfile.yo Zsh/mod_mathfunc.yo \ Zsh/mod_nearcolor.yo Zsh/mod_newuser.yo \ Zsh/mod_parameter.yo Zsh/mod_pcre.yo Zsh/mod_private.yo \ -Zsh/mod_regex.yo Zsh/mod_sched.yo Zsh/mod_socket.yo \ +Zsh/mod_regex.yo Zsh/mod_random.yo Zsh/mod_sched.yo Zsh/mod_socket.yo \ Zsh/mod_stat.yo Zsh/mod_system.yo Zsh/mod_tcp.yo \ Zsh/mod_termcap.yo Zsh/mod_terminfo.yo \ Zsh/mod_watch.yo \ diff --git a/Doc/Zsh/mod_random.yo b/Doc/Zsh/mod_random.yo new file mode 100644 index 000000000..d2892bf79 --- /dev/null +++ b/Doc/Zsh/mod_random.yo @@ -0,0 +1,56 @@ +COMMENT(!MOD!zsh/random +Some High-quality randomness parameters and functions. +!MOD!) +The tt(zsh/random) module gets random data from the kernel random pool. If no +kernel random pool can be found, the module will not load. + +subsect(Parameters) + +startitem() +vindex(SRANDOM) +item(tt(SRANDOM)) ( +A random positive 32-bit integer between 0 and 4,294,967,295. This parameter +is read-only. The name was chosen for compatibility with Bash and to +distinguish it from tt(RANDOM) which has a documented repeatable behavior. +) +enditem() + +subsect(Math Functions) + +startitem() +item(tt(zrand_float+LPAR()RPAR())) ( +Returns a random floating point number between 0 and 1 inclusive. +) +enditem() + +startitem() +item(tt(zrand_int)+LPAR()tt(upper), tt(lower), tt(inclusive)RPAR()) ( +Returns a random integer between tt(lower) and tt(upper). All parameters are +optional. If none are specified it is equivalent to +tt(SRANDOM). + +tt(upper) is the upper bound on the resultant number and defaults to +4,294,967,295. + +tt(lower) is the lower bound and defaults to 0. + +The defaults of these two arguments are also the maximum and minimum to which +either can be set. + +tt(inclusive) is a flag that controls whether the result is ever equal to +tt(upper). By default it is not. If this argument is set to a non-zero value +then it may be. + +This is to facilitate a construct like tt($a[zrand_int($#a)+1]) rather +than tt($a[zrand_int+LPAR()$#a-1+RPAR()+1]). +For example, if $#a is 16, you would use tt(zrand_int+LPAR()16RPAR()) which has +16 possible return values 0-15. Because the function can return zero, in order +to use it as an array index from 1-16 you need to add one. It would +be an array index range error for it to also potentially return 16 ($#a). You +could, however, use the construct tt(zrand_int+LPAR()16,1,1+RPAR()) instead of +adding 1 to achieve the same result, but it is more verbose. + +Most statistics algorithms seem to also expect 0 to tt(upper)-1, so this was +deemed the most commonly desired case and chosen as the default. +) +enditem() diff --git a/Src/Modules/random.c b/Src/Modules/random.c new file mode 100644 index 000000000..a153d8f64 --- /dev/null +++ b/Src/Modules/random.c @@ -0,0 +1,322 @@ +/* + * random.c - module to access kernel random sources. + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 2022 Clinton Bunch + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Clinton Bunch or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Clinton Bunch and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Clinton Bunch and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Clinton Bunch and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "random.mdh" + +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_SYS_RANDOM_H +#include +#endif + +/* Simplify select URANDOM specific code */ +#if !defined(HAVE_ARC4RANDOM_BUF) && !defined(HAVE_GETRANDOM) +#define USE_URANDOM +#endif + +/* buffer to pre-load integers for SRANDOM to lessen the context switches */ +static uint32_t rand_buff[8]; +static int buf_cnt = -1; + +#ifdef USE_URANDOM +/* File descriptor for /dev/urandom */ +int randfd = -1; +#endif /* USE_URANDOM */ + +static zlong get_srandom(UNUSED(Param p)); + +/**/ +ssize_t +getrandom_buffer(void *buf, size_t len) +{ + ssize_t ret; + size_t val = 0; + uint8_t *bufptr = buf; + + do { + errno = 0; +#ifdef HAVE_ARC4RANDOM_BUF + arc4random_buf(buf,len); + ret = len; +#elif defined(HAVE_GETRANDOM) + ret=getrandom(bufptr,(len - val),0); +#else + ret=read(randfd,bufptr,(len - val)); +#endif + if (ret < 0) { + if (errno != EINTR || errflag || retflag || breaks || contflag) { + zwarn("Unable to get random data: %e.", errno); + return -1; + } + } + bufptr += ret; + val += ret; + } while (ret < len); + return ret; +} + +/* + * Generate count number of random 32-bit integers between 0 and max-1 + * Got this algorithm from + *https://lemire.me/blog/2016/06/30/fast-random-shuffling/ + * adapting the public domain code. + * + */ + +/**/ +void +get_bound_random_buffer(uint32_t *buffer, size_t count, uint32_t max) +{ + uint64_t multi_result; + uint32_t threshold; + uint32_t leftover; + + size_t i; /* loop counter */ + + getrandom_buffer((void*) buffer, count*sizeof(uint32_t)); + if (max == UINT32_MAX) + return; + + for(i=0;i> 32; + } +} + +/* + * Provides for the SRANDOM parameter and returns an unsigned 32-bit random + * integer. + */ + +/**/ +static zlong +get_srandom(UNUSED(Param pm)) { + + if(buf_cnt <= 0) { + getrandom_buffer((void*) rand_buff,sizeof(rand_buff)); + buf_cnt=sizeof(rand_buff)/sizeof(rand_buff[0]); + } + return rand_buff[--buf_cnt]; +} + +/* + * Implements math function zrand_int takes 0 to 3 arguments an upper bound, + * a lower bound and a flag as to whether the range is inclusive or not. The + * default is exclusive. If neither upper or lower is specified this is no + * different than SRANDOM. + */ + +/**/ +static mnumber +math_zrand_int(UNUSED(char *name), int argc, mnumber *argv, UNUSED(int id)) +{ + mnumber ret; + uint32_t i; + zlong lower=0, upper=UINT32_MAX,incl=0, diff; + + ret.type = MN_INTEGER; + + switch (argc) { + case 0: ret.u.l=get_srandom(NULL); + return ret; + break; + case 3: incl = (argv[2].u.l != 0)?1:0; + case 2: lower = argv[1].u.l; + case 1: upper = argv[0].u.l; + default: diff = upper-lower+incl; + } + + if (lower < 0 || lower >= UINT32_MAX) { + zwarn("Lower bound (%z) out of range: 0-4294967295",lower); + } else if (upper < lower) { + zwarn("Upper bound (%z) must be greater than Lower Bound (%z)",upper,lower); + } else if (upper < 0 || upper >= UINT32_MAX) { + zwarn("Upper bound (%z) out of range: 0-4294967295",upper); + } + + if ( diff == 0 ) { + ret.u.l=upper; /* still not convinced this shouldn't be an error. */ + } else { + get_bound_random_buffer(&i,1,(uint32_t) diff); + ret.u.l=i+lower; + } + return ret; +} + +/* + * Implements the mathfunc zrand_float and returns a random floating-point + * number between 0 and 1. + * + */ + +/**/ +static mnumber +math_zrand_float(UNUSED(char *name), UNUSED(int argc), UNUSED(mnumber *argv), + UNUSED(int id)) +{ + mnumber ret; + double r; + + r = random_real(); + if (r < 0) { + zwarnnam(name, "Failed to get sufficient random data."); + } + ret.type = MN_FLOAT; + ret.u.d = r; + + return ret; +} + +static const struct gsu_integer srandom_gsu = +{ get_srandom, nullintsetfn, stdunsetfn }; + +static struct paramdef patab[] = { + {"SRANDOM", PM_INTEGER | PM_READONLY_SPECIAL | PM_HIDEVAL, NULL, + &srandom_gsu, NULL, NULL, NULL}, +}; + +static struct mathfunc mftab[] = { + NUMMATHFUNC("zrand_float", math_zrand_float, 0, 0, 0), + NUMMATHFUNC("zrand_int", math_zrand_int, 0, 3, 0), +}; + +static struct features module_features = { + NULL, 0, + NULL, 0, + mftab, sizeof(mftab)/sizeof(*mftab), + patab, sizeof(patab)/sizeof(*patab), + 0 +}; + +/**/ +int +setup_(UNUSED(Module m)) +{ +#ifdef USE_URANDOM + /* Check for the existence of /dev/urandom */ + + struct stat st; + + errno=0; + if (stat("/dev/urandom",&st) < 0) { + zwarn("Error getting kernel random pool: %e.", errno); + return 1; + } + + errno=0; + if (!(S_ISCHR(st.st_mode)) ) { + zwarn("Error getting kernel random pool: %e.", errno); + return 1; + } +#endif /* USE_URANDOM */ + return 0; +} + +/**/ +int +features_(Module m, char ***features) +{ + *features = featuresarray(m, &module_features); + return 0; +} + +/**/ +int +enables_(Module m, int **enables) +{ + return handlefeatures(m, &module_features, enables); +} + +/**/ +int +boot_(UNUSED(Module m)) +{ +#ifdef USE_URANDOM + /* + * Open /dev/urandom. Here because of a weird issue on HP-UX 11.31 + * When opening in setup_ open returned 0. In boot_, it returns + * an unused file descriptor. Decided against ifdef HPUX as it works + * here just as well for other platforms. + * + */ + + int tmpfd=-1; + + errno=0; + if ((tmpfd = open("/dev/urandom", O_RDONLY)) < 0) { + zwarn("Could not access kernel random pool: %e.",errno); + return 1; + } + randfd = movefd(tmpfd); + addmodulefd(randfd,FDT_MODULE); + if (randfd < 0) { + zwarn("Could not access kernel random pool."); + return 1; + } +#endif /* USE_URANDOM */ + return 0; +} + +/**/ +int +cleanup_(Module m) +{ + return setfeatureenables(m, &module_features, NULL); +} + +/**/ +int +finish_(UNUSED(Module m)) +{ +#ifdef USE_URANDOM + if (randfd >= 0) + zclose(randfd); +#endif /* USE_URANDOM */ + return 0; +} diff --git a/Src/Modules/random.mdd b/Src/Modules/random.mdd new file mode 100644 index 000000000..7a75f29ff --- /dev/null +++ b/Src/Modules/random.mdd @@ -0,0 +1,7 @@ +name=zsh/random +link=either +load=yes + +autofeatures="p:SRANDOM f:zrand_float f:zrand_int" + +objects="random.o random_real.o" diff --git a/Src/Modules/random_real.c b/Src/Modules/random_real.c new file mode 100644 index 000000000..4a8fcae19 --- /dev/null +++ b/Src/Modules/random_real.c @@ -0,0 +1,213 @@ +/* This file contains code under different copyrights separated by */ +/* ====@@@@@=== */ + +/* + * random_real.c - module to access kernel random sources. + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 2022 Clinton Bunch + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Clinton Bunch or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Clinton Bunch and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Clinton Bunch and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Clinton Bunch and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "random.mdh" + +#include +#include +#include + + +/* Count the number of leading zeros, hopefully in gcc/clang by HW + * instruction */ +#if defined(__GNUC__) || defined(__clang__) +#define clz64(x) __builtin_clzll(x) +#else +#define clz64(x) _zclz64(x) + +/**/ +int +_zclz64(uint64_t x) { + int n = 0; + + if (x == 0) + return 64; + + if (!(x & 0xFFFFFFFF00000000ull)) { + n+=32; + x<<=32; + } + if (!(x & 0xFFFF000000000000ull)) { + n+=16; + x<<=16; + } + if (!(x & 0xFF00000000000000ull)) { + n+=8; + x<<=8; + } + if (!(x & 0xF000000000000000ull)) { + n+=4; + x<<=4; + } + if (!(x & 0xC000000000000000ull)) { + n+=2; + x<<=1; + } + if (!(x & 0x8000000000000000ull)) { + n+=1; + } + return n; +} +#endif /* __GNU_C__ or __clang__ */ + +/**/ +uint64_t +random_64bit(void) { + uint64_t r; + + if(getrandom_buffer(&r,sizeof(r)) < 0) { + zwarn("zsh/random: Can't get sufficient random data."); + return 1; /* 0 will cause loop */ + } + + return r; +} + +/* ====@@@@@=== */ +/* Following code is under the below copyright, despite changes for error + * handling and removing GCCisms */ + +/*- + * Copyright (c) 2014 Taylor R. Campbell + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Uniform random floats: How to generate a double-precision + * floating-point numbers in [0, 1] uniformly at random given a uniform + * random source of bits. + * + * See + * for explanation. + * + * Updated 2015-02-22 to replace ldexp(x, ) by x * ldexp(1, + * ), since glibc and NetBSD libm both have slow software + * bit-twiddling implementations of ldexp, but GCC can constant-fold + * the latter. + */ + +/* + * random_real: Generate a stream of bits uniformly at random and + * interpret it as the fractional part of the binary expansion of a + * number in [0, 1], 0.00001010011111010100...; then round it. + */ + +/**/ +double +random_real(void) +{ + int exponent = 0; + uint64_t significand = 0; + uint64_t r = 0; + unsigned shift; + + /* + * Read zeros into the exponent until we hit a one; the rest + * will go into the significand. + */ + while (significand == 0) { + exponent -= 64; + + /* Get random_64bit and check for error */ + errno = 0; + significand = random_64bit(); + if (errno) + return -1; + /* + * If the exponent falls below -1074 = emin + 1 - p, + * the exponent of the smallest subnormal, we are + * guaranteed the result will be rounded to zero. This + * case is so unlikely it will happen in realistic + * terms only if random_64bit is broken. + */ + if (exponent < -1074) + return 0; + } + + /* + * There is a 1 somewhere in significand, not necessarily in + * the most significant position. If there are leading zeros, + * shift them into the exponent and refill the less-significant + * bits of the significand. Can't predict one way or another + * whether there are leading zeros: there's a fifty-fifty + * chance, if random_64bit is uniformly distributed. + */ + shift = clz64(significand); + if (shift != 0) { + + errno = 0; + r = random_64bit(); + if (errno) + return -1; + + exponent -= shift; + significand <<= shift; + significand |= (r >> (64 - shift)); + } + + /* + * Set the sticky bit, since there is almost surely another 1 + * in the bit stream. Otherwise, we might round what looks + * like a tie to even when, almost surely, were we to look + * further in the bit stream, there would be a 1 breaking the + * tie. + */ + significand |= 1; + + /* + * Finally, convert to double (rounding) and scale by + * 2^exponent. + */ + return ldexp((double)significand, exponent); +} +/* ====@@@@@=== */ diff --git a/configure.ac b/configure.ac index 78621042d..a88101f2b 100644 --- a/configure.ac +++ b/configure.ac @@ -636,6 +636,7 @@ fi AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \ termios.h sys/param.h sys/filio.h string.h memory.h \ limits.h fcntl.h libc.h sys/utsname.h sys/resource.h \ + sys/random.h \ locale.h errno.h stdio.h stdarg.h varargs.h stdlib.h \ unistd.h sys/capability.h \ utmp.h utmpx.h sys/types.h pwd.h grp.h poll.h sys/mman.h \ @@ -1292,6 +1293,7 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \ cygwin_conv_path \ nanosleep \ srand_deterministic \ + getrandom arc4random_buf \ setutxent getutxent endutxent getutent) AC_FUNC_STRCOLL -- cgit v1.2.3 From 6b9704e2c4e4c8524137a9c15bf9b166a975f3eb Mon Sep 17 00:00:00 2001 From: Clinton Bunch Date: Sat, 31 Aug 2024 09:10:12 -0500 Subject: 53060: silence build warnings --- ChangeLog | 3 +++ Doc/Zsh/mod_random.yo | 2 +- Src/Modules/random.c | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 51a859143..7e181e959 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-08-31 Oliver Kiddle + * Clinton Bunch: 53060: Doc/Zsh/mod_random.yo, + Src/Modules/random.c: silence build warnings + * Clinton Bunch: 53056: Completion/Zsh/Type/_module_math_func, Doc/Makefile.in, Doc/Zsh/mod_random.yo, Src/Modules/random.c, Src/Modules/random.mdd, Src/Modules/random_real.c, configure.ac: diff --git a/Doc/Zsh/mod_random.yo b/Doc/Zsh/mod_random.yo index d2892bf79..4f5622e61 100644 --- a/Doc/Zsh/mod_random.yo +++ b/Doc/Zsh/mod_random.yo @@ -41,7 +41,7 @@ tt(inclusive) is a flag that controls whether the result is ever equal to tt(upper). By default it is not. If this argument is set to a non-zero value then it may be. -This is to facilitate a construct like tt($a[zrand_int($#a)+1]) rather +This is to facilitate a construct like tt($a[zrand_int+LPAR()$#a+RPAR()+1]) rather than tt($a[zrand_int+LPAR()$#a-1+RPAR()+1]). For example, if $#a is 16, you would use tt(zrand_int+LPAR()16RPAR()) which has 16 possible return values 0-15. Because the function can return zero, in order diff --git a/Src/Modules/random.c b/Src/Modules/random.c index a153d8f64..88ac9543c 100644 --- a/Src/Modules/random.c +++ b/Src/Modules/random.c @@ -62,8 +62,10 @@ ssize_t getrandom_buffer(void *buf, size_t len) { ssize_t ret; +#ifndef HAVE_ARC4RANDOM_BUF size_t val = 0; uint8_t *bufptr = buf; +#endif do { errno = 0; @@ -81,8 +83,10 @@ getrandom_buffer(void *buf, size_t len) return -1; } } +#ifndef HAVE_ARC4RANDOM_BUF bufptr += ret; val += ret; +#endif } while (ret < len); return ret; } -- cgit v1.2.3 From 79593399c4bfa3eefb7eaa379fdc922b9c14c0df Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sat, 14 Sep 2024 19:14:40 +0200 Subject: 53080: remove code for systems that only have the old pre-POSIX signal() --- ChangeLog | 5 ++ Src/init.c | 12 ++-- Src/signals.c | 210 +--------------------------------------------------------- Src/signals.h | 16 ----- configure.ac | 31 --------- 5 files changed, 10 insertions(+), 264 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 7e181e959..3301c5287 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-09-14 Oliver Kiddle + + * 53080: configure.ac, Src/init.c, Src/signals.c, Src/signals.h: + remove code for systems that only have the old pre-POSIX signal() + 2024-08-31 Oliver Kiddle * Clinton Bunch: 53060: Doc/Zsh/mod_random.yo, diff --git a/Src/init.c b/Src/init.c index 0aecb5db9..8c7776c7a 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1386,6 +1386,8 @@ setupshin(char *runscript) void init_signals(void) { + struct sigaction act; + sigtrapped = (int *) hcalloc(TRAPCOUNT * sizeof(int)); siglists = (Eprog *) hcalloc(TRAPCOUNT * sizeof(Eprog)); @@ -1399,14 +1401,8 @@ init_signals(void) intr(); -#ifdef POSIX_SIGNALS - { - struct sigaction act; - if (!sigaction(SIGQUIT, NULL, &act) && - act.sa_handler == SIG_IGN) - sigtrapped[SIGQUIT] = ZSIG_IGNORED; - } -#endif + if (!sigaction(SIGQUIT, NULL, &act) && act.sa_handler == SIG_IGN) + sigtrapped[SIGQUIT] = ZSIG_IGNORED; #ifndef QDEBUG signal_ignore(SIGQUIT); diff --git a/Src/signals.c b/Src/signals.c index d28853ea6..6eecbf7d5 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -91,33 +91,6 @@ mod_export volatile int trap_queueing_enabled, trap_queue_front, trap_queue_rear /**/ mod_export int trap_queue[MAX_QUEUE_SIZE]; -/* This is only used on machines that don't understand signal sets. * - * On SYSV machines this will represent the signals that are blocked * - * (held) using sighold. On machines which can't block signals at * - * all, we will simulate this by ignoring them and remembering them * - * in this variable. */ -#if !defined(POSIX_SIGNALS) && !defined(BSD_SIGNALS) -static sigset_t blocked_set; -#endif - -#ifdef POSIX_SIGNALS -# define signal_jmp_buf sigjmp_buf -# define signal_setjmp(b) sigsetjmp((b),1) -# define signal_longjmp(b,n) siglongjmp((b),(n)) -#else -# define signal_jmp_buf jmp_buf -# define signal_setjmp(b) setjmp(b) -# define signal_longjmp(b,n) longjmp((b),(n)) -#endif - -#ifdef NO_SIGNAL_BLOCKING -# define signal_process(sig) signal_ignore(sig) -# define signal_reset(sig) install_handler(sig) -#else -# define signal_process(sig) ; -# define signal_reset(sig) ; -#endif - /* Install signal handler for given signal. * * If possible, we want to make sure that interrupted * * system calls are not restarted. */ @@ -126,10 +99,9 @@ static sigset_t blocked_set; mod_export void install_handler(int sig) { -#ifdef POSIX_SIGNALS struct sigaction act; - act.sa_handler = (SIGNAL_HANDTYPE) zhandler; + act.sa_handler = (void (*)(int)) zhandler; sigemptyset(&act.sa_mask); /* only block sig while in handler */ act.sa_flags = 0; # ifdef SA_INTERRUPT /* SunOS 4.x */ @@ -137,27 +109,6 @@ install_handler(int sig) act.sa_flags |= SA_INTERRUPT; /* make sure system calls are not restarted */ # endif sigaction(sig, &act, (struct sigaction *)NULL); -#else -# ifdef BSD_SIGNALS - struct sigvec vec; - - vec.sv_handler = (SIGNAL_HANDTYPE) zhandler; - vec.sv_mask = sigmask(sig); /* mask out this signal while in handler */ -# ifdef SV_INTERRUPT - vec.sv_flags = SV_INTERRUPT; /* make sure system calls are not restarted */ -# endif - sigvec(sig, &vec, (struct sigvec *)NULL); -# else -# ifdef SYSV_SIGNALS - /* we want sigset rather than signal because it will * - * block sig while in handler. signal usually doesn't */ - sigset(sig, zhandler); -# else /* NO_SIGNAL_BLOCKING (bummer) */ - signal(sig, zhandler); - -# endif /* SYSV_SIGNALS */ -# endif /* BSD_SIGNALS */ -#endif /* POSIX_SIGNALS */ } /* enable ^C interrupts */ @@ -219,50 +170,17 @@ signal_mask(int sig) /* Block the signals in the given signal * * set. Return the old signal set. */ -/**/ -#ifndef BSD_SIGNALS - /**/ mod_export sigset_t signal_block(sigset_t set) { sigset_t oset; -#ifdef POSIX_SIGNALS sigprocmask(SIG_BLOCK, &set, &oset); -#else -# ifdef SYSV_SIGNALS - int i; - - oset = blocked_set; - for (i = 1; i <= NSIG; ++i) { - if (sigismember(&set, i) && !sigismember(&blocked_set, i)) { - sigaddset(&blocked_set, i); - sighold(i); - } - } -# else /* NO_SIGNAL_BLOCKING */ -/* We will just ignore signals if the system doesn't have * - * the ability to block them. */ - int i; - - oset = blocked_set; - for (i = 1; i <= NSIG; ++i) { - if (sigismember(&set, i) && !sigismember(&blocked_set, i)) { - sigaddset(&blocked_set, i); - signal_ignore(i); - } - } -# endif /* SYSV_SIGNALS */ -#endif /* POSIX_SIGNALS */ - return oset; } -/**/ -#endif /* BSD_SIGNALS */ - /* Unblock the signals in the given signal * * set. Return the old signal set. */ @@ -272,39 +190,7 @@ signal_unblock(sigset_t set) { sigset_t oset; -#ifdef POSIX_SIGNALS sigprocmask(SIG_UNBLOCK, &set, &oset); -#else -# ifdef BSD_SIGNALS - sigfillset(&oset); - oset = sigsetmask(oset); - sigsetmask(oset & ~set); -# else -# ifdef SYSV_SIGNALS - int i; - - oset = blocked_set; - for (i = 1; i <= NSIG; ++i) { - if (sigismember(&set, i) && sigismember(&blocked_set, i)) { - sigdelset(&blocked_set, i); - sigrelse(i); - } - } -# else /* NO_SIGNAL_BLOCKING */ -/* On systems that can't block signals, we are just ignoring them. So * - * to unblock signals, we just reenable the signal handler for them. */ - int i; - - oset = blocked_set; - for (i = 1; i <= NSIG; ++i) { - if (sigismember(&set, i) && sigismember(&blocked_set, i)) { - sigdelset(&blocked_set, i); - install_handler(i); - } - } -# endif /* SYSV_SIGNALS */ -# endif /* BSD_SIGNALS */ -#endif /* POSIX_SIGNALS */ return oset; } @@ -318,61 +204,19 @@ signal_setmask(sigset_t set) { sigset_t oset; -#ifdef POSIX_SIGNALS sigprocmask(SIG_SETMASK, &set, &oset); -#else -# ifdef BSD_SIGNALS - oset = sigsetmask(set); -# else -# ifdef SYSV_SIGNALS - int i; - - oset = blocked_set; - for (i = 1; i <= NSIG; ++i) { - if (sigismember(&set, i) && !sigismember(&blocked_set, i)) { - sigaddset(&blocked_set, i); - sighold(i); - } else if (!sigismember(&set, i) && sigismember(&blocked_set, i)) { - sigdelset(&blocked_set, i); - sigrelse(i); - } - } -# else /* NO_SIGNAL_BLOCKING */ - int i; - - oset = blocked_set; - for (i = 1; i < NSIG; ++i) { - if (sigismember(&set, i) && !sigismember(&blocked_set, i)) { - sigaddset(&blocked_set, i); - signal_ignore(i); - } else if (!sigismember(&set, i) && sigismember(&blocked_set, i)) { - sigdelset(&blocked_set, i); - install_handler(i); - } - } -# endif /* SYSV_SIGNALS */ -# endif /* BSD_SIGNALS */ -#endif /* POSIX_SIGNALS */ return oset; } -#if defined(NO_SIGNAL_BLOCKING) -static int suspend_longjmp = 0; -static signal_jmp_buf suspend_jmp_buf; -#endif - /**/ int signal_suspend(UNUSED(int sig), int wait_cmd) { int ret; -#if defined(POSIX_SIGNALS) || defined(BSD_SIGNALS) sigset_t set; -# if defined(POSIX_SIGNALS) && defined(BROKEN_POSIX_SIGSUSPEND) sigset_t oset; -# endif sigemptyset(&set); @@ -384,9 +228,7 @@ signal_suspend(UNUSED(int sig), int wait_cmd) if (!(wait_cmd || isset(TRAPSASYNC) || (sigtrapped[SIGINT] & ~ZSIG_IGNORED))) sigaddset(&set, SIGINT); -#endif /* POSIX_SIGNALS || BSD_SIGNALS */ -#ifdef POSIX_SIGNALS # ifdef BROKEN_POSIX_SIGSUSPEND sigprocmask(SIG_SETMASK, &set, &oset); ret = pause(); @@ -394,26 +236,6 @@ signal_suspend(UNUSED(int sig), int wait_cmd) # else /* not BROKEN_POSIX_SIGSUSPEND */ ret = sigsuspend(&set); # endif /* BROKEN_POSIX_SIGSUSPEND */ -#else /* not POSIX_SIGNALS */ -# ifdef BSD_SIGNALS - ret = sigpause(set); -# else -# ifdef SYSV_SIGNALS - ret = sigpause(sig); - -# else /* NO_SIGNAL_BLOCKING */ - /* need to use signal_longjmp to make this race-free * - * between the child_unblock() and pause() */ - if (signal_setjmp(suspend_jmp_buf) == 0) { - suspend_longjmp = 1; /* we want to signal_longjmp after catching signal */ - child_unblock(); /* do we need to do wait_cmd stuff as well? */ - ret = pause(); - } - suspend_longjmp = 0; /* turn off using signal_longjmp since we are past * - * the pause() function. */ -# endif /* SYSV_SIGNALS */ -# endif /* BSD_SIGNALS */ -#endif /* POSIX_SIGNALS */ return ret; } @@ -586,33 +408,12 @@ zhandler(int sig) { sigset_t newmask, oldmask; -#if defined(NO_SIGNAL_BLOCKING) - int do_jump; - signal_jmp_buf jump_to; -#endif - last_signal = sig; - signal_process(sig); sigfillset(&newmask); /* Block all signals temporarily */ oldmask = signal_block(newmask); -#if defined(NO_SIGNAL_BLOCKING) - /* do we need to longjmp to signal_suspend */ - do_jump = suspend_longjmp; - /* In case a SIGCHLD somehow arrives */ - suspend_longjmp = 0; - - /* Traps can cause nested signal_suspend() */ - if (sig == SIGCHLD) { - if (do_jump) { - /* Copy suspend_jmp_buf */ - jump_to = suspend_jmp_buf; - } - } -#endif - /* Are we queueing signals now? */ if (queueing_enabled) { int temp_rear = ++queue_rear % MAX_QUEUE_SIZE; @@ -627,7 +428,6 @@ zhandler(int sig) /* save current signal mask */ signal_mask_queue[queue_rear] = oldmask; } - signal_reset(sig); return; } @@ -704,14 +504,6 @@ zhandler(int sig) break; } /* end of switch(sig) */ - signal_reset(sig); - -/* This is used to make signal_suspend() race-free */ -#if defined(NO_SIGNAL_BLOCKING) - if (do_jump) - signal_longjmp(jump_to, 1); -#endif - } /* handler */ diff --git a/Src/signals.h b/Src/signals.h index 7910f6b79..a9c12679f 100644 --- a/Src/signals.h +++ b/Src/signals.h @@ -27,8 +27,6 @@ * */ -#define SIGNAL_HANDTYPE void (*)(int) - #ifndef HAVE_KILLPG # define killpg(pgrp,sig) kill(-(pgrp),sig) #endif @@ -51,20 +49,6 @@ # define SV_INTERRUPT SV_BSDSIG #endif -/* If not a POSIX machine, then we create our * - * own POSIX style signal sets functions. */ -#ifndef POSIX_SIGNALS -# define sigemptyset(s) (*(s) = 0) -# if NSIG == 32 -# define sigfillset(s) (*(s) = ~(sigset_t)0, 0) -# else -# define sigfillset(s) (*(s) = (1 << NSIG) - 1, 0) -# endif -# define sigaddset(s,n) (*(s) |= (1 << ((n) - 1)), 0) -# define sigdelset(s,n) (*(s) &= ~(1 << ((n) - 1)), 0) -# define sigismember(s,n) ((*(s) & (1 << ((n) - 1))) != 0) -#endif /* ifndef POSIX_SIGNALS */ - #define child_block() signal_block(sigchld_mask) #define child_unblock() signal_unblock(sigchld_mask) diff --git a/configure.ac b/configure.ac index a88101f2b..eab95105c 100644 --- a/configure.ac +++ b/configure.ac @@ -1483,35 +1483,6 @@ case $host_os in darwin1[0-5]*) AC_DEFINE(SETENV_MANGLES_EQUAL) ;; esac -dnl ------------- -dnl CHECK SIGNALS -dnl ------------- -dnl What style of signal do you have (POSIX, BSD, or SYSV)? -AH_TEMPLATE([POSIX_SIGNALS], -[Define to 1 if you use POSIX style signal handling.]) -AH_TEMPLATE([BSD_SIGNALS], -[Define to 1 if you use BSD style signal handling (and can block signals).]) -AH_TEMPLATE([SYSV_SIGNALS], -[Define to 1 if you use SYS style signal handling (and can block signals).]) -AH_TEMPLATE([NO_SIGNAL_BLOCKING], -[Define to 1 if you have no signal blocking at all (bummer).]) -AC_MSG_CHECKING(what style of signals to use) -if test x$ac_cv_func_sigaction = xyes && test x$ac_cv_func_sigprocmask = xyes; then - signals_style=POSIX_SIGNALS - AC_DEFINE(POSIX_SIGNALS) -elif test x$ac_cv_func_sigblock = xyes && test x$ac_cv_func_sigsetmask = xyes; then - signals_style=BSD_SIGNALS - AC_DEFINE(BSD_SIGNALS) -elif test x$ac_cv_func_sighold = xyes && test x$ac_cv_func_sigrelse = xyes; then - signals_style=SYSV_SIGNALS - AC_DEFINE(SYSV_SIGNALS) -else - signals_style=NO_SIGNAL_BLOCKING - AC_DEFINE(NO_SIGNAL_BLOCKING) -fi -AC_DEFINE_UNQUOTED($signals_style) -AC_MSG_RESULT($signals_style) - dnl Where is located? Needed as input for signals.awk AC_CACHE_CHECK(where signal.h is located, zsh_cv_path_signal_h, [dnl Look at the output from the preprocessor. @@ -2324,7 +2295,6 @@ dnl for instance, BeOS R4.51 is broken. dnl ----------- AH_TEMPLATE([BROKEN_POSIX_SIGSUSPEND], Define to 1 if sigsuspend() is broken, ie BeOS R4.51.]) -if test x$signals_style = xPOSIX_SIGNALS; then AC_CACHE_CHECK(if POSIX sigsuspend() works, zsh_cv_sys_sigsuspend, [AC_RUN_IFELSE([AC_LANG_SOURCE([[ @@ -2356,7 +2326,6 @@ int main() { if test x$zsh_cv_sys_sigsuspend = xno; then AC_DEFINE(BROKEN_POSIX_SIGSUSPEND) fi -fi dnl ----------- dnl if found tcsetpgrp, test to see if it actually works -- cgit v1.2.3 From 8dd271fdec526241ad3ed9c7f2641127a939893c Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sat, 14 Sep 2024 19:21:58 +0200 Subject: 53081: remove old BeOS support code --- ChangeLog | 3 + INSTALL | 7 --- Src/init.c | 8 +-- Src/options.c | 9 --- Src/signals.c | 7 --- Src/zsh_system.h | 12 ---- configure.ac | 165 ------------------------------------------------------- 7 files changed, 5 insertions(+), 206 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 3301c5287..335ae40d3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-09-14 Oliver Kiddle + * 53081: INSTALL, configure.ac, Src/init.c, Src/options.c, + Src/signals.c, Src/zsh_system.h: remove old BeOS support code + * 53080: configure.ac, Src/init.c, Src/signals.c, Src/signals.h: remove code for systems that only have the old pre-POSIX signal() diff --git a/INSTALL b/INSTALL index f347a4480..8b139fa9b 100644 --- a/INSTALL +++ b/INSTALL @@ -576,13 +576,6 @@ shell is running in some privileged mode. This is turned off by default as on some systems non-standard headers (in particular AIX) are required. A direct fix for that problem would be appreciated. -A test for the function tcsetpgrp is turned on by default. The test -needs to run the function to determine if the implementation is -usable. However, this can cause problems when configure is run without -a controlling terminal (eg. from cron). To avoid this, use ---with-tcsetpgrp or --without-tcsetpgrp to tell configure whether the -function should be used. - Options For Configure --------------------- diff --git a/Src/init.c b/Src/init.c index 8c7776c7a..70e878e20 100644 --- a/Src/init.c +++ b/Src/init.c @@ -695,7 +695,6 @@ init_io(char *cmd) } else opts[USEZLE] = 0; -#ifdef JOB_CONTROL /* If interactive, make sure the shell is in the foreground and is the * process group leader. */ @@ -708,9 +707,6 @@ init_io(char *cmd) acquire_pgrp(); /* might also clear opts[MONITOR] */ } } -#else - opts[MONITOR] = 0; -#endif } /**/ @@ -718,7 +714,7 @@ mod_export void init_shout(void) { static char shoutbuf[BUFSIZ]; -#if defined(JOB_CONTROL) && defined(TIOCSETD) && defined(NTTYDISC) +#if defined(TIOCSETD) && defined(NTTYDISC) int ldisc; #endif @@ -729,7 +725,7 @@ init_shout(void) return; } -#if defined(JOB_CONTROL) && defined(TIOCSETD) && defined(NTTYDISC) +#if defined(TIOCSETD) && defined(NTTYDISC) ldisc = NTTYDISC; ioctl(SHTTY, TIOCSETD, (char *)&ldisc); #endif diff --git a/Src/options.c b/Src/options.c index a0e1aa024..8b37ab5e8 100644 --- a/Src/options.c +++ b/Src/options.c @@ -877,7 +877,6 @@ dosetopt(int optno, int value, int force, char *new_opts) } #endif /* HAVE_SETRESGID && HAVE_SETRESUID */ -#ifdef JOB_CONTROL } else if (!force && optno == MONITOR && value) { if (new_opts[optno] == value) return 0; @@ -887,14 +886,6 @@ dosetopt(int optno, int value, int force, char *new_opts) origpgrp = GETPGRP(); acquire_pgrp(); } -#else - } else if(optno == MONITOR && value) { - return -1; -#endif /* not JOB_CONTROL */ -#ifdef GETPWNAM_FAKED - } else if(optno == CDABLEVARS && value) { - return -1; -#endif /* GETPWNAM_FAKED */ } else if ((optno == EMACSMODE || optno == VIMODE) && value) { if (sticky && sticky->emulation) return -1; diff --git a/Src/signals.c b/Src/signals.c index 6eecbf7d5..86f1a49f6 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -216,7 +216,6 @@ signal_suspend(UNUSED(int sig), int wait_cmd) int ret; sigset_t set; - sigset_t oset; sigemptyset(&set); @@ -229,13 +228,7 @@ signal_suspend(UNUSED(int sig), int wait_cmd) (sigtrapped[SIGINT] & ~ZSIG_IGNORED))) sigaddset(&set, SIGINT); -# ifdef BROKEN_POSIX_SIGSUSPEND - sigprocmask(SIG_SETMASK, &set, &oset); - ret = pause(); - sigprocmask(SIG_SETMASK, &oset, NULL); -# else /* not BROKEN_POSIX_SIGSUSPEND */ ret = sigsuspend(&set); -# endif /* BROKEN_POSIX_SIGSUSPEND */ return ret; } diff --git a/Src/zsh_system.h b/Src/zsh_system.h index 5c004d53e..21446a9b1 100644 --- a/Src/zsh_system.h +++ b/Src/zsh_system.h @@ -369,8 +369,6 @@ struct timespec { # ifndef TIME_H_SELECT_H_CONFLICTS # include # endif -#elif defined(SELECT_IN_SYS_SOCKET_H) -# include #endif #if defined(__APPLE__) && defined(HAVE_SELECT) @@ -803,16 +801,6 @@ extern short ospeed; #endif #endif -/* Can't support job control without working tcsetgrp() */ -#ifdef BROKEN_TCSETPGRP -#undef JOB_CONTROL -#endif /* BROKEN_TCSETPGRP */ - -#ifdef BROKEN_KILL_ESRCH -#undef ESRCH -#define ESRCH EINVAL -#endif /* BROKEN_KILL_ESRCH */ - /* Can we do locale stuff? */ #undef USE_LOCALE #if defined(CONFIG_LOCALE) && defined(HAVE_SETLOCALE) && defined(LC_ALL) diff --git a/configure.ac b/configure.ac index eab95105c..b2721fa77 100644 --- a/configure.ac +++ b/configure.ac @@ -2119,20 +2119,6 @@ if test x$zsh_cv_header_sys_ioctl_h_ioctl_proto = xyes; then AC_DEFINE(IOCTL_IN_SYS_IOCTL) fi -dnl ------------------- -dnl select() defined in , ie BeOS R4.51 -dnl ------------------- -AH_TEMPLATE([SELECT_IN_SYS_SOCKET_H], -[Define to 1 if select() is defined in , ie BeOS R4.51]) -if test x$ac_cv_header_sys_select_h != xyes; then - AC_CACHE_CHECK(for select() in , - zsh_cv_header_socket_h_select_proto, - [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[fd_set fd;]])],[zsh_cv_header_socket_h_select_proto=yes],[zsh_cv_header_socket_h_select_proto=no])]) - if test x$zsh_cv_header_socket_h_select_proto = xyes; then - AC_DEFINE(SELECT_IN_SYS_SOCKET_H) - fi -fi - dnl ----------- dnl named FIFOs dnl ----------- @@ -2266,154 +2252,6 @@ if test x$zsh_cv_sys_link = xyes; then AC_DEFINE(HAVE_LINK) fi -dnl ----------- -dnl test for whether kill(pid, 0) where pid doesn't exit -dnl should set errno to ESRCH, but some like BeOS R4.51 set to EINVAL -dnl ----------- -AC_CACHE_CHECK(if kill(pid, 0) returns ESRCH correctly, -zsh_cv_sys_killesrch, -[AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include -#include -#include -int main() -{ - int pid = (getpid() + 10000) & 0xffffff; - while (pid && (kill(pid, 0) == 0 || errno != ESRCH)) pid >>= 1; - return(errno!=ESRCH); -} -]])],[zsh_cv_sys_killesrch=yes],[zsh_cv_sys_killesrch=no],[zsh_cv_sys_killesrch=yes])]) -AH_TEMPLATE([BROKEN_KILL_ESRCH], -[Define to 1 if kill(pid, 0) doesn't return ESRCH, ie BeOS R4.51.]) -if test x$zsh_cv_sys_killesrch = xno; then - AC_DEFINE(BROKEN_KILL_ESRCH) -fi - -dnl ----------- -dnl if POSIX, test for working sigsuspend(). -dnl for instance, BeOS R4.51 is broken. -dnl ----------- -AH_TEMPLATE([BROKEN_POSIX_SIGSUSPEND], -Define to 1 if sigsuspend() is broken, ie BeOS R4.51.]) - AC_CACHE_CHECK(if POSIX sigsuspend() works, - zsh_cv_sys_sigsuspend, - [AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include -#include -int child=0; -void handler(sig) - int sig; -{if(sig==SIGCHLD) child=1;} -int main() { - struct sigaction act; - sigset_t set; - int pid, ret; - act.sa_handler = &handler; - sigfillset(&act.sa_mask); - act.sa_flags = 0; - sigaction(SIGCHLD, &act, 0); - sigfillset(&set); - sigprocmask(SIG_SETMASK, &set, 0); - pid=fork(); - if(pid==0) return 0; - if(pid>0) { - sigemptyset(&set); - ret=sigsuspend(&set); - return(child==0); - } -} -]])],[zsh_cv_sys_sigsuspend=yes],[zsh_cv_sys_sigsuspend=no],[zsh_cv_sys_sigsuspend=yes])]) - if test x$zsh_cv_sys_sigsuspend = xno; then - AC_DEFINE(BROKEN_POSIX_SIGSUSPEND) - fi - -dnl ----------- -dnl if found tcsetpgrp, test to see if it actually works -dnl for instance, BeOS R4.51 does not support it yet -dnl ----------- -AH_TEMPLATE([BROKEN_TCSETPGRP], -[Define to 1 if tcsetpgrp() doesn't work, ie BeOS R4.51.]) -AC_ARG_WITH(tcsetpgrp, -AS_HELP_STRING([--with-tcsetpgrp],[assumes that tcsetpgrp() exists and works correctly]),[ -case "x$withval" in - xyes) zsh_working_tcsetpgrp=yes;; - xno) zsh_working_tcsetpgrp=no;; - *) AC_MSG_ERROR(please use --with-tcsetpgrp=yes or --with-tcsetpgrp=no);; -esac],[zsh_working_tcsetpgrp=check]) -if test "x$ac_cv_func_tcsetpgrp" = xyes; then -case "x$zsh_working_tcsetpgrp" in - xcheck) - trap "" TTOU > /dev/null 2>&1 || : - AC_CACHE_CHECK(if tcsetpgrp() actually works, - zsh_cv_sys_tcsetpgrp, - [AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include -#include -#include -int main() { - int fd; - int ret; - fd=open("/dev/tty", O_RDWR); - if (fd < 0) return(2); - ret=tcsetpgrp(fd, tcgetpgrp(fd)); - if (ret < 0) return(1); - return(0); -} -]])],[zsh_cv_sys_tcsetpgrp=yes],[ -case $? in - 1) zsh_cv_sys_tcsetpgrp=no;; - 2) zsh_cv_sys_tcsetpgrp=notty;; - *) zsh_cv_sys_tcsetpgrp=error;; -esac - ],[zsh_cv_sys_tcsetpgrp=yes])]) - case "x$zsh_cv_sys_tcsetpgrp" in - xno) AC_DEFINE(BROKEN_TCSETPGRP);; - xyes) :;; - xnotty) AC_MSG_ERROR([no controlling tty -Try running configure with --with-tcsetpgrp or --without-tcsetpgrp]);; - *) AC_MSG_ERROR([unexpected return status]);; - esac - trap - TTOU > /dev/null 2>&1 || : - ;; - xyes) :;; - xno) AC_DEFINE(BROKEN_TCSETPGRP);; - *) AC_MSG_ERROR([unexpected value zsh_working_tcsetpgrp=$zsh_working_tcsetpgrp]);; -esac -fi - -dnl ----------- -dnl test for faked getpwnam() entry, ie a single entry returned for any username -dnl for instance, BeOS R4.51 is not multiuser yet, and fakes getpwnam() -dnl test by looking up two usernames that shouldn't succeed, and compare entry -dnl ----------- -AH_TEMPLATE([GETPWNAM_FAKED], -[Define to 1 if getpwnam() is faked, ie BeOS R4.51.]) -if test x$ac_cv_func_getpwnam = xyes; then - AC_CACHE_CHECK(if getpwnam() is faked, - zsh_cv_sys_getpwnam_faked, - [AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#include -#include -#include -#include -#include -int main() { - struct passwd *pw1, *pw2; - char buf[1024], name[1024]; - sprintf(buf, "%d:%d", getpid(), rand()); - pw1=getpwnam(buf); - if (pw1) strcpy(name, pw1->pw_name); - sprintf(buf, "%d:%d", rand(), getpid()); - pw2=getpwnam(buf); - return(pw1!=0 && pw2!=0 && !strcmp(name, pw2->pw_name)); -} -]])],[zsh_cv_sys_getpwnam_faked=no],[zsh_cv_sys_getpwnam_faked=yes],[zsh_cv_sys_getpwnam_faked=no])]) - if test x$zsh_cv_sys_getpwnam_faked = xyes; then - AC_DEFINE(GETPWNAM_FAKED) - fi -fi - - dnl --------------- dnl check for the type of third argument of accept dnl --------------- @@ -3151,9 +2989,6 @@ AH_TOP([/***** begin user configuration section *****/ /* Define to 1 if you want user names to be cached */ #define CACHE_USERNAMES 1 -/* Define to 1 if system supports job control */ -#define JOB_CONTROL 1 - /* Define this if you use "suspended" instead of "stopped" */ #define USE_SUSPENDED 1 -- cgit v1.2.3 From 55ff6f88649a592eb42e04e7434fd89349d4518a Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sat, 14 Sep 2024 19:30:30 +0200 Subject: 53083: handle Haiku specific resource limit for open vnode monitors --- ChangeLog | 3 +++ Src/Builtins/rlimits.c | 4 ++++ configure.ac | 1 + 3 files changed, 8 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 70c1213b9..3ecb05e63 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-09-14 Oliver Kiddle + * 53083: configure.ac, Src/Builtins/rlimits.c: handle Haiku + specific resource limit for open vnode monitors + * 53082: configure.ac: remove a couple of unused #defines * 53081: INSTALL, configure.ac, Src/init.c, Src/options.c, diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c index 5f9c84b0f..f25dd2530 100644 --- a/Src/Builtins/rlimits.c +++ b/Src/Builtins/rlimits.c @@ -171,6 +171,10 @@ static const resinfo_T known_resources[] = { {RLIMIT_TCACHE, "cachedthreads", ZLIMTYPE_NUMBER, 1, 'N', "cached threads"}, # endif +# ifdef HAVE_RLIMIT_NOVMON /* Haiku */ + {RLIMIT_NOVMON, "vnodemonitors", ZLIMTYPE_NUMBER, 1, + 'N', "open vnode monitors"}, +# endif }; /* resinfo[RLIMIT_XXX] points to the corresponding entry diff --git a/configure.ac b/configure.ac index aca355ff4..220f353c3 100644 --- a/configure.ac +++ b/configure.ac @@ -1879,6 +1879,7 @@ zsh_LIMIT_PRESENT(RLIMIT_NPTS) zsh_LIMIT_PRESENT(RLIMIT_SWAP) zsh_LIMIT_PRESENT(RLIMIT_KQUEUES) zsh_LIMIT_PRESENT(RLIMIT_UMTXP) +zsh_LIMIT_PRESENT(RLIMIT_NOVMON) zsh_LIMITS_EQUAL(VMEM, vmem, RSS, rss) zsh_LIMITS_EQUAL(VMEM, vmem, AS, as) -- cgit v1.2.3 From 701fad5021db0b3d07eaf90013a1ad760aaca78c Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sat, 14 Sep 2024 19:38:23 +0200 Subject: 53085: support for BSDs in $ZSH_EXEPATH implementation --- ChangeLog | 3 +++ Src/init.c | 34 ++++++++++++++++++++++++++-------- configure.ac | 2 +- 3 files changed, 30 insertions(+), 9 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index a4bec3999..2d06a3ffb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-09-14 Oliver Kiddle + * 53085: configure.ac, Src/init.c: support for BSDs in + $ZSH_EXEPATH implementation + * 53084: configure.ac: yet another approach to the /dev/fd autoconf test because of shells emulating /dev/fd diff --git a/Src/init.c b/Src/init.c index 70e878e20..2f914f96b 100644 --- a/Src/init.c +++ b/Src/init.c @@ -36,6 +36,10 @@ #include "version.h" +#ifdef HAVE_SYS_SYSCTL_H +#include +#endif + /**/ int noexitct = 0; @@ -913,12 +917,6 @@ getmypath(const char *name, const char *cwd) char *buf; int namelen; - if (!name) - return NULL; - if (*name == '-') - ++name; - if ((namelen = strlen(name)) == 0) - return NULL; #if defined(__APPLE__) { uint32_t n = PATH_MAX; @@ -934,6 +932,19 @@ getmypath(const char *name, const char *cwd) else free(buf); } +#elif defined(KERN_PROC_PATHNAME) + { +#ifdef __NetBSD__ + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_PATHNAME }; +#else + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; +#endif + size_t len = PATH_MAX; + buf = (char *) zalloc(PATH_MAX); + if (!sysctl(mib, 4, buf, &len, NULL, 0) && len > 1) + return buf; + free(buf); + } #elif defined(PROC_SELF_EXE) { ssize_t n; @@ -947,6 +958,13 @@ getmypath(const char *name, const char *cwd) free(buf); } #endif + + if (!name) + return NULL; + if (*name == '-') + ++name; + if ((namelen = strlen(name)) == 0) + return NULL; /* guess the absolute pathname of 'name' */ if (name[namelen-1] == '/') /* name should not end with '/' */ return NULL; @@ -964,7 +982,7 @@ getmypath(const char *name, const char *cwd) } #ifdef HAVE_REALPATH else { - /* search each dir in PARH */ + /* search each dir in PATH */ const char *path, *sep; char *real, *try; int pathlen, dirlen; @@ -978,7 +996,7 @@ getmypath(const char *name, const char *cwd) do { sep = strchr(path, ':'); dirlen = sep ? sep - path : strlen(path); - strncpy(try, path, dirlen); + memcpy(try, path, dirlen); try[dirlen] = '/'; try[dirlen+1] = '\0'; strcat(try, name); diff --git a/configure.ac b/configure.ac index e8f434274..2931f6615 100644 --- a/configure.ac +++ b/configure.ac @@ -636,7 +636,7 @@ fi AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \ termios.h sys/param.h sys/filio.h string.h memory.h \ limits.h fcntl.h libc.h sys/utsname.h sys/resource.h \ - sys/random.h \ + sys/sysctl.h sys/random.h \ locale.h errno.h stdio.h stdarg.h varargs.h stdlib.h \ unistd.h sys/capability.h \ utmp.h utmpx.h sys/types.h pwd.h grp.h poll.h sys/mman.h \ -- cgit v1.2.3 From 8ad625d90c2df0246aa921c51d248a3b28f43da9 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 14 Sep 2024 12:40:56 -0700 Subject: 53088: enable `time' on builtins, assignments, and current-shell actions --- ChangeLog | 6 +++++ Src/exec.c | 42 ++++++++++++++++++++++++++++++++-- Src/jobs.c | 41 +++++++++++++++++++++++++++++---- Test/A01grammar.ztst | 7 ------ Test/A08time.ztst | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 147 insertions(+), 13 deletions(-) create mode 100644 Test/A08time.ztst (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 5260a9665..00a89d48f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2024-09-14 Bart Schaefer + + * 53088: Src/exec.c, Src/jobs.c, Test/A01grammar.ztst, + Test/A08time.ztst: enable `time' on builtins, assignments, and + other current-shell actions, including failed commands. Tests. + 2024-09-14 Oliver Kiddle * c.f. Emil Velikov: 53072: Completion/Linux/Command/_dkms: diff --git a/Src/exec.c b/Src/exec.c index 00278ac50..8aa7466f5 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -881,6 +881,10 @@ execute(LinkList args, int flags, int defpath) _realexit(); else zerr("command not found: %s", arg0); + /* This is bash behavior, but fails to restore interactive settings etc. + lastval = ((eno == EACCES || eno == ENOEXEC) ? 126 : 127); + return; + */ _exit((eno == EACCES || eno == ENOEXEC) ? 126 : 127); } @@ -1677,7 +1681,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) wordcode code = *state->pc++; static int lastwj, lpforked; - if (wc_code(code) != WC_PIPE) + if (wc_code(code) != WC_PIPE && !(how & Z_TIMED)) return lastval = (slflags & WC_SUBLIST_NOT) != 0; else if (slflags & WC_SUBLIST_NOT) last1 = 0; @@ -2939,6 +2943,14 @@ execcmd_exec(Estate state, Execcmd_params eparams, */ LinkList preargs; + /* + * for the "time" keyword + */ + child_times_t shti, chti; + struct timeval then; + if (how & Z_TIMED) + shelltime(&shti, &chti, &then, 0); + doneps4 = 0; /* @@ -3071,6 +3083,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, if (!(hn = resolvebuiltin(cmdarg, hn))) { if (forked) _realexit(); + if (how & Z_TIMED) + shelltime(&shti, &chti, &then, 1); return; } if (type != WC_TYPESET) @@ -3252,6 +3266,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, errflag |= ERRFLAG_ERROR; if (forked) _realexit(); + if (how & Z_TIMED) + shelltime(&shti, &chti, &then, 1); return; } } @@ -3343,6 +3359,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, errflag |= ERRFLAG_ERROR; if (forked) _realexit(); + if (how & Z_TIMED) + shelltime(&shti, &chti, &then, 1); return; } else if (!nullcmd || !*nullcmd || opts[SHNULLCMD]) { if (!args) @@ -3363,6 +3381,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, lastval = 0; if (forked) _realexit(); + if (how & Z_TIMED) + shelltime(&shti, &chti, &then, 1); return; } else { /* @@ -3375,6 +3395,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, lastval = 1; if (forked) _realexit(); + if (how & Z_TIMED) + shelltime(&shti, &chti, &then, 1); return; } cmdoutval = use_cmdoutval ? lastval : 0; @@ -3393,6 +3415,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, } if (forked) _realexit(); + if (how & Z_TIMED) + shelltime(&shti, &chti, &then, 1); return; } } else if (isset(RESTRICTED) && (cflags & BINF_EXEC) && do_exec) { @@ -3401,6 +3425,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, lastval = 1; if (forked) _realexit(); + if (how & Z_TIMED) + shelltime(&shti, &chti, &then, 1); return; } @@ -3437,6 +3463,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, opts[AUTOCONTINUE] = oautocont; if (forked) _realexit(); + if (how & Z_TIMED) + shelltime(&shti, &chti, &then, 1); return; } break; @@ -3448,6 +3476,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, if (!(hn = resolvebuiltin(cmdarg, hn))) { if (forked) _realexit(); + if (how & Z_TIMED) + shelltime(&shti, &chti, &then, 1); return; } break; @@ -3466,6 +3496,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, opts[AUTOCONTINUE] = oautocont; if (forked) _realexit(); + if (how & Z_TIMED) + shelltime(&shti, &chti, &then, 1); return; } @@ -3545,6 +3577,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, opts[AUTOCONTINUE] = oautocont; if (forked) _realexit(); + if (how & Z_TIMED) + shelltime(&shti, &chti, &then, 1); return; } } @@ -3575,6 +3609,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, opts[AUTOCONTINUE] = oautocont; if (forked) _realexit(); + if (how & Z_TIMED) + shelltime(&shti, &chti, &then, 1); return; } @@ -4377,6 +4413,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, errflag |= ERRFLAG_ERROR; } } + if ((is_cursh || do_exec) && (how & Z_TIMED)) + shelltime(&shti, &chti, &then, 1); if (newxtrerr) { int eno = errno; fil = fileno(newxtrerr); @@ -5265,7 +5303,7 @@ exectime(Estate state, UNUSED(int do_exec)) jb = thisjob; if (WC_TIMED_TYPE(state->pc[-1]) == WC_TIMED_EMPTY) { - shelltime(); + shelltime(NULL,NULL,NULL,0); return 0; } execpline(state, *state->pc++, Z_TIMED|Z_SYNC, 0); diff --git a/Src/jobs.c b/Src/jobs.c index 07facc60b..39c664388 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -1894,7 +1894,7 @@ spawnjob(void) /**/ void -shelltime(void) +shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int delta) { struct timezone dummy_tz; struct timeval dtimeval, now; @@ -1913,7 +1913,28 @@ shelltime(void) ti.ut = buf.tms_utime; ti.st = buf.tms_stime; #endif - printtime(dtime(&dtimeval, &shtimer, &now), &ti, "shell"); + if (shell) { + if (delta) { +#ifdef HAVE_GETRUSAGE + dtime(&ti.ru_utime, &shell->ru_utime, &ti.ru_utime); + dtime(&ti.ru_stime, &shell->ru_stime, &ti.ru_stime); +#else + ti.ut -= shell->ut; + ti.st -= shell->st; +#endif + } else + *shell = ti; + } + if (delta) + dtime(&dtimeval, then, &now); + else { + if (then) + *then = now; + dtime(&dtimeval, &shtimer, &now); + } + + if (!delta == !shell) + printtime(&dtimeval, &ti, "shell"); #ifdef HAVE_GETRUSAGE getrusage(RUSAGE_CHILDREN, &ti); @@ -1921,8 +1942,20 @@ shelltime(void) ti.ut = buf.tms_cutime; ti.st = buf.tms_cstime; #endif - printtime(&dtimeval, &ti, "children"); - + if (kids) { + if (delta) { +#ifdef HAVE_GETRUSAGE + dtime(&ti.ru_utime, &kids->ru_utime, &ti.ru_utime); + dtime(&ti.ru_stime, &kids->ru_stime, &ti.ru_stime); +#else + ti.ut -= shell->ut; + ti.st -= shell->st; +#endif + } else + *kids = ti; + } + if (!delta == !kids) + printtime(&dtimeval, &ti, "children"); } /* see if jobs need printing */ diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst index d57085798..660602caf 100644 --- a/Test/A01grammar.ztst +++ b/Test/A01grammar.ztst @@ -399,13 +399,6 @@ >This is name2 >This is still name2 - (time cat) >&/dev/null -0:`time' keyword (status only) - - TIMEFMT='%E %mE %uE %* %m%mm %u%uu'; time (:) -0:`time' keyword with custom TIMEFMT -*?[0-9]##.[0-9](#c2)s [0-9]##ms [0-9]##us %\* %m%mm %u%uu - if [[ -f foo && -d . && -n $ZTST_testdir ]]; then true else diff --git a/Test/A08time.ztst b/Test/A08time.ztst new file mode 100644 index 000000000..9fb1f3ebf --- /dev/null +++ b/Test/A08time.ztst @@ -0,0 +1,64 @@ +# +# This file contains tests for the "time" reserved word +# + +%prep + + unset TIMEFMT + +%test + + (time cat) >&/dev/null +0:`time' keyword (status only) + + ( TIMEFMT='%E %mE %uE %* %m%mm %u%uu'; time (:) ) +0:`time' keyword with custom TIMEFMT +*?[0-9]##.[0-9](#c2)s [0-9]##ms [0-9]##us %\* %m%mm %u%uu + + time x=1 +0:`time' simple assignment +*?shell* +*?children* + + time x=$(date) +0:`time' assignment with external command +*?shell* +*?children* + + x=0; time for ((i=1; i<=10000; ++i)); do ((x+=i)); done; echo $x +0:`time' for-loop with arithmetic condition +>50005000 +*?shell* +*?children* + + time echo $(x=0;for ((i=0; i<=100000; ++i)); do ((x+=i)); done; echo $x) +0:`time' of a builtin with argument command substitution +>5000050000 +*?shell* +*?children* + + time cat <(x=0;for ((i=0; i<=100000; ++i)); do ((x+=i)); done; echo $x) +0:`time' of external command with process substitution +>5000050000 +*?*user*system*cpu*total + + print -u $ZTST_fd 'This test takes 2 seconds' + time builtin nonesuch $(sleep 2) +1:`time' of nonexistent builtin with command substitution +*?*: no such builtin: nonesuch +*?shell* +*?children* + + time /no/such/commmand +127:`time' of nonexistent external +*?*no such file or directory: /no/such/commmand +*?*user*system*cpu*total + + ( setopt errexit; time false; print notreached ) +1:`time' of failed builtin with errexit +*?shell* +*?children* + + ( setopt errexit; time =false; print notreached ) +1:`time' of failed external with errexit +*?*user*system*cpu*total -- cgit v1.2.3 From ad4afde923bc6e809795c99544f5937d159c6bb9 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 18 Sep 2024 10:53:41 +0200 Subject: 53092: silence deprecated header warning on older Linux systems --- ChangeLog | 4 ++++ Src/init.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 00a89d48f..7ca08cf8c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2024-09-18 Oliver Kiddle + + * 53092: Src/init.c: silence deprecated header warning on Linux + 2024-09-14 Bart Schaefer * 53088: Src/exec.c, Src/jobs.c, Test/A01grammar.ztst, diff --git a/Src/init.c b/Src/init.c index 2f914f96b..61f759ded 100644 --- a/Src/init.c +++ b/Src/init.c @@ -36,7 +36,7 @@ #include "version.h" -#ifdef HAVE_SYS_SYSCTL_H +#if defined(HAVE_SYS_SYSCTL_H) && !defined(__linux) #include #endif -- cgit v1.2.3 From 06f423f8a99a3a1d40551cc88cfdcb3a3fe84616 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 18 Sep 2024 10:56:39 +0200 Subject: 53093: silence compiler warning when USE_LSEEK is not defined --- ChangeLog | 3 +++ Src/input.c | 11 ++++------- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 7ca08cf8c..aae14e249 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-09-18 Oliver Kiddle + * 53093: Src/input.c: silence compiler warning when USE_LSEEK + is not defined + * 53092: Src/init.c: silence deprecated header warning on Linux 2024-09-14 Bart Schaefer diff --git a/Src/input.c b/Src/input.c index d8ac2c0e7..320fd6500 100644 --- a/Src/input.c +++ b/Src/input.c @@ -217,26 +217,23 @@ shinbufrestore(void) static int shingetchar(void) { - int nread, rsize = isset(SHINSTDIN) ? 1 : SHINBUFSIZE; + int nread; if (shinbufptr < shinbufendptr) return (unsigned char) *shinbufptr++; shinbufreset(); #ifdef USE_LSEEK - if (rsize == 1 && lseek(SHIN, 0, SEEK_CUR) != (off_t)-1) - rsize = SHINBUFSIZE; - if (rsize > 1) { + if (!isset(SHINSTDIN) || lseek(SHIN, 0, SEEK_CUR) != (off_t) -1) { do { errno = 0; - nread = read(SHIN, shinbuffer, rsize); + nread = read(SHIN, shinbuffer, SHINBUFSIZE); } while (nread < 0 && errno == EINTR); if (nread <= 0) return -1; if (isset(SHINSTDIN) && (shinbufendptr = memchr(shinbuffer, '\n', nread))) { - shinbufendptr++; - rsize = (shinbufendptr - shinbuffer); + int rsize = (++shinbufendptr - shinbuffer); if (nread > rsize && lseek(SHIN, -(nread - rsize), SEEK_CUR) < 0) zerr("lseek(%d, %d): %e", SHIN, -(nread - rsize), errno); -- cgit v1.2.3 From 62131ae0499da56ecad237e5f82007a73cfff4fd Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Wed, 18 Sep 2024 10:58:55 +0200 Subject: 53094: remove unused autoconf tests --- ChangeLog | 2 ++ Src/signals.h | 5 ----- configure.ac | 12 +++--------- 3 files changed, 5 insertions(+), 14 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index aae14e249..f1f23bd31 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2024-09-18 Oliver Kiddle + * 53094: configure.ac, Src/signals.h: remove unused autoconf tests + * 53093: Src/input.c: silence compiler warning when USE_LSEEK is not defined diff --git a/Src/signals.h b/Src/signals.h index a9c12679f..0540d4b0b 100644 --- a/Src/signals.h +++ b/Src/signals.h @@ -126,10 +126,5 @@ #define queue_signal_level() queueing_enabled -#ifdef BSD_SIGNALS -#define signal_block(S) sigblock(S) -#else extern sigset_t signal_block (sigset_t); -#endif /* BSD_SIGNALS */ - extern sigset_t signal_unblock (sigset_t); diff --git a/configure.ac b/configure.ac index 474f0ccee..7073f4e2c 100644 --- a/configure.ac +++ b/configure.ac @@ -634,7 +634,7 @@ if test "x$enable_pcre" = xyes; then fi AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \ - termios.h sys/param.h sys/filio.h string.h memory.h \ + termios.h sys/param.h sys/filio.h \ limits.h fcntl.h libc.h sys/utsname.h sys/resource.h \ sys/sysctl.h sys/random.h \ locale.h errno.h stdio.h stdarg.h varargs.h stdlib.h \ @@ -849,11 +849,6 @@ esac]) AC_SEARCH_LIBS(getpwnam, nsl) -dnl I am told that told that unicos reqire these for nis_list -if test `echo $host_os | sed 's/^\(unicos\).*/\1/'` = unicos; then - LIBS="-lcraylm -lkrb -lnisdb -lnsl -lrpcsvc $LIBS" -fi - if test "x$dynamic" = xyes; then AC_CHECK_LIB(dl, dlopen) fi @@ -1258,13 +1253,12 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \ fpurge fseeko ftello \ mkfifo _mktemp mkstemp \ waitpid wait3 \ - sigaction sigblock sighold sigrelse sigsetmask sigprocmask \ sigqueue \ killpg setpgid setpgrp tcsetpgrp tcgetattr nice \ gethostname gethostbyname2 getipnodebyname \ inet_aton inet_pton inet_ntop \ getlogin getpwent getpwnam getpwuid getgrgid getgrnam \ - initgroups nis_list \ + initgroups \ setuid seteuid setreuid setresuid setsid \ setgid setegid setregid setresgid \ memcpy memmove strstr strerror strtoul \ @@ -1275,7 +1269,7 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \ signgam tgamma \ log2 \ scalbn \ - putenv getenv setenv unsetenv xw\ + putenv getenv setenv unsetenv \ brk sbrk \ pathconf sysconf \ tgetent tigetflag tigetnum tigetstr setupterm initscr resize_term \ -- cgit v1.2.3 From ac1fd06d7ce073c86f00d50fd74464bcb5194e97 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 16 Oct 2024 13:07:56 -0700 Subject: 53146: flags cannot be copied from an undefined function (cf. ca6f4466) --- ChangeLog | 5 +++++ Src/exec.c | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index a2b9eadae..977e25159 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-10-16 Bart Schaefer + + * 53146: Src/exec.c: trace flags cannot be copied from an + undefined function (cf. workers/45131) + 2024-09-02 Jun-ichi Takimoto * 53134: configure.ac: use -undefined dynamic_lookup on recent macOS diff --git a/Src/exec.c b/Src/exec.c index 8aa7466f5..bc07e8c39 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -5504,7 +5504,8 @@ execfuncdef(Estate state, Eprog redir_prog) if (funcstack && funcstack->tp == FS_FUNC && !strcmp(s, funcstack->name)) { Shfunc old = ((Shfunc)shfunctab->getnode(shfunctab, s)); - shf->node.flags |= old->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL); + if (old) + shf->node.flags |= old->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL); } shfunctab->addnode(shfunctab, ztrdup(s), shf); } -- cgit v1.2.3 From f282ff579284c7dd918438bc8e30fba4f6776c8c Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 5 Nov 2024 13:50:51 -0800 Subject: 53209 + tests: do not unmetafy via string pointers into global parameter table --- ChangeLog | 5 +++++ Src/params.c | 3 ++- Test/D04parameter.ztst | 8 ++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 977e25159..35b01f95e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2024-11-05 Bart Schaefer + + * 53209 + comments + test: Src/params.c, Test/D04parameter.ztst: + do not unmetafy via string pointers into global parameter table + 2024-10-16 Bart Schaefer * 53146: Src/exec.c: trace flags cannot be copied from an diff --git a/Src/params.c b/Src/params.c index acd577527..6f137585b 100644 --- a/Src/params.c +++ b/Src/params.c @@ -3064,8 +3064,9 @@ getsparam(char *s) mod_export char * getsparam_u(char *s) { + /* getsparam() returns pointer into global params table, so ... */ if ((s = getsparam(s))) - return unmetafy(s, NULL); + return unmeta(s); /* returns static pointer to copy */ return s; } diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst index 0e2a04eb5..7953827d6 100644 --- a/Test/D04parameter.ztst +++ b/Test/D04parameter.ztst @@ -2834,3 +2834,11 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888 fi 1:${(#X)...}: array of out-of-range characters ?(eval):4: character not in range + + ( + export ZDOTDIR=${ echo $PWD/'\0360\0237\0224\0256' } + $ZTST_testdir/../Src/zsh -c 'echo $ZDOTDIR' + ) +0:regression for workers/53179 unicode ZDOTDIR +F:output ignorable as long as not an error +*>* -- cgit v1.2.3 From 3c0f932300adbc099658375137cffcaf10678cc1 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sat, 23 Nov 2024 22:31:44 +0100 Subject: 53243: fix use of vi-fetch-history with a range --- ChangeLog | 3 +++ Src/Zle/zle_hist.c | 21 +++++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 0b5c03ce6..31793f1a5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-11-23 Oliver Kiddle + * 53243: Src/Zle/zle_hist.c: fix use of vi-fetch-history + with a range + * 53244: Test/Y01completion.ztst: rename user in test case to reduce likelihood of failure due to user existing diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c index 0fdad70d9..53c722621 100644 --- a/Src/Zle/zle_hist.c +++ b/Src/Zle/zle_hist.c @@ -1758,7 +1758,8 @@ acceptandinfernexthistory(char **args) { Histent he; - if (!(he = infernexthist(hist_ring, args))) + if (virangeflag || !(zlereadflags & ZLRF_HISTORY) || + !(he = infernexthist(hist_ring, args))) return 1; zpushnode(bufstack, ztrdup(he->node.nam)); done = 1; @@ -1770,8 +1771,11 @@ acceptandinfernexthistory(char **args) int infernexthistory(char **args) { - Histent he = quietgethist(histline); + Histent he; + if (virangeflag || !(zlereadflags & ZLRF_HISTORY)) + return 1; + he = quietgethist(histline); if (!he || !(he = infernexthist(he, args))) return 1; zle_setline(he); @@ -1784,12 +1788,14 @@ vifetchhistory(UNUSED(char **args)) { if (zmult < 0) return 1; - if (histline == curhist) { + if (histline == curhist || virangeflag || !(zlereadflags & ZLRF_HISTORY)) { if (!(zmod.flags & MOD_MULT)) { zlecs = zlell; zlecs = findbol(); return 0; } + if (virangeflag || !(zlereadflags & ZLRF_HISTORY)) + return 1; } if (!zle_goto_hist((zmod.flags & MOD_MULT) ? zmult : curhist, 0, 0) && isset(HISTBEEP)) { @@ -1933,6 +1939,9 @@ getvisrchstr(void) int vihistorysearchforward(char **args) { + if (virangeflag || !(zlereadflags & ZLRF_HISTORY)) + return 1; + if (*args) { int ose = visrchsense, ret; char *ost = visrchstr; @@ -1954,6 +1963,9 @@ vihistorysearchforward(char **args) int vihistorysearchbackward(char **args) { + if (virangeflag || !(zlereadflags & ZLRF_HISTORY)) + return 1; + if (*args) { int ose = visrchsense, ret; char *ost = visrchstr; @@ -1979,8 +1991,9 @@ virepeatsearch(UNUSED(char **args)) int n = zmult; char *zt; - if (!visrchstr) + if (!visrchstr || virangeflag || !(zlereadflags & ZLRF_HISTORY)) return 1; + if (zmult < 0) { n = -n; visrchsense = -visrchsense; -- cgit v1.2.3 From 6bb792dba89016c250bc9f2581c9c267dd322254 Mon Sep 17 00:00:00 2001 From: dana Date: Thu, 26 Dec 2024 09:36:45 -0600 Subject: 53257: use monotonic clock where appropriate update the following features to use the monotonic clock for calculating time deltas and intervals: * MAILCHECK parameter * PERIOD parameter * SECONDS parameter * %(nS.t.f) prompt-expansion sequence * time built-in's elapsed time and cpu % values * zsh/zftp ZFTP_TMOUT parameter * zsh/zprof timings also use CLOCK_MONOTONIC_RAW instead of CLOCK_MONOTONIC on macOS --- ChangeLog | 9 ++++++ Doc/Zsh/params.yo | 18 ++++++++---- Doc/Zsh/prompt.yo | 3 +- Src/Modules/zftp.c | 4 +-- Src/Modules/zprof.c | 19 ++++++------ Src/compat.c | 12 ++++++++ Src/exec.c | 21 +++++++------- Src/hist.c | 6 ++-- Src/init.c | 7 ++--- Src/jobs.c | 78 ++++++++++++++++++++++++++++++++++---------------- Src/params.c | 38 ++++++++++++------------ Src/prompt.c | 2 +- Src/signals.c | 3 +- Src/utils.c | 25 ++++++++++++---- Src/zsh.h | 4 +-- Test/A08time.ztst | 39 +++++++++++++++++++++++-- Test/D01prompt.ztst | 7 +++++ Test/D04parameter.ztst | 22 ++++++++++++++ 18 files changed, 225 insertions(+), 92 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index a58002d71..989cc0aa3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2024-12-26 dana + + * 53257: Doc/Zsh/params.yo, Doc/Zsh/prompt.yo, + Src/Modules/zftp.c, Src/Modules/zprof.c, Src/compat.c, + Src/exec.c, Src/hist.c, Src/init.c, Src/jobs.c, Src/params.c, + Src/prompt.c, Src/signals.c, Src/utils.c, Src/zsh.h, + Test/A08time.ztst, Test/D01prompt.ztst, Test/D04parameter.ztst: + use monotonic clock where appropriate + 2024-12-16 dana * 53251: Completion/Unix/Command/_man: fix page completion on diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 02ce796a9..69298855f 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -926,7 +926,9 @@ referenced or seeded in the parent shell in between subshell invocations. ) vindex(SECONDS) item(tt(SECONDS) )( -The number of seconds since shell invocation. If this parameter +The number of seconds since shell invocation. On most platforms, this +is a monotonic value, so it is not affected by NTP time jumps or other +clock changes (though it may be affected by slewing). If this parameter is assigned a value, then the value returned upon reference will be the value that was assigned plus the number of seconds since the assignment. @@ -936,8 +938,10 @@ be changed using the tt(typeset) command. The type may be changed only to one of the floating point types or back to integer. For example, `tt(typeset -F SECONDS)' causes the value to be reported as a floating point number. The -value is available to microsecond accuracy, although the shell may -show more or fewer digits depending on the use of tt(typeset). See +value is nominally available to nanosecond precision, although this +varies by platform (and probably isn't accurate to 1 ns regardless), +and the shell may show more or fewer digits depending on the +use of tt(typeset). See the documentation for the builtin tt(typeset) in ifzman(zmanref(zshbuiltins))\ ifnzman(noderef(Shell Builtin Commands)) for more details. @@ -1735,8 +1739,12 @@ A star may be inserted between the percent sign and flags printing time (e.g., `tt(%*E)'); this causes the time to be printed in `var(hh)tt(:)var(mm)tt(:)var(ss)tt(.)var(ttt)' format (hours and minutes are only printed if they are not zero). -Alternatively, `tt(m)' or `tt(u)' may be used (e.g., `tt(%mE)') to produce -time output in milliseconds or microseconds, respectively. +Alternatively, `tt(m)', `tt(u)', or `tt(n)' may be used (e.g., +`tt(%mE)') to produce time output in milliseconds, microseconds, or +nanoseconds, respectively. Note that some timings on some platforms +are not actually nanosecond-precise (nor accurate to 1 ns when +they are); in fact on many systems user and kernel times are not +even microsecond-precise. ) vindex(TMOUT) item(tt(TMOUT))( diff --git a/Doc/Zsh/prompt.yo b/Doc/Zsh/prompt.yo index de988ab7c..108cb62e5 100644 --- a/Doc/Zsh/prompt.yo +++ b/Doc/Zsh/prompt.yo @@ -195,7 +195,8 @@ sitem(tt(%K))(the hour of the day on the 24-hour clock) sitem(tt(%L))(the hour of the day on the 12-hour clock) endsitem() -In addition, if the system supports the POSIX tt(gettimeofday) system +In addition, if the system supports the POSIX tt(clock_gettime) +or tt(gettimeofday) system call, tt(%.) provides decimal fractions of a second since the epoch with leading zeroes. By default three decimal places are provided, but a number of digits up to 9 may be given following the tt(%); hence tt(%6.) diff --git a/Src/Modules/zftp.c b/Src/Modules/zftp.c index b60e5bf31..230ad86f6 100644 --- a/Src/Modules/zftp.c +++ b/Src/Modules/zftp.c @@ -397,7 +397,7 @@ zfalarm(int tmout) signal(SIGALRM, zfhandler); oalremain = alarm(tmout); if (oalremain) - oaltime = time(NULL); + oaltime = zmonotime(NULL); /* * We'll leave sigtrapped, sigfuncs and TRAPXXX as they are; since the * shell's handler doesn't get the signal, they don't matter. @@ -431,7 +431,7 @@ zfunalarm(void) * I love the way alarm() uses unsigned int while time_t * is probably something completely different. */ - unsigned int tdiff = time(NULL) - oaltime; + time_t tdiff = zmonotime(NULL) - oaltime; alarm(oalremain < tdiff ? 1 : oalremain - tdiff); } else alarm(0); diff --git a/Src/Modules/zprof.c b/Src/Modules/zprof.c index 171a15b90..f5a50effc 100644 --- a/Src/Modules/zprof.c +++ b/Src/Modules/zprof.c @@ -239,8 +239,7 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name) struct sfunc sf, *sp; Pfunc f = NULL; Parc a = NULL; - struct timeval tv; - struct timezone dummy; + struct timespec ts; double prev = 0, now; char *name_for_lookups; @@ -278,19 +277,19 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name) stack = &sf; f->calls++; - tv.tv_sec = tv.tv_usec = 0; - gettimeofday(&tv, &dummy); - sf.beg = prev = ((((double) tv.tv_sec) * 1000.0) + - (((double) tv.tv_usec) / 1000.0)); + ts.tv_sec = ts.tv_nsec = 0; + zgettime_monotonic_if_available(&ts); + sf.beg = prev = ((((double) ts.tv_sec) * 1000.0) + + (((double) ts.tv_nsec) / 1000000.0)); } runshfunc(prog, w, name); if (active) { if (zprof_module && !(zprof_module->node.flags & MOD_UNLOAD)) { - tv.tv_sec = tv.tv_usec = 0; - gettimeofday(&tv, &dummy); + ts.tv_sec = ts.tv_nsec = 0; + zgettime_monotonic_if_available(&ts); - now = ((((double) tv.tv_sec) * 1000.0) + - (((double) tv.tv_usec) / 1000.0)); + now = ((((double) ts.tv_sec) * 1000.0) + + (((double) ts.tv_nsec) / 1000000.0)); f->self += now - sf.beg; for (sp = sf.prev; sp && sp->p != f; sp = sp->prev); if (!sp) diff --git a/Src/compat.c b/Src/compat.c index 8b31ad9f4..918d98e69 100644 --- a/Src/compat.c +++ b/Src/compat.c @@ -136,7 +136,19 @@ zgettime_monotonic_if_available(struct timespec *ts) #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) struct timespec dts; + +/* + * On at least some versions of macOS it appears that CLOCK_MONOTONIC is not + * actually monotonic -- there are reports that it can go backwards. + * CLOCK_MONOTONIC_RAW does not have this problem. On top of that, it is faster + * to read and it has nanosecond precision. We could use it on other systems + * too, but on Linux at least it seems that CLOCK_MONOTONIC is preferred + */ +#if defined(__APPLE__) && defined(CLOCK_MONOTONIC_RAW) + if (clock_gettime(CLOCK_MONOTONIC_RAW, &dts) < 0) { +#else if (clock_gettime(CLOCK_MONOTONIC, &dts) < 0) { +#endif zwarn("unable to retrieve CLOCK_MONOTONIC time: %e", errno); ret--; } else { diff --git a/Src/exec.c b/Src/exec.c index bc07e8c39..874ff41f7 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -348,10 +348,9 @@ setlimits(char *nam) /**/ static pid_t -zfork(struct timeval *tv) +zfork(struct timespec *ts) { pid_t pid; - struct timezone dummy_tz; /* * Is anybody willing to explain this test? @@ -360,8 +359,8 @@ zfork(struct timeval *tv) zerr("job table full"); return -1; } - if (tv) - gettimeofday(tv, &dummy_tz); + if (ts) + zgettime_monotonic_if_available(ts); /* * Queueing signals is necessary on Linux because fork() * manipulates mutexes, leading to deadlock in memory @@ -460,7 +459,7 @@ zfork(struct timeval *tv) int list_pipe = 0, simple_pline = 0; static pid_t list_pipe_pid; -static struct timeval list_pipe_start; +static struct timespec list_pipe_start; static int nowait, pline_level = 0; static int list_pipe_child = 0, list_pipe_job; static char list_pipe_text[JOBTEXTSIZE]; @@ -1863,7 +1862,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) (jobtab[list_pipe_job].stat & STAT_STOPPED)))) { pid_t pid = 0; int synch[2]; - struct timeval bgtime; + struct timespec bgtime; /* * A pipeline with the shell handling the right @@ -2284,7 +2283,7 @@ closemn(struct multio **mfds, int fd, int type) char buf[TCBUFSIZE]; int len, i; pid_t pid; - struct timeval bgtime; + struct timespec bgtime; /* * We need to block SIGCHLD in case the process @@ -2829,7 +2828,7 @@ execcmd_fork(Estate state, int how, int type, Wordcode varspc, pid_t pid; int synch[2], flags; struct entersubsh_ret esret; - struct timeval bgtime; + struct timespec bgtime; child_block(); esret.gleader = -1; @@ -2947,7 +2946,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, * for the "time" keyword */ child_times_t shti, chti; - struct timeval then; + struct timespec then; if (how & Z_TIMED) shelltime(&shti, &chti, &then, 0); @@ -5060,7 +5059,7 @@ getproc(char *cmd, char **eptr) int out = *cmd == Inang; char *pnam; pid_t pid; - struct timeval bgtime; + struct timespec bgtime; #ifndef PATH_DEV_FD int fd; @@ -5149,7 +5148,7 @@ getpipe(char *cmd, int nullexec) Eprog prog; int pipes[2], out = *cmd == Inang; pid_t pid; - struct timeval bgtime; + struct timespec bgtime; char *ends; if (!(prog = parsecmd(cmd, &ends))) diff --git a/Src/hist.c b/Src/hist.c index 1a00c30ed..d0960a284 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -2891,9 +2891,9 @@ flockhistfile(char *fn, int keep_trying) /* * Timeout is ten seconds. */ - end_time = time(NULL) + (time_t)10; + end_time = zmonotime(NULL) + (time_t)10; while (fcntl(flock_fd, F_SETLKW, &lck) == -1) { - if (!keep_trying || time(NULL) >= end_time || + if (!keep_trying || zmonotime(NULL) >= end_time || /* * Randomise wait to minimise clashes with shells exiting at * the same time. @@ -3137,7 +3137,7 @@ static int lockhistct; static int checklocktime(char *lockfile, long *sleep_usp, time_t then) { - time_t now = time(NULL); + time_t now = zmonotime(NULL); if (now + 10 < then) { /* File is more than 10 seconds in the future? */ diff --git a/Src/init.c b/Src/init.c index 61f759ded..378aee348 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1022,7 +1022,6 @@ setupvals(char *cmd, char *runscript, char *zsh_name) #ifdef USE_GETPWUID struct passwd *pswd; #endif - struct timezone dummy_tz; char *ptr; int i, j; #if defined(SITEFPATH_DIR) || defined(FPATH_DIR) || defined (ADDITIONAL_FPATH) || defined(FIXED_FPATH_DIR) @@ -1109,8 +1108,8 @@ setupvals(char *cmd, char *runscript, char *zsh_name) hatchar = '^'; termflags = TERM_UNKNOWN; curjob = prevjob = coprocin = coprocout = -1; - gettimeofday(&shtimer, &dummy_tz); /* init $SECONDS */ - srand((unsigned int)(shtimer.tv_sec + shtimer.tv_usec)); /* seed $RANDOM */ + zgettime_monotonic_if_available(&shtimer); /* init $SECONDS */ + srand((unsigned int)(shtimer.tv_sec + shtimer.tv_nsec)); /* seed $RANDOM */ /* Set default path */ path = (char **) zalloc(sizeof(*path) * 5); @@ -1297,7 +1296,7 @@ setupvals(char *cmd, char *runscript, char *zsh_name) #endif breaks = loops = 0; - lastmailcheck = time(NULL); + lastmailcheck = zmonotime(NULL); locallevel = sourcelevel = 0; sfcontext = SFC_NONE; trap_return = 0; diff --git a/Src/jobs.c b/Src/jobs.c index 39c664388..ad14f6312 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -136,7 +136,7 @@ int numpipestats, pipestats[MAX_PIPESTATS]; /**/ static struct timeval * -dtime(struct timeval *dt, struct timeval *t1, struct timeval *t2) +dtime_tv(struct timeval *dt, struct timeval *t1, struct timeval *t2) { dt->tv_sec = t2->tv_sec - t1->tv_sec; dt->tv_usec = t2->tv_usec - t1->tv_usec; @@ -147,6 +147,21 @@ dtime(struct timeval *dt, struct timeval *t1, struct timeval *t2) return dt; } +/* As above, but with timespecs */ + +/**/ +static struct timespec * +dtime_ts(struct timespec *dt, struct timespec *t1, struct timespec *t2) +{ + dt->tv_sec = t2->tv_sec - t1->tv_sec; + dt->tv_nsec = t2->tv_nsec - t1->tv_nsec; + if (dt->tv_nsec < 0) { + dt->tv_nsec += 1000000000.0; + dt->tv_sec -= 1.0; + } + return dt; +} + /* change job table entry from stopped to running */ /**/ @@ -349,7 +364,6 @@ get_usage(void) void update_process(Process pn, int status) { - struct timezone dummy_tz; #ifdef HAVE_GETRUSAGE struct timeval childs = child_usage.ru_stime; struct timeval childu = child_usage.ru_utime; @@ -360,12 +374,12 @@ update_process(Process pn, int status) /* get time-accounting info */ get_usage(); - gettimeofday(&pn->endtime, &dummy_tz); /* record time process exited */ + zgettime_monotonic_if_available(&pn->endtime); /* record time process exited */ pn->status = status; /* save the status returned by WAIT */ #ifdef HAVE_GETRUSAGE - dtime(&pn->ti.ru_stime, &childs, &child_usage.ru_stime); - dtime(&pn->ti.ru_utime, &childu, &child_usage.ru_utime); + dtime_tv(&pn->ti.ru_stime, &childs, &child_usage.ru_stime); + dtime_tv(&pn->ti.ru_utime, &childu, &child_usage.ru_utime); #else pn->ti.st = shtms.tms_cstime - childs; /* compute process system space time */ pn->ti.ut = shtms.tms_cutime - childu; /* compute process user space time */ @@ -753,7 +767,7 @@ printhhmmss(double secs) } static void -printtime(struct timeval *real, child_times_t *ti, char *desc) +printtime(struct timespec *real, child_times_t *ti, char *desc) { char *s; double elapsed_time, user_time, system_time; @@ -774,21 +788,21 @@ printtime(struct timeval *real, child_times_t *ti, char *desc) } /* go ahead and compute these, since almost every TIMEFMT will have them */ - elapsed_time = real->tv_sec + real->tv_usec / 1000000.0; + elapsed_time = real->tv_sec + real->tv_nsec / 1000000000.0; #ifdef HAVE_GETRUSAGE user_time = ti->ru_utime.tv_sec + ti->ru_utime.tv_usec / 1000000.0; system_time = ti->ru_stime.tv_sec + ti->ru_stime.tv_usec / 1000000.0; total_time = user_time + system_time; percent = 100.0 * total_time - / (real->tv_sec + real->tv_usec / 1000000.0); + / (real->tv_sec + real->tv_nsec / 1000000000.0); #else { long clktck = get_clktck(); user_time = ti->ut / (double) clktck; system_time = ti->st / (double) clktck; percent = 100.0 * (ti->ut + ti->st) - / (clktck * real->tv_sec + clktck * real->tv_usec / 1000000.0); + / (clktck * real->tv_sec + clktck * real->tv_nsec / 1000000000.0); } #endif @@ -844,6 +858,23 @@ printtime(struct timeval *real, child_times_t *ti, char *desc) break; } break; + case 'n': + switch (*++s) { + case 'E': + fprintf(stderr, "%0.fns", elapsed_time * 1000000000.0); + break; + case 'U': + fprintf(stderr, "%0.fns", user_time * 1000000000.0); + break; + case 'S': + fprintf(stderr, "%0.fns", system_time * 1000000000.0); + break; + default: + fprintf(stderr, "%%n"); + s--; + break; + } + break; case '*': switch (*++s) { case 'E': @@ -991,12 +1022,12 @@ static void dumptime(Job jn) { Process pn; - struct timeval dtimeval; + struct timespec dtimespec; if (!jn->procs) return; for (pn = jn->procs; pn; pn = pn->next) - printtime(dtime(&dtimeval, &pn->bgtime, &pn->endtime), &pn->ti, + printtime(dtime_ts(&dtimespec, &pn->bgtime, &pn->endtime), &pn->ti, pn->text); } @@ -1506,7 +1537,7 @@ deletejob(Job jn, int disowning) /**/ void -addproc(pid_t pid, char *text, int aux, struct timeval *bgtime, +addproc(pid_t pid, char *text, int aux, struct timespec *bgtime, int gleader, int list_pipe_job_used) { Process pn, *pnlist; @@ -1894,16 +1925,15 @@ spawnjob(void) /**/ void -shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int delta) +shelltime(child_times_t *shell, child_times_t *kids, struct timespec *then, int delta) { - struct timezone dummy_tz; - struct timeval dtimeval, now; + struct timespec dtimespec, now; child_times_t ti; #ifndef HAVE_GETRUSAGE struct tms buf; #endif - gettimeofday(&now, &dummy_tz); + zgettime_monotonic_if_available(&now); #ifdef HAVE_GETRUSAGE getrusage(RUSAGE_SELF, &ti); @@ -1916,8 +1946,8 @@ shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int d if (shell) { if (delta) { #ifdef HAVE_GETRUSAGE - dtime(&ti.ru_utime, &shell->ru_utime, &ti.ru_utime); - dtime(&ti.ru_stime, &shell->ru_stime, &ti.ru_stime); + dtime_tv(&ti.ru_utime, &shell->ru_utime, &ti.ru_utime); + dtime_tv(&ti.ru_stime, &shell->ru_stime, &ti.ru_stime); #else ti.ut -= shell->ut; ti.st -= shell->st; @@ -1926,15 +1956,15 @@ shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int d *shell = ti; } if (delta) - dtime(&dtimeval, then, &now); + dtime_ts(&dtimespec, then, &now); else { if (then) *then = now; - dtime(&dtimeval, &shtimer, &now); + dtime_ts(&dtimespec, &shtimer, &now); } if (!delta == !shell) - printtime(&dtimeval, &ti, "shell"); + printtime(&dtimespec, &ti, "shell"); #ifdef HAVE_GETRUSAGE getrusage(RUSAGE_CHILDREN, &ti); @@ -1945,8 +1975,8 @@ shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int d if (kids) { if (delta) { #ifdef HAVE_GETRUSAGE - dtime(&ti.ru_utime, &kids->ru_utime, &ti.ru_utime); - dtime(&ti.ru_stime, &kids->ru_stime, &ti.ru_stime); + dtime_tv(&ti.ru_utime, &kids->ru_utime, &ti.ru_utime); + dtime_tv(&ti.ru_stime, &kids->ru_stime, &ti.ru_stime); #else ti.ut -= shell->ut; ti.st -= shell->st; @@ -1955,7 +1985,7 @@ shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int d *kids = ti; } if (!delta == !kids) - printtime(&dtimeval, &ti, "children"); + printtime(&dtimespec, &ti, "children"); } /* see if jobs need printing */ diff --git a/Src/params.c b/Src/params.c index 6f137585b..d1c06b893 100644 --- a/Src/params.c +++ b/Src/params.c @@ -137,11 +137,11 @@ unsigned char hatchar, hashchar; unsigned char keyboardhackchar = '\0'; /* $SECONDS = now.tv_sec - shtimer.tv_sec - * + (now.tv_usec - shtimer.tv_usec) / 1000000.0 + * + (now.tv_nsec - shtimer.tv_nsec) / 1000000000.0 * (rounded to an integer if the parameter is not set to float) */ /**/ -struct timeval shtimer; +struct timespec shtimer; /* 0 if this $TERM setup is usable, otherwise it contains TERM_* flags */ @@ -4496,13 +4496,12 @@ randomsetfn(UNUSED(Param pm), zlong v) zlong intsecondsgetfn(UNUSED(Param pm)) { - struct timeval now; - struct timezone dummy_tz; + struct timespec now; - gettimeofday(&now, &dummy_tz); + zgettime_monotonic_if_available(&now); return (zlong)(now.tv_sec - shtimer.tv_sec - - (now.tv_usec < shtimer.tv_usec ? 1 : 0)); + (now.tv_nsec < shtimer.tv_nsec ? 1 : 0)); } /* Function to set value of special parameter `SECONDS' */ @@ -4511,48 +4510,47 @@ intsecondsgetfn(UNUSED(Param pm)) void intsecondssetfn(UNUSED(Param pm), zlong x) { - struct timeval now; - struct timezone dummy_tz; + struct timespec now; zlong diff; - gettimeofday(&now, &dummy_tz); + zgettime_monotonic_if_available(&now); + diff = (zlong)now.tv_sec - x; shtimer.tv_sec = diff; if ((zlong)shtimer.tv_sec != diff) zwarn("SECONDS truncated on assignment"); - shtimer.tv_usec = now.tv_usec; + shtimer.tv_nsec = now.tv_nsec; } /**/ double floatsecondsgetfn(UNUSED(Param pm)) { - struct timeval now; - struct timezone dummy_tz; + struct timespec now; - gettimeofday(&now, &dummy_tz); + zgettime_monotonic_if_available(&now); return (double)(now.tv_sec - shtimer.tv_sec) + - (double)(now.tv_usec - shtimer.tv_usec) / 1000000.0; + (double)(now.tv_nsec - shtimer.tv_nsec) / 1000000000.0; } /**/ void floatsecondssetfn(UNUSED(Param pm), double x) { - struct timeval now; - struct timezone dummy_tz; + struct timespec now; + + zgettime_monotonic_if_available(&now); - gettimeofday(&now, &dummy_tz); shtimer.tv_sec = now.tv_sec - (zlong)x; - shtimer.tv_usec = now.tv_usec - (zlong)((x - (zlong)x) * 1000000.0); + shtimer.tv_nsec = now.tv_nsec - (zlong)((x - (zlong)x) * 1000000000.0); } /**/ double getrawseconds(void) { - return (double)shtimer.tv_sec + (double)shtimer.tv_usec / 1000000.0; + return (double)shtimer.tv_sec + (double)shtimer.tv_nsec / 1000000000.0; } /**/ @@ -4560,7 +4558,7 @@ void setrawseconds(double x) { shtimer.tv_sec = (zlong)x; - shtimer.tv_usec = (zlong)((x - (zlong)x) * 1000000.0); + shtimer.tv_nsec = (zlong)((x - (zlong)x) * 1000000000.0); } /**/ diff --git a/Src/prompt.c b/Src/prompt.c index e10b05215..f36aba9d3 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -469,7 +469,7 @@ putpromptchar(int doprint, int endchar) test = 1; break; case 'S': - if (time(NULL) - shtimer.tv_sec >= arg) + if (zmonotime(NULL) - shtimer.tv_sec >= arg) test = 1; break; case 'v': diff --git a/Src/signals.c b/Src/signals.c index 86f1a49f6..de42f302d 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -342,8 +342,7 @@ wait_for_processes(void) zwarn("job can't be suspended"); } else { #if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE) - struct timezone dummy_tz; - gettimeofday(&pn->endtime, &dummy_tz); + zgettime_monotonic_if_available(&pn->endtime); #ifdef WIFCONTINUED if (WIFCONTINUED(status)) pn->status = SP_RUNNING; diff --git a/Src/utils.c b/Src/utils.c index ce4e875fd..5c91dfcda 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1570,14 +1570,14 @@ preprompt(void) /* If 1) the parameter PERIOD exists, 2) a hook function for * * "periodic" exists, 3) it's been greater than PERIOD since we * * executed any such hook, then execute it now. */ - if (period && ((zlong)time(NULL) > (zlong)lastperiodic + period) && + if (period && ((zlong)zmonotime(NULL) > (zlong)lastperiodic + period) && !callhookfunc("periodic", NULL, 1, NULL)) - lastperiodic = time(NULL); + lastperiodic = zmonotime(NULL); if (errflag) return; /* Check mail */ - currentmailcheck = time(NULL); + currentmailcheck = zmonotime(NULL); if (mailcheck && (zlong) difftime(currentmailcheck, lastmailcheck) > mailcheck) { char *mailfile; @@ -2761,6 +2761,19 @@ timespec_diff_us(const struct timespec *t1, const struct timespec *t2) return (reverse ? LONG_MIN : LONG_MAX); } +/* Like time(), but uses the monotonic clock */ + +/**/ +mod_export int +zmonotime(time_t *tloc) +{ + struct timespec ts; + zgettime_monotonic_if_available(&ts); + if (tloc) + *tloc = ts.tv_sec; + return ts.tv_sec; +} + /* * Sleep for the given number of microseconds --- must be within * range of a long at the moment, but this is only used for @@ -2794,7 +2807,9 @@ zsleep(long us) /** * Sleep for time (fairly) randomly up to max_us microseconds. - * Don't let the wallclock time extend beyond end_time. + * Don't let the time extend beyond end_time. end_time is compared to + * the current *monotonic* clock time, so do NOT base it on e.g. time(2); + * use zmonotime() or zgettime_monotonic_if_available(). * Return 1 if that seemed to work, else 0. * * For best results max_us should be a multiple of 2**16 or large @@ -2806,7 +2821,7 @@ int zsleep_random(long max_us, time_t end_time) { long r; - time_t now = time(NULL); + time_t now = zmonotime(NULL); /* * Randomish backoff. Doesn't need to be fundamentally diff --git a/Src/zsh.h b/Src/zsh.h index 090abf8f5..85b5c9bdc 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1115,8 +1115,8 @@ struct process { char text[JOBTEXTSIZE]; /* text to print when 'jobs' is run */ int status; /* return code from waitpid/wait3() */ child_times_t ti; - struct timeval bgtime; /* time job was spawned */ - struct timeval endtime; /* time job exited */ + struct timespec bgtime; /* time job was spawned */ + struct timespec endtime; /* time job exited */ }; struct execstack { diff --git a/Test/A08time.ztst b/Test/A08time.ztst index 22a460f5e..4a41cc76a 100644 --- a/Test/A08time.ztst +++ b/Test/A08time.ztst @@ -11,9 +11,44 @@ (time cat) >&/dev/null 0:`time' keyword (status only) - ( TIMEFMT='%E %mE %uE %* %m%mm %u%uu'; time (:) ) + ( TIMEFMT='%E %mE %uE %nE %* %m%mm %u%uu %n%nn'; time (:) ) 0:`time' keyword with custom TIMEFMT -*?[0-9]##.[0-9](#c2)s [0-9]##ms [0-9]##us %\* %m%mm %u%uu +*?[0-9]##.[0-9](#c2)s [0-9]##ms [0-9]##us [0-9]##ns %\* %m%mm %u%uu %n%nn + + ( TIMEFMT='x %U %S %E'; time (:) ) +0:TIMEFMT %[USE] use centisecond precision +*?x( <0-9>.<00-99>s)(#c3) + + ( TIMEFMT='x %*U %*S %*E'; time (:) ) +0:TIMEFMT %*[USE] use millisecond precision +*?x( <0-9>.<000-999>)(#c3) + + ( TIMEFMT='%nU %nS'; time (read -k3 -t0.1) ) +1:TIMEFMT %nU and %nS are limited to microsecond precision +*?[1-9][0-9]#000ns [1-9][0-9]#000ns + +# SECONDS (after - before) must be greater than the elapsed time, but not much +# greater. 25% was picked arbitrarily as something that hopefully will prevent +# the test from failing on slow machines + ( + typeset -F SECONDS + TIMEFMT=%nE + a=$SECONDS + t=$( (time (read -k3 -t0.1)) 2>&1 ) + b=$SECONDS + s=$(( b - a )) + t=$(( ${t%ns}.0 / 10**9 )) + echo $s $t $(( s > t )) $(( t > s - (s * 0.25) )) + ) +0:`time' elapsed time matches SECONDS +*>[0-9.]## [0-9.]## 1 1 + +# Again, the wide range here is an attempt to prevent this test from failing on +# slow machines. We don't care about the exact time, just that it's vaguely sane +# and that each representation has the same basis + ( TIMEFMT='%E %mE %uE %nE %*E'; time (read -k3 -t0.1) ) +1:TIMEFMT elapsed time values +*?0.<10-50>s <10-500>ms <100000-500000>us <100000000-500000000>ns 0.<100-500> time x=1 0:`time' simple assignment diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst index 55861cca1..f42e19714 100644 --- a/Test/D01prompt.ztst +++ b/Test/D01prompt.ztst @@ -68,6 +68,13 @@ print -P '%(?.true.false)' 0:ternary prompt escapes >true +>false + + sec=$SECONDS + eval "print -P '%(${sec}S.true.false)'" + eval "print -P '%($((sec+30))S.true.false)'" +0:ternary prompt escape with test character S +>true >false print -P 'start %10<...1 >1 + # Integer + a=$SECONDS + sleep 1 + b=$SECONDS + print -r - $a $b $(( b > a )) + # Float + typeset -F SECONDS + a=$SECONDS + repeat 10 : + b=$SECONDS + print -r - $a $b $(( b > a )) + # Assignment + a=$SECONDS + SECONDS=8888 + repeat 10 : + b=$SECONDS + print -r - $(( a < 8888 )) $(( b > 8888 )) +0:SECONDS +*>[0-9]## [0-9]## 1 +*>[0-9]##.[0-9]## [0-9]##.[0-9]## 1 +*>1 1 + foo=("|" "?") [[ "|" = ${(j.|.)foo} ]] && print yes || print no [[ "|" = ${(j.|.)~foo} ]] && print yes || print no -- cgit v1.2.3 From d051857e03d474bf7a122148f323bd90d8c16b36 Mon Sep 17 00:00:00 2001 From: dana Date: Thu, 26 Dec 2024 09:39:11 -0600 Subject: 53260: zparseopts: add options -v (argv) and -G (GNU-style parsing) --- ChangeLog | 3 + Doc/Zsh/mod_zutil.yo | 44 ++++++++++-- Src/Modules/zutil.c | 116 +++++++++++++++++++++++++------ Test/V12zparseopts.ztst | 181 ++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 313 insertions(+), 31 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 989cc0aa3..9a40cae5e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2024-12-26 dana + * 53260: Doc/Zsh/mod_zutil.yo, Src/Modules/zutil.c, + Test/V12zparseopts.ztst: add -v and -G options to zparseopts + * 53257: Doc/Zsh/params.yo, Doc/Zsh/prompt.yo, Src/Modules/zftp.c, Src/Modules/zprof.c, Src/compat.c, Src/exec.c, Src/hist.c, Src/init.c, Src/jobs.c, Src/params.c, diff --git a/Doc/Zsh/mod_zutil.yo b/Doc/Zsh/mod_zutil.yo index 9946618d6..76907352f 100644 --- a/Doc/Zsh/mod_zutil.yo +++ b/Doc/Zsh/mod_zutil.yo @@ -228,7 +228,7 @@ item(tt(zregexparse))( This implements some internals of the tt(_regex_arguments) function. ) findex(zparseopts) -item(tt(zparseopts) [ tt(-D) tt(-E) tt(-F) tt(-K) tt(-M) ] [ tt(-a) var(array) ] [ tt(-A) var(assoc) ] [ tt(-) ] var(spec) ...)( +item(tt(zparseopts) [ tt(-D) tt(-E) tt(-F) tt(-G) tt(-K) tt(-M) ] [ tt(-a) var(array) ] [ tt(-A) var(assoc) ] [ tt(-v) var(argv) ] [ tt(-) ] var(spec) ...)( This builtin simplifies the parsing of options in positional parameters, i.e. the set of arguments given by tt($*). Each var(spec) describes one option and must be of the form `var(opt)[tt(=)var(array)]'. If an option @@ -282,19 +282,19 @@ first colon. ) enditem() -In all cases, option-arguments must appear either immediately following the +By default, option-arguments may appear either immediately following the option in the same positional parameter or in the next one. Even an optional argument may appear in the next parameter, unless it begins with a `tt(-)'. -There is no special handling of `tt(=)' as with GNU-style argument parsers; -given the var(spec) `tt(-foo:)', the positional parameter `tt(-)tt(-foo=bar)' -is parsed as `tt(-)tt(-foo)' with an argument of `tt(=bar)'. +Additionally, there is no special consideration given to an `tt(=)' +between an option and its argument. To make parsing use the more common +GNU-style conventions, use the tt(-G) option. When the names of two options that take no arguments overlap, the longest one wins, so that parsing for the var(spec)s `tt(-foo -foobar)' (for example) is unambiguous. However, due to the aforementioned handling of option-arguments, ambiguities may arise when at least one overlapping var(spec) takes an argument, as in `tt(-foo: -foobar)'. In that case, the last matching -var(spec) wins. +var(spec) wins. (This is not an issue with GNU-style parsing.) The options of tt(zparseopts) itself cannot be stacked because, for example, the stack `tt(-DEK)' is indistinguishable from a var(spec) for @@ -338,6 +338,33 @@ Note that the appearance in the positional parameters of an option without its required argument always aborts parsing and returns an error as described above regardless of whether this option is used. ) +item(tt(-G))( +This option specifies that options are parsed in the GNU style, +similar to tt(getopt_long+LPAR()3+RPAR()). In particular: + +Given the var(spec) `tt(-foo:)', the positional parameter +`tt(-)tt(-foo=bar)' is interpreted as option `tt(-)tt(-foo)' with +argument `tt(bar)', rather than argument `tt(=bar)' as is the default, +whilst the positional parameter `tt(-)tt(-foobar)' is considered an +unknown option (unless another var(spec) describes it). This applies +to both singly and doubly hyphenated long options. + +An empty option-argument can be given to its long option in the same +parameter using a trailing `tt(=)'. + +An optional argument to a long option must be given with the +equals syntax, and an optional argument to a short option must +follow it immediately in the same parameter; in both cases the +following parameter is considered unrelated. + +Whenever a long option and its argument are stored in the same array +element, an `tt(=)' is used to separate them. + +A mandatory option-argument given in a separate parameter from its +option (as in `tt(-)tt(-foo) tt(bar)'), or any option-argument given to +a short option in the same parameter, is always treated the same +regardless of whether this option is in effect. +) item(tt(-K))( With this option, the arrays specified with the tt(-a) option and with the `tt(=)var(array)' forms are kept unchanged when none of the var(spec)s for @@ -355,6 +382,11 @@ is found, the values are stored as usual. This changes only the way the values are stored, not the way tt($*) is parsed, so results may be unpredictable if the `var(name)tt(+)' specifier is used inconsistently. ) +item(tt(-v) var(argv))( +With this option, the elements of the named array var(argv) are used as the +positional parameters to parse, rather than those given by tt($*). The +array may be modified according to the tt(-D) option. +) enditem() For example, diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 5eccea7a9..9b2721a09 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -1528,12 +1528,14 @@ struct zoptdesc { Zoptval vals, last; }; -#define ZOF_ARG 1 -#define ZOF_OPT 2 -#define ZOF_MULT 4 -#define ZOF_SAME 8 -#define ZOF_MAP 16 -#define ZOF_CYC 32 +#define ZOF_ARG 1 +#define ZOF_OPT 2 +#define ZOF_MULT 4 +#define ZOF_SAME 8 +#define ZOF_MAP 16 +#define ZOF_CYC 32 +#define ZOF_GNUS 64 +#define ZOF_GNUL 128 struct zoptarr { Zoptarr next; @@ -1568,9 +1570,29 @@ static Zoptdesc lookup_opt(char *str) { Zoptdesc p; - for (p = opt_descs; p; p = p->next) { - if ((p->flags & ZOF_ARG) ? strpfx(p->name, str) : !strcmp(p->name, str)) + /* + * Option takes argument, with GNU-style handling of =. This should only + * be set for long options, though we don't care about that here. Unlike + * the default behaviour, matching is unambiguous + */ + if (p->flags & ZOF_GNUL) { + if (!strcmp(p->name, str) || /* Inefficient, whatever */ + (strpfx(p->name, str) && str[strlen(p->name)] == '=')) + return p; + /* + * Option takes argument, default 'cuddled' style. This is weird with + * long options because we naively accept whatever option has a prefix + * match for the given parameter. Thus if you have specs `-foo:` and + * `-foobar:` (or even `-foobar` with no optarg), without the GNU + * setting, the behaviour depends on the order in which they were + * defined. It is documented to work this way though + */ + } else if (p->flags & ZOF_ARG) { + if (strpfx(p->name, str)) + return p; + /* Option takes no argument */ + } else if (!strcmp(p->name, str)) return p; } return NULL; @@ -1641,10 +1663,13 @@ add_opt_val(Zoptdesc d, char *arg) if (d->arr) d->arr->num += (arg ? 2 : 1); } else if (arg) { - char *s = (char *) zhalloc(strlen(d->name) + strlen(arg) + 2); + /* 3 here is '-' + '=' + NUL */ + char *s = (char *) zhalloc(strlen(d->name) + strlen(arg) + 3); *s = '-'; strcpy(s + 1, d->name); + if (d->flags & ZOF_GNUL) + strcat(s, "="); strcat(s, arg); v->str = s; if (d->arr) @@ -1713,7 +1738,8 @@ static int bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { char *o, *p, *n, **pp, **aval, **ap, *assoc = NULL, **cp, **np; - int del = 0, flags = 0, extract = 0, fail = 0, keep = 0; + char *paramsname = NULL, **params; + int del = 0, flags = 0, extract = 0, fail = 0, gnu = 0, keep = 0; Zoptdesc sopts[256], d; Zoptarr a, defarr = NULL; Zoptval v; @@ -1758,6 +1784,14 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } fail = 1; break; + case 'G': + if (o[2]) { + args--; + o = NULL; + break; + } + gnu = 1; + break; case 'K': if (o[2]) { args--; @@ -1808,6 +1842,20 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) return 1; } break; + case 'v': + if (paramsname) { + zwarnnam(nam, "argv array given more than once"); + return 1; + } + if (o[2]) + paramsname = o + 2; + else if (*args) + paramsname = *args++; + else { + zwarnnam(nam, "missing array name"); + return 1; + } + break; default: /* Anything else is an option description */ args--; @@ -1849,6 +1897,9 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) if (*p == ':') { f |= ZOF_ARG; *p = '\0'; + if (gnu) { + f |= o[1] ? ZOF_GNUL : ZOF_GNUS; + } if (*++p == ':') { p++; f |= ZOF_OPT; @@ -1901,8 +1952,10 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) return 1; } } - np = cp = pp = ((extract && del) ? arrdup(pparams) : pparams); + params = getaparam((paramsname = paramsname ? paramsname : "argv")); + np = cp = pp = ((extract && del) ? arrdup(params) : params); for (; (o = *pp); pp++) { + /* Not an option */ if (*o != '-') { if (extract) { if (del) @@ -1911,6 +1964,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } else break; } + /* '-' or '--', end parsing */ if (!o[1] || (o[1] == '-' && !o[2])) { if (del && extract) *cp++ = o; @@ -1918,6 +1972,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) break; } if (!(d = lookup_opt(o + 1))) { + /* No match for whole param, try each character as a short option */ while (*++o) { if (!(d = sopts[(unsigned char) *o])) { if (fail) { @@ -1931,19 +1986,27 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) break; } if (d->flags & ZOF_ARG) { + /* Optarg in same parameter */ if (o[1]) { add_opt_val(d, o + 1); break; + /* + * Mandatory optarg or (if not GNU style) optional optarg in + * next parameter + */ } else if (!(d->flags & ZOF_OPT) || - (pp[1] && pp[1][0] != '-')) { + (!(d->flags & (ZOF_GNUL | ZOF_GNUS)) && + pp[1] && pp[1][0] != '-')) { if (!pp[1]) { zwarnnam(nam, "missing argument for option: -%s", d->name); return 1; } add_opt_val(d, *++pp); + /* Missing optional optarg */ } else add_opt_val(d, NULL); + /* No optarg required */ } else add_opt_val(d, NULL); } @@ -1956,21 +2019,37 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) break; } } else { + /* Whole param matches a defined option */ if (d->flags & ZOF_ARG) { char *e = o + strlen(d->name) + 1; - if (*e) + /* GNU style allows an empty optarg in the same parameter */ + if ((d->flags & ZOF_GNUL) && *e == '=') { + add_opt_val(d, ++e); + /* + * Non-empty optarg in same parameter. lookup_opt() test ensures + * that this won't be a GNU-style long option, where this would + * be invalid + */ + } else if (*e) { add_opt_val(d, e); - else if (!(d->flags & ZOF_OPT) || - (pp[1] && pp[1][0] != '-')) { + /* + * Mandatory optarg or (if not GNU style) optional optarg in + * next parameter + */ + } else if (!(d->flags & ZOF_OPT) || + (!(d->flags & (ZOF_GNUL | ZOF_GNUS)) && + pp[1] && pp[1][0] != '-')) { if (!pp[1]) { zwarnnam(nam, "missing argument for option: -%s", d->name); return 1; } add_opt_val(d, *++pp); + /* Missing optional optarg */ } else add_opt_val(d, NULL); + /* No optarg required */ } else add_opt_val(d, NULL); } @@ -2041,12 +2120,9 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) if (del) { if (extract) { *cp = NULL; - freearray(pparams); - pparams = zarrdup(np); + setaparam(paramsname, zarrdup(np)); } else { - pp = zarrdup(pp); - freearray(pparams); - pparams = pp; + setaparam(paramsname, zarrdup(pp)); } } return 0; diff --git a/Test/V12zparseopts.ztst b/Test/V12zparseopts.ztst index 816e1d041..a2743ea0e 100644 --- a/Test/V12zparseopts.ztst +++ b/Test/V12zparseopts.ztst @@ -103,6 +103,14 @@ 0:zparseopts -M >ret: 0, optv: --aaa bar, opts: --aaa bar, argv: 1 2 3 + () { + local -a optv argvv=( -a -b -c 1 2 3 ) + zparseopts -D -a optv -v argvv - a b c + print -r - ret: $?, optv: $optv, argvv: $argvv, argv: $argv + } -x -y -z 7 8 9 +0:zparseopts -v +>ret: 0, optv: -a -b -c, argvv: 1 2 3, argv: -x -y -z 7 8 9 + () { local -a optv aa ab zparseopts -a optv - a=aa b:=ab c:- z @@ -151,16 +159,35 @@ >ret: 0, optv: -+ -: -= -\, argv: 1 2 3 >ret: 0, optv: --::: foo, argv: 1 2 3 - for specs in '-foo: -foobar' '-foobar -foo:'; do + for specs in \ + '-foo: -foobar' '-foobar -foo:' '-foo: -foobar:' '-foobar: -foo:' + do + () { + local -a optv + zparseopts -a optv -D - $=specs + print -r - ret: $?, optv: $optv, argv: $argv + } --foobar 1 2 3 + done +0:overlapping option specs without -G (scan order) +>ret: 0, optv: --foobar, argv: 1 2 3 +>ret: 0, optv: --foo bar, argv: 1 2 3 +>ret: 0, optv: --foobar 1, argv: 2 3 +>ret: 0, optv: --foo bar, argv: 1 2 3 + + for specs in \ + '-foo: -foobar' '-foobar -foo:' '-foo: -foobar:' '-foobar: -foo:' + do () { local -a optv - zparseopts -a optv - $=specs + zparseopts -a optv -D -G - $=specs print -r - ret: $?, optv: $optv, argv: $argv } --foobar 1 2 3 done -0:overlapping option specs (scan order) ->ret: 0, optv: --foobar, argv: --foobar 1 2 3 ->ret: 0, optv: --foo bar, argv: --foobar 1 2 3 +0:overlapping option specs with -G (scan order) +>ret: 0, optv: --foobar, argv: 1 2 3 +>ret: 0, optv: --foobar, argv: 1 2 3 +>ret: 0, optv: --foobar 1, argv: 2 3 +>ret: 0, optv: --foobar 1, argv: 2 3 () { local -a optv @@ -170,3 +197,147 @@ 0:missing optarg ?(anon):zparseopts:2: missing argument for option: -c >ret: 1, optv: , argv: -ab1 -c + + for spec in -foo: -foo:- -foo::; do + () { + local -a optv + zparseopts -a optv -D -F - $=spec + print -r - ret: $?, optv: $optv, argv: $argv + } --foo=bar 1 2 3 + done +0:zparseopts without -G, =optarg handling +>ret: 0, optv: --foo =bar, argv: 1 2 3 +>ret: 0, optv: --foo=bar, argv: 1 2 3 +>ret: 0, optv: --foo=bar, argv: 1 2 3 + + for spec in -foo: -foo:- -foo::; do + () { + local -a optv + zparseopts -a optv -D -F -G - $=spec + print -r - ret: $?, optv: $optv, argv: $argv + } --foo=bar 1 2 3 + done +0:zparseopts -G, single parameter, with = +>ret: 0, optv: --foo bar, argv: 1 2 3 +>ret: 0, optv: --foo=bar, argv: 1 2 3 +>ret: 0, optv: --foo=bar, argv: 1 2 3 + + for spec in -foo: -foo:- -foo:: ; do + () { + local -a optv + zparseopts -a optv -D -F -G - $=spec + print -r - ret: $?, optv: $optv, argv: $argv + } --foobar 1 2 3 + done +0:zparseopts -G, single parameter, without = +?(anon):zparseopts:2: bad option: --foobar +>ret: 1, optv: , argv: --foobar 1 2 3 +?(anon):zparseopts:2: bad option: --foobar +>ret: 1, optv: , argv: --foobar 1 2 3 +?(anon):zparseopts:2: bad option: --foobar +>ret: 1, optv: , argv: --foobar 1 2 3 + + for spec in -foo: -foo:- -foo::; do + () { + local -a optv + zparseopts -a optv -D -F -G - $=spec + print -r - ret: $?, optv: $optv, argv: $argv + } --foo bar 1 2 3 + done +0:zparseopts -G, separate parameters +>ret: 0, optv: --foo bar, argv: 1 2 3 +>ret: 0, optv: --foo=bar, argv: 1 2 3 +>ret: 0, optv: --foo, argv: bar 1 2 3 + + for spec in -foo: -foo:- -foo::; do + () { + local -a optv + zparseopts -a optv -D -F -G - $=spec + print -r - ret: $?, optv: ${(j< >)${(q+)optv}}, argv: $argv + } --foo= 1 2 3 + done +0:zparseopts -G, empty optarg +>ret: 0, optv: --foo '', argv: 1 2 3 +>ret: 0, optv: '--foo=', argv: 1 2 3 +>ret: 0, optv: '--foo=', argv: 1 2 3 + + for gopt in '' -G; do + for spec args in \ + f: '-fbar 1 2 3' \ + f: '-f=bar 1 2 3' \ + f: '-f bar 1 2 3' \ + f:- '-fbar 1 2 3' \ + f:- '-f=bar 1 2 3' \ + f:- '-f bar 1 2 3' \ + f:: '-fbar 1 2 3' \ + f:: '-f=bar 1 2 3' \ + f:: '-f bar 1 2 3' + do + () { + local -a optv + zparseopts -a optv -D -F $gopt - $=spec + print -r - ret: $?, gopt: $gopt, optv: $optv, argv: $argv + } $=args + done + done +0:short options, with and without -G +>ret: 0, gopt: , optv: -f bar, argv: 1 2 3 +>ret: 0, gopt: , optv: -f =bar, argv: 1 2 3 +>ret: 0, gopt: , optv: -f bar, argv: 1 2 3 +>ret: 0, gopt: , optv: -fbar, argv: 1 2 3 +>ret: 0, gopt: , optv: -f=bar, argv: 1 2 3 +>ret: 0, gopt: , optv: -fbar, argv: 1 2 3 +>ret: 0, gopt: , optv: -fbar, argv: 1 2 3 +>ret: 0, gopt: , optv: -f=bar, argv: 1 2 3 +>ret: 0, gopt: , optv: -fbar, argv: 1 2 3 +>ret: 0, gopt: -G, optv: -f bar, argv: 1 2 3 +>ret: 0, gopt: -G, optv: -f =bar, argv: 1 2 3 +>ret: 0, gopt: -G, optv: -f bar, argv: 1 2 3 +>ret: 0, gopt: -G, optv: -fbar, argv: 1 2 3 +>ret: 0, gopt: -G, optv: -f=bar, argv: 1 2 3 +>ret: 0, gopt: -G, optv: -fbar, argv: 1 2 3 +>ret: 0, gopt: -G, optv: -fbar, argv: 1 2 3 +>ret: 0, gopt: -G, optv: -f=bar, argv: 1 2 3 +>ret: 0, gopt: -G, optv: -f, argv: bar 1 2 3 + + for gopt in '' -G; do + for spec args in \ + foo: '-foobar 1 2 3' \ + foo: '-foo=bar 1 2 3' \ + foo: '-foo bar 1 2 3' \ + foo:- '-foobar 1 2 3' \ + foo:- '-foo=bar 1 2 3' \ + foo:- '-foo bar 1 2 3' \ + foo:: '-foobar 1 2 3' \ + foo:: '-foo=bar 1 2 3' \ + foo:: '-foo bar 1 2 3' + do + () { + local -a optv + zparseopts -a optv -D -F $gopt - $=spec + print -r - ret: $?, gopt: $gopt, optv: $optv, argv: $argv + } $=args + done + done +0:Sun-style long options, with and without -G +>ret: 0, gopt: , optv: -foo bar, argv: 1 2 3 +>ret: 0, gopt: , optv: -foo =bar, argv: 1 2 3 +>ret: 0, gopt: , optv: -foo bar, argv: 1 2 3 +>ret: 0, gopt: , optv: -foobar, argv: 1 2 3 +>ret: 0, gopt: , optv: -foo=bar, argv: 1 2 3 +>ret: 0, gopt: , optv: -foobar, argv: 1 2 3 +>ret: 0, gopt: , optv: -foobar, argv: 1 2 3 +>ret: 0, gopt: , optv: -foo=bar, argv: 1 2 3 +>ret: 0, gopt: , optv: -foobar, argv: 1 2 3 +?(anon):zparseopts:2: bad option: -f +>ret: 1, gopt: -G, optv: , argv: -foobar 1 2 3 +>ret: 0, gopt: -G, optv: -foo bar, argv: 1 2 3 +>ret: 0, gopt: -G, optv: -foo bar, argv: 1 2 3 +?(anon):zparseopts:2: bad option: -f +>ret: 1, gopt: -G, optv: , argv: -foobar 1 2 3 +>ret: 0, gopt: -G, optv: -foo=bar, argv: 1 2 3 +>ret: 0, gopt: -G, optv: -foo=bar, argv: 1 2 3 +?(anon):zparseopts:2: bad option: -f +>ret: 1, gopt: -G, optv: , argv: -foobar 1 2 3 +>ret: 0, gopt: -G, optv: -foo=bar, argv: 1 2 3 +>ret: 0, gopt: -G, optv: -foo, argv: bar 1 2 3 -- cgit v1.2.3 From 263659acb73d0222e641dfd8d37e48e96582de02 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Fri, 3 Jan 2025 09:18:23 -0800 Subject: 53294: fix multibyte handling in "select" prompts --- ChangeLog | 4 ++++ Src/loop.c | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index c43adf70f..63b9623d6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2025-01-03 Bart Schaefer + + * 53294: Src/loop.c: fix multibyte handling in "select" prompts + 2025-01-03 Jun-ichi Takimoto * 53293: Doc/Zsh/compsys.yo, Doc/Zsh/index.yo, Doc/Zsh/manual.yo: diff --git a/Src/loop.c b/Src/loop.c index 84dc66476..979285abc 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -351,9 +351,16 @@ selectlist(LinkList l, size_t start) zleentry(ZLE_CMD_TRASH); arr = hlinklist2array(l, 0); - for (ap = arr; *ap; ap++) - if (strlen(*ap) > longest) - longest = strlen(*ap); + for (ap = arr; *ap; ap++) { +#ifdef MB_METASTRWIDTH + int aplen = MB_METASTRWIDTH(*ap); +#else + int aplen = 0; + (void) unmetafy(*ap, &aplen); +#endif + if (aplen > longest) + longest = aplen; + } t0 = ct = ap - arr; longest++; while (t0) @@ -368,7 +375,12 @@ selectlist(LinkList l, size_t start) for (t1 = start; t1 != colsz && t1 - start < zterm_lines - 2; t1++) { ap = arr + t1; do { +#ifdef MB_METASTRWIDTH + size_t t2 = MB_METASTRWIDTH(*ap) + 2; + (void) unmetafy(*ap, NULL); +#else size_t t2 = strlen(*ap) + 2; +#endif int t3; fprintf(stderr, "%d) %s", t3 = ap - arr + 1, *ap); -- cgit v1.2.3 From a61b105795620943206c1f85be801943cadbb1c3 Mon Sep 17 00:00:00 2001 From: "Wu, Zhenyu" Date: Tue, 26 Nov 2024 19:39:33 +0800 Subject: github #126: Fix some typos --- ChangeLog | 4 ++++ Completion/Base/Completer/_expand_alias | 2 +- Src/exec.c | 4 ++-- Src/init.c | 2 +- Src/parse.c | 2 +- Src/utils.c | 2 +- Src/zsh.h | 2 +- configure.ac | 4 ++-- 8 files changed, 13 insertions(+), 9 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 02d9739d8..5ccfccd66 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2025-01-27 Oliver Kiddle + * github #126: Wu, Zhenyu: Completion/Base/Completer/_expand_alias, + Src/exec.c, Src/init.c, Src/parse.c, Src/utils.c, Src/zsh.h, + configure.ac: Fix some typos + * github #125: Kouhei Yanagita: Completion/Unix/Command/_ruby: Add completion for irb --no-pager diff --git a/Completion/Base/Completer/_expand_alias b/Completion/Base/Completer/_expand_alias index 8240e4162..0f165a968 100644 --- a/Completion/Base/Completer/_expand_alias +++ b/Completion/Base/Completer/_expand_alias @@ -49,7 +49,7 @@ if [[ -n $tmp ]]; then if [[ $tmpa[1] = $word && $tmp = $aliases[$word] ]]; then # This is an active regular alias and the first word in the result # is the same as what was on the line already. Quote it so - # that it doesn't get reexanded on execution. + # that it doesn't get reexpanded on execution. # # Strictly we also need to check if the original word matches # a later word in the expansion and the previous words are diff --git a/Src/exec.c b/Src/exec.c index 874ff41f7..c1181c5eb 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -1386,7 +1386,7 @@ execlist(Estate state, int dont_change_job, int exiting) *list_pipe_text = '\0'; } - /* Loop over all sets of comands separated by newline, * + /* Loop over all sets of commands separated by newline, * * semi-colon or ampersand (`sublists'). */ code = *state->pc++; if (wc_code(code) != WC_LIST) { @@ -3033,7 +3033,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, /* * preargs contains args that have been expanded by prefork. * Running execcmd_getargs() causes any argument available - * in args to be exanded where necessary and transferred to + * in args to be expanded where necessary and transferred to * preargs. We call execcmd_getargs() every time we need to * analyse an argument not available in preargs, though there is * no guarantee a further argument will be available. diff --git a/Src/init.c b/Src/init.c index 378aee348..75d9dd136 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1282,7 +1282,7 @@ setupvals(char *cmd, char *runscript, char *zsh_name) adjustwinsize(0); #else /* columns and lines are normally zero, unless something different * - * was inhereted from the environment. If either of them are zero * + * was inherited from the environment. If either of them are zero * * the setiparam calls below set them to the defaults from termcap */ setiparam("COLUMNS", zterm_columns); setiparam("LINES", zterm_lines); diff --git a/Src/parse.c b/Src/parse.c index 334365649..8edc701f4 100644 --- a/Src/parse.c +++ b/Src/parse.c @@ -490,7 +490,7 @@ init_parse_status(void) /* * These variables are currently declared by the parser, so we * initialise them here. Possibly they are more naturally declared - * by the lexical anaylser; however, as they are used for signalling + * by the lexical analyser; however, as they are used for signalling * between the two it's a bit ambiguous. We clear them when * using the lexical analyser for strings as well as here. */ diff --git a/Src/utils.c b/Src/utils.c index 5c91dfcda..19fd61a8b 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -2059,7 +2059,7 @@ redup(int x, int y) * Add an fd opened ithin a module. * * fdt is the type of the fd; see the FDT_ definitions in zsh.h. - * The most likely falures are: + * The most likely failures are: * * FDT_EXTERNAL: the fd can be used within the shell for normal I/O but * it will not be closed automatically or by normal shell syntax. diff --git a/Src/zsh.h b/Src/zsh.h index 85b5c9bdc..4e5c02980 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1667,7 +1667,7 @@ enum zpc_chars { ZPC_KSH_BANG, /* ! for !(...) in KSH_GLOB */ ZPC_KSH_BANG2, /* ! for !(...) in KSH_GLOB, untokenised */ ZPC_KSH_AT, /* @ for @(...) in KSH_GLOB */ - ZPC_COUNT /* Number of special chararacters */ + ZPC_COUNT /* Number of special characters */ }; /* diff --git a/configure.ac b/configure.ac index bca99b7f8..b5548c2b9 100644 --- a/configure.ac +++ b/configure.ac @@ -1342,7 +1342,7 @@ dnl Check if tgetent accepts NULL (and will allocate its own termcap buffer) dnl Some termcaps reportedly accept a zero buffer, but then dump core dnl in tgetstr(). dnl Under Cygwin test program crashes but exit code is still 0. So, -dnl we test for a file that porgram should create +dnl we test for a file that program should create AH_TEMPLATE([TGETENT_ACCEPTS_NULL], [Define to 1 if tgetent() accepts NULL as a buffer.]) AC_CACHE_CHECK(if tgetent accepts NULL, @@ -1495,7 +1495,7 @@ $AWK '{ if ($1 ~ /sig/) files[[$1]] = $1 } END { for (var in files) print var }'`" rm -f nametmp.c if test -z "$sigfile_list"; then - dnl In case we don't get the stuff from the preprocesor, use the old + dnl In case we don't get the stuff from the preprocessor, use the old dnl list of standard places. sigfile_list="/usr/include/sys/iso/signal_iso.h /usr/include/bsd/sys/signal.h -- cgit v1.2.3 From 4f3d69e2a0bc6b4d98a4aa3ef37ebea44cbda51f Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Mon, 27 Jan 2025 23:50:27 +0100 Subject: 53329: adapt .zle.sgr for CSI sequences that use : instead of ; --- ChangeLog | 3 +++ Src/Modules/hlgroup.c | 11 ++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index f6b41f94a..d826e6c8b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2025-01-27 Oliver Kiddle + * 53329: Src/Modules/hlgroup.c: adapt .zle.sgr for CSI sequences + that use : instead of ; + * github #128: GI : Completion/Unix/Command/_vim: Updated completion for neovim diff --git a/Src/Modules/hlgroup.c b/Src/Modules/hlgroup.c index 082762623..6c5a4bec4 100644 --- a/Src/Modules/hlgroup.c +++ b/Src/Modules/hlgroup.c @@ -50,9 +50,14 @@ convertattr(char *attrstr, int sgr) char *c = s, *t = s - 1; while (c[0] == '\033' && c[1] == '[') { - c += 2; - while (isdigit(*c) || *c == ';') - *++t = *c++; + for (c += 2; ; c++) { + if (isdigit(*c)) + *++t = *c; + else if (*c == ';' || *c == ':') + *++t = ';'; + else + break; + } t++; if (*c != 'm') break; -- 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') 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 86a0891952f8cd83138bc2b6fe760190c67c8ff9 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Thu, 30 Jan 2025 12:41:53 +0100 Subject: 53335: Remove unused dupstring_glen() function Also make use of the dupstring_wlen() variant in more places to avoid a strlen() --- ChangeLog | 5 +++++ Src/Zle/compcore.c | 22 +++++++++------------- Src/Zle/compctl.c | 4 ++-- Src/Zle/compmatch.c | 4 ++-- Src/Zle/computil.c | 13 ++++--------- Src/hist.c | 5 ++--- Src/string.c | 15 --------------- 7 files changed, 24 insertions(+), 44 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index f96e4f00c..1dce0b334 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2025-01-30 Oliver Kiddle + * 53335: Src/hist.c, Src/string.c, Src/Zle/compcore.c, + Src/Zle/compctl.c, Src/Zle/compmatch.c, Src/Zle/computil.c: + Remove unused dupstring_glen() function and make use of the + dupstring_wlen() variant in more places to avoid a strlen() + * 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 diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 09282d42d..fb703f801 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -708,7 +708,7 @@ callcompfunc(char *s, char *fn) sav = *ss; *ss = '\0'; - tmp = (linwhat == IN_MATH ? dupstring(s) : multiquote(s, 0)); + tmp = (linwhat == IN_MATH ? dupstring_wlen(s, offs) : multiquote(s, 0)); untokenize(tmp); compprefix = ztrdup(tmp); *ss = sav; @@ -1820,7 +1820,7 @@ set_comp_sep(void) */ sav = s[(i = swb - 1 - sqq + dq)]; s[i] = '\0'; - qp = (qttype == QT_SINGLE) ? dupstring(s) : rembslash(s); + qp = (qttype == QT_SINGLE) ? dupstring_wlen(s, i) : rembslash(s); s[i] = sav; if (swe < swb) swe = swb; @@ -2244,10 +2244,10 @@ addmatches(Cadata dat, char **argv) if (dat->aflags & CAF_MATCH) { lipre = dupstring(compiprefix); lisuf = dupstring(compisuffix); - lpre = dupstring(compprefix); - lsuf = dupstring(compsuffix); - llpl = strlen(lpre); - llsl = strlen(lsuf); + llpl = strlen(compprefix); + llsl = strlen(compsuffix); + lpre = dupstring_wlen(compprefix, llpl); + lsuf = dupstring_wlen(compsuffix, llsl); /* This used to reference compqiprefix and compqisuffix, why? */ if (llpl + (int)strlen(qipre) + (int)strlen(lipre) != origlpre @@ -2300,12 +2300,8 @@ addmatches(Cadata dat, char **argv) for (p = lpre + 2; *p && *p != ')'; p++); if (*p == ')') { - char sav = p[1]; - - p[1] = '\0'; - globflag = dupstring(lpre); gfl = p - lpre + 1; - p[1] = sav; + globflag = dupstring_wlen(lpre, gfl); lpre = p + 1; llpl -= gfl; @@ -2731,7 +2727,7 @@ add_match_data(int alt, char *str, char *orig, Cline line, sl = tsl; } if (qisl) { - Cline qsl = bld_parts(dupstring(qisuf), qisl, qisl, NULL, NULL); + Cline qsl = bld_parts(dupstring_wlen(qisuf, qisl), qisl, qisl, NULL, NULL); qsl->flags |= CLF_SUF; qsl->suffix = qsl->prefix; @@ -2814,7 +2810,7 @@ add_match_data(int alt, char *str, char *orig, Cline line, line = p; } if (qipl) { - Cline lp, p = bld_parts(dupstring(qipre), qipl, qipl, &lp, NULL); + Cline lp, p = bld_parts(dupstring_wlen(qipre, qipl), qipl, qipl, &lp, NULL); lp->next = line; line = p; diff --git a/Src/Zle/compctl.c b/Src/Zle/compctl.c index 08355d1b9..de3ccdfce 100644 --- a/Src/Zle/compctl.c +++ b/Src/Zle/compctl.c @@ -3201,8 +3201,8 @@ makecomplistflags(Compctl cc, char *s, int incmd, int compadd) memcpy(lpre, s, lpl); lpre[lpl] = '\0'; qlpre = quotename(lpre); - lsuf = dupstring(s + offs); - lsl = strlen(lsuf); + lsl = strlen(s + offs); + lsuf = dupstring_wlen(s + offs, lsl); qlsuf = quotename(lsuf); /* First check for ~.../... */ diff --git a/Src/Zle/compmatch.c b/Src/Zle/compmatch.c index ddcecd589..b58bd1f05 100644 --- a/Src/Zle/compmatch.c +++ b/Src/Zle/compmatch.c @@ -1161,8 +1161,8 @@ comp_match(char *pfx, char *sfx, char *w, Patprog cp, Cline *clp, int qu, /* We still break it into parts here, trying to build a sensible * cline list for these matches, too. */ - w = dupstring(w); wl = strlen(w); + w = dupstring_wlen(w, wl); *clp = bld_parts(w, wl, wl, NULL, NULL); *exact = 0; } else { @@ -2127,7 +2127,7 @@ cmp_anchors(Cline o, Cline n, int join) (j = join_strs(o->wlen, o->word, n->wlen, n->word))) { o->flags |= CLF_JOIN; o->wlen = strlen(j); - o->word = dupstring(j); + o->word = dupstring_wlen(j, o->wlen); return 2; } diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 360667884..6ac458c91 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -1296,8 +1296,7 @@ parse_cadef(char *nam, char **args) int l = strlen(p) - 1; if (*p == '(' && p[l] == ')') { - axor = p = dupstring(p + 1); - p[l - 1] = '\0'; + axor = p = dupstring_wlen(p + 1, l - 1); } else axor = NULL; if (!*p) { @@ -1317,8 +1316,7 @@ parse_cadef(char *nam, char **args) p = *++args; l = strlen(p) - 1; if (*p == '(' && p[l] == ')') { - axor = p = dupstring(p + 1); - p[l - 1] = '\0'; + axor = p = dupstring_wlen(p + 1, l - 1); } else axor = NULL; if (!*p) { @@ -1339,7 +1337,7 @@ parse_cadef(char *nam, char **args) LinkList list = newlinklist(); LinkNode node; - char **xp, sav; + char **xp; while (*p && *p != ')') { for (p++; inblank(*p); p++); @@ -1351,11 +1349,8 @@ parse_cadef(char *nam, char **args) if (!*p) break; - sav = *p; - *p = '\0'; - addlinknode(list, dupstring(q)); + addlinknode(list, dupstring_wlen(q, p - q)); xnum++; - *p = sav; } /* Oops, end-of-string. */ if (*p != ')') { diff --git a/Src/hist.c b/Src/hist.c index d0960a284..fa1ede3f0 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -3553,9 +3553,8 @@ bufferwords(LinkList list, char *buf, int *index, int flags) } else if (buf) { if (IS_REDIROP(tok) && tokfd >= 0) { char b[20]; - - sprintf(b, "%d%s", tokfd, tokstrings[tok]); - addlinknode(list, dupstring(b)); + int l = sprintf(b, "%d%s", tokfd, tokstrings[tok]); + addlinknode(list, dupstring_wlen(b, l)); num++; } else if (tok != NEWLIN) { addlinknode(list, dupstring(tokstrings[tok])); diff --git a/Src/string.c b/Src/string.c index 5f439926e..0fa13ac0d 100644 --- a/Src/string.c +++ b/Src/string.c @@ -57,21 +57,6 @@ dupstring_wlen(const char *s, unsigned len) return t; } -/* Duplicate string on heap, returning length of string */ - -/**/ -mod_export char * -dupstring_glen(const char *s, unsigned *len_ret) -{ - char *t; - - if (!s) - return NULL; - t = (char *) zhalloc((*len_ret = strlen((char *)s)) + 1); - strcpy(t, s); - return t; -} - /**/ mod_export char * ztrdup(const char *s) -- cgit v1.2.3 From 20990fa7e4bad34bd7e3c145f93f19cb811e8856 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Thu, 30 Jan 2025 12:51:37 +0100 Subject: 53337: allow nameref -p --- ChangeLog | 2 ++ Doc/Zsh/mod_ksh93.yo | 2 +- Src/Modules/ksh93.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 19e5b2497..3ddb16ace 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2025-01-30 Oliver Kiddle + * 53337: Doc/Zsh/mod_ksh93.yo, Src/Modules/ksh93.c: allow nameref -p + * 53336: Doc/Makefile.in, Etc/Makefile.in: avoid GNU make specific use of $< in a non-inference rule diff --git a/Doc/Zsh/mod_ksh93.yo b/Doc/Zsh/mod_ksh93.yo index 7508758aa..7d22064ee 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(-gur) ] var(pname)[tt(=)var(rname)])( +item(tt(nameref) [ tt(-gpur) ] 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/Src/Modules/ksh93.c b/Src/Modules/ksh93.c index 3206c11f3..fa0785cda 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, "gur", "n") + BUILTIN("nameref", BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "gpru", "n") }; #include "zsh.mdh" -- 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') 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 3ce354c049d265371187147dc4541b56fd60d786 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 12 Feb 2025 20:11:47 -0800 Subject: 53360: domenuselect() fails if the previous completion did not generate a list (fix for segfault) --- ChangeLog | 3 +++ Src/Zle/complist.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 2c11f7081..cc1caff26 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2025-02-12 Bart Schaefer + * 53360: Src/Zle/complist.c: domenuselect() fails if the previous + completion did not generate a list (fix for segfault) + * 53346 (cf. 53350): Util/helpfiles: fix use of "man" on file paths * 53348: Doc/Zsh/builtins.yo, Src/builtin.c, Test/K01nameref.ztst: diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 5619160a9..091ad03b1 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -2391,6 +2391,9 @@ domenuselect(Hookdef dummy, Chdata dat) char *s; char status[MAX_STATUS], *modeline = NULL; + if (! hasoldlist) + return 2; + msearchstack = NULL; msearchstr = ""; msearchstate = MS_OK; -- 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') 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 e160cf85f05c3106189edf2158146b9f522d1f1c Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Thu, 27 Feb 2025 16:02:02 +0100 Subject: 53378: support new pipebuf resource limit on FreeBSD Also add other newer limits to the documentation. --- ChangeLog | 5 +++++ Doc/Zsh/builtins.yo | 14 ++++++++++++-- Src/Builtins/rlimits.c | 4 ++++ configure.ac | 1 + 4 files changed, 22 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 05aa89c88..80745557f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2025-02-27 Oliver Kiddle + + * 53378: Doc/Zsh/builtins.yo, Src/Builtins/rlimits.c, configure.ac: + support new pipebuf resource limit on FreeBSD + 2025-02-15 Bart Schaefer * 53363: Src/builtin.c: permit "typeset -n +m pattern" diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 69bb86a0f..103b6d842 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -1218,19 +1218,26 @@ sitem(tt(datasize))(Maximum data size (including stack) for each process.) sitem(tt(descriptors))(Maximum value for a file descriptor.) sitem(tt(filesize))(Largest single file allowed.) sitem(tt(kqueues))(Maximum number of kqueues allocated.) +sitem(tt(maxfilelocks))(Maximum number of file locks.) sitem(tt(maxproc))(Maximum number of processes.) sitem(tt(maxpthreads))(Maximum number of threads per process.) sitem(tt(memorylocked))(Maximum amount of memory locked in RAM.) sitem(tt(memoryuse))(Maximum resident set size.) sitem(tt(msgqueue))(Maximum number of bytes in POSIX message queues.) +sitem(tt(nice))(Maximum nice value.) +sitem(tt(pipebuf))(Maximum size of buffers for pipes/fifos.) sitem(tt(posixlocks))(Maximum number of POSIX locks per user.) sitem(tt(pseudoterminals))(Maximum number of pseudo-terminals.) sitem(tt(resident))(Maximum resident set size.) +sitem(tt(rt_priority))(Maximum real-time priority.) +sitem(tt(rt_time))(Maximum CPU time without a blocking system call.) sitem(tt(sigpending))(Maximum number of pending signals.) sitem(tt(sockbufsize))(Maximum size of all socket buffers.) sitem(tt(stacksize))(Maximum stack size for each process.) sitem(tt(swapsize))(Maximum amount of swap used.) +sitem(tt(umtxp))(Maximum number of POSIX thread library objects.) sitem(tt(vmemorysize))(Maximum amount of virtual memory.) +sitem(tt(vnodemonitors))(Maximum number of open vnode monitors.) endsitem() Which of these resource limits are available depends on the system. @@ -2390,16 +2397,18 @@ Not all the following resources are supported on all systems. Running tt(ulimit -a) will show which are supported. startsitem() -sitem(tt(-a))(Lists all of the current resource limits.) +sitem(tt(-a))(List all of the current resource limits.) sitem(tt(-b))(Socket buffer size in bytes LPAR()N.B. not kilobytes+RPAR()) sitem(tt(-c))(512-byte blocks on the size of core dumps.) sitem(tt(-d))(Kilobytes on the size of the data segment.) +sitem(tt(-e))(Maximum nice value.) sitem(tt(-f))(512-byte blocks on the size of files written.) sitem(tt(-i))(The number of pending signals.) sitem(tt(-k))(The number of kqueues allocated.) sitem(tt(-l))(Kilobytes on the size of locked-in memory.) sitem(tt(-m))(Kilobytes on the size of physical memory.) -sitem(tt(-n))(open file descriptors.) +sitem(tt(-n))(Open file descriptors.) +sitem(tt(-o))(Maximum number of POSIX thread library objects.) sitem(tt(-p))(The number of pseudo-terminals.) sitem(tt(-q))(Bytes in POSIX message queues.) sitem(tt(-r))(Maximum real time priority. On some systems where this @@ -2413,6 +2422,7 @@ sitem(tt(-v))(Kilobytes on the size of virtual memory. On some systems this refers to the limit called `address space'.) sitem(tt(-w))(Kilobytes on the size of swapped out memory.) sitem(tt(-x))(The number of locks on files.) +sitem(tt(-y))(Maximum size of buffers for pipes/fifos.) endsitem() A resource may also be specified by integer in the form `tt(-N) diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c index f25dd2530..65226dc9a 100644 --- a/Src/Builtins/rlimits.c +++ b/Src/Builtins/rlimits.c @@ -145,6 +145,10 @@ static const resinfo_T known_resources[] = { {RLIMIT_UMTXP, "umtxp", ZLIMTYPE_NUMBER, 1, 'o', "umtx shared locks"}, # endif +# ifdef HAVE_RLIMIT_PIPEBUF /* FreeBSD */ + {RLIMIT_PIPEBUF, "pipebuf", ZLIMTYPE_MEMORY, 1024, + 'y', "size of buffers for pipes/fifos"}, +#endif # ifdef HAVE_RLIMIT_POSIXLOCKS /* DragonFly */ {RLIMIT_POSIXLOCKS, "posixlocks", ZLIMTYPE_NUMBER, 1, diff --git a/configure.ac b/configure.ac index b5548c2b9..db0828a56 100644 --- a/configure.ac +++ b/configure.ac @@ -1873,6 +1873,7 @@ zsh_LIMIT_PRESENT(RLIMIT_NPTS) zsh_LIMIT_PRESENT(RLIMIT_SWAP) zsh_LIMIT_PRESENT(RLIMIT_KQUEUES) zsh_LIMIT_PRESENT(RLIMIT_UMTXP) +zsh_LIMIT_PRESENT(RLIMIT_PIPEBUF) zsh_LIMIT_PRESENT(RLIMIT_NOVMON) zsh_LIMITS_EQUAL(VMEM, vmem, RSS, rss) -- cgit v1.2.3 From d315401a032cfada4ff733b20d708a5e76d75e48 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 2 Mar 2025 11:23:22 -0800 Subject: 53368: ignore no-op changes to EMACS/VI options --- ChangeLog | 4 ++++ Src/options.c | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 8cc81f4b4..340a39479 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2025-03-02 Bart Schaefer + + * 53368: Src/options.c: ignore no-op changes to EMACS/VI options + 2025-02-28 Oliver Kiddle * 53395: Completion/Unix/Type/_zfs_dataset, diff --git a/Src/options.c b/Src/options.c index 8b37ab5e8..696ab5c69 100644 --- a/Src/options.c +++ b/Src/options.c @@ -887,8 +887,14 @@ dosetopt(int optno, int value, int force, char *new_opts) acquire_pgrp(); } } else if ((optno == EMACSMODE || optno == VIMODE) && value) { + /* What's going on here: + * 1) unsetopt of either emacs or vi is an effective no-op. + * 2) setopt of either emacs or vi toggles off the other. + * Hence we disallow changing these options in emulation mode, + * but only if the change is setopt rather than unsetopt. + */ if (sticky && sticky->emulation) - return -1; + return opts[optno] ? 0 : -1; zleentry(ZLE_CMD_SET_KEYMAP, optno); new_opts[(optno == EMACSMODE) ? VIMODE : EMACSMODE] = 0; } else if (optno == SUNKEYBOARDHACK) { -- cgit v1.2.3 From 71f35afa1cd48d6c25a517797e543cb2aa23a47b Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Mon, 3 Mar 2025 01:15:00 +0100 Subject: 53394: drop OSC and DCS sequences in key input They are likely to be terminal query responses not keys. If they do match a keybinding that's not affected. Also does likewise for certain CSI sequences. --- ChangeLog | 5 +++++ Src/Zle/zle_keymap.c | 28 ++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 340a39479..926f36d58 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2025-03-03 Oliver Kiddle + + * 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 + 2025-03-02 Bart Schaefer * 53368: Src/options.c: ignore no-op changes to EMACS/VI options diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c index 5012917f5..ce52abe66 100644 --- a/Src/Zle/zle_keymap.c +++ b/Src/Zle/zle_keymap.c @@ -1580,7 +1580,7 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp) Thingy func = t_undefinedkey; char *str = NULL; int lastlen = 0, lastc = lastchar; - int timeout = 0, csi = 0; + int timeout = 0, csi = 0, oscdcs = 0; keybuflen = 0; keybuf[0] = 0; @@ -1647,14 +1647,38 @@ getkeymapcmd(Keymap km, Thingy *funcp, char **strp) if (keybuf[keybuflen - 1] >= 0x40 && keybuf[keybuflen - 1] <= 0x7e && lastlen > csi - 2 && lastlen <= csi) { + if (keybuf[csi] == '?' && (keybuf[keybuflen - 1] == 'c' || + keybuf[keybuflen - 1] == 'u')) + { /* is a terminal query response - discard */ + keybuflen = csi - 2; + timeout = csi = 0; + continue; + } func = t_undefinedkey; lastlen = keybuflen; } csi = 0; } } + /* An OSC or DCS sequence is likely a late arriving terminal query + * response. Keep looping; if we reach an ST, discard the sequence + * - unless we first match a keybinding or a keytimeout elapses. */ + if (oscdcs) { + if (keybuf[keybuflen - 1] == '\007' || /* BEL sometimes used */ + (keybuf[keybuflen - 2] == '\033' && + keybuf[keybuflen - 1] == '\\') || + (keybuf[keybuflen - 2] == Meta && /* ST can be 0x9b */ + (unsigned char) keybuf[keybuflen - 1] == (0x9b ^ 32))) + { + keybuflen = oscdcs - 2; /* discard */ + timeout = oscdcs = 0; + continue; + } + } else if (keybuflen >= 2 && keybuf[keybuflen - 2] == '\033' && + (keybuf[keybuflen - 1] == ']' || keybuf[keybuflen - 1] == 'P')) + oscdcs = keybuflen; - if (!ispfx && !csi) + if (!ispfx && !csi && !oscdcs) break; } if(!lastlen && keybuflen) -- 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') 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 From e7163e69d90f8dcd5cdeea054df929b635f89260 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 31 Mar 2025 15:25:34 -0700 Subject: 53431: fix assignment via named reference to parameters in outer scopes --- ChangeLog | 5 ++++- Src/params.c | 6 +++++- Test/K01nameref.ztst | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index ed79feb18..af75d0ea7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,7 @@ -2025-03-31 Bart Schaefer +2025-03-31 Bart Schaefer + + * 53431: Src/params.c, Test/K01nameref.ztst: fix assignment via + named reference to parameters in outer scopes; add tests * Frank Dana: 53414: Functions/Prompts/prompt_restore_setup: attempting to preview the "restore" keyword is nonsensical, diff --git a/Src/params.c b/Src/params.c index d1c06b893..c10236a0d 100644 --- a/Src/params.c +++ b/Src/params.c @@ -6395,7 +6395,9 @@ setscope(Param pm) } } else pm->base = basepm->level; - } + } else if (pm->base < locallevel && refname && + (basepm = (Param)getparamnode(realparamtab, refname))) + pm->base = basepm->level; if (pm->base > pm->level) { if (EMULATION(EMULATE_KSH)) { zerr("%s: global reference cannot refer to local variable", @@ -6420,6 +6422,8 @@ upscope(Param pm, int reflevel) { Param up = pm->old; while (up && up->level >= reflevel) { + if (reflevel < 0 && up->level < locallevel) + break; pm = up; up = up->old; } diff --git a/Test/K01nameref.ztst b/Test/K01nameref.ztst index bacc3ade2..1603ab1b9 100644 --- a/Test/K01nameref.ztst +++ b/Test/K01nameref.ztst @@ -595,6 +595,53 @@ F:Same test, should part 5 output look like this? >nameref-local-nameref-local >typeset -h parameters + ( + inner() { local -n var="${1:?}"; var=(alpha beta gamma); } + outer() { local -a foo=(outer); inner foo; typeset -p foo; } + foo=3 ; { outer foo } always { typeset -p foo } + ) +0:up-reference part 10, assignment to enclosing scope, types match +>typeset -a foo=( alpha beta gamma ) +>typeset -g foo=3 + + ( + inner() { local -n var="${1:?}"; var=(alpha beta gamma); } + outer() { local foo=outer; inner foo; typeset -p foo; } + foo=3 ; { outer foo } always { typeset -p foo } + ) +1:up-reference part 11, assignment to enclosing scope, type mismatch +>typeset -g foo=3 +?inner: foo: attempt to assign array value to non-array + + ( + inner() { local -n var="${1:?}"; unset var; var=(alpha beta gamma); } + outer() { local foo=outer; inner foo; typeset -p foo; } + foo=3 ; { outer foo } always { typeset -p foo } + ) +0:up-reference part 12, assignment to enclosing scope, unset by reference +>typeset -a foo=( alpha beta gamma ) +>typeset -g foo=3 + + ( + inner() { local "${1:?}"; local -nu var="$1"; var=(alpha beta gamma); } + outer() { local -a foo=(outer); inner foo; typeset -p foo; } + foo=3 ; { outer foo } always { typeset -p foo } + ) +0:up-reference part 13, assignment to enclosing scope, skip local +>typeset -a foo=( alpha beta gamma ) +>typeset -g foo=3 + + ( + inner() { local "${1:?}"; local -nu var="$1"; + typeset -g var=(alpha beta gamma); } + outer() { local -a foo=(outer); inner foo; typeset -p foo; } + foo=3 ; { outer foo } always { typeset -p foo } + ) +0f:up-reference part 14, typeset -g to enclosing scope, skip local +F:typeset cannot bypass a name in the local scope, even via nameref +>typeset -a foo=( alpha beta gamma ) +>typeset -g foo=3 + if [[ $options[typesettounset] != on ]]; then ZTST_skip='Ignoring zmodload bug that resets TYPESET_TO_UNSET' setopt typesettounset -- cgit v1.2.3 From bacc78ec3f9d75ff242d0592b2f44484e1198801 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Tue, 15 Apr 2025 10:03:23 -0700 Subject: 53454: fix interrupt handling in savehistfile() --- ChangeLog | 4 ++++ Src/hist.c | 13 ++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index eb2924d45..4b4913252 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2025-04-15 Bart Schaefer + + * 53454: Src/hist.c: fix interrupt handling in savehistfile() + 2025-04-15 Eric Cook * 53485: NEWS: update for version 5.10 diff --git a/Src/hist.c b/Src/hist.c index fa1ede3f0..00bdbb2b8 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -2838,11 +2838,12 @@ readhistfile(char *fn, int err, int readflags) */ if (uselex || remeta) freeheap(); - if (errflag & ERRFLAG_INT) { - /* Can't assume fast read next time if interrupted. */ - lasthist.interrupted = 1; + if (errflag & ERRFLAG_INT) break; - } + } + if (errflag & ERRFLAG_INT) { + /* Can't assume fast read next time if interrupted. */ + lasthist.interrupted = 1; } if (start && readflags & HFILE_USE_OPTIONS) { zsfree(lasthist.text); @@ -3108,7 +3109,9 @@ savehistfile(char *fn, int err, int writeflags) hist_ignore_all_dups |= isset(HISTSAVENODUPS); readhistfile(fn, err, 0); hist_ignore_all_dups = isset(HISTIGNOREALLDUPS); - if (histlinect) + if (errflag & ERRFLAG_INT) + ret = -1; + else if (histlinect) savehistfile(fn, err, 0); pophiststack(); -- cgit v1.2.3 From 494fcd1799d4d2d236d3183de12b0c99ceb83b1c Mon Sep 17 00:00:00 2001 From: dana Date: Sun, 13 Apr 2025 17:15:53 -0500 Subject: 53482: zparseopts -G: always add options with optional args to array with = --- ChangeLog | 6 ++++++ Src/Modules/zutil.c | 6 +++--- Test/V12zparseopts.ztst | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 4b4913252..39b488e14 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2025-04-19 dana + + * 53482: Src/Modules/zutil.c, + Src/Modules/Test/V12zparseopts.ztst: with `zparseopts -G`, + always add options options with optional args to array with = + 2025-04-15 Bart Schaefer * 53454: Src/hist.c: fix interrupt handling in savehistfile() diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 9b2721a09..ef99303d2 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -1662,15 +1662,15 @@ add_opt_val(Zoptdesc d, char *arg) v->str = NULL; if (d->arr) d->arr->num += (arg ? 2 : 1); - } else if (arg) { + } else if (arg || d->flags & ZOF_GNUL) { /* 3 here is '-' + '=' + NUL */ - char *s = (char *) zhalloc(strlen(d->name) + strlen(arg) + 3); + char *s = (char *) zhalloc(strlen(d->name) + strlen(arg ? arg : "") + 3); *s = '-'; strcpy(s + 1, d->name); if (d->flags & ZOF_GNUL) strcat(s, "="); - strcat(s, arg); + strcat(s, arg ? arg : ""); v->str = s; if (d->arr) d->arr->num += 1; diff --git a/Test/V12zparseopts.ztst b/Test/V12zparseopts.ztst index a2743ea0e..e465d0e0c 100644 --- a/Test/V12zparseopts.ztst +++ b/Test/V12zparseopts.ztst @@ -247,7 +247,7 @@ 0:zparseopts -G, separate parameters >ret: 0, optv: --foo bar, argv: 1 2 3 >ret: 0, optv: --foo=bar, argv: 1 2 3 ->ret: 0, optv: --foo, argv: bar 1 2 3 +>ret: 0, optv: --foo=, argv: bar 1 2 3 for spec in -foo: -foo:- -foo::; do () { @@ -340,4 +340,4 @@ ?(anon):zparseopts:2: bad option: -f >ret: 1, gopt: -G, optv: , argv: -foobar 1 2 3 >ret: 0, gopt: -G, optv: -foo=bar, argv: 1 2 3 ->ret: 0, gopt: -G, optv: -foo, argv: bar 1 2 3 +>ret: 0, gopt: -G, optv: -foo=, argv: bar 1 2 3 -- cgit v1.2.3 From 8c3c45732131433645686cdb6bbbb8974230c5a9 Mon Sep 17 00:00:00 2001 From: dana Date: Sun, 13 Apr 2025 18:00:54 -0500 Subject: 53483: zparseopts -G: accept only '--' as parsing terminator --- ChangeLog | 4 ++++ Doc/Zsh/mod_zutil.yo | 14 +++++++++----- Src/Modules/zutil.c | 6 +++--- Test/V12zparseopts.ztst | 25 +++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 8 deletions(-) (limited to 'Src') diff --git a/ChangeLog b/ChangeLog index 39b488e14..73127d0f2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2025-04-19 dana + * 53483: Doc/Zsh/mod_zutil.yo, Src/Modules/zutil.c, + Test/V12zparseopts.ztst: with `zparseopts -G`, accept only + '--' as parsing terminator + * 53482: Src/Modules/zutil.c, Src/Modules/Test/V12zparseopts.ztst: with `zparseopts -G`, always add options options with optional args to array with = diff --git a/Doc/Zsh/mod_zutil.yo b/Doc/Zsh/mod_zutil.yo index 76907352f..19f9989f4 100644 --- a/Doc/Zsh/mod_zutil.yo +++ b/Doc/Zsh/mod_zutil.yo @@ -242,8 +242,8 @@ Note that it is an error to give any var(spec) without an Unless the tt(-E) option is given, parsing stops at the first string that isn't described by one of the var(spec)s. Even with tt(-E), -parsing always stops at a positional parameter equal to `tt(-)' or -`tt(-)tt(-)'. See also tt(-F). +parsing always stops at a positional parameter equal to `tt(-)tt(-)' or +(without tt(-G)) `tt(-)'. See also tt(-F). The var(opt) description must be one of the following. Any of the special characters can appear in the option name provided it is preceded by a @@ -314,9 +314,9 @@ as the values. item(tt(-D))( If this option is given, all options found are removed from the positional parameters of the calling shell or shell function, up to but not including -any not described by the var(spec)s. If the first such parameter is `tt(-)' -or `tt(-)tt(-)', it is removed as well. This is similar to using the -tt(shift) builtin. +any not described by the var(spec)s. If the first such parameter is +`tt(-)tt(-)' or (without tt(-G)) `tt(-)', it is removed as well. This is +similar to using the tt(shift) builtin. ) item(tt(-E))( This changes the parsing rules to em(not) stop at the first string @@ -364,6 +364,10 @@ A mandatory option-argument given in a separate parameter from its option (as in `tt(-)tt(-foo) tt(bar)'), or any option-argument given to a short option in the same parameter, is always treated the same regardless of whether this option is in effect. + +Lastly, when this option is active, only `tt(-)tt(-)' is treated as an +explicit option-parsing terminator in the parsed arguments; a single +`tt(-)' is considered a normal operand. ) item(tt(-K))( With this option, the arrays specified with the tt(-a) option and with the diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index ef99303d2..676fe1872 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -1955,8 +1955,8 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) params = getaparam((paramsname = paramsname ? paramsname : "argv")); np = cp = pp = ((extract && del) ? arrdup(params) : params); for (; (o = *pp); pp++) { - /* Not an option */ - if (*o != '-') { + /* Not an option. With GNU style, this includes '-' */ + if (*o != '-' || (gnu && !o[1])) { if (extract) { if (del) *cp++ = o; @@ -1964,7 +1964,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } else break; } - /* '-' or '--', end parsing */ + /* '--' or (with non-GNU style, see above) '-', end parsing */ if (!o[1] || (o[1] == '-' && !o[2])) { if (del && extract) *cp++ = o; diff --git a/Test/V12zparseopts.ztst b/Test/V12zparseopts.ztst index e465d0e0c..e6139ea5e 100644 --- a/Test/V12zparseopts.ztst +++ b/Test/V12zparseopts.ztst @@ -341,3 +341,28 @@ >ret: 1, gopt: -G, optv: , argv: -foobar 1 2 3 >ret: 0, gopt: -G, optv: -foo=bar, argv: 1 2 3 >ret: 0, gopt: -G, optv: -foo=, argv: bar 1 2 3 + + for term in - --; do + # With -D -E -G + () { + local -a optv + zparseopts -a optv -D -E -F -G - -foo -bar + print -r - ret: $?, term: $term, optv: $optv, argv: $argv + } --foo x --bar $term --baz + for gopt in '' -G; do + # With -D + with/without -G + () { + local -a optv + zparseopts -a optv -D -F $gopt - -foo -bar + print -r - ret: $?, term: $term, gopt: $gopt, optv: $optv, argv: $argv + } --foo $term --bar + done + done +0:only -- acts as explicit parsing terminator with -G +?(anon):zparseopts:2: bad option: --baz +>ret: 1, term: -, optv: , argv: --foo x --bar - --baz +>ret: 0, term: -, gopt: , optv: --foo, argv: --bar +>ret: 0, term: -, gopt: -G, optv: --foo, argv: - --bar +>ret: 0, term: --, optv: --foo --bar, argv: x -- --baz +>ret: 0, term: --, gopt: , optv: --foo, argv: --bar +>ret: 0, term: --, gopt: -G, optv: --foo, argv: --bar -- cgit v1.2.3