1 /* $NetBSD: el.c,v 1.74 2015/12/08 12:56:55 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.74 2015/12/08 12:56:55 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 <string.h> 50 #include <stdlib.h> 51 #include <stdarg.h> 52 #include <ctype.h> 53 #include <locale.h> 54 #include <langinfo.h> 55 #include "el.h" 56 57 /* el_init(): 58 * Initialize editline and set default parameters. 59 */ 60 public EditLine * 61 el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr) 62 { 63 return el_init_fd(prog, fin, fout, ferr, fileno(fin), fileno(fout), 64 fileno(ferr)); 65 } 66 67 public EditLine * 68 el_init_fd(const char *prog, FILE *fin, FILE *fout, FILE *ferr, 69 int fdin, int fdout, int fderr) 70 { 71 EditLine *el = el_malloc(sizeof(*el)); 72 73 if (el == NULL) 74 return NULL; 75 76 memset(el, 0, sizeof(EditLine)); 77 78 el->el_infile = fin; 79 el->el_outfile = fout; 80 el->el_errfile = ferr; 81 82 el->el_infd = fdin; 83 el->el_outfd = fdout; 84 el->el_errfd = fderr; 85 86 el->el_prog = Strdup(ct_decode_string(prog, &el->el_scratch)); 87 if (el->el_prog == NULL) { 88 el_free(el); 89 return NULL; 90 } 91 92 /* 93 * Initialize all the modules. Order is important!!! 94 */ 95 el->el_flags = 0; 96 #ifdef WIDECHAR 97 if (setlocale(LC_CTYPE, NULL) != NULL){ 98 if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) 99 el->el_flags |= CHARSET_IS_UTF8; 100 } 101 #endif 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, 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 (!(el->el_flags & CHARSET_IS_UTF8)) 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 el->el_flags &= ~NARROW_READ; 310 break; 311 } 312 313 case EL_CLIENTDATA: 314 el->el_data = va_arg(ap, void *); 315 break; 316 317 case EL_UNBUFFERED: 318 rv = va_arg(ap, int); 319 if (rv && !(el->el_flags & UNBUFFERED)) { 320 el->el_flags |= UNBUFFERED; 321 read_prepare(el); 322 } else if (!rv && (el->el_flags & UNBUFFERED)) { 323 el->el_flags &= ~UNBUFFERED; 324 read_finish(el); 325 } 326 rv = 0; 327 break; 328 329 case EL_PREP_TERM: 330 rv = va_arg(ap, int); 331 if (rv) 332 (void) tty_rawmode(el); 333 else 334 (void) tty_cookedmode(el); 335 rv = 0; 336 break; 337 338 case EL_SETFP: 339 { 340 FILE *fp; 341 int what; 342 343 what = va_arg(ap, int); 344 fp = va_arg(ap, FILE *); 345 346 rv = 0; 347 switch (what) { 348 case 0: 349 el->el_infile = fp; 350 el->el_infd = fileno(fp); 351 break; 352 case 1: 353 el->el_outfile = fp; 354 el->el_outfd = fileno(fp); 355 break; 356 case 2: 357 el->el_errfile = fp; 358 el->el_errfd = fileno(fp); 359 break; 360 default: 361 rv = -1; 362 break; 363 } 364 break; 365 } 366 367 case EL_REFRESH: 368 re_clear_display(el); 369 re_refresh(el); 370 terminal__flush(el); 371 break; 372 373 default: 374 rv = -1; 375 break; 376 } 377 378 va_end(ap); 379 return rv; 380 } 381 382 383 /* el_get(): 384 * retrieve the editline parameters 385 */ 386 public int 387 FUN(el,get)(EditLine *el, int op, ...) 388 { 389 va_list ap; 390 int rv; 391 392 if (el == NULL) 393 return -1; 394 395 va_start(ap, op); 396 397 switch (op) { 398 case EL_PROMPT: 399 case EL_RPROMPT: { 400 el_pfunc_t *p = va_arg(ap, el_pfunc_t *); 401 rv = prompt_get(el, p, 0, op); 402 break; 403 } 404 case EL_PROMPT_ESC: 405 case EL_RPROMPT_ESC: { 406 el_pfunc_t *p = va_arg(ap, el_pfunc_t *); 407 Char *c = va_arg(ap, Char *); 408 409 rv = prompt_get(el, p, c, op); 410 break; 411 } 412 413 case EL_EDITOR: 414 rv = map_get_editor(el, va_arg(ap, const Char **)); 415 break; 416 417 case EL_SIGNAL: 418 *va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS); 419 rv = 0; 420 break; 421 422 case EL_EDITMODE: 423 *va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED); 424 rv = 0; 425 break; 426 427 case EL_TERMINAL: 428 terminal_get(el, va_arg(ap, const char **)); 429 rv = 0; 430 break; 431 432 case EL_GETTC: 433 { 434 static char name[] = "gettc"; 435 char *argv[20]; 436 int i; 437 438 for (i = 1; i < (int)__arraycount(argv); i++) 439 if ((argv[i] = va_arg(ap, char *)) == NULL) 440 break; 441 442 argv[0] = name; 443 rv = terminal_gettc(el, i, argv); 444 break; 445 } 446 447 case EL_GETCFN: 448 *va_arg(ap, el_rfunc_t *) = el_read_getfn(el); 449 rv = 0; 450 break; 451 452 case EL_CLIENTDATA: 453 *va_arg(ap, void **) = el->el_data; 454 rv = 0; 455 break; 456 457 case EL_UNBUFFERED: 458 *va_arg(ap, int *) = (el->el_flags & UNBUFFERED) != 0; 459 rv = 0; 460 break; 461 462 case EL_GETFP: 463 { 464 int what; 465 FILE **fpp; 466 467 what = va_arg(ap, int); 468 fpp = va_arg(ap, FILE **); 469 rv = 0; 470 switch (what) { 471 case 0: 472 *fpp = el->el_infile; 473 break; 474 case 1: 475 *fpp = el->el_outfile; 476 break; 477 case 2: 478 *fpp = el->el_errfile; 479 break; 480 default: 481 rv = -1; 482 break; 483 } 484 break; 485 } 486 default: 487 rv = -1; 488 break; 489 } 490 va_end(ap); 491 492 return rv; 493 } 494 495 496 /* el_line(): 497 * Return editing info 498 */ 499 public const TYPE(LineInfo) * 500 FUN(el,line)(EditLine *el) 501 { 502 503 return (const TYPE(LineInfo) *)(void *)&el->el_line; 504 } 505 506 507 /* el_source(): 508 * Source a file 509 */ 510 public int 511 el_source(EditLine *el, const char *fname) 512 { 513 FILE *fp; 514 size_t len; 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 while ((ptr = fgetln(fp, &len)) != NULL) { 552 if (*ptr == '\n') 553 continue; /* Empty line. */ 554 dptr = ct_decode_string(ptr, &el->el_scratch); 555 if (!dptr) 556 continue; 557 if (len > 0 && dptr[len - 1] == '\n') 558 --len; 559 560 /* loop until first non-space char or EOL */ 561 while (*dptr != '\0' && Isspace(*dptr)) 562 dptr++; 563 if (*dptr == '#') 564 continue; /* ignore, this is a comment line */ 565 if ((error = parse_line(el, dptr)) == -1) 566 break; 567 } 568 569 el_free(path); 570 (void) fclose(fp); 571 return error; 572 } 573 574 575 /* el_resize(): 576 * Called from program when terminal is resized 577 */ 578 public void 579 el_resize(EditLine *el) 580 { 581 int lins, cols; 582 sigset_t oset, nset; 583 584 (void) sigemptyset(&nset); 585 (void) sigaddset(&nset, SIGWINCH); 586 (void) sigprocmask(SIG_BLOCK, &nset, &oset); 587 588 /* get the correct window size */ 589 if (terminal_get_size(el, &lins, &cols)) 590 terminal_change_size(el, lins, cols); 591 592 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 593 } 594 595 596 /* el_beep(): 597 * Called from the program to beep 598 */ 599 public void 600 el_beep(EditLine *el) 601 { 602 603 terminal_beep(el); 604 } 605 606 607 /* el_editmode() 608 * Set the state of EDIT_DISABLED from the `edit' command. 609 */ 610 protected int 611 /*ARGSUSED*/ 612 el_editmode(EditLine *el, int argc, const Char **argv) 613 { 614 const Char *how; 615 616 if (argv == NULL || argc != 2 || argv[1] == NULL) 617 return -1; 618 619 how = argv[1]; 620 if (Strcmp(how, STR("on")) == 0) { 621 el->el_flags &= ~EDIT_DISABLED; 622 tty_rawmode(el); 623 } else if (Strcmp(how, STR("off")) == 0) { 624 tty_cookedmode(el); 625 el->el_flags |= EDIT_DISABLED; 626 } 627 else { 628 (void) fprintf(el->el_errfile, "edit: Bad value `" FSTR "'.\n", 629 how); 630 return -1; 631 } 632 return 0; 633 } 634