xref: /openbsd-src/regress/bin/ksh/edit/edit.c (revision 5f46c18318b86ef0893e9ea8278cd57039cf2ec9)
1*5f46c183Santon /* $OpenBSD: edit.c,v 1.8 2021/07/10 07:10:31 anton Exp $ */
282270653Santon /*
382270653Santon  * Copyright (c) 2017 Anton Lindqvist <anton@openbsd.org>
482270653Santon  *
582270653Santon  * Permission to use, copy, modify, and distribute this software for any
682270653Santon  * purpose with or without fee is hereby granted, provided that the above
782270653Santon  * copyright notice and this permission notice appear in all copies.
882270653Santon  *
982270653Santon  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1082270653Santon  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1182270653Santon  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1282270653Santon  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1382270653Santon  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1482270653Santon  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1582270653Santon  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1682270653Santon  */
1782270653Santon 
18cf14a182Santon #include <sys/wait.h>
19cf14a182Santon 
2082270653Santon #include <err.h>
2182270653Santon #include <errno.h>
2282270653Santon #include <poll.h>
2382270653Santon #include <signal.h>
2482270653Santon #include <stdio.h>
2578f99cd3Santon #include <stdlib.h>
2682270653Santon #include <string.h>
2782270653Santon #include <termios.h>
2882270653Santon #include <unistd.h>
2982270653Santon #include <util.h>
3082270653Santon 
3190e71d7fSanton #define	WRTIM	50	/* input write timeout */
3290e71d7fSanton #define	PRTIM	5000	/* prompt read timeout */
3390e71d7fSanton 
3490e71d7fSanton static size_t		findprompt(const char *, const char *);
352a676cd6Santon static void		sighandler(int);
3678f99cd3Santon static void __dead	usage(void);
3782270653Santon 
382a676cd6Santon static volatile sig_atomic_t	gotsig;
3982270653Santon 
4082270653Santon int
main(int argc,char * argv[])4178f99cd3Santon main(int argc, char *argv[])
4282270653Santon {
4390e71d7fSanton 	const char	*linefeed = "\003\004\n\r";
4490e71d7fSanton 	const char	*prompt = "";
4582270653Santon 	char		 in[BUFSIZ], out[BUFSIZ];
4682270653Santon 	struct pollfd	 pfd;
4782270653Santon 	struct winsize	 ws;
4882270653Santon 	pid_t		 pid;
4982270653Santon 	ssize_t		 n;
5090e71d7fSanton 	size_t		 nin, nprompt, nread, nwrite;
51cf14a182Santon 	int		 c, nready, ptyfd, readprompt, ret, status, timeout;
5282270653Santon 
5390e71d7fSanton 	while ((c = getopt(argc, argv, "p:")) != -1) {
5490e71d7fSanton 		switch (c) {
5590e71d7fSanton 		case 'p':
5690e71d7fSanton 			prompt = optarg;
5790e71d7fSanton 			break;
5890e71d7fSanton 		default:
5990e71d7fSanton 			usage();
6090e71d7fSanton 		}
6190e71d7fSanton 	}
6290e71d7fSanton 	argc -= optind;
6390e71d7fSanton 	argv += optind;
6490e71d7fSanton 	if (argc == 0 || strlen(prompt) == 0)
6578f99cd3Santon 		usage();
6678f99cd3Santon 
6782270653Santon 	nin = 0;
6882270653Santon 	for (;;) {
6982270653Santon 		if (nin == sizeof(in))
7082270653Santon 			errx(1, "input buffer too small");
7182270653Santon 
7290e71d7fSanton 		n = read(0, in + nin, sizeof(in) - nin);
7382270653Santon 		if (n == -1)
7482270653Santon 			err(1, "read");
7582270653Santon 		if (n == 0)
7682270653Santon 			break;
7782270653Santon 
7882270653Santon 		nin += n;
7982270653Santon 	}
8082270653Santon 
812a676cd6Santon 	if (signal(SIGCHLD, sighandler) == SIG_ERR)
822a676cd6Santon 		err(1, "signal: SIGCHLD");
832a676cd6Santon 	if (signal(SIGINT, sighandler) == SIG_ERR)
842a676cd6Santon 		err(1, "signal: SIGINT");
8582270653Santon 
8682270653Santon 	memset(&ws, 0, sizeof(ws));
87*5f46c183Santon 	ws.ws_col = 80;
8882270653Santon 	ws.ws_row = 24;
8982270653Santon 
9082270653Santon 	pid = forkpty(&ptyfd, NULL, NULL, &ws);
9182270653Santon 	if (pid == -1)
9282270653Santon 		err(1, "forkpty");
9382270653Santon 	if (pid == 0) {
9490e71d7fSanton 		execvp(*argv, argv);
9590e71d7fSanton 		err(1, "%s", *argv);
9682270653Santon 	}
9782270653Santon 
9890e71d7fSanton 	nprompt = nread = nwrite = ret = 0;
9990e71d7fSanton 	readprompt = 1;
1002a676cd6Santon 	while (!gotsig) {
10190e71d7fSanton 		pfd.fd = ptyfd;
10290e71d7fSanton 		if (!readprompt && nwrite < nin)
10390e71d7fSanton 			pfd.events = POLLOUT;
10490e71d7fSanton 		else
10590e71d7fSanton 			pfd.events = POLLIN;
10690e71d7fSanton 		timeout = readprompt ? PRTIM : WRTIM;
10790e71d7fSanton 		nready = poll(&pfd, 1, timeout);
10882270653Santon 		if (nready == -1) {
10982270653Santon 			if (errno == EINTR)
11082270653Santon 				continue;
11182270653Santon 			err(1, "poll");
11282270653Santon 		}
11390e71d7fSanton 		if (nready == 0) {
114*5f46c183Santon 			if (timeout == PRTIM) {
115*5f46c183Santon 				warnx("timeout waiting from prompt");
11690e71d7fSanton 				ret = 1;
117*5f46c183Santon 			}
11890e71d7fSanton 			break;
11990e71d7fSanton 		}
12082270653Santon 		if (pfd.revents & (POLLERR | POLLNVAL))
12182270653Santon 			errc(1, EBADF, NULL);
12282270653Santon 
12382270653Santon 		if (pfd.revents & (POLLIN | POLLHUP)) {
12482270653Santon 			if (nread == sizeof(out))
12582270653Santon 				errx(1, "output buffer too small");
12682270653Santon 
12790e71d7fSanton 			n = read(ptyfd, out + nread, sizeof(out) - 1 - nread);
12882270653Santon 			if (n == -1)
12982270653Santon 				err(1, "read");
13082270653Santon 			nread += n;
13190e71d7fSanton 			out[nread] = '\0';
13290e71d7fSanton 
13390e71d7fSanton 			if (readprompt &&
13490e71d7fSanton 			    (n = findprompt(out + nprompt, prompt)) > 0) {
13590e71d7fSanton 				nprompt += n;
13690e71d7fSanton 				readprompt = 0;
13790e71d7fSanton 			}
13882270653Santon 		} else if (pfd.revents & POLLOUT) {
13990e71d7fSanton 			if (strchr(linefeed, in[nwrite]) != NULL)
14090e71d7fSanton 				readprompt = 1;
14182270653Santon 
14290e71d7fSanton 			n = write(ptyfd, in + nwrite, 1);
14382270653Santon 			if (n == -1)
14482270653Santon 				err(1, "write");
14582270653Santon 			nwrite += n;
14682270653Santon 		}
14782270653Santon 	}
14882270653Santon 	close(ptyfd);
149cf14a182Santon 	while (waitpid(pid, &status, 0) == -1)
150cf14a182Santon 		if (errno != EINTR)
151cf14a182Santon 			err(1, "waitpid");
152cf14a182Santon 	if (WIFSIGNALED(status) && WTERMSIG(status) != SIGHUP) {
153cf14a182Santon 		warnx("%s: terminated by signal %d", *argv, WTERMSIG(status));
154cf14a182Santon 		ret = 128 + WTERMSIG(status);
155cf14a182Santon 	}
15682270653Santon 
15790e71d7fSanton 	printf("%.*s", (int)nread, out);
15882270653Santon 
15990e71d7fSanton 	return ret;
16090e71d7fSanton }
16190e71d7fSanton 
16290e71d7fSanton static size_t
findprompt(const char * str,const char * prompt)16390e71d7fSanton findprompt(const char *str, const char *prompt)
16490e71d7fSanton {
16590e71d7fSanton 	char	*cp;
16690e71d7fSanton 	size_t	 len;
16790e71d7fSanton 
16890e71d7fSanton 	if ((cp = strstr(str, prompt)) == NULL)
16982270653Santon 		return 0;
17090e71d7fSanton 	len = strlen(prompt);
17190e71d7fSanton 
17290e71d7fSanton 	return (cp - str) + len;
17382270653Santon }
17482270653Santon 
17582270653Santon static void
sighandler(int sig)1762a676cd6Santon sighandler(int sig)
17782270653Santon {
1782a676cd6Santon 	gotsig = sig;
17982270653Santon }
18078f99cd3Santon 
18178f99cd3Santon static void __dead
usage(void)18278f99cd3Santon usage(void)
18378f99cd3Santon {
18490e71d7fSanton 	fprintf(stderr, "usage: edit -p prompt command [args]\n");
18578f99cd3Santon 	exit(1);
18678f99cd3Santon }
187