textselect

Interactively select lines and pipe it to a command
Log | Files | Refs | README | LICENSE

pipeto.c (3171B)


      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 
     16 static char       *argv0     = NULL;
     17 static const char *delimiter = "+";
     18 
     19 static void die(const char *message) {
     20 	fprintf(stderr, "error: %s: %s\n", message, strerror(errno));
     21 	exit(EXIT_FAILURE);
     22 }
     23 
     24 static 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 
     38 static void usage(int exitcode) {
     39 	fprintf(stderr, USAGE, argv0);
     40 	exit(exitcode);
     41 }
     42 
     43 static 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 
     93 int 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 }