]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/nvi/cl/cl_read.c
Update nvi to 2.2.0
[FreeBSD/FreeBSD.git] / contrib / nvi / cl / cl_read.c
1 /*-
2  * Copyright (c) 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 1993, 1994, 1995, 1996
5  *      Keith Bostic.  All rights reserved.
6  *
7  * See the LICENSE file for redistribution information.
8  */
9
10 #include "config.h"
11
12 #include <sys/types.h>
13 #include <sys/queue.h>
14 #include <sys/select.h>
15
16 #include <bitstring.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <signal.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <termios.h>
24 #include <unistd.h>
25
26 #include "../common/common.h"
27 #include "../ex/script.h"
28 #include "cl.h"
29
30 /* Pollution by Solaris curses. */
31 #undef columns
32 #undef lines  
33
34 static input_t  cl_read(SCR *,
35     u_int32_t, char *, 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 = 0;
51         CHAR_T *wp;
52         size_t wlen;
53         int rc;
54
55         /*
56          * Queue signal based events.  We never clear SIGHUP or SIGTERM events,
57          * so that we just keep returning them until the editor dies.
58          */
59         clp = CLP(sp);
60 retest: if (LF_ISSET(EC_INTERRUPT) || F_ISSET(clp, CL_SIGINT)) {
61                 if (F_ISSET(clp, CL_SIGINT)) {
62                         F_CLR(clp, CL_SIGINT);
63                         evp->e_event = E_INTERRUPT;
64                 } else
65                         evp->e_event = E_TIMEOUT;
66                 return (0);
67         }
68         if (F_ISSET(clp, CL_SIGHUP | CL_SIGTERM | CL_SIGWINCH)) {
69                 if (F_ISSET(clp, CL_SIGHUP)) {
70                         evp->e_event = E_SIGHUP;
71                         return (0);
72                 }
73                 if (F_ISSET(clp, CL_SIGTERM)) {
74                         evp->e_event = E_SIGTERM;
75                         return (0);
76                 }
77                 if (F_ISSET(clp, CL_SIGWINCH)) {
78                         F_CLR(clp, CL_SIGWINCH);
79                         if (cl_ssize(sp, 1, &lines, &columns, &changed))
80                                 return (1);
81                         if (changed) {
82                                 (void)cl_resize(sp, lines, columns);
83                                 evp->e_event = E_WRESIZE;
84                                 return (0);
85                         }
86                         /* No real change, ignore the signal. */
87                 }
88         }
89
90         /* Set timer. */
91         if (ms == 0)
92                 tp = NULL;
93         else {
94                 t.tv_sec = ms / 1000;
95                 t.tv_usec = (ms % 1000) * 1000;
96                 tp = &t;
97         }
98
99         /* Read input characters. */
100 read:
101         switch (cl_read(sp, LF_ISSET(EC_QUOTED | EC_RAW),
102             clp->ibuf + clp->skip, SIZE(clp->ibuf) - clp->skip, &nr, tp)) {
103         case INP_OK:
104                 rc = INPUT2INT5(sp, clp->cw, clp->ibuf, nr + clp->skip, 
105                                 wp, wlen);
106                 evp->e_csp = wp;
107                 evp->e_len = wlen;
108                 evp->e_event = E_STRING;
109                 if (rc < 0) {
110                     int n = -rc;
111                     memmove(clp->ibuf, clp->ibuf + nr + clp->skip - n, n);
112                     clp->skip = n;
113                     if (wlen == 0)
114                         goto read;
115                 } else if (rc == 0)
116                     clp->skip = 0;
117                 else
118                     msgq(sp, M_ERR, "323|Invalid input. Truncated.");
119                 break;
120         case INP_EOF:
121                 evp->e_event = E_EOF;
122                 break;
123         case INP_ERR:
124                 evp->e_event = E_ERR;
125                 break;
126         case INP_INTR:
127                 goto retest;
128         case INP_TIMEOUT:
129                 evp->e_event = E_TIMEOUT;
130                 break;
131         default:
132                 abort();
133         }
134         return (0);
135 }
136
137 /*
138  * cl_read --
139  *      Read characters from the input.
140  */
141 static input_t
142 cl_read(SCR *sp, u_int32_t flags, char *bp, size_t blen, int *nrp,
143     struct timeval *tp)
144 {
145         struct termios term1, term2;
146         CL_PRIVATE *clp;
147         GS *gp;
148         fd_set rdfd;
149         input_t rval;
150         int maxfd, nr, term_reset;
151
152         gp = sp->gp;
153         clp = CLP(sp);
154         term_reset = 0;
155
156         /*
157          * 1: A read from a file or a pipe.  In this case, the reads
158          *    never timeout regardless.  This means that we can hang
159          *    when trying to complete a map, but we're going to hang
160          *    on the next read anyway.
161          */
162         if (!F_ISSET(clp, CL_STDIN_TTY)) {
163                 switch (nr = read(STDIN_FILENO, bp, blen)) {
164                 case 0:
165                         return (INP_EOF);
166                 case -1:
167                         goto err;
168                 default:
169                         *nrp = nr;
170                         return (INP_OK);
171                 }
172                 /* NOTREACHED */
173         }
174
175         /*
176          * 2: A read with an associated timeout, e.g., trying to complete
177          *    a map sequence.  If input exists, we fall into #3.
178          */
179         if (tp != NULL) {
180                 FD_ZERO(&rdfd);
181                 FD_SET(STDIN_FILENO, &rdfd);
182                 switch (select(STDIN_FILENO + 1, &rdfd, NULL, NULL, tp)) {
183                 case 0:
184                         return (INP_TIMEOUT);
185                 case -1:
186                         goto err;
187                 default:
188                         break;
189                 }
190         }
191         
192         /*
193          * The user can enter a key in the editor to quote a character.  If we
194          * get here and the next key is supposed to be quoted, do what we can.
195          * Reset the tty so that the user can enter a ^C, ^Q, ^S.  There's an
196          * obvious race here, when the key has already been entered, but there's
197          * nothing that we can do to fix that problem.
198          *
199          * The editor can ask for the next literal character even thought it's
200          * generally running in line-at-a-time mode.  Do what we can.
201          */
202         if (LF_ISSET(EC_QUOTED | EC_RAW) && !tcgetattr(STDIN_FILENO, &term1)) {
203                 term_reset = 1;
204                 if (LF_ISSET(EC_QUOTED)) {
205                         term2 = term1;
206                         term2.c_lflag &= ~ISIG;
207                         term2.c_iflag &= ~(IXON | IXOFF);
208                         (void)tcsetattr(STDIN_FILENO,
209                             TCSASOFT | TCSADRAIN, &term2);
210                 } else
211                         (void)tcsetattr(STDIN_FILENO,
212                             TCSASOFT | TCSADRAIN, &clp->vi_enter);
213         }
214
215         /*
216          * 3: Wait for input.
217          *
218          * Select on the command input and scripting window file descriptors.
219          * It's ugly that we wait on scripting file descriptors here, but it's
220          * the only way to keep from locking out scripting windows.
221          */
222         if (F_ISSET(gp, G_SCRWIN)) {
223 loop:           FD_ZERO(&rdfd);
224                 FD_SET(STDIN_FILENO, &rdfd);
225                 maxfd = STDIN_FILENO;
226                 if (F_ISSET(sp, SC_SCRIPT)) {
227                         FD_SET(sp->script->sh_master, &rdfd);
228                         if (sp->script->sh_master > maxfd)
229                                 maxfd = sp->script->sh_master;
230                 }
231                 switch (select(maxfd + 1, &rdfd, NULL, NULL, NULL)) {
232                 case 0:
233                         abort();
234                 case -1:
235                         goto err;
236                 default:
237                         break;
238                 }
239                 if (!FD_ISSET(STDIN_FILENO, &rdfd)) {
240                         if (sscr_input(sp))
241                                 return (INP_ERR);
242                         goto loop;
243                 }
244         }
245
246         /*
247          * 4: Read the input.
248          *
249          * !!!
250          * What's going on here is some scary stuff.  Ex runs the terminal in
251          * canonical mode.  So, the <newline> character terminating a line of
252          * input is returned in the buffer, but a trailing <EOF> character is
253          * not similarly included.  As ex uses 0<EOF> and ^<EOF> as autoindent
254          * commands, it has to see the trailing <EOF> characters to determine
255          * the difference between the user entering "0ab" and "0<EOF>ab".  We
256          * leave an extra slot in the buffer, so that we can add a trailing
257          * <EOF> character if the buffer isn't terminated by a <newline>.  We
258          * lose if the buffer is too small for the line and exactly N characters
259          * are entered followed by an <EOF> character.
260          */
261 #define ONE_FOR_EOF     1
262         switch (nr = read(STDIN_FILENO, bp, blen - ONE_FOR_EOF)) {
263         case  0:                                /* EOF. */
264                 /*
265                  * ^D in canonical mode returns a read of 0, i.e. EOF.  EOF is
266                  * a valid command, but we don't want to loop forever because
267                  * the terminal driver is returning EOF because the user has
268                  * disconnected. The editor will almost certainly try to write
269                  * something before this fires, which should kill us, but You
270                  * Never Know.
271                  */
272                 if (++clp->eof_count < 50) {
273                         bp[0] = clp->orig.c_cc[VEOF];
274                         *nrp = 1;
275                         rval = INP_OK;
276
277                 } else
278                         rval = INP_EOF;
279                 break;
280         case -1:                                /* Error or interrupt. */
281 err:            if (errno == EINTR)
282                         rval = INP_INTR;
283                 else {
284                         rval = INP_ERR;
285                         msgq(sp, M_SYSERR, "input");
286                 }
287                 break;
288         default:                                /* Input characters. */
289                 if (F_ISSET(sp, SC_EX) && bp[nr - 1] != '\n')
290                         bp[nr++] = clp->orig.c_cc[VEOF];
291                 *nrp = nr;
292                 clp->eof_count = 0;
293                 rval = INP_OK;
294                 break;
295         }
296
297         /* Restore the terminal state if it was modified. */
298         if (term_reset)
299                 (void)tcsetattr(STDIN_FILENO, TCSASOFT | TCSADRAIN, &term1);
300         return (rval);
301 }
302
303 /* 
304  * cl_resize --
305  *      Reset the options for a resize event.
306  */
307 static int
308 cl_resize(SCR *sp, size_t lines, size_t columns)
309 {
310         ARGS *argv[2], a, b;
311         CHAR_T b1[1024];
312
313         a.bp = b1;
314         b.bp = NULL;
315         a.len = b.len = 0;
316         argv[0] = &a;
317         argv[1] = &b;
318
319         a.len = SPRINTF(b1, sizeof(b1), L("lines=%lu"), (u_long)lines);
320         if (opts_set(sp, argv, NULL))
321                 return (1);
322         a.len = SPRINTF(b1, sizeof(b1), L("columns=%lu"), (u_long)columns);
323         if (opts_set(sp, argv, NULL))
324                 return (1);
325         return (0);
326 }