summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/builtin.c50
-rw-r--r--Src/hashtable.c74
-rw-r--r--Src/pattern.c13
-rw-r--r--Src/text.c46
-rw-r--r--Src/utils.c43
-rw-r--r--Src/zsh.h9
6 files changed, 196 insertions, 39 deletions
diff --git a/Src/builtin.c b/Src/builtin.c
index 4b081468d..0d1d00ec3 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -72,7 +72,7 @@ static struct builtin builtins[] =
     BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRt:W", NULL),
     BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
     BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"),
-    BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUz", NULL),
+    BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUx:z", NULL),
     BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
     BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
     BUILTIN("hash", BINF_MAGICEQUALS, bin_hash, 0, -1, 0, "Ldfmrv", NULL),
@@ -128,9 +128,9 @@ static struct builtin builtins[] =
     BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, 0, "fmv", NULL),
     BUILTIN("unsetopt", 0, bin_setopt, 0, -1, BIN_UNSETOPT, NULL, NULL),
     BUILTIN("wait", 0, bin_fg, 0, -1, BIN_WAIT, NULL, NULL),
-    BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSw", NULL),
-    BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsSw", "ca"),
-    BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsSw", "c"),
+    BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsSwx:", NULL),
+    BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsSwx:", "ca"),
+    BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsSwx:", "c"),
     BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "AFRILP:abcfdilmpue", NULL),
     BUILTIN("zcompile", 0, bin_zcompile, 0, -1, 0, "tUMRcmzka", NULL),
 };
@@ -2749,7 +2749,7 @@ bin_functions(char *name, char **argv, Options ops, int func)
     Patprog pprog;
     Shfunc shf;
     int i, returnval = 0;
-    int on = 0, off = 0, pflags = 0, roff;
+    int on = 0, off = 0, pflags = 0, roff, expand = 0;
 
     /* Do we have any flags defined? */
     if (OPT_PLUS(ops,'u'))
@@ -2785,11 +2785,23 @@ bin_functions(char *name, char **argv, Options ops, int func)
     }
 
     if ((off & PM_UNDEFINED) || (OPT_ISSET(ops,'k') && OPT_ISSET(ops,'z')) ||
+	(OPT_ISSET(ops,'x') && !OPT_HASARG(ops,'x')) ||
 	(OPT_MINUS(ops,'X') && (OPT_ISSET(ops,'m') || *argv || !scriptname))) {
 	zwarnnam(name, "invalid option(s)");
 	return 1;
     }
 
+    if (OPT_ISSET(ops,'x')) {
+	char *eptr;
+	expand = (int)zstrtol(OPT_ARG(ops,'x'), &eptr, 10);
+	if (*eptr) {
+	    zwarnnam(name, "number expected after -x");
+	    return 1;
+	}
+	if (expand == 0)	/* no indentation at all */
+	    expand = -1;
+    }
+
     if (OPT_PLUS(ops,'f') || roff || OPT_ISSET(ops,'+'))
 	pflags |= PRINT_NAMEONLY;
 
@@ -2948,8 +2960,8 @@ bin_functions(char *name, char **argv, Options ops, int func)
 	} else {
 	    if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u'))
 		on &= ~PM_UNDEFINED;
-	    scanhashtable(shfunctab, 1, on|off, DISABLED, shfunctab->printnode,
-			  pflags);
+	    scanshfunc(1, on|off, DISABLED, shfunctab->printnode,
+		       pflags, expand);
 	}
 	unqueue_signals();
 	return ret;
