1 /* $NetBSD: el.c,v 1.46 2008/09/10 15:45:37 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.46 2008/09/10 15:45:37 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 rv = prompt_set(el, va_arg(ap, el_pfunc_t), op); 160 break; 161 162 case EL_TERMINAL: 163 rv = term_set(el, va_arg(ap, char *)); 164 break; 165 166 case EL_EDITOR: 167 rv = map_set_editor(el, va_arg(ap, char *)); 168 break; 169 170 case EL_SIGNAL: 171 if (va_arg(ap, int)) 172 el->el_flags |= HANDLE_SIGNALS; 173 else 174 el->el_flags &= ~HANDLE_SIGNALS; 175 break; 176 177 case EL_BIND: 178 case EL_TELLTC: 179 case EL_SETTC: 180 case EL_GETTC: 181 case EL_ECHOTC: 182 case EL_SETTY: 183 { 184 const char *argv[20]; 185 int i; 186 187 for (i = 1; i < 20; i++) 188 if ((argv[i] = va_arg(ap, char *)) == NULL) 189 break; 190 191 switch (op) { 192 case EL_BIND: 193 argv[0] = "bind"; 194 rv = map_bind(el, i, argv); 195 break; 196 197 case EL_TELLTC: 198 argv[0] = "telltc"; 199 rv = term_telltc(el, i, argv); 200 break; 201 202 case EL_SETTC: 203 argv[0] = "settc"; 204 rv = term_settc(el, i, argv); 205 break; 206 207 case EL_ECHOTC: 208 argv[0] = "echotc"; 209 rv = term_echotc(el, i, argv); 210 break; 211 212 case EL_SETTY: 213 argv[0] = "setty"; 214 rv = tty_stty(el, i, argv); 215 break; 216 217 default: 218 rv = -1; 219 EL_ABORT((el->el_errfile, "Bad op %d\n", op)); 220 break; 221 } 222 break; 223 } 224 225 case EL_ADDFN: 226 { 227 char *name = va_arg(ap, char *); 228 char *help = va_arg(ap, char *); 229 el_func_t func = va_arg(ap, el_func_t); 230 231 rv = map_addfunc(el, name, help, func); 232 break; 233 } 234 235 case EL_HIST: 236 { 237 hist_fun_t func = va_arg(ap, hist_fun_t); 238 ptr_t ptr = va_arg(ap, char *); 239 240 rv = hist_set(el, func, ptr); 241 break; 242 } 243 244 case EL_EDITMODE: 245 if (va_arg(ap, int)) 246 el->el_flags &= ~EDIT_DISABLED; 247 else 248 el->el_flags |= EDIT_DISABLED; 249 rv = 0; 250 break; 251 252 case EL_GETCFN: 253 { 254 el_rfunc_t rc = va_arg(ap, el_rfunc_t); 255 rv = el_read_setfn(el, rc); 256 break; 257 } 258 259 case EL_CLIENTDATA: 260 el->el_data = va_arg(ap, void *); 261 break; 262 263 case EL_UNBUFFERED: 264 rv = va_arg(ap, int); 265 if (rv && !(el->el_flags & UNBUFFERED)) { 266 el->el_flags |= UNBUFFERED; 267 read_prepare(el); 268 } else if (!rv && (el->el_flags & UNBUFFERED)) { 269 el->el_flags &= ~UNBUFFERED; 270 read_finish(el); 271 } 272 rv = 0; 273 break; 274 275 case EL_PREP_TERM: 276 rv = va_arg(ap, int); 277 if (rv) 278 (void) tty_rawmode(el); 279 else 280 (void) tty_cookedmode(el); 281 rv = 0; 282 break; 283 284 case EL_SETFP: 285 { 286 FILE *fp; 287 int what; 288 289 what = va_arg(ap, int); 290 fp = va_arg(ap, FILE *); 291 292 rv = 0; 293 switch (what) { 294 case 0: 295 el->el_infile = fp; 296 el->el_infd = fileno(fp); 297 break; 298 case 1: 299 el->el_outfile = fp; 300 break; 301 case 2: 302 el->el_errfile = fp; 303 break; 304 default: 305 rv = -1; 306 break; 307 } 308 break; 309 } 310 311 case EL_REFRESH: 312 re_clear_display(el); 313 re_refresh(el); 314 term__flush(el); 315 break; 316 317 default: 318 rv = -1; 319 break; 320 } 321 322 va_end(ap); 323 return (rv); 324 } 325 326 327 /* el_get(): 328 * retrieve the editline parameters 329 */ 330 public int 331 el_get(EditLine *el, int op, ...) 332 { 333 va_list ap; 334 int rv; 335 336 if (el == NULL) 337 return -1; 338 339 va_start(ap, op); 340 341 switch (op) { 342 case EL_PROMPT: 343 case EL_RPROMPT: 344 rv = prompt_get(el, va_arg(ap, el_pfunc_t *), op); 345 break; 346 347 case EL_EDITOR: 348 rv = map_get_editor(el, va_arg(ap, const char **)); 349 break; 350 351 case EL_SIGNAL: 352 *va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS); 353 rv = 0; 354 break; 355 356 case EL_EDITMODE: 357 *va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED); 358 rv = 0; 359 break; 360 361 case EL_TERMINAL: 362 term_get(el, va_arg(ap, const char **)); 363 rv = 0; 364 break; 365 366 case EL_GETTC: 367 { 368 static char name[] = "gettc"; 369 char *argv[20]; 370 int i; 371 372 for (i = 1; i < sizeof(argv) / sizeof(argv[0]); i++) 373 if ((argv[i] = va_arg(ap, char *)) == NULL) 374 break; 375 376 switch (op) { 377 case EL_GETTC: 378 argv[0] = name; 379 rv = term_gettc(el, i, argv); 380 break; 381 382 default: 383 rv = -1; 384 EL_ABORT((el->el_errfile, "Bad op %d\n", op)); 385 break; 386 } 387 break; 388 } 389 390 #if 0 /* XXX */ 391 case EL_ADDFN: 392 { 393 char *name = va_arg(ap, char *); 394 char *help = va_arg(ap, char *); 395 el_func_t func = va_arg(ap, el_func_t); 396 397 rv = map_addfunc(el, name, help, func); 398 break; 399 } 400 401 case EL_HIST: 402 { 403 hist_fun_t func = va_arg(ap, hist_fun_t); 404 ptr_t ptr = va_arg(ap, char *); 405 rv = hist_set(el, func, ptr); 406 } 407 break; 408 #endif /* XXX */ 409 410 case EL_GETCFN: 411 *va_arg(ap, el_rfunc_t *) = el_read_getfn(el); 412 rv = 0; 413 break; 414 415 case EL_CLIENTDATA: 416 *va_arg(ap, void **) = el->el_data; 417 rv = 0; 418 break; 419 420 case EL_UNBUFFERED: 421 *va_arg(ap, int *) = (!(el->el_flags & UNBUFFERED)); 422 rv = 0; 423 break; 424 425 case EL_GETFP: 426 { 427 int what; 428 FILE **fpp; 429 430 what = va_arg(ap, int); 431 fpp = va_arg(ap, FILE **); 432 rv = 0; 433 switch (what) { 434 case 0: 435 *fpp = el->el_infile; 436 break; 437 case 1: 438 *fpp = el->el_outfile; 439 break; 440 case 2: 441 *fpp = el->el_errfile; 442 break; 443 default: 444 rv = -1; 445 break; 446 } 447 break; 448 } 449 default: 450 rv = -1; 451 break; 452 } 453 va_end(ap); 454 455 return (rv); 456 } 457 458 459 /* el_line(): 460 * Return editing info 461 */ 462 public const LineInfo * 463 el_line(EditLine *el) 464 { 465 466 return (const LineInfo *) (void *) &el->el_line; 467 } 468 469 470 /* el_source(): 471 * Source a file 472 */ 473 public int 474 el_source(EditLine *el, const char *fname) 475 { 476 FILE *fp; 477 size_t len; 478 char *ptr; 479 480 fp = NULL; 481 if (fname == NULL) { 482 #ifdef HAVE_ISSETUGID 483 static const char elpath[] = "/.editrc"; 484 char path[MAXPATHLEN]; 485 486 if (issetugid()) 487 return (-1); 488 if ((ptr = getenv("HOME")) == NULL) 489 return (-1); 490 if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path)) 491 return (-1); 492 if (strlcat(path, elpath, sizeof(path)) >= sizeof(path)) 493 return (-1); 494 fname = path; 495 #else 496 /* 497 * If issetugid() is missing, always return an error, in order 498 * to keep from inadvertently opening up the user to a security 499 * hole. 500 */ 501 return (-1); 502 #endif 503 } 504 if (fp == NULL) 505 fp = fopen(fname, "r"); 506 if (fp == NULL) 507 return (-1); 508 509 while ((ptr = fgetln(fp, &len)) != NULL) { 510 if (len > 0 && ptr[len - 1] == '\n') 511 --len; 512 ptr[len] = '\0'; 513 if (parse_line(el, ptr) == -1) { 514 (void) fclose(fp); 515 return (-1); 516 } 517 } 518 519 (void) fclose(fp); 520 return (0); 521 } 522 523 524 /* el_resize(): 525 * Called from program when terminal is resized 526 */ 527 public void 528 el_resize(EditLine *el) 529 { 530 int lins, cols; 531 sigset_t oset, nset; 532 533 (void) sigemptyset(&nset); 534 (void) sigaddset(&nset, SIGWINCH); 535 (void) sigprocmask(SIG_BLOCK, &nset, &oset); 536 537 /* get the correct window size */ 538 if (term_get_size(el, &lins, &cols)) 539 term_change_size(el, lins, cols); 540 541 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 542 } 543 544 545 /* el_beep(): 546 * Called from the program to beep 547 */ 548 public void 549 el_beep(EditLine *el) 550 { 551 552 term_beep(el); 553 } 554 555 556 /* el_editmode() 557 * Set the state of EDIT_DISABLED from the `edit' command. 558 */ 559 protected int 560 /*ARGSUSED*/ 561 el_editmode(EditLine *el, int argc, const char **argv) 562 { 563 const char *how; 564 565 if (argv == NULL || argc != 2 || argv[1] == NULL) 566 return (-1); 567 568 how = argv[1]; 569 if (strcmp(how, "on") == 0) { 570 el->el_flags &= ~EDIT_DISABLED; 571 tty_rawmode(el); 572 } else if (strcmp(how, "off") == 0) { 573 tty_cookedmode(el); 574 el->el_flags |= EDIT_DISABLED; 575 } 576 else { 577 (void) fprintf(el->el_errfile, "edit: Bad value `%s'.\n", how); 578 return (-1); 579 } 580 return (0); 581 } 582