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