1 /* $NetBSD: el.c,v 1.100 2021/08/15 10:08:41 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Christos Zoulas of Cornell University. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include "config.h" 36 #if !defined(lint) && !defined(SCCSID) 37 #if 0 38 static char sccsid[] = "@(#)el.c 8.2 (Berkeley) 1/3/94"; 39 #else 40 __RCSID("$NetBSD: el.c,v 1.100 2021/08/15 10:08:41 christos Exp $"); 41 #endif 42 #endif /* not lint && not SCCSID */ 43 44 /* 45 * el.c: EditLine interface functions 46 */ 47 #include <sys/types.h> 48 #include <sys/param.h> 49 #include <ctype.h> 50 #include <langinfo.h> 51 #include <locale.h> 52 #include <stdarg.h> 53 #include <stdlib.h> 54 #include <string.h> 55 56 #include "el.h" 57 #include "parse.h" 58 #include "read.h" 59 60 /* el_init(): 61 * Initialize editline and set default parameters. 62 */ 63 EditLine * 64 el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr) 65 { 66 return el_init_fd(prog, fin, fout, ferr, fileno(fin), fileno(fout), 67 fileno(ferr)); 68 } 69 70 libedit_private EditLine * 71 el_init_internal(const char *prog, FILE *fin, FILE *fout, FILE *ferr, 72 int fdin, int fdout, int fderr, int flags) 73 { 74 EditLine *el = el_calloc(1, sizeof(*el)); 75 76 if (el == NULL) 77 return NULL; 78 79 el->el_infile = fin; 80 el->el_outfile = fout; 81 el->el_errfile = ferr; 82 83 el->el_infd = fdin; 84 el->el_outfd = fdout; 85 el->el_errfd = fderr; 86 87 el->el_prog = wcsdup(ct_decode_string(prog, &el->el_scratch)); 88 if (el->el_prog == NULL) { 89 el_free(el); 90 return NULL; 91 } 92 93 /* 94 * Initialize all the modules. Order is important!!! 95 */ 96 el->el_flags = flags; 97 98 if (terminal_init(el) == -1) { 99 el_free(el->el_prog); 100 el_free(el); 101 return NULL; 102 } 103 (void) keymacro_init(el); 104 (void) map_init(el); 105 if (tty_init(el) == -1) 106 el->el_flags |= NO_TTY; 107 (void) ch_init(el); 108 (void) search_init(el); 109 (void) hist_init(el); 110 (void) prompt_init(el); 111 (void) sig_init(el); 112 (void) literal_init(el); 113 if (read_init(el) == -1) { 114 el_end(el); 115 return NULL; 116 } 117 return el; 118 } 119 120 EditLine * 121 el_init_fd(const char *prog, FILE *fin, FILE *fout, FILE *ferr, 122 int fdin, int fdout, int fderr) 123 { 124 return el_init_internal(prog, fin, fout, ferr, fdin, fdout, fderr, 0); 125 } 126 127 /* el_end(): 128 * Clean up. 129 */ 130 void 131 el_end(EditLine *el) 132 { 133 134 if (el == NULL) 135 return; 136 137 el_reset(el); 138 139 terminal_end(el); 140 keymacro_end(el); 141 map_end(el); 142 if (!(el->el_flags & NO_TTY)) 143 tty_end(el, TCSAFLUSH); 144 ch_end(el); 145 read_end(el->el_read); 146 search_end(el); 147 hist_end(el); 148 prompt_end(el); 149 sig_end(el); 150 literal_end(el); 151 152 el_free(el->el_prog); 153 el_free(el->el_visual.cbuff); 154 el_free(el->el_visual.wbuff); 155 el_free(el->el_scratch.cbuff); 156 el_free(el->el_scratch.wbuff); 157 el_free(el->el_lgcyconv.cbuff); 158 el_free(el->el_lgcyconv.wbuff); 159 el_free(el); 160 } 161 162 163 /* el_reset(): 164 * Reset the tty and the parser 165 */ 166 void 167 el_reset(EditLine *el) 168 { 169 170 tty_cookedmode(el); 171 ch_reset(el); /* XXX: Do we want that? */ 172 } 173 174 175 /* el_set(): 176 * set the editline parameters 177 */ 178 int 179 el_wset(EditLine *el, int op, ...) 180 { 181 va_list ap; 182 int rv = 0; 183 184 if (el == NULL) 185 return -1; 186 va_start(ap, op); 187 188 switch (op) { 189 case EL_PROMPT: 190 case EL_RPROMPT: { 191 el_pfunc_t p = va_arg(ap, el_pfunc_t); 192 193 rv = prompt_set(el, p, 0, op, 1); 194 break; 195 } 196 197 case EL_RESIZE: { 198 el_zfunc_t p = va_arg(ap, el_zfunc_t); 199 void *arg = va_arg(ap, void *); 200 rv = ch_resizefun(el, p, arg); 201 break; 202 } 203 204 case EL_ALIAS_TEXT: { 205 el_afunc_t p = va_arg(ap, el_afunc_t); 206 void *arg = va_arg(ap, void *); 207 rv = ch_aliasfun(el, p, arg); 208 break; 209 } 210 211 case EL_PROMPT_ESC: 212 case EL_RPROMPT_ESC: { 213 el_pfunc_t p = va_arg(ap, el_pfunc_t); 214 int c = va_arg(ap, int); 215 216 rv = prompt_set(el, p, (wchar_t)c, op, 1); 217 break; 218 } 219 220 case EL_TERMINAL: 221 rv = terminal_set(el, va_arg(ap, char *)); 222 break; 223 224 case EL_EDITOR: 225 rv = map_set_editor(el, va_arg(ap, wchar_t *)); 226 break; 227 228 case EL_SIGNAL: 229 if (va_arg(ap, int)) 230 el->el_flags |= HANDLE_SIGNALS; 231 else 232 el->el_flags &= ~HANDLE_SIGNALS; 233 break; 234 235 case EL_BIND: 236 case EL_TELLTC: 237 case EL_SETTC: 238 case EL_ECHOTC: 239 case EL_SETTY: 240 { 241 const wchar_t *argv[20]; 242 int i; 243 244 for (i = 1; i < (int)__arraycount(argv); i++) 245 if ((argv[i] = va_arg(ap, wchar_t *)) == NULL) 246 break; 247 248 switch (op) { 249 case EL_BIND: 250 argv[0] = L"bind"; 251 rv = map_bind(el, i, argv); 252 break; 253 254 case EL_TELLTC: 255 argv[0] = L"telltc"; 256 rv = terminal_telltc(el, i, argv); 257 break; 258 259 case EL_SETTC: 260 argv[0] = L"settc"; 261 rv = terminal_settc(el, i, argv); 262 break; 263 264 case EL_ECHOTC: 265 argv[0] = L"echotc"; 266 rv = terminal_echotc(el, i, argv); 267 break; 268 269 case EL_SETTY: 270 argv[0] = L"setty"; 271 rv = tty_stty(el, i, argv); 272 break; 273 274 default: 275 rv = -1; 276 EL_ABORT((el->el_errfile, "Bad op %d\n", op)); 277 break; 278 } 279 break; 280 } 281 282 case EL_ADDFN: 283 { 284 wchar_t *name = va_arg(ap, wchar_t *); 285 wchar_t *help = va_arg(ap, wchar_t *); 286 el_func_t func = va_arg(ap, el_func_t); 287 288 rv = map_addfunc(el, name, help, func); 289 break; 290 } 291 292 case EL_HIST: 293 { 294 hist_fun_t func = va_arg(ap, hist_fun_t); 295 void *ptr = va_arg(ap, void *); 296 297 rv = hist_set(el, func, ptr); 298 if (MB_CUR_MAX == 1) 299 el->el_flags &= ~NARROW_HISTORY; 300 break; 301 } 302 303 case EL_SAFEREAD: 304 if (va_arg(ap, int)) 305 el->el_flags |= FIXIO; 306 else 307 el->el_flags &= ~FIXIO; 308 rv = 0; 309 break; 310 311 case EL_EDITMODE: 312 if (va_arg(ap, int)) 313 el->el_flags &= ~EDIT_DISABLED; 314 else 315 el->el_flags |= EDIT_DISABLED; 316 rv = 0; 317 break; 318 319 case EL_GETCFN: 320 { 321 el_rfunc_t rc = va_arg(ap, el_rfunc_t); 322 rv = el_read_setfn(el->el_read, rc); 323 break; 324 } 325 326 case EL_CLIENTDATA: 327 el->el_data = va_arg(ap, void *); 328 break; 329 330 case EL_UNBUFFERED: 331 rv = va_arg(ap, int); 332 if (rv && !(el->el_flags & UNBUFFERED)) { 333 el->el_flags |= UNBUFFERED; 334 read_prepare(el); 335 } else if (!rv && (el->el_flags & UNBUFFERED)) { 336 el->el_flags &= ~UNBUFFERED; 337 read_finish(el); 338 } 339 rv = 0; 340 break; 341 342 case EL_PREP_TERM: 343 rv = va_arg(ap, int); 344 if (rv) 345 (void) tty_rawmode(el); 346 else 347 (void) tty_cookedmode(el); 348 rv = 0; 349 break; 350 351 case EL_SETFP: 352 { 353 FILE *fp; 354 int what; 355 356 what = va_arg(ap, int); 357 fp = va_arg(ap, FILE *); 358 359 rv = 0; 360 switch (what) { 361 case 0: 362 el->el_infile = fp; 363 el->el_infd = fileno(fp); 364 break; 365 case 1: 366 el->el_outfile = fp; 367 el->el_outfd = fileno(fp); 368 break; 369 case 2: 370 el->el_errfile = fp; 371 el->el_errfd = fileno(fp); 372 break; 373 default: 374 rv = -1; 375 break; 376 } 377 break; 378 } 379 380 case EL_REFRESH: 381 re_clear_display(el); 382 re_refresh(el); 383 terminal__flush(el); 384 break; 385 386 default: 387 rv = -1; 388 break; 389 } 390 391 va_end(ap); 392 return rv; 393 } 394 395 396 /* el_get(): 397 * retrieve the editline parameters 398 */ 399 int 400 el_wget(EditLine *el, int op, ...) 401 { 402 va_list ap; 403 int rv; 404 405 if (el == NULL) 406 return -1; 407 408 va_start(ap, op); 409 410 switch (op) { 411 case EL_PROMPT: 412 case EL_RPROMPT: { 413 el_pfunc_t *p = va_arg(ap, el_pfunc_t *); 414 rv = prompt_get(el, p, 0, op); 415 break; 416 } 417 case EL_PROMPT_ESC: 418 case EL_RPROMPT_ESC: { 419 el_pfunc_t *p = va_arg(ap, el_pfunc_t *); 420 wchar_t *c = va_arg(ap, wchar_t *); 421 422 rv = prompt_get(el, p, c, op); 423 break; 424 } 425 426 case EL_EDITOR: 427 rv = map_get_editor(el, va_arg(ap, const wchar_t **)); 428 break; 429 430 case EL_SIGNAL: 431 *va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS); 432 rv = 0; 433 break; 434 435 case EL_EDITMODE: 436 *va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED); 437 rv = 0; 438 break; 439 440 case EL_SAFEREAD: 441 *va_arg(ap, int *) = (el->el_flags & FIXIO); 442 rv = 0; 443 break; 444 445 case EL_TERMINAL: 446 terminal_get(el, va_arg(ap, const char **)); 447 rv = 0; 448 break; 449 450 case EL_GETTC: 451 { 452 static char name[] = "gettc"; 453 char *argv[3]; 454 argv[0] = name; 455 argv[1] = va_arg(ap, char *); 456 argv[2] = va_arg(ap, void *); 457 rv = terminal_gettc(el, 3, argv); 458 break; 459 } 460 461 case EL_GETCFN: 462 *va_arg(ap, el_rfunc_t *) = el_read_getfn(el->el_read); 463 rv = 0; 464 break; 465 466 case EL_CLIENTDATA: 467 *va_arg(ap, void **) = el->el_data; 468 rv = 0; 469 break; 470 471 case EL_UNBUFFERED: 472 *va_arg(ap, int *) = (el->el_flags & UNBUFFERED) != 0; 473 rv = 0; 474 break; 475 476 case EL_GETFP: 477 { 478 int what; 479 FILE **fpp; 480 481 what = va_arg(ap, int); 482 fpp = va_arg(ap, FILE **); 483 rv = 0; 484 switch (what) { 485 case 0: 486 *fpp = el->el_infile; 487 break; 488 case 1: 489 *fpp = el->el_outfile; 490 break; 491 case 2: 492 *fpp = el->el_errfile; 493 break; 494 default: 495 rv = -1; 496 break; 497 } 498 break; 499 } 500 default: 501 rv = -1; 502 break; 503 } 504 va_end(ap); 505 506 return rv; 507 } 508 509 510 /* el_line(): 511 * Return editing info 512 */ 513 const LineInfoW * 514 el_wline(EditLine *el) 515 { 516 517 return (const LineInfoW *)(void *)&el->el_line; 518 } 519 520 521 /* el_source(): 522 * Source a file 523 */ 524 int 525 el_source(EditLine *el, const char *fname) 526 { 527 FILE *fp; 528 size_t len; 529 ssize_t slen; 530 char *ptr; 531 char *path = NULL; 532 const wchar_t *dptr; 533 int error = 0; 534 535 fp = NULL; 536 if (fname == NULL) { 537 #ifdef HAVE_ISSETUGID 538 if (issetugid()) 539 return -1; 540 541 if ((fname = getenv("EDITRC")) == NULL) { 542 static const char elpath[] = "/.editrc"; 543 size_t plen = sizeof(elpath); 544 545 if ((ptr = getenv("HOME")) == NULL) 546 return -1; 547 plen += strlen(ptr); 548 if ((path = el_calloc(plen, sizeof(*path))) == NULL) 549 return -1; 550 (void)snprintf(path, plen, "%s%s", ptr, 551 elpath + (*ptr == '\0')); 552 fname = path; 553 } 554 #else 555 /* 556 * If issetugid() is missing, always return an error, in order 557 * to keep from inadvertently opening up the user to a security 558 * hole. 559 */ 560 return -1; 561 #endif 562 } 563 if (fname[0] == '\0') 564 return -1; 565 566 if (fp == NULL) 567 fp = fopen(fname, "r"); 568 if (fp == NULL) { 569 el_free(path); 570 return -1; 571 } 572 573 ptr = NULL; 574 len = 0; 575 while ((slen = getline(&ptr, &len, fp)) != -1) { 576 if (*ptr == '\n') 577 continue; /* Empty line. */ 578 if (slen > 0 && ptr[--slen] == '\n') 579 ptr[slen] = '\0'; 580 581 dptr = ct_decode_string(ptr, &el->el_scratch); 582 if (!dptr) 583 continue; 584 /* loop until first non-space char or EOL */ 585 while (*dptr != '\0' && iswspace(*dptr)) 586 dptr++; 587 if (*dptr == '#') 588 continue; /* ignore, this is a comment line */ 589 if ((error = parse_line(el, dptr)) == -1) 590 break; 591 } 592 free(ptr); 593 594 el_free(path); 595 (void) fclose(fp); 596 return error; 597 } 598 599 600 /* el_resize(): 601 * Called from program when terminal is resized 602 */ 603 void 604 el_resize(EditLine *el) 605 { 606 int lins, cols; 607 sigset_t oset, nset; 608 609 (void) sigemptyset(&nset); 610 (void) sigaddset(&nset, SIGWINCH); 611 (void) sigprocmask(SIG_BLOCK, &nset, &oset); 612 613 /* get the correct window size */ 614 if (terminal_get_size(el, &lins, &cols)) 615 terminal_change_size(el, lins, cols); 616 617 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 618 } 619 620 621 /* el_beep(): 622 * Called from the program to beep 623 */ 624 void 625 el_beep(EditLine *el) 626 { 627 628 terminal_beep(el); 629 } 630 631 632 /* el_editmode() 633 * Set the state of EDIT_DISABLED from the `edit' command. 634 */ 635 libedit_private int 636 /*ARGSUSED*/ 637 el_editmode(EditLine *el, int argc, const wchar_t **argv) 638 { 639 const wchar_t *how; 640 641 if (argv == NULL || argc != 2 || argv[1] == NULL) 642 return -1; 643 644 how = argv[1]; 645 if (wcscmp(how, L"on") == 0) { 646 el->el_flags &= ~EDIT_DISABLED; 647 tty_rawmode(el); 648 } else if (wcscmp(how, L"off") == 0) { 649 tty_cookedmode(el); 650 el->el_flags |= EDIT_DISABLED; 651 } 652 else { 653 (void) fprintf(el->el_errfile, "edit: Bad value `%ls'.\n", 654 how); 655 return -1; 656 } 657 return 0; 658 } 659