GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/vi/build/../cl/cl_read.c Lines: 0 99 0.0 %
Date: 2017-11-13 Branches: 0 67 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: cl_read.c,v 1.21 2016/05/27 09:18:11 martijn Exp $	*/
2
3
/*-
4
 * Copyright (c) 1993, 1994
5
 *	The Regents of the University of California.  All rights reserved.
6
 * Copyright (c) 1993, 1994, 1995, 1996
7
 *	Keith Bostic.  All rights reserved.
8
 *
9
 * See the LICENSE file for redistribution information.
10
 */
11
12
#include "config.h"
13
14
#include <sys/types.h>
15
#include <sys/queue.h>
16
#include <sys/time.h>
17
18
#include <bitstring.h>
19
#include <curses.h>
20
#include <errno.h>
21
#include <fcntl.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
30
#include "../common/common.h"
31
#include "../ex/script.h"
32
#include "cl.h"
33
34
static input_t	cl_read(SCR *,
35
		    u_int32_t, CHAR_T *, size_t, int *, struct timeval *);
36
static int	cl_resize(SCR *, size_t, size_t);
37
38
/*
39
 * cl_event --
40
 *	Return a single event.
41
 *
42
 * PUBLIC: int cl_event(SCR *, EVENT *, u_int32_t, int);
43
 */
44
int
45
cl_event(SCR *sp, EVENT *evp, u_int32_t flags, int ms)
46
{
47
	struct timeval t, *tp;
48
	CL_PRIVATE *clp;
49
	size_t lines, columns;
50
	int changed, nr;
51
52
	/*
53
	 * Queue signal based events.  We never clear SIGHUP or SIGTERM events,
54
	 * so that we just keep returning them until the editor dies.
55
	 */
56
	clp = CLP(sp);
57
retest:	if (LF_ISSET(EC_INTERRUPT) || F_ISSET(clp, CL_SIGINT)) {
58
		if (F_ISSET(clp, CL_SIGINT)) {
59
			F_CLR(clp, CL_SIGINT);
60
			evp->e_event = E_INTERRUPT;
61
		} else
62
			evp->e_event = E_TIMEOUT;
63
		return (0);
64
	}
65
	if (F_ISSET(clp, CL_SIGHUP | CL_SIGTERM | CL_SIGWINCH)) {
66
		if (F_ISSET(clp, CL_SIGHUP)) {
67
			evp->e_event = E_SIGHUP;
68
			return (0);
69
		}
70
		if (F_ISSET(clp, CL_SIGTERM)) {
71
			evp->e_event = E_SIGTERM;
72
			return (0);
73
		}
74
		if (F_ISSET(clp, CL_SIGWINCH)) {
75
			F_CLR(clp, CL_SIGWINCH);
76
			if (cl_ssize(sp, 1, &lines, &columns, &changed))
77
				return (1);
78
			if (changed) {
79
				(void)cl_resize(sp, lines, columns);
80
				evp->e_event = E_WRESIZE;
81
				return (0);
82
			}
83
			/* No real change, ignore the signal. */
84
		}
85
	}
86
87
	/* Set timer. */
88
	if (ms == 0)
89
		tp = NULL;
90
	else {
91
		t.tv_sec = ms / 1000;
92
		t.tv_usec = (ms % 1000) * 1000;
93
		tp = &t;
94
	}
95
96
	/* Read input characters. */
97
	switch (cl_read(sp, LF_ISSET(EC_QUOTED | EC_RAW),
98
	    clp->ibuf, sizeof(clp->ibuf), &nr, tp)) {
99
	case INP_OK:
100
		evp->e_csp = clp->ibuf;
101
		evp->e_len = nr;
102
		evp->e_event = E_STRING;
103
		break;
104
	case INP_EOF:
105
		evp->e_event = E_EOF;
106
		break;
107
	case INP_ERR:
108
		evp->e_event = E_ERR;
109
		break;
110
	case INP_INTR:
111
		goto retest;
112
	case INP_TIMEOUT:
113
		evp->e_event = E_TIMEOUT;
114
		break;
115
	default:
116
		abort();
117
	}
118
	return (0);
119
}
120
121
/*
122
 * cl_read --
123
 *	Read characters from the input.
124
 */
125
static input_t
126
cl_read(SCR *sp, u_int32_t flags, CHAR_T *bp, size_t blen, int *nrp,
127
    struct timeval *tp)
