histedit.c revision 267654
1321936Shselasky/*- 2321936Shselasky * Copyright (c) 1993 3321936Shselasky * The Regents of the University of California. All rights reserved. 4321936Shselasky * 5321936Shselasky * This code is derived from software contributed to Berkeley by 6321936Shselasky * Kenneth Almquist. 7321936Shselasky * 8321936Shselasky * Redistribution and use in source and binary forms, with or without 9321936Shselasky * modification, are permitted provided that the following conditions 10321936Shselasky * are met: 11321936Shselasky * 1. Redistributions of source code must retain the above copyright 12321936Shselasky * notice, this list of conditions and the following disclaimer. 13321936Shselasky * 2. Redistributions in binary form must reproduce the above copyright 14321936Shselasky * notice, this list of conditions and the following disclaimer in the 15321936Shselasky * documentation and/or other materials provided with the distribution. 16321936Shselasky * 4. Neither the name of the University nor the names of its contributors 17321936Shselasky * may be used to endorse or promote products derived from this software 18321936Shselasky * without specific prior written permission. 19321936Shselasky * 20321936Shselasky * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21321936Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22321936Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23321936Shselasky * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24321936Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25321936Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26321936Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27321936Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28321936Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29321936Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30321936Shselasky * SUCH DAMAGE. 31321936Shselasky */ 32321936Shselasky 33321936Shselasky#ifndef lint 34321936Shselasky#if 0 35321936Shselaskystatic char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95"; 36321936Shselasky#endif 37321936Shselasky#endif /* not lint */ 38321936Shselasky#include <sys/cdefs.h> 39321936Shselasky__FBSDID("$FreeBSD: releng/9.3/bin/sh/histedit.c 231790 2012-02-15 22:45:57Z jilles $"); 40321936Shselasky 41321936Shselasky#include <sys/param.h> 42321936Shselasky#include <limits.h> 43321936Shselasky#include <paths.h> 44321936Shselasky#include <stdio.h> 45321936Shselasky#include <stdlib.h> 46321936Shselasky#include <unistd.h> 47321936Shselasky/* 48321936Shselasky * Editline and history functions (and glue). 49321936Shselasky */ 50321936Shselasky#include "shell.h" 51321936Shselasky#include "parser.h" 52321936Shselasky#include "var.h" 53321936Shselasky#include "options.h" 54321936Shselasky#include "main.h" 55321936Shselasky#include "output.h" 56321936Shselasky#include "mystring.h" 57321936Shselasky#ifndef NO_HISTORY 58321936Shselasky#include "myhistedit.h" 59321936Shselasky#include "error.h" 60321936Shselasky#include "eval.h" 61321936Shselasky#include "memalloc.h" 62321936Shselasky#include "builtins.h" 63321936Shselasky 64321936Shselasky#define MAXHISTLOOPS 4 /* max recursions through fc */ 65321936Shselasky#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */ 66321936Shselasky 67321936ShselaskyHistory *hist; /* history cookie */ 68321936ShselaskyEditLine *el; /* editline cookie */ 69321936Shselaskyint displayhist; 70321936Shselaskystatic FILE *el_in, *el_out, *el_err; 71321936Shselasky 72321936Shselaskystatic char *fc_replace(const char *, char *, char *); 73321936Shselaskystatic int not_fcnumber(const char *); 74321936Shselaskystatic int str_to_event(const char *, int); 75321936Shselasky 76321936Shselasky/* 77321936Shselasky * Set history and editing status. Called whenever the status may 78321936Shselasky * have changed (figures out what to do). 79321936Shselasky */ 80321936Shselaskyvoid 81321936Shselaskyhistedit(void) 82321936Shselasky{ 83321936Shselasky 84321936Shselasky#define editing (Eflag || Vflag) 85321936Shselasky 86321936Shselasky if (iflag) { 87321936Shselasky if (!hist) { 88321936Shselasky /* 89321936Shselasky * turn history on 90321936Shselasky */ 91321936Shselasky INTOFF; 92321936Shselasky hist = history_init(); 93321936Shselasky INTON; 94321936Shselasky 95321936Shselasky if (hist != NULL) 96321936Shselasky sethistsize(histsizeval()); 97321936Shselasky else 98321936Shselasky out2fmt_flush("sh: can't initialize history\n"); 99321936Shselasky } 100321936Shselasky if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ 101321936Shselasky /* 102321936Shselasky * turn editing on 103321936Shselasky */ 104321936Shselasky char *term; 105321936Shselasky 106321936Shselasky INTOFF; 107321936Shselasky if (el_in == NULL) 108321936Shselasky el_in = fdopen(0, "r"); 109321936Shselasky if (el_err == NULL) 110321936Shselasky el_err = fdopen(1, "w"); 111321936Shselasky if (el_out == NULL) 112321936Shselasky el_out = fdopen(2, "w"); 113321936Shselasky if (el_in == NULL || el_err == NULL || el_out == NULL) 114321936Shselasky goto bad; 115321936Shselasky term = lookupvar("TERM"); 116321936Shselasky if (term) 117321936Shselasky setenv("TERM", term, 1); 118321936Shselasky else 119321936Shselasky unsetenv("TERM"); 120321936Shselasky el = el_init(arg0, el_in, el_out, el_err); 121321936Shselasky if (el != NULL) { 122321936Shselasky if (hist) 123321936Shselasky el_set(el, EL_HIST, history, hist); 124321936Shselasky el_set(el, EL_PROMPT, getprompt); 125321936Shselasky el_set(el, EL_ADDFN, "sh-complete", 126321936Shselasky "Filename completion", 127321936Shselasky _el_fn_sh_complete); 128321936Shselasky } else { 129321936Shselaskybad: 130321936Shselasky out2fmt_flush("sh: can't initialize editing\n"); 131321936Shselasky } 132321936Shselasky INTON; 133321936Shselasky } else if (!editing && el) { 134321936Shselasky INTOFF; 135321936Shselasky el_end(el); 136321936Shselasky el = NULL; 137321936Shselasky INTON; 138321936Shselasky } 139321936Shselasky if (el) { 140321936Shselasky if (Vflag) 141321936Shselasky el_set(el, EL_EDITOR, "vi"); 142321936Shselasky else if (Eflag) 143321936Shselasky el_set(el, EL_EDITOR, "emacs"); 144321936Shselasky el_set(el, EL_BIND, "^I", "sh-complete", NULL); 145321936Shselasky el_source(el, NULL); 146321936Shselasky } 147321936Shselasky } else { 148321936Shselasky INTOFF; 149321936Shselasky if (el) { /* no editing if not interactive */ 150321936Shselasky el_end(el); 151321936Shselasky el = NULL; 152321936Shselasky } 153321936Shselasky if (hist) { 154321936Shselasky history_end(hist); 155321936Shselasky hist = NULL; 156321936Shselasky } 157321936Shselasky INTON; 158321936Shselasky } 159321936Shselasky} 160321936Shselasky 161321936Shselasky 162321936Shselaskyvoid 163321936Shselaskysethistsize(hs) 164321936Shselasky const char *hs; 165321936Shselasky{ 166321936Shselasky int histsize; 167321936Shselasky HistEvent he; 168321936Shselasky 169321936Shselasky if (hist != NULL) { 170321936Shselasky if (hs == NULL || *hs == '\0' || 171321936Shselasky (histsize = atoi(hs)) < 0) 172321936Shselasky histsize = 100; 173321936Shselasky history(hist, &he, H_SETSIZE, histsize); 174321936Shselasky history(hist, &he, H_SETUNIQUE, 1); 175321936Shselasky } 176321936Shselasky} 177321936Shselasky 178321936Shselaskyvoid 179321936Shselaskysetterm(const char *term) 180321936Shselasky{ 181321936Shselasky if (rootshell && el != NULL && term != NULL) 182321936Shselasky el_set(el, EL_TERMINAL, term); 183321936Shselasky} 184321936Shselasky 185321936Shselaskyint 186321936Shselaskyhistcmd(int argc, char **argv) 187321936Shselasky{ 188321936Shselasky int ch; 189321936Shselasky const char *editor = NULL; 190321936Shselasky HistEvent he; 191321936Shselasky int lflg = 0, nflg = 0, rflg = 0, sflg = 0; 192321936Shselasky int i, retval; 193321936Shselasky const char *firststr, *laststr; 194321936Shselasky int first, last, direction; 195321936Shselasky char *pat = NULL, *repl = NULL; 196321936Shselasky static int active = 0; 197321936Shselasky struct jmploc jmploc; 198321936Shselasky struct jmploc *savehandler; 199321936Shselasky char editfilestr[PATH_MAX]; 200321936Shselasky char *volatile editfile; 201321936Shselasky FILE *efp = NULL; 202321936Shselasky int oldhistnum; 203321936Shselasky 204321936Shselasky if (hist == NULL) 205321936Shselasky error("history not active"); 206321936Shselasky 207321936Shselasky if (argc == 1) 208321936Shselasky error("missing history argument"); 209321936Shselasky 210321936Shselasky optreset = 1; optind = 1; /* initialize getopt */ 211321936Shselasky opterr = 0; 212321936Shselasky while (not_fcnumber(argv[optind]) && 213321936Shselasky (ch = getopt(argc, argv, ":e:lnrs")) != -1) 214321936Shselasky switch ((char)ch) { 215321936Shselasky case 'e': 216321936Shselasky editor = optarg; 217321936Shselasky break; 218321936Shselasky case 'l': 219321936Shselasky lflg = 1; 220321936Shselasky break; 221321936Shselasky case 'n': 222321936Shselasky nflg = 1; 223321936Shselasky break; 224321936Shselasky case 'r': 225321936Shselasky rflg = 1; 226321936Shselasky break; 227321936Shselasky case 's': 228321936Shselasky sflg = 1; 229321936Shselasky break; 230321936Shselasky case ':': 231321936Shselasky error("option -%c expects argument", optopt); 232321936Shselasky case '?': 233321936Shselasky default: 234321936Shselasky error("unknown option: -%c", optopt); 235321936Shselasky } 236321936Shselasky argc -= optind, argv += optind; 237321936Shselasky 238321936Shselasky savehandler = handler; 239321936Shselasky /* 240321936Shselasky * If executing... 241321936Shselasky */ 242321936Shselasky if (lflg == 0 || editor || sflg) { 243321936Shselasky lflg = 0; /* ignore */ 244321936Shselasky editfile = NULL; 245321936Shselasky /* 246321936Shselasky * Catch interrupts to reset active counter and 247321936Shselasky * cleanup temp files. 248321936Shselasky */ 249321936Shselasky if (setjmp(jmploc.loc)) { 250321936Shselasky active = 0; 251321936Shselasky if (editfile) 252321936Shselasky unlink(editfile); 253321936Shselasky handler = savehandler; 254321936Shselasky longjmp(handler->loc, 1); 255321936Shselasky } 256321936Shselasky handler = &jmploc; 257321936Shselasky if (++active > MAXHISTLOOPS) { 258321936Shselasky active = 0; 259321936Shselasky displayhist = 0; 260321936Shselasky error("called recursively too many times"); 261321936Shselasky } 262321936Shselasky /* 263321936Shselasky * Set editor. 264321936Shselasky */ 265321936Shselasky if (sflg == 0) { 266321936Shselasky if (editor == NULL && 267321936Shselasky (editor = bltinlookup("FCEDIT", 1)) == NULL && 268321936Shselasky (editor = bltinlookup("EDITOR", 1)) == NULL) 269321936Shselasky editor = DEFEDITOR; 270321936Shselasky if (editor[0] == '-' && editor[1] == '\0') { 271321936Shselasky sflg = 1; /* no edit */ 272321936Shselasky editor = NULL; 273321936Shselasky } 274321936Shselasky } 275321936Shselasky } 276321936Shselasky 277321936Shselasky /* 278321936Shselasky * If executing, parse [old=new] now 279321936Shselasky */ 280321936Shselasky if (lflg == 0 && argc > 0 && 281321936Shselasky ((repl = strchr(argv[0], '=')) != NULL)) { 282321936Shselasky pat = argv[0]; 283321936Shselasky *repl++ = '\0'; 284321936Shselasky argc--, argv++; 285321936Shselasky } 286321936Shselasky /* 287321936Shselasky * determine [first] and [last] 288321936Shselasky */ 289321936Shselasky switch (argc) { 290321936Shselasky case 0: 291321936Shselasky firststr = lflg ? "-16" : "-1"; 292321936Shselasky laststr = "-1"; 293321936Shselasky break; 294321936Shselasky case 1: 295321936Shselasky firststr = argv[0]; 296321936Shselasky laststr = lflg ? "-1" : argv[0]; 297321936Shselasky break; 298321936Shselasky case 2: 299321936Shselasky firststr = argv[0]; 300321936Shselasky laststr = argv[1]; 301321936Shselasky break; 302321936Shselasky default: 303321936Shselasky error("too many arguments"); 304321936Shselasky } 305321936Shselasky /* 306321936Shselasky * Turn into event numbers. 307321936Shselasky */ 308321936Shselasky first = str_to_event(firststr, 0); 309321936Shselasky last = str_to_event(laststr, 1); 310321936Shselasky 311321936Shselasky if (rflg) { 312321936Shselasky i = last; 313321936Shselasky last = first; 314321936Shselasky first = i; 315321936Shselasky } 316321936Shselasky /* 317321936Shselasky * XXX - this should not depend on the event numbers 318321936Shselasky * always increasing. Add sequence numbers or offset 319321936Shselasky * to the history element in next (diskbased) release. 320321936Shselasky */ 321321936Shselasky direction = first < last ? H_PREV : H_NEXT; 322321936Shselasky 323321936Shselasky /* 324321936Shselasky * If editing, grab a temp file. 325321936Shselasky */ 326321936Shselasky if (editor) { 327321936Shselasky int fd; 328321936Shselasky INTOFF; /* easier */ 329321936Shselasky sprintf(editfilestr, "%s/_shXXXXXX", _PATH_TMP); 330321936Shselasky if ((fd = mkstemp(editfilestr)) < 0) 331321936Shselasky error("can't create temporary file %s", editfile); 332321936Shselasky editfile = editfilestr; 333321936Shselasky if ((efp = fdopen(fd, "w")) == NULL) { 334321936Shselasky close(fd); 335321936Shselasky error("Out of space"); 336321936Shselasky } 337321936Shselasky } 338321936Shselasky 339321936Shselasky /* 340321936Shselasky * Loop through selected history events. If listing or executing, 341321936Shselasky * do it now. Otherwise, put into temp file and call the editor 342321936Shselasky * after. 343321936Shselasky * 344321936Shselasky * The history interface needs rethinking, as the following 345321936Shselasky * convolutions will demonstrate. 346321936Shselasky */ 347321936Shselasky history(hist, &he, H_FIRST); 348321936Shselasky retval = history(hist, &he, H_NEXT_EVENT, first); 349321936Shselasky for (;retval != -1; retval = history(hist, &he, direction)) { 350321936Shselasky if (lflg) { 351321936Shselasky if (!nflg) 352321936Shselasky out1fmt("%5d ", he.num); 353321936Shselasky out1str(he.str); 354321936Shselasky } else { 355321936Shselasky char *s = pat ? 356321936Shselasky fc_replace(he.str, pat, repl) : (char *)he.str; 357321936Shselasky 358321936Shselasky if (sflg) { 359321936Shselasky if (displayhist) { 360321936Shselasky out2str(s); 361321936Shselasky flushout(out2); 362321936Shselasky } 363321936Shselasky evalstring(s, 0); 364321936Shselasky if (displayhist && hist) { 365321936Shselasky /* 366321936Shselasky * XXX what about recursive and 367321936Shselasky * relative histnums. 368321936Shselasky */ 369321936Shselasky oldhistnum = he.num; 370321936Shselasky history(hist, &he, H_ENTER, s); 371321936Shselasky /* 372321936Shselasky * XXX H_ENTER moves the internal 373321936Shselasky * cursor, set it back to the current 374321936Shselasky * entry. 375321936Shselasky */ 376321936Shselasky retval = history(hist, &he, 377321936Shselasky H_NEXT_EVENT, oldhistnum); 378321936Shselasky } 379321936Shselasky } else 380321936Shselasky fputs(s, efp); 381321936Shselasky } 382321936Shselasky /* 383321936Shselasky * At end? (if we were to lose last, we'd sure be 384321936Shselasky * messed up). 385321936Shselasky */ 386321936Shselasky if (he.num == last) 387321936Shselasky break; 388321936Shselasky } 389321936Shselasky if (editor) { 390321936Shselasky char *editcmd; 391321936Shselasky 392321936Shselasky fclose(efp); 393321936Shselasky editcmd = stalloc(strlen(editor) + strlen(editfile) + 2); 394321936Shselasky sprintf(editcmd, "%s %s", editor, editfile); 395321936Shselasky evalstring(editcmd, 0); /* XXX - should use no JC command */ 396321936Shselasky INTON; 397321936Shselasky readcmdfile(editfile); /* XXX - should read back - quick tst */ 398321936Shselasky unlink(editfile); 399321936Shselasky } 400321936Shselasky 401321936Shselasky if (lflg == 0 && active > 0) 402321936Shselasky --active; 403321936Shselasky if (displayhist) 404321936Shselasky displayhist = 0; 405321936Shselasky handler = savehandler; 406321936Shselasky return 0; 407321936Shselasky} 408321936Shselasky 409321936Shselaskystatic char * 410321936Shselaskyfc_replace(const char *s, char *p, char *r) 411321936Shselasky{ 412321936Shselasky char *dest; 413321936Shselasky int plen = strlen(p); 414321936Shselasky 415321936Shselasky STARTSTACKSTR(dest); 416321936Shselasky while (*s) { 417321936Shselasky if (*s == *p && strncmp(s, p, plen) == 0) { 418321936Shselasky STPUTS(r, dest); 419321936Shselasky s += plen; 420321936Shselasky *p = '\0'; /* so no more matches */ 421321936Shselasky } else 422321936Shselasky STPUTC(*s++, dest); 423321936Shselasky } 424321936Shselasky STPUTC('\0', dest); 425321936Shselasky dest = grabstackstr(dest); 426321936Shselasky 427321936Shselasky return (dest); 428321936Shselasky} 429321936Shselasky 430321936Shselaskystatic int 431321936Shselaskynot_fcnumber(const char *s) 432321936Shselasky{ 433321936Shselasky if (s == NULL) 434321936Shselasky return (0); 435321936Shselasky if (*s == '-') 436321936Shselasky s++; 437321936Shselasky return (!is_number(s)); 438321936Shselasky} 439321936Shselasky 440321936Shselaskystatic int 441321936Shselaskystr_to_event(const char *str, int last) 442321936Shselasky{ 443321936Shselasky HistEvent he; 444321936Shselasky const char *s = str; 445321936Shselasky int relative = 0; 446321936Shselasky int i, retval; 447321936Shselasky 448321936Shselasky retval = history(hist, &he, H_FIRST); 449321936Shselasky switch (*s) { 450321936Shselasky case '-': 451321936Shselasky relative = 1; 452321936Shselasky /*FALLTHROUGH*/ 453321936Shselasky case '+': 454321936Shselasky s++; 455321936Shselasky } 456321936Shselasky if (is_number(s)) { 457321936Shselasky i = atoi(s); 458321936Shselasky if (relative) { 459321936Shselasky while (retval != -1 && i--) { 460321936Shselasky retval = history(hist, &he, H_NEXT); 461321936Shselasky } 462321936Shselasky if (retval == -1) 463321936Shselasky retval = history(hist, &he, H_LAST); 464321936Shselasky } else { 465321936Shselasky retval = history(hist, &he, H_NEXT_EVENT, i); 466321936Shselasky if (retval == -1) { 467321936Shselasky /* 468321936Shselasky * the notion of first and last is 469321936Shselasky * backwards to that of the history package 470321936Shselasky */ 471321936Shselasky retval = history(hist, &he, last ? H_FIRST : H_LAST); 472321936Shselasky } 473321936Shselasky } 474321936Shselasky if (retval == -1) 475321936Shselasky error("history number %s not found (internal error)", 476321936Shselasky str); 477321936Shselasky } else { 478321936Shselasky /* 479321936Shselasky * pattern 480321936Shselasky */ 481321936Shselasky retval = history(hist, &he, H_PREV_STR, str); 482321936Shselasky if (retval == -1) 483321936Shselasky error("history pattern not found: %s", str); 484321936Shselasky } 485321936Shselasky return (he.num); 486321936Shselasky} 487321936Shselasky 488321936Shselaskyint 489321936Shselaskybindcmd(int argc, char **argv) 490321936Shselasky{ 491321936Shselasky 492321936Shselasky if (el == NULL) 493321936Shselasky error("line editing is disabled"); 494321936Shselasky return (el_parse(el, argc, (const char **)argv)); 495321936Shselasky} 496321936Shselasky 497321936Shselasky#else 498321936Shselasky#include "error.h" 499321936Shselasky 500321936Shselaskyint 501321936Shselaskyhistcmd(int argc, char **argv) 502321936Shselasky{ 503321936Shselasky 504321936Shselasky error("not compiled with history support"); 505321936Shselasky /*NOTREACHED*/ 506321936Shselasky return (0); 507321936Shselasky} 508321936Shselasky 509321936Shselaskyint 510321936Shselaskybindcmd(int argc, char **argv) 511321936Shselasky{ 512321936Shselasky 513321936Shselasky error("not compiled with line editing support"); 514321936Shselasky return (0); 515321936Shselasky} 516321936Shselasky#endif 517321936Shselasky