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