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