20 |
* rewrites. |
* rewrites. |
21 |
* |
* |
22 |
* Other credits: |
* Other credits: |
23 |
* simple_itoa() was lifted from boa-0.93.15 |
* o_addchr() derived from similar w_addchar function in glibc-2.2. |
|
* b_addchr() derived from similar w_addchar function in glibc-2.2 |
|
24 |
* setup_redirect(), redirect_opt_num(), and big chunks of main() |
* setup_redirect(), redirect_opt_num(), and big chunks of main() |
25 |
* and many builtins derived from contributions by Erik Andersen |
* and many builtins derived from contributions by Erik Andersen |
26 |
* miscellaneous bugfixes from Matt Kraai |
* miscellaneous bugfixes from Matt Kraai. |
27 |
* |
* |
28 |
* There are two big (and related) architecture differences between |
* There are two big (and related) architecture differences between |
29 |
* this parser and the lash parser. One is that this version is |
* this parser and the lash parser. One is that this version is |
37 |
* across continuation lines. |
* across continuation lines. |
38 |
* |
* |
39 |
* Bash grammar not implemented: (how many of these were in original sh?) |
* Bash grammar not implemented: (how many of these were in original sh?) |
|
* $@ (those sure look like weird quoting rules) |
|
40 |
* $_ |
* $_ |
|
* ! negation operator for pipes |
|
41 |
* &> and >& redirection of stdout+stderr |
* &> and >& redirection of stdout+stderr |
42 |
* Brace Expansion |
* Brace Expansion |
43 |
* Tilde Expansion |
* Tilde Expansion |
45 |
* aliases |
* aliases |
46 |
* Arithmetic Expansion |
* Arithmetic Expansion |
47 |
* <(list) and >(list) Process Substitution |
* <(list) and >(list) Process Substitution |
48 |
* reserved words: case, esac, select, function |
* reserved words: select, function |
49 |
* Here Documents ( << word ) |
* Here Documents ( << word ) |
50 |
* Functions |
* Functions |
51 |
* Major bugs: |
* Major bugs: |
52 |
* job handling woefully incomplete and buggy |
* job handling woefully incomplete and buggy (improved --vda) |
|
* reserved word execution woefully incomplete and buggy |
|
53 |
* to-do: |
* to-do: |
54 |
* port selected bugfixes from post-0.49 busybox lash - done? |
* port selected bugfixes from post-0.49 busybox lash - done? |
|
* finish implementing reserved words: for, while, until, do, done |
|
55 |
* change { and } from special chars to reserved words |
* change { and } from special chars to reserved words |
56 |
* builtins: break, continue, eval, return, set, trap, ulimit |
* builtins: return, trap, ulimit |
57 |
* test magic exec |
* test magic exec with redirection only |
|
* handle children going into background |
|
|
* clean up recognition of null pipes |
|
58 |
* check setting of global_argc and global_argv |
* check setting of global_argc and global_argv |
|
* control-C handling, probably with longjmp |
|
59 |
* follow IFS rules more precisely, including update semantics |
* follow IFS rules more precisely, including update semantics |
60 |
* figure out what to do with backslash-newline |
* figure out what to do with backslash-newline |
|
* explain why we use signal instead of sigaction |
|
61 |
* propagate syntax errors, die on resource errors? |
* propagate syntax errors, die on resource errors? |
62 |
* continuation lines, both explicit and implicit - done? |
* continuation lines, both explicit and implicit - done? |
63 |
* memory leak finding and plugging - done? |
* memory leak finding and plugging - done? |
64 |
* more testing, especially quoting rules and redirection |
* maybe change charmap[] to use 2-bit entries |
|
* document how quoting rules not precisely followed for variable assignments |
|
|
* maybe change map[] to use 2-bit entries |
|
|
* (eventually) remove all the printf's |
|
65 |
* |
* |
66 |
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
67 |
*/ |
*/ |
68 |
|
|
69 |
#include "busybox.h" |
#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ |
70 |
#include <ctype.h> /* isalpha, isdigit */ |
#include <glob.h> |
|
#include <unistd.h> /* getpid */ |
|
|
#include <stdlib.h> /* getenv, atoi */ |
|
|
#include <string.h> /* strchr */ |
|
|
#include <stdio.h> /* popen etc. */ |
|
|
#include <glob.h> /* glob, of course */ |
|
|
#include <stdarg.h> /* va_list */ |
|
|
#include <errno.h> |
|
|
#include <fcntl.h> |
|
|
#include <getopt.h> /* should be pretty obvious */ |
|
|
|
|
|
#include <sys/stat.h> /* ulimit */ |
|
|
#include <sys/types.h> |
|
|
#include <sys/wait.h> |
|
|
#include <signal.h> |
|
|
|
|
71 |
/* #include <dmalloc.h> */ |
/* #include <dmalloc.h> */ |
72 |
/* #define DEBUG_SHELL */ |
#if ENABLE_HUSH_CASE |
73 |
|
#include <fnmatch.h> |
74 |
|
#endif |
75 |
|
|
76 |
|
#define HUSH_VER_STR "0.91" |
77 |
|
|
78 |
|
#if !BB_MMU && ENABLE_HUSH_TICK |
79 |
|
//#undef ENABLE_HUSH_TICK |
80 |
|
//#define ENABLE_HUSH_TICK 0 |
81 |
|
#warning On NOMMU, hush command substitution is dangerous. |
82 |
|
#warning Dont use it for commands which produce lots of output. |
83 |
|
#warning For more info see shell/hush.c, generate_stream_from_list(). |
84 |
|
#endif |
85 |
|
|
86 |
|
#if !BB_MMU && ENABLE_HUSH_JOB |
87 |
|
#undef ENABLE_HUSH_JOB |
88 |
|
#define ENABLE_HUSH_JOB 0 |
89 |
|
#endif |
90 |
|
|
91 |
|
#if !ENABLE_HUSH_INTERACTIVE |
92 |
|
#undef ENABLE_FEATURE_EDITING |
93 |
|
#define ENABLE_FEATURE_EDITING 0 |
94 |
|
#undef ENABLE_FEATURE_EDITING_FANCY_PROMPT |
95 |
|
#define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0 |
96 |
|
#endif |
97 |
|
|
98 |
|
|
99 |
|
/* Keep unconditionally on for now */ |
100 |
|
#define HUSH_DEBUG 1 |
101 |
|
/* In progress... */ |
102 |
|
#define ENABLE_HUSH_FUNCTIONS 0 |
103 |
|
|
104 |
|
|
105 |
|
/* If you comment out one of these below, it will be #defined later |
106 |
|
* to perform debug printfs to stderr: */ |
107 |
|
#define debug_printf(...) do {} while (0) |
108 |
|
/* Finer-grained debug switches */ |
109 |
|
#define debug_printf_parse(...) do {} while (0) |
110 |
|
#define debug_print_tree(a, b) do {} while (0) |
111 |
|
#define debug_printf_exec(...) do {} while (0) |
112 |
|
#define debug_printf_env(...) do {} while (0) |
113 |
|
#define debug_printf_jobs(...) do {} while (0) |
114 |
|
#define debug_printf_expand(...) do {} while (0) |
115 |
|
#define debug_printf_glob(...) do {} while (0) |
116 |
|
#define debug_printf_list(...) do {} while (0) |
117 |
|
#define debug_printf_subst(...) do {} while (0) |
118 |
|
#define debug_printf_clean(...) do {} while (0) |
119 |
|
|
120 |
|
#ifndef debug_printf |
121 |
|
#define debug_printf(...) fprintf(stderr, __VA_ARGS__) |
122 |
|
#endif |
123 |
|
|
124 |
|
#ifndef debug_printf_parse |
125 |
|
#define debug_printf_parse(...) fprintf(stderr, __VA_ARGS__) |
126 |
|
#endif |
127 |
|
|
128 |
|
#ifndef debug_printf_exec |
129 |
|
#define debug_printf_exec(...) fprintf(stderr, __VA_ARGS__) |
130 |
|
#endif |
131 |
|
|
132 |
|
#ifndef debug_printf_env |
133 |
|
#define debug_printf_env(...) fprintf(stderr, __VA_ARGS__) |
134 |
|
#endif |
135 |
|
|
136 |
|
#ifndef debug_printf_jobs |
137 |
|
#define debug_printf_jobs(...) fprintf(stderr, __VA_ARGS__) |
138 |
|
#define DEBUG_JOBS 1 |
139 |
|
#else |
140 |
|
#define DEBUG_JOBS 0 |
141 |
|
#endif |
142 |
|
|
143 |
|
#ifndef debug_printf_expand |
144 |
|
#define debug_printf_expand(...) fprintf(stderr, __VA_ARGS__) |
145 |
|
#define DEBUG_EXPAND 1 |
146 |
|
#else |
147 |
|
#define DEBUG_EXPAND 0 |
148 |
|
#endif |
149 |
|
|
150 |
|
#ifndef debug_printf_glob |
151 |
|
#define debug_printf_glob(...) fprintf(stderr, __VA_ARGS__) |
152 |
|
#define DEBUG_GLOB 1 |
153 |
|
#else |
154 |
|
#define DEBUG_GLOB 0 |
155 |
|
#endif |
156 |
|
|
157 |
|
#ifndef debug_printf_list |
158 |
|
#define debug_printf_list(...) fprintf(stderr, __VA_ARGS__) |
159 |
|
#endif |
160 |
|
|
161 |
|
#ifndef debug_printf_subst |
162 |
|
#define debug_printf_subst(...) fprintf(stderr, __VA_ARGS__) |
163 |
|
#endif |
164 |
|
|
165 |
#include "cmdedit.h" |
#ifndef debug_printf_clean |
166 |
|
/* broken, of course, but OK for testing */ |
167 |
|
static const char *indenter(int i) |
168 |
|
{ |
169 |
|
static const char blanks[] ALIGN1 = |
170 |
|
" "; |
171 |
|
return &blanks[sizeof(blanks) - i - 1]; |
172 |
|
} |
173 |
|
#define debug_printf_clean(...) fprintf(stderr, __VA_ARGS__) |
174 |
|
#define DEBUG_CLEAN 1 |
175 |
|
#endif |
176 |
|
|
177 |
|
#if DEBUG_EXPAND |
178 |
|
static void debug_print_strings(const char *prefix, char **vv) |
179 |
|
{ |
180 |
|
fprintf(stderr, "%s:\n", prefix); |
181 |
|
while (*vv) |
182 |
|
fprintf(stderr, " '%s'\n", *vv++); |
183 |
|
} |
184 |
|
#else |
185 |
|
#define debug_print_strings(prefix, vv) ((void)0) |
186 |
|
#endif |
187 |
|
|
188 |
|
/* |
189 |
|
* Leak hunting. Use hush_leaktool.sh for post-processing. |
190 |
|
*/ |
191 |
|
#ifdef FOR_HUSH_LEAKTOOL |
192 |
|
/* suppress "warning: no previous prototype..." */ |
193 |
|
void *xxmalloc(int lineno, size_t size); |
194 |
|
void *xxrealloc(int lineno, void *ptr, size_t size); |
195 |
|
char *xxstrdup(int lineno, const char *str); |
196 |
|
void xxfree(void *ptr); |
197 |
|
void *xxmalloc(int lineno, size_t size) |
198 |
|
{ |
199 |
|
void *ptr = xmalloc((size + 0xff) & ~0xff); |
200 |
|
fprintf(stderr, "line %d: malloc %p\n", lineno, ptr); |
201 |
|
return ptr; |
202 |
|
} |
203 |
|
void *xxrealloc(int lineno, void *ptr, size_t size) |
204 |
|
{ |
205 |
|
ptr = xrealloc(ptr, (size + 0xff) & ~0xff); |
206 |
|
fprintf(stderr, "line %d: realloc %p\n", lineno, ptr); |
207 |
|
return ptr; |
208 |
|
} |
209 |
|
char *xxstrdup(int lineno, const char *str) |
210 |
|
{ |
211 |
|
char *ptr = xstrdup(str); |
212 |
|
fprintf(stderr, "line %d: strdup %p\n", lineno, ptr); |
213 |
|
return ptr; |
214 |
|
} |
215 |
|
void xxfree(void *ptr) |
216 |
|
{ |
217 |
|
fprintf(stderr, "free %p\n", ptr); |
218 |
|
free(ptr); |
219 |
|
} |
220 |
|
#define xmalloc(s) xxmalloc(__LINE__, s) |
221 |
|
#define xrealloc(p, s) xxrealloc(__LINE__, p, s) |
222 |
|
#define xstrdup(s) xxstrdup(__LINE__, s) |
223 |
|
#define free(p) xxfree(p) |
224 |
|
#endif |
225 |
|
|
226 |
|
|
227 |
|
/* Do we support ANY keywords? */ |
228 |
|
#if ENABLE_HUSH_IF || ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE |
229 |
|
#define HAS_KEYWORDS 1 |
230 |
|
#define IF_HAS_KEYWORDS(...) __VA_ARGS__ |
231 |
|
#define IF_HAS_NO_KEYWORDS(...) |
232 |
|
#else |
233 |
|
#define HAS_KEYWORDS 0 |
234 |
|
#define IF_HAS_KEYWORDS(...) |
235 |
|
#define IF_HAS_NO_KEYWORDS(...) __VA_ARGS__ |
236 |
|
#endif |
237 |
|
|
|
#define SPECIAL_VAR_SYMBOL 03 |
|
|
#define FLAG_EXIT_FROM_LOOP 1 |
|
|
#define FLAG_PARSE_SEMICOLON (1 << 1) /* symbol ';' is special for parser */ |
|
|
#define FLAG_REPARSING (1 << 2) /* >=2nd pass */ |
|
238 |
|
|
239 |
typedef enum { |
#define SPECIAL_VAR_SYMBOL 3 |
240 |
|
#define PARSEFLAG_EXIT_FROM_LOOP 1 |
241 |
|
|
242 |
|
typedef enum redir_type { |
243 |
REDIRECT_INPUT = 1, |
REDIRECT_INPUT = 1, |
244 |
REDIRECT_OVERWRITE = 2, |
REDIRECT_OVERWRITE = 2, |
245 |
REDIRECT_APPEND = 3, |
REDIRECT_APPEND = 3, |
247 |
REDIRECT_IO = 5 |
REDIRECT_IO = 5 |
248 |
} redir_type; |
} redir_type; |
249 |
|
|
250 |
/* The descrip member of this structure is only used to make debugging |
/* The descrip member of this structure is only used to make |
251 |
* output pretty */ |
* debugging output pretty */ |
252 |
static const struct {int mode; int default_fd; const char *descrip;} redir_table[] = { |
static const struct { |
253 |
|
int mode; |
254 |
|
signed char default_fd; |
255 |
|
char descrip[3]; |
256 |
|
} redir_table[] = { |
257 |
{ 0, 0, "()" }, |
{ 0, 0, "()" }, |
258 |
{ O_RDONLY, 0, "<" }, |
{ O_RDONLY, 0, "<" }, |
259 |
{ O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, |
{ O_CREAT|O_TRUNC|O_WRONLY, 1, ">" }, |
262 |
{ O_RDWR, 1, "<>" } |
{ O_RDWR, 1, "<>" } |
263 |
}; |
}; |
264 |
|
|
265 |
typedef enum { |
typedef enum pipe_style { |
266 |
PIPE_SEQ = 1, |
PIPE_SEQ = 1, |
267 |
PIPE_AND = 2, |
PIPE_AND = 2, |
268 |
PIPE_OR = 3, |
PIPE_OR = 3, |
269 |
PIPE_BG = 4, |
PIPE_BG = 4, |
270 |
} pipe_style; |
} pipe_style; |
271 |
|
|
272 |
/* might eventually control execution */ |
typedef enum reserved_style { |
|
typedef enum { |
|
273 |
RES_NONE = 0, |
RES_NONE = 0, |
274 |
RES_IF = 1, |
#if ENABLE_HUSH_IF |
275 |
RES_THEN = 2, |
RES_IF , |
276 |
RES_ELIF = 3, |
RES_THEN , |
277 |
RES_ELSE = 4, |
RES_ELIF , |
278 |
RES_FI = 5, |
RES_ELSE , |
279 |
RES_FOR = 6, |
RES_FI , |
280 |
RES_WHILE = 7, |
#endif |
281 |
RES_UNTIL = 8, |
#if ENABLE_HUSH_LOOPS |
282 |
RES_DO = 9, |
RES_FOR , |
283 |
RES_DONE = 10, |
RES_WHILE , |
284 |
RES_XXXX = 11, |
RES_UNTIL , |
285 |
RES_IN = 12, |
RES_DO , |
286 |
RES_SNTX = 13 |
RES_DONE , |
287 |
|
#endif |
288 |
|
#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE |
289 |
|
RES_IN , |
290 |
|
#endif |
291 |
|
#if ENABLE_HUSH_CASE |
292 |
|
RES_CASE , |
293 |
|
/* two pseudo-keywords support contrived "case" syntax: */ |
294 |
|
RES_MATCH , /* "word)" */ |
295 |
|
RES_CASEI , /* "this command is inside CASE" */ |
296 |
|
RES_ESAC , |
297 |
|
#endif |
298 |
|
RES_XXXX , |
299 |
|
RES_SNTX |
300 |
} reserved_style; |
} reserved_style; |
|
#define FLAG_END (1<<RES_NONE) |
|
|
#define FLAG_IF (1<<RES_IF) |
|
|
#define FLAG_THEN (1<<RES_THEN) |
|
|
#define FLAG_ELIF (1<<RES_ELIF) |
|
|
#define FLAG_ELSE (1<<RES_ELSE) |
|
|
#define FLAG_FI (1<<RES_FI) |
|
|
#define FLAG_FOR (1<<RES_FOR) |
|
|
#define FLAG_WHILE (1<<RES_WHILE) |
|
|
#define FLAG_UNTIL (1<<RES_UNTIL) |
|
|
#define FLAG_DO (1<<RES_DO) |
|
|
#define FLAG_DONE (1<<RES_DONE) |
|
|
#define FLAG_IN (1<<RES_IN) |
|
|
#define FLAG_START (1<<RES_XXXX) |
|
|
|
|
|
/* This holds pointers to the various results of parsing */ |
|
|
struct p_context { |
|
|
struct child_prog *child; |
|
|
struct pipe *list_head; |
|
|
struct pipe *pipe; |
|
|
struct redir_struct *pending_redirect; |
|
|
reserved_style w; |
|
|
int old_flag; /* for figuring out valid reserved words */ |
|
|
struct p_context *stack; |
|
|
int type; /* define type of parser : ";$" common or special symbol */ |
|
|
/* How about quoting status? */ |
|
|
}; |
|
301 |
|
|
302 |
struct redir_struct { |
struct redir_struct { |
303 |
redir_type type; /* type of redirection */ |
struct redir_struct *next; |
304 |
int fd; /* file descriptor being redirected */ |
char *rd_filename; /* filename */ |
305 |
int dup; /* -1, or file descriptor being duplicated */ |
int fd; /* file descriptor being redirected */ |
306 |
struct redir_struct *next; /* pointer to the next redirect in the list */ |
int dup; /* -1, or file descriptor being duplicated */ |
307 |
glob_t word; /* *word.gl_pathv is the filename */ |
smallint /*enum redir_type*/ rd_type; |
308 |
}; |
}; |
309 |
|
|
310 |
struct child_prog { |
struct command { |
311 |
pid_t pid; /* 0 if exited */ |
pid_t pid; /* 0 if exited */ |
312 |
char **argv; /* program name and arguments */ |
int assignment_cnt; /* how many argv[i] are assignments? */ |
313 |
struct pipe *group; /* if non-NULL, first in group or subshell */ |
smallint is_stopped; /* is the command currently running? */ |
314 |
int subshell; /* flag, non-zero if group must be forked */ |
smallint grp_type; |
315 |
struct redir_struct *redirects; /* I/O redirections */ |
struct pipe *group; /* if non-NULL, this "prog" is {} group, |
316 |
glob_t glob_result; /* result of parameter globbing */ |
* subshell, or a compound statement */ |
317 |
int is_stopped; /* is the program currently running? */ |
char **argv; /* command name and arguments */ |
318 |
struct pipe *family; /* pointer back to the child's parent pipe */ |
struct redir_struct *redirects; /* I/O redirections */ |
|
int sp; /* number of SPECIAL_VAR_SYMBOL */ |
|
|
int type; |
|
319 |
}; |
}; |
320 |
|
/* argv vector may contain variable references (^Cvar^C, ^C0^C etc) |
321 |
|
* and on execution these are substituted with their values. |
322 |
|
* Substitution can make _several_ words out of one argv[n]! |
323 |
|
* Example: argv[0]=='.^C*^C.' here: echo .$*. |
324 |
|
* References of the form ^C`cmd arg^C are `cmd arg` substitutions. |
325 |
|
*/ |
326 |
|
#define GRP_NORMAL 0 |
327 |
|
#define GRP_SUBSHELL 1 |
328 |
|
#if ENABLE_HUSH_FUNCTIONS |
329 |
|
#define GRP_FUNCTION 2 |
330 |
|
#endif |
331 |
|
|
332 |
struct pipe { |
struct pipe { |
333 |
int jobid; /* job number */ |
struct pipe *next; |
334 |
int num_progs; /* total number of programs in job */ |
int num_cmds; /* total number of commands in job */ |
335 |
int running_progs; /* number of programs running */ |
int alive_cmds; /* number of commands running (not exited) */ |
336 |
char *text; /* name of job */ |
int stopped_cmds; /* number of commands alive, but stopped */ |
337 |
char *cmdbuf; /* buffer various argv's point into */ |
#if ENABLE_HUSH_JOB |
338 |
pid_t pgrp; /* process group ID for the job */ |
int jobid; /* job number */ |
339 |
struct child_prog *progs; /* array of commands in pipe */ |
pid_t pgrp; /* process group ID for the job */ |
340 |
struct pipe *next; /* to track background commands */ |
char *cmdtext; /* name of job */ |
341 |
int stopped_progs; /* number of programs alive, but stopped */ |
#endif |
342 |
int job_context; /* bitmask defining current context */ |
struct command *cmds; /* array of commands in pipe */ |
343 |
pipe_style followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */ |
smallint followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */ |
344 |
reserved_style r_mode; /* supports if, for, while, until */ |
IF_HAS_KEYWORDS(smallint pi_inverted;) /* "! cmd | cmd" */ |
345 |
|
IF_HAS_KEYWORDS(smallint res_word;) /* needed for if, for, while, until... */ |
346 |
}; |
}; |
347 |
|
|
348 |
struct close_me { |
/* This holds pointers to the various results of parsing */ |
349 |
int fd; |
struct parse_context { |
350 |
struct close_me *next; |
struct command *command; |
351 |
|
struct pipe *list_head; |
352 |
|
struct pipe *pipe; |
353 |
|
struct redir_struct *pending_redirect; |
354 |
|
#if HAS_KEYWORDS |
355 |
|
smallint ctx_res_w; |
356 |
|
smallint ctx_inverted; /* "! cmd | cmd" */ |
357 |
|
#if ENABLE_HUSH_CASE |
358 |
|
smallint ctx_dsemicolon; /* ";;" seen */ |
359 |
|
#endif |
360 |
|
int old_flag; /* bitmask of FLAG_xxx, for figuring out valid reserved words */ |
361 |
|
struct parse_context *stack; |
362 |
|
#endif |
363 |
}; |
}; |
364 |
|
|
365 |
struct variables { |
/* On program start, environ points to initial environment. |
366 |
char *name; |
* putenv adds new pointers into it, unsetenv removes them. |
367 |
char *value; |
* Neither of these (de)allocates the strings. |
368 |
int flg_export; |
* setenv allocates new strings in malloc space and does putenv, |
369 |
int flg_read_only; |
* and thus setenv is unusable (leaky) for shell's purposes */ |
370 |
struct variables *next; |
#define setenv(...) setenv_is_leaky_dont_use() |
371 |
|
struct variable { |
372 |
|
struct variable *next; |
373 |
|
char *varstr; /* points to "name=" portion */ |
374 |
|
int max_len; /* if > 0, name is part of initial env; else name is malloced */ |
375 |
|
smallint flg_export; /* putenv should be done on this var */ |
376 |
|
smallint flg_read_only; |
377 |
}; |
}; |
378 |
|
|
379 |
/* globals, connect us to the outside world |
typedef struct o_string { |
|
* the first three support $?, $#, and $1 */ |
|
|
static char **global_argv; |
|
|
static int global_argc; |
|
|
static int last_return_code; |
|
|
extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */ |
|
|
|
|
|
/* "globals" within this file */ |
|
|
static char *ifs; |
|
|
static unsigned char map[256]; |
|
|
static int fake_mode; |
|
|
static int interactive; |
|
|
static struct close_me *close_me_head; |
|
|
static const char *cwd; |
|
|
static struct pipe *job_list; |
|
|
static unsigned int last_bg_pid; |
|
|
static int last_jobid; |
|
|
static unsigned int shell_terminal; |
|
|
static char *PS1; |
|
|
static char *PS2; |
|
|
static struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; |
|
|
static struct variables *top_vars = &shell_ver; |
|
|
|
|
|
|
|
|
#define B_CHUNK (100) |
|
|
#define B_NOSPAC 1 |
|
|
|
|
|
typedef struct { |
|
380 |
char *data; |
char *data; |
381 |
int length; |
int length; /* position where data is appended */ |
382 |
int maxlen; |
int maxlen; |
383 |
int quote; |
/* Misnomer! it's not "quoting", it's "protection against globbing"! |
384 |
int nonnull; |
* (by prepending \ to *, ?, [ and to \ too) */ |
385 |
|
smallint o_quote; |
386 |
|
smallint o_glob; |
387 |
|
smallint nonnull; |
388 |
|
smallint has_empty_slot; |
389 |
|
smallint o_assignment; /* 0:maybe, 1:yes, 2:no */ |
390 |
} o_string; |
} o_string; |
391 |
#define NULL_O_STRING {NULL,0,0,0,0} |
enum { |
392 |
/* used for initialization: |
MAYBE_ASSIGNMENT = 0, |
393 |
o_string foo = NULL_O_STRING; */ |
DEFINITELY_ASSIGNMENT = 1, |
394 |
|
NOT_ASSIGNMENT = 2, |
395 |
|
WORD_IS_KEYWORD = 3, /* not assigment, but next word may be: "if v=xyz cmd;" */ |
396 |
|
}; |
397 |
|
/* Used for initialization: o_string foo = NULL_O_STRING; */ |
398 |
|
#define NULL_O_STRING { NULL } |
399 |
|
|
400 |
/* I can almost use ordinary FILE *. Is open_memstream() universally |
/* I can almost use ordinary FILE*. Is open_memstream() universally |
401 |
* available? Where is it documented? */ |
* available? Where is it documented? */ |
402 |
struct in_str { |
typedef struct in_str { |
403 |
const char *p; |
const char *p; |
404 |
|
/* eof_flag=1: last char in ->p is really an EOF */ |
405 |
|
char eof_flag; /* meaningless if ->p == NULL */ |
406 |
char peek_buf[2]; |
char peek_buf[2]; |
407 |
int __promptme; |
#if ENABLE_HUSH_INTERACTIVE |
408 |
int promptmode; |
smallint promptme; |
409 |
|
smallint promptmode; /* 0: PS1, 1: PS2 */ |
410 |
|
#endif |
411 |
FILE *file; |
FILE *file; |
412 |
int (*get) (struct in_str *); |
int (*get) (struct in_str *); |
413 |
int (*peek) (struct in_str *); |
int (*peek) (struct in_str *); |
414 |
|
} in_str; |
415 |
|
#define i_getch(input) ((input)->get(input)) |
416 |
|
#define i_peek(input) ((input)->peek(input)) |
417 |
|
|
418 |
|
enum { |
419 |
|
CHAR_ORDINARY = 0, |
420 |
|
CHAR_ORDINARY_IF_QUOTED = 1, /* example: *, # */ |
421 |
|
CHAR_IFS = 2, /* treated as ordinary if quoted */ |
422 |
|
CHAR_SPECIAL = 3, /* example: $ */ |
423 |
}; |
}; |
|
#define b_getch(input) ((input)->get(input)) |
|
|
#define b_peek(input) ((input)->peek(input)) |
|
424 |
|
|
425 |
#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" |
enum { |
426 |
|
BC_BREAK = 1, |
427 |
|
BC_CONTINUE = 2, |
428 |
|
}; |
429 |
|
|
430 |
struct built_in_command { |
|
431 |
const char *cmd; /* name */ |
/* "Globals" within this file */ |
432 |
const char *descr; /* description */ |
|
433 |
int (*function) (struct child_prog *); /* function ptr */ |
/* Sorted roughly by size (smaller offsets == smaller code) */ |
434 |
|
struct globals { |
435 |
|
#if ENABLE_HUSH_INTERACTIVE |
436 |
|
/* 'interactive_fd' is a fd# open to ctty, if we have one |
437 |
|
* _AND_ if we decided to act interactively */ |
438 |
|
int interactive_fd; |
439 |
|
const char *PS1; |
440 |
|
const char *PS2; |
441 |
|
#endif |
442 |
|
#if ENABLE_FEATURE_EDITING |
443 |
|
line_input_t *line_input_state; |
444 |
|
#endif |
445 |
|
pid_t root_pid; |
446 |
|
pid_t last_bg_pid; |
447 |
|
#if ENABLE_HUSH_JOB |
448 |
|
int run_list_level; |
449 |
|
pid_t saved_tty_pgrp; |
450 |
|
int last_jobid; |
451 |
|
struct pipe *job_list; |
452 |
|
struct pipe *toplevel_list; |
453 |
|
smallint ctrl_z_flag; |
454 |
|
#endif |
455 |
|
#if ENABLE_HUSH_LOOPS |
456 |
|
smallint flag_break_continue; |
457 |
|
#endif |
458 |
|
smallint fake_mode; |
459 |
|
/* these three support $?, $#, and $1 */ |
460 |
|
smalluint last_return_code; |
461 |
|
/* is global_argv and global_argv[1..n] malloced? (note: not [0]) */ |
462 |
|
smalluint global_args_malloced; |
463 |
|
/* how many non-NULL argv's we have. NB: $# + 1 */ |
464 |
|
int global_argc; |
465 |
|
char **global_argv; |
466 |
|
#if ENABLE_HUSH_LOOPS |
467 |
|
unsigned depth_break_continue; |
468 |
|
unsigned depth_of_loop; |
469 |
|
#endif |
470 |
|
const char *ifs; |
471 |
|
const char *cwd; |
472 |
|
struct variable *top_var; /* = &G.shell_ver (set in main()) */ |
473 |
|
struct variable shell_ver; |
474 |
|
#if ENABLE_FEATURE_SH_STANDALONE |
475 |
|
struct nofork_save_area nofork_save; |
476 |
|
#endif |
477 |
|
#if ENABLE_HUSH_JOB |
478 |
|
sigjmp_buf toplevel_jb; |
479 |
|
#endif |
480 |
|
unsigned char charmap[256]; |
481 |
|
char user_input_buf[ENABLE_FEATURE_EDITING ? BUFSIZ : 2]; |
482 |
}; |
}; |
483 |
|
|
484 |
/* belongs in busybox.h */ |
#define G (*ptr_to_globals) |
485 |
static int max(int a, int b) { |
/* Not #defining name to G.name - this quickly gets unwieldy |
486 |
return (a>b)?a:b; |
* (too many defines). Also, I actually prefer to see when a variable |
487 |
} |
* is global, thus "G." prefix is a useful hint */ |
488 |
|
#define INIT_G() do { \ |
489 |
|
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ |
490 |
|
} while (0) |
491 |
|
|
492 |
|
|
493 |
/* This should be in utility.c */ |
#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" |
494 |
#ifdef DEBUG_SHELL |
|
495 |
static void debug_printf(const char *format, ...) |
#if 1 |
496 |
{ |
/* Normal */ |
497 |
va_list args; |
static void syntax(const char *msg) |
498 |
va_start(args, format); |
{ |
499 |
vfprintf(stderr, format, args); |
#if ENABLE_HUSH_INTERACTIVE |
500 |
va_end(args); |
/* Was using fancy stuff: |
501 |
|
* (G.interactive_fd ? bb_error_msg : bb_error_msg_and_die)(...params...) |
502 |
|
* but it SEGVs. ?! Oh well... explicit temp ptr works around that */ |
503 |
|
void FAST_FUNC (*fp)(const char *s, ...); |
504 |
|
fp = (G.interactive_fd ? bb_error_msg : bb_error_msg_and_die); |
505 |
|
fp(msg ? "%s: %s" : "syntax error", "syntax error", msg); |
506 |
|
#else |
507 |
|
bb_error_msg_and_die(msg ? "%s: %s" : "syntax error", "syntax error", msg); |
508 |
|
#endif |
509 |
} |
} |
510 |
/* broken, of course, but OK for testing */ |
|
511 |
static char *indenter(int i) |
#else |
512 |
|
/* Debug */ |
513 |
|
static void syntax_lineno(int line) |
514 |
{ |
{ |
515 |
static char blanks[]=" "; |
#if ENABLE_HUSH_INTERACTIVE |
516 |
return &blanks[sizeof(blanks)-i-1]; |
void FAST_FUNC (*fp)(const char *s, ...); |
517 |
} |
fp = (G.interactive_fd ? bb_error_msg : bb_error_msg_and_die); |
518 |
|
fp("syntax error hush.c:%d", line); |
519 |
#else |
#else |
520 |
#define debug_printf(...) do {;} while(0); |
bb_error_msg_and_die("syntax error hush.c:%d", line); |
521 |
#endif |
#endif |
|
#define final_printf debug_printf |
|
|
|
|
|
static void __syntax(char *file, int line) { |
|
|
bb_error_msg("syntax error %s:%d", file, line); |
|
522 |
} |
} |
523 |
// NB: was __FILE__, but that produces full path sometimess, so... |
#define syntax(str) syntax_lineno(__LINE__) |
524 |
#define syntax() __syntax("hush.c", __LINE__) |
#endif |
525 |
|
|
526 |
/* Index of subroutines: */ |
/* Index of subroutines: */ |
|
/* function prototypes for builtins */ |
|
|
static int builtin_cd(struct child_prog *child); |
|
|
static int builtin_env(struct child_prog *child); |
|
|
static int builtin_eval(struct child_prog *child); |
|
|
static int builtin_exec(struct child_prog *child); |
|
|
static int builtin_exit(struct child_prog *child); |
|
|
static int builtin_export(struct child_prog *child); |
|
|
static int builtin_fg_bg(struct child_prog *child); |
|
|
static int builtin_help(struct child_prog *child); |
|
|
static int builtin_jobs(struct child_prog *child); |
|
|
static int builtin_pwd(struct child_prog *child); |
|
|
static int builtin_read(struct child_prog *child); |
|
|
static int builtin_set(struct child_prog *child); |
|
|
static int builtin_shift(struct child_prog *child); |
|
|
static int builtin_source(struct child_prog *child); |
|
|
static int builtin_umask(struct child_prog *child); |
|
|
static int builtin_unset(struct child_prog *child); |
|
|
static int builtin_not_written(struct child_prog *child); |
|
|
/* o_string manipulation: */ |
|
|
static int b_check_space(o_string *o, int len); |
|
|
static int b_addchr(o_string *o, int ch); |
|
|
static void b_reset(o_string *o); |
|
|
static int b_addqchr(o_string *o, int ch, int quote); |
|
|
static int b_adduint(o_string *o, unsigned int i); |
|
527 |
/* in_str manipulations: */ |
/* in_str manipulations: */ |
528 |
static int static_get(struct in_str *i); |
static int static_get(struct in_str *i); |
529 |
static int static_peek(struct in_str *i); |
static int static_peek(struct in_str *i); |
531 |
static int file_peek(struct in_str *i); |
static int file_peek(struct in_str *i); |
532 |
static void setup_file_in_str(struct in_str *i, FILE *f); |
static void setup_file_in_str(struct in_str *i, FILE *f); |
533 |
static void setup_string_in_str(struct in_str *i, const char *s); |
static void setup_string_in_str(struct in_str *i, const char *s); |
|
/* close_me manipulations: */ |
|
|
static void mark_open(int fd); |
|
|
static void mark_closed(int fd); |
|
|
static void close_all(void); |
|
534 |
/* "run" the final data structures: */ |
/* "run" the final data structures: */ |
535 |
|
#if !defined(DEBUG_CLEAN) |
536 |
|
#define free_pipe_list(head, indent) free_pipe_list(head) |
537 |
|
#define free_pipe(pi, indent) free_pipe(pi) |
538 |
|
#endif |
539 |
static int free_pipe_list(struct pipe *head, int indent); |
static int free_pipe_list(struct pipe *head, int indent); |
540 |
static int free_pipe(struct pipe *pi, int indent); |
static int free_pipe(struct pipe *pi, int indent); |
541 |
/* really run the final data structures: */ |
/* really run the final data structures: */ |
542 |
static int setup_redirects(struct child_prog *prog, int squirrel[]); |
typedef struct nommu_save_t { |
543 |
static int run_list_real(struct pipe *pi); |
char **new_env; |
544 |
static void pseudo_exec(struct child_prog *child) ATTRIBUTE_NORETURN; |
char **old_env; |
545 |
static int run_pipe_real(struct pipe *pi); |
char **argv; |
546 |
/* extended glob support: */ |
} nommu_save_t; |
547 |
static int globhack(const char *src, int flags, glob_t *pglob); |
#if BB_MMU |
548 |
static int glob_needed(const char *s); |
#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ |
549 |
static int xglob(o_string *dest, int flags, glob_t *pglob); |
pseudo_exec_argv(argv, assignment_cnt, argv_expanded) |
550 |
/* variable assignment: */ |
#define pseudo_exec(nommu_save, command, argv_expanded) \ |
551 |
static int is_assignment(const char *s); |
pseudo_exec(command, argv_expanded) |
552 |
|
#endif |
553 |
|
static void pseudo_exec_argv(nommu_save_t *nommu_save, char **argv, int assignment_cnt, char **argv_expanded) NORETURN; |
554 |
|
static void pseudo_exec(nommu_save_t *nommu_save, struct command *command, char **argv_expanded) NORETURN; |
555 |
|
static int setup_redirects(struct command *prog, int squirrel[]); |
556 |
|
static int run_list(struct pipe *pi); |
557 |
|
static int run_pipe(struct pipe *pi); |
558 |
/* data structure manipulation: */ |
/* data structure manipulation: */ |
559 |
static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input); |
static int setup_redirect(struct parse_context *ctx, int fd, redir_type style, struct in_str *input); |
560 |
static void initialize_context(struct p_context *ctx); |
static void initialize_context(struct parse_context *ctx); |
561 |
static int done_word(o_string *dest, struct p_context *ctx); |
static int done_word(o_string *dest, struct parse_context *ctx); |
562 |
static int done_command(struct p_context *ctx); |
static int done_command(struct parse_context *ctx); |
563 |
static int done_pipe(struct p_context *ctx, pipe_style type); |
static void done_pipe(struct parse_context *ctx, pipe_style type); |
564 |
/* primary string parsing: */ |
/* primary string parsing: */ |
565 |
static int redirect_dup_num(struct in_str *input); |
static int redirect_dup_num(struct in_str *input); |
566 |
static int redirect_opt_num(o_string *o); |
static int redirect_opt_num(o_string *o); |
567 |
static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end); |
#if ENABLE_HUSH_TICK |
568 |
static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch); |
static int process_command_subs(o_string *dest, |
569 |
static char *lookup_param(char *src); |
struct in_str *input, const char *subst_end); |
570 |
static char *make_string(char **inp); |
#endif |
571 |
static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input); |
static int parse_group(o_string *dest, struct parse_context *ctx, struct in_str *input, int ch); |
572 |
static int parse_string(o_string *dest, struct p_context *ctx, const char *src); |
static const char *lookup_param(const char *src); |
573 |
static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, int end_trigger); |
static int handle_dollar(o_string *dest, |
574 |
|
struct in_str *input); |
575 |
|
static int parse_stream(o_string *dest, struct parse_context *ctx, struct in_str *input0, const char *end_trigger); |
576 |
/* setup: */ |
/* setup: */ |
577 |
static int parse_stream_outer(struct in_str *inp, int flag); |
static int parse_and_run_stream(struct in_str *inp, int parse_flag); |
578 |
static int parse_string_outer(const char *s, int flag); |
static int parse_and_run_string(const char *s, int parse_flag); |
579 |
static int parse_file_outer(FILE *f); |
static int parse_and_run_file(FILE *f); |
580 |
/* job management: */ |
/* job management: */ |
581 |
static int checkjobs(struct pipe* fg_pipe); |
static int checkjobs(struct pipe* fg_pipe); |
582 |
|
#if ENABLE_HUSH_JOB |
583 |
|
static int checkjobs_and_fg_shell(struct pipe* fg_pipe); |
584 |
static void insert_bg_job(struct pipe *pi); |
static void insert_bg_job(struct pipe *pi); |
585 |
static void remove_bg_job(struct pipe *pi); |
static void remove_bg_job(struct pipe *pi); |
586 |
|
static void delete_finished_bg_job(struct pipe *pi); |
587 |
|
#else |
588 |
|
int checkjobs_and_fg_shell(struct pipe* fg_pipe); /* never called */ |
589 |
|
#endif |
590 |
/* local variable support */ |
/* local variable support */ |
591 |
static char **make_list_in(char **inp, char *name); |
static char **expand_strvec_to_strvec(char **argv); |
592 |
static char *insert_var_value(char *inp); |
/* used for eval */ |
593 |
static char *get_local_var(const char *var); |
static char *expand_strvec_to_string(char **argv); |
594 |
static void unset_local_var(const char *name); |
/* used for expansion of right hand of assignments */ |
595 |
static int set_local_var(const char *s, int flg_export); |
static char *expand_string_to_string(const char *str); |
596 |
|
static struct variable *get_local_var(const char *name); |
597 |
|
static int set_local_var(char *str, int flg_export); |
598 |
|
static void unset_local_var(const char *name); |
599 |
|
|
|
/* Table of built-in functions. They can be forked or not, depending on |
|
|
* context: within pipes, they fork. As simple commands, they do not. |
|
|
* When used in non-forking context, they can change global variables |
|
|
* in the parent shell process. If forked, of course they cannot. |
|
|
* For example, 'unset foo | whatever' will parse and run, but foo will |
|
|
* still be set at the end. */ |
|
|
static const struct built_in_command bltins[] = { |
|
|
{"bg", "Resume a job in the background", builtin_fg_bg}, |
|
|
{"break", "Exit for, while or until loop", builtin_not_written}, |
|
|
{"cd", "Change working directory", builtin_cd}, |
|
|
{"continue", "Continue for, while or until loop", builtin_not_written}, |
|
|
{"env", "Print all environment variables", builtin_env}, |
|
|
{"eval", "Construct and run shell command", builtin_eval}, |
|
|
{"exec", "Exec command, replacing this shell with the exec'd process", |
|
|
builtin_exec}, |
|
|
{"exit", "Exit from shell()", builtin_exit}, |
|
|
{"export", "Set environment variable", builtin_export}, |
|
|
{"fg", "Bring job into the foreground", builtin_fg_bg}, |
|
|
{"jobs", "Lists the active jobs", builtin_jobs}, |
|
|
{"pwd", "Print current directory", builtin_pwd}, |
|
|
{"read", "Input environment variable", builtin_read}, |
|
|
{"return", "Return from a function", builtin_not_written}, |
|
|
{"set", "Set/unset shell local variables", builtin_set}, |
|
|
{"shift", "Shift positional parameters", builtin_shift}, |
|
|
{"trap", "Trap signals", builtin_not_written}, |
|
|
{"ulimit","Controls resource limits", builtin_not_written}, |
|
|
{"umask","Sets file creation mask", builtin_umask}, |
|
|
{"unset", "Unset environment variable", builtin_unset}, |
|
|
{".", "Source-in and run commands in a file", builtin_source}, |
|
|
{"help", "List shell built-in commands", builtin_help}, |
|
|
{NULL, NULL, NULL} |
|
|
}; |
|
600 |
|
|
601 |
static const char *set_cwd(void) |
static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="HUSH_VER_STR; |
|
{ |
|
|
if(cwd==bb_msg_unknown) |
|
|
cwd = NULL; /* xgetcwd(arg) called free(arg) */ |
|
|
cwd = xgetcwd((char *)cwd); |
|
|
if (!cwd) |
|
|
cwd = bb_msg_unknown; |
|
|
return cwd; |
|
|
} |
|
602 |
|
|
|
/* built-in 'eval' handler */ |
|
|
static int builtin_eval(struct child_prog *child) |
|
|
{ |
|
|
char *str = NULL; |
|
|
int rcode = EXIT_SUCCESS; |
|
603 |
|
|
604 |
if (child->argv[1]) { |
static int glob_needed(const char *s) |
605 |
str = make_string(child->argv + 1); |
{ |
606 |
parse_string_outer(str, FLAG_EXIT_FROM_LOOP | |
while (*s) { |
607 |
FLAG_PARSE_SEMICOLON); |
if (*s == '\\') |
608 |
free(str); |
s++; |
609 |
rcode = last_return_code; |
if (*s == '*' || *s == '[' || *s == '?') |
610 |
|
return 1; |
611 |
|
s++; |
612 |
} |
} |
613 |
return rcode; |
return 0; |
614 |
} |
} |
615 |
|
|
616 |
/* built-in 'cd <path>' handler */ |
static int is_assignment(const char *s) |
|
static int builtin_cd(struct child_prog *child) |
|
617 |
{ |
{ |
618 |
char *newdir; |
if (!s || !(isalpha(*s) || *s == '_')) |
619 |
if (child->argv[1] == NULL) |
return 0; |
620 |
newdir = getenv("HOME"); |
s++; |
621 |
else |
while (isalnum(*s) || *s == '_') |
622 |
newdir = child->argv[1]; |
s++; |
623 |
if (chdir(newdir)) { |
return *s == '='; |
|
printf("cd: %s: %s\n", newdir, strerror(errno)); |
|
|
return EXIT_FAILURE; |
|
|
} |
|
|
set_cwd(); |
|
|
return EXIT_SUCCESS; |
|
624 |
} |
} |
625 |
|
|
626 |
/* built-in 'env' handler */ |
/* Replace each \x with x in place, return ptr past NUL. */ |
627 |
static int builtin_env(struct child_prog *dummy ATTRIBUTE_UNUSED) |
static char *unbackslash(char *src) |
628 |
{ |
{ |
629 |
char **e = environ; |
char *dst = src; |
630 |
if (e == NULL) return EXIT_FAILURE; |
while (1) { |
631 |
for (; *e; e++) { |
if (*src == '\\') |
632 |
puts(*e); |
src++; |
633 |
|
if ((*dst++ = *src++) == '\0') |
634 |
|
break; |
635 |
} |
} |
636 |
return EXIT_SUCCESS; |
return dst; |
637 |
} |
} |
638 |
|
|
639 |
/* built-in 'exec' handler */ |
static char **add_strings_to_strings(char **strings, char **add, int need_to_dup) |
|
static int builtin_exec(struct child_prog *child) |
|
640 |
{ |
{ |
641 |
if (child->argv[1] == NULL) |
int i; |
642 |
return EXIT_SUCCESS; /* Really? */ |
unsigned count1; |
643 |
child->argv++; |
unsigned count2; |
644 |
pseudo_exec(child); |
char **v; |
645 |
/* never returns */ |
|
646 |
|
v = strings; |
647 |
|
count1 = 0; |
648 |
|
if (v) { |
649 |
|
while (*v) { |
650 |
|
count1++; |
651 |
|
v++; |
652 |
|
} |
653 |
|
} |
654 |
|
count2 = 0; |
655 |
|
v = add; |
656 |
|
while (*v) { |
657 |
|
count2++; |
658 |
|
v++; |
659 |
|
} |
660 |
|
v = xrealloc(strings, (count1 + count2 + 1) * sizeof(char*)); |
661 |
|
v[count1 + count2] = NULL; |
662 |
|
i = count2; |
663 |
|
while (--i >= 0) |
664 |
|
v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]); |
665 |
|
return v; |
666 |
} |
} |
667 |
|
|
668 |
/* built-in 'exit' handler */ |
static char **add_string_to_strings(char **strings, char *add) |
|
static int builtin_exit(struct child_prog *child) |
|
669 |
{ |
{ |
670 |
if (child->argv[1] == NULL) |
char *v[2]; |
671 |
exit(last_return_code); |
v[0] = add; |
672 |
exit (atoi(child->argv[1])); |
v[1] = NULL; |
673 |
|
return add_strings_to_strings(strings, v, /*dup:*/ 0); |
674 |
} |
} |
675 |
|
|
676 |
/* built-in 'export VAR=value' handler */ |
static void putenv_all(char **strings) |
|
static int builtin_export(struct child_prog *child) |
|
677 |
{ |
{ |
678 |
int res = 0; |
if (!strings) |
679 |
char *name = child->argv[1]; |
return; |
680 |
|
while (*strings) { |
681 |
if (name == NULL) { |
debug_printf_env("putenv '%s'\n", *strings); |
682 |
return builtin_env(child); |
putenv(*strings++); |
683 |
} |
} |
684 |
|
} |
685 |
|
|
686 |
name = strdup(name); |
static char **putenv_all_and_save_old(char **strings) |
687 |
|
{ |
688 |
if(name) { |
char **old = NULL; |
689 |
char *value = strchr(name, '='); |
char **s = strings; |
|
|
|
|
if (!value) { |
|
|
char *tmp; |
|
|
/* They are exporting something without an =VALUE */ |
|
690 |
|
|
691 |
value = get_local_var(name); |
if (!strings) |
692 |
if (value) { |
return old; |
693 |
size_t ln = strlen(name); |
while (*strings) { |
694 |
|
char *v, *eq; |
695 |
|
|
696 |
tmp = realloc(name, ln+strlen(value)+2); |
eq = strchr(*strings, '='); |
697 |
if(tmp==NULL) |
if (eq) { |
698 |
res = -1; |
*eq = '\0'; |
699 |
else { |
v = getenv(*strings); |
700 |
sprintf(tmp+ln, "=%s", value); |
*eq = '='; |
701 |
name = tmp; |
if (v) { |
702 |
} |
/* v points to VAL in VAR=VAL, go back to VAR */ |
703 |
} else { |
v -= (eq - *strings) + 1; |
704 |
/* bash does not return an error when trying to export |
old = add_string_to_strings(old, v); |
|
* an undefined variable. Do likewise. */ |
|
|
res = 1; |
|
705 |
} |
} |
706 |
} |
} |
707 |
|
strings++; |
708 |
} |
} |
709 |
if (res<0) |
putenv_all(s); |
710 |
bb_perror_msg("export"); |
return old; |
|
else if(res==0) |
|
|
res = set_local_var(name, 1); |
|
|
else |
|
|
res = 0; |
|
|
free(name); |
|
|
return res; |
|
711 |
} |
} |
712 |
|
|
713 |
/* built-in 'fg' and 'bg' handler */ |
static void free_strings_and_unsetenv(char **strings, int unset) |
|
static int builtin_fg_bg(struct child_prog *child) |
|
714 |
{ |
{ |
715 |
int i, jobnum; |
char **v; |
|
struct pipe *pi=NULL; |
|
716 |
|
|
717 |
if (!interactive) |
if (!strings) |
718 |
return EXIT_FAILURE; |
return; |
719 |
/* If they gave us no args, assume they want the last backgrounded task */ |
|
720 |
if (!child->argv[1]) { |
v = strings; |
721 |
for (pi = job_list; pi; pi = pi->next) { |
while (*v) { |
722 |
if (pi->jobid == last_jobid) { |
if (unset) { |
723 |
break; |
char *copy; |
724 |
} |
/* *strchrnul(*v, '=') = '\0'; -- BAD |
725 |
} |
* In case *v was putenv'ed, we can't |
726 |
if (!pi) { |
* unsetenv(*v) after taking out '=': |
727 |
bb_error_msg("%s: no current job", child->argv[0]); |
* it won't work, env is modified by taking out! |
728 |
return EXIT_FAILURE; |
* horror :( */ |
729 |
} |
copy = xstrndup(*v, strchrnul(*v, '=') - *v); |
730 |
} else { |
debug_printf_env("unsetenv '%s'\n", copy); |
731 |
if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) { |
unsetenv(copy); |
732 |
bb_error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]); |
free(copy); |
|
return EXIT_FAILURE; |
|
|
} |
|
|
for (pi = job_list; pi; pi = pi->next) { |
|
|
if (pi->jobid == jobnum) { |
|
|
break; |
|
|
} |
|
|
} |
|
|
if (!pi) { |
|
|
bb_error_msg("%s: %d: no such job", child->argv[0], jobnum); |
|
|
return EXIT_FAILURE; |
|
733 |
} |
} |
734 |
|
free(*v++); |
735 |
} |
} |
736 |
|
free(strings); |
737 |
|
} |
738 |
|
|
739 |
if (*child->argv[0] == 'f') { |
static void free_strings(char **strings) |
740 |
/* Put the job into the foreground. */ |
{ |
741 |
tcsetpgrp(shell_terminal, pi->pgrp); |
free_strings_and_unsetenv(strings, 0); |
742 |
} |
} |
743 |
|
|
|
/* Restart the processes in the job */ |
|
|
for (i = 0; i < pi->num_progs; i++) |
|
|
pi->progs[i].is_stopped = 0; |
|
744 |
|
|
745 |
if ( (i=kill(- pi->pgrp, SIGCONT)) < 0) { |
/* Function prototypes for builtins */ |
746 |
if (i == ESRCH) { |
static int builtin_cd(char **argv); |
747 |
remove_bg_job(pi); |
static int builtin_echo(char **argv); |
748 |
} else { |
static int builtin_eval(char **argv); |
749 |
bb_perror_msg("kill (SIGCONT)"); |
static int builtin_exec(char **argv); |
750 |
} |
static int builtin_exit(char **argv); |
751 |
} |
static int builtin_export(char **argv); |
752 |
|
#if ENABLE_HUSH_JOB |
753 |
|
static int builtin_fg_bg(char **argv); |
754 |
|
static int builtin_jobs(char **argv); |
755 |
|
#endif |
756 |
|
#if ENABLE_HUSH_HELP |
757 |
|
static int builtin_help(char **argv); |
758 |
|
#endif |
759 |
|
static int builtin_pwd(char **argv); |
760 |
|
static int builtin_read(char **argv); |
761 |
|
static int builtin_test(char **argv); |
762 |
|
static int builtin_true(char **argv); |
763 |
|
static int builtin_set(char **argv); |
764 |
|
static int builtin_shift(char **argv); |
765 |
|
static int builtin_source(char **argv); |
766 |
|
static int builtin_umask(char **argv); |
767 |
|
static int builtin_unset(char **argv); |
768 |
|
#if ENABLE_HUSH_LOOPS |
769 |
|
static int builtin_break(char **argv); |
770 |
|
static int builtin_continue(char **argv); |
771 |
|
#endif |
772 |
|
//static int builtin_not_written(char **argv); |
773 |
|
|
774 |
pi->stopped_progs = 0; |
/* Table of built-in functions. They can be forked or not, depending on |
775 |
return EXIT_SUCCESS; |
* context: within pipes, they fork. As simple commands, they do not. |
776 |
} |
* When used in non-forking context, they can change global variables |
777 |
|
* in the parent shell process. If forked, of course they cannot. |
778 |
|
* For example, 'unset foo | whatever' will parse and run, but foo will |
779 |
|
* still be set at the end. */ |
780 |
|
struct built_in_command { |
781 |
|
const char *cmd; |
782 |
|
int (*function)(char **argv); |
783 |
|
#if ENABLE_HUSH_HELP |
784 |
|
const char *descr; |
785 |
|
#define BLTIN(cmd, func, help) { cmd, func, help } |
786 |
|
#else |
787 |
|
#define BLTIN(cmd, func, help) { cmd, func } |
788 |
|
#endif |
789 |
|
}; |
790 |
|
|
791 |
|
/* For now, echo and test are unconditionally enabled. |
792 |
|
* Maybe make it configurable? */ |
793 |
|
static const struct built_in_command bltins[] = { |
794 |
|
BLTIN("." , builtin_source, "Run commands in a file"), |
795 |
|
BLTIN(":" , builtin_true, "No-op"), |
796 |
|
BLTIN("[" , builtin_test, "Test condition"), |
797 |
|
BLTIN("[[" , builtin_test, "Test condition"), |
798 |
|
#if ENABLE_HUSH_JOB |
799 |
|
BLTIN("bg" , builtin_fg_bg, "Resume a job in the background"), |
800 |
|
#endif |
801 |
|
#if ENABLE_HUSH_LOOPS |
802 |
|
BLTIN("break" , builtin_break, "Exit from a loop"), |
803 |
|
#endif |
804 |
|
BLTIN("cd" , builtin_cd, "Change directory"), |
805 |
|
#if ENABLE_HUSH_LOOPS |
806 |
|
BLTIN("continue", builtin_continue, "Start new loop iteration"), |
807 |
|
#endif |
808 |
|
BLTIN("echo" , builtin_echo, "Write to stdout"), |
809 |
|
BLTIN("eval" , builtin_eval, "Construct and run shell command"), |
810 |
|
BLTIN("exec" , builtin_exec, "Execute command, don't return to shell"), |
811 |
|
BLTIN("exit" , builtin_exit, "Exit"), |
812 |
|
BLTIN("export", builtin_export, "Set environment variable"), |
813 |
|
#if ENABLE_HUSH_JOB |
814 |
|
BLTIN("fg" , builtin_fg_bg, "Bring job into the foreground"), |
815 |
|
BLTIN("jobs" , builtin_jobs, "List active jobs"), |
816 |
|
#endif |
817 |
|
BLTIN("pwd" , builtin_pwd, "Print current directory"), |
818 |
|
BLTIN("read" , builtin_read, "Input environment variable"), |
819 |
|
// BLTIN("return", builtin_not_written, "Return from a function"), |
820 |
|
BLTIN("set" , builtin_set, "Set/unset shell local variables"), |
821 |
|
BLTIN("shift" , builtin_shift, "Shift positional parameters"), |
822 |
|
// BLTIN("trap" , builtin_not_written, "Trap signals"), |
823 |
|
BLTIN("test" , builtin_test, "Test condition"), |
824 |
|
// BLTIN("ulimit", builtin_not_written, "Control resource limits"), |
825 |
|
BLTIN("umask" , builtin_umask, "Set file creation mask"), |
826 |
|
BLTIN("unset" , builtin_unset, "Unset environment variable"), |
827 |
|
#if ENABLE_HUSH_HELP |
828 |
|
BLTIN("help" , builtin_help, "List shell built-in commands"), |
829 |
|
#endif |
830 |
|
}; |
831 |
|
|
832 |
/* built-in 'help' handler */ |
|
833 |
static int builtin_help(struct child_prog *dummy ATTRIBUTE_UNUSED) |
/* Signals are grouped, we handle them in batches */ |
834 |
|
static void set_misc_sighandler(void (*handler)(int)) |
835 |
{ |
{ |
836 |
const struct built_in_command *x; |
bb_signals(0 |
837 |
|
+ (1 << SIGINT) |
838 |
|
+ (1 << SIGQUIT) |
839 |
|
+ (1 << SIGTERM) |
840 |
|
, handler); |
841 |
|
} |
842 |
|
|
843 |
|
#if ENABLE_HUSH_JOB |
844 |
|
|
845 |
|
static void set_fatal_sighandler(void (*handler)(int)) |
846 |
|
{ |
847 |
|
bb_signals(0 |
848 |
|
+ (1 << SIGILL) |
849 |
|
+ (1 << SIGTRAP) |
850 |
|
+ (1 << SIGABRT) |
851 |
|
+ (1 << SIGFPE) |
852 |
|
+ (1 << SIGBUS) |
853 |
|
+ (1 << SIGSEGV) |
854 |
|
/* bash 3.2 seems to handle these just like 'fatal' ones */ |
855 |
|
+ (1 << SIGHUP) |
856 |
|
+ (1 << SIGPIPE) |
857 |
|
+ (1 << SIGALRM) |
858 |
|
, handler); |
859 |
|
} |
860 |
|
static void set_jobctrl_sighandler(void (*handler)(int)) |
861 |
|
{ |
862 |
|
bb_signals(0 |
863 |
|
+ (1 << SIGTSTP) |
864 |
|
+ (1 << SIGTTIN) |
865 |
|
+ (1 << SIGTTOU) |
866 |
|
, handler); |
867 |
|
} |
868 |
|
/* SIGCHLD is special and handled separately */ |
869 |
|
|
870 |
|
static void set_every_sighandler(void (*handler)(int)) |
871 |
|
{ |
872 |
|
set_fatal_sighandler(handler); |
873 |
|
set_jobctrl_sighandler(handler); |
874 |
|
set_misc_sighandler(handler); |
875 |
|
signal(SIGCHLD, handler); |
876 |
|
} |
877 |
|
|
878 |
|
static void handler_ctrl_c(int sig UNUSED_PARAM) |
879 |
|
{ |
880 |
|
debug_printf_jobs("got sig %d\n", sig); |
881 |
|
// as usual we can have all kinds of nasty problems with leaked malloc data here |
882 |
|
siglongjmp(G.toplevel_jb, 1); |
883 |
|
} |
884 |
|
|
885 |
|
static void handler_ctrl_z(int sig UNUSED_PARAM) |
886 |
|
{ |
887 |
|
pid_t pid; |
888 |
|
|
889 |
|
debug_printf_jobs("got tty sig %d in pid %d\n", sig, getpid()); |
890 |
|
pid = fork(); |
891 |
|
if (pid < 0) /* can't fork. Pretend there was no ctrl-Z */ |
892 |
|
return; |
893 |
|
G.ctrl_z_flag = 1; |
894 |
|
if (!pid) { /* child */ |
895 |
|
if (ENABLE_HUSH_JOB) |
896 |
|
die_sleep = 0; /* let nofork's xfuncs die */ |
897 |
|
bb_setpgrp(); |
898 |
|
debug_printf_jobs("set pgrp for child %d ok\n", getpid()); |
899 |
|
set_every_sighandler(SIG_DFL); |
900 |
|
raise(SIGTSTP); /* resend TSTP so that child will be stopped */ |
901 |
|
debug_printf_jobs("returning in child\n"); |
902 |
|
/* return to nofork, it will eventually exit now, |
903 |
|
* not return back to shell */ |
904 |
|
return; |
905 |
|
} |
906 |
|
/* parent */ |
907 |
|
/* finish filling up pipe info */ |
908 |
|
G.toplevel_list->pgrp = pid; /* child is in its own pgrp */ |
909 |
|
G.toplevel_list->cmds[0].pid = pid; |
910 |
|
/* parent needs to longjmp out of running nofork. |
911 |
|
* we will "return" exitcode 0, with child put in background */ |
912 |
|
// as usual we can have all kinds of nasty problems with leaked malloc data here |
913 |
|
debug_printf_jobs("siglongjmp in parent\n"); |
914 |
|
siglongjmp(G.toplevel_jb, 1); |
915 |
|
} |
916 |
|
|
917 |
|
/* Restores tty foreground process group, and exits. |
918 |
|
* May be called as signal handler for fatal signal |
919 |
|
* (will faithfully resend signal to itself, producing correct exit state) |
920 |
|
* or called directly with -EXITCODE. |
921 |
|
* We also call it if xfunc is exiting. */ |
922 |
|
static void sigexit(int sig) NORETURN; |
923 |
|
static void sigexit(int sig) |
924 |
|
{ |
925 |
|
/* Disable all signals: job control, SIGPIPE, etc. */ |
926 |
|
sigprocmask_allsigs(SIG_BLOCK); |
927 |
|
|
928 |
|
#if ENABLE_HUSH_INTERACTIVE |
929 |
|
if (G.interactive_fd) |
930 |
|
tcsetpgrp(G.interactive_fd, G.saved_tty_pgrp); |
931 |
|
#endif |
932 |
|
|
933 |
printf("\nBuilt-in commands:\n"); |
/* Not a signal, just exit */ |
934 |
printf("-------------------\n"); |
if (sig <= 0) |
935 |
for (x = bltins; x->cmd; x++) { |
_exit(- sig); |
936 |
if (x->descr==NULL) |
|
937 |
continue; |
kill_myself_with_sig(sig); /* does not return */ |
|
printf("%s\t%s\n", x->cmd, x->descr); |
|
|
} |
|
|
printf("\n\n"); |
|
|
return EXIT_SUCCESS; |
|
938 |
} |
} |
939 |
|
|
940 |
/* built-in 'jobs' handler */ |
/* Restores tty foreground process group, and exits. */ |
941 |
static int builtin_jobs(struct child_prog *child ATTRIBUTE_UNUSED) |
static void hush_exit(int exitcode) NORETURN; |
942 |
|
static void hush_exit(int exitcode) |
943 |
{ |
{ |
944 |
struct pipe *job; |
fflush(NULL); /* flush all streams */ |
945 |
char *status_string; |
sigexit(- (exitcode & 0xff)); |
946 |
|
} |
947 |
|
|
948 |
for (job = job_list; job; job = job->next) { |
#else /* !JOB */ |
|
if (job->running_progs == job->stopped_progs) |
|
|
status_string = "Stopped"; |
|
|
else |
|
|
status_string = "Running"; |
|
949 |
|
|
950 |
printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text); |
#define set_fatal_sighandler(handler) ((void)0) |
951 |
} |
#define set_jobctrl_sighandler(handler) ((void)0) |
952 |
return EXIT_SUCCESS; |
#define hush_exit(e) exit(e) |
953 |
|
|
954 |
|
#endif /* JOB */ |
955 |
|
|
956 |
|
|
957 |
|
static const char *set_cwd(void) |
958 |
|
{ |
959 |
|
if (G.cwd == bb_msg_unknown) |
960 |
|
G.cwd = NULL; /* xrealloc_getcwd_or_warn(arg) calls free(arg)! */ |
961 |
|
G.cwd = xrealloc_getcwd_or_warn((char *)G.cwd); |
962 |
|
if (!G.cwd) |
963 |
|
G.cwd = bb_msg_unknown; |
964 |
|
return G.cwd; |
965 |
} |
} |
966 |
|
|
967 |
|
|
968 |
/* built-in 'pwd' handler */ |
/* |
969 |
static int builtin_pwd(struct child_prog *dummy ATTRIBUTE_UNUSED) |
* o_string support |
970 |
|
*/ |
971 |
|
#define B_CHUNK (32 * sizeof(char*)) |
972 |
|
|
973 |
|
static void o_reset(o_string *o) |
974 |
{ |
{ |
975 |
puts(set_cwd()); |
o->length = 0; |
976 |
return EXIT_SUCCESS; |
o->nonnull = 0; |
977 |
|
if (o->data) |
978 |
|
o->data[0] = '\0'; |
979 |
} |
} |
980 |
|
|
981 |
/* built-in 'read VAR' handler */ |
static void o_free(o_string *o) |
|
static int builtin_read(struct child_prog *child) |
|
982 |
{ |
{ |
983 |
int res; |
free(o->data); |
984 |
|
memset(o, 0, sizeof(*o)); |
985 |
|
} |
986 |
|
|
987 |
if (child->argv[1]) { |
static void o_grow_by(o_string *o, int len) |
988 |
char string[BUFSIZ]; |
{ |
989 |
char *var = 0; |
if (o->length + len > o->maxlen) { |
990 |
|
o->maxlen += (2*len > B_CHUNK ? 2*len : B_CHUNK); |
991 |
string[0] = 0; /* In case stdin has only EOF */ |
o->data = xrealloc(o->data, 1 + o->maxlen); |
|
/* read string */ |
|
|
fgets(string, sizeof(string), stdin); |
|
|
chomp(string); |
|
|
var = malloc(strlen(child->argv[1])+strlen(string)+2); |
|
|
if(var) { |
|
|
sprintf(var, "%s=%s", child->argv[1], string); |
|
|
res = set_local_var(var, 0); |
|
|
} else |
|
|
res = -1; |
|
|
if (res) |
|
|
bb_perror_msg("read"); |
|
|
free(var); /* So not move up to avoid breaking errno */ |
|
|
return res; |
|
|
} else { |
|
|
do res=getchar(); while(res!='\n' && res!=EOF); |
|
|
return 0; |
|
992 |
} |
} |
993 |
} |
} |
994 |
|
|
995 |
/* built-in 'set VAR=value' handler */ |
static void o_addchr(o_string *o, int ch) |
|
static int builtin_set(struct child_prog *child) |
|
996 |
{ |
{ |
997 |
char *temp = child->argv[1]; |
debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o); |
998 |
struct variables *e; |
o_grow_by(o, 1); |
999 |
|
o->data[o->length] = ch; |
1000 |
if (temp == NULL) |
o->length++; |
1001 |
for(e = top_vars; e; e=e->next) |
o->data[o->length] = '\0'; |
1002 |
printf("%s=%s\n", e->name, e->value); |
} |
|
else |
|
|
set_local_var(temp, 0); |
|
1003 |
|
|
1004 |
return EXIT_SUCCESS; |
static void o_addstr(o_string *o, const char *str, int len) |
1005 |
|
{ |
1006 |
|
o_grow_by(o, len); |
1007 |
|
memcpy(&o->data[o->length], str, len); |
1008 |
|
o->length += len; |
1009 |
|
o->data[o->length] = '\0'; |
1010 |
} |
} |
1011 |
|
|
1012 |
|
static void o_addstr_duplicate_backslash(o_string *o, const char *str, int len) |
1013 |
|
{ |
1014 |
|
while (len) { |
1015 |
|
o_addchr(o, *str); |
1016 |
|
if (*str++ == '\\' |
1017 |
|
&& (*str != '*' && *str != '?' && *str != '[') |
1018 |
|
) { |
1019 |
|
o_addchr(o, '\\'); |
1020 |
|
} |
1021 |
|
len--; |
1022 |
|
} |
1023 |
|
} |
1024 |
|
|
1025 |
/* Built-in 'shift' handler */ |
/* My analysis of quoting semantics tells me that state information |
1026 |
static int builtin_shift(struct child_prog *child) |
* is associated with a destination, not a source. |
1027 |
|
*/ |
1028 |
|
static void o_addqchr(o_string *o, int ch) |
1029 |
{ |
{ |
1030 |
int n=1; |
int sz = 1; |
1031 |
if (child->argv[1]) { |
char *found = strchr("*?[\\", ch); |
1032 |
n=atoi(child->argv[1]); |
if (found) |
1033 |
} |
sz++; |
1034 |
if (n>=0 && n<global_argc) { |
o_grow_by(o, sz); |
1035 |
/* XXX This probably breaks $0 */ |
if (found) { |
1036 |
global_argc -= n; |
o->data[o->length] = '\\'; |
1037 |
global_argv += n; |
o->length++; |
|
return EXIT_SUCCESS; |
|
|
} else { |
|
|
return EXIT_FAILURE; |
|
1038 |
} |
} |
1039 |
|
o->data[o->length] = ch; |
1040 |
|
o->length++; |
1041 |
|
o->data[o->length] = '\0'; |
1042 |
} |
} |
1043 |
|
|
1044 |
/* Built-in '.' handler (read-in and execute commands from file) */ |
static void o_addQchr(o_string *o, int ch) |
|
static int builtin_source(struct child_prog *child) |
|
1045 |
{ |
{ |
1046 |
FILE *input; |
int sz = 1; |
1047 |
int status; |
if (o->o_quote && strchr("*?[\\", ch)) { |
1048 |
|
sz++; |
1049 |
if (child->argv[1] == NULL) |
o->data[o->length] = '\\'; |
1050 |
return EXIT_FAILURE; |
o->length++; |
|
|
|
|
/* XXX search through $PATH is missing */ |
|
|
input = fopen(child->argv[1], "r"); |
|
|
if (!input) { |
|
|
bb_error_msg("cannot open '%s'", child->argv[1]); |
|
|
return EXIT_FAILURE; |
|
1051 |
} |
} |
1052 |
|
o_grow_by(o, sz); |
1053 |
/* Now run the file */ |
o->data[o->length] = ch; |
1054 |
/* XXX argv and argc are broken; need to save old global_argv |
o->length++; |
1055 |
* (pointer only is OK!) on this stack frame, |
o->data[o->length] = '\0'; |
|
* set global_argv=child->argv+1, recurse, and restore. */ |
|
|
mark_open(fileno(input)); |
|
|
status = parse_file_outer(input); |
|
|
mark_closed(fileno(input)); |
|
|
fclose(input); |
|
|
return status; |
|
1056 |
} |
} |
1057 |
|
|
1058 |
static int builtin_umask(struct child_prog *child) |
static void o_addQstr(o_string *o, const char *str, int len) |
1059 |
{ |
{ |
1060 |
mode_t new_umask; |
if (!o->o_quote) { |
1061 |
const char *arg = child->argv[1]; |
o_addstr(o, str, len); |
1062 |
char *end; |
return; |
1063 |
if (arg) { |
} |
1064 |
new_umask=strtoul(arg, &end, 8); |
while (len) { |
1065 |
if (*end!='\0' || end == arg) { |
char ch; |
1066 |
return EXIT_FAILURE; |
int sz; |
1067 |
} |
int ordinary_cnt = strcspn(str, "*?[\\"); |
1068 |
} else { |
if (ordinary_cnt > len) /* paranoia */ |
1069 |
printf("%.3o\n", (unsigned int) (new_umask=umask(0))); |
ordinary_cnt = len; |
1070 |
|
o_addstr(o, str, ordinary_cnt); |
1071 |
|
if (ordinary_cnt == len) |
1072 |
|
return; |
1073 |
|
str += ordinary_cnt; |
1074 |
|
len -= ordinary_cnt + 1; /* we are processing + 1 char below */ |
1075 |
|
|
1076 |
|
ch = *str++; |
1077 |
|
sz = 1; |
1078 |
|
if (ch) { /* it is necessarily one of "*?[\\" */ |
1079 |
|
sz++; |
1080 |
|
o->data[o->length] = '\\'; |
1081 |
|
o->length++; |
1082 |
|
} |
1083 |
|
o_grow_by(o, sz); |
1084 |
|
o->data[o->length] = ch; |
1085 |
|
o->length++; |
1086 |
|
o->data[o->length] = '\0'; |
1087 |
} |
} |
|
umask(new_umask); |
|
|
return EXIT_SUCCESS; |
|
1088 |
} |
} |
1089 |
|
|
1090 |
/* built-in 'unset VAR' handler */ |
/* A special kind of o_string for $VAR and `cmd` expansion. |
1091 |
static int builtin_unset(struct child_prog *child) |
* It contains char* list[] at the beginning, which is grown in 16 element |
1092 |
{ |
* increments. Actual string data starts at the next multiple of 16 * (char*). |
1093 |
/* bash returned already true */ |
* list[i] contains an INDEX (int!) into this string data. |
1094 |
unset_local_var(child->argv[1]); |
* It means that if list[] needs to grow, data needs to be moved higher up |
1095 |
return EXIT_SUCCESS; |
* but list[i]'s need not be modified. |
1096 |
} |
* NB: remembering how many list[i]'s you have there is crucial. |
1097 |
|
* o_finalize_list() operation post-processes this structure - calculates |
1098 |
static int builtin_not_written(struct child_prog *child) |
* and stores actual char* ptrs in list[]. Oh, it NULL terminates it as well. |
1099 |
|
*/ |
1100 |
|
#if DEBUG_EXPAND || DEBUG_GLOB |
1101 |
|
static void debug_print_list(const char *prefix, o_string *o, int n) |
1102 |
{ |
{ |
1103 |
printf("builtin_%s not written\n",child->argv[0]); |
char **list = (char**)o->data; |
1104 |
return EXIT_FAILURE; |
int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); |
1105 |
|
int i = 0; |
1106 |
|
fprintf(stderr, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d\n", |
1107 |
|
prefix, list, n, string_start, o->length, o->maxlen); |
1108 |
|
while (i < n) { |
1109 |
|
fprintf(stderr, " list[%d]=%d '%s' %p\n", i, (int)list[i], |
1110 |
|
o->data + (int)list[i] + string_start, |
1111 |
|
o->data + (int)list[i] + string_start); |
1112 |
|
i++; |
1113 |
|
} |
1114 |
|
if (n) { |
1115 |
|
const char *p = o->data + (int)list[n - 1] + string_start; |
1116 |
|
fprintf(stderr, " total_sz:%d\n", (p + strlen(p) + 1) - o->data); |
1117 |
|
} |
1118 |
} |
} |
1119 |
|
#else |
1120 |
|
#define debug_print_list(prefix, o, n) ((void)0) |
1121 |
|
#endif |
1122 |
|
|
1123 |
static int b_check_space(o_string *o, int len) |
/* n = o_save_ptr_helper(str, n) "starts new string" by storing an index value |
1124 |
{ |
* in list[n] so that it points past last stored byte so far. |
1125 |
/* It would be easy to drop a more restrictive policy |
* It returns n+1. */ |
1126 |
* in here, such as setting a maximum string length */ |
static int o_save_ptr_helper(o_string *o, int n) |
1127 |
if (o->length + len > o->maxlen) { |
{ |
1128 |
char *old_data = o->data; |
char **list = (char**)o->data; |
1129 |
/* assert (data == NULL || o->maxlen != 0); */ |
int string_start; |
1130 |
o->maxlen += max(2*len, B_CHUNK); |
int string_len; |
1131 |
o->data = realloc(o->data, 1 + o->maxlen); |
|
1132 |
if (o->data == NULL) { |
if (!o->has_empty_slot) { |
1133 |
free(old_data); |
string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); |
1134 |
} |
string_len = o->length - string_start; |
1135 |
|
if (!(n & 0xf)) { /* 0, 0x10, 0x20...? */ |
1136 |
|
debug_printf_list("list[%d]=%d string_start=%d (growing)\n", n, string_len, string_start); |
1137 |
|
/* list[n] points to string_start, make space for 16 more pointers */ |
1138 |
|
o->maxlen += 0x10 * sizeof(list[0]); |
1139 |
|
o->data = xrealloc(o->data, o->maxlen + 1); |
1140 |
|
list = (char**)o->data; |
1141 |
|
memmove(list + n + 0x10, list + n, string_len); |
1142 |
|
o->length += 0x10 * sizeof(list[0]); |
1143 |
|
} else |
1144 |
|
debug_printf_list("list[%d]=%d string_start=%d\n", n, string_len, string_start); |
1145 |
|
} else { |
1146 |
|
/* We have empty slot at list[n], reuse without growth */ |
1147 |
|
string_start = ((n+1 + 0xf) & ~0xf) * sizeof(list[0]); /* NB: n+1! */ |
1148 |
|
string_len = o->length - string_start; |
1149 |
|
debug_printf_list("list[%d]=%d string_start=%d (empty slot)\n", n, string_len, string_start); |
1150 |
|
o->has_empty_slot = 0; |
1151 |
} |
} |
1152 |
return o->data == NULL; |
list[n] = (char*)(ptrdiff_t)string_len; |
1153 |
|
return n + 1; |
1154 |
} |
} |
1155 |
|
|
1156 |
static int b_addchr(o_string *o, int ch) |
/* "What was our last o_save_ptr'ed position (byte offset relative o->data)?" */ |
1157 |
|
static int o_get_last_ptr(o_string *o, int n) |
1158 |
{ |
{ |
1159 |
debug_printf("b_addchr: %c %d %p\n", ch, o->length, o); |
char **list = (char**)o->data; |
1160 |
if (b_check_space(o, 1)) return B_NOSPAC; |
int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); |
|
o->data[o->length] = ch; |
|
|
o->length++; |
|
|
o->data[o->length] = '\0'; |
|
|
return 0; |
|
|
} |
|
1161 |
|
|
1162 |
static void b_reset(o_string *o) |
return ((int)(ptrdiff_t)list[n-1]) + string_start; |
|
{ |
|
|
o->length = 0; |
|
|
o->nonnull = 0; |
|
|
if (o->data != NULL) *o->data = '\0'; |
|
1163 |
} |
} |
1164 |
|
|
1165 |
static void b_free(o_string *o) |
/* o_glob performs globbing on last list[], saving each result |
1166 |
|
* as a new list[]. */ |
1167 |
|
static int o_glob(o_string *o, int n) |
1168 |
{ |
{ |
1169 |
b_reset(o); |
glob_t globdata; |
1170 |
free(o->data); |
int gr; |
1171 |
o->data = NULL; |
char *pattern; |
|
o->maxlen = 0; |
|
|
} |
|
1172 |
|
|
1173 |
/* My analysis of quoting semantics tells me that state information |
debug_printf_glob("start o_glob: n:%d o->data:%p\n", n, o->data); |
1174 |
* is associated with a destination, not a source. |
if (!o->data) |
1175 |
*/ |
return o_save_ptr_helper(o, n); |
1176 |
static int b_addqchr(o_string *o, int ch, int quote) |
pattern = o->data + o_get_last_ptr(o, n); |
1177 |
{ |
debug_printf_glob("glob pattern '%s'\n", pattern); |
1178 |
if (quote && strchr("*?[\\",ch)) { |
if (!glob_needed(pattern)) { |
1179 |
int rc; |
literal: |
1180 |
rc = b_addchr(o, '\\'); |
o->length = unbackslash(pattern) - o->data; |
1181 |
if (rc) return rc; |
debug_printf_glob("glob pattern '%s' is literal\n", pattern); |
1182 |
|
return o_save_ptr_helper(o, n); |
1183 |
|
} |
1184 |
|
|
1185 |
|
memset(&globdata, 0, sizeof(globdata)); |
1186 |
|
gr = glob(pattern, 0, NULL, &globdata); |
1187 |
|
debug_printf_glob("glob('%s'):%d\n", pattern, gr); |
1188 |
|
if (gr == GLOB_NOSPACE) |
1189 |
|
bb_error_msg_and_die("out of memory during glob"); |
1190 |
|
if (gr == GLOB_NOMATCH) { |
1191 |
|
globfree(&globdata); |
1192 |
|
goto literal; |
1193 |
|
} |
1194 |
|
if (gr != 0) { /* GLOB_ABORTED ? */ |
1195 |
|
//TODO: testcase for bad glob pattern behavior |
1196 |
|
bb_error_msg("glob(3) error %d on '%s'", gr, pattern); |
1197 |
|
} |
1198 |
|
if (globdata.gl_pathv && globdata.gl_pathv[0]) { |
1199 |
|
char **argv = globdata.gl_pathv; |
1200 |
|
o->length = pattern - o->data; /* "forget" pattern */ |
1201 |
|
while (1) { |
1202 |
|
o_addstr(o, *argv, strlen(*argv) + 1); |
1203 |
|
n = o_save_ptr_helper(o, n); |
1204 |
|
argv++; |
1205 |
|
if (!*argv) |
1206 |
|
break; |
1207 |
|
} |
1208 |
} |
} |
1209 |
return b_addchr(o, ch); |
globfree(&globdata); |
1210 |
|
if (DEBUG_GLOB) |
1211 |
|
debug_print_list("o_glob returning", o, n); |
1212 |
|
return n; |
1213 |
} |
} |
1214 |
|
|
1215 |
/* belongs in utility.c */ |
/* If o->o_glob == 1, glob the string so far remembered. |
1216 |
static char *simple_itoa(unsigned int i) |
* Otherwise, just finish current list[] and start new */ |
1217 |
|
static int o_save_ptr(o_string *o, int n) |
1218 |
{ |
{ |
1219 |
/* 21 digits plus null terminator, good for 64-bit or smaller ints */ |
if (o->o_glob) { /* if globbing is requested */ |
1220 |
static char local[22]; |
/* If o->has_empty_slot, list[n] was already globbed |
1221 |
char *p = &local[21]; |
* (if it was requested back then when it was filled) |
1222 |
*p-- = '\0'; |
* so don't do that again! */ |
1223 |
do { |
if (!o->has_empty_slot) |
1224 |
*p-- = '0' + i % 10; |
return o_glob(o, n); /* o_save_ptr_helper is inside */ |
1225 |
i /= 10; |
} |
1226 |
} while (i > 0); |
return o_save_ptr_helper(o, n); |
|
return p + 1; |
|
1227 |
} |
} |
1228 |
|
|
1229 |
static int b_adduint(o_string *o, unsigned int i) |
/* "Please convert list[n] to real char* ptrs, and NULL terminate it." */ |
1230 |
|
static char **o_finalize_list(o_string *o, int n) |
1231 |
{ |
{ |
1232 |
int r; |
char **list; |
1233 |
char *p = simple_itoa(i); |
int string_start; |
1234 |
/* no escape checking necessary */ |
|
1235 |
do r=b_addchr(o, *p++); while (r==0 && *p); |
n = o_save_ptr(o, n); /* force growth for list[n] if necessary */ |
1236 |
return r; |
if (DEBUG_EXPAND) |
1237 |
|
debug_print_list("finalized", o, n); |
1238 |
|
debug_printf_expand("finalized n:%d\n", n); |
1239 |
|
list = (char**)o->data; |
1240 |
|
string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); |
1241 |
|
list[--n] = NULL; |
1242 |
|
while (n) { |
1243 |
|
n--; |
1244 |
|
list[n] = o->data + (int)(ptrdiff_t)list[n] + string_start; |
1245 |
|
} |
1246 |
|
return list; |
1247 |
} |
} |
1248 |
|
|
1249 |
|
|
1250 |
|
/* |
1251 |
|
* in_str support |
1252 |
|
*/ |
1253 |
static int static_get(struct in_str *i) |
static int static_get(struct in_str *i) |
1254 |
{ |
{ |
1255 |
int ch=*i->p++; |
int ch = *i->p++; |
1256 |
if (ch=='\0') return EOF; |
if (ch == '\0') return EOF; |
1257 |
return ch; |
return ch; |
1258 |
} |
} |
1259 |
|
|
1262 |
return *i->p; |
return *i->p; |
1263 |
} |
} |
1264 |
|
|
1265 |
|
#if ENABLE_HUSH_INTERACTIVE |
1266 |
|
|
1267 |
|
#if ENABLE_FEATURE_EDITING |
1268 |
static void cmdedit_set_initial_prompt(void) |
static void cmdedit_set_initial_prompt(void) |
1269 |
{ |
{ |
1270 |
#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT |
#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT |
1271 |
PS1 = NULL; |
G.PS1 = NULL; |
1272 |
#else |
#else |
1273 |
PS1 = getenv("PS1"); |
G.PS1 = getenv("PS1"); |
1274 |
if(PS1==0) |
if (G.PS1 == NULL) |
1275 |
PS1 = "\\w \\$ "; |
G.PS1 = "\\w \\$ "; |
1276 |
#endif |
#endif |
1277 |
} |
} |
1278 |
|
#endif /* EDITING */ |
1279 |
|
|
1280 |
static void setup_prompt_string(int promptmode, char **prompt_str) |
static const char* setup_prompt_string(int promptmode) |
1281 |
{ |
{ |
1282 |
debug_printf("setup_prompt_string %d ",promptmode); |
const char *prompt_str; |
1283 |
#ifndef CONFIG_FEATURE_SH_FANCY_PROMPT |
debug_printf("setup_prompt_string %d ", promptmode); |
1284 |
|
#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT |
1285 |
/* Set up the prompt */ |
/* Set up the prompt */ |
1286 |
if (promptmode == 1) { |
if (promptmode == 0) { /* PS1 */ |
1287 |
free(PS1); |
free((char*)G.PS1); |
1288 |
PS1=xmalloc(strlen(cwd)+4); |
G.PS1 = xasprintf("%s %c ", G.cwd, (geteuid() != 0) ? '$' : '#'); |
1289 |
sprintf(PS1, "%s %s", cwd, ( geteuid() != 0 ) ? "$ ":"# "); |
prompt_str = G.PS1; |
|
*prompt_str = PS1; |
|
1290 |
} else { |
} else { |
1291 |
*prompt_str = PS2; |
prompt_str = G.PS2; |
1292 |
} |
} |
1293 |
#else |
#else |
1294 |
*prompt_str = (promptmode==1)? PS1 : PS2; |
prompt_str = (promptmode == 0) ? G.PS1 : G.PS2; |
1295 |
#endif |
#endif |
1296 |
debug_printf("result %s\n",*prompt_str); |
debug_printf("result '%s'\n", prompt_str); |
1297 |
|
return prompt_str; |
1298 |
} |
} |
1299 |
|
|
1300 |
static void get_user_input(struct in_str *i) |
static void get_user_input(struct in_str *i) |
1301 |
{ |
{ |
1302 |
char *prompt_str; |
int r; |
1303 |
static char the_command[BUFSIZ]; |
const char *prompt_str; |
1304 |
|
|
1305 |
setup_prompt_string(i->promptmode, &prompt_str); |
prompt_str = setup_prompt_string(i->promptmode); |
1306 |
#ifdef CONFIG_FEATURE_COMMAND_EDITING |
#if ENABLE_FEATURE_EDITING |
1307 |
/* |
/* Enable command line editing only while a command line |
1308 |
** enable command line editing only while a command line |
* is actually being read */ |
1309 |
** is actually being read; otherwise, we'll end up bequeathing |
do { |
1310 |
** atexit() handlers and other unwanted stuff to our |
r = read_line_input(prompt_str, G.user_input_buf, BUFSIZ-1, G.line_input_state); |
1311 |
** child processes (rob@sysgo.de) |
} while (r == 0); /* repeat if Ctrl-C */ |
1312 |
*/ |
i->eof_flag = (r < 0); |
1313 |
cmdedit_read_input(prompt_str, the_command); |
if (i->eof_flag) { /* EOF/error detected */ |
1314 |
|
G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */ |
1315 |
|
G.user_input_buf[1] = '\0'; |
1316 |
|
} |
1317 |
#else |
#else |
1318 |
fputs(prompt_str, stdout); |
fputs(prompt_str, stdout); |
1319 |
fflush(stdout); |
fflush(stdout); |
1320 |
the_command[0]=fgetc(i->file); |
G.user_input_buf[0] = r = fgetc(i->file); |
1321 |
the_command[1]='\0'; |
/*G.user_input_buf[1] = '\0'; - already is and never changed */ |
1322 |
|
i->eof_flag = (r == EOF); |
1323 |
#endif |
#endif |
1324 |
fflush(stdout); |
i->p = G.user_input_buf; |
|
i->p = the_command; |
|
1325 |
} |
} |
1326 |
|
|
1327 |
|
#endif /* INTERACTIVE */ |
1328 |
|
|
1329 |
/* This is the magic location that prints prompts |
/* This is the magic location that prints prompts |
1330 |
* and gets data back from the user */ |
* and gets data back from the user */ |
1331 |
static int file_get(struct in_str *i) |
static int file_get(struct in_str *i) |
1332 |
{ |
{ |
1333 |
int ch; |
int ch; |
1334 |
|
|
|
ch = 0; |
|
1335 |
/* If there is data waiting, eat it up */ |
/* If there is data waiting, eat it up */ |
1336 |
if (i->p && *i->p) { |
if (i->p && *i->p) { |
1337 |
ch=*i->p++; |
#if ENABLE_HUSH_INTERACTIVE |
1338 |
|
take_cached: |
1339 |
|
#endif |
1340 |
|
ch = *i->p++; |
1341 |
|
if (i->eof_flag && !*i->p) |
1342 |
|
ch = EOF; |
1343 |
} else { |
} else { |
1344 |
/* need to double check i->file because we might be doing something |
/* need to double check i->file because we might be doing something |
1345 |
* more complicated by now, like sourcing or substituting. */ |
* more complicated by now, like sourcing or substituting. */ |
1346 |
if (i->__promptme && interactive && i->file == stdin) { |
#if ENABLE_HUSH_INTERACTIVE |
1347 |
while(! i->p || (interactive && strlen(i->p)==0) ) { |
if (G.interactive_fd && i->promptme && i->file == stdin) { |
1348 |
|
do { |
1349 |
get_user_input(i); |
get_user_input(i); |
1350 |
} |
} while (!*i->p); /* need non-empty line */ |
1351 |
i->promptmode=2; |
i->promptmode = 1; /* PS2 */ |
1352 |
i->__promptme = 0; |
i->promptme = 0; |
1353 |
if (i->p && *i->p) { |
goto take_cached; |
|
ch=*i->p++; |
|
|
} |
|
|
} else { |
|
|
ch = fgetc(i->file); |
|
1354 |
} |
} |
1355 |
|
#endif |
1356 |
debug_printf("b_getch: got a %d\n", ch); |
ch = fgetc(i->file); |
1357 |
} |
} |
1358 |
if (ch == '\n') i->__promptme=1; |
debug_printf("file_get: got a '%c' %d\n", ch, ch); |
1359 |
|
#if ENABLE_HUSH_INTERACTIVE |
1360 |
|
if (ch == '\n') |
1361 |
|
i->promptme = 1; |
1362 |
|
#endif |
1363 |
return ch; |
return ch; |
1364 |
} |
} |
1365 |
|
|
1368 |
*/ |
*/ |
1369 |
static int file_peek(struct in_str *i) |
static int file_peek(struct in_str *i) |
1370 |
{ |
{ |
1371 |
|
int ch; |
1372 |
if (i->p && *i->p) { |
if (i->p && *i->p) { |
1373 |
return *i->p; |
if (i->eof_flag && !i->p[1]) |
1374 |
} else { |
return EOF; |
|
i->peek_buf[0] = fgetc(i->file); |
|
|
i->peek_buf[1] = '\0'; |
|
|
i->p = i->peek_buf; |
|
|
debug_printf("b_peek: got a %d\n", *i->p); |
|
1375 |
return *i->p; |
return *i->p; |
1376 |
} |
} |
1377 |
|
ch = fgetc(i->file); |
1378 |
|
i->eof_flag = (ch == EOF); |
1379 |
|
i->peek_buf[0] = ch; |
1380 |
|
i->peek_buf[1] = '\0'; |
1381 |
|
i->p = i->peek_buf; |
1382 |
|
debug_printf("file_peek: got a '%c' %d\n", *i->p, *i->p); |
1383 |
|
return ch; |
1384 |
} |
} |
1385 |
|
|
1386 |
static void setup_file_in_str(struct in_str *i, FILE *f) |
static void setup_file_in_str(struct in_str *i, FILE *f) |
1387 |
{ |
{ |
1388 |
i->peek = file_peek; |
i->peek = file_peek; |
1389 |
i->get = file_get; |
i->get = file_get; |
1390 |
i->__promptme=1; |
#if ENABLE_HUSH_INTERACTIVE |
1391 |
i->promptmode=1; |
i->promptme = 1; |
1392 |
|
i->promptmode = 0; /* PS1 */ |
1393 |
|
#endif |
1394 |
i->file = f; |
i->file = f; |
1395 |
i->p = NULL; |
i->p = NULL; |
1396 |
} |
} |
1399 |
{ |
{ |
1400 |
i->peek = static_peek; |
i->peek = static_peek; |
1401 |
i->get = static_get; |
i->get = static_get; |
1402 |
i->__promptme=1; |
#if ENABLE_HUSH_INTERACTIVE |
1403 |
i->promptmode=1; |
i->promptme = 1; |
1404 |
|
i->promptmode = 0; /* PS1 */ |
1405 |
|
#endif |
1406 |
i->p = s; |
i->p = s; |
1407 |
|
i->eof_flag = 0; |
1408 |
} |
} |
1409 |
|
|
|
static void mark_open(int fd) |
|
|
{ |
|
|
struct close_me *new = xmalloc(sizeof(struct close_me)); |
|
|
new->fd = fd; |
|
|
new->next = close_me_head; |
|
|
close_me_head = new; |
|
|
} |
|
|
|
|
|
static void mark_closed(int fd) |
|
|
{ |
|
|
struct close_me *tmp; |
|
|
if (close_me_head == NULL || close_me_head->fd != fd) |
|
|
bb_error_msg_and_die("corrupt close_me"); |
|
|
tmp = close_me_head; |
|
|
close_me_head = close_me_head->next; |
|
|
free(tmp); |
|
|
} |
|
|
|
|
|
static void close_all(void) |
|
|
{ |
|
|
struct close_me *c; |
|
|
for (c=close_me_head; c; c=c->next) { |
|
|
close(c->fd); |
|
|
} |
|
|
close_me_head = NULL; |
|
|
} |
|
1410 |
|
|
1411 |
/* squirrel != NULL means we squirrel away copies of stdin, stdout, |
/* squirrel != NULL means we squirrel away copies of stdin, stdout, |
1412 |
* and stderr if they are redirected. */ |
* and stderr if they are redirected. */ |
1413 |
static int setup_redirects(struct child_prog *prog, int squirrel[]) |
static int setup_redirects(struct command *prog, int squirrel[]) |
1414 |
{ |
{ |
1415 |
int openfd, mode; |
int openfd, mode; |
1416 |
struct redir_struct *redir; |
struct redir_struct *redir; |
1417 |
|
|
1418 |
for (redir=prog->redirects; redir; redir=redir->next) { |
for (redir = prog->redirects; redir; redir = redir->next) { |
1419 |
if (redir->dup == -1 && redir->word.gl_pathv == NULL) { |
if (redir->dup == -1 && redir->rd_filename == NULL) { |
1420 |
/* something went wrong in the parse. Pretend it didn't happen */ |
/* something went wrong in the parse. Pretend it didn't happen */ |
1421 |
continue; |
continue; |
1422 |
} |
} |
1423 |
if (redir->dup == -1) { |
if (redir->dup == -1) { |
1424 |
mode=redir_table[redir->type].mode; |
char *p; |
1425 |
openfd = open(redir->word.gl_pathv[0], mode, 0666); |
mode = redir_table[redir->rd_type].mode; |
1426 |
|
//TODO: check redir for names like '\\' |
1427 |
|
p = expand_string_to_string(redir->rd_filename); |
1428 |
|
openfd = open_or_warn(p, mode); |
1429 |
|
free(p); |
1430 |
if (openfd < 0) { |
if (openfd < 0) { |
1431 |
/* this could get lost if stderr has been redirected, but |
/* this could get lost if stderr has been redirected, but |
1432 |
bash and ash both lose it as well (though zsh doesn't!) */ |
bash and ash both lose it as well (though zsh doesn't!) */ |
|
bb_perror_msg("error opening %s", redir->word.gl_pathv[0]); |
|
1433 |
return 1; |
return 1; |
1434 |
} |
} |
1435 |
} else { |
} else { |
1441 |
squirrel[redir->fd] = dup(redir->fd); |
squirrel[redir->fd] = dup(redir->fd); |
1442 |
} |
} |
1443 |
if (openfd == -3) { |
if (openfd == -3) { |
1444 |
close(openfd); |
//close(openfd); // close(-3) ??! |
1445 |
} else { |
} else { |
1446 |
dup2(openfd, redir->fd); |
dup2(openfd, redir->fd); |
1447 |
if (redir->dup == -1) |
if (redir->dup == -1) |
1448 |
close (openfd); |
close(openfd); |
1449 |
} |
} |
1450 |
} |
} |
1451 |
} |
} |
1455 |
static void restore_redirects(int squirrel[]) |
static void restore_redirects(int squirrel[]) |
1456 |
{ |
{ |
1457 |
int i, fd; |
int i, fd; |
1458 |
for (i=0; i<3; i++) { |
for (i = 0; i < 3; i++) { |
1459 |
fd = squirrel[i]; |
fd = squirrel[i]; |
1460 |
if (fd != -1) { |
if (fd != -1) { |
1461 |
/* No error checking. I sure wouldn't know what |
/* We simply die on error */ |
1462 |
* to do with an error if I found one! */ |
xmove_fd(fd, i); |
|
dup2(fd, i); |
|
|
close(fd); |
|
1463 |
} |
} |
1464 |
} |
} |
1465 |
} |
} |
1466 |
|
|
1467 |
/* never returns */ |
static char **expand_assignments(char **argv, int count) |
1468 |
/* XXX no exit() here. If you don't exec, use _exit instead. |
{ |
1469 |
|
int i; |
1470 |
|
char **p = NULL; |
1471 |
|
/* Expand assignments into one string each */ |
1472 |
|
for (i = 0; i < count; i++) { |
1473 |
|
p = add_string_to_strings(p, expand_string_to_string(argv[i])); |
1474 |
|
} |
1475 |
|
return p; |
1476 |
|
} |
1477 |
|
|
1478 |
|
/* Called after [v]fork() in run_pipe(), or from builtin_exec(). |
1479 |
|
* Never returns. |
1480 |
|
* XXX no exit() here. If you don't exec, use _exit instead. |
1481 |
* The at_exit handlers apparently confuse the calling process, |
* The at_exit handlers apparently confuse the calling process, |
1482 |
* in particular stdin handling. Not sure why? */ |
* in particular stdin handling. Not sure why? -- because of vfork! (vda) */ |
1483 |
static void pseudo_exec(struct child_prog *child) |
static void pseudo_exec_argv(nommu_save_t *nommu_save, char **argv, int assignment_cnt, char **argv_expanded) |
1484 |
{ |
{ |
1485 |
int i, rcode; |
int rcode; |
1486 |
char *p; |
char **new_env; |
1487 |
const struct built_in_command *x; |
const struct built_in_command *x; |
1488 |
if (child->argv) { |
|
1489 |
for (i=0; is_assignment(child->argv[i]); i++) { |
/* If a variable is assigned in a forest, and nobody listens, |
1490 |
debug_printf("pid %d environment modification: %s\n",getpid(),child->argv[i]); |
* was it ever really set? |
1491 |
p = insert_var_value(child->argv[i]); |
*/ |
1492 |
putenv(strdup(p)); |
if (!argv[assignment_cnt]) |
1493 |
if (p != child->argv[i]) free(p); |
_exit(EXIT_SUCCESS); |
1494 |
} |
|
1495 |
child->argv+=i; /* XXX this hack isn't so horrible, since we are about |
new_env = expand_assignments(argv, assignment_cnt); |
1496 |
to exit, and therefore don't need to keep data |
#if BB_MMU |
1497 |
structures consistent for free() use. */ |
putenv_all(new_env); |
1498 |
/* If a variable is assigned in a forest, and nobody listens, |
free(new_env); /* optional */ |
1499 |
* was it ever really set? |
#else |
1500 |
*/ |
nommu_save->new_env = new_env; |
1501 |
if (child->argv[0] == NULL) { |
nommu_save->old_env = putenv_all_and_save_old(new_env); |
1502 |
_exit(EXIT_SUCCESS); |
#endif |
1503 |
|
if (argv_expanded) { |
1504 |
|
argv = argv_expanded; |
1505 |
|
} else { |
1506 |
|
argv = expand_strvec_to_strvec(argv); |
1507 |
|
#if !BB_MMU |
1508 |
|
nommu_save->argv = argv; |
1509 |
|
#endif |
1510 |
|
} |
1511 |
|
|
1512 |
|
/* |
1513 |
|
* Check if the command matches any of the builtins. |
1514 |
|
* Depending on context, this might be redundant. But it's |
1515 |
|
* easier to waste a few CPU cycles than it is to figure out |
1516 |
|
* if this is one of those cases. |
1517 |
|
*/ |
1518 |
|
for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { |
1519 |
|
if (strcmp(argv[0], x->cmd) == 0) { |
1520 |
|
debug_printf_exec("running builtin '%s'\n", argv[0]); |
1521 |
|
rcode = x->function(argv); |
1522 |
|
fflush(stdout); |
1523 |
|
_exit(rcode); |
1524 |
} |
} |
1525 |
|
} |
1526 |
|
|
1527 |
/* |
/* Check if the command matches any busybox applets */ |
1528 |
* Check if the command matches any of the builtins. |
#if ENABLE_FEATURE_SH_STANDALONE |
1529 |
* Depending on context, this might be redundant. But it's |
if (strchr(argv[0], '/') == NULL) { |
1530 |
* easier to waste a few CPU cycles than it is to figure out |
int a = find_applet_by_name(argv[0]); |
1531 |
* if this is one of those cases. |
if (a >= 0) { |
1532 |
*/ |
if (APPLET_IS_NOEXEC(a)) { |
1533 |
for (x = bltins; x->cmd; x++) { |
debug_printf_exec("running applet '%s'\n", argv[0]); |
1534 |
if (strcmp(child->argv[0], x->cmd) == 0 ) { |
// is it ok that run_applet_no_and_exit() does exit(), not _exit()? |
1535 |
debug_printf("builtin exec %s\n", child->argv[0]); |
run_applet_no_and_exit(a, argv); |
1536 |
rcode = x->function(child); |
} |
1537 |
fflush(stdout); |
/* re-exec ourselves with the new arguments */ |
1538 |
_exit(rcode); |
debug_printf_exec("re-execing applet '%s'\n", argv[0]); |
1539 |
} |
execvp(bb_busybox_exec_path, argv); |
1540 |
|
/* If they called chroot or otherwise made the binary no longer |
1541 |
|
* executable, fall through */ |
1542 |
} |
} |
1543 |
|
} |
1544 |
|
#endif |
1545 |
|
|
1546 |
/* Check if the command matches any busybox internal commands |
debug_printf_exec("execing '%s'\n", argv[0]); |
1547 |
* ("applets") here. |
execvp(argv[0], argv); |
1548 |
* FIXME: This feature is not 100% safe, since |
bb_perror_msg("can't exec '%s'", argv[0]); |
1549 |
* BusyBox is not fully reentrant, so we have no guarantee the things |
_exit(EXIT_FAILURE); |
1550 |
* from the .bss are still zeroed, or that things from .data are still |
} |
1551 |
* at their defaults. We could exec ourself from /proc/self/exe, but I |
|
1552 |
* really dislike relying on /proc for things. We could exec ourself |
/* Called after [v]fork() in run_pipe() |
1553 |
* from global_argv[0], but if we are in a chroot, we may not be able |
*/ |
1554 |
* to find ourself... */ |
static void pseudo_exec(nommu_save_t *nommu_save, struct command *command, char **argv_expanded) |
1555 |
#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL |
{ |
1556 |
{ |
if (command->argv) |
1557 |
int argc_l; |
pseudo_exec_argv(nommu_save, command->argv, command->assignment_cnt, argv_expanded); |
1558 |
char** argv_l=child->argv; |
|
1559 |
char *name = child->argv[0]; |
if (command->group) { |
1560 |
|
#if !BB_MMU |
1561 |
/* Count argc for use in a second... */ |
bb_error_msg_and_die("nested lists are not supported on NOMMU"); |
1562 |
for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++); |
#else |
1563 |
optind = 1; |
int rcode; |
1564 |
debug_printf("running applet %s\n", name); |
debug_printf_exec("pseudo_exec: run_list\n"); |
1565 |
run_applet_by_name(name, argc_l, child->argv); |
rcode = run_list(command->group); |
|
} |
|
|
#endif |
|
|
debug_printf("exec of %s\n",child->argv[0]); |
|
|
execvp(child->argv[0],child->argv); |
|
|
bb_perror_msg("cannot exec: %s",child->argv[0]); |
|
|
_exit(1); |
|
|
} else if (child->group) { |
|
|
debug_printf("runtime nesting to group\n"); |
|
|
interactive=0; /* crucial!!!! */ |
|
|
rcode = run_list_real(child->group); |
|
1566 |
/* OK to leak memory by not calling free_pipe_list, |
/* OK to leak memory by not calling free_pipe_list, |
1567 |
* since this process is about to exit */ |
* since this process is about to exit */ |
1568 |
_exit(rcode); |
_exit(rcode); |
1569 |
} else { |
#endif |
|
/* Can happen. See what bash does with ">foo" by itself. */ |
|
|
debug_printf("trying to pseudo_exec null command\n"); |
|
|
_exit(EXIT_SUCCESS); |
|
1570 |
} |
} |
1571 |
|
|
1572 |
|
/* Can happen. See what bash does with ">foo" by itself. */ |
1573 |
|
debug_printf("trying to pseudo_exec null command\n"); |
1574 |
|
_exit(EXIT_SUCCESS); |
1575 |
|
} |
1576 |
|
|
1577 |
|
#if ENABLE_HUSH_JOB |
1578 |
|
static const char *get_cmdtext(struct pipe *pi) |
1579 |
|
{ |
1580 |
|
char **argv; |
1581 |
|
char *p; |
1582 |
|
int len; |
1583 |
|
|
1584 |
|
/* This is subtle. ->cmdtext is created only on first backgrounding. |
1585 |
|
* (Think "cat, <ctrl-z>, fg, <ctrl-z>, fg, <ctrl-z>...." here...) |
1586 |
|
* On subsequent bg argv is trashed, but we won't use it */ |
1587 |
|
if (pi->cmdtext) |
1588 |
|
return pi->cmdtext; |
1589 |
|
argv = pi->cmds[0].argv; |
1590 |
|
if (!argv || !argv[0]) { |
1591 |
|
pi->cmdtext = xzalloc(1); |
1592 |
|
return pi->cmdtext; |
1593 |
|
} |
1594 |
|
|
1595 |
|
len = 0; |
1596 |
|
do len += strlen(*argv) + 1; while (*++argv); |
1597 |
|
pi->cmdtext = p = xmalloc(len); |
1598 |
|
argv = pi->cmds[0].argv; |
1599 |
|
do { |
1600 |
|
len = strlen(*argv); |
1601 |
|
memcpy(p, *argv, len); |
1602 |
|
p += len; |
1603 |
|
*p++ = ' '; |
1604 |
|
} while (*++argv); |
1605 |
|
p[-1] = '\0'; |
1606 |
|
return pi->cmdtext; |
1607 |
} |
} |
1608 |
|
|
1609 |
static void insert_bg_job(struct pipe *pi) |
static void insert_bg_job(struct pipe *pi) |
1610 |
{ |
{ |
1611 |
struct pipe *thejob; |
struct pipe *thejob; |
1612 |
|
int i; |
1613 |
|
|
1614 |
/* Linear search for the ID of the job to use */ |
/* Linear search for the ID of the job to use */ |
1615 |
pi->jobid = 1; |
pi->jobid = 1; |
1616 |
for (thejob = job_list; thejob; thejob = thejob->next) |
for (thejob = G.job_list; thejob; thejob = thejob->next) |
1617 |
if (thejob->jobid >= pi->jobid) |
if (thejob->jobid >= pi->jobid) |
1618 |
pi->jobid = thejob->jobid + 1; |
pi->jobid = thejob->jobid + 1; |
1619 |
|
|
1620 |
/* add thejob to the list of running jobs */ |
/* Add thejob to the list of running jobs */ |
1621 |
if (!job_list) { |
if (!G.job_list) { |
1622 |
thejob = job_list = xmalloc(sizeof(*thejob)); |
thejob = G.job_list = xmalloc(sizeof(*thejob)); |
1623 |
} else { |
} else { |
1624 |
for (thejob = job_list; thejob->next; thejob = thejob->next) /* nothing */; |
for (thejob = G.job_list; thejob->next; thejob = thejob->next) |
1625 |
|
continue; |
1626 |
thejob->next = xmalloc(sizeof(*thejob)); |
thejob->next = xmalloc(sizeof(*thejob)); |
1627 |
thejob = thejob->next; |
thejob = thejob->next; |
1628 |
} |
} |
1629 |
|
|
1630 |
/* physically copy the struct job */ |
/* Physically copy the struct job */ |
1631 |
memcpy(thejob, pi, sizeof(struct pipe)); |
memcpy(thejob, pi, sizeof(struct pipe)); |
1632 |
thejob->next = NULL; |
thejob->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds); |
1633 |
thejob->running_progs = thejob->num_progs; |
/* We cannot copy entire pi->cmds[] vector! Double free()s will happen */ |
1634 |
thejob->stopped_progs = 0; |
for (i = 0; i < pi->num_cmds; i++) { |
1635 |
thejob->text = xmalloc(BUFSIZ); /* cmdedit buffer size */ |
// TODO: do we really need to have so many fields which are just dead weight |
1636 |
|
// at execution stage? |
1637 |
//if (pi->progs[0] && pi->progs[0].argv && pi->progs[0].argv[0]) |
thejob->cmds[i].pid = pi->cmds[i].pid; |
1638 |
{ |
/* all other fields are not used and stay zero */ |
|
char *bar=thejob->text; |
|
|
char **foo=pi->progs[0].argv; |
|
|
while(foo && *foo) { |
|
|
bar += sprintf(bar, "%s ", *foo++); |
|
|
} |
|
1639 |
} |
} |
1640 |
|
thejob->next = NULL; |
1641 |
|
thejob->cmdtext = xstrdup(get_cmdtext(pi)); |
1642 |
|
|
1643 |
/* we don't wait for background thejobs to return -- append it |
/* We don't wait for background thejobs to return -- append it |
1644 |
to the list of backgrounded thejobs and leave it alone */ |
to the list of backgrounded thejobs and leave it alone */ |
1645 |
printf("[%d] %d\n", thejob->jobid, thejob->progs[0].pid); |
printf("[%d] %d %s\n", thejob->jobid, thejob->cmds[0].pid, thejob->cmdtext); |
1646 |
last_bg_pid = thejob->progs[0].pid; |
G.last_bg_pid = thejob->cmds[0].pid; |
1647 |
last_jobid = thejob->jobid; |
G.last_jobid = thejob->jobid; |
1648 |
} |
} |
1649 |
|
|
|
/* remove a backgrounded job */ |
|
1650 |
static void remove_bg_job(struct pipe *pi) |
static void remove_bg_job(struct pipe *pi) |
1651 |
{ |
{ |
1652 |
struct pipe *prev_pipe; |
struct pipe *prev_pipe; |
1653 |
|
|
1654 |
if (pi == job_list) { |
if (pi == G.job_list) { |
1655 |
job_list = pi->next; |
G.job_list = pi->next; |
1656 |
} else { |
} else { |
1657 |
prev_pipe = job_list; |
prev_pipe = G.job_list; |
1658 |
while (prev_pipe->next != pi) |
while (prev_pipe->next != pi) |
1659 |
prev_pipe = prev_pipe->next; |
prev_pipe = prev_pipe->next; |
1660 |
prev_pipe->next = pi->next; |
prev_pipe->next = pi->next; |
1661 |
} |
} |
1662 |
if (job_list) |
if (G.job_list) |
1663 |
last_jobid = job_list->jobid; |
G.last_jobid = G.job_list->jobid; |
1664 |
else |
else |
1665 |
last_jobid = 0; |
G.last_jobid = 0; |
1666 |
|
} |
1667 |
|
|
1668 |
pi->stopped_progs = 0; |
/* Remove a backgrounded job */ |
1669 |
|
static void delete_finished_bg_job(struct pipe *pi) |
1670 |
|
{ |
1671 |
|
remove_bg_job(pi); |
1672 |
|
pi->stopped_cmds = 0; |
1673 |
free_pipe(pi, 0); |
free_pipe(pi, 0); |
1674 |
free(pi); |
free(pi); |
1675 |
} |
} |
1676 |
|
#endif /* JOB */ |
1677 |
|
|
1678 |
/* Checks to see if any processes have exited -- if they |
/* Check to see if any processes have exited -- if they |
1679 |
have, figure out why and see if a job has completed */ |
* have, figure out why and see if a job has completed */ |
1680 |
static int checkjobs(struct pipe* fg_pipe) |
static int checkjobs(struct pipe* fg_pipe) |
1681 |
{ |
{ |
1682 |
int attributes; |
int attributes; |
1683 |
int status; |
int status; |
1684 |
int prognum = 0; |
#if ENABLE_HUSH_JOB |
1685 |
struct pipe *pi; |
struct pipe *pi; |
1686 |
|
#endif |
1687 |
pid_t childpid; |
pid_t childpid; |
1688 |
|
int rcode = 0; |
1689 |
|
|
1690 |
attributes = WUNTRACED; |
attributes = WUNTRACED; |
1691 |
if (fg_pipe==NULL) { |
if (fg_pipe == NULL) |
1692 |
attributes |= WNOHANG; |
attributes |= WNOHANG; |
|
} |
|
1693 |
|
|
1694 |
|
/* Do we do this right? |
1695 |
|
* bash-3.00# sleep 20 | false |
1696 |
|
* <ctrl-Z pressed> |
1697 |
|
* [3]+ Stopped sleep 20 | false |
1698 |
|
* bash-3.00# echo $? |
1699 |
|
* 1 <========== bg pipe is not fully done, but exitcode is already known! |
1700 |
|
*/ |
1701 |
|
|
1702 |
|
//FIXME: non-interactive bash does not continue even if all processes in fg pipe |
1703 |
|
//are stopped. Testcase: "cat | cat" in a script (not on command line) |
1704 |
|
// + killall -STOP cat |
1705 |
|
|
1706 |
|
wait_more: |
1707 |
|
// TODO: safe_waitpid? |
1708 |
while ((childpid = waitpid(-1, &status, attributes)) > 0) { |
while ((childpid = waitpid(-1, &status, attributes)) > 0) { |
1709 |
|
int i; |
1710 |
|
const int dead = WIFEXITED(status) || WIFSIGNALED(status); |
1711 |
|
#if DEBUG_JOBS |
1712 |
|
if (WIFSTOPPED(status)) |
1713 |
|
debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n", |
1714 |
|
childpid, WSTOPSIG(status), WEXITSTATUS(status)); |
1715 |
|
if (WIFSIGNALED(status)) |
1716 |
|
debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n", |
1717 |
|
childpid, WTERMSIG(status), WEXITSTATUS(status)); |
1718 |
|
if (WIFEXITED(status)) |
1719 |
|
debug_printf_jobs("pid %d exited, exitcode %d\n", |
1720 |
|
childpid, WEXITSTATUS(status)); |
1721 |
|
#endif |
1722 |
|
/* Were we asked to wait for fg pipe? */ |
1723 |
if (fg_pipe) { |
if (fg_pipe) { |
1724 |
int i, rcode = 0; |
for (i = 0; i < fg_pipe->num_cmds; i++) { |
1725 |
for (i=0; i < fg_pipe->num_progs; i++) { |
debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid); |
1726 |
if (fg_pipe->progs[i].pid == childpid) { |
if (fg_pipe->cmds[i].pid != childpid) |
1727 |
if (i==fg_pipe->num_progs-1) |
continue; |
1728 |
rcode=WEXITSTATUS(status); |
/* printf("process %d exit %d\n", i, WEXITSTATUS(status)); */ |
1729 |
(fg_pipe->num_progs)--; |
if (dead) { |
1730 |
|
fg_pipe->cmds[i].pid = 0; |
1731 |
|
fg_pipe->alive_cmds--; |
1732 |
|
if (i == fg_pipe->num_cmds - 1) { |
1733 |
|
/* last process gives overall exitstatus */ |
1734 |
|
rcode = WEXITSTATUS(status); |
1735 |
|
IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;) |
1736 |
|
} |
1737 |
|
} else { |
1738 |
|
fg_pipe->cmds[i].is_stopped = 1; |
1739 |
|
fg_pipe->stopped_cmds++; |
1740 |
|
} |
1741 |
|
debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n", |
1742 |
|
fg_pipe->alive_cmds, fg_pipe->stopped_cmds); |
1743 |
|
if (fg_pipe->alive_cmds - fg_pipe->stopped_cmds <= 0) { |
1744 |
|
/* All processes in fg pipe have exited/stopped */ |
1745 |
|
#if ENABLE_HUSH_JOB |
1746 |
|
if (fg_pipe->alive_cmds) |
1747 |
|
insert_bg_job(fg_pipe); |
1748 |
|
#endif |
1749 |
return rcode; |
return rcode; |
1750 |
} |
} |
1751 |
|
/* There are still running processes in the fg pipe */ |
1752 |
|
goto wait_more; /* do waitpid again */ |
1753 |
} |
} |
1754 |
|
/* it wasnt fg_pipe, look for process in bg pipes */ |
1755 |
} |
} |
1756 |
|
|
1757 |
for (pi = job_list; pi; pi = pi->next) { |
#if ENABLE_HUSH_JOB |
1758 |
prognum = 0; |
/* We asked to wait for bg or orphaned children */ |
1759 |
while (prognum < pi->num_progs && pi->progs[prognum].pid != childpid) { |
/* No need to remember exitcode in this case */ |
1760 |
prognum++; |
for (pi = G.job_list; pi; pi = pi->next) { |
1761 |
|
for (i = 0; i < pi->num_cmds; i++) { |
1762 |
|
if (pi->cmds[i].pid == childpid) |
1763 |
|
goto found_pi_and_prognum; |
1764 |
} |
} |
|
if (prognum < pi->num_progs) |
|
|
break; |
|
|
} |
|
|
|
|
|
if(pi==NULL) { |
|
|
debug_printf("checkjobs: pid %d was not in our list!\n", childpid); |
|
|
continue; |
|
1765 |
} |
} |
1766 |
|
/* Happens when shell is used as init process (init=/bin/sh) */ |
1767 |
|
debug_printf("checkjobs: pid %d was not in our list!\n", childpid); |
1768 |
|
continue; /* do waitpid again */ |
1769 |
|
|
1770 |
if (WIFEXITED(status) || WIFSIGNALED(status)) { |
found_pi_and_prognum: |
1771 |
|
if (dead) { |
1772 |
/* child exited */ |
/* child exited */ |
1773 |
pi->running_progs--; |
pi->cmds[i].pid = 0; |
1774 |
pi->progs[prognum].pid = 0; |
pi->alive_cmds--; |
1775 |
|
if (!pi->alive_cmds) { |
1776 |
if (!pi->running_progs) { |
printf(JOB_STATUS_FORMAT, pi->jobid, |
1777 |
printf(JOB_STATUS_FORMAT, pi->jobid, "Done", pi->text); |
"Done", pi->cmdtext); |
1778 |
remove_bg_job(pi); |
delete_finished_bg_job(pi); |
1779 |
} |
} |
1780 |
} else { |
} else { |
1781 |
/* child stopped */ |
/* child stopped */ |
1782 |
pi->stopped_progs++; |
pi->cmds[i].is_stopped = 1; |
1783 |
pi->progs[prognum].is_stopped = 1; |
pi->stopped_cmds++; |
1784 |
} |
} |
1785 |
} |
#endif |
1786 |
|
} /* while (waitpid succeeds)... */ |
1787 |
|
|
1788 |
|
/* wait found no children or failed */ |
1789 |
|
|
1790 |
if (childpid == -1 && errno != ECHILD) |
if (childpid && errno != ECHILD) |
1791 |
bb_perror_msg("waitpid"); |
bb_perror_msg("waitpid"); |
1792 |
|
return rcode; |
1793 |
|
} |
1794 |
|
|
1795 |
/* move the shell to the foreground */ |
#if ENABLE_HUSH_JOB |
1796 |
//if (interactive && tcsetpgrp(shell_terminal, getpgid(0))) |
static int checkjobs_and_fg_shell(struct pipe* fg_pipe) |
1797 |
// bb_perror_msg("tcsetpgrp-2"); |
{ |
1798 |
return -1; |
pid_t p; |
1799 |
|
int rcode = checkjobs(fg_pipe); |
1800 |
|
/* Job finished, move the shell to the foreground */ |
1801 |
|
p = getpgid(0); /* pgid of our process */ |
1802 |
|
debug_printf_jobs("fg'ing ourself: getpgid(0)=%d\n", (int)p); |
1803 |
|
tcsetpgrp(G.interactive_fd, p); |
1804 |
|
return rcode; |
1805 |
} |
} |
1806 |
|
#endif |
1807 |
|
|
1808 |
/* run_pipe_real() starts all the jobs, but doesn't wait for anything |
/* run_pipe() starts all the jobs, but doesn't wait for anything |
1809 |
* to finish. See checkjobs(). |
* to finish. See checkjobs(). |
1810 |
* |
* |
1811 |
* return code is normally -1, when the caller has to wait for children |
* return code is normally -1, when the caller has to wait for children |
1812 |
* to finish to determine the exit status of the pipe. If the pipe |
* to finish to determine the exit status of the pipe. If the pipe |
1813 |
* is a simple builtin command, however, the action is done by the |
* is a simple builtin command, however, the action is done by the |
1814 |
* time run_pipe_real returns, and the exit code is provided as the |
* time run_pipe returns, and the exit code is provided as the |
1815 |
* return value. |
* return value. |
1816 |
* |
* |
1817 |
* The input of the pipe is always stdin, the output is always |
* The input of the pipe is always stdin, the output is always |
1820 |
* subshell, when that is in fact necessary. The subshell process |
* subshell, when that is in fact necessary. The subshell process |
1821 |
* now has its stdout directed to the input of the appropriate pipe, |
* now has its stdout directed to the input of the appropriate pipe, |
1822 |
* so this routine is noticeably simpler. |
* so this routine is noticeably simpler. |
1823 |
|
* |
1824 |
|
* Returns -1 only if started some children. IOW: we have to |
1825 |
|
* mask out retvals of builtins etc with 0xff! |
1826 |
*/ |
*/ |
1827 |
static int run_pipe_real(struct pipe *pi) |
static int run_pipe(struct pipe *pi) |
1828 |
{ |
{ |
1829 |
int i; |
int i; |
1830 |
int nextin, nextout; |
int nextin; |
1831 |
int pipefds[2]; /* pipefds[0] is for reading */ |
int pipefds[2]; /* pipefds[0] is for reading */ |
1832 |
struct child_prog *child; |
struct command *command; |
1833 |
|
char **argv_expanded; |
1834 |
|
char **argv; |
1835 |
const struct built_in_command *x; |
const struct built_in_command *x; |
1836 |
char *p; |
char *p; |
1837 |
|
/* it is not always needed, but we aim to smaller code */ |
1838 |
|
int squirrel[] = { -1, -1, -1 }; |
1839 |
|
int rcode; |
1840 |
|
const int single_and_fg = (pi->num_cmds == 1 && pi->followup != PIPE_BG); |
1841 |
|
|
1842 |
nextin = 0; |
debug_printf_exec("run_pipe start: single_and_fg=%d\n", single_and_fg); |
1843 |
|
|
1844 |
|
#if ENABLE_HUSH_JOB |
1845 |
pi->pgrp = -1; |
pi->pgrp = -1; |
1846 |
|
#endif |
1847 |
|
pi->alive_cmds = 1; |
1848 |
|
pi->stopped_cmds = 0; |
1849 |
|
|
1850 |
/* Check if this is a simple builtin (not part of a pipe). |
/* Check if this is a simple builtin (not part of a pipe). |
1851 |
* Builtins within pipes have to fork anyway, and are handled in |
* Builtins within pipes have to fork anyway, and are handled in |
1852 |
* pseudo_exec. "echo foo | read bar" doesn't work on bash, either. |
* pseudo_exec. "echo foo | read bar" doesn't work on bash, either. |
1853 |
*/ |
*/ |
1854 |
child = & (pi->progs[0]); |
command = &(pi->cmds[0]); |
1855 |
if (pi->num_progs == 1 && child->group && child->subshell == 0) { |
|
1856 |
int squirrel[] = {-1, -1, -1}; |
#if ENABLE_HUSH_FUNCTIONS |
1857 |
int rcode; |
if (single_and_fg && command->group && command->grp_type == GRP_FUNCTION) { |
1858 |
|
/* We "execute" function definition */ |
1859 |
|
bb_error_msg("here we ought to remember function definition, and go on"); |
1860 |
|
return EXIT_SUCCESS; |
1861 |
|
} |
1862 |
|
#endif |
1863 |
|
|
1864 |
|
if (single_and_fg && command->group && command->grp_type == GRP_NORMAL) { |
1865 |
debug_printf("non-subshell grouping\n"); |
debug_printf("non-subshell grouping\n"); |
1866 |
setup_redirects(child, squirrel); |
setup_redirects(command, squirrel); |
1867 |
/* XXX could we merge code with following builtin case, |
debug_printf_exec(": run_list\n"); |
1868 |
* by creating a pseudo builtin that calls run_list_real? */ |
rcode = run_list(command->group) & 0xff; |
|
rcode = run_list_real(child->group); |
|
1869 |
restore_redirects(squirrel); |
restore_redirects(squirrel); |
1870 |
|
debug_printf_exec("run_pipe return %d\n", rcode); |
1871 |
|
IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) |
1872 |
return rcode; |
return rcode; |
1873 |
} else if (pi->num_progs == 1 && pi->progs[0].argv != NULL) { |
} |
1874 |
for (i=0; is_assignment(child->argv[i]); i++) { /* nothing */ } |
|
1875 |
if (i!=0 && child->argv[i]==NULL) { |
argv = command->argv; |
1876 |
/* assignments, but no command: set the local environment */ |
argv_expanded = NULL; |
1877 |
for (i=0; child->argv[i]!=NULL; i++) { |
|
1878 |
|
if (single_and_fg && argv != NULL) { |
1879 |
/* Ok, this case is tricky. We have to decide if this is a |
char **new_env = NULL; |
1880 |
* local variable, or an already exported variable. If it is |
char **old_env = NULL; |
1881 |
* already exported, we have to export the new value. If it is |
|
1882 |
* not exported, we need only set this as a local variable. |
i = command->assignment_cnt; |
1883 |
* This junk is all to decide whether or not to export this |
if (i != 0 && argv[i] == NULL) { |
1884 |
* variable. */ |
/* assignments, but no command: set local environment */ |
1885 |
int export_me=0; |
for (i = 0; argv[i] != NULL; i++) { |
1886 |
char *name, *value; |
debug_printf("local environment set: %s\n", argv[i]); |
1887 |
name = xstrdup(child->argv[i]); |
p = expand_string_to_string(argv[i]); |
1888 |
debug_printf("Local environment set: %s\n", name); |
set_local_var(p, 0); |
|
value = strchr(name, '='); |
|
|
if (value) |
|
|
*value=0; |
|
|
if ( get_local_var(name)) { |
|
|
export_me=1; |
|
|
} |
|
|
free(name); |
|
|
p = insert_var_value(child->argv[i]); |
|
|
set_local_var(p, export_me); |
|
|
if (p != child->argv[i]) free(p); |
|
|
} |
|
|
return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ |
|
|
} |
|
|
for (i = 0; is_assignment(child->argv[i]); i++) { |
|
|
p = insert_var_value(child->argv[i]); |
|
|
putenv(strdup(p)); |
|
|
if (p != child->argv[i]) { |
|
|
child->sp--; |
|
|
free(p); |
|
1889 |
} |
} |
1890 |
|
return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ |
1891 |
} |
} |
|
if (child->sp) { |
|
|
char * str = NULL; |
|
1892 |
|
|
1893 |
str = make_string((child->argv + i)); |
/* Expand the rest into (possibly) many strings each */ |
1894 |
parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING); |
argv_expanded = expand_strvec_to_strvec(argv + i); |
1895 |
free(str); |
|
1896 |
return last_return_code; |
for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { |
1897 |
|
if (strcmp(argv_expanded[0], x->cmd) != 0) |
1898 |
|
continue; |
1899 |
|
if (x->function == builtin_exec && argv_expanded[1] == NULL) { |
1900 |
|
debug_printf("exec with redirects only\n"); |
1901 |
|
setup_redirects(command, NULL); |
1902 |
|
rcode = EXIT_SUCCESS; |
1903 |
|
goto clean_up_and_ret1; |
1904 |
|
} |
1905 |
|
debug_printf("builtin inline %s\n", argv_expanded[0]); |
1906 |
|
/* XXX setup_redirects acts on file descriptors, not FILEs. |
1907 |
|
* This is perfect for work that comes after exec(). |
1908 |
|
* Is it really safe for inline use? Experimentally, |
1909 |
|
* things seem to work with glibc. */ |
1910 |
|
setup_redirects(command, squirrel); |
1911 |
|
new_env = expand_assignments(argv, command->assignment_cnt); |
1912 |
|
old_env = putenv_all_and_save_old(new_env); |
1913 |
|
debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv_expanded[1]); |
1914 |
|
rcode = x->function(argv_expanded) & 0xff; |
1915 |
|
#if ENABLE_FEATURE_SH_STANDALONE |
1916 |
|
clean_up_and_ret: |
1917 |
|
#endif |
1918 |
|
restore_redirects(squirrel); |
1919 |
|
free_strings_and_unsetenv(new_env, 1); |
1920 |
|
putenv_all(old_env); |
1921 |
|
free(old_env); /* not free_strings()! */ |
1922 |
|
clean_up_and_ret1: |
1923 |
|
free(argv_expanded); |
1924 |
|
IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) |
1925 |
|
debug_printf_exec("run_pipe return %d\n", rcode); |
1926 |
|
return rcode; |
1927 |
|
} |
1928 |
|
#if ENABLE_FEATURE_SH_STANDALONE |
1929 |
|
i = find_applet_by_name(argv_expanded[0]); |
1930 |
|
if (i >= 0 && APPLET_IS_NOFORK(i)) { |
1931 |
|
setup_redirects(command, squirrel); |
1932 |
|
save_nofork_data(&G.nofork_save); |
1933 |
|
new_env = expand_assignments(argv, command->assignment_cnt); |
1934 |
|
old_env = putenv_all_and_save_old(new_env); |
1935 |
|
debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]); |
1936 |
|
rcode = run_nofork_applet_prime(&G.nofork_save, i, argv_expanded); |
1937 |
|
goto clean_up_and_ret; |
1938 |
} |
} |
1939 |
for (x = bltins; x->cmd; x++) { |
#endif |
1940 |
if (strcmp(child->argv[i], x->cmd) == 0 ) { |
} |
1941 |
int squirrel[] = {-1, -1, -1}; |
|
1942 |
int rcode; |
/* NB: argv_expanded may already be created, and that |
1943 |
if (x->function == builtin_exec && child->argv[i+1]==NULL) { |
* might include `cmd` runs! Do not rerun it! We *must* |
1944 |
debug_printf("magic exec\n"); |
* use argv_expanded if it's non-NULL */ |
1945 |
setup_redirects(child,NULL); |
|
1946 |
return EXIT_SUCCESS; |
/* Disable job control signals for shell (parent) and |
1947 |
|
* for initial child code after fork */ |
1948 |
|
set_jobctrl_sighandler(SIG_IGN); |
1949 |
|
|
1950 |
|
/* Going to fork a child per each pipe member */ |
1951 |
|
pi->alive_cmds = 0; |
1952 |
|
nextin = 0; |
1953 |
|
|
1954 |
|
for (i = 0; i < pi->num_cmds; i++) { |
1955 |
|
#if !BB_MMU |
1956 |
|
volatile nommu_save_t nommu_save; |
1957 |
|
nommu_save.new_env = NULL; |
1958 |
|
nommu_save.old_env = NULL; |
1959 |
|
nommu_save.argv = NULL; |
1960 |
|
#endif |
1961 |
|
command = &(pi->cmds[i]); |
1962 |
|
if (command->argv) { |
1963 |
|
debug_printf_exec(": pipe member '%s' '%s'...\n", command->argv[0], command->argv[1]); |
1964 |
|
} else |
1965 |
|
debug_printf_exec(": pipe member with no argv\n"); |
1966 |
|
|
1967 |
|
/* pipes are inserted between pairs of commands */ |
1968 |
|
pipefds[0] = 0; |
1969 |
|
pipefds[1] = 1; |
1970 |
|
if ((i + 1) < pi->num_cmds) |
1971 |
|
xpipe(pipefds); |
1972 |
|
|
1973 |
|
command->pid = BB_MMU ? fork() : vfork(); |
1974 |
|
if (!command->pid) { /* child */ |
1975 |
|
if (ENABLE_HUSH_JOB) |
1976 |
|
die_sleep = 0; /* let nofork's xfuncs die */ |
1977 |
|
#if ENABLE_HUSH_JOB |
1978 |
|
/* Every child adds itself to new process group |
1979 |
|
* with pgid == pid_of_first_child_in_pipe */ |
1980 |
|
if (G.run_list_level == 1 && G.interactive_fd) { |
1981 |
|
pid_t pgrp; |
1982 |
|
/* Don't do pgrp restore anymore on fatal signals */ |
1983 |
|
set_fatal_sighandler(SIG_DFL); |
1984 |
|
pgrp = pi->pgrp; |
1985 |
|
if (pgrp < 0) /* true for 1st process only */ |
1986 |
|
pgrp = getpid(); |
1987 |
|
if (setpgid(0, pgrp) == 0 && pi->followup != PIPE_BG) { |
1988 |
|
/* We do it in *every* child, not just first, |
1989 |
|
* to avoid races */ |
1990 |
|
tcsetpgrp(G.interactive_fd, pgrp); |
1991 |
} |
} |
|
debug_printf("builtin inline %s\n", child->argv[0]); |
|
|
/* XXX setup_redirects acts on file descriptors, not FILEs. |
|
|
* This is perfect for work that comes after exec(). |
|
|
* Is it really safe for inline use? Experimentally, |
|
|
* things seem to work with glibc. */ |
|
|
setup_redirects(child, squirrel); |
|
|
child->argv+=i; /* XXX horrible hack */ |
|
|
rcode = x->function(child); |
|
|
child->argv-=i; /* XXX restore hack so free() can work right */ |
|
|
restore_redirects(squirrel); |
|
|
return rcode; |
|
1992 |
} |
} |
1993 |
|
#endif |
1994 |
|
xmove_fd(nextin, 0); |
1995 |
|
xmove_fd(pipefds[1], 1); /* write end */ |
1996 |
|
if (pipefds[0] > 1) |
1997 |
|
close(pipefds[0]); /* read end */ |
1998 |
|
/* Like bash, explicit redirects override pipes, |
1999 |
|
* and the pipe fd is available for dup'ing. */ |
2000 |
|
setup_redirects(command, NULL); |
2001 |
|
|
2002 |
|
/* Restore default handlers just prior to exec */ |
2003 |
|
set_jobctrl_sighandler(SIG_DFL); |
2004 |
|
set_misc_sighandler(SIG_DFL); |
2005 |
|
signal(SIGCHLD, SIG_DFL); |
2006 |
|
/* Stores to nommu_save list of env vars putenv'ed |
2007 |
|
* (NOMMU, on MMU we don't need that) */ |
2008 |
|
/* cast away volatility... */ |
2009 |
|
pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded); |
2010 |
|
/* pseudo_exec() does not return */ |
2011 |
|
} |
2012 |
|
/* parent */ |
2013 |
|
#if !BB_MMU |
2014 |
|
/* Clean up after vforked child */ |
2015 |
|
free(nommu_save.argv); |
2016 |
|
free_strings_and_unsetenv(nommu_save.new_env, 1); |
2017 |
|
putenv_all(nommu_save.old_env); |
2018 |
|
#endif |
2019 |
|
free(argv_expanded); |
2020 |
|
argv_expanded = NULL; |
2021 |
|
if (command->pid < 0) { /* [v]fork failed */ |
2022 |
|
/* Clearly indicate, was it fork or vfork */ |
2023 |
|
bb_perror_msg(BB_MMU ? "fork" : "vfork"); |
2024 |
|
} else { |
2025 |
|
pi->alive_cmds++; |
2026 |
|
#if ENABLE_HUSH_JOB |
2027 |
|
/* Second and next children need to know pid of first one */ |
2028 |
|
if (pi->pgrp < 0) |
2029 |
|
pi->pgrp = command->pid; |
2030 |
|
#endif |
2031 |
} |
} |
2032 |
|
|
2033 |
|
if (i) |
2034 |
|
close(nextin); |
2035 |
|
if ((i + 1) < pi->num_cmds) |
2036 |
|
close(pipefds[1]); /* write end */ |
2037 |
|
/* Pass read (output) pipe end to next iteration */ |
2038 |
|
nextin = pipefds[0]; |
2039 |
} |
} |
2040 |
|
|
2041 |
for (i = 0; i < pi->num_progs; i++) { |
if (!pi->alive_cmds) { |
2042 |
child = & (pi->progs[i]); |
debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n"); |
2043 |
|
return 1; |
2044 |
|
} |
2045 |
|
|
2046 |
/* pipes are inserted between pairs of commands */ |
debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->alive_cmds); |
2047 |
if ((i + 1) < pi->num_progs) { |
return -1; |
2048 |
if (pipe(pipefds)<0) bb_perror_msg_and_die("pipe"); |
} |
2049 |
nextout = pipefds[1]; |
|
2050 |
} else { |
#ifndef debug_print_tree |
2051 |
nextout=1; |
static void debug_print_tree(struct pipe *pi, int lvl) |
2052 |
pipefds[0] = -1; |
{ |
2053 |
|
static const char *const PIPE[] = { |
2054 |
|
[PIPE_SEQ] = "SEQ", |
2055 |
|
[PIPE_AND] = "AND", |
2056 |
|
[PIPE_OR ] = "OR" , |
2057 |
|
[PIPE_BG ] = "BG" , |
2058 |
|
}; |
2059 |
|
static const char *RES[] = { |
2060 |
|
[RES_NONE ] = "NONE" , |
2061 |
|
#if ENABLE_HUSH_IF |
2062 |
|
[RES_IF ] = "IF" , |
2063 |
|
[RES_THEN ] = "THEN" , |
2064 |
|
[RES_ELIF ] = "ELIF" , |
2065 |
|
[RES_ELSE ] = "ELSE" , |
2066 |
|
[RES_FI ] = "FI" , |
2067 |
|
#endif |
2068 |
|
#if ENABLE_HUSH_LOOPS |
2069 |
|
[RES_FOR ] = "FOR" , |
2070 |
|
[RES_WHILE] = "WHILE", |
2071 |
|
[RES_UNTIL] = "UNTIL", |
2072 |
|
[RES_DO ] = "DO" , |
2073 |
|
[RES_DONE ] = "DONE" , |
2074 |
|
#endif |
2075 |
|
#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE |
2076 |
|
[RES_IN ] = "IN" , |
2077 |
|
#endif |
2078 |
|
#if ENABLE_HUSH_CASE |
2079 |
|
[RES_CASE ] = "CASE" , |
2080 |
|
[RES_MATCH] = "MATCH", |
2081 |
|
[RES_CASEI] = "CASEI", |
2082 |
|
[RES_ESAC ] = "ESAC" , |
2083 |
|
#endif |
2084 |
|
[RES_XXXX ] = "XXXX" , |
2085 |
|
[RES_SNTX ] = "SNTX" , |
2086 |
|
}; |
2087 |
|
static const char *const GRPTYPE[] = { |
2088 |
|
"()", |
2089 |
|
"{}", |
2090 |
|
#if ENABLE_HUSH_FUNCTIONS |
2091 |
|
"func()", |
2092 |
|
#endif |
2093 |
|
}; |
2094 |
|
|
2095 |
|
int pin, prn; |
2096 |
|
|
2097 |
|
pin = 0; |
2098 |
|
while (pi) { |
2099 |
|
fprintf(stderr, "%*spipe %d res_word=%s followup=%d %s\n", lvl*2, "", |
2100 |
|
pin, RES[pi->res_word], pi->followup, PIPE[pi->followup]); |
2101 |
|
prn = 0; |
2102 |
|
while (prn < pi->num_cmds) { |
2103 |
|
struct command *command = &pi->cmds[prn]; |
2104 |
|
char **argv = command->argv; |
2105 |
|
|
2106 |
|
fprintf(stderr, "%*s prog %d assignment_cnt:%d", lvl*2, "", prn, command->assignment_cnt); |
2107 |
|
if (command->group) { |
2108 |
|
fprintf(stderr, " group %s: (argv=%p)\n", |
2109 |
|
GRPTYPE[command->grp_type], |
2110 |
|
argv); |
2111 |
|
debug_print_tree(command->group, lvl+1); |
2112 |
|
prn++; |
2113 |
|
continue; |
2114 |
|
} |
2115 |
|
if (argv) while (*argv) { |
2116 |
|
fprintf(stderr, " '%s'", *argv); |
2117 |
|
argv++; |
2118 |
|
} |
2119 |
|
fprintf(stderr, "\n"); |
2120 |
|
prn++; |
2121 |
} |
} |
2122 |
|
pi = pi->next; |
2123 |
|
pin++; |
2124 |
|
} |
2125 |
|
} |
2126 |
|
#endif |
2127 |
|
|
2128 |
/* XXX test for failed fork()? */ |
/* NB: called by pseudo_exec, and therefore must not modify any |
2129 |
#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__) |
* global data until exec/_exit (we can be a child after vfork!) */ |
2130 |
if (!(child->pid = fork())) |
static int run_list(struct pipe *pi) |
2131 |
|
{ |
2132 |
|
#if ENABLE_HUSH_CASE |
2133 |
|
char *case_word = NULL; |
2134 |
|
#endif |
2135 |
|
#if ENABLE_HUSH_LOOPS |
2136 |
|
struct pipe *loop_top = NULL; |
2137 |
|
char *for_varname = NULL; |
2138 |
|
char **for_lcur = NULL; |
2139 |
|
char **for_list = NULL; |
2140 |
|
#endif |
2141 |
|
smallint flag_skip = 1; |
2142 |
|
smalluint rcode = 0; /* probably just for compiler */ |
2143 |
|
#if ENABLE_HUSH_IF || ENABLE_HUSH_CASE |
2144 |
|
smalluint cond_code = 0; |
2145 |
#else |
#else |
2146 |
if (!(child->pid = vfork())) |
enum { cond_code = 0, }; |
2147 |
#endif |
#endif |
2148 |
{ |
/*enum reserved_style*/ smallint rword = RES_NONE; |
2149 |
/* Set the handling for job control signals back to the default. */ |
/*enum reserved_style*/ smallint skip_more_for_this_rword = RES_XXXX; |
2150 |
signal(SIGINT, SIG_DFL); |
|
2151 |
signal(SIGQUIT, SIG_DFL); |
debug_printf_exec("run_list start lvl %d\n", G.run_list_level + 1); |
|
signal(SIGTERM, SIG_DFL); |
|
|
signal(SIGTSTP, SIG_DFL); |
|
|
signal(SIGTTIN, SIG_DFL); |
|
|
signal(SIGTTOU, SIG_DFL); |
|
|
signal(SIGCHLD, SIG_DFL); |
|
2152 |
|
|
2153 |
close_all(); |
#if ENABLE_HUSH_LOOPS |
2154 |
|
/* Check syntax for "for" */ |
2155 |
|
for (struct pipe *cpipe = pi; cpipe; cpipe = cpipe->next) { |
2156 |
|
if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN) |
2157 |
|
continue; |
2158 |
|
/* current word is FOR or IN (BOLD in comments below) */ |
2159 |
|
if (cpipe->next == NULL) { |
2160 |
|
syntax("malformed for"); |
2161 |
|
debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level); |
2162 |
|
return 1; |
2163 |
|
} |
2164 |
|
/* "FOR v; do ..." and "for v IN a b; do..." are ok */ |
2165 |
|
if (cpipe->next->res_word == RES_DO) |
2166 |
|
continue; |
2167 |
|
/* next word is not "do". It must be "in" then ("FOR v in ...") */ |
2168 |
|
if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */ |
2169 |
|
|| cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */ |
2170 |
|
) { |
2171 |
|
syntax("malformed for"); |
2172 |
|
debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level); |
2173 |
|
return 1; |
2174 |
|
} |
2175 |
|
} |
2176 |
|
#endif |
2177 |
|
|
2178 |
if (nextin != 0) { |
/* Past this point, all code paths should jump to ret: label |
2179 |
dup2(nextin, 0); |
* in order to return, no direct "return" statements please. |
2180 |
close(nextin); |
* This helps to ensure that no memory is leaked. */ |
2181 |
} |
|
2182 |
if (nextout != 1) { |
#if ENABLE_HUSH_JOB |
2183 |
dup2(nextout, 1); |
/* Example of nested list: "while true; do { sleep 1 | exit 2; } done". |
2184 |
close(nextout); |
* We are saving state before entering outermost list ("while...done") |
2185 |
|
* so that ctrl-Z will correctly background _entire_ outermost list, |
2186 |
|
* not just a part of it (like "sleep 1 | exit 2") */ |
2187 |
|
if (++G.run_list_level == 1 && G.interactive_fd) { |
2188 |
|
if (sigsetjmp(G.toplevel_jb, 1)) { |
2189 |
|
/* ctrl-Z forked and we are parent; or ctrl-C. |
2190 |
|
* Sighandler has longjmped us here */ |
2191 |
|
signal(SIGINT, SIG_IGN); |
2192 |
|
signal(SIGTSTP, SIG_IGN); |
2193 |
|
/* Restore level (we can be coming from deep inside |
2194 |
|
* nested levels) */ |
2195 |
|
G.run_list_level = 1; |
2196 |
|
#if ENABLE_FEATURE_SH_STANDALONE |
2197 |
|
if (G.nofork_save.saved) { /* if save area is valid */ |
2198 |
|
debug_printf_jobs("exiting nofork early\n"); |
2199 |
|
restore_nofork_data(&G.nofork_save); |
2200 |
} |
} |
2201 |
if (pipefds[0]!=-1) { |
#endif |
2202 |
close(pipefds[0]); /* opposite end of our output pipe */ |
if (G.ctrl_z_flag) { |
2203 |
|
/* ctrl-Z has forked and stored pid of the child in pi->pid. |
2204 |
|
* Remember this child as background job */ |
2205 |
|
insert_bg_job(pi); |
2206 |
|
} else { |
2207 |
|
/* ctrl-C. We just stop doing whatever we were doing */ |
2208 |
|
bb_putchar('\n'); |
2209 |
} |
} |
2210 |
|
USE_HUSH_LOOPS(loop_top = NULL;) |
2211 |
|
USE_HUSH_LOOPS(G.depth_of_loop = 0;) |
2212 |
|
rcode = 0; |
2213 |
|
goto ret; |
2214 |
|
} |
2215 |
|
/* ctrl-Z handler will store pid etc in pi */ |
2216 |
|
G.toplevel_list = pi; |
2217 |
|
G.ctrl_z_flag = 0; |
2218 |
|
#if ENABLE_FEATURE_SH_STANDALONE |
2219 |
|
G.nofork_save.saved = 0; /* in case we will run a nofork later */ |
2220 |
|
#endif |
2221 |
|
signal_SA_RESTART_empty_mask(SIGTSTP, handler_ctrl_z); |
2222 |
|
signal(SIGINT, handler_ctrl_c); |
2223 |
|
} |
2224 |
|
#endif /* JOB */ |
2225 |
|
|
2226 |
/* Like bash, explicit redirects override pipes, |
/* Go through list of pipes, (maybe) executing them. */ |
2227 |
* and the pipe fd is available for dup'ing. */ |
for (; pi; pi = USE_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) { |
2228 |
setup_redirects(child,NULL); |
IF_HAS_KEYWORDS(rword = pi->res_word;) |
2229 |
|
IF_HAS_NO_KEYWORDS(rword = RES_NONE;) |
2230 |
|
debug_printf_exec(": rword=%d cond_code=%d skip_more=%d\n", |
2231 |
|
rword, cond_code, skip_more_for_this_rword); |
2232 |
|
#if ENABLE_HUSH_LOOPS |
2233 |
|
if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) |
2234 |
|
&& loop_top == NULL /* avoid bumping G.depth_of_loop twice */ |
2235 |
|
) { |
2236 |
|
/* start of a loop: remember where loop starts */ |
2237 |
|
loop_top = pi; |
2238 |
|
G.depth_of_loop++; |
2239 |
|
} |
2240 |
|
#endif |
2241 |
|
if (rword == skip_more_for_this_rword && flag_skip) { |
2242 |
|
if (pi->followup == PIPE_SEQ) |
2243 |
|
flag_skip = 0; |
2244 |
|
/* it is "<false> && CMD" or "<true> || CMD" |
2245 |
|
* and we should not execute CMD */ |
2246 |
|
continue; |
2247 |
|
} |
2248 |
|
flag_skip = 1; |
2249 |
|
skip_more_for_this_rword = RES_XXXX; |
2250 |
|
#if ENABLE_HUSH_IF |
2251 |
|
if (cond_code) { |
2252 |
|
if (rword == RES_THEN) { |
2253 |
|
/* "if <false> THEN cmd": skip cmd */ |
2254 |
|
continue; |
2255 |
|
} |
2256 |
|
} else { |
2257 |
|
if (rword == RES_ELSE || rword == RES_ELIF) { |
2258 |
|
/* "if <true> then ... ELSE/ELIF cmd": |
2259 |
|
* skip cmd and all following ones */ |
2260 |
|
break; |
2261 |
|
} |
2262 |
|
} |
2263 |
|
#endif |
2264 |
|
#if ENABLE_HUSH_LOOPS |
2265 |
|
if (rword == RES_FOR) { /* && pi->num_cmds - always == 1 */ |
2266 |
|
if (!for_lcur) { |
2267 |
|
/* first loop through for */ |
2268 |
|
|
2269 |
|
static const char encoded_dollar_at[] ALIGN1 = { |
2270 |
|
SPECIAL_VAR_SYMBOL, '@' | 0x80, SPECIAL_VAR_SYMBOL, '\0' |
2271 |
|
}; /* encoded representation of "$@" */ |
2272 |
|
static const char *const encoded_dollar_at_argv[] = { |
2273 |
|
encoded_dollar_at, NULL |
2274 |
|
}; /* argv list with one element: "$@" */ |
2275 |
|
char **vals; |
2276 |
|
|
2277 |
|
vals = (char**)encoded_dollar_at_argv; |
2278 |
|
if (pi->next->res_word == RES_IN) { |
2279 |
|
/* if no variable values after "in" we skip "for" */ |
2280 |
|
if (!pi->next->cmds[0].argv) |
2281 |
|
break; |
2282 |
|
vals = pi->next->cmds[0].argv; |
2283 |
|
} /* else: "for var; do..." -> assume "$@" list */ |
2284 |
|
/* create list of variable values */ |
2285 |
|
debug_print_strings("for_list made from", vals); |
2286 |
|
for_list = expand_strvec_to_strvec(vals); |
2287 |
|
for_lcur = for_list; |
2288 |
|
debug_print_strings("for_list", for_list); |
2289 |
|
for_varname = pi->cmds[0].argv[0]; |
2290 |
|
pi->cmds[0].argv[0] = NULL; |
2291 |
|
} |
2292 |
|
free(pi->cmds[0].argv[0]); |
2293 |
|
if (!*for_lcur) { |
2294 |
|
/* "for" loop is over, clean up */ |
2295 |
|
free(for_list); |
2296 |
|
for_list = NULL; |
2297 |
|
for_lcur = NULL; |
2298 |
|
pi->cmds[0].argv[0] = for_varname; |
2299 |
|
break; |
2300 |
|
} |
2301 |
|
/* insert next value from for_lcur */ |
2302 |
|
//TODO: does it need escaping? |
2303 |
|
pi->cmds[0].argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++); |
2304 |
|
pi->cmds[0].assignment_cnt = 1; |
2305 |
|
} |
2306 |
|
if (rword == RES_IN) /* "for v IN list;..." - "in" has no cmds anyway */ |
2307 |
|
continue; |
2308 |
|
if (rword == RES_DONE) { |
2309 |
|
continue; /* "done" has no cmds too */ |
2310 |
|
} |
2311 |
|
#endif |
2312 |
|
#if ENABLE_HUSH_CASE |
2313 |
|
if (rword == RES_CASE) { |
2314 |
|
case_word = expand_strvec_to_string(pi->cmds->argv); |
2315 |
|
continue; |
2316 |
|
} |
2317 |
|
if (rword == RES_MATCH) { |
2318 |
|
char **argv; |
2319 |
|
|
2320 |
|
if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */ |
2321 |
|
break; |
2322 |
|
/* all prev words didn't match, does this one match? */ |
2323 |
|
argv = pi->cmds->argv; |
2324 |
|
while (*argv) { |
2325 |
|
char *pattern = expand_string_to_string(*argv); |
2326 |
|
/* TODO: which FNM_xxx flags to use? */ |
2327 |
|
cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0); |
2328 |
|
free(pattern); |
2329 |
|
if (cond_code == 0) { /* match! we will execute this branch */ |
2330 |
|
free(case_word); /* make future "word)" stop */ |
2331 |
|
case_word = NULL; |
2332 |
|
break; |
2333 |
|
} |
2334 |
|
argv++; |
2335 |
|
} |
2336 |
|
continue; |
2337 |
|
} |
2338 |
|
if (rword == RES_CASEI) { /* inside of a case branch */ |
2339 |
|
if (cond_code != 0) |
2340 |
|
continue; /* not matched yet, skip this pipe */ |
2341 |
|
} |
2342 |
|
#endif |
2343 |
|
if (pi->num_cmds == 0) |
2344 |
|
continue; |
2345 |
|
|
2346 |
if (interactive && pi->followup!=PIPE_BG) { |
/* After analyzing all keywords and conditions, we decided |
2347 |
/* If we (the child) win the race, put ourselves in the process |
* to execute this pipe. NB: has to do checkjobs(NULL) |
2348 |
* group whose leader is the first process in this pipe. */ |
* after run_pipe() to collect any background children, |
2349 |
if (pi->pgrp < 0) { |
* even if list execution is to be stopped. */ |
2350 |
pi->pgrp = getpid(); |
debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds); |
2351 |
|
{ |
2352 |
|
int r; |
2353 |
|
#if ENABLE_HUSH_LOOPS |
2354 |
|
G.flag_break_continue = 0; |
2355 |
|
#endif |
2356 |
|
rcode = r = run_pipe(pi); /* NB: rcode is a smallint */ |
2357 |
|
if (r != -1) { |
2358 |
|
/* we only ran a builtin: rcode is already known |
2359 |
|
* and we don't need to wait for anything. */ |
2360 |
|
#if ENABLE_HUSH_LOOPS |
2361 |
|
/* was it "break" or "continue"? */ |
2362 |
|
if (G.flag_break_continue) { |
2363 |
|
smallint fbc = G.flag_break_continue; |
2364 |
|
/* we might fall into outer *loop*, |
2365 |
|
* don't want to break it too */ |
2366 |
|
if (loop_top) { |
2367 |
|
G.depth_break_continue--; |
2368 |
|
if (G.depth_break_continue == 0) |
2369 |
|
G.flag_break_continue = 0; |
2370 |
|
/* else: e.g. "continue 2" should *break* once, *then* continue */ |
2371 |
|
} /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */ |
2372 |
|
if (G.depth_break_continue != 0 || fbc == BC_BREAK) |
2373 |
|
goto check_jobs_and_break; |
2374 |
|
/* "continue": simulate end of loop */ |
2375 |
|
rword = RES_DONE; |
2376 |
|
continue; |
2377 |
} |
} |
2378 |
if (setpgid(0, pi->pgrp) == 0) { |
#endif |
2379 |
tcsetpgrp(2, pi->pgrp); |
} else if (pi->followup == PIPE_BG) { |
2380 |
|
/* what does bash do with attempts to background builtins? */ |
2381 |
|
/* even bash 3.2 doesn't do that well with nested bg: |
2382 |
|
* try "{ { sleep 10; echo DEEP; } & echo HERE; } &". |
2383 |
|
* I'm NOT treating inner &'s as jobs */ |
2384 |
|
#if ENABLE_HUSH_JOB |
2385 |
|
if (G.run_list_level == 1) |
2386 |
|
insert_bg_job(pi); |
2387 |
|
#endif |
2388 |
|
rcode = 0; /* EXIT_SUCCESS */ |
2389 |
|
} else { |
2390 |
|
#if ENABLE_HUSH_JOB |
2391 |
|
if (G.run_list_level == 1 && G.interactive_fd) { |
2392 |
|
/* waits for completion, then fg's main shell */ |
2393 |
|
rcode = checkjobs_and_fg_shell(pi); |
2394 |
|
debug_printf_exec(": checkjobs_and_fg_shell returned %d\n", rcode); |
2395 |
|
} else |
2396 |
|
#endif |
2397 |
|
{ /* this one just waits for completion */ |
2398 |
|
rcode = checkjobs(pi); |
2399 |
|
debug_printf_exec(": checkjobs returned %d\n", rcode); |
2400 |
} |
} |
2401 |
} |
} |
2402 |
|
} |
2403 |
|
debug_printf_exec(": setting last_return_code=%d\n", rcode); |
2404 |
|
G.last_return_code = rcode; |
2405 |
|
|
2406 |
pseudo_exec(child); |
/* Analyze how result affects subsequent commands */ |
2407 |
|
#if ENABLE_HUSH_IF |
2408 |
|
if (rword == RES_IF || rword == RES_ELIF) |
2409 |
|
cond_code = rcode; |
2410 |
|
#endif |
2411 |
|
#if ENABLE_HUSH_LOOPS |
2412 |
|
if (rword == RES_WHILE) { |
2413 |
|
if (rcode) { |
2414 |
|
rcode = 0; /* "while false; do...done" - exitcode 0 */ |
2415 |
|
goto check_jobs_and_break; |
2416 |
|
} |
2417 |
|
} |
2418 |
|
if (rword == RES_UNTIL) { |
2419 |
|
if (!rcode) { |
2420 |
|
check_jobs_and_break: |
2421 |
|
checkjobs(NULL); |
2422 |
|
break; |
2423 |
|
} |
2424 |
} |
} |
2425 |
|
#endif |
2426 |
|
if ((rcode == 0 && pi->followup == PIPE_OR) |
2427 |
|
|| (rcode != 0 && pi->followup == PIPE_AND) |
2428 |
|
) { |
2429 |
|
skip_more_for_this_rword = rword; |
2430 |
|
} |
2431 |
|
checkjobs(NULL); |
2432 |
|
} /* for (pi) */ |
2433 |
|
|
2434 |
|
#if ENABLE_HUSH_JOB |
2435 |
|
if (G.ctrl_z_flag) { |
2436 |
|
/* ctrl-Z forked somewhere in the past, we are the child, |
2437 |
|
* and now we completed running the list. Exit. */ |
2438 |
|
//TODO: _exit? |
2439 |
|
exit(rcode); |
2440 |
|
} |
2441 |
|
ret: |
2442 |
|
if (!--G.run_list_level && G.interactive_fd) { |
2443 |
|
signal(SIGTSTP, SIG_IGN); |
2444 |
|
signal(SIGINT, SIG_IGN); |
2445 |
|
} |
2446 |
|
#endif |
2447 |
|
debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level + 1, rcode); |
2448 |
|
#if ENABLE_HUSH_LOOPS |
2449 |
|
if (loop_top) |
2450 |
|
G.depth_of_loop--; |
2451 |
|
free(for_list); |
2452 |
|
#endif |
2453 |
|
#if ENABLE_HUSH_CASE |
2454 |
|
free(case_word); |
2455 |
|
#endif |
2456 |
|
return rcode; |
2457 |
|
} |
2458 |
|
|
2459 |
|
/* return code is the exit status of the pipe */ |
2460 |
|
static int free_pipe(struct pipe *pi, int indent) |
2461 |
|
{ |
2462 |
|
char **p; |
2463 |
|
struct command *command; |
2464 |
|
struct redir_struct *r, *rnext; |
2465 |
|
int a, i, ret_code = 0; |
2466 |
|
|
2467 |
/* put our child in the process group whose leader is the |
if (pi->stopped_cmds > 0) |
2468 |
first process in this pipe */ |
return ret_code; |
2469 |
if (pi->pgrp < 0) { |
debug_printf_clean("%s run pipe: (pid %d)\n", indenter(indent), getpid()); |
2470 |
pi->pgrp = child->pid; |
for (i = 0; i < pi->num_cmds; i++) { |
2471 |
|
command = &pi->cmds[i]; |
2472 |
|
debug_printf_clean("%s command %d:\n", indenter(indent), i); |
2473 |
|
if (command->argv) { |
2474 |
|
for (a = 0, p = command->argv; *p; a++, p++) { |
2475 |
|
debug_printf_clean("%s argv[%d] = %s\n", indenter(indent), a, *p); |
2476 |
|
} |
2477 |
|
free_strings(command->argv); |
2478 |
|
command->argv = NULL; |
2479 |
|
} else if (command->group) { |
2480 |
|
debug_printf_clean("%s begin group (grp_type:%d)\n", indenter(indent), command->grp_type); |
2481 |
|
ret_code = free_pipe_list(command->group, indent+3); |
2482 |
|
debug_printf_clean("%s end group\n", indenter(indent)); |
2483 |
|
} else { |
2484 |
|
debug_printf_clean("%s (nil)\n", indenter(indent)); |
2485 |
|
} |
2486 |
|
for (r = command->redirects; r; r = rnext) { |
2487 |
|
debug_printf_clean("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->rd_type].descrip); |
2488 |
|
if (r->dup == -1) { |
2489 |
|
/* guard against the case >$FOO, where foo is unset or blank */ |
2490 |
|
if (r->rd_filename) { |
2491 |
|
debug_printf_clean(" %s\n", r->rd_filename); |
2492 |
|
free(r->rd_filename); |
2493 |
|
r->rd_filename = NULL; |
2494 |
|
} |
2495 |
|
} else { |
2496 |
|
debug_printf_clean("&%d\n", r->dup); |
2497 |
|
} |
2498 |
|
rnext = r->next; |
2499 |
|
free(r); |
2500 |
} |
} |
2501 |
/* Don't check for errors. The child may be dead already, |
command->redirects = NULL; |
2502 |
* in which case setpgid returns error code EACCES. */ |
} |
2503 |
setpgid(child->pid, pi->pgrp); |
free(pi->cmds); /* children are an array, they get freed all at once */ |
2504 |
|
pi->cmds = NULL; |
2505 |
|
#if ENABLE_HUSH_JOB |
2506 |
|
free(pi->cmdtext); |
2507 |
|
pi->cmdtext = NULL; |
2508 |
|
#endif |
2509 |
|
return ret_code; |
2510 |
|
} |
2511 |
|
|
2512 |
if (nextin != 0) |
static int free_pipe_list(struct pipe *head, int indent) |
2513 |
close(nextin); |
{ |
2514 |
if (nextout != 1) |
int rcode = 0; /* if list has no members */ |
2515 |
close(nextout); |
struct pipe *pi, *next; |
2516 |
|
|
2517 |
/* If there isn't another process, nextin is garbage |
for (pi = head; pi; pi = next) { |
2518 |
but it doesn't matter */ |
#if HAS_KEYWORDS |
2519 |
nextin = pipefds[0]; |
debug_printf_clean("%s pipe reserved mode %d\n", indenter(indent), pi->res_word); |
2520 |
|
#endif |
2521 |
|
rcode = free_pipe(pi, indent); |
2522 |
|
debug_printf_clean("%s pipe followup code %d\n", indenter(indent), pi->followup); |
2523 |
|
next = pi->next; |
2524 |
|
/*pi->next = NULL;*/ |
2525 |
|
free(pi); |
2526 |
} |
} |
2527 |
return -1; |
return rcode; |
2528 |
} |
} |
2529 |
|
|
2530 |
static int run_list_real(struct pipe *pi) |
/* Select which version we will use */ |
2531 |
|
static int run_and_free_list(struct pipe *pi) |
2532 |
{ |
{ |
2533 |
char *save_name = NULL; |
int rcode = 0; |
2534 |
char **list = NULL; |
debug_printf_exec("run_and_free_list entered\n"); |
2535 |
char **save_list = NULL; |
if (!G.fake_mode) { |
2536 |
struct pipe *rpipe; |
debug_printf_exec(": run_list with %d members\n", pi->num_cmds); |
2537 |
int flag_rep = 0; |
rcode = run_list(pi); |
|
int save_num_progs; |
|
|
int rcode=0, flag_skip=1; |
|
|
int flag_restore = 0; |
|
|
int if_code=0, next_if_code=0; /* need double-buffer to handle elif */ |
|
|
reserved_style rmode, skip_more_in_this_rmode=RES_XXXX; |
|
|
/* check syntax for "for" */ |
|
|
for (rpipe = pi; rpipe; rpipe = rpipe->next) { |
|
|
if ((rpipe->r_mode == RES_IN || |
|
|
rpipe->r_mode == RES_FOR) && |
|
|
(rpipe->next == NULL)) { |
|
|
syntax(); |
|
|
return 1; |
|
|
} |
|
|
if ((rpipe->r_mode == RES_IN && |
|
|
(rpipe->next->r_mode == RES_IN && |
|
|
rpipe->next->progs->argv != NULL))|| |
|
|
(rpipe->r_mode == RES_FOR && |
|
|
rpipe->next->r_mode != RES_IN)) { |
|
|
syntax(); |
|
|
return 1; |
|
|
} |
|
2538 |
} |
} |
2539 |
for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) { |
/* free_pipe_list has the side effect of clearing memory. |
2540 |
if (pi->r_mode == RES_WHILE || pi->r_mode == RES_UNTIL || |
* In the long run that function can be merged with run_list, |
2541 |
pi->r_mode == RES_FOR) { |
* but doing that now would hobble the debugging effort. */ |
2542 |
flag_restore = 0; |
free_pipe_list(pi, /* indent: */ 0); |
2543 |
if (!rpipe) { |
debug_printf_exec("run_and_free_list return %d\n", rcode); |
2544 |
flag_rep = 0; |
return rcode; |
2545 |
rpipe = pi; |
} |
2546 |
} |
|
2547 |
} |
|
2548 |
rmode = pi->r_mode; |
/* expand_strvec_to_strvec() takes a list of strings, expands |
2549 |
debug_printf("rmode=%d if_code=%d next_if_code=%d skip_more=%d\n", rmode, if_code, next_if_code, skip_more_in_this_rmode); |
* all variable references within and returns a pointer to |
2550 |
if (rmode == skip_more_in_this_rmode && flag_skip) { |
* a list of expanded strings, possibly with larger number |
2551 |
if (pi->followup == PIPE_SEQ) flag_skip=0; |
* of strings. (Think VAR="a b"; echo $VAR). |
2552 |
continue; |
* This new list is allocated as a single malloc block. |
2553 |
|
* NULL-terminated list of char* pointers is at the beginning of it, |
2554 |
|
* followed by strings themself. |
2555 |
|
* Caller can deallocate entire list by single free(list). */ |
2556 |
|
|
2557 |
|
/* Store given string, finalizing the word and starting new one whenever |
2558 |
|
* we encounter IFS char(s). This is used for expanding variable values. |
2559 |
|
* End-of-string does NOT finalize word: think about 'echo -$VAR-' */ |
2560 |
|
static int expand_on_ifs(o_string *output, int n, const char *str) |
2561 |
|
{ |
2562 |
|
while (1) { |
2563 |
|
int word_len = strcspn(str, G.ifs); |
2564 |
|
if (word_len) { |
2565 |
|
if (output->o_quote || !output->o_glob) |
2566 |
|
o_addQstr(output, str, word_len); |
2567 |
|
else /* protect backslashes against globbing up :) */ |
2568 |
|
o_addstr_duplicate_backslash(output, str, word_len); |
2569 |
|
str += word_len; |
2570 |
} |
} |
2571 |
flag_skip = 1; |
if (!*str) /* EOL - do not finalize word */ |
2572 |
skip_more_in_this_rmode = RES_XXXX; |
break; |
2573 |
if (rmode == RES_THEN || rmode == RES_ELSE) if_code = next_if_code; |
o_addchr(output, '\0'); |
2574 |
if (rmode == RES_THEN && if_code) continue; |
debug_print_list("expand_on_ifs", output, n); |
2575 |
if (rmode == RES_ELSE && !if_code) continue; |
n = o_save_ptr(output, n); |
2576 |
if (rmode == RES_ELIF && !if_code) break; |
str += strspn(str, G.ifs); /* skip ifs chars */ |
2577 |
if (rmode == RES_FOR && pi->num_progs) { |
} |
2578 |
if (!list) { |
debug_print_list("expand_on_ifs[1]", output, n); |
2579 |
/* if no variable values after "in" we skip "for" */ |
return n; |
2580 |
if (!pi->next->progs->argv) continue; |
} |
2581 |
/* create list of variable values */ |
|
2582 |
list = make_list_in(pi->next->progs->argv, |
/* Expand all variable references in given string, adding words to list[] |
2583 |
pi->progs->argv[0]); |
* at n, n+1,... positions. Return updated n (so that list[n] is next one |
2584 |
save_list = list; |
* to be filled). This routine is extremely tricky: has to deal with |
2585 |
save_name = pi->progs->argv[0]; |
* variables/parameters with whitespace, $* and $@, and constructs like |
2586 |
pi->progs->argv[0] = NULL; |
* 'echo -$*-'. If you play here, you must run testsuite afterwards! */ |
2587 |
flag_rep = 1; |
static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) |
2588 |
} |
{ |
2589 |
if (!(*list)) { |
/* or_mask is either 0 (normal case) or 0x80 |
2590 |
free(pi->progs->argv[0]); |
* (expansion of right-hand side of assignment == 1-element expand. |
2591 |
free(save_list); |
* It will also do no globbing, and thus we must not backslash-quote!) */ |
2592 |
list = NULL; |
|
2593 |
flag_rep = 0; |
char first_ch, ored_ch; |
2594 |
pi->progs->argv[0] = save_name; |
int i; |
2595 |
pi->progs->glob_result.gl_pathv[0] = |
const char *val; |
2596 |
pi->progs->argv[0]; |
char *p; |
2597 |
continue; |
|
2598 |
} else { |
ored_ch = 0; |
2599 |
/* insert new value from list for variable */ |
|
2600 |
if (pi->progs->argv[0]) |
debug_printf_expand("expand_vars_to_list: arg '%s'\n", arg); |
2601 |
free(pi->progs->argv[0]); |
debug_print_list("expand_vars_to_list", output, n); |
2602 |
pi->progs->argv[0] = *list++; |
n = o_save_ptr(output, n); |
2603 |
pi->progs->glob_result.gl_pathv[0] = |
debug_print_list("expand_vars_to_list[0]", output, n); |
2604 |
pi->progs->argv[0]; |
|
2605 |
|
while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { |
2606 |
|
#if ENABLE_HUSH_TICK |
2607 |
|
o_string subst_result = NULL_O_STRING; |
2608 |
|
#endif |
2609 |
|
o_addstr(output, arg, p - arg); |
2610 |
|
debug_print_list("expand_vars_to_list[1]", output, n); |
2611 |
|
arg = ++p; |
2612 |
|
p = strchr(p, SPECIAL_VAR_SYMBOL); |
2613 |
|
|
2614 |
|
first_ch = arg[0] | or_mask; /* forced to "quoted" if or_mask = 0x80 */ |
2615 |
|
/* "$@" is special. Even if quoted, it can still |
2616 |
|
* expand to nothing (not even an empty string) */ |
2617 |
|
if ((first_ch & 0x7f) != '@') |
2618 |
|
ored_ch |= first_ch; |
2619 |
|
val = NULL; |
2620 |
|
switch (first_ch & 0x7f) { |
2621 |
|
/* Highest bit in first_ch indicates that var is double-quoted */ |
2622 |
|
case '$': /* pid */ |
2623 |
|
val = utoa(G.root_pid); |
2624 |
|
break; |
2625 |
|
case '!': /* bg pid */ |
2626 |
|
val = G.last_bg_pid ? utoa(G.last_bg_pid) : (char*)""; |
2627 |
|
break; |
2628 |
|
case '?': /* exitcode */ |
2629 |
|
val = utoa(G.last_return_code); |
2630 |
|
break; |
2631 |
|
case '#': /* argc */ |
2632 |
|
val = utoa(G.global_argc ? G.global_argc-1 : 0); |
2633 |
|
break; |
2634 |
|
case '*': |
2635 |
|
case '@': |
2636 |
|
i = 1; |
2637 |
|
if (!G.global_argv[i]) |
2638 |
|
break; |
2639 |
|
ored_ch |= first_ch; /* do it for "$@" _now_, when we know it's not empty */ |
2640 |
|
if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ |
2641 |
|
smallint sv = output->o_quote; |
2642 |
|
/* unquoted var's contents should be globbed, so don't quote */ |
2643 |
|
output->o_quote = 0; |
2644 |
|
while (G.global_argv[i]) { |
2645 |
|
n = expand_on_ifs(output, n, G.global_argv[i]); |
2646 |
|
debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1); |
2647 |
|
if (G.global_argv[i++][0] && G.global_argv[i]) { |
2648 |
|
/* this argv[] is not empty and not last: |
2649 |
|
* put terminating NUL, start new word */ |
2650 |
|
o_addchr(output, '\0'); |
2651 |
|
debug_print_list("expand_vars_to_list[2]", output, n); |
2652 |
|
n = o_save_ptr(output, n); |
2653 |
|
debug_print_list("expand_vars_to_list[3]", output, n); |
2654 |
|
} |
2655 |
|
} |
2656 |
|
output->o_quote = sv; |
2657 |
|
} else |
2658 |
|
/* If or_mask is nonzero, we handle assignment 'a=....$@.....' |
2659 |
|
* and in this case should treat it like '$*' - see 'else...' below */ |
2660 |
|
if (first_ch == ('@'|0x80) && !or_mask) { /* quoted $@ */ |
2661 |
|
while (1) { |
2662 |
|
o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i])); |
2663 |
|
if (++i >= G.global_argc) |
2664 |
|
break; |
2665 |
|
o_addchr(output, '\0'); |
2666 |
|
debug_print_list("expand_vars_to_list[4]", output, n); |
2667 |
|
n = o_save_ptr(output, n); |
2668 |
|
} |
2669 |
|
} else { /* quoted $*: add as one word */ |
2670 |
|
while (1) { |
2671 |
|
o_addQstr(output, G.global_argv[i], strlen(G.global_argv[i])); |
2672 |
|
if (!G.global_argv[++i]) |
2673 |
|
break; |
2674 |
|
if (G.ifs[0]) |
2675 |
|
o_addchr(output, G.ifs[0]); |
2676 |
|
} |
2677 |
} |
} |
2678 |
|
break; |
2679 |
|
case SPECIAL_VAR_SYMBOL: /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */ |
2680 |
|
/* "Empty variable", used to make "" etc to not disappear */ |
2681 |
|
arg++; |
2682 |
|
ored_ch = 0x80; |
2683 |
|
break; |
2684 |
|
#if ENABLE_HUSH_TICK |
2685 |
|
case '`': { /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ |
2686 |
|
struct in_str input; |
2687 |
|
*p = '\0'; |
2688 |
|
arg++; |
2689 |
|
//TODO: can we just stuff it into "output" directly? |
2690 |
|
debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch); |
2691 |
|
setup_string_in_str(&input, arg); |
2692 |
|
process_command_subs(&subst_result, &input, NULL); |
2693 |
|
debug_printf_subst("SUBST RES '%s'\n", subst_result.data); |
2694 |
|
val = subst_result.data; |
2695 |
|
goto store_val; |
2696 |
} |
} |
2697 |
if (rmode == RES_IN) continue; |
#endif |
2698 |
if (rmode == RES_DO) { |
default: /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */ |
2699 |
if (!flag_rep) continue; |
*p = '\0'; |
2700 |
} |
arg[0] = first_ch & 0x7f; |
2701 |
if ((rmode == RES_DONE)) { |
if (isdigit(arg[0])) { |
2702 |
if (flag_rep) { |
i = xatoi_u(arg); |
2703 |
flag_restore = 1; |
if (i < G.global_argc) |
2704 |
} else { |
val = G.global_argv[i]; |
2705 |
rpipe = NULL; |
/* else val remains NULL: $N with too big N */ |
2706 |
|
} else |
2707 |
|
val = lookup_param(arg); |
2708 |
|
arg[0] = first_ch; |
2709 |
|
#if ENABLE_HUSH_TICK |
2710 |
|
store_val: |
2711 |
|
#endif |
2712 |
|
*p = SPECIAL_VAR_SYMBOL; |
2713 |
|
if (!(first_ch & 0x80)) { /* unquoted $VAR */ |
2714 |
|
debug_printf_expand("unquoted '%s', output->o_quote:%d\n", val, output->o_quote); |
2715 |
|
if (val) { |
2716 |
|
/* unquoted var's contents should be globbed, so don't quote */ |
2717 |
|
smallint sv = output->o_quote; |
2718 |
|
output->o_quote = 0; |
2719 |
|
n = expand_on_ifs(output, n, val); |
2720 |
|
val = NULL; |
2721 |
|
output->o_quote = sv; |
2722 |
|
} |
2723 |
|
} else { /* quoted $VAR, val will be appended below */ |
2724 |
|
debug_printf_expand("quoted '%s', output->o_quote:%d\n", val, output->o_quote); |
2725 |
} |
} |
2726 |
} |
} |
2727 |
if (pi->num_progs == 0) continue; |
if (val) { |
2728 |
save_num_progs = pi->num_progs; /* save number of programs */ |
o_addQstr(output, val, strlen(val)); |
|
rcode = run_pipe_real(pi); |
|
|
debug_printf("run_pipe_real returned %d\n",rcode); |
|
|
if (rcode!=-1) { |
|
|
/* We only ran a builtin: rcode was set by the return value |
|
|
* of run_pipe_real(), and we don't need to wait for anything. */ |
|
|
} else if (pi->followup==PIPE_BG) { |
|
|
/* XXX check bash's behavior with nontrivial pipes */ |
|
|
/* XXX compute jobid */ |
|
|
/* XXX what does bash do with attempts to background builtins? */ |
|
|
insert_bg_job(pi); |
|
|
rcode = EXIT_SUCCESS; |
|
|
} else { |
|
|
if (interactive) { |
|
|
/* move the new process group into the foreground */ |
|
|
if (tcsetpgrp(shell_terminal, pi->pgrp) && errno != ENOTTY) |
|
|
bb_perror_msg("tcsetpgrp-3"); |
|
|
rcode = checkjobs(pi); |
|
|
/* move the shell to the foreground */ |
|
|
if (tcsetpgrp(shell_terminal, getpgid(0)) && errno != ENOTTY) |
|
|
bb_perror_msg("tcsetpgrp-4"); |
|
|
} else { |
|
|
rcode = checkjobs(pi); |
|
|
} |
|
|
debug_printf("checkjobs returned %d\n",rcode); |
|
2729 |
} |
} |
|
last_return_code=rcode; |
|
|
pi->num_progs = save_num_progs; /* restore number of programs */ |
|
|
if ( rmode == RES_IF || rmode == RES_ELIF ) |
|
|
next_if_code=rcode; /* can be overwritten a number of times */ |
|
|
if (rmode == RES_WHILE) |
|
|
flag_rep = !last_return_code; |
|
|
if (rmode == RES_UNTIL) |
|
|
flag_rep = last_return_code; |
|
|
if ( (rcode==EXIT_SUCCESS && pi->followup==PIPE_OR) || |
|
|
(rcode!=EXIT_SUCCESS && pi->followup==PIPE_AND) ) |
|
|
skip_more_in_this_rmode=rmode; |
|
|
checkjobs(NULL); |
|
|
} |
|
|
return rcode; |
|
|
} |
|
2730 |
|
|
2731 |
/* return code is the exit status of the pipe */ |
#if ENABLE_HUSH_TICK |
2732 |
static int free_pipe(struct pipe *pi, int indent) |
o_free(&subst_result); |
2733 |
{ |
#endif |
2734 |
char **p; |
arg = ++p; |
2735 |
struct child_prog *child; |
} /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */ |
|
struct redir_struct *r, *rnext; |
|
|
int a, i, ret_code=0; |
|
2736 |
|
|
2737 |
if (pi->stopped_progs > 0) |
if (arg[0]) { |
2738 |
return ret_code; |
debug_print_list("expand_vars_to_list[a]", output, n); |
2739 |
final_printf("%s run pipe: (pid %d)\n",indenter(indent),getpid()); |
/* this part is literal, and it was already pre-quoted |
2740 |
for (i=0; i<pi->num_progs; i++) { |
* if needed (much earlier), do not use o_addQstr here! */ |
2741 |
child = &pi->progs[i]; |
o_addstr(output, arg, strlen(arg) + 1); |
2742 |
final_printf("%s command %d:\n",indenter(indent),i); |
debug_print_list("expand_vars_to_list[b]", output, n); |
2743 |
if (child->argv) { |
} else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ |
2744 |
for (a=0,p=child->argv; *p; a++,p++) { |
&& !(ored_ch & 0x80) /* and all vars were not quoted. */ |
2745 |
final_printf("%s argv[%d] = %s\n",indenter(indent),a,*p); |
) { |
2746 |
} |
n--; |
2747 |
globfree(&child->glob_result); |
/* allow to reuse list[n] later without re-growth */ |
2748 |
child->argv=NULL; |
output->has_empty_slot = 1; |
2749 |
} else if (child->group) { |
} else { |
2750 |
final_printf("%s begin group (subshell:%d)\n",indenter(indent), child->subshell); |
o_addchr(output, '\0'); |
|
ret_code = free_pipe_list(child->group,indent+3); |
|
|
final_printf("%s end group\n",indenter(indent)); |
|
|
} else { |
|
|
final_printf("%s (nil)\n",indenter(indent)); |
|
|
} |
|
|
for (r=child->redirects; r; r=rnext) { |
|
|
final_printf("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->type].descrip); |
|
|
if (r->dup == -1) { |
|
|
/* guard against the case >$FOO, where foo is unset or blank */ |
|
|
if (r->word.gl_pathv) { |
|
|
final_printf(" %s\n", *r->word.gl_pathv); |
|
|
globfree(&r->word); |
|
|
} |
|
|
} else { |
|
|
final_printf("&%d\n", r->dup); |
|
|
} |
|
|
rnext=r->next; |
|
|
free(r); |
|
|
} |
|
|
child->redirects=NULL; |
|
2751 |
} |
} |
2752 |
free(pi->progs); /* children are an array, they get freed all at once */ |
return n; |
|
pi->progs=NULL; |
|
|
return ret_code; |
|
2753 |
} |
} |
2754 |
|
|
2755 |
static int free_pipe_list(struct pipe *head, int indent) |
static char **expand_variables(char **argv, int or_mask) |
2756 |
{ |
{ |
2757 |
int rcode=0; /* if list has no members */ |
int n; |
2758 |
struct pipe *pi, *next; |
char **list; |
2759 |
for (pi=head; pi; pi=next) { |
char **v; |
2760 |
final_printf("%s pipe reserved mode %d\n", indenter(indent), pi->r_mode); |
o_string output = NULL_O_STRING; |
|
rcode = free_pipe(pi, indent); |
|
|
final_printf("%s pipe followup code %d\n", indenter(indent), pi->followup); |
|
|
next=pi->next; |
|
|
pi->next=NULL; |
|
|
free(pi); |
|
|
} |
|
|
return rcode; |
|
|
} |
|
2761 |
|
|
2762 |
/* Select which version we will use */ |
if (or_mask & 0x100) { |
2763 |
static int run_list(struct pipe *pi) |
output.o_quote = 1; /* protect against globbing for "$var" */ |
2764 |
{ |
/* (unquoted $var will temporarily switch it off) */ |
2765 |
int rcode=0; |
output.o_glob = 1; |
2766 |
if (fake_mode==0) { |
} |
2767 |
rcode = run_list_real(pi); |
|
2768 |
} |
n = 0; |
2769 |
/* free_pipe_list has the side effect of clearing memory |
v = argv; |
2770 |
* In the long run that function can be merged with run_list_real, |
while (*v) { |
2771 |
* but doing that now would hobble the debugging effort. */ |
n = expand_vars_to_list(&output, n, *v, (char)or_mask); |
2772 |
free_pipe_list(pi,0); |
v++; |
2773 |
return rcode; |
} |
2774 |
|
debug_print_list("expand_variables", &output, n); |
2775 |
|
|
2776 |
|
/* output.data (malloced in one block) gets returned in "list" */ |
2777 |
|
list = o_finalize_list(&output, n); |
2778 |
|
debug_print_strings("expand_variables[1]", list); |
2779 |
|
return list; |
2780 |
} |
} |
2781 |
|
|
2782 |
/* The API for glob is arguably broken. This routine pushes a non-matching |
static char **expand_strvec_to_strvec(char **argv) |
|
* string into the output structure, removing non-backslashed backslashes. |
|
|
* If someone can prove me wrong, by performing this function within the |
|
|
* original glob(3) api, feel free to rewrite this routine into oblivion. |
|
|
* Return code (0 vs. GLOB_NOSPACE) matches glob(3). |
|
|
* XXX broken if the last character is '\\', check that before calling. |
|
|
*/ |
|
|
static int globhack(const char *src, int flags, glob_t *pglob) |
|
2783 |
{ |
{ |
2784 |
int cnt=0, pathc; |
return expand_variables(argv, 0x100); |
|
const char *s; |
|
|
char *dest; |
|
|
for (cnt=1, s=src; s && *s; s++) { |
|
|
if (*s == '\\') s++; |
|
|
cnt++; |
|
|
} |
|
|
dest = malloc(cnt); |
|
|
if (!dest) return GLOB_NOSPACE; |
|
|
if (!(flags & GLOB_APPEND)) { |
|
|
pglob->gl_pathv=NULL; |
|
|
pglob->gl_pathc=0; |
|
|
pglob->gl_offs=0; |
|
|
pglob->gl_offs=0; |
|
|
} |
|
|
pathc = ++pglob->gl_pathc; |
|
|
pglob->gl_pathv = realloc(pglob->gl_pathv, (pathc+1)*sizeof(*pglob->gl_pathv)); |
|
|
if (pglob->gl_pathv == NULL) return GLOB_NOSPACE; |
|
|
pglob->gl_pathv[pathc-1]=dest; |
|
|
pglob->gl_pathv[pathc]=NULL; |
|
|
for (s=src; s && *s; s++, dest++) { |
|
|
if (*s == '\\') s++; |
|
|
*dest = *s; |
|
|
} |
|
|
*dest='\0'; |
|
|
return 0; |
|
2785 |
} |
} |
2786 |
|
|
2787 |
/* XXX broken if the last character is '\\', check that before calling */ |
/* Used for expansion of right hand of assignments */ |
2788 |
static int glob_needed(const char *s) |
/* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs |
2789 |
|
* "v=/bin/c*" */ |
2790 |
|
static char *expand_string_to_string(const char *str) |
2791 |
{ |
{ |
2792 |
for (; *s; s++) { |
char *argv[2], **list; |
2793 |
if (*s == '\\') s++; |
|
2794 |
if (strchr("*[?",*s)) return 1; |
argv[0] = (char*)str; |
2795 |
} |
argv[1] = NULL; |
2796 |
return 0; |
list = expand_variables(argv, 0x80); /* 0x80: make one-element expansion */ |
2797 |
|
if (HUSH_DEBUG) |
2798 |
|
if (!list[0] || list[1]) |
2799 |
|
bb_error_msg_and_die("BUG in varexp2"); |
2800 |
|
/* actually, just move string 2*sizeof(char*) bytes back */ |
2801 |
|
strcpy((char*)list, list[0]); |
2802 |
|
debug_printf_expand("string_to_string='%s'\n", (char*)list); |
2803 |
|
return (char*)list; |
2804 |
} |
} |
2805 |
|
|
2806 |
static int xglob(o_string *dest, int flags, glob_t *pglob) |
/* Used for "eval" builtin */ |
2807 |
|
static char* expand_strvec_to_string(char **argv) |
2808 |
{ |
{ |
2809 |
int gr; |
char **list; |
2810 |
|
|
2811 |
/* short-circuit for null word */ |
list = expand_variables(argv, 0x80); |
2812 |
/* we can code this better when the debug_printf's are gone */ |
/* Convert all NULs to spaces */ |
2813 |
if (dest->length == 0) { |
if (list[0]) { |
2814 |
if (dest->nonnull) { |
int n = 1; |
2815 |
/* bash man page calls this an "explicit" null */ |
while (list[n]) { |
2816 |
gr = globhack(dest->data, flags, pglob); |
if (HUSH_DEBUG) |
2817 |
debug_printf("globhack returned %d\n",gr); |
if (list[n-1] + strlen(list[n-1]) + 1 != list[n]) |
2818 |
} else { |
bb_error_msg_and_die("BUG in varexp3"); |
2819 |
return 0; |
list[n][-1] = ' '; /* TODO: or to G.ifs[0]? */ |
2820 |
|
n++; |
2821 |
} |
} |
|
} else if (glob_needed(dest->data)) { |
|
|
gr = glob(dest->data, flags, NULL, pglob); |
|
|
debug_printf("glob returned %d\n",gr); |
|
|
if (gr == GLOB_NOMATCH) { |
|
|
/* quote removal, or more accurately, backslash removal */ |
|
|
gr = globhack(dest->data, flags, pglob); |
|
|
debug_printf("globhack returned %d\n",gr); |
|
|
} |
|
|
} else { |
|
|
gr = globhack(dest->data, flags, pglob); |
|
|
debug_printf("globhack returned %d\n",gr); |
|
|
} |
|
|
if (gr == GLOB_NOSPACE) |
|
|
bb_error_msg_and_die("out of memory during glob"); |
|
|
if (gr != 0) { /* GLOB_ABORTED ? */ |
|
|
bb_error_msg("glob(3) error %d",gr); |
|
2822 |
} |
} |
2823 |
/* globprint(glob_target); */ |
strcpy((char*)list, list[0]); |
2824 |
return gr; |
debug_printf_expand("strvec_to_string='%s'\n", (char*)list); |
2825 |
|
return (char*)list; |
2826 |
} |
} |
2827 |
|
|
2828 |
/* This is used to get/check local shell variables */ |
|
2829 |
static char *get_local_var(const char *s) |
/* Used to get/check local shell variables */ |
2830 |
|
static struct variable *get_local_var(const char *name) |
2831 |
{ |
{ |
2832 |
struct variables *cur; |
struct variable *cur; |
2833 |
|
int len; |
2834 |
|
|
2835 |
if (!s) |
if (!name) |
2836 |
return NULL; |
return NULL; |
2837 |
for (cur = top_vars; cur; cur=cur->next) |
len = strlen(name); |
2838 |
if(strcmp(cur->name, s)==0) |
for (cur = G.top_var; cur; cur = cur->next) { |
2839 |
return cur->value; |
if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=') |
2840 |
|
return cur; |
2841 |
|
} |
2842 |
return NULL; |
return NULL; |
2843 |
} |
} |
2844 |
|
|
2845 |
/* This is used to set local shell variables |
/* str holds "NAME=VAL" and is expected to be malloced. |
2846 |
flg_export==0 if only local (not exporting) variable |
* We take ownership of it. */ |
2847 |
flg_export==1 if "new" exporting environ |
static int set_local_var(char *str, int flg_export) |
2848 |
flg_export>1 if current startup environ (not call putenv()) */ |
{ |
2849 |
static int set_local_var(const char *s, int flg_export) |
struct variable *cur; |
2850 |
{ |
char *value; |
2851 |
char *name, *value; |
int name_len; |
|
int result=0; |
|
|
struct variables *cur; |
|
|
|
|
|
name=strdup(s); |
|
|
|
|
|
/* Assume when we enter this function that we are already in |
|
|
* NAME=VALUE format. So the first order of business is to |
|
|
* split 's' on the '=' into 'name' and 'value' */ |
|
|
value = strchr(name, '='); |
|
|
if (value==0 && ++value==0) { |
|
|
free(name); |
|
|
return -1; |
|
|
} |
|
|
*value++ = 0; |
|
2852 |
|
|
2853 |
for(cur = top_vars; cur; cur = cur->next) { |
value = strchr(str, '='); |
2854 |
if(strcmp(cur->name, name)==0) |
if (!value) { /* not expected to ever happen? */ |
2855 |
break; |
free(str); |
2856 |
|
return -1; |
2857 |
} |
} |
2858 |
|
|
2859 |
if(cur) { |
name_len = value - str + 1; /* including '=' */ |
2860 |
if(strcmp(cur->value, value)==0) { |
cur = G.top_var; /* cannot be NULL (we have HUSH_VERSION and it's RO) */ |
2861 |
if(flg_export>0 && cur->flg_export==0) |
while (1) { |
2862 |
cur->flg_export=flg_export; |
if (strncmp(cur->varstr, str, name_len) != 0) { |
2863 |
else |
if (!cur->next) { |
2864 |
result++; |
/* Bail out. Note that now cur points |
2865 |
} else { |
* to last var in linked list */ |
2866 |
if(cur->flg_read_only) { |
break; |
|
bb_error_msg("%s: readonly variable", name); |
|
|
result = -1; |
|
|
} else { |
|
|
if(flg_export>0 || cur->flg_export>1) |
|
|
cur->flg_export=1; |
|
|
free(cur->value); |
|
|
|
|
|
cur->value = strdup(value); |
|
2867 |
} |
} |
2868 |
|
cur = cur->next; |
2869 |
|
continue; |
2870 |
} |
} |
2871 |
} else { |
/* We found an existing var with this name */ |
2872 |
cur = malloc(sizeof(struct variables)); |
*value = '\0'; |
2873 |
if(!cur) { |
if (cur->flg_read_only) { |
2874 |
result = -1; |
bb_error_msg("%s: readonly variable", str); |
2875 |
} else { |
free(str); |
2876 |
cur->name = strdup(name); |
return -1; |
|
if(cur->name == 0) { |
|
|
free(cur); |
|
|
result = -1; |
|
|
} else { |
|
|
struct variables *bottom = top_vars; |
|
|
cur->value = strdup(value); |
|
|
cur->next = 0; |
|
|
cur->flg_export = flg_export; |
|
|
cur->flg_read_only = 0; |
|
|
while(bottom->next) bottom=bottom->next; |
|
|
bottom->next = cur; |
|
|
} |
|
2877 |
} |
} |
2878 |
|
debug_printf_env("%s: unsetenv '%s'\n", __func__, str); |
2879 |
|
unsetenv(str); /* just in case */ |
2880 |
|
*value = '='; |
2881 |
|
if (strcmp(cur->varstr, str) == 0) { |
2882 |
|
free_and_exp: |
2883 |
|
free(str); |
2884 |
|
goto exp; |
2885 |
|
} |
2886 |
|
if (cur->max_len >= strlen(str)) { |
2887 |
|
/* This one is from startup env, reuse space */ |
2888 |
|
strcpy(cur->varstr, str); |
2889 |
|
goto free_and_exp; |
2890 |
|
} |
2891 |
|
/* max_len == 0 signifies "malloced" var, which we can |
2892 |
|
* (and has to) free */ |
2893 |
|
if (!cur->max_len) |
2894 |
|
free(cur->varstr); |
2895 |
|
cur->max_len = 0; |
2896 |
|
goto set_str_and_exp; |
2897 |
|
} |
2898 |
|
|
2899 |
|
/* Not found - create next variable struct */ |
2900 |
|
cur->next = xzalloc(sizeof(*cur)); |
2901 |
|
cur = cur->next; |
2902 |
|
|
2903 |
|
set_str_and_exp: |
2904 |
|
cur->varstr = str; |
2905 |
|
exp: |
2906 |
|
if (flg_export) |
2907 |
|
cur->flg_export = 1; |
2908 |
|
if (cur->flg_export) { |
2909 |
|
debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr); |
2910 |
|
return putenv(cur->varstr); |
2911 |
} |
} |
2912 |
|
return 0; |
|
if(result==0 && cur->flg_export==1) { |
|
|
*(value-1) = '='; |
|
|
result = putenv(name); |
|
|
} else { |
|
|
free(name); |
|
|
if(result>0) /* equivalent to previous set */ |
|
|
result = 0; |
|
|
} |
|
|
return result; |
|
2913 |
} |
} |
2914 |
|
|
2915 |
static void unset_local_var(const char *name) |
static void unset_local_var(const char *name) |
2916 |
{ |
{ |
2917 |
struct variables *cur; |
struct variable *cur; |
2918 |
|
struct variable *prev = prev; /* for gcc */ |
2919 |
if (name) { |
int name_len; |
2920 |
for (cur = top_vars; cur; cur=cur->next) { |
|
2921 |
if(strcmp(cur->name, name)==0) |
if (!name) |
2922 |
break; |
return; |
2923 |
} |
name_len = strlen(name); |
2924 |
if(cur!=0) { |
cur = G.top_var; |
2925 |
struct variables *next = top_vars; |
while (cur) { |
2926 |
if(cur->flg_read_only) { |
if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') { |
2927 |
|
if (cur->flg_read_only) { |
2928 |
bb_error_msg("%s: readonly variable", name); |
bb_error_msg("%s: readonly variable", name); |
2929 |
return; |
return; |
|
} else { |
|
|
if(cur->flg_export) |
|
|
unsetenv(cur->name); |
|
|
free(cur->name); |
|
|
free(cur->value); |
|
|
while (next->next != cur) |
|
|
next = next->next; |
|
|
next->next = cur->next; |
|
2930 |
} |
} |
2931 |
|
/* prev is ok to use here because 1st variable, HUSH_VERSION, |
2932 |
|
* is ro, and we cannot reach this code on the 1st pass */ |
2933 |
|
prev->next = cur->next; |
2934 |
|
debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr); |
2935 |
|
unsetenv(cur->varstr); |
2936 |
|
if (!cur->max_len) |
2937 |
|
free(cur->varstr); |
2938 |
free(cur); |
free(cur); |
2939 |
|
return; |
2940 |
} |
} |
2941 |
|
prev = cur; |
2942 |
|
cur = cur->next; |
2943 |
} |
} |
2944 |
} |
} |
2945 |
|
|
2946 |
static int is_assignment(const char *s) |
/* The src parameter allows us to peek forward to a possible &n syntax |
|
{ |
|
|
if (s==NULL || !isalpha(*s)) return 0; |
|
|
++s; |
|
|
while(isalnum(*s) || *s=='_') ++s; |
|
|
return *s=='='; |
|
|
} |
|
|
|
|
|
/* the src parameter allows us to peek forward to a possible &n syntax |
|
2947 |
* for file descriptor duplication, e.g., "2>&1". |
* for file descriptor duplication, e.g., "2>&1". |
2948 |
* Return code is 0 normally, 1 if a syntax error is detected in src. |
* Return code is 0 normally, 1 if a syntax error is detected in src. |
2949 |
* Resource errors (in xmalloc) cause the process to exit */ |
* Resource errors (in xmalloc) cause the process to exit */ |
2950 |
static int setup_redirect(struct p_context *ctx, int fd, redir_type style, |
static int setup_redirect(struct parse_context *ctx, int fd, redir_type style, |
2951 |
struct in_str *input) |
struct in_str *input) |
2952 |
{ |
{ |
2953 |
struct child_prog *child=ctx->child; |
struct command *command = ctx->command; |
2954 |
struct redir_struct *redir = child->redirects; |
struct redir_struct *redir = command->redirects; |
2955 |
struct redir_struct *last_redir=NULL; |
struct redir_struct *last_redir = NULL; |
2956 |
|
|
2957 |
/* Create a new redir_struct and drop it onto the end of the linked list */ |
/* Create a new redir_struct and drop it onto the end of the linked list */ |
2958 |
while(redir) { |
while (redir) { |
2959 |
last_redir=redir; |
last_redir = redir; |
2960 |
redir=redir->next; |
redir = redir->next; |
2961 |
} |
} |
2962 |
redir = xmalloc(sizeof(struct redir_struct)); |
redir = xzalloc(sizeof(struct redir_struct)); |
2963 |
redir->next=NULL; |
/* redir->next = NULL; */ |
2964 |
redir->word.gl_pathv=NULL; |
/* redir->rd_filename = NULL; */ |
2965 |
if (last_redir) { |
if (last_redir) { |
2966 |
last_redir->next=redir; |
last_redir->next = redir; |
2967 |
} else { |
} else { |
2968 |
child->redirects=redir; |
command->redirects = redir; |
2969 |
} |
} |
2970 |
|
|
2971 |
redir->type=style; |
redir->rd_type = style; |
2972 |
redir->fd= (fd==-1) ? redir_table[style].default_fd : fd ; |
redir->fd = (fd == -1) ? redir_table[style].default_fd : fd; |
2973 |
|
|
2974 |
debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip); |
debug_printf("Redirect type %d%s\n", redir->fd, redir_table[style].descrip); |
2975 |
|
|
2976 |
/* Check for a '2>&1' type redirect */ |
/* Check for a '2>&1' type redirect */ |
2977 |
redir->dup = redirect_dup_num(input); |
redir->dup = redirect_dup_num(input); |
2978 |
if (redir->dup == -2) return 1; /* syntax error */ |
if (redir->dup == -2) |
2979 |
|
return 1; /* syntax error */ |
2980 |
if (redir->dup != -1) { |
if (redir->dup != -1) { |
2981 |
/* Erik had a check here that the file descriptor in question |
/* Erik had a check here that the file descriptor in question |
2982 |
* is legit; I postpone that to "run time" |
* is legit; I postpone that to "run time" |
2986 |
/* We do _not_ try to open the file that src points to, |
/* We do _not_ try to open the file that src points to, |
2987 |
* since we need to return and let src be expanded first. |
* since we need to return and let src be expanded first. |
2988 |
* Set ctx->pending_redirect, so we know what to do at the |
* Set ctx->pending_redirect, so we know what to do at the |
2989 |
* end of the next parsed word. |
* end of the next parsed word. */ |
|
*/ |
|
2990 |
ctx->pending_redirect = redir; |
ctx->pending_redirect = redir; |
2991 |
} |
} |
2992 |
return 0; |
return 0; |
2993 |
} |
} |
2994 |
|
|
2995 |
static struct pipe *new_pipe(void) { |
static struct pipe *new_pipe(void) |
2996 |
|
{ |
2997 |
struct pipe *pi; |
struct pipe *pi; |
2998 |
pi = xmalloc(sizeof(struct pipe)); |
pi = xzalloc(sizeof(struct pipe)); |
2999 |
pi->num_progs = 0; |
/*pi->followup = 0; - deliberately invalid value */ |
3000 |
pi->progs = NULL; |
/*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */ |
|
pi->next = NULL; |
|
|
pi->followup = 0; /* invalid */ |
|
|
pi->r_mode = RES_NONE; |
|
3001 |
return pi; |
return pi; |
3002 |
} |
} |
3003 |
|
|
3004 |
static void initialize_context(struct p_context *ctx) |
static void initialize_context(struct parse_context *ctx) |
3005 |
{ |
{ |
3006 |
ctx->pipe=NULL; |
memset(ctx, 0, sizeof(*ctx)); |
3007 |
ctx->pending_redirect=NULL; |
ctx->pipe = ctx->list_head = new_pipe(); |
3008 |
ctx->child=NULL; |
/* Create the memory for command, roughly: |
3009 |
ctx->list_head=new_pipe(); |
* ctx->pipe->cmds = new struct command; |
3010 |
ctx->pipe=ctx->list_head; |
* ctx->command = &ctx->pipe->cmds[0]; |
3011 |
ctx->w=RES_NONE; |
*/ |
3012 |
ctx->stack=NULL; |
done_command(ctx); |
3013 |
ctx->old_flag=0; |
} |
3014 |
done_command(ctx); /* creates the memory for working child */ |
|
3015 |
} |
/* If a reserved word is found and processed, parse context is modified |
3016 |
|
* and 1 is returned. |
3017 |
/* normal return is 0 |
* Handles if, then, elif, else, fi, for, while, until, do, done. |
|
* if a reserved word is found, and processed, return 1 |
|
|
* should handle if, then, elif, else, fi, for, while, until, do, done. |
|
3018 |
* case, function, and select are obnoxious, save those for later. |
* case, function, and select are obnoxious, save those for later. |
3019 |
*/ |
*/ |
3020 |
static int reserved_word(o_string *dest, struct p_context *ctx) |
#if HAS_KEYWORDS |
3021 |
|
struct reserved_combo { |
3022 |
|
char literal[6]; |
3023 |
|
unsigned char res; |
3024 |
|
unsigned char assignment_flag; |
3025 |
|
int flag; |
3026 |
|
}; |
3027 |
|
enum { |
3028 |
|
FLAG_END = (1 << RES_NONE ), |
3029 |
|
#if ENABLE_HUSH_IF |
3030 |
|
FLAG_IF = (1 << RES_IF ), |
3031 |
|
FLAG_THEN = (1 << RES_THEN ), |
3032 |
|
FLAG_ELIF = (1 << RES_ELIF ), |
3033 |
|
FLAG_ELSE = (1 << RES_ELSE ), |
3034 |
|
FLAG_FI = (1 << RES_FI ), |
3035 |
|
#endif |
3036 |
|
#if ENABLE_HUSH_LOOPS |
3037 |
|
FLAG_FOR = (1 << RES_FOR ), |
3038 |
|
FLAG_WHILE = (1 << RES_WHILE), |
3039 |
|
FLAG_UNTIL = (1 << RES_UNTIL), |
3040 |
|
FLAG_DO = (1 << RES_DO ), |
3041 |
|
FLAG_DONE = (1 << RES_DONE ), |
3042 |
|
FLAG_IN = (1 << RES_IN ), |
3043 |
|
#endif |
3044 |
|
#if ENABLE_HUSH_CASE |
3045 |
|
FLAG_MATCH = (1 << RES_MATCH), |
3046 |
|
FLAG_ESAC = (1 << RES_ESAC ), |
3047 |
|
#endif |
3048 |
|
FLAG_START = (1 << RES_XXXX ), |
3049 |
|
}; |
3050 |
|
|
3051 |
|
static const struct reserved_combo* match_reserved_word(o_string *word) |
3052 |
{ |
{ |
|
struct reserved_combo { |
|
|
char *literal; |
|
|
int code; |
|
|
long flag; |
|
|
}; |
|
3053 |
/* Mostly a list of accepted follow-up reserved words. |
/* Mostly a list of accepted follow-up reserved words. |
3054 |
* FLAG_END means we are done with the sequence, and are ready |
* FLAG_END means we are done with the sequence, and are ready |
3055 |
* to turn the compound list into a command. |
* to turn the compound list into a command. |
3056 |
* FLAG_START means the word must start a new compound list. |
* FLAG_START means the word must start a new compound list. |
3057 |
*/ |
*/ |
3058 |
static struct reserved_combo reserved_list[] = { |
static const struct reserved_combo reserved_list[] = { |
3059 |
{ "if", RES_IF, FLAG_THEN | FLAG_START }, |
#if ENABLE_HUSH_IF |
3060 |
{ "then", RES_THEN, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, |
{ "!", RES_NONE, NOT_ASSIGNMENT , 0 }, |
3061 |
{ "elif", RES_ELIF, FLAG_THEN }, |
{ "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START }, |
3062 |
{ "else", RES_ELSE, FLAG_FI }, |
{ "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, |
3063 |
{ "fi", RES_FI, FLAG_END }, |
{ "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN }, |
3064 |
{ "for", RES_FOR, FLAG_IN | FLAG_START }, |
{ "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI }, |
3065 |
{ "while", RES_WHILE, FLAG_DO | FLAG_START }, |
{ "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, |
3066 |
{ "until", RES_UNTIL, FLAG_DO | FLAG_START }, |
#endif |
3067 |
{ "in", RES_IN, FLAG_DO }, |
#if ENABLE_HUSH_LOOPS |
3068 |
{ "do", RES_DO, FLAG_DONE }, |
{ "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, |
3069 |
{ "done", RES_DONE, FLAG_END } |
{ "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, |
3070 |
|
{ "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, |
3071 |
|
{ "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, |
3072 |
|
{ "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE }, |
3073 |
|
{ "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, |
3074 |
|
#endif |
3075 |
|
#if ENABLE_HUSH_CASE |
3076 |
|
{ "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, |
3077 |
|
{ "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, |
3078 |
|
#endif |
3079 |
}; |
}; |
3080 |
struct reserved_combo *r; |
const struct reserved_combo *r; |
3081 |
for (r=reserved_list; |
|
3082 |
#define NRES sizeof(reserved_list)/sizeof(struct reserved_combo) |
for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) { |
3083 |
r<reserved_list+NRES; r++) { |
if (strcmp(word->data, r->literal) == 0) |
3084 |
if (strcmp(dest->data, r->literal) == 0) { |
return r; |
|
debug_printf("found reserved word %s, code %d\n",r->literal,r->code); |
|
|
if (r->flag & FLAG_START) { |
|
|
struct p_context *new = xmalloc(sizeof(struct p_context)); |
|
|
debug_printf("push stack\n"); |
|
|
if (ctx->w == RES_IN || ctx->w == RES_FOR) { |
|
|
syntax(); |
|
|
free(new); |
|
|
ctx->w = RES_SNTX; |
|
|
b_reset(dest); |
|
|
return 1; |
|
|
} |
|
|
*new = *ctx; /* physical copy */ |
|
|
initialize_context(ctx); |
|
|
ctx->stack=new; |
|
|
} else if ( ctx->w == RES_NONE || ! (ctx->old_flag & (1<<r->code))) { |
|
|
syntax(); |
|
|
ctx->w = RES_SNTX; |
|
|
b_reset(dest); |
|
|
return 1; |
|
|
} |
|
|
ctx->w=r->code; |
|
|
ctx->old_flag = r->flag; |
|
|
if (ctx->old_flag & FLAG_END) { |
|
|
struct p_context *old; |
|
|
debug_printf("pop stack\n"); |
|
|
done_pipe(ctx,PIPE_SEQ); |
|
|
old = ctx->stack; |
|
|
old->child->group = ctx->list_head; |
|
|
old->child->subshell = 0; |
|
|
*ctx = *old; /* physical copy */ |
|
|
free(old); |
|
|
} |
|
|
b_reset (dest); |
|
|
return 1; |
|
|
} |
|
3085 |
} |
} |
3086 |
return 0; |
return NULL; |
3087 |
} |
} |
3088 |
|
static int reserved_word(o_string *word, struct parse_context *ctx) |
3089 |
|
{ |
3090 |
|
#if ENABLE_HUSH_CASE |
3091 |
|
static const struct reserved_combo reserved_match = { |
3092 |
|
"", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC |
3093 |
|
}; |
3094 |
|
#endif |
3095 |
|
const struct reserved_combo *r; |
3096 |
|
|
3097 |
/* normal return is 0. |
r = match_reserved_word(word); |
3098 |
* Syntax or xglob errors return 1. */ |
if (!r) |
|
static int done_word(o_string *dest, struct p_context *ctx) |
|
|
{ |
|
|
struct child_prog *child=ctx->child; |
|
|
glob_t *glob_target; |
|
|
int gr, flags = 0; |
|
|
|
|
|
debug_printf("done_word: %s %p\n", dest->data, child); |
|
|
if (dest->length == 0 && !dest->nonnull) { |
|
|
debug_printf(" true null, ignored\n"); |
|
3099 |
return 0; |
return 0; |
3100 |
|
|
3101 |
|
debug_printf("found reserved word %s, res %d\n", r->literal, r->res); |
3102 |
|
#if ENABLE_HUSH_CASE |
3103 |
|
if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE) |
3104 |
|
/* "case word IN ..." - IN part starts first match part */ |
3105 |
|
r = &reserved_match; |
3106 |
|
else |
3107 |
|
#endif |
3108 |
|
if (r->flag == 0) { /* '!' */ |
3109 |
|
if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */ |
3110 |
|
syntax(NULL); |
3111 |
|
IF_HAS_KEYWORDS(ctx->ctx_res_w = RES_SNTX;) |
3112 |
|
} |
3113 |
|
ctx->ctx_inverted = 1; |
3114 |
|
return 1; |
3115 |
|
} |
3116 |
|
if (r->flag & FLAG_START) { |
3117 |
|
struct parse_context *new; |
3118 |
|
debug_printf("push stack\n"); |
3119 |
|
new = xmalloc(sizeof(*new)); |
3120 |
|
*new = *ctx; /* physical copy */ |
3121 |
|
initialize_context(ctx); |
3122 |
|
ctx->stack = new; |
3123 |
|
} else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) { |
3124 |
|
syntax(NULL); |
3125 |
|
ctx->ctx_res_w = RES_SNTX; |
3126 |
|
return 1; |
3127 |
|
} |
3128 |
|
ctx->ctx_res_w = r->res; |
3129 |
|
ctx->old_flag = r->flag; |
3130 |
|
if (ctx->old_flag & FLAG_END) { |
3131 |
|
struct parse_context *old; |
3132 |
|
debug_printf("pop stack\n"); |
3133 |
|
done_pipe(ctx, PIPE_SEQ); |
3134 |
|
old = ctx->stack; |
3135 |
|
old->command->group = ctx->list_head; |
3136 |
|
old->command->grp_type = GRP_NORMAL; |
3137 |
|
*ctx = *old; /* physical copy */ |
3138 |
|
free(old); |
3139 |
} |
} |
3140 |
if (ctx->pending_redirect) { |
word->o_assignment = r->assignment_flag; |
3141 |
glob_target = &ctx->pending_redirect->word; |
return 1; |
3142 |
|
} |
3143 |
|
#endif |
3144 |
|
|
3145 |
|
//TODO: many, many callers don't check error from done_word() |
3146 |
|
|
3147 |
|
/* Word is complete, look at it and update parsing context. |
3148 |
|
* Normal return is 0. Syntax errors return 1. */ |
3149 |
|
static int done_word(o_string *word, struct parse_context *ctx) |
3150 |
|
{ |
3151 |
|
struct command *command = ctx->command; |
3152 |
|
|
3153 |
|
debug_printf_parse("done_word entered: '%s' %p\n", word->data, command); |
3154 |
|
if (word->length == 0 && word->nonnull == 0) { |
3155 |
|
debug_printf_parse("done_word return 0: true null, ignored\n"); |
3156 |
|
return 0; |
3157 |
|
} |
3158 |
|
/* If this word wasn't an assignment, next ones definitely |
3159 |
|
* can't be assignments. Even if they look like ones. */ |
3160 |
|
if (word->o_assignment != DEFINITELY_ASSIGNMENT |
3161 |
|
&& word->o_assignment != WORD_IS_KEYWORD |
3162 |
|
) { |
3163 |
|
word->o_assignment = NOT_ASSIGNMENT; |
3164 |
} else { |
} else { |
3165 |
if (child->group) { |
if (word->o_assignment == DEFINITELY_ASSIGNMENT) |
3166 |
syntax(); |
command->assignment_cnt++; |
3167 |
return 1; /* syntax error, groups and arglists don't mix */ |
word->o_assignment = MAYBE_ASSIGNMENT; |
|
} |
|
|
if (!child->argv && (ctx->type & FLAG_PARSE_SEMICOLON)) { |
|
|
debug_printf("checking %s for reserved-ness\n",dest->data); |
|
|
if (reserved_word(dest,ctx)) return ctx->w==RES_SNTX; |
|
|
} |
|
|
glob_target = &child->glob_result; |
|
|
if (child->argv) flags |= GLOB_APPEND; |
|
3168 |
} |
} |
|
gr = xglob(dest, flags, glob_target); |
|
|
if (gr != 0) return 1; |
|
3169 |
|
|
|
b_reset(dest); |
|
3170 |
if (ctx->pending_redirect) { |
if (ctx->pending_redirect) { |
3171 |
ctx->pending_redirect=NULL; |
/* We do not glob in e.g. >*.tmp case. bash seems to glob here |
3172 |
if (glob_target->gl_pathc != 1) { |
* only if run as "bash", not "sh" */ |
3173 |
bb_error_msg("ambiguous redirect"); |
ctx->pending_redirect->rd_filename = xstrdup(word->data); |
3174 |
|
word->o_assignment = NOT_ASSIGNMENT; |
3175 |
|
debug_printf("word stored in rd_filename: '%s'\n", word->data); |
3176 |
|
} else { |
3177 |
|
/* "{ echo foo; } echo bar" - bad */ |
3178 |
|
/* NB: bash allows e.g. "if true; then { echo foo; } fi". TODO? */ |
3179 |
|
if (command->group) { |
3180 |
|
syntax(NULL); |
3181 |
|
debug_printf_parse("done_word return 1: syntax error, groups and arglists don't mix\n"); |
3182 |
return 1; |
return 1; |
3183 |
} |
} |
3184 |
} else { |
#if HAS_KEYWORDS |
3185 |
child->argv = glob_target->gl_pathv; |
#if ENABLE_HUSH_CASE |
3186 |
|
if (ctx->ctx_dsemicolon |
3187 |
|
&& strcmp(word->data, "esac") != 0 /* not "... pattern) cmd;; esac" */ |
3188 |
|
) { |
3189 |
|
/* already done when ctx_dsemicolon was set to 1: */ |
3190 |
|
/* ctx->ctx_res_w = RES_MATCH; */ |
3191 |
|
ctx->ctx_dsemicolon = 0; |
3192 |
|
} else |
3193 |
|
#endif |
3194 |
|
|
3195 |
|
if (!command->argv /* if it's the first word... */ |
3196 |
|
#if ENABLE_HUSH_LOOPS |
3197 |
|
&& ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */ |
3198 |
|
&& ctx->ctx_res_w != RES_IN |
3199 |
|
#endif |
3200 |
|
) { |
3201 |
|
debug_printf_parse(": checking '%s' for reserved-ness\n", word->data); |
3202 |
|
if (reserved_word(word, ctx)) { |
3203 |
|
o_reset(word); |
3204 |
|
debug_printf_parse("done_word return %d\n", (ctx->ctx_res_w == RES_SNTX)); |
3205 |
|
return (ctx->ctx_res_w == RES_SNTX); |
3206 |
|
} |
3207 |
|
} |
3208 |
|
#endif |
3209 |
|
if (word->nonnull /* word had "xx" or 'xx' at least as part of it. */ |
3210 |
|
/* optimization: and if it's ("" or '') or ($v... or `cmd`...): */ |
3211 |
|
&& (word->data[0] == '\0' || word->data[0] == SPECIAL_VAR_SYMBOL) |
3212 |
|
/* (otherwise it's known to be not empty and is already safe) */ |
3213 |
|
) { |
3214 |
|
/* exclude "$@" - it can expand to no word despite "" */ |
3215 |
|
char *p = word->data; |
3216 |
|
while (p[0] == SPECIAL_VAR_SYMBOL |
3217 |
|
&& (p[1] & 0x7f) == '@' |
3218 |
|
&& p[2] == SPECIAL_VAR_SYMBOL |
3219 |
|
) { |
3220 |
|
p += 3; |
3221 |
|
} |
3222 |
|
if (p == word->data || p[0] != '\0') { |
3223 |
|
/* saw no "$@", or not only "$@" but some |
3224 |
|
* real text is there too */ |
3225 |
|
/* insert "empty variable" reference, this makes |
3226 |
|
* e.g. "", $empty"" etc to not disappear */ |
3227 |
|
o_addchr(word, SPECIAL_VAR_SYMBOL); |
3228 |
|
o_addchr(word, SPECIAL_VAR_SYMBOL); |
3229 |
|
} |
3230 |
|
} |
3231 |
|
command->argv = add_string_to_strings(command->argv, xstrdup(word->data)); |
3232 |
|
debug_print_strings("word appended to argv", command->argv); |
3233 |
|
} |
3234 |
|
|
3235 |
|
o_reset(word); |
3236 |
|
ctx->pending_redirect = NULL; |
3237 |
|
|
3238 |
|
#if ENABLE_HUSH_LOOPS |
3239 |
|
/* Force FOR to have just one word (variable name) */ |
3240 |
|
/* NB: basically, this makes hush see "for v in ..." syntax as if |
3241 |
|
* as it is "for v; in ...". FOR and IN become two pipe structs |
3242 |
|
* in parse tree. */ |
3243 |
|
if (ctx->ctx_res_w == RES_FOR) { |
3244 |
|
//TODO: check that command->argv[0] is a valid variable name! |
3245 |
|
done_pipe(ctx, PIPE_SEQ); |
3246 |
} |
} |
3247 |
if (ctx->w == RES_FOR) { |
#endif |
3248 |
done_word(dest,ctx); |
#if ENABLE_HUSH_CASE |
3249 |
done_pipe(ctx,PIPE_SEQ); |
/* Force CASE to have just one word */ |
3250 |
|
if (ctx->ctx_res_w == RES_CASE) { |
3251 |
|
done_pipe(ctx, PIPE_SEQ); |
3252 |
} |
} |
3253 |
|
#endif |
3254 |
|
debug_printf_parse("done_word return 0\n"); |
3255 |
return 0; |
return 0; |
3256 |
} |
} |
3257 |
|
|
3258 |
/* The only possible error here is out of memory, in which case |
/* Command (member of a pipe) is complete. The only possible error here |
3259 |
* xmalloc exits. */ |
* is out of memory, in which case xmalloc exits. */ |
3260 |
static int done_command(struct p_context *ctx) |
static int done_command(struct parse_context *ctx) |
3261 |
{ |
{ |
3262 |
/* The child is really already in the pipe structure, so |
/* The command is really already in the pipe structure, so |
3263 |
* advance the pipe counter and make a new, null child. |
* advance the pipe counter and make a new, null command. */ |
3264 |
* Only real trickiness here is that the uncommitted |
struct pipe *pi = ctx->pipe; |
3265 |
* child structure, to which ctx->child points, is not |
struct command *command = ctx->command; |
3266 |
* counted in pi->num_progs. */ |
|
3267 |
struct pipe *pi=ctx->pipe; |
if (command) { |
3268 |
struct child_prog *prog=ctx->child; |
if (command->group == NULL |
3269 |
|
&& command->argv == NULL |
3270 |
if (prog && prog->group == NULL |
&& command->redirects == NULL |
3271 |
&& prog->argv == NULL |
) { |
3272 |
&& prog->redirects == NULL) { |
debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds); |
3273 |
debug_printf("done_command: skipping null command\n"); |
return pi->num_cmds; |
3274 |
return 0; |
} |
3275 |
} else if (prog) { |
pi->num_cmds++; |
3276 |
pi->num_progs++; |
debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds); |
|
debug_printf("done_command: num_progs incremented to %d\n",pi->num_progs); |
|
3277 |
} else { |
} else { |
3278 |
debug_printf("done_command: initializing\n"); |
debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds); |
3279 |
} |
} |
|
pi->progs = xrealloc(pi->progs, sizeof(*pi->progs) * (pi->num_progs+1)); |
|
3280 |
|
|
3281 |
prog = pi->progs + pi->num_progs; |
/* Only real trickiness here is that the uncommitted |
3282 |
prog->redirects = NULL; |
* command structure is not counted in pi->num_cmds. */ |
3283 |
prog->argv = NULL; |
pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1)); |
3284 |
prog->is_stopped = 0; |
command = &pi->cmds[pi->num_cmds]; |
3285 |
prog->group = NULL; |
memset(command, 0, sizeof(*command)); |
|
prog->glob_result.gl_pathv = NULL; |
|
|
prog->family = pi; |
|
|
prog->sp = 0; |
|
|
ctx->child = prog; |
|
|
prog->type = ctx->type; |
|
3286 |
|
|
3287 |
|
ctx->command = command; |
3288 |
/* but ctx->pipe and ctx->list_head remain unchanged */ |
/* but ctx->pipe and ctx->list_head remain unchanged */ |
3289 |
return 0; |
|
3290 |
|
return pi->num_cmds; /* used only for 0/nonzero check */ |
3291 |
} |
} |
3292 |
|
|
3293 |
static int done_pipe(struct p_context *ctx, pipe_style type) |
static void done_pipe(struct parse_context *ctx, pipe_style type) |
3294 |
{ |
{ |
3295 |
struct pipe *new_p; |
int not_null; |
3296 |
done_command(ctx); /* implicit closure of previous command */ |
|
3297 |
debug_printf("done_pipe, type %d\n", type); |
debug_printf_parse("done_pipe entered, followup %d\n", type); |
3298 |
|
/* Close previous command */ |
3299 |
|
not_null = done_command(ctx); |
3300 |
ctx->pipe->followup = type; |
ctx->pipe->followup = type; |
3301 |
ctx->pipe->r_mode = ctx->w; |
IF_HAS_KEYWORDS(ctx->pipe->pi_inverted = ctx->ctx_inverted;) |
3302 |
new_p=new_pipe(); |
IF_HAS_KEYWORDS(ctx->ctx_inverted = 0;) |
3303 |
ctx->pipe->next = new_p; |
IF_HAS_KEYWORDS(ctx->pipe->res_word = ctx->ctx_res_w;) |
3304 |
ctx->pipe = new_p; |
|
3305 |
ctx->child = NULL; |
/* Without this check, even just <enter> on command line generates |
3306 |
done_command(ctx); /* set up new pipe to accept commands */ |
* tree of three NOPs (!). Which is harmless but annoying. |
3307 |
return 0; |
* IOW: it is safe to do it unconditionally. |
3308 |
|
* RES_NONE case is for "for a in; do ..." (empty IN set) |
3309 |
|
* to work, possibly other cases too. */ |
3310 |
|
if (not_null IF_HAS_KEYWORDS(|| ctx->ctx_res_w != RES_NONE)) { |
3311 |
|
struct pipe *new_p; |
3312 |
|
debug_printf_parse("done_pipe: adding new pipe: " |
3313 |
|
"not_null:%d ctx->ctx_res_w:%d\n", |
3314 |
|
not_null, ctx->ctx_res_w); |
3315 |
|
new_p = new_pipe(); |
3316 |
|
ctx->pipe->next = new_p; |
3317 |
|
ctx->pipe = new_p; |
3318 |
|
ctx->command = NULL; /* needed! */ |
3319 |
|
/* RES_THEN, RES_DO etc are "sticky" - |
3320 |
|
* they remain set for commands inside if/while. |
3321 |
|
* This is used to control execution. |
3322 |
|
* RES_FOR and RES_IN are NOT sticky (needed to support |
3323 |
|
* cases where variable or value happens to match a keyword): |
3324 |
|
*/ |
3325 |
|
#if ENABLE_HUSH_LOOPS |
3326 |
|
if (ctx->ctx_res_w == RES_FOR |
3327 |
|
|| ctx->ctx_res_w == RES_IN) |
3328 |
|
ctx->ctx_res_w = RES_NONE; |
3329 |
|
#endif |
3330 |
|
#if ENABLE_HUSH_CASE |
3331 |
|
if (ctx->ctx_res_w == RES_MATCH) |
3332 |
|
ctx->ctx_res_w = RES_CASEI; |
3333 |
|
#endif |
3334 |
|
/* Create the memory for command, roughly: |
3335 |
|
* ctx->pipe->cmds = new struct command; |
3336 |
|
* ctx->command = &ctx->pipe->cmds[0]; |
3337 |
|
*/ |
3338 |
|
done_command(ctx); |
3339 |
|
} |
3340 |
|
debug_printf_parse("done_pipe return\n"); |
3341 |
} |
} |
3342 |
|
|
3343 |
/* peek ahead in the in_str to find out if we have a "&n" construct, |
/* Peek ahead in the in_str to find out if we have a "&n" construct, |
3344 |
* as in "2>&1", that represents duplicating a file descriptor. |
* as in "2>&1", that represents duplicating a file descriptor. |
3345 |
* returns either -2 (syntax error), -1 (no &), or the number found. |
* Return either -2 (syntax error), -1 (no &), or the number found. |
3346 |
*/ |
*/ |
3347 |
static int redirect_dup_num(struct in_str *input) |
static int redirect_dup_num(struct in_str *input) |
3348 |
{ |
{ |
3349 |
int ch, d=0, ok=0; |
int ch, d = 0, ok = 0; |
3350 |
ch = b_peek(input); |
ch = i_peek(input); |
3351 |
if (ch != '&') return -1; |
if (ch != '&') return -1; |
3352 |
|
|
3353 |
b_getch(input); /* get the & */ |
i_getch(input); /* get the & */ |
3354 |
ch=b_peek(input); |
ch = i_peek(input); |
3355 |
if (ch == '-') { |
if (ch == '-') { |
3356 |
b_getch(input); |
i_getch(input); |
3357 |
return -3; /* "-" represents "close me" */ |
return -3; /* "-" represents "close me" */ |
3358 |
} |
} |
3359 |
while (isdigit(ch)) { |
while (isdigit(ch)) { |
3360 |
d = d*10+(ch-'0'); |
d = d*10 + (ch-'0'); |
3361 |
ok=1; |
ok = 1; |
3362 |
b_getch(input); |
i_getch(input); |
3363 |
ch = b_peek(input); |
ch = i_peek(input); |
3364 |
} |
} |
3365 |
if (ok) return d; |
if (ok) return d; |
3366 |
|
|
3383 |
{ |
{ |
3384 |
int num; |
int num; |
3385 |
|
|
3386 |
if (o->length==0) return -1; |
if (o->length == 0) |
3387 |
for(num=0; num<o->length; num++) { |
return -1; |
3388 |
if (!isdigit(*(o->data+num))) { |
for (num = 0; num < o->length; num++) { |
3389 |
|
if (!isdigit(o->data[num])) { |
3390 |
return -1; |
return -1; |
3391 |
} |
} |
3392 |
} |
} |
3393 |
/* reuse num (and save an int) */ |
num = atoi(o->data); |
3394 |
num=atoi(o->data); |
o_reset(o); |
|
b_reset(o); |
|
3395 |
return num; |
return num; |
3396 |
} |
} |
3397 |
|
|
3398 |
|
#if ENABLE_HUSH_TICK |
3399 |
static FILE *generate_stream_from_list(struct pipe *head) |
static FILE *generate_stream_from_list(struct pipe *head) |
3400 |
{ |
{ |
3401 |
FILE *pf; |
FILE *pf; |
3402 |
int pid, channel[2]; |
int pid, channel[2]; |
3403 |
if (pipe(channel)<0) bb_perror_msg_and_die("pipe"); |
|
3404 |
#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__) |
xpipe(channel); |
3405 |
pid=fork(); |
/* *** NOMMU WARNING *** */ |
3406 |
#else |
/* By using vfork here, we suspend parent till child exits or execs. |
3407 |
pid=vfork(); |
* If child will not do it before it fills the pipe, it can block forever |
3408 |
|
* in write(STDOUT_FILENO), and parent (shell) will be also stuck. |
3409 |
|
* Try this script: |
3410 |
|
* yes "0123456789012345678901234567890" | dd bs=32 count=64k >TESTFILE |
3411 |
|
* huge=`cat TESTFILE` # will block here forever |
3412 |
|
* echo OK |
3413 |
|
*/ |
3414 |
|
pid = BB_MMU ? fork() : vfork(); |
3415 |
|
if (pid < 0) |
3416 |
|
bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork"); |
3417 |
|
if (pid == 0) { /* child */ |
3418 |
|
if (ENABLE_HUSH_JOB) |
3419 |
|
die_sleep = 0; /* let nofork's xfuncs die */ |
3420 |
|
close(channel[0]); /* NB: close _first_, then move fd! */ |
3421 |
|
xmove_fd(channel[1], 1); |
3422 |
|
/* Prevent it from trying to handle ctrl-z etc */ |
3423 |
|
#if ENABLE_HUSH_JOB |
3424 |
|
G.run_list_level = 1; |
3425 |
#endif |
#endif |
3426 |
if (pid<0) { |
/* Process substitution is not considered to be usual |
3427 |
bb_perror_msg_and_die("fork"); |
* 'command execution'. |
3428 |
} else if (pid==0) { |
* SUSv3 says ctrl-Z should be ignored, ctrl-C should not. */ |
3429 |
close(channel[0]); |
/* Not needed, we are relying on it being disabled |
3430 |
if (channel[1] != 1) { |
* everywhere outside actual command execution. */ |
3431 |
dup2(channel[1],1); |
/*set_jobctrl_sighandler(SIG_IGN);*/ |
3432 |
close(channel[1]); |
set_misc_sighandler(SIG_DFL); |
3433 |
} |
/* Freeing 'head' here would break NOMMU. */ |
3434 |
_exit(run_list_real(head)); /* leaks memory */ |
_exit(run_list(head)); |
3435 |
} |
} |
|
debug_printf("forked child %d\n",pid); |
|
3436 |
close(channel[1]); |
close(channel[1]); |
3437 |
pf = fdopen(channel[0],"r"); |
pf = fdopen(channel[0], "r"); |
|
debug_printf("pipe on FILE *%p\n",pf); |
|
3438 |
return pf; |
return pf; |
3439 |
|
/* 'head' is freed by the caller */ |
3440 |
} |
} |
3441 |
|
|
3442 |
/* this version hacked for testing purposes */ |
/* Return code is exit status of the process that is run. */ |
3443 |
/* return code is exit status of the process that is run. */ |
static int process_command_subs(o_string *dest, |
3444 |
static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, int subst_end) |
struct in_str *input, |
3445 |
{ |
const char *subst_end) |
3446 |
int retcode; |
{ |
3447 |
o_string result=NULL_O_STRING; |
int retcode, ch, eol_cnt; |
3448 |
struct p_context inner; |
o_string result = NULL_O_STRING; |
3449 |
|
struct parse_context inner; |
3450 |
FILE *p; |
FILE *p; |
3451 |
struct in_str pipe_str; |
struct in_str pipe_str; |
3452 |
|
|
3453 |
initialize_context(&inner); |
initialize_context(&inner); |
3454 |
|
|
3455 |
/* recursion to generate command */ |
/* Recursion to generate command */ |
3456 |
retcode = parse_stream(&result, &inner, input, subst_end); |
retcode = parse_stream(&result, &inner, input, subst_end); |
3457 |
if (retcode != 0) return retcode; /* syntax error or EOF */ |
if (retcode != 0) |
3458 |
|
return retcode; /* syntax error or EOF */ |
3459 |
done_word(&result, &inner); |
done_word(&result, &inner); |
3460 |
done_pipe(&inner, PIPE_SEQ); |
done_pipe(&inner, PIPE_SEQ); |
3461 |
b_free(&result); |
o_free(&result); |
3462 |
|
|
3463 |
p=generate_stream_from_list(inner.list_head); |
p = generate_stream_from_list(inner.list_head); |
3464 |
if (p==NULL) return 1; |
if (p == NULL) |
3465 |
mark_open(fileno(p)); |
return 1; |
3466 |
|
close_on_exec_on(fileno(p)); |
3467 |
setup_file_in_str(&pipe_str, p); |
setup_file_in_str(&pipe_str, p); |
3468 |
|
|
3469 |
/* now send results of command back into original context */ |
/* Now send results of command back into original context */ |
3470 |
retcode = parse_stream(dest, ctx, &pipe_str, '\0'); |
eol_cnt = 0; |
3471 |
/* XXX In case of a syntax error, should we try to kill the child? |
while ((ch = i_getch(&pipe_str)) != EOF) { |
3472 |
* That would be tough to do right, so just read until EOF. */ |
if (ch == '\n') { |
3473 |
if (retcode == 1) { |
eol_cnt++; |
3474 |
while (b_getch(&pipe_str)!=EOF) { /* discard */ }; |
continue; |
3475 |
|
} |
3476 |
|
while (eol_cnt) { |
3477 |
|
o_addchr(dest, '\n'); |
3478 |
|
eol_cnt--; |
3479 |
|
} |
3480 |
|
o_addQchr(dest, ch); |
3481 |
} |
} |
3482 |
|
|
3483 |
debug_printf("done reading from pipe, pclose()ing\n"); |
debug_printf("done reading from pipe, pclose()ing\n"); |
3484 |
/* This is the step that wait()s for the child. Should be pretty |
/* This is the step that wait()s for the child. Should be pretty |
3485 |
* safe, since we just read an EOF from its stdout. We could try |
* safe, since we just read an EOF from its stdout. We could try |
3486 |
* to better, by using wait(), and keeping track of background jobs |
* to do better, by using wait(), and keeping track of background jobs |
3487 |
* at the same time. That would be a lot of work, and contrary |
* at the same time. That would be a lot of work, and contrary |
3488 |
* to the KISS philosophy of this program. */ |
* to the KISS philosophy of this program. */ |
3489 |
mark_closed(fileno(p)); |
retcode = fclose(p); |
3490 |
retcode=pclose(p); |
free_pipe_list(inner.list_head, /* indent: */ 0); |
3491 |
free_pipe_list(inner.list_head,0); |
debug_printf("closed FILE from child, retcode=%d\n", retcode); |
|
debug_printf("pclosed, retcode=%d\n",retcode); |
|
|
/* XXX this process fails to trim a single trailing newline */ |
|
3492 |
return retcode; |
return retcode; |
3493 |
} |
} |
3494 |
|
#endif |
3495 |
|
|
3496 |
static int parse_group(o_string *dest, struct p_context *ctx, |
static int parse_group(o_string *dest, struct parse_context *ctx, |
3497 |
struct in_str *input, int ch) |
struct in_str *input, int ch) |
3498 |
{ |
{ |
3499 |
int rcode, endch=0; |
/* dest contains characters seen prior to ( or {. |
3500 |
struct p_context sub; |
* Typically it's empty, but for functions defs, |
3501 |
struct child_prog *child = ctx->child; |
* it contains function name (without '()'). */ |
3502 |
if (child->argv) { |
int rcode; |
3503 |
syntax(); |
const char *endch = NULL; |
3504 |
return 1; /* syntax error, groups and arglists don't mix */ |
struct parse_context sub; |
3505 |
|
struct command *command = ctx->command; |
3506 |
|
|
3507 |
|
debug_printf_parse("parse_group entered\n"); |
3508 |
|
#if ENABLE_HUSH_FUNCTIONS |
3509 |
|
if (ch == 'F') { /* function definition? */ |
3510 |
|
bb_error_msg("aha '%s' is a function, parsing it...", dest->data); |
3511 |
|
//command->fname = dest->data; |
3512 |
|
command->grp_type = GRP_FUNCTION; |
3513 |
|
//TODO: review every o_reset() location... do they handle all o_string fields correctly? |
3514 |
|
memset(dest, 0, sizeof(*dest)); |
3515 |
|
} |
3516 |
|
#endif |
3517 |
|
if (command->argv /* word [word](... */ |
3518 |
|
|| dest->length /* word(... */ |
3519 |
|
|| dest->nonnull /* ""(... */ |
3520 |
|
) { |
3521 |
|
syntax(NULL); |
3522 |
|
debug_printf_parse("parse_group return 1: syntax error, groups and arglists don't mix\n"); |
3523 |
|
return 1; |
3524 |
} |
} |
3525 |
initialize_context(&sub); |
initialize_context(&sub); |
3526 |
switch (ch) { |
endch = "}"; |
3527 |
case '(': endch=')'; child->subshell=1; break; |
if (ch == '(') { |
3528 |
case '{': endch='}'; break; |
endch = ")"; |
3529 |
default: syntax(); /* really logic error */ |
command->grp_type = GRP_SUBSHELL; |
3530 |
} |
} |
3531 |
rcode=parse_stream(dest,&sub,input,endch); |
rcode = parse_stream(dest, &sub, input, endch); |
3532 |
done_word(dest,&sub); /* finish off the final word in the subcontext */ |
if (rcode == 0) { |
3533 |
done_pipe(&sub, PIPE_SEQ); /* and the final command there, too */ |
done_word(dest, &sub); /* finish off the final word in the subcontext */ |
3534 |
child->group = sub.list_head; |
done_pipe(&sub, PIPE_SEQ); /* and the final command there, too */ |
3535 |
|
command->group = sub.list_head; |
3536 |
|
} |
3537 |
|
debug_printf_parse("parse_group return %d\n", rcode); |
3538 |
return rcode; |
return rcode; |
3539 |
/* child remains "open", available for possible redirects */ |
/* command remains "open", available for possible redirects */ |
3540 |
} |
} |
3541 |
|
|
3542 |
/* basically useful version until someone wants to get fancier, |
/* Basically useful version until someone wants to get fancier, |
3543 |
* see the bash man page under "Parameter Expansion" */ |
* see the bash man page under "Parameter Expansion" */ |
3544 |
static char *lookup_param(char *src) |
static const char *lookup_param(const char *src) |
3545 |
{ |
{ |
3546 |
char *p=NULL; |
struct variable *var = get_local_var(src); |
3547 |
if (src) { |
if (var) |
3548 |
p = getenv(src); |
return strchr(var->varstr, '=') + 1; |
3549 |
if (!p) |
return NULL; |
|
p = get_local_var(src); |
|
|
} |
|
|
return p; |
|
3550 |
} |
} |
3551 |
|
|
3552 |
/* return code: 0 for OK, 1 for syntax error */ |
#if ENABLE_HUSH_TICK |
3553 |
static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input) |
/* Subroutines for copying $(...) and `...` things */ |
3554 |
|
static void add_till_backquote(o_string *dest, struct in_str *input); |
3555 |
|
/* '...' */ |
3556 |
|
static void add_till_single_quote(o_string *dest, struct in_str *input) |
3557 |
|
{ |
3558 |
|
while (1) { |
3559 |
|
int ch = i_getch(input); |
3560 |
|
if (ch == EOF) |
3561 |
|
break; |
3562 |
|
if (ch == '\'') |
3563 |
|
break; |
3564 |
|
o_addchr(dest, ch); |
3565 |
|
} |
3566 |
|
} |
3567 |
|
/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */ |
3568 |
|
static void add_till_double_quote(o_string *dest, struct in_str *input) |
3569 |
{ |
{ |
3570 |
int i, advance=0; |
while (1) { |
3571 |
char sep[]=" "; |
int ch = i_getch(input); |
3572 |
int ch = input->peek(input); /* first character after the $ */ |
if (ch == '"') |
3573 |
debug_printf("handle_dollar: ch=%c\n",ch); |
break; |
3574 |
if (isalpha(ch)) { |
if (ch == '\\') { /* \x. Copy both chars. */ |
3575 |
b_addchr(dest, SPECIAL_VAR_SYMBOL); |
o_addchr(dest, ch); |
3576 |
ctx->child->sp++; |
ch = i_getch(input); |
|
while(ch=b_peek(input),isalnum(ch) || ch=='_') { |
|
|
b_getch(input); |
|
|
b_addchr(dest,ch); |
|
3577 |
} |
} |
3578 |
b_addchr(dest, SPECIAL_VAR_SYMBOL); |
if (ch == EOF) |
3579 |
} else if (isdigit(ch)) { |
break; |
3580 |
i = ch-'0'; /* XXX is $0 special? */ |
o_addchr(dest, ch); |
3581 |
if (i<global_argc) { |
if (ch == '`') { |
3582 |
parse_string(dest, ctx, global_argv[i]); /* recursion */ |
add_till_backquote(dest, input); |
3583 |
|
o_addchr(dest, ch); |
3584 |
|
continue; |
3585 |
} |
} |
3586 |
advance = 1; |
//if (ch == '$') ... |
3587 |
} else switch (ch) { |
} |
3588 |
case '$': |
} |
3589 |
b_adduint(dest,getpid()); |
/* Process `cmd` - copy contents until "`" is seen. Complicated by |
3590 |
advance = 1; |
* \` quoting. |
3591 |
|
* "Within the backquoted style of command substitution, backslash |
3592 |
|
* shall retain its literal meaning, except when followed by: '$', '`', or '\'. |
3593 |
|
* The search for the matching backquote shall be satisfied by the first |
3594 |
|
* backquote found without a preceding backslash; during this search, |
3595 |
|
* if a non-escaped backquote is encountered within a shell comment, |
3596 |
|
* a here-document, an embedded command substitution of the $(command) |
3597 |
|
* form, or a quoted string, undefined results occur. A single-quoted |
3598 |
|
* or double-quoted string that begins, but does not end, within the |
3599 |
|
* "`...`" sequence produces undefined results." |
3600 |
|
* Example Output |
3601 |
|
* echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST |
3602 |
|
*/ |
3603 |
|
static void add_till_backquote(o_string *dest, struct in_str *input) |
3604 |
|
{ |
3605 |
|
while (1) { |
3606 |
|
int ch = i_getch(input); |
3607 |
|
if (ch == '`') |
3608 |
break; |
break; |
3609 |
case '!': |
if (ch == '\\') { /* \x. Copy both chars unless it is \` */ |
3610 |
if (last_bg_pid > 0) b_adduint(dest, last_bg_pid); |
int ch2 = i_getch(input); |
3611 |
advance = 1; |
if (ch2 != '`' && ch2 != '$' && ch2 != '\\') |
3612 |
break; |
o_addchr(dest, ch); |
3613 |
case '?': |
ch = ch2; |
3614 |
b_adduint(dest,last_return_code); |
} |
3615 |
advance = 1; |
if (ch == EOF) |
3616 |
break; |
break; |
3617 |
case '#': |
o_addchr(dest, ch); |
3618 |
b_adduint(dest,global_argc ? global_argc-1 : 0); |
} |
3619 |
advance = 1; |
} |
3620 |
|
/* Process $(cmd) - copy contents until ")" is seen. Complicated by |
3621 |
|
* quoting and nested ()s. |
3622 |
|
* "With the $(command) style of command substitution, all characters |
3623 |
|
* following the open parenthesis to the matching closing parenthesis |
3624 |
|
* constitute the command. Any valid shell script can be used for command, |
3625 |
|
* except a script consisting solely of redirections which produces |
3626 |
|
* unspecified results." |
3627 |
|
* Example Output |
3628 |
|
* echo $(echo '(TEST)' BEST) (TEST) BEST |
3629 |
|
* echo $(echo 'TEST)' BEST) TEST) BEST |
3630 |
|
* echo $(echo \(\(TEST\) BEST) ((TEST) BEST |
3631 |
|
*/ |
3632 |
|
static void add_till_closing_curly_brace(o_string *dest, struct in_str *input) |
3633 |
|
{ |
3634 |
|
int count = 0; |
3635 |
|
while (1) { |
3636 |
|
int ch = i_getch(input); |
3637 |
|
if (ch == EOF) |
3638 |
break; |
break; |
3639 |
|
if (ch == '(') |
3640 |
|
count++; |
3641 |
|
if (ch == ')') |
3642 |
|
if (--count < 0) |
3643 |
|
break; |
3644 |
|
o_addchr(dest, ch); |
3645 |
|
if (ch == '\'') { |
3646 |
|
add_till_single_quote(dest, input); |
3647 |
|
o_addchr(dest, ch); |
3648 |
|
continue; |
3649 |
|
} |
3650 |
|
if (ch == '"') { |
3651 |
|
add_till_double_quote(dest, input); |
3652 |
|
o_addchr(dest, ch); |
3653 |
|
continue; |
3654 |
|
} |
3655 |
|
if (ch == '\\') { /* \x. Copy verbatim. Important for \(, \) */ |
3656 |
|
ch = i_getch(input); |
3657 |
|
if (ch == EOF) |
3658 |
|
break; |
3659 |
|
o_addchr(dest, ch); |
3660 |
|
continue; |
3661 |
|
} |
3662 |
|
} |
3663 |
|
} |
3664 |
|
#endif /* ENABLE_HUSH_TICK */ |
3665 |
|
|
3666 |
|
/* Return code: 0 for OK, 1 for syntax error */ |
3667 |
|
static int handle_dollar(o_string *dest, struct in_str *input) |
3668 |
|
{ |
3669 |
|
int ch = i_peek(input); /* first character after the $ */ |
3670 |
|
unsigned char quote_mask = dest->o_quote ? 0x80 : 0; |
3671 |
|
|
3672 |
|
debug_printf_parse("handle_dollar entered: ch='%c'\n", ch); |
3673 |
|
if (isalpha(ch)) { |
3674 |
|
i_getch(input); |
3675 |
|
make_var: |
3676 |
|
o_addchr(dest, SPECIAL_VAR_SYMBOL); |
3677 |
|
while (1) { |
3678 |
|
debug_printf_parse(": '%c'\n", ch); |
3679 |
|
o_addchr(dest, ch | quote_mask); |
3680 |
|
quote_mask = 0; |
3681 |
|
ch = i_peek(input); |
3682 |
|
if (!isalnum(ch) && ch != '_') |
3683 |
|
break; |
3684 |
|
i_getch(input); |
3685 |
|
} |
3686 |
|
o_addchr(dest, SPECIAL_VAR_SYMBOL); |
3687 |
|
} else if (isdigit(ch)) { |
3688 |
|
make_one_char_var: |
3689 |
|
i_getch(input); |
3690 |
|
o_addchr(dest, SPECIAL_VAR_SYMBOL); |
3691 |
|
debug_printf_parse(": '%c'\n", ch); |
3692 |
|
o_addchr(dest, ch | quote_mask); |
3693 |
|
o_addchr(dest, SPECIAL_VAR_SYMBOL); |
3694 |
|
} else switch (ch) { |
3695 |
|
case '$': /* pid */ |
3696 |
|
case '!': /* last bg pid */ |
3697 |
|
case '?': /* last exit code */ |
3698 |
|
case '#': /* number of args */ |
3699 |
|
case '*': /* args */ |
3700 |
|
case '@': /* args */ |
3701 |
|
goto make_one_char_var; |
3702 |
case '{': |
case '{': |
3703 |
b_addchr(dest, SPECIAL_VAR_SYMBOL); |
o_addchr(dest, SPECIAL_VAR_SYMBOL); |
3704 |
ctx->child->sp++; |
i_getch(input); |
|
b_getch(input); |
|
3705 |
/* XXX maybe someone will try to escape the '}' */ |
/* XXX maybe someone will try to escape the '}' */ |
3706 |
while(ch=b_getch(input),ch!=EOF && ch!='}') { |
while (1) { |
3707 |
b_addchr(dest,ch); |
ch = i_getch(input); |
3708 |
} |
if (ch == '}') |
3709 |
if (ch != '}') { |
break; |
3710 |
syntax(); |
if (!isalnum(ch) && ch != '_') { |
3711 |
return 1; |
syntax("unterminated ${name}"); |
3712 |
|
debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); |
3713 |
|
return 1; |
3714 |
|
} |
3715 |
|
debug_printf_parse(": '%c'\n", ch); |
3716 |
|
o_addchr(dest, ch | quote_mask); |
3717 |
|
quote_mask = 0; |
3718 |
} |
} |
3719 |
b_addchr(dest, SPECIAL_VAR_SYMBOL); |
o_addchr(dest, SPECIAL_VAR_SYMBOL); |
3720 |
break; |
break; |
3721 |
case '(': |
#if ENABLE_HUSH_TICK |
3722 |
b_getch(input); |
case '(': { |
3723 |
process_command_subs(dest, ctx, input, ')'); |
//int pos = dest->length; |
3724 |
|
i_getch(input); |
3725 |
|
o_addchr(dest, SPECIAL_VAR_SYMBOL); |
3726 |
|
o_addchr(dest, quote_mask | '`'); |
3727 |
|
add_till_closing_curly_brace(dest, input); |
3728 |
|
//debug_printf_subst("SUBST RES2 '%s'\n", dest->data + pos); |
3729 |
|
o_addchr(dest, SPECIAL_VAR_SYMBOL); |
3730 |
break; |
break; |
3731 |
case '*': |
} |
3732 |
sep[0]=ifs[0]; |
#endif |
3733 |
for (i=1; i<global_argc; i++) { |
case '_': |
3734 |
parse_string(dest, ctx, global_argv[i]); |
i_getch(input); |
3735 |
if (i+1 < global_argc) parse_string(dest, ctx, sep); |
ch = i_peek(input); |
3736 |
|
if (isalnum(ch)) { /* it's $_name or $_123 */ |
3737 |
|
ch = '_'; |
3738 |
|
goto make_var; |
3739 |
} |
} |
3740 |
break; |
/* else: it's $_ */ |
|
case '@': |
|
3741 |
case '-': |
case '-': |
|
case '_': |
|
3742 |
/* still unhandled, but should be eventually */ |
/* still unhandled, but should be eventually */ |
3743 |
bb_error_msg("unhandled syntax: $%c",ch); |
bb_error_msg("unhandled syntax: $%c", ch); |
3744 |
return 1; |
return 1; |
3745 |
break; |
break; |
3746 |
default: |
default: |
3747 |
b_addqchr(dest,'$',dest->quote); |
o_addQchr(dest, '$'); |
3748 |
} |
} |
3749 |
/* Eat the character if the flag was set. If the compiler |
debug_printf_parse("handle_dollar return 0\n"); |
|
* is smart enough, we could substitute "b_getch(input);" |
|
|
* for all the "advance = 1;" above, and also end up with |
|
|
* a nice size-optimized program. Hah! That'll be the day. |
|
|
*/ |
|
|
if (advance) b_getch(input); |
|
3750 |
return 0; |
return 0; |
3751 |
} |
} |
3752 |
|
|
3753 |
int parse_string(o_string *dest, struct p_context *ctx, const char *src) |
/* Scan input, call done_word() whenever full IFS delimited word was seen. |
3754 |
{ |
* Call done_pipe if '\n' was seen (and end_trigger != NULL). |
3755 |
struct in_str foo; |
* Return code is 0 if end_trigger char is met, |
3756 |
setup_string_in_str(&foo, src); |
* -1 on EOF (but if end_trigger == NULL then return 0), |
3757 |
return parse_stream(dest, ctx, &foo, '\0'); |
* 1 for syntax error */ |
3758 |
} |
static int parse_stream(o_string *dest, struct parse_context *ctx, |
3759 |
|
struct in_str *input, const char *end_trigger) |
|
/* return code is 0 for normal exit, 1 for syntax error */ |
|
|
int parse_stream(o_string *dest, struct p_context *ctx, |
|
|
struct in_str *input, int end_trigger) |
|
3760 |
{ |
{ |
3761 |
int ch, m; |
int ch, m; |
3762 |
int redir_fd; |
int redir_fd; |
3763 |
redir_type redir_style; |
redir_type redir_style; |
3764 |
|
int shadow_quote = dest->o_quote; |
3765 |
int next; |
int next; |
3766 |
|
|
3767 |
/* Only double-quote state is handled in the state variable dest->quote. |
/* Only double-quote state is handled in the state variable dest->o_quote. |
3768 |
* A single-quote triggers a bypass of the main loop until its mate is |
* A single-quote triggers a bypass of the main loop until its mate is |
3769 |
* found. When recursing, quote state is passed in via dest->quote. */ |
* found. When recursing, quote state is passed in via dest->o_quote. */ |
3770 |
|
|
3771 |
debug_printf("parse_stream, end_trigger=%d\n",end_trigger); |
debug_printf_parse("parse_stream entered, end_trigger='%s' dest->o_assignment:%d\n", end_trigger, dest->o_assignment); |
3772 |
while ((ch=b_getch(input))!=EOF) { |
|
3773 |
m = map[ch]; |
while (1) { |
3774 |
next = (ch == '\n') ? 0 : b_peek(input); |
m = CHAR_IFS; |
3775 |
debug_printf("parse_stream: ch=%c (%d) m=%d quote=%d\n", |
next = '\0'; |
3776 |
ch,ch,m,dest->quote); |
ch = i_getch(input); |
3777 |
if (m==0 || ((m==1 || m==2) && dest->quote)) { |
if (ch != EOF) { |
3778 |
b_addqchr(dest, ch, dest->quote); |
m = G.charmap[ch]; |
3779 |
} else { |
if (ch != '\n') { |
3780 |
if (m==2) { /* unquoted IFS */ |
next = i_peek(input); |
3781 |
if (done_word(dest, ctx)) { |
} |
3782 |
return 1; |
} |
3783 |
|
debug_printf_parse(": ch=%c (%d) m=%d quote=%d\n", |
3784 |
|
ch, ch, m, dest->o_quote); |
3785 |
|
if (m == CHAR_ORDINARY |
3786 |
|
|| (m != CHAR_SPECIAL && shadow_quote) |
3787 |
|
) { |
3788 |
|
if (ch == EOF) { |
3789 |
|
syntax("unterminated \""); |
3790 |
|
debug_printf_parse("parse_stream return 1: unterminated \"\n"); |
3791 |
|
return 1; |
3792 |
|
} |
3793 |
|
o_addQchr(dest, ch); |
3794 |
|
if ((dest->o_assignment == MAYBE_ASSIGNMENT |
3795 |
|
|| dest->o_assignment == WORD_IS_KEYWORD) |
3796 |
|
&& ch == '=' |
3797 |
|
&& is_assignment(dest->data) |
3798 |
|
) { |
3799 |
|
dest->o_assignment = DEFINITELY_ASSIGNMENT; |
3800 |
|
} |
3801 |
|
continue; |
3802 |
|
} |
3803 |
|
if (m == CHAR_IFS) { |
3804 |
|
if (done_word(dest, ctx)) { |
3805 |
|
debug_printf_parse("parse_stream return 1: done_word!=0\n"); |
3806 |
|
return 1; |
3807 |
|
} |
3808 |
|
if (ch == EOF) |
3809 |
|
break; |
3810 |
|
/* If we aren't performing a substitution, treat |
3811 |
|
* a newline as a command separator. |
3812 |
|
* [why we don't handle it exactly like ';'? --vda] */ |
3813 |
|
if (end_trigger && ch == '\n') { |
3814 |
|
#if ENABLE_HUSH_CASE |
3815 |
|
/* "case ... in <newline> word) ..." - |
3816 |
|
* newlines are ignored (but ';' wouldn't be) */ |
3817 |
|
if (dest->length == 0 // && argv[0] == NULL |
3818 |
|
&& ctx->ctx_res_w == RES_MATCH |
3819 |
|
) { |
3820 |
|
continue; |
3821 |
|
} |
3822 |
|
#endif |
3823 |
|
done_pipe(ctx, PIPE_SEQ); |
3824 |
|
dest->o_assignment = MAYBE_ASSIGNMENT; |
3825 |
|
} |
3826 |
|
} |
3827 |
|
if (end_trigger) { |
3828 |
|
if (!shadow_quote && strchr(end_trigger, ch)) { |
3829 |
|
/* Special case: (...word) makes last word terminate, |
3830 |
|
* as if ';' is seen */ |
3831 |
|
if (ch == ')') { |
3832 |
|
done_word(dest, ctx); |
3833 |
|
//err chk? |
3834 |
|
done_pipe(ctx, PIPE_SEQ); |
3835 |
|
dest->o_assignment = MAYBE_ASSIGNMENT; |
3836 |
|
} |
3837 |
|
if (!HAS_KEYWORDS |
3838 |
|
IF_HAS_KEYWORDS(|| (ctx->ctx_res_w == RES_NONE && ctx->old_flag == 0)) |
3839 |
|
) { |
3840 |
|
debug_printf_parse("parse_stream return 0: end_trigger char found\n"); |
3841 |
|
return 0; |
3842 |
} |
} |
|
/* If we aren't performing a substitution, treat a newline as a |
|
|
* command separator. */ |
|
|
if (end_trigger != '\0' && ch=='\n') |
|
|
done_pipe(ctx,PIPE_SEQ); |
|
|
} |
|
|
if (ch == end_trigger && !dest->quote && ctx->w==RES_NONE) { |
|
|
debug_printf("leaving parse_stream (triggered)\n"); |
|
|
return 0; |
|
3843 |
} |
} |
3844 |
if (m!=2) switch (ch) { |
} |
3845 |
|
if (m == CHAR_IFS) |
3846 |
|
continue; |
3847 |
|
|
3848 |
|
if (dest->o_assignment == MAYBE_ASSIGNMENT) { |
3849 |
|
/* ch is a special char and thus this word |
3850 |
|
* cannot be an assignment: */ |
3851 |
|
dest->o_assignment = NOT_ASSIGNMENT; |
3852 |
|
} |
3853 |
|
|
3854 |
|
switch (ch) { |
3855 |
case '#': |
case '#': |
3856 |
if (dest->length == 0 && !dest->quote) { |
if (dest->length == 0 && !shadow_quote) { |
3857 |
while(ch=b_peek(input),ch!=EOF && ch!='\n') { b_getch(input); } |
while (1) { |
3858 |
|
ch = i_peek(input); |
3859 |
|
if (ch == EOF || ch == '\n') |
3860 |
|
break; |
3861 |
|
i_getch(input); |
3862 |
|
} |
3863 |
} else { |
} else { |
3864 |
b_addqchr(dest, ch, dest->quote); |
o_addQchr(dest, ch); |
3865 |
} |
} |
3866 |
break; |
break; |
3867 |
case '\\': |
case '\\': |
3868 |
if (next == EOF) { |
if (next == EOF) { |
3869 |
syntax(); |
syntax("\\<eof>"); |
3870 |
|
debug_printf_parse("parse_stream return 1: \\<eof>\n"); |
3871 |
return 1; |
return 1; |
3872 |
} |
} |
3873 |
b_addqchr(dest, '\\', dest->quote); |
/* bash: |
3874 |
b_addqchr(dest, b_getch(input), dest->quote); |
* "The backslash retains its special meaning [in "..."] |
3875 |
|
* only when followed by one of the following characters: |
3876 |
|
* $, `, ", \, or <newline>. A double quote may be quoted |
3877 |
|
* within double quotes by preceding it with a backslash. |
3878 |
|
* If enabled, history expansion will be performed unless |
3879 |
|
* an ! appearing in double quotes is escaped using |
3880 |
|
* a backslash. The backslash preceding the ! is not removed." |
3881 |
|
*/ |
3882 |
|
if (shadow_quote) { //NOT SURE dest->o_quote) { |
3883 |
|
if (strchr("$`\"\\", next) != NULL) { |
3884 |
|
o_addqchr(dest, i_getch(input)); |
3885 |
|
} else { |
3886 |
|
o_addqchr(dest, '\\'); |
3887 |
|
} |
3888 |
|
} else { |
3889 |
|
o_addchr(dest, '\\'); |
3890 |
|
o_addchr(dest, i_getch(input)); |
3891 |
|
} |
3892 |
break; |
break; |
3893 |
case '$': |
case '$': |
3894 |
if (handle_dollar(dest, ctx, input)!=0) return 1; |
if (handle_dollar(dest, input) != 0) { |
3895 |
|
debug_printf_parse("parse_stream return 1: handle_dollar returned non-0\n"); |
3896 |
|
return 1; |
3897 |
|
} |
3898 |
break; |
break; |
3899 |
case '\'': |
case '\'': |
3900 |
dest->nonnull = 1; |
dest->nonnull = 1; |
3901 |
while(ch=b_getch(input),ch!=EOF && ch!='\'') { |
while (1) { |
3902 |
b_addchr(dest,ch); |
ch = i_getch(input); |
3903 |
} |
if (ch == EOF) { |
3904 |
if (ch==EOF) { |
syntax("unterminated '"); |
3905 |
syntax(); |
debug_printf_parse("parse_stream return 1: unterminated '\n"); |
3906 |
return 1; |
return 1; |
3907 |
|
} |
3908 |
|
if (ch == '\'') |
3909 |
|
break; |
3910 |
|
if (dest->o_assignment == NOT_ASSIGNMENT) |
3911 |
|
o_addqchr(dest, ch); |
3912 |
|
else |
3913 |
|
o_addchr(dest, ch); |
3914 |
} |
} |
3915 |
break; |
break; |
3916 |
case '"': |
case '"': |
3917 |
dest->nonnull = 1; |
dest->nonnull = 1; |
3918 |
dest->quote = !dest->quote; |
shadow_quote ^= 1; /* invert */ |
3919 |
|
if (dest->o_assignment == NOT_ASSIGNMENT) |
3920 |
|
dest->o_quote ^= 1; |
3921 |
break; |
break; |
3922 |
case '`': |
#if ENABLE_HUSH_TICK |
3923 |
process_command_subs(dest, ctx, input, '`'); |
case '`': { |
3924 |
|
//int pos = dest->length; |
3925 |
|
o_addchr(dest, SPECIAL_VAR_SYMBOL); |
3926 |
|
o_addchr(dest, shadow_quote /*or dest->o_quote??*/ ? 0x80 | '`' : '`'); |
3927 |
|
add_till_backquote(dest, input); |
3928 |
|
o_addchr(dest, SPECIAL_VAR_SYMBOL); |
3929 |
|
//debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos); |
3930 |
break; |
break; |
3931 |
|
} |
3932 |
|
#endif |
3933 |
case '>': |
case '>': |
3934 |
redir_fd = redirect_opt_num(dest); |
redir_fd = redirect_opt_num(dest); |
3935 |
done_word(dest, ctx); |
done_word(dest, ctx); |
3936 |
redir_style=REDIRECT_OVERWRITE; |
redir_style = REDIRECT_OVERWRITE; |
3937 |
if (next == '>') { |
if (next == '>') { |
3938 |
redir_style=REDIRECT_APPEND; |
redir_style = REDIRECT_APPEND; |
3939 |
b_getch(input); |
i_getch(input); |
3940 |
} else if (next == '(') { |
} |
3941 |
syntax(); /* until we support >(list) Process Substitution */ |
#if 0 |
3942 |
|
else if (next == '(') { |
3943 |
|
syntax(">(process) not supported"); |
3944 |
|
debug_printf_parse("parse_stream return 1: >(process) not supported\n"); |
3945 |
return 1; |
return 1; |
3946 |
} |
} |
3947 |
|
#endif |
3948 |
setup_redirect(ctx, redir_fd, redir_style, input); |
setup_redirect(ctx, redir_fd, redir_style, input); |
3949 |
break; |
break; |
3950 |
case '<': |
case '<': |
3951 |
redir_fd = redirect_opt_num(dest); |
redir_fd = redirect_opt_num(dest); |
3952 |
done_word(dest, ctx); |
done_word(dest, ctx); |
3953 |
redir_style=REDIRECT_INPUT; |
redir_style = REDIRECT_INPUT; |
3954 |
if (next == '<') { |
if (next == '<') { |
3955 |
redir_style=REDIRECT_HEREIS; |
redir_style = REDIRECT_HEREIS; |
3956 |
b_getch(input); |
i_getch(input); |
3957 |
} else if (next == '>') { |
} else if (next == '>') { |
3958 |
redir_style=REDIRECT_IO; |
redir_style = REDIRECT_IO; |
3959 |
b_getch(input); |
i_getch(input); |
3960 |
} else if (next == '(') { |
} |
3961 |
syntax(); /* until we support <(list) Process Substitution */ |
#if 0 |
3962 |
|
else if (next == '(') { |
3963 |
|
syntax("<(process) not supported"); |
3964 |
|
debug_printf_parse("parse_stream return 1: <(process) not supported\n"); |
3965 |
return 1; |
return 1; |
3966 |
} |
} |
3967 |
|
#endif |
3968 |
setup_redirect(ctx, redir_fd, redir_style, input); |
setup_redirect(ctx, redir_fd, redir_style, input); |
3969 |
break; |
break; |
3970 |
case ';': |
case ';': |
3971 |
|
#if ENABLE_HUSH_CASE |
3972 |
|
case_semi: |
3973 |
|
#endif |
3974 |
done_word(dest, ctx); |
done_word(dest, ctx); |
3975 |
done_pipe(ctx,PIPE_SEQ); |
done_pipe(ctx, PIPE_SEQ); |
3976 |
|
#if ENABLE_HUSH_CASE |
3977 |
|
/* Eat multiple semicolons, detect |
3978 |
|
* whether it means something special */ |
3979 |
|
while (1) { |
3980 |
|
ch = i_peek(input); |
3981 |
|
if (ch != ';') |
3982 |
|
break; |
3983 |
|
i_getch(input); |
3984 |
|
if (ctx->ctx_res_w == RES_CASEI) { |
3985 |
|
ctx->ctx_dsemicolon = 1; |
3986 |
|
ctx->ctx_res_w = RES_MATCH; |
3987 |
|
break; |
3988 |
|
} |
3989 |
|
} |
3990 |
|
#endif |
3991 |
|
new_cmd: |
3992 |
|
/* We just finished a cmd. New one may start |
3993 |
|
* with an assignment */ |
3994 |
|
dest->o_assignment = MAYBE_ASSIGNMENT; |
3995 |
break; |
break; |
3996 |
case '&': |
case '&': |
3997 |
done_word(dest, ctx); |
done_word(dest, ctx); |
3998 |
if (next=='&') { |
if (next == '&') { |
3999 |
b_getch(input); |
i_getch(input); |
4000 |
done_pipe(ctx,PIPE_AND); |
done_pipe(ctx, PIPE_AND); |
4001 |
} else { |
} else { |
4002 |
done_pipe(ctx,PIPE_BG); |
done_pipe(ctx, PIPE_BG); |
4003 |
} |
} |
4004 |
break; |
goto new_cmd; |
4005 |
case '|': |
case '|': |
4006 |
done_word(dest, ctx); |
done_word(dest, ctx); |
4007 |
if (next=='|') { |
#if ENABLE_HUSH_CASE |
4008 |
b_getch(input); |
if (ctx->ctx_res_w == RES_MATCH) |
4009 |
done_pipe(ctx,PIPE_OR); |
break; /* we are in case's "word | word)" */ |
4010 |
|
#endif |
4011 |
|
if (next == '|') { /* || */ |
4012 |
|
i_getch(input); |
4013 |
|
done_pipe(ctx, PIPE_OR); |
4014 |
} else { |
} else { |
4015 |
/* we could pick up a file descriptor choice here |
/* we could pick up a file descriptor choice here |
4016 |
* with redirect_opt_num(), but bash doesn't do it. |
* with redirect_opt_num(), but bash doesn't do it. |
4017 |
* "echo foo 2| cat" yields "foo 2". */ |
* "echo foo 2| cat" yields "foo 2". */ |
4018 |
done_command(ctx); |
done_command(ctx); |
4019 |
} |
} |
4020 |
break; |
goto new_cmd; |
4021 |
case '(': |
case '(': |
4022 |
|
#if ENABLE_HUSH_CASE |
4023 |
|
/* "case... in [(]word)..." - skip '(' */ |
4024 |
|
if (ctx->ctx_res_w == RES_MATCH |
4025 |
|
&& ctx->command->argv == NULL /* not (word|(... */ |
4026 |
|
&& dest->length == 0 /* not word(... */ |
4027 |
|
&& dest->nonnull == 0 /* not ""(... */ |
4028 |
|
) { |
4029 |
|
continue; |
4030 |
|
} |
4031 |
|
#endif |
4032 |
|
#if ENABLE_HUSH_FUNCTIONS |
4033 |
|
if (dest->length != 0 /* not just () but word() */ |
4034 |
|
&& dest->nonnull == 0 /* not a"b"c() */ |
4035 |
|
&& ctx->command->argv == NULL /* it's the first word */ |
4036 |
|
//TODO: "func ( ) {...}" - note spaces - is valid format too in bash |
4037 |
|
&& i_peek(input) == ')' |
4038 |
|
&& !match_reserved_word(dest) |
4039 |
|
) { |
4040 |
|
bb_error_msg("seems like a function definition"); |
4041 |
|
i_getch(input); |
4042 |
|
do { |
4043 |
|
//TODO: do it properly. |
4044 |
|
ch = i_getch(input); |
4045 |
|
} while (ch == ' ' || ch == '\n'); |
4046 |
|
if (ch != '{') { |
4047 |
|
syntax("was expecting {"); |
4048 |
|
debug_printf_parse("parse_stream return 1\n"); |
4049 |
|
return 1; |
4050 |
|
} |
4051 |
|
ch = 'F'; /* magic value */ |
4052 |
|
} |
4053 |
|
#endif |
4054 |
case '{': |
case '{': |
4055 |
if (parse_group(dest, ctx, input, ch)!=0) return 1; |
if (parse_group(dest, ctx, input, ch) != 0) { |
4056 |
break; |
debug_printf_parse("parse_stream return 1: parse_group returned non-0\n"); |
4057 |
|
return 1; |
4058 |
|
} |
4059 |
|
goto new_cmd; |
4060 |
case ')': |
case ')': |
4061 |
|
#if ENABLE_HUSH_CASE |
4062 |
|
if (ctx->ctx_res_w == RES_MATCH) |
4063 |
|
goto case_semi; |
4064 |
|
#endif |
4065 |
case '}': |
case '}': |
4066 |
syntax(); /* Proper use of this character caught by end_trigger */ |
/* proper use of this character is caught by end_trigger: |
4067 |
|
* if we see {, we call parse_group(..., end_trigger='}') |
4068 |
|
* and it will match } earlier (not here). */ |
4069 |
|
syntax("unexpected } or )"); |
4070 |
|
debug_printf_parse("parse_stream return 1: unexpected '}'\n"); |
4071 |
return 1; |
return 1; |
|
break; |
|
4072 |
default: |
default: |
4073 |
syntax(); /* this is really an internal logic error */ |
if (HUSH_DEBUG) |
4074 |
return 1; |
bb_error_msg_and_die("BUG: unexpected %c\n", ch); |
|
} |
|
4075 |
} |
} |
4076 |
} |
} /* while (1) */ |
4077 |
/* complain if quote? No, maybe we just finished a command substitution |
debug_printf_parse("parse_stream return %d\n", -(end_trigger != NULL)); |
4078 |
* that was quoted. Example: |
if (end_trigger) |
4079 |
* $ echo "`cat foo` plus more" |
return -1; |
|
* and we just got the EOF generated by the subshell that ran "cat foo" |
|
|
* The only real complaint is if we got an EOF when end_trigger != '\0', |
|
|
* that is, we were really supposed to get end_trigger, and never got |
|
|
* one before the EOF. Can't use the standard "syntax error" return code, |
|
|
* so that parse_stream_outer can distinguish the EOF and exit smoothly. */ |
|
|
debug_printf("leaving parse_stream (EOF)\n"); |
|
|
if (end_trigger != '\0') return -1; |
|
4080 |
return 0; |
return 0; |
4081 |
} |
} |
4082 |
|
|
4083 |
static void mapset(const char *set, int code) |
static void set_in_charmap(const char *set, int code) |
4084 |
{ |
{ |
4085 |
const unsigned char *s; |
while (*set) |
4086 |
for (s = (const unsigned char *)set; *s; s++) map[(int)*s] = code; |
G.charmap[(unsigned char)*set++] = code; |
4087 |
} |
} |
4088 |
|
|
4089 |
static void update_ifs_map(void) |
static void update_charmap(void) |
4090 |
{ |
{ |
4091 |
/* char *ifs and char map[256] are both globals. */ |
G.ifs = getenv("IFS"); |
4092 |
ifs = getenv("IFS"); |
if (G.ifs == NULL) |
4093 |
if (ifs == NULL) ifs=" \t\n"; |
G.ifs = " \t\n"; |
4094 |
/* Precompute a list of 'flow through' behavior so it can be treated |
/* Precompute a list of 'flow through' behavior so it can be treated |
4095 |
* quickly up front. Computation is necessary because of IFS. |
* quickly up front. Computation is necessary because of IFS. |
4096 |
* Special case handling of IFS == " \t\n" is not implemented. |
* Special case handling of IFS == " \t\n" is not implemented. |
4097 |
* The map[] array only really needs two bits each, and on most machines |
* The charmap[] array only really needs two bits each, |
4098 |
* that would be faster because of the reduced L1 cache footprint. |
* and on most machines that would be faster (reduced L1 cache use). |
4099 |
*/ |
*/ |
4100 |
memset(map,0,sizeof(map)); /* most characters flow through always */ |
memset(G.charmap, CHAR_ORDINARY, sizeof(G.charmap)); |
4101 |
mapset("\\$'\"`", 3); /* never flow through */ |
#if ENABLE_HUSH_TICK |
4102 |
mapset("<>;&|(){}#", 1); /* flow through if quoted */ |
set_in_charmap("\\$\"`", CHAR_SPECIAL); |
4103 |
mapset(ifs, 2); /* also flow through if quoted */ |
#else |
4104 |
|
set_in_charmap("\\$\"", CHAR_SPECIAL); |
4105 |
|
#endif |
4106 |
|
set_in_charmap("<>;&|(){}#'", CHAR_ORDINARY_IF_QUOTED); |
4107 |
|
set_in_charmap(G.ifs, CHAR_IFS); /* are ordinary if quoted */ |
4108 |
} |
} |
4109 |
|
|
4110 |
/* most recursion does not come through here, the exception is |
/* Most recursion does not come through here, the exception is |
4111 |
* from builtin_source() */ |
* from builtin_source() and builtin_eval() */ |
4112 |
int parse_stream_outer(struct in_str *inp, int flag) |
static int parse_and_run_stream(struct in_str *inp, int parse_flag) |
4113 |
{ |
{ |
4114 |
|
struct parse_context ctx; |
4115 |
struct p_context ctx; |
o_string temp = NULL_O_STRING; |
|
o_string temp=NULL_O_STRING; |
|
4116 |
int rcode; |
int rcode; |
4117 |
|
|
4118 |
do { |
do { |
|
ctx.type = flag; |
|
4119 |
initialize_context(&ctx); |
initialize_context(&ctx); |
4120 |
update_ifs_map(); |
update_charmap(); |
4121 |
if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset(";$&|", 0); |
#if ENABLE_HUSH_INTERACTIVE |
4122 |
inp->promptmode=1; |
inp->promptmode = 0; /* PS1 */ |
4123 |
rcode = parse_stream(&temp, &ctx, inp, '\n'); |
#endif |
4124 |
|
/* We will stop & execute after each ';' or '\n'. |
4125 |
|
* Example: "sleep 9999; echo TEST" + ctrl-C: |
4126 |
|
* TEST should be printed */ |
4127 |
|
temp.o_assignment = MAYBE_ASSIGNMENT; |
4128 |
|
rcode = parse_stream(&temp, &ctx, inp, ";\n"); |
4129 |
|
#if HAS_KEYWORDS |
4130 |
if (rcode != 1 && ctx.old_flag != 0) { |
if (rcode != 1 && ctx.old_flag != 0) { |
4131 |
syntax(); |
syntax(NULL); |
4132 |
} |
} |
4133 |
if (rcode != 1 && ctx.old_flag == 0) { |
#endif |
4134 |
|
if (rcode != 1 IF_HAS_KEYWORDS(&& ctx.old_flag == 0)) { |
4135 |
done_word(&temp, &ctx); |
done_word(&temp, &ctx); |
4136 |
done_pipe(&ctx,PIPE_SEQ); |
done_pipe(&ctx, PIPE_SEQ); |
4137 |
run_list(ctx.list_head); |
debug_print_tree(ctx.list_head, 0); |
4138 |
|
debug_printf_exec("parse_stream_outer: run_and_free_list\n"); |
4139 |
|
run_and_free_list(ctx.list_head); |
4140 |
} else { |
} else { |
4141 |
|
/* We arrive here also if rcode == 1 (error in parse_stream) */ |
4142 |
|
#if HAS_KEYWORDS |
4143 |
if (ctx.old_flag != 0) { |
if (ctx.old_flag != 0) { |
4144 |
free(ctx.stack); |
free(ctx.stack); |
4145 |
b_reset(&temp); |
o_reset(&temp); |
4146 |
} |
} |
4147 |
temp.nonnull = 0; |
#endif |
4148 |
temp.quote = 0; |
/*temp.nonnull = 0; - o_free does it below */ |
4149 |
|
/*temp.o_quote = 0; - o_free does it below */ |
4150 |
|
free_pipe_list(ctx.list_head, /* indent: */ 0); |
4151 |
|
/* Discard all unprocessed line input, force prompt on */ |
4152 |
inp->p = NULL; |
inp->p = NULL; |
4153 |
free_pipe_list(ctx.list_head,0); |
#if ENABLE_HUSH_INTERACTIVE |
4154 |
|
inp->promptme = 1; |
4155 |
|
#endif |
4156 |
} |
} |
4157 |
b_free(&temp); |
o_free(&temp); |
4158 |
} while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP)); /* loop on syntax errors, return on EOF */ |
/* loop on syntax errors, return on EOF: */ |
4159 |
|
} while (rcode != -1 && !(parse_flag & PARSEFLAG_EXIT_FROM_LOOP)); |
4160 |
return 0; |
return 0; |
4161 |
} |
} |
4162 |
|
|
4163 |
static int parse_string_outer(const char *s, int flag) |
static int parse_and_run_string(const char *s, int parse_flag) |
4164 |
{ |
{ |
4165 |
struct in_str input; |
struct in_str input; |
4166 |
setup_string_in_str(&input, s); |
setup_string_in_str(&input, s); |
4167 |
return parse_stream_outer(&input, flag); |
return parse_and_run_stream(&input, parse_flag); |
4168 |
} |
} |
4169 |
|
|
4170 |
static int parse_file_outer(FILE *f) |
static int parse_and_run_file(FILE *f) |
4171 |
{ |
{ |
4172 |
int rcode; |
int rcode; |
4173 |
struct in_str input; |
struct in_str input; |
4174 |
setup_file_in_str(&input, f); |
setup_file_in_str(&input, f); |
4175 |
rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON); |
rcode = parse_and_run_stream(&input, 0 /* parse_flag */); |
4176 |
return rcode; |
return rcode; |
4177 |
} |
} |
4178 |
|
|
4179 |
|
#if ENABLE_HUSH_JOB |
4180 |
/* Make sure we have a controlling tty. If we get started under a job |
/* Make sure we have a controlling tty. If we get started under a job |
4181 |
* aware app (like bash for example), make sure we are now in charge so |
* aware app (like bash for example), make sure we are now in charge so |
4182 |
* we don't fight over who gets the foreground */ |
* we don't fight over who gets the foreground */ |
4183 |
static void setup_job_control(void) |
static void setup_job_control(void) |
4184 |
{ |
{ |
4185 |
static pid_t shell_pgrp; |
pid_t shell_pgrp; |
|
/* Loop until we are in the foreground. */ |
|
|
while (tcgetpgrp (shell_terminal) != (shell_pgrp = getpgrp ())) |
|
|
kill (- shell_pgrp, SIGTTIN); |
|
|
|
|
|
/* Ignore interactive and job-control signals. */ |
|
|
signal(SIGINT, SIG_IGN); |
|
|
signal(SIGQUIT, SIG_IGN); |
|
|
signal(SIGTERM, SIG_IGN); |
|
|
signal(SIGTSTP, SIG_IGN); |
|
|
signal(SIGTTIN, SIG_IGN); |
|
|
signal(SIGTTOU, SIG_IGN); |
|
|
signal(SIGCHLD, SIG_IGN); |
|
4186 |
|
|
4187 |
/* Put ourselves in our own process group. */ |
shell_pgrp = getpgrp(); |
4188 |
setsid(); |
close_on_exec_on(G.interactive_fd); |
4189 |
shell_pgrp = getpid(); |
|
4190 |
setpgid(shell_pgrp, shell_pgrp); |
/* If we were ran as 'hush &', |
4191 |
|
* sleep until we are in the foreground. */ |
4192 |
|
while (tcgetpgrp(G.interactive_fd) != shell_pgrp) { |
4193 |
|
/* Send TTIN to ourself (should stop us) */ |
4194 |
|
kill(- shell_pgrp, SIGTTIN); |
4195 |
|
shell_pgrp = getpgrp(); |
4196 |
|
} |
4197 |
|
|
4198 |
|
/* Ignore job-control and misc signals. */ |
4199 |
|
set_jobctrl_sighandler(SIG_IGN); |
4200 |
|
set_misc_sighandler(SIG_IGN); |
4201 |
|
//huh? signal(SIGCHLD, SIG_IGN); |
4202 |
|
|
4203 |
|
/* We _must_ restore tty pgrp on fatal signals */ |
4204 |
|
set_fatal_sighandler(sigexit); |
4205 |
|
|
4206 |
|
/* Put ourselves in our own process group. */ |
4207 |
|
bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ |
4208 |
/* Grab control of the terminal. */ |
/* Grab control of the terminal. */ |
4209 |
tcsetpgrp(shell_terminal, shell_pgrp); |
tcsetpgrp(G.interactive_fd, getpid()); |
4210 |
} |
} |
4211 |
|
#endif |
4212 |
|
|
4213 |
|
|
4214 |
|
int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
4215 |
int hush_main(int argc, char **argv) |
int hush_main(int argc, char **argv) |
4216 |
{ |
{ |
4217 |
|
static const struct variable const_shell_ver = { |
4218 |
|
.next = NULL, |
4219 |
|
.varstr = (char*)hush_version_str, |
4220 |
|
.max_len = 1, /* 0 can provoke free(name) */ |
4221 |
|
.flg_export = 1, |
4222 |
|
.flg_read_only = 1, |
4223 |
|
}; |
4224 |
|
|
4225 |
int opt; |
int opt; |
4226 |
FILE *input; |
FILE *input; |
4227 |
char **e = environ; |
char **e; |
4228 |
|
struct variable *cur_var; |
4229 |
|
|
4230 |
/* XXX what should these be while sourcing /etc/profile? */ |
INIT_G(); |
|
global_argc = argc; |
|
|
global_argv = argv; |
|
|
|
|
|
/* (re?) initialize globals. Sometimes hush_main() ends up calling |
|
|
* hush_main(), therefore we cannot rely on the BSS to zero out this |
|
|
* stuff. Reset these to 0 every time. */ |
|
|
ifs = NULL; |
|
|
/* map[] is taken care of with call to update_ifs_map() */ |
|
|
fake_mode = 0; |
|
|
interactive = 0; |
|
|
close_me_head = NULL; |
|
|
last_bg_pid = 0; |
|
|
job_list = NULL; |
|
|
last_jobid = 0; |
|
4231 |
|
|
4232 |
/* Initialize some more globals to non-zero values */ |
G.root_pid = getpid(); |
|
set_cwd(); |
|
|
if (ENABLE_FEATURE_COMMAND_EDITING) cmdedit_set_initial_prompt(); |
|
|
else PS1 = NULL; |
|
|
PS2 = "> "; |
|
4233 |
|
|
4234 |
/* initialize our shell local variables with the values |
/* Deal with HUSH_VERSION */ |
4235 |
|
G.shell_ver = const_shell_ver; /* copying struct here */ |
4236 |
|
G.top_var = &G.shell_ver; |
4237 |
|
debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); |
4238 |
|
unsetenv("HUSH_VERSION"); /* in case it exists in initial env */ |
4239 |
|
/* Initialize our shell local variables with the values |
4240 |
* currently living in the environment */ |
* currently living in the environment */ |
4241 |
if (e) { |
cur_var = G.top_var; |
4242 |
for (; *e; e++) |
e = environ; |
4243 |
set_local_var(*e, 2); /* without call putenv() */ |
if (e) while (*e) { |
4244 |
|
char *value = strchr(*e, '='); |
4245 |
|
if (value) { /* paranoia */ |
4246 |
|
cur_var->next = xzalloc(sizeof(*cur_var)); |
4247 |
|
cur_var = cur_var->next; |
4248 |
|
cur_var->varstr = *e; |
4249 |
|
cur_var->max_len = strlen(*e); |
4250 |
|
cur_var->flg_export = 1; |
4251 |
|
} |
4252 |
|
e++; |
4253 |
} |
} |
4254 |
|
debug_printf_env("putenv '%s'\n", hush_version_str); |
4255 |
|
putenv((char *)hush_version_str); /* reinstate HUSH_VERSION */ |
4256 |
|
|
4257 |
last_return_code=EXIT_SUCCESS; |
#if ENABLE_FEATURE_EDITING |
4258 |
|
G.line_input_state = new_line_input_t(FOR_SHELL); |
4259 |
|
#endif |
4260 |
|
/* XXX what should these be while sourcing /etc/profile? */ |
4261 |
|
G.global_argc = argc; |
4262 |
|
G.global_argv = argv; |
4263 |
|
/* Initialize some more globals to non-zero values */ |
4264 |
|
set_cwd(); |
4265 |
|
#if ENABLE_HUSH_INTERACTIVE |
4266 |
|
#if ENABLE_FEATURE_EDITING |
4267 |
|
cmdedit_set_initial_prompt(); |
4268 |
|
#endif |
4269 |
|
G.PS2 = "> "; |
4270 |
|
#endif |
4271 |
|
|
4272 |
|
if (EXIT_SUCCESS) /* otherwise is already done */ |
4273 |
|
G.last_return_code = EXIT_SUCCESS; |
4274 |
|
|
4275 |
if (argv[0] && argv[0][0] == '-') { |
if (argv[0] && argv[0][0] == '-') { |
4276 |
debug_printf("\nsourcing /etc/profile\n"); |
debug_printf("sourcing /etc/profile\n"); |
4277 |
if ((input = fopen("/etc/profile", "r")) != NULL) { |
input = fopen_for_read("/etc/profile"); |
4278 |
mark_open(fileno(input)); |
if (input != NULL) { |
4279 |
parse_file_outer(input); |
close_on_exec_on(fileno(input)); |
4280 |
mark_closed(fileno(input)); |
parse_and_run_file(input); |
4281 |
fclose(input); |
fclose(input); |
4282 |
} |
} |
4283 |
} |
} |
4284 |
input=stdin; |
input = stdin; |
4285 |
|
|
4286 |
while ((opt = getopt(argc, argv, "c:xif")) > 0) { |
while ((opt = getopt(argc, argv, "c:xif")) > 0) { |
4287 |
switch (opt) { |
switch (opt) { |
4288 |
case 'c': |
case 'c': |
4289 |
{ |
G.global_argv = argv + optind; |
4290 |
global_argv = argv+optind; |
if (!argv[optind]) { |
4291 |
global_argc = argc-optind; |
/* -c 'script' (no params): prevent empty $0 */ |
4292 |
opt = parse_string_outer(optarg, FLAG_PARSE_SEMICOLON); |
*--G.global_argv = argv[0]; |
4293 |
goto final_return; |
optind--; |
4294 |
} |
} /* else -c 'script' PAR0 PAR1: $0 is PAR0 */ |
4295 |
break; |
G.global_argc = argc - optind; |
4296 |
case 'i': |
opt = parse_and_run_string(optarg, 0 /* parse_flag */); |
4297 |
interactive++; |
goto final_return; |
4298 |
break; |
case 'i': |
4299 |
case 'f': |
/* Well, we cannot just declare interactiveness, |
4300 |
fake_mode++; |
* we have to have some stuff (ctty, etc) */ |
4301 |
break; |
/* G.interactive_fd++; */ |
4302 |
default: |
break; |
4303 |
|
case 'f': |
4304 |
|
G.fake_mode = 1; |
4305 |
|
break; |
4306 |
|
default: |
4307 |
#ifndef BB_VER |
#ifndef BB_VER |
4308 |
fprintf(stderr, "Usage: sh [FILE]...\n" |
fprintf(stderr, "Usage: sh [FILE]...\n" |
4309 |
" or: sh -c command [args]...\n\n"); |
" or: sh -c command [args]...\n\n"); |
4310 |
exit(EXIT_FAILURE); |
exit(EXIT_FAILURE); |
4311 |
#else |
#else |
4312 |
bb_show_usage(); |
bb_show_usage(); |
4313 |
#endif |
#endif |
4314 |
} |
} |
4315 |
} |
} |
4316 |
/* A shell is interactive if the `-i' flag was given, or if all of |
#if ENABLE_HUSH_JOB |
4317 |
|
/* A shell is interactive if the '-i' flag was given, or if all of |
4318 |
* the following conditions are met: |
* the following conditions are met: |
4319 |
* no -c command |
* no -c command |
4320 |
* no arguments remaining or the -s flag given |
* no arguments remaining or the -s flag given |
4321 |
* standard input is a terminal |
* standard input is a terminal |
4322 |
* standard output is a terminal |
* standard output is a terminal |
4323 |
* Refer to Posix.2, the description of the `sh' utility. */ |
* Refer to Posix.2, the description of the 'sh' utility. */ |
4324 |
if (argv[optind]==NULL && input==stdin && |
if (argv[optind] == NULL && input == stdin |
4325 |
isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { |
&& isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) |
4326 |
interactive++; |
) { |
4327 |
} |
G.saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); |
4328 |
|
debug_printf("saved_tty_pgrp=%d\n", G.saved_tty_pgrp); |
4329 |
debug_printf("\ninteractive=%d\n", interactive); |
if (G.saved_tty_pgrp >= 0) { |
4330 |
if (interactive) { |
/* try to dup to high fd#, >= 255 */ |
4331 |
|
G.interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); |
4332 |
|
if (G.interactive_fd < 0) { |
4333 |
|
/* try to dup to any fd */ |
4334 |
|
G.interactive_fd = dup(STDIN_FILENO); |
4335 |
|
if (G.interactive_fd < 0) |
4336 |
|
/* give up */ |
4337 |
|
G.interactive_fd = 0; |
4338 |
|
} |
4339 |
|
// TODO: track & disallow any attempts of user |
4340 |
|
// to (inadvertently) close/redirect it |
4341 |
|
} |
4342 |
|
} |
4343 |
|
debug_printf("G.interactive_fd=%d\n", G.interactive_fd); |
4344 |
|
if (G.interactive_fd) { |
4345 |
|
fcntl(G.interactive_fd, F_SETFD, FD_CLOEXEC); |
4346 |
/* Looks like they want an interactive shell */ |
/* Looks like they want an interactive shell */ |
|
#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET |
|
|
printf( "\n\n%s hush - the humble shell v0.01 (testing)\n", |
|
|
BB_BANNER); |
|
|
printf( "Enter 'help' for a list of built-in commands.\n\n"); |
|
|
#endif |
|
4347 |
setup_job_control(); |
setup_job_control(); |
4348 |
|
/* -1 is special - makes xfuncs longjmp, not exit |
4349 |
|
* (we reset die_sleep = 0 whereever we [v]fork) */ |
4350 |
|
die_sleep = -1; |
4351 |
|
if (setjmp(die_jmp)) { |
4352 |
|
/* xfunc has failed! die die die */ |
4353 |
|
hush_exit(xfunc_error_retval); |
4354 |
|
} |
4355 |
|
#if !ENABLE_FEATURE_SH_EXTRA_QUIET |
4356 |
|
printf("\n\n%s hush - the humble shell v"HUSH_VER_STR"\n", bb_banner); |
4357 |
|
printf("Enter 'help' for a list of built-in commands.\n\n"); |
4358 |
|
#endif |
4359 |
|
} |
4360 |
|
#elif ENABLE_HUSH_INTERACTIVE |
4361 |
|
/* no job control compiled, only prompt/line editing */ |
4362 |
|
if (argv[optind] == NULL && input == stdin |
4363 |
|
&& isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) |
4364 |
|
) { |
4365 |
|
G.interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); |
4366 |
|
if (G.interactive_fd < 0) { |
4367 |
|
/* try to dup to any fd */ |
4368 |
|
G.interactive_fd = dup(STDIN_FILENO); |
4369 |
|
if (G.interactive_fd < 0) |
4370 |
|
/* give up */ |
4371 |
|
G.interactive_fd = 0; |
4372 |
|
} |
4373 |
|
if (G.interactive_fd) { |
4374 |
|
fcntl(G.interactive_fd, F_SETFD, FD_CLOEXEC); |
4375 |
|
set_misc_sighandler(SIG_IGN); |
4376 |
|
} |
4377 |
} |
} |
4378 |
|
#endif |
4379 |
|
|
4380 |
if (argv[optind]==NULL) { |
if (argv[optind] == NULL) { |
4381 |
opt=parse_file_outer(stdin); |
opt = parse_and_run_file(stdin); |
4382 |
goto final_return; |
} else { |
4383 |
|
debug_printf("\nrunning script '%s'\n", argv[optind]); |
4384 |
|
G.global_argv = argv + optind; |
4385 |
|
G.global_argc = argc - optind; |
4386 |
|
input = xfopen_for_read(argv[optind]); |
4387 |
|
fcntl(fileno(input), F_SETFD, FD_CLOEXEC); |
4388 |
|
opt = parse_and_run_file(input); |
4389 |
} |
} |
4390 |
|
|
4391 |
debug_printf("\nrunning script '%s'\n", argv[optind]); |
final_return: |
|
global_argv = argv+optind; |
|
|
global_argc = argc-optind; |
|
|
input = xfopen(argv[optind], "r"); |
|
|
opt = parse_file_outer(input); |
|
4392 |
|
|
4393 |
#ifdef CONFIG_FEATURE_CLEAN_UP |
#if ENABLE_FEATURE_CLEAN_UP |
4394 |
fclose(input); |
fclose(input); |
4395 |
if (cwd && cwd != bb_msg_unknown) |
if (G.cwd != bb_msg_unknown) |
4396 |
free((char*)cwd); |
free((char*)G.cwd); |
4397 |
|
cur_var = G.top_var->next; |
4398 |
|
while (cur_var) { |
4399 |
|
struct variable *tmp = cur_var; |
4400 |
|
if (!cur_var->max_len) |
4401 |
|
free(cur_var->varstr); |
4402 |
|
cur_var = cur_var->next; |
4403 |
|
free(tmp); |
4404 |
|
} |
4405 |
|
#endif |
4406 |
|
hush_exit(opt ? opt : G.last_return_code); |
4407 |
|
} |
4408 |
|
|
4409 |
|
|
4410 |
|
#if ENABLE_LASH |
4411 |
|
int lash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
4412 |
|
int lash_main(int argc, char **argv) |
4413 |
|
{ |
4414 |
|
//bb_error_msg("lash is deprecated, please use hush instead"); |
4415 |
|
return hush_main(argc, argv); |
4416 |
|
} |
4417 |
|
#endif |
4418 |
|
|
4419 |
|
|
4420 |
|
/* |
4421 |
|
* Built-ins |
4422 |
|
*/ |
4423 |
|
static int builtin_true(char **argv UNUSED_PARAM) |
4424 |
|
{ |
4425 |
|
return 0; |
4426 |
|
} |
4427 |
|
|
4428 |
|
static int builtin_test(char **argv) |
4429 |
|
{ |
4430 |
|
int argc = 0; |
4431 |
|
while (*argv) { |
4432 |
|
argc++; |
4433 |
|
argv++; |
4434 |
|
} |
4435 |
|
return test_main(argc, argv - argc); |
4436 |
|
} |
4437 |
|
|
4438 |
|
static int builtin_echo(char **argv) |
4439 |
|
{ |
4440 |
|
int argc = 0; |
4441 |
|
while (*argv) { |
4442 |
|
argc++; |
4443 |
|
argv++; |
4444 |
|
} |
4445 |
|
return echo_main(argc, argv - argc); |
4446 |
|
} |
4447 |
|
|
4448 |
|
static int builtin_eval(char **argv) |
4449 |
|
{ |
4450 |
|
int rcode = EXIT_SUCCESS; |
4451 |
|
|
4452 |
|
if (argv[1]) { |
4453 |
|
char *str = expand_strvec_to_string(argv + 1); |
4454 |
|
parse_and_run_string(str, PARSEFLAG_EXIT_FROM_LOOP); |
4455 |
|
free(str); |
4456 |
|
rcode = G.last_return_code; |
4457 |
|
} |
4458 |
|
return rcode; |
4459 |
|
} |
4460 |
|
|
4461 |
|
static int builtin_cd(char **argv) |
4462 |
|
{ |
4463 |
|
const char *newdir; |
4464 |
|
if (argv[1] == NULL) { |
4465 |
|
// bash does nothing (exitcode 0) if HOME is ""; if it's unset, |
4466 |
|
// bash says "bash: cd: HOME not set" and does nothing (exitcode 1) |
4467 |
|
newdir = getenv("HOME") ? : "/"; |
4468 |
|
} else |
4469 |
|
newdir = argv[1]; |
4470 |
|
if (chdir(newdir)) { |
4471 |
|
printf("cd: %s: %s\n", newdir, strerror(errno)); |
4472 |
|
return EXIT_FAILURE; |
4473 |
|
} |
4474 |
|
set_cwd(); |
4475 |
|
return EXIT_SUCCESS; |
4476 |
|
} |
4477 |
|
|
4478 |
|
static int builtin_exec(char **argv) |
4479 |
|
{ |
4480 |
|
if (argv[1] == NULL) |
4481 |
|
return EXIT_SUCCESS; /* bash does this */ |
4482 |
{ |
{ |
4483 |
struct variables *cur, *tmp; |
#if !BB_MMU |
4484 |
for(cur = top_vars; cur; cur = tmp) { |
nommu_save_t dummy; |
4485 |
tmp = cur->next; |
#endif |
4486 |
if (!cur->flg_read_only) { |
// FIXME: if exec fails, bash does NOT exit! We do... |
4487 |
free(cur->name); |
pseudo_exec_argv(&dummy, argv + 1, 0, NULL); |
4488 |
free(cur->value); |
/* never returns */ |
4489 |
free(cur); |
} |
4490 |
|
} |
4491 |
|
|
4492 |
|
static int builtin_exit(char **argv) |
4493 |
|
{ |
4494 |
|
// TODO: bash does it ONLY on top-level sh exit (+interacive only?) |
4495 |
|
//puts("exit"); /* bash does it */ |
4496 |
|
// TODO: warn if we have background jobs: "There are stopped jobs" |
4497 |
|
// On second consecutive 'exit', exit anyway. |
4498 |
|
if (argv[1] == NULL) |
4499 |
|
hush_exit(G.last_return_code); |
4500 |
|
/* mimic bash: exit 123abc == exit 255 + error msg */ |
4501 |
|
xfunc_error_retval = 255; |
4502 |
|
/* bash: exit -2 == exit 254, no error msg */ |
4503 |
|
hush_exit(xatoi(argv[1]) & 0xff); |
4504 |
|
} |
4505 |
|
|
4506 |
|
static int builtin_export(char **argv) |
4507 |
|
{ |
4508 |
|
const char *value; |
4509 |
|
char *name = argv[1]; |
4510 |
|
|
4511 |
|
if (name == NULL) { |
4512 |
|
// TODO: |
4513 |
|
// ash emits: export VAR='VAL' |
4514 |
|
// bash: declare -x VAR="VAL" |
4515 |
|
// (both also escape as needed (quotes, $, etc)) |
4516 |
|
char **e = environ; |
4517 |
|
if (e) |
4518 |
|
while (*e) |
4519 |
|
puts(*e++); |
4520 |
|
return EXIT_SUCCESS; |
4521 |
|
} |
4522 |
|
|
4523 |
|
value = strchr(name, '='); |
4524 |
|
if (!value) { |
4525 |
|
/* They are exporting something without a =VALUE */ |
4526 |
|
struct variable *var; |
4527 |
|
|
4528 |
|
var = get_local_var(name); |
4529 |
|
if (var) { |
4530 |
|
var->flg_export = 1; |
4531 |
|
debug_printf_env("%s: putenv '%s'\n", __func__, var->varstr); |
4532 |
|
putenv(var->varstr); |
4533 |
|
} |
4534 |
|
/* bash does not return an error when trying to export |
4535 |
|
* an undefined variable. Do likewise. */ |
4536 |
|
return EXIT_SUCCESS; |
4537 |
|
} |
4538 |
|
|
4539 |
|
set_local_var(xstrdup(name), 1); |
4540 |
|
return EXIT_SUCCESS; |
4541 |
|
} |
4542 |
|
|
4543 |
|
#if ENABLE_HUSH_JOB |
4544 |
|
/* built-in 'fg' and 'bg' handler */ |
4545 |
|
static int builtin_fg_bg(char **argv) |
4546 |
|
{ |
4547 |
|
int i, jobnum; |
4548 |
|
struct pipe *pi; |
4549 |
|
|
4550 |
|
if (!G.interactive_fd) |
4551 |
|
return EXIT_FAILURE; |
4552 |
|
/* If they gave us no args, assume they want the last backgrounded task */ |
4553 |
|
if (!argv[1]) { |
4554 |
|
for (pi = G.job_list; pi; pi = pi->next) { |
4555 |
|
if (pi->jobid == G.last_jobid) { |
4556 |
|
goto found; |
4557 |
} |
} |
4558 |
} |
} |
4559 |
|
bb_error_msg("%s: no current job", argv[0]); |
4560 |
|
return EXIT_FAILURE; |
4561 |
|
} |
4562 |
|
if (sscanf(argv[1], "%%%d", &jobnum) != 1) { |
4563 |
|
bb_error_msg("%s: bad argument '%s'", argv[0], argv[1]); |
4564 |
|
return EXIT_FAILURE; |
4565 |
|
} |
4566 |
|
for (pi = G.job_list; pi; pi = pi->next) { |
4567 |
|
if (pi->jobid == jobnum) { |
4568 |
|
goto found; |
4569 |
|
} |
4570 |
|
} |
4571 |
|
bb_error_msg("%s: %d: no such job", argv[0], jobnum); |
4572 |
|
return EXIT_FAILURE; |
4573 |
|
found: |
4574 |
|
// TODO: bash prints a string representation |
4575 |
|
// of job being foregrounded (like "sleep 1 | cat") |
4576 |
|
if (*argv[0] == 'f') { |
4577 |
|
/* Put the job into the foreground. */ |
4578 |
|
tcsetpgrp(G.interactive_fd, pi->pgrp); |
4579 |
|
} |
4580 |
|
|
4581 |
|
/* Restart the processes in the job */ |
4582 |
|
debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_cmds, pi->pgrp); |
4583 |
|
for (i = 0; i < pi->num_cmds; i++) { |
4584 |
|
debug_printf_jobs("reviving pid %d\n", pi->cmds[i].pid); |
4585 |
|
pi->cmds[i].is_stopped = 0; |
4586 |
|
} |
4587 |
|
pi->stopped_cmds = 0; |
4588 |
|
|
4589 |
|
i = kill(- pi->pgrp, SIGCONT); |
4590 |
|
if (i < 0) { |
4591 |
|
if (errno == ESRCH) { |
4592 |
|
delete_finished_bg_job(pi); |
4593 |
|
return EXIT_SUCCESS; |
4594 |
|
} else { |
4595 |
|
bb_perror_msg("kill (SIGCONT)"); |
4596 |
|
} |
4597 |
|
} |
4598 |
|
|
4599 |
|
if (*argv[0] == 'f') { |
4600 |
|
remove_bg_job(pi); |
4601 |
|
return checkjobs_and_fg_shell(pi); |
4602 |
} |
} |
4603 |
|
return EXIT_SUCCESS; |
4604 |
|
} |
4605 |
#endif |
#endif |
4606 |
|
|
4607 |
final_return: |
#if ENABLE_HUSH_HELP |
4608 |
return opt ? opt : last_return_code; |
static int builtin_help(char **argv UNUSED_PARAM) |
4609 |
|
{ |
4610 |
|
const struct built_in_command *x; |
4611 |
|
|
4612 |
|
printf("\nBuilt-in commands:\n"); |
4613 |
|
printf("-------------------\n"); |
4614 |
|
for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { |
4615 |
|
printf("%s\t%s\n", x->cmd, x->descr); |
4616 |
|
} |
4617 |
|
printf("\n\n"); |
4618 |
|
return EXIT_SUCCESS; |
4619 |
} |
} |
4620 |
|
#endif |
4621 |
|
|
4622 |
static char *insert_var_value(char *inp) |
#if ENABLE_HUSH_JOB |
4623 |
|
static int builtin_jobs(char **argv UNUSED_PARAM) |
4624 |
{ |
{ |
4625 |
int res_str_len = 0; |
struct pipe *job; |
4626 |
int len; |
const char *status_string; |
|
int done = 0; |
|
|
char *p, *p1, *res_str = NULL; |
|
4627 |
|
|
4628 |
while ((p = strchr(inp, SPECIAL_VAR_SYMBOL))) { |
for (job = G.job_list; job; job = job->next) { |
4629 |
if (p != inp) { |
if (job->alive_cmds == job->stopped_cmds) |
4630 |
len = p - inp; |
status_string = "Stopped"; |
4631 |
res_str = xrealloc(res_str, (res_str_len + len)); |
else |
4632 |
strncpy((res_str + res_str_len), inp, len); |
status_string = "Running"; |
4633 |
res_str_len += len; |
|
4634 |
} |
printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext); |
4635 |
inp = ++p; |
} |
4636 |
p = strchr(inp, SPECIAL_VAR_SYMBOL); |
return EXIT_SUCCESS; |
4637 |
*p = '\0'; |
} |
4638 |
if ((p1 = lookup_param(inp))) { |
#endif |
4639 |
len = res_str_len + strlen(p1); |
|
4640 |
res_str = xrealloc(res_str, (1 + len)); |
static int builtin_pwd(char **argv UNUSED_PARAM) |
4641 |
strcpy((res_str + res_str_len), p1); |
{ |
4642 |
res_str_len = len; |
puts(set_cwd()); |
4643 |
} |
return EXIT_SUCCESS; |
4644 |
*p = SPECIAL_VAR_SYMBOL; |
} |
4645 |
inp = ++p; |
|
4646 |
done = 1; |
static int builtin_read(char **argv) |
4647 |
} |
{ |
4648 |
if (done) { |
char *string; |
4649 |
res_str = xrealloc(res_str, (1 + res_str_len + strlen(inp))); |
const char *name = argv[1] ? argv[1] : "REPLY"; |
4650 |
strcpy((res_str + res_str_len), inp); |
|
4651 |
while ((p = strchr(res_str, '\n'))) { |
string = xmalloc_reads(STDIN_FILENO, xasprintf("%s=", name), NULL); |
4652 |
*p = ' '; |
return set_local_var(string, 0); |
4653 |
} |
} |
4654 |
} |
|
4655 |
return (res_str == NULL) ? inp : res_str; |
/* built-in 'set' handler |
4656 |
} |
* SUSv3 says: |
4657 |
|
* set [-abCefmnuvx] [-h] [-o option] [argument...] |
4658 |
static char **make_list_in(char **inp, char *name) |
* set [+abCefmnuvx] [+h] [+o option] [argument...] |
4659 |
{ |
* set -- [argument...] |
4660 |
int len, i; |
* set -o |
4661 |
int name_len = strlen(name); |
* set +o |
4662 |
int n = 0; |
* Implementations shall support the options in both their hyphen and |
4663 |
char **list; |
* plus-sign forms. These options can also be specified as options to sh. |
4664 |
char *p1, *p2, *p3; |
* Examples: |
4665 |
|
* Write out all variables and their values: set |
4666 |
|
* Set $1, $2, and $3 and set "$#" to 3: set c a b |
4667 |
|
* Turn on the -x and -v options: set -xv |
4668 |
|
* Unset all positional parameters: set -- |
4669 |
|
* Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x" |
4670 |
|
* Set the positional parameters to the expansion of x, even if x expands |
4671 |
|
* with a leading '-' or '+': set -- $x |
4672 |
|
* |
4673 |
|
* So far, we only support "set -- [argument...]" by ignoring all options |
4674 |
|
* (also, "-o option" will be mishandled by taking "option" as parameter #1). |
4675 |
|
*/ |
4676 |
|
static int builtin_set(char **argv) |
4677 |
|
{ |
4678 |
|
struct variable *e; |
4679 |
|
char **pp; |
4680 |
|
char *arg = *++argv; |
4681 |
|
|
4682 |
|
if (arg == NULL) { |
4683 |
|
for (e = G.top_var; e; e = e->next) |
4684 |
|
puts(e->varstr); |
4685 |
|
} else { |
4686 |
|
/* NB: G.global_argv[0] ($0) is never freed/changed */ |
4687 |
|
|
4688 |
/* create list of variable values */ |
if (G.global_args_malloced) { |
4689 |
list = xmalloc(sizeof(*list)); |
pp = G.global_argv; |
4690 |
for (i = 0; inp[i]; i++) { |
while (*++pp) |
4691 |
p3 = insert_var_value(inp[i]); |
free(*pp); |
4692 |
p1 = p3; |
G.global_argv[1] = NULL; |
4693 |
while (*p1) { |
} else { |
4694 |
if ((*p1 == ' ')) { |
G.global_args_malloced = 1; |
4695 |
p1++; |
pp = xzalloc(sizeof(pp[0]) * 2); |
4696 |
|
pp[0] = G.global_argv[0]; /* retain $0 */ |
4697 |
|
G.global_argv = pp; |
4698 |
|
} |
4699 |
|
do { |
4700 |
|
if (arg[0] == '+') |
4701 |
continue; |
continue; |
4702 |
|
if (arg[0] != '-') |
4703 |
|
break; |
4704 |
|
if (arg[1] == '-' && arg[2] == '\0') { |
4705 |
|
argv++; |
4706 |
|
break; |
4707 |
} |
} |
4708 |
if ((p2 = strchr(p1, ' '))) { |
} while ((arg = *++argv) != NULL); |
4709 |
len = p2 - p1; |
/* Now argv[0] is 1st argument */ |
4710 |
} else { |
|
4711 |
len = strlen(p1); |
/* This realloc's G.global_argv */ |
4712 |
p2 = p1 + len; |
G.global_argv = pp = add_strings_to_strings(G.global_argv, argv, /*dup:*/ 1); |
4713 |
} |
G.global_argc = 1; |
4714 |
/* we use n + 2 in realloc for list,because we add |
while (*++pp) |
4715 |
* new element and then we will add NULL element */ |
G.global_argc++; |
4716 |
list = xrealloc(list, sizeof(*list) * (n + 2)); |
} |
4717 |
list[n] = xmalloc(2 + name_len + len); |
|
4718 |
strcpy(list[n], name); |
return EXIT_SUCCESS; |
4719 |
strcat(list[n], "="); |
} |
4720 |
strncat(list[n], p1, len); |
|
4721 |
list[n++][name_len + len + 1] = '\0'; |
static int builtin_shift(char **argv) |
4722 |
p1 = p2; |
{ |
4723 |
|
int n = 1; |
4724 |
|
if (argv[1]) { |
4725 |
|
n = atoi(argv[1]); |
4726 |
|
} |
4727 |
|
if (n >= 0 && n < G.global_argc) { |
4728 |
|
if (G.global_args_malloced) { |
4729 |
|
int m = 1; |
4730 |
|
while (m <= n) |
4731 |
|
free(G.global_argv[m++]); |
4732 |
|
} |
4733 |
|
G.global_argc -= n; |
4734 |
|
memmove(&G.global_argv[1], &G.global_argv[n+1], |
4735 |
|
G.global_argc * sizeof(G.global_argv[0])); |
4736 |
|
return EXIT_SUCCESS; |
4737 |
|
} |
4738 |
|
return EXIT_FAILURE; |
4739 |
|
} |
4740 |
|
|
4741 |
|
static int builtin_source(char **argv) |
4742 |
|
{ |
4743 |
|
FILE *input; |
4744 |
|
int status; |
4745 |
|
|
4746 |
|
if (argv[1] == NULL) |
4747 |
|
return EXIT_FAILURE; |
4748 |
|
|
4749 |
|
/* XXX search through $PATH is missing */ |
4750 |
|
input = fopen_for_read(argv[1]); |
4751 |
|
if (!input) { |
4752 |
|
bb_error_msg("can't open '%s'", argv[1]); |
4753 |
|
return EXIT_FAILURE; |
4754 |
|
} |
4755 |
|
close_on_exec_on(fileno(input)); |
4756 |
|
|
4757 |
|
/* Now run the file */ |
4758 |
|
/* XXX argv and argc are broken; need to save old G.global_argv |
4759 |
|
* (pointer only is OK!) on this stack frame, |
4760 |
|
* set G.global_argv=argv+1, recurse, and restore. */ |
4761 |
|
status = parse_and_run_file(input); |
4762 |
|
fclose(input); |
4763 |
|
return status; |
4764 |
|
} |
4765 |
|
|
4766 |
|
static int builtin_umask(char **argv) |
4767 |
|
{ |
4768 |
|
mode_t new_umask; |
4769 |
|
const char *arg = argv[1]; |
4770 |
|
char *end; |
4771 |
|
if (arg) { |
4772 |
|
new_umask = strtoul(arg, &end, 8); |
4773 |
|
if (*end != '\0' || end == arg) { |
4774 |
|
return EXIT_FAILURE; |
4775 |
} |
} |
4776 |
if (p3 != inp[i]) free(p3); |
} else { |
4777 |
|
new_umask = umask(0); |
4778 |
|
printf("%.3o\n", (unsigned) new_umask); |
4779 |
} |
} |
4780 |
list[n] = NULL; |
umask(new_umask); |
4781 |
return list; |
return EXIT_SUCCESS; |
4782 |
} |
} |
4783 |
|
|
4784 |
/* Make new string for parser */ |
static int builtin_unset(char **argv) |
|
static char * make_string(char ** inp) |
|
4785 |
{ |
{ |
4786 |
char *p; |
/* bash always returns true */ |
4787 |
char *str = NULL; |
unset_local_var(argv[1]); |
4788 |
int n; |
return EXIT_SUCCESS; |
4789 |
int len = 2; |
} |
4790 |
|
|
4791 |
for (n = 0; inp[n]; n++) { |
#if ENABLE_HUSH_LOOPS |
4792 |
p = insert_var_value(inp[n]); |
static int builtin_break(char **argv) |
4793 |
str = xrealloc(str, (len + strlen(p))); |
{ |
4794 |
if (n) { |
if (G.depth_of_loop == 0) { |
4795 |
strcat(str, " "); |
bb_error_msg("%s: only meaningful in a loop", argv[0]); |
4796 |
} else { |
return EXIT_SUCCESS; /* bash compat */ |
4797 |
*str = '\0'; |
} |
4798 |
|
G.flag_break_continue++; /* BC_BREAK = 1 */ |
4799 |
|
G.depth_break_continue = 1; |
4800 |
|
if (argv[1]) { |
4801 |
|
G.depth_break_continue = bb_strtou(argv[1], NULL, 10); |
4802 |
|
if (errno || !G.depth_break_continue || argv[2]) { |
4803 |
|
bb_error_msg("%s: bad arguments", argv[0]); |
4804 |
|
G.flag_break_continue = BC_BREAK; |
4805 |
|
G.depth_break_continue = UINT_MAX; |
4806 |
} |
} |
4807 |
strcat(str, p); |
} |
4808 |
len = strlen(str) + 3; |
if (G.depth_of_loop < G.depth_break_continue) |
4809 |
if (p != inp[n]) free(p); |
G.depth_break_continue = G.depth_of_loop; |
4810 |
} |
return EXIT_SUCCESS; |
|
len = strlen(str); |
|
|
*(str + len) = '\n'; |
|
|
*(str + len + 1) = '\0'; |
|
|
return str; |
|
4811 |
} |
} |
4812 |
|
|
4813 |
|
static int builtin_continue(char **argv) |
4814 |
|
{ |
4815 |
|
G.flag_break_continue = 1; /* BC_CONTINUE = 2 = 1+1 */ |
4816 |
|
return builtin_break(argv); |
4817 |
|
} |
4818 |
|
#endif |