util/textselect

pipeto.c in master
Repositories | Summary | Log | Files | README.md | LICENSE

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}