First test with an external encoding.
[nvi.git] / ip / ip_read.c
blobb8927be75f572ea173c5e4e7d44a7836c902c36d
1 /*-
2 * Copyright (c) 1996
3 * Keith Bostic. All rights reserved.
5 * See the LICENSE file for redistribution information.
6 */
8 #include "config.h"
10 #ifndef lint
11 static const char sccsid[] = "$Id: ip_read.c,v 8.21 2000/07/16 20:49:33 skimo Exp $ (Berkeley) $Date: 2000/07/16 20:49:33 $";
12 #endif /* not lint */
14 #include <sys/types.h>
15 #include <sys/queue.h>
16 #include <sys/time.h>
18 #include <bitstring.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <termios.h>
23 #include <time.h>
24 #include <unistd.h>
25 #include <netinet/in.h>
27 #include "../common/common.h"
28 #include "../ex/script.h"
29 #include "../ipc/ip.h"
30 #include "extern.h"
32 extern GS *__global_list;
34 VIPFUNLIST const vipfuns[] = {
35 /* VI_C_BOL 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 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 Flags */
74 {"1", E_FLAGS},
75 /* VI_INTERRUPT 20 /* Interrupt. */
76 {"", E_INTERRUPT},
77 /* VI_MOUSE_MOVE 21 /* Mouse click move: IPO_INT, IPO_INT. */
78 {"12", E_IPCOMMAND},
79 /* VI_QUIT 22 /* Quit. */
80 {"", E_IPCOMMAND},
81 /* VI_RESIZE Screen resize: IPO_INT, IPO_INT. */
82 {"12", E_WRESIZE},
83 /* VI_SEL_END 24 /* Select end: IPO_INT, IPO_INT. */
84 {"12", E_IPCOMMAND},
85 /* VI_SEL_START 25 /* Select start: IPO_INT, IPO_INT. */
86 {"12", E_IPCOMMAND},
87 /* VI_SIGHUP 26 /* SIGHUP. */
88 {"", E_SIGHUP},
89 /* VI_SIGTERM 27 /* SIGTERM. */
90 {"", E_SIGTERM},
91 /* VI_STRING Input string: IPO_STR. */
92 {"a", E_STRING},
93 /* VI_TAG 29 /* Tag. */
94 {"", E_IPCOMMAND},
95 /* VI_TAGAS 30 /* Tag to a string: IPO_STR. */
96 {"a", E_IPCOMMAND},
97 /* VI_TAGSPLIT 31 /* Split to a tag. */
98 {"", E_IPCOMMAND},
99 /* VI_UNDO 32 /* Undo. */
100 {"", E_IPCOMMAND},
101 /* VI_WQ 33 /* Write and quit. */
102 {"", E_IPCOMMAND},
103 /* VI_WRITE 34 /* Write. */
104 {"", E_IPCOMMAND},
105 /* VI_WRITEAS 35 /* Write as another file: IPO_STR. */
106 {"a", E_IPCOMMAND},
107 /* VI_EVENT_SUP */
110 typedef enum { INP_OK=0, INP_EOF, INP_ERR, INP_TIMEOUT } input_t;
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 *));
117 * ip_event --
118 * Return a single event.
120 * PUBLIC: int ip_event __P((SCR *, EVENT *, u_int32_t, int));
123 ip_event(sp, evp, flags, ms)
124 SCR *sp;
125 EVENT *evp;
126 u_int32_t flags;
127 int ms;
129 return ip_wevent(sp->wp, sp, evp, flags, ms);
133 * XXX probably better to require new_window to send size
134 * so we never have to call ip_wevent with sp == NULL
136 * ip_wevent --
137 * Return a single event.
139 * PUBLIC: int ip_wevent __P((WIN *, SCR *, EVENT *, u_int32_t, int));
142 ip_wevent(wp, sp, evp, flags, ms)
143 WIN *wp;
144 SCR *sp;
145 EVENT *evp;
146 u_int32_t flags;
147 int ms;
149 IP_PRIVATE *ipp;
150 struct timeval t, *tp;
151 int termread;
152 int nr;
154 if (LF_ISSET(EC_INTERRUPT)) { /* XXX */
155 evp->e_event = E_TIMEOUT;
156 return (0);
159 ipp = sp == NULL ? WIPP(wp) : IPP(sp);
161 /* Discard the last command. */
162 if (ipp->iskip != 0) {
163 ipp->iblen -= ipp->iskip;
164 memmove(ipp->ibuf, ipp->ibuf + ipp->iskip, ipp->iblen);
165 ipp->iskip = 0;
168 termread = F_ISSET(ipp, IP_IN_EX) ||
169 (sp && F_ISSET(sp, SC_SCR_EXWROTE));
171 /* Process possible remaining commands */
172 if (!termread && ipp->iblen >= IPO_CODE_LEN && ip_trans(sp, ipp, evp))
173 return 0;
175 /* Set timer. */
176 if (ms == 0)
177 tp = NULL;
178 else {
179 t.tv_sec = ms / 1000;
180 t.tv_usec = (ms % 1000) * 1000;
181 tp = &t;
184 /* Read input events. */
185 for (;;) {
186 switch (ip_read(sp, ipp, tp, termread, &nr)) {
187 case INP_OK:
188 if (termread) {
189 evp->e_csp = ipp->tbuf;
190 evp->e_len = nr;
191 evp->e_event = E_STRING;
192 } else if (!ip_trans(sp, ipp, evp))
193 continue;
194 break;
195 case INP_EOF:
196 evp->e_event = E_EOF;
197 break;
198 case INP_ERR:
199 evp->e_event = E_ERR;
200 break;
201 case INP_TIMEOUT:
202 evp->e_event = E_TIMEOUT;
203 break;
204 default:
205 abort();
207 break;
209 return (0);
213 * ip_read --
214 * Read characters from the input.
216 static input_t
217 ip_read(sp, ipp, tp, termread, nr)
218 SCR *sp;
219 IP_PRIVATE *ipp;
220 struct timeval *tp;
221 int termread;
222 int *nr;
224 struct timeval poll;
225 GS *gp;
226 fd_set rdfd;
227 input_t rval;
228 size_t blen;
229 int maxfd;
230 char *bp;
231 int fd;
232 CHAR_T *wp;
233 size_t wlen;
235 gp = sp == NULL ? __global_list : sp->gp;
236 bp = ipp->ibuf + ipp->iblen;
237 blen = sizeof(ipp->ibuf) - ipp->iblen;
238 fd = termread ? ipp->t_fd : ipp->i_fd;
241 * 1: A read with an associated timeout, e.g., trying to complete
242 * a map sequence. If input exists, we fall into #2.
244 FD_ZERO(&rdfd);
245 poll.tv_sec = 0;
246 poll.tv_usec = 0;
247 if (tp != NULL) {
248 FD_SET(fd, &rdfd);
249 switch (select(fd + 1,
250 &rdfd, NULL, NULL, tp == NULL ? &poll : tp)) {
251 case 0:
252 return (INP_TIMEOUT);
253 case -1:
254 goto err;
255 default:
256 break;
261 * 2: Wait for input.
263 * Select on the command input and scripting window file descriptors.
264 * It's ugly that we wait on scripting file descriptors here, but it's
265 * the only way to keep from locking out scripting windows.
267 if (sp != NULL && F_ISSET(gp, G_SCRWIN)) {
268 FD_ZERO(&rdfd);
269 FD_SET(fd, &rdfd);
270 maxfd = fd;
271 if (sscr_check_input(sp, &rdfd, maxfd))
272 goto err;
276 * 3: Read the input.
278 switch (*nr = read(fd, termread ? (char *)ipp->tbuf : bp,
279 termread ? sizeof(ipp->tbuf)/sizeof(CHAR_T)
280 : blen)) {
281 case 0: /* EOF. */
282 rval = INP_EOF;
283 break;
284 case -1: /* Error or interrupt. */
285 err: rval = INP_ERR;
286 msgq(sp, M_SYSERR, "input");
287 break;
288 default: /* Input characters. */
289 if (!termread) ipp->iblen += *nr;
290 else {
291 CHAR2INT(sp, (char *)ipp->tbuf, *nr, wp, wlen);
292 MEMMOVEW(ipp->tbuf, wp, wlen);
294 rval = INP_OK;
295 break;
297 return (rval);
301 * ip_trans --
302 * Translate messages into events.
304 static int
305 ip_trans(sp, ipp, evp)
306 SCR *sp;
307 IP_PRIVATE *ipp;
308 EVENT *evp;
310 u_int32_t skip, val;
311 char *fmt;
312 CHAR_T *wp;
313 size_t wlen;
315 if (ipp->ibuf[0] == CODE_OOB ||
316 ipp->ibuf[0] >= VI_EVENT_SUP)
319 * XXX: Protocol is out of sync?
321 abort();
323 fmt = vipfuns[ipp->ibuf[0]-1].format;
324 evp->e_event = vipfuns[ipp->ibuf[0]-1].e_event;
325 evp->e_ipcom = ipp->ibuf[0];
327 for (skip = IPO_CODE_LEN; *fmt != '\0'; ++fmt)
328 switch (*fmt) {
329 case '1':
330 case '2':
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 if (*fmt == '1')
336 evp->e_val1 = val;
337 else
338 evp->e_val2 = val;
339 skip += IPO_INT_LEN;
340 break;
341 case 'a':
342 case 'b':
343 if (ipp->iblen < skip + IPO_INT_LEN)
344 return (0);
345 memcpy(&val, ipp->ibuf + skip, IPO_INT_LEN);
346 val = ntohl(val);
347 skip += IPO_INT_LEN;
348 if (ipp->iblen < skip + val)
349 return (0);
350 if (*fmt == 'a') {
351 CHAR2INT(sp, ipp->ibuf + skip, val,
352 wp, wlen);
353 MEMCPYW(ipp->tbuf, wp, wlen);
354 evp->e_str1 = ipp->tbuf;
355 evp->e_len1 = wlen;
356 } else {
357 CHAR2INT(sp, ipp->ibuf + skip, val,
358 wp, wlen);
359 MEMCPYW(ipp->tbuf, wp, wlen);
360 evp->e_str2 = ipp->tbuf;
361 evp->e_len2 = wlen;
363 skip += val;
364 break;
367 ipp->iskip = skip;
369 if (evp->e_event == E_WRESIZE)
370 (void)ip_resize(sp, evp->e_val1, evp->e_val2);
372 return (1);
376 * ip_resize --
377 * Reset the options for a resize event.
379 static int
380 ip_resize(sp, lines, columns)
381 SCR *sp;
382 u_int32_t lines, columns;
384 GS *gp;
385 int rval;
388 * XXX
389 * The IP screen has to know the lines and columns before anything
390 * else happens. So, we may not have a valid SCR pointer, and we
391 * have to deal with that.
393 if (sp == NULL) {
394 gp = __global_list;
395 OG_VAL(gp, GO_LINES) = OG_D_VAL(gp, GO_LINES) = lines;
396 OG_VAL(gp, GO_COLUMNS) = OG_D_VAL(gp, GO_COLUMNS) = columns;
397 return (0);
400 rval = api_opts_set(sp, "lines", NULL, lines, 0);
401 if (api_opts_set(sp, "columns", NULL, columns, 0))
402 rval = 1;
403 return (rval);