Vendor import of tcsh 6.15.00
[dragonfly.git] / contrib / tcsh-6 / sh.hist.c
blob72b376a19bef9904e3c7971cac359351dac75a25
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.
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.
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.
33 #include "sh.h"
35 RCSID("$tcsh: sh.hist.c,v 3.40 2007/03/01 17:14:51 christos Exp $")
37 #include "tc.h"
39 extern int histvalid;
40 extern struct Strbuf histline;
41 Char HistLit = 0;
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
51 #define HIST_REV 0x08
52 #define HIST_CLEAR 0x10
53 #define HIST_MERGE 0x20
54 #define HIST_TIME 0x40
57 * C shell
60 void
61 savehist(struct wordent *sp, int mflg)
63 struct Hist *hp, *np;
64 int histlen = 0;
65 Char *cp;
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;
76 histlen = histlen * 10 + *cp++ - '0';
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;
87 static int
88 heq(const struct wordent *a0, const struct wordent *b0)
90 const struct wordent *a = a0->next, *b = b0->next;
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;
105 struct Hist *
106 enthist(int event, struct wordent *lp, int docopy, int mflg)
108 struct Hist *p = NULL, *pp = &Histlist;
109 int n, r;
110 struct Hist *np;
111 const Char *dp;
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;
129 else if (eq(dp, STRall)) {
130 for (p = pp; (p = p->Hnext) != NULL;)
131 if (heq(lp, &(p->Hlex))) {
132 eventno--;
133 break;
136 else if (eq(dp, STRprev)) {
137 if (pp->Hnext && heq(lp, &(pp->Hnext->Hlex))) {
138 p = pp->Hnext;
139 eventno--;
144 np = p ? p : xmalloc(sizeof(*np));
146 /* Pick up timestamp set by lex() in Htime if reading saved history */
147 if (Htime != 0) {
148 np->Htime = Htime;
149 Htime = 0;
151 else
152 (void) time(&(np->Htime));
154 if (p == np)
155 return np;
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;
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;
172 if (mflg)
174 while ((p = pp->Hnext) && (p->Htime > np->Htime))
175 pp = p;
176 while (p && p->Htime == np->Htime)
178 if (heq(&p->Hlex, &np->Hlex))
180 eventno--;
181 hfree(np);
182 return (p);
184 pp = p;
185 p = p->Hnext;
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;
195 pp->Hnext = np;
196 return (np);
199 static void
200 hfree(struct Hist *hp)
203 freelex(&hp->Hlex);
204 if (hp->histline)
205 xfree(hp->histline);
206 xfree(hp);
210 /*ARGSUSED*/
211 void
212 dohist(Char **vp, struct command *c)
214 int n, hflg = 0;
216 USE(c);
217 if (getn(varval(STRhistory)) == 0)
218 return;
219 while (*++vp && **vp == '-') {
220 Char *vp2 = *vp;
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;
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)
260 rechist(*vp, 1);
261 else {
262 if (*vp)
263 n = getn(*vp);
264 else {
265 n = getn(varval(STRhistory));
267 dohist1(Histlist.Hnext, &n, hflg);
271 static void
272 dohist1(struct Hist *hp, int *np, int hflg)
274 int print = (*np) > 0;
276 for (; hp != 0; hp = hp->Hnext) {
277 if (setintr) {
278 int old_pintr_disabled;
280 pintr_push_enable(&old_pintr_disabled);
281 cleanup_until(&old_pintr_disabled);
283 (*np)--;
284 if ((hflg & HIST_REV) == 0) {
285 dohist1(hp->Hnext, np, hflg);
286 if (print)
287 phist(hp, hflg);
288 return;
290 if (*np >= 0)
291 phist(hp, hflg);
295 static void
296 phist(struct Hist *hp, int hflg)
298 if (hflg & HIST_ONLY) {
299 int old_output_raw;
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)
307 old_output_raw = output_raw;
308 output_raw = 1;
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);
320 else
321 prlex(&hp->Hlex);
322 cleanup_until(&old_output_raw);
324 else {
325 Char *cp = str2short("%h\t%T\t%R\n");
326 Char *p;
327 struct varent *vp = adrof(STRhistory);
329 if (vp && vp->vec != NULL && vp->vec[0] && vp->vec[1])
330 cp = vp->vec[1];
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);
341 char *
342 fmthist(int fmt, ptr_t ptr)
344 struct Hist *hp = ptr;
345 char *buf;
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;
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);
363 *p = '\0';
364 xfree(istr);
365 return buf;
367 default:
368 buf = xmalloc(1);
369 buf[0] = '\0';
370 return buf;
374 void
375 rechist(Char *fname, int ref)
377 Char *snum;
378 int fp, ftmp, oldidfds;
379 struct varent *shist;
380 static Char *dumphist[] = {STRhistory, STRmhT, 0, 0};
382 if (fname == NULL && !ref)
383 return;
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))
390 snum = STRmaxint;
393 if (fname == NULL) {
394 if ((fname = varval(STRhistfile)) == STRNULL)
395 fname = Strspl(varval(STRhome), &STRtildothist[1]);
396 else
397 fname = Strsave(fname);
399 else
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
413 * sequence.
415 * jw.
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)
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;
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);
444 void
445 loadhist(Char *fname, int mflg)
447 static Char *loadhist_cmd[] = {STRsource, NULL, NULL, NULL};
448 loadhist_cmd[1] = mflg ? STRmm : STRmh;
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;
457 dosource(loadhist_cmd, NULL);