pipeto.c (3171B) download
1#include "arg.h"
2
3#include <errno.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <sys/wait.h>
8#include <unistd.h>
9
10#define USAGE "Usage: %s [-h] [-d delimiter] <inputcmd> {delimiter} <outputcmd>\n"
11
12#define NORETURN __attribute__((noreturn))
13#define MAX(a, b) ((a) > (b) ? (a) : (b))
14
15
16static char *argv0 = NULL;
17static const char *delimiter = "+";
18
19static void die(const char *message) {
20 fprintf(stderr, "error: %s: %s\n", message, strerror(errno));
21 exit(EXIT_FAILURE);
22}
23
24static void help(void) {
25 fprintf(stderr,
26 USAGE "Pipe output of command to another without a shell.\n"
27 "\n"
28 "Options:\n"
29 " -h Display this help message and exit\n"
30 " -d delimeter Split commands by demiliter (default: %s)\n"
31 "\n"
32 "Examples:\n"
33 " pipeto xbps-query -l + wc -l\n"
34 " pipeto find -name 'myfile' + xargs rm\n",
35 argv0, delimiter);
36}
37
38static void usage(int exitcode) {
39 fprintf(stderr, USAGE, argv0);
40 exit(exitcode);
41}
42
43static void runcommand(char **inputcmd, char **outputcmd) {
44 int pipefd[2]; // pipefd[0] is for reading, pipefd[1] is for writing
45 pid_t pid1, pid2;
46
47 if (pipe(pipefd) == -1) {
48 perror("pipe");
49 exit(EXIT_FAILURE);
50 }
51
52 // First child process to run inputcmd
53 if ((pid1 = fork()) < 0) {
54 perror("fork");
55 exit(EXIT_FAILURE);
56 }
57
58 if (pid1 == 0) {
59 // Child process 1: will exec inputcmd
60 close(pipefd[0]); // Close unused read end
61 dup2(pipefd[1], STDOUT_FILENO); // Redirect stdout to pipe write end
62 close(pipefd[1]); // Close write end after dup
63
64 execvp(inputcmd[0], inputcmd); // Replace child process with inputcmd
65 die("unable to execute input command");
66 }
67
68 // Second child process to run outputcmd
69 if ((pid2 = fork()) < 0) {
70 perror("fork");
71 exit(EXIT_FAILURE);
72 }
73
74 if (pid2 == 0) {
75 // Child process 2: will exec outputcmd
76 close(pipefd[1]); // Close unused write end
77 dup2(pipefd[0], STDIN_FILENO); // Redirect stdin to pipe read end
78 close(pipefd[0]); // Close read end after dup
79
80 execvp(outputcmd[0], outputcmd); // Replace child process with outputcmd
81 die("unable to execute output command");
82 }
83
84 // Parent process
85 close(pipefd[0]); // Close both ends of the pipe in the parent
86 close(pipefd[1]);
87
88 // Wait for both children to finish
89 waitpid(pid1, NULL, 0);
90 waitpid(pid2, NULL, 0);
91}
92
93int main(int argc, char *argv[]) {
94 argv0 = argv[0];
95 ARGBEGIN
96 switch (OPT) {
97 case 'h':
98 help();
99 exit(0);
100 case 'd':
101 delimiter = EARGF(usage(1));
102 break;
103 default:
104 fprintf(stderr, "error: unknown option '-%c'\n", OPT);
105 usage(1);
106 }
107 ARGEND;
108
109 if (argc == 0) {
110 fprintf(stderr, "error: missing command\n");
111 usage(1);
112 }
113
114 char **outputcmd = NULL;
115
116 for (int i = 0; i < argc; i++) {
117 if (!strcmp(argv[i], delimiter)) {
118 if (outputcmd) {
119 fprintf(stderr, "error: delimiter occured more than once\n");
120 exit(EXIT_FAILURE);
121 }
122 argv[i] = NULL;
123 outputcmd = &argv[i + 1];
124 }
125 }
126
127 runcommand(argv, outputcmd);
128
129 return 0;
130}