From a6a1b28b9807c8c19d5fef424a86b86ddf33bc7f Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 24 Feb 2020 10:55:48 +0000 Subject: 45487: Missing mod_export declarations for AIX --- Src/utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Src/utils.c') diff --git a/Src/utils.c b/Src/utils.c index f5667f389..f9c2d4a2b 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1023,7 +1023,7 @@ xsymlinks(char *s, int full) */ /**/ -char * +mod_export char * xsymlink(char *s, int heap) { if (*s != '/') -- cgit v1.2.3 From 0bc1edef5a1297a2389a480ea0436d2d9ff0392c Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Tue, 17 Mar 2020 15:04:12 +0900 Subject: 45536: fix handling of 8bit character under C-locale correctly escape unprintable 8bit character by using \M- --- ChangeLog | 5 +++++ Src/utils.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index cc270ddc0..0d95307de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2020-03-17 Jun-ichi Takimoto + + * 45536: Src/utils.c: fix handling of 8bit character in + is_wcs_nicechar() and wcs_nicechar_sel() under C-locale. + 2020-03-15 Daniel Shahaf * 45541: Functions/VCS_Info/test-repo-git-rebase-apply: internal: diff --git a/Src/utils.c b/Src/utils.c index f9c2d4a2b..339404489 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -711,7 +711,7 @@ wcs_nicechar_sel(wchar_t c, size_t *widthp, char **swidep, int quotable) if (widthp) *widthp = 6; } else { - strcpy(buf, nicechar((int)c)); + strcpy(buf, nicechar_sel((int)c, quotable)); /* * There may be metafied characters from nicechar(), * so compute width and end position independently. @@ -771,7 +771,7 @@ mod_export int is_wcs_nicechar(wchar_t c) if (c == 0x7f || c == L'\n' || c == L'\t' || c < 0x20) return 1; if (c >= 0x80) { - return (c >= 0x100); + return (c >= 0x100 || is_nicechar((int)c)); } } return 0; -- cgit v1.2.3 From 2b1e5a3d22ed4b66af3926e2034e2e8f16acc830 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Sun, 22 Mar 2020 18:45:02 +0000 Subject: 45563: %z can be used for zlong in warnings and errors --- ChangeLog | 4 ++++ Src/utils.c | 9 +++++++++ 2 files changed, 13 insertions(+) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index f1dadc9d0..af51fc5a8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2020-03-22 Peter Stephenson + + * 45563: Src/utils.c: %z supplies zlong for errors and warnings. + 2020-03-22 Daniel Shahaf * 45583/0009: README, Src/parse.c, Test/E02xtrace.ztst: Add diff --git a/Src/utils.c b/Src/utils.c index 339404489..4d16de591 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -130,6 +130,7 @@ set_widearray(char *mb_array, Widechar_array wca) %l const char *, int C string of given length (null not required) %L long decimal value %d int decimal value + %z zlong decimal value %% (none) literal '%' %c int character at that codepoint %e int strerror() message (argument is typically 'errno') @@ -331,6 +332,14 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) num = va_arg(ap, int); fprintf(file, "%d", num); break; + case 'z': + { + zlong znum = va_arg(ap, zlong); + char buf[DIGBUFSIZE]; + convbase(buf, znum, 10); + fputs(buf, file); + break; + } case '%': putc('%', file); break; -- cgit v1.2.3 From 47fc5fe01fa315c38335739beb6102e103a80702 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Sat, 21 Mar 2020 18:45:35 +0000 Subject: 45583/0004: Fix segfault on resolving symlink loops --- ChangeLog | 3 +++ Etc/BUGS | 3 ++- Src/utils.c | 6 +++--- Test/D02glob.ztst | 11 +++++------ 4 files changed, 13 insertions(+), 10 deletions(-) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index c6478935c..3083552c3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2020-03-25 Daniel Shahaf + * 45583/0004: Etc/BUGS, Src/utils.c, Test/D02glob.ztst: Fix + segfault on resolving symlink loops + * 45583/0003: Src/hist.c, Src/subst.c: chrealpath: Let caller decide how the return value should be allocated. diff --git a/Etc/BUGS b/Etc/BUGS index 99a0d9753..2501d59a7 100644 --- a/Etc/BUGS +++ b/Etc/BUGS @@ -29,5 +29,6 @@ skipped when STTY=... is set for that command 44007 - Martijn - exit in trap executes rest of function See test case in Test/C03traps.ztst. ------------------------------------------------------------------------ -45282: ${${:-foo}:P} where foo is a symlink that points to itself segfaults +45282: xsymlinks() segfaults on symlink loops +Fixed for some cases; need to audit remaining callers ------------------------------------------------------------------------ diff --git a/Src/utils.c b/Src/utils.c index 4d16de591..b0f6820fb 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1038,11 +1038,11 @@ xsymlink(char *s, int heap) if (*s != '/') return NULL; *xbuf = '\0'; - if (xsymlinks(s + 1, 1) < 0) + if (!chrealpath(&s, 'P', heap)) { zwarn("path expansion failed, using root directory"); - if (!*xbuf) return heap ? dupstring("/") : ztrdup("/"); - return heap ? dupstring(xbuf) : ztrdup(xbuf); + } + return s; } /**/ diff --git a/Test/D02glob.ztst b/Test/D02glob.ztst index 248cc7ff5..041784310 100644 --- a/Test/D02glob.ztst +++ b/Test/D02glob.ztst @@ -690,10 +690,9 @@ # This is a bit brittle as it depends on PATH_MAX. # We could use sysconf.. bad_pwd="/${(l:16000:: :):-}" - print ${bad_pwd:P} + print ${bad_pwd:P} | wc -c 0:modifier ':P' with path too long -?(eval):4: path expansion failed, using root directory ->/ +>16002 foo=a value="ac" @@ -765,7 +764,7 @@ } always { rm -f glob.tmp/trap glob.tmp/loop } --f:the ':P' modifier handles symlink loops in the last path component +0:the ':P' modifier handles symlink loops in the last path component *>*/(trap|loop) *>*/(trap|loop) @@ -777,7 +776,7 @@ } always { rm -f glob.tmp/trap glob.tmp/loop } --f:the ':P' modifier handles symlink loops before the last path component +0:the ':P' modifier handles symlink loops before the last path component *>*/glob.tmp/loop/trailing/components *>*/glob.tmp/(loop|trap)/trailing/components @@ -789,7 +788,7 @@ } always { rm -f glob.tmp/flip glob.tmp/flop } --f:the ':P' modifier handles symlink loops other than the trivial case +0:the ':P' modifier handles symlink loops other than the trivial case *>*/glob.tmp/(flip|flop) *>*/glob.tmp/(flip|flop)/trailing/components -- cgit v1.2.3 From 12347c26ce2041e9c06abb0a42d1fa759c4a0551 Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Sat, 21 Mar 2020 19:09:04 +0000 Subject: 45583/0006: Don't use xsymlinks() in 'whence -s'. --- ChangeLog | 2 ++ Src/utils.c | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index 58296c0df..d3d3e6ac3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2020-03-25 Daniel Shahaf + * 45583/0006: Src/utils.c: Don't use xsymlinks() in 'whence -s'. + * 45583/0005: Test/B13whence.ztst: Add a test for bin_whence's symlinks resolution. diff --git a/Src/utils.c b/Src/utils.c index b0f6820fb..df2ab11a6 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -919,7 +919,14 @@ slashsplit(char *s) return r; } -/* expands symlinks and .. or . expressions */ +/* expands symlinks and .. or . expressions + * + * Puts the result in the global "xbuf" + * + * If "full" is true, resolve one level of symlinks only. + * + * WARNING: This will segfault on symlink loops (thread: workers/45282) + */ /**/ static int @@ -1050,10 +1057,10 @@ void print_if_link(char *s, int all) { if (*s == '/') { - *xbuf = '\0'; if (all) { char *start = s + 1; char xbuflink[PATH_MAX+1]; + *xbuf = '\0'; for (;;) { if (xsymlinks(start, 0) > 0) { printf(" -> "); @@ -1068,8 +1075,11 @@ print_if_link(char *s, int all) } } } else { - if (xsymlinks(s + 1, 1) > 0) - printf(" -> "), zputs(*xbuf ? xbuf : "/", stdout); + if (chrealpath(&s, 'P', 0)) { + printf(" -> "); + zputs(*s ? s : "/", stdout); + zsfree(s); + } } } } -- cgit v1.2.3 From dabfd1f1fbab2b6543f541488c73bdc28230f43f Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Sat, 21 Mar 2020 19:12:55 +0000 Subject: 45583/0007: Remove code that is now unreachable. --- ChangeLog | 2 ++ Src/utils.c | 21 ++++----------------- 2 files changed, 6 insertions(+), 17 deletions(-) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index d3d3e6ac3..aa056bbd6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2020-03-25 Daniel Shahaf + * 45583/0007: Src/utils.c: Remove code that is now unreachable. + * 45583/0006: Src/utils.c: Don't use xsymlinks() in 'whence -s'. * 45583/0005: Test/B13whence.ztst: Add a test for bin_whence's diff --git a/Src/utils.c b/Src/utils.c index df2ab11a6..98dac6780 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -919,18 +919,16 @@ slashsplit(char *s) return r; } -/* expands symlinks and .. or . expressions +/* expands .. or . expressions and one level of symlinks * * Puts the result in the global "xbuf" * - * If "full" is true, resolve one level of symlinks only. - * * WARNING: This will segfault on symlink loops (thread: workers/45282) */ /**/ static int -xsymlinks(char *s, int full) +xsymlinks(char *s) { char **pp, **opp; char xbuf2[PATH_MAX*3+1], xbuf3[PATH_MAX*2+1]; @@ -979,7 +977,7 @@ xsymlinks(char *s, int full) } else { ret = 1; metafy(xbuf3, t0, META_NOALLOC); - if (!full) { + { /* * If only one expansion requested, ensure the * full path is in xbuf. @@ -1014,17 +1012,6 @@ xsymlinks(char *s, int full) */ break; } - if (*xbuf3 == '/') { - strcpy(xbuf, ""); - if (xsymlinks(xbuf3 + 1, 1) < 0) - ret = -1; - else - xbuflen = strlen(xbuf); - } else - if (xsymlinks(xbuf3, 1) < 0) - ret = -1; - else - xbuflen = strlen(xbuf); } } freearray(opp); @@ -1062,7 +1049,7 @@ print_if_link(char *s, int all) char xbuflink[PATH_MAX+1]; *xbuf = '\0'; for (;;) { - if (xsymlinks(start, 0) > 0) { + if (xsymlinks(start) > 0) { printf(" -> "); zputs(*xbuf ? xbuf : "/", stdout); if (!*xbuf) -- cgit v1.2.3 From fc286a7c11aa767cab203f1fe111133eac9056fa Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Sat, 21 Mar 2020 19:16:17 +0000 Subject: 45583/0008: Extend tests to prove that what remains of xsymlinks() handles symlink loops gracefully. --- ChangeLog | 4 ++++ Etc/BUGS | 3 --- Src/utils.c | 2 -- Test/B13whence.ztst | 9 +++++++++ 4 files changed, 13 insertions(+), 5 deletions(-) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index aa056bbd6..fea7b0f76 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2020-03-25 Daniel Shahaf + * 45583/0008: Etc/BUGS, Src/utils.c, Test/B13whence.ztst: + Extend tests to prove that what remains of xsymlinks() handles + symlink loops gracefully. + * 45583/0007: Src/utils.c: Remove code that is now unreachable. * 45583/0006: Src/utils.c: Don't use xsymlinks() in 'whence -s'. diff --git a/Etc/BUGS b/Etc/BUGS index 2501d59a7..8112299f5 100644 --- a/Etc/BUGS +++ b/Etc/BUGS @@ -29,6 +29,3 @@ skipped when STTY=... is set for that command 44007 - Martijn - exit in trap executes rest of function See test case in Test/C03traps.ztst. ------------------------------------------------------------------------ -45282: xsymlinks() segfaults on symlink loops -Fixed for some cases; need to audit remaining callers ------------------------------------------------------------------------- diff --git a/Src/utils.c b/Src/utils.c index 98dac6780..69885fed3 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -922,8 +922,6 @@ slashsplit(char *s) /* expands .. or . expressions and one level of symlinks * * Puts the result in the global "xbuf" - * - * WARNING: This will segfault on symlink loops (thread: workers/45282) */ /**/ diff --git a/Test/B13whence.ztst b/Test/B13whence.ztst index b22363980..ea0a4dae5 100644 --- a/Test/B13whence.ztst +++ b/Test/B13whence.ztst @@ -5,6 +5,9 @@ ln -s real step3 ln -s step3 step2 ln -s step2 step1 + ln -s loop loop + ln -s flip flop + ln -s flop flip touch real chmod +x real prefix=$PWD @@ -20,3 +23,9 @@ 0q:whence symlink resolution >$prefix/step1 -> $prefix/step2 -> $prefix/step3 -> $prefix/real >$prefix/step1 -> $prefix/real + + ( + path=( $PWD/whence.tmp $path ) + whence -S flip || whence -S loop || whence -s flip || whence -s loop + ) +1:whence deals with symlink loops gracefully -- 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/utils.c') 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 deca7c928520fba5a73383f1cac0b3ace8e0e45d Mon Sep 17 00:00:00 2001 From: Daniel Shahaf Date: Mon, 27 Apr 2020 19:30:40 +0000 Subject: 45730: _arguments: Add the -0 flag, which makes $opt_args be populated sanely. Also, write/extend docstrings for sepjoin() and zjoin(). --- ChangeLog | 5 +++++ Completion/Base/Utility/_arguments | 8 +++++--- Doc/Zsh/compsys.yo | 25 +++++++++++++++++++++--- Src/Zle/computil.c | 40 +++++++++++++++++++++++++++++++++----- Src/utils.c | 15 +++++++++++++- Test/Y03arguments.ztst | 8 +++++++- 6 files changed, 88 insertions(+), 13 deletions(-) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index 1942d9aab..1afa30114 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2020-05-03 Daniel Shahaf + * 45730: Completion/Base/Utility/_arguments, Doc/Zsh/compsys.yo, + Src/Zle/computil.c, Src/utils.c, Test/Y03arguments.ztst: + _arguments: Add the -0 flag, which makes $opt_args be populated + sanely. + * 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 diff --git a/Completion/Base/Utility/_arguments b/Completion/Base/Utility/_arguments index 136dd5826..3f1b39304 100644 --- a/Completion/Base/Utility/_arguments +++ b/Completion/Base/Utility/_arguments @@ -7,11 +7,13 @@ local long cmd="$words[1]" descr odescr mesg subopts opt opt2 usecc autod local oldcontext="$curcontext" hasopts rawret optarg singopt alwopt local setnormarg start rest local -a match mbegin mend +integer opt_args_use_NUL_separators=0 subopts=() singopt=() -while [[ "$1" = -([AMO]*|[CRSWnsw]) ]]; do +while [[ "$1" = -([AMO]*|[0CRSWnsw]) ]]; do case "$1" in + -0) opt_args_use_NUL_separators=1; shift ;; -C) usecc=yes; shift ;; -O) subopts=( "${(@P)2}" ); shift 2 ;; -O*) subopts=( "${(@P)${1[3,-1]}}" ); shift ;; @@ -388,7 +390,7 @@ if (( $# )) && comparguments -i "$autod" "$singopt[@]" "$@"; then if [[ "$action" = -\>* ]]; then action="${${action[3,-1]##[ ]#}%%[ ]#}" if (( ! $state[(I)$action] )); then - comparguments -W line opt_args + comparguments -W line opt_args $opt_args_use_NUL_separators state+=( "$action" ) state_descr+=( "$descr" ) if [[ -n "$usecc" ]]; then @@ -406,7 +408,7 @@ if (( $# )) && comparguments -i "$autod" "$singopt[@]" "$@"; then local=yes fi - comparguments -W line opt_args + comparguments -W line opt_args $opt_args_use_NUL_separators if [[ "$action" = \ # ]]; then diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo index 1ce17dccc..98ab46d8a 100644 --- a/Doc/Zsh/compsys.yo +++ b/Doc/Zsh/compsys.yo @@ -3740,6 +3740,12 @@ The default var(matchspec) allows partial word completion after `tt(_)' and var(matchspec) is: example(tt(r:|[_-]=* r:|=*)) ) +item(tt(-0))( +When populating values of the `tt(opt_args)' associative array, don't +backslash-escape colons and backslashes and use NUL rather than colon for +joining multiple values. This option is described in more detail below, under +the heading em(var(spec)s: actions). +) enditem() em(var(spec)s: overview) @@ -3912,6 +3918,7 @@ specific contexts: on the first call `tt(_arguments $global_options)' is used, and on subsequent calls `tt(_arguments !$^global_options)'. em(var(spec)s: actions) +COMMENT(If you change this section title, change the references to it in running text.) In each of the forms above the var(action) determines how completions should be generated. Except for the `tt(->)var(string)' @@ -4033,9 +4040,21 @@ the normal arguments from the command line, i.e. the words from the command line after the command name excluding all options and their arguments. Options are stored in the associative array `tt(opt_args)' with option names as keys and their arguments as -the values. For options that have more than one argument these are -given as one string, separated by colons. All colons and backslashes -in the original arguments are preceded with backslashes. +the values. By default, all colons and backslashes in the value are escaped +with backslashes, and if an option has multiple arguments (for example, when +using an var(optspec) of the form `tt(*)var(optspec)'), they are joined with +(unescaped) colons. However, if the tt(-0) option was passed, no backslash +escaping is performed, and multiple values are joined with NUL bytes. For +example, after `tt(zsh -o foo:foo -o bar:bar -o )', the contents of +`tt(opt_args)' would be + +example(typeset -A opt_args=( [-o]='foo\:foo:bar\:bar:' )) + +by default, and + +example(typeset -A opt_args=( [-o]=$'foo:foo\x00bar:bar\x00' )) + +if tt(_arguments) had been called with the tt(-0) option. The parameter `tt(context)' is set when returning to the calling function to perform an action of the form `tt(->)var(string)'. It is set to an diff --git a/Src/Zle/computil.c b/Src/Zle/computil.c index ddfa70077..e08788e89 100644 --- a/Src/Zle/computil.c +++ b/Src/Zle/computil.c @@ -2375,6 +2375,23 @@ ca_parse_line(Cadef d, Cadef all, int multi, int first) return 0; } +/* Build a NUL-separated from a list. + * + * This is only used to populate values of $opt_args. + */ + +static char * +ca_nullist(LinkList l) +{ + if (l) { + char **array = zlinklist2array(l, 0 /* don't dup elements */); + char *ret = zjoin(array, '\0', 0 /* permanent allocation */); + free(array); /* the elements are owned by the list */ + return ret; + } else + return ztrdup(""); +} + /* Build a colon-list from a list. * * This is only used to populate values of $opt_args. @@ -2563,7 +2580,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) case 's': min = 1; max = 1; break; case 'M': min = 1; max = 1; break; case 'a': min = 0; max = 0; break; - case 'W': min = 2; max = 2; break; + case 'W': min = 3; max = 3; break; case 'n': min = 1; max = 1; break; default: zwarnnam(nam, "invalid option: %s", args[0]); @@ -2795,11 +2812,20 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) return 0; return 1; case 'W': - /* This gets two parameter names as arguments. The first is set to - * the current word sans any option prefixes handled by comparguments. + /* This gets two parameter names and one integer as arguments. + * + * The first parameter is set to the current word sans any option + * prefixes handled by comparguments. + * * The second parameter is set to an array containing the options on * the line and their arguments. I.e. the stuff _arguments returns - * to its caller in the `line' and `opt_args' parameters. */ + * to its caller in the `line' and `opt_args' parameters. + * + * The integer is one if the second parameter (which is just $opt_args, + * you know) should encode multiple values by joining them with NULs + * and zero if it should encode multiple values by joining them with + * colons after backslash-escaping colons and backslashes. + */ { Castate s; char **ret, **p; @@ -2807,6 +2833,7 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) LinkList *a; Caopt o; int num; + int opt_args_use_NUL_separators = (args[3][0] != '0'); for (num = 0, s = lstate; s; s = s->snext) num += countlinknodes(s->args); @@ -2832,7 +2859,10 @@ bin_comparguments(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) if (*a) { *p++ = (o->gsname ? tricat(o->gsname, o->name, "") : ztrdup(o->name)); - *p++ = ca_colonlist(*a); + if (opt_args_use_NUL_separators) + *p++ = ca_nullist(*a); + else + *p++ = ca_colonlist(*a); } *p = NULL; diff --git a/Src/utils.c b/Src/utils.c index e258ef836..5158a70b1 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -3596,6 +3596,17 @@ strftimehandling: return buf - origbuf; } +/* + * Return a string consisting of the elements of 'arr' joined by the character + * 'delim', which will be metafied if necessary. The string will be allocated + * on the heap iff 'heap'. + * + * Comparable to: + * + * char metafied_delim[] = { Meta, delim ^ 32, '\0' }; + * sepjoin(arr, metafied_delim, heap) + */ + /**/ mod_export char * zjoin(char **arr, int delim, int heap) @@ -3894,10 +3905,12 @@ wordcount(char *s, char *sep, int mul) /* * 's' is a NULL-terminated array of strings. - * 'sep' is a string. + * 'sep' is a string, or NULL to split on ${IFS[1]}. * * Return a string consisting of the elements of 's' joined by 'sep', * allocated on the heap iff 'heap'. + * + * See also zjoin(). */ /**/ diff --git a/Test/Y03arguments.ztst b/Test/Y03arguments.ztst index fa4589374..a815799b3 100644 --- a/Test/Y03arguments.ztst +++ b/Test/Y03arguments.ztst @@ -231,9 +231,15 @@ tst_arguments '-a:one: :two' ':descr:{compadd -Q - $opt_args[-a]}' comptest $'tst -a 1:x \\2 \t' -0:opt_args with multiple arguments and quoting of colons and backslashes +0:opt_args with multiple arguments and quoting of colons and backslashes, part #1: default behaviour >line: {tst -a 1:x \2 1\:x:\\2 }{} + # Same as previous test, except with -0 and (qqqq) added + tst_arguments -0 : '-a:one: :two' ':descr:{compadd -Q - ${(qqqq)opt_args[-a]}}' + comptest $'tst -a 1:x \\2 \t' +0:opt_args with multiple arguments and quoting of colons and backslashes, part #2: NUL escaping +>line: {tst -a 1:x \2 $'1:x\0\\2' }{} + tst_arguments -a -b comptest $'tst rest -\t\C-w\eb\C-b-\t' 0:option completion with rest arguments on the line but not in the specs -- cgit v1.2.3 From 94e38548e306e6dd6fe601378fea85903d060bf5 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Sat, 30 May 2020 14:31:10 -0700 Subject: 45915: fix handling of hyphens in spckword() --- ChangeLog | 4 ++++ Src/utils.c | 16 +++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index f91fe53f3..773e5b64d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2020-05-30 Bart Schaefer + + * 45915: Src/utils.c: fix handling of hyphens in spckword() + 2020-05-28 Yasuhiro KIMURA * 45934: Completion/Unix/Command/_subversion: Make 'svnliteadmin' diff --git a/Src/utils.c b/Src/utils.c index 5158a70b1..5151b89a8 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -3128,11 +3128,13 @@ spckword(char **s, int hist, int cmd, int ask) int preflen = 0; int autocd = cmd && isset(AUTOCD) && strcmp(*s, ".") && strcmp(*s, ".."); - if ((histdone & HISTFLAG_NOEXEC) || **s == '-' || **s == '%') + if (!(*s)[0] || !(*s)[1]) return; - if (!strcmp(*s, "in")) + if ((histdone & HISTFLAG_NOEXEC) || + /* Leading % is a job, else leading hyphen is an option */ + (cmd ? **s == '%' : (**s == '-' || **s == Dash))) return; - if (!(*s)[0] || !(*s)[1]) + if (!strcmp(*s, "in")) return; if (cmd) { if (shfunctab->getnode(shfunctab, *s) || @@ -3151,8 +3153,12 @@ spckword(char **s, int hist, int cmd, int ask) if (*t == Tilde || *t == Equals || *t == String) t++; for (; *t; t++) - if (itok(*t)) - return; + if (itok(*t)) { + if (*t == Dash) + *t = '-'; + else + return; + } best = NULL; for (t = *s; *t; t++) if (*t == '/') -- cgit v1.2.3 From d96c8981011294cfef62cd1f8d76b2a982957ca4 Mon Sep 17 00:00:00 2001 From: Stephane Chazelas Date: Thu, 17 Dec 2020 14:49:50 +0000 Subject: 47745: Fix [:IDENT:] vs posixidentifiers wcsitype(c, IIDENT) should return false for non-ASCII characters when the POSIX_IDENTIFIERS option is on, not the other way round. --- ChangeLog | 5 +++++ Src/utils.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index b50d6b2b7..8cef6370b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2021-03-23 dana + + * 47745: Stephane Chazelas: Src/utils.c: Fix [:IDENT:] vs + posixidentifiers + 2021-03-17 dana * 48180: Marlon Richert: Completion/Base/Core/_main_complete, diff --git a/Src/utils.c b/Src/utils.c index 5151b89a8..2a8d677a7 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -4327,7 +4327,7 @@ wcsitype(wchar_t c, int itype) } else { switch (itype) { case IIDENT: - if (!isset(POSIXIDENTIFIERS)) + if (isset(POSIXIDENTIFIERS)) return 0; return iswalnum(c); -- cgit v1.2.3 From ea3351c49610355512672ab478c715ba3cb92aef Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Fri, 12 Mar 2021 03:50:01 +0100 Subject: 48202 + 48366: Fix handling of NUL bytes in zexpandtabs multibyte version --- ChangeLog | 6 ++++++ Src/utils.c | 5 ++++- Test/B03print.ztst | 9 +++++---- 3 files changed, 15 insertions(+), 5 deletions(-) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index 237a1bb3b..c9e305bde 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2021-04-01 Mikael Magnusson + + * 48202 (+ 48366 test fix from Jun): Src/utils.c, + Test/B03print.ztst: Fix handling of NUL bytes in zexpandtabs + multibyte version + 2021-03-31 Oliver Kiddle * github #74: Caleb Maclennan: Completion/Unix/Command/_vcsh: diff --git a/Src/utils.c b/Src/utils.c index 2a8d677a7..1ac064a4e 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -5912,8 +5912,11 @@ zexpandtabs(const char *s, int len, int width, int startpos, FILE *fout, memset(&mbs, 0, sizeof(mbs)); s++; len--; - } else if (ret == MB_INCOMPLETE) { + } else if (ret == MB_INCOMPLETE || /* incomplete at end --- assume likewise, best we've got */ + ret == 0) { + /* NUL character returns 0, which would loop infinitely, so advance + * one byte in this case too */ s++; len--; } else { diff --git a/Test/B03print.ztst b/Test/B03print.ztst index 563423934..4d2cf9764 100644 --- a/Test/B03print.ztst +++ b/Test/B03print.ztst @@ -304,15 +304,16 @@ foo=$'one\ttwo\tthree\tfour\n' foo+=$'\tone\ttwo\tthree\tfour\n' foo+=$'\t\tone\t\ttwo\t\tthree\t\tfour' - print -x4 $foo - print -X4 $foo + foo+='\0' # regression test for multibyte tab expand + print -x4 $foo | tr '\0' Z # avoid raw nul byte in expected output below + print -X4 $foo | tr '\0' Z 0:Tab expansion by print >one two three four > one two three four -> one two three four +> one two three fourZ >one two three four > one two three four -> one two three four +> one two three fourZ unset foo print -v foo once more -- cgit v1.2.3 From 0f62e07c802e3fa58d1199f34fcf9772da70c264 Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Tue, 6 Apr 2021 23:05:03 +0900 Subject: 48389: getkeystring() should not return ptr to local var Now it returns NULL if called with GETKEY_SINGLE_CHAR and next character is not found. Caller must check the return value. --- ChangeLog | 5 +++++ Src/math.c | 7 ++++++- Src/utils.c | 33 +++++++++++++++++++++++++-------- 3 files changed, 36 insertions(+), 9 deletions(-) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index d2b58a0f1..54d65ae61 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2021-04-06 Jun-ichi Takimoto + + * 48389: Src/math.c, Src/utils.c: getkeystring(GETKEY_SINGLE_CHAR) + should not return a pointer to a local variable + 2021-04-06 Oliver Kiddle * Marc Chantreux: users/26579: Completion/Unix/Command/_surfraw: diff --git a/Src/math.c b/Src/math.c index b57ba42d4..1d0d86639 100644 --- a/Src/math.c +++ b/Src/math.c @@ -840,13 +840,18 @@ zzlex(void) if (*ptr == '#') { if (*++ptr == '\\' || *ptr == '#') { int v; + char *optr = ptr; ptr++; if (!*ptr) { zerr("bad math expression: character missing after ##"); return EOI; } - ptr = getkeystring(ptr, NULL, GETKEYS_MATH, &v); + if(!(ptr = getkeystring(ptr, NULL, GETKEYS_MATH, &v))) { + zerr("bad math expression: bad character after ##"); + ptr = optr; + return EOI; + } yyval.u.l = v; return NUM; } diff --git a/Src/utils.c b/Src/utils.c index 1ac064a4e..5a9222919 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -6697,13 +6697,21 @@ ucs4toutf8(char *dest, unsigned int wval) * * The return value is unmetafied unless GETKEY_DOLLAR_QUOTE is * in use. + * + * If GETKEY_SINGLE_CHAR is set in how, a next character in the given + * string is parsed, and the character code for it is returned in misc. + * The return value of the function is a pointer to the byte in the + * given string from where the next parsing should start. If the next + * character can't be found then NULL is returned. + * CAUTION: Currently, GETKEY_SINGLE_CHAR can be used only via + * GETKEYS_MATH. Other use of it may cause trouble. */ /**/ mod_export char * getkeystring(char *s, int *len, int how, int *misc) { - char *buf, tmp[1]; + char *buf = NULL, tmp[1]; char *t, *tdest = NULL, *u = NULL, *sstart = s, *tbuf = NULL; char svchar = '\0'; int meta = 0, control = 0, ignoring = 0; @@ -6729,9 +6737,11 @@ getkeystring(char *s, int *len, int how, int *misc) DPUTS((how & (GETKEY_DOLLAR_QUOTE|GETKEY_SINGLE_CHAR)) == (GETKEY_DOLLAR_QUOTE|GETKEY_SINGLE_CHAR), "BUG: incompatible options in getkeystring"); + DPUTS((how & GETKEY_SINGLE_CHAR) && (how != GETKEYS_MATH), + "BUG: unsupported options in getkeystring"); if (how & GETKEY_SINGLE_CHAR) - t = buf = tmp; + t = tmp; else { /* Length including terminating NULL */ int maxlen = 1; @@ -7165,13 +7175,20 @@ getkeystring(char *s, int *len, int how, int *misc) */ DPUTS((how & (GETKEY_DOLLAR_QUOTE|GETKEY_UPDATE_OFFSET)) == GETKEY_DOLLAR_QUOTE, "BUG: unterminated $' substitution"); - *t = '\0'; - if (how & GETKEY_DOLLAR_QUOTE) - *tdest = '\0'; - if (how & GETKEY_SINGLE_CHAR) + + if (how & GETKEY_SINGLE_CHAR) { + /* couldn't find a character */ *misc = 0; - else - *len = ((how & GETKEY_DOLLAR_QUOTE) ? tdest : t) - buf; + return NULL; + } + if (how & GETKEY_DOLLAR_QUOTE) { + *tdest = '\0'; + *len = tdest - buf; + } + else { + *t = '\0'; + *len = t - buf; + } return buf; } -- cgit v1.2.3 From 988688d23d44bd83b292e2ae61293214bd259862 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 6 Sep 2021 14:00:36 -0700 Subject: 49282: set $0 correctly when calling functions from hooks --- Src/utils.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'Src/utils.c') diff --git a/Src/utils.c b/Src/utils.c index 5a9222919..c32741ca7 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1498,7 +1498,7 @@ time_t lastwatch; /* * Call a function given by "name" with optional arguments - * "lnklist". If these are present the first argument is the function name. + * "lnklst". If these are present the first argument is the function name. * * If "arrayp" is not zero, we also look through * the array "name"_functions and execute functions found there. @@ -1527,6 +1527,10 @@ callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval) incompfunc = 0; if ((shfunc = getshfunc(name))) { + if (!lnklst) { + lnklst = newlinklist(); + addlinknode(lnklst, name); + } ret = doshfunc(shfunc, lnklst, 1); stat = 0; } @@ -1539,10 +1543,16 @@ callhookfunc(char *name, LinkList lnklst, int arrayp, int *retval) memcpy(arrnam + namlen, HOOK_SUFFIX, HOOK_SUFFIX_LEN); if ((arrptr = getaparam(arrnam))) { + char **argarr = lnklst ? hlinklist2array(lnklst, 0) : NULL; arrptr = arrdup(arrptr); for (; *arrptr; arrptr++) { if ((shfunc = getshfunc(*arrptr))) { - int newret = doshfunc(shfunc, lnklst, 1); + int newret, i = 1; + LinkList arg0 = newlinklist(); + addlinknode(arg0, *arrptr); + while (argarr && argarr[i]) + addlinknode(arg0, argarr[i++]); + newret = doshfunc(shfunc, arg0, 1); if (!ret) ret = newret; stat = 0; -- 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/utils.c') 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 68c8c60eaab795353e30218a4c41615328697256 Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sat, 30 Oct 2021 23:33:44 +0200 Subject: 49533: add null check for preprompt functions list that could occur following an error when loading a module --- ChangeLog | 3 +++ Src/utils.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index 7721ccc8d..87d8536a7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2021-10-30 Oliver Kiddle + * 49533: Src/utils.c: add null check for preprompt functions list + that could occur following an error when loading a module + * 49528: Src/Zle/comp.h, Src/Zle/compcore.c, Src/Zle/complete.c, Completion/X/Command/_xinput, Completion/Zsh/Command/_compadd, Doc/Zsh/compwid.yo: allow multiple -D options to compadd diff --git a/Src/utils.c b/Src/utils.c index a74c8bd2c..ed3690172 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1378,6 +1378,9 @@ delprepromptfn(voidvoidfnptr_t func) { LinkNode ln; + if (!prepromptfns) + return; + for (ln = firstnode(prepromptfns); ln; ln = nextnode(ln)) { Prepromptfn ppdat = (Prepromptfn)getdata(ln); if (ppdat->func == func) { -- 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/utils.c') 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 07c77dad5025a3c1a1cef8c707fa1f1ae43d0f1c Mon Sep 17 00:00:00 2001 From: Oliver Kiddle Date: Sun, 28 Nov 2021 20:55:02 +0100 Subject: 49606: remove old NIS-specific code for retrieving user data --- ChangeLog | 3 ++ Src/hashnameddir.c | 123 ----------------------------------------------------- Src/utils.c | 2 +- configure.ac | 32 -------------- 4 files changed, 4 insertions(+), 156 deletions(-) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index bf1f88b03..f9ae6a3bb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2021-11-28 Oliver Kiddle + * 49606: Src/hashnameddir.c, Src/utils.c, configure.ac: remove old + NIS-specific code for retrieving user data + * 49602: Src/params.c: only set LOGCHECK from the watch module * 49601: Src/hist.c, Test/W01history.ztst: don't create diff --git a/Src/hashnameddir.c b/Src/hashnameddir.c index cbd1344ef..bab7b64b2 100644 --- a/Src/hashnameddir.c +++ b/Src/hashnameddir.c @@ -42,17 +42,6 @@ /* Named Directory Hash Table Functions */ /****************************************/ -#ifdef HAVE_NIS_PLUS -# include -#else -# ifdef HAVE_NIS -# include -# include -# include -# include -# endif -#endif - /* hash table containing named directories */ /**/ @@ -102,122 +91,11 @@ emptynameddirtable(HashTable ht) /* Add all the usernames in the password file/database * * to the named directories table. */ -#ifdef HAVE_NIS_PLUS -static int -add_userdir(nis_name table, nis_object *object, void *userdata) -{ - if (object->zo_data.objdata_u.en_data.en_cols.en_cols_len >= 6) { - static char name[40], dir[PATH_MAX + 1]; - register entry_col *ec = - object->zo_data.objdata_u.en_data.en_cols.en_cols_val; - register int nl = minimum(ec[0].ec_value.ec_value_len, 39); - register int dl = minimum(ec[5].ec_value.ec_value_len, PATH_MAX); - - memcpy(name, ec[0].ec_value.ec_value_val, nl); - name[nl] = '\0'; - memcpy(dir, ec[5].ec_value.ec_value_val, dl); - dir[dl] = '\0'; - - adduserdir(name, dir, ND_USERNAME, 1); - } - return 0; -} -#else -# ifdef HAVE_NIS -static int -add_userdir(int status, char *key, int keylen, char *val, int vallen, char *dummy) -{ - char *p, *d, *de; - - if (status != YP_TRUE) - return 1; - - if (vallen > keylen && *(p = val + keylen) == ':') { - *p++ = '\0'; - for (de = val + vallen - 1; *de != ':' && de > val; de--); - if (de > val) { - *de = '\0'; - if ((d = strrchr(p, ':'))) { - if (*++d && val[0]) - adduserdir(val, d, ND_USERNAME, 1); - } - } - } - return 0; -} -# endif /* HAVE_NIS */ -#endif /* HAVE_NIS_PLUS */ - /**/ static void fillnameddirtable(UNUSED(HashTable ht)) { if (!allusersadded) { -#if defined(HAVE_NIS) || defined(HAVE_NIS_PLUS) - FILE *pwf; - char buf[BUFSIZ], *p, *d, *de; - int skipping, oldct = nameddirtab->ct, usepwf = 1; - -# ifndef HAVE_NIS_PLUS - char domain[YPMAXDOMAIN]; - struct ypall_callback cb; - - /* Get potential matches from NIS and cull those without local accounts */ - if (getdomainname(domain, YPMAXDOMAIN) == 0) { - cb.foreach = (int (*)()) add_userdir; - cb.data = NULL; - yp_all(domain, PASSWD_MAP, &cb); - } -# else /* HAVE_NIS_PLUS */ - /* Maybe we should turn this string into a #define'd constant...? */ - - nis_list("passwd.org_dir", EXPAND_NAME|ALL_RESULTS|FOLLOW_LINKS|FOLLOW_PATH, - add_userdir, 0); -# endif - if (nameddirtab->ct == oldct) { - /* 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 USE_GETPWENT - struct passwd *pw; - - setpwent(); - - /* loop through the password file/database * - * and add all entries returned. */ - while ((pw = getpwent()) && !errflag) - adduserdir(pw->pw_name, pw->pw_dir, ND_USERNAME, 1); - - endpwent(); - usepwf = 0; -#endif /* USE_GETPWENT */ - } - if (usepwf) { - /* Don't forget the non-NIS matches from the flat passwd file */ - if ((pwf = fopen(PASSWD_FILE, "r")) != NULL) { - skipping = 0; - while (fgets(buf, BUFSIZ, pwf) != NULL) { - if (strchr(buf, '\n') != NULL) { - if (!skipping) { - if ((p = strchr(buf, ':')) != NULL) { - *p++ = '\0'; - if ((de = strrchr(p, ':'))) { - *de = '\0'; - if ((d = strrchr(p, ':'))) { - if (*++d && buf[0]) - adduserdir(buf, d, ND_USERNAME, 1); - } - } - } - } else - skipping = 0; - } else - skipping = 1; - } - fclose(pwf); - } - } -#else /* no NIS or NIS_PLUS */ #ifdef USE_GETPWENT struct passwd *pw; @@ -230,7 +108,6 @@ fillnameddirtable(UNUSED(HashTable ht)) endpwent(); #endif /* USE_GETPWENT */ -#endif allusersadded = 1; } } diff --git a/Src/utils.c b/Src/utils.c index 8adab2bd7..f9127c70c 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -1108,7 +1108,7 @@ substnamedir(char *s) /* Returns the current username. It caches the username * * and uid to try to avoid requerying the password files * - * or NIS/NIS+ database. */ + * or other source. */ /**/ uid_t cached_uid; diff --git a/configure.ac b/configure.ac index 297a7482f..1af5a2854 100644 --- a/configure.ac +++ b/configure.ac @@ -2071,38 +2071,6 @@ AH_TEMPLATE([HAVE_PRCTL], AC_CHECK_FUNC(prctl,AC_DEFINE(HAVE_PRCTL), AC_SEARCH_LIBS(prctl,c,AC_DEFINE(HAVE_PRCTL))) -dnl ------------- -dnl CHECK FOR NIS -dnl ------------- -AH_TEMPLATE([HAVE_NIS], -[Define to 1 if you have NIS.]) -AC_CACHE_CHECK(for NIS, zsh_cv_sys_nis, -[test -f /usr/bin/ypcat && /usr/bin/ypcat passwd.byname > /dev/null 2>&1 && \ -zsh_cv_sys_nis=yes || zsh_cv_sys_nis=no]) -if test x$zsh_cv_sys_nis = xyes; then - AC_DEFINE(HAVE_NIS) -dnl RPC is removed from glibc-2.26 and replaced by libtirpc - AC_CHECK_HEADER(rpc/rpc.h, [], - [test -f /usr/include/tirpc/rpc/rpc.h && \ - CPPFLAGS="$CPPFLAGS -I/usr/include/tirpc"]) -dnl Some systems (Solaris 2.x, Linux Redhat 5.x) require -dnl libnsl (Network Services Library) to find yp_all - AC_SEARCH_LIBS(yp_all, nsl) -fi - -dnl ----------------- -dnl CHECK FOR NISPLUS -dnl ----------------- -AH_TEMPLATE([HAVE_NIS_PLUS], -[Define to 1 if you have NISPLUS.]) -AC_CACHE_CHECK(for NIS+, zsh_cv_sys_nis_plus, -[test x$ac_cv_func_nis_list = xyes && test -f /usr/bin/nisls && \ - /usr/bin/nisls > /dev/null 2>&1 && \ -zsh_cv_sys_nis_plus=yes || zsh_cv_sys_nis_plus=no]) -if test x$zsh_cv_sys_nis_plus = xyes; then - AC_DEFINE(HAVE_NIS_PLUS) -fi - dnl ---------------------------------------- dnl CHECK FOR LOCATION OF {U,W}TMP{,X} FILES dnl ---------------------------------------- -- cgit v1.2.3 From b80d1600395c46b24ebe8d34e40fb4e660692de1 Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 4 Apr 2022 14:21:50 -0700 Subject: 49991: single-byte brace expansion handles $'\0' and control character output aligns with multibyte --- ChangeLog | 3 +++ Src/glob.c | 15 ++++++--------- Src/utils.c | 11 ++++++++--- 3 files changed, 17 insertions(+), 12 deletions(-) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index 92e5b6716..d2ffce95e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-04-04 Bart Schaefer + * 49991: Src/glob.c, Src/utils.c: single-byte brace expansion + handles $'\0' and control character output aligns with multibyte + * 49990: Src/hist.c: in single-byte, like multibyte, casemodify() avoids metafying characters that it otherwise did not touch diff --git a/Src/glob.c b/Src/glob.c index d4ffc2274..ca28f20e8 100644 --- a/Src/glob.c +++ b/Src/glob.c @@ -2220,7 +2220,7 @@ bracechardots(char *str, convchar_t *c1p, convchar_t *c2p) #ifdef MULTIBYTE_SUPPORT cstart == WEOF || #else - !cstart || + !*pconv || #endif pnext[0] != '.' || pnext[1] != '.') return 0; @@ -2241,7 +2241,7 @@ bracechardots(char *str, convchar_t *c1p, convchar_t *c2p) #ifdef MULTIBYTE_SUPPORT cend == WEOF || #else - !cend || + !*pconv || #endif *pnext != Outbrace) return 0; @@ -2305,22 +2305,19 @@ xpandbraces(LinkList list, LinkNode *np) strp = str - str3; lenalloc = strp + strlen(str2+1) + 1; do { -#ifdef MULTIBYTE_SUPPORT char *ncptr; int nclen; +#ifdef MULTIBYTE_SUPPORT mb_charinit(); ncptr = wcs_nicechar(cend, NULL, NULL); +#else + ncptr = nicechar(cend); +#endif nclen = strlen(ncptr); p = zhalloc(lenalloc + nclen); memcpy(p, str3, strp); memcpy(p + strp, ncptr, nclen); strcpy(p + strp + nclen, str2 + 1); -#else - p = zhalloc(lenalloc + 1); - memcpy(p, str3, strp); - sprintf(p + strp, "%c", cend); - strcat(p + strp, str2 + 1); -#endif insertlinknode(list, last, p); if (rev) /* decreasing: add in reverse order. */ last = nextnode(last); diff --git a/Src/utils.c b/Src/utils.c index f9127c70c..66cb2a63a 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -557,9 +557,14 @@ nicechar(int c) *s++ = '\\'; c = 't'; } else if (c < 0x20) { - *s++ = '\\'; - *s++ = 'C'; - *s++ = '-'; + /* + if (quotable) { + *s++ = '\\'; + *s++ = 'C'; + *s++ = '-'; + } else + */ + *s++ = '^'; c += 0x40; } done: -- cgit v1.2.3 From 0d9c2422bf3815ee04e5c1b562a22b9df56c07ed Mon Sep 17 00:00:00 2001 From: Bart Schaefer Date: Mon, 4 Apr 2022 14:48:21 -0700 Subject: 49992: further unify single- and multi-byte implementations of nicechar() --- ChangeLog | 3 +++ Src/utils.c | 62 ------------------------------------------------------------- 2 files changed, 3 insertions(+), 62 deletions(-) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index d2ffce95e..1a5d48415 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2022-04-04 Bart Schaefer + * 49992: Src/utils.c: further unify single- and multi-byte + implementations of nicechar() to fix ${(V)...} output format + * 49991: Src/glob.c, Src/utils.c: single-byte brace expansion handles $'\0' and control character output aligns with multibyte diff --git a/Src/utils.c b/Src/utils.c index 66cb2a63a..ffa59eff8 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -438,7 +438,6 @@ putshout(int c) return 0; } -#ifdef MULTIBYTE_SUPPORT /* * Turn a character into a visible representation thereof. The visible * string is put together in a static buffer, and this function returns @@ -524,67 +523,6 @@ nicechar(int c) return nicechar_sel(c, 0); } -#else /* MULTIBYTE_SUPPORT */ - -/**/ -mod_export char * -nicechar(int c) -{ - static char buf[10]; - char *s = buf; - c &= 0xff; - if (ZISPRINT(c)) - goto done; - if (c & 0x80) { - if (isset(PRINTEIGHTBIT)) - goto done; - *s++ = '\\'; - *s++ = 'M'; - *s++ = '-'; - c &= 0x7f; - if(ZISPRINT(c)) - goto done; - } - if (c == 0x7f) { - *s++ = '\\'; - *s++ = 'C'; - *s++ = '-'; - c = '?'; - } else if (c == '\n') { - *s++ = '\\'; - c = 'n'; - } else if (c == '\t') { - *s++ = '\\'; - c = 't'; - } else if (c < 0x20) { - /* - if (quotable) { - *s++ = '\\'; - *s++ = 'C'; - *s++ = '-'; - } else - */ - *s++ = '^'; - c += 0x40; - } - done: - /* - * The resulting string is still metafied, so check if - * we are returning a character in the range that needs metafication. - * This can't happen if the character is printed "nicely", so - * this results in a maximum of two bytes total (plus the null). - */ - if (imeta(c)) { - *s++ = Meta; - *s++ = c ^ 32; - } else - *s++ = c; - *s = 0; - return buf; -} - -#endif /* MULTIBYTE_SUPPORT */ - /* * Return 1 if nicechar() would reformat this character. */ -- cgit v1.2.3 From 02747bbbd37d81a2a2fff938e1cc94fb73980848 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 4 Apr 2022 15:34:40 +0100 Subject: 49989: Single byte versions of nice quoting. Align interfaces for "nice" printing of characters with those for multibyte to make more available in single-byte compilation. --- ChangeLog | 5 ++ Src/Zle/compresult.c | 6 +- Src/utils.c | 171 +++++++++++++++++++++++++++++++++++++++++---------- Src/zsh.h | 5 +- 4 files changed, 147 insertions(+), 40 deletions(-) (limited to 'Src/utils.c') diff --git a/ChangeLog b/ChangeLog index 1a5d48415..b412b883d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2022-04-05 Peter Stephenson + + * 49989: Src/Zle/compresult.c, Src/utils.c, Src/zsh.h: Align + "nice" printing for single byte compilation with multibyte. + 2022-04-04 Bart Schaefer * 49992: Src/utils.c: further unify single- and multi-byte diff --git a/Src/Zle/compresult.c b/Src/Zle/compresult.c index 0fed297b5..57789c0f3 100644 --- a/Src/Zle/compresult.c +++ b/Src/Zle/compresult.c @@ -2248,15 +2248,13 @@ iprintm(Cmgroup g, Cmatch *mp, UNUSED(int mc), UNUSED(int ml), int lastc, int wi #ifdef MULTIBYTE_SUPPORT len = mb_niceformat(m->disp, shout, NULL, 0); #else - nicezputs(m->disp, shout); - len = niceztrlen(m->disp); + len = sb_niceformat(m->disp, shout, NULL, 0); #endif } else { #ifdef MULTIBYTE_SUPPORT len = mb_niceformat(m->str, shout, NULL, 0); #else - nicezputs(m->str, shout); - len = niceztrlen(m->str); + len = sb_niceformat(m->str, shout, NULL, 0); #endif if ((g->flags & CGF_FILES) && m->modec) { diff --git a/Src/utils.c b/Src/utils.c index ffa59eff8..62bd3e602 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -5196,26 +5196,11 @@ zputs(char const *s, FILE *stream) mod_export char * nicedup(char const *s, int heap) { - int c, len = strlen(s) * 5 + 1; - VARARR(char, buf, len); - char *p = buf, *n; + char *retstr; - while ((c = *s++)) { - if (itok(c)) { - if (c <= Comma) - c = ztokens[c - Pound]; - else - continue; - } - if (c == Meta) - c = *s++ ^ 32; - /* The result here is metafied */ - n = nicechar(c); - while(*n) - *p++ = *n++; - } - *p = '\0'; - return heap ? dupstring(buf) : ztrdup(buf); + (void)sb_niceformat(s, NULL, &retstr, heap ? NICEFLAG_HEAP : 0); + + return retstr; } #endif @@ -5234,20 +5219,7 @@ nicedupstring(char const *s) mod_export int nicezputs(char const *s, FILE *stream) { - int c; - - while ((c = *s++)) { - if (itok(c)) { - if (c <= Comma) - c = ztokens[c - Pound]; - else - continue; - } - if (c == Meta) - c = *s++ ^ 32; - if(zputs(nicechar(c), stream) < 0) - return EOF; - } + sb_niceformat(s, stream, NULL, 0); return 0; } @@ -5736,7 +5708,7 @@ mb_charlenconv(const char *s, int slen, wint_t *wcp) } /**/ -#else +#else /* MULTIBYTE_SUPPORT */ /* Simple replacement for mb_metacharlenconv */ @@ -5776,6 +5748,121 @@ charlenconv(const char *x, int len, int *c) return 1; } +/* + * Non-multibyte version of mb_niceformat() above. Same basic interface. + */ + +/**/ +mod_export size_t +sb_niceformat(const char *s, FILE *stream, char **outstrp, int flags) +{ + size_t l = 0, newl; + int umlen, outalloc, outleft; + char *ums, *ptr, *eptr, *fmt, *outstr, *outptr; + + if (outstrp) { + outleft = outalloc = 2 * strlen(s); + outptr = outstr = zalloc(outalloc); + } else { + outleft = outalloc = 0; + outptr = outstr = NULL; + } + + ums = ztrdup(s); + /* + * is this necessary at this point? niceztrlen does this + * but it's used in lots of places. however, one day this may + * be, too. + */ + untokenize(ums); + ptr = unmetafy(ums, ¨en); + eptr = ptr + umlen; + + while (ptr < eptr) { + int c = STOUC(*ptr); + if (c == '\'' && (flags & NICEFLAG_QUOTE)) { + fmt = "\\'"; + newl = 2; + } + else if (c == '\\' && (flags & NICEFLAG_QUOTE)) { + fmt = "\\\\"; + newl = 2; + } + else { + fmt = nicechar_sel(c, flags & NICEFLAG_QUOTE); + newl = 1; + } + + ++ptr; + l += newl; + + if (stream) + zputs(fmt, stream); + if (outstr) { + /* Append to output string */ + int outlen = strlen(fmt); + if (outlen >= outleft) { + /* Reallocate to twice the length */ + int outoffset = outptr - outstr; + + outleft += outalloc; + outalloc *= 2; + outstr = zrealloc(outstr, outalloc); + outptr = outstr + outoffset; + } + memcpy(outptr, fmt, outlen); + /* Update start position */ + outptr += outlen; + /* Update available bytes */ + outleft -= outlen; + } + } + + free(ums); + if (outstrp) { + *outptr = '\0'; + /* Use more efficient storage for returned string */ + if (flags & NICEFLAG_NODUP) + *outstrp = outstr; + else { + *outstrp = (flags & NICEFLAG_HEAP) ? dupstring(outstr) : + ztrdup(outstr); + free(outstr); + } + } + + return l; +} + +/* + * Return 1 if sb_niceformat() would reformat this string, else 0. + */ + +/**/ +mod_export int +is_sb_niceformat(const char *s) +{ + int umlen, ret = 0; + char *ums, *ptr, *eptr; + + ums = ztrdup(s); + untokenize(ums); + ptr = unmetafy(ums, ¨en); + eptr = ptr + umlen; + + while (ptr < eptr) { + if (is_nicechar(*ptr)) { + ret = 1; + break; + } + ++ptr; + } + + free(ums); + + return ret; +} + /**/ #endif /* MULTIBYTE_SUPPORT */ @@ -6309,6 +6396,22 @@ quotedzputs(char const *s, FILE *stream) return outstr; } } +#else + if (is_sb_niceformat(s)){ + if (stream) { + fputs("$'", stream); + sb_niceformat(s, stream, NULL, NICEFLAG_QUOTE); + fputc('\'', stream); + return NULL; + } else { + char *substr; + sb_niceformat(s, NULL, &substr, NICEFLAG_QUOTE|NICEFLAG_NODUP); + outstr = (char *)zhalloc(4 + strlen(substr)); + sprintf(outstr, "$'%s'", substr); + free(substr); + return outstr; + } + } #endif /* MULTIBYTE_SUPPORT */ if (!hasspecial(s)) { diff --git a/Src/zsh.h b/Src/zsh.h index 641d9c95c..40f9ea537 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -3277,14 +3277,15 @@ enum zexit_t { #define AFTERTRAPHOOK (zshhooks + 2) #define GETCOLORATTR (zshhooks + 3) -#ifdef MULTIBYTE_SUPPORT -/* Final argument to mb_niceformat() */ +/* Final argument to [ms]b_niceformat() */ enum { NICEFLAG_HEAP = 1, /* Heap allocation where needed */ NICEFLAG_QUOTE = 2, /* Result will appear in $'...' */ NICEFLAG_NODUP = 4, /* Leave allocated */ }; +#ifdef MULTIBYTE_SUPPORT + /* Metafied input */ #define nicezputs(str, outs) (void)mb_niceformat((str), (outs), NULL, 0) #define MB_METACHARINIT() mb_charinit() -- cgit v1.2.3