|
@@ -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
|
|