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