# HG changeset patch # User Paul Boddie # Date 1645227886 -3600 # Node ID 69bbf40245617b472cc4981de74405f456c46eb5 # Parent a12a6eac6d420cc368d0370fcc78cfd5b634a4c5 Added elementary scripting support for more versatile testing. diff -r a12a6eac6d42 -r 69bbf4024561 libe2access/host/e2access.c --- a/libe2access/host/e2access.c Sat Feb 19 00:43:46 2022 +0100 +++ b/libe2access/host/e2access.c Sat Feb 19 00:44:46 2022 +0100 @@ -41,6 +41,10 @@ const int BUFSIZE = 4096; +/* Maximum number of arguments in scripts. */ + +const int MAX_ARGS = 32; + /* Alternative metadata set by options. */ @@ -447,7 +451,7 @@ if (utils_list_dir(fs, path)) { - fprintf(stderr, "Failed to list directory: %s\n", path); + fprintf(stderr, "Failed to list object: %s\n", path); return 1; } } @@ -576,6 +580,169 @@ return _remove(fs, argc, argv, 0); } +/* Read a line from a file into the given buffer. */ + +struct read_line_state +{ + char *buffer, *start, *end, *eolp; + size_t remaining; +}; + +static char *read_line(FILE *fp, struct read_line_state *state) +{ + size_t nread; + + do + { + do + { + /* Search for a newline character in any available text. */ + + if (state->end > state->start) + { + state->eolp = strchr(state->start, '\n'); + + if (state->eolp != NULL) + { + *(state->eolp) = '\0'; + return state->eolp; + } + } + + /* Obtain more text if necessary. */ + + nread = fread(state->end, sizeof(char), state->remaining, fp); + + /* Handle end of file condition. */ + + if (!nread) + { + if (state->end > state->start) + return state->end; + else + return NULL; + } + + /* Zero-terminate the string for searching. */ + + *(state->end + nread) = '\0'; + + /* Advance the end of string and subtract remaining space. */ + + state->end += nread; + state->remaining -= nread; + } + while (state->remaining); + + /* Copy the remaining text to the start of the buffer. */ + + if (state->start > state->buffer) + { + strcpy(state->buffer, state->start); + + state->end -= (state->start - state->buffer); + state->start = state->buffer; + state->remaining = BUFSIZE - 1 - (state->end - state->buffer); + } + } + while (state->remaining); + + return NULL; +} + +/* Parse the text in the given region, returning details of arguments. */ + +static void parse_line(char *start, char *end, int *num_args, char *args[], const int max_args) +{ + *num_args = 0; + + while ((start != NULL) && (start < end) && (*num_args < max_args)) + { + args[*num_args] = start; + (*num_args)++; + + /* NOTE: Only handling spaces as delimiters. */ + + start = strchr(start, ' '); + + if (start != NULL) + { + *start = '\0'; + + if (start < end) + start++; + } + } +} + +/* Read operations from a script file. */ + +enum op_results +{ + OP_SUCCESS = 0, + OP_FAILED = 1, + OP_UNKNOWN = 2, +}; + +int handle_op_result(const char *operation, enum op_results op_result) +{ + if (op_result == OP_UNKNOWN) + { + fprintf(stderr, "Operation not recognised: %s\n", operation); + return 1; + } + else if (op_result == OP_FAILED) + { + fprintf(stderr, "Operation failed: %s\n", operation); + return 1; + } + else + return 0; +} + +enum op_results run_operation(ext2_filsys fs, const char *operation, int argc, char *argv[]); + +int run_script(ext2_filsys fs, int argc, char *argv[]) +{ + FILE *fp; + char buffer[BUFSIZE]; + struct read_line_state state; + enum op_results op_result; + int num_args; + char *args[MAX_ARGS]; + int i; + + for (i = 0; i < argc; i++) + { + fp = fopen(argv[i], "r"); + + state.buffer = buffer; + state.start = buffer; + state.end = buffer; + state.remaining = BUFSIZE - 1; + + while (read_line(fp, &state) != NULL) + { + parse_line(state.start, state.eolp, &num_args, args, MAX_ARGS); + + if (num_args > 1) + { + op_result = run_operation(fs, args[0], num_args - 1, &args[1]); + + if (handle_op_result(args[0], op_result)) + return 1; + } + + state.start = state.eolp + 1; + } + + fclose(fp); + } + + return 0; +} + + /* Help message. */ @@ -592,14 +759,21 @@ \n\ -m MASK Set mode/permissions mask for new directories\n\ \n\ -Operations:\n\ +Transfer operations:\n\ \n\ copy-in Copy files into a directory within the image\n\ copy-out Copy files from the image into a directory\n\ +\n\ +Image operations:\n\ +\n\ ls List files and directories within the image\n\ mkdir Make directories within the image\n\ rm Remove non-directory objects from the image\n\ rmdir Remove directories from the image\n\ +\n\ +Script operations:\n\ +\n\ + script Read operations from a script file\n\ "; /* Operations exposed by the program. */ @@ -617,9 +791,34 @@ {"mkdir", make_dirs}, {"rm", remove_non_dirs}, {"rmdir", remove_dirs}, + {"script", run_script}, {NULL, NULL}, }; +/* Invocation of operations. */ + +enum op_results run_operation(ext2_filsys fs, const char *operation, int argc, char *argv[]) +{ + struct operation *op; + int exitcode; + + for (op = &operations[0]; op->name != NULL; op++) + { + if (!strcmp(operation, op->name)) + { + exitcode = op->fn(fs, argc, argv); + if (exitcode) + return OP_FAILED; + break; + } + } + + if (op->name == NULL) + return OP_UNKNOWN; + + return OP_SUCCESS; +} + /* Main program. */ int main(int argc, char *argv[]) @@ -632,9 +831,9 @@ /* Program argument details. */ char **args; - char *fsname, *operation, *filename; + char *fsname, *filename; int num_args; - struct operation *op; + enum op_results op_result; /* Parse program options and initialise the argument details. */ @@ -672,26 +871,8 @@ /* Perform the requested operation. */ - operation = args[1]; - args = &args[2]; - num_args -= 2; - - for (op = &operations[0]; op->name != NULL; op++) - { - if (!strcmp(operation, op->name)) - { - exitcode = op->fn(fs, num_args, args); - if (exitcode) - fprintf(stderr, "Operation failed: %s\n", operation); - break; - } - } - - if (op->name == NULL) - { - fprintf(stderr, "Operation not recognised: %s\n", operation); - exitcode = 1; - } + op_result = run_operation(fs, args[1], num_args - 2, &args[2]); + exitcode = handle_op_result(args[1], op_result); /* Close the filesystem image. */