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