Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: RFC: function -T f { … }



Peter Stephenson wrote on Wed, 18 Mar 2020 19:53 +0000:
> On Wed, 2020-03-18 at 18:30 +0000, Daniel Shahaf wrote:
> > How about adding a «function -T f { … }» syntax, that defines a
> > function and enables tracing for it simultaneously?
> > 
> > I've often found myself going to the first line of a function, copying
> > its name, then going to the closing brace of the function and adding
> > a «functions -T $thefunctionsname» statement there, in order to have
> > tracing enabled during multiple «zsh -f» runs.
> > 
> > It's backwards incompatible, but not more than «printf -v» was.  
> 
> Can't see any objection, and can't see an easy workaround.

Thanks for the sanity check, Peter.

Patch series attached.

The first six patches are no-ops; the functional change is confined to
the last three patches.  Furthermore, patch #9 changes some of the lines
added by patch #8, so y'all may find it easier to review the cumulative
diff of those two patches¹ than to review them one by one.

Regarding the man page patch, I wanted to have have the documentation
of «function» hyperlink directly to the documentation of «typeset» (in
the HTML/PDF output formats), but didn't figure out how to do this.

> It does work for autoloads --- you can turn on xtrace before the
> function is loaded.

*nod* «autoload -T foo» and «autoload foo; functions -T foo» both work;
however, in my use-cases I'm normally trying to trace a function that's
defined in the middle of a *.zsh file that also defines multiple other
functions.

Cheers,

Daniel

¹ For example, using «combinediff <8-9>*» or «git diff HEAD^^».
>From 4a8d83f9b86f0ad6041640e9ad28781ad440036e Mon Sep 17 00:00:00 2001
From: Daniel Shahaf <d.s@xxxxxxxxxxxxxxxxxx>
Date: Wed, 18 Mar 2020 19:42:08 +0000
Subject: [PATCH 1/9] internal: Remove a redundant assignment.

The value is overwritten five lines below, without being read in the interim.
---
 Src/exec.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Src/exec.c b/Src/exec.c
index bca051d4f..cd014ff38 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -5166,7 +5166,6 @@ execfuncdef(Estate state, Eprog redir_prog)
 
     end = beg + WC_FUNCDEF_SKIP(state->pc[-1]);
     names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok);
-    nprg = end - beg;
     sbeg = *state->pc++;
     nstrs = *state->pc++;
     npats = *state->pc++;
>From e4ad3e10812fd54a46a91f08b7943ea18b813a55 Mon Sep 17 00:00:00 2001
From: Daniel Shahaf <d.s@xxxxxxxxxxxxxxxxxx>
Date: Wed, 18 Mar 2020 17:45:23 +0000
Subject: [PATCH 2/9] internal: Reduce some variables' visibility. No
 functional change.

---
 Src/parse.c | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/Src/parse.c b/Src/parse.c
index de1b27967..bd974a573 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -253,14 +253,13 @@ struct heredocs *hdocs;
  * to avoid a lot of string parsing and some more string duplication.
  */
 
-/**/
-int eclen, ecused, ecnpats;
-/**/
-Wordcode ecbuf;
-/**/
-Eccstr ecstrs;
-/**/
-int ecsoffs, ecssub, ecnfunc;
+static int eclen, ecused, ecnpats;
+
+static Wordcode ecbuf;
+
+static Eccstr ecstrs;
+
+static int ecsoffs, ecssub, ecnfunc;
 
 #define EC_INIT_SIZE         256
 #define EC_DOUBLE_THRESHOLD  32768
>From cce0d2cfe2d4728d4dcbc2a56cb8243ecd58a80e Mon Sep 17 00:00:00 2001
From: Daniel Shahaf <d.s@xxxxxxxxxxxxxxxxxx>
Date: Thu, 19 Mar 2020 21:14:42 +0000
Subject: [PATCH 3/9] internal: Add some comments around Eccstr. No functional
 change.

---
 Src/parse.c | 31 +++++++++++++++++++++++++++----
 Src/zsh.h   | 24 ++++++++++++++++++++++--
 2 files changed, 49 insertions(+), 6 deletions(-)

