Remove tcpslice(1).
[dragonfly.git] / contrib / tcsh-6 / sh.hist.c
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 }