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