@@ -2965,8 +2977,8 @@ bin_functions(char *name, char **argv, Options ops, int func)
 		/* with no options, just print all functions matching the glob pattern */
 		queue_signals();
 		if (!(on|off) && !OPT_ISSET(ops,'X')) {
-		    scanmatchtable(shfunctab, pprog, 1, 0, DISABLED,
-				   shfunctab->printnode, pflags);
+		    scanmatchshfunc(pprog, 1, 0, DISABLED,
+				   shfunctab->printnode, pflags, expand);
 		} else {
 		    /* apply the options to all functions matching the glob pattern */
 		    for (i = 0; i < shfunctab->hsize; i++) {
@@ -3008,7 +3020,7 @@ bin_functions(char *name, char **argv, Options ops, int func)
 		    returnval = 1;
 	    } else
 		/* no flags, so just print */
-		shfunctab->printnode(&shf->node, pflags);
+		printshfuncexpand(&shf->node, pflags, expand);
 	} else if (on & PM_UNDEFINED) {
 	    int signum = -1, ok = 1;
 
@@ -3222,6 +3234,7 @@ bin_whence(char *nam, char **argv, Options ops, int func)
     int aliasflags;
     int csh, all, v, wd;
     int informed = 0;
+    int expand = 0;
     char *cnam, **allmatched = 0;
 
     /* Check some option information */
@@ -3230,6 +3243,17 @@ bin_whence(char *nam, char **argv, Options ops, int func)
     all = OPT_ISSET(ops,'a');
     wd  = OPT_ISSET(ops,'w');
 
+    if (OPT_ISSET(ops,'x')) {
+	char *eptr;
+	expand = (int)zstrtol(OPT_ARG(ops,'x'), &eptr, 10);
+	if (*eptr) {
+	    zwarnnam(nam, "number expected after -x");
+	    return 1;
+	}
+	if (expand == 0)	/* no indentation at all */
+	    expand = -1;
+    }
+
     if (OPT_ISSET(ops,'w'))
 	printflags |= PRINT_WHENCE_WORD;
     else if (OPT_ISSET(ops,'c'))
@@ -3286,8 +3310,8 @@ bin_whence(char *nam, char **argv, Options ops, int func)
 
 		/* and shell functions... */
 		informed +=
-		scanmatchtable(shfunctab, pprog, 1, 0, DISABLED,
-			       shfunctab->printnode, printflags);
+		scanmatchshfunc(pprog, 1, 0, DISABLED,
+			       shfunctab->printnode, printflags, expand);
 
 		/* and builtins. */
 		informed +=
@@ -3342,7 +3366,7 @@ bin_whence(char *nam, char **argv, Options ops, int func)
 	    }
 	    /* Look for shell function */
 	    if ((hn = shfunctab->getnode(shfunctab, *argv))) {
-		shfunctab->printnode(hn, printflags);
+		printshfuncexpand(hn, printflags, expand);
 		informed = 1;
 		if (!all)
 		    continue;
diff --git a/Src/hashtable.c b/Src/hashtable.c
index ab381cc6a..2b5524999 100644
--- a/Src/hashtable.c
+++ b/Src/hashtable.c
@@ -937,13 +937,17 @@ printshfuncnode(HashNode hn, int printflags)
  
     quotedzputs(f->node.nam, stdout);
     if (f->funcdef || f->node.flags & PM_UNDEFINED) {
-	printf(" () {\n\t");
-	if (f->node.flags & PM_UNDEFINED)
-	    printf("%c undefined\n\t", hashchar);
-	else
+	printf(" () {\n");
+	zoutputtab(stdout);
+	if (f->node.flags & PM_UNDEFINED) {
+	    printf("%c undefined\n", hashchar);
+	    zoutputtab(stdout);
+	} else
 	    t = getpermtext(f->funcdef, NULL, 1);
-	if (f->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL))
-	    printf("%c traced\n\t", hashchar);
+	if (f->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL)) {
+	    printf("%c traced\n", hashchar);
+	    zoutputtab(stdout);
+	}
 	if (!t) {
 	    char *fopt = "UtTkz";
 	    int flgs[] = {
@@ -959,11 +963,12 @@ printshfuncnode(HashNode hn, int printflags)
 	    zputs(t, stdout);
 	    zsfree(t);
 	    if (f->funcdef->flags & EF_RUN) {
-		printf("\n\t");
+		printf("\n");
+		zoutputtab(stdout);
 		quotedzputs(f->node.nam, stdout);
 		printf(" \"$@\"");
 	    }
-	}   
+	}
 	printf("\n}");
     } else {
 	printf(" () { }");
@@ -979,6 +984,59 @@ printshfuncnode(HashNode hn, int printflags)
     putchar('\n');
 }
 
