From 1d8edebe0717e846945e0ba595451ebc489c7d8b Mon Sep 17 00:00:00 2001 From: Alessio Chiapperini Date: Tue, 1 Mar 2022 21:27:05 +0100 Subject: [PATCH] Refactor out execution code and implement -a flag Implements: https://todo.sr.ht/~spidernet/consume/1 --- README.md | 7 +-- consume.1 | 11 +++-- src/consume.c | 138 ++++++++++++++++++++++++++++++++++------------------------ 3 files changed, 92 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 0a7ba69..e6c213e 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ not have to be installed before use. ## Usage ``` -consume [-0ch] [-i file] [-v] [utility [argument ...]] +consume [-0ach] [-i file] [-v] [utility [argument ...]] ``` The **consume** program reads input from the file named by the `file` operand or from `stdin` if absent, and splits it when encountering the given delimiter (defaults to newline). It then invokes the `utility` repeatedly, feeding it the @@ -64,6 +64,7 @@ generated data as input. On success the given data is consumed (removed) from th The options are as follows: +* `-a` input items are fed as a command-line argument to `utility`. * `-0` input items are teminated by a null character instead of by newline. The GNU find -print0 option produces input suitable for this mode. * `-c` continue even if `utility` failed. @@ -79,11 +80,11 @@ $ curl -s https://dmf.unicatt.it/~della/pythoncourse18/commedia.txt | consume wc Find files named `core` in or below the directory `/tmp` and delete them. Before deleting each file the full command is printed to `stderr`: ```sh -$ find /tmp -name core -type f -print | consume -v /bin/rm -f +$ find /tmp -name core -type f -print | consume -a -v /bin/rm -f ``` Consume all lines delimited by `0` in biglistoflinks.txt that wget has downloaded successfully: ```sh -$ consume -0 -i biglistoflinks.txt wget -qc {} +$ consume -0 -a -i biglistoflinks.txt wget -qc {} ``` ### Static analysis diff --git a/consume.1 b/consume.1 index 429df62..b6ecd04 100644 --- a/consume.1 +++ b/consume.1 @@ -31,7 +31,7 @@ .Nd split input, feed it into the given utility and consume it if successful. .Sh SYNOPSIS .Nm -.Op Fl \&0ch +.Op Fl \&0ach .Op Fl i Ar file .Op Fl v .Op Ar utility Op Ar argument ... @@ -52,12 +52,15 @@ The options are as follows: .It Fl 0 Input items are teminated by a null character instead of by newline. The GNU find -print0 option produces input suitable for this mode. +.It Fl a +Input items are fed as a command-line argument to +.Ar utility. .It Fl c Continue even if .Ar utility failed. .It Fl h -Print usage information and exit +Print usage information and exit. .It Fl i Ar file Read items from .Ar file @@ -78,12 +81,12 @@ Find files named in or below the directory .Ar /tmp and delete them. Before deleting each file the full command is printed to stderr: -.Dl find /tmp -name core -type f -print | consume -v /bin/rm -f +.Dl find /tmp -name core -type f -print | consume -a -v /bin/rm -f .Pp Consume all lines delimited by .Sq 0 in biglistoflinks.txt that wget has downloaded successfully: -.Dl consume -0 -i biglistoflinks.txt wget -qc {} +.Dl consume -0 -a -i biglistoflinks.txt wget -qc {} .Sh SEE ALSO .Xr split 1 , .Xr tee 1 , diff --git a/src/consume.c b/src/consume.c index 851380f..28cf2cb 100644 --- a/src/consume.c +++ b/src/consume.c @@ -54,7 +54,7 @@ static FILE *tfile = 0; static void usage(void) { - (void)fprintf(stderr, "usage: consume [-0ch] [-i file] [-v]" + (void)fprintf(stderr, "usage: consume [-0ach] [-i file] [-v]" " [utility [argument ...]]\n"); exit(1); } @@ -93,9 +93,8 @@ readinput(char * restrict str, size_t size) } static int -consume(char *cmd[], int nargs) +execute(char *cmd[], int nargs, char *data) { - char buf[LINE_MAX]; int ipc[2]; char **args; size_t count; @@ -116,99 +115,124 @@ consume(char *cmd[], int nargs) args[arg] = cmd[arg]; } - while (readinput(buf, LINE_MAX) != 0) { - buf[strcspn(buf, "\n")] = 0; - args[arg] = buf; + if (aflag == 1) { + args[arg] = data; args[arg+1] = 0; + } - if (vflag) { - (void)fprintf(stderr, "Executing `%s ", cmd[0]); - for (arg = 1; arg < nargs; arg++) { - (void)fprintf(stderr, "%s ", cmd[arg]); - } - (void)fprintf(stderr, "%s`\n", buf); + if (vflag) { + (void)fprintf(stderr, "Executing `%s ", cmd[0]); + for (arg = 1; arg < nargs; arg++) { + (void)fprintf(stderr, "%s ", cmd[arg]); } + (void)fprintf(stderr, "%s`\n", data); + } + if (!aflag) { if (pipe(ipc) < 0) { ret = 1; perror("consume"); goto pipe_err; } + } - pid = fork(); - if (pid < 0) { - ret = 1; - perror("fork failed"); - break; - } else if (pid == 0) { + pid = fork(); + if (pid < 0) { + ret = 1; + perror("fork failed"); + goto fork_err; + } else if (pid == 0) { + if (!aflag) { (void)close(ipc[1]); if (ipc[0] != STDIN_FILENO) { if (dup2(ipc[0], STDIN_FILENO) != STDIN_FILENO) { ret = 1; perror("consume"); - break; + goto dup_err; } } - if (execvp(*cmd, cmd) < 0) { - ret = 1; - perror("consume"); - break; - } - } else { + } + + if (execvp(*args, args) < 0) { + ret = 1; + perror("consume"); + goto exec_err; + } + } else { + if (!aflag) { (void)close(ipc[0]); - count = strlen(buf); - if (write(ipc[1], buf, count) != (ssize_t)count) { + count = strlen(data); + if (write(ipc[1], data, count) != (ssize_t)count) { ret = 1; perror("consume"); - break; + goto write_err; } (void)close(ipc[1]); } + } - if (waitpid(pid, &status, 0) < 0) { - ret = 1; - perror("consume"); - break; - } + if (waitpid(pid, &status, 0) < 0) { + ret = 1; + perror("consume"); + goto wait_err; + } - if (WIFEXITED(status)) { - if (WEXITSTATUS(status) != 0) { - if (iflag) { - (void)fprintf(tfile, "%s%c", buf, - delimiter); - } - if (cflag == 0) { - ret = 1; - break; - } + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) != 0) { + if (iflag) { + (void)fprintf(tfile, "%s%c", data, + delimiter); } - } else if (WIFSIGNALED(status)) { - if (WTERMSIG(status) < SIGRTMAX) { - (void)fprintf(stderr, "%s terminated by " - "SIG%s\n", args[0], - strsignal(WTERMSIG(status))); - } else { - (void)fprintf(stderr, "%s terminated by " - "signal %d\n", args[0], - WTERMSIG(status)); - } - if (cflag == 0) { ret = 1; - break; } } + } else if (WIFSIGNALED(status)) { + if (WTERMSIG(status) < SIGRTMAX) { + (void)fprintf(stderr, "%s terminated by " + "SIG%s\n", args[0], + strsignal(WTERMSIG(status))); + } else { + (void)fprintf(stderr, "%s terminated by " + "signal %d\n", args[0], + WTERMSIG(status)); + } + + if (cflag == 0) { + ret = 1; + } } +wait_err: +write_err: +exec_err: +dup_err: +fork_err: pipe_err: -free(args); - + free(args); alloc_err: return (ret); } static int +consume(char *cmd[], int nargs) +{ + char buf[LINE_MAX]; + int ret; + + ret = 0; + while (readinput(buf, LINE_MAX) != 0) { + buf[strcspn(buf, "\n")] = 0; + if ((ret = execute(cmd, nargs, buf)) == 1) { + break; + } + } + + return (ret); +} + +static int consume_file(char *src, char *cmd[], int nargs) { char template[] = "tmp.XXXXXX"; -- 2.11.4.GIT