diff --git a/Src/parse.c b/Src/parse.c
index bd974a573..39d2172d4 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -253,13 +253,24 @@ struct heredocs *hdocs;
  * to avoid a lot of string parsing and some more string duplication.
  */
 
-static int eclen, ecused, ecnpats;
+/* Number of wordcodes allocated. */
+static int eclen;
+/* Number of wordcodes populated. */
+static int ecused;
+/* Number of patterns... */
+static int ecnpats;
 
 static Wordcode ecbuf;
 
 static Eccstr ecstrs;
 
-static int ecsoffs, ecssub, ecnfunc;
+static int ecsoffs, ecssub;
+
+/*
+ * ### The number of starts and ends of function definitions up to this point.
+ * Never decremented.
+ */
+static int ecnfunc;
 
 #define EC_INIT_SIZE         256
 #define EC_DOUBLE_THRESHOLD  32768
@@ -363,7 +374,11 @@ ecispace(int p, int n)
     ecadjusthere(p, n);
 }
 
-/* Add one wordcode. */
+/* 
+ * Add one wordcode.
+ *
+ * Return the index of the added wordcode.
+ */
 
 static int
 ecadd(wordcode c)
@@ -402,6 +417,7 @@ ecstrcode(char *s)
     unsigned val = hasher(s);
 
     if ((l = strlen(s) + 1) && l <= 4) {
+	/* Short string. */
 	t = has_token(s);
 	wordcode c = (t ? 3 : 2);
 	switch (l) {
@@ -412,11 +428,13 @@ ecstrcode(char *s)
 	}
 	return c;
     } else {
+	/* Long string. */
 	Eccstr p, *pp;
 	long cmp;
 
 	for (pp = &ecstrs; (p = *pp); ) {
 	    if (!(cmp = p->nfunc - ecnfunc) && !(cmp = (((long)p->hashval) - ((long)val))) && !(cmp = strcmp(p->str, s))) {
+		/* Re-use the existing string. */
 		return p->offs;
             }
 	    pp = (cmp < 0 ? &(p->left) : &(p->right));
@@ -493,7 +511,12 @@ init_parse(void)
 
 /* Build eprog. */
 
-/* careful: copy_ecstr is from arg1 to arg2, unlike memcpy */
+/*
+ * Copy the strings of s and all its descendants in the binary tree to the
+ * memory block p.
+ *
+ * careful: copy_ecstr is from arg1 to arg2, unlike memcpy
+ */
 
 static void
 copy_ecstr(Eccstr s, char *p)
diff --git a/Src/zsh.h b/Src/zsh.h
index 834142895..82d152bb8 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -832,13 +832,33 @@ struct estate {
     char *strs;			/* strings from prog */
 };
 
+/* 
+ * A binary tree of strings.
+ *
+ * Refer to the "Word code." comment at the top of Src/parse.c for details.
+ */
 typedef struct eccstr *Eccstr;
-
 struct eccstr {
+    /* Child pointers. */
     Eccstr left, right;
+
+    /* String; pointer into to estate::strs. */
     char *str;
-    wordcode offs, aoffs;
+
+    /* Wordcode of a long string, as described in the Src/parse.c comment. */
+    wordcode offs;
+
+    /* Raw memory offset of str in estate::strs. */
+    wordcode aoffs;
+
+    /* 
+     * ### The number of starts and ends of function definitions up to this point.
+     *
+     * String reuse may only happen between strings that have the same "nfunc" value.
+     */
     int nfunc;
+
+    /* Hash of str. */
     int hashval;
 };
 
>From e217f6e22e5c88ed31b1cafac0c75efa372c3ba6 Mon Sep 17 00:00:00 2001
From: Daniel Shahaf <d.s@xxxxxxxxxxxxxxxxxx>
Date: Thu, 19 Mar 2020 21:15:54 +0000
Subject: [PATCH 4/9] internal: Add some comments around wordcodes. No
 functional change.

---
 Src/parse.c | 7 +++++++
 Src/zsh.h   | 8 ++++++--
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/Src/parse.c b/Src/parse.c
index 39d2172d4..666595ef1 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -102,6 +102,13 @@ struct heredocs *hdocs;
  * The parser now produces word code, reducing memory consumption compared
  * to the nested structs we had before.
  *
+ * Word codes are represented by the "wordcode" type.
+ *
+ * Each wordcode variable consists of a "code", in the least-significant bits
+ * of the value, and "data" in the other bits.  The macros wc_code() and wc_data()
+ * access the "code" and "data" parts of a wordcode.  The macros wc_bdata() and
+ * wc_bld() build wordcodes from code and data.
+ *
  * Word code layout:
  *
  *   WC_END
diff --git a/Src/zsh.h b/Src/zsh.h
index 82d152bb8..d72c338da 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -866,8 +866,8 @@ struct eccstr {
 #define EC_DUP    1
 #define EC_DUPTOK 2
 
+/* See comment at the top of Src/parse.c for details. */
 #define WC_CODEBITS 5
-
 #define wc_code(C)   ((C) & ((wordcode) ((1 << WC_CODEBITS) - 1)))
 #define wc_data(C)   ((C) >> WC_CODEBITS)
 #define wc_bdata(D)  ((D) << WC_CODEBITS)
@@ -896,7 +896,11 @@ struct eccstr {
 #define WC_AUTOFN  20
 #define WC_TRY     21
 
-/* increment as necessary */
+/* 
+ * Increment as necessary.
+ * 
+ * If this exceeds 31, increment WC_CODEBITS.
+ */
 #define WC_COUNT   22
 
 #define WCB_END()           wc_bld(WC_END, 0)
>From edbe98a63a704b0cb2db52f3ed6473f631e04e41 Mon Sep 17 00:00:00 2001
From: Daniel Shahaf <d.s@xxxxxxxxxxxxxxxxxx>
Date: Thu, 19 Mar 2020 21:16:12 +0000
Subject: [PATCH 5/9] internal: Document the WC_FUNCDEF data layout for
 anonymous functions with arguments (follow-up to 29492)

---
 Src/parse.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/Src/parse.c b/Src/parse.c
index 666595ef1..acbf42a9c 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -175,6 +175,10 @@ struct heredocs *hdocs;
  *     - followed by number of patterns for body
  *     - followed by codes for body
  *     - followed by strings for body
+ *     - if number of names is 0, followed by:
+ *       - the offset to the end of the funcdef
+ *       - the number of arguments to the function
+ *       - the arguments to the function
  *
  *   WC_FOR
  *     - data contains type (list, ...) and offset to after body
@@ -1734,8 +1738,9 @@ par_funcdef(int *cmplx)
 
     ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p);
 
+    /* If it's an anonymous function... */
     if (num == 0) {
-	/* Unnamed function */
+	/* ... look for arguments to it. */
 	int parg = ecadd(0);
 	ecadd(0);
 	while (tok == STRING) {
@@ -2110,8 +2115,9 @@ par_simple(int *cmplx, int nr)
 
 	    ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p);
 
+	    /* If it's an anonymous function... */
 	    if (argc == 0) {
-		/* Unnamed function */
+		/* ... look for arguments to it. */
 		int parg = ecadd(0);
 		ecadd(0);
 		while (tok == STRING || IS_REDIROP(tok)) {
>From c4b6bebf6e4e454c6a13c82c916c594cf300791f Mon Sep 17 00:00:00 2001
From: Daniel Shahaf <d.s@xxxxxxxxxxxxxxxxxx>
Date: Wed, 18 Mar 2020 18:53:11 +0000
Subject: [PATCH 6/9] internal: Add some comments for orientation. No
 functional change.

---
 Src/parse.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/Src/parse.c b/Src/parse.c
index acbf42a9c..8c14aa80b 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -1676,7 +1676,7 @@ par_funcdef(int *cmplx)
     zshlex();
 
     p = ecadd(0);
-    ecadd(0);
+    ecadd(0); /* p + 1 */
 
     while (tok == STRING) {
 	if ((*tokstr == Inbrace || *tokstr == '{') &&
@@ -1688,9 +1688,9 @@ par_funcdef(int *cmplx)
 	num++;
 	zshlex();
     }
-    ecadd(0);
-    ecadd(0);
-    ecadd(0);
+    ecadd(0); /* p + num + 2 */
+    ecadd(0); /* p + num + 3 */
+    ecadd(0); /* p + num + 4 */
 
     nocorrect = 0;
     incmdpos = 1;
@@ -1728,15 +1728,15 @@ par_funcdef(int *cmplx)
 
     ecadd(WCB_END());
     ecbuf[p + num + 2] = so - oecssub;
-    ecbuf[p + num + 3] = ecsoffs - so;
-    ecbuf[p + num + 4] = ecnpats;
-    ecbuf[p + 1] = num;
+    ecbuf[p + num + 3] = ecsoffs - so; /* "length of string table" */
+    ecbuf[p + num + 4] = ecnpats; /* "number of patterns for body" */
+    ecbuf[p + 1] = num; /* "number of names" */
 
     ecnpats = onp;
     ecssub = oecssub;
     ecnfunc++;
 
-    ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p);
+    ecbuf[p] = WCB_FUNCDEF(ecused - 1 - p); /* "offset to after body" */
 
     /* If it's an anonymous function... */
     if (num == 0) {
>From 745f602c16324a77a38a6d5290c7a11610380069 Mon Sep 17 00:00:00 2001
From: Daniel Shahaf <d.s@xxxxxxxxxxxxxxxxxx>
Date: Wed, 18 Mar 2020 19:57:49 +0000
Subject: [PATCH 7/9] WC_FUNCDEF: Add a placeholder element.

---
 Config/version.mk | 4 ++--
 Src/exec.c        | 3 ++-
 Src/parse.c       | 5 +++++
 Src/text.c        | 2 +-
 4 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/Config/version.mk b/Config/version.mk
index 6540e4b98..7ecfd35ba 100644
--- a/Config/version.mk
+++ b/Config/version.mk
@@ -27,5 +27,5 @@
 # This must also serve as a shell script, so do not add spaces around the
 # `=' signs.
 
-VERSION=5.8.0.1-dev
-VERSION_DATE='February 15, 2020'
+VERSION=5.8.0.2-dev
+VERSION_DATE='March 19, 2020'
diff --git a/Src/exec.c b/Src/exec.c
index cd014ff38..3c3fcfa3e 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -5169,6 +5169,7 @@ execfuncdef(Estate state, Eprog redir_prog)
     sbeg = *state->pc++;
     nstrs = *state->pc++;
     npats = *state->pc++;
+    (void) *state->pc++;
 
     nprg = (end - state->pc);
     plen = nprg * sizeof(wordcode);
@@ -6138,7 +6139,7 @@ stripkshdef(Eprog prog, char *name)
 	int sbeg = pc[2], nstrs = pc[3], nprg, npats = pc[4], plen, len, i;
 	Patprog *pp;
 
-	pc += 5;
+	pc += 6;
 
 	nprg = end - pc;
 	plen = nprg * sizeof(wordcode);
diff --git a/Src/parse.c b/Src/parse.c
index 8c14aa80b..eb32d4faf 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -173,6 +173,7 @@ struct heredocs *hdocs;
  *     - followed by offset to first string
  *     - followed by length of string table
  *     - followed by number of patterns for body
+ *     - followed by a placeholder
  *     - followed by codes for body
  *     - followed by strings for body
  *     - if number of names is 0, followed by:
@@ -1691,6 +1692,7 @@ par_funcdef(int *cmplx)
     ecadd(0); /* p + num + 2 */
     ecadd(0); /* p + num + 3 */
     ecadd(0); /* p + num + 4 */
+    ecadd(0); /* p + num + 5 */
 
     nocorrect = 0;
     incmdpos = 1;
@@ -1730,6 +1732,7 @@ par_funcdef(int *cmplx)
     ecbuf[p + num + 2] = so - oecssub;
     ecbuf[p + num + 3] = ecsoffs - so; /* "length of string table" */
     ecbuf[p + num + 4] = ecnpats; /* "number of patterns for body" */
+    ecbuf[p + num + 5] = 0;
     ecbuf[p + 1] = num; /* "number of names" */
 
     ecnpats = onp;
@@ -2053,6 +2056,7 @@ par_simple(int *cmplx, int nr)
 	    ecadd(0);
 	    ecadd(0);
 	    ecadd(0);
+	    ecadd(0);
 
 	    ecnfunc++;
 	    ecssub = so = ecsoffs;
@@ -2108,6 +2112,7 @@ par_simple(int *cmplx, int nr)
 	    ecbuf[p + argc + 2] = so - oecssub;
 	    ecbuf[p + argc + 3] = ecsoffs - so;
 	    ecbuf[p + argc + 4] = ecnpats;
+	    ecbuf[p + argc + 5] = 0;
 
 	    ecnpats = onp;
 	    ecssub = oecssub;
diff --git a/Src/text.c b/Src/text.c
index 69530ae79..4bf88f2e2 100644
--- a/Src/text.c
+++ b/Src/text.c
@@ -600,7 +600,7 @@ gettext2(Estate state)
 		    n->u._funcdef.end = end;
 		    n->u._funcdef.nargs = nargs;
 		    state->strs += *state->pc;
-		    state->pc += 3;
+		    state->pc += 4;
 		}
 	    } else {
 		state->strs = s->u._funcdef.strs;
>From c7b30b127659fce57329dbf1b646021454d0f5e3 Mon Sep 17 00:00:00 2001
From: Daniel Shahaf <d.s@xxxxxxxxxxxxxxxxxx>
Date: Thu, 19 Mar 2020 18:00:16 +0000
Subject: [PATCH 8/9] Add the 'function -T' syntax.

Config/version.mk was bumped in the previous commit.
---
 Doc/Zsh/grammar.yo  | 13 ++++++++++++-
 README              |  7 +++++++
 Src/exec.c          |  8 +++++---
 Src/parse.c         | 11 +++++++++--
 Test/E02xtrace.ztst | 25 +++++++++++++++++++++++++
 5 files changed, 58 insertions(+), 6 deletions(-)

diff --git a/Doc/Zsh/grammar.yo b/Doc/Zsh/grammar.yo
index e028c8512..fa0d72ff5 100644
--- a/Doc/Zsh/grammar.yo
+++ b/Doc/Zsh/grammar.yo
@@ -357,7 +357,7 @@ deliberately left unspecified, because historically there was a mismatch between
 the documented and implemented behaviours.  Cf. 20076, 21734/21735, 45075.)
 )
 findex(function)
-xitem(tt(function) var(word) ... [ tt(()) ] [ var(term) ] tt({) var(list) tt(}))
+xitem(tt(function) [ tt(-T) ] var(word) ... [ tt(()) ] [ var(term) ] tt({) var(list) tt(}))
 xitem(var(word) ... tt(()) [ var(term) ] tt({) var(list) tt(}))
 item(var(word) ... tt(()) [ var(term) ] var(command))(
 where var(term) is one or more newline or tt(;).
@@ -367,6 +367,17 @@ are usually only useful for setting traps.
 The body of the function is the var(list) between
 the tt({) and tt(}).  See noderef(Functions).
 
+The options of tt(function) have the following meanings:
+
+startitem()
+item(-T)(
+Enable tracing for this function, as though with tt(functions -T).  See the
+documentation of the tt(-f) option to the tt(typeset) builtin, in
+ifzman(zmanref(zshbuiltins))\
+ifnzman(noderef(Shell Builtin Commands)).
+)
+enditem()
+
 If the option tt(SH_GLOB) is set for compatibility with other shells, then
 whitespace may appear between the left and right parentheses when
 there is a single var(word);  otherwise, the parentheses will be treated as
diff --git a/README b/README
index 2bd5c2179..ae4f788bc 100644
--- a/README
+++ b/README
@@ -43,6 +43,13 @@ name of an external command.  Now it may also be a shell function.  Normal
 command word precedece rules apply, so if you have a function and a command
 with the same name, the function will be used.
 
+The syntax "function -T { ... }" used to define a function named "-T".
+It now defines an anonymous function with single-level tracing enabled ---
+same as "function f { ... }; functions -T f; f", but without naming the
+function.  The syntax "function -T foo { ... }" is similarly affected: it
+now defines a function "foo" with tracing enabled; previously it defined
+two functions, named "-T" and "foo" (see the MULTI_FUNC_DEF option).
+
 Incompatibilities since 5.7.1
 -----------------------------
 
diff --git a/Src/exec.c b/Src/exec.c
index 3c3fcfa3e..2b8e2167f 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -5157,23 +5157,25 @@ execfuncdef(Estate state, Eprog redir_prog)
 {
     Shfunc shf;
     char *s = NULL;
-    int signum, nprg, sbeg, nstrs, npats, len, plen, i, htok = 0, ret = 0;
+    int signum, nprg, sbeg, nstrs, npats, do_tracing, len, plen, i, htok = 0, ret = 0;
     int anon_func = 0;
     Wordcode beg = state->pc, end;
     Eprog prog;
     Patprog *pp;
     LinkList names;
+    int tracing_flags;
 
     end = beg + WC_FUNCDEF_SKIP(state->pc[-1]);
     names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok);
     sbeg = *state->pc++;
     nstrs = *state->pc++;
     npats = *state->pc++;
-    (void) *state->pc++;
+    do_tracing = *state->pc++;
 
     nprg = (end - state->pc);
     plen = nprg * sizeof(wordcode);
     len = plen + (npats * sizeof(Patprog)) + nstrs;
+    tracing_flags = do_tracing ? PM_TAGGED_LOCAL : 0;
 
     if (htok && names) {
 	execsubst(names);
@@ -5223,7 +5225,7 @@ execfuncdef(Estate state, Eprog redir_prog)
 
 	shf = (Shfunc) zalloc(sizeof(*shf));
 	shf->funcdef = prog;
-	shf->node.flags = 0;
+	shf->node.flags = tracing_flags;
 	/* No dircache here, not a directory */
 	shf->filename = ztrdup(scriptfilename);
 	shf->lineno =
diff --git a/Src/parse.c b/Src/parse.c
index eb32d4faf..dafc8cca6 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -173,7 +173,7 @@ struct heredocs *hdocs;
  *     - followed by offset to first string
  *     - followed by length of string table
  *     - followed by number of patterns for body
- *     - followed by a placeholder
+ *     - followed by an integer indicating tracing status
  *     - followed by codes for body
  *     - followed by strings for body
  *     - if number of names is 0, followed by:
@@ -1670,6 +1670,7 @@ par_funcdef(int *cmplx)
     int oecused = ecused, num = 0, onp, p, c = 0;
     int so, oecssub = ecssub;
     zlong oldlineno = lineno;
+    int do_tracing = 0;
 
     lineno = 0;
     nocorrect = 1;
@@ -1679,6 +1680,12 @@ par_funcdef(int *cmplx)
     p = ecadd(0);
     ecadd(0); /* p + 1 */
 
+    if (tok == STRING && tokstr[0] == Dash &&
+	tokstr[1] == 'T' && !tokstr[2]) {
+	++do_tracing;
+	zshlex();
+    }
+
     while (tok == STRING) {
 	if ((*tokstr == Inbrace || *tokstr == '{') &&
 	    !tokstr[1]) {
@@ -1732,7 +1739,7 @@ par_funcdef(int *cmplx)
     ecbuf[p + num + 2] = so - oecssub;
     ecbuf[p + num + 3] = ecsoffs - so; /* "length of string table" */
     ecbuf[p + num + 4] = ecnpats; /* "number of patterns for body" */
-    ecbuf[p + num + 5] = 0;
+    ecbuf[p + num + 5] = do_tracing;
     ecbuf[p + 1] = num; /* "number of names" */
 
     ecnpats = onp;
diff --git a/Test/E02xtrace.ztst b/Test/E02xtrace.ztst
index 795f7e616..d72b2d000 100644
--- a/Test/E02xtrace.ztst
+++ b/Test/E02xtrace.ztst
@@ -180,3 +180,28 @@
 >	# traced
 >	echo inner
 >}
+
+ function -T { echo traced anonymous function }
+ functions -- -T # no output
+1:define traced function: anonymous function
+?+(anon):0> echo traced anonymous function
+>traced anonymous function
+
+ function -T f { echo traced named function }
+ functions -- -T # no output
+ functions f
+ f
+0:define traced function: named function
+>f () {
+>	# traced
+>	echo traced named function
+>}
+?+f:0> echo traced named function
+>traced named function
+
+ function -T -T { echo trace function literally named "-T" }
+ -T
+0:define traced function: parse test
+?+-T:0> echo trace function literally named -T
+>trace function literally named -T
+
>From 0a0e1d5d3191e2a2ef9217ce66c87de60cefa3bf Mon Sep 17 00:00:00 2001
From: Daniel Shahaf <d.s@xxxxxxxxxxxxxxxxxx>
Date: Thu, 19 Mar 2020 18:11:39 +0000
Subject: [PATCH 9/9] Add end-of-options guard support to 'function -T'.

---
 README              | 23 +++++++++++++++++------
 Src/parse.c         | 16 ++++++++++++----
 Test/E02xtrace.ztst | 22 +++++++++++++++++++++-
 3 files changed, 50 insertions(+), 11 deletions(-)

diff --git a/README b/README
index ae4f788bc..d08440ce1 100644
--- a/README
+++ b/README
@@ -43,12 +43,23 @@ name of an external command.  Now it may also be a shell function.  Normal
 command word precedece rules apply, so if you have a function and a command
 with the same name, the function will be used.
 
-The syntax "function -T { ... }" used to define a function named "-T".
-It now defines an anonymous function with single-level tracing enabled ---
-same as "function f { ... }; functions -T f; f", but without naming the
-function.  The syntax "function -T foo { ... }" is similarly affected: it
-now defines a function "foo" with tracing enabled; previously it defined
-two functions, named "-T" and "foo" (see the MULTI_FUNC_DEF option).
+The "function" reserved word, used to define functions, gained a new -T option.
+That affects syntaxes such as:
+
+1. "function -T { ... }".  It used to define a function named "-T".  It
+now defines and executes an anonymous function with single-level tracing
+enabled --- same as "function f { ... }; functions -T f; f", but without
+naming the function.
+
+2. "function -T foo { ... }".  It used to define two functions, named "-T"
+and "foo" (see the MULTI_FUNC_DEF option).  It now defines a function
+"foo" with tracing enabled.
+
+3. "function -- { ... }".  It used to define a function named "--".  It
+now defines and executes an anonymous function.  The "--" is taken to be
+an end-of-options guard (same as "ls --").
+
+The sh-compatible function definition syntax, "f() { ... }", is unchanged.
 
 Incompatibilities since 5.7.1
 -----------------------------
diff --git a/Src/parse.c b/Src/parse.c
index dafc8cca6..b3650526b 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -1680,10 +1680,18 @@ par_funcdef(int *cmplx)
     p = ecadd(0);
     ecadd(0); /* p + 1 */
 
-    if (tok == STRING && tokstr[0] == Dash &&
-	tokstr[1] == 'T' && !tokstr[2]) {
-	++do_tracing;
-	zshlex();
+    /* Consume an initial (-T), (--), or (-T --).
+     * Anything else is a literal function name.
+     */
+    if (tok == STRING && tokstr[0] == Dash) {
+	if (tokstr[1] == 'T' && !tokstr[2]) {
+	    ++do_tracing;
+	    zshlex();
+	}
+	if (tok == STRING && tokstr[0] == Dash &&
+	    tokstr[1] == Dash && !tokstr[2]) {
+	    zshlex();
+	}
     }
 
     while (tok == STRING) {
diff --git a/Test/E02xtrace.ztst b/Test/E02xtrace.ztst
index d72b2d000..8b9cc89a8 100644
--- a/Test/E02xtrace.ztst
+++ b/Test/E02xtrace.ztst
@@ -199,9 +199,29 @@
 ?+f:0> echo traced named function
 >traced named function
 
- function -T -T { echo trace function literally named "-T" }
+ function -T -- -T { echo trace function literally named "-T" }
  -T
+ function -T -- { echo trace anonymous function }
+ functions -- -- # no output
 0:define traced function: parse test
 ?+-T:0> echo trace function literally named -T
 >trace function literally named -T
+?+(anon):0> echo trace anonymous function
+>trace anonymous function
+
+ function -- g { echo g }
+ g
+ function -- { echo anonymous }
+ functions -- -- # no output
+0:function end-of-"options" syntax, #1
+>g
+>anonymous
+
+ function -- -T { echo runs }
+ functions -- -- # no output
+ echo the definition didn\'t execute it
+ -T
+0:function end-of-"options" syntax, #2
+>the definition didn't execute it
+>runs
 


Messages sorted by: Reverse Date, Date, Thread, Author