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