+/*
+ * Wrap scanmatchtable for shell functions with optional
+ * expansion of leading tabs.
+ * expand = 0 is standard: use hard tabs.
+ * expand > 0 uses that many spaces.
+ * expand < 0 uses no identation.
+ *
+ * Note this function and the following two are called with
+ * interrupts queued, so saving and restoring text_expand_tabs
+ * is safe.
+ */
+
+/**/
+mod_export int
+scanmatchshfunc(Patprog pprog, int sorted, int flags1, int flags2,
+		ScanFunc scanfunc, int scanflags, int expand)
+{
+    int ret, save_expand;
+
+    save_expand = text_expand_tabs;
+    text_expand_tabs = expand;
+    ret = scanmatchtable(shfunctab, pprog, sorted, flags1, flags2,
+			scanfunc, scanflags);
+    text_expand_tabs = save_expand;
+
+    return ret;
+}
+
+/* Wrap scanhashtable to expand tabs for shell functions */
+
+/**/
+mod_export int
+scanshfunc(int sorted, int flags1, int flags2,
+	      ScanFunc scanfunc, int scanflags, int expand)
+{
+    return scanmatchshfunc(NULL, sorted, flags1, flags2,
+			   scanfunc, scanflags, expand);
+}
+
+/* Wrap shfunctab->printnode to expand tabs */
+
+/**/
+mod_export void
+printshfuncexpand(HashNode hn, int printflags, int expand)
+{
+    int save_expand;
+
+    save_expand = text_expand_tabs;
+    text_expand_tabs = expand;
+    shfunctab->printnode(hn, printflags);
+    text_expand_tabs = save_expand;
+}
+
 /**************************************/
 /* Reserved Word Hash Table Functions */
 /**************************************/
