summaryrefslogtreecommitdiffstats
path: root/src/core/exec.c
diff options
context:
space:
mode:
authorMichael Brown2010-11-22 02:47:07 +0100
committerMichael Brown2010-11-22 02:47:07 +0100
commit9ba988809d2bae422fe36d5e6c8563b3cd6cac55 (patch)
tree00a06c6a9d9b4702f3c31e5924b82b78c5ba6146 /src/core/exec.c
parent[pxe] Remove startpxe and stoppxe commands from default builds (diff)
downloadipxe-9ba988809d2bae422fe36d5e6c8563b3cd6cac55.tar.gz
ipxe-9ba988809d2bae422fe36d5e6c8563b3cd6cac55.tar.xz
ipxe-9ba988809d2bae422fe36d5e6c8563b3cd6cac55.zip
[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 <mcb30@ipxe.org>
Diffstat (limited to 'src/core/exec.c')
-rw-r--r--src/core/exec.c151
1 files changed, 115 insertions, 36 deletions
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;
}