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