xref: /netbsd-src/external/bsd/less/dist/lesstest/pipeline.c (revision e4a6e799a67c2028562d75b4e61407b22434aa36)
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <fcntl.h>
5 #include "lesstest.h"
6 
7 #define RD 0
8 #define WR 1
9 
10 extern int verbose;
11 extern char* lt_screen;
12 extern char* lt_screen_opts;
13 static const int run_less = 1;
14 
15 // Make 2 specified file descriptors be stdin and stdout.
dup_std(int fd0,int fd1)16 static void dup_std(int fd0, int fd1) {
17 	if (fd0 >= 0) dup2(fd0, 0);
18 	if (fd1 >= 0) dup2(fd1, 1);
19 }
20 
basename(const char * path)21 static const char* basename(const char* path) {
22 	const char* slash = strrchr(path, '/');
23 	if (slash == NULL) return path;
24 	return slash+1;
25 }
26 
27 // Exec an instance of less in the current process.
become_child_less(char * less,int argc,char * const * argv,char * const * envp,const char * tempfile,int less_in_pipe[2],int screen_in_pipe[2])28 static void become_child_less(char* less, int argc, char* const* argv, char* const* envp, const char* tempfile, int less_in_pipe[2], int screen_in_pipe[2]) {
29 	if (verbose) fprintf(stderr, "less child: in %d, out %d, close %d,%d\n", less_in_pipe[RD], screen_in_pipe[WR], less_in_pipe[WR], screen_in_pipe[RD]);
30 	close(less_in_pipe[WR]);
31 	close(screen_in_pipe[RD]);
32 	dup_std(less_in_pipe[RD], screen_in_pipe[WR]);
33 
34 	char** less_argv = malloc(sizeof(char*) * (argc + 6));
35 	int less_argc = 0;
36 	less_argv[less_argc++] = less;
37 	less_argv[less_argc++] = "--tty";
38 	less_argv[less_argc++] = "/dev/stdin";
39 	while (--argc > 0) {
40 		char* arg = *++argv;
41 		less_argv[less_argc++] = (argc > 1 || tempfile == NULL) ? arg : (char*) tempfile;
42 	}
43 	less_argv[less_argc] = NULL;
44 	if (verbose) { print_strings("less argv", less_argv); print_strings("less envp", envp); }
45 	execve(less, less_argv, envp);
46 	fprintf(stderr, "cannot exec %s: %s\n", less, strerror(errno));
47 	exit(1);
48 }
49 
50 // Exec an instance of lt_screen in the current process.
become_child_screen(char * lt_screen,int screen_width,int screen_height,int screen_in_pipe[2],int screen_out_pipe[2])51 static void become_child_screen(char* lt_screen, int screen_width, int screen_height, int screen_in_pipe[2], int screen_out_pipe[2]) {
52 	if (verbose) fprintf(stderr, "screen child: in %d, out %d, close %d\n", screen_in_pipe[RD], screen_out_pipe[WR], screen_out_pipe[RD]);
53 	close(screen_out_pipe[RD]);
54 	dup_std(screen_in_pipe[RD], screen_out_pipe[WR]);
55 
56 	char* screen_argv[10];
57 	int screen_argc = 0;
58 	char sw[16];
59 	char sh[16];
60 	screen_argv[screen_argc++] = lt_screen;
61 	if (screen_width >= 0) {
62 		snprintf(sw, sizeof(sw), "%d", screen_width);
63 		screen_argv[screen_argc++] = "-w";
64 		screen_argv[screen_argc++] = sw;
65 	}
66 	if (screen_height >= 0) {
67 		snprintf(sh, sizeof(sh), "%d", screen_height);
68 		screen_argv[screen_argc++] = "-h";
69 		screen_argv[screen_argc++] = sh;
70 	}
71 	if (lt_screen_opts != NULL) {
72 		screen_argv[screen_argc++] = lt_screen_opts;
73 	}
74 	if (1)
75 		screen_argv[screen_argc++] = "-q";
76 	screen_argv[screen_argc] = NULL;
77 	if (verbose) print_strings("screen argv", screen_argv);
78 	char* const screen_envp[] = { NULL };
79 	execve(lt_screen, screen_argv, screen_envp);
80 	fprintf(stderr, "cannot exec %s: %s\n", lt_screen, strerror(errno));
81 	exit(1);
82 }
83 
84 // Create an empty LessPipeline.
new_pipeline(void)85 static LessPipeline* new_pipeline(void) {
86 	LessPipeline* pipeline = malloc(sizeof(LessPipeline));
87 	pipeline->less_in_pipe[RD] = pipeline->less_in_pipe[WR] = -1;
88 	pipeline->screen_in_pipe[RD] = pipeline->screen_in_pipe[WR] = -1;
89 	pipeline->screen_out_pipe[RD] = pipeline->screen_out_pipe[WR] = -1;
90 	pipeline->less_in = pipeline->screen_out = -1;
91 	pipeline->tempfile = NULL;
92 	pipeline->screen_pid = 0;
93 	pipeline->screen_width = pipeline->screen_height = 0;
94 	return pipeline;
95 }
96 
97 // Create a LessPipeline.
create_less_pipeline(char * const * argv,int argc,char * const * envp)98 LessPipeline* create_less_pipeline(char* const* argv, int argc, char* const* envp) {
99 	// If textfile contains a slash, create a temporary link from
100 	// the named text file to its basename, and run less on the link.
101 	LessPipeline* pipeline = new_pipeline();
102 	const char* textfile = argv[argc-1];
103 	const char* textbase = basename(textfile);
104 	if (textbase != textfile) {
105 		pipeline->tempfile = textbase;
106 		if (link(textfile, textbase) < 0) {
107 			fprintf(stderr, "cannot link %s to %s: %s\n", textfile, textbase, strerror(errno));
108 			return NULL;
109 		}
110 		textfile = textbase;
111 	}
112 	if (pipe(pipeline->screen_in_pipe) < 0) {
113 		destroy_less_pipeline(pipeline);
114 		return NULL;
115 	}
116 	const char* w = get_envp(envp, "COLUMNS");
117 	const char* h = get_envp(envp, "LINES");
118 	if (w != NULL) pipeline->screen_width = atoi(w);
119 	if (h != NULL) pipeline->screen_height = atoi(h);
120 	if (verbose) fprintf(stderr, "less out pipe %d,%d\n", pipeline->screen_in_pipe[0], pipeline->screen_in_pipe[1]);
121 	if (run_less) {
122 		if (pipe(pipeline->less_in_pipe) < 0) {
123 			destroy_less_pipeline(pipeline);
124 			return 0;
125 		}
126 		if (verbose) fprintf(stderr, "less in pipe %d,%d\n", pipeline->less_in_pipe[RD], pipeline->less_in_pipe[WR]);
127 		char* less = argv[0];
128 		if (verbose) fprintf(stderr, "testing %s on %s\n", less, textfile);
129 		pipeline->less_pid = fork();
130 		if (pipeline->less_pid < 0) {
131 			destroy_less_pipeline(pipeline);
132 			return NULL;
133 		}
134 		if (!pipeline->less_pid)
135 			become_child_less(less, argc, argv, envp, pipeline->tempfile, pipeline->less_in_pipe, pipeline->screen_in_pipe);
136 		if (verbose) fprintf(stderr, "less child %ld\n", (long) pipeline->less_pid);
137 		close(pipeline->less_in_pipe[RD]); pipeline->less_in_pipe[RD] = -1;
138 		close(pipeline->screen_in_pipe[WR]); pipeline->screen_in_pipe[WR] = -1;
139 	}
140 	if (pipe(pipeline->screen_out_pipe) < 0) {
141 		destroy_less_pipeline(pipeline);
142 		return NULL;
143 	}
144 	if (verbose) fprintf(stderr, "screen out pipe %d,%d\n", pipeline->screen_out_pipe[RD], pipeline->screen_out_pipe[WR]);
145 	pipeline->screen_pid = fork();
146 	if (!pipeline->screen_pid) // child: lt_screen
147 		become_child_screen(lt_screen, pipeline->screen_width, pipeline->screen_height, pipeline->screen_in_pipe, pipeline->screen_out_pipe);
148 	if (verbose) fprintf(stderr, "screen child %ld\n", (long) pipeline->screen_pid);
149 	close(pipeline->screen_out_pipe[WR]); pipeline->screen_out_pipe[WR] = -1;
150 	close(pipeline->screen_in_pipe[RD]); pipeline->screen_in_pipe[RD] = -1;
151 
152 	pipeline->less_in = run_less ? pipeline->less_in_pipe[WR] : pipeline->screen_in_pipe[WR];
153 	pipeline->screen_out = pipeline->screen_out_pipe[RD];
154 	if (verbose) fprintf(stderr, "less in %d, screen out %d, pid %ld\n", pipeline->less_in, pipeline->screen_out, (long) pipeline->screen_pid);
155 	return pipeline;
156 }
157 
destroy_less_pipeline(LessPipeline * pipeline)158 void destroy_less_pipeline(LessPipeline* pipeline) {
159 	close(pipeline->less_in);
160 	close(pipeline->screen_out);
161 	close(pipeline->less_in_pipe[RD]); close(pipeline->less_in_pipe[WR]);
162 	close(pipeline->screen_in_pipe[RD]); close(pipeline->screen_in_pipe[WR]);
163 	close(pipeline->screen_out_pipe[RD]); close(pipeline->screen_out_pipe[WR]);
164 	if (pipeline->tempfile != NULL)
165 		unlink(pipeline->tempfile);
166 	free(pipeline);
167 }
168