summaryrefslogtreecommitdiff
path: root/dotfiles/system/.zsh/modules/Src/builtin.c
diff options
context:
space:
mode:
Diffstat (limited to 'dotfiles/system/.zsh/modules/Src/builtin.c')
-rw-r--r--dotfiles/system/.zsh/modules/Src/builtin.c7236
1 files changed, 0 insertions, 7236 deletions
diff --git a/dotfiles/system/.zsh/modules/Src/builtin.c b/dotfiles/system/.zsh/modules/Src/builtin.c
deleted file mode 100644
index 93fa911..0000000
--- a/dotfiles/system/.zsh/modules/Src/builtin.c
+++ /dev/null
@@ -1,7236 +0,0 @@
-/*
- * builtin.c - builtin commands
- *
- * 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.
- *
- */
-
-/* this is defined so we get the prototype for open_memstream */
-#define _GNU_SOURCE 1
-
-#include "zsh.mdh"
-#include "builtin.pro"
-
-/* Builtins in the main executable */
-
-static struct builtin builtins[] =
-{
- BIN_PREFIX("-", BINF_DASH),
- BIN_PREFIX("builtin", BINF_BUILTIN),
- BIN_PREFIX("command", BINF_COMMAND),
- BIN_PREFIX("exec", BINF_EXEC),
- BIN_PREFIX("noglob", BINF_NOGLOB),
- BUILTIN("[", BINF_HANDLES_OPTS, bin_test, 0, -1, BIN_BRACKET, NULL, NULL),
- 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, "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),
- BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
- BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
- BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
- BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmp:%rtuxz", NULL),
- BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL),
- BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmprs", NULL),
- BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
- BUILTIN("echo", BINF_SKIPINVALID, bin_print, 0, -1, BIN_ECHO, "neE", "-"),
- BUILTIN("emulate", 0, bin_emulate, 0, -1, 0, "lLR", NULL),
- BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmprs", NULL),
- BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
- BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
- BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%TUZ:%afhi:%lp:%rtu", "xg"),
- BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
- /*
- * We used to behave as if the argument to -e was optional.
- * But that's actually not useful, so it's more consistent to
- * cause an error.
- */
- 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:%ghlp:%rtux", "E"),
- 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),
-
-#ifdef ZSH_HASH_DEBUG
- BUILTIN("hashinfo", 0, bin_hashinfo, 0, 0, 0, NULL, NULL),
-#endif
-
- BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "adDEfiLmnpPrt:", "l"),
- BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lp:%rtux", "i"),
- BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
- 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)
- BUILTIN("mem", 0, bin_mem, 0, 0, 0, "v", NULL),
-#endif
-
-#if defined(ZSH_PAT_DEBUG)
- BUILTIN("patdebug", 0, bin_patdebug, 1, -1, 0, "p", NULL),
-#endif
-
- 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", 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),
- BUILTIN("r", 0, bin_fc, 0, -1, BIN_R, "IlLnr", NULL),
- BUILTIN("read", 0, bin_read, 0, -1, 0, "cd:ek:%lnpqrst:%zu:AE", NULL),
- BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, BIN_READONLY, "AE:%F:%HL:%R:%TUZ:%afghi:%lptux", "r"),
- BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "df", "r"),
- BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
- BUILTIN("set", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_set, 0, -1, 0, NULL, NULL),
- BUILTIN("setopt", 0, bin_setopt, 0, -1, BIN_SETOPT, NULL, NULL),
- BUILTIN("shift", BINF_PSPECIAL, bin_shift, 0, -1, 0, "p", NULL),
- BUILTIN("source", BINF_PSPECIAL, bin_dot, 1, -1, 0, NULL, NULL),
- BUILTIN("suspend", 0, bin_suspend, 0, 0, 0, "f", NULL),
- BUILTIN("test", BINF_HANDLES_OPTS, bin_test, 0, -1, BIN_TEST, NULL, NULL),
- BUILTIN("ttyctl", 0, bin_ttyctl, 0, 0, 0, "fu", NULL),
- BUILTIN("times", BINF_PSPECIAL, bin_times, 0, 0, 0, NULL, NULL),
- BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL),
- BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
- BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsSw", "v"),
- BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klp:%rtuxmz", NULL),
- BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
- BUILTIN("unalias", 0, bin_unhash, 0, -1, BIN_UNALIAS, "ams", NULL),
- BUILTIN("unfunction", 0, bin_unhash, 1, -1, BIN_UNFUNCTION, "m", "f"),
- BUILTIN("unhash", 0, bin_unhash, 1, -1, BIN_UNHASH, "adfms", NULL),
- BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, BIN_UNSET, "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, "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:abcfdilmpsue", NULL),
- BUILTIN("zcompile", 0, bin_zcompile, 0, -1, 0, "tUMRcmzka", NULL),
-};
-
-/****************************************/
-/* Builtin Command Hash Table Functions */
-/****************************************/
-
-/* hash table containing builtin commands */
-
-/**/
-mod_export HashTable builtintab;
-
-/**/
-void
-createbuiltintable(void)
-{
- builtintab = newhashtable(85, "builtintab", NULL);
-
- builtintab->hash = hasher;
- builtintab->emptytable = NULL;
- builtintab->filltable = NULL;
- builtintab->cmpnodes = strcmp;
- builtintab->addnode = addhashnode;
- builtintab->getnode = gethashnode;
- builtintab->getnode2 = gethashnode2;
- builtintab->removenode = removehashnode;
- builtintab->disablenode = disablehashnode;
- builtintab->enablenode = enablehashnode;
- builtintab->freenode = freebuiltinnode;
- builtintab->printnode = printbuiltinnode;
-
- (void)addbuiltins("zsh", builtins, sizeof(builtins)/sizeof(*builtins));
-}
-
-/* Print a builtin */
-
-/**/
-static void
-printbuiltinnode(HashNode hn, int printflags)
-{
- Builtin bn = (Builtin) hn;
-
- if (printflags & PRINT_WHENCE_WORD) {
- printf("%s: builtin\n", bn->node.nam);
- return;
- }
-
- if (printflags & PRINT_WHENCE_CSH) {
- printf("%s: shell built-in command\n", bn->node.nam);
- return;
- }
-
- if (printflags & PRINT_WHENCE_VERBOSE) {
- printf("%s is a shell builtin\n", bn->node.nam);
- return;
- }
-
- /* default is name only */
- printf("%s\n", bn->node.nam);
-}
-
-/**/
-static void
-freebuiltinnode(HashNode hn)
-{
- Builtin bn = (Builtin) hn;
-
- if(!(bn->node.flags & BINF_ADDED)) {
- zsfree(bn->node.nam);
- zsfree(bn->optstr);
- zfree(bn, sizeof(struct builtin));
- }
-}
-
-/**/
-void
-init_builtins(void)
-{
- if (!EMULATION(EMULATE_ZSH)) {
- HashNode hn = reswdtab->getnode2(reswdtab, "repeat");
- if (hn)
- reswdtab->disablenode(hn, 0);
- }
-}
-
-/* Make sure we have space for a new option and increment. */
-
-#define OPT_ALLOC_CHUNK 16
-
-/**/
-static int
-new_optarg(Options ops)
-{
- /* Argument index must be a non-zero 6-bit number. */
- if (ops->argscount == 63)
- return 1;
- if (ops->argsalloc == ops->argscount) {
- char **newptr =
- (char **)zhalloc((ops->argsalloc + OPT_ALLOC_CHUNK) *
- sizeof(char *));
- if (ops->argsalloc)
- memcpy(newptr, ops->args, ops->argsalloc * sizeof(char *));
- ops->args = newptr;
- ops->argsalloc += OPT_ALLOC_CHUNK;
- }
- ops->argscount++;
- return 0;
-}
-
-
-/* execute a builtin handler function after parsing the arguments */
-
-/**/
-int
-execbuiltin(LinkList args, LinkList assigns, Builtin bn)
-{
- char *pp, *name, *optstr;
- int flags, argc, execop, xtr = isset(XTRACE);
- struct options ops;
-
- /* initialise options structure */
- memset(ops.ind, 0, MAX_OPS*sizeof(unsigned char));
- ops.args = NULL;
- ops.argscount = ops.argsalloc = 0;
-
- /* initialize some local variables */
- name = (char *) ugetnode(args);
-
- if (!bn->handlerfunc) {
- DPUTS(1, "Missing builtin detected too late");
- deletebuiltin(bn->node.nam);
- return 1;
- }
- /* get some information about the command */
- flags = bn->node.flags;
- optstr = bn->optstr;
-
- /* Set up the argument list. */
- /* count the arguments */
- argc = countlinknodes(args);
-
- {
- /*
- * Keep all arguments, including options, in an array.
- * We don't actually need the option part of the argument
- * after option processing, but it makes XTRACE output
- * much simpler.
- */
- VARARR(char *, argarr, argc + 1);
- char **argv;
-
- /*
- * Get the actual arguments, into argv. Remember argarr
- * may be an array declaration, depending on the compiler.
- */
- argv = argarr;
- while ((*argv++ = (char *)ugetnode(args)));
- argv = argarr;
-
- /* 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 + */
- ((sense = (*arg == '-')) ||
- ((flags & BINF_PLUSOPTS) && *arg == '+'))) {
- /* Digits aren't arguments unless the command says they are. */
- if (!(flags & BINF_KEEPNUM) && idigit(arg[1]))
- break;
- /* For cd and friends, a single dash is not an option. */
- if ((flags & BINF_SKIPDASH) && !arg[1])
- break;
- if ((flags & BINF_DASHDASHVALID) && !strcmp(arg, "--")) {
- /*
- * Need to skip this before checking whether this is
- * really an option.
- */
- argv++;
- break;
- }
- /*
- * Unrecognised options to echo etc. are not really
- * options.
- *
- * Note this flag is not smart enough to handle option
- * arguments. In fact, ideally it shouldn't be added
- * to any new builtins, to preserve standard option
- * handling as much as possible.
- */
- if (flags & BINF_SKIPINVALID) {
- char *p = arg;
- while (*++p && strchr(optstr, (int) *p));
- if (*p)
- break;
- }
- /* handle -- or - (ops.ind['-']), and +
- * (ops.ind['-'] and ops.ind['+']) */
- if (arg[1] == '-')
- arg++;
- if (!arg[1]) {
- ops.ind['-'] = 1;
- if (!sense)
- ops.ind['+'] = 1;
- }
- /* save options in ops, as long as they are in bn->optstr */
- while (*++arg) {
- char *optptr;
- if ((optptr = strchr(optstr, execop = (int)*arg))) {
- ops.ind[(int)*arg] = (sense) ? 1 : 2;
- if (optptr[1] == ':') {
- char *argptr = NULL;
- if (optptr[2] == ':') {
- if (arg[1])
- argptr = arg+1;
- /* Optional argument in same word*/
- } else if (optptr[2] == '%') {
- /* Optional numeric argument in same
- * or next word. */
- if (arg[1] && idigit(arg[1]))
- argptr = arg+1;
- else if (argv[1] && idigit(*argv[1]))
- argptr = arg = *++argv;
- } else {
- /* Mandatory argument */
- if (arg[1])
- argptr = arg+1;
- else if ((arg = *++argv))
- argptr = arg;
- else {
- zwarnnam(name, "argument expected: -%c",
- execop);
- return 1;
- }
- }
- if (argptr) {
- if (new_optarg(&ops)) {
- zwarnnam(name,
- "too many option arguments");
- return 1;
- }
- ops.ind[execop] |= ops.argscount << 2;
- ops.args[ops.argscount-1] = argptr;
- while (arg[1])
- arg++;
- }
- }
- } else
- break;
- }
- /* The above loop may have exited on an invalid option. (We *
- * assume that any option requiring metafication is invalid.) */
- if (*arg) {
- if(*arg == Meta)
- *++arg ^= 32;
- zwarnnam(name, "bad option: %c%c", "+-"[sense], *arg);
- return 1;
- }
- arg = *++argv;
- /* for the "print" builtin, the options after -R are treated as
- options to "echo" */
- if ((flags & BINF_PRINTOPTS) && ops.ind['R'] &&
- !ops.ind['f']) {
- optstr = "ne";
- flags |= BINF_SKIPINVALID;
- }
- /* the option -- indicates the end of the options */
- if (ops.ind['-'])
- break;
- }
- } else if (!(flags & BINF_HANDLES_OPTS) && *argv &&
- !strcmp(*argv, "--")) {
- ops.ind['-'] = 1;
- argv++;
- }
-
- /* handle built-in options, for overloaded handler functions */
- if ((pp = bn->defopts)) {
- while (*pp) {
- /* only if not already set */
- if (!ops.ind[(int)*pp])
- ops.ind[(int)*pp] = 1;
- pp++;
- }
- }
-
- /* Fix the argument count by subtracting option arguments */
- argc -= argv - argarr;
-
- if (errflag) {
- errflag &= ~ERRFLAG_ERROR;
- return 1;
- }
-
- /* check that the argument count lies within the specified bounds */
- if (argc < bn->minargs || (argc > bn->maxargs && bn->maxargs != -1)) {
- zwarnnam(name, (argc < bn->minargs)
- ? "not enough arguments" : "too many arguments");
- return 1;
- }
-
- /* display execution trace information, if required */
- if (xtr) {
- /* Use full argument list including options for trace output */
- char **fullargv = argarr;
- printprompt4();
- fprintf(xtrerr, "%s", name);
- while (*fullargv) {
- fputc(' ', xtrerr);
- quotedzputs(*fullargv++, xtrerr);
- }
- if (assigns) {
- LinkNode node;
- for (node = firstnode(assigns); node; incnode(node)) {
- Asgment asg = (Asgment)node;
- fputc(' ', xtrerr);
- quotedzputs(asg->name, xtrerr);
- if (asg->flags & ASG_ARRAY) {
- fprintf(xtrerr, "=(");
- if (asg->value.array) {
- if (asg->flags & ASG_KEY_VALUE) {
- LinkNode keynode, valnode;
- keynode = firstnode(asg->value.array);
- for (;;) {
- if (!keynode)
- break;
- valnode = nextnode(keynode);
- if (!valnode)
- break;
- fputc('[', xtrerr);
- quotedzputs((char *)getdata(keynode),
- xtrerr);
- fprintf(stderr, "]=");
- quotedzputs((char *)getdata(valnode),
- xtrerr);
- keynode = nextnode(valnode);
- }
- } else {
- LinkNode arrnode;
- for (arrnode = firstnode(asg->value.array);
- arrnode;
- incnode(arrnode)) {
- fputc(' ', xtrerr);
- quotedzputs((char *)getdata(arrnode),
- xtrerr);
- }
- }
- }
- fprintf(xtrerr, " )");
- } else if (asg->value.scalar) {
- fputc('=', xtrerr);
- quotedzputs(asg->value.scalar, xtrerr);
- }
- }
- }
- fputc('\n', xtrerr);
- fflush(xtrerr);
- }
- /* call the handler function, and return its return value */
- if (flags & BINF_ASSIGN)
- {
- /*
- * Takes two sets of arguments.
- */
- HandlerFuncAssign assignfunc = (HandlerFuncAssign)bn->handlerfunc;
- return (*(assignfunc)) (name, argv, assigns, &ops, bn->funcid);
- }
- else
- {
- return (*(bn->handlerfunc)) (name, argv, &ops, bn->funcid);
- }
- }
-}
-
-/* Enable/disable an element in one of the internal hash tables. *
- * With no arguments, it lists all the currently enabled/disabled *
- * elements in that particular hash table. */
-
-/**/
-int
-bin_enable(char *name, char **argv, Options ops, int func)
-{
- HashTable ht;
- HashNode hn;
- ScanFunc scanfunc;
- Patprog pprog;
- int flags1 = 0, flags2 = 0;
- int match = 0, returnval = 0;
-
- /* Find out which hash table we are working with. */
- if (OPT_ISSET(ops,'p')) {
- return pat_enables(name, argv, func == BIN_ENABLE);
- } else if (OPT_ISSET(ops,'f'))
- ht = shfunctab;
- else if (OPT_ISSET(ops,'r'))
- ht = reswdtab;
- else if (OPT_ISSET(ops,'s'))
- ht = sufaliastab;
- else if (OPT_ISSET(ops,'a'))
- ht = aliastab;
- else
- ht = builtintab;
-
- /* Do we want to enable or disable? */
- if (func == BIN_ENABLE) {
- flags2 = DISABLED;
- scanfunc = ht->enablenode;
- } else {
- flags1 = DISABLED;
- scanfunc = ht->disablenode;
- }
-
- /* Given no arguments, print the names of the enabled/disabled elements *
- * in this hash table. If func == BIN_ENABLE, then scanhashtable will *
- * print nodes NOT containing the DISABLED flag, else scanhashtable will *
- * print nodes containing the DISABLED flag. */
- if (!*argv) {
- queue_signals();
- scanhashtable(ht, 1, flags1, flags2, ht->printnode, 0);
- unqueue_signals();
- return 0;
- }
-
- /* 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)))
- match += scanmatchtable(ht, pprog, 0, 0, 0, scanfunc, 0);
- else {
- untokenize(*argv);
- zwarnnam(name, "bad pattern : %s", *argv);
- returnval = 1;
- }
- unqueue_signals();
- }
- /* If we didn't match anything, we return 1. */
- if (!match)
- returnval = 1;
- return returnval;
- }
-
- /* Take arguments literally -- do not glob */
- queue_signals();
- for (; *argv; argv++) {
- if ((hn = ht->getnode2(ht, *argv))) {
- scanfunc(hn, 0);
- } else {
- zwarnnam(name, "no such hash table element: %s", *argv);
- returnval = 1;
- }
- }
- unqueue_signals();
- return returnval;
-}
-
-/* set: either set the shell options, or set the shell arguments, *
- * or declare an array, or show various things */
-
-/**/
-int
-bin_set(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
-{
- int action, optno, array = 0, hadopt = 0,
- hadplus = 0, hadend = 0, sort = 0;
- char **x, *arrayname = NULL;
-
- /* Obsolescent sh compatibility: set - is the same as set +xv *
- * and set - args is the same as set +xv -- args */
- if (!EMULATION(EMULATE_ZSH) && *args && **args == '-' && !args[0][1]) {
- dosetopt(VERBOSE, 0, 0, opts);
- dosetopt(XTRACE, 0, 0, opts);
- if (!args[1])
- return 0;
- }
-
- /* loop through command line options (begins with "-" or "+") */
- while (*args && (**args == '-' || **args == '+')) {
- action = (**args == '-');
- hadplus |= !action;
- if(!args[0][1])
- *args = "--";
- while (*++*args) {
- if(**args == Meta)
- *++*args ^= 32;
- if(**args != '-' || action)
- hadopt = 1;
- /* The pseudo-option `--' signifies the end of options. */
- if (**args == '-') {
- hadend = 1;
- args++;
- goto doneoptions;
- } else if (**args == 'o') {
- if (!*++*args)
- args++;
- if (!*args) {
- printoptionstates(hadplus);
- inittyptab();
- return 0;
- }
- if(!(optno = optlookup(*args)))
- zerrnam(nam, "no such option: %s", *args);
- else if(dosetopt(optno, action, 0, opts))
- zerrnam(nam, "can't change option: %s", *args);
- break;
- } else if(**args == 'A') {
- if(!*++*args)
- args++;
- array = action ? 1 : -1;
- arrayname = *args;
- if (!arrayname)
- goto doneoptions;
- else if (!isset(KSHARRAYS))
- {
- args++;
- goto doneoptions;
- }
- break;
- } else if (**args == 's')
- sort = action ? 1 : -1;
- else {
- if (!(optno = optlookupc(**args)))
- zerrnam(nam, "bad option: -%c", **args);
- else if(dosetopt(optno, action, 0, opts))
- zerrnam(nam, "can't change option: -%c", **args);
- }
- }
- args++;
- }
- if (errflag)
- return 1;
- doneoptions:
- inittyptab();
-
- /* Show the parameters, possibly with values */
- queue_signals();
- if (!arrayname)
- {
- if (!hadopt && !*args)
- scanhashtable(paramtab, 1, 0, 0, paramtab->printnode,
- hadplus ? PRINT_NAMEONLY : 0);
-
- if (array) {
- /* display arrays */
- scanhashtable(paramtab, 1, PM_ARRAY, 0, paramtab->printnode,
- hadplus ? PRINT_NAMEONLY : 0);
- }
- if (!*args && !hadend) {
- unqueue_signals();
- return 0;
- }
- }
- if (sort)
- strmetasort(args, sort < 0 ? SORTIT_BACKWARDS : 0, NULL);
- if (array) {
- /* create an array with the specified elements */
- char **a = NULL, **y;
- int len = arrlen(args);
-
- if (array < 0 && (a = getaparam(arrayname)) && arrlen_gt(a, len)) {
- a += len;
- len += arrlen(a);
- }
- for (x = y = zalloc((len + 1) * sizeof(char *)); len--;) {
- if (!*args)
- args = a;
- *y++ = ztrdup(*args++);
- }
- *y++ = NULL;
- setaparam(arrayname, x);
- } else {
- /* set shell arguments */
- freearray(pparams);
- pparams = zarrdup(args);
- }
- unqueue_signals();
- return 0;
-}
-
-/**** directory-handling builtins ****/
-
-/**/
-int doprintdir = 0; /* set in exec.c (for autocd) */
-
-/* pwd: display the name of the current directory */
-
-/**/
-int
-bin_pwd(UNUSED(char *name), UNUSED(char **argv), Options ops, UNUSED(int func))
-{
- if (OPT_ISSET(ops,'r') || OPT_ISSET(ops,'P') ||
- (isset(CHASELINKS) && !OPT_ISSET(ops,'L')))
- printf("%s\n", zgetcwd());
- else {
- zputs(pwd, stdout);
- putchar('\n');
- }
- return 0;
-}
-
-/* the directory stack */
-
-/**/
-mod_export LinkList dirstack;
-
-/* dirs: list the directory stack, or replace it with a provided list */
-
-/**/
-int
-bin_dirs(UNUSED(char *name), char **argv, Options ops, UNUSED(int func))
-{
- LinkList l;
-
- queue_signals();
- /* with -v, -p or no arguments display the directory stack */
- if (!(*argv || OPT_ISSET(ops,'c')) || OPT_ISSET(ops,'v') ||
- OPT_ISSET(ops,'p')) {
- LinkNode node;
- char *fmt;
- int pos = 1;
-
- /* with the -v option, display a numbered list, starting at zero */
- if (OPT_ISSET(ops,'v')) {
- printf("0\t");
- fmt = "\n%d\t";
- /* with the -p option, display entries one per line */
- } else if (OPT_ISSET(ops,'p'))
- fmt = "\n";
- else
- fmt = " ";
- if (OPT_ISSET(ops,'l'))
- zputs(pwd, stdout);
- else
- fprintdir(pwd, stdout);
- for (node = firstnode(dirstack); node; incnode(node)) {
- printf(fmt, pos++);
- if (OPT_ISSET(ops,'l'))
- zputs(getdata(node), stdout);
- else
- fprintdir(getdata(node), stdout);
-
- }
- unqueue_signals();
- putchar('\n');
- return 0;
- }
- /* replace the stack with the specified directories */
- l = znewlinklist();
- while (*argv)
- zaddlinknode(l, ztrdup(*argv++));
- freelinklist(dirstack, freestr);
- dirstack = l;
- unqueue_signals();
- return 0;
-}
-
-/* cd, chdir, pushd, popd */
-
-/**/
-void
-set_pwd_env(void)
-{
- Param pm;
-
- /* update the PWD and OLDPWD shell parameters */
-
- pm = (Param) paramtab->getnode(paramtab, "PWD");
- if (pm && PM_TYPE(pm->node.flags) != PM_SCALAR) {
- pm->node.flags &= ~PM_READONLY;
- unsetparam_pm(pm, 0, 1);
- }
-
- pm = (Param) paramtab->getnode(paramtab, "OLDPWD");
- if (pm && PM_TYPE(pm->node.flags) != PM_SCALAR) {
- pm->node.flags &= ~PM_READONLY;
- unsetparam_pm(pm, 0, 1);
- }
-
- assignsparam("PWD", ztrdup(pwd), 0);
- assignsparam("OLDPWD", ztrdup(oldpwd), 0);
-
- pm = (Param) paramtab->getnode(paramtab, "PWD");
- if (!(pm->node.flags & PM_EXPORTED))
- addenv(pm, pwd);
- pm = (Param) paramtab->getnode(paramtab, "OLDPWD");
- if (!(pm->node.flags & PM_EXPORTED))
- addenv(pm, oldpwd);
-}
-
-/* set if we are resolving links to their true paths */
-static int chasinglinks;
-
-/* The main pwd changing function. The real work is done by other *
- * functions. cd_get_dest() does the initial argument processing; *
- * cd_do_chdir() actually changes directory, if possible; cd_new_pwd() *
- * does the ancillary processing associated with actually changing *
- * directory. */
-
-/**/
-int
-bin_cd(char *nam, char **argv, Options ops, int func)
-{
- LinkNode dir;
- struct stat st1, st2;
-
- if (isset(RESTRICTED)) {
- zwarnnam(nam, "restricted");
- return 1;
- }
- doprintdir = (doprintdir == -1);
-
- chasinglinks = OPT_ISSET(ops,'P') ||
- (isset(CHASELINKS) && !OPT_ISSET(ops,'L'));
- queue_signals();
- zpushnode(dirstack, ztrdup(pwd));
- if (!(dir = cd_get_dest(nam, argv, OPT_ISSET(ops,'s'), func))) {
- zsfree(getlinknode(dirstack));
- unqueue_signals();
- return 1;
- }
- cd_new_pwd(func, dir, OPT_ISSET(ops, 'q'));
-
- if (stat(unmeta(pwd), &st1) < 0) {
- setjobpwd();
- zsfree(pwd);
- pwd = NULL;
- pwd = metafy(zgetcwd(), -1, META_DUP);
- } else if (stat(".", &st2) < 0) {
- if (chdir(unmeta(pwd)) < 0)
- zwarn("unable to chdir(%s): %e", pwd, errno);
- } else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) {
- if (chasinglinks) {
- setjobpwd();
- zsfree(pwd);
- pwd = NULL;
- pwd = metafy(zgetcwd(), -1, META_DUP);
- } else if (chdir(unmeta(pwd)) < 0)
- zwarn("unable to chdir(%s): %e", pwd, errno);
- }
- unqueue_signals();
- return 0;
-}
-
-/* Get directory to chdir to */
-
-/**/
-static LinkNode
-cd_get_dest(char *nam, char **argv, int hard, int func)
-{
- LinkNode dir = NULL;
- LinkNode target;
- char *dest;
-
- if (!argv[0]) {
- if (func == BIN_POPD && !nextnode(firstnode(dirstack))) {
- zwarnnam(nam, "directory stack empty");
- return NULL;
- }
- if (func == BIN_PUSHD && unset(PUSHDTOHOME))
- dir = nextnode(firstnode(dirstack));
- if (dir)
- zinsertlinknode(dirstack, dir, getlinknode(dirstack));
- else if (func != BIN_POPD) {
- if (!home) {
- zwarnnam(nam, "HOME not set");
- return NULL;
- }
- zpushnode(dirstack, ztrdup(home));
- }
- } else if (!argv[1]) {
- int dd;
- char *end;
-
- doprintdir++;
- if (argv[0][1] && (argv[0][0] == '+' || argv[0][0] == '-')
- && strspn(argv[0]+1, "0123456789") == strlen(argv[0]+1)) {
- dd = zstrtol(argv[0] + 1, &end, 10);
- if (*end == '\0') {
- if ((argv[0][0] == '+') ^ isset(PUSHDMINUS))
- for (dir = firstnode(dirstack); dir && dd; dd--, incnode(dir));
- else
- for (dir = lastnode(dirstack); dir != (LinkNode) dirstack && dd;
- dd--, dir = prevnode(dir));
- if (!dir || dir == (LinkNode) dirstack) {
- zwarnnam(nam, "no such entry in dir stack");
- return NULL;
- }
- }
- }
- if (!dir)
- zpushnode(dirstack, ztrdup(strcmp(argv[0], "-")
- ? (doprintdir--, argv[0]) : oldpwd));
- } else {
- char *u, *d;
- int len1, len2, len3;
-
- if (!(u = strstr(pwd, argv[0]))) {
- zwarnnam(nam, "string not in pwd: %s", argv[0]);
- return NULL;
- }
- len1 = strlen(argv[0]);
- len2 = strlen(argv[1]);
- len3 = u - pwd;
- d = (char *)zalloc(len3 + len2 + strlen(u + len1) + 1);
- strncpy(d, pwd, len3);
- strcpy(d + len3, argv[1]);
- strcat(d, u + len1);
- zpushnode(dirstack, d);
- doprintdir++;
- }
-
- target = dir;
- if (func == BIN_POPD) {
- if (!dir) {
- target = dir = firstnode(dirstack);
- } else if (dir != firstnode(dirstack)) {
- return dir;
- }
- dir = nextnode(dir);
- }
- if (!dir) {
- dir = firstnode(dirstack);
- }
- if (!dir || !getdata(dir)) {
- DPUTS(1, "Directory not set, not detected early enough");
- return NULL;
- }
- if (!(dest = cd_do_chdir(nam, getdata(dir), hard))) {
- if (!target)
- zsfree(getlinknode(dirstack));
- if (func == BIN_POPD)
- zsfree(remnode(dirstack, dir));
- return NULL;
- }
- if (dest != (char *)getdata(dir)) {
- zsfree(getdata(dir));
- setdata(dir, dest);
- }
- return target ? target : dir;
-}
-
-/* Change to given directory, if possible. This function works out *
- * exactly how the directory should be interpreted, including cdpath *
- * and CDABLEVARS. For each possible interpretation of the given *
- * path, this calls cd_try_chdir(), which attempts to chdir to that *
- * particular path. */
-
-/**/
-static char *
-cd_do_chdir(char *cnam, char *dest, int hard)
-{
- char **pp, *ret;
- int hasdot = 0, eno = ENOENT;
- /*
- * nocdpath indicates that cdpath should not be used.
- * This is the case iff dest is a relative path
- * whose first segment is . or .., but if the path is
- * absolute then cdpath won't be used anyway.
- */
- int nocdpath;
-#ifdef __CYGWIN__
- /*
- * Normalize path under Cygwin to avoid messing with
- * DOS style names with drives in them
- */
- 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);
-#else
-#ifndef _SYS_CYGWIN_H
- void cygwin_conv_to_posix_path(const char *, char *);
-#endif
-
- cygwin_conv_to_posix_path(dest, buf);
-#endif
- dest = buf;
-#endif
- nocdpath = dest[0] == '.' &&
- (dest[1] == '/' || !dest[1] || (dest[1] == '.' &&
- (dest[2] == '/' || !dest[2])));
-
- /*
- * If we have an absolute path, use it as-is only
- */
- if (*dest == '/') {
- if ((ret = cd_try_chdir(NULL, dest, hard)))
- return ret;
- zwarnnam(cnam, "%e: %s", errno, dest);
- return NULL;
- }
-
- /*
- * If cdpath is being used, check it for ".".
- * Don't bother doing this if POSIXCD is set, we don't
- * need to know (though it doesn't actually matter).
- */
- if (!nocdpath && !isset(POSIXCD))
- for (pp = cdpath; *pp; pp++)
- if (!(*pp)[0] || ((*pp)[0] == '.' && (*pp)[1] == '\0'))
- hasdot = 1;
- /*
- * If
- * (- there is no . in cdpath
- * - or cdpath is not being used)
- * - and the POSIXCD option is not set
- * try the directory as-is (i.e. from .)
- */
- if (!hasdot && !isset(POSIXCD)) {
- if ((ret = cd_try_chdir(NULL, dest, hard)))
- return ret;
- if (errno != ENOENT)
- eno = errno;
- }
- /* if cdpath is being used, try given directory relative to each element in
- cdpath in turn */
- if (!nocdpath)
- for (pp = cdpath; *pp; pp++) {
- if ((ret = cd_try_chdir(*pp, dest, hard))) {
- if (isset(POSIXCD)) {
- /*
- * For POSIX we need to print the directory
- * any time CDPATH was used, except in the
- * special case of an empty segment being
- * treated as a ".".
- */
- if (**pp)
- doprintdir++;
- } else {
- if (strcmp(*pp, ".")) {
- doprintdir++;
- }
- }
- return ret;
- }
- if (errno != ENOENT)
- eno = errno;
- }
- /*
- * POSIX requires us to check "." after CDPATH rather than before.
- */
- if (isset(POSIXCD)) {
- if ((ret = cd_try_chdir(NULL, dest, hard)))
- return ret;
- if (errno != ENOENT)
- eno = errno;
- }
-
- /* handle the CDABLEVARS option */
- if ((ret = cd_able_vars(dest))) {
- if ((ret = cd_try_chdir(NULL, ret,hard))) {
- doprintdir++;
- return ret;
- }
- if (errno != ENOENT)
- eno = errno;
- }
-
- /* If we got here, it means that we couldn't chdir to any of the
- multitudinous possible paths allowed by zsh. We've run out of options!
- Add more here! */
- zwarnnam(cnam, "%e: %s", eno, dest);
- return NULL;
-}
-
-/* If the CDABLEVARS option is set, return the new *
- * interpretation of the given path. */
-
-/**/
-char *
-cd_able_vars(char *s)
-{
- char *rest, save;
-
- if (isset(CDABLEVARS)) {
- for (rest = s; *rest && *rest != '/'; rest++);
- save = *rest;
- *rest = 0;
- s = getnameddir(s);
- *rest = save;
-
- if (s && *rest)
- s = dyncat(s, rest);
-
- return s;
- }
- return NULL;
-}
-
-/* Attempt to change to a single given directory. The directory, *
- * for the convenience of the calling function, may be provided in *
- * two parts, which must be concatenated before attempting to chdir. *
- * Returns NULL if the chdir fails. If the directory change is *
- * possible, it is performed, and a pointer to the new full pathname *
- * is returned. */
-
-/**/
-static char *
-cd_try_chdir(char *pfix, char *dest, int hard)
-{
- char *buf;
- int dlen, dochaselinks = 0;
-
- /* handle directory prefix */
- if (pfix && *pfix) {
- if (*pfix == '/') {
-#ifdef __CYGWIN__
-/* NB: Don't turn "/"+"bin" into "//"+"bin" by mistake! "//bin" may *
- * not be what user really wants (probably wants "/bin"), but *
- * "//bin" could be valid too (see fixdir())! This is primarily for *
- * handling CDPATH correctly. Likewise for "//"+"bin" not becoming *
- * "///bin" (aka "/bin"). */
- int root = pfix[1] == '\0' || (pfix[1] == '/' && pfix[2] == '\0');
- buf = tricat(pfix, ( root ? "" : "/" ), dest);
-#else
- buf = tricat(pfix, "/", dest);
-#endif
- } else {
- int pfl = strlen(pfix);
- dlen = strlen(pwd);
- if (dlen == 1 && *pwd == '/')
- dlen = 0;
- buf = zalloc(dlen + pfl + strlen(dest) + 3);
- if (dlen)
- strcpy(buf, pwd);
- buf[dlen] = '/';
- strcpy(buf + dlen + 1, pfix);
- buf[dlen + 1 + pfl] = '/';
- strcpy(buf + dlen + pfl + 2, dest);
- }
- } else if (*dest == '/')
- buf = ztrdup(dest);
- else {
- dlen = strlen(pwd);
- if (pwd[dlen-1] == '/')
- --dlen;
- buf = zalloc(dlen + strlen(dest) + 2);
- strcpy(buf, pwd);
- buf[dlen] = '/';
- strcpy(buf + dlen + 1, dest);
- }
-
- /* Normalise path. See the definition of fixdir() for what this means.
- * We do not do this if we are chasing links.
- */
- if (!chasinglinks)
- dochaselinks = fixdir(buf);
- else
- unmetafy(buf, &dlen);
-
- /* We try the full path first. If that fails, try the
- * argument to cd relatively. This is useful if the cwd
- * or a parent directory is renamed in the interim.
- */
- if (lchdir(buf, NULL, hard) &&
- (pfix || *dest == '/' || lchdir(unmeta(dest), NULL, hard))) {
- free(buf);
- return NULL;
- }
- /* the chdir succeeded, so decide if we should force links to be chased */
- if (dochaselinks)
- chasinglinks = 1;
- return metafy(buf, -1, META_NOALLOC);
-}
-
-/* do the extra processing associated with changing directory */
-
-/**/
-static void
-cd_new_pwd(int func, LinkNode dir, int quiet)
-{
- char *new_pwd, *s;
- int dirstacksize;
-
- if (func == BIN_PUSHD)
- rolllist(dirstack, dir);
- new_pwd = remnode(dirstack, dir);
-
- if (func == BIN_POPD && firstnode(dirstack)) {
- zsfree(new_pwd);
- new_pwd = getlinknode(dirstack);
- } else if (func == BIN_CD && unset(AUTOPUSHD))
- zsfree(getlinknode(dirstack));
-
- if (chasinglinks) {
- s = findpwd(new_pwd);
- if (s) {
- zsfree(new_pwd);
- new_pwd = s;
- }
- }
- if (isset(PUSHDIGNOREDUPS)) {
- LinkNode n;
- for (n = firstnode(dirstack); n; incnode(n)) {
- if (!strcmp(new_pwd, getdata(n))) {
- zsfree(remnode(dirstack, n));
- break;
- }
- }
- }
-
- /* shift around the pwd variables, to make oldpwd and pwd relate to the
- current (i.e. new) pwd */
- zsfree(oldpwd);
- oldpwd = pwd;
- setjobpwd();
- pwd = new_pwd;
- set_pwd_env();
-
- if (isset(INTERACTIVE) || isset(POSIXCD)) {
- if (func != BIN_CD && isset(INTERACTIVE)) {
- if (unset(PUSHDSILENT) && !quiet)
- printdirstack();
- } else if (doprintdir) {
- fprintdir(pwd, stdout);
- putchar('\n');
- }
- }
-
- /* execute the chpwd function */
- fflush(stdout);
- fflush(stderr);
- if (!quiet)
- callhookfunc("chpwd", NULL, 1, NULL);
-
- dirstacksize = getiparam("DIRSTACKSIZE");
- /* handle directory stack sizes out of range */
- if (dirstacksize > 0) {
- int remove = countlinknodes(dirstack) -
- (dirstacksize < 2 ? 2 : dirstacksize);
- while (remove-- >= 0)
- zsfree(remnode(dirstack, lastnode(dirstack)));
- }
-}
-
-/* Print the directory stack */
-
-/**/
-static void
-printdirstack(void)
-{
- LinkNode node;
-
- fprintdir(pwd, stdout);
- for (node = firstnode(dirstack); node; incnode(node)) {
- putchar(' ');
- fprintdir(getdata(node), stdout);
- }
- putchar('\n');
-}
-
-/* Normalise a path. Segments consisting of ., and foo/.. *
- * combinations, are removed and the path is unmetafied.
- * Returns 1 if we found a ../ path which should force links to
- * be chased, 0 otherwise.
- */
-
-/**/
-int
-fixdir(char *src)
-{
- char *dest = src, *d0 = dest;
-#ifdef __CYGWIN__
- char *s0 = src;
-#endif
- /* 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
- /* allow /.. segments to remain */
- while (*src == '/' && src[1] == '.' && src[2] == '.' &&
- (!src[3] || src[3] == '/')) {
- *dest++ = '/';
- *dest++ = '.';
- *dest++ = '.';
- src += 3;
- }
-#endif
-
- for (;;) {
- /* compress multiple /es into single */
- if (*src == '/') {
-#ifdef __CYGWIN__
- /* allow leading // under cygwin, but /// still becomes / */
- if (src == s0 && src[1] == '/' && src[2] != '/')
- *dest++ = *src++;
-#endif
- *dest++ = *src++;
- while (*src == '/')
- src++;
- }
- /* if we are at the end of the input path, remove a trailing / (if it
- exists), and return ct */
- if (!*src) {
- while (dest > d0 + 1 && dest[-1] == '/')
- dest--;
- *dest = '\0';
- return chasedots;
- }
- if (src[0] == '.' && src[1] == '.' &&
- (src[2] == '\0' || src[2] == '/')) {
- if (isset(CHASEDOTS) || chasedots > 1) {
- chasedots = 1;
- /* and treat as normal path segment */
- } else {
- if (dest > d0 + 1) {
- /*
- * remove a foo/.. combination:
- * first check foo exists, else return.
- */
- struct stat st;
- *dest = '\0';
- if (stat(d0, &st) < 0 || !S_ISDIR(st.st_mode)) {
- char *ptrd, *ptrs;
- if (dest == src)
- *dest = '.';
- for (ptrs = src, ptrd = dest; *ptrs; ptrs++, ptrd++)
- *ptrd = (*ptrs == Meta) ? (*++ptrs ^ 32) : *ptrs;
- *ptrd = '\0';
- return 1;
- }
- for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--);
- if (dest[-1] != '/')
- dest--;
- }
- src++;
- while (*++src == '/');
- continue;
- }
- }
- if (src[0] == '.' && (src[1] == '/' || src[1] == '\0')) {
- /* skip a . section */
- while (*++src == '/');
- } else {
- /* copy a normal segment into the output */
- while (*src != '/' && *src != '\0')
- if ((*dest++ = *src++) == Meta)
- dest[-1] = *src++ ^ 32;
- }
- }
- /* unreached */
-}
-
-/**/
-mod_export void
-printqt(char *str)
-{
- /* Print str, but turn any single quote into '\'' or ''. */
- for (; *str; str++)
- if (*str == '\'')
- printf(isset(RCQUOTES) ? "''" : "'\\''");
- else
- putchar(*str);
-}
-
-/**/
-mod_export void
-printif(char *str, int c)
-{
- /* If flag c has an argument, print that */
- if (str) {
- printf(" -%c ", c);
- quotedzputs(str, stdout);
- }
-}
-
-/**** history list functions ****/
-
-/* fc, history, r */
-
-/**/
-int
-bin_fc(char *nam, char **argv, Options ops, int func)
-{
- zlong first = -1, last = -1;
- int retval;
- char *s;
- struct asgment *asgf = NULL, *asgl = NULL;
- Patprog pprog = NULL;
-
- /* fc is only permitted in interactive shells */
-#ifdef FACIST_INTERACTIVE
- if (!interact) {
- zwarnnam(nam, "not interactive shell");
- return 1;
- }
-#endif
- if (OPT_ISSET(ops,'p')) {
- char *hf = "";
- zlong hs = DEFAULT_HISTSIZE;
- zlong shs = 0;
- int level = OPT_ISSET(ops,'a') ? locallevel : -1;
- if (*argv) {
- hf = *argv++;
- if (*argv) {
- char *check;
- hs = zstrtol(*argv++, &check, 10);
- if (*check) {
- zwarnnam("fc", "HISTSIZE must be an integer");
- return 1;
- }
- if (*argv) {
- shs = zstrtol(*argv++, &check, 10);
- if (*check) {
- zwarnnam("fc", "SAVEHIST must be an integer");
- return 1;
- }
- } else
- shs = hs;
- if (*argv) {
- zwarnnam("fc", "too many arguments");
- return 1;
- }
- } else {
- hs = histsiz;
- shs = savehistsiz;
- }
- }
- if (!pushhiststack(hf, hs, shs, level))
- return 1;
- if (*hf) {
- struct stat st;
- if (stat(hf, &st) >= 0 || errno != ENOENT)
- readhistfile(hf, 1, HFILE_USE_OPTIONS);
- }
- return 0;
- }
- if (OPT_ISSET(ops,'P')) {
- if (*argv) {
- zwarnnam("fc", "too many arguments");
- return 1;
- }
- return !saveandpophiststack(-1, HFILE_USE_OPTIONS);
- }
- /* with the -m option, the first argument is taken *
- * as a pattern that history lines have to match */
- if (*argv && OPT_ISSET(ops,'m')) {
- tokenize(*argv);
- if (!(pprog = patcompile(*argv++, 0, NULL))) {
- zwarnnam(nam, "invalid match pattern");
- return 1;
- }
- }
- queue_signals();
- if (OPT_ISSET(ops,'R')) {
- /* read history from a file */
- readhistfile(*argv, 1, OPT_ISSET(ops,'I') ? HFILE_SKIPOLD : 0);
- unqueue_signals();
- return 0;
- }
- if (OPT_ISSET(ops,'W')) {
- /* write history to a file */
- savehistfile(*argv, 1, OPT_ISSET(ops,'I') ? HFILE_SKIPOLD : 0);
- unqueue_signals();
- return 0;
- }
- if (OPT_ISSET(ops,'A')) {
- /* append history to a file */
- savehistfile(*argv, 1, HFILE_APPEND |
- (OPT_ISSET(ops,'I') ? HFILE_SKIPOLD : 0));
- unqueue_signals();
- return 0;
- }
-
- if (zleactive) {
- unqueue_signals();
- zwarnnam(nam, "no interactive history within ZLE");
- return 1;
- }
-
- /* put foo=bar type arguments into the substitution list */
- while (*argv && equalsplit(*argv, &s)) {
- Asgment a = (Asgment) zhalloc(sizeof *a);
-
- if (!**argv) {
- zwarnnam(nam, "invalid replacement pattern: =%s", s);
- return 1;
- }
- if (!asgf)
- asgf = asgl = a;
- else {
- asgl->node.next = &a->node;
- asgl = a;
- }
- a->name = *argv;
- a->flags = 0;
- a->value.scalar = s;
- a->node.next = a->node.prev = NULL;
- argv++;
- }
- /* interpret and check first history line specifier */
- if (*argv) {
- first = fcgetcomm(*argv);
- if (first == -1) {
- unqueue_signals();
- return 1;
- }
- argv++;
- }
- /* interpret and check second history line specifier */
- if (*argv) {
- last = fcgetcomm(*argv);
- if (last == -1) {
- unqueue_signals();
- return 1;
- }
- argv++;
- }
- /* There is a maximum of two history specifiers. At least, there *
- * will be as long as the history list is one-dimensional. */
- if (*argv) {
- unqueue_signals();
- zwarnnam("fc", "too many arguments");
- return 1;
- }
- /* default values of first and last, and range checking */
- if (last == -1) {
- if (OPT_ISSET(ops,'l') && first < curhist) {
- /*
- * When listing base our calculations on curhist,
- * to show anything added since the edited history line.
- * Also, in that case curhist will have been modified
- * past the current history line; then we want to
- * show everything, because the user expects to
- * see the result of "print -s". Otherwise, we subtract
- * -1 from the line, because the user doesn't usually expect
- * to see the command line that caused history to be
- * listed.
- */
- last = (curline.histnum == curhist) ? addhistnum(curhist,-1,0)
- : curhist;
- if (last < firsthist())
- last = firsthist();
- }
- else
- last = first;
- }
- if (first == -1) {
- /*
- * When listing, we want to see everything that's been
- * added to the history, including by print -s, so use
- * curhist.
- * When reexecuting, we want to restrict to the last edited
- * command line to avoid giving the user a nasty turn
- * if some helpful soul ran "print -s 'rm -rf /'".
- */
- first = OPT_ISSET(ops,'l')? addhistnum(curhist,-16,0)
- : addhistnum(curline.histnum,-1,0);
- if (first < 1)
- first = 1;
- if (last < first)
- last = first;
- }
- if (OPT_ISSET(ops,'l')) {
- /* list the required part of the history */
- retval = fclist(stdout, ops, first, last, asgf, pprog, 0);
- unqueue_signals();
- }
- else {
- /* edit history file, and (if successful) use the result as a new command */
- int tempfd;
- FILE *out;
- char *fil;
-
- retval = 1;
- if ((tempfd = gettempfile(NULL, 1, &fil)) < 0
- || ((out = fdopen(tempfd, "w")) == NULL)) {
- unqueue_signals();
- zwarnnam("fc", "can't open temp file: %e", errno);
- } else {
- /*
- * Nasty behaviour results if we use the current history
- * line here. Treat it as if it doesn't exist, unless
- * that gives us an empty range.
- */
- if (last >= curhist) {
- last = curhist - 1;
- if (first > last) {
- unqueue_signals();
- zwarnnam("fc",
- "current history line would recurse endlessly, aborted");
- fclose(out);
- unlink(fil);
- return 1;
- }
- }
- ops->ind['n'] = 1; /* No line numbers here. */
- if (!fclist(out, ops, first, last, asgf, pprog, 1)) {
- char *editor;
-
- if (func == BIN_R)
- editor = "-";
- else if (OPT_HASARG(ops, 'e'))
- editor = OPT_ARG(ops, 'e');
- else
- editor = getsparam("FCEDIT");
- if (!editor)
- editor = getsparam("EDITOR");
- if (!editor)
- editor = DEFAULT_FCEDIT;
-
- unqueue_signals();
- if (fcedit(editor, fil)) {
- if (stuff(fil))
- zwarnnam("fc", "%e: %s", errno, fil);
- else {
- loop(0,1);
- retval = lastval;
- }
- }
- } else
- unqueue_signals();
- }
- unlink(fil);
- }
- return retval;
-}
-
-/* History handling functions: these are called by ZLE, as well as *
- * the actual builtins. fcgetcomm() gets a history line, specified *
- * either by number or leading string. fcsubs() performs a given *
- * set of simple old=new substitutions on a given command line. *
- * fclist() outputs a given range of history lines to a text file. */
-
-/* get the history event associated with s */
-
-/**/
-static zlong
-fcgetcomm(char *s)
-{
- zlong cmd;
-
- /* First try to match a history number. Negative *
- * numbers indicate reversed numbering. */
- if ((cmd = atoi(s)) != 0 || *s == '0') {
- if (cmd < 0)
- cmd = addhistnum(curline.histnum,cmd,HIST_FOREIGN);
- if (cmd < 0)
- cmd = 0;
- return cmd;
- }
- /* not a number, so search by string */
- cmd = hcomsearch(s);
- if (cmd == -1)
- zwarnnam("fc", "event not found: %s", s);
- return cmd;
-}
-
-/* Perform old=new substitutions. Uses the asgment structure from zsh.h, *
- * which is essentially a linked list of string,replacement pairs. */
-
-/**/
-static int
-fcsubs(char **sp, struct asgment *sub)
-{
- char *oldstr, *newstr, *oldpos, *newpos, *newmem, *s = *sp;
- int subbed = 0;
-
- /* loop through the linked list */
- while (sub) {
- oldstr = sub->name;
- newstr = sub->value.scalar;
- sub = (Asgment)sub->node.next;
- oldpos = s;
- /* loop over occurences of oldstr in s, replacing them with newstr */
- while ((newpos = (char *)strstr(oldpos, oldstr))) {
- newmem = (char *) zhalloc(1 + (newpos - s)
- + strlen(newstr) + strlen(newpos + strlen(oldstr)));
- ztrncpy(newmem, s, newpos - s);
- strcat(newmem, newstr);
- oldpos = newmem + strlen(newmem);
- strcat(newmem, newpos + strlen(oldstr));
- s = newmem;
- subbed = 1;
- }
- }
- *sp = s;
- return subbed;
-}
-
-/* Print a series of history events to a file. The file pointer is *
- * given by f, and the required range of events by first and last. *
- * subs is an optional list of foo=bar substitutions to perform on the *
- * history lines before output. com is an optional comp structure *
- * that the history lines are required to match. n, r, D and d are *
- * options: n indicates that each line should be numbered. r indicates *
- * that the lines should be output in reverse order (newest first). *
- * D indicates that the real time taken by each command should be *
- * output. d indicates that the time of execution of each command *
- * should be output; d>1 means that the date should be output too; d>3 *
- * means that mm/dd/yyyy form should be used for the dates, as opposed *
- * to dd.mm.yyyy form; d>7 means that yyyy-mm-dd form should be used. */
-
-/**/
-static int
-fclist(FILE *f, Options ops, zlong first, zlong last,
- struct asgment *subs, Patprog pprog, int is_command)
-{
- int fclistdone = 0, xflags = 0;
- zlong tmp;
- char *s, *tdfmt, *timebuf;
- Histent ent;
-
- /* reverse range if required */
- if (OPT_ISSET(ops,'r')) {
- tmp = last;
- last = first;
- first = tmp;
- }
- if (is_command && first > last) {
- zwarnnam("fc", "history events can't be executed backwards, aborted");
- if (f != stdout)
- fclose(f);
- return 1;
- }
-
- ent = gethistent(first, first < last? GETHIST_DOWNWARD : GETHIST_UPWARD);
- if (!ent || (first < last? ent->histnum > last : ent->histnum < last)) {
- if (first == last) {
- char buf[DIGBUFSIZE];
- convbase(buf, first, 10);
- zwarnnam("fc", "no such event: %s", buf);
- } else
- zwarnnam("fc", "no events in that range");
- if (f != stdout)
- fclose(f);
- return 1;
- }
-
- if (OPT_ISSET(ops,'d') || OPT_ISSET(ops,'f') ||
- OPT_ISSET(ops,'E') || OPT_ISSET(ops,'i') ||
- OPT_ISSET(ops,'t')) {
- if (OPT_ISSET(ops,'t')) {
- tdfmt = OPT_ARG(ops,'t');
- } else if (OPT_ISSET(ops,'i')) {
- tdfmt = "%Y-%m-%d %H:%M";
- } else if (OPT_ISSET(ops,'E')) {
- tdfmt = "%f.%-m.%Y %H:%M";
- } else if (OPT_ISSET(ops,'f')) {
- tdfmt = "%-m/%f/%Y %H:%M";
- } else {
- tdfmt = "%H:%M";
- }
- timebuf = zhalloc(256);
- } else {
- tdfmt = timebuf = NULL;
- }
-
- /* xflags exclude events */
- if (OPT_ISSET(ops,'L')) {
- xflags |= HIST_FOREIGN;
- }
- if (OPT_ISSET(ops,'I')) {
- xflags |= HIST_READ;
- }
-
- for (;;) {
- if (ent->node.flags & xflags)
- s = NULL;
- else
- s = dupstring(ent->node.nam);
- /* this if does the pattern matching, if required */
- if (s && (!pprog || pattry(pprog, s))) {
- /* perform substitution */
- fclistdone |= (subs ? fcsubs(&s, subs) : 1);
-
- /* do numbering */
- if (!OPT_ISSET(ops,'n')) {
- char buf[DIGBUFSIZE];
- convbase(buf, ent->histnum, 10);
- fprintf(f, "%5s%c ", buf,
- ent->node.flags & HIST_FOREIGN ? '*' : ' ');
- }
- /* output actual time (and possibly date) of execution of the
- command, if required */
- if (tdfmt != NULL) {
- struct tm *ltm;
- int len;
- ltm = localtime(&ent->stim);
- if ((len = ztrftime(timebuf, 256, tdfmt, ltm, 0L)) >= 0) {
- fwrite(timebuf, 1, len, f);
- fprintf(f, " ");
- }
- }
- /* display the time taken by the command, if required */
- if (OPT_ISSET(ops,'D')) {
- long diff;
- diff = (ent->ftim) ? ent->ftim - ent->stim : 0;
- fprintf(f, "%ld:%02ld ", diff / 60, diff % 60);
- }
-
- /* output the command */
- if (f == stdout) {
- nicezputs(s, f);
- putc('\n', f);
- } else {
- int len;
- unmetafy(s, &len);
- fwrite(s, 1, len, f);
- putc('\n', f);
- }
- }
- /* move on to the next history line, or quit the loop */
- if (first < last) {
- if (!(ent = down_histent(ent)) || ent->histnum > last)
- break;
- }
- else {
- if (!(ent = up_histent(ent)) || ent->histnum < last)
- break;
- }
- }
-
- /* final processing */
- if (f != stdout)
- fclose(f);
- if (!fclistdone) {
- if (subs)
- zwarnnam("fc", "no substitutions performed");
- else if (xflags || pprog)
- zwarnnam("fc", "no matching events found");
- return 1;
- }
- return 0;
-}
-
-/* edit a history file */
-
-/**/
-static int
-fcedit(char *ename, char *fn)
-{
- char *s;
-
- if (!strcmp(ename, "-"))
- return 1;
-
- s = tricat(ename, " ", fn);
- execstring(s, 1, 0, "fc");
- zsfree(s);
-
- return !lastval;
-}
-
-/**** parameter builtins ****/
-
-/* Separate an argument into name=value parts, returning them in an *
- * asgment structure. Because the asgment structure used is global, *
- * only one of these can be active at a time. The string s gets placed *
- * in this global structure, so it needs to be in permanent memory. */
-
-/**/
-static Asgment
-getasg(char ***argvp, LinkList assigns)
-{
- char *s = **argvp;
- static struct asgment asg;
-
- /* sanity check for valid argument */
- if (!s) {
- if (assigns) {
- Asgment asgp = (Asgment)firstnode(assigns);
- if (!asgp)
- return NULL;
- (void)uremnode(assigns, &asgp->node);
- return asgp;
- }
- return NULL;
- }
-
- /* check if name is empty */
- if (*s == '=') {
- zerr("bad assignment");
- return NULL;
- }
- asg.name = s;
- asg.flags = 0;
-
- /* search for `=' */
- for (; *s && *s != '='; s++);
-
- /* found `=', so return with a value */
- if (*s) {
- *s = '\0';
- asg.value.scalar = s + 1;
- } else {
- /* didn't find `=', so we only have a name */
- asg.value.scalar = NULL;
- }
- (*argvp)++;
- return &asg;
-}
-
-/* for new special parameters */
-enum {
- NS_NONE,
- NS_NORMAL,
- NS_SECONDS
-};
-
-static const struct gsu_scalar tiedarr_gsu =
-{ tiedarrgetfn, tiedarrsetfn, tiedarrunsetfn };
-
-/* Install a base if we are turning on a numeric option with an argument */
-
-static int
-typeset_setbase(const char *name, Param pm, Options ops, int on, int always)
-{
- char *arg = NULL;
-
- if ((on & PM_INTEGER) && OPT_HASARG(ops,'i'))
- arg = OPT_ARG(ops,'i');
- else if ((on & PM_EFLOAT) && OPT_HASARG(ops,'E'))
- arg = OPT_ARG(ops,'E');
- else if ((on & PM_FFLOAT) && OPT_HASARG(ops,'F'))
- arg = OPT_ARG(ops,'F');
-
- if (arg) {
- char *eptr;
- int base = (int)zstrtol(arg, &eptr, 10);
- if (*eptr) {
- if (on & PM_INTEGER)
- zwarnnam(name, "bad base value: %s", arg);
- else
- zwarnnam(name, "bad precision value: %s", arg);
- return 1;
- }
- if ((on & PM_INTEGER) && (base < 2 || base > 36)) {
- zwarnnam(name, "invalid base (must be 2 to 36 inclusive): %d",
- base);
- return 1;
- }
- pm->base = base;
- } else if (always)
- pm->base = 0;
-
- return 0;
-}
-
-/* Install a width if we are turning on a padding option with an argument */
-
-static int
-typeset_setwidth(const char * name, Param pm, Options ops, int on, int always)
-{
- char *arg = NULL;
-
- if ((on & PM_LEFT) && OPT_HASARG(ops,'L'))
- arg = OPT_ARG(ops,'L');
- else if ((on & PM_RIGHT_B) && OPT_HASARG(ops,'R'))
- arg = OPT_ARG(ops,'R');
- else if ((on & PM_RIGHT_Z) && OPT_HASARG(ops,'Z'))
- arg = OPT_ARG(ops,'Z');
-
- if (arg) {
- char *eptr;
- pm->width = (int)zstrtol(arg, &eptr, 10);
- if (*eptr) {
- zwarnnam(name, "bad width value: %s", arg);
- return 1;
- }
- } else if (always)
- pm->width = 0;
-
- return 0;
-}
-
-/* function to set a single parameter */
-
-/**/
-static Param
-typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
- int on, int off, int roff, Asgment asg, Param altpm,
- Options ops, int joinchar)
-{
- int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly, dont_set = 0;
- char *subscript;
-
- /*
- * Do we use the existing pm? Note that this isn't the end of the
- * story, because if we try and create a new pm at the same
- * locallevel as an unset one we use the pm struct anyway: that's
- * 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' or 'export'
- * flags of an unset parameter.
- */
- usepm = pm && (!(pm->node.flags & PM_UNSET) ||
- (isset(POSIXBUILTINS) &&
- (pm->node.flags & (PM_READONLY|PM_EXPORTED))));
-
- /*
- * We need to compare types with an existing pm if special,
- * even if that's unset
- */
- if (!usepm && pm && (pm->node.flags & PM_SPECIAL))
- usepm = 2; /* indicate that we preserve the PM_UNSET flag */
-
- /*
- * Don't use an existing param if
- * - the local level has changed, and
- * - we are really locallizing the parameter
- */
- if (usepm && locallevel != pm->level && (on & PM_LOCAL)) {
- /*
- * If the original parameter was special and we're creating
- * a new one, we need to keep it special.
- *
- * The -h (hide) flag prevents an existing special being made
- * local. It can be applied either to the special or in the
- * typeset/local statement for the local variable.
- */
- if ((pm->node.flags & PM_SPECIAL)
- && !(on & PM_HIDE) && !(pm->node.flags & PM_HIDE & ~off))
- newspecial = NS_NORMAL;
- usepm = 0;
- }
-
- /* attempting a type conversion, or making a tied colonarray? */
- tc = 0;
- if (ASG_ARRAYP(asg) && PM_TYPE(on) == PM_SCALAR &&
- !(usepm && (PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED))))
- on |= PM_ARRAY;
- if (usepm && ASG_ARRAYP(asg) && newspecial == NS_NONE &&
- PM_TYPE(pm->node.flags) != PM_ARRAY &&
- PM_TYPE(pm->node.flags) != PM_HASHED) {
- if (on & (PM_EFLOAT|PM_FFLOAT|PM_INTEGER)) {
- zerrnam(cname, "%s: can't assign array value to non-array", pname);
- return NULL;
- }
- if (pm->node.flags & PM_SPECIAL) {
- zerrnam(cname, "%s: can't assign array value to non-array special", pname);
- return NULL;
- }
- tc = 1;
- usepm = 0;
- }
- else if (usepm || newspecial != NS_NONE) {
- int chflags = ((off & pm->node.flags) | (on & ~pm->node.flags)) &
- (PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED|
- PM_ARRAY|PM_TIED|PM_AUTOLOAD);
- /* keep the parameter if just switching between floating types */
- if ((tc = chflags && chflags != (PM_EFLOAT|PM_FFLOAT)))
- usepm = 0;
- }
-
- /*
- * Extra checks if converting the type of a parameter, or if
- * trying to remove readonlyness. It's dangerous doing either
- * with a special or a parameter which isn't loaded yet (which
- * may be special when it is loaded; we can't tell yet).
- */
- if ((readonly =
- ((usepm || newspecial != NS_NONE) &&
- (off & pm->node.flags & PM_READONLY))) ||
- tc) {
- if (pm->node.flags & PM_SPECIAL) {
- int err = 1;
- if (!readonly && !strcmp(pname, "SECONDS"))
- {
- /*
- * We allow SECONDS to change type between integer
- * and floating point. If we are creating a new
- * local copy we check the type here and allow
- * a new special to be created with that type.
- * We then need to make sure the correct type
- * for the special is restored at the end of the scope.
- * If we are changing the type of an existing
- * parameter, we do the whole thing here.
- */
- if (newspecial != NS_NONE)
- {
- /*
- * The first test allows `typeset' to copy the
- * existing type. This is the usual behaviour
- * for making special parameters local.
- */
- if (PM_TYPE(on) == 0 || PM_TYPE(on) == PM_INTEGER ||
- PM_TYPE(on) == PM_FFLOAT || PM_TYPE(on) == PM_EFLOAT)
- {
- newspecial = NS_SECONDS;
- err = 0; /* and continue */
- tc = 0; /* but don't do a normal conversion */
- }
- } else if (!setsecondstype(pm, on, off)) {
- if (asg->value.scalar &&
- !(pm = assignsparam(
- pname, ztrdup(asg->value.scalar), 0)))
- return NULL;
- usepm = 1;
- err = 0;
- }
- }
- if (err)
- {
- zerrnam(cname, "%s: can't change type of a special parameter",
- pname);
- return NULL;
- }
- } else if (pm->node.flags & PM_AUTOLOAD) {
- zerrnam(cname, "%s: can't change type of autoloaded parameter",
- pname);
- return NULL;
- }
- }
- else if (newspecial != NS_NONE && strcmp(pname, "SECONDS") == 0)
- newspecial = NS_SECONDS;
-
- if (isset(POSIXBUILTINS)) {
- /*
- * Stricter rules about retaining readonly attribute in this case.
- */
- 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) &&
- !(on & PM_READONLY)) {
- 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;
- */
- }
-
- /*
- * A parameter will be local if
- * 1. we are re-using an existing local parameter
- * or
- * 2. we are not using an existing parameter, but
- * i. there is already a parameter, which will be hidden
- * or
- * ii. we are creating a new local parameter
- */
- if (usepm) {
- if ((asg->flags & ASG_ARRAY) ?
- !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)) :
- (asg->value.scalar && (PM_TYPE(pm->node.flags &
- (PM_ARRAY|PM_HASHED))))) {
- zerrnam(cname, "%s: inconsistent type for assignment", pname);
- return NULL;
- }
- on &= ~PM_LOCAL;
- if (!on && !roff && !ASG_VALUEP(asg)) {
- if (OPT_ISSET(ops,'p'))
- paramtab->printnode(&pm->node, PRINT_TYPESET);
- else if (!OPT_ISSET(ops,'g') &&
- (unset(TYPESETSILENT) || OPT_ISSET(ops,'m')))
- paramtab->printnode(&pm->node, PRINT_INCLUDEVALUE);
- return pm;
- }
- if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
- zerrnam(cname, "%s: restricted", pname);
- return pm;
- }
- if ((on & PM_UNIQUE) && !(pm->node.flags & PM_READONLY & ~off)) {
- Param apm;
- char **x;
- if (PM_TYPE(pm->node.flags) == PM_ARRAY) {
- x = (*pm->gsu.a->getfn)(pm);
- uniqarray(x);
- if (pm->node.flags & PM_SPECIAL) {
- if (zheapptr(x))
- x = zarrdup(x);
- (*pm->gsu.a->setfn)(pm, x);
- } else if (pm->ename && x)
- arrfixenv(pm->ename, x);
- } else if (PM_TYPE(pm->node.flags) == PM_SCALAR && pm->ename &&
- (apm =
- (Param) paramtab->getnode(paramtab, pm->ename))) {
- x = (*apm->gsu.a->getfn)(apm);
- uniqarray(x);
- if (x)
- arrfixenv(pm->node.nam, x);
- }
- }
- if (usepm == 2) /* do not change the PM_UNSET flag */
- pm->node.flags = (pm->node.flags | (on & ~PM_READONLY)) & ~off;
- else {
- /*
- * Keep unset if using readonly in POSIX mode.
- */
- if (!(on & PM_READONLY) || !isset(POSIXBUILTINS))
- off |= PM_UNSET;
- pm->node.flags = (pm->node.flags |
- (on & ~PM_READONLY)) & ~off;
- }
- if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) {
- if (typeset_setwidth(cname, pm, ops, on, 0))
- return NULL;
- }
- if (on & (PM_INTEGER | PM_EFLOAT | PM_FFLOAT)) {
- if (typeset_setbase(cname, pm, ops, on, 0))
- return NULL;
- }
- if (!(pm->node.flags & (PM_ARRAY|PM_HASHED))) {
- if (pm->node.flags & PM_EXPORTED) {
- if (!(pm->node.flags & PM_UNSET) && !pm->env && !ASG_VALUEP(asg))
- addenv(pm, getsparam(pname));
- } else if (pm->env && !(pm->node.flags & PM_HASHELEM))
- delenv(pm);
- DPUTS(ASG_ARRAYP(asg), "BUG: typeset got array value where scalar expected");
- if (asg->value.scalar &&
- !(pm = assignsparam(pname, ztrdup(asg->value.scalar), 0)))
- return NULL;
- } else if (asg->flags & ASG_ARRAY) {
- int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0;
- if (!(pm = assignaparam(pname, asg->value.array ?
- zlinklist2array(asg->value.array) :
- mkarray(NULL), flags)))
- return NULL;
- }
- if (errflag)
- return NULL;
- pm->node.flags |= (on & PM_READONLY);
- if (OPT_ISSET(ops,'p'))
- paramtab->printnode(&pm->node, PRINT_TYPESET);
- return pm;
- }
-
- if ((asg->flags & ASG_ARRAY) ?
- !(on & (PM_ARRAY|PM_HASHED)) :
- (asg->value.scalar && (on & (PM_ARRAY|PM_HASHED)))) {
- zerrnam(cname, "%s: inconsistent type for assignment", pname);
- return NULL;
- }
-
- /*
- * We're here either because we're creating a new parameter,
- * or we're adding a parameter at a different local level,
- * or we're converting the type of a parameter. In the
- * last case only, we need to delete the old parameter.
- */
- if (tc) {
- /* Maintain existing readonly/exported status... */
- on |= ~off & (PM_READONLY|PM_EXPORTED) & pm->node.flags;
- /* ...but turn off existing readonly so we can delete it */
- pm->node.flags &= ~PM_READONLY;
- /*
- * If we're just changing the type, we should keep the
- * variable at the current level of localness.
- */
- keeplocal = pm->level;
- /*
- * Try to carry over a value, but not when changing from,
- * to, or between non-scalar types.
- *
- * (We can do better now, but it does have user-visible
- * implications.)
- */
- if (!ASG_VALUEP(asg) && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED))) {
- asg->value.scalar = dupstring(getsparam(pname));
- asg->flags = 0;
- }
- /* pname may point to pm->nam which is about to disappear */
- pname = dupstring(pname);
- unsetparam_pm(pm, 0, 1);
- }
-
- if (newspecial != NS_NONE) {
- Param tpm, pm2;
- if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
- 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
- * the get/set methods.
- */
- tpm = (Param) zshcalloc(sizeof *tpm);
-
- tpm->node.nam = pm->node.nam;
- if (pm->ename &&
- (pm2 = (Param) paramtab->getnode(paramtab, pm->ename)) &&
- pm2->level == locallevel) {
- /* This is getting silly, but anyway: if one of a path/PATH
- * pair has already been made local at the current level, we
- * have to make sure that the other one does not have its value
- * saved: since that comes from an internal variable it will
- * already reflect the local value, so restoring it on exit
- * would be wrong.
- *
- * This problem is also why we make sure we have a copy
- * of the environment entry in tpm->env, rather than relying
- * on the restored value to provide it.
- */
- tpm->node.flags = pm->node.flags | PM_NORESTORE;
- } else {
- copyparam(tpm, pm, 1);
- }
- tpm->old = pm->old;
- tpm->level = pm->level;
- tpm->base = pm->base;
- tpm->width = pm->width;
- if (pm->env)
- delenv(pm);
- tpm->env = NULL;
-
- pm->old = tpm;
- /*
- * The remaining on/off flags should be harmless to use,
- * because we've checked for unpleasant surprises above.
- */
- pm->node.flags = (PM_TYPE(pm->node.flags) | on | PM_SPECIAL) & ~off;
- /*
- * Readonlyness of special parameters must be preserved.
- */
- pm->node.flags |= tpm->node.flags & PM_READONLY;
- if (newspecial == NS_SECONDS) {
- /* We save off the raw internal value of the SECONDS var */
- tpm->u.dval = getrawseconds();
- setsecondstype(pm, on, off);
- }
-
- /*
- * Final tweak: if we've turned on one of the flags with
- * numbers, we should use the appropriate integer.
- */
- if (on & (PM_LEFT|PM_RIGHT_B|PM_RIGHT_Z)) {
- if (typeset_setwidth(cname, pm, ops, on, 1))
- return NULL;
- }
- if (on & (PM_INTEGER|PM_EFLOAT|PM_FFLOAT)) {
- if (typeset_setbase(cname, pm, ops, on, 1))
- return NULL;
- }
- } else if ((subscript = strchr(pname, '['))) {
- if (on & PM_READONLY) {
- zerrnam(cname,
- "%s: can't create readonly array elements", pname);
- return NULL;
- } else if ((on & PM_LOCAL) && locallevel) {
- *subscript = 0;
- pm = (Param) (paramtab == realparamtab ?
- /* getnode2() to avoid autoloading */
- paramtab->getnode2(paramtab, pname) :
- paramtab->getnode(paramtab, pname));
- *subscript = '[';
- if (!pm || pm->level != locallevel) {
- zerrnam(cname,
- "%s: can't create local array elements", pname);
- return NULL;
- }
- }
- if (PM_TYPE(on) == PM_SCALAR && !ASG_ARRAYP(asg)) {
- /*
- * This will either complain about bad identifiers, or will set
- * a hash element or array slice. This once worked by accident,
- * creating a stray parameter along the way via createparam(),
- * now called below in the isident() branch.
- */
- if (!(pm = assignsparam(
- pname,
- ztrdup(asg->value.scalar ? asg->value.scalar : ""), 0)))
- return NULL;
- dont_set = 1;
- asg->flags = 0;
- keeplocal = 0;
- on = pm->node.flags;
- } else if (PM_TYPE(on) == PM_ARRAY && ASG_ARRAYP(asg)) {
- int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0;
- if (!(pm = assignaparam(pname, asg->value.array ?
- zlinklist2array(asg->value.array) :
- mkarray(NULL), flags)))
- return NULL;
- dont_set = 1;
- keeplocal = 0;
- on = pm->node.flags;
- } else {
- zerrnam(cname,
- "%s: inconsistent array element or slice assignment", pname);
- return NULL;
- }
- }
- /*
- * As we can hide existing parameters, we allow a name if
- * it's not a normal identifier but is one of the special
- * set found in the parameter table. The second test is
- * because we can set individual positional parameters;
- * however "0" is not a positional parameter and is OK.
- *
- * It would be neater to extend isident() and be clearer
- * about where we allow various parameter types. It's
- * not entirely clear to me isident() should reject
- * specially named parameters given that it accepts digits.
- */
- else if ((isident(pname) || paramtab->getnode(paramtab, pname))
- && (!idigit(*pname) || !strcmp(pname, "0"))) {
- /*
- * Create a new node for a parameter with the flags in `on' minus the
- * readonly flag
- */
- pm = createparam(pname, on & ~PM_READONLY);
- if (!pm) {
- if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z |
- PM_INTEGER | PM_EFLOAT | PM_FFLOAT))
- zerrnam(cname, "can't change variable attribute: %s", pname);
- return NULL;
- }
- if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) {
- if (typeset_setwidth(cname, pm, ops, on, 0))
- return NULL;
- }
- if (on & (PM_INTEGER | PM_EFLOAT | PM_FFLOAT)) {
- if (typeset_setbase(cname, pm, ops, on, 0))
- return NULL;
- }
- } else {
- if (idigit(*pname))
- zerrnam(cname, "not an identifier: %s", pname);
- else
- zerrnam(cname, "not valid in this context: %s", pname);
- return NULL;
- }
-
- if (altpm && PM_TYPE(pm->node.flags) == PM_SCALAR) {
- /*
- * It seems safer to set this here than in createparam(),
- * to make sure we only ever use the colonarr functions
- * when u.data is correctly set.
- */
- struct tieddata *tdp = (struct tieddata *)
- zalloc(sizeof(struct tieddata));
- if (!tdp)
- return NULL;
- tdp->joinchar = joinchar;
- tdp->arrptr = &altpm->u.arr;
-
- pm->gsu.s = &tiedarr_gsu;
- pm->u.data = tdp;
- }
-
- if (keeplocal)
- pm->level = keeplocal;
- else if (on & PM_LOCAL)
- pm->level = locallevel;
- if (ASG_VALUEP(asg) && !dont_set) {
- Param ipm = pm;
- if (pm->node.flags & (PM_ARRAY|PM_HASHED)) {
- char **arrayval;
- int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0;
- if (!ASG_ARRAYP(asg)) {
- /*
- * Attempt to assign a scalar value to an array.
- * This can happen if the array is special.
- * We'll be lenient and guess what the user meant.
- * This is how normal assigment works.
- */
- if (*asg->value.scalar) {
- /* Array with one value */
- arrayval = mkarray(ztrdup(asg->value.scalar));
- } else {
- /* Empty array */
- arrayval = mkarray(NULL);
- }
- } else if (asg->value.array)
- arrayval = zlinklist2array(asg->value.array);
- else
- arrayval = mkarray(NULL);
- if (!(pm=assignaparam(pname, arrayval, flags)))
- return NULL;
- } else {
- DPUTS(ASG_ARRAYP(asg), "BUG: inconsistent array value for scalar");
- if (!(pm = assignsparam(pname, ztrdup(asg->value.scalar), 0)))
- return NULL;
- }
- if (pm != ipm) {
- DPUTS(ipm->node.flags != pm->node.flags,
- "BUG: parameter recreated with wrong flags");
- unsetparam_pm(ipm, 0, 1);
- }
- } else if (newspecial != NS_NONE &&
- !(pm->old->node.flags & (PM_NORESTORE|PM_READONLY))) {
- /*
- * We need to use the special setting function to re-initialise
- * the special parameter to empty.
- */
- switch (PM_TYPE(pm->node.flags)) {
- case PM_SCALAR:
- pm->gsu.s->setfn(pm, ztrdup(""));
- break;
- case PM_INTEGER:
- /*
- * Restricted integers are dangerous to initialize to 0,
- * so don't do that.
- */
- if (!(pm->old->node.flags & PM_RESTRICTED))
- pm->gsu.i->setfn(pm, 0);
- break;
- case PM_EFLOAT:
- case PM_FFLOAT:
- pm->gsu.f->setfn(pm, 0.0);
- break;
- case PM_ARRAY:
- pm->gsu.a->setfn(pm, mkarray(NULL));
- break;
- case PM_HASHED:
- pm->gsu.h->setfn(pm, newparamtable(17, pm->node.nam));
- break;
- }
- }
- pm->node.flags |= (on & PM_READONLY);
-
- if (OPT_ISSET(ops,'p'))
- paramtab->printnode(&pm->node, PRINT_TYPESET);
-
- return pm;
-}
-
-/*
- * declare, export, float, integer, local, readonly, typeset
- *
- * Note the difference in interface from most builtins, covered by the
- * BINF_ASSIGN builtin flag. This is only made use of by builtins
- * called by reserved word, which only covers declare, local, readonly
- * and typeset. Otherwise assigns is NULL.
- */
-
-/**/
-int
-bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
-{
- Param pm;
- Asgment asg;
- Patprog pprog;
- char *optstr = TYPESET_OPTSTR;
- int on = 0, off = 0, roff, bit = PM_ARRAY;
- int i;
- int returnval = 0, printflags = 0;
- int hasargs;
-
- /* hash -f is really the builtin `functions' */
- if (OPT_ISSET(ops,'f'))
- return bin_functions(name, argv, ops, func);
-
- /* POSIX handles "readonly" specially */
- if (func == BIN_READONLY && isset(POSIXBUILTINS) && !OPT_PLUS(ops, 'g'))
- ops->ind['g'] = 1;
-
- /* Translate the options into PM_* flags. *
- * Unfortunately, this depends on the order *
- * these flags are defined in zsh.h */
- for (; *optstr; optstr++, bit <<= 1)
- {
- int optval = STOUC(*optstr);
- if (OPT_MINUS(ops,optval))
- on |= bit;
- else if (OPT_PLUS(ops,optval))
- off |= bit;
- }
- roff = off;
-
- /* Sanity checks on the options. Remove conflicting options. */
- if (on & PM_FFLOAT) {
- off |= PM_UPPER | PM_ARRAY | PM_HASHED | PM_INTEGER | PM_EFLOAT;
- /* Allow `float -F' to work even though float sets -E by default */
- on &= ~PM_EFLOAT;
- }
- if (on & PM_EFLOAT)
- off |= PM_UPPER | PM_ARRAY | PM_HASHED | PM_INTEGER | PM_FFLOAT;
- if (on & PM_INTEGER)
- off |= PM_UPPER | PM_ARRAY | PM_HASHED | PM_EFLOAT | PM_FFLOAT;
- /*
- * Allowing -Z with -L is a feature: left justify, suppressing
- * leading zeroes.
- */
- if (on & (PM_LEFT|PM_RIGHT_Z))
- off |= PM_RIGHT_B;
- if (on & PM_RIGHT_B)
- off |= PM_LEFT | PM_RIGHT_Z;
- if (on & PM_UPPER)
- off |= PM_LOWER;
- if (on & PM_LOWER)
- off |= PM_UPPER;
- if (on & PM_HASHED)
- off |= PM_ARRAY;
- if (on & PM_TIED)
- off |= PM_INTEGER | PM_EFLOAT | PM_FFLOAT | PM_ARRAY | PM_HASHED;
-
- on &= ~off;
-
- queue_signals();
-
- /* Given no arguments, list whatever the options specify. */
- if (OPT_ISSET(ops,'p')) {
- printflags |= PRINT_TYPESET;
- if (OPT_HASARG(ops,'p')) {
- char *eptr;
- int pflag = (int)zstrtol(OPT_ARG(ops,'p'), &eptr, 10);
- if (pflag == 1 && !*eptr)
- printflags |= PRINT_LINE;
- else if (pflag || *eptr) {
- zwarnnam(name, "bad argument to -p: %s", OPT_ARG(ops,'p'));
- unqueue_signals();
- return 1;
- }
- /* -p0 treated as -p for consistency */
- }
- }
- hasargs = *argv != NULL || (assigns && firstnode(assigns));
- if (!hasargs) {
- if (!OPT_ISSET(ops,'p')) {
- if (!(on|roff))
- printflags |= PRINT_TYPE;
- if (roff || OPT_ISSET(ops,'+'))
- printflags |= PRINT_NAMEONLY;
- }
- scanhashtable(paramtab, 1, on|roff, 0, paramtab->printnode, printflags);
- unqueue_signals();
- return 0;
- }
-
- if (!(OPT_ISSET(ops,'g') || OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m')) ||
- OPT_PLUS(ops,'g') || *name == 'l' ||
- (!isset(GLOBALEXPORT) && !OPT_ISSET(ops,'g')))
- on |= PM_LOCAL;
-
- if (on & PM_TIED) {
- Param apm;
- struct asgment asg0, asg2;
- char *oldval = NULL, *joinstr;
- int joinchar, nargs;
-
- if (OPT_ISSET(ops,'m')) {
- zwarnnam(name, "incompatible options for -T");
- unqueue_signals();
- return 1;
- }
- on &= ~off;
- nargs = arrlen(argv) + (assigns ? countlinknodes(assigns) : 0);
- if (nargs < 2) {
- zwarnnam(name, "-T requires names of scalar and array");
- unqueue_signals();
- return 1;
- }
- if (nargs > 3) {
- zwarnnam(name, "too many arguments for -T");
- unqueue_signals();
- return 1;
- }
-
- if (!(asg = getasg(&argv, assigns))) {
- unqueue_signals();
- return 1;
- }
- asg0 = *asg;
- if (ASG_ARRAYP(&asg0)) {
- unqueue_signals();
- zwarnnam(name, "first argument of tie must be scalar: %s",
- asg0.name);
- return 1;
- }
-
- if (!(asg = getasg(&argv, assigns))) {
- unqueue_signals();
- return 1;
- }
- if (!ASG_ARRAYP(asg) && asg->value.scalar) {
- unqueue_signals();
- zwarnnam(name, "second argument of tie must be array: %s",
- asg->name);
- return 1;
- }
-
- if (!strcmp(asg0.name, asg->name)) {
- unqueue_signals();
- zerrnam(name, "can't tie a variable to itself: %s", asg0.name);
- return 1;
- }
- if (strchr(asg0.name, '[') || strchr(asg->name, '[')) {
- unqueue_signals();
- zerrnam(name, "can't tie array elements: %s", asg0.name);
- return 1;
- }
- if (ASG_VALUEP(asg) && ASG_VALUEP(&asg0)) {
- unqueue_signals();
- zerrnam(name, "only one tied parameter can have value: %s", asg0.name);
- return 1;
- }
-
- /*
- * Third argument, if given, is character used to join
- * the elements of the array in the scalar.
- */
- if (*argv)
- joinstr = *argv;
- else if (assigns && firstnode(assigns)) {
- Asgment nextasg = (Asgment)firstnode(assigns);
- if (ASG_ARRAYP(nextasg) || ASG_VALUEP(nextasg)) {
- zwarnnam(name, "third argument of tie must be join character");
- unqueue_signals();
- return 1;
- }
- joinstr = nextasg->name;
- } else
- joinstr = NULL;
- if (!joinstr)
- joinchar = ':';
- else if (!*joinstr)
- joinchar = 0;
- else if (*joinstr == Meta)
- joinchar = joinstr[1] ^ 32;
- else
- joinchar = *joinstr;
- /*
- * Keep the old value of the scalar. We need to do this
- * here as if it is already tied to the same array it
- * will be unset when we retie the array. This is all
- * so that typeset -T is idempotent.
- *
- * We also need to remember here whether the damn thing is
- * exported and pass that along. Isn't the world complicated?
- */
- if ((pm = (Param) paramtab->getnode(paramtab, asg0.name))
- && !(pm->node.flags & PM_UNSET)
- && (locallevel == pm->level || !(on & PM_LOCAL))) {
- if (pm->node.flags & PM_TIED) {
- unqueue_signals();
- if (PM_TYPE(pm->node.flags) != PM_SCALAR) {
- zwarnnam(name, "already tied as non-scalar: %s", asg0.name);
- } else if (!strcmp(asg->name, pm->ename)) {
- /*
- * Already tied in the fashion requested.
- */
- struct tieddata *tdp = (struct tieddata*)pm->u.data;
- int flags = (asg->flags & ASG_KEY_VALUE) ?
- ASSPM_KEY_VALUE : 0;
- /* Update join character */
- tdp->joinchar = joinchar;
- if (asg0.value.scalar)
- assignsparam(asg0.name, ztrdup(asg0.value.scalar), 0);
- else if (asg->value.array)
- assignaparam(
- asg->name, zlinklist2array(asg->value.array),flags);
- return 0;
- } else {
- zwarnnam(name, "can't tie already tied scalar: %s",
- asg0.name);
- }
- return 1;
- }
- if (!asg0.value.scalar && !asg->value.array &&
- !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)))
- oldval = ztrdup(getsparam(asg0.name));
- on |= (pm->node.flags & PM_EXPORTED);
- }
- /*
- * Create the tied array; this is normal except that
- * it has the PM_TIED flag set. Do it first because
- * we need the address.
- *
- * Don't attempt to set it yet, it's too early
- * to be exported properly.
- */
- asg2.name = asg->name;
- asg2.flags = 0;
- asg2.value.array = (LinkList)0;
- if (!(apm=typeset_single(name, asg->name,
- (Param)paramtab->getnode(paramtab,
- asg->name),
- func, (on | PM_ARRAY) & ~PM_EXPORTED,
- off, roff, &asg2, NULL, ops, 0))) {
- if (oldval)
- zsfree(oldval);
- unqueue_signals();
- return 1;
- }
- /*
- * Create the tied colonarray. We make it as a normal scalar
- * and fix up the oddities later.
- */
- if (!(pm=typeset_single(name, asg0.name,
- (Param)paramtab->getnode(paramtab,
- asg0.name),
- func, on, off, roff, &asg0, apm,
- ops, joinchar))) {
- if (oldval)
- zsfree(oldval);
- unsetparam_pm(apm, 1, 1);
- unqueue_signals();
- return 1;
- }
-
- /*
- * pm->ename is only deleted when the struct is, so
- * we need to free it here if it already exists.
- */
- if (pm->ename)
- zsfree(pm->ename);
- pm->ename = ztrdup(asg->name);
- if (apm->ename)
- zsfree(apm->ename);
- apm->ename = ztrdup(asg0.name);
- if (asg->value.array) {
- int flags = (asg->flags & ASG_KEY_VALUE) ? ASSPM_KEY_VALUE : 0;
- assignaparam(asg->name, zlinklist2array(asg->value.array), flags);
- } else if (oldval)
- assignsparam(asg0.name, oldval, 0);
- unqueue_signals();
-
- return 0;
- }
- if (off & PM_TIED) {
- unqueue_signals();
- zerrnam(name, "use unset to remove tied variables");
- return 1;
- }
-
- /* With the -m option, treat arguments as glob patterns */
- if (OPT_ISSET(ops,'m')) {
- if (!OPT_ISSET(ops,'p')) {
- if (!(on|roff))
- printflags |= PRINT_TYPE;
- if (!on)
- printflags |= PRINT_NAMEONLY;
- }
-
- while ((asg = getasg(&argv, assigns))) {
- LinkList pmlist = newlinklist();
- LinkNode pmnode;
-
- tokenize(asg->name); /* expand argument */
- if (!(pprog = patcompile(asg->name, 0, NULL))) {
- untokenize(asg->name);
- zwarnnam(name, "bad pattern : %s", asg->name);
- returnval = 1;
- continue;
- }
- if (OPT_PLUS(ops,'m') && !ASG_VALUEP(asg)) {
- scanmatchtable(paramtab, pprog, 1, on|roff, 0,
- paramtab->printnode, printflags);
- continue;
- }
- /*
- * Search through the parameter table and change all parameters
- * matching the glob pattern to have these flags and/or value.
- * Bad news: if the parameter gets altered, e.g. by
- * a type conversion, then paramtab can be shifted around,
- * so we need to store the parameters to alter on a separate
- * list for later use.
- */
- for (i = 0; i < paramtab->hsize; i++) {
- for (pm = (Param) paramtab->nodes[i]; pm;
- pm = (Param) pm->node.next) {
- if (((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) ||
- (pm->node.flags & PM_UNSET))
- continue;
- if (pattry(pprog, pm->node.nam))
- addlinknode(pmlist, pm);
- }
- }
- for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) {
- pm = (Param) getdata(pmnode);
- if (!typeset_single(name, pm->node.nam, pm, func, on, off, roff,
- asg, NULL, ops, 0))
- returnval = 1;
- }
- }
- unqueue_signals();
- return returnval;
- }
-
- /* Take arguments literally. Don't glob */
- while ((asg = getasg(&argv, assigns))) {
- HashNode hn = (paramtab == realparamtab ?
- /* getnode2() to avoid autoloading */
- paramtab->getnode2(paramtab, asg->name) :
- paramtab->getnode(paramtab, asg->name));
- if (OPT_ISSET(ops,'p')) {
- if (hn)
- paramtab->printnode(hn, printflags);
- else {
- zwarnnam(name, "no such variable: %s", asg->name);
- returnval = 1;
- }
- continue;
- }
- if (!typeset_single(name, asg->name, (Param)hn,
- func, on, off, roff, asg, NULL,
- ops, 0))
- returnval = 1;
- }
- unqueue_signals();
- return returnval;
-}
-
-/* Helper for bin_functions() when run as "autoload -X" */
-
-/**/
-int
-eval_autoload(Shfunc shf, char *name, Options ops, int func)
-{
- if (!(shf->node.flags & PM_UNDEFINED))
- return 1;
-
- if (shf->funcdef) {
- freeeprog(shf->funcdef);
- shf->funcdef = &dummy_eprog;
- }
- if (OPT_MINUS(ops,'X')) {
- char *fargv[3];
- fargv[0] = name;
- fargv[1] = "\"$@\"";
- fargv[2] = 0;
- shf->funcdef = mkautofn(shf);
- return bin_eval(name, fargv, ops, func);
- }
-
- return !loadautofn(shf, (OPT_ISSET(ops,'k') ? 2 :
- (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
-listusermathfunc(MathFunc p)
-{
- int showargs;
-
- if (p->module)
- showargs = 3;
- else if (p->maxargs != (p->minargs ? p->minargs : -1))
- showargs = 2;
- else if (p->minargs)
- showargs = 1;
- else
- showargs = 0;
-
- printf("functions -M%s %s", (p->flags & MFF_STR) ? "s" : "", p->name);
- if (showargs) {
- printf(" %d", p->minargs);
- showargs--;
- }
- if (showargs) {
- printf(" %d", p->maxargs);
- showargs--;
- }
- if (showargs) {
- /*
- * function names are not required to consist of ident characters
- */
- putchar(' ');
- quotedzputs(p->module, stdout);
- showargs--;
- }
- putchar('\n');
-}
-
-
-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. */
-
-/**/
-int
-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, expand = 0;
-
- /* Do we have any flags defined? */
- if (OPT_PLUS(ops,'u'))
- off |= PM_UNDEFINED;
- else if (OPT_MINUS(ops,'u') || OPT_ISSET(ops,'X'))
- on |= PM_UNDEFINED;
- if (OPT_MINUS(ops,'U'))
- on |= PM_UNALIASED|PM_UNDEFINED;
- else if (OPT_PLUS(ops,'U'))
- off |= PM_UNALIASED;
- if (OPT_MINUS(ops,'t'))
- on |= PM_TAGGED;
- else if (OPT_PLUS(ops,'t'))
- off |= PM_TAGGED;
- if (OPT_MINUS(ops,'T'))
- 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;
- off |= PM_KSHSTORED;
- } else if (OPT_PLUS(ops,'z')) {
- off |= PM_ZSHSTORED;
- roff |= PM_ZSHSTORED;
- }
- if (OPT_MINUS(ops,'k')) {
- on |= PM_KSHSTORED;
- off |= PM_ZSHSTORED;
- } else if (OPT_PLUS(ops,'k')) {
- 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') || !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;
-
- if (OPT_MINUS(ops,'M') || OPT_PLUS(ops,'M')) {
- MathFunc p, q, prev;
- /*
- * Add/remove/list function as mathematical.
- */
- if (on || off || pflags || OPT_ISSET(ops,'X') || OPT_ISSET(ops,'u')
- || OPT_ISSET(ops,'U') || OPT_ISSET(ops,'w')) {
- zwarnnam(name, "invalid option(s)");
- return 1;
- }
- if (!*argv) {
- /* List functions. */
- queue_signals();
- for (p = mathfuncs; p; p = p->next)
- if (p->flags & MFF_USERFUNC)
- listusermathfunc(p);
- unqueue_signals();
- } else if (OPT_ISSET(ops,'m')) {
- /* List matching functions. */
- for (; *argv; argv++) {
- queue_signals();
- tokenize(*argv);
- if ((pprog = patcompile(*argv, PAT_STATIC, 0))) {
- for (p = mathfuncs, q = NULL; p; q = p) {
- MathFunc next;
- do {
- next = NULL;
- if ((p->flags & MFF_USERFUNC) &&
- pattry(pprog, p->name)) {
- if (OPT_PLUS(ops,'M')) {
- next = p->next;
- removemathfunc(q, p);
- p = next;
- } else
- listusermathfunc(p);
- }
- /* if we deleted one, retry with the new p */
- } while (next);
- if (p)
- p = p->next;
- }
- } 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. */
- for (; *argv; argv++) {
- queue_signals();
- for (p = mathfuncs, q = NULL; p; q = p, p = p->next) {
- if (!strcmp(p->name, *argv)) {
- if (!(p->flags & MFF_USERFUNC)) {
- zwarnnam(name, "+M %s: is a library function",
- *argv);
- returnval = 1;
- break;
- }
- removemathfunc(q, p);
- break;
- }
- }
- unqueue_signals();
- }
- } else {
- /* Add a function */
- 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);
- return 1;
- }
-
- if (*argv) {
- minargs = (int)zstrtol(*argv, &ptr, 0);
- if (minargs < 0 || *ptr) {
- zwarnnam(name, "-M: invalid min number of arguments: %s",
- *argv);
- return 1;
- }
- if (OPT_ISSET(ops,'s') && minargs != 1) {
- zwarnnam(name, "-Ms: must take a single string argument");
- return 1;
- }
- maxargs = minargs;
- argv++;
- }
- if (*argv) {
- maxargs = (int)zstrtol(*argv, &ptr, 0);
- if (maxargs < -1 ||
- (maxargs != -1 && maxargs < minargs) ||
- *ptr) {
- zwarnnam(name,
- "-M: invalid max number of arguments: %s",
- *argv);
- return 1;
- }
- if (OPT_ISSET(ops,'s') && maxargs != 1) {
- zwarnnam(name, "-Ms: must take a single string argument");
- return 1;
- }
- argv++;
- }
- if (*argv)
- modname = *argv++;
- if (*argv) {
- zwarnnam(name, "-M: too many arguments");
- return 1;
- }
-
- 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;
-
- queue_signals();
- for (q = mathfuncs, prev = NULL; q; prev = q, q = q->next) {
- if (!strcmp(q->name, funcname)) {
- removemathfunc(prev, q);
- break;
- }
- }
-
- p->next = mathfuncs;
- mathfuncs = p;
- unqueue_signals();
- }
-
- return returnval;
- }
-
- 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();
- 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;
- } 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);
- }
- 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;
- }
-
- /* With the -m option, treat arguments as glob patterns */
- 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 */
- if (!(on|off) && !OPT_ISSET(ops,'X')) {
- 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++) {
- for (shf = (Shfunc) shfunctab->nodes[i]; shf;
- shf = (Shfunc) shf->node.next)
- if (pattry(pprog, shf->node.nam) &&
- !(shf->node.flags & DISABLED)) {
- shf->node.flags = (shf->node.flags |
- (on & ~PM_UNDEFINED)) & ~off;
- if (check_autoload(shf, shf->node.nam,
- ops, func)) {
- returnval = 1;
- }
- }
- }
- }
- } else {
- untokenize(*argv);
- zwarnnam(name, "bad pattern : %s", *argv);
- returnval = 1;
- }
- unqueue_signals();
- }
- return returnval;
- }
-
- /* Take the arguments literally -- do not glob */
- queue_signals();
- for (; *argv; argv++) {
- if (OPT_ISSET(ops,'w'))
- returnval = dump_autoload(name, *argv, on, ops, func);
- else if ((shf = (Shfunc) shfunctab->getnode(shfunctab, *argv))) {
- /* if any flag was given */
- if (on|off) {
- /* turn on/off the given flags */
- shf->node.flags = (shf->node.flags | (on & ~PM_UNDEFINED)) & ~off;
- if (check_autoload(shf, shf->node.nam, ops, func))
- returnval = 1;
- } else
- /* no flags, so just print */
- printshfuncexpand(&shf->node, pflags, expand);
- } else if (on & PM_UNDEFINED) {
- int signum = -1, ok = 1;
-
- if (!strncmp(*argv, "TRAP", 4) &&
- (signum = getsignum(*argv + 4)) != -1) {
- /*
- * Because of the possibility of alternative names,
- * we must remove the trap explicitly.
- */
- 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);
- add_autoload_function(shf, *argv);
-
- if (signum != -1) {
- if (settrap(signum, NULL, ZSIG_FUNC)) {
- shfunctab->removenode(shfunctab, *argv);
- shfunctab->freenode(&shf->node);
- returnval = 1;
- ok = 0;
- }
- }
-
- if (ok && check_autoload(shf, shf->node.nam, ops, func))
- returnval = 1;
- } else
- returnval = 1;
- }
- unqueue_signals();
- return returnval;
-}
-
-/**/
-Eprog
-mkautofn(Shfunc shf)
-{
- Eprog p;
-
- p = (Eprog) zalloc(sizeof(*p));
- p->len = 5 * sizeof(wordcode);
- p->prog = (Wordcode) zalloc(p->len);
- p->strs = NULL;
- p->shf = shf;
- p->npats = 0;
- p->nref = 1; /* allocated from permanent storage */
- p->pats = (Patprog *) p->prog;
- p->flags = EF_REAL;
- p->dump = NULL;
-
- p->prog[0] = WCB_LIST((Z_SYNC | Z_END), 0);
- p->prog[1] = WCB_SUBLIST(WC_SUBLIST_END, 0, 3);
- p->prog[2] = WCB_PIPE(WC_PIPE_END, 0);
- p->prog[3] = WCB_AUTOFN();
- p->prog[4] = WCB_END();
-
- return p;
-}
-
-/* unset: unset parameters */
-
-/**/
-int
-bin_unset(char *name, char **argv, Options ops, int func)
-{
- Param pm, next;
- Patprog pprog;
- char *s;
- int match = 0, returnval = 0;
- int i;
-
- /* unset -f is the same as unfunction */
- if (OPT_ISSET(ops,'f'))
- return bin_unhash(name, argv, ops, 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 */
- 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 */
- next = (Param) pm->node.next;
- if ((!(pm->node.flags & PM_RESTRICTED) ||
- unset(RESTRICTED)) &&
- pattry(pprog, pm->node.nam)) {
- unsetparam_pm(pm, 0, 1);
- match++;
- }
- }
- }
- } else {
- untokenize(s);
- zwarnnam(name, "bad pattern : %s", s);
- returnval = 1;
- }
- unqueue_signals();
- }
- /* If we didn't match anything, we return 1. */
- if (!match)
- returnval = 1;
- return returnval;
- }
-
- /* do not glob -- unset the given parameter */
- queue_signals();
- while ((s = *argv++)) {
- char *ss = strchr(s, '['), *subscript = 0;
- if (ss) {
- char *sse;
- *ss = 0;
- if ((sse = parse_subscript(ss+1, 1, ']'))) {
- *sse = 0;
- subscript = dupstring(ss+1);
- *sse = ']';
- remnulargs(subscript);
- untokenize(subscript);
- }
- }
- if ((ss && !subscript) || !isident(s)) {
- if (ss)
- *ss = '[';
- zerrnam(name, "%s: invalid parameter name", s);
- returnval = 1;
- continue;
- }
- pm = (Param) (paramtab == realparamtab ?
- /* getnode2() to avoid autoloading */
- paramtab->getnode2(paramtab, s) :
- paramtab->getnode(paramtab, s));
- /*
- * Unsetting an unset variable is not an error.
- * This appears to be reasonably standard behaviour.
- */
- if (!pm)
- continue;
- else if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
- zerrnam(name, "%s: restricted", pm->node.nam);
- returnval = 1;
- } else if (ss) {
- if (PM_TYPE(pm->node.flags) == PM_HASHED) {
- HashTable tht = paramtab;
- if ((paramtab = pm->gsu.h->getfn(pm)))
- unsetparam(subscript);
- paramtab = tht;
- } else if (PM_TYPE(pm->node.flags) == PM_SCALAR ||
- PM_TYPE(pm->node.flags) == PM_ARRAY) {
- struct value vbuf;
- vbuf.isarr = (PM_TYPE(pm->node.flags) == PM_ARRAY ?
- SCANPM_ARRONLY : 0);
- vbuf.pm = pm;
- vbuf.flags = 0;
- vbuf.start = 0;
- vbuf.end = -1;
- vbuf.arr = 0;
- *ss = '[';
- if (getindex(&ss, &vbuf, SCANPM_ASSIGNING) == 0 &&
- vbuf.pm && !(vbuf.pm->node.flags & PM_UNSET)) {
- if (PM_TYPE(pm->node.flags) == PM_SCALAR) {
- setstrvalue(&vbuf, ztrdup(""));
- } else {
- /* start is after the element for reverse index */
- int start = vbuf.start - !!(vbuf.flags & VALFLAG_INV);
- if (arrlen_gt(vbuf.pm->u.arr, start)) {
- char *arr[2];
- arr[0] = "";
- arr[1] = 0;
- setarrvalue(&vbuf, zarrdup(arr));
- }
- }
- }
- returnval = errflag;
- errflag &= ~ERRFLAG_ERROR;
- } else {
- zerrnam(name, "%s: invalid element for unset", s);
- returnval = 1;
- }
- } else {
- if (unsetparam_pm(pm, 0, 1))
- returnval = 1;
- }
- if (ss)
- *ss = '[';
- }
- unqueue_signals();
- return returnval;
-}
-
-/* type, whence, which, command */
-
-static LinkList matchednodes;
-
-static void
-fetchcmdnamnode(HashNode hn, UNUSED(int printflags))
-{
- Cmdnam cn = (Cmdnam) hn;
- addlinknode(matchednodes, cn->node.nam);
-}
-
-/**/
-int
-bin_whence(char *nam, char **argv, Options ops, int func)
-{
- HashNode hn;
- Patprog pprog;
- int returnval = 0;
- int printflags = 0;
- int aliasflags;
- int csh, all, v, wd;
- int informed = 0;
- int expand = 0;
- char *cnam, **allmatched = 0;
-
- /* Check some option information */
- csh = OPT_ISSET(ops,'c');
- v = OPT_ISSET(ops,'v');
- 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'))
- printflags |= PRINT_WHENCE_CSH;
- else if (OPT_ISSET(ops,'v'))
- printflags |= PRINT_WHENCE_VERBOSE;
- else
- printflags |= PRINT_WHENCE_SIMPLE;
- if (OPT_ISSET(ops,'f'))
- printflags |= PRINT_WHENCE_FUNCDEF;
-
- if (func == BIN_COMMAND)
- if (OPT_ISSET(ops,'V')) {
- printflags = aliasflags = PRINT_WHENCE_VERBOSE;
- v = 1;
- } else {
- aliasflags = PRINT_LIST;
- printflags = PRINT_WHENCE_SIMPLE;
- v = 0;
- }
- else
- aliasflags = printflags;
-
- /* With -m option -- treat arguments as a glob patterns */
- if (OPT_ISSET(ops,'m')) {
- cmdnamtab->filltable(cmdnamtab);
- if (all) {
- pushheap();
- matchednodes = newlinklist();
- }
- queue_signals();
- for (; *argv; argv++) {
- /* parse the pattern */
- tokenize(*argv);
- if (!(pprog = patcompile(*argv, PAT_STATIC, NULL))) {
- untokenize(*argv);
- zwarnnam(nam, "bad pattern : %s", *argv);
- returnval = 1;
- continue;
- }
- if (!OPT_ISSET(ops,'p')) {
- /* -p option is for path search only. *
- * We're not using it, so search for ... */
-
- /* aliases ... */
- informed +=
- scanmatchtable(aliastab, pprog, 1, 0, DISABLED,
- aliastab->printnode, printflags);
-
- /* and reserved words ... */
- informed +=
- scanmatchtable(reswdtab, pprog, 1, 0, DISABLED,
- reswdtab->printnode, printflags);
-
- /* and shell functions... */
- informed +=
- scanmatchshfunc(pprog, 1, 0, DISABLED,
- shfunctab->printnode, printflags, expand);
-
- /* and builtins. */
- informed +=
- scanmatchtable(builtintab, pprog, 1, 0, DISABLED,
- builtintab->printnode, printflags);
- }
- /* Done search for `internal' commands, if the -p option *
- * was not used. Now search the path. */
- informed +=
- scanmatchtable(cmdnamtab, pprog, 1, 0, 0,
- (all ? fetchcmdnamnode : cmdnamtab->printnode),
- printflags);
- run_queued_signals();
- }
- unqueue_signals();
- if (all) {
- allmatched = argv = zlinklist2array(matchednodes);
- matchednodes = NULL;
- popheap();
- } else
- return returnval || !informed;
- }
-
- /* Take arguments literally -- do not glob */
- queue_signals();
- for (; *argv; argv++) {
- if (!OPT_ISSET(ops,'p') && !allmatched) {
- char *suf;
-
- /* Look for alias */
- if ((hn = aliastab->getnode(aliastab, *argv))) {
- aliastab->printnode(hn, aliasflags);
- informed = 1;
- if (!all)
- continue;
- }
- /* Look for suffix alias */
- if ((suf = strrchr(*argv, '.')) && suf[1] &&
- suf > *argv && suf[-1] != Meta &&
- (hn = sufaliastab->getnode(sufaliastab, suf+1))) {
- sufaliastab->printnode(hn, printflags);
- informed = 1;
- if (!all)
- continue;
- }
- /* Look for reserved word */
- if ((hn = reswdtab->getnode(reswdtab, *argv))) {
- reswdtab->printnode(hn, printflags);
- informed = 1;
- if (!all)
- continue;
- }
- /* Look for shell function */
- if ((hn = shfunctab->getnode(shfunctab, *argv))) {
- printshfuncexpand(hn, printflags, expand);
- informed = 1;
- if (!all)
- continue;
- }
- /* Look for builtin command */
- if ((hn = builtintab->getnode(builtintab, *argv))) {
- builtintab->printnode(hn, printflags);
- informed = 1;
- if (!all)
- continue;
- }
- /* Look for commands that have been added to the *
- * cmdnamtab with the builtin `hash foo=bar'. */
- if ((hn = cmdnamtab->getnode(cmdnamtab, *argv)) && (hn->flags & HASHED)) {
- cmdnamtab->printnode(hn, printflags);
- informed = 1;
- if (!all)
- continue;
- }
- }
-
- /* Option -a is to search the entire path, *
- * rather than just looking for one match. */
- if (all && **argv != '/') {
- char **pp, *buf;
-
- pushheap();
- for (pp = path; *pp; pp++) {
- if (**pp) {
- buf = zhtricat(*pp, "/", *argv);
- } else buf = dupstring(*argv);
-
- if (iscom(buf)) {
- if (wd) {
- printf("%s: command\n", *argv);
- } else {
- if (v && !csh) {
- zputs(*argv, stdout), fputs(" is ", 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);
- }
- informed = 1;
- }
- }
- 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 (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) {
- zputs(*argv, stdout), fputs(" is ", 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. 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;
- }
- }
- if (allmatched)
- freearray(allmatched);
-
- unqueue_signals();
- return returnval || !informed;
-}
-
-/**** command & named directory hash table builtins ****/
-
-/*****************************************************************
- * hash -- explicitly hash a command. *
- * 1) Given no arguments, list the hash table. *
- * 2) The -m option prints out commands in the hash table that *
- * match a given glob pattern. *
- * 3) The -f option causes the entire path to be added to the *
- * hash table (cannot be combined with any arguments). *
- * 4) The -r option causes the entire hash table to be discarded *
- * (cannot be combined with any arguments). *
- * 5) Given argument of the form foo=bar, add element to command *
- * hash table, so that when `foo' is entered, then `bar' is *
- * executed. *
- * 6) Given arguments not of the previous form, add it to the *
- * command hash table as if it were being executed. *
- * 7) The -d option causes analogous things to be done using *
- * the named directory hash table. *
- *****************************************************************/
-
-/**/
-int
-bin_hash(char *name, char **argv, Options ops, UNUSED(int func))
-{
- HashTable ht;
- Patprog pprog;
- Asgment asg;
- int returnval = 0;
- int printflags = 0;
-
- if (OPT_ISSET(ops,'d'))
- ht = nameddirtab;
- else
- ht = cmdnamtab;
-
- if (OPT_ISSET(ops,'r') || OPT_ISSET(ops,'f')) {
- /* -f and -r can't be used with any arguments */
- if (*argv) {
- zwarnnam("hash", "too many arguments");
- return 1;
- }
-
- /* empty the hash table */
- if (OPT_ISSET(ops,'r'))
- ht->emptytable(ht);
-
- /* fill the hash table in a standard way */
- if (OPT_ISSET(ops,'f'))
- ht->filltable(ht);
-
- return 0;
- }
-
- if (OPT_ISSET(ops,'L')) printflags |= PRINT_LIST;
-
- /* Given no arguments, display current hash table. */
- if (!*argv) {
- queue_signals();
- scanhashtable(ht, 1, 0, 0, ht->printnode, printflags);
- unqueue_signals();
- return 0;
- }
-
- queue_signals();
- while (*argv) {
- void *hn;
- if (OPT_ISSET(ops,'m')) {
- /* with the -m option, treat the argument as a glob pattern */
- tokenize(*argv); /* expand */
- if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) {
- /* display matching hash table elements */
- scanmatchtable(ht, pprog, 1, 0, 0, ht->printnode, printflags);
- } else {
- untokenize(*argv);
- zwarnnam(name, "bad pattern : %s", *argv);
- returnval = 1;
- }
- argv++;
- continue;
- }
- 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);
- returnval = 1;
- } else {
- /* The argument is of the form foo=bar, *
- * so define an entry for the table. */
- if(OPT_ISSET(ops,'d')) {
- /* shouldn't return NULL if asg->name is not NULL */
- if (*itype_end(asg->name, IUSER, 0)) {
- zwarnnam(name,
- "invalid character in directory name: %s",
- asg->name);
- returnval = 1;
- continue;
- } else {
- Nameddir nd = hn = zshcalloc(sizeof *nd);
- nd->node.flags = 0;
- nd->dir = ztrdup(asg->value.scalar);
- }
- } else {
- Cmdnam cn = hn = zshcalloc(sizeof *cn);
- cn->node.flags = HASHED;
- cn->u.cmd = ztrdup(asg->value.scalar);
- }
- ht->addnode(ht, ztrdup(asg->name), hn);
- if(OPT_ISSET(ops,'v'))
- ht->printnode(hn, 0);
- }
- } else if (!(hn = ht->getnode2(ht, asg->name))) {
- /* With no `=value' part to the argument, *
- * work out what it ought to be. */
- if(OPT_ISSET(ops,'d')) {
- if(!getnameddir(asg->name)) {
- zwarnnam(name, "no such directory name: %s", asg->name);
- returnval = 1;
- }
- } else {
- if (!hashcmd(asg->name, path)) {
- zwarnnam(name, "no such command: %s", asg->name);
- returnval = 1;
- }
- }
- if(OPT_ISSET(ops,'v') && (hn = ht->getnode2(ht, asg->name)))
- ht->printnode(hn, 0);
- } else if(OPT_ISSET(ops,'v'))
- ht->printnode(hn, 0);
- }
- unqueue_signals();
- return returnval;
-}
-
-/* unhash: remove specified elements from a hash table */
-
-/**/
-int
-bin_unhash(char *name, char **argv, Options ops, int func)
-{
- HashTable ht;
- HashNode hn, nhn;
- Patprog pprog;
- int match = 0, returnval = 0, all = 0;
- int i;
-
- /* Check which hash table we are working with. */
- if (func == BIN_UNALIAS) {
- if (OPT_ISSET(ops,'s'))
- ht = sufaliastab; /* suffix aliases */
- else
- ht = aliastab; /* aliases */
- if (OPT_ISSET(ops, 'a')) {
- if (*argv) {
- zwarnnam(name, "-a: too many arguments");
- return 1;
- }
- all = 1;
- } else if (!*argv) {
- zwarnnam(name, "not enough arguments");
- return 1;
- }
- } else if (OPT_ISSET(ops,'d'))
- ht = nameddirtab; /* named directories */
- else if (OPT_ISSET(ops,'f'))
- ht = shfunctab; /* shell functions */
- else if (OPT_ISSET(ops,'s'))
- ht = sufaliastab; /* suffix aliases, must precede aliases */
- else if (func == BIN_UNHASH && (OPT_ISSET(ops,'a')))
- ht = aliastab; /* aliases */
- else
- ht = cmdnamtab; /* external commands */
-
- if (all) {
- 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 */
- nhn = hn->next;
- ht->freenode(ht->removenode(ht, hn->nam));
- }
- }
- unqueue_signals();
- return 0;
- }
-
- /* With -m option, treat arguments as glob patterns. *
- * "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 */
- 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 */
- nhn = hn->next;
- if (pattry(pprog, hn->nam)) {
- ht->freenode(ht->removenode(ht, hn->nam));
- match++;
- }
- }
- }
- } else {
- untokenize(*argv);
- zwarnnam(name, "bad pattern : %s", *argv);
- returnval = 1;
- }
- unqueue_signals();
- }
- /* If we didn't match anything, we return 1. */
- if (!match)
- returnval = 1;
- return returnval;
- }
-
- /* Take arguments literally -- do not glob */
- queue_signals();
- for (; *argv; argv++) {
- if ((hn = ht->removenode(ht, *argv))) {
- ht->freenode(hn);
- } else if (func == BIN_UNSET && isset(POSIXBUILTINS)) {
- /* POSIX: unset: "Unsetting a variable or function that was *
- * not previously set shall not be considered an error." */
- returnval = 0;
- } else {
- zwarnnam(name, "no such hash table element: %s", *argv);
- returnval = 1;
- }
- }
- unqueue_signals();
- return returnval;
-}
-
-/**** alias builtins ****/
-
-/* alias: display or create aliases. */
-
-/**/
-int
-bin_alias(char *name, char **argv, Options ops, UNUSED(int func))
-{
- Alias a;
- Patprog pprog;
- Asgment asg;
- int returnval = 0;
- int flags1 = 0, flags2 = DISABLED;
- int printflags = 0;
- int type_opts;
- HashTable ht = aliastab;
-
- /* Did we specify the type of alias? */
- type_opts = OPT_ISSET(ops, 'r') + OPT_ISSET(ops, 'g') +
- OPT_ISSET(ops, 's');
- if (type_opts) {
- if (type_opts > 1) {
- zwarnnam(name, "illegal combination of options");
- return 1;
- }
- if (OPT_ISSET(ops,'g'))
- flags1 |= ALIAS_GLOBAL;
- else
- flags2 |= ALIAS_GLOBAL;
- if (OPT_ISSET(ops, 's')) {
- /*
- * Although we keep suffix aliases in a different table,
- * it is useful to be able to distinguish Alias structures
- * without reference to the table, so we have a separate
- * flag, too.
- */
- flags1 |= ALIAS_SUFFIX;
- ht = sufaliastab;
- } else
- flags2 |= ALIAS_SUFFIX;
- }
-
- if (OPT_ISSET(ops,'L'))
- printflags |= PRINT_LIST;
- else if (OPT_PLUS(ops,'g') || OPT_PLUS(ops,'r') || OPT_PLUS(ops,'s') ||
- OPT_PLUS(ops,'m') || OPT_ISSET(ops,'+'))
- printflags |= PRINT_NAMEONLY;
-
- /* In the absence of arguments, list all aliases. If a command *
- * line flag is specified, list only those of that type. */
- if (!*argv) {
- queue_signals();
- scanhashtable(ht, 1, flags1, flags2, ht->printnode, printflags);
- unqueue_signals();
- return 0;
- }
-
- /* With the -m option, treat the arguments as *
- * 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 */
- scanmatchtable(ht, pprog, 1, flags1, flags2,
- ht->printnode, printflags);
- } else {
- untokenize(*argv);
- zwarnnam(name, "bad pattern : %s", *argv);
- returnval = 1;
- }
- unqueue_signals();
- }
- return returnval;
- }
-
- /* Take arguments literally. Don't glob */
- queue_signals();
- while ((asg = getasg(&argv, NULL))) {
- if (asg->value.scalar && !OPT_ISSET(ops,'L')) {
- /* The argument is of the form foo=bar and we are not *
- * forcing a listing with -L, so define an alias */
- ht->addnode(ht, ztrdup(asg->name),
- createaliasnode(ztrdup(asg->value.scalar), flags1));
- } else if ((a = (Alias) ht->getnode(ht, asg->name))) {
- /* display alias if appropriate */
- if (!type_opts || ht == sufaliastab ||
- (OPT_ISSET(ops,'r') &&
- !(a->node.flags & (ALIAS_GLOBAL|ALIAS_SUFFIX))) ||
- (OPT_ISSET(ops,'g') && (a->node.flags & ALIAS_GLOBAL)))
- ht->printnode(&a->node, printflags);
- } else
- returnval = 1;
- }
- unqueue_signals();
- return returnval;
-}
-
-
-/**** miscellaneous builtins ****/
-
-/* true, : (colon) */
-
-/**/
-int
-bin_true(UNUSED(char *name), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func))
-{
- return 0;
-}
-
-/* false builtin */
-
-/**/
-int
-bin_false(UNUSED(char *name), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func))
-{
- return 1;
-}
-
-/* the zle buffer stack */
-
-/**/
-mod_export LinkList bufstack;
-
-/* echo, print, printf, pushln */
-
-#define print_val(VAL) \
- if (prec >= 0) \
- count += fprintf(fout, spec, width, prec, VAL); \
- else \
- count += fprintf(fout, spec, width, VAL);
-
-/*
- * Because of the use of getkeystring() to interpret the arguments,
- * the elements of args spend a large part of the function unmetafied
- * with the lengths in len. This may have seemed a good idea once.
- * As we are stuck with this for now, we need to be very careful
- * deciding what state args is in.
- */
-
-/**/
-int
-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, 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;
-#define ASSIGN_MSTREAM(BUF,FOUT) \
- do { \
- if ((FOUT = open_memstream(&BUF, &mcount)) == NULL) { \
- zwarnnam(name, "open_memstream failed"); \
- return 1; \
- } \
- } while (0)
- /*
- * Some implementations of open_memstream() have a bug such that,
- * if fflush() is followed by fclose(), another NUL byte is written
- * to the buffer at the wrong position. Therefore we must fclose()
- * before reading.
- */
-#define READ_MSTREAM(BUF,FOUT) \
- ((fclose(FOUT) == 0) ? mcount : (size_t)-1)
-#define CLOSE_MSTREAM(FOUT) 0
-
-#else /* simulate HAVE_OPEN_MEMSTREAM */
-
-#define ASSIGN_MSTREAM(BUF,FOUT) \
- do { \
- int tempfd; \
- char *tmpf; \
- if ((tempfd = gettempfile(NULL, 1, &tmpf)) < 0) { \
- zwarnnam(name, "can't open temp file: %e", errno); \
- return 1; \
- } \
- unlink(tmpf); \
- if ((FOUT = fdopen(tempfd, "w+")) == NULL) { \
- close(tempfd); \
- zwarnnam(name, "can't open temp file: %e", errno); \
- return 1; \
- } \
- } while (0)
-#define READ_MSTREAM(BUF,FOUT) \
- ((((count = ftell(FOUT)), (BUF = (char *)zalloc(count + 1))) && \
- ((fseek(FOUT, 0L, SEEK_SET) == 0) && !(BUF[count] = '\0')) && \
- (fread(BUF, 1, count, FOUT) == count)) ? count : (size_t)-1)
-#define CLOSE_MSTREAM(FOUT) fclose(FOUT)
-
-#endif
-
-#define IS_MSTREAM(FOUT) \
- (FOUT != stdout && \
- (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s') || OPT_ISSET(ops,'v')))
-
- /* Testing EBADF special-cases >&- redirections */
-#define CLOSE_CLEANLY(FOUT) \
- (IS_MSTREAM(FOUT) ? CLOSE_MSTREAM(FOUT) == 0 : \
- ((FOUT == stdout) ? (fflush(FOUT) == 0 || errno == EBADF) : \
- (fclose(FOUT) == 0))) /* implies error for -u on a closed fd */
-
- Histent ent;
- mnumber mnumval;
- double doubleval;
- int intval;
- zlong zlongval;
- zulong zulongval;
- char *stringval;
-
- /* Error check option combinations and option arguments */
-
- if (OPT_ISSET(ops, 'z') +
- OPT_ISSET(ops, 's') + OPT_ISSET(ops, 'S') +
- OPT_ISSET(ops, 'v') > 1) {
- zwarnnam(name, "only one of -s, -S, -v, or -z allowed");
- return 1;
- }
- if ((OPT_ISSET(ops, 'z') | OPT_ISSET(ops, 's') | OPT_ISSET(ops, 'S')) +
- (OPT_ISSET(ops, 'c') | OPT_ISSET(ops, 'C')) > 1) {
- zwarnnam(name, "-c or -C not allowed with -s, -S, or -z");
- return 1;
- }
- if ((OPT_ISSET(ops, 'z') | OPT_ISSET(ops, 'v') |
- OPT_ISSET(ops, 's') | OPT_ISSET(ops, 'S')) +
- (OPT_ISSET(ops, 'p') | OPT_ISSET(ops, 'u')) > 1) {
- zwarnnam(name, "-p or -u not allowed with -s, -S, -v, or -z");
- return 1;
- }
- /*
- if (OPT_ISSET(ops, 'f') &&
- (OPT_ISSET(ops, 'S') || OPT_ISSET(ops, 'c') || OPT_ISSET(ops, 'C'))) {
- zwarnnam(name, "-f not allowed with -c, -C, or -S");
- return 1;
- }
- */
-
- /* -C -- number of columns */
- if (!fmt && OPT_ISSET(ops,'C')) {
- char *eptr, *argptr = OPT_ARG(ops,'C');
- nc = (int)zstrtol(argptr, &eptr, 10);
- if (*eptr) {
- zwarnnam(name, "number expected after -%c: %s", 'C', argptr);
- return 1;
- }
- if (nc <= 0) {
- zwarnnam(name, "invalid number of columns: %s", argptr);
- return 1;
- }
- }
-
- if (func == BIN_PRINTF) {
- if (!strcmp(*args, "--") && !*++args) {
- zwarnnam(name, "not enough arguments");
- return 1;
- }
- fmt = *args++;
- } else if (func == BIN_ECHO && isset(BSDECHO))
- ops->ind['E'] = 1;
- else if (OPT_HASARG(ops,'f'))
- fmt = OPT_ARG(ops,'f');
- if (fmt)
- fmt = getkeystring(fmt, &flen, OPT_ISSET(ops,'b') ? GETKEYS_BINDKEY :
- GETKEYS_PRINTF_FMT, &fmttrunc);
-
- first = args;
-
- /* -m option -- treat the first argument as a pattern and remove
- * arguments not matching */
- if (OPT_ISSET(ops,'m')) {
- Patprog pprog;
- char **t, **p;
-
- if (!*args) {
- 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++)
- if (pattry(pprog, *p))
- *t++ = *p;
- *t = NULL;
- first = args;
- unqueue_signals();
- if (fmt && !*args) return 0;
- }
- /* compute lengths, and interpret according to -P, -D, -e, etc. */
- argc = arrlen(args);
- len = (int *) hcalloc(argc * sizeof(int));
- for (n = 0; n < argc; n++) {
- /* first \ sequences */
- if (fmt ||
- (!OPT_ISSET(ops,'e') &&
- (OPT_ISSET(ops,'R') || OPT_ISSET(ops,'r') || OPT_ISSET(ops,'E'))))
- unmetafy(args[n], &len[n]);
- else {
- int escape_how;
- if (OPT_ISSET(ops,'b'))
- escape_how = GETKEYS_BINDKEY;
- else if (func != BIN_ECHO && !OPT_ISSET(ops,'e'))
- escape_how = GETKEYS_PRINT;
- else
- escape_how = GETKEYS_ECHO;
- args[n] = getkeystring(args[n], &len[n], escape_how, &nnl);
- if (nnl) {
- /* If there was a \c escape, make this the last arg. */
- argc = n + 1;
- args[argc] = NULL;
- }
- }
- /* -P option -- interpret as a prompt sequence */
- if (OPT_ISSET(ops,'P')) {
- /*
- * promptexpand uses permanent storage: to avoid
- * messy memory management, stick it on the heap
- * instead.
- */
- char *str = unmetafy(
- promptexpand(metafy(args[n], len[n], META_NOALLOC),
- 0, NULL, NULL, NULL),
- &len[n]);
- args[n] = dupstrpfx(str, len[n]);
- free(str);
- }
- /* -D option -- interpret as a directory, and use ~ */
- if (OPT_ISSET(ops,'D')) {
- Nameddir d;
-
- queue_signals();
- /* TODO: finddir takes a metafied file */
- d = finddir(args[n]);
- if (d) {
- int dirlen = strlen(d->dir);
- char *arg = zhalloc(len[n] - dirlen + strlen(d->node.nam) + 2);
- sprintf(arg, "~%s%s", d->node.nam, args[n] + dirlen);
- args[n] = arg;
- len[n] = strlen(args[n]);
- }
- unqueue_signals();
- }
- }
-
- /* -o and -O -- sort the arguments */
- if (OPT_ISSET(ops,'o') || OPT_ISSET(ops,'O')) {
- int flags;
-
- if (fmt && !*args)
- return 0;
- flags = OPT_ISSET(ops,'i') ? SORTIT_IGNORING_CASE : 0;
- if (OPT_ISSET(ops,'O'))
- flags |= SORTIT_BACKWARDS;
- strmetasort(args, flags, len);
- }
-
- /* -u and -p -- output to other than standard output */
- if ((OPT_HASARG(ops,'u') || OPT_ISSET(ops,'p')) &&
- /* rule out conflicting options -- historical precedence */
- ((!fmt && (OPT_ISSET(ops,'c') || OPT_ISSET(ops,'C'))) ||
- !(OPT_ISSET(ops, 'z') || OPT_ISSET(ops, 'v') ||
- OPT_ISSET(ops, 's') || OPT_ISSET(ops, 'S')))) {
- int fdarg, fd;
-
- if (OPT_ISSET(ops, 'p')) {
- fdarg = coprocout;
- if (fdarg < 0) {
- zwarnnam(name, "-p: no coprocess");
- return 1;
- }
- } else {
- char *argptr = OPT_ARG(ops,'u'), *eptr;
- /* Handle undocumented feature that -up worked */
- if (!strcmp(argptr, "p")) {
- fdarg = coprocout;
- if (fdarg < 0) {
- zwarnnam(name, "-p: no coprocess");
- return 1;
- }
- } else {
- fdarg = (int)zstrtol(argptr, &eptr, 10);
- if (*eptr) {
- zwarnnam(name, "number expected after -u: %s", argptr);
- return 1;
- }
- }
- }
-
- if ((fd = dup(fdarg)) < 0) {
- zwarnnam(name, "bad file number: %d", fdarg);
- return 1;
- }
- if ((fout = fdopen(fd, "w")) == 0) {
- close(fd);
- zwarnnam(name, "bad mode on fd %d", fd);
- return 1;
- }
- }
-
- if (OPT_ISSET(ops, 'v') ||
- (fmt && (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s'))))
- ASSIGN_MSTREAM(buf,fout);
-
- /* -c -- output in columns */
- if (!fmt && (OPT_ISSET(ops,'c') || OPT_ISSET(ops,'C'))) {
- int l, nr, sc, n, t, i;
-#ifdef MULTIBYTE_SUPPORT
- int *widths;
-
- if (isset(MULTIBYTE)) {
- int *wptr;
-
- /*
- * We need the character widths to align output in
- * columns.
- */
- wptr = widths = (int *) zhalloc(argc * sizeof(int));
- for (i = 0; i < argc && args[i]; i++, wptr++) {
- int l = len[i], width = 0;
- char *aptr = args[i];
- mbstate_t mbs;
-
- memset(&mbs, 0, sizeof(mbstate_t));
- while (l > 0) {
- wchar_t wc;
- size_t cnt;
- int wcw;
-
- /*
- * Prevent misaligned columns due to escape sequences by
- * skipping over them. Octals \033 and \233 are the
- * possible escape characters recognized by ANSI.
- *
- * It ought to be possible to do this in the case
- * of prompt expansion by propagating the information
- * about escape sequences (currently we strip this
- * out).
- */
- if (*aptr == '\033' || *aptr == '\233') {
- for (aptr++, l--;
- l && !isalpha(STOUC(*aptr));
- aptr++, l--)
- ;
- aptr++;
- l--;
- continue;
- }
-
- cnt = mbrtowc(&wc, aptr, l, &mbs);
-
- if (cnt == MB_INCOMPLETE || cnt == MB_INVALID)
- {
- /* treat as ordinary string */
- width += l;
- break;
- }
- wcw = WCWIDTH(wc);
- /* treat unprintable as 0 */
- if (wcw > 0)
- width += wcw;
- /* skip over NUL normally */
- if (cnt == 0)
- cnt = 1;
- aptr += cnt;
- l -= cnt;
- }
- widths[i] = width;
- }
- }
- else
- widths = len;
-#else
- int *widths = len;
-#endif
-
- if (OPT_ISSET(ops,'C')) {
- /*
- * n: number of elements
- * nc: number of columns (above)
- * nr: number of rows
- */
- n = arrlen(args);
- nr = (n + nc - 1) / nc;
-
- /*
- * i: loop counter
- * l: maximum length seen
- *
- * Ignore lengths in last column since they don't affect
- * the separation.
- */
- for (i = l = 0; i < argc; i++) {
- if (OPT_ISSET(ops, 'a')) {
- if ((i % nc) == nc - 1)
- continue;
- } else {
- if (i >= nr * (nc - 1))
- break;
- }
- if (l < widths[i])
- l = widths[i];
- }
- sc = l + 2;
- }
- else
- {
- /*
- * n: loop counter
- * l: maximum length seen
- */
- for (n = l = 0; n < argc; n++)
- if (l < widths[n])
- l = widths[n];
-
- /*
- * sc: column width
- * nc: number of columns (at least one)
- */
- sc = l + 2;
- nc = (zterm_columns + 1) / sc;
- if (!nc)
- nc = 1;
- nr = (n + nc - 1) / nc;
- }
-
- if (OPT_ISSET(ops,'a')) /* print across, i.e. columns first */
- n = 0;
- for (i = 0; i < nr; i++) {
- if (OPT_ISSET(ops,'a'))
- {
- int ic;
- for (ic = 0; ic < nc && n < argc; ic++, n++)
- {
- fwrite(args[n], len[n], 1, fout);
- l = widths[n];
- if (n < argc)
- for (; l < sc; l++)
- fputc(' ', fout);
- }
- }
- else
- {
- n = i;
- do {
- fwrite(args[n], len[n], 1, fout);
- l = widths[n];
- for (t = nr; t && n < argc; t--, n++);
- if (n < argc)
- for (; l < sc; l++)
- fputc(' ', fout);
- } while (n < argc);
- }
- fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout);
- }
- if (IS_MSTREAM(fout) && (rcount = READ_MSTREAM(buf,fout)) == -1)
- ret = 1;
- if (!CLOSE_CLEANLY(fout) || ret) {
- zwarnnam(name, "write error: %e", errno);
- ret = 1;
- }
- if (buf) {
- /* assert: we must be doing -v at this point */
- queue_signals();
- if (ret)
- free(buf);
- else
- setsparam(OPT_ARG(ops, 'v'),
- metafy(buf, rcount, META_REALLOC));
- unqueue_signals();
- }
- return ret;
- }
-
- /* normal output */
- if (!fmt) {
- if (OPT_ISSET(ops, 'z') || OPT_ISSET(ops, 'v') ||
- OPT_ISSET(ops, 's') || OPT_ISSET(ops, 'S')) {
- /*
- * We don't want the arguments unmetafied after all.
- */
- for (n = 0; n < argc; n++)
- metafy(args[n], len[n], META_NOALLOC);
- }
-
- /* -z option -- push the arguments onto the editing buffer stack */
- if (OPT_ISSET(ops,'z')) {
- queue_signals();
- zpushnode(bufstack, sepjoin(args, NULL, 0));
- unqueue_signals();
- return 0;
- }
- /* -s option -- add the arguments to the history list */
- if (OPT_ISSET(ops,'s') || OPT_ISSET(ops,'S')) {
- int nwords = 0, nlen, iwords;
- char **pargs = args;
-
- queue_signals();
- while (*pargs++)
- nwords++;
- if (nwords) {
- if (OPT_ISSET(ops,'S')) {
- int wordsize;
- short *words;
- if (nwords > 1) {
- zwarnnam(name, "option -S takes a single argument");
- unqueue_signals();
- return 1;
- }
- words = NULL;
- wordsize = 0;
- histsplitwords(*args, &words, &wordsize, &nwords, 1);
- ent = prepnexthistent();
- ent->words = (short *)zalloc(nwords*sizeof(short));
- memcpy(ent->words, words, nwords*sizeof(short));
- free(words);
- ent->nwords = nwords/2;
- } else {
- ent = prepnexthistent();
- ent->words = (short *)zalloc(nwords*2*sizeof(short));
- ent->nwords = nwords;
- nlen = iwords = 0;
- for (pargs = args; *pargs; pargs++) {
- ent->words[iwords++] = nlen;
- nlen += strlen(*pargs);
- ent->words[iwords++] = nlen;
- nlen++;
- }
- }
- } else {
- ent = prepnexthistent();
- ent->words = (short *)NULL;
- }
- ent->node.nam = zjoin(args, ' ', 0);
- ent->stim = ent->ftim = time(NULL);
- ent->node.flags = 0;
- addhistnode(histtab, ent->node.nam, ent);
- unqueue_signals();
- return 0;
- }
-
- if (OPT_HASARG(ops, 'x') || OPT_HASARG(ops, 'X')) {
- char *eptr;
- int expand, startpos = 0;
- int all = OPT_HASARG(ops, 'X');
- char *xarg = all ? OPT_ARG(ops, 'X') : OPT_ARG(ops, 'x');
-
- expand = (int)zstrtol(xarg, &eptr, 10);
- if (*eptr || expand <= 0) {
- zwarnnam(name, "positive integer expected after -%c: %s", 'x',
- xarg);
- return 1;
- }
- for (; *args; args++, len++) {
- startpos = zexpandtabs(*args, *len, expand, startpos, fout,
- all);
- if (args[1]) {
- if (OPT_ISSET(ops, 'l')) {
- fputc('\n', fout);
- startpos = 0;
- } else if (OPT_ISSET(ops,'N')) {
- fputc('\0', fout);
- } else {
- fputc(' ', fout);
- startpos++;
- }
- }
- }
- } else {
- for (; *args; args++, len++) {
- fwrite(*args, *len, 1, fout);
- if (args[1])
- fputc(OPT_ISSET(ops,'l') ? '\n' :
- OPT_ISSET(ops,'N') ? '\0' : ' ', fout);
- }
- }
- 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;
- if (!CLOSE_CLEANLY(fout) || ret) {
- zwarnnam(name, "write error: %e", errno);
- ret = 1;
- }
- if (buf) {
- /* assert: we must be doing -v at this point */
- queue_signals();
- if (ret)
- free(buf);
- else
- setsparam(OPT_ARG(ops, 'v'),
- metafy(buf, rcount, META_REALLOC));
- unqueue_signals();
- }
- return ret;
- }
-
- /*
- * All the remaining code in this function is for printf-style
- * output (printf itself, or print -f). We still have to handle
- * 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;
- maxarg = 0;
- }
- for (c = fmt; c-fmt < flen; c++) {
- if (*c != '%') {
- putc(*c, fout);
- ++count;
- continue;
- }
-
- start = c++;
- if (*c == '%') {
- putc('%', fout);
- ++count;
- continue;
- }
-
- type = prec = -1;
- width = 0;
- curarg = NULL;
- d = spec + 1;
-
- if (*c >= '1' && *c <= '9') {
- narg = strtoul(c, &endptr, 0);
- if (*endptr == '$') {
- c = endptr + 1;
- DPUTS(narg <= 0, "specified zero or negative arg");
- if (narg > argc) {
- zwarnnam(name, "%d: argument specifier out of range",
- narg);
- if (fout != stdout)
- fclose(fout);
-#ifdef HAVE_OPEN_MEMSTREAM
- if (buf)
- free(buf);
-#endif
- return 1;
- } else {
- if (narg > maxarg) maxarg = narg;
- curarg = *(first + narg - 1);
- curlen = len[first - args + narg - 1];
- }
- }
- }
-
- /* copy only one of each flag as spec has finite size */
- memset(flags, 0, sizeof(flags));
- while (*c && (flag = strchr(flagch, *c))) {
- if (!flags[flag - flagch]) {
- flags[flag - flagch] = 1;
- *d++ = *c;
- }
- c++;
- }
-
- if (idigit(*c)) {
- width = strtoul(c, &endptr, 0);
- c = endptr;
- } else if (*c == '*') {
- if (idigit(*++c)) {
- narg = strtoul(c, &endptr, 0);
- if (*endptr == '$') {
- c = endptr + 1;
- if (narg > argc || narg <= 0) {
- zwarnnam(name,
- "%d: argument specifier out of range",
- narg);
- if (fout != stdout)
- fclose(fout);
-#ifdef HAVE_OPEN_MEMSTREAM
- if (buf)
- free(buf);
-#endif
- return 1;
- } else {
- if (narg > maxarg) maxarg = narg;
- argp = first + narg - 1;
- }
- }
- }
- if (*argp) {
- width = (int)mathevali(*argp++);
- if (errflag) {
- errflag &= ~ERRFLAG_ERROR;
- ret = 1;
- }
- }
- }
- *d++ = '*';
-
- if (*c == '.') {
- if (*++c == '*') {
- if (idigit(*++c)) {
- narg = strtoul(c, &endptr, 0);
- if (*endptr == '$') {
- c = endptr + 1;
- if (narg > argc || narg <= 0) {
- zwarnnam(name,
- "%d: argument specifier out of range",
- narg);
- if (fout != stdout)
- fclose(fout);
-#ifdef HAVE_OPEN_MEMSTREAM
- if (buf)
- free(buf);
-#endif
- return 1;
- } else {
- if (narg > maxarg) maxarg = narg;
- argp = first + narg - 1;
- }
- }
- }
-
- if (*argp) {
- prec = (int)mathevali(*argp++);
- if (errflag) {
- errflag &= ~ERRFLAG_ERROR;
- ret = 1;
- }
- }
- } else if (idigit(*c)) {
- prec = strtoul(c, &endptr, 0);
- c = endptr;
- } else
- prec = 0;
- if (prec >= 0) *d++ = '.', *d++ = '*';
- }
-
- /* ignore any size modifier */
- if (*c == 'l' || *c == 'L' || *c == 'h') c++;
-
- if (!curarg && *argp) {
- curarg = *argp;
- curlen = len[argp++ - args];
- }
- d[1] = '\0';
- switch (*d = *c) {
- case 'c':
- if (curarg)
- intval = *curarg;
- else
- intval = 0;
- print_val(intval);
- break;
- case 's':
- case 'b':
- if (curarg) {
- char *b, *ptr;
- int lbytes, lchars, lleft;
-#ifdef MULTIBYTE_SUPPORT
- mbstate_t mbs;
-#endif
-
- if (*c == 'b') {
- b = getkeystring(metafy(curarg, curlen, META_USEHEAP),
- &lbytes,
- OPT_ISSET(ops,'b') ? GETKEYS_BINDKEY :
- GETKEYS_PRINTF_ARG, &nnl);
- } else {
- b = curarg;
- lbytes = curlen;
- }
- /*
- * Handle width/precision here and use fwrite so that
- * nul characters can be output.
- *
- * First, examine width of string given that it
- * may contain multibyte characters. The output
- * widths are for characters, so we need to count
- * (in lchars). However, if we need to truncate
- * the string we need the width in bytes (in lbytes).
- */
- ptr = b;
-#ifdef MULTIBYTE_SUPPORT
- memset(&mbs, 0, sizeof(mbs));
-#endif
-
- for (lchars = 0, lleft = lbytes; lleft > 0; lchars++) {
- int chars;
-
- if (lchars == prec) {
- /* Truncate at this point. */
- lbytes = ptr - b;
- break;
- }
-#ifdef MULTIBYTE_SUPPORT
- if (isset(MULTIBYTE)) {
- chars = mbrlen(ptr, lleft, &mbs);
- if (chars < 0) {
- /*
- * Invalid/incomplete character at this
- * point. Assume all the rest are a
- * single byte. That's about the best we
- * can do.
- */
- lchars += lleft;
- lbytes = (ptr - b) + lleft;
- break;
- } else if (chars == 0) {
- /* NUL, handle as real character */
- chars = 1;
- }
- }
- else /* use the non-multibyte code below */
-#endif
- chars = 1; /* compiler can optimise this...*/
- lleft -= chars;
- ptr += chars;
- }
- if (width > 0 && flags[3]) width = -width;
- if (width > 0 && lchars < width)
- count += fprintf(fout, "%*c", width - lchars, ' ');
- count += fwrite(b, 1, lbytes, fout);
- if (width < 0 && lchars < -width)
- count += fprintf(fout, "%*c", -width - lchars, ' ');
- if (nnl) {
- /* If the %b arg had a \c escape, truncate the fmt. */
- flen = c - fmt + 1;
- fmttrunc = 1;
- }
- } else if (width)
- count += fprintf(fout, "%*c", width, ' ');
- break;
- case 'q':
- stringval = curarg ?
- quotestring(metafy(curarg, curlen, META_USEHEAP),
- QT_BACKSLASH_SHOWNULL) : &nullstr;
- *d = 's';
- print_val(unmetafy(stringval, &curlen));
- break;
- case 'd':
- case 'i':
- type=1;
- break;
- case 'e':
- case 'E':
- case 'f':
- case 'g':
- case 'G':
- type=2;
- break;
- case 'o':
- case 'u':
- case 'x':
- case 'X':
- type=3;
- break;
- case 'n':
- if (curarg) setiparam(curarg, count - rcount);
- break;
- default:
- if (*c) {
- save = c[1];
- c[1] = '\0';
- }
- zwarnnam(name, "%s: invalid directive", start);
- if (*c) c[1] = save;
- /* Why do we care about a clean close here? */
- if (!CLOSE_CLEANLY(fout))
- zwarnnam(name, "write error: %e", errno);
-#ifdef HAVE_OPEN_MEMSTREAM
- if (buf)
- free(buf);
-#endif
- return 1;
- }
-
- if (type > 0) {
- if (curarg && (*curarg == '\'' || *curarg == '"' )) {
- convchar_t cc;
-#ifdef MULTIBYTE_SUPPORT
- if (isset(MULTIBYTE)) {
- mb_charinit();
- (void)mb_metacharlenconv(metafy(curarg+1, curlen-1,
- META_USEHEAP), &cc);
- }
- else
- cc = WEOF;
- if (cc == WEOF)
- cc = (curlen > 1) ? STOUC(curarg[1]) : 0;
-#else
- cc = (curlen > 1) ? STOUC(curarg[1]) : 0;
-#endif
- if (type == 2) {
- doubleval = cc;
- print_val(doubleval);
- } else {
- intval = cc;
- print_val(intval);
- }
- } else {
- switch (type) {
- case 1:
-#ifdef ZSH_64_BIT_TYPE
- *d++ = 'l';
-#endif
- *d++ = 'l', *d++ = *c, *d = '\0';
- zlongval = (curarg) ? mathevali(curarg) : 0;
- if (errflag) {
- zlongval = 0;
- errflag &= ~ERRFLAG_ERROR;
- ret = 1;
- }
- print_val(zlongval)
- break;
- case 2:
- if (curarg) {
- char *eptr;
- /*
- * First attempt to parse as a floating
- * point constant. If we go through
- * a math evaluation, we can lose
- * mostly unimportant information
- * that people in standards organizations
- * worry about.
- */
- doubleval = strtod(curarg, &eptr);
- /*
- * If it didn't parse as a constant,
- * parse it as an expression.
- */
- if (*eptr != '\0') {
- mnumval = matheval(curarg);
- doubleval = (mnumval.type & MN_FLOAT) ?
- mnumval.u.d : (double)mnumval.u.l;
- }
- } else doubleval = 0;
- if (errflag) {
- doubleval = 0;
- errflag &= ~ERRFLAG_ERROR;
- ret = 1;
- }
- /* force consistent form for Inf/NaN output */
- if (isnan(doubleval))
- count += fputs("nan", fout);
- else if (isinf(doubleval))
- count += fputs((doubleval < 0.0) ? "-inf" : "inf", fout);
- else
- print_val(doubleval)
- break;
- case 3:
-#ifdef ZSH_64_BIT_UTYPE
- *d++ = 'l';
-#endif
- *d++ = 'l', *d++ = *c, *d = '\0';
- if (!curarg)
- zulongval = (zulong)0;
- else if (!zstrtoul_underscore(curarg, &zulongval))
- zulongval = mathevali(curarg);
- if (errflag) {
- zulongval = 0;
- errflag &= ~ERRFLAG_ERROR;
- ret = 1;
- }
- print_val(zulongval)
- }
- }
- }
- if (maxarg && (argp - first > maxarg))
- maxarg = argp - first;
- }
-
- if (maxarg) argp = first + maxarg;
- /* if there are remaining args, reuse format string */
- } while (*argp && argp != first && !fmttrunc && !OPT_ISSET(ops,'r'));
-
- if (IS_MSTREAM(fout)) {
- queue_signals();
- if ((rcount = READ_MSTREAM(buf,fout)) == -1) {
- zwarnnam(name, "i/o error: %e", errno);
- if (buf)
- free(buf);
- } else {
- 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 {
- 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();
- }
-
- if (!CLOSE_CLEANLY(fout))
- {
- zwarnnam(name, "write error: %e", errno);
- ret = 1;
- }
- return ret;
-}
-
-/* shift builtin */
-
-/**/
-int
-bin_shift(char *name, char **argv, Options ops, UNUSED(int func))
-{
- int num = 1, l, ret = 0;
- char **s;
-
- /* optional argument can be either numeric or an array */
- queue_signals();
- if (*argv && !getaparam(*argv)) {
- num = mathevali(*argv++);
- if (errflag) {
- unqueue_signals();
- return 1;
- }
- }
-
- if (num < 0) {
- unqueue_signals();
- zwarnnam(name, "argument to shift must be non-negative");
- return 1;
- }
-
- if (*argv) {
- for (; *argv; argv++)
- if ((s = getaparam(*argv))) {
- if (arrlen_lt(s, num)) {
- zwarnnam(name, "shift count must be <= $#");
- ret++;
- continue;
- }
- if (OPT_ISSET(ops,'p')) {
- char **s2, **src, **dst;
- int count;
- l = arrlen(s);
- src = s;
- dst = s2 = (char **)zalloc((l - num + 1) * sizeof(char *));
- for (count = l - num; count; count--)
- *dst++ = ztrdup(*src++);
- *dst = NULL;
- s = s2;
- } else {
- s = zarrdup(s + num);
- }
- setaparam(*argv, s);
- }
- } else {
- if (num > (l = arrlen(pparams))) {
- zwarnnam(name, "shift count must be <= $#");
- ret = 1;
- } else {
- s = zalloc((l - num + 1) * sizeof(char *));
- if (OPT_ISSET(ops,'p')) {
- memcpy(s, pparams, (l - num) * sizeof(char *));
- s[l-num] = NULL;
- while (num--)
- zsfree(pparams[l-1-num]);
- } else {
- memcpy(s, pparams + num, (l - num + 1) * sizeof(char *));
- while (num--)
- zsfree(pparams[num]);
- }
- zfree(pparams, (l + 1) * sizeof(char *));
- pparams = s;
- }
- }
- unqueue_signals();
- return ret;
-}
-
-/*
- * Position of getopts option within OPTIND argument with multiple options.
- */
-
-/**/
-int optcind;
-
-/* getopts: automagical option handling for shell scripts */
-
-/**/
-int
-bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int func))
-{
- int lenstr, lenoptstr, quiet, lenoptbuf;
- char *optstr = unmetafy(*argv++, &lenoptstr), *var = *argv++;
- char **args = (*argv) ? argv : pparams;
- char *str, optbuf[2] = " ", *p, opch;
-
- /* zoptind keeps count of the current argument number. The *
- * user can set it to zero to start a new option parse. */
- if (zoptind < 1) {
- /* first call */
- zoptind = 1;
- optcind = 0;
- }
- if (arrlen_lt(args, zoptind))
- /* no more options */
- return 1;
-
- /* leading ':' in optstr means don't print an error message */
- quiet = *optstr == ':';
- optstr += quiet;
- lenoptstr -= quiet;
-
- /* find place in relevant argument */
- str = unmetafy(dupstring(args[zoptind - 1]), &lenstr);
- if (!lenstr) /* Definitely not an option. */
- return 1;
- if(optcind >= lenstr) {
- optcind = 0;
- if(!args[zoptind++])
- return 1;
- str = unmetafy(dupstring(args[zoptind - 1]), &lenstr);
- }
- if(!optcind) {
- if(lenstr < 2 || (*str != '-' && *str != '+'))
- return 1;
- if(lenstr == 2 && str[0] == '-' && str[1] == '-') {
- zoptind++;
- return 1;
- }
- optcind++;
- }
- opch = str[optcind++];
- if(str[0] == '+') {
- optbuf[0] = '+';
- lenoptbuf = 2;
- } else
- lenoptbuf = 1;
- optbuf[lenoptbuf - 1] = opch;
-
- /* check for legality */
- if(opch == ':' || !(p = memchr(optstr, opch, lenoptstr))) {
- p = "?";
- err:
- zsfree(zoptarg);
- setsparam(var, ztrdup(p));
- if(quiet) {
- zoptarg = metafy(optbuf, lenoptbuf, META_DUP);
- } else {
- zwarn(*p == '?' ? "bad option: %c%c" :
- "argument expected after %c%c option",
- "?-+"[lenoptbuf], opch);
- zoptarg=ztrdup("");
- }
- return 0;
- }
-
- /* check for required argument */
- if(p[1] == ':') {
- if(optcind == lenstr) {
- if(!args[zoptind]) {
- p = ":";
- goto err;
- }
- p = ztrdup(args[zoptind++]);
- } else
- p = metafy(str+optcind, lenstr-optcind, META_DUP);
- /*
- * Careful: I've just changed the following two lines from
- * optcind = ztrlen(args[zoptind - 1]);
- * and it's a rigorous theorem that every change in getopts breaks
- * something. See zsh-workers/9095 for the bug fixed here.
- * PWS 2000/05/02
- */
- optcind = 0;
- zoptind++;
- zsfree(zoptarg);
- zoptarg = p;
- } else {
- zsfree(zoptarg);
- zoptarg = ztrdup("");
- }
-
- setsparam(var, metafy(optbuf, lenoptbuf, META_DUP));
- return 0;
-}
-
-/* Flag that we should exit the shell as soon as all functions return. */
-/**/
-mod_export int
-exit_pending;
-
-/* Shell level at which we exit if exit_pending */
-/**/
-mod_export int
-exit_level;
-
-/* break, bye, continue, exit, logout, return -- most of these take *
- * one numeric argument, and the other (logout) is related to return. *
- * (return is treated as a logout when in a login shell.) */
-
-/**/
-int
-bin_break(char *name, char **argv, UNUSED(Options ops), int func)
-{
- int num = lastval, nump = 0, implicit;
-
- /* handle one optional numeric argument */
- implicit = !*argv;
- if (*argv) {
- num = mathevali(*argv++);
- nump = 1;
- }
-
- if (nump > 0 && (func == BIN_CONTINUE || func == BIN_BREAK) && num <= 0) {
- zerrnam(name, "argument is not positive: %d", num);
- return 1;
- }
-
- switch (func) {
- case BIN_CONTINUE:
- if (!loops) { /* continue is only permitted in loops */
- zerrnam(name, "not in while, until, select, or repeat loop");
- return 1;
- }
- contflag = 1; /* FALLTHROUGH */
- case BIN_BREAK:
- if (!loops) { /* break is only permitted in loops */
- zerrnam(name, "not in while, until, select, or repeat loop");
- return 1;
- }
- breaks = nump ? minimum(num,loops) : 1;
- break;
- case BIN_RETURN:
- if ((isset(INTERACTIVE) && isset(SHINSTDIN))
- || locallevel || sourcelevel) {
- retflag = 1;
- breaks = loops;
- lastval = num;
- if (trap_state == TRAP_STATE_PRIMED && trap_return == -2
- /*
- * With POSIX, "return" on its own in a trap doesn't
- * update $? --- we keep the status from before the
- * trap.
- */
- && !(isset(POSIXTRAPS) && implicit)) {
- trap_state = TRAP_STATE_FORCE_RETURN;
- trap_return = lastval;
- }
- return lastval;
- }
- zexit(num, 0); /* else treat return as logout/exit */
- break;
- case BIN_LOGOUT:
- if (unset(LOGINSHELL)) {
- zerrnam(name, "not login shell");
- return 1;
- }
- /*FALLTHROUGH*/
- case BIN_EXIT:
- 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
- * the usual interactive tests to see if we can exit at
- * all, however.
- *
- * 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;
- breaks = loops;
- exit_pending = (num << 1) | 1;
- exit_level = locallevel;
- }
- } else
- zexit(num, 0);
- break;
- }
- 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 */
-
-/**/
-static void
-checkjobs(void)
-{
- int i;
-
- for (i = 1; i <= maxjob; i++)
- if (i != thisjob && (jobtab[i].stat & STAT_LOCKED) &&
- !(jobtab[i].stat & STAT_NOPRINT) &&
- (isset(CHECKRUNNINGJOBS) || jobtab[i].stat & STAT_STOPPED))
- break;
- if (i <= maxjob) {
- if (jobtab[i].stat & STAT_STOPPED) {
-
-#ifdef USE_SUSPENDED
- zerr("you have suspended jobs.");
-#else
- zerr("you have stopped jobs.");
-#endif
-
- } else
- zerr("you have running jobs.");
- stopmsg = 1;
- }
-}
-
-/*
- * -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
- * 2 if we can't actually exit yet (e.g. functions need
- * terminating) but should perform the usual interactive tests.
- */
-
-/**/
-mod_export void
-zexit(int val, int from_where)
-{
- /* Don't do anything recursively: see below */
- if (shell_exiting == -1)
- return;
-
- if (isset(MONITOR) && !stopmsg && from_where != 1) {
- scanjobs(); /* check if jobs need printing */
- if (isset(CHECKJOBS))
- checkjobs(); /* check if any jobs are running/stopped */
- if (stopmsg) {
- stopmsg = 2;
- return;
- }
- }
- /* Positive in_exit means we have been here before */
- if (from_where == 2 || (shell_exiting++ && from_where))
- return;
-
- /*
- * We're now committed to exiting. Set shell_exiting to -1 to
- * indicate we shouldn't do any recursive processing.
- */
- shell_exiting = -1;
- /*
- * We want to do all remaining processing regardless of preceding
- * errors, even user interrupts.
- */
- errflag = 0;
-
- if (isset(MONITOR)) {
- /* send SIGHUP to any jobs left running */
- killrunjobs(from_where == 1);
- }
- if (isset(RCS) && interact) {
- if (!nohistsave) {
- int writeflags = HFILE_USE_OPTIONS;
- if (from_where == 1)
- writeflags |= HFILE_NO_REWRITE;
- saveandpophiststack(1, writeflags);
- savehistfile(NULL, 1, writeflags);
- }
- if (islogin && !subsh) {
- sourcehome(".zlogout");
-#ifdef GLOBAL_ZLOGOUT
- if (isset(RCS) && isset(GLOBALRCS))
- source(GLOBAL_ZLOGOUT);
-#endif
- }
- }
- 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);
- runhookdef(EXITHOOK, NULL);
- if (opts[MONITOR] && interact && (SHTTY != -1)) {
- release_pgrp();
- }
- if (mypid != getpid())
- _exit(val);
- else
- exit(val);
-}
-
-/* . (dot), source */
-
-/**/
-int
-bin_dot(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
-{
- char **old, *old0 = NULL;
- int diddot = 0, dotdot = 0;
- char *s, **t, *enam, *arg0, *buf;
- struct stat st;
- enum source_return ret;
-
- if (!*argv)
- return 0;
- old = pparams;
- /* get arguments for the script */
- if (argv[1])
- pparams = zarrdup(argv + 1);
-
- enam = arg0 = ztrdup(*argv);
- if (isset(FUNCTIONARGZERO)) {
- old0 = argzero;
- argzero = ztrdup(arg0);
- }
- s = unmeta(enam);
- errno = ENOENT;
- ret = SOURCE_NOT_FOUND;
- /* for source only, check in current directory first */
- if (*name != '.' && access(s, F_OK) == 0
- && stat(s, &st) >= 0 && !S_ISDIR(st.st_mode)) {
- diddot = 1;
- ret = source(enam);
- }
- if (ret == SOURCE_NOT_FOUND) {
- /* use a path with / in it */
- for (s = arg0; *s; s++)
- if (*s == '/') {
- if (*arg0 == '.') {
- if (arg0 + 1 == s)
- ++diddot;
- else if (arg0[1] == '.' && arg0 + 2 == s)
- ++dotdot;
- }
- ret = source(arg0);
- break;
- }
- if (!*s || (ret == SOURCE_NOT_FOUND &&
- isset(PATHDIRS) && diddot < 2 && dotdot == 0)) {
- pushheap();
- /* search path for script */
- for (t = path; *t; t++) {
- if (!(*t)[0] || ((*t)[0] == '.' && !(*t)[1])) {
- if (diddot)
- continue;
- diddot = 1;
- buf = dupstring(arg0);
- } else
- buf = zhtricat(*t, "/", arg0);
-
- s = unmeta(buf);
- if (access(s, F_OK) == 0 && stat(s, &st) >= 0
- && !S_ISDIR(st.st_mode)) {
- ret = source(enam = buf);
- break;
- }
- }
- popheap();
- }
- }
- /* clean up and return */
- if (argv[1]) {
- freearray(pparams);
- pparams = old;
- }
- if (ret == SOURCE_NOT_FOUND) {
- if (isset(POSIXBUILTINS)) {
- /* hard error in POSIX (we'll exit later) */
- zerrnam(name, "%e: %s", errno, enam);
- } else {
- zwarnnam(name, "%e: %s", errno, enam);
- }
- }
- zsfree(arg0);
- if (old0) {
- zsfree(argzero);
- argzero = old0;
- }
- return ret == SOURCE_OK ? lastval : 128 - ret;
-}
-
-/*
- * common for bin_emulate and bin_eval
- */
-
-static int
-eval(char **argv)
-{
- Eprog prog;
- char *oscriptname = scriptname;
- int oineval = ineval, fpushed;
- struct funcstack fstack;
-
- /*
- * If EVALLINENO is not set, we use the line number of the
- * environment and must flag this up to exec.c. Otherwise,
- * we use a special script name to indicate the special line number.
- */
- ineval = !isset(EVALLINENO);
- if (!ineval) {
- scriptname = "(eval)";
- fstack.prev = funcstack;
- fstack.name = scriptname;
- fstack.caller = funcstack ? funcstack->name : dupstring(argzero);
- fstack.lineno = lineno;
- fstack.tp = FS_EVAL;
-
- /*
- * To get file line numbers, we need to know if parent is
- * the original script/shell or a sourced file, in which
- * case we use the line number raw, or a function or eval,
- * in which case we need to deduce where that came from.
- *
- * This replicates the logic for working out the information
- * for $funcfiletrace---eval is similar to an inlined function
- * call from a tracing perspective.
- */
- if (!funcstack || funcstack->tp == FS_SOURCE) {
- fstack.flineno = fstack.lineno;
- fstack.filename = fstack.caller;
- } else {
- fstack.flineno = funcstack->flineno + lineno;
- /*
- * Line numbers in eval start from 1, not zero,
- * so offset by one to get line in file.
- */
- if (funcstack->tp == FS_EVAL)
- fstack.flineno--;
- fstack.filename = funcstack->filename;
- if (!fstack.filename)
- fstack.filename = "";
- }
- funcstack = &fstack;
-
- fpushed = 1;
- } else
- fpushed = 0;
-
- prog = parse_string(zjoin(argv, ' ', 1), 1);
- if (prog) {
- if (wc_code(*prog->prog) != WC_LIST) {
- /* No code to execute */
- lastval = 0;
- } else {
- execode(prog, 1, 0, "eval");
-
- if (errflag && !lastval)
- lastval = errflag;
- }
- } else {
- lastval = 1;
- }
-
- if (fpushed)
- funcstack = funcstack->prev;
-
- errflag &= ~ERRFLAG_ERROR;
- scriptname = oscriptname;
- ineval = oineval;
-
- return lastval;
-}
-
-/* emulate: set emulation mode and optionally evaluate shell code */
-
-/**/
-int
-bin_emulate(char *nam, char **argv, Options ops, UNUSED(int func))
-{
- int opt_L = OPT_ISSET(ops, 'L');
- int opt_R = OPT_ISSET(ops, 'R');
- int opt_l = OPT_ISSET(ops, 'l');
- int saveemulation, savehackchar;
- int ret = 1, new_emulation;
- unsigned int savepatterns;
- char saveopts[OPT_SIZE], new_opts[OPT_SIZE];
- char *cmd = 0;
- const char *shname = *argv;
- LinkList optlist;
- LinkNode optnode;
- Emulation_options save_sticky;
- OptIndex *on_ptr, *off_ptr;
-
- /* without arguments just print current emulation */
- if (!shname) {
- if (opt_L || opt_R) {
- zwarnnam(nam, "not enough arguments");
- return 1;
- }
-
- switch(SHELL_EMULATION()) {
- case EMULATE_CSH:
- shname = "csh";
- break;
-
- case EMULATE_KSH:
- shname = "ksh";
- break;
-
- case EMULATE_SH:
- shname = "sh";
- break;
-
- default:
- shname = "zsh";
- break;
- }
-
- printf("%s\n", shname);
- return 0;
- }
-
- /* with single argument set current emulation */
- if (!argv[1]) {
- char *cmdopts;
- if (opt_l) {
- cmdopts = (char *)zhalloc(OPT_SIZE);
- memcpy(cmdopts, opts, OPT_SIZE);
- } else
- cmdopts = opts;
- emulate(shname, opt_R, &emulation, cmdopts);
- if (opt_L)
- cmdopts[LOCALOPTIONS] = cmdopts[LOCALTRAPS] =
- cmdopts[LOCALPATTERNS] = 1;
- if (opt_l) {
- list_emulate_options(cmdopts, opt_R);
- return 0;
- }
- clearpatterndisables();
- return 0;
- }
-
- if (opt_l) {
- zwarnnam(nam, "too many arguments for -l");
- return 1;
- }
-
- argv++;
- memcpy(saveopts, opts, sizeof(opts));
- memcpy(new_opts, opts, sizeof(opts));
- savehackchar = keyboardhackchar;
- emulate(shname, opt_R, &new_emulation, new_opts);
- optlist = newlinklist();
- if (parseopts(nam, &argv, new_opts, &cmd, optlist, 0)) {
- ret = 1;
- goto restore;
- }
-
- /* parseopts() has consumed anything that looks like an option */
- if (*argv) {
- zwarnnam(nam, "unknown argument %s", *argv);
- goto restore;
- }
-
- savepatterns = savepatterndisables();
- /*
- * All emulations start with an empty set of pattern disables,
- * hence no special "sticky" behaviour is required.
- */
- clearpatterndisables();
-
- saveemulation = emulation;
- emulation = new_emulation;
- memcpy(opts, new_opts, sizeof(opts));
- /* If "-c command" is given, evaluate command using specified
- * emulation mode.
- */
- if (cmd) {
- if (opt_L) {
- zwarnnam(nam, "option -L incompatible with -c");
- goto restore2;
- }
- *--argv = cmd; /* on stack, never free()d, see execbuiltin() */
- } else {
- if (opt_L)
- opts[LOCALOPTIONS] = opts[LOCALTRAPS] = opts[LOCALPATTERNS] = 1;
- return 0;
- }
-
- save_sticky = sticky;
- sticky = hcalloc(sizeof(*sticky));
- sticky->emulation = emulation;
- for (optnode = firstnode(optlist); optnode; incnode(optnode)) {
- /* Data is index into new_opts */
- char *optptr = (char *)getdata(optnode);
- if (*optptr)
- sticky->n_on_opts++;
- else
- sticky->n_off_opts++;
- }
- if (sticky->n_on_opts)
- on_ptr = sticky->on_opts =
- zhalloc(sticky->n_on_opts * sizeof(*sticky->on_opts));
- else
- on_ptr = NULL;
- if (sticky->n_off_opts)
- off_ptr = sticky->off_opts = zhalloc(sticky->n_off_opts *
- sizeof(*sticky->off_opts));
- else
- off_ptr = NULL;
- for (optnode = firstnode(optlist); optnode; incnode(optnode)) {
- /* Data is index into new_opts */
- char *optptr = (char *)getdata(optnode);
- int optno = optptr - new_opts;
- if (*optptr)
- *on_ptr++ = optno;
- else
- *off_ptr++ = optno;
- }
- ret = eval(argv);
- sticky = save_sticky;
-restore2:
- emulation = saveemulation;
- memcpy(opts, saveopts, sizeof(opts));
- restorepatterndisables(savepatterns);
-restore:
- keyboardhackchar = savehackchar;
- inittyptab(); /* restore banghist */
- return ret;
-}
-
-/* eval: simple evaluation */
-
-/**/
-mod_export int ineval;
-
-/**/
-int
-bin_eval(UNUSED(char *nam), char **argv, UNUSED(Options ops), UNUSED(int func))
-{
- return eval(argv);
-}
-
-static char *zbuf;
-static int readfd;
-
-/* Read a character from readfd, or from the buffer zbuf. Return EOF on end of
-file/buffer. */
-
-/* read: get a line of input, or (for compctl functions) return some *
- * useful data about the state of the editing line. The -E and -e *
- * options mean that the result should be sent to stdout. -e means, *
- * in addition, that the result should not actually be assigned to *
- * the specified parameters. */
-
-/**/
-int
-bin_read(char *name, char **args, Options ops, UNUSED(int func))
-{
- char *reply, *readpmpt;
- int bsiz, c = 0, gotnl = 0, al = 0, first, nchars = 1, bslash, keys = 0;
- int haso = 0; /* true if /dev/tty has been opened specially */
- int isem = !strcmp(term, "emacs"), izle = zleactive;
- char *buf, *bptr, *firstarg, *zbuforig;
- LinkList readll = newlinklist();
- FILE *oshout = NULL;
- int readchar = -1, val, resettty = 0;
- struct ttyinfo saveti;
- char d;
- long izle_timeout = 0;
-#ifdef MULTIBYTE_SUPPORT
- wchar_t delim = L'\n', wc;
- mbstate_t mbs;
- char *laststart;
- size_t ret;
-#else
- char delim = '\n';
-#endif
-
- if (OPT_HASARG(ops,c='k')) {
- char *eptr, *optarg = OPT_ARG(ops,c);
- nchars = (int)zstrtol(optarg, &eptr, 10);
- if (*eptr) {
- zwarnnam(name, "number expected after -%c: %s", c, optarg);
- return 1;
- }
- }
- /* This `*args++ : *args' looks a bit weird, but it works around a bug
- * in gcc-2.8.1 under DU 4.0. */
- firstarg = (*args && **args == '?' ? *args++ : *args);
- reply = *args ? *args++ : OPT_ISSET(ops,'A') ? "reply" : "REPLY";
-
- if (OPT_ISSET(ops,'A') && *args) {
- zwarnnam(name, "only one array argument allowed");
- return 1;
- }
-
- /* handle compctl case */
- if(OPT_ISSET(ops,'l') || OPT_ISSET(ops,'c'))
- return compctlreadptr(name, args, ops, reply);
-
- if ((OPT_ISSET(ops,'k') || OPT_ISSET(ops,'q')) &&
- !OPT_ISSET(ops,'u') && !OPT_ISSET(ops,'p')) {
- if (!zleactive) {
- if (SHTTY == -1) {
- /* need to open /dev/tty specially */
- if ((SHTTY = open("/dev/tty", O_RDWR|O_NOCTTY)) != -1) {
- haso = 1;
- oshout = shout;
- init_shout();
- }
- } else if (!shout) {
- /* We need an output FILE* on the tty */
- init_shout();
- }
- /* We should have a SHTTY opened by now. */
- if (SHTTY == -1) {
- /* Unfortunately, we didn't. */
- fprintf(stderr, "not interactive and can't open terminal\n");
- fflush(stderr);
- return 1;
- }
- if (unset(INTERACTIVE))
- gettyinfo(&shttyinfo);
- /* attach to the tty */
- attachtty(mypgrp);
- if (!isem)
- setcbreak();
- readfd = SHTTY;
- }
- keys = 1;
- } else if (OPT_HASARG(ops,'u') && !OPT_ISSET(ops,'p')) {
- /* -u means take input from the specified file descriptor. */
- char *eptr, *argptr = OPT_ARG(ops,'u');
- /* The old code handled -up, but that was never documented. Still...*/
- if (!strcmp(argptr, "p")) {
- readfd = coprocin;
- if (readfd < 0) {
- zwarnnam(name, "-p: no coprocess");
- return 1;
- }
- } else {
- readfd = (int)zstrtol(argptr, &eptr, 10);
- if (*eptr) {
- zwarnnam(name, "number expected after -%c: %s", 'u', argptr);
- return 1;
- }
- }
-#if 0
- /* This code is left as a warning to future generations --- pws. */
- for (readfd = 9; readfd && !OPT_ISSET(ops,readfd + '0'); --readfd);
-#endif
- izle = 0;
- } else if (OPT_ISSET(ops,'p')) {
- readfd = coprocin;
- if (readfd < 0) {
- zwarnnam(name, "-p: no coprocess");
- return 1;
- }
- izle = 0;
- } else
- readfd = izle = 0;
-
- if (OPT_ISSET(ops,'s') && SHTTY != -1) {
- struct ttyinfo ti;
- gettyinfo(&ti);
- saveti = ti;
- resettty = 1;
-#ifdef HAS_TIO
- ti.tio.c_lflag &= ~ECHO;
-#else
- ti.sgttyb.sg_flags &= ~ECHO;
-#endif
- settyinfo(&ti);
- }
-
- /* handle prompt */
- if (firstarg) {
- for (readpmpt = firstarg;
- *readpmpt && *readpmpt != '?'; readpmpt++);
- if (*readpmpt++) {
- if (keys || isatty(0)) {
- zputs(readpmpt, (shout ? shout : stderr));
- fflush(shout ? shout : stderr);
- }
- readpmpt[-1] = '\0';
- }
- }
-
- if (OPT_ISSET(ops,'d')) {
- char *delimstr = OPT_ARG(ops,'d');
-#ifdef MULTIBYTE_SUPPORT
- wint_t wi;
-
- if (isset(MULTIBYTE)) {
- mb_charinit();
- (void)mb_metacharlenconv(delimstr, &wi);
- }
- else
- wi = WEOF;
- if (wi != WEOF)
- delim = (wchar_t)wi;
- else
- delim = (wchar_t)((delimstr[0] == Meta) ?
- delimstr[1] ^ 32 : delimstr[0]);
-#else
- delim = (delimstr[0] == Meta) ? delimstr[1] ^ 32 : delimstr[0];
-#endif
- if (SHTTY != -1) {
- struct ttyinfo ti;
- gettyinfo(&ti);
- if (! resettty) {
- saveti = ti;
- resettty = 1;
- }
-#ifdef HAS_TIO
- ti.tio.c_lflag &= ~ICANON;
- ti.tio.c_cc[VMIN] = 1;
- ti.tio.c_cc[VTIME] = 0;
-#else
- ti.sgttyb.sg_flags |= CBREAK;
-#endif
- settyinfo(&ti);
- }
- }
- if (OPT_ISSET(ops,'t')) {
- zlong timeout = 0;
- if (OPT_HASARG(ops,'t')) {
- mnumber mn = zero_mnumber;
- mn = matheval(OPT_ARG(ops,'t'));
- if (errflag)
- return 1;
- if (mn.type == MN_FLOAT) {
- mn.u.d *= 1e6;
- timeout = (zlong)mn.u.d;
- } else {
- timeout = (zlong)mn.u.l * (zlong)1000000;
- }
- }
- if (izle) {
- /*
- * Timeout is in 100ths of a second rather than us.
- * See calc_timeout() in zle_main for format of this.
- */
- timeout = -(timeout/(zlong)10000 + 1L);
- izle_timeout = (long)timeout;
-#ifdef LONG_MAX
- /* saturate if range exceeded */
- if ((zlong)izle_timeout != timeout)
- izle_timeout = LONG_MAX;
-#endif
- } else {
- if (readfd == -1 ||
- !read_poll(readfd, &readchar, keys && !zleactive,
- timeout)) {
- if (keys && !zleactive && !isem)
- settyinfo(&shttyinfo);
- else if (resettty && SHTTY != -1)
- settyinfo(&saveti);
- if (haso) {
- fclose(shout);
- shout = oshout;
- SHTTY = -1;
- }
- return OPT_ISSET(ops,'q') ? 2 : 1;
- }
- }
- }
-
-#ifdef MULTIBYTE_SUPPORT
- memset(&mbs, 0, sizeof(mbs));
-#endif
-
- /*
- * option -k means read only a given number of characters (default 1)
- * option -q means get one character, and interpret it as a Y or N
- */
- if (OPT_ISSET(ops,'k') || OPT_ISSET(ops,'q')) {
- int eof = 0;
- /* allocate buffer space for result */
-#ifdef MULTIBYTE_SUPPORT
- bptr = buf = (char *)zalloc(nchars*MB_CUR_MAX+1);
-#else
- bptr = buf = (char *)zalloc(nchars+1);
-#endif
-
- do {
- if (izle) {
- zleentry(ZLE_CMD_GET_KEY, izle_timeout, NULL, &val);
- if (val < 0) {
- eof = 1;
- break;
- }
- *bptr = (char) val;
-#ifdef MULTIBYTE_SUPPORT
- if (isset(MULTIBYTE)) {
- ret = mbrlen(bptr++, 1, &mbs);
- if (ret == MB_INVALID)
- memset(&mbs, 0, sizeof(mbs));
- /* treat invalid as single character */
- if (ret != MB_INCOMPLETE)
- nchars--;
- continue;
- } else {
- bptr++;
- nchars--;
- }
-#else
- bptr++;
- nchars--;
-#endif
- } else {
- /* If read returns 0, is end of file */
- if (readchar >= 0) {
- *bptr = readchar;
- val = 1;
- readchar = -1;
- } else {
- while ((val = read(readfd, bptr, nchars)) < 0) {
- if (errno != EINTR ||
- errflag || retflag || breaks || contflag)
- break;
- }
- if (val <= 0) {
- eof = 1;
- break;
- }
- }
-
-#ifdef MULTIBYTE_SUPPORT
- if (isset(MULTIBYTE)) {
- while (val > 0) {
- ret = mbrlen(bptr, val, &mbs);
- if (ret == MB_INCOMPLETE) {
- bptr += val;
- break;
- } else {
- if (ret == MB_INVALID) {
- memset(&mbs, 0, sizeof(mbs));
- /* treat as single byte */
- ret = 1;
- }
- else if (ret == 0) /* handle null as normal char */
- ret = 1;
- else if (ret > (size_t)val) {
- /* Some mbrlen()s return the full char len */
- ret = val;
- }
- nchars--;
- val -= ret;
- bptr += ret;
- }
- }
- continue;
- }
-#endif
- /* decrement number of characters read from number required */
- nchars -= val;
-
- /* increment pointer past read characters */
- bptr += val;
- }
- } while (nchars > 0);
-
- if (!izle && !OPT_ISSET(ops,'u') && !OPT_ISSET(ops,'p')) {
- /* dispose of result appropriately, etc. */
- if (isem)
- while (val > 0 && read(SHTTY, &d, 1) == 1 && d != '\n');
- else {
- settyinfo(&shttyinfo);
- resettty = 0;
- }
- if (haso) {
- fclose(shout); /* close(SHTTY) */
- shout = oshout;
- SHTTY = -1;
- }
- }
-
- if (OPT_ISSET(ops,'q'))
- {
- /*
- * Keep eof as status but status is now whether we read
- * 'y' or 'Y'. If we timed out, status is 2.
- */
- if (eof)
- eof = 2;
- else
- eof = (bptr - buf != 1 || (buf[0] != 'y' && buf[0] != 'Y'));
- buf[0] = eof ? 'n' : 'y';
- bptr = buf + 1;
- }
- if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E'))
- fwrite(buf, bptr - buf, 1, stdout);
- if (!OPT_ISSET(ops,'e'))
- setsparam(reply, metafy(buf, bptr - buf, META_REALLOC));
- else
- zfree(buf, bptr - buf + 1);
- if (resettty && SHTTY != -1)
- settyinfo(&saveti);
- return eof;
- }
-
- /* All possible special types of input have been exhausted. Take one line,
- and assign words to the parameters until they run out. Leftover words go
- onto the last parameter. If an array is specified, all the words become
- separate elements of the array. */
-
- zbuforig = zbuf = (!OPT_ISSET(ops,'z')) ? NULL :
- (nonempty(bufstack)) ? (char *) getlinknode(bufstack) : ztrdup("");
- first = 1;
- bslash = 0;
- while (*args || (OPT_ISSET(ops,'A') && !gotnl)) {
- sigset_t s = child_unblock();
- buf = bptr = (char *)zalloc(bsiz = 64);
-#ifdef MULTIBYTE_SUPPORT
- laststart = buf;
- ret = MB_INCOMPLETE;
-#endif
- /* get input, a character at a time */
- while (!gotnl) {
- c = zread(izle, &readchar, izle_timeout);
- /* \ at the end of a line indicates a continuation *
- * line, except in raw mode (-r option) */
-#ifdef MULTIBYTE_SUPPORT
- if (c == EOF) {
- /* not waiting to be completed any more */
- ret = 0;
- break;
- }
- *bptr = (char)c;
- if (isset(MULTIBYTE)) {
- ret = mbrtowc(&wc, bptr, 1, &mbs);
- if (!ret) /* NULL */
- ret = 1;
- } else {
- ret = 1;
- wc = (wchar_t)c;
- }
- if (ret != MB_INCOMPLETE) {
- if (ret == MB_INVALID) {
- memset(&mbs, 0, sizeof(mbs));
- /* Treat this as a single character */
- wc = (wchar_t)c;
- laststart = bptr;
- }
- if (bslash && wc == delim) {
- bslash = 0;
- continue;
- }
- if (wc == delim)
- break;
- /*
- * `first' is non-zero if any separator we encounter is a
- * non-whitespace separator, which means that anything
- * (even an empty string) between, before or after separators
- * is significant. If it is zero, we have a whitespace
- * separator, which shouldn't cause extra empty strings to
- * be emitted. Hence the test for (*buf || first) when
- * we assign the result of reading a word.
- */
- if (!bslash && wcsitype(wc, ISEP)) {
- if (bptr != buf ||
- (!(c < 128 && iwsep(c)) && first)) {
- first |= !(c < 128 && iwsep(c));
- break;
- }
- first |= !(c < 128 && iwsep(c));
- continue;
- }
- bslash = (wc == L'\\' && !bslash && !OPT_ISSET(ops,'r'));
- if (bslash)
- continue;
- first = 0;
- }
- if (imeta(STOUC(*bptr))) {
- bptr[1] = bptr[0] ^ 32;
- bptr[0] = Meta;
- bptr += 2;
- }
- else
- bptr++;
- if (ret != MB_INCOMPLETE)
- laststart = bptr;
-#else
- if (c == EOF)
- break;
- if (bslash && c == delim) {
- bslash = 0;
- continue;
- }
- if (c == delim)
- break;
- /*
- * `first' is non-zero if any separator we encounter is a
- * non-whitespace separator, which means that anything
- * (even an empty string) between, before or after separators
- * is significant. If it is zero, we have a whitespace
- * separator, which shouldn't cause extra empty strings to
- * be emitted. Hence the test for (*buf || first) when
- * we assign the result of reading a word.
- */
- if (!bslash && isep(c)) {
- if (bptr != buf || (!iwsep(c) && first)) {
- first |= !iwsep(c);
- break;
- }
- first |= !iwsep(c);
- continue;
- }
- bslash = c == '\\' && !bslash && !OPT_ISSET(ops,'r');
- if (bslash)
- continue;
- first = 0;
- if (imeta(c)) {
- *bptr++ = Meta;
- *bptr++ = c ^ 32;
- } else
- *bptr++ = c;
-#endif
- /* increase the buffer size, if necessary */
- if (bptr >= buf + bsiz - 1) {
- int blen = bptr - buf;
-#ifdef MULTIBYTE_SUPPORT
- int llen = laststart - buf;
-#endif
-
- buf = realloc(buf, bsiz *= 2);
- bptr = buf + blen;
-#ifdef MULTIBYTE_SUPPORT
- laststart = buf + llen;
-#endif
- }
- }
- signal_setmask(s);
-#ifdef MULTIBYTE_SUPPORT
- if (c == EOF) {
- gotnl = 1;
- *bptr = '\0'; /* see below */
- } else if (ret == MB_INCOMPLETE) {
- /*
- * We can only get here if there is an EOF in the
- * middle of a character... safest to keep the debris,
- * I suppose.
- */
- *bptr = '\0';
- } else {
- if (wc == delim)
- gotnl = 1;
- *laststart = '\0';
- }
-#else
- if (c == delim || c == EOF)
- gotnl = 1;
- *bptr = '\0';
-#endif
- /* dispose of word appropriately */
- if (OPT_ISSET(ops,'e') ||
- /*
- * When we're doing an array assignment, we'll
- * handle echoing at that point. In all other
- * cases (including -A with no assignment)
- * we'll do it here.
- */
- (OPT_ISSET(ops,'E') && !OPT_ISSET(ops,'A'))) {
- zputs(buf, stdout);
- putchar('\n');
- }
- if (!OPT_ISSET(ops,'e') && (*buf || first || gotnl)) {
- if (OPT_ISSET(ops,'A')) {
- addlinknode(readll, buf);
- al++;
- } else
- setsparam(reply, buf);
- } else
- free(buf);
- if (!OPT_ISSET(ops,'A'))
- reply = *args++;
- }
- /* handle EOF */
- if (c == EOF) {
- if (readfd == coprocin) {
- close(coprocin);
- close(coprocout);
- coprocin = coprocout = -1;
- }
- }
- /* final assignment (and display) of array parameter */
- if (OPT_ISSET(ops,'A')) {
- char **pp, **p = NULL;
- LinkNode n;
-
- p = (OPT_ISSET(ops,'e') ? (char **)NULL
- : (char **)zalloc((al + 1) * sizeof(char *)));
-
- for (pp = p, n = firstnode(readll); n; incnode(n)) {
- if (OPT_ISSET(ops,'E')) {
- zputs((char *) getdata(n), stdout);
- putchar('\n');
- }
- if (p)
- *pp++ = (char *)getdata(n);
- else
- zsfree(getdata(n));
- }
- if (p) {
- *pp++ = NULL;
- setaparam(reply, p);
- }
- if (resettty && SHTTY != -1)
- settyinfo(&saveti);
- return c == EOF;
- }
- buf = bptr = (char *)zalloc(bsiz = 64);
-#ifdef MULTIBYTE_SUPPORT
- laststart = buf;
- ret = MB_INCOMPLETE;
-#endif
- /* any remaining part of the line goes into one parameter */
- bslash = 0;
- if (!gotnl) {
- sigset_t s = child_unblock();
- for (;;) {
- c = zread(izle, &readchar, izle_timeout);
-#ifdef MULTIBYTE_SUPPORT
- if (c == EOF) {
- /* not waiting to be completed any more */
- ret = 0;
- break;
- }
- *bptr = (char)c;
- if (isset(MULTIBYTE)) {
- ret = mbrtowc(&wc, bptr, 1, &mbs);
- if (!ret) /* NULL */
- ret = 1;
- } else {
- ret = 1;
- wc = (wchar_t)c;
- }
- if (ret != MB_INCOMPLETE) {
- if (ret == MB_INVALID) {
- memset(&mbs, 0, sizeof(mbs));
- /* Treat this as a single character */
- wc = (wchar_t)c;
- laststart = bptr;
- }
- /*
- * \ at the end of a line introduces a continuation line,
- * except in raw mode (-r option)
- */
- if (bslash && wc == delim) {
- bslash = 0;
- continue;
- }
- if (wc == delim && !zbuf)
- break;
- if (!bslash && bptr == buf && wcsitype(wc, ISEP)) {
- if (c < 128 && iwsep(c))
- continue;
- else if (!first) {
- first = 1;
- continue;
- }
- }
- bslash = (wc == L'\\' && !bslash && !OPT_ISSET(ops,'r'));
- if (bslash)
- continue;
- }
- if (imeta(STOUC(*bptr))) {
- bptr[1] = bptr[0] ^ 32;
- bptr[0] = Meta;
- bptr += 2;
- }
- else
- bptr++;
- if (ret != MB_INCOMPLETE)
- laststart = bptr;
-#else
- /* \ at the end of a line introduces a continuation line, except in
- raw mode (-r option) */
- if (bslash && c == delim) {
- bslash = 0;
- continue;
- }
- if (c == EOF || (c == delim && !zbuf))
- break;
- if (!bslash && isep(c) && bptr == buf) {
- if (iwsep(c))
- continue;
- else if (!first) {
- first = 1;
- continue;
- }
- }
- bslash = c == '\\' && !bslash && !OPT_ISSET(ops,'r');
- if (bslash)
- continue;
- if (imeta(c)) {
- *bptr++ = Meta;
- *bptr++ = c ^ 32;
- } else
- *bptr++ = c;
-#endif
- /* increase the buffer size, if necessary */
- if (bptr >= buf + bsiz - 1) {
- int blen = bptr - buf;
-#ifdef MULTIBYTE_SUPPORT
- int llen = laststart - buf;
-#endif
-
- buf = realloc(buf, bsiz *= 2);
- bptr = buf + blen;
-#ifdef MULTIBYTE_SUPPORT
- laststart = buf + llen;
-#endif
- }
- }
- signal_setmask(s);
- }
-#ifdef MULTIBYTE_SUPPORT
- if (ret != MB_INCOMPLETE)
- bptr = laststart;
-#endif
- /*
- * Strip trailing IFS whitespace.
- * iwsep can only be certain single-byte ASCII bytes, but we
- * must check the byte isn't metafied.
- */
- while (bptr > buf) {
- if (bptr > buf + 1 && bptr[-2] == Meta) {
- /* non-ASCII, can't be IWSEP */
- break;
- } else if (iwsep(bptr[-1]))
- bptr--;
- else
- break;
- }
- *bptr = '\0';
- if (resettty && SHTTY != -1)
- settyinfo(&saveti);
- /* final assignment of reply, etc. */
- if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E')) {
- zputs(buf, stdout);
- putchar('\n');
- }
- if (!OPT_ISSET(ops,'e'))
- setsparam(reply, buf);
- else
- zsfree(buf);
- if (zbuforig) {
- char first = *zbuforig;
-
- zsfree(zbuforig);
- if (!first)
- return 1;
- } else if (c == EOF) {
- if (readfd == coprocin) {
- close(coprocin);
- close(coprocout);
- coprocin = coprocout = -1;
- }
- return 1;
- }
- /*
- * The following is to ensure a failure to set the parameter
- * causes a non-zero status return. There are arguments for
- * turning a non-zero status into errflag more widely.
- */
- return errflag;
-}
-
-/**/
-static int
-zread(int izle, int *readchar, long izle_timeout)
-{
- char cc, retry = 0;
- int ret;
-
- if (izle) {
- int c;
- zleentry(ZLE_CMD_GET_KEY, izle_timeout, NULL, &c);
-
- return (c < 0 ? EOF : c);
- }
- /* use zbuf if possible */
- if (zbuf) {
- /* If zbuf points to anything, it points to the next character in the
- buffer. This may be a null byte to indicate EOF. If reading from the
- buffer, move on the buffer pointer. */
- if (*zbuf == Meta)
- return zbuf++, STOUC(*zbuf++ ^ 32);
- else
- return (*zbuf) ? STOUC(*zbuf++) : EOF;
- }
- if (*readchar >= 0) {
- cc = *readchar;
- *readchar = -1;
- return STOUC(cc);
- }
- for (;;) {
- /* read a character from readfd */
- ret = read(readfd, &cc, 1);
- switch (ret) {
- case 1:
- /* return the character read */
- return STOUC(cc);
- case -1:
-#if defined(EAGAIN) || defined(EWOULDBLOCK)
- if (!retry && readfd == 0 && (
-# ifdef EAGAIN
- errno == EAGAIN
-# ifdef EWOULDBLOCK
- ||
-# endif /* EWOULDBLOCK */
-# endif /* EAGAIN */
-# ifdef EWOULDBLOCK
- errno == EWOULDBLOCK
-# endif /* EWOULDBLOCK */
- ) && setblock_stdin()) {
- retry = 1;
- continue;
- } else
-#endif /* EAGAIN || EWOULDBLOCK */
- if (errno == EINTR && !(errflag || retflag || breaks || contflag))
- continue;
- break;
- }
- return EOF;
- }
-}
-
-/* holds arguments for testlex() */
-/**/
-char **testargs, **curtestarg;
-
-/* test, [: the old-style general purpose logical expression builtin */
-
-/**/
-void
-testlex(void)
-{
- if (tok == LEXERR)
- return;
-
- tokstr = *(curtestarg = testargs);
- if (!*testargs) {
- /* if tok is already zero, reading past the end: error */
- tok = tok ? NULLTOK : LEXERR;
- return;
- } else if (!strcmp(*testargs, "-o"))
- tok = DBAR;
- else if (!strcmp(*testargs, "-a"))
- tok = DAMPER;
- else if (!strcmp(*testargs, "!"))
- tok = BANG;
- else if (!strcmp(*testargs, "("))
- tok = INPAR;
- else if (!strcmp(*testargs, ")"))
- tok = OUTPAR;
- else
- tok = STRING;
- testargs++;
-}
-
-/**/
-int
-bin_test(char *name, char **argv, UNUSED(Options ops), int func)
-{
- char **s;
- Eprog prog;
- struct estate state;
- int nargs, sense = 0, ret;
-
- /* if "test" was invoked as "[", it needs a matching "]" *
- * which is subsequently ignored */
- if (func == BIN_BRACKET) {
- for (s = argv; *s; s++);
- if (s == argv || strcmp(s[-1], "]")) {
- zwarnnam(name, "']' expected");
- return 2;
- }
- s[-1] = NULL;
- }
- /* an empty argument list evaluates to false (1) */
- if (!*argv)
- return 1;
-
- /*
- * Implement some XSI extensions to POSIX here.
- * See
- * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
- */
- nargs = arrlen(argv);
- if (nargs == 3 || nargs == 4)
- {
- /*
- * As parentheses are an extension, we need to be careful ---
- * if this is a three-argument expression that could
- * be a binary operator, prefer that.
- */
- if (!strcmp(argv[0], "(") && !strcmp(argv[nargs-1],")") &&
- (nargs != 3 || !is_cond_binary_op(argv[1]))) {
- argv[nargs-1] = NULL;
- argv++;
- }
- if (nargs == 4 && !strcmp("!", argv[0])) {
- sense = 1;
- argv++;
- }
- }
-
- zcontext_save();
- testargs = argv;
- tok = NULLTOK;
- condlex = testlex;
- testlex();
- prog = parse_cond();
- condlex = zshlex;
-
- if (errflag) {
- errflag &= ~ERRFLAG_ERROR;
- zcontext_restore();
- return 2;
- }
-
- if (!prog || tok == LEXERR) {
- zwarnnam(name, tokstr ? "parse error" : "argument expected");
- zcontext_restore();
- return 2;
- }
- zcontext_restore();
-
- if (*curtestarg) {
- zwarnnam(name, "too many arguments");
- return 2;
- }
-
- /* syntax is OK, so evaluate */
-
- state.prog = prog;
- state.pc = prog->prog;
- state.strs = prog->strs;
-
- ret = evalcond(&state, name);
- if (ret < 2 && sense)
- ret = ! ret;
-
- return ret;
-}
-
-/* display a time, provided in units of 1/60s, as minutes and seconds */
-#define pttime(X) printf("%ldm%ld.%02lds",((long) (X))/(60 * clktck),\
- ((long) (X))/clktck%clktck,\
- ((long) (X))*100/clktck%100)
-
-/* times: display, in a two-line format, the times provided by times(3) */
-
-/**/
-int
-bin_times(UNUSED(char *name), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func))
-{
- struct tms buf;
- long clktck = get_clktck();
-
- /* get time accounting information */
- if (times(&buf) == -1)
- return 1;
- pttime(buf.tms_utime); /* user time */
- putchar(' ');
- pttime(buf.tms_stime); /* system time */
- putchar('\n');
- pttime(buf.tms_cutime); /* user time, children */
- putchar(' ');
- pttime(buf.tms_cstime); /* system time, children */
- putchar('\n');
- return 0;
-}
-
-/* trap: set/unset signal traps */
-
-/**/
-int
-bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
-{
- Eprog prog;
- char *arg, *s;
- int sig;
-
- if (*argv && !strcmp(*argv, "--"))
- argv++;
-
- /* If given no arguments, list all currently-set traps */
- if (!*argv) {
- queue_signals();
- for (sig = 0; sig < VSIGCOUNT; sig++) {
- if (sigtrapped[sig] & ZSIG_FUNC) {
- HashNode hn;
-
- if ((hn = gettrapnode(sig, 0)))
- shfunctab->printnode(hn, 0);
- DPUTS(!hn, "BUG: I did not find any trap functions!");
- } else if (sigtrapped[sig]) {
- const char *name = getsigname(sig);
- if (!siglists[sig])
- printf("trap -- '' %s\n", name);
- else {
- s = getpermtext(siglists[sig], NULL, 0);
- printf("trap -- ");
- quotedzputs(s, stdout);
- printf(" %s\n", name);
- zsfree(s);
- }
- }
- }
- unqueue_signals();
- return 0;
- }
-
- /* If we have a signal number, unset the specified *
- * signals. With only -, remove all traps. */
- if ((getsignum(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) {
- if (!*argv) {
- for (sig = 0; sig < VSIGCOUNT; sig++)
- unsettrap(sig);
- } else {
- for (; *argv; argv++) {
- sig = getsignum(*argv);
- if (sig == -1) {
- zwarnnam(name, "undefined signal: %s", *argv);
- break;
- }
- unsettrap(sig);
- }
- }
- return *argv != NULL;
- }
-
- /* Sort out the command to execute on trap */
- arg = *argv++;
- if (!*arg)
- prog = &dummy_eprog;
- else if (!(prog = parse_string(arg, 1))) {
- zwarnnam(name, "couldn't parse trap command");
- return 1;
- }
-
- /* set traps */
- for (; *argv; argv++) {
- Eprog t;
- int flags;
-
- sig = getsignum(*argv);
- if (sig == -1) {
- zwarnnam(name, "undefined signal: %s", *argv);
- break;
- }
- if (idigit(**argv) ||
- !strcmp(sigs[sig], *argv) ||
- (!strncmp("SIG", *argv, 3) && !strcmp(sigs[sig], *argv+3))) {
- /* The signal was specified by number or by canonical name (with
- * or without SIG prefix).
- */
- flags = 0;
- }
- else {
- /*
- * Record that the signal is used under an assumed name.
- * If we ever have more than one alias per signal this
- * will need improving.
- */
- flags = ZSIG_ALIAS;
- }
- t = dupeprog(prog, 0);
- if (settrap(sig, t, flags))
- freeeprog(t);
- }
- return *argv != NULL;
-}
-
-/**/
-int
-bin_ttyctl(UNUSED(char *name), UNUSED(char **argv), Options ops, UNUSED(int func))
-{
- if (OPT_ISSET(ops,'f'))
- ttyfrozen = 1;
- else if (OPT_ISSET(ops,'u'))
- ttyfrozen = 0;
- else
- printf("tty is %sfrozen\n", ttyfrozen ? "" : "not ");
- return 0;
-}
-
-/* let -- mathematical evaluation */
-
-/**/
-int
-bin_let(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int func))
-{
- mnumber val = zero_mnumber;
-
- while (*argv)
- val = matheval(*argv++);
- /* Errors in math evaluation in let are non-fatal. */
- errflag &= ~ERRFLAG_ERROR;
- /* should test for fabs(val.u.d) < epsilon? */
- return (val.type == MN_INTEGER) ? val.u.l == 0 : val.u.d == 0.0;
-}
-
-/* umask command. umask may be specified as octal digits, or in the *
- * symbolic form that chmod(1) uses. Well, a subset of it. Remember *
- * that only the bottom nine bits of umask are used, so there's no *
- * point allowing the set{u,g}id and sticky bits to be specified. */
-
-/**/
-int
-bin_umask(char *nam, char **args, Options ops, UNUSED(int func))
-{
- mode_t um;
- char *s = *args;
-
- /* Get the current umask. */
- um = umask(0);
- umask(um);
- /* No arguments means to display the current setting. */
- if (!s) {
- if (OPT_ISSET(ops,'S')) {
- char *who = "ugo";
-
- while (*who) {
- char *what = "rwx";
- printf("%c=", *who++);
- while (*what) {
- if (!(um & 0400))
- putchar(*what);
- um <<= 1;
- what++;
- }
- putchar(*who ? ',' : '\n');
- }
- } else {
- if (um & 0700)
- putchar('0');
- printf("%03o\n", (unsigned)um);
- }
- return 0;
- }
-
- if (idigit(*s)) {
- /* Simple digital umask. */
- um = zstrtol(s, &s, 8);
- if (*s) {
- zwarnnam(nam, "bad umask");
- return 1;
- }
- } else {
- /* Symbolic notation -- slightly complicated. */
- int whomask, umaskop, mask;
-
- /* More than one symbolic argument may be used at once, each separated
- by commas. */
- for (;;) {
- /* First part of the argument -- who does this apply to?
- u=owner, g=group, o=other. */
- whomask = 0;
- while (*s == 'u' || *s == 'g' || *s == 'o' || *s == 'a')
- if (*s == 'u')
- s++, whomask |= 0700;
- else if (*s == 'g')
- s++, whomask |= 0070;
- else if (*s == 'o')
- s++, whomask |= 0007;
- else if (*s == 'a')
- s++, whomask |= 0777;
- /* Default whomask is everyone. */
- if (!whomask)
- whomask = 0777;
- /* Operation may be +, - or =. */
- umaskop = (int)*s;
- if (!(umaskop == '+' || umaskop == '-' || umaskop == '=')) {
- if (umaskop)
- zwarnnam(nam, "bad symbolic mode operator: %c", umaskop);
- else
- zwarnnam(nam, "bad umask");
- return 1;
- }
- /* Permissions mask -- r=read, w=write, x=execute. */
- mask = 0;
- while (*++s && *s != ',')
- if (*s == 'r')
- mask |= 0444 & whomask;
- else if (*s == 'w')
- mask |= 0222 & whomask;
- else if (*s == 'x')
- mask |= 0111 & whomask;
- else {
- zwarnnam(nam, "bad symbolic mode permission: %c", *s);
- return 1;
- }
- /* Apply parsed argument to um. */
- if (umaskop == '+')
- um &= ~mask;
- else if (umaskop == '-')
- um |= mask;
- else /* umaskop == '=' */
- um = (um | (whomask)) & ~mask;
- if (*s == ',')
- s++;
- else
- break;
- }
- if (*s) {
- zwarnnam(nam, "bad character in symbolic mode: %c", *s);
- return 1;
- }
- }
-
- /* Finally, set the new umask. */
- umask(um);
- return 0;
-}
-
-/* Generic builtin for facilities not available on this OS */
-
-/**/
-mod_export int
-bin_notavail(char *nam, UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func))
-{
- zwarnnam(nam, "not available on this system");
- return 1;
-}