diff options
Diffstat (limited to 'Src/builtin.c')
-rw-r--r-- | Src/builtin.c | 484 |
1 files changed, 375 insertions, 109 deletions
diff --git a/Src/builtin.c b/Src/builtin.c index ba7136eb4..d54b0f41c 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -48,7 +48,7 @@ static struct builtin builtins[] = BUILTIN(".", BINF_PSPECIAL, bin_dot, 1, -1, 0, NULL, NULL), BUILTIN(":", BINF_PSPECIAL, bin_true, 0, -1, 0, NULL, NULL), BUILTIN("alias", BINF_MAGICEQUALS | BINF_PLUSOPTS, bin_alias, 0, -1, 0, "Lgmrs", NULL), - BUILTIN("autoload", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "mktTUwXz", "u"), + BUILTIN("autoload", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "dmktrRTUwWXz", "u"), BUILTIN("bg", 0, bin_fg, 0, -1, BIN_BG, NULL, NULL), BUILTIN("break", BINF_PSPECIAL, bin_break, 0, 1, BIN_BREAK, NULL, NULL), BUILTIN("bye", 0, bin_break, 0, 1, BIN_EXIT, NULL, NULL), @@ -74,7 +74,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 | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"), - BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUx:z", NULL), + BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMstTuUWx: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), @@ -102,7 +102,7 @@ static struct builtin builtins[] = BUILTIN("popd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 1, BIN_POPD, "q", NULL), BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "abcC:Df:ilmnNoOpPrRsSu:v:x:X:z-", NULL), - BUILTIN("printf", 0, bin_print, 1, -1, BIN_PRINTF, "v:", NULL), + BUILTIN("printf", BINF_SKIPINVALID | BINF_SKIPDASH, bin_print, 1, -1, BIN_PRINTF, "v:", NULL), BUILTIN("pushd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_PUSHD, "qsPL", NULL), BUILTIN("pushln", 0, bin_print, 0, -1, BIN_PRINT, NULL, "-nz"), BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL), @@ -251,7 +251,7 @@ int execbuiltin(LinkList args, LinkList assigns, Builtin bn) { char *pp, *name, *optstr; - int flags, sense, argc, execop, xtr = isset(XTRACE); + int flags, argc, execop, xtr = isset(XTRACE); struct options ops; /* initialise options structure */ @@ -296,6 +296,7 @@ execbuiltin(LinkList args, LinkList assigns, Builtin bn) /* Sort out the options. */ if (optstr) { char *arg = *argv; + int sense; /* 1 for -x, 0 for +x */ /* while arguments look like options ... */ while (arg && /* Must begin with - or maybe + */ @@ -389,7 +390,7 @@ execbuiltin(LinkList args, LinkList assigns, Builtin bn) if (*arg) { if(*arg == Meta) *++arg ^= 32; - zwarnnam(name, "bad option: -%c", *arg); + zwarnnam(name, "bad option: %c%c", "+-"[sense], *arg); return 1; } arg = *++argv; @@ -540,18 +541,18 @@ bin_enable(char *name, char **argv, Options ops, int func) /* With -m option, treat arguments as glob patterns. */ if (OPT_ISSET(ops,'m')) { for (; *argv; argv++) { + queue_signals(); + /* parse pattern */ tokenize(*argv); - if ((pprog = patcompile(*argv, PAT_STATIC, 0))) { - queue_signals(); + if ((pprog = patcompile(*argv, PAT_STATIC, 0))) match += scanmatchtable(ht, pprog, 0, 0, 0, scanfunc, 0); - unqueue_signals(); - } else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv); returnval = 1; } + unqueue_signals(); } /* If we didn't match anything, we return 1. */ if (!match) @@ -795,8 +796,8 @@ set_pwd_env(void) unsetparam_pm(pm, 0, 1); } - setsparam("PWD", ztrdup(pwd)); - setsparam("OLDPWD", ztrdup(oldpwd)); + assignsparam("PWD", ztrdup(pwd), 0); + assignsparam("OLDPWD", ztrdup(oldpwd), 0); pm = (Param) paramtab->getnode(paramtab, "PWD"); if (!(pm->node.flags & PM_EXPORTED)) @@ -973,7 +974,7 @@ cd_do_chdir(char *cnam, char *dest, int hard) * Normalize path under Cygwin to avoid messing with * DOS style names with drives in them */ - static char buf[PATH_MAX]; + static char buf[PATH_MAX+1]; #ifdef HAVE_CYGWIN_CONV_PATH cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_RELATIVE, dest, buf, PATH_MAX); @@ -1273,7 +1274,23 @@ fixdir(char *src) #ifdef __CYGWIN__ char *s0 = src; #endif - int ret = 0; + /* This function is always called with n path containing at + * least one slash, either because one was input by the user or + * because the caller has prepended either pwd or a cdpath dir. + * If asked to make a relative change and pwd is set to ".", + * the current directory has been removed out from under us, + * so force links to be chased. + * + * Ordinarily we can't get here with "../" as the first component + * but handle the silly special case of ".." in cdpath. + * + * Order of comparisons here looks funny, but it short-circuits + * most rapidly in the event of a false condition. Set to 2 + * here so we still obey the (lack of) CHASEDOTS option after + * the first "../" is preserved (test chasedots > 1 below). + */ + int chasedots = (src[0] == '.' && pwd[0] == '.' && pwd[1] == '\0' && + (src[1] == '/' || (src[1] == '.' && src[2] == '/'))) * 2; /*** if have RFS superroot directory ***/ #ifdef HAVE_SUPERROOT @@ -1305,12 +1322,12 @@ fixdir(char *src) while (dest > d0 + 1 && dest[-1] == '/') dest--; *dest = '\0'; - return ret; + return chasedots; } if (src[0] == '.' && src[1] == '.' && (src[2] == '\0' || src[2] == '/')) { - if (isset(CHASEDOTS)) { - ret = 1; + if (isset(CHASEDOTS) || chasedots > 1) { + chasedots = 1; /* and treat as normal path segment */ } else { if (dest > d0 + 1) { @@ -1348,6 +1365,7 @@ fixdir(char *src) dest[-1] = *src++ ^ 32; } } + /* unreached */ } /**/ @@ -1472,6 +1490,7 @@ bin_fc(char *nam, char **argv, Options ops, int func) } if (zleactive) { + unqueue_signals(); zwarnnam(nam, "no interactive history within ZLE"); return 1; } @@ -1610,7 +1629,7 @@ bin_fc(char *nam, char **argv, Options ops, int func) unqueue_signals(); if (fcedit(editor, fil)) { if (stuff(fil)) - zwarnnam("fc", "%e: %s", errno, s); + zwarnnam("fc", "%e: %s", errno, fil); else { loop(0,1); retval = lastval; @@ -1990,11 +2009,12 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), * handled in createparam(). Here we just avoid using it for the * present tests if it's unset. * - * POSIXBUILTINS horror: we need to retain the 'readonly' flag - * of an unset parameter. + * POSIXBUILTINS horror: we need to retain the 'readonly' or 'export' + * flags of an unset parameter. */ usepm = pm && (!(pm->node.flags & PM_UNSET) || - (isset(POSIXBUILTINS) && (pm->node.flags & PM_READONLY))); + (isset(POSIXBUILTINS) && + (pm->node.flags & (PM_READONLY|PM_EXPORTED)))); /* * We need to compare types with an existing pm if special, @@ -2117,7 +2137,8 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), /* * Stricter rules about retaining readonly attribute in this case. */ - if ((on & PM_READONLY) && (!usepm || (pm->node.flags & PM_UNSET)) && + if ((on & (PM_READONLY|PM_EXPORTED)) && + (!usepm || (pm->node.flags & PM_UNSET)) && !ASG_VALUEP(asg)) on |= PM_UNSET; else if (usepm && (pm->node.flags & PM_READONLY) && @@ -2125,6 +2146,10 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), zerr("read-only variable: %s", pm->node.nam); return NULL; } + /* This is handled by createparam(): + if (usepm && (pm->node.flags & PM_EXPORTED) && !(off & PM_EXPORTED)) + on |= PM_EXPORTED; + */ } /* @@ -2266,6 +2291,10 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func), zerrnam(cname, "%s: restricted", pname); return pm; } + if (pm->node.flags & PM_SINGLE) { + zerrnam(cname, "%s: can only have a single instance", pname); + return pm; + } /* * For specials, we keep the same struct but zero everything. * Maybe it would be easier to create a new struct but copy @@ -2787,6 +2816,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func) return 0; } if (off & PM_TIED) { + unqueue_signals(); zerrnam(name, "use unset to remove tied variables"); return 1; } @@ -2892,9 +2922,61 @@ eval_autoload(Shfunc shf, char *name, Options ops, int func) } return !loadautofn(shf, (OPT_ISSET(ops,'k') ? 2 : - (OPT_ISSET(ops,'z') ? 0 : 1)), 1); + (OPT_ISSET(ops,'z') ? 0 : 1)), 1, + OPT_ISSET(ops,'d')); } +/* Helper for bin_functions() for -X and -r options */ + +/**/ +static int +check_autoload(Shfunc shf, char *name, Options ops, int func) +{ + if (OPT_ISSET(ops,'X')) + { + return eval_autoload(shf, name, ops, func); + } + if ((OPT_ISSET(ops,'r') || OPT_ISSET(ops,'R')) && + (shf->node.flags & PM_UNDEFINED)) + { + char *dir_path; + if (shf->filename && (shf->node.flags & PM_LOADDIR)) { + char *spec_path[2]; + spec_path[0] = shf->filename; + spec_path[1] = NULL; + if (getfpfunc(shf->node.nam, NULL, &dir_path, spec_path, 1)) { + /* shf->filename is already correct. */ + return 0; + } + if (!OPT_ISSET(ops,'d')) { + if (OPT_ISSET(ops,'R')) { + zerr("%s: function definition file not found", + shf->node.nam); + return 1; + } + return 0; + } + } + if (getfpfunc(shf->node.nam, NULL, &dir_path, NULL, 1)) { + dircache_set(&shf->filename, NULL); + if (*dir_path != '/') { + dir_path = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), + "/", dir_path); + dir_path = xsymlink(dir_path, 1); + } + dircache_set(&shf->filename, dir_path); + shf->node.flags |= PM_LOADDIR; + return 0; + } + if (OPT_ISSET(ops,'R')) { + zerr("%s: function definition file not found", + shf->node.nam); + return 1; + } + /* with -r, we don't flag an error, just let it be found later. */ + } + return 0; +} /* List a user-defined math function. */ static void @@ -2911,7 +2993,7 @@ listusermathfunc(MathFunc p) else showargs = 0; - printf("functions -M %s", p->name); + printf("functions -M%s %s", (p->flags & MFF_STR) ? "s" : "", p->name); if (showargs) { printf(" %d", p->minargs); showargs--; @@ -2932,6 +3014,66 @@ listusermathfunc(MathFunc p) } +static void +add_autoload_function(Shfunc shf, char *funcname) +{ + char *nam; + if (*funcname == '/' && funcname[1] && + (nam = strrchr(funcname, '/')) && nam[1] && + (shf->node.flags & PM_UNDEFINED)) { + char *dir; + nam = strrchr(funcname, '/'); + if (nam == funcname) { + dir = "/"; + } else { + *nam++ = '\0'; + dir = funcname; + } + dircache_set(&shf->filename, NULL); + dircache_set(&shf->filename, dir); + shf->node.flags |= PM_LOADDIR; + shf->node.flags |= PM_ABSPATH_USED; + shfunctab->addnode(shfunctab, ztrdup(nam), shf); + } else { + Shfunc shf2; + Funcstack fs; + const char *calling_f = NULL; + char buf[PATH_MAX+1]; + + /* Find calling function */ + for (fs = funcstack; fs; fs = fs->prev) { + if (fs->tp == FS_FUNC && fs->name && (!shf->node.nam || 0 != strcmp(fs->name,shf->node.nam))) { + calling_f = fs->name; + break; + } + } + + /* Get its directory */ + if (calling_f) { + /* Should contain load directory, and be loaded via absolute path */ + if ((shf2 = (Shfunc) shfunctab->getnode2(shfunctab, calling_f)) + && (shf2->node.flags & PM_LOADDIR) && (shf2->node.flags & PM_ABSPATH_USED) + && shf2->filename) + { + if (strlen(shf2->filename) + strlen(funcname) + 1 < PATH_MAX) + { + sprintf(buf, "%s/%s", shf2->filename, funcname); + /* Set containing directory if the function file + * exists (do normal FPATH processing otherwise) */ + if (!access(buf, R_OK)) { + dircache_set(&shf->filename, NULL); + dircache_set(&shf->filename, shf2->filename); + shf->node.flags |= PM_LOADDIR; + shf->node.flags |= PM_ABSPATH_USED; + } + } + } + } + + shfunctab->addnode(shfunctab, ztrdup(funcname), shf); + } +} + /* Display or change the attributes of shell functions. * * If called as autoload, it will define a new autoloaded * * (undefined) shell function. */ @@ -2962,6 +3104,10 @@ bin_functions(char *name, char **argv, Options ops, int func) on |= PM_TAGGED_LOCAL; else if (OPT_PLUS(ops,'T')) off |= PM_TAGGED_LOCAL; + if (OPT_MINUS(ops,'W')) + on |= PM_WARNNESTED; + else if (OPT_PLUS(ops,'W')) + off |= PM_WARNNESTED; roff = off; if (OPT_MINUS(ops,'z')) { on |= PM_ZSHSTORED; @@ -2977,10 +3123,17 @@ bin_functions(char *name, char **argv, Options ops, int func) off |= PM_KSHSTORED; roff |= PM_KSHSTORED; } + if (OPT_MINUS(ops,'d')) { + on |= PM_CUR_FPATH; + off |= PM_CUR_FPATH; + } else if (OPT_PLUS(ops,'d')) { + off |= PM_CUR_FPATH; + roff |= PM_CUR_FPATH; + } 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))) { + (OPT_MINUS(ops,'X') && (OPT_ISSET(ops,'m') || !scriptname))) { zwarnnam(name, "invalid option(s)"); return 1; } @@ -3019,9 +3172,9 @@ bin_functions(char *name, char **argv, Options ops, int func) } else if (OPT_ISSET(ops,'m')) { /* List matching functions. */ for (; *argv; argv++) { + queue_signals(); tokenize(*argv); if ((pprog = patcompile(*argv, PAT_STATIC, 0))) { - queue_signals(); for (p = mathfuncs, q = NULL; p; q = p) { MathFunc next; do { @@ -3040,12 +3193,12 @@ bin_functions(char *name, char **argv, Options ops, int func) if (p) p = p->next; } - unqueue_signals(); } else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv); returnval = 1; } + unqueue_signals(); } } else if (OPT_PLUS(ops,'M')) { /* Delete functions. -m is allowed but is handled above. */ @@ -3067,11 +3220,18 @@ bin_functions(char *name, char **argv, Options ops, int func) } } else { /* Add a function */ - int minargs = 0, maxargs = -1; + int minargs, maxargs; char *funcname = *argv++; char *modname = NULL; char *ptr; + if (OPT_ISSET(ops,'s')) { + minargs = maxargs = 1; + } else { + minargs = 0; + maxargs = -1; + } + ptr = itype_end(funcname, IIDENT, 0); if (idigit(*funcname) || funcname == ptr || *ptr) { zwarnnam(name, "-M %s: bad math function name", funcname); @@ -3085,6 +3245,10 @@ bin_functions(char *name, char **argv, Options ops, int func) *argv); return 1; } + if (OPT_ISSET(ops,'s') && minargs != 1) { + zwarnnam(name, "-Ms: must take a single string argument"); + return 1; + } maxargs = minargs; argv++; } @@ -3098,6 +3262,10 @@ bin_functions(char *name, char **argv, Options ops, int func) *argv); return 1; } + if (OPT_ISSET(ops,'s') && maxargs != 1) { + zwarnnam(name, "-Ms: must take a single string argument"); + return 1; + } argv++; } if (*argv) @@ -3110,6 +3278,8 @@ bin_functions(char *name, char **argv, Options ops, int func) p = (MathFunc)zshcalloc(sizeof(struct mathfunc)); p->name = ztrdup(funcname); p->flags = MFF_USERFUNC; + if (OPT_ISSET(ops,'s')) + p->flags |= MFF_STR; p->module = modname ? ztrdup(modname) : NULL; p->minargs = minargs; p->maxargs = maxargs; @@ -3117,6 +3287,7 @@ bin_functions(char *name, char **argv, Options ops, int func) queue_signals(); for (q = mathfuncs; q; q = q->next) { if (!strcmp(q->name, funcname)) { + unqueue_signals(); zwarnnam(name, "-M %s: function already exists", funcname); zsfree(p->name); @@ -3134,47 +3305,58 @@ bin_functions(char *name, char **argv, Options ops, int func) return returnval; } - /* If no arguments given, we will print functions. If flags * - * are given, we will print only functions containing these * - * flags, else we'll print them all. */ - if (!*argv) { - int ret = 0; - + if (OPT_MINUS(ops,'X')) { + Funcstack fs; + char *funcname = NULL; + int ret; + if (*argv && argv[1]) { + zwarnnam(name, "-X: too many arguments"); + return 1; + } queue_signals(); - if (OPT_MINUS(ops,'X')) { - Funcstack fs; - char *funcname = NULL; - for (fs = funcstack; fs; fs = fs->prev) { - if (fs->tp == FS_FUNC) { - /* - * dupstring here is paranoia but unlikely to be - * problematic - */ - funcname = dupstring(fs->name); - break; - } + for (fs = funcstack; fs; fs = fs->prev) { + if (fs->tp == FS_FUNC) { + /* + * dupstring here is paranoia but unlikely to be + * problematic + */ + funcname = dupstring(fs->name); + break; } - if (!funcname) - { - zerrnam(name, "bad autoload"); - ret = 1; + } + if (!funcname) + { + zerrnam(name, "bad autoload"); + ret = 1; + } else { + if ((shf = (Shfunc) shfunctab->getnode(shfunctab, funcname))) { + DPUTS(!shf->funcdef, + "BUG: Calling autoload from empty function"); } else { - if ((shf = (Shfunc) shfunctab->getnode(shfunctab, funcname))) { - DPUTS(!shf->funcdef, - "BUG: Calling autoload from empty function"); - } else { - shf = (Shfunc) zshcalloc(sizeof *shf); - shfunctab->addnode(shfunctab, ztrdup(funcname), shf); - } - shf->node.flags = on; - ret = eval_autoload(shf, funcname, ops, func); + shf = (Shfunc) zshcalloc(sizeof *shf); + shfunctab->addnode(shfunctab, ztrdup(funcname), shf); } - } else { - if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u')) + if (*argv) { + dircache_set(&shf->filename, NULL); + dircache_set(&shf->filename, *argv); + on |= PM_LOADDIR; + } + shf->node.flags = on; + ret = eval_autoload(shf, funcname, ops, func); + } + unqueue_signals(); + return ret; + } else if (!*argv) { + /* If no arguments given, we will print functions. If flags * + * are given, we will print only functions containing these * + * flags, else we'll print them all. */ + int ret = 0; + + queue_signals(); + if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u')) on &= ~PM_UNDEFINED; scanshfunc(1, on|off, DISABLED, shfunctab->printnode, pflags, expand); - } unqueue_signals(); return ret; } @@ -3183,11 +3365,11 @@ bin_functions(char *name, char **argv, Options ops, int func) if (OPT_ISSET(ops,'m')) { on &= ~PM_UNDEFINED; for (; *argv; argv++) { + queue_signals(); /* expand argument */ tokenize(*argv); if ((pprog = patcompile(*argv, PAT_STATIC, 0))) { /* with no options, just print all functions matching the glob pattern */ - queue_signals(); if (!(on|off) && !OPT_ISSET(ops,'X')) { scanmatchshfunc(pprog, 1, 0, DISABLED, shfunctab->printnode, pflags, expand); @@ -3200,19 +3382,19 @@ bin_functions(char *name, char **argv, Options ops, int func) !(shf->node.flags & DISABLED)) { shf->node.flags = (shf->node.flags | (on & ~PM_UNDEFINED)) & ~off; - if (OPT_ISSET(ops,'X') && - eval_autoload(shf, shf->node.nam, ops, func)) { + if (check_autoload(shf, shf->node.nam, + ops, func)) { returnval = 1; } } } } - unqueue_signals(); } else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv); returnval = 1; } + unqueue_signals(); } return returnval; } @@ -3227,8 +3409,7 @@ bin_functions(char *name, char **argv, Options ops, int func) if (on|off) { /* turn on/off the given flags */ shf->node.flags = (shf->node.flags | (on & ~PM_UNDEFINED)) & ~off; - if (OPT_ISSET(ops,'X') && - eval_autoload(shf, shf->node.nam, ops, func)) + if (check_autoload(shf, shf->node.nam, ops, func)) returnval = 1; } else /* no flags, so just print */ @@ -3245,13 +3426,38 @@ bin_functions(char *name, char **argv, Options ops, int func) removetrapnode(signum); } + if (**argv == '/') { + char *base = strrchr(*argv, '/') + 1; + if (*base && + (shf = (Shfunc) shfunctab->getnode(shfunctab, base))) { + char *dir; + /* turn on/off the given flags */ + shf->node.flags = + (shf->node.flags | (on & ~PM_UNDEFINED)) & ~off; + if (shf->node.flags & PM_UNDEFINED) { + /* update path if not yet loaded */ + if (base == *argv + 1) + dir = "/"; + else { + dir = *argv; + base[-1] = '\0'; + } + dircache_set(&shf->filename, NULL); + dircache_set(&shf->filename, dir); + } + if (check_autoload(shf, shf->node.nam, ops, func)) + returnval = 1; + continue; + } + } + /* Add a new undefined (autoloaded) function to the * * hash table with the corresponding flags set. */ shf = (Shfunc) zshcalloc(sizeof *shf); shf->node.flags = on; shf->funcdef = mkautofn(shf); shfunc_set_sticky(shf); - shfunctab->addnode(shfunctab, ztrdup(*argv), shf); + add_autoload_function(shf, *argv); if (signum != -1) { if (settrap(signum, NULL, ZSIG_FUNC)) { @@ -3262,8 +3468,7 @@ bin_functions(char *name, char **argv, Options ops, int func) } } - if (ok && OPT_ISSET(ops,'X') && - eval_autoload(shf, shf->node.nam, ops, func)) + if (ok && check_autoload(shf, shf->node.nam, ops, func)) returnval = 1; } else returnval = 1; @@ -3317,11 +3522,11 @@ bin_unset(char *name, char **argv, Options ops, int func) /* with -m option, treat arguments as glob patterns */ if (OPT_ISSET(ops,'m')) { while ((s = *argv++)) { + queue_signals(); /* expand */ tokenize(s); if ((pprog = patcompile(s, PAT_STATIC, NULL))) { /* Go through the parameter table, and unset any matches */ - queue_signals(); for (i = 0; i < paramtab->hsize; i++) { for (pm = (Param) paramtab->nodes[i]; pm; pm = next) { /* record pointer to next, since we may free this one */ @@ -3334,12 +3539,12 @@ bin_unset(char *name, char **argv, Options ops, int func) } } } - unqueue_signals(); } else { untokenize(s); zwarnnam(name, "bad pattern : %s", s); returnval = 1; } + unqueue_signals(); } /* If we didn't match anything, we return 1. */ if (!match) @@ -3506,6 +3711,7 @@ bin_whence(char *nam, char **argv, Options ops, int func) pushheap(); matchednodes = newlinklist(); } + queue_signals(); for (; *argv; argv++) { /* parse the pattern */ tokenize(*argv); @@ -3515,7 +3721,6 @@ bin_whence(char *nam, char **argv, Options ops, int func) returnval = 1; continue; } - queue_signals(); if (!OPT_ISSET(ops,'p')) { /* -p option is for path search only. * * We're not using it, so search for ... */ @@ -3546,9 +3751,9 @@ bin_whence(char *nam, char **argv, Options ops, int func) scanmatchtable(cmdnamtab, pprog, 1, 0, 0, (all ? fetchcmdnamnode : cmdnamtab->printnode), printflags); - - unqueue_signals(); + run_queued_signals(); } + unqueue_signals(); if (all) { allmatched = argv = zlinklist2array(matchednodes); matchednodes = NULL; @@ -3625,9 +3830,11 @@ bin_whence(char *nam, char **argv, Options ops, int func) if (wd) { printf("%s: command\n", *argv); } else { - if (v && !csh) + if (v && !csh) { zputs(*argv, stdout), fputs(" is ", stdout); - zputs(buf, stdout); + quotedzputs(buf, stdout); + } else + zputs(buf, stdout); if (OPT_ISSET(ops,'s') || OPT_ISSET(ops, 'S')) print_if_link(buf, OPT_ISSET(ops, 'S')); fputc('\n', stdout); @@ -3636,26 +3843,39 @@ bin_whence(char *nam, char **argv, Options ops, int func) } } if (!informed && (wd || v || csh)) { + /* this is information and not an error so, as in csh, use stdout */ zputs(*argv, stdout); puts(wd ? ": none" : " not found"); returnval = 1; } popheap(); - } else if ((cnam = findcmd(*argv, 1))) { + } else if (func == BIN_COMMAND && OPT_ISSET(ops,'p') && + (hn = builtintab->getnode(builtintab, *argv))) { + /* + * Special case for "command -p[vV]" which needs to + * show a builtin in preference to an external command. + */ + builtintab->printnode(hn, printflags); + informed = 1; + } else if ((cnam = findcmd(*argv, 1, + func == BIN_COMMAND && + OPT_ISSET(ops,'p')))) { /* Found external command. */ if (wd) { printf("%s: command\n", *argv); } else { - if (v && !csh) + if (v && !csh) { zputs(*argv, stdout), fputs(" is ", stdout); - zputs(cnam, stdout); + quotedzputs(cnam, stdout); + } else + zputs(cnam, stdout); if (OPT_ISSET(ops,'s') || OPT_ISSET(ops,'S')) print_if_link(cnam, OPT_ISSET(ops,'S')); fputc('\n', stdout); } informed = 1; } else { - /* Not found at all. */ + /* Not found at all. That's not an error as such so this goes to stdout */ if (v || csh || wd) zputs(*argv, stdout), puts(wd ? ": none" : " not found"); returnval = 1; @@ -3751,6 +3971,7 @@ bin_hash(char *name, char **argv, Options ops, UNUSED(int func)) if (!(asg = getasg(&argv, NULL))) { zwarnnam(name, "bad assignment"); returnval = 1; + break; } else if (ASG_VALUEP(asg)) { if(isset(RESTRICTED)) { zwarnnam(name, "restricted: %s", asg->value.scalar); @@ -3859,11 +4080,11 @@ bin_unhash(char *name, char **argv, Options ops, int func) * "unhash -m '*'" is legal, but not recommended. */ if (OPT_ISSET(ops,'m')) { for (; *argv; argv++) { + queue_signals(); /* expand argument */ tokenize(*argv); if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) { /* remove all nodes matching glob pattern */ - queue_signals(); for (i = 0; i < ht->hsize; i++) { for (hn = ht->nodes[i]; hn; hn = nhn) { /* record pointer to next, since we may free this one */ @@ -3874,12 +4095,12 @@ bin_unhash(char *name, char **argv, Options ops, int func) } } } - unqueue_signals(); } else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv); returnval = 1; } + unqueue_signals(); } /* If we didn't match anything, we return 1. */ if (!match) @@ -3962,18 +4183,18 @@ bin_alias(char *name, char **argv, Options ops, UNUSED(int func)) * glob patterns of aliases to display. */ if (OPT_ISSET(ops,'m')) { for (; *argv; argv++) { + queue_signals(); tokenize(*argv); /* expand argument */ if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) { /* display the matching aliases */ - queue_signals(); scanmatchtable(ht, pprog, 1, flags1, flags2, ht->printnode, printflags); - unqueue_signals(); } else { untokenize(*argv); zwarnnam(name, "bad pattern : %s", *argv); returnval = 1; } + unqueue_signals(); } return returnval; } @@ -4048,10 +4269,11 @@ bin_print(char *name, char **args, Options ops, int func) { int flen, width, prec, type, argc, n, narg, curlen = 0; int nnl = 0, fmttrunc = 0, ret = 0, maxarg = 0, nc = 0; - int flags[6], *len; + int flags[6], *len, visarr = 0; char *start, *endptr, *c, *d, *flag, *buf = NULL, spec[14], *fmt = NULL; char **first, **argp, *curarg, *flagch = "'0+- #", save = '\0', nullstr = '\0'; size_t rcount = 0, count = 0; + size_t *cursplit = 0, *splits = 0; FILE *fout = stdout; #ifdef HAVE_OPEN_MEMSTREAM size_t mcount; @@ -4083,7 +4305,7 @@ bin_print(char *name, char **args, Options ops, int func) return 1; \ } \ unlink(tmpf); \ - if ((fout = fdopen(tempfd, "w+")) == NULL) { \ + if ((FOUT = fdopen(tempfd, "w+")) == NULL) { \ close(tempfd); \ zwarnnam(name, "can't open temp file: %e", errno); \ return 1; \ @@ -4182,10 +4404,12 @@ bin_print(char *name, char **args, Options ops, int func) zwarnnam(name, "no pattern specified"); return 1; } + queue_signals(); tokenize(*args); if (!(pprog = patcompile(*args, PAT_STATIC, NULL))) { untokenize(*args); zwarnnam(name, "bad pattern: %s", *args); + unqueue_signals(); return 1; } for (t = p = ++args; *p; p++) @@ -4193,6 +4417,7 @@ bin_print(char *name, char **args, Options ops, int func) *t++ = *p; *t = NULL; first = args; + unqueue_signals(); if (fmt && !*args) return 0; } /* compute lengths, and interpret according to -P, -D, -e, etc. */ @@ -4512,6 +4737,7 @@ bin_print(char *name, char **args, Options ops, int func) short *words; if (nwords > 1) { zwarnnam(name, "option -S takes a single argument"); + unqueue_signals(); return 1; } words = NULL; @@ -4581,7 +4807,8 @@ bin_print(char *name, char **args, Options ops, int func) OPT_ISSET(ops,'N') ? '\0' : ' ', fout); } } - if (!(OPT_ISSET(ops,'n') || OPT_ISSET(ops, 'v') || nnl)) + if (!(OPT_ISSET(ops,'n') || nnl || + (OPT_ISSET(ops, 'v') && !OPT_ISSET(ops, 'l')))) fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout); if (IS_MSTREAM(fout) && (rcount = READ_MSTREAM(buf,fout)) == -1) ret = 1; @@ -4608,11 +4835,23 @@ bin_print(char *name, char **args, Options ops, int func) * special cases of printing to a ZLE buffer or the history, however. */ + if (OPT_ISSET(ops,'v')) { + struct value vbuf; + char* s = OPT_ARG(ops,'v'); + Value v = getvalue(&vbuf, &s, 0); + visarr = v && PM_TYPE(v->pm->node.flags) == PM_ARRAY; + } /* printf style output */ *spec = '%'; argp = args; do { rcount = count; + if (argp > args && visarr) { /* reusing format string */ + if (!splits) + cursplit = splits = (size_t *)zhalloc(sizeof(size_t) * + (arrlen(args) / (argp - args) + 1)); + *cursplit++ = count; + } if (maxarg) { first += maxarg; argc -= maxarg; @@ -4841,9 +5080,10 @@ bin_print(char *name, char **args, Options ops, int func) break; case 'q': stringval = curarg ? - quotestring(curarg, NULL, QT_BACKSLASH_SHOWNULL) : &nullstr; + quotestring(metafy(curarg, curlen, META_USEHEAP), + QT_BACKSLASH_SHOWNULL) : &nullstr; *d = 's'; - print_val(stringval); + print_val(unmetafy(stringval, &curlen)); break; case 'd': case 'i': @@ -4979,18 +5219,30 @@ bin_print(char *name, char **args, Options ops, int func) if (buf) free(buf); } else { - stringval = metafy(buf, rcount, META_REALLOC); - if (OPT_ISSET(ops,'z')) { - zpushnode(bufstack, stringval); - } else if (OPT_ISSET(ops,'v')) { - setsparam(OPT_ARG(ops, 'v'), stringval); + if (visarr && splits) { + char **arrayval = zshcalloc((cursplit - splits + 2) * sizeof(char *)); + for (;cursplit >= splits; cursplit--) { + int start = cursplit == splits ? 0 : cursplit[-1]; + arrayval[cursplit - splits] = + metafy(buf + start, count - start, META_DUP); + count = start; + } + setaparam(OPT_ARG(ops, 'v'), arrayval); + free(buf); } else { - ent = prepnexthistent(); - ent->node.nam = stringval; - ent->stim = ent->ftim = time(NULL); - ent->node.flags = 0; - ent->words = (short *)NULL; - addhistnode(histtab, ent->node.nam, ent); + stringval = metafy(buf, rcount, META_REALLOC); + if (OPT_ISSET(ops,'z')) { + zpushnode(bufstack, stringval); + } else if (OPT_ISSET(ops,'v')) { + setsparam(OPT_ARG(ops, 'v'), stringval); + } else { + ent = prepnexthistent(); + ent->node.nam = stringval; + ent->stim = ent->ftim = time(NULL); + ent->node.flags = 0; + ent->words = (short *)NULL; + addhistnode(histtab, ent->node.nam, ent); + } } } unqueue_signals(); @@ -5097,7 +5349,7 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun zoptind = 1; optcind = 0; } - if(zoptind > arrlen(args)) + if (arrlen_lt(args, zoptind)) /* no more options */ return 1; @@ -5252,7 +5504,7 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func) } /*FALLTHROUGH*/ case BIN_EXIT: - if (locallevel > forklevel) { + if (locallevel > forklevel && shell_exiting != -1) { /* * We don't exit directly from functions to allow tidying * up, in particular EXIT traps. We still need to perform @@ -5261,6 +5513,9 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func) * * If we are forked, we exit the shell at the function depth * at which we became a subshell, hence the comparison. + * + * If we are already exiting... give this all up as + * a bad job. */ if (stopmsg || (zexit(0,2), !stopmsg)) { retflag = 1; @@ -5307,6 +5562,14 @@ checkjobs(void) } } +/* + * -1 if the shell is already committed to exit. + * positive if zexit() was already called. + */ + +/**/ +int shell_exiting; + /* exit the shell. val is the return value of the shell. * * from_where is * 1 if zexit is called because of a signal @@ -5318,10 +5581,8 @@ checkjobs(void) mod_export void zexit(int val, int from_where) { - static int in_exit; - /* Don't do anything recursively: see below */ - if (in_exit == -1) + if (shell_exiting == -1) return; if (isset(MONITOR) && !stopmsg && from_where != 1) { @@ -5334,14 +5595,14 @@ zexit(int val, int from_where) } } /* Positive in_exit means we have been here before */ - if (from_where == 2 || (in_exit++ && from_where)) + if (from_where == 2 || (shell_exiting++ && from_where)) return; /* - * We're now committed to exiting. Set in_exit to -1 to + * We're now committed to exiting. Set shell_exiting to -1 to * indicate we shouldn't do any recursive processing. */ - in_exit = -1; + shell_exiting = -1; /* * We want to do all remaining processing regardless of preceding * errors, even user interrupts. @@ -5369,6 +5630,11 @@ zexit(int val, int from_where) } } lastval = val; + /* + * Now we are committed to exiting any previous state + * is irrelevant. Ensure trap can run. + */ + errflag = intrap = 0; if (sigtrapped[SIGEXIT]) dotrap(SIGEXIT); callhookfunc("zshexit", NULL, 1, NULL); |