#endif
-#define SPECIAL_VAR_SYMBOL 3
-
+#define SPECIAL_VAR_SYMBOL 3
#define PARSEFLAG_EXIT_FROM_LOOP 1
-#define PARSEFLAG_SEMICOLON (1 << 1) /* symbol ';' is special for parser */
-#define PARSEFLAG_REPARSING (1 << 2) /* >= 2nd pass */
typedef enum {
REDIRECT_INPUT = 1,
struct pipe *pipe;
struct redir_struct *pending_redirect;
smallint res_w;
- smallint parse_type; /* bitmask of PARSEFLAG_xxx, defines type of parser: ";$" common or special symbol */
smallint ctx_inverted; /* "! cmd | cmd" */
int old_flag; /* bitmask of FLAG_xxx, for figuring out valid reserved words */
struct p_context *stack;
- /* How about quoting status? */
};
struct redir_struct {
static void pseudo_exec_argv(char **ptrs2free, char **argv) ATTRIBUTE_NORETURN;
static void pseudo_exec(char **ptrs2free, struct child_prog *child) ATTRIBUTE_NORETURN;
static int run_pipe(struct pipe *pi);
-/* extended glob support: */
-static char **globhack(const char *src, char **strings);
-static int glob_needed(const char *s);
-static int xglob(o_string *dest, char ***pglob);
/* variable assignment: */
static int is_assignment(const char *s);
/* data structure manipulation: */
}
}
-
#if !BB_MMU
#define EXTRA_PTRS 5 /* 1 for NULL, 1 for args, 3 for paranoid reasons */
static char **alloc_ptrs(char **argv)
}
#endif
+#ifdef DEBUG_EXPAND
+static void debug_print_strings(const char *prefix, char **vv)
+{
+ fprintf(stderr, "%s:\n", prefix);
+ while (*vv)
+ fprintf(stderr, " '%s'\n", *vv++);
+}
+#else
+#define debug_print_strings(prefix, vv) ((void)0)
+#endif
+
/* Function prototypes for builtins */
static int builtin_cd(char **argv);
if (argv[1]) {
char *str = expand_strvec_to_string(argv + 1);
- parse_and_run_string(str, PARSEFLAG_EXIT_FROM_LOOP |
- PARSEFLAG_SEMICOLON);
+ parse_and_run_string(str, PARSEFLAG_EXIT_FROM_LOOP);
free(str);
rcode = last_return_code;
}
//puts("exit"); /* bash does it */
// TODO: warn if we have background jobs: "There are stopped jobs"
// On second consecutive 'exit', exit anyway.
-
if (argv[1] == NULL)
hush_exit(last_return_code);
/* mimic bash: exit 123abc == exit 255 + error msg */
return EXIT_SUCCESS;
}
-//static int builtin_not_written(char **argv)
-//{
-// printf("builtin_%s not written\n", argv[0]);
-// return EXIT_FAILURE;
-//}
-
/*
* o_string support
*/
bb_error_msg_and_die("nested lists are not supported on NOMMU");
#else
int rcode;
-
-#if ENABLE_HUSH_INTERACTIVE
-// run_list_level now takes care of it?
-// debug_printf_exec("pseudo_exec: setting interactive_fd=0\n");
-// interactive_fd = 0; /* crucial!!!! */
-#endif
debug_printf_exec("pseudo_exec: run_list\n");
rcode = run_list(child->group);
/* OK to leak memory by not calling free_pipe_list,
#endif /* JOB */
for (; pi; pi = flag_restore ? rpipe : pi->next) {
-//why? int save_num_progs;
rword = pi->res_word;
#if ENABLE_HUSH_LOOPS
if (rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR) {
if (!pi->next->progs->argv)
continue;
/* create list of variable values */
+ debug_print_strings("for_list made from", pi->next->progs->argv);
for_list = expand_strvec_to_strvec(pi->next->progs->argv);
+ debug_print_strings("for_list", for_list);
for_lcur = for_list;
for_varname = pi->progs->argv[0];
pi->progs->argv[0] = NULL;
#endif
if (pi->num_progs == 0)
continue;
-//why? save_num_progs = pi->num_progs;
debug_printf_exec(": run_pipe with %d members\n", pi->num_progs);
rcode = run_pipe(pi);
if (rcode != -1) {
}
debug_printf_exec(": setting last_return_code=%d\n", rcode);
last_return_code = rcode;
-//why? pi->num_progs = save_num_progs;
#if ENABLE_HUSH_IF
if (rword == RES_IF || rword == RES_ELIF)
next_if_code = rcode; /* can be overwritten a number of times */
return rcode;
}
-/* Whoever decided to muck with glob internal data is AN IDIOT! */
-/* uclibc happily changed the way it works (and it has rights to do so!),
- all hell broke loose (SEGVs) */
-
-/* The API for glob is arguably broken. This routine pushes a non-matching
- * 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.
+/* Remove non-backslashed backslashes and add to "strings" vector.
* XXX broken if the last character is '\\', check that before calling.
*/
-static char **globhack(const char *src, char **strings)
+static char **add_unq_string_to_strings(char **strings, const char *src)
{
int cnt;
const char *s;
return 0;
}
-static int xglob(o_string *dest, char ***pglob)
+static int xglob(char ***pglob, const char *pattern)
{
- /* short-circuit for null word */
- /* we can code this better when the debug_printf's are gone */
- if (dest->length == 0) {
- if (dest->nonnull) {
- /* bash man page calls this an "explicit" null */
- *pglob = globhack(dest->data, *pglob);
- }
- return 0;
- }
-
- if (glob_needed(dest->data)) {
+ if (glob_needed(pattern)) {
glob_t globdata;
int gr;
memset(&globdata, 0, sizeof(globdata));
- gr = glob(dest->data, 0, NULL, &globdata);
+ gr = glob(pattern, 0, NULL, &globdata);
debug_printf("glob returned %d\n", gr);
if (gr == GLOB_NOSPACE)
bb_error_msg_and_die("out of memory during glob");
if (gr == GLOB_NOMATCH) {
- debug_printf("globhack returned %d\n", gr);
- /* quote removal, or more accurately, backslash removal */
- *pglob = globhack(dest->data, *pglob);
globfree(&globdata);
- return 0;
+ goto literal;
}
if (gr != 0) { /* GLOB_ABORTED ? */
bb_error_msg("glob(3) error %d", gr);
return gr;
}
- *pglob = globhack(dest->data, *pglob);
+ literal:
+ /* quote removal, or more accurately, backslash removal */
+ *pglob = add_unq_string_to_strings(*pglob, pattern);
+ debug_print_strings("after xglob", *pglob);
return 0;
}
n = expand_vars_to_list(&output, n, *v++, or_mask);
o_debug_list("expand_variables", &output, n);
- /* output.data (malloced) gets returned in "list" */
+ /* output.data (malloced in one block) gets returned in "list" */
list = o_finalize_list(&output, n);
#ifdef DEBUG_EXPAND
static char **expand_strvec_to_strvec(char **argv)
{
- return expand_variables(argv, 0);
+ char **exp;
+ char **res = NULL;
+
+ debug_print_strings("expand_strvec_to_strvec: pre expand", argv);
+ exp = argv = expand_variables(argv, 0);
+ debug_print_strings("expand_strvec_to_strvec: post expand", argv);
+ while (*argv) {
+ int r = xglob(&res, *argv);
+ if (r)
+ bb_error_msg("xglob returned %d on '%s'", r, *argv);
+//TODO: testcase for bad glob pattern behavior
+ argv++;
+ }
+ free(exp);
+ debug_print_strings("expand_strvec_to_strvec: res", res);
+ return res;
}
+/* used for expansion of right hand of assignments */
+/* NB: should NOT do globbing! "export v=/bin/c*; env | grep ^v=" outputs
+ * "v=/bin/c*" */
static char *expand_string_to_string(const char *str)
{
char *argv[2], **list;
return (char*)list;
}
+/* used for eval */
static char* expand_strvec_to_string(char **argv)
{
char **list;
static void initialize_context(struct p_context *ctx)
{
- smallint sv = ctx->parse_type;
memset(ctx, 0, sizeof(*ctx));
- ctx->parse_type = sv;
ctx->pipe = ctx->list_head = new_pipe();
/* Create the memory for child, roughly:
* ctx->pipe->progs = new struct child_prog;
{
struct child_prog *child = ctx->child;
char ***glob_target;
- int gr;
debug_printf_parse("done_word entered: '%s' %p\n", word->data, child);
if (word->length == 0 && !word->nonnull) {
debug_printf_parse("done_word return 1: syntax error, groups and arglists don't mix\n");
return 1;
}
- if (!child->argv && (ctx->parse_type & PARSEFLAG_SEMICOLON)) {
+ if (!child->argv) {
debug_printf_parse(": checking '%s' for reserved-ness\n", word->data);
if (reserved_word(word, ctx)) {
o_reset(word);
}
glob_target = &child->argv;
}
-//BUG! globbing should be done after variable expansion!
-//See glob_and_vars testcase
- gr = xglob(word, glob_target);
- if (gr != 0) {
- debug_printf_parse("done_word return 1: xglob returned %d\n", gr);
- return 1;
+
+ if (word->length || word->nonnull) {
+ *glob_target = add_string_to_strings(*glob_target, xstrdup(word->data));
+ debug_print_strings("glob_target appended", *glob_target);
}
o_reset(word);
o_addQchr(dest, '\n');
eol_cnt--;
}
+ /* Even unquoted `echo '\'` results in two backslashes
+ * (which are converted into one by globbing later) */
+ if (!dest->o_quote && ch == '\\') {
+ o_addchr(dest, ch);
+ }
o_addQchr(dest, ch);
}
child->subshell = 1;
}
rcode = parse_stream(dest, &sub, input, endch);
-//vda: err chk?
- done_word(dest, &sub); /* finish off the final word in the subcontext */
- done_pipe(&sub, PIPE_SEQ); /* and the final command there, too */
- child->group = sub.list_head;
-
+ if (rcode == 0) {
+ done_word(dest, &sub); /* finish off the final word in the subcontext */
+ done_pipe(&sub, PIPE_SEQ); /* and the final command there, too */
+ child->group = sub.list_head;
+ }
debug_printf_parse("parse_group return %d\n", rcode);
return rcode;
/* child remains "open", available for possible redirects */
break;
if (ch == '\'')
break;
- o_addqchr(dest, ch);
+ o_addchr(dest, ch);
}
}
/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
if (ch == '"')
break;
if (ch == '\\') { /* \x. Copy both chars. */
- o_addqchr(dest, ch);
+ o_addchr(dest, ch);
ch = i_getch(input);
}
if (ch == EOF)
break;
- o_addqchr(dest, ch);
+ o_addchr(dest, ch);
if (ch == '`') {
add_till_backquote(dest, input);
- o_addqchr(dest, ch);
+ o_addchr(dest, ch);
continue;
}
-// if (ch == '$') ...
+ //if (ch == '$') ...
}
}
/* Process `cmd` - copy contents until "`" is seen. Complicated by
if (ch == '\\') { /* \x. Copy both chars unless it is \` */
int ch2 = i_getch(input);
if (ch2 != '`' && ch2 != '$' && ch2 != '\\')
- o_addqchr(dest, ch);
+ o_addchr(dest, ch);
ch = ch2;
}
if (ch == EOF)
break;
- o_addqchr(dest, ch);
+ o_addchr(dest, ch);
}
}
/* Process $(cmd) - copy contents until ")" is seen. Complicated by
if (ch == ')')
if (--count < 0)
break;
- o_addqchr(dest, ch);
+ o_addchr(dest, ch);
if (ch == '\'') {
add_till_single_quote(dest, input);
- o_addqchr(dest, ch);
+ o_addchr(dest, ch);
continue;
}
if (ch == '"') {
add_till_double_quote(dest, input);
- o_addqchr(dest, ch);
+ o_addchr(dest, ch);
continue;
}
if (ch == '\\') { /* \x. Copy verbatim. Important for \(, \) */
ch = i_getch(input);
if (ch == EOF)
break;
- o_addqchr(dest, ch);
+ o_addchr(dest, ch);
continue;
}
}
done_pipe(ctx, PIPE_SEQ);
}
}
- if ((end_trigger && strchr(end_trigger, ch))
- && !dest->o_quote && ctx->res_w == RES_NONE
- ) {
- debug_printf_parse("parse_stream return 0: end_trigger char found\n");
- return 0;
+ if (end_trigger) {
+ if (!dest->o_quote && strchr(end_trigger, ch)) {
+ /* Special case: (...word) makes last word terminate,
+ * as if ';' is seen */
+ if (ch == ')') {
+ done_word(dest, ctx);
+ done_pipe(ctx, PIPE_SEQ);
+ }
+ if (ctx->res_w == RES_NONE) {
+ debug_printf_parse("parse_stream return 0: end_trigger char found\n");
+ return 0;
+ }
+ }
}
if (m == CHAR_IFS)
continue;
dest->nonnull = 1;
while (1) {
ch = i_getch(input);
- if (ch == EOF || ch == '\'')
+ if (ch == EOF) {
+ syntax("unterminated '");
+ debug_printf_parse("parse_stream return 1: unterminated '\n");
+ return 1;
+ }
+ if (ch == '\'')
break;
o_addqchr(dest, ch);
}
- if (ch == EOF) {
- syntax("unterminated '");
- debug_printf_parse("parse_stream return 1: unterminated '\n");
- return 1;
- }
break;
case '"':
dest->nonnull = 1;
if (ENABLE_HUSH_DEBUG)
bb_error_msg_and_die("BUG: unexpected %c\n", ch);
}
- }
+ } /* while (1) */
/* Complain if quote? No, maybe we just finished a command substitution
* that was quoted. Example:
* $ echo "`cat foo` plus more"
* from builtin_source() and builtin_eval() */
static int parse_and_run_stream(struct in_str *inp, int parse_flag)
{
-//TODO: PARSEFLAG_SEMICOLON bit is always set in parse_flag. fishy
-//TODO: PARSEFLAG_REPARSING bit is never set (grep for it). wow
struct p_context ctx;
o_string temp = NULL_O_STRING;
int rcode;
+
do {
-// It always has PARSEFLAG_SEMICOLON, can we remove all checks for this bit?
-// After that, the whole parse_type fiels is not needed.
- ctx.parse_type = parse_flag;
initialize_context(&ctx);
update_charmap();
- if (!(parse_flag & PARSEFLAG_SEMICOLON) || (parse_flag & PARSEFLAG_REPARSING))
- set_in_charmap(";$&|", CHAR_ORDINARY);
#if ENABLE_HUSH_INTERACTIVE
inp->promptmode = 0; /* PS1 */
#endif
debug_printf_exec("parse_stream_outer: run_and_free_list\n");
run_and_free_list(ctx.list_head);
} else {
+ /* We arrive here also if rcode == 1 (error in parse_stream) */
if (ctx.old_flag != 0) {
free(ctx.stack);
o_reset(&temp);
}
temp.nonnull = 0;
temp.o_quote = 0;
- inp->p = NULL;
free_pipe_list(ctx.list_head, /* indent: */ 0);
+ /* Discard all unprocessed line input, force prompt on */
+ inp->p = NULL;
+ inp->promptme = 1;
}
o_free(&temp);
- } while (rcode != -1 && !(parse_flag & PARSEFLAG_EXIT_FROM_LOOP)); /* loop on syntax errors, return on EOF */
+ /* loop on syntax errors, return on EOF: */
+ } while (rcode != -1 && !(parse_flag & PARSEFLAG_EXIT_FROM_LOOP));
return 0;
}
static int parse_and_run_string(const char *s, int parse_flag)
{
-//TODO: PARSEFLAG_SEMICOLON bit is always set in parse_flag. fishy
struct in_str input;
setup_string_in_str(&input, s);
return parse_and_run_stream(&input, parse_flag);
int rcode;
struct in_str input;
setup_file_in_str(&input, f);
- rcode = parse_and_run_stream(&input, PARSEFLAG_SEMICOLON);
+ rcode = parse_and_run_stream(&input, 0 /* parse_flag */);
return rcode;
}
case 'c':
global_argv = argv + optind;
global_argc = argc - optind;
- opt = parse_and_run_string(optarg, PARSEFLAG_SEMICOLON);
+ opt = parse_and_run_string(optarg, 0 /* parse_flag */);
goto final_return;
case 'i':
/* Well, we cannot just declare interactiveness,