xref: /freebsd/contrib/tcsh/sh.hist.c (revision 2a4a1db342263067035ce69a4017c645da63455d)
1 /* $Header: /src/pub/tcsh/sh.hist.c,v 3.27 2000/06/10 22:07:56 kim 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 
39 RCSID("$Id: sh.hist.c,v 3.27 2000/06/10 22:07:56 kim Exp $")
40 
41 #include "tc.h"
42 
43 extern bool histvalid;
44 extern Char histline[];
45 Char HistLit = 0;
46 
47 static	bool	heq	__P((struct wordent *, struct wordent *));
48 static	void	hfree	__P((struct Hist *));
49 static	void	dohist1	__P((struct Hist *, int *, int));
50 static	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 
64 void
65 savehist(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 
97 static bool
98 heq(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 
116 struct Hist *
117 enthist(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 
215 static void
216 hfree(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*/
228 void
229 dohist(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 
298 static void
299 dohist1(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 
318 static void
319 phist(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 
361 void
362 fmthist(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) / sizeof(Char), &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 
391 void
392 rechist(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 
461 void
462 loadhist(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