sh.hist.c revision 59243
1/* $Header: /src/pub/tcsh/sh.hist.c,v 3.26 1999/02/06 15:01:23 christos Exp $ */
2/*
3 * sh.hist.c: Shell history expansions and substitutions
4 */
5/*-
6 * Copyright (c) 1980, 1991 The Regents of the University of California.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *	This product includes software developed by the University of
20 *	California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37#include "sh.h"
38
39RCSID("$Id: sh.hist.c,v 3.26 1999/02/06 15:01:23 christos Exp $")
40
41#include "tc.h"
42
43extern bool histvalid;
44extern Char histline[];
45Char HistLit = 0;
46
47static	bool	heq	__P((struct wordent *, struct wordent *));
48static	void	hfree	__P((struct Hist *));
49static	void	dohist1	__P((struct Hist *, int *, int));
50static	void	phist	__P((struct Hist *, int));
51
52#define HIST_ONLY	0x01
53#define HIST_SAVE	0x02
54#define HIST_LOAD	0x04
55#define HIST_REV	0x08
56#define HIST_CLEAR	0x10
57#define HIST_MERGE	0x20
58#define HIST_TIME	0x40
59
60/*
61 * C shell
62 */
63
64void
65savehist(sp, mflg)
66    struct wordent *sp;
67    bool mflg;
68{
69    register struct Hist *hp, *np;
70    register int histlen = 0;
71    Char   *cp;
72
73    /* throw away null lines */
74    if (sp && sp->next->word[0] == '\n')
75	return;
76    cp = varval(STRhistory);
77    if (*cp) {
78	register Char *p = cp;
79
80	while (*p) {
81	    if (!Isdigit(*p)) {
82		histlen = 0;
83		break;
84	    }
85	    histlen = histlen * 10 + *p++ - '0';
86	}
87    }
88    if (sp)
89	(void) enthist(++eventno, sp, 1, mflg);
90    for (hp = &Histlist; (np = hp->Hnext) != NULL;)
91	if (eventno - np->Href >= histlen || histlen == 0)
92	    hp->Hnext = np->Hnext, hfree(np);
93	else
94	    hp = np;
95}
96
97static bool
98heq(a0, b0)
99    struct wordent *a0, *b0;
100{
101    struct wordent *a = a0->next, *b = b0->next;
102
103    for (;;) {
104	if (Strcmp(a->word, b->word) != 0)
105	    return 0;
106	a = a->next;
107	b = b->next;
108	if (a == a0)
109	    return (b == b0) ? 1 : 0;
110	if (b == b0)
111	    return 0;
112    }
113}
114
115
116struct Hist *
117enthist(event, lp, docopy, mflg)
118    int     event;
119    register struct wordent *lp;
120    bool    docopy;
121    bool    mflg;
122{
123    extern time_t Htime;
124    struct Hist *p = NULL, *pp = &Histlist;
125    int n, r;
126    register struct Hist *np;
127    Char *dp;
128
129    if ((dp = varval(STRhistdup)) != STRNULL) {
130	if (eq(dp, STRerase)) {
131	    /* masaoki@akebono.tky.hp.com (Kobayashi Masaoki) */
132	    struct Hist *px;
133	    for (p = pp; (px = p, p = p->Hnext) != NULL;)
134		if (heq(lp, &(p->Hlex))){
135		    px->Hnext = p->Hnext;
136		    if (Htime != 0 && p->Htime > Htime)
137			Htime = p->Htime;
138		    n = p->Href;
139		    hfree(p);
140		    for (p = px->Hnext; p != NULL; p = p->Hnext)
141			p->Href = n--;
142		    break;
143		}
144	}
145	else if (eq(dp, STRall)) {
146	    for (p = pp; (p = p->Hnext) != NULL;)
147		if (heq(lp, &(p->Hlex))) {
148		    eventno--;
149		    break;
150		}
151	}
152	else if (eq(dp, STRprev)) {
153	    if (pp->Hnext && heq(lp, &(pp->Hnext->Hlex))) {
154		p = pp->Hnext;
155		eventno--;
156	    }
157	}
158    }
159
160    np = p ? p : (struct Hist *) xmalloc((size_t) sizeof(*np));
161
162    /* Pick up timestamp set by lex() in Htime if reading saved history */
163    if (Htime != (time_t) 0) {
164	np->Htime = Htime;
165	Htime = 0;
166    }
167    else
168	(void) time(&(np->Htime));
169
170    if (p == np)
171	return np;
172
173    np->Hnum = np->Href = event;
174    if (docopy) {
175	copylex(&np->Hlex, lp);
176	if (histvalid)
177	    np->histline = Strsave(histline);
178	else
179	    np->histline = NULL;
180    }
181    else {
182	np->Hlex.next = lp->next;
183	lp->next->prev = &np->Hlex;
184	np->Hlex.prev = lp->prev;
185	lp->prev->next = &np->Hlex;
186	np->histline = NULL;
187    }
188    if (mflg)
189      {
190        while ((p = pp->Hnext) && (p->Htime > np->Htime))
191	  pp = p;
192	while (p && p->Htime == np->Htime)
193	  {
194	    if (heq(&p->Hlex, &np->Hlex))
195	      {
196	        eventno--;
197		hfree(np);
198	        return (p);
199	      }
200	    pp = p;
201	    p = p->Hnext;
202	  }
203	for (p = Histlist.Hnext; p != pp->Hnext; p = p->Hnext)
204	  {
205	    n = p->Hnum; r = p->Href;
206	    p->Hnum = np->Hnum; p->Href = np->Href;
207	    np->Hnum = n; np->Href = r;
208	  }
209      }
210    np->Hnext = pp->Hnext;
211    pp->Hnext = np;
212    return (np);
213}
214
215static void
216hfree(hp)
217    register struct Hist *hp;
218{
219
220    freelex(&hp->Hlex);
221    if (hp->histline)
222	xfree((ptr_t) hp->histline);
223    xfree((ptr_t) hp);
224}
225
226
227/*ARGSUSED*/
228void
229dohist(vp, c)
230    Char  **vp;
231    struct command *c;
232{
233    int     n, hflg = 0;
234
235    USE(c);
236    if (getn(varval(STRhistory)) == 0)
237	return;
238    if (setintr)
239#ifdef BSDSIGS
240	(void) sigsetmask(sigblock((sigmask_t) 0) & ~sigmask(SIGINT));
241#else
242	(void) sigrelse(SIGINT);
243#endif
244    while (*++vp && **vp == '-') {
245	Char   *vp2 = *vp;
246
247	while (*++vp2)
248	    switch (*vp2) {
249	    case 'c':
250		hflg |= HIST_CLEAR;
251		break;
252	    case 'h':
253		hflg |= HIST_ONLY;
254		break;
255	    case 'r':
256		hflg |= HIST_REV;
257		break;
258	    case 'S':
259		hflg |= HIST_SAVE;
260		break;
261	    case 'L':
262		hflg |= HIST_LOAD;
263		break;
264	    case 'M':
265	    	hflg |= HIST_MERGE;
266		break;
267	    case 'T':
268	    	hflg |= HIST_TIME;
269		break;
270	    default:
271		stderror(ERR_HISTUS, "chrSLMT");
272		break;
273	    }
274    }
275
276    if (hflg & HIST_CLEAR) {
277	struct Hist *np, *hp;
278	for (hp = &Histlist; (np = hp->Hnext) != NULL;)
279	    hp->Hnext = np->Hnext, hfree(np);
280    }
281
282    if (hflg & (HIST_LOAD | HIST_MERGE)) {
283	loadhist(*vp, (hflg & HIST_MERGE) ? 1 : 0);
284	return;
285    }
286    else if (hflg & HIST_SAVE) {
287	rechist(*vp, 1);
288	return;
289    }
290    if (*vp)
291	n = getn(*vp);
292    else {
293	n = getn(varval(STRhistory));
294    }
295    dohist1(Histlist.Hnext, &n, hflg);
296}
297
298static void
299dohist1(hp, np, hflg)
300    struct Hist *hp;
301    int    *np, hflg;
302{
303    bool    print = (*np) > 0;
304
305    for (; hp != 0; hp = hp->Hnext) {
306	(*np)--;
307	if ((hflg & HIST_REV) == 0) {
308	    dohist1(hp->Hnext, np, hflg);
309	    if (print)
310		phist(hp, hflg);
311	    return;
312	}
313	if (*np >= 0)
314	    phist(hp, hflg);
315    }
316}
317
318static void
319phist(hp, hflg)
320    register struct Hist *hp;
321    int     hflg;
322{
323    extern bool output_raw;
324    if (hflg & HIST_ONLY) {
325       /*
326        * Control characters have to be written as is (output_raw).
327        * This way one can preserve special characters (like tab) in
328        * the history file.
329        * From: mveksler@vnet.ibm.com (Veksler Michael)
330        */
331        output_raw= 1;
332	if (hflg & HIST_TIME)
333	    /*
334	     * Make file entry with history time in format:
335	     * "+NNNNNNNNNN" (10 digits, left padded with ascii '0')
336	     */
337
338	    xprintf("#+%010lu\n", hp->Htime);
339
340	if (HistLit && hp->histline)
341	    xprintf("%S\n", hp->histline);
342	else
343	    prlex(&hp->Hlex);
344        output_raw= 0;
345    }
346    else {
347	Char   *cp = str2short("%h\t%T\t%R\n");
348	Char buf[INBUFSIZE];
349	struct varent *vp = adrof(STRhistory);
350
351	if (vp && vp->vec[0] && vp->vec[1])
352	    cp = vp->vec[1];
353
354	tprintf(FMT_HISTORY, buf, cp, INBUFSIZE, NULL, hp->Htime, (ptr_t) hp);
355	for (cp = buf; *cp;)
356	    xputchar(*cp++);
357    }
358}
359
360
361void
362fmthist(fmt, ptr, buf, bufsiz)
363    int fmt;
364    ptr_t ptr;
365    char *buf;
366    size_t bufsiz;
367{
368    struct Hist *hp = (struct Hist *) ptr;
369    switch (fmt) {
370    case 'h':
371	(void) xsnprintf(buf, bufsiz, "%6d", hp->Hnum);
372	break;
373    case 'R':
374	if (HistLit && hp->histline)
375	    (void) xsnprintf(buf, bufsiz, "%S", hp->histline);
376	else {
377	    Char ibuf[INBUFSIZE], *ip;
378	    char *p;
379	    (void) sprlex(ibuf, sizeof(ibuf), &hp->Hlex);
380	    for (p = buf, ip = ibuf; (*p++ = (CHAR & *ip++)) != '\0'; )
381		continue;
382	}
383	break;
384    default:
385	buf[0] = '\0';
386	break;
387    }
388
389}
390
391void
392rechist(fname, ref)
393    Char *fname;
394    int ref;
395{
396    Char    *snum;
397    int     fp, ftmp, oldidfds;
398    struct varent *shist;
399    static Char   *dumphist[] = {STRhistory, STRmhT, 0, 0};
400
401    if (fname == NULL && !ref)
402	return;
403    /*
404     * If $savehist is just set, we use the value of $history
405     * else we use the value in $savehist
406     */
407    if (((snum = varval(STRsavehist)) == STRNULL) &&
408	((snum = varval(STRhistory)) == STRNULL))
409	snum = STRmaxint;
410
411
412    if (fname == NULL) {
413	if ((fname = varval(STRhistfile)) == STRNULL)
414	    fname = Strspl(varval(STRhome), &STRtildothist[1]);
415	else
416	    fname = Strsave(fname);
417    }
418    else
419	fname = globone(fname, G_ERROR);
420
421    /*
422     * The 'savehist merge' feature is intended for an environment
423     * with numerous shells beeing in simultaneous use. Imagine
424     * any kind of window system. All these shells 'share' the same
425     * ~/.history file for recording their command line history.
426     * Currently the automatic merge can only succeed when the shells
427     * nicely quit one after another.
428     *
429     * Users that like to nuke their environment require here an atomic
430     * 	loadhist-creat-dohist(dumphist)-close
431     * sequence.
432     *
433     * jw.
434     */
435    /*
436     * We need the didfds stuff before loadhist otherwise
437     * exec in a script will fail to print if merge is set.
438     * From: mveksler@iil.intel.com (Veksler Michael)
439     */
440    oldidfds = didfds;
441    didfds = 0;
442    if ((shist = adrof(STRsavehist)) != NULL)
443	if (shist->vec[1] && eq(shist->vec[1], STRmerge))
444	    loadhist(fname, 1);
445    fp = creat(short2str(fname), 0600);
446    if (fp == -1) {
447	didfds = oldidfds;
448	return;
449    }
450    ftmp = SHOUT;
451    SHOUT = fp;
452    dumphist[2] = snum;
453    dohist(dumphist, NULL);
454    (void) close(fp);
455    SHOUT = ftmp;
456    didfds = oldidfds;
457    xfree((ptr_t) fname);
458}
459
460
461void
462loadhist(fname, mflg)
463    Char *fname;
464    bool mflg;
465{
466    static Char   *loadhist_cmd[] = {STRsource, NULL, NULL, NULL};
467    loadhist_cmd[1] = mflg ? STRmm : STRmh;
468
469    if (fname != NULL)
470	loadhist_cmd[2] = fname;
471    else if ((fname = varval(STRhistfile)) != STRNULL)
472	loadhist_cmd[2] = fname;
473    else
474	loadhist_cmd[2] = STRtildothist;
475
476    dosource(loadhist_cmd, NULL);
477}
478