1/* $OpenBSD: read.c,v 1.49 2021/08/13 10:21:25 schwarze Exp $ */ 2/* $NetBSD: read.c,v 1.100 2016/05/24 19:31:27 christos Exp $ */ 3 4/*- 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Christos Zoulas of Cornell University. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "config.h" 37 38/* 39 * read.c: Clean this junk up! This is horrible code. 40 * Terminal read functions 41 */ 42#include <sys/ioctl.h> 43 44#include <ctype.h> 45#include <errno.h> 46#include <limits.h> 47#include <stdlib.h> 48#include <string.h> 49#include <unistd.h> 50 51#include "el.h" 52#include "fcns.h" 53#include "read.h" 54 55#define EL_MAXMACRO 10 56 57struct macros { 58 wchar_t **macro; 59 int level; 60 int offset; 61}; 62 63struct el_read_t { 64 struct macros macros; 65 el_rfunc_t read_char; /* Function to read a character. */ 66 int read_errno; 67}; 68 69static int read_char(EditLine *, wchar_t *); 70static int read_getcmd(EditLine *, el_action_t *, wchar_t *); 71static void read_clearmacros(struct macros *); 72static void read_pop(struct macros *); 73 74/* read_init(): 75 * Initialize the read stuff 76 */ 77protected int 78read_init(EditLine *el) 79{ 80 struct macros *ma; 81 82 if ((el->el_read = malloc(sizeof(*el->el_read))) == NULL) 83 return -1; 84 85 ma = &el->el_read->macros; 86 if ((ma->macro = reallocarray(NULL, EL_MAXMACRO, 87 sizeof(*ma->macro))) == NULL) { 88 free(el->el_read); 89 return -1; 90 } 91 ma->level = -1; 92 ma->offset = 0; 93 94 /* builtin read_char */ 95 el->el_read->read_char = read_char; 96 return 0; 97} 98 99/* el_read_end(): 100 * Free the data structures used by the read stuff. 101 */ 102protected void 103read_end(struct el_read_t *el_read) 104{ 105 read_clearmacros(&el_read->macros); 106 free(el_read->macros.macro); 107 el_read->macros.macro = NULL; 108} 109 110/* el_read_setfn(): 111 * Set the read char function to the one provided. 112 * If it is set to EL_BUILTIN_GETCFN, then reset to the builtin one. 113 */ 114protected int 115el_read_setfn(struct el_read_t *el_read, el_rfunc_t rc) 116{ 117 el_read->read_char = (rc == EL_BUILTIN_GETCFN) ? read_char : rc; 118 return 0; 119} 120 121 122/* el_read_getfn(): 123 * return the current read char function, or EL_BUILTIN_GETCFN 124 * if it is the default one 125 */ 126protected el_rfunc_t 127el_read_getfn(struct el_read_t *el_read) 128{ 129 return el_read->read_char == read_char ? 130 EL_BUILTIN_GETCFN : el_read->read_char; 131} 132 133 134/* el_push(): 135 * Push a macro 136 */ 137void 138el_wpush(EditLine *el, const wchar_t *str) 139{ 140 struct macros *ma = &el->el_read->macros; 141 142 if (str != NULL && ma->level + 1 < EL_MAXMACRO) { 143 ma->level++; 144 if ((ma->macro[ma->level] = wcsdup(str)) != NULL) 145 return; 146 ma->level--; 147 } 148 terminal_beep(el); 149 terminal__flush(el); 150} 151 152 153/* read_getcmd(): 154 * Get next command from the input stream, 155 * return 0 on success or -1 on EOF or error. 156 * Character values > 255 are not looked up in the map, but inserted. 157 */ 158static int 159read_getcmd(EditLine *el, el_action_t *cmdnum, wchar_t *ch) 160{ 161 static const wchar_t meta = (wchar_t)0x80; 162 el_action_t cmd; 163 int num; 164 165 do { 166 if ((num = el_wgetc(el, ch)) != 1) 167 return -1; 168 169#ifdef KANJI 170 if ((*ch & meta)) { 171 el->el_state.metanext = 0; 172 cmd = CcViMap[' ']; 173 break; 174 } else 175#endif /* KANJI */ 176 177 if (el->el_state.metanext) { 178 el->el_state.metanext = 0; 179 *ch |= meta; 180 } 181 if (*ch >= N_KEYS) 182 cmd = ED_INSERT; 183 else 184 cmd = el->el_map.current[(unsigned char) *ch]; 185 if (cmd == ED_SEQUENCE_LEAD_IN) { 186 keymacro_value_t val; 187 switch (keymacro_get(el, ch, &val)) { 188 case XK_CMD: 189 cmd = val.cmd; 190 break; 191 case XK_STR: 192 el_wpush(el, val.str); 193 break; 194 case XK_NOD: 195 return -1; 196 default: 197 EL_ABORT((el->el_errfile, "Bad XK_ type \n")); 198 break; 199 } 200 } 201 } while (cmd == ED_SEQUENCE_LEAD_IN); 202 *cmdnum = cmd; 203 return 0; 204} 205 206/* read_char(): 207 * Read a character from the tty. 208 */ 209static int 210read_char(EditLine *el, wchar_t *cp) 211{ 212 char cbuf[MB_LEN_MAX]; 213 int cbp = 0; 214 215 again: 216 el->el_signal->sig_no = 0; 217 switch (read(el->el_infd, cbuf + cbp, 1)) { 218 case -1: 219 if (errno == EINTR) { 220 switch (el->el_signal->sig_no) { 221 case SIGCONT: 222 el_set(el, EL_REFRESH); 223 /*FALLTHROUGH*/ 224 case SIGWINCH: 225 sig_set(el); 226 goto again; 227 default: 228 break; 229 } 230 } 231 *cp = L'\0'; 232 return -1; 233 case 0: 234 *cp = L'\0'; 235 return 0; 236 default: 237 break; 238 } 239 240 for (;;) { 241 mbstate_t mbs; 242 243 ++cbp; 244 /* This only works because UTF8 is stateless. */ 245 memset(&mbs, 0, sizeof(mbs)); 246 switch (mbrtowc(cp, cbuf, cbp, &mbs)) { 247 case (size_t)-1: 248 if (cbp > 1) { 249 /* 250 * Invalid sequence, discard all bytes 251 * except the last one. 252 */ 253 cbuf[0] = cbuf[cbp - 1]; 254 cbp = 0; 255 break; 256 } else { 257 /* Invalid byte, discard it. */ 258 cbp = 0; 259 goto again; 260 } 261 case (size_t)-2: 262 /* 263 * We don't support other multibyte charsets. 264 * The second condition shouldn't happen 265 * and is here merely for additional safety. 266 */ 267 if ((el->el_flags & CHARSET_IS_UTF8) == 0 || 268 cbp >= MB_LEN_MAX) { 269 errno = EILSEQ; 270 *cp = L'\0'; 271 return -1; 272 } 273 /* Incomplete sequence, read another byte. */ 274 goto again; 275 default: 276 /* Valid character, process it. */ 277 return 1; 278 } 279 } 280} 281 282/* read_pop(): 283 * Pop a macro from the stack 284 */ 285static void 286read_pop(struct macros *ma) 287{ 288 int i; 289 290 free(ma->macro[0]); 291 for (i = 0; i < ma->level; i++) 292 ma->macro[i] = ma->macro[i + 1]; 293 ma->level--; 294 ma->offset = 0; 295} 296 297static void 298read_clearmacros(struct macros *ma) 299{ 300 while (ma->level >= 0) 301 free(ma->macro[ma->level--]); 302 ma->offset = 0; 303} 304 305/* el_wgetc(): 306 * Read a wide character 307 */ 308int 309el_wgetc(EditLine *el, wchar_t *cp) 310{ 311 struct macros *ma = &el->el_read->macros; 312 int num_read; 313 314 terminal__flush(el); 315 for (;;) { 316 if (ma->level < 0) 317 break; 318 319 if (ma->macro[0][ma->offset] == '\0') { 320 read_pop(ma); 321 continue; 322 } 323 324 *cp = ma->macro[0][ma->offset++]; 325 326 if (ma->macro[0][ma->offset] == '\0') { 327 /* Needed for QuoteMode On */ 328 read_pop(ma); 329 } 330 331 return 1; 332 } 333 334 if (tty_rawmode(el) < 0)/* make sure the tty is set up correctly */ 335 return 0; 336 337 num_read = (*el->el_read->read_char)(el, cp); 338 339 /* 340 * Remember the original reason of a read failure 341 * such that el_wgets() can restore it after doing 342 * various cleanup operation that might change errno. 343 */ 344 if (num_read < 0) 345 el->el_read->read_errno = errno; 346 347 return num_read; 348} 349 350protected void 351read_prepare(EditLine *el) 352{ 353 if (el->el_flags & HANDLE_SIGNALS) 354 sig_set(el); 355 if (el->el_flags & NO_TTY) 356 return; 357 if ((el->el_flags & (UNBUFFERED|EDIT_DISABLED)) == UNBUFFERED) 358 tty_rawmode(el); 359 360 /* This is relatively cheap, and things go terribly wrong if 361 we have the wrong size. */ 362 el_resize(el); 363 re_clear_display(el); /* reset the display stuff */ 364 ch_reset(el); 365 re_refresh(el); /* print the prompt */ 366 367 if (el->el_flags & UNBUFFERED) 368 terminal__flush(el); 369} 370 371protected void 372read_finish(EditLine *el) 373{ 374 if ((el->el_flags & UNBUFFERED) == 0) 375 (void) tty_cookedmode(el); 376 if (el->el_flags & HANDLE_SIGNALS) 377 sig_clr(el); 378} 379 380const wchar_t * 381el_wgets(EditLine *el, int *nread) 382{ 383 int retval; 384 el_action_t cmdnum = 0; 385 int num; /* how many chars we have read at NL */ 386 wchar_t wc; 387 wchar_t ch, *cp; 388 int crlf = 0; 389 int nrb; 390 391 if (nread == NULL) 392 nread = &nrb; 393 *nread = 0; 394 el->el_read->read_errno = 0; 395 396 if (el->el_flags & NO_TTY) { 397 size_t idx; 398 399 cp = el->el_line.buffer; 400 while ((num = (*el->el_read->read_char)(el, &wc)) == 1) { 401 *cp = wc; 402 /* make sure there is space for next character */ 403 if (cp + 1 >= el->el_line.limit) { 404 idx = (cp - el->el_line.buffer); 405 if (!ch_enlargebufs(el, 2)) 406 break; 407 cp = &el->el_line.buffer[idx]; 408 } 409 cp++; 410 if (el->el_flags & UNBUFFERED) 411 break; 412 if (cp[-1] == '\r' || cp[-1] == '\n') 413 break; 414 } 415 if (num == -1 && errno == EINTR) 416 cp = el->el_line.buffer; 417 goto noedit; 418 } 419 420 421#ifdef FIONREAD 422 if (el->el_tty.t_mode == EX_IO && el->el_read->macros.level < 0) { 423 int chrs = 0; 424 425 (void) ioctl(el->el_infd, FIONREAD, &chrs); 426 if (chrs == 0) { 427 if (tty_rawmode(el) < 0) { 428 errno = 0; 429 *nread = 0; 430 return NULL; 431 } 432 } 433 } 434#endif /* FIONREAD */ 435 436 if ((el->el_flags & UNBUFFERED) == 0) 437 read_prepare(el); 438 439 if (el->el_flags & EDIT_DISABLED) { 440 size_t idx; 441 442 if ((el->el_flags & UNBUFFERED) == 0) 443 cp = el->el_line.buffer; 444 else 445 cp = el->el_line.lastchar; 446 447 terminal__flush(el); 448 449 while ((num = (*el->el_read->read_char)(el, &wc)) == 1) { 450 *cp = wc; 451 /* make sure there is space next character */ 452 if (cp + 1 >= el->el_line.limit) { 453 idx = (cp - el->el_line.buffer); 454 if (!ch_enlargebufs(el, 2)) 455 break; 456 cp = &el->el_line.buffer[idx]; 457 } 458 cp++; 459 crlf = cp[-1] == '\r' || cp[-1] == '\n'; 460 if (el->el_flags & UNBUFFERED) 461 break; 462 if (crlf) 463 break; 464 } 465 if (num == -1 && errno == EINTR) 466 cp = el->el_line.buffer; 467 goto noedit; 468 } 469 470 for (num = -1; num == -1;) { /* while still editing this line */ 471 /* if EOF or error */ 472 if (read_getcmd(el, &cmdnum, &ch) == -1) 473 break; 474 if ((int)cmdnum >= el->el_map.nfunc) /* BUG CHECK command */ 475 continue; /* try again */ 476 /* now do the real command */ 477 /* vi redo needs these way down the levels... */ 478 el->el_state.thiscmd = cmdnum; 479 el->el_state.thisch = ch; 480 if (el->el_map.type == MAP_VI && 481 el->el_map.current == el->el_map.key && 482 el->el_chared.c_redo.pos < el->el_chared.c_redo.lim) { 483 if (cmdnum == VI_DELETE_PREV_CHAR && 484 el->el_chared.c_redo.pos != el->el_chared.c_redo.buf 485 && iswprint(el->el_chared.c_redo.pos[-1])) 486 el->el_chared.c_redo.pos--; 487 else 488 *el->el_chared.c_redo.pos++ = ch; 489 } 490 retval = (*el->el_map.func[cmdnum]) (el, ch); 491 492 /* save the last command here */ 493 el->el_state.lastcmd = cmdnum; 494 495 /* use any return value */ 496 switch (retval) { 497 case CC_CURSOR: 498 re_refresh_cursor(el); 499 break; 500 501 case CC_REDISPLAY: 502 re_clear_lines(el); 503 re_clear_display(el); 504 /* FALLTHROUGH */ 505 506 case CC_REFRESH: 507 re_refresh(el); 508 break; 509 510 case CC_REFRESH_BEEP: 511 re_refresh(el); 512 terminal_beep(el); 513 break; 514 515 case CC_NORM: /* normal char */ 516 break; 517 518 case CC_ARGHACK: /* Suggested by Rich Salz */ 519 /* <rsalz@pineapple.bbn.com> */ 520 continue; /* keep going... */ 521 522 case CC_EOF: /* end of file typed */ 523 if ((el->el_flags & UNBUFFERED) == 0) 524 num = 0; 525 else if (num == -1) { 526 *el->el_line.lastchar++ = CONTROL('d'); 527 el->el_line.cursor = el->el_line.lastchar; 528 num = 1; 529 } 530 break; 531 532 case CC_NEWLINE: /* normal end of line */ 533 num = (int)(el->el_line.lastchar - el->el_line.buffer); 534 break; 535 536 case CC_FATAL: /* fatal error, reset to known state */ 537 /* put (real) cursor in a known place */ 538 re_clear_display(el); /* reset the display stuff */ 539 ch_reset(el); /* reset the input pointers */ 540 read_clearmacros(&el->el_read->macros); 541 re_refresh(el); /* print the prompt again */ 542 break; 543 544 case CC_ERROR: 545 default: /* functions we don't know about */ 546 terminal_beep(el); 547 terminal__flush(el); 548 break; 549 } 550 el->el_state.argument = 1; 551 el->el_state.doingarg = 0; 552 el->el_chared.c_vcmd.action = NOP; 553 if (el->el_flags & UNBUFFERED) 554 break; 555 } 556 557 terminal__flush(el); /* flush any buffered output */ 558 /* make sure the tty is set up correctly */ 559 if ((el->el_flags & UNBUFFERED) == 0) { 560 read_finish(el); 561 *nread = num != -1 ? num : 0; 562 } else { 563 *nread = (int)(el->el_line.lastchar - el->el_line.buffer); 564 } 565 goto done; 566noedit: 567 el->el_line.cursor = el->el_line.lastchar = cp; 568 *cp = '\0'; 569 *nread = (int)(el->el_line.cursor - el->el_line.buffer); 570done: 571 if (*nread == 0) { 572 if (num == -1) { 573 *nread = -1; 574 if (el->el_read->read_errno) 575 errno = el->el_read->read_errno; 576 } 577 return NULL; 578 } else 579 return el->el_line.buffer; 580} 581