From c486040220e07f2d9fbf45fe6b74f46b669553dc Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Tue, 5 May 2020 20:28:31 +0100
Subject: 45772: Restore locale on parameter scope end.
Check if relevant parameters changes and if so restore system settings.
---
Src/params.c | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
(limited to 'Src/params.c')
diff --git a/Src/params.c b/Src/params.c
index 863b32600..122f5da7d 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -5569,6 +5569,14 @@ startparamscope(void)
locallevel++;
}
+#ifdef USE_LOCALE
+/*
+ * Flag that one of the special LC_ functions or LANG changed on scope
+ * end
+ */
+static int lc_update_needed;
+#endif /* USE_LOCALE */
+
/* End a parameter scope: delete the parameters local to the scope. */
/**/
@@ -5579,7 +5587,28 @@ endparamscope(void)
locallevel--;
/* This pops anything from a higher locallevel */
saveandpophiststack(0, HFILE_USE_OPTIONS);
+#ifdef USE_LOCALE
+ lc_update_needed = 0;
+#endif
scanhashtable(paramtab, 0, 0, 0, scanendscope, 0);
+#ifdef USE_LOCALE
+ if (lc_update_needed)
+ {
+ /* Locale changed --- ensure it is restored. */
+ char *val;
+ if ((val = getsparam_u("LC_ALL")) && *val) {
+ setlocale(LC_ALL, val);
+ } else {
+ struct localename *ln;
+ if ((val = getsparam_u("LANG")) && *val)
+ setlang(val);
+ for (ln = lc_names; ln->name; ln++) {
+ if ((val = getsparam_u(ln->name)) && *val)
+ setlocale(ln->category, val);
+ }
+ }
+ }
+#endif /* USE_LOCALE */
unqueue_signals();
}
@@ -5600,6 +5629,11 @@ scanendscope(HashNode hn, UNUSED(int flags))
*/
Param tpm = pm->old;
+#ifdef USE_LOCALE
+ if (!strncmp(pm->node.nam, "LC_", 3) ||
+ !strcmp(pm->node.nam, "LANG"))
+ lc_update_needed = 1;
+#endif
if (!strcmp(pm->node.nam, "SECONDS"))
{
setsecondstype(pm, PM_TYPE(tpm->node.flags), PM_TYPE(pm->node.flags));
--
cgit v1.2.3
From 82ff9f24f170eea7daa935fdaa09ab75a2f277ff Mon Sep 17 00:00:00 2001
From: Bart Schaefer
Date: Sun, 18 Apr 2021 13:58:09 -0700
Subject: 48560: add TYPESET_TO_UNSET option to remove initialization of
parameters
Changes typeset such that ${newparam-notset} yields "notset" and
"typeset -p newparam" does not show an assignment to the parameter. This
is similar to the default behavior of bash and ksh, with minor differences
in typeset output.
Also add tests for some POSIX incompatibilities plus minor changes for test
harness robustness.
---
ChangeLog | 19 +++++++++++++++++--
Completion/compinit | 1 +
Doc/Zsh/builtins.yo | 6 +++++-
Doc/Zsh/options.yo | 10 ++++++++++
Doc/Zsh/params.yo | 5 +++++
Src/builtin.c | 14 +++++++++++---
Src/options.c | 1 +
Src/params.c | 22 +++++++++++++++++++---
Src/subst.c | 3 ++-
Src/zsh.h | 3 +++
Test/D06subscript.ztst | 5 +++++
Test/E01options.ztst | 15 +++++++++++++++
Test/V10private.ztst | 12 ++++++------
Test/runtests.zsh | 2 +-
Test/ztst.zsh | 2 +-
15 files changed, 102 insertions(+), 18 deletions(-)
(limited to 'Src/params.c')
diff --git a/ChangeLog b/ChangeLog
index 722a2437c..7dcbbc533 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2021-04-18 Bart Schaefer
+
+ * 48560: Completion/compinit, Doc/Zsh/builtins.yo,
+ Doc/Zsh/options.yo, Doc/Zsh/params.yo, Src/builtin.c,
+ Src/options.c, Src/params.c, Src/subst.c, Src/zsh.h,
+ Test/D06subscript.ztst, Test/E01options.ztst, Test/E03posix.ztst,
+ Test/V10private.ztst, Test/runtests.zsh, Test/ztst.zsh: add
+ TYPESET_TO_UNSET option, which removes initialization of newly
+ declared parameters such that ${newparam-notset} yields "notset"
+ and "typeset -p newparam" does not show an assignment to the
+ parameter. This is similar to the default behavior of bash and
+ ksh, with minor differences in typeset output. Also add tests for
+ some POSIX incompatibilities plus minor changes for test harness
+ robustness.
+
2021-04-18 Jun-ichi Takimoto
* unposted: Etc/BUGS: remove a bug fixed by 47301
@@ -2909,7 +2924,7 @@ g
2019-02-14 Peter Stephenson
- * see 44062: back off change to ZLE per-line initiialisation,
+ * see 44062: back off change to ZLE per-line initialisation,
causing problems after failed reads and apparently not needed
for the intended fix of interrupt handling (40305 / 34656ec2).
@@ -15632,7 +15647,7 @@ g
* 32338: Doc/Makefile.in: create Doc/help.txt as an empty file
when Util/helpfiles fails, so that the rest of the build does not
- yeild a spurious error
+ yield a spurious error
* 32337: Src/params.c: initialize several special parameters to
unset for better compatibility in emulation modes; for the same
diff --git a/Completion/compinit b/Completion/compinit
index e81cd1604..1f2e7c634 100644
--- a/Completion/compinit
+++ b/Completion/compinit
@@ -165,6 +165,7 @@ _comp_options=(
NO_posixidentifiers
NO_shwordsplit
NO_shglob
+ NO_typesettounset
NO_warnnestedvar
NO_warncreateglobal
)
diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index a7afe42cf..61dc6986f 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -1872,7 +1872,11 @@ ifnzman(noderef(Local Parameters))\
retain their special attributes when made local.
For each var(name)tt(=)var(value) assignment, the parameter
-var(name) is set to var(value).
+var(name) is set to var(value). If the assignment is omitted and var(name)
+does em(not) refer to an existing parameter, a new parameter is intialized
+to empty string, zero, or empty array (as appropriate), em(unless) the
+shell option tt(TYPESET_TO_UNSET) is set. When that option is set,
+the parameter attributes are recorded but the parameter remains unset.
If the shell option tt(TYPESET_SILENT) is not set, for each remaining
var(name) that refers to a parameter that is already set, the name and
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 714e8a1a1..6e862fae8 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -1942,6 +1942,16 @@ If the option is set, they will only be shown when parameters are selected
with the `tt(-m)' option. The option `tt(-p)' is available whether or not
the option is set.
)
+pindex(TYPESET_TO_UNSET)
+pindex(NO_TYPESET_TO_UNSET)
+pindex(TYPESETTOUNSET)
+pindex(NOTYPESETTOUNSET)
+item(tt(TYPESET_TO_UNSET) )(
+When declaring a new parameter with any of the `tt(typeset)' family of
+related commands, the parameter remains unset unless and until a
+value is explicity assigned to it, either in the `tt(typeset)' command
+itself or as a later assignment statement.
+)
pindex(VERBOSE)
pindex(NO_VERBOSE)
pindex(NOVERBOSE)
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 36c1ae4c2..a9044336f 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -393,6 +393,11 @@ is compared to the pattern, and the first matching key found is the
result. On failure substitutes the length of the array plus one, as
discussed under the description of `tt(r)', or the empty string for an
associative array.
+
+Note: Although `tt(i)' may be applied to a scalar substitution to find
+the offset of a substring, the results are likely to be misleading when
+searching within substitutions that yield an empty string, or when
+searching for the empty substring.
)
item(tt(I))(
Like `tt(i)', but gives the index of the last match, or all possible
diff --git a/Src/builtin.c b/Src/builtin.c
index 26335a2e8..6d119f7a5 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -2491,6 +2491,8 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
return NULL;
}
}
+ if (isset(TYPESETTOUNSET))
+ pm->node.flags |= PM_DEFAULTED;
} else {
if (idigit(*pname))
zerrnam(cname, "not an identifier: %s", pname);
@@ -2836,7 +2838,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
unqueue_signals();
return 1;
} else if (pm) {
- if (!(pm->node.flags & PM_UNSET)
+ if ((!(pm->node.flags & PM_UNSET) || pm->node.flags & PM_DECLARED)
&& (locallevel == pm->level || !(on & PM_LOCAL))) {
if (pm->node.flags & PM_TIED) {
if (PM_TYPE(pm->node.flags) != PM_SCALAR) {
@@ -2889,6 +2891,8 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
*
* Don't attempt to set it yet, it's too early
* to be exported properly.
+ *
+ * This may create the array with PM_DEFAULTED.
*/
asg2.name = asg->name;
asg2.flags = 0;
@@ -2930,8 +2934,12 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
if (asg->value.array) {
int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0;
assignaparam(asg->name, zlinklist2array(asg->value.array, 1), flags);
- } else if (oldval)
- assignsparam(asg0.name, oldval, 0);
+ } else if (asg0.value.scalar || oldval) {
+ /* We have to undo what we did wrong with asg2 */
+ apm->node.flags &= ~PM_DEFAULTED;
+ if (oldval)
+ assignsparam(asg0.name, oldval, 0);
+ }
unqueue_signals();
return 0;
diff --git a/Src/options.c b/Src/options.c
index 6ea6290e5..783022591 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -259,6 +259,7 @@ static struct optname optns[] = {
{{NULL, "transientrprompt", 0}, TRANSIENTRPROMPT},
{{NULL, "trapsasync", 0}, TRAPSASYNC},
{{NULL, "typesetsilent", OPT_EMULATE|OPT_BOURNE}, TYPESETSILENT},
+{{NULL, "typesettounset", OPT_EMULATE|OPT_BOURNE}, TYPESETTOUNSET},
{{NULL, "unset", OPT_EMULATE|OPT_BSHELL}, UNSET},
{{NULL, "verbose", 0}, VERBOSE},
{{NULL, "vi", 0}, VIMODE},
diff --git a/Src/params.c b/Src/params.c
index 122f5da7d..33bbc54f6 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -2093,7 +2093,8 @@ fetchvalue(Value v, char **pptr, int bracks, int flags)
if (sav)
*s = sav;
*pptr = s;
- if (!pm || (pm->node.flags & PM_UNSET))
+ if (!pm || ((pm->node.flags & PM_UNSET) &&
+ !(pm->node.flags & PM_DECLARED)))
return NULL;
if (v)
memset(v, 0, sizeof(*v));
@@ -3055,6 +3056,7 @@ assignsparam(char *s, char *val, int flags)
* Don't warn about anything.
*/
flags &= ~ASSPM_WARN;
+ v->pm->node.flags &= ~PM_DEFAULTED;
}
*ss = '[';
v = NULL;
@@ -3080,6 +3082,7 @@ assignsparam(char *s, char *val, int flags)
}
if (flags & ASSPM_WARN)
check_warn_pm(v->pm, "scalar", created, 1);
+ v->pm->node.flags &= ~PM_DEFAULTED;
if (flags & ASSPM_AUGMENT) {
if (v->start == 0 && v->end == -1) {
switch (PM_TYPE(v->pm->node.flags)) {
@@ -3232,6 +3235,7 @@ assignaparam(char *s, char **val, int flags)
if (flags & ASSPM_WARN)
check_warn_pm(v->pm, "array", created, may_warn_about_nested_vars);
+ v->pm->node.flags &= ~PM_DEFAULTED;
/*
* At this point, we may have array entries consisting of
@@ -3444,6 +3448,7 @@ sethparam(char *s, char **val)
return NULL;
}
check_warn_pm(v->pm, "associative array", checkcreate, 1);
+ v->pm->node.flags &= ~PM_DEFAULTED;
setarrvalue(v, val);
unqueue_signals();
return v->pm;
@@ -3515,6 +3520,7 @@ assignnparam(char *s, mnumber val, int flags)
if (flags & ASSPM_WARN)
check_warn_pm(v->pm, "numeric", 0, 1);
}
+ v->pm->node.flags &= ~PM_DEFAULTED;
setnumvalue(v, val);
unqueue_signals();
return v->pm;
@@ -3619,6 +3625,7 @@ unsetparam_pm(Param pm, int altflag, int exp)
else
altremove = NULL;
+ pm->node.flags &= ~PM_DECLARED; /* like ksh, not like bash */
if (!(pm->node.flags & PM_UNSET))
pm->gsu.s->unsetfn(pm, exp);
if (pm->env)
@@ -3652,6 +3659,8 @@ unsetparam_pm(Param pm, int altflag, int exp)
}
zsfree(altremove);
+ if (!(pm->node.flags & PM_SPECIAL))
+ pm->gsu.s = &stdscalar_gsu;
}
/*
@@ -4116,6 +4125,11 @@ tiedarrsetfn(Param pm, char *x)
if (*dptr->arrptr)
freearray(*dptr->arrptr);
+ else if (pm->ename) {
+ Param altpm = (Param) paramtab->getnode(paramtab, pm->ename);
+ if (altpm)
+ altpm->node.flags &= ~PM_DEFAULTED;
+ }
if (x) {
char sepbuf[3];
if (imeta(dptr->joinchar))
@@ -5035,6 +5049,7 @@ arrfixenv(char *s, char **t)
if (isset(ALLEXPORT))
pm->node.flags |= PM_EXPORTED;
+ pm->node.flags &= ~PM_DEFAULTED;
/*
* Do not "fix" parameters that were not exported
@@ -5839,8 +5854,9 @@ printparamnode(HashNode hn, int printflags)
Param peer = NULL;
if (p->node.flags & PM_UNSET) {
- if (printflags & (PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT) &&
- p->node.flags & (PM_READONLY|PM_EXPORTED)) {
+ if ((printflags & (PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT) &&
+ p->node.flags & (PM_READONLY|PM_EXPORTED)) ||
+ (p->node.flags & PM_DEFAULTED) == PM_DEFAULTED) {
/*
* Special POSIX rules: show the parameter as readonly/exported
* even though it's unset, but with no value.
diff --git a/Src/subst.c b/Src/subst.c
index 96e0914eb..9928be0e9 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -2563,7 +2563,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
* Handle the (t) flag: value now becomes the type
* information for the parameter.
*/
- if (v && v->pm && !(v->pm->node.flags & PM_UNSET)) {
+ if (v && v->pm && ((v->pm->node.flags & PM_DECLARED) ||
+ !(v->pm->node.flags & PM_UNSET))) {
int f = v->pm->node.flags;
switch (PM_TYPE(f)) {
diff --git a/Src/zsh.h b/Src/zsh.h
index d70a4017c..af9b4fb67 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1929,8 +1929,10 @@ struct tieddata {
made read-only by the user */
#define PM_READONLY_SPECIAL (PM_SPECIAL|PM_READONLY|PM_RO_BY_DESIGN)
#define PM_DONTIMPORT (1<<22) /* do not import this variable */
+#define PM_DECLARED (1<<22) /* explicitly named with typeset */
#define PM_RESTRICTED (1<<23) /* cannot be changed in restricted mode */
#define PM_UNSET (1<<24) /* has null value */
+#define PM_DEFAULTED (PM_DECLARED|PM_UNSET)
#define PM_REMOVABLE (1<<25) /* special can be removed from paramtab */
#define PM_AUTOLOAD (1<<26) /* autoloaded from module */
#define PM_NORESTORE (1<<27) /* do not restore value of local special */
@@ -2536,6 +2538,7 @@ enum {
TRANSIENTRPROMPT,
TRAPSASYNC,
TYPESETSILENT,
+ TYPESETTOUNSET,
UNSET,
VERBOSE,
VIMODE,
diff --git a/Test/D06subscript.ztst b/Test/D06subscript.ztst
index c1a8d79cf..adbd398c4 100644
--- a/Test/D06subscript.ztst
+++ b/Test/D06subscript.ztst
@@ -289,3 +289,8 @@ F:Regression test for workers/42297
>14 24
>b b
>b?rbaz foob?r
+
+ i=1,3
+ [[ ${a[$i]} = ${a[i]} ]]
+0f:Math evaluation of commas in array subscripts
+F:In math, (($i)) should be the same as ((i)), see workers/47748.
diff --git a/Test/E01options.ztst b/Test/E01options.ztst
index 415f46cd7..72749e6ab 100644
--- a/Test/E01options.ztst
+++ b/Test/E01options.ztst
@@ -1451,3 +1451,18 @@ F:If this test fails at the first unsetopt, refer to P01privileged.ztst.
0q:RM_STAR_SILENT
*>zsh: sure you want to delete all 15 files in ${PWD:h}/options.tmp \[yn\]\? ${BEL}(|n)
*>zsh: sure you want to delete (all <->|more than <->) files in / \[yn\]\? ${BEL}(|n)
+
+ () {
+ local var
+ print ${(t)var}
+ }
+0:(t) returns correct type
+>scalar-local
+
+ () {
+ readonly var
+ typeset -p var
+ }
+0:readonly with typeset -p
+F:compare E03posix.ztst
+>typeset -r var=''
diff --git a/Test/V10private.ztst b/Test/V10private.ztst
index a3a63867b..03e8259d5 100644
--- a/Test/V10private.ztst
+++ b/Test/V10private.ztst
@@ -19,14 +19,14 @@
() {
print $scalar_test
private scalar_test
- print $+scalar_test
+ typeset +m scalar_test
unset scalar_test
print $+scalar_test
}
print $scalar_test
0:basic scope hiding
>toplevel
->1
+>local scalar_test
>0
>toplevel
@@ -45,14 +45,14 @@
print $+unset_test
() {
private unset_test
- print $+unset_test
+ typeset +m unset_test
unset_test=setme
print $unset_test
}
print $+unset_test
0:variable defined only in scope
>0
->1
+>local unset_test
>setme
>0
@@ -62,13 +62,13 @@
local -Pa array_test=(in function)
() {
private array_test
- print $+array_test
+ typeset +m array_test
}
print $array_test
}
print $array_test
0:nested scope with different type, correctly restored
->1
+>local array_test
>in function
>top level
diff --git a/Test/runtests.zsh b/Test/runtests.zsh
index 562234d91..b66d579b6 100644
--- a/Test/runtests.zsh
+++ b/Test/runtests.zsh
@@ -7,7 +7,7 @@ emulate zsh
# protect from catastrophic failure of an individual test.
# We could probably do that with subshells instead.
-integer success failure skipped retval
+integer success=0 failure=0 skipped=0 retval
for file in "${(f)ZTST_testlist}"; do
$ZTST_exe +Z -f $ZTST_srcdir/ztst.zsh $file
retval=$?
diff --git a/Test/ztst.zsh b/Test/ztst.zsh
index e668ae942..a59c06dcf 100755
--- a/Test/ztst.zsh
+++ b/Test/ztst.zsh
@@ -60,7 +60,7 @@ ZTST_mainopts=(${(kv)options})
ZTST_testdir=$PWD
ZTST_testname=$1
-integer ZTST_testfailed
+integer ZTST_testfailed=0
# This is POSIX nonsense. Because of the vague feeling someone, somewhere
# may one day need to examine the arguments of "tail" using a standard
--
cgit v1.2.3
From 71b747567e350c5f849897c424ea76fd05b34ffe Mon Sep 17 00:00:00 2001
From: Bart Schaefer
Date: Sun, 18 Apr 2021 14:26:12 -0700
Subject: 47704: POSIX export and readonly ignore "-p" when parameter names
also appear
---
ChangeLog | 4 ++++
Src/builtin.c | 8 ++++++--
Src/params.c | 4 ++++
Test/B02typeset.ztst | 8 +++-----
4 files changed, 17 insertions(+), 7 deletions(-)
(limited to 'Src/params.c')
diff --git a/ChangeLog b/ChangeLog
index 7dcbbc533..c450afd05 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
2021-04-18 Bart Schaefer
+ * 47704: Src/builtin.c, Src/params.c, Test/B02typeset.ztst:
+ POSIX export and readonly ignore the "-p" option when parameter
+ names are also present.
+
* 48560: Completion/compinit, Doc/Zsh/builtins.yo,
Doc/Zsh/options.yo, Doc/Zsh/params.yo, Src/builtin.c,
Src/options.c, Src/params.c, Src/subst.c, Src/zsh.h,
diff --git a/Src/builtin.c b/Src/builtin.c
index 6d119f7a5..efa20607e 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -2615,7 +2615,12 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
int on = 0, off = 0, roff, bit = PM_ARRAY;
int i;
int returnval = 0, printflags = 0;
- int hasargs;
+ int hasargs = *argv != NULL || (assigns && firstnode(assigns));
+
+ /* POSIXBUILTINS is set for bash/ksh and both ignore -p with args */
+ if ((func == BIN_READONLY || func == BIN_EXPORT) &&
+ isset(POSIXBUILTINS) && hasargs)
+ ops->ind['p'] = 0;
/* hash -f is really the builtin `functions' */
if (OPT_ISSET(ops,'f'))
@@ -2695,7 +2700,6 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
/* -p0 treated as -p for consistency */
}
}
- hasargs = *argv != NULL || (assigns && firstnode(assigns));
if (!hasargs) {
int exclude = 0;
if (!OPT_ISSET(ops,'p')) {
diff --git a/Src/params.c b/Src/params.c
index 33bbc54f6..20dfb5b5f 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -5883,8 +5883,12 @@ printparamnode(HashNode hn, int printflags)
* don't.
*/
if (printflags & PRINT_POSIX_EXPORT) {
+ if (!(p->node.flags & PM_EXPORTED))
+ return;
printf("export ");
} else if (printflags & PRINT_POSIX_READONLY) {
+ if (!(p->node.flags & PM_READONLY))
+ return;
printf("readonly ");
} else if (locallevel && p->level >= locallevel) {
printf("typeset "); /* printf("local "); */
diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst
index e7bf93794..8b3988151 100644
--- a/Test/B02typeset.ztst
+++ b/Test/B02typeset.ztst
@@ -620,7 +620,7 @@
print ${+pbro} >&2
(typeset -g pbro=3)
(pbro=4)
- readonly -p pbro >&2 # shows up as "readonly" although unset
+ readonly -p >&2 # shows up as "readonly" although unset
typeset -gr pbro # idempotent (no error)...
print ${+pbro} >&2 # ...so still readonly...
typeset -g +r pbro # ...can't turn it off
@@ -1050,23 +1050,21 @@
$ZTST_testdir/../Src/zsh --emulate sh -f -c '
PATH=/bin; export PATH; readonly PATH
- export -p PATH
+ export -p PATH # Should be a no-op, -p ignored
typeset -p PATH
readonly -p'
0: readonly/export output for exported+readonly+special when started as sh
->export PATH=/bin
>export -r PATH=/bin
>readonly PATH=/bin
function {
emulate -L sh
MANPATH=/bin; export MANPATH; readonly MANPATH
- export -p MANPATH
+ export -p MANPATH # Should be a no-op, -p ignored
typeset -p MANPATH
readonly -p
}
0: readonly/export output for exported+readonly+tied+special after switching to sh emulation
->export MANPATH=/bin
>export -rT MANPATH manpath=( /bin )
>readonly MANPATH=/bin
--
cgit v1.2.3
From cf5c4828d1cdfd79e369a6b3323466bc961851c4 Mon Sep 17 00:00:00 2001
From: Bart Schaefer
Date: Sun, 16 May 2021 19:51:11 -0700
Subject: 48857: declare "volatile" all globals that may be modified by signal
handlers
---
ChangeLog | 6 ++++++
Src/builtin.c | 16 +++++++---------
Src/exec.c | 8 +++++---
Src/loop.c | 2 +-
Src/makepro.awk | 2 +-
Src/params.c | 6 ++++--
Src/signals.c | 16 ++++++++--------
7 files changed, 32 insertions(+), 24 deletions(-)
(limited to 'Src/params.c')
diff --git a/ChangeLog b/ChangeLog
index 6f8520db7..89b469c0f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2021-05-16 Bart Schaefer
+
+ * 48857: Src/builtin.c, Src/exec.c, Src/loop.c, Src/makepro.awk,
+ Src/params.c, Src/signals.c: declare as "volatile" all globals
+ that may be modified by signal handlers; recognize in makepro.awk
+
2021-05-16 Oliver Kiddle
* Jörg Sommer: users/26649: Completion/Unix/Command/_rake:
diff --git a/Src/builtin.c b/Src/builtin.c
index a29eb49e4..a16fddcb7 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -5635,13 +5635,16 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun
*/
/**/
-mod_export int
-exit_pending;
+mod_export volatile int exit_pending;
/* Shell level at which we exit if exit_pending */
/**/
-mod_export int
-exit_level;
+mod_export volatile int exit_level;
+
+/* we have printed a 'you have stopped (running) jobs.' message */
+
+/**/
+mod_export volatile int stopmsg;
/* break, bye, continue, exit, logout, return -- most of these take *
* one numeric argument, and the other (logout) is related to return. *
@@ -5733,11 +5736,6 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func)
return 0;
}
-/* we have printed a 'you have stopped (running) jobs.' message */
-
-/**/
-mod_export int stopmsg;
-
/* check to see if user has jobs running/stopped */
/**/
diff --git a/Src/exec.c b/Src/exec.c
index 6f09e0d9f..49ff88b80 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -84,7 +84,7 @@ int nohistsave;
/* error flag: bits from enum errflag_bits */
/**/
-mod_export int errflag;
+mod_export volatile int errflag;
/*
* State of trap return value. Value is from enum trap_state.
@@ -122,7 +122,7 @@ int subsh;
/* != 0 if we have a return pending */
/**/
-mod_export int retflag;
+mod_export volatile int retflag;
/**/
long lastval2;
@@ -1268,7 +1268,9 @@ execsimple(Estate state)
} else {
int q = queue_signal_level();
dont_queue_signals();
- if (code == WC_FUNCDEF)
+ if (errflag)
+ lv = errflag;
+ else if (code == WC_FUNCDEF)
lv = execfuncdef(state, NULL);
else
lv = (execfuncs[code - WC_CURSH])(state, 0);
diff --git a/Src/loop.c b/Src/loop.c
index aa733a2cb..db5b3e097 100644
--- a/Src/loop.c
+++ b/Src/loop.c
@@ -43,7 +43,7 @@ mod_export int contflag;
/* # of break levels */
/**/
-mod_export int breaks;
+mod_export volatile int breaks;
/**/
int
diff --git a/Src/makepro.awk b/Src/makepro.awk
index 226d3f96b..f69660531 100644
--- a/Src/makepro.awk
+++ b/Src/makepro.awk
@@ -79,7 +79,7 @@ BEGIN {
break
}
sub(/^ */, "", line)
- match(line, /^((const|enum|mod_export|static|struct|union) +)*([_0-9A-Za-z]+ +|((char|double|float|int|long|short|unsigned|void) +)+)((const|static) +)*/)
+ match(line, /^((const|enum|mod_export|static|struct|union|volatile) +)*([_0-9A-Za-z]+ +|((char|double|float|int|long|short|unsigned|void) +)+)((const|static) +)*/)
dtype = substr(line, 1, RLENGTH)
sub(/ *$/, "", dtype)
if(" " dtype " " ~ / static /)
diff --git a/Src/params.c b/Src/params.c
index 20dfb5b5f..4f6b361f9 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -98,8 +98,10 @@ char *ifs, /* $IFS */
*pwd; /* $PWD */
/**/
-mod_export
-zlong lastval, /* $? */
+mod_export volatile zlong
+ lastval; /* $? */
+/**/
+mod_export zlong
mypid, /* $$ */
lastpid, /* $! */
zterm_columns, /* $COLUMNS */
diff --git a/Src/signals.c b/Src/signals.c
index 4adf03202..2c540f38f 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -53,7 +53,7 @@ mod_export Eprog siglists[VSIGCOUNT];
/* Total count of trapped signals */
/**/
-mod_export int nsigtrapped;
+mod_export volatile int nsigtrapped;
/* Running an exit trap? */
@@ -72,20 +72,20 @@ static int exit_trap_posix;
/* Variables used by signal queueing */
/**/
-mod_export int queueing_enabled, queue_front, queue_rear;
+mod_export volatile int queueing_enabled, queue_front, queue_rear;
/**/
mod_export int signal_queue[MAX_QUEUE_SIZE];
/**/
mod_export sigset_t signal_mask_queue[MAX_QUEUE_SIZE];
#ifdef DEBUG
/**/
-mod_export int queue_in;
+mod_export volatile int queue_in;
#endif
/* Variables used by trap queueing */
/**/
-mod_export int trap_queueing_enabled, trap_queue_front, trap_queue_rear;
+mod_export volatile int trap_queueing_enabled, trap_queue_front, trap_queue_rear;
/**/
mod_export int trap_queue[MAX_QUEUE_SIZE];
@@ -672,9 +672,9 @@ zhandler(int sig)
if ((isset(PRIVILEGED) || isset(RESTRICTED)) &&
isset(INTERACTIVE) && (noerrexit & NOERREXIT_SIGNAL))
zexit(SIGINT, ZEXIT_SIGNAL);
+ errflag |= ERRFLAG_INT;
if (list_pipe || chline || simple_pline) {
breaks = loops;
- errflag |= ERRFLAG_INT;
inerrflush();
check_cursh_sig(SIGINT);
}
@@ -1266,19 +1266,19 @@ unqueue_traps(void)
/* Are we already executing a trap? */
/**/
-int intrap;
+volatile int intrap;
/* Is the current trap a function? */
/**/
-int trapisfunc;
+volatile int trapisfunc;
/*
* If the current trap is not a function, at what function depth
* did the trap get called?
*/
/**/
-int traplocallevel;
+volatile int traplocallevel;
/*
* sig is the signal number.
--
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/params.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 fa4c88ca25f587f52074698d4ff7eb263de07930 Mon Sep 17 00:00:00 2001
From: Bart Schaefer
Date: Mon, 4 Oct 2021 09:02:27 -0700
Subject: 49456: clean up detection of private params in nested scopes, update
doc
---
ChangeLog | 6 ++++++
Doc/Zsh/mod_private.yo | 9 +++++++--
Doc/Zsh/params.yo | 8 ++++++++
Src/Modules/param_private.c | 12 +++++++-----
Src/params.c | 5 +++++
Test/V10private.ztst | 6 +++---
6 files changed, 36 insertions(+), 10 deletions(-)
(limited to 'Src/params.c')
diff --git a/ChangeLog b/ChangeLog
index b116ec4f7..62470ba0b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2021-10-04 Bart Schaefer
+
+ * 49456: Doc/Zsh/mod_private.yo, Doc/Zsh/params.yo,
+ Src/Modules/param_private.c, Src/params.c, Test/V10private.ztst:
+ clean up detection of private params in nested scopes, update doc
+
2021-09-28 Oliver Kiddle
* ivan tkachenko: 49440: Doc/Zsh/expn.yo: Make double-flag
diff --git a/Doc/Zsh/mod_private.yo b/Doc/Zsh/mod_private.yo
index 78aee0acf..184fa2be8 100644
--- a/Doc/Zsh/mod_private.yo
+++ b/Doc/Zsh/mod_private.yo
@@ -10,12 +10,16 @@ ifnzman()
startitem()
findex(private)
cindex(private parameter, creating)
-item(tt(private) [ {tt(PLUS())|tt(-)}tt(AHUahlprtux) ] \
+item(tt(private) [ {tt(PLUS())|tt(-)}tt(AHUahlmrtux) ] \
[ {tt(PLUS())|tt(-)}tt(EFLRZi) [ var(n) ] ] [ var(name)[tt(=)var(value)] ... ])(
The tt(private) builtin accepts all the same options and arguments as tt(local)
(ifzman(zmanref(zshbuiltins))ifnzman(noderef(Shell Builtin Commands))) except
for the `tt(-)tt(T)' option. Tied parameters may not be made private.
+The `tt(-)tt(p)' option is presently a no-op because the state of
+private parameters cannot reliably be reloaded. This also applies
+to printing private parameters with `tt(typeset -p)'.
+
If used at the top level (outside a function scope), tt(private) creates a
normal parameter in the same manner as tt(declare) or tt(typeset). A
warning about this is printed if tt(WARN_CREATE_GLOBAL) is set
@@ -75,7 +79,8 @@ itemiz(Within any other function called by the declaring function, the
private parameter does em(NOT) hide other parameters of the same name, so
for example a global parameter of the same name is visible and may be
assigned or unset. This includes calls to anonymous functions, although
-that may also change in the future.)
+that may also change in the future. However, the private name may not be
+created outside the local scope when it was not previously declared.)
itemiz(An exported private remains in the environment of inner scopes but
appears unset for the current shell in those scopes. Generally, exporting
private parameters should be avoided.)
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index b514eb072..a88e44d4f 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -632,6 +632,14 @@ In the parameter lists that follow, the mark `' indicates that the
parameter is special. `' indicates that the parameter does not exist
when the shell initializes in tt(sh) or tt(ksh) emulation mode.
+The parameters `tt(!)', `tt(#)', `tt(*)', `tt(-)', `tt(?)', `tt(@)',
+`tt($)', `tt(ARGC)', `tt(HISTCMD)', `tt(LINENO)', `tt(PPID)',
+`tt(status)', `tt(TTYIDLE)', `tt(zsh_eval_context)',
+`tt(ZSH_EVAL_CONTEXT)', and `tt(ZSH_SUBSHELL)' are read-only and thus
+cannot be restored by the user, so they are not output by
+`tt(typeset -p)'. This also applies to many read-only parameters loaded
+from modules.
+
The following parameters are automatically set by the shell:
startitem()
diff --git a/Src/Modules/param_private.c b/Src/Modules/param_private.c
index 24545819d..c53839152 100644
--- a/Src/Modules/param_private.c
+++ b/Src/Modules/param_private.c
@@ -125,7 +125,7 @@ makeprivate(HashNode hn, UNUSED(int flags))
break;
}
/* PM_HIDE so new parameters in deeper scopes do not shadow */
- pm->node.flags |= (PM_HIDE|PM_SPECIAL|PM_REMOVABLE);
+ pm->node.flags |= (PM_HIDE|PM_SPECIAL|PM_REMOVABLE|PM_RO_BY_DESIGN);
pm->level -= 1;
}
}
@@ -171,7 +171,7 @@ bin_private(char *nam, char **args, LinkList assigns, Options ops, int func)
{
int from_typeset = 1;
int ofake = fakelevel; /* paranoia in case of recursive call */
- int hasargs = *args != NULL || (assigns && firstnode(assigns));
+ int hasargs = /* *args != NULL || */ (assigns && firstnode(assigns));
makeprivate_error = 0;
if (!OPT_ISSET(ops, 'P')) {
@@ -190,8 +190,10 @@ bin_private(char *nam, char **args, LinkList assigns, Options ops, int func)
return bin_typeset("private", args, assigns, ops, func);
}
- ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */
- if (OPT_ISSET(ops, 'p') || (!hasargs && OPT_ISSET(ops, '+'))) {
+ if (!(OPT_ISSET(ops, 'm') || OPT_ISSET(ops, '+')))
+ ops->ind['g'] = 2; /* force bin_typeset() to behave as "local" */
+ if (OPT_ISSET(ops, 'p') || OPT_ISSET(ops, 'm') ||
+ (!hasargs && OPT_ISSET(ops, '+'))) {
return bin_typeset("private", args, assigns, ops, func);
}
@@ -559,7 +561,7 @@ printprivatenode(HashNode hn, int printflags)
static struct builtin bintab[] = {
/* Copied from BUILTIN("local"), "P" added */
- BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lprtux", "P")
+ BUILTIN("private", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_private, 0, -1, 0, "AE:%F:%HL:%PR:%TUZ:%ahi:%lmprtux", "P")
};
static struct features module_features = {
diff --git a/Src/params.c b/Src/params.c
index 704aad588..b703a97ce 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -1013,6 +1013,11 @@ createparam(char *name, int flags)
(oldpm->node.flags & PM_SPECIAL) ||
/* POSIXBUILTINS horror: we need to retain 'export' flags */
(isset(POSIXBUILTINS) && (oldpm->node.flags & PM_EXPORTED))) {
+ if (oldpm->node.flags & PM_RO_BY_DESIGN) {
+ zerr("%s: can't change parameter attribute",
+ name);
+ return NULL;
+ }
oldpm->node.flags &= ~PM_UNSET;
if ((oldpm->node.flags & PM_SPECIAL) && oldpm->ename) {
Param altpm =
diff --git a/Test/V10private.ztst b/Test/V10private.ztst
index 03e8259d5..56ffbc5b4 100644
--- a/Test/V10private.ztst
+++ b/Test/V10private.ztst
@@ -116,14 +116,14 @@
}
outer () {
local -PA hash_test=(in function)
- typeset -p hash_test
+ private + hash_test
inner
}
outer
print ${(kv)hash_test}
0:private hides value from surrounding scope in nested scope
>typeset -a hash_test=( top level )
->typeset -A hash_test=( [in]=function )
+>hash_test=( [in]=function )
>typeset -g -a hash_test=( top level )
>array-local top level
>top level
@@ -246,7 +246,7 @@ F:note "typeset" rather than "private" in output from outer
1:privates are not visible in anonymous functions, part 3
>X top level
>array_test not set
-?(anon):4: array_test: attempt to assign private in nested scope
+?(anon):4: array_test: can't change parameter attribute
F:future revision will create a global with this assignment
typeset -a array_test
--
cgit v1.2.3
From 271cfc685b17938e67a8212f2df78c32989411d7 Mon Sep 17 00:00:00 2001
From: Oliver Kiddle
Date: Tue, 2 Nov 2021 21:39:52 +0100
Subject: 49534, 49539: separate watch/log functionality out into a module
---
ChangeLog | 6 +
Doc/Makefile.in | 1 +
Doc/Zsh/builtins.yo | 8 -
Doc/Zsh/compat.yo | 3 +-
Doc/Zsh/mod_watch.yo | 140 ++++++++++
Doc/Zsh/params.yo | 118 ---------
Src/Modules/watch.c | 716 ++++++++++++++++++++++++++++++++++++++++++++++++++
Src/Modules/watch.mdd | 7 +
Src/builtin.c | 1 -
Src/init.c | 1 -
Src/params.c | 16 +-
Src/utils.c | 16 --
Src/watch.c | 626 -------------------------------------------
Src/zsh.mdd | 2 +-
14 files changed, 878 insertions(+), 783 deletions(-)
create mode 100644 Doc/Zsh/mod_watch.yo
create mode 100644 Src/Modules/watch.c
create mode 100644 Src/Modules/watch.mdd
delete mode 100644 Src/watch.c
(limited to 'Src/params.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 926a1a729661e0ea31e5dcfd3965cea747d36af3 Mon Sep 17 00:00:00 2001
From: Oliver Kiddle
Date: Sun, 28 Nov 2021 20:51:41 +0100
Subject: 49602: only set LOGCHECK from the watch module
---
ChangeLog | 2 ++
Src/params.c | 3 +--
2 files changed, 3 insertions(+), 2 deletions(-)
(limited to 'Src/params.c')
diff --git a/ChangeLog b/ChangeLog
index a05322d0b..bf1f88b03 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,7 @@
2021-11-28 Oliver Kiddle
+ * 49602: Src/params.c: only set LOGCHECK from the watch module
+
* 49601: Src/hist.c, Test/W01history.ztst: don't create
ambiguous history file entries for lines ending with a backslash
diff --git a/Src/params.c b/Src/params.c
index dadf83129..970a207e4 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -758,7 +758,7 @@ split_env_string(char *env, char **name, char **value)
*/
static int dontimport(int flags)
{
- /* If explicitly marked as don't export */
+ /* If explicitly marked as don't import */
if (flags & PM_DONTIMPORT)
return 1;
/* If value already exported */
@@ -818,7 +818,6 @@ createparamtable(void)
* given them in the environment. */
opts[ALLEXPORT] = 0;
setiparam("MAILCHECK", 60);
- setiparam("LOGCHECK", 60);
setiparam("KEYTIMEOUT", 40);
setiparam("LISTMAX", 100);
/*
--
cgit v1.2.3