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 /* 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_PROMPT_ESC: 198 case EL_RPROMPT_ESC: { 199 el_pfunc_t p = va_arg(ap, el_pfunc_t); 200 int c = va_arg(ap, int); 201 202 rv = prompt_set(el, p, c, op, 1); 203 break; 204 } 205 206 case EL_TERMINAL: 207 rv = terminal_set(el, va_arg(ap, char *)); 208 break; 209 210 case EL_EDITOR: 211 rv = map_set_editor(el, va_arg(ap, Char *)); 212 break; 213 214 case EL_SIGNAL: 215 if (va_arg(ap, int)) 216 el->el_flags |= HANDLE_SIGNALS; 217 else 218 el->el_flags &= ~HANDLE_SIGNALS; 219 break; 220 221 case EL_BIND: 222 case EL_TELLTC: 223 case EL_SETTC: 224 case EL_ECHOTC: 225 case EL_SETTY: 226 { 227 const Char *argv[20]; 228 int i; 229 230 for (i = 1; i < (int)__arraycount(argv); i++) 231 if ((argv[i] = va_arg(ap, Char *)) == NULL) 232 break; 233 234 switch (op) { 235 case EL_BIND: 236 argv[0] = STR("bind"); 237 rv = map_bind(el, i, argv); 238 break; 239 240 case EL_TELLTC: 241 argv[0] = STR("telltc"); 242 rv = terminal_telltc(el, i, argv); 243 break; 244 245 case EL_SETTC: 246 argv[0] = STR("settc"); 247 rv = terminal_settc(el, i, argv); 248 break; 249 250 case EL_ECHOTC: 251 argv[0] = STR("echotc"); 252 rv = terminal_echotc(el, i, argv); 253 break; 254 255 case EL_SETTY: 256 argv[0] = STR("setty"); 257 rv = tty_stty(el, i, argv); 258 break; 259 260 default: 261 rv = -1; 262 EL_ABORT((el->el_errfile, "Bad op %d\n", op)); 263 break; 264 } 265 break; 266 } 267 268 case EL_ADDFN: 269 { 270 Char *name = va_arg(ap, Char *); 271 Char *help = va_arg(ap, Char *); 272 el_func_t func = va_arg(ap, el_func_t); 273 274 rv = map_addfunc(el, name, help, func); 275 break; 276 } 277 278 case EL_HIST: 279 { 280 hist_fun_t func = va_arg(ap, hist_fun_t); 281 void *ptr = va_arg(ap, void *); 282 283 rv = hist_set(el, func, ptr); 284 if (!(el->el_flags & CHARSET_IS_UTF8)) 285 el->el_flags &= ~NARROW_HISTORY; 286 break; 287 } 288 289 case EL_EDITMODE: 290 if (va_arg(ap, int)) 291 el->el_flags &= ~EDIT_DISABLED; 292 else 293 el->el_flags |= EDIT_DISABLED; 294 rv = 0; 295 break; 296 297 case EL_GETCFN: 298 { 299 el_rfunc_t rc = va_arg(ap, el_rfunc_t); 300 rv = el_read_setfn(el, rc); 301 el->el_flags &= ~NARROW_READ; 302 break; 303 } 304 305 case EL_CLIENTDATA: 306 el->el_data = va_arg(ap, void *); 307 break; 308 309 case EL_UNBUFFERED: 310 rv = va_arg(ap, int); 311 if (rv && !(el->el_flags & UNBUFFERED)) { 312 el->el_flags |= UNBUFFERED; 313 read_prepare(el); 314 } else if (!rv && (el->el_flags & UNBUFFERED)) { 315 el->el_flags &= ~UNBUFFERED; 316 read_finish(el); 317 } 318 rv = 0; 319 break; 320 321 case EL_PREP_TERM: 322 rv = va_arg(ap, int); 323 if (rv) 324 (void) tty_rawmode(el); 325 else 326 (void) tty_cookedmode(el); 327 rv = 0; 328 break; 329 330 case EL_SETFP: 331 { 332 FILE *fp; 333 int what; 334 335 what = va_arg(ap, int); 336 fp = va_arg(ap, FILE *); 337 338 rv = 0; 339 switch (what) { 340 case 0: 341 el->el_infile = fp; 342 el->el_infd = fileno(fp); 343 break; 344 case 1: 345 el->el_outfile = fp; 346 el->el_outfd = fileno(fp); 347 break; 348 case 2: 349 el->el_errfile = fp; 350 el->el_errfd = fileno(fp); 351 break; 352 default: 353 rv = -1; 354 break; 355 } 356 break; 357 } 358 359 case EL_REFRESH: 360 re_clear_display(el); 361 re_refresh(el); 362 terminal__flush(el); 363 break; 364 365 default: 366 rv = -1; 367 break; 368 } 369 370 va_end(ap); 371 return rv; 372 } 373 374 375 /* el_get(): 376 * retrieve the editline parameters 377 */ 378 public int 379 FUN(el,get)(EditLine *el, int op, ...) 380 { 381 va_list ap; 382 int rv; 383 384 if (el == NULL) 385 return -1; 386 387 va_start(ap, op); 388 389 switch (op) { 390 case EL_PROMPT: 391 case EL_RPROMPT: { 392 el_pfunc_t *p = va_arg(ap, el_pfunc_t *); 393 rv = prompt_get(el, p, 0, op); 394 break; 395 } 396 case EL_PROMPT_ESC: 397 case EL_RPROMPT_ESC: { 398 el_pfunc_t *p = va_arg(ap, el_pfunc_t *); 399 Char *c = va_arg(ap, Char *); 400 401 rv = prompt_get(el, p, c, op); 402 break; 403 } 404 405 case EL_EDITOR: 406 rv = map_get_editor(el, va_arg(ap, const Char **)); 407 break; 408 409 case EL_SIGNAL: 410 *va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS); 411 rv = 0; 412 break; 413 414 case EL_EDITMODE: 415 *va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED); 416 rv = 0; 417 break; 418 419 case EL_TERMINAL: 420 terminal_get(el, va_arg(ap, const char **)); 421 rv = 0; 422 break; 423 424 case EL_GETTC: 425 { 426 static char name[] = "gettc"; 427 char *argv[20]; 428 int i; 429 430 for (i = 1; i < (int)__arraycount(argv); i++) 431 if ((argv[i] = va_arg(ap, char *)) == NULL) 432 break; 433 434 argv[0] = name; 435 rv = terminal_gettc(el, i, argv); 436 break; 437 } 438 439 case EL_GETCFN: 440 *va_arg(ap, el_rfunc_t *) = el_read_getfn(el); 441 rv = 0; 442 break; 443 444 case EL_CLIENTDATA: 445 *va_arg(ap, void **) = el->el_data; 446 rv = 0; 447 break; 448 449 case EL_UNBUFFERED: 450 *va_arg(ap, int *) = (el->el_flags & UNBUFFERED) != 0; 451 rv = 0; 452 break; 453 454 case EL_GETFP: 455 { 456 int what; 457 FILE **fpp; 458 459 what = va_arg(ap, int); 460 fpp = va_arg(ap, FILE **); 461 rv = 0; 462 switch (what) { 463 case 0: 464 *fpp = el->el_infile; 465 break; 466 case 1: 467 *fpp = el->el_outfile; 468 break; 469 case 2: 470 *fpp = el->el_errfile; 471 break; 472 default: 473 rv = -1; 474 break; 475 } 476 break; 477 } 478 default: 479 rv = -1; 480 break; 481 } 482 va_end(ap); 483 484 return rv; 485 } 486 487 488 /* el_line(): 489 * Return editing info 490 */ 491 public const TYPE(LineInfo) * 492 FUN(el,line)(EditLine *el) 493 { 494 495 return (const TYPE(LineInfo) *)(void *)&el->el_line; 496 } 497 498 499 /* el_source(): 500 * Source a file 501 */ 502 public int 503 el_source(EditLine *el, const char *fname) 504 { 505 FILE *fp; 506 size_t len; 507 char *ptr; 508 char *path = NULL; 509 const Char *dptr; 510 int error = 0; 511 512 fp = NULL; 513 if (fname == NULL) { 514 #ifdef HAVE_ISSETUGID 515 static const char elpath[] = "/.editrc"; 516 size_t plen = sizeof(elpath); 517 518 if (issetugid()) 519 return -1; 520 if ((ptr = getenv("HOME")) == NULL) 521 return -1; 522 plen += strlen(ptr); 523 if ((path = el_malloc(plen * sizeof(*path))) == NULL) 524 return -1; 525 (void)snprintf(path, plen, "%s%s", ptr, elpath); 526 fname = path; 527 #else 528 /* 529 * If issetugid() is missing, always return an error, in order 530 * to keep from inadvertently opening up the user to a security 531 * hole. 532 */ 533 return -1; 534 #endif 535 } 536 if (fp == NULL) 537 fp = fopen(fname, "r"); 538 if (fp == NULL) { 539 el_free(path); 540 return -1; 541 } 542 543 while ((ptr = fgetln(fp, &len)) != NULL) { 544 if (*ptr == '\n') 545 continue; /* Empty line. */ 546 dptr = ct_decode_string(ptr, &el->el_scratch); 547 if (!dptr) 548 continue; 549 if (len > 0 && dptr[len - 1] == '\n') 550 --len; 551 552 /* loop until first non-space char or EOL */ 553 while (*dptr != '\0' && Isspace(*dptr)) 554 dptr++; 555 if (*dptr == '#') 556 continue; /* ignore, this is a comment line */ 557 if ((error = parse_line(el, dptr)) == -1) 558 break; 559 } 560 561 el_free(path); 562 (void) fclose(fp); 563 return error; 564 } 565 566 567 /* el_resize(): 568 * Called from program when terminal is resized 569 */ 570 public void 571 el_resize(EditLine *el) 572 { 573 int lins, cols; 574 sigset_t oset, nset; 575 576 (void) sigemptyset(&nset); 577 (void) sigaddset(&nset, SIGWINCH); 578 (void) sigprocmask(SIG_BLOCK, &nset, &oset); 579 580 /* get the correct window size */ 581 if (terminal_get_size(el, &lins, &cols)) 582 terminal_change_size(el, lins, cols); 583 584 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 585 } 586 587 588 /* el_beep(): 589 * Called from the program to beep 590 */ 591 public void 592 el_beep(EditLine *el) 593 { 594 595 terminal_beep(el); 596 } 597 598 599 /* el_editmode() 600 * Set the state of EDIT_DISABLED from the `edit' command. 601 */ 602 protected int 603 /*ARGSUSED*/ 604 el_editmode(EditLine *el, int argc, const Char **argv) 605 { 606 const Char *how; 607 608 if (argv == NULL || argc != 2 || argv[1] == NULL) 609 return -1; 610 611 how = argv[1]; 612 if (Strcmp(how, STR("on")) == 0) { 613 el->el_flags &= ~EDIT_DISABLED; 614 tty_rawmode(el); 615 } else if (Strcmp(how, STR("off")) == 0) { 616 tty_cookedmode(el); 617 el->el_flags |= EDIT_DISABLED; 618 } 619 else { 620 (void) fprintf(el->el_errfile, "edit: Bad value `" FSTR "'.\n", 621 how); 622 return -1; 623 } 624 return 0; 625 } 626