]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[cmdline] Match user expectations for &&, ||, goto, and exit
authorMichael Brown <mcb30@ipxe.org>
Mon, 29 Nov 2010 14:19:59 +0000 (14:19 +0000)
committerMichael Brown <mcb30@ipxe.org>
Mon, 29 Nov 2010 14:19:59 +0000 (14:19 +0000)
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>
src/core/exec.c
src/hci/shell.c
src/image/script.c
src/include/ipxe/command.h
src/include/ipxe/shell.h

index d96b8a768ab8d59571162c3a77957ca5529fd417..bb3b343d8de653a16d56aace49362daccca2ee81 100644 (file)
@@ -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;
 }
index 7bf138a2ece75dda1dcfab7018851e0a34ca84d2..3860b2e406bdbbae88b100fa134766def4cbf296 100644 (file)
@@ -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;
 }
index f9d5a3939c6895176742f9eae91696c86fb09e48..1c3ff82e2d3cdf8ee5291daac6cf33471126304e 100644 (file)
@@ -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;
 }
 
index 2486e7aa2e5be0778410ec40b5e37773be001116..a7d0ae4623af8f3bf77d7daba6e5040982776cfe 100644 (file)
@@ -23,6 +23,4 @@ struct command {
 
 #define __command __table_entry ( COMMANDS, 01 )
 
-extern int shell_exit;
-
 #endif /* _IPXE_COMMAND_H */
index 55e56346cdb639e8d30b373f50e0012189cbafa2..faa32f422c61576dd6193182ae898fbfbce8ab5b 100644 (file)
@@ -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 */