1 /* $NetBSD: el.c,v 1.99 2019/07/23 10:18:52 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.99 2019/07/23 10:18:52 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_EDITMODE: 327 if (va_arg(ap, int)) 328 el->el_flags &= ~EDIT_DISABLED; 329 else 330 el->el_flags |= EDIT_DISABLED; 331 rv = 0; 332 break; 333 334 case EL_GETCFN: 335 { 336 el_rfunc_t rc = va_arg(ap, el_rfunc_t); 337 rv = el_read_setfn(el->el_read, rc); 338 break; 339 } 340 341 case EL_CLIENTDATA: 342 el->el_data = va_arg(ap, void *); 343 break; 344 345 case EL_UNBUFFERED: 346 rv = va_arg(ap, int); 347 if (rv && !(el->el_flags & UNBUFFERED)) { 348 el->el_flags |= UNBUFFERED; 349 read_prepare(el); 350 } else if (!rv && (el->el_flags & UNBUFFERED)) { 351 el->el_flags &= ~UNBUFFERED; 352 read_finish(el); 353 } 354 rv = 0; 355 break; 356 357 case EL_PREP_TERM: 358 rv = va_arg(ap, int); 359 if (rv) 360 (void) tty_rawmode(el); 361 else 362 (void) tty_cookedmode(el); 363 rv = 0; 364 break; 365 366 case EL_SETFP: 367 { 368 FILE *fp; 369 int what; 370 371 what = va_arg(ap, int); 372 fp = va_arg(ap, FILE *); 373 374 rv = 0; 375 switch (what) { 376 case 0: 377 el->el_infile = fp; 378 el->el_infd = fileno(fp); 379 break; 380 case 1: 381 el->el_outfile = fp; 382 el->el_outfd = fileno(fp); 383 break; 384 case 2: 385 el->el_errfile = fp; 386 el->el_errfd = fileno(fp); 387 break; 388 default: 389 rv = -1; 390 break; 391 } 392 break; 393 } 394 395 case EL_REFRESH: 396 re_clear_display(el); 397 re_refresh(el); 398 terminal__flush(el); 399 break; 400 401 default: 402 rv = -1; 403 break; 404 } 405 406 va_end(ap); 407 return rv; 408 } 409 410 411 /* el_get(): 412 * retrieve the editline parameters 413 */ 414 int 415 el_wget(EditLine *el, int op, ...) 416 { 417 va_list ap; 418 int rv; 419 420 if (el == NULL) 421 return -1; 422 423 va_start(ap, op); 424 425 switch (op) { 426 case EL_PROMPT: 427 case EL_RPROMPT: { 428 el_pfunc_t *p = va_arg(ap, el_pfunc_t *); 429 rv = prompt_get(el, p, 0, op); 430 break; 431 } 432 case EL_PROMPT_ESC: 433 case EL_RPROMPT_ESC: { 434 el_pfunc_t *p = va_arg(ap, el_pfunc_t *); 435 wchar_t *c = va_arg(ap, wchar_t *); 436 437 rv = prompt_get(el, p, c, op); 438 break; 439 } 440 441 case EL_EDITOR: 442 rv = map_get_editor(el, va_arg(ap, const wchar_t **)); 443 break; 444 445 case EL_SIGNAL: 446 *va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS); 447 rv = 0; 448 break; 449 450 case EL_EDITMODE: 451 *va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED); 452 rv = 0; 453 break; 454 455 case EL_TERMINAL: 456 terminal_get(el, va_arg(ap, const char **)); 457 rv = 0; 458 break; 459 460 case EL_GETTC: 461 { 462 static char name[] = "gettc"; 463 char *argv[3]; 464 argv[0] = name; 465 argv[1] = va_arg(ap, char *); 466 argv[2] = va_arg(ap, void *); 467 rv = terminal_gettc(el, 3, argv); 468 break; 469 } 470 471 case EL_GETCFN: 472 *va_arg(ap, el_rfunc_t *) = el_read_getfn(el->el_read); 473 rv = 0; 474 break; 475 476 case EL_CLIENTDATA: 477 *va_arg(ap, void **) = el->el_data; 478 rv = 0; 479 break; 480 481 case EL_UNBUFFERED: 482 *va_arg(ap, int *) = (el->el_flags & UNBUFFERED) != 0; 483 rv = 0; 484 break; 485 486 case EL_GETFP: 487 { 488 int what; 489 FILE **fpp; 490 491 what = va_arg(ap, int); 492 fpp = va_arg(ap, FILE **); 493 rv = 0; 494 switch (what) { 495 case 0: 496 *fpp = el->el_infile; 497 break; 498 case 1: 499 *fpp = el->el_outfile; 500 break; 501 case 2: 502 *fpp = el->el_errfile; 503 break; 504 default: 505 rv = -1; 506 break; 507 } 508 break; 509 } 510 default: 511 rv = -1; 512 break; 513 } 514 va_end(ap); 515 516 return rv; 517 } 518 519 520 /* el_line(): 521 * Return editing info 522 */ 523 const LineInfoW * 524 el_wline(EditLine *el) 525 { 526 527 return (const LineInfoW *)(void *)&el->el_line; 528 } 529 530 531 /* el_source(): 532 * Source a file 533 */ 534 int 535 el_source(EditLine *el, const char *fname) 536 { 537 FILE *fp; 538 size_t len; 539 ssize_t slen; 540 char *ptr; 541 char *path = NULL; 542 const wchar_t *dptr; 543 int error = 0; 544 545 fp = NULL; 546 if (fname == NULL) { 547 #ifdef HAVE_ISSETUGID 548 if (issetugid()) 549 return -1; 550 551 if ((fname = getenv("EDITRC")) == NULL) { 552 static const char elpath[] = "/.editrc"; 553 size_t plen = sizeof(elpath); 554 555 if ((ptr = getenv("HOME")) == NULL) 556 return -1; 557 plen += strlen(ptr); 558 if ((path = el_calloc(plen, sizeof(*path))) == NULL) 559 return -1; 560 (void)snprintf(path, plen, "%s%s", ptr, 561 elpath + (*ptr == '\0')); 562 fname = path; 563 } 564 #else 565 /* 566 * If issetugid() is missing, always return an error, in order 567 * to keep from inadvertently opening up the user to a security 568 * hole. 569 */ 570 return -1; 571 #endif 572 } 573 if (fname[0] == '\0') 574 return -1; 575 576 if (fp == NULL) 577 fp = fopen(fname, "r"); 578 if (fp == NULL) { 579 el_free(path); 580 return -1; 581 } 582 583 ptr = NULL; 584 len = 0; 585 while ((slen = getline(&ptr, &len, fp)) != -1) { 586 if (*ptr == '\n') 587 continue; /* Empty line. */ 588 if (slen > 0 && ptr[--slen] == '\n') 589 ptr[slen] = '\0'; 590 591 dptr = ct_decode_string(ptr, &el->el_scratch); 592 if (!dptr) 593 continue; 594 /* loop until first non-space char or EOL */ 595 while (*dptr != '\0' && iswspace(*dptr)) 596 dptr++; 597 if (*dptr == '#') 598 continue; /* ignore, this is a comment line */ 599 if ((error = parse_line(el, dptr)) == -1) 600 break; 601 } 602 free(ptr); 603 604 el_free(path); 605 (void) fclose(fp); 606 return error; 607 } 608 609 610 /* el_resize(): 611 * Called from program when terminal is resized 612 */ 613 void 614 el_resize(EditLine *el) 615 { 616 int lins, cols; 617 sigset_t oset, nset; 618 619 (void) sigemptyset(&nset); 620 (void) sigaddset(&nset, SIGWINCH); 621 (void) sigprocmask(SIG_BLOCK, &nset, &oset); 622 623 /* get the correct window size */ 624 if (terminal_get_size(el, &lins, &cols)) 625 terminal_change_size(el, lins, cols); 626 627 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 628 } 629 630 631 /* el_beep(): 632 * Called from the program to beep 633 */ 634 void 635 el_beep(EditLine *el) 636 { 637 638 terminal_beep(el); 639 } 640 641 642 /* el_editmode() 643 * Set the state of EDIT_DISABLED from the `edit' command. 644 */ 645 libedit_private int 646 /*ARGSUSED*/ 647 el_editmode(EditLine *el, int argc, const wchar_t **argv) 648 { 649 const wchar_t *how; 650 651 if (argv == NULL || argc != 2 || argv[1] == NULL) 652 return -1; 653 654 how = argv[1]; 655 if (wcscmp(how, L"on") == 0) { 656 el->el_flags &= ~EDIT_DISABLED; 657 tty_rawmode(el); 658 } else if (wcscmp(how, L"off") == 0) { 659 tty_cookedmode(el); 660 el->el_flags |= EDIT_DISABLED; 661 } 662 else { 663 (void) fprintf(el->el_errfile, "edit: Bad value `%ls'.\n", 664 how); 665 return -1; 666 } 667 return 0; 668 } 669