hush: fix job control with eval /bin/external_prog
hush: fix parsing of unterminated "str with no EOL hush: improved make_string() (smaller, faster, needs less RAM) hush: renamed several functions
This commit is contained in:
parent
1a7358612f
commit
170435c575
185
shell/hush.c
185
shell/hush.c
@ -117,6 +117,9 @@ extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */
|
|||||||
#define DEBUG_EXPAND 1
|
#define DEBUG_EXPAND 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Keep unconditionally on for now */
|
||||||
|
#define ENABLE_HUSH_DEBUG 1
|
||||||
|
|
||||||
#ifndef debug_printf_clean
|
#ifndef debug_printf_clean
|
||||||
/* broken, of course, but OK for testing */
|
/* broken, of course, but OK for testing */
|
||||||
static const char *indenter(int i)
|
static const char *indenter(int i)
|
||||||
@ -497,13 +500,12 @@ static int process_command_subs(o_string *dest, struct p_context *ctx, struct in
|
|||||||
#endif
|
#endif
|
||||||
static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch);
|
static int parse_group(o_string *dest, struct p_context *ctx, struct in_str *input, int ch);
|
||||||
static const char *lookup_param(const char *src);
|
static const char *lookup_param(const char *src);
|
||||||
static char *make_string(char **inp);
|
|
||||||
static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input);
|
static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input);
|
||||||
static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, const char *end_trigger);
|
static int parse_stream(o_string *dest, struct p_context *ctx, struct in_str *input0, const char *end_trigger);
|
||||||
/* setup: */
|
/* setup: */
|
||||||
static int parse_stream_outer(struct in_str *inp, int parse_flag);
|
static int parse_and_run_stream(struct in_str *inp, int parse_flag);
|
||||||
static int parse_string_outer(const char *s, int parse_flag);
|
static int parse_and_run_string(const char *s, int parse_flag);
|
||||||
static int parse_file_outer(FILE *f);
|
static int parse_and_run_file(FILE *f);
|
||||||
/* job management: */
|
/* job management: */
|
||||||
static int checkjobs(struct pipe* fg_pipe);
|
static int checkjobs(struct pipe* fg_pipe);
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
@ -515,9 +517,11 @@ static void delete_finished_bg_job(struct pipe *pi);
|
|||||||
int checkjobs_and_fg_shell(struct pipe* fg_pipe); /* never called */
|
int checkjobs_and_fg_shell(struct pipe* fg_pipe); /* never called */
|
||||||
#endif
|
#endif
|
||||||
/* local variable support */
|
/* local variable support */
|
||||||
static char **expand_variables_to_list(char **argv);
|
static char **expand_strvec_to_strvec(char **argv);
|
||||||
|
/* used for eval */
|
||||||
|
static char *expand_strvec_to_string(char **argv);
|
||||||
/* used for expansion of right hand of assignments */
|
/* used for expansion of right hand of assignments */
|
||||||
static char *expand_variables_to_string(const char *str);
|
static char *expand_string_to_string(const char *str);
|
||||||
static const char *get_local_var(const char *var);
|
static const char *get_local_var(const char *var);
|
||||||
static int set_local_var(const char *s, int flg_export);
|
static int set_local_var(const char *s, int flg_export);
|
||||||
static void unset_local_var(const char *name);
|
static void unset_local_var(const char *name);
|
||||||
@ -716,12 +720,11 @@ static const char *set_cwd(void)
|
|||||||
/* built-in 'eval' handler */
|
/* built-in 'eval' handler */
|
||||||
static int builtin_eval(char **argv)
|
static int builtin_eval(char **argv)
|
||||||
{
|
{
|
||||||
char *str = NULL;
|
|
||||||
int rcode = EXIT_SUCCESS;
|
int rcode = EXIT_SUCCESS;
|
||||||
|
|
||||||
if (argv[1]) {
|
if (argv[1]) {
|
||||||
str = make_string(argv + 1);
|
char *str = expand_strvec_to_string(argv + 1);
|
||||||
parse_string_outer(str, PARSEFLAG_EXIT_FROM_LOOP |
|
parse_and_run_string(str, PARSEFLAG_EXIT_FROM_LOOP |
|
||||||
PARSEFLAG_SEMICOLON);
|
PARSEFLAG_SEMICOLON);
|
||||||
free(str);
|
free(str);
|
||||||
rcode = last_return_code;
|
rcode = last_return_code;
|
||||||
@ -732,9 +735,9 @@ static int builtin_eval(char **argv)
|
|||||||
/* built-in 'cd <path>' handler */
|
/* built-in 'cd <path>' handler */
|
||||||
static int builtin_cd(char **argv)
|
static int builtin_cd(char **argv)
|
||||||
{
|
{
|
||||||
char *newdir;
|
const char *newdir;
|
||||||
if (argv[1] == NULL)
|
if (argv[1] == NULL)
|
||||||
newdir = getenv("HOME");
|
newdir = getenv("HOME") ? : "/";
|
||||||
else
|
else
|
||||||
newdir = argv[1];
|
newdir = argv[1];
|
||||||
if (chdir(newdir)) {
|
if (chdir(newdir)) {
|
||||||
@ -999,7 +1002,7 @@ static int builtin_source(char **argv)
|
|||||||
* (pointer only is OK!) on this stack frame,
|
* (pointer only is OK!) on this stack frame,
|
||||||
* set global_argv=argv+1, recurse, and restore. */
|
* set global_argv=argv+1, recurse, and restore. */
|
||||||
mark_open(fileno(input));
|
mark_open(fileno(input));
|
||||||
status = parse_file_outer(input);
|
status = parse_and_run_file(input);
|
||||||
mark_closed(fileno(input));
|
mark_closed(fileno(input));
|
||||||
fclose(input);
|
fclose(input);
|
||||||
return status;
|
return status;
|
||||||
@ -1146,12 +1149,10 @@ static void get_user_input(struct in_str *i)
|
|||||||
|
|
||||||
prompt_str = setup_prompt_string(i->promptmode);
|
prompt_str = setup_prompt_string(i->promptmode);
|
||||||
#if ENABLE_FEATURE_EDITING
|
#if ENABLE_FEATURE_EDITING
|
||||||
/*
|
/* Enable command line editing only while a command line
|
||||||
** enable command line editing only while a command line
|
* is actually being read; otherwise, we'll end up bequeathing
|
||||||
** is actually being read; otherwise, we'll end up bequeathing
|
* atexit() handlers and other unwanted stuff to our
|
||||||
** atexit() handlers and other unwanted stuff to our
|
* child processes (rob@sysgo.de) */
|
||||||
** child processes (rob@sysgo.de)
|
|
||||||
*/
|
|
||||||
r = read_line_input(prompt_str, user_input_buf, BUFSIZ-1, line_input_state);
|
r = read_line_input(prompt_str, user_input_buf, BUFSIZ-1, line_input_state);
|
||||||
i->eof_flag = (r < 0);
|
i->eof_flag = (r < 0);
|
||||||
if (i->eof_flag) { /* EOF/error detected */
|
if (i->eof_flag) { /* EOF/error detected */
|
||||||
@ -1343,8 +1344,8 @@ static void pseudo_exec_argv(char **argv)
|
|||||||
debug_printf_exec("pid %d environment modification: %s\n",
|
debug_printf_exec("pid %d environment modification: %s\n",
|
||||||
getpid(), argv[i]);
|
getpid(), argv[i]);
|
||||||
// FIXME: vfork case??
|
// FIXME: vfork case??
|
||||||
p = expand_variables_to_string(argv[i]);
|
p = expand_string_to_string(argv[i]);
|
||||||
putenv(p == argv[i] ? xstrdup(p) : p);
|
putenv(p);
|
||||||
}
|
}
|
||||||
argv += i;
|
argv += i;
|
||||||
/* If a variable is assigned in a forest, and nobody listens,
|
/* If a variable is assigned in a forest, and nobody listens,
|
||||||
@ -1354,7 +1355,7 @@ static void pseudo_exec_argv(char **argv)
|
|||||||
_exit(EXIT_SUCCESS);
|
_exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
argv = expand_variables_to_list(argv);
|
argv = expand_strvec_to_strvec(argv);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the command matches any of the builtins.
|
* Check if the command matches any of the builtins.
|
||||||
@ -1652,8 +1653,8 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
|
|||||||
pid_t p;
|
pid_t p;
|
||||||
int rcode = checkjobs(fg_pipe);
|
int rcode = checkjobs(fg_pipe);
|
||||||
/* Job finished, move the shell to the foreground */
|
/* Job finished, move the shell to the foreground */
|
||||||
p = getpgid(0);
|
p = getpgid(0); /* pgid of our process */
|
||||||
debug_printf("fg'ing ourself: getpgid(0)=%d\n", (int)p);
|
debug_printf_jobs("fg'ing ourself: getpgid(0)=%d\n", (int)p);
|
||||||
if (tcsetpgrp(interactive_fd, p) && errno != ENOTTY)
|
if (tcsetpgrp(interactive_fd, p) && errno != ENOTTY)
|
||||||
bb_perror_msg("tcsetpgrp-4a");
|
bb_perror_msg("tcsetpgrp-4a");
|
||||||
return rcode;
|
return rcode;
|
||||||
@ -1675,6 +1676,9 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe)
|
|||||||
* subshell, when that is in fact necessary. The subshell process
|
* subshell, when that is in fact necessary. The subshell process
|
||||||
* now has its stdout directed to the input of the appropriate pipe,
|
* now has its stdout directed to the input of the appropriate pipe,
|
||||||
* so this routine is noticeably simpler.
|
* so this routine is noticeably simpler.
|
||||||
|
*
|
||||||
|
* Returns -1 only if started some children. IOW: we have to
|
||||||
|
* mask out retvals of builtins etc with 0xff!
|
||||||
*/
|
*/
|
||||||
static int run_pipe_real(struct pipe *pi)
|
static int run_pipe_real(struct pipe *pi)
|
||||||
{
|
{
|
||||||
@ -1710,7 +1714,7 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
rcode = run_list_real(child->group);
|
rcode = run_list_real(child->group);
|
||||||
restore_redirects(squirrel);
|
restore_redirects(squirrel);
|
||||||
debug_printf_exec("run_pipe_real return %d\n", rcode);
|
debug_printf_exec("run_pipe_real return %d\n", rcode);
|
||||||
return rcode;
|
return rcode; // do we need to add '... & 0xff' ?
|
||||||
}
|
}
|
||||||
|
|
||||||
if (single_fg && child->argv != NULL) {
|
if (single_fg && child->argv != NULL) {
|
||||||
@ -1739,21 +1743,16 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
export_me = 1;
|
export_me = 1;
|
||||||
}
|
}
|
||||||
free(name);
|
free(name);
|
||||||
p = expand_variables_to_string(argv[i]);
|
p = expand_string_to_string(argv[i]);
|
||||||
set_local_var(p, export_me);
|
set_local_var(p, export_me);
|
||||||
if (p != argv[i])
|
free(p);
|
||||||
free(p);
|
|
||||||
}
|
}
|
||||||
return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */
|
return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */
|
||||||
}
|
}
|
||||||
for (i = 0; is_assignment(argv[i]); i++) {
|
for (i = 0; is_assignment(argv[i]); i++) {
|
||||||
p = expand_variables_to_string(argv[i]);
|
p = expand_string_to_string(argv[i]);
|
||||||
if (p != argv[i]) {
|
//sp: child->sp--;
|
||||||
//sp: child->sp--;
|
putenv(p);
|
||||||
putenv(p);
|
|
||||||
} else {
|
|
||||||
putenv(xstrdup(p));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (x = bltins; x->cmd; x++) {
|
for (x = bltins; x->cmd; x++) {
|
||||||
if (strcmp(argv[i], x->cmd) == 0) {
|
if (strcmp(argv[i], x->cmd) == 0) {
|
||||||
@ -1770,8 +1769,8 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
setup_redirects(child, squirrel);
|
setup_redirects(child, squirrel);
|
||||||
debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv[i+1]);
|
debug_printf_exec(": builtin '%s' '%s'...\n", x->cmd, argv[i+1]);
|
||||||
//sp: if (child->sp) /* btw we can do it unconditionally... */
|
//sp: if (child->sp) /* btw we can do it unconditionally... */
|
||||||
argv_expanded = expand_variables_to_list(argv + i);
|
argv_expanded = expand_strvec_to_strvec(argv + i);
|
||||||
rcode = x->function(argv_expanded);
|
rcode = x->function(argv_expanded) & 0xff;
|
||||||
free(argv_expanded);
|
free(argv_expanded);
|
||||||
restore_redirects(squirrel);
|
restore_redirects(squirrel);
|
||||||
debug_printf_exec("run_pipe_real return %d\n", rcode);
|
debug_printf_exec("run_pipe_real return %d\n", rcode);
|
||||||
@ -1786,9 +1785,9 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
save_nofork_data(&nofork_save);
|
save_nofork_data(&nofork_save);
|
||||||
argv_expanded = argv + i;
|
argv_expanded = argv + i;
|
||||||
//sp: if (child->sp)
|
//sp: if (child->sp)
|
||||||
argv_expanded = expand_variables_to_list(argv + i);
|
argv_expanded = expand_strvec_to_strvec(argv + i);
|
||||||
debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]);
|
debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", argv_expanded[0], argv_expanded[1]);
|
||||||
rcode = run_nofork_applet_prime(&nofork_save, a, argv_expanded);
|
rcode = run_nofork_applet_prime(&nofork_save, a, argv_expanded) & 0xff;
|
||||||
free(argv_expanded);
|
free(argv_expanded);
|
||||||
restore_redirects(squirrel);
|
restore_redirects(squirrel);
|
||||||
debug_printf_exec("run_pipe_real return %d\n", rcode);
|
debug_printf_exec("run_pipe_real return %d\n", rcode);
|
||||||
@ -1832,7 +1831,7 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
/* Every child adds itself to new process group
|
/* Every child adds itself to new process group
|
||||||
* with pgid == pid of first child in pipe */
|
* with pgid == pid of first child in pipe */
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
if (interactive_fd) {
|
if (run_list_level == 1 && interactive_fd) {
|
||||||
/* Don't do pgrp restore anymore on fatal signals */
|
/* Don't do pgrp restore anymore on fatal signals */
|
||||||
set_fatal_sighandler(SIG_DFL);
|
set_fatal_sighandler(SIG_DFL);
|
||||||
if (pi->pgrp < 0) /* true for 1st process only */
|
if (pi->pgrp < 0) /* true for 1st process only */
|
||||||
@ -2078,7 +2077,7 @@ static int run_list_real(struct pipe *pi)
|
|||||||
if (!pi->next->progs->argv)
|
if (!pi->next->progs->argv)
|
||||||
continue;
|
continue;
|
||||||
/* create list of variable values */
|
/* create list of variable values */
|
||||||
for_list = expand_variables_to_list(pi->next->progs->argv);
|
for_list = expand_strvec_to_strvec(pi->next->progs->argv);
|
||||||
for_lcur = for_list;
|
for_lcur = for_list;
|
||||||
for_varname = pi->progs->argv[0];
|
for_varname = pi->progs->argv[0];
|
||||||
pi->progs->argv[0] = NULL;
|
pi->progs->argv[0] = NULL;
|
||||||
@ -2122,24 +2121,24 @@ static int run_list_real(struct pipe *pi)
|
|||||||
* of run_pipe_real(), and we don't need to wait for anything. */
|
* of run_pipe_real(), and we don't need to wait for anything. */
|
||||||
} else if (pi->followup == PIPE_BG) {
|
} else if (pi->followup == PIPE_BG) {
|
||||||
/* What does bash do with attempts to background builtins? */
|
/* What does bash do with attempts to background builtins? */
|
||||||
|
|
||||||
/* Even bash 3.2 doesn't do that well with nested bg:
|
/* Even bash 3.2 doesn't do that well with nested bg:
|
||||||
* try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
|
* try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
|
||||||
* I'm considering NOT treating inner bgs as jobs -
|
* I'm NOT treating inner &'s as jobs */
|
||||||
* thus maybe "if (run_list_level == 1 && pi->followup == PIPE_BG)"
|
|
||||||
* above? */
|
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
insert_bg_job(pi);
|
if (run_list_level == 1)
|
||||||
|
insert_bg_job(pi);
|
||||||
#endif
|
#endif
|
||||||
rcode = EXIT_SUCCESS;
|
rcode = EXIT_SUCCESS;
|
||||||
} else {
|
} else {
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
/* Paranoia, just "interactive_fd" should be enough */
|
/* Paranoia, just "interactive_fd" should be enough? */
|
||||||
if (run_list_level == 1 && interactive_fd) {
|
if (run_list_level == 1 && interactive_fd) {
|
||||||
|
/* waits for completion, then fg's main shell */
|
||||||
rcode = checkjobs_and_fg_shell(pi);
|
rcode = checkjobs_and_fg_shell(pi);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
/* this one just waits for completion */
|
||||||
rcode = checkjobs(pi);
|
rcode = checkjobs(pi);
|
||||||
}
|
}
|
||||||
debug_printf_exec(": checkjobs returned %d\n", rcode);
|
debug_printf_exec(": checkjobs returned %d\n", rcode);
|
||||||
@ -2343,7 +2342,7 @@ static int xglob(o_string *dest, int flags, glob_t *pglob)
|
|||||||
return gr;
|
return gr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* expand_variables_to_list() takes a list of strings, expands
|
/* expand_strvec_to_strvec() takes a list of strings, expands
|
||||||
* all variable references within and returns a pointer to
|
* all variable references within and returns a pointer to
|
||||||
* a list of expanded strings, possibly with larger number
|
* a list of expanded strings, possibly with larger number
|
||||||
* of strings. (Think VAR="a b"; echo $VAR).
|
* of strings. (Think VAR="a b"; echo $VAR).
|
||||||
@ -2629,29 +2628,51 @@ static char **expand_variables(char **argv, char or_mask)
|
|||||||
debug_printf_expand("used_space=%d\n", pos - (char*)list);
|
debug_printf_expand("used_space=%d\n", pos - (char*)list);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* To be removed / made conditional later. */
|
if (ENABLE_HUSH_DEBUG)
|
||||||
if (pos - (char*)list > len)
|
if (pos - (char*)list > len)
|
||||||
bb_error_msg_and_die("BUG in varexp");
|
bb_error_msg_and_die("BUG in varexp");
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char **expand_variables_to_list(char **argv)
|
static char **expand_strvec_to_strvec(char **argv)
|
||||||
{
|
{
|
||||||
return expand_variables(argv, 0);
|
return expand_variables(argv, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *expand_variables_to_string(const char *str)
|
static char *expand_string_to_string(const char *str)
|
||||||
{
|
{
|
||||||
char *argv[2], **list;
|
char *argv[2], **list;
|
||||||
|
|
||||||
argv[0] = (char*)str;
|
argv[0] = (char*)str;
|
||||||
argv[1] = NULL;
|
argv[1] = NULL;
|
||||||
list = expand_variables(argv, 0x80); /* 0x80: make one-element expansion */
|
list = expand_variables(argv, 0x80); /* 0x80: make one-element expansion */
|
||||||
/* To be removed / made conditional later. */
|
if (ENABLE_HUSH_DEBUG)
|
||||||
if (!list[0] || list[1])
|
if (!list[0] || list[1])
|
||||||
bb_error_msg_and_die("BUG in varexp");
|
bb_error_msg_and_die("BUG in varexp2");
|
||||||
/* actually, just move string 2*sizeof(char*) bytes back */
|
/* actually, just move string 2*sizeof(char*) bytes back */
|
||||||
strcpy((char*)list, list[0]);
|
strcpy((char*)list, list[0]);
|
||||||
|
debug_printf_expand("string_to_string='%s'\n", (char*)list);
|
||||||
|
return (char*)list;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* expand_strvec_to_string(char **argv)
|
||||||
|
{
|
||||||
|
char **list;
|
||||||
|
|
||||||
|
list = expand_variables(argv, 0x80);
|
||||||
|
/* Convert all NULs to spaces */
|
||||||
|
if (list[0]) {
|
||||||
|
int n = 1;
|
||||||
|
while (list[n]) {
|
||||||
|
if (ENABLE_HUSH_DEBUG)
|
||||||
|
if (list[n-1] + strlen(list[n-1]) + 1 != list[n])
|
||||||
|
bb_error_msg_and_die("BUG in varexp3");
|
||||||
|
list[n][-1] = ' '; /* TODO: or to ifs[0]? */
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strcpy((char*)list, list[0]);
|
||||||
|
debug_printf_expand("strvec_to_string='%s'\n", (char*)list);
|
||||||
return (char*)list;
|
return (char*)list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2713,9 +2734,9 @@ static int set_local_var(const char *s, int flg_export)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cur = xzalloc(sizeof(*cur));
|
cur = xzalloc(sizeof(*cur));
|
||||||
|
/*cur->next = 0;*/
|
||||||
cur->name = xstrdup(name);
|
cur->name = xstrdup(name);
|
||||||
cur->value = xstrdup(value);
|
cur->value = xstrdup(value);
|
||||||
/*cur->next = 0;*/
|
|
||||||
cur->flg_export = flg_export;
|
cur->flg_export = flg_export;
|
||||||
/*cur->flg_read_only = 0;*/
|
/*cur->flg_read_only = 0;*/
|
||||||
{
|
{
|
||||||
@ -3242,31 +3263,6 @@ static const char *lookup_param(const char *src)
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make new string for parser */
|
|
||||||
static char* make_string(char **inp)
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
char *str = NULL;
|
|
||||||
int n;
|
|
||||||
int val_len;
|
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
for (n = 0; inp[n]; n++) {
|
|
||||||
p = expand_variables_to_string(inp[n]);
|
|
||||||
val_len = strlen(p);
|
|
||||||
str = xrealloc(str, len + val_len + 3); /* +3: space, '\n', <nul>*/
|
|
||||||
str[len++] = ' ';
|
|
||||||
strcpy(str + len, p);
|
|
||||||
len += val_len;
|
|
||||||
if (p != inp[n]) free(p);
|
|
||||||
}
|
|
||||||
/* We do not check for case where loop had no iterations at all
|
|
||||||
* - cannot happen? */
|
|
||||||
str[len] = '\n';
|
|
||||||
str[len+1] = '\0';
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* return code: 0 for OK, 1 for syntax error */
|
/* return code: 0 for OK, 1 for syntax error */
|
||||||
static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input)
|
static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input)
|
||||||
{
|
{
|
||||||
@ -3358,9 +3354,9 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
debug_printf_parse("parse_stream entered, end_trigger='%s'\n", end_trigger);
|
debug_printf_parse("parse_stream entered, end_trigger='%s'\n", end_trigger);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
ch = b_getch(input);
|
|
||||||
m = CHAR_IFS;
|
m = CHAR_IFS;
|
||||||
next = '\0';
|
next = '\0';
|
||||||
|
ch = b_getch(input);
|
||||||
if (ch != EOF) {
|
if (ch != EOF) {
|
||||||
m = charmap[ch];
|
m = charmap[ch];
|
||||||
if (ch != '\n')
|
if (ch != '\n')
|
||||||
@ -3371,6 +3367,11 @@ static int parse_stream(o_string *dest, struct p_context *ctx,
|
|||||||
if (m == CHAR_ORDINARY
|
if (m == CHAR_ORDINARY
|
||||||
|| (m != CHAR_SPECIAL && dest->quote)
|
|| (m != CHAR_SPECIAL && dest->quote)
|
||||||
) {
|
) {
|
||||||
|
if (ch == EOF) {
|
||||||
|
syntax();
|
||||||
|
debug_printf_parse("parse_stream return 1: unterminated \"\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
b_addqchr(dest, ch, dest->quote);
|
b_addqchr(dest, ch, dest->quote);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -3564,8 +3565,8 @@ static void update_charmap(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* most recursion does not come through here, the exception is
|
/* most recursion does not come through here, the exception is
|
||||||
* from builtin_source() */
|
* from builtin_source() and builtin_eval() */
|
||||||
static int parse_stream_outer(struct in_str *inp, int parse_flag)
|
static int parse_and_run_stream(struct in_str *inp, int parse_flag)
|
||||||
{
|
{
|
||||||
struct p_context ctx;
|
struct p_context ctx;
|
||||||
o_string temp = NULL_O_STRING;
|
o_string temp = NULL_O_STRING;
|
||||||
@ -3607,19 +3608,19 @@ static int parse_stream_outer(struct in_str *inp, int parse_flag)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_string_outer(const char *s, int parse_flag)
|
static int parse_and_run_string(const char *s, int parse_flag)
|
||||||
{
|
{
|
||||||
struct in_str input;
|
struct in_str input;
|
||||||
setup_string_in_str(&input, s);
|
setup_string_in_str(&input, s);
|
||||||
return parse_stream_outer(&input, parse_flag);
|
return parse_and_run_stream(&input, parse_flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_file_outer(FILE *f)
|
static int parse_and_run_file(FILE *f)
|
||||||
{
|
{
|
||||||
int rcode;
|
int rcode;
|
||||||
struct in_str input;
|
struct in_str input;
|
||||||
setup_file_in_str(&input, f);
|
setup_file_in_str(&input, f);
|
||||||
rcode = parse_stream_outer(&input, PARSEFLAG_SEMICOLON);
|
rcode = parse_and_run_stream(&input, PARSEFLAG_SEMICOLON);
|
||||||
return rcode;
|
return rcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3698,7 +3699,7 @@ int hush_main(int argc, char **argv)
|
|||||||
input = fopen("/etc/profile", "r");
|
input = fopen("/etc/profile", "r");
|
||||||
if (input != NULL) {
|
if (input != NULL) {
|
||||||
mark_open(fileno(input));
|
mark_open(fileno(input));
|
||||||
parse_file_outer(input);
|
parse_and_run_file(input);
|
||||||
mark_closed(fileno(input));
|
mark_closed(fileno(input));
|
||||||
fclose(input);
|
fclose(input);
|
||||||
}
|
}
|
||||||
@ -3710,7 +3711,7 @@ int hush_main(int argc, char **argv)
|
|||||||
case 'c':
|
case 'c':
|
||||||
global_argv = argv + optind;
|
global_argv = argv + optind;
|
||||||
global_argc = argc - optind;
|
global_argc = argc - optind;
|
||||||
opt = parse_string_outer(optarg, PARSEFLAG_SEMICOLON);
|
opt = parse_and_run_string(optarg, PARSEFLAG_SEMICOLON);
|
||||||
goto final_return;
|
goto final_return;
|
||||||
case 'i':
|
case 'i':
|
||||||
/* Well, we cannot just declare interactiveness,
|
/* Well, we cannot just declare interactiveness,
|
||||||
@ -3791,7 +3792,7 @@ int hush_main(int argc, char **argv)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (argv[optind] == NULL) {
|
if (argv[optind] == NULL) {
|
||||||
opt = parse_file_outer(stdin);
|
opt = parse_and_run_file(stdin);
|
||||||
goto final_return;
|
goto final_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3799,7 +3800,7 @@ int hush_main(int argc, char **argv)
|
|||||||
global_argv = argv + optind;
|
global_argv = argv + optind;
|
||||||
global_argc = argc - optind;
|
global_argc = argc - optind;
|
||||||
input = xfopen(argv[optind], "r");
|
input = xfopen(argv[optind], "r");
|
||||||
opt = parse_file_outer(input);
|
opt = parse_and_run_file(input);
|
||||||
|
|
||||||
#if ENABLE_FEATURE_CLEAN_UP
|
#if ENABLE_FEATURE_CLEAN_UP
|
||||||
fclose(input);
|
fclose(input);
|
||||||
|
1
shell/hush_test/hush-bugs/noeol3.right
Normal file
1
shell/hush_test/hush-bugs/noeol3.right
Normal file
@ -0,0 +1 @@
|
|||||||
|
hush: syntax error hush.c:3370
|
2
shell/hush_test/hush-bugs/noeol3.tests
Executable file
2
shell/hush_test/hush-bugs/noeol3.tests
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
# last line has no EOL!
|
||||||
|
echo "unterminated
|
Loading…
Reference in New Issue
Block a user