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