1 /* $Header: /src/pub/tcsh/sh.hist.c,v 3.29 2002/06/25 19:02:11 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("$Id: sh.hist.c,v 3.29 2002/06/25 19:02:11 christos Exp $") 36 37 #include "tc.h" 38 39 extern bool histvalid; 40 extern Char histline[]; 41 Char HistLit = 0; 42 43 static bool heq __P((struct wordent *, struct wordent *)); 44 static void hfree __P((struct Hist *)); 45 static void dohist1 __P((struct Hist *, int *, int)); 46 static void phist __P((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(sp, mflg) 62 struct wordent *sp; 63 bool mflg; 64 { 65 register struct Hist *hp, *np; 66 register int histlen = 0; 67 Char *cp; 68 69 /* throw away null lines */ 70 if (sp && sp->next->word[0] == '\n') 71 return; 72 cp = varval(STRhistory); 73 if (*cp) { 74 register Char *p = cp; 75 76 while (*p) { 77 if (!Isdigit(*p)) { 78 histlen = 0; 79 break; 80 } 81 histlen = histlen * 10 + *p++ - '0'; 82 } 83 } 84 if (sp) 85 (void) enthist(++eventno, sp, 1, mflg); 86 for (hp = &Histlist; (np = hp->Hnext) != NULL;) 87 if (eventno - np->Href >= histlen || histlen == 0) 88 hp->Hnext = np->Hnext, hfree(np); 89 else 90 hp = np; 91 } 92 93 static bool 94 heq(a0, b0) 95 struct wordent *a0, *b0; 96 { 97 struct wordent *a = a0->next, *b = b0->next; 98 99 for (;;) { 100 if (Strcmp(a->word, b->word) != 0) 101 return 0; 102 a = a->next; 103 b = b->next; 104 if (a == a0) 105 return (b == b0) ? 1 : 0; 106 if (b == b0) 107 return 0; 108 } 109 } 110 111 112 struct Hist * 113 enthist(event, lp, docopy, mflg) 114 int event; 115 register struct wordent *lp; 116 bool docopy; 117 bool mflg; 118 { 119 extern time_t Htime; 120 struct Hist *p = NULL, *pp = &Histlist; 121 int n, r; 122 register struct Hist *np; 123 Char *dp; 124 125 if ((dp = varval(STRhistdup)) != STRNULL) { 126 if (eq(dp, STRerase)) { 127 /* masaoki@akebono.tky.hp.com (Kobayashi Masaoki) */ 128 struct Hist *px; 129 for (p = pp; (px = p, p = p->Hnext) != NULL;) 130 if (heq(lp, &(p->Hlex))){ 131 px->Hnext = p->Hnext; 132 if (Htime != 0 && p->Htime > Htime) 133 Htime = p->Htime; 134 n = p->Href; 135 hfree(p); 136 for (p = px->Hnext; p != NULL; p = p->Hnext) 137 p->Href = n--; 138 break; 139 } 140 } 141 else if (eq(dp, STRall)) { 142 for (p = pp; (p = p->Hnext) != NULL;) 143 if (heq(lp, &(p->Hlex))) { 144 eventno--; 145 break; 146 } 147 } 148 else if (eq(dp, STRprev)) { 149 if (pp->Hnext && heq(lp, &(pp->Hnext->Hlex))) { 150 p = pp->Hnext; 151 eventno--; 152 } 153 } 154 } 155 156 np = p ? p : (struct Hist *) xmalloc((size_t) sizeof(*np)); 157 158 /* Pick up timestamp set by lex() in Htime if reading saved history */ 159 if (Htime != (time_t) 0) { 160 np->Htime = Htime; 161 Htime = 0; 162 } 163 else 164 (void) time(&(np->Htime)); 165 166 if (p == np) 167 return np; 168 169 np->Hnum = np->Href = event; 170 if (docopy) { 171 copylex(&np->Hlex, lp); 172 if (histvalid) 173 np->histline = Strsave(histline); 174 else 175 np->histline = NULL; 176 } 177 else { 178 np->Hlex.next = lp->next; 179 lp->next->prev = &np->Hlex; 180 np->Hlex.prev = lp->prev; 181 lp->prev->next = &np->Hlex; 182 np->histline = NULL; 183 } 184 if (mflg) 185 { 186 while ((p = pp->Hnext) && (p->Htime > np->Htime)) 187 pp = p; 188 while (p && p->Htime == np->Htime) 189 { 190 if (heq(&p->Hlex, &np->Hlex)) 191 { 192 eventno--; 193 hfree(np); 194 return (p); 195 } 196 pp = p; 197 p = p->Hnext; 198 } 199 for (p = Histlist.Hnext; p != pp->Hnext; p = p->Hnext) 200 { 201 n = p->Hnum; r = p->Href; 202 p->Hnum = np->Hnum; p->Href = np->Href; 203 np->Hnum = n; np->Href = r; 204 } 205 } 206 np->Hnext = pp->Hnext; 207 pp->Hnext = np; 208 return (np); 209 } 210 211 static void 212 hfree(hp) 213 register struct Hist *hp; 214 { 215 216 freelex(&hp->Hlex); 217 if (hp->histline) 218 xfree((ptr_t) hp->histline); 219 xfree((ptr_t) hp); 220 } 221 222 223 /*ARGSUSED*/ 224 void 225 dohist(vp, c) 226 Char **vp; 227 struct command *c; 228 { 229 int n, hflg = 0; 230 231 USE(c); 232 if (getn(varval(STRhistory)) == 0) 233 return; 234 if (setintr) 235 #ifdef BSDSIGS 236 (void) sigsetmask(sigblock((sigmask_t) 0) & ~sigmask(SIGINT)); 237 #else 238 (void) sigrelse(SIGINT); 239 #endif 240 while (*++vp && **vp == '-') { 241 Char *vp2 = *vp; 242 243 while (*++vp2) 244 switch (*vp2) { 245 case 'c': 246 hflg |= HIST_CLEAR; 247 break; 248 case 'h': 249 hflg |= HIST_ONLY; 250 break; 251 case 'r': 252 hflg |= HIST_REV; 253 break; 254 case 'S': 255 hflg |= HIST_SAVE; 256 break; 257 case 'L': 258 hflg |= HIST_LOAD; 259 break; 260 case 'M': 261 hflg |= HIST_MERGE; 262 break; 263 case 'T': 264 hflg |= HIST_TIME; 265 break; 266 default: 267 stderror(ERR_HISTUS, "chrSLMT"); 268 break; 269 } 270 } 271 272 if (hflg & HIST_CLEAR) { 273 struct Hist *np, *hp; 274 for (hp = &Histlist; (np = hp->Hnext) != NULL;) 275 hp->Hnext = np->Hnext, hfree(np); 276 } 277 278 if (hflg & (HIST_LOAD | HIST_MERGE)) { 279 loadhist(*vp, (hflg & HIST_MERGE) ? 1 : 0); 280 return; 281 } 282 else if (hflg & HIST_SAVE) { 283 rechist(*vp, 1); 284 return; 285 } 286 if (*vp) 287 n = getn(*vp); 288 else { 289 n = getn(varval(STRhistory)); 290 } 291 dohist1(Histlist.Hnext, &n, hflg); 292 } 293 294 static void 295 dohist1(hp, np, hflg) 296 struct Hist *hp; 297 int *np, hflg; 298 { 299 bool print = (*np) > 0; 300 301 for (; hp != 0; hp = hp->Hnext) { 302 (*np)--; 303 if ((hflg & HIST_REV) == 0) { 304 dohist1(hp->Hnext, np, hflg); 305 if (print) 306 phist(hp, hflg); 307 return; 308 } 309 if (*np >= 0) 310 phist(hp, hflg); 311 } 312 } 313 314 static void 315 phist(hp, hflg) 316 register struct Hist *hp; 317 int hflg; 318 { 319 extern bool output_raw; 320 if (hflg & HIST_ONLY) { 321 /* 322 * Control characters have to be written as is (output_raw). 323 * This way one can preserve special characters (like tab) in 324 * the history file. 325 * From: mveksler@vnet.ibm.com (Veksler Michael) 326 */ 327 output_raw= 1; 328 if (hflg & HIST_TIME) 329 /* 330 * Make file entry with history time in format: 331 * "+NNNNNNNNNN" (10 digits, left padded with ascii '0') 332 */ 333 334 xprintf("#+%010lu\n", hp->Htime); 335 336 if (HistLit && hp->histline) 337 xprintf("%S\n", hp->histline); 338 else 339 prlex(&hp->Hlex); 340 output_raw= 0; 341 } 342 else { 343 Char *cp = str2short("%h\t%T\t%R\n"); 344 Char buf[INBUFSIZE]; 345 struct varent *vp = adrof(STRhistory); 346 347 if (vp && vp->vec != NULL && vp->vec[0] && vp->vec[1]) 348 cp = vp->vec[1]; 349 350 tprintf(FMT_HISTORY, buf, cp, INBUFSIZE, NULL, hp->Htime, (ptr_t) hp); 351 for (cp = buf; *cp;) 352 xputchar(*cp++); 353 } 354 } 355 356 357 void 358 fmthist(fmt, ptr, buf, bufsiz) 359 int fmt; 360 ptr_t ptr; 361 char *buf; 362 size_t bufsiz; 363 { 364 struct Hist *hp = (struct Hist *) ptr; 365 switch (fmt) { 366 case 'h': 367 (void) xsnprintf(buf, bufsiz, "%6d", hp->Hnum); 368 break; 369 case 'R': 370 if (HistLit && hp->histline) 371 (void) xsnprintf(buf, bufsiz, "%S", hp->histline); 372 else { 373 Char ibuf[INBUFSIZE], *ip; 374 char *p; 375 (void) sprlex(ibuf, sizeof(ibuf) / sizeof(Char), &hp->Hlex); 376 for (p = buf, ip = ibuf; (*p++ = (CHAR & *ip++)) != '\0'; ) 377 continue; 378 } 379 break; 380 default: 381 buf[0] = '\0'; 382 break; 383 } 384 385 } 386 387 void 388 rechist(fname, ref) 389 Char *fname; 390 int ref; 391 { 392 Char *snum; 393 int fp, ftmp, oldidfds; 394 struct varent *shist; 395 static Char *dumphist[] = {STRhistory, STRmhT, 0, 0}; 396 397 if (fname == NULL && !ref) 398 return; 399 /* 400 * If $savehist is just set, we use the value of $history 401 * else we use the value in $savehist 402 */ 403 if (((snum = varval(STRsavehist)) == STRNULL) && 404 ((snum = varval(STRhistory)) == STRNULL)) 405 snum = STRmaxint; 406 407 408 if (fname == NULL) { 409 if ((fname = varval(STRhistfile)) == STRNULL) 410 fname = Strspl(varval(STRhome), &STRtildothist[1]); 411 else 412 fname = Strsave(fname); 413 } 414 else 415 fname = globone(fname, G_ERROR); 416 417 /* 418 * The 'savehist merge' feature is intended for an environment 419 * with numerous shells beeing in simultaneous use. Imagine 420 * any kind of window system. All these shells 'share' the same 421 * ~/.history file for recording their command line history. 422 * Currently the automatic merge can only succeed when the shells 423 * nicely quit one after another. 424 * 425 * Users that like to nuke their environment require here an atomic 426 * loadhist-creat-dohist(dumphist)-close 427 * sequence. 428 * 429 * jw. 430 */ 431 /* 432 * We need the didfds stuff before loadhist otherwise 433 * exec in a script will fail to print if merge is set. 434 * From: mveksler@iil.intel.com (Veksler Michael) 435 */ 436 oldidfds = didfds; 437 didfds = 0; 438 if ((shist = adrof(STRsavehist)) != NULL && shist->vec != NULL) 439 if (shist->vec[1] && eq(shist->vec[1], STRmerge)) 440 loadhist(fname, 1); 441 fp = creat(short2str(fname), 0600); 442 if (fp == -1) { 443 didfds = oldidfds; 444 return; 445 } 446 ftmp = SHOUT; 447 SHOUT = fp; 448 dumphist[2] = snum; 449 dohist(dumphist, NULL); 450 (void) close(fp); 451 SHOUT = ftmp; 452 didfds = oldidfds; 453 xfree((ptr_t) fname); 454 } 455 456 457 void 458 loadhist(fname, mflg) 459 Char *fname; 460 bool mflg; 461 { 462 static Char *loadhist_cmd[] = {STRsource, NULL, NULL, NULL}; 463 loadhist_cmd[1] = mflg ? STRmm : STRmh; 464 465 if (fname != NULL) 466 loadhist_cmd[2] = fname; 467 else if ((fname = varval(STRhistfile)) != STRNULL) 468 loadhist_cmd[2] = fname; 469 else 470 loadhist_cmd[2] = STRtildothist; 471 472 dosource(loadhist_cmd, NULL); 473 } 474