xref: /netbsd-src/external/bsd/nvi/dist/ip/ip_read.c (revision 75219f3a016dfaad1cb304eb017f9787b1de8292)
1 /*	$NetBSD: ip_read.c,v 1.5 2013/11/27 20:44:56 christos Exp $	*/
2 /*-
3  * Copyright (c) 1996
4  *	Keith Bostic.  All rights reserved.
5  *
6  * See the LICENSE file for redistribution information.
7  */
8 
9 #include "config.h"
10 
11 #ifndef lint
12 static const char sccsid[] = "Id: ip_read.c,v 8.23 2001/06/25 15:19:24 skimo Exp  (Berkeley) Date: 2001/06/25 15:19:24 ";
13 #endif /* not lint */
14 
15 #include <sys/types.h>
16 #include <sys/queue.h>
17 #include <sys/time.h>
18 
19 #include <bitstring.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <termios.h>
24 #include <time.h>
25 #include <unistd.h>
26 #include <netinet/in.h>
27 
28 #include "../common/common.h"
29 #include "../ex/script.h"
30 #include "../ipc/ip.h"
31 
32 extern GS *__global_list;
33 
34 VIPFUNLIST const vipfuns[] = {
35 /* VI_C_BOL	 1      Cursor to start of line. */
36     {"",    E_IPCOMMAND},
37 /* VI_C_BOTTOM	 2	Cursor to bottom. */
38     {"",    E_IPCOMMAND},
39 /* VI_C_DEL	 3	Cursor delete. */
40     {"",    E_IPCOMMAND},
41 /* VI_C_DOWN	 4      Cursor down N lines: IPO_INT. */
42     {"1",   E_IPCOMMAND},
43 /* VI_C_EOL	 5	Cursor to end of line. */
44     {"",    E_IPCOMMAND},
45 /* VI_C_INSERT	 6	Cursor: enter insert mode. */
46     {"",    E_IPCOMMAND},
47 /* VI_C_LEFT	 7	Cursor left. */
48     {"",    E_IPCOMMAND},
49 /* VI_C_PGDOWN	 8	Cursor down N pages: IPO_INT. */
50     {"1",   E_IPCOMMAND},
51 /* VI_C_PGUP	 9	Cursor up N lines: IPO_INT. */
52     {"1",   E_IPCOMMAND},
53 /* VI_C_RIGHT	10	Cursor right. */
54     {"",    E_IPCOMMAND},
55 /* VI_C_SEARCH	11	Cursor: search: IPO_INT, IPO_STR. */
56     {"a1",  E_IPCOMMAND},
57 /* VI_C_SETTOP	12	Cursor: set screen top line: IPO_INT. */
58     {"1",   E_IPCOMMAND},
59 /* VI_C_TOP	13	Cursor to top. */
60     {"",    E_IPCOMMAND},
61 /* VI_C_UP	14	Cursor up N lines: IPO_INT. */
62     {"1",   E_IPCOMMAND},
63 /* VI_EDIT	15	Edit a file: IPO_STR. */
64     {"a",   E_IPCOMMAND},
65 /* VI_EDITOPT	16	Edit option: 2 * IPO_STR, IPO_INT. */
66     {"ab1", E_IPCOMMAND},
67 /* VI_EDITSPLIT	17	Split to a file: IPO_STR. */
68     {"a",   E_IPCOMMAND},
69 /* VI_EOF	18	End of input (NOT ^D). */
70     {"",    E_EOF},
71 /* VI_ERR	19	Input error. */
72     {"",    E_ERR},
73 /* VI_FLAGS	20	Flags */
74     {"1",   E_FLAGS},
75 /* VI_INTERRUPT	21	Interrupt. */
76     {"",    E_INTERRUPT},
77 /* VI_MOUSE_MOVE 22	Mouse click move: IPO_INT, IPO_INT. */
78     {"12",  E_IPCOMMAND},
79 /* VI_QUIT	23	Quit. */
80     {"",    E_IPCOMMAND},
81 /* VI_RESIZE	24      Screen resize: IPO_INT, IPO_INT. */
82     {"12",  E_WRESIZE},
83 /* VI_SEL_END	25	Select end: IPO_INT, IPO_INT. */
84     {"12",  E_IPCOMMAND},
85 /* VI_SEL_START	26	Select start: IPO_INT, IPO_INT. */
86     {"12",  E_IPCOMMAND},
87 /* VI_SIGHUP	27	SIGHUP. */
88     {"",    E_SIGHUP},
89 /* VI_SIGTERM	28	SIGTERM. */
90     {"",    E_SIGTERM},
91 /* VI_STRING	29      Input string: IPO_STR. */
92     {"a",   E_STRING},
93 /* VI_TAG	30	Tag. */
94     {"",    E_IPCOMMAND},
95 /* VI_TAGAS	31	Tag to a string: IPO_STR. */
96     {"a",   E_IPCOMMAND},
97 /* VI_TAGSPLIT	32	Split to a tag. */
98     {"",    E_IPCOMMAND},
99 /* VI_UNDO	33	Undo. */
100     {"",    E_IPCOMMAND},
101 /* VI_WQ	34	Write and quit. */
102     {"",    E_IPCOMMAND},
103 /* VI_WRITE	35	Write. */
104     {"",    E_IPCOMMAND},
105 /* VI_WRITEAS	36	Write as another file: IPO_STR. */
106     {"a",   E_IPCOMMAND},
107 /* VI_EVENT_SUP 37	*/
108 };
109 
110 typedef enum { INP_OK=0, INP_EOF, INP_ERR, INP_TIMEOUT } input_t;
111 
112 static input_t	ip_read __P((SCR *, IP_PRIVATE *, struct timeval *, int, int*));
113 static int	ip_resize __P((SCR *, u_int32_t, u_int32_t));
114 static int	ip_trans __P((SCR *, IP_PRIVATE *, EVENT *));
115 
116 /*
117  * ip_event --
118  *	Return a single event.
119  *
120  * PUBLIC: int ip_event __P((SCR *, EVENT *, u_int32_t, int));
121  */
122 int
123 ip_event(SCR *sp, EVENT *evp, u_int32_t flags, int ms)
124 {
125 	return ip_wevent(sp->wp, sp, evp, flags, ms);
126 }
127 
128 /*
129  * XXX probably better to require new_window to send size
130  *     so we never have to call ip_wevent with sp == NULL
131  *
132  * ip_wevent --
133  *	Return a single event.
134  *
135  * PUBLIC: int ip_wevent __P((WIN *, SCR *, EVENT *, u_int32_t, int));
136  */
137 int
138 ip_wevent(WIN *wp, SCR *sp, EVENT *evp, u_int32_t flags, int ms)
139 {
140 	IP_PRIVATE *ipp;
141 	struct timeval t, *tp;
142 	int termread;
143 	int nr = 0;
144 
145 	if (LF_ISSET(EC_INTERRUPT)) {		/* XXX */
146 		evp->e_event = E_TIMEOUT;
147 		return (0);
148 	}
149 
150 	ipp = sp == NULL ? WIPP(wp) : IPP(sp);
151 
152 	/* Discard the last command. */
153 	if (ipp->iskip != 0) {
154 		ipp->iblen -= ipp->iskip;
155 		memmove(ipp->ibuf, ipp->ibuf + ipp->iskip, ipp->iblen);
156 		ipp->iskip = 0;
157 	}
158 
159 	termread = F_ISSET(ipp, IP_IN_EX) ||
160 		    (sp && F_ISSET(sp, SC_SCR_EXWROTE));
161 
162 	/* Process possible remaining commands */
163 	if (!termread && ipp->iblen >= IPO_CODE_LEN && ip_trans(sp, ipp, evp))
164 		return 0;
165 
166 	/* Set timer. */
167 	if (ms == 0)
168 		tp = NULL;
169 	else {
170 		t.tv_sec = ms / 1000;
171 		t.tv_usec = (ms % 1000) * 1000;
172 		tp = &t;
173 	}
174 
175 	/* Read input events. */
176 	for (;;) {
177 		switch (ip_read(sp, ipp, tp, termread, &nr)) {
178 		case INP_OK:
179 			if (termread) {
180 				evp->e_csp = ipp->tbuf;
181 				evp->e_len = nr;
182 				evp->e_event = E_STRING;
183 			} else if (!ip_trans(sp, ipp, evp))
184 				continue;
185 			break;
186 		case INP_EOF:
187 			evp->e_event = E_EOF;
188 			break;
189 		case INP_ERR:
190 			evp->e_event = E_ERR;
191 			break;
192 		case INP_TIMEOUT:
193 			evp->e_event = E_TIMEOUT;
194 			break;
195 		default:
196 			abort();
197 		}
198 		break;
199 	}
200 	return (0);
201 }
202 
203 /*
204  * ip_read --
205  *	Read characters from the input.
206  */
207 static input_t
208 ip_read(SCR *sp, IP_PRIVATE *ipp, struct timeval *tp, int termread, int *nr)
209 {
210 	struct timeval poll;
211 	GS *gp;
212 	fd_set rdfd;
213 	input_t rval;
214 	size_t blen;
215 	int maxfd;
216 	char *bp;
217 	int fd;
218 	const CHAR_T *wp;
219 	size_t wlen;
220 
221 	gp = sp == NULL ? __global_list : sp->gp;
222 	bp = ipp->ibuf + ipp->iblen;
223 	blen = sizeof(ipp->ibuf) - ipp->iblen;
224 	fd = termread ? ipp->t_fd : ipp->i_fd;
225 
226 	/*
227 	 * 1: A read with an associated timeout, e.g., trying to complete
228 	 *    a map sequence.  If input exists, we fall into #2.
229 	 */
230 	FD_ZERO(&rdfd);
231 	poll.tv_sec = 0;
232 	poll.tv_usec = 0;
233 	if (tp != NULL) {
234 		FD_SET(fd, &rdfd);
235 		switch (select(fd + 1, &rdfd, NULL, NULL, tp)) {
236 		case 0:
237 			return (INP_TIMEOUT);
238 		case -1:
239 			goto err;
240 		default:
241 			break;
242 		}
243 	}
244 
245 	/*
246 	 * 2: Wait for input.
247 	 *
248 	 * Select on the command input and scripting window file descriptors.
249 	 * It's ugly that we wait on scripting file descriptors here, but it's
250 	 * the only way to keep from locking out scripting windows.
251 	 */
252 	if (sp != NULL && F_ISSET(gp, G_SCRWIN)) {
253 		FD_ZERO(&rdfd);
254 		FD_SET(fd, &rdfd);
255 		maxfd = fd;
256 		if (sscr_check_input(sp, &rdfd, maxfd))
257 			goto err;
258 	}
259 
260 	/*
261 	 * 3: Read the input.
262 	 */
263 	switch (*nr = read(fd, termread ? (char *)ipp->tbuf : bp,
264 			      termread ? sizeof(ipp->tbuf)/sizeof(CHAR_T)
265 				       : blen)) {
266 	case  0:				/* EOF. */
267 		rval = INP_EOF;
268 		break;
269 	case -1:				/* Error or interrupt. */
270 err:	        rval = INP_ERR;
271 		msgq(sp, M_SYSERR, "input");
272 		break;
273 	default:				/* Input characters. */
274 		if (sp == NULL) {
275 			rval = INP_ERR;
276 			msgq(sp, M_SYSERR,
277 			    "Can't convert input with NULL screen");
278 			break;
279 		}
280 		if (!termread) ipp->iblen += *nr;
281 		else {
282 			CHAR2INT(sp, (char *)ipp->tbuf, *nr, wp, wlen);
283 			MEMMOVEW(ipp->tbuf, wp, wlen);
284 		}
285 		rval = INP_OK;
286 		break;
287 	}
288 	return (rval);
289 }
290 
291 /*
292  * ip_trans --
293  *	Translate messages into events.
294  */
295 static int
296 ip_trans(SCR *sp, IP_PRIVATE *ipp, EVENT *evp)
297 {
298 	u_int32_t skip, val;
299 	const char *fmt;
300 	const CHAR_T *wp;
301 	size_t wlen;
302 
303 	if (ipp->ibuf[0] == CODE_OOB ||
304 	    ipp->ibuf[0] >= VI_EVENT_SUP)
305 	{
306 		/*
307 		 * XXX: Protocol is out of sync?
308 		 */
309 		abort();
310 	}
311 	fmt = vipfuns[ipp->ibuf[0]-1].format;
312 	evp->e_event = vipfuns[ipp->ibuf[0]-1].e_event;
313 	evp->e_ipcom = ipp->ibuf[0];
314 
315 	for (skip = IPO_CODE_LEN; *fmt != '\0'; ++fmt)
316 		switch (*fmt) {
317 		case '1':
318 		case '2':
319 			if (ipp->iblen < skip + IPO_INT_LEN)
320 				return (0);
321 			memcpy(&val, ipp->ibuf + skip, IPO_INT_LEN);
322 			val = ntohl(val);
323 			if (*fmt == '1')
324 				evp->e_val1 = val;
325 			else
326 				evp->e_val2 = val;
327 			skip += IPO_INT_LEN;
328 			break;
329 		case 'a':
330 		case 'b':
331 			if (ipp->iblen < skip + IPO_INT_LEN)
332 				return (0);
333 			memcpy(&val, ipp->ibuf + skip, IPO_INT_LEN);
334 			val = ntohl(val);
335 			skip += IPO_INT_LEN;
336 			if (ipp->iblen < skip + val)
337 				return (0);
338 			if (*fmt == 'a') {
339 				CHAR2INT(sp, ipp->ibuf + skip, val,
340 					 wp, wlen);
341 				MEMCPYW(ipp->tbuf, wp, wlen);
342 				evp->e_str1 = ipp->tbuf;
343 				evp->e_len1 = wlen;
344 			} else {
345 				CHAR2INT(sp, ipp->ibuf + skip, val,
346 					 wp, wlen);
347 				MEMCPYW(ipp->tbuf, wp, wlen);
348 				evp->e_str2 = ipp->tbuf;
349 				evp->e_len2 = wlen;
350 			}
351 			skip += val;
352 			break;
353 		}
354 
355 	ipp->iskip = skip;
356 
357 	if (evp->e_event == E_WRESIZE)
358 		(void)ip_resize(sp, evp->e_val1, evp->e_val2);
359 
360 	return (1);
361 }
362 
363 /*
364  * ip_resize --
365  *	Reset the options for a resize event.
366  */
367 static int
368 ip_resize(SCR *sp, u_int32_t lines, u_int32_t columns)
369 {
370 	GS *gp;
371 	int rval;
372 
373 	/*
374 	 * XXX
375 	 * The IP screen has to know the lines and columns before anything
376 	 * else happens.  So, we may not have a valid SCR pointer, and we
377 	 * have to deal with that.
378 	 */
379 	if (sp == NULL) {
380 		gp = __global_list;
381 		OG_VAL(gp, GO_LINES) = OG_D_VAL(gp, GO_LINES) = lines;
382 		OG_VAL(gp, GO_COLUMNS) = OG_D_VAL(gp, GO_COLUMNS) = columns;
383 		return (0);
384 	}
385 
386 	rval = api_opts_set(sp, L("lines"), NULL, lines, 0);
387 	if (api_opts_set(sp, L("columns"), NULL, columns, 0))
388 		rval = 1;
389 	return (rval);
390 }
391