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