xref: /freebsd/contrib/tcsh/sh.hist.c (revision 4f29da19bd44f0e99f021510460a81bf754c21d2)
1 /* $Header: /src/pub/tcsh/sh.hist.c,v 3.33 2004/12/25 21:15:07 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 
35 RCSID("$Id: sh.hist.c,v 3.33 2004/12/25 21:15:07 christos Exp $")
36 
37 #include "tc.h"
38 
39 extern int histvalid;
40 extern Char histline[];
41 Char HistLit = 0;
42 
43 static	int	heq	__P((struct wordent *, struct wordent *));
44 static	void	hfree	__P((struct Hist *));
45 static	void	dohist1	__P((struct Hist *, int *, int));
46 static	void	phist	__P((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 
60 void
61 savehist(sp, mflg)
62     struct wordent *sp;
63     int mflg;
64 {
65     struct Hist *hp, *np;
66     int histlen = 0;
67     Char   *cp;
68 
69     /* throw away null lines */
70     if (sp && sp->next->word[0] == '\n')
71 	return;
72     cp = varval(STRhistory);
73     if (*cp) {
74 	Char *p = cp;
75 
76 	while (*p) {
77 	    if (!Isdigit(*p)) {
78 		histlen = 0;
79 		break;
80 	    }
81 	    histlen = histlen * 10 + *p++ - '0';
82 	}
83     }
84     if (sp)
85 	(void) enthist(++eventno, sp, 1, mflg);
86     for (hp = &Histlist; (np = hp->Hnext) != NULL;)
87 	if (eventno - np->Href >= histlen || histlen == 0)
88 	    hp->Hnext = np->Hnext, hfree(np);
89 	else
90 	    hp = np;
91 }
92 
93 static int
94 heq(a0, b0)
95     struct wordent *a0, *b0;
96 {
97     struct wordent *a = a0->next, *b = b0->next;
98 
99     for (;;) {
100 	if (Strcmp(a->word, b->word) != 0)
101 	    return 0;
102 	a = a->next;
103 	b = b->next;
104 	if (a == a0)
105 	    return (b == b0) ? 1 : 0;
106 	if (b == b0)
107 	    return 0;
108     }
109 }
110 
111 
112 struct Hist *
113 enthist(event, lp, docopy, mflg)
114     int     event;
115     struct wordent *lp;
116     int    docopy;
117     int    mflg;
118 {
119     struct Hist *p = NULL, *pp = &Histlist;
120     int n, r;
121     struct Hist *np;
122     Char *dp;
123 
124     if ((dp = varval(STRhistdup)) != STRNULL) {
125 	if (eq(dp, STRerase)) {
126 	    /* masaoki@akebono.tky.hp.com (Kobayashi Masaoki) */
127 	    struct Hist *px;
128 	    for (p = pp; (px = p, p = p->Hnext) != NULL;)
129 		if (heq(lp, &(p->Hlex))){
130 		    px->Hnext = p->Hnext;
131 		    if (Htime != 0 && p->Htime > Htime)
132 			Htime = p->Htime;
133 		    n = p->Href;
134 		    hfree(p);
135 		    for (p = px->Hnext; p != NULL; p = p->Hnext)
136 			p->Href = n--;
137 		    break;
138 		}
139 	}
140 	else if (eq(dp, STRall)) {
141 	    for (p = pp; (p = p->Hnext) != NULL;)
142 		if (heq(lp, &(p->Hlex))) {
143 		    eventno--;
144 		    break;
145 		}
146 	}
147 	else if (eq(dp, STRprev)) {
148 	    if (pp->Hnext && heq(lp, &(pp->Hnext->Hlex))) {
149 		p = pp->Hnext;
150 		eventno--;
151 	    }
152 	}
153     }
154 
155     np = p ? p : (struct Hist *) xmalloc((size_t) sizeof(*np));
156 
157     /* Pick up timestamp set by lex() in Htime if reading saved history */
158     if (Htime != (time_t) 0) {
159 	np->Htime = Htime;
160 	Htime = 0;
161     }
162     else
163 	(void) time(&(np->Htime));
164 
165     if (p == np)
166 	return np;
167 
168     np->Hnum = np->Href = event;
169     if (docopy) {
170 	copylex(&np->Hlex, lp);
171 	if (histvalid)
172 	    np->histline = Strsave(histline);
173 	else
174 	    np->histline = NULL;
175     }
176     else {
177 	np->Hlex.next = lp->next;
178 	lp->next->prev = &np->Hlex;
179 	np->Hlex.prev = lp->prev;
180 	lp->prev->next = &np->Hlex;
181 	np->histline = NULL;
182     }
183     if (mflg)
184       {
185         while ((p = pp->Hnext) && (p->Htime > np->Htime))
186 	  pp = p;
187 	while (p && p->Htime == np->Htime)
188 	  {
189 	    if (heq(&p->Hlex, &np->Hlex))
190 	      {
191 	        eventno--;
192 		hfree(np);
193 	        return (p);
194 	      }
195 	    pp = p;
196 	    p = p->Hnext;
197 	  }
198 	for (p = Histlist.Hnext; p != pp->Hnext; p = p->Hnext)
199 	  {
200 	    n = p->Hnum; r = p->Href;
201 	    p->Hnum = np->Hnum; p->Href = np->Href;
202 	    np->Hnum = n; np->Href = r;
203 	  }
204       }
205     np->Hnext = pp->Hnext;
206     pp->Hnext = np;
207     return (np);
208 }
209 
210 static void
211 hfree(hp)
212     struct Hist *hp;
213 {
214 
215     freelex(&hp->Hlex);
216     if (hp->histline)
217 	xfree((ptr_t) hp->histline);
218     xfree((ptr_t) hp);
219 }
220 
221 
222 /*ARGSUSED*/
223 void
224 dohist(vp, c)
225     Char  **vp;
226     struct command *c;
227 {
228     int     n, hflg = 0;
229 
230     USE(c);
231     if (getn(varval(STRhistory)) == 0)
232 	return;
233     if (setintr)
234 #ifdef BSDSIGS
235 	(void) sigsetmask(sigblock((sigmask_t) 0) & ~sigmask(SIGINT));
236 #else
237 	(void) sigrelse(SIGINT);
238 #endif
239     while (*++vp && **vp == '-') {
240 	Char   *vp2 = *vp;
241 
242 	while (*++vp2)
243 	    switch (*vp2) {
244 	    case 'c':
245 		hflg |= HIST_CLEAR;
246 		break;
247 	    case 'h':
248 		hflg |= HIST_ONLY;
249 		break;
250 	    case 'r':
251 		hflg |= HIST_REV;
252 		break;
253 	    case 'S':
254 		hflg |= HIST_SAVE;
255 		break;
256 	    case 'L':
257 		hflg |= HIST_LOAD;
258 		break;
259 	    case 'M':
260 	    	hflg |= HIST_MERGE;
261 		break;
262 	    case 'T':
263 	    	hflg |= HIST_TIME;
264 		break;
265 	    default:
266 		stderror(ERR_HISTUS, "chrSLMT");
267 		break;
268 	    }
269     }
270 
271     if (hflg & HIST_CLEAR) {
272 	struct Hist *np, *hp;
273 	for (hp = &Histlist; (np = hp->Hnext) != NULL;)
274 	    hp->Hnext = np->Hnext, hfree(np);
275     }
276 
277     if (hflg & (HIST_LOAD | HIST_MERGE)) {
278 	loadhist(*vp, (hflg & HIST_MERGE) ? 1 : 0);
279 	return;
280     }
281     else if (hflg & HIST_SAVE) {
282 	rechist(*vp, 1);
283 	return;
284     }
285     if (*vp)
286 	n = getn(*vp);
287     else {
288 	n = getn(varval(STRhistory));
289     }
290     dohist1(Histlist.Hnext, &n, hflg);
291 }
292 
293 static void
294 dohist1(hp, np, hflg)
295     struct Hist *hp;
296     int    *np, hflg;
297 {
298     int    print = (*np) > 0;
299 
300     for (; hp != 0; hp = hp->Hnext) {
301 	(*np)--;
302 	if ((hflg & HIST_REV) == 0) {
303 	    dohist1(hp->Hnext, np, hflg);
304 	    if (print)
305 		phist(hp, hflg);
306 	    return;
307 	}
308 	if (*np >= 0)
309 	    phist(hp, hflg);
310     }
311 }
312 
313 static void
314 phist(hp, hflg)
315     struct Hist *hp;
316     int     hflg;
317 {
318     if (hflg & HIST_ONLY) {
319        /*
320         * Control characters have to be written as is (output_raw).
321         * This way one can preserve special characters (like tab) in
322         * the history file.
323         * From: mveksler@vnet.ibm.com (Veksler Michael)
324         */
325         output_raw= 1;
326 	if (hflg & HIST_TIME)
327 	    /*
328 	     * Make file entry with history time in format:
329 	     * "+NNNNNNNNNN" (10 digits, left padded with ascii '0')
330 	     */
331 
332 	    xprintf("#+%010lu\n", hp->Htime);
333 
334 	if (HistLit && hp->histline)
335 	    xprintf("%S\n", hp->histline);
336 	else
337 	    prlex(&hp->Hlex);
338         output_raw= 0;
339     }
340     else {
341 	Char   *cp = str2short("%h\t%T\t%R\n");
342 	Char buf[INBUFSIZE];
343 	struct varent *vp = adrof(STRhistory);
344 
345 	if (vp && vp->vec != NULL && vp->vec[0] && vp->vec[1])
346 	    cp = vp->vec[1];
347 
348 	tprintf(FMT_HISTORY, buf, cp, INBUFSIZE, NULL, hp->Htime, (ptr_t) hp);
349 	for (cp = buf; *cp;)
350 	    xputwchar(*cp++);
351     }
352 }
353 
354 
355 void
356 fmthist(fmt, ptr, buf, bufsiz)
357     int fmt;
358     ptr_t ptr;
359     char *buf;
360     size_t bufsiz;
361 {
362     struct Hist *hp = (struct Hist *) ptr;
363     switch (fmt) {
364     case 'h':
365 	(void) xsnprintf(buf, bufsiz, "%6d", hp->Hnum);
366 	break;
367     case 'R':
368 	if (HistLit && hp->histline)
369 	    (void) xsnprintf(buf, bufsiz, "%S", hp->histline);
370 	else {
371 	    Char ibuf[INBUFSIZE], *ip;
372 	    char *p;
373 	    (void) sprlex(ibuf, sizeof(ibuf) / sizeof(Char), &hp->Hlex);
374 	    p = buf;
375 	    ip = ibuf;
376 	    do {
377 	        char xbuf[MB_LEN_MAX];
378 		size_t len;
379 
380 		len = one_wctomb(xbuf, CHAR & *ip);
381 		if ((size_t)((p - buf) + len) >= bufsiz)
382 		    break;
383 		memcpy(p, xbuf, len);
384 		p += len;
385 	    } while ((CHAR & *ip++) != 0);
386 	    if (p <= buf + bufsiz - 1)
387 	        *p = '\0';
388 	}
389 	break;
390     default:
391 	buf[0] = '\0';
392 	break;
393     }
394 
395 }
396 
397 void
398 rechist(fname, ref)
399     Char *fname;
400     int ref;
401 {
402     Char    *snum;
403     int     fp, ftmp, oldidfds;
404     struct varent *shist;
405     static Char   *dumphist[] = {STRhistory, STRmhT, 0, 0};
406 
407     if (fname == NULL && !ref)
408 	return;
409     /*
410      * If $savehist is just set, we use the value of $history
411      * else we use the value in $savehist
412      */
413     if (((snum = varval(STRsavehist)) == STRNULL) &&
414 	((snum = varval(STRhistory)) == STRNULL))
415 	snum = STRmaxint;
416 
417 
418     if (fname == NULL) {
419 	if ((fname = varval(STRhistfile)) == STRNULL)
420 	    fname = Strspl(varval(STRhome), &STRtildothist[1]);
421 	else
422 	    fname = Strsave(fname);
423     }
424     else
425 	fname = globone(fname, G_ERROR);
426 
427     /*
428      * The 'savehist merge' feature is intended for an environment
429      * with numerous shells beeing in simultaneous use. Imagine
430      * any kind of window system. All these shells 'share' the same
431      * ~/.history file for recording their command line history.
432      * Currently the automatic merge can only succeed when the shells
433      * nicely quit one after another.
434      *
435      * Users that like to nuke their environment require here an atomic
436      * 	loadhist-creat-dohist(dumphist)-close
437      * sequence.
438      *
439      * jw.
440      */
441     /*
442      * We need the didfds stuff before loadhist otherwise
443      * exec in a script will fail to print if merge is set.
444      * From: mveksler@iil.intel.com (Veksler Michael)
445      */
446     oldidfds = didfds;
447     didfds = 0;
448     if ((shist = adrof(STRsavehist)) != NULL && shist->vec != NULL)
449 	if (shist->vec[1] && eq(shist->vec[1], STRmerge))
450 	    loadhist(fname, 1);
451     fp = creat(short2str(fname), 0600);
452     if (fp == -1) {
453 	didfds = oldidfds;
454 	return;
455     }
456     ftmp = SHOUT;
457     SHOUT = fp;
458     dumphist[2] = snum;
459     dohist(dumphist, NULL);
460     (void) close(fp);
461     SHOUT = ftmp;
462     didfds = oldidfds;
463     xfree((ptr_t) fname);
464 }
465 
466 
467 void
468 loadhist(fname, mflg)
469     Char *fname;
470     int mflg;
471 {
472     static Char   *loadhist_cmd[] = {STRsource, NULL, NULL, NULL};
473     loadhist_cmd[1] = mflg ? STRmm : STRmh;
474 
475     if (fname != NULL)
476 	loadhist_cmd[2] = fname;
477     else if ((fname = varval(STRhistfile)) != STRNULL)
478 	loadhist_cmd[2] = fname;
479     else
480 	loadhist_cmd[2] = STRtildothist;
481 
482     dosource(loadhist_cmd, NULL);
483 }
484