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