1 /* $NetBSD: el.c,v 1.98 2019/04/26 16:56:57 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.98 2019/04/26 16:56:57 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[3]; 443 argv[0] = name; 444 argv[1] = va_arg(ap, char *); 445 argv[2] = va_arg(ap, void *); 446 rv = terminal_gettc(el, 3, argv); 447 break; 448 } 449 450 case EL_GETCFN: 451 *va_arg(ap, el_rfunc_t *) = el_read_getfn(el->el_read); 452 rv = 0; 453 break; 454 455 case EL_CLIENTDATA: 456 *va_arg(ap, void **) = el->el_data; 457 rv = 0; 458 break; 459 460 case EL_UNBUFFERED: 461 *va_arg(ap, int *) = (el->el_flags & UNBUFFERED) != 0; 462 rv = 0; 463 break; 464 465 case EL_GETFP: 466 { 467 int what; 468 FILE **fpp; 469 470 what = va_arg(ap, int); 471 fpp = va_arg(ap, FILE **); 472 rv = 0; 473 switch (what) { 474 case 0: 475 *fpp = el->el_infile; 476 break; 477 case 1: 478 *fpp = el->el_outfile; 479 break; 480 case 2: 481 *fpp = el->el_errfile; 482 break; 483 default: 484 rv = -1; 485 break; 486 } 487 break; 488 } 489 default: 490 rv = -1; 491 break; 492 } 493 va_end(ap); 494 495 return rv; 496 } 497 498 499 /* el_line(): 500 * Return editing info 501 */ 502 const LineInfoW * 503 el_wline(EditLine *el) 504 { 505 506 return (const LineInfoW *)(void *)&el->el_line; 507 } 508 509 510 /* el_source(): 511 * Source a file 512 */ 513 int 514 el_source(EditLine *el, const char *fname) 515 { 516 FILE *fp; 517 size_t len; 518 ssize_t slen; 519 char *ptr; 520 char *path = NULL; 521 const wchar_t *dptr; 522 int error = 0; 523 524 fp = NULL; 525 if (fname == NULL) { 526 #ifdef HAVE_ISSETUGID 527 if (issetugid()) 528 return -1; 529 530 if ((fname = getenv("EDITRC")) == NULL) { 531 static const char elpath[] = "/.editrc"; 532 size_t plen = sizeof(elpath); 533 534 if ((ptr = getenv("HOME")) == NULL) 535 return -1; 536 plen += strlen(ptr); 537 if ((path = el_malloc(plen * sizeof(*path))) == NULL) 538 return -1; 539 (void)snprintf(path, plen, "%s%s", ptr, 540 elpath + (*ptr == '\0')); 541 fname = path; 542 } 543 #else 544 /* 545 * If issetugid() is missing, always return an error, in order 546 * to keep from inadvertently opening up the user to a security 547 * hole. 548 */ 549 return -1; 550 #endif 551 } 552 if (fname[0] == '\0') 553 return -1; 554 555 if (fp == NULL) 556 fp = fopen(fname, "r"); 557 if (fp == NULL) { 558 el_free(path); 559 return -1; 560 } 561 562 ptr = NULL; 563 len = 0; 564 while ((slen = getline(&ptr, &len, fp)) != -1) { 565 if (*ptr == '\n') 566 continue; /* Empty line. */ 567 if (slen > 0 && ptr[--slen] == '\n') 568 ptr[slen] = '\0'; 569 570 dptr = ct_decode_string(ptr, &el->el_scratch); 571 if (!dptr) 572 continue; 573 /* loop until first non-space char or EOL */ 574 while (*dptr != '\0' && iswspace(*dptr)) 575 dptr++; 576 if (*dptr == '#') 577 continue; /* ignore, this is a comment line */ 578 if ((error = parse_line(el, dptr)) == -1) 579 break; 580 } 581 free(ptr); 582 583 el_free(path); 584 (void) fclose(fp); 585 return error; 586 } 587 588 589 /* el_resize(): 590 * Called from program when terminal is resized 591 */ 592 void 593 el_resize(EditLine *el) 594 { 595 int lins, cols; 596 sigset_t oset, nset; 597 598 (void) sigemptyset(&nset); 599 (void) sigaddset(&nset, SIGWINCH); 600 (void) sigprocmask(SIG_BLOCK, &nset, &oset); 601 602 /* get the correct window size */ 603 if (terminal_get_size(el, &lins, &cols)) 604 terminal_change_size(el, lins, cols); 605 606 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 607 } 608 609 610 /* el_beep(): 611 * Called from the program to beep 612 */ 613 void 614 el_beep(EditLine *el) 615 { 616 617 terminal_beep(el); 618 } 619 620 621 /* el_editmode() 622 * Set the state of EDIT_DISABLED from the `edit' command. 623 */ 624 libedit_private int 625 /*ARGSUSED*/ 626 el_editmode(EditLine *el, int argc, const wchar_t **argv) 627 { 628 const wchar_t *how; 629 630 if (argv == NULL || argc != 2 || argv[1] == NULL) 631 return -1; 632 633 how = argv[1]; 634 if (wcscmp(how, L"on") == 0) { 635 el->el_flags &= ~EDIT_DISABLED; 636 tty_rawmode(el); 637 } else if (wcscmp(how, L"off") == 0) { 638 tty_cookedmode(el); 639 el->el_flags |= EDIT_DISABLED; 640 } 641 else { 642 (void) fprintf(el->el_errfile, "edit: Bad value `%ls'.\n", 643 how); 644 return -1; 645 } 646 return 0; 647 } 648