From 8e0253af022abe9f9225352aae1088d6621a81ab Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Tue, 24 Dec 2019 18:25:08 +0000 Subject: 45138: Add zformat unit tests. --- Src/Modules/zutil.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Src/Modules') diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 24659cb16..de5fe8034 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -913,13 +913,13 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) switch (opt) { case 'f': { - char **ap, *specs[256], *out; + char **ap, *specs[256] = {0}, *out; int olen, oused = 0; - memset(specs, 0, 256 * sizeof(char *)); - specs['%'] = "%"; specs[')'] = ")"; + + /* Parse the specs in argv. */ for (ap = args + 2; *ap; ap++) { if (!ap[0][0] || ap[0][0] == '-' || ap[0][0] == '.' || idigit(ap[0][0]) || ap[0][1] != ':') { -- cgit v1.2.3 From 525faae5498adb4b4f1c0f4657b9ef71fc31c4d6 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Tue, 24 Dec 2019 18:25:09 +0000 Subject: 45137: zformat: Allow the specifying minimum width and a dot with an empty maximum width. Before this commit, format specs such as '%5.s' would be printed literally. Now, they are treated as equivalent to '%5s'. The '.' character is not allowed to be used in specs, so there is no incompatibility. --- ChangeLog | 4 ++++ Src/Modules/zutil.c | 3 +-- Test/V13zformat.ztst | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index fe3b6951c..321216c7a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2019-12-26 Daniel Shahaf + * 45137: Src/Modules/zutil.c, Test/V13zformat.ztst: zformat: + Allow the specifying minimum width and a dot with an empty + maximum width. + * 45138: Src/Modules/zutil.c, Test/V13zformat.ztst: Add zformat unit tests. diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index de5fe8034..7d9bf05d6 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -797,8 +797,7 @@ 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'; - } - else if (testit) + } else if (*s == '.' || testit) s++; if (testit && STOUC(*s)) { diff --git a/Test/V13zformat.ztst b/Test/V13zformat.ztst index d8de2bb04..982866e13 100644 --- a/Test/V13zformat.ztst +++ b/Test/V13zformat.ztst @@ -17,12 +17,14 @@ zformat_and_print_s '%s' foo zformat_and_print_s '%5s' min zformat_and_print_s '%-5s' neg + zformat_and_print_s '%5.s' empty zformat_and_print_s '%.5s' max zformat_and_print_s '%.5s' truncated 0:basic zformat test >'foo' >'min ' >' neg' +>'empty' >'max' >'trunc' -- cgit v1.2.3 From 25c9b61a6663f90bfb22fa73c1a7aa4fb9dee4ae Mon Sep 17 00:00:00 2001 From: Cedric Ware Date: Mon, 20 Apr 2020 12:10:01 -0500 Subject: 45708: zsh/system: Enable sub-second timeout in zsystem flock --- ChangeLog | 4 ++ Doc/Zsh/mod_system.yo | 16 ++++-- Src/Modules/system.c | 79 ++++++++++++++++++++++++-- Src/compat.c | 26 +++++++++ Src/utils.c | 36 ++++++++++++ Test/V14system.ztst | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 302 insertions(+), 9 deletions(-) create mode 100644 Test/V14system.ztst (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 50c05fa59..b73a7ed80 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2020-04-20 dana + * Cedric Ware: 45708: Doc/Zsh/mod_system.yo, + Src/Modules/system.c, Src/compat.c, Src/utils.c, + Test/V14system.ztst: Enable sub-second timeout in zsystem flock + * 45702: Doc/Zsh/compsys.yo: Improve documentation of {insert,separate}-sections diff --git a/Doc/Zsh/mod_system.yo b/Doc/Zsh/mod_system.yo index 6292af071..06ea87918 100644 --- a/Doc/Zsh/mod_system.yo +++ b/Doc/Zsh/mod_system.yo @@ -166,7 +166,7 @@ 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 the error that occurred. ) -xitem(tt(zsystem flock) [ tt(-t) var(timeout) ] [ tt(-f) var(var) ] [tt(-er)] var(file)) +xitem(tt(zsystem flock) [ tt(-t) var(timeout) ] [ tt(-i) var(interval) ] [ tt(-f) var(var) ] [tt(-er)] var(file)) item(tt(zsystem flock -u) var(fd_expr))( The builtin tt(zsystem)'s subcommand tt(flock) performs advisory file locking (via the manref(fcntl)(2) system call) over the entire contents @@ -196,9 +196,17 @@ a safety check that the file descriptor is in use for file locking. By default the shell waits indefinitely for the lock to succeed. The option tt(-t) var(timeout) specifies a timeout for the lock in -seconds; currently this must be an integer. The shell will attempt -to lock the file once a second during this period. If the attempt -times out, status 2 is returned. +seconds; fractional seconds are allowed. During this period, the +shell will attempt to lock the file every var(interval) seconds +if the tt(-i) var(interval) option is given, otherwise once a second. +(This var(interval) is shortened before the last attempt if needed, +so that the shell waits only until the var(timeout) and not longer.) +If the attempt times out, status 2 is returned. + +(Note: var(timeout) must be less than +ifzman(2^30-1)ifnzman(2NOTRANS(@sup{30})-1) seconds (about 34 years), +and var(interval) must be less than 0.999 * LONG_MAX microseconds +(only about 35 minutes on 32-bit systems).) If the option tt(-e) is given, the file descriptor for the lock is preserved when the shell uses tt(exec) to start a new process; diff --git a/Src/Modules/system.c b/Src/Modules/system.c index fb3d80773..972aa0767 100644 --- a/Src/Modules/system.c +++ b/Src/Modules/system.c @@ -29,6 +29,7 @@ #include "system.mdh" #include "system.pro" +#include #ifdef HAVE_POLL_H # include @@ -531,7 +532,9 @@ static int bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { int cloexec = 1, unlock = 0, readlock = 0; - zlong timeout = -1; + double timeout = -1; + long timeout_interval = 1e6; + mnumber timeout_param; char *fdvar = NULL; #ifdef HAVE_FCNTL_H struct flock lck; @@ -583,7 +586,51 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } else { optarg = *args++; } - timeout = mathevali(optarg); + timeout_param = matheval(optarg); + timeout = (timeout_param.type & MN_FLOAT) ? + timeout_param.u.d : (double)timeout_param.u.l; + + /* + * timeout must not overflow time_t, but little is known + * about this type's limits. Conservatively limit to 2^30-1 + * (34 years). Then it can only overflow if time_t is only + * a 32-bit int and CLOCK_MONOTONIC is not supported, in which + * case there is a Y2038 problem anyway. + */ + if (timeout < 1e-6 || timeout > 1073741823.) { + zwarnnam(nam, "flock: invalid timeout value: '%s'", + optarg); + return 1; + } + break; + + case 'i': + /* retry interval in seconds */ + if (optptr[1]) { + optarg = optptr + 1; + optptr += strlen(optarg) - 1; + } else if (!*args) { + zwarnnam(nam, + "flock: option %c requires " + "a numeric retry interval", + opt); + return 1; + } else { + optarg = *args++; + } + timeout_param = matheval(optarg); + if (!(timeout_param.type & MN_FLOAT)) { + timeout_param.type = MN_FLOAT; + timeout_param.u.d = (double)timeout_param.u.l; + } + timeout_param.u.d *= 1e6; + if (timeout_param.u.d < 1 + || timeout_param.u.d > 0.999 * LONG_MAX) { + zwarnnam(nam, "flock: invalid interval value: '%s'", + optarg); + return 1; + } + timeout_interval = (long)timeout_param.u.d; break; case 'u': @@ -647,7 +694,24 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) lck.l_len = 0; /* lock the whole file */ if (timeout > 0) { - time_t end = time(NULL) + (time_t)timeout; + /* + * Get current time, calculate timeout time. + * No need to check for overflow, already checked above. + */ + struct timespec now, end; + double timeout_s; + long remaining_us; + zgettime_monotonic_if_available(&now); + end.tv_sec = now.tv_sec; + end.tv_nsec = now.tv_nsec; + end.tv_nsec += modf(timeout, &timeout_s) * 1000000000L; + end.tv_sec += timeout_s; + if (end.tv_nsec >= 1000000000L) { + end.tv_nsec -= 1000000000L; + end.tv_sec += 1; + } + + /* Try acquiring lock, loop until timeout. */ while (fcntl(flock_fd, F_SETLK, &lck) < 0) { if (errflag) { zclose(flock_fd); @@ -658,11 +722,16 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) zwarnnam(nam, "failed to lock file %s: %e", args[0], errno); return 1; } - if (time(NULL) >= end) { + zgettime_monotonic_if_available(&now); + remaining_us = timespec_diff_us(&now, &end); + if (remaining_us <= 0) { zclose(flock_fd); return 2; } - sleep(1); + if (remaining_us <= timeout_interval) { + timeout_interval = remaining_us; + } + zsleep(timeout_interval); } } else { while (fcntl(flock_fd, timeout == 0 ? F_SETLK : F_SETLKW, &lck) < 0) { diff --git a/Src/compat.c b/Src/compat.c index 74e426fba..817bb4aaf 100644 --- a/Src/compat.c +++ b/Src/compat.c @@ -126,6 +126,32 @@ zgettime(struct timespec *ts) return ret; } +/* Likewise with CLOCK_MONOTONIC if available. */ + +/**/ +mod_export int +zgettime_monotonic_if_available(struct timespec *ts) +{ + int ret = -1; + +#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) + struct timespec dts; + if (clock_gettime(CLOCK_MONOTONIC, &dts) < 0) { + zwarn("unable to retrieve CLOCK_MONOTONIC time: %e", errno); + ret--; + } else { + ret++; + ts->tv_sec = (time_t) dts.tv_sec; + ts->tv_nsec = (long) dts.tv_nsec; + } +#endif + + if (ret) { + ret = zgettime(ts); + } + return ret; +} + /* compute the difference between two calendar times */ diff --git a/Src/utils.c b/Src/utils.c index 69885fed3..e258ef836 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -2748,6 +2748,42 @@ read_poll(int fd, int *readchar, int polltty, zlong microseconds) return (ret > 0); } +/* + * Return the difference between 2 times, given as struct timespec*, + * expressed in microseconds, as a long. If the difference doesn't fit + * into a long, return LONG_MIN or LONG_MAX so that the times can still + * be compared. + * + * Note: returns a long rather than a zlong because zsleep() below + * takes a long. + */ + +/**/ +long +timespec_diff_us(const struct timespec *t1, const struct timespec *t2) +{ + int reverse = (t1->tv_sec > t2->tv_sec); + time_t diff_sec; + long diff_usec, max_margin, res; + + /* Don't just subtract t2-t1 because time_t might be unsigned. */ + diff_sec = (reverse ? t1->tv_sec - t2->tv_sec : t2->tv_sec - t1->tv_sec); + if (diff_sec > LONG_MAX / 1000000L) { + goto overflow; + } + res = diff_sec * 1000000L; + max_margin = LONG_MAX - res; + diff_usec = (reverse ? + t1->tv_nsec - t2->tv_nsec : t2->tv_nsec - t1->tv_nsec + ) / 1000; + if (diff_usec <= max_margin) { + res += diff_usec; + return (reverse ? -res : res); + } + overflow: + return (reverse ? LONG_MIN : LONG_MAX); +} + /* * Sleep for the given number of microseconds --- must be within * range of a long at the moment, but this is only used for diff --git a/Test/V14system.ztst b/Test/V14system.ztst new file mode 100644 index 000000000..b8af96cda --- /dev/null +++ b/Test/V14system.ztst @@ -0,0 +1,150 @@ +# Test zsh/system module + +%prep + + if zmodload -s zsh/system && zmodload -s zsh/zselect; then + tst_dir=V14.tmp + mkdir -p -- $tst_dir + else + ZTST_unimplemented='the zsh/system and zsh/zselect modules are not available' + fi + : > $tst_dir/file # File on which to acquire flock. + +%test + + ( + zsystem flock -t 0.1 -i 0.000001 $tst_dir/file + ) +0:zsystem flock valid time arguments + + ( + zsystem flock -t -1 $tst_dir/file || + zsystem flock -t 0.49e-6 $tst_dir/file || + zsystem flock -t 1073741824 $tst_dir/file || + zsystem flock -t 1e100 $tst_dir/file || + zsystem flock -i -1 $tst_dir/file || + zsystem flock -i 0.49e-6 $tst_dir/file || + zsystem flock -i 1e100 $tst_dir/file + ) +1:zsystem flock invalid time arguments +?(eval):zsystem:2: flock: invalid timeout value: '-1' +?(eval):zsystem:3: flock: invalid timeout value: '0.49e-6' +?(eval):zsystem:4: flock: invalid timeout value: '1073741824' +?(eval):zsystem:5: flock: invalid timeout value: '1e100' +?(eval):zsystem:6: flock: invalid interval value: '-1' +?(eval):zsystem:7: flock: invalid interval value: '0.49e-6' +?(eval):zsystem:8: flock: invalid interval value: '1e100' + + ( + # Lock file for 1 second in the background. + lock_flag=$tst_dir/locked1 + (zsystem flock $tst_dir/file \ + && touch $lock_flag \ + && zselect -t 100 + mv $lock_flag $lock_flag.done) & + # Wait until sub-shell above has started. + while ! [[ -f $lock_flag || -f $lock_flag.done ]]; do + zselect -t 1 + done + if [[ -f $lock_flag.done ]]; then + echo "Background shell should not have completed already." 1>&2 + else + # Attempt to lock file with 0.5 second timeout: must fail. + zsystem flock -t 0.5 $tst_dir/file + fi + ) +2:zsystem flock unsuccessful wait test +F:This timing test might fail due to process scheduling issues unrelated to zsh. + + ( + # Lock file for 0.5 second in the background. + lock_flag=$tst_dir/locked2 + (zsystem flock $tst_dir/file \ + && touch $lock_flag \ + && zselect -t 50 + mv $lock_flag $lock_flag.done) & + # Wait until sub-shell above has started. + while ! [[ -f $lock_flag || -f $lock_flag.done ]]; do + zselect -t 1 + done + if [[ -f $lock_flag.done ]]; then + echo "Background shell should not have completed already." 1>&2 + fi + typeset -F SECONDS + start=$SECONDS + # Attempt to lock file without a timeout: + # must succeed after sub-shell above releases it (0.5 second). + if zsystem flock $tst_dir/file; then + elapsed=$[ $SECONDS - $start ] + if [[ $elapsed -ge 0.3 && $elapsed -le 0.7 ]]; then + echo "elapsed time seems OK" 1>&2 + else + echo "elapsed time $elapsed should be ~ 0.5 second" 1>&2 + fi + fi + ) +0:zsystem flock successful wait test, no timeout +?elapsed time seems OK +F:This timing test might fail due to process scheduling issues unrelated to zsh. + + ( + # Lock file for 0.5 second in the background. + lock_flag=$tst_dir/locked3 + (zsystem flock $tst_dir/file \ + && touch $lock_flag \ + && zselect -t 50 + mv $lock_flag $lock_flag.done) & + # Wait until sub-shell above has started. + while ! [[ -f $lock_flag || -f $lock_flag.done ]]; do + zselect -t 1 + done + if [[ -f $lock_flag.done ]]; then + echo "Background shell should not have completed already." 1>&2 + fi + typeset -F SECONDS + start=$SECONDS + # Attempt to lock file with 1-second timeout: + # must succeed 1 second after start because we retry every 1 second. + if zsystem flock -t 1 $tst_dir/file; then + elapsed=$[ $SECONDS - $start ] + if [[ $elapsed -ge 0.8 && $elapsed -le 1.2 ]]; then + echo "elapsed time seems OK" 1>&2 + else + echo "elapsed time $elapsed should be ~ 1 second" 1>&2 + fi + fi + ) +0:zsystem flock successful wait test, integral seconds +?elapsed time seems OK +F:This timing test might fail due to process scheduling issues unrelated to zsh. + + ( + # Lock file for 0.25 second in the background. + lock_flag=$tst_dir/locked4 + (zsystem flock $tst_dir/file \ + && touch $lock_flag \ + && zselect -t 25 + mv $lock_flag $lock_flag.done) & + # Wait until sub-shell above has started. + while ! [[ -f $lock_flag || -f $lock_flag.done ]]; do + zselect -t 1 + done + if [[ -f $lock_flag.done ]]; then + echo "Background shell should not have completed already." 1>&2 + fi + typeset -F SECONDS + start=$SECONDS + # Attempt to lock file with 0.4-second timeout, retrying every 0.1 second: + # must succeed 0.3 second after start. + if zsystem flock -t 0.4 -i 0.1 $tst_dir/file; then + elapsed=$[ $SECONDS - $start ] + if [[ $elapsed -ge 0.2 && $elapsed -le 0.5 ]]; then + echo "elapsed time seems OK" 1>&2 + else + echo "elapsed time $elapsed should be ~ 0.3 second" 1>&2 + fi + fi + ) +0:zsystem flock successful wait test, fractional seconds +?elapsed time seems OK +F:This timing test might fail due to process scheduling issues unrelated to zsh. -- cgit v1.2.3 From 34d69acbef44f654ea51d762ffdb02f5b1e8b9e1 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Wed, 29 Apr 2020 00:30:17 +0000 Subject: 45737 (+ docs, and update the test from 45722): zstyle: When determining the weight (specificity) of a pattern, consider the number of components before anything else, as documented. --- ChangeLog | 6 ++++++ Doc/Zsh/mod_zutil.yo | 6 +++--- README | 19 +++++++++++++++++++ Src/Modules/zutil.c | 13 +++++++++++-- Test/V05styles.ztst | 2 +- 5 files changed, 40 insertions(+), 6 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index ec1e413c7..1fd8b9944 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2020-05-02 Daniel Shahaf + * 45737 (+ docs, and update the test from 45722): + Doc/Zsh/mod_zutil.yo, README, Src/Modules/zutil.c, + Test/V05styles.ztst: zstyle: When determining the weight + (specificity) of a pattern, consider the number of components + before anything else, as documented. + * unposted: Test/V05styles.ztst: Revert unintentional move from 45722. diff --git a/Doc/Zsh/mod_zutil.yo b/Doc/Zsh/mod_zutil.yo index c69233f9d..9c50e6122 100644 --- a/Doc/Zsh/mod_zutil.yo +++ b/Doc/Zsh/mod_zutil.yo @@ -40,8 +40,8 @@ that it looks up the tt(preferred-precipitation) style under the `tt(:weather:)var(continent)tt(:)var(day-of-the-week)tt(:)var(phase-of-the-moon))' context. According to this, you might set the following in your tt(zshrc): -example(zstyle ':weather:*:Sunday:*' preferred-precipitation snow -zstyle ':weather:europe:*' preferred-precipitation rain) +example(zstyle ':weather:europe:*' preferred-precipitation rain +zstyle ':weather:*:Sunday:*' preferred-precipitation snow) Then the plugin would run under the hood a command such as @@ -52,7 +52,7 @@ On Sundays tt($REPLY) would be set to `tt(snow)'; in Europe it would be set to `tt(rain)'; and on Sundays in Europe it would be set to `tt(snow)' again, because the patterns `tt(:weather:europe:*)' and `tt(:weather:*:Sunday:*)' both match the var(context) argument to tt(zstyle -s), are equally specific, and the -latter was defined first. +latter is more specific (because it has more colon-separated components). em(Usage) diff --git a/README b/README index b87f660bf..8ae615153 100644 --- a/README +++ b/README @@ -64,6 +64,25 @@ The sh-compatible function definition syntax, "f() { ... }", is unchanged. The time-out (-t) value given to zsh/system's `zsystem flock` command is now limited to 2^30-1 seconds (= a little over 34 years). +zstyle: For background, recall that the zstyle builtin associates styles with +values for particular contexts, and when a context (such as ':foo:bar:baz') is +matched by multiple patterns (such as ':foo:*' and ':foo:bar:*'), the style's +value for the more specific of the patterns is used. In zsh 5.8 and earlier +the determination of which pattern is "more specific" used semantics slightly +different to those the documentation promised. The implementation was changed +to match the documentation. The upshot of this is that if you set a single +style in multiple contexts, zsh 5.9 may use the values set for a pattern other +than the one zsh 5.8 used. For example, if you do + zstyle ':foo:bar:*' style value1 + zstyle ':foo:*:baz:*' style value2 +and the style is looked up under a context that both patterns match (e.g., +:foo:bar:baz:qux), zsh 5.9 will use value2 -- which is consistent with the +documentation of both 5.8 and 5.9 -- but zsh 5.8 will use value1. If this +affects you, make the implied colons in the first pattern explicit, as in: + zstyle ':foo:bar:*:*' style value1 + zstyle ':foo:*:baz:*' style value2 +This will use value1 in both 5.8 and 5.9. + Incompatibilities between 5.7.1 and 5.8 --------------------------------------- diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 7d9bf05d6..5c96d06c1 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -96,7 +96,7 @@ struct stypat { Stypat next; char *pat; /* pattern string */ Patprog prog; /* compiled pattern */ - int weight; /* how specific is the pattern? */ + zulong weight; /* how specific is the pattern? */ Eprog eval; /* eval-on-retrieve? */ char **vals; }; @@ -293,7 +293,9 @@ newzstyletable(int size, char const *name) static int setstypat(Style s, char *pat, Patprog prog, char **vals, int eval) { - int weight, tmp, first; + zulong weight; + int tmp; + int first; char *str; Stypat p, q, qq; Eprog eprog = NULL; @@ -348,6 +350,12 @@ setstypat(Style s, char *pat, Patprog prog, char **vals, int eval) * - A component that's a literal string scores 2 points. * - The score of a pattern is the sum of the score of its components. * + * The result of this calculation is stored in the low bits of 'weight'. + * The high bits of 'weight' are used to store the number of ':'-separated + * components. This provides a lexicographic comparison: first compare + * the number of components, and if that's equal, compare the specificity + * of the components. + * * This corresponds to the notion of 'more specific' in the zshmodules(1) * documentation of zstyle. */ @@ -367,6 +375,7 @@ setstypat(Style s, char *pat, Patprog prog, char **vals, int eval) if (*str == ':') { /* Yet another component. */ + weight += ZLONG_CONST(1) << (CHAR_BIT * sizeof(weight) / 2); first = 1; weight += tmp; diff --git a/Test/V05styles.ztst b/Test/V05styles.ztst index 9b5fc4517..048751941 100644 --- a/Test/V05styles.ztst +++ b/Test/V05styles.ztst @@ -163,4 +163,4 @@ ) 0:the example in the documentation remains correct >snow ->rain +>snow -- cgit v1.2.3 From 4d2bcf2fe7637b641ccde31a8ca7c4875f0699c1 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Mon, 27 Apr 2020 19:30:39 +0000 Subject: 45729: internal: Add a second parameter to zlinklist2array(), analogously to hlinklist2array(). Will be used in the next commit. --- ChangeLog | 7 +++++++ Src/Modules/curses.c | 4 ++-- Src/Zle/compcore.c | 4 ++-- Src/Zle/computil.c | 4 ++-- Src/builtin.c | 10 +++++----- Src/linklist.c | 13 +++++++++---- 6 files changed, 27 insertions(+), 15 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index ad366f1c5..1942d9aab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2020-05-03 Daniel Shahaf + + * 45729: Src/Modules/curses.c, Src/Zle/compcore.c, + Src/Zle/computil.c, Src/builtin.c, Src/linklist.c: internal: + Add a second parameter to zlinklist2array(), analogously to + hlinklist2array(). + 2020-05-02 Daniel Shahaf * unposted: Util/zyodl.vim: Use 'conceal' for some macros that diff --git a/Src/Modules/curses.c b/Src/Modules/curses.c index 19f285e34..e46903916 100644 --- a/Src/Modules/curses.c +++ b/Src/Modules/curses.c @@ -1212,7 +1212,7 @@ zccmd_input(const char *nam, char **args) addlinknode(margs, "CTRL"); if (mevent.bstate & BUTTON_ALT) addlinknode(margs, "ALT"); - if (!setaparam(args[3], zlinklist2array(margs))) + if (!setaparam(args[3], zlinklist2array(margs, 1))) return 1; } else { #endif @@ -1464,7 +1464,7 @@ zccmd_querychar(const char *nam, char **args) } /* Turn this into an array and store it. */ - return !setaparam(args[1] ? args[1] : "reply", zlinklist2array(clist)); + return !setaparam(args[1] ? args[1] : "reply", zlinklist2array(clist, 1)); } diff --git a/Src/Zle/compcore.c b/Src/Zle/compcore.c index 7e3badc57..958fef8e7 100644 --- a/Src/Zle/compcore.c +++ b/Src/Zle/compcore.c @@ -648,7 +648,7 @@ callcompfunc(char *s, char *fn) if (compredirs) freearray(compredirs); if (rdstrs) - compredirs = zlinklist2array(rdstrs); + compredirs = zlinklist2array(rdstrs, 1); else compredirs = (char **) zshcalloc(sizeof(char *)); @@ -1922,7 +1922,7 @@ set_comp_sep(void) mod_export void set_list_array(char *name, LinkList l) { - setaparam(name, zlinklist2array(l)); + setaparam(name, zlinklist2array(l, 1)); } /* Get the words from a variable or a (list of words). */ diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index 90db8b4b8..ddfa70077 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -3591,7 +3591,7 @@ bin_compvalues(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) if (cv_laststate.vals) { char **ret; - ret = zlinklist2array(cv_laststate.vals); + ret = zlinklist2array(cv_laststate.vals, 1); sethparam(args[1], ret); return 0; @@ -4016,7 +4016,7 @@ bin_comptry(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) set = (Ctset) zalloc(sizeof(*set)); - set->tags = zlinklist2array(list); + set->tags = zlinklist2array(list, 1); set->next = NULL; set->ptr = NULL; set->tag = NULL; diff --git a/Src/builtin.c b/Src/builtin.c index 3dab3f9b4..d5a874a95 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2280,7 +2280,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), } else if (asg->flags & ASG_ARRAY) { int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; if (!(pm = assignaparam(pname, asg->value.array ? - zlinklist2array(asg->value.array) : + zlinklist2array(asg->value.array, 1) : mkarray(NULL), flags))) return NULL; } @@ -2442,7 +2442,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), } else if (PM_TYPE(on) == PM_ARRAY && ASG_ARRAYP(asg)) { int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; if (!(pm = assignaparam(pname, asg->value.array ? - zlinklist2array(asg->value.array) : + zlinklist2array(asg->value.array, 1) : mkarray(NULL), flags))) return NULL; dont_set = 1; @@ -2536,7 +2536,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), arrayval = mkarray(NULL); } } else if (asg->value.array) - arrayval = zlinklist2array(asg->value.array); + arrayval = zlinklist2array(asg->value.array, 1); else arrayval = mkarray(NULL); if (!(pm=assignaparam(pname, arrayval, flags))) @@ -2923,7 +2923,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) apm->ename = ztrdup(asg0.name); if (asg->value.array) { int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0; - assignaparam(asg->name, zlinklist2array(asg->value.array), flags); + assignaparam(asg->name, zlinklist2array(asg->value.array, 1), flags); } else if (oldval) assignsparam(asg0.name, oldval, 0); unqueue_signals(); @@ -3901,7 +3901,7 @@ bin_whence(char *nam, char **argv, Options ops, int func) } unqueue_signals(); if (all) { - allmatched = argv = zlinklist2array(matchednodes); + allmatched = argv = zlinklist2array(matchednodes, 1); matchednodes = NULL; popheap(); } else diff --git a/Src/linklist.c b/Src/linklist.c index 85d9bb367..f64685d9e 100644 --- a/Src/linklist.c +++ b/Src/linklist.c @@ -438,22 +438,27 @@ hlinklist2array(LinkList list, int copy) /* * Convert a linked list whose data elements are strings to - * an array. The result is a permanently allocated, freearrayable - * array. + * a permanently-allocated array. The elements of the array are the same + * elements as the linked list data if copy is 0, else they are duplicated + * into permanent memory so the result is a permanently allocated, + * freearrayable array that's a deep copy of the linked list. */ /**/ mod_export char ** -zlinklist2array(LinkList list) +zlinklist2array(LinkList list, int copy) { int l = countlinknodes(list); char **ret = (char **) zalloc((l + 1) * sizeof(char *)), **p; LinkNode n; for (n = firstnode(list), p = ret; n; incnode(n), p++) { - *p = ztrdup((char *) getdata(n)); + *p = (char *) getdata(n); + if (copy) + *p = ztrdup(*p); } *p = NULL; return ret; } + -- cgit v1.2.3 From fb1aa3fe1e78ee1f521b64db062addf74ea9d263 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Tue, 26 May 2020 22:06:39 +0000 Subject: 45923 (with memory leak fixed, cf. 45924): zprof: Don't tally all anonymous functions as though they were a single function named "(anon)". Before: % zmodload zsh/zprof % () : % () : % zprof num calls time self name ----------------------------------------------------------------------------------- 1) 2 0.08 0.04 100.00% 0.08 0.04 100.00% (anon) After: % zmodload zsh/zprof % () : % () : % zprof num calls time self name ----------------------------------------------------------------------------------- 1) 1 0.04 0.04 50.45% 0.04 0.04 50.45% (anon) [:3] 2) 1 0.04 0.04 49.55% 0.04 0.04 49.55% (anon) [:2] --- ChangeLog | 6 ++++++ Src/Modules/zprof.c | 31 ++++++++++++++++++++++++++++--- Src/exec.c | 20 ++++++++++++++++++-- 3 files changed, 52 insertions(+), 5 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index d1927a089..57635bea7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2020-05-28 Daniel Shahaf + + * 45923 (with memory leak fixed, cf. 45924): Src/Modules/zprof.c, + Src/exec.c: zprof: Don't tally all anonymous functions as though + they were a single function named "(anon)". + 2020-05-23 Peter Stephenson * 45900: Src/lex.c, Test/D04parameter.ztst: Fix issues with diff --git a/Src/Modules/zprof.c b/Src/Modules/zprof.c index bc97771c0..56cdab888 100644 --- a/Src/Modules/zprof.c +++ b/Src/Modules/zprof.c @@ -213,7 +213,25 @@ bin_zprof(UNUSED(char *nam), UNUSED(char **args), Options ops, UNUSED(int func)) return 0; } -/**/ +static char * +name_for_anonymous_function(char *name) +{ + char lineno[DIGBUFSIZE]; + char *parts[7]; + + convbase(lineno, funcstack[0].flineno, 10); + + parts[0] = name; + parts[1] = " ["; + parts[2] = funcstack[0].filename ? funcstack[0].filename : ""; + parts[3] = ":"; + parts[4] = lineno; + parts[5] = "]"; + parts[6] = NULL; + + return sepjoin(parts, "", 1); +} + static int zprof_wrapper(Eprog prog, FuncWrap w, char *name) { @@ -224,12 +242,19 @@ zprof_wrapper(Eprog prog, FuncWrap w, char *name) struct timeval tv; struct timezone dummy; double prev = 0, now; + char *name_for_lookups; + + if (is_anonymous_function_name(name)) { + name_for_lookups = name_for_anonymous_function(name); + } else { + name_for_lookups = name; + } if (zprof_module && !(zprof_module->node.flags & MOD_UNLOAD)) { active = 1; - if (!(f = findpfunc(name))) { + if (!(f = findpfunc(name_for_lookups))) { f = (Pfunc) zalloc(sizeof(*f)); - f->name = ztrdup(name); + f->name = ztrdup(name_for_lookups); f->calls = 0; f->time = f->self = 0.0; f->next = calls; diff --git a/Src/exec.c b/Src/exec.c index 2b8e2167f..29f4fc5ca 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -5147,10 +5147,26 @@ exectime(Estate state, UNUSED(int do_exec)) return lastval; } -/* Define a shell function */ - +/* The string displayed in lieu of the name of an anonymous function (in PS4, + * zprof output, etc) + */ static const char *const ANONYMOUS_FUNCTION_NAME = "(anon)"; +/* + * Take a function name argument and return true iff it is equal to the string + * used for the names of anonymous functions, "(anon)". + * + * Note that it's possible to define a named function literally called "(anon)" + * (though I doubt anyone would ever do that). + */ +/**/ +int is_anonymous_function_name(const char *name) +{ + return !strcmp(name, ANONYMOUS_FUNCTION_NAME); +} + +/* Define a shell function */ + /**/ static int execfuncdef(Estate state, Eprog redir_prog) -- cgit v1.2.3 From 4d7aa71d8ed64827df4d8efd993abffcbc0fc075 Mon Sep 17 00:00:00 2001 From: Cedric Ware Date: Sat, 11 Jul 2020 00:14:58 -0500 Subject: 46152: zsh/system: Re-allow '0' timeout in zsystem flock --- ChangeLog | 5 +++++ Src/Modules/system.c | 6 +++--- Test/V14system.ztst | 21 ++++++++++----------- 3 files changed, 18 insertions(+), 14 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index d8da32500..a05703cbb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2020-07-11 dana + + * Cedric Ware: 46152: Src/Modules/system.c, Test/V14system.ztst: + Re-allow '0' timeout in zsystem flock + 2020-07-09 Jun-ichi Takimoto * 46215 (w/ minor tweak): Test/E01options.ztst: make the test diff --git a/Src/Modules/system.c b/Src/Modules/system.c index 972aa0767..ecd4e2546 100644 --- a/Src/Modules/system.c +++ b/Src/Modules/system.c @@ -597,7 +597,7 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) * a 32-bit int and CLOCK_MONOTONIC is not supported, in which * case there is a Y2038 problem anyway. */ - if (timeout < 1e-6 || timeout > 1073741823.) { + if (timeout > 1073741823.) { zwarnnam(nam, "flock: invalid timeout value: '%s'", optarg); return 1; @@ -623,7 +623,7 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) timeout_param.type = MN_FLOAT; timeout_param.u.d = (double)timeout_param.u.l; } - timeout_param.u.d *= 1e6; + timeout_param.u.d = ceil(timeout_param.u.d * 1e6); if (timeout_param.u.d < 1 || timeout_param.u.d > 0.999 * LONG_MAX) { zwarnnam(nam, "flock: invalid interval value: '%s'", @@ -704,7 +704,7 @@ bin_zsystem_flock(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) zgettime_monotonic_if_available(&now); end.tv_sec = now.tv_sec; end.tv_nsec = now.tv_nsec; - end.tv_nsec += modf(timeout, &timeout_s) * 1000000000L; + end.tv_nsec += ceil(modf(timeout, &timeout_s) * 1000000000L); end.tv_sec += timeout_s; if (end.tv_nsec >= 1000000000L) { end.tv_nsec -= 1000000000L; diff --git a/Test/V14system.ztst b/Test/V14system.ztst index b8af96cda..100daab08 100644 --- a/Test/V14system.ztst +++ b/Test/V14system.ztst @@ -13,27 +13,26 @@ %test ( - zsystem flock -t 0.1 -i 0.000001 $tst_dir/file + zsystem flock -t 0 -i 0.000001 $tst_dir/file && + zsystem flock -t 0.1 -i 0.000001 $tst_dir/file && + zsystem flock -t 0.1 -i 0.0000001 $tst_dir/file && + zsystem flock -t 1 -i 0.000001 $tst_dir/file ) 0:zsystem flock valid time arguments ( - zsystem flock -t -1 $tst_dir/file || - zsystem flock -t 0.49e-6 $tst_dir/file || zsystem flock -t 1073741824 $tst_dir/file || zsystem flock -t 1e100 $tst_dir/file || zsystem flock -i -1 $tst_dir/file || - zsystem flock -i 0.49e-6 $tst_dir/file || + zsystem flock -i 0 $tst_dir/file || zsystem flock -i 1e100 $tst_dir/file ) 1:zsystem flock invalid time arguments -?(eval):zsystem:2: flock: invalid timeout value: '-1' -?(eval):zsystem:3: flock: invalid timeout value: '0.49e-6' -?(eval):zsystem:4: flock: invalid timeout value: '1073741824' -?(eval):zsystem:5: flock: invalid timeout value: '1e100' -?(eval):zsystem:6: flock: invalid interval value: '-1' -?(eval):zsystem:7: flock: invalid interval value: '0.49e-6' -?(eval):zsystem:8: flock: invalid interval value: '1e100' +?(eval):zsystem:2: flock: invalid timeout value: '1073741824' +?(eval):zsystem:3: flock: invalid timeout value: '1e100' +?(eval):zsystem:4: flock: invalid interval value: '-1' +?(eval):zsystem:5: flock: invalid interval value: '0' +?(eval):zsystem:6: flock: invalid interval value: '1e100' ( # Lock file for 1 second in the background. -- cgit v1.2.3 From c6a85163619ed1cee89ab047a0d98108ed46828d Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Fri, 28 Aug 2020 02:36:35 +0000 Subject: github #64: Fix a build-time error when building against ncurses that hadn't been built with --enable-wgetch-events. The --enable-wgetch-events codepath is experimental (according to ncurses-6.2/INSTALL) and off by default (according to ncurses-6.2/configure.in). With that codepath disabled, the macro KEY_EVENT is not provided, which (before this commit) manifested as a build-time error: [ 245s] gcc -c -I. -I../../Src -I../../Src -I../../Src/Zle -I. -DHAVE_CONFIG_H -DMODULE -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -Werror=return-type -flto=auto -g -fPIE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -I/usr/include/ncursesw -fPIC -o curses..o curses.c [ 246s] In file included from curses.c:210: [ 246s] curses_keys.h:93:15: error: 'KEY_EVENT' undeclared here (not in a function); did you mean 'KEY_RESET'? [ 246s] 93 | {"EVENT", KEY_EVENT}, [ 246s] | ^~~~~~~~~ [ 246s] | KEY_RESET curses_keys.h is only used for setting the "kevent" output parameter of 'zcurses input' (and the associated $zcurses_keycodes special variable), so there's no harm in just leaving KEY_EVENT out of it. (That codepath deals gracefully with numeric values that don't correspond to any of the known compile-time values, as that can happen whenever the build- and run-time versions of ncurses don't provide the same set of KEY_* macros, with or without relation to that configure flag.) Reported by Martin Liska. --- ChangeLog | 6 ++++++ Src/Modules/curses_keys.awk | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 83f305356..8b5e11c63 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2020-08-28 Daniel Shahaf + + * github #64: Src/Modules/curses_keys.awk: Fix a build-time + error when building against ncurses that hadn't been built + with --enable-wgetch-events. (Reported by Martin Liska.) + 2020-08-21 Oliver Kiddle * 47328: Doc/Zsh/metafaq.yo, Etc/CONTRIBUTORS: remove references diff --git a/Src/Modules/curses_keys.awk b/Src/Modules/curses_keys.awk index ffb182c35..25bd63c7e 100644 --- a/Src/Modules/curses_keys.awk +++ b/Src/Modules/curses_keys.awk @@ -12,8 +12,13 @@ BEGIN {nkeydefs = 0} END { printf("static const struct zcurses_namenumberpair keypad_names[] = {\n") - for (i = 0; i < 0 + nkeydefs; i++) + for (i = 0; i < 0 + nkeydefs; i++) { + if (name[i] == "EVENT") + printf("#ifdef KEY_EVENT\n") printf(" {\"%s\", KEY_%s},\n", name[i], name[i]) + if (name[i] == "EVENT") + printf("#endif\n") + } printf(" {NULL, 0}\n") printf("};\n") } -- cgit v1.2.3 From ead29c2a366eb2c5006d7da64963ce5f60deb684 Mon Sep 17 00:00:00 2001 From: Roman Perepelitsa Date: Fri, 23 Oct 2020 11:42:30 +0200 Subject: Fix a race condition in zf_mkdir -p If ~/foo does not exist and `zf_mkdir -p zf_mkdir -p` is executed concurrently in multiple shells, it was possible prior to this patch for the command to fail with EEXIST. --- ChangeLog | 5 +++++ Src/Modules/files.c | 28 +++++++++++++++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index c6569edb7..5650bdd68 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2020-10-18 Roman Perepelitsa + + * 47476: Src/Modules/files.c: Fix a race condition in zf_mkdir -p + (based on the patch by Matthew Martin in workers/47436) + 2020-10-18 Axel Beckert * 47468: Doc/Zsh/contrib.yo: Fix typo diff --git a/Src/Modules/files.c b/Src/Modules/files.c index 6d20e38a8..7ebacba6c 100644 --- a/Src/Modules/files.c +++ b/Src/Modules/files.c @@ -122,19 +122,29 @@ domkdir(char *nam, char *path, mode_t mode, int p) { int err; mode_t oumask; + struct stat st; + int n = 8; char const *rpath = unmeta(path); - if(p) { - struct stat st; - - if(!stat(rpath, &st) && S_ISDIR(st.st_mode)) + while(n-- > 0) { + oumask = umask(0); + err = mkdir(rpath, mode) ? errno : 0; + umask(oumask); + if (!err) + return 0; + if(!p || err != EEXIST) + break; + if(stat(rpath, &st)) { + if(errno == ENOENT) + continue; + err = errno; + break; + } + if(S_ISDIR(st.st_mode)) return 0; + break; } - oumask = umask(0); - err = mkdir(rpath, mode) ? errno : 0; - umask(oumask); - if(!err) - return 0; + zwarnnam(nam, "cannot make directory `%s': %e", path, err); return 1; } -- cgit v1.2.3 From 8773f01f2b7a908af70dc06d7d00481f1c9bec44 Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Sat, 24 Oct 2020 13:45:08 +0200 Subject: 47494, 47495: Add -n option to strftime --- ChangeLog | 4 ++++ Completion/Zsh/Command/_strftime | 12 +++++++----- Doc/Zsh/mod_datetime.yo | 7 +++++-- Src/Modules/datetime.c | 5 +++-- Test/V09datetime.ztst | 4 ++++ 5 files changed, 23 insertions(+), 9 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 5a9ef3213..9db67eea0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,10 @@ * 47302: Test/B03print.ztst, Test/D07multibyte.ztst: Test for print -v fix + * 47494, 47495: Completion/Zsh/Command/_strftime, + Doc/Zsh/mod_datetime.yo, Src/Modules/datetime.c, + Test/V09datetime.ztst: Add -n option to strftime + 2020-10-18 Roman Perepelitsa * 47476: Src/Modules/files.c: Fix a race condition in zf_mkdir -p diff --git a/Completion/Zsh/Command/_strftime b/Completion/Zsh/Command/_strftime index 0849f1c85..a57a76ce4 100644 --- a/Completion/Zsh/Command/_strftime +++ b/Completion/Zsh/Command/_strftime @@ -1,14 +1,16 @@ #compdef strftime -local ret=1 expl +local expl two='epoch time' + +if (( words[(I)-r] )); then + two='date string' +fi _arguments -S -A '-*' -s \ + '-n[omit trailing newline]' \ '-q[run quietly]' \ '(3)-r[reverse lookup using strptime]' \ '-s+[assign result to parameter]:param:_parameters' \ '1:format: _date_formats zsh' \ - '2:epoch time (or date string with -r)' \ + "2:$two" \ '3:nanoseconds' \ -&& ret=0 - -return ret diff --git a/Doc/Zsh/mod_datetime.yo b/Doc/Zsh/mod_datetime.yo index da65a9bbd..853656128 100644 --- a/Doc/Zsh/mod_datetime.yo +++ b/Doc/Zsh/mod_datetime.yo @@ -6,8 +6,8 @@ The tt(zsh/datetime) module makes available one builtin command: startitem() findex(strftime) cindex(date string, printing) -xitem(tt(strftime) [ tt(-s) var(scalar) ] var(format) [ var(epochtime) [ var(nanoseconds) ] ] ) -item(tt(strftime) tt(-r) [ tt(-q) ] [ tt(-s) var(scalar) ] var(format) var(timestring) )( +xitem(tt(strftime) [ tt(-s) var(scalar) | tt(-n) ] var(format) [ var(epochtime) [ var(nanoseconds) ] ] ) +item(tt(strftime) tt(-r) [ tt(-q) ] [ tt(-s) var(scalar) | tt(-n) ] var(format) var(timestring) )( Output the date in the var(format) specified. With no var(epochtime), the current system date/time is used; optionally, var(epochtime) may be used to specify the number of seconds since the epoch, and var(nanoseconds) may @@ -18,6 +18,9 @@ ifzman(the section EXPANSION OF PROMPT SEQUENCES in zmanref(zshmisc))\ ifnzman(noderef(Prompt Expansion)) are also available. startitem() +item(tt(-n))( +Suppress printing a newline after the formatted string. +) item(tt(-q))( Run quietly; suppress printing of all error messages described below. Errors for invalid var(epochtime) values are always printed. diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c index 521c15a5b..085e4cc26 100644 --- a/Src/Modules/datetime.c +++ b/Src/Modules/datetime.c @@ -175,7 +175,8 @@ output_strftime(char *nam, char **argv, Options ops, UNUSED(int func)) setsparam(scalar, metafy(buffer, len, META_DUP)); } else { fwrite(buffer, 1, len, stdout); - putchar('\n'); + if (!OPT_ISSET(ops,'n')) + putchar('\n'); } zfree(buffer, bufsize); @@ -235,7 +236,7 @@ getcurrenttime(UNUSED(Param pm)) } static struct builtin bintab[] = { - BUILTIN("strftime", 0, bin_strftime, 1, 3, 0, "qrs:", NULL), + BUILTIN("strftime", 0, bin_strftime, 1, 3, 0, "nqrs:", NULL), }; static const struct gsu_integer epochseconds_gsu = diff --git a/Test/V09datetime.ztst b/Test/V09datetime.ztst index 9f67ecec3..622bdf6ed 100644 --- a/Test/V09datetime.ztst +++ b/Test/V09datetime.ztst @@ -130,3 +130,7 @@ >%6. 2002-02-02 02:02:02.999999 >%9. 2002-02-02 02:02:02.999999999 >%12. 2002-02-02 02:02:02.999999999 + + strftime -n 'one line%n' 2> /dev/null +0:-n option +>one line -- cgit v1.2.3 From 3499a676fa556639545836dbc4855736f61c8789 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Wed, 2 Dec 2020 20:14:45 -0800 Subject: 47704: fix scope for "private -p" --- ChangeLog | 4 ++++ Src/Modules/param_private.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index f3aabff42..3b9baa39f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2020-12-02 Bart Schaefer + + * 47704: Src/Modules/param_private.c: fix scope for "private -p" + 2020-12-01 Bart Schaefer * unposted: Etc/BUGS: Add users/26150 (multios + exec) diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index 86416c5c5..24545819d 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -171,6 +171,7 @@ bin_private(char *nam, char **args, LinkList assigns, Options ops, int func) { int from_typeset = 1; int ofake = fakelevel; /* paranoia in case of recursive call */ + int hasargs = *args != NULL || (assigns && firstnode(assigns)); makeprivate_error = 0; if (!OPT_ISSET(ops, 'P')) { @@ -190,6 +191,9 @@ bin_private(char *nam, char **args, LinkList assigns, Options ops, int func) } ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */ + if (OPT_ISSET(ops, 'p') || (!hasargs && OPT_ISSET(ops, '+'))) { + return bin_typeset("private", args, assigns, ops, func); + } queue_signals(); fakelevel = locallevel; -- cgit v1.2.3 From bf8ca5f019fa9fd585a7480a3d4198719369df92 Mon Sep 17 00:00:00 2001 From: Peiyuan Song Date: Wed, 20 Jan 2021 13:33:52 +0900 Subject: 47840: make zpty module work on Cygwin --- ChangeLog | 5 +++++ Src/Modules/zpty.c | 11 +++++++++++ configure.ac | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 88c205103..eb99fcd5b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2021-01-20 Jun-ichi Takimoto + + * Peiyuan Song: 47840: Src/Modules/zpty.c, configure.ac: make + zpty module work on Cygwin + 2020-12-12 dana * unposted: NEWS: Catch up on new features diff --git a/Src/Modules/zpty.c b/Src/Modules/zpty.c index 45fd15ee0..dfd2a2a7a 100644 --- a/Src/Modules/zpty.c +++ b/Src/Modules/zpty.c @@ -30,6 +30,13 @@ #include "zpty.mdh" #include "zpty.pro" +#ifdef __CYGWIN__ +#include +#if defined(CYGWIN_VERSION_DLL_MAJOR) && CYGWIN_VERSION_DLL_MAJOR<3002 +#define USE_CYGWIN_FIX 1 +#endif +#endif + /* The number of bytes we normally read when given no pattern and the * upper bound on the number of bytes we read (even if we are give a * pattern). */ @@ -428,6 +435,7 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) mypid = 0; /* trick to ensure we _exit() */ zexit(lastval, ZEXIT_NORMAL); } +#ifndef USE_CYGWIN_FIX master = movefd(master); if (master == -1) { zerrnam(nam, "cannot duplicate fd %d: %e", master, errno); @@ -435,6 +443,9 @@ newptycmd(char *nam, char *pname, char **args, int echo, int nblock) ineval = oineval; return 1; } +#else + addmodulefd(master, FDT_INTERNAL); +#endif p = (Ptycmd) zalloc(sizeof(*p)); diff --git a/configure.ac b/configure.ac index 549cae3d6..16dafac05 100644 --- a/configure.ac +++ b/configure.ac @@ -2460,7 +2460,7 @@ if test x$ac_cv_have_dev_ptmx = xyes -o x$ac_cv_func_posix_openpt = xyes && \ test x$ac_cv_func_ptsname = xyes; then AC_CACHE_CHECK([if /dev/ptmx is usable], ac_cv_use_dev_ptmx, - [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#ifdef __linux + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#if defined(__linux) || defined(__CYGWIN__) #define _GNU_SOURCE 1 #endif #include -- cgit v1.2.3 From 9120d1e841b0813f1c71d55f77c3d18fc8318187 Mon Sep 17 00:00:00 2001 From: Joshua Krusell Date: Wed, 3 Feb 2021 11:33:47 +0000 Subject: 47899: Improve error message from zparseopts. --- ChangeLog | 5 +++++ Src/Modules/zutil.c | 5 ++++- Test/V12zparseopts.ztst | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 52ebf9513..d5725d379 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2021-02-03 Joshua Krusell + + * 47899: Src/Modules/zutil.c, Test/V12zparseopts.ztst: Improved + error message from zparseopts. + 2021-01-20 Jun-ichi Takimoto * 47883: Completion/Unix/Command/_awk: support gawk ver.5 diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 5c96d06c1..c8017d0c0 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -1873,7 +1873,10 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) while (*++o) { if (!(d = sopts[STOUC(*o)])) { if (fail) { - zwarnnam(nam, "bad option: %c", *o); + if (*o != '-') + zwarnnam(nam, "bad option: %c", *o); + else + zwarnnam(nam, "bad option: %s", o); return 1; } o = NULL; diff --git a/Test/V12zparseopts.ztst b/Test/V12zparseopts.ztst index d7fc33f72..c41c49022 100644 --- a/Test/V12zparseopts.ztst +++ b/Test/V12zparseopts.ztst @@ -69,7 +69,7 @@ >ret: 1, optv: , argv: -a -x -z ?(anon):zparseopts:2: bad option: x >ret: 1, optv: , argv: -ax -z -?(anon):zparseopts:2: bad option: - +?(anon):zparseopts:2: bad option: -x >ret: 1, optv: , argv: -a --x -z for 1 in '-a 1 2 3' '1 2 3'; do -- cgit v1.2.3 From 3d6e5b6231f0d80873c6f83924a48df223121e72 Mon Sep 17 00:00:00 2001 From: Joshua Krusell Date: Wed, 3 Feb 2021 16:46:59 +0100 Subject: 47905: Add leading '-' to zparseopts option parsing errors --- ChangeLog | 6 ++++++ Src/Modules/zutil.c | 8 ++++---- Test/V12zparseopts.ztst | 8 ++++---- 3 files changed, 14 insertions(+), 8 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 4946b84c3..20f7eccf3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2021-02-13 Oliver Kiddle + + * 47905: Joshua Krusell: Src/Modules/zutil.c, + Test/V12zparseopts.ztst: Add leading '-' to zparseopts option + parsing errors + 2021-02-11 Bart Schaefer * unposted: NEWS, README: mention the effects of 47997. diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index c8017d0c0..cecea6d51 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -1874,9 +1874,9 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) if (!(d = sopts[STOUC(*o)])) { if (fail) { if (*o != '-') - zwarnnam(nam, "bad option: %c", *o); + zwarnnam(nam, "bad option: -%c", *o); else - zwarnnam(nam, "bad option: %s", o); + zwarnnam(nam, "bad option: -%s", o); return 1; } o = NULL; @@ -1889,7 +1889,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } else if (!(d->flags & ZOF_OPT) || (pp[1] && pp[1][0] != '-')) { if (!pp[1]) { - zwarnnam(nam, "missing argument for option: %s", + zwarnnam(nam, "missing argument for option: -%s", d->name); return 1; } @@ -1916,7 +1916,7 @@ bin_zparseopts(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) else if (!(d->flags & ZOF_OPT) || (pp[1] && pp[1][0] != '-')) { if (!pp[1]) { - zwarnnam(nam, "missing argument for option: %s", + zwarnnam(nam, "missing argument for option: -%s", d->name); return 1; } diff --git a/Test/V12zparseopts.ztst b/Test/V12zparseopts.ztst index c41c49022..816e1d041 100644 --- a/Test/V12zparseopts.ztst +++ b/Test/V12zparseopts.ztst @@ -65,11 +65,11 @@ } $=1 done 0:zparseopts -F -?(anon):zparseopts:2: bad option: x +?(anon):zparseopts:2: bad option: -x >ret: 1, optv: , argv: -a -x -z -?(anon):zparseopts:2: bad option: x ->ret: 1, optv: , argv: -ax -z ?(anon):zparseopts:2: bad option: -x +>ret: 1, optv: , argv: -ax -z +?(anon):zparseopts:2: bad option: --x >ret: 1, optv: , argv: -a --x -z for 1 in '-a 1 2 3' '1 2 3'; do @@ -168,5 +168,5 @@ print -r - ret: $?, optv: $optv, argv: $argv } -ab1 -c 0:missing optarg -?(anon):zparseopts:2: missing argument for option: c +?(anon):zparseopts:2: missing argument for option: -c >ret: 1, optv: , argv: -ab1 -c -- cgit v1.2.3 From df48cc8404f3fc2976c9d70c9aeefda171432cf9 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Wed, 30 Dec 2020 23:41:30 -0600 Subject: 47785: remove deprecated autoconf functions STDC_HEADERS and TIME_WITH_SYS_TIME are deprecated. --- ChangeLog | 5 +++++ Src/Modules/files.c | 6 ------ Src/mem.c | 16 ++++------------ Src/zsh_system.h | 21 ++------------------- configure.ac | 4 +--- 5 files changed, 12 insertions(+), 40 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 18c576b5c..b3cd5b8de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2021-04-09 Oliver Kiddle + + * Felipe Contreras: 47785: Src/Modules/files.c, Src/zsh_system.h, + Src/mem.c, configure.ac: remove deprecated autoconf functions + 2021-04-08 Jun-ichi Takimoto * 48416: Completion/Unix/Command/_gcore: support macOS, with diff --git a/Src/Modules/files.c b/Src/Modules/files.c index 7ebacba6c..a1d6f6bf2 100644 --- a/Src/Modules/files.c +++ b/Src/Modules/files.c @@ -32,12 +32,6 @@ typedef int (*MoveFunc) _((char const *, char const *)); typedef int (*RecurseFunc) _((char *, char *, struct stat const *, void *)); -#ifndef STDC_HEADERS -extern int link _((const char *, const char *)); -extern int symlink _((const char *, const char *)); -extern int rename _((const char *, const char *)); -#endif - struct recursivecmd; #include "files.pro" diff --git a/Src/mem.c b/Src/mem.c index 5951e57ed..25b2bbce7 100644 --- a/Src/mem.c +++ b/Src/mem.c @@ -1072,18 +1072,10 @@ zrealloc(void *ptr, size_t size) # endif #endif -#if defined(_BSD) && !defined(STDC_HEADERS) -# define FREE_RET_T int -# define FREE_ARG_T char * -# define FREE_DO_RET -# define MALLOC_RET_T char * -# define MALLOC_ARG_T size_t -#else -# define FREE_RET_T void -# define FREE_ARG_T void * -# define MALLOC_RET_T void * -# define MALLOC_ARG_T size_t -#endif +#define FREE_RET_T void +#define FREE_ARG_T void * +#define MALLOC_RET_T void * +#define MALLOC_ARG_T size_t /* structure for building free list in blocks holding small blocks */ diff --git a/Src/zsh_system.h b/Src/zsh_system.h index 161b073b4..6f4efce96 100644 --- a/Src/zsh_system.h +++ b/Src/zsh_system.h @@ -235,16 +235,8 @@ char *alloca _((size_t)); # include #endif -#ifdef TIME_WITH_SYS_TIME -# include -# include -#else -# ifdef HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif +#include +#include /* This is needed by some old SCO unices */ #if !defined(HAVE_STRUCT_TIMEZONE) && !defined(ZSH_OOT_MODULE) @@ -279,16 +271,7 @@ struct timespec { # include #endif -#if STDC_HEADERS || HAVE_STRING_H # include -/* An ANSI string.h and pre-ANSI memory.h might conflict. */ -# if !STDC_HEADERS && HAVE_MEMORY_H -# include -# endif /* not STDC_HEADERS and HAVE_MEMORY_H */ -#else /* not STDC_HEADERS and not HAVE_STRING_H */ -# include -/* memory.h and strings.h conflict on some systems. */ -#endif /* not STDC_HEADERS and not HAVE_STRING_H */ #ifdef HAVE_LOCALE_H # include diff --git a/configure.ac b/configure.ac index 16dafac05..e2ddf0e55 100644 --- a/configure.ac +++ b/configure.ac @@ -28,7 +28,7 @@ dnl AC_INIT AC_CONFIG_SRCDIR([Src/zsh.h]) AC_PREREQ([2.69]) -AC_CONFIG_HEADER(config.h) +AC_CONFIG_HEADERS([config.h]) dnl What version of zsh are we building ? . ${srcdir}/Config/version.mk @@ -657,8 +657,6 @@ dnl ------------------ dnl CHECK HEADER FILES dnl ------------------ AC_HEADER_DIRENT -AC_HEADER_STDC -AC_HEADER_TIME AC_HEADER_STAT AC_HEADER_SYS_WAIT -- cgit v1.2.3 From 283d2f3c2761ac549a647638bb7d8fd8de3dabb4 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 9 Apr 2021 21:01:37 +0100 Subject: 48432 and enable test: fix quotiing of zstyle -L for zstyle -e --- ChangeLog | 6 ++++++ Src/Modules/zutil.c | 3 ++- Test/V05styles.ztst | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index cb4ad6833..f894cc2fe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2021-04-09 Peter Stephenson + + * 48432 (plus test change): Src/Modules/zutil.c, + Test/V05styles.ztst: Fix quoting of zstyle -L output for + -e styles, and enable test. + 2021-04-09 Oliver Kiddle * 48378: Completion/Zsh/Command/_compadd: complete compadd diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index cecea6d51..691ba6c2f 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -200,7 +200,8 @@ printstylenode(HashNode hn, int printflags) else { printf("zstyle %s", (p->eval ? "-e " : "")); quotedzputs(p->pat, stdout); - printf(" %s", s->node.nam); + putchar(' '); + quotedzputs(s->node.nam, stdout); } for (v = p->vals; *v; v++) { putchar(' '); diff --git a/Test/V05styles.ztst b/Test/V05styles.ztst index e4bdfece3..61d2cdb0a 100644 --- a/Test/V05styles.ztst +++ b/Test/V05styles.ztst @@ -171,5 +171,5 @@ a=( ${(M)a:#*con*text*ke*y*val*u*e} ) print -r -- "$a" ) --f:zstyle -L escapes the key (regression: workers/48424) +0:zstyle -L escapes the key (regression: workers/48424) >zstyle $'con\C-@text' $'ke\C-@y' $'val\C-@u' e -- cgit v1.2.3 From 4a9437317fd374e983934d5b18c7d1d6ee041645 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 24 Aug 2021 19:21:53 +0100 Subject: 49297 (quoting amended): error message in files module. If ENONENT it could be the other argument that doesn't exist, so check. --- ChangeLog | 5 +++++ Src/Modules/files.c | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 3d1a97e41..428998717 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2021-08-24 Peter Stephenson + + * 49297 with quoting updated: Src/Modules/files.c: check + which files is in error when ENOENT on link etc. + 2021-08-24 dana * github #78: DCsunset: Completion/Unix/Command/_pandoc: Fix diff --git a/Src/Modules/files.c b/Src/Modules/files.c index a1d6f6bf2..d991f69d7 100644 --- a/Src/Modules/files.c +++ b/Src/Modules/files.c @@ -346,7 +346,13 @@ domove(char *nam, MoveFunc movefn, char *p, char *q, int flags) unlink(qbuf); } if(movefn(pbuf, qbuf)) { - zwarnnam(nam, "%s: %e", p, errno); + int ferrno = errno; + char *errfile = p; + if (ferrno == ENOENT && !lstat(pbuf, &st)) { + /* p *does* exist, so error is in q */ + errfile = q; + } + zwarnnam(nam, "`%s': %e", errfile, ferrno); zsfree(pbuf); return 1; } -- cgit v1.2.3 From 7000b04e766376890e597ffe7d6cf26c444fce53 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 6 Sep 2021 13:27:01 -0700 Subject: 49196: gdbm keys not present in the database appear unset in tied hashes --- ChangeLog | 3 +++ Src/Modules/db_gdbm.c | 2 ++ 2 files changed, 5 insertions(+) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 58822495e..31beb268e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2021-09-06 Bart Schaefer + * 49196: Src/Modules/db_gdbm.c: gdbm keys not present in the + database appear unset in tied hashes + * Marlon Richert: 48969: fix for "zle -N" completion * 48888: Doc/Zsh/mod_system.yo, Doc/Zsh/params.yo, Test/E03posix.ztst: diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c index b8e7c76c6..84fdfa905 100644 --- a/Src/Modules/db_gdbm.c +++ b/Src/Modules/db_gdbm.c @@ -332,6 +332,8 @@ gdbmgetfn(Param pm) /* Can return pointer, correctly saved inside hash */ return pm->u.str; + } else { + pm->node.flags |= PM_DEFAULTED; } /* Free key */ -- cgit v1.2.3 From 2b81d4be3232fced220be1cf9c80c087d91d88d6 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Wed, 8 Sep 2021 11:58:29 +0900 Subject: unposted: add/remove UNUSED() for some function parameters --- ChangeLog | 4 ++++ Src/Modules/db_gdbm.c | 2 +- Src/Modules/files.c | 2 +- Src/Modules/nearcolor.c | 2 +- Src/Zle/complete.c | 2 +- Src/builtin.c | 2 +- 6 files changed, 9 insertions(+), 5 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index fe9a3b4ee..6f4646287 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2021-09-08 Jun-ichi Takimoto + * unposted: Src/Modules/db_gdbm.c, Src/Modules/files.c, + Src/Modules/nearcolor.c, Src/Zle/complete.c, Src/builtin.c: + add/remove UNUSED() for some funtion parameters + * unposted: Src/input.c: add 'static' to shinsavestack * 49377: Src/Zle/zle_keymap.c, Test/X03zlebindkey.ztst: fix diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c index 84fdfa905..7e11ec939 100644 --- a/Src/Modules/db_gdbm.c +++ b/Src/Modules/db_gdbm.c @@ -233,7 +233,7 @@ bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func)) /**/ static int -bin_zgdbmpath(char *nam, char **args, Options ops, UNUSED(int func)) +bin_zgdbmpath(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { Param pm; char *pmname; diff --git a/Src/Modules/files.c b/Src/Modules/files.c index d991f69d7..bf0e8f8a8 100644 --- a/Src/Modules/files.c +++ b/Src/Modules/files.c @@ -652,7 +652,7 @@ chmod_dochmod(char *arg, char *rp, UNUSED(struct stat const *sp), void *magic) /**/ static int -bin_chmod(char *nam, char **args, Options ops, int func) +bin_chmod(char *nam, char **args, Options ops, UNUSED(int func)) { struct chmodmagic chm; char *str = args[0], *ptr; diff --git a/Src/Modules/nearcolor.c b/Src/Modules/nearcolor.c index b49ee9afb..d50a6bb44 100644 --- a/Src/Modules/nearcolor.c +++ b/Src/Modules/nearcolor.c @@ -188,7 +188,7 @@ enables_(Module m, int **enables) /**/ int -boot_(Module m) +boot_(UNUSED(Module m)) { addhookfunc("get_color_attr", (Hookfn) getnearestcolor); return 0; diff --git a/Src/Zle/complete.c b/Src/Zle/complete.c index 7beb6d847..71d114de9 100644 --- a/Src/Zle/complete.c +++ b/Src/Zle/complete.c @@ -1343,7 +1343,7 @@ get_compstate(Param pm) /**/ static void -set_compstate(UNUSED(Param pm), HashTable ht) +set_compstate(Param pm, HashTable ht) { struct compparam *cp; Param *pp; diff --git a/Src/builtin.c b/Src/builtin.c index d7d2ea297..89bcd98db 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -2024,7 +2024,7 @@ typeset_setwidth(const char * name, Param pm, Options ops, int on, int always) /**/ static Param -typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), +typeset_single(char *cname, char *pname, Param pm, int func, int on, int off, int roff, Asgment asg, Param altpm, Options ops, int joinchar) { -- cgit v1.2.3 From dd51ffa5b4b9759af2667df8e4505f117b8e2b23 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Wed, 22 Sep 2021 13:36:57 +0900 Subject: 49422: improve support of --disable-dynamic-nss see also 49392 (Vincent) and 49412 (Axel) --- ChangeLog | 5 +++++ Src/Modules/parameter.c | 12 ++++++++++++ Src/hashnameddir.c | 6 +++--- Src/options.c | 2 +- Src/params.c | 13 ++++++++----- Src/utils.c | 10 +++++----- 6 files changed, 34 insertions(+), 14 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 878299d12..1331c1d10 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2021-09-22 Jun-ichi Takimoto + + * 49422: Src/Modules/parameter.c, Src/hashnameddir.c, Src/options.c, + Src/params.c, Src/utils.c: improve support of --disable-dynamic-nss + 2021-09-09 Peter Stephenson * 49353: Src/exe.c, Test/A01grammar.ztst: In sourced file, diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index ef9148d7b..b44555323 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -2011,6 +2011,9 @@ scanpmdissaliases(HashTable ht, ScanFunc func, int flags) /**/ static Groupset get_all_groups(void) { +#ifdef DISABLE_DYNAMIC_NSS + return NULL; +#else Groupset gs = zhalloc(sizeof(*gs)); Groupmap gaptr; gid_t *list, *lptr, egid; @@ -2063,6 +2066,7 @@ static Groupset get_all_groups(void) } return gs; +#endif /* DISABLE_DYNAMIC_NSS */ } /* Standard hash element lookup. */ @@ -2081,7 +2085,11 @@ getpmusergroups(UNUSED(HashTable ht), const char *name) pm->gsu.s = &nullsetscalar_gsu; if (!gs) { +#ifdef DISABLE_DYNAMIC_NSS + zerr("parameter 'usergroups' not available: NSS is disabled"); +#else zerr("failed to retrieve groups for user: %e", errno); +#endif pm->u.str = dupstring(""); pm->node.flags |= (PM_UNSET|PM_SPECIAL); return &pm->node; @@ -2113,7 +2121,11 @@ scanpmusergroups(UNUSED(HashTable ht), ScanFunc func, int flags) Groupmap gaptr; if (!gs) { +#ifdef DISABLE_DYNAMIC_NSS + zerr("parameter 'usergroups' not available: NSS is disabled"); +#else zerr("failed to retrieve groups for user: %e", errno); +#endif return; } diff --git a/Src/hashnameddir.c b/Src/hashnameddir.c index bed43d025..cbd1344ef 100644 --- a/Src/hashnameddir.c +++ b/Src/hashnameddir.c @@ -178,7 +178,7 @@ fillnameddirtable(UNUSED(HashTable ht)) /* Using NIS or NIS+ didn't add any user directories. This seems * fishy, so we fall back to using getpwent(). If we don't have * that, we only use the passwd file. */ -#ifdef HAVE_GETPWENT +#ifdef USE_GETPWENT struct passwd *pw; setpwent(); @@ -190,7 +190,7 @@ fillnameddirtable(UNUSED(HashTable ht)) endpwent(); usepwf = 0; -#endif /* HAVE_GETPWENT */ +#endif /* USE_GETPWENT */ } if (usepwf) { /* Don't forget the non-NIS matches from the flat passwd file */ @@ -229,7 +229,7 @@ fillnameddirtable(UNUSED(HashTable ht)) adduserdir(pw->pw_name, pw->pw_dir, ND_USERNAME, 1); endpwent(); -#endif /* HAVE_GETPWENT */ +#endif /* USE_GETPWENT */ #endif allusersadded = 1; } diff --git a/Src/options.c b/Src/options.c index 783022591..a1fe918fc 100644 --- a/Src/options.c +++ b/Src/options.c @@ -811,7 +811,7 @@ dosetopt(int optno, int value, int force, char *new_opts) return -1; } -# ifdef HAVE_INITGROUPS +# ifdef USE_INITGROUPS /* Set the supplementary groups list. * * Note that on macOS, FreeBSD, and possibly some other platforms, diff --git a/Src/params.c b/Src/params.c index 4f6b361f9..704aad588 100644 --- a/Src/params.c +++ b/Src/params.c @@ -843,9 +843,12 @@ createparamtable(void) setsparam("HOST", ztrdup_metafy(hostnam)); zfree(hostnam, 256); - setsparam("LOGNAME", - ztrdup_metafy((str = getlogin()) && *str ? - str : cached_username)); + setsparam("LOGNAME", ztrdup_metafy( +#ifndef DISABLE_DYNAMIC_NSS + (str = getlogin()) && *str ? str : +#endif + cached_username + )); #if !defined(HAVE_PUTENV) && !defined(USE_SET_UNSET_ENV) /* Copy the environment variables we are inheriting to dynamic * @@ -4430,7 +4433,7 @@ usernamegetfn(UNUSED(Param pm)) void usernamesetfn(UNUSED(Param pm), char *x) { -#if defined(HAVE_SETUID) && defined(HAVE_GETPWNAM) +#if defined(HAVE_SETUID) && defined(USE_GETPWNAM) struct passwd *pswd; if (x && (pswd = getpwnam(x)) && (pswd->pw_uid != cached_uid)) { @@ -4447,7 +4450,7 @@ usernamesetfn(UNUSED(Param pm), char *x) cached_uid = pswd->pw_uid; } } -#endif /* HAVE_SETUID && HAVE_GETPWNAM */ +#endif /* HAVE_SETUID && USE_GETPWNAM */ zsfree(x); } diff --git a/Src/utils.c b/Src/utils.c index c32741ca7..a74c8bd2c 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1119,7 +1119,7 @@ char *cached_username; char * get_username(void) { -#ifdef HAVE_GETPWUID +#ifdef USE_GETPWUID struct passwd *pswd; uid_t current_uid; @@ -1132,9 +1132,9 @@ get_username(void) else cached_username = ztrdup(""); } -#else /* !HAVE_GETPWUID */ +#else /* !USE_GETPWUID */ cached_uid = getuid(); -#endif /* !HAVE_GETPWUID */ +#endif /* !USE_GETPWUID */ return cached_username; } @@ -1310,7 +1310,7 @@ getnameddir(char *name) return str; } -#ifdef HAVE_GETPWNAM +#ifdef USE_GETPWNAM { /* Retrieve an entry from the password table/database for this user. */ struct passwd *pw; @@ -1326,7 +1326,7 @@ getnameddir(char *name) return dupstring(pw->pw_dir); } } -#endif /* HAVE_GETPWNAM */ +#endif /* USE_GETPWNAM */ /* There are no more possible sources of directory names, so give up. */ return NULL; -- cgit v1.2.3 From fa4c88ca25f587f52074698d4ff7eb263de07930 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 4 Oct 2021 09:02:27 -0700 Subject: 49456: clean up detection of private params in nested scopes, update doc --- ChangeLog | 6 ++++++ Doc/Zsh/mod_private.yo | 9 +++++++-- Doc/Zsh/params.yo | 8 ++++++++ Src/Modules/param_private.c | 12 +++++++----- Src/params.c | 5 +++++ Test/V10private.ztst | 6 +++--- 6 files changed, 36 insertions(+), 10 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index b116ec4f7..62470ba0b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2021-10-04 Bart Schaefer + + * 49456: Doc/Zsh/mod_private.yo, Doc/Zsh/params.yo, + Src/Modules/param_private.c, Src/params.c, Test/V10private.ztst: + clean up detection of private params in nested scopes, update doc + 2021-09-28 Oliver Kiddle * ivan tkachenko: 49440: Doc/Zsh/expn.yo: Make double-flag diff --git a/Doc/Zsh/mod_private.yo b/Doc/Zsh/mod_private.yo index 78aee0acf..184fa2be8 100644 --- a/Doc/Zsh/mod_private.yo +++ b/Doc/Zsh/mod_private.yo @@ -10,12 +10,16 @@ ifnzman() startitem() findex(private) cindex(private parameter, creating) -item(tt(private) [ {tt(PLUS())|tt(-)}tt(AHUahlprtux) ] \ +item(tt(private) [ {tt(PLUS())|tt(-)}tt(AHUahlmrtux) ] \ [ {tt(PLUS())|tt(-)}tt(EFLRZi) [ var(n) ] ] [ var(name)[tt(=)var(value)] ... ])( 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)'. + 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 warning about this is printed if tt(WARN_CREATE_GLOBAL) is set @@ -75,7 +79,8 @@ itemiz(Within any other function called by the declaring function, the private parameter does em(NOT) hide other parameters of the same name, so for example a global parameter of the same name is visible and may be assigned or unset. This includes calls to anonymous functions, although -that may also change in the future.) +that may also change in the future. However, the private name may not be +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.) diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index b514eb072..a88e44d4f 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -632,6 +632,14 @@ In the parameter lists that follow, the mark `' indicates that the parameter is special. `' indicates that the parameter does not exist when the shell initializes in tt(sh) or tt(ksh) emulation mode. +The parameters `tt(!)', `tt(#)', `tt(*)', `tt(-)', `tt(?)', `tt(@)', +`tt($)', `tt(ARGC)', `tt(HISTCMD)', `tt(LINENO)', `tt(PPID)', +`tt(status)', `tt(TTYIDLE)', `tt(zsh_eval_context)', +`tt(ZSH_EVAL_CONTEXT)', and `tt(ZSH_SUBSHELL)' are read-only and thus +cannot be restored by the user, so they are not output by +`tt(typeset -p)'. This also applies to many read-only parameters loaded +from modules. + The following parameters are automatically set by the shell: startitem() diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c index 24545819d..c53839152 100644 --- a/Src/Modules/param_private.c +++ b/Src/Modules/param_private.c @@ -125,7 +125,7 @@ makeprivate(HashNode hn, UNUSED(int flags)) break; } /* PM_HIDE so new parameters in deeper scopes do not shadow */ - pm->node.flags |= (PM_HIDE|PM_SPECIAL|PM_REMOVABLE); + pm->node.flags |= (PM_HIDE|PM_SPECIAL|PM_REMOVABLE|PM_RO_BY_DESIGN); pm->level -= 1; } } @@ -171,7 +171,7 @@ bin_private(char *nam, char **args, LinkList assigns, Options ops, int func) { int from_typeset = 1; int ofake = fakelevel; /* paranoia in case of recursive call */ - int hasargs = *args != NULL || (assigns && firstnode(assigns)); + int hasargs = /* *args != NULL || */ (assigns && firstnode(assigns)); makeprivate_error = 0; if (!OPT_ISSET(ops, 'P')) { @@ -190,8 +190,10 @@ bin_private(char *nam, char **args, LinkList assigns, Options ops, int func) return bin_typeset("private", args, assigns, ops, func); } - ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */ - if (OPT_ISSET(ops, 'p') || (!hasargs && OPT_ISSET(ops, '+'))) { + if (!(OPT_ISSET(ops, 'm') || OPT_ISSET(ops, '+'))) + ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */ + if (OPT_ISSET(ops, 'p') || OPT_ISSET(ops, 'm') || + (!hasargs && OPT_ISSET(ops, '+'))) { return bin_typeset("private", args, assigns, ops, func); } @@ -559,7 +561,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:%lprtux", "P") + BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lmprtux", "P") }; static struct features module_features = { diff --git a/Src/params.c b/Src/params.c index 704aad588..b703a97ce 100644 --- a/Src/params.c +++ b/Src/params.c @@ -1013,6 +1013,11 @@ createparam(char *name, int flags) (oldpm->node.flags & PM_SPECIAL) || /* 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", + name); + return NULL; + } oldpm->node.flags &= ~PM_UNSET; if ((oldpm->node.flags & PM_SPECIAL) && oldpm->ename) { Param altpm = diff --git a/Test/V10private.ztst b/Test/V10private.ztst index 03e8259d5..56ffbc5b4 100644 --- a/Test/V10private.ztst +++ b/Test/V10private.ztst @@ -116,14 +116,14 @@ } outer () { local -PA hash_test=(in function) - typeset -p hash_test + private + hash_test inner } outer print ${(kv)hash_test} 0:private hides value from surrounding scope in nested scope >typeset -a hash_test=( top level ) ->typeset -A hash_test=( [in]=function ) +>hash_test=( [in]=function ) >typeset -g -a hash_test=( top level ) >array-local top level >top level @@ -246,7 +246,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: attempt to assign private in nested scope +?(anon):4: array_test: can't change parameter attribute F:future revision will create a global with this assignment typeset -a array_test -- cgit v1.2.3 From 271cfc685b17938e67a8212f2df78c32989411d7 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Tue, 2 Nov 2021 21:39:52 +0100 Subject: 49534, 49539: separate watch/log functionality out into a module --- ChangeLog | 6 + Doc/Makefile.in | 1 + Doc/Zsh/builtins.yo | 8 - Doc/Zsh/compat.yo | 3 +- Doc/Zsh/mod_watch.yo | 140 ++++++++++ Doc/Zsh/params.yo | 118 --------- Src/Modules/watch.c | 716 ++++++++++++++++++++++++++++++++++++++++++++++++++ Src/Modules/watch.mdd | 7 + Src/builtin.c | 1 - Src/init.c | 1 - Src/params.c | 16 +- Src/utils.c | 16 -- Src/watch.c | 626 ------------------------------------------- Src/zsh.mdd | 2 +- 14 files changed, 878 insertions(+), 783 deletions(-) create mode 100644 Doc/Zsh/mod_watch.yo create mode 100644 Src/Modules/watch.c create mode 100644 Src/Modules/watch.mdd delete mode 100644 Src/watch.c (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 5179555ca..00c0d5be6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2021-11-02 Oliver Kiddle + * 49534, 49539: Doc/Makefile.in, Doc/Zsh/builtins.yo, + Doc/Zsh/compat.yo, Doc/Zsh/mod_watch.yo, Doc/Zsh/params.yo, + Src/Modules/watch.mdd, Src/builtin.c, Src/init.c, Src/params.c, + Src/utils.c, Src/Modules/watch.c, Src/zsh.mdd: separate watch/log + functionality out into a module + * 49537: aczsh.m4, configure.ac: fix finding utmpx file on FreeBSD 2021-11-01 Jun-ichi Takimoto diff --git a/Doc/Makefile.in b/Doc/Makefile.in index 5a6a705ff..23e5fc7e2 100644 --- a/Doc/Makefile.in +++ b/Doc/Makefile.in @@ -69,6 +69,7 @@ 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_stat.yo Zsh/mod_system.yo Zsh/mod_tcp.yo \ Zsh/mod_termcap.yo Zsh/mod_terminfo.yo \ +Zsh/mod_watch.yo \ Zsh/mod_zftp.yo Zsh/mod_zle.yo Zsh/mod_zleparameter.yo \ Zsh/mod_zprof.yo Zsh/mod_zpty.yo Zsh/mod_zselect.yo \ Zsh/mod_zutil.yo diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index ddbcd4363..733d8f185 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -1235,14 +1235,6 @@ Same as tt(typeset), except that the options tt(-g), and tt(-f) are not permitted. In this case the tt(-x) option does not force the use of tt(-g), i.e. exported variables will be local to functions. ) -findex(log) -vindex(watch, use of) -cindex(watching users) -cindex(users, watching) -item(tt(log))( -List all users currently logged in who are affected by -the current setting of the tt(watch) parameter. -) findex(logout) item(tt(logout) [ var(n) ])( Same as tt(exit), except that it only works in a login shell. diff --git a/Doc/Zsh/compat.yo b/Doc/Zsh/compat.yo index 6e4dbcfa4..4d3567d45 100644 --- a/Doc/Zsh/compat.yo +++ b/Doc/Zsh/compat.yo @@ -30,8 +30,7 @@ tt(PROMPT2), tt(PROMPT3), tt(PROMPT4), tt(psvar), -tt(status), -tt(watch). +tt(status). vindex(ENV, use of) The usual zsh startup/shutdown scripts are not executed. Login shells diff --git a/Doc/Zsh/mod_watch.yo b/Doc/Zsh/mod_watch.yo new file mode 100644 index 000000000..4eea89e23 --- /dev/null +++ b/Doc/Zsh/mod_watch.yo @@ -0,0 +1,140 @@ +COMMENT(!MOD!zsh/watch +Reporting of login and logout events. +!MOD!) +The tt(zsh/watch) module can be used to report when specific users log in or +out. This is controlled via the following parameters. + +startitem() +vindex(LOGCHECK) +item(tt(LOGCHECK))( +The interval in seconds between checks for login/logout activity +using the tt(watch) parameter. +) +vindex(watch) +vindex(WATCH) +item(tt(watch) (tt(WATCH) ))( +An array (colon-separated list) of login/logout events to report. + +If it contains the single word `tt(all)', then all login/logout events +are reported. If it contains the single word `tt(notme)', then all +events are reported as with `tt(all)' except tt($USERNAME). + +An entry in this list may consist of a username, +an `tt(@)' followed by a remote hostname, +and a `tt(%)' followed by a line (tty). Any of these may +be a pattern (be sure to quote this during the assignment to +tt(watch) so that it does not immediately perform file generation); +the setting of the tt(EXTENDED_GLOB) option is respected. +Any or all of these components may be present in an entry; +if a login/logout event matches all of them, +it is reported. + +For example, with the tt(EXTENDED_GLOB) option set, the following: + +example(watch=('^(pws|barts)')) + +causes reports for activity associated with any user other than tt(pws) +or tt(barts). +) +vindex(WATCHFMT) +item(tt(WATCHFMT))( +The format of login/logout reports if the tt(watch) parameter is set. +Default is `tt(%n has %a %l from %m)'. +Recognizes the following escape sequences: + +startitem() +item(tt(%n))( +The name of the user that logged in/out. +) +item(tt(%a))( +The observed action, i.e. "logged on" or "logged off". +) +item(tt(%l))( +The line (tty) the user is logged in on. +) +item(tt(%M))( +The full hostname of the remote host. +) +item(tt(%m))( +The hostname up to the first `tt(.)'. If only the +IP address is available or the utmp field contains +the name of an X-windows display, the whole name is printed. + +em(NOTE:) +The `tt(%m)' and `tt(%M)' escapes will work only if there is a host name +field in the utmp on your machine. Otherwise they are +treated as ordinary strings. +) +item(tt(%S) LPAR()tt(%s)RPAR())( +Start (stop) standout mode. +) +item(tt(%U) LPAR()tt(%u)RPAR())( +Start (stop) underline mode. +) +item(tt(%B) LPAR()tt(%b)RPAR())( +Start (stop) boldface mode. +) +xitem(tt(%t)) +item(tt(%@))( +The time, in 12-hour, am/pm format. +) +item(tt(%T))( +The time, in 24-hour format. +) +item(tt(%w))( +The date in `var(day)tt(-)var(dd)' format. +) +item(tt(%W))( +The date in `var(mm)tt(/)var(dd)tt(/)var(yy)' format. +) +item(tt(%D))( +The date in `var(yy)tt(-)var(mm)tt(-)var(dd)' format. +) +item(tt(%D{)var(string)tt(}))( +The date formatted as var(string) using the tt(strftime) function, with +zsh extensions as described by +ifzman(EXPANSION OF PROMPT SEQUENCES in zmanref(zshmisc))\ +ifnzman(noderef(Prompt Expansion)). +) +item(tt(%LPAR())var(x)tt(:)var(true-text)tt(:)var(false-text)tt(RPAR()))( +Specifies a ternary expression. +The character following the var(x) is +arbitrary; the same character is used to separate the text +for the "true" result from that for the "false" result. +Both the separator and the right parenthesis may be escaped +with a backslash. +Ternary expressions may be nested. + +The test character var(x) may be any one of `tt(l)', `tt(n)', `tt(m)' +or `tt(M)', which indicate a `true' result if the corresponding +escape sequence would return a non-empty value; or it may be `tt(a)', +which indicates a `true' result if the watched user has logged in, +or `false' if he has logged out. +Other characters evaluate to neither true nor false; the entire +expression is omitted in this case. + +If the result is `true', then the var(true-text) +is formatted according to the rules above and printed, +and the var(false-text) is skipped. +If `false', the var(true-text) is skipped and the var(false-text) +is formatted and printed. +Either or both of the branches may be empty, but +both separators must be present in any case. +) +enditem() +) +enditem() + +Furthermore, the tt(zsh/watch) module makes available one builtin +command: + +startitem() +findex(log) +vindex(watch, use of) +cindex(watching users) +cindex(users, watching) +item(tt(log))( +List all users currently logged in who are affected by +the current setting of the tt(watch) parameter. +) +enditem() diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index a88e44d4f..1f2f01f55 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -1332,11 +1332,6 @@ most as many lines as given by the absolute value. If set to zero, the shell asks only if the top of the listing would scroll off the screen. ) -vindex(LOGCHECK) -item(tt(LOGCHECK))( -The interval in seconds between checks for login/logout activity -using the tt(watch) parameter. -) vindex(MAIL) item(tt(MAIL))( If this parameter is set and tt(mailpath) is not set, @@ -1670,119 +1665,6 @@ to be interpreted as a file extension. The default is not to append any suffix, thus this parameter should be assigned only when needed and then unset again. ) -vindex(watch) -vindex(WATCH) -item(tt(watch) (tt(WATCH) ))( -An array (colon-separated list) of login/logout events to report. - -If it contains the single word `tt(all)', then all login/logout events -are reported. If it contains the single word `tt(notme)', then all -events are reported as with `tt(all)' except tt($USERNAME). - -An entry in this list may consist of a username, -an `tt(@)' followed by a remote hostname, -and a `tt(%)' followed by a line (tty). Any of these may -be a pattern (be sure to quote this during the assignment to -tt(watch) so that it does not immediately perform file generation); -the setting of the tt(EXTENDED_GLOB) option is respected. -Any or all of these components may be present in an entry; -if a login/logout event matches all of them, -it is reported. - -For example, with the tt(EXTENDED_GLOB) option set, the following: - -example(watch=('^(pws|barts)')) - -causes reports for activity associated with any user other than tt(pws) -or tt(barts). -) -vindex(WATCHFMT) -item(tt(WATCHFMT))( -The format of login/logout reports if the tt(watch) parameter is set. -Default is `tt(%n has %a %l from %m)'. -Recognizes the following escape sequences: - -startitem() -item(tt(%n))( -The name of the user that logged in/out. -) -item(tt(%a))( -The observed action, i.e. "logged on" or "logged off". -) -item(tt(%l))( -The line (tty) the user is logged in on. -) -item(tt(%M))( -The full hostname of the remote host. -) -item(tt(%m))( -The hostname up to the first `tt(.)'. If only the -IP address is available or the utmp field contains -the name of an X-windows display, the whole name is printed. - -em(NOTE:) -The `tt(%m)' and `tt(%M)' escapes will work only if there is a host name -field in the utmp on your machine. Otherwise they are -treated as ordinary strings. -) -item(tt(%S) LPAR()tt(%s)RPAR())( -Start (stop) standout mode. -) -item(tt(%U) LPAR()tt(%u)RPAR())( -Start (stop) underline mode. -) -item(tt(%B) LPAR()tt(%b)RPAR())( -Start (stop) boldface mode. -) -xitem(tt(%t)) -item(tt(%@))( -The time, in 12-hour, am/pm format. -) -item(tt(%T))( -The time, in 24-hour format. -) -item(tt(%w))( -The date in `var(day)tt(-)var(dd)' format. -) -item(tt(%W))( -The date in `var(mm)tt(/)var(dd)tt(/)var(yy)' format. -) -item(tt(%D))( -The date in `var(yy)tt(-)var(mm)tt(-)var(dd)' format. -) -item(tt(%D{)var(string)tt(}))( -The date formatted as var(string) using the tt(strftime) function, with -zsh extensions as described by -ifzman(EXPANSION OF PROMPT SEQUENCES in zmanref(zshmisc))\ -ifnzman(noderef(Prompt Expansion)). -) -item(tt(%LPAR())var(x)tt(:)var(true-text)tt(:)var(false-text)tt(RPAR()))( -Specifies a ternary expression. -The character following the var(x) is -arbitrary; the same character is used to separate the text -for the "true" result from that for the "false" result. -Both the separator and the right parenthesis may be escaped -with a backslash. -Ternary expressions may be nested. - -The test character var(x) may be any one of `tt(l)', `tt(n)', `tt(m)' -or `tt(M)', which indicate a `true' result if the corresponding -escape sequence would return a non-empty value; or it may be `tt(a)', -which indicates a `true' result if the watched user has logged in, -or `false' if he has logged out. -Other characters evaluate to neither true nor false; the entire -expression is omitted in this case. - -If the result is `true', then the var(true-text) -is formatted according to the rules above and printed, -and the var(false-text) is skipped. -If `false', the var(true-text) is skipped and the var(false-text) -is formatted and printed. -Either or both of the branches may be empty, but -both separators must be present in any case. -) -enditem() -) vindex(WORDCHARS) item(tt(WORDCHARS) )( A list of non-alphanumeric characters considered part of a word diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c new file mode 100644 index 000000000..02f0562fc --- /dev/null +++ b/Src/Modules/watch.c @@ -0,0 +1,716 @@ +/* + * watch.c - login/logout watching + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad 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 Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "watch.mdh" + +/* Headers for utmp/utmpx structures */ +#ifdef HAVE_UTMP_H +# include +#endif +#ifdef HAVE_UTMPX_H +# include +#endif + +/* Find utmp file */ +#if !defined(REAL_UTMP_FILE) && defined(UTMP_FILE) +# define REAL_UTMP_FILE UTMP_FILE +#endif +#if !defined(REAL_UTMP_FILE) && defined(_PATH_UTMP) +# define REAL_UTMP_FILE _PATH_UTMP +#endif +#if !defined(REAL_UTMP_FILE) && defined(PATH_UTMP_FILE) +# define REAL_UTMP_FILE PATH_UTMP_FILE +#endif + +/* Find wtmp file */ +#if !defined(REAL_WTMP_FILE) && defined(WTMP_FILE) +# define REAL_WTMP_FILE WTMP_FILE +#endif +#if !defined(REAL_WTMP_FILE) && defined(_PATH_WTMP) +# define REAL_WTMP_FILE _PATH_WTMP +#endif +#if !defined(REAL_WTMP_FILE) && defined(PATH_WTMP_FILE) +# define REAL_WTMP_FILE PATH_WTMP_FILE +#endif + +/* Find utmpx file */ +#if !defined(REAL_UTMPX_FILE) && defined(UTMPX_FILE) +# define REAL_UTMPX_FILE UTMPX_FILE +#endif +#if !defined(REAL_UTMPX_FILE) && defined(_PATH_UTMPX) +# define REAL_UTMPX_FILE _PATH_UTMPX +#endif +#if !defined(REAL_UTMPX_FILE) && defined(PATH_UTMPX_FILE) +# define REAL_UTMPX_FILE PATH_UTMPX_FILE +#endif + +/* Find wtmpx file */ +#if !defined(REAL_WTMPX_FILE) && defined(WTMPX_FILE) +# define REAL_WTMPX_FILE WTMPX_FILE +#endif +#if !defined(REAL_WTMPX_FILE) && defined(_PATH_WTMPX) +# define REAL_WTMPX_FILE _PATH_WTMPX +#endif +#if !defined(REAL_WTMPX_FILE) && defined(PATH_WTMPX_FILE) +# define REAL_WTMPX_FILE PATH_WTMPX_FILE +#endif + +/* Decide which structure to use. We use a structure that exists in * + * the headers, and require that its corresponding utmp file exist. * + * (wtmp is less important.) */ + +#if !defined(WATCH_STRUCT_UTMP) && defined(HAVE_STRUCT_UTMPX) && defined(REAL_UTMPX_FILE) +# define WATCH_STRUCT_UTMP struct utmpx +# if defined(HAVE_SETUTXENT) && defined(HAVE_GETUTXENT) && defined(HAVE_ENDUTXENT) +# define setutent setutxent +# define getutent getutxent +# define endutent endutxent +# ifndef HAVE_GETUTENT +# define HAVE_GETUTENT 1 +# endif +# endif + +/* + * In utmpx, the ut_name field is replaced by ut_user. + * However, on some systems ut_name may already be defined this + * way for the purposes of utmp. + */ +# ifndef ut_name +# define ut_name ut_user +# endif +# ifdef HAVE_STRUCT_UTMPX_UT_XTIME +# undef ut_time +# define ut_time ut_xtime +# else /* !HAVE_STRUCT_UTMPX_UT_XTIME */ +# ifdef HAVE_STRUCT_UTMPX_UT_TV +# undef ut_time +# define ut_time ut_tv.tv_sec +# endif /* HAVE_STRUCT_UTMPX_UT_TV */ +# endif /* !HAVE_STRUCT_UTMPX_UT_XTIME */ +# define WATCH_UTMP_FILE REAL_UTMPX_FILE +# ifdef REAL_WTMPX_FILE +# define WATCH_WTMP_FILE REAL_WTMPX_FILE +# endif +# ifdef HAVE_STRUCT_UTMPX_UT_HOST +# define WATCH_UTMP_UT_HOST 1 +# endif +#endif + +#if !defined(WATCH_STRUCT_UTMP) && defined(HAVE_STRUCT_UTMP) && defined(REAL_UTMP_FILE) +# define WATCH_STRUCT_UTMP struct utmp +# define WATCH_UTMP_FILE REAL_UTMP_FILE +# ifdef REAL_WTMP_FILE +# define WATCH_WTMP_FILE REAL_WTMP_FILE +# endif +# ifdef HAVE_STRUCT_UTMP_UT_HOST +# define WATCH_UTMP_UT_HOST 1 +# endif +#endif + +#ifdef WATCH_UTMP_UT_HOST +# define DEFAULT_WATCHFMT "%n has %a %l from %m." +#else /* !WATCH_UTMP_UT_HOST */ +# define DEFAULT_WATCHFMT "%n has %a %l." +#endif /* !WATCH_UTMP_UT_HOST */ + +#ifdef WATCH_STRUCT_UTMP + +# include "watch.pro" + +# ifndef WATCH_WTMP_FILE +# define WATCH_WTMP_FILE "/dev/null" +# endif + +static int wtabsz = 0; +static WATCH_STRUCT_UTMP *wtab = NULL; + +/* the last time we checked the people in the WATCH variable */ +static time_t lastwatch; + +static time_t lastutmpcheck = 0; + +/* get the time of login/logout for WATCH */ + +static time_t +getlogtime(WATCH_STRUCT_UTMP *u, int inout) +{ + FILE *in; + WATCH_STRUCT_UTMP uu; + int first = 1; + int srchlimit = 50; /* max number of wtmp records to search */ + + if (inout) + return u->ut_time; + if (!(in = fopen(WATCH_WTMP_FILE, "r"))) + return time(NULL); + fseek(in, 0, SEEK_END); + do { + if (fseek(in, ((first) ? -1 : -2) * sizeof(WATCH_STRUCT_UTMP), SEEK_CUR)) { + fclose(in); + return time(NULL); + } + first = 0; + if (!fread(&uu, sizeof(WATCH_STRUCT_UTMP), 1, in)) { + fclose(in); + return time(NULL); + } + if (uu.ut_time < lastwatch || !srchlimit--) { + fclose(in); + return time(NULL); + } + } + while (memcmp(&uu, u, sizeof(uu))); + + do + if (!fread(&uu, sizeof(WATCH_STRUCT_UTMP), 1, in)) { + fclose(in); + return time(NULL); + } + while (strncmp(uu.ut_line, u->ut_line, sizeof(u->ut_line))); + fclose(in); + return uu.ut_time; +} + +/* Mutually recursive call to handle ternaries in $WATCHFMT */ + +# define BEGIN3 '(' +# define END3 ')' + +static char * +watch3ary(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt) +{ + int truth = 1, sep; + + switch (*fmt++) { + case 'n': + truth = (u->ut_name[0] != 0); + break; + case 'a': + truth = inout; + break; + case 'l': + if (!strncmp(u->ut_line, "tty", 3)) + truth = (u->ut_line[3] != 0); + else + truth = (u->ut_line[0] != 0); + break; +# ifdef WATCH_UTMP_UT_HOST + case 'm': + case 'M': + truth = (u->ut_host[0] != 0); + break; +# endif /* WATCH_UTMP_UT_HOST */ + default: + prnt = 0; /* Skip unknown conditionals entirely */ + break; + } + sep = *fmt++; + fmt = watchlog2(inout, u, fmt, (truth && prnt), sep); + return watchlog2(inout, u, fmt, (!truth && prnt), END3); +} + +/* print a login/logout event */ + +/**/ +static char * +watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) +{ + char buf[40], buf2[80]; + time_t timet; + struct tm *tm; + char *fm2; + int len; +# ifdef WATCH_UTMP_UT_HOST + char *p; + int i; +# endif /* WATCH_UTMP_UT_HOST */ + + while (*fmt) + if (*fmt == '\\') { + if (*++fmt) { + if (prnt) + putchar(*fmt); + ++fmt; + } else if (fini) + return fmt; + else + break; + } + else if (*fmt == fini) + return ++fmt; + else if (*fmt != '%') { + if (prnt) + putchar(*fmt); + ++fmt; + } else { + if (*++fmt == BEGIN3) + fmt = watch3ary(inout, u, ++fmt, prnt); + else if (!prnt) + ++fmt; + else + switch (*(fm2 = fmt++)) { + case 'n': + printf("%.*s", (int)sizeof(u->ut_name), u->ut_name); + break; + case 'a': + printf("%s", (!inout) ? "logged off" : "logged on"); + break; + case 'l': + if (!strncmp(u->ut_line, "tty", 3)) + printf("%.*s", (int)sizeof(u->ut_line) - 3, u->ut_line + 3); + else + printf("%.*s", (int)sizeof(u->ut_line), u->ut_line); + break; +# ifdef WATCH_UTMP_UT_HOST + case 'm': + for (p = u->ut_host, i = sizeof(u->ut_host); i && *p; i--, p++) { + if (*p == '.' && !idigit(p[1])) + break; + putchar(*p); + } + break; + case 'M': + printf("%.*s", (int)sizeof(u->ut_host), u->ut_host); + break; +# endif /* WATCH_UTMP_UT_HOST */ + case 'T': + case 't': + case '@': + case 'W': + case 'w': + case 'D': + switch (*fm2) { + case '@': + case 't': + fm2 = "%l:%M%p"; + break; + case 'T': + fm2 = "%K:%M"; + break; + case 'w': + fm2 = "%a %f"; + break; + case 'W': + fm2 = "%m/%d/%y"; + break; + case 'D': + if (fm2[1] == '{') { + char *dd, *ss; + int n = 79; + + for (ss = fm2 + 2, dd = buf2; + n-- && *ss && *ss != '}'; ++ss, ++dd) + *dd = *((*ss == '\\' && ss[1]) ? ++ss : ss); + if (*ss == '}') { + *dd = '\0'; + fmt = ss + 1; + fm2 = buf2; + } + else fm2 = "%y-%m-%d"; + } + else fm2 = "%y-%m-%d"; + break; + } + timet = getlogtime(u, inout); + tm = localtime(&timet); + len = ztrftime(buf, 40, fm2, tm, 0L); + if (len > 0) + metafy(buf, len, META_NOALLOC); + printf("%s", (*buf == ' ') ? buf + 1 : buf); + break; + case '%': + putchar('%'); + break; + case 'S': + txtset(TXTSTANDOUT); + tsetcap(TCSTANDOUTBEG, TSC_RAW); + break; + case 's': + txtunset(TXTSTANDOUT); + tsetcap(TCSTANDOUTEND, TSC_RAW|TSC_DIRTY); + break; + case 'B': + txtset(TXTBOLDFACE); + tsetcap(TCBOLDFACEBEG, TSC_RAW|TSC_DIRTY); + break; + case 'b': + txtunset(TXTBOLDFACE); + tsetcap(TCALLATTRSOFF, TSC_RAW|TSC_DIRTY); + break; + case 'U': + txtset(TXTUNDERLINE); + tsetcap(TCUNDERLINEBEG, TSC_RAW); + break; + case 'u': + txtunset(TXTUNDERLINE); + tsetcap(TCUNDERLINEEND, TSC_RAW|TSC_DIRTY); + break; + default: + putchar('%'); + putchar(*fm2); + break; + } + } + if (prnt) + putchar('\n'); + + return fmt; +} + +/* See if the watch entry matches */ + +static int +watchlog_match(char *teststr, char *actual, int len) +{ + int ret = 0; + Patprog pprog; + char *str = dupstring(teststr); + + tokenize(str); + + if ((pprog = patcompile(str, PAT_STATIC, 0))) { + queue_signals(); + if (pattry(pprog, actual)) + ret = 1; + unqueue_signals(); + } else if (!strncmp(actual, teststr, len)) + ret = 1; + return ret; +} + +/* check the List for login/logouts */ + +static void +watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt) +{ + char *v, *vv, sav; + int bad; + + if (!*u->ut_name) + return; + + if (*w && !strcmp(*w, "all")) { + (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; + } + for (; *w; w++) { + bad = 0; + v = *w; + if (*v != '@' && *v != '%') { + for (vv = v; *vv && *vv != '@' && *vv != '%'; vv++); + sav = *vv; + *vv = '\0'; + if (!watchlog_match(v, u->ut_name, sizeof(u->ut_name))) + bad = 1; + *vv = sav; + v = vv; + } + for (;;) + if (*v == '%') { + for (vv = ++v; *vv && *vv != '@'; vv++); + sav = *vv; + *vv = '\0'; + if (!watchlog_match(v, u->ut_line, sizeof(u->ut_line))) + bad = 1; + *vv = sav; + v = vv; + } +# ifdef WATCH_UTMP_UT_HOST + else if (*v == '@') { + for (vv = ++v; *vv && *vv != '%'; vv++); + sav = *vv; + *vv = '\0'; + if (!watchlog_match(v, u->ut_host, strlen(v))) + bad = 1; + *vv = sav; + v = vv; + } +# endif /* WATCH_UTMP_UT_HOST */ + else + break; + if (!bad) { + (void)watchlog2(inout, u, fmt, 1, 0); + return; + } + } +} + +/* compare 2 utmp entries */ + +static int +ucmp(WATCH_STRUCT_UTMP *u, WATCH_STRUCT_UTMP *v) +{ + if (u->ut_time == v->ut_time) + return strncmp(u->ut_line, v->ut_line, sizeof(u->ut_line)); + return u->ut_time - v->ut_time; +} + +/* initialize the user List */ + +static int +readwtab(WATCH_STRUCT_UTMP **head, int initial_sz) +{ + WATCH_STRUCT_UTMP *uptr; + int wtabmax = initial_sz < 2 ? 32 : initial_sz; + int sz = 0; +# ifdef HAVE_GETUTENT + WATCH_STRUCT_UTMP *tmp; +# else + FILE *in; +# endif + + uptr = *head = (WATCH_STRUCT_UTMP *) + zalloc(wtabmax * sizeof(WATCH_STRUCT_UTMP)); +# ifdef HAVE_GETUTENT + setutent(); + while ((tmp = getutent()) != NULL) { + memcpy(uptr, tmp, sizeof (WATCH_STRUCT_UTMP)); +# else + if (!(in = fopen(WATCH_UTMP_FILE, "r"))) + return 0; + while (fread(uptr, sizeof(WATCH_STRUCT_UTMP), 1, in)) { +# endif +# ifdef USER_PROCESS + if (uptr->ut_type == USER_PROCESS) +# else /* !USER_PROCESS */ + if (uptr->ut_name[0]) +# endif /* !USER_PROCESS */ + { + uptr++; + if (++sz == wtabmax) { + uptr = (WATCH_STRUCT_UTMP *) + realloc(*head, (wtabmax *= 2) * sizeof(WATCH_STRUCT_UTMP)); + if (uptr == NULL) { + /* memory pressure - so stop consuming and use, what we have + * Other option is to exit() here, as zmalloc does on error */ + sz--; + break; + } + *head = uptr; + uptr += sz; + } + } + } +# ifdef HAVE_GETUTENT + endutent(); +# else + fclose(in); +# endif + + if (sz) + qsort((void *) *head, sz, sizeof(WATCH_STRUCT_UTMP), + (int (*) _((const void *, const void *)))ucmp); + return sz; +} + +/* Check for login/logout events; executed before * + * each prompt if WATCH is set */ + +/**/ +void +dowatch(void) +{ + WATCH_STRUCT_UTMP *utab, *uptr, *wptr; + struct stat st; + char **s; + char *fmt; + int utabsz, uct, wct; + + s = watch; + + holdintr(); + if (!wtab) + wtabsz = readwtab(&wtab, 32); + if ((stat(WATCH_UTMP_FILE, &st) == -1) || (st.st_mtime <= lastutmpcheck)) { + noholdintr(); + return; + } + lastutmpcheck = st.st_mtime; + utabsz = readwtab(&utab, wtabsz + 4); + noholdintr(); + if (errflag) { + free(utab); + return; + } + + wct = wtabsz; + uct = utabsz; + uptr = utab; + wptr = wtab; + if (errflag) { + free(utab); + return; + } + queue_signals(); + if (!(fmt = getsparam_u("WATCHFMT"))) + fmt = DEFAULT_WATCHFMT; + while ((uct || wct) && !errflag) { + if (!uct || (wct && ucmp(uptr, wptr) > 0)) + wct--, watchlog(0, wptr++, s, fmt); + else if (!wct || (uct && ucmp(uptr, wptr) < 0)) + uct--, watchlog(1, uptr++, s, fmt); + else + uptr++, wptr++, wct--, uct--; + } + unqueue_signals(); + free(wtab); + wtab = utab; + wtabsz = utabsz; + fflush(stdout); + lastwatch = time(NULL); +} + +static void +checksched(void) +{ + /* Do nothing if WATCH is not set, or LOGCHECK has not elapsed */ + if (watch && (int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK")) + dowatch(); +} + +/**/ +static int +bin_log(UNUSED(char *nam), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func)) +{ + if (!watch) + return 1; + if (wtab) + free(wtab); + wtab = (WATCH_STRUCT_UTMP *)zalloc(1); + wtabsz = 0; + lastutmpcheck = 0; + dowatch(); + return 0; +} + +#else /* !WATCH_STRUCT_UTMP */ + +static void +checksched(void) +{ +} + +/**/ +static int +bin_log(char *nam, char **argv, Options ops, int func) +{ + return bin_notavail(nam, argv, ops, func); +} + +#endif /* !WATCH_STRUCT_UTMP */ + +/**/ +static char **watch; /* $watch */ + +/* module setup */ + +static struct builtin bintab[] = { + BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL), +}; + +static struct paramdef partab[] = { + PARAMDEF("WATCH", PM_TIED|PM_SCALAR|PM_SPECIAL, &watch, &colonarr_gsu), + PARAMDEF("watch", PM_TIED|PM_ARRAY|PM_SPECIAL, &watch, &vararray_gsu), +}; + +static struct features module_features = { + bintab, sizeof(bintab)/sizeof(*bintab), + 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)) +{ + static char const * const default_watchfmt = DEFAULT_WATCHFMT; + Param pm; + + if ((pm = (Param) paramtab->getnode(paramtab, "watch"))) + pm->ename = "WATCH"; + if ((pm = (Param) paramtab->getnode(paramtab, "WATCH"))) + pm->ename = "watch"; + watch = mkarray(NULL); + + /* These two parameters are only set to defaults if not set. + * So setting them in .zshrc will not be enough to load the + * module. It's useless until the watch array is set anyway. */ + if (!paramtab->getnode(paramtab, "WATCHFMT")) + setsparam("WATCHFMT", ztrdup_metafy(default_watchfmt)); + if (!paramtab->getnode(paramtab, "LOGCHECK")) + setiparam("LOGCHECK", 60); + + addprepromptfn(&checksched); + + return 0; +} + +/**/ +int +cleanup_(Module m) +{ + delprepromptfn(&checksched); + return setfeatureenables(m, &module_features, NULL); +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + return 0; +} diff --git a/Src/Modules/watch.mdd b/Src/Modules/watch.mdd new file mode 100644 index 000000000..7e8454ede --- /dev/null +++ b/Src/Modules/watch.mdd @@ -0,0 +1,7 @@ +name=zsh/watch +link=dynamic +load=yes + +autofeatures="b:log p:WATCH p:watch" + +objects="watch.o" diff --git a/Src/builtin.c b/Src/builtin.c index 89bcd98db..8ef678b22 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -89,7 +89,6 @@ static struct builtin builtins[] = 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("log", 0, bin_log, 0, 0, 0, NULL, NULL), BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL), #if defined(ZSH_MEM) & defined(ZSH_MEM_DEBUG) diff --git a/Src/init.c b/Src/init.c index 878a53a37..871d46b12 100644 --- a/Src/init.c +++ b/Src/init.c @@ -1042,7 +1042,6 @@ setupvals(char *cmd, char *runscript, char *zsh_name) #endif /* FPATH_NEEDS_INIT */ mailpath = mkarray(NULL); - watch = mkarray(NULL); psvar = mkarray(NULL); module_path = mkarray(ztrdup(MODULE_DIR)); modulestab = newmoduletable(17, "modules"); diff --git a/Src/params.c b/Src/params.c index b703a97ce..dadf83129 100644 --- a/Src/params.c +++ b/Src/params.c @@ -63,7 +63,6 @@ char **pparams, /* $argv */ **mailpath, /* $mailpath */ **manpath, /* $manpath */ **psvar, /* $psvar */ - **watch, /* $watch */ **zsh_eval_context; /* $zsh_eval_context */ /**/ mod_export @@ -194,6 +193,10 @@ mod_export const struct gsu_hash stdhash_gsu = mod_export const struct gsu_hash nullsethash_gsu = { hashgetfn, nullsethashfn, nullunsetfn }; +/**/ +mod_export const struct gsu_scalar colonarr_gsu = +{ colonarrgetfn, colonarrsetfn, stdunsetfn }; + /* Non standard methods (not exported) */ static const struct gsu_integer pound_gsu = @@ -259,9 +262,6 @@ static const struct gsu_integer varint_readonly_gsu = static const struct gsu_integer zlevar_gsu = { intvargetfn, zlevarsetfn, stdunsetfn }; -static const struct gsu_scalar colonarr_gsu = -{ colonarrgetfn, colonarrsetfn, stdunsetfn }; - static const struct gsu_integer argc_gsu = { poundgetfn, nullintsetfn, stdunsetfn }; static const struct gsu_array pipestatus_gsu = @@ -398,7 +398,6 @@ IPDEF8("CDPATH", &cdpath, "cdpath", PM_TIED), IPDEF8("FIGNORE", &fignore, "fignore", PM_TIED), IPDEF8("FPATH", &fpath, "fpath", PM_TIED), IPDEF8("MAILPATH", &mailpath, "mailpath", PM_TIED), -IPDEF8("WATCH", &watch, "watch", PM_TIED), IPDEF8("PATH", &path, "path", PM_RESTRICTED|PM_TIED), IPDEF8("PSVAR", &psvar, "psvar", PM_TIED), IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, "zsh_eval_context", PM_READONLY_SPECIAL|PM_TIED), @@ -430,7 +429,6 @@ IPDEF9("fpath", &fpath, "FPATH", PM_TIED), IPDEF9("mailpath", &mailpath, "MAILPATH", PM_TIED), IPDEF9("manpath", &manpath, "MANPATH", PM_TIED), IPDEF9("psvar", &psvar, "PSVAR", PM_TIED), -IPDEF9("watch", &watch, "WATCH", PM_TIED), IPDEF9("zsh_eval_context", &zsh_eval_context, "ZSH_EVAL_CONTEXT", PM_TIED|PM_READONLY_SPECIAL), @@ -453,7 +451,6 @@ IPDEF8("CDPATH", &cdpath, NULL, 0), IPDEF8("FIGNORE", &fignore, NULL, 0), IPDEF8("FPATH", &fpath, NULL, 0), IPDEF8("MAILPATH", &mailpath, NULL, 0), -IPDEF8("WATCH", &watch, NULL, 0), IPDEF8("PATH", &path, NULL, PM_RESTRICTED), IPDEF8("PSVAR", &psvar, NULL, 0), IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, NULL, PM_READONLY_SPECIAL), @@ -836,7 +833,6 @@ createparamtable(void) */ setsparam("TMPPREFIX", ztrdup_metafy(DEFAULT_TMPPREFIX)); setsparam("TIMEFMT", ztrdup_metafy(DEFAULT_TIMEFMT)); - setsparam("WATCHFMT", ztrdup_metafy(default_watchfmt)); hostnam = (char *)zalloc(256); gethostname(hostnam, 256); @@ -4093,7 +4089,7 @@ arrvarsetfn(Param pm, char **x) } /**/ -char * +mod_export char * colonarrgetfn(Param pm) { char ***dptr = (char ***)pm->u.data; @@ -4101,7 +4097,7 @@ colonarrgetfn(Param pm) } /**/ -void +mod_export void colonarrsetfn(Param pm, char *x) { char ***dptr = (char ***)pm->u.data; diff --git a/Src/utils.c b/Src/utils.c index ed3690172..8adab2bd7 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1494,11 +1494,6 @@ deltimedfn(voidvoidfnptr_t func) /**/ time_t lastmailcheck; -/* the last time we checked the people in the WATCH variable */ - -/**/ -time_t lastwatch; - /* * Call a function given by "name" with optional arguments * "lnklst". If these are present the first argument is the function name. @@ -1637,17 +1632,6 @@ preprompt(void) if (errflag) return; - /* If WATCH is set, then check for the * - * specified login/logout events. */ - if (watch) { - if ((int) difftime(time(NULL), lastwatch) > getiparam("LOGCHECK")) { - dowatch(); - lastwatch = time(NULL); - } - } - if (errflag) - return; - /* Check mail */ currentmailcheck = time(NULL); if (mailcheck && diff --git a/Src/watch.c b/Src/watch.c deleted file mode 100644 index c41704315..000000000 --- a/Src/watch.c +++ /dev/null @@ -1,626 +0,0 @@ -/* - * watch.c - login/logout watching - * - * This file is part of zsh, the Z shell. - * - * Copyright (c) 1992-1997 Paul Falstad - * 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 Paul Falstad 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 Paul Falstad and the Zsh Development Group have been advised of - * the possibility of such damage. - * - * Paul Falstad 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 Paul Falstad and the - * Zsh Development Group have no obligation to provide maintenance, - * support, updates, enhancements, or modifications. - * - */ - -#include "zsh.mdh" - -/* Headers for utmp/utmpx structures */ -#ifdef HAVE_UTMP_H -# include -#endif -#ifdef HAVE_UTMPX_H -# include -#endif - -/* Find utmp file */ -#if !defined(REAL_UTMP_FILE) && defined(UTMP_FILE) -# define REAL_UTMP_FILE UTMP_FILE -#endif -#if !defined(REAL_UTMP_FILE) && defined(_PATH_UTMP) -# define REAL_UTMP_FILE _PATH_UTMP -#endif -#if !defined(REAL_UTMP_FILE) && defined(PATH_UTMP_FILE) -# define REAL_UTMP_FILE PATH_UTMP_FILE -#endif - -/* Find wtmp file */ -#if !defined(REAL_WTMP_FILE) && defined(WTMP_FILE) -# define REAL_WTMP_FILE WTMP_FILE -#endif -#if !defined(REAL_WTMP_FILE) && defined(_PATH_WTMP) -# define REAL_WTMP_FILE _PATH_WTMP -#endif -#if !defined(REAL_WTMP_FILE) && defined(PATH_WTMP_FILE) -# define REAL_WTMP_FILE PATH_WTMP_FILE -#endif - -/* Find utmpx file */ -#if !defined(REAL_UTMPX_FILE) && defined(UTMPX_FILE) -# define REAL_UTMPX_FILE UTMPX_FILE -#endif -#if !defined(REAL_UTMPX_FILE) && defined(_PATH_UTMPX) -# define REAL_UTMPX_FILE _PATH_UTMPX -#endif -#if !defined(REAL_UTMPX_FILE) && defined(PATH_UTMPX_FILE) -# define REAL_UTMPX_FILE PATH_UTMPX_FILE -#endif - -/* Find wtmpx file */ -#if !defined(REAL_WTMPX_FILE) && defined(WTMPX_FILE) -# define REAL_WTMPX_FILE WTMPX_FILE -#endif -#if !defined(REAL_WTMPX_FILE) && defined(_PATH_WTMPX) -# define REAL_WTMPX_FILE _PATH_WTMPX -#endif -#if !defined(REAL_WTMPX_FILE) && defined(PATH_WTMPX_FILE) -# define REAL_WTMPX_FILE PATH_WTMPX_FILE -#endif - -/* Decide which structure to use. We use a structure that exists in * - * the headers, and require that its corresponding utmp file exist. * - * (wtmp is less important.) */ - -#if !defined(WATCH_STRUCT_UTMP) && defined(HAVE_STRUCT_UTMPX) && defined(REAL_UTMPX_FILE) -# define WATCH_STRUCT_UTMP struct utmpx -# if defined(HAVE_SETUTXENT) && defined(HAVE_GETUTXENT) && defined(HAVE_ENDUTXENT) -# define setutent setutxent -# define getutent getutxent -# define endutent endutxent -# ifndef HAVE_GETUTENT -# define HAVE_GETUTENT 1 -# endif -# endif - -/* - * In utmpx, the ut_name field is replaced by ut_user. - * However, on some systems ut_name may already be defined this - * way for the purposes of utmp. - */ -# ifndef ut_name -# define ut_name ut_user -# endif -# ifdef HAVE_STRUCT_UTMPX_UT_XTIME -# undef ut_time -# define ut_time ut_xtime -# else /* !HAVE_STRUCT_UTMPX_UT_XTIME */ -# ifdef HAVE_STRUCT_UTMPX_UT_TV -# undef ut_time -# define ut_time ut_tv.tv_sec -# endif /* HAVE_STRUCT_UTMPX_UT_TV */ -# endif /* !HAVE_STRUCT_UTMPX_UT_XTIME */ -# define WATCH_UTMP_FILE REAL_UTMPX_FILE -# ifdef REAL_WTMPX_FILE -# define WATCH_WTMP_FILE REAL_WTMPX_FILE -# endif -# ifdef HAVE_STRUCT_UTMPX_UT_HOST -# define WATCH_UTMP_UT_HOST 1 -# endif -#endif - -#if !defined(WATCH_STRUCT_UTMP) && defined(HAVE_STRUCT_UTMP) && defined(REAL_UTMP_FILE) -# define WATCH_STRUCT_UTMP struct utmp -# define WATCH_UTMP_FILE REAL_UTMP_FILE -# ifdef REAL_WTMP_FILE -# define WATCH_WTMP_FILE REAL_WTMP_FILE -# endif -# ifdef HAVE_STRUCT_UTMP_UT_HOST -# define WATCH_UTMP_UT_HOST 1 -# endif -#endif - -#ifdef WATCH_UTMP_UT_HOST -# define DEFAULT_WATCHFMT "%n has %a %l from %m." -#else /* !WATCH_UTMP_UT_HOST */ -# define DEFAULT_WATCHFMT "%n has %a %l." -#endif /* !WATCH_UTMP_UT_HOST */ - -/**/ -char const * const default_watchfmt = DEFAULT_WATCHFMT; - -#ifdef WATCH_STRUCT_UTMP - -# include "watch.pro" - -# ifndef WATCH_WTMP_FILE -# define WATCH_WTMP_FILE "/dev/null" -# endif - -static int wtabsz = 0; -static WATCH_STRUCT_UTMP *wtab = NULL; -static time_t lastutmpcheck = 0; - -/* get the time of login/logout for WATCH */ - -/**/ -static time_t -getlogtime(WATCH_STRUCT_UTMP *u, int inout) -{ - FILE *in; - WATCH_STRUCT_UTMP uu; - int first = 1; - int srchlimit = 50; /* max number of wtmp records to search */ - - if (inout) - return u->ut_time; - if (!(in = fopen(WATCH_WTMP_FILE, "r"))) - return time(NULL); - fseek(in, 0, SEEK_END); - do { - if (fseek(in, ((first) ? -1 : -2) * sizeof(WATCH_STRUCT_UTMP), SEEK_CUR)) { - fclose(in); - return time(NULL); - } - first = 0; - if (!fread(&uu, sizeof(WATCH_STRUCT_UTMP), 1, in)) { - fclose(in); - return time(NULL); - } - if (uu.ut_time < lastwatch || !srchlimit--) { - fclose(in); - return time(NULL); - } - } - while (memcmp(&uu, u, sizeof(uu))); - - do - if (!fread(&uu, sizeof(WATCH_STRUCT_UTMP), 1, in)) { - fclose(in); - return time(NULL); - } - while (strncmp(uu.ut_line, u->ut_line, sizeof(u->ut_line))); - fclose(in); - return uu.ut_time; -} - -/* Mutually recursive call to handle ternaries in $WATCHFMT */ - -# define BEGIN3 '(' -# define END3 ')' - -/**/ -static char * -watch3ary(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt) -{ - int truth = 1, sep; - - switch (*fmt++) { - case 'n': - truth = (u->ut_name[0] != 0); - break; - case 'a': - truth = inout; - break; - case 'l': - if (!strncmp(u->ut_line, "tty", 3)) - truth = (u->ut_line[3] != 0); - else - truth = (u->ut_line[0] != 0); - break; -# ifdef WATCH_UTMP_UT_HOST - case 'm': - case 'M': - truth = (u->ut_host[0] != 0); - break; -# endif /* WATCH_UTMP_UT_HOST */ - default: - prnt = 0; /* Skip unknown conditionals entirely */ - break; - } - sep = *fmt++; - fmt = watchlog2(inout, u, fmt, (truth && prnt), sep); - return watchlog2(inout, u, fmt, (!truth && prnt), END3); -} - -/* print a login/logout event */ - -/**/ -static char * -watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) -{ - char buf[40], buf2[80]; - time_t timet; - struct tm *tm; - char *fm2; - int len; -# ifdef WATCH_UTMP_UT_HOST - char *p; - int i; -# endif /* WATCH_UTMP_UT_HOST */ - - while (*fmt) - if (*fmt == '\\') { - if (*++fmt) { - if (prnt) - putchar(*fmt); - ++fmt; - } else if (fini) - return fmt; - else - break; - } - else if (*fmt == fini) - return ++fmt; - else if (*fmt != '%') { - if (prnt) - putchar(*fmt); - ++fmt; - } else { - if (*++fmt == BEGIN3) - fmt = watch3ary(inout, u, ++fmt, prnt); - else if (!prnt) - ++fmt; - else - switch (*(fm2 = fmt++)) { - case 'n': - printf("%.*s", (int)sizeof(u->ut_name), u->ut_name); - break; - case 'a': - printf("%s", (!inout) ? "logged off" : "logged on"); - break; - case 'l': - if (!strncmp(u->ut_line, "tty", 3)) - printf("%.*s", (int)sizeof(u->ut_line) - 3, u->ut_line + 3); - else - printf("%.*s", (int)sizeof(u->ut_line), u->ut_line); - break; -# ifdef WATCH_UTMP_UT_HOST - case 'm': - for (p = u->ut_host, i = sizeof(u->ut_host); i && *p; i--, p++) { - if (*p == '.' && !idigit(p[1])) - break; - putchar(*p); - } - break; - case 'M': - printf("%.*s", (int)sizeof(u->ut_host), u->ut_host); - break; -# endif /* WATCH_UTMP_UT_HOST */ - case 'T': - case 't': - case '@': - case 'W': - case 'w': - case 'D': - switch (*fm2) { - case '@': - case 't': - fm2 = "%l:%M%p"; - break; - case 'T': - fm2 = "%K:%M"; - break; - case 'w': - fm2 = "%a %f"; - break; - case 'W': - fm2 = "%m/%d/%y"; - break; - case 'D': - if (fm2[1] == '{') { - char *dd, *ss; - int n = 79; - - for (ss = fm2 + 2, dd = buf2; - n-- && *ss && *ss != '}'; ++ss, ++dd) - *dd = *((*ss == '\\' && ss[1]) ? ++ss : ss); - if (*ss == '}') { - *dd = '\0'; - fmt = ss + 1; - fm2 = buf2; - } - else fm2 = "%y-%m-%d"; - } - else fm2 = "%y-%m-%d"; - break; - } - timet = getlogtime(u, inout); - tm = localtime(&timet); - len = ztrftime(buf, 40, fm2, tm, 0L); - if (len > 0) - metafy(buf, len, META_NOALLOC); - printf("%s", (*buf == ' ') ? buf + 1 : buf); - break; - case '%': - putchar('%'); - break; - case 'S': - txtset(TXTSTANDOUT); - tsetcap(TCSTANDOUTBEG, TSC_RAW); - break; - case 's': - txtunset(TXTSTANDOUT); - tsetcap(TCSTANDOUTEND, TSC_RAW|TSC_DIRTY); - break; - case 'B': - txtset(TXTBOLDFACE); - tsetcap(TCBOLDFACEBEG, TSC_RAW|TSC_DIRTY); - break; - case 'b': - txtunset(TXTBOLDFACE); - tsetcap(TCALLATTRSOFF, TSC_RAW|TSC_DIRTY); - break; - case 'U': - txtset(TXTUNDERLINE); - tsetcap(TCUNDERLINEBEG, TSC_RAW); - break; - case 'u': - txtunset(TXTUNDERLINE); - tsetcap(TCUNDERLINEEND, TSC_RAW|TSC_DIRTY); - break; - default: - putchar('%'); - putchar(*fm2); - break; - } - } - if (prnt) - putchar('\n'); - - return fmt; -} - -/* See if the watch entry matches */ - -static int -watchlog_match(char *teststr, char *actual, int len) -{ - int ret = 0; - Patprog pprog; - char *str = dupstring(teststr); - - tokenize(str); - - if ((pprog = patcompile(str, PAT_STATIC, 0))) { - queue_signals(); - if (pattry(pprog, actual)) - ret = 1; - unqueue_signals(); - } else if (!strncmp(actual, teststr, len)) - ret = 1; - return ret; -} - -/* check the List for login/logouts */ - -/**/ -static void -watchlog(int inout, WATCH_STRUCT_UTMP *u, char **w, char *fmt) -{ - char *v, *vv, sav; - int bad; - - if (!*u->ut_name) - return; - - if (*w && !strcmp(*w, "all")) { - (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; - } - for (; *w; w++) { - bad = 0; - v = *w; - if (*v != '@' && *v != '%') { - for (vv = v; *vv && *vv != '@' && *vv != '%'; vv++); - sav = *vv; - *vv = '\0'; - if (!watchlog_match(v, u->ut_name, sizeof(u->ut_name))) - bad = 1; - *vv = sav; - v = vv; - } - for (;;) - if (*v == '%') { - for (vv = ++v; *vv && *vv != '@'; vv++); - sav = *vv; - *vv = '\0'; - if (!watchlog_match(v, u->ut_line, sizeof(u->ut_line))) - bad = 1; - *vv = sav; - v = vv; - } -# ifdef WATCH_UTMP_UT_HOST - else if (*v == '@') { - for (vv = ++v; *vv && *vv != '%'; vv++); - sav = *vv; - *vv = '\0'; - if (!watchlog_match(v, u->ut_host, strlen(v))) - bad = 1; - *vv = sav; - v = vv; - } -# endif /* WATCH_UTMP_UT_HOST */ - else - break; - if (!bad) { - (void)watchlog2(inout, u, fmt, 1, 0); - return; - } - } -} - -/* compare 2 utmp entries */ - -/**/ -static int -ucmp(WATCH_STRUCT_UTMP *u, WATCH_STRUCT_UTMP *v) -{ - if (u->ut_time == v->ut_time) - return strncmp(u->ut_line, v->ut_line, sizeof(u->ut_line)); - return u->ut_time - v->ut_time; -} - -/* initialize the user List */ - -/**/ -static int -readwtab(WATCH_STRUCT_UTMP **head, int initial_sz) -{ - WATCH_STRUCT_UTMP *uptr; - int wtabmax = initial_sz < 2 ? 32 : initial_sz; - int sz = 0; -# ifdef HAVE_GETUTENT - WATCH_STRUCT_UTMP *tmp; -# else - FILE *in; -# endif - - uptr = *head = (WATCH_STRUCT_UTMP *) - zalloc(wtabmax * sizeof(WATCH_STRUCT_UTMP)); -# ifdef HAVE_GETUTENT - setutent(); - while ((tmp = getutent()) != NULL) { - memcpy(uptr, tmp, sizeof (WATCH_STRUCT_UTMP)); -# else - if (!(in = fopen(WATCH_UTMP_FILE, "r"))) - return 0; - while (fread(uptr, sizeof(WATCH_STRUCT_UTMP), 1, in)) { -# endif -# ifdef USER_PROCESS - if (uptr->ut_type == USER_PROCESS) -# else /* !USER_PROCESS */ - if (uptr->ut_name[0]) -# endif /* !USER_PROCESS */ - { - uptr++; - if (++sz == wtabmax) { - uptr = (WATCH_STRUCT_UTMP *) - realloc(*head, (wtabmax *= 2) * sizeof(WATCH_STRUCT_UTMP)); - if (uptr == NULL) { - /* memory pressure - so stop consuming and use, what we have - * Other option is to exit() here, as zmalloc does on error */ - sz--; - break; - } - *head = uptr; - uptr += sz; - } - } - } -# ifdef HAVE_GETUTENT - endutent(); -# else - fclose(in); -# endif - - if (sz) - qsort((void *) *head, sz, sizeof(WATCH_STRUCT_UTMP), - (int (*) _((const void *, const void *)))ucmp); - return sz; -} - -/* Check for login/logout events; executed before * - * each prompt if WATCH is set */ - -/**/ -void -dowatch(void) -{ - WATCH_STRUCT_UTMP *utab, *uptr, *wptr; - struct stat st; - char **s; - char *fmt; - int utabsz, uct, wct; - - s = watch; - - holdintr(); - if (!wtab) - wtabsz = readwtab(&wtab, 32); - if ((stat(WATCH_UTMP_FILE, &st) == -1) || (st.st_mtime <= lastutmpcheck)) { - noholdintr(); - return; - } - lastutmpcheck = st.st_mtime; - utabsz = readwtab(&utab, wtabsz + 4); - noholdintr(); - if (errflag) { - free(utab); - return; - } - - wct = wtabsz; - uct = utabsz; - uptr = utab; - wptr = wtab; - if (errflag) { - free(utab); - return; - } - queue_signals(); - if (!(fmt = getsparam_u("WATCHFMT"))) - fmt = DEFAULT_WATCHFMT; - while ((uct || wct) && !errflag) { - if (!uct || (wct && ucmp(uptr, wptr) > 0)) - wct--, watchlog(0, wptr++, s, fmt); - else if (!wct || (uct && ucmp(uptr, wptr) < 0)) - uct--, watchlog(1, uptr++, s, fmt); - else - uptr++, wptr++, wct--, uct--; - } - unqueue_signals(); - free(wtab); - wtab = utab; - wtabsz = utabsz; - fflush(stdout); -} - -/**/ -int -bin_log(UNUSED(char *nam), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func)) -{ - if (!watch) - return 1; - if (wtab) - free(wtab); - wtab = (WATCH_STRUCT_UTMP *)zalloc(1); - wtabsz = 0; - lastutmpcheck = 0; - dowatch(); - return 0; -} - -#else /* !WATCH_STRUCT_UTMP */ - -/**/ -void dowatch(void) -{ -} - -/**/ -int -bin_log(char *nam, char **argv, Options ops, int func) -{ - return bin_notavail(nam, argv, ops, func); -} - -#endif /* !WATCH_STRUCT_UTMP */ diff --git a/Src/zsh.mdd b/Src/zsh.mdd index 9bcaccae5..da8d58322 100644 --- a/Src/zsh.mdd +++ b/Src/zsh.mdd @@ -13,7 +13,7 @@ objects="builtin.o compat.o cond.o context.o \ exec.o glob.o hashtable.o hashnameddir.o \ hist.o init.o input.o jobs.o lex.o linklist.o loop.o math.o \ mem.o module.o options.o params.o parse.o pattern.o prompt.o signals.o \ -signames.o sort.o string.o subst.o text.o utils.o watch.o \ +signames.o sort.o string.o subst.o text.o utils.o \ openssh_bsd_setres_id.o" headers="../config.h zsh_system.h zsh.h sigcount.h signals.h \ -- cgit v1.2.3 From 631576de0f7b4e52487175e3e017e5136424b626 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sun, 7 Nov 2021 12:55:27 +0100 Subject: 49544: only tie watch/WATCH if both come from the module --- ChangeLog | 5 +++++ Src/Modules/watch.c | 18 +++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 00c0d5be6..5865cb727 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2021-11-07 Oliver Kiddle + + * 49544: Src/Modules/watch.c: only tie watch/WATCH if both come + from the module + 2021-11-02 Oliver Kiddle * 49534, 49539: Doc/Makefile.in, Doc/Zsh/builtins.yo, diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c index 02f0562fc..5ce604c63 100644 --- a/Src/Modules/watch.c +++ b/Src/Modules/watch.c @@ -640,8 +640,8 @@ static struct builtin bintab[] = { }; static struct paramdef partab[] = { - PARAMDEF("WATCH", PM_TIED|PM_SCALAR|PM_SPECIAL, &watch, &colonarr_gsu), - PARAMDEF("watch", PM_TIED|PM_ARRAY|PM_SPECIAL, &watch, &vararray_gsu), + PARAMDEF("WATCH", PM_SCALAR|PM_SPECIAL, &watch, &colonarr_gsu), + PARAMDEF("watch", PM_ARRAY|PM_SPECIAL, &watch, &vararray_gsu), }; static struct features module_features = { @@ -679,12 +679,16 @@ int boot_(UNUSED(Module m)) { static char const * const default_watchfmt = DEFAULT_WATCHFMT; - Param pm; - if ((pm = (Param) paramtab->getnode(paramtab, "watch"))) - pm->ename = "WATCH"; - if ((pm = (Param) paramtab->getnode(paramtab, "WATCH"))) - pm->ename = "watch"; + Param pma = (Param) paramtab->getnode(paramtab, "watch"); + Param pms = (Param) paramtab->getnode(paramtab, "WATCH"); + if (pma && pms && pma->u.arr == watch && pms->u.arr == watch) { + /* only tie the two parameters if both were added */ + pma->ename = "WATCH"; + pms->ename = "watch"; + pma->node.flags |= PM_TIED; + pms->node.flags |= PM_TIED; + } watch = mkarray(NULL); /* These two parameters are only set to defaults if not set. -- cgit v1.2.3 From dfb7ac94bb4c28472d759a61ea3c2dab30cf9cd3 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Fri, 12 Nov 2021 20:33:52 +0100 Subject: 49561: add zformat -F option, similar to -f but ternary expressions check for existence instead of doing math evaluation --- ChangeLog | 8 +++++++ Completion/Base/Core/_description | 2 +- Completion/Base/Core/_message | 2 +- Doc/Zsh/mod_zutil.yo | 11 +++++++-- Src/Modules/zutil.c | 48 ++++++++++++++++++++++++++------------- Test/V13zformat.ztst | 24 ++++++++++++++++++++ 6 files changed, 75 insertions(+), 20 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 5865cb727..65c0dfa8f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2021-11-12 Oliver Kiddle + + * 49561: Src/Modules/zutil.c, Doc/Zsh/mod_zutil.yo, + Completion/Base/Core/_description, Completion/Base/Core/_message, + Test/V13zformat.ztst: Add zformat -F option, similar to -f but + ternary expressions check for existence instead of doing math + evaluation. Make use it with the format style. + 2021-11-07 Oliver Kiddle * 49544: Src/Modules/watch.c: only tie watch/WATCH if both come diff --git a/Completion/Base/Core/_description b/Completion/Base/Core/_description index bdb4007a6..5b54484c7 100644 --- a/Completion/Base/Core/_description +++ b/Completion/Base/Core/_description @@ -78,7 +78,7 @@ shift 2 if [[ -z "$1" && $# -eq 1 ]]; then format= elif [[ -n "$format" ]]; then - zformat -f format "$format" "d:$1" "${(@)argv[2,-1]}" + zformat -F format "$format" "d:$1" "${(@)argv[2,-1]}" fi if [[ -n "$gname" ]]; then diff --git a/Completion/Base/Core/_message b/Completion/Base/Core/_message index 4d5645eaf..dbeed4a88 100644 --- a/Completion/Base/Core/_message +++ b/Completion/Base/Core/_message @@ -39,7 +39,7 @@ else fi if [[ -n "$format$raw" ]]; then - [[ -z "$raw" ]] && zformat -f format "$format" "d:$1" "${(@)argv[2,-1]}" + [[ -z "$raw" ]] && zformat -F format "$format" "d:$1" "${(@)argv[2,-1]}" builtin compadd "$gopt[@]" -x "$format" _comp_mesg=yes fi diff --git a/Doc/Zsh/mod_zutil.yo b/Doc/Zsh/mod_zutil.yo index 41d3dfdb0..3cf9e5028 100644 --- a/Doc/Zsh/mod_zutil.yo +++ b/Doc/Zsh/mod_zutil.yo @@ -150,8 +150,9 @@ enditem() ) findex(zformat) xitem(tt(zformat -f) var(param) var(format) var(spec) ...) +xitem(tt(zformat -F) var(param) var(format) var(spec) ...) item(tt(zformat -a) var(array) var(sep) var(spec) ...)( -This builtin provides two different forms of formatting. The first form +This builtin provides different forms of formatting. The first form is selected with the tt(-f) option. In this case the var(format) string will be modified by replacing sequences starting with a percent sign in it with strings from the var(spec)s. Each var(spec) should be @@ -195,7 +196,13 @@ outputs "The answer is 'yes'." to tt(REPLY) since the value for the format specifier tt(c) is 3, agreeing with the digit argument to the ternary expression. -The second form, using the tt(-a) option, can be used for aligning +With tt(-F) instead of tt(-f), ternary expressions choose between the +`true' or `false' text on the basis of whether the format specifier is +present and non-empty. A test number indicates a minimum width for the +value given in the format specifier. Negative numbers reverse this, +so the test is for whether the value exceeds a maximum width. + +The form, using the tt(-a) option, can be used for aligning strings. Here, the var(spec)s are of the form `var(left)tt(:)var(right)' where `var(left)' and `var(right)' are arbitrary strings. These strings are modified by replacing the colons diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c index 691ba6c2f..2f17c03f1 100644 --- a/Src/Modules/zutil.c +++ b/Src/Modules/zutil.c @@ -776,10 +776,12 @@ bin_zstyle(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) * ousedp (*outp)[*ousedp] is where to write next * olenp *olenp is the size allocated for *outp * endchar Terminator character in addition to `\0' (may be '\0') + * presence -F: Ternary expressions test emptyness instead * skip If 1, don't output, just parse. */ static char *zformat_substring(char* instr, char **specs, char **outp, - int *ousedp, int *olenp, int endchar, int skip) + int *ousedp, int *olenp, int endchar, + int presence, int skip) { char *s; @@ -813,20 +815,29 @@ static char *zformat_substring(char* instr, char **specs, char **outp, if (testit && STOUC(*s)) { int actval, testval, endcharl; - /* - * One one number is useful for ternary expressions. - * Remember to put the sign back. - */ + /* Only one number is useful for ternary expressions. */ testval = (min >= 0) ? min : (max >= 0) ? max : 0; - if (right) - testval *= -1; - if (specs[STOUC(*s)]) - actval = (int)mathevali(specs[STOUC(*s)]); - else - actval = 0; - /* zero means values are equal, i.e. true */ - actval -= testval; + if (specs[STOUC(*s)] && *specs[STOUC(*s)]) { + if (presence) { + if (testval) +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) + actval = MB_METASTRWIDTH(specs[STOUC(*s)]); + else +#endif + actval = strlen(specs[STOUC(*s)]); + else + actval = 1; + actval = right ? (testval < actval) : (testval >= actval); + } else { + if (right) /* put the sign back */ + testval *= -1; + /* zero means values are equal, i.e. true */ + actval = (int)mathevali(specs[STOUC(*s)]) - testval; + } + } else + actval = presence ? !right : testval; /* careful about premature end of string */ if (!(endcharl = *++s)) @@ -837,10 +848,10 @@ static char *zformat_substring(char* instr, char **specs, char **outp, * vice versa... unless we are already skipping. */ if (!(s = zformat_substring(s+1, specs, outp, ousedp, - olenp, endcharl, skip || actval))) + olenp, endcharl, presence, skip || actval))) return NULL; if (!(s = zformat_substring(s+1, specs, outp, ousedp, - olenp, ')', skip || !actval))) + olenp, ')', presence, skip || !actval))) return NULL; } else if (skip) { continue; @@ -912,6 +923,7 @@ static int bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { char opt; + int presence = 0; if (args[0][0] != '-' || !(opt = args[0][1]) || args[0][2]) { zwarnnam(nam, "invalid argument: %s", args[0]); @@ -920,6 +932,9 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) args++; switch (opt) { + case 'F': + presence = 1; + /* fall-through */ case 'f': { char **ap, *specs[256] = {0}, *out; @@ -939,7 +954,8 @@ bin_zformat(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) } out = (char *) zhalloc(olen = 128); - zformat_substring(args[1], specs, &out, &oused, &olen, '\0', 0); + zformat_substring(args[1], specs, &out, &oused, &olen, '\0', + presence, 0); out[oused] = '\0'; setsparam(args[0], ztrdup(out)); diff --git a/Test/V13zformat.ztst b/Test/V13zformat.ztst index 982866e13..035a0a495 100644 --- a/Test/V13zformat.ztst +++ b/Test/V13zformat.ztst @@ -58,6 +58,30 @@ 0:nested conditionals test >yes + () { + zformat -f 1 '%(w.zero.fail) %(x.fail.present) %(y.empty.fail) %(z.missing.fail)' w:0 x:1 y: + zformat -F 2 '%(w.zero.fail) %(x.present.fail) %(y.fail.empty) %(z.fail.missing)' w:0 x:1 y: + echo $1 + echo $2 + } +0:conditionals with empty and missing values +>zero present empty missing +>zero present empty missing + + () { + local l + for l in 0 1 2 3; do + zformat -F 1 "%$l(a.a.A)%$l(b.b.B)%$l(c.c.C)%$l(d.d.D)" a: b:1 c:12 d:123 + zformat -F 2 "%-$l(a.a.A)%-$l(b.b.B)%-$l(c.c.C)%-$l(d.d.D)" a: b:1 c:12 d:123 + print - $1 $2 + done + } +0:minimum and maximum widths +>Abcd aBCD +>ABcd abCD +>ABCd abcD +>ABCD abcd + zformat -a argv . foo:lorem ipsum:bar bazbaz '\\esc\:ape' print -rl -- "$@" 0:basic -a test -- cgit v1.2.3 From a361b88717026c4af6d8d2d208ac4ac8b530f037 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Mon, 15 Nov 2021 10:00:12 +0900 Subject: 49563: fix build problem of watch module on Cygwin --- ChangeLog | 5 +++++ Src/Modules/watch.c | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 1dfe2e39a..db166f7ee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2021-11-15 Jun-ichi Takimoto + + * 49563: Src/Modules/watch.c: fix build problem of watch module + on Cygwin + 2021-11-12 Oliver Kiddle * github #82: Dimitris Apostolou: Completion/BSD/Command/_kdump, diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c index 5ce604c63..95d591a67 100644 --- a/Src/Modules/watch.c +++ b/Src/Modules/watch.c @@ -640,8 +640,8 @@ static struct builtin bintab[] = { }; static struct paramdef partab[] = { - PARAMDEF("WATCH", PM_SCALAR|PM_SPECIAL, &watch, &colonarr_gsu), - PARAMDEF("watch", PM_ARRAY|PM_SPECIAL, &watch, &vararray_gsu), + PARAMDEF("WATCH", PM_SCALAR|PM_SPECIAL, &watch, NULL), + PARAMDEF("watch", PM_ARRAY|PM_SPECIAL, &watch, NULL), }; static struct features module_features = { @@ -656,6 +656,10 @@ static struct features module_features = { int setup_(UNUSED(Module m)) { + /* On Cygwin, colonarr_gsu exists in libzsh.dll and we can't + * use &colonarr_gsu in the initialization of partab[] above */ + partab[0].gsu = (void *)&colonarr_gsu; + partab[1].gsu = (void *)&vararray_gsu; return 0; } -- cgit v1.2.3 From 91b7baf25929a20ee776100f406021a422d56e98 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Mon, 13 Dec 2021 21:06:57 +0100 Subject: 49646: allow colors in WATCHFMT with %F/%K --- ChangeLog | 3 +++ Doc/Zsh/mod_watch.yo | 6 ++++++ Src/Modules/watch.c | 35 +++++++++++++++++++++++++++++++++++ Src/prompt.c | 9 +++++---- 4 files changed, 49 insertions(+), 4 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index cb5d4564e..2fd7926ab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2021-12-13 Oliver Kiddle + * 49646: Doc/Zsh/mod_watch.yo, Src/Modules/watch.c, Src/prompt.c: + allow colors in WATCHFMT with %F/%K + * 49645: Completion/Unix/Type/_path_commands: when completing for the path_dirs option, add a / suffix and follow symlinks diff --git a/Doc/Zsh/mod_watch.yo b/Doc/Zsh/mod_watch.yo index 4eea89e23..d97a41d13 100644 --- a/Doc/Zsh/mod_watch.yo +++ b/Doc/Zsh/mod_watch.yo @@ -65,6 +65,12 @@ The `tt(%m)' and `tt(%M)' escapes will work only if there is a host name field in the utmp on your machine. Otherwise they are treated as ordinary strings. ) +item(tt(%F{)var(color)tt(}) LPAR()tt(%f)RPAR())( +Start (stop) using a different foreground color. +) +item(tt(%K{)var(color)tt(}) LPAR()tt(%k)RPAR())( +Start (stop) using a different background color. +) item(tt(%S) LPAR()tt(%s)RPAR())( Start (stop) standout mode. ) diff --git a/Src/Modules/watch.c b/Src/Modules/watch.c index 95d591a67..d45c3cf3d 100644 --- a/Src/Modules/watch.c +++ b/Src/Modules/watch.c @@ -246,6 +246,7 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) struct tm *tm; char *fm2; int len; + zattr atr; # ifdef WATCH_UTMP_UT_HOST char *p; int i; @@ -347,6 +348,40 @@ watchlog2(int inout, WATCH_STRUCT_UTMP *u, char *fmt, int prnt, int fini) case '%': putchar('%'); break; + case 'F': + if (*fmt == '{') { + fmt++; + 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); + } + } + break; + case 'f': + txtunset(TXT_ATTR_FG_ON_MASK); + set_colour_attribute(TXTNOFGCOLOUR, COL_SEQ_FG, TSC_RAW); + break; + case 'K': + if (*fmt == '{') { + fmt++; + 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); + } + } + break; + case 'k': + txtunset(TXT_ATTR_BG_ON_MASK); + set_colour_attribute(TXTNOBGCOLOUR, COL_SEQ_BG, TSC_RAW); + break; case 'S': txtset(TXTSTANDOUT); tsetcap(TCSTANDOUTBEG, TSC_RAW); diff --git a/Src/prompt.c b/Src/prompt.c index 6943eabc3..d6b378539 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -1045,9 +1045,9 @@ tsetcap(int cap, int flags) if (txtisset(TXTUNDERLINE)) tsetcap(TCUNDERLINEBEG, flags); if (txtisset(TXTFGCOLOUR)) - set_colour_attribute(txtattrmask, COL_SEQ_FG, TSC_PROMPT); + set_colour_attribute(txtattrmask, COL_SEQ_FG, flags); if (txtisset(TXTBGCOLOUR)) - set_colour_attribute(txtattrmask, COL_SEQ_BG, TSC_PROMPT); + set_colour_attribute(txtattrmask, COL_SEQ_BG, flags); } } } @@ -2062,7 +2062,8 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) *bv->bp++ = Outpar; } } else { - tputs(tgoto(tcstr[tc], colour, colour), 1, putshout); + tputs(tgoto(tcstr[tc], colour, colour), 1, + (flags & TSC_RAW) ? putraw : putshout); } /* That worked. */ return; @@ -2121,7 +2122,7 @@ set_colour_attribute(zattr atr, int fg_bg, int flags) *bv->bp++ = Outpar; } } else - tputs(colseq_buf, 1, putshout); + tputs(colseq_buf, 1, (flags & TSC_RAW) ? putraw : putshout); if (do_free) free_colour_buffer(); -- cgit v1.2.3 From 45182eb4d447a6fbf5f76538326e1a944880ea52 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Tue, 22 Feb 2022 13:09:33 +0000 Subject: unposted: Fix some typos and markup. --- ChangeLog | 5 +++++ Doc/Zsh/contrib.yo | 4 ++-- Src/Modules/parameter.c | 2 +- Src/string.c | 5 ++++- Src/zsh.h | 2 +- 5 files changed, 13 insertions(+), 5 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 401ec9379..5dc07937f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-02-22 Daniel Shahaf + + * unposted: Doc/Zsh/contrib.yo, Src/Modules/parameter.c, + Src/string.c, Src/zsh.h: Fix some typos and markup. + 2022-02-22 Peter Stephenson * 49672: Doc/Zsh/expn.yo: document "tied" output in parameter diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index 0adef7334..0ef59dbc9 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -1159,7 +1159,7 @@ This is used by the Perforce backend (tt(p4)) to decide if it should contact the Perforce server to find out if a directory is managed by Perforce. This is the only reliable way of doing this, but runs the risk of a delay if the server name cannot be found. If the -server (more specifically, the tt(host)tt(:)tt(port) pair describing the +server (more specifically, the var(host)tt(:)var(port) pair describing the server) cannot be contacted, its name is put into the associative array tt(vcs_info_p4_dead_servers) and is not contacted again during the session until it is removed by hand. If you do not set this style, the tt(p4) @@ -1318,7 +1318,7 @@ tt(%Q) expando. item(tt(%Q))( Quilt series information. When quilt is used (either in `addon' mode or as a `standalone' backend), -this expando is set to quilt series' tt(patch-format) string. +this expando is set to the quilt series' tt(patch-format) string. The tt(set-patch-format) hook and tt(nopatch-format) style are honoured. See ifzman(tt(Quilt Support))ifnzman(noderef(vcs_info Quilt Support)) below for details. diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index b44555323..c5cb5ce62 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -592,7 +592,7 @@ getpmfunction_source(HashTable ht, const char *name) return getfunction_source(ht, name, 0); } -/* Param table entry for retrieving ds_functions_source element */ +/* Param table entry for retrieving dis_functions_source element */ /**/ static HashNode diff --git a/Src/string.c b/Src/string.c index 9e14ef949..5f439926e 100644 --- a/Src/string.c +++ b/Src/string.c @@ -103,7 +103,10 @@ wcs_ztrdup(const wchar_t *s) #endif /* MULTIBYTE_SUPPORT */ -/* concatenate s1, s2, and s3 in dynamically allocated buffer */ +/* Concatenate s1, s2, and s3 into dynamically allocated buffer. + * + * To concatenate four or more strings, see zjoin(). + */ /**/ mod_export char * diff --git a/Src/zsh.h b/Src/zsh.h index af9b4fb67..641d9c95c 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1448,7 +1448,7 @@ struct builtin { int minargs; /* minimum number of arguments */ int maxargs; /* maximum number of arguments, or -1 for no limit */ int funcid; /* xbins (see above) for overloaded handlerfuncs */ - char *optstr; /* string of legal options */ + char *optstr; /* string of legal options (see execbuiltin()) */ char *defopts; /* options set by default for overloaded handlerfuncs */ }; -- cgit v1.2.3 From 6a8aa2aa5a07c46a6b6d5c481a18a12e7ab6047a Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Tue, 1 Mar 2022 17:27:42 +0000 Subject: 49783: Consistently use old job table in parameter module --- ChangeLog | 3 ++ Src/Modules/parameter.c | 96 +++++++++++++++++++++++++++++-------------------- Src/jobs.c | 34 +++++++++++++----- 3 files changed, 85 insertions(+), 48 deletions(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 6371cb85d..5635e5068 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-03-01 Peter Stephenson + * 49783: Src/jobs.c, Src/Modules/parameter.c: Consistently use + old job table in parmaeter module. + * users/27536: Doc/Zsh/builtins.yo: Clarify interaction of history matching and numeric indices. diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c index c5cb5ce62..dbb61e474 100644 --- a/Src/Modules/parameter.c +++ b/Src/Modules/parameter.c @@ -1244,19 +1244,19 @@ histwgetfn(UNUSED(Param pm)) /**/ static char * -pmjobtext(int job) +pmjobtext(Job jtab, int job) { Process pn; int len = 1; char *ret; - for (pn = jobtab[job].procs; pn; pn = pn->next) + for (pn = jtab[job].procs; pn; pn = pn->next) len += strlen(pn->text) + 3; ret = (char *) zhalloc(len); ret[0] = '\0'; - for (pn = jobtab[job].procs; pn; pn = pn->next) { + for (pn = jtab[job].procs; pn; pn = pn->next) { strcat(ret, pn->text); if (pn->next) strcat(ret, " | "); @@ -1269,22 +1269,25 @@ static HashNode getpmjobtext(UNUSED(HashTable ht), const char *name) { Param pm = NULL; - int job; + int job, jmax; char *pend; + Job jtab; pm = (Param) hcalloc(sizeof(struct param)); pm->node.nam = dupstring(name); pm->node.flags = PM_SCALAR | PM_READONLY; pm->gsu.s = &nullsetscalar_gsu; + selectjobtab(&jtab, &jmax); + job = strtod(name, &pend); /* Non-numeric keys are looked up by job name */ if (*pend) job = getjob(name, NULL); - if (job >= 1 && job <= maxjob && - jobtab[job].stat && jobtab[job].procs && - !(jobtab[job].stat & STAT_NOPRINT)) - pm->u.str = pmjobtext(job); + if (job >= 1 && job <= jmax && + jtab[job].stat && jtab[job].procs && + !(jtab[job].stat & STAT_NOPRINT)) + pm->u.str = pmjobtext(jtab, job); else { pm->u.str = dupstring(""); pm->node.flags |= (PM_UNSET|PM_SPECIAL); @@ -1297,22 +1300,25 @@ static void scanpmjobtexts(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; - int job; + int job, jmax; char buf[40]; + Job jtab; memset((void *)&pm, 0, sizeof(struct param)); pm.node.flags = PM_SCALAR | PM_READONLY; pm.gsu.s = &nullsetscalar_gsu; - for (job = 1; job <= maxjob; job++) { - if (jobtab[job].stat && jobtab[job].procs && - !(jobtab[job].stat & STAT_NOPRINT)) { + selectjobtab(&jtab, &jmax); + + for (job = 1; job <= jmax; job++) { + if (jtab[job].stat && jtab[job].procs && + !(jtab[job].stat & STAT_NOPRINT)) { if (func != scancountparams) { sprintf(buf, "%d", job); pm.node.nam = dupstring(buf); if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || !(flags & SCANPM_WANTKEYS)) - pm.u.str = pmjobtext(job); + pm.u.str = pmjobtext(jtab, job); } func(&pm.node, flags); } @@ -1323,7 +1329,7 @@ scanpmjobtexts(UNUSED(HashTable ht), ScanFunc func, int flags) /**/ static char * -pmjobstate(int job) +pmjobstate(Job jtab, int job) { Process pn; char buf[256], buf2[128], *ret, *state, *cp; @@ -1335,14 +1341,14 @@ pmjobstate(int job) else cp = ":"; - if (jobtab[job].stat & STAT_DONE) + if (jtab[job].stat & STAT_DONE) ret = dyncat("done", cp); - else if (jobtab[job].stat & STAT_STOPPED) + else if (jtab[job].stat & STAT_STOPPED) ret = dyncat("suspended", cp); else ret = dyncat("running", cp); - for (pn = jobtab[job].procs; pn; pn = pn->next) { + for (pn = jtab[job].procs; pn; pn = pn->next) { if (pn->status == SP_RUNNING) state = "running"; @@ -1371,21 +1377,24 @@ static HashNode getpmjobstate(UNUSED(HashTable ht), const char *name) { Param pm = NULL; - int job; + int job, jmax; char *pend; + Job jtab; pm = (Param) hcalloc(sizeof(struct param)); pm->node.nam = dupstring(name); pm->node.flags = PM_SCALAR | PM_READONLY; pm->gsu.s = &nullsetscalar_gsu; + selectjobtab(&jtab, &jmax); + job = strtod(name, &pend); if (*pend) job = getjob(name, NULL); - if (job >= 1 && job <= maxjob && - jobtab[job].stat && jobtab[job].procs && - !(jobtab[job].stat & STAT_NOPRINT)) - pm->u.str = pmjobstate(job); + if (job >= 1 && job <= jmax && + jtab[job].stat && jtab[job].procs && + !(jtab[job].stat & STAT_NOPRINT)) + pm->u.str = pmjobstate(jtab, job); else { pm->u.str = dupstring(""); pm->node.flags |= (PM_UNSET|PM_SPECIAL); @@ -1398,22 +1407,25 @@ static void scanpmjobstates(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; - int job; + int job, jmax; + Job jtab; char buf[40]; + selectjobtab(&jtab, &jmax); + memset((void *)&pm, 0, sizeof(struct param)); pm.node.flags = PM_SCALAR | PM_READONLY; pm.gsu.s = &nullsetscalar_gsu; - for (job = 1; job <= maxjob; job++) { - if (jobtab[job].stat && jobtab[job].procs && - !(jobtab[job].stat & STAT_NOPRINT)) { + for (job = 1; job <= jmax; job++) { + if (jtab[job].stat && jtab[job].procs && + !(jtab[job].stat & STAT_NOPRINT)) { if (func != scancountparams) { sprintf(buf, "%d", job); pm.node.nam = dupstring(buf); if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || !(flags & SCANPM_WANTKEYS)) - pm.u.str = pmjobstate(job); + pm.u.str = pmjobstate(jtab, job); } func(&pm.node, flags); } @@ -1424,11 +1436,11 @@ scanpmjobstates(UNUSED(HashTable ht), ScanFunc func, int flags) /**/ static char * -pmjobdir(int job) +pmjobdir(Job jtab, int job) { char *ret; - ret = dupstring(jobtab[job].pwd ? jobtab[job].pwd : pwd); + ret = dupstring(jtab[job].pwd ? jtab[job].pwd : pwd); return ret; } @@ -1437,21 +1449,24 @@ static HashNode getpmjobdir(UNUSED(HashTable ht), const char *name) { Param pm = NULL; - int job; + int job, jmax; char *pend; + Job jtab; pm = (Param) hcalloc(sizeof(struct param)); pm->node.nam = dupstring(name); pm->node.flags = PM_SCALAR | PM_READONLY; pm->gsu.s = &nullsetscalar_gsu; + selectjobtab(&jtab, &jmax); + job = strtod(name, &pend); if (*pend) job = getjob(name, NULL); - if (job >= 1 && job <= maxjob && - jobtab[job].stat && jobtab[job].procs && - !(jobtab[job].stat & STAT_NOPRINT)) - pm->u.str = pmjobdir(job); + if (job >= 1 && job <= jmax && + jtab[job].stat && jtab[job].procs && + !(jtab[job].stat & STAT_NOPRINT)) + pm->u.str = pmjobdir(jtab, job); else { pm->u.str = dupstring(""); pm->node.flags |= (PM_UNSET|PM_SPECIAL); @@ -1464,22 +1479,25 @@ static void scanpmjobdirs(UNUSED(HashTable ht), ScanFunc func, int flags) { struct param pm; - int job; + int job, jmax; char buf[40]; + Job jtab; memset((void *)&pm, 0, sizeof(struct param)); pm.node.flags = PM_SCALAR | PM_READONLY; pm.gsu.s = &nullsetscalar_gsu; - for (job = 1; job <= maxjob; job++) { - if (jobtab[job].stat && jobtab[job].procs && - !(jobtab[job].stat & STAT_NOPRINT)) { + selectjobtab(&jtab, &jmax); + + for (job = 1; job <= jmax; job++) { + if (jtab[job].stat && jtab[job].procs && + !(jtab[job].stat & STAT_NOPRINT)) { if (func != scancountparams) { sprintf(buf, "%d", job); pm.node.nam = dupstring(buf); if ((flags & (SCANPM_WANTVALS|SCANPM_MATCHVAL)) || !(flags & SCANPM_WANTKEYS)) - pm.u.str = pmjobdir(job); + pm.u.str = pmjobdir(jtab, job); } func(&pm.node, flags); } diff --git a/Src/jobs.c b/Src/jobs.c index f0b337110..18e43f03c 100644 --- a/Src/jobs.c +++ b/Src/jobs.c @@ -98,10 +98,12 @@ mod_export int jobtabsize; mod_export int maxjob; /* If we have entered a subshell, the original shell's job table. */ -static struct job *oldjobtab; +/**/ +mod_export struct job *oldjobtab; /* The size of that. */ -static int oldmaxjob; +/**/ +mod_export int oldmaxjob; /* shell timings */ @@ -1894,6 +1896,26 @@ setcurjob(void) } } +/* Find the job table for reporting jobs */ + +/**/ +mod_export void +selectjobtab(Job *jtabp, int *jmaxp) +{ + if (oldjobtab) + { + /* In subshell --- use saved job table to report */ + *jtabp = oldjobtab; + *jmaxp = oldmaxjob; + } + else + { + /* Use main job table */ + *jtabp = jobtab; + *jmaxp = maxjob; + } +} + /* Convert a job specifier ("%%", "%1", "%foo", "%?bar?", etc.) * * to a job number. */ @@ -1904,13 +1926,7 @@ getjob(const char *s, const char *prog) int jobnum, returnval, mymaxjob; Job myjobtab; - if (oldjobtab) { - myjobtab = oldjobtab; - mymaxjob = oldmaxjob; - } else { - myjobtab= jobtab; - mymaxjob = maxjob; - } + selectjobtab(&myjobtab, &mymaxjob); /* if there is no %, treat as a name */ if (*s != '%') -- cgit v1.2.3 From 95749e9e652849215b5d09c5aaf8928056c41688 Mon Sep 17 00:00:00 2001 From: Matthew Martin Date: Thu, 31 Mar 2022 17:40:22 -0500 Subject: 49933: Add nonblock to sysopen --- ChangeLog | 3 +++ Doc/Zsh/mod_system.yo | 3 +++ Src/Modules/system.c | 5 ++++- 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'Src/Modules') diff --git a/ChangeLog b/ChangeLog index 7e97c79f0..a4f74a581 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-03-31 Matthew Martin + * 49933: Doc/Zsh/mod_system.yo, Src/Modules/system.c: Add + nonblock to sysopen. + * 49932: Completion/Zsh/Context/_brace_parameter: Update _brace_parameter # description. diff --git a/Doc/Zsh/mod_system.yo b/Doc/Zsh/mod_system.yo index 399b6fe03..884c3e753 100644 --- a/Doc/Zsh/mod_system.yo +++ b/Doc/Zsh/mod_system.yo @@ -62,6 +62,9 @@ suppress updating of the file atime item(tt(nofollow))( fail if var(file) is a symbolic link ) +item(tt(nonblock))( +the file is opened in nonblocking mode +) item(tt(sync))( request that writes wait until data has been physically written ) diff --git a/Src/Modules/system.c b/Src/Modules/system.c index ecd4e2546..71745548f 100644 --- a/Src/Modules/system.c +++ b/Src/Modules/system.c @@ -280,7 +280,7 @@ bin_syswrite(char *nam, char **args, Options ops, UNUSED(int func)) } -static struct { char *name; int oflag; } openopts[] = { +static struct { const char *name; int oflag; } openopts[] = { #ifdef O_CLOEXEC { "cloexec", O_CLOEXEC }, #else @@ -296,6 +296,9 @@ static struct { char *name; int oflag; } openopts[] = { #endif #ifdef O_NOATIME { "noatime", O_NOATIME }, +#endif +#ifdef O_NONBLOCK + { "nonblock", O_NONBLOCK}, #endif { "excl", O_EXCL | O_CREAT }, { "creat", O_CREAT }, -- cgit v1.2.3