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