david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[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>
This commit is contained in:
Michael Brown 2010-11-29 14:19:59 +00:00
parent 01df5c510f
commit 7bebe9579e
5 changed files with 94 additions and 52 deletions

View File

@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/command.h> #include <ipxe/command.h>
#include <ipxe/parseopt.h> #include <ipxe/parseopt.h>
#include <ipxe/settings.h> #include <ipxe/settings.h>
#include <ipxe/shell.h>
/** @file /** @file
* *
@ -38,15 +39,15 @@ FILE_LICENCE ( GPL2_OR_LATER );
* *
*/ */
/** Shell exit flag */ /** Shell stop state */
int shell_exit; static int stop_state;
/** /**
* Execute command * Execute command
* *
* @v command Command name * @v command Command name
* @v argv Argument list * @v argv Argument list
* @ret rc Command exit status * @ret rc Return status code
* *
* Execute the named command. Unlike a traditional POSIX execv(), * Execute the named command. Unlike a traditional POSIX execv(),
* this function returns the exit status of the command. * 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 * @v rc Status of previous command
* @ret terminate Terminate command * @ret process Process next command
*/ */
static int terminate_always ( int rc __unused ) { static int process_on_success ( int rc ) {
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 ) {
return ( rc == 0 ); return ( rc == 0 );
} }
/** /**
* Terminate command only if previous command failed * Process next command only if previous command failed
* *
* @v rc Status of previous command * @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 ); return ( rc != 0 );
} }
@ -229,54 +220,79 @@ static int terminate_on_failure ( int rc ) {
* Find command terminator * Find command terminator
* *
* @v tokens Token list * @v tokens Token list
* @ret terminator Terminator type * @ret process_next "Should next command be processed?" function
* @ret argc Argument count * @ret argc Argument count
*/ */
static int command_terminator ( char **tokens, static int command_terminator ( char **tokens,
int ( **terminator ) ( int rc ) ) { int ( **process_next ) ( int rc ) ) {
unsigned int i; unsigned int i;
/* Find first terminating token */ /* Find first terminating token */
for ( i = 0 ; tokens[i] ; i++ ) { for ( i = 0 ; tokens[i] ; i++ ) {
if ( tokens[i][0] == '#' ) { if ( tokens[i][0] == '#' ) {
/* Start of a comment */ /* Start of a comment */
*terminator = terminate_always; break;
return i;
} else if ( strcmp ( tokens[i], "||" ) == 0 ) { } else if ( strcmp ( tokens[i], "||" ) == 0 ) {
/* Short-circuit logical OR */ /* Short-circuit logical OR */
*terminator = terminate_on_success; *process_next = process_on_failure;
return i; return i;
} else if ( strcmp ( tokens[i], "&&" ) == 0 ) { } else if ( strcmp ( tokens[i], "&&" ) == 0 ) {
/* Short-circuit logical AND */ /* Short-circuit logical AND */
*terminator = terminate_on_failure; *process_next = process_on_success;
return i; return i;
} }
} }
/* End of token list */ /* End of token list */
*terminator = terminate_always; *process_next = NULL;
return i; 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 * Execute command line
* *
* @v command Command line * @v command Command line
* @ret rc Command exit status * @ret rc Return status code
* *
* Execute the named command and arguments. * Execute the named command and arguments.
*/ */
int system ( const char *command ) { int system ( const char *command ) {
int ( * terminator ) ( int rc ); int ( * process_next ) ( int rc );
char *expcmd; char *expcmd;
char **argv; char **argv;
int argc; int argc;
int count; int count;
int process;
int rc = 0; int rc = 0;
/* Reset exit flag */
shell_exit = 0;
/* Perform variable expansion */ /* Perform variable expansion */
expcmd = expand_command ( command ); expcmd = expand_command ( command );
if ( ! expcmd ) if ( ! expcmd )
@ -291,23 +307,31 @@ int system ( const char *command ) {
split_command ( expcmd, tokens ); split_command ( expcmd, tokens );
tokens[count] = NULL; tokens[count] = NULL;
process = 1;
for ( argv = tokens ; ; argv += ( argc + 1 ) ) { for ( argv = tokens ; ; argv += ( argc + 1 ) ) {
/* Find command terminator */ /* Find command terminator */
argc = command_terminator ( argv, &terminator ); argc = command_terminator ( argv, &process_next );
/* Execute command */ /* Execute command */
if ( process ) {
argv[argc] = NULL; argv[argc] = NULL;
rc = execv ( argv[0], argv ); rc = execv ( argv[0], argv );
}
/* Check exit flag */ /* Stop processing, if applicable */
if ( shell_exit ) if ( shell_stopped ( SHELL_STOP_COMMAND ) )
break; break;
/* Handle terminator */ /* Stop processing if we have reached the end
if ( terminator ( rc ) ) * of the command.
*/
if ( ! process_next )
break; 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 argc Argument count
* @v argv Argument list * @v argv Argument list
* @ret rc Exit code * @ret rc Return status code
*/ */
static int echo_exec ( int argc, char **argv ) { static int echo_exec ( int argc, char **argv ) {
int i; int i;
@ -373,8 +397,8 @@ static int exit_exec ( int argc, char **argv ) {
return rc; return rc;
} }
/* Set exit flag */ /* Stop shell processing */
shell_exit = 1; shell_stop ( SHELL_STOP_COMMAND_SEQUENCE );
return exit_code; return exit_code;
} }

View File

@ -84,8 +84,7 @@ int shell ( void ) {
rc = system ( line ); rc = system ( line );
free ( line ); free ( line );
} }
} while ( shell_exit == 0 ); } while ( ! shell_stopped ( SHELL_STOP_COMMAND_SEQUENCE ) );
shell_exit = 0;
return rc; return rc;
} }

View File

@ -34,6 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/command.h> #include <ipxe/command.h>
#include <ipxe/parseopt.h> #include <ipxe/parseopt.h>
#include <ipxe/image.h> #include <ipxe/image.h>
#include <ipxe/shell.h>
struct image_type script_image_type __image_type ( PROBE_NORMAL ); 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 ) { static int terminate_on_exit_or_failure ( int rc ) {
/* Check and consume exit flag */ return ( shell_stopped ( SHELL_STOP_COMMAND_SEQUENCE ) ||
if ( shell_exit ) { ( rc != 0 ) );
shell_exit = 0;
return 1;
}
return ( rc != 0 );
} }
/** /**
@ -292,6 +288,9 @@ static int goto_exec ( int argc, char **argv ) {
return rc; return rc;
} }
/* Terminate processing of current command */
shell_stop ( SHELL_STOP_COMMAND );
return 0; return 0;
} }

View File

@ -23,6 +23,4 @@ struct command {
#define __command __table_entry ( COMMANDS, 01 ) #define __command __table_entry ( COMMANDS, 01 )
extern int shell_exit;
#endif /* _IPXE_COMMAND_H */ #endif /* _IPXE_COMMAND_H */

View File

@ -9,6 +9,28 @@
FILE_LICENCE ( GPL2_OR_LATER ); 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 ); extern int shell ( void );
#endif /* _IPXE_SHELL_H */ #endif /* _IPXE_SHELL_H */