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 } 327