1 /* $OpenBSD: el.c,v 1.38 2023/03/08 04:43:05 guenther Exp $ */ 2 /* $NetBSD: el.c,v 1.61 2011/01/27 23:11:40 christos Exp $ */ 3 4 /*- 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Christos Zoulas of Cornell University. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "config.h" 37 38 /* 39 * el.c: EditLine interface functions 40 */ 41 #include <sys/types.h> 42 #include <ctype.h> 43 #include <langinfo.h> 44 #include <limits.h> 45 #include <locale.h> 46 #include <stdarg.h> 47 #include <stdlib.h> 48 #include <string.h> 49 50 #include "el.h" 51 #include "parse.h" 52 #include "read.h" 53 54 /* el_init(): 55 * Initialize editline and set default parameters. 56 */ 57 EditLine * 58 el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr) 59 { 60 EditLine *el = (EditLine *) calloc(1, sizeof(EditLine)); 61 62 if (el == NULL) 63 return NULL; 64 65 el->el_infile = fin; 66 el->el_outfile = fout; 67 el->el_errfile = ferr; 68 69 el->el_infd = fileno(fin); 70 el->el_outfd = fileno(fout); 71 el->el_errfd = fileno(ferr); 72 73 el->el_prog = wcsdup(ct_decode_string(prog, &el->el_scratch)); 74 if (el->el_prog == NULL) { 75 free(el); 76 return NULL; 77 } 78 79 /* 80 * Initialize all the modules. Order is important!!! 81 */ 82 el->el_flags = 0; 83 if (setlocale(LC_CTYPE, NULL) != NULL){ 84 if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) 85 el->el_flags |= CHARSET_IS_UTF8; 86 } 87 88 if (terminal_init(el) == -1) { 89 free(el->el_prog); 90 free(el); 91 return NULL; 92 } 93 (void) keymacro_init(el); 94 (void) map_init(el); 95 if (tty_init(el) == -1) 96 el->el_flags |= NO_TTY; 97 (void) ch_init(el); 98 (void) search_init(el); 99 (void) hist_init(el); 100 (void) prompt_init(el); 101 (void) sig_init(el); 102 if (read_init(el) == -1) { 103 el_end(el); 104 return NULL; 105 } 106 return el; 107 } 108 109 110 /* el_end(): 111 * Clean up. 112 */ 113 void 114 el_end(EditLine *el) 115 { 116 117 if (el == NULL) 118 return; 119 120 el_reset(el); 121 122 terminal_end(el); 123 keymacro_end(el); 124 map_end(el); 125 tty_end(el); 126 ch_end(el); 127 read_end(el->el_read); 128 search_end(el); 129 hist_end(el); 130 prompt_end(el); 131 sig_end(el); 132 133 free(el->el_prog); 134 free(el->el_scratch.cbuff); 135 free(el->el_scratch.wbuff); 136 free(el->el_lgcyconv.cbuff); 137 free(el->el_lgcyconv.wbuff); 138 free(el); 139 } 140 141 142 /* el_reset(): 143 * Reset the tty and the parser 144 */ 145 void 146 el_reset(EditLine *el) 147 { 148 149 tty_cookedmode(el); 150 ch_reset(el); /* XXX: Do we want that? */ 151 } 152 153 154 /* el_set(): 155 * set the editline parameters 156 */ 157 int 158 el_wset(EditLine *el, int op, ...) 159 { 160 va_list ap; 161 int rv = 0; 162 163 if (el == NULL) 164 return -1; 165 va_start(ap, op); 166 167 switch (op) { 168 case EL_PROMPT: 169 case EL_RPROMPT: { 170 el_pfunc_t p = va_arg(ap, el_pfunc_t); 171 172 rv = prompt_set(el, p, 0, op, 1); 173 break; 174 } 175 176 case EL_RESIZE: { 177 el_zfunc_t p = va_arg(ap, el_zfunc_t); 178 void *arg = va_arg(ap, void *); 179 rv = ch_resizefun(el, p, arg); 180 break; 181 } 182 183 case EL_PROMPT_ESC: 184 case EL_RPROMPT_ESC: { 185 el_pfunc_t p = va_arg(ap, el_pfunc_t); 186 int c = va_arg(ap, int); 187 188 rv = prompt_set(el, p, c, op, 1); 189 break; 190 } 191 192 case EL_TERMINAL: 193 rv = terminal_set(el, va_arg(ap, char *)); 194 break; 195 196 case EL_EDITOR: 197 rv = map_set_editor(el, va_arg(ap, wchar_t *)); 198 break; 199 200 case EL_SIGNAL: 201 if (va_arg(ap, int)) 202 el->el_flags |= HANDLE_SIGNALS; 203 else 204 el->el_flags &= ~HANDLE_SIGNALS; 205 break; 206 207 case EL_BIND: 208 case EL_TELLTC: 209 case EL_SETTC: 210 case EL_ECHOTC: 211 case EL_SETTY: 212 { 213 const wchar_t *argv[20]; 214 int i; 215 216 for (i = 1; i < 20; i++) 217 if ((argv[i] = va_arg(ap, wchar_t *)) == NULL) 218 break; 219 220 switch (op) { 221 case EL_BIND: 222 argv[0] = L"bind"; 223 rv = map_bind(el, i, argv); 224 break; 225 226 case EL_TELLTC: 227 argv[0] = L"telltc"; 228 rv = terminal_telltc(el, i, argv); 229 break; 230 231 case EL_SETTC: 232 argv[0] = L"settc"; 233 rv = terminal_settc(el, i, argv); 234 break; 235 236 case EL_ECHOTC: 237 argv[0] = L"echotc"; 238 rv = terminal_echotc(el, i, argv); 239 break; 240 241 case EL_SETTY: 242 argv[0] = L"setty"; 243 rv = tty_stty(el, i, argv); 244 break; 245 246 default: 247 rv = -1; 248 EL_ABORT((el->el_errfile, "Bad op %d\n", op)); 249 break; 250 } 251 break; 252 } 253 254 case EL_ADDFN: 255 { 256 wchar_t *name = va_arg(ap, wchar_t *); 257 wchar_t *help = va_arg(ap, wchar_t *); 258 el_func_t func = va_arg(ap, el_func_t); 259 260 rv = map_addfunc(el, name, help, func); 261 break; 262 } 263 264 case EL_HIST: 265 { 266 hist_fun_t func = va_arg(ap, hist_fun_t); 267 void *ptr = va_arg(ap, void *); 268 269 rv = hist_set(el, func, ptr); 270 if (!(el->el_flags & CHARSET_IS_UTF8)) 271 el->el_flags &= ~NARROW_HISTORY; 272 break; 273 } 274 275 case EL_EDITMODE: 276 if (va_arg(ap, int)) 277 el->el_flags &= ~EDIT_DISABLED; 278 else 279 el->el_flags |= EDIT_DISABLED; 280 rv = 0; 281 break; 282 283 case EL_GETCFN: 284 { 285 el_rfunc_t rc = va_arg(ap, el_rfunc_t); 286 rv = el_read_setfn(el->el_read, rc); 287 break; 288 } 289 290 case EL_CLIENTDATA: 291 el->el_data = va_arg(ap, void *); 292 break; 293 294 case EL_UNBUFFERED: 295 rv = va_arg(ap, int); 296 if (rv && !(el->el_flags & UNBUFFERED)) { 297 el->el_flags |= UNBUFFERED; 298 read_prepare(el); 299 } else if (!rv && (el->el_flags & UNBUFFERED)) { 300 el->el_flags &= ~UNBUFFERED; 301 read_finish(el); 302 } 303 rv = 0; 304 break; 305 306 case EL_PREP_TERM: 307 rv = va_arg(ap, int); 308 if (rv) 309 (void) tty_rawmode(el); 310 else 311 (void) tty_cookedmode(el); 312 rv = 0; 313 break; 314 315 case EL_SETFP: 316 { 317 FILE *fp; 318 int what; 319 320 what = va_arg(ap, int); 321 fp = va_arg(ap, FILE *); 322 323 rv = 0; 324 switch (what) { 325 case 0: 326 el->el_infile = fp; 327 el->el_infd = fileno(fp); 328 break; 329 case 1: 330 el->el_outfile = fp; 331 el->el_outfd = fileno(fp); 332 break; 333 case 2: 334 el->el_errfile = fp; 335 el->el_errfd = fileno(fp); 336 break; 337 default: 338 rv = -1; 339 break; 340 } 341 break; 342 } 343 344 case EL_REFRESH: 345 re_clear_display(el); 346 re_refresh(el); 347 terminal__flush(el); 348 break; 349 350 default: 351 rv = -1; 352 break; 353 } 354 355 va_end(ap); 356 return rv; 357 } 358 359 360 /* el_get(): 361 * retrieve the editline parameters 362 */ 363 int 364 el_wget(EditLine *el, int op, ...) 365 { 366 va_list ap; 367 int rv; 368 369 if (el == NULL) 370 return -1; 371 372 va_start(ap, op); 373 374 switch (op) { 375 case EL_PROMPT: 376 case EL_RPROMPT: { 377 el_pfunc_t *p = va_arg(ap, el_pfunc_t *); 378 rv = prompt_get(el, p, 0, op); 379 break; 380 } 381 case EL_PROMPT_ESC: 382 case EL_RPROMPT_ESC: { 383 el_pfunc_t *p = va_arg(ap, el_pfunc_t *); 384 wchar_t *c = va_arg(ap, wchar_t *); 385 386 rv = prompt_get(el, p, c, op); 387 break; 388 } 389 390 case EL_EDITOR: 391 rv = map_get_editor(el, va_arg(ap, const wchar_t **)); 392 break; 393 394 case EL_SIGNAL: 395 *va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS); 396 rv = 0; 397 break; 398 399 case EL_EDITMODE: 400 *va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED); 401 rv = 0; 402 break; 403 404 case EL_TERMINAL: 405 terminal_get(el, va_arg(ap, const char **)); 406 rv = 0; 407 break; 408 409 case EL_GETTC: 410 { 411 static char name[] = "gettc"; 412 char *argv[20]; 413 int i; 414 415 for (i = 1; i < (int)(sizeof(argv) / sizeof(argv[0])); i++) 416 if ((argv[i] = va_arg(ap, char *)) == NULL) 417 break; 418 419 switch (op) { 420 case EL_GETTC: 421 argv[0] = name; 422 rv = terminal_gettc(el, i, argv); 423 break; 424 425 default: 426 rv = -1; 427 EL_ABORT((el->el_errfile, "Bad op %d\n", op)); 428 break; 429 } 430 break; 431 } 432 433 case EL_GETCFN: 434 *va_arg(ap, el_rfunc_t *) = el_read_getfn(el->el_read); 435 rv = 0; 436 break; 437 438 case EL_CLIENTDATA: 439 *va_arg(ap, void **) = el->el_data; 440 rv = 0; 441 break; 442 443 case EL_UNBUFFERED: 444 *va_arg(ap, int *) = (!(el->el_flags & UNBUFFERED)); 445 rv = 0; 446 break; 447 448 case EL_GETFP: 449 { 450 int what; 451 FILE **fpp; 452 453 what = va_arg(ap, int); 454 fpp = va_arg(ap, FILE **); 455 rv = 0; 456 switch (what) { 457 case 0: 458 *fpp = el->el_infile; 459 break; 460 case 1: 461 *fpp = el->el_outfile; 462 break; 463 case 2: 464 *fpp = el->el_errfile; 465 break; 466 default: 467 rv = -1; 468 break; 469 } 470 break; 471 } 472 default: 473 rv = -1; 474 break; 475 } 476 va_end(ap); 477 478 return rv; 479 } 480 481 482 /* el_line(): 483 * Return editing info 484 */ 485 const LineInfoW * 486 el_wline(EditLine *el) 487 { 488 489 return (const LineInfoW *)(void *)&el->el_line; 490 } 491 492 493 /* el_source(): 494 * Source a file 495 */ 496 int 497 el_source(EditLine *el, const char *fname) 498 { 499 FILE *fp; 500 size_t len; 501 ssize_t slen; 502 char *ptr; 503 #ifdef HAVE_ISSETUGID 504 char path[PATH_MAX]; 505 #endif 506 const wchar_t *dptr; 507 508 fp = NULL; 509 if (fname == NULL) { 510 #ifdef HAVE_ISSETUGID 511 static const char elpath[] = "/.editrc"; 512 513 if (issetugid()) 514 return -1; 515 if ((ptr = getenv("HOME")) == NULL) 516 return -1; 517 if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path)) 518 return -1; 519 if (strlcat(path, elpath, sizeof(path)) >= sizeof(path)) 520 return -1; 521 fname = path; 522 #else 523 /* 524 * If issetugid() is missing, always return an error, in order 525 * to keep from inadvertently opening up the user to a security 526 * hole. 527 */ 528 return -1; 529 #endif 530 } 531 if (fp == NULL) 532 fp = fopen(fname, "r"); 533 if (fp == NULL) 534 return -1; 535 536 ptr = NULL; 537 len = 0; 538 while ((slen = getline(&ptr, &len, fp)) != -1) { 539 if (*ptr == '\n') 540 continue; /* Empty line. */ 541 if (slen > 0 && ptr[--slen] == '\n') 542 ptr[slen] = '\0'; 543 544 dptr = ct_decode_string(ptr, &el->el_scratch); 545 if (!dptr) 546 continue; 547 /* loop until first non-space char or EOL */ 548 while (*dptr != '\0' && iswspace(*dptr)) 549 dptr++; 550 if (*dptr == '#') 551 continue; /* ignore, this is a comment line */ 552 if (parse_line(el, dptr) == -1) { 553 free(ptr); 554 (void) fclose(fp); 555 return -1; 556 } 557 } 558 free(ptr); 559 (void) fclose(fp); 560 return 0; 561 } 562 563 564 /* el_resize(): 565 * Called from program when terminal is resized 566 */ 567 void 568 el_resize(EditLine *el) 569 { 570 int lins, cols; 571 sigset_t oset, nset; 572 573 (void) sigemptyset(&nset); 574 (void) sigaddset(&nset, SIGWINCH); 575 (void) sigprocmask(SIG_BLOCK, &nset, &oset); 576 577 /* get the correct window size */ 578 if (terminal_get_size(el, &lins, &cols)) 579 terminal_change_size(el, lins, cols); 580 581 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 582 } 583 584 585 /* el_beep(): 586 * Called from the program to beep 587 */ 588 void 589 el_beep(EditLine *el) 590 { 591 592 terminal_beep(el); 593 } 594 595 596 /* el_editmode() 597 * Set the state of EDIT_DISABLED from the `edit' command. 598 */ 599 protected int 600 el_editmode(EditLine *el, int argc, const wchar_t **argv) 601 { 602 const wchar_t *how; 603 604 if (argv == NULL || argc != 2 || argv[1] == NULL) 605 return -1; 606 607 how = argv[1]; 608 if (wcscmp(how, L"on") == 0) { 609 el->el_flags &= ~EDIT_DISABLED; 610 tty_rawmode(el); 611 } else if (wcscmp(how, L"off") == 0) { 612 tty_cookedmode(el); 613 el->el_flags |= EDIT_DISABLED; 614 } 615 else { 616 (void) fprintf(el->el_errfile, "edit: Bad value `%ls'.\n", 617 how); 618 return -1; 619 } 620 return 0; 621 } 622