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