1/*
2 * builtin.c - builtin commands
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1992-1997 Paul Falstad
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Paul Falstad or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Paul Falstad and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Paul Falstad and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose.  The software
24 * provided hereunder is on an "as is" basis, and Paul Falstad and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30/* this is defined so we get the prototype for open_memstream */
31#define _GNU_SOURCE 1
32
33#include "zsh.mdh"
34#include "builtin.pro"
35
36/* Builtins in the main executable */
37
38static struct builtin builtins[] =
39{
40    BIN_PREFIX("-", BINF_DASH),
41    BIN_PREFIX("builtin", BINF_BUILTIN),
42    BIN_PREFIX("command", BINF_COMMAND),
43    BIN_PREFIX("exec", BINF_EXEC),
44    BIN_PREFIX("noglob", BINF_NOGLOB),
45    BUILTIN("[", BINF_HANDLES_OPTS, bin_test, 0, -1, BIN_BRACKET, NULL, NULL),
46    BUILTIN(".", BINF_PSPECIAL, bin_dot, 1, -1, 0, NULL, NULL),
47    BUILTIN(":", BINF_PSPECIAL, bin_true, 0, -1, 0, NULL, NULL),
48    BUILTIN("alias", BINF_MAGICEQUALS | BINF_PLUSOPTS, bin_alias, 0, -1, 0, "Lgmrs", NULL),
49    BUILTIN("autoload", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "mktTUwXz", "u"),
50    BUILTIN("bg", 0, bin_fg, 0, -1, BIN_BG, NULL, NULL),
51    BUILTIN("break", BINF_PSPECIAL, bin_break, 0, 1, BIN_BREAK, NULL, NULL),
52    BUILTIN("bye", 0, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
53    BUILTIN("cd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
54    BUILTIN("chdir", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_CD, "qsPL", NULL),
55    BUILTIN("continue", BINF_PSPECIAL, bin_break, 0, 1, BIN_CONTINUE, NULL, NULL),
56    BUILTIN("declare", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klmprtuxz", NULL),
57    BUILTIN("dirs", 0, bin_dirs, 0, -1, 0, "clpv", NULL),
58    BUILTIN("disable", 0, bin_enable, 0, -1, BIN_DISABLE, "afmrs", NULL),
59    BUILTIN("disown", 0, bin_fg, 0, -1, BIN_DISOWN, NULL, NULL),
60    BUILTIN("echo", BINF_SKIPINVALID, bin_print, 0, -1, BIN_ECHO, "neE", "-"),
61    BUILTIN("emulate", 0, bin_emulate, 0, -1, 0, "LR", NULL),
62    BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmrs", NULL),
63    BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
64    BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
65    BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, BIN_EXPORT, "E:%F:%HL:%R:%TUZ:%afhi:%lprtu", "xg"),
66    BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
67    /*
68     * We used to behave as if the argument to -e was optional.
69     * But that's actually not useful, so it's more consistent to
70     * cause an error.
71     */
72    BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlmnpPrRt:W", NULL),
73    BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL),
74    BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"),
75    BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUz", NULL),
76    BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"),
77    BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL),
78    BUILTIN("hash", BINF_MAGICEQUALS, bin_hash, 0, -1, 0, "Ldfmrv", NULL),
79
80#ifdef ZSH_HASH_DEBUG
81    BUILTIN("hashinfo", 0, bin_hashinfo, 0, 0, 0, NULL, NULL),
82#endif
83
84    BUILTIN("history", 0, bin_fc, 0, -1, BIN_FC, "adDEfimnpPrt:", "l"),
85    BUILTIN("integer", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "HL:%R:%Z:%ghi:%lprtux", "i"),
86    BUILTIN("jobs", 0, bin_fg, 0, -1, BIN_JOBS, "dlpZrs", NULL),
87    BUILTIN("kill", BINF_HANDLES_OPTS, bin_kill, 0, -1, 0, NULL, NULL),
88    BUILTIN("let", 0, bin_let, 1, -1, 0, NULL, NULL),
89    BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lprtux", NULL),
90    BUILTIN("log", 0, bin_log, 0, 0, 0, NULL, NULL),
91    BUILTIN("logout", 0, bin_break, 0, 1, BIN_LOGOUT, NULL, NULL),
92
93#if defined(ZSH_MEM) & defined(ZSH_MEM_DEBUG)
94    BUILTIN("mem", 0, bin_mem, 0, 0, 0, "v", NULL),
95#endif
96
97#if defined(ZSH_PAT_DEBUG)
98    BUILTIN("patdebug", 0, bin_patdebug, 1, -1, 0, "p", NULL),
99#endif
100
101    BUILTIN("popd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 1, BIN_POPD, "q", NULL),
102    BUILTIN("print", BINF_PRINTOPTS, bin_print, 0, -1, BIN_PRINT, "abcC:Df:ilmnNoOpPrRsSu:z-", NULL),
103    BUILTIN("printf", 0, bin_print, 1, -1, BIN_PRINTF, NULL, NULL),
104    BUILTIN("pushd", BINF_SKIPINVALID | BINF_SKIPDASH | BINF_DASHDASHVALID, bin_cd, 0, 2, BIN_PUSHD, "qsPL", NULL),
105    BUILTIN("pushln", 0, bin_print, 0, -1, BIN_PRINT, NULL, "-nz"),
106    BUILTIN("pwd", 0, bin_pwd, 0, 0, 0, "rLP", NULL),
107    BUILTIN("r", 0, bin_fc, 0, -1, BIN_R, "nrl", NULL),
108    BUILTIN("read", 0, bin_read, 0, -1, 0, "cd:ek:%lnpqrst:%zu:AE", NULL),
109    BUILTIN("readonly", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%lptux", "r"),
110    BUILTIN("rehash", 0, bin_hash, 0, 0, 0, "df", "r"),
111    BUILTIN("return", BINF_PSPECIAL, bin_break, 0, 1, BIN_RETURN, NULL, NULL),
112    BUILTIN("set", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_set, 0, -1, 0, NULL, NULL),
113    BUILTIN("setopt", 0, bin_setopt, 0, -1, BIN_SETOPT, NULL, NULL),
114    BUILTIN("shift", BINF_PSPECIAL, bin_shift, 0, -1, 0, NULL, NULL),
115    BUILTIN("source", BINF_PSPECIAL, bin_dot, 1, -1, 0, NULL, NULL),
116    BUILTIN("suspend", 0, bin_suspend, 0, 0, 0, "f", NULL),
117    BUILTIN("test", BINF_HANDLES_OPTS, bin_test, 0, -1, BIN_TEST, NULL, NULL),
118    BUILTIN("ttyctl", 0, bin_ttyctl, 0, 0, 0, "fu", NULL),
119    BUILTIN("times", BINF_PSPECIAL, bin_times, 0, 0, 0, NULL, NULL),
120    BUILTIN("trap", BINF_PSPECIAL | BINF_HANDLES_OPTS, bin_trap, 0, -1, 0, NULL, NULL),
121    BUILTIN("true", 0, bin_true, 0, -1, 0, NULL, NULL),
122    BUILTIN("type", 0, bin_whence, 0, -1, 0, "ampfsw", "v"),
123    BUILTIN("typeset", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL, bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%afghi:%klprtuxmz", NULL),
124    BUILTIN("umask", 0, bin_umask, 0, 1, 0, "S", NULL),
125    BUILTIN("unalias", 0, bin_unhash, 1, -1, 0, "ms", "a"),
126    BUILTIN("unfunction", 0, bin_unhash, 1, -1, 0, "m", "f"),
127    BUILTIN("unhash", 0, bin_unhash, 1, -1, 0, "adfms", NULL),
128    BUILTIN("unset", BINF_PSPECIAL, bin_unset, 1, -1, 0, "fmv", NULL),
129    BUILTIN("unsetopt", 0, bin_setopt, 0, -1, BIN_UNSETOPT, NULL, NULL),
130    BUILTIN("wait", 0, bin_fg, 0, -1, BIN_WAIT, NULL, NULL),
131    BUILTIN("whence", 0, bin_whence, 0, -1, 0, "acmpvfsw", NULL),
132    BUILTIN("where", 0, bin_whence, 0, -1, 0, "pmsw", "ca"),
133    BUILTIN("which", 0, bin_whence, 0, -1, 0, "ampsw", "c"),
134    BUILTIN("zmodload", 0, bin_zmodload, 0, -1, 0, "AFRILP:abcfdilmpue", NULL),
135    BUILTIN("zcompile", 0, bin_zcompile, 0, -1, 0, "tUMRcmzka", NULL),
136};
137
138/****************************************/
139/* Builtin Command Hash Table Functions */
140/****************************************/
141
142/* hash table containing builtin commands */
143
144/**/
145mod_export HashTable builtintab;
146
147/**/
148void
149createbuiltintable(void)
150{
151    builtintab = newhashtable(85, "builtintab", NULL);
152
153    builtintab->hash        = hasher;
154    builtintab->emptytable  = NULL;
155    builtintab->filltable   = NULL;
156    builtintab->cmpnodes    = strcmp;
157    builtintab->addnode     = addhashnode;
158    builtintab->getnode     = gethashnode;
159    builtintab->getnode2    = gethashnode2;
160    builtintab->removenode  = removehashnode;
161    builtintab->disablenode = disablehashnode;
162    builtintab->enablenode  = enablehashnode;
163    builtintab->freenode    = freebuiltinnode;
164    builtintab->printnode   = printbuiltinnode;
165
166    (void)addbuiltins("zsh", builtins, sizeof(builtins)/sizeof(*builtins));
167}
168
169/* Print a builtin */
170
171/**/
172static void
173printbuiltinnode(HashNode hn, int printflags)
174{
175    Builtin bn = (Builtin) hn;
176
177    if (printflags & PRINT_WHENCE_WORD) {
178	printf("%s: builtin\n", bn->node.nam);
179	return;
180    }
181
182    if (printflags & PRINT_WHENCE_CSH) {
183	printf("%s: shell built-in command\n", bn->node.nam);
184	return;
185    }
186
187    if (printflags & PRINT_WHENCE_VERBOSE) {
188	printf("%s is a shell builtin\n", bn->node.nam);
189	return;
190    }
191
192    /* default is name only */
193    printf("%s\n", bn->node.nam);
194}
195
196/**/
197static void
198freebuiltinnode(HashNode hn)
199{
200    Builtin bn = (Builtin) hn;
201
202    if(!(bn->node.flags & BINF_ADDED)) {
203	zsfree(bn->node.nam);
204	zsfree(bn->optstr);
205	zfree(bn, sizeof(struct builtin));
206    }
207}
208
209/**/
210void
211init_builtins(void)
212{
213    if (!EMULATION(EMULATE_ZSH)) {
214	HashNode hn = reswdtab->getnode2(reswdtab, "repeat");
215	if (hn)
216	    reswdtab->disablenode(hn, 0);
217    }
218}
219
220/* Make sure we have space for a new option and increment. */
221
222#define OPT_ALLOC_CHUNK 16
223
224/**/
225static int
226new_optarg(Options ops)
227{
228    /* Argument index must be a non-zero 6-bit number. */
229    if (ops->argscount == 63)
230	return 1;
231    if (ops->argsalloc == ops->argscount) {
232	char **newptr =
233	    (char **)zhalloc((ops->argsalloc + OPT_ALLOC_CHUNK) *
234			     sizeof(char *));
235	if (ops->argsalloc)
236	    memcpy(newptr, ops->args, ops->argsalloc * sizeof(char *));
237	ops->args = newptr;
238	ops->argsalloc += OPT_ALLOC_CHUNK;
239    }
240    ops->argscount++;
241    return 0;
242}
243
244
245/* execute a builtin handler function after parsing the arguments */
246
247/**/
248int
249execbuiltin(LinkList args, Builtin bn)
250{
251    char *pp, *name, *optstr;
252    int flags, sense, argc, execop, xtr = isset(XTRACE);
253    struct options ops;
254
255    /* initialise options structure */
256    memset(ops.ind, 0, MAX_OPS*sizeof(unsigned char));
257    ops.args = NULL;
258    ops.argscount = ops.argsalloc = 0;
259
260    /* initialize some local variables */
261    name = (char *) ugetnode(args);
262
263    if (!bn->handlerfunc) {
264	DPUTS(1, "Missing builtin detected too late");
265	deletebuiltin(bn->node.nam);
266	return 1;
267    }
268    /* get some information about the command */
269    flags = bn->node.flags;
270    optstr = bn->optstr;
271
272    /* Set up the argument list. */
273    /* count the arguments */
274    argc = countlinknodes(args);
275
276    {
277	/*
278	 * Keep all arguments, including options, in an array.
279	 * We don't actually need the option part of the argument
280	 * after option processing, but it makes XTRACE output
281	 * much simpler.
282	 */
283	VARARR(char *, argarr, argc + 1);
284	char **argv;
285
286	/*
287	 * Get the actual arguments, into argv.  Remember argarr
288	 * may be an array declaration, depending on the compiler.
289	 */
290	argv = argarr;
291	while ((*argv++ = (char *)ugetnode(args)));
292	argv = argarr;
293
294	/* Sort out the options. */
295	if (optstr) {
296	    char *arg = *argv;
297	    /* while arguments look like options ... */
298	    while (arg &&
299		   /* Must begin with - or maybe + */
300		   ((sense = (*arg == '-')) ||
301		    ((flags & BINF_PLUSOPTS) && *arg == '+'))) {
302		/* Digits aren't arguments unless the command says they are. */
303		if (!(flags & BINF_KEEPNUM) && idigit(arg[1]))
304		    break;
305		/* For cd and friends, a single dash is not an option. */
306		if ((flags & BINF_SKIPDASH) && !arg[1])
307		    break;
308		if ((flags & BINF_DASHDASHVALID) && !strcmp(arg, "--")) {
309		    /*
310		     * Need to skip this before checking whether this is
311		     * really an option.
312		     */
313		    argv++;
314		    break;
315		}
316		/*
317		 * Unrecognised options to echo etc. are not really
318		 * options.
319		 *
320		 * Note this flag is not smart enough to handle option
321		 * arguments.  In fact, ideally it shouldn't be added
322		 * to any new builtins, to preserve standard option
323		 * handling as much as possible.
324		*/
325		if (flags & BINF_SKIPINVALID) {
326		    char *p = arg;
327		    while (*++p && strchr(optstr, (int) *p));
328		    if (*p)
329			break;
330		}
331		/* handle -- or - (ops.ind['-']), and +
332		 * (ops.ind['-'] and ops.ind['+']) */
333		if (arg[1] == '-')
334		    arg++;
335		if (!arg[1]) {
336		    ops.ind['-'] = 1;
337		    if (!sense)
338			ops.ind['+'] = 1;
339		}
340		/* save options in ops, as long as they are in bn->optstr */
341		while (*++arg) {
342		    char *optptr;
343		    if ((optptr = strchr(optstr, execop = (int)*arg))) {
344			ops.ind[(int)*arg] = (sense) ? 1 : 2;
345			if (optptr[1] == ':') {
346			    char *argptr = NULL;
347			    if (optptr[2] == ':') {
348				if (arg[1])
349				    argptr = arg+1;
350				/* Optional argument in same word*/
351			    } else if (optptr[2] == '%') {
352				/* Optional numeric argument in same
353				 * or next word. */
354				if (arg[1] && idigit(arg[1]))
355				    argptr = arg+1;
356				else if (argv[1] && idigit(*argv[1]))
357				    argptr = arg = *++argv;
358			    } else {
359				/* Mandatory argument */
360				if (arg[1])
361				    argptr = arg+1;
362				else if ((arg = *++argv))
363				    argptr = arg;
364				else {
365				    zwarnnam(name, "argument expected: -%c",
366					     execop);
367				    return 1;
368				}
369			    }
370			    if (argptr) {
371				if (new_optarg(&ops)) {
372				    zwarnnam(name,
373					     "too many option arguments");
374				    return 1;
375				}
376				ops.ind[execop] |= ops.argscount << 2;
377				ops.args[ops.argscount-1] = argptr;
378				while (arg[1])
379				    arg++;
380			    }
381			}
382		    } else
383			break;
384		}
385		/* The above loop may have exited on an invalid option.  (We  *
386		 * assume that any option requiring metafication is invalid.) */
387		if (*arg) {
388		    if(*arg == Meta)
389			*++arg ^= 32;
390		    zwarn("bad option: -%c", *arg);
391		    return 1;
392		}
393		arg = *++argv;
394		/* for the "print" builtin, the options after -R are treated as
395		   options to "echo" */
396		if ((flags & BINF_PRINTOPTS) && ops.ind['R'] &&
397		    !ops.ind['f']) {
398		    optstr = "ne";
399		    flags |= BINF_SKIPINVALID;
400		}
401		/* the option -- indicates the end of the options */
402		if (ops.ind['-'])
403		    break;
404	    }
405	} else if (!(flags & BINF_HANDLES_OPTS) && *argv &&
406		   !strcmp(*argv, "--")) {
407	    ops.ind['-'] = 1;
408	    argv++;
409	}
410
411	/* handle built-in options, for overloaded handler functions */
412	if ((pp = bn->defopts)) {
413	    while (*pp) {
414		/* only if not already set */
415		if (!ops.ind[(int)*pp])
416		    ops.ind[(int)*pp] = 1;
417		pp++;
418	    }
419	}
420
421	/* Fix the argument count by subtracting option arguments */
422	argc -= argv - argarr;
423
424	if (errflag) {
425	    errflag = 0;
426	    return 1;
427	}
428
429	/* check that the argument count lies within the specified bounds */
430	if (argc < bn->minargs || (argc > bn->maxargs && bn->maxargs != -1)) {
431	    zwarnnam(name, (argc < bn->minargs)
432		     ? "not enough arguments" : "too many arguments");
433	    return 1;
434	}
435
436	/* display execution trace information, if required */
437	if (xtr) {
438	    /* Use full argument list including options for trace output */
439	    char **fullargv = argarr;
440	    printprompt4();
441	    fprintf(xtrerr, "%s", name);
442	    while (*fullargv) {
443	        fputc(' ', xtrerr);
444	        quotedzputs(*fullargv++, xtrerr);
445	    }
446	    fputc('\n', xtrerr);
447	    fflush(xtrerr);
448	}
449	/* call the handler function, and return its return value */
450	return (*(bn->handlerfunc)) (name, argv, &ops, bn->funcid);
451    }
452}
453
454/* Enable/disable an element in one of the internal hash tables.  *
455 * With no arguments, it lists all the currently enabled/disabled *
456 * elements in that particular hash table.                        */
457
458/**/
459int
460bin_enable(char *name, char **argv, Options ops, int func)
461{
462    HashTable ht;
463    HashNode hn;
464    ScanFunc scanfunc;
465    Patprog pprog;
466    int flags1 = 0, flags2 = 0;
467    int match = 0, returnval = 0;
468
469    /* Find out which hash table we are working with. */
470    if (OPT_ISSET(ops,'f'))
471	ht = shfunctab;
472    else if (OPT_ISSET(ops,'r'))
473	ht = reswdtab;
474    else if (OPT_ISSET(ops,'s'))
475	ht = sufaliastab;
476    else if (OPT_ISSET(ops,'a'))
477	ht = aliastab;
478    else
479	ht = builtintab;
480
481    /* Do we want to enable or disable? */
482    if (func == BIN_ENABLE) {
483	flags2 = DISABLED;
484	scanfunc = ht->enablenode;
485    } else {
486	flags1 = DISABLED;
487	scanfunc = ht->disablenode;
488    }
489
490    /* Given no arguments, print the names of the enabled/disabled elements  *
491     * in this hash table.  If func == BIN_ENABLE, then scanhashtable will   *
492     * print nodes NOT containing the DISABLED flag, else scanhashtable will *
493     * print nodes containing the DISABLED flag.                             */
494    if (!*argv) {
495	queue_signals();
496	scanhashtable(ht, 1, flags1, flags2, ht->printnode, 0);
497	unqueue_signals();
498	return 0;
499    }
500
501    /* With -m option, treat arguments as glob patterns. */
502    if (OPT_ISSET(ops,'m')) {
503	for (; *argv; argv++) {
504	    /* parse pattern */
505	    tokenize(*argv);
506	    if ((pprog = patcompile(*argv, PAT_STATIC, 0))) {
507		queue_signals();
508		match += scanmatchtable(ht, pprog, 0, 0, 0, scanfunc, 0);
509		unqueue_signals();
510	    }
511	    else {
512		untokenize(*argv);
513		zwarnnam(name, "bad pattern : %s", *argv);
514		returnval = 1;
515	    }
516	}
517	/* If we didn't match anything, we return 1. */
518	if (!match)
519	    returnval = 1;
520	return returnval;
521    }
522
523    /* Take arguments literally -- do not glob */
524    queue_signals();
525    for (; *argv; argv++) {
526	if ((hn = ht->getnode2(ht, *argv))) {
527	    scanfunc(hn, 0);
528	} else {
529	    zwarnnam(name, "no such hash table element: %s", *argv);
530	    returnval = 1;
531	}
532    }
533    unqueue_signals();
534    return returnval;
535}
536
537/* set: either set the shell options, or set the shell arguments, *
538 * or declare an array, or show various things                    */
539
540/**/
541int
542bin_set(char *nam, char **args, UNUSED(Options ops), UNUSED(int func))
543{
544    int action, optno, array = 0, hadopt = 0,
545	hadplus = 0, hadend = 0, sort = 0;
546    char **x, *arrayname = NULL;
547
548    /* Obsolescent sh compatibility: set - is the same as set +xv *
549     * and set - args is the same as set +xv -- args              */
550    if (!EMULATION(EMULATE_ZSH) && *args && **args == '-' && !args[0][1]) {
551	dosetopt(VERBOSE, 0, 0, opts);
552	dosetopt(XTRACE, 0, 0, opts);
553	if (!args[1])
554	    return 0;
555    }
556
557    /* loop through command line options (begins with "-" or "+") */
558    while (*args && (**args == '-' || **args == '+')) {
559	action = (**args == '-');
560	hadplus |= !action;
561	if(!args[0][1])
562	    *args = "--";
563	while (*++*args) {
564	    if(**args == Meta)
565		*++*args ^= 32;
566	    if(**args != '-' || action)
567		hadopt = 1;
568	    /* The pseudo-option `--' signifies the end of options. */
569	    if (**args == '-') {
570		hadend = 1;
571		args++;
572		goto doneoptions;
573	    } else if (**args == 'o') {
574		if (!*++*args)
575		    args++;
576		if (!*args) {
577		    printoptionstates(hadplus);
578		    inittyptab();
579		    return 0;
580		}
581		if(!(optno = optlookup(*args)))
582		    zerrnam(nam, "no such option: %s", *args);
583		else if(dosetopt(optno, action, 0, opts))
584		    zerrnam(nam, "can't change option: %s", *args);
585		break;
586	    } else if(**args == 'A') {
587		if(!*++*args)
588		    args++;
589		array = action ? 1 : -1;
590		arrayname = *args;
591		if (!arrayname)
592		    goto doneoptions;
593		else if  (!isset(KSHARRAYS))
594		{
595		    args++;
596		    goto doneoptions;
597		}
598		break;
599	    } else if (**args == 's')
600		sort = action ? 1 : -1;
601	    else {
602	    	if (!(optno = optlookupc(**args)))
603		    zerrnam(nam, "bad option: -%c", **args);
604		else if(dosetopt(optno, action, 0, opts))
605		    zerrnam(nam, "can't change option: -%c", **args);
606	    }
607	}
608	args++;
609    }
610    if (errflag)
611	return 1;
612 doneoptions:
613    inittyptab();
614
615    /* Show the parameters, possibly with values */
616    queue_signals();
617    if (!arrayname)
618    {
619	if (!hadopt && !*args)
620	    scanhashtable(paramtab, 1, 0, 0, paramtab->printnode,
621			  hadplus ? PRINT_NAMEONLY : 0);
622
623	if (array) {
624	    /* display arrays */
625	    scanhashtable(paramtab, 1, PM_ARRAY, 0, paramtab->printnode,
626			  hadplus ? PRINT_NAMEONLY : 0);
627	}
628	if (!*args && !hadend) {
629	    unqueue_signals();
630	    return 0;
631	}
632    }
633    if (sort)
634	strmetasort(args, sort < 0 ? SORTIT_BACKWARDS : 0, NULL);
635    if (array) {
636	/* create an array with the specified elements */
637	char **a = NULL, **y;
638	int len = arrlen(args);
639
640	if (array < 0 && (a = getaparam(arrayname))) {
641	    int al = arrlen(a);
642
643	    if (al > len)
644		len = al;
645	}
646	for (x = y = zalloc((len + 1) * sizeof(char *)); len--; a++) {
647	    if (!*args)
648		args = a;
649	    *y++ = ztrdup(*args++);
650	}
651	*y++ = NULL;
652	setaparam(arrayname, x);
653    } else {
654	/* set shell arguments */
655	freearray(pparams);
656	pparams = zarrdup(args);
657    }
658    unqueue_signals();
659    return 0;
660}
661
662/**** directory-handling builtins ****/
663
664/**/
665int doprintdir = 0;		/* set in exec.c (for autocd) */
666
667/* pwd: display the name of the current directory */
668
669/**/
670int
671bin_pwd(UNUSED(char *name), UNUSED(char **argv), Options ops, UNUSED(int func))
672{
673    if (OPT_ISSET(ops,'r') || OPT_ISSET(ops,'P') ||
674	(isset(CHASELINKS) && !OPT_ISSET(ops,'L')))
675	printf("%s\n", zgetcwd());
676    else {
677	zputs(pwd, stdout);
678	putchar('\n');
679    }
680    return 0;
681}
682
683/* the directory stack */
684
685/**/
686mod_export LinkList dirstack;
687
688/* dirs: list the directory stack, or replace it with a provided list */
689
690/**/
691int
692bin_dirs(UNUSED(char *name), char **argv, Options ops, UNUSED(int func))
693{
694    LinkList l;
695
696    queue_signals();
697    /* with -v, -p or no arguments display the directory stack */
698    if (!(*argv || OPT_ISSET(ops,'c')) || OPT_ISSET(ops,'v') ||
699	OPT_ISSET(ops,'p')) {
700	LinkNode node;
701	char *fmt;
702	int pos = 1;
703
704	/* with the -v option, display a numbered list, starting at zero */
705	if (OPT_ISSET(ops,'v')) {
706	    printf("0\t");
707	    fmt = "\n%d\t";
708	/* with the -p option, display entries one per line */
709	} else if (OPT_ISSET(ops,'p'))
710	    fmt = "\n";
711	else
712	    fmt = " ";
713	if (OPT_ISSET(ops,'l'))
714	    zputs(pwd, stdout);
715	else
716	    fprintdir(pwd, stdout);
717	for (node = firstnode(dirstack); node; incnode(node)) {
718	    printf(fmt, pos++);
719	    if (OPT_ISSET(ops,'l'))
720		fputs(getdata(node), stdout);
721	    else
722		fprintdir(getdata(node), stdout);
723
724	}
725	unqueue_signals();
726	putchar('\n');
727	return 0;
728    }
729    /* replace the stack with the specified directories */
730    l = znewlinklist();
731    while (*argv)
732	zaddlinknode(l, ztrdup(*argv++));
733    freelinklist(dirstack, freestr);
734    dirstack = l;
735    unqueue_signals();
736    return 0;
737}
738
739/* cd, chdir, pushd, popd */
740
741/**/
742void
743set_pwd_env(void)
744{
745    Param pm;
746
747    /* update the PWD and OLDPWD shell parameters */
748
749    pm = (Param) paramtab->getnode(paramtab, "PWD");
750    if (pm && PM_TYPE(pm->node.flags) != PM_SCALAR) {
751	pm->node.flags &= ~PM_READONLY;
752	unsetparam_pm(pm, 0, 1);
753    }
754
755    pm = (Param) paramtab->getnode(paramtab, "OLDPWD");
756    if (pm && PM_TYPE(pm->node.flags) != PM_SCALAR) {
757	pm->node.flags &= ~PM_READONLY;
758	unsetparam_pm(pm, 0, 1);
759    }
760
761    setsparam("PWD", ztrdup(pwd));
762    setsparam("OLDPWD", ztrdup(oldpwd));
763
764    pm = (Param) paramtab->getnode(paramtab, "PWD");
765    if (!(pm->node.flags & PM_EXPORTED))
766	addenv(pm, pwd);
767    pm = (Param) paramtab->getnode(paramtab, "OLDPWD");
768    if (!(pm->node.flags & PM_EXPORTED))
769	addenv(pm, oldpwd);
770}
771
772/* set if we are resolving links to their true paths */
773static int chasinglinks;
774
775/* The main pwd changing function.  The real work is done by other     *
776 * functions.  cd_get_dest() does the initial argument processing;     *
777 * cd_do_chdir() actually changes directory, if possible; cd_new_pwd() *
778 * does the ancillary processing associated with actually changing    *
779 * directory.                                                          */
780
781/**/
782int
783bin_cd(char *nam, char **argv, Options ops, int func)
784{
785    LinkNode dir;
786    struct stat st1, st2;
787
788    if (isset(RESTRICTED)) {
789	zwarnnam(nam, "restricted");
790	return 1;
791    }
792    doprintdir = (doprintdir == -1);
793
794    chasinglinks = OPT_ISSET(ops,'P') ||
795	(isset(CHASELINKS) && !OPT_ISSET(ops,'L'));
796    queue_signals();
797    zpushnode(dirstack, ztrdup(pwd));
798    if (!(dir = cd_get_dest(nam, argv, OPT_ISSET(ops,'s'), func))) {
799	zsfree(getlinknode(dirstack));
800	unqueue_signals();
801	return 1;
802    }
803    cd_new_pwd(func, dir, OPT_ISSET(ops, 'q'));
804
805    if (stat(unmeta(pwd), &st1) < 0) {
806	setjobpwd();
807	zsfree(pwd);
808	pwd = NULL;
809	pwd = metafy(zgetcwd(), -1, META_DUP);
810    } else if (stat(".", &st2) < 0) {
811	if (chdir(unmeta(pwd)) < 0)
812	    zwarn("unable to chdir(%s): %e", pwd, errno);
813    } else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) {
814	if (chasinglinks) {
815	    setjobpwd();
816	    zsfree(pwd);
817	    pwd = NULL;
818	    pwd = metafy(zgetcwd(), -1, META_DUP);
819	} else if (chdir(unmeta(pwd)) < 0)
820	    zwarn("unable to chdir(%s): %e", pwd, errno);
821    }
822    unqueue_signals();
823    return 0;
824}
825
826/* Get directory to chdir to */
827
828/**/
829static LinkNode
830cd_get_dest(char *nam, char **argv, int hard, int func)
831{
832    LinkNode dir = NULL;
833    LinkNode target;
834    char *dest;
835
836    if (!argv[0]) {
837	if (func == BIN_POPD && !nextnode(firstnode(dirstack))) {
838	    zwarnnam(nam, "directory stack empty");
839	    return NULL;
840	}
841	if (func == BIN_PUSHD && unset(PUSHDTOHOME))
842	    dir = nextnode(firstnode(dirstack));
843	if (dir)
844	    zinsertlinknode(dirstack, dir, getlinknode(dirstack));
845	else if (func != BIN_POPD)
846	    zpushnode(dirstack, ztrdup(home));
847    } else if (!argv[1]) {
848	int dd;
849	char *end;
850
851	doprintdir++;
852	if (argv[0][1] && (argv[0][0] == '+' || argv[0][0] == '-')
853	    && strspn(argv[0]+1, "0123456789") == strlen(argv[0]+1)) {
854	    dd = zstrtol(argv[0] + 1, &end, 10);
855	    if (*end == '\0') {
856		if ((argv[0][0] == '+') ^ isset(PUSHDMINUS))
857		    for (dir = firstnode(dirstack); dir && dd; dd--, incnode(dir));
858		else
859		    for (dir = lastnode(dirstack); dir != (LinkNode) dirstack && dd;
860			 dd--, dir = prevnode(dir));
861		if (!dir || dir == (LinkNode) dirstack) {
862		    zwarnnam(nam, "no such entry in dir stack");
863		    return NULL;
864		}
865	    }
866	}
867	if (!dir)
868	    zpushnode(dirstack, ztrdup(strcmp(argv[0], "-")
869				       ? (doprintdir--, argv[0]) : oldpwd));
870    } else {
871	char *u, *d;
872	int len1, len2, len3;
873
874	if (!(u = strstr(pwd, argv[0]))) {
875	    zwarnnam(nam, "string not in pwd: %s", argv[0]);
876	    return NULL;
877	}
878	len1 = strlen(argv[0]);
879	len2 = strlen(argv[1]);
880	len3 = u - pwd;
881	d = (char *)zalloc(len3 + len2 + strlen(u + len1) + 1);
882	strncpy(d, pwd, len3);
883	strcpy(d + len3, argv[1]);
884	strcat(d, u + len1);
885	zpushnode(dirstack, d);
886	doprintdir++;
887    }
888
889    target = dir;
890    if (func == BIN_POPD) {
891	if (!dir) {
892	    target = dir = firstnode(dirstack);
893	} else if (dir != firstnode(dirstack)) {
894	    return dir;
895	}
896	dir = nextnode(dir);
897    }
898    if (!dir) {
899	dir = firstnode(dirstack);
900    }
901    if (!(dest = cd_do_chdir(nam, getdata(dir), hard))) {
902	if (!target)
903	    zsfree(getlinknode(dirstack));
904	if (func == BIN_POPD)
905	    zsfree(remnode(dirstack, dir));
906	return NULL;
907    }
908    if (dest != (char *)getdata(dir)) {
909	zsfree(getdata(dir));
910	setdata(dir, dest);
911    }
912    return target ? target : dir;
913}
914
915/* Change to given directory, if possible.  This function works out  *
916 * exactly how the directory should be interpreted, including cdpath *
917 * and CDABLEVARS.  For each possible interpretation of the given    *
918 * path, this calls cd_try_chdir(), which attempts to chdir to that  *
919 * particular path.                                                  */
920
921/**/
922static char *
923cd_do_chdir(char *cnam, char *dest, int hard)
924{
925    char **pp, *ret;
926    int hasdot = 0, eno = ENOENT;
927    /*
928     * nocdpath indicates that cdpath should not be used.
929     * This is the case iff dest is a relative path
930     * whose first segment is . or .., but if the path is
931     * absolute then cdpath won't be used anyway.
932     */
933    int nocdpath;
934#ifdef __CYGWIN__
935    /*
936     * Normalize path under Cygwin to avoid messing with
937     * DOS style names with drives in them
938     */
939    static char buf[PATH_MAX];
940#ifndef _SYS_CYGWIN_H
941    void cygwin_conv_to_posix_path(const char *, char *);
942#endif
943
944    cygwin_conv_to_posix_path(dest, buf);
945    dest = buf;
946#endif
947    nocdpath = dest[0] == '.' &&
948	(dest[1] == '/' || !dest[1] || (dest[1] == '.' &&
949					(dest[2] == '/' || !dest[2])));
950
951    /*
952     * If we have an absolute path, use it as-is only
953     */
954    if (*dest == '/') {
955	if ((ret = cd_try_chdir(NULL, dest, hard)))
956	    return ret;
957	zwarnnam(cnam, "%e: %s", errno, dest);
958	return NULL;
959    }
960
961    /*
962     * If cdpath is being used, check it for ".".
963     * Don't bother doing this if POSIXCD is set, we don't
964     * need to know (though it doesn't actually matter).
965     */
966    if (!nocdpath && !isset(POSIXCD))
967	for (pp = cdpath; *pp; pp++)
968	    if (!(*pp)[0] || ((*pp)[0] == '.' && (*pp)[1] == '\0'))
969		hasdot = 1;
970    /*
971     * If
972     * (- there is no . in cdpath
973     *  - or cdpath is not being used)
974     *  - and the POSIXCD option is not set
975     * try the directory as-is (i.e. from .)
976     */
977    if (!hasdot && !isset(POSIXCD)) {
978	if ((ret = cd_try_chdir(NULL, dest, hard)))
979	    return ret;
980	if (errno != ENOENT)
981	    eno = errno;
982    }
983    /* if cdpath is being used, try given directory relative to each element in
984       cdpath in turn */
985    if (!nocdpath)
986	for (pp = cdpath; *pp; pp++) {
987	    if ((ret = cd_try_chdir(*pp, dest, hard))) {
988		if (isset(POSIXCD)) {
989		    /*
990		     * For POSIX we need to print the directory
991		     * any time CDPATH was used, except in the
992		     * special case of an empty segment being
993		     * treated as a ".".
994		     */
995		    if (**pp)
996			doprintdir++;
997		}  else {
998		    if (strcmp(*pp, ".")) {
999			doprintdir++;
1000		    }
1001		}
1002		return ret;
1003	    }
1004	    if (errno != ENOENT)
1005		eno = errno;
1006	}
1007    /*
1008     * POSIX requires us to check "." after CDPATH rather than before.
1009     */
1010    if (isset(POSIXCD)) {
1011	if ((ret = cd_try_chdir(NULL, dest, hard)))
1012	    return ret;
1013	if (errno != ENOENT)
1014	    eno = errno;
1015    }
1016
1017    /* handle the CDABLEVARS option */
1018    if ((ret = cd_able_vars(dest))) {
1019	if ((ret = cd_try_chdir(NULL, ret,hard))) {
1020	    doprintdir++;
1021	    return ret;
1022	}
1023	if (errno != ENOENT)
1024	    eno = errno;
1025    }
1026
1027    /* If we got here, it means that we couldn't chdir to any of the
1028       multitudinous possible paths allowed by zsh.  We've run out of options!
1029       Add more here! */
1030    zwarnnam(cnam, "%e: %s", eno, dest);
1031    return NULL;
1032}
1033
1034/* If the CDABLEVARS option is set, return the new *
1035 * interpretation of the given path.               */
1036
1037/**/
1038char *
1039cd_able_vars(char *s)
1040{
1041    char *rest, save;
1042
1043    if (isset(CDABLEVARS)) {
1044	for (rest = s; *rest && *rest != '/'; rest++);
1045	save = *rest;
1046	*rest = 0;
1047	s = getnameddir(s);
1048	*rest = save;
1049
1050	if (s && *rest)
1051	    s = dyncat(s, rest);
1052
1053	return s;
1054    }
1055    return NULL;
1056}
1057
1058/* Attempt to change to a single given directory.  The directory,    *
1059 * for the convenience of the calling function, may be provided in   *
1060 * two parts, which must be concatenated before attempting to chdir. *
1061 * Returns NULL if the chdir fails.  If the directory change is      *
1062 * possible, it is performed, and a pointer to the new full pathname *
1063 * is returned.                                                      */
1064
1065/**/
1066static char *
1067cd_try_chdir(char *pfix, char *dest, int hard)
1068{
1069    char *buf;
1070    int dlen, dochaselinks = 0;
1071
1072    /* handle directory prefix */
1073    if (pfix && *pfix) {
1074	if (*pfix == '/') {
1075#ifdef __CYGWIN__
1076/* NB: Don't turn "/"+"bin" into "//"+"bin" by mistake!  "//bin" may *
1077 * not be what user really wants (probably wants "/bin"), but        *
1078 * "//bin" could be valid too (see fixdir())!  This is primarily for *
1079 * handling CDPATH correctly.  Likewise for "//"+"bin" not becoming  *
1080 * "///bin" (aka "/bin").                                            */
1081	    int root = pfix[1] == '\0' || (pfix[1] == '/' && pfix[2] == '\0');
1082	    buf = tricat(pfix, ( root ? "" : "/" ), dest);
1083#else
1084	    buf = tricat(pfix, "/", dest);
1085#endif
1086	} else {
1087	    int pfl = strlen(pfix);
1088	    dlen = strlen(pwd);
1089
1090	    buf = zalloc(dlen + pfl + strlen(dest) + 3);
1091	    strcpy(buf, pwd);
1092	    buf[dlen] = '/';
1093	    strcpy(buf + dlen + 1, pfix);
1094	    buf[dlen + 1 + pfl] = '/';
1095	    strcpy(buf + dlen + pfl + 2, dest);
1096	}
1097    } else if (*dest == '/')
1098	buf = ztrdup(dest);
1099    else {
1100	dlen = strlen(pwd);
1101	if (pwd[dlen-1] == '/')
1102	    --dlen;
1103	buf = zalloc(dlen + strlen(dest) + 2);
1104	strcpy(buf, pwd);
1105	buf[dlen] = '/';
1106	strcpy(buf + dlen + 1, dest);
1107    }
1108
1109    /* Normalise path.  See the definition of fixdir() for what this means.
1110     * We do not do this if we are chasing links.
1111     */
1112    if (!chasinglinks)
1113	dochaselinks = fixdir(buf);
1114    else
1115	unmetafy(buf, &dlen);
1116
1117    /* We try the full path first.  If that fails, try the
1118     * argument to cd relatively.  This is useful if the cwd
1119     * or a parent directory is renamed in the interim.
1120     */
1121    if (lchdir(buf, NULL, hard) && lchdir(dest, NULL, hard)) {
1122	free(buf);
1123	return NULL;
1124    }
1125    /* the chdir succeeded, so decide if we should force links to be chased */
1126    if (dochaselinks)
1127	chasinglinks = 1;
1128    return metafy(buf, -1, META_NOALLOC);
1129}
1130
1131/* do the extra processing associated with changing directory */
1132
1133/**/
1134static void
1135cd_new_pwd(int func, LinkNode dir, int quiet)
1136{
1137    char *new_pwd, *s;
1138    int dirstacksize;
1139
1140    if (func == BIN_PUSHD)
1141	rolllist(dirstack, dir);
1142    new_pwd = remnode(dirstack, dir);
1143
1144    if (func == BIN_POPD && firstnode(dirstack)) {
1145	zsfree(new_pwd);
1146	new_pwd = getlinknode(dirstack);
1147    } else if (func == BIN_CD && unset(AUTOPUSHD))
1148	zsfree(getlinknode(dirstack));
1149
1150    if (chasinglinks) {
1151	s = new_pwd;
1152	new_pwd = findpwd(s);
1153	zsfree(s);
1154    }
1155    if (isset(PUSHDIGNOREDUPS)) {
1156	LinkNode n;
1157	for (n = firstnode(dirstack); n; incnode(n)) {
1158	    if (!strcmp(new_pwd, getdata(n))) {
1159		zsfree(remnode(dirstack, n));
1160		break;
1161	    }
1162	}
1163    }
1164
1165    /* shift around the pwd variables, to make oldpwd and pwd relate to the
1166       current (i.e. new) pwd */
1167    zsfree(oldpwd);
1168    oldpwd = pwd;
1169    setjobpwd();
1170    pwd = new_pwd;
1171    set_pwd_env();
1172
1173    if (isset(INTERACTIVE) || isset(POSIXCD)) {
1174	if (func != BIN_CD && isset(INTERACTIVE)) {
1175            if (unset(PUSHDSILENT) && !quiet)
1176	        printdirstack();
1177        } else if (doprintdir) {
1178	    fprintdir(pwd, stdout);
1179	    putchar('\n');
1180	}
1181    }
1182
1183    /* execute the chpwd function */
1184    fflush(stdout);
1185    fflush(stderr);
1186    if (!quiet)
1187	callhookfunc("chpwd", NULL, 1, NULL);
1188
1189    dirstacksize = getiparam("DIRSTACKSIZE");
1190    /* handle directory stack sizes out of range */
1191    if (dirstacksize > 0) {
1192	int remove = countlinknodes(dirstack) -
1193	    (dirstacksize < 2 ? 2 : dirstacksize);
1194	while (remove-- >= 0)
1195	    zsfree(remnode(dirstack, lastnode(dirstack)));
1196    }
1197}
1198
1199/* Print the directory stack */
1200
1201/**/
1202static void
1203printdirstack(void)
1204{
1205    LinkNode node;
1206
1207    fprintdir(pwd, stdout);
1208    for (node = firstnode(dirstack); node; incnode(node)) {
1209	putchar(' ');
1210	fprintdir(getdata(node), stdout);
1211    }
1212    putchar('\n');
1213}
1214
1215/* Normalise a path.  Segments consisting of ., and foo/.. *
1216 * combinations, are removed and the path is unmetafied.
1217 * Returns 1 if we found a ../ path which should force links to
1218 * be chased, 0 otherwise.
1219 */
1220
1221/**/
1222int
1223fixdir(char *src)
1224{
1225    char *dest = src, *d0 = dest;
1226#ifdef __CYGWIN__
1227    char *s0 = src;
1228#endif
1229    int ret = 0;
1230
1231/*** if have RFS superroot directory ***/
1232#ifdef HAVE_SUPERROOT
1233    /* allow /.. segments to remain */
1234    while (*src == '/' && src[1] == '.' && src[2] == '.' &&
1235	   (!src[3] || src[3] == '/')) {
1236	*dest++ = '/';
1237	*dest++ = '.';
1238	*dest++ = '.';
1239	src += 3;
1240    }
1241#endif
1242
1243    for (;;) {
1244	/* compress multiple /es into single */
1245	if (*src == '/') {
1246#ifdef __CYGWIN__
1247	    /* allow leading // under cygwin, but /// still becomes / */
1248	    if (src == s0 && src[1] == '/' && src[2] != '/')
1249		*dest++ = *src++;
1250#endif
1251	    *dest++ = *src++;
1252	    while (*src == '/')
1253		src++;
1254	}
1255	/* if we are at the end of the input path, remove a trailing / (if it
1256	   exists), and return ct */
1257	if (!*src) {
1258	    while (dest > d0 + 1 && dest[-1] == '/')
1259		dest--;
1260	    *dest = '\0';
1261	    return ret;
1262	}
1263	if (src[0] == '.' && src[1] == '.' &&
1264	    (src[2] == '\0' || src[2] == '/')) {
1265	    if (isset(CHASEDOTS)) {
1266		ret = 1;
1267		/* and treat as normal path segment */
1268	    } else {
1269		if (dest > d0 + 1) {
1270		    /*
1271		     * remove a foo/.. combination:
1272		     * first check foo exists, else return.
1273		     */
1274		    struct stat st;
1275		    *dest = '\0';
1276		    if (stat(d0, &st) < 0 || !S_ISDIR(st.st_mode)) {
1277			char *ptrd, *ptrs;
1278			if (dest == src)
1279			    *dest = '.';
1280			for (ptrs = src, ptrd = dest; *ptrs; ptrs++, ptrd++)
1281			    *ptrd = (*ptrs == Meta) ? (*++ptrs ^ 32) : *ptrs;
1282			*ptrd = '\0';
1283			return 1;
1284		    }
1285		    for (dest--; dest > d0 + 1 && dest[-1] != '/'; dest--);
1286		    if (dest[-1] != '/')
1287			dest--;
1288		}
1289		src++;
1290		while (*++src == '/');
1291		continue;
1292	    }
1293	}
1294	if (src[0] == '.' && (src[1] == '/' || src[1] == '\0')) {
1295	    /* skip a . section */
1296	    while (*++src == '/');
1297	} else {
1298	    /* copy a normal segment into the output */
1299	    while (*src != '/' && *src != '\0')
1300		if ((*dest++ = *src++) == Meta)
1301		    dest[-1] = *src++ ^ 32;
1302	}
1303    }
1304}
1305
1306/**/
1307mod_export void
1308printqt(char *str)
1309{
1310    /* Print str, but turn any single quote into '\'' or ''. */
1311    for (; *str; str++)
1312	if (*str == '\'')
1313	    printf(isset(RCQUOTES) ? "''" : "'\\''");
1314	else
1315	    putchar(*str);
1316}
1317
1318/**/
1319mod_export void
1320printif(char *str, int c)
1321{
1322    /* If flag c has an argument, print that */
1323    if (str) {
1324	printf(" -%c ", c);
1325	quotedzputs(str, stdout);
1326    }
1327}
1328
1329/**** history list functions ****/
1330
1331/* fc, history, r */
1332
1333/**/
1334int
1335bin_fc(char *nam, char **argv, Options ops, int func)
1336{
1337    zlong first = -1, last = -1;
1338    int retval;
1339    char *s;
1340    struct asgment *asgf = NULL, *asgl = NULL;
1341    Patprog pprog = NULL;
1342
1343    /* fc is only permitted in interactive shells */
1344#ifdef FACIST_INTERACTIVE
1345    if (!interact) {
1346	zwarnnam(nam, "not interactive shell");
1347	return 1;
1348    }
1349#endif
1350    if (OPT_ISSET(ops,'p')) {
1351	char *hf = "";
1352	zlong hs = DEFAULT_HISTSIZE;
1353	zlong shs = 0;
1354	int level = OPT_ISSET(ops,'a') ? locallevel : -1;
1355	if (*argv) {
1356	    hf = *argv++;
1357	    if (*argv) {
1358		hs = zstrtol(*argv++, NULL, 10);
1359		if (*argv)
1360		    shs = zstrtol(*argv++, NULL, 10);
1361		else
1362		    shs = hs;
1363		if (*argv) {
1364		    zwarnnam("fc", "too many arguments");
1365		    return 1;
1366		}
1367	    } else {
1368		hs = histsiz;
1369		shs = savehistsiz;
1370	    }
1371	}
1372	if (!pushhiststack(hf, hs, shs, level))
1373	    return 1;
1374	if (*hf) {
1375	    struct stat st;
1376	    if (stat(hf, &st) >= 0 || errno != ENOENT)
1377		readhistfile(hf, 1, HFILE_USE_OPTIONS);
1378	}
1379	return 0;
1380    }
1381    if (OPT_ISSET(ops,'P')) {
1382	if (*argv) {
1383	    zwarnnam("fc", "too many arguments");
1384	    return 1;
1385	}
1386	return !saveandpophiststack(-1, HFILE_USE_OPTIONS);
1387    }
1388    /* with the -m option, the first argument is taken *
1389     * as a pattern that history lines have to match   */
1390    if (*argv && OPT_ISSET(ops,'m')) {
1391	tokenize(*argv);
1392	if (!(pprog = patcompile(*argv++, 0, NULL))) {
1393	    zwarnnam(nam, "invalid match pattern");
1394	    return 1;
1395	}
1396    }
1397    queue_signals();
1398    if (OPT_ISSET(ops,'R')) {
1399	/* read history from a file */
1400	readhistfile(*argv, 1, OPT_ISSET(ops,'I') ? HFILE_SKIPOLD : 0);
1401	unqueue_signals();
1402	return 0;
1403    }
1404    if (OPT_ISSET(ops,'W')) {
1405	/* write history to a file */
1406	savehistfile(*argv, 1, OPT_ISSET(ops,'I') ? HFILE_SKIPOLD : 0);
1407	unqueue_signals();
1408	return 0;
1409    }
1410    if (OPT_ISSET(ops,'A')) {
1411	/* append history to a file */
1412	savehistfile(*argv, 1, HFILE_APPEND |
1413		     (OPT_ISSET(ops,'I') ? HFILE_SKIPOLD : 0));
1414	unqueue_signals();
1415	return 0;
1416    }
1417
1418    if (zleactive) {
1419	zwarnnam(nam, "no interactive history within ZLE");
1420	return 1;
1421    }
1422
1423    /* put foo=bar type arguments into the substitution list */
1424    while (*argv && equalsplit(*argv, &s)) {
1425	Asgment a = (Asgment) zhalloc(sizeof *a);
1426
1427	if (!**argv) {
1428	    zwarnnam(nam, "invalid replacement pattern: =%s", s);
1429	    return 1;
1430	}
1431	if (!asgf)
1432	    asgf = asgl = a;
1433	else {
1434	    asgl->next = a;
1435	    asgl = a;
1436	}
1437	a->name = *argv;
1438	a->value = s;
1439	a->next = NULL;
1440	argv++;
1441    }
1442    /* interpret and check first history line specifier */
1443    if (*argv) {
1444	first = fcgetcomm(*argv);
1445	if (first == -1) {
1446	    unqueue_signals();
1447	    return 1;
1448	}
1449	argv++;
1450    }
1451    /* interpret and check second history line specifier */
1452    if (*argv) {
1453	last = fcgetcomm(*argv);
1454	if (last == -1) {
1455	    unqueue_signals();
1456	    return 1;
1457	}
1458	argv++;
1459    }
1460    /* There is a maximum of two history specifiers.  At least, there *
1461     * will be as long as the history list is one-dimensional.        */
1462    if (*argv) {
1463	unqueue_signals();
1464	zwarnnam("fc", "too many arguments");
1465	return 1;
1466    }
1467    /* default values of first and last, and range checking */
1468    if (last == -1) {
1469	if (OPT_ISSET(ops,'l') && first < curhist) {
1470	    /*
1471	     * When listing base our calculations on curhist,
1472	     * to show anything added since the edited history line.
1473	     * Also, in that case curhist will have been modified
1474	     * past the current history line; then we want to
1475	     * show everything, because the user expects to
1476	     * see the result of "print -s".  Otherwise, we subtract
1477	     * -1 from the line, because the user doesn't usually expect
1478	     * to see the command line that caused history to be
1479	     * listed.
1480	     */
1481	    last = (curline.histnum == curhist) ? addhistnum(curhist,-1,0)
1482		: curhist;
1483	    if (last < firsthist())
1484		last = firsthist();
1485	}
1486	else
1487	    last = first;
1488    }
1489    if (first == -1) {
1490	/*
1491	 * When listing, we want to see everything that's been
1492	 * added to the history, including by print -s, so use
1493	 * curhist.
1494	 * When reexecuting, we want to restrict to the last edited
1495	 * command line to avoid giving the user a nasty turn
1496	 * if some helpful soul ran "print -s 'rm -rf /'".
1497	 */
1498	first = OPT_ISSET(ops,'l')? addhistnum(curhist,-16,0)
1499			: addhistnum(curline.histnum,-1,0);
1500	if (first < 1)
1501	    first = 1;
1502	if (last < first)
1503	    last = first;
1504    }
1505    if (OPT_ISSET(ops,'l')) {
1506	/* list the required part of the history */
1507	retval = fclist(stdout, ops, first, last, asgf, pprog, 0);
1508	unqueue_signals();
1509    }
1510    else {
1511	/* edit history file, and (if successful) use the result as a new command */
1512	int tempfd;
1513	FILE *out;
1514	char *fil;
1515
1516	retval = 1;
1517	if ((tempfd = gettempfile(NULL, 1, &fil)) < 0
1518	 || ((out = fdopen(tempfd, "w")) == NULL)) {
1519	    unqueue_signals();
1520	    zwarnnam("fc", "can't open temp file: %e", errno);
1521	} else {
1522	    /*
1523	     * Nasty behaviour results if we use the current history
1524	     * line here.  Treat it as if it doesn't exist, unless
1525	     * that gives us an empty range.
1526	     */
1527	    if (last >= curhist) {
1528		last = curhist - 1;
1529		if (first > last) {
1530		    unqueue_signals();
1531		    zwarnnam("fc",
1532		      "current history line would recurse endlessly, aborted");
1533		    fclose(out);
1534		    unlink(fil);
1535		    return 1;
1536		}
1537	    }
1538	    ops->ind['n'] = 1;	/* No line numbers here. */
1539	    if (!fclist(out, ops, first, last, asgf, pprog, 1)) {
1540		char *editor;
1541
1542		if (func == BIN_R)
1543		    editor = "-";
1544		else if (OPT_HASARG(ops, 'e'))
1545		    editor = OPT_ARG(ops, 'e');
1546		else
1547		    editor = getsparam("FCEDIT");
1548		if (!editor)
1549		    editor = getsparam("EDITOR");
1550		if (!editor)
1551		    editor = DEFAULT_FCEDIT;
1552
1553		unqueue_signals();
1554		if (fcedit(editor, fil)) {
1555		    if (stuff(fil))
1556			zwarnnam("fc", "%e: %s", errno, s);
1557		    else {
1558			loop(0,1);
1559			retval = lastval;
1560		    }
1561		}
1562	    } else
1563		unqueue_signals();
1564	}
1565	unlink(fil);
1566    }
1567    return retval;
1568}
1569
1570/* History handling functions: these are called by ZLE, as well as  *
1571 * the actual builtins.  fcgetcomm() gets a history line, specified *
1572 * either by number or leading string.  fcsubs() performs a given   *
1573 * set of simple old=new substitutions on a given command line.     *
1574 * fclist() outputs a given range of history lines to a text file.  */
1575
1576/* get the history event associated with s */
1577
1578/**/
1579static zlong
1580fcgetcomm(char *s)
1581{
1582    zlong cmd;
1583
1584    /* First try to match a history number.  Negative *
1585     * numbers indicate reversed numbering.           */
1586    if ((cmd = atoi(s)) != 0 || *s == '0') {
1587	if (cmd < 0)
1588	    cmd = addhistnum(curline.histnum,cmd,HIST_FOREIGN);
1589	if (cmd < 0)
1590	    cmd = 0;
1591	return cmd;
1592    }
1593    /* not a number, so search by string */
1594    cmd = hcomsearch(s);
1595    if (cmd == -1)
1596	zwarnnam("fc", "event not found: %s", s);
1597    return cmd;
1598}
1599
1600/* Perform old=new substitutions.  Uses the asgment structure from zsh.h, *
1601 * which is essentially a linked list of string,replacement pairs.       */
1602
1603/**/
1604static int
1605fcsubs(char **sp, struct asgment *sub)
1606{
1607    char *oldstr, *newstr, *oldpos, *newpos, *newmem, *s = *sp;
1608    int subbed = 0;
1609
1610    /* loop through the linked list */
1611    while (sub) {
1612	oldstr = sub->name;
1613	newstr = sub->value;
1614	sub = sub->next;
1615	oldpos = s;
1616	/* loop over occurences of oldstr in s, replacing them with newstr */
1617	while ((newpos = (char *)strstr(oldpos, oldstr))) {
1618	    newmem = (char *) zhalloc(1 + (newpos - s)
1619				      + strlen(newstr) + strlen(newpos + strlen(oldstr)));
1620	    ztrncpy(newmem, s, newpos - s);
1621	    strcat(newmem, newstr);
1622	    oldpos = newmem + strlen(newmem);
1623	    strcat(newmem, newpos + strlen(oldstr));
1624	    s = newmem;
1625	    subbed = 1;
1626	}
1627    }
1628    *sp = s;
1629    return subbed;
1630}
1631
1632/* Print a series of history events to a file.  The file pointer is     *
1633 * given by f, and the required range of events by first and last.      *
1634 * subs is an optional list of foo=bar substitutions to perform on the  *
1635 * history lines before output.  com is an optional comp structure      *
1636 * that the history lines are required to match.  n, r, D and d are     *
1637 * options: n indicates that each line should be numbered.  r indicates *
1638 * that the lines should be output in reverse order (newest first).     *
1639 * D indicates that the real time taken by each command should be       *
1640 * output.  d indicates that the time of execution of each command      *
1641 * should be output; d>1 means that the date should be output too; d>3  *
1642 * means that mm/dd/yyyy form should be used for the dates, as opposed  *
1643 * to dd.mm.yyyy form; d>7 means that yyyy-mm-dd form should be used.   */
1644
1645/**/
1646static int
1647fclist(FILE *f, Options ops, zlong first, zlong last,
1648       struct asgment *subs, Patprog pprog, int is_command)
1649{
1650    int fclistdone = 0;
1651    zlong tmp;
1652    char *s, *tdfmt, *timebuf;
1653    Histent ent;
1654
1655    /* reverse range if required */
1656    if (OPT_ISSET(ops,'r')) {
1657	tmp = last;
1658	last = first;
1659	first = tmp;
1660    }
1661    if (is_command && first > last) {
1662	zwarnnam("fc", "history events can't be executed backwards, aborted");
1663	if (f != stdout)
1664	    fclose(f);
1665	return 1;
1666    }
1667    /* suppress "no substitution" warning if no substitution is requested */
1668    if (!subs)
1669	fclistdone = 1;
1670
1671    ent = gethistent(first, first < last? GETHIST_DOWNWARD : GETHIST_UPWARD);
1672    if (!ent || (first < last? ent->histnum > last : ent->histnum < last)) {
1673	if (first == last) {
1674	    char buf[DIGBUFSIZE];
1675	    convbase(buf, first, 10);
1676	    zwarnnam("fc", "no such event: %s", buf);
1677	} else
1678	    zwarnnam("fc", "no events in that range");
1679	if (f != stdout)
1680	    fclose(f);
1681	return 1;
1682    }
1683
1684    if (OPT_ISSET(ops,'d') || OPT_ISSET(ops,'f') ||
1685	OPT_ISSET(ops,'E') || OPT_ISSET(ops,'i') ||
1686	OPT_ISSET(ops,'t')) {
1687	if (OPT_ISSET(ops,'t')) {
1688	    tdfmt = OPT_ARG(ops,'t');
1689	} else if (OPT_ISSET(ops,'i')) {
1690	    tdfmt = "%Y-%m-%d %H:%M";
1691	} else if (OPT_ISSET(ops,'E')) {
1692	    tdfmt = "%f.%-m.%Y %H:%M";
1693	} else if (OPT_ISSET(ops,'f')) {
1694	    tdfmt = "%-m/%f/%Y %H:%M";
1695	} else {
1696	    tdfmt = "%H:%M";
1697	}
1698	timebuf = zhalloc(256);
1699    } else {
1700	tdfmt = timebuf = NULL;
1701    }
1702
1703    for (;;) {
1704	s = dupstring(ent->node.nam);
1705	/* this if does the pattern matching, if required */
1706	if (!pprog || pattry(pprog, s)) {
1707	    /* perform substitution */
1708	    fclistdone |= fcsubs(&s, subs);
1709
1710	    /* do numbering */
1711	    if (!OPT_ISSET(ops,'n')) {
1712		char buf[DIGBUFSIZE];
1713		convbase(buf, ent->histnum, 10);
1714		fprintf(f, "%5s%c ", buf,
1715			ent->node.flags & HIST_FOREIGN ? '*' : ' ');
1716	    }
1717	    /* output actual time (and possibly date) of execution of the
1718	       command, if required */
1719	    if (tdfmt != NULL) {
1720		struct tm *ltm;
1721		ltm = localtime(&ent->stim);
1722		if (ztrftime(timebuf, 256, tdfmt, ltm))
1723		    fprintf(f, "%s  ", timebuf);
1724	    }
1725	    /* display the time taken by the command, if required */
1726	    if (OPT_ISSET(ops,'D')) {
1727		long diff;
1728		diff = (ent->ftim) ? ent->ftim - ent->stim : 0;
1729		fprintf(f, "%ld:%02ld  ", diff / 60, diff % 60);
1730	    }
1731
1732	    /* output the command */
1733	    if (f == stdout) {
1734		nicezputs(s, f);
1735		putc('\n', f);
1736	    } else {
1737		int len;
1738		unmetafy(s, &len);
1739		fwrite(s, 1, len, f);
1740		putc('\n', f);
1741	    }
1742	}
1743	/* move on to the next history line, or quit the loop */
1744	if (first < last) {
1745	    if (!(ent = down_histent(ent)) || ent->histnum > last)
1746		break;
1747	}
1748	else {
1749	    if (!(ent = up_histent(ent)) || ent->histnum < last)
1750		break;
1751	}
1752    }
1753
1754    /* final processing */
1755    if (f != stdout)
1756	fclose(f);
1757    if (!fclistdone) {
1758	zwarnnam("fc", "no substitutions performed");
1759	return 1;
1760    }
1761    return 0;
1762}
1763
1764/* edit a history file */
1765
1766/**/
1767static int
1768fcedit(char *ename, char *fn)
1769{
1770    char *s;
1771
1772    if (!strcmp(ename, "-"))
1773	return 1;
1774
1775    s = tricat(ename, " ", fn);
1776    execstring(s, 1, 0, "fc");
1777    zsfree(s);
1778
1779    return !lastval;
1780}
1781
1782/**** parameter builtins ****/
1783
1784/* Separate an argument into name=value parts, returning them in an     *
1785 * asgment structure.  Because the asgment structure used is global,    *
1786 * only one of these can be active at a time.  The string s gets placed *
1787 * in this global structure, so it needs to be in permanent memory.     */
1788
1789/**/
1790static Asgment
1791getasg(char *s)
1792{
1793    static struct asgment asg;
1794
1795    /* sanity check for valid argument */
1796    if (!s)
1797	return NULL;
1798
1799    /* check if name is empty */
1800    if (*s == '=') {
1801	zerr("bad assignment");
1802	return NULL;
1803    }
1804    asg.name = s;
1805
1806    /* search for `=' */
1807    for (; *s && *s != '='; s++);
1808
1809    /* found `=', so return with a value */
1810    if (*s) {
1811	*s = '\0';
1812	asg.value = s + 1;
1813    } else {
1814	/* didn't find `=', so we only have a name */
1815	asg.value = NULL;
1816    }
1817    return &asg;
1818}
1819
1820/* for new special parameters */
1821enum {
1822    NS_NONE,
1823    NS_NORMAL,
1824    NS_SECONDS
1825};
1826
1827static const struct gsu_scalar tiedarr_gsu =
1828{ tiedarrgetfn, tiedarrsetfn, tiedarrunsetfn };
1829
1830/* Install a base if we are turning on a numeric option with an argument */
1831
1832static int
1833typeset_setbase(const char *name, Param pm, Options ops, int on, int always)
1834{
1835    char *arg = NULL;
1836
1837    if ((on & PM_INTEGER) && OPT_HASARG(ops,'i'))
1838	arg = OPT_ARG(ops,'i');
1839    else if ((on & PM_EFLOAT) && OPT_HASARG(ops,'E'))
1840	arg = OPT_ARG(ops,'E');
1841    else if ((on & PM_FFLOAT) && OPT_HASARG(ops,'F'))
1842	arg = OPT_ARG(ops,'F');
1843
1844    if (arg) {
1845	char *eptr;
1846	pm->base = (int)zstrtol(arg, &eptr, 10);
1847	if (*eptr) {
1848	    if (on & PM_INTEGER)
1849		zwarnnam(name, "bad base value: %s", arg);
1850	    else
1851		zwarnnam(name, "bad precision value: %s", arg);
1852	    return 1;
1853	}
1854	if (pm->base < 2 || pm->base > 36) {
1855	    zwarnnam(name, "invalid base (must be 2 to 36 inclusive): %d",
1856		     pm->base);
1857	    return 1;
1858	}
1859    } else if (always)
1860	pm->base = 0;
1861
1862    return 0;
1863}
1864
1865/* Install a width if we are turning on a padding option with an argument */
1866
1867static int
1868typeset_setwidth(const char * name, Param pm, Options ops, int on, int always)
1869{
1870    char *arg = NULL;
1871
1872    if ((on & PM_LEFT) && OPT_HASARG(ops,'L'))
1873	arg = OPT_ARG(ops,'L');
1874    else if ((on & PM_RIGHT_B) && OPT_HASARG(ops,'R'))
1875	arg = OPT_ARG(ops,'R');
1876    else if ((on & PM_RIGHT_Z) && OPT_HASARG(ops,'Z'))
1877	arg = OPT_ARG(ops,'Z');
1878
1879    if (arg) {
1880	char *eptr;
1881	pm->width = (int)zstrtol(arg, &eptr, 10);
1882	if (*eptr) {
1883	    zwarnnam(name, "bad width value: %s", arg);
1884	    return 1;
1885	}
1886    } else if (always)
1887	pm->width = 0;
1888
1889    return 0;
1890}
1891
1892/* function to set a single parameter */
1893
1894/**/
1895static Param
1896typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
1897	       int on, int off, int roff, char *value, Param altpm,
1898	       Options ops, int joinchar)
1899{
1900    int usepm, tc, keeplocal = 0, newspecial = NS_NONE, readonly;
1901    char *subscript;
1902
1903    /*
1904     * Do we use the existing pm?  Note that this isn't the end of the
1905     * story, because if we try and create a new pm at the same
1906     * locallevel as an unset one we use the pm struct anyway: that's
1907     * handled in createparam().  Here we just avoid using it for the
1908     * present tests if it's unset.
1909     */
1910    usepm = pm && !(pm->node.flags & PM_UNSET);
1911
1912    /*
1913     * We need to compare types with an existing pm if special,
1914     * even if that's unset
1915     */
1916    if (pm && (pm->node.flags & PM_SPECIAL))
1917	usepm = 1;
1918
1919    /*
1920     * Don't use an existing param if
1921     *   - the local level has changed, and
1922     *   - we are really locallizing the parameter
1923     */
1924    if (usepm && locallevel != pm->level && (on & PM_LOCAL)) {
1925	/*
1926	 * If the original parameter was special and we're creating
1927	 * a new one, we need to keep it special.
1928	 *
1929	 * The -h (hide) flag prevents an existing special being made
1930	 * local.  It can be applied either to the special or in the
1931	 * typeset/local statement for the local variable.
1932	 */
1933	if ((pm->node.flags & PM_SPECIAL)
1934	    && !(on & PM_HIDE) && !(pm->node.flags & PM_HIDE & ~off))
1935	    newspecial = NS_NORMAL;
1936	usepm = 0;
1937    }
1938
1939    /* attempting a type conversion, or making a tied colonarray? */
1940    tc = 0;
1941    if (usepm || newspecial != NS_NONE) {
1942	int chflags = ((off & pm->node.flags) | (on & ~pm->node.flags)) &
1943	    (PM_INTEGER|PM_EFLOAT|PM_FFLOAT|PM_HASHED|
1944	     PM_ARRAY|PM_TIED|PM_AUTOLOAD);
1945	/* keep the parameter if just switching between floating types */
1946	if ((tc = chflags && chflags != (PM_EFLOAT|PM_FFLOAT)))
1947	    usepm = 0;
1948    }
1949
1950    /*
1951     * Extra checks if converting the type of a parameter, or if
1952     * trying to remove readonlyness.  It's dangerous doing either
1953     * with a special or a parameter which isn't loaded yet (which
1954     * may be special when it is loaded; we can't tell yet).
1955     */
1956    if ((readonly =
1957	 ((usepm || newspecial != NS_NONE) &&
1958	  (off & pm->node.flags & PM_READONLY))) ||
1959	tc) {
1960	if (pm->node.flags & PM_SPECIAL) {
1961	    int err = 1;
1962	    if (!readonly && !strcmp(pname, "SECONDS"))
1963	    {
1964		/*
1965		 * We allow SECONDS to change type between integer
1966		 * and floating point.  If we are creating a new
1967		 * local copy we check the type here and allow
1968		 * a new special to be created with that type.
1969		 * We then need to make sure the correct type
1970		 * for the special is restored at the end of the scope.
1971		 * If we are changing the type of an existing
1972		 * parameter, we do the whole thing here.
1973		 */
1974		if (newspecial != NS_NONE)
1975		{
1976		    /*
1977		     * The first test allows `typeset' to copy the
1978		     * existing type.  This is the usual behaviour
1979		     * for making special parameters local.
1980		     */
1981		    if (PM_TYPE(on) == 0 || PM_TYPE(on) == PM_INTEGER ||
1982			PM_TYPE(on) == PM_FFLOAT || PM_TYPE(on) == PM_EFLOAT)
1983		    {
1984			newspecial = NS_SECONDS;
1985			err = 0;	/* and continue */
1986			tc = 0;	/* but don't do a normal conversion */
1987		    }
1988		} else if (!setsecondstype(pm, on, off)) {
1989		    if (value && !(pm = setsparam(pname, ztrdup(value))))
1990			return NULL;
1991		    usepm = 1;
1992		    err = 0;
1993		}
1994	    }
1995	    if (err)
1996	    {
1997		zerrnam(cname, "%s: can't change type of a special parameter",
1998			pname);
1999		return NULL;
2000	    }
2001	} else if (pm->node.flags & PM_AUTOLOAD) {
2002	    zerrnam(cname, "%s: can't change type of autoloaded parameter",
2003		    pname);
2004	    return NULL;
2005	}
2006    }
2007    else if (newspecial != NS_NONE && strcmp(pname, "SECONDS") == 0)
2008	newspecial = NS_SECONDS;
2009
2010    /*
2011     * A parameter will be local if
2012     * 1. we are re-using an existing local parameter
2013     *    or
2014     * 2. we are not using an existing parameter, but
2015     *   i. there is already a parameter, which will be hidden
2016     *     or
2017     *   ii. we are creating a new local parameter
2018     */
2019    if (usepm) {
2020	on &= ~PM_LOCAL;
2021	if (!on && !roff && !value) {
2022	    if (OPT_ISSET(ops,'p'))
2023		paramtab->printnode(&pm->node, PRINT_TYPESET);
2024	    else if (!OPT_ISSET(ops,'g') &&
2025		     (unset(TYPESETSILENT) || OPT_ISSET(ops,'m')))
2026		paramtab->printnode(&pm->node, PRINT_INCLUDEVALUE);
2027	    return pm;
2028	}
2029	if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
2030	    zerrnam(cname, "%s: restricted", pname);
2031	    return pm;
2032	}
2033	if ((on & PM_UNIQUE) && !(pm->node.flags & PM_READONLY & ~off)) {
2034	    Param apm;
2035	    char **x;
2036	    if (PM_TYPE(pm->node.flags) == PM_ARRAY) {
2037		x = (*pm->gsu.a->getfn)(pm);
2038		uniqarray(x);
2039		if (pm->node.flags & PM_SPECIAL) {
2040		    if (zheapptr(x))
2041			x = zarrdup(x);
2042		    (*pm->gsu.a->setfn)(pm, x);
2043		} else if (pm->ename && x)
2044		    arrfixenv(pm->ename, x);
2045	    } else if (PM_TYPE(pm->node.flags) == PM_SCALAR && pm->ename &&
2046		       (apm =
2047			(Param) paramtab->getnode(paramtab, pm->ename))) {
2048		x = (*apm->gsu.a->getfn)(apm);
2049		uniqarray(x);
2050		if (x)
2051		    arrfixenv(pm->node.nam, x);
2052	    }
2053	}
2054	pm->node.flags = (pm->node.flags | (on & ~PM_READONLY)) & ~(off | PM_UNSET);
2055	if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) {
2056	    if (typeset_setwidth(cname, pm, ops, on, 0))
2057		return NULL;
2058	}
2059	if (on & (PM_INTEGER | PM_EFLOAT | PM_FFLOAT)) {
2060	    if (typeset_setbase(cname, pm, ops, on, 0))
2061		return NULL;
2062	}
2063	if (!(pm->node.flags & (PM_ARRAY|PM_HASHED))) {
2064	    if (pm->node.flags & PM_EXPORTED) {
2065		if (!(pm->node.flags & PM_UNSET) && !pm->env && !value)
2066		    addenv(pm, getsparam(pname));
2067	    } else if (pm->env && !(pm->node.flags & PM_HASHELEM))
2068		delenv(pm);
2069	    if (value && !(pm = setsparam(pname, ztrdup(value))))
2070		return NULL;
2071	} else if (value) {
2072	    zwarnnam(cname, "can't assign new value for array %s", pname);
2073	    return NULL;
2074	}
2075	pm->node.flags |= (on & PM_READONLY);
2076	if (OPT_ISSET(ops,'p'))
2077	    paramtab->printnode(&pm->node, PRINT_TYPESET);
2078	return pm;
2079    }
2080
2081    /*
2082     * We're here either because we're creating a new parameter,
2083     * or we're adding a parameter at a different local level,
2084     * or we're converting the type of a parameter.  In the
2085     * last case only, we need to delete the old parameter.
2086     */
2087    if (tc) {
2088	/* Maintain existing readonly/exported status... */
2089	on |= ~off & (PM_READONLY|PM_EXPORTED) & pm->node.flags;
2090	/* ...but turn off existing readonly so we can delete it */
2091	pm->node.flags &= ~PM_READONLY;
2092	/*
2093	 * If we're just changing the type, we should keep the
2094	 * variable at the current level of localness.
2095	 */
2096	keeplocal = pm->level;
2097	/*
2098	 * Try to carry over a value, but not when changing from,
2099	 * to, or between non-scalar types.
2100	 */
2101	if (!value && !((pm->node.flags|on) & (PM_ARRAY|PM_HASHED)))
2102	    value = dupstring(getsparam(pname));
2103	/* pname may point to pm->nam which is about to disappear */
2104	pname = dupstring(pname);
2105	unsetparam_pm(pm, 0, 1);
2106    }
2107
2108    if (newspecial != NS_NONE) {
2109	Param tpm, pm2;
2110	if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
2111	    zerrnam(cname, "%s: restricted", pname);
2112	    return pm;
2113	}
2114	/*
2115	 * For specials, we keep the same struct but zero everything.
2116	 * Maybe it would be easier to create a new struct but copy
2117	 * the get/set methods.
2118	 */
2119	tpm = (Param) zshcalloc(sizeof *tpm);
2120
2121	tpm->node.nam = pm->node.nam;
2122	if (pm->ename &&
2123	    (pm2 = (Param) paramtab->getnode(paramtab, pm->ename)) &&
2124	    pm2->level == locallevel) {
2125	    /* This is getting silly, but anyway:  if one of a path/PATH
2126	     * pair has already been made local at the current level, we
2127	     * have to make sure that the other one does not have its value
2128	     * saved:  since that comes from an internal variable it will
2129	     * already reflect the local value, so restoring it on exit
2130	     * would be wrong.
2131	     *
2132	     * This problem is also why we make sure we have a copy
2133	     * of the environment entry in tpm->env, rather than relying
2134	     * on the restored value to provide it.
2135	     */
2136	    tpm->node.flags = pm->node.flags | PM_NORESTORE;
2137	} else {
2138	    copyparam(tpm, pm, 1);
2139	}
2140	tpm->old = pm->old;
2141	tpm->level = pm->level;
2142	tpm->base = pm->base;
2143	tpm->width = pm->width;
2144	if (pm->env)
2145	    delenv(pm);
2146	tpm->env = NULL;
2147
2148	pm->old = tpm;
2149	/*
2150	 * The remaining on/off flags should be harmless to use,
2151	 * because we've checked for unpleasant surprises above.
2152	 */
2153	pm->node.flags = (PM_TYPE(pm->node.flags) | on | PM_SPECIAL) & ~off;
2154	/*
2155	 * Readonlyness of special parameters must be preserved.
2156	 */
2157	pm->node.flags |= tpm->node.flags & PM_READONLY;
2158	if (newspecial == NS_SECONDS) {
2159	    /* We save off the raw internal value of the SECONDS var */
2160	    tpm->u.dval = getrawseconds();
2161	    setsecondstype(pm, on, off);
2162	}
2163
2164	/*
2165	 * Final tweak: if we've turned on one of the flags with
2166	 * numbers, we should use the appropriate integer.
2167	 */
2168	if (on & (PM_LEFT|PM_RIGHT_B|PM_RIGHT_Z)) {
2169	    if (typeset_setwidth(cname, pm, ops, on, 1))
2170		return NULL;
2171	}
2172	if (on & (PM_INTEGER|PM_EFLOAT|PM_FFLOAT)) {
2173	    if (typeset_setbase(cname, pm, ops, on, 1))
2174		return NULL;
2175	}
2176    } else if ((subscript = strchr(pname, '['))) {
2177	if (on & PM_READONLY) {
2178	    zerrnam(cname,
2179		    "%s: can't create readonly array elements", pname);
2180	    return NULL;
2181	} else if (on & PM_LOCAL) {
2182	    *subscript = 0;
2183	    pm = (Param) (paramtab == realparamtab ?
2184			  gethashnode2(paramtab, pname) :
2185			  paramtab->getnode(paramtab, pname));
2186	    *subscript = '[';
2187	    if (!pm || pm->level != locallevel) {
2188		zerrnam(cname,
2189			"%s: can't create local array elements", pname);
2190		return NULL;
2191	    }
2192	}
2193	if (PM_TYPE(on) == PM_SCALAR) {
2194	    /*
2195	     * This will either complain about bad identifiers, or will set
2196	     * a hash element or array slice.  This once worked by accident,
2197	     * creating a stray parameter along the way via createparam(),
2198	     * now called below in the isident() branch.
2199	     */
2200	    if (!(pm = setsparam(pname, ztrdup(value ? value : ""))))
2201		return NULL;
2202	    value = NULL;
2203	    keeplocal = 0;
2204	    on = pm->node.flags;
2205	} else {
2206	    zerrnam(cname,
2207		    "%s: array elements must be scalar", pname);
2208	    return NULL;
2209	}
2210    }
2211    /*
2212     * As we can hide existing parameters, we allow a name if
2213     * it's not a normal identifier but is one of the special
2214     * set found in the parameter table.  The second test is
2215     * because we can set individual positional parameters;
2216     * however "0" is not a positional parameter and is OK.
2217     *
2218     * It would be neater to extend isident() and be clearer
2219     * about where we allow various parameter types.  It's
2220     * not entirely clear to me isident() should reject
2221     * specially named parameters given that it accepts digits.
2222     */
2223    else if ((isident(pname) || paramtab->getnode(paramtab, pname))
2224	     && (!idigit(*pname) || !strcmp(pname, "0"))) {
2225	/*
2226	 * Create a new node for a parameter with the flags in `on' minus the
2227	 * readonly flag
2228	 */
2229	pm = createparam(pname, on & ~PM_READONLY);
2230	DPUTS(!pm, "BUG: parameter not created");
2231	if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) {
2232	    if (typeset_setwidth(cname, pm, ops, on, 0))
2233		return NULL;
2234	}
2235	if (on & (PM_INTEGER | PM_EFLOAT | PM_FFLOAT)) {
2236	    if (typeset_setbase(cname, pm, ops, on, 0))
2237		return NULL;
2238	}
2239    } else {
2240	if (idigit(*pname))
2241	    zerrnam(cname, "not an identifier: %s", pname);
2242	else
2243	    zerrnam(cname, "not valid in this context: %s", pname);
2244	return NULL;
2245    }
2246
2247    if (altpm && PM_TYPE(pm->node.flags) == PM_SCALAR) {
2248	/*
2249	 * It seems safer to set this here than in createparam(),
2250	 * to make sure we only ever use the colonarr functions
2251	 * when u.data is correctly set.
2252	 */
2253	struct tieddata *tdp = (struct tieddata *)
2254	    zalloc(sizeof(struct tieddata));
2255	if (!tdp)
2256	    return NULL;
2257	tdp->joinchar = joinchar;
2258	tdp->arrptr = &altpm->u.arr;
2259
2260	pm->gsu.s = &tiedarr_gsu;
2261	pm->u.data = tdp;
2262    }
2263
2264    if (keeplocal)
2265	pm->level = keeplocal;
2266    else if (on & PM_LOCAL)
2267	pm->level = locallevel;
2268    if (value && !(pm->node.flags & (PM_ARRAY|PM_HASHED))) {
2269	Param ipm = pm;
2270	if (!(pm = setsparam(pname, ztrdup(value))))
2271	    return NULL;
2272	if (pm != ipm) {
2273	    DPUTS(ipm->node.flags != pm->node.flags,
2274		  "BUG: parameter recreated with wrong flags");
2275	    unsetparam_pm(ipm, 0, 1);
2276	}
2277    } else if (newspecial != NS_NONE &&
2278	       !(pm->old->node.flags & (PM_NORESTORE|PM_READONLY))) {
2279	/*
2280	 * We need to use the special setting function to re-initialise
2281	 * the special parameter to empty.
2282	 */
2283	switch (PM_TYPE(pm->node.flags)) {
2284	case PM_SCALAR:
2285	    pm->gsu.s->setfn(pm, ztrdup(""));
2286	    break;
2287	case PM_INTEGER:
2288	    pm->gsu.i->setfn(pm, 0);
2289	    break;
2290	case PM_EFLOAT:
2291	case PM_FFLOAT:
2292	    pm->gsu.f->setfn(pm, 0.0);
2293	    break;
2294	case PM_ARRAY:
2295	    pm->gsu.a->setfn(pm, mkarray(NULL));
2296	    break;
2297	case PM_HASHED:
2298	    pm->gsu.h->setfn(pm, newparamtable(17, pm->node.nam));
2299	    break;
2300	}
2301    }
2302    pm->node.flags |= (on & PM_READONLY);
2303    if (value && (pm->node.flags & (PM_ARRAY|PM_HASHED))) {
2304	zerrnam(cname, "%s: can't assign initial value for array", pname);
2305	/* the only safe thing to do here seems to be unset the param */
2306	unsetparam_pm(pm, 0, 1);
2307	return NULL;
2308    }
2309
2310    if (OPT_ISSET(ops,'p'))
2311	paramtab->printnode(&pm->node, PRINT_TYPESET);
2312
2313    return pm;
2314}
2315
2316/* declare, export, integer, local, readonly, typeset */
2317
2318/**/
2319int
2320bin_typeset(char *name, char **argv, Options ops, int func)
2321{
2322    Param pm;
2323    Asgment asg;
2324    Patprog pprog;
2325    char *optstr = TYPESET_OPTSTR;
2326    int on = 0, off = 0, roff, bit = PM_ARRAY;
2327    int i;
2328    int returnval = 0, printflags = 0;
2329
2330    /* hash -f is really the builtin `functions' */
2331    if (OPT_ISSET(ops,'f'))
2332	return bin_functions(name, argv, ops, func);
2333
2334    /* Translate the options into PM_* flags.   *
2335     * Unfortunately, this depends on the order *
2336     * these flags are defined in zsh.h         */
2337    for (; *optstr; optstr++, bit <<= 1)
2338    {
2339	int optval = STOUC(*optstr);
2340	if (OPT_MINUS(ops,optval))
2341	    on |= bit;
2342	else if (OPT_PLUS(ops,optval))
2343	    off |= bit;
2344    }
2345    roff = off;
2346
2347    /* Sanity checks on the options.  Remove conflicting options. */
2348    if (on & PM_FFLOAT) {
2349	off |= PM_UPPER | PM_ARRAY | PM_HASHED | PM_INTEGER | PM_EFLOAT;
2350	/* Allow `float -F' to work even though float sets -E by default */
2351	on &= ~PM_EFLOAT;
2352    }
2353    if (on & PM_EFLOAT)
2354	off |= PM_UPPER | PM_ARRAY | PM_HASHED | PM_INTEGER | PM_FFLOAT;
2355    if (on & PM_INTEGER)
2356	off |= PM_UPPER | PM_ARRAY | PM_HASHED | PM_EFLOAT | PM_FFLOAT;
2357    /*
2358     * Allowing -Z with -L is a feature: left justify, suppressing
2359     * leading zeroes.
2360     */
2361    if (on & (PM_LEFT|PM_RIGHT_Z))
2362	off |= PM_RIGHT_B;
2363    if (on & PM_RIGHT_B)
2364	off |= PM_LEFT | PM_RIGHT_Z;
2365    if (on & PM_UPPER)
2366	off |= PM_LOWER;
2367    if (on & PM_LOWER)
2368	off |= PM_UPPER;
2369    if (on & PM_HASHED)
2370	off |= PM_ARRAY;
2371    if (on & PM_TIED)
2372	off |= PM_INTEGER | PM_EFLOAT | PM_FFLOAT | PM_ARRAY | PM_HASHED;
2373
2374    on &= ~off;
2375
2376    queue_signals();
2377
2378    /* Given no arguments, list whatever the options specify. */
2379    if (OPT_ISSET(ops,'p'))
2380	printflags |= PRINT_TYPESET;
2381    if (!*argv) {
2382	if (!OPT_ISSET(ops,'p')) {
2383	    if (!(on|roff))
2384		printflags |= PRINT_TYPE;
2385	    if (roff || OPT_ISSET(ops,'+'))
2386		printflags |= PRINT_NAMEONLY;
2387	}
2388	scanhashtable(paramtab, 1, on|roff, 0, paramtab->printnode, printflags);
2389	unqueue_signals();
2390	return 0;
2391    }
2392
2393    if (!(OPT_ISSET(ops,'g') || OPT_ISSET(ops,'x') || OPT_ISSET(ops,'m')) ||
2394	OPT_PLUS(ops,'g') || *name == 'l' ||
2395	(!isset(GLOBALEXPORT) && !OPT_ISSET(ops,'g')))
2396	on |= PM_LOCAL;
2397
2398    if (on & PM_TIED) {
2399	Param apm;
2400	struct asgment asg0;
2401	char *oldval = NULL;
2402	int joinchar;
2403
2404	if (OPT_ISSET(ops,'m')) {
2405	    zwarnnam(name, "incompatible options for -T");
2406	    unqueue_signals();
2407	    return 1;
2408	}
2409	on &= ~off;
2410	if (!argv[1] || (argv[2] && argv[3])) {
2411	    zwarnnam(name, "-T requires names of scalar and array");
2412	    unqueue_signals();
2413	    return 1;
2414	}
2415
2416	/*
2417	 * Third argument, if given, is character used to join
2418	 * the elements of the array in the scalar.
2419	 */
2420	if (!argv[2])
2421	    joinchar = ':';
2422	else if (!*argv[2])
2423	    joinchar = 0;
2424	else if (*argv[2] == Meta)
2425	    joinchar = argv[2][1] ^ 32;
2426	else
2427	    joinchar = *argv[2];
2428
2429	if (!(asg = getasg(argv[0]))) {
2430	    unqueue_signals();
2431	    return 1;
2432	}
2433	asg0 = *asg;
2434	if (!(asg = getasg(argv[1]))) {
2435	    unqueue_signals();
2436	    return 1;
2437	}
2438	if (!strcmp(asg0.name, asg->name)) {
2439	    unqueue_signals();
2440	    zerrnam(name, "can't tie a variable to itself: %s", asg0.name);
2441	    return 1;
2442	}
2443	if (strchr(asg0.name, '[') || strchr(asg->name, '[')) {
2444	    unqueue_signals();
2445	    zerrnam(name, "can't tie array elements: %s", asg0.name);
2446	    return 1;
2447	}
2448	/*
2449	 * Keep the old value of the scalar.  We need to do this
2450	 * here as if it is already tied to the same array it
2451	 * will be unset when we retie the array.  This is all
2452	 * so that typeset -T is idempotent.
2453	 *
2454	 * We also need to remember here whether the damn thing is
2455	 * exported and pass that along.  Isn't the world complicated?
2456	 */
2457	if ((pm = (Param) paramtab->getnode(paramtab, asg0.name))
2458	    && !(pm->node.flags & PM_UNSET)
2459	    && (locallevel == pm->level || !(on & PM_LOCAL))) {
2460	    if (pm->node.flags & PM_TIED) {
2461		unqueue_signals();
2462		if (!strcmp(asg->name, pm->ename)) {
2463		    /*
2464		     * Already tied in the fashion requested.
2465		     */
2466		    struct tieddata *tdp = (struct tieddata*)pm->u.data;
2467		    /* Update join character */
2468		    tdp->joinchar = joinchar;
2469		    if (asg0.value)
2470			setsparam(asg0.name, ztrdup(asg0.value));
2471		    return 0;
2472		} else {
2473		    zerrnam(name, "can't tie already tied scalar: %s",
2474			    asg0.name);
2475		}
2476		return 1;
2477	    }
2478	    if (!asg0.value && !(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)))
2479		oldval = ztrdup(getsparam(asg0.name));
2480	    on |= (pm->node.flags & PM_EXPORTED);
2481	}
2482	/*
2483	 * Create the tied array; this is normal except that
2484	 * it has the PM_TIED flag set.  Do it first because
2485	 * we need the address.
2486	 */
2487	if (!(apm=typeset_single(name, asg->name,
2488				 (Param)paramtab->getnode(paramtab,
2489							  asg->name),
2490				 func, (on | PM_ARRAY) & ~PM_EXPORTED,
2491				 off, roff, asg->value, NULL, ops, 0))) {
2492	    unqueue_signals();
2493	    return 1;
2494	}
2495	/*
2496	 * Create the tied colonarray.  We make it as a normal scalar
2497	 * and fix up the oddities later.
2498	 */
2499	if (!(pm=typeset_single(name, asg0.name,
2500				(Param)paramtab->getnode(paramtab,
2501							 asg0.name),
2502				func, on, off, roff, asg0.value, apm,
2503				ops, joinchar))) {
2504	    if (oldval)
2505		zsfree(oldval);
2506	    unsetparam_pm(apm, 1, 1);
2507	    unqueue_signals();
2508	    return 1;
2509	}
2510
2511	/*
2512	 * pm->ename is only deleted when the struct is, so
2513	 * we need to free it here if it already exists.
2514	 */
2515	if (pm->ename)
2516	    zsfree(pm->ename);
2517	pm->ename = ztrdup(asg->name);
2518	if (apm->ename)
2519	    zsfree(apm->ename);
2520	apm->ename = ztrdup(asg0.name);
2521	if (oldval)
2522	    setsparam(asg0.name, oldval);
2523	unqueue_signals();
2524
2525	return 0;
2526    }
2527    if (off & PM_TIED) {
2528	zerrnam(name, "use unset to remove tied variables");
2529	return 1;
2530    }
2531
2532    /* With the -m option, treat arguments as glob patterns */
2533    if (OPT_ISSET(ops,'m')) {
2534	if (!OPT_ISSET(ops,'p')) {
2535	    if (!(on|roff))
2536		printflags |= PRINT_TYPE;
2537	    if (!on)
2538		printflags |= PRINT_NAMEONLY;
2539	}
2540
2541	while ((asg = getasg(*argv++))) {
2542	    LinkList pmlist = newlinklist();
2543	    LinkNode pmnode;
2544
2545	    tokenize(asg->name);   /* expand argument */
2546	    if (!(pprog = patcompile(asg->name, 0, NULL))) {
2547		untokenize(asg->name);
2548		zwarnnam(name, "bad pattern : %s", argv[-1]);
2549		returnval = 1;
2550		continue;
2551	    }
2552	    if (OPT_PLUS(ops,'m') && !asg->value) {
2553		scanmatchtable(paramtab, pprog, 1, on|roff, 0,
2554			       paramtab->printnode, printflags);
2555		continue;
2556	    }
2557	    /*
2558	     * Search through the parameter table and change all parameters
2559	     * matching the glob pattern to have these flags and/or value.
2560	     * Bad news:  if the parameter gets altered, e.g. by
2561	     * a type conversion, then paramtab can be shifted around,
2562	     * so we need to store the parameters to alter on a separate
2563	     * list for later use.
2564	     */
2565	    for (i = 0; i < paramtab->hsize; i++) {
2566		for (pm = (Param) paramtab->nodes[i]; pm;
2567		     pm = (Param) pm->node.next) {
2568		    if (((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) ||
2569			(pm->node.flags & PM_UNSET))
2570			continue;
2571		    if (pattry(pprog, pm->node.nam))
2572			addlinknode(pmlist, pm);
2573		}
2574	    }
2575	    for (pmnode = firstnode(pmlist); pmnode; incnode(pmnode)) {
2576		pm = (Param) getdata(pmnode);
2577		if (!typeset_single(name, pm->node.nam, pm, func, on, off, roff,
2578				    asg->value, NULL, ops, 0))
2579		    returnval = 1;
2580	    }
2581	}
2582	unqueue_signals();
2583	return returnval;
2584    }
2585
2586    /* Take arguments literally.  Don't glob */
2587    while ((asg = getasg(*argv++))) {
2588	HashNode hn = (paramtab == realparamtab ?
2589		       gethashnode2(paramtab, asg->name) :
2590		       paramtab->getnode(paramtab, asg->name));
2591	if (OPT_ISSET(ops,'p')) {
2592	    if (hn)
2593		printparamnode(hn, printflags);
2594	    else {
2595		zwarnnam(name, "no such variable: %s", asg->name);
2596		returnval = 1;
2597	    }
2598	    continue;
2599	}
2600	if (!typeset_single(name, asg->name, (Param)hn,
2601			    func, on, off, roff, asg->value, NULL,
2602			    ops, 0))
2603	    returnval = 1;
2604    }
2605    unqueue_signals();
2606    return returnval;
2607}
2608
2609/* Helper for bin_functions() when run as "autoload -X" */
2610
2611/**/
2612int
2613eval_autoload(Shfunc shf, char *name, Options ops, int func)
2614{
2615    if (!(shf->node.flags & PM_UNDEFINED))
2616	return 1;
2617
2618    if (shf->funcdef) {
2619	freeeprog(shf->funcdef);
2620	shf->funcdef = &dummy_eprog;
2621    }
2622    if (OPT_MINUS(ops,'X')) {
2623	char *fargv[3];
2624	fargv[0] = name;
2625	fargv[1] = "\"$@\"";
2626	fargv[2] = 0;
2627	shf->funcdef = mkautofn(shf);
2628	return bin_eval(name, fargv, ops, func);
2629    }
2630
2631    return !loadautofn(shf, (OPT_ISSET(ops,'k') ? 2 :
2632			     (OPT_ISSET(ops,'z') ? 0 : 1)), 1);
2633}
2634
2635
2636/* List a user-defined math function. */
2637static void
2638listusermathfunc(MathFunc p)
2639{
2640    int showargs;
2641
2642    if (p->module)
2643	showargs = 3;
2644    else if (p->maxargs != (p->minargs ? p->minargs : -1))
2645	showargs = 2;
2646    else if (p->minargs)
2647	showargs = 1;
2648    else
2649	showargs = 0;
2650
2651    printf("functions -M %s", p->name);
2652    if (showargs) {
2653	printf(" %d", p->minargs);
2654	showargs--;
2655    }
2656    if (showargs) {
2657	printf(" %d", p->maxargs);
2658	showargs--;
2659    }
2660    if (showargs) {
2661	/*
2662	 * function names are not required to consist of ident characters
2663	 */
2664	putchar(' ');
2665	quotedzputs(p->module, stdout);
2666	showargs--;
2667    }
2668    putchar('\n');
2669}
2670
2671
2672/* Display or change the attributes of shell functions.   *
2673 * If called as autoload, it will define a new autoloaded *
2674 * (undefined) shell function.                            */
2675
2676/**/
2677int
2678bin_functions(char *name, char **argv, Options ops, int func)
2679{
2680    Patprog pprog;
2681    Shfunc shf;
2682    int i, returnval = 0;
2683    int on = 0, off = 0, pflags = 0;
2684
2685    /* Do we have any flags defined? */
2686    if (OPT_PLUS(ops,'u'))
2687	off |= PM_UNDEFINED;
2688    else if (OPT_MINUS(ops,'u') || OPT_ISSET(ops,'X'))
2689	on |= PM_UNDEFINED;
2690    if (OPT_MINUS(ops,'U'))
2691	on |= PM_UNALIASED|PM_UNDEFINED;
2692    else if (OPT_PLUS(ops,'U'))
2693	off |= PM_UNALIASED;
2694    if (OPT_MINUS(ops,'t'))
2695	on |= PM_TAGGED;
2696    else if (OPT_PLUS(ops,'t'))
2697	off |= PM_TAGGED;
2698    if (OPT_MINUS(ops,'T'))
2699	on |= PM_TAGGED_LOCAL;
2700    else if (OPT_PLUS(ops,'T'))
2701	off |= PM_TAGGED_LOCAL;
2702    if (OPT_MINUS(ops,'z')) {
2703	on |= PM_ZSHSTORED;
2704	off |= PM_KSHSTORED;
2705    } else if (OPT_PLUS(ops,'z'))
2706	off |= PM_ZSHSTORED;
2707    if (OPT_MINUS(ops,'k')) {
2708	on |= PM_KSHSTORED;
2709	off |= PM_ZSHSTORED;
2710    } else if (OPT_PLUS(ops,'k'))
2711	off |= PM_KSHSTORED;
2712
2713    if ((off & PM_UNDEFINED) || (OPT_ISSET(ops,'k') && OPT_ISSET(ops,'z')) ||
2714	(OPT_MINUS(ops,'X') && (OPT_ISSET(ops,'m') || *argv || !scriptname))) {
2715	zwarnnam(name, "invalid option(s)");
2716	return 1;
2717    }
2718
2719    if (OPT_PLUS(ops,'f') || OPT_ISSET(ops,'+'))
2720	pflags |= PRINT_NAMEONLY;
2721
2722    if (OPT_MINUS(ops,'M') || OPT_PLUS(ops,'M')) {
2723	MathFunc p, q;
2724	/*
2725	 * Add/remove/list function as mathematical.
2726	 */
2727	if (on || off || pflags || OPT_ISSET(ops,'X') || OPT_ISSET(ops,'u')
2728	    || OPT_ISSET(ops,'U') || OPT_ISSET(ops,'w')) {
2729	    zwarnnam(name, "invalid option(s)");
2730	    return 1;
2731	}
2732	if (!*argv) {
2733	    /* List functions. */
2734	    queue_signals();
2735	    for (p = mathfuncs; p; p = p->next)
2736		if (p->flags & MFF_USERFUNC)
2737		    listusermathfunc(p);
2738	    unqueue_signals();
2739	} else if (OPT_ISSET(ops,'m')) {
2740	    /* List matching functions. */
2741	    for (; *argv; argv++) {
2742		tokenize(*argv);
2743		if ((pprog = patcompile(*argv, PAT_STATIC, 0))) {
2744		    queue_signals();
2745		    for (p = mathfuncs, q = NULL; p; q = p, p = p->next) {
2746			MathFunc next;
2747			do {
2748			    next = NULL;
2749			    if ((p->flags & MFF_USERFUNC) &&
2750				pattry(pprog, p->name)) {
2751				if (OPT_PLUS(ops,'M')) {
2752				    next = p->next;
2753				    removemathfunc(q, p);
2754				    p = next;
2755				} else
2756				    listusermathfunc(p);
2757			    }
2758			    /* if we deleted one, retry with the new p */
2759			} while (next);
2760		    }
2761		    unqueue_signals();
2762		} else {
2763		    untokenize(*argv);
2764		    zwarnnam(name, "bad pattern : %s", *argv);
2765		    returnval = 1;
2766		}
2767	    }
2768	} else if (OPT_PLUS(ops,'M')) {
2769	    /* Delete functions. -m is allowed but is handled above. */
2770	    for (; *argv; argv++) {
2771		queue_signals();
2772		for (p = mathfuncs, q = NULL; p; q = p, p = p->next) {
2773		    if (!strcmp(p->name, *argv)) {
2774			if (!(p->flags & MFF_USERFUNC)) {
2775			    zwarnnam(name, "+M %s: is a library function",
2776				     *argv);
2777			    returnval = 1;
2778			    break;
2779			}
2780			removemathfunc(q, p);
2781			break;
2782		    }
2783		}
2784		unqueue_signals();
2785	    }
2786	} else {
2787	    /* Add a function */
2788	    int minargs = 0, maxargs = -1;
2789	    char *funcname = *argv++;
2790	    char *modname = NULL;
2791	    char *ptr;
2792
2793	    ptr = itype_end(funcname, IIDENT, 0);
2794	    if (idigit(*funcname) || funcname == ptr || *ptr) {
2795		zwarnnam(name, "-M %s: bad math function name", funcname);
2796		return 1;
2797	    }
2798
2799	    if (*argv) {
2800		minargs = (int)zstrtol(*argv, &ptr, 0);
2801		if (minargs < 0 || *ptr) {
2802		    zwarnnam(name, "-M: invalid min number of arguments: %s",
2803			     *argv);
2804		    return 1;
2805		}
2806		maxargs = minargs;
2807		argv++;
2808	    }
2809	    if (*argv) {
2810		maxargs = (int)zstrtol(*argv, &ptr, 0);
2811		if (maxargs < -1 ||
2812		    (maxargs != -1 && maxargs < minargs) ||
2813		    *ptr) {
2814		    zwarnnam(name,
2815			     "-M: invalid max number of arguments: %s",
2816			     *argv);
2817		    return 1;
2818		}
2819		argv++;
2820	    }
2821	    if (*argv)
2822		modname = *argv++;
2823	    if (*argv) {
2824		zwarnnam(name, "-M: too many arguments");
2825		return 1;
2826	    }
2827
2828	    p = (MathFunc)zshcalloc(sizeof(struct mathfunc));
2829	    p->name = ztrdup(funcname);
2830	    p->flags = MFF_USERFUNC;
2831	    p->module = modname ? ztrdup(modname) : NULL;
2832	    p->minargs = minargs;
2833	    p->maxargs = maxargs;
2834
2835	    queue_signals();
2836	    for (q = mathfuncs; q; q = q->next) {
2837		if (!strcmp(q->name, funcname)) {
2838		    zwarnnam(name, "-M %s: function already exists",
2839			     funcname);
2840		    zsfree(p->name);
2841		    zsfree(p->module);
2842		    zfree(p, sizeof(struct mathfunc));
2843		    return 1;
2844		}
2845	    }
2846
2847	    p->next = mathfuncs;
2848	    mathfuncs = p;
2849	    unqueue_signals();
2850	}
2851
2852	return returnval;
2853    }
2854
2855    /* If no arguments given, we will print functions.  If flags *
2856     * are given, we will print only functions containing these  *
2857     * flags, else we'll print them all.                         */
2858    if (!*argv) {
2859	int ret = 0;
2860
2861	queue_signals();
2862	if (OPT_MINUS(ops,'X')) {
2863	    if ((shf = (Shfunc) shfunctab->getnode(shfunctab, scriptname))) {
2864		DPUTS(!shf->funcdef,
2865		      "BUG: Calling autoload from empty function");
2866	    } else {
2867		shf = (Shfunc) zshcalloc(sizeof *shf);
2868		shfunctab->addnode(shfunctab, ztrdup(scriptname), shf);
2869	    }
2870	    shf->node.flags = on;
2871	    ret = eval_autoload(shf, scriptname, ops, func);
2872	} else {
2873	    if (OPT_ISSET(ops,'U') && !OPT_ISSET(ops,'u'))
2874		on &= ~PM_UNDEFINED;
2875	    scanhashtable(shfunctab, 1, on|off, DISABLED, shfunctab->printnode,
2876			  pflags);
2877	}
2878	unqueue_signals();
2879	return ret;
2880    }
2881
2882    /* With the -m option, treat arguments as glob patterns */
2883    if (OPT_ISSET(ops,'m')) {
2884	on &= ~PM_UNDEFINED;
2885	for (; *argv; argv++) {
2886	    /* expand argument */
2887	    tokenize(*argv);
2888	    if ((pprog = patcompile(*argv, PAT_STATIC, 0))) {
2889		/* with no options, just print all functions matching the glob pattern */
2890		queue_signals();
2891		if (!(on|off) && !OPT_ISSET(ops,'X')) {
2892		    scanmatchtable(shfunctab, pprog, 1, 0, DISABLED,
2893				   shfunctab->printnode, pflags);
2894		} else {
2895		    /* apply the options to all functions matching the glob pattern */
2896		    for (i = 0; i < shfunctab->hsize; i++) {
2897			for (shf = (Shfunc) shfunctab->nodes[i]; shf;
2898			     shf = (Shfunc) shf->node.next)
2899			    if (pattry(pprog, shf->node.nam) &&
2900				!(shf->node.flags & DISABLED)) {
2901				shf->node.flags = (shf->node.flags |
2902					      (on & ~PM_UNDEFINED)) & ~off;
2903				if (OPT_ISSET(ops,'X') &&
2904				    eval_autoload(shf, shf->node.nam, ops, func)) {
2905				    returnval = 1;
2906				}
2907			    }
2908		    }
2909		}
2910		unqueue_signals();
2911	    } else {
2912		untokenize(*argv);
2913		zwarnnam(name, "bad pattern : %s", *argv);
2914		returnval = 1;
2915	    }
2916	}
2917	return returnval;
2918    }
2919
2920    /* Take the arguments literally -- do not glob */
2921    queue_signals();
2922    for (; *argv; argv++) {
2923	if (OPT_ISSET(ops,'w'))
2924	    returnval = dump_autoload(name, *argv, on, ops, func);
2925	else if ((shf = (Shfunc) shfunctab->getnode(shfunctab, *argv))) {
2926	    /* if any flag was given */
2927	    if (on|off) {
2928		/* turn on/off the given flags */
2929		shf->node.flags = (shf->node.flags | (on & ~PM_UNDEFINED)) & ~off;
2930		if (OPT_ISSET(ops,'X') &&
2931		    eval_autoload(shf, shf->node.nam, ops, func))
2932		    returnval = 1;
2933	    } else
2934		/* no flags, so just print */
2935		shfunctab->printnode(&shf->node, pflags);
2936	} else if (on & PM_UNDEFINED) {
2937	    int signum = -1, ok = 1;
2938
2939	    if (!strncmp(*argv, "TRAP", 4) &&
2940		(signum = getsignum(*argv + 4)) != -1) {
2941		/*
2942		 * Because of the possibility of alternative names,
2943		 * we must remove the trap explicitly.
2944		 */
2945		removetrapnode(signum);
2946	    }
2947
2948	    /* Add a new undefined (autoloaded) function to the *
2949	     * hash table with the corresponding flags set.     */
2950	    shf = (Shfunc) zshcalloc(sizeof *shf);
2951	    shf->node.flags = on;
2952	    shf->funcdef = mkautofn(shf);
2953	    shfunc_set_sticky(shf);
2954	    shfunctab->addnode(shfunctab, ztrdup(*argv), shf);
2955
2956	    if (signum != -1) {
2957		if (settrap(signum, NULL, ZSIG_FUNC)) {
2958		    shfunctab->removenode(shfunctab, *argv);
2959		    shfunctab->freenode(&shf->node);
2960		    returnval = 1;
2961		    ok = 0;
2962		}
2963	    }
2964
2965	    if (ok && OPT_ISSET(ops,'X') &&
2966		eval_autoload(shf, shf->node.nam, ops, func))
2967		returnval = 1;
2968	} else
2969	    returnval = 1;
2970    }
2971    unqueue_signals();
2972    return returnval;
2973}
2974
2975/**/
2976Eprog
2977mkautofn(Shfunc shf)
2978{
2979    Eprog p;
2980
2981    p = (Eprog) zalloc(sizeof(*p));
2982    p->len = 5 * sizeof(wordcode);
2983    p->prog = (Wordcode) zalloc(p->len);
2984    p->strs = NULL;
2985    p->shf = shf;
2986    p->npats = 0;
2987    p->nref = 1; /* allocated from permanent storage */
2988    p->pats = (Patprog *) p->prog;
2989    p->flags = EF_REAL;
2990    p->dump = NULL;
2991
2992    p->prog[0] = WCB_LIST((Z_SYNC | Z_END), 0);
2993    p->prog[1] = WCB_SUBLIST(WC_SUBLIST_END, 0, 3);
2994    p->prog[2] = WCB_PIPE(WC_PIPE_END, 0);
2995    p->prog[3] = WCB_AUTOFN();
2996    p->prog[4] = WCB_END();
2997
2998    return p;
2999}
3000
3001/* unset: unset parameters */
3002
3003/**/
3004int
3005bin_unset(char *name, char **argv, Options ops, int func)
3006{
3007    Param pm, next;
3008    Patprog pprog;
3009    char *s;
3010    int match = 0, returnval = 0;
3011    int i;
3012
3013    /* unset -f is the same as unfunction */
3014    if (OPT_ISSET(ops,'f'))
3015	return bin_unhash(name, argv, ops, func);
3016
3017    /* with -m option, treat arguments as glob patterns */
3018    if (OPT_ISSET(ops,'m')) {
3019	while ((s = *argv++)) {
3020	    /* expand */
3021	    tokenize(s);
3022	    if ((pprog = patcompile(s, PAT_STATIC, NULL))) {
3023		/* Go through the parameter table, and unset any matches */
3024		queue_signals();
3025		for (i = 0; i < paramtab->hsize; i++) {
3026		    for (pm = (Param) paramtab->nodes[i]; pm; pm = next) {
3027			/* record pointer to next, since we may free this one */
3028			next = (Param) pm->node.next;
3029			if ((!(pm->node.flags & PM_RESTRICTED) ||
3030			     unset(RESTRICTED)) &&
3031			    pattry(pprog, pm->node.nam)) {
3032			    unsetparam_pm(pm, 0, 1);
3033			    match++;
3034			}
3035		    }
3036		}
3037		unqueue_signals();
3038	    } else {
3039		untokenize(s);
3040		zwarnnam(name, "bad pattern : %s", s);
3041		returnval = 1;
3042	    }
3043	}
3044	/* If we didn't match anything, we return 1. */
3045	if (!match)
3046	    returnval = 1;
3047	return returnval;
3048    }
3049
3050    /* do not glob -- unset the given parameter */
3051    queue_signals();
3052    while ((s = *argv++)) {
3053	char *ss = strchr(s, '[');
3054	char *sse = ss;
3055	if (ss) {
3056	    if (skipparens('[', ']', &sse) || *sse) {
3057		zerrnam(name, "%s: invalid parameter name", s);
3058		returnval = 1;
3059		continue;
3060	    }
3061	    *ss = 0;
3062	}
3063	pm = (Param) (paramtab == realparamtab ?
3064		      gethashnode2(paramtab, s) :
3065		      paramtab->getnode(paramtab, s));
3066	/*
3067	 * Unsetting an unset variable is not an error.
3068	 * This appears to be reasonably standard behaviour.
3069	 */
3070	if (!pm)
3071	    continue;
3072	else if ((pm->node.flags & PM_RESTRICTED) && isset(RESTRICTED)) {
3073	    zerrnam(name, "%s: restricted", pm->node.nam);
3074	    returnval = 1;
3075	} else if (ss) {
3076	    if (PM_TYPE(pm->node.flags) == PM_HASHED) {
3077		HashTable tht = paramtab;
3078		if ((paramtab = pm->gsu.h->getfn(pm))) {
3079		    *--sse = 0;
3080		    unsetparam(ss+1);
3081		    *sse = ']';
3082		}
3083		paramtab = tht;
3084	    } else if (PM_TYPE(pm->node.flags) == PM_SCALAR ||
3085		       PM_TYPE(pm->node.flags) == PM_ARRAY) {
3086		struct value vbuf;
3087		vbuf.isarr = (PM_TYPE(pm->node.flags) == PM_ARRAY ?
3088			      SCANPM_ARRONLY : 0);
3089		vbuf.pm = pm;
3090		vbuf.flags = 0;
3091		vbuf.start = 0;
3092		vbuf.end = -1;
3093		vbuf.arr = 0;
3094		*ss = '[';
3095		if (getindex(&ss, &vbuf, SCANPM_ASSIGNING) == 0 &&
3096		    vbuf.pm && !(vbuf.pm->node.flags & PM_UNSET)) {
3097		    if (PM_TYPE(pm->node.flags) == PM_SCALAR) {
3098			setstrvalue(&vbuf, ztrdup(""));
3099		    } else {
3100			/* start is after the element for reverse index */
3101			int start = vbuf.start - !!(vbuf.flags & VALFLAG_INV);
3102			if (start < arrlen(vbuf.pm->u.arr)) {
3103			    char *arr[2];
3104			    arr[0] = "";
3105			    arr[1] = 0;
3106			    setarrvalue(&vbuf, zarrdup(arr));
3107			}
3108		    }
3109		}
3110		returnval = errflag;
3111		errflag = 0;
3112	    } else {
3113		zerrnam(name, "%s: invalid element for unset", s);
3114		returnval = 1;
3115	    }
3116	} else {
3117	    if (unsetparam_pm(pm, 0, 1))
3118		returnval = 1;
3119	}
3120	if (ss)
3121	    *ss = '[';
3122    }
3123    unqueue_signals();
3124    return returnval;
3125}
3126
3127/* type, whence, which, command */
3128
3129/**/
3130int
3131bin_whence(char *nam, char **argv, Options ops, int func)
3132{
3133    HashNode hn;
3134    Patprog pprog;
3135    int returnval = 0;
3136    int printflags = 0;
3137    int aliasflags;
3138    int csh, all, v, wd;
3139    int informed;
3140    char *cnam;
3141
3142    /* Check some option information */
3143    csh = OPT_ISSET(ops,'c');
3144    v   = OPT_ISSET(ops,'v');
3145    all = OPT_ISSET(ops,'a');
3146    wd  = OPT_ISSET(ops,'w');
3147
3148    if (OPT_ISSET(ops,'w'))
3149	printflags |= PRINT_WHENCE_WORD;
3150    else if (OPT_ISSET(ops,'c'))
3151	printflags |= PRINT_WHENCE_CSH;
3152    else if (OPT_ISSET(ops,'v'))
3153	printflags |= PRINT_WHENCE_VERBOSE;
3154    else
3155	printflags |= PRINT_WHENCE_SIMPLE;
3156    if (OPT_ISSET(ops,'f'))
3157	printflags |= PRINT_WHENCE_FUNCDEF;
3158
3159    if (func == BIN_COMMAND)
3160	if (OPT_ISSET(ops,'V')) {
3161	    printflags = aliasflags = PRINT_WHENCE_VERBOSE;
3162	    v = 1;
3163	} else {
3164	    aliasflags = PRINT_LIST;
3165	    printflags = PRINT_WHENCE_SIMPLE;
3166	    v = 0;
3167	}
3168    else
3169	aliasflags = printflags;
3170
3171    /* With -m option -- treat arguments as a glob patterns */
3172    if (OPT_ISSET(ops,'m')) {
3173	for (; *argv; argv++) {
3174	    /* parse the pattern */
3175	    tokenize(*argv);
3176	    if (!(pprog = patcompile(*argv, PAT_STATIC, NULL))) {
3177		untokenize(*argv);
3178		zwarnnam(nam, "bad pattern : %s", *argv);
3179		returnval = 1;
3180		continue;
3181	    }
3182	    queue_signals();
3183	    if (!OPT_ISSET(ops,'p')) {
3184		/* -p option is for path search only.    *
3185		 * We're not using it, so search for ... */
3186
3187		/* aliases ... */
3188		scanmatchtable(aliastab, pprog, 1, 0, DISABLED,
3189			       aliastab->printnode, printflags);
3190
3191		/* and reserved words ... */
3192		scanmatchtable(reswdtab, pprog, 1, 0, DISABLED,
3193			       reswdtab->printnode, printflags);
3194
3195		/* and shell functions... */
3196		scanmatchtable(shfunctab, pprog, 1, 0, DISABLED,
3197			       shfunctab->printnode, printflags);
3198
3199		/* and builtins. */
3200		scanmatchtable(builtintab, pprog, 1, 0, DISABLED,
3201			       builtintab->printnode, printflags);
3202	    }
3203	    /* Done search for `internal' commands, if the -p option *
3204	     * was not used.  Now search the path.                   */
3205	    cmdnamtab->filltable(cmdnamtab);
3206	    scanmatchtable(cmdnamtab, pprog, 1, 0, 0,
3207			   cmdnamtab->printnode, printflags);
3208
3209	    unqueue_signals();
3210	}
3211	return returnval;
3212    }
3213
3214    /* Take arguments literally -- do not glob */
3215    queue_signals();
3216    for (; *argv; argv++) {
3217	informed = 0;
3218
3219	if (!OPT_ISSET(ops,'p')) {
3220	    char *suf;
3221
3222	    /* Look for alias */
3223	    if ((hn = aliastab->getnode(aliastab, *argv))) {
3224		aliastab->printnode(hn, aliasflags);
3225		if (!all)
3226		    continue;
3227		informed = 1;
3228	    }
3229	    /* Look for suffix alias */
3230	    if ((suf = strrchr(*argv, '.')) && suf[1] &&
3231		suf > *argv && suf[-1] != Meta &&
3232		(hn = sufaliastab->getnode(sufaliastab, suf+1))) {
3233		sufaliastab->printnode(hn, printflags);
3234		if (!all)
3235		    continue;
3236		informed = 1;
3237	    }
3238	    /* Look for reserved word */
3239	    if ((hn = reswdtab->getnode(reswdtab, *argv))) {
3240		reswdtab->printnode(hn, printflags);
3241		if (!all)
3242		    continue;
3243		informed = 1;
3244	    }
3245	    /* Look for shell function */
3246	    if ((hn = shfunctab->getnode(shfunctab, *argv))) {
3247		shfunctab->printnode(hn, printflags);
3248		if (!all)
3249		    continue;
3250		informed = 1;
3251	    }
3252	    /* Look for builtin command */
3253	    if ((hn = builtintab->getnode(builtintab, *argv))) {
3254		builtintab->printnode(hn, printflags);
3255		if (!all)
3256		    continue;
3257		informed = 1;
3258	    }
3259	    /* Look for commands that have been added to the *
3260	     * cmdnamtab with the builtin `hash foo=bar'.    */
3261	    if ((hn = cmdnamtab->getnode(cmdnamtab, *argv)) && (hn->flags & HASHED)) {
3262		cmdnamtab->printnode(hn, printflags);
3263		if (!all)
3264		    continue;
3265		informed = 1;
3266	    }
3267	}
3268
3269	/* Option -a is to search the entire path, *
3270	 * rather than just looking for one match. */
3271	if (all) {
3272	    char **pp, *buf;
3273
3274	    pushheap();
3275	    for (pp = path; *pp; pp++) {
3276		if (**pp) {
3277		    buf = zhtricat(*pp, "/", *argv);
3278		} else buf = ztrdup(*argv);
3279
3280		if (iscom(buf)) {
3281		    if (wd) {
3282			printf("%s: command\n", *argv);
3283		    } else {
3284			if (v && !csh)
3285			    zputs(*argv, stdout), fputs(" is ", stdout);
3286			zputs(buf, stdout);
3287			if (OPT_ISSET(ops,'s'))
3288			    print_if_link(buf);
3289			fputc('\n', stdout);
3290		    }
3291		    informed = 1;
3292		}
3293	    }
3294	    if (!informed && (wd || v || csh)) {
3295		zputs(*argv, stdout);
3296		puts(wd ? ": none" : " not found");
3297		returnval = 1;
3298	    }
3299	    popheap();
3300	} else if ((cnam = findcmd(*argv, 1))) {
3301	    /* Found external command. */
3302	    if (wd) {
3303		printf("%s: command\n", *argv);
3304	    } else {
3305		if (v && !csh)
3306		    zputs(*argv, stdout), fputs(" is ", stdout);
3307		zputs(cnam, stdout);
3308		if (OPT_ISSET(ops,'s'))
3309		    print_if_link(cnam);
3310		fputc('\n', stdout);
3311	    }
3312	} else {
3313	    /* Not found at all. */
3314	    if (v || csh || wd)
3315		zputs(*argv, stdout), puts(wd ? ": none" : " not found");
3316	    returnval = 1;
3317	}
3318    }
3319    unqueue_signals();
3320    return returnval;
3321}
3322
3323/**** command & named directory hash table builtins ****/
3324
3325/*****************************************************************
3326 * hash -- explicitly hash a command.                            *
3327 * 1) Given no arguments, list the hash table.                   *
3328 * 2) The -m option prints out commands in the hash table that   *
3329 *    match a given glob pattern.                                *
3330 * 3) The -f option causes the entire path to be added to the    *
3331 *    hash table (cannot be combined with any arguments).        *
3332 * 4) The -r option causes the entire hash table to be discarded *
3333 *    (cannot be combined with any arguments).                   *
3334 * 5) Given argument of the form foo=bar, add element to command *
3335 *    hash table, so that when `foo' is entered, then `bar' is   *
3336 *    executed.                                                  *
3337 * 6) Given arguments not of the previous form, add it to the    *
3338 *    command hash table as if it were being executed.           *
3339 * 7) The -d option causes analogous things to be done using     *
3340 *    the named directory hash table.                            *
3341 *****************************************************************/
3342
3343/**/
3344int
3345bin_hash(char *name, char **argv, Options ops, UNUSED(int func))
3346{
3347    HashTable ht;
3348    Patprog pprog;
3349    Asgment asg;
3350    int returnval = 0;
3351    int printflags = 0;
3352
3353    if (OPT_ISSET(ops,'d'))
3354	ht = nameddirtab;
3355    else
3356	ht = cmdnamtab;
3357
3358    if (OPT_ISSET(ops,'r') || OPT_ISSET(ops,'f')) {
3359	/* -f and -r can't be used with any arguments */
3360	if (*argv) {
3361	    zwarnnam("hash", "too many arguments");
3362	    return 1;
3363	}
3364
3365	/* empty the hash table */
3366	if (OPT_ISSET(ops,'r'))
3367	    ht->emptytable(ht);
3368
3369	/* fill the hash table in a standard way */
3370	if (OPT_ISSET(ops,'f'))
3371	    ht->filltable(ht);
3372
3373	return 0;
3374    }
3375
3376    if (OPT_ISSET(ops,'L')) printflags |= PRINT_LIST;
3377
3378    /* Given no arguments, display current hash table. */
3379    if (!*argv) {
3380	queue_signals();
3381	scanhashtable(ht, 1, 0, 0, ht->printnode, printflags);
3382	unqueue_signals();
3383	return 0;
3384    }
3385
3386    queue_signals();
3387    for (;*argv;++argv) {
3388	void *hn;
3389	if (OPT_ISSET(ops,'m')) {
3390	    /* with the -m option, treat the argument as a glob pattern */
3391	    tokenize(*argv);  /* expand */
3392	    if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) {
3393		/* display matching hash table elements */
3394		scanmatchtable(ht, pprog, 1, 0, 0, ht->printnode, printflags);
3395	    } else {
3396		untokenize(*argv);
3397		zwarnnam(name, "bad pattern : %s", *argv);
3398		returnval = 1;
3399	    }
3400            continue;
3401	}
3402        if (!(asg = getasg(*argv))) {
3403	    zwarnnam(name, "bad assignment");
3404	    returnval = 1;
3405        } else if (asg->value) {
3406	    if(isset(RESTRICTED)) {
3407		zwarnnam(name, "restricted: %s", asg->value);
3408		returnval = 1;
3409	    } else {
3410		/* The argument is of the form foo=bar, *
3411		 * so define an entry for the table.    */
3412		if(OPT_ISSET(ops,'d')) {
3413		    /* shouldn't return NULL if asg->name is not NULL */
3414		    if (*itype_end(asg->name, IUSER, 0)) {
3415			zwarnnam(name,
3416				 "invalid character in directory name: %s",
3417				 asg->name);
3418			returnval = 1;
3419			continue;
3420		    } else {
3421			Nameddir nd = hn = zshcalloc(sizeof *nd);
3422			nd->node.flags = 0;
3423			nd->dir = ztrdup(asg->value);
3424		    }
3425		} else {
3426		    Cmdnam cn = hn = zshcalloc(sizeof *cn);
3427		    cn->node.flags = HASHED;
3428		    cn->u.cmd = ztrdup(asg->value);
3429		}
3430		ht->addnode(ht, ztrdup(asg->name), hn);
3431		if(OPT_ISSET(ops,'v'))
3432		    ht->printnode(hn, 0);
3433	    }
3434	} else if (!(hn = ht->getnode2(ht, asg->name))) {
3435	    /* With no `=value' part to the argument, *
3436	     * work out what it ought to be.          */
3437	    if(OPT_ISSET(ops,'d')) {
3438		if(!getnameddir(asg->name)) {
3439		    zwarnnam(name, "no such directory name: %s", asg->name);
3440		    returnval = 1;
3441		}
3442	    } else {
3443		if (!hashcmd(asg->name, path)) {
3444		    zwarnnam(name, "no such command: %s", asg->name);
3445		    returnval = 1;
3446		}
3447	    }
3448	    if(OPT_ISSET(ops,'v') && (hn = ht->getnode2(ht, asg->name)))
3449		ht->printnode(hn, 0);
3450	} else if(OPT_ISSET(ops,'v'))
3451	    ht->printnode(hn, 0);
3452    }
3453    unqueue_signals();
3454    return returnval;
3455}
3456
3457/* unhash: remove specified elements from a hash table */
3458
3459/**/
3460int
3461bin_unhash(char *name, char **argv, Options ops, UNUSED(int func))
3462{
3463    HashTable ht;
3464    HashNode hn, nhn;
3465    Patprog pprog;
3466    int match = 0, returnval = 0;
3467    int i;
3468
3469    /* Check which hash table we are working with. */
3470    if (OPT_ISSET(ops,'d'))
3471	ht = nameddirtab;	/* named directories */
3472    else if (OPT_ISSET(ops,'f'))
3473	ht = shfunctab;		/* shell functions   */
3474    else if (OPT_ISSET(ops,'s'))
3475	ht = sufaliastab;	/* suffix aliases, must precede aliases */
3476    else if (OPT_ISSET(ops,'a'))
3477	ht = aliastab;		/* aliases           */
3478    else
3479	ht = cmdnamtab;		/* external commands */
3480
3481    /* With -m option, treat arguments as glob patterns. *
3482     * "unhash -m '*'" is legal, but not recommended.    */
3483    if (OPT_ISSET(ops,'m')) {
3484	for (; *argv; argv++) {
3485	    /* expand argument */
3486	    tokenize(*argv);
3487	    if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) {
3488		/* remove all nodes matching glob pattern */
3489		queue_signals();
3490		for (i = 0; i < ht->hsize; i++) {
3491		    for (hn = ht->nodes[i]; hn; hn = nhn) {
3492			/* record pointer to next, since we may free this one */
3493			nhn = hn->next;
3494			if (pattry(pprog, hn->nam)) {
3495			    ht->freenode(ht->removenode(ht, hn->nam));
3496			    match++;
3497			}
3498		    }
3499		}
3500		unqueue_signals();
3501	    } else {
3502		untokenize(*argv);
3503		zwarnnam(name, "bad pattern : %s", *argv);
3504		returnval = 1;
3505	    }
3506	}
3507	/* If we didn't match anything, we return 1. */
3508	if (!match)
3509	    returnval = 1;
3510	return returnval;
3511    }
3512
3513    /* Take arguments literally -- do not glob */
3514    queue_signals();
3515    for (; *argv; argv++) {
3516	if ((hn = ht->removenode(ht, *argv))) {
3517	    ht->freenode(hn);
3518	} else {
3519	    zwarnnam(name, "no such hash table element: %s", *argv);
3520	    returnval = 1;
3521	}
3522    }
3523    unqueue_signals();
3524    return returnval;
3525}
3526
3527/**** alias builtins ****/
3528
3529/* alias: display or create aliases. */
3530
3531/**/
3532int
3533bin_alias(char *name, char **argv, Options ops, UNUSED(int func))
3534{
3535    Alias a;
3536    Patprog pprog;
3537    Asgment asg;
3538    int returnval = 0;
3539    int flags1 = 0, flags2 = DISABLED;
3540    int printflags = 0;
3541    int type_opts;
3542    HashTable ht = aliastab;
3543
3544    /* Did we specify the type of alias? */
3545    type_opts = OPT_ISSET(ops, 'r') + OPT_ISSET(ops, 'g') +
3546	OPT_ISSET(ops, 's');
3547    if (type_opts) {
3548	if (type_opts > 1) {
3549	    zwarnnam(name, "illegal combination of options");
3550	    return 1;
3551	}
3552	if (OPT_ISSET(ops,'g'))
3553	    flags1 |= ALIAS_GLOBAL;
3554	else
3555	    flags2 |= ALIAS_GLOBAL;
3556	if (OPT_ISSET(ops, 's')) {
3557	    /*
3558	     * Although we keep suffix aliases in a different table,
3559	     * it is useful to be able to distinguish Alias structures
3560	     * without reference to the table, so we have a separate
3561	     * flag, too.
3562	     */
3563	    flags1 |= ALIAS_SUFFIX;
3564	    ht = sufaliastab;
3565	} else
3566	    flags2 |= ALIAS_SUFFIX;
3567    }
3568
3569    if (OPT_ISSET(ops,'L'))
3570	printflags |= PRINT_LIST;
3571    else if (OPT_PLUS(ops,'g') || OPT_PLUS(ops,'r') || OPT_PLUS(ops,'s') ||
3572	     OPT_PLUS(ops,'m') || OPT_ISSET(ops,'+'))
3573	printflags |= PRINT_NAMEONLY;
3574
3575    /* In the absence of arguments, list all aliases.  If a command *
3576     * line flag is specified, list only those of that type.        */
3577    if (!*argv) {
3578	queue_signals();
3579	scanhashtable(ht, 1, flags1, flags2, ht->printnode, printflags);
3580	unqueue_signals();
3581	return 0;
3582    }
3583
3584    /* With the -m option, treat the arguments as *
3585     * glob patterns of aliases to display.       */
3586    if (OPT_ISSET(ops,'m')) {
3587	for (; *argv; argv++) {
3588	    tokenize(*argv);  /* expand argument */
3589	    if ((pprog = patcompile(*argv, PAT_STATIC, NULL))) {
3590		/* display the matching aliases */
3591		queue_signals();
3592		scanmatchtable(ht, pprog, 1, flags1, flags2,
3593			       ht->printnode, printflags);
3594		unqueue_signals();
3595	    } else {
3596		untokenize(*argv);
3597		zwarnnam(name, "bad pattern : %s", *argv);
3598		returnval = 1;
3599	    }
3600	}
3601	return returnval;
3602    }
3603
3604    /* Take arguments literally.  Don't glob */
3605    queue_signals();
3606    while ((asg = getasg(*argv++))) {
3607	if (asg->value && !OPT_ISSET(ops,'L')) {
3608	    /* The argument is of the form foo=bar and we are not *
3609	     * forcing a listing with -L, so define an alias      */
3610	    ht->addnode(ht, ztrdup(asg->name),
3611			createaliasnode(ztrdup(asg->value), flags1));
3612	} else if ((a = (Alias) ht->getnode(ht, asg->name))) {
3613	    /* display alias if appropriate */
3614	    if (!type_opts || ht == sufaliastab ||
3615		(OPT_ISSET(ops,'r') &&
3616		 !(a->node.flags & (ALIAS_GLOBAL|ALIAS_SUFFIX))) ||
3617		(OPT_ISSET(ops,'g') && (a->node.flags & ALIAS_GLOBAL)))
3618		ht->printnode(&a->node, printflags);
3619	} else
3620	    returnval = 1;
3621    }
3622    unqueue_signals();
3623    return returnval;
3624}
3625
3626
3627/**** miscellaneous builtins ****/
3628
3629/* true, : (colon) */
3630
3631/**/
3632int
3633bin_true(UNUSED(char *name), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func))
3634{
3635    return 0;
3636}
3637
3638/* false builtin */
3639
3640/**/
3641int
3642bin_false(UNUSED(char *name), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func))
3643{
3644    return 1;
3645}
3646
3647/* the zle buffer stack */
3648
3649/**/
3650mod_export LinkList bufstack;
3651
3652/* echo, print, printf, pushln */
3653
3654#define print_val(VAL) \
3655    if (prec >= 0) \
3656	count += fprintf(fout, spec, width, prec, VAL); \
3657    else \
3658	count += fprintf(fout, spec, width, VAL);
3659
3660/*
3661 * Because of the use of getkeystring() to interpret the arguments,
3662 * the elements of args spend a large part of the function unmetafied
3663 * with the lengths in len.  This may have seemed a good idea once.
3664 * As we are stuck with this for now, we need to be very careful
3665 * deciding what state args is in.
3666 */
3667
3668/**/
3669int
3670bin_print(char *name, char **args, Options ops, int func)
3671{
3672    int flen, width, prec, type, argc, n, narg, curlen = 0;
3673    int nnl = 0, fmttrunc = 0, ret = 0, maxarg = 0;
3674    int flags[5], *len;
3675    char *start, *endptr, *c, *d, *flag, *buf, spec[13], *fmt = NULL;
3676    char **first, **argp, *curarg, *flagch = "0+- #", save = '\0', nullstr = '\0';
3677    size_t rcount, count = 0;
3678#ifdef HAVE_OPEN_MEMSTREAM
3679    size_t mcount;
3680#endif
3681    FILE *fout = stdout;
3682    Histent ent;
3683
3684    mnumber mnumval;
3685    double doubleval;
3686    int intval;
3687    zlong zlongval;
3688    zulong zulongval;
3689    char *stringval;
3690
3691    if (func == BIN_PRINTF) {
3692        if (!strcmp(*args, "--") && !*++args) {
3693            zwarnnam(name, "not enough arguments");
3694	    return 1;
3695        }
3696  	fmt = *args++;
3697    } else if (func == BIN_ECHO && isset(BSDECHO))
3698	ops->ind['E'] = 1;
3699    else if (OPT_HASARG(ops,'f'))
3700	fmt = OPT_ARG(ops,'f');
3701    if (fmt)
3702	fmt = getkeystring(fmt, &flen, OPT_ISSET(ops,'b') ? GETKEYS_BINDKEY :
3703			   GETKEYS_PRINTF_FMT, &fmttrunc);
3704
3705    first = args;
3706
3707    /* -m option -- treat the first argument as a pattern and remove
3708     * arguments not matching */
3709    if (OPT_ISSET(ops,'m')) {
3710	Patprog pprog;
3711	char **t, **p;
3712
3713	if (!*args) {
3714	    zwarnnam(name, "no pattern specified");
3715	    return 1;
3716	}
3717	tokenize(*args);
3718	if (!(pprog = patcompile(*args, PAT_STATIC, NULL))) {
3719	    untokenize(*args);
3720	    zwarnnam(name, "bad pattern: %s", *args);
3721	    return 1;
3722	}
3723	for (t = p = ++args; *p; p++)
3724	    if (pattry(pprog, *p))
3725		*t++ = *p;
3726	*t = NULL;
3727	first = args;
3728	if (fmt && !*args) return 0;
3729    }
3730    /* compute lengths, and interpret according to -P, -D, -e, etc. */
3731    argc = arrlen(args);
3732    len = (int *) hcalloc(argc * sizeof(int));
3733    for(n = 0; n < argc; n++) {
3734	/* first \ sequences */
3735	if (fmt ||
3736	    (!OPT_ISSET(ops,'e') &&
3737	     (OPT_ISSET(ops,'R') || OPT_ISSET(ops,'r') || OPT_ISSET(ops,'E'))))
3738	    unmetafy(args[n], &len[n]);
3739	else {
3740	    int escape_how;
3741	    if (OPT_ISSET(ops,'b'))
3742		escape_how = GETKEYS_BINDKEY;
3743	    else if (func != BIN_ECHO && !OPT_ISSET(ops,'e'))
3744		escape_how = GETKEYS_PRINT;
3745	    else
3746		escape_how = GETKEYS_ECHO;
3747	    args[n] = getkeystring(args[n], &len[n], escape_how, &nnl);
3748	    if (nnl) {
3749		/* If there was a \c escape, make this the last arg. */
3750		argc = n + 1;
3751		args[argc] = NULL;
3752	    }
3753	}
3754	/* -P option -- interpret as a prompt sequence */
3755	if(OPT_ISSET(ops,'P')) {
3756	    /*
3757	     * promptexpand uses permanent storage: to avoid
3758	     * messy memory management, stick it on the heap
3759	     * instead.
3760	     */
3761	    char *str = unmetafy(
3762		promptexpand(metafy(args[n], len[n], META_NOALLOC),
3763			     0, NULL, NULL, NULL),
3764		&len[n]);
3765	    args[n] = dupstrpfx(str, len[n]);
3766	    free(str);
3767	}
3768	/* -D option -- interpret as a directory, and use ~ */
3769	if(OPT_ISSET(ops,'D')) {
3770	    Nameddir d;
3771
3772	    queue_signals();
3773	    /* TODO: finddir takes a metafied file */
3774	    d = finddir(args[n]);
3775	    if(d) {
3776		int dirlen = strlen(d->dir);
3777		char *arg = zhalloc(len[n] - dirlen + strlen(d->node.nam) + 2);
3778		sprintf(arg, "~%s%s", d->node.nam, args[n] + dirlen);
3779		args[n] = arg;
3780		len[n] = strlen(args[n]);
3781	    }
3782	    unqueue_signals();
3783	}
3784    }
3785
3786    /* -u and -p -- output to other than standard output */
3787    if (OPT_HASARG(ops,'u') || OPT_ISSET(ops,'p')) {
3788	int fd;
3789
3790	if (OPT_ISSET(ops, 'p')) {
3791	    fd = coprocout;
3792	    if (fd < 0) {
3793		zwarnnam(name, "-p: no coprocess");
3794		return 1;
3795	    }
3796	} else {
3797	    char *argptr = OPT_ARG(ops,'u'), *eptr;
3798	    /* Handle undocumented feature that -up worked */
3799	    if (!strcmp(argptr, "p")) {
3800		fd = coprocout;
3801		if (fd < 0) {
3802		    zwarnnam(name, "-p: no coprocess");
3803		    return 1;
3804		}
3805	    } else {
3806		fd = (int)zstrtol(argptr, &eptr, 10);
3807		if (*eptr) {
3808		    zwarnnam(name, "number expected after -%c: %s", 'u',
3809			     argptr);
3810		    return 1;
3811		}
3812	    }
3813	}
3814
3815	if ((fd = dup(fd)) < 0) {
3816	    zwarnnam(name, "bad file number: %d", fd);
3817	    return 1;
3818	}
3819	if ((fout = fdopen(fd, "w")) == 0) {
3820	    close(fd);
3821	    zwarnnam(name, "bad mode on fd %d", fd);
3822	    return 1;
3823	}
3824    }
3825
3826    /* -o and -O -- sort the arguments */
3827    if (OPT_ISSET(ops,'o') || OPT_ISSET(ops,'O')) {
3828	int flags;
3829
3830	if (fmt && !*args) {
3831	    if (fout != stdout)
3832	        fclose(fout);
3833	    return 0;
3834	}
3835	flags = OPT_ISSET(ops,'i') ? SORTIT_IGNORING_CASE : 0;
3836	if (OPT_ISSET(ops,'O'))
3837	    flags |= SORTIT_BACKWARDS;
3838	strmetasort(args, flags, len);
3839    }
3840
3841    /* -c -- output in columns */
3842    if (!fmt && (OPT_ISSET(ops,'c') || OPT_ISSET(ops,'C'))) {
3843	int l, nc, nr, sc, n, t, i;
3844#ifdef MULTIBYTE_SUPPORT
3845	int *widths;
3846
3847	if (isset(MULTIBYTE)) {
3848	    int *wptr;
3849
3850	    /*
3851	     * We need the character widths to align output in
3852	     * columns.
3853	     */
3854	    wptr = widths = (int *) zhalloc(argc * sizeof(int));
3855	    for (i = 0; i < argc && args[i]; i++, wptr++) {
3856		int l = len[i], width = 0;
3857		char *aptr = args[i];
3858		mbstate_t mbs;
3859
3860		memset(&mbs, 0, sizeof(mbstate_t));
3861		while (l > 0) {
3862		    wchar_t wc;
3863		    size_t cnt;
3864		    int wcw;
3865
3866		    /*
3867		     * Prevent misaligned columns due to escape sequences by
3868		     * skipping over them. Octals \033 and \233 are the
3869		     * possible escape characters recognized by ANSI.
3870		     *
3871		     * It ought to be possible to do this in the case
3872		     * of prompt expansion by propagating the information
3873		     * about escape sequences (currently we strip this
3874		     * out).
3875		     */
3876		    if (*aptr == '\033' || *aptr == '\233') {
3877			for (aptr++, l--;
3878			     l && !isalpha(STOUC(*aptr));
3879			     aptr++, l--)
3880			    ;
3881			aptr++;
3882			l--;
3883			continue;
3884		    }
3885
3886		    cnt = mbrtowc(&wc, aptr, l, &mbs);
3887
3888		    if (cnt == MB_INCOMPLETE || cnt == MB_INVALID)
3889		    {
3890			/* treat as ordinary string */
3891			width += l;
3892			break;
3893		    }
3894		    wcw = WCWIDTH(wc);
3895		    /* treat unprintable as 0 */
3896		    if (wcw > 0)
3897			width += wcw;
3898		    /* skip over NUL normally */
3899		    if (cnt == 0)
3900			cnt = 1;
3901		    aptr += cnt;
3902		    l -= cnt;
3903		}
3904		widths[i] = width;
3905	    }
3906	}
3907	else
3908	    widths = len;
3909#else
3910	int *widths = len;
3911#endif
3912
3913	if (OPT_ISSET(ops,'C')) {
3914	    char *eptr, *argptr = OPT_ARG(ops,'C');
3915	    nc = (int)zstrtol(argptr, &eptr, 10);
3916	    if (*eptr) {
3917		zwarnnam(name, "number expcted after -%c: %s", 'C', argptr);
3918		return 1;
3919	    }
3920	    if (nc <= 0) {
3921		zwarnnam(name, "invalid number of columns: %s", argptr);
3922		return 1;
3923	    }
3924	    /*
3925	     * n: number of elements
3926	     * nc: number of columns
3927	     * nr: number of rows
3928	     */
3929	    n = arrlen(args);
3930	    nr = (n + nc - 1) / nc;
3931
3932	    /*
3933	     * i: loop counter
3934	     * l: maximum length seen
3935	     *
3936	     * Ignore lengths in last column since they don't affect
3937	     * the separation.
3938	     */
3939	    for (i = l = 0; i < argc; i++) {
3940		if (OPT_ISSET(ops, 'a')) {
3941		    if ((i % nc) == nc - 1)
3942			continue;
3943		} else {
3944		    if (i >= nr * (nc - 1))
3945			break;
3946		}
3947		if (l < widths[i])
3948		    l = widths[i];
3949	    }
3950	    sc = l + 2;
3951	}
3952	else
3953	{
3954	    /*
3955	     * n: loop counter
3956	     * l: maximum length seen
3957	     */
3958	    for (n = l = 0; n < argc; n++)
3959		if (l < widths[n])
3960		    l = widths[n];
3961
3962	    /*
3963	     * sc: column width
3964	     * nc: number of columns (at least one)
3965	     */
3966	    sc = l + 2;
3967	    nc = (zterm_columns + 1) / sc;
3968	    if (!nc)
3969		nc = 1;
3970	    nr = (n + nc - 1) / nc;
3971	}
3972
3973	if (OPT_ISSET(ops,'a'))	/* print across, i.e. columns first */
3974	    n = 0;
3975	for (i = 0; i < nr; i++) {
3976	    if (OPT_ISSET(ops,'a'))
3977	    {
3978		int ic;
3979		for (ic = 0; ic < nc && n < argc; ic++, n++)
3980		{
3981		    fwrite(args[n], len[n], 1, fout);
3982		    l = widths[n];
3983		    if (n < argc)
3984			for (; l < sc; l++)
3985			    fputc(' ', fout);
3986		}
3987	    }
3988	    else
3989	    {
3990		n = i;
3991		do {
3992		    fwrite(args[n], len[n], 1, fout);
3993		    l = widths[n];
3994		    for (t = nr; t && n < argc; t--, n++);
3995		    if (n < argc)
3996			for (; l < sc; l++)
3997			    fputc(' ', fout);
3998		} while (n < argc);
3999	    }
4000	    fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout);
4001	}
4002	/* Testing EBADF special-cases >&- redirections */
4003	if ((fout != stdout) ? (fclose(fout) != 0) :
4004	    (fflush(fout) != 0 && errno != EBADF)) {
4005            zwarnnam(name, "write error: %e", errno);
4006            ret = 1;
4007	}
4008	return ret;
4009    }
4010
4011    /* normal output */
4012    if (!fmt) {
4013	if (OPT_ISSET(ops, 'z') || OPT_ISSET(ops, 's')) {
4014	    /*
4015	     * We don't want the arguments unmetafied after all.
4016	     */
4017	    for (n = 0; n < argc; n++)
4018		metafy(args[n], len[n], META_NOALLOC);
4019	}
4020
4021	/* -z option -- push the arguments onto the editing buffer stack */
4022	if (OPT_ISSET(ops,'z')) {
4023	    queue_signals();
4024	    zpushnode(bufstack, sepjoin(args, NULL, 0));
4025	    unqueue_signals();
4026	    return 0;
4027	}
4028	/* -s option -- add the arguments to the history list */
4029	if (OPT_ISSET(ops,'s') || OPT_ISSET(ops,'S')) {
4030	    int nwords = 0, nlen, iwords;
4031	    char **pargs = args;
4032
4033	    queue_signals();
4034	    while (*pargs++)
4035		nwords++;
4036	    if (nwords) {
4037		if (OPT_ISSET(ops,'S')) {
4038		    int wordsize;
4039		    short *words;
4040		    if (nwords > 1) {
4041			zwarnnam(name, "option -S takes a single argument");
4042			return 1;
4043		    }
4044		    words = NULL;
4045		    wordsize = 0;
4046		    histsplitwords(*args, &words, &wordsize, &nwords, 1);
4047		    ent = prepnexthistent();
4048		    ent->words = (short *)zalloc(nwords*sizeof(short));
4049		    memcpy(ent->words, words, nwords*sizeof(short));
4050		    free(words);
4051		    ent->nwords = nwords/2;
4052		} else {
4053		    ent = prepnexthistent();
4054		    ent->words = (short *)zalloc(nwords*2*sizeof(short));
4055		    ent->nwords = nwords;
4056		    nlen = iwords = 0;
4057		    for (pargs = args; *pargs; pargs++) {
4058			ent->words[iwords++] = nlen;
4059			nlen += strlen(*pargs);
4060			ent->words[iwords++] = nlen;
4061			nlen++;
4062		    }
4063		}
4064	    } else {
4065		ent = prepnexthistent();
4066		ent->words = (short *)NULL;
4067	    }
4068	    ent->node.nam = zjoin(args, ' ', 0);
4069	    ent->stim = ent->ftim = time(NULL);
4070	    ent->node.flags = 0;
4071	    addhistnode(histtab, ent->node.nam, ent);
4072	    unqueue_signals();
4073	    return 0;
4074	}
4075
4076	for (; *args; args++, len++) {
4077	    fwrite(*args, *len, 1, fout);
4078	    if (args[1])
4079		fputc(OPT_ISSET(ops,'l') ? '\n' :
4080		      OPT_ISSET(ops,'N') ? '\0' : ' ', fout);
4081	}
4082	if (!(OPT_ISSET(ops,'n') || nnl))
4083	    fputc(OPT_ISSET(ops,'N') ? '\0' : '\n', fout);
4084	/* Testing EBADF special-cases >&- redirections */
4085	if ((fout != stdout) ? (fclose(fout) != 0) :
4086	    (fflush(fout) != 0 && errno != EBADF)) {
4087            zwarnnam(name, "write error: %e", errno);
4088            ret = 1;
4089	}
4090	return ret;
4091    }
4092
4093    /*
4094     * All the remaining code in this function is for printf-style
4095     * output (printf itself, or print -f).  We still have to handle
4096     * special cases of printing to a ZLE buffer or the history, however.
4097     */
4098
4099    if (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s')) {
4100#ifdef HAVE_OPEN_MEMSTREAM
4101    	if ((fout = open_memstream(&buf, &mcount)) == NULL)
4102	    zwarnnam(name, "open_memstream failed");
4103#else
4104	int tempfd;
4105	char *tmpf;
4106	if ((tempfd = gettempfile(NULL, 1, &tmpf)) < 0
4107	 || (fout = fdopen(tempfd, "w+")) == NULL)
4108	    zwarnnam(name, "can't open temp file: %e", errno);
4109	unlink(tmpf);
4110#endif
4111    }
4112
4113    /* printf style output */
4114    *spec = '%';
4115    argp = args;
4116    do {
4117    	rcount = count;
4118    	if (maxarg) {
4119	    first += maxarg;
4120	    argc -= maxarg;
4121    	    maxarg = 0;
4122	}
4123	for (c = fmt; c-fmt < flen; c++) {
4124	    if (*c != '%') {
4125		putc(*c, fout);
4126		++count;
4127		continue;
4128	    }
4129
4130	    start = c++;
4131	    if (*c == '%') {
4132		putc('%', fout);
4133		++count;
4134		continue;
4135	    }
4136
4137	    type = prec = -1;
4138	    width = 0;
4139	    curarg = NULL;
4140	    d = spec + 1;
4141
4142	    if (*c >= '1' && *c <= '9') {
4143	    	narg = strtoul(c, &endptr, 0);
4144		if (*endptr == '$') {
4145		    c = endptr + 1;
4146		    DPUTS(narg <= 0, "specified zero or negative arg");
4147		    if (narg > argc) {
4148		    	zwarnnam(name, "%d: argument specifier out of range",
4149				 narg);
4150			if (fout != stdout)
4151			    fclose(fout);
4152			return 1;
4153		    } else {
4154		    	if (narg > maxarg) maxarg = narg;
4155		    	curarg = *(first + narg - 1);
4156			curlen = len[first - args + narg - 1];
4157		    }
4158		}
4159	    }
4160
4161	    /* copy only one of each flag as spec has finite size */
4162	    memset(flags, 0, sizeof(flags));
4163	    while (*c && (flag = strchr(flagch, *c))) {
4164	    	if (!flags[flag - flagch]) {
4165	    	    flags[flag - flagch] = 1;
4166		    *d++ = *c;
4167		}
4168	    	c++;
4169	    }
4170
4171	    if (idigit(*c)) {
4172		width = strtoul(c, &endptr, 0);
4173		c = endptr;
4174	    } else if (*c == '*') {
4175		if (idigit(*++c)) {
4176		    narg = strtoul(c, &endptr, 0);
4177		    if (*endptr == '$') {
4178		    	c = endptr + 1;
4179			if (narg > argc || narg <= 0) {
4180		    	    zwarnnam(name,
4181				     "%d: argument specifier out of range",
4182				     narg);
4183			    if (fout != stdout)
4184				fclose(fout);
4185			    return 1;
4186			} else {
4187		    	    if (narg > maxarg) maxarg = narg;
4188		    	    argp = first + narg - 1;
4189			}
4190		    }
4191		}
4192		if (*argp) {
4193		    width = (int)mathevali(*argp++);
4194		    if (errflag) {
4195			errflag = 0;
4196			ret = 1;
4197		    }
4198		}
4199	    }
4200	    *d++ = '*';
4201
4202	    if (*c == '.') {
4203		if (*++c == '*') {
4204		    if (idigit(*++c)) {
4205			narg = strtoul(c, &endptr, 0);
4206			if (*endptr == '$') {
4207			    c = endptr + 1;
4208			    if (narg > argc || narg <= 0) {
4209		    		zwarnnam(name,
4210					 "%d: argument specifier out of range",
4211					 narg);
4212				if (fout != stdout)
4213				    fclose(fout);
4214				return 1;
4215			    } else {
4216		    		if (narg > maxarg) maxarg = narg;
4217		    		argp = first + narg - 1;
4218			    }
4219			}
4220		    }
4221
4222		    if (*argp) {
4223			prec = (int)mathevali(*argp++);
4224			if (errflag) {
4225			    errflag = 0;
4226			    ret = 1;
4227			}
4228		    }
4229		} else if (idigit(*c)) {
4230		    prec = strtoul(c, &endptr, 0);
4231		    c = endptr;
4232		}
4233		if (prec >= 0) *d++ = '.', *d++ = '*';
4234	    }
4235
4236	    /* ignore any size modifier */
4237	    if (*c == 'l' || *c == 'L' || *c == 'h') c++;
4238
4239	    if (!curarg && *argp) {
4240		curarg = *argp;
4241		curlen = len[argp++ - args];
4242	    }
4243	    d[1] = '\0';
4244	    switch (*d = *c) {
4245	    case 'c':
4246		if (curarg)
4247		    intval = *curarg;
4248		else
4249		    intval = 0;
4250		print_val(intval);
4251		break;
4252	    case 's':
4253	    case 'b':
4254		if (curarg) {
4255		    char *b, *ptr;
4256		    int lbytes, lchars, lleft;
4257#ifdef MULTIBYTE_SUPPORT
4258		    mbstate_t mbs;
4259#endif
4260
4261		    if (*c == 'b') {
4262			b = getkeystring(metafy(curarg, curlen, META_USEHEAP),
4263					 &lbytes,
4264					 OPT_ISSET(ops,'b') ? GETKEYS_BINDKEY :
4265					 GETKEYS_PRINTF_ARG, &nnl);
4266		    } else {
4267			b = curarg;
4268			lbytes = curlen;
4269		    }
4270		    /*
4271		     * Handle width/precision here and use fwrite so that
4272		     * nul characters can be output.
4273		     *
4274		     * First, examine width of string given that it
4275		     * may contain multibyte characters.  The output
4276		     * widths are for characters, so we need to count
4277		     * (in lchars).  However, if we need to truncate
4278		     * the string we need the width in bytes (in lbytes).
4279		     */
4280		    ptr = b;
4281#ifdef MULTIBYTE_SUPPORT
4282		    memset(&mbs, 0, sizeof(mbs));
4283#endif
4284
4285		    for (lchars = 0, lleft = lbytes; lleft > 0; lchars++) {
4286			int chars;
4287
4288			if (lchars == prec) {
4289			    /* Truncate at this point. */
4290			    lbytes = ptr - b;
4291			    break;
4292			}
4293#ifdef MULTIBYTE_SUPPORT
4294			if (isset(MULTIBYTE)) {
4295			    chars = mbrlen(ptr, lleft, &mbs);
4296			    if (chars < 0) {
4297				/*
4298				 * Invalid/incomplete character at this
4299				 * point.  Assume all the rest are a
4300				 * single byte.  That's about the best we
4301				 * can do.
4302				 */
4303				lchars += lleft;
4304				lbytes = (ptr - b) + lleft;
4305				break;
4306			    } else if (chars == 0) {
4307				/* NUL, handle as real character */
4308				chars = 1;
4309			    }
4310			}
4311			else	/* use the non-multibyte code below */
4312#endif
4313			    chars = 1; /* compiler can optimise this...*/
4314			lleft -= chars;
4315			ptr += chars;
4316		    }
4317		    if (width > 0 && flags[2]) width = -width;
4318		    if (width > 0 && lchars < width)
4319		    	count += fprintf(fout, "%*c", width - lchars, ' ');
4320		    count += fwrite(b, 1, lbytes, fout);
4321		    if (width < 0 && lchars < -width)
4322		    	count += fprintf(fout, "%*c", -width - lchars, ' ');
4323		    if (nnl) {
4324			/* If the %b arg had a \c escape, truncate the fmt. */
4325			flen = c - fmt + 1;
4326			fmttrunc = 1;
4327		    }
4328		} else if (width)
4329		    count += fprintf(fout, "%*c", width, ' ');
4330		break;
4331	    case 'q':
4332		stringval = curarg ?
4333		    quotestring(curarg, NULL, QT_BACKSLASH_SHOWNULL) : &nullstr;
4334		*d = 's';
4335		print_val(stringval);
4336		break;
4337	    case 'd':
4338	    case 'i':
4339		type=1;
4340		break;
4341	    case 'e':
4342	    case 'E':
4343	    case 'f':
4344	    case 'g':
4345	    case 'G':
4346		type=2;
4347		break;
4348	    case 'o':
4349	    case 'u':
4350	    case 'x':
4351	    case 'X':
4352		type=3;
4353		break;
4354	    case 'n':
4355		if (curarg) setiparam(curarg, count - rcount);
4356		break;
4357	    default:
4358	        if (*c) {
4359		    save = c[1];
4360	            c[1] = '\0';
4361		}
4362		zwarnnam(name, "%s: invalid directive", start);
4363		if (*c) c[1] = save;
4364		/* Testing EBADF special-cases >&- redirections */
4365		if ((fout != stdout) ? (fclose(fout) != 0) :
4366		    (fflush(fout) != 0 && errno != EBADF)) {
4367		    zwarnnam(name, "write error: %e", errno);
4368		}
4369		return 1;
4370	    }
4371
4372	    if (type > 0) {
4373		if (curarg && (*curarg == '\'' || *curarg == '"' )) {
4374		    convchar_t cc;
4375#ifdef MULTIBYTE_SUPPORT
4376		    if (isset(MULTIBYTE)) {
4377			mb_metacharinit();
4378			(void)mb_metacharlenconv(metafy(curarg+1, curlen-1,
4379							META_USEHEAP), &cc);
4380		    }
4381		    else
4382			cc = WEOF;
4383		    if (cc == WEOF)
4384			cc = (curlen > 1) ? STOUC(curarg[1]) : 0;
4385#else
4386		    cc = (curlen > 1) ? STOUC(curarg[1]) : 0;
4387#endif
4388		    if (type == 2) {
4389			doubleval = cc;
4390			print_val(doubleval);
4391		    } else {
4392			intval = cc;
4393			print_val(intval);
4394		    }
4395		} else {
4396		    switch (type) {
4397		    case 1:
4398#ifdef ZSH_64_BIT_TYPE
4399 		    	*d++ = 'l';
4400#endif
4401		    	*d++ = 'l', *d++ = *c, *d = '\0';
4402			zlongval = (curarg) ? mathevali(curarg) : 0;
4403			if (errflag) {
4404			    zlongval = 0;
4405			    errflag = 0;
4406			    ret = 1;
4407			}
4408			print_val(zlongval)
4409			    break;
4410		    case 2:
4411			if (curarg) {
4412			    char *eptr;
4413			    /*
4414			     * First attempt to parse as a floating
4415			     * point constant.  If we go through
4416			     * a math evaluation, we can lose
4417			     * mostly unimportant information
4418			     * that people in standards organizations
4419			     * worry about.
4420			     */
4421			    doubleval = strtod(curarg, &eptr);
4422			    /*
4423			     * If it didn't parse as a constant,
4424			     * parse it as an expression.
4425			     */
4426			    if (*eptr != '\0') {
4427				mnumval = matheval(curarg);
4428				doubleval = (mnumval.type & MN_FLOAT) ?
4429				    mnumval.u.d : (double)mnumval.u.l;
4430			    }
4431			} else doubleval = 0;
4432			if (errflag) {
4433			    doubleval = 0;
4434			    errflag = 0;
4435			    ret = 1;
4436			}
4437			print_val(doubleval)
4438			    break;
4439		    case 3:
4440#ifdef ZSH_64_BIT_UTYPE
4441 		    	*d++ = 'l';
4442#endif
4443		    	*d++ = 'l', *d++ = *c, *d = '\0';
4444			zulongval = (curarg) ? mathevali(curarg) : 0;
4445			if (errflag) {
4446			    zulongval = 0;
4447			    errflag = 0;
4448			    ret = 1;
4449			}
4450			print_val(zulongval)
4451		    }
4452		}
4453	    }
4454	    if (maxarg && (argp - first > maxarg))
4455	    	maxarg = argp - first;
4456	}
4457
4458    	if (maxarg) argp = first + maxarg;
4459	/* if there are remaining args, reuse format string */
4460    } while (*argp && argp != first && !fmttrunc && !OPT_ISSET(ops,'r'));
4461
4462    if (OPT_ISSET(ops,'z') || OPT_ISSET(ops,'s')) {
4463#ifdef HAVE_OPEN_MEMSTREAM
4464	putc(0, fout);
4465	fflush(fout);
4466#else
4467	rewind(fout);
4468	buf = (char *)zalloc(count + 1);
4469	fread(buf, count, 1, fout);
4470	buf[count] = '\0';
4471#endif
4472	queue_signals();
4473	if (OPT_ISSET(ops,'z')) {
4474	    zpushnode(bufstack, buf);
4475	} else {
4476	    ent = prepnexthistent();
4477	    ent->node.nam = buf;
4478	    ent->stim = ent->ftim = time(NULL);
4479	    ent->node.flags = 0;
4480	    ent->words = (short *)NULL;
4481	    addhistnode(histtab, ent->node.nam, ent);
4482	}
4483	unqueue_signals();
4484    }
4485
4486    /* Testing EBADF special-cases >&- redirections */
4487    if ((fout != stdout) ? (fclose(fout) != 0) :
4488	(fflush(fout) != 0 && errno != EBADF)) {
4489	zwarnnam(name, "write error: %e", errno);
4490	ret = 1;
4491    }
4492    return ret;
4493}
4494
4495/* shift builtin */
4496
4497/**/
4498int
4499bin_shift(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
4500{
4501    int num = 1, l, ret = 0;
4502    char **s;
4503
4504    /* optional argument can be either numeric or an array */
4505    queue_signals();
4506    if (*argv && !getaparam(*argv))
4507        num = mathevali(*argv++);
4508
4509    if (num < 0) {
4510	unqueue_signals();
4511        zwarnnam(name, "argument to shift must be non-negative");
4512        return 1;
4513    }
4514
4515    if (*argv) {
4516        for (; *argv; argv++)
4517            if ((s = getaparam(*argv))) {
4518                if (num > arrlen(s)) {
4519		    zwarnnam(name, "shift count must be <= $#");
4520		    ret++;
4521		    continue;
4522		}
4523		s = zarrdup(s + num);
4524                setaparam(*argv, s);
4525            }
4526    } else {
4527        if (num > (l = arrlen(pparams))) {
4528	    zwarnnam(name, "shift count must be <= $#");
4529	    ret = 1;
4530	} else {
4531	    s = zalloc((l - num + 1) * sizeof(char *));
4532	    memcpy(s, pparams + num, (l - num + 1) * sizeof(char *));
4533	    while (num--)
4534		zsfree(pparams[num]);
4535	    zfree(pparams, (l + 1) * sizeof(char *));
4536	    pparams = s;
4537	}
4538    }
4539    unqueue_signals();
4540    return ret;
4541}
4542
4543/**/
4544int optcind;
4545
4546/* getopts: automagical option handling for shell scripts */
4547
4548/**/
4549int
4550bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int func))
4551{
4552    int lenstr, lenoptstr, quiet, lenoptbuf;
4553    char *optstr = unmetafy(*argv++, &lenoptstr), *var = *argv++;
4554    char **args = (*argv) ? argv : pparams;
4555    char *str, optbuf[2] = " ", *p, opch;
4556
4557    /* zoptind keeps count of the current argument number.  The *
4558     * user can set it to zero to start a new option parse.     */
4559    if (zoptind < 1) {
4560	/* first call */
4561	zoptind = 1;
4562	optcind = 0;
4563    }
4564    if(zoptind > arrlen(args))
4565	/* no more options */
4566	return 1;
4567
4568    /* leading ':' in optstr means don't print an error message */
4569    quiet = *optstr == ':';
4570    optstr += quiet;
4571    lenoptstr -= quiet;
4572
4573    /* find place in relevant argument */
4574    str = unmetafy(dupstring(args[zoptind - 1]), &lenstr);
4575    if (!lenstr)		/* Definitely not an option. */
4576	return 1;
4577    if(optcind >= lenstr) {
4578	optcind = 0;
4579	if(!args[zoptind++])
4580	    return 1;
4581	str = unmetafy(dupstring(args[zoptind - 1]), &lenstr);
4582    }
4583    if(!optcind) {
4584	if(lenstr < 2 || (*str != '-' && *str != '+'))
4585	    return 1;
4586	if(lenstr == 2 && str[0] == '-' && str[1] == '-') {
4587	    zoptind++;
4588	    return 1;
4589	}
4590	optcind++;
4591    }
4592    opch = str[optcind++];
4593    if(str[0] == '+') {
4594	optbuf[0] = '+';
4595	lenoptbuf = 2;
4596    } else
4597	lenoptbuf = 1;
4598    optbuf[lenoptbuf - 1] = opch;
4599
4600    /* check for legality */
4601    if(opch == ':' || !(p = memchr(optstr, opch, lenoptstr))) {
4602	p = "?";
4603    err:
4604	zsfree(zoptarg);
4605	setsparam(var, ztrdup(p));
4606	if(quiet) {
4607	    zoptarg = metafy(optbuf, lenoptbuf, META_DUP);
4608	} else {
4609	    zwarn(*p == '?' ? "bad option: -%c" :
4610		  "argument expected after -%c option", opch);
4611	    zoptarg=ztrdup("");
4612	}
4613	return 0;
4614    }
4615
4616    /* check for required argument */
4617    if(p[1] == ':') {
4618	if(optcind == lenstr) {
4619	    if(!args[zoptind]) {
4620		p = ":";
4621		goto err;
4622	    }
4623	    p = ztrdup(args[zoptind++]);
4624	} else
4625	    p = metafy(str+optcind, lenstr-optcind, META_DUP);
4626	/*
4627	 * Careful:  I've just changed the following two lines from
4628	 *   optcind = ztrlen(args[zoptind - 1]);
4629	 * and it's a rigorous theorem that every change in getopts breaks
4630	 * something.  See zsh-workers/9095 for the bug fixed here.
4631	 *   PWS 2000/05/02
4632	 */
4633	optcind = 0;
4634	zoptind++;
4635	zsfree(zoptarg);
4636	zoptarg = p;
4637    } else {
4638	zsfree(zoptarg);
4639	zoptarg = ztrdup("");
4640    }
4641
4642    setsparam(var, metafy(optbuf, lenoptbuf, META_DUP));
4643    return 0;
4644}
4645
4646/* Flag that we should exit the shell as soon as all functions return. */
4647/**/
4648mod_export int
4649exit_pending;
4650
4651/* break, bye, continue, exit, logout, return -- most of these take   *
4652 * one numeric argument, and the other (logout) is related to return. *
4653 * (return is treated as a logout when in a login shell.)             */
4654
4655/**/
4656int
4657bin_break(char *name, char **argv, UNUSED(Options ops), int func)
4658{
4659    int num = lastval, nump = 0;
4660
4661    /* handle one optional numeric argument */
4662    if (*argv) {
4663	num = mathevali(*argv++);
4664	nump = 1;
4665    }
4666
4667    if (nump > 0 && (func == BIN_CONTINUE || func == BIN_BREAK) && num <= 0) {
4668	zerrnam(name, "argument is not positive: %d", num);
4669	return 1;
4670    }
4671
4672    switch (func) {
4673    case BIN_CONTINUE:
4674	if (!loops) {   /* continue is only permitted in loops */
4675	    zerrnam(name, "not in while, until, select, or repeat loop");
4676	    return 1;
4677	}
4678	contflag = 1; /* FALLTHROUGH */
4679    case BIN_BREAK:
4680	if (!loops) {   /* break is only permitted in loops */
4681	    zerrnam(name, "not in while, until, select, or repeat loop");
4682	    return 1;
4683	}
4684	breaks = nump ? minimum(num,loops) : 1;
4685	break;
4686    case BIN_RETURN:
4687	if ((isset(INTERACTIVE) && isset(SHINSTDIN))
4688	    || locallevel || sourcelevel) {
4689	    retflag = 1;
4690	    breaks = loops;
4691	    lastval = num;
4692	    if (trap_state == TRAP_STATE_PRIMED && trap_return == -2) {
4693		trap_state = TRAP_STATE_FORCE_RETURN;
4694		trap_return = lastval;
4695	    }
4696	    return lastval;
4697	}
4698	zexit(num, 0);	/* else treat return as logout/exit */
4699	break;
4700    case BIN_LOGOUT:
4701	if (unset(LOGINSHELL)) {
4702	    zerrnam(name, "not login shell");
4703	    return 1;
4704	}
4705	/*FALLTHROUGH*/
4706    case BIN_EXIT:
4707	if (locallevel > forklevel) {
4708	    /*
4709	     * We don't exit directly from functions to allow tidying
4710	     * up, in particular EXIT traps.  We still need to perform
4711	     * the usual interactive tests to see if we can exit at
4712	     * all, however.
4713	     *
4714	     * If we are forked, we exit the shell at the function depth
4715	     * at which we became a subshell, hence the comparison.
4716	     */
4717	    if (stopmsg || (zexit(0,2), !stopmsg)) {
4718		retflag = 1;
4719		breaks = loops;
4720		exit_pending = (num << 1) | 1;
4721	    }
4722	} else
4723	    zexit(num, 0);
4724	break;
4725    }
4726    return 0;
4727}
4728
4729/* we have printed a 'you have stopped (running) jobs.' message */
4730
4731/**/
4732mod_export int stopmsg;
4733
4734/* check to see if user has jobs running/stopped */
4735
4736/**/
4737static void
4738checkjobs(void)
4739{
4740    int i;
4741
4742    for (i = 1; i <= maxjob; i++)
4743	if (i != thisjob && (jobtab[i].stat & STAT_LOCKED) &&
4744	    !(jobtab[i].stat & STAT_NOPRINT))
4745	    break;
4746    if (i <= maxjob) {
4747	if (jobtab[i].stat & STAT_STOPPED) {
4748
4749#ifdef USE_SUSPENDED
4750	    zerr("you have suspended jobs.");
4751#else
4752	    zerr("you have stopped jobs.");
4753#endif
4754
4755	} else
4756	    zerr("you have running jobs.");
4757	stopmsg = 1;
4758    }
4759}
4760
4761/* exit the shell.  val is the return value of the shell.  *
4762 * from_where is
4763 *   1   if zexit is called because of a signal
4764 *   2   if we can't actually exit yet (e.g. functions need
4765 *       terminating) but should perform the usual interactive tests.
4766 */
4767
4768/**/
4769mod_export void
4770zexit(int val, int from_where)
4771{
4772    static int in_exit;
4773
4774    /* Don't do anything recursively:  see below */
4775    if (in_exit == -1)
4776	return;
4777
4778    if (isset(MONITOR) && !stopmsg && from_where != 1) {
4779	scanjobs();    /* check if jobs need printing           */
4780	if (isset(CHECKJOBS))
4781	    checkjobs();   /* check if any jobs are running/stopped */
4782	if (stopmsg) {
4783	    stopmsg = 2;
4784	    return;
4785	}
4786    }
4787    /* Positive in_exit means we have been here before */
4788    if (from_where == 2 || (in_exit++ && from_where))
4789	return;
4790
4791    /*
4792     * We're now committed to exiting.  Set in_exit to -1 to
4793     * indicate we shouldn't do any recursive processing.
4794     */
4795    in_exit = -1;
4796    /*
4797     * We want to do all remaining processing regardless of preceding
4798     * errors.
4799     */
4800    errflag = 0;
4801
4802    if (isset(MONITOR)) {
4803	/* send SIGHUP to any jobs left running  */
4804	killrunjobs(from_where == 1);
4805    }
4806    if (isset(RCS) && interact) {
4807	if (!nohistsave) {
4808	    int writeflags = HFILE_USE_OPTIONS;
4809	    if (from_where == 1)
4810		writeflags |= HFILE_NO_REWRITE;
4811	    saveandpophiststack(1, writeflags);
4812	    savehistfile(NULL, 1, writeflags);
4813	}
4814	if (islogin && !subsh) {
4815	    sourcehome(".zlogout");
4816#ifdef GLOBAL_ZLOGOUT
4817	    if (isset(RCS) && isset(GLOBALRCS))
4818		source(GLOBAL_ZLOGOUT);
4819#endif
4820	}
4821    }
4822    lastval = val;
4823    if (sigtrapped[SIGEXIT])
4824	dotrap(SIGEXIT);
4825    callhookfunc("zshexit", NULL, 1, NULL);
4826    runhookdef(EXITHOOK, NULL);
4827    if (opts[MONITOR] && interact && (SHTTY != -1)) {
4828       release_pgrp();
4829    }
4830    if (mypid != getpid())
4831	_exit(val);
4832    else
4833	exit(val);
4834}
4835
4836/* . (dot), source */
4837
4838/**/
4839int
4840bin_dot(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
4841{
4842    char **old, *old0 = NULL;
4843    int diddot = 0, dotdot = 0;
4844    char *s, **t, *enam, *arg0, *buf;
4845    struct stat st;
4846    enum source_return ret;
4847
4848    if (!*argv)
4849	return 0;
4850    old = pparams;
4851    /* get arguments for the script */
4852    if (argv[1])
4853	pparams = zarrdup(argv + 1);
4854
4855    enam = arg0 = ztrdup(*argv);
4856    if (isset(FUNCTIONARGZERO)) {
4857	old0 = argzero;
4858	argzero = ztrdup(arg0);
4859    }
4860    s = unmeta(enam);
4861    errno = ENOENT;
4862    ret = SOURCE_NOT_FOUND;
4863    /* for source only, check in current directory first */
4864    if (*name != '.' && access(s, F_OK) == 0
4865	&& stat(s, &st) >= 0 && !S_ISDIR(st.st_mode)) {
4866	diddot = 1;
4867	ret = source(enam);
4868    }
4869    if (ret == SOURCE_NOT_FOUND) {
4870	/* use a path with / in it */
4871	for (s = arg0; *s; s++)
4872	    if (*s == '/') {
4873		if (*arg0 == '.') {
4874		    if (arg0 + 1 == s)
4875			++diddot;
4876		    else if (arg0[1] == '.' && arg0 + 2 == s)
4877			++dotdot;
4878		}
4879		ret = source(arg0);
4880		break;
4881	    }
4882	if (!*s || (ret == SOURCE_NOT_FOUND &&
4883		    isset(PATHDIRS) && diddot < 2 && dotdot == 0)) {
4884	    pushheap();
4885	    /* search path for script */
4886	    for (t = path; *t; t++) {
4887		if (!(*t)[0] || ((*t)[0] == '.' && !(*t)[1])) {
4888		    if (diddot)
4889			continue;
4890		    diddot = 1;
4891		    buf = dupstring(arg0);
4892		} else
4893		    buf = zhtricat(*t, "/", arg0);
4894
4895		s = unmeta(buf);
4896		if (access(s, F_OK) == 0 && stat(s, &st) >= 0
4897		    && !S_ISDIR(st.st_mode)) {
4898		    ret = source(enam = buf);
4899		    break;
4900		}
4901	    }
4902	    popheap();
4903	}
4904    }
4905    /* clean up and return */
4906    if (argv[1]) {
4907	freearray(pparams);
4908	pparams = old;
4909    }
4910    if (ret == SOURCE_NOT_FOUND) {
4911	if (isset(POSIXBUILTINS)) {
4912	    /* hard error in POSIX (we'll exit later) */
4913	    zerrnam(name, "%e: %s", errno, enam);
4914	} else {
4915	    zwarnnam(name, "%e: %s", errno, enam);
4916	}
4917    }
4918    zsfree(arg0);
4919    if (old0) {
4920	zsfree(argzero);
4921	argzero = old0;
4922    }
4923    return ret == SOURCE_OK ? lastval : 128 - ret;
4924}
4925
4926/*
4927 * common for bin_emulate and bin_eval
4928 */
4929
4930static int
4931eval(char **argv)
4932{
4933    Eprog prog;
4934    char *oscriptname = scriptname;
4935    int oineval = ineval, fpushed;
4936    struct funcstack fstack;
4937
4938    /*
4939     * If EVALLINENO is not set, we use the line number of the
4940     * environment and must flag this up to exec.c.  Otherwise,
4941     * we use a special script name to indicate the special line number.
4942     */
4943    ineval = !isset(EVALLINENO);
4944    if (!ineval) {
4945	scriptname = "(eval)";
4946	fstack.prev = funcstack;
4947	fstack.name = scriptname;
4948	fstack.caller = funcstack ? funcstack->name : dupstring(argzero);
4949	fstack.lineno = lineno;
4950	fstack.tp = FS_EVAL;
4951
4952	/*
4953	 * To get file line numbers, we need to know if parent is
4954	 * the original script/shell or a sourced file, in which
4955	 * case we use the line number raw, or a function or eval,
4956	 * in which case we need to deduce where that came from.
4957	 *
4958	 * This replicates the logic for working out the information
4959	 * for $funcfiletrace---eval is similar to an inlined function
4960	 * call from a tracing perspective.
4961	 */
4962	if (!funcstack || funcstack->tp == FS_SOURCE) {
4963	    fstack.flineno = fstack.lineno;
4964	    fstack.filename = fstack.caller;
4965	} else {
4966	    fstack.flineno = funcstack->flineno + lineno;
4967	    /*
4968	     * Line numbers in eval start from 1, not zero,
4969	     * so offset by one to get line in file.
4970	     */
4971	    if (funcstack->tp == FS_EVAL)
4972		fstack.flineno--;
4973	    fstack.filename = funcstack->filename;
4974	    if (!fstack.filename)
4975		fstack.filename = "";
4976	}
4977	funcstack = &fstack;
4978
4979	fpushed = 1;
4980    } else
4981	fpushed = 0;
4982
4983    prog = parse_string(zjoin(argv, ' ', 1), 1);
4984    if (prog) {
4985	if (wc_code(*prog->prog) != WC_LIST) {
4986	    /* No code to execute */
4987	    lastval = 0;
4988	} else {
4989	    execode(prog, 1, 0, "eval");
4990
4991	    if (errflag && !lastval)
4992		lastval = errflag;
4993	}
4994    } else {
4995	lastval = 1;
4996    }
4997
4998    if (fpushed)
4999	funcstack = funcstack->prev;
5000
5001    errflag = 0;
5002    scriptname = oscriptname;
5003    ineval = oineval;
5004
5005    return lastval;
5006}
5007
5008/* emulate: set emulation mode and optionally evaluate shell code */
5009
5010/**/
5011int
5012bin_emulate(UNUSED(char *nam), char **argv, Options ops, UNUSED(int func))
5013{
5014    int opt_L = OPT_ISSET(ops, 'L');
5015    int opt_R = OPT_ISSET(ops, 'R');
5016    int saveemulation, savehackchar;
5017    int ret = 1, new_emulation;
5018    char saveopts[OPT_SIZE], new_opts[OPT_SIZE];
5019    char *cmd = 0;
5020    const char *shname = *argv;
5021    LinkList optlist;
5022    LinkNode optnode;
5023    Emulation_options save_sticky;
5024    OptIndex *on_ptr, *off_ptr;
5025
5026    /* without arguments just print current emulation */
5027    if (!shname) {
5028	if (opt_L || opt_R) {
5029	    zwarnnam("emulate", "not enough arguments");
5030	    return 1;
5031	}
5032
5033	switch(SHELL_EMULATION()) {
5034	case EMULATE_CSH:
5035	    shname = "csh";
5036	    break;
5037
5038	case EMULATE_KSH:
5039	    shname = "ksh";
5040	    break;
5041
5042	case EMULATE_SH:
5043	    shname = "sh";
5044	    break;
5045
5046	default:
5047	    shname = "zsh";
5048	    break;
5049	}
5050
5051	printf("%s\n", shname);
5052	return 0;
5053    }
5054
5055    /* with single argument set current emulation */
5056    if (!argv[1]) {
5057	emulate(shname, OPT_ISSET(ops,'R'), &emulation, opts);
5058	if (OPT_ISSET(ops,'L'))
5059	    opts[LOCALOPTIONS] = opts[LOCALTRAPS] = 1;
5060	return 0;
5061    }
5062
5063    argv++;
5064    memcpy(saveopts, opts, sizeof(opts));
5065    memcpy(new_opts, opts, sizeof(opts));
5066    savehackchar = keyboardhackchar;
5067    emulate(shname, OPT_ISSET(ops,'R'), &new_emulation, new_opts);
5068    optlist = newlinklist();
5069    if (parseopts("emulate", &argv, new_opts, &cmd, optlist)) {
5070	ret = 1;
5071	goto restore;
5072    }
5073
5074    /* parseopts() has consumed anything that looks like an option */
5075    if (*argv) {
5076	zwarnnam("emulate", "unknown argument %s", *argv);
5077	goto restore;
5078    }
5079
5080    saveemulation = emulation;
5081    emulation = new_emulation;
5082    memcpy(opts, new_opts, sizeof(opts));
5083    /* If "-c command" is given, evaluate command using specified
5084     * emulation mode.
5085     */
5086    if (cmd) {
5087	if (opt_L) {
5088	    zwarnnam("emulate", "option -L incompatible with -c");
5089	    goto restore;
5090	}
5091	*--argv = cmd;	/* on stack, never free()d, see execbuiltin() */
5092    } else
5093	return 0;
5094
5095    save_sticky = sticky;
5096    sticky = hcalloc(sizeof(*sticky));
5097    sticky->emulation = emulation;
5098    for (optnode = firstnode(optlist); optnode; incnode(optnode)) {
5099	/* Data is index into new_opts */
5100	char *optptr = (char *)getdata(optnode);
5101	if (*optptr)
5102	    sticky->n_on_opts++;
5103	else
5104	    sticky->n_off_opts++;
5105    }
5106    if (sticky->n_on_opts)
5107	on_ptr = sticky->on_opts =
5108	    zhalloc(sticky->n_on_opts * sizeof(*sticky->on_opts));
5109    else
5110	on_ptr = NULL;
5111    if (sticky->n_off_opts)
5112	off_ptr = sticky->off_opts = zhalloc(sticky->n_off_opts *
5113					     sizeof(*sticky->off_opts));
5114    else
5115	off_ptr = NULL;
5116    for (optnode = firstnode(optlist); optnode; incnode(optnode)) {
5117	/* Data is index into new_opts */
5118	char *optptr = (char *)getdata(optnode);
5119	int optno = optptr - new_opts;
5120	if (*optptr)
5121	    *on_ptr++ = optno;
5122	else
5123	    *off_ptr++ = optno;
5124    }
5125    ret = eval(argv);
5126    sticky = save_sticky;
5127    emulation = saveemulation;
5128    memcpy(opts, saveopts, sizeof(opts));
5129restore:
5130    keyboardhackchar = savehackchar;
5131    inittyptab();	/* restore banghist */
5132    return ret;
5133}
5134
5135/* eval: simple evaluation */
5136
5137/**/
5138mod_export int ineval;
5139
5140/**/
5141int
5142bin_eval(UNUSED(char *nam), char **argv, UNUSED(Options ops), UNUSED(int func))
5143{
5144    return eval(argv);
5145}
5146
5147static char *zbuf;
5148static int readfd;
5149
5150/* Read a character from readfd, or from the buffer zbuf.  Return EOF on end of
5151file/buffer. */
5152
5153/* read: get a line of input, or (for compctl functions) return some *
5154 * useful data about the state of the editing line.  The -E and -e   *
5155 * options mean that the result should be sent to stdout.  -e means, *
5156 * in addition, that the result should not actually be assigned to   *
5157 * the specified parameters.                                         */
5158
5159/**/
5160int
5161bin_read(char *name, char **args, Options ops, UNUSED(int func))
5162{
5163    char *reply, *readpmpt;
5164    int bsiz, c = 0, gotnl = 0, al = 0, first, nchars = 1, bslash, keys = 0;
5165    int haso = 0;	/* true if /dev/tty has been opened specially */
5166    int isem = !strcmp(term, "emacs"), izle = zleactive;
5167    char *buf, *bptr, *firstarg, *zbuforig;
5168    LinkList readll = newlinklist();
5169    FILE *oshout = NULL;
5170    int readchar = -1, val, resettty = 0;
5171    struct ttyinfo saveti;
5172    char d;
5173    long izle_timeout = 0;
5174#ifdef MULTIBYTE_SUPPORT
5175    wchar_t delim = L'\n', wc;
5176    mbstate_t mbs;
5177    char *laststart;
5178    size_t ret;
5179#else
5180    char delim = '\n';
5181#endif
5182
5183    if (OPT_HASARG(ops,c='k')) {
5184	char *eptr, *optarg = OPT_ARG(ops,c);
5185	nchars = (int)zstrtol(optarg, &eptr, 10);
5186	if (*eptr) {
5187	    zwarnnam(name, "number expected after -%c: %s", c, optarg);
5188	    return 1;
5189	}
5190    }
5191    /* This `*args++ : *args' looks a bit weird, but it works around a bug
5192     * in gcc-2.8.1 under DU 4.0. */
5193    firstarg = (*args && **args == '?' ? *args++ : *args);
5194    reply = *args ? *args++ : OPT_ISSET(ops,'A') ? "reply" : "REPLY";
5195
5196    if (OPT_ISSET(ops,'A') && *args) {
5197	zwarnnam(name, "only one array argument allowed");
5198	return 1;
5199    }
5200
5201    /* handle compctl case */
5202    if(OPT_ISSET(ops,'l') || OPT_ISSET(ops,'c'))
5203	return compctlreadptr(name, args, ops, reply);
5204
5205    if ((OPT_ISSET(ops,'k') || OPT_ISSET(ops,'q')) &&
5206	!OPT_ISSET(ops,'u') && !OPT_ISSET(ops,'p')) {
5207	if (!zleactive) {
5208	    if (SHTTY == -1) {
5209		/* need to open /dev/tty specially */
5210		if ((SHTTY = open("/dev/tty", O_RDWR|O_NOCTTY)) != -1) {
5211		    haso = 1;
5212		    oshout = shout;
5213		    init_shout();
5214		}
5215	    } else if (!shout) {
5216		/* We need an output FILE* on the tty */
5217		init_shout();
5218	    }
5219	    /* We should have a SHTTY opened by now. */
5220	    if (SHTTY == -1) {
5221		/* Unfortunately, we didn't. */
5222		fprintf(stderr, "not interactive and can't open terminal\n");
5223		fflush(stderr);
5224		return 1;
5225	    }
5226	    if (unset(INTERACTIVE))
5227		gettyinfo(&shttyinfo);
5228	    /* attach to the tty */
5229	    attachtty(mypgrp);
5230	    if (!isem)
5231		setcbreak();
5232	    readfd = SHTTY;
5233	}
5234	keys = 1;
5235    } else if (OPT_HASARG(ops,'u') && !OPT_ISSET(ops,'p')) {
5236	/* -u means take input from the specified file descriptor. */
5237	char *eptr, *argptr = OPT_ARG(ops,'u');
5238	/* The old code handled -up, but that was never documented. Still...*/
5239	if (!strcmp(argptr, "p")) {
5240	    readfd = coprocin;
5241	    if (readfd < 0) {
5242		zwarnnam(name, "-p: no coprocess");
5243		return 1;
5244	    }
5245	} else {
5246	    readfd = (int)zstrtol(argptr, &eptr, 10);
5247	    if (*eptr) {
5248		zwarnnam(name, "number expected after -%c: %s", 'u', argptr);
5249		return 1;
5250	    }
5251	}
5252#if 0
5253	/* This code is left as a warning to future generations --- pws. */
5254	for (readfd = 9; readfd && !OPT_ISSET(ops,readfd + '0'); --readfd);
5255#endif
5256	izle = 0;
5257    } else if (OPT_ISSET(ops,'p')) {
5258	readfd = coprocin;
5259	if (readfd < 0) {
5260	    zwarnnam(name, "-p: no coprocess");
5261	    return 1;
5262	}
5263	izle = 0;
5264    } else
5265	readfd = izle = 0;
5266
5267    if (OPT_ISSET(ops,'s') && SHTTY != -1) {
5268	struct ttyinfo ti;
5269	gettyinfo(&ti);
5270	saveti = ti;
5271	resettty = 1;
5272#ifdef HAS_TIO
5273	ti.tio.c_lflag &= ~ECHO;
5274#else
5275	ti.sgttyb.sg_flags &= ~ECHO;
5276#endif
5277	settyinfo(&ti);
5278    }
5279
5280    /* handle prompt */
5281    if (firstarg) {
5282	for (readpmpt = firstarg;
5283	     *readpmpt && *readpmpt != '?'; readpmpt++);
5284	if (*readpmpt++) {
5285	    if (keys || isatty(0)) {
5286		zputs(readpmpt, (shout ? shout : stderr));
5287		fflush(shout ? shout : stderr);
5288	    }
5289	    readpmpt[-1] = '\0';
5290	}
5291    }
5292
5293    if (OPT_ISSET(ops,'d')) {
5294	char *delimstr = OPT_ARG(ops,'d');
5295#ifdef MULTIBYTE_SUPPORT
5296	wint_t wi;
5297
5298	if (isset(MULTIBYTE)) {
5299	    mb_metacharinit();
5300	    (void)mb_metacharlenconv(delimstr, &wi);
5301	}
5302	else
5303	    wi = WEOF;
5304	if (wi != WEOF)
5305	    delim = (wchar_t)wi;
5306	else
5307	    delim = (wchar_t)((delimstr[0] == Meta) ?
5308			      delimstr[1] ^ 32 : delimstr[0]);
5309#else
5310        delim = (delimstr[0] == Meta) ? delimstr[1] ^ 32 : delimstr[0];
5311#endif
5312	if (SHTTY != -1) {
5313	    struct ttyinfo ti;
5314	    gettyinfo(&ti);
5315	    if (! resettty) {
5316	      saveti = ti;
5317	      resettty = 1;
5318	    }
5319#ifdef HAS_TIO
5320	    ti.tio.c_lflag &= ~ICANON;
5321	    ti.tio.c_cc[VMIN] = 1;
5322	    ti.tio.c_cc[VTIME] = 0;
5323#else
5324	    ti.sgttyb.sg_flags |= CBREAK;
5325#endif
5326	    settyinfo(&ti);
5327	}
5328    }
5329    if (OPT_ISSET(ops,'t')) {
5330	zlong timeout = 0;
5331	if (OPT_HASARG(ops,'t')) {
5332	    mnumber mn = zero_mnumber;
5333	    mn = matheval(OPT_ARG(ops,'t'));
5334	    if (errflag)
5335		return 1;
5336	    if (mn.type == MN_FLOAT) {
5337		mn.u.d *= 1e6;
5338		timeout = (zlong)mn.u.d;
5339	    } else {
5340		timeout = (zlong)mn.u.l * (zlong)1000000;
5341	    }
5342	}
5343	if (izle) {
5344	    /*
5345	     * Timeout is in 100ths of a second rather than us.
5346	     * See calc_timeout() in zle_main for format of this.
5347	     */
5348	    timeout = -(timeout/(zlong)10000 + 1L);
5349	    izle_timeout = (long)timeout;
5350#ifdef LONG_MAX
5351	    /* saturate if range exceeded */
5352	    if ((zlong)izle_timeout != timeout)
5353		izle_timeout = LONG_MAX;
5354#endif
5355	} else {
5356	    if (readfd == -1 ||
5357		!read_poll(readfd, &readchar, keys && !zleactive,
5358			   timeout)) {
5359		if (keys && !zleactive && !isem)
5360		    settyinfo(&shttyinfo);
5361		else if (resettty && SHTTY != -1)
5362		    settyinfo(&saveti);
5363		if (haso) {
5364		    fclose(shout);
5365		    shout = oshout;
5366		    SHTTY = -1;
5367		}
5368		return OPT_ISSET(ops,'q') ? 2 : 1;
5369	    }
5370	}
5371    }
5372
5373#ifdef MULTIBYTE_SUPPORT
5374    memset(&mbs, 0, sizeof(mbs));
5375#endif
5376
5377    /*
5378     * option -k means read only a given number of characters (default 1)
5379     * option -q means get one character, and interpret it as a Y or N
5380     */
5381    if (OPT_ISSET(ops,'k') || OPT_ISSET(ops,'q')) {
5382	int eof = 0;
5383	/* allocate buffer space for result */
5384#ifdef MULTIBYTE_SUPPORT
5385	bptr = buf = (char *)zalloc(nchars*MB_CUR_MAX+1);
5386#else
5387	bptr = buf = (char *)zalloc(nchars+1);
5388#endif
5389
5390	do {
5391	    if (izle) {
5392		zleentry(ZLE_CMD_GET_KEY, izle_timeout, NULL, &val);
5393		if (val < 0) {
5394		    eof = 1;
5395		    break;
5396		}
5397		*bptr = (char) val;
5398#ifdef MULTIBYTE_SUPPORT
5399		if (isset(MULTIBYTE)) {
5400		    ret = mbrlen(bptr++, 1, &mbs);
5401		    if (ret == MB_INVALID)
5402			memset(&mbs, 0, sizeof(mbs));
5403		    /* treat invalid as single character */
5404		    if (ret != MB_INCOMPLETE)
5405			nchars--;
5406		    continue;
5407		} else {
5408		    bptr++;
5409		    nchars--;
5410		}
5411#else
5412		bptr++;
5413		nchars--;
5414#endif
5415	    } else {
5416		/* If read returns 0, is end of file */
5417		if (readchar >= 0) {
5418		    *bptr = readchar;
5419		    val = 1;
5420		    readchar = -1;
5421		} else {
5422		    while ((val = read(readfd, bptr, nchars)) < 0) {
5423			if (errno != EINTR ||
5424			    errflag || retflag || breaks || contflag)
5425			    break;
5426		    }
5427		    if (val <= 0) {
5428			eof = 1;
5429			break;
5430		    }
5431		}
5432
5433#ifdef MULTIBYTE_SUPPORT
5434		if (isset(MULTIBYTE)) {
5435		    while (val > 0) {
5436			ret = mbrlen(bptr, val, &mbs);
5437			if (ret == MB_INCOMPLETE) {
5438			    bptr += val;
5439			    break;
5440			} else {
5441			    if (ret == MB_INVALID) {
5442				memset(&mbs, 0, sizeof(mbs));
5443				/* treat as single byte */
5444				ret = 1;
5445			    }
5446			    else if (ret == 0) /* handle null as normal char */
5447				ret = 1;
5448			    else if (ret > (size_t)val) {
5449				/* Some mbrlen()s return the full char len */
5450				ret = val;
5451			    }
5452			    nchars--;
5453			    val -= ret;
5454			    bptr += ret;
5455			}
5456		    }
5457		    continue;
5458		}
5459#endif
5460		/* decrement number of characters read from number required */
5461		nchars -= val;
5462
5463		/* increment pointer past read characters */
5464		bptr += val;
5465	    }
5466	} while (nchars > 0);
5467
5468	if (!izle && !OPT_ISSET(ops,'u') && !OPT_ISSET(ops,'p')) {
5469	    /* dispose of result appropriately, etc. */
5470	    if (isem)
5471		while (val > 0 && read(SHTTY, &d, 1) == 1 && d != '\n');
5472	    else {
5473		settyinfo(&shttyinfo);
5474		resettty = 0;
5475	    }
5476	    if (haso) {
5477		fclose(shout);	/* close(SHTTY) */
5478		shout = oshout;
5479		SHTTY = -1;
5480	    }
5481	}
5482
5483	if (OPT_ISSET(ops,'q'))
5484	{
5485	    /*
5486	     * Keep eof as status but status is now whether we read
5487	     * 'y' or 'Y'.  If we timed out, status is 2.
5488	     */
5489	    if (eof)
5490		eof = 2;
5491	    else
5492		eof = (bptr - buf != 1 || (buf[0] != 'y' && buf[0] != 'Y'));
5493	}
5494	if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E'))
5495	    fwrite(buf, bptr - buf, 1, stdout);
5496	if (!OPT_ISSET(ops,'e'))
5497	    setsparam(reply, metafy(buf, bptr - buf, META_REALLOC));
5498	else
5499	    zfree(buf, bptr - buf + 1);
5500	if (resettty && SHTTY != -1)
5501	    settyinfo(&saveti);
5502	return eof;
5503    }
5504
5505    /* All possible special types of input have been exhausted.  Take one line,
5506       and assign words to the parameters until they run out.  Leftover words go
5507       onto the last parameter.  If an array is specified, all the words become
5508       separate elements of the array. */
5509
5510    zbuforig = zbuf = (!OPT_ISSET(ops,'z')) ? NULL :
5511	(nonempty(bufstack)) ? (char *) getlinknode(bufstack) : ztrdup("");
5512    first = 1;
5513    bslash = 0;
5514    while (*args || (OPT_ISSET(ops,'A') && !gotnl)) {
5515	sigset_t s = child_unblock();
5516	buf = bptr = (char *)zalloc(bsiz = 64);
5517#ifdef MULTIBYTE_SUPPORT
5518	laststart = buf;
5519	ret = MB_INCOMPLETE;
5520#endif
5521	/* get input, a character at a time */
5522	while (!gotnl) {
5523	    c = zread(izle, &readchar, izle_timeout);
5524	    /* \ at the end of a line indicates a continuation *
5525	     * line, except in raw mode (-r option)            */
5526#ifdef MULTIBYTE_SUPPORT
5527	    if (c == EOF) {
5528		/* not waiting to be completed any more */
5529		ret = 0;
5530		break;
5531	    }
5532	    *bptr = (char)c;
5533	    if (isset(MULTIBYTE)) {
5534		ret = mbrtowc(&wc, bptr, 1, &mbs);
5535		if (!ret)	/* NULL */
5536		    ret = 1;
5537	    } else {
5538		ret = 1;
5539		wc = (wchar_t)c;
5540	    }
5541	    if (ret != MB_INCOMPLETE) {
5542		if (ret == MB_INVALID) {
5543		    memset(&mbs, 0, sizeof(mbs));
5544		    /* Treat this as a single character */
5545		    wc = (wchar_t)c;
5546		    laststart = bptr;
5547		}
5548		if (bslash && wc == delim) {
5549		    bslash = 0;
5550		    continue;
5551		}
5552		if (wc == delim)
5553		    break;
5554		/*
5555		 * `first' is non-zero if any separator we encounter is a
5556		 * non-whitespace separator, which means that anything
5557		 * (even an empty string) between, before or after separators
5558		 * is significant.  If it is zero, we have a whitespace
5559		 * separator, which shouldn't cause extra empty strings to
5560		 * be emitted.  Hence the test for (*buf || first) when
5561		 * we assign the result of reading a word.
5562		 */
5563		if (!bslash && wcsitype(wc, ISEP)) {
5564		    if (bptr != buf ||
5565			(!(c < 128 && iwsep(c)) && first)) {
5566			first |= !(c < 128 && iwsep(c));
5567			break;
5568		    }
5569		    first |= !(c < 128 && iwsep(c));
5570		    continue;
5571		}
5572		bslash = (wc == L'\\' && !bslash && !OPT_ISSET(ops,'r'));
5573		if (bslash)
5574		    continue;
5575		first = 0;
5576	    }
5577	    if (imeta(STOUC(*bptr))) {
5578		bptr[1] = bptr[0] ^ 32;
5579		bptr[0] = Meta;
5580		bptr += 2;
5581	    }
5582	    else
5583		bptr++;
5584	    if (ret != MB_INCOMPLETE)
5585		laststart = bptr;
5586#else
5587	    if (c == EOF)
5588		break;
5589	    if (bslash && c == delim) {
5590		bslash = 0;
5591		continue;
5592	    }
5593	    if (c == delim)
5594		break;
5595	    /*
5596	     * `first' is non-zero if any separator we encounter is a
5597	     * non-whitespace separator, which means that anything
5598	     * (even an empty string) between, before or after separators
5599	     * is significant.  If it is zero, we have a whitespace
5600	     * separator, which shouldn't cause extra empty strings to
5601	     * be emitted.  Hence the test for (*buf || first) when
5602	     * we assign the result of reading a word.
5603	     */
5604	    if (!bslash && isep(c)) {
5605		if (bptr != buf || (!iwsep(c) && first)) {
5606		    first |= !iwsep(c);
5607		    break;
5608		}
5609		first |= !iwsep(c);
5610		continue;
5611	    }
5612	    bslash = c == '\\' && !bslash && !OPT_ISSET(ops,'r');
5613	    if (bslash)
5614		continue;
5615	    first = 0;
5616	    if (imeta(c)) {
5617		*bptr++ = Meta;
5618		*bptr++ = c ^ 32;
5619	    } else
5620		*bptr++ = c;
5621#endif
5622	    /* increase the buffer size, if necessary */
5623	    if (bptr >= buf + bsiz - 1) {
5624		int blen = bptr - buf;
5625#ifdef MULTIBYTE_SUPPORT
5626		int llen = laststart - buf;
5627#endif
5628
5629		buf = realloc(buf, bsiz *= 2);
5630		bptr = buf + blen;
5631#ifdef MULTIBYTE_SUPPORT
5632		laststart = buf + llen;
5633#endif
5634	    }
5635	}
5636	signal_setmask(s);
5637#ifdef MULTIBYTE_SUPPORT
5638	if (c == EOF) {
5639	    gotnl = 1;
5640	    *bptr = '\0';	/* see below */
5641	} else if (ret == MB_INCOMPLETE) {
5642	    /*
5643	     * We can only get here if there is an EOF in the
5644	     * middle of a character... safest to keep the debris,
5645	     * I suppose.
5646	     */
5647	    *bptr = '\0';
5648	} else {
5649	    if (wc == delim)
5650		gotnl = 1;
5651	    *laststart = '\0';
5652	}
5653#else
5654	if (c == delim || c == EOF)
5655	    gotnl = 1;
5656	*bptr = '\0';
5657#endif
5658	/* dispose of word appropriately */
5659	if (OPT_ISSET(ops,'e') ||
5660	    /*
5661	     * When we're doing an array assignment, we'll
5662	     * handle echoing at that point.  In all other
5663	     * cases (including -A with no assignment)
5664	     * we'll do it here.
5665	     */
5666	    (OPT_ISSET(ops,'E') && !OPT_ISSET(ops,'A'))) {
5667	    zputs(buf, stdout);
5668	    putchar('\n');
5669	}
5670	if (!OPT_ISSET(ops,'e') && (*buf || first)) {
5671	    if (OPT_ISSET(ops,'A')) {
5672		addlinknode(readll, buf);
5673		al++;
5674	    } else
5675		setsparam(reply, buf);
5676	} else
5677	    free(buf);
5678	if (!OPT_ISSET(ops,'A'))
5679	    reply = *args++;
5680    }
5681    /* handle EOF */
5682    if (c == EOF) {
5683	if (readfd == coprocin) {
5684	    close(coprocin);
5685	    close(coprocout);
5686	    coprocin = coprocout = -1;
5687	}
5688    }
5689    /* final assignment (and display) of array parameter */
5690    if (OPT_ISSET(ops,'A')) {
5691	char **pp, **p = NULL;
5692	LinkNode n;
5693
5694	p = (OPT_ISSET(ops,'e') ? (char **)NULL
5695	     : (char **)zalloc((al + 1) * sizeof(char *)));
5696
5697	for (pp = p, n = firstnode(readll); n; incnode(n)) {
5698	    if (OPT_ISSET(ops,'E')) {
5699		zputs((char *) getdata(n), stdout);
5700		putchar('\n');
5701	    }
5702	    if (p)
5703		*pp++ = (char *)getdata(n);
5704	    else
5705		zsfree(getdata(n));
5706	}
5707	if (p) {
5708	    *pp++ = NULL;
5709	    setaparam(reply, p);
5710	}
5711	if (resettty && SHTTY != -1)
5712	    settyinfo(&saveti);
5713	return c == EOF;
5714    }
5715    buf = bptr = (char *)zalloc(bsiz = 64);
5716#ifdef MULTIBYTE_SUPPORT
5717    laststart = buf;
5718    ret = MB_INCOMPLETE;
5719#endif
5720    /* any remaining part of the line goes into one parameter */
5721    bslash = 0;
5722    if (!gotnl) {
5723	sigset_t s = child_unblock();
5724	for (;;) {
5725	    c = zread(izle, &readchar, izle_timeout);
5726#ifdef MULTIBYTE_SUPPORT
5727	    if (c == EOF) {
5728		/* not waiting to be completed any more */
5729		ret = 0;
5730		break;
5731	    }
5732	    *bptr = (char)c;
5733	    if (isset(MULTIBYTE)) {
5734		ret = mbrtowc(&wc, bptr, 1, &mbs);
5735		if (!ret)	/* NULL */
5736		    ret = 1;
5737	    } else {
5738		ret = 1;
5739		wc = (wchar_t)c;
5740	    }
5741	    if (ret != MB_INCOMPLETE) {
5742		if (ret == MB_INVALID) {
5743		    memset(&mbs, 0, sizeof(mbs));
5744		    /* Treat this as a single character */
5745		    wc = (wchar_t)c;
5746		    laststart = bptr;
5747		}
5748		/*
5749		 * \ at the end of a line introduces a continuation line,
5750		 * except in raw mode (-r option)
5751		 */
5752		if (bslash && wc == delim) {
5753		    bslash = 0;
5754		    continue;
5755		}
5756		if (wc == delim && !zbuf)
5757		    break;
5758		if (!bslash && bptr == buf && wcsitype(wc, ISEP)) {
5759		    if (c < 128 && iwsep(c))
5760			continue;
5761		    else if (!first) {
5762			first = 1;
5763			continue;
5764		    }
5765		}
5766		bslash = (wc == L'\\' && !bslash && !OPT_ISSET(ops,'r'));
5767		if (bslash)
5768		    continue;
5769	    }
5770	    if (imeta(STOUC(*bptr))) {
5771		bptr[1] = bptr[0] ^ 32;
5772		bptr[0] = Meta;
5773		bptr += 2;
5774	    }
5775	    else
5776		bptr++;
5777	    if (ret != MB_INCOMPLETE)
5778		laststart = bptr;
5779#else
5780	    /* \ at the end of a line introduces a continuation line, except in
5781	       raw mode (-r option) */
5782	    if (bslash && c == delim) {
5783		bslash = 0;
5784		continue;
5785	    }
5786	    if (c == EOF || (c == delim && !zbuf))
5787		break;
5788	    if (!bslash && isep(c) && bptr == buf) {
5789		if (iwsep(c))
5790		    continue;
5791		else if (!first) {
5792		    first = 1;
5793		    continue;
5794		}
5795	    }
5796	    bslash = c == '\\' && !bslash && !OPT_ISSET(ops,'r');
5797	    if (bslash)
5798		continue;
5799	    if (imeta(c)) {
5800		*bptr++ = Meta;
5801		*bptr++ = c ^ 32;
5802	    } else
5803		*bptr++ = c;
5804#endif
5805	    /* increase the buffer size, if necessary */
5806	    if (bptr >= buf + bsiz - 1) {
5807		int blen = bptr - buf;
5808#ifdef MULTIBYTE_SUPPORT
5809		int llen = laststart - buf;
5810#endif
5811
5812		buf = realloc(buf, bsiz *= 2);
5813		bptr = buf + blen;
5814#ifdef MULTIBYTE_SUPPORT
5815		laststart = buf + llen;
5816#endif
5817	    }
5818	}
5819	signal_setmask(s);
5820    }
5821#ifdef MULTIBYTE_SUPPORT
5822    if (ret != MB_INCOMPLETE)
5823	bptr = laststart;
5824#endif
5825    /*
5826     * Strip trailing IFS whitespace.
5827     * iwsep can only be certain single-byte ASCII bytes, but we
5828     * must check the byte isn't metafied.
5829     */
5830    while (bptr > buf) {
5831	if (bptr > buf + 1 && bptr[-2] == Meta) {
5832	    /* non-ASCII, can't be IWSEP */
5833	    break;
5834	} else if (iwsep(bptr[-1]))
5835	    bptr--;
5836	else
5837	    break;
5838    }
5839    *bptr = '\0';
5840    if (resettty && SHTTY != -1)
5841	settyinfo(&saveti);
5842    /* final assignment of reply, etc. */
5843    if (OPT_ISSET(ops,'e') || OPT_ISSET(ops,'E')) {
5844	zputs(buf, stdout);
5845	putchar('\n');
5846    }
5847    if (!OPT_ISSET(ops,'e'))
5848	setsparam(reply, buf);
5849    else
5850	zsfree(buf);
5851    if (zbuforig) {
5852	char first = *zbuforig;
5853
5854	zsfree(zbuforig);
5855	if (!first)
5856	    return 1;
5857    } else if (c == EOF) {
5858	if (readfd == coprocin) {
5859	    close(coprocin);
5860	    close(coprocout);
5861	    coprocin = coprocout = -1;
5862	}
5863	return 1;
5864    }
5865    /*
5866     * The following is to ensure a failure to set the parameter
5867     * causes a non-zero status return.  There are arguments for
5868     * turning a non-zero status into errflag more widely.
5869     */
5870    return errflag;
5871}
5872
5873/**/
5874static int
5875zread(int izle, int *readchar, long izle_timeout)
5876{
5877    char cc, retry = 0;
5878    int ret;
5879
5880    if (izle) {
5881	int c;
5882	zleentry(ZLE_CMD_GET_KEY, izle_timeout, NULL, &c);
5883
5884	return (c < 0 ? EOF : c);
5885    }
5886    /* use zbuf if possible */
5887    if (zbuf) {
5888	/* If zbuf points to anything, it points to the next character in the
5889	   buffer.  This may be a null byte to indicate EOF.  If reading from the
5890	   buffer, move on the buffer pointer. */
5891	if (*zbuf == Meta)
5892	    return zbuf++, STOUC(*zbuf++ ^ 32);
5893	else
5894	    return (*zbuf) ? STOUC(*zbuf++) : EOF;
5895    }
5896    if (*readchar >= 0) {
5897	cc = *readchar;
5898	*readchar = -1;
5899	return STOUC(cc);
5900    }
5901    for (;;) {
5902	/* read a character from readfd */
5903	ret = read(readfd, &cc, 1);
5904	switch (ret) {
5905	case 1:
5906	    /* return the character read */
5907	    return STOUC(cc);
5908	case -1:
5909#if defined(EAGAIN) || defined(EWOULDBLOCK)
5910	    if (!retry && readfd == 0 && (
5911# ifdef EAGAIN
5912		errno == EAGAIN
5913#  ifdef EWOULDBLOCK
5914		||
5915#  endif /* EWOULDBLOCK */
5916# endif /* EAGAIN */
5917# ifdef EWOULDBLOCK
5918		errno == EWOULDBLOCK
5919# endif /* EWOULDBLOCK */
5920		) && setblock_stdin()) {
5921		retry = 1;
5922		continue;
5923	    } else
5924#endif /* EAGAIN || EWOULDBLOCK */
5925		if (errno == EINTR && !(errflag || retflag || breaks || contflag))
5926		    continue;
5927	    break;
5928	}
5929	return EOF;
5930    }
5931}
5932
5933/* holds arguments for testlex() */
5934/**/
5935char **testargs, **curtestarg;
5936
5937/* test, [: the old-style general purpose logical expression builtin */
5938
5939/**/
5940void
5941testlex(void)
5942{
5943    if (tok == LEXERR)
5944	return;
5945
5946    tokstr = *(curtestarg = testargs);
5947    if (!*testargs) {
5948	/* if tok is already zero, reading past the end:  error */
5949	tok = tok ? NULLTOK : LEXERR;
5950	return;
5951    } else if (!strcmp(*testargs, "-o"))
5952	tok = DBAR;
5953    else if (!strcmp(*testargs, "-a"))
5954	tok = DAMPER;
5955    else if (!strcmp(*testargs, "!"))
5956	tok = BANG;
5957    else if (!strcmp(*testargs, "("))
5958	tok = INPAR;
5959    else if (!strcmp(*testargs, ")"))
5960	tok = OUTPAR;
5961    else
5962	tok = STRING;
5963    testargs++;
5964}
5965
5966/**/
5967int
5968bin_test(char *name, char **argv, UNUSED(Options ops), int func)
5969{
5970    char **s;
5971    Eprog prog;
5972    struct estate state;
5973    int nargs;
5974
5975    /* if "test" was invoked as "[", it needs a matching "]" *
5976     * which is subsequently ignored                         */
5977    if (func == BIN_BRACKET) {
5978	for (s = argv; *s; s++);
5979	if (s == argv || strcmp(s[-1], "]")) {
5980	    zwarnnam(name, "']' expected");
5981	    return 1;
5982	}
5983	s[-1] = NULL;
5984    }
5985    /* an empty argument list evaluates to false (1) */
5986    if (!*argv)
5987	return 1;
5988
5989    /*
5990     * Implement some XSI extensions to POSIX here.
5991     * See
5992     * http://www.opengroup.org/onlinepubs/009695399/utilities/test.html.
5993     */
5994    nargs = arrlen(argv);
5995    if (nargs == 3 || nargs == 4)
5996    {
5997	if (*argv[0] == '(' && *argv[nargs-1] == ')') {
5998	    argv[nargs-1] = NULL;
5999	    argv++;
6000	}
6001    }
6002
6003    lexsave();
6004    testargs = argv;
6005    tok = NULLTOK;
6006    condlex = testlex;
6007    testlex();
6008    prog = parse_cond();
6009    condlex = zshlex;
6010
6011    if (errflag) {
6012	errflag = 0;
6013	lexrestore();
6014	return 1;
6015    }
6016
6017    if (!prog || tok == LEXERR) {
6018	zwarnnam(name, tokstr ? "parse error" : "argument expected");
6019	lexrestore();
6020	return 1;
6021    }
6022    lexrestore();
6023
6024    if (*curtestarg) {
6025	zwarnnam(name, "too many arguments");
6026	return 1;
6027    }
6028
6029    /* syntax is OK, so evaluate */
6030
6031    state.prog = prog;
6032    state.pc = prog->prog;
6033    state.strs = prog->strs;
6034
6035
6036    return evalcond(&state, name);
6037}
6038
6039/* display a time, provided in units of 1/60s, as minutes and seconds */
6040#define pttime(X) printf("%ldm%ld.%02lds",((long) (X))/3600,\
6041			 ((long) (X))/60%60,((long) (X))*100/60%100)
6042
6043/* times: display, in a two-line format, the times provided by times(3) */
6044
6045/**/
6046int
6047bin_times(UNUSED(char *name), UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func))
6048{
6049    struct tms buf;
6050
6051    /* get time accounting information */
6052    if (times(&buf) == -1)
6053	return 1;
6054    pttime(buf.tms_utime);	/* user time */
6055    putchar(' ');
6056    pttime(buf.tms_stime);	/* system time */
6057    putchar('\n');
6058    pttime(buf.tms_cutime);	/* user time, children */
6059    putchar(' ');
6060    pttime(buf.tms_cstime);	/* system time, children */
6061    putchar('\n');
6062    return 0;
6063}
6064
6065/* trap: set/unset signal traps */
6066
6067/**/
6068int
6069bin_trap(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
6070{
6071    Eprog prog;
6072    char *arg, *s;
6073    int sig;
6074
6075    if (*argv && !strcmp(*argv, "--"))
6076	argv++;
6077
6078    /* If given no arguments, list all currently-set traps */
6079    if (!*argv) {
6080	queue_signals();
6081	for (sig = 0; sig < VSIGCOUNT; sig++) {
6082	    if (sigtrapped[sig] & ZSIG_FUNC) {
6083		HashNode hn;
6084
6085		if ((hn = gettrapnode(sig, 0)))
6086		    shfunctab->printnode(hn, 0);
6087		DPUTS(!hn, "BUG: I did not find any trap functions!");
6088	    } else if (sigtrapped[sig]) {
6089		const char *name = getsigname(sig);
6090		if (!siglists[sig])
6091		    printf("trap -- '' %s\n", name);
6092		else {
6093		    s = getpermtext(siglists[sig], NULL, 0);
6094		    printf("trap -- ");
6095		    quotedzputs(s, stdout);
6096		    printf(" %s\n", name);
6097		    zsfree(s);
6098		}
6099	    }
6100	}
6101	unqueue_signals();
6102	return 0;
6103    }
6104
6105    /* If we have a signal number, unset the specified *
6106     * signals.  With only -, remove all traps.        */
6107    if ((getsignum(*argv) != -1) || (!strcmp(*argv, "-") && argv++)) {
6108	if (!*argv) {
6109	    for (sig = 0; sig < VSIGCOUNT; sig++)
6110		unsettrap(sig);
6111	} else {
6112	    for (; *argv; argv++) {
6113		sig = getsignum(*argv);
6114		if (sig == -1) {
6115		    zwarnnam(name, "undefined signal: %s", *argv);
6116		    break;
6117		}
6118		unsettrap(sig);
6119	    }
6120	}
6121	return *argv != NULL;
6122    }
6123
6124    /* Sort out the command to execute on trap */
6125    arg = *argv++;
6126    if (!*arg)
6127	prog = &dummy_eprog;
6128    else if (!(prog = parse_string(arg, 1))) {
6129	zwarnnam(name, "couldn't parse trap command");
6130	return 1;
6131    }
6132
6133    /* set traps */
6134    for (; *argv; argv++) {
6135	Eprog t;
6136	int flags;
6137
6138	sig = getsignum(*argv);
6139	if (sig == -1) {
6140	    zwarnnam(name, "undefined signal: %s", *argv);
6141	    break;
6142	}
6143	if (!strcmp(sigs[sig], *argv))
6144	    flags = 0;
6145	else {
6146	    /*
6147	     * Record that the signal is used under an assumed name.
6148	     * If we ever have more than one alias per signal this
6149	     * will need improving.
6150	     */
6151	    flags = ZSIG_ALIAS;
6152	}
6153	t = dupeprog(prog, 0);
6154	if (settrap(sig, t, flags))
6155	    freeeprog(t);
6156    }
6157    return *argv != NULL;
6158}
6159
6160/**/
6161int
6162bin_ttyctl(UNUSED(char *name), UNUSED(char **argv), Options ops, UNUSED(int func))
6163{
6164    if (OPT_ISSET(ops,'f'))
6165	ttyfrozen = 1;
6166    else if (OPT_ISSET(ops,'u'))
6167	ttyfrozen = 0;
6168    else
6169	printf("tty is %sfrozen\n", ttyfrozen ? "" : "not ");
6170    return 0;
6171}
6172
6173/* let -- mathematical evaluation */
6174
6175/**/
6176int
6177bin_let(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int func))
6178{
6179    mnumber val = zero_mnumber;
6180
6181    while (*argv)
6182	val = matheval(*argv++);
6183    /* Errors in math evaluation in let are non-fatal. */
6184    errflag = 0;
6185    /* should test for fabs(val.u.d) < epsilon? */
6186    return (val.type == MN_INTEGER) ? val.u.l == 0 : val.u.d == 0.0;
6187}
6188
6189/* umask command.  umask may be specified as octal digits, or in the  *
6190 * symbolic form that chmod(1) uses.  Well, a subset of it.  Remember *
6191 * that only the bottom nine bits of umask are used, so there's no    *
6192 * point allowing the set{u,g}id and sticky bits to be specified.     */
6193
6194/**/
6195int
6196bin_umask(char *nam, char **args, Options ops, UNUSED(int func))
6197{
6198    mode_t um;
6199    char *s = *args;
6200
6201    /* Get the current umask. */
6202    um = umask(0);
6203    umask(um);
6204    /* No arguments means to display the current setting. */
6205    if (!s) {
6206	if (OPT_ISSET(ops,'S')) {
6207	    char *who = "ugo";
6208
6209	    while (*who) {
6210		char *what = "rwx";
6211		printf("%c=", *who++);
6212		while (*what) {
6213		    if (!(um & 0400))
6214			putchar(*what);
6215		    um <<= 1;
6216		    what++;
6217		}
6218		putchar(*who ? ',' : '\n');
6219	    }
6220	} else {
6221	    if (um & 0700)
6222		putchar('0');
6223	    printf("%03o\n", (unsigned)um);
6224	}
6225	return 0;
6226    }
6227
6228    if (idigit(*s)) {
6229	/* Simple digital umask. */
6230	um = zstrtol(s, &s, 8);
6231	if (*s) {
6232	    zwarnnam(nam, "bad umask");
6233	    return 1;
6234	}
6235    } else {
6236	/* Symbolic notation -- slightly complicated. */
6237	int whomask, umaskop, mask;
6238
6239	/* More than one symbolic argument may be used at once, each separated
6240	   by commas. */
6241	for (;;) {
6242	    /* First part of the argument -- who does this apply to?
6243	       u=owner, g=group, o=other. */
6244	    whomask = 0;
6245	    while (*s == 'u' || *s == 'g' || *s == 'o' || *s == 'a')
6246		if (*s == 'u')
6247		    s++, whomask |= 0700;
6248		else if (*s == 'g')
6249		    s++, whomask |= 0070;
6250		else if (*s == 'o')
6251		    s++, whomask |= 0007;
6252		else if (*s == 'a')
6253		    s++, whomask |= 0777;
6254	    /* Default whomask is everyone. */
6255	    if (!whomask)
6256		whomask = 0777;
6257	    /* Operation may be +, - or =. */
6258	    umaskop = (int)*s;
6259	    if (!(umaskop == '+' || umaskop == '-' || umaskop == '=')) {
6260		if (umaskop)
6261		    zwarnnam(nam, "bad symbolic mode operator: %c", umaskop);
6262		else
6263		    zwarnnam(nam, "bad umask");
6264		return 1;
6265	    }
6266	    /* Permissions mask -- r=read, w=write, x=execute. */
6267	    mask = 0;
6268	    while (*++s && *s != ',')
6269		if (*s == 'r')
6270		    mask |= 0444 & whomask;
6271		else if (*s == 'w')
6272		    mask |= 0222 & whomask;
6273		else if (*s == 'x')
6274		    mask |= 0111 & whomask;
6275		else {
6276		    zwarnnam(nam, "bad symbolic mode permission: %c", *s);
6277		    return 1;
6278		}
6279	    /* Apply parsed argument to um. */
6280	    if (umaskop == '+')
6281		um &= ~mask;
6282	    else if (umaskop == '-')
6283		um |= mask;
6284	    else		/* umaskop == '=' */
6285		um = (um | (whomask)) & ~mask;
6286	    if (*s == ',')
6287		s++;
6288	    else
6289		break;
6290	}
6291	if (*s) {
6292	    zwarnnam(nam, "bad character in symbolic mode: %c", *s);
6293	    return 1;
6294	}
6295    }
6296
6297    /* Finally, set the new umask. */
6298    umask(um);
6299    return 0;
6300}
6301
6302/* Generic builtin for facilities not available on this OS */
6303
6304/**/
6305mod_export int
6306bin_notavail(char *nam, UNUSED(char **argv), UNUSED(Options ops), UNUSED(int func))
6307{
6308    zwarnnam(nam, "not available on this system");
6309    return 1;
6310}
6311