blob: 90725a1780f430e49058770c97bd77e4373e851a [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
5 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersencb57d552001-06-28 07:25:16 +00006 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Kenneth Almquist.
10 *
Eric Andersendf82f612001-06-28 07:46:40 +000011 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
Eric Andersencb57d552001-06-28 07:25:16 +000015 *
Eric Andersendf82f612001-06-28 07:46:40 +000016 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 * This version of ash is adapted from the source in Debian's ash 0.3.8-5
26 * package.
27 *
28 * Modified by Erik Andersen <andersee@debian.org> to be used in
29 * busybox, based in part on prior work done by Vladimir Oleynik
30 * <vodz@usa.net>
31 *
32 * Original copyright notice is retained at the end of this file.
Eric Andersencb57d552001-06-28 07:25:16 +000033 */
34
35#undef _GNU_SOURCE
36#undef ASH_TYPE
37#undef ASH_GETOPTS
38#undef ASH_MATH_SUPPORT
Eric Andersendf82f612001-06-28 07:46:40 +000039#undef FNMATCH_BROKEN
40#undef GLOB_BROKEN
Eric Andersencb57d552001-06-28 07:25:16 +000041
42#include <assert.h>
43#include <ctype.h>
44#include <dirent.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <limits.h>
48#include <paths.h>
49#include <pwd.h>
50#include <setjmp.h>
51#include <signal.h>
52#include <stdarg.h>
53#include <stdbool.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <sysexits.h>
58#include <unistd.h>
59#include <sys/stat.h>
60#include <sys/cdefs.h>
61#include <sys/ioctl.h>
62#include <sys/param.h>
63#include <sys/resource.h>
64#include <sys/time.h>
65#include <sys/times.h>
66#include <sys/types.h>
67#include <sys/wait.h>
68
69
70#if !defined(FNMATCH_BROKEN)
71#include <fnmatch.h>
72#endif
73#if !defined(GLOB_BROKEN)
74#include <glob.h>
75#endif
76
77#if JOBS
78#include <termios.h>
79#undef CEOF /* syntax.h redefines this */
80#endif
81
82#include "cmdedit.h"
83#include "busybox.h"
84#include "ash.h"
85
86
87#define _DIAGASSERT(x)
88
89#define ATABSIZE 39
90
91#define S_DFL 1 /* default signal handling (SIG_DFL) */
92#define S_CATCH 2 /* signal is caught */
93#define S_IGN 3 /* signal is ignored (SIG_IGN) */
94#define S_HARD_IGN 4 /* signal is ignored permenantly */
95#define S_RESET 5 /* temporary - to reset a hard ignored sig */
96
97
98
99struct alias *atab[ATABSIZE];
100
101static void setalias __P((char *, char *));
102static struct alias **hashalias __P((const char *));
103static struct alias *freealias __P((struct alias *));
104static struct alias **__lookupalias __P((const char *));
105static char *trap[NSIG]; /* trap handler commands */
106static char sigmode[NSIG - 1]; /* current value of signal */
107static char gotsig[NSIG - 1]; /* indicates specified signal received */
108static int pendingsigs; /* indicates some signal received */
109
110
111static void
112setalias(name, val)
113 char *name, *val;
114{
115 struct alias *ap, **app;
116
117 app = __lookupalias(name);
118 ap = *app;
119 INTOFF;
120 if (ap) {
121 if (!(ap->flag & ALIASINUSE)) {
122 ckfree(ap->val);
123 }
124 ap->val = savestr(val);
125 ap->flag &= ~ALIASDEAD;
126 } else {
127 /* not found */
128 ap = ckmalloc(sizeof (struct alias));
129 ap->name = savestr(name);
130 ap->val = savestr(val);
131 ap->flag = 0;
132 ap->next = 0;
133 *app = ap;
134 }
135 INTON;
136}
137
138static int
139unalias(name)
140 char *name;
141 {
142 struct alias **app;
143
144 app = __lookupalias(name);
145
146 if (*app) {
147 INTOFF;
148 *app = freealias(*app);
149 INTON;
150 return (0);
151 }
152
153 return (1);
154}
155
156#ifdef mkinit
157static void rmaliases __P((void));
158
159SHELLPROC {
160 rmaliases();
161}
162#endif
163
164static void
165rmaliases() {
166 struct alias *ap, **app;
167 int i;
168
169 INTOFF;
170 for (i = 0; i < ATABSIZE; i++) {
171 app = &atab[i];
172 for (ap = *app; ap; ap = *app) {
173 *app = freealias(*app);
174 if (ap == *app) {
175 app = &ap->next;
176 }
177 }
178 }
179 INTON;
180}
181
182struct alias *
183lookupalias(name, check)
184 const char *name;
185 int check;
186{
187 struct alias *ap = *__lookupalias(name);
188
189 if (check && ap && (ap->flag & ALIASINUSE))
190 return (NULL);
191 return (ap);
192}
193
194
195/*
196 * TODO - sort output
197 */
198static int
199aliascmd(argc, argv)
200 int argc;
201 char **argv;
202{
203 char *n, *v;
204 int ret = 0;
205 struct alias *ap;
206
207 if (argc == 1) {
208 int i;
209
210 for (i = 0; i < ATABSIZE; i++)
211 for (ap = atab[i]; ap; ap = ap->next) {
212 printalias(ap);
213 }
214 return (0);
215 }
216 while ((n = *++argv) != NULL) {
217 if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
218 if ((ap = *__lookupalias(n)) == NULL) {
219 outfmt(out2, "%s: %s not found\n", "alias", n);
220 ret = 1;
221 } else
222 printalias(ap);
223 }
224 else {
225 *v++ = '\0';
226 setalias(n, v);
227 }
228 }
229
230 return (ret);
231}
232
233static int
234unaliascmd(argc, argv)
235 int argc;
236 char **argv;
237{
238 int i;
239
240 while ((i = nextopt("a")) != '\0') {
241 if (i == 'a') {
242 rmaliases();
243 return (0);
244 }
245 }
246 for (i = 0; *argptr; argptr++) {
247 if (unalias(*argptr)) {
248 outfmt(out2, "%s: %s not found\n", "unalias", *argptr);
249 i = 1;
250 }
251 }
252
253 return (i);
254}
255
256static struct alias **
257hashalias(p)
258 const char *p;
259 {
260 unsigned int hashval;
261
262 hashval = *p << 4;
263 while (*p)
264 hashval+= *p++;
265 return &atab[hashval % ATABSIZE];
266}
267
268static struct alias *
269freealias(struct alias *ap) {
270 struct alias *next;
271
272 if (ap->flag & ALIASINUSE) {
273 ap->flag |= ALIASDEAD;
274 return ap;
275 }
276
277 next = ap->next;
278 ckfree(ap->name);
279 ckfree(ap->val);
280 ckfree(ap);
281 return next;
282}
283
284static void
285printalias(const struct alias *ap) {
286 char *p;
287
288 p = single_quote(ap->val);
289 out1fmt("alias %s=%s\n", ap->name, p);
290 stunalloc(p);
291}
292
293static struct alias **
294__lookupalias(const char *name) {
295 struct alias **app = hashalias(name);
296
297 for (; *app; app = &(*app)->next) {
298 if (equal(name, (*app)->name)) {
299 break;
300 }
301 }
302
303 return app;
304}
305
306#ifdef ASH_MATH_SUPPORT
307/* The generated file arith.c has been snipped. If you want this
308 * stuff back in, feel free to add it to your own copy. */
309#endif
310
311/*
312 * This file was generated by the mkbuiltins program.
313 */
314
315static int bgcmd __P((int, char **));
316static int breakcmd __P((int, char **));
317static int cdcmd __P((int, char **));
318static int commandcmd __P((int, char **));
319static int dotcmd __P((int, char **));
320static int evalcmd __P((int, char **));
321static int execcmd __P((int, char **));
322static int exitcmd __P((int, char **));
323static int exportcmd __P((int, char **));
324static int histcmd __P((int, char **));
325static int fgcmd __P((int, char **));
326static int hashcmd __P((int, char **));
327static int jobscmd __P((int, char **));
328static int killcmd __P((int, char **));
329static int localcmd __P((int, char **));
330static int pwdcmd __P((int, char **));
331static int readcmd __P((int, char **));
332static int returncmd __P((int, char **));
333static int setcmd __P((int, char **));
334static int setvarcmd __P((int, char **));
335static int shiftcmd __P((int, char **));
336static int trapcmd __P((int, char **));
337static int umaskcmd __P((int, char **));
338static int unaliascmd __P((int, char **));
339static int unsetcmd __P((int, char **));
340static int waitcmd __P((int, char **));
341static int aliascmd __P((int, char **));
342static int ulimitcmd __P((int, char **));
343static int timescmd __P((int, char **));
344#ifdef ASH_MATH_SUPPORT
345static int expcmd __P((int, char **));
346#endif
347#ifdef ASH_TYPE
348static int typecmd __P((int, char **));
349#endif
350#ifdef ASH_GETOPTS
351static int getoptscmd __P((int, char **));
352#endif
353#ifndef BB_TRUE_FALSE
354static int true_main __P((int, char **));
355static int false_main __P((int, char **));
356#endif
357
358static struct builtincmd *DOTCMD;
359static struct builtincmd *BLTINCMD;
360static struct builtincmd *COMMANDCMD;
361static struct builtincmd *EXECCMD;
362static struct builtincmd *EVALCMD;
363
364/* It is CRUCIAL that this listing be kept in ascii order, otherwise
365 * the binary search in find_builtin() will stop working. If you value
366 * your kneecaps, you'll be sure to *make sure* that any changes made
367 * to this array result in the listing remaining in ascii order. You
368 * have been warned.
369 */
370static const struct builtincmd builtincmds[] = {
371 { ".", dotcmd, 1 },
372 { ":", true_main, 1 },
373 { "alias", aliascmd, 6 },
374 { "bg", bgcmd, 2 },
375 { "break", breakcmd, 1 },
376 { "builtin", bltincmd, 1 },
377 { "cd", cdcmd, 2 },
378 { "chdir", cdcmd, 0 },
379 { "command", commandcmd, 2 },
380 { "continue", breakcmd, 1 },
381 { "eval", evalcmd, 1 },
382 { "exec", execcmd, 1 },
383 { "exit", exitcmd, 1 },
384#ifdef ASH_MATH_SUPPORT
385 { "exp", expcmd, 0 },
386#endif
387 { "export", exportcmd, 5 },
388 { "false", false_main, 2 },
389 { "fc", histcmd, 2 },
390 { "fg", fgcmd, 2 },
391#ifdef ASH_GETOPTS
392 { "getopts", getoptscmd, 2 },
393#endif
394 { "hash", hashcmd, 0 },
395 { "jobs", jobscmd, 2 },
396 { "kill", killcmd, 2 },
397#ifdef ASH_MATH_SUPPORT
398 { "let", expcmd, 0 },
399#endif
400 { "local", localcmd, 4 },
401 { "pwd", pwdcmd, 0 },
402 { "read", readcmd, 2 },
403 { "readonly", exportcmd, 5 },
404 { "return", returncmd, 1 },
405 { "set", setcmd, 1 },
406 { "setvar", setvarcmd, 0 },
407 { "shift", shiftcmd, 1 },
408 { "times", timescmd, 1 },
409 { "trap", trapcmd, 1 },
410 { "true", true_main, 2 },
411#ifdef ASH_TYPE
412 { "type", typecmd, 0 },
413#endif
414 { "ulimit", ulimitcmd, 0 },
415 { "umask", umaskcmd, 2 },
416 { "unalias", unaliascmd, 2 },
417 { "unset", unsetcmd, 1 },
418 { "wait", waitcmd, 2 },
419};
420#define NUMBUILTINS (sizeof (builtincmds) / sizeof (struct builtincmd) )
421
422
423/* $NetBSD: cd.c,v 1.27 1999/07/09 03:05:49 christos Exp $ */
424
Eric Andersencb57d552001-06-28 07:25:16 +0000425static int docd __P((char *, int));
426static char *getcomponent __P((void));
427static void updatepwd __P((char *));
428static void getpwd __P((void));
429
430static char *curdir = nullstr; /* current working directory */
431static char *cdcomppath;
432
433#ifdef mkinit
434INCLUDE "cd.h"
435INIT {
436 setpwd(0, 0);
437}
438#endif
439
440static int
441cdcmd(argc, argv)
442 int argc;
443 char **argv;
444{
445 const char *dest;
446 const char *path;
447 char *p;
448 struct stat statb;
449 int print = 0;
450
451 nextopt(nullstr);
452 if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME")) == NULL)
453 error("HOME not set");
454 if (*dest == '\0')
455 dest = ".";
456 if (dest[0] == '-' && dest[1] == '\0') {
457 dest = bltinlookup("OLDPWD");
458 if (!dest || !*dest) {
459 dest = curdir;
460 }
461 print = 1;
462 if (dest)
463 print = 1;
464 else
465 dest = ".";
466 }
467 if (*dest == '/' || (path = bltinlookup("CDPATH")) == NULL)
468 path = nullstr;
469 while ((p = padvance(&path, dest)) != NULL) {
470 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
471 if (!print) {
472 /*
473 * XXX - rethink
474 */
475 if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
476 p += 2;
477 print = strcmp(p, dest);
478 }
479 if (docd(p, print) >= 0)
480 return 0;
481
482 }
483 }
484 error("can't cd to %s", dest);
485 /* NOTREACHED */
486}
487
488
489/*
490 * Actually do the chdir. In an interactive shell, print the
491 * directory name if "print" is nonzero.
492 */
493
494static int
495docd(dest, print)
496 char *dest;
497 int print;
498{
499 char *p;
500 char *q;
501 char *component;
502 struct stat statb;
503 int first;
504 int badstat;
505
506 TRACE(("docd(\"%s\", %d) called\n", dest, print));
507
508 /*
509 * Check each component of the path. If we find a symlink or
510 * something we can't stat, clear curdir to force a getcwd()
511 * next time we get the value of the current directory.
512 */
513 badstat = 0;
514 cdcomppath = sstrdup(dest);
515 STARTSTACKSTR(p);
516 if (*dest == '/') {
517 STPUTC('/', p);
518 cdcomppath++;
519 }
520 first = 1;
521 while ((q = getcomponent()) != NULL) {
522 if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
523 continue;
524 if (! first)
525 STPUTC('/', p);
526 first = 0;
527 component = q;
528 while (*q)
529 STPUTC(*q++, p);
530 if (equal(component, ".."))
531 continue;
532 STACKSTRNUL(p);
533 if ((lstat(stackblock(), &statb) < 0)
534 || (S_ISLNK(statb.st_mode))) {
535 /* print = 1; */
536 badstat = 1;
537 break;
538 }
539 }
540
541 INTOFF;
542 if (chdir(dest) < 0) {
543 INTON;
544 return -1;
545 }
546 updatepwd(badstat ? NULL : dest);
547 INTON;
548 if (print && iflag)
549 out1fmt(snlfmt, curdir);
550 return 0;
551}
552
553
554/*
555 * Get the next component of the path name pointed to by cdcomppath.
556 * This routine overwrites the string pointed to by cdcomppath.
557 */
558
559static char *
560getcomponent() {
561 char *p;
562 char *start;
563
564 if ((p = cdcomppath) == NULL)
565 return NULL;
566 start = cdcomppath;
567 while (*p != '/' && *p != '\0')
568 p++;
569 if (*p == '\0') {
570 cdcomppath = NULL;
571 } else {
572 *p++ = '\0';
573 cdcomppath = p;
574 }
575 return start;
576}
577
578
579
580/*
581 * Update curdir (the name of the current directory) in response to a
582 * cd command. We also call hashcd to let the routines in exec.c know
583 * that the current directory has changed.
584 */
585
586static void
587updatepwd(dir)
588 char *dir;
589 {
590 char *new;
591 char *p;
592 size_t len;
593
594 hashcd(); /* update command hash table */
595
596 /*
597 * If our argument is NULL, we don't know the current directory
598 * any more because we traversed a symbolic link or something
599 * we couldn't stat().
600 */
601 if (dir == NULL || curdir == nullstr) {
602 setpwd(0, 1);
603 return;
604 }
605 len = strlen(dir);
606 cdcomppath = sstrdup(dir);
607 STARTSTACKSTR(new);
608 if (*dir != '/') {
609 p = curdir;
610 while (*p)
611 STPUTC(*p++, new);
612 if (p[-1] == '/')
613 STUNPUTC(new);
614 }
615 while ((p = getcomponent()) != NULL) {
616 if (equal(p, "..")) {
617 while (new > stackblock() && (STUNPUTC(new), *new) != '/');
618 } else if (*p != '\0' && ! equal(p, ".")) {
619 STPUTC('/', new);
620 while (*p)
621 STPUTC(*p++, new);
622 }
623 }
624 if (new == stackblock())
625 STPUTC('/', new);
626 STACKSTRNUL(new);
627 setpwd(stackblock(), 1);
628}
629
630
631
632static int
633pwdcmd(argc, argv)
634 int argc;
635 char **argv;
636{
637 out1fmt(snlfmt, curdir);
638 return 0;
639}
640
641
642
643
644#define MAXPWD 256
645
646/*
647 * Find out what the current directory is. If we already know the current
648 * directory, this routine returns immediately.
649 */
650static void
651getpwd()
652{
653 char buf[MAXPWD];
654
655 /*
656 * Things are a bit complicated here; we could have just used
657 * getcwd, but traditionally getcwd is implemented using popen
658 * to /bin/pwd. This creates a problem for us, since we cannot
659 * keep track of the job if it is being ran behind our backs.
660 * So we re-implement getcwd(), and we suppress interrupts
661 * throughout the process. This is not completely safe, since
662 * the user can still break out of it by killing the pwd program.
663 * We still try to use getcwd for systems that we know have a
664 * c implementation of getcwd, that does not open a pipe to
665 * /bin/pwd.
666 */
667#if defined(__NetBSD__) || defined(__SVR4) || defined(__GLIBC__)
668
669 if (getcwd(buf, sizeof(buf)) == NULL) {
670 char *pwd = getenv("PWD");
671 struct stat stdot, stpwd;
672
673 if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
674 stat(pwd, &stpwd) != -1 &&
675 stdot.st_dev == stpwd.st_dev &&
676 stdot.st_ino == stpwd.st_ino) {
677 curdir = savestr(pwd);
678 return;
679 }
680 error("getcwd() failed: %s", strerror(errno));
681 }
682 curdir = savestr(buf);
683#else
684 {
685 char *p;
686 int i;
687 int status;
688 struct job *jp;
689 int pip[2];
690
691 if (pipe(pip) < 0)
692 error("Pipe call failed");
693 jp = makejob((union node *)NULL, 1);
694 if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
695 (void) close(pip[0]);
696 if (pip[1] != 1) {
697 close(1);
698 dup_as_newfd(pip[1], 1);
699 close(pip[1]);
700 }
701 (void) execl("/bin/pwd", "pwd", (char *)0);
702 error("Cannot exec /bin/pwd");
703 }
704 (void) close(pip[1]);
705 pip[1] = -1;
706 p = buf;
707 while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
708 || (i == -1 && errno == EINTR)) {
709 if (i > 0)
710 p += i;
711 }
712 (void) close(pip[0]);
713 pip[0] = -1;
714 status = waitforjob(jp);
715 if (status != 0)
716 error((char *)0);
717 if (i < 0 || p == buf || p[-1] != '\n')
718 error("pwd command failed");
719 p[-1] = '\0';
720 }
721 curdir = savestr(buf);
722#endif
723}
724
725static void
726setpwd(const char *val, int setold)
727{
728 if (setold) {
729 setvar("OLDPWD", curdir, VEXPORT);
730 }
731 INTOFF;
732 if (curdir != nullstr) {
733 free(curdir);
734 curdir = nullstr;
735 }
736 if (!val) {
737 getpwd();
738 } else {
739 curdir = savestr(val);
740 }
741 INTON;
742 setvar("PWD", curdir, VEXPORT);
743}
744
745/* $NetBSD: error.c,v 1.23 2000/07/03 03:26:19 matt Exp $ */
746
Eric Andersencb57d552001-06-28 07:25:16 +0000747/*
748 * Errors and exceptions.
749 */
750
751/*
752 * Code to handle exceptions in C.
753 */
754
755struct jmploc *handler;
756static int exception;
757volatile int suppressint;
758volatile int intpending;
759
760
761static void exverror __P((int, const char *, va_list))
762 __attribute__((__noreturn__));
763
764/*
765 * Called to raise an exception. Since C doesn't include exceptions, we
766 * just do a longjmp to the exception handler. The type of exception is
767 * stored in the global variable "exception".
768 */
769
770static void
771exraise(e)
772 int e;
773{
774#ifdef DEBUG
775 if (handler == NULL)
776 abort();
777#endif
778 exception = e;
779 longjmp(handler->loc, 1);
780}
781
782
783/*
784 * Called from trap.c when a SIGINT is received. (If the user specifies
785 * that SIGINT is to be trapped or ignored using the trap builtin, then
786 * this routine is not called.) Suppressint is nonzero when interrupts
787 * are held using the INTOFF macro. The call to _exit is necessary because
788 * there is a short period after a fork before the signal handlers are
789 * set to the appropriate value for the child. (The test for iflag is
790 * just defensive programming.)
791 */
792
793static void
794onint() {
795 sigset_t mysigset;
796
797 if (suppressint) {
798 intpending++;
799 return;
800 }
801 intpending = 0;
802 sigemptyset(&mysigset);
803 sigprocmask(SIG_SETMASK, &mysigset, NULL);
804 if (rootshell && iflag)
805 exraise(EXINT);
806 else {
807 signal(SIGINT, SIG_DFL);
808 raise(SIGINT);
809 }
810 /* NOTREACHED */
811}
812
813
814/*
815 * Exverror is called to raise the error exception. If the first argument
816 * is not NULL then error prints an error message using printf style
817 * formatting. It then raises the error exception.
818 */
819static void
820exverror(cond, msg, ap)
821 int cond;
822 const char *msg;
823 va_list ap;
824{
825 CLEAR_PENDING_INT;
826 INTOFF;
827
828#ifdef DEBUG
829 if (msg)
830 TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid()));
831 else
832 TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
833#endif
834 if (msg) {
835 if (commandname)
836 outfmt(&errout, "%s: ", commandname);
837 doformat(&errout, msg, ap);
838#if FLUSHERR
839 outc('\n', &errout);
840#else
841 outcslow('\n', &errout);
842#endif
843 }
844 flushall();
845 exraise(cond);
846 /* NOTREACHED */
847}
848
849
850#ifdef __STDC__
851static void
852error(const char *msg, ...)
853#else
854static void
855error(va_alist)
856 va_dcl
857#endif
858{
859#ifndef __STDC__
860 const char *msg;
861#endif
862 va_list ap;
863#ifdef __STDC__
864 va_start(ap, msg);
865#else
866 va_start(ap);
867 msg = va_arg(ap, const char *);
868#endif
869 exverror(EXERROR, msg, ap);
870 /* NOTREACHED */
871 va_end(ap);
872}
873
874
875#ifdef __STDC__
876static void
877exerror(int cond, const char *msg, ...)
878#else
879static void
880exerror(va_alist)
881 va_dcl
882#endif
883{
884#ifndef __STDC__
885 int cond;
886 const char *msg;
887#endif
888 va_list ap;
889#ifdef __STDC__
890 va_start(ap, msg);
891#else
892 va_start(ap);
893 cond = va_arg(ap, int);
894 msg = va_arg(ap, const char *);
895#endif
896 exverror(cond, msg, ap);
897 /* NOTREACHED */
898 va_end(ap);
899}
900
901
902
903/*
904 * Table of error messages.
905 */
906
907struct errname {
908 short errcode; /* error number */
909 short action; /* operation which encountered the error */
910 const char *msg; /* text describing the error */
911};
912
913
914#define ALL (E_OPEN|E_CREAT|E_EXEC)
915
916static const struct errname errormsg[] = {
917 { EINTR, ALL, "interrupted" },
918 { EACCES, ALL, "permission denied" },
919 { EIO, ALL, "I/O error" },
920 { ENOENT, E_OPEN, "no such file" },
921 { ENOENT, E_CREAT,"directory nonexistent" },
922 { ENOENT, E_EXEC, "not found" },
923 { ENOTDIR, E_OPEN, "no such file" },
924 { ENOTDIR, E_CREAT,"directory nonexistent" },
925 { ENOTDIR, E_EXEC, "not found" },
926 { EISDIR, ALL, "is a directory" },
927 { EEXIST, E_CREAT,"file exists" },
928#ifdef notdef
929 { EMFILE, ALL, "too many open files" },
930#endif
931 { ENFILE, ALL, "file table overflow" },
932 { ENOSPC, ALL, "file system full" },
933#ifdef EDQUOT
934 { EDQUOT, ALL, "disk quota exceeded" },
935#endif
936#ifdef ENOSR
937 { ENOSR, ALL, "no streams resources" },
938#endif
939 { ENXIO, ALL, "no such device or address" },
940 { EROFS, ALL, "read-only file system" },
941 { ETXTBSY, ALL, "text busy" },
942#ifdef SYSV
943 { EAGAIN, E_EXEC, "not enough memory" },
944#endif
945 { ENOMEM, ALL, "not enough memory" },
946#ifdef ENOLINK
947 { ENOLINK, ALL, "remote access failed" },
948#endif
949#ifdef EMULTIHOP
950 { EMULTIHOP, ALL, "remote access failed" },
951#endif
952#ifdef ECOMM
953 { ECOMM, ALL, "remote access failed" },
954#endif
955#ifdef ESTALE
956 { ESTALE, ALL, "remote access failed" },
957#endif
958#ifdef ETIMEDOUT
959 { ETIMEDOUT, ALL, "remote access failed" },
960#endif
961#ifdef ELOOP
962 { ELOOP, ALL, "symbolic link loop" },
963#endif
964 { E2BIG, E_EXEC, "argument list too long" },
965#ifdef ELIBACC
966 { ELIBACC, E_EXEC, "shared library missing" },
967#endif
968 { 0, 0, NULL },
969};
970
971
972/*
973 * Return a string describing an error. The returned string may be a
974 * pointer to a static buffer that will be overwritten on the next call.
975 * Action describes the operation that got the error.
976 */
977
978static const char *
979errmsg(e, action)
980 int e;
981 int action;
982{
983 struct errname const *ep;
984 static char buf[12];
985
986 for (ep = errormsg ; ep->errcode ; ep++) {
987 if (ep->errcode == e && (ep->action & action) != 0)
988 return ep->msg;
989 }
990 fmtstr(buf, sizeof buf, "error %d", e);
991 return buf;
992}
993
994
995#ifdef REALLY_SMALL
996static void
997__inton() {
998 if (--suppressint == 0 && intpending) {
999 onint();
1000 }
1001}
1002#endif
1003/* $NetBSD: eval.c,v 1.57 2001/02/04 19:52:06 christos Exp $ */
1004
Eric Andersencb57d552001-06-28 07:25:16 +00001005
1006/* flags in argument to evaltree */
1007#define EV_EXIT 01 /* exit after evaluating tree */
1008#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
1009#define EV_BACKCMD 04 /* command executing within back quotes */
1010
1011static int evalskip; /* set if we are skipping commands */
1012static int skipcount; /* number of levels to skip */
1013static int loopnest; /* current loop nesting level */
1014static int funcnest; /* depth of function calls */
1015
1016
1017static char *commandname;
1018struct strlist *cmdenviron;
1019static int exitstatus; /* exit status of last command */
1020static int oexitstatus; /* saved exit status */
1021
1022
1023static void evalloop __P((union node *, int));
1024static void evalfor __P((union node *, int));
1025static void evalcase __P((union node *, int));
1026static void evalsubshell __P((union node *, int));
1027static void expredir __P((union node *));
1028static void evalpipe __P((union node *));
1029#ifdef notyet
1030static void evalcommand __P((union node *, int, struct backcmd *));
1031#else
1032static void evalcommand __P((union node *, int));
1033#endif
1034static void prehash __P((union node *));
1035static void eprintlist __P((struct strlist *));
1036
1037
1038/*
1039 * Called to reset things after an exception.
1040 */
1041
1042#ifdef mkinit
1043INCLUDE "eval.h"
1044
1045RESET {
1046 evalskip = 0;
1047 loopnest = 0;
1048 funcnest = 0;
1049}
1050
1051SHELLPROC {
1052 exitstatus = 0;
1053}
1054#endif
1055
1056
1057
1058/*
1059 * The eval commmand.
1060 */
1061
1062static int
1063evalcmd(argc, argv)
1064 int argc;
1065 char **argv;
1066{
1067 char *p;
1068 char *concat;
1069 char **ap;
1070
1071 if (argc > 1) {
1072 p = argv[1];
1073 if (argc > 2) {
1074 STARTSTACKSTR(concat);
1075 ap = argv + 2;
1076 for (;;) {
1077 while (*p)
1078 STPUTC(*p++, concat);
1079 if ((p = *ap++) == NULL)
1080 break;
1081 STPUTC(' ', concat);
1082 }
1083 STPUTC('\0', concat);
1084 p = grabstackstr(concat);
1085 }
1086 evalstring(p, EV_TESTED);
1087 }
1088 return exitstatus;
1089}
1090
1091
1092/*
1093 * Execute a command or commands contained in a string.
1094 */
1095
1096static void
1097evalstring(s, flag)
1098 char *s;
1099 int flag;
1100 {
1101 union node *n;
1102 struct stackmark smark;
1103
1104 setstackmark(&smark);
1105 setinputstring(s);
1106 while ((n = parsecmd(0)) != NEOF) {
1107 evaltree(n, flag);
1108 popstackmark(&smark);
1109 }
1110 popfile();
1111 popstackmark(&smark);
1112}
1113
1114
1115
1116/*
1117 * Evaluate a parse tree. The value is left in the global variable
1118 * exitstatus.
1119 */
1120
1121static void
1122evaltree(n, flags)
1123 union node *n;
1124 int flags;
1125{
1126 int checkexit = 0;
1127 if (n == NULL) {
1128 TRACE(("evaltree(NULL) called\n"));
1129 goto out;
1130 }
1131 TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type));
1132 switch (n->type) {
1133 case NSEMI:
1134 evaltree(n->nbinary.ch1, flags & EV_TESTED);
1135 if (evalskip)
1136 goto out;
1137 evaltree(n->nbinary.ch2, flags);
1138 break;
1139 case NAND:
1140 evaltree(n->nbinary.ch1, EV_TESTED);
1141 if (evalskip || exitstatus != 0)
1142 goto out;
1143 evaltree(n->nbinary.ch2, flags);
1144 break;
1145 case NOR:
1146 evaltree(n->nbinary.ch1, EV_TESTED);
1147 if (evalskip || exitstatus == 0)
1148 goto out;
1149 evaltree(n->nbinary.ch2, flags);
1150 break;
1151 case NREDIR:
1152 expredir(n->nredir.redirect);
1153 redirect(n->nredir.redirect, REDIR_PUSH);
1154 evaltree(n->nredir.n, flags);
1155 popredir();
1156 break;
1157 case NSUBSHELL:
1158 evalsubshell(n, flags);
1159 break;
1160 case NBACKGND:
1161 evalsubshell(n, flags);
1162 break;
1163 case NIF: {
1164 evaltree(n->nif.test, EV_TESTED);
1165 if (evalskip)
1166 goto out;
1167 if (exitstatus == 0)
1168 evaltree(n->nif.ifpart, flags);
1169 else if (n->nif.elsepart)
1170 evaltree(n->nif.elsepart, flags);
1171 else
1172 exitstatus = 0;
1173 break;
1174 }
1175 case NWHILE:
1176 case NUNTIL:
1177 evalloop(n, flags);
1178 break;
1179 case NFOR:
1180 evalfor(n, flags);
1181 break;
1182 case NCASE:
1183 evalcase(n, flags);
1184 break;
1185 case NDEFUN: {
1186 struct builtincmd *bcmd;
1187 if (
1188 (bcmd = find_builtin(n->narg.text)) &&
1189 bcmd->flags & BUILTIN_SPECIAL
1190 ) {
1191 outfmt(out2, "%s is a special built-in\n", n->narg.text);
1192 exitstatus = 1;
1193 break;
1194 }
1195 defun(n->narg.text, n->narg.next);
1196 exitstatus = 0;
1197 break;
1198 }
1199 case NNOT:
1200 evaltree(n->nnot.com, EV_TESTED);
1201 exitstatus = !exitstatus;
1202 break;
1203
1204 case NPIPE:
1205 evalpipe(n);
1206 checkexit = 1;
1207 break;
1208 case NCMD:
1209#ifdef notyet
1210 evalcommand(n, flags, (struct backcmd *)NULL);
1211#else
1212 evalcommand(n, flags);
1213#endif
1214 checkexit = 1;
1215 break;
1216#ifdef DEBUG
1217 default:
1218 out1fmt("Node type = %d\n", n->type);
1219#ifndef USE_GLIBC_STDIO
1220 flushout(out1);
1221#endif
1222 break;
1223#endif
1224 }
1225out:
1226 if (pendingsigs)
1227 dotrap();
1228 if (
1229 flags & EV_EXIT ||
1230 (checkexit && eflag && exitstatus && !(flags & EV_TESTED))
1231 )
1232 exitshell(exitstatus);
1233}
1234
1235
1236static void
1237evalloop(n, flags)
1238 union node *n;
1239 int flags;
1240{
1241 int status;
1242
1243 loopnest++;
1244 status = 0;
1245 for (;;) {
1246 evaltree(n->nbinary.ch1, EV_TESTED);
1247 if (evalskip) {
1248skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
1249 evalskip = 0;
1250 continue;
1251 }
1252 if (evalskip == SKIPBREAK && --skipcount <= 0)
1253 evalskip = 0;
1254 break;
1255 }
1256 if (n->type == NWHILE) {
1257 if (exitstatus != 0)
1258 break;
1259 } else {
1260 if (exitstatus == 0)
1261 break;
1262 }
1263 evaltree(n->nbinary.ch2, flags & EV_TESTED);
1264 status = exitstatus;
1265 if (evalskip)
1266 goto skipping;
1267 }
1268 loopnest--;
1269 exitstatus = status;
1270}
1271
1272
1273
1274static void
1275evalfor(n, flags)
1276 union node *n;
1277 int flags;
1278{
1279 struct arglist arglist;
1280 union node *argp;
1281 struct strlist *sp;
1282 struct stackmark smark;
1283
1284 setstackmark(&smark);
1285 arglist.lastp = &arglist.list;
1286 for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
1287 oexitstatus = exitstatus;
1288 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
1289 if (evalskip)
1290 goto out;
1291 }
1292 *arglist.lastp = NULL;
1293
1294 exitstatus = 0;
1295 loopnest++;
1296 for (sp = arglist.list ; sp ; sp = sp->next) {
1297 setvar(n->nfor.var, sp->text, 0);
1298 evaltree(n->nfor.body, flags & EV_TESTED);
1299 if (evalskip) {
1300 if (evalskip == SKIPCONT && --skipcount <= 0) {
1301 evalskip = 0;
1302 continue;
1303 }
1304 if (evalskip == SKIPBREAK && --skipcount <= 0)
1305 evalskip = 0;
1306 break;
1307 }
1308 }
1309 loopnest--;
1310out:
1311 popstackmark(&smark);
1312}
1313
1314
1315
1316static void
1317evalcase(n, flags)
1318 union node *n;
1319 int flags;
1320{
1321 union node *cp;
1322 union node *patp;
1323 struct arglist arglist;
1324 struct stackmark smark;
1325
1326 setstackmark(&smark);
1327 arglist.lastp = &arglist.list;
1328 oexitstatus = exitstatus;
1329 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
1330 for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
1331 for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
1332 if (casematch(patp, arglist.list->text)) {
1333 if (evalskip == 0) {
1334 evaltree(cp->nclist.body, flags);
1335 }
1336 goto out;
1337 }
1338 }
1339 }
1340out:
1341 popstackmark(&smark);
1342}
1343
1344
1345
1346/*
1347 * Kick off a subshell to evaluate a tree.
1348 */
1349
1350static void
1351evalsubshell(n, flags)
1352 union node *n;
1353 int flags;
1354{
1355 struct job *jp;
1356 int backgnd = (n->type == NBACKGND);
1357
1358 expredir(n->nredir.redirect);
1359 jp = makejob(n, 1);
1360 if (forkshell(jp, n, backgnd) == 0) {
1361 if (backgnd)
1362 flags &=~ EV_TESTED;
1363 redirect(n->nredir.redirect, 0);
1364 evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
1365 }
1366 if (! backgnd) {
1367 INTOFF;
1368 exitstatus = waitforjob(jp);
1369 INTON;
1370 }
1371}
1372
1373
1374
1375/*
1376 * Compute the names of the files in a redirection list.
1377 */
1378
1379static void
1380expredir(n)
1381 union node *n;
1382{
1383 union node *redir;
1384
1385 for (redir = n ; redir ; redir = redir->nfile.next) {
1386 struct arglist fn;
1387 fn.lastp = &fn.list;
1388 oexitstatus = exitstatus;
1389 switch (redir->type) {
1390 case NFROMTO:
1391 case NFROM:
1392 case NTO:
1393 case NAPPEND:
1394 case NTOOV:
1395 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
1396 redir->nfile.expfname = fn.list->text;
1397 break;
1398 case NFROMFD:
1399 case NTOFD:
1400 if (redir->ndup.vname) {
1401 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
1402 fixredir(redir, fn.list->text, 1);
1403 }
1404 break;
1405 }
1406 }
1407}
1408
1409
1410
1411/*
1412 * Evaluate a pipeline. All the processes in the pipeline are children
1413 * of the process creating the pipeline. (This differs from some versions
1414 * of the shell, which make the last process in a pipeline the parent
1415 * of all the rest.)
1416 */
1417
1418static void
1419evalpipe(n)
1420 union node *n;
1421{
1422 struct job *jp;
1423 struct nodelist *lp;
1424 int pipelen;
1425 int prevfd;
1426 int pip[2];
1427
1428 TRACE(("evalpipe(0x%lx) called\n", (long)n));
1429 pipelen = 0;
1430 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
1431 pipelen++;
1432 INTOFF;
1433 jp = makejob(n, pipelen);
1434 prevfd = -1;
1435 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
1436 prehash(lp->n);
1437 pip[1] = -1;
1438 if (lp->next) {
1439 if (pipe(pip) < 0) {
1440 close(prevfd);
1441 error("Pipe call failed");
1442 }
1443 }
1444 if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
1445 INTON;
1446 if (prevfd > 0) {
1447 close(0);
1448 dup_as_newfd(prevfd, 0);
1449 close(prevfd);
1450 if (pip[0] == 0) {
1451 pip[0] = -1;
1452 }
1453 }
1454 if (pip[1] >= 0) {
1455 if (pip[0] >= 0) {
1456 close(pip[0]);
1457 }
1458 if (pip[1] != 1) {
1459 close(1);
1460 dup_as_newfd(pip[1], 1);
1461 close(pip[1]);
1462 }
1463 }
1464 evaltree(lp->n, EV_EXIT);
1465 }
1466 if (prevfd >= 0)
1467 close(prevfd);
1468 prevfd = pip[0];
1469 close(pip[1]);
1470 }
1471 INTON;
1472 if (n->npipe.backgnd == 0) {
1473 INTOFF;
1474 exitstatus = waitforjob(jp);
1475 TRACE(("evalpipe: job done exit status %d\n", exitstatus));
1476 INTON;
1477 }
1478}
1479
1480
1481
1482/*
1483 * Execute a command inside back quotes. If it's a builtin command, we
1484 * want to save its output in a block obtained from malloc. Otherwise
1485 * we fork off a subprocess and get the output of the command via a pipe.
1486 * Should be called with interrupts off.
1487 */
1488
1489static void
1490evalbackcmd(n, result)
1491 union node *n;
1492 struct backcmd *result;
1493{
1494 int pip[2];
1495 struct job *jp;
1496 struct stackmark smark; /* unnecessary */
1497
1498 setstackmark(&smark);
1499 result->fd = -1;
1500 result->buf = NULL;
1501 result->nleft = 0;
1502 result->jp = NULL;
1503 if (n == NULL) {
1504 exitstatus = 0;
1505 goto out;
1506 }
1507#ifdef notyet
1508 /*
1509 * For now we disable executing builtins in the same
1510 * context as the shell, because we are not keeping
1511 * enough state to recover from changes that are
1512 * supposed only to affect subshells. eg. echo "`cd /`"
1513 */
1514 if (n->type == NCMD) {
1515 exitstatus = oexitstatus;
1516 evalcommand(n, EV_BACKCMD, result);
1517 } else
1518#endif
1519 {
1520 exitstatus = 0;
1521 if (pipe(pip) < 0)
1522 error("Pipe call failed");
1523 jp = makejob(n, 1);
1524 if (forkshell(jp, n, FORK_NOJOB) == 0) {
1525 FORCEINTON;
1526 close(pip[0]);
1527 if (pip[1] != 1) {
1528 close(1);
1529 dup_as_newfd(pip[1], 1);
1530 close(pip[1]);
1531 }
1532 eflag = 0;
1533 evaltree(n, EV_EXIT);
1534 }
1535 close(pip[1]);
1536 result->fd = pip[0];
1537 result->jp = jp;
1538 }
1539out:
1540 popstackmark(&smark);
1541 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
1542 result->fd, result->buf, result->nleft, result->jp));
1543}
1544
1545
1546
1547/*
1548 * Execute a simple command.
1549 */
1550
1551static void
1552#ifdef notyet
1553evalcommand(cmd, flags, backcmd)
1554 union node *cmd;
1555 int flags;
1556 struct backcmd *backcmd;
1557#else
1558evalcommand(cmd, flags)
1559 union node *cmd;
1560 int flags;
1561#endif
1562{
1563 struct stackmark smark;
1564 union node *argp;
1565 struct arglist arglist;
1566 struct arglist varlist;
1567 char **argv;
1568 int argc;
1569 char **envp;
1570 struct strlist *sp;
1571 int mode;
1572#ifdef notyet
1573 int pip[2];
1574#endif
1575 struct cmdentry cmdentry;
1576 struct job *jp;
1577 char *volatile savecmdname;
1578 volatile struct shparam saveparam;
1579 struct localvar *volatile savelocalvars;
1580 volatile int e;
1581 char *lastarg;
1582 const char *path;
1583 const struct builtincmd *firstbltin;
1584 struct jmploc *volatile savehandler;
1585 struct jmploc jmploc;
1586#if __GNUC__
1587 /* Avoid longjmp clobbering */
1588 (void) &argv;
1589 (void) &argc;
1590 (void) &lastarg;
1591 (void) &flags;
1592#endif
1593
1594 /* First expand the arguments. */
1595 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
1596 setstackmark(&smark);
1597 arglist.lastp = &arglist.list;
1598 varlist.lastp = &varlist.list;
1599 arglist.list = 0;
1600 oexitstatus = exitstatus;
1601 exitstatus = 0;
1602 path = pathval();
1603 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
1604 expandarg(argp, &varlist, EXP_VARTILDE);
1605 }
1606 for (
1607 argp = cmd->ncmd.args; argp && !arglist.list;
1608 argp = argp->narg.next
1609 ) {
1610 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
1611 }
1612 if (argp) {
1613 struct builtincmd *bcmd;
1614 bool pseudovarflag;
1615 bcmd = find_builtin(arglist.list->text);
1616 pseudovarflag = bcmd && bcmd->flags & BUILTIN_ASSIGN;
1617 for (; argp; argp = argp->narg.next) {
1618 if (pseudovarflag && isassignment(argp->narg.text)) {
1619 expandarg(argp, &arglist, EXP_VARTILDE);
1620 continue;
1621 }
1622 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
1623 }
1624 }
1625 *arglist.lastp = NULL;
1626 *varlist.lastp = NULL;
1627 expredir(cmd->ncmd.redirect);
1628 argc = 0;
1629 for (sp = arglist.list ; sp ; sp = sp->next)
1630 argc++;
1631 argv = stalloc(sizeof (char *) * (argc + 1));
1632
1633 for (sp = arglist.list ; sp ; sp = sp->next) {
1634 TRACE(("evalcommand arg: %s\n", sp->text));
1635 *argv++ = sp->text;
1636 }
1637 *argv = NULL;
1638 lastarg = NULL;
1639 if (iflag && funcnest == 0 && argc > 0)
1640 lastarg = argv[-1];
1641 argv -= argc;
1642
1643 /* Print the command if xflag is set. */
1644 if (xflag) {
1645#ifdef FLUSHERR
1646 outc('+', &errout);
1647#else
1648 outcslow('+', &errout);
1649#endif
1650 eprintlist(varlist.list);
1651 eprintlist(arglist.list);
1652#ifdef FLUSHERR
1653 outc('\n', &errout);
1654 flushout(&errout);
1655#else
1656 outcslow('\n', &errout);
1657#endif
1658 }
1659
1660 /* Now locate the command. */
1661 if (argc == 0) {
1662 cmdentry.cmdtype = CMDBUILTIN;
1663 firstbltin = cmdentry.u.cmd = BLTINCMD;
1664 } else {
1665 const char *oldpath;
1666 int findflag = DO_ERR;
1667 int oldfindflag;
1668
1669 /*
1670 * Modify the command lookup path, if a PATH= assignment
1671 * is present
1672 */
1673 for (sp = varlist.list ; sp ; sp = sp->next)
1674 if (varequal(sp->text, defpathvar)) {
1675 path = sp->text + 5;
1676 findflag |= DO_BRUTE;
1677 }
1678 oldpath = path;
1679 oldfindflag = findflag;
1680 firstbltin = 0;
1681 for(;;) {
1682 find_command(argv[0], &cmdentry, findflag, path);
1683 if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */
1684 exitstatus = 127;
1685#ifdef FLUSHERR
1686 flushout(&errout);
1687#endif
1688 goto out;
1689 }
1690 /* implement bltin and command here */
1691 if (cmdentry.cmdtype != CMDBUILTIN) {
1692 break;
1693 }
1694 if (!firstbltin) {
1695 firstbltin = cmdentry.u.cmd;
1696 }
1697 if (cmdentry.u.cmd == BLTINCMD) {
1698 for(;;) {
1699 struct builtincmd *bcmd;
1700
1701 argv++;
1702 if (--argc == 0)
1703 goto found;
1704 if (!(bcmd = find_builtin(*argv))) {
1705 outfmt(&errout, "%s: not found\n", *argv);
1706 exitstatus = 127;
1707#ifdef FLUSHERR
1708 flushout(&errout);
1709#endif
1710 goto out;
1711 }
1712 cmdentry.u.cmd = bcmd;
1713 if (bcmd != BLTINCMD)
1714 break;
1715 }
1716 }
1717 if (cmdentry.u.cmd == COMMANDCMD) {
1718 argv++;
1719 if (--argc == 0) {
1720 goto found;
1721 }
1722 if (*argv[0] == '-') {
1723 if (!equal(argv[0], "-p")) {
1724 argv--;
1725 argc++;
1726 break;
1727 }
1728 argv++;
1729 if (--argc == 0) {
1730 goto found;
1731 }
1732 path = defpath;
1733 findflag |= DO_BRUTE;
1734 } else {
1735 path = oldpath;
1736 findflag = oldfindflag;
1737 }
1738 findflag |= DO_NOFUN;
1739 continue;
1740 }
1741found:
1742 break;
1743 }
1744 }
1745
1746 /* Fork off a child process if necessary. */
1747 if (cmd->ncmd.backgnd
1748 || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
1749#ifdef notyet
1750 || ((flags & EV_BACKCMD) != 0
1751 && (cmdentry.cmdtype != CMDBUILTIN
1752 || cmdentry.u.bcmd == DOTCMD
1753 || cmdentry.u.bcmd == EVALCMD))
1754#endif
1755 ) {
1756 jp = makejob(cmd, 1);
1757 mode = cmd->ncmd.backgnd;
1758#ifdef notyet
1759 if (flags & EV_BACKCMD) {
1760 mode = FORK_NOJOB;
1761 if (pipe(pip) < 0)
1762 error("Pipe call failed");
1763 }
1764#endif
1765 if (forkshell(jp, cmd, mode) != 0)
1766 goto parent; /* at end of routine */
1767#ifdef notyet
1768 if (flags & EV_BACKCMD) {
1769 FORCEINTON;
1770 close(pip[0]);
1771 if (pip[1] != 1) {
1772 close(1);
1773 dup_as_newfd(pip[1], 1);
1774 close(pip[1]);
1775 }
1776 }
1777#endif
1778 flags |= EV_EXIT;
1779 }
1780
1781 /* This is the child process if a fork occurred. */
1782 /* Execute the command. */
1783 if (cmdentry.cmdtype == CMDFUNCTION) {
1784#ifdef DEBUG
1785 trputs("Shell function: "); trargs(argv);
1786#endif
1787 exitstatus = oexitstatus;
1788 redirect(cmd->ncmd.redirect, REDIR_PUSH);
1789 saveparam = shellparam;
1790 shellparam.malloc = 0;
1791 shellparam.nparam = argc - 1;
1792 shellparam.p = argv + 1;
1793 INTOFF;
1794 savelocalvars = localvars;
1795 localvars = NULL;
1796 INTON;
1797 if (setjmp(jmploc.loc)) {
1798 if (exception == EXSHELLPROC) {
1799 freeparam((volatile struct shparam *)
1800 &saveparam);
1801 } else {
1802 saveparam.optind = shellparam.optind;
1803 saveparam.optoff = shellparam.optoff;
1804 freeparam(&shellparam);
1805 shellparam = saveparam;
1806 }
1807 poplocalvars();
1808 localvars = savelocalvars;
1809 handler = savehandler;
1810 longjmp(handler->loc, 1);
1811 }
1812 savehandler = handler;
1813 handler = &jmploc;
1814 for (sp = varlist.list ; sp ; sp = sp->next)
1815 mklocal(sp->text);
1816 funcnest++;
1817 evaltree(cmdentry.u.func, flags & EV_TESTED);
1818 funcnest--;
1819 INTOFF;
1820 poplocalvars();
1821 localvars = savelocalvars;
1822 saveparam.optind = shellparam.optind;
1823 saveparam.optoff = shellparam.optoff;
1824 freeparam(&shellparam);
1825 shellparam = saveparam;
1826 handler = savehandler;
1827 popredir();
1828 INTON;
1829 if (evalskip == SKIPFUNC) {
1830 evalskip = 0;
1831 skipcount = 0;
1832 }
1833 if (flags & EV_EXIT)
1834 exitshell(exitstatus);
1835 } else if (cmdentry.cmdtype == CMDBUILTIN) {
1836#ifdef DEBUG
1837 trputs("builtin command: "); trargs(argv);
1838#endif
1839 mode = (cmdentry.u.cmd == EXECCMD)? 0 : REDIR_PUSH;
1840#ifdef notyet
1841 if (flags == EV_BACKCMD) {
1842#ifdef USE_GLIBC_STDIO
1843 openmemout();
1844#else
1845 memout.nleft = 0;
1846 memout.nextc = memout.buf;
1847 memout.bufsize = 64;
1848#endif
1849 mode |= REDIR_BACKQ;
1850 }
1851#endif
1852 redirect(cmd->ncmd.redirect, mode);
1853 savecmdname = commandname;
1854 if (firstbltin->flags & BUILTIN_SPECIAL) {
1855 listsetvar(varlist.list);
1856 } else {
1857 cmdenviron = varlist.list;
1858 }
1859 e = -1;
1860 if (setjmp(jmploc.loc)) {
1861 e = exception;
1862 exitstatus = (e == EXINT)? SIGINT+128 : 2;
1863 goto cmddone;
1864 }
1865 savehandler = handler;
1866 handler = &jmploc;
1867 commandname = argv[0];
1868 argptr = argv + 1;
1869 optptr = NULL; /* initialize nextopt */
1870 exitstatus = (*cmdentry.u.cmd->builtinfunc)(argc, argv);
1871 flushall();
1872cmddone:
1873 exitstatus |= outerr(out1);
1874 out1 = &output;
1875 out2 = &errout;
1876 freestdout();
1877 cmdenviron = NULL;
1878 if (e != EXSHELLPROC) {
1879 commandname = savecmdname;
1880 if (flags & EV_EXIT)
1881 exitshell(exitstatus);
1882 }
1883 handler = savehandler;
1884 if (e != -1) {
1885 if ((e != EXERROR && e != EXEXEC)
1886 || cmdentry.u.cmd == BLTINCMD
1887 || cmdentry.u.cmd == DOTCMD
1888 || cmdentry.u.cmd == EVALCMD
1889 || cmdentry.u.cmd == EXECCMD)
1890 exraise(e);
1891 FORCEINTON;
1892 }
1893 if (cmdentry.u.cmd != EXECCMD)
1894 popredir();
1895#ifdef notyet
1896 if (flags == EV_BACKCMD) {
1897 INTOFF;
1898#ifdef USE_GLIBC_STDIO
1899 if (__closememout()) {
1900 error(
1901 "__closememout() failed: %s",
1902 strerror(errno)
1903 );
1904 }
1905#endif
1906 backcmd->buf = memout.buf;
1907#ifdef USE_GLIBC_STDIO
1908 backcmd->nleft = memout.bufsize;
1909#else
1910 backcmd->nleft = memout.nextc - memout.buf;
1911#endif
1912 memout.buf = NULL;
1913 INTON;
1914 }
1915#endif
1916 } else {
1917#ifdef DEBUG
1918 trputs("normal command: "); trargs(argv);
1919#endif
1920 redirect(cmd->ncmd.redirect, 0);
1921 clearredir();
1922 for (sp = varlist.list ; sp ; sp = sp->next)
1923 setvareq(sp->text, VEXPORT|VSTACK);
1924 envp = environment();
1925 shellexec(argv, envp, path, cmdentry.u.index);
1926 }
1927 goto out;
1928
1929parent: /* parent process gets here (if we forked) */
1930 if (mode == 0) { /* argument to fork */
1931 INTOFF;
1932 exitstatus = waitforjob(jp);
1933 INTON;
1934#ifdef notyet
1935 } else if (mode == 2) {
1936 backcmd->fd = pip[0];
1937 close(pip[1]);
1938 backcmd->jp = jp;
1939#endif
1940 }
1941
1942out:
1943 if (lastarg)
1944 setvar("_", lastarg, 0);
1945 popstackmark(&smark);
1946}
1947
1948
1949
1950/*
1951 * Search for a command. This is called before we fork so that the
1952 * location of the command will be available in the parent as well as
1953 * the child. The check for "goodname" is an overly conservative
1954 * check that the name will not be subject to expansion.
1955 */
1956
1957static void
1958prehash(n)
1959 union node *n;
1960{
1961 struct cmdentry entry;
1962
1963 if (n->type == NCMD && n->ncmd.args)
1964 if (goodname(n->ncmd.args->narg.text))
1965 find_command(n->ncmd.args->narg.text, &entry, 0,
1966 pathval());
1967}
1968
1969
1970
1971/*
1972 * Builtin commands. Builtin commands whose functions are closely
1973 * tied to evaluation are implemented here.
1974 */
1975
1976/*
1977 * No command given, or a bltin command with no arguments. Set the
1978 * specified variables.
1979 */
1980
1981int
1982bltincmd(argc, argv)
1983 int argc;
1984 char **argv;
1985{
1986 /*
1987 * Preserve exitstatus of a previous possible redirection
1988 * as POSIX mandates
1989 */
1990 return exitstatus;
1991}
1992
1993
1994/*
1995 * Handle break and continue commands. Break, continue, and return are
1996 * all handled by setting the evalskip flag. The evaluation routines
1997 * above all check this flag, and if it is set they start skipping
1998 * commands rather than executing them. The variable skipcount is
1999 * the number of loops to break/continue, or the number of function
2000 * levels to return. (The latter is always 1.) It should probably
2001 * be an error to break out of more loops than exist, but it isn't
2002 * in the standard shell so we don't make it one here.
2003 */
2004
2005static int
2006breakcmd(argc, argv)
2007 int argc;
2008 char **argv;
2009{
2010 int n = argc > 1 ? number(argv[1]) : 1;
2011
2012 if (n > loopnest)
2013 n = loopnest;
2014 if (n > 0) {
2015 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
2016 skipcount = n;
2017 }
2018 return 0;
2019}
2020
2021
2022/*
2023 * The return command.
2024 */
2025
2026static int
2027returncmd(argc, argv)
2028 int argc;
2029 char **argv;
2030{
2031 int ret = argc > 1 ? number(argv[1]) : oexitstatus;
2032
2033 if (funcnest) {
2034 evalskip = SKIPFUNC;
2035 skipcount = 1;
2036 return ret;
2037 }
2038 else {
2039 /* Do what ksh does; skip the rest of the file */
2040 evalskip = SKIPFILE;
2041 skipcount = 1;
2042 return ret;
2043 }
2044}
2045
2046
2047#ifndef BB_TRUE_FALSE
2048static int
2049false_main(argc, argv)
2050 int argc;
2051 char **argv;
2052{
2053 return 1;
2054}
2055
2056
2057static int
2058true_main(argc, argv)
2059 int argc;
2060 char **argv;
2061{
2062 return 0;
2063}
2064#endif
2065
2066static int
2067execcmd(argc, argv)
2068 int argc;
2069 char **argv;
2070{
2071 if (argc > 1) {
2072 struct strlist *sp;
2073
2074 iflag = 0; /* exit on error */
2075 mflag = 0;
2076 optschanged();
2077 for (sp = cmdenviron; sp ; sp = sp->next)
2078 setvareq(sp->text, VEXPORT|VSTACK);
2079 shellexec(argv + 1, environment(), pathval(), 0);
2080 }
2081 return 0;
2082}
2083
2084static void
2085eprintlist(struct strlist *sp)
2086{
2087 for (; sp; sp = sp->next) {
2088 outfmt(&errout, " %s",sp->text);
2089 }
2090}
2091/* $NetBSD: exec.c,v 1.32 2001/02/04 19:52:06 christos Exp $ */
2092
Eric Andersencb57d552001-06-28 07:25:16 +00002093/*
2094 * When commands are first encountered, they are entered in a hash table.
2095 * This ensures that a full path search will not have to be done for them
2096 * on each invocation.
2097 *
2098 * We should investigate converting to a linear search, even though that
2099 * would make the command name "hash" a misnomer.
2100 */
2101#define CMDTABLESIZE 31 /* should be prime */
2102#define ARB 1 /* actual size determined at run time */
2103
2104
2105
2106struct tblentry {
2107 struct tblentry *next; /* next entry in hash chain */
2108 union param param; /* definition of builtin function */
2109 short cmdtype; /* index identifying command */
2110 char rehash; /* if set, cd done since entry created */
2111 char cmdname[ARB]; /* name of command */
2112};
2113
2114
2115static struct tblentry *cmdtable[CMDTABLESIZE];
2116static int builtinloc = -1; /* index in path of %builtin, or -1 */
2117static int exerrno = 0; /* Last exec error */
2118
2119
2120static void tryexec __P((char *, char **, char **));
2121#if !defined(BSD) && !defined(linux)
2122static void execinterp __P((char **, char **));
2123#endif
2124static void printentry __P((struct tblentry *, int));
2125static void clearcmdentry __P((int));
2126static struct tblentry *cmdlookup __P((char *, int));
2127static void delete_cmd_entry __P((void));
2128#ifdef ASH_TYPE
2129static int describe_command __P((char *, int));
2130#endif
2131static int path_change __P((const char *, int *));
2132
2133
2134/*
2135 * Exec a program. Never returns. If you change this routine, you may
2136 * have to change the find_command routine as well.
2137 */
2138
2139static void
2140shellexec(argv, envp, path, idx)
2141 char **argv, **envp;
2142 const char *path;
2143 int idx;
2144{
2145 char *cmdname;
2146 int e;
2147
2148 if (strchr(argv[0], '/') != NULL) {
2149 tryexec(argv[0], argv, envp);
2150 e = errno;
2151 } else {
2152 e = ENOENT;
2153 while ((cmdname = padvance(&path, argv[0])) != NULL) {
2154 if (--idx < 0 && pathopt == NULL) {
2155 tryexec(cmdname, argv, envp);
2156 if (errno != ENOENT && errno != ENOTDIR)
2157 e = errno;
2158 }
2159 stunalloc(cmdname);
2160 }
2161 }
2162
2163 /* Map to POSIX errors */
2164 switch (e) {
2165 case EACCES:
2166 exerrno = 126;
2167 break;
2168 case ENOENT:
2169 exerrno = 127;
2170 break;
2171 default:
2172 exerrno = 2;
2173 break;
2174 }
2175 exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
2176 /* NOTREACHED */
2177}
2178
2179
2180static void
2181tryexec(cmd, argv, envp)
2182 char *cmd;
2183 char **argv;
2184 char **envp;
2185 {
2186 int e;
2187#if !defined(BSD) && !defined(linux)
2188 char *p;
2189#endif
2190
2191#ifdef SYSV
2192 do {
2193 execve(cmd, argv, envp);
2194 } while (errno == EINTR);
2195#else
2196 execve(cmd, argv, envp);
2197#endif
2198 e = errno;
2199 if (e == ENOEXEC) {
2200 INTOFF;
2201 initshellproc();
2202 setinputfile(cmd, 0);
2203 commandname = arg0 = savestr(argv[0]);
2204#if !defined(BSD) && !defined(linux)
2205 INTON;
2206 pgetc(); pungetc(); /* fill up input buffer */
2207 p = parsenextc;
2208 if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
2209 argv[0] = cmd;
2210 execinterp(argv, envp);
2211 }
2212 INTOFF;
2213#endif
2214 setparam(argv + 1);
2215 exraise(EXSHELLPROC);
2216 }
2217 errno = e;
2218}
2219
2220
2221#if !defined(BSD) && !defined(linux)
2222/*
2223 * Execute an interpreter introduced by "#!", for systems where this
2224 * feature has not been built into the kernel. If the interpreter is
2225 * the shell, return (effectively ignoring the "#!"). If the execution
2226 * of the interpreter fails, exit.
2227 *
2228 * This code peeks inside the input buffer in order to avoid actually
2229 * reading any input. It would benefit from a rewrite.
2230 */
2231
2232#define NEWARGS 5
2233
2234static void
2235execinterp(argv, envp)
2236 char **argv, **envp;
2237 {
2238 int n;
2239 char *inp;
2240 char *outp;
2241 char c;
2242 char *p;
2243 char **ap;
2244 char *newargs[NEWARGS];
2245 int i;
2246 char **ap2;
2247 char **new;
2248
2249 n = parsenleft - 2;
2250 inp = parsenextc + 2;
2251 ap = newargs;
2252 for (;;) {
2253 while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
2254 inp++;
2255 if (n < 0)
2256 goto bad;
2257 if ((c = *inp++) == '\n')
2258 break;
2259 if (ap == &newargs[NEWARGS])
2260bad: error("Bad #! line");
2261 STARTSTACKSTR(outp);
2262 do {
2263 STPUTC(c, outp);
2264 } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
2265 STPUTC('\0', outp);
2266 n++, inp--;
2267 *ap++ = grabstackstr(outp);
2268 }
2269 if (ap == newargs + 1) { /* if no args, maybe no exec is needed */
2270 p = newargs[0];
2271 for (;;) {
2272 if (equal(p, "sh") || equal(p, "ash")) {
2273 return;
2274 }
2275 while (*p != '/') {
2276 if (*p == '\0')
2277 goto break2;
2278 p++;
2279 }
2280 p++;
2281 }
2282break2:;
2283 }
2284 i = (char *)ap - (char *)newargs; /* size in bytes */
2285 if (i == 0)
2286 error("Bad #! line");
2287 for (ap2 = argv ; *ap2++ != NULL ; );
2288 new = ckmalloc(i + ((char *)ap2 - (char *)argv));
2289 ap = newargs, ap2 = new;
2290 while ((i -= sizeof (char **)) >= 0)
2291 *ap2++ = *ap++;
2292 ap = argv;
2293 while (*ap2++ = *ap++);
2294 shellexec(new, envp, pathval(), 0);
2295 /* NOTREACHED */
2296}
2297#endif
2298
2299
2300
2301/*
2302 * Do a path search. The variable path (passed by reference) should be
2303 * set to the start of the path before the first call; padvance will update
2304 * this value as it proceeds. Successive calls to padvance will return
2305 * the possible path expansions in sequence. If an option (indicated by
2306 * a percent sign) appears in the path entry then the global variable
2307 * pathopt will be set to point to it; otherwise pathopt will be set to
2308 * NULL.
2309 */
2310
2311static const char *pathopt;
2312
2313static char *
2314padvance(path, name)
2315 const char **path;
2316 const char *name;
2317 {
2318 const char *p;
2319 char *q;
2320 const char *start;
2321 int len;
2322
2323 if (*path == NULL)
2324 return NULL;
2325 start = *path;
2326 for (p = start ; *p && *p != ':' && *p != '%' ; p++);
2327 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2328 while (stackblocksize() < len)
2329 growstackblock();
2330 q = stackblock();
2331 if (p != start) {
2332 memcpy(q, start, p - start);
2333 q += p - start;
2334 *q++ = '/';
2335 }
2336 strcpy(q, name);
2337 pathopt = NULL;
2338 if (*p == '%') {
2339 pathopt = ++p;
2340 while (*p && *p != ':') p++;
2341 }
2342 if (*p == ':')
2343 *path = p + 1;
2344 else
2345 *path = NULL;
2346 return stalloc(len);
2347}
2348
2349
2350
2351/*** Command hashing code ***/
2352
2353
2354static int
2355hashcmd(argc, argv)
2356 int argc;
2357 char **argv;
2358{
2359 struct tblentry **pp;
2360 struct tblentry *cmdp;
2361 int c;
2362 int verbose;
2363 struct cmdentry entry;
2364 char *name;
2365
2366 verbose = 0;
2367 while ((c = nextopt("rv")) != '\0') {
2368 if (c == 'r') {
2369 clearcmdentry(0);
2370 return 0;
2371 } else if (c == 'v') {
2372 verbose++;
2373 }
2374 }
2375 if (*argptr == NULL) {
2376 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
2377 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
2378 if (cmdp->cmdtype != CMDBUILTIN) {
2379 printentry(cmdp, verbose);
2380 }
2381 }
2382 }
2383 return 0;
2384 }
2385 c = 0;
2386 while ((name = *argptr) != NULL) {
2387 if ((cmdp = cmdlookup(name, 0)) != NULL
2388 && (cmdp->cmdtype == CMDNORMAL
2389 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
2390 delete_cmd_entry();
2391 find_command(name, &entry, DO_ERR, pathval());
2392 if (entry.cmdtype == CMDUNKNOWN) c = 1;
2393 else if (verbose) {
2394 cmdp = cmdlookup(name, 0);
2395 if (cmdp) printentry(cmdp, verbose);
2396 flushall();
2397 }
2398 argptr++;
2399 }
2400 return c;
2401}
2402
2403
2404static void
2405printentry(cmdp, verbose)
2406 struct tblentry *cmdp;
2407 int verbose;
2408 {
2409 int idx;
2410 const char *path;
2411 char *name;
2412
2413 if (cmdp->cmdtype == CMDNORMAL) {
2414 idx = cmdp->param.index;
2415 path = pathval();
2416 do {
2417 name = padvance(&path, cmdp->cmdname);
2418 stunalloc(name);
2419 } while (--idx >= 0);
2420 out1str(name);
2421 } else if (cmdp->cmdtype == CMDBUILTIN) {
2422 out1fmt("builtin %s", cmdp->cmdname);
2423 } else if (cmdp->cmdtype == CMDFUNCTION) {
2424 out1fmt("function %s", cmdp->cmdname);
2425 if (verbose) {
2426 INTOFF;
2427 name = commandtext(cmdp->param.func);
2428 out1fmt(" %s", name);
2429 ckfree(name);
2430 INTON;
2431 }
2432#ifdef DEBUG
2433 } else {
2434 error("internal error: cmdtype %d", cmdp->cmdtype);
2435#endif
2436 }
2437 out1fmt(snlfmt, cmdp->rehash ? "*" : nullstr);
2438}
2439
2440
2441
2442/*
2443 * Resolve a command name. If you change this routine, you may have to
2444 * change the shellexec routine as well.
2445 */
2446
2447static void
2448find_command(name, entry, act, path)
2449 char *name;
2450 struct cmdentry *entry;
2451 int act;
2452 const char *path;
2453{
2454 struct tblentry *cmdp;
2455 int idx;
2456 int prev;
2457 char *fullname;
2458 struct stat statb;
2459 int e;
2460 int bltin;
2461 int firstchange;
2462 int updatetbl;
2463 bool regular;
2464 struct builtincmd *bcmd;
2465
2466 /* If name contains a slash, don't use the hash table */
2467 if (strchr(name, '/') != NULL) {
2468 if (act & DO_ABS) {
2469 while (stat(name, &statb) < 0) {
2470 #ifdef SYSV
2471 if (errno == EINTR)
2472 continue;
2473 #endif
2474 if (errno != ENOENT && errno != ENOTDIR)
2475 e = errno;
2476 entry->cmdtype = CMDUNKNOWN;
2477 entry->u.index = -1;
2478 return;
2479 }
2480 entry->cmdtype = CMDNORMAL;
2481 entry->u.index = -1;
2482 return;
2483 }
2484 entry->cmdtype = CMDNORMAL;
2485 entry->u.index = 0;
2486 return;
2487 }
2488
2489 updatetbl = 1;
2490 if (act & DO_BRUTE) {
2491 firstchange = path_change(path, &bltin);
2492 } else {
2493 bltin = builtinloc;
2494 firstchange = 9999;
2495 }
2496
2497 /* If name is in the table, and not invalidated by cd, we're done */
2498 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
2499 if (cmdp->cmdtype == CMDFUNCTION) {
2500 if (act & DO_NOFUN) {
2501 updatetbl = 0;
2502 } else {
2503 goto success;
2504 }
2505 } else if (act & DO_BRUTE) {
2506 if ((cmdp->cmdtype == CMDNORMAL &&
2507 cmdp->param.index >= firstchange) ||
2508 (cmdp->cmdtype == CMDBUILTIN &&
2509 ((builtinloc < 0 && bltin >= 0) ?
2510 bltin : builtinloc) >= firstchange)) {
2511 /* need to recompute the entry */
2512 } else {
2513 goto success;
2514 }
2515 } else {
2516 goto success;
2517 }
2518 }
2519
2520 bcmd = find_builtin(name);
2521 regular = bcmd && bcmd->flags & BUILTIN_REGULAR;
2522
2523 if (regular) {
2524 if (cmdp && (cmdp->cmdtype == CMDBUILTIN)) {
2525 goto success;
2526 }
2527 } else if (act & DO_BRUTE) {
2528 if (firstchange == 0) {
2529 updatetbl = 0;
2530 }
2531 }
2532
2533 /* If %builtin not in path, check for builtin next */
2534 if (regular || (bltin < 0 && bcmd)) {
2535builtin:
2536 if (!updatetbl) {
2537 entry->cmdtype = CMDBUILTIN;
2538 entry->u.cmd = bcmd;
2539 return;
2540 }
2541 INTOFF;
2542 cmdp = cmdlookup(name, 1);
2543 cmdp->cmdtype = CMDBUILTIN;
2544 cmdp->param.cmd = bcmd;
2545 INTON;
2546 goto success;
2547 }
2548
2549 /* We have to search path. */
2550 prev = -1; /* where to start */
2551 if (cmdp && cmdp->rehash) { /* doing a rehash */
2552 if (cmdp->cmdtype == CMDBUILTIN)
2553 prev = builtinloc;
2554 else
2555 prev = cmdp->param.index;
2556 }
2557
2558 e = ENOENT;
2559 idx = -1;
2560loop:
2561 while ((fullname = padvance(&path, name)) != NULL) {
2562 stunalloc(fullname);
2563 idx++;
2564 if (idx >= firstchange) {
2565 updatetbl = 0;
2566 }
2567 if (pathopt) {
2568 if (prefix("builtin", pathopt)) {
2569 if ((bcmd = find_builtin(name))) {
2570 goto builtin;
2571 }
2572 continue;
2573 } else if (!(act & DO_NOFUN) &&
2574 prefix("func", pathopt)) {
2575 /* handled below */
2576 } else {
2577 continue; /* ignore unimplemented options */
2578 }
2579 }
2580 /* if rehash, don't redo absolute path names */
2581 if (fullname[0] == '/' && idx <= prev &&
2582 idx < firstchange) {
2583 if (idx < prev)
2584 continue;
2585 TRACE(("searchexec \"%s\": no change\n", name));
2586 goto success;
2587 }
2588 while (stat(fullname, &statb) < 0) {
2589#ifdef SYSV
2590 if (errno == EINTR)
2591 continue;
2592#endif
2593 if (errno != ENOENT && errno != ENOTDIR)
2594 e = errno;
2595 goto loop;
2596 }
2597 e = EACCES; /* if we fail, this will be the error */
2598 if (!S_ISREG(statb.st_mode))
2599 continue;
2600 if (pathopt) { /* this is a %func directory */
2601 stalloc(strlen(fullname) + 1);
2602 readcmdfile(fullname);
2603 if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
2604 error("%s not defined in %s", name, fullname);
2605 stunalloc(fullname);
2606 goto success;
2607 }
2608#ifdef notdef
2609 if (statb.st_uid == geteuid()) {
2610 if ((statb.st_mode & 0100) == 0)
2611 goto loop;
2612 } else if (statb.st_gid == getegid()) {
2613 if ((statb.st_mode & 010) == 0)
2614 goto loop;
2615 } else {
2616 if ((statb.st_mode & 01) == 0)
2617 goto loop;
2618 }
2619#endif
2620 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
2621 /* If we aren't called with DO_BRUTE and cmdp is set, it must
2622 be a function and we're being called with DO_NOFUN */
2623 if (!updatetbl) {
2624 entry->cmdtype = CMDNORMAL;
2625 entry->u.index = idx;
2626 return;
2627 }
2628 INTOFF;
2629 cmdp = cmdlookup(name, 1);
2630 cmdp->cmdtype = CMDNORMAL;
2631 cmdp->param.index = idx;
2632 INTON;
2633 goto success;
2634 }
2635
2636 /* We failed. If there was an entry for this command, delete it */
2637 if (cmdp && updatetbl)
2638 delete_cmd_entry();
2639 if (act & DO_ERR)
2640 outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
2641 entry->cmdtype = CMDUNKNOWN;
2642 return;
2643
2644success:
2645 cmdp->rehash = 0;
2646 entry->cmdtype = cmdp->cmdtype;
2647 entry->u = cmdp->param;
2648}
2649
2650
2651
2652/*
2653 * Search the table of builtin commands.
2654 */
2655
2656struct builtincmd *
2657find_builtin(name)
2658 char *name;
2659{
2660 struct builtincmd *bp;
2661
2662 bp = bsearch( &name, builtincmds, NUMBUILTINS, sizeof(struct builtincmd),
2663 pstrcmp
2664 );
2665 return bp;
2666}
2667
2668
2669/*
2670 * Called when a cd is done. Marks all commands so the next time they
2671 * are executed they will be rehashed.
2672 */
2673
2674static void
2675hashcd() {
2676 struct tblentry **pp;
2677 struct tblentry *cmdp;
2678
2679 for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
2680 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
2681 if (cmdp->cmdtype == CMDNORMAL
2682 || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
2683 cmdp->rehash = 1;
2684 }
2685 }
2686}
2687
2688
2689
2690/*
2691 * Called before PATH is changed. The argument is the new value of PATH;
2692 * pathval() still returns the old value at this point. Called with
2693 * interrupts off.
2694 */
2695
2696static void
2697changepath(newval)
2698 const char *newval;
2699{
2700 int firstchange;
2701 int bltin;
2702
2703 firstchange = path_change(newval, &bltin);
2704 if (builtinloc < 0 && bltin >= 0)
2705 builtinloc = bltin; /* zap builtins */
2706 clearcmdentry(firstchange);
2707 builtinloc = bltin;
2708}
2709
2710
2711/*
2712 * Clear out command entries. The argument specifies the first entry in
2713 * PATH which has changed.
2714 */
2715
2716static void
2717clearcmdentry(firstchange)
2718 int firstchange;
2719{
2720 struct tblentry **tblp;
2721 struct tblentry **pp;
2722 struct tblentry *cmdp;
2723
2724 INTOFF;
2725 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
2726 pp = tblp;
2727 while ((cmdp = *pp) != NULL) {
2728 if ((cmdp->cmdtype == CMDNORMAL &&
2729 cmdp->param.index >= firstchange)
2730 || (cmdp->cmdtype == CMDBUILTIN &&
2731 builtinloc >= firstchange)) {
2732 *pp = cmdp->next;
2733 ckfree(cmdp);
2734 } else {
2735 pp = &cmdp->next;
2736 }
2737 }
2738 }
2739 INTON;
2740}
2741
2742
2743/*
2744 * Delete all functions.
2745 */
2746
2747#ifdef mkinit
2748static void deletefuncs __P((void));
2749
2750SHELLPROC {
2751 deletefuncs();
2752}
2753#endif
2754
2755static void
2756deletefuncs() {
2757 struct tblentry **tblp;
2758 struct tblentry **pp;
2759 struct tblentry *cmdp;
2760
2761 INTOFF;
2762 for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
2763 pp = tblp;
2764 while ((cmdp = *pp) != NULL) {
2765 if (cmdp->cmdtype == CMDFUNCTION) {
2766 *pp = cmdp->next;
2767 freefunc(cmdp->param.func);
2768 ckfree(cmdp);
2769 } else {
2770 pp = &cmdp->next;
2771 }
2772 }
2773 }
2774 INTON;
2775}
2776
2777
2778
2779/*
2780 * Locate a command in the command hash table. If "add" is nonzero,
2781 * add the command to the table if it is not already present. The
2782 * variable "lastcmdentry" is set to point to the address of the link
2783 * pointing to the entry, so that delete_cmd_entry can delete the
2784 * entry.
2785 */
2786
2787struct tblentry **lastcmdentry;
2788
2789
2790static struct tblentry *
2791cmdlookup(name, add)
2792 char *name;
2793 int add;
2794{
2795 int hashval;
2796 char *p;
2797 struct tblentry *cmdp;
2798 struct tblentry **pp;
2799
2800 p = name;
2801 hashval = *p << 4;
2802 while (*p)
2803 hashval += *p++;
2804 hashval &= 0x7FFF;
2805 pp = &cmdtable[hashval % CMDTABLESIZE];
2806 for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
2807 if (equal(cmdp->cmdname, name))
2808 break;
2809 pp = &cmdp->next;
2810 }
2811 if (add && cmdp == NULL) {
2812 INTOFF;
2813 cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
2814 + strlen(name) + 1);
2815 cmdp->next = NULL;
2816 cmdp->cmdtype = CMDUNKNOWN;
2817 cmdp->rehash = 0;
2818 strcpy(cmdp->cmdname, name);
2819 INTON;
2820 }
2821 lastcmdentry = pp;
2822 return cmdp;
2823}
2824
2825/*
2826 * Delete the command entry returned on the last lookup.
2827 */
2828
2829static void
2830delete_cmd_entry() {
2831 struct tblentry *cmdp;
2832
2833 INTOFF;
2834 cmdp = *lastcmdentry;
2835 *lastcmdentry = cmdp->next;
2836 ckfree(cmdp);
2837 INTON;
2838}
2839
2840
2841
2842#ifdef notdef
2843static void
2844getcmdentry(name, entry)
2845 char *name;
2846 struct cmdentry *entry;
2847 {
2848 struct tblentry *cmdp = cmdlookup(name, 0);
2849
2850 if (cmdp) {
2851 entry->u = cmdp->param;
2852 entry->cmdtype = cmdp->cmdtype;
2853 } else {
2854 entry->cmdtype = CMDUNKNOWN;
2855 entry->u.index = 0;
2856 }
2857}
2858#endif
2859
2860
2861/*
2862 * Add a new command entry, replacing any existing command entry for
2863 * the same name.
2864 */
2865
2866static void
2867addcmdentry(name, entry)
2868 char *name;
2869 struct cmdentry *entry;
2870 {
2871 struct tblentry *cmdp;
2872
2873 INTOFF;
2874 cmdp = cmdlookup(name, 1);
2875 if (cmdp->cmdtype == CMDFUNCTION) {
2876 freefunc(cmdp->param.func);
2877 }
2878 cmdp->cmdtype = entry->cmdtype;
2879 cmdp->param = entry->u;
2880 INTON;
2881}
2882
2883
2884/*
2885 * Define a shell function.
2886 */
2887
2888static void
2889defun(name, func)
2890 char *name;
2891 union node *func;
2892 {
2893 struct cmdentry entry;
2894
2895 entry.cmdtype = CMDFUNCTION;
2896 entry.u.func = copyfunc(func);
2897 addcmdentry(name, &entry);
2898}
2899
2900
2901/*
2902 * Delete a function if it exists.
2903 */
2904
2905static void
2906unsetfunc(name)
2907 char *name;
2908 {
2909 struct tblentry *cmdp;
2910
2911 if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
2912 freefunc(cmdp->param.func);
2913 delete_cmd_entry();
2914 }
2915}
2916
2917#ifdef ASH_TYPE
2918/*
2919 * Locate and print what a word is...
2920 */
2921
2922static int
2923typecmd(argc, argv)
2924 int argc;
2925 char **argv;
2926{
2927 int i;
2928 int err = 0;
2929
2930 for (i = 1; i < argc; i++) {
2931 err |= describe_command(argv[i], 1);
2932 }
2933 return err;
2934}
2935
2936static int
2937describe_command(command, verbose)
2938 char *command;
2939 int verbose;
2940{
2941 struct cmdentry entry;
2942 struct tblentry *cmdp;
2943 const struct alias *ap;
2944 const char *path = pathval();
2945
2946 if (verbose) {
2947 out1str(command);
2948 }
2949
2950 /* First look at the keywords */
2951 if (findkwd(command)) {
2952 out1str(verbose ? " is a shell keyword" : command);
2953 goto out;
2954 }
2955
2956 /* Then look at the aliases */
2957 if ((ap = lookupalias(command, 0)) != NULL) {
2958 if (verbose) {
2959 out1fmt(" is an alias for %s", ap->val);
2960 } else {
2961 printalias(ap);
2962 }
2963 goto out;
2964 }
2965
2966 /* Then check if it is a tracked alias */
2967 if ((cmdp = cmdlookup(command, 0)) != NULL) {
2968 entry.cmdtype = cmdp->cmdtype;
2969 entry.u = cmdp->param;
2970 } else {
2971 /* Finally use brute force */
2972 find_command(command, &entry, DO_ABS, path);
2973 }
2974
2975 switch (entry.cmdtype) {
2976 case CMDNORMAL: {
2977 int j = entry.u.index;
2978 char *p;
2979 if (j == -1) {
2980 p = command;
2981 } else {
2982 do {
2983 p = padvance(&path, command);
2984 stunalloc(p);
2985 } while (--j >= 0);
2986 }
2987 if (verbose) {
2988 out1fmt(
2989 " is%s %s",
2990 cmdp ? " a tracked alias for" : nullstr, p
2991 );
2992 } else {
2993 out1str(p);
2994 }
2995 break;
2996 }
2997
2998 case CMDFUNCTION:
2999 if (verbose) {
3000 out1str(" is a shell function");
3001 } else {
3002 out1str(command);
3003 }
3004 break;
3005
3006 case CMDBUILTIN:
3007 if (verbose) {
3008 out1fmt(
3009 " is a %sshell builtin",
3010 entry.u.cmd->flags & BUILTIN_SPECIAL ?
3011 "special " : nullstr
3012 );
3013 } else {
3014 out1str(command);
3015 }
3016 break;
3017
3018 default:
3019 if (verbose) {
3020 out1str(": not found\n");
3021 }
3022 return 127;
3023 }
3024
3025out:
3026 out1c('\n');
3027 return 0;
3028}
3029#endif
3030
3031static int
3032commandcmd(argc, argv)
3033 int argc;
3034 char **argv;
3035{
3036 int c;
3037 int default_path = 0;
3038 int verify_only = 0;
3039 int verbose_verify_only = 0;
3040
3041 while ((c = nextopt("pvV")) != '\0')
3042 switch (c) {
3043 case 'p':
3044 default_path = 1;
3045 break;
3046 case 'v':
3047 verify_only = 1;
3048 break;
3049 case 'V':
3050 verbose_verify_only = 1;
3051 break;
3052 default:
3053 outfmt(out2,
3054"command: nextopt returned character code 0%o\n", c);
3055 return EX_SOFTWARE;
3056 }
3057
3058 if (default_path + verify_only + verbose_verify_only > 1 ||
3059 !*argptr) {
3060 outfmt(out2,
3061"command [-p] command [arg ...]\n");
3062 outfmt(out2,
3063"command {-v|-V} command\n");
3064 return EX_USAGE;
3065 }
3066
3067#ifdef ASH_TYPE
3068 if (verify_only || verbose_verify_only) {
3069 return describe_command(*argptr, verbose_verify_only);
3070 }
3071#endif
3072
3073 return 0;
3074}
3075
3076static int
3077path_change(newval, bltin)
3078 const char *newval;
3079 int *bltin;
3080{
3081 const char *old, *new;
3082 int idx;
3083 int firstchange;
3084
3085 old = pathval();
3086 new = newval;
3087 firstchange = 9999; /* assume no change */
3088 idx = 0;
3089 *bltin = -1;
3090 for (;;) {
3091 if (*old != *new) {
3092 firstchange = idx;
3093 if ((*old == '\0' && *new == ':')
3094 || (*old == ':' && *new == '\0'))
3095 firstchange++;
3096 old = new; /* ignore subsequent differences */
3097 }
3098 if (*new == '\0')
3099 break;
3100 if (*new == '%' && *bltin < 0 && prefix("builtin", new + 1))
3101 *bltin = idx;
3102 if (*new == ':') {
3103 idx++;
3104 }
3105 new++, old++;
3106 }
3107 if (builtinloc >= 0 && *bltin < 0)
3108 firstchange = 0;
3109 return firstchange;
3110}
3111/* $NetBSD: expand.c,v 1.50 2001/02/04 19:52:06 christos Exp $ */
3112
Eric Andersencb57d552001-06-28 07:25:16 +00003113/*
3114 * Routines to expand arguments to commands. We have to deal with
3115 * backquotes, shell variables, and file metacharacters.
3116 */
3117/*
3118 * _rmescape() flags
3119 */
3120#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
3121#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
3122
3123/*
3124 * Structure specifying which parts of the string should be searched
3125 * for IFS characters.
3126 */
3127
3128struct ifsregion {
3129 struct ifsregion *next; /* next region in list */
3130 int begoff; /* offset of start of region */
3131 int endoff; /* offset of end of region */
3132 int nulonly; /* search for nul bytes only */
3133};
3134
3135
3136static char *expdest; /* output of current string */
3137struct nodelist *argbackq; /* list of back quote expressions */
3138struct ifsregion ifsfirst; /* first struct in list of ifs regions */
3139struct ifsregion *ifslastp; /* last struct in list */
3140struct arglist exparg; /* holds expanded arg list */
3141
3142static void argstr __P((char *, int));
3143static char *exptilde __P((char *, int));
3144static void expbackq __P((union node *, int, int));
3145static int subevalvar __P((char *, char *, int, int, int, int, int));
3146static char *evalvar __P((char *, int));
3147static int varisset __P((char *, int));
3148static void strtodest __P((const char *, const char *, int));
3149static void varvalue __P((char *, int, int));
3150static void recordregion __P((int, int, int));
3151static void removerecordregions __P((int));
3152static void ifsbreakup __P((char *, struct arglist *));
3153static void ifsfree __P((void));
3154static void expandmeta __P((struct strlist *, int));
3155#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
3156#define preglob(p) _rmescapes((p), RMESCAPE_ALLOC | RMESCAPE_GLOB)
3157#if !defined(GLOB_BROKEN)
3158static void addglob __P((const glob_t *));
3159#endif
3160#endif
3161#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
3162static void expmeta __P((char *, char *));
3163#endif
3164static void addfname __P((char *));
3165#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
3166static struct strlist *expsort __P((struct strlist *));
3167static struct strlist *msort __P((struct strlist *, int));
3168#endif
3169#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
3170static int patmatch __P((char *, char *, int));
3171static int patmatch2 __P((char *, char *, int));
3172#else
3173static int pmatch __P((char *, char *, int));
3174#define patmatch2 patmatch
3175#endif
3176static char *cvtnum __P((int, char *));
3177
3178extern int oexitstatus;
3179
3180/*
3181 * Expand shell variables and backquotes inside a here document.
3182 */
3183
3184static void
3185expandhere(arg, fd)
3186 union node *arg; /* the document */
3187 int fd; /* where to write the expanded version */
3188 {
3189 herefd = fd;
3190 expandarg(arg, (struct arglist *)NULL, 0);
3191 xwrite(fd, stackblock(), expdest - stackblock());
3192}
3193
3194
3195/*
3196 * Perform variable substitution and command substitution on an argument,
3197 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
3198 * perform splitting and file name expansion. When arglist is NULL, perform
3199 * here document expansion.
3200 */
3201
3202static void
3203expandarg(arg, arglist, flag)
3204 union node *arg;
3205 struct arglist *arglist;
3206 int flag;
3207{
3208 struct strlist *sp;
3209 char *p;
3210
3211 argbackq = arg->narg.backquote;
3212 STARTSTACKSTR(expdest);
3213 ifsfirst.next = NULL;
3214 ifslastp = NULL;
3215 argstr(arg->narg.text, flag);
3216 if (arglist == NULL) {
3217 return; /* here document expanded */
3218 }
3219 STPUTC('\0', expdest);
3220 p = grabstackstr(expdest);
3221 exparg.lastp = &exparg.list;
3222 /*
3223 * TODO - EXP_REDIR
3224 */
3225 if (flag & EXP_FULL) {
3226 ifsbreakup(p, &exparg);
3227 *exparg.lastp = NULL;
3228 exparg.lastp = &exparg.list;
3229 expandmeta(exparg.list, flag);
3230 } else {
3231 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
3232 rmescapes(p);
3233 sp = (struct strlist *)stalloc(sizeof (struct strlist));
3234 sp->text = p;
3235 *exparg.lastp = sp;
3236 exparg.lastp = &sp->next;
3237 }
3238 ifsfree();
3239 *exparg.lastp = NULL;
3240 if (exparg.list) {
3241 *arglist->lastp = exparg.list;
3242 arglist->lastp = exparg.lastp;
3243 }
3244}
3245
3246
3247
3248/*
3249 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
3250 * characters to allow for further processing. Otherwise treat
3251 * $@ like $* since no splitting will be performed.
3252 */
3253
3254static void
3255argstr(p, flag)
3256 char *p;
3257 int flag;
3258{
3259 char c;
3260 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
3261 int firsteq = 1;
3262
3263 if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
3264 p = exptilde(p, flag);
3265 for (;;) {
3266 switch (c = *p++) {
3267 case '\0':
3268 case CTLENDVAR: /* ??? */
3269 goto breakloop;
3270 case CTLQUOTEMARK:
3271 /* "$@" syntax adherence hack */
3272 if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
3273 break;
3274 if ((flag & EXP_FULL) != 0)
3275 STPUTC(c, expdest);
3276 break;
3277 case CTLESC:
3278 if (quotes)
3279 STPUTC(c, expdest);
3280 c = *p++;
3281 STPUTC(c, expdest);
3282 break;
3283 case CTLVAR:
3284 p = evalvar(p, flag);
3285 break;
3286 case CTLBACKQ:
3287 case CTLBACKQ|CTLQUOTE:
3288 expbackq(argbackq->n, c & CTLQUOTE, flag);
3289 argbackq = argbackq->next;
3290 break;
3291#ifdef ASH_MATH_SUPPORT
3292 case CTLENDARI:
3293 expari(flag);
3294 break;
3295#endif
3296 case ':':
3297 case '=':
3298 /*
3299 * sort of a hack - expand tildes in variable
3300 * assignments (after the first '=' and after ':'s).
3301 */
3302 STPUTC(c, expdest);
3303 if (flag & EXP_VARTILDE && *p == '~') {
3304 if (c == '=') {
3305 if (firsteq)
3306 firsteq = 0;
3307 else
3308 break;
3309 }
3310 p = exptilde(p, flag);
3311 }
3312 break;
3313 default:
3314 STPUTC(c, expdest);
3315 }
3316 }
3317breakloop:;
3318 return;
3319}
3320
3321static char *
3322exptilde(p, flag)
3323 char *p;
3324 int flag;
3325{
3326 char c, *startp = p;
3327 struct passwd *pw;
3328 const char *home;
3329 int quotes = flag & (EXP_FULL | EXP_CASE);
3330
3331 while ((c = *p) != '\0') {
3332 switch(c) {
3333 case CTLESC:
3334 return (startp);
3335 case CTLQUOTEMARK:
3336 return (startp);
3337 case ':':
3338 if (flag & EXP_VARTILDE)
3339 goto done;
3340 break;
3341 case '/':
3342 goto done;
3343 }
3344 p++;
3345 }
3346done:
3347 *p = '\0';
3348 if (*(startp+1) == '\0') {
3349 if ((home = lookupvar("HOME")) == NULL)
3350 goto lose;
3351 } else {
3352 if ((pw = getpwnam(startp+1)) == NULL)
3353 goto lose;
3354 home = pw->pw_dir;
3355 }
3356 if (*home == '\0')
3357 goto lose;
3358 *p = c;
3359 strtodest(home, SQSYNTAX, quotes);
3360 return (p);
3361lose:
3362 *p = c;
3363 return (startp);
3364}
3365
3366
3367static void
3368removerecordregions(endoff)
3369 int endoff;
3370{
3371 if (ifslastp == NULL)
3372 return;
3373
3374 if (ifsfirst.endoff > endoff) {
3375 while (ifsfirst.next != NULL) {
3376 struct ifsregion *ifsp;
3377 INTOFF;
3378 ifsp = ifsfirst.next->next;
3379 ckfree(ifsfirst.next);
3380 ifsfirst.next = ifsp;
3381 INTON;
3382 }
3383 if (ifsfirst.begoff > endoff)
3384 ifslastp = NULL;
3385 else {
3386 ifslastp = &ifsfirst;
3387 ifsfirst.endoff = endoff;
3388 }
3389 return;
3390 }
3391
3392 ifslastp = &ifsfirst;
3393 while (ifslastp->next && ifslastp->next->begoff < endoff)
3394 ifslastp=ifslastp->next;
3395 while (ifslastp->next != NULL) {
3396 struct ifsregion *ifsp;
3397 INTOFF;
3398 ifsp = ifslastp->next->next;
3399 ckfree(ifslastp->next);
3400 ifslastp->next = ifsp;
3401 INTON;
3402 }
3403 if (ifslastp->endoff > endoff)
3404 ifslastp->endoff = endoff;
3405}
3406
3407
3408#ifdef ASH_MATH_SUPPORT
3409/*
3410 * Expand arithmetic expression. Backup to start of expression,
3411 * evaluate, place result in (backed up) result, adjust string position.
3412 */
3413static void
3414expari(flag)
3415 int flag;
3416{
3417 char *p, *start;
3418 int result;
3419 int begoff;
3420 int quotes = flag & (EXP_FULL | EXP_CASE);
3421 int quoted;
3422
3423 /* ifsfree(); */
3424
3425 /*
3426 * This routine is slightly over-complicated for
3427 * efficiency. First we make sure there is
3428 * enough space for the result, which may be bigger
3429 * than the expression if we add exponentation. Next we
3430 * scan backwards looking for the start of arithmetic. If the
3431 * next previous character is a CTLESC character, then we
3432 * have to rescan starting from the beginning since CTLESC
3433 * characters have to be processed left to right.
3434 */
3435 CHECKSTRSPACE(10, expdest);
3436 USTPUTC('\0', expdest);
3437 start = stackblock();
3438 p = expdest - 1;
3439 while (*p != CTLARI && p >= start)
3440 --p;
3441 if (*p != CTLARI)
3442 error("missing CTLARI (shouldn't happen)");
3443 if (p > start && *(p-1) == CTLESC)
3444 for (p = start; *p != CTLARI; p++)
3445 if (*p == CTLESC)
3446 p++;
3447
3448 if (p[1] == '"')
3449 quoted=1;
3450 else
3451 quoted=0;
3452 begoff = p - start;
3453 removerecordregions(begoff);
3454 if (quotes)
3455 rmescapes(p+2);
3456 result = arith(p+2);
3457 fmtstr(p, 12, "%d", result);
3458
3459 while (*p++)
3460 ;
3461
3462 if (quoted == 0)
3463 recordregion(begoff, p - 1 - start, 0);
3464 result = expdest - p + 1;
3465 STADJUST(-result, expdest);
3466}
3467#endif
3468
3469
3470/*
3471 * Expand stuff in backwards quotes.
3472 */
3473
3474static void
3475expbackq(cmd, quoted, flag)
3476 union node *cmd;
3477 int quoted;
3478 int flag;
3479{
3480 volatile struct backcmd in;
3481 int i;
3482 char buf[128];
3483 char *p;
3484 char *dest = expdest;
3485 volatile struct ifsregion saveifs;
3486 struct ifsregion *volatile savelastp;
3487 struct nodelist *volatile saveargbackq;
3488 char lastc;
3489 int startloc = dest - stackblock();
3490 char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
3491 volatile int saveherefd;
3492 int quotes = flag & (EXP_FULL | EXP_CASE);
3493 struct jmploc jmploc;
3494 struct jmploc *volatile savehandler;
3495 int ex;
3496
3497#if __GNUC__
3498 /* Avoid longjmp clobbering */
3499 (void) &dest;
3500 (void) &syntax;
3501#endif
3502
3503 in.fd = -1;
3504 in.buf = 0;
3505 in.jp = 0;
3506
3507 INTOFF;
3508 saveifs = ifsfirst;
3509 savelastp = ifslastp;
3510 saveargbackq = argbackq;
3511 saveherefd = herefd;
3512 herefd = -1;
3513 if ((ex = setjmp(jmploc.loc))) {
3514 goto err1;
3515 }
3516 savehandler = handler;
3517 handler = &jmploc;
3518 INTON;
3519 p = grabstackstr(dest);
3520 evalbackcmd(cmd, (struct backcmd *) &in);
3521 ungrabstackstr(p, dest);
3522err1:
3523 INTOFF;
3524 ifsfirst = saveifs;
3525 ifslastp = savelastp;
3526 argbackq = saveargbackq;
3527 herefd = saveherefd;
3528 if (ex) {
3529 goto err2;
3530 }
3531
3532 p = in.buf;
3533 lastc = '\0';
3534 for (;;) {
3535 if (--in.nleft < 0) {
3536 if (in.fd < 0)
3537 break;
3538 while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
3539 TRACE(("expbackq: read returns %d\n", i));
3540 if (i <= 0)
3541 break;
3542 p = buf;
3543 in.nleft = i - 1;
3544 }
3545 lastc = *p++;
3546 if (lastc != '\0') {
3547 if (quotes && syntax[(int)lastc] == CCTL)
3548 STPUTC(CTLESC, dest);
3549 STPUTC(lastc, dest);
3550 }
3551 }
3552
3553 /* Eat all trailing newlines */
3554 for (; dest > stackblock() && dest[-1] == '\n';)
3555 STUNPUTC(dest);
3556
3557err2:
3558 if (in.fd >= 0)
3559 close(in.fd);
3560 if (in.buf)
3561 ckfree(in.buf);
3562 if (in.jp)
3563 exitstatus = waitforjob(in.jp);
3564 handler = savehandler;
3565 if (ex) {
3566 longjmp(handler->loc, 1);
3567 }
3568 if (quoted == 0)
3569 recordregion(startloc, dest - stackblock(), 0);
3570 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
3571 (dest - stackblock()) - startloc,
3572 (dest - stackblock()) - startloc,
3573 stackblock() + startloc));
3574 expdest = dest;
3575 INTON;
3576}
3577
3578
3579
3580static int
3581subevalvar(p, str, strloc, subtype, startloc, varflags, quotes)
3582 char *p;
3583 char *str;
3584 int strloc;
3585 int subtype;
3586 int startloc;
3587 int varflags;
3588 int quotes;
3589{
3590 char *startp;
3591 char *loc = NULL;
3592 char *q;
3593 int c = 0;
3594 int saveherefd = herefd;
3595 struct nodelist *saveargbackq = argbackq;
3596 int amount;
3597
3598 herefd = -1;
3599 argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
3600 STACKSTRNUL(expdest);
3601 herefd = saveherefd;
3602 argbackq = saveargbackq;
3603 startp = stackblock() + startloc;
3604 if (str == NULL)
3605 str = stackblock() + strloc;
3606
3607 switch (subtype) {
3608 case VSASSIGN:
3609 setvar(str, startp, 0);
3610 amount = startp - expdest;
3611 STADJUST(amount, expdest);
3612 varflags &= ~VSNUL;
3613 if (c != 0)
3614 *loc = c;
3615 return 1;
3616
3617 case VSQUESTION:
3618 if (*p != CTLENDVAR) {
3619 outfmt(&errout, snlfmt, startp);
3620 error((char *)NULL);
3621 }
3622 error("%.*s: parameter %snot set", p - str - 1,
3623 str, (varflags & VSNUL) ? "null or "
3624 : nullstr);
3625 /* NOTREACHED */
3626
3627 case VSTRIMLEFT:
3628 for (loc = startp; loc < str; loc++) {
3629 c = *loc;
3630 *loc = '\0';
3631 if (patmatch2(str, startp, quotes))
3632 goto recordleft;
3633 *loc = c;
3634 if (quotes && *loc == CTLESC)
3635 loc++;
3636 }
3637 return 0;
3638
3639 case VSTRIMLEFTMAX:
3640 for (loc = str - 1; loc >= startp;) {
3641 c = *loc;
3642 *loc = '\0';
3643 if (patmatch2(str, startp, quotes))
3644 goto recordleft;
3645 *loc = c;
3646 loc--;
3647 if (quotes && loc > startp && *(loc - 1) == CTLESC) {
3648 for (q = startp; q < loc; q++)
3649 if (*q == CTLESC)
3650 q++;
3651 if (q > loc)
3652 loc--;
3653 }
3654 }
3655 return 0;
3656
3657 case VSTRIMRIGHT:
3658 for (loc = str - 1; loc >= startp;) {
3659 if (patmatch2(str, loc, quotes))
3660 goto recordright;
3661 loc--;
3662 if (quotes && loc > startp && *(loc - 1) == CTLESC) {
3663 for (q = startp; q < loc; q++)
3664 if (*q == CTLESC)
3665 q++;
3666 if (q > loc)
3667 loc--;
3668 }
3669 }
3670 return 0;
3671
3672 case VSTRIMRIGHTMAX:
3673 for (loc = startp; loc < str - 1; loc++) {
3674 if (patmatch2(str, loc, quotes))
3675 goto recordright;
3676 if (quotes && *loc == CTLESC)
3677 loc++;
3678 }
3679 return 0;
3680
3681#ifdef DEBUG
3682 default:
3683 abort();
3684#endif
3685 }
3686
3687recordleft:
3688 *loc = c;
3689 amount = ((str - 1) - (loc - startp)) - expdest;
3690 STADJUST(amount, expdest);
3691 while (loc != str - 1)
3692 *startp++ = *loc++;
3693 return 1;
3694
3695recordright:
3696 amount = loc - expdest;
3697 STADJUST(amount, expdest);
3698 STPUTC('\0', expdest);
3699 STADJUST(-1, expdest);
3700 return 1;
3701}
3702
3703
3704/*
3705 * Expand a variable, and return a pointer to the next character in the
3706 * input string.
3707 */
3708
3709static char *
3710evalvar(p, flag)
3711 char *p;
3712 int flag;
3713{
3714 int subtype;
3715 int varflags;
3716 char *var;
3717 char *val;
3718 int patloc;
3719 int c;
3720 int set;
3721 int special;
3722 int startloc;
3723 int varlen;
3724 int easy;
3725 int quotes = flag & (EXP_FULL | EXP_CASE);
3726
3727 varflags = *p++;
3728 subtype = varflags & VSTYPE;
3729 var = p;
3730 special = 0;
3731 if (! is_name(*p))
3732 special = 1;
3733 p = strchr(p, '=') + 1;
3734again: /* jump here after setting a variable with ${var=text} */
3735 if (special) {
3736 set = varisset(var, varflags & VSNUL);
3737 val = NULL;
3738 } else {
3739 val = lookupvar(var);
3740 if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
3741 val = NULL;
3742 set = 0;
3743 } else
3744 set = 1;
3745 }
3746 varlen = 0;
3747 startloc = expdest - stackblock();
3748 if (set && subtype != VSPLUS) {
3749 /* insert the value of the variable */
3750 if (special) {
3751 varvalue(var, varflags & VSQUOTE, flag);
3752 if (subtype == VSLENGTH) {
3753 varlen = expdest - stackblock() - startloc;
3754 STADJUST(-varlen, expdest);
3755 }
3756 } else {
3757 if (subtype == VSLENGTH) {
3758 varlen = strlen(val);
3759 } else {
3760 strtodest(
3761 val,
3762 varflags & VSQUOTE ?
3763 DQSYNTAX : BASESYNTAX,
3764 quotes
3765 );
3766 }
3767 }
3768 }
3769
3770 if (subtype == VSPLUS)
3771 set = ! set;
3772
3773 easy = ((varflags & VSQUOTE) == 0 ||
3774 (*var == '@' && shellparam.nparam != 1));
3775
3776
3777 switch (subtype) {
3778 case VSLENGTH:
3779 expdest = cvtnum(varlen, expdest);
3780 goto record;
3781
3782 case VSNORMAL:
3783 if (!easy)
3784 break;
3785record:
3786 recordregion(startloc, expdest - stackblock(),
3787 varflags & VSQUOTE);
3788 break;
3789
3790 case VSPLUS:
3791 case VSMINUS:
3792 if (!set) {
3793 argstr(p, flag);
3794 break;
3795 }
3796 if (easy)
3797 goto record;
3798 break;
3799
3800 case VSTRIMLEFT:
3801 case VSTRIMLEFTMAX:
3802 case VSTRIMRIGHT:
3803 case VSTRIMRIGHTMAX:
3804 if (!set)
3805 break;
3806 /*
3807 * Terminate the string and start recording the pattern
3808 * right after it
3809 */
3810 STPUTC('\0', expdest);
3811 patloc = expdest - stackblock();
3812 if (subevalvar(p, NULL, patloc, subtype,
3813 startloc, varflags, quotes) == 0) {
3814 int amount = (expdest - stackblock() - patloc) + 1;
3815 STADJUST(-amount, expdest);
3816 }
3817 /* Remove any recorded regions beyond start of variable */
3818 removerecordregions(startloc);
3819 goto record;
3820
3821 case VSASSIGN:
3822 case VSQUESTION:
3823 if (!set) {
3824 if (subevalvar(p, var, 0, subtype, startloc,
3825 varflags, quotes)) {
3826 varflags &= ~VSNUL;
3827 /*
3828 * Remove any recorded regions beyond
3829 * start of variable
3830 */
3831 removerecordregions(startloc);
3832 goto again;
3833 }
3834 break;
3835 }
3836 if (easy)
3837 goto record;
3838 break;
3839
3840#ifdef DEBUG
3841 default:
3842 abort();
3843#endif
3844 }
3845
3846 if (subtype != VSNORMAL) { /* skip to end of alternative */
3847 int nesting = 1;
3848 for (;;) {
3849 if ((c = *p++) == CTLESC)
3850 p++;
3851 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
3852 if (set)
3853 argbackq = argbackq->next;
3854 } else if (c == CTLVAR) {
3855 if ((*p++ & VSTYPE) != VSNORMAL)
3856 nesting++;
3857 } else if (c == CTLENDVAR) {
3858 if (--nesting == 0)
3859 break;
3860 }
3861 }
3862 }
3863 return p;
3864}
3865
3866
3867
3868/*
3869 * Test whether a specialized variable is set.
3870 */
3871
3872static int
3873varisset(name, nulok)
3874 char *name;
3875 int nulok;
3876{
3877 if (*name == '!')
3878 return backgndpid != -1;
3879 else if (*name == '@' || *name == '*') {
3880 if (*shellparam.p == NULL)
3881 return 0;
3882
3883 if (nulok) {
3884 char **av;
3885
3886 for (av = shellparam.p; *av; av++)
3887 if (**av != '\0')
3888 return 1;
3889 return 0;
3890 }
3891 } else if (is_digit(*name)) {
3892 char *ap;
3893 int num = atoi(name);
3894
3895 if (num > shellparam.nparam)
3896 return 0;
3897
3898 if (num == 0)
3899 ap = arg0;
3900 else
3901 ap = shellparam.p[num - 1];
3902
3903 if (nulok && (ap == NULL || *ap == '\0'))
3904 return 0;
3905 }
3906 return 1;
3907}
3908
3909
3910
3911/*
3912 * Put a string on the stack.
3913 */
3914
3915static void
3916strtodest(p, syntax, quotes)
3917 const char *p;
3918 const char *syntax;
3919 int quotes;
3920{
3921 while (*p) {
3922 if (quotes && syntax[(int) *p] == CCTL)
3923 STPUTC(CTLESC, expdest);
3924 STPUTC(*p++, expdest);
3925 }
3926}
3927
3928
3929
3930/*
3931 * Add the value of a specialized variable to the stack string.
3932 */
3933
3934static void
3935varvalue(name, quoted, flags)
3936 char *name;
3937 int quoted;
3938 int flags;
3939{
3940 int num;
3941 char *p;
3942 int i;
3943 int sep;
3944 int sepq = 0;
3945 char **ap;
3946 char const *syntax;
3947 int allow_split = flags & EXP_FULL;
3948 int quotes = flags & (EXP_FULL | EXP_CASE);
3949
3950 syntax = quoted ? DQSYNTAX : BASESYNTAX;
3951 switch (*name) {
3952 case '$':
3953 num = rootpid;
3954 goto numvar;
3955 case '?':
3956 num = oexitstatus;
3957 goto numvar;
3958 case '#':
3959 num = shellparam.nparam;
3960 goto numvar;
3961 case '!':
3962 num = backgndpid;
3963numvar:
3964 expdest = cvtnum(num, expdest);
3965 break;
3966 case '-':
3967 for (i = 0 ; i < NOPTS ; i++) {
3968 if (optlist[i].val)
3969 STPUTC(optlist[i].letter, expdest);
3970 }
3971 break;
3972 case '@':
3973 if (allow_split && quoted) {
3974 sep = 1 << CHAR_BIT;
3975 goto param;
3976 }
3977 /* fall through */
3978 case '*':
3979 sep = ifsset() ? ifsval()[0] : ' ';
3980 if (quotes) {
3981 sepq = syntax[(int) sep] == CCTL;
3982 }
3983param:
3984 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
3985 strtodest(p, syntax, quotes);
3986 if (*ap && sep) {
3987 if (sepq)
3988 STPUTC(CTLESC, expdest);
3989 STPUTC(sep, expdest);
3990 }
3991 }
3992 break;
3993 case '0':
3994 strtodest(arg0, syntax, quotes);
3995 break;
3996 default:
3997 num = atoi(name);
3998 if (num > 0 && num <= shellparam.nparam) {
3999 strtodest(shellparam.p[num - 1], syntax, quotes);
4000 }
4001 break;
4002 }
4003}
4004
4005
4006
4007/*
4008 * Record the fact that we have to scan this region of the
4009 * string for IFS characters.
4010 */
4011
4012static void
4013recordregion(start, end, nulonly)
4014 int start;
4015 int end;
4016 int nulonly;
4017{
4018 struct ifsregion *ifsp;
4019
4020 if (ifslastp == NULL) {
4021 ifsp = &ifsfirst;
4022 } else {
4023 INTOFF;
4024 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
4025 ifsp->next = NULL;
4026 ifslastp->next = ifsp;
4027 INTON;
4028 }
4029 ifslastp = ifsp;
4030 ifslastp->begoff = start;
4031 ifslastp->endoff = end;
4032 ifslastp->nulonly = nulonly;
4033}
4034
4035
4036
4037/*
4038 * Break the argument string into pieces based upon IFS and add the
4039 * strings to the argument list. The regions of the string to be
4040 * searched for IFS characters have been stored by recordregion.
4041 */
4042static void
4043ifsbreakup(string, arglist)
4044 char *string;
4045 struct arglist *arglist;
4046 {
4047 struct ifsregion *ifsp;
4048 struct strlist *sp;
4049 char *start;
4050 char *p;
4051 char *q;
4052 const char *ifs, *realifs;
4053 int ifsspc;
4054 int nulonly;
4055
4056
4057 start = string;
4058 ifsspc = 0;
4059 nulonly = 0;
4060 realifs = ifsset() ? ifsval() : defifs;
4061 if (ifslastp != NULL) {
4062 ifsp = &ifsfirst;
4063 do {
4064 p = string + ifsp->begoff;
4065 nulonly = ifsp->nulonly;
4066 ifs = nulonly ? nullstr : realifs;
4067 ifsspc = 0;
4068 while (p < string + ifsp->endoff) {
4069 q = p;
4070 if (*p == CTLESC)
4071 p++;
4072 if (strchr(ifs, *p)) {
4073 if (!nulonly)
4074 ifsspc = (strchr(defifs, *p) != NULL);
4075 /* Ignore IFS whitespace at start */
4076 if (q == start && ifsspc) {
4077 p++;
4078 start = p;
4079 continue;
4080 }
4081 *q = '\0';
4082 sp = (struct strlist *)stalloc(sizeof *sp);
4083 sp->text = start;
4084 *arglist->lastp = sp;
4085 arglist->lastp = &sp->next;
4086 p++;
4087 if (!nulonly) {
4088 for (;;) {
4089 if (p >= string + ifsp->endoff) {
4090 break;
4091 }
4092 q = p;
4093 if (*p == CTLESC)
4094 p++;
4095 if (strchr(ifs, *p) == NULL ) {
4096 p = q;
4097 break;
4098 } else if (strchr(defifs, *p) == NULL) {
4099 if (ifsspc) {
4100 p++;
4101 ifsspc = 0;
4102 } else {
4103 p = q;
4104 break;
4105 }
4106 } else
4107 p++;
4108 }
4109 }
4110 start = p;
4111 } else
4112 p++;
4113 }
4114 } while ((ifsp = ifsp->next) != NULL);
4115 if (!(*start || (!ifsspc && start > string && nulonly))) {
4116 return;
4117 }
4118 }
4119
4120 sp = (struct strlist *)stalloc(sizeof *sp);
4121 sp->text = start;
4122 *arglist->lastp = sp;
4123 arglist->lastp = &sp->next;
4124}
4125
4126static void
4127ifsfree()
4128{
4129 while (ifsfirst.next != NULL) {
4130 struct ifsregion *ifsp;
4131 INTOFF;
4132 ifsp = ifsfirst.next->next;
4133 ckfree(ifsfirst.next);
4134 ifsfirst.next = ifsp;
4135 INTON;
4136 }
4137 ifslastp = NULL;
4138 ifsfirst.next = NULL;
4139}
4140
4141
4142
4143/*
4144 * Expand shell metacharacters. At this point, the only control characters
4145 * should be escapes. The results are stored in the list exparg.
4146 */
4147
4148#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)
4149static void
4150expandmeta(str, flag)
4151 struct strlist *str;
4152 int flag;
4153{
4154 const char *p;
4155 glob_t pglob;
4156 /* TODO - EXP_REDIR */
4157
4158 while (str) {
4159 if (fflag)
4160 goto nometa;
4161 p = preglob(str->text);
4162 INTOFF;
4163 switch (glob(p, GLOB_NOMAGIC, 0, &pglob)) {
4164 case 0:
4165 if (!(pglob.gl_flags & GLOB_MAGCHAR))
4166 goto nometa2;
4167 addglob(&pglob);
4168 globfree(&pglob);
4169 INTON;
4170 break;
4171 case GLOB_NOMATCH:
4172nometa2:
4173 globfree(&pglob);
4174 INTON;
4175nometa:
4176 *exparg.lastp = str;
4177 rmescapes(str->text);
4178 exparg.lastp = &str->next;
4179 break;
4180 default: /* GLOB_NOSPACE */
4181 error("Out of space");
4182 }
4183 str = str->next;
4184 }
4185}
4186
4187
4188/*
4189 * Add the result of glob(3) to the list.
4190 */
4191
4192static void
4193addglob(pglob)
4194 const glob_t *pglob;
4195{
4196 char **p = pglob->gl_pathv;
4197
4198 do {
4199 addfname(*p);
4200 } while (*++p);
4201}
4202
4203
4204#else /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
4205static char *expdir;
4206
4207
4208static void
4209expandmeta(str, flag)
4210 struct strlist *str;
4211 int flag;
4212{
4213 char *p;
4214 struct strlist **savelastp;
4215 struct strlist *sp;
4216 char c;
4217 /* TODO - EXP_REDIR */
4218
4219 while (str) {
4220 if (fflag)
4221 goto nometa;
4222 p = str->text;
4223 for (;;) { /* fast check for meta chars */
4224 if ((c = *p++) == '\0')
4225 goto nometa;
4226 if (c == '*' || c == '?' || c == '[' || c == '!')
4227 break;
4228 }
4229 savelastp = exparg.lastp;
4230 INTOFF;
4231 if (expdir == NULL) {
4232 int i = strlen(str->text);
4233 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
4234 }
4235
4236 expmeta(expdir, str->text);
4237 ckfree(expdir);
4238 expdir = NULL;
4239 INTON;
4240 if (exparg.lastp == savelastp) {
4241 /*
4242 * no matches
4243 */
4244nometa:
4245 *exparg.lastp = str;
4246 rmescapes(str->text);
4247 exparg.lastp = &str->next;
4248 } else {
4249 *exparg.lastp = NULL;
4250 *savelastp = sp = expsort(*savelastp);
4251 while (sp->next != NULL)
4252 sp = sp->next;
4253 exparg.lastp = &sp->next;
4254 }
4255 str = str->next;
4256 }
4257}
4258
4259
4260/*
4261 * Do metacharacter (i.e. *, ?, [...]) expansion.
4262 */
4263
4264static void
4265expmeta(enddir, name)
4266 char *enddir;
4267 char *name;
4268 {
4269 char *p;
4270 const char *cp;
4271 char *q;
4272 char *start;
4273 char *endname;
4274 int metaflag;
4275 struct stat statb;
4276 DIR *dirp;
4277 struct dirent *dp;
4278 int atend;
4279 int matchdot;
4280
4281 metaflag = 0;
4282 start = name;
4283 for (p = name ; ; p++) {
4284 if (*p == '*' || *p == '?')
4285 metaflag = 1;
4286 else if (*p == '[') {
4287 q = p + 1;
4288 if (*q == '!')
4289 q++;
4290 for (;;) {
4291 while (*q == CTLQUOTEMARK)
4292 q++;
4293 if (*q == CTLESC)
4294 q++;
4295 if (*q == '/' || *q == '\0')
4296 break;
4297 if (*++q == ']') {
4298 metaflag = 1;
4299 break;
4300 }
4301 }
4302 } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) {
4303 metaflag = 1;
4304 } else if (*p == '\0')
4305 break;
4306 else if (*p == CTLQUOTEMARK)
4307 continue;
4308 else if (*p == CTLESC)
4309 p++;
4310 if (*p == '/') {
4311 if (metaflag)
4312 break;
4313 start = p + 1;
4314 }
4315 }
4316 if (metaflag == 0) { /* we've reached the end of the file name */
4317 if (enddir != expdir)
4318 metaflag++;
4319 for (p = name ; ; p++) {
4320 if (*p == CTLQUOTEMARK)
4321 continue;
4322 if (*p == CTLESC)
4323 p++;
4324 *enddir++ = *p;
4325 if (*p == '\0')
4326 break;
4327 }
4328 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
4329 addfname(expdir);
4330 return;
4331 }
4332 endname = p;
4333 if (start != name) {
4334 p = name;
4335 while (p < start) {
4336 while (*p == CTLQUOTEMARK)
4337 p++;
4338 if (*p == CTLESC)
4339 p++;
4340 *enddir++ = *p++;
4341 }
4342 }
4343 if (enddir == expdir) {
4344 cp = ".";
4345 } else if (enddir == expdir + 1 && *expdir == '/') {
4346 cp = "/";
4347 } else {
4348 cp = expdir;
4349 enddir[-1] = '\0';
4350 }
4351 if ((dirp = opendir(cp)) == NULL)
4352 return;
4353 if (enddir != expdir)
4354 enddir[-1] = '/';
4355 if (*endname == 0) {
4356 atend = 1;
4357 } else {
4358 atend = 0;
4359 *endname++ = '\0';
4360 }
4361 matchdot = 0;
4362 p = start;
4363 while (*p == CTLQUOTEMARK)
4364 p++;
4365 if (*p == CTLESC)
4366 p++;
4367 if (*p == '.')
4368 matchdot++;
4369 while (! int_pending() && (dp = readdir(dirp)) != NULL) {
4370 if (dp->d_name[0] == '.' && ! matchdot)
4371 continue;
4372 if (patmatch(start, dp->d_name, 0)) {
4373 if (atend) {
4374 scopy(dp->d_name, enddir);
4375 addfname(expdir);
4376 } else {
4377 for (p = enddir, cp = dp->d_name;
4378 (*p++ = *cp++) != '\0';)
4379 continue;
4380 p[-1] = '/';
4381 expmeta(p, endname);
4382 }
4383 }
4384 }
4385 closedir(dirp);
4386 if (! atend)
4387 endname[-1] = '/';
4388}
4389#endif /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
4390
4391
4392/*
4393 * Add a file name to the list.
4394 */
4395
4396static void
4397addfname(name)
4398 char *name;
4399 {
4400 char *p;
4401 struct strlist *sp;
4402
4403 p = sstrdup(name);
4404 sp = (struct strlist *)stalloc(sizeof *sp);
4405 sp->text = p;
4406 *exparg.lastp = sp;
4407 exparg.lastp = &sp->next;
4408}
4409
4410
4411#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
4412/*
4413 * Sort the results of file name expansion. It calculates the number of
4414 * strings to sort and then calls msort (short for merge sort) to do the
4415 * work.
4416 */
4417
4418static struct strlist *
4419expsort(str)
4420 struct strlist *str;
4421 {
4422 int len;
4423 struct strlist *sp;
4424
4425 len = 0;
4426 for (sp = str ; sp ; sp = sp->next)
4427 len++;
4428 return msort(str, len);
4429}
4430
4431
4432static struct strlist *
4433msort(list, len)
4434 struct strlist *list;
4435 int len;
4436{
4437 struct strlist *p, *q = NULL;
4438 struct strlist **lpp;
4439 int half;
4440 int n;
4441
4442 if (len <= 1)
4443 return list;
4444 half = len >> 1;
4445 p = list;
4446 for (n = half ; --n >= 0 ; ) {
4447 q = p;
4448 p = p->next;
4449 }
4450 q->next = NULL; /* terminate first half of list */
4451 q = msort(list, half); /* sort first half of list */
4452 p = msort(p, len - half); /* sort second half */
4453 lpp = &list;
4454 for (;;) {
4455 if (strcmp(p->text, q->text) < 0) {
4456 *lpp = p;
4457 lpp = &p->next;
4458 if ((p = *lpp) == NULL) {
4459 *lpp = q;
4460 break;
4461 }
4462 } else {
4463 *lpp = q;
4464 lpp = &q->next;
4465 if ((q = *lpp) == NULL) {
4466 *lpp = p;
4467 break;
4468 }
4469 }
4470 }
4471 return list;
4472}
4473#endif
4474
4475
4476
4477/*
4478 * Returns true if the pattern matches the string.
4479 */
4480
4481#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
4482static int
4483patmatch(pattern, string, squoted)
4484 char *pattern;
4485 char *string;
4486 int squoted; /* string might have quote chars */
4487 {
4488 const char *p;
4489 char *q;
4490
4491 p = preglob(pattern);
4492 q = squoted ? _rmescapes(string, RMESCAPE_ALLOC) : string;
4493
4494 return !fnmatch(p, q, 0);
4495}
4496
4497
4498static int
4499patmatch2(pattern, string, squoted)
4500 char *pattern;
4501 char *string;
4502 int squoted; /* string might have quote chars */
4503 {
4504 char *p;
4505 int res;
4506
4507 sstrnleft--;
4508 p = grabstackstr(expdest);
4509 res = patmatch(pattern, string, squoted);
4510 ungrabstackstr(p, expdest);
4511 return res;
4512}
4513#else
4514static int
4515patmatch(pattern, string, squoted)
4516 char *pattern;
4517 char *string;
4518 int squoted; /* string might have quote chars */
4519 {
4520#ifdef notdef
4521 if (pattern[0] == '!' && pattern[1] == '!')
4522 return 1 - pmatch(pattern + 2, string);
4523 else
4524#endif
4525 return pmatch(pattern, string, squoted);
4526}
4527
4528
4529static int
4530pmatch(pattern, string, squoted)
4531 char *pattern;
4532 char *string;
4533 int squoted;
4534 {
4535 char *p, *q;
4536 char c;
4537
4538 p = pattern;
4539 q = string;
4540 for (;;) {
4541 switch (c = *p++) {
4542 case '\0':
4543 goto breakloop;
4544 case CTLESC:
4545 if (squoted && *q == CTLESC)
4546 q++;
4547 if (*q++ != *p++)
4548 return 0;
4549 break;
4550 case CTLQUOTEMARK:
4551 continue;
4552 case '?':
4553 if (squoted && *q == CTLESC)
4554 q++;
4555 if (*q++ == '\0')
4556 return 0;
4557 break;
4558 case '*':
4559 c = *p;
4560 while (c == CTLQUOTEMARK || c == '*')
4561 c = *++p;
4562 if (c != CTLESC && c != CTLQUOTEMARK &&
4563 c != '?' && c != '*' && c != '[') {
4564 while (*q != c) {
4565 if (squoted && *q == CTLESC &&
4566 q[1] == c)
4567 break;
4568 if (*q == '\0')
4569 return 0;
4570 if (squoted && *q == CTLESC)
4571 q++;
4572 q++;
4573 }
4574 }
4575 do {
4576 if (pmatch(p, q, squoted))
4577 return 1;
4578 if (squoted && *q == CTLESC)
4579 q++;
4580 } while (*q++ != '\0');
4581 return 0;
4582 case '[': {
4583 char *endp;
4584 int invert, found;
4585 char chr;
4586
4587 endp = p;
4588 if (*endp == '!')
4589 endp++;
4590 for (;;) {
4591 while (*endp == CTLQUOTEMARK)
4592 endp++;
4593 if (*endp == '\0')
4594 goto dft; /* no matching ] */
4595 if (*endp == CTLESC)
4596 endp++;
4597 if (*++endp == ']')
4598 break;
4599 }
4600 invert = 0;
4601 if (*p == '!') {
4602 invert++;
4603 p++;
4604 }
4605 found = 0;
4606 chr = *q++;
4607 if (squoted && chr == CTLESC)
4608 chr = *q++;
4609 if (chr == '\0')
4610 return 0;
4611 c = *p++;
4612 do {
4613 if (c == CTLQUOTEMARK)
4614 continue;
4615 if (c == CTLESC)
4616 c = *p++;
4617 if (*p == '-' && p[1] != ']') {
4618 p++;
4619 while (*p == CTLQUOTEMARK)
4620 p++;
4621 if (*p == CTLESC)
4622 p++;
4623 if (chr >= c && chr <= *p)
4624 found = 1;
4625 p++;
4626 } else {
4627 if (chr == c)
4628 found = 1;
4629 }
4630 } while ((c = *p++) != ']');
4631 if (found == invert)
4632 return 0;
4633 break;
4634 }
4635dft: default:
4636 if (squoted && *q == CTLESC)
4637 q++;
4638 if (*q++ != c)
4639 return 0;
4640 break;
4641 }
4642 }
4643breakloop:
4644 if (*q != '\0')
4645 return 0;
4646 return 1;
4647}
4648#endif
4649
4650
4651
4652/*
4653 * Remove any CTLESC characters from a string.
4654 */
4655
4656#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
4657static char *
4658_rmescapes(str, flag)
4659 char *str;
4660 int flag;
4661{
4662 char *p, *q, *r;
4663 static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
4664
4665 p = strpbrk(str, qchars);
4666 if (!p) {
4667 return str;
4668 }
4669 q = p;
4670 r = str;
4671 if (flag & RMESCAPE_ALLOC) {
4672 size_t len = p - str;
4673 q = r = stalloc(strlen(p) + len + 1);
4674 if (len > 0) {
4675#ifdef _GNU_SOURCE
4676 q = mempcpy(q, str, len);
4677#else
4678 memcpy(q, str, len);
4679 q += len;
4680#endif
4681 }
4682 }
4683 while (*p) {
4684 if (*p == CTLQUOTEMARK) {
4685 p++;
4686 continue;
4687 }
4688 if (*p == CTLESC) {
4689 p++;
4690 if (flag & RMESCAPE_GLOB && *p != '/') {
4691 *q++ = '\\';
4692 }
4693 }
4694 *q++ = *p++;
4695 }
4696 *q = '\0';
4697 return r;
4698}
4699#else
4700static void
4701rmescapes(str)
4702 char *str;
4703{
4704 char *p, *q;
4705
4706 p = str;
4707 while (*p != CTLESC && *p != CTLQUOTEMARK) {
4708 if (*p++ == '\0')
4709 return;
4710 }
4711 q = p;
4712 while (*p) {
4713 if (*p == CTLQUOTEMARK) {
4714 p++;
4715 continue;
4716 }
4717 if (*p == CTLESC)
4718 p++;
4719 *q++ = *p++;
4720 }
4721 *q = '\0';
4722}
4723#endif
4724
4725
4726
4727/*
4728 * See if a pattern matches in a case statement.
4729 */
4730
4731static int
4732casematch(pattern, val)
4733 union node *pattern;
4734 char *val;
4735 {
4736 struct stackmark smark;
4737 int result;
4738 char *p;
4739
4740 setstackmark(&smark);
4741 argbackq = pattern->narg.backquote;
4742 STARTSTACKSTR(expdest);
4743 ifslastp = NULL;
4744 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
4745 STPUTC('\0', expdest);
4746 p = grabstackstr(expdest);
4747 result = patmatch(p, val, 0);
4748 popstackmark(&smark);
4749 return result;
4750}
4751
4752/*
4753 * Our own itoa().
4754 */
4755
4756static char *
4757cvtnum(num, buf)
4758 int num;
4759 char *buf;
4760 {
4761 int len;
4762
4763 CHECKSTRSPACE(32, buf);
4764 len = sprintf(buf, "%d", num);
4765 STADJUST(len, buf);
4766 return buf;
4767}
4768/* $NetBSD: histedit.c,v 1.25 2001/02/04 19:52:06 christos Exp $ */
4769
Eric Andersencb57d552001-06-28 07:25:16 +00004770/*
4771 * Editline and history functions (and glue).
4772 */
4773static int histcmd(argc, argv)
4774 int argc;
4775 char **argv;
4776{
4777 error("not compiled with history support");
4778 /* NOTREACHED */
4779}
4780
4781
4782/*
4783 * This file was generated by the mkinit program.
4784 */
4785
4786extern void rmaliases __P((void));
4787
4788extern int loopnest; /* current loop nesting level */
4789
4790extern void deletefuncs __P((void));
4791
4792struct strpush {
4793 struct strpush *prev; /* preceding string on stack */
4794 char *prevstring;
4795 int prevnleft;
4796 struct alias *ap; /* if push was associated with an alias */
4797 char *string; /* remember the string since it may change */
4798};
4799
4800struct parsefile {
4801 struct parsefile *prev; /* preceding file on stack */
4802 int linno; /* current line */
4803 int fd; /* file descriptor (or -1 if string) */
4804 int nleft; /* number of chars left in this line */
4805 int lleft; /* number of chars left in this buffer */
4806 char *nextc; /* next char in buffer */
4807 char *buf; /* input buffer */
4808 struct strpush *strpush; /* for pushing strings at this level */
4809 struct strpush basestrpush; /* so pushing one is fast */
4810};
4811
4812extern int parselleft; /* copy of parsefile->lleft */
4813extern struct parsefile basepf; /* top level input file */
4814extern char basebuf[BUFSIZ]; /* buffer for top level input file */
4815
4816extern short backgndpid; /* pid of last background process */
4817extern int jobctl;
4818
4819extern int tokpushback; /* last token pushed back */
4820extern int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */
4821
4822struct redirtab {
4823 struct redirtab *next;
4824 short renamed[10];
4825};
4826
4827extern struct redirtab *redirlist;
4828
4829extern char sigmode[NSIG - 1]; /* current value of signal */
4830
4831extern char **environ;
4832
4833
4834
4835/*
4836 * Initialization code.
4837 */
4838
4839static void
4840init() {
4841
4842 /* from cd.c: */
4843 {
4844 setpwd(0, 0);
4845 }
4846
4847 /* from input.c: */
4848 {
4849 basepf.nextc = basepf.buf = basebuf;
4850 }
4851
4852 /* from output.c: */
4853 {
4854#ifdef USE_GLIBC_STDIO
4855 initstreams();
4856#endif
4857 }
4858
4859 /* from var.c: */
4860 {
4861 char **envp;
4862 char ppid[32];
4863
4864 initvar();
4865 for (envp = environ ; *envp ; envp++) {
4866 if (strchr(*envp, '=')) {
4867 setvareq(*envp, VEXPORT|VTEXTFIXED);
4868 }
4869 }
4870
4871 fmtstr(ppid, sizeof(ppid), "%d", (int) getppid());
4872 setvar("PPID", ppid, 0);
4873 }
4874}
4875
4876
4877
4878/*
4879 * This routine is called when an error or an interrupt occurs in an
4880 * interactive shell and control is returned to the main command loop.
4881 */
4882
4883static void
4884reset() {
4885
4886 /* from eval.c: */
4887 {
4888 evalskip = 0;
4889 loopnest = 0;
4890 funcnest = 0;
4891 }
4892
4893 /* from input.c: */
4894 {
4895 if (exception != EXSHELLPROC)
4896 parselleft = parsenleft = 0; /* clear input buffer */
4897 popallfiles();
4898 }
4899
4900 /* from parser.c: */
4901 {
4902 tokpushback = 0;
4903 checkkwd = 0;
4904 checkalias = 0;
4905 }
4906
4907 /* from redir.c: */
4908 {
4909 while (redirlist)
4910 popredir();
4911 }
4912
4913 /* from output.c: */
4914 {
4915 out1 = &output;
4916 out2 = &errout;
4917#ifdef USE_GLIBC_STDIO
4918 if (memout.stream != NULL)
4919 __closememout();
4920#endif
4921 if (memout.buf != NULL) {
4922 ckfree(memout.buf);
4923 memout.buf = NULL;
4924 }
4925 }
4926}
4927
4928
4929
4930/*
4931 * This routine is called to initialize the shell to run a shell procedure.
4932 */
4933
4934static void
4935initshellproc() {
4936
4937 /* from alias.c: */
4938 {
4939 rmaliases();
4940 }
4941
4942 /* from eval.c: */
4943 {
4944 exitstatus = 0;
4945 }
4946
4947 /* from exec.c: */
4948 {
4949 deletefuncs();
4950 }
4951
4952 /* from jobs.c: */
4953 {
4954 backgndpid = -1;
4955#if JOBS
4956 jobctl = 0;
4957#endif
4958 }
4959
4960 /* from options.c: */
4961 {
4962 int i;
4963
4964 for (i = 0; i < NOPTS; i++)
4965 optlist[i].val = 0;
4966 optschanged();
4967
4968 }
4969
4970 /* from redir.c: */
4971 {
4972 clearredir();
4973 }
4974
4975 /* from trap.c: */
4976 {
4977 char *sm;
4978
4979 clear_traps();
4980 for (sm = sigmode ; sm < sigmode + NSIG - 1; sm++) {
4981 if (*sm == S_IGN)
4982 *sm = S_HARD_IGN;
4983 }
4984 }
4985
4986 /* from var.c: */
4987 {
4988 shprocvar();
4989 }
4990}
4991/* $NetBSD: input.c,v 1.35 2001/02/04 19:52:06 christos Exp $ */
4992
Eric Andersencb57d552001-06-28 07:25:16 +00004993/*
4994 * This file implements the input routines used by the parser.
4995 */
4996
4997#ifdef BB_FEATURE_COMMAND_EDITING
4998unsigned int shell_context;
4999static const char * cmdedit_prompt;
5000static inline void putprompt(const char *s) {
5001 cmdedit_prompt = s;
5002}
5003#else
5004static inline void putprompt(const char *s) {
5005 out2str(s);
5006}
5007#endif
5008
5009#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
5010
5011static int plinno = 1; /* input line number */
5012static int parsenleft; /* copy of parsefile->nleft */
5013static int parselleft; /* copy of parsefile->lleft */
5014static char *parsenextc; /* copy of parsefile->nextc */
5015struct parsefile basepf; /* top level input file */
5016static char basebuf[BUFSIZ]; /* buffer for top level input file */
5017struct parsefile *parsefile = &basepf; /* current input file */
5018static int whichprompt; /* 1 == PS1, 2 == PS2 */
5019
5020static void pushfile __P((void));
5021static int preadfd __P((void));
5022
5023#ifdef mkinit
5024INCLUDE <stdio.h>
5025INCLUDE "input.h"
5026INCLUDE "error.h"
5027
5028INIT {
5029 basepf.nextc = basepf.buf = basebuf;
5030}
5031
5032RESET {
5033 if (exception != EXSHELLPROC)
5034 parselleft = parsenleft = 0; /* clear input buffer */
5035 popallfiles();
5036}
5037#endif
5038
5039
5040/*
5041 * Read a line from the script.
5042 */
5043
5044static char *
5045pfgets(line, len)
5046 char *line;
5047 int len;
5048{
5049 char *p = line;
5050 int nleft = len;
5051 int c;
5052
5053 while (--nleft > 0) {
5054 c = pgetc2();
5055 if (c == PEOF) {
5056 if (p == line)
5057 return NULL;
5058 break;
5059 }
5060 *p++ = c;
5061 if (c == '\n')
5062 break;
5063 }
5064 *p = '\0';
5065 return line;
5066}
5067
5068
5069/*
5070 * Read a character from the script, returning PEOF on end of file.
5071 * Nul characters in the input are silently discarded.
5072 */
5073
5074static int
5075pgetc()
5076{
5077 return pgetc_macro();
5078}
5079
5080
5081/*
5082 * Same as pgetc(), but ignores PEOA.
5083 */
5084
5085static int
5086pgetc2()
5087{
5088 int c;
5089 do {
5090 c = pgetc_macro();
5091 } while (c == PEOA);
5092 return c;
5093}
5094
5095
5096static int
5097preadfd()
5098{
5099 int nr;
5100 char *buf = parsefile->buf;
5101 parsenextc = buf;
5102
5103retry:
5104#ifdef BB_FEATURE_COMMAND_EDITING
5105 {
5106 if (parsefile->fd)
5107 nr = read(parsefile->fd, buf, BUFSIZ - 1);
5108 else {
5109 do {
5110 cmdedit_read_input((char*)cmdedit_prompt, buf);
5111 nr = strlen(buf);
5112 } while (nr <=0 || shell_context);
5113 cmdedit_terminate();
5114 }
5115 }
5116#else
5117 nr = read(parsefile->fd, buf, BUFSIZ - 1);
5118#endif
5119
5120 if (nr < 0) {
5121 if (errno == EINTR)
5122 goto retry;
5123 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
5124 int flags = fcntl(0, F_GETFL, 0);
5125 if (flags >= 0 && flags & O_NONBLOCK) {
5126 flags &=~ O_NONBLOCK;
5127 if (fcntl(0, F_SETFL, flags) >= 0) {
5128 out2str("sh: turning off NDELAY mode\n");
5129 goto retry;
5130 }
5131 }
5132 }
5133 }
5134 return nr;
5135}
5136
5137/*
5138 * Refill the input buffer and return the next input character:
5139 *
5140 * 1) If a string was pushed back on the input, pop it;
5141 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
5142 * from a string so we can't refill the buffer, return EOF.
5143 * 3) If the is more stuff in this buffer, use it else call read to fill it.
5144 * 4) Process input up to the next newline, deleting nul characters.
5145 */
5146
5147static int
5148preadbuffer()
5149{
5150 char *p, *q;
5151 int more;
5152 char savec;
5153
5154 while (parsefile->strpush) {
5155 if (
5156 parsenleft == -1 && parsefile->strpush->ap &&
5157 parsenextc[-1] != ' ' && parsenextc[-1] != '\t'
5158 ) {
5159 return PEOA;
5160 }
5161 popstring();
5162 if (--parsenleft >= 0)
5163 return (*parsenextc++);
5164 }
5165 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
5166 return PEOF;
5167 flushout(&output);
5168#ifdef FLUSHERR
5169 flushout(&errout);
5170#endif
5171
5172again:
5173 if (parselleft <= 0) {
5174 if ((parselleft = preadfd()) <= 0) {
5175 parselleft = parsenleft = EOF_NLEFT;
5176 return PEOF;
5177 }
5178 }
5179
5180 q = p = parsenextc;
5181
5182 /* delete nul characters */
5183 for (more = 1; more;) {
5184 switch (*p) {
5185 case '\0':
5186 p++; /* Skip nul */
5187 goto check;
5188
5189
5190 case '\n':
5191 parsenleft = q - parsenextc;
5192 more = 0; /* Stop processing here */
5193 break;
5194 }
5195
5196 *q++ = *p++;
5197check:
5198 if (--parselleft <= 0 && more) {
5199 parsenleft = q - parsenextc - 1;
5200 if (parsenleft < 0)
5201 goto again;
5202 more = 0;
5203 }
5204 }
5205
5206 savec = *q;
5207 *q = '\0';
5208
5209 if (vflag) {
5210 out2str(parsenextc);
5211#ifdef FLUSHERR
5212 flushout(out2);
5213#endif
5214 }
5215
5216 *q = savec;
5217
5218 return *parsenextc++;
5219}
5220
5221/*
5222 * Undo the last call to pgetc. Only one character may be pushed back.
5223 * PEOF may be pushed back.
5224 */
5225
5226static void
5227pungetc() {
5228 parsenleft++;
5229 parsenextc--;
5230}
5231
5232/*
5233 * Push a string back onto the input at this current parsefile level.
5234 * We handle aliases this way.
5235 */
5236static void
5237pushstring(s, len, ap)
5238 char *s;
5239 int len;
5240 void *ap;
5241 {
5242 struct strpush *sp;
5243
5244 INTOFF;
5245/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
5246 if (parsefile->strpush) {
5247 sp = ckmalloc(sizeof (struct strpush));
5248 sp->prev = parsefile->strpush;
5249 parsefile->strpush = sp;
5250 } else
5251 sp = parsefile->strpush = &(parsefile->basestrpush);
5252 sp->prevstring = parsenextc;
5253 sp->prevnleft = parsenleft;
5254 sp->ap = (struct alias *)ap;
5255 if (ap) {
5256 ((struct alias *)ap)->flag |= ALIASINUSE;
5257 sp->string = s;
5258 }
5259 parsenextc = s;
5260 parsenleft = len;
5261 INTON;
5262}
5263
5264static void
5265popstring()
5266{
5267 struct strpush *sp = parsefile->strpush;
5268
5269 INTOFF;
5270 if (sp->ap) {
5271 if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
5272 if (!checkalias) {
5273 checkalias = 1;
5274 }
5275 }
5276 if (sp->string != sp->ap->val) {
5277 ckfree(sp->string);
5278 }
5279 sp->ap->flag &= ~ALIASINUSE;
5280 if (sp->ap->flag & ALIASDEAD) {
5281 unalias(sp->ap->name);
5282 }
5283 }
5284 parsenextc = sp->prevstring;
5285 parsenleft = sp->prevnleft;
5286/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
5287 parsefile->strpush = sp->prev;
5288 if (sp != &(parsefile->basestrpush))
5289 ckfree(sp);
5290 INTON;
5291}
5292
5293/*
5294 * Set the input to take input from a file. If push is set, push the
5295 * old input onto the stack first.
5296 */
5297
5298static void
5299setinputfile(fname, push)
5300 const char *fname;
5301 int push;
5302{
5303 int fd;
5304 int myfileno2;
5305
5306 INTOFF;
5307 if ((fd = open(fname, O_RDONLY)) < 0)
5308 error("Can't open %s", fname);
5309 if (fd < 10) {
5310 myfileno2 = dup_as_newfd(fd, 10);
5311 close(fd);
5312 if (myfileno2 < 0)
5313 error("Out of file descriptors");
5314 fd = myfileno2;
5315 }
5316 setinputfd(fd, push);
5317 INTON;
5318}
5319
5320
5321/*
5322 * Like setinputfile, but takes an open file descriptor. Call this with
5323 * interrupts off.
5324 */
5325
5326static void
5327setinputfd(fd, push)
5328 int fd, push;
5329{
5330 (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
5331 if (push) {
5332 pushfile();
5333 parsefile->buf = 0;
5334 } else {
5335 closescript();
5336 while (parsefile->strpush)
5337 popstring();
5338 }
5339 parsefile->fd = fd;
5340 if (parsefile->buf == NULL)
5341 parsefile->buf = ckmalloc(BUFSIZ);
5342 parselleft = parsenleft = 0;
5343 plinno = 1;
5344}
5345
5346
5347/*
5348 * Like setinputfile, but takes input from a string.
5349 */
5350
5351static void
5352setinputstring(string)
5353 char *string;
5354 {
5355 INTOFF;
5356 pushfile();
5357 parsenextc = string;
5358 parsenleft = strlen(string);
5359 parsefile->buf = NULL;
5360 plinno = 1;
5361 INTON;
5362}
5363
5364
5365
5366/*
5367 * To handle the "." command, a stack of input files is used. Pushfile
5368 * adds a new entry to the stack and popfile restores the previous level.
5369 */
5370
5371static void
5372pushfile() {
5373 struct parsefile *pf;
5374
5375 parsefile->nleft = parsenleft;
5376 parsefile->lleft = parselleft;
5377 parsefile->nextc = parsenextc;
5378 parsefile->linno = plinno;
5379 pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
5380 pf->prev = parsefile;
5381 pf->fd = -1;
5382 pf->strpush = NULL;
5383 pf->basestrpush.prev = NULL;
5384 parsefile = pf;
5385}
5386
5387
5388static void
5389popfile() {
5390 struct parsefile *pf = parsefile;
5391
5392 INTOFF;
5393 if (pf->fd >= 0)
5394 close(pf->fd);
5395 if (pf->buf)
5396 ckfree(pf->buf);
5397 while (pf->strpush)
5398 popstring();
5399 parsefile = pf->prev;
5400 ckfree(pf);
5401 parsenleft = parsefile->nleft;
5402 parselleft = parsefile->lleft;
5403 parsenextc = parsefile->nextc;
5404 plinno = parsefile->linno;
5405 INTON;
5406}
5407
5408
5409/*
5410 * Return to top level.
5411 */
5412
5413static void
5414popallfiles() {
5415 while (parsefile != &basepf)
5416 popfile();
5417}
5418
5419
5420
5421/*
5422 * Close the file(s) that the shell is reading commands from. Called
5423 * after a fork is done.
5424 */
5425
5426static void
5427closescript() {
5428 popallfiles();
5429 if (parsefile->fd > 0) {
5430 close(parsefile->fd);
5431 parsefile->fd = 0;
5432 }
5433}
5434/* $NetBSD: jobs.c,v 1.36 2000/05/22 10:18:47 elric Exp $ */
5435
Eric Andersencb57d552001-06-28 07:25:16 +00005436
5437struct job *jobtab; /* array of jobs */
5438static int njobs; /* size of array */
5439short backgndpid = -1; /* pid of last background process */
5440#if JOBS
5441static int initialpgrp; /* pgrp of shell on invocation */
5442short curjob; /* current job */
5443#endif
5444static int intreceived;
5445
5446static void restartjob __P((struct job *));
5447static void freejob __P((struct job *));
5448static struct job *getjob __P((char *));
5449static int dowait __P((int, struct job *));
5450#ifdef SYSV
5451static int onsigchild __P((void));
5452#endif
5453static int waitproc __P((int, int *));
5454static void cmdtxt __P((union node *));
5455static void cmdputs __P((const char *));
5456static void waitonint(int);
5457
5458
5459#if JOBS
5460/*
5461 * Turn job control on and off.
5462 *
5463 * Note: This code assumes that the third arg to ioctl is a character
5464 * pointer, which is true on Berkeley systems but not System V. Since
5465 * System V doesn't have job control yet, this isn't a problem now.
5466 */
5467
5468static int jobctl;
5469
5470static void setjobctl(int enable)
5471{
5472#ifdef OLD_TTY_DRIVER
5473 int ldisc;
5474#endif
5475
5476 if (enable == jobctl || rootshell == 0)
5477 return;
5478 if (enable) {
5479 do { /* while we are in the background */
5480#ifdef OLD_TTY_DRIVER
5481 if (ioctl(fileno2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
5482#else
5483 initialpgrp = tcgetpgrp(fileno2);
5484 if (initialpgrp < 0) {
5485#endif
5486 out2str("sh: can't access tty; job cenabletrol turned off\n");
5487 mflag = 0;
5488 return;
5489 }
5490 if (initialpgrp == -1)
5491 initialpgrp = getpgrp();
5492 else if (initialpgrp != getpgrp()) {
5493 killpg(initialpgrp, SIGTTIN);
5494 continue;
5495 }
5496 } while (0);
5497#ifdef OLD_TTY_DRIVER
5498 if (ioctl(fileno2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
5499 out2str("sh: need new tty driver to run job cenabletrol; job cenabletrol turned off\n");
5500 mflag = 0;
5501 return;
5502 }
5503#endif
5504 setsignal(SIGTSTP);
5505 setsignal(SIGTTOU);
5506 setsignal(SIGTTIN);
5507 setpgid(0, rootpid);
5508#ifdef OLD_TTY_DRIVER
5509 ioctl(fileno2, TIOCSPGRP, (char *)&rootpid);
5510#else
5511 tcsetpgrp(fileno2, rootpid);
5512#endif
5513 } else { /* turning job cenabletrol off */
5514 setpgid(0, initialpgrp);
5515#ifdef OLD_TTY_DRIVER
5516 ioctl(fileno2, TIOCSPGRP, (char *)&initialpgrp);
5517#else
5518 tcsetpgrp(fileno2, initialpgrp);
5519#endif
5520 setsignal(SIGTSTP);
5521 setsignal(SIGTTOU);
5522 setsignal(SIGTTIN);
5523 }
5524 jobctl = enable;
5525}
5526#endif
5527
5528
5529#ifdef mkinit
5530INCLUDE <stdlib.h>
5531
5532SHELLPROC {
5533 backgndpid = -1;
5534#if JOBS
5535 jobctl = 0;
5536#endif
5537}
5538
5539#endif
5540
5541
5542/* This file was automatically created by ./mksignames.
5543 Do not edit. Edit support/mksignames.c instead. */
5544
5545/* A translation list so we can be polite to our users. */
5546static char *signal_names[NSIG + 2] = {
5547 "EXIT",
5548 "SIGHUP",
5549 "SIGINT",
5550 "SIGQUIT",
5551 "SIGILL",
5552 "SIGTRAP",
5553 "SIGABRT",
5554 "SIGBUS",
5555 "SIGFPE",
5556 "SIGKILL",
5557 "SIGUSR1",
5558 "SIGSEGV",
5559 "SIGUSR2",
5560 "SIGPIPE",
5561 "SIGALRM",
5562 "SIGTERM",
5563 "SIGJUNK(16)",
5564 "SIGCHLD",
5565 "SIGCONT",
5566 "SIGSTOP",
5567 "SIGTSTP",
5568 "SIGTTIN",
5569 "SIGTTOU",
5570 "SIGURG",
5571 "SIGXCPU",
5572 "SIGXFSZ",
5573 "SIGVTALRM",
5574 "SIGPROF",
5575 "SIGWINCH",
5576 "SIGIO",
5577 "SIGPWR",
5578 "SIGSYS",
5579 "SIGRTMIN",
5580 "SIGRTMIN+1",
5581 "SIGRTMIN+2",
5582 "SIGRTMIN+3",
5583 "SIGRTMIN+4",
5584 "SIGRTMIN+5",
5585 "SIGRTMIN+6",
5586 "SIGRTMIN+7",
5587 "SIGRTMIN+8",
5588 "SIGRTMIN+9",
5589 "SIGRTMIN+10",
5590 "SIGRTMIN+11",
5591 "SIGRTMIN+12",
5592 "SIGRTMIN+13",
5593 "SIGRTMIN+14",
5594 "SIGRTMIN+15",
5595 "SIGRTMAX-15",
5596 "SIGRTMAX-14",
5597 "SIGRTMAX-13",
5598 "SIGRTMAX-12",
5599 "SIGRTMAX-11",
5600 "SIGRTMAX-10",
5601 "SIGRTMAX-9",
5602 "SIGRTMAX-8",
5603 "SIGRTMAX-7",
5604 "SIGRTMAX-6",
5605 "SIGRTMAX-5",
5606 "SIGRTMAX-4",
5607 "SIGRTMAX-3",
5608 "SIGRTMAX-2",
5609 "SIGRTMAX-1",
5610 "SIGRTMAX",
5611 "DEBUG",
5612 (char *)0x0,
5613};
5614
5615
5616
5617#if JOBS
5618static int
5619killcmd(argc, argv)
5620 int argc;
5621 char **argv;
5622{
5623 int signo = -1;
5624 int list = 0;
5625 int i;
5626 pid_t pid;
5627 struct job *jp;
5628
5629 if (argc <= 1) {
5630usage:
5631 error(
5632"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
5633"kill -l [exitstatus]"
5634 );
5635 }
5636
5637 if (*argv[1] == '-') {
5638 signo = decode_signal(argv[1] + 1, 1);
5639 if (signo < 0) {
5640 int c;
5641
5642 while ((c = nextopt("ls:")) != '\0')
5643 switch (c) {
5644 case 'l':
5645 list = 1;
5646 break;
5647 case 's':
5648 signo = decode_signal(optionarg, 1);
5649 if (signo < 0) {
5650 error(
5651 "invalid signal number or name: %s",
5652 optionarg
5653 );
5654 }
5655 break;
5656#ifdef DEBUG
5657 default:
5658 error(
5659 "nextopt returned character code 0%o", c);
5660#endif
5661 }
5662 } else
5663 argptr++;
5664 }
5665
5666 if (!list && signo < 0)
5667 signo = SIGTERM;
5668
5669 if ((signo < 0 || !*argptr) ^ list) {
5670 goto usage;
5671 }
5672
5673 if (list) {
5674 if (!*argptr) {
5675 out1str("0\n");
5676 for (i = 1; i < NSIG; i++) {
5677 out1fmt(snlfmt, signal_names[i] + 3);
5678 }
5679 return 0;
5680 }
5681 signo = atoi(*argptr);
5682 if (signo > 128)
5683 signo -= 128;
5684 if (0 < signo && signo < NSIG)
5685 out1fmt(snlfmt, signal_names[signo] + 3);
5686 else
5687 error("invalid signal number or exit status: %s",
5688 *argptr);
5689 return 0;
5690 }
5691
5692 do {
5693 if (**argptr == '%') {
5694 jp = getjob(*argptr);
5695 if (jp->jobctl == 0)
5696 error("job %s not created under job control",
5697 *argptr);
5698 pid = -jp->ps[0].pid;
5699 } else
5700 pid = atoi(*argptr);
5701 if (kill(pid, signo) != 0)
5702 error("%s: %s", *argptr, strerror(errno));
5703 } while (*++argptr);
5704
5705 return 0;
5706}
5707
5708static int
5709fgcmd(argc, argv)
5710 int argc;
5711 char **argv;
5712{
5713 struct job *jp;
5714 int pgrp;
5715 int status;
5716
5717 jp = getjob(argv[1]);
5718 if (jp->jobctl == 0)
5719 error("job not created under job control");
5720 pgrp = jp->ps[0].pid;
5721#ifdef OLD_TTY_DRIVER
5722 ioctl(fileno2, TIOCSPGRP, (char *)&pgrp);
5723#else
5724 tcsetpgrp(fileno2, pgrp);
5725#endif
5726 restartjob(jp);
5727 INTOFF;
5728 status = waitforjob(jp);
5729 INTON;
5730 return status;
5731}
5732
5733
5734static int
5735bgcmd(argc, argv)
5736 int argc;
5737 char **argv;
5738{
5739 struct job *jp;
5740
5741 do {
5742 jp = getjob(*++argv);
5743 if (jp->jobctl == 0)
5744 error("job not created under job control");
5745 restartjob(jp);
5746 } while (--argc > 1);
5747 return 0;
5748}
5749
5750
5751static void
5752restartjob(jp)
5753 struct job *jp;
5754{
5755 struct procstat *ps;
5756 int i;
5757
5758 if (jp->state == JOBDONE)
5759 return;
5760 INTOFF;
5761 killpg(jp->ps[0].pid, SIGCONT);
5762 for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
5763 if (WIFSTOPPED(ps->status)) {
5764 ps->status = -1;
5765 jp->state = 0;
5766 }
5767 }
5768 INTON;
5769}
5770#endif
5771
5772
5773static int
5774jobscmd(argc, argv)
5775 int argc;
5776 char **argv;
5777{
5778 showjobs(0);
5779 return 0;
5780}
5781
5782
5783/*
5784 * Print a list of jobs. If "change" is nonzero, only print jobs whose
5785 * statuses have changed since the last call to showjobs.
5786 *
5787 * If the shell is interrupted in the process of creating a job, the
5788 * result may be a job structure containing zero processes. Such structures
5789 * will be freed here.
5790 */
5791
5792static void
5793showjobs(change)
5794 int change;
5795{
5796 int jobno;
5797 int procno;
5798 int i;
5799 struct job *jp;
5800 struct procstat *ps;
5801 int col;
5802 char s[64];
5803
5804 TRACE(("showjobs(%d) called\n", change));
5805 while (dowait(0, (struct job *)NULL) > 0);
5806 for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
5807 if (! jp->used)
5808 continue;
5809 if (jp->nprocs == 0) {
5810 freejob(jp);
5811 continue;
5812 }
5813 if (change && ! jp->changed)
5814 continue;
5815 procno = jp->nprocs;
5816 for (ps = jp->ps ; ; ps++) { /* for each process */
5817 if (ps == jp->ps)
5818 fmtstr(s, 64, "[%d] %ld ", jobno,
5819 (long)ps->pid);
5820 else
5821 fmtstr(s, 64, " %ld ",
5822 (long)ps->pid);
5823 out1str(s);
5824 col = strlen(s);
5825 s[0] = '\0';
5826 if (ps->status == -1) {
5827 /* don't print anything */
5828 } else if (WIFEXITED(ps->status)) {
5829 fmtstr(s, 64, "Exit %d",
5830 WEXITSTATUS(ps->status));
5831 } else {
5832#if JOBS
5833 if (WIFSTOPPED(ps->status))
5834 i = WSTOPSIG(ps->status);
5835 else /* WIFSIGNALED(ps->status) */
5836#endif
5837 i = WTERMSIG(ps->status);
5838 if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F])
5839 scopy(sys_siglist[i & 0x7F], s);
5840 else
5841 fmtstr(s, 64, "Signal %d", i & 0x7F);
5842 if (WCOREDUMP(ps->status))
5843 strcat(s, " (core dumped)");
5844 }
5845 out1str(s);
5846 col += strlen(s);
5847 out1fmt(
5848 "%*c%s\n", 30 - col >= 0 ? 30 - col : 0, ' ',
5849 ps->cmd
5850 );
5851 if (--procno <= 0)
5852 break;
5853 }
5854 jp->changed = 0;
5855 if (jp->state == JOBDONE) {
5856 freejob(jp);
5857 }
5858 }
5859}
5860
5861
5862/*
5863 * Mark a job structure as unused.
5864 */
5865
5866static void
5867freejob(jp)
5868 struct job *jp;
5869 {
5870 struct procstat *ps;
5871 int i;
5872
5873 INTOFF;
5874 for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
5875 if (ps->cmd != nullstr)
5876 ckfree(ps->cmd);
5877 }
5878 if (jp->ps != &jp->ps0)
5879 ckfree(jp->ps);
5880 jp->used = 0;
5881#if JOBS
5882 if (curjob == jp - jobtab + 1)
5883 curjob = 0;
5884#endif
5885 INTON;
5886}
5887
5888
5889
5890static int
5891waitcmd(argc, argv)
5892 int argc;
5893 char **argv;
5894{
5895 struct job *job;
5896 int status, retval;
5897 struct job *jp;
5898
5899 if (--argc > 0) {
5900start:
5901 job = getjob(*++argv);
5902 } else {
5903 job = NULL;
5904 }
5905 for (;;) { /* loop until process terminated or stopped */
5906 if (job != NULL) {
5907 if (job->state) {
5908 status = job->ps[job->nprocs - 1].status;
5909 if (! iflag)
5910 freejob(job);
5911 if (--argc) {
5912 goto start;
5913 }
5914 if (WIFEXITED(status))
5915 retval = WEXITSTATUS(status);
5916#if JOBS
5917 else if (WIFSTOPPED(status))
5918 retval = WSTOPSIG(status) + 128;
5919#endif
5920 else {
5921 /* XXX: limits number of signals */
5922 retval = WTERMSIG(status) + 128;
5923 }
5924 return retval;
5925 }
5926 } else {
5927 for (jp = jobtab ; ; jp++) {
5928 if (jp >= jobtab + njobs) { /* no running procs */
5929 return 0;
5930 }
5931 if (jp->used && jp->state == 0)
5932 break;
5933 }
5934 }
5935 if (dowait(2, 0) < 0 && errno == EINTR) {
5936 return 129;
5937 }
5938 }
5939}
5940
5941
5942
5943/*
5944 * Convert a job name to a job structure.
5945 */
5946
5947static struct job *
5948getjob(name)
5949 char *name;
5950 {
5951 int jobno;
5952 struct job *jp;
5953 int pid;
5954 int i;
5955
5956 if (name == NULL) {
5957#if JOBS
5958currentjob:
5959 if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0)
5960 error("No current job");
5961 return &jobtab[jobno - 1];
5962#else
5963 error("No current job");
5964#endif
5965 } else if (name[0] == '%') {
5966 if (is_digit(name[1])) {
5967 jobno = number(name + 1);
5968 if (jobno > 0 && jobno <= njobs
5969 && jobtab[jobno - 1].used != 0)
5970 return &jobtab[jobno - 1];
5971#if JOBS
5972 } else if (name[1] == '%' && name[2] == '\0') {
5973 goto currentjob;
5974#endif
5975 } else {
5976 struct job *found = NULL;
5977 for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
5978 if (jp->used && jp->nprocs > 0
5979 && prefix(name + 1, jp->ps[0].cmd)) {
5980 if (found)
5981 error("%s: ambiguous", name);
5982 found = jp;
5983 }
5984 }
5985 if (found)
5986 return found;
5987 }
5988 } else if (is_number(name)) {
5989 pid = number(name);
5990 for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
5991 if (jp->used && jp->nprocs > 0
5992 && jp->ps[jp->nprocs - 1].pid == pid)
5993 return jp;
5994 }
5995 }
5996 error("No such job: %s", name);
5997 /* NOTREACHED */
5998}
5999
6000
6001
6002/*
6003 * Return a new job structure,
6004 */
6005
6006struct job *
6007makejob(node, nprocs)
6008 union node *node;
6009 int nprocs;
6010{
6011 int i;
6012 struct job *jp;
6013
6014 for (i = njobs, jp = jobtab ; ; jp++) {
6015 if (--i < 0) {
6016 INTOFF;
6017 if (njobs == 0) {
6018 jobtab = ckmalloc(4 * sizeof jobtab[0]);
6019 } else {
6020 jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
6021 memcpy(jp, jobtab, njobs * sizeof jp[0]);
6022 /* Relocate `ps' pointers */
6023 for (i = 0; i < njobs; i++)
6024 if (jp[i].ps == &jobtab[i].ps0)
6025 jp[i].ps = &jp[i].ps0;
6026 ckfree(jobtab);
6027 jobtab = jp;
6028 }
6029 jp = jobtab + njobs;
6030 for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0);
6031 INTON;
6032 break;
6033 }
6034 if (jp->used == 0)
6035 break;
6036 }
6037 INTOFF;
6038 jp->state = 0;
6039 jp->used = 1;
6040 jp->changed = 0;
6041 jp->nprocs = 0;
6042#if JOBS
6043 jp->jobctl = jobctl;
6044#endif
6045 if (nprocs > 1) {
6046 jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
6047 } else {
6048 jp->ps = &jp->ps0;
6049 }
6050 INTON;
6051 TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
6052 jp - jobtab + 1));
6053 return jp;
6054}
6055
6056
6057/*
6058 * Fork of a subshell. If we are doing job control, give the subshell its
6059 * own process group. Jp is a job structure that the job is to be added to.
6060 * N is the command that will be evaluated by the child. Both jp and n may
6061 * be NULL. The mode parameter can be one of the following:
6062 * FORK_FG - Fork off a foreground process.
6063 * FORK_BG - Fork off a background process.
6064 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
6065 * process group even if job control is on.
6066 *
6067 * When job control is turned off, background processes have their standard
6068 * input redirected to /dev/null (except for the second and later processes
6069 * in a pipeline).
6070 */
6071
6072static int
6073forkshell(jp, n, mode)
6074 union node *n;
6075 struct job *jp;
6076 int mode;
6077{
6078 int pid;
6079 int pgrp;
6080 const char *devnull = _PATH_DEVNULL;
6081 const char *nullerr = "Can't open %s";
6082
6083 TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long)n,
6084 mode));
6085 INTOFF;
6086 pid = fork();
6087 if (pid == -1) {
6088 TRACE(("Fork failed, errno=%d\n", errno));
6089 INTON;
6090 error("Cannot fork");
6091 }
6092 if (pid == 0) {
6093 struct job *p;
6094 int wasroot;
6095 int i;
6096
6097 TRACE(("Child shell %d\n", getpid()));
6098 wasroot = rootshell;
6099 rootshell = 0;
6100 closescript();
6101 INTON;
6102 clear_traps();
6103#if JOBS
6104 jobctl = 0; /* do job control only in root shell */
6105 if (wasroot && mode != FORK_NOJOB && mflag) {
6106 if (jp == NULL || jp->nprocs == 0)
6107 pgrp = getpid();
6108 else
6109 pgrp = jp->ps[0].pid;
6110 setpgid(0, pgrp);
6111 if (mode == FORK_FG) {
6112 /*** this causes superfluous TIOCSPGRPS ***/
6113#ifdef OLD_TTY_DRIVER
6114 if (ioctl(fileno2, TIOCSPGRP, (char *)&pgrp) < 0)
6115 error("TIOCSPGRP failed, errno=%d", errno);
6116#else
6117 if (tcsetpgrp(fileno2, pgrp) < 0)
6118 error("tcsetpgrp failed, errno=%d", errno);
6119#endif
6120 }
6121 setsignal(SIGTSTP);
6122 setsignal(SIGTTOU);
6123 } else if (mode == FORK_BG) {
6124 ignoresig(SIGINT);
6125 ignoresig(SIGQUIT);
6126 if ((jp == NULL || jp->nprocs == 0) &&
6127 ! fd0_redirected_p ()) {
6128 close(0);
6129 if (open(devnull, O_RDONLY) != 0)
6130 error(nullerr, devnull);
6131 }
6132 }
6133#else
6134 if (mode == FORK_BG) {
6135 ignoresig(SIGINT);
6136 ignoresig(SIGQUIT);
6137 if ((jp == NULL || jp->nprocs == 0) &&
6138 ! fd0_redirected_p ()) {
6139 close(0);
6140 if (open(devnull, O_RDONLY) != 0)
6141 error(nullerr, devnull);
6142 }
6143 }
6144#endif
6145 for (i = njobs, p = jobtab ; --i >= 0 ; p++)
6146 if (p->used)
6147 freejob(p);
6148 if (wasroot && iflag) {
6149 setsignal(SIGINT);
6150 setsignal(SIGQUIT);
6151 setsignal(SIGTERM);
6152 }
6153 return pid;
6154 }
6155 if (rootshell && mode != FORK_NOJOB && mflag) {
6156 if (jp == NULL || jp->nprocs == 0)
6157 pgrp = pid;
6158 else
6159 pgrp = jp->ps[0].pid;
6160 setpgid(pid, pgrp);
6161 }
6162 if (mode == FORK_BG)
6163 backgndpid = pid; /* set $! */
6164 if (jp) {
6165 struct procstat *ps = &jp->ps[jp->nprocs++];
6166 ps->pid = pid;
6167 ps->status = -1;
6168 ps->cmd = nullstr;
6169 if (iflag && rootshell && n)
6170 ps->cmd = commandtext(n);
6171 }
6172 INTON;
6173 TRACE(("In parent shell: child = %d\n", pid));
6174 return pid;
6175}
6176
6177
6178
6179/*
6180 * Wait for job to finish.
6181 *
6182 * Under job control we have the problem that while a child process is
6183 * running interrupts generated by the user are sent to the child but not
6184 * to the shell. This means that an infinite loop started by an inter-
6185 * active user may be hard to kill. With job control turned off, an
6186 * interactive user may place an interactive program inside a loop. If
6187 * the interactive program catches interrupts, the user doesn't want
6188 * these interrupts to also abort the loop. The approach we take here
6189 * is to have the shell ignore interrupt signals while waiting for a
6190 * forground process to terminate, and then send itself an interrupt
6191 * signal if the child process was terminated by an interrupt signal.
6192 * Unfortunately, some programs want to do a bit of cleanup and then
6193 * exit on interrupt; unless these processes terminate themselves by
6194 * sending a signal to themselves (instead of calling exit) they will
6195 * confuse this approach.
6196 */
6197
6198static int
6199waitforjob(jp)
6200 struct job *jp;
6201 {
6202#if JOBS
6203 int mypgrp = getpgrp();
6204#endif
6205 int status;
6206 int st;
6207 struct sigaction act, oact;
6208
6209 INTOFF;
6210 intreceived = 0;
6211#if JOBS
6212 if (!jobctl) {
6213#else
6214 if (!iflag) {
6215#endif
6216 sigaction(SIGINT, 0, &act);
6217 act.sa_handler = waitonint;
6218 sigaction(SIGINT, &act, &oact);
6219 }
6220 TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
6221 while (jp->state == 0) {
6222 dowait(1, jp);
6223 }
6224#if JOBS
6225 if (!jobctl) {
6226#else
6227 if (!iflag) {
6228#endif
6229 sigaction(SIGINT, &oact, 0);
6230 if (intreceived && trap[SIGINT]) kill(getpid(), SIGINT);
6231 }
6232#if JOBS
6233 if (jp->jobctl) {
6234#ifdef OLD_TTY_DRIVER
6235 if (ioctl(fileno2, TIOCSPGRP, (char *)&mypgrp) < 0)
6236 error("TIOCSPGRP failed, errno=%d\n", errno);
6237#else
6238 if (tcsetpgrp(fileno2, mypgrp) < 0)
6239 error("tcsetpgrp failed, errno=%d\n", errno);
6240#endif
6241 }
6242 if (jp->state == JOBSTOPPED)
6243 curjob = jp - jobtab + 1;
6244#endif
6245 status = jp->ps[jp->nprocs - 1].status;
6246 /* convert to 8 bits */
6247 if (WIFEXITED(status))
6248 st = WEXITSTATUS(status);
6249#if JOBS
6250 else if (WIFSTOPPED(status))
6251 st = WSTOPSIG(status) + 128;
6252#endif
6253 else
6254 st = WTERMSIG(status) + 128;
6255#if JOBS
6256 if (jp->jobctl) {
6257 /*
6258 * This is truly gross.
6259 * If we're doing job control, then we did a TIOCSPGRP which
6260 * caused us (the shell) to no longer be in the controlling
6261 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
6262 * intuit from the subprocess exit status whether a SIGINT
6263 * occured, and if so interrupt ourselves. Yuck. - mycroft
6264 */
6265 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
6266 raise(SIGINT);
6267 }
6268#endif
6269 if (! JOBS || jp->state == JOBDONE)
6270 freejob(jp);
6271 INTON;
6272 return st;
6273}
6274
6275
6276
6277/*
6278 * Wait for a process to terminate.
6279 */
6280
6281static int
6282dowait(block, job)
6283 int block;
6284 struct job *job;
6285{
6286 int pid;
6287 int status;
6288 struct procstat *sp;
6289 struct job *jp;
6290 struct job *thisjob;
6291 int done;
6292 int stopped;
6293 int core;
6294 int sig;
6295
6296 TRACE(("dowait(%d) called\n", block));
6297 do {
6298 pid = waitproc(block, &status);
6299 TRACE(("wait returns %d, status=%d\n", pid, status));
6300 } while (!(block & 2) && pid == -1 && errno == EINTR);
6301 if (pid <= 0)
6302 return pid;
6303 INTOFF;
6304 thisjob = NULL;
6305 for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
6306 if (jp->used) {
6307 done = 1;
6308 stopped = 1;
6309 for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
6310 if (sp->pid == -1)
6311 continue;
6312 if (sp->pid == pid) {
6313 TRACE(("Changing status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status));
6314 sp->status = status;
6315 thisjob = jp;
6316 }
6317 if (sp->status == -1)
6318 stopped = 0;
6319 else if (WIFSTOPPED(sp->status))
6320 done = 0;
6321 }
6322 if (stopped) { /* stopped or done */
6323 int state = done? JOBDONE : JOBSTOPPED;
6324 if (jp->state != state) {
6325 TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
6326 jp->state = state;
6327#if JOBS
6328 if (done && curjob == jp - jobtab + 1)
6329 curjob = 0; /* no current job */
6330#endif
6331 }
6332 }
6333 }
6334 }
6335 INTON;
6336 if (! rootshell || ! iflag || (job && thisjob == job)) {
6337 core = WCOREDUMP(status);
6338#if JOBS
6339 if (WIFSTOPPED(status)) sig = WSTOPSIG(status);
6340 else
6341#endif
6342 if (WIFEXITED(status)) sig = 0;
6343 else sig = WTERMSIG(status);
6344
6345 if (sig != 0 && sig != SIGINT && sig != SIGPIPE) {
6346 if (thisjob != job)
6347 outfmt(out2, "%d: ", pid);
6348#if JOBS
6349 if (sig == SIGTSTP && rootshell && iflag)
6350 outfmt(out2, "%%%ld ",
6351 (long)(job - jobtab + 1));
6352#endif
6353 if (sig < NSIG && sys_siglist[sig])
6354 out2str(sys_siglist[sig]);
6355 else
6356 outfmt(out2, "Signal %d", sig);
6357 if (core)
6358 out2str(" - core dumped");
6359 out2c('\n');
6360#ifdef FLUSHERR
6361 flushout(&errout);
6362#endif
6363 } else {
6364 TRACE(("Not printing status: status=%d, sig=%d\n",
6365 status, sig));
6366 }
6367 } else {
6368 TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job));
6369 if (thisjob)
6370 thisjob->changed = 1;
6371 }
6372 return pid;
6373}
6374
6375
6376
6377/*
6378 * Do a wait system call. If job control is compiled in, we accept
6379 * stopped processes. If block is zero, we return a value of zero
6380 * rather than blocking.
6381 *
6382 * System V doesn't have a non-blocking wait system call. It does
6383 * have a SIGCLD signal that is sent to a process when one of it's
6384 * children dies. The obvious way to use SIGCLD would be to install
6385 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
6386 * was received, and have waitproc bump another counter when it got
6387 * the status of a process. Waitproc would then know that a wait
6388 * system call would not block if the two counters were different.
6389 * This approach doesn't work because if a process has children that
6390 * have not been waited for, System V will send it a SIGCLD when it
6391 * installs a signal handler for SIGCLD. What this means is that when
6392 * a child exits, the shell will be sent SIGCLD signals continuously
6393 * until is runs out of stack space, unless it does a wait call before
6394 * restoring the signal handler. The code below takes advantage of
6395 * this (mis)feature by installing a signal handler for SIGCLD and
6396 * then checking to see whether it was called. If there are any
6397 * children to be waited for, it will be.
6398 *
6399 * If neither SYSV nor BSD is defined, we don't implement nonblocking
6400 * waits at all. In this case, the user will not be informed when
6401 * a background process until the next time she runs a real program
6402 * (as opposed to running a builtin command or just typing return),
6403 * and the jobs command may give out of date information.
6404 */
6405
6406#ifdef SYSV
6407static int gotsigchild;
6408
6409static int onsigchild() {
6410 gotsigchild = 1;
6411}
6412#endif
6413
6414
6415static int
6416waitproc(block, status)
6417 int block;
6418 int *status;
6419{
6420#ifdef BSD
6421 int flags;
6422
6423 flags = 0;
6424#if JOBS
6425 if (jobctl)
6426 flags |= WUNTRACED;
6427#endif
6428 if (block == 0)
6429 flags |= WNOHANG;
6430 return wait3(status, flags, (struct rusage *)NULL);
6431#else
6432#ifdef SYSV
6433 int (*save)();
6434
6435 if (block == 0) {
6436 gotsigchild = 0;
6437 save = signal(SIGCLD, onsigchild);
6438 signal(SIGCLD, save);
6439 if (gotsigchild == 0)
6440 return 0;
6441 }
6442 return wait(status);
6443#else
6444 if (block == 0)
6445 return 0;
6446 return wait(status);
6447#endif
6448#endif
6449}
6450
6451/*
6452 * return 1 if there are stopped jobs, otherwise 0
6453 */
6454static int job_warning = 0;
6455static int
6456stoppedjobs()
6457{
6458 int jobno;
6459 struct job *jp;
6460
6461 if (job_warning)
6462 return (0);
6463 for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
6464 if (jp->used == 0)
6465 continue;
6466 if (jp->state == JOBSTOPPED) {
6467 out2str("You have stopped jobs.\n");
6468 job_warning = 2;
6469 return (1);
6470 }
6471 }
6472
6473 return (0);
6474}
6475
6476/*
6477 * Return a string identifying a command (to be printed by the
6478 * jobs command.
6479 */
6480
6481static char *cmdnextc;
6482static int cmdnleft;
6483#define MAXCMDTEXT 200
6484
6485static char *
6486commandtext(n)
6487 union node *n;
6488 {
6489 char *name;
6490
6491 cmdnextc = name = ckmalloc(MAXCMDTEXT);
6492 cmdnleft = MAXCMDTEXT - 4;
6493 cmdtxt(n);
6494 *cmdnextc = '\0';
6495 return name;
6496}
6497
6498
6499static void
6500cmdtxt(n)
6501 union node *n;
6502 {
6503 union node *np;
6504 struct nodelist *lp;
6505 const char *p;
6506 int i;
6507 char s[2];
6508
6509 if (n == NULL)
6510 return;
6511 switch (n->type) {
6512 case NSEMI:
6513 cmdtxt(n->nbinary.ch1);
6514 cmdputs("; ");
6515 cmdtxt(n->nbinary.ch2);
6516 break;
6517 case NAND:
6518 cmdtxt(n->nbinary.ch1);
6519 cmdputs(" && ");
6520 cmdtxt(n->nbinary.ch2);
6521 break;
6522 case NOR:
6523 cmdtxt(n->nbinary.ch1);
6524 cmdputs(" || ");
6525 cmdtxt(n->nbinary.ch2);
6526 break;
6527 case NPIPE:
6528 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
6529 cmdtxt(lp->n);
6530 if (lp->next)
6531 cmdputs(" | ");
6532 }
6533 break;
6534 case NSUBSHELL:
6535 cmdputs("(");
6536 cmdtxt(n->nredir.n);
6537 cmdputs(")");
6538 break;
6539 case NREDIR:
6540 case NBACKGND:
6541 cmdtxt(n->nredir.n);
6542 break;
6543 case NIF:
6544 cmdputs("if ");
6545 cmdtxt(n->nif.test);
6546 cmdputs("; then ");
6547 cmdtxt(n->nif.ifpart);
6548 cmdputs("...");
6549 break;
6550 case NWHILE:
6551 cmdputs("while ");
6552 goto until;
6553 case NUNTIL:
6554 cmdputs("until ");
6555until:
6556 cmdtxt(n->nbinary.ch1);
6557 cmdputs("; do ");
6558 cmdtxt(n->nbinary.ch2);
6559 cmdputs("; done");
6560 break;
6561 case NFOR:
6562 cmdputs("for ");
6563 cmdputs(n->nfor.var);
6564 cmdputs(" in ...");
6565 break;
6566 case NCASE:
6567 cmdputs("case ");
6568 cmdputs(n->ncase.expr->narg.text);
6569 cmdputs(" in ...");
6570 break;
6571 case NDEFUN:
6572 cmdputs(n->narg.text);
6573 cmdputs("() ...");
6574 break;
6575 case NCMD:
6576 for (np = n->ncmd.args ; np ; np = np->narg.next) {
6577 cmdtxt(np);
6578 if (np->narg.next)
6579 cmdputs(spcstr);
6580 }
6581 for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
6582 cmdputs(spcstr);
6583 cmdtxt(np);
6584 }
6585 break;
6586 case NARG:
6587 cmdputs(n->narg.text);
6588 break;
6589 case NTO:
6590 p = ">"; i = 1; goto redir;
6591 case NAPPEND:
6592 p = ">>"; i = 1; goto redir;
6593 case NTOFD:
6594 p = ">&"; i = 1; goto redir;
6595 case NTOOV:
6596 p = ">|"; i = 1; goto redir;
6597 case NFROM:
6598 p = "<"; i = 0; goto redir;
6599 case NFROMFD:
6600 p = "<&"; i = 0; goto redir;
6601 case NFROMTO:
6602 p = "<>"; i = 0; goto redir;
6603redir:
6604 if (n->nfile.fd != i) {
6605 s[0] = n->nfile.fd + '0';
6606 s[1] = '\0';
6607 cmdputs(s);
6608 }
6609 cmdputs(p);
6610 if (n->type == NTOFD || n->type == NFROMFD) {
6611 s[0] = n->ndup.dupfd + '0';
6612 s[1] = '\0';
6613 cmdputs(s);
6614 } else {
6615 cmdtxt(n->nfile.fname);
6616 }
6617 break;
6618 case NHERE:
6619 case NXHERE:
6620 cmdputs("<<...");
6621 break;
6622 default:
6623 cmdputs("???");
6624 break;
6625 }
6626}
6627
6628
6629
6630static void
6631cmdputs(s)
6632 const char *s;
6633 {
6634 const char *p;
6635 char *q;
6636 char c;
6637 int subtype = 0;
6638
6639 if (cmdnleft <= 0)
6640 return;
6641 p = s;
6642 q = cmdnextc;
6643 while ((c = *p++) != '\0') {
6644 if (c == CTLESC)
6645 *q++ = *p++;
6646 else if (c == CTLVAR) {
6647 *q++ = '$';
6648 if (--cmdnleft > 0)
6649 *q++ = '{';
6650 subtype = *p++;
6651 } else if (c == '=' && subtype != 0) {
6652 *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
6653 subtype = 0;
6654 } else if (c == CTLENDVAR) {
6655 *q++ = '}';
6656 } else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE)
6657 cmdnleft++; /* ignore it */
6658 else
6659 *q++ = c;
6660 if (--cmdnleft <= 0) {
6661 *q++ = '.';
6662 *q++ = '.';
6663 *q++ = '.';
6664 break;
6665 }
6666 }
6667 cmdnextc = q;
6668}
6669
6670static void waitonint(int sig) {
6671 intreceived = 1;
6672 return;
6673}
6674/* $NetBSD: mail.c,v 1.14 2000/07/03 03:26:19 matt Exp $ */
6675
Eric Andersencb57d552001-06-28 07:25:16 +00006676/*
6677 * Routines to check for mail. (Perhaps make part of main.c?)
6678 */
6679
6680
6681#define MAXMBOXES 10
6682
6683
6684static int nmboxes; /* number of mailboxes */
6685static time_t mailtime[MAXMBOXES]; /* times of mailboxes */
6686
6687
6688
6689/*
6690 * Print appropriate message(s) if mail has arrived. If the argument is
6691 * nozero, then the value of MAIL has changed, so we just update the
6692 * values.
6693 */
6694
6695static void
6696chkmail(silent)
6697 int silent;
6698{
6699 int i;
6700 const char *mpath;
6701 char *p;
6702 char *q;
6703 struct stackmark smark;
6704 struct stat statb;
6705
6706 if (silent)
6707 nmboxes = 10;
6708 if (nmboxes == 0)
6709 return;
6710 setstackmark(&smark);
6711 mpath = mpathset()? mpathval() : mailval();
6712 for (i = 0 ; i < nmboxes ; i++) {
6713 p = padvance(&mpath, nullstr);
6714 if (p == NULL)
6715 break;
6716 if (*p == '\0')
6717 continue;
6718 for (q = p ; *q ; q++);
6719#ifdef DEBUG
6720 if (q[-1] != '/')
6721 abort();
6722#endif
6723 q[-1] = '\0'; /* delete trailing '/' */
6724#ifdef notdef /* this is what the System V shell claims to do (it lies) */
6725 if (stat(p, &statb) < 0)
6726 statb.st_mtime = 0;
6727 if (statb.st_mtime > mailtime[i] && ! silent) {
6728 outfmt(
6729 &errout, snlfmt,
6730 pathopt? pathopt : "you have mail"
6731 );
6732 }
6733 mailtime[i] = statb.st_mtime;
6734#else /* this is what it should do */
6735 if (stat(p, &statb) < 0)
6736 statb.st_size = 0;
6737 if (statb.st_size > mailtime[i] && ! silent) {
6738 outfmt(
6739 &errout, snlfmt,
6740 pathopt? pathopt : "you have mail"
6741 );
6742 }
6743 mailtime[i] = statb.st_size;
6744#endif
6745 }
6746 nmboxes = i;
6747 popstackmark(&smark);
6748}
6749/* $NetBSD: main.c,v 1.40 2001/02/04 19:52:06 christos Exp $ */
6750
Eric Andersencb57d552001-06-28 07:25:16 +00006751
6752#define PROFILE 0
6753
6754static int rootpid;
6755static int rootshell;
6756#if PROFILE
6757short profile_buf[16384];
6758extern int etext();
6759#endif
6760
6761static void read_profile __P((const char *));
6762static char *find_dot_file __P((char *));
6763int shell_main __P((int, char **));
6764
6765extern int oexitstatus;
6766/*
6767 * Main routine. We initialize things, parse the arguments, execute
6768 * profiles if we're a login shell, and then call cmdloop to execute
6769 * commands. The setjmp call sets up the location to jump to when an
6770 * exception occurs. When an exception occurs the variable "state"
6771 * is used to figure out how far we had gotten.
6772 */
6773
6774int
6775shell_main(argc, argv)
6776 int argc;
6777 char **argv;
6778{
6779 struct jmploc jmploc;
6780 struct stackmark smark;
6781 volatile int state;
6782 char *shinit;
6783
6784 DOTCMD = find_builtin(".");
6785 BLTINCMD = find_builtin("builtin");
6786 COMMANDCMD = find_builtin("command");
6787 EXECCMD = find_builtin("exec");
6788 EVALCMD = find_builtin("eval");
6789
6790#if PROFILE
6791 monitor(4, etext, profile_buf, sizeof profile_buf, 50);
6792#endif
6793#if defined(linux) || defined(__GNU__)
6794 signal(SIGCHLD, SIG_DFL);
6795#endif
6796 state = 0;
6797 if (setjmp(jmploc.loc)) {
6798 INTOFF;
6799 /*
6800 * When a shell procedure is executed, we raise the
6801 * exception EXSHELLPROC to clean up before executing
6802 * the shell procedure.
6803 */
6804 switch (exception) {
6805 case EXSHELLPROC:
6806 rootpid = getpid();
6807 rootshell = 1;
6808 minusc = NULL;
6809 state = 3;
6810 break;
6811
6812 case EXEXEC:
6813 exitstatus = exerrno;
6814 break;
6815
6816 case EXERROR:
6817 exitstatus = 2;
6818 break;
6819
6820 default:
6821 break;
6822 }
6823
6824 if (exception != EXSHELLPROC) {
6825 if (state == 0 || iflag == 0 || ! rootshell)
6826 exitshell(exitstatus);
6827 }
6828 reset();
6829 if (exception == EXINT
6830#if ATTY
6831 && (! attyset() || equal(termval(), "emacs"))
6832#endif
6833 ) {
6834 out2c('\n');
6835#ifdef FLUSHERR
6836 flushout(out2);
6837#endif
6838 }
6839 popstackmark(&smark);
6840 FORCEINTON; /* enable interrupts */
6841 if (state == 1)
6842 goto state1;
6843 else if (state == 2)
6844 goto state2;
6845 else if (state == 3)
6846 goto state3;
6847 else
6848 goto state4;
6849 }
6850 handler = &jmploc;
6851#ifdef DEBUG
6852 opentrace();
6853 trputs("Shell args: "); trargs(argv);
6854#endif
6855 rootpid = getpid();
6856 rootshell = 1;
6857 init();
6858 setstackmark(&smark);
6859 procargs(argc, argv);
6860 if (argv[0] && argv[0][0] == '-') {
6861 state = 1;
6862 read_profile("/etc/profile");
6863state1:
6864 state = 2;
6865 read_profile(".profile");
6866 }
6867state2:
6868 state = 3;
6869#ifndef linux
6870 if (getuid() == geteuid() && getgid() == getegid()) {
6871#endif
6872 if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
6873 state = 3;
6874 read_profile(shinit);
6875 }
6876#ifndef linux
6877 }
6878#endif
6879state3:
6880 state = 4;
6881 if (sflag == 0 || minusc) {
6882 static int sigs[] = {
6883 SIGINT, SIGQUIT, SIGHUP,
6884#ifdef SIGTSTP
6885 SIGTSTP,
6886#endif
6887 SIGPIPE
6888 };
6889#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0]))
6890 int i;
6891
6892 for (i = 0; i < SIGSSIZE; i++)
6893 setsignal(sigs[i]);
6894 }
6895
6896 if (minusc)
6897 evalstring(minusc, 0);
6898
6899 if (sflag || minusc == NULL) {
6900state4: /* XXX ??? - why isn't this before the "if" statement */
6901 cmdloop(1);
6902 }
6903#if PROFILE
6904 monitor(0);
6905#endif
6906 exitshell(exitstatus);
6907 /* NOTREACHED */
6908}
6909
6910
6911/*
6912 * Read and execute commands. "Top" is nonzero for the top level command
6913 * loop; it turns on prompting if the shell is interactive.
6914 */
6915
6916static void
6917cmdloop(top)
6918 int top;
6919{
6920 union node *n;
6921 struct stackmark smark;
6922 int inter;
6923 int numeof = 0;
6924
6925 TRACE(("cmdloop(%d) called\n", top));
6926 setstackmark(&smark);
6927 for (;;) {
6928 if (pendingsigs)
6929 dotrap();
6930 inter = 0;
6931 if (iflag && top) {
6932 inter++;
6933 showjobs(1);
6934 chkmail(0);
6935 flushout(&output);
6936 }
6937 n = parsecmd(inter);
6938 /* showtree(n); DEBUG */
6939 if (n == NEOF) {
6940 if (!top || numeof >= 50)
6941 break;
6942 if (!stoppedjobs()) {
6943 if (!Iflag)
6944 break;
6945 out2str("\nUse \"exit\" to leave shell.\n");
6946 }
6947 numeof++;
6948 } else if (n != NULL && nflag == 0) {
6949 job_warning = (job_warning == 2) ? 1 : 0;
6950 numeof = 0;
6951 evaltree(n, 0);
6952 }
6953 popstackmark(&smark);
6954 setstackmark(&smark);
6955 if (evalskip == SKIPFILE) {
6956 evalskip = 0;
6957 break;
6958 }
6959 }
6960 popstackmark(&smark);
6961}
6962
6963
6964
6965/*
6966 * Read /etc/profile or .profile. Return on error.
6967 */
6968
6969static void
6970read_profile(name)
6971 const char *name;
6972{
6973 int fd;
6974 int xflag_set = 0;
6975 int vflag_set = 0;
6976
6977 INTOFF;
6978 if ((fd = open(name, O_RDONLY)) >= 0)
6979 setinputfd(fd, 1);
6980 INTON;
6981 if (fd < 0)
6982 return;
6983 /* -q turns off -x and -v just when executing init files */
6984 if (qflag) {
6985 if (xflag)
6986 xflag = 0, xflag_set = 1;
6987 if (vflag)
6988 vflag = 0, vflag_set = 1;
6989 }
6990 cmdloop(0);
6991 if (qflag) {
6992 if (xflag_set)
6993 xflag = 1;
6994 if (vflag_set)
6995 vflag = 1;
6996 }
6997 popfile();
6998}
6999
7000
7001
7002/*
7003 * Read a file containing shell functions.
7004 */
7005
7006static void
7007readcmdfile(name)
7008 char *name;
7009{
7010 int fd;
7011
7012 INTOFF;
7013 if ((fd = open(name, O_RDONLY)) >= 0)
7014 setinputfd(fd, 1);
7015 else
7016 error("Can't open %s", name);
7017 INTON;
7018 cmdloop(0);
7019 popfile();
7020}
7021
7022
7023
7024/*
7025 * Take commands from a file. To be compatable we should do a path
7026 * search for the file, which is necessary to find sub-commands.
7027 */
7028
7029
7030static char *
7031find_dot_file(mybasename)
7032 char *mybasename;
7033{
7034 char *fullname;
7035 const char *path = pathval();
7036 struct stat statb;
7037
7038 /* don't try this for absolute or relative paths */
7039 if (strchr(mybasename, '/'))
7040 return mybasename;
7041
7042 while ((fullname = padvance(&path, mybasename)) != NULL) {
7043 if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
7044 /*
7045 * Don't bother freeing here, since it will
7046 * be freed by the caller.
7047 */
7048 return fullname;
7049 }
7050 stunalloc(fullname);
7051 }
7052
7053 /* not found in the PATH */
7054 error("%s: not found", mybasename);
7055 /* NOTREACHED */
7056}
7057
7058static int
7059dotcmd(argc, argv)
7060 int argc;
7061 char **argv;
7062{
7063 struct strlist *sp;
7064 exitstatus = 0;
7065
7066 for (sp = cmdenviron; sp ; sp = sp->next)
7067 setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED);
7068
7069 if (argc >= 2) { /* That's what SVR2 does */
7070 char *fullname;
7071 struct stackmark smark;
7072
7073 setstackmark(&smark);
7074 fullname = find_dot_file(argv[1]);
7075 setinputfile(fullname, 1);
7076 commandname = fullname;
7077 cmdloop(0);
7078 popfile();
7079 popstackmark(&smark);
7080 }
7081 return exitstatus;
7082}
7083
7084
7085static int
7086exitcmd(argc, argv)
7087 int argc;
7088 char **argv;
7089{
7090 if (stoppedjobs())
7091 return 0;
7092 if (argc > 1)
7093 exitstatus = number(argv[1]);
7094 else
7095 exitstatus = oexitstatus;
7096 exitshell(exitstatus);
7097 /* NOTREACHED */
7098}
7099/* $NetBSD: memalloc.c,v 1.23 2000/11/01 19:56:01 christos Exp $ */
7100
Eric Andersencb57d552001-06-28 07:25:16 +00007101/*
7102 * Parse trees for commands are allocated in lifo order, so we use a stack
7103 * to make this more efficient, and also to avoid all sorts of exception
7104 * handling code to handle interrupts in the middle of a parse.
7105 *
7106 * The size 504 was chosen because the Ultrix malloc handles that size
7107 * well.
7108 */
7109
7110#define MINSIZE 504 /* minimum size of a block */
7111
7112
7113struct stack_block {
7114 struct stack_block *prev;
7115 char space[MINSIZE];
7116};
7117
7118struct stack_block stackbase;
7119struct stack_block *stackp = &stackbase;
7120struct stackmark *markp;
7121static char *stacknxt = stackbase.space;
7122static int stacknleft = MINSIZE;
7123static int sstrnleft;
7124static int herefd = -1;
7125
7126
7127
7128pointer
7129stalloc(nbytes)
7130 int nbytes;
7131{
7132 char *p;
7133
7134 nbytes = ALIGN(nbytes);
7135 if (nbytes > stacknleft) {
7136 int blocksize;
7137 struct stack_block *sp;
7138
7139 blocksize = nbytes;
7140 if (blocksize < MINSIZE)
7141 blocksize = MINSIZE;
7142 INTOFF;
7143 sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize);
7144 sp->prev = stackp;
7145 stacknxt = sp->space;
7146 stacknleft = blocksize;
7147 stackp = sp;
7148 INTON;
7149 }
7150 p = stacknxt;
7151 stacknxt += nbytes;
7152 stacknleft -= nbytes;
7153 return p;
7154}
7155
7156
7157static void
7158stunalloc(p)
7159 pointer p;
7160 {
7161#ifdef DEBUG
7162 if (p == NULL) { /*DEBUG */
7163 write(2, "stunalloc\n", 10);
7164 abort();
7165 }
7166#endif
7167 if (!(stacknxt >= (char *)p && (char *)p >= stackp->space)) {
7168 p = stackp->space;
7169 }
7170 stacknleft += stacknxt - (char *)p;
7171 stacknxt = p;
7172}
7173
7174
7175
7176static void
7177setstackmark(mark)
7178 struct stackmark *mark;
7179 {
7180 mark->stackp = stackp;
7181 mark->stacknxt = stacknxt;
7182 mark->stacknleft = stacknleft;
7183 mark->marknext = markp;
7184 markp = mark;
7185}
7186
7187
7188static void
7189popstackmark(mark)
7190 struct stackmark *mark;
7191 {
7192 struct stack_block *sp;
7193
7194 INTOFF;
7195 markp = mark->marknext;
7196 while (stackp != mark->stackp) {
7197 sp = stackp;
7198 stackp = sp->prev;
7199 ckfree(sp);
7200 }
7201 stacknxt = mark->stacknxt;
7202 stacknleft = mark->stacknleft;
7203 INTON;
7204}
7205
7206
7207/*
7208 * When the parser reads in a string, it wants to stick the string on the
7209 * stack and only adjust the stack pointer when it knows how big the
7210 * string is. Stackblock (defined in stack.h) returns a pointer to a block
7211 * of space on top of the stack and stackblocklen returns the length of
7212 * this block. Growstackblock will grow this space by at least one byte,
7213 * possibly moving it (like realloc). Grabstackblock actually allocates the
7214 * part of the block that has been used.
7215 */
7216
7217static void
7218growstackblock() {
7219 char *p;
7220 int newlen = ALIGN(stacknleft * 2 + 100);
7221 char *oldspace = stacknxt;
7222 int oldlen = stacknleft;
7223 struct stack_block *sp;
7224 struct stack_block *oldstackp;
7225
7226 if (stacknxt == stackp->space && stackp != &stackbase) {
7227 INTOFF;
7228 oldstackp = stackp;
7229 sp = stackp;
7230 stackp = sp->prev;
7231 sp = ckrealloc((pointer)sp, sizeof(struct stack_block) - MINSIZE + newlen);
7232 sp->prev = stackp;
7233 stackp = sp;
7234 stacknxt = sp->space;
7235 stacknleft = newlen;
7236 {
7237 /* Stack marks pointing to the start of the old block
7238 * must be relocated to point to the new block
7239 */
7240 struct stackmark *xmark;
7241 xmark = markp;
7242 while (xmark != NULL && xmark->stackp == oldstackp) {
7243 xmark->stackp = stackp;
7244 xmark->stacknxt = stacknxt;
7245 xmark->stacknleft = stacknleft;
7246 xmark = xmark->marknext;
7247 }
7248 }
7249 INTON;
7250 } else {
7251 p = stalloc(newlen);
7252 memcpy(p, oldspace, oldlen);
7253 stacknxt = p; /* free the space */
7254 stacknleft += newlen; /* we just allocated */
7255 }
7256}
7257
7258
7259
7260static void
7261grabstackblock(len)
7262 int len;
7263{
7264 len = ALIGN(len);
7265 stacknxt += len;
7266 stacknleft -= len;
7267}
7268
7269
7270
7271/*
7272 * The following routines are somewhat easier to use that the above.
7273 * The user declares a variable of type STACKSTR, which may be declared
7274 * to be a register. The macro STARTSTACKSTR initializes things. Then
7275 * the user uses the macro STPUTC to add characters to the string. In
7276 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
7277 * grown as necessary. When the user is done, she can just leave the
7278 * string there and refer to it using stackblock(). Or she can allocate
7279 * the space for it using grabstackstr(). If it is necessary to allow
7280 * someone else to use the stack temporarily and then continue to grow
7281 * the string, the user should use grabstack to allocate the space, and
7282 * then call ungrabstr(p) to return to the previous mode of operation.
7283 *
7284 * USTPUTC is like STPUTC except that it doesn't check for overflow.
7285 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
7286 * is space for at least one character.
7287 */
7288
7289
7290static char *
7291growstackstr() {
7292 int len = stackblocksize();
7293 if (herefd >= 0 && len >= 1024) {
7294 xwrite(herefd, stackblock(), len);
7295 sstrnleft = len - 1;
7296 return stackblock();
7297 }
7298 growstackblock();
7299 sstrnleft = stackblocksize() - len - 1;
7300 return stackblock() + len;
7301}
7302
7303
7304/*
7305 * Called from CHECKSTRSPACE.
7306 */
7307
7308static char *
7309makestrspace(size_t newlen) {
7310 int len = stackblocksize() - sstrnleft;
7311 do {
7312 growstackblock();
7313 sstrnleft = stackblocksize() - len;
7314 } while (sstrnleft < newlen);
7315 return stackblock() + len;
7316}
7317
7318
7319
7320static void
7321ungrabstackstr(s, p)
7322 char *s;
7323 char *p;
7324 {
7325 stacknleft += stacknxt - s;
7326 stacknxt = s;
7327 sstrnleft = stacknleft - (p - s);
7328}
7329/* $NetBSD: miscbltin.c,v 1.30 2001/02/04 19:52:06 christos Exp $ */
7330
Eric Andersencb57d552001-06-28 07:25:16 +00007331/*
7332 * Miscelaneous builtins.
7333 */
7334
7335
7336#undef rflag
7337
7338#ifdef __GLIBC__
7339mode_t getmode(const void *, mode_t);
7340static void *setmode(const char *);
7341
7342#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
7343typedef enum __rlimit_resource rlim_t;
7344#endif
7345#endif
7346
7347
7348
7349/*
7350 * The read builtin. The -e option causes backslashes to escape the
7351 * following character.
7352 *
7353 * This uses unbuffered input, which may be avoidable in some cases.
7354 */
7355
7356static int
7357readcmd(argc, argv)
7358 int argc;
7359 char **argv;
7360{
7361 char **ap;
7362 int backslash;
7363 char c;
7364 int rflag;
7365 char *prompt;
7366 const char *ifs;
7367 char *p;
7368 int startword;
7369 int status;
7370 int i;
7371
7372 rflag = 0;
7373 prompt = NULL;
7374 while ((i = nextopt("p:r")) != '\0') {
7375 if (i == 'p')
7376 prompt = optionarg;
7377 else
7378 rflag = 1;
7379 }
7380 if (prompt && isatty(0)) {
7381 putprompt(prompt);
7382 flushall();
7383 }
7384 if (*(ap = argptr) == NULL)
7385 error("arg count");
7386 if ((ifs = bltinlookup("IFS")) == NULL)
7387 ifs = defifs;
7388 status = 0;
7389 startword = 1;
7390 backslash = 0;
7391 STARTSTACKSTR(p);
7392 for (;;) {
7393 if (read(0, &c, 1) != 1) {
7394 status = 1;
7395 break;
7396 }
7397 if (c == '\0')
7398 continue;
7399 if (backslash) {
7400 backslash = 0;
7401 if (c != '\n')
7402 STPUTC(c, p);
7403 continue;
7404 }
7405 if (!rflag && c == '\\') {
7406 backslash++;
7407 continue;
7408 }
7409 if (c == '\n')
7410 break;
7411 if (startword && *ifs == ' ' && strchr(ifs, c)) {
7412 continue;
7413 }
7414 startword = 0;
7415 if (backslash && c == '\\') {
7416 if (read(0, &c, 1) != 1) {
7417 status = 1;
7418 break;
7419 }
7420 STPUTC(c, p);
7421 } else if (ap[1] != NULL && strchr(ifs, c) != NULL) {
7422 STACKSTRNUL(p);
7423 setvar(*ap, stackblock(), 0);
7424 ap++;
7425 startword = 1;
7426 STARTSTACKSTR(p);
7427 } else {
7428 STPUTC(c, p);
7429 }
7430 }
7431 STACKSTRNUL(p);
7432 /* Remove trailing blanks */
7433 while (stackblock() <= --p && strchr(ifs, *p) != NULL)
7434 *p = '\0';
7435 setvar(*ap, stackblock(), 0);
7436 while (*++ap != NULL)
7437 setvar(*ap, nullstr, 0);
7438 return status;
7439}
7440
7441
7442
7443static int
7444umaskcmd(argc, argv)
7445 int argc;
7446 char **argv;
7447{
7448 char *ap;
7449 int mask;
7450 int i;
7451 int symbolic_mode = 0;
7452
7453 while ((i = nextopt("S")) != '\0') {
7454 symbolic_mode = 1;
7455 }
7456
7457 INTOFF;
7458 mask = umask(0);
7459 umask(mask);
7460 INTON;
7461
7462 if ((ap = *argptr) == NULL) {
7463 if (symbolic_mode) {
7464 char u[4], g[4], o[4];
7465
7466 i = 0;
7467 if ((mask & S_IRUSR) == 0)
7468 u[i++] = 'r';
7469 if ((mask & S_IWUSR) == 0)
7470 u[i++] = 'w';
7471 if ((mask & S_IXUSR) == 0)
7472 u[i++] = 'x';
7473 u[i] = '\0';
7474
7475 i = 0;
7476 if ((mask & S_IRGRP) == 0)
7477 g[i++] = 'r';
7478 if ((mask & S_IWGRP) == 0)
7479 g[i++] = 'w';
7480 if ((mask & S_IXGRP) == 0)
7481 g[i++] = 'x';
7482 g[i] = '\0';
7483
7484 i = 0;
7485 if ((mask & S_IROTH) == 0)
7486 o[i++] = 'r';
7487 if ((mask & S_IWOTH) == 0)
7488 o[i++] = 'w';
7489 if ((mask & S_IXOTH) == 0)
7490 o[i++] = 'x';
7491 o[i] = '\0';
7492
7493 out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
7494 } else {
7495 out1fmt("%.4o\n", mask);
7496 }
7497 } else {
7498 if (isdigit((unsigned char)*ap)) {
7499 mask = 0;
7500 do {
7501 if (*ap >= '8' || *ap < '0')
7502 error("Illegal number: %s", argv[1]);
7503 mask = (mask << 3) + (*ap - '0');
7504 } while (*++ap != '\0');
7505 umask(mask);
7506 } else {
7507 void *set;
7508
7509 INTOFF;
7510 if ((set = setmode(ap)) != 0) {
7511 mask = getmode(set, ~mask & 0777);
7512 ckfree(set);
7513 }
7514 INTON;
7515 if (!set)
7516 error("Illegal mode: %s", ap);
7517
7518 umask(~mask & 0777);
7519 }
7520 }
7521 return 0;
7522}
7523
7524/*
7525 * ulimit builtin
7526 *
7527 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
7528 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
7529 * ash by J.T. Conklin.
7530 *
7531 * Public domain.
7532 */
7533
7534struct limits {
7535 const char *name;
7536 int cmd;
7537 int factor; /* multiply by to get rlim_{cur,max} values */
7538 char option;
7539};
7540
7541static const struct limits limits[] = {
7542#ifdef RLIMIT_CPU
7543 { "time(seconds)", RLIMIT_CPU, 1, 't' },
7544#endif
7545#ifdef RLIMIT_FSIZE
7546 { "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
7547#endif
7548#ifdef RLIMIT_DATA
7549 { "data(kbytes)", RLIMIT_DATA, 1024, 'd' },
7550#endif
7551#ifdef RLIMIT_STACK
7552 { "stack(kbytes)", RLIMIT_STACK, 1024, 's' },
7553#endif
7554#ifdef RLIMIT_CORE
7555 { "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
7556#endif
7557#ifdef RLIMIT_RSS
7558 { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' },
7559#endif
7560#ifdef RLIMIT_MEMLOCK
7561 { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' },
7562#endif
7563#ifdef RLIMIT_NPROC
7564 { "process(processes)", RLIMIT_NPROC, 1, 'p' },
7565#endif
7566#ifdef RLIMIT_NOFILE
7567 { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' },
7568#endif
7569#ifdef RLIMIT_VMEM
7570 { "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' },
7571#endif
7572#ifdef RLIMIT_SWAP
7573 { "swap(kbytes)", RLIMIT_SWAP, 1024, 'w' },
7574#endif
7575 { (char *) 0, 0, 0, '\0' }
7576};
7577
7578static int
7579ulimitcmd(argc, argv)
7580 int argc;
7581 char **argv;
7582{
7583 int c;
7584 rlim_t val = 0;
7585 enum { SOFT = 0x1, HARD = 0x2 }
7586 how = SOFT | HARD;
7587 const struct limits *l;
7588 int set, all = 0;
7589 int optc, what;
7590 struct rlimit limit;
7591
7592 what = 'f';
7593 while ((optc = nextopt("HSatfdsmcnpl")) != '\0')
7594 switch (optc) {
7595 case 'H':
7596 how = HARD;
7597 break;
7598 case 'S':
7599 how = SOFT;
7600 break;
7601 case 'a':
7602 all = 1;
7603 break;
7604 default:
7605 what = optc;
7606 }
7607
7608 for (l = limits; l->name && l->option != what; l++)
7609 ;
7610 if (!l->name)
7611 error("internal error (%c)", what);
7612
7613 set = *argptr ? 1 : 0;
7614 if (set) {
7615 char *p = *argptr;
7616
7617 if (all || argptr[1])
7618 error("too many arguments");
7619 if (strcmp(p, "unlimited") == 0)
7620 val = RLIM_INFINITY;
7621 else {
7622 val = (rlim_t) 0;
7623
7624 while ((c = *p++) >= '0' && c <= '9')
7625 {
7626 val = (val * 10) + (long)(c - '0');
7627 if (val < (rlim_t) 0)
7628 break;
7629 }
7630 if (c)
7631 error("bad number");
7632 val *= l->factor;
7633 }
7634 }
7635 if (all) {
7636 for (l = limits; l->name; l++) {
7637 getrlimit(l->cmd, &limit);
7638 if (how & SOFT)
7639 val = limit.rlim_cur;
7640 else if (how & HARD)
7641 val = limit.rlim_max;
7642
7643 out1fmt("%-20s ", l->name);
7644 if (val == RLIM_INFINITY)
7645 out1fmt("unlimited\n");
7646 else
7647 {
7648 val /= l->factor;
7649#ifdef BSD4_4
7650 out1fmt("%lld\n", (long long) val);
7651#else
7652 out1fmt("%ld\n", (long) val);
7653#endif
7654 }
7655 }
7656 return 0;
7657 }
7658
7659 getrlimit(l->cmd, &limit);
7660 if (set) {
7661 if (how & HARD)
7662 limit.rlim_max = val;
7663 if (how & SOFT)
7664 limit.rlim_cur = val;
7665 if (setrlimit(l->cmd, &limit) < 0)
7666 error("error setting limit (%s)", strerror(errno));
7667 } else {
7668 if (how & SOFT)
7669 val = limit.rlim_cur;
7670 else if (how & HARD)
7671 val = limit.rlim_max;
7672
7673 if (val == RLIM_INFINITY)
7674 out1fmt("unlimited\n");
7675 else
7676 {
7677 val /= l->factor;
7678#ifdef BSD4_4
7679 out1fmt("%lld\n", (long long) val);
7680#else
7681 out1fmt("%ld\n", (long) val);
7682#endif
7683 }
7684 }
7685 return 0;
7686}
7687/* $NetBSD: mystring.c,v 1.14 1999/07/09 03:05:50 christos Exp $ */
7688
Eric Andersencb57d552001-06-28 07:25:16 +00007689/*
7690 * String functions.
7691 *
7692 * equal(s1, s2) Return true if strings are equal.
7693 * scopy(from, to) Copy a string.
7694 * scopyn(from, to, n) Like scopy, but checks for overflow.
7695 * number(s) Convert a string of digits to an integer.
7696 * is_number(s) Return true if s is a string of digits.
7697 */
7698
7699static char nullstr[1]; /* zero length string */
7700static const char spcstr[] = " ";
7701static const char snlfmt[] = "%s\n";
7702
7703/*
7704 * equal - #defined in mystring.h
7705 */
7706
7707/*
7708 * scopy - #defined in mystring.h
7709 */
7710
7711
7712#if 0
7713/*
7714 * scopyn - copy a string from "from" to "to", truncating the string
7715 * if necessary. "To" is always nul terminated, even if
7716 * truncation is performed. "Size" is the size of "to".
7717 */
7718
7719static void
7720scopyn(from, to, size)
7721 char const *from;
7722 char *to;
7723 int size;
7724 {
7725
7726 while (--size > 0) {
7727 if ((*to++ = *from++) == '\0')
7728 return;
7729 }
7730 *to = '\0';
7731}
7732#endif
7733
7734
7735/*
7736 * prefix -- see if pfx is a prefix of string.
7737 */
7738
7739static int
7740prefix(pfx, string)
7741 char const *pfx;
7742 char const *string;
7743 {
7744 while (*pfx) {
7745 if (*pfx++ != *string++)
7746 return 0;
7747 }
7748 return 1;
7749}
7750
7751
7752/*
7753 * Convert a string of digits to an integer, printing an error message on
7754 * failure.
7755 */
7756
7757static int
7758number(s)
7759 const char *s;
7760 {
7761
7762 if (! is_number(s))
7763 error("Illegal number: %s", s);
7764 return atoi(s);
7765}
7766
7767
7768
7769/*
7770 * Check for a valid number. This should be elsewhere.
7771 */
7772
7773static int
7774is_number(p)
7775 const char *p;
7776 {
7777 do {
7778 if (! is_digit(*p))
7779 return 0;
7780 } while (*++p != '\0');
7781 return 1;
7782}
7783
7784
7785/*
7786 * Produce a possibly single quoted string suitable as input to the shell.
7787 * The return string is allocated on the stack.
7788 */
7789
7790static char *
7791single_quote(const char *s) {
7792 char *p;
7793
7794 STARTSTACKSTR(p);
7795
7796 do {
7797 char *q = p;
7798 size_t len1, len1p, len2, len2p;
7799
7800 len1 = strcspn(s, "'");
7801 len2 = strspn(s + len1, "'");
7802
7803 len1p = len1 ? len1 + 2 : len1;
7804 switch (len2) {
7805 case 0:
7806 len2p = 0;
7807 break;
7808 case 1:
7809 len2p = 2;
7810 break;
7811 default:
7812 len2p = len2 + 2;
7813 }
7814
7815 CHECKSTRSPACE(len1p + len2p + 1, p);
7816
7817 if (len1) {
7818 *p = '\'';
7819#ifdef _GNU_SOURCE
7820 q = mempcpy(p + 1, s, len1);
7821#else
7822 q = p + 1 + len1;
7823 memcpy(p + 1, s, len1);
7824#endif
7825 *q++ = '\'';
7826 s += len1;
7827 }
7828
7829 switch (len2) {
7830 case 0:
7831 break;
7832 case 1:
7833 *q++ = '\\';
7834 *q = '\'';
7835 s++;
7836 break;
7837 default:
7838 *q = '"';
7839#ifdef _GNU_SOURCE
7840 *(char *) mempcpy(q + 1, s, len2) = '"';
7841#else
7842 q += 1 + len2;
7843 memcpy(q + 1, s, len2);
7844 *q = '"';
7845#endif
7846 s += len2;
7847 }
7848
7849 STADJUST(len1p + len2p, p);
7850 } while (*s);
7851
7852 USTPUTC(0, p);
7853
7854 return grabstackstr(p);
7855}
7856
7857/*
7858 * Like strdup but works with the ash stack.
7859 */
7860
7861static char *
7862sstrdup(const char *p)
7863{
7864 size_t len = strlen(p) + 1;
7865 return memcpy(stalloc(len), p, len);
7866}
7867
7868/*
7869 * Wrapper around strcmp for qsort/bsearch/...
7870 */
7871static int
7872pstrcmp(const void *a, const void *b)
7873{
7874 return strcmp(*(const char *const *) a, *(const char *const *) b);
7875}
7876
7877/*
7878 * Find a string is in a sorted array.
7879 */
7880static const char *const *
7881findstring(const char *s, const char *const *array, size_t nmemb)
7882{
7883 return bsearch(&s, array, nmemb, sizeof(const char *), pstrcmp);
7884}
7885/*
7886 * This file was generated by the mknodes program.
7887 */
7888
7889/* $NetBSD: nodes.c.pat,v 1.8 1997/04/11 23:03:09 christos Exp $ */
7890
Eric Andersencb57d552001-06-28 07:25:16 +00007891/*
7892 * Routine for dealing with parsed shell commands.
7893 */
7894
7895
7896static int funcblocksize; /* size of structures in function */
7897static int funcstringsize; /* size of strings in node */
7898pointer funcblock; /* block to allocate function from */
7899static char *funcstring; /* block to allocate strings from */
7900
7901static const short nodesize[26] = {
7902 ALIGN(sizeof (struct nbinary)),
7903 ALIGN(sizeof (struct ncmd)),
7904 ALIGN(sizeof (struct npipe)),
7905 ALIGN(sizeof (struct nredir)),
7906 ALIGN(sizeof (struct nredir)),
7907 ALIGN(sizeof (struct nredir)),
7908 ALIGN(sizeof (struct nbinary)),
7909 ALIGN(sizeof (struct nbinary)),
7910 ALIGN(sizeof (struct nif)),
7911 ALIGN(sizeof (struct nbinary)),
7912 ALIGN(sizeof (struct nbinary)),
7913 ALIGN(sizeof (struct nfor)),
7914 ALIGN(sizeof (struct ncase)),
7915 ALIGN(sizeof (struct nclist)),
7916 ALIGN(sizeof (struct narg)),
7917 ALIGN(sizeof (struct narg)),
7918 ALIGN(sizeof (struct nfile)),
7919 ALIGN(sizeof (struct nfile)),
7920 ALIGN(sizeof (struct nfile)),
7921 ALIGN(sizeof (struct nfile)),
7922 ALIGN(sizeof (struct nfile)),
7923 ALIGN(sizeof (struct ndup)),
7924 ALIGN(sizeof (struct ndup)),
7925 ALIGN(sizeof (struct nhere)),
7926 ALIGN(sizeof (struct nhere)),
7927 ALIGN(sizeof (struct nnot)),
7928};
7929
7930
7931static void calcsize __P((union node *));
7932static void sizenodelist __P((struct nodelist *));
7933static union node *copynode __P((union node *));
7934static struct nodelist *copynodelist __P((struct nodelist *));
7935static char *nodesavestr __P((char *));
7936
7937
7938
7939/*
7940 * Make a copy of a parse tree.
7941 */
7942
7943union node *
7944copyfunc(n)
7945 union node *n;
7946{
7947 if (n == NULL)
7948 return NULL;
7949 funcblocksize = 0;
7950 funcstringsize = 0;
7951 calcsize(n);
7952 funcblock = ckmalloc(funcblocksize + funcstringsize);
7953 funcstring = (char *) funcblock + funcblocksize;
7954 return copynode(n);
7955}
7956
7957
7958
7959static void
7960calcsize(n)
7961 union node *n;
7962{
7963 if (n == NULL)
7964 return;
7965 funcblocksize += nodesize[n->type];
7966 switch (n->type) {
7967 case NSEMI:
7968 case NAND:
7969 case NOR:
7970 case NWHILE:
7971 case NUNTIL:
7972 calcsize(n->nbinary.ch2);
7973 calcsize(n->nbinary.ch1);
7974 break;
7975 case NCMD:
7976 calcsize(n->ncmd.redirect);
7977 calcsize(n->ncmd.args);
7978 calcsize(n->ncmd.assign);
7979 break;
7980 case NPIPE:
7981 sizenodelist(n->npipe.cmdlist);
7982 break;
7983 case NREDIR:
7984 case NBACKGND:
7985 case NSUBSHELL:
7986 calcsize(n->nredir.redirect);
7987 calcsize(n->nredir.n);
7988 break;
7989 case NIF:
7990 calcsize(n->nif.elsepart);
7991 calcsize(n->nif.ifpart);
7992 calcsize(n->nif.test);
7993 break;
7994 case NFOR:
7995 funcstringsize += strlen(n->nfor.var) + 1;
7996 calcsize(n->nfor.body);
7997 calcsize(n->nfor.args);
7998 break;
7999 case NCASE:
8000 calcsize(n->ncase.cases);
8001 calcsize(n->ncase.expr);
8002 break;
8003 case NCLIST:
8004 calcsize(n->nclist.body);
8005 calcsize(n->nclist.pattern);
8006 calcsize(n->nclist.next);
8007 break;
8008 case NDEFUN:
8009 case NARG:
8010 sizenodelist(n->narg.backquote);
8011 funcstringsize += strlen(n->narg.text) + 1;
8012 calcsize(n->narg.next);
8013 break;
8014 case NTO:
8015 case NFROM:
8016 case NFROMTO:
8017 case NAPPEND:
8018 case NTOOV:
8019 calcsize(n->nfile.fname);
8020 calcsize(n->nfile.next);
8021 break;
8022 case NTOFD:
8023 case NFROMFD:
8024 calcsize(n->ndup.vname);
8025 calcsize(n->ndup.next);
8026 break;
8027 case NHERE:
8028 case NXHERE:
8029 calcsize(n->nhere.doc);
8030 calcsize(n->nhere.next);
8031 break;
8032 case NNOT:
8033 calcsize(n->nnot.com);
8034 break;
8035 };
8036}
8037
8038
8039
8040static void
8041sizenodelist(lp)
8042 struct nodelist *lp;
8043{
8044 while (lp) {
8045 funcblocksize += ALIGN(sizeof(struct nodelist));
8046 calcsize(lp->n);
8047 lp = lp->next;
8048 }
8049}
8050
8051
8052
8053static union node *
8054copynode(n)
8055 union node *n;
8056{
8057 union node *new;
8058
8059 if (n == NULL)
8060 return NULL;
8061 new = funcblock;
8062 funcblock = (char *) funcblock + nodesize[n->type];
8063 switch (n->type) {
8064 case NSEMI:
8065 case NAND:
8066 case NOR:
8067 case NWHILE:
8068 case NUNTIL:
8069 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8070 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8071 break;
8072 case NCMD:
8073 new->ncmd.redirect = copynode(n->ncmd.redirect);
8074 new->ncmd.args = copynode(n->ncmd.args);
8075 new->ncmd.assign = copynode(n->ncmd.assign);
8076 new->ncmd.backgnd = n->ncmd.backgnd;
8077 break;
8078 case NPIPE:
8079 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
8080 new->npipe.backgnd = n->npipe.backgnd;
8081 break;
8082 case NREDIR:
8083 case NBACKGND:
8084 case NSUBSHELL:
8085 new->nredir.redirect = copynode(n->nredir.redirect);
8086 new->nredir.n = copynode(n->nredir.n);
8087 break;
8088 case NIF:
8089 new->nif.elsepart = copynode(n->nif.elsepart);
8090 new->nif.ifpart = copynode(n->nif.ifpart);
8091 new->nif.test = copynode(n->nif.test);
8092 break;
8093 case NFOR:
8094 new->nfor.var = nodesavestr(n->nfor.var);
8095 new->nfor.body = copynode(n->nfor.body);
8096 new->nfor.args = copynode(n->nfor.args);
8097 break;
8098 case NCASE:
8099 new->ncase.cases = copynode(n->ncase.cases);
8100 new->ncase.expr = copynode(n->ncase.expr);
8101 break;
8102 case NCLIST:
8103 new->nclist.body = copynode(n->nclist.body);
8104 new->nclist.pattern = copynode(n->nclist.pattern);
8105 new->nclist.next = copynode(n->nclist.next);
8106 break;
8107 case NDEFUN:
8108 case NARG:
8109 new->narg.backquote = copynodelist(n->narg.backquote);
8110 new->narg.text = nodesavestr(n->narg.text);
8111 new->narg.next = copynode(n->narg.next);
8112 break;
8113 case NTO:
8114 case NFROM:
8115 case NFROMTO:
8116 case NAPPEND:
8117 case NTOOV:
8118 new->nfile.fname = copynode(n->nfile.fname);
8119 new->nfile.fd = n->nfile.fd;
8120 new->nfile.next = copynode(n->nfile.next);
8121 break;
8122 case NTOFD:
8123 case NFROMFD:
8124 new->ndup.vname = copynode(n->ndup.vname);
8125 new->ndup.dupfd = n->ndup.dupfd;
8126 new->ndup.fd = n->ndup.fd;
8127 new->ndup.next = copynode(n->ndup.next);
8128 break;
8129 case NHERE:
8130 case NXHERE:
8131 new->nhere.doc = copynode(n->nhere.doc);
8132 new->nhere.fd = n->nhere.fd;
8133 new->nhere.next = copynode(n->nhere.next);
8134 break;
8135 case NNOT:
8136 new->nnot.com = copynode(n->nnot.com);
8137 break;
8138 };
8139 new->type = n->type;
8140 return new;
8141}
8142
8143
8144static struct nodelist *
8145copynodelist(lp)
8146 struct nodelist *lp;
8147{
8148 struct nodelist *start;
8149 struct nodelist **lpp;
8150
8151 lpp = &start;
8152 while (lp) {
8153 *lpp = funcblock;
8154 funcblock = (char *) funcblock + ALIGN(sizeof(struct nodelist));
8155 (*lpp)->n = copynode(lp->n);
8156 lp = lp->next;
8157 lpp = &(*lpp)->next;
8158 }
8159 *lpp = NULL;
8160 return start;
8161}
8162
8163
8164
8165static char *
8166nodesavestr(s)
8167 char *s;
8168{
8169#ifdef _GNU_SOURCE
8170 char *rtn = funcstring;
8171
8172 funcstring = stpcpy(funcstring, s) + 1;
8173 return rtn;
8174#else
8175 register char *p = s;
8176 register char *q = funcstring;
8177 char *rtn = funcstring;
8178
8179 while ((*q++ = *p++) != '\0')
8180 continue;
8181 funcstring = q;
8182 return rtn;
8183#endif
8184}
8185
8186
8187
8188/*
8189 * Free a parse tree.
8190 */
8191
8192static void
8193freefunc(n)
8194 union node *n;
8195{
8196 if (n)
8197 ckfree(n);
8198}
8199/* $NetBSD: options.c,v 1.31 2001/02/26 13:06:43 wiz Exp $ */
8200
Eric Andersencb57d552001-06-28 07:25:16 +00008201
8202struct optent optlist[NOPTS] = {
8203 { "errexit", 'e', 0 },
8204 { "noglob", 'f', 0 },
8205 { "ignoreeof", 'I', 0 },
8206 { "interactive",'i', 0 },
8207 { "monitor", 'm', 0 },
8208 { "noexec", 'n', 0 },
8209 { "stdin", 's', 0 },
8210 { "xtrace", 'x', 0 },
8211 { "verbose", 'v', 0 },
8212 { "vi", 'V', 0 },
8213 { "emacs", 'E', 0 },
8214 { "noclobber", 'C', 0 },
8215 { "allexport", 'a', 0 },
8216 { "notify", 'b', 0 },
8217 { "nounset", 'u', 0 },
8218 { "quietprofile", 'q', 0 },
8219};
8220static char *arg0; /* value of $0 */
8221struct shparam shellparam; /* current positional parameters */
8222static char **argptr; /* argument list for builtin commands */
8223static char *optionarg; /* set by nextopt (like getopt) */
8224static char *optptr; /* used by nextopt */
8225
8226static char *minusc; /* argument to -c option */
8227
8228
8229static void options __P((int));
8230static void minus_o __P((char *, int));
8231static void setoption __P((int, int));
8232#ifdef ASH_GETOPTS
8233static int getopts __P((char *, char *, char **, int *, int *));
8234#endif
8235
8236
8237/*
8238 * Process the shell command line arguments.
8239 */
8240
8241static void
8242procargs(argc, argv)
8243 int argc;
8244 char **argv;
8245{
8246 int i;
8247
8248 argptr = argv;
8249 if (argc > 0)
8250 argptr++;
8251 for (i = 0; i < NOPTS; i++)
8252 optlist[i].val = 2;
8253 options(1);
8254 if (*argptr == NULL && minusc == NULL)
8255 sflag = 1;
8256 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
8257 iflag = 1;
8258 if (mflag == 2)
8259 mflag = iflag;
8260 for (i = 0; i < NOPTS; i++)
8261 if (optlist[i].val == 2)
8262 optlist[i].val = 0;
8263 arg0 = argv[0];
8264 if (sflag == 0 && minusc == NULL) {
8265 commandname = argv[0];
8266 arg0 = *argptr++;
8267 setinputfile(arg0, 0);
8268 commandname = arg0;
8269 }
8270 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
8271 if (argptr && minusc && *argptr)
8272 arg0 = *argptr++;
8273
8274 shellparam.p = argptr;
8275 shellparam.optind = 1;
8276 shellparam.optoff = -1;
8277 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
8278 while (*argptr) {
8279 shellparam.nparam++;
8280 argptr++;
8281 }
8282 optschanged();
8283}
8284
8285
8286static void
8287optschanged()
8288{
8289 setinteractive(iflag);
8290 setjobctl(mflag);
8291}
8292
8293/*
8294 * Process shell options. The global variable argptr contains a pointer
8295 * to the argument list; we advance it past the options.
8296 */
8297
8298static void
8299options(cmdline)
8300 int cmdline;
8301{
8302 char *p;
8303 int val;
8304 int c;
8305
8306 if (cmdline)
8307 minusc = NULL;
8308 while ((p = *argptr) != NULL) {
8309 argptr++;
8310 if ((c = *p++) == '-') {
8311 val = 1;
8312 if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
8313 if (!cmdline) {
8314 /* "-" means turn off -x and -v */
8315 if (p[0] == '\0')
8316 xflag = vflag = 0;
8317 /* "--" means reset params */
8318 else if (*argptr == NULL)
8319 setparam(argptr);
8320 }
8321 break; /* "-" or "--" terminates options */
8322 }
8323 } else if (c == '+') {
8324 val = 0;
8325 } else {
8326 argptr--;
8327 break;
8328 }
8329 while ((c = *p++) != '\0') {
8330 if (c == 'c' && cmdline) {
8331 char *q;
8332#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */
8333 if (*p == '\0')
8334#endif
8335 q = *argptr++;
8336 if (q == NULL || minusc != NULL)
8337 error("Bad -c option");
8338 minusc = q;
8339#ifdef NOHACK
8340 break;
8341#endif
8342 } else if (c == 'o') {
8343 minus_o(*argptr, val);
8344 if (*argptr)
8345 argptr++;
8346 } else {
8347 setoption(c, val);
8348 }
8349 }
8350 }
8351}
8352
8353static void
8354minus_o(name, val)
8355 char *name;
8356 int val;
8357{
8358 int i;
8359
8360 if (name == NULL) {
8361 out1str("Current option settings\n");
8362 for (i = 0; i < NOPTS; i++)
8363 out1fmt("%-16s%s\n", optlist[i].name,
8364 optlist[i].val ? "on" : "off");
8365 } else {
8366 for (i = 0; i < NOPTS; i++)
8367 if (equal(name, optlist[i].name)) {
8368 setoption(optlist[i].letter, val);
8369 return;
8370 }
8371 error("Illegal option -o %s", name);
8372 }
8373}
8374
8375
8376static void
8377setoption(flag, val)
8378 char flag;
8379 int val;
8380 {
8381 int i;
8382
8383 for (i = 0; i < NOPTS; i++)
8384 if (optlist[i].letter == flag) {
8385 optlist[i].val = val;
8386 if (val) {
8387 /* #%$ hack for ksh semantics */
8388 if (flag == 'V')
8389 Eflag = 0;
8390 else if (flag == 'E')
8391 Vflag = 0;
8392 }
8393 return;
8394 }
8395 error("Illegal option -%c", flag);
8396 /* NOTREACHED */
8397}
8398
8399
8400
8401#ifdef mkinit
8402SHELLPROC {
8403 int i;
8404
8405 for (i = 0; i < NOPTS; i++)
8406 optlist[i].val = 0;
8407 optschanged();
8408
8409}
8410#endif
8411
8412
8413/*
8414 * Set the shell parameters.
8415 */
8416
8417static void
8418setparam(argv)
8419 char **argv;
8420 {
8421 char **newparam;
8422 char **ap;
8423 int nparam;
8424
8425 for (nparam = 0 ; argv[nparam] ; nparam++);
8426 ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
8427 while (*argv) {
8428 *ap++ = savestr(*argv++);
8429 }
8430 *ap = NULL;
8431 freeparam(&shellparam);
8432 shellparam.malloc = 1;
8433 shellparam.nparam = nparam;
8434 shellparam.p = newparam;
8435 shellparam.optind = 1;
8436 shellparam.optoff = -1;
8437}
8438
8439
8440/*
8441 * Free the list of positional parameters.
8442 */
8443
8444static void
8445freeparam(param)
8446 volatile struct shparam *param;
8447 {
8448 char **ap;
8449
8450 if (param->malloc) {
8451 for (ap = param->p ; *ap ; ap++)
8452 ckfree(*ap);
8453 ckfree(param->p);
8454 }
8455}
8456
8457
8458
8459/*
8460 * The shift builtin command.
8461 */
8462
8463static int
8464shiftcmd(argc, argv)
8465 int argc;
8466 char **argv;
8467{
8468 int n;
8469 char **ap1, **ap2;
8470
8471 n = 1;
8472 if (argc > 1)
8473 n = number(argv[1]);
8474 if (n > shellparam.nparam)
8475 error("can't shift that many");
8476 INTOFF;
8477 shellparam.nparam -= n;
8478 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
8479 if (shellparam.malloc)
8480 ckfree(*ap1);
8481 }
8482 ap2 = shellparam.p;
8483 while ((*ap2++ = *ap1++) != NULL);
8484 shellparam.optind = 1;
8485 shellparam.optoff = -1;
8486 INTON;
8487 return 0;
8488}
8489
8490
8491
8492/*
8493 * The set command builtin.
8494 */
8495
8496static int
8497setcmd(argc, argv)
8498 int argc;
8499 char **argv;
8500{
8501 if (argc == 1)
8502 return showvarscmd(argc, argv);
8503 INTOFF;
8504 options(0);
8505 optschanged();
8506 if (*argptr != NULL) {
8507 setparam(argptr);
8508 }
8509 INTON;
8510 return 0;
8511}
8512
8513
8514static void
8515getoptsreset(value)
8516 const char *value;
8517{
8518 shellparam.optind = number(value);
8519 shellparam.optoff = -1;
8520}
8521
8522#ifdef ASH_GETOPTS
8523/*
8524 * The getopts builtin. Shellparam.optnext points to the next argument
8525 * to be processed. Shellparam.optptr points to the next character to
8526 * be processed in the current argument. If shellparam.optnext is NULL,
8527 * then it's the first time getopts has been called.
8528 */
8529
8530static int
8531getoptscmd(argc, argv)
8532 int argc;
8533 char **argv;
8534{
8535 char **optbase;
8536
8537 if (argc < 3)
8538 error("Usage: getopts optstring var [arg]");
8539 else if (argc == 3) {
8540 optbase = shellparam.p;
8541 if (shellparam.optind > shellparam.nparam + 1) {
8542 shellparam.optind = 1;
8543 shellparam.optoff = -1;
8544 }
8545 }
8546 else {
8547 optbase = &argv[3];
8548 if (shellparam.optind > argc - 2) {
8549 shellparam.optind = 1;
8550 shellparam.optoff = -1;
8551 }
8552 }
8553
8554 return getopts(argv[1], argv[2], optbase, &shellparam.optind,
8555 &shellparam.optoff);
8556}
8557
8558/*
8559 * Safe version of setvar, returns 1 on success 0 on failure.
8560 */
8561
8562static int
8563setvarsafe(name, val, flags)
8564 const char *name, *val;
8565 int flags;
8566{
8567 struct jmploc jmploc;
8568 struct jmploc *volatile savehandler = handler;
8569 int err = 0;
8570#ifdef __GNUC__
8571 (void) &err;
8572#endif
8573
8574 if (setjmp(jmploc.loc))
8575 err = 1;
8576 else {
8577 handler = &jmploc;
8578 setvar(name, val, flags);
8579 }
8580 handler = savehandler;
8581 return err;
8582}
8583
8584static int
8585getopts(optstr, optvar, optfirst, myoptind, optoff)
8586 char *optstr;
8587 char *optvar;
8588 char **optfirst;
8589 int *myoptind;
8590 int *optoff;
8591{
8592 char *p, *q;
8593 char c = '?';
8594 int done = 0;
8595 int err = 0;
8596 char s[10];
8597 char **optnext = optfirst + *myoptind - 1;
8598
8599 if (*myoptind <= 1 || *optoff < 0 || !(*(optnext - 1)) ||
8600 strlen(*(optnext - 1)) < *optoff)
8601 p = NULL;
8602 else
8603 p = *(optnext - 1) + *optoff;
8604 if (p == NULL || *p == '\0') {
8605 /* Current word is done, advance */
8606 if (optnext == NULL)
8607 return 1;
8608 p = *optnext;
8609 if (p == NULL || *p != '-' || *++p == '\0') {
8610atend:
8611 *myoptind = optnext - optfirst + 1;
8612 p = NULL;
8613 done = 1;
8614 goto out;
8615 }
8616 optnext++;
8617 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
8618 goto atend;
8619 }
8620
8621 c = *p++;
8622 for (q = optstr; *q != c; ) {
8623 if (*q == '\0') {
8624 if (optstr[0] == ':') {
8625 s[0] = c;
8626 s[1] = '\0';
8627 err |= setvarsafe("OPTARG", s, 0);
8628 }
8629 else {
8630 outfmt(&errout, "Illegal option -%c\n", c);
8631 (void) unsetvar("OPTARG");
8632 }
8633 c = '?';
8634 goto bad;
8635 }
8636 if (*++q == ':')
8637 q++;
8638 }
8639
8640 if (*++q == ':') {
8641 if (*p == '\0' && (p = *optnext) == NULL) {
8642 if (optstr[0] == ':') {
8643 s[0] = c;
8644 s[1] = '\0';
8645 err |= setvarsafe("OPTARG", s, 0);
8646 c = ':';
8647 }
8648 else {
8649 outfmt(&errout, "No arg for -%c option\n", c);
8650 (void) unsetvar("OPTARG");
8651 c = '?';
8652 }
8653 goto bad;
8654 }
8655
8656 if (p == *optnext)
8657 optnext++;
8658 setvarsafe("OPTARG", p, 0);
8659 p = NULL;
8660 }
8661 else
8662 setvarsafe("OPTARG", "", 0);
8663 *myoptind = optnext - optfirst + 1;
8664 goto out;
8665
8666bad:
8667 *myoptind = 1;
8668 p = NULL;
8669out:
8670 *optoff = p ? p - *(optnext - 1) : -1;
8671 fmtstr(s, sizeof(s), "%d", *myoptind);
8672 err |= setvarsafe("OPTIND", s, VNOFUNC);
8673 s[0] = c;
8674 s[1] = '\0';
8675 err |= setvarsafe(optvar, s, 0);
8676 if (err) {
8677 *myoptind = 1;
8678 *optoff = -1;
8679 flushall();
8680 exraise(EXERROR);
8681 }
8682 return done;
8683}
8684#endif
8685
8686/*
8687 * XXX - should get rid of. have all builtins use getopt(3). the
8688 * library getopt must have the BSD extension static variable "optreset"
8689 * otherwise it can't be used within the shell safely.
8690 *
8691 * Standard option processing (a la getopt) for builtin routines. The
8692 * only argument that is passed to nextopt is the option string; the
8693 * other arguments are unnecessary. It return the character, or '\0' on
8694 * end of input.
8695 */
8696
8697static int
8698nextopt(optstring)
8699 const char *optstring;
8700 {
8701 char *p;
8702 const char *q;
8703 char c;
8704
8705 if ((p = optptr) == NULL || *p == '\0') {
8706 p = *argptr;
8707 if (p == NULL || *p != '-' || *++p == '\0')
8708 return '\0';
8709 argptr++;
8710 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
8711 return '\0';
8712 }
8713 c = *p++;
8714 for (q = optstring ; *q != c ; ) {
8715 if (*q == '\0')
8716 error("Illegal option -%c", c);
8717 if (*++q == ':')
8718 q++;
8719 }
8720 if (*++q == ':') {
8721 if (*p == '\0' && (p = *argptr++) == NULL)
8722 error("No arg for -%c option", c);
8723 optionarg = p;
8724 p = NULL;
8725 }
8726 optptr = p;
8727 return c;
8728}
8729
8730
8731/* $NetBSD: output.c,v 1.23 2001/01/07 23:39:07 lukem Exp $ */
8732
Eric Andersencb57d552001-06-28 07:25:16 +00008733/*
8734 * Shell output routines. We use our own output routines because:
8735 * When a builtin command is interrupted we have to discard
8736 * any pending output.
8737 * When a builtin command appears in back quotes, we want to
8738 * save the output of the command in a region obtained
8739 * via malloc, rather than doing a fork and reading the
8740 * output of the command via a pipe.
8741 * Our output routines may be smaller than the stdio routines.
8742 */
8743
8744
8745#define OUTBUFSIZ BUFSIZ
8746#define MEM_OUT -3 /* output to dynamically allocated memory */
8747
8748
8749#ifdef USE_GLIBC_STDIO
8750struct output output = {NULL, NULL, 0, NULL, 0, 1, 0};
8751struct output errout = {NULL, NULL, 0, NULL, 0, 2, 0};
8752struct output memout = {NULL, NULL, 0, NULL, 0, MEM_OUT, 0};
8753#else
8754struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
8755struct output errout = {NULL, 0, NULL, 0, 2, 0};
8756struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
8757#endif
8758struct output *out1 = &output;
8759struct output *out2 = &errout;
8760
8761
8762#ifndef USE_GLIBC_STDIO
8763static void __outstr __P((const char *, size_t, struct output*));
8764#endif
8765
8766
8767#ifdef mkinit
8768
8769INCLUDE "output.h"
8770INCLUDE "memalloc.h"
8771
8772INIT {
8773#ifdef USE_GLIBC_STDIO
8774 initstreams();
8775#endif
8776}
8777
8778RESET {
8779 out1 = &output;
8780 out2 = &errout;
8781#ifdef USE_GLIBC_STDIO
8782 if (memout.stream != NULL)
8783 __closememout();
8784#endif
8785 if (memout.buf != NULL) {
8786 ckfree(memout.buf);
8787 memout.buf = NULL;
8788 }
8789}
8790
8791#endif
8792
8793
8794#ifndef USE_GLIBC_STDIO
8795static void
8796__outstr(const char *p, size_t len, struct output *dest) {
8797 if (!dest->bufsize) {
8798 dest->nleft = 0;
8799 } else if (dest->buf == NULL) {
8800 if (len > dest->bufsize && dest->fd == MEM_OUT) {
8801 dest->bufsize = len;
8802 }
8803 INTOFF;
8804 dest->buf = ckmalloc(dest->bufsize);
8805 dest->nextc = dest->buf;
8806 dest->nleft = dest->bufsize;
8807 INTON;
8808 } else if (dest->fd == MEM_OUT) {
8809 int offset;
8810
8811 offset = dest->bufsize;
8812 INTOFF;
8813 if (dest->bufsize >= len) {
8814 dest->bufsize <<= 1;
8815 } else {
8816 dest->bufsize += len;
8817 }
8818 dest->buf = ckrealloc(dest->buf, dest->bufsize);
8819 dest->nleft = dest->bufsize - offset;
8820 dest->nextc = dest->buf + offset;
8821 INTON;
8822 } else {
8823 flushout(dest);
8824 }
8825
8826 if (len < dest->nleft) {
8827 dest->nleft -= len;
8828 memcpy(dest->nextc, p, len);
8829 dest->nextc += len;
8830 return;
8831 }
8832
8833 if (xwrite(dest->fd, p, len) < len)
8834 dest->flags |= OUTPUT_ERR;
8835}
8836#endif
8837
8838
8839static void
8840outstr(p, file)
8841 const char *p;
8842 struct output *file;
8843 {
8844#ifdef USE_GLIBC_STDIO
8845 INTOFF;
8846 fputs(p, file->stream);
8847 INTON;
8848#else
8849 size_t len;
8850
8851 if (!*p) {
8852 return;
8853 }
8854 len = strlen(p);
8855 if ((file->nleft -= len) > 0) {
8856 memcpy(file->nextc, p, len);
8857 file->nextc += len;
8858 return;
8859 }
8860 __outstr(p, len, file);
8861#endif
8862}
8863
8864
8865#ifndef USE_GLIBC_STDIO
8866
8867
8868static void
8869outcslow(c, dest)
8870 char c;
8871 struct output *dest;
8872 {
8873 __outstr(&c, 1, dest);
8874}
8875#endif
8876
8877
8878static void
8879flushall() {
8880 flushout(&output);
8881#ifdef FLUSHERR
8882 flushout(&errout);
8883#endif
8884}
8885
8886
8887static void
8888flushout(dest)
8889 struct output *dest;
8890 {
8891#ifdef USE_GLIBC_STDIO
8892 INTOFF;
8893 fflush(dest->stream);
8894 INTON;
8895#else
8896 size_t len;
8897
8898 len = dest->nextc - dest->buf;
8899 if (dest->buf == NULL || !len || dest->fd < 0)
8900 return;
8901 dest->nextc = dest->buf;
8902 dest->nleft = dest->bufsize;
8903 if (xwrite(dest->fd, dest->buf, len) < len)
8904 dest->flags |= OUTPUT_ERR;
8905#endif
8906}
8907
8908
8909static void
8910freestdout() {
8911 if (output.buf) {
8912 INTOFF;
8913 ckfree(output.buf);
8914 output.buf = NULL;
8915 output.nleft = 0;
8916 INTON;
8917 }
8918 output.flags = 0;
8919}
8920
8921
8922static void
8923#ifdef __STDC__
8924outfmt(struct output *file, const char *fmt, ...)
8925#else
8926static void
8927outfmt(va_alist)
8928 va_dcl
8929#endif
8930{
8931 va_list ap;
8932#ifndef __STDC__
8933 struct output *file;
8934 const char *fmt;
8935
8936 va_start(ap);
8937 file = va_arg(ap, struct output *);
8938 fmt = va_arg(ap, const char *);
8939#else
8940 va_start(ap, fmt);
8941#endif
8942 doformat(file, fmt, ap);
8943 va_end(ap);
8944}
8945
8946
8947static void
8948#ifdef __STDC__
8949out1fmt(const char *fmt, ...)
8950#else
8951out1fmt(va_alist)
8952 va_dcl
8953#endif
8954{
8955 va_list ap;
8956#ifndef __STDC__
8957 const char *fmt;
8958
8959 va_start(ap);
8960 fmt = va_arg(ap, const char *);
8961#else
8962 va_start(ap, fmt);
8963#endif
8964 doformat(out1, fmt, ap);
8965 va_end(ap);
8966}
8967
8968static void
8969#ifdef __STDC__
8970fmtstr(char *outbuf, size_t length, const char *fmt, ...)
8971#else
8972fmtstr(va_alist)
8973 va_dcl
8974#endif
8975{
8976 va_list ap;
8977#ifndef __STDC__
8978 char *outbuf;
8979 size_t length;
8980 const char *fmt;
8981
8982 va_start(ap);
8983 outbuf = va_arg(ap, char *);
8984 length = va_arg(ap, size_t);
8985 fmt = va_arg(ap, const char *);
8986#else
8987 va_start(ap, fmt);
8988#endif
8989 INTOFF;
8990 vsnprintf(outbuf, length, fmt, ap);
8991 INTON;
8992}
8993
8994#ifndef USE_GLIBC_STDIO
8995/*
8996 * Formatted output. This routine handles a subset of the printf formats:
8997 * - Formats supported: d, u, o, p, X, s, and c.
8998 * - The x format is also accepted but is treated like X.
8999 * - The l, ll and q modifiers are accepted.
9000 * - The - and # flags are accepted; # only works with the o format.
9001 * - Width and precision may be specified with any format except c.
9002 * - An * may be given for the width or precision.
9003 * - The obsolete practice of preceding the width with a zero to get
9004 * zero padding is not supported; use the precision field.
9005 * - A % may be printed by writing %% in the format string.
9006 */
9007
9008#define TEMPSIZE 24
9009
9010#ifdef BSD4_4
9011#define HAVE_VASPRINTF 1
9012#endif
9013
9014#if !HAVE_VASPRINTF
9015static const char digit[] = "0123456789ABCDEF";
9016#endif
9017
9018
9019static void
9020doformat(dest, f, ap)
9021 struct output *dest;
9022 const char *f; /* format string */
9023 va_list ap;
9024{
9025#if HAVE_VASPRINTF
9026 char *s, *t;
9027 int len;
9028
9029 INTOFF;
9030 len = vasprintf(&t, f, ap);
9031 if (len < 0) {
9032 return;
9033 }
9034 s = stalloc(++len);
9035 memcpy(s, t, len);
9036 free(t);
9037 INTON;
9038 outstr(s, dest);
9039 stunalloc(s);
9040#else /* !HAVE_VASPRINTF */
9041 char c;
9042 char temp[TEMPSIZE];
9043 int flushleft;
9044 int sharp;
9045 int width;
9046 int prec;
9047 int islong;
9048 int isquad;
9049 char *p;
9050 int sign;
9051#ifdef BSD4_4
9052 quad_t l;
9053 u_quad_t num;
9054#else
9055 long l;
9056 u_long num;
9057#endif
9058 unsigned base;
9059 int len;
9060 int size;
9061 int pad;
9062
9063 while ((c = *f++) != '\0') {
9064 if (c != '%') {
9065 outc(c, dest);
9066 continue;
9067 }
9068 flushleft = 0;
9069 sharp = 0;
9070 width = 0;
9071 prec = -1;
9072 islong = 0;
9073 isquad = 0;
9074 for (;;) {
9075 if (*f == '-')
9076 flushleft++;
9077 else if (*f == '#')
9078 sharp++;
9079 else
9080 break;
9081 f++;
9082 }
9083 if (*f == '*') {
9084 width = va_arg(ap, int);
9085 f++;
9086 } else {
9087 while (is_digit(*f)) {
9088 width = 10 * width + digit_val(*f++);
9089 }
9090 }
9091 if (*f == '.') {
9092 if (*++f == '*') {
9093 prec = va_arg(ap, int);
9094 f++;
9095 } else {
9096 prec = 0;
9097 while (is_digit(*f)) {
9098 prec = 10 * prec + digit_val(*f++);
9099 }
9100 }
9101 }
9102 if (*f == 'l') {
9103 f++;
9104 if (*f == 'l') {
9105 isquad++;
9106 f++;
9107 } else
9108 islong++;
9109 } else if (*f == 'q') {
9110 isquad++;
9111 f++;
9112 }
9113 switch (*f) {
9114 case 'd':
9115#ifdef BSD4_4
9116 if (isquad)
9117 l = va_arg(ap, quad_t);
9118 else
9119#endif
9120 if (islong)
9121 l = va_arg(ap, long);
9122 else
9123 l = va_arg(ap, int);
9124 sign = 0;
9125 num = l;
9126 if (l < 0) {
9127 num = -l;
9128 sign = 1;
9129 }
9130 base = 10;
9131 goto number;
9132 case 'u':
9133 base = 10;
9134 goto uns_number;
9135 case 'o':
9136 base = 8;
9137 goto uns_number;
9138 case 'p':
9139 outc('0', dest);
9140 outc('x', dest);
9141 /*FALLTHROUGH*/
9142 case 'x':
9143 /* we don't implement 'x'; treat like 'X' */
9144 case 'X':
9145 base = 16;
9146uns_number: /* an unsigned number */
9147 sign = 0;
9148#ifdef BSD4_4
9149 if (isquad)
9150 num = va_arg(ap, u_quad_t);
9151 else
9152#endif
9153 if (islong)
9154 num = va_arg(ap, unsigned long);
9155 else
9156 num = va_arg(ap, unsigned int);
9157number: /* process a number */
9158 p = temp + TEMPSIZE - 1;
9159 *p = '\0';
9160 while (num) {
9161 *--p = digit[num % base];
9162 num /= base;
9163 }
9164 len = (temp + TEMPSIZE - 1) - p;
9165 if (prec < 0)
9166 prec = 1;
9167 if (sharp && *f == 'o' && prec <= len)
9168 prec = len + 1;
9169 pad = 0;
9170 if (width) {
9171 size = len;
9172 if (size < prec)
9173 size = prec;
9174 size += sign;
9175 pad = width - size;
9176 if (flushleft == 0) {
9177 while (--pad >= 0)
9178 outc(' ', dest);
9179 }
9180 }
9181 if (sign)
9182 outc('-', dest);
9183 prec -= len;
9184 while (--prec >= 0)
9185 outc('0', dest);
9186 while (*p)
9187 outc(*p++, dest);
9188 while (--pad >= 0)
9189 outc(' ', dest);
9190 break;
9191 case 's':
9192 p = va_arg(ap, char *);
9193 pad = 0;
9194 if (width) {
9195 len = strlen(p);
9196 if (prec >= 0 && len > prec)
9197 len = prec;
9198 pad = width - len;
9199 if (flushleft == 0) {
9200 while (--pad >= 0)
9201 outc(' ', dest);
9202 }
9203 }
9204 prec++;
9205 while (--prec != 0 && *p)
9206 outc(*p++, dest);
9207 while (--pad >= 0)
9208 outc(' ', dest);
9209 break;
9210 case 'c':
9211 c = va_arg(ap, int);
9212 outc(c, dest);
9213 break;
9214 default:
9215 outc(*f, dest);
9216 break;
9217 }
9218 f++;
9219 }
9220#endif /* !HAVE_VASPRINTF */
9221}
9222#endif
9223
9224
9225
9226/*
9227 * Version of write which resumes after a signal is caught.
9228 */
9229
9230static int
9231xwrite(fd, buf, nbytes)
9232 int fd;
9233 const char *buf;
9234 int nbytes;
9235 {
9236 int ntry;
9237 int i;
9238 int n;
9239
9240 n = nbytes;
9241 ntry = 0;
9242 for (;;) {
9243 i = write(fd, buf, n);
9244 if (i > 0) {
9245 if ((n -= i) <= 0)
9246 return nbytes;
9247 buf += i;
9248 ntry = 0;
9249 } else if (i == 0) {
9250 if (++ntry > 10)
9251 return nbytes - n;
9252 } else if (errno != EINTR) {
9253 return -1;
9254 }
9255 }
9256}
9257
9258
9259#ifdef notdef
9260/*
9261 * Version of ioctl that retries after a signal is caught.
9262 * XXX unused function
9263 */
9264
9265static int
9266xioctl(fd, request, arg)
9267 int fd;
9268 unsigned long request;
9269 char * arg;
9270{
9271 int i;
9272
9273 while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR);
9274 return i;
9275}
9276#endif
9277
9278
9279#ifdef USE_GLIBC_STDIO
9280static void initstreams() {
9281 output.stream = stdout;
9282 errout.stream = stderr;
9283}
9284
9285
9286static void
9287openmemout() {
9288 INTOFF;
9289 memout.stream = open_memstream(&memout.buf, &memout.bufsize);
9290 INTON;
9291}
9292
9293
9294static int
9295__closememout() {
9296 int error;
9297 error = fclose(memout.stream);
9298 memout.stream = NULL;
9299 return error;
9300}
9301#endif
9302/* $NetBSD: parser.c,v 1.46 2001/02/04 19:52:06 christos Exp $ */
9303
Eric Andersencb57d552001-06-28 07:25:16 +00009304/*
9305 * Shell command parser.
9306 */
9307
9308#define EOFMARKLEN 79
9309
9310
9311
9312struct heredoc {
9313 struct heredoc *next; /* next here document in list */
9314 union node *here; /* redirection node */
9315 char *eofmark; /* string indicating end of input */
9316 int striptabs; /* if set, strip leading tabs */
9317};
9318
9319
9320
9321struct heredoc *heredoclist; /* list of here documents to read */
9322static int parsebackquote; /* nonzero if we are inside backquotes */
9323static int doprompt; /* if set, prompt the user */
9324static int needprompt; /* true if interactive and at start of line */
9325static int lasttoken; /* last token read */
9326static int tokpushback; /* last token pushed back */
9327static char *wordtext; /* text of last word returned by readtoken */
9328static int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */
9329/* 1 == check for aliases, 2 == also check for assignments */
9330static int checkalias;
9331struct nodelist *backquotelist;
9332union node *redirnode;
9333struct heredoc *heredoc;
9334static int quoteflag; /* set if (part of) last token was quoted */
9335static int startlinno; /* line # where last token started */
9336
9337
9338static union node *list __P((int));
9339static union node *andor __P((void));
9340static union node *pipeline __P((void));
9341static union node *command __P((void));
9342static union node *simplecmd __P((void));
9343static union node *makename __P((void));
9344static void parsefname __P((void));
9345static void parseheredoc __P((void));
9346static int peektoken __P((void));
9347static int readtoken __P((void));
9348static int xxreadtoken __P((void));
9349static int readtoken1 __P((int, char const *, char *, int));
9350static int noexpand __P((char *));
9351static void synexpect __P((int)) __attribute__((noreturn));
9352static void synerror __P((const char *)) __attribute__((noreturn));
9353static void setprompt __P((int));
9354
9355
9356/*
9357 * Read and parse a command. Returns NEOF on end of file. (NULL is a
9358 * valid parse tree indicating a blank line.)
9359 */
9360
9361union node *
9362parsecmd(int interact)
9363{
9364 int t;
9365
9366 tokpushback = 0;
9367 doprompt = interact;
9368 if (doprompt)
9369 setprompt(1);
9370 else
9371 setprompt(0);
9372 needprompt = 0;
9373 t = readtoken();
9374 if (t == TEOF)
9375 return NEOF;
9376 if (t == TNL)
9377 return NULL;
9378 tokpushback++;
9379 return list(1);
9380}
9381
9382
9383static union node *
9384list(nlflag)
9385 int nlflag;
9386{
9387 union node *n1, *n2, *n3;
9388 int tok;
9389
9390 checkkwd = 2;
9391 if (nlflag == 0 && tokendlist[peektoken()])
9392 return NULL;
9393 n1 = NULL;
9394 for (;;) {
9395 n2 = andor();
9396 tok = readtoken();
9397 if (tok == TBACKGND) {
9398 if (n2->type == NCMD || n2->type == NPIPE) {
9399 n2->ncmd.backgnd = 1;
9400 } else if (n2->type == NREDIR) {
9401 n2->type = NBACKGND;
9402 } else {
9403 n3 = (union node *)stalloc(sizeof (struct nredir));
9404 n3->type = NBACKGND;
9405 n3->nredir.n = n2;
9406 n3->nredir.redirect = NULL;
9407 n2 = n3;
9408 }
9409 }
9410 if (n1 == NULL) {
9411 n1 = n2;
9412 }
9413 else {
9414 n3 = (union node *)stalloc(sizeof (struct nbinary));
9415 n3->type = NSEMI;
9416 n3->nbinary.ch1 = n1;
9417 n3->nbinary.ch2 = n2;
9418 n1 = n3;
9419 }
9420 switch (tok) {
9421 case TBACKGND:
9422 case TSEMI:
9423 tok = readtoken();
9424 /* fall through */
9425 case TNL:
9426 if (tok == TNL) {
9427 parseheredoc();
9428 if (nlflag)
9429 return n1;
9430 } else {
9431 tokpushback++;
9432 }
9433 checkkwd = 2;
9434 if (tokendlist[peektoken()])
9435 return n1;
9436 break;
9437 case TEOF:
9438 if (heredoclist)
9439 parseheredoc();
9440 else
9441 pungetc(); /* push back EOF on input */
9442 return n1;
9443 default:
9444 if (nlflag)
9445 synexpect(-1);
9446 tokpushback++;
9447 return n1;
9448 }
9449 }
9450}
9451
9452
9453
9454static union node *
9455andor() {
9456 union node *n1, *n2, *n3;
9457 int t;
9458
9459 checkkwd = 1;
9460 n1 = pipeline();
9461 for (;;) {
9462 if ((t = readtoken()) == TAND) {
9463 t = NAND;
9464 } else if (t == TOR) {
9465 t = NOR;
9466 } else {
9467 tokpushback++;
9468 return n1;
9469 }
9470 checkkwd = 2;
9471 n2 = pipeline();
9472 n3 = (union node *)stalloc(sizeof (struct nbinary));
9473 n3->type = t;
9474 n3->nbinary.ch1 = n1;
9475 n3->nbinary.ch2 = n2;
9476 n1 = n3;
9477 }
9478}
9479
9480
9481
9482static union node *
9483pipeline() {
9484 union node *n1, *n2, *pipenode;
9485 struct nodelist *lp, *prev;
9486 int negate;
9487
9488 negate = 0;
9489 TRACE(("pipeline: entered\n"));
9490 if (readtoken() == TNOT) {
9491 negate = !negate;
9492 checkkwd = 1;
9493 } else
9494 tokpushback++;
9495 n1 = command();
9496 if (readtoken() == TPIPE) {
9497 pipenode = (union node *)stalloc(sizeof (struct npipe));
9498 pipenode->type = NPIPE;
9499 pipenode->npipe.backgnd = 0;
9500 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
9501 pipenode->npipe.cmdlist = lp;
9502 lp->n = n1;
9503 do {
9504 prev = lp;
9505 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
9506 checkkwd = 2;
9507 lp->n = command();
9508 prev->next = lp;
9509 } while (readtoken() == TPIPE);
9510 lp->next = NULL;
9511 n1 = pipenode;
9512 }
9513 tokpushback++;
9514 if (negate) {
9515 n2 = (union node *)stalloc(sizeof (struct nnot));
9516 n2->type = NNOT;
9517 n2->nnot.com = n1;
9518 return n2;
9519 } else
9520 return n1;
9521}
9522
9523
9524
9525static union node *
9526command() {
9527 union node *n1, *n2;
9528 union node *ap, **app;
9529 union node *cp, **cpp;
9530 union node *redir, **rpp;
9531 int t;
9532
9533 redir = NULL;
9534 n1 = NULL;
9535 rpp = &redir;
9536
9537 switch (readtoken()) {
9538 case TIF:
9539 n1 = (union node *)stalloc(sizeof (struct nif));
9540 n1->type = NIF;
9541 n1->nif.test = list(0);
9542 if (readtoken() != TTHEN)
9543 synexpect(TTHEN);
9544 n1->nif.ifpart = list(0);
9545 n2 = n1;
9546 while (readtoken() == TELIF) {
9547 n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
9548 n2 = n2->nif.elsepart;
9549 n2->type = NIF;
9550 n2->nif.test = list(0);
9551 if (readtoken() != TTHEN)
9552 synexpect(TTHEN);
9553 n2->nif.ifpart = list(0);
9554 }
9555 if (lasttoken == TELSE)
9556 n2->nif.elsepart = list(0);
9557 else {
9558 n2->nif.elsepart = NULL;
9559 tokpushback++;
9560 }
9561 if (readtoken() != TFI)
9562 synexpect(TFI);
9563 checkkwd = 1;
9564 break;
9565 case TWHILE:
9566 case TUNTIL: {
9567 int got;
9568 n1 = (union node *)stalloc(sizeof (struct nbinary));
9569 n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
9570 n1->nbinary.ch1 = list(0);
9571 if ((got=readtoken()) != TDO) {
9572TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
9573 synexpect(TDO);
9574 }
9575 n1->nbinary.ch2 = list(0);
9576 if (readtoken() != TDONE)
9577 synexpect(TDONE);
9578 checkkwd = 1;
9579 break;
9580 }
9581 case TFOR:
9582 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
9583 synerror("Bad for loop variable");
9584 n1 = (union node *)stalloc(sizeof (struct nfor));
9585 n1->type = NFOR;
9586 n1->nfor.var = wordtext;
9587 checkkwd = 1;
9588 if (readtoken() == TIN) {
9589 app = &ap;
9590 while (readtoken() == TWORD) {
9591 n2 = (union node *)stalloc(sizeof (struct narg));
9592 n2->type = NARG;
9593 n2->narg.text = wordtext;
9594 n2->narg.backquote = backquotelist;
9595 *app = n2;
9596 app = &n2->narg.next;
9597 }
9598 *app = NULL;
9599 n1->nfor.args = ap;
9600 if (lasttoken != TNL && lasttoken != TSEMI)
9601 synexpect(-1);
9602 } else {
9603 static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
9604 '@', '=', '\0'};
9605 n2 = (union node *)stalloc(sizeof (struct narg));
9606 n2->type = NARG;
9607 n2->narg.text = argvars;
9608 n2->narg.backquote = NULL;
9609 n2->narg.next = NULL;
9610 n1->nfor.args = n2;
9611 /*
9612 * Newline or semicolon here is optional (but note
9613 * that the original Bourne shell only allowed NL).
9614 */
9615 if (lasttoken != TNL && lasttoken != TSEMI)
9616 tokpushback++;
9617 }
9618 checkkwd = 2;
9619 if (readtoken() != TDO)
9620 synexpect(TDO);
9621 n1->nfor.body = list(0);
9622 if (readtoken() != TDONE)
9623 synexpect(TDONE);
9624 checkkwd = 1;
9625 break;
9626 case TCASE:
9627 n1 = (union node *)stalloc(sizeof (struct ncase));
9628 n1->type = NCASE;
9629 if (readtoken() != TWORD)
9630 synexpect(TWORD);
9631 n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
9632 n2->type = NARG;
9633 n2->narg.text = wordtext;
9634 n2->narg.backquote = backquotelist;
9635 n2->narg.next = NULL;
9636 do {
9637 checkkwd = 1;
9638 } while (readtoken() == TNL);
9639 if (lasttoken != TIN)
9640 synerror("expecting \"in\"");
9641 cpp = &n1->ncase.cases;
9642 checkkwd = 2, readtoken();
9643 do {
9644 if (lasttoken == TLP)
9645 readtoken();
9646 *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
9647 cp->type = NCLIST;
9648 app = &cp->nclist.pattern;
9649 for (;;) {
9650 *app = ap = (union node *)stalloc(sizeof (struct narg));
9651 ap->type = NARG;
9652 ap->narg.text = wordtext;
9653 ap->narg.backquote = backquotelist;
9654 if (checkkwd = 2, readtoken() != TPIPE)
9655 break;
9656 app = &ap->narg.next;
9657 readtoken();
9658 }
9659 ap->narg.next = NULL;
9660 if (lasttoken != TRP)
9661 synexpect(TRP);
9662 cp->nclist.body = list(0);
9663
9664 checkkwd = 2;
9665 if ((t = readtoken()) != TESAC) {
9666 if (t != TENDCASE)
9667 synexpect(TENDCASE);
9668 else
9669 checkkwd = 2, readtoken();
9670 }
9671 cpp = &cp->nclist.next;
9672 } while(lasttoken != TESAC);
9673 *cpp = NULL;
9674 checkkwd = 1;
9675 break;
9676 case TLP:
9677 n1 = (union node *)stalloc(sizeof (struct nredir));
9678 n1->type = NSUBSHELL;
9679 n1->nredir.n = list(0);
9680 n1->nredir.redirect = NULL;
9681 if (readtoken() != TRP)
9682 synexpect(TRP);
9683 checkkwd = 1;
9684 break;
9685 case TBEGIN:
9686 n1 = list(0);
9687 if (readtoken() != TEND)
9688 synexpect(TEND);
9689 checkkwd = 1;
9690 break;
9691 /* Handle an empty command like other simple commands. */
9692 case TSEMI:
9693 case TAND:
9694 case TOR:
9695 case TNL:
9696 case TEOF:
9697 case TRP:
9698 case TBACKGND:
9699 /*
9700 * An empty command before a ; doesn't make much sense, and
9701 * should certainly be disallowed in the case of `if ;'.
9702 */
9703 if (!redir)
9704 synexpect(-1);
9705 case TWORD:
9706 case TREDIR:
9707 tokpushback++;
9708 n1 = simplecmd();
9709 return n1;
9710 default:
9711 synexpect(-1);
9712 /* NOTREACHED */
9713 }
9714
9715 /* Now check for redirection which may follow command */
9716 while (readtoken() == TREDIR) {
9717 *rpp = n2 = redirnode;
9718 rpp = &n2->nfile.next;
9719 parsefname();
9720 }
9721 tokpushback++;
9722 *rpp = NULL;
9723 if (redir) {
9724 if (n1->type != NSUBSHELL) {
9725 n2 = (union node *)stalloc(sizeof (struct nredir));
9726 n2->type = NREDIR;
9727 n2->nredir.n = n1;
9728 n1 = n2;
9729 }
9730 n1->nredir.redirect = redir;
9731 }
9732
9733 return n1;
9734}
9735
9736
9737static union node *
9738simplecmd() {
9739 union node *args, **app;
9740 union node *n = NULL;
9741 union node *vars, **vpp;
9742 union node **rpp, *redir;
9743
9744 args = NULL;
9745 app = &args;
9746 vars = NULL;
9747 vpp = &vars;
9748 redir = NULL;
9749 rpp = &redir;
9750
9751 checkalias = 2;
9752 for (;;) {
9753 switch (readtoken()) {
9754 case TWORD:
9755 case TASSIGN:
9756 n = (union node *)stalloc(sizeof (struct narg));
9757 n->type = NARG;
9758 n->narg.text = wordtext;
9759 n->narg.backquote = backquotelist;
9760 if (lasttoken == TWORD) {
9761 *app = n;
9762 app = &n->narg.next;
9763 } else {
9764 *vpp = n;
9765 vpp = &n->narg.next;
9766 }
9767 break;
9768 case TREDIR:
9769 *rpp = n = redirnode;
9770 rpp = &n->nfile.next;
9771 parsefname(); /* read name of redirection file */
9772 break;
9773 case TLP:
9774 if (
9775 args && app == &args->narg.next &&
9776 !vars && !redir
9777 ) {
9778 /* We have a function */
9779 if (readtoken() != TRP)
9780 synexpect(TRP);
9781#ifdef notdef
9782 if (! goodname(n->narg.text))
9783 synerror("Bad function name");
9784#endif
9785 n->type = NDEFUN;
9786 checkkwd = 2;
9787 n->narg.next = command();
9788 return n;
9789 }
9790 /* fall through */
9791 default:
9792 tokpushback++;
9793 goto out;
9794 }
9795 }
9796out:
9797 *app = NULL;
9798 *vpp = NULL;
9799 *rpp = NULL;
9800 n = (union node *)stalloc(sizeof (struct ncmd));
9801 n->type = NCMD;
9802 n->ncmd.backgnd = 0;
9803 n->ncmd.args = args;
9804 n->ncmd.assign = vars;
9805 n->ncmd.redirect = redir;
9806 return n;
9807}
9808
9809static union node *
9810makename() {
9811 union node *n;
9812
9813 n = (union node *)stalloc(sizeof (struct narg));
9814 n->type = NARG;
9815 n->narg.next = NULL;
9816 n->narg.text = wordtext;
9817 n->narg.backquote = backquotelist;
9818 return n;
9819}
9820
9821static void fixredir(union node *n, const char *text, int err)
9822 {
9823 TRACE(("Fix redir %s %d\n", text, err));
9824 if (!err)
9825 n->ndup.vname = NULL;
9826
9827 if (is_digit(text[0]) && text[1] == '\0')
9828 n->ndup.dupfd = digit_val(text[0]);
9829 else if (text[0] == '-' && text[1] == '\0')
9830 n->ndup.dupfd = -1;
9831 else {
9832
9833 if (err)
9834 synerror("Bad fd number");
9835 else
9836 n->ndup.vname = makename();
9837 }
9838}
9839
9840
9841static void
9842parsefname() {
9843 union node *n = redirnode;
9844
9845 if (readtoken() != TWORD)
9846 synexpect(-1);
9847 if (n->type == NHERE) {
9848 struct heredoc *here = heredoc;
9849 struct heredoc *p;
9850 int i;
9851
9852 if (quoteflag == 0)
9853 n->type = NXHERE;
9854 TRACE(("Here document %d\n", n->type));
9855 if (here->striptabs) {
9856 while (*wordtext == '\t')
9857 wordtext++;
9858 }
9859 if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
9860 synerror("Illegal eof marker for << redirection");
9861 rmescapes(wordtext);
9862 here->eofmark = wordtext;
9863 here->next = NULL;
9864 if (heredoclist == NULL)
9865 heredoclist = here;
9866 else {
9867 for (p = heredoclist ; p->next ; p = p->next);
9868 p->next = here;
9869 }
9870 } else if (n->type == NTOFD || n->type == NFROMFD) {
9871 fixredir(n, wordtext, 0);
9872 } else {
9873 n->nfile.fname = makename();
9874 }
9875}
9876
9877
9878/*
9879 * Input any here documents.
9880 */
9881
9882static void
9883parseheredoc() {
9884 struct heredoc *here;
9885 union node *n;
9886
9887 while (heredoclist) {
9888 here = heredoclist;
9889 heredoclist = here->next;
9890 if (needprompt) {
9891 setprompt(2);
9892 needprompt = 0;
9893 }
9894 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
9895 here->eofmark, here->striptabs);
9896 n = (union node *)stalloc(sizeof (struct narg));
9897 n->narg.type = NARG;
9898 n->narg.next = NULL;
9899 n->narg.text = wordtext;
9900 n->narg.backquote = backquotelist;
9901 here->here->nhere.doc = n;
9902 }
9903}
9904
9905static int
9906peektoken() {
9907 int t;
9908
9909 t = readtoken();
9910 tokpushback++;
9911 return (t);
9912}
9913
9914static int
9915readtoken() {
9916 int t;
9917 int savecheckkwd = checkkwd;
9918 int savecheckalias = checkalias;
9919 struct alias *ap;
9920#ifdef DEBUG
9921 int alreadyseen = tokpushback;
9922#endif
9923
9924top:
9925 t = xxreadtoken();
9926 checkalias = savecheckalias;
9927
9928 if (checkkwd) {
9929 /*
9930 * eat newlines
9931 */
9932 if (checkkwd == 2) {
9933 checkkwd = 0;
9934 while (t == TNL) {
9935 parseheredoc();
9936 t = xxreadtoken();
9937 }
9938 }
9939 checkkwd = 0;
9940 /*
9941 * check for keywords
9942 */
9943 if (t == TWORD && !quoteflag)
9944 {
9945 const char *const *pp;
9946
9947 if ((pp = findkwd(wordtext))) {
9948 lasttoken = t = pp - parsekwd + KWDOFFSET;
9949 TRACE(("keyword %s recognized\n", tokname[t]));
9950 goto out;
9951 }
9952 }
9953 }
9954
9955 if (t != TWORD) {
9956 if (t != TREDIR) {
9957 checkalias = 0;
9958 }
9959 } else if (checkalias == 2 && isassignment(wordtext)) {
9960 lasttoken = t = TASSIGN;
9961 } else if (checkalias) {
9962 if (!quoteflag && (ap = lookupalias(wordtext, 1)) != NULL) {
9963 if (*ap->val) {
9964 pushstring(ap->val, strlen(ap->val), ap);
9965 }
9966 checkkwd = savecheckkwd;
9967 goto top;
9968 }
9969 checkalias = 0;
9970 }
9971out:
9972#ifdef DEBUG
9973 if (!alreadyseen)
9974 TRACE(("token %s %s\n", tokname[t], t == TWORD || t == TASSIGN ? wordtext : ""));
9975 else
9976 TRACE(("reread token %s %s\n", tokname[t], t == TWORD || t == TASSIGN ? wordtext : ""));
9977#endif
9978 return (t);
9979}
9980
9981
9982/*
9983 * Read the next input token.
9984 * If the token is a word, we set backquotelist to the list of cmds in
9985 * backquotes. We set quoteflag to true if any part of the word was
9986 * quoted.
9987 * If the token is TREDIR, then we set redirnode to a structure containing
9988 * the redirection.
9989 * In all cases, the variable startlinno is set to the number of the line
9990 * on which the token starts.
9991 *
9992 * [Change comment: here documents and internal procedures]
9993 * [Readtoken shouldn't have any arguments. Perhaps we should make the
9994 * word parsing code into a separate routine. In this case, readtoken
9995 * doesn't need to have any internal procedures, but parseword does.
9996 * We could also make parseoperator in essence the main routine, and
9997 * have parseword (readtoken1?) handle both words and redirection.]
9998 */
9999
10000#define RETURN(token) return lasttoken = token
10001
10002static int
10003xxreadtoken() {
10004 int c;
10005
10006 if (tokpushback) {
10007 tokpushback = 0;
10008 return lasttoken;
10009 }
10010 if (needprompt) {
10011 setprompt(2);
10012 needprompt = 0;
10013 }
10014 startlinno = plinno;
10015 for (;;) { /* until token or start of word found */
10016 c = pgetc_macro();
10017 switch (c) {
10018 case ' ': case '\t':
10019 case PEOA:
10020 continue;
10021 case '#':
10022 while ((c = pgetc()) != '\n' && c != PEOF);
10023 pungetc();
10024 continue;
10025 case '\\':
10026 if (pgetc() == '\n') {
10027 startlinno = ++plinno;
10028 if (doprompt)
10029 setprompt(2);
10030 else
10031 setprompt(0);
10032 continue;
10033 }
10034 pungetc();
10035 goto breakloop;
10036 case '\n':
10037 plinno++;
10038 needprompt = doprompt;
10039 RETURN(TNL);
10040 case PEOF:
10041 RETURN(TEOF);
10042 case '&':
10043 if (pgetc() == '&')
10044 RETURN(TAND);
10045 pungetc();
10046 RETURN(TBACKGND);
10047 case '|':
10048 if (pgetc() == '|')
10049 RETURN(TOR);
10050 pungetc();
10051 RETURN(TPIPE);
10052 case ';':
10053 if (pgetc() == ';')
10054 RETURN(TENDCASE);
10055 pungetc();
10056 RETURN(TSEMI);
10057 case '(':
10058 RETURN(TLP);
10059 case ')':
10060 RETURN(TRP);
10061 default:
10062 goto breakloop;
10063 }
10064 }
10065breakloop:
10066 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
10067#undef RETURN
10068}
10069
10070
10071
10072/*
10073 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
10074 * is not NULL, read a here document. In the latter case, eofmark is the
10075 * word which marks the end of the document and striptabs is true if
10076 * leading tabs should be stripped from the document. The argument firstc
10077 * is the first character of the input token or document.
10078 *
10079 * Because C does not have internal subroutines, I have simulated them
10080 * using goto's to implement the subroutine linkage. The following macros
10081 * will run code that appears at the end of readtoken1.
10082 */
10083
10084#define CHECKEND() {goto checkend; checkend_return:;}
10085#define PARSEREDIR() {goto parseredir; parseredir_return:;}
10086#define PARSESUB() {goto parsesub; parsesub_return:;}
10087#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
10088#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
10089#define PARSEARITH() {goto parsearith; parsearith_return:;}
10090
10091static int
10092readtoken1(firstc, syntax, eofmark, striptabs)
10093 int firstc;
10094 char const *syntax;
10095 char *eofmark;
10096 int striptabs;
10097 {
10098 int c = firstc;
10099 char *out;
10100 int len;
10101 char line[EOFMARKLEN + 1];
10102 struct nodelist *bqlist;
10103 int quotef;
10104 int dblquote;
10105 int varnest; /* levels of variables expansion */
10106 int arinest; /* levels of arithmetic expansion */
10107 int parenlevel; /* levels of parens in arithmetic */
10108 int dqvarnest; /* levels of variables expansion within double quotes */
10109 int oldstyle;
10110 char const *prevsyntax; /* syntax before arithmetic */
10111#if __GNUC__
10112 /* Avoid longjmp clobbering */
10113 (void) &out;
10114 (void) &quotef;
10115 (void) &dblquote;
10116 (void) &varnest;
10117 (void) &arinest;
10118 (void) &parenlevel;
10119 (void) &dqvarnest;
10120 (void) &oldstyle;
10121 (void) &prevsyntax;
10122 (void) &syntax;
10123#endif
10124
10125 startlinno = plinno;
10126 dblquote = 0;
10127 if (syntax == DQSYNTAX)
10128 dblquote = 1;
10129 quotef = 0;
10130 bqlist = NULL;
10131 varnest = 0;
10132 arinest = 0;
10133 parenlevel = 0;
10134 dqvarnest = 0;
10135
10136 STARTSTACKSTR(out);
10137 loop: { /* for each line, until end of word */
10138#if ATTY
10139 if (c == '\034' && doprompt
10140 && attyset() && ! equal(termval(), "emacs")) {
10141 attyline();
10142 if (syntax == BASESYNTAX)
10143 return readtoken();
10144 c = pgetc();
10145 goto loop;
10146 }
10147#endif
10148 CHECKEND(); /* set c to PEOF if at end of here document */
10149 for (;;) { /* until end of line or end of word */
10150 CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */
10151 switch(syntax[c]) {
10152 case CNL: /* '\n' */
10153 if (syntax == BASESYNTAX)
10154 goto endword; /* exit outer loop */
10155 USTPUTC(c, out);
10156 plinno++;
10157 if (doprompt)
10158 setprompt(2);
10159 else
10160 setprompt(0);
10161 c = pgetc();
10162 goto loop; /* continue outer loop */
10163 case CWORD:
10164 USTPUTC(c, out);
10165 break;
10166 case CCTL:
10167 if ((eofmark == NULL || dblquote) &&
10168 dqvarnest == 0)
10169 USTPUTC(CTLESC, out);
10170 USTPUTC(c, out);
10171 break;
10172 case CBACK: /* backslash */
10173 c = pgetc2();
10174 if (c == PEOF) {
10175 USTPUTC('\\', out);
10176 pungetc();
10177 } else if (c == '\n') {
10178 if (doprompt)
10179 setprompt(2);
10180 else
10181 setprompt(0);
10182 } else {
10183 if (dblquote && c != '\\' && c != '`' && c != '$'
10184 && (c != '"' || eofmark != NULL))
10185 USTPUTC('\\', out);
10186 if (SQSYNTAX[c] == CCTL)
10187 USTPUTC(CTLESC, out);
10188 else if (eofmark == NULL)
10189 USTPUTC(CTLQUOTEMARK, out);
10190 USTPUTC(c, out);
10191 quotef++;
10192 }
10193 break;
10194 case CSQUOTE:
10195 if (eofmark == NULL)
10196 USTPUTC(CTLQUOTEMARK, out);
10197 syntax = SQSYNTAX;
10198 break;
10199 case CDQUOTE:
10200 if (eofmark == NULL)
10201 USTPUTC(CTLQUOTEMARK, out);
10202 syntax = DQSYNTAX;
10203 dblquote = 1;
10204 break;
10205 case CENDQUOTE:
10206 if (eofmark != NULL && arinest == 0 &&
10207 varnest == 0) {
10208 USTPUTC(c, out);
10209 } else {
10210 if (arinest) {
10211 syntax = ARISYNTAX;
10212 dblquote = 0;
10213 } else if (eofmark == NULL &&
10214 dqvarnest == 0) {
10215 syntax = BASESYNTAX;
10216 dblquote = 0;
10217 }
10218 quotef++;
10219 }
10220 break;
10221 case CVAR: /* '$' */
10222 PARSESUB(); /* parse substitution */
10223 break;
10224 case CENDVAR: /* '}' */
10225 if (varnest > 0) {
10226 varnest--;
10227 if (dqvarnest > 0) {
10228 dqvarnest--;
10229 }
10230 USTPUTC(CTLENDVAR, out);
10231 } else {
10232 USTPUTC(c, out);
10233 }
10234 break;
10235#ifdef ASH_MATH_SUPPORT
10236 case CLP: /* '(' in arithmetic */
10237 parenlevel++;
10238 USTPUTC(c, out);
10239 break;
10240 case CRP: /* ')' in arithmetic */
10241 if (parenlevel > 0) {
10242 USTPUTC(c, out);
10243 --parenlevel;
10244 } else {
10245 if (pgetc() == ')') {
10246 if (--arinest == 0) {
10247 USTPUTC(CTLENDARI, out);
10248 syntax = prevsyntax;
10249 if (syntax == DQSYNTAX)
10250 dblquote = 1;
10251 else
10252 dblquote = 0;
10253 } else
10254 USTPUTC(')', out);
10255 } else {
10256 /*
10257 * unbalanced parens
10258 * (don't 2nd guess - no error)
10259 */
10260 pungetc();
10261 USTPUTC(')', out);
10262 }
10263 }
10264 break;
10265#endif
10266 case CBQUOTE: /* '`' */
10267 PARSEBACKQOLD();
10268 break;
10269 case CEOF:
10270 goto endword; /* exit outer loop */
10271 case CIGN:
10272 break;
10273 default:
10274 if (varnest == 0)
10275 goto endword; /* exit outer loop */
10276 if (c != PEOA) {
10277 USTPUTC(c, out);
10278 }
10279 }
10280 c = pgetc_macro();
10281 }
10282 }
10283endword:
10284 if (syntax == ARISYNTAX)
10285 synerror("Missing '))'");
10286 if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
10287 synerror("Unterminated quoted string");
10288 if (varnest != 0) {
10289 startlinno = plinno;
10290 synerror("Missing '}'");
10291 }
10292 USTPUTC('\0', out);
10293 len = out - stackblock();
10294 out = stackblock();
10295 if (eofmark == NULL) {
10296 if ((c == '>' || c == '<')
10297 && quotef == 0
10298 && len <= 2
10299 && (*out == '\0' || is_digit(*out))) {
10300 PARSEREDIR();
10301 return lasttoken = TREDIR;
10302 } else {
10303 pungetc();
10304 }
10305 }
10306 quoteflag = quotef;
10307 backquotelist = bqlist;
10308 grabstackblock(len);
10309 wordtext = out;
10310 return lasttoken = TWORD;
10311/* end of readtoken routine */
10312
10313
10314
10315/*
10316 * Check to see whether we are at the end of the here document. When this
10317 * is called, c is set to the first character of the next input line. If
10318 * we are at the end of the here document, this routine sets the c to PEOF.
10319 */
10320
10321checkend: {
10322 if (eofmark) {
10323 if (c == PEOA) {
10324 c = pgetc2();
10325 }
10326 if (striptabs) {
10327 while (c == '\t') {
10328 c = pgetc2();
10329 }
10330 }
10331 if (c == *eofmark) {
10332 if (pfgets(line, sizeof line) != NULL) {
10333 char *p, *q;
10334
10335 p = line;
10336 for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
10337 if (*p == '\n' && *q == '\0') {
10338 c = PEOF;
10339 plinno++;
10340 needprompt = doprompt;
10341 } else {
10342 pushstring(line, strlen(line), NULL);
10343 }
10344 }
10345 }
10346 }
10347 goto checkend_return;
10348}
10349
10350
10351/*
10352 * Parse a redirection operator. The variable "out" points to a string
10353 * specifying the fd to be redirected. The variable "c" contains the
10354 * first character of the redirection operator.
10355 */
10356
10357parseredir: {
10358 char fd = *out;
10359 union node *np;
10360
10361 np = (union node *)stalloc(sizeof (struct nfile));
10362 if (c == '>') {
10363 np->nfile.fd = 1;
10364 c = pgetc();
10365 if (c == '>')
10366 np->type = NAPPEND;
10367 else if (c == '&')
10368 np->type = NTOFD;
10369 else if (c == '|')
10370 np->type = NTOOV;
10371 else {
10372 np->type = NTO;
10373 pungetc();
10374 }
10375 } else { /* c == '<' */
10376 np->nfile.fd = 0;
10377 switch (c = pgetc()) {
10378 case '<':
10379 if (sizeof (struct nfile) != sizeof (struct nhere)) {
10380 np = (union node *)stalloc(sizeof (struct nhere));
10381 np->nfile.fd = 0;
10382 }
10383 np->type = NHERE;
10384 heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
10385 heredoc->here = np;
10386 if ((c = pgetc()) == '-') {
10387 heredoc->striptabs = 1;
10388 } else {
10389 heredoc->striptabs = 0;
10390 pungetc();
10391 }
10392 break;
10393
10394 case '&':
10395 np->type = NFROMFD;
10396 break;
10397
10398 case '>':
10399 np->type = NFROMTO;
10400 break;
10401
10402 default:
10403 np->type = NFROM;
10404 pungetc();
10405 break;
10406 }
10407 }
10408 if (fd != '\0')
10409 np->nfile.fd = digit_val(fd);
10410 redirnode = np;
10411 goto parseredir_return;
10412}
10413
10414
10415/*
10416 * Parse a substitution. At this point, we have read the dollar sign
10417 * and nothing else.
10418 */
10419
10420parsesub: {
10421 int subtype;
10422 int typeloc;
10423 int flags;
10424 char *p;
10425 static const char types[] = "}-+?=";
10426
10427 c = pgetc();
10428 if (
10429 c <= PEOA ||
10430 (c != '(' && c != '{' && !is_name(c) && !is_special(c))
10431 ) {
10432 USTPUTC('$', out);
10433 pungetc();
10434 } else if (c == '(') { /* $(command) or $((arith)) */
10435 if (pgetc() == '(') {
10436 PARSEARITH();
10437 } else {
10438 pungetc();
10439 PARSEBACKQNEW();
10440 }
10441 } else {
10442 USTPUTC(CTLVAR, out);
10443 typeloc = out - stackblock();
10444 USTPUTC(VSNORMAL, out);
10445 subtype = VSNORMAL;
10446 if (c == '{') {
10447 c = pgetc();
10448 if (c == '#') {
10449 if ((c = pgetc()) == '}')
10450 c = '#';
10451 else
10452 subtype = VSLENGTH;
10453 }
10454 else
10455 subtype = 0;
10456 }
10457 if (c > PEOA && is_name(c)) {
10458 do {
10459 STPUTC(c, out);
10460 c = pgetc();
10461 } while (c > PEOA && is_in_name(c));
10462 } else if (is_digit(c)) {
10463 do {
10464 USTPUTC(c, out);
10465 c = pgetc();
10466 } while (is_digit(c));
10467 }
10468 else if (is_special(c)) {
10469 USTPUTC(c, out);
10470 c = pgetc();
10471 }
10472 else
10473badsub: synerror("Bad substitution");
10474
10475 STPUTC('=', out);
10476 flags = 0;
10477 if (subtype == 0) {
10478 switch (c) {
10479 case ':':
10480 flags = VSNUL;
10481 c = pgetc();
10482 /*FALLTHROUGH*/
10483 default:
10484 p = strchr(types, c);
10485 if (p == NULL)
10486 goto badsub;
10487 subtype = p - types + VSNORMAL;
10488 break;
10489 case '%':
10490 case '#':
10491 {
10492 int cc = c;
10493 subtype = c == '#' ? VSTRIMLEFT :
10494 VSTRIMRIGHT;
10495 c = pgetc();
10496 if (c == cc)
10497 subtype++;
10498 else
10499 pungetc();
10500 break;
10501 }
10502 }
10503 } else {
10504 pungetc();
10505 }
10506 if (dblquote || arinest)
10507 flags |= VSQUOTE;
10508 *(stackblock() + typeloc) = subtype | flags;
10509 if (subtype != VSNORMAL) {
10510 varnest++;
10511 if (dblquote) {
10512 dqvarnest++;
10513 }
10514 }
10515 }
10516 goto parsesub_return;
10517}
10518
10519
10520/*
10521 * Called to parse command substitutions. Newstyle is set if the command
10522 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
10523 * list of commands (passed by reference), and savelen is the number of
10524 * characters on the top of the stack which must be preserved.
10525 */
10526
10527parsebackq: {
10528 struct nodelist **nlpp;
10529 int savepbq;
10530 union node *n;
10531 char *volatile str;
10532 struct jmploc jmploc;
10533 struct jmploc *volatile savehandler;
10534 int savelen;
10535 int saveprompt;
10536#ifdef __GNUC__
10537 (void) &saveprompt;
10538#endif
10539
10540 savepbq = parsebackquote;
10541 if (setjmp(jmploc.loc)) {
10542 if (str)
10543 ckfree(str);
10544 parsebackquote = 0;
10545 handler = savehandler;
10546 longjmp(handler->loc, 1);
10547 }
10548 INTOFF;
10549 str = NULL;
10550 savelen = out - stackblock();
10551 if (savelen > 0) {
10552 str = ckmalloc(savelen);
10553 memcpy(str, stackblock(), savelen);
10554 }
10555 savehandler = handler;
10556 handler = &jmploc;
10557 INTON;
10558 if (oldstyle) {
10559 /* We must read until the closing backquote, giving special
10560 treatment to some slashes, and then push the string and
10561 reread it as input, interpreting it normally. */
10562 char *pout;
10563 int pc;
10564 int psavelen;
10565 char *pstr;
10566
10567
10568 STARTSTACKSTR(pout);
10569 for (;;) {
10570 if (needprompt) {
10571 setprompt(2);
10572 needprompt = 0;
10573 }
10574 switch (pc = pgetc()) {
10575 case '`':
10576 goto done;
10577
10578 case '\\':
10579 if ((pc = pgetc()) == '\n') {
10580 plinno++;
10581 if (doprompt)
10582 setprompt(2);
10583 else
10584 setprompt(0);
10585 /*
10586 * If eating a newline, avoid putting
10587 * the newline into the new character
10588 * stream (via the STPUTC after the
10589 * switch).
10590 */
10591 continue;
10592 }
10593 if (pc != '\\' && pc != '`' && pc != '$'
10594 && (!dblquote || pc != '"'))
10595 STPUTC('\\', pout);
10596 if (pc > PEOA) {
10597 break;
10598 }
10599 /* fall through */
10600
10601 case PEOF:
10602 case PEOA:
10603 startlinno = plinno;
10604 synerror("EOF in backquote substitution");
10605
10606 case '\n':
10607 plinno++;
10608 needprompt = doprompt;
10609 break;
10610
10611 default:
10612 break;
10613 }
10614 STPUTC(pc, pout);
10615 }
10616done:
10617 STPUTC('\0', pout);
10618 psavelen = pout - stackblock();
10619 if (psavelen > 0) {
10620 pstr = grabstackstr(pout);
10621 setinputstring(pstr);
10622 }
10623 }
10624 nlpp = &bqlist;
10625 while (*nlpp)
10626 nlpp = &(*nlpp)->next;
10627 *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
10628 (*nlpp)->next = NULL;
10629 parsebackquote = oldstyle;
10630
10631 if (oldstyle) {
10632 saveprompt = doprompt;
10633 doprompt = 0;
10634 }
10635
10636 n = list(0);
10637
10638 if (oldstyle)
10639 doprompt = saveprompt;
10640 else {
10641 if (readtoken() != TRP)
10642 synexpect(TRP);
10643 }
10644
10645 (*nlpp)->n = n;
10646 if (oldstyle) {
10647 /*
10648 * Start reading from old file again, ignoring any pushed back
10649 * tokens left from the backquote parsing
10650 */
10651 popfile();
10652 tokpushback = 0;
10653 }
10654 while (stackblocksize() <= savelen)
10655 growstackblock();
10656 STARTSTACKSTR(out);
10657 if (str) {
10658 memcpy(out, str, savelen);
10659 STADJUST(savelen, out);
10660 INTOFF;
10661 ckfree(str);
10662 str = NULL;
10663 INTON;
10664 }
10665 parsebackquote = savepbq;
10666 handler = savehandler;
10667 if (arinest || dblquote)
10668 USTPUTC(CTLBACKQ | CTLQUOTE, out);
10669 else
10670 USTPUTC(CTLBACKQ, out);
10671 if (oldstyle)
10672 goto parsebackq_oldreturn;
10673 else
10674 goto parsebackq_newreturn;
10675}
10676
10677/*
10678 * Parse an arithmetic expansion (indicate start of one and set state)
10679 */
10680parsearith: {
10681
10682 if (++arinest == 1) {
10683 prevsyntax = syntax;
10684 syntax = ARISYNTAX;
10685 USTPUTC(CTLARI, out);
10686 if (dblquote)
10687 USTPUTC('"',out);
10688 else
10689 USTPUTC(' ',out);
10690 } else {
10691 /*
10692 * we collapse embedded arithmetic expansion to
10693 * parenthesis, which should be equivalent
10694 */
10695 USTPUTC('(', out);
10696 }
10697 goto parsearith_return;
10698}
10699
10700} /* end of readtoken */
10701
10702
10703
10704#ifdef mkinit
10705INCLUDE "parser.h"
10706RESET {
10707 tokpushback = 0;
10708 checkkwd = 0;
10709 checkalias = 0;
10710}
10711#endif
10712
10713/*
10714 * Returns true if the text contains nothing to expand (no dollar signs
10715 * or backquotes).
10716 */
10717
10718static int
10719noexpand(text)
10720 char *text;
10721 {
10722 char *p;
10723 char c;
10724
10725 p = text;
10726 while ((c = *p++) != '\0') {
10727 if (c == CTLQUOTEMARK)
10728 continue;
10729 if (c == CTLESC)
10730 p++;
10731 else if (BASESYNTAX[(int)c] == CCTL)
10732 return 0;
10733 }
10734 return 1;
10735}
10736
10737
10738/*
10739 * Return true if the argument is a legal variable name (a letter or
10740 * underscore followed by zero or more letters, underscores, and digits).
10741 */
10742
10743static int
10744goodname(char *name)
10745 {
10746 char *p;
10747
10748 p = name;
10749 if (! is_name(*p))
10750 return 0;
10751 while (*++p) {
10752 if (! is_in_name(*p))
10753 return 0;
10754 }
10755 return 1;
10756}
10757
10758
10759/*
10760 * Called when an unexpected token is read during the parse. The argument
10761 * is the token that is expected, or -1 if more than one type of token can
10762 * occur at this point.
10763 */
10764
10765static void
10766synexpect(token)
10767 int token;
10768{
10769 char msg[64];
10770
10771 if (token >= 0) {
10772 fmtstr(msg, 64, "%s unexpected (expecting %s)",
10773 tokname[lasttoken], tokname[token]);
10774 } else {
10775 fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
10776 }
10777 synerror(msg);
10778 /* NOTREACHED */
10779}
10780
10781
10782static void
10783synerror(msg)
10784 const char *msg;
10785 {
10786 if (commandname)
10787 outfmt(&errout, "%s: %d: ", commandname, startlinno);
10788 outfmt(&errout, "Syntax error: %s\n", msg);
10789 error((char *)NULL);
10790 /* NOTREACHED */
10791}
10792
10793static void
10794setprompt(int which)
10795{
10796 whichprompt = which;
10797 putprompt(getprompt(NULL));
10798}
10799
10800/*
10801 * called by editline -- any expansions to the prompt
10802 * should be added here.
10803 */
10804static const char *
10805getprompt(void *unused)
10806 {
10807 switch (whichprompt) {
10808 case 0:
10809 return "";
10810 case 1:
10811 return ps1val();
10812 case 2:
10813 return ps2val();
10814 default:
10815 return "<internal prompt error>";
10816 }
10817}
10818
10819static int
10820isassignment(const char *word) {
10821 if (!is_name(*word)) {
10822 return 0;
10823 }
10824 do {
10825 word++;
10826 } while (is_in_name(*word));
10827 return *word == '=';
10828}
10829
10830static const char *const *
10831findkwd(const char *s) {
10832 return findstring(
10833 s, parsekwd, sizeof(parsekwd) / sizeof(const char *)
10834 );
10835}
10836/* $NetBSD: redir.c,v 1.22 2000/05/22 10:18:47 elric Exp $ */
10837
Eric Andersencb57d552001-06-28 07:25:16 +000010838/*
10839 * Code for dealing with input/output redirection.
10840 */
10841
10842#define EMPTY -2 /* marks an unused slot in redirtab */
10843#ifndef PIPE_BUF
10844# define PIPESIZE 4096 /* amount of buffering in a pipe */
10845#else
10846# define PIPESIZE PIPE_BUF
10847#endif
10848
10849
10850struct redirtab *redirlist;
10851
10852/*
10853 * We keep track of whether or not fd0 has been redirected. This is for
10854 * background commands, where we want to redirect fd0 to /dev/null only
10855 * if it hasn't already been redirected.
10856*/
10857static int fd0_redirected = 0;
10858
10859/*
10860 * We also keep track of where fileno2 goes.
10861 */
10862static int fileno2 = 2;
10863
10864static int openredirect __P((union node *));
10865static void dupredirect __P((union node *, int, char[10 ]));
10866static int openhere __P((union node *));
10867static int noclobberopen __P((const char *));
10868
10869
10870/*
10871 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
10872 * old file descriptors are stashed away so that the redirection can be
10873 * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
10874 * standard output, and the standard error if it becomes a duplicate of
10875 * stdout, is saved in memory.
10876 */
10877
10878static void
10879redirect(redir, flags)
10880 union node *redir;
10881 int flags;
10882 {
10883 union node *n;
10884 struct redirtab *sv = NULL;
10885 int i;
10886 int fd;
10887 int newfd;
10888 int try;
10889 char memory[10]; /* file descriptors to write to memory */
10890
10891 for (i = 10 ; --i >= 0 ; )
10892 memory[i] = 0;
10893 memory[1] = flags & REDIR_BACKQ;
10894 if (flags & REDIR_PUSH) {
10895 sv = ckmalloc(sizeof (struct redirtab));
10896 for (i = 0 ; i < 10 ; i++)
10897 sv->renamed[i] = EMPTY;
10898 sv->next = redirlist;
10899 redirlist = sv;
10900 }
10901 for (n = redir ; n ; n = n->nfile.next) {
10902 fd = n->nfile.fd;
10903 try = 0;
10904 if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
10905 n->ndup.dupfd == fd)
10906 continue; /* redirect from/to same file descriptor */
10907
10908 INTOFF;
10909 newfd = openredirect(n);
10910 if (((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) ||
10911 (fd == fileno2)) {
10912 if (newfd == fd) {
10913 try++;
10914 } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) {
10915 switch (errno) {
10916 case EBADF:
10917 if (!try) {
10918 dupredirect(n, newfd, memory);
10919 try++;
10920 break;
10921 }
10922 /* FALLTHROUGH*/
10923 default:
10924 if (newfd >= 0) {
10925 close(newfd);
10926 }
10927 INTON;
10928 error("%d: %s", fd, strerror(errno));
10929 /* NOTREACHED */
10930 }
10931 }
10932 if (!try) {
10933 close(fd);
10934 if (flags & REDIR_PUSH) {
10935 sv->renamed[fd] = i;
10936 }
10937 if (fd == fileno2) {
10938 fileno2 = i;
10939 }
10940 }
10941 } else if (fd != newfd) {
10942 close(fd);
10943 }
10944 if (fd == 0)
10945 fd0_redirected++;
10946 if (!try)
10947 dupredirect(n, newfd, memory);
10948 INTON;
10949 }
10950 if (memory[1])
10951 out1 = &memout;
10952 if (memory[2])
10953 out2 = &memout;
10954}
10955
10956
10957static int
10958openredirect(redir)
10959 union node *redir;
10960 {
10961 char *fname;
10962 int f;
10963
10964 switch (redir->nfile.type) {
10965 case NFROM:
10966 fname = redir->nfile.expfname;
10967 if ((f = open(fname, O_RDONLY)) < 0)
10968 goto eopen;
10969 break;
10970 case NFROMTO:
10971 fname = redir->nfile.expfname;
10972 if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
10973 goto ecreate;
10974 break;
10975 case NTO:
10976 /* Take care of noclobber mode. */
10977 if (Cflag) {
10978 fname = redir->nfile.expfname;
10979 if ((f = noclobberopen(fname)) < 0)
10980 goto ecreate;
10981 break;
10982 }
10983 case NTOOV:
10984 fname = redir->nfile.expfname;
10985#ifdef O_CREAT
10986 if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
10987 goto ecreate;
10988#else
10989 if ((f = creat(fname, 0666)) < 0)
10990 goto ecreate;
10991#endif
10992 break;
10993 case NAPPEND:
10994 fname = redir->nfile.expfname;
10995#ifdef O_APPEND
10996 if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
10997 goto ecreate;
10998#else
10999 if ((f = open(fname, O_WRONLY)) < 0
11000 && (f = creat(fname, 0666)) < 0)
11001 goto ecreate;
11002 lseek(f, (off_t)0, 2);
11003#endif
11004 break;
11005 default:
11006#ifdef DEBUG
11007 abort();
11008#endif
11009 /* Fall through to eliminate warning. */
11010 case NTOFD:
11011 case NFROMFD:
11012 f = -1;
11013 break;
11014 case NHERE:
11015 case NXHERE:
11016 f = openhere(redir);
11017 break;
11018 }
11019
11020 return f;
11021ecreate:
11022 error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
11023eopen:
11024 error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
11025}
11026
11027
11028static void
11029dupredirect(redir, f, memory)
11030 union node *redir;
11031 int f;
11032 char memory[10];
11033 {
11034 int fd = redir->nfile.fd;
11035
11036 memory[fd] = 0;
11037 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
11038 if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
11039 if (memory[redir->ndup.dupfd])
11040 memory[fd] = 1;
11041 else
11042 dup_as_newfd(redir->ndup.dupfd, fd);
11043 }
11044 return;
11045 }
11046
11047 if (f != fd) {
11048 dup_as_newfd(f, fd);
11049 close(f);
11050 }
11051 return;
11052}
11053
11054
11055/*
11056 * Handle here documents. Normally we fork off a process to write the
11057 * data to a pipe. If the document is short, we can stuff the data in
11058 * the pipe without forking.
11059 */
11060
11061static int
11062openhere(redir)
11063 union node *redir;
11064 {
11065 int pip[2];
11066 int len = 0;
11067
11068 if (pipe(pip) < 0)
11069 error("Pipe call failed");
11070 if (redir->type == NHERE) {
11071 len = strlen(redir->nhere.doc->narg.text);
11072 if (len <= PIPESIZE) {
11073 xwrite(pip[1], redir->nhere.doc->narg.text, len);
11074 goto out;
11075 }
11076 }
11077 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
11078 close(pip[0]);
11079 signal(SIGINT, SIG_IGN);
11080 signal(SIGQUIT, SIG_IGN);
11081 signal(SIGHUP, SIG_IGN);
11082#ifdef SIGTSTP
11083 signal(SIGTSTP, SIG_IGN);
11084#endif
11085 signal(SIGPIPE, SIG_DFL);
11086 if (redir->type == NHERE)
11087 xwrite(pip[1], redir->nhere.doc->narg.text, len);
11088 else
11089 expandhere(redir->nhere.doc, pip[1]);
11090 _exit(0);
11091 }
11092out:
11093 close(pip[1]);
11094 return pip[0];
11095}
11096
11097
11098
11099/*
11100 * Undo the effects of the last redirection.
11101 */
11102
11103static void
11104popredir() {
11105 struct redirtab *rp = redirlist;
11106 int i;
11107
11108 INTOFF;
11109 for (i = 0 ; i < 10 ; i++) {
11110 if (rp->renamed[i] != EMPTY) {
11111 if (i == 0)
11112 fd0_redirected--;
11113 close(i);
11114 if (rp->renamed[i] >= 0) {
11115 dup_as_newfd(rp->renamed[i], i);
11116 close(rp->renamed[i]);
11117 }
11118 if (rp->renamed[i] == fileno2) {
11119 fileno2 = i;
11120 }
11121 }
11122 }
11123 redirlist = rp->next;
11124 ckfree(rp);
11125 INTON;
11126}
11127
11128/*
11129 * Undo all redirections. Called on error or interrupt.
11130 */
11131
11132#ifdef mkinit
11133
11134INCLUDE "redir.h"
11135
11136RESET {
11137 while (redirlist)
11138 popredir();
11139}
11140
11141SHELLPROC {
11142 clearredir();
11143}
11144
11145#endif
11146
11147/* Return true if fd 0 has already been redirected at least once. */
11148static int
11149fd0_redirected_p () {
11150 return fd0_redirected != 0;
11151}
11152
11153/*
11154 * Discard all saved file descriptors.
11155 */
11156
11157static void
11158clearredir() {
11159 struct redirtab *rp;
11160 int i;
11161
11162 for (rp = redirlist ; rp ; rp = rp->next) {
11163 for (i = 0 ; i < 10 ; i++) {
11164 if (rp->renamed[i] >= 0) {
11165 close(rp->renamed[i]);
11166 if (rp->renamed[i] == fileno2) {
11167 fileno2 = -1;
11168 }
11169 }
11170 rp->renamed[i] = EMPTY;
11171 }
11172 }
11173 if (fileno2 != 2 && fileno2 >= 0) {
11174 close(fileno2);
11175 fileno2 = -1;
11176 }
11177}
11178
11179
11180
11181/*
11182 * Copy a file descriptor to be >= to. Returns -1
11183 * if the source file descriptor is closed, EMPTY if there are no unused
11184 * file descriptors left.
11185 */
11186
11187static int
11188dup_as_newfd(from, to)
11189 int from;
11190 int to;
11191{
11192 int newfd;
11193
11194 newfd = fcntl(from, F_DUPFD, to);
11195 if (newfd < 0) {
11196 if (errno == EMFILE)
11197 return EMPTY;
11198 else
11199 error("%d: %s", from, strerror(errno));
11200 }
11201 return newfd;
11202}
11203
11204/*
11205 * Open a file in noclobber mode.
11206 * The code was copied from bash.
11207 */
11208static int
11209noclobberopen(fname)
11210 const char *fname;
11211{
11212 int r, fd;
11213 struct stat finfo, finfo2;
11214
11215 /*
11216 * If the file exists and is a regular file, return an error
11217 * immediately.
11218 */
11219 r = stat(fname, &finfo);
11220 if (r == 0 && S_ISREG(finfo.st_mode)) {
11221 errno = EEXIST;
11222 return -1;
11223 }
11224
11225 /*
11226 * If the file was not present (r != 0), make sure we open it
11227 * exclusively so that if it is created before we open it, our open
11228 * will fail. Make sure that we do not truncate an existing file.
11229 * Note that we don't turn on O_EXCL unless the stat failed -- if the
11230 * file was not a regular file, we leave O_EXCL off.
11231 */
11232 if (r != 0)
11233 return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
11234 fd = open(fname, O_WRONLY|O_CREAT, 0666);
11235
11236 /* If the open failed, return the file descriptor right away. */
11237 if (fd < 0)
11238 return fd;
11239
11240 /*
11241 * OK, the open succeeded, but the file may have been changed from a
11242 * non-regular file to a regular file between the stat and the open.
11243 * We are assuming that the O_EXCL open handles the case where FILENAME
11244 * did not exist and is symlinked to an existing file between the stat
11245 * and open.
11246 */
11247
11248 /*
11249 * If we can open it and fstat the file descriptor, and neither check
11250 * revealed that it was a regular file, and the file has not been
11251 * replaced, return the file descriptor.
11252 */
11253 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
11254 finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
11255 return fd;
11256
11257 /* The file has been replaced. badness. */
11258 close(fd);
11259 errno = EEXIST;
11260 return -1;
11261}
11262/* $NetBSD: setmode.c,v 1.28 2000/01/25 15:43:43 enami Exp $ */
11263
Eric Andersencb57d552001-06-28 07:25:16 +000011264#ifdef __weak_alias
11265__weak_alias(getmode,_getmode)
11266__weak_alias(setmode,_setmode)
11267#endif
11268
11269#ifdef __GLIBC__
11270#define S_ISTXT __S_ISVTX
11271#endif
11272
11273#define SET_LEN 6 /* initial # of bitcmd struct to malloc */
11274#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */
11275
11276typedef struct bitcmd {
11277 char cmd;
11278 char cmd2;
11279 mode_t bits;
11280} BITCMD;
11281
11282#define CMD2_CLR 0x01
11283#define CMD2_SET 0x02
11284#define CMD2_GBITS 0x04
11285#define CMD2_OBITS 0x08
11286#define CMD2_UBITS 0x10
11287
11288static BITCMD *addcmd __P((BITCMD *, int, int, int, u_int));
11289static void compress_mode __P((BITCMD *));
11290#ifdef SETMODE_DEBUG
11291static void dumpmode __P((BITCMD *));
11292#endif
11293
11294/*
11295 * Given the old mode and an array of bitcmd structures, apply the operations
11296 * described in the bitcmd structures to the old mode, and return the new mode.
11297 * Note that there is no '=' command; a strict assignment is just a '-' (clear
11298 * bits) followed by a '+' (set bits).
11299 */
11300mode_t
11301getmode(bbox, omode)
11302 const void *bbox;
11303 mode_t omode;
11304{
11305 const BITCMD *set;
11306 mode_t clrval, newmode, value;
11307
11308 _DIAGASSERT(bbox != NULL);
11309
11310 set = (const BITCMD *)bbox;
11311 newmode = omode;
11312 for (value = 0;; set++)
11313 switch(set->cmd) {
11314 /*
11315 * When copying the user, group or other bits around, we "know"
11316 * where the bits are in the mode so that we can do shifts to
11317 * copy them around. If we don't use shifts, it gets real
11318 * grundgy with lots of single bit checks and bit sets.
11319 */
11320 case 'u':
11321 value = (newmode & S_IRWXU) >> 6;
11322 goto common;
11323
11324 case 'g':
11325 value = (newmode & S_IRWXG) >> 3;
11326 goto common;
11327
11328 case 'o':
11329 value = newmode & S_IRWXO;
11330common: if (set->cmd2 & CMD2_CLR) {
11331 clrval =
11332 (set->cmd2 & CMD2_SET) ? S_IRWXO : value;
11333 if (set->cmd2 & CMD2_UBITS)
11334 newmode &= ~((clrval<<6) & set->bits);
11335 if (set->cmd2 & CMD2_GBITS)
11336 newmode &= ~((clrval<<3) & set->bits);
11337 if (set->cmd2 & CMD2_OBITS)
11338 newmode &= ~(clrval & set->bits);
11339 }
11340 if (set->cmd2 & CMD2_SET) {
11341 if (set->cmd2 & CMD2_UBITS)
11342 newmode |= (value<<6) & set->bits;
11343 if (set->cmd2 & CMD2_GBITS)
11344 newmode |= (value<<3) & set->bits;
11345 if (set->cmd2 & CMD2_OBITS)
11346 newmode |= value & set->bits;
11347 }
11348 break;
11349
11350 case '+':
11351 newmode |= set->bits;
11352 break;
11353
11354 case '-':
11355 newmode &= ~set->bits;
11356 break;
11357
11358 case 'X':
11359 if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
11360 newmode |= set->bits;
11361 break;
11362
11363 case '\0':
11364 default:
11365#ifdef SETMODE_DEBUG
11366 (void)printf("getmode:%04o -> %04o\n", omode, newmode);
11367#endif
11368 return (newmode);
11369 }
11370}
11371
11372#define ADDCMD(a, b, c, d) do { \
11373 if (set >= endset) { \
11374 BITCMD *newset; \
11375 setlen += SET_LEN_INCR; \
11376 newset = realloc(saveset, sizeof(BITCMD) * setlen); \
11377 if (newset == NULL) { \
11378 free(saveset); \
11379 return (NULL); \
11380 } \
11381 set = newset + (set - saveset); \
11382 saveset = newset; \
11383 endset = newset + (setlen - 2); \
11384 } \
11385 set = addcmd(set, (a), (b), (c), (d)); \
11386} while (/*CONSTCOND*/0)
11387
11388#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
11389
11390static void *
11391setmode(p)
11392 const char *p;
11393{
11394 int perm, who;
11395 char op, *ep;
11396 BITCMD *set, *saveset, *endset;
11397 sigset_t mysigset, sigoset;
11398 mode_t mask;
11399 int equalopdone = 0; /* pacify gcc */
11400 int permXbits, setlen;
11401
11402 if (!*p)
11403 return (NULL);
11404
11405 /*
11406 * Get a copy of the mask for the permissions that are mask relative.
11407 * Flip the bits, we want what's not set. Since it's possible that
11408 * the caller is opening files inside a signal handler, protect them
11409 * as best we can.
11410 */
11411 sigfillset(&mysigset);
11412 (void)sigprocmask(SIG_BLOCK, &mysigset, &sigoset);
11413 (void)umask(mask = umask(0));
11414 mask = ~mask;
11415 (void)sigprocmask(SIG_SETMASK, &sigoset, NULL);
11416
11417 setlen = SET_LEN + 2;
11418
11419 if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL)
11420 return (NULL);
11421 saveset = set;
11422 endset = set + (setlen - 2);
11423
11424 /*
11425 * If an absolute number, get it and return; disallow non-octal digits
11426 * or illegal bits.
11427 */
11428 if (isdigit((unsigned char)*p)) {
11429 perm = (mode_t)strtol(p, &ep, 8);
11430 if (*ep || perm & ~(STANDARD_BITS|S_ISTXT)) {
11431 free(saveset);
11432 return (NULL);
11433 }
11434 ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
11435 set->cmd = 0;
11436 return (saveset);
11437 }
11438
11439 /*
11440 * Build list of structures to set/clear/copy bits as described by
11441 * each clause of the symbolic mode.
11442 */
11443 for (;;) {
11444 /* First, find out which bits might be modified. */
11445 for (who = 0;; ++p) {
11446 switch (*p) {
11447 case 'a':
11448 who |= STANDARD_BITS;
11449 break;
11450 case 'u':
11451 who |= S_ISUID|S_IRWXU;
11452 break;
11453 case 'g':
11454 who |= S_ISGID|S_IRWXG;
11455 break;
11456 case 'o':
11457 who |= S_IRWXO;
11458 break;
11459 default:
11460 goto getop;
11461 }
11462 }
11463
11464getop: if ((op = *p++) != '+' && op != '-' && op != '=') {
11465 free(saveset);
11466 return (NULL);
11467 }
11468 if (op == '=')
11469 equalopdone = 0;
11470
11471 who &= ~S_ISTXT;
11472 for (perm = 0, permXbits = 0;; ++p) {
11473 switch (*p) {
11474 case 'r':
11475 perm |= S_IRUSR|S_IRGRP|S_IROTH;
11476 break;
11477 case 's':
11478 /*
11479 * If specific bits where requested and
11480 * only "other" bits ignore set-id.
11481 */
11482 if (who == 0 || (who & ~S_IRWXO))
11483 perm |= S_ISUID|S_ISGID;
11484 break;
11485 case 't':
11486 /*
11487 * If specific bits where requested and
11488 * only "other" bits ignore set-id.
11489 */
11490 if (who == 0 || (who & ~S_IRWXO)) {
11491 who |= S_ISTXT;
11492 perm |= S_ISTXT;
11493 }
11494 break;
11495 case 'w':
11496 perm |= S_IWUSR|S_IWGRP|S_IWOTH;
11497 break;
11498 case 'X':
11499 permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
11500 break;
11501 case 'x':
11502 perm |= S_IXUSR|S_IXGRP|S_IXOTH;
11503 break;
11504 case 'u':
11505 case 'g':
11506 case 'o':
11507 /*
11508 * When ever we hit 'u', 'g', or 'o', we have
11509 * to flush out any partial mode that we have,
11510 * and then do the copying of the mode bits.
11511 */
11512 if (perm) {
11513 ADDCMD(op, who, perm, mask);
11514 perm = 0;
11515 }
11516 if (op == '=')
11517 equalopdone = 1;
11518 if (op == '+' && permXbits) {
11519 ADDCMD('X', who, permXbits, mask);
11520 permXbits = 0;
11521 }
11522 ADDCMD(*p, who, op, mask);
11523 break;
11524
11525 default:
11526 /*
11527 * Add any permissions that we haven't already
11528 * done.
11529 */
11530 if (perm || (op == '=' && !equalopdone)) {
11531 if (op == '=')
11532 equalopdone = 1;
11533 ADDCMD(op, who, perm, mask);
11534 perm = 0;
11535 }
11536 if (permXbits) {
11537 ADDCMD('X', who, permXbits, mask);
11538 permXbits = 0;
11539 }
11540 goto apply;
11541 }
11542 }
11543
11544apply: if (!*p)
11545 break;
11546 if (*p != ',')
11547 goto getop;
11548 ++p;
11549 }
11550 set->cmd = 0;
11551#ifdef SETMODE_DEBUG
11552 (void)printf("Before compress_mode()\n");
11553 dumpmode(saveset);
11554#endif
11555 compress_mode(saveset);
11556#ifdef SETMODE_DEBUG
11557 (void)printf("After compress_mode()\n");
11558 dumpmode(saveset);
11559#endif
11560 return (saveset);
11561}
11562
11563static BITCMD *
11564addcmd(set, op, who, oparg, mask)
11565 BITCMD *set;
11566 int oparg, who;
11567 int op;
11568 u_int mask;
11569{
11570
11571 _DIAGASSERT(set != NULL);
11572
11573 switch (op) {
11574 case '=':
11575 set->cmd = '-';
11576 set->bits = who ? who : STANDARD_BITS;
11577 set++;
11578
11579 op = '+';
11580 /* FALLTHROUGH */
11581 case '+':
11582 case '-':
11583 case 'X':
11584 set->cmd = op;
11585 set->bits = (who ? who : mask) & oparg;
11586 break;
11587
11588 case 'u':
11589 case 'g':
11590 case 'o':
11591 set->cmd = op;
11592 if (who) {
11593 set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
11594 ((who & S_IRGRP) ? CMD2_GBITS : 0) |
11595 ((who & S_IROTH) ? CMD2_OBITS : 0);
11596 set->bits = (mode_t)~0;
11597 } else {
11598 set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
11599 set->bits = mask;
11600 }
11601
11602 if (oparg == '+')
11603 set->cmd2 |= CMD2_SET;
11604 else if (oparg == '-')
11605 set->cmd2 |= CMD2_CLR;
11606 else if (oparg == '=')
11607 set->cmd2 |= CMD2_SET|CMD2_CLR;
11608 break;
11609 }
11610 return (set + 1);
11611}
11612
11613#ifdef SETMODE_DEBUG
11614static void
11615dumpmode(set)
11616 BITCMD *set;
11617{
11618
11619 _DIAGASSERT(set != NULL);
11620
11621 for (; set->cmd; ++set)
11622 (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
11623 set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
11624 set->cmd2 & CMD2_CLR ? " CLR" : "",
11625 set->cmd2 & CMD2_SET ? " SET" : "",
11626 set->cmd2 & CMD2_UBITS ? " UBITS" : "",
11627 set->cmd2 & CMD2_GBITS ? " GBITS" : "",
11628 set->cmd2 & CMD2_OBITS ? " OBITS" : "");
11629}
11630#endif
11631
11632/*
11633 * Given an array of bitcmd structures, compress by compacting consecutive
11634 * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u',
11635 * 'g' and 'o' commands continue to be separate. They could probably be
11636 * compacted, but it's not worth the effort.
11637 */
11638static void
11639compress_mode(set)
11640 BITCMD *set;
11641{
11642 BITCMD *nset;
11643 int setbits, clrbits, Xbits, op;
11644
11645 _DIAGASSERT(set != NULL);
11646
11647 for (nset = set;;) {
11648 /* Copy over any 'u', 'g' and 'o' commands. */
11649 while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
11650 *set++ = *nset++;
11651 if (!op)
11652 return;
11653 }
11654
11655 for (setbits = clrbits = Xbits = 0;; nset++) {
11656 if ((op = nset->cmd) == '-') {
11657 clrbits |= nset->bits;
11658 setbits &= ~nset->bits;
11659 Xbits &= ~nset->bits;
11660 } else if (op == '+') {
11661 setbits |= nset->bits;
11662 clrbits &= ~nset->bits;
11663 Xbits &= ~nset->bits;
11664 } else if (op == 'X')
11665 Xbits |= nset->bits & ~setbits;
11666 else
11667 break;
11668 }
11669 if (clrbits) {
11670 set->cmd = '-';
11671 set->cmd2 = 0;
11672 set->bits = clrbits;
11673 set++;
11674 }
11675 if (setbits) {
11676 set->cmd = '+';
11677 set->cmd2 = 0;
11678 set->bits = setbits;
11679 set++;
11680 }
11681 if (Xbits) {
11682 set->cmd = 'X';
11683 set->cmd2 = 0;
11684 set->bits = Xbits;
11685 set++;
11686 }
11687 }
11688}
11689/* $NetBSD: show.c,v 1.18 1999/10/08 21:10:44 pk Exp $ */
11690
Eric Andersencb57d552001-06-28 07:25:16 +000011691
11692#ifdef DEBUG
11693static void shtree __P((union node *, int, char *, FILE*));
11694static void shcmd __P((union node *, FILE *));
11695static void sharg __P((union node *, FILE *));
11696static void indent __P((int, char *, FILE *));
11697static void trstring __P((char *));
11698
11699
11700static void
11701showtree(n)
11702 union node *n;
11703{
11704 trputs("showtree called\n");
11705 shtree(n, 1, NULL, stdout);
11706}
11707
11708
11709static void
11710shtree(n, ind, pfx, fp)
11711 union node *n;
11712 int ind;
11713 char *pfx;
11714 FILE *fp;
11715{
11716 struct nodelist *lp;
11717 const char *s;
11718
11719 if (n == NULL)
11720 return;
11721
11722 indent(ind, pfx, fp);
11723 switch(n->type) {
11724 case NSEMI:
11725 s = "; ";
11726 goto binop;
11727 case NAND:
11728 s = " && ";
11729 goto binop;
11730 case NOR:
11731 s = " || ";
11732binop:
11733 shtree(n->nbinary.ch1, ind, NULL, fp);
11734 /* if (ind < 0) */
11735 fputs(s, fp);
11736 shtree(n->nbinary.ch2, ind, NULL, fp);
11737 break;
11738 case NCMD:
11739 shcmd(n, fp);
11740 if (ind >= 0)
11741 putc('\n', fp);
11742 break;
11743 case NPIPE:
11744 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
11745 shcmd(lp->n, fp);
11746 if (lp->next)
11747 fputs(" | ", fp);
11748 }
11749 if (n->npipe.backgnd)
11750 fputs(" &", fp);
11751 if (ind >= 0)
11752 putc('\n', fp);
11753 break;
11754 default:
11755 fprintf(fp, "<node type %d>", n->type);
11756 if (ind >= 0)
11757 putc('\n', fp);
11758 break;
11759 }
11760}
11761
11762
11763
11764static void
11765shcmd(cmd, fp)
11766 union node *cmd;
11767 FILE *fp;
11768{
11769 union node *np;
11770 int first;
11771 const char *s;
11772 int dftfd;
11773
11774 first = 1;
11775 for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
11776 if (! first)
11777 putchar(' ');
11778 sharg(np, fp);
11779 first = 0;
11780 }
11781 for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) {
11782 if (! first)
11783 putchar(' ');
11784 switch (np->nfile.type) {
11785 case NTO: s = ">"; dftfd = 1; break;
11786 case NAPPEND: s = ">>"; dftfd = 1; break;
11787 case NTOFD: s = ">&"; dftfd = 1; break;
11788 case NTOOV: s = ">|"; dftfd = 1; break;
11789 case NFROM: s = "<"; dftfd = 0; break;
11790 case NFROMFD: s = "<&"; dftfd = 0; break;
11791 case NFROMTO: s = "<>"; dftfd = 0; break;
11792 default: s = "*error*"; dftfd = 0; break;
11793 }
11794 if (np->nfile.fd != dftfd)
11795 fprintf(fp, "%d", np->nfile.fd);
11796 fputs(s, fp);
11797 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
11798 fprintf(fp, "%d", np->ndup.dupfd);
11799 } else {
11800 sharg(np->nfile.fname, fp);
11801 }
11802 first = 0;
11803 }
11804}
11805
11806
11807
11808static void
11809sharg(arg, fp)
11810 union node *arg;
11811 FILE *fp;
11812 {
11813 char *p;
11814 struct nodelist *bqlist;
11815 int subtype;
11816
11817 if (arg->type != NARG) {
11818 printf("<node type %d>\n", arg->type);
11819 fflush(stdout);
11820 abort();
11821 }
11822 bqlist = arg->narg.backquote;
11823 for (p = arg->narg.text ; *p ; p++) {
11824 switch (*p) {
11825 case CTLESC:
11826 putc(*++p, fp);
11827 break;
11828 case CTLVAR:
11829 putc('$', fp);
11830 putc('{', fp);
11831 subtype = *++p;
11832 if (subtype == VSLENGTH)
11833 putc('#', fp);
11834
11835 while (*p != '=')
11836 putc(*p++, fp);
11837
11838 if (subtype & VSNUL)
11839 putc(':', fp);
11840
11841 switch (subtype & VSTYPE) {
11842 case VSNORMAL:
11843 putc('}', fp);
11844 break;
11845 case VSMINUS:
11846 putc('-', fp);
11847 break;
11848 case VSPLUS:
11849 putc('+', fp);
11850 break;
11851 case VSQUESTION:
11852 putc('?', fp);
11853 break;
11854 case VSASSIGN:
11855 putc('=', fp);
11856 break;
11857 case VSTRIMLEFT:
11858 putc('#', fp);
11859 break;
11860 case VSTRIMLEFTMAX:
11861 putc('#', fp);
11862 putc('#', fp);
11863 break;
11864 case VSTRIMRIGHT:
11865 putc('%', fp);
11866 break;
11867 case VSTRIMRIGHTMAX:
11868 putc('%', fp);
11869 putc('%', fp);
11870 break;
11871 case VSLENGTH:
11872 break;
11873 default:
11874 printf("<subtype %d>", subtype);
11875 }
11876 break;
11877 case CTLENDVAR:
11878 putc('}', fp);
11879 break;
11880 case CTLBACKQ:
11881 case CTLBACKQ|CTLQUOTE:
11882 putc('$', fp);
11883 putc('(', fp);
11884 shtree(bqlist->n, -1, NULL, fp);
11885 putc(')', fp);
11886 break;
11887 default:
11888 putc(*p, fp);
11889 break;
11890 }
11891 }
11892}
11893
11894
11895static void
11896indent(amount, pfx, fp)
11897 int amount;
11898 char *pfx;
11899 FILE *fp;
11900{
11901 int i;
11902
11903 for (i = 0 ; i < amount ; i++) {
11904 if (pfx && i == amount - 1)
11905 fputs(pfx, fp);
11906 putc('\t', fp);
11907 }
11908}
11909#endif
11910
11911
11912
11913/*
11914 * Debugging stuff.
11915 */
11916
11917
11918#ifdef DEBUG
11919FILE *tracefile;
11920
11921#if DEBUG == 2
11922static int debug = 1;
11923#else
11924static int debug = 0;
11925#endif
11926
11927
11928static void
11929trputc(c)
11930 int c;
11931{
11932 if (tracefile == NULL)
11933 return;
11934 putc(c, tracefile);
11935 if (c == '\n')
11936 fflush(tracefile);
11937}
11938
11939static void
11940trace(const char *fmt, ...)
11941{
11942 va_list va;
11943#ifdef __STDC__
11944 va_start(va, fmt);
11945#else
11946 char *fmt;
11947 va_start(va);
11948 fmt = va_arg(va, char *);
11949#endif
11950 if (tracefile != NULL) {
11951 (void) vfprintf(tracefile, fmt, va);
11952 if (strchr(fmt, '\n'))
11953 (void) fflush(tracefile);
11954 }
11955 va_end(va);
11956}
11957
11958
11959static void
11960trputs(s)
11961 const char *s;
11962{
11963 if (tracefile == NULL)
11964 return;
11965 fputs(s, tracefile);
11966 if (strchr(s, '\n'))
11967 fflush(tracefile);
11968}
11969
11970
11971static void
11972trstring(s)
11973 char *s;
11974{
11975 char *p;
11976 char c;
11977
11978 if (tracefile == NULL)
11979 return;
11980 putc('"', tracefile);
11981 for (p = s ; *p ; p++) {
11982 switch (*p) {
11983 case '\n': c = 'n'; goto backslash;
11984 case '\t': c = 't'; goto backslash;
11985 case '\r': c = 'r'; goto backslash;
11986 case '"': c = '"'; goto backslash;
11987 case '\\': c = '\\'; goto backslash;
11988 case CTLESC: c = 'e'; goto backslash;
11989 case CTLVAR: c = 'v'; goto backslash;
11990 case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
11991 case CTLBACKQ: c = 'q'; goto backslash;
11992 case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
11993backslash: putc('\\', tracefile);
11994 putc(c, tracefile);
11995 break;
11996 default:
11997 if (*p >= ' ' && *p <= '~')
11998 putc(*p, tracefile);
11999 else {
12000 putc('\\', tracefile);
12001 putc(*p >> 6 & 03, tracefile);
12002 putc(*p >> 3 & 07, tracefile);
12003 putc(*p & 07, tracefile);
12004 }
12005 break;
12006 }
12007 }
12008 putc('"', tracefile);
12009}
12010
12011
12012static void
12013trargs(ap)
12014 char **ap;
12015{
12016 if (tracefile == NULL)
12017 return;
12018 while (*ap) {
12019 trstring(*ap++);
12020 if (*ap)
12021 putc(' ', tracefile);
12022 else
12023 putc('\n', tracefile);
12024 }
12025 fflush(tracefile);
12026}
12027
12028
12029static void
12030opentrace() {
12031 char s[100];
12032#ifdef O_APPEND
12033 int flags;
12034#endif
12035
12036 if (!debug)
12037 return;
12038#ifdef not_this_way
12039 {
12040 char *p;
12041 if ((p = getenv("HOME")) == NULL) {
12042 if (geteuid() == 0)
12043 p = "/";
12044 else
12045 p = "/tmp";
12046 }
12047 scopy(p, s);
12048 strcat(s, "/trace");
12049 }
12050#else
12051 scopy("./trace", s);
12052#endif /* not_this_way */
12053 if ((tracefile = fopen(s, "a")) == NULL) {
12054 fprintf(stderr, "Can't open %s\n", s);
12055 return;
12056 }
12057#ifdef O_APPEND
12058 if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
12059 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
12060#endif
12061 fputs("\nTracing started.\n", tracefile);
12062 fflush(tracefile);
12063}
12064#endif /* DEBUG */
12065
12066
12067/*
12068 * This file was generated by the mksyntax program.
12069 */
12070
12071/* syntax table used when not in quotes */
12072static const char basesyntax[257] = {
12073 CEOF, CSPCL, CWORD, CCTL,
12074 CCTL, CCTL, CCTL, CCTL,
12075 CCTL, CCTL, CCTL, CWORD,
12076 CWORD, CWORD, CWORD, CWORD,
12077 CWORD, CWORD, CWORD, CWORD,
12078 CWORD, CWORD, CWORD, CWORD,
12079 CWORD, CWORD, CWORD, CWORD,
12080 CWORD, CWORD, CWORD, CWORD,
12081 CWORD, CWORD, CWORD, CWORD,
12082 CWORD, CWORD, CWORD, CWORD,
12083 CWORD, CWORD, CWORD, CWORD,
12084 CWORD, CWORD, CWORD, CWORD,
12085 CWORD, CWORD, CWORD, CWORD,
12086 CWORD, CWORD, CWORD, CWORD,
12087 CWORD, CWORD, CWORD, CWORD,
12088 CWORD, CWORD, CWORD, CWORD,
12089 CWORD, CWORD, CWORD, CWORD,
12090 CWORD, CWORD, CWORD, CWORD,
12091 CWORD, CWORD, CWORD, CWORD,
12092 CWORD, CWORD, CWORD, CWORD,
12093 CWORD, CWORD, CWORD, CWORD,
12094 CWORD, CWORD, CWORD, CWORD,
12095 CWORD, CWORD, CWORD, CWORD,
12096 CWORD, CWORD, CWORD, CWORD,
12097 CWORD, CWORD, CWORD, CWORD,
12098 CWORD, CWORD, CWORD, CWORD,
12099 CWORD, CWORD, CWORD, CWORD,
12100 CWORD, CWORD, CWORD, CWORD,
12101 CWORD, CWORD, CWORD, CWORD,
12102 CWORD, CWORD, CWORD, CWORD,
12103 CWORD, CWORD, CWORD, CWORD,
12104 CWORD, CWORD, CWORD, CWORD,
12105 CWORD, CWORD, CWORD, CWORD,
12106 CWORD, CWORD, CWORD, CWORD,
12107 CWORD, CWORD, CWORD, CSPCL,
12108 CNL, CWORD, CWORD, CWORD,
12109 CWORD, CWORD, CWORD, CWORD,
12110 CWORD, CWORD, CWORD, CWORD,
12111 CWORD, CWORD, CWORD, CWORD,
12112 CWORD, CWORD, CWORD, CWORD,
12113 CWORD, CWORD, CSPCL, CWORD,
12114 CDQUOTE, CWORD, CVAR, CWORD,
12115 CSPCL, CSQUOTE, CSPCL, CSPCL,
12116 CWORD, CWORD, CWORD, CWORD,
12117 CWORD, CWORD, CWORD, CWORD,
12118 CWORD, CWORD, CWORD, CWORD,
12119 CWORD, CWORD, CWORD, CWORD,
12120 CWORD, CSPCL, CSPCL, CWORD,
12121 CSPCL, CWORD, CWORD, CWORD,
12122 CWORD, CWORD, CWORD, CWORD,
12123 CWORD, CWORD, CWORD, CWORD,
12124 CWORD, CWORD, CWORD, CWORD,
12125 CWORD, CWORD, CWORD, CWORD,
12126 CWORD, CWORD, CWORD, CWORD,
12127 CWORD, CWORD, CWORD, CWORD,
12128 CWORD, CWORD, CBACK, CWORD,
12129 CWORD, CWORD, CBQUOTE, CWORD,
12130 CWORD, CWORD, CWORD, CWORD,
12131 CWORD, CWORD, CWORD, CWORD,
12132 CWORD, CWORD, CWORD, CWORD,
12133 CWORD, CWORD, CWORD, CWORD,
12134 CWORD, CWORD, CWORD, CWORD,
12135 CWORD, CWORD, CWORD, CWORD,
12136 CWORD, CWORD, CSPCL, CENDVAR,
12137 CWORD
12138};
12139
12140/* syntax table used when in double quotes */
12141static const char dqsyntax[257] = {
12142 CEOF, CIGN, CWORD, CCTL,
12143 CCTL, CCTL, CCTL, CCTL,
12144 CCTL, CCTL, CCTL, CWORD,
12145 CWORD, CWORD, CWORD, CWORD,
12146 CWORD, CWORD, CWORD, CWORD,
12147 CWORD, CWORD, CWORD, CWORD,
12148 CWORD, CWORD, CWORD, CWORD,
12149 CWORD, CWORD, CWORD, CWORD,
12150 CWORD, CWORD, CWORD, CWORD,
12151 CWORD, CWORD, CWORD, CWORD,
12152 CWORD, CWORD, CWORD, CWORD,
12153 CWORD, CWORD, CWORD, CWORD,
12154 CWORD, CWORD, CWORD, CWORD,
12155 CWORD, CWORD, CWORD, CWORD,
12156 CWORD, CWORD, CWORD, CWORD,
12157 CWORD, CWORD, CWORD, CWORD,
12158 CWORD, CWORD, CWORD, CWORD,
12159 CWORD, CWORD, CWORD, CWORD,
12160 CWORD, CWORD, CWORD, CWORD,
12161 CWORD, CWORD, CWORD, CWORD,
12162 CWORD, CWORD, CWORD, CWORD,
12163 CWORD, CWORD, CWORD, CWORD,
12164 CWORD, CWORD, CWORD, CWORD,
12165 CWORD, CWORD, CWORD, CWORD,
12166 CWORD, CWORD, CWORD, CWORD,
12167 CWORD, CWORD, CWORD, CWORD,
12168 CWORD, CWORD, CWORD, CWORD,
12169 CWORD, CWORD, CWORD, CWORD,
12170 CWORD, CWORD, CWORD, CWORD,
12171 CWORD, CWORD, CWORD, CWORD,
12172 CWORD, CWORD, CWORD, CWORD,
12173 CWORD, CWORD, CWORD, CWORD,
12174 CWORD, CWORD, CWORD, CWORD,
12175 CWORD, CWORD, CWORD, CWORD,
12176 CWORD, CWORD, CWORD, CWORD,
12177 CNL, CWORD, CWORD, CWORD,
12178 CWORD, CWORD, CWORD, CWORD,
12179 CWORD, CWORD, CWORD, CWORD,
12180 CWORD, CWORD, CWORD, CWORD,
12181 CWORD, CWORD, CWORD, CWORD,
12182 CWORD, CWORD, CWORD, CCTL,
12183 CENDQUOTE,CWORD, CVAR, CWORD,
12184 CWORD, CWORD, CWORD, CWORD,
12185 CCTL, CWORD, CWORD, CCTL,
12186 CWORD, CCTL, CWORD, CWORD,
12187 CWORD, CWORD, CWORD, CWORD,
12188 CWORD, CWORD, CWORD, CWORD,
12189 CCTL, CWORD, CWORD, CCTL,
12190 CWORD, CCTL, CWORD, CWORD,
12191 CWORD, CWORD, CWORD, CWORD,
12192 CWORD, CWORD, CWORD, CWORD,
12193 CWORD, CWORD, CWORD, CWORD,
12194 CWORD, CWORD, CWORD, CWORD,
12195 CWORD, CWORD, CWORD, CWORD,
12196 CWORD, CWORD, CWORD, CWORD,
12197 CWORD, CCTL, CBACK, CCTL,
12198 CWORD, CWORD, CBQUOTE, CWORD,
12199 CWORD, CWORD, CWORD, CWORD,
12200 CWORD, CWORD, CWORD, CWORD,
12201 CWORD, CWORD, CWORD, CWORD,
12202 CWORD, CWORD, CWORD, CWORD,
12203 CWORD, CWORD, CWORD, CWORD,
12204 CWORD, CWORD, CWORD, CWORD,
12205 CWORD, CWORD, CWORD, CENDVAR,
12206 CCTL
12207};
12208
12209/* syntax table used when in single quotes */
12210static const char sqsyntax[257] = {
12211 CEOF, CIGN, CWORD, CCTL,
12212 CCTL, CCTL, CCTL, CCTL,
12213 CCTL, CCTL, CCTL, CWORD,
12214 CWORD, CWORD, CWORD, CWORD,
12215 CWORD, CWORD, CWORD, CWORD,
12216 CWORD, CWORD, CWORD, CWORD,
12217 CWORD, CWORD, CWORD, CWORD,
12218 CWORD, CWORD, CWORD, CWORD,
12219 CWORD, CWORD, CWORD, CWORD,
12220 CWORD, CWORD, CWORD, CWORD,
12221 CWORD, CWORD, CWORD, CWORD,
12222 CWORD, CWORD, CWORD, CWORD,
12223 CWORD, CWORD, CWORD, CWORD,
12224 CWORD, CWORD, CWORD, CWORD,
12225 CWORD, CWORD, CWORD, CWORD,
12226 CWORD, CWORD, CWORD, CWORD,
12227 CWORD, CWORD, CWORD, CWORD,
12228 CWORD, CWORD, CWORD, CWORD,
12229 CWORD, CWORD, CWORD, CWORD,
12230 CWORD, CWORD, CWORD, CWORD,
12231 CWORD, CWORD, CWORD, CWORD,
12232 CWORD, CWORD, CWORD, CWORD,
12233 CWORD, CWORD, CWORD, CWORD,
12234 CWORD, CWORD, CWORD, CWORD,
12235 CWORD, CWORD, CWORD, CWORD,
12236 CWORD, CWORD, CWORD, CWORD,
12237 CWORD, CWORD, CWORD, CWORD,
12238 CWORD, CWORD, CWORD, CWORD,
12239 CWORD, CWORD, CWORD, CWORD,
12240 CWORD, CWORD, CWORD, CWORD,
12241 CWORD, CWORD, CWORD, CWORD,
12242 CWORD, CWORD, CWORD, CWORD,
12243 CWORD, CWORD, CWORD, CWORD,
12244 CWORD, CWORD, CWORD, CWORD,
12245 CWORD, CWORD, CWORD, CWORD,
12246 CNL, CWORD, CWORD, CWORD,
12247 CWORD, CWORD, CWORD, CWORD,
12248 CWORD, CWORD, CWORD, CWORD,
12249 CWORD, CWORD, CWORD, CWORD,
12250 CWORD, CWORD, CWORD, CWORD,
12251 CWORD, CWORD, CWORD, CCTL,
12252 CWORD, CWORD, CWORD, CWORD,
12253 CWORD, CENDQUOTE,CWORD, CWORD,
12254 CCTL, CWORD, CWORD, CCTL,
12255 CWORD, CCTL, CWORD, CWORD,
12256 CWORD, CWORD, CWORD, CWORD,
12257 CWORD, CWORD, CWORD, CWORD,
12258 CCTL, CWORD, CWORD, CCTL,
12259 CWORD, CCTL, CWORD, CWORD,
12260 CWORD, CWORD, CWORD, CWORD,
12261 CWORD, CWORD, CWORD, CWORD,
12262 CWORD, CWORD, CWORD, CWORD,
12263 CWORD, CWORD, CWORD, CWORD,
12264 CWORD, CWORD, CWORD, CWORD,
12265 CWORD, CWORD, CWORD, CWORD,
12266 CWORD, CCTL, CCTL, CCTL,
12267 CWORD, CWORD, CWORD, CWORD,
12268 CWORD, CWORD, CWORD, CWORD,
12269 CWORD, CWORD, CWORD, CWORD,
12270 CWORD, CWORD, CWORD, CWORD,
12271 CWORD, CWORD, CWORD, CWORD,
12272 CWORD, CWORD, CWORD, CWORD,
12273 CWORD, CWORD, CWORD, CWORD,
12274 CWORD, CWORD, CWORD, CWORD,
12275 CCTL
12276};
12277
12278/* syntax table used when in arithmetic */
12279static const char arisyntax[257] = {
12280 CEOF, CIGN, CWORD, CCTL,
12281 CCTL, CCTL, CCTL, CCTL,
12282 CCTL, CCTL, CCTL, CWORD,
12283 CWORD, CWORD, CWORD, CWORD,
12284 CWORD, CWORD, CWORD, CWORD,
12285 CWORD, CWORD, CWORD, CWORD,
12286 CWORD, CWORD, CWORD, CWORD,
12287 CWORD, CWORD, CWORD, CWORD,
12288 CWORD, CWORD, CWORD, CWORD,
12289 CWORD, CWORD, CWORD, CWORD,
12290 CWORD, CWORD, CWORD, CWORD,
12291 CWORD, CWORD, CWORD, CWORD,
12292 CWORD, CWORD, CWORD, CWORD,
12293 CWORD, CWORD, CWORD, CWORD,
12294 CWORD, CWORD, CWORD, CWORD,
12295 CWORD, CWORD, CWORD, CWORD,
12296 CWORD, CWORD, CWORD, CWORD,
12297 CWORD, CWORD, CWORD, CWORD,
12298 CWORD, CWORD, CWORD, CWORD,
12299 CWORD, CWORD, CWORD, CWORD,
12300 CWORD, CWORD, CWORD, CWORD,
12301 CWORD, CWORD, CWORD, CWORD,
12302 CWORD, CWORD, CWORD, CWORD,
12303 CWORD, CWORD, CWORD, CWORD,
12304 CWORD, CWORD, CWORD, CWORD,
12305 CWORD, CWORD, CWORD, CWORD,
12306 CWORD, CWORD, CWORD, CWORD,
12307 CWORD, CWORD, CWORD, CWORD,
12308 CWORD, CWORD, CWORD, CWORD,
12309 CWORD, CWORD, CWORD, CWORD,
12310 CWORD, CWORD, CWORD, CWORD,
12311 CWORD, CWORD, CWORD, CWORD,
12312 CWORD, CWORD, CWORD, CWORD,
12313 CWORD, CWORD, CWORD, CWORD,
12314 CWORD, CWORD, CWORD, CWORD,
12315 CNL, CWORD, CWORD, CWORD,
12316 CWORD, CWORD, CWORD, CWORD,
12317 CWORD, CWORD, CWORD, CWORD,
12318 CWORD, CWORD, CWORD, CWORD,
12319 CWORD, CWORD, CWORD, CWORD,
12320 CWORD, CWORD, CWORD, CWORD,
12321 CDQUOTE, CWORD, CVAR, CWORD,
12322 CWORD, CSQUOTE, CLP, CRP,
12323 CWORD, CWORD, CWORD, CWORD,
12324 CWORD, CWORD, CWORD, CWORD,
12325 CWORD, CWORD, CWORD, CWORD,
12326 CWORD, CWORD, CWORD, CWORD,
12327 CWORD, CWORD, CWORD, CWORD,
12328 CWORD, CWORD, CWORD, CWORD,
12329 CWORD, CWORD, CWORD, CWORD,
12330 CWORD, CWORD, CWORD, CWORD,
12331 CWORD, CWORD, CWORD, CWORD,
12332 CWORD, CWORD, CWORD, CWORD,
12333 CWORD, CWORD, CWORD, CWORD,
12334 CWORD, CWORD, CWORD, CWORD,
12335 CWORD, CWORD, CBACK, CWORD,
12336 CWORD, CWORD, CBQUOTE, CWORD,
12337 CWORD, CWORD, CWORD, CWORD,
12338 CWORD, CWORD, CWORD, CWORD,
12339 CWORD, CWORD, CWORD, CWORD,
12340 CWORD, CWORD, CWORD, CWORD,
12341 CWORD, CWORD, CWORD, CWORD,
12342 CWORD, CWORD, CWORD, CWORD,
12343 CWORD, CWORD, CWORD, CENDVAR,
12344 CWORD
12345};
12346
12347/* character classification table */
12348static const char is_type[257] = {
12349 0, 0, 0, 0,
12350 0, 0, 0, 0,
12351 0, 0, 0, 0,
12352 0, 0, 0, 0,
12353 0, 0, 0, 0,
12354 0, 0, 0, 0,
12355 0, 0, 0, 0,
12356 0, 0, 0, 0,
12357 0, 0, 0, 0,
12358 0, 0, 0, 0,
12359 0, 0, 0, 0,
12360 0, 0, 0, 0,
12361 0, 0, 0, 0,
12362 0, 0, 0, 0,
12363 0, 0, 0, 0,
12364 0, 0, 0, 0,
12365 0, 0, 0, 0,
12366 0, 0, 0, 0,
12367 0, 0, 0, 0,
12368 0, 0, 0, 0,
12369 0, 0, 0, 0,
12370 0, 0, 0, 0,
12371 0, 0, 0, 0,
12372 0, 0, 0, 0,
12373 0, 0, 0, 0,
12374 0, 0, 0, 0,
12375 0, 0, 0, 0,
12376 0, 0, 0, 0,
12377 0, 0, 0, 0,
12378 0, 0, 0, 0,
12379 0, 0, 0, 0,
12380 0, 0, 0, 0,
12381 0, 0, 0, 0,
12382 0, 0, 0, 0,
12383 0, 0, 0, 0,
12384 0, 0, 0, 0,
12385 0, 0, 0, 0,
12386 0, 0, 0, 0,
12387 0, 0, 0, 0,
12388 0, 0, 0, 0,
12389 0, 0, 0, ISSPECL,
12390 0, ISSPECL, ISSPECL, 0,
12391 0, 0, 0, 0,
12392 ISSPECL, 0, 0, ISSPECL,
12393 0, 0, ISDIGIT, ISDIGIT,
12394 ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT,
12395 ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT,
12396 0, 0, 0, 0,
12397 0, ISSPECL, ISSPECL, ISUPPER,
12398 ISUPPER, ISUPPER, ISUPPER, ISUPPER,
12399 ISUPPER, ISUPPER, ISUPPER, ISUPPER,
12400 ISUPPER, ISUPPER, ISUPPER, ISUPPER,
12401 ISUPPER, ISUPPER, ISUPPER, ISUPPER,
12402 ISUPPER, ISUPPER, ISUPPER, ISUPPER,
12403 ISUPPER, ISUPPER, ISUPPER, ISUPPER,
12404 ISUPPER, 0, 0, 0,
12405 0, ISUNDER, 0, ISLOWER,
12406 ISLOWER, ISLOWER, ISLOWER, ISLOWER,
12407 ISLOWER, ISLOWER, ISLOWER, ISLOWER,
12408 ISLOWER, ISLOWER, ISLOWER, ISLOWER,
12409 ISLOWER, ISLOWER, ISLOWER, ISLOWER,
12410 ISLOWER, ISLOWER, ISLOWER, ISLOWER,
12411 ISLOWER, ISLOWER, ISLOWER, ISLOWER,
12412 ISLOWER, 0, 0, 0,
12413 0
12414};
12415/* $NetBSD: trap.c,v 1.25 2001/02/04 19:52:07 christos Exp $ */
12416
Eric Andersencb57d552001-06-28 07:25:16 +000012417/*
12418 * The trap builtin.
12419 */
12420
12421static int
12422trapcmd(argc, argv)
12423 int argc;
12424 char **argv;
12425{
12426 char *action;
12427 char **ap;
12428 int signo;
12429
12430 if (argc <= 1) {
12431 for (signo = 0 ; signo < NSIG ; signo++) {
12432 if (trap[signo] != NULL) {
12433 char *p;
12434
12435 p = single_quote(trap[signo]);
12436 out1fmt("trap -- %s %s\n", p,
12437 signal_names[signo] + (signo ? 3 : 0)
12438 );
12439 stunalloc(p);
12440 }
12441 }
12442 return 0;
12443 }
12444 ap = argv + 1;
12445 if (argc == 2)
12446 action = NULL;
12447 else
12448 action = *ap++;
12449 while (*ap) {
12450 if ((signo = decode_signal(*ap, 0)) < 0)
12451 error("%s: bad trap", *ap);
12452 INTOFF;
12453 if (action) {
12454 if (action[0] == '-' && action[1] == '\0')
12455 action = NULL;
12456 else
12457 action = savestr(action);
12458 }
12459 if (trap[signo])
12460 ckfree(trap[signo]);
12461 trap[signo] = action;
12462 if (signo != 0)
12463 setsignal(signo);
12464 INTON;
12465 ap++;
12466 }
12467 return 0;
12468}
12469
12470
12471
12472/*
12473 * Clear traps on a fork.
12474 */
12475
12476static void
12477clear_traps() {
12478 char **tp;
12479
12480 for (tp = trap ; tp < &trap[NSIG] ; tp++) {
12481 if (*tp && **tp) { /* trap not NULL or SIG_IGN */
12482 INTOFF;
12483 ckfree(*tp);
12484 *tp = NULL;
12485 if (tp != &trap[0])
12486 setsignal(tp - trap);
12487 INTON;
12488 }
12489 }
12490}
12491
12492
12493
12494/*
12495 * Set the signal handler for the specified signal. The routine figures
12496 * out what it should be set to.
12497 */
12498
12499static void
12500setsignal(signo)
12501 int signo;
12502{
12503 int action;
12504 char *t;
12505 struct sigaction act;
12506
12507 if ((t = trap[signo]) == NULL)
12508 action = S_DFL;
12509 else if (*t != '\0')
12510 action = S_CATCH;
12511 else
12512 action = S_IGN;
12513 if (rootshell && action == S_DFL) {
12514 switch (signo) {
12515 case SIGINT:
12516 if (iflag || minusc || sflag == 0)
12517 action = S_CATCH;
12518 break;
12519 case SIGQUIT:
12520#ifdef DEBUG
12521 {
12522 extern int debug;
12523
12524 if (debug)
12525 break;
12526 }
12527#endif
12528 /* FALLTHROUGH */
12529 case SIGTERM:
12530 if (iflag)
12531 action = S_IGN;
12532 break;
12533#if JOBS
12534 case SIGTSTP:
12535 case SIGTTOU:
12536 if (mflag)
12537 action = S_IGN;
12538 break;
12539#endif
12540 }
12541 }
12542
12543 t = &sigmode[signo - 1];
12544 if (*t == 0) {
12545 /*
12546 * current setting unknown
12547 */
12548 if (sigaction(signo, 0, &act) == -1) {
12549 /*
12550 * Pretend it worked; maybe we should give a warning
12551 * here, but other shells don't. We don't alter
12552 * sigmode, so that we retry every time.
12553 */
12554 return;
12555 }
12556 if (act.sa_handler == SIG_IGN) {
12557 if (mflag && (signo == SIGTSTP ||
12558 signo == SIGTTIN || signo == SIGTTOU)) {
12559 *t = S_IGN; /* don't hard ignore these */
12560 } else
12561 *t = S_HARD_IGN;
12562 } else {
12563 *t = S_RESET; /* force to be set */
12564 }
12565 }
12566 if (*t == S_HARD_IGN || *t == action)
12567 return;
12568 switch (action) {
12569 case S_CATCH:
12570 act.sa_handler = onsig;
12571 break;
12572 case S_IGN:
12573 act.sa_handler = SIG_IGN;
12574 break;
12575 default:
12576 act.sa_handler = SIG_DFL;
12577 }
12578 *t = action;
12579 act.sa_flags = 0;
12580 sigemptyset(&act.sa_mask);
12581 sigaction(signo, &act, 0);
12582}
12583
12584/*
12585 * Ignore a signal.
12586 */
12587
12588static void
12589ignoresig(signo)
12590 int signo;
12591{
12592 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
12593 signal(signo, SIG_IGN);
12594 }
12595 sigmode[signo - 1] = S_HARD_IGN;
12596}
12597
12598
12599#ifdef mkinit
12600INCLUDE <signal.h>
12601INCLUDE "trap.h"
12602
12603SHELLPROC {
12604 char *sm;
12605
12606 clear_traps();
12607 for (sm = sigmode ; sm < sigmode + NSIG - 1; sm++) {
12608 if (*sm == S_IGN)
12609 *sm = S_HARD_IGN;
12610 }
12611}
12612#endif
12613
12614
12615
12616/*
12617 * Signal handler.
12618 */
12619
12620static void
12621onsig(signo)
12622 int signo;
12623{
12624 if (signo == SIGINT && trap[SIGINT] == NULL) {
12625 onint();
12626 return;
12627 }
12628 gotsig[signo - 1] = 1;
12629 pendingsigs++;
12630}
12631
12632
12633
12634/*
12635 * Called to execute a trap. Perhaps we should avoid entering new trap
12636 * handlers while we are executing a trap handler.
12637 */
12638
12639static void
12640dotrap() {
12641 int i;
12642 int savestatus;
12643
12644 for (;;) {
12645 for (i = 1 ; ; i++) {
12646 if (gotsig[i - 1])
12647 break;
12648 if (i >= NSIG - 1)
12649 goto done;
12650 }
12651 gotsig[i - 1] = 0;
12652 savestatus=exitstatus;
12653 evalstring(trap[i], 0);
12654 exitstatus=savestatus;
12655 }
12656done:
12657 pendingsigs = 0;
12658}
12659
12660
12661
12662/*
12663 * Controls whether the shell is interactive or not.
12664 */
12665
12666
12667static void
12668setinteractive(on)
12669 int on;
12670{
12671 static int is_interactive;
12672
12673 if (on == is_interactive)
12674 return;
12675 setsignal(SIGINT);
12676 setsignal(SIGQUIT);
12677 setsignal(SIGTERM);
12678 chkmail(1);
12679 is_interactive = on;
12680}
12681
12682
12683
12684/*
12685 * Called to exit the shell.
12686 */
12687
12688static void
12689exitshell(status)
12690 int status;
12691{
12692 struct jmploc loc1, loc2;
12693 char *p;
12694
12695 TRACE(("exitshell(%d) pid=%d\n", status, getpid()));
12696 if (setjmp(loc1.loc)) {
12697 goto l1;
12698 }
12699 if (setjmp(loc2.loc)) {
12700 goto l2;
12701 }
12702 handler = &loc1;
12703 if ((p = trap[0]) != NULL && *p != '\0') {
12704 trap[0] = NULL;
12705 evalstring(p, 0);
12706 }
12707l1: handler = &loc2; /* probably unnecessary */
12708 flushall();
12709#if JOBS
12710 setjobctl(0);
12711#endif
12712l2: _exit(status);
12713 /* NOTREACHED */
12714}
12715
12716static int decode_signal(const char *string, int minsig)
12717{
12718 int signo;
12719
12720 if (is_number(string)) {
12721 signo = atoi(string);
12722 if (signo >= NSIG) {
12723 return -1;
12724 }
12725 return signo;
12726 }
12727
12728 signo = minsig;
12729 if (!signo) {
12730 goto zero;
12731 }
12732 for (; signo < NSIG; signo++) {
12733 if (!strcasecmp(string, &(signal_names[signo])[3])) {
12734 return signo;
12735 }
12736zero:
12737 if (!strcasecmp(string, signal_names[signo])) {
12738 return signo;
12739 }
12740 }
12741
12742 return -1;
12743}
12744/* $NetBSD: var.c,v 1.27 2001/02/04 19:52:07 christos Exp $ */
12745
Eric Andersencb57d552001-06-28 07:25:16 +000012746#define VTABSIZE 39
12747
12748
12749struct varinit {
12750 struct var *var;
12751 int flags;
12752 const char *text;
12753 void (*func) __P((const char *));
12754};
12755
12756struct localvar *localvars;
12757
12758#if ATTY
12759struct var vatty;
12760#endif
12761struct var vifs;
12762struct var vmail;
12763struct var vmpath;
12764struct var vpath;
12765struct var vps1;
12766struct var vps2;
12767struct var vvers;
12768struct var voptind;
12769
12770static const char defpathvar[] =
12771 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
12772#ifdef IFS_BROKEN
12773static const char defifsvar[] = "IFS= \t\n";
12774#else
12775static const char defifs[] = " \t\n";
12776#endif
12777
12778static const struct varinit varinit[] = {
12779#if ATTY
12780 { &vatty, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY=",
12781 NULL },
12782#endif
12783#ifdef IFS_BROKEN
12784 { &vifs, VSTRFIXED|VTEXTFIXED, defifsvar,
12785#else
12786 { &vifs, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS=",
12787#endif
12788 NULL },
12789 { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=",
12790 NULL },
12791 { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=",
12792 NULL },
12793 { &vpath, VSTRFIXED|VTEXTFIXED, defpathvar,
12794 changepath },
12795 /*
12796 * vps1 depends on uid
12797 */
12798 { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ",
12799 NULL },
12800 { &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1",
12801 getoptsreset },
12802 { NULL, 0, NULL,
12803 NULL }
12804};
12805
12806struct var *vartab[VTABSIZE];
12807
12808static struct var **hashvar __P((const char *));
12809static void showvars __P((const char *, int, int));
12810static struct var **findvar __P((struct var **, const char *));
12811
12812/*
12813 * Initialize the varable symbol tables and import the environment
12814 */
12815
12816#ifdef mkinit
12817INCLUDE <unistd.h>
12818INCLUDE "output.h"
12819INCLUDE "var.h"
12820static char **environ;
12821INIT {
12822 char **envp;
12823 char ppid[32];
12824
12825 initvar();
12826 for (envp = environ ; *envp ; envp++) {
12827 if (strchr(*envp, '=')) {
12828 setvareq(*envp, VEXPORT|VTEXTFIXED);
12829 }
12830 }
12831
12832 fmtstr(ppid, sizeof(ppid), "%d", (int) getppid());
12833 setvar("PPID", ppid, 0);
12834}
12835#endif
12836
12837
12838/*
12839 * This routine initializes the builtin variables. It is called when the
12840 * shell is initialized and again when a shell procedure is spawned.
12841 */
12842
12843static void
12844initvar() {
12845 const struct varinit *ip;
12846 struct var *vp;
12847 struct var **vpp;
12848
12849 for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
12850 if ((vp->flags & VEXPORT) == 0) {
12851 vpp = hashvar(ip->text);
12852 vp->next = *vpp;
12853 *vpp = vp;
12854 vp->text = strdup(ip->text);
12855 vp->flags = ip->flags;
12856 vp->func = ip->func;
12857 }
12858 }
12859 /*
12860 * PS1 depends on uid
12861 */
12862 if ((vps1.flags & VEXPORT) == 0) {
12863 vpp = hashvar("PS1=");
12864 vps1.next = *vpp;
12865 *vpp = &vps1;
12866 vps1.text = strdup(geteuid() ? "PS1=$ " : "PS1=# ");
12867 vps1.flags = VSTRFIXED|VTEXTFIXED;
12868 }
12869}
12870
12871/*
12872 * Set the value of a variable. The flags argument is ored with the
12873 * flags of the variable. If val is NULL, the variable is unset.
12874 */
12875
12876static void
12877setvar(name, val, flags)
12878 const char *name, *val;
12879 int flags;
12880{
12881 const char *p;
12882 int len;
12883 int namelen;
12884 char *nameeq;
12885 int isbad;
12886 int vallen = 0;
12887
12888 isbad = 0;
12889 p = name;
12890 if (! is_name(*p))
12891 isbad = 1;
12892 p++;
12893 for (;;) {
12894 if (! is_in_name(*p)) {
12895 if (*p == '\0' || *p == '=')
12896 break;
12897 isbad = 1;
12898 }
12899 p++;
12900 }
12901 namelen = p - name;
12902 if (isbad)
12903 error("%.*s: bad variable name", namelen, name);
12904 len = namelen + 2; /* 2 is space for '=' and '\0' */
12905 if (val == NULL) {
12906 flags |= VUNSET;
12907 } else {
12908 len += vallen = strlen(val);
12909 }
12910 INTOFF;
12911 nameeq = ckmalloc(len);
12912 memcpy(nameeq, name, namelen);
12913 nameeq[namelen] = '=';
12914 if (val) {
12915 memcpy(nameeq + namelen + 1, val, vallen + 1);
12916 } else {
12917 nameeq[namelen + 1] = '\0';
12918 }
12919 setvareq(nameeq, flags);
12920 INTON;
12921}
12922
12923
12924
12925/*
12926 * Same as setvar except that the variable and value are passed in
12927 * the first argument as name=value. Since the first argument will
12928 * be actually stored in the table, it should not be a string that
12929 * will go away.
12930 */
12931
12932static void
12933setvareq(s, flags)
12934 char *s;
12935 int flags;
12936{
12937 struct var *vp, **vpp;
12938
12939 vpp = hashvar(s);
12940 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
12941 if ((vp = *findvar(vpp, s))) {
12942 if (vp->flags & VREADONLY) {
12943 size_t len = strchr(s, '=') - s;
12944 error("%.*s: is read only", len, s);
12945 }
12946 INTOFF;
12947
12948 if (vp->func && (flags & VNOFUNC) == 0)
12949 (*vp->func)(strchr(s, '=') + 1);
12950
12951 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
12952 ckfree(vp->text);
12953
12954 vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
12955 vp->flags |= flags;
12956 vp->text = s;
12957
12958 /*
12959 * We could roll this to a function, to handle it as
12960 * a regular variable function callback, but why bother?
12961 */
12962 if (iflag && (vp == &vmpath || (vp == &vmail && !mpathset())))
12963 chkmail(1);
12964 INTON;
12965 return;
12966 }
12967 /* not found */
12968 vp = ckmalloc(sizeof (*vp));
12969 vp->flags = flags;
12970 vp->text = s;
12971 vp->next = *vpp;
12972 vp->func = NULL;
12973 *vpp = vp;
12974}
12975
12976
12977
12978/*
12979 * Process a linked list of variable assignments.
12980 */
12981
12982static void
12983listsetvar(mylist)
12984 struct strlist *mylist;
12985 {
12986 struct strlist *lp;
12987
12988 INTOFF;
12989 for (lp = mylist ; lp ; lp = lp->next) {
12990 setvareq(savestr(lp->text), 0);
12991 }
12992 INTON;
12993}
12994
12995
12996
12997/*
12998 * Find the value of a variable. Returns NULL if not set.
12999 */
13000
13001static char *
13002lookupvar(name)
13003 const char *name;
13004 {
13005 struct var *v;
13006
13007 if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) {
13008 return strchr(v->text, '=') + 1;
13009 }
13010 return NULL;
13011}
13012
13013
13014
13015/*
13016 * Search the environment of a builtin command.
13017 */
13018
13019static char *
13020bltinlookup(name)
13021 const char *name;
13022{
13023 struct strlist *sp;
13024
13025 for (sp = cmdenviron ; sp ; sp = sp->next) {
13026 if (varequal(sp->text, name))
13027 return strchr(sp->text, '=') + 1;
13028 }
13029 return lookupvar(name);
13030}
13031
13032
13033
13034/*
13035 * Generate a list of exported variables. This routine is used to construct
13036 * the third argument to execve when executing a program.
13037 */
13038
13039static char **
13040environment() {
13041 int nenv;
13042 struct var **vpp;
13043 struct var *vp;
13044 char **env;
13045 char **ep;
13046
13047 nenv = 0;
13048 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
13049 for (vp = *vpp ; vp ; vp = vp->next)
13050 if (vp->flags & VEXPORT)
13051 nenv++;
13052 }
13053 ep = env = stalloc((nenv + 1) * sizeof *env);
13054 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
13055 for (vp = *vpp ; vp ; vp = vp->next)
13056 if (vp->flags & VEXPORT)
13057 *ep++ = vp->text;
13058 }
13059 *ep = NULL;
13060 return env;
13061}
13062
13063
13064/*
13065 * Called when a shell procedure is invoked to clear out nonexported
13066 * variables. It is also necessary to reallocate variables of with
13067 * VSTACK set since these are currently allocated on the stack.
13068 */
13069
13070#ifdef mkinit
13071static void shprocvar __P((void));
13072
13073SHELLPROC {
13074 shprocvar();
13075}
13076#endif
13077
13078static void
13079shprocvar() {
13080 struct var **vpp;
13081 struct var *vp, **prev;
13082
13083 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
13084 for (prev = vpp ; (vp = *prev) != NULL ; ) {
13085 if ((vp->flags & VEXPORT) == 0) {
13086 *prev = vp->next;
13087 if ((vp->flags & VTEXTFIXED) == 0)
13088 ckfree(vp->text);
13089 if ((vp->flags & VSTRFIXED) == 0)
13090 ckfree(vp);
13091 } else {
13092 if (vp->flags & VSTACK) {
13093 vp->text = savestr(vp->text);
13094 vp->flags &=~ VSTACK;
13095 }
13096 prev = &vp->next;
13097 }
13098 }
13099 }
13100 initvar();
13101}
13102
13103
13104
13105/*
13106 * Command to list all variables which are set. Currently this command
13107 * is invoked from the set command when the set command is called without
13108 * any variables.
13109 */
13110
13111static int
13112showvarscmd(argc, argv)
13113 int argc;
13114 char **argv;
13115{
13116 showvars(nullstr, VUNSET, VUNSET);
13117 return 0;
13118}
13119
13120
13121
13122/*
13123 * The export and readonly commands.
13124 */
13125
13126static int
13127exportcmd(argc, argv)
13128 int argc;
13129 char **argv;
13130{
13131 struct var *vp;
13132 char *name;
13133 const char *p;
13134 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
13135 int pflag;
13136
13137 listsetvar(cmdenviron);
13138 pflag = (nextopt("p") == 'p');
13139 if (argc > 1 && !pflag) {
13140 while ((name = *argptr++) != NULL) {
13141 if ((p = strchr(name, '=')) != NULL) {
13142 p++;
13143 } else {
13144 if ((vp = *findvar(hashvar(name), name))) {
13145 vp->flags |= flag;
13146 goto found;
13147 }
13148 }
13149 setvar(name, p, flag);
13150found:;
13151 }
13152 } else {
13153 showvars(argv[0], flag, 0);
13154 }
13155 return 0;
13156}
13157
13158
13159/*
13160 * The "local" command.
13161 */
13162
13163static int
13164localcmd(argc, argv)
13165 int argc;
13166 char **argv;
13167{
13168 char *name;
13169
13170 if (! in_function())
13171 error("Not in a function");
13172 while ((name = *argptr++) != NULL) {
13173 mklocal(name);
13174 }
13175 return 0;
13176}
13177
13178
13179/*
13180 * Make a variable a local variable. When a variable is made local, it's
13181 * value and flags are saved in a localvar structure. The saved values
13182 * will be restored when the shell function returns. We handle the name
13183 * "-" as a special case.
13184 */
13185
13186static void
13187mklocal(name)
13188 char *name;
13189 {
13190 struct localvar *lvp;
13191 struct var **vpp;
13192 struct var *vp;
13193
13194 INTOFF;
13195 lvp = ckmalloc(sizeof (struct localvar));
13196 if (name[0] == '-' && name[1] == '\0') {
13197 char *p;
13198 p = ckmalloc(sizeof optlist);
13199 lvp->text = memcpy(p, optlist, sizeof optlist);
13200 vp = NULL;
13201 } else {
13202 vpp = hashvar(name);
13203 vp = *findvar(vpp, name);
13204 if (vp == NULL) {
13205 if (strchr(name, '='))
13206 setvareq(savestr(name), VSTRFIXED);
13207 else
13208 setvar(name, NULL, VSTRFIXED);
13209 vp = *vpp; /* the new variable */
13210 lvp->text = NULL;
13211 lvp->flags = VUNSET;
13212 } else {
13213 lvp->text = vp->text;
13214 lvp->flags = vp->flags;
13215 vp->flags |= VSTRFIXED|VTEXTFIXED;
13216 if (strchr(name, '='))
13217 setvareq(savestr(name), 0);
13218 }
13219 }
13220 lvp->vp = vp;
13221 lvp->next = localvars;
13222 localvars = lvp;
13223 INTON;
13224}
13225
13226
13227/*
13228 * Called after a function returns.
13229 */
13230
13231static void
13232poplocalvars() {
13233 struct localvar *lvp;
13234 struct var *vp;
13235
13236 while ((lvp = localvars) != NULL) {
13237 localvars = lvp->next;
13238 vp = lvp->vp;
13239 if (vp == NULL) { /* $- saved */
13240 memcpy(optlist, lvp->text, sizeof optlist);
13241 ckfree(lvp->text);
13242 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
13243 (void)unsetvar(vp->text);
13244 } else {
13245 if ((vp->flags & VTEXTFIXED) == 0)
13246 ckfree(vp->text);
13247 vp->flags = lvp->flags;
13248 vp->text = lvp->text;
13249 }
13250 ckfree(lvp);
13251 }
13252}
13253
13254
13255static int
13256setvarcmd(argc, argv)
13257 int argc;
13258 char **argv;
13259{
13260 if (argc <= 2)
13261 return unsetcmd(argc, argv);
13262 else if (argc == 3)
13263 setvar(argv[1], argv[2], 0);
13264 else
13265 error("List assignment not implemented");
13266 return 0;
13267}
13268
13269
13270/*
13271 * The unset builtin command. We unset the function before we unset the
13272 * variable to allow a function to be unset when there is a readonly variable
13273 * with the same name.
13274 */
13275
13276static int
13277unsetcmd(argc, argv)
13278 int argc;
13279 char **argv;
13280{
13281 char **ap;
13282 int i;
13283 int flg_func = 0;
13284 int flg_var = 0;
13285 int ret = 0;
13286
13287 while ((i = nextopt("vf")) != '\0') {
13288 if (i == 'f')
13289 flg_func = 1;
13290 else
13291 flg_var = 1;
13292 }
13293 if (flg_func == 0 && flg_var == 0)
13294 flg_var = 1;
13295
13296 for (ap = argptr; *ap ; ap++) {
13297 if (flg_func)
13298 unsetfunc(*ap);
13299 if (flg_var)
13300 ret |= unsetvar(*ap);
13301 }
13302 return ret;
13303}
13304
13305
13306/*
13307 * Unset the specified variable.
13308 */
13309
13310static int
13311unsetvar(s)
13312 const char *s;
13313 {
13314 struct var **vpp;
13315 struct var *vp;
13316
13317 vpp = findvar(hashvar(s), s);
13318 vp = *vpp;
13319 if (vp) {
13320 if (vp->flags & VREADONLY)
13321 return (1);
13322 INTOFF;
13323 if (*(strchr(vp->text, '=') + 1) != '\0')
13324 setvar(s, nullstr, 0);
13325 vp->flags &= ~VEXPORT;
13326 vp->flags |= VUNSET;
13327 if ((vp->flags & VSTRFIXED) == 0) {
13328 if ((vp->flags & VTEXTFIXED) == 0)
13329 ckfree(vp->text);
13330 *vpp = vp->next;
13331 ckfree(vp);
13332 }
13333 INTON;
13334 return (0);
13335 }
13336
13337 return (0);
13338}
13339
13340
13341
13342/*
13343 * Find the appropriate entry in the hash table from the name.
13344 */
13345
13346static struct var **
13347hashvar(p)
13348 const char *p;
13349 {
13350 unsigned int hashval;
13351
13352 hashval = ((unsigned char) *p) << 4;
13353 while (*p && *p != '=')
13354 hashval += (unsigned char) *p++;
13355 return &vartab[hashval % VTABSIZE];
13356}
13357
13358
13359
13360/*
13361 * Returns true if the two strings specify the same varable. The first
13362 * variable name is terminated by '='; the second may be terminated by
13363 * either '=' or '\0'.
13364 */
13365
13366static int
13367varequal(p, q)
13368 const char *p, *q;
13369 {
13370 while (*p == *q++) {
13371 if (*p++ == '=')
13372 return 1;
13373 }
13374 if (*p == '=' && *(q - 1) == '\0')
13375 return 1;
13376 return 0;
13377}
13378
13379static void
13380showvars(const char *myprefix, int mask, int xor)
13381{
13382 struct var **vpp;
13383 struct var *vp;
13384 const char *sep = myprefix == nullstr ? myprefix : spcstr;
13385
13386 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
13387 for (vp = *vpp ; vp ; vp = vp->next) {
13388 if ((vp->flags & mask) ^ xor) {
13389 char *p;
13390 int len;
13391
13392 p = strchr(vp->text, '=') + 1;
13393 len = p - vp->text;
13394 p = single_quote(p);
13395
13396 out1fmt(
13397 "%s%s%.*s%s\n", myprefix, sep, len,
13398 vp->text, p
13399 );
13400 stunalloc(p);
13401 }
13402 }
13403 }
13404}
13405
13406static struct var **
13407findvar(struct var **vpp, const char *name)
13408{
13409 for (; *vpp; vpp = &(*vpp)->next) {
13410 if (varequal((*vpp)->text, name)) {
13411 break;
13412 }
13413 }
13414 return vpp;
13415}
13416
13417/*
13418 * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
13419 * This file contains code for the times builtin.
Eric Andersendf82f612001-06-28 07:46:40 +000013420 * $Id: ash.c,v 1.2 2001/06/28 07:46:40 andersen Exp $
Eric Andersencb57d552001-06-28 07:25:16 +000013421 */
13422static int timescmd (int argc, char **argv)
13423{
13424 struct tms buf;
13425 long int clk_tck = sysconf(_SC_CLK_TCK);
13426
13427 times(&buf);
13428 printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n",
13429 (int) (buf.tms_utime / clk_tck / 60),
13430 ((double) buf.tms_utime) / clk_tck,
13431 (int) (buf.tms_stime / clk_tck / 60),
13432 ((double) buf.tms_stime) / clk_tck,
13433 (int) (buf.tms_cutime / clk_tck / 60),
13434 ((double) buf.tms_cutime) / clk_tck,
13435 (int) (buf.tms_cstime / clk_tck / 60),
13436 ((double) buf.tms_cstime) / clk_tck);
13437 return 0;
13438}
13439
Eric Andersendf82f612001-06-28 07:46:40 +000013440
13441/*-
13442 * Copyright (c) 1989, 1991, 1993, 1994
13443 * The Regents of the University of California. All rights reserved.
13444 *
13445 * This code is derived from software contributed to Berkeley by
13446 * Kenneth Almquist.
13447 *
13448 * Redistribution and use in source and binary forms, with or without
13449 * modification, are permitted provided that the following conditions
13450 * are met:
13451 * 1. Redistributions of source code must retain the above copyright
13452 * notice, this list of conditions and the following disclaimer.
13453 * 2. Redistributions in binary form must reproduce the above copyright
13454 * notice, this list of conditions and the following disclaimer in the
13455 * documentation and/or other materials provided with the distribution.
13456 *
13457 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
13458 * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
13459 *
13460 * 4. Neither the name of the University nor the names of its contributors
13461 * may be used to endorse or promote products derived from this software
13462 * without specific prior written permission.
13463 *
13464 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
13465 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
13466 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13467 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
13468 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
13469 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
13470 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
13471 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
13472 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
13473 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
13474 * SUCH DAMAGE.
13475 */