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