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 } 459