1 /* $OpenBSD: el.c,v 1.14 2004/08/23 18:31:25 otto Exp $ */ 2 /* $NetBSD: el.c,v 1.36 2003/10/18 23:48:42 christos Exp $ */ 3 4 /*- 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Christos Zoulas of Cornell University. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "config.h" 37 #if !defined(lint) && !defined(SCCSID) 38 #if 0 39 static char sccsid[] = "@(#)el.c 8.2 (Berkeley) 1/3/94"; 40 #else 41 static const char rcsid[] = "$OpenBSD: el.c,v 1.14 2004/08/23 18:31:25 otto Exp $"; 42 #endif 43 #endif /* not lint && not SCCSID */ 44 45 /* 46 * el.c: EditLine interface functions 47 */ 48 #include <sys/types.h> 49 #include <sys/param.h> 50 #include <string.h> 51 #include <stdlib.h> 52 #include <stdarg.h> 53 #include "el.h" 54 55 /* el_init(): 56 * Initialize editline and set default parameters. 57 */ 58 public EditLine * 59 el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr) 60 { 61 62 EditLine *el = (EditLine *) el_malloc(sizeof(EditLine)); 63 64 if (el == NULL) 65 return (NULL); 66 67 memset(el, 0, sizeof(EditLine)); 68 69 el->el_infd = fileno(fin); 70 el->el_outfile = fout; 71 el->el_errfile = ferr; 72 if ((el->el_prog = el_strdup(prog)) == NULL) { 73 el_free(el); 74 return NULL; 75 } 76 77 /* 78 * Initialize all the modules. Order is important!!! 79 */ 80 el->el_flags = 0; 81 82 if (term_init(el) == -1) { 83 el_free(el->el_prog); 84 el_free(el); 85 return NULL; 86 } 87 (void) key_init(el); 88 (void) map_init(el); 89 if (tty_init(el) == -1) 90 el->el_flags |= NO_TTY; 91 (void) ch_init(el); 92 (void) search_init(el); 93 (void) hist_init(el); 94 (void) prompt_init(el); 95 (void) sig_init(el); 96 (void) read_init(el); 97 98 return (el); 99 } 100 101 102 /* el_end(): 103 * Clean up. 104 */ 105 public void 106 el_end(EditLine *el) 107 { 108 109 if (el == NULL) 110 return; 111 112 el_reset(el); 113 114 term_end(el); 115 key_end(el); 116 map_end(el); 117 tty_end(el); 118 ch_end(el); 119 search_end(el); 120 hist_end(el); 121 prompt_end(el); 122 sig_end(el); 123 124 el_free((ptr_t) el->el_prog); 125 el_free((ptr_t) el); 126 } 127 128 129 /* el_reset(): 130 * Reset the tty and the parser 131 */ 132 public void 133 el_reset(EditLine *el) 134 { 135 136 tty_cookedmode(el); 137 ch_reset(el); /* XXX: Do we want that? */ 138 } 139 140 141 /* el_set(): 142 * set the editline parameters 143 */ 144 public int 145 el_set(EditLine *el, int op, ...) 146 { 147 va_list va; 148 int rv = 0; 149 150 if (el == NULL) 151 return (-1); 152 va_start(va, op); 153 154 switch (op) { 155 case EL_PROMPT: 156 case EL_RPROMPT: 157 rv = prompt_set(el, va_arg(va, el_pfunc_t), op); 158 break; 159 160 case EL_TERMINAL: 161 rv = term_set(el, va_arg(va, char *)); 162 break; 163 164 case EL_EDITOR: 165 rv = map_set_editor(el, va_arg(va, char *)); 166 break; 167 168 case EL_SIGNAL: 169 if (va_arg(va, int)) 170 el->el_flags |= HANDLE_SIGNALS; 171 else 172 el->el_flags &= ~HANDLE_SIGNALS; 173 break; 174 175 case EL_BIND: 176 case EL_TELLTC: 177 case EL_SETTC: 178 case EL_ECHOTC: 179 case EL_SETTY: 180 { 181 const char *argv[20]; 182 int i; 183 184 for (i = 1; i < 20; i++) 185 if ((argv[i] = va_arg(va, char *)) == NULL) 186 break; 187 188 switch (op) { 189 case EL_BIND: 190 argv[0] = "bind"; 191 rv = map_bind(el, i, argv); 192 break; 193 194 case EL_TELLTC: 195 argv[0] = "telltc"; 196 rv = term_telltc(el, i, argv); 197 break; 198 199 case EL_SETTC: 200 argv[0] = "settc"; 201 rv = term_settc(el, i, argv); 202 break; 203 204 case EL_ECHOTC: 205 argv[0] = "echotc"; 206 rv = term_echotc(el, i, argv); 207 break; 208 209 case EL_SETTY: 210 argv[0] = "setty"; 211 rv = tty_stty(el, i, argv); 212 break; 213 214 default: 215 rv = -1; 216 EL_ABORT((el->el_errfile, "Bad op %d\n", op)); 217 break; 218 } 219 break; 220 } 221 222 case EL_ADDFN: 223 { 224 char *name = va_arg(va, char *); 225 char *help = va_arg(va, char *); 226 el_func_t func = va_arg(va, el_func_t); 227 228 rv = map_addfunc(el, name, help, func); 229 break; 230 } 231 232 case EL_HIST: 233 { 234 hist_fun_t func = va_arg(va, hist_fun_t); 235 ptr_t ptr = va_arg(va, char *); 236 237 rv = hist_set(el, func, ptr); 238 break; 239 } 240 241 case EL_EDITMODE: 242 if (va_arg(va, int)) 243 el->el_flags &= ~EDIT_DISABLED; 244 else 245 el->el_flags |= EDIT_DISABLED; 246 rv = 0; 247 break; 248 249 case EL_GETCFN: 250 { 251 el_rfunc_t rc = va_arg(va, el_rfunc_t); 252 rv = el_read_setfn(el, rc); 253 break; 254 } 255 256 case EL_CLIENTDATA: 257 el->el_data = va_arg(va, void *); 258 break; 259 260 case EL_UNBUFFERED: 261 rv = va_arg(va, int); 262 if (rv && !(el->el_flags & UNBUFFERED)) { 263 el->el_flags |= UNBUFFERED; 264 read_prepare(el); 265 } else if (!rv && (el->el_flags & UNBUFFERED)) { 266 el->el_flags &= ~UNBUFFERED; 267 read_finish(el); 268 } 269 rv = 0; 270 break; 271 272 case EL_PREP_TERM: 273 rv = va_arg(va, int); 274 if (rv) 275 read_prepare(el); 276 else 277 read_finish(el); 278 rv = 0; 279 break; 280 281 default: 282 rv = -1; 283 break; 284 } 285 286 va_end(va); 287 return (rv); 288 } 289 290 291 /* el_get(): 292 * retrieve the editline parameters 293 */ 294 public int 295 el_get(EditLine *el, int op, void *ret) 296 { 297 int rv; 298 299 if (el == NULL || ret == NULL) 300 return (-1); 301 switch (op) { 302 case EL_PROMPT: 303 case EL_RPROMPT: 304 rv = prompt_get(el, (void *) &ret, op); 305 break; 306 307 case EL_EDITOR: 308 rv = map_get_editor(el, (void *) &ret); 309 break; 310 311 case EL_SIGNAL: 312 *((int *) ret) = (el->el_flags & HANDLE_SIGNALS); 313 rv = 0; 314 break; 315 316 case EL_EDITMODE: 317 *((int *) ret) = (!(el->el_flags & EDIT_DISABLED)); 318 rv = 0; 319 break; 320 321 case EL_TERMINAL: 322 term_get(el, (const char **)ret); 323 rv = 0; 324 break; 325 326 #if 0 /* XXX */ 327 case EL_BIND: 328 case EL_TELLTC: 329 case EL_SETTC: 330 case EL_ECHOTC: 331 case EL_SETTY: 332 { 333 const char *argv[20]; 334 int i; 335 336 for (i = 1; i < sizeof(argv) / sizeof(argv[0]); i++) 337 if ((argv[i] = va_arg(va, char *)) == NULL) 338 break; 339 340 switch (op) { 341 case EL_BIND: 342 argv[0] = "bind"; 343 rv = map_bind(el, i, argv); 344 break; 345 346 case EL_TELLTC: 347 argv[0] = "telltc"; 348 rv = term_telltc(el, i, argv); 349 break; 350 351 case EL_SETTC: 352 argv[0] = "settc"; 353 rv = term_settc(el, i, argv); 354 break; 355 356 case EL_ECHOTC: 357 argv[0] = "echotc"; 358 rv = term_echotc(el, i, argv); 359 break; 360 361 case EL_SETTY: 362 argv[0] = "setty"; 363 rv = tty_stty(el, i, argv); 364 break; 365 366 default: 367 rv = -1; 368 EL_ABORT((el->errfile, "Bad op %d\n", op)); 369 break; 370 } 371 break; 372 } 373 374 case EL_ADDFN: 375 { 376 char *name = va_arg(va, char *); 377 char *help = va_arg(va, char *); 378 el_func_t func = va_arg(va, el_func_t); 379 380 rv = map_addfunc(el, name, help, func); 381 break; 382 } 383 384 case EL_HIST: 385 { 386 hist_fun_t func = va_arg(va, hist_fun_t); 387 ptr_t ptr = va_arg(va, char *); 388 rv = hist_set(el, func, ptr); 389 } 390 break; 391 #endif /* XXX */ 392 393 case EL_GETCFN: 394 *((el_rfunc_t *)ret) = el_read_getfn(el); 395 rv = 0; 396 break; 397 398 case EL_CLIENTDATA: 399 *((void **)ret) = el->el_data; 400 rv = 0; 401 break; 402 403 case EL_UNBUFFERED: 404 *((int *) ret) = (!(el->el_flags & UNBUFFERED)); 405 rv = 0; 406 break; 407 408 default: 409 rv = -1; 410 } 411 412 return (rv); 413 } 414 415 416 /* el_line(): 417 * Return editing info 418 */ 419 public const LineInfo * 420 el_line(EditLine *el) 421 { 422 423 return (const LineInfo *) (void *) &el->el_line; 424 } 425 426 427 /* el_source(): 428 * Source a file 429 */ 430 public int 431 el_source(EditLine *el, const char *fname) 432 { 433 FILE *fp; 434 size_t len; 435 char *ptr, *lptr = NULL; 436 437 fp = NULL; 438 if (fname == NULL) { 439 #ifdef HAVE_ISSETUGID 440 static const char elpath[] = "/.editrc"; 441 char path[MAXPATHLEN]; 442 443 if (issetugid()) 444 return (-1); 445 if ((ptr = getenv("HOME")) == NULL) 446 return (-1); 447 if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path)) 448 return (-1); 449 if (strlcat(path, elpath, sizeof(path)) >= sizeof(path)) 450 return (-1); 451 fname = path; 452 #else 453 /* 454 * If issetugid() is missing, always return an error, in order 455 * to keep from inadvertently opening up the user to a security 456 * hole. 457 */ 458 return (-1); 459 #endif 460 } 461 if (fp == NULL) 462 fp = fopen(fname, "r"); 463 if (fp == NULL) 464 return (-1); 465 466 while ((ptr = fgetln(fp, &len)) != NULL) { 467 if (ptr[len - 1] == '\n') 468 ptr[len - 1] = '\0'; 469 else { 470 if ((lptr = (char *)malloc(len + 1)) == NULL) { 471 (void) fclose(fp); 472 return (-1); 473 } 474 memcpy(lptr, ptr, len); 475 lptr[len] = '\0'; 476 ptr = lptr; 477 } 478 if (parse_line(el, ptr) == -1) { 479 free(lptr); 480 (void) fclose(fp); 481 return (-1); 482 } 483 } 484 free(lptr); 485 (void) fclose(fp); 486 return (0); 487 } 488 489 490 /* el_resize(): 491 * Called from program when terminal is resized 492 */ 493 public void 494 el_resize(EditLine *el) 495 { 496 int lins, cols; 497 sigset_t oset, nset; 498 499 (void) sigemptyset(&nset); 500 (void) sigaddset(&nset, SIGWINCH); 501 (void) sigprocmask(SIG_BLOCK, &nset, &oset); 502 503 /* get the correct window size */ 504 if (term_get_size(el, &lins, &cols)) 505 term_change_size(el, lins, cols); 506 507 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 508 } 509 510 511 /* el_beep(): 512 * Called from the program to beep 513 */ 514 public void 515 el_beep(EditLine *el) 516 { 517 518 term_beep(el); 519 } 520 521 522 /* el_editmode() 523 * Set the state of EDIT_DISABLED from the `edit' command. 524 */ 525 protected int 526 /*ARGSUSED*/ 527 el_editmode(EditLine *el, int argc, const char **argv) 528 { 529 const char *how; 530 531 if (argv == NULL || argc != 2 || argv[1] == NULL) 532 return (-1); 533 534 how = argv[1]; 535 if (strcmp(how, "on") == 0) 536 el->el_flags &= ~EDIT_DISABLED; 537 else if (strcmp(how, "off") == 0) 538 el->el_flags |= EDIT_DISABLED; 539 else { 540 (void) fprintf(el->el_errfile, "edit: Bad value `%s'.\n", how); 541 return (-1); 542 } 543 return (0); 544 } 545