1/* $NetBSD: cl_read.c,v 1.2 2013/11/22 15:52:05 christos Exp $ */ 2/*- 3 * Copyright (c) 1993, 1994 4 * The Regents of the University of California. All rights reserved. 5 * Copyright (c) 1993, 1994, 1995, 1996 6 * Keith Bostic. All rights reserved. 7 * 8 * See the LICENSE file for redistribution information. 9 */ 10 11#include "config.h" 12 13#include <sys/cdefs.h> 14#if 0 15#ifndef lint 16static const char sccsid[] = "Id: cl_read.c,v 10.29 2001/08/18 21:51:59 skimo Exp (Berkeley) Date: 2001/08/18 21:51:59 "; 17#endif /* not lint */ 18#else 19__RCSID("$NetBSD$"); 20#endif 21 22#include <sys/types.h> 23#include <sys/queue.h> 24#ifdef HAVE_SYS_SELECT_H 25#include <sys/select.h> 26#endif 27#include <sys/time.h> 28 29#include <bitstring.h> 30#include <errno.h> 31#include <fcntl.h> 32#include <signal.h> 33#include <stdio.h> 34#include <stdlib.h> 35#include <string.h> 36#include <termios.h> 37#include <unistd.h> 38 39#include "../common/common.h" 40#include "../ex/script.h" 41#include "cl.h" 42 43/* Pollution by Solaris curses. */ 44#undef columns 45#undef lines 46 47static input_t cl_read __P((SCR *, 48 u_int32_t, char *, size_t, int *, struct timeval *)); 49static int cl_resize __P((SCR *, size_t, size_t)); 50 51/* 52 * cl_event -- 53 * Return a single event. 54 * 55 * PUBLIC: int cl_event __P((SCR *, EVENT *, u_int32_t, int)); 56 */ 57int 58cl_event(SCR *sp, EVENT *evp, u_int32_t flags, int ms) 59{ 60 struct timeval t, *tp; 61 CL_PRIVATE *clp; 62 size_t lines, columns; 63 int changed, nr = 0; 64 const CHAR_T *wp; 65 size_t wlen; 66 int rc; 67 68 /* 69 * Queue signal based events. We never clear SIGHUP or SIGTERM events, 70 * so that we just keep returning them until the editor dies. 71 */ 72 clp = CLP(sp); 73retest: if (LF_ISSET(EC_INTERRUPT) || F_ISSET(clp, CL_SIGINT)) { 74 if (F_ISSET(clp, CL_SIGINT)) { 75 F_CLR(clp, CL_SIGINT); 76 evp->e_event = E_INTERRUPT; 77 } else 78 evp->e_event = E_TIMEOUT; 79 return (0); 80 } 81 if (F_ISSET(clp, CL_SIGHUP | CL_SIGTERM | CL_SIGWINCH)) { 82 if (F_ISSET(clp, CL_SIGHUP)) { 83 evp->e_event = E_SIGHUP; 84 return (0); 85 } 86 if (F_ISSET(clp, CL_SIGTERM)) { 87 evp->e_event = E_SIGTERM; 88 return (0); 89 } 90 if (F_ISSET(clp, CL_SIGWINCH)) { 91 F_CLR(clp, CL_SIGWINCH); 92 if (cl_ssize(sp, 1, &lines, &columns, &changed)) 93 return (1); 94 if (changed) { 95 (void)cl_resize(sp, lines, columns); 96 evp->e_event = E_WRESIZE; 97 return (0); 98 } 99 /* No real change, ignore the signal. */ 100 } 101 } 102 103 /* Set timer. */ 104 if (ms == 0) 105 tp = NULL; 106 else { 107 t.tv_sec = ms / 1000; 108 t.tv_usec = (ms % 1000) * 1000; 109 tp = &t; 110 } 111 112 /* Read input characters. */ 113read: 114 switch (cl_read(sp, LF_ISSET(EC_QUOTED | EC_RAW), 115 clp->ibuf + clp->skip, SIZE(clp->ibuf) - clp->skip, &nr, tp)) { 116 case INP_OK: 117 rc = INPUT2INT5(sp, clp->cw, clp->ibuf, nr + clp->skip, 118 wp, wlen); 119 evp->e_csp = __UNCONST(wp); 120 evp->e_len = wlen; 121 evp->e_event = E_STRING; 122 if (rc < 0) { 123 int n = -rc; 124 memmove(clp->ibuf, clp->ibuf + nr + clp->skip - n, n); 125 clp->skip = n; 126 if (wlen == 0) 127 goto read; 128 } else if (rc == 0) 129 clp->skip = 0; 130 else 131 msgq(sp, M_ERR, "323|Invalid input. Truncated."); 132 break; 133 case INP_ERR: 134 evp->e_event = E_ERR; 135 break; 136 case INP_EOF: 137 evp->e_event = E_EOF; 138 break; 139 case INP_INTR: 140 goto retest; 141 case INP_TIMEOUT: 142 evp->e_event = E_TIMEOUT; 143 break; 144 default: 145 abort(); 146 } 147 return (0); 148} 149 150/* 151 * cl_read -- 152 * Read characters from the input. 153 */ 154static input_t 155cl_read(SCR *sp, u_int32_t flags, char *bp, size_t blen, int *nrp, struct timeval *tp) 156{ 157 struct termios term1, term2; 158 struct timeval poll; 159 CL_PRIVATE *clp; 160 GS *gp; 161 fd_set rdfd; 162 input_t rval; 163 int maxfd, nr, term_reset; 164 165 gp = sp->gp; 166 clp = CLP(sp); 167 term_reset = 0; 168 169 /* 170 * 1: A read from a file or a pipe. In this case, the reads 171 * never timeout regardless. This means that we can hang 172 * when trying to complete a map, but we're going to hang 173 * on the next read anyway. 174 */ 175 if (!F_ISSET(clp, CL_STDIN_TTY)) { 176 switch (nr = read(STDIN_FILENO, bp, blen)) { 177 case 0: 178 return (INP_EOF); 179 case -1: 180 goto err; 181 default: 182 *nrp = nr; 183 return (INP_OK); 184 } 185 /* NOTREACHED */ 186 } 187 188 /* 189 * 2: A read with an associated timeout, e.g., trying to complete 190 * a map sequence. If input exists, we fall into #3. 191 */ 192 FD_ZERO(&rdfd); 193 poll.tv_sec = 0; 194 poll.tv_usec = 0; 195 if (tp != NULL) { 196 FD_SET(STDIN_FILENO, &rdfd); 197 switch (select(STDIN_FILENO + 1, 198 &rdfd, NULL, NULL, tp == NULL ? &poll : tp)) { 199 case 0: 200 return (INP_TIMEOUT); 201 case -1: 202 goto err; 203 default: 204 break; 205 } 206 } 207 208 /* 209 * The user can enter a key in the editor to quote a character. If we 210 * get here and the next key is supposed to be quoted, do what we can. 211 * Reset the tty so that the user can enter a ^C, ^Q, ^S. There's an 212 * obvious race here, when the key has already been entered, but there's 213 * nothing that we can do to fix that problem. 214 * 215 * The editor can ask for the next literal character even thought it's 216 * generally running in line-at-a-time mode. Do what we can. 217 */ 218 if (LF_ISSET(EC_QUOTED | EC_RAW) && !tcgetattr(STDIN_FILENO, &term1)) { 219 term_reset = 1; 220 if (LF_ISSET(EC_QUOTED)) { 221 term2 = term1; 222 term2.c_lflag &= ~ISIG; 223 term2.c_iflag &= ~(IXON | IXOFF); 224 (void)tcsetattr(STDIN_FILENO, 225 TCSASOFT | TCSADRAIN, &term2); 226 } else 227 (void)tcsetattr(STDIN_FILENO, 228 TCSASOFT | TCSADRAIN, &clp->vi_enter); 229 } 230 231 /* 232 * 3: Wait for input. 233 * 234 * Select on the command input and scripting window file descriptors. 235 * It's ugly that we wait on scripting file descriptors here, but it's 236 * the only way to keep from locking out scripting windows. 237 */ 238 if (F_ISSET(gp, G_SCRWIN)) { 239 FD_ZERO(&rdfd); 240 FD_SET(STDIN_FILENO, &rdfd); 241 maxfd = STDIN_FILENO; 242 if (sscr_check_input(sp, &rdfd, maxfd)) 243 goto err; 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. */ 281err: 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 */ 307static int 308cl_resize(SCR *sp, size_t lines, size_t columns) 309{ 310 int rval; 311 312 rval = api_opts_set(sp, L("lines"), NULL, lines, 0); 313 if (api_opts_set(sp, L("columns"), NULL, columns, 0)) 314 rval = 1; 315 return (rval); 316} 317