sh.hist.c revision 167465
1153761Swollman/* $Header: /p/tcsh/cvsroot/tcsh/sh.hist.c,v 3.40 2007/03/01 17:14:51 christos Exp $ */
2223629Sedwin/*
3192886Sedwin * sh.hist.c: Shell history expansions and substitutions
4192886Sedwin */
564499Swollman/*-
62742Swollman * Copyright (c) 1980, 1991 The Regents of the University of California.
72742Swollman * All rights reserved.
82742Swollman *
92742Swollman * Redistribution and use in source and binary forms, with or without
10158421Swollman * modification, are permitted provided that the following conditions
112742Swollman * are met:
12158421Swollman * 1. Redistributions of source code must retain the above copyright
13158421Swollman *    notice, this list of conditions and the following disclaimer.
142742Swollman * 2. Redistributions in binary form must reproduce the above copyright
1586222Swollman *    notice, this list of conditions and the following disclaimer in the
1620094Swollman *    documentation and/or other materials provided with the distribution.
1720094Swollman * 3. Neither the name of the University nor the names of its contributors
1820094Swollman *    may be used to endorse or promote products derived from this software
1920094Swollman *    without specific prior written permission.
2020094Swollman *
21158421Swollman * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22158421Swollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2320094Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2443543Swollman * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
252742Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2643543Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2743543Swollman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2843543Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2943543Swollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30121098Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31121098Swollman * SUCH DAMAGE.
32121098Swollman */
33121098Swollman#include "sh.h"
3443543Swollman
3543543SwollmanRCSID("$tcsh: sh.hist.c,v 3.40 2007/03/01 17:14:51 christos Exp $")
3643543Swollman
3743543Swollman#include "tc.h"
3843543Swollman
3943543Swollmanextern int histvalid;
402742Swollmanextern struct Strbuf histline;
412742SwollmanChar HistLit = 0;
4219878Swollman
43114173Swollmanstatic	int	heq	(const struct wordent *, const struct wordent *);
44114173Swollmanstatic	void	hfree	(struct Hist *);
45114173Swollmanstatic	void	dohist1	(struct Hist *, int *, int);
46114173Swollmanstatic	void	phist	(struct Hist *, int);
47114173Swollman
48114173Swollman#define HIST_ONLY	0x01
49114173Swollman#define HIST_SAVE	0x02
50114173Swollman#define HIST_LOAD	0x04
51114173Swollman#define HIST_REV	0x08
52114173Swollman#define HIST_CLEAR	0x10
53114173Swollman#define HIST_MERGE	0x20
54114173Swollman#define HIST_TIME	0x40
55114173Swollman
56114173Swollman/*
572742Swollman * C shell
582742Swollman */
5958787Sru
602742Swollmanvoid
61149514Swollmansavehist(struct wordent *sp, int mflg)
6214343Swollman{
639908Swollman    struct Hist *hp, *np;
649908Swollman    int histlen = 0;
659908Swollman    Char   *cp;
669908Swollman
679908Swollman    /* throw away null lines */
689908Swollman    if (sp && sp->next->word[0] == '\n')
699908Swollman	return;
709908Swollman    cp = varval(STRhistory);
719908Swollman    while (*cp) {
729908Swollman	if (!Isdigit(*cp)) {
7314343Swollman	    histlen = 0;
7414343Swollman	    break;
759908Swollman	}
769908Swollman	histlen = histlen * 10 + *cp++ - '0';
779908Swollman    }
789908Swollman    if (sp)
799908Swollman	(void) enthist(++eventno, sp, 1, mflg);
809908Swollman    for (hp = &Histlist; (np = hp->Hnext) != NULL;)
819908Swollman	if (eventno - np->Href >= histlen || histlen == 0)
8219878Swollman	    hp->Hnext = np->Hnext, hfree(np);
832742Swollman	else
842742Swollman	    hp = np;
8543014Swollman}
862742Swollman
87149514Swollmanstatic int
882742Swollmanheq(const struct wordent *a0, const struct wordent *b0)
892742Swollman{
902742Swollman    const struct wordent *a = a0->next, *b = b0->next;
912742Swollman
922742Swollman    for (;;) {
932742Swollman	if (Strcmp(a->word, b->word) != 0)
942742Swollman	    return 0;
9521217Swollman	a = a->next;
962742Swollman	b = b->next;
972742Swollman	if (a == a0)
982742Swollman	    return (b == b0) ? 1 : 0;
992742Swollman	if (b == b0)
1002742Swollman	    return 0;
1012742Swollman    }
1022742Swollman}
10321217Swollman
1042742Swollman
1052742Swollmanstruct Hist *
1062742Swollmanenthist(int event, struct wordent *lp, int docopy, int mflg)
1072742Swollman{
108149514Swollman    struct Hist *p = NULL, *pp = &Histlist;
1092742Swollman    int n, r;
1102742Swollman    struct Hist *np;
1112742Swollman    const Char *dp;
1122742Swollman
11358787Sru    if ((dp = varval(STRhistdup)) != STRNULL) {
11458787Sru	if (eq(dp, STRerase)) {
1152742Swollman	    /* masaoki@akebono.tky.hp.com (Kobayashi Masaoki) */
1162742Swollman	    struct Hist *px;
11758787Sru	    for (p = pp; (px = p, p = p->Hnext) != NULL;)
1182742Swollman		if (heq(lp, &(p->Hlex))){
11958787Sru		    px->Hnext = p->Hnext;
12058787Sru		    if (Htime != 0 && p->Htime > Htime)
12158787Sru			Htime = p->Htime;
1222742Swollman		    n = p->Href;
12358787Sru		    hfree(p);
12458787Sru		    for (p = px->Hnext; p != NULL; p = p->Hnext)
1252742Swollman			p->Href = n--;
1262742Swollman		    break;
1272742Swollman		}
1282742Swollman	}
12958787Sru	else if (eq(dp, STRall)) {
1302742Swollman	    for (p = pp; (p = p->Hnext) != NULL;)
1312742Swollman		if (heq(lp, &(p->Hlex))) {
13258787Sru		    eventno--;
13358787Sru		    break;
1342742Swollman		}
135136638Swollman	}
13617200Swollman	else if (eq(dp, STRprev)) {
13743543Swollman	    if (pp->Hnext && heq(lp, &(pp->Hnext->Hlex))) {
13858787Sru		p = pp->Hnext;
13917200Swollman		eventno--;
14017200Swollman	    }
14117200Swollman	}
14217200Swollman    }
143121098Swollman
144121098Swollman    np = p ? p : xmalloc(sizeof(*np));
145121098Swollman
146121098Swollman    /* Pick up timestamp set by lex() in Htime if reading saved history */
147136638Swollman    if (Htime != 0) {
148121098Swollman	np->Htime = Htime;
149221092Sedwin	Htime = 0;
150121098Swollman    }
15117200Swollman    else
152121098Swollman	(void) time(&(np->Htime));
153121098Swollman
154121098Swollman    if (p == np)
155121098Swollman	return np;
156121098Swollman
157121098Swollman    np->Hnum = np->Href = event;
158121098Swollman    if (docopy) {
159121098Swollman	copylex(&np->Hlex, lp);
160121098Swollman	if (histvalid)
161121098Swollman	    np->histline = Strsave(histline.s);
16219878Swollman	else
16319878Swollman	    np->histline = NULL;
16419878Swollman    }
16519878Swollman    else {
16619878Swollman	np->Hlex.next = lp->next;
16719878Swollman	lp->next->prev = &np->Hlex;
16819878Swollman	np->Hlex.prev = lp->prev;
1692742Swollman	lp->prev->next = &np->Hlex;
1702742Swollman	np->histline = NULL;
1712742Swollman    }
1722742Swollman    if (mflg)
1732742Swollman      {
174149514Swollman        while ((p = pp->Hnext) && (p->Htime > np->Htime))
1752742Swollman	  pp = p;
1762742Swollman	while (p && p->Htime == np->Htime)
1772742Swollman	  {
1782742Swollman	    if (heq(&p->Hlex, &np->Hlex))
179149514Swollman	      {
18043014Swollman	        eventno--;
18143014Swollman		hfree(np);
18243014Swollman	        return (p);
18343014Swollman	      }
18443014Swollman	    pp = p;
18558787Sru	    p = p->Hnext;
18658787Sru	  }
18758787Sru	for (p = Histlist.Hnext; p != pp->Hnext; p = p->Hnext)
18858787Sru	  {
1892742Swollman	    n = p->Hnum; r = p->Href;
19075267Swollman	    p->Hnum = np->Hnum; p->Href = np->Href;
19167578Swollman	    np->Hnum = n; np->Href = r;
19267578Swollman	  }
19367578Swollman      }
19467578Swollman    np->Hnext = pp->Hnext;
19567578Swollman    pp->Hnext = np;
19675267Swollman    return (np);
19767578Swollman}
19858787Sru
19967578Swollmanstatic void
20067578Swollmanhfree(struct Hist *hp)
20167578Swollman{
20267578Swollman
20367578Swollman    freelex(&hp->Hlex);
20467578Swollman    if (hp->histline)
20567578Swollman	xfree(hp->histline);
20667578Swollman    xfree(hp);
20767578Swollman}
20867578Swollman
20967578Swollman
210149514Swollman/*ARGSUSED*/
21158787Sruvoid
212149514Swollmandohist(Char **vp, struct command *c)
21358787Sru{
21458787Sru    int     n, hflg = 0;
21558787Sru
21693799Swollman    USE(c);
21758787Sru    if (getn(varval(STRhistory)) == 0)
218149514Swollman	return;
21943014Swollman    while (*++vp && **vp == '-') {
22043014Swollman	Char   *vp2 = *vp;
22143014Swollman
22243014Swollman	while (*++vp2)
22343014Swollman	    switch (*vp2) {
22443014Swollman	    case 'c':
2252742Swollman		hflg |= HIST_CLEAR;
226158421Swollman		break;
2272742Swollman	    case 'h':
228158421Swollman		hflg |= HIST_ONLY;
2292742Swollman		break;
230158421Swollman	    case 'r':
231158421Swollman		hflg |= HIST_REV;
23230711Swollman		break;
2332742Swollman	    case 'S':
2342742Swollman		hflg |= HIST_SAVE;
2352742Swollman		break;
2362742Swollman	    case 'L':
23730711Swollman		hflg |= HIST_LOAD;
2382742Swollman		break;
23917200Swollman	    case 'M':
24017200Swollman	    	hflg |= HIST_MERGE;
24117200Swollman		break;
2422742Swollman	    case 'T':
243158421Swollman	    	hflg |= HIST_TIME;
2442742Swollman		break;
24530711Swollman	    default:
2462742Swollman		stderror(ERR_HISTUS, "chrSLMT");
24758787Sru		break;
24858787Sru	    }
249158421Swollman    }
25058787Sru
25117200Swollman    if (hflg & HIST_CLEAR) {
2522742Swollman	struct Hist *np, *hp;
25317200Swollman	for (hp = &Histlist; (np = hp->Hnext) != NULL;)
254158421Swollman	    hp->Hnext = np->Hnext, hfree(np);
255158421Swollman    }
25686222Swollman
25786222Swollman    if (hflg & (HIST_LOAD | HIST_MERGE))
25886222Swollman	loadhist(*vp, (hflg & HIST_MERGE) ? 1 : 0);
25986222Swollman    else if (hflg & HIST_SAVE)
26086222Swollman	rechist(*vp, 1);
26186222Swollman    else {
2622742Swollman	if (*vp)
263149514Swollman	    n = getn(*vp);
264169811Swollman	else {
265149514Swollman	    n = getn(varval(STRhistory));
266169811Swollman	}
267149514Swollman	dohist1(Histlist.Hnext, &n, hflg);
268169811Swollman    }
269149514Swollman}
270149514Swollman
271169811Swollmanstatic void
272149514Swollmandohist1(struct Hist *hp, int *np, int hflg)
273149514Swollman{
274149514Swollman    int    print = (*np) > 0;
275169811Swollman
276149514Swollman    for (; hp != 0; hp = hp->Hnext) {
277149514Swollman	if (setintr) {
278149514Swollman	    int old_pintr_disabled;
279149514Swollman
280149514Swollman	    pintr_push_enable(&old_pintr_disabled);
281149514Swollman	    cleanup_until(&old_pintr_disabled);
282169811Swollman	}
283169811Swollman	(*np)--;
284169811Swollman	if ((hflg & HIST_REV) == 0) {
285149514Swollman	    dohist1(hp->Hnext, np, hflg);
286169811Swollman	    if (print)
287169811Swollman		phist(hp, hflg);
288169811Swollman	    return;
289169811Swollman	}
290149514Swollman	if (*np >= 0)
291149514Swollman	    phist(hp, hflg);
29258787Sru    }
29358787Sru}
29458787Sru
29558787Srustatic void
29658787Sruphist(struct Hist *hp, int hflg)
29758787Sru{
29858787Sru    if (hflg & HIST_ONLY) {
29958787Sru	int old_output_raw;
30058787Sru
30158787Sru       /*
30258787Sru        * Control characters have to be written as is (output_raw).
30358787Sru        * This way one can preserve special characters (like tab) in
30458787Sru        * the history file.
30558787Sru        * From: mveksler@vnet.ibm.com (Veksler Michael)
30617200Swollman        */
30717200Swollman	old_output_raw = output_raw;
30817200Swollman        output_raw = 1;
30917200Swollman	cleanup_push(&old_output_raw, output_raw_restore);
31017200Swollman	if (hflg & HIST_TIME)
31117200Swollman	    /*
31217200Swollman	     * Make file entry with history time in format:
31317200Swollman	     * "+NNNNNNNNNN" (10 digits, left padded with ascii '0')
3142742Swollman	     */
31543014Swollman
3162742Swollman	    xprintf("#+%010lu\n", (unsigned long)hp->Htime);
3172742Swollman
31843014Swollman	if (HistLit && hp->histline)
3192742Swollman	    xprintf("%S\n", hp->histline);
3202742Swollman	else
32143014Swollman	    prlex(&hp->Hlex);
3222742Swollman        cleanup_until(&old_output_raw);
3232742Swollman    }
32443014Swollman    else {
3252742Swollman	Char   *cp = str2short("%h\t%T\t%R\n");
3262742Swollman	Char *p;
32743014Swollman	struct varent *vp = adrof(STRhistory);
3282742Swollman
32943014Swollman	if (vp && vp->vec != NULL && vp->vec[0] && vp->vec[1])
3302742Swollman	    cp = vp->vec[1];
33143014Swollman
3322742Swollman	p = tprintf(FMT_HISTORY, cp, NULL, hp->Htime, hp);
3332742Swollman	cleanup_push(p, xfree);
33443014Swollman	for (cp = p; *cp;)
3352742Swollman	    xputwchar(*cp++);
33658787Sru	cleanup_until(p);
33743014Swollman    }
3382742Swollman}
3392742Swollman
34043014Swollman
3412742Swollmanchar *
34243014Swollmanfmthist(int fmt, ptr_t ptr)
3432742Swollman{
34443014Swollman    struct Hist *hp = ptr;
3452742Swollman    char *buf;
34643014Swollman
3472742Swollman    switch (fmt) {
34843014Swollman    case 'h':
3492742Swollman	return xasprintf("%6d", hp->Hnum);
35043014Swollman    case 'R':
3512742Swollman	if (HistLit && hp->histline)
35243014Swollman	    return xasprintf("%S", hp->histline);
3532742Swollman	else {
35443014Swollman	    Char *istr, *ip;
35543014Swollman	    char *p;
35643014Swollman
35743014Swollman	    istr = sprlex(&hp->Hlex);
35843014Swollman	    buf = xmalloc(Strlen(istr) * MB_LEN_MAX + 1);
35919878Swollman
3602742Swollman	    for (p = buf, ip = istr; *ip != '\0'; ip++)
36143014Swollman		p += one_wctomb(p, CHAR & *ip);
36219878Swollman
36343014Swollman	    *p = '\0';
3642742Swollman	    xfree(istr);
36543014Swollman	    return buf;
36643014Swollman	}
36743014Swollman    default:
36843014Swollman	buf = xmalloc(1);
36943014Swollman	buf[0] = '\0';
37043014Swollman	return buf;
37143014Swollman    }
3722742Swollman}
37319878Swollman
3742742Swollmanvoid
3752742Swollmanrechist(Char *fname, int ref)
37643014Swollman{
3772742Swollman    Char    *snum;
37843014Swollman    int     fp, ftmp, oldidfds;
37943014Swollman    struct varent *shist;
3802742Swollman    static Char   *dumphist[] = {STRhistory, STRmhT, 0, 0};
38143014Swollman
38243014Swollman    if (fname == NULL && !ref)
38343014Swollman	return;
38443014Swollman    /*
38543014Swollman     * If $savehist is just set, we use the value of $history
3862742Swollman     * else we use the value in $savehist
38743014Swollman     */
38843014Swollman    if (((snum = varval(STRsavehist)) == STRNULL) &&
38943014Swollman	((snum = varval(STRhistory)) == STRNULL))
39043014Swollman	snum = STRmaxint;
3912742Swollman
39243014Swollman
3932742Swollman    if (fname == NULL) {
39443014Swollman	if ((fname = varval(STRhistfile)) == STRNULL)
39543014Swollman	    fname = Strspl(varval(STRhome), &STRtildothist[1]);
39643014Swollman	else
39743014Swollman	    fname = Strsave(fname);
3982742Swollman    }
39943014Swollman    else
40043014Swollman	fname = globone(fname, G_ERROR);
40143014Swollman    cleanup_push(fname, xfree);
40243014Swollman
4032742Swollman    /*
40443014Swollman     * The 'savehist merge' feature is intended for an environment
40517200Swollman     * with numerous shells being in simultaneous use. Imagine
40643014Swollman     * any kind of window system. All these shells 'share' the same
40743014Swollman     * ~/.history file for recording their command line history.
40843014Swollman     * Currently the automatic merge can only succeed when the shells
4092742Swollman     * nicely quit one after another.
4102742Swollman     *
41143014Swollman     * Users that like to nuke their environment require here an atomic
41243014Swollman     * 	loadhist-creat-dohist(dumphist)-close
41343014Swollman     * sequence.
41443014Swollman     *
4159908Swollman     * jw.
4169908Swollman     */
41743014Swollman    /*
41843014Swollman     * We need the didfds stuff before loadhist otherwise
41943014Swollman     * exec in a script will fail to print if merge is set.
4209908Swollman     * From: mveksler@iil.intel.com (Veksler Michael)
42143014Swollman     */
42217200Swollman    oldidfds = didfds;
4232742Swollman    didfds = 0;
4242742Swollman    if ((shist = adrof(STRsavehist)) != NULL && shist->vec != NULL)
425158421Swollman	if (shist->vec[1] && eq(shist->vec[1], STRmerge))
42617200Swollman	    loadhist(fname, 1);
42717200Swollman    fp = xcreat(short2str(fname), 0600);
4289908Swollman    if (fp == -1) {
42917200Swollman	didfds = oldidfds;
430163302Sru	cleanup_until(fname);
431163302Sru	return;
432163302Sru    }
43386222Swollman    ftmp = SHOUT;
43486222Swollman    SHOUT = fp;
43543014Swollman    dumphist[2] = snum;
43619878Swollman    dohist(dumphist, NULL);
43717200Swollman    xclose(fp);
43817200Swollman    SHOUT = ftmp;
4392742Swollman    didfds = oldidfds;
44017200Swollman    cleanup_until(fname);
4412742Swollman}
44217200Swollman
44317200Swollman
44417200Swollmanvoid
44517200Swollmanloadhist(Char *fname, int mflg)
4462742Swollman{
4472742Swollman    static Char   *loadhist_cmd[] = {STRsource, NULL, NULL, NULL};
4482742Swollman    loadhist_cmd[1] = mflg ? STRmm : STRmh;
449171948Sedwin
4502742Swollman    if (fname != NULL)
45117200Swollman	loadhist_cmd[2] = fname;
45217200Swollman    else if ((fname = varval(STRhistfile)) != STRNULL)
4539908Swollman	loadhist_cmd[2] = fname;
4549908Swollman    else
45519878Swollman	loadhist_cmd[2] = STRtildothist;
45617200Swollman
45717200Swollman    dosource(loadhist_cmd, NULL);
45817200Swollman}
45919878Swollman