1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * A copy of the CDDL is also available via the Internet at
11  * http://www.opensource.org/licenses/cddl1.txt
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  */
23 
24 /*
25  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  */
28 
29 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 #if defined(sun)
33 #pragma ident	"@(#)bltin.c	1.16	06/06/16 SMI"
34 #endif
35 
36 #include "defs.h"
37 
38 /*
39  * Copyright 2008-2021 J. Schilling
40  *
41  * @(#)bltin.c	1.150 21/07/12 2008-2021 J. Schilling
42  */
43 #ifndef lint
44 static	UConst char sccsid[] =
45 	"@(#)bltin.c	1.150 21/07/12 2008-2021 J. Schilling";
46 #endif
47 
48 /*
49  *
50  * UNIX shell
51  *
52  */
53 
54 #if	defined(INTERACTIVE) || defined(DO_SYSFC)
55 #include	<schily/shedit.h>
56 #endif
57 
58 #include	<errno.h>
59 #include	"sym.h"
60 #include	"hash.h"
61 #ifdef	DO_SYSALIAS
62 #include	"abbrev.h"
63 #endif
64 #include	<sys/types.h>
65 #include	<sys/stat.h>
66 #include	<sys/times.h>
67 
68 #include	<schily/resource.h>
69 #include	<schily/wait.h>		/* Needed for CLD_EXITED */
70 
71 	void	builtin	__PR((int type, int argc, unsigned char **argv,
72 					struct trenod *t, int xflags));
73 #ifdef	DO_POSIX_CD
74 static	int	opt_LP	__PR((int argc, unsigned char **argv,
75 					int *opts, const char *use));
76 #endif
77 static	int	whatis	__PR((unsigned char *arg, int verbose));
78 #ifdef	DO_SYSCOMMAND
79 static	void	syscommand __PR((int argc, unsigned char **argv,
80 					struct trenod *t, int xflags));
81 #endif
82 #if	defined(INTERACTIVE) || defined(DO_SYSFC)
83 static	void	syshist	__PR((int argc, unsigned char **argv,
84 					struct trenod *t, int xflags));
85 #endif
86 #ifdef	DO_TILDE
87 static unsigned char *etilde	__PR((unsigned char *arg, unsigned char *s));
88 #endif
89 
90 #define	no_pipe	(int *)0
91 
92 void
builtin(type,argc,argv,t,xflags)93 builtin(type, argc, argv, t, xflags)
94 	int		type;
95 	int		argc;
96 	unsigned char	**argv;
97 	struct trenod	*t;
98 	int		xflags;
99 {
100 	short		fdindex;
101 	unsigned char	*a0 = NULL;
102 	unsigned char	*a1 = argv[1];
103 	struct argnod	*np = NULL;
104 	int		cdopt = 0;
105 #if defined(DO_POSIX_CD) || defined(DO_SYSPUSHD) || defined(DO_SYSDOSH) || \
106 	defined(DO_GETOPT_UTILS) || defined(DO_SYSERRSTR)
107 	int		ind = 1;
108 #endif
109 #ifdef	DO_POSIX_FAILURE
110 	unsigned long	oflags = flags;
111 #endif
112 
113 	exitval = 0;
114 	exval_clear();
115 #ifdef	DO_POSIX_FAILURE
116 	if ((type & SPC_BUILTIN) == 0)
117 		flags |= noexit;
118 #endif
119 	type = hashdata(type);
120 	fdindex = initio(t->treio, (type != SYSEXEC));
121 #ifdef	DO_POSIX_FAILURE
122 	flags = oflags;
123 	if (exitval)
124 		goto out;
125 #endif
126 
127 	switch (type) {
128 
129 	case SYSSUSP:
130 		syssusp(argc, (char **)argv);
131 		break;
132 
133 	case SYSSTOP:
134 		sysstop(argc, (char **)argv);
135 		break;
136 
137 	case SYSKILL:
138 		syskill(argc, (char **)argv);
139 		break;
140 
141 	case SYSFGBG:
142 		sysfgbg(argc, (char **)argv);
143 		break;
144 
145 	case SYSJOBS:
146 		sysjobs(argc, argv);
147 		break;
148 
149 	case SYSDOT:			/* POSIX special builtin */
150 		if (a1) {
151 			int	f;
152 
153 			if ((f = pathopen(getpath(a1), a1)) < 0)
154 				failed(a1, notfound);
155 			else {
156 #ifdef	DO_POSIX_RETURN
157 				jmps_t	dotjmp;
158 				jmps_t	*odotjmp = dotshell;
159 				struct fileblk	*ostandin = standin;
160 
161 				if (setjmp(dotjmp.jb)) {
162 					/*
163 					 * pushed in execexp()
164 					 */
165 					while (ostandin != standin) {
166 						if (!pop())
167 							break;
168 					}
169 					dotshell = odotjmp;
170 					dotcnt--;
171 					break;
172 				}
173 				dotshell = &dotjmp;
174 				dotcnt++;
175 #endif
176 
177 				execexp(0, (Intptr_t)f, xflags);
178 
179 #ifdef	DO_POSIX_RETURN
180 				dotcnt--;
181 				dotshell = odotjmp;
182 #endif
183 			}
184 		}
185 		break;
186 
187 	case SYSTIMES:			/* POSIX special builtin */
188 		{
189 			struct rusage	ru;
190 
191 			getrusage(RUSAGE_SELF, &ru);
192 			prtv(&ru.ru_utime, 3, 'l');
193 			prc_buff(SPACE);
194 			prtv(&ru.ru_stime, 3, 'l');
195 			prc_buff(NL);
196 			getrusage(RUSAGE_CHILDREN, &ru);
197 			prtv(&ru.ru_utime, 3, 'l');
198 			prc_buff(SPACE);
199 			prtv(&ru.ru_stime, 3, 'l');
200 			prc_buff(NL);
201 		}
202 		break;
203 
204 	case SYSEXIT:			/* POSIX special builtin */
205 		if (tried_to_exit++ || endjobs(JOB_STOPPED)) {
206 #ifdef	INTERACTIVE
207 			if (!(xflags & XEC_EXECED))
208 				shedit_treset();	/* Writes ~/.history */
209 #endif
210 			flags |= forcexit;	/* force exit */
211 #ifdef	DO_SIGNED_EXIT
212 			exitsh(a1 ? stosi(a1) : retval);
213 #else
214 			exitsh(a1 ? stoi(a1) : retval);
215 #endif
216 		}
217 		break;
218 
219 	case SYSNULL:			/* POSIX special builtin */
220 		break;
221 
222 	case SYSCONT:			/* POSIX special builtin */
223 		if (loopcnt) {
224 			execbrk = breakcnt = 1;
225 			if (a1) {
226 				breakcnt = stoi(a1);
227 #ifdef	DO_CONT_BRK_POSIX
228 				if (breakcnt == 0) {
229 					exitval = ERROR;
230 					break;
231 				}
232 #endif
233 			}
234 			if (breakcnt > loopcnt)
235 				breakcnt = loopcnt;
236 #ifndef	DO_CONT_BRK_FIX
237 			else
238 #endif
239 			breakcnt = -breakcnt;
240 		}
241 		break;
242 
243 	case SYSBREAK:			/* POSIX special builtin */
244 		if (loopcnt) {
245 			execbrk = breakcnt = 1;
246 			if (a1) {
247 				breakcnt = stoi(a1);
248 #ifdef	DO_CONT_BRK_POSIX
249 				if (breakcnt == 0) {
250 					exitval = ERROR;
251 					break;
252 				}
253 #endif
254 			}
255 			if (breakcnt > loopcnt)
256 				breakcnt = loopcnt;
257 		}
258 		break;
259 
260 	case SYSTRAP:			/* POSIX special builtin */
261 		systrap(argc, (char **)argv);
262 		break;
263 
264 	case SYSEXEC:			/* POSIX special builtin */
265 		argv++;
266 		ioset = 0;
267 		if (a1 == 0) {
268 			setmode(0);
269 			break;
270 		}
271 #ifdef	DO_EXEC_AC
272 		if (eq(a1, "-a")) {
273 			argv++;
274 			if (*argv) {
275 				a0 = *argv++;
276 			}
277 		}
278 #endif
279 		/* FALLTHROUGH */
280 
281 #ifdef RES	/* Research includes login as part of the shell */
282 
283 	case SYSLOGIN:
284 		if (!endjobs(JOB_STOPPED|JOB_RUNNING))
285 			break;
286 		oldsigs(TRUE);
287 		execa(argv, -1, FALSE, a0);
288 		done(0);
289 #else
290 
291 	case SYSNEWGRP:
292 		if (flags & rshflg)
293 			failed(argv[0], restricted);
294 		else if (!endjobs(JOB_STOPPED|JOB_RUNNING))
295 			break;
296 		else {
297 			flags |= forcexit; /* bad exec will terminate shell */
298 			oldsigs(TRUE);
299 			rmtemp(0);
300 			rmfunctmp(0);
301 #ifdef ACCT
302 			doacct();
303 #endif
304 			execa(argv, -1, FALSE, a0);
305 			done(0);
306 			/* NOTREACHED */
307 		}
308 
309 #endif
310 
311 #ifdef	DO_SYSPUSHD
312 	case SYSPOPD:
313 		/* FALLTHROUGH */
314 	case SYSPUSHD:
315 #ifdef	DO_POSIX_CD
316 		cdopt = -1;
317 		ind = opt_LP(argc, argv, &cdopt,
318 			type == SYSPOPD ?
319 				"popd [ -L | -P ] [-offset]":
320 				"pushd [ -L | -P ] [-offset | directory]");
321 		if (ind < 0)
322 			break;
323 		a1 = argv[ind];
324 #endif
325 		init_dirs();
326 		if (a1 && a1[0] == '-') {
327 			if (type == SYSPUSHD && a1[1] == '\0') {
328 				extern struct namnod opwdnod;
329 
330 				a1 = opwdnod.namval;
331 				if (a1 == NULL || *a1 == '\0') {
332 					free(np);
333 					failure(a1, baddir);
334 					break;
335 				}
336 			} else {
337 				int	off = stoi(&a1[1]);
338 
339 				if (!(np = pop_dir(off))) {
340 					failure(a1, badoff);
341 					break;
342 				}
343 				a1 = np->argval;
344 			}
345 		} else if (type == SYSPOPD) {
346 			struct argnod *dnp = pop_dir(0);
347 
348 			/*
349 			 * init_dirs() grants pop_dir(0) != NULL
350 			 */
351 			if (dnp->argnxt == NULL) {
352 				/*
353 				 * "dnp" is the static "dirs",
354 				 * no need to free()
355 				 */
356 				gfailure(UC emptystack, NULL);
357 				break;
358 			}
359 			a1 = dnp->argnxt->argval;
360 			free(dnp);
361 		}
362 #endif	/* DO_SYSPUSHD */
363 
364 		/* FALLTHROUGH */
365 	case SYSCD:
366 #ifdef	DO_POSIX_CD
367 		if (type == SYSCD) {
368 			ind = opt_LP(argc, argv, &cdopt,
369 						"cd [ -L | -P ] directory");
370 			if (ind < 0)
371 				break;
372 			a1 = argv[ind];
373 		}
374 
375 		/*
376 		 * Enable cd - & cd -- ... only with DO_POSIX_CD
377 		 */
378 		if (type == SYSCD && a1 && a1[0] == '-') {
379 			if (a1[1] == '\0') {
380 				extern struct namnod opwdnod;
381 
382 				a1 = opwdnod.namval;
383 				if (a1 == NULL || *a1 == '\0') {
384 					free(np);
385 					failure(a1, baddir);
386 					break;
387 				}
388 			}
389 		}
390 #endif
391 		/*
392 		 * A restricted Shell does not allow "cd" at all.
393 		 */
394 		if (flags & rshflg) {
395 			free(np);
396 			failed(argv[0], restricted);
397 		} else if ((a1 && *a1) || (a1 == 0 && (a1 = homenod.namval))) {
398 			unsigned char *cdpath;
399 			unsigned char *dir;
400 			int f;
401 
402 			/*
403 			 * Make sure that cwdname[] is set to be able to track
404 			 * the previous working directory in OLDPWD.
405 			 */
406 			cwdset();
407 
408 			if ((cdpath = cdpnod.namval) == 0 ||
409 			    *a1 == '/' ||
410 			    cf(a1, UC ".") == 0 ||
411 			    cf(a1, UC "..") == 0 ||
412 			    (*a1 == '.' && (*(a1+1) == '/' ||
413 			    (*(a1+1) == '.' && *(a1+2) == '/'))))
414 				cdpath = UC nullstr;
415 
416 			do {
417 				dir = cdpath;
418 				cdpath = catpath(cdpath, a1);
419 #ifdef	DO_POSIX_CD
420 				if ((cdopt & CHDIR_L) && *curstak() != '/') {
421 					/*
422 					 * Concatenate $PWD and curstak() and
423 					 * normalize the resulting path.
424 					 */
425 					if (!cwdrel2abs()) {
426 						Failure(a1, baddir);
427 						goto out;
428 					}
429 				}
430 #endif
431 			} while ((f = lchdir((char *) curstak())) < 0 &&
432 			    cdpath);
433 
434 			free(np);
435 			if (f < 0) {
436 				switch (errno) {
437 #ifdef	EMULTIHOP
438 				case EMULTIHOP:
439 					Failure(a1, emultihop);
440 					break;
441 #endif
442 				case ENOTDIR:
443 					Failure(a1, enotdir);
444 					break;
445 				case ENOENT:
446 					Failure(a1, enoent);
447 					break;
448 				case EACCES:
449 					Failure(a1, eacces);
450 					break;
451 #ifdef	ENOLINK
452 				case ENOLINK:
453 					Failure(a1, enolink);
454 					break;
455 #endif
456 				default:
457 					Failure(a1, baddir);
458 					break;
459 				}
460 				break;	/* No zapcd(), chdir() did not work */
461 			} else {
462 				unsigned char	*wd;
463 
464 				ocwdnod();		/* Update OLDPWD=    */
465 				cwd(curstak(), NULL);	/* Canonic from stak */
466 				wd = cwdget(cdopt);	/* Get reliable cwd  */
467 #ifdef	DO_SYSPUSHD
468 				if (type != SYSPUSHD)
469 					free(pop_dir(0));
470 				push_dir(wd);		/* Update dir stack  */
471 				if (pr_dirs(type ==	/* Print if len > 0  */
472 				    SYSPOPD ?		/* or cmd was "popd" */
473 				    0:1, cdopt))	/* If already printed */
474 					wd = NULL;	/* don't do it again */
475 #endif
476 #ifdef	DO_POSIX_CD
477 				/*
478 				 * "cd -" should print the new directory.
479 				 */
480 				if ((cf(UC nullstr, dir) || a1 != argv[ind]) &&
481 #else
482 				if (cf(UC nullstr, dir) &&
483 #endif
484 				    *dir != ':' &&
485 				    any('/', curstak()) &&
486 				    flags & prompt) {
487 					if (wd) {	/* Not yet printed */
488 						prs_buff(wd);
489 						prc_buff(NL);
490 					}
491 				}
492 #ifdef	DO_SYSALIAS
493 				if (flags2 & localaliasflg) {
494 					ab_use(LOCAL_AB, (char *)localname);
495 				}
496 #endif
497 			}
498 			zapcd();
499 		} else {
500 			free(np);
501 			/*
502 			 * cd "" is not permitted,
503 			 * cd	 without parameter is cd $HOME
504 			 * but $HOME was not set.
505 			 */
506 			if (a1)
507 				Error(nulldir);
508 			else
509 				Error(nohome);
510 		}
511 		break;
512 
513 	case SYSSHFT:			/* POSIX special builtin */
514 		{
515 			int places;
516 
517 			places = a1 ? stoi(a1) : 1;
518 
519 			if ((dolc -= places) < 0) {
520 				dolc = 0;
521 				error(badshift);
522 			} else {
523 				dolv += places;
524 			}
525 		}
526 		break;
527 
528 	case SYSWAIT:
529 		syswait(argc, (char **)argv);
530 		break;
531 
532 	case SYSREAD:
533 #ifndef	DO_READ_R
534 		if (argc < 2) {
535 			Failure(argv[0], mssgargn);
536 			break;
537 		}
538 #endif
539 		rwait = 1;
540 		exitval = readvar(argc, argv);
541 		rwait = 0;
542 		break;
543 
544 	case SYSSET:			/* POSIX special builtin */
545 		if (a1) {
546 			int	cnt;
547 
548 			cnt = options(argc, argv);
549 #ifdef	DO_POSIX_SET
550 			if (cnt > 1 || dashdash)
551 #else
552 			if (cnt > 1)
553 #endif
554 				setargs(argv + argc - cnt);
555 		} else if (comptr(t)->comset == 0) {
556 			/*
557 			 * scan name chain and print
558 			 */
559 			namscan(printnam);
560 		}
561 		break;
562 
563 #ifdef	DO_SYSLOCAL
564 	case SYSLOCAL:
565 		{
566 			ind = optskip(argc, argv, "local [name[=value] ...]");
567 			if (ind-- < 0)
568 				break;
569 			argv += ind;
570 
571 			if (localp == NULL)
572 				error("local can only be used in a function");
573 
574 			if (argv[1]) {
575 				while (*++argv) {
576 					unsigned char *p;
577 
578 					p = UC strchr((char *)*argv, '=');
579 					if (p)
580 						*p = '\0';
581 					pushval(lookup(*argv), localp);
582 					if (p) {
583 						*p = '=';
584 #ifdef	DO_TILDE
585 						if (strchr(C p, '~') != NULL)
586 							p = etilde(*argv, p);
587 						else
588 #endif
589 							p = *argv;
590 						setname(p, 0);
591 					}
592 					localcnt++;
593 				}
594 			} else {
595 				namscan(printlocal);
596 			}
597 		}
598 		break;
599 #endif	/* DO_SYSLOCAL */
600 
601 	case SYSRDONLY:			/* POSIX special builtin */
602 		{
603 #ifdef	DO_POSIX_EXPORT
604 			struct optv	optv;
605 			int		c;
606 			int		isp = 0;
607 
608 			optinit(&optv);
609 			optv.optflag |= OPT_SPC;
610 
611 			while ((c = optnext(argc, argv, &optv, "p",
612 				    "readonly [-p] [name[=value] ...]")) !=
613 									-1) {
614 				if (c == 0)	/* Was -help */
615 					goto out;
616 				else if (c == 'p')
617 					isp++;
618 			}
619 			argv += --optv.optind;
620 #endif
621 			if (argv[1]) {
622 				while (*++argv) {
623 #ifdef	DO_POSIX_EXPORT
624 					unsigned char	*p;
625 
626 					p = UC strchr((char *)*argv, '=');
627 					if (p) {
628 #ifdef	DO_TILDE
629 						if (strchr(C p, '~') != NULL)
630 							p = etilde(*argv, p);
631 						else
632 #endif
633 							p = *argv;
634 						setname(p, N_RDONLY);
635 						continue;
636 					}
637 #endif
638 					attrib(lookup(*argv), N_RDONLY);
639 				}
640 			} else {
641 #ifdef	DO_POSIX_EXPORT
642 				namscan(isp?printpro:printro);
643 #else
644 				namscan(printro);
645 #endif
646 			}
647 		}
648 		break;
649 
650 	case SYSXPORT:			/* POSIX special builtin */
651 		{
652 			struct namnod	*n;
653 #ifdef	DO_POSIX_EXPORT
654 			struct optv	optv;
655 			int		c;
656 			int		isp = 0;
657 
658 			optinit(&optv);
659 			optv.optflag |= OPT_SPC;
660 
661 			while ((c = optnext(argc, argv, &optv, "p",
662 				    "export [-p] [name[=value] ...]")) != -1) {
663 				if (c == 0)	/* Was -help */
664 					goto out;
665 				else if (c == 'p')
666 					isp++;
667 			}
668 			argv += --optv.optind;
669 #endif
670 			if (argv[1]) {
671 				while (*++argv) {
672 #ifdef	DO_POSIX_EXPORT
673 					unsigned char	*p;
674 
675 					p = UC strchr((char *)*argv, '=');
676 					if (p) {
677 #ifdef	DO_TILDE
678 						if (strchr(C p, '~') != NULL)
679 							p = etilde(*argv, p);
680 						else
681 #endif
682 							p = *argv;
683 						setname(p, N_EXPORT);
684 						continue;
685 					}
686 #endif
687 					n = lookup(*argv);
688 #ifndef	DO_POSIX_UNSET
689 					if (n->namflg & N_FUNCTN)
690 						error(badexport);
691 					else
692 #endif
693 						attrib(n, N_EXPORT);
694 				}
695 			} else {
696 #ifdef	DO_POSIX_EXPORT
697 				namscan(isp?printpexp:printexp);
698 #else
699 				namscan(printexp);
700 #endif
701 			}
702 		}
703 		break;
704 
705 	case SYSEVAL:			/* POSIX special builtin */
706 		if (a1) {
707 			flags &= ~noexit;
708 			execexp(a1, (Intptr_t)&argv[2], xflags);
709 		}
710 		break;
711 
712 #ifdef	DO_SYSDOSH
713 	case SYSDOSH:
714 		if (a1 == NULL) {
715 			break;
716 		} else {
717 			struct dolnod	*olddolh;
718 			unsigned char	**olddolv = dolv;
719 			int		olddolc = dolc;
720 			struct ionod	*io = t->treio;
721 			short		idx;
722 
723 			ind = optskip(argc, argv,
724 					    "dosh command [commandname args]");
725 			if (ind < 0)
726 				break;
727 			if (ind >= argc)
728 				break;
729 
730 			/*
731 			 * save current positional parameters
732 			 */
733 			olddolh = (struct dolnod *)savargs(funcnt);
734 			funcnt++;
735 			setargs(&argv[ind+1]);
736 			idx = initio(io, 1);
737 			execexp(argv[ind], (Intptr_t)0, xflags);
738 			restore(idx);
739 			(void) restorargs(olddolh, funcnt);
740 			dolv = olddolv;
741 			dolc = olddolc;
742 			funcnt--;
743 		}
744 		break;
745 #endif	/* DO_SYSDOSH */
746 
747 #ifdef	DO_SYSREPEAT
748 	case SYSREPEAT:
749 		if (a1) {
750 			struct optv optv;
751 			int	c;
752 			int	delay = 0;
753 			int	count = -1;
754 			int	err = 0;
755 
756 			optinit(&optv);
757 
758 			while ((c = optget(argc, argv, &optv,
759 						"c:(count)d:(delay)")) != -1) {
760 				switch (c) {
761 				case 'c':
762 					count = stoi(UC optv.optarg);
763 					break;
764 				case 'd':
765 					delay = stoi(UC optv.optarg);
766 					break;
767 				case '?':
768 					gfailure(UC usage, repuse);
769 					err = 1;
770 					goto reperr;
771 				}
772 			}
773 		reperr:
774 			if (err)
775 				break;
776 
777 			while (count != 0) {
778 				unsigned char		*sav = savstak();
779 				struct ionod		*iosav = iotemp;
780 
781 				execexp(argv[optv.optind],
782 					(Intptr_t)&argv[optv.optind+1], xflags);
783 				tdystak(sav, iosav);
784 
785 				if (delay > 0)
786 					sh_sleep(delay);
787 				if (count > 0)
788 					count--;
789 				/*
790 				 * Exit if this shell received a signal
791 				 */
792 				sigchk();
793 				/*
794 				 * Exit if child received a signal
795 				 */
796 				if ((ex.ex_code != 0 &&
797 				    ex.ex_code != CLD_EXITED) ||
798 				    ex.ex_status != 0) {
799 					exitsh(exitval ? exitval : SIGFAIL);
800 				}
801 			}
802 		} else {
803 			gfailure(UC usage, repuse);
804 		}
805 		break;
806 #endif	/* DO_SYSREPEAT */
807 
808 #ifndef RES
809 	case SYSULIMIT:
810 		sysulimit(argc, argv);
811 		break;
812 
813 	case SYSUMASK:
814 		sysumask(argc, (char **)argv);
815 		break;
816 #endif
817 
818 	case SYSTST:
819 		exitval = test(argc, argv);
820 		break;
821 
822 #ifdef	DO_SYSATEXPR
823 	case SYSEXPR:
824 		expr(argc, argv);
825 		break;
826 #endif
827 
828 	case SYSECHO:
829 		exitval = echo(argc, argv);
830 		break;
831 
832 	case SYSHASH:
833 		{
834 #ifdef	DO_GETOPT_UTILS		/* For all builtins that support -- */
835 			struct optv	optv;
836 			int		c;
837 
838 			optinit(&optv);
839 
840 			while ((c = optnext(argc, argv, &optv, "r",
841 				    "hash [-r] [name ...]")) != -1) {
842 				if (c == 0)	/* Was -help */
843 					goto out;
844 				else if (c == 'r') {
845 					zaphash();
846 					goto out;
847 				}
848 			}
849 			argv += --optv.optind;
850 #else
851 			if (a1 && a1[0] == '-') {
852 				if (a1[1] == 'r')
853 					zaphash();
854 				else
855 					Error(badopt);
856 				break;
857 			}
858 #endif
859 			if (argv[1]) {
860 				while (*++argv) {
861 					if (hashtype(hash_cmd(*argv)) ==
862 						NOTFOUND) {
863 						Failure(*argv, notfound);
864 					}
865 				}
866 			} else {
867 				hashpr();
868 			}
869 		}
870 		break;
871 
872 #ifdef	DO_SYSPUSHD
873 	case SYSDIRS:
874 		ind = opt_LP(argc, argv, &cdopt, "dirs [ -L | -P ]");
875 		if (ind < 0)
876 			break;
877 		pr_dirs(0, cdopt);
878 		break;
879 #endif
880 
881 	case SYSPWD:
882 		{
883 #ifdef	DO_POSIX_CD
884 			ind = opt_LP(argc, argv, &cdopt, "pwd [ -L | -P ]");
885 			if (ind < 0)
886 				break;
887 #endif
888 			cwdprint(cdopt);
889 		}
890 		break;
891 
892 	case SYSRETURN:			/* POSIX special builtin */
893 		if (funcnt == 0 && dotcnt == 0)
894 			error(badreturn);
895 
896 		if (dotcnt > 0)
897 			dotbrk = 1;
898 		else
899 			execbrk = 1;
900 		exitval = (a1 ? stoi(a1) : retval);
901 		break;
902 
903 	case SYSTYPE:
904 		if (a1) {
905 #ifdef	DO_GETOPT_UTILS		/* For all builtins that support -- */
906 			struct optv	optv;
907 			int		c;
908 
909 			optinit(&optv);
910 			while ((c = optnext(argc, argv, &optv, "F",
911 					"type [-F] [name ...]")) != -1) {
912 				if (c == 0)	/* Was -help */
913 					goto out;
914 				else if (c == 'F') {
915 					if (argv[optv.optind] == NULL)
916 						namscan(printfunc);
917 					else
918 						failure(argv[0], toomanyargs);
919 					goto out;
920 				}
921 			}
922 			argv += --optv.optind;
923 #endif
924 			/* return success only if all names are found */
925 			while (*++argv) {
926 				exitval |= whatis(*argv, 2);
927 			}
928 		}
929 		break;
930 
931 	case SYSUNS:			/* POSIX special builtin */
932 		if (a1) {
933 			int	uflg = 0;
934 #ifdef	DO_POSIX_UNSET
935 			struct optv	optv;
936 			int		c;
937 
938 			optinit(&optv);
939 			optv.optflag |= OPT_SPC;
940 
941 			while ((c = optnext(argc, argv, &optv, "fv",
942 					"unset [-f | -v] [name ...]")) != -1) {
943 				if (c == 0)	/* Was -help */
944 					goto out;
945 				else if (c == 'f')
946 					uflg = UNSET_FUNC;
947 				else if (c == 'v')
948 					uflg = UNSET_VAR;
949 			}
950 			argv += --optv.optind;
951 #endif
952 			while (*++argv)
953 				unset_name(*argv, uflg);
954 		}
955 		break;
956 
957 	case SYSGETOPT: {
958 		int getoptval;
959 		struct namnod *n;
960 		extern unsigned char numbuf[];
961 		unsigned char *varnam;
962 		unsigned char c[3];	/* '+', 'c' and '\0' */
963 		unsigned char *cptr;	/* points to + or to c */
964 		unsigned char *cmdp = *argv;
965 		unsigned char *optstring;
966 
967 #ifdef	DO_GETOPT_UTILS		/* For all builtins that support -- */
968 		ind = optskip(argc, argv,
969 					"getopts optstring name [arg ...]");
970 		if (ind-- < 0)
971 			break;
972 		argc -= ind;
973 		argv += ind;
974 #endif
975 		if (argc < 3) {
976 			failure(cmdp, mssgargn);
977 			break;
978 		}
979 		exitval = 0;
980 		n = lookup(UC "OPTIND");
981 		optind = stoi(n->namval);
982 		if (optind <= 0)		/* Paranoia */
983 			optind = 1;
984 		varnam = argv[2];
985 		optstring = argv[1];
986 #ifndef	DO_GETOPT_PLUS
987 		if (*optstring == '+')
988 			optstring++;
989 #endif
990 		if (argc > 3) {
991 			argv[2] = dolv[0];
992 			getoptval = getopt(argc-2,
993 					(char **)&argv[2], (char *)optstring);
994 			argv[2] = varnam;
995 		} else {
996 			getoptval = getopt(dolc+1,
997 					(char **)dolv, (char *)optstring);
998 		}
999 		if (getoptval == -1) {
1000 			itos(optind);
1001 			assign(n, numbuf);
1002 			n = lookup(varnam);
1003 #ifdef	DO_GETOPT_POSIX
1004 			assign(n, UC "?");
1005 #else
1006 			assign(n, UC nullstr);
1007 #endif
1008 			exitval = 1;
1009 			break;
1010 		}
1011 		itos(optind);
1012 		assign(n, numbuf);
1013 		c[0] = '+';
1014 		c[1] = (char)getoptval;
1015 		c[2] = '\0';
1016 		cptr = &c[1];
1017 #ifdef	DO_GETOPT_PLUS
1018 		if (optflg & GETOPT_PLUS_FL)
1019 			cptr = c;
1020 #endif
1021 		n = lookup(varnam);
1022 		if (getoptval > 256) {
1023 			/*
1024 			 * We should come here only with the Schily enhanced
1025 			 * getopt() from libgetopt.
1026 			 */
1027 #ifdef	DO_GETOPT_LONGONLY
1028 			itos(getoptval);
1029 #ifdef	DO_GETOPT_PLUS
1030 			if (optflg & GETOPT_PLUS_FL) {
1031 				unsigned char pnumbuf[64];
1032 				strcpy(C pnumbuf, "+");
1033 				strcat(C pnumbuf, C numbuf);
1034 				assign(n, pnumbuf);
1035 			} else
1036 #endif	/* DO_GETOPT_PLUS */
1037 				assign(n, numbuf);
1038 #else
1039 			assign(n, UC "?");	/* Pretend illegal option */
1040 #endif
1041 		} else
1042 			assign(n, cptr);
1043 		n = lookup(UC "OPTARG");
1044 		assign(n, UC optarg);
1045 #ifdef	DO_GETOPT_POSIX
1046 		/*
1047 		 * In case that the option string starts with ':',
1048 		 * POSIX requires to set OPTARG to the option
1049 		 * character that causes getopt() to return '?'/':'.
1050 		 */
1051 		if (optarg == NULL && argv[1][0] == ':' &&
1052 		    (getoptval == '?' || getoptval == ':')) {
1053 			c[1] = optopt;
1054 			assign(n, &c[1]);
1055 		}
1056 #endif
1057 		}
1058 		break;
1059 
1060 #ifdef	INTERACTIVE
1061 	case SYSHISTORY:
1062 		syshist(argc, argv, t, xflags);
1063 		if (intrptr) {		/* Was set by syshist()?	*/
1064 			*intrptr = 0;	/* Clear interrupt counter	*/
1065 			intrptr = 0;	/* Disable intrptr for now	*/
1066 		}
1067 		break;
1068 
1069 	case SYSSAVEHIST:
1070 		shedit_bshist(&intrptr);
1071 
1072 		if (intrptr) {		/* Was set by shedit_bshist()?	*/
1073 			*intrptr = 0;	/* Clear interrupt counter	*/
1074 			intrptr = 0;	/* Disable intrptr for now	*/
1075 		}
1076 		break;
1077 
1078 	case SYSMAP:
1079 		{
1080 			int		f = STDOUT_FILENO;
1081 			struct optv	optv;
1082 			int		c;
1083 			int		rfl = 0;
1084 			int		ufl = 0;
1085 			unsigned char	*cmdp = *argv;
1086 
1087 			optinit(&optv);
1088 
1089 			while ((c = optnext(argc, argv, &optv, "ru",
1090 			    "map [-r | -u] [fromstr [tostr [comment]]]")) !=
1091 			    -1) {
1092 				if (c == 0)	/* Was -help */
1093 					goto out;
1094 				else if (c == 'r')
1095 					rfl++;
1096 				else if (c == 'u')
1097 					ufl++;
1098 			}
1099 			argv += --optv.optind;
1100 			argc -= optv.optind;
1101 
1102 			if (argc == 1 && (rfl+ufl) == 0) {
1103 				shedit_list_map(&f);
1104 			} else if (argc == 1 && rfl) {
1105 				shedit_remap();
1106 			} else if (argc == 2 && ufl) {
1107 				shedit_del_map((char *)argv[1]);
1108 			} else if (argc == 3 || argc == 4) {
1109 				/*
1110 				 * argv[1] is map from
1111 				 * argv[2] is map to
1112 				 * argv[3] is the optional comment
1113 				 */
1114 				if (!shedit_add_map((char *)argv[1],
1115 						(char *)argv[2],
1116 						(char *)argv[3])) {
1117 					prs(cmdp);
1118 					prs(UC ": ");
1119 					prs(UC "already defined\n");
1120 					gfailure(UC "bad map", NULL);
1121 				}
1122 			} else if (argc > 4) {
1123 				gfailure(UC arglist, NULL);
1124 			} else {
1125 				gfailure(UC mssgargn, NULL);
1126 			}
1127 		}
1128 		break;
1129 #endif	/* INTERACTIVE */
1130 
1131 #ifdef	DO_SYSALLOC
1132 	case SYSALLOC:
1133 		chkmem();
1134 		break;
1135 #endif
1136 
1137 #ifdef	DO_SYSALIAS
1138 	case SYSALIAS:
1139 		sysalias(argc, argv);
1140 		break;
1141 	case SYSUNALIAS:
1142 		sysunalias(argc, argv);
1143 		break;
1144 #endif
1145 
1146 #ifdef	DO_SYSTRUE
1147 	case SYSTRUE:
1148 		break;
1149 	case SYSFALSE:
1150 		exitval = 1;
1151 		break;
1152 #endif
1153 
1154 #ifdef	DO_SYSBUILTIN
1155 	case SYSBUILTIN:
1156 		sysbuiltin(argc, argv);
1157 		break;
1158 #endif
1159 
1160 #ifdef	HAVE_LOADABLE_LIBS
1161 	case SYSLOADABLE: {
1162 		int		exval;
1163 		struct sysnod2	*s2;
1164 
1165 		s2 = sh_findbuiltin(*argv);
1166 		if (s2 == NULL) {
1167 			failure(*argv, notfound);
1168 			break;
1169 		}
1170 		exval = (*s2->sysptr)(argc, argv, &bosh);
1171 		if (exval)
1172 			exitval = exval;
1173 	}
1174 		break;
1175 #endif
1176 
1177 #ifdef	DO_SYSFIND
1178 	case SYSFIND: {
1179 		int	exval = sysfind(argc, argv, &bosh);
1180 
1181 		if (exval)
1182 			exitval = exval;
1183 	}
1184 		break;
1185 #endif
1186 
1187 #ifdef	DO_SYSSYNC
1188 	case SYSSYNC:
1189 #ifdef	HAVE_SYNC
1190 		sync();
1191 #else
1192 		failure(*argv, unimplemented);
1193 #endif
1194 		break;
1195 #endif
1196 
1197 #ifdef	DO_SYSPGRP
1198 	case SYSPGRP:
1199 		syspgrp(argc, (char **)argv);
1200 		break;
1201 #endif
1202 
1203 #ifdef	DO_SYSERRSTR
1204 	case SYSERRSTR: {
1205 			int	err;
1206 			char	*msg;
1207 
1208 			ind = optskip(argc, argv, "errstr errno");
1209 #ifndef	HAVE_STRERROR
1210 #define	strerror(a)	errmsgstr(a)
1211 #endif
1212 			if (ind < 0)
1213 				break;
1214 
1215 			a1 = argv[ind];
1216 
1217 			if (a1 && (err = stoi(a1)) > 0) {
1218 				errno = 0;
1219 				msg = strerror(err);
1220 				if (errno == 0 && msg) {
1221 					prs_buff(UC msg);
1222 					prc_buff(NL);
1223 				}
1224 			}
1225 		}
1226 		break;
1227 #endif
1228 
1229 #ifdef	DO_SYSPRINTF
1230 	case SYSPRINTF:
1231 		sysprintf(argc, argv);
1232 		break;
1233 #endif
1234 
1235 #ifdef	DO_SYSCOMMAND
1236 	case SYSCOMMAND:
1237 		syscommand(argc, argv, t, xflags);
1238 		break;
1239 #endif
1240 
1241 #ifdef	DO_SYSFC
1242 	case SYSFC:
1243 		syshist(argc, argv, t, xflags);
1244 		if (intrptr) {		/* Was set by syshist()?	*/
1245 			*intrptr = 0;	/* Clear interrupt counter	*/
1246 			intrptr = 0;	/* Disable intrptr for now	*/
1247 		}
1248 		break;
1249 #endif
1250 
1251 	default:
1252 		prs_buff(_gettext("unknown builtin\n"));
1253 	}
1254 #if	defined(DO_POSIX_EXPORT) || defined(DO_POSIX_UNSET) || \
1255 	defined(DO_GETOPT_UTILS) || defined(INTERACTIVE) || \
1256 	defined(DO_POSIX_CD) || defined(DO_POSIX_FAILURE)
1257 out:
1258 #endif
1259 	flushb();			/* Flush print buffer */
1260 	restore(fdindex);		/* Restore file descriptors */
1261 	if (type != SYSWAIT)		/* Do not clobber $/ from wait */
1262 		exval_set(exitval);	/* Prepare ${.sh.*} parameters */
1263 #ifdef	DO_ERR_TRAP
1264 	if ((trapnote & TRAPSET) ||
1265 	    (exitval &&
1266 	    (xflags & XEC_NOSTOP) == 0))
1267 #endif
1268 		chktrap();		/* Run installed traps */
1269 }
1270 
1271 #ifdef	DO_POSIX_CD
1272 static int
opt_LP(argc,argv,opts,use)1273 opt_LP(argc, argv, opts, use)
1274 	int		argc;
1275 	unsigned char	**argv;
1276 	int		*opts;
1277 	const	 char	*use;
1278 {
1279 	struct optv	optv;
1280 	int		c;
1281 	int		opt = *opts;
1282 
1283 	optinit(&optv);
1284 	if (opt < 0) {
1285 		optv.optflag |= OPT_NOFAIL;
1286 		*opts = 0;
1287 	}
1288 	while ((c = optnext(argc, argv, &optv, "LP", use)) != -1) {
1289 		if (c == 0) {	/* Was -help or bad opt */
1290 			return (-1);
1291 		} else if (c == '?') {
1292 			if (opt < 0) {
1293 				/*
1294 				 * If *opts has been preinitialzed with -1,
1295 				 * accept e.g. -2 as a a pushd offset arg.
1296 				 */
1297 				c = argv[optv.ooptind][1];
1298 				if (c >= '0' && c <= '9')
1299 					return (optv.ooptind);
1300 				optbad(argc, argv, &optv);
1301 				gfailure((unsigned char *)usage, use);
1302 				return (-1);
1303 			}
1304 		} else if (c == 'L') {
1305 			*opts = CHDIR_L;
1306 		} else if (c == 'P') {
1307 			*opts = CHDIR_P;
1308 		}
1309 	}
1310 
1311 	/*
1312 	 * POSIX decided in 1992 to introduce an incompatible default for "cd".
1313 	 * Even though this is incompatible with the Bourne Shell behavior, it
1314 	 * may be too late to go back.
1315 	 */
1316 	if ((flags2 & posixflg) && *opts == 0)
1317 		*opts = CHDIR_L;
1318 
1319 	return (optv.optind);
1320 }
1321 #endif	/* DO_POSIX_CD */
1322 
1323 static int
whatis(arg,verbose)1324 whatis(arg, verbose)
1325 	unsigned char	*arg;
1326 	int		verbose;
1327 {
1328 #ifdef	DO_SYSALIAS
1329 	char		*val;
1330 
1331 	if ((val = ab_value(LOCAL_AB, (char *)arg, NULL,
1332 				AB_BEGIN)) != NULL) {
1333 		if (verbose) {
1334 			prs_buff(arg);
1335 			prs_buff(_gettext(" is a local alias for '"));
1336 		}
1337 		prs_buff(UC val);
1338 		if (verbose)
1339 			prs_buff(UC "'\n");
1340 		else
1341 			prs_buff(UC "\n");
1342 		return (0);
1343 	} else if ((val = ab_value(GLOBAL_AB, (char *)arg, NULL,
1344 				AB_BEGIN)) != NULL) {
1345 		if (verbose) {
1346 			prs_buff(arg);
1347 			prs_buff(_gettext(" is a global alias for '"));
1348 		}
1349 		prs_buff(UC val);
1350 		if (verbose)
1351 			prs_buff(UC "'\n");
1352 		else
1353 			prs_buff(UC "\n");
1354 		return (0);
1355 	}
1356 #endif	/* DO_SYSALIAS */
1357 	return (what_is_path(arg, verbose));
1358 }
1359 
1360 #ifdef	DO_SYSCOMMAND
1361 static void
syscommand(argc,argv,t,xflags)1362 syscommand(argc, argv, t, xflags)
1363 	int		argc;
1364 	unsigned char	**argv;
1365 	struct trenod	*t;
1366 	int		xflags;
1367 {
1368 	struct optv	optv;
1369 	int		c;
1370 	int		pflg = 0;
1371 	int		vflg = 0;
1372 	int		Vflg = 0;
1373 	char		*cusage = "command [-p][-v | -V] [name [arg ...]]";
1374 
1375 	optinit(&optv);
1376 
1377 	while ((c = optnext(argc, argv, &optv, "pvV", cusage)) != -1) {
1378 		if (c == 0) {	/* Was -help */
1379 			return;
1380 		} else if (c == 'p') {
1381 			pflg++;
1382 		} else if (c == 'v') {
1383 			if (Vflg) {
1384 				optbad(argc, UCP argv, &optv);
1385 				gfailure((unsigned char *)usage, cusage);
1386 				return;
1387 			}
1388 			vflg++;
1389 		} else if (c == 'V') {
1390 			if (vflg) {
1391 				optbad(argc, UCP argv, &optv);
1392 				gfailure((unsigned char *)usage, cusage);
1393 				return;
1394 			}
1395 			Vflg++;
1396 		}
1397 	}
1398 	argc -= optv.optind;
1399 	argv += optv.optind;
1400 	if ((vflg + Vflg) && *argv == NULL) {
1401 		gfailure((unsigned char *)usage, cusage);
1402 		return;
1403 	}
1404 	if (pflg) {
1405 		flags |= ppath;
1406 	}
1407 	if (vflg) {
1408 		exitval = whatis(*argv, 0);
1409 	} else if (Vflg) {
1410 		exitval = whatis(*argv, 1);
1411 	} else if (*argv) {
1412 		short		cmdhash;
1413 		unsigned long	oflags = flags;
1414 
1415 		/*
1416 		 * A function may overlay a builtin command.
1417 		 * We therefore do not search for functions.
1418 		 */
1419 		flags |= nofuncs;
1420 		cmdhash = pathlook(argv[0], 0, (struct argnod *)0);
1421 		flags = oflags;
1422 
1423 		if (hashtype(cmdhash) == BUILTIN) {
1424 			struct ionod	*iop = t->treio;
1425 
1426 			if (cmdhash & SPC_BUILTIN)
1427 				flags |= noexit;
1428 			t->treio = NULL;
1429 			builtin(cmdhash, argc, argv, t, xflags);
1430 			t->treio = iop;
1431 			flags &= ~(ppath | noexit);
1432 			return;
1433 		} else {
1434 			unsigned char		*sav = savstak();
1435 			struct ionod		*iosav = iotemp;
1436 			struct comnod	cnod;
1437 
1438 			cnod.comtyp = TCOM;
1439 			cnod.comio  = NULL;
1440 			cnod.comarg = (struct argnod *)argv;
1441 			cnod.comset = comptr(t)->comset;
1442 
1443 			flags |= nofuncs;
1444 			execute((struct trenod *)&cnod, xflags | XEC_HASARGV,
1445 				0, no_pipe, no_pipe);
1446 			flags &= ~nofuncs;
1447 			tdystak(sav, iosav);
1448 		}
1449 	}
1450 	flags &= ~ppath;
1451 }
1452 #endif	/* DO_SYSCOMMAND */
1453 
1454 #if	defined(INTERACTIVE) || defined(DO_SYSFC)
1455 #define	LFLAG	1	/* Do listing		*/
1456 #define	SFLAG	2	/* Re-execute		*/
1457 #define	EFLAG	4	/* Edit and Re-execute	*/
1458 #define	ALLFLAG	1024	/* Show full history	*/
1459 /*
1460  * The implementation for the "history" and the "fc" builtin command.
1461  *
1462  * The "fc" builtin is an historic artefact from ksh.
1463  * It is based on the original ksh history concept that has never been used in
1464  * our history editor that is rather based on the history editor in "bsh", the
1465  * shell from H.Berthold AG that had a fully integrated history editor in 1984
1466  * already; based on a concept from 1982. Ksh originally called an external
1467  * editor command to modify the history.
1468  *
1469  * As "fc" is part of the POSIX standard, we have to implement it even though
1470  * the features required by POSIX do not match the features from a modern
1471  * history editor, so we try to do the best we can...
1472  *
1473  * The "history" implementation currently just disables the re-run and edit
1474  * features.
1475  */
1476 static void
syshist(argc,argv,t,xflags)1477 syshist(argc, argv, t, xflags)
1478 	int		argc;
1479 	unsigned char	**argv;
1480 	struct trenod	*t;
1481 	int		xflags;
1482 {
1483 	struct optv	optv;
1484 	int		c;
1485 	int		fd;
1486 	int		first = 0;
1487 	int		last = 0;
1488 	int		flg = 0;
1489 	int		neg = 0;
1490 	int		hflg = HI_INTR|HI_TAB;
1491 	unsigned char	*av0 = argv[0];
1492 	unsigned char	*cp;
1493 	unsigned char	*editor = NULL;
1494 	unsigned char	*substp = NULL;
1495 	struct tempblk	tb;
1496 	char		*fusage =
1497 			"fc [-r][-l][-n][-s][-e editor] [first [last]]";
1498 	char		*husage =
1499 			"history [-r][-n] [first [last]]";
1500 	char		*xusage = fusage;
1501 
1502 	if (*av0 == 'h') {
1503 		xusage = husage;
1504 		flg |= LFLAG;
1505 	}
1506 
1507 	optinit(&optv);
1508 
1509 	while ((c = optnext(argc, argv, &optv,
1510 				"rlnse:1234567890", xusage)) != -1) {
1511 		if (c == 0) {	/* Was -help */
1512 			return;
1513 		} else if (c == 'r') {
1514 			hflg |= HI_REVERSE;
1515 		} else if (c == 'l') {
1516 			flg |= LFLAG;
1517 		} else if (c == 'n') {
1518 			hflg |= HI_NONUM;
1519 		} else if (c == 's') {
1520 			flg |= SFLAG;
1521 		} else if (c == 'e') {
1522 			editor = UC optv.optarg;
1523 		} else if (digit(c)) {
1524 			first = stosi(argv[optv.ooptind]);
1525 			argc--;
1526 			argv++;
1527 			neg++;
1528 			break;
1529 		}
1530 	}
1531 	argc -= optv.optind;
1532 	argv += optv.optind;
1533 
1534 	if ((flg & (LFLAG | SFLAG)) == 0)
1535 		flg |= EFLAG;
1536 
1537 	if ((flg & SFLAG) && argc > 0) {
1538 		/*
1539 		 * POSIX requires new=old only with "fc -s".
1540 		 */
1541 		if (anys(UC "=", argv[0])) {
1542 			substp = argv[0];
1543 			argc--;
1544 			argv++;
1545 		}
1546 	}
1547 
1548 	if (!first && argc > 0) {
1549 		cp = argv[0];
1550 		if (*cp == '+')
1551 			cp++;
1552 		if (digit(*cp)) {
1553 			first = stoi(UC cp);
1554 			if (first == 0)
1555 				flg |= ALLFLAG;
1556 		} else {
1557 			first = shedit_search_history(&intrptr,
1558 							hflg, 0, C argv[0]);
1559 		}
1560 		argc--;
1561 		argv++;
1562 	}
1563 	if (first == 0 && !neg && (flg & ALLFLAG) == 0) {
1564 		if (flg & LFLAG)
1565 			first = -15;
1566 		else
1567 			first = -1;
1568 	}
1569 	if (flg & SFLAG) {
1570 		last = first;
1571 	} else if (argc > 0) {
1572 		cp = argv[0];
1573 		if (*cp == '+')
1574 			cp++;
1575 		if (digit(*cp)) {
1576 			last = stosi(UC cp);
1577 		} else {
1578 			last = shedit_search_history(&intrptr,
1579 							hflg, 0, C argv[0]);
1580 		}
1581 		argc--;
1582 		argv++;
1583 	}
1584 	if (argc > 0) {
1585 		gfailure(av0, toomanyargs);
1586 		return;
1587 	}
1588 	if (last == 0) {
1589 		if (flg & LFLAG) {
1590 			unsigned	lastn;
1591 
1592 			shedit_histrange(NULL, &lastn, NULL);
1593 			last = lastn;
1594 			if (first == 0 && neg)
1595 				first = last;
1596 		} else {
1597 			last = first;
1598 		}
1599 	}
1600 
1601 	if (flg & LFLAG) {
1602 		if (isatty(STDOUT_FILENO))
1603 			hflg |= HI_PRETTYP;
1604 		/*
1605 		 * We do not remove the new "fc -l" command but should include
1606 		 * the last command in our listing.
1607 		 */
1608 		if (first < 0)
1609 			first--;
1610 	} else {
1611 		unsigned	lastn;
1612 
1613 		shedit_histrange(NULL, &lastn, NULL);
1614 		/*
1615 		 * Remove this "fc" command from the history.
1616 		 */
1617 		(void) shedit_remove_history(&intrptr,
1618 						hflg, lastn, NULL);
1619 		if (last == lastn) {
1620 			shedit_histrange(NULL, &lastn, NULL);
1621 			last = lastn;
1622 		}
1623 		fd = tmpfil(&tb);
1624 	}
1625 	/*
1626 	 * Even in case we write the history into a file, we do not
1627 	 * convert ASCII newlines into ANSI newlines, so we may see
1628 	 * more lines in the file than history entries selected.
1629 	 */
1630 	if (shedit_history((flg & LFLAG) ? NULL : &fd,
1631 			&intrptr, hflg, first, last, C substp) != 0) {
1632 		gfailure(av0, "history specification out of range");
1633 		return;
1634 	}
1635 	if (flg & LFLAG)
1636 		return;
1637 
1638 	if (flg & EFLAG) {
1639 		char	*av[2];
1640 
1641 		av[0] = C make(tmpout);
1642 		av[1] = NULL;
1643 
1644 		if (editor == NULL)
1645 			editor = fcenod.namval;
1646 		if (editor == NULL)
1647 			editor = UC fcename;
1648 		execexp(editor, (Intptr_t)av, xflags);
1649 		unlink(av[0]);
1650 		free(av[0]);
1651 		if (exitval) {
1652 			close(fd);
1653 			return;
1654 		}
1655 	} else {
1656 		unlink(C tmpout);
1657 	}
1658 
1659 	lseek(fd, (off_t)0, SEEK_SET);
1660 	execexp(0, (Intptr_t)fd, xflags);
1661 	/*
1662 	 * Add re-executed commands to the history.
1663 	 */
1664 	lseek(fd, (off_t)0, SEEK_SET);
1665 	shedit_read_history(&fd, &intrptr, hflg);
1666 
1667 	close(fd);
1668 }
1669 #endif	/* defined(INTERACTIVE) || defined(DO_SYSFC) */
1670 
1671 #ifdef	DO_TILDE
1672 static unsigned char *
etilde(arg,s)1673 etilde(arg, s)
1674 	unsigned char	*arg;		/* complete encironment entry	*/
1675 	unsigned char	*s;		/* points to '='		*/
1676 {
1677 	UIntptr_t	b = relstak();
1678 	unsigned char	*p;
1679 
1680 	++s;				/* Point to 1st char of value	*/
1681 	while (arg < s) {		/* Copy parts up to '='		*/
1682 		GROWSTAKTOP();
1683 		pushstak(*arg++);
1684 	}
1685 	while (*arg) {
1686 		p = UC strchr(C arg, '~');
1687 		if (p == NULL)
1688 			break;
1689 		while (arg < p) {	/* Copy up to '~'		*/
1690 			GROWSTAKTOP();
1691 			pushstak(*arg++);
1692 		}
1693 		if (p > s && p[-1] != ':') {
1694 			/* EMPTY */
1695 			;
1696 		} else if ((p = do_tilde(arg)) != NULL) {
1697 			unsigned char	c;
1698 
1699 			while (*p) {	/* Copy expanded directory	*/
1700 				GROWSTAKTOP();
1701 				pushstak(*p++);
1702 			}
1703 			do {		/* Skip unexpanded input	*/
1704 				c = *++arg;
1705 			} while (c && c != '/' && c != ':');
1706 			continue;
1707 		}
1708 		GROWSTAKTOP();
1709 		pushstak(*arg++);	/* Copy '~'			*/
1710 	}
1711 	while (*arg) {			/* Copy rest			*/
1712 		GROWSTAKTOP();
1713 		pushstak(*arg++);
1714 	}
1715 	zerostak();
1716 	return (absstak(b));
1717 }
1718 #endif
1719