Browse Source

[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>
tags/v1.20.1
Michael Brown 13 years ago
parent
commit
9ba988809d
1 changed files with 115 additions and 36 deletions
  1. 115
    36
      src/core/exec.c

+ 115
- 36
src/core/exec.c View File

@@ -58,12 +58,12 @@ int execv ( const char *command, char * const argv[] ) {
58 58
 	/* Count number of arguments */
59 59
 	for ( argc = 0 ; argv[argc] ; argc++ ) {}
60 60
 
61
+	/* An empty command is deemed to do nothing, successfully */
62
+	if ( command == NULL )
63
+		return 0;
64
+
61 65
 	/* Sanity checks */
62
-	if ( ! command ) {
63
-		DBG ( "No command\n" );
64
-		return -EINVAL;
65
-	}
66
-	if ( ! argc ) {
66
+	if ( argc == 0 ) {
67 67
 		DBG ( "%s: empty argument list\n", command );
68 68
 		return -EINVAL;
69 69
 	}
@@ -156,39 +156,102 @@ static char * expand_command ( const char *command ) {
156 156
 }
157 157
 
158 158
 /**
159
- * Split command line into argv array
159
+ * Split command line into tokens
160 160
  *
161
- * @v args		Command line
162
- * @v argv		Argument array to populate, or NULL
163
- * @ret argc		Argument count
161
+ * @v command		Command line
162
+ * @v tokens		Token list to populate, or NULL
163
+ * @ret count		Number of tokens
164 164
  *
165
- * Splits the command line into whitespace-delimited arguments.  If @c
166
- * argv is non-NULL, any whitespace in the command line will be
165
+ * Splits the command line into whitespace-delimited tokens.  If @c
166
+ * tokens is non-NULL, any whitespace in the command line will be
167 167
  * replaced with NULs.
168 168
  */
169
-static int split_args ( char *args, char * argv[] ) {
170
-	int argc = 0;
169
+static int split_command ( char *command, char **tokens ) {
170
+	int count = 0;
171 171
 
172 172
 	while ( 1 ) {
173 173
 		/* Skip over any whitespace / convert to NUL */
174
-		while ( isspace ( *args ) ) {
175
-			if ( argv )
176
-				*args = '\0';
177
-			args++;
174
+		while ( isspace ( *command ) ) {
175
+			if ( tokens )
176
+				*command = '\0';
177
+			command++;
178 178
 		}
179 179
 		/* Check for end of line */
180
-		if ( ! *args )
180
+		if ( ! *command )
181 181
 			break;
182 182
 		/* We have found the start of the next argument */
183
-		if ( argv )
184
-			argv[argc] = args;
185
-		argc++;
183
+		if ( tokens )
184
+			tokens[count] = command;
185
+		count++;
186 186
 		/* Skip to start of next whitespace, if any */
187
-		while ( *args && ! isspace ( *args ) ) {
188
-			args++;
187
+		while ( *command && ! isspace ( *command ) ) {
188
+			command++;
189
+		}
190
+	}
191
+	return count;
192
+}
193
+
194
+/**
195
+ * Terminate command unconditionally
196
+ *
197
+ * @v rc		Status of previous command
198
+ * @ret terminate	Terminate command
199
+ */
200
+static int terminate_always ( int rc __unused ) {
201
+	return 1;
202
+}
203
+
204
+/**
205
+ * Terminate command only if previous command succeeded
206
+ *
207
+ * @v rc		Status of previous command
208
+ * @ret terminate	Terminate command
209
+ */
210
+static int terminate_on_success ( int rc ) {
211
+	return ( rc == 0 );
212
+}
213
+
214
+/**
215
+ * Terminate command only if previous command failed
216
+ *
217
+ * @v rc		Status of previous command
218
+ * @ret terminate	Terminate command
219
+ */
220
+static int terminate_on_failure ( int rc ) {
221
+	return ( rc != 0 );
222
+}
223
+
224
+/**
225
+ * Find command terminator
226
+ *
227
+ * @v tokens		Token list
228
+ * @ret terminator	Terminator type
229
+ * @ret argc		Argument count
230
+ */
231
+static int command_terminator ( char **tokens,
232
+				int ( **terminator ) ( int rc ) ) {
233
+	unsigned int i;
234
+
235
+	/* Find first terminating token */
236
+	for ( i = 0 ; tokens[i] ; i++ ) {
237
+		if ( tokens[i][0] == '#' ) {
238
+			/* Start of a comment */
239
+			*terminator = terminate_always;
240
+			return i;
241
+		} else if ( strcmp ( tokens[i], "||" ) == 0 ) {
242
+			/* Short-circuit logical OR */
243
+			*terminator = terminate_on_success;
244
+			return i;
245
+		} else if ( strcmp ( tokens[i], "&&" ) == 0 ) {
246
+			/* Short-circuit logical AND */
247
+			*terminator = terminate_on_failure;
248
+			return i;
189 249
 		}
190 250
 	}
191
-	return argc;
251
+
252
+	/* End of token list */
253
+	*terminator = terminate_always;
254
+	return i;
192 255
 }
193 256
 
194 257
 /**
@@ -200,30 +263,46 @@ static int split_args ( char *args, char * argv[] ) {
200 263
  * Execute the named command and arguments.
201 264
  */
202 265
 int system ( const char *command ) {
203
-	char *args;
266
+	int ( * terminator ) ( int rc );
267
+	char *expcmd;
268
+	char **argv;
204 269
 	int argc;
270
+	int count;
205 271
 	int rc = 0;
206 272
 
207 273
 	/* Perform variable expansion */
208
-	args = expand_command ( command );
209
-	if ( ! args )
274
+	expcmd = expand_command ( command );
275
+	if ( ! expcmd )
210 276
 		return -ENOMEM;
211 277
 
212
-	/* Count arguments */
213
-	argc = split_args ( args, NULL );
278
+	/* Count tokens */
279
+	count = split_command ( expcmd, NULL );
214 280
 
215
-	/* Create argv array and execute command */
216
-	if ( argc ) {
217
-		char * argv[argc + 1];
281
+	/* Create token array */
282
+	if ( count ) {
283
+		char * tokens[count + 1];
218 284
 		
219
-		split_args ( args, argv );
220
-		argv[argc] = NULL;
285
+		split_command ( expcmd, tokens );
286
+		tokens[count] = NULL;
287
+
288
+		for ( argv = tokens ; ; argv += ( argc + 1 ) ) {
221 289
 
222
-		if ( argv[0][0] != '#' )
290
+			/* Find command terminator */
291
+			argc = command_terminator ( argv, &terminator );
292
+
293
+			/* Execute command */
294
+			argv[argc] = NULL;
223 295
 			rc = execv ( argv[0], argv );
296
+
297
+			/* Handle terminator */
298
+			if ( terminator ( rc ) )
299
+				break;
300
+		}
224 301
 	}
225 302
 
226
-	free ( args );
303
+	/* Free expanded command */
304
+	free ( expcmd );
305
+
227 306
 	return rc;
228 307
 }
229 308
 

Loading…
Cancel
Save