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