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