xref: /dragonfly/contrib/tcsh-6/sh.hist.c (revision 6b5c5d0d)
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 
35 RCSID("$tcsh: sh.hist.c,v 3.40 2007/03/01 17:14:51 christos Exp $")
36 
37 #include "tc.h"
38 
39 extern int histvalid;
40 extern struct Strbuf histline;
41 Char HistLit = 0;
42 
43 static	int	heq	(const struct wordent *, const struct wordent *);
44 static	void	hfree	(struct Hist *);
45 static	void	dohist1	(struct Hist *, int *, int);
46 static	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 
60 void
61 savehist(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 
87 static int
88 heq(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 
105 struct Hist *
106 enthist(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 
199 static void
200 hfree(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*/
211 void
212 dohist(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 
271 static void
272 dohist1(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 
295 static void
296 phist(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 
341 char *
342 fmthist(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 
374 void
375 rechist(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 
444 void
445 loadhist(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