From 2abba7243a736a2fc626f3cc917d8a67014d4d20 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 5 Jun 2015 11:21:22 +0100 Subject: 35386: expand tabs where useful in builtins outputing function. Also add to zed -f. Option is -x . --- ChangeLog | 8 ++++++ Doc/Zsh/builtins.yo | 25 ++++++++++++++---- Doc/Zsh/contrib.yo | 9 ++++--- Functions/Misc/zed | 17 +++++++++--- Src/builtin.c | 50 ++++++++++++++++++++++++++---------- Src/hashtable.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++------ Src/pattern.c | 13 +++------- Src/text.c | 46 +++++++++++++++++++++++++++++++-- Src/utils.c | 43 ++++++++++++++++++++++++++++--- Src/zsh.h | 9 ++++--- 10 files changed, 243 insertions(+), 51 deletions(-) diff --git a/ChangeLog b/ChangeLog index 24ec700f9..b6c233d8c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2015-06-05 Peter Stephenson + + * 35386: Doc/Zsh/builtins.yo, Doc/Zsh/contrib.yo, + Functions/Misc/zed, Src/builtin.c, Src/hashtable.c, + Src/pattern.c, Src/text.c, Src/utils.c, Src/zsh.h: + expand tabs for function output in functions, whence, where, + which and also zed -f using -x num option. + 2015-06-03 Oliver Kiddle * 35360 (replacing 35357): configure.ac, Src/Modules/zpty.c: diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 6fa603ac8..53b668214 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -791,11 +791,18 @@ Equivalent to tt(typeset -E), except that options irrelevant to floating point numbers are not permitted. ) findex(functions) -xitem(tt(functions) [ {tt(PLUS())|tt(-)}tt(UkmtTuz) ] [ var(name) ... ]) +xitem(tt(functions) [ {tt(PLUS())|tt(-)}tt(UkmtTuz) ] [ tt(-x) var(num) ] [ var(name) ... ]) xitem(tt(functions -M) var(mathfn) [ var(min) [ var(max) [ var(shellfn) ] ] ]) xitem(tt(functions -M) [ tt(-m) var(pattern) ... ]) item(tt(functions +M) [ tt(-m) ] var(mathfn) ... )( -Equivalent to tt(typeset -f), with the exception of the tt(-M) option. +Equivalent to tt(typeset -f), with the exception of the tt(-x) and +tt(-M) options. + +The tt(-x) option indicates that any functions output will have +each leading tab for indentation, added by the shell to show syntactic +structure, expanded to the given number var(num) of spaces. var(num) +can also be 0 to suppress all indentation. + Use of the tt(-M) option may not be combined with any of the options handled by tt(typeset -f). @@ -1927,6 +1934,9 @@ function is first referenced; see noderef(Functions). The tt(-k) and tt(-z) flags make the function be loaded using ksh-style or zsh-style autoloading respectively. If neither is given, the setting of the tt(KSH_AUTOLOAD) option determines how the function is loaded. + +Note that the builtin tt(functions) provides the same basic capabilities +as tt(typeset -f) but gives access to a few extra options. ) item(tt(-h))( Hide: only useful for special parameters (those marked `' in the table in @@ -2180,7 +2190,7 @@ the user is potentially interested in both, so this problem is intrinsic to process IDs. ) findex(whence) -item(tt(whence) [ tt(-vcwfpamsS) ] var(name) ...)( +item(tt(whence) [ tt(-vcwfpamsS) ] [ tt(-x) var(num) ] var(name) ...)( For each var(name), indicate how it would be interpreted if used as a command name. @@ -2233,14 +2243,19 @@ As tt(-s), but if the pathname had to be resolved by following multiple symlinks, the intermediate steps are printed, too. The symlink resolved at each step might be anywhere in the path. ) +item(tt(-x) var(num))( +Expand tabs when outputting shell functions using the tt(-c) option. +This has the same effect as the tt(-x) option to the tt(functions) +builtin. +) enditem() ) findex(where) -item(tt(where) [ tt(-wpmsS) ] var(name) ...)( +item(tt(where) [ tt(-wpmsS) ] [ tt(-x) var(num) ] var(name) ...)( Equivalent to tt(whence -ca). ) findex(which) -item(tt(which) [ tt(-wpamsS) ] var(name) ...)( +item(tt(which) [ tt(-wpamsS) ] [ tt(-x) var(num) ] var(name) ...)( Equivalent to tt(whence -c). ) findex(zcompile) diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index 8b6b7d3b7..323bf0f9a 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -3561,7 +3561,7 @@ set to the ANSI terminal escapes that turn off all attributes and turn on bold intensity, respectively. ) findex(fned) -item(tt(fned) var(name))( +item(tt(fned) [ tt(-x) var(num) ] var(name))( Same as tt(zed -f). This function does not appear in the zsh distribution, but can be created by linking tt(zed) to the name tt(fned) in some directory in your tt(fpath). @@ -3749,7 +3749,7 @@ the difference in function between tt(zargs) and tt(xargs)) or run tt(zargs) with the tt(-)tt(-help) option. ) findex(zed) -xitem(tt(zed) [ tt(-f) ] var(name)) +xitem(tt(zed) [ tt(-f) [ tt(-x) var(num) ] var(name)) item(tt(zed -b))( This function uses the ZLE editor to edit a file or function. @@ -3758,7 +3758,10 @@ If the tt(-f) option is given, the name is taken to be that of a function; if the function is marked for autoloading, tt(zed) searches for it in the tt(fpath) and loads it. Note that functions edited this way are installed into the current shell, but em(not) written back to the -autoload file. +autoload file. In this case the tt(-x) option specifies that leading +tabs indenting the function according to syntax should be converted into +the given number of spaces; `tt(-x 2)' is consistent with the layout +of functions distributed with the shell. Without tt(-f), var(name) is the path name of the file to edit, which need not exist; it is created on write, if necessary. diff --git a/Functions/Misc/zed b/Functions/Misc/zed index c2caaf3f5..010b69bee 100644 --- a/Functions/Misc/zed +++ b/Functions/Misc/zed @@ -9,8 +9,9 @@ local var opt zed_file_name # We do not want timeout while we are editing a file integer TMOUT=0 okargs=1 fun bind +local -a expand -while getopts "fb" opt; do +while getopts "fbx:" opt; do case $opt in (f) fun=1 @@ -19,6 +20,14 @@ while getopts "fb" opt; do (b) bind=1 ;; + + (x) + if [[ $OPTARG != <-> ]]; then + print -r "Integer expected after -x: $OPTARG" >&2 + return 1 + fi + expand=(-x $OPTARG) + ;; esac done shift $(( OPTIND - 1 )) @@ -29,8 +38,8 @@ shift $(( OPTIND - 1 )) if (( $# != okargs )); then echo 'Usage: zed filename -zed -f function -zed -b' +zed -f [ -x N ] function +zed -b' >&2 return 1 fi @@ -71,7 +80,7 @@ fi setopt localoptions nobanghist if ((fun)) then - var="$(functions $1)" + var="$(functions $expand $1)" # If function is undefined but autoloadable, load it if [[ $var = *\#\ undefined* ]] then var="$(autoload +X $1; functions $1)" 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) -- cgit 1.4.1