128
{
129
	struct termios term1, term2;
130
	CL_PRIVATE *clp;
131
	GS *gp;
132
	struct pollfd pfd[1];
133
	input_t rval;
134
	int nr, term_reset, timeout;
135
136
	gp = sp->gp;
137
	clp = CLP(sp);
138
	term_reset = 0;
139
140
	/*
141
	 * 1: A read from a file or a pipe.  In this case, the reads
142
	 *    never timeout regardless.  This means that we can hang
143
	 *    when trying to complete a map, but we're going to hang
144
	 *    on the next read anyway.
145
	 */
146
	if (!F_ISSET(clp, CL_STDIN_TTY)) {
147
		switch (nr = read(STDIN_FILENO, bp, blen)) {
148
		case 0:
149
			return (INP_EOF);
150
		case -1:
151
			goto err;
152
		default:
153
			*nrp = nr;
154
			return (INP_OK);
155
		}
156
		/* NOTREACHED */
157
	}
158
159
	/*
160
	 * 2: A read with an associated timeout, e.g., trying to complete
161
	 *    a map sequence.  If input exists, we fall into #3.
162
	 */
163
tty_retry:
164
	if (tp != NULL) {
165
		pfd[0].fd = STDIN_FILENO;
166
		pfd[0].events = POLLIN;
167
		timeout = tp ? (tp->tv_sec * 1000) + (tp->tv_usec / 1000) : 0;
168
		switch (poll(pfd, 1, timeout)) {
169
		case 0:
170
			return (INP_TIMEOUT);
171
		case -1:
172
			goto err;
173
		default:
174
			break;
175
		}
176
	}
177
178
	/*
179
	 * The user can enter a key in the editor to quote a character.  If we
180
	 * get here and the next key is supposed to be quoted, do what we can.
181
	 * Reset the tty so that the user can enter a ^C, ^Q, ^S.  There's an
182
	 * obvious race here, when the key has already been entered, but there's
183
	 * nothing that we can do to fix that problem.
184
	 *
185
	 * The editor can ask for the next literal character even thought it's
186
	 * generally running in line-at-a-time mode.  Do what we can.
187
	 */
188
	if (LF_ISSET(EC_QUOTED | EC_RAW) && !tcgetattr(STDIN_FILENO, &term1)) {
189
		term_reset = 1;
190
		if (LF_ISSET(EC_QUOTED)) {
191
			term2 = term1;
192
			term2.c_lflag &= ~ISIG;
193
			term2.c_iflag &= ~(IXON | IXOFF);
194
			(void)tcsetattr(STDIN_FILENO,
195
			    TCSASOFT | TCSADRAIN, &term2);
196
		} else
197
			(void)tcsetattr(STDIN_FILENO,
198
			    TCSASOFT | TCSADRAIN, &clp->vi_enter);
199
	}
200
201
	/*
202
	 * 3: Wait for input.
203
	 *
204
	 * Select on the command input and scripting window file descriptors.
205
	 * It's ugly that we wait on scripting file descriptors here, but it's
206
	 * the only way to keep from locking out scripting windows.
207
	 */
208
	if (F_ISSET(gp, G_SCRWIN)) {
209
		if (sscr_check_input(sp))
210
			goto err;
211
	}
212
213
	/*
214
	 * 4: Read the input.
215
	 *
216
	 * !!!
217
	 * What's going on here is some scary stuff.  Ex runs the terminal in
218
	 * canonical mode.  So, the <newline> character terminating a line of
219
	 * input is returned in the buffer, but a trailing <EOF> character is
220
	 * not similarly included.  As ex uses 0<EOF> and ^<EOF> as autoindent
221
	 * commands, it has to see the trailing <EOF> characters to determine
222
	 * the difference between the user entering "0ab" and "0<EOF>ab".  We
223
	 * leave an extra slot in the buffer, so that we can add a trailing
224
	 * <EOF> character if the buffer isn't terminated by a <newline>.  We
225
	 * lose if the buffer is too small for the line and exactly N characters
226
	 * are entered followed by an <EOF> character.
227
	 */
228
#define	ONE_FOR_EOF	1
229
	switch (nr = read(STDIN_FILENO, bp, blen - ONE_FOR_EOF)) {
230
	case  0:				/* EOF. */
231
		/*
232
		 * ^D in canonical mode returns a read of 0, i.e. EOF.  EOF is
233
		 * a valid command, but we don't want to loop forever because
234
		 * the terminal driver is returning EOF because the user has
235
		 * disconnected. The editor will almost certainly try to write
236
		 * something before this fires, which should kill us, but You
237
		 * Never Know.
238
		 */
239
		if (++clp->eof_count < 50) {
240
			bp[0] = clp->orig.c_cc[VEOF];
241
			*nrp = 1;
242
			rval = INP_OK;
243
244
		} else
245
			rval = INP_EOF;
246
		break;
247
	case -1:				/* Error or interrupt. */
248
err:		if (errno == EINTR)
249
			rval = INP_INTR;
250
		else if (errno == EAGAIN)
251
			goto tty_retry;
252
		else {
253
			rval = INP_ERR;
254
			msgq(sp, M_SYSERR, "input");
255
		}
256
		break;
257
	default:				/* Input characters. */
258
		if (F_ISSET(sp, SC_EX) && bp[nr - 1] != '\n')
259
			bp[nr++] = clp->orig.c_cc[VEOF];
260
		*nrp = nr;
261
		clp->eof_count = 0;
262
		rval = INP_OK;
263
		break;
264
	}
265
266
	/* Restore the terminal state if it was modified. */
267
	if (term_reset)
268
		(void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &term1);
269
	return (rval);
270
}
271
272
/*
273
 * cl_resize --
274
 *	Reset the options for a resize event.
275
 */
276
static int
277
cl_resize(SCR *sp, size_t lines, size_t columns)
278
{
279
	ARGS *argv[2], a, b;
280
	char b1[1024];
281
282
	a.bp = b1;
283
	b.bp = NULL;
284
	a.len = b.len = 0;
285
	argv[0] = &a;
286
	argv[1] = &b;
287
288
	(void)snprintf(b1, sizeof(b1), "lines=%lu", (u_long)lines);
289
	a.len = strlen(b1);
290
	if (opts_set(sp, argv, NULL))
291
		return (1);
292
	(void)snprintf(b1, sizeof(b1), "columns=%lu", (u_long)columns);
293
	a.len = strlen(b1);
294
	if (opts_set(sp, argv, NULL))
295
		return (1);
296
	return (0);
297
}