diff --git a/Src/pattern.c b/Src/pattern.c
index 4e5e8a110..7e07548f9 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -2202,20 +2202,15 @@ pattryrefs(Patprog prog, char *string, int stringlen, int unmetalen,
 		if ((patglobflags & GF_MATCHREF) &&
 		    !(patflags & PAT_FILE)) {
 		    char *str = ztrduppfx(patinstart, patinlen);
-		    char *ptr = patinstart;
-		    int mlen = 0;
+		    int mlen;
 
 		    /*
 		     * Count the characters.  We're not using CHARSUB()
-		     * because the string is still metafied.  We're
-		     * not using mb_metastrlen() because that expects
-		     * the string to be null terminated.
+		     * because the string is still metafied.
 		     */
 		    MB_METACHARINIT();
-		    while (ptr < patinstart + patinlen) {
-			mlen++;
-			ptr += MB_METACHARLEN(ptr);
-		    }
+		    mlen = MB_METASTRLEN2END(patinstart, 0,
+					     patinstart + patinlen);
 
 		    setsparam("MATCH", str);
 		    setiparam("MBEGIN",
diff --git a/Src/text.c b/Src/text.c
index 958303c68..850879699 100644
--- a/Src/text.c
+++ b/Src/text.c
@@ -30,6 +30,16 @@
 #include "zsh.mdh"
 #include "text.pro"
 
+/*
+ * If non-zero, expand syntactically significant leading tabs in text
+ * to this number of spaces.
+ *
+ * If negative, don't output leading whitespace at all.
+ */
+
+/**/
+int text_expand_tabs;
+
 static char *tptr, *tbuf, *tlim, *tpending;
 static int tsiz, tindent, tnewlins, tjob;
 
@@ -156,8 +166,16 @@ taddnl(int no_semicolon)
     if (tnewlins) {
 	tdopending();
 	taddchr('\n');
-	for (t0 = 0; t0 != tindent; t0++)
-	    taddchr('\t');
+	for (t0 = 0; t0 != tindent; t0++) {
+	    if (text_expand_tabs >= 0) {
+		if (text_expand_tabs) {
+		    int t1;
+		    for (t1 = 0; t1 < text_expand_tabs; t1++)
+			taddchr(' ');
+		} else
+		    taddchr('\t');
+	    }
+	}
     } else if (no_semicolon) {
 	taddstr(" ");
     } else {
@@ -165,6 +183,30 @@ taddnl(int no_semicolon)
     }
 }
 
+/*
+ * Output a tab that may be expanded as part of a leading set.
+ * Note this is not part of the text framework; it's for
+ * code that needs to output its own tabs that are to be
+ * consistent with those from getpermtext().
+ *
+ * Note these tabs are only expected to be useful at the
+ * start of the line, so we make no attempt to count columns.
+ */
+
+/**/
+void
+zoutputtab(FILE *outf)
+{
+    if (text_expand_tabs < 0)
+	return;
+    if (text_expand_tabs) {
+	int i;
+	for (i = 0; i < text_expand_tabs; i++)
+	    fputc(' ', outf);
+    } else
+	fputc('\t', outf);
+}
+
 /* get a permanent textual representation of n */
 
 /**/
diff --git a/Src/utils.c b/Src/utils.c
index 7409dc876..c33c16d5a 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -4471,9 +4471,37 @@ ztrlen(char const *s)
     for (l = 0; *s; l++) {
 	if (*s++ == Meta) {
 #ifdef DEBUG
-	    if (! *s)
+	    if (! *s) {
 		fprintf(stderr, "BUG: unexpected end of string in ztrlen()\n");
-	    else
+		break;
+	    } else
+#endif
+	    s++;
+	}
+    }
+    return l;
+}
+
+#ifndef MULTIBYTE_SUPPORT
+/*
+ * ztrlen() but with explicit end point for non-null-terminated
+ * segments.  eptr may not be NULL.
+ */
+
+/**/
+mod_export int
+ztrlenend(char const *s, char const *eptr)
+{
+    int l;
+
+    for (l = 0; s < eptr; l++) {
+	if (*s++ == Meta) {
+#ifdef DEBUG
+	    if (! *s) {
+		fprintf(stderr,
+			"BUG: unexpected end of string in ztrlenend()\n");
+		break;
+	    } else
 #endif
 	    s++;
 	}
@@ -4481,6 +4509,8 @@ ztrlen(char const *s)
     return l;
 }
 
+#endif /* MULTIBYTE_SUPPORT */
+
 /* Subtract two pointers in a metafied string. */
 
 /**/
@@ -4879,11 +4909,16 @@ mb_metacharlenconv(const char *s, wint_t *wcp)
  * If width is 1, return total character width rather than number.
  * If width is greater than 1, return 1 if character has non-zero width,
  * else 0.
+ *
+ * Ends if either *ptr is '\0', the normal case (eptr may be NULL for
+ * this), or ptr is eptr (i.e.  *eptr is where the null would be if null
+ * terminated) for strings not delimited by nulls --- note these are
+ * still metafied.
  */
 
 /**/
 mod_export int
-mb_metastrlen(char *ptr, int width)
+mb_metastrlenend(char *ptr, int width, char *eptr)
 {
     char inchar, *laststart;
     size_t ret;
@@ -4898,7 +4933,7 @@ mb_metastrlen(char *ptr, int width)
     num = num_in_char = 0;
 
     memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
-    while (*ptr) {
+    while (*ptr && !(eptr && ptr >= eptr)) {
 	if (*ptr == Meta)
 	    inchar = *++ptr ^ 32;
 	else
diff --git a/Src/zsh.h b/Src/zsh.h
index f6e08e28d..c88c2e739 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2926,9 +2926,11 @@ enum {
 typedef wint_t convchar_t;
 #define MB_METACHARLENCONV(str, cp)	mb_metacharlenconv((str), (cp))
 #define MB_METACHARLEN(str)	mb_metacharlenconv(str, NULL)
-#define MB_METASTRLEN(str)	mb_metastrlen(str, 0)
-#define MB_METASTRWIDTH(str)	mb_metastrlen(str, 1)
-#define MB_METASTRLEN2(str, widthp)	mb_metastrlen(str, widthp)
+#define MB_METASTRLEN(str)	mb_metastrlenend(str, 0, NULL)
+#define MB_METASTRWIDTH(str)	mb_metastrlenend(str, 1, NULL)
+#define MB_METASTRLEN2(str, widthp)	mb_metastrlenend(str, widthp, NULL)
+#define MB_METASTRLEN2END(str, widthp, eptr)	\
+    mb_metastrlenend(str, widthp, eptr)
 
 /*
  * We replace broken implementations with one that uses Unicode
@@ -3011,6 +3013,7 @@ typedef int convchar_t;
 #define MB_METASTRLEN(str)	ztrlen(str)
 #define MB_METASTRWIDTH(str)	ztrlen(str)
 #define MB_METASTRLEN2(str, widthp)	ztrlen(str)
+#define MB_METASTRLEN2END(str, widthp, eptr)	ztrlenend(str, eptr)
 
 #define WCWIDTH_WINT(c)	(1)