diff options
author | Tyler Murphy <tylerm@tylerm.dev> | 2023-05-01 18:43:32 -0400 |
---|---|---|
committer | Tyler Murphy <tylerm@tylerm.dev> | 2023-05-01 18:43:32 -0400 |
commit | 82e55dde69dc89fdf06e751b75449f35836282a1 (patch) | |
tree | 29e49ae19cba7f6a7c2987c381ebe4b253edc053 /src/commands/xargs.c | |
parent | move back to c99 (diff) | |
download | lazysphere-82e55dde69dc89fdf06e751b75449f35836282a1.tar.gz lazysphere-82e55dde69dc89fdf06e751b75449f35836282a1.tar.bz2 lazysphere-82e55dde69dc89fdf06e751b75449f35836282a1.zip |
refactor and xargs
Diffstat (limited to '')
-rw-r--r-- | src/commands/xargs.c | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/src/commands/xargs.c b/src/commands/xargs.c new file mode 100644 index 0000000..3b96bb6 --- /dev/null +++ b/src/commands/xargs.c @@ -0,0 +1,163 @@ +#include "../command.h" +#include <stdio.h> +#include <stdlib.h> + +static struct { + bool null_seperated; + bool ignore_empty; + bool print_command; + bool prompt_command; + int max_args; + FILE* file; +} flags; + +static void help(void) { + printf("Usage: xargs [OPTIONS] [PROG ARGS]\n\n"); + printf("Run PROG on every item given by stdin\n\n"); + printf("\t-0\tInput is separated by NULs\n"); + printf("\t-a FILE\tRead from FILE instead of stdin\n"); + printf("\t-r\tDon't run command if input is empty\n"); + printf("\t-t\tPrint the command on stderr before execution\n"); + printf("\t-p\tAsk user whether to run each command\n"); + printf("\t-n N\tPass no more than N args to PROG\n"); +} + +static int short_arg(char c, char* next) { + UNUSED(next); + switch (c) { + case '0': + flags.null_seperated = true; + break; + case 'a': + check_arg(next); + flags.file = get_file(next, "r"); + return ARG_USED; + case 'r': + flags.ignore_empty = true; + break; + case 't': + flags.print_command = true; + break; + case 'p': + flags.prompt_command = true; + break; + case 'n': + check_arg(next); + long int n = get_number(next); + if (n < 1) { + error("error: max arg count must be at least 1"); + } + return ARG_USED; + } + return ARG_UNUSED; +} + +char* read_next(FILE* file, int arg_count) { + + if (arg_count == flags.max_args) return NULL; + + int size = 0; + int capacity = 8; + char* buf = malloc(sizeof(char) * capacity); + + char c; + while(c = getc(file), true) { + if (c == EOF && size == 0) { + free(buf); + return NULL; + } + + if (size == capacity) { + capacity *= 2; + buf = realloc(buf, sizeof(char) * capacity); + } + + if (c == '\0' || c == EOF || (!flags.null_seperated && c == '\n')) { + buf[size++] = '\0'; + return buf; + } else { + buf[size++] = c; + } + } +} + +void read_args(FILE* file, char*** args, int* size, int* capacity) { + char* arg; + while (arg = read_next(file, *size), true) { + if (*size == *capacity) { + *capacity *= 2; + *args = realloc(*args, sizeof(char*) * *capacity); + } + (*args)[(*size)++] = arg; + if (arg == NULL) break; + } +} + +COMMAND(xargs) { + flags.null_seperated = false; + flags.ignore_empty = false; + flags.print_command = false; + flags.print_command = false; + flags.max_args = -1; + flags.file = stdin; + + int start = parse_args(argc, argv, help, short_arg, NULL); + + char* command; + if (start >= argc) { + command = "echo"; + } else { + command = argv[start]; + } + + int arg_start = start + 1; + int arg_on_stack_count; + + if (arg_start >= argc) { + arg_on_stack_count = 0; + arg_start = argc - 1; + } else { + arg_on_stack_count = argc - arg_start; + } + + int size = arg_on_stack_count + 1; + int capacity = size + 8; + char** args = malloc(sizeof(char*) * capacity); + args[0] = command; + memcpy(&args[1], &argv[arg_start], arg_on_stack_count * sizeof(char*)); + read_args(flags.file, &args, &size, &capacity); + + if (flags.ignore_empty && size < 2) goto cleanup; + + if (flags.prompt_command || flags.print_command) { + for (int i = 0; i < size - 1; i++) { + fprintf(stderr, "%s ", args[i]); + } + fprintf(stderr, "\b\n"); + } + + if (flags.prompt_command) { + fprintf(stderr, "Run command? "); + fflush(stderr); + FILE* in = get_tty_stream("r"); + char c = getc(in); + fclose(in); + if (c != 'y' && c != 'Y') { + fprintf(stderr, "Skipping...\n"); + goto cleanup; + } + } + + if (execvp(command, args) == -1) { + error("error: failed to execute command: %s", strerror(errno)); + } + +cleanup: + + for (int i = arg_on_stack_count + 1; i < size - 1; i++) { + free(args[i]); + } + fclose(flags.file); + + return EXIT_SUCCESS; +} |