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);