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