119304Speter/*-
219304Speter * Copyright (c) 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: cl_read.c,v 10.30 2012/07/12 18:28:58 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/select.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <errno.h>
2219304Speter#include <fcntl.h>
2319304Speter#include <signal.h>
2419304Speter#include <stdio.h>
2519304Speter#include <stdlib.h>
2619304Speter#include <string.h>
2719304Speter#include <termios.h>
2819304Speter#include <unistd.h>
2919304Speter
3019304Speter#include "../common/common.h"
3119304Speter#include "../ex/script.h"
3219304Speter#include "cl.h"
3319304Speter
34254225Speter/* Pollution by Solaris curses. */
35254225Speter#undef columns
36254225Speter#undef lines
37254225Speter
3819304Speterstatic input_t	cl_read __P((SCR *,
39254225Speter    u_int32_t, char *, size_t, int *, struct timeval *));
4019304Speterstatic int	cl_resize __P((SCR *, size_t, size_t));
4119304Speter
4219304Speter/*
4319304Speter * cl_event --
4419304Speter *	Return a single event.
4519304Speter *
4619304Speter * PUBLIC: int cl_event __P((SCR *, EVENT *, u_int32_t, int));
4719304Speter */
4819304Speterint
49254225Spetercl_event(SCR *sp, EVENT *evp, u_int32_t flags, int ms)
5019304Speter{
5119304Speter	struct timeval t, *tp;
5219304Speter	CL_PRIVATE *clp;
5319304Speter	size_t lines, columns;
54254225Speter	int changed, nr = 0;
55254225Speter	CHAR_T *wp;
56254225Speter	size_t wlen;
57254225Speter	int rc;
5819304Speter
5919304Speter	/*
6019304Speter	 * Queue signal based events.  We never clear SIGHUP or SIGTERM events,
6119304Speter	 * so that we just keep returning them until the editor dies.
6219304Speter	 */
6319304Speter	clp = CLP(sp);
6419304Speterretest:	if (LF_ISSET(EC_INTERRUPT) || F_ISSET(clp, CL_SIGINT)) {
6519304Speter		if (F_ISSET(clp, CL_SIGINT)) {
6619304Speter			F_CLR(clp, CL_SIGINT);
6719304Speter			evp->e_event = E_INTERRUPT;
6819304Speter		} else
6919304Speter			evp->e_event = E_TIMEOUT;
7019304Speter		return (0);
7119304Speter	}
7219304Speter	if (F_ISSET(clp, CL_SIGHUP | CL_SIGTERM | CL_SIGWINCH)) {
7319304Speter		if (F_ISSET(clp, CL_SIGHUP)) {
7419304Speter			evp->e_event = E_SIGHUP;
7519304Speter			return (0);
7619304Speter		}
7719304Speter		if (F_ISSET(clp, CL_SIGTERM)) {
7819304Speter			evp->e_event = E_SIGTERM;
7919304Speter			return (0);
8019304Speter		}
8119304Speter		if (F_ISSET(clp, CL_SIGWINCH)) {
8219304Speter			F_CLR(clp, CL_SIGWINCH);
8319304Speter			if (cl_ssize(sp, 1, &lines, &columns, &changed))
8419304Speter				return (1);
8519304Speter			if (changed) {
8619304Speter				(void)cl_resize(sp, lines, columns);
8719304Speter				evp->e_event = E_WRESIZE;
8819304Speter				return (0);
8919304Speter			}
9019304Speter			/* No real change, ignore the signal. */
9119304Speter		}
9219304Speter	}
9319304Speter
9419304Speter	/* Set timer. */
9519304Speter	if (ms == 0)
9619304Speter		tp = NULL;
9719304Speter	else {
9819304Speter		t.tv_sec = ms / 1000;
9919304Speter		t.tv_usec = (ms % 1000) * 1000;
10019304Speter		tp = &t;
10119304Speter	}
10219304Speter
10319304Speter	/* Read input characters. */
104254225Speterread:
10519304Speter	switch (cl_read(sp, LF_ISSET(EC_QUOTED | EC_RAW),
106254225Speter	    clp->ibuf + clp->skip, SIZE(clp->ibuf) - clp->skip, &nr, tp)) {
10719304Speter	case INP_OK:
108254225Speter		rc = INPUT2INT5(sp, clp->cw, clp->ibuf, nr + clp->skip,
109254225Speter				wp, wlen);
110254225Speter		evp->e_csp = wp;
111254225Speter		evp->e_len = wlen;
11219304Speter		evp->e_event = E_STRING;
113254225Speter		if (rc < 0) {
114254225Speter		    int n = -rc;
115254225Speter		    memmove(clp->ibuf, clp->ibuf + nr + clp->skip - n, n);
116254225Speter		    clp->skip = n;
117254225Speter		    if (wlen == 0)
118254225Speter			goto read;
119254225Speter		} else if (rc == 0)
120254225Speter		    clp->skip = 0;
121254225Speter		else
122254225Speter		    msgq(sp, M_ERR, "323|Invalid input. Truncated.");
12319304Speter		break;
12419304Speter	case INP_EOF:
12519304Speter		evp->e_event = E_EOF;
12619304Speter		break;
12719304Speter	case INP_ERR:
12819304Speter		evp->e_event = E_ERR;
12919304Speter		break;
13019304Speter	case INP_INTR:
13119304Speter		goto retest;
13219304Speter	case INP_TIMEOUT:
13319304Speter		evp->e_event = E_TIMEOUT;
13419304Speter		break;
13519304Speter	default:
13619304Speter		abort();
13719304Speter	}
13819304Speter	return (0);
13919304Speter}
14019304Speter
14119304Speter/*
14219304Speter * cl_read --
14319304Speter *	Read characters from the input.
14419304Speter */
14519304Speterstatic input_t
146254225Spetercl_read(SCR *sp, u_int32_t flags, char *bp, size_t blen, int *nrp, struct timeval *tp)
14719304Speter{
14819304Speter	struct termios term1, term2;
14919304Speter	CL_PRIVATE *clp;
15019304Speter	GS *gp;
15119304Speter	fd_set rdfd;
15219304Speter	input_t rval;
15319304Speter	int maxfd, nr, term_reset;
15419304Speter
15519304Speter	gp = sp->gp;
15619304Speter	clp = CLP(sp);
15719304Speter	term_reset = 0;
15819304Speter
15919304Speter	/*
16019304Speter	 * 1: A read from a file or a pipe.  In this case, the reads
16119304Speter	 *    never timeout regardless.  This means that we can hang
16219304Speter	 *    when trying to complete a map, but we're going to hang
16319304Speter	 *    on the next read anyway.
16419304Speter	 */
16519304Speter	if (!F_ISSET(clp, CL_STDIN_TTY)) {
16619304Speter		switch (nr = read(STDIN_FILENO, bp, blen)) {
16719304Speter		case 0:
16819304Speter			return (INP_EOF);
16919304Speter		case -1:
17019304Speter			goto err;
17119304Speter		default:
17219304Speter			*nrp = nr;
17319304Speter			return (INP_OK);
17419304Speter		}
17519304Speter		/* NOTREACHED */
17619304Speter	}
17719304Speter
17819304Speter	/*
17919304Speter	 * 2: A read with an associated timeout, e.g., trying to complete
18019304Speter	 *    a map sequence.  If input exists, we fall into #3.
18119304Speter	 */
18219304Speter	if (tp != NULL) {
183254225Speter		FD_ZERO(&rdfd);
18419304Speter		FD_SET(STDIN_FILENO, &rdfd);
185254225Speter		switch (select(STDIN_FILENO + 1, &rdfd, NULL, NULL, tp)) {
18619304Speter		case 0:
18719304Speter			return (INP_TIMEOUT);
18819304Speter		case -1:
18919304Speter			goto err;
19019304Speter		default:
19119304Speter			break;
19219304Speter		}
19319304Speter	}
19419304Speter
19519304Speter	/*
19619304Speter	 * The user can enter a key in the editor to quote a character.  If we
19719304Speter	 * get here and the next key is supposed to be quoted, do what we can.
19819304Speter	 * Reset the tty so that the user can enter a ^C, ^Q, ^S.  There's an
19919304Speter	 * obvious race here, when the key has already been entered, but there's
20019304Speter	 * nothing that we can do to fix that problem.
20119304Speter	 *
20219304Speter	 * The editor can ask for the next literal character even thought it's
20319304Speter	 * generally running in line-at-a-time mode.  Do what we can.
20419304Speter	 */
20519304Speter	if (LF_ISSET(EC_QUOTED | EC_RAW) && !tcgetattr(STDIN_FILENO, &term1)) {
20619304Speter		term_reset = 1;
20719304Speter		if (LF_ISSET(EC_QUOTED)) {
20819304Speter			term2 = term1;
20919304Speter			term2.c_lflag &= ~ISIG;
21019304Speter			term2.c_iflag &= ~(IXON | IXOFF);
21119304Speter			(void)tcsetattr(STDIN_FILENO,
21219304Speter			    TCSASOFT | TCSADRAIN, &term2);
21319304Speter		} else
21419304Speter			(void)tcsetattr(STDIN_FILENO,
21519304Speter			    TCSASOFT | TCSADRAIN, &clp->vi_enter);
21619304Speter	}
21719304Speter
21819304Speter	/*
21919304Speter	 * 3: Wait for input.
22019304Speter	 *
22119304Speter	 * Select on the command input and scripting window file descriptors.
22219304Speter	 * It's ugly that we wait on scripting file descriptors here, but it's
22319304Speter	 * the only way to keep from locking out scripting windows.
22419304Speter	 */
22519304Speter	if (F_ISSET(gp, G_SCRWIN)) {
22619304Speterloop:		FD_ZERO(&rdfd);
22719304Speter		FD_SET(STDIN_FILENO, &rdfd);
22819304Speter		maxfd = STDIN_FILENO;
229254225Speter		if (F_ISSET(sp, SC_SCRIPT)) {
230254225Speter			FD_SET(sp->script->sh_master, &rdfd);
231254225Speter			if (sp->script->sh_master > maxfd)
232254225Speter				maxfd = sp->script->sh_master;
233254225Speter		}
23419304Speter		switch (select(maxfd + 1, &rdfd, NULL, NULL, NULL)) {
23519304Speter		case 0:
23619304Speter			abort();
23719304Speter		case -1:
23819304Speter			goto err;
23919304Speter		default:
24019304Speter			break;
24119304Speter		}
24219304Speter		if (!FD_ISSET(STDIN_FILENO, &rdfd)) {
24319304Speter			if (sscr_input(sp))
24419304Speter				return (INP_ERR);
24519304Speter			goto loop;
24619304Speter		}
24719304Speter	}
24819304Speter
24919304Speter	/*
25019304Speter	 * 4: Read the input.
25119304Speter	 *
25219304Speter	 * !!!
25319304Speter	 * What's going on here is some scary stuff.  Ex runs the terminal in
25419304Speter	 * canonical mode.  So, the <newline> character terminating a line of
25519304Speter	 * input is returned in the buffer, but a trailing <EOF> character is
25619304Speter	 * not similarly included.  As ex uses 0<EOF> and ^<EOF> as autoindent
25719304Speter	 * commands, it has to see the trailing <EOF> characters to determine
25819304Speter	 * the difference between the user entering "0ab" and "0<EOF>ab".  We
25919304Speter	 * leave an extra slot in the buffer, so that we can add a trailing
26019304Speter	 * <EOF> character if the buffer isn't terminated by a <newline>.  We
26119304Speter	 * lose if the buffer is too small for the line and exactly N characters
26219304Speter	 * are entered followed by an <EOF> character.
26319304Speter	 */
26419304Speter#define	ONE_FOR_EOF	1
26519304Speter	switch (nr = read(STDIN_FILENO, bp, blen - ONE_FOR_EOF)) {
26619304Speter	case  0:				/* EOF. */
26719304Speter		/*
26819304Speter		 * ^D in canonical mode returns a read of 0, i.e. EOF.  EOF is
26919304Speter		 * a valid command, but we don't want to loop forever because
27019304Speter		 * the terminal driver is returning EOF because the user has
27119304Speter		 * disconnected. The editor will almost certainly try to write
27219304Speter		 * something before this fires, which should kill us, but You
27319304Speter		 * Never Know.
27419304Speter		 */
27519304Speter		if (++clp->eof_count < 50) {
27619304Speter			bp[0] = clp->orig.c_cc[VEOF];
27719304Speter			*nrp = 1;
27819304Speter			rval = INP_OK;
27919304Speter
28019304Speter		} else
28119304Speter			rval = INP_EOF;
28219304Speter		break;
28319304Speter	case -1:				/* Error or interrupt. */
28419304Spetererr:		if (errno == EINTR)
28519304Speter			rval = INP_INTR;
28619304Speter		else {
28719304Speter			rval = INP_ERR;
28819304Speter			msgq(sp, M_SYSERR, "input");
28919304Speter		}
29019304Speter		break;
29119304Speter	default:				/* Input characters. */
29219304Speter		if (F_ISSET(sp, SC_EX) && bp[nr - 1] != '\n')
29319304Speter			bp[nr++] = clp->orig.c_cc[VEOF];
29419304Speter		*nrp = nr;
29519304Speter		clp->eof_count = 0;
29619304Speter		rval = INP_OK;
29719304Speter		break;
29819304Speter	}
29919304Speter
30019304Speter	/* Restore the terminal state if it was modified. */
30119304Speter	if (term_reset)
30219304Speter		(void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &term1);
30319304Speter	return (rval);
30419304Speter}
30519304Speter
30619304Speter/*
30719304Speter * cl_resize --
30819304Speter *	Reset the options for a resize event.
30919304Speter */
31019304Speterstatic int
311254225Spetercl_resize(SCR *sp, size_t lines, size_t columns)
31219304Speter{
31319304Speter	ARGS *argv[2], a, b;
314254225Speter	CHAR_T b1[1024];
31519304Speter
31619304Speter	a.bp = b1;
31719304Speter	b.bp = NULL;
31819304Speter	a.len = b.len = 0;
31919304Speter	argv[0] = &a;
32019304Speter	argv[1] = &b;
32119304Speter
322254225Speter	a.len = SPRINTF(b1, sizeof(b1), L("lines=%lu"), (u_long)lines);
32319304Speter	if (opts_set(sp, argv, NULL))
32419304Speter		return (1);
325254225Speter	a.len = SPRINTF(b1, sizeof(b1), L("columns=%lu"), (u_long)columns);
32619304Speter	if (opts_set(sp, argv, NULL))
32719304Speter		return (1);
32819304Speter	return (0);
32919304Speter}
330