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