histedit.c revision 100663
1121159Sbmah/*- 2121159Sbmah * Copyright (c) 1993 3121159Sbmah * The Regents of the University of California. All rights reserved. 4121159Sbmah * 5121159Sbmah * This code is derived from software contributed to Berkeley by 6121159Sbmah * Kenneth Almquist. 7121159Sbmah * 8121159Sbmah * Redistribution and use in source and binary forms, with or without 9121159Sbmah * modification, are permitted provided that the following conditions 10121159Sbmah * are met: 11121159Sbmah * 1. Redistributions of source code must retain the above copyright 12121159Sbmah * notice, this list of conditions and the following disclaimer. 13121159Sbmah * 2. Redistributions in binary form must reproduce the above copyright 14121159Sbmah * notice, this list of conditions and the following disclaimer in the 15121159Sbmah * documentation and/or other materials provided with the distribution. 16121159Sbmah * 3. All advertising materials mentioning features or use of this software 17121159Sbmah * must display the following acknowledgement: 18121159Sbmah * This product includes software developed by the University of 19121159Sbmah * California, Berkeley and its contributors. 20121159Sbmah * 4. Neither the name of the University nor the names of its contributors 21121159Sbmah * may be used to endorse or promote products derived from this software 22121159Sbmah * without specific prior written permission. 23121159Sbmah * 24121159Sbmah * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25121159Sbmah * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26121159Sbmah * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27121159Sbmah * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28192923Sbms * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29121159Sbmah * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30121159Sbmah * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31121159Sbmah * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32121159Sbmah * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33121159Sbmah * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34121159Sbmah * SUCH DAMAGE. 35121159Sbmah */ 36121159Sbmah 37121159Sbmah#ifndef lint 38121159Sbmah#if 0 39121159Sbmahstatic char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95"; 40121159Sbmah#endif 41121159Sbmah#endif /* not lint */ 42121159Sbmah#include <sys/cdefs.h> 43121159Sbmah__FBSDID("$FreeBSD: head/bin/sh/histedit.c 100663 2002-07-25 10:47:38Z tjr $"); 44121159Sbmah 45121159Sbmah#include <sys/param.h> 46121159Sbmah#include <limits.h> 47121159Sbmah#include <paths.h> 48121159Sbmah#include <stdio.h> 49121159Sbmah#include <stdlib.h> 50121159Sbmah#include <unistd.h> 51121159Sbmah/* 52121159Sbmah * Editline and history functions (and glue). 53121159Sbmah */ 54121159Sbmah#include "shell.h" 55121159Sbmah#include "parser.h" 56121159Sbmah#include "var.h" 57121159Sbmah#include "options.h" 58121159Sbmah#include "main.h" 59121159Sbmah#include "output.h" 60121159Sbmah#include "mystring.h" 61121159Sbmah#ifndef NO_HISTORY 62121159Sbmah#include "myhistedit.h" 63121159Sbmah#include "error.h" 64167676Sbms#include "eval.h" 65121159Sbmah#include "memalloc.h" 66167676Sbms 67167676Sbms#define MAXHISTLOOPS 4 /* max recursions through fc */ 68167676Sbms#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */ 69167676Sbms 70167676SbmsHistory *hist; /* history cookie */ 71167676SbmsEditLine *el; /* editline cookie */ 72121159Sbmahint displayhist; 73121159Sbmahstatic FILE *el_in, *el_out, *el_err; 74121159Sbmah 75121159SbmahSTATIC char *fc_replace(const char *, char *, char *); 76121159Sbmah 77121159Sbmah/* 78121159Sbmah * Set history and editing status. Called whenever the status may 79121159Sbmah * have changed (figures out what to do). 80121159Sbmah */ 81121159Sbmahvoid 82121159Sbmahhistedit(void) 83121159Sbmah{ 84121159Sbmah 85121159Sbmah#define editing (Eflag || Vflag) 86121159Sbmah 87121159Sbmah if (iflag) { 88121159Sbmah if (!hist) { 89121159Sbmah /* 90121159Sbmah * turn history on 91121159Sbmah */ 92121159Sbmah INTOFF; 93121159Sbmah hist = history_init(); 94121159Sbmah INTON; 95121159Sbmah 96121159Sbmah if (hist != NULL) 97121159Sbmah sethistsize(histsizeval()); 98121159Sbmah else 99121159Sbmah out2str("sh: can't initialize history\n"); 100121159Sbmah } 101121159Sbmah if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ 102121159Sbmah /* 103121159Sbmah * turn editing on 104121159Sbmah */ 105121159Sbmah INTOFF; 106121159Sbmah if (el_in == NULL) 107121159Sbmah el_in = fdopen(0, "r"); 108121159Sbmah if (el_err == NULL) 109121159Sbmah el_err = fdopen(1, "w"); 110131864Sru if (el_out == NULL) 111131864Sru el_out = fdopen(2, "w"); 112131864Sru if (el_in == NULL || el_err == NULL || el_out == NULL) 113131864Sru goto bad; 114131864Sru el = el_init(arg0, el_in, el_out, el_err); 115121159Sbmah if (el != NULL) { 116131864Sru if (hist) 117131864Sru el_set(el, EL_HIST, history, hist); 118131864Sru el_set(el, EL_PROMPT, getprompt); 119121159Sbmah } else { 120131864Srubad: 121131864Sru out2str("sh: can't initialize editing\n"); 122131864Sru } 123121159Sbmah INTON; 124121159Sbmah } else if (!editing && el) { 125121159Sbmah INTOFF; 126121159Sbmah el_end(el); 127121159Sbmah el = NULL; 128121159Sbmah INTON; 129121159Sbmah } 130121159Sbmah if (el) { 131121159Sbmah if (Vflag) 132121159Sbmah el_set(el, EL_EDITOR, "vi"); 133121159Sbmah else if (Eflag) 134121159Sbmah el_set(el, EL_EDITOR, "emacs"); 135121159Sbmah el_source(el, NULL); 136121159Sbmah } 137121159Sbmah } else { 138121159Sbmah INTOFF; 139121159Sbmah if (el) { /* no editing if not interactive */ 140121159Sbmah el_end(el); 141121159Sbmah el = NULL; 142121159Sbmah } 143121159Sbmah if (hist) { 144121159Sbmah history_end(hist); 145121159Sbmah hist = NULL; 146121159Sbmah } 147121159Sbmah INTON; 148121159Sbmah } 149121159Sbmah} 150121159Sbmah 151121159Sbmah 152121159Sbmahvoid 153121159Sbmahsethistsize(hs) 154121159Sbmah const char *hs; 155121159Sbmah{ 156121159Sbmah int histsize; 157121159Sbmah HistEvent he; 158121159Sbmah 159121159Sbmah if (hist != NULL) { 160121159Sbmah if (hs == NULL || *hs == '\0' || 161121159Sbmah (histsize = atoi(hs)) < 0) 162121159Sbmah histsize = 100; 163121159Sbmah history(hist, &he, H_EVENT, histsize); 164166973Sbms } 165121159Sbmah} 166121159Sbmah 167121159Sbmah/* 168121159Sbmah * This command is provided since POSIX decided to standardize 169121159Sbmah * the Korn shell fc command. Oh well... 170121159Sbmah */ 171131864Sruint 172121159Sbmahhistcmd(int argc, char **argv) 173121159Sbmah{ 174131864Sru int ch; 175121159Sbmah char *editor = NULL; 176131864Sru HistEvent he; 177131864Sru int lflg = 0, nflg = 0, rflg = 0, sflg = 0; 178131864Sru int i, retval; 179121159Sbmah char *firststr, *laststr; 180166973Sbms int first, last, direction; 181166973Sbms char *pat = NULL, *repl; /* ksh "fc old=new" crap */ 182166973Sbms static int active = 0; 183166973Sbms struct jmploc jmploc; 184166973Sbms struct jmploc *volatile savehandler; 185166973Sbms char editfile[PATH_MAX]; 186166973Sbms FILE *efp; 187166973Sbms int oldhistnum; 188166973Sbms#ifdef __GNUC__ 189166973Sbms /* Avoid longjmp clobbering */ 190166973Sbms (void) &editor; 191131864Sru (void) &lflg; 192121159Sbmah (void) &nflg; 193121159Sbmah (void) &rflg; 194121159Sbmah (void) &sflg; 195166973Sbms (void) &firststr; 196121159Sbmah (void) &laststr; 197131864Sru (void) &pat; 198166973Sbms (void) &repl; 199166973Sbms (void) &efp; 200166973Sbms (void) &argc; 201166973Sbms (void) &argv; 202166973Sbms#endif 203166973Sbms 204166973Sbms if (hist == NULL) 205166973Sbms error("history not active"); 206131472Sru 207131864Sru if (argc == 1) 208121159Sbmah error("missing history argument"); 209121159Sbmah 210131864Sru optreset = 1; optind = 1; /* initialize getopt */ 211121159Sbmah opterr = 0; 212121159Sbmah while (not_fcnumber(argv[optind]) && 213121159Sbmah (ch = getopt(argc, argv, ":e:lnrs")) != -1) 214121159Sbmah switch ((char)ch) { 215121159Sbmah case 'e': 216121159Sbmah editor = optarg; 217121159Sbmah break; 218121159Sbmah case 'l': 219121159Sbmah lflg = 1; 220121159Sbmah break; 221121159Sbmah case 'n': 222121159Sbmah nflg = 1; 223121159Sbmah break; 224121159Sbmah case 'r': 225131864Sru rflg = 1; 226121159Sbmah break; 227121159Sbmah case 's': 228131864Sru sflg = 1; 229121159Sbmah break; 230131864Sru case ':': 231131864Sru error("option -%c expects argument", optopt); 232131864Sru case '?': 233121159Sbmah default: 234131864Sru error("unknown option: -%c", optopt); 235121159Sbmah } 236121159Sbmah argc -= optind, argv += optind; 237121159Sbmah 238121159Sbmah /* 239121159Sbmah * If executing... 240121159Sbmah */ 241121159Sbmah if (lflg == 0 || editor || sflg) { 242121159Sbmah lflg = 0; /* ignore */ 243121159Sbmah editfile[0] = '\0'; 244121159Sbmah /* 245121159Sbmah * Catch interrupts to reset active counter and 246121159Sbmah * cleanup temp files. 247121159Sbmah */ 248121159Sbmah if (setjmp(jmploc.loc)) { 249121159Sbmah active = 0; 250121159Sbmah if (*editfile) 251121159Sbmah unlink(editfile); 252121159Sbmah handler = savehandler; 253121159Sbmah longjmp(handler->loc, 1); 254121159Sbmah } 255121159Sbmah savehandler = handler; 256131864Sru handler = &jmploc; 257121159Sbmah if (++active > MAXHISTLOOPS) { 258131864Sru active = 0; 259121159Sbmah displayhist = 0; 260131864Sru error("called recursively too many times"); 261131864Sru } 262131864Sru /* 263131864Sru * Set editor. 264131864Sru */ 265121159Sbmah if (sflg == 0) { 266121159Sbmah if (editor == NULL && 267131864Sru (editor = bltinlookup("FCEDIT", 1)) == NULL && 268121159Sbmah (editor = bltinlookup("EDITOR", 1)) == NULL) 269131864Sru editor = DEFEDITOR; 270121159Sbmah if (editor[0] == '-' && editor[1] == '\0') { 271121159Sbmah sflg = 1; /* no edit */ 272131864Sru editor = NULL; 273131864Sru } 274131864Sru } 275131864Sru } 276131864Sru 277121159Sbmah /* 278121159Sbmah * If executing, parse [old=new] now 279131864Sru */ 280121159Sbmah if (lflg == 0 && argc > 0 && 281131864Sru ((repl = strchr(argv[0], '=')) != NULL)) { 282121159Sbmah pat = argv[0]; 283121159Sbmah *repl++ = '\0'; 284121159Sbmah argc--, argv++; 285131864Sru } 286121159Sbmah /* 287131864Sru * determine [first] and [last] 288121159Sbmah */ 289131864Sru switch (argc) { 290121159Sbmah case 0: 291131864Sru firststr = lflg ? "-16" : "-1"; 292121159Sbmah laststr = "-1"; 293121159Sbmah break; 294121159Sbmah case 1: 295121159Sbmah firststr = argv[0]; 296121159Sbmah laststr = lflg ? "-1" : argv[0]; 297131864Sru break; 298121159Sbmah case 2: 299131864Sru firststr = argv[0]; 300121159Sbmah laststr = argv[1]; 301121159Sbmah break; 302121159Sbmah default: 303121159Sbmah error("too many args"); 304121159Sbmah } 305121159Sbmah /* 306131864Sru * Turn into event numbers. 307121159Sbmah */ 308121159Sbmah first = str_to_event(firststr, 0); 309121159Sbmah last = str_to_event(laststr, 1); 310121159Sbmah 311121159Sbmah if (rflg) { 312121159Sbmah i = last; 313121159Sbmah last = first; 314121159Sbmah first = i; 315121159Sbmah } 316121159Sbmah /* 317121159Sbmah * XXX - this should not depend on the event numbers 318121159Sbmah * always increasing. Add sequence numbers or offset 319121159Sbmah * to the history element in next (diskbased) release. 320121159Sbmah */ 321121159Sbmah direction = first < last ? H_PREV : H_NEXT; 322121159Sbmah 323121159Sbmah /* 324121159Sbmah * If editing, grab a temp file. 325121159Sbmah */ 326121159Sbmah if (editor) { 327121159Sbmah int fd; 328121159Sbmah INTOFF; /* easier */ 329261876Sbrueffer sprintf(editfile, "%s/_shXXXXXX", _PATH_TMP); 330121159Sbmah if ((fd = mkstemp(editfile)) < 0) 331121159Sbmah error("can't create temporary file %s", editfile); 332121159Sbmah if ((efp = fdopen(fd, "w")) == NULL) { 333121159Sbmah close(fd); 334131864Sru error("can't allocate stdio buffer for temp"); 335121159Sbmah } 336131864Sru } 337121159Sbmah 338121159Sbmah /* 339121159Sbmah * Loop through selected history events. If listing or executing, 340131864Sru * do it now. Otherwise, put into temp file and call the editor 341121159Sbmah * after. 342121159Sbmah * 343121159Sbmah * The history interface needs rethinking, as the following 344131864Sru * convolutions will demonstrate. 345121159Sbmah */ 346121159Sbmah history(hist, &he, H_FIRST); 347121159Sbmah retval = history(hist, &he, H_NEXT_EVENT, first); 348121159Sbmah for (;retval != -1; retval = history(hist, &he, direction)) { 349121159Sbmah if (lflg) { 350121159Sbmah if (!nflg) 351121159Sbmah out1fmt("%5d ", he.num); 352131864Sru out1str(he.str); 353121159Sbmah } else { 354121159Sbmah char *s = pat ? 355121159Sbmah fc_replace(he.str, pat, repl) : (char *)he.str; 356121159Sbmah 357121159Sbmah if (sflg) { 358121159Sbmah if (displayhist) { 359121159Sbmah out2str(s); 360121159Sbmah } 361121159Sbmah evalstring(s); 362121159Sbmah if (displayhist && hist) { 363121159Sbmah /* 364121159Sbmah * XXX what about recursive and 365121159Sbmah * relative histnums. 366121159Sbmah */ 367121159Sbmah oldhistnum = he.num; 368261876Sbrueffer history(hist, &he, H_ENTER, s); 369121159Sbmah /* 370121159Sbmah * XXX H_ENTER moves the internal 371121159Sbmah * cursor, set it back to the current 372121159Sbmah * entry. 373121159Sbmah */ 374121159Sbmah retval = history(hist, &he, 375121159Sbmah H_NEXT_EVENT, oldhistnum); 376121159Sbmah } 377121159Sbmah } else 378121159Sbmah fputs(s, efp); 379121159Sbmah } 380121159Sbmah /* 381121159Sbmah * At end? (if we were to loose last, we'd sure be 382121159Sbmah * messed up). 383121159Sbmah */ 384121159Sbmah if (he.num == last) 385121159Sbmah break; 386121159Sbmah } 387121159Sbmah if (editor) { 388121159Sbmah char *editcmd; 389121159Sbmah 390121159Sbmah fclose(efp); 391121159Sbmah editcmd = stalloc(strlen(editor) + strlen(editfile) + 2); 392121159Sbmah sprintf(editcmd, "%s %s", editor, editfile); 393121159Sbmah evalstring(editcmd); /* XXX - should use no JC command */ 394121159Sbmah INTON; 395121159Sbmah readcmdfile(editfile); /* XXX - should read back - quick tst */ 396121159Sbmah unlink(editfile); 397121159Sbmah } 398121159Sbmah 399121159Sbmah if (lflg == 0 && active > 0) 400121159Sbmah --active; 401121159Sbmah if (displayhist) 402121159Sbmah displayhist = 0; 403121159Sbmah return 0; 404121159Sbmah} 405121159Sbmah 406121159SbmahSTATIC char * 407121159Sbmahfc_replace(const char *s, char *p, char *r) 408121159Sbmah{ 409121159Sbmah char *dest; 410121159Sbmah int plen = strlen(p); 411121159Sbmah 412121159Sbmah STARTSTACKSTR(dest); 413121159Sbmah while (*s) { 414121159Sbmah if (*s == *p && strncmp(s, p, plen) == 0) { 415131472Sru while (*r) 416121159Sbmah STPUTC(*r++, dest); 417121159Sbmah s += plen; 418121159Sbmah *p = '\0'; /* so no more matches */ 419121159Sbmah } else 420121159Sbmah STPUTC(*s++, dest); 421121159Sbmah } 422121159Sbmah STACKSTRNUL(dest); 423121159Sbmah dest = grabstackstr(dest); 424121159Sbmah 425121159Sbmah return (dest); 426121159Sbmah} 427121159Sbmah 428121159Sbmahint 429121159Sbmahnot_fcnumber(char *s) 430121159Sbmah{ 431131864Sru if (s == NULL) 432121159Sbmah return (0); 433121159Sbmah if (*s == '-') 434121159Sbmah s++; 435121159Sbmah return (!is_number(s)); 436121159Sbmah} 437121159Sbmah 438121159Sbmahint 439121159Sbmahstr_to_event(char *str, int last) 440121159Sbmah{ 441121159Sbmah HistEvent he; 442121159Sbmah char *s = str; 443131864Sru int relative = 0; 444131864Sru int i, retval; 445131864Sru 446131864Sru retval = history(hist, &he, H_FIRST); 447121159Sbmah switch (*s) { 448121159Sbmah case '-': 449131864Sru relative = 1; 450131864Sru /*FALLTHROUGH*/ 451131864Sru case '+': 452121159Sbmah s++; 453121159Sbmah } 454121159Sbmah if (is_number(s)) { 455121159Sbmah i = atoi(s); 456131864Sru if (relative) { 457131864Sru while (retval != -1 && i--) { 458131864Sru retval = history(hist, &he, H_NEXT); 459131864Sru } 460131864Sru if (retval == -1) 461131864Sru retval = history(hist, &he, H_LAST); 462131864Sru } else { 463121159Sbmah retval = history(hist, &he, H_NEXT_EVENT, i); 464131864Sru if (retval == -1) { 465121159Sbmah /* 466121159Sbmah * the notion of first and last is 467121159Sbmah * backwards to that of the history package 468121159Sbmah */ 469121159Sbmah retval = history(hist, &he, last ? H_FIRST : H_LAST); 470121159Sbmah } 471121159Sbmah } 472131864Sru if (retval == -1) 473121159Sbmah error("history number %s not found (internal error)", 474131864Sru str); 475131864Sru } else { 476131864Sru /* 477121159Sbmah * pattern 478131864Sru */ 479121159Sbmah retval = history(hist, &he, H_PREV_STR, str); 480131864Sru if (retval == -1) 481121159Sbmah error("history pattern not found: %s", str); 482131864Sru } 483121159Sbmah return (he.num); 484131864Sru} 485131864Sru 486131864Sruint 487131864Srubindcmd(int argc, char **argv) 488131864Sru{ 489121159Sbmah 490121159Sbmah if (el == NULL) 491121159Sbmah error("line editing is disabled"); 492121159Sbmah return (el_parse(el, argc, argv)); 493121159Sbmah} 494121159Sbmah 495121159Sbmah#else 496121159Sbmah#include "error.h" 497121159Sbmah 498121159Sbmahint 499121159Sbmahhistcmd(int argc, char **argv) 500121159Sbmah{ 501121159Sbmah 502121159Sbmah error("not compiled with history support"); 503131864Sru /*NOTREACHED*/ 504131864Sru return (0); 505131864Sru} 506121159Sbmah 507121159Sbmahint 508121159Sbmahbindcmd(int argc, char **argv) 509131864Sru{ 510121159Sbmah 511121159Sbmah error("not compiled with line editing support"); 512121159Sbmah return (0); 513121159Sbmah} 514121159Sbmah#endif 515121159Sbmah