1 /* $OpenBSD: edit.c,v 1.7 2017/11/21 19:22:45 anton Exp $ */ 2 /* 3 * Copyright (c) 2017 Anton Lindqvist <anton@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/wait.h> 19 20 #include <err.h> 21 #include <errno.h> 22 #include <poll.h> 23 #include <signal.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <termios.h> 28 #include <unistd.h> 29 #include <util.h> 30 31 #define WRTIM 50 /* input write timeout */ 32 #define PRTIM 5000 /* prompt read timeout */ 33 34 static size_t findprompt(const char *, const char *); 35 static void sighandler(int); 36 static void __dead usage(void); 37 38 static volatile sig_atomic_t gotsig; 39 40 int 41 main(int argc, char *argv[]) 42 { 43 const char *linefeed = "\003\004\n\r"; 44 const char *prompt = ""; 45 char in[BUFSIZ], out[BUFSIZ]; 46 struct pollfd pfd; 47 struct winsize ws; 48 pid_t pid; 49 ssize_t n; 50 size_t nin, nprompt, nread, nwrite; 51 int c, nready, ptyfd, readprompt, ret, status, timeout; 52 53 while ((c = getopt(argc, argv, "p:")) != -1) { 54 switch (c) { 55 case 'p': 56 prompt = optarg; 57 break; 58 default: 59 usage(); 60 } 61 } 62 argc -= optind; 63 argv += optind; 64 if (argc == 0 || strlen(prompt) == 0) 65 usage(); 66 67 nin = 0; 68 for (;;) { 69 if (nin == sizeof(in)) 70 errx(1, "input buffer too small"); 71 72 n = read(0, in + nin, sizeof(in) - nin); 73 if (n == -1) 74 err(1, "read"); 75 if (n == 0) 76 break; 77 78 nin += n; 79 } 80 81 if (signal(SIGCHLD, sighandler) == SIG_ERR) 82 err(1, "signal: SIGCHLD"); 83 if (signal(SIGINT, sighandler) == SIG_ERR) 84 err(1, "signal: SIGINT"); 85 86 memset(&ws, 0, sizeof(ws)); 87 ws.ws_col = 80, 88 ws.ws_row = 24; 89 90 pid = forkpty(&ptyfd, NULL, NULL, &ws); 91 if (pid == -1) 92 err(1, "forkpty"); 93 if (pid == 0) { 94 execvp(*argv, argv); 95 err(1, "%s", *argv); 96 } 97 98 nprompt = nread = nwrite = ret = 0; 99 readprompt = 1; 100 while (!gotsig) { 101 pfd.fd = ptyfd; 102 if (!readprompt && nwrite < nin) 103 pfd.events = POLLOUT; 104 else 105 pfd.events = POLLIN; 106 timeout = readprompt ? PRTIM : WRTIM; 107 nready = poll(&pfd, 1, timeout); 108 if (nready == -1) { 109 if (errno == EINTR) 110 continue; 111 err(1, "poll"); 112 } 113 if (nready == 0) { 114 if (timeout == PRTIM) 115 ret = 1; 116 break; 117 } 118 if (pfd.revents & (POLLERR | POLLNVAL)) 119 errc(1, EBADF, NULL); 120 121 if (pfd.revents & (POLLIN | POLLHUP)) { 122 if (nread == sizeof(out)) 123 errx(1, "output buffer too small"); 124 125 n = read(ptyfd, out + nread, sizeof(out) - 1 - nread); 126 if (n == -1) 127 err(1, "read"); 128 nread += n; 129 out[nread] = '\0'; 130 131 if (readprompt && 132 (n = findprompt(out + nprompt, prompt)) > 0) { 133 nprompt += n; 134 readprompt = 0; 135 } 136 } else if (pfd.revents & POLLOUT) { 137 if (strchr(linefeed, in[nwrite]) != NULL) 138 readprompt = 1; 139 140 n = write(ptyfd, in + nwrite, 1); 141 if (n == -1) 142 err(1, "write"); 143 nwrite += n; 144 } 145 } 146 close(ptyfd); 147 while (waitpid(pid, &status, 0) == -1) 148 if (errno != EINTR) 149 err(1, "waitpid"); 150 if (WIFSIGNALED(status) && WTERMSIG(status) != SIGHUP) { 151 warnx("%s: terminated by signal %d", *argv, WTERMSIG(status)); 152 ret = 128 + WTERMSIG(status); 153 } 154 155 printf("%.*s", (int)nread, out); 156 157 return ret; 158 } 159 160 static size_t 161 findprompt(const char *str, const char *prompt) 162 { 163 char *cp; 164 size_t len; 165 166 if ((cp = strstr(str, prompt)) == NULL) 167 return 0; 168 len = strlen(prompt); 169 170 return (cp - str) + len; 171 } 172 173 static void 174 sighandler(int sig) 175 { 176 gotsig = sig; 177 } 178 179 static void __dead 180 usage(void) 181 { 182 fprintf(stderr, "usage: edit -p prompt command [args]\n"); 183 exit(1); 184 } 185