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 #ifndef HAVE_SECURE_GETENV 61 # ifdef HAVE___SECURE_GETENV 62 # define secure_getenv __secure_getenv 63 # define HAVE_SECURE_GETENV 1 64 # else 65 # ifdef HAVE_ISSETUGID 66 # include <unistd.h> 67 # else 68 # undef issetugid 69 # define issetugid() 1 70 # endif 71 # endif 72 #endif 73 74 #ifndef HAVE_SECURE_GETENV 75 char *secure_getenv(char const *name) 76 { 77 if (issetugid()) 78 return 0; 79 return getenv(name); 80 } 81 #endif 82 83 /* el_init(): 84 * Initialize editline and set default parameters. 85 */ 86 EditLine * 87 el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr) 88 { 89 return el_init_fd(prog, fin, fout, ferr, fileno(fin), fileno(fout), 90 fileno(ferr)); 91 } 92 93 libedit_private EditLine * 94 el_init_internal(const char *prog, FILE *fin, FILE *fout, FILE *ferr, 95 int fdin, int fdout, int fderr, int flags) 96 { 97 EditLine *el = el_calloc(1, sizeof(*el)); 98 99 if (el == NULL) 100 return NULL; 101 102 el->el_infile = fin; 103 el->el_outfile = fout; 104 el->el_errfile = ferr; 105 106 el->el_infd = fdin; 107 el->el_outfd = fdout; 108 el->el_errfd = fderr; 109 110 el->el_prog = wcsdup(ct_decode_string(prog, &el->el_scratch)); 111 if (el->el_prog == NULL) { 112 el_free(el); 113 return NULL; 114 } 115 116 /* 117 * Initialize all the modules. Order is important!!! 118 */ 119 el->el_flags = flags; 120 121 if (terminal_init(el) == -1) { 122 el_free(el->el_prog); 123 el_free(el); 124 return NULL; 125 } 126 (void) keymacro_init(el); 127 (void) map_init(el); 128 if (tty_init(el) == -1) 129 el->el_flags |= NO_TTY; 130 (void) ch_init(el); 131 (void) search_init(el); 132 (void) hist_init(el); 133 (void) prompt_init(el); 134 (void) sig_init(el); 135 (void) literal_init(el); 136 if (read_init(el) == -1) { 137 el_end(el); 138 return NULL; 139 } 140 return el; 141 } 142 143 EditLine * 144 el_init_fd(const char *prog, FILE *fin, FILE *fout, FILE *ferr, 145 int fdin, int fdout, int fderr) 146 { 147 return el_init_internal(prog, fin, fout, ferr, fdin, fdout, fderr, 0); 148 } 149 150 /* el_end(): 151 * Clean up. 152 */ 153 void 154 el_end(EditLine *el) 155 { 156 157 if (el == NULL) 158 return; 159 160 el_reset(el); 161 162 terminal_end(el); 163 keymacro_end(el); 164 map_end(el); 165 if (!(el->el_flags & NO_TTY)) 166 tty_end(el, TCSAFLUSH); 167 ch_end(el); 168 read_end(el->el_read); 169 search_end(el); 170 hist_end(el); 171 prompt_end(el); 172 sig_end(el); 173 literal_end(el); 174 175 el_free(el->el_prog); 176 el_free(el->el_visual.cbuff); 177 el_free(el->el_visual.wbuff); 178 el_free(el->el_scratch.cbuff); 179 el_free(el->el_scratch.wbuff); 180 el_free(el->el_lgcyconv.cbuff); 181 el_free(el->el_lgcyconv.wbuff); 182 el_free(el); 183 } 184 185 186 /* el_reset(): 187 * Reset the tty and the parser 188 */ 189 void 190 el_reset(EditLine *el) 191 { 192 193 tty_cookedmode(el); 194 ch_reset(el); /* XXX: Do we want that? */ 195 } 196 197 198 /* el_set(): 199 * set the editline parameters 200 */ 201 int 202 el_wset(EditLine *el, int op, ...) 203 { 204 va_list ap; 205 int rv = 0; 206 207 if (el == NULL) 208 return -1; 209 va_start(ap, op); 210 211 switch (op) { 212 case EL_PROMPT: 213 case EL_RPROMPT: { 214 el_pfunc_t p = va_arg(ap, el_pfunc_t); 215 216 rv = prompt_set(el, p, 0, op, 1); 217 break; 218 } 219 220 case EL_RESIZE: { 221 el_zfunc_t p = va_arg(ap, el_zfunc_t); 222 void *arg = va_arg(ap, void *); 223 rv = ch_resizefun(el, p, arg); 224 break; 225 } 226 227 case EL_ALIAS_TEXT: { 228 el_afunc_t p = va_arg(ap, el_afunc_t); 229 void *arg = va_arg(ap, void *); 230 rv = ch_aliasfun(el, p, arg); 231 break; 232 } 233 234 case EL_PROMPT_ESC: 235 case EL_RPROMPT_ESC: { 236 el_pfunc_t p = va_arg(ap, el_pfunc_t); 237 int c = va_arg(ap, int); 238 239 rv = prompt_set(el, p, (wchar_t)c, op, 1); 240 break; 241 } 242 243 case EL_TERMINAL: 244 rv = terminal_set(el, va_arg(ap, char *)); 245 break; 246 247 case EL_EDITOR: 248 rv = map_set_editor(el, va_arg(ap, wchar_t *)); 249 break; 250 251 case EL_SIGNAL: 252 if (va_arg(ap, int)) 253 el->el_flags |= HANDLE_SIGNALS; 254 else 255 el->el_flags &= ~HANDLE_SIGNALS; 256 break; 257 258 case EL_BIND: 259 case EL_TELLTC: 260 case EL_SETTC: 261 case EL_ECHOTC: 262 case EL_SETTY: 263 { 264 const wchar_t *argv[20]; 265 int i; 266 267 for (i = 1; i < (int)__arraycount(argv); i++) 268 if ((argv[i] = va_arg(ap, wchar_t *)) == NULL) 269 break; 270 271 switch (op) { 272 case EL_BIND: 273 argv[0] = L"bind"; 274 rv = map_bind(el, i, argv); 275 break; 276 277 case EL_TELLTC: 278 argv[0] = L"telltc"; 279 rv = terminal_telltc(el, i, argv); 280 break; 281 282 case EL_SETTC: 283 argv[0] = L"settc"; 284 rv = terminal_settc(el, i, argv); 285 break; 286 287 case EL_ECHOTC: 288 argv[0] = L"echotc"; 289 rv = terminal_echotc(el, i, argv); 290 break; 291 292 case EL_SETTY: 293 argv[0] = L"setty"; 294 rv = tty_stty(el, i, argv); 295 break; 296 297 default: 298 rv = -1; 299 EL_ABORT((el->el_errfile, "Bad op %d\n", op)); 300 break; 301 } 302 break; 303 } 304 305 case EL_ADDFN: 306 { 307 wchar_t *name = va_arg(ap, wchar_t *); 308 wchar_t *help = va_arg(ap, wchar_t *); 309 el_func_t func = va_arg(ap, el_func_t); 310 311 rv = map_addfunc(el, name, help, func); 312 break; 313 } 314 315 case EL_HIST: 316 { 317 hist_fun_t func = va_arg(ap, hist_fun_t); 318 void *ptr = va_arg(ap, void *); 319 320 rv = hist_set(el, func, ptr); 321 if (MB_CUR_MAX == 1) 322 el->el_flags &= ~NARROW_HISTORY; 323 break; 324 } 325 326 case EL_SAFEREAD: 327 if (va_arg(ap, int)) 328 el->el_flags |= FIXIO; 329 else 330 el->el_flags &= ~FIXIO; 331 rv = 0; 332 break; 333 334 case EL_EDITMODE: 335 if (va_arg(ap, int)) 336 el->el_flags &= ~EDIT_DISABLED; 337 else 338 el->el_flags |= EDIT_DISABLED; 339 rv = 0; 340 break; 341 342 case EL_GETCFN: 343 { 344 el_rfunc_t rc = va_arg(ap, el_rfunc_t); 345 rv = el_read_setfn(el->el_read, rc); 346 break; 347 } 348 349 case EL_CLIENTDATA: 350 el->el_data = va_arg(ap, void *); 351 break; 352 353 case EL_UNBUFFERED: 354 rv = va_arg(ap, int); 355 if (rv && !(el->el_flags & UNBUFFERED)) { 356 el->el_flags |= UNBUFFERED; 357 read_prepare(el); 358 } else if (!rv && (el->el_flags & UNBUFFERED)) { 359 el->el_flags &= ~UNBUFFERED; 360 read_finish(el); 361 } 362 rv = 0; 363 break; 364 365 case EL_PREP_TERM: 366 rv = va_arg(ap, int); 367 if (rv) 368 (void) tty_rawmode(el); 369 else 370 (void) tty_cookedmode(el); 371 rv = 0; 372 break; 373 374 case EL_SETFP: 375 { 376 FILE *fp; 377 int what; 378 379 what = va_arg(ap, int); 380 fp = va_arg(ap, FILE *); 381 382 rv = 0; 383 switch (what) { 384 case 0: 385 el->el_infile = fp; 386 el->el_infd = fileno(fp); 387 break; 388 case 1: 389 el->el_outfile = fp; 390 el->el_outfd = fileno(fp); 391 break; 392 case 2: 393 el->el_errfile = fp; 394 el->el_errfd = fileno(fp); 395 break; 396 default: 397 rv = -1; 398 break; 399 } 400 break; 401 } 402 403 case EL_REFRESH: 404 re_clear_display(el); 405 re_refresh(el); 406 terminal__flush(el); 407 break; 408 409 default: 410 rv = -1; 411 break; 412 } 413 414 va_end(ap); 415 return rv; 416 } 417 418 419 /* el_get(): 420 * retrieve the editline parameters 421 */ 422 int 423 el_wget(EditLine *el, int op, ...) 424 { 425 va_list ap; 426 int rv; 427 428 if (el == NULL) 429 return -1; 430 431 va_start(ap, op); 432 433 switch (op) { 434 case EL_PROMPT: 435 case EL_RPROMPT: { 436 el_pfunc_t *p = va_arg(ap, el_pfunc_t *); 437 rv = prompt_get(el, p, 0, op); 438 break; 439 } 440 case EL_PROMPT_ESC: 441 case EL_RPROMPT_ESC: { 442 el_pfunc_t *p = va_arg(ap, el_pfunc_t *); 443 wchar_t *c = va_arg(ap, wchar_t *); 444 445 rv = prompt_get(el, p, c, op); 446 break; 447 } 448 449 case EL_EDITOR: 450 rv = map_get_editor(el, va_arg(ap, const wchar_t **)); 451 break; 452 453 case EL_SIGNAL: 454 *va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS); 455 rv = 0; 456 break; 457 458 case EL_EDITMODE: 459 *va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED); 460 rv = 0; 461 break; 462 463 case EL_SAFEREAD: 464 *va_arg(ap, int *) = (el->el_flags & FIXIO); 465 rv = 0; 466 break; 467 468 case EL_TERMINAL: 469 terminal_get(el, va_arg(ap, const char **)); 470 rv = 0; 471 break; 472 473 case EL_GETTC: 474 { 475 static char name[] = "gettc"; 476 char *argv[3]; 477 argv[0] = name; 478 argv[1] = va_arg(ap, char *); 479 argv[2] = va_arg(ap, void *); 480 rv = terminal_gettc(el, 3, argv); 481 break; 482 } 483 484 case EL_GETCFN: 485 *va_arg(ap, el_rfunc_t *) = el_read_getfn(el->el_read); 486 rv = 0; 487 break; 488 489 case EL_CLIENTDATA: 490 *va_arg(ap, void **) = el->el_data; 491 rv = 0; 492 break; 493 494 case EL_UNBUFFERED: 495 *va_arg(ap, int *) = (el->el_flags & UNBUFFERED) != 0; 496 rv = 0; 497 break; 498 499 case EL_GETFP: 500 { 501 int what; 502 FILE **fpp; 503 504 what = va_arg(ap, int); 505 fpp = va_arg(ap, FILE **); 506 rv = 0; 507 switch (what) { 508 case 0: 509 *fpp = el->el_infile; 510 break; 511 case 1: 512 *fpp = el->el_outfile; 513 break; 514 case 2: 515 *fpp = el->el_errfile; 516 break; 517 default: 518 rv = -1; 519 break; 520 } 521 break; 522 } 523 default: 524 rv = -1; 525 break; 526 } 527 va_end(ap); 528 529 return rv; 530 } 531 532 533 /* el_line(): 534 * Return editing info 535 */ 536 const LineInfoW * 537 el_wline(EditLine *el) 538 { 539 540 return (const LineInfoW *)(void *)&el->el_line; 541 } 542 543 544 /* el_source(): 545 * Source a file 546 */ 547 int 548 el_source(EditLine *el, const char *fname) 549 { 550 FILE *fp; 551 size_t len; 552 ssize_t slen; 553 char *ptr; 554 char *path = NULL; 555 const wchar_t *dptr; 556 int error = 0; 557 558 fp = NULL; 559 if (fname == NULL) { 560 561 /* secure_getenv is guaranteed to be defined and do the right thing here */ 562 /* because of the defines above which take into account issetugid, */ 563 /* secure_getenv and __secure_getenv availability. */ 564 if ((fname = secure_getenv("EDITRC")) == NULL) { 565 static const char elpath[] = "/.editrc"; 566 size_t plen = sizeof(elpath); 567 568 if ((ptr = secure_getenv("HOME")) == NULL) 569 return -1; 570 plen += strlen(ptr); 571 if ((path = el_calloc(plen, sizeof(*path))) == NULL) 572 return -1; 573 (void)snprintf(path, plen, "%s%s", ptr, 574 elpath + (*ptr == '\0')); 575 fname = path; 576 } 577 578 } 579 if (fname[0] == '\0') 580 return -1; 581 582 if (fp == NULL) 583 fp = fopen(fname, "r"); 584 if (fp == NULL) { 585 el_free(path); 586 return -1; 587 } 588 589 ptr = NULL; 590 len = 0; 591 while ((slen = getline(&ptr, &len, fp)) != -1) { 592 if (*ptr == '\n') 593 continue; /* Empty line. */ 594 if (slen > 0 && ptr[--slen] == '\n') 595 ptr[slen] = '\0'; 596 597 dptr = ct_decode_string(ptr, &el->el_scratch); 598 if (!dptr) 599 continue; 600 /* loop until first non-space char or EOL */ 601 while (*dptr != '\0' && iswspace(*dptr)) 602 dptr++; 603 if (*dptr == '#') 604 continue; /* ignore, this is a comment line */ 605 if ((error = parse_line(el, dptr)) == -1) 606 break; 607 } 608 free(ptr); 609 610 el_free(path); 611 (void) fclose(fp); 612 return error; 613 } 614 615 616 /* el_resize(): 617 * Called from program when terminal is resized 618 */ 619 void 620 el_resize(EditLine *el) 621 { 622 int lins, cols; 623 sigset_t oset, nset; 624 625 (void) sigemptyset(&nset); 626 (void) sigaddset(&nset, SIGWINCH); 627 (void) sigprocmask(SIG_BLOCK, &nset, &oset); 628 629 /* get the correct window size */ 630 if (terminal_get_size(el, &lins, &cols)) 631 terminal_change_size(el, lins, cols); 632 633 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 634 } 635 636 637 /* el_beep(): 638 * Called from the program to beep 639 */ 640 void 641 el_beep(EditLine *el) 642 { 643 644 terminal_beep(el); 645 } 646 647 648 /* el_editmode() 649 * Set the state of EDIT_DISABLED from the `edit' command. 650 */ 651 libedit_private int 652 /*ARGSUSED*/ 653 el_editmode(EditLine *el, int argc, const wchar_t **argv) 654 { 655 const wchar_t *how; 656 657 if (argv == NULL || argc != 2 || argv[1] == NULL) 658 return -1; 659 660 how = argv[1]; 661 if (wcscmp(how, L"on") == 0) { 662 el->el_flags &= ~EDIT_DISABLED; 663 tty_rawmode(el); 664 } else if (wcscmp(how, L"off") == 0) { 665 tty_cookedmode(el); 666 el->el_flags |= EDIT_DISABLED; 667 } 668 else { 669 (void) fprintf(el->el_errfile, "edit: Bad value `%ls'.\n", 670 how); 671 return -1; 672 } 673 return 0; 674 } 675