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 --- Src/Modules/param_private.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'Src/Modules') 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/Modules') 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/Modules') 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 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/Modules') 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 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/Modules') 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 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/Modules') diff --git a/ChangeLog b/ChangeLog index 773971c5a..b3518f1bf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2023-01-10 Oliver Kiddle + * 51258, 51272: Src/Modules/watch.c, Src/Zle/complist.c, + Src/Zle/zle.h, Src/Zle/zle_main.c, Src/Zle/zle_refresh.c, + Src/Zle/zle_tricky.c, Src/Zle/zle_utils.c, Src/builtin.c, + Src/init.c, Src/input.c, Src/loop.c, Src/prompt.c, + Src/subst.c, Src/utils.c, Src/zsh.h, Test/D01prompt.ztst, + Test/X04zlehighlight.ztst: refactor handling of terminal + attributes, removing OFF flags in zattr + * Nathan Houghton: 51276: Completion/Unix/Type/_diff_options: Fix diff completion for non GNU / FreeBSD platforms diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c index d45c3cf3d..0de8cbf9a 100644 --- a/Src/Modules/watch.c +++ b/Src/Modules/watch.c @@ -255,8 +255,10 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) while (*fmt) if (*fmt == '\\') { if (*++fmt) { - if (prnt) + if (prnt) { + applytextattributes(TSC_RAW); putchar(*fmt); + } ++fmt; } else if (fini) return fmt; @@ -266,8 +268,10 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) else if (*fmt == fini) return ++fmt; else if (*fmt != '%') { - if (prnt) + if (prnt) { + applytextattributes(TSC_RAW); putchar(*fmt); + } ++fmt; } else { if (*++fmt == BEGIN3) @@ -277,12 +281,15 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) else switch (*(fm2 = fmt++)) { case 'n': + applytextattributes(TSC_RAW); printf("%.*s", (int)sizeof(u->ut_name), u->ut_name); break; case 'a': + applytextattributes(TSC_RAW); printf("%s", (!inout) ? "logged off" : "logged on"); break; case 'l': + applytextattributes(TSC_RAW); if (!strncmp(u->ut_line, "tty", 3)) printf("%.*s", (int)sizeof(u->ut_line) - 3, u->ut_line + 3); else @@ -290,6 +297,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) break; # ifdef WATCH_UTMP_UT_HOST case 'm': + applytextattributes(TSC_RAW); for (p = u->ut_host, i = sizeof(u->ut_host); i && *p; i--, p++) { if (*p == '.' && !idigit(p[1])) break; @@ -297,6 +305,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) } break; case 'M': + applytextattributes(TSC_RAW); printf("%.*s", (int)sizeof(u->ut_host), u->ut_host); break; # endif /* WATCH_UTMP_UT_HOST */ @@ -343,9 +352,11 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) len = ztrftime(buf, 40, fm2, tm, 0L); if (len > 0) metafy(buf, len, META_NOALLOC); + applytextattributes(TSC_RAW); printf("%s", (*buf == ' ') ? buf + 1 : buf); break; case '%': + applytextattributes(TSC_RAW); putchar('%'); break; case 'F': @@ -354,16 +365,13 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) atr = match_colour((const char**)&fmt, 1, 0); if (*fmt == '}') fmt++; - if (!(atr & (TXT_ERROR | TXTNOFGCOLOUR))) { - txtunset(TXT_ATTR_FG_COL_MASK); - txtset(atr & TXT_ATTR_FG_ON_MASK); - set_colour_attribute(atr, COL_SEQ_FG, TSC_RAW); + if (atr && atr != TXT_ERROR) { + tsetattrs(atr); + break; } - } - break; + } /* fall-through */ case 'f': - txtunset(TXT_ATTR_FG_ON_MASK); - set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_RAW); + tunsetattrs(TXTFGCOLOUR); break; case 'K': if (*fmt == '{') { @@ -371,49 +379,43 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) atr = match_colour((const char**)&fmt, 0, 0); if (*fmt == '}') fmt++; - if (!(atr & (TXT_ERROR | TXTNOBGCOLOUR))) { - txtunset(TXT_ATTR_BG_COL_MASK); - txtset(atr & TXT_ATTR_BG_ON_MASK); - set_colour_attribute(atr, COL_SEQ_BG, TSC_RAW); + if (atr && atr != TXT_ERROR) { + tsetattrs(atr); + break; } - } - break; + } /* fall-through */ case 'k': - txtunset(TXT_ATTR_BG_ON_MASK); - set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_RAW); + tunsetattrs(TXTBGCOLOUR); break; case 'S': - txtset(TXTSTANDOUT); - tsetcap(TCSTANDOUTBEG, TSC_RAW); + tsetattrs(TXTSTANDOUT); break; case 's': - txtunset(TXTSTANDOUT); - tsetcap(TCSTANDOUTEND, TSC_RAW|TSC_DIRTY); + tunsetattrs(TXTSTANDOUT); break; case 'B': - txtset(TXTBOLDFACE); - tsetcap(TCBOLDFACEBEG, TSC_RAW|TSC_DIRTY); + tsetattrs(TXTBOLDFACE); break; case 'b': - txtunset(TXTBOLDFACE); - tsetcap(TCALLATTRSOFF, TSC_RAW|TSC_DIRTY); + tunsetattrs(TXTBOLDFACE); break; case 'U': - txtset(TXTUNDERLINE); - tsetcap(TCUNDERLINEBEG, TSC_RAW); + tsetattrs(TXTUNDERLINE); break; case 'u': - txtunset(TXTUNDERLINE); - tsetcap(TCUNDERLINEEND, TSC_RAW|TSC_DIRTY); + tunsetattrs(TXTUNDERLINE); break; default: + applytextattributes(TSC_RAW); putchar('%'); putchar(*fm2); break; } } - if (prnt) + if (prnt) { + applytextattributes(TSC_RAW); putchar('\n'); + } return fmt; } diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c index 6e0eac31f..8bdf1bb29 100644 --- a/Src/Zle/complist.c +++ b/Src/Zle/complist.c @@ -1072,7 +1072,7 @@ static int compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) { char *p, nc[2*DIGBUFSIZE + 12], nbuf[2*DIGBUFSIZE + 12]; - int l = 0, cc = 0, b = 0, s = 0, u = 0, m, ask, beg, stat; + int l = 0, cc = 0, m, ask, beg, stat; if ((stat = !fmt)) { if (mlbeg >= 0) { @@ -1118,48 +1118,46 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) m = 0; switch (cchar) { case ZWC('%'): - if (dopr == 1) + if (dopr == 1) { + applytextattributes(0); putc('%', shout); + } cc++; break; case ZWC('n'): if (!stat) { sprintf(nc, "%d", n); - if (dopr == 1) + if (dopr == 1) { + applytextattributes(0); fputs(nc, shout); + } /* everything here is ASCII... */ cc += strlen(nc); } break; case ZWC('B'): - b = 1; if (dopr) - tcout(TCBOLDFACEBEG); + tsetattrs(TXTBOLDFACE); break; case ZWC('b'): - b = 0; m = 1; if (dopr) - tcout(TCALLATTRSOFF); + tunsetattrs(TXTBOLDFACE); break; case ZWC('S'): - s = 1; if (dopr) - tcout(TCSTANDOUTBEG); + tsetattrs(TXTSTANDOUT); break; case ZWC('s'): - s = 0; m = 1; if (dopr) - tcout(TCSTANDOUTEND); + tunsetattrs(TXTSTANDOUT); break; case ZWC('U'): - u = 1; if (dopr) - tcout(TCUNDERLINEBEG); + tsetattrs(TXTUNDERLINE); break; case ZWC('u'): - u = 0; m = 1; if (dopr) - tcout(TCUNDERLINEEND); + tunsetattrs(TXTUNDERLINE); break; case ZWC('F'): case ZWC('K'): @@ -1173,20 +1171,21 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) } else atr = match_colour(NULL, is_fg, arg); if (atr != TXT_ERROR && dopr) - set_colour_attribute(atr, is_fg ? COL_SEQ_FG : - COL_SEQ_BG, 0); + tsetattrs(atr); break; case ZWC('f'): if (dopr) - set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, 0); + tunsetattrs(TXTFGCOLOUR); break; case ZWC('k'): if (dopr) - set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, 0); + tunsetattrs(TXTBGCOLOUR); break; case ZWC('{'): if (arg) cc += arg; + if (dopr) + applytextattributes(0); for (; *p && (*p != '%' || p[1] != '}'); p++) if (dopr) putc(*p == Meta ? *++p ^ 32 : *p, shout); @@ -1197,7 +1196,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) if (stat) { sprintf(nc, "%d/%d", (n ? mlastm : mselect), listdat.nlist); - m = 2; + m = 1; } break; case ZWC('M'): @@ -1205,20 +1204,20 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) sprintf(nbuf, "%d/%d", (n ? mlastm : mselect), listdat.nlist); sprintf(nc, "%-9s", nbuf); - m = 2; + m = 1; } break; case ZWC('l'): if (stat) { sprintf(nc, "%d/%d", ml + 1, listdat.nlines); - m = 2; + m = 1; } break; case ZWC('L'): if (stat) { sprintf(nbuf, "%d/%d", ml + 1, listdat.nlines); sprintf(nc, "%-9s", nbuf); - m = 2; + m = 1; } break; case ZWC('p'): @@ -1230,7 +1229,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) ((ml + 1) * 100) / listdat.nlines); else strcpy(nc, "Top"); - m = 2; + m = 1; } break; case ZWC('P'): @@ -1242,25 +1241,19 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) ((ml + 1) * 100) / listdat.nlines); else strcpy(nc, "Top "); - m = 2; + m = 1; } break; } - if (m == 2 && dopr == 1) { + if (m && dopr) { /* nc only contains ASCII text */ int l = strlen(nc); if (l + cc > zterm_columns - 2) nc[l -= l + cc - (zterm_columns - 2)] = '\0'; + applytextattributes(0); fputs(nc, shout); cc += l; - } else if (dopr && m == 1) { - if (b) - tcout(TCBOLDFACEBEG); - if (s) - tcout(TCSTANDOUTBEG); - if (u) - tcout(TCUNDERLINEBEG); } } else break; @@ -1276,6 +1269,7 @@ compprintfmt(char *fmt, int n, int dopr, int doesc, int ml, int *stop) cc = 0; } if (dopr == 1) { + applytextattributes(0); if (ml == mlend - 1 && (cc % zterm_columns) == zterm_columns - 1) { dopr = 0; diff --git a/Src/Zle/zle.h b/Src/Zle/zle.h index 97cc7d797..1a3e4c241 100644 --- a/Src/Zle/zle.h +++ b/Src/Zle/zle.h @@ -490,11 +490,7 @@ typedef struct { */ REFRESH_CHAR chr; /* - * Its attributes. 'On' attributes (TXT_ATTR_ON_MASK) are - * applied before the character, 'off' attributes (TXT_ATTR_OFF_MASK) - * after it. 'On' attributes are present for all characters that - * need the effect; 'off' attributes are only present for the - * last character in the sequence. + * Its attributes. */ zattr atr; } REFRESH_ELEMENT; diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c index 40b902901..39be33939 100644 --- a/Src/Zle/zle_main.c +++ b/Src/Zle/zle_main.c @@ -1230,9 +1230,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) char *pptbuf; int pptlen; - pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL, - &pmpt_attr), + pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL), &pptlen); + pmpt_attr = txtcurrentattrs; write_loop(2, pptbuf, pptlen); free(pptbuf); return shingetline(); @@ -1267,10 +1267,11 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish) fetchttyinfo = 0; trashedzle = 0; raw_lp = lp; - lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL, &pmpt_attr); + lpromptbuf = promptexpand(lp ? *lp : NULL, 1, NULL, NULL); + pmpt_attr = txtcurrentattrs; raw_rp = rp; - rpmpt_attr = pmpt_attr; - rpromptbuf = promptexpand(rp ? *rp : NULL, 1, NULL, NULL, &rpmpt_attr); + rpromptbuf = promptexpand(rp ? *rp : NULL, 1, NULL, NULL); + rpmpt_attr = txtcurrentattrs; free_prepostdisplay(); zlereadflags = flags; @@ -2009,8 +2010,8 @@ reexpandprompt(void) char *new_lprompt, *new_rprompt; looping = reexpanding; - new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL, - &pmpt_attr); + new_lprompt = promptexpand(raw_lp ? *raw_lp : NULL, 1, NULL, NULL); + pmpt_attr = txtcurrentattrs; free(lpromptbuf); lpromptbuf = new_lprompt; @@ -2018,8 +2019,8 @@ reexpandprompt(void) continue; rpmpt_attr = pmpt_attr; - new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL, - &rpmpt_attr); + new_rprompt = promptexpand(raw_rp ? *raw_rp : NULL, 1, NULL, NULL); + rpmpt_attr = txtcurrentattrs; free(rpromptbuf); rpromptbuf = new_rprompt; } while (looping != reexpanding); diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c index 2db5f0642..ae8e5c109 100644 --- a/Src/Zle/zle_refresh.c +++ b/Src/Zle/zle_refresh.c @@ -208,7 +208,7 @@ int predisplaylen, postdisplaylen; * displayed on screen. */ -static zattr default_atr_on, special_atr_on; +static zattr default_attr, special_attr; /* * Array of region highlights, no special termination. @@ -245,12 +245,12 @@ char *tcout_func_name; int cost; # define SELECT_ADD_COST(X) (cost += X) -# define zputc(a) (zwcputc(a, NULL), cost++) +# define zputc(a) (zwcputc(a), cost++) # define zwrite(a, b) (zwcwrite((a), (b)), \ cost += ((b) * ZLE_CHAR_SIZE)) #else # define SELECT_ADD_COST(X) -# define zputc(a) zwcputc(a, NULL) +# define zputc(a) zwcputc(a) # define zwrite(a, b) zwcwrite((a), (b)) #endif @@ -316,14 +316,14 @@ static void zle_set_highlight(void) { char **atrs = getaparam("zle_highlight"); - int special_atr_on_set = 0; - int region_atr_on_set = 0; - int isearch_atr_on_set = 0; - int suffix_atr_on_set = 0; - int paste_atr_on_set = 0; + int special_attr_set = 0; + int region_attr_set = 0; + int isearch_attr_set = 0; + int suffix_attr_set = 0; + int paste_attr_set = 0; struct region_highlight *rhp; - special_atr_on = default_atr_on = 0; + special_attr = default_attr = 0; if (!region_highlights) { region_highlights = (struct region_highlight *) zshcalloc(N_SPECIAL_HIGHLIGHTS*sizeof(struct region_highlight)); @@ -340,41 +340,41 @@ zle_set_highlight(void) for (; *atrs; atrs++) { if (!strcmp(*atrs, "none")) { /* reset attributes for consistency... usually unnecessary */ - special_atr_on = default_atr_on = 0; - special_atr_on_set = 1; - paste_atr_on_set = region_atr_on_set = - isearch_atr_on_set = suffix_atr_on_set = 1; + special_attr = default_attr = 0; + special_attr_set = 1; + paste_attr_set = region_attr_set = + isearch_attr_set = suffix_attr_set = 1; } else if (strpfx("default:", *atrs)) { - match_highlight(*atrs + 8, &default_atr_on); + match_highlight(*atrs + 8, &default_attr); } else if (strpfx("special:", *atrs)) { - match_highlight(*atrs + 8, &special_atr_on); - special_atr_on_set = 1; + match_highlight(*atrs + 8, &special_attr); + special_attr_set = 1; } else if (strpfx("region:", *atrs)) { match_highlight(*atrs + 7, ®ion_highlights[0].atr); - region_atr_on_set = 1; + region_attr_set = 1; } else if (strpfx("isearch:", *atrs)) { match_highlight(*atrs + 8, &(region_highlights[1].atr)); - isearch_atr_on_set = 1; + isearch_attr_set = 1; } else if (strpfx("suffix:", *atrs)) { match_highlight(*atrs + 7, &(region_highlights[2].atr)); - suffix_atr_on_set = 1; + suffix_attr_set = 1; } else if (strpfx("paste:", *atrs)) { match_highlight(*atrs + 6, &(region_highlights[3].atr)); - paste_atr_on_set = 1; + paste_attr_set = 1; } } } /* Defaults */ - if (!special_atr_on_set) - special_atr_on = TXTSTANDOUT; - if (!region_atr_on_set) + if (!special_attr_set) + special_attr = TXTSTANDOUT; + if (!region_attr_set) region_highlights[0].atr = TXTSTANDOUT; - if (!isearch_atr_on_set) + if (!isearch_attr_set) region_highlights[1].atr = TXTUNDERLINE; - if (!suffix_atr_on_set) + if (!suffix_attr_set) region_highlights[2].atr = TXTBOLDFACE; - if (!paste_atr_on_set) + if (!paste_attr_set) region_highlights[3].atr = TXTSTANDOUT; allocate_colour_buffer(); @@ -571,22 +571,6 @@ unset_region_highlight(Param pm, int exp) } -/* The last attributes that were on. */ -static zattr lastatr; - -/* - * Clear the last attributes that we set: used when we're going - * to be outputting stuff that shouldn't show up as text. - */ -static void -clearattributes(void) -{ - if (lastatr) { - settextattributes(TXT_ATTR_OFF_FROM_ON(lastatr)); - lastatr = 0; - } -} - /* * Output a termcap capability, clearing any text attributes so * as not to mess up the display. @@ -595,7 +579,7 @@ clearattributes(void) static void tcoutclear(int cap) { - clearattributes(); + cleartextattributes(0); tcout(cap); } @@ -603,47 +587,20 @@ tcoutclear(int cap) * Output the character. This must come from the new video * buffer, nbuf, since we access the multiword buffer nmwbuf * directly. - * - * curatrp may be NULL, otherwise points to an integer specifying - * what attributes were turned on for a character output immediately - * before, in order to optimise output of attribute changes. */ /**/ void -zwcputc(const REFRESH_ELEMENT *c, zattr *curatrp) +zwcputc(const REFRESH_ELEMENT *c) { - /* - * Safety: turn attributes off if last heard of turned on. - * This differs from *curatrp, which is an optimisation for - * writing lots of stuff at once. - */ #ifdef MULTIBYTE_SUPPORT mbstate_t mbstate; int i; VARARR(char, mbtmp, MB_CUR_MAX + 1); #endif - if (lastatr & ~c->atr) { - /* Stuff on we don't want, turn it off */ - settextattributes(TXT_ATTR_OFF_FROM_ON(lastatr & ~c->atr)); - lastatr = 0; - } - - /* - * Don't output "on" attributes in a string of characters with - * the same attributes. Be careful in case a different colour - * needs setting. - */ - if ((c->atr & TXT_ATTR_ON_MASK) && - (!curatrp || - ((*curatrp & TXT_ATTR_ON_VALUES_MASK) != - (c->atr & TXT_ATTR_ON_VALUES_MASK)))) { - /* Record just the control flags we might need to turn off... */ - lastatr = c->atr & TXT_ATTR_ON_MASK; - /* ...but set including the values for colour attributes */ - settextattributes(c->atr & TXT_ATTR_ON_VALUES_MASK); - } + treplaceattrs(c->atr); + applytextattributes(0); #ifdef MULTIBYTE_SUPPORT if (c->atr & TXT_MULTIWORD_MASK) { @@ -664,35 +621,15 @@ zwcputc(const REFRESH_ELEMENT *c, zattr *curatrp) #else fputc(c->chr, shout); #endif - - /* - * Always output "off" attributes since we only turn off at - * the end of a chunk of highlighted text. - */ - if (c->atr & TXT_ATTR_OFF_MASK) { - settextattributes(c->atr & TXT_ATTR_OFF_MASK); - lastatr &= ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT); - } - if (curatrp) { - /* - * Remember the current attributes: those that are turned - * on, less those that are turned off again. Include - * colour attributes here in case the colour changes to - * another non-default one. - */ - *curatrp = (c->atr & TXT_ATTR_ON_VALUES_MASK) & - ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT); - } } static int zwcwrite(const REFRESH_STRING s, size_t i) { size_t j; - zattr curatr = 0; for (j = 0; j < i; j++) - zwcputc(s + j, &curatr); + zwcputc(s + j); return i; /* TODO something better for error indication */ } @@ -939,29 +876,6 @@ snextline(Rparams rpms) rpms->sen = rpms->s + winw; } - -/**/ -static void -settextattributes(zattr atr) -{ - if (txtchangeisset(atr, TXTNOBOLDFACE)) - tsetcap(TCALLATTRSOFF, 0); - if (txtchangeisset(atr, TXTNOSTANDOUT)) - tsetcap(TCSTANDOUTEND, 0); - if (txtchangeisset(atr, TXTNOUNDERLINE)) - tsetcap(TCUNDERLINEEND, 0); - if (txtchangeisset(atr, TXTBOLDFACE)) - tsetcap(TCBOLDFACEBEG, 0); - if (txtchangeisset(atr, TXTSTANDOUT)) - tsetcap(TCSTANDOUTBEG, 0); - if (txtchangeisset(atr, TXTUNDERLINE)) - tsetcap(TCUNDERLINEBEG, 0); - if (txtchangeisset(atr, TXTFGCOLOUR|TXTNOFGCOLOUR)) - set_colour_attribute(atr, COL_SEQ_FG, 0); - if (txtchangeisset(atr, TXTBGCOLOUR|TXTNOBGCOLOUR)) - set_colour_attribute(atr, COL_SEQ_BG, 0); -} - #ifdef MULTIBYTE_SUPPORT /* * Add a multiword glyph at the screen location base. @@ -1043,7 +957,6 @@ zrefresh(void) int tmppos; /* t - tmpline */ int tmpalloced; /* flag to free tmpline when finished */ int remetafy; /* flag that zle line is metafied */ - zattr txtchange; /* attributes set after prompts */ int rprompt_off = 1; /* Offset of rprompt from right of screen */ struct rparams rpms; #ifdef MULTIBYTE_SUPPORT @@ -1194,7 +1107,7 @@ zrefresh(void) tsetcap(TCALLATTRSOFF, 0); tsetcap(TCSTANDOUTEND, 0); tsetcap(TCUNDERLINEEND, 0); - txtattrmask = 0; + txtcurrentattrs = txtpendingattrs = txtunknownattrs = 0; if (trashedzle && !clearflag) reexpandprompt(); @@ -1219,8 +1132,8 @@ zrefresh(void) if (lpromptwof == winw) zputs("\n", shout); /* works with both hasam and !hasam */ } else { - txtchange = pmpt_attr; - settextattributes(txtchange); + treplaceattrs(pmpt_attr); + applytextattributes(0); } if (clearflag) { zputc(&zr_cr); @@ -1264,8 +1177,8 @@ zrefresh(void) rpms.sen = *nbuf + winw; for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) { unsigned ireg; - zattr base_atr_on = default_atr_on, base_atr_off = 0; - zattr all_atr_on, all_atr_off; + zattr base_attr = default_attr; + zattr all_attr; struct region_highlight *rhp; /* * Calculate attribute based on region. @@ -1282,26 +1195,21 @@ zrefresh(void) tmppos < rhp->end + offset) { if (rhp->atr & (TXTFGCOLOUR|TXTBGCOLOUR)) { /* override colour with later entry */ - base_atr_on = (base_atr_on & ~TXT_ATTR_ON_VALUES_MASK) | - rhp->atr; + base_attr = rhp->atr; } else { /* no colour set yet */ - base_atr_on |= rhp->atr; + base_attr |= rhp->atr; } - if (tmppos == rhp->end + offset - 1 || - tmppos == tmpll - 1) - base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr); } } - if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) { + if (special_attr & (TXTFGCOLOUR|TXTBGCOLOUR)) { /* keep colours from special attributes */ - all_atr_on = special_atr_on | - (base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK); + all_attr = special_attr | + (base_attr & ~TXT_ATTR_COLOUR_MASK); } else { /* keep colours from standard attributes */ - all_atr_on = special_atr_on | base_atr_on; + all_attr = special_attr | base_attr; } - all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on); if (t == scs) /* if cursor is here, remember it */ rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln]; @@ -1319,10 +1227,9 @@ zrefresh(void) } else { do { rpms.s->chr = ZWC(' '); - rpms.s->atr = base_atr_on; + rpms.s->atr = base_attr; rpms.s++; } while ((++t0) & 7); - rpms.s[-1].atr |= base_atr_off; } } #ifdef MULTIBYTE_SUPPORT @@ -1341,11 +1248,9 @@ zrefresh(void) rpms.s->chr = ZWC(' '); if (!started) started = 1; - rpms.s->atr = all_atr_on; + rpms.s->atr = all_attr; rpms.s++; } while (rpms.s < rpms.sen); - if (started) - rpms.s[-1].atr |= all_atr_off; if (nextline(&rpms, 1)) break; if (t == scs) { @@ -1369,7 +1274,7 @@ zrefresh(void) * occurrence. */ rpms.s->chr = ZWC('?'); - rpms.s->atr = all_atr_on | all_atr_off; + rpms.s->atr = all_attr; rpms.s++; } else { /* We can fit it without reaching the end of the line. */ @@ -1377,7 +1282,7 @@ zrefresh(void) * As we don't actually output the WEOF, we attach * any off attributes to the character itself. */ - rpms.s->atr = base_atr_on | base_atr_off; + rpms.s->atr = base_attr; if (ichars > 1) { /* * Glyph includes combining characters. @@ -1393,7 +1298,7 @@ zrefresh(void) while (--width > 0) { rpms.s->chr = WEOF; /* Not used, but be consistent... */ - rpms.s->atr = base_atr_on | base_atr_off; + rpms.s->atr = base_attr; rpms.s++; } } @@ -1410,17 +1315,16 @@ zrefresh(void) #endif ) { /* other control character */ rpms.s->chr = ZWC('^'); - rpms.s->atr = all_atr_on; + rpms.s->atr = all_attr; rpms.s++; if (rpms.s == rpms.sen) { /* text wrapped */ - rpms.s[-1].atr |= all_atr_off; if (nextline(&rpms, 1)) break; } rpms.s->chr = (((unsigned int)*t & ~0x80u) > 31) ? ZWC('?') : (*t | ZWC('@')); - rpms.s->atr = all_atr_on | all_atr_off; + rpms.s->atr = all_attr; rpms.s++; } #ifdef MULTIBYTE_SUPPORT @@ -1432,7 +1336,6 @@ zrefresh(void) char dispchars[11]; char *dispptr = dispchars; wchar_t wc; - int started = 0; #ifdef __STDC_ISO_10646__ if (ZSH_INVALID_WCHAR_TEST(*t)) { @@ -1449,31 +1352,23 @@ zrefresh(void) if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */) { rpms.s->chr = wc; - if (!started) - started = 1; - rpms.s->atr = all_atr_on; + rpms.s->atr = all_attr; rpms.s++; if (rpms.s == rpms.sen) { /* text wrapped */ - if (started) { - rpms.s[-1].atr |= all_atr_off; - started = 0; - } if (nextline(&rpms, 1)) break; } } dispptr++; } - if (started) - rpms.s[-1].atr |= all_atr_off; if (*dispptr) /* nextline said stop processing */ break; } #else else { /* normal character */ rpms.s->chr = *t; - rpms.s->atr = base_atr_on | base_atr_off; + rpms.s->atr = base_attr; rpms.s++; } #endif @@ -1499,13 +1394,12 @@ zrefresh(void) if (statusline) { int outll, outsz; - zattr all_atr_on, all_atr_off; + zattr all_attr; char *statusdup = ztrdup(statusline); ZLE_STRING_T outputline = stringaszleline(statusdup, 0, &outll, &outsz, NULL); - all_atr_on = special_atr_on; - all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on); + all_attr = special_attr; rpms.tosln = rpms.ln + 1; nbuf[rpms.ln][winw + 1] = zr_zr; /* text not wrapped */ @@ -1525,7 +1419,7 @@ zrefresh(void) } if (width > rpms.sen - rpms.s) { rpms.s->chr = ZWC('?'); - rpms.s->atr = all_atr_on | all_atr_off; + rpms.s->atr = all_attr; rpms.s++; } else { rpms.s->chr = *u; @@ -1542,7 +1436,7 @@ zrefresh(void) #endif if (ZC_icntrl(*u)) { /* simplified processing in the status line */ rpms.s->chr = ZWC('^'); - rpms.s->atr = all_atr_on; + rpms.s->atr = all_attr; rpms.s++; if (rpms.s == rpms.sen) { nbuf[rpms.ln][winw + 1] = zr_nl;/* text wrapped */ @@ -1550,7 +1444,7 @@ zrefresh(void) } rpms.s->chr = (((unsigned int)*u & ~0x80u) > 31) ? ZWC('?') : (*u | ZWC('@')); - rpms.s->atr = all_atr_on | all_atr_off; + rpms.s->atr = all_attr; rpms.s++; } else { rpms.s->chr = *u; @@ -1725,7 +1619,6 @@ zrefresh(void) /* output the right-prompt if appropriate */ if (put_rpmpt && !iln && !oput_rpmpt) { - zattr attrchange; moveto(0, winw - rprompt_off - rpromptw); zputs(rpromptbuf, shout); @@ -1735,39 +1628,9 @@ zrefresh(void) zputc(&zr_cr); vcs = 0; } - /* reset character attributes to that set by the main prompt */ - txtchange = pmpt_attr; - /* - * Keep attributes that have actually changed, - * which are ones off in rpmpt_attr and on in - * pmpt_attr, and vice versa. - */ - attrchange = txtchange & - (TXT_ATTR_OFF_FROM_ON(rpmpt_attr) | - TXT_ATTR_ON_FROM_OFF(rpmpt_attr)); - /* - * Careful in case the colour changed. - */ - if (txtchangeisset(txtchange, TXTFGCOLOUR) && - (!txtchangeisset(rpmpt_attr, TXTFGCOLOUR) || - ((txtchange ^ rpmpt_attr) & TXT_ATTR_FG_COL_MASK))) - { - attrchange |= - txtchange & (TXTFGCOLOUR | TXT_ATTR_FG_COL_MASK); - } - if (txtchangeisset(txtchange, TXTBGCOLOUR) && - (!txtchangeisset(rpmpt_attr, TXTBGCOLOUR) || - ((txtchange ^ rpmpt_attr) & TXT_ATTR_BG_COL_MASK))) - { - attrchange |= - txtchange & (TXTBGCOLOUR | TXT_ATTR_BG_COL_MASK); - } - /* - * Now feed these changes into the usual function, - * if necessary. - */ - if (attrchange) - settextattributes(attrchange); + /* reset character attributes to that set by the main prompt */ + treplaceattrs(pmpt_attr); + applytextattributes(0); } } @@ -1782,8 +1645,8 @@ individually */ /* reset character attributes */ if (clearf && postedit) { - if ((txtchange = pmpt_attr ? pmpt_attr : rpmpt_attr)) - settextattributes(txtchange); + treplaceattrs(pmpt_attr ? pmpt_attr : rpmpt_attr); + applytextattributes(0); } clearf = 0; oput_rpmpt = put_rpmpt; @@ -1984,8 +1847,6 @@ refreshline(int ln) /* 3: main display loop - write out the buffer using whatever tricks we can */ for (;;) { - zattr now_off; - #ifdef MULTIBYTE_SUPPORT if ((!nl->chr || nl->chr != WEOF) && (!ol->chr || ol->chr != WEOF)) { #endif @@ -2087,7 +1948,7 @@ refreshline(int ln) * deletions, so turn off text attributes. */ if (first) { - clearattributes(); + cleartextattributes(0); first = 0; } tc_delchars(i); @@ -2176,13 +2037,8 @@ refreshline(int ln) break; do { #endif - /* - * If an attribute was on here but isn't any more, - * output the sequence to turn it off. - */ - now_off = ol->atr & ~nl->atr & TXT_ATTR_ON_MASK; - if (now_off) - settextattributes(TXT_ATTR_OFF_FROM_ON(now_off)); + treplaceattrs(nl->atr); + applytextattributes(0); /* * This is deliberately called if nl->chr is WEOF @@ -2560,8 +2416,8 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) for (t0 = 0; t0 < tmpll; t0++) { unsigned ireg; - zattr base_atr_on = 0, base_atr_off = 0; - zattr all_atr_on, all_atr_off; + zattr base_attr = 0; + zattr all_attr; struct region_highlight *rhp; /* * Calculate attribute based on region. @@ -2576,38 +2432,33 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) offset = predisplaylen; /* increment over it */ if (rhp->start + offset <= t0 && t0 < rhp->end + offset) { - if (base_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) { + if (base_attr & (TXTFGCOLOUR|TXTBGCOLOUR)) { /* keep colour already set */ - base_atr_on |= rhp->atr & ~TXT_ATTR_COLOUR_ON_MASK; + base_attr |= rhp->atr & ~TXT_ATTR_COLOUR_MASK; } else { /* no colour set yet */ - base_atr_on |= rhp->atr; + base_attr |= rhp->atr; } - if (t0 == rhp->end + offset - 1 || - t0 == tmpll - 1) - base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr); } } - if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) { + if (special_attr & (TXTFGCOLOUR|TXTBGCOLOUR)) { /* keep colours from special attributes */ - all_atr_on = special_atr_on | - (base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK); + all_attr = special_attr | + (base_attr & ~TXT_ATTR_COLOUR_MASK); } else { /* keep colours from standard attributes */ - all_atr_on = special_atr_on | base_atr_on; + all_attr = special_attr | base_attr; } - all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on); if (tmpline[t0] == ZWC('\t')) { for (*vp++ = zr_sp; (vp - vbuf) & 7; ) *vp++ = zr_sp; - vp[-1].atr |= base_atr_off; } else if (tmpline[t0] == ZWC('\n')) { vp->chr = ZWC('\\'); - vp->atr = all_atr_on; + vp->atr = all_attr; vp++; vp->chr = ZWC('n'); - vp->atr = all_atr_on | all_atr_off; + vp->atr = all_attr; vp++; #ifdef MULTIBYTE_SUPPORT } else if (WC_ISPRINT(tmpline[t0]) && @@ -2623,7 +2474,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) } } else ichars = 1; - vp->atr = base_atr_on | base_atr_off; + vp->atr = base_attr; if (ichars > 1) addmultiword(vp, tmpline+t0, ichars); else @@ -2631,7 +2482,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) vp++; while (--width > 0) { vp->chr = WEOF; - vp->atr = base_atr_on | base_atr_off; + vp->atr = base_attr; vp++; } t0 += ichars - 1; @@ -2644,11 +2495,11 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) ZLE_INT_T t = tmpline[++t0]; vp->chr = ZWC('^'); - vp->atr = all_atr_on; + vp->atr = all_attr; vp++; vp->chr = (((unsigned int)t & ~0x80u) > 31) ? ZWC('?') : (t | ZWC('@')); - vp->atr = all_atr_on | all_atr_off; + vp->atr = all_attr; vp++; } #ifdef MULTIBYTE_SUPPORT @@ -2656,7 +2507,6 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) char dispchars[11]; char *dispptr = dispchars; wchar_t wc; - int started = 0; if ((unsigned)tmpline[t0] > 0xffffU) { sprintf(dispchars, "<%.08x>", (unsigned)tmpline[t0]); @@ -2666,20 +2516,16 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs) while (*dispptr) { if (mbtowc(&wc, dispptr, 1) == 1 /* paranoia */) { vp->chr = wc; - if (!started) - started = 1; - vp->atr = all_atr_on; + vp->atr = all_attr; vp++; } dispptr++; } - if (started) - vp[-1].atr |= all_atr_off; } #else else { vp->chr = tmpline[t0]; - vp->atr = base_atr_on | base_atr_off; + vp->atr = base_attr; vp++; } #endif diff --git a/Src/Zle/zle_tricky.c b/Src/Zle/zle_tricky.c index fdd168763..f94bfce3c 100644 --- a/Src/Zle/zle_tricky.c +++ b/Src/Zle/zle_tricky.c @@ -2426,7 +2426,7 @@ mod_export int printfmt(char *fmt, int n, int dopr, int doesc) { char *p = fmt, nc[DIGBUFSIZE]; - int l = 0, cc = 0, b = 0, s = 0, u = 0, m; + int l = 0, cc = 0; MB_METACHARINIT(); for (; *p; ) { @@ -2437,48 +2437,45 @@ printfmt(char *fmt, int n, int dopr, int doesc) if (idigit(*++p)) arg = zstrtol(p, &p, 10); if (*p) { - m = 0; switch (*p) { case '%': - if (dopr) + if (dopr) { + applytextattributes(0); putc('%', shout); + } cc++; break; case 'n': sprintf(nc, "%d", n); - if (dopr) + if (dopr) { + applytextattributes(0); fputs(nc, shout); + } cc += MB_METASTRWIDTH(nc); break; case 'B': - b = 1; if (dopr) - tcout(TCBOLDFACEBEG); + tsetattrs(TXTBOLDFACE); break; case 'b': - b = 0; m = 1; if (dopr) - tcout(TCALLATTRSOFF); + tunsetattrs(TXTBOLDFACE); break; case 'S': - s = 1; if (dopr) - tcout(TCSTANDOUTBEG); + tsetattrs(TXTSTANDOUT); break; case 's': - s = 0; m = 1; if (dopr) - tcout(TCSTANDOUTEND); + tunsetattrs(TXTSTANDOUT); break; case 'U': - u = 1; if (dopr) - tcout(TCUNDERLINEBEG); + tsetattrs(TXTUNDERLINE); break; case 'u': - u = 0; m = 1; if (dopr) - tcout(TCUNDERLINEEND); + tunsetattrs(TXTUNDERLINE); break; case 'F': case 'K': @@ -2491,18 +2488,19 @@ printfmt(char *fmt, int n, int dopr, int doesc) } else atr = match_colour(NULL, is_fg, arg); if (atr != TXT_ERROR) - set_colour_attribute(atr, is_fg ? COL_SEQ_FG : - COL_SEQ_BG, 0); + tsetattrs(atr); break; case 'f': - set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, 0); + tunsetattrs(TXTFGCOLOUR); break; case 'k': - set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, 0); + tunsetattrs(TXTBGCOLOUR); break; case '{': if (arg) cc += arg; + if (dopr) + applytextattributes(0); for (p++; *p && (*p != '%' || p[1] != '}'); p++) { if (*p == Meta) { p++; @@ -2518,14 +2516,6 @@ printfmt(char *fmt, int n, int dopr, int doesc) p--; break; } - if (dopr && m) { - if (b) - tcout(TCBOLDFACEBEG); - if (s) - tcout(TCSTANDOUTBEG); - if (u) - tcout(TCUNDERLINEBEG); - } } else break; p++; @@ -2533,6 +2523,7 @@ printfmt(char *fmt, int n, int dopr, int doesc) if (*p == '\n') { cc++; if (dopr) { + applytextattributes(0); if (tccan(TCCLEAREOL)) tcout(TCCLEAREOL); else { @@ -2551,6 +2542,7 @@ printfmt(char *fmt, int n, int dopr, int doesc) convchar_t cchar; int clen = MB_METACHARLENCONV(p, &cchar); if (dopr) { + applytextattributes(0); while (clen--) { if (*p == Meta) { p++; diff --git a/Src/Zle/zle_utils.c b/Src/Zle/zle_utils.c index 2536e9faa..1a580a9e6 100644 --- a/Src/Zle/zle_utils.c +++ b/Src/Zle/zle_utils.c @@ -1250,7 +1250,7 @@ getzlequery(void) REFRESH_ELEMENT re; re.chr = c; re.atr = 0; - zwcputc(&re, NULL); + zwcputc(&re); } return c == ZWC('y'); } diff --git a/Src/builtin.c b/Src/builtin.c index 70a950666..4c295d11f 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -4603,6 +4603,8 @@ bin_print(char *name, char **args, Options ops, int func) /* compute lengths, and interpret according to -P, -D, -e, etc. */ argc = arrlen(args); len = (int *) hcalloc(argc * sizeof(int)); + if (OPT_ISSET(ops, 'P')) + txtunknownattrs = TXT_ATTR_ALL; for (n = 0; n < argc; n++) { /* first \ sequences */ if (fmt || @@ -4633,7 +4635,7 @@ bin_print(char *name, char **args, Options ops, int func) */ char *str = unmetafy( promptexpand(metafy(args[n], len[n], META_NOALLOC), - 0, NULL, NULL, NULL), + 0, NULL, NULL), &len[n]); args[n] = dupstrpfx(str, len[n]); free(str); diff --git a/Src/init.c b/Src/init.c index 9981d059a..75ef2e094 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1654,8 +1654,7 @@ VA_DCL lp = va_arg(ap, char **); - pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL, - NULL), + pptbuf = unmetafy(promptexpand(lp ? *lp : NULL, 0, NULL, NULL), &pptlen); write_loop(2, pptbuf, pptlen); free(pptbuf); diff --git a/Src/input.c b/Src/input.c index d55b05696..5a612669b 100644 --- a/Src/input.c +++ b/Src/input.c @@ -402,7 +402,7 @@ inputline(void) char *pptbuf; int pptlen; pptbuf = unmetafy(promptexpand(ingetcpmptl ? *ingetcpmptl : NULL, - 0, NULL, NULL, NULL), &pptlen); + 0, NULL, NULL), &pptlen); write_loop(2, pptbuf, pptlen); free(pptbuf); } diff --git a/Src/loop.c b/Src/loop.c index 88c55dd1a..7df379ecf 100644 --- a/Src/loop.c +++ b/Src/loop.c @@ -282,7 +282,7 @@ execselect(Estate state, UNUSED(int do_exec)) /* Keep any user interrupt error status */ errflag = oef | (errflag & ERRFLAG_INT); } else { - str = promptexpand(prompt3, 0, NULL, NULL, NULL); + str = promptexpand(prompt3, 0, NULL, NULL); zputs(str, stderr); free(str); fflush(stderr); diff --git a/Src/prompt.c b/Src/prompt.c index 3cb95039c..880194f87 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -30,10 +30,20 @@ #include "zsh.mdh" #include "prompt.pro" -/* text attribute mask */ +/* current text attributes */ /**/ -mod_export zattr txtattrmask; +mod_export zattr txtcurrentattrs; + +/* pending changes for attributes */ + +/**/ +mod_export zattr txtpendingattrs; + +/* mask of attributes with an unknown state */ + +/**/ +mod_export zattr txtunknownattrs; /* the command stack for use with %_ in prompts */ @@ -160,15 +170,11 @@ promptpath(char *p, int npath, int tilde) * between spacing and non-spacing parts of the prompt, and * Nularg, which (in a non-spacing sequence) indicates a * `glitch' space. - * - * txtchangep gives an integer controlling the attributes of - * the prompt. This is for use in zle to maintain the attributes - * consistently. Other parts of the shell should not need to use it. */ /**/ mod_export char * -promptexpand(char *s, int ns, char *rs, char *Rs, zattr *txtchangep) +promptexpand(char *s, int ns, char *rs, char *Rs) { struct buf_vars new_vars; @@ -212,7 +218,7 @@ promptexpand(char *s, int ns, char *rs, char *Rs, zattr *txtchangep) new_vars.bp1 = NULL; new_vars.truncwidth = 0; - putpromptchar(1, '\0', txtchangep); + putpromptchar(1, '\0'); addbufspc(2); if (new_vars.dontcount) *new_vars.bp++ = Outpar; @@ -253,7 +259,7 @@ parsecolorchar(zattr arg, int is_fg) *ep = '\0'; /* expand the contents of the argument so you can use * %v for example */ - coll = col = promptexpand(bv->fm, 0, NULL, NULL, NULL); + coll = col = promptexpand(bv->fm, 0, NULL, NULL); *ep = oc; arg = match_colour((const char **)&coll, is_fg, 0); free(col); @@ -278,7 +284,7 @@ parsecolorchar(zattr arg, int is_fg) /**/ static int -putpromptchar(int doprint, int endchar, zattr *txtchangep) +putpromptchar(int doprint, int endchar) { char *ss, *hostnam; int t0, arg, test, sep, j, numjobs, len; @@ -430,10 +436,9 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep) /* Don't do the current truncation until we get back */ otruncwidth = bv->truncwidth; bv->truncwidth = 0; - if (!putpromptchar(test == 1 && doprint, sep, - txtchangep) || !*++bv->fm || - !putpromptchar(test == 0 && doprint, ')', - txtchangep)) { + if (!putpromptchar(test == 1 && doprint, sep) || + !*++bv->fm || + !putpromptchar(test == 0 && doprint, ')')) { bv->truncwidth = otruncwidth; return 0; } @@ -519,71 +524,57 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep) unqueue_signals(); break; case 'S': - txtchangeset(txtchangep, TXTSTANDOUT, TXTNOSTANDOUT); - txtset(TXTSTANDOUT); - tsetcap(TCSTANDOUTBEG, TSC_PROMPT); + tsetattrs(TXTSTANDOUT); + applytextattributes(TSC_PROMPT); break; case 's': - txtchangeset(txtchangep, TXTNOSTANDOUT, TXTSTANDOUT); - txtunset(TXTSTANDOUT); - tsetcap(TCSTANDOUTEND, TSC_PROMPT|TSC_DIRTY); + tunsetattrs(TXTSTANDOUT); + applytextattributes(TSC_PROMPT); break; case 'B': - txtchangeset(txtchangep, TXTBOLDFACE, TXTNOBOLDFACE); - txtset(TXTBOLDFACE); - tsetcap(TCBOLDFACEBEG, TSC_PROMPT|TSC_DIRTY); + tsetattrs(TXTBOLDFACE); + applytextattributes(TSC_PROMPT); break; case 'b': - txtchangeset(txtchangep, TXTNOBOLDFACE, TXTBOLDFACE); - txtunset(TXTBOLDFACE); - tsetcap(TCALLATTRSOFF, TSC_PROMPT|TSC_DIRTY); + tunsetattrs(TXTBOLDFACE); + applytextattributes(TSC_PROMPT); break; case 'U': - txtchangeset(txtchangep, TXTUNDERLINE, TXTNOUNDERLINE); - txtset(TXTUNDERLINE); - tsetcap(TCUNDERLINEBEG, TSC_PROMPT); + tsetattrs(TXTUNDERLINE); + applytextattributes(TSC_PROMPT); break; case 'u': - txtchangeset(txtchangep, TXTNOUNDERLINE, TXTUNDERLINE); - txtunset(TXTUNDERLINE); - tsetcap(TCUNDERLINEEND, TSC_PROMPT|TSC_DIRTY); + tunsetattrs(TXTUNDERLINE); + applytextattributes(TSC_PROMPT); break; case 'F': atr = parsecolorchar(arg, 1); - if (!(atr & (TXT_ERROR | TXTNOFGCOLOUR))) { - txtchangeset(txtchangep, atr & TXT_ATTR_FG_ON_MASK, - TXTNOFGCOLOUR | TXT_ATTR_FG_COL_MASK); - txtunset(TXT_ATTR_FG_COL_MASK); - txtset(atr & TXT_ATTR_FG_ON_MASK); - set_colour_attribute(atr, COL_SEQ_FG, TSC_PROMPT); + if (atr && atr != TXT_ERROR) { + tsetattrs(atr); + applytextattributes(TSC_PROMPT); break; } /* else FALLTHROUGH */ case 'f': - txtchangeset(txtchangep, TXTNOFGCOLOUR, TXT_ATTR_FG_ON_MASK); - txtunset(TXT_ATTR_FG_ON_MASK); - set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_PROMPT); + tunsetattrs(TXTFGCOLOUR); + applytextattributes(TSC_PROMPT); break; case 'K': atr = parsecolorchar(arg, 0); - if (!(atr & (TXT_ERROR | TXTNOBGCOLOUR))) { - txtchangeset(txtchangep, atr & TXT_ATTR_BG_ON_MASK, - TXTNOBGCOLOUR | TXT_ATTR_BG_COL_MASK); - txtunset(TXT_ATTR_BG_COL_MASK); - txtset(atr & TXT_ATTR_BG_ON_MASK); - set_colour_attribute(atr, COL_SEQ_BG, TSC_PROMPT); + if (atr && atr != TXT_ERROR) { + tsetattrs(atr); + applytextattributes(TSC_PROMPT); break; } /* else FALLTHROUGH */ case 'k': - txtchangeset(txtchangep, TXTNOBGCOLOUR, TXT_ATTR_BG_ON_MASK); - txtunset(TXT_ATTR_BG_ON_MASK); - set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_PROMPT); + tunsetattrs(TXTBGCOLOUR); + applytextattributes(TSC_PROMPT); break; case '[': if (idigit(*++bv->fm)) arg = zstrtol(bv->fm, &bv->fm, 10); - if (!prompttrunc(arg, ']', doprint, endchar, txtchangep)) + if (!prompttrunc(arg, ']', doprint, endchar)) return *bv->fm; break; case '<': @@ -596,7 +587,7 @@ putpromptchar(int doprint, int endchar, zattr *txtchangep) if (arg <= 0) arg = 1; } - if (!prompttrunc(arg, *bv->fm, doprint, endchar, txtchangep)) + if (!prompttrunc(arg, *bv->fm, doprint, endchar)) return *bv->fm; break; case '{': /*}*/ @@ -1015,7 +1006,7 @@ tsetcap(int cap, int flags) { if (tccan(cap) && !isset(SINGLELINEZLE) && !(termflags & (TERM_NOUP|TERM_BAD|TERM_UNKNOWN))) { - switch (flags & TSC_OUTPUT_MASK) { + switch (flags) { case TSC_RAW: tputs(tcstr[cap], 1, putraw); break; @@ -1045,20 +1036,6 @@ tsetcap(int cap, int flags) } break; } - - if (flags & TSC_DIRTY) { - flags &= ~TSC_DIRTY; - if (txtisset(TXTBOLDFACE) && cap != TCBOLDFACEBEG) - tsetcap(TCBOLDFACEBEG, flags); - if (txtisset(TXTSTANDOUT)) - tsetcap(TCSTANDOUTBEG, flags); - if (txtisset(TXTUNDERLINE)) - tsetcap(TCUNDERLINEBEG, flags); - if (txtisset(TXTFGCOLOUR)) - set_colour_attribute(txtattrmask, COL_SEQ_FG, flags); - if (txtisset(TXTBGCOLOUR)) - set_colour_attribute(txtattrmask, COL_SEQ_BG, flags); - } } } @@ -1219,8 +1196,7 @@ countprompt(char *str, int *wp, int *hp, int overf) /**/ static int -prompttrunc(int arg, int truncchar, int doprint, int endchar, - zattr *txtchangep) +prompttrunc(int arg, int truncchar, int doprint, int endchar) { if (arg > 0) { char ch = *bv->fm, *ptr, *truncstr; @@ -1267,7 +1243,7 @@ prompttrunc(int arg, int truncchar, int doprint, int endchar, w = bv->bp - bv->buf; bv->fm++; bv->trunccount = bv->dontcount; - putpromptchar(doprint, endchar, txtchangep); + putpromptchar(doprint, endchar); bv->trunccount = 0; ptr = bv->buf + w; /* putpromptchar() may have realloc()'d */ *bv->bp = '\0'; @@ -1547,7 +1523,7 @@ prompttrunc(int arg, int truncchar, int doprint, int endchar, * With bv->truncwidth set to zero, we always reach endchar * * (or the terminating NULL) this time round. * */ - if (!putpromptchar(doprint, endchar, txtchangep)) + if (!putpromptchar(doprint, endchar)) return 0; } /* Now we have to trick it into matching endchar again */ @@ -1585,6 +1561,122 @@ cmdpop(void) cmdsp--; } +/* functions for handling attributes */ + +/**/ +mod_export void +applytextattributes(int flags) +{ + zattr change = txtcurrentattrs ^ txtpendingattrs; + zattr keepon = ~change & txtpendingattrs & TXT_ATTR_ALL; + zattr turnoff = change & ~txtpendingattrs & TXT_ATTR_ALL; + int keepcount, turncount = 0; + + /* bail out early if we wouldn't do anything */ + if (!change) + return; + + if (txtunknownattrs) { + txtunknownattrs &= ~change; /* changes cease to be unknown */ + /* can't turn unknown attrs back on so avoid wiping them */ + keepcount = 1; + } else { + /* If we want to turn off more attributes than we want to keep on + * then it takes fewer termcap sequences to just turn off all the + * attributes. */ + for (keepcount = 0; keepon; keepcount++) /* count bits */ + keepon &= keepon - 1; + for (; turnoff; turncount++) + turnoff &= turnoff - 1; + } + + if (keepcount < turncount || (change & ~txtpendingattrs & TXTBOLDFACE)) { + tsetcap(TCALLATTRSOFF, flags); + /* this cleared all attributes, may need to restore some */ + change = txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs; + txtunknownattrs = 0; + } else { + if (change & ~txtpendingattrs & TXTSTANDOUT) { + tsetcap(TCSTANDOUTEND, flags); + /* in some cases, that clears all attributes */ + change = (txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs) | + (TXTUNDERLINE & change); + } + if (change & ~txtpendingattrs & TXTUNDERLINE) { + tsetcap(TCUNDERLINEEND, flags); + /* in some cases, that clears all attributes */ + change = txtpendingattrs & TXT_ATTR_ALL & ~txtunknownattrs; + } + } + if (change & txtpendingattrs & TXTBOLDFACE) + tsetcap(TCBOLDFACEBEG, flags); + if (change & txtpendingattrs & TXTSTANDOUT) + tsetcap(TCSTANDOUTBEG, flags); + if (change & txtpendingattrs & TXTUNDERLINE) + tsetcap(TCUNDERLINEBEG, flags); + + if (change & TXT_ATTR_FG_MASK) + set_colour_attribute(txtpendingattrs, COL_SEQ_FG, flags); + if (change & TXT_ATTR_BG_MASK) + set_colour_attribute(txtpendingattrs, COL_SEQ_BG, flags); + + txtcurrentattrs = txtpendingattrs; +} + +/**/ +mod_export void +cleartextattributes(int flags) +{ + treplaceattrs(0); + applytextattributes(flags); +} + +/**/ +mod_export void +treplaceattrs(zattr newattrs) +{ + if (txtunknownattrs) { + /* Set current attributes to the opposite of the new ones + * for any that are unknown so that applytextattributes() + * detects them as changed. */ + txtcurrentattrs &= ~txtunknownattrs; + txtcurrentattrs |= txtunknownattrs & ~newattrs; + } + + txtpendingattrs = newattrs; +} + +/**/ +mod_export void +tsetattrs(zattr newattrs) +{ + /* assume any unknown attributes that we're now setting were unset */ + txtcurrentattrs &= ~(newattrs & txtunknownattrs); + + txtpendingattrs |= newattrs & TXT_ATTR_ALL; + if (newattrs & TXTFGCOLOUR) { + txtpendingattrs &= ~TXT_ATTR_FG_MASK; + txtpendingattrs |= newattrs & TXT_ATTR_FG_MASK; + } + if (newattrs & TXTBGCOLOUR) { + txtpendingattrs &= ~TXT_ATTR_BG_MASK; + txtpendingattrs |= newattrs & TXT_ATTR_BG_MASK; + } +} + +/**/ +mod_export void +tunsetattrs(zattr newattrs) +{ + /* assume any unknown attributes that we're now unsetting were set */ + txtcurrentattrs |= newattrs & txtunknownattrs; + + txtpendingattrs &= ~(newattrs & TXT_ATTR_ALL); + if (newattrs & TXTFGCOLOUR) + txtpendingattrs &= ~TXT_ATTR_FG_MASK; + if (newattrs & TXTBGCOLOUR) + txtpendingattrs &= ~TXT_ATTR_BG_MASK; +} /***************************************************************************** * Utilities dealing with colour and other forms of highlighting. @@ -1607,7 +1699,7 @@ struct highlight { }; static const struct highlight highlights[] = { - { "none", 0, TXT_ATTR_ON_MASK }, + { "none", 0, TXT_ATTR_ALL }, { "bold", TXTBOLDFACE, 0 }, { "standout", TXTSTANDOUT, 0 }, { "underline", TXTUNDERLINE, 0 }, @@ -1645,8 +1737,8 @@ match_named_colour(const char **teststrp) * Match just the colour part of a highlight specification. * If teststrp is NULL, use the already parsed numeric colour. * Return the attributes to set in the attribute variable. - * Return -1 for out of range. Does not check the character - * following the colour specification. + * Return TXT_ERROR for out of range. Does not check the + * character following the colour specification. */ /**/ @@ -1693,10 +1785,8 @@ match_colour(const char **teststrp, int is_fg, int colour) } } else if ((named = ialpha(**teststrp))) { colour = match_named_colour(teststrp); - if (colour == 8) { - /* default */ - return is_fg ? TXTNOFGCOLOUR : TXTNOBGCOLOUR; - } + if (colour == 8) /* default */ + return 0; if (colour < 0) return TXT_ERROR; } @@ -2024,13 +2114,13 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) if (fg_bg == COL_SEQ_FG) { colour = txtchangeget(atr, TXT_ATTR_FG_COL); tc = TCFGCOLOUR; - def = txtchangeisset(atr, TXTNOFGCOLOUR); - use_truecolor = txtchangeisset(atr, TXT_ATTR_FG_24BIT); + def = !(atr & TXTFGCOLOUR); + use_truecolor = atr & TXT_ATTR_FG_24BIT; } else { colour = txtchangeget(atr, TXT_ATTR_BG_COL); tc = TCBGCOLOUR; - def = txtchangeisset(atr, TXTNOBGCOLOUR); - use_truecolor = txtchangeisset(atr, TXT_ATTR_BG_24BIT); + def = !(atr & TXTBGCOLOUR); + use_truecolor = atr & TXT_ATTR_BG_24BIT; } /* Test if current zle_highlight settings are customized, or diff --git a/Src/subst.c b/Src/subst.c index b8e4023e1..897188862 100644 --- a/Src/subst.c +++ b/Src/subst.c @@ -3716,6 +3716,8 @@ colonsubscript: if (presc) { int ops = opts[PROMPTSUBST], opb = opts[PROMPTBANG]; int opp = opts[PROMPTPERCENT]; + zattr savecurrent = txtcurrentattrs; + zattr saveunknown = txtunknownattrs; if (presc < 2) { opts[PROMPTPERCENT] = 1; @@ -3738,7 +3740,8 @@ colonsubscript: for (; *ap; ap++) { char *tmps; untokenize(*ap); - tmps = promptexpand(*ap, 0, NULL, NULL, NULL); + txtunknownattrs = TXT_ATTR_ALL; + tmps = promptexpand(*ap, 0, NULL, NULL); *ap = dupstring(tmps); free(tmps); } @@ -3747,10 +3750,14 @@ colonsubscript: if (!copied) val = dupstring(val), copied = 1; untokenize(val); - tmps = promptexpand(val, 0, NULL, NULL, NULL); + txtunknownattrs = TXT_ATTR_ALL; + tmps = promptexpand(val, 0, NULL, NULL); val = dupstring(tmps); free(tmps); } + + txtpendingattrs = txtcurrentattrs = savecurrent; + txtunknownattrs = saveunknown; opts[PROMPTSUBST] = ops; opts[PROMPTBANG] = opb; opts[PROMPTPERCENT] = opp; diff --git a/Src/utils.c b/Src/utils.c index 32492a93b..55f2d1ab0 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1543,7 +1543,8 @@ preprompt(void) if (!eolmark) eolmark = "%B%S%#%s%b"; opts[PROMPTPERCENT] = 1; - str = promptexpand(eolmark, 1, NULL, NULL, NULL); + txtunknownattrs = TXT_ATTR_ALL; + str = promptexpand(eolmark, 1, NULL, NULL); countprompt(str, &w, 0, -1); opts[PROMPTPERCENT] = percents; zputs(str, shout); @@ -1713,7 +1714,7 @@ printprompt4(void) opts[XTRACE] = 0; unmetafy(s, &l); s = unmetafy(promptexpand(metafy(s, l, META_NOALLOC), - 0, NULL, NULL, NULL), &l); + 0, NULL, NULL), &l); opts[XTRACE] = t; fprintf(xtrerr, "%s", s); @@ -3211,7 +3212,7 @@ spckword(char **s, int hist, int cmd, int ask) x = 'n'; } else if (shout) { char *pptbuf; - pptbuf = promptexpand(sprompt, 0, best, guess, NULL); + pptbuf = promptexpand(sprompt, 0, best, guess); zputs(pptbuf, shout); free(pptbuf); fflush(shout); diff --git a/Src/zsh.h b/Src/zsh.h index b035a1184..35ae033e3 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -2681,25 +2681,8 @@ struct ttyinfo { #define TXTFGCOLOUR 0x0008 #define TXTBGCOLOUR 0x0010 -#define TXT_ATTR_ON_MASK 0x001F - -#define txtisset(X) (txtattrmask & (X)) -#define txtset(X) (txtattrmask |= (X)) -#define txtunset(X) (txtattrmask &= ~(X)) - -#define TXTNOBOLDFACE 0x0020 -#define TXTNOSTANDOUT 0x0040 -#define TXTNOUNDERLINE 0x0080 -#define TXTNOFGCOLOUR 0x0100 -#define TXTNOBGCOLOUR 0x0200 - -#define TXT_ATTR_OFF_MASK 0x03E0 -/* Bits to shift off right to get on */ -#define TXT_ATTR_OFF_ON_SHIFT 5 -#define TXT_ATTR_OFF_FROM_ON(attr) \ - (((attr) & TXT_ATTR_ON_MASK) << TXT_ATTR_OFF_ON_SHIFT) -#define TXT_ATTR_ON_FROM_OFF(attr) \ - (((attr) & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT) +#define TXT_ATTR_ALL 0x001F + /* * Indicates to zle_refresh.c that the character entry is an * index into the list of multiword symbols. @@ -2707,7 +2690,7 @@ struct ttyinfo { #define TXT_MULTIWORD_MASK 0x0400 /* used when, e.g an invalid colour is specified */ -#define TXT_ERROR 0x0800 +#define TXT_ERROR 0xF00000F000000800 /* Mask for colour to use in foreground */ #define TXT_ATTR_FG_COL_MASK 0x000000FFFFFF0000 @@ -2723,26 +2706,19 @@ struct ttyinfo { /* Flag to indicate that background is a 24-bit colour */ #define TXT_ATTR_BG_24BIT 0x8000 -/* Things to turn on, including values for the colour elements */ -#define TXT_ATTR_ON_VALUES_MASK \ - (TXT_ATTR_ON_MASK|TXT_ATTR_FG_COL_MASK|TXT_ATTR_BG_COL_MASK|\ - TXT_ATTR_FG_24BIT|TXT_ATTR_BG_24BIT) - /* Mask out everything to do with setting a foreground colour */ -#define TXT_ATTR_FG_ON_MASK \ +#define TXT_ATTR_FG_MASK \ (TXTFGCOLOUR|TXT_ATTR_FG_COL_MASK|TXT_ATTR_FG_24BIT) /* Mask out everything to do with setting a background colour */ -#define TXT_ATTR_BG_ON_MASK \ +#define TXT_ATTR_BG_MASK \ (TXTBGCOLOUR|TXT_ATTR_BG_COL_MASK|TXT_ATTR_BG_24BIT) /* Mask out everything to do with activating colours */ -#define TXT_ATTR_COLOUR_ON_MASK \ - (TXT_ATTR_FG_ON_MASK|TXT_ATTR_BG_ON_MASK) +#define TXT_ATTR_COLOUR_MASK \ + (TXT_ATTR_FG_MASK|TXT_ATTR_BG_MASK) -#define txtchangeisset(T,X) ((T) & (X)) #define txtchangeget(T,A) (((T) & A ## _MASK) >> A ## _SHIFT) -#define txtchangeset(T, X, Y) ((void)(T && (*T &= ~(Y), *T |= (X)))) /* * For outputting sequences to change colour: specify foreground @@ -2750,7 +2726,6 @@ struct ttyinfo { */ #define COL_SEQ_FG (0) #define COL_SEQ_BG (1) -#define COL_SEQ_COUNT (2) struct color_rgb { unsigned int red, green, blue; @@ -2766,11 +2741,7 @@ enum { /* Raw output: use stdout rather than shout */ TSC_RAW = 0x0001, /* Output to current prompt buffer: only used when assembling prompt */ - TSC_PROMPT = 0x0002, - /* Mask to get the output mode */ - TSC_OUTPUT_MASK = 0x0003, - /* Change needs reset of other attributes */ - TSC_DIRTY = 0x0004 + TSC_PROMPT = 0x0002 }; /****************************************/ diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst index 6879e6fd1..a0abb7e1d 100644 --- a/Test/D01prompt.ztst +++ b/Test/D01prompt.ztst @@ -258,6 +258,19 @@ fi 0:Equivalence of terminal colour settings (background colour) + A1=${(%):-%s} + A2=${(%):-%u} + A3=${(%):-%s%u%s} + [[ $A3 = $A1$A2 && -n $A1 && -n $A2 ]] +0:Attribute optimisation - preserve initial disabling of attribute but drop useless later one + + : ${(%):-%K{blue}} + A1="${(%):-%b}x" + : ${(%):-%k} + A2="${(%):-%b}x" + [[ $A1 = $A2 && -n $A1 && -n $A2 ]] +0:Don't restore attributes from earlier substitution after disabling bold + (RPS1=foo; echo $RPS1 $RPROMPT) (RPS2=bar; echo $RPS2 $RPROMPT2) -fD:RPS1 and RPROMPT are aliases (regression from 5.0.6) (workers/49600) diff --git a/Test/X04zlehighlight.ztst b/Test/X04zlehighlight.ztst index f84c02505..6d9ca4a48 100644 --- a/Test/X04zlehighlight.ztst +++ b/Test/X04zlehighlight.ztst @@ -79,7 +79,7 @@ zpty_line 1 p # the line of interest, preserving escapes ("p") zpty_stop 0:region highlight - standout overlapping on other region_highlight entry ->0m27m24mtr7mu27me word2 word3 +>0m27m24mtr7mu0me word2 word3 zpty_start zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=green" ); }' @@ -90,7 +90,7 @@ zpty_line 1 p # the line of interest, preserving escapes ("p") zpty_stop 0:basic region_highlight with 8 colors ->0m27m24mCDE|32|trueCDE|39| +>0m27m24mCDE|32|true0m zpty_start zpty_input 'rh_widget() { region_highlight+=( "0 4 fg=green memo=someplugin" ); typeset -p region_highlight }' @@ -145,7 +145,7 @@ zpty_line 1 p # the line of interest, preserving escapes ("p") zpty_stop 0:basic region_highlight with true-color (hex-triplets) ->0m27m24m38;2;4;8;16mtrueCDE|39| +>0m27m24m38;2;4;8;16mtrue0m zpty_start zpty_input 'zmodload zsh/nearcolor' @@ -157,7 +157,7 @@ zpty_line 1 p # the line of interest, preserving escapes ("p") zpty_stop 0:basic region_highlight with near-color (hex-triplets at input) ->0m27m24mCDE|3232|trueCDE|39| +>0m27m24mCDE|3232|true0m zpty_start zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=green" ); rh2; }' @@ -169,7 +169,7 @@ zpty_line 1 p # the line of interest, preserving escapes ("p") zpty_stop 0:overlapping region_highlight with 8 colors ->0m27m24mCDE|32|tCDE|31|rCDE|39|CDE|32|ueCDE|39| +>0m27m24mCDE|32|tCDE|31|rCDE|32|ue0m zpty_start zpty_input 'rh_widget() { BUFFER="true"; region_highlight+=( "0 4 fg=#00cc00" ); rh2; }' @@ -181,7 +181,7 @@ zpty_line 1 p # the line of interest, preserving escapes ("p") zpty_stop 0:overlapping region_highlight with true-color ->0m27m24m38;2;0;204;0mt38;2;204;0;0mrCDE|39|38;2;0;204;0mueCDE|39| +>0m27m24m38;2;0;204;0mt38;2;204;0;0mr38;2;0;204;0mue0m zpty_start zpty_input 'zmodload zsh/nearcolor' @@ -194,7 +194,7 @@ zpty_line 1 p # the line of interest, preserving escapes ("p") zpty_stop 0:overlapping region_highlight with near-color (hex-triplets at input) ->0m27m24mCDE|340|tCDE|3160|rCDE|39|CDE|340|ueCDE|39| +>0m27m24mCDE|340|tCDE|3160|rCDE|340|ue0m zpty_start zpty_input 'f () { zle clear-screen; zle g -f nolast; BUFFER=": ${(q)LASTWIDGET}" }; zle -N f' -- cgit v1.2.3 From 511e020c68955f737036b7febd360615517a3637 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sun, 12 Feb 2023 11:21:23 -0800 Subject: 51360: Initial implementation of named references. --- ChangeLog | 4 + Src/Modules/param_private.c | 17 +++- Src/Modules/parameter.c | 4 +- Src/builtin.c | 56 ++++++++++++- Src/params.c | 190 ++++++++++++++++++++++++++++++++++++++++++-- Src/subst.c | 3 +- Src/zsh.h | 8 +- 7 files changed, 266 insertions(+), 16 deletions(-) (limited to 'Src/Modules') 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/Modules') 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 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/Modules') 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 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/Modules') 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 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/Modules') 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/Modules') 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 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/Modules') 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 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/Modules') 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/Modules') 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/Modules') 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/Modules') 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 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/Modules') 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 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/Modules') 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 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/Modules') 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/Modules') 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 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/Modules') 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 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/Modules') 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 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/Modules') 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 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/Modules') 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/Modules') 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/Modules') 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 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/Modules') 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 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/Modules') 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/Modules') 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 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/Modules') 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 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/Modules') 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 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/Modules') 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 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/Modules') 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/Modules') 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 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/Modules') 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 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/Modules') 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 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/Modules') 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/Modules') 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 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/Modules') 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/Modules') 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/Modules') 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 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/Modules') 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 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/Modules') 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/Modules') 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/Modules') 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 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/Modules') 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 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/Modules') 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/Modules') 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 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/Modules') 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/Modules') 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 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/Modules') 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/Modules') 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 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/Modules') 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 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/Modules') 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/Modules') 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