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