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