diff options
author | Michael Brown <mcb30@ipxe.org> | 2010-11-29 14:19:59 +0000 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2010-11-29 14:19:59 +0000 |
commit | 7bebe9579ee5ea1cfcfcdf25032dbab80ccc489f (patch) | |
tree | 1d9a7812b7b79c1ba5f71af8ce01823cb89763a0 | |
parent | 01df5c510f4949f1d9d85c0067c24b1093e3f838 (diff) | |
download | ipxe-7bebe9579ee5ea1cfcfcdf25032dbab80ccc489f.tar.gz |
[cmdline] Match user expectations for &&, ||, goto, and exit
The && and || operators should be left-associative, since that is how
they are treated in most other languages (including C and Unix
shell). For example, in the command:
dhcp net0 && goto dhcp_ok || echo No DHCP on net0
if the "dhcp net0" fails then the "echo" should be executed.
After an "exit" or a successful "goto", further commands on the same
line should never be executed. For example:
goto somewhere && echo This should never be printed
exit 0 && echo This should never be printed
exit 1 && echo This should never be printed
An "exit" should cause the current shell or script to terminate and
return the specified exit status to its caller. For example:
chain test.ipxe && echo Success || echo Failure
[in test.ipxe]
#!ipxe
exit 0
should echo "Success".
Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r-- | src/core/exec.c | 106 | ||||
-rw-r--r-- | src/hci/shell.c | 3 | ||||
-rw-r--r-- | src/image/script.c | 13 | ||||
-rw-r--r-- | src/include/ipxe/command.h | 2 | ||||
-rw-r--r-- | src/include/ipxe/shell.h | 22 |
5 files changed, 94 insertions, 52 deletions
diff --git a/src/core/exec.c b/src/core/exec.c index d96b8a768..bb3b343d8 100644 --- a/src/core/exec.c +++ b/src/core/exec.c @@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/command.h> #include <ipxe/parseopt.h> #include <ipxe/settings.h> +#include <ipxe/shell.h> /** @file * @@ -38,15 +39,15 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -/** Shell exit flag */ -int shell_exit; +/** Shell stop state */ +static int stop_state; /** * Execute command * * @v command Command name * @v argv Argument list - * @ret rc Command exit status + * @ret rc Return status code * * Execute the named command. Unlike a traditional POSIX execv(), * this function returns the exit status of the command. @@ -196,32 +197,22 @@ static int split_command ( char *command, char **tokens ) { } /** - * Terminate command unconditionally - * - * @v rc Status of previous command - * @ret terminate Terminate command - */ -static int terminate_always ( int rc __unused ) { - return 1; -} - -/** - * Terminate command only if previous command succeeded + * Process next command only if previous command succeeded * * @v rc Status of previous command - * @ret terminate Terminate command + * @ret process Process next command */ -static int terminate_on_success ( int rc ) { +static int process_on_success ( int rc ) { return ( rc == 0 ); } /** - * Terminate command only if previous command failed + * Process next command only if previous command failed * * @v rc Status of previous command - * @ret terminate Terminate command + * @ret process Process next command */ -static int terminate_on_failure ( int rc ) { +static int process_on_failure ( int rc ) { return ( rc != 0 ); } @@ -229,54 +220,79 @@ static int terminate_on_failure ( int rc ) { * Find command terminator * * @v tokens Token list - * @ret terminator Terminator type + * @ret process_next "Should next command be processed?" function * @ret argc Argument count */ static int command_terminator ( char **tokens, - int ( **terminator ) ( int rc ) ) { + int ( **process_next ) ( int rc ) ) { unsigned int i; /* Find first terminating token */ for ( i = 0 ; tokens[i] ; i++ ) { if ( tokens[i][0] == '#' ) { /* Start of a comment */ - *terminator = terminate_always; - return i; + break; } else if ( strcmp ( tokens[i], "||" ) == 0 ) { /* Short-circuit logical OR */ - *terminator = terminate_on_success; + *process_next = process_on_failure; return i; } else if ( strcmp ( tokens[i], "&&" ) == 0 ) { /* Short-circuit logical AND */ - *terminator = terminate_on_failure; + *process_next = process_on_success; return i; } } /* End of token list */ - *terminator = terminate_always; + *process_next = NULL; return i; } /** + * Set shell stop state + * + * @v stop Shell stop state + */ +void shell_stop ( int stop ) { + stop_state = stop; +} + +/** + * Test and consume shell stop state + * + * @v stop Shell stop state to consume + * @v stopped Shell had been stopped + */ +int shell_stopped ( int stop ) { + int stopped; + + /* Test to see if we need to stop */ + stopped = ( stop_state >= stop ); + + /* Consume stop state */ + if ( stop_state <= stop ) + stop_state = 0; + + return stopped; +} + +/** * Execute command line * * @v command Command line - * @ret rc Command exit status + * @ret rc Return status code * * Execute the named command and arguments. */ int system ( const char *command ) { - int ( * terminator ) ( int rc ); + int ( * process_next ) ( int rc ); char *expcmd; char **argv; int argc; int count; + int process; int rc = 0; - /* Reset exit flag */ - shell_exit = 0; - /* Perform variable expansion */ expcmd = expand_command ( command ); if ( ! expcmd ) @@ -291,23 +307,31 @@ int system ( const char *command ) { split_command ( expcmd, tokens ); tokens[count] = NULL; + process = 1; for ( argv = tokens ; ; argv += ( argc + 1 ) ) { /* Find command terminator */ - argc = command_terminator ( argv, &terminator ); + argc = command_terminator ( argv, &process_next ); /* Execute command */ - argv[argc] = NULL; - rc = execv ( argv[0], argv ); + if ( process ) { + argv[argc] = NULL; + rc = execv ( argv[0], argv ); + } - /* Check exit flag */ - if ( shell_exit ) + /* Stop processing, if applicable */ + if ( shell_stopped ( SHELL_STOP_COMMAND ) ) break; - /* Handle terminator */ - if ( terminator ( rc ) ) + /* Stop processing if we have reached the end + * of the command. + */ + if ( ! process_next ) break; + + /* Determine whether or not to process next command */ + process = process_next ( rc ); } } @@ -322,7 +346,7 @@ int system ( const char *command ) { * * @v argc Argument count * @v argv Argument list - * @ret rc Exit code + * @ret rc Return status code */ static int echo_exec ( int argc, char **argv ) { int i; @@ -373,8 +397,8 @@ static int exit_exec ( int argc, char **argv ) { return rc; } - /* Set exit flag */ - shell_exit = 1; + /* Stop shell processing */ + shell_stop ( SHELL_STOP_COMMAND_SEQUENCE ); return exit_code; } diff --git a/src/hci/shell.c b/src/hci/shell.c index 7bf138a2e..3860b2e40 100644 --- a/src/hci/shell.c +++ b/src/hci/shell.c @@ -84,8 +84,7 @@ int shell ( void ) { rc = system ( line ); free ( line ); } - } while ( shell_exit == 0 ); - shell_exit = 0; + } while ( ! shell_stopped ( SHELL_STOP_COMMAND_SEQUENCE ) ); return rc; } diff --git a/src/image/script.c b/src/image/script.c index f9d5a3939..1c3ff82e2 100644 --- a/src/image/script.c +++ b/src/image/script.c @@ -34,6 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/command.h> #include <ipxe/parseopt.h> #include <ipxe/image.h> +#include <ipxe/shell.h> struct image_type script_image_type __image_type ( PROBE_NORMAL ); @@ -106,13 +107,8 @@ static int process_script ( int ( * process_line ) ( const char *line ), */ static int terminate_on_exit_or_failure ( int rc ) { - /* Check and consume exit flag */ - if ( shell_exit ) { - shell_exit = 0; - return 1; - } - - return ( rc != 0 ); + return ( shell_stopped ( SHELL_STOP_COMMAND_SEQUENCE ) || + ( rc != 0 ) ); } /** @@ -292,6 +288,9 @@ static int goto_exec ( int argc, char **argv ) { return rc; } + /* Terminate processing of current command */ + shell_stop ( SHELL_STOP_COMMAND ); + return 0; } diff --git a/src/include/ipxe/command.h b/src/include/ipxe/command.h index 2486e7aa2..a7d0ae462 100644 --- a/src/include/ipxe/command.h +++ b/src/include/ipxe/command.h @@ -23,6 +23,4 @@ struct command { #define __command __table_entry ( COMMANDS, 01 ) -extern int shell_exit; - #endif /* _IPXE_COMMAND_H */ diff --git a/src/include/ipxe/shell.h b/src/include/ipxe/shell.h index 55e56346c..faa32f422 100644 --- a/src/include/ipxe/shell.h +++ b/src/include/ipxe/shell.h @@ -9,6 +9,28 @@ FILE_LICENCE ( GPL2_OR_LATER ); +/** Shell stop states */ +enum shell_stop_state { + /** Continue processing */ + SHELL_CONTINUE = 0, + /** + * Stop processing current command line + * + * This is the stop state entered by commands that change the flow + * of execution, such as "goto". + */ + SHELL_STOP_COMMAND = 1, + /** + * Stop processing commands + * + * This is the stop state entered by commands that terminate + * the flow of execution, such as "exit". + */ + SHELL_STOP_COMMAND_SEQUENCE = 2, +}; + +extern void shell_stop ( int stop ); +extern int shell_stopped ( int stop ); extern int shell ( void ); #endif /* _IPXE_SHELL_H */ |