#include "../command.h" #include #include 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': { long int n; check_arg(next); n = get_number(next); if (n < 1) { error("max arg count must be at least 1"); } flags.max_args = n; return ARG_USED; } default: return ARG_INVALID; } return ARG_UNUSED; } char* read_next(FILE* file, int arg_count) { int size, capacity; char* buf; char c; if (flags.max_args != -1 && arg_count == flags.max_args) return NULL; size = 0; capacity = 8; buf = malloc(sizeof(char) * capacity); 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; static int read = 0; while (arg = read_next(file, read), true) { if (*size == *capacity) { *capacity *= 2; *args = realloc(*args, sizeof(char*) * *capacity); } (*args)[(*size)++] = arg; read++; if (arg == NULL) break; } } COMMAND(xargs) { int start, arg_start, arg_on_stack_count; int size, capacity, i; char* command; char** args; flags.null_seperated = false; flags.ignore_empty = false; flags.print_command = false; flags.print_command = false; flags.max_args = -1; flags.file = stdin; start = parse_args(argc, argv, help, short_arg, NULL); if (start >= argc) { command = "echo"; } else { command = argv[start]; } arg_start = start + 1; if (arg_start >= argc) { arg_on_stack_count = 0; arg_start = argc - 1; } else { arg_on_stack_count = argc - arg_start; } size = arg_on_stack_count + 1; capacity = size + 8; 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 (i = 0; i < size - 1; i++) { fprintf(stderr, "%s ", args[i]); } fprintf(stderr, "\b\n"); } if (flags.prompt_command) { FILE* in; char c; fprintf(stderr, "Run command? "); fflush(stderr); in = get_tty_stream("r"); 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 (i = arg_on_stack_count + 1; i < size - 1; i++) { free(args[i]); } fclose(flags.file); return EXIT_SUCCESS; }