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
ip_event(SCR * sp,EVENT * evp,u_int32_t flags,int ms)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
ip_wevent(WIN * wp,SCR * sp,EVENT * evp,u_int32_t flags,int ms)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
ip_read(SCR * sp,IP_PRIVATE * ipp,struct timeval * tp,int termread,int * nr)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
ip_trans(SCR * sp,IP_PRIVATE * ipp,EVENT * evp)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
ip_resize(SCR * sp,u_int32_t lines,u_int32_t columns)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