This patch covers one big part of variable handling.
$ a=b foo should be handled correctly. $ a=b is parsed OK, but the actual variable setting is not yet written. Except for some weird exceptions related to quoting rules, this code passes (matches ash behavior) all the tests I threw at it. If someone now writes set_local_var(), and updates lookup_param() to match, we can claim success! - Larry
This commit is contained in:
parent
be9f44a7df
commit
78a7c99f7f
54
hush.c
54
hush.c
@ -43,6 +43,7 @@
|
|||||||
* Brace Expansion
|
* Brace Expansion
|
||||||
* Tilde Expansion
|
* Tilde Expansion
|
||||||
* fancy forms of Parameter Expansion
|
* fancy forms of Parameter Expansion
|
||||||
|
* aliases
|
||||||
* Arithmetic Expansion
|
* Arithmetic Expansion
|
||||||
* <(list) and >(list) Process Substitution
|
* <(list) and >(list) Process Substitution
|
||||||
* reserved words: case, esac, select, function
|
* reserved words: case, esac, select, function
|
||||||
@ -54,15 +55,14 @@
|
|||||||
* to-do:
|
* to-do:
|
||||||
* 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
|
* finish implementing reserved words: for, while, until, do, done
|
||||||
|
* finish implementing local variable assignment
|
||||||
* change { and } from special chars to reserved words
|
* change { and } from special chars to reserved words
|
||||||
* builtins: break, continue, eval, return, set, trap, ulimit
|
* builtins: break, continue, eval, return, set, trap, ulimit
|
||||||
* test magic exec
|
* test magic exec
|
||||||
* handle children going into background
|
* handle children going into background
|
||||||
* clean up recognition of null pipes
|
* clean up recognition of null pipes
|
||||||
* have builtin_exec set flag to avoid restore_redirects
|
|
||||||
* check setting of global_argc and global_argv
|
* check setting of global_argc and global_argv
|
||||||
* control-C handling, probably with longjmp
|
* control-C handling, probably with longjmp
|
||||||
* VAR=value prefix for simple commands
|
|
||||||
* follow IFS rules more precisely, including update semantics
|
* follow IFS rules more precisely, including update semantics
|
||||||
* figure out what to do with backslash-newline
|
* figure out what to do with backslash-newline
|
||||||
* explain why we use signal instead of sigaction
|
* explain why we use signal instead of sigaction
|
||||||
@ -70,6 +70,7 @@
|
|||||||
* continuation lines, both explicit and implicit - done?
|
* continuation lines, both explicit and implicit - done?
|
||||||
* memory leak finding and plugging - done?
|
* memory leak finding and plugging - done?
|
||||||
* more testing, especially quoting rules and redirection
|
* more testing, especially quoting rules and redirection
|
||||||
|
* document how quoting rules not precisely followed for variable assignments
|
||||||
* maybe change map[] to use 2-bit entries
|
* maybe change map[] to use 2-bit entries
|
||||||
* (eventually) remove all the printf's
|
* (eventually) remove all the printf's
|
||||||
*
|
*
|
||||||
@ -359,6 +360,9 @@ static int run_pipe_real(struct pipe *pi);
|
|||||||
static int globhack(const char *src, int flags, glob_t *pglob);
|
static int globhack(const char *src, int flags, glob_t *pglob);
|
||||||
static int glob_needed(const char *s);
|
static int glob_needed(const char *s);
|
||||||
static int xglob(o_string *dest, int flags, glob_t *pglob);
|
static int xglob(o_string *dest, int flags, glob_t *pglob);
|
||||||
|
/* variable assignment: */
|
||||||
|
static int set_local_var(const char *s);
|
||||||
|
static int is_assignment(const char *s);
|
||||||
/* data structure manipulation: */
|
/* data structure manipulation: */
|
||||||
static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input);
|
static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input);
|
||||||
static void initialize_context(struct p_context *ctx);
|
static void initialize_context(struct p_context *ctx);
|
||||||
@ -1001,9 +1005,20 @@ static int pipe_wait(struct pipe *pi)
|
|||||||
/* very simple version for testing */
|
/* very simple version for testing */
|
||||||
static void pseudo_exec(struct child_prog *child)
|
static void pseudo_exec(struct child_prog *child)
|
||||||
{
|
{
|
||||||
int rcode;
|
int i, rcode;
|
||||||
struct built_in_command *x;
|
struct built_in_command *x;
|
||||||
if (child->argv) {
|
if (child->argv) {
|
||||||
|
for (i=0; is_assignment(child->argv[i]); i++) {
|
||||||
|
putenv(strdup(child->argv[i]));
|
||||||
|
}
|
||||||
|
child->argv+=i; /* XXX this hack isn't so horrible, since we are about
|
||||||
|
to exit, and therefore don't need to keep data
|
||||||
|
structures consistent for free() use. */
|
||||||
|
/* If a variable is assigned in a forest, and nobody listens,
|
||||||
|
* was it ever really set?
|
||||||
|
*/
|
||||||
|
if (child->argv[0] == NULL) exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the command matches any of the builtins.
|
* Check if the command matches any of the builtins.
|
||||||
* Depending on context, this might be redundant. But it's
|
* Depending on context, this might be redundant. But it's
|
||||||
@ -1253,11 +1268,19 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
restore_redirects(squirrel);
|
restore_redirects(squirrel);
|
||||||
return rcode;
|
return rcode;
|
||||||
}
|
}
|
||||||
|
for (i=0; is_assignment(child->argv[i]); i++) { /* nothing */ }
|
||||||
|
if (i!=0 && child->argv[i]==NULL) {
|
||||||
|
/* assignments, but no command: set the local environment */
|
||||||
|
for (i=0; child->argv[i]!=NULL; i++) {
|
||||||
|
set_local_var(child->argv[i]);
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */
|
||||||
|
}
|
||||||
for (x = bltins; x->cmd; x++) {
|
for (x = bltins; x->cmd; x++) {
|
||||||
if (strcmp(child->argv[0], x->cmd) == 0 ) {
|
if (strcmp(child->argv[i], x->cmd) == 0 ) {
|
||||||
int squirrel[] = {-1, -1, -1};
|
int squirrel[] = {-1, -1, -1};
|
||||||
int rcode;
|
int rcode;
|
||||||
if (x->function == builtin_exec && child->argv[1]==NULL) {
|
if (x->function == builtin_exec && child->argv[i+1]==NULL) {
|
||||||
debug_printf("magic exec\n");
|
debug_printf("magic exec\n");
|
||||||
setup_redirects(child,NULL);
|
setup_redirects(child,NULL);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
@ -1268,7 +1291,12 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
* Is it really safe for inline use? Experimentally,
|
* Is it really safe for inline use? Experimentally,
|
||||||
* things seem to work with glibc. */
|
* things seem to work with glibc. */
|
||||||
setup_redirects(child, squirrel);
|
setup_redirects(child, squirrel);
|
||||||
|
for (i=0; is_assignment(child->argv[i]); i++) {
|
||||||
|
putenv(strdup(child->argv[i]));
|
||||||
|
}
|
||||||
|
child->argv+=i; /* XXX horrible hack */
|
||||||
rcode = x->function(child);
|
rcode = x->function(child);
|
||||||
|
child->argv-=i; /* XXX restore hack so free() can work right */
|
||||||
restore_redirects(squirrel);
|
restore_redirects(squirrel);
|
||||||
return rcode;
|
return rcode;
|
||||||
}
|
}
|
||||||
@ -1581,6 +1609,22 @@ static int xglob(o_string *dest, int flags, glob_t *pglob)
|
|||||||
return gr;
|
return gr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int set_local_var(const char *s)
|
||||||
|
{
|
||||||
|
/* when you write this, also need to update lookup_param() */
|
||||||
|
printf("assignment %s not handled: write me!\n",s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_assignment(const char *s)
|
||||||
|
{
|
||||||
|
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
|
/* the src parameter allows us to peek forward to a possible &n syntax
|
||||||
* for file descriptor duplication, e.g., "2>&1".
|
* for file descriptor duplication, e.g., "2>&1".
|
||||||
* 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.
|
||||||
|
54
shell/hush.c
54
shell/hush.c
@ -43,6 +43,7 @@
|
|||||||
* Brace Expansion
|
* Brace Expansion
|
||||||
* Tilde Expansion
|
* Tilde Expansion
|
||||||
* fancy forms of Parameter Expansion
|
* fancy forms of Parameter Expansion
|
||||||
|
* aliases
|
||||||
* Arithmetic Expansion
|
* Arithmetic Expansion
|
||||||
* <(list) and >(list) Process Substitution
|
* <(list) and >(list) Process Substitution
|
||||||
* reserved words: case, esac, select, function
|
* reserved words: case, esac, select, function
|
||||||
@ -54,15 +55,14 @@
|
|||||||
* to-do:
|
* to-do:
|
||||||
* 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
|
* finish implementing reserved words: for, while, until, do, done
|
||||||
|
* finish implementing local variable assignment
|
||||||
* change { and } from special chars to reserved words
|
* change { and } from special chars to reserved words
|
||||||
* builtins: break, continue, eval, return, set, trap, ulimit
|
* builtins: break, continue, eval, return, set, trap, ulimit
|
||||||
* test magic exec
|
* test magic exec
|
||||||
* handle children going into background
|
* handle children going into background
|
||||||
* clean up recognition of null pipes
|
* clean up recognition of null pipes
|
||||||
* have builtin_exec set flag to avoid restore_redirects
|
|
||||||
* check setting of global_argc and global_argv
|
* check setting of global_argc and global_argv
|
||||||
* control-C handling, probably with longjmp
|
* control-C handling, probably with longjmp
|
||||||
* VAR=value prefix for simple commands
|
|
||||||
* follow IFS rules more precisely, including update semantics
|
* follow IFS rules more precisely, including update semantics
|
||||||
* figure out what to do with backslash-newline
|
* figure out what to do with backslash-newline
|
||||||
* explain why we use signal instead of sigaction
|
* explain why we use signal instead of sigaction
|
||||||
@ -70,6 +70,7 @@
|
|||||||
* continuation lines, both explicit and implicit - done?
|
* continuation lines, both explicit and implicit - done?
|
||||||
* memory leak finding and plugging - done?
|
* memory leak finding and plugging - done?
|
||||||
* more testing, especially quoting rules and redirection
|
* more testing, especially quoting rules and redirection
|
||||||
|
* document how quoting rules not precisely followed for variable assignments
|
||||||
* maybe change map[] to use 2-bit entries
|
* maybe change map[] to use 2-bit entries
|
||||||
* (eventually) remove all the printf's
|
* (eventually) remove all the printf's
|
||||||
*
|
*
|
||||||
@ -359,6 +360,9 @@ static int run_pipe_real(struct pipe *pi);
|
|||||||
static int globhack(const char *src, int flags, glob_t *pglob);
|
static int globhack(const char *src, int flags, glob_t *pglob);
|
||||||
static int glob_needed(const char *s);
|
static int glob_needed(const char *s);
|
||||||
static int xglob(o_string *dest, int flags, glob_t *pglob);
|
static int xglob(o_string *dest, int flags, glob_t *pglob);
|
||||||
|
/* variable assignment: */
|
||||||
|
static int set_local_var(const char *s);
|
||||||
|
static int is_assignment(const char *s);
|
||||||
/* data structure manipulation: */
|
/* data structure manipulation: */
|
||||||
static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input);
|
static int setup_redirect(struct p_context *ctx, int fd, redir_type style, struct in_str *input);
|
||||||
static void initialize_context(struct p_context *ctx);
|
static void initialize_context(struct p_context *ctx);
|
||||||
@ -1001,9 +1005,20 @@ static int pipe_wait(struct pipe *pi)
|
|||||||
/* very simple version for testing */
|
/* very simple version for testing */
|
||||||
static void pseudo_exec(struct child_prog *child)
|
static void pseudo_exec(struct child_prog *child)
|
||||||
{
|
{
|
||||||
int rcode;
|
int i, rcode;
|
||||||
struct built_in_command *x;
|
struct built_in_command *x;
|
||||||
if (child->argv) {
|
if (child->argv) {
|
||||||
|
for (i=0; is_assignment(child->argv[i]); i++) {
|
||||||
|
putenv(strdup(child->argv[i]));
|
||||||
|
}
|
||||||
|
child->argv+=i; /* XXX this hack isn't so horrible, since we are about
|
||||||
|
to exit, and therefore don't need to keep data
|
||||||
|
structures consistent for free() use. */
|
||||||
|
/* If a variable is assigned in a forest, and nobody listens,
|
||||||
|
* was it ever really set?
|
||||||
|
*/
|
||||||
|
if (child->argv[0] == NULL) exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the command matches any of the builtins.
|
* Check if the command matches any of the builtins.
|
||||||
* Depending on context, this might be redundant. But it's
|
* Depending on context, this might be redundant. But it's
|
||||||
@ -1253,11 +1268,19 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
restore_redirects(squirrel);
|
restore_redirects(squirrel);
|
||||||
return rcode;
|
return rcode;
|
||||||
}
|
}
|
||||||
|
for (i=0; is_assignment(child->argv[i]); i++) { /* nothing */ }
|
||||||
|
if (i!=0 && child->argv[i]==NULL) {
|
||||||
|
/* assignments, but no command: set the local environment */
|
||||||
|
for (i=0; child->argv[i]!=NULL; i++) {
|
||||||
|
set_local_var(child->argv[i]);
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */
|
||||||
|
}
|
||||||
for (x = bltins; x->cmd; x++) {
|
for (x = bltins; x->cmd; x++) {
|
||||||
if (strcmp(child->argv[0], x->cmd) == 0 ) {
|
if (strcmp(child->argv[i], x->cmd) == 0 ) {
|
||||||
int squirrel[] = {-1, -1, -1};
|
int squirrel[] = {-1, -1, -1};
|
||||||
int rcode;
|
int rcode;
|
||||||
if (x->function == builtin_exec && child->argv[1]==NULL) {
|
if (x->function == builtin_exec && child->argv[i+1]==NULL) {
|
||||||
debug_printf("magic exec\n");
|
debug_printf("magic exec\n");
|
||||||
setup_redirects(child,NULL);
|
setup_redirects(child,NULL);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
@ -1268,7 +1291,12 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
* Is it really safe for inline use? Experimentally,
|
* Is it really safe for inline use? Experimentally,
|
||||||
* things seem to work with glibc. */
|
* things seem to work with glibc. */
|
||||||
setup_redirects(child, squirrel);
|
setup_redirects(child, squirrel);
|
||||||
|
for (i=0; is_assignment(child->argv[i]); i++) {
|
||||||
|
putenv(strdup(child->argv[i]));
|
||||||
|
}
|
||||||
|
child->argv+=i; /* XXX horrible hack */
|
||||||
rcode = x->function(child);
|
rcode = x->function(child);
|
||||||
|
child->argv-=i; /* XXX restore hack so free() can work right */
|
||||||
restore_redirects(squirrel);
|
restore_redirects(squirrel);
|
||||||
return rcode;
|
return rcode;
|
||||||
}
|
}
|
||||||
@ -1581,6 +1609,22 @@ static int xglob(o_string *dest, int flags, glob_t *pglob)
|
|||||||
return gr;
|
return gr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int set_local_var(const char *s)
|
||||||
|
{
|
||||||
|
/* when you write this, also need to update lookup_param() */
|
||||||
|
printf("assignment %s not handled: write me!\n",s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_assignment(const char *s)
|
||||||
|
{
|
||||||
|
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
|
/* the src parameter allows us to peek forward to a possible &n syntax
|
||||||
* for file descriptor duplication, e.g., "2>&1".
|
* for file descriptor duplication, e.g., "2>&1".
|
||||||
* 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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user