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. 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 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. 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. 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. 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. 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 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