Browse Source

[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>
tags/v1.20.1
Michael Brown 14 years ago
parent
commit
7bebe9579e
5 changed files with 94 additions and 52 deletions
  1. 65
    41
      src/core/exec.c
  2. 1
    2
      src/hci/shell.c
  3. 6
    7
      src/image/script.c
  4. 0
    2
      src/include/ipxe/command.h
  5. 22
    0
      src/include/ipxe/shell.h

+ 65
- 41
src/core/exec.c View File

31
 #include <ipxe/command.h>
31
 #include <ipxe/command.h>
32
 #include <ipxe/parseopt.h>
32
 #include <ipxe/parseopt.h>
33
 #include <ipxe/settings.h>
33
 #include <ipxe/settings.h>
34
+#include <ipxe/shell.h>
34
 
35
 
35
 /** @file
36
 /** @file
36
  *
37
  *
38
  *
39
  *
39
  */
40
  */
40
 
41
 
41
-/** Shell exit flag */
42
-int shell_exit;
42
+/** Shell stop state */
43
+static int stop_state;
43
 
44
 
44
 /**
45
 /**
45
  * Execute command
46
  * Execute command
46
  *
47
  *
47
  * @v command		Command name
48
  * @v command		Command name
48
  * @v argv		Argument list
49
  * @v argv		Argument list
49
- * @ret rc		Command exit status
50
+ * @ret rc		Return status code
50
  *
51
  *
51
  * Execute the named command.  Unlike a traditional POSIX execv(),
52
  * Execute the named command.  Unlike a traditional POSIX execv(),
52
  * this function returns the exit status of the command.
53
  * this function returns the exit status of the command.
196
 }
197
 }
197
 
198
 
198
 /**
199
 /**
199
- * Terminate command unconditionally
200
- *
201
- * @v rc		Status of previous command
202
- * @ret terminate	Terminate command
203
- */
204
-static int terminate_always ( int rc __unused ) {
205
-	return 1;
206
-}
207
-
208
-/**
209
- * Terminate command only if previous command succeeded
200
+ * Process next command only if previous command succeeded
210
  *
201
  *
211
  * @v rc		Status of previous command
202
  * @v rc		Status of previous command
212
- * @ret terminate	Terminate command
203
+ * @ret process		Process next command
213
  */
204
  */
214
-static int terminate_on_success ( int rc ) {
205
+static int process_on_success ( int rc ) {
215
 	return ( rc == 0 );
206
 	return ( rc == 0 );
216
 }
207
 }
217
 
208
 
218
 /**
209
 /**
219
- * Terminate command only if previous command failed
210
+ * Process next command only if previous command failed
220
  *
211
  *
221
  * @v rc		Status of previous command
212
  * @v rc		Status of previous command
222
- * @ret terminate	Terminate command
213
+ * @ret process		Process next command
223
  */
214
  */
224
-static int terminate_on_failure ( int rc ) {
215
+static int process_on_failure ( int rc ) {
225
 	return ( rc != 0 );
216
 	return ( rc != 0 );
226
 }
217
 }
227
 
218
 
229
  * Find command terminator
220
  * Find command terminator
230
  *
221
  *
231
  * @v tokens		Token list
222
  * @v tokens		Token list
232
- * @ret terminator	Terminator type
223
+ * @ret process_next	"Should next command be processed?" function
233
  * @ret argc		Argument count
224
  * @ret argc		Argument count
234
  */
225
  */
235
 static int command_terminator ( char **tokens,
226
 static int command_terminator ( char **tokens,
236
-				int ( **terminator ) ( int rc ) ) {
227
+				int ( **process_next ) ( int rc ) ) {
237
 	unsigned int i;
228
 	unsigned int i;
238
 
229
 
239
 	/* Find first terminating token */
230
 	/* Find first terminating token */
240
 	for ( i = 0 ; tokens[i] ; i++ ) {
231
 	for ( i = 0 ; tokens[i] ; i++ ) {
241
 		if ( tokens[i][0] == '#' ) {
232
 		if ( tokens[i][0] == '#' ) {
242
 			/* Start of a comment */
233
 			/* Start of a comment */
243
-			*terminator = terminate_always;
244
-			return i;
234
+			break;
245
 		} else if ( strcmp ( tokens[i], "||" ) == 0 ) {
235
 		} else if ( strcmp ( tokens[i], "||" ) == 0 ) {
246
 			/* Short-circuit logical OR */
236
 			/* Short-circuit logical OR */
247
-			*terminator = terminate_on_success;
237
+			*process_next = process_on_failure;
248
 			return i;
238
 			return i;
249
 		} else if ( strcmp ( tokens[i], "&&" ) == 0 ) {
239
 		} else if ( strcmp ( tokens[i], "&&" ) == 0 ) {
250
 			/* Short-circuit logical AND */
240
 			/* Short-circuit logical AND */
251
-			*terminator = terminate_on_failure;
241
+			*process_next = process_on_success;
252
 			return i;
242
 			return i;
253
 		}
243
 		}
254
 	}
244
 	}
255
 
245
 
256
 	/* End of token list */
246
 	/* End of token list */
257
-	*terminator = terminate_always;
247
+	*process_next = NULL;
258
 	return i;
248
 	return i;
259
 }
249
 }
260
 
250
 
251
+/**
252
+ * Set shell stop state
253
+ *
254
+ * @v stop		Shell stop state
255
+ */
256
+void shell_stop ( int stop ) {
257
+	stop_state = stop;
258
+}
259
+
260
+/**
261
+ * Test and consume shell stop state
262
+ *
263
+ * @v stop		Shell stop state to consume
264
+ * @v stopped		Shell had been stopped
265
+ */
266
+int shell_stopped ( int stop ) {
267
+	int stopped;
268
+
269
+	/* Test to see if we need to stop */
270
+	stopped = ( stop_state >= stop );
271
+
272
+	/* Consume stop state */
273
+	if ( stop_state <= stop )
274
+		stop_state = 0;
275
+
276
+	return stopped;
277
+}
278
+
261
 /**
279
 /**
262
  * Execute command line
280
  * Execute command line
263
  *
281
  *
264
  * @v command		Command line
282
  * @v command		Command line
265
- * @ret rc		Command exit status
283
+ * @ret rc		Return status code
266
  *
284
  *
267
  * Execute the named command and arguments.
285
  * Execute the named command and arguments.
268
  */
286
  */
269
 int system ( const char *command ) {
287
 int system ( const char *command ) {
270
-	int ( * terminator ) ( int rc );
288
+	int ( * process_next ) ( int rc );
271
 	char *expcmd;
289
 	char *expcmd;
272
 	char **argv;
290
 	char **argv;
273
 	int argc;
291
 	int argc;
274
 	int count;
292
 	int count;
293
+	int process;
275
 	int rc = 0;
294
 	int rc = 0;
276
 
295
 
277
-	/* Reset exit flag */
278
-	shell_exit = 0;
279
-
280
 	/* Perform variable expansion */
296
 	/* Perform variable expansion */
281
 	expcmd = expand_command ( command );
297
 	expcmd = expand_command ( command );
282
 	if ( ! expcmd )
298
 	if ( ! expcmd )
291
 		
307
 		
292
 		split_command ( expcmd, tokens );
308
 		split_command ( expcmd, tokens );
293
 		tokens[count] = NULL;
309
 		tokens[count] = NULL;
310
+		process = 1;
294
 
311
 
295
 		for ( argv = tokens ; ; argv += ( argc + 1 ) ) {
312
 		for ( argv = tokens ; ; argv += ( argc + 1 ) ) {
296
 
313
 
297
 			/* Find command terminator */
314
 			/* Find command terminator */
298
-			argc = command_terminator ( argv, &terminator );
315
+			argc = command_terminator ( argv, &process_next );
299
 
316
 
300
 			/* Execute command */
317
 			/* Execute command */
301
-			argv[argc] = NULL;
302
-			rc = execv ( argv[0], argv );
318
+			if ( process ) {
319
+				argv[argc] = NULL;
320
+				rc = execv ( argv[0], argv );
321
+			}
303
 
322
 
304
-			/* Check exit flag */
305
-			if ( shell_exit )
323
+			/* Stop processing, if applicable */
324
+			if ( shell_stopped ( SHELL_STOP_COMMAND ) )
306
 				break;
325
 				break;
307
 
326
 
308
-			/* Handle terminator */
309
-			if ( terminator ( rc ) )
327
+			/* Stop processing if we have reached the end
328
+			 * of the command.
329
+			 */
330
+			if ( ! process_next )
310
 				break;
331
 				break;
332
+
333
+			/* Determine whether or not to process next command */
334
+			process = process_next ( rc );
311
 		}
335
 		}
312
 	}
336
 	}
313
 
337
 
322
  *
346
  *
323
  * @v argc		Argument count
347
  * @v argc		Argument count
324
  * @v argv		Argument list
348
  * @v argv		Argument list
325
- * @ret rc		Exit code
349
+ * @ret rc		Return status code
326
  */
350
  */
327
 static int echo_exec ( int argc, char **argv ) {
351
 static int echo_exec ( int argc, char **argv ) {
328
 	int i;
352
 	int i;
373
 			return rc;
397
 			return rc;
374
 	}
398
 	}
375
 
399
 
376
-	/* Set exit flag */
377
-	shell_exit = 1;
400
+	/* Stop shell processing */
401
+	shell_stop ( SHELL_STOP_COMMAND_SEQUENCE );
378
 
402
 
379
 	return exit_code;
403
 	return exit_code;
380
 }
