aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2010-05-23 17:49:50 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2010-05-23 17:49:50 +0200
commit599ae1eb9f20b4731735e14f9bac6371ad89b6d7 (patch)
treede6b43bd83c3b9d6fe55a4dc2154fc5c14fd86a3
parent4f63c7931c42351e38619842681026ff2c20c7ee (diff)
downloadbusybox-w32-599ae1eb9f20b4731735e14f9bac6371ad89b6d7.tar.gz
busybox-w32-599ae1eb9f20b4731735e14f9bac6371ad89b6d7.tar.bz2
busybox-w32-599ae1eb9f20b4731735e14f9bac6371ad89b6d7.zip
shell: consolidate builtin_foo.? into shell_common.?; delete obsolete shells
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/Kbuild4
-rw-r--r--shell/ash.c2
-rw-r--r--shell/builtin_read.c229
-rw-r--r--shell/builtin_read.h42
-rw-r--r--shell/builtin_ulimit.c228
-rw-r--r--shell/builtin_ulimit.h19
-rw-r--r--shell/hush.c2
-rw-r--r--shell/lash_unused.c1576
-rw-r--r--shell/msh_unused.c5336
-rw-r--r--shell/shell_common.c420
-rw-r--r--shell/shell_common.h20
11 files changed, 442 insertions, 7436 deletions
diff --git a/shell/Kbuild b/shell/Kbuild
index 8b528654a..8bdb68b11 100644
--- a/shell/Kbuild
+++ b/shell/Kbuild
@@ -5,8 +5,8 @@
5# Licensed under the GPL v2, see the file LICENSE in this tarball. 5# Licensed under the GPL v2, see the file LICENSE in this tarball.
6 6
7lib-y:= 7lib-y:=
8lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o builtin_read.o builtin_ulimit.o 8lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
9lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o builtin_read.o builtin_ulimit.o 9lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
10lib-$(CONFIG_CTTYHACK) += cttyhack.o 10lib-$(CONFIG_CTTYHACK) += cttyhack.o
11 11
12lib-$(CONFIG_SH_MATH_SUPPORT) += math.o 12lib-$(CONFIG_SH_MATH_SUPPORT) += math.o
diff --git a/shell/ash.c b/shell/ash.c
index 7efd477d1..9e7da1edd 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -48,8 +48,6 @@
48#include <fnmatch.h> 48#include <fnmatch.h>
49 49
50#include "shell_common.h" 50#include "shell_common.h"
51#include "builtin_read.h"
52#include "builtin_ulimit.h"
53#include "math.h" 51#include "math.h"
54#if ENABLE_ASH_RANDOM_SUPPORT 52#if ENABLE_ASH_RANDOM_SUPPORT
55# include "random.h" 53# include "random.h"
diff --git a/shell/builtin_read.c b/shell/builtin_read.c
deleted file mode 100644
index 954e4cd14..000000000
--- a/shell/builtin_read.c
+++ /dev/null
@@ -1,229 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Adapted from ash applet code
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Copyright (c) 1989, 1991, 1993, 1994
9 * The Regents of the University of California. All rights reserved.
10 *
11 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
12 * was re-ported from NetBSD and debianized.
13 *
14 * Copyright (c) 2010 Denys Vlasenko
15 * Split from ash.c
16 *
17 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
18 */
19#include "libbb.h"
20#include "shell_common.h"
21#include "builtin_read.h"
22
23//TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL"
24//string. hush naturally has it, and ash has setvareq().
25//Here we can simply store "VAR=" at buffer start and store read data directly
26//after "=", then pass buffer to setvar() to consume.
27
28const char* FAST_FUNC
29shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
30 char **argv,
31 const char *ifs,
32 int read_flags,
33 const char *opt_n,
34 const char *opt_p,
35 const char *opt_t,
36 const char *opt_u
37)
38{
39 unsigned end_ms; /* -t TIMEOUT */
40 int fd; /* -u FD */
41 int nchars; /* -n NUM */
42 char **pp;
43 char *buffer;
44 struct termios tty, old_tty;
45 const char *retval;
46 int bufpos; /* need to be able to hold -1 */
47 int startword;
48 smallint backslash;
49
50 pp = argv;
51 while (*pp) {
52 if (!is_well_formed_var_name(*pp, '\0')) {
53 /* Mimic bash message */
54 bb_error_msg("read: '%s': not a valid identifier", *pp);
55 return (const char *)(uintptr_t)1;
56 }
57 pp++;
58 }
59
60 nchars = 0; /* if != 0, -n is in effect */
61 if (opt_n) {
62 nchars = bb_strtou(opt_n, NULL, 10);
63 if (nchars < 0 || errno)
64 return "invalid count";
65 /* note: "-n 0": off (bash 3.2 does this too) */
66 }
67 end_ms = 0;
68 if (opt_t) {
69 end_ms = bb_strtou(opt_t, NULL, 10);
70 if (errno || end_ms > UINT_MAX / 2048)
71 return "invalid timeout";
72 end_ms *= 1000;
73#if 0 /* even bash has no -t N.NNN support */
74 ts.tv_sec = bb_strtou(opt_t, &p, 10);
75 ts.tv_usec = 0;
76 /* EINVAL means number is ok, but not terminated by NUL */
77 if (*p == '.' && errno == EINVAL) {
78 char *p2;
79 if (*++p) {
80 int scale;
81 ts.tv_usec = bb_strtou(p, &p2, 10);
82 if (errno)
83 return "invalid timeout";
84 scale = p2 - p;
85 /* normalize to usec */
86 if (scale > 6)
87 return "invalid timeout";
88 while (scale++ < 6)
89 ts.tv_usec *= 10;
90 }
91 } else if (ts.tv_sec < 0 || errno) {
92 return "invalid timeout";
93 }
94 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
95 return "invalid timeout";
96 }
97#endif /* if 0 */
98 }
99 fd = STDIN_FILENO;
100 if (opt_u) {
101 fd = bb_strtou(opt_u, NULL, 10);
102 if (fd < 0 || errno)
103 return "invalid file descriptor";
104 }
105
106 if (opt_p && isatty(fd)) {
107 fputs(opt_p, stderr);
108 fflush_all();
109 }
110
111 if (ifs == NULL)
112 ifs = defifs;
113
114 if (nchars || (read_flags & BUILTIN_READ_SILENT)) {
115 tcgetattr(fd, &tty);
116 old_tty = tty;
117 if (nchars) {
118 tty.c_lflag &= ~ICANON;
119 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
120 }
121 if (read_flags & BUILTIN_READ_SILENT) {
122 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
123 }
124 /* This forces execution of "restoring" tcgetattr later */
125 read_flags |= BUILTIN_READ_SILENT;
126 /* if tcgetattr failed, tcsetattr will fail too.
127 * Ignoring, it's harmless. */
128 tcsetattr(fd, TCSANOW, &tty);
129 }
130
131 retval = (const char *)(uintptr_t)0;
132 startword = 1;
133 backslash = 0;
134 if (end_ms) /* NB: end_ms stays nonzero: */
135 end_ms = ((unsigned)monotonic_ms() + end_ms) | 1;
136 buffer = NULL;
137 bufpos = 0;
138 do {
139 char c;
140
141 if (end_ms) {
142 int timeout;
143 struct pollfd pfd[1];
144
145 pfd[0].fd = fd;
146 pfd[0].events = POLLIN;
147 timeout = end_ms - (unsigned)monotonic_ms();
148 if (timeout <= 0 /* already late? */
149 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
150 ) { /* timed out! */
151 retval = (const char *)(uintptr_t)1;
152 goto ret;
153 }
154 }
155
156 if ((bufpos & 0xff) == 0)
157 buffer = xrealloc(buffer, bufpos + 0x100);
158 if (nonblock_safe_read(fd, &buffer[bufpos], 1) != 1) {
159 retval = (const char *)(uintptr_t)1;
160 break;
161 }
162 c = buffer[bufpos];
163 if (c == '\0')
164 continue;
165 if (backslash) {
166 backslash = 0;
167 if (c != '\n')
168 goto put;
169 continue;
170 }
171 if (!(read_flags & BUILTIN_READ_RAW) && c == '\\') {
172 backslash = 1;
173 continue;
174 }
175 if (c == '\n')
176 break;
177
178 /* $IFS splitting. NOT done if we run "read"
179 * without variable names (bash compat).
180 * Thus, "read" and "read REPLY" are not the same.
181 */
182 if (argv[0]) {
183/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */
184 const char *is_ifs = strchr(ifs, c);
185 if (startword && is_ifs) {
186 if (isspace(c))
187 continue;
188 /* it is a non-space ifs char */
189 startword--;
190 if (startword == 1) /* first one? */
191 continue; /* yes, it is not next word yet */
192 }
193 startword = 0;
194 if (argv[1] != NULL && is_ifs) {
195 buffer[bufpos] = '\0';
196 bufpos = 0;
197 setvar(*argv, buffer);
198 argv++;
199 /* can we skip one non-space ifs char? (2: yes) */
200 startword = isspace(c) ? 2 : 1;
201 continue;
202 }
203 }
204 put:
205 bufpos++;
206 } while (--nchars);
207
208 if (argv[0]) {
209 /* Remove trailing space $IFS chars */
210 while (--bufpos >= 0 && isspace(buffer[bufpos]) && strchr(ifs, buffer[bufpos]) != NULL)
211 continue;
212 buffer[bufpos + 1] = '\0';
213 /* Use the remainder as a value for the next variable */
214 setvar(*argv, buffer);
215 /* Set the rest to "" */
216 while (*++argv)
217 setvar(*argv, "");
218 } else {
219 /* Note: no $IFS removal */
220 buffer[bufpos] = '\0';
221 setvar("REPLY", buffer);
222 }
223
224 ret:
225 free(buffer);
226 if (read_flags & BUILTIN_READ_SILENT)
227 tcsetattr(fd, TCSANOW, &old_tty);
228 return retval;
229}
diff --git a/shell/builtin_read.h b/shell/builtin_read.h
deleted file mode 100644
index bc23dc5ae..000000000
--- a/shell/builtin_read.h
+++ /dev/null
@@ -1,42 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Adapted from ash applet code
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Copyright (c) 1989, 1991, 1993, 1994
9 * The Regents of the University of California. All rights reserved.
10 *
11 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
12 * was re-ported from NetBSD and debianized.
13 *
14 * Copyright (c) 2010 Denys Vlasenko
15 * Split from ash.c
16 *
17 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
18 */
19#ifndef SHELL_BUILTIN_READ_H
20#define SHELL_BUILTIN_READ_H 1
21
22PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
23
24enum {
25 BUILTIN_READ_SILENT = 1 << 0,
26 BUILTIN_READ_RAW = 1 << 1,
27};
28
29const char* FAST_FUNC
30shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
31 char **argv,
32 const char *ifs,
33 int read_flags,
34 const char *opt_n,
35 const char *opt_p,
36 const char *opt_t,
37 const char *opt_u
38);
39
40POP_SAVED_FUNCTION_VISIBILITY
41
42#endif
diff --git a/shell/builtin_ulimit.c b/shell/builtin_ulimit.c
deleted file mode 100644
index 9f9205eb6..000000000
--- a/shell/builtin_ulimit.c
+++ /dev/null
@@ -1,228 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * ulimit builtin
4 *
5 * Adapted from ash applet code
6 *
7 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
8 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
9 * ash by J.T. Conklin.
10 *
11 * Public domain.
12 *
13 * Copyright (c) 2010 Tobias Klauser
14 * Split from ash.c and slightly adapted.
15 *
16 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
17 */
18#include "libbb.h"
19#include "builtin_ulimit.h"
20
21
22struct limits {
23 uint8_t cmd; /* RLIMIT_xxx fit into it */
24 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
25 char option;
26 const char *name;
27};
28
29static const struct limits limits_tbl[] = {
30#ifdef RLIMIT_FSIZE
31 { RLIMIT_FSIZE, 9, 'f', "file size (blocks)" },
32#endif
33#ifdef RLIMIT_CPU
34 { RLIMIT_CPU, 0, 't', "cpu time (seconds)" },
35#endif
36#ifdef RLIMIT_DATA
37 { RLIMIT_DATA, 10, 'd', "data seg size (kb)" },
38#endif
39#ifdef RLIMIT_STACK
40 { RLIMIT_STACK, 10, 's', "stack size (kb)" },
41#endif
42#ifdef RLIMIT_CORE
43 { RLIMIT_CORE, 9, 'c', "core file size (blocks)" },
44#endif
45#ifdef RLIMIT_RSS
46 { RLIMIT_RSS, 10, 'm', "resident set size (kb)" },
47#endif
48#ifdef RLIMIT_MEMLOCK
49 { RLIMIT_MEMLOCK, 10, 'l', "locked memory (kb)" },
50#endif
51#ifdef RLIMIT_NPROC
52 { RLIMIT_NPROC, 0, 'p', "processes" },
53#endif
54#ifdef RLIMIT_NOFILE
55 { RLIMIT_NOFILE, 0, 'n', "file descriptors" },
56#endif
57#ifdef RLIMIT_AS
58 { RLIMIT_AS, 10, 'v', "address space (kb)" },
59#endif
60#ifdef RLIMIT_LOCKS
61 { RLIMIT_LOCKS, 0, 'w', "locks" },
62#endif
63};
64
65enum {
66 OPT_hard = (1 << 0),
67 OPT_soft = (1 << 1),
68};
69
70/* "-": treat args as parameters of option with ASCII code 1 */
71static const char ulimit_opt_string[] = "-HSa"
72#ifdef RLIMIT_FSIZE
73 "f::"
74#endif
75#ifdef RLIMIT_CPU
76 "t::"
77#endif
78#ifdef RLIMIT_DATA
79 "d::"
80#endif
81#ifdef RLIMIT_STACK
82 "s::"
83#endif
84#ifdef RLIMIT_CORE
85 "c::"
86#endif
87#ifdef RLIMIT_RSS
88 "m::"
89#endif
90#ifdef RLIMIT_MEMLOCK
91 "l::"
92#endif
93#ifdef RLIMIT_NPROC
94 "p::"
95#endif
96#ifdef RLIMIT_NOFILE
97 "n::"
98#endif
99#ifdef RLIMIT_AS
100 "v::"
101#endif
102#ifdef RLIMIT_LOCKS
103 "w::"
104#endif
105 ;
106
107static void printlim(unsigned opts, const struct rlimit *limit,
108 const struct limits *l)
109{
110 rlim_t val;
111
112 val = limit->rlim_max;
113 if (!(opts & OPT_hard))
114 val = limit->rlim_cur;
115
116 if (val == RLIM_INFINITY)
117 printf("unlimited\n");
118 else {
119 val >>= l->factor_shift;
120 printf("%llu\n", (long long) val);
121 }
122}
123
124int FAST_FUNC shell_builtin_ulimit(char **argv)
125{
126 unsigned opts;
127 unsigned argc;
128
129 /* We can't use getopt32: need to handle commands like
130 * ulimit 123 -c2 -l 456
131 */
132
133 /* In case getopt was already called:
134 * reset the libc getopt() function, which keeps internal state.
135 */
136#ifdef __GLIBC__
137 optind = 0;
138#else /* BSD style */
139 optind = 1;
140 /* optreset = 1; */
141#endif
142 /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */
143
144 argc = 1;
145 while (argv[argc])
146 argc++;
147
148 opts = 0;
149 while (1) {
150 struct rlimit limit;
151 const struct limits *l;
152 int opt_char = getopt(argc, argv, ulimit_opt_string);
153
154 if (opt_char == -1)
155 break;
156 if (opt_char == 'H') {
157 opts |= OPT_hard;
158 continue;
159 }
160 if (opt_char == 'S') {
161 opts |= OPT_soft;
162 continue;
163 }
164
165 if (opt_char == 'a') {
166 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
167 getrlimit(l->cmd, &limit);
168 printf("-%c: %-30s ", l->option, l->name);
169 printlim(opts, &limit, l);
170 }
171 continue;
172 }
173
174 if (opt_char == 1)
175 opt_char = 'f';
176 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
177 if (opt_char == l->option) {
178 char *val_str;
179
180 getrlimit(l->cmd, &limit);
181
182 val_str = optarg;
183 if (!val_str && argv[optind] && argv[optind][0] != '-')
184 val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */
185 if (val_str) {
186 rlim_t val;
187
188 if (strcmp(val_str, "unlimited") == 0)
189 val = RLIM_INFINITY;
190 else {
191 if (sizeof(val) == sizeof(int))
192 val = bb_strtou(val_str, NULL, 10);
193 else if (sizeof(val) == sizeof(long))
194 val = bb_strtoul(val_str, NULL, 10);
195 else
196 val = bb_strtoull(val_str, NULL, 10);
197 if (errno) {
198 bb_error_msg("bad number");
199 return EXIT_FAILURE;
200 }
201 val <<= l->factor_shift;
202 }
203//bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val);
204 if (opts & OPT_hard)
205 limit.rlim_max = val;
206 if ((opts & OPT_soft) || opts == 0)
207 limit.rlim_cur = val;
208//bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max);
209 if (setrlimit(l->cmd, &limit) < 0) {
210 bb_perror_msg("error setting limit");
211 return EXIT_FAILURE;
212 }
213 } else {
214 printlim(opts, &limit, l);
215 }
216 break;
217 }
218 } /* for (every possible opt) */
219
220 if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) {
221 /* bad option. getopt already complained. */
222 break;
223 }
224
225 } /* while (there are options) */
226
227 return 0;
228}
diff --git a/shell/builtin_ulimit.h b/shell/builtin_ulimit.h
deleted file mode 100644
index ec1af78a6..000000000
--- a/shell/builtin_ulimit.h
+++ /dev/null
@@ -1,19 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Adapted from ash applet code
4 *
5 * Copyright (c) 2010 Tobias Klauser
6 * Split from ash.c and slightly adapted.
7 *
8 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
9 */
10#ifndef SHELL_BUILTIN_ULIMIT_H
11#define SHELL_BUILTIN_ULIMIT_H 1
12
13PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
14
15int FAST_FUNC shell_builtin_ulimit(char **argv);
16
17POP_SAVED_FUNCTION_VISIBILITY
18
19#endif
diff --git a/shell/hush.c b/shell/hush.c
index 72cfb232a..4cccf31a4 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -90,8 +90,6 @@
90#endif 90#endif
91 91
92#include "shell_common.h" 92#include "shell_common.h"
93#include "builtin_read.h"
94#include "builtin_ulimit.h"
95#include "math.h" 93#include "math.h"
96#include "match.h" 94#include "match.h"
97#if ENABLE_HUSH_RANDOM_SUPPORT 95#if ENABLE_HUSH_RANDOM_SUPPORT
diff --git a/shell/lash_unused.c b/shell/lash_unused.c
deleted file mode 100644
index 513918962..000000000
--- a/shell/lash_unused.c
+++ /dev/null
@@ -1,1576 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * lash -- the BusyBox Lame-Ass SHell
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is
8 * under the following liberal license: "We have placed this source code in the
9 * public domain. Use it in any project, free or commercial."
10 *
11 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
12 */
13
14/* This shell's parsing engine is officially at a dead-end. Future
15 * work shell work should be done using hush, msh, or ash. This is
16 * still a very useful, small shell -- it just don't need any more
17 * features beyond what it already has...
18 */
19
20//For debugging/development on the shell only...
21//#define DEBUG_SHELL
22
23#include <getopt.h>
24#include <glob.h>
25
26#include "libbb.h"
27
28#define expand_t glob_t
29
30/* Always enable for the moment... */
31#define CONFIG_LASH_PIPE_N_REDIRECTS
32#define CONFIG_LASH_JOB_CONTROL
33#define ENABLE_LASH_PIPE_N_REDIRECTS 1
34#define ENABLE_LASH_JOB_CONTROL 1
35
36
37enum { MAX_READ = 128 }; /* size of input buffer for 'read' builtin */
38#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
39
40
41#if ENABLE_LASH_PIPE_N_REDIRECTS
42enum redir_type { REDIRECT_INPUT, REDIRECT_OVERWRITE,
43 REDIRECT_APPEND
44};
45#endif
46
47enum {
48 DEFAULT_CONTEXT = 0x1,
49 IF_TRUE_CONTEXT = 0x2,
50 IF_FALSE_CONTEXT = 0x4,
51 THEN_EXP_CONTEXT = 0x8,
52 ELSE_EXP_CONTEXT = 0x10
53};
54
55#define LASH_OPT_DONE (1)
56#define LASH_OPT_SAW_QUOTE (2)
57
58#if ENABLE_LASH_PIPE_N_REDIRECTS
59struct redir_struct {
60 enum redir_type type; /* type of redirection */
61 int fd; /* file descriptor being redirected */
62 char *filename; /* file to redirect fd to */
63};
64#endif
65
66struct child_prog {
67 pid_t pid; /* 0 if exited */
68 char **argv; /* program name and arguments */
69 int num_redirects; /* elements in redirection array */
70 int is_stopped; /* is the program currently running? */
71 struct job *family; /* pointer back to the child's parent job */
72#if ENABLE_LASH_PIPE_N_REDIRECTS
73 struct redir_struct *redirects; /* I/O redirects */
74#endif
75};
76
77struct jobset {
78 struct job *head; /* head of list of running jobs */
79 struct job *fg; /* current foreground job */
80};
81
82struct job {
83 int jobid; /* job number */
84 int num_progs; /* total number of programs in job */
85 int running_progs; /* number of programs running */
86 char *text; /* name of job */
87 char *cmdbuf; /* buffer various argv's point into */
88 pid_t pgrp; /* process group ID for the job */
89 struct child_prog *progs; /* array of programs in job */
90 struct job *next; /* to track background commands */
91 int stopped_progs; /* number of programs alive, but stopped */
92 unsigned int job_context; /* bitmask defining current context */
93 struct jobset *job_list;
94};
95
96struct built_in_command {
97 const char *cmd; /* name */
98 const char *descr; /* description */
99 int (*function) (struct child_prog *); /* function ptr */
100};
101
102/* function prototypes for builtins */
103static int builtin_cd(struct child_prog *cmd);
104static int builtin_exec(struct child_prog *cmd);
105static int builtin_exit(struct child_prog *cmd);
106static int builtin_fg_bg(struct child_prog *cmd);
107static int builtin_help(struct child_prog *cmd);
108static int builtin_jobs(struct child_prog *dummy);
109static int builtin_pwd(struct child_prog *dummy);
110static int builtin_export(struct child_prog *cmd);
111static int builtin_source(struct child_prog *cmd);
112static int builtin_unset(struct child_prog *cmd);
113static int builtin_read(struct child_prog *cmd);
114
115
116/* function prototypes for shell stuff */
117static void checkjobs(struct jobset *job_list);
118static void remove_job(struct jobset *j_list, struct job *job);
119static int get_command_bufsiz(FILE *source, char *command);
120static int parse_command(char **command_ptr, struct job *job, int *inbg);
121static int run_command(struct job *newjob, int inbg, int outpipe[2]);
122static int pseudo_exec(struct child_prog *cmd) NORETURN;
123static int busy_loop(FILE *input);
124
125
126/* Table of built-in functions (these are non-forking builtins, meaning they
127 * can change global variables in the parent shell process but they will not
128 * work with pipes and redirects; 'unset foo | whatever' will not work) */
129static const struct built_in_command bltins[] = {
130 {"bg" , "Resume a job in the background", builtin_fg_bg},
131 {"cd" , "Change working directory", builtin_cd},
132 {"exec" , "Exec command, replacing this shell with the exec'd process", builtin_exec},
133 {"exit" , "Exit from shell()", builtin_exit},
134 {"fg" , "Bring job into the foreground", builtin_fg_bg},
135 {"jobs" , "Lists the active jobs", builtin_jobs},
136 {"export", "Set environment variable", builtin_export},
137 {"unset" , "Unset environment variable", builtin_unset},
138 {"read" , "Input environment variable", builtin_read},
139 {"." , "Source-in and run commands in a file", builtin_source},
140 /* These were "forked applets", but distinction was nuked */
141 /* Original comment retained: */
142 /* Table of forking built-in functions (things that fork cannot change global
143 * variables in the parent process, such as the current working directory) */
144 {"pwd" , "Print current directory", builtin_pwd},
145 {"help" , "List shell built-in commands", builtin_help},
146 /* to do: add ulimit */
147};
148
149
150#define VEC_LAST(v) v[ARRAY_SIZE(v)-1]
151
152
153static int shell_context; /* Type prompt trigger (PS1 or PS2) */
154
155
156/* Globals that are static to this file */
157static char *cwd;
158static char *local_pending_command;
159static struct jobset job_list = { NULL, NULL };
160static int global_argc;
161static char **global_argv;
162static llist_t *close_me_list;
163static int last_return_code;
164static int last_bg_pid;
165static unsigned int last_jobid;
166static int shell_terminal;
167static const char *PS1;
168static const char *PS2 = "> ";
169
170
171#ifdef DEBUG_SHELL
172static inline void debug_printf(const char *format, ...)
173{
174 va_list args;
175 va_start(args, format);
176 vfprintf(stderr, format, args);
177 va_end(args);
178}
179#else
180static inline void debug_printf(const char UNUSED_PARAM *format, ...) { }
181#endif
182
183/*
184 Most builtins need access to the struct child_prog that has
185 their arguments, previously coded as cmd->progs[0]. That coding
186 can exhibit a bug, if the builtin is not the first command in
187 a pipeline: "echo foo | exec sort" will attempt to exec foo.
188
189builtin previous use notes
190------ ----------------- ---------
191cd cmd->progs[0]
192exec cmd->progs[0] squashed bug: didn't look for applets or forking builtins
193exit cmd->progs[0]
194fg_bg cmd->progs[0], job_list->head, job_list->fg
195help 0
196jobs job_list->head
197pwd 0
198export cmd->progs[0]
199source cmd->progs[0]
200unset cmd->progs[0]
201read cmd->progs[0]
202
203I added "struct job *family;" to struct child_prog,
204and switched API to builtin_foo(struct child_prog *child);
205So cmd->text becomes child->family->text
206 cmd->job_context becomes child->family->job_context
207 cmd->progs[0] becomes *child
208 job_list becomes child->family->job_list
209 */
210
211
212static void update_cwd(void)
213{
214 cwd = xrealloc_getcwd_or_warn(cwd);
215 if (!cwd)
216 cwd = xstrdup(bb_msg_unknown);
217}
218
219/* built-in 'cd <path>' handler */
220static int builtin_cd(struct child_prog *child)
221{
222 char *newdir;
223
224 if (child->argv[1] == NULL)
225 newdir = getenv("HOME");
226 else
227 newdir = child->argv[1];
228 if (chdir(newdir)) {
229 bb_perror_msg("cd: %s", newdir);
230 return EXIT_FAILURE;
231 }
232 update_cwd();
233 return EXIT_SUCCESS;
234}
235
236/* built-in 'exec' handler */
237static int builtin_exec(struct child_prog *child)
238{
239 if (child->argv[1] == NULL)
240 return EXIT_SUCCESS; /* Really? */
241 child->argv++;
242 while (close_me_list)
243 close((long)llist_pop(&close_me_list));
244 pseudo_exec(child);
245 /* never returns */
246}
247
248/* built-in 'exit' handler */
249static int builtin_exit(struct child_prog *child)
250{
251 if (child->argv[1] == NULL)
252 exit(EXIT_SUCCESS);
253
254 exit(atoi(child->argv[1]));
255}
256
257/* built-in 'fg' and 'bg' handler */
258static int builtin_fg_bg(struct child_prog *child)
259{
260 int i, jobnum;
261 struct job *job;
262
263 /* If they gave us no args, assume they want the last backgrounded task */
264 if (!child->argv[1]) {
265 for (job = child->family->job_list->head; job; job = job->next) {
266 if (job->jobid == last_jobid) {
267 goto found;
268 }
269 }
270 bb_error_msg("%s: no current job", child->argv[0]);
271 return EXIT_FAILURE;
272 }
273 if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) {
274 bb_error_msg(bb_msg_invalid_arg, child->argv[1], child->argv[0]);
275 return EXIT_FAILURE;
276 }
277 for (job = child->family->job_list->head; job; job = job->next) {
278 if (job->jobid == jobnum) {
279 goto found;
280 }
281 }
282 bb_error_msg("%s: %d: no such job", child->argv[0], jobnum);
283 return EXIT_FAILURE;
284 found:
285 if (*child->argv[0] == 'f') {
286 /* Put the job into the foreground. */
287 tcsetpgrp(shell_terminal, job->pgrp);
288
289 child->family->job_list->fg = job;
290 }
291
292 /* Restart the processes in the job */
293 for (i = 0; i < job->num_progs; i++)
294 job->progs[i].is_stopped = 0;
295
296 job->stopped_progs = 0;
297
298 i = kill(- job->pgrp, SIGCONT);
299 if (i < 0) {
300 if (errno == ESRCH) {
301 remove_job(&job_list, job);
302 } else {
303 bb_perror_msg("kill (SIGCONT)");
304 }
305 }
306
307 return EXIT_SUCCESS;
308}
309
310/* built-in 'help' handler */
311static int builtin_help(struct child_prog UNUSED_PARAM *dummy)
312{
313 const struct built_in_command *x;
314
315 printf(
316 "Built-in commands:\n"
317 "------------------\n");
318 for (x = bltins; x <= &VEC_LAST(bltins); x++) {
319 if (x->descr == NULL)
320 continue;
321 printf("%s\t%s\n", x->cmd, x->descr);
322 }
323 bb_putchar('\n');
324 return EXIT_SUCCESS;
325}
326
327/* built-in 'jobs' handler */
328static int builtin_jobs(struct child_prog *child)
329{
330 struct job *job;
331 const char *status_string;
332
333 for (job = child->family->job_list->head; job; job = job->next) {
334 if (job->running_progs == job->stopped_progs)
335 status_string = "Stopped";
336 else
337 status_string = "Running";
338
339 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text);
340 }
341 return EXIT_SUCCESS;
342}
343
344
345/* built-in 'pwd' handler */
346static int builtin_pwd(struct child_prog UNUSED_PARAM *dummy)
347{
348 update_cwd();
349 puts(cwd);
350 return EXIT_SUCCESS;
351}
352
353/* built-in 'export VAR=value' handler */
354static int builtin_export(struct child_prog *child)
355{
356 int res;
357 char *v = child->argv[1];
358
359 if (v == NULL) {
360 char **e;
361 for (e = environ; *e; e++) {
362 puts(*e);
363 }
364 return 0;
365 }
366 res = putenv(v);
367 if (res)
368 bb_perror_msg("export");
369#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
370 if (strncmp(v, "PS1=", 4) == 0)
371 PS1 = getenv("PS1");
372#endif
373
374#if ENABLE_LOCALE_SUPPORT
375 // TODO: why getenv? "" would be just as good...
376 if (strncmp(v, "LC_ALL=", 7) == 0)
377 setlocale(LC_ALL, getenv("LC_ALL"));
378 if (strncmp(v, "LC_CTYPE=", 9) == 0)
379 setlocale(LC_CTYPE, getenv("LC_CTYPE"));
380#endif
381
382 return res;
383}
384
385/* built-in 'read VAR' handler */
386static int builtin_read(struct child_prog *child)
387{
388 int res = 0, len;
389 char *s;
390 char string[MAX_READ];
391
392 if (child->argv[1]) {
393 /* argument (VAR) given: put "VAR=" into buffer */
394 safe_strncpy(string, child->argv[1], MAX_READ-1);
395 len = strlen(string);
396 string[len++] = '=';
397 string[len] = '\0';
398 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
399 res = strlen(string);
400 if (res > len)
401 string[--res] = '\0'; /* chomp trailing newline */
402 /*
403 ** string should now contain "VAR=<value>"
404 ** copy it (putenv() won't do that, so we must make sure
405 ** the string resides in a static buffer!)
406 */
407 res = -1;
408 s = strdup(string);
409 if (s)
410 res = putenv(s);
411 if (res)
412 bb_perror_msg("read");
413 } else
414 fgets(string, sizeof(string), stdin);
415
416 return res;
417}
418
419/* Built-in '.' handler (read-in and execute commands from file) */
420static int builtin_source(struct child_prog *child)
421{
422 FILE *input;
423 int status;
424
425 input = fopen_or_warn(child->argv[1], "r");
426 if (!input) {
427 return EXIT_FAILURE;
428 }
429
430 llist_add_to(&close_me_list, (void *)(long)fileno(input));
431 /* Now run the file */
432 status = busy_loop(input);
433 fclose(input);
434 llist_pop(&close_me_list);
435 return status;
436}
437
438/* built-in 'unset VAR' handler */
439static int builtin_unset(struct child_prog *child)
440{
441 if (child->argv[1] == NULL) {
442 printf(bb_msg_requires_arg, "unset");
443 return EXIT_FAILURE;
444 }
445 unsetenv(child->argv[1]);
446 return EXIT_SUCCESS;
447}
448
449#if ENABLE_LASH_JOB_CONTROL
450/* free up all memory from a job */
451static void free_job(struct job *cmd)
452{
453 int i;
454 struct jobset *keep;
455
456 for (i = 0; i < cmd->num_progs; i++) {
457 free(cmd->progs[i].argv);
458#if ENABLE_LASH_PIPE_N_REDIRECTS
459 free(cmd->progs[i].redirects);
460#endif
461 }
462 free(cmd->progs);
463 free(cmd->text);
464 free(cmd->cmdbuf);
465 keep = cmd->job_list;
466 memset(cmd, 0, sizeof(struct job));
467 cmd->job_list = keep;
468}
469
470/* remove a job from a jobset */
471static void remove_job(struct jobset *j_list, struct job *job)
472{
473 struct job *prevjob;
474
475 free_job(job);
476 if (job == j_list->head) {
477 j_list->head = job->next;
478 } else {
479 prevjob = j_list->head;
480 while (prevjob->next != job)
481 prevjob = prevjob->next;
482 prevjob->next = job->next;
483 }
484
485 if (j_list->head)
486 last_jobid = j_list->head->jobid;
487 else
488 last_jobid = 0;
489
490 free(job);
491}
492
493/* Checks to see if any background processes have exited -- if they
494 have, figure out why and see if a job has completed */
495static void checkjobs(struct jobset *j_list)
496{
497 struct job *job;
498 pid_t childpid;
499 int status;
500 int prognum = 0;
501
502 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
503 for (job = j_list->head; job; job = job->next) {
504 prognum = 0;
505 while (prognum < job->num_progs &&
506 job->progs[prognum].pid != childpid) prognum++;
507 if (prognum < job->num_progs)
508 break;
509 }
510
511 /* This happens on backticked commands */
512 if (job == NULL)
513 return;
514
515 if (WIFEXITED(status) || WIFSIGNALED(status)) {
516 /* child exited */
517 job->running_progs--;
518 job->progs[prognum].pid = 0;
519
520 if (!job->running_progs) {
521 printf(JOB_STATUS_FORMAT, job->jobid, "Done", job->text);
522 last_jobid = 0;
523 remove_job(j_list, job);
524 }
525 } else {
526 /* child stopped */
527 job->stopped_progs++;
528 job->progs[prognum].is_stopped = 1;
529 }
530 }
531
532 if (childpid == -1 && errno != ECHILD)
533 bb_perror_msg("waitpid");
534}
535#else
536static void checkjobs(struct jobset *j_list)
537{
538}
539static void free_job(struct job *cmd)
540{
541}
542static void remove_job(struct jobset *j_list, struct job *job)
543{
544}
545#endif
546
547#if ENABLE_LASH_PIPE_N_REDIRECTS
548/* squirrel != NULL means we squirrel away copies of stdin, stdout,
549 * and stderr if they are redirected. */
550static int setup_redirects(struct child_prog *prog, int squirrel[])
551{
552 int i;
553 int openfd;
554 int mode = O_RDONLY;
555 struct redir_struct *redir = prog->redirects;
556
557 for (i = 0; i < prog->num_redirects; i++, redir++) {
558 switch (redir->type) {
559 case REDIRECT_INPUT:
560 mode = O_RDONLY;
561 break;
562 case REDIRECT_OVERWRITE:
563 mode = O_WRONLY | O_CREAT | O_TRUNC;
564 break;
565 case REDIRECT_APPEND:
566 mode = O_WRONLY | O_CREAT | O_APPEND;
567 break;
568 }
569
570 openfd = open_or_warn(redir->filename, mode);
571 if (openfd < 0) {
572 /* this could get lost if stderr has been redirected, but
573 bash and ash both lose it as well (though zsh doesn't!) */
574 return 1;
575 }
576
577 if (openfd != redir->fd) {
578 if (squirrel && redir->fd < 3) {
579 squirrel[redir->fd] = dup(redir->fd);
580 close_on_exec_on(squirrel[redir->fd]);
581 }
582 dup2(openfd, redir->fd);
583 close(openfd);
584 }
585 }
586
587 return 0;
588}
589
590static void restore_redirects(int squirrel[])
591{
592 int i, fd;
593 for (i = 0; i < 3; i++) {
594 fd = squirrel[i];
595 if (fd != -1) {
596 /* No error checking. I sure wouldn't know what
597 * to do with an error if I found one! */
598 dup2(fd, i);
599 close(fd);
600 }
601 }
602}
603#else
604static inline int setup_redirects(struct child_prog *prog, int squirrel[])
605{
606 return 0;
607}
608static inline void restore_redirects(int squirrel[])
609{
610}
611#endif
612
613static inline void cmdedit_set_initial_prompt(void)
614{
615#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
616 PS1 = NULL;
617#else
618 PS1 = getenv("PS1");
619 if (PS1 == 0)
620 PS1 = "\\w \\$ ";
621#endif
622}
623
624static inline const char* setup_prompt_string(void)
625{
626#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
627 /* Set up the prompt */
628 if (shell_context == 0) {
629 char *ns;
630 free((char*)PS1);
631 ns = xmalloc(strlen(cwd)+4);
632 sprintf(ns, "%s %c ", cwd, (geteuid() != 0) ? '$': '#');
633 PS1 = ns;
634 return ns;
635 } else {
636 return PS2;
637 }
638#else
639 return (shell_context == 0)? PS1 : PS2;
640#endif
641}
642
643#if ENABLE_FEATURE_EDITING
644static line_input_t *line_input_state;
645#endif
646
647static int get_command_bufsiz(FILE *source, char *command)
648{
649 const char *prompt_str;
650
651 if (source == NULL) {
652 if (local_pending_command) {
653 /* a command specified (-c option): return it & mark it done */
654 strncpy(command, local_pending_command, BUFSIZ);
655 local_pending_command = NULL;
656 return 0;
657 }
658 return 1;
659 }
660
661 if (source == stdin) {
662 prompt_str = setup_prompt_string();
663
664#if ENABLE_FEATURE_EDITING
665 /*
666 ** enable command line editing only while a command line
667 ** is actually being read; otherwise, we'll end up bequeathing
668 ** atexit() handlers and other unwanted stuff to our
669 ** child processes (rob@sysgo.de)
670 */
671 read_line_input(prompt_str, command, BUFSIZ, line_input_state);
672 return 0;
673#else
674 fputs(prompt_str, stdout);
675#endif
676 }
677
678 if (!fgets(command, BUFSIZ - 2, source)) {
679 if (source == stdin)
680 bb_putchar('\n');
681 return 1;
682 }
683
684 return 0;
685}
686
687static char * strsep_space(char *string, int * ix)
688{
689 /* Short circuit the trivial case */
690 if (!string || ! string[*ix])
691 return NULL;
692
693 /* Find the end of the token. */
694 while (string[*ix] && !isspace(string[*ix]) ) {
695 (*ix)++;
696 }
697
698 /* Find the end of any whitespace trailing behind
699 * the token and let that be part of the token */
700 while (string[*ix] && isspace(string[*ix])) {
701 (*ix)++;
702 }
703
704 if (!*ix) {
705 /* Nothing useful was found */
706 return NULL;
707 }
708
709 return xstrndup(string, *ix);
710}
711
712static int expand_arguments(char *command)
713{
714 static const char out_of_space[] ALIGN1 = "out of space during expansion";
715
716 int total_length = 0, length, i, retval, ix = 0;
717 expand_t expand_result;
718 char *tmpcmd, *cmd, *cmd_copy;
719 char *src, *dst, *var;
720 int flags = GLOB_NOCHECK
721#ifdef GLOB_BRACE
722 | GLOB_BRACE
723#endif
724#ifdef GLOB_TILDE
725 | GLOB_TILDE
726#endif
727 ;
728
729 /* get rid of the terminating \n */
730 chomp(command);
731
732 /* Fix up escape sequences to be the Real Thing(tm) */
733 while (command && command[ix]) {
734 if (command[ix] == '\\') {
735 const char *tmp = command+ix+1;
736 command[ix] = bb_process_escape_sequence( &tmp );
737 memmove(command+ix + 1, tmp, strlen(tmp)+1);
738 }
739 ix++;
740 }
741 /* Use glob and then fixup environment variables and such */
742
743 /* It turns out that glob is very stupid. We have to feed it one word at a
744 * time since it can't cope with a full string. Here we convert command
745 * (char*) into cmd (char**, one word per string) */
746
747 /* We need a clean copy, so strsep can mess up the copy while
748 * we write stuff into the original (in a minute) */
749 cmd = cmd_copy = xstrdup(command);
750 *command = '\0';
751 for (ix = 0, tmpcmd = cmd;
752 (tmpcmd = strsep_space(cmd, &ix)) != NULL; cmd += ix, ix = 0) {
753 if (*tmpcmd == '\0')
754 break;
755 /* we need to trim() the result for glob! */
756 trim(tmpcmd);
757 retval = glob(tmpcmd, flags, NULL, &expand_result);
758 free(tmpcmd); /* Free mem allocated by strsep_space */
759 if (retval == GLOB_NOSPACE) {
760 /* Mem may have been allocated... */
761 globfree(&expand_result);
762 bb_error_msg(out_of_space);
763 return FALSE;
764 } else if (retval != 0) {
765 /* Some other error. GLOB_NOMATCH shouldn't
766 * happen because of the GLOB_NOCHECK flag in
767 * the glob call. */
768 bb_error_msg("syntax error");
769 return FALSE;
770 } else {
771 /* Convert from char** (one word per string) to a simple char*,
772 * but don't overflow command which is BUFSIZ in length */
773 for (i = 0; i < expand_result.gl_pathc; i++) {
774 length = strlen(expand_result.gl_pathv[i]);
775 if (total_length+length+1 >= BUFSIZ) {
776 bb_error_msg(out_of_space);
777 return FALSE;
778 }
779 strcat(command+total_length, " ");
780 total_length += 1;
781 strcat(command+total_length, expand_result.gl_pathv[i]);
782 total_length += length;
783 }
784 globfree(&expand_result);
785 }
786 }
787 free(cmd_copy);
788 trim(command);
789
790 /* Now do the shell variable substitutions which
791 * wordexp can't do for us, namely $? and $! */
792 src = command;
793 while ((dst = strchr(src,'$')) != NULL) {
794 var = NULL;
795 switch (*(dst+1)) {
796 case '?':
797 var = itoa(last_return_code);
798 break;
799 case '!':
800 if (last_bg_pid == -1)
801 *var = '\0';
802 else
803 var = itoa(last_bg_pid);
804 break;
805 /* Everything else like $$, $#, $[0-9], etc. should all be
806 * expanded by wordexp(), so we can in theory skip that stuff
807 * here, but just to be on the safe side (i.e., since uClibc
808 * wordexp doesn't do this stuff yet), lets leave it in for
809 * now. */
810 case '$':
811 var = itoa(getpid());
812 break;
813 case '#':
814 var = itoa(global_argc - 1);
815 break;
816 case '0':case '1':case '2':case '3':case '4':
817 case '5':case '6':case '7':case '8':case '9':
818 {
819 int ixx = *(dst+1)-48+1;
820 if (ixx >= global_argc) {
821 var = '\0';
822 } else {
823 var = global_argv[ixx];
824 }
825 }
826 break;
827
828 }
829 if (var) {
830 /* a single character construction was found, and
831 * already handled in the case statement */
832 src = dst + 2;
833 } else {
834 /* Looks like an environment variable */
835 char delim_hold;
836 int num_skip_chars = 0;
837 int dstlen = strlen(dst);
838 /* Is this a ${foo} type variable? */
839 if (dstlen >= 2 && *(dst+1) == '{') {
840 src = strchr(dst+1, '}');
841 num_skip_chars = 1;
842 } else {
843 src = dst + 1;
844 while (isalnum(*src) || *src == '_') src++;
845 }
846 if (src == NULL) {
847 src = dst+dstlen;
848 }
849 delim_hold = *src;
850 *src = '\0'; /* temporary */
851 var = getenv(dst + 1 + num_skip_chars);
852 *src = delim_hold;
853 src += num_skip_chars;
854 }
855 if (var == NULL) {
856 /* Seems we got an un-expandable variable. So delete it. */
857 var = (char*)"";
858 }
859 {
860 int subst_len = strlen(var);
861 int trail_len = strlen(src);
862 if (dst+subst_len+trail_len >= command+BUFSIZ) {
863 bb_error_msg(out_of_space);
864 return FALSE;
865 }
866 /* Move stuff to the end of the string to accommodate
867 * filling the created gap with the new stuff */
868 memmove(dst+subst_len, src, trail_len+1);
869 /* Now copy in the new stuff */
870 memcpy(dst, var, subst_len);
871 src = dst+subst_len;
872 }
873 }
874
875 return TRUE;
876}
877
878/* Return cmd->num_progs as 0 if no command is present (e.g. an empty
879 line). If a valid command is found, command_ptr is set to point to
880 the beginning of the next command (if the original command had more
881 then one job associated with it) or NULL if no more commands are
882 present. */
883static int parse_command(char **command_ptr, struct job *job, int *inbg)
884{
885 char *command;
886 char *return_command = NULL;
887 char *src, *buf;
888 int argc_l;
889 int flag;
890 int argv_alloced;
891 char quote = '\0';
892 struct child_prog *prog;
893#if ENABLE_LASH_PIPE_N_REDIRECTS
894 int i;
895 char *chptr;
896#endif
897
898 /* skip leading white space */
899 *command_ptr = skip_whitespace(*command_ptr);
900
901 /* this handles empty lines or leading '#' characters */
902 if (!**command_ptr || (**command_ptr == '#')) {
903 job->num_progs = 0;
904 return 0;
905 }
906
907 *inbg = 0;
908 job->num_progs = 1;
909 job->progs = xmalloc(sizeof(*job->progs));
910
911 /* We set the argv elements to point inside of this string. The
912 memory is freed by free_job(). Allocate twice the original
913 length in case we need to quote every single character.
914
915 Getting clean memory relieves us of the task of NULL
916 terminating things and makes the rest of this look a bit
917 cleaner (though it is, admittedly, a tad less efficient) */
918 job->cmdbuf = command = xzalloc(2*strlen(*command_ptr) + 1);
919 job->text = NULL;
920
921 prog = job->progs;
922 prog->num_redirects = 0;
923 prog->is_stopped = 0;
924 prog->family = job;
925#if ENABLE_LASH_PIPE_N_REDIRECTS
926 prog->redirects = NULL;
927#endif
928
929 argv_alloced = 5;
930 prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
931 prog->argv[0] = job->cmdbuf;
932
933 flag = argc_l = 0;
934 buf = command;
935 src = *command_ptr;
936 while (*src && !(flag & LASH_OPT_DONE)) {
937 if (quote == *src) {
938 quote = '\0';
939 } else if (quote) {
940 if (*src == '\\') {
941 src++;
942 if (!*src) {
943 bb_error_msg("character expected after \\");
944 free_job(job);
945 return 1;
946 }
947
948 /* in shell, "\'" should yield \' */
949 if (*src != quote) {
950 *buf++ = '\\';
951 *buf++ = '\\';
952 }
953 } else
954 if (*src == '*' || *src == '?'
955 || *src == '[' || *src == ']'
956 ) {
957 *buf++ = '\\';
958 }
959 *buf++ = *src;
960 } else if (isspace(*src)) {
961 if (*prog->argv[argc_l] || (flag & LASH_OPT_SAW_QUOTE)) {
962 buf++, argc_l++;
963 /* +1 here leaves room for the NULL which ends argv */
964 if ((argc_l + 1) == argv_alloced) {
965 argv_alloced += 5;
966 prog->argv = xrealloc(prog->argv,
967 sizeof(*prog->argv) * argv_alloced);
968 }
969 prog->argv[argc_l] = buf;
970 flag ^= LASH_OPT_SAW_QUOTE;
971 }
972 } else
973 switch (*src) {
974 case '"':
975 case '\'':
976 quote = *src;
977 flag |= LASH_OPT_SAW_QUOTE;
978 break;
979
980 case '#': /* comment */
981 if (*(src-1)== '$')
982 *buf++ = *src;
983 else
984 flag |= LASH_OPT_DONE;
985 break;
986
987#if ENABLE_LASH_PIPE_N_REDIRECTS
988 case '>': /* redirects */
989 case '<':
990 i = prog->num_redirects++;
991 prog->redirects = xrealloc(prog->redirects,
992 sizeof(*prog->redirects) * (i + 1));
993
994 prog->redirects[i].fd = -1;
995 if (buf != prog->argv[argc_l]) {
996 /* the stuff before this character may be the file number
997 being redirected */
998 prog->redirects[i].fd =
999 strtol(prog->argv[argc_l], &chptr, 10);
1000
1001 if (*chptr && *prog->argv[argc_l]) {
1002 buf++, argc_l++;
1003 prog->argv[argc_l] = buf;
1004 }
1005 }
1006
1007 if (prog->redirects[i].fd == -1) {
1008 if (*src == '>')
1009 prog->redirects[i].fd = 1;
1010 else
1011 prog->redirects[i].fd = 0;
1012 }
1013
1014 if (*src++ == '>') {
1015 if (*src == '>')
1016 prog->redirects[i].type =
1017 REDIRECT_APPEND, src++;
1018 else
1019 prog->redirects[i].type = REDIRECT_OVERWRITE;
1020 } else {
1021 prog->redirects[i].type = REDIRECT_INPUT;
1022 }
1023
1024 /* This isn't POSIX sh compliant. Oh well. */
1025 chptr = src;
1026 chptr = skip_whitespace(chptr);
1027
1028 if (!*chptr) {
1029 bb_error_msg("file name expected after %c", *(src-1));
1030 free_job(job);
1031 job->num_progs = 0;
1032 return 1;
1033 }
1034
1035 prog->redirects[i].filename = buf;
1036 while (*chptr && !isspace(*chptr))
1037 *buf++ = *chptr++;
1038
1039 src = chptr - 1; /* we src++ later */
1040 prog->argv[argc_l] = ++buf;
1041 break;
1042
1043 case '|': /* pipe */
1044 /* finish this command */
1045 if (*prog->argv[argc_l] || flag & LASH_OPT_SAW_QUOTE)
1046 argc_l++;
1047 if (!argc_l) {
1048 goto empty_command_in_pipe;
1049 }
1050 prog->argv[argc_l] = NULL;
1051
1052 /* and start the next */
1053 job->num_progs++;
1054 job->progs = xrealloc(job->progs,
1055 sizeof(*job->progs) * job->num_progs);
1056 prog = job->progs + (job->num_progs - 1);
1057 prog->num_redirects = 0;
1058 prog->redirects = NULL;
1059 prog->is_stopped = 0;
1060 prog->family = job;
1061 argc_l = 0;
1062
1063 argv_alloced = 5;
1064 prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
1065 prog->argv[0] = ++buf;
1066
1067 src++;
1068 src = skip_whitespace(src);
1069
1070 if (!*src) {
1071empty_command_in_pipe:
1072 bb_error_msg("empty command in pipe");
1073 free_job(job);
1074 job->num_progs = 0;
1075 return 1;
1076 }
1077 src--; /* we'll ++ it at the end of the loop */
1078
1079 break;
1080#endif
1081
1082#if ENABLE_LASH_JOB_CONTROL
1083 case '&': /* background */
1084 *inbg = 1;
1085 /* fallthrough */
1086#endif
1087 case ';': /* multiple commands */
1088 flag |= LASH_OPT_DONE;
1089 return_command = *command_ptr + (src - *command_ptr) + 1;
1090 break;
1091
1092 case '\\':
1093 src++;
1094 if (!*src) {
1095 bb_error_msg("character expected after \\");
1096 free_job(job);
1097 return 1;
1098 }
1099 if (*src == '*' || *src == '[' || *src == ']'
1100 || *src == '?') *buf++ = '\\';
1101 /* fallthrough */
1102 default:
1103 *buf++ = *src;
1104 }
1105
1106 src++;
1107 }
1108
1109 if (*prog->argv[argc_l] || flag & LASH_OPT_SAW_QUOTE) {
1110 argc_l++;
1111 }
1112 if (!argc_l) {
1113 free_job(job);
1114 return 0;
1115 }
1116 prog->argv[argc_l] = NULL;
1117
1118 if (!return_command) {
1119 job->text = xstrdup(*command_ptr);
1120 } else {
1121 /* This leaves any trailing spaces, which is a bit sloppy */
1122 job->text = xstrndup(*command_ptr, return_command - *command_ptr);
1123 }
1124
1125 *command_ptr = return_command;
1126
1127 return 0;
1128}
1129
1130/* Run the child_prog, no matter what kind of command it uses.
1131 */
1132static int pseudo_exec(struct child_prog *child)
1133{
1134 const struct built_in_command *x;
1135
1136 /* Check if the command matches any of the non-forking builtins.
1137 * Depending on context, this might be redundant. But it's
1138 * easier to waste a few CPU cycles than it is to figure out
1139 * if this is one of those cases.
1140 */
1141 /* Check if the command matches any of the forking builtins. */
1142 for (x = bltins; x <= &VEC_LAST(bltins); x++) {
1143 if (strcmp(child->argv[0], x->cmd) == 0) {
1144 _exit(x->function(child));
1145 }
1146 }
1147
1148 /* Check if the command matches any busybox internal
1149 * commands ("applets") here. Following discussions from
1150 * November 2000 on busybox@busybox.net, don't use
1151 * bb_get_last_path_component_nostrip(). This way explicit
1152 * (with slashes) filenames will never be interpreted as an
1153 * applet, just like with builtins. This way the user can
1154 * override an applet with an explicit filename reference.
1155 * The only downside to this change is that an explicit
1156 * /bin/foo invocation will fork and exec /bin/foo, even if
1157 * /bin/foo is a symlink to busybox.
1158 */
1159 if (ENABLE_FEATURE_SH_STANDALONE) {
1160 run_applet_and_exit(child->argv[0], child->argv);
1161 }
1162
1163 execvp(child->argv[0], child->argv);
1164
1165 /* Do not use bb_perror_msg_and_die() here, since we must not
1166 * call exit() but should call _exit() instead */
1167 bb_simple_perror_msg(child->argv[0]);
1168 _exit(EXIT_FAILURE);
1169}
1170
1171static void insert_job(struct job *newjob, int inbg)
1172{
1173 struct job *thejob;
1174 struct jobset *j_list = newjob->job_list;
1175
1176 /* find the ID for thejob to use */
1177 newjob->jobid = 1;
1178 for (thejob = j_list->head; thejob; thejob = thejob->next)
1179 if (thejob->jobid >= newjob->jobid)
1180 newjob->jobid = thejob->jobid + 1;
1181
1182 /* add thejob to the list of running jobs */
1183 if (!j_list->head) {
1184 thejob = j_list->head = xmalloc(sizeof(*thejob));
1185 } else {
1186 for (thejob = j_list->head; thejob->next; thejob = thejob->next) /* nothing */;
1187 thejob->next = xmalloc(sizeof(*thejob));
1188 thejob = thejob->next;
1189 }
1190
1191 *thejob = *newjob; /* physically copy the struct job */
1192 thejob->next = NULL;
1193 thejob->running_progs = thejob->num_progs;
1194 thejob->stopped_progs = 0;
1195
1196#if ENABLE_LASH_JOB_CONTROL
1197 if (inbg) {
1198 /* we don't wait for background thejobs to return -- append it
1199 to the list of backgrounded thejobs and leave it alone */
1200 printf("[%d] %d\n", thejob->jobid,
1201 newjob->progs[newjob->num_progs - 1].pid);
1202 last_jobid = newjob->jobid;
1203 last_bg_pid = newjob->progs[newjob->num_progs - 1].pid;
1204 } else {
1205 newjob->job_list->fg = thejob;
1206
1207 /* move the new process group into the foreground */
1208 /* Ignore errors since child could have already exited */
1209 tcsetpgrp(shell_terminal, newjob->pgrp);
1210 }
1211#endif
1212}
1213
1214static int run_command(struct job *newjob, int inbg, int outpipe[2])
1215{
1216 /* struct job *thejob; */
1217 int i;
1218 int nextin, nextout;
1219 int pipefds[2]; /* pipefd[0] is for reading */
1220 const struct built_in_command *x;
1221 struct child_prog *child;
1222
1223 nextin = 0;
1224 for (i = 0; i < newjob->num_progs; i++) {
1225 child = &(newjob->progs[i]);
1226
1227 nextout = 1;
1228 if ((i + 1) < newjob->num_progs) {
1229 xpipe(pipefds);
1230 nextout = pipefds[1];
1231 } else if (outpipe[1] != -1) {
1232 nextout = outpipe[1];
1233 }
1234
1235 /* Check if the command matches any non-forking builtins,
1236 * but only if this is a simple command.
1237 * Non-forking builtins within pipes have to fork anyway,
1238 * and are handled in pseudo_exec. "echo foo | read bar"
1239 * is doomed to failure, and doesn't work on bash, either.
1240 */
1241 if (newjob->num_progs == 1) {
1242 int rcode;
1243 int squirrel[] = {-1, -1, -1};
1244
1245 /* Check if the command sets an environment variable. */
1246 if (strchr(child->argv[0], '=') != NULL) {
1247 child->argv[1] = child->argv[0];
1248 return builtin_export(child);
1249 }
1250
1251 for (x = bltins; x <= &VEC_LAST(bltins); x++) {
1252 if (strcmp(child->argv[0], x->cmd) == 0) {
1253 setup_redirects(child, squirrel);
1254 rcode = x->function(child);
1255 restore_redirects(squirrel);
1256 return rcode;
1257 }
1258 }
1259#if ENABLE_FEATURE_SH_STANDALONE
1260 {
1261 int a = find_applet_by_name(child->argv[i]);
1262 if (a >= 0 && APPLET_IS_NOFORK(a)) {
1263 setup_redirects(child, squirrel);
1264 rcode = run_nofork_applet(a, child->argv + i);
1265 restore_redirects(squirrel);
1266 return rcode;
1267 }
1268 }
1269#endif
1270 }
1271
1272#if BB_MMU
1273 child->pid = fork();
1274#else
1275 child->pid = vfork();
1276#endif
1277 if (!child->pid) {
1278 /* Set the handling for job control signals back to the default. */
1279 signal(SIGINT, SIG_DFL);
1280 signal(SIGQUIT, SIG_DFL);
1281 signal(SIGTSTP, SIG_DFL);
1282 signal(SIGTTIN, SIG_DFL);
1283 signal(SIGTTOU, SIG_DFL);
1284 signal(SIGCHLD, SIG_DFL);
1285
1286 /* Close all open filehandles. */
1287 while (close_me_list)
1288 close((long)llist_pop(&close_me_list));
1289
1290 if (outpipe[1] != -1) {
1291 close(outpipe[0]);
1292 }
1293 if (nextin != 0) {
1294 dup2(nextin, 0);
1295 close(nextin);
1296 }
1297
1298 if (nextout != 1) {
1299 dup2(nextout, 1);
1300 dup2(nextout, 2); /* Really? */
1301 close(nextout);
1302 close(pipefds[0]);
1303 }
1304
1305 /* explicit redirects override pipes */
1306 setup_redirects(child,NULL);
1307
1308 pseudo_exec(child);
1309 }
1310 if (outpipe[1] != -1) {
1311 close(outpipe[1]);
1312 }
1313
1314 /* put our child in the process group whose leader is the
1315 first process in this pipe */
1316 setpgid(child->pid, newjob->progs[0].pid);
1317 if (nextin != 0)
1318 close(nextin);
1319 if (nextout != 1)
1320 close(nextout);
1321
1322 /* If there isn't another process, nextin is garbage
1323 but it doesn't matter */
1324 nextin = pipefds[0];
1325 }
1326
1327 newjob->pgrp = newjob->progs[0].pid;
1328
1329 insert_job(newjob, inbg);
1330
1331 return 0;
1332}
1333
1334static int busy_loop(FILE *input)
1335{
1336 char *command;
1337 char *next_command = NULL;
1338 struct job newjob;
1339 int i;
1340 int inbg = 0;
1341 int status;
1342#if ENABLE_LASH_JOB_CONTROL
1343 pid_t parent_pgrp;
1344 /* save current owner of TTY so we can restore it on exit */
1345 parent_pgrp = tcgetpgrp(shell_terminal);
1346#endif
1347 newjob.job_list = &job_list;
1348 newjob.job_context = DEFAULT_CONTEXT;
1349
1350 command = xzalloc(BUFSIZ);
1351
1352 while (1) {
1353 if (!job_list.fg) {
1354 /* no job is in the foreground */
1355
1356 /* see if any background processes have exited */
1357 checkjobs(&job_list);
1358
1359 if (!next_command) {
1360 if (get_command_bufsiz(input, command))
1361 break;
1362 next_command = command;
1363 }
1364
1365 if (!expand_arguments(next_command)) {
1366 free(command);
1367 command = xzalloc(BUFSIZ);
1368 next_command = NULL;
1369 continue;
1370 }
1371
1372 if (!parse_command(&next_command, &newjob, &inbg)
1373 && newjob.num_progs
1374 ) {
1375 int pipefds[2] = { -1, -1 };
1376 debug_printf("job=%p fed to run_command by busy_loop()'\n",
1377 &newjob);
1378 run_command(&newjob, inbg, pipefds);
1379 }
1380 else {
1381 free(command);
1382 command = xzalloc(BUFSIZ);
1383 next_command = NULL;
1384 }
1385 } else {
1386 /* a job is running in the foreground; wait for it */
1387 i = 0;
1388 while (!job_list.fg->progs[i].pid ||
1389 job_list.fg->progs[i].is_stopped == 1) i++;
1390
1391 if (waitpid(job_list.fg->progs[i].pid, &status, WUNTRACED) < 0) {
1392 if (errno != ECHILD) {
1393 bb_perror_msg_and_die("waitpid(%d)", job_list.fg->progs[i].pid);
1394 }
1395 }
1396
1397 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1398 /* the child exited */
1399 job_list.fg->running_progs--;
1400 job_list.fg->progs[i].pid = 0;
1401
1402 last_return_code = WEXITSTATUS(status);
1403
1404 if (!job_list.fg->running_progs) {
1405 /* child exited */
1406 remove_job(&job_list, job_list.fg);
1407 job_list.fg = NULL;
1408 }
1409 }
1410#if ENABLE_LASH_JOB_CONTROL
1411 else {
1412 /* the child was stopped */
1413 job_list.fg->stopped_progs++;
1414 job_list.fg->progs[i].is_stopped = 1;
1415
1416 if (job_list.fg->stopped_progs == job_list.fg->running_progs) {
1417 printf("\n" JOB_STATUS_FORMAT, job_list.fg->jobid,
1418 "Stopped", job_list.fg->text);
1419 job_list.fg = NULL;
1420 }
1421 }
1422
1423 if (!job_list.fg) {
1424 /* move the shell to the foreground */
1425 /* suppress messages when run from /linuxrc mag@sysgo.de */
1426 if (tcsetpgrp(shell_terminal, getpgrp()) && errno != ENOTTY)
1427 bb_perror_msg("tcsetpgrp");
1428 }
1429#endif
1430 }
1431 }
1432 free(command);
1433
1434#if ENABLE_LASH_JOB_CONTROL
1435 /* return controlling TTY back to parent process group before exiting */
1436 if (tcsetpgrp(shell_terminal, parent_pgrp) && errno != ENOTTY)
1437 bb_perror_msg("tcsetpgrp");
1438#endif
1439
1440 /* return exit status if called with "-c" */
1441 if (input == NULL && WIFEXITED(status))
1442 return WEXITSTATUS(status);
1443
1444 return 0;
1445}
1446
1447#if ENABLE_FEATURE_CLEAN_UP
1448static void free_memory(void)
1449{
1450 free(cwd);
1451
1452 if (job_list.fg && !job_list.fg->running_progs) {
1453 remove_job(&job_list, job_list.fg);
1454 }
1455}
1456#else
1457void free_memory(void);
1458#endif
1459
1460#if ENABLE_LASH_JOB_CONTROL
1461/* Make sure we have a controlling tty. If we get started under a job
1462 * aware app (like bash for example), make sure we are now in charge so
1463 * we don't fight over who gets the foreground */
1464static void setup_job_control(void)
1465{
1466 int status;
1467 pid_t shell_pgrp;
1468
1469 /* Loop until we are in the foreground. */
1470 while ((status = tcgetpgrp(shell_terminal)) >= 0) {
1471 shell_pgrp = getpgrp();
1472 if (status == shell_pgrp) {
1473 break;
1474 }
1475 kill(- shell_pgrp, SIGTTIN);
1476 }
1477
1478 /* Ignore interactive and job-control signals. */
1479 signal(SIGINT, SIG_IGN);
1480 signal(SIGQUIT, SIG_IGN);
1481 signal(SIGTSTP, SIG_IGN);
1482 signal(SIGTTIN, SIG_IGN);
1483 signal(SIGTTOU, SIG_IGN);
1484 signal(SIGCHLD, SIG_IGN);
1485
1486 /* Put ourselves in our own process group. */
1487 setsid();
1488 shell_pgrp = getpid();
1489 setpgid(shell_pgrp, shell_pgrp);
1490
1491 /* Grab control of the terminal. */
1492 tcsetpgrp(shell_terminal, shell_pgrp);
1493}
1494#else
1495static inline void setup_job_control(void)
1496{
1497}
1498#endif
1499
1500int lash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1501int lash_main(int argc, char **argv)
1502{
1503 unsigned opt;
1504 FILE *input = stdin;
1505
1506 global_argc = argc;
1507 global_argv = argv;
1508
1509#if ENABLE_FEATURE_EDITING
1510 line_input_state = new_line_input_t(FOR_SHELL);
1511#endif
1512
1513 /* These variables need re-initializing when recursing */
1514 last_jobid = 0;
1515 close_me_list = NULL;
1516 job_list.head = NULL;
1517 job_list.fg = NULL;
1518 last_return_code = 1;
1519
1520 if (global_argv[0] && global_argv[0][0] == '-') {
1521 FILE *prof_input;
1522 prof_input = fopen_for_read("/etc/profile");
1523 if (prof_input) {
1524 llist_add_to(&close_me_list, (void *)(long)fileno(prof_input));
1525 /* Now run the file */
1526 busy_loop(prof_input);
1527 fclose_if_not_stdin(prof_input);
1528 llist_pop(&close_me_list);
1529 }
1530 }
1531
1532 opt = getopt32(argv, "+ic:", &local_pending_command);
1533#define LASH_OPT_i (1<<0)
1534#define LASH_OPT_c (1<<1)
1535 if (opt & LASH_OPT_c) {
1536 input = NULL;
1537 optind++;
1538 global_argv += optind;
1539 }
1540 /* A shell is interactive if the `-i' flag was given, or if all of
1541 * the following conditions are met:
1542 * no -c command
1543 * no arguments remaining or the -s flag given
1544 * standard input is a terminal
1545 * standard output is a terminal
1546 * Refer to Posix.2, the description of the `sh' utility. */
1547 if (global_argv[optind] == NULL && input == stdin
1548 && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
1549 ) {
1550 opt |= LASH_OPT_i;
1551 }
1552 setup_job_control();
1553 if (opt & LASH_OPT_i) {
1554 /* Looks like they want an interactive shell */
1555 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
1556 printf("\n\n%s built-in shell (lash)\n"
1557 "Enter 'help' for a list of built-in commands.\n\n",
1558 bb_banner);
1559 }
1560 } else if (!local_pending_command && global_argv[optind]) {
1561 //printf( "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1562 input = xfopen_for_read(global_argv[optind]);
1563 /* be lazy, never mark this closed */
1564 llist_add_to(&close_me_list, (void *)(long)fileno(input));
1565 }
1566
1567 /* initialize the cwd -- this is never freed...*/
1568 update_cwd();
1569
1570 if (ENABLE_FEATURE_CLEAN_UP) atexit(free_memory);
1571
1572 if (ENABLE_FEATURE_EDITING) cmdedit_set_initial_prompt();
1573 else PS1 = NULL;
1574
1575 return busy_loop(input);
1576}
diff --git a/shell/msh_unused.c b/shell/msh_unused.c
deleted file mode 100644
index fe2cca816..000000000
--- a/shell/msh_unused.c
+++ /dev/null
@@ -1,5336 +0,0 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Minix shell port for busybox
4 *
5 * This version of the Minix shell was adapted for use in busybox
6 * by Erik Andersen <andersen@codepoet.org>
7 *
8 * - backtick expansion did not work properly
9 * Jonas Holmberg <jonas.holmberg@axis.com>
10 * Robert Schwebel <r.schwebel@pengutronix.de>
11 * Erik Andersen <andersen@codepoet.org>
12 *
13 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
14 */
15#include <sys/times.h>
16#include <setjmp.h>
17
18#ifdef STANDALONE
19# ifndef _GNU_SOURCE
20# define _GNU_SOURCE
21# endif
22# include <sys/types.h>
23# include <sys/stat.h>
24# include <sys/wait.h>
25# include <signal.h>
26# include <stdio.h>
27# include <stdlib.h>
28# include <unistd.h>
29# include <string.h>
30# include <errno.h>
31# include <dirent.h>
32# include <fcntl.h>
33# include <ctype.h>
34# include <assert.h>
35# define bb_dev_null "/dev/null"
36# define DEFAULT_SHELL "/proc/self/exe"
37# define bb_banner "busybox standalone"
38# define ENABLE_FEATURE_SH_STANDALONE 0
39# define bb_msg_memory_exhausted "memory exhausted"
40# define xmalloc(size) malloc(size)
41# define msh_main(argc,argv) main(argc,argv)
42# define safe_read(fd,buf,count) read(fd,buf,count)
43# define nonblock_safe_read(fd,buf,count) read(fd,buf,count)
44# define NOT_LONE_DASH(s) ((s)[0] != '-' || (s)[1])
45# define LONE_CHAR(s,c) ((s)[0] == (c) && !(s)[1])
46# define NORETURN __attribute__ ((__noreturn__))
47static int find_applet_by_name(const char *applet)
48{
49 return -1;
50}
51static char *utoa_to_buf(unsigned n, char *buf, unsigned buflen)
52{
53 unsigned i, out, res;
54 assert(sizeof(unsigned) == 4);
55 if (buflen) {
56 out = 0;
57 for (i = 1000000000; i; i /= 10) {
58 res = n / i;
59 if (res || out || i == 1) {
60 if (!--buflen) break;
61 out++;
62 n -= res*i;
63 *buf++ = '0' + res;
64 }
65 }
66 }
67 return buf;
68}
69static char *itoa_to_buf(int n, char *buf, unsigned buflen)
70{
71 if (buflen && n < 0) {
72 n = -n;
73 *buf++ = '-';
74 buflen--;
75 }
76 return utoa_to_buf((unsigned)n, buf, buflen);
77}
78static char local_buf[12];
79static char *itoa(int n)
80{
81 *(itoa_to_buf(n, local_buf, sizeof(local_buf))) = '\0';
82 return local_buf;
83}
84#else
85# include "busybox.h" /* for applet_names */
86#endif
87
88//#define MSHDEBUG 4
89
90#ifdef MSHDEBUG
91static int mshdbg = MSHDEBUG;
92
93#define DBGPRINTF(x) if (mshdbg > 0) printf x
94#define DBGPRINTF0(x) if (mshdbg > 0) printf x
95#define DBGPRINTF1(x) if (mshdbg > 1) printf x
96#define DBGPRINTF2(x) if (mshdbg > 2) printf x
97#define DBGPRINTF3(x) if (mshdbg > 3) printf x
98#define DBGPRINTF4(x) if (mshdbg > 4) printf x
99#define DBGPRINTF5(x) if (mshdbg > 5) printf x
100#define DBGPRINTF6(x) if (mshdbg > 6) printf x
101#define DBGPRINTF7(x) if (mshdbg > 7) printf x
102#define DBGPRINTF8(x) if (mshdbg > 8) printf x
103#define DBGPRINTF9(x) if (mshdbg > 9) printf x
104
105static int mshdbg_rc = 0;
106
107#define RCPRINTF(x) if (mshdbg_rc) printf x
108
109#else
110
111#define DBGPRINTF(x)
112#define DBGPRINTF0(x) ((void)0)
113#define DBGPRINTF1(x) ((void)0)
114#define DBGPRINTF2(x) ((void)0)
115#define DBGPRINTF3(x) ((void)0)
116#define DBGPRINTF4(x) ((void)0)
117#define DBGPRINTF5(x) ((void)0)
118#define DBGPRINTF6(x) ((void)0)
119#define DBGPRINTF7(x) ((void)0)
120#define DBGPRINTF8(x) ((void)0)
121#define DBGPRINTF9(x) ((void)0)
122
123#define RCPRINTF(x) ((void)0)
124
125#endif /* MSHDEBUG */
126
127
128#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
129# define DEFAULT_ROOT_PROMPT "\\u:\\w> "
130# define DEFAULT_USER_PROMPT "\\u:\\w$ "
131#else
132# define DEFAULT_ROOT_PROMPT "# "
133# define DEFAULT_USER_PROMPT "$ "
134#endif
135
136
137/* -------- sh.h -------- */
138/*
139 * shell
140 */
141
142#define LINELIM 2100
143#define NPUSH 8 /* limit to input nesting */
144
145#undef NOFILE
146#define NOFILE 20 /* Number of open files */
147#define NUFILE 10 /* Number of user-accessible files */
148#define FDBASE 10 /* First file usable by Shell */
149
150/*
151 * values returned by wait
152 */
153#define WAITSIG(s) ((s) & 0177)
154#define WAITVAL(s) (((s) >> 8) & 0377)
155#define WAITCORE(s) (((s) & 0200) != 0)
156
157/*
158 * library and system definitions
159 */
160typedef void xint; /* base type of jmp_buf, for not broken compilers */
161
162/*
163 * shell components
164 */
165#define NOBLOCK ((struct op *)NULL)
166#define NOWORD ((char *)NULL)
167#define NOWORDS ((char **)NULL)
168#define NOPIPE ((int *)NULL)
169
170/*
171 * redirection
172 */
173struct ioword {
174 smallint io_flag; /* action (below) */
175 int io_fd; /* fd affected */
176 char *io_name; /* file name */
177};
178
179#define IOREAD 1 /* < */
180#define IOHERE 2 /* << (here file) */
181#define IOWRITE 4 /* > */
182#define IOCAT 8 /* >> */
183#define IOXHERE 16 /* ${}, ` in << */
184#define IODUP 32 /* >&digit */
185#define IOCLOSE 64 /* >&- */
186
187#define IODEFAULT (-1) /* "default" IO fd */
188
189
190/*
191 * Description of a command or an operation on commands.
192 * Might eventually use a union.
193 */
194struct op {
195 smallint op_type; /* operation type, see Txxxx below */
196 char **op_words; /* arguments to a command */
197 struct ioword **ioact; /* IO actions (eg, < > >>) */
198 struct op *left;
199 struct op *right;
200 char *str; /* identifier for case and for */
201};
202
203#define TCOM 1 /* command */
204#define TPAREN 2 /* (c-list) */
205#define TPIPE 3 /* a | b */
206#define TLIST 4 /* a [&;] b */
207#define TOR 5 /* || */
208#define TAND 6 /* && */
209#define TFOR 7
210#define TDO 8
211#define TCASE 9
212#define TIF 10
213#define TWHILE 11
214#define TUNTIL 12
215#define TELIF 13
216#define TPAT 14 /* pattern in case */
217#define TBRACE 15 /* {c-list} */
218#define TASYNC 16 /* c & */
219/* Added to support "." file expansion */
220#define TDOT 17
221
222/* Strings for names to make debug easier */
223#ifdef MSHDEBUG
224static const char *const T_CMD_NAMES[] = {
225 "PLACEHOLDER",
226 "TCOM",
227 "TPAREN",
228 "TPIPE",
229 "TLIST",
230 "TOR",
231 "TAND",
232 "TFOR",
233 "TDO",
234 "TCASE",
235 "TIF",
236 "TWHILE",
237 "TUNTIL",
238 "TELIF",
239 "TPAT",
240 "TBRACE",
241 "TASYNC",
242 "TDOT",
243};
244#endif
245
246#define AREASIZE (90000)
247
248/*
249 * flags to control evaluation of words
250 */
251#define DOSUB 1 /* interpret $, `, and quotes */
252#define DOBLANK 2 /* perform blank interpretation */
253#define DOGLOB 4 /* interpret [?* */
254#define DOKEY 8 /* move words with `=' to 2nd arg. list */
255#define DOTRIM 16 /* trim resulting string */
256
257#define DOALL (DOSUB|DOBLANK|DOGLOB|DOKEY|DOTRIM)
258
259
260struct brkcon {
261 jmp_buf brkpt;
262 struct brkcon *nextlev;
263};
264
265
266static smallint trapset; /* trap pending (signal number) */
267
268static smallint yynerrs; /* yacc (flag) */
269
270/* moved to G: static char line[LINELIM]; */
271
272#if ENABLE_FEATURE_EDITING
273static char *current_prompt;
274static line_input_t *line_input_state;
275#endif
276
277
278/*
279 * other functions
280 */
281static const char *rexecve(char *c, char **v, char **envp);
282static char *evalstr(char *cp, int f);
283static char *putn(int n);
284static char *unquote(char *as);
285static int rlookup(char *n);
286static struct wdblock *glob(char *cp, struct wdblock *wb);
287static int my_getc(int ec);
288static int subgetc(char ec, int quoted);
289static char **makenv(int all, struct wdblock *wb);
290static char **eval(char **ap, int f);
291static int setstatus(int s);
292static int waitfor(int lastpid, int canintr);
293
294static void onintr(int s); /* SIGINT handler */
295
296static int newenv(int f);
297static void quitenv(void);
298static void next(int f);
299static void setdash(void);
300static void onecommand(void);
301static void runtrap(int i);
302
303
304/* -------- area stuff -------- */
305
306#define REGSIZE sizeof(struct region)
307#define GROWBY (256)
308/* #define SHRINKBY (64) */
309#undef SHRINKBY
310#define FREE (32767)
311#define BUSY (0)
312#define ALIGN (sizeof(int)-1)
313
314
315struct region {
316 struct region *next;
317 int area;
318};
319
320
321/* -------- grammar stuff -------- */
322typedef union {
323 char *cp;
324 char **wp;
325 int i;
326 struct op *o;
327} YYSTYPE;
328
329#define WORD 256
330#define LOGAND 257
331#define LOGOR 258
332#define BREAK 259
333#define IF 260
334#define THEN 261
335#define ELSE 262
336#define ELIF 263
337#define FI 264
338#define CASE 265
339#define ESAC 266
340#define FOR 267
341#define WHILE 268
342#define UNTIL 269
343#define DO 270
344#define DONE 271
345#define IN 272
346/* Added for "." file expansion */
347#define DOT 273
348
349#define YYERRCODE 300
350
351/* flags to yylex */
352#define CONTIN 01 /* skip new lines to complete command */
353
354static struct op *pipeline(int cf);
355static struct op *andor(void);
356static struct op *c_list(void);
357static int synio(int cf);
358static void musthave(int c, int cf);
359static struct op *simple(void);
360static struct op *nested(int type, int mark);
361static struct op *command(int cf);
362static struct op *dogroup(int onlydone);
363static struct op *thenpart(void);
364static struct op *elsepart(void);
365static struct op *caselist(void);
366static struct op *casepart(void);
367static char **pattern(void);
368static char **wordlist(void);
369static struct op *list(struct op *t1, struct op *t2);
370static struct op *block(int type, struct op *t1, struct op *t2, char **wp);
371static struct op *newtp(void);
372static struct op *namelist(struct op *t);
373static char **copyw(void);
374static void word(char *cp);
375static struct ioword **copyio(void);
376static struct ioword *io(int u, int f, char *cp);
377static int yylex(int cf);
378static int collect(int c, int c1);
379static int dual(int c);
380static void diag(int ec);
381static char *tree(unsigned size);
382
383/* -------- var.h -------- */
384
385struct var {
386 char *value;
387 char *name;
388 struct var *next;
389 char status;
390};
391
392#define COPYV 1 /* flag to setval, suggesting copy */
393#define RONLY 01 /* variable is read-only */
394#define EXPORT 02 /* variable is to be exported */
395#define GETCELL 04 /* name & value space was got with getcell */
396
397static int yyparse(void);
398
399
400/* -------- io.h -------- */
401/* io buffer */
402struct iobuf {
403 unsigned id; /* buffer id */
404 char buf[512]; /* buffer */
405 char *bufp; /* pointer into buffer */
406 char *ebufp; /* pointer to end of buffer */
407};
408
409/* possible arguments to an IO function */
410struct ioarg {
411 const char *aword;
412 char **awordlist;
413 int afile; /* file descriptor */
414 unsigned afid; /* buffer id */
415 off_t afpos; /* file position */
416 struct iobuf *afbuf; /* buffer for this file */
417};
418
419/* an input generator's state */
420struct io {
421 int (*iofn) (struct ioarg *, struct io *);
422 struct ioarg *argp;
423 int peekc;
424 char prev; /* previous character read by readc() */
425 char nlcount; /* for `'s */
426 char xchar; /* for `'s */
427 char task; /* reason for pushed IO */
428};
429/* ->task: */
430#define XOTHER 0 /* none of the below */
431#define XDOLL 1 /* expanding ${} */
432#define XGRAVE 2 /* expanding `'s */
433#define XIO 3 /* file IO */
434
435
436/*
437 * input generators for IO structure
438 */
439static int nlchar(struct ioarg *ap);
440static int strchar(struct ioarg *ap);
441static int qstrchar(struct ioarg *ap);
442static int filechar(struct ioarg *ap);
443static int herechar(struct ioarg *ap);
444static int linechar(struct ioarg *ap);
445static int gravechar(struct ioarg *ap, struct io *iop);
446static int qgravechar(struct ioarg *ap, struct io *iop);
447static int dolchar(struct ioarg *ap);
448static int wdchar(struct ioarg *ap);
449static void scraphere(void);
450static void freehere(int area);
451static void gethere(void);
452static void markhere(char *s, struct ioword *iop);
453static int herein(char *hname, int xdoll);
454static int run(struct ioarg *argp, int (*f) (struct ioarg *));
455
456
457static int eofc(void);
458static int readc(void);
459static void unget(int c);
460static void ioecho(char c);
461
462
463/*
464 * IO control
465 */
466static void pushio(struct ioarg *argp, int (*f) (struct ioarg *));
467#define PUSHIO(what,arg,gen) ((temparg.what = (arg)), pushio(&temparg, (gen)))
468static int remap(int fd);
469static int openpipe(int *pv);
470static void closepipe(int *pv);
471static struct io *setbase(struct io *ip);
472
473/* -------- word.h -------- */
474
475#define NSTART 16 /* default number of words to allow for initially */
476
477struct wdblock {
478 short w_bsize;
479 short w_nword;
480 /* bounds are arbitrary */
481 char *w_words[1];
482};
483
484static struct wdblock *addword(char *wd, struct wdblock *wb);
485static struct wdblock *newword(int nw);
486static char **getwords(struct wdblock *wb);
487
488/* -------- misc stuff -------- */
489
490static int dolabel(struct op *t, char **args);
491static int dohelp(struct op *t, char **args);
492static int dochdir(struct op *t, char **args);
493static int doshift(struct op *t, char **args);
494static int dologin(struct op *t, char **args);
495static int doumask(struct op *t, char **args);
496static int doexec(struct op *t, char **args);
497static int dodot(struct op *t, char **args);
498static int dowait(struct op *t, char **args);
499static int doread(struct op *t, char **args);
500static int doeval(struct op *t, char **args);
501static int dotrap(struct op *t, char **args);
502static int dobreak(struct op *t, char **args);
503static int doexit(struct op *t, char **args);
504static int doexport(struct op *t, char **args);
505static int doreadonly(struct op *t, char **args);
506static int doset(struct op *t, char **args);
507static int dotimes(struct op *t, char **args);
508static int docontinue(struct op *t, char **args);
509
510static int forkexec(struct op *t, int *pin, int *pout, int no_fork, char **wp);
511static int execute(struct op *t, int *pin, int *pout, int no_fork);
512static int iosetup(struct ioword *iop, int pipein, int pipeout);
513static void brkset(struct brkcon *bc);
514static int getsig(char *s);
515static void setsig(int n, sighandler_t f);
516static int getn(char *as);
517static int brkcontin(char *cp, int val);
518static void rdexp(char **wp, void (*f) (struct var *), int key);
519static void badid(char *s);
520static void varput(char *s, int out);
521static int expand(const char *cp, struct wdblock **wbp, int f);
522static char *blank(int f);
523static int dollar(int quoted);
524static int grave(int quoted);
525static void globname(char *we, char *pp);
526static char *generate(char *start1, char *end1, char *middle, char *end);
527static int anyspcl(struct wdblock *wb);
528static void readhere(char **name, char *s, int ec);
529static int xxchar(struct ioarg *ap);
530
531struct here {
532 char *h_tag;
533 char h_dosub;
534 struct ioword *h_iop;
535 struct here *h_next;
536};
537
538static const char *const signame[] = {
539 "Signal 0",
540 "Hangup",
541 NULL, /* interrupt */
542 "Quit",
543 "Illegal instruction",
544 "Trace/BPT trap",
545 "Abort",
546 "Bus error",
547 "Floating Point Exception",
548 "Killed",
549 "SIGUSR1",
550 "SIGSEGV",
551 "SIGUSR2",
552 NULL, /* broken pipe */
553 "Alarm clock",
554 "Terminated"
555};
556
557
558typedef int (*builtin_func_ptr)(struct op *, char **);
559
560struct builtincmd {
561 const char *name;
562 builtin_func_ptr builtinfunc;
563};
564
565static const struct builtincmd builtincmds[] = {
566 { "." , dodot },
567 { ":" , dolabel },
568 { "break" , dobreak },
569 { "cd" , dochdir },
570 { "continue", docontinue },
571 { "eval" , doeval },
572 { "exec" , doexec },
573 { "exit" , doexit },
574 { "export" , doexport },
575 { "help" , dohelp },
576 { "login" , dologin },
577 { "newgrp" , dologin },
578 { "read" , doread },
579 { "readonly", doreadonly },
580 { "set" , doset },
581 { "shift" , doshift },
582 { "times" , dotimes },
583 { "trap" , dotrap },
584 { "umask" , doumask },
585 { "wait" , dowait },
586 { NULL , NULL },
587};
588
589static struct op *dowholefile(int /*, int*/);
590
591
592/* Globals */
593static char **dolv;
594static int dolc;
595static uint8_t exstat;
596static smallint gflg; /* (seems to be a parse error indicator) */
597static smallint interactive; /* Is this an interactive shell */
598static smallint execflg;
599static smallint isbreak; /* "break" statement was seen */
600static int multiline; /* '\n' changed to ';' (counter) */
601static struct op *outtree; /* result from parser */
602static xint *failpt;
603static xint *errpt;
604static struct brkcon *brklist;
605static struct wdblock *wdlist;
606static struct wdblock *iolist;
607
608#ifdef MSHDEBUG
609static struct var *mshdbg_var;
610#endif
611static struct var *vlist; /* dictionary */
612static struct var *homedir; /* home directory */
613static struct var *prompt; /* main prompt */
614static struct var *cprompt; /* continuation prompt */
615static struct var *path; /* search path for commands */
616static struct var *shell; /* shell to interpret command files */
617static struct var *ifs; /* field separators */
618
619static int areanum; /* current allocation area */
620static smallint intr; /* interrupt pending (bool) */
621static smallint heedint = 1; /* heed interrupt signals (bool) */
622static int inparse;
623static char *null = (char*)""; /* null value for variable */
624static void (*qflag)(int) = SIG_IGN;
625static int startl;
626static int peeksym;
627static int nlseen;
628static int iounit = IODEFAULT;
629static YYSTYPE yylval;
630static char *elinep; /* done in main(): = line + sizeof(line) - 5 */
631
632static struct here *inhere; /* list of hear docs while parsing */
633static struct here *acthere; /* list of active here documents */
634static struct region *areabot; /* bottom of area */
635static struct region *areatop; /* top of area */
636static struct region *areanxt; /* starting point of scan */
637static void *brktop;
638static void *brkaddr;
639
640#define AFID_NOBUF (~0)
641#define AFID_ID 0
642
643
644/*
645 * parsing & execution environment
646 */
647struct env {
648 char *linep;
649 struct io *iobase;
650 struct io *iop;
651 xint *errpt; /* void * */
652 int iofd;
653 struct env *oenv;
654};
655
656
657struct globals {
658 struct env global_env;
659 struct ioarg temparg; // = { .afid = AFID_NOBUF }; /* temporary for PUSHIO */
660 unsigned bufid; // = AFID_ID; /* buffer id counter */
661 char ourtrap[_NSIG + 1];
662 char *trap[_NSIG + 1];
663 struct iobuf sharedbuf; /* in main(): set to { AFID_NOBUF } */
664 struct iobuf mainbuf; /* in main(): set to { AFID_NOBUF } */
665 struct ioarg ioargstack[NPUSH];
666 /*
667 * flags:
668 * -e: quit on error
669 * -k: look for name=value everywhere on command line
670 * -n: no execution
671 * -t: exit after reading and executing one command
672 * -v: echo as read
673 * -x: trace
674 * -u: unset variables net diagnostic
675 */
676 char flags['z' - 'a' + 1];
677 char filechar_cmdbuf[BUFSIZ];
678 char line[LINELIM];
679 char child_cmd[LINELIM];
680
681 struct io iostack[NPUSH];
682
683 char grave__var_name[LINELIM];
684 char grave__alt_value[LINELIM];
685};
686
687#define G (*ptr_to_globals)
688#define global_env (G.global_env )
689#define temparg (G.temparg )
690#define bufid (G.bufid )
691#define ourtrap (G.ourtrap )
692#define trap (G.trap )
693#define sharedbuf (G.sharedbuf )
694#define mainbuf (G.mainbuf )
695#define ioargstack (G.ioargstack )
696/* this looks weird, but is OK ... we index FLAG with 'a'...'z' */
697#define FLAG (G.flags - 'a' )
698#define filechar_cmdbuf (G.filechar_cmdbuf)
699#define line (G.line )
700#define child_cmd (G.child_cmd )
701#define iostack (G.iostack )
702#define INIT_G() do { \
703 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
704 global_env.linep = line; \
705 global_env.iobase = iostack; \
706 global_env.iop = iostack - 1; \
707 global_env.iofd = FDBASE; \
708 temparg.afid = AFID_NOBUF; \
709 bufid = AFID_ID; \
710} while (0)
711
712
713/* in substitution */
714#define INSUB() (global_env.iop->task == XGRAVE || global_env.iop->task == XDOLL)
715
716#define RUN(what, arg, gen) ((temparg.what = (arg)), run(&temparg, (gen)))
717
718#ifdef MSHDEBUG
719static void print_tree(struct op *head)
720{
721 if (head == NULL) {
722 DBGPRINTF(("PRINT_TREE: no tree\n"));
723 return;
724 }
725
726 DBGPRINTF(("NODE: %p, left %p, right %p\n", head, head->left,
727 head->right));
728
729 if (head->left)
730 print_tree(head->left);
731
732 if (head->right)
733 print_tree(head->right);
734}
735#endif /* MSHDEBUG */
736
737
738/*
739 * IO functions
740 */
741static void prs(const char *s)
742{
743 if (*s)
744 xwrite_str(STDERR_FILENO, s);
745}
746
747static void prn(unsigned u)
748{
749 prs(itoa(u));
750}
751
752static void echo(char **wp)
753{
754 int i;
755
756 prs("+");
757 for (i = 0; wp[i]; i++) {
758 if (i)
759 prs(" ");
760 prs(wp[i]);
761 }
762 prs("\n");
763}
764
765static void closef(int i)
766{
767 if (i > 2)
768 close(i);
769}
770
771static void closeall(void)
772{
773 int u;
774
775 for (u = NUFILE; u < NOFILE;)
776 close(u++);
777}
778
779
780/* fail but return to process next command */
781static void fail(void) NORETURN;
782static void fail(void)
783{
784 longjmp(failpt, 1);
785 /* NOTREACHED */
786}
787
788/* abort shell (or fail in subshell) */
789static void leave(void) NORETURN;
790static void leave(void)
791{
792 DBGPRINTF(("LEAVE: leave called!\n"));
793
794 if (execflg)
795 fail();
796 scraphere();
797 freehere(1);
798 runtrap(0);
799 _exit(exstat);
800 /* NOTREACHED */
801}
802
803static void warn(const char *s)
804{
805 if (*s) {
806 prs(s);
807 if (!exstat)
808 exstat = 255;
809 }
810 prs("\n");
811 if (FLAG['e'])
812 leave();
813}
814
815static void err(const char *s)
816{
817 warn(s);
818 if (FLAG['n'])
819 return;
820 if (!interactive)
821 leave();
822 if (global_env.errpt)
823 longjmp(global_env.errpt, 1);
824 closeall();
825 global_env.iop = global_env.iobase = iostack;
826}
827
828
829/* -------- area.c -------- */
830
831/*
832 * All memory between (char *)areabot and (char *)(areatop+1) is
833 * exclusively administered by the area management routines.
834 * It is assumed that sbrk() and brk() manipulate the high end.
835 */
836
837#define sbrk(X) ({ \
838 void * __q = (void *)-1; \
839 if (brkaddr + (int)(X) < brktop) { \
840 __q = brkaddr; \
841 brkaddr += (int)(X); \
842 } \
843 __q; \
844})
845
846static void initarea(void)
847{
848 brkaddr = xmalloc(AREASIZE);
849 brktop = brkaddr + AREASIZE;
850
851 while ((long) sbrk(0) & ALIGN)
852 sbrk(1);
853 areabot = (struct region *) sbrk(REGSIZE);
854
855 areabot->next = areabot;
856 areabot->area = BUSY;
857 areatop = areabot;
858 areanxt = areabot;
859}
860
861static char *getcell(unsigned nbytes)
862{
863 int nregio;
864 struct region *p, *q;
865 int i;
866
867 if (nbytes == 0) {
868 puts("getcell(0)");
869 abort();
870 }
871 /* silly and defeats the algorithm */
872 /*
873 * round upwards and add administration area
874 */
875 nregio = (nbytes + (REGSIZE - 1)) / REGSIZE + 1;
876 p = areanxt;
877 for (;;) {
878 if (p->area > areanum) {
879 /*
880 * merge free cells
881 */
882 while ((q = p->next)->area > areanum && q != areanxt)
883 p->next = q->next;
884 /*
885 * exit loop if cell big enough
886 */
887 if (q >= p + nregio)
888 goto found;
889 }
890 p = p->next;
891 if (p == areanxt)
892 break;
893 }
894 i = nregio >= GROWBY ? nregio : GROWBY;
895 p = (struct region *) sbrk(i * REGSIZE);
896 if (p == (struct region *) -1)
897 return NULL;
898 p--;
899 if (p != areatop) {
900 puts("not contig");
901 abort(); /* allocated areas are contiguous */
902 }
903 q = p + i;
904 p->next = q;
905 p->area = FREE;
906 q->next = areabot;
907 q->area = BUSY;
908 areatop = q;
909 found:
910 /*
911 * we found a FREE area big enough, pointed to by 'p', and up to 'q'
912 */
913 areanxt = p + nregio;
914 if (areanxt < q) {
915 /*
916 * split into requested area and rest
917 */
918 if (areanxt + 1 > q) {
919 puts("OOM");
920 abort(); /* insufficient space left for admin */
921 }
922 areanxt->next = q;
923 areanxt->area = FREE;
924 p->next = areanxt;
925 }
926 p->area = areanum;
927 return (char *) (p + 1);
928}
929
930static void freecell(char *cp)
931{
932 struct region *p;
933
934 p = (struct region *) cp;
935 if (p != NULL) {
936 p--;
937 if (p < areanxt)
938 areanxt = p;
939 p->area = FREE;
940 }
941}
942#define DELETE(obj) freecell((char *)obj)
943
944static void freearea(int a)
945{
946 struct region *p, *top;
947
948 top = areatop;
949 for (p = areabot; p != top; p = p->next)
950 if (p->area >= a)
951 p->area = FREE;
952}
953
954static void setarea(char *cp, int a)
955{
956 struct region *p;
957
958 p = (struct region *) cp;
959 if (p != NULL)
960 (p - 1)->area = a;
961}
962
963static int getarea(char *cp)
964{
965 return ((struct region *) cp - 1)->area;
966}
967
968static void garbage(void)
969{
970 struct region *p, *q, *top;
971
972 top = areatop;
973 for (p = areabot; p != top; p = p->next) {
974 if (p->area > areanum) {
975 while ((q = p->next)->area > areanum)
976 p->next = q->next;
977 areanxt = p;
978 }
979 }
980#ifdef SHRINKBY
981 if (areatop >= q + SHRINKBY && q->area > areanum) {
982 brk((char *) (q + 1));
983 q->next = areabot;
984 q->area = BUSY;
985 areatop = q;
986 }
987#endif
988}
989
990static void *get_space(int n)
991{
992 char *cp;
993
994 cp = getcell(n);
995 if (cp == NULL)
996 err("out of string space");
997 return cp;
998}
999
1000static char *strsave(const char *s, int a)
1001{
1002 char *cp;
1003
1004 cp = get_space(strlen(s) + 1);
1005 if (cp == NULL) {
1006// FIXME: I highly doubt this is good.
1007 return (char*)"";
1008 }
1009 setarea(cp, a);
1010 strcpy(cp, s);
1011 return cp;
1012}
1013
1014
1015/* -------- var.c -------- */
1016
1017static int eqname(const char *n1, const char *n2)
1018{
1019 for (; *n1 != '=' && *n1 != '\0'; n1++)
1020 if (*n2++ != *n1)
1021 return 0;
1022 return *n2 == '\0' || *n2 == '=';
1023}
1024
1025static const char *findeq(const char *cp)
1026{
1027 while (*cp != '\0' && *cp != '=')
1028 cp++;
1029 return cp;
1030}
1031
1032/*
1033 * Find the given name in the dictionary
1034 * and return its value. If the name was
1035 * not previously there, enter it now and
1036 * return a null value.
1037 */
1038static struct var *lookup(const char *n)
1039{
1040// FIXME: dirty hack
1041 static struct var dummy;
1042
1043 struct var *vp;
1044 const char *cp;
1045 char *xp;
1046 int c;
1047
1048 if (isdigit(*n)) {
1049 dummy.name = (char*)n;
1050 for (c = 0; isdigit(*n) && c < 1000; n++)
1051 c = c * 10 + *n - '0';
1052 dummy.status = RONLY;
1053 dummy.value = (c <= dolc ? dolv[c] : null);
1054 return &dummy;
1055 }
1056
1057 for (vp = vlist; vp; vp = vp->next)
1058 if (eqname(vp->name, n))
1059 return vp;
1060
1061 cp = findeq(n);
1062 vp = get_space(sizeof(*vp));
1063 if (vp == 0 || (vp->name = get_space((int) (cp - n) + 2)) == NULL) {
1064 dummy.name = dummy.value = (char*)"";
1065 return &dummy;
1066 }
1067
1068 xp = vp->name;
1069 while ((*xp = *n++) != '\0' && *xp != '=')
1070 xp++;
1071 *xp++ = '=';
1072 *xp = '\0';
1073 setarea((char *) vp, 0);
1074 setarea((char *) vp->name, 0);
1075 vp->value = null;
1076 vp->next = vlist;
1077 vp->status = GETCELL;
1078 vlist = vp;
1079 return vp;
1080}
1081
1082/*
1083 * if name is not NULL, it must be
1084 * a prefix of the space `val',
1085 * and end with `='.
1086 * this is all so that exporting
1087 * values is reasonably painless.
1088 */
1089static void nameval(struct var *vp, const char *val, const char *name)
1090{
1091 const char *cp;
1092 char *xp;
1093 int fl;
1094
1095 if (vp->status & RONLY) {
1096 xp = vp->name;
1097 while (*xp && *xp != '=')
1098 fputc(*xp++, stderr);
1099 err(" is read-only");
1100 return;
1101 }
1102 fl = 0;
1103 if (name == NULL) {
1104 xp = get_space(strlen(vp->name) + strlen(val) + 2);
1105 if (xp == NULL)
1106 return;
1107 /* make string: name=value */
1108 setarea(xp, 0);
1109 name = xp;
1110 cp = vp->name;
1111 while ((*xp = *cp++) != '\0' && *xp != '=')
1112 xp++;
1113 *xp++ = '=';
1114 strcpy(xp, val);
1115 val = xp;
1116 fl = GETCELL;
1117 }
1118 if (vp->status & GETCELL)
1119 freecell(vp->name); /* form new string `name=value' */
1120 vp->name = (char*)name;
1121 vp->value = (char*)val;
1122 vp->status |= fl;
1123}
1124
1125/*
1126 * give variable at `vp' the value `val'.
1127 */
1128static void setval(struct var *vp, const char *val)
1129{
1130 nameval(vp, val, NULL);
1131}
1132
1133static void export(struct var *vp)
1134{
1135 vp->status |= EXPORT;
1136}
1137
1138static void ronly(struct var *vp)
1139{
1140 if (isalpha(vp->name[0]) || vp->name[0] == '_') /* not an internal symbol */
1141 vp->status |= RONLY;
1142}
1143
1144static int isassign(const char *s)
1145{
1146 unsigned char c;
1147 DBGPRINTF7(("ISASSIGN: enter, s=%s\n", s));
1148
1149 c = *s;
1150 /* no isalpha() - we shouldn't use locale */
1151 /* c | 0x20 - lowercase (Latin) letters */
1152 if (c != '_' && (unsigned)((c|0x20) - 'a') > 25)
1153 /* not letter */
1154 return 0;
1155
1156 while (1) {
1157 c = *++s;
1158 if (c == '=')
1159 return 1;
1160 if (c == '\0')
1161 return 0;
1162 if (c != '_'
1163 && (unsigned)(c - '0') > 9 /* not number */
1164 && (unsigned)((c|0x20) - 'a') > 25 /* not letter */
1165 ) {
1166 return 0;
1167 }
1168 }
1169}
1170
1171static int assign(const char *s, int cf)
1172{
1173 const char *cp;
1174 struct var *vp;
1175
1176 DBGPRINTF7(("ASSIGN: enter, s=%s, cf=%d\n", s, cf));
1177
1178 if (!isalpha(*s) && *s != '_')
1179 return 0;
1180 for (cp = s; *cp != '='; cp++)
1181 if (*cp == '\0' || (!isalnum(*cp) && *cp != '_'))
1182 return 0;
1183 vp = lookup(s);
1184 nameval(vp, ++cp, cf == COPYV ? NULL : s);
1185 if (cf != COPYV)
1186 vp->status &= ~GETCELL;
1187 return 1;
1188}
1189
1190static int checkname(char *cp)
1191{
1192 DBGPRINTF7(("CHECKNAME: enter, cp=%s\n", cp));
1193
1194 if (!isalpha(*cp++) && *(cp - 1) != '_')
1195 return 0;
1196 while (*cp)
1197 if (!isalnum(*cp++) && *(cp - 1) != '_')
1198 return 0;
1199 return 1;
1200}
1201
1202static void putvlist(int f, int out)
1203{
1204 struct var *vp;
1205
1206 for (vp = vlist; vp; vp = vp->next) {
1207 if (vp->status & f && (isalpha(*vp->name) || *vp->name == '_')) {
1208 if (vp->status & EXPORT)
1209 write(out, "export ", 7);
1210 if (vp->status & RONLY)
1211 write(out, "readonly ", 9);
1212 write(out, vp->name, (int) (findeq(vp->name) - vp->name));
1213 write(out, "\n", 1);
1214 }
1215 }
1216}
1217
1218
1219/*
1220 * trap handling
1221 */
1222static void sig(int i)
1223{
1224 trapset = i;
1225 signal(i, sig);
1226}
1227
1228static void runtrap(int i)
1229{
1230 char *trapstr;
1231
1232 trapstr = trap[i];
1233 if (trapstr == NULL)
1234 return;
1235
1236 if (i == 0)
1237 trap[i] = NULL;
1238
1239 RUN(aword, trapstr, nlchar);
1240}
1241
1242
1243static void setdash(void)
1244{
1245 char *cp;
1246 int c;
1247 char m['z' - 'a' + 1];
1248
1249 cp = m;
1250 for (c = 'a'; c <= 'z'; c++)
1251 if (FLAG[c])
1252 *cp++ = c;
1253 *cp = '\0';
1254 setval(lookup("-"), m);
1255}
1256
1257static int newfile(char *s)
1258{
1259 int f;
1260
1261 DBGPRINTF7(("NEWFILE: opening %s\n", s));
1262
1263 f = 0;
1264 if (NOT_LONE_DASH(s)) {
1265 DBGPRINTF(("NEWFILE: s is %s\n", s));
1266 f = open(s, O_RDONLY);
1267 if (f < 0) {
1268 prs(s);
1269 err(": can't open");
1270 return 1;
1271 }
1272 }
1273
1274 next(remap(f));
1275 return 0;
1276}
1277
1278
1279#ifdef UNUSED
1280struct op *scantree(struct op *head)
1281{
1282 struct op *dotnode;
1283
1284 if (head == NULL)
1285 return NULL;
1286
1287 if (head->left != NULL) {
1288 dotnode = scantree(head->left);
1289 if (dotnode)
1290 return dotnode;
1291 }
1292
1293 if (head->right != NULL) {
1294 dotnode = scantree(head->right);
1295 if (dotnode)
1296 return dotnode;
1297 }
1298
1299 if (head->op_words == NULL)
1300 return NULL;
1301
1302 DBGPRINTF5(("SCANTREE: checking node %p\n", head));
1303
1304 if ((head->op_type != TDOT) && LONE_CHAR(head->op_words[0], '.')) {
1305 DBGPRINTF5(("SCANTREE: dot found in node %p\n", head));
1306 return head;
1307 }
1308
1309 return NULL;
1310}
1311#endif
1312
1313
1314static void onecommand(void)
1315{
1316 int i;
1317 jmp_buf m1;
1318
1319 DBGPRINTF(("ONECOMMAND: enter, outtree=%p\n", outtree));
1320
1321 while (global_env.oenv)
1322 quitenv();
1323
1324 areanum = 1;
1325 freehere(areanum);
1326 freearea(areanum);
1327 garbage();
1328 wdlist = NULL;
1329 iolist = NULL;
1330 global_env.errpt = NULL;
1331 global_env.linep = line;
1332 yynerrs = 0;
1333 multiline = 0;
1334 inparse = 1;
1335 intr = 0;
1336 execflg = 0;
1337
1338 failpt = m1;
1339 setjmp(failpt); /* Bruce Evans' fix */
1340 failpt = m1;
1341 if (setjmp(failpt) || yyparse() || intr) {
1342 DBGPRINTF(("ONECOMMAND: this is not good.\n"));
1343
1344 while (global_env.oenv)
1345 quitenv();
1346 scraphere();
1347 if (!interactive && intr)
1348 leave();
1349 inparse = 0;
1350 intr = 0;
1351 return;
1352 }
1353
1354 inparse = 0;
1355 brklist = 0;
1356 intr = 0;
1357 execflg = 0;
1358
1359 if (!FLAG['n']) {
1360 DBGPRINTF(("ONECOMMAND: calling execute, t=outtree=%p\n",
1361 outtree));
1362 execute(outtree, NOPIPE, NOPIPE, /* no_fork: */ 0);
1363 }
1364
1365 if (!interactive && intr) {
1366 execflg = 0;
1367 leave();
1368 }
1369
1370 i = trapset;
1371 if (i != 0) {
1372 trapset = 0;
1373 runtrap(i);
1374 }
1375}
1376
1377static int newenv(int f)
1378{
1379 struct env *ep;
1380
1381 DBGPRINTF(("NEWENV: f=%d (indicates quitenv and return)\n", f));
1382
1383 if (f) {
1384 quitenv();
1385 return 1;
1386 }
1387
1388 ep = get_space(sizeof(*ep));
1389 if (ep == NULL) {
1390 while (global_env.oenv)
1391 quitenv();
1392 fail();
1393 }
1394 *ep = global_env;
1395 global_env.oenv = ep;
1396 global_env.errpt = errpt;
1397
1398 return 0;
1399}
1400
1401static void quitenv(void)
1402{
1403 struct env *ep;
1404 int fd;
1405
1406 DBGPRINTF(("QUITENV: global_env.oenv=%p\n", global_env.oenv));
1407
1408 ep = global_env.oenv;
1409 if (ep != NULL) {
1410 fd = global_env.iofd;
1411 global_env = *ep;
1412 /* should close `'d files */
1413 DELETE(ep);
1414 while (--fd >= global_env.iofd)
1415 close(fd);
1416 }
1417}
1418
1419/*
1420 * Is character c in s?
1421 */
1422static int any(int c, const char *s)
1423{
1424 while (*s)
1425 if (*s++ == c)
1426 return 1;
1427 return 0;
1428}
1429
1430/*
1431 * Is any character from s1 in s2?
1432 */
1433static int anys(const char *s1, const char *s2)
1434{
1435 while (*s1)
1436 if (any(*s1++, s2))
1437 return 1;
1438 return 0;
1439}
1440
1441static char *putn(int n)
1442{
1443 return itoa(n);
1444}
1445
1446static void next(int f)
1447{
1448 PUSHIO(afile, f, filechar);
1449}
1450
1451static void onintr(int s UNUSED_PARAM) /* ANSI C requires a parameter */
1452{
1453 signal(SIGINT, onintr);
1454 intr = 1;
1455 if (interactive) {
1456 if (inparse) {
1457 prs("\n");
1458 fail();
1459 }
1460 } else if (heedint) {
1461 execflg = 0;
1462 leave();
1463 }
1464}
1465
1466
1467/* -------- gmatch.c -------- */
1468/*
1469 * int gmatch(string, pattern)
1470 * char *string, *pattern;
1471 *
1472 * Match a pattern as in sh(1).
1473 */
1474
1475#define CMASK 0377
1476#define QUOTE 0200
1477#define QMASK (CMASK & ~QUOTE)
1478#define NOT '!' /* might use ^ */
1479
1480static const char *cclass(const char *p, int sub)
1481{
1482 int c, d, not, found;
1483
1484 not = (*p == NOT);
1485 if (not != 0)
1486 p++;
1487 found = not;
1488 do {
1489 if (*p == '\0')
1490 return NULL;
1491 c = *p & CMASK;
1492 if (p[1] == '-' && p[2] != ']') {
1493 d = p[2] & CMASK;
1494 p++;
1495 } else
1496 d = c;
1497 if (c == sub || (c <= sub && sub <= d))
1498 found = !not;
1499 } while (*++p != ']');
1500 return found ? p + 1 : NULL;
1501}
1502
1503static int gmatch(const char *s, const char *p)
1504{
1505 int sc, pc;
1506
1507 if (s == NULL || p == NULL)
1508 return 0;
1509
1510 while ((pc = *p++ & CMASK) != '\0') {
1511 sc = *s++ & QMASK;
1512 switch (pc) {
1513 case '[':
1514 p = cclass(p, sc);
1515 if (p == NULL)
1516 return 0;
1517 break;
1518
1519 case '?':
1520 if (sc == 0)
1521 return 0;
1522 break;
1523
1524 case '*':
1525 s--;
1526 do {
1527 if (*p == '\0' || gmatch(s, p))
1528 return 1;
1529 } while (*s++ != '\0');
1530 return 0;
1531
1532 default:
1533 if (sc != (pc & ~QUOTE))
1534 return 0;
1535 }
1536 }
1537 return *s == '\0';
1538}
1539
1540
1541/* -------- csyn.c -------- */
1542/*
1543 * shell: syntax (C version)
1544 */
1545
1546static void yyerror(const char *s) NORETURN;
1547static void yyerror(const char *s)
1548{
1549 yynerrs = 1;
1550 if (interactive && global_env.iop <= iostack) {
1551 multiline = 0;
1552 while (eofc() == 0 && yylex(0) != '\n')
1553 continue;
1554 }
1555 err(s);
1556 fail();
1557}
1558
1559static void zzerr(void) NORETURN;
1560static void zzerr(void)
1561{
1562 yyerror("syntax error");
1563}
1564
1565int yyparse(void)
1566{
1567 DBGPRINTF7(("YYPARSE: enter...\n"));
1568
1569 startl = 1;
1570 peeksym = 0;
1571 yynerrs = 0;
1572 outtree = c_list();
1573 musthave('\n', 0);
1574 return yynerrs; /* 0/1 */
1575}
1576
1577static struct op *pipeline(int cf)
1578{
1579 struct op *t, *p;
1580 int c;
1581
1582 DBGPRINTF7(("PIPELINE: enter, cf=%d\n", cf));
1583
1584 t = command(cf);
1585
1586 DBGPRINTF9(("PIPELINE: t=%p\n", t));
1587
1588 if (t != NULL) {
1589 while ((c = yylex(0)) == '|') {
1590 p = command(CONTIN);
1591 if (p == NULL) {
1592 DBGPRINTF8(("PIPELINE: error!\n"));
1593 zzerr();
1594 }
1595
1596 if (t->op_type != TPAREN && t->op_type != TCOM) {
1597 /* shell statement */
1598 t = block(TPAREN, t, NOBLOCK, NOWORDS);
1599 }
1600
1601 t = block(TPIPE, t, p, NOWORDS);
1602 }
1603 peeksym = c;
1604 }
1605
1606 DBGPRINTF7(("PIPELINE: returning t=%p\n", t));
1607 return t;
1608}
1609
1610static struct op *andor(void)
1611{
1612 struct op *t, *p;
1613 int c;
1614
1615 DBGPRINTF7(("ANDOR: enter...\n"));
1616
1617 t = pipeline(0);
1618
1619 DBGPRINTF9(("ANDOR: t=%p\n", t));
1620
1621 if (t != NULL) {
1622 while ((c = yylex(0)) == LOGAND || c == LOGOR) {
1623 p = pipeline(CONTIN);
1624 if (p == NULL) {
1625 DBGPRINTF8(("ANDOR: error!\n"));
1626 zzerr();
1627 }
1628
1629 t = block(c == LOGAND ? TAND : TOR, t, p, NOWORDS);
1630 }
1631
1632 peeksym = c;
1633 }
1634
1635 DBGPRINTF7(("ANDOR: returning t=%p\n", t));
1636 return t;
1637}
1638
1639static struct op *c_list(void)
1640{
1641 struct op *t, *p;
1642 int c;
1643
1644 DBGPRINTF7(("C_LIST: enter...\n"));
1645
1646 t = andor();
1647
1648 if (t != NULL) {
1649 peeksym = yylex(0);
1650 if (peeksym == '&')
1651 t = block(TASYNC, t, NOBLOCK, NOWORDS);
1652
1653 while ((c = yylex(0)) == ';' || c == '&'
1654 || (multiline && c == '\n')
1655 ) {
1656 p = andor();
1657 if (p== NULL)
1658 return t;
1659
1660 peeksym = yylex(0);
1661 if (peeksym == '&')
1662 p = block(TASYNC, p, NOBLOCK, NOWORDS);
1663
1664 t = list(t, p);
1665 } /* WHILE */
1666
1667 peeksym = c;
1668 }
1669 /* IF */
1670 DBGPRINTF7(("C_LIST: returning t=%p\n", t));
1671 return t;
1672}
1673
1674static int synio(int cf)
1675{
1676 struct ioword *iop;
1677 int i;
1678 int c;
1679
1680 DBGPRINTF7(("SYNIO: enter, cf=%d\n", cf));
1681
1682 c = yylex(cf);
1683 if (c != '<' && c != '>') {
1684 peeksym = c;
1685 return 0;
1686 }
1687
1688 i = yylval.i;
1689 musthave(WORD, 0);
1690 iop = io(iounit, i, yylval.cp);
1691 iounit = IODEFAULT;
1692
1693 if (i & IOHERE)
1694 markhere(yylval.cp, iop);
1695
1696 DBGPRINTF7(("SYNIO: returning 1\n"));
1697 return 1;
1698}
1699
1700static void musthave(int c, int cf)
1701{
1702 peeksym = yylex(cf);
1703 if (peeksym != c) {
1704 DBGPRINTF7(("MUSTHAVE: error!\n"));
1705 zzerr();
1706 }
1707
1708 peeksym = 0;
1709}
1710
1711static struct op *simple(void)
1712{
1713 struct op *t;
1714
1715 t = NULL;
1716 for (;;) {
1717 switch (peeksym = yylex(0)) {
1718 case '<':
1719 case '>':
1720 (void) synio(0);
1721 break;
1722
1723 case WORD:
1724 if (t == NULL) {
1725 t = newtp();
1726 t->op_type = TCOM;
1727 }
1728 peeksym = 0;
1729 word(yylval.cp);
1730 break;
1731
1732 default:
1733 return t;
1734 }
1735 }
1736}
1737
1738static struct op *nested(int type, int mark)
1739{
1740 struct op *t;
1741
1742 DBGPRINTF3(("NESTED: enter, type=%d, mark=%d\n", type, mark));
1743
1744 multiline++;
1745 t = c_list();
1746 musthave(mark, 0);
1747 multiline--;
1748 return block(type, t, NOBLOCK, NOWORDS);
1749}
1750
1751static struct op *command(int cf)
1752{
1753 struct op *t;
1754 struct wdblock *iosave;
1755 int c;
1756
1757 DBGPRINTF(("COMMAND: enter, cf=%d\n", cf));
1758
1759 iosave = iolist;
1760 iolist = NULL;
1761
1762 if (multiline)
1763 cf |= CONTIN;
1764
1765 while (synio(cf))
1766 cf = 0;
1767
1768 c = yylex(cf);
1769
1770 switch (c) {
1771 default:
1772 peeksym = c;
1773 t = simple();
1774 if (t == NULL) {
1775 if (iolist == NULL)
1776 return NULL;
1777 t = newtp();
1778 t->op_type = TCOM;
1779 }
1780 break;
1781
1782 case '(':
1783 t = nested(TPAREN, ')');
1784 break;
1785
1786 case '{':
1787 t = nested(TBRACE, '}');
1788 break;
1789
1790 case FOR:
1791 t = newtp();
1792 t->op_type = TFOR;
1793 musthave(WORD, 0);
1794 startl = 1;
1795 t->str = yylval.cp;
1796 multiline++;
1797 t->op_words = wordlist();
1798 c = yylex(0);
1799 if (c != '\n' && c != ';')
1800 peeksym = c;
1801 t->left = dogroup(0);
1802 multiline--;
1803 break;
1804
1805 case WHILE:
1806 case UNTIL:
1807 multiline++;
1808 t = newtp();
1809 t->op_type = (c == WHILE ? TWHILE : TUNTIL);
1810 t->left = c_list();
1811 t->right = dogroup(1);
1812 /* t->op_words = NULL; - newtp() did this */
1813 multiline--;
1814 break;
1815
1816 case CASE:
1817 t = newtp();
1818 t->op_type = TCASE;
1819 musthave(WORD, 0);
1820 t->str = yylval.cp;
1821 startl++;
1822 multiline++;
1823 musthave(IN, CONTIN);
1824 startl++;
1825
1826 t->left = caselist();
1827
1828 musthave(ESAC, 0);
1829 multiline--;
1830 break;
1831
1832 case IF:
1833 multiline++;
1834 t = newtp();
1835 t->op_type = TIF;
1836 t->left = c_list();
1837 t->right = thenpart();
1838 musthave(FI, 0);
1839 multiline--;
1840 break;
1841
1842 case DOT:
1843 t = newtp();
1844 t->op_type = TDOT;
1845
1846 musthave(WORD, 0); /* gets name of file */
1847 DBGPRINTF7(("COMMAND: DOT clause, yylval.cp is %s\n", yylval.cp));
1848
1849 word(yylval.cp); /* add word to wdlist */
1850 word(NOWORD); /* terminate wdlist */
1851 t->op_words = copyw(); /* dup wdlist */
1852 break;
1853
1854 }
1855
1856 while (synio(0))
1857 continue;
1858
1859 t = namelist(t);
1860 iolist = iosave;
1861
1862 DBGPRINTF(("COMMAND: returning %p\n", t));
1863
1864 return t;
1865}
1866
1867static struct op *dowholefile(int type /*, int mark*/)
1868{
1869 struct op *t;
1870
1871 DBGPRINTF(("DOWHOLEFILE: enter, type=%d\n", type /*, mark*/));
1872
1873 multiline++;
1874 t = c_list();
1875 multiline--;
1876 t = block(type, t, NOBLOCK, NOWORDS);
1877 DBGPRINTF(("DOWHOLEFILE: return t=%p\n", t));
1878 return t;
1879}
1880
1881static struct op *dogroup(int onlydone)
1882{
1883 int c;
1884 struct op *mylist;
1885
1886 c = yylex(CONTIN);
1887 if (c == DONE && onlydone)
1888 return NULL;
1889 if (c != DO)
1890 zzerr();
1891 mylist = c_list();
1892 musthave(DONE, 0);
1893 return mylist;
1894}
1895
1896static struct op *thenpart(void)
1897{
1898 int c;
1899 struct op *t;
1900
1901 c = yylex(0);
1902 if (c != THEN) {
1903 peeksym = c;
1904 return NULL;
1905 }
1906 t = newtp();
1907 /*t->op_type = 0; - newtp() did this */
1908 t->left = c_list();
1909 if (t->left == NULL)
1910 zzerr();
1911 t->right = elsepart();
1912 return t;
1913}
1914
1915static struct op *elsepart(void)
1916{
1917 int c;
1918 struct op *t;
1919
1920 switch (c = yylex(0)) {
1921 case ELSE:
1922 t = c_list();
1923 if (t == NULL)
1924 zzerr();
1925 return t;
1926
1927 case ELIF:
1928 t = newtp();
1929 t->op_type = TELIF;
1930 t->left = c_list();
1931 t->right = thenpart();
1932 return t;
1933
1934 default:
1935 peeksym = c;
1936 return NULL;
1937 }
1938}
1939
1940static struct op *caselist(void)
1941{
1942 struct op *t;
1943
1944 t = NULL;
1945 while ((peeksym = yylex(CONTIN)) != ESAC) {
1946 DBGPRINTF(("CASELIST, doing yylex, peeksym=%d\n", peeksym));
1947 t = list(t, casepart());
1948 }
1949
1950 DBGPRINTF(("CASELIST, returning t=%p\n", t));
1951 return t;
1952}
1953
1954static struct op *casepart(void)
1955{
1956 struct op *t;
1957
1958 DBGPRINTF7(("CASEPART: enter...\n"));
1959
1960 t = newtp();
1961 t->op_type = TPAT;
1962 t->op_words = pattern();
1963 musthave(')', 0);
1964 t->left = c_list();
1965 peeksym = yylex(CONTIN);
1966 if (peeksym != ESAC)
1967 musthave(BREAK, CONTIN);
1968
1969 DBGPRINTF7(("CASEPART: made newtp(TPAT, t=%p)\n", t));
1970
1971 return t;
1972}
1973
1974static char **pattern(void)
1975{
1976 int c, cf;
1977
1978 cf = CONTIN;
1979 do {
1980 musthave(WORD, cf);
1981 word(yylval.cp);
1982 cf = 0;
1983 c = yylex(0);
1984 } while (c == '|');
1985 peeksym = c;
1986 word(NOWORD);
1987
1988 return copyw();
1989}
1990
1991static char **wordlist(void)
1992{
1993 int c;
1994
1995 c = yylex(0);
1996 if (c != IN) {
1997 peeksym = c;
1998 return NULL;
1999 }
2000 startl = 0;
2001 while ((c = yylex(0)) == WORD)
2002 word(yylval.cp);
2003 word(NOWORD);
2004 peeksym = c;
2005 return copyw();
2006}
2007
2008/*
2009 * supporting functions
2010 */
2011static struct op *list(struct op *t1, struct op *t2)
2012{
2013 DBGPRINTF7(("LIST: enter, t1=%p, t2=%p\n", t1, t2));
2014
2015 if (t1 == NULL)
2016 return t2;
2017 if (t2 == NULL)
2018 return t1;
2019
2020 return block(TLIST, t1, t2, NOWORDS);
2021}
2022
2023static struct op *block(int type, struct op *t1, struct op *t2, char **wp)
2024{
2025 struct op *t;
2026
2027 DBGPRINTF7(("BLOCK: enter, type=%d (%s)\n", type, T_CMD_NAMES[type]));
2028
2029 t = newtp();
2030 t->op_type = type;
2031 t->left = t1;
2032 t->right = t2;
2033 t->op_words = wp;
2034
2035 DBGPRINTF7(("BLOCK: inserted %p between %p and %p\n", t, t1, t2));
2036
2037 return t;
2038}
2039
2040/* See if given string is a shell multiline (FOR, IF, etc) */
2041static int rlookup(char *n)
2042{
2043 struct res {
2044 char r_name[6];
2045 int16_t r_val;
2046 };
2047 static const struct res restab[] = {
2048 { "for" , FOR },
2049 { "case" , CASE },
2050 { "esac" , ESAC },
2051 { "while", WHILE },
2052 { "do" , DO },
2053 { "done" , DONE },
2054 { "if" , IF },
2055 { "in" , IN },
2056 { "then" , THEN },
2057 { "else" , ELSE },
2058 { "elif" , ELIF },
2059 { "until", UNTIL },
2060 { "fi" , FI },
2061 { ";;" , BREAK },
2062 { "||" , LOGOR },
2063 { "&&" , LOGAND },
2064 { "{" , '{' },
2065 { "}" , '}' },
2066 { "." , DOT },
2067 { "" , 0 },
2068 };
2069
2070 const struct res *rp;
2071
2072 DBGPRINTF7(("RLOOKUP: enter, n is %s\n", n));
2073
2074 for (rp = restab; rp->r_name[0]; rp++)
2075 if (strcmp(rp->r_name, n) == 0) {
2076 DBGPRINTF7(("RLOOKUP: match, returning %d\n", rp->r_val));
2077 return rp->r_val; /* Return numeric code for shell multiline */
2078 }
2079
2080 DBGPRINTF7(("RLOOKUP: NO match, returning 0\n"));
2081 return 0; /* Not a shell multiline */
2082}
2083
2084static struct op *newtp(void)
2085{
2086 struct op *t;
2087
2088 t = (struct op *) tree(sizeof(*t));
2089 memset(t, 0, sizeof(*t));
2090
2091 DBGPRINTF3(("NEWTP: allocated %p\n", t));
2092
2093 return t;
2094}
2095
2096static struct op *namelist(struct op *t)
2097{
2098 DBGPRINTF7(("NAMELIST: enter, t=%p, type %s, iolist=%p\n", t,
2099 T_CMD_NAMES[t->op_type], iolist));
2100
2101 if (iolist) {
2102 iolist = addword((char *) NULL, iolist);
2103 t->ioact = copyio();
2104 } else
2105 t->ioact = NULL;
2106
2107 if (t->op_type != TCOM) {
2108 if (t->op_type != TPAREN && t->ioact != NULL) {
2109 t = block(TPAREN, t, NOBLOCK, NOWORDS);
2110 t->ioact = t->left->ioact;
2111 t->left->ioact = NULL;
2112 }
2113 return t;
2114 }
2115
2116 word(NOWORD);
2117 t->op_words = copyw();
2118
2119 return t;
2120}
2121
2122static char **copyw(void)
2123{
2124 char **wd;
2125
2126 wd = getwords(wdlist);
2127 wdlist = NULL;
2128 return wd;
2129}
2130
2131static void word(char *cp)
2132{
2133 wdlist = addword(cp, wdlist);
2134}
2135
2136static struct ioword **copyio(void)
2137{
2138 struct ioword **iop;
2139
2140 iop = (struct ioword **) getwords(iolist);
2141 iolist = NULL;
2142 return iop;
2143}
2144
2145static struct ioword *io(int u, int f, char *cp)
2146{
2147 struct ioword *iop;
2148
2149 iop = (struct ioword *) tree(sizeof(*iop));
2150 iop->io_fd = u;
2151 iop->io_flag = f;
2152 iop->io_name = cp;
2153 iolist = addword((char *) iop, iolist);
2154 return iop;
2155}
2156
2157static int yylex(int cf)
2158{
2159 int c, c1;
2160 int atstart;
2161
2162 c = peeksym;
2163 if (c > 0) {
2164 peeksym = 0;
2165 if (c == '\n')
2166 startl = 1;
2167 return c;
2168 }
2169
2170 nlseen = 0;
2171 atstart = startl;
2172 startl = 0;
2173 yylval.i = 0;
2174 global_env.linep = line;
2175
2176/* MALAMO */
2177 line[LINELIM - 1] = '\0';
2178
2179 loop:
2180 while ((c = my_getc(0)) == ' ' || c == '\t') /* Skip whitespace */
2181 continue;
2182
2183 switch (c) {
2184 default:
2185 if (any(c, "0123456789")) {
2186 c1 = my_getc(0);
2187 unget(c1);
2188 if (c1 == '<' || c1 == '>') {
2189 iounit = c - '0';
2190 goto loop;
2191 }
2192 *global_env.linep++ = c;
2193 c = c1;
2194 }
2195 break;
2196
2197 case '#': /* Comment, skip to next newline or End-of-string */
2198 while ((c = my_getc(0)) != '\0' && c != '\n')
2199 continue;
2200 unget(c);
2201 goto loop;
2202
2203 case 0:
2204 DBGPRINTF5(("YYLEX: return 0, c=%d\n", c));
2205 return c;
2206
2207 case '$':
2208 DBGPRINTF9(("YYLEX: found $\n"));
2209 *global_env.linep++ = c;
2210 c = my_getc(0);
2211 if (c == '{') {
2212 c = collect(c, '}');
2213 if (c != '\0')
2214 return c;
2215 goto pack;
2216 }
2217 break;
2218
2219 case '`':
2220 case '\'':
2221 case '"':
2222 c = collect(c, c);
2223 if (c != '\0')
2224 return c;
2225 goto pack;
2226
2227 case '|':
2228 case '&':
2229 case ';':
2230 startl = 1;
2231 /* If more chars process them, else return NULL char */
2232 c1 = dual(c);
2233 if (c1 != '\0')
2234 return c1;
2235 return c;
2236
2237 case '^':
2238 startl = 1;
2239 return '|';
2240 case '>':
2241 case '<':
2242 diag(c);
2243 return c;
2244
2245 case '\n':
2246 nlseen++;
2247 gethere();
2248 startl = 1;
2249 if (multiline || cf & CONTIN) {
2250 if (interactive && global_env.iop <= iostack) {
2251#if ENABLE_FEATURE_EDITING
2252 current_prompt = cprompt->value;
2253#else
2254 prs(cprompt->value);
2255#endif
2256 }
2257 if (cf & CONTIN)
2258 goto loop;
2259 }
2260 return c;
2261
2262 case '(':
2263 case ')':
2264 startl = 1;
2265 return c;
2266 }
2267
2268 unget(c);
2269
2270 pack:
2271 while ((c = my_getc(0)) != '\0' && !any(c, "`$ '\"\t;&<>()|^\n")) {
2272 if (global_env.linep >= elinep)
2273 err("word too long");
2274 else
2275 *global_env.linep++ = c;
2276 };
2277
2278 unget(c);
2279
2280 if (any(c, "\"'`$"))
2281 goto loop;
2282
2283 *global_env.linep++ = '\0';
2284
2285 if (atstart) {
2286 c = rlookup(line);
2287 if (c != 0) {
2288 startl = 1;
2289 return c;
2290 }
2291 }
2292
2293 yylval.cp = strsave(line, areanum);
2294 return WORD;
2295}
2296
2297
2298static int collect(int c, int c1)
2299{
2300 char s[2];
2301
2302 DBGPRINTF8(("COLLECT: enter, c=%d, c1=%d\n", c, c1));
2303
2304 *global_env.linep++ = c;
2305 while ((c = my_getc(c1)) != c1) {
2306 if (c == 0) {
2307 unget(c);
2308 s[0] = c1;
2309 s[1] = 0;
2310 prs("no closing ");
2311 yyerror(s);
2312 return YYERRCODE;
2313 }
2314 if (interactive && c == '\n' && global_env.iop <= iostack) {
2315#if ENABLE_FEATURE_EDITING
2316 current_prompt = cprompt->value;
2317#else
2318 prs(cprompt->value);
2319#endif
2320 }
2321 *global_env.linep++ = c;
2322 }
2323
2324 *global_env.linep++ = c;
2325
2326 DBGPRINTF8(("COLLECT: return 0, line is %s\n", line));
2327
2328 return 0;
2329}
2330
2331/* "multiline commands" helper func */
2332/* see if next 2 chars form a shell multiline */
2333static int dual(int c)
2334{
2335 char s[3];
2336 char *cp = s;
2337
2338 DBGPRINTF8(("DUAL: enter, c=%d\n", c));
2339
2340 *cp++ = c; /* c is the given "peek" char */
2341 *cp++ = my_getc(0); /* get next char of input */
2342 *cp = '\0'; /* add EOS marker */
2343
2344 c = rlookup(s); /* see if 2 chars form a shell multiline */
2345 if (c == 0)
2346 unget(*--cp); /* String is not a shell multiline, put peek char back */
2347
2348 return c; /* String is multiline, return numeric multiline (restab) code */
2349}
2350
2351static void diag(int ec)
2352{
2353 int c;
2354
2355 DBGPRINTF8(("DIAG: enter, ec=%d\n", ec));
2356
2357 c = my_getc(0);
2358 if (c == '>' || c == '<') {
2359 if (c != ec)
2360 zzerr();
2361 yylval.i = (ec == '>' ? IOWRITE | IOCAT : IOHERE);
2362 c = my_getc(0);
2363 } else
2364 yylval.i = (ec == '>' ? IOWRITE : IOREAD);
2365 if (c != '&' || yylval.i == IOHERE)
2366 unget(c);
2367 else
2368 yylval.i |= IODUP;
2369}
2370
2371static char *tree(unsigned size)
2372{
2373 char *t;
2374
2375 t = getcell(size);
2376 if (t == NULL) {
2377 DBGPRINTF2(("TREE: getcell(%d) failed!\n", size));
2378 prs("command line too complicated\n");
2379 fail();
2380 /* NOTREACHED */
2381 }
2382 return t;
2383}
2384
2385
2386/* VARARGS1 */
2387/* ARGSUSED */
2388
2389/* -------- exec.c -------- */
2390
2391static struct op **find1case(struct op *t, const char *w)
2392{
2393 struct op *t1;
2394 struct op **tp;
2395 char **wp;
2396 char *cp;
2397
2398 if (t == NULL) {
2399 DBGPRINTF3(("FIND1CASE: enter, t==NULL, returning.\n"));
2400 return NULL;
2401 }
2402
2403 DBGPRINTF3(("FIND1CASE: enter, t->op_type=%d (%s)\n", t->op_type,
2404 T_CMD_NAMES[t->op_type]));
2405
2406 if (t->op_type == TLIST) {
2407 tp = find1case(t->left, w);
2408 if (tp != NULL) {
2409 DBGPRINTF3(("FIND1CASE: found one to the left, returning tp=%p\n", tp));
2410 return tp;
2411 }
2412 t1 = t->right; /* TPAT */
2413 } else
2414 t1 = t;
2415
2416 for (wp = t1->op_words; *wp;) {
2417 cp = evalstr(*wp++, DOSUB);
2418 if (cp && gmatch(w, cp)) {
2419 DBGPRINTF3(("FIND1CASE: returning &t1->left= %p.\n",
2420 &t1->left));
2421 return &t1->left;
2422 }
2423 }
2424
2425 DBGPRINTF(("FIND1CASE: returning NULL\n"));
2426 return NULL;
2427}
2428
2429static struct op *findcase(struct op *t, const char *w)
2430{
2431 struct op **tp;
2432
2433 tp = find1case(t, w);
2434 return tp != NULL ? *tp : NULL;
2435}
2436
2437/*
2438 * execute tree
2439 */
2440
2441static int execute(struct op *t, int *pin, int *pout, int no_fork)
2442{
2443 struct op *t1;
2444 volatile int i, rv, a;
2445 const char *cp;
2446 char **wp, **wp2;
2447 struct var *vp;
2448 struct op *outtree_save;
2449 struct brkcon bc;
2450
2451#if __GNUC__
2452 /* Avoid longjmp clobbering */
2453 (void) &wp;
2454#endif
2455
2456 if (t == NULL) {
2457 DBGPRINTF4(("EXECUTE: enter, t==null, returning.\n"));
2458 return 0;
2459 }
2460
2461 DBGPRINTF(("EXECUTE: t=%p, t->op_type=%d (%s), t->op_words is %s\n", t,
2462 t->op_type, T_CMD_NAMES[t->op_type],
2463 ((t->op_words == NULL) ? "NULL" : t->op_words[0])));
2464
2465 rv = 0;
2466 a = areanum++;
2467 wp2 = t->op_words;
2468 wp = (wp2 != NULL)
2469 ? eval(wp2, t->op_type == TCOM ? DOALL : DOALL & ~DOKEY)
2470 : NULL;
2471
2472 switch (t->op_type) {
2473 case TDOT:
2474 DBGPRINTF3(("EXECUTE: TDOT\n"));
2475
2476 outtree_save = outtree;
2477
2478 newfile(evalstr(t->op_words[0], DOALL));
2479
2480 t->left = dowholefile(TLIST /*, 0*/);
2481 t->right = NULL;
2482
2483 outtree = outtree_save;
2484
2485 if (t->left)
2486 rv = execute(t->left, pin, pout, /* no_fork: */ 0);
2487 if (t->right)
2488 rv = execute(t->right, pin, pout, /* no_fork: */ 0);
2489 break;
2490
2491 case TPAREN:
2492 rv = execute(t->left, pin, pout, /* no_fork: */ 0);
2493 break;
2494
2495 case TCOM:
2496 rv = forkexec(t, pin, pout, no_fork, wp);
2497 break;
2498
2499 case TPIPE:
2500 {
2501 int pv[2];
2502
2503 rv = openpipe(pv);
2504 if (rv < 0)
2505 break;
2506 pv[0] = remap(pv[0]);
2507 pv[1] = remap(pv[1]);
2508 (void) execute(t->left, pin, pv, /* no_fork: */ 0);
2509 rv = execute(t->right, pv, pout, /* no_fork: */ 0);
2510 }
2511 break;
2512
2513 case TLIST:
2514 (void) execute(t->left, pin, pout, /* no_fork: */ 0);
2515 rv = execute(t->right, pin, pout, /* no_fork: */ 0);
2516 break;
2517
2518 case TASYNC:
2519 {
2520 smallint hinteractive = interactive;
2521
2522 DBGPRINTF7(("EXECUTE: TASYNC clause, calling vfork()...\n"));
2523
2524 i = vfork();
2525 if (i == 0) { /* child */
2526 signal(SIGINT, SIG_IGN);
2527 signal(SIGQUIT, SIG_IGN);
2528 if (interactive)
2529 signal(SIGTERM, SIG_DFL);
2530 interactive = 0;
2531 if (pin == NULL) {
2532 close(0);
2533 xopen(bb_dev_null, O_RDONLY);
2534 }
2535 _exit(execute(t->left, pin, pout, /* no_fork: */ 1));
2536 }
2537 interactive = hinteractive;
2538 if (i != -1) {
2539 setval(lookup("!"), putn(i));
2540 closepipe(pin);
2541 if (interactive) {
2542 prs(putn(i));
2543 prs("\n");
2544 }
2545 } else
2546 rv = -1;
2547 setstatus(rv);
2548 }
2549 break;
2550
2551 case TOR:
2552 case TAND:
2553 rv = execute(t->left, pin, pout, /* no_fork: */ 0);
2554 t1 = t->right;
2555 if (t1 != NULL && (rv == 0) == (t->op_type == TAND))
2556 rv = execute(t1, pin, pout, /* no_fork: */ 0);
2557 break;
2558
2559 case TFOR:
2560 if (wp == NULL) {
2561 wp = dolv + 1;
2562 i = dolc;
2563 if (i < 0)
2564 i = 0;
2565 } else {
2566 i = -1;
2567 while (*wp++ != NULL)
2568 continue;
2569 }
2570 vp = lookup(t->str);
2571 while (setjmp(bc.brkpt))
2572 if (isbreak)
2573 goto broken;
2574 /* Restore areanum value. It may be incremented by execute()
2575 * below, and then "continue" may jump back to setjmp above */
2576 areanum = a + 1;
2577 freearea(areanum + 1);
2578 brkset(&bc);
2579 for (t1 = t->left; i-- && *wp != NULL;) {
2580 setval(vp, *wp++);
2581 rv = execute(t1, pin, pout, /* no_fork: */ 0);
2582 }
2583 brklist = brklist->nextlev;
2584 break;
2585
2586 case TWHILE:
2587 case TUNTIL:
2588 while (setjmp(bc.brkpt))
2589 if (isbreak)
2590 goto broken;
2591 /* Restore areanum value. It may be incremented by execute()
2592 * below, and then "continue" may jump back to setjmp above */
2593 areanum = a + 1;
2594 freearea(areanum + 1);
2595 brkset(&bc);
2596 t1 = t->left;
2597 while ((execute(t1, pin, pout, /* no_fork: */ 0) == 0) == (t->op_type == TWHILE))
2598 rv = execute(t->right, pin, pout, /* no_fork: */ 0);
2599 brklist = brklist->nextlev;
2600 break;
2601
2602 case TIF:
2603 case TELIF:
2604 if (t->right != NULL) {
2605 rv = !execute(t->left, pin, pout, /* no_fork: */ 0) ?
2606 execute(t->right->left, pin, pout, /* no_fork: */ 0) :
2607 execute(t->right->right, pin, pout, /* no_fork: */ 0);
2608 }
2609 break;
2610
2611 case TCASE:
2612 cp = evalstr(t->str, DOSUB | DOTRIM);
2613 if (cp == NULL)
2614 cp = "";
2615
2616 DBGPRINTF7(("EXECUTE: TCASE, t->str is %s, cp is %s\n",
2617 ((t->str == NULL) ? "NULL" : t->str),
2618 ((cp == NULL) ? "NULL" : cp)));
2619
2620 t1 = findcase(t->left, cp);
2621 if (t1 != NULL) {
2622 DBGPRINTF7(("EXECUTE: TCASE, calling execute(t=%p, t1=%p)...\n", t, t1));
2623 rv = execute(t1, pin, pout, /* no_fork: */ 0);
2624 DBGPRINTF7(("EXECUTE: TCASE, back from execute(t=%p, t1=%p)...\n", t, t1));
2625 }
2626 break;
2627
2628 case TBRACE:
2629/*
2630 iopp = t->ioact;
2631 if (i)
2632 while (*iopp)
2633 if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) {
2634 rv = -1;
2635 break;
2636 }
2637*/
2638 if (rv >= 0) {
2639 t1 = t->left;
2640 if (t1) {
2641 rv = execute(t1, pin, pout, /* no_fork: */ 0);
2642 }
2643 }
2644 break;
2645
2646 };
2647
2648 broken:
2649// Restoring op_words is most likely not needed now: see comment in forkexec()
2650// (also take a look at exec builtin (doexec) - it touches t->op_words)
2651 t->op_words = wp2;
2652 isbreak = 0;
2653 freehere(areanum);
2654 freearea(areanum);
2655 areanum = a;
2656 if (interactive && intr) {
2657 closeall();
2658 fail();
2659 }
2660
2661 i = trapset;
2662 if (i != 0) {
2663 trapset = 0;
2664 runtrap(i);
2665 }
2666
2667 DBGPRINTF(("EXECUTE: returning from t=%p, rv=%d\n", t, rv));
2668 return rv;
2669}
2670
2671static builtin_func_ptr inbuilt(const char *s)
2672{
2673 const struct builtincmd *bp;
2674
2675 for (bp = builtincmds; bp->name; bp++)
2676 if (strcmp(bp->name, s) == 0)
2677 return bp->builtinfunc;
2678 return NULL;
2679}
2680
2681static int forkexec(struct op *t, int *pin, int *pout, int no_fork, char **wp)
2682{
2683 pid_t newpid;
2684 int i;
2685 builtin_func_ptr bltin = NULL;
2686 const char *bltin_name = NULL;
2687 const char *cp;
2688 struct ioword **iopp;
2689 int resetsig;
2690 char **owp;
2691 int forked;
2692
2693 int *hpin = pin;
2694 int *hpout = pout;
2695 char *hwp;
2696 smallint hinteractive;
2697 smallint hintr;
2698 smallint hexecflg;
2699 struct brkcon *hbrklist;
2700
2701#if __GNUC__
2702 /* Avoid longjmp clobbering */
2703 (void) &pin;
2704 (void) &pout;
2705 (void) &wp;
2706 (void) &bltin;
2707 (void) &cp;
2708 (void) &resetsig;
2709 (void) &owp;
2710#endif
2711
2712 DBGPRINTF(("FORKEXEC: t=%p, pin %p, pout %p, no_fork %d\n", t, pin,
2713 pout, no_fork));
2714 DBGPRINTF7(("FORKEXEC: t->op_words is %s\n",
2715 ((t->op_words == NULL) ? "NULL" : t->op_words[0])));
2716 owp = wp;
2717 resetsig = 0;
2718 if (t->op_type == TCOM) {
2719 while (*wp++ != NULL)
2720 continue;
2721 cp = *wp;
2722
2723 /* strip all initial assignments */
2724 /* FIXME: not correct wrt PATH=yyy command etc */
2725 if (FLAG['x']) {
2726 DBGPRINTF9(("FORKEXEC: echo'ing, cp=%p, wp=%p, owp=%p\n",
2727 cp, wp, owp));
2728 echo(cp ? wp : owp);
2729 }
2730
2731 if (cp == NULL) {
2732 if (t->ioact == NULL) {
2733 while ((cp = *owp++) != NULL && assign(cp, COPYV))
2734 continue;
2735 DBGPRINTF(("FORKEXEC: returning setstatus(0)\n"));
2736 return setstatus(0);
2737 }
2738 } else { /* cp != NULL */
2739 bltin_name = cp;
2740 bltin = inbuilt(cp);
2741 }
2742 }
2743
2744 forked = 0;
2745 // We were pointing t->op_words to temporary (expanded) arg list:
2746 // t->op_words = wp;
2747 // and restored it later (in execute()), but "break"
2748 // longjmps away (at "Run builtin" below), leaving t->op_words clobbered!
2749 // See http://bugs.busybox.net/view.php?id=846.
2750 // Now we do not touch t->op_words, but separately pass wp as param list
2751 // to builtins
2752 DBGPRINTF(("FORKEXEC: bltin %p, no_fork %d, owp %p\n", bltin,
2753 no_fork, owp));
2754 /* Don't fork if it is a lone builtin (not in pipe)
2755 * OR we are told to _not_ fork */
2756 if ((!bltin || pin || pout) /* not lone bltin AND */
2757 && !no_fork /* not told to avoid fork */
2758 ) {
2759 /* Save values in case child alters them after vfork */
2760 hpin = pin;
2761 hpout = pout;
2762 hwp = *wp;
2763 hinteractive = interactive;
2764 hintr = intr;
2765 hbrklist = brklist;
2766 hexecflg = execflg;
2767
2768 DBGPRINTF3(("FORKEXEC: calling vfork()...\n"));
2769 newpid = vfork();
2770 if (newpid == -1) {
2771 DBGPRINTF(("FORKEXEC: ERROR, can't vfork()!\n"));
2772 return -1;
2773 }
2774
2775 if (newpid > 0) { /* Parent */
2776 /* Restore values */
2777 pin = hpin;
2778 pout = hpout;
2779 *wp = hwp;
2780 interactive = hinteractive;
2781 intr = hintr;
2782 brklist = hbrklist;
2783 execflg = hexecflg;
2784
2785 closepipe(pin);
2786 return (pout == NULL ? setstatus(waitfor(newpid, 0)) : 0);
2787 }
2788
2789 /* Child */
2790 DBGPRINTF(("FORKEXEC: child process, bltin=%p (%s)\n", bltin, bltin_name));
2791 if (interactive) {
2792 signal(SIGINT, SIG_IGN);
2793 signal(SIGQUIT, SIG_IGN);
2794 resetsig = 1;
2795 }
2796 interactive = 0;
2797 intr = 0;
2798 forked = 1;
2799 brklist = 0;
2800 execflg = 0;
2801 }
2802
2803 if (owp)
2804 while ((cp = *owp++) != NULL && assign(cp, COPYV))
2805 if (!bltin)
2806 export(lookup(cp));
2807
2808 if (pin) { /* NB: close _first_, then move fds! */
2809 close(pin[1]);
2810 xmove_fd(pin[0], 0);
2811 }
2812 if (pout) {
2813 close(pout[0]);
2814 xmove_fd(pout[1], 1);
2815 }
2816
2817 iopp = t->ioact;
2818 if (iopp) {
2819 if (bltin && bltin != doexec) {
2820 prs(bltin_name);
2821 err(": can't redirect shell command");
2822 if (forked)
2823 _exit(-1);
2824 return -1;
2825 }
2826 while (*iopp) {
2827 if (iosetup(*iopp++, pin != NULL, pout != NULL)) {
2828 /* system-detected error */
2829 if (forked)
2830 _exit(-1);
2831 return -1;
2832 }
2833 }
2834 }
2835
2836 if (bltin) {
2837 if (forked || pin || pout) {
2838 /* Builtin in pipe: disallowed */
2839 /* TODO: allow "exec"? */
2840 prs(bltin_name);
2841 err(": can't run builtin as part of pipe");
2842 if (forked)
2843 _exit(-1);
2844 return -1;
2845 }
2846 /* Run builtin */
2847 i = setstatus(bltin(t, wp));
2848 if (forked)
2849 _exit(i);
2850 DBGPRINTF(("FORKEXEC: returning i=%d\n", i));
2851 return i;
2852 }
2853
2854 /* should use FIOCEXCL */
2855 for (i = FDBASE; i < NOFILE; i++)
2856 close(i);
2857 if (resetsig) {
2858 signal(SIGINT, SIG_DFL);
2859 signal(SIGQUIT, SIG_DFL);
2860 }
2861
2862 if (t->op_type == TPAREN)
2863 _exit(execute(t->left, NOPIPE, NOPIPE, /* no_fork: */ 1));
2864 if (wp[0] == NULL)
2865 _exit(EXIT_SUCCESS);
2866
2867 cp = rexecve(wp[0], wp, makenv(0, NULL));
2868 prs(wp[0]);
2869 prs(": ");
2870 err(cp);
2871 if (!execflg)
2872 trap[0] = NULL;
2873
2874 DBGPRINTF(("FORKEXEC: calling leave(), pid=%d\n", getpid()));
2875
2876 leave();
2877 /* NOTREACHED */
2878 return 0;
2879}
2880
2881/*
2882 * 0< 1> are ignored as required
2883 * within pipelines.
2884 */
2885static int iosetup(struct ioword *iop, int pipein, int pipeout)
2886{
2887 int u = -1;
2888 char *cp = NULL;
2889 const char *msg;
2890
2891 DBGPRINTF(("IOSETUP: iop %p, pipein %i, pipeout %i\n", iop,
2892 pipein, pipeout));
2893
2894 if (iop->io_fd == IODEFAULT) /* take default */
2895 iop->io_fd = iop->io_flag & (IOREAD | IOHERE) ? 0 : 1;
2896
2897 if (pipein && iop->io_fd == 0)
2898 return 0;
2899
2900 if (pipeout && iop->io_fd == 1)
2901 return 0;
2902
2903 msg = iop->io_flag & (IOREAD | IOHERE) ? "open" : "create";
2904 if ((iop->io_flag & IOHERE) == 0) {
2905 cp = iop->io_name; /* huh?? */
2906 cp = evalstr(cp, DOSUB | DOTRIM);
2907 if (cp == NULL)
2908 return 1;
2909 }
2910
2911 if (iop->io_flag & IODUP) {
2912 if (cp[1] || (!isdigit(*cp) && *cp != '-')) {
2913 prs(cp);
2914 err(": illegal >& argument");
2915 return 1;
2916 }
2917 if (*cp == '-')
2918 iop->io_flag = IOCLOSE;
2919 iop->io_flag &= ~(IOREAD | IOWRITE);
2920 }
2921
2922 switch (iop->io_flag) {
2923 case IOREAD:
2924 u = open(cp, O_RDONLY);
2925 break;
2926
2927 case IOHERE:
2928 case IOHERE | IOXHERE:
2929 u = herein(iop->io_name, iop->io_flag & IOXHERE);
2930 cp = (char*)"here file";
2931 break;
2932
2933 case IOWRITE | IOCAT:
2934 u = open(cp, O_WRONLY);
2935 if (u >= 0) {
2936 lseek(u, (long) 0, SEEK_END);
2937 break;
2938 }
2939 /* fall through to creation if >>file doesn't exist */
2940
2941 case IOWRITE:
2942 u = creat(cp, 0666);
2943 break;
2944
2945 case IODUP:
2946 u = dup2(*cp - '0', iop->io_fd);
2947 break;
2948
2949 case IOCLOSE:
2950 close(iop->io_fd);
2951 return 0;
2952 }
2953
2954 if (u < 0) {
2955 prs(cp);
2956 prs(": can't ");
2957 warn(msg);
2958 return 1;
2959 }
2960 xmove_fd(u, iop->io_fd);
2961 return 0;
2962}
2963
2964/*
2965 * Enter a new loop level (marked for break/continue).
2966 */
2967static void brkset(struct brkcon *bc)
2968{
2969 bc->nextlev = brklist;
2970 brklist = bc;
2971}
2972
2973/*
2974 * Wait for the last process created.
2975 * Print a message for each process found
2976 * that was killed by a signal.
2977 * Ignore interrupt signals while waiting
2978 * unless `canintr' is true.
2979 */
2980static int waitfor(int lastpid, int canintr)
2981{
2982 int pid, rv;
2983 int s;
2984 smallint oheedint = heedint;
2985
2986 heedint = 0;
2987 rv = 0;
2988 do {
2989 pid = wait(&s);
2990 if (pid == -1) {
2991 if (errno != EINTR || canintr)
2992 break;
2993 } else {
2994 rv = WAITSIG(s);
2995 if (rv != 0) {
2996 if (rv < ARRAY_SIZE(signame)) {
2997 if (signame[rv] != NULL) {
2998 if (pid != lastpid) {
2999 prn(pid);
3000 prs(": ");
3001 }
3002 prs(signame[rv]);
3003 }
3004 } else {
3005 if (pid != lastpid) {
3006 prn(pid);
3007 prs(": ");
3008 }
3009 prs("Signal ");
3010 prn(rv);
3011 prs(" ");
3012 }
3013 if (WAITCORE(s))
3014 prs(" - core dumped");
3015 if (rv >= ARRAY_SIZE(signame) || signame[rv])
3016 prs("\n");
3017 rv |= 0x80;
3018 } else
3019 rv = WAITVAL(s);
3020 }
3021 } while (pid != lastpid);
3022 heedint = oheedint;
3023 if (intr) {
3024 if (interactive) {
3025 if (canintr)
3026 intr = 0;
3027 } else {
3028 if (exstat == 0)
3029 exstat = rv;
3030 onintr(0);
3031 }
3032 }
3033 return rv;
3034}
3035
3036static int setstatus(int s)
3037{
3038 exstat = s;
3039 setval(lookup("?"), putn(s));
3040 return s;
3041}
3042
3043/*
3044 * PATH-searching interface to execve.
3045 * If getenv("PATH") were kept up-to-date,
3046 * execvp might be used.
3047 */
3048static const char *rexecve(char *c, char **v, char **envp)
3049{
3050 const char *sp;
3051 char *tp;
3052 int asis = 0;
3053 char *name = c;
3054
3055 if (ENABLE_FEATURE_SH_STANDALONE) {
3056 if (find_applet_by_name(name) >= 0) {
3057 /* We have to exec here since we vforked. Running
3058 * run_applet_and_exit() won't work and bad things
3059 * will happen. */
3060 execve(bb_busybox_exec_path, v, envp);
3061 }
3062 }
3063
3064 DBGPRINTF(("REXECVE: c=%p, v=%p, envp=%p\n", c, v, envp));
3065
3066 sp = any('/', c) ? "" : path->value;
3067 asis = (*sp == '\0');
3068 while (asis || *sp != '\0') {
3069 asis = 0;
3070 tp = global_env.linep;
3071 for (; *sp != '\0'; tp++) {
3072 *tp = *sp++;
3073 if (*tp == ':') {
3074 asis = (*sp == '\0');
3075 break;
3076 }
3077 }
3078 if (tp != global_env.linep)
3079 *tp++ = '/';
3080 strcpy(tp, c);
3081
3082 DBGPRINTF3(("REXECVE: global_env.linep is %s\n", global_env.linep));
3083
3084 execve(global_env.linep, v, envp);
3085
3086 switch (errno) {
3087 case ENOEXEC:
3088 /* File is executable but file format isnt recognized */
3089 /* Run it as a shell script */
3090 /* (execve above didnt do it itself, unlike execvp) */
3091 *v = global_env.linep;
3092 v--;
3093 tp = *v;
3094 *v = (char*)DEFAULT_SHELL;
3095 execve(DEFAULT_SHELL, v, envp);
3096 *v = tp;
3097 return "no shell";
3098
3099 case ENOMEM:
3100 return (char *) bb_msg_memory_exhausted;
3101
3102 case E2BIG:
3103 return "argument list too long";
3104 }
3105 }
3106 if (errno == ENOENT) {
3107 exstat = 127; /* standards require this */
3108 return "not found";
3109 }
3110 exstat = 126; /* mimic bash */
3111 return "can't execute";
3112}
3113
3114/*
3115 * Run the command produced by generator `f'
3116 * applied to stream `arg'.
3117 */
3118static int run(struct ioarg *argp, int (*f) (struct ioarg *))
3119{
3120 struct op *otree;
3121 struct wdblock *swdlist;
3122 struct wdblock *siolist;
3123 jmp_buf ev, rt;
3124 xint *ofail;
3125 int rv;
3126
3127#if __GNUC__
3128 /* Avoid longjmp clobbering */
3129 (void) &rv;
3130#endif
3131
3132 DBGPRINTF(("RUN: enter, areanum %d, outtree %p, failpt %p\n",
3133 areanum, outtree, failpt));
3134
3135 areanum++;
3136 swdlist = wdlist;
3137 siolist = iolist;
3138 otree = outtree;
3139 ofail = failpt;
3140 rv = -1;
3141
3142 errpt = ev;
3143 if (newenv(setjmp(errpt)) == 0) {
3144 wdlist = NULL;
3145 iolist = NULL;
3146 pushio(argp, f);
3147 global_env.iobase = global_env.iop;
3148 yynerrs = 0;
3149 failpt = rt;
3150 if (setjmp(failpt) == 0 && yyparse() == 0)
3151 rv = execute(outtree, NOPIPE, NOPIPE, /* no_fork: */ 0);
3152 quitenv();
3153 } else {
3154 DBGPRINTF(("RUN: error from newenv()!\n"));
3155 }
3156
3157 wdlist = swdlist;
3158 iolist = siolist;
3159 failpt = ofail;
3160 outtree = otree;
3161 freearea(areanum--);
3162
3163 return rv;
3164}
3165
3166/* -------- do.c -------- */
3167
3168/*
3169 * built-in commands: doX
3170 */
3171
3172static int dohelp(struct op *t UNUSED_PARAM, char **args UNUSED_PARAM)
3173{
3174 int col;
3175 const struct builtincmd *x;
3176
3177 printf(
3178 "Built-in commands:\n"
3179 "------------------\n");
3180
3181 col = 0;
3182 x = builtincmds;
3183 while (x->name) {
3184 col += printf("%c%s", ((col == 0) ? '\t' : ' '), x->name);
3185 if (col > 60) {
3186 bb_putchar('\n');
3187 col = 0;
3188 }
3189 x++;
3190 }
3191#if ENABLE_FEATURE_SH_STANDALONE
3192 {
3193 const char *applet = applet_names;
3194
3195 while (*applet) {
3196 col += printf("%c%s", ((col == 0) ? '\t' : ' '), applet);
3197 if (col > 60) {
3198 bb_putchar('\n');
3199 col = 0;
3200 }
3201 applet += strlen(applet) + 1;
3202 }
3203 }
3204#endif
3205 puts("\n");
3206 return EXIT_SUCCESS;
3207}
3208
3209static int dolabel(struct op *t UNUSED_PARAM, char **args UNUSED_PARAM)
3210{
3211 return 0;
3212}
3213
3214static int dochdir(struct op *t UNUSED_PARAM, char **args)
3215{
3216 const char *cp, *er;
3217
3218 cp = args[1];
3219 if (cp == NULL) {
3220 cp = homedir->value;
3221 if (cp != NULL)
3222 goto do_cd;
3223 er = ": no home directory";
3224 } else {
3225 do_cd:
3226 if (chdir(cp) >= 0)
3227 return 0;
3228 er = ": bad directory";
3229 }
3230 prs(cp != NULL ? cp : "cd");
3231 err(er);
3232 return 1;
3233}
3234
3235static int doshift(struct op *t UNUSED_PARAM, char **args)
3236{
3237 int n;
3238
3239 n = args[1] ? getn(args[1]) : 1;
3240 if (dolc < n) {
3241 err("nothing to shift");
3242 return 1;
3243 }
3244 dolv[n] = dolv[0];
3245 dolv += n;
3246 dolc -= n;
3247 setval(lookup("#"), putn(dolc));
3248 return 0;
3249}
3250
3251/*
3252 * execute login and newgrp directly
3253 */
3254static int dologin(struct op *t UNUSED_PARAM, char **args)
3255{
3256 const char *cp;
3257
3258 if (interactive) {
3259 signal(SIGINT, SIG_DFL);
3260 signal(SIGQUIT, SIG_DFL);
3261 }
3262 cp = rexecve(args[0], args, makenv(0, NULL));
3263 prs(args[0]);
3264 prs(": ");
3265 err(cp);
3266 return 1;
3267}
3268
3269static int doumask(struct op *t UNUSED_PARAM, char **args)
3270{
3271 int i;
3272 char *cp;
3273
3274 cp = args[1];
3275 if (cp == NULL) {
3276 i = umask(0);
3277 umask(i);
3278 printf("%04o\n", i);
3279 } else {
3280 i = bb_strtou(cp, NULL, 8);
3281 if (errno) {
3282 err("umask: bad octal number");
3283 return 1;
3284 }
3285 umask(i);
3286 }
3287 return 0;
3288}
3289
3290static int doexec(struct op *t, char **args)
3291{
3292 jmp_buf ex;
3293 xint *ofail;
3294 char **sv_words;
3295
3296 t->ioact = NULL;
3297 if (!args[1])
3298 return 1;
3299
3300 execflg = 1;
3301 ofail = failpt;
3302 failpt = ex;
3303
3304 sv_words = t->op_words;
3305 t->op_words = args + 1;
3306// TODO: test what will happen with "exec break" -
3307// will it leave t->op_words pointing to garbage?
3308// (see http://bugs.busybox.net/view.php?id=846)
3309 if (setjmp(failpt) == 0)
3310 execute(t, NOPIPE, NOPIPE, /* no_fork: */ 1);
3311 t->op_words = sv_words;
3312
3313 failpt = ofail;
3314 execflg = 0;
3315
3316 return 1;
3317}
3318
3319static int dodot(struct op *t UNUSED_PARAM, char **args)
3320{
3321 int i;
3322 const char *sp;
3323 char *tp;
3324 char *cp;
3325 int maltmp;
3326
3327 DBGPRINTF(("DODOT: enter, t=%p, tleft %p, tright %p, global_env.linep is %s\n",
3328 t, t->left, t->right, ((global_env.linep == NULL) ? "NULL" : global_env.linep)));
3329
3330 cp = args[1];
3331 if (cp == NULL) {
3332 DBGPRINTF(("DODOT: bad args, ret 0\n"));
3333 return 0;
3334 }
3335 DBGPRINTF(("DODOT: cp is %s\n", cp));
3336
3337 sp = any('/', cp) ? ":" : path->value;
3338
3339 DBGPRINTF(("DODOT: sp is %s, global_env.linep is %s\n",
3340 ((sp == NULL) ? "NULL" : sp),
3341 ((global_env.linep == NULL) ? "NULL" : global_env.linep)));
3342
3343 while (*sp) {
3344 tp = global_env.linep;
3345 while (*sp && (*tp = *sp++) != ':')
3346 tp++;
3347 if (tp != global_env.linep)
3348 *tp++ = '/';
3349 strcpy(tp, cp);
3350
3351 /* Original code */
3352 i = open(global_env.linep, O_RDONLY);
3353 if (i >= 0) {
3354 exstat = 0;
3355 maltmp = remap(i);
3356 DBGPRINTF(("DODOT: remap=%d, exstat=%d, global_env.iofd %d, i %d, global_env.linep is %s\n",
3357 maltmp, exstat, global_env.iofd, i, global_env.linep));
3358
3359 next(maltmp); /* Basically a PUSHIO */
3360
3361 DBGPRINTF(("DODOT: returning exstat=%d\n", exstat));
3362
3363 return exstat;
3364 }
3365 } /* while */
3366
3367 prs(cp);
3368 err(": not found");
3369
3370 return -1;
3371}
3372
3373static int dowait(struct op *t UNUSED_PARAM, char **args)
3374{
3375 int i;
3376 char *cp;
3377
3378 cp = args[1];
3379 if (cp != NULL) {
3380 i = getn(cp);
3381 if (i == 0)
3382 return 0;
3383 } else
3384 i = -1;
3385 setstatus(waitfor(i, 1));
3386 return 0;
3387}
3388
3389static int doread(struct op *t UNUSED_PARAM, char **args)
3390{
3391 char *cp, **wp;
3392 int nb = 0;
3393 int nl = 0;
3394
3395 if (args[1] == NULL) {
3396 err("Usage: read name ...");
3397 return 1;
3398 }
3399 for (wp = args + 1; *wp; wp++) {
3400 for (cp = global_env.linep; !nl && cp < elinep - 1; cp++) {
3401 nb = nonblock_safe_read(STDIN_FILENO, cp, sizeof(*cp));
3402 if (nb != sizeof(*cp))
3403 break;
3404 nl = (*cp == '\n');
3405 if (nl || (wp[1] && any(*cp, ifs->value)))
3406 break;
3407 }
3408 *cp = '\0';
3409 if (nb <= 0)
3410 break;
3411 setval(lookup(*wp), global_env.linep);
3412 }
3413 return nb <= 0;
3414}
3415
3416static int doeval(struct op *t UNUSED_PARAM, char **args)
3417{
3418 return RUN(awordlist, args + 1, wdchar);
3419}
3420
3421static int dotrap(struct op *t UNUSED_PARAM, char **args)
3422{
3423 int n, i;
3424 int resetsig;
3425
3426 if (args[1] == NULL) {
3427 for (i = 0; i <= _NSIG; i++)
3428 if (trap[i]) {
3429 prn(i);
3430 prs(": ");
3431 prs(trap[i]);
3432 prs("\n");
3433 }
3434 return 0;
3435 }
3436 resetsig = isdigit(args[1][0]);
3437 for (i = resetsig ? 1 : 2; args[i] != NULL; ++i) {
3438 n = getsig(args[i]);
3439 freecell(trap[n]);
3440 trap[n] = 0;
3441 if (!resetsig) {
3442 if (args[1][0] != '\0') {
3443 trap[n] = strsave(args[1], 0);
3444 setsig(n, sig);
3445 } else
3446 setsig(n, SIG_IGN);
3447 } else {
3448 if (interactive) {
3449 if (n == SIGINT)
3450 setsig(n, onintr);
3451 else
3452 setsig(n, n == SIGQUIT ? SIG_IGN : SIG_DFL);
3453 } else
3454 setsig(n, SIG_DFL);
3455 }
3456 }
3457 return 0;
3458}
3459
3460static int getsig(char *s)
3461{
3462 int n;
3463
3464 n = getn(s);
3465 if (n < 0 || n > _NSIG) {
3466 err("trap: bad signal number");
3467 n = 0;
3468 }
3469 return n;
3470}
3471
3472static void setsig(int n, sighandler_t f)
3473{
3474 if (n == 0)
3475 return;
3476 if (signal(n, SIG_IGN) != SIG_IGN || ourtrap[n]) {
3477 ourtrap[n] = 1;
3478 signal(n, f);
3479 }
3480}
3481
3482static int getn(char *as)
3483{
3484 char *s;
3485 int n, m;
3486
3487 s = as;
3488 m = 1;
3489 if (*s == '-') {
3490 m = -1;
3491 s++;
3492 }
3493 for (n = 0; isdigit(*s); s++)
3494 n = (n * 10) + (*s - '0');
3495 if (*s) {
3496 prs(as);
3497 err(": bad number");
3498 }
3499 return n * m;
3500}
3501
3502static int dobreak(struct op *t UNUSED_PARAM, char **args)
3503{
3504 return brkcontin(args[1], 1);
3505}
3506
3507static int docontinue(struct op *t UNUSED_PARAM, char **args)
3508{
3509 return brkcontin(args[1], 0);
3510}
3511
3512static int brkcontin(char *cp, int val)
3513{
3514 struct brkcon *bc;
3515 int nl;
3516
3517 nl = cp == NULL ? 1 : getn(cp);
3518 if (nl <= 0)
3519 nl = 999;
3520 do {
3521 bc = brklist;
3522 if (bc == NULL)
3523 break;
3524 brklist = bc->nextlev;
3525 } while (--nl);
3526 if (nl) {
3527 err("bad break/continue level");
3528 return 1;
3529 }
3530 isbreak = (val != 0);
3531 longjmp(bc->brkpt, 1);
3532 /* NOTREACHED */
3533}
3534
3535static int doexit(struct op *t UNUSED_PARAM, char **args)
3536{
3537 char *cp;
3538
3539 execflg = 0;
3540 cp = args[1];
3541 if (cp != NULL)
3542 setstatus(getn(cp));
3543
3544 DBGPRINTF(("DOEXIT: calling leave(), t=%p\n", t));
3545
3546 leave();
3547 /* NOTREACHED */
3548 return 0;
3549}
3550
3551static int doexport(struct op *t UNUSED_PARAM, char **args)
3552{
3553 rdexp(args + 1, export, EXPORT);
3554 return 0;
3555}
3556
3557static int doreadonly(struct op *t UNUSED_PARAM, char **args)
3558{
3559 rdexp(args + 1, ronly, RONLY);
3560 return 0;
3561}
3562
3563static void rdexp(char **wp, void (*f) (struct var *), int key)
3564{
3565 DBGPRINTF6(("RDEXP: enter, wp=%p, func=%p, key=%d\n", wp, f, key));
3566 DBGPRINTF6(("RDEXP: *wp=%s\n", *wp));
3567
3568 if (*wp != NULL) {
3569 for (; *wp != NULL; wp++) {
3570 if (isassign(*wp)) {
3571 char *cp;
3572
3573 assign(*wp, COPYV);
3574 for (cp = *wp; *cp != '='; cp++)
3575 continue;
3576 *cp = '\0';
3577 }
3578 if (checkname(*wp))
3579 (*f) (lookup(*wp));
3580 else
3581 badid(*wp);
3582 }
3583 } else
3584 putvlist(key, 1);
3585}
3586
3587static void badid(char *s)
3588{
3589 prs(s);
3590 err(": bad identifier");
3591}
3592
3593static int doset(struct op *t UNUSED_PARAM, char **args)
3594{
3595 struct var *vp;
3596 char *cp;
3597 int n;
3598
3599 cp = args[1];
3600 if (cp == NULL) {
3601 for (vp = vlist; vp; vp = vp->next)
3602 varput(vp->name, STDOUT_FILENO);
3603 return 0;
3604 }
3605 if (*cp == '-') {
3606 args++;
3607 if (*++cp == 0)
3608 FLAG['x'] = FLAG['v'] = 0;
3609 else {
3610 for (; *cp; cp++) {
3611 switch (*cp) {
3612 case 'e':
3613 if (!interactive)
3614 FLAG['e']++;
3615 break;
3616
3617 default:
3618 if (*cp >= 'a' && *cp <= 'z')
3619 FLAG[(int) *cp]++;
3620 break;
3621 }
3622 }
3623 }
3624 setdash();
3625 }
3626 if (args[1]) {
3627 args[0] = dolv[0];
3628 for (n = 1; args[n]; n++)
3629 setarea((char *) args[n], 0);
3630 dolc = n - 1;
3631 dolv = args;
3632 setval(lookup("#"), putn(dolc));
3633 setarea((char *) (dolv - 1), 0);
3634 }
3635 return 0;
3636}
3637
3638static void varput(char *s, int out)
3639{
3640 if (isalnum(*s) || *s == '_') {
3641 xwrite_str(out, s);
3642 xwrite(out, "\n", 1);
3643 }
3644}
3645
3646
3647/*
3648 * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
3649 * This file contains code for the times builtin.
3650 */
3651static void times_fmt(char *buf, clock_t val, unsigned clk_tck)
3652{
3653 unsigned min, sec;
3654 if (sizeof(val) > sizeof(int))
3655 sec = ((unsigned long)val) / clk_tck;
3656 else
3657 sec = ((unsigned)val) / clk_tck;
3658 min = sec / 60;
3659#if ENABLE_DESKTOP
3660 sprintf(buf, "%um%u.%03us", min, (sec - min * 60),
3661 /* msec: */ ((unsigned)(val - (clock_t)sec * clk_tck)) * 1000 / clk_tck
3662 );
3663#else
3664 sprintf(buf, "%um%us", min, (sec - min * 60));
3665#endif
3666}
3667
3668static int dotimes(struct op *t UNUSED_PARAM, char **args UNUSED_PARAM)
3669{
3670 struct tms buf;
3671 unsigned clk_tck = sysconf(_SC_CLK_TCK);
3672 /* How much do we need for "NmN.NNNs" ? */
3673 enum { TIMEBUF_SIZE = sizeof(int)*3 + sizeof(int)*3 + 6 };
3674 char u[TIMEBUF_SIZE], s[TIMEBUF_SIZE];
3675 char cu[TIMEBUF_SIZE], cs[TIMEBUF_SIZE];
3676
3677 times(&buf);
3678
3679 times_fmt(u, buf.tms_utime, clk_tck);
3680 times_fmt(s, buf.tms_stime, clk_tck);
3681 times_fmt(cu, buf.tms_cutime, clk_tck);
3682 times_fmt(cs, buf.tms_cstime, clk_tck);
3683
3684 printf("%s %s\n%s %s\n", u, s, cu, cs);
3685 return 0;
3686}
3687
3688
3689/* -------- eval.c -------- */
3690
3691/*
3692 * ${}
3693 * `command`
3694 * blank interpretation
3695 * quoting
3696 * glob
3697 */
3698
3699static char **eval(char **ap, int f)
3700{
3701 struct wdblock *wb;
3702 char **wp;
3703 char **wf;
3704 jmp_buf ev;
3705
3706#if __GNUC__
3707 /* Avoid longjmp clobbering */
3708 (void) &wp;
3709 (void) &ap;
3710#endif
3711
3712 DBGPRINTF4(("EVAL: enter, f=%d\n", f));
3713
3714 wp = NULL;
3715 wb = NULL;
3716 wf = NULL;
3717 errpt = ev;
3718 if (newenv(setjmp(errpt)) == 0) {
3719 while (*ap && isassign(*ap))
3720 expand(*ap++, &wb, f & ~DOGLOB);
3721 if (FLAG['k']) {
3722 for (wf = ap; *wf; wf++) {
3723 if (isassign(*wf))
3724 expand(*wf, &wb, f & ~DOGLOB);
3725 }
3726 }
3727 for (wb = addword((char *) NULL, wb); *ap; ap++) {
3728 if (!FLAG['k'] || !isassign(*ap))
3729 expand(*ap, &wb, f & ~DOKEY);
3730 }
3731 wb = addword((char *) 0, wb);
3732 wp = getwords(wb);
3733 quitenv();
3734 } else
3735 gflg = 1;
3736
3737 return gflg ? (char **) NULL : wp;
3738}
3739
3740
3741/*
3742 * Make the exported environment from the exported
3743 * names in the dictionary. Keyword assignments
3744 * will already have been done.
3745 */
3746static char **makenv(int all, struct wdblock *wb)
3747{
3748 struct var *vp;
3749
3750 DBGPRINTF5(("MAKENV: enter, all=%d\n", all));
3751
3752 for (vp = vlist; vp; vp = vp->next)
3753 if (all || vp->status & EXPORT)
3754 wb = addword(vp->name, wb);
3755 wb = addword((char *) 0, wb);
3756 return getwords(wb);
3757}
3758
3759static int expand(const char *cp, struct wdblock **wbp, int f)
3760{
3761 jmp_buf ev;
3762 char *xp;
3763
3764#if __GNUC__
3765 /* Avoid longjmp clobbering */
3766 (void) &cp;
3767#endif
3768
3769 DBGPRINTF3(("EXPAND: enter, f=%d\n", f));
3770
3771 gflg = 0;
3772
3773 if (cp == NULL)
3774 return 0;
3775
3776 if (!anys("$`'\"", cp) && !anys(ifs->value, cp)
3777 && ((f & DOGLOB) == 0 || !anys("[*?", cp))
3778 ) {
3779 xp = strsave(cp, areanum);
3780 if (f & DOTRIM)
3781 unquote(xp);
3782 *wbp = addword(xp, *wbp);
3783 return 1;
3784 }
3785 errpt = ev;
3786 if (newenv(setjmp(errpt)) == 0) {
3787 PUSHIO(aword, cp, strchar);
3788 global_env.iobase = global_env.iop;
3789 while ((xp = blank(f)) && gflg == 0) {
3790 global_env.linep = xp;
3791 xp = strsave(xp, areanum);
3792 if ((f & DOGLOB) == 0) {
3793 if (f & DOTRIM)
3794 unquote(xp);
3795 *wbp = addword(xp, *wbp);
3796 } else
3797 *wbp = glob(xp, *wbp);
3798 }
3799 quitenv();
3800 } else
3801 gflg = 1;
3802 return gflg == 0;
3803}
3804
3805static char *evalstr(char *cp, int f)
3806{
3807 struct wdblock *wb;
3808
3809 DBGPRINTF6(("EVALSTR: enter, cp=%p, f=%d\n", cp, f));
3810
3811 wb = NULL;
3812 if (expand(cp, &wb, f)) {
3813 if (wb == NULL || wb->w_nword == 0
3814 || (cp = wb->w_words[0]) == NULL
3815 ) {
3816// TODO: I suspect that
3817// char *evalstr(char *cp, int f) is actually
3818// const char *evalstr(const char *cp, int f)!
3819 cp = (char*)"";
3820 }
3821 DELETE(wb);
3822 } else
3823 cp = NULL;
3824 return cp;
3825}
3826
3827
3828/*
3829 * Blank interpretation and quoting
3830 */
3831static char *blank(int f)
3832{
3833 int c, c1;
3834 char *sp;
3835 int scanequals, foundequals;
3836
3837 DBGPRINTF3(("BLANK: enter, f=%d\n", f));
3838
3839 sp = global_env.linep;
3840 scanequals = f & DOKEY;
3841 foundequals = 0;
3842
3843 loop:
3844 c = subgetc('"', foundequals);
3845 switch (c) {
3846 case 0:
3847 if (sp == global_env.linep)
3848 return 0;
3849 *global_env.linep++ = 0;
3850 return sp;
3851
3852 default:
3853 if (f & DOBLANK && any(c, ifs->value))
3854 goto loop;
3855 break;
3856
3857 case '"':
3858 case '\'':
3859 scanequals = 0;
3860 if (INSUB())
3861 break;
3862 for (c1 = c; (c = subgetc(c1, 1)) != c1;) {
3863 if (c == 0)
3864 break;
3865 if (c == '\'' || !any(c, "$`\""))
3866 c |= QUOTE;
3867 *global_env.linep++ = c;
3868 }
3869 c = 0;
3870 }
3871 unget(c);
3872 if (!isalpha(c) && c != '_')
3873 scanequals = 0;
3874 for (;;) {
3875 c = subgetc('"', foundequals);
3876 if (c == 0
3877 || f & (DOBLANK && any(c, ifs->value))
3878 || (!INSUB() && any(c, "\"'"))
3879 ) {
3880 scanequals = 0;
3881 unget(c);
3882 if (any(c, "\"'"))
3883 goto loop;
3884 break;
3885 }
3886 if (scanequals) {
3887 if (c == '=') {
3888 foundequals = 1;
3889 scanequals = 0;
3890 } else if (!isalnum(c) && c != '_')
3891 scanequals = 0;
3892 }
3893 *global_env.linep++ = c;
3894 }
3895 *global_env.linep++ = 0;
3896 return sp;
3897}
3898
3899/*
3900 * Get characters, substituting for ` and $
3901 */
3902static int subgetc(char ec, int quoted)
3903{
3904 char c;
3905
3906 DBGPRINTF3(("SUBGETC: enter, quoted=%d\n", quoted));
3907
3908 again:
3909 c = my_getc(ec);
3910 if (!INSUB() && ec != '\'') {
3911 if (c == '`') {
3912 if (grave(quoted) == 0)
3913 return 0;
3914 global_env.iop->task = XGRAVE;
3915 goto again;
3916 }
3917 if (c == '$') {
3918 c = dollar(quoted);
3919 if (c == 0) {
3920 global_env.iop->task = XDOLL;
3921 goto again;
3922 }
3923 }
3924 }
3925 return c;
3926}
3927
3928/*
3929 * Prepare to generate the string returned by ${} substitution.
3930 */
3931static int dollar(int quoted)
3932{
3933 int otask;
3934 struct io *oiop;
3935 char *dolp;
3936 char *s, c, *cp = NULL;
3937 struct var *vp;
3938
3939 DBGPRINTF3(("DOLLAR: enter, quoted=%d\n", quoted));
3940
3941 c = readc();
3942 s = global_env.linep;
3943 if (c != '{') {
3944 *global_env.linep++ = c;
3945 if (isalpha(c) || c == '_') {
3946 while ((c = readc()) != 0 && (isalnum(c) || c == '_'))
3947 if (global_env.linep < elinep)
3948 *global_env.linep++ = c;
3949 unget(c);
3950 }
3951 c = 0;
3952 } else {
3953 oiop = global_env.iop;
3954 otask = global_env.iop->task;
3955
3956 global_env.iop->task = XOTHER;
3957 while ((c = subgetc('"', 0)) != 0 && c != '}' && c != '\n')
3958 if (global_env.linep < elinep)
3959 *global_env.linep++ = c;
3960 if (oiop == global_env.iop)
3961 global_env.iop->task = otask;
3962 if (c != '}') {
3963 err("unclosed ${");
3964 gflg = 1;
3965 return c;
3966 }
3967 }
3968 if (global_env.linep >= elinep) {
3969 err("string in ${} too long");
3970 gflg = 1;
3971 global_env.linep -= 10;
3972 }
3973 *global_env.linep = 0;
3974 if (*s)
3975 for (cp = s + 1; *cp; cp++)
3976 if (any(*cp, "=-+?")) {
3977 c = *cp;
3978 *cp++ = 0;
3979 break;
3980 }
3981 if (s[1] == 0 && (*s == '*' || *s == '@')) {
3982 if (dolc > 1) {
3983 /* currently this does not distinguish $* and $@ */
3984 /* should check dollar */
3985 global_env.linep = s;
3986 PUSHIO(awordlist, dolv + 1, dolchar);
3987 return 0;
3988 } else { /* trap the nasty ${=} */
3989 s[0] = '1';
3990 s[1] = '\0';
3991 }
3992 }
3993 vp = lookup(s);
3994 dolp = vp->value;
3995 if (dolp == null) {
3996 switch (c) {
3997 case '=':
3998 if (isdigit(*s)) {
3999 err("can't use ${...=...} with $n");
4000 gflg = 1;
4001 break;
4002 }
4003 setval(vp, cp);
4004 dolp = vp->value;
4005 break;
4006
4007 case '-':
4008 dolp = strsave(cp, areanum);
4009 break;
4010
4011 case '?':
4012 if (*cp == 0) {
4013 prs("missing value for ");
4014 err(s);
4015 } else
4016 err(cp);
4017 gflg = 1;
4018 break;
4019 }
4020 } else if (c == '+')
4021 dolp = strsave(cp, areanum);
4022 if (FLAG['u'] && dolp == null) {
4023 prs("unset variable: ");
4024 err(s);
4025 gflg = 1;
4026 }
4027 global_env.linep = s;
4028 PUSHIO(aword, dolp, quoted ? qstrchar : strchar);
4029 return 0;
4030}
4031
4032/*
4033 * Run the command in `...` and read its output.
4034 */
4035
4036static int grave(int quoted)
4037{
4038 /* moved to G: static char child_cmd[LINELIM]; */
4039
4040 const char *cp;
4041 int i;
4042 int j;
4043 int pf[2];
4044 const char *src;
4045 char *dest;
4046 int count;
4047 int ignore;
4048 int ignore_once;
4049 char *argument_list[4];
4050 struct wdblock *wb = NULL;
4051
4052#if __GNUC__
4053 /* Avoid longjmp clobbering */
4054 (void) &cp;
4055#endif
4056
4057 for (cp = global_env.iop->argp->aword; *cp != '`'; cp++) {
4058 if (*cp == 0) {
4059 err("no closing `");
4060 return 0;
4061 }
4062 }
4063
4064 /* string copy with dollar expansion */
4065 src = global_env.iop->argp->aword;
4066 dest = child_cmd;
4067 count = 0;
4068 ignore = 0;
4069 ignore_once = 0;
4070 while ((*src != '`') && (count < LINELIM)) {
4071 if (*src == '\'')
4072 ignore = !ignore;
4073 if (*src == '\\')
4074 ignore_once = 1;
4075 if (*src == '$' && !ignore && !ignore_once) {
4076 struct var *vp;
4077 /* moved to G to reduce stack usage
4078 char var_name[LINELIM];
4079 char alt_value[LINELIM];
4080 */
4081#define var_name (G.grave__var_name)
4082#define alt_value (G.grave__alt_value)
4083 int var_index = 0;
4084 int alt_index = 0;
4085 char operator = 0;
4086 int braces = 0;
4087 char *value;
4088
4089 src++;
4090 if (*src == '{') {
4091 braces = 1;
4092 src++;
4093 }
4094
4095 var_name[var_index++] = *src++;
4096 while (isalnum(*src) || *src=='_')
4097 var_name[var_index++] = *src++;
4098 var_name[var_index] = 0;
4099
4100 if (braces) {
4101 switch (*src) {
4102 case '}':
4103 break;
4104 case '-':
4105 case '=':
4106 case '+':
4107 case '?':
4108 operator = * src;
4109 break;
4110 default:
4111 err("unclosed ${\n");
4112 return 0;
4113 }
4114 if (operator) {
4115 src++;
4116 while (*src && (*src != '}')) {
4117 alt_value[alt_index++] = *src++;
4118 }
4119 alt_value[alt_index] = 0;
4120 if (*src != '}') {
4121 err("unclosed ${\n");
4122 return 0;
4123 }
4124 }
4125 src++;
4126 }
4127
4128 if (isalpha(*var_name)) {
4129 /* let subshell handle it instead */
4130
4131 char *namep = var_name;
4132
4133 *dest++ = '$';
4134 if (braces)
4135 *dest++ = '{';
4136 while (*namep)
4137 *dest++ = *namep++;
4138 if (operator) {
4139 char *altp = alt_value;
4140 *dest++ = operator;
4141 while (*altp)
4142 *dest++ = *altp++;
4143 }
4144 if (braces)
4145 *dest++ = '}';
4146
4147 wb = addword(lookup(var_name)->name, wb);
4148 } else {
4149 /* expand */
4150
4151 vp = lookup(var_name);
4152 if (vp->value != null)
4153 value = (operator == '+') ?
4154 alt_value : vp->value;
4155 else if (operator == '?') {
4156 err(alt_value);
4157 return 0;
4158 } else if (alt_index && (operator != '+')) {
4159 value = alt_value;
4160 if (operator == '=')
4161 setval(vp, value);
4162 } else
4163 continue;
4164
4165 while (*value && (count < LINELIM)) {
4166 *dest++ = *value++;
4167 count++;
4168 }
4169 }
4170#undef var_name
4171#undef alt_value
4172 } else {
4173 *dest++ = *src++;
4174 count++;
4175 ignore_once = 0;
4176 }
4177 }
4178 *dest = '\0';
4179
4180 if (openpipe(pf) < 0)
4181 return 0;
4182
4183 while ((i = vfork()) == -1 && errno == EAGAIN)
4184 continue;
4185
4186 DBGPRINTF3(("GRAVE: i is %p\n", io));
4187
4188 if (i < 0) {
4189 closepipe(pf);
4190 err((char *) bb_msg_memory_exhausted);
4191 return 0;
4192 }
4193 if (i != 0) {
4194 waitpid(i, NULL, 0); // safe_waitpid?
4195 global_env.iop->argp->aword = ++cp;
4196 close(pf[1]);
4197 PUSHIO(afile, remap(pf[0]),
4198 (int (*)(struct ioarg *)) ((quoted) ? qgravechar : gravechar));
4199 return 1;
4200 }
4201 /* allow trapped signals */
4202 /* XXX - Maybe this signal stuff should go as well? */
4203 for (j = 0; j <= _NSIG; j++)
4204 if (ourtrap[j] && signal(j, SIG_IGN) != SIG_IGN)
4205 signal(j, SIG_DFL);
4206
4207 /* Testcase where below checks are needed:
4208 * close stdout & run this script:
4209 * files=`ls`
4210 * echo "$files" >zz
4211 */
4212 xmove_fd(pf[1], 1);
4213 if (pf[0] != 1)
4214 close(pf[0]);
4215
4216 argument_list[0] = (char *) DEFAULT_SHELL;
4217 argument_list[1] = (char *) "-c";
4218 argument_list[2] = child_cmd;
4219 argument_list[3] = NULL;
4220
4221 cp = rexecve(argument_list[0], argument_list, makenv(1, wb));
4222 prs(argument_list[0]);
4223 prs(": ");
4224 err(cp);
4225 _exit(EXIT_FAILURE);
4226}
4227
4228
4229static char *unquote(char *as)
4230{
4231 char *s;
4232
4233 s = as;
4234 if (s != NULL)
4235 while (*s)
4236 *s++ &= ~QUOTE;
4237 return as;
4238}
4239
4240/* -------- glob.c -------- */
4241
4242/*
4243 * glob
4244 */
4245
4246#define scopy(x) strsave((x), areanum)
4247#define BLKSIZ 512
4248#define NDENT ((BLKSIZ+sizeof(struct dirent)-1)/sizeof(struct dirent))
4249
4250static struct wdblock *cl, *nl;
4251static const char spcl[] ALIGN1= "[?*";
4252
4253static struct wdblock *glob(char *cp, struct wdblock *wb)
4254{
4255 int i;
4256 char *pp;
4257
4258 if (cp == 0)
4259 return wb;
4260 i = 0;
4261 for (pp = cp; *pp; pp++)
4262 if (any(*pp, spcl))
4263 i++;
4264 else if (!any(*pp & ~QUOTE, spcl))
4265 *pp &= ~QUOTE;
4266 if (i != 0) {
4267 for (cl = addword(scopy(cp), NULL); anyspcl(cl); cl = nl) {
4268 nl = newword(cl->w_nword * 2);
4269 for (i = 0; i < cl->w_nword; i++) { /* for each argument */
4270 for (pp = cl->w_words[i]; *pp; pp++)
4271 if (any(*pp, spcl)) {
4272 globname(cl->w_words[i], pp);
4273 break;
4274 }
4275 if (*pp == '\0')
4276 nl = addword(scopy(cl->w_words[i]), nl);
4277 }
4278 for (i = 0; i < cl->w_nword; i++)
4279 DELETE(cl->w_words[i]);
4280 DELETE(cl);
4281 }
4282 if (cl->w_nword) {
4283 for (i = 0; i < cl->w_nword; i++)
4284 unquote(cl->w_words[i]);
4285 qsort_string_vector(cl->w_words, cl->w_nword);
4286 for (i = 0; i < cl->w_nword; i++)
4287 wb = addword(cl->w_words[i], wb);
4288 DELETE(cl);
4289 return wb;
4290 }
4291 }
4292 wb = addword(unquote(cp), wb);
4293 return wb;
4294}
4295
4296static void globname(char *we, char *pp)
4297{
4298 char *np, *cp;
4299 char *name, *gp, *dp;
4300 int k;
4301 DIR *dirp;
4302 struct dirent *de;
4303 char dname[NAME_MAX + 1];
4304 struct stat dbuf;
4305
4306 for (np = we; np != pp; pp--)
4307 if (pp[-1] == '/')
4308 break;
4309 dp = cp = get_space((int) (pp - np) + 3);
4310 while (np < pp)
4311 *cp++ = *np++;
4312 *cp++ = '.';
4313 *cp = '\0';
4314 gp = cp = get_space(strlen(pp) + 1);
4315 while (*np && *np != '/')
4316 *cp++ = *np++;
4317 *cp = '\0';
4318 dirp = opendir(dp);
4319 if (dirp == 0) {
4320 DELETE(dp);
4321 DELETE(gp);
4322 return;
4323 }
4324 dname[NAME_MAX] = '\0';
4325 while ((de = readdir(dirp)) != NULL) {
4326 /* XXX Hmmm... What this could be? (abial) */
4327 /* if (ent[j].d_ino == 0) continue;
4328 */
4329 strncpy(dname, de->d_name, NAME_MAX);
4330 if (dname[0] == '.')
4331 if (*gp != '.')
4332 continue;
4333 for (k = 0; k < NAME_MAX; k++)
4334 if (any(dname[k], spcl))
4335 dname[k] |= QUOTE;
4336 if (gmatch(dname, gp)) {
4337 name = generate(we, pp, dname, np);
4338 if (*np && !anys(np, spcl)) {
4339 if (stat(name, &dbuf)) {
4340 DELETE(name);
4341 continue;
4342 }
4343 }
4344 nl = addword(name, nl);
4345 }
4346 }
4347 closedir(dirp);
4348 DELETE(dp);
4349 DELETE(gp);
4350}
4351
4352/*
4353 * generate a pathname as below.
4354 * start..end1 / middle end
4355 * the slashes come for free
4356 */
4357static char *generate(char *start1, char *end1, char *middle, char *end)
4358{
4359 char *p;
4360 char *op, *xp;
4361
4362 p = op = get_space((int)(end1 - start1) + strlen(middle) + strlen(end) + 2);
4363 xp = start1;
4364 while (xp != end1)
4365 *op++ = *xp++;
4366 xp = middle;
4367 while (*xp != '\0')
4368 *op++ = *xp++;
4369 strcpy(op, end);
4370 return p;
4371}
4372
4373static int anyspcl(struct wdblock *wb)
4374{
4375 int i;
4376 char **wd;
4377
4378 wd = wb->w_words;
4379 for (i = 0; i < wb->w_nword; i++)
4380 if (anys(spcl, *wd++))
4381 return 1;
4382 return 0;
4383}
4384
4385
4386/* -------- word.c -------- */
4387
4388static struct wdblock *newword(int nw)
4389{
4390 struct wdblock *wb;
4391
4392 wb = get_space(sizeof(*wb) + nw * sizeof(char *));
4393 wb->w_bsize = nw;
4394 wb->w_nword = 0;
4395 return wb;
4396}
4397
4398static struct wdblock *addword(char *wd, struct wdblock *wb)
4399{
4400 struct wdblock *wb2;
4401 int nw;
4402
4403 if (wb == NULL)
4404 wb = newword(NSTART);
4405 nw = wb->w_nword;
4406 if (nw >= wb->w_bsize) {
4407 wb2 = newword(nw * 2);
4408 memcpy((char *) wb2->w_words, (char *) wb->w_words,
4409 nw * sizeof(char *));
4410 wb2->w_nword = nw;
4411 DELETE(wb);
4412 wb = wb2;
4413 }
4414 wb->w_words[wb->w_nword++] = wd;
4415 return wb;
4416}
4417
4418static char **getwords(struct wdblock *wb)
4419{
4420 char **wd;
4421 int nb;
4422
4423 if (wb == NULL)
4424 return NULL;
4425 if (wb->w_nword == 0) {
4426 DELETE(wb);
4427 return NULL;
4428 }
4429 nb = sizeof(*wd) * wb->w_nword;
4430 wd = get_space(nb);
4431 memcpy(wd, wb->w_words, nb);
4432 DELETE(wb); /* perhaps should done by caller */
4433 return wd;
4434}
4435
4436
4437/* -------- io.c -------- */
4438
4439/*
4440 * shell IO
4441 */
4442
4443static int my_getc(int ec)
4444{
4445 int c;
4446
4447 if (global_env.linep > elinep) {
4448 while ((c = readc()) != '\n' && c)
4449 continue;
4450 err("input line too long");
4451 gflg = 1;
4452 return c;
4453 }
4454 c = readc();
4455 if ((ec != '\'') && (ec != '`') && (global_env.iop->task != XGRAVE)) {
4456 if (c == '\\') {
4457 c = readc();
4458 if (c == '\n' && ec != '\"')
4459 return my_getc(ec);
4460 c |= QUOTE;
4461 }
4462 }
4463 return c;
4464}
4465
4466static void unget(int c)
4467{
4468 if (global_env.iop >= global_env.iobase)
4469 global_env.iop->peekc = c;
4470}
4471
4472static int eofc(void)
4473{
4474 return global_env.iop < global_env.iobase || (global_env.iop->peekc == 0 && global_env.iop->prev == 0);
4475}
4476
4477static int readc(void)
4478{
4479 int c;
4480
4481 RCPRINTF(("READC: global_env.iop %p, global_env.iobase %p\n", global_env.iop, global_env.iobase));
4482
4483 for (; global_env.iop >= global_env.iobase; global_env.iop--) {
4484 RCPRINTF(("READC: global_env.iop %p, peekc 0x%x\n", global_env.iop, global_env.iop->peekc));
4485 c = global_env.iop->peekc;
4486 if (c != '\0') {
4487 global_env.iop->peekc = 0;
4488 return c;
4489 }
4490 if (global_env.iop->prev != 0) {
4491 c = (*global_env.iop->iofn)(global_env.iop->argp, global_env.iop);
4492 if (c != '\0') {
4493 if (c == -1) {
4494 global_env.iop++;
4495 continue;
4496 }
4497 if (global_env.iop == iostack)
4498 ioecho(c);
4499 global_env.iop->prev = c;
4500 return c;
4501 }
4502 if (global_env.iop->task == XIO && global_env.iop->prev != '\n') {
4503 global_env.iop->prev = 0;
4504 if (global_env.iop == iostack)
4505 ioecho('\n');
4506 return '\n';
4507 }
4508 }
4509 if (global_env.iop->task == XIO) {
4510 if (multiline) {
4511 global_env.iop->prev = 0;
4512 return 0;
4513 }
4514 if (interactive && global_env.iop == iostack + 1) {
4515#if ENABLE_FEATURE_EDITING
4516 current_prompt = prompt->value;
4517#else
4518 prs(prompt->value);
4519#endif
4520 }
4521 }
4522 } /* FOR */
4523
4524 if (global_env.iop >= iostack) {
4525 RCPRINTF(("READC: return 0, global_env.iop %p\n", global_env.iop));
4526 return 0;
4527 }
4528
4529 DBGPRINTF(("READC: leave()...\n"));
4530 leave();
4531 /* NOTREACHED */
4532 return 0;
4533}
4534
4535static void ioecho(char c)
4536{
4537 if (FLAG['v'])
4538 write(STDERR_FILENO, &c, sizeof c);
4539}
4540
4541static void pushio(struct ioarg *argp, int (*fn) (struct ioarg *))
4542{
4543 DBGPRINTF(("PUSHIO: argp %p, argp->afid 0x%x, global_env.iop %p\n", argp,
4544 argp->afid, global_env.iop));
4545
4546 /* Set env ptr for io source to next array spot and check for array overflow */
4547 if (++global_env.iop >= &iostack[NPUSH]) {
4548 global_env.iop--;
4549 err("Shell input nested too deeply");
4550 gflg = 1;
4551 return;
4552 }
4553
4554 /* We did not overflow the NPUSH array spots so setup data structs */
4555
4556 global_env.iop->iofn = (int (*)(struct ioarg *, struct io *)) fn; /* Store data source func ptr */
4557
4558 if (argp->afid != AFID_NOBUF)
4559 global_env.iop->argp = argp;
4560 else {
4561
4562 global_env.iop->argp = ioargstack + (global_env.iop - iostack); /* MAL - index into stack */
4563 *global_env.iop->argp = *argp; /* copy data from temp area into stack spot */
4564
4565 /* MAL - mainbuf is for 1st data source (command line?) and all nested use a single shared buffer? */
4566
4567 if (global_env.iop == &iostack[0])
4568 global_env.iop->argp->afbuf = &mainbuf;
4569 else
4570 global_env.iop->argp->afbuf = &sharedbuf;
4571
4572 /* MAL - if not a termimal AND (commandline OR readable file) then give it a buffer id? */
4573 /* This line appears to be active when running scripts from command line */
4574 if ((isatty(global_env.iop->argp->afile) == 0)
4575 && (global_env.iop == &iostack[0]
4576 || lseek(global_env.iop->argp->afile, 0L, SEEK_CUR) != -1)) {
4577 if (++bufid == AFID_NOBUF) /* counter rollover check, AFID_NOBUF = 11111111 */
4578 bufid = AFID_ID; /* AFID_ID = 0 */
4579
4580 global_env.iop->argp->afid = bufid; /* assign buffer id */
4581 }
4582
4583 DBGPRINTF(("PUSHIO: iostack %p, global_env.iop %p, afbuf %p\n",
4584 iostack, global_env.iop, global_env.iop->argp->afbuf));
4585 DBGPRINTF(("PUSHIO: mbuf %p, sbuf %p, bid %d, global_env.iop %p\n",
4586 &mainbuf, &sharedbuf, bufid, global_env.iop));
4587
4588 }
4589
4590 global_env.iop->prev = ~'\n';
4591 global_env.iop->peekc = 0;
4592 global_env.iop->xchar = 0;
4593 global_env.iop->nlcount = 0;
4594
4595 if (fn == filechar || fn == linechar)
4596 global_env.iop->task = XIO;
4597 else if (fn == (int (*)(struct ioarg *)) gravechar
4598 || fn == (int (*)(struct ioarg *)) qgravechar)
4599 global_env.iop->task = XGRAVE;
4600 else
4601 global_env.iop->task = XOTHER;
4602}
4603
4604static struct io *setbase(struct io *ip)
4605{
4606 struct io *xp;
4607
4608 xp = global_env.iobase;
4609 global_env.iobase = ip;
4610 return xp;
4611}
4612
4613/*
4614 * Input generating functions
4615 */
4616
4617/*
4618 * Produce the characters of a string, then a newline, then NUL.
4619 */
4620static int nlchar(struct ioarg *ap)
4621{
4622 char c;
4623
4624 if (ap->aword == NULL)
4625 return '\0';
4626 c = *ap->aword++;
4627 if (c == '\0') {
4628 ap->aword = NULL;
4629 return '\n';
4630 }
4631 return c;
4632}
4633
4634/*
4635 * Given a list of words, produce the characters
4636 * in them, with a space after each word.
4637 */
4638static int wdchar(struct ioarg *ap)
4639{
4640 char c;
4641 char **wl;
4642
4643 wl = ap->awordlist;
4644 if (wl == NULL)
4645 return 0;
4646 if (*wl != NULL) {
4647 c = *(*wl)++;
4648 if (c != 0)
4649 return c & 0177;
4650 ap->awordlist++;
4651 return ' ';
4652 }
4653 ap->awordlist = NULL;
4654 return '\n';
4655}
4656
4657/*
4658 * Return the characters of a list of words,
4659 * producing a space between them.
4660 */
4661static int dolchar(struct ioarg *ap)
4662{
4663 char *wp;
4664
4665 wp = *ap->awordlist++;
4666 if (wp != NULL) {
4667 PUSHIO(aword, wp, *ap->awordlist == NULL ? strchar : xxchar);
4668 return -1;
4669 }
4670 return 0;
4671}
4672
4673static int xxchar(struct ioarg *ap)
4674{
4675 int c;
4676
4677 if (ap->aword == NULL)
4678 return 0;
4679 c = *ap->aword++;
4680 if (c == '\0') {
4681 ap->aword = NULL;
4682 return ' ';
4683 }
4684 return c;
4685}
4686
4687/*
4688 * Produce the characters from a single word (string).
4689 */
4690static int strchar(struct ioarg *ap)
4691{
4692 if (ap->aword == NULL)
4693 return 0;
4694 return *ap->aword++;
4695}
4696
4697/*
4698 * Produce quoted characters from a single word (string).
4699 */
4700static int qstrchar(struct ioarg *ap)
4701{
4702 int c;
4703
4704 if (ap->aword == NULL)
4705 return 0;
4706 c = *ap->aword++;
4707 if (c)
4708 c |= QUOTE;
4709 return c;
4710}
4711
4712/*
4713 * Return the characters from a file.
4714 */
4715static int filechar(struct ioarg *ap)
4716{
4717 int i;
4718 char c;
4719 struct iobuf *bp = ap->afbuf;
4720
4721 if (ap->afid != AFID_NOBUF) {
4722 i = (ap->afid != bp->id);
4723 if (i || bp->bufp == bp->ebufp) {
4724 if (i)
4725 lseek(ap->afile, ap->afpos, SEEK_SET);
4726
4727 i = nonblock_safe_read(ap->afile, bp->buf, sizeof(bp->buf));
4728 if (i <= 0) {
4729 closef(ap->afile);
4730 return 0;
4731 }
4732
4733 bp->id = ap->afid;
4734 bp->bufp = bp->buf;
4735 bp->ebufp = bp->bufp + i;
4736 }
4737
4738 ap->afpos++;
4739 return *bp->bufp++ & 0177;
4740 }
4741#if ENABLE_FEATURE_EDITING
4742 if (interactive && isatty(ap->afile)) {
4743 /* moved to G: static char filechar_cmdbuf[BUFSIZ]; */
4744 static int position = 0, size = 0;
4745
4746 while (size == 0 || position >= size) {
4747 size = read_line_input(current_prompt, filechar_cmdbuf, BUFSIZ, line_input_state);
4748 if (size < 0) /* Error/EOF */
4749 exit(EXIT_SUCCESS);
4750 position = 0;
4751 /* if Ctrl-C, size == 0 and loop will repeat */
4752 }
4753 c = filechar_cmdbuf[position];
4754 position++;
4755 return c;
4756 }
4757#endif
4758 i = nonblock_safe_read(ap->afile, &c, sizeof(c));
4759 return i == sizeof(c) ? (c & 0x7f) : (closef(ap->afile), 0);
4760}
4761
4762/*
4763 * Return the characters from a here temp file.
4764 */
4765static int herechar(struct ioarg *ap)
4766{
4767 char c;
4768
4769 if (nonblock_safe_read(ap->afile, &c, sizeof(c)) != sizeof(c)) {
4770 close(ap->afile);
4771 c = '\0';
4772 }
4773 return c;
4774}
4775
4776/*
4777 * Return the characters produced by a process (`...`).
4778 * Quote them if required, and remove any trailing newline characters.
4779 */
4780static int gravechar(struct ioarg *ap, struct io *iop)
4781{
4782 int c;
4783
4784 c = qgravechar(ap, iop) & ~QUOTE;
4785 if (c == '\n')
4786 c = ' ';
4787 return c;
4788}
4789
4790static int qgravechar(struct ioarg *ap, struct io *iop)
4791{
4792 int c;
4793
4794 DBGPRINTF3(("QGRAVECHAR: enter, ap=%p, iop=%p\n", ap, iop));
4795
4796 if (iop->xchar) {
4797 if (iop->nlcount) {
4798 iop->nlcount--;
4799 return '\n' | QUOTE;
4800 }
4801 c = iop->xchar;
4802 iop->xchar = 0;
4803 } else if ((c = filechar(ap)) == '\n') {
4804 iop->nlcount = 1;
4805 while ((c = filechar(ap)) == '\n')
4806 iop->nlcount++;
4807 iop->xchar = c;
4808 if (c == 0)
4809 return c;
4810 iop->nlcount--;
4811 c = '\n';
4812 }
4813 return c != 0 ? c | QUOTE : 0;
4814}
4815
4816/*
4817 * Return a single command (usually the first line) from a file.
4818 */
4819static int linechar(struct ioarg *ap)
4820{
4821 int c;
4822
4823 c = filechar(ap);
4824 if (c == '\n') {
4825 if (!multiline) {
4826 closef(ap->afile);
4827 ap->afile = -1; /* illegal value */
4828 }
4829 }
4830 return c;
4831}
4832
4833/*
4834 * Remap fd into shell's fd space
4835 */
4836static int remap(int fd)
4837{
4838 int i;
4839 int map[NOFILE];
4840 int newfd;
4841
4842 DBGPRINTF(("REMAP: fd=%d, global_env.iofd=%d\n", fd, global_env.iofd));
4843
4844 if (fd < global_env.iofd) {
4845 for (i = 0; i < NOFILE; i++)
4846 map[i] = 0;
4847
4848 do {
4849 map[fd] = 1;
4850 newfd = dup(fd);
4851 fd = newfd;
4852 } while (fd >= 0 && fd < global_env.iofd);
4853
4854 for (i = 0; i < NOFILE; i++)
4855 if (map[i])
4856 close(i);
4857
4858 if (fd < 0)
4859 err("too many files open in shell");
4860 }
4861
4862 return fd;
4863}
4864
4865static int openpipe(int *pv)
4866{
4867 int i;
4868
4869 i = pipe(pv);
4870 if (i < 0)
4871 err("can't create pipe - try again");
4872 return i;
4873}
4874
4875static void closepipe(int *pv)
4876{
4877 if (pv != NULL) {
4878 close(pv[0]);
4879 close(pv[1]);
4880 }
4881}
4882
4883
4884/* -------- here.c -------- */
4885
4886/*
4887 * here documents
4888 */
4889
4890static void markhere(char *s, struct ioword *iop)
4891{
4892 struct here *h, *lh;
4893
4894 DBGPRINTF7(("MARKHERE: enter, s=%p\n", s));
4895
4896 h = get_space(sizeof(struct here));
4897 if (h == NULL)
4898 return;
4899
4900 h->h_tag = evalstr(s, DOSUB);
4901 if (h->h_tag == 0)
4902 return;
4903
4904 h->h_iop = iop;
4905 iop->io_name = 0;
4906 h->h_next = NULL;
4907 if (inhere == 0)
4908 inhere = h;
4909 else {
4910 for (lh = inhere; lh != NULL; lh = lh->h_next) {
4911 if (lh->h_next == 0) {
4912 lh->h_next = h;
4913 break;
4914 }
4915 }
4916 }
4917 iop->io_flag |= IOHERE | IOXHERE;
4918 for (s = h->h_tag; *s; s++) {
4919 if (*s & QUOTE) {
4920 iop->io_flag &= ~IOXHERE;
4921 *s &= ~QUOTE;
4922 }
4923 }
4924 h->h_dosub = ((iop->io_flag & IOXHERE) ? '\0' : '\'');
4925}
4926
4927static void gethere(void)
4928{
4929 struct here *h, *hp;
4930
4931 DBGPRINTF7(("GETHERE: enter...\n"));
4932
4933 /* Scan here files first leaving inhere list in place */
4934 for (hp = h = inhere; h != NULL; hp = h, h = h->h_next)
4935 readhere(&h->h_iop->io_name, h->h_tag, h->h_dosub /* NUL or ' */);
4936
4937 /* Make inhere list active - keep list intact for scraphere */
4938 if (hp != NULL) {
4939 hp->h_next = acthere;
4940 acthere = inhere;
4941 inhere = NULL;
4942 }
4943}
4944
4945static void readhere(char **name, char *s, int ec)
4946{
4947 int tf;
4948 char tname[30] = ".msh_XXXXXX";
4949 int c;
4950 jmp_buf ev;
4951 char myline[LINELIM + 1];
4952 char *thenext;
4953
4954 DBGPRINTF7(("READHERE: enter, name=%p, s=%p\n", name, s));
4955
4956 tf = mkstemp(tname);
4957 if (tf < 0)
4958 return;
4959
4960 *name = strsave(tname, areanum);
4961 errpt = ev;
4962 if (newenv(setjmp(errpt)) != 0)
4963 unlink(tname);
4964 else {
4965 pushio(global_env.iop->argp, (int (*)(struct ioarg *)) global_env.iop->iofn);
4966 global_env.iobase = global_env.iop;
4967 for (;;) {
4968 if (interactive && global_env.iop <= iostack) {
4969#if ENABLE_FEATURE_EDITING
4970 current_prompt = cprompt->value;
4971#else
4972 prs(cprompt->value);
4973#endif
4974 }
4975 thenext = myline;
4976 while ((c = my_getc(ec)) != '\n' && c) {
4977 if (ec == '\'')
4978 c &= ~QUOTE;
4979 if (thenext >= &myline[LINELIM]) {
4980 c = 0;
4981 break;
4982 }
4983 *thenext++ = c;
4984 }
4985 *thenext = 0;
4986 if (strcmp(s, myline) == 0 || c == 0)
4987 break;
4988 *thenext++ = '\n';
4989 write(tf, myline, (int) (thenext - myline));
4990 }
4991 if (c == 0) {
4992 prs("here document `");
4993 prs(s);
4994 err("' unclosed");
4995 }
4996 quitenv();
4997 }
4998 close(tf);
4999}
5000
5001/*
5002 * open here temp file.
5003 * if unquoted here, expand here temp file into second temp file.
5004 */
5005static int herein(char *hname, int xdoll)
5006{
5007 int hf;
5008 int tf;
5009
5010#if __GNUC__
5011 /* Avoid longjmp clobbering */
5012 (void) &tf;
5013#endif
5014 if (hname == NULL)
5015 return -1;
5016
5017 DBGPRINTF7(("HEREIN: hname is %s, xdoll=%d\n", hname, xdoll));
5018
5019 hf = open(hname, O_RDONLY);
5020 if (hf < 0)
5021 return -1;
5022
5023 if (xdoll) {
5024 char c;
5025 char tname[30] = ".msh_XXXXXX";
5026 jmp_buf ev;
5027
5028 tf = mkstemp(tname);
5029 if (tf < 0)
5030 return -1;
5031 errpt = ev;
5032 if (newenv(setjmp(errpt)) == 0) {
5033 PUSHIO(afile, hf, herechar);
5034 setbase(global_env.iop);
5035 while ((c = subgetc(0, 0)) != 0) {
5036 c &= ~QUOTE;
5037 write(tf, &c, sizeof c);
5038 }
5039 quitenv();
5040 } else
5041 unlink(tname);
5042 close(tf);
5043 tf = open(tname, O_RDONLY);
5044 unlink(tname);
5045 return tf;
5046 }
5047 return hf;
5048}
5049
5050static void scraphere(void)
5051{
5052 struct here *h;
5053
5054 DBGPRINTF7(("SCRAPHERE: enter...\n"));
5055
5056 for (h = inhere; h != NULL; h = h->h_next) {
5057 if (h->h_iop && h->h_iop->io_name)
5058 unlink(h->h_iop->io_name);
5059 }
5060 inhere = NULL;
5061}
5062
5063/* unlink here temp files before a freearea(area) */
5064static void freehere(int area)
5065{
5066 struct here *h, *hl;
5067
5068 DBGPRINTF6(("FREEHERE: enter, area=%d\n", area));
5069
5070 hl = NULL;
5071 for (h = acthere; h != NULL; h = h->h_next) {
5072 if (getarea((char *) h) >= area) {
5073 if (h->h_iop->io_name != NULL)
5074 unlink(h->h_iop->io_name);
5075 if (hl == NULL)
5076 acthere = h->h_next;
5077 else
5078 hl->h_next = h->h_next;
5079 } else {
5080 hl = h;
5081 }
5082 }
5083}
5084
5085
5086/* -------- sh.c -------- */
5087/*
5088 * shell
5089 */
5090
5091int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
5092int msh_main(int argc, char **argv)
5093{
5094 int f;
5095 char *s;
5096 int cflag;
5097 char *name, **ap;
5098 int (*iof) (struct ioarg *);
5099
5100 INIT_G();
5101
5102 sharedbuf.id = AFID_NOBUF;
5103 mainbuf.id = AFID_NOBUF;
5104 elinep = line + sizeof(line) - 5;
5105
5106#if ENABLE_FEATURE_EDITING
5107 line_input_state = new_line_input_t(FOR_SHELL);
5108#endif
5109
5110 DBGPRINTF(("MSH_MAIN: argc %d, environ %p\n", argc, environ));
5111
5112 initarea();
5113 ap = environ;
5114 if (ap != NULL) {
5115 while (*ap)
5116 assign(*ap++, !COPYV);
5117 for (ap = environ; *ap;)
5118 export(lookup(*ap++));
5119 }
5120 closeall();
5121 areanum = 1;
5122
5123 shell = lookup("SHELL");
5124 if (shell->value == null)
5125 setval(shell, (char *)DEFAULT_SHELL);
5126 export(shell);
5127
5128 homedir = lookup("HOME");
5129 if (homedir->value == null)
5130 setval(homedir, "/");
5131 export(homedir);
5132
5133 setval(lookup("$"), putn(getpid()));
5134
5135 path = lookup("PATH");
5136 if (path->value == null) {
5137 /* Can be merged with same string elsewhere in bbox */
5138 if (geteuid() == 0)
5139 setval(path, bb_default_root_path);
5140 else
5141 setval(path, bb_default_path);
5142 }
5143 export(path);
5144
5145 ifs = lookup("IFS");
5146 if (ifs->value == null)
5147 setval(ifs, " \t\n");
5148
5149#ifdef MSHDEBUG
5150 mshdbg_var = lookup("MSHDEBUG");
5151 if (mshdbg_var->value == null)
5152 setval(mshdbg_var, "0");
5153#endif
5154
5155 prompt = lookup("PS1");
5156#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
5157 if (prompt->value == null)
5158#endif
5159 setval(prompt, DEFAULT_USER_PROMPT);
5160 if (geteuid() == 0) {
5161 setval(prompt, DEFAULT_ROOT_PROMPT);
5162 prompt->status &= ~EXPORT;
5163 }
5164 cprompt = lookup("PS2");
5165#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
5166 if (cprompt->value == null)
5167#endif
5168 setval(cprompt, "> ");
5169
5170 iof = filechar;
5171 cflag = 0;
5172 name = *argv++;
5173 if (--argc >= 1) {
5174 if (argv[0][0] == '-' && argv[0][1] != '\0') {
5175 for (s = argv[0] + 1; *s; s++)
5176 switch (*s) {
5177 case 'c':
5178 prompt->status &= ~EXPORT;
5179 cprompt->status &= ~EXPORT;
5180 setval(prompt, "");
5181 setval(cprompt, "");
5182 cflag = 1;
5183 if (--argc > 0)
5184 PUSHIO(aword, *++argv, iof = nlchar);
5185 break;
5186
5187 case 'q':
5188 qflag = SIG_DFL;
5189 break;
5190
5191 case 's':
5192 /* standard input */
5193 break;
5194
5195 case 't':
5196 prompt->status &= ~EXPORT;
5197 setval(prompt, "");
5198 iof = linechar;
5199 break;
5200
5201 case 'i':
5202 interactive = 1;
5203 default:
5204 if (*s >= 'a' && *s <= 'z')
5205 FLAG[(int) *s]++;
5206 }
5207 } else {
5208 argv--;
5209 argc++;
5210 }
5211
5212 if (iof == filechar && --argc > 0) {
5213 setval(prompt, "");
5214 setval(cprompt, "");
5215 prompt->status &= ~EXPORT;
5216 cprompt->status &= ~EXPORT;
5217
5218/* Shell is non-interactive, activate printf-based debug */
5219#ifdef MSHDEBUG
5220 mshdbg = mshdbg_var->value[0] - '0';
5221 if (mshdbg < 0)
5222 mshdbg = 0;
5223#endif
5224 DBGPRINTF(("MSH_MAIN: calling newfile()\n"));
5225
5226 name = *++argv;
5227 if (newfile(name))
5228 exit(EXIT_FAILURE); /* Exit on error */
5229 }
5230 }
5231
5232 setdash();
5233
5234 /* This won't be true if PUSHIO has been called, say from newfile() above */
5235 if (global_env.iop < iostack) {
5236 PUSHIO(afile, 0, iof);
5237 if (isatty(0) && isatty(1) && !cflag) {
5238 interactive = 1;
5239#if !ENABLE_FEATURE_SH_EXTRA_QUIET
5240#ifdef MSHDEBUG
5241 printf("\n\n%s built-in shell (msh with debug)\n", bb_banner);
5242#else
5243 printf("\n\n%s built-in shell (msh)\n", bb_banner);
5244#endif
5245 printf("Enter 'help' for a list of built-in commands.\n\n");
5246#endif
5247 }
5248 }
5249
5250 signal(SIGQUIT, qflag);
5251 if (name && name[0] == '-') {
5252 interactive = 1;
5253 f = open(".profile", O_RDONLY);
5254 if (f >= 0)
5255 next(remap(f));
5256 f = open("/etc/profile", O_RDONLY);
5257 if (f >= 0)
5258 next(remap(f));
5259 }
5260 if (interactive)
5261 signal(SIGTERM, sig);
5262
5263 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
5264 signal(SIGINT, onintr);
5265
5266/* Handle "msh SCRIPT VAR=val params..." */
5267/* Disabled: bash does not do it! */
5268#if 0
5269 argv++;
5270 /* skip leading args of the form VAR=val */
5271 while (*argv && assign(*argv, !COPYV)) {
5272 argc--;
5273 argv++;
5274 }
5275 argv--;
5276#endif
5277 dolv = argv;
5278 dolc = argc;
5279 dolv[0] = name;
5280
5281 setval(lookup("#"), putn((--dolc < 0) ? (dolc = 0) : dolc));
5282
5283 DBGPRINTF(("MSH_MAIN: begin FOR loop, interactive %d, global_env.iop %p, iostack %p\n", interactive, global_env.iop, iostack));
5284
5285 for (;;) {
5286 if (interactive && global_env.iop <= iostack) {
5287#if ENABLE_FEATURE_EDITING
5288 current_prompt = prompt->value;
5289#else
5290 prs(prompt->value);
5291#endif
5292 }
5293 onecommand();
5294 /* Ensure that getenv("PATH") stays current */
5295 setenv("PATH", path->value, 1);
5296 }
5297
5298 DBGPRINTF(("MSH_MAIN: returning.\n"));
5299}
5300
5301
5302/*
5303 * Copyright (c) 1987,1997, Prentice Hall
5304 * All rights reserved.
5305 *
5306 * Redistribution and use of the MINIX operating system in source and
5307 * binary forms, with or without modification, are permitted provided
5308 * that the following conditions are met:
5309 *
5310 * Redistributions of source code must retain the above copyright
5311 * notice, this list of conditions and the following disclaimer.
5312 *
5313 * Redistributions in binary form must reproduce the above
5314 * copyright notice, this list of conditions and the following
5315 * disclaimer in the documentation and/or other materials provided
5316 * with the distribution.
5317 *
5318 * Neither the name of Prentice Hall nor the names of the software
5319 * authors or contributors may be used to endorse or promote
5320 * products derived from this software without specific prior
5321 * written permission.
5322 *
5323 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, AUTHORS, AND
5324 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
5325 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
5326 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
5327 * IN NO EVENT SHALL PRENTICE HALL OR ANY AUTHORS OR CONTRIBUTORS BE
5328 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
5329 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
5330 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
5331 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
5332 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
5333 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
5334 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5335 *
5336 */
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 669a18dfd..3114ff3f7 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -33,3 +33,423 @@ int FAST_FUNC is_well_formed_var_name(const char *s, char terminator)
33 33
34 return *s == terminator; 34 return *s == terminator;
35} 35}
36
37/* read builtin */
38
39//TODO: use more efficient setvar() which takes a pointer to malloced "VAR=VAL"
40//string. hush naturally has it, and ash has setvareq().
41//Here we can simply store "VAR=" at buffer start and store read data directly
42//after "=", then pass buffer to setvar() to consume.
43const char* FAST_FUNC
44shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
45 char **argv,
46 const char *ifs,
47 int read_flags,
48 const char *opt_n,
49 const char *opt_p,
50 const char *opt_t,
51 const char *opt_u
52)
53{
54 unsigned end_ms; /* -t TIMEOUT */
55 int fd; /* -u FD */
56 int nchars; /* -n NUM */
57 char **pp;
58 char *buffer;
59 struct termios tty, old_tty;
60 const char *retval;
61 int bufpos; /* need to be able to hold -1 */
62 int startword;
63 smallint backslash;
64
65 pp = argv;
66 while (*pp) {
67 if (!is_well_formed_var_name(*pp, '\0')) {
68 /* Mimic bash message */
69 bb_error_msg("read: '%s': not a valid identifier", *pp);
70 return (const char *)(uintptr_t)1;
71 }
72 pp++;
73 }
74
75 nchars = 0; /* if != 0, -n is in effect */
76 if (opt_n) {
77 nchars = bb_strtou(opt_n, NULL, 10);
78 if (nchars < 0 || errno)
79 return "invalid count";
80 /* note: "-n 0": off (bash 3.2 does this too) */
81 }
82 end_ms = 0;
83 if (opt_t) {
84 end_ms = bb_strtou(opt_t, NULL, 10);
85 if (errno || end_ms > UINT_MAX / 2048)
86 return "invalid timeout";
87 end_ms *= 1000;
88#if 0 /* even bash has no -t N.NNN support */
89 ts.tv_sec = bb_strtou(opt_t, &p, 10);
90 ts.tv_usec = 0;
91 /* EINVAL means number is ok, but not terminated by NUL */
92 if (*p == '.' && errno == EINVAL) {
93 char *p2;
94 if (*++p) {
95 int scale;
96 ts.tv_usec = bb_strtou(p, &p2, 10);
97 if (errno)
98 return "invalid timeout";
99 scale = p2 - p;
100 /* normalize to usec */
101 if (scale > 6)
102 return "invalid timeout";
103 while (scale++ < 6)
104 ts.tv_usec *= 10;
105 }
106 } else if (ts.tv_sec < 0 || errno) {
107 return "invalid timeout";
108 }
109 if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
110 return "invalid timeout";
111 }
112#endif /* if 0 */
113 }
114 fd = STDIN_FILENO;
115 if (opt_u) {
116 fd = bb_strtou(opt_u, NULL, 10);
117 if (fd < 0 || errno)
118 return "invalid file descriptor";
119 }
120
121 if (opt_p && isatty(fd)) {
122 fputs(opt_p, stderr);
123 fflush_all();
124 }
125
126 if (ifs == NULL)
127 ifs = defifs;
128
129 if (nchars || (read_flags & BUILTIN_READ_SILENT)) {
130 tcgetattr(fd, &tty);
131 old_tty = tty;
132 if (nchars) {
133 tty.c_lflag &= ~ICANON;
134 tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
135 }
136 if (read_flags & BUILTIN_READ_SILENT) {
137 tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
138 }
139 /* This forces execution of "restoring" tcgetattr later */
140 read_flags |= BUILTIN_READ_SILENT;
141 /* if tcgetattr failed, tcsetattr will fail too.
142 * Ignoring, it's harmless. */
143 tcsetattr(fd, TCSANOW, &tty);
144 }
145
146 retval = (const char *)(uintptr_t)0;
147 startword = 1;
148 backslash = 0;
149 if (end_ms) /* NB: end_ms stays nonzero: */
150 end_ms = ((unsigned)monotonic_ms() + end_ms) | 1;
151 buffer = NULL;
152 bufpos = 0;
153 do {
154 char c;
155
156 if (end_ms) {
157 int timeout;
158 struct pollfd pfd[1];
159
160 pfd[0].fd = fd;
161 pfd[0].events = POLLIN;
162 timeout = end_ms - (unsigned)monotonic_ms();
163 if (timeout <= 0 /* already late? */
164 || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */
165 ) { /* timed out! */
166 retval = (const char *)(uintptr_t)1;
167 goto ret;
168 }
169 }
170
171 if ((bufpos & 0xff) == 0)
172 buffer = xrealloc(buffer, bufpos + 0x100);
173 if (nonblock_safe_read(fd, &buffer[bufpos], 1) != 1) {
174 retval = (const char *)(uintptr_t)1;
175 break;
176 }
177 c = buffer[bufpos];
178 if (c == '\0')
179 continue;
180 if (backslash) {
181 backslash = 0;
182 if (c != '\n')
183 goto put;
184 continue;
185 }
186 if (!(read_flags & BUILTIN_READ_RAW) && c == '\\') {
187 backslash = 1;
188 continue;
189 }
190 if (c == '\n')
191 break;
192
193 /* $IFS splitting. NOT done if we run "read"
194 * without variable names (bash compat).
195 * Thus, "read" and "read REPLY" are not the same.
196 */
197 if (argv[0]) {
198/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */
199 const char *is_ifs = strchr(ifs, c);
200 if (startword && is_ifs) {
201 if (isspace(c))
202 continue;
203 /* it is a non-space ifs char */
204 startword--;
205 if (startword == 1) /* first one? */
206 continue; /* yes, it is not next word yet */
207 }
208 startword = 0;
209 if (argv[1] != NULL && is_ifs) {
210 buffer[bufpos] = '\0';
211 bufpos = 0;
212 setvar(*argv, buffer);
213 argv++;
214 /* can we skip one non-space ifs char? (2: yes) */
215 startword = isspace(c) ? 2 : 1;
216 continue;
217 }
218 }
219 put:
220 bufpos++;
221 } while (--nchars);
222
223 if (argv[0]) {
224 /* Remove trailing space $IFS chars */
225 while (--bufpos >= 0 && isspace(buffer[bufpos]) && strchr(ifs, buffer[bufpos]) != NULL)
226 continue;
227 buffer[bufpos + 1] = '\0';
228 /* Use the remainder as a value for the next variable */
229 setvar(*argv, buffer);
230 /* Set the rest to "" */
231 while (*++argv)
232 setvar(*argv, "");
233 } else {
234 /* Note: no $IFS removal */
235 buffer[bufpos] = '\0';
236 setvar("REPLY", buffer);
237 }
238
239 ret:
240 free(buffer);
241 if (read_flags & BUILTIN_READ_SILENT)
242 tcsetattr(fd, TCSANOW, &old_tty);
243 return retval;
244}
245
246/* ulimit builtin */
247
248struct limits {
249 uint8_t cmd; /* RLIMIT_xxx fit into it */
250 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
251 char option;
252 const char *name;
253};
254
255static const struct limits limits_tbl[] = {
256#ifdef RLIMIT_FSIZE
257 { RLIMIT_FSIZE, 9, 'f', "file size (blocks)" },
258#endif
259#ifdef RLIMIT_CPU
260 { RLIMIT_CPU, 0, 't', "cpu time (seconds)" },
261#endif
262#ifdef RLIMIT_DATA
263 { RLIMIT_DATA, 10, 'd', "data seg size (kb)" },
264#endif
265#ifdef RLIMIT_STACK
266 { RLIMIT_STACK, 10, 's', "stack size (kb)" },
267#endif
268#ifdef RLIMIT_CORE
269 { RLIMIT_CORE, 9, 'c', "core file size (blocks)" },
270#endif
271#ifdef RLIMIT_RSS
272 { RLIMIT_RSS, 10, 'm', "resident set size (kb)" },
273#endif
274#ifdef RLIMIT_MEMLOCK
275 { RLIMIT_MEMLOCK, 10, 'l', "locked memory (kb)" },
276#endif
277#ifdef RLIMIT_NPROC
278 { RLIMIT_NPROC, 0, 'p', "processes" },
279#endif
280#ifdef RLIMIT_NOFILE
281 { RLIMIT_NOFILE, 0, 'n', "file descriptors" },
282#endif
283#ifdef RLIMIT_AS
284 { RLIMIT_AS, 10, 'v', "address space (kb)" },
285#endif
286#ifdef RLIMIT_LOCKS
287 { RLIMIT_LOCKS, 0, 'w', "locks" },
288#endif
289};
290
291enum {
292 OPT_hard = (1 << 0),
293 OPT_soft = (1 << 1),
294};
295
296/* "-": treat args as parameters of option with ASCII code 1 */
297static const char ulimit_opt_string[] = "-HSa"
298#ifdef RLIMIT_FSIZE
299 "f::"
300#endif
301#ifdef RLIMIT_CPU
302 "t::"
303#endif
304#ifdef RLIMIT_DATA
305 "d::"
306#endif
307#ifdef RLIMIT_STACK
308 "s::"
309#endif
310#ifdef RLIMIT_CORE
311 "c::"
312#endif
313#ifdef RLIMIT_RSS
314 "m::"
315#endif
316#ifdef RLIMIT_MEMLOCK
317 "l::"
318#endif
319#ifdef RLIMIT_NPROC
320 "p::"
321#endif
322#ifdef RLIMIT_NOFILE
323 "n::"
324#endif
325#ifdef RLIMIT_AS
326 "v::"
327#endif
328#ifdef RLIMIT_LOCKS
329 "w::"
330#endif
331 ;
332
333static void printlim(unsigned opts, const struct rlimit *limit,
334 const struct limits *l)
335{
336 rlim_t val;
337
338 val = limit->rlim_max;
339 if (!(opts & OPT_hard))
340 val = limit->rlim_cur;
341
342 if (val == RLIM_INFINITY)
343 printf("unlimited\n");
344 else {
345 val >>= l->factor_shift;
346 printf("%llu\n", (long long) val);
347 }
348}
349
350int FAST_FUNC
351shell_builtin_ulimit(char **argv)
352{
353 unsigned opts;
354 unsigned argc;
355
356 /* We can't use getopt32: need to handle commands like
357 * ulimit 123 -c2 -l 456
358 */
359
360 /* In case getopt was already called:
361 * reset the libc getopt() function, which keeps internal state.
362 */
363#ifdef __GLIBC__
364 optind = 0;
365#else /* BSD style */
366 optind = 1;
367 /* optreset = 1; */
368#endif
369 /* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */
370
371 argc = 1;
372 while (argv[argc])
373 argc++;
374
375 opts = 0;
376 while (1) {
377 struct rlimit limit;
378 const struct limits *l;
379 int opt_char = getopt(argc, argv, ulimit_opt_string);
380
381 if (opt_char == -1)
382 break;
383 if (opt_char == 'H') {
384 opts |= OPT_hard;
385 continue;
386 }
387 if (opt_char == 'S') {
388 opts |= OPT_soft;
389 continue;
390 }
391
392 if (opt_char == 'a') {
393 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
394 getrlimit(l->cmd, &limit);
395 printf("-%c: %-30s ", l->option, l->name);
396 printlim(opts, &limit, l);
397 }
398 continue;
399 }
400
401 if (opt_char == 1)
402 opt_char = 'f';
403 for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
404 if (opt_char == l->option) {
405 char *val_str;
406
407 getrlimit(l->cmd, &limit);
408
409 val_str = optarg;
410 if (!val_str && argv[optind] && argv[optind][0] != '-')
411 val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */
412 if (val_str) {
413 rlim_t val;
414
415 if (strcmp(val_str, "unlimited") == 0)
416 val = RLIM_INFINITY;
417 else {
418 if (sizeof(val) == sizeof(int))
419 val = bb_strtou(val_str, NULL, 10);
420 else if (sizeof(val) == sizeof(long))
421 val = bb_strtoul(val_str, NULL, 10);
422 else
423 val = bb_strtoull(val_str, NULL, 10);
424 if (errno) {
425 bb_error_msg("bad number");
426 return EXIT_FAILURE;
427 }
428 val <<= l->factor_shift;
429 }
430//bb_error_msg("opt %c val_str:'%s' val:%lld", opt_char, val_str, (long long)val);
431 if (opts & OPT_hard)
432 limit.rlim_max = val;
433 if ((opts & OPT_soft) || opts == 0)
434 limit.rlim_cur = val;
435//bb_error_msg("setrlimit(%d, %lld, %lld)", l->cmd, (long long)limit.rlim_cur, (long long)limit.rlim_max);
436 if (setrlimit(l->cmd, &limit) < 0) {
437 bb_perror_msg("error setting limit");
438 return EXIT_FAILURE;
439 }
440 } else {
441 printlim(opts, &limit, l);
442 }
443 break;
444 }
445 } /* for (every possible opt) */
446
447 if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) {
448 /* bad option. getopt already complained. */
449 break;
450 }
451
452 } /* while (there are options) */
453
454 return 0;
455}
diff --git a/shell/shell_common.h b/shell/shell_common.h
index 7c8e8c356..1e9f6a691 100644
--- a/shell/shell_common.h
+++ b/shell/shell_common.h
@@ -26,6 +26,26 @@ extern const char defifsvar[]; /* "IFS= \t\n" */
26 26
27int FAST_FUNC is_well_formed_var_name(const char *s, char terminator); 27int FAST_FUNC is_well_formed_var_name(const char *s, char terminator);
28 28
29/* Builtins */
30
31enum {
32 BUILTIN_READ_SILENT = 1 << 0,
33 BUILTIN_READ_RAW = 1 << 1,
34};
35const char* FAST_FUNC
36shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
37 char **argv,
38 const char *ifs,
39 int read_flags,
40 const char *opt_n,
41 const char *opt_p,
42 const char *opt_t,
43 const char *opt_u
44);
45
46int FAST_FUNC
47shell_builtin_ulimit(char **argv);
48
29POP_SAVED_FUNCTION_VISIBILITY 49POP_SAVED_FUNCTION_VISIBILITY
30 50
31#endif 51#endif