From 9ba988809d2bae422fe36d5e6c8563b3cd6cac55 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 22 Nov 2010 01:47:07 +0000 Subject: [cmdline] Add trivial logical operators to iPXE command lines Make the "||" and "&&" operators available within iPXE commands. For example: dhcp net0 || set net0/ip 192.168.0.2 would attempt to acquire an IP address via DHCP, falling back to a static address if DHCP fails. As a side-effect, comments may now be appended to any line. For example: dhcp net0 || set net0/ip 192.168.0.2 # Try DHCP first, then static Signed-off-by: Michael Brown --- src/core/exec.c | 151 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 115 insertions(+), 36 deletions(-) (limited to 'src/core') diff --git a/src/core/exec.c b/src/core/exec.c index 7e866325..20b97a6d 100644 --- a/src/core/exec.c +++ b/src/core/exec.c @@ -58,12 +58,12 @@ int execv ( const char *command, char * const argv[] ) { /* Count number of arguments */ for ( argc = 0 ; argv[argc] ; argc++ ) {} + /* An empty command is deemed to do nothing, successfully */ + if ( command == NULL ) + return 0; + /* Sanity checks */ - if ( ! command ) { - DBG ( "No command\n" ); - return -EINVAL; - } - if ( ! argc ) { + if ( argc == 0 ) { DBG ( "%s: empty argument list\n", command ); return -EINVAL; } @@ -156,39 +156,102 @@ static char * expand_command ( const char *command ) { } /** - * Split command line into argv array + * Split command line into tokens * - * @v args Command line - * @v argv Argument array to populate, or NULL - * @ret argc Argument count + * @v command Command line + * @v tokens Token list to populate, or NULL + * @ret count Number of tokens * - * Splits the command line into whitespace-delimited arguments. If @c - * argv is non-NULL, any whitespace in the command line will be + * Splits the command line into whitespace-delimited tokens. If @c + * tokens is non-NULL, any whitespace in the command line will be * replaced with NULs. */ -static int split_args ( char *args, char * argv[] ) { - int argc = 0; +static int split_command ( char *command, char **tokens ) { + int count = 0; while ( 1 ) { /* Skip over any whitespace / convert to NUL */ - while ( isspace ( *args ) ) { - if ( argv ) - *args = '\0'; - args++; + while ( isspace ( *command ) ) { + if ( tokens ) + *command = '\0'; + command++; } /* Check for end of line */ - if ( ! *args ) + if ( ! *command ) break; /* We have found the start of the next argument */ - if ( argv ) - argv[argc] = args; - argc++; + if ( tokens ) + tokens[count] = command; + count++; /* Skip to start of next whitespace, if any */ - while ( *args && ! isspace ( *args ) ) { - args++; + while ( *command && ! isspace ( *command ) ) { + command++; + } + } + return count; +} + +/** + * 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 + * + * @v rc Status of previous command + * @ret terminate Terminate command + */ +static int terminate_on_success ( int rc ) { + return ( rc == 0 ); +} + +/** + * Terminate command only if previous command failed + * + * @v rc Status of previous command + * @ret terminate Terminate command + */ +static int terminate_on_failure ( int rc ) { + return ( rc != 0 ); +} + +/** + * Find command terminator + * + * @v tokens Token list + * @ret terminator Terminator type + * @ret argc Argument count + */ +static int command_terminator ( char **tokens, + int ( **terminator ) ( 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; + } else if ( strcmp ( tokens[i], "||" ) == 0 ) { + /* Short-circuit logical OR */ + *terminator = terminate_on_success; + return i; + } else if ( strcmp ( tokens[i], "&&" ) == 0 ) { + /* Short-circuit logical AND */ + *terminator = terminate_on_failure; + return i; } } - return argc; + + /* End of token list */ + *terminator = terminate_always; + return i; } /** @@ -200,30 +263,46 @@ static int split_args ( char *args, char * argv[] ) { * Execute the named command and arguments. */ int system ( const char *command ) { - char *args; + int ( * terminator ) ( int rc ); + char *expcmd; + char **argv; int argc; + int count; int rc = 0; /* Perform variable expansion */ - args = expand_command ( command ); - if ( ! args ) + expcmd = expand_command ( command ); + if ( ! expcmd ) return -ENOMEM; - /* Count arguments */ - argc = split_args ( args, NULL ); + /* Count tokens */ + count = split_command ( expcmd, NULL ); - /* Create argv array and execute command */ - if ( argc ) { - char * argv[argc + 1]; + /* Create token array */ + if ( count ) { + char * tokens[count + 1]; - split_args ( args, argv ); - argv[argc] = NULL; + split_command ( expcmd, tokens ); + tokens[count] = NULL; + + for ( argv = tokens ; ; argv += ( argc + 1 ) ) { - if ( argv[0][0] != '#' ) + /* Find command terminator */ + argc = command_terminator ( argv, &terminator ); + + /* Execute command */ + argv[argc] = NULL; rc = execv ( argv[0], argv ); + + /* Handle terminator */ + if ( terminator ( rc ) ) + break; + } } - free ( args ); + /* Free expanded command */ + free ( expcmd ); + return rc; } -- cgit v1.2.3-55-g7522