1 /* $Header: /p/tcsh/cvsroot/tcsh/sh.hist.c,v 3.40 2007/03/01 17:14:51 christos Exp $ */
3 * sh.hist.c: Shell history expansions and substitutions
6 * Copyright (c) 1980, 1991 The Regents of the University of California.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
35 RCSID("$tcsh: sh.hist.c,v 3.40 2007/03/01 17:14:51 christos Exp $")
40 extern struct Strbuf histline
;
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);
48 #define HIST_ONLY 0x01
49 #define HIST_SAVE 0x02
50 #define HIST_LOAD 0x04
52 #define HIST_CLEAR 0x10
53 #define HIST_MERGE 0x20
54 #define HIST_TIME 0x40
61 savehist(struct wordent
*sp
, int mflg
)
67 /* throw away null lines */
68 if (sp
&& sp
->next
->word
[0] == '\n')
70 cp
= varval(STRhistory
);
76 histlen
= histlen
* 10 + *cp
++ - '0';
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
);
88 heq(const struct wordent
*a0
, const struct wordent
*b0
)
90 const struct wordent
*a
= a0
->next
, *b
= b0
->next
;
93 if (Strcmp(a
->word
, b
->word
) != 0)
98 return (b
== b0
) ? 1 : 0;
106 enthist(int event
, struct wordent
*lp
, int docopy
, int mflg
)
108 struct Hist
*p
= NULL
, *pp
= &Histlist
;
113 if ((dp
= varval(STRhistdup
)) != STRNULL
) {
114 if (eq(dp
, STRerase
)) {
115 /* masaoki@akebono.tky.hp.com (Kobayashi Masaoki) */
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
)
124 for (p
= px
->Hnext
; p
!= NULL
; p
= p
->Hnext
)
129 else if (eq(dp
, STRall
)) {
130 for (p
= pp
; (p
= p
->Hnext
) != NULL
;)
131 if (heq(lp
, &(p
->Hlex
))) {
136 else if (eq(dp
, STRprev
)) {
137 if (pp
->Hnext
&& heq(lp
, &(pp
->Hnext
->Hlex
))) {
144 np
= p
? p
: xmalloc(sizeof(*np
));
146 /* Pick up timestamp set by lex() in Htime if reading saved history */
152 (void) time(&(np
->Htime
));
157 np
->Hnum
= np
->Href
= event
;
159 copylex(&np
->Hlex
, lp
);
161 np
->histline
= Strsave(histline
.s
);
166 np
->Hlex
.next
= lp
->next
;
167 lp
->next
->prev
= &np
->Hlex
;
168 np
->Hlex
.prev
= lp
->prev
;
169 lp
->prev
->next
= &np
->Hlex
;
174 while ((p
= pp
->Hnext
) && (p
->Htime
> np
->Htime
))
176 while (p
&& p
->Htime
== np
->Htime
)
178 if (heq(&p
->Hlex
, &np
->Hlex
))
187 for (p
= Histlist
.Hnext
; p
!= pp
->Hnext
; p
= p
->Hnext
)
189 n
= p
->Hnum
; r
= p
->Href
;
190 p
->Hnum
= np
->Hnum
; p
->Href
= np
->Href
;
191 np
->Hnum
= n
; np
->Href
= r
;
194 np
->Hnext
= pp
->Hnext
;
200 hfree(struct Hist
*hp
)
212 dohist(Char
**vp
, struct command
*c
)
217 if (getn(varval(STRhistory
)) == 0)
219 while (*++vp
&& **vp
== '-') {
246 stderror(ERR_HISTUS
, "chrSLMT");
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
);
257 if (hflg
& (HIST_LOAD
| HIST_MERGE
))
258 loadhist(*vp
, (hflg
& HIST_MERGE
) ? 1 : 0);
259 else if (hflg
& HIST_SAVE
)
265 n
= getn(varval(STRhistory
));
267 dohist1(Histlist
.Hnext
, &n
, hflg
);
272 dohist1(struct Hist
*hp
, int *np
, int hflg
)
274 int print
= (*np
) > 0;
276 for (; hp
!= 0; hp
= hp
->Hnext
) {
278 int old_pintr_disabled
;
280 pintr_push_enable(&old_pintr_disabled
);
281 cleanup_until(&old_pintr_disabled
);
284 if ((hflg
& HIST_REV
) == 0) {
285 dohist1(hp
->Hnext
, np
, hflg
);
296 phist(struct Hist
*hp
, int hflg
)
298 if (hflg
& HIST_ONLY
) {
302 * Control characters have to be written as is (output_raw).
303 * This way one can preserve special characters (like tab) in
305 * From: mveksler@vnet.ibm.com (Veksler Michael)
307 old_output_raw
= output_raw
;
309 cleanup_push(&old_output_raw
, output_raw_restore
);
310 if (hflg
& HIST_TIME
)
312 * Make file entry with history time in format:
313 * "+NNNNNNNNNN" (10 digits, left padded with ascii '0')
316 xprintf("#+%010lu\n", (unsigned long)hp
->Htime
);
318 if (HistLit
&& hp
->histline
)
319 xprintf("%S\n", hp
->histline
);
322 cleanup_until(&old_output_raw
);
325 Char
*cp
= str2short("%h\t%T\t%R\n");
327 struct varent
*vp
= adrof(STRhistory
);
329 if (vp
&& vp
->vec
!= NULL
&& vp
->vec
[0] && vp
->vec
[1])
332 p
= tprintf(FMT_HISTORY
, cp
, NULL
, hp
->Htime
, hp
);
333 cleanup_push(p
, xfree
);
342 fmthist(int fmt
, ptr_t ptr
)
344 struct Hist
*hp
= ptr
;
349 return xasprintf("%6d", hp
->Hnum
);
351 if (HistLit
&& hp
->histline
)
352 return xasprintf("%S", hp
->histline
);
357 istr
= sprlex(&hp
->Hlex
);
358 buf
= xmalloc(Strlen(istr
) * MB_LEN_MAX
+ 1);
360 for (p
= buf
, ip
= istr
; *ip
!= '\0'; ip
++)
361 p
+= one_wctomb(p
, CHAR
& *ip
);
375 rechist(Char
*fname
, int ref
)
378 int fp
, ftmp
, oldidfds
;
379 struct varent
*shist
;
380 static Char
*dumphist
[] = {STRhistory
, STRmhT
, 0, 0};
382 if (fname
== NULL
&& !ref
)
385 * If $savehist is just set, we use the value of $history
386 * else we use the value in $savehist
388 if (((snum
= varval(STRsavehist
)) == STRNULL
) &&
389 ((snum
= varval(STRhistory
)) == STRNULL
))
394 if ((fname
= varval(STRhistfile
)) == STRNULL
)
395 fname
= Strspl(varval(STRhome
), &STRtildothist
[1]);
397 fname
= Strsave(fname
);
400 fname
= globone(fname
, G_ERROR
);
401 cleanup_push(fname
, xfree
);
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.
411 * Users that like to nuke their environment require here an atomic
412 * loadhist-creat-dohist(dumphist)-close
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)
424 if ((shist
= adrof(STRsavehist
)) != NULL
&& shist
->vec
!= NULL
)
425 if (shist
->vec
[1] && eq(shist
->vec
[1], STRmerge
))
427 fp
= xcreat(short2str(fname
), 0600);
430 cleanup_until(fname
);
436 dohist(dumphist
, NULL
);
440 cleanup_until(fname
);
445 loadhist(Char
*fname
, int mflg
)
447 static Char
*loadhist_cmd
[] = {STRsource
, NULL
, NULL
, NULL
};
448 loadhist_cmd
[1] = mflg
? STRmm
: STRmh
;
451 loadhist_cmd
[2] = fname
;
452 else if ((fname
= varval(STRhistfile
)) != STRNULL
)
453 loadhist_cmd
[2] = fname
;
455 loadhist_cmd
[2] = STRtildothist
;
457 dosource(loadhist_cmd
, NULL
);