282 lines
4.1 KiB
C
282 lines
4.1 KiB
C
#define XARGS
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <sys/wait.h>
|
|
#include <ctype.h>
|
|
#include "config.h"
|
|
|
|
char *I_flag;
|
|
char t_flag;
|
|
char r_flag;
|
|
char nl_flag;
|
|
int n_flag;
|
|
int s_flag;
|
|
int P_flag;
|
|
int P_flag2;
|
|
|
|
int args;
|
|
char *cmd[NARGS + 1];
|
|
|
|
enum {
|
|
NONE,
|
|
QUOTE
|
|
};
|
|
|
|
enum {
|
|
NORMAL,
|
|
CEOF,
|
|
ERROR,
|
|
I_FLAG
|
|
};
|
|
|
|
void clear_cmd(void) {
|
|
for (int i = 0; i < args; i++)
|
|
if (cmd[i] != NULL)
|
|
free(cmd[i]);
|
|
|
|
args = 0;
|
|
}
|
|
|
|
int add_arg(const char *str, int chars, int flag) {
|
|
if (args >= NARGS)
|
|
return ERROR;
|
|
|
|
else if (n_flag > 0 && args > n_flag)
|
|
return ERROR;
|
|
|
|
else if (s_flag > 0 && chars > s_flag)
|
|
return ERROR;
|
|
|
|
if (!flag && I_flag) {
|
|
for (int i = 0; i < args; i++)
|
|
if (!strcmp(cmd[i], I_flag))
|
|
cmd[i] = strdup(str);
|
|
|
|
return I_FLAG;
|
|
}
|
|
|
|
cmd[args] = strdup(str);
|
|
args++;
|
|
|
|
return NORMAL;
|
|
}
|
|
|
|
int is_correct(char c) {
|
|
if (nl_flag)
|
|
return c == '\0';
|
|
|
|
else
|
|
return isspace(c);
|
|
}
|
|
|
|
int stdin_read(void) {
|
|
char arg[ARG_SIZE + 1];
|
|
|
|
/* Word start */
|
|
char *p = arg;
|
|
|
|
int chars = 0;
|
|
int args_passed = 0;
|
|
|
|
int ret = NORMAL;
|
|
char flag = NONE;
|
|
while (1) {
|
|
int c = getchar();
|
|
if (c == EOF) {
|
|
if (flag == QUOTE) {
|
|
fprintf(stderr, "xargs: unterminated double quote\n");
|
|
ret = ERROR;
|
|
}
|
|
|
|
if (p != arg)
|
|
goto PUSH;
|
|
|
|
ret = CEOF;
|
|
break;
|
|
}
|
|
|
|
/* Place str */
|
|
else if ((flag == NONE && is_correct(c) && strlen(arg) > 0) || p == arg + sizeof(arg) - 1) {
|
|
PUSH:
|
|
|
|
*p = '\0';
|
|
p = arg;
|
|
|
|
int r = add_arg(arg, chars, 0);
|
|
if (r == ERROR)
|
|
break;
|
|
|
|
else if (r == I_FLAG)
|
|
return NORMAL;
|
|
|
|
args_passed++;
|
|
}
|
|
|
|
/* Add char to str */
|
|
else {
|
|
if (c == '"' || c == '\'') {
|
|
if (flag == QUOTE)
|
|
flag = NONE;
|
|
|
|
else
|
|
flag = QUOTE;
|
|
|
|
continue;
|
|
}
|
|
|
|
*p = c;
|
|
if (p + 1 == arg + sizeof(arg))
|
|
continue;
|
|
|
|
p++;
|
|
chars++;
|
|
}
|
|
}
|
|
|
|
if (args_passed == 0)
|
|
ret = CEOF;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Run */
|
|
void sig_handler(int sig) {
|
|
if (sig == SIGUSR1)
|
|
if (P_flag2)
|
|
P_flag2--;
|
|
}
|
|
|
|
int run(void) {
|
|
if (t_flag) {
|
|
for (int i = 0; i < args; i++)
|
|
fprintf(stderr, "%s ", cmd[i]);
|
|
|
|
fputc('\n', stderr);
|
|
}
|
|
|
|
pid_t pid;
|
|
if ((pid = fork()) == 0) {
|
|
execvp(cmd[0], cmd);
|
|
fprintf(stderr, "xargs: %s\n", strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
int status = 0;
|
|
waitpid(pid, &status, 0);
|
|
|
|
if (status == 255)
|
|
status = 124;
|
|
|
|
else if (status >= 0x180)
|
|
status = 125;
|
|
|
|
return status;
|
|
}
|
|
|
|
int spawn(void) {
|
|
if (P_flag == 0)
|
|
return run();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void check(int val) {
|
|
if (val <= 0) {
|
|
fprintf(stderr, "xargs: invalid number: %s\n", optarg);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
signal(SIGUSR1, sig_handler);
|
|
|
|
int opt;
|
|
while ((opt = getopt(argc, argv, "tn:s:rP:0I:")) != -1) {
|
|
switch (opt) {
|
|
case 't':
|
|
t_flag = 1;
|
|
break;
|
|
|
|
case 'n':
|
|
n_flag = atoi(optarg);
|
|
check(n_flag);
|
|
break;
|
|
|
|
case 's':
|
|
s_flag = atoi(optarg);
|
|
check(s_flag);
|
|
break;
|
|
|
|
case 'r':
|
|
r_flag = 1;
|
|
break;
|
|
|
|
case 'P':
|
|
P_flag = atoi(optarg);
|
|
check(P_flag);
|
|
break;
|
|
|
|
case '0':
|
|
nl_flag = 1;
|
|
break;
|
|
|
|
case 'I':
|
|
I_flag = optarg;
|
|
break;
|
|
|
|
default:
|
|
printf("xargs [tnsrP0I] [cmd [arg1] [arg2...]\n\t-t Print the command before start\n\t-n Pass no more than N args\n\t-r Don't run command if input is empty\n\t-s Pass command line of no more than N bytes\n\t-P Run up to N PROGs in parallel\n\t-0 NUL terminated input\n\t-I STR Replace STR within PROG ARGS with input line\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
argv += optind;
|
|
argc -= optind;
|
|
|
|
int ret = 0;
|
|
while (1) {
|
|
|
|
/* Arg */
|
|
if (argc) {
|
|
for (int i = 0; i < argc; i++)
|
|
if (add_arg(argv[i], 0, 1))
|
|
break;
|
|
}
|
|
|
|
else
|
|
add_arg(ECHO_CMD, 0, 1);
|
|
|
|
/* Stdin */
|
|
int stdin_stat = stdin_read();
|
|
|
|
/* Check NULL */
|
|
for (int i = 0; i < args; i++) {
|
|
if (cmd[i] == NULL) {
|
|
fprintf(stderr, "xargs: strdup failed\n");
|
|
clear_cmd();
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Run */
|
|
if (stdin_stat == CEOF && I_flag == NULL) {
|
|
if (!r_flag)
|
|
ret = spawn();
|
|
}
|
|
|
|
else if (stdin_stat == NORMAL)
|
|
ret = spawn();
|
|
|
|
/* Clear */
|
|
clear_cmd();
|
|
if (stdin_stat)
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|