404
 }

+ 1
- 2
src/hci/shell.c View File

84
 			rc = system ( line );
84
 			rc = system ( line );
85
 			free ( line );
85
 			free ( line );
86
 		}
86
 		}
87
-	} while ( shell_exit == 0 );
88
-	shell_exit = 0;
87
+	} while ( ! shell_stopped ( SHELL_STOP_COMMAND_SEQUENCE ) );
89
 
88
 
90
 	return rc;
89
 	return rc;
91
 }
90
 }

+ 6
- 7
src/image/script.c View File

34
 #include <ipxe/command.h>
34
 #include <ipxe/command.h>
35
 #include <ipxe/parseopt.h>
35
 #include <ipxe/parseopt.h>
36
 #include <ipxe/image.h>
36
 #include <ipxe/image.h>
37
+#include <ipxe/shell.h>
37
 
38
 
38
 struct image_type script_image_type __image_type ( PROBE_NORMAL );
39
 struct image_type script_image_type __image_type ( PROBE_NORMAL );
39
 
40
 
106
  */
107
  */
107
 static int terminate_on_exit_or_failure ( int rc ) {
108
 static int terminate_on_exit_or_failure ( int rc ) {
108
 
109
 
109
-	/* Check and consume exit flag */
110
-	if ( shell_exit ) {
111
-		shell_exit = 0;
112
-		return 1;
113
-	}
114
-
115
-	return ( rc != 0 );
110
+	return ( shell_stopped ( SHELL_STOP_COMMAND_SEQUENCE ) ||
111
+		 ( rc != 0 ) );
116
 }
112
 }
