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