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