117
 
113
 
118
 /**
114
 /**
292
 		return rc;
288
 		return rc;
293
 	}
289
 	}
294
 
290
 
291
+	/* Terminate processing of current command */
292
+	shell_stop ( SHELL_STOP_COMMAND );
293
+
295
 	return 0;
294
 	return 0;
296
 }
295
 }
297
 
296
 

+ 0
- 2
src/include/ipxe/command.h View File

23
 
23
 
24
 #define __command __table_entry ( COMMANDS, 01 )
24
 #define __command __table_entry ( COMMANDS, 01 )
25
 
25
 
26
-extern int shell_exit;
27
-
28
 #endif /* _IPXE_COMMAND_H */
26
 #endif /* _IPXE_COMMAND_H */

+ 22
- 0
src/include/ipxe/shell.h View File

9
 
9
 
10
 FILE_LICENCE ( GPL2_OR_LATER );
10
 FILE_LICENCE ( GPL2_OR_LATER );
11
 
11
 
12
+/** Shell stop states */
13
+enum shell_stop_state {
14
+	/** Continue processing */
15
+	SHELL_CONTINUE = 0,
16
+	/**
17
+	 * Stop processing current command line
18
+	 *
19
+	 * This is the stop state entered by commands that change the flow
20
+	 * of execution, such as "goto".
21
+	 */
22
+	SHELL_STOP_COMMAND = 1,
23
+	/**
24
+	 * Stop processing commands
25
+	 *
26
+	 * This is the stop state entered by commands that terminate
27
+	 * the flow of execution, such as "exit".
28
+	 */
29
+	SHELL_STOP_COMMAND_SEQUENCE = 2,
30
+};
31
+
32
+extern void shell_stop ( int stop );
33
+extern int shell_stopped ( int stop );
12
 extern int shell ( void );
34
 extern int shell ( void );
13
 
35
 
14
 #endif /* _IPXE_SHELL_H */
36
 #endif /* _IPXE_SHELL_H */

Loading…
Cancel
Save