micro-utils/src/findutils/xargs/xargs.c

319 lines
4.9 KiB
C
Raw Normal View History

2023-11-29 11:05:59 +00:00
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
2024-04-06 15:34:14 +00:00
#include <signal.h>
2023-11-29 11:05:59 +00:00
#include <sys/wait.h>
2024-05-27 10:29:18 +00:00
#include <limits.h>
2024-04-06 15:34:14 +00:00
#include <ctype.h>
2024-05-27 10:29:18 +00:00
2024-05-31 12:52:38 +00:00
#ifndef ARG_MAX
#define ARG_MAX 10000
#endif
2024-05-27 10:29:18 +00:00
int args;
char *cmd[ARG_MAX + 1];
2023-11-29 11:05:59 +00:00
2024-04-06 15:34:14 +00:00
char *I_flag;
2024-03-31 13:45:49 +00:00
char t_flag;
char r_flag;
2024-04-06 15:34:14 +00:00
char nl_flag;
2023-12-06 15:26:26 +00:00
int n_flag;
2024-06-19 09:11:48 +00:00
size_t s_flag;
2024-03-31 13:45:49 +00:00
2024-04-06 15:34:14 +00:00
enum {
NORMAL,
CEOF,
ERROR,
I_FLAG
};
2024-05-27 10:29:18 +00:00
enum {
NONE,
QUOTE
};
2023-12-06 15:26:26 +00:00
void clear_cmd(void) {
2024-06-19 09:11:48 +00:00
for (int i = 0; i < args; i++) {
if (cmd[i] != NULL) {
2023-12-06 15:26:26 +00:00
free(cmd[i]);
2024-06-19 09:11:48 +00:00
cmd[i] = NULL;
}
}
2023-11-29 11:05:59 +00:00
2023-12-06 15:26:26 +00:00
args = 0;
2023-11-29 11:05:59 +00:00
}
2024-06-19 09:11:48 +00:00
int Iflag_push(const char *str, char **arg) {
size_t arg_len = strlen(*arg);
size_t str_len = strlen(str);
size_t Iflag_len = strlen(I_flag);
char *buf = malloc(arg_len - Iflag_len + str_len + 1);
if (buf == NULL) {
fprintf(stderr, "xargs: malloc failed\n");
return 1;
}
strcpy(buf, *arg);
/* Replace */
char *ptr = strstr(buf, I_flag);
memmove(ptr + str_len, ptr + Iflag_len, strlen(ptr + Iflag_len) + 1);
strncpy(ptr, str, str_len);
free(*arg);
*arg = buf;
return 0;
}
int add_arg(const char *str, size_t chars, int flag) {
2024-05-27 10:29:18 +00:00
if (args >= ARG_MAX)
2024-04-06 15:34:14 +00:00
return ERROR;
2023-12-06 15:26:26 +00:00
2024-05-27 10:29:18 +00:00
else if (n_flag > 0 && args > n_flag && I_flag == NULL)
2024-04-06 15:34:14 +00:00
return ERROR;
2023-12-06 15:26:26 +00:00
2024-01-15 19:26:18 +00:00
else if (s_flag > 0 && chars > s_flag)
2024-04-06 15:34:14 +00:00
return ERROR;
2024-06-19 09:11:48 +00:00
if (!flag && I_flag) {
for (int i = 0; i < args; i++) {
while (1) {
if (strstr(cmd[i], I_flag)) {
if (Iflag_push(str, &cmd[i]))
return ERROR;
}
else
break;
}
}
2024-04-06 15:34:14 +00:00
return I_FLAG;
}
2024-01-15 19:26:18 +00:00
2023-12-06 15:26:26 +00:00
cmd[args] = strdup(str);
args++;
2024-04-06 15:34:14 +00:00
return NORMAL;
}
int is_correct(char c) {
if (nl_flag)
return c == '\0';
else
return isspace(c);
2023-12-06 15:26:26 +00:00
}
2024-05-27 10:29:18 +00:00
int xargs(void) {
size_t arg_size = 0;
char *arg = malloc(1);
if (arg == NULL) {
fprintf(stderr, "xargs: malloc failed\n");
return ERROR;
}
2023-12-06 15:26:26 +00:00
2024-05-27 10:29:18 +00:00
size_t index = 0;
int flag = NONE;
int ret = NORMAL;
2024-06-19 09:11:48 +00:00
size_t chars = 0;
2023-12-06 19:04:40 +00:00
int args_passed = 0;
2024-03-31 13:45:49 +00:00
2023-12-06 19:04:40 +00:00
while (1) {
int c = getchar();
2023-12-07 13:06:30 +00:00
if (c == EOF) {
2024-03-31 13:45:49 +00:00
if (flag == QUOTE) {
2024-05-27 10:29:18 +00:00
fprintf(stderr, "xargs: unterminated quote\n");
2024-04-06 15:34:14 +00:00
ret = ERROR;
2024-03-31 13:45:49 +00:00
}
2024-04-06 15:34:14 +00:00
ret = CEOF;
2023-12-07 13:06:30 +00:00
break;
}
2024-06-18 12:35:50 +00:00
if (flag == NONE && is_correct(c) && index > 0) {
arg[index] = '\0';
2024-06-21 22:06:32 +00:00
2024-06-18 12:35:50 +00:00
int r = add_arg(arg, chars, 0);
2024-06-19 09:11:48 +00:00
if (r == ERROR) {
ret = ERROR;
2024-02-14 15:48:01 +00:00
break;
2024-06-19 09:11:48 +00:00
}
2023-12-06 15:26:26 +00:00
2024-05-27 10:29:18 +00:00
else if (r == I_FLAG) {
free(arg);
2024-04-06 15:34:14 +00:00
return NORMAL;
2024-05-27 10:29:18 +00:00
}
2024-04-06 15:34:14 +00:00
2024-05-27 10:29:18 +00:00
index = 0;
arg_size = 0;
2023-12-06 19:04:40 +00:00
args_passed++;
2024-05-27 10:29:18 +00:00
free(arg);
2024-06-21 22:06:32 +00:00
2024-05-27 10:29:18 +00:00
arg = malloc(1);
if (arg == NULL) {
fprintf(stderr, "xargs: malloc failed\n");
return ERROR;
}
2023-12-06 19:04:40 +00:00
}
2023-12-06 15:26:26 +00:00
2023-12-06 19:04:40 +00:00
else {
2024-03-31 13:45:49 +00:00
if (c == '"' || c == '\'') {
if (flag == QUOTE)
flag = NONE;
else
flag = QUOTE;
continue;
}
2023-12-17 13:45:13 +00:00
2024-06-22 15:55:30 +00:00
else if (is_correct(c) && flag == NONE)
2024-06-21 13:25:11 +00:00
continue;
2024-06-21 22:06:32 +00:00
char *tmp = realloc(arg, (++arg_size) + 1);
2024-06-17 09:43:35 +00:00
if (tmp == NULL) {
free(arg);
2024-05-27 10:29:18 +00:00
fprintf(stderr, "xargs: realloc failed\n");
return ERROR;
}
2024-06-18 12:35:50 +00:00
arg = tmp;
2024-06-21 22:06:32 +00:00
arg[index] = (char)c;
2024-05-27 10:29:18 +00:00
index++;
2024-01-15 19:26:18 +00:00
chars++;
2023-12-06 15:26:26 +00:00
}
2023-11-29 11:05:59 +00:00
}
2024-06-19 09:11:48 +00:00
if (args_passed == 0 && ret != ERROR)
2024-04-06 15:34:14 +00:00
ret = CEOF;
2023-12-06 15:26:26 +00:00
2024-05-27 10:29:18 +00:00
free(arg);
return ret;
2023-11-29 11:05:59 +00:00
}
2024-05-27 10:29:18 +00:00
int spawn(void) {
2023-11-29 11:05:59 +00:00
if (t_flag) {
2023-12-06 15:26:26 +00:00
for (int i = 0; i < args; i++)
2023-11-29 11:05:59 +00:00
fprintf(stderr, "%s ", cmd[i]);
fputc('\n', stderr);
}
pid_t pid;
if ((pid = fork()) == 0) {
2023-12-06 15:26:26 +00:00
execvp(cmd[0], cmd);
2024-06-19 09:11:48 +00:00
fprintf(stderr, "xargs: exec: %s\n", strerror(errno));
2023-11-29 11:05:59 +00:00
exit(1);
}
int status = 0;
waitpid(pid, &status, 0);
2024-03-31 13:45:49 +00:00
if (status == 255)
status = 124;
else if (status >= 0x180)
status = 125;
2023-11-29 11:05:59 +00:00
return status;
}
int main(int argc, char **argv) {
2024-06-19 09:11:48 +00:00
/* For -s flag */
char *p = NULL;
2023-11-29 11:05:59 +00:00
int opt;
2024-04-06 15:34:14 +00:00
while ((opt = getopt(argc, argv, "tn:s:rP:0I:")) != -1) {
2023-11-29 11:05:59 +00:00
switch (opt) {
case 't':
t_flag = 1;
break;
2023-12-06 15:26:26 +00:00
case 'n':
n_flag = atoi(optarg);
2024-06-19 09:11:48 +00:00
if (n_flag <= 0) {
fprintf(stderr, "xargs: -n: invalid number: %s\n", optarg);
return 1;
}
2023-12-06 15:26:26 +00:00
break;
2024-01-15 19:26:18 +00:00
case 's':
2024-06-19 09:11:48 +00:00
s_flag = strtoul(optarg, &p, 0);
if (s_flag <= 0 || *p) {
fprintf(stderr, "xargs: -s: invalid number: %zu%s\n", s_flag, (p) ? p : "");
return 1;
}
2024-01-15 19:27:41 +00:00
break;
2024-01-15 19:26:18 +00:00
2023-12-06 15:26:26 +00:00
case 'r':
r_flag = 1;
break;
2024-04-06 15:34:14 +00:00
case '0':
nl_flag = 1;
break;
case 'I':
I_flag = optarg;
break;
2023-11-29 11:05:59 +00:00
default:
2024-05-27 10:29:18 +00:00
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-0 NUL terminated input\n\t-I STR Replace STR within PROG ARGS with input line\n");
2023-11-29 11:05:59 +00:00
return 0;
}
}
argv += optind;
argc -= optind;
2023-12-06 15:26:26 +00:00
int ret = 0;
while (1) {
2024-01-15 19:29:43 +00:00
2023-12-06 15:26:26 +00:00
/* Arg */
if (argc) {
for (int i = 0; i < argc; i++)
2024-04-06 15:34:14 +00:00
if (add_arg(argv[i], 0, 1))
2023-12-06 15:26:26 +00:00
break;
}
2023-11-29 11:05:59 +00:00
2023-12-06 15:26:26 +00:00
else
2024-05-27 10:29:18 +00:00
add_arg("echo", 0, 1);
2023-12-06 15:26:26 +00:00
2024-05-27 10:29:18 +00:00
int stdin_stat = xargs();
2024-06-19 09:11:48 +00:00
if (stdin_stat == ERROR)
ret = 1;
2023-11-29 11:05:59 +00:00
2023-12-06 15:26:26 +00:00
/* Check NULL */
for (int i = 0; i < args; i++) {
if (cmd[i] == NULL) {
fprintf(stderr, "xargs: strdup failed\n");
clear_cmd();
return 1;
}
}
/* Run */
2024-04-06 15:34:14 +00:00
if (stdin_stat == CEOF && I_flag == NULL) {
2023-12-06 19:04:40 +00:00
if (!r_flag)
2024-03-31 13:45:49 +00:00
ret = spawn();
2023-12-06 19:04:40 +00:00
}
2024-04-06 15:34:14 +00:00
else if (stdin_stat == NORMAL)
2024-03-31 13:45:49 +00:00
ret = spawn();
2023-12-06 15:26:26 +00:00
clear_cmd();
if (stdin_stat)
break;
}
2023-11-29 11:05:59 +00:00
return ret;
}