From 7bebe9579ee5ea1cfcfcdf25032dbab80ccc489f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 29 Nov 2010 14:19:59 +0000 Subject: [PATCH] [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 --- src/core/exec.c | 106 +++++++++++++++++++++++-------------- src/hci/shell.c | 3 +- src/image/script.c | 13 +++-- src/include/ipxe/command.h | 2 - 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 d96b8a76..bb3b343d 100644 --- a/src/core/exec.c +++ b/src/core/exec.c @@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include /** @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 + * 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_always ( int rc __unused ) { - return 1; -} - -/** - * Terminate command only if previous command succeeded - * - * @v rc Status of previous command - * @ret terminate Terminate 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 7bf138a2..3860b2e4 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 f9d5a393..1c3ff82e 100644 --- a/src/image/script.c +++ b/src/image/script.c @@ -34,6 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include 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 2486e7aa..a7d0ae46 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 55e56346..faa32f42 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 */