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 /* 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 tty_end(el); 139 ch_end(el); 140 search_end(el); 141 hist_end(el); 142 prompt_end(el); 143 sig_end(el); 144 145 el_free(el->el_prog); 146 #ifdef WIDECHAR 147 el_free(el->el_scratch.cbuff); 148 el_free(el->el_scratch.wbuff); 149 el_free(el->el_lgcyconv.cbuff); 150 el_free(el->el_lgcyconv.wbuff); 151 #endif 152 el_free(el); 153 } 154 155 156 /* el_reset(): 157 * Reset the tty and the parser 158 */ 159 public void 160 el_reset(EditLine *el) 161 { 162 163 tty_cookedmode(el); 164 ch_reset(el, 0); /* XXX: Do we want that? */ 165 } 166 167 168 /* el_set(): 169 * set the editline parameters 170 */ 171 public int 172 FUN(el,set)(EditLine *el, int op, ...) 173 { 174 va_list ap; 175 int rv = 0; 176 177 if (el == NULL) 178 return -1; 179 va_start(ap, op); 180 181 switch (op) { 182 case EL_PROMPT: 183 case EL_RPROMPT: { 184 el_pfunc_t p = va_arg(ap, el_pfunc_t); 185 186 rv = prompt_set(el, p, 0, op, 1); 187 break; 188 } 189 190 case EL_RESIZE: { 191 el_zfunc_t p = va_arg(ap, el_zfunc_t); 192 void *arg = va_arg(ap, void *); 193 rv = ch_resizefun(el, p, arg); 194 break; 195 } 196 197 case EL_ALIAS_TEXT: { 198 el_afunc_t p = va_arg(ap, el_afunc_t); 199 void *arg = va_arg(ap, void *); 200 rv = ch_aliasfun(el, p, arg); 201 break; 202 } 203 204 case EL_PROMPT_ESC: 205 case EL_RPROMPT_ESC: { 206 el_pfunc_t p = va_arg(ap, el_pfunc_t); 207 int c = va_arg(ap, int); 208 209 rv = prompt_set(el, p, c, op, 1); 210 break; 211 } 212 213 case EL_TERMINAL: 214 rv = terminal_set(el, va_arg(ap, char *)); 215 break; 216 217 case EL_EDITOR: 218 rv = map_set_editor(el, va_arg(ap, Char *)); 219 break; 220 221 case EL_SIGNAL: 222 if (va_arg(ap, int)) 223 el->el_flags |= HANDLE_SIGNALS; 224 else 225 el->el_flags &= ~HANDLE_SIGNALS; 226 break; 227 228 case EL_BIND: 229 case EL_TELLTC: 230 case EL_SETTC: 231 case EL_ECHOTC: 232 case EL_SETTY: 233 { 234 const Char *argv[20]; 235 int i; 236 237 for (i = 1; i < (int)__arraycount(argv); i++) 238 if ((argv[i] = va_arg(ap, Char *)) == NULL) 239 break; 240 241 switch (op) { 242 case EL_BIND: 243 argv[0] = STR("bind"); 244 rv = map_bind(el, i, argv); 245 break; 246 247 case EL_TELLTC: 248 argv[0] = STR("telltc"); 249 rv = terminal_telltc(el, i, argv); 250 break; 251 252 case EL_SETTC: 253 argv[0] = STR("settc"); 254 rv = terminal_settc(el, i, argv); 255 break; 256 257 case EL_ECHOTC: 258 argv[0] = STR("echotc"); 259 rv = terminal_echotc(el, i, argv); 260 break; 261 262 case EL_SETTY: 263 argv[0] = STR("setty"); 264 rv = tty_stty(el, i, argv); 265 break; 266 267 default: 268 rv = -1; 269 EL_ABORT((el->el_errfile, "Bad op %d\n", op)); 270 break; 271 } 272 break; 273 } 274 275 case EL_ADDFN: 276 { 277 Char *name = va_arg(ap, Char *); 278 Char *help = va_arg(ap, Char *); 279 el_func_t func = va_arg(ap, el_func_t); 280 281 rv = map_addfunc(el, name, help, func); 282 break; 283 } 284 285 case EL_HIST: 286 { 287 hist_fun_t func = va_arg(ap, hist_fun_t); 288 void *ptr = va_arg(ap, void *); 289 290 rv = hist_set(el, func, ptr); 291 if (!(el->el_flags & CHARSET_IS_UTF8)) 292 el->el_flags &= ~NARROW_HISTORY; 293 break; 294 } 295 296 case EL_EDITMODE: 297 if (va_arg(ap, int)) 298 el->el_flags &= ~EDIT_DISABLED; 299 else 300 el->el_flags |= EDIT_DISABLED; 301 rv = 0; 302 break; 303 304 case EL_GETCFN: 305 { 306 el_rfunc_t rc = va_arg(ap, el_rfunc_t); 307 rv = el_read_setfn(el, rc); 308 el->el_flags &= ~NARROW_READ; 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 char *ptr; 515 char *path = NULL; 516 const Char *dptr; 517 int error = 0; 518 519 fp = NULL; 520 if (fname == NULL) { 521 #ifdef HAVE_ISSETUGID 522 static const char elpath[] = "/.editrc"; 523 size_t plen = sizeof(elpath); 524 525 if (issetugid()) 526 return -1; 527 if ((ptr = getenv("HOME")) == NULL) 528 return -1; 529 plen += strlen(ptr); 530 if ((path = el_malloc(plen * sizeof(*path))) == NULL) 531 return -1; 532 (void)snprintf(path, plen, "%s%s", ptr, elpath); 533 fname = path; 534 #else 535 /* 536 * If issetugid() is missing, always return an error, in order 537 * to keep from inadvertently opening up the user to a security 538 * hole. 539 */ 540 return -1; 541 #endif 542 } 543 if (fp == NULL) 544 fp = fopen(fname, "r"); 545 if (fp == NULL) { 546 el_free(path); 547 return -1; 548 } 549 550 while ((ptr = fgetln(fp, &len)) != NULL) { 551 if (*ptr == '\n') 552 continue; /* Empty line. */ 553 dptr = ct_decode_string(ptr, &el->el_scratch); 554 if (!dptr) 555 continue; 556 if (len > 0 && dptr[len - 1] == '\n') 557 --len; 558 559 /* loop until first non-space char or EOL */ 560 while (*dptr != '\0' && Isspace(*dptr)) 561 dptr++; 562 if (*dptr == '#') 563 continue; /* ignore, this is a comment line */ 564 if ((error = parse_line(el, dptr)) == -1) 565 break; 566 } 567 568 el_free(path); 569 (void) fclose(fp); 570 return error; 571 } 572 573 574 /* el_resize(): 575 * Called from program when terminal is resized 576 */ 577 public void 578 el_resize(EditLine *el) 579 { 580 int lins, cols; 581 sigset_t oset, nset; 582 583 (void) sigemptyset(&nset); 584 (void) sigaddset(&nset, SIGWINCH); 585 (void) sigprocmask(SIG_BLOCK, &nset, &oset); 586 587 /* get the correct window size */ 588 if (terminal_get_size(el, &lins, &cols)) 589 terminal_change_size(el, lins, cols); 590 591 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 592 } 593 594 595 /* el_beep(): 596 * Called from the program to beep 597 */ 598 public void 599 el_beep(EditLine *el) 600 { 601 602 terminal_beep(el); 603 } 604 605 606 /* el_editmode() 607 * Set the state of EDIT_DISABLED from the `edit' command. 608 */ 609 protected int 610 /*ARGSUSED*/ 611 el_editmode(EditLine *el, int argc, const Char **argv) 612 { 613 const Char *how; 614 615 if (argv == NULL || argc != 2 || argv[1] == NULL) 616 return -1; 617 618 how = argv[1]; 619 if (Strcmp(how, STR("on")) == 0) { 620 el->el_flags &= ~EDIT_DISABLED; 621 tty_rawmode(el); 622 } else if (Strcmp(how, STR("off")) == 0) { 623 tty_cookedmode(el); 624 el->el_flags |= EDIT_DISABLED; 625 } 626 else { 627 (void) fprintf(el->el_errfile, "edit: Bad value `" FSTR "'.\n", 628 how); 629 return -1; 630 } 631 return 0; 632 } 633