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