1 /* 2 * sh.hist.c: Shell history expansions and substitutions 3 */ 4 /*- 5 * Copyright (c) 1980, 1991 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 #include "sh.h" 33 #include <stdio.h> /* for rename(2), grr. */ 34 #include <assert.h> 35 #include "tc.h" 36 #include "dotlock.h" 37 38 extern int histvalid; 39 extern struct Strbuf histline; 40 Char HistLit = 0; 41 42 static int heq (const struct wordent *, const struct wordent *); 43 static void hfree (struct Hist *); 44 45 #define HIST_ONLY 0x01 46 #define HIST_SAVE 0x02 47 #define HIST_LOAD 0x04 48 #define HIST_REV 0x08 49 #define HIST_CLEAR 0x10 50 #define HIST_MERGE 0x20 51 #define HIST_TIME 0x40 52 53 /* 54 * C shell 55 */ 56 57 /* Static functions don't show up in gprof summaries. So eliminate "static" 58 * modifier from some frequently called functions. */ 59 #ifdef PROF 60 #define PG_STATIC 61 #else 62 #define PG_STATIC static 63 #endif 64 65 /* #define DEBUG_HIST 1 */ 66 67 static const int fastMergeErase = 1; 68 static unsigned histCount = 0; /* number elements on history list */ 69 static int histlen = 0; 70 static struct Hist *histTail = NULL; /* last element on history list */ 71 static struct Hist *histMerg = NULL; /* last element merged by Htime */ 72 73 static void insertHistHashTable(struct Hist *, unsigned); 74 75 /* Insert new element (hp) in history list after specified predecessor (pp). */ 76 static void 77 hinsert(struct Hist *hp, struct Hist *pp) 78 { 79 struct Hist *fp = pp->Hnext; /* following element, if any */ 80 hp->Hnext = fp, hp->Hprev = pp; 81 pp->Hnext = hp; 82 if (fp) 83 fp->Hprev = hp; 84 else 85 histTail = hp; /* meaning hp->Hnext == NULL */ 86 histCount++; 87 } 88 89 /* Remove the entry from the history list. */ 90 static void 91 hremove(struct Hist *hp) 92 { 93 struct Hist *pp = hp->Hprev; 94 assert(pp); /* elements always have a previous */ 95 pp->Hnext = hp->Hnext; 96 if (hp->Hnext) 97 hp->Hnext->Hprev = pp; 98 else 99 histTail = pp; /* we must have been last */ 100 if (hp == histMerg) /* deleting this hint from list */ 101 histMerg = NULL; 102 assert(histCount > 0); 103 histCount--; 104 } 105 106 /* Prune length of history list to specified size by history variable. */ 107 PG_STATIC void 108 discardExcess(int hlen) 109 { 110 struct Hist *hp, *np; 111 if (histTail == NULL) { 112 assert(histCount == 0); 113 return; /* no entries on history list */ 114 } 115 /* Prune dummy entries from the front, then old entries from the back. If 116 * the list is still too long scan the whole list as before. But only do a 117 * full scan if the list is more than 6% (1/16th) too long. */ 118 while (histCount > (unsigned)hlen && (np = Histlist.Hnext)) { 119 if (eventno - np->Href >= hlen || hlen == 0) 120 hremove(np), hfree(np); 121 else 122 break; 123 } 124 while (histCount > (unsigned)hlen && (np = histTail) != &Histlist) { 125 if (eventno - np->Href >= hlen || hlen == 0) 126 hremove(np), hfree(np); 127 else 128 break; 129 } 130 if (histCount - (hlen >> 4) <= (unsigned)hlen) 131 return; /* don't bother doing the full scan */ 132 for (hp = &Histlist; histCount > (unsigned)hlen && 133 (np = hp->Hnext) != NULL;) 134 if (eventno - np->Href >= hlen || hlen == 0) 135 hremove(np), hfree(np); 136 else 137 hp = np; 138 } 139 140 /* Add the command "sp" to the history list. */ 141 void 142 savehist( 143 struct wordent *sp, 144 int mflg) /* true if -m (merge) specified */ 145 { 146 /* throw away null lines */ 147 if (sp && sp->next->word[0] == '\n') 148 return; 149 if (sp) 150 (void) enthist(++eventno, sp, 1, mflg, histlen); 151 discardExcess(histlen); 152 } 153 154 #define USE_JENKINS_HASH 1 155 /* #define USE_ONE_AT_A_TIME 1 */ 156 #undef PRIME_LENGTH /* no need for good HTL */ 157 158 #ifdef USE_JENKINS_HASH 159 #define hashFcnName "lookup3" 160 /* From: 161 lookup3.c, by Bob Jenkins, May 2006, Public Domain. 162 "... You can use this free for any purpose. It's in 163 the public domain. It has no warranty." 164 http://burtleburtle.net/bob/hash/index.html 165 */ 166 167 #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) 168 #define mix(a,b,c) \ 169 { \ 170 a -= c; a ^= rot(c, 4); c += b; \ 171 b -= a; b ^= rot(a, 6); a += c; \ 172 c -= b; c ^= rot(b, 8); b += a; \ 173 a -= c; a ^= rot(c,16); c += b; \ 174 b -= a; b ^= rot(a,19); a += c; \ 175 c -= b; c ^= rot(b, 4); b += a; \ 176 } 177 #define final(a,b,c) \ 178 { \ 179 c ^= b; c -= rot(b,14); \ 180 a ^= c; a -= rot(c,11); \ 181 b ^= a; b -= rot(a,25); \ 182 c ^= b; c -= rot(b,16); \ 183 a ^= c; a -= rot(c, 4); \ 184 b ^= a; b -= rot(a,14); \ 185 c ^= b; c -= rot(b,24); \ 186 } 187 188 struct hashValue /* State used to hash a wordend word list. */ 189 { 190 uint32_t a, b, c; 191 }; 192 193 /* Set up the internal state */ 194 static void 195 initializeHash(struct hashValue *h) 196 { 197 h->a = h->b = h->c = 0xdeadbeef; 198 } 199 200 /* This does a partial hash of the Chars in a single word. For efficiency we 201 * include 3 versions of the code to pack Chars into 32-bit words for the 202 * mixing function. */ 203 static void 204 addWordToHash(struct hashValue *h, const Char *word) 205 { 206 uint32_t a = h->a, b = h->b, c = h->c; 207 #ifdef SHORT_STRINGS 208 #define GETK if ((k = (uChar)*word++) == 0) break 209 #ifdef WIDE_STRINGS 210 assert(sizeof(Char) >= 4); 211 while (1) { 212 unsigned k; 213 GETK; 214 a += k; 215 GETK; 216 b += k; 217 GETK; 218 c += k; 219 mix(a, b, c); 220 } 221 #else 222 assert(sizeof(Char) == 2); 223 while (1) { 224 unsigned k; 225 GETK; 226 a += k; 227 GETK; 228 a += k << 16; 229 GETK; 230 b += k; 231 GETK; 232 b += k << 16; 233 GETK; 234 c += k; 235 GETK; 236 c += k << 16; 237 mix(a, b, c); 238 } 239 #endif 240 #else 241 assert(sizeof(Char) == 1); 242 while (1) { 243 unsigned k; 244 if ((k = *word++) == 0) break; a += k; 245 if ((k = *word++) == 0) break; a += k << 8; 246 if ((k = *word++) == 0) break; a += k << 16; 247 if ((k = *word++) == 0) break; a += k << 24; 248 if ((k = *word++) == 0) break; b += k; 249 if ((k = *word++) == 0) break; b += k << 8; 250 if ((k = *word++) == 0) break; b += k << 16; 251 if ((k = *word++) == 0) break; b += k << 24; 252 if ((k = *word++) == 0) break; c += k; 253 if ((k = *word++) == 0) break; c += k << 8; 254 if ((k = *word++) == 0) break; c += k << 16; 255 if ((k = *word++) == 0) break; c += k << 24; 256 mix(a, b, c); 257 } 258 #endif 259 h->a = a, h->b = b, h->c = c; 260 } 261 262 static void 263 addCharToHash(struct hashValue *h, Char ch) 264 { 265 /* The compiler (gcc -O2) seems to do a good job optimizing this without 266 * explicitly extracting into local variables. */ 267 h->a += (uChar)ch; 268 mix(h->a, h->b, h->c); 269 } 270 271 static uint32_t 272 finalizeHash(struct hashValue *h) 273 { 274 uint32_t a = h->a, b = h->b, c = h->c; 275 final(a, b, c); 276 return c; 277 } 278 279 #elif USE_ONE_AT_A_TIME 280 #define hashFcnName "one-at-a-time" 281 /* This one is also from Bob Jenkins, but is slower but simpler than lookup3. 282 "... The code given here are all public domain." 283 http://burtleburtle.net/bob/hash/doobs.html */ 284 285 #if 0 286 ub4 287 one_at_a_time(char *key, ub4 len) 288 { 289 ub4 hash, i; 290 for (hash=0, i=0; i<len; ++i) 291 { 292 hash += key[i]; 293 hash += (hash << 10); 294 hash ^= (hash >> 6); 295 } 296 hash += (hash << 3); 297 hash ^= (hash >> 11); 298 hash += (hash << 15); 299 return (hash & mask); 300 } 301 #endif 302 303 struct hashValue { uint32_t h; }; 304 static void 305 initializeHash(struct hashValue *h) 306 { 307 h->h = 0; 308 } 309 310 static void 311 addWordToHash(struct hashValue *h, const Char *word) 312 { 313 unsigned k; 314 uint32_t hash = h->h; 315 while (k = (uChar)*word++) 316 hash += k, hash += hash << 10, hash ^= hash >> 6; 317 h->h = hash; 318 } 319 320 static void 321 addCharToHash(struct hashValue *h, Char c) 322 { 323 Char b[2] = { c, 0 }; 324 addWordToHash(h, b); 325 } 326 327 static uint32_t 328 finalizeHash(struct hashValue *h) 329 { 330 unsigned hash = h->h; 331 hash += (hash << 3); 332 hash ^= (hash >> 11); 333 hash += (hash << 15); 334 return hash; 335 } 336 337 #else 338 #define hashFcnName "add-mul" 339 /* Simple multipy and add hash. */ 340 #define PRIME_LENGTH 1 /* need "good" HTL */ 341 struct hashValue { uint32_t h; }; 342 static void 343 initializeHash(struct hashValue *h) 344 { 345 h->h = 0xe13e2345; 346 } 347 348 static void 349 addWordToHash(struct hashValue *h, const Char *word) 350 { 351 unsigned k; 352 uint32_t hash = h->h; 353 while (k = (uChar)*word++) 354 hash = hash * 0x9e4167b9 + k; 355 h->h = hash; 356 } 357 358 static void 359 addCharToHash(struct hashValue *h, Char c) 360 { 361 h->h = h->h * 0x9e4167b9 + (uChar)c; 362 } 363 364 static uint32_t 365 finalizeHash(struct hashValue *h) 366 { 367 return h->h; 368 } 369 #endif 370 371 static unsigned 372 hashhist(struct wordent *h0) 373 { 374 struct hashValue s; 375 struct wordent *firstWord = h0->next; 376 struct wordent *h = firstWord; 377 unsigned hash = 0; 378 379 initializeHash(&s); 380 for (; h != h0; h = h->next) { 381 if (h->word[0] == '\n') 382 break; /* don't hash newline */ 383 if (h != firstWord) 384 addCharToHash(&s, ' '); /* space between words */ 385 addWordToHash(&s, h->word); 386 } 387 hash = finalizeHash(&s); 388 /* Zero means no hash value, so never return zero as a hash value. */ 389 return hash ? hash : 0x7fffffff; /* prime! */ 390 } 391 392 #if 0 393 unsigned 394 hashStr(Char *str) 395 { 396 struct hashValue s; 397 initializeHash(&s); 398 addWordToHash(&s, str); 399 return finalizeHash(&s); 400 } 401 #endif 402 403 #ifdef PRIME_LENGTH /* need good HTL */ 404 #define hash2tableIndex(hash, len) ((hash) % len) 405 #else 406 #define hash2tableIndex(hash, len) ((hash) & (len-1)) 407 #endif 408 409 /* This code can be enabled to test the above hash functions for speed and 410 * collision avoidance. The testing is enabled by "occasional" calls to 411 * displayHistStats(), see which. */ 412 #ifdef DEBUG_HIST 413 414 #ifdef BSDTIMES 415 static double 416 doTiming(int start) { 417 static struct timeval beginTime; 418 if (start) { 419 gettimeofday(&beginTime, NULL); 420 return 0.0; 421 } else { 422 struct timeval now; 423 gettimeofday(&now, NULL); 424 return (now.tv_sec-beginTime.tv_sec) + 425 (now.tv_usec-beginTime.tv_usec)/1e6; 426 } 427 } 428 #else 429 static double 430 doTiming(int start) { 431 USE(start); 432 return 0.0; 433 } 434 #endif 435 436 static void 437 generateHashes(int nChars, unsigned nWords, unsigned samples, unsigned *hashes, 438 unsigned length) 439 { 440 if (nChars < 1) 441 return; 442 nWords = (nWords < 1) ? 1 : (nWords > 4) ? 4 : nWords; 443 Char *number = xmalloc((nChars+nWords)*sizeof(Char)); 444 struct wordent word[4]; 445 struct wordent base = { NULL, &word[0], &word[0] }; 446 word[0].word = number, word[0].next = &base, word[0].prev = &base; 447 unsigned w = 0; /* word number */ 448 /* Generate multiple words of length 2, 3, 5, then all the rest. */ 449 unsigned wBoundaries[4] = { 2-1, 2+3-1, 2+3+5-1, 0 }; 450 /* Ensure the last word has at least 4 Chars in it. */ 451 while (nWords >= 2 && nChars < (wBoundaries[nWords-2]+1) + 4) 452 nWords--; 453 wBoundaries[nWords-1] = 0xffffffff; /* don't end word past this point */ 454 unsigned i; 455 for (i = 0; i<nChars; i++) { 456 /* In deference to the gawd awful add-mul hash, we won't use the worse 457 * case here (setting all Chars to 1), but assume mostly (or at least 458 * initially) ASCII data. */ 459 number[i+w] = '!'; /* 0x21 = 33 */ 460 461 if (i == wBoundaries[w]) { /* end a word here and move to next */ 462 w++; /* next word */ 463 number[i+w] = 0; /* terminate */ 464 word[w].word = &number[i+w+1]; 465 word[w].next = &base, word[w].prev = &word[w-1]; 466 word[w-1].next = &word[w], base.prev = &word[w]; 467 } 468 } 469 /* w is the index of the last word actually created. */ 470 number[nChars + w] = 0; /* terminate last word */ 471 unsigned timeLimit = !samples; 472 if (samples == 0) 473 samples = 1000000000; 474 doTiming(1); 475 double sec; 476 for (i = 0; i < samples; i++) { 477 /* increment 4 digit base 255 number; last characters vary fastest */ 478 unsigned j = nChars-1 + w; 479 while (1) { 480 if (++number[j] != 0) 481 break; 482 /* else reset this digit and proceed to next one */ 483 number[j] = 1; 484 if (&number[j] <= word[w].word) 485 break; /* stop at beginning of last word */ 486 j--; 487 } 488 if (word[w].word[0] == '\n') 489 word[w].word[0]++; /* suppress newline character */ 490 unsigned hash = hashhist(&base); 491 hashes[hash2tableIndex(hash, length)]++; 492 if (timeLimit && (i & 0x3ffff) == 0x3ffff) { 493 sec = doTiming(0); 494 if (sec > 10) 495 break; 496 } 497 } 498 if (i >= samples) 499 sec = doTiming(0); 500 else 501 samples = i; /* number we actually did */ 502 if (sec > 0.01) { 503 xprintf("Hash %d (%d Char %u words) with %s: %d nsec/hash, %d mcps\n", 504 samples, nChars, w+1, hashFcnName, (int)((sec/samples)*1e9), 505 (int)((double)samples*nChars/sec/1e6)); 506 } 507 } 508 #endif /* DEBUG_HIST */ 509 510 #ifdef DEBUG_HIST 511 static void 512 testHash(void) 513 { 514 static const Char STRtestHashTimings[] = 515 { 't','e','s','t','H','a','s','h','T','i','m','i','n','g','s', 0 }; 516 struct varent *vp = adrof(STRtestHashTimings); 517 if (vp && vp->vec) { 518 unsigned hashes[4]; /* dummy place to put hashes */ 519 Char **vals = vp->vec; 520 while (*vals) { 521 int length = getn(*vals); 522 unsigned words = 523 (length < 5) ? 1 : (length < 25) ? 2 : (length < 75) ? 3 : 4; 524 if (length > 0) 525 generateHashes(length, words, 0, hashes, 4); 526 vals++; 527 } 528 } 529 unsigned length = 1024; 530 #ifdef PRIME_LENGTH /* need good HTL */ 531 length = 1021; 532 #endif 533 unsigned *hashes = xmalloc(length*sizeof(unsigned)); 534 memset(hashes, 0, length*sizeof(unsigned)); 535 /* Compute collision statistics for half full hashes modulo "length". */ 536 generateHashes(4, 1, length/2, hashes, length); 537 /* Evaluate collisions by comparing occupancy rates (mean value 0.5). 538 * One bin for each number of hits. */ 539 unsigned bins[155]; 540 memset(bins, 0, sizeof(bins)); 541 unsigned highest = 0; 542 unsigned i; 543 for (i = 0; i<length; i++) { 544 unsigned hits = hashes[i]; 545 if (hits >= sizeof(bins)/sizeof(bins[0])) /* clip */ 546 hits = highest = sizeof(bins)/sizeof(bins[0]) - 1; 547 if (hits > highest) 548 highest = hits; 549 bins[hits]++; 550 } 551 xprintf("Occupancy of %d buckets by %d hashes %d Chars %d word with %s\n", 552 length, length/2, 4, 1, hashFcnName); 553 for (i = 0; i <= highest; i++) { 554 xprintf(" %d buckets (%d%%) with %d hits\n", 555 bins[i], bins[i]*100/length, i); 556 } 557 /* Count run lengths to evaluate linear rehashing effectiveness. Estimate 558 * a little corrupted by edge effects. */ 559 memset(bins, 0, sizeof(bins)); 560 highest = 0; 561 for (i = 0; hashes[i] == 0; i++); /* find first occupied bucket */ 562 unsigned run = 0; 563 unsigned rehashed = 0; 564 for (; i<length; i++) { 565 unsigned hits = hashes[i]; 566 if (hits == 0 && rehashed > 0) 567 hits = 1 && rehashed--; 568 else if (hits > 1) 569 rehashed += hits-1; 570 if (hits) 571 run++; 572 else { 573 /* a real free slot, count it */ 574 if (run >= sizeof(bins)/sizeof(bins[0])) /* clip */ 575 run = highest = sizeof(bins)/sizeof(bins[0]) - 1; 576 if (run > highest) 577 highest = run; 578 bins[run]++; 579 run = 0; 580 } 581 } 582 /* Ignore the partial run at end as we ignored the beginning. */ 583 double merit = 0.0, entries = 0; 584 for (i = 0; i <= highest; i++) { 585 entries += bins[i]*i; /* total hashed objects */ 586 merit += bins[i]*i*i; 587 } 588 xprintf("Rehash collision figure of merit %u (ideal=100), run lengths:\n", 589 (int)(100.0*merit/entries)); 590 for (i = 0; i <= highest; i++) { 591 if (bins[i] != 0) 592 xprintf(" %d runs of length %d buckets\n", bins[i], i); 593 } 594 xfree(hashes); 595 } 596 #endif /* DEBUG_HIST */ 597 598 /* Compares two word lists for equality. */ 599 static int 600 heq(const struct wordent *a0, const struct wordent *b0) 601 { 602 const struct wordent *a = a0->next, *b = b0->next; 603 604 for (;;) { 605 if (Strcmp(a->word, b->word) != 0) 606 return 0; 607 a = a->next; 608 b = b->next; 609 if (a == a0) 610 return (b == b0) ? 1 : 0; 611 if (b == b0) 612 return 0; 613 } 614 } 615 616 /* Renumber entries following p, which we will be deleting. */ 617 PG_STATIC void 618 renumberHist(struct Hist *p) 619 { 620 int n = p->Href; 621 while ((p = p->Hnext)) 622 p->Href = n--; 623 } 624 625 /* The hash table is implemented as an array of pointers to Hist entries. Each 626 * entry is located in the table using hash2tableIndex() and checking the 627 * following entries in case of a collision (linear rehash). Free entries in 628 * the table are zero (0, NULL, emptyHTE). Deleted entries that cannot yet be 629 * freed are set to one (deletedHTE). The Hist.Hhash member is non-zero iff 630 * the entry is in the hash table. When the hash table get too full, it is 631 * reallocated to be approximately twice the history length (see 632 * getHashTableSize). */ 633 static struct Hist **histHashTable = NULL; 634 static unsigned histHashTableLength = 0; /* number of Hist pointers in table */ 635 636 static struct Hist * const emptyHTE = NULL; 637 static struct Hist * const deletedHTE = (struct Hist *)1; 638 639 static struct { 640 unsigned insertCount; 641 unsigned removeCount; 642 unsigned rehashes; 643 int deleted; 644 } hashStats; 645 646 #ifdef DEBUG_HIST 647 void 648 checkHistHashTable(int print) 649 { 650 unsigned occupied = 0; 651 unsigned deleted = 0; 652 unsigned i; 653 for (i = 0; i<histHashTableLength; i++) 654 if (histHashTable[i] == emptyHTE) 655 continue; 656 else if (histHashTable[i] == deletedHTE) 657 deleted++; 658 else 659 occupied++; 660 if (print) 661 xprintf(" found len %u occupied %u deleted %u\n", 662 histHashTableLength, occupied, deleted); 663 assert(deleted == hashStats.deleted); 664 } 665 666 static int doneTest = 0; 667 668 /* Main entry point for displaying history statistics and hash function 669 * behavior. */ 670 void 671 displayHistStats(const char *reason) 672 { 673 /* Just hash statistics for now. */ 674 xprintf("%s history hash table len %u count %u (deleted %d)\n", reason, 675 histHashTableLength, histCount, hashStats.deleted); 676 xprintf(" inserts %u rehashes %u%% each\n", 677 hashStats.insertCount, 678 (hashStats.insertCount 679 ? 100*hashStats.rehashes/hashStats.insertCount : 0)); 680 xprintf(" removes %u net %u\n", 681 hashStats.removeCount, 682 hashStats.insertCount - hashStats.removeCount); 683 assert(hashStats.insertCount >= hashStats.removeCount); 684 checkHistHashTable(1); 685 memset(&hashStats, 0, sizeof(hashStats)); 686 if (!doneTest) { 687 testHash(); 688 doneTest = 1; 689 } 690 } 691 #else 692 void 693 displayHistStats(const char *reason) 694 { 695 USE(reason); 696 } 697 #endif 698 699 static void 700 discardHistHashTable(void) 701 { 702 if (histHashTable == NULL) 703 return; 704 displayHistStats("Discarding"); 705 xfree(histHashTable); 706 histHashTable = NULL; 707 } 708 709 /* Computes a new hash table size, when the current one is too small. */ 710 static unsigned 711 getHashTableSize(int hlen) 712 { 713 unsigned target = hlen * 2; 714 unsigned e = 5; 715 unsigned size; 716 while ((size = 1<<e) < target) 717 e++; 718 #ifdef PRIME_LENGTH /* need good HTL */ 719 /* Not all prime, but most are and none have factors smaller than 11. */ 720 return size+15; 721 #else 722 assert((size & (size-1)) == 0); /* must be a power of two */ 723 return size; 724 #endif 725 } 726 727 /* Create the hash table or resize, if necessary. */ 728 static void 729 createHistHashTable(int hlen) 730 { 731 if (hlen == 0) { 732 discardHistHashTable(); 733 return; 734 } 735 if (hlen < 0) { 736 if (histlen <= 0) 737 return; /* no need for hash table */ 738 hlen = histlen; 739 } 740 if (histHashTable != NULL) { 741 if (histCount < histHashTableLength * 3 / 4) 742 return; /* good enough for now */ 743 discardHistHashTable(); /* too small */ 744 } 745 histHashTableLength = getHashTableSize( 746 hlen > (int)histCount ? hlen : (int)histCount); 747 histHashTable = xmalloc(histHashTableLength * sizeof(struct Hist *)); 748 memset(histHashTable, 0, histHashTableLength * sizeof(struct Hist *)); 749 assert(histHashTable[0] == emptyHTE); 750 751 /* Now insert all the entries on the history list into the hash table. */ 752 { 753 struct Hist *hp; 754 for (hp = &Histlist; (hp = hp->Hnext) != NULL;) { 755 unsigned lpHash = hashhist(&hp->Hlex); 756 assert(!hp->Hhash || hp->Hhash == lpHash); 757 hp->Hhash = 0; /* force insert to new hash table */ 758 insertHistHashTable(hp, lpHash); 759 } 760 } 761 } 762 763 /* Insert np into the hash table. We assume that np is already on the 764 * Histlist. The specified hashval matches the new Hist entry but has not yet 765 * been assigned to Hhash (or the element is already on the hash table). */ 766 static void 767 insertHistHashTable(struct Hist *np, unsigned hashval) 768 { 769 unsigned rehashes = 0; 770 unsigned hi = 0; 771 if (!histHashTable) 772 return; 773 if (np->Hhash != 0) { 774 /* already in hash table */ 775 assert(hashval == np->Hhash); 776 return; 777 } 778 assert(np != deletedHTE); 779 /* Find a free (empty or deleted) slot, using linear rehash. */ 780 assert(histHashTable); 781 for (rehashes = 0; 782 ((hi = hash2tableIndex(hashval + rehashes, histHashTableLength)), 783 histHashTable[hi] != emptyHTE && histHashTable[hi] != deletedHTE); 784 rehashes++) { 785 assert(np != histHashTable[hi]); 786 if (rehashes >= histHashTableLength / 10) { 787 /* Hash table is full, so grow it. We assume the create function 788 * will roughly double the size we give it. Create initializes the 789 * new table with everything on the Histlist, so we are done when 790 * it returns. */ 791 #ifdef DEBUG_HIST 792 xprintf("Growing history hash table from %d ...", 793 histHashTableLength); 794 flush(); 795 #endif 796 discardHistHashTable(); 797 createHistHashTable(histHashTableLength); 798 #ifdef DEBUG_HIST 799 xprintf("to %d.\n", histHashTableLength); 800 #endif 801 return; 802 } 803 } 804 /* Might be sensible to grow hash table if rehashes is "too big" here. */ 805 if (histHashTable[hi] == deletedHTE) 806 hashStats.deleted--; 807 histHashTable[hi] = np; 808 np->Hhash = hashval; 809 hashStats.insertCount++; 810 hashStats.rehashes += rehashes; 811 } 812 813 /* Remove the 'np' entry from the hash table. */ 814 static void 815 removeHistHashTable(struct Hist *np) 816 { 817 unsigned hi = np->Hhash; 818 if (!histHashTable || !hi) 819 return; /* no hash table or not on it */ 820 /* find desired entry */ 821 while ((hi = hash2tableIndex(hi, histHashTableLength)), 822 histHashTable[hi] != emptyHTE) { 823 if (np == histHashTable[hi]) { 824 unsigned i; 825 unsigned deletes = 0; 826 histHashTable[hi] = deletedHTE; /* dummy, but non-zero entry */ 827 /* now peek ahead to see if the dummies are really necessary. */ 828 i = 1; 829 while (histHashTable[hash2tableIndex(hi+i, histHashTableLength)] == 830 deletedHTE) 831 i++; 832 if (histHashTable[hash2tableIndex(hi+i, histHashTableLength)] == 833 emptyHTE) { 834 /* dummies are no longer necessary placeholders. */ 835 deletes = i; 836 while (i-- > 0) { 837 histHashTable[hash2tableIndex(hi+i, histHashTableLength)] = 838 emptyHTE; 839 } 840 } 841 hashStats.deleted += 1 - deletes; /* delta deleted entries */ 842 hashStats.removeCount++; 843 return; 844 } 845 hi++; /* linear rehash */ 846 } 847 assert(!"Hist entry not found in hash table"); 848 } 849 850 /* Search the history hash table for a command matching lp, using hashval as 851 * its hash value. */ 852 static struct Hist * 853 findHistHashTable(struct wordent *lp, unsigned hashval) 854 { 855 unsigned deleted = 0; /* number of deleted entries skipped */ 856 unsigned hi = hashval; 857 struct Hist *hp; 858 if (!histHashTable) 859 return NULL; 860 while ((hi = hash2tableIndex(hi, histHashTableLength)), 861 (hp = histHashTable[hi]) != emptyHTE) { 862 if (hp == deletedHTE) 863 deleted++; 864 else if (hp->Hhash == hashval && heq(lp, &(hp->Hlex))) 865 return hp; 866 if (deleted > (histHashTableLength>>4)) { 867 /* lots of deletes, so we need a sparser table. */ 868 discardHistHashTable(); 869 createHistHashTable(histHashTableLength); 870 return findHistHashTable(lp, hashval); 871 } 872 hi++; /* linear rehash */ 873 } 874 return NULL; 875 } 876 877 /* When merge semantics are in use, find the approximate predecessor for the 878 * new entry, so that the Htime entries are decreasing. Return the entry just 879 * before the first entry with equal times, so the caller can check for 880 * duplicates. When pTime is not NULL, use it as a starting point for search, 881 * otherwise search from beginning (largest time value) of history list. */ 882 PG_STATIC struct Hist * 883 mergeInsertionPoint( 884 struct Hist *np, /* new entry to be inserted */ 885 struct Hist *pTime) /* hint about where to insert */ 886 { 887 struct Hist *pp, *p; 888 if (histTail && histTail->Htime >= np->Htime) 889 pTime = histTail; /* new entry goes at the end */ 890 if (histMerg && histMerg != &Histlist && histMerg != Histlist.Hnext) { 891 /* Check above and below previous insertion point, in case we're adding 892 * sequential times in the middle of the list (e.g. history -M). */ 893 if (histMerg->Htime >= np->Htime) 894 pTime = histMerg; 895 else if (histMerg->Hprev->Htime >= np->Htime) 896 pTime = histMerg->Hprev; 897 } 898 if (pTime) { 899 /* With hint, search up the list until Htime is greater. We skip past 900 * the equal ones, too, so our caller can elide duplicates. */ 901 pp = pTime; 902 while (pp != &Histlist && pp->Htime <= np->Htime) 903 pp = pp->Hprev; 904 } else 905 pp = &Histlist; 906 /* Search down the list while current entry's time is too large. */ 907 while ((p = pp->Hnext) && (p->Htime > np->Htime)) 908 pp = p; /* advance insertion point */ 909 /* Remember recent position as hint for next time */ 910 histMerg = pp; 911 return pp; 912 } 913 914 /* Bubble Hnum & Href in new entry down to pp through earlier part of list. */ 915 PG_STATIC void bubbleHnumHrefDown(struct Hist *np, struct Hist *pp) 916 { 917 struct Hist *p; 918 for (p = Histlist.Hnext; p != pp->Hnext; p = p->Hnext) { 919 /* swap Hnum & Href values of p and np. */ 920 int n = p->Hnum, r = p->Href; 921 p->Hnum = np->Hnum; p->Href = np->Href; 922 np->Hnum = n; np->Href = r; 923 } 924 } 925 926 /* Enter new command into the history list according to current settings. */ 927 struct Hist * 928 enthist( 929 int event, /* newly incremented global eventno */ 930 struct wordent *lp, 931 int docopy, 932 int mflg, /* true if merge requested */ 933 int hlen) /* -1 if unknown */ 934 { 935 struct Hist *p = NULL, *pp = &Histlist, *pTime = NULL; 936 struct Hist *np; 937 const Char *dp; 938 unsigned lpHash = 0; /* non-zero if hashing entries */ 939 940 if ((dp = varval(STRhistdup)) != STRNULL) { 941 if (eq(dp, STRerase)) { 942 /* masaoki@akebono.tky.hp.com (Kobayashi Masaoki) */ 943 createHistHashTable(hlen); 944 lpHash = hashhist(lp); 945 assert(lpHash != 0); 946 p = findHistHashTable(lp, lpHash); 947 if (p) { 948 if (Htime != 0 && p->Htime > Htime) 949 Htime = p->Htime; 950 /* If we are merging, and the old entry is at the place we want 951 * to insert the new entry, then remember the place. */ 952 if (mflg && Htime != 0 && p->Hprev->Htime >= Htime) 953 pTime = p->Hprev; 954 if (!fastMergeErase) 955 renumberHist(p); /* Reset Href of subsequent entries */ 956 hremove(p); 957 hfree(p); 958 p = NULL; /* so new entry is allocated below */ 959 } 960 } 961 else if (eq(dp, STRall)) { 962 createHistHashTable(hlen); 963 lpHash = hashhist(lp); 964 assert(lpHash != 0); 965 p = findHistHashTable(lp, lpHash); 966 if (p) /* p!=NULL, only update this entry's Htime below */ 967 eventno--; /* not adding a new event */ 968 } 969 else if (eq(dp, STRprev)) { 970 if (pp->Hnext && heq(lp, &(pp->Hnext->Hlex))) { 971 p = pp->Hnext; 972 eventno--; 973 } 974 } 975 } 976 977 np = p ? p : xmalloc(sizeof(*np)); 978 979 /* Pick up timestamp set by lex() in Htime if reading saved history */ 980 if (Htime != 0) { 981 np->Htime = Htime; 982 Htime = 0; 983 } 984 else 985 (void) time(&(np->Htime)); 986 987 if (p == np) 988 return np; /* reused existing entry */ 989 990 /* Initialize the new entry. */ 991 np->Hnum = np->Href = event; 992 if (docopy) { 993 copylex(&np->Hlex, lp); 994 if (histvalid) 995 np->histline = Strsave(histline.s); 996 else 997 np->histline = NULL; 998 } 999 else { 1000 np->Hlex.next = lp->next; 1001 lp->next->prev = &np->Hlex; 1002 np->Hlex.prev = lp->prev; 1003 lp->prev->next = &np->Hlex; 1004 np->histline = NULL; 1005 } 1006 np->Hhash = 0; 1007 1008 /* The head of history list is the default insertion point. 1009 If merging, advance insertion point, in pp, according to Htime. */ 1010 /* XXX -- In histdup=all, Htime values can be non-monotonic. */ 1011 if (mflg) { /* merge according to np->Htime */ 1012 pp = mergeInsertionPoint(np, pTime); 1013 for (p = pp->Hnext; p && p->Htime == np->Htime; pp = p, p = p->Hnext) { 1014 if (heq(&p->Hlex, &np->Hlex)) { 1015 eventno--; /* duplicate, so don't add new event */ 1016 hfree(np); 1017 return (p); 1018 } 1019 } 1020 /* pp is now the last entry with time >= to np. */ 1021 if (!fastMergeErase) { /* renumber at end of loadhist */ 1022 /* Before inserting np after pp, bubble its Hnum & Href values down 1023 * through the earlier part of list. */ 1024 bubbleHnumHrefDown(np, pp); 1025 } 1026 } 1027 else 1028 pp = &Histlist; /* insert at beginning of history */ 1029 hinsert(np, pp); 1030 if (lpHash && hlen != 0) /* erase & all modes use hash table */ 1031 insertHistHashTable(np, lpHash); 1032 else 1033 discardHistHashTable(); 1034 return (np); 1035 } 1036 1037 static void 1038 hfree(struct Hist *hp) 1039 { 1040 assert(hp != histMerg); 1041 if (hp->Hhash) 1042 removeHistHashTable(hp); 1043 freelex(&hp->Hlex); 1044 if (hp->histline) 1045 xfree(hp->histline); 1046 xfree(hp); 1047 } 1048 1049 PG_STATIC void 1050 phist(struct Hist *hp, int hflg) 1051 { 1052 if (hp->Href < 0) 1053 return; 1054 if (hflg & HIST_ONLY) { 1055 int old_output_raw; 1056 1057 /* 1058 * Control characters have to be written as is (output_raw). 1059 * This way one can preserve special characters (like tab) in 1060 * the history file. 1061 * From: mveksler@vnet.ibm.com (Veksler Michael) 1062 */ 1063 old_output_raw = output_raw; 1064 output_raw = 1; 1065 cleanup_push(&old_output_raw, output_raw_restore); 1066 if (hflg & HIST_TIME) 1067 /* 1068 * Make file entry with history time in format: 1069 * "+NNNNNNNNNN" (10 digits, left padded with ascii '0') 1070 */ 1071 1072 xprintf("#+%010lu\n", (unsigned long)hp->Htime); 1073 1074 if (HistLit && hp->histline) 1075 xprintf("%S\n", hp->histline); 1076 else 1077 prlex(&hp->Hlex); 1078 cleanup_until(&old_output_raw); 1079 } 1080 else { 1081 Char *cp = str2short("%h\t%T\t%R\n"); 1082 Char *p; 1083 struct varent *vp = adrof(STRhistory); 1084 1085 if (vp && vp->vec != NULL && vp->vec[0] && vp->vec[1]) 1086 cp = vp->vec[1]; 1087 1088 p = tprintf(FMT_HISTORY, cp, NULL, hp->Htime, hp); 1089 cleanup_push(p, xfree); 1090 for (cp = p; *cp;) 1091 xputwchar(*cp++); 1092 cleanup_until(p); 1093 } 1094 } 1095 1096 PG_STATIC void 1097 dophist(int n, int hflg) 1098 { 1099 struct Hist *hp; 1100 if (setintr) { 1101 int old_pintr_disabled; 1102 1103 pintr_push_enable(&old_pintr_disabled); 1104 cleanup_until(&old_pintr_disabled); 1105 } 1106 if ((hflg & HIST_REV) == 0) { 1107 /* Since the history list is stored most recent first, non-reversing 1108 * print needs to print (backwards) up the list. */ 1109 if ((unsigned)n >= histCount) 1110 hp = histTail; 1111 else { 1112 for (hp = Histlist.Hnext; 1113 --n > 0 && hp->Hnext != NULL; 1114 hp = hp->Hnext) 1115 ; 1116 } 1117 if (hp == NULL) 1118 return; /* nothing to print */ 1119 for (; hp != &Histlist; hp = hp->Hprev) 1120 phist(hp, hflg); 1121 } else { 1122 for (hp = Histlist.Hnext; n-- > 0 && hp != NULL; hp = hp->Hnext) 1123 phist(hp, hflg); 1124 } 1125 } 1126 1127 /*ARGSUSED*/ 1128 void 1129 dohist(Char **vp, struct command *c) 1130 { 1131 int n, hflg = 0; 1132 1133 USE(c); 1134 if (getn(varval(STRhistory)) == 0) 1135 return; 1136 while (*++vp && **vp == '-') { 1137 Char *vp2 = *vp; 1138 1139 while (*++vp2) 1140 switch (*vp2) { 1141 case 'c': 1142 hflg |= HIST_CLEAR; 1143 break; 1144 case 'h': 1145 hflg |= HIST_ONLY; 1146 break; 1147 case 'r': 1148 hflg |= HIST_REV; 1149 break; 1150 case 'S': 1151 hflg |= HIST_SAVE; 1152 break; 1153 case 'L': 1154 hflg |= HIST_LOAD; 1155 break; 1156 case 'M': 1157 hflg |= HIST_MERGE; 1158 break; 1159 case 'T': 1160 hflg |= HIST_TIME; 1161 break; 1162 default: 1163 stderror(ERR_HISTUS, "chrSLMT"); 1164 break; 1165 } 1166 } 1167 if (hflg & HIST_CLEAR) { 1168 struct Hist *np, *hp; 1169 for (hp = &Histlist; (np = hp->Hnext) != NULL;) 1170 hremove(np), hfree(np); 1171 } 1172 1173 if (hflg & (HIST_LOAD | HIST_MERGE)) 1174 loadhist(*vp, (hflg & HIST_MERGE) ? 1 : 0); 1175 else if (hflg & HIST_SAVE) 1176 rechist(*vp, 1); 1177 else { 1178 if (*vp) 1179 n = getn(*vp); 1180 else { 1181 n = getn(varval(STRhistory)); 1182 } 1183 dophist(n, hflg); 1184 } 1185 } 1186 1187 1188 char * 1189 fmthist(int fmt, ptr_t ptr) 1190 { 1191 struct Hist *hp = ptr; 1192 char *buf; 1193 1194 switch (fmt) { 1195 case 'h': 1196 return xasprintf("%6d", hp->Hnum); 1197 case 'R': 1198 if (HistLit && hp->histline) 1199 return xasprintf("%S", hp->histline); 1200 else { 1201 Char *istr, *ip; 1202 char *p; 1203 1204 istr = sprlex(&hp->Hlex); 1205 buf = xmalloc(Strlen(istr) * MB_LEN_MAX + 1); 1206 1207 for (p = buf, ip = istr; *ip != '\0'; ip++) 1208 p += one_wctomb(p, *ip); 1209 1210 *p = '\0'; 1211 xfree(istr); 1212 return buf; 1213 } 1214 default: 1215 buf = xmalloc(1); 1216 buf[0] = '\0'; 1217 return buf; 1218 } 1219 } 1220 1221 static void 1222 dotlock_cleanup(void* lockpath) 1223 { 1224 dot_unlock((char*)lockpath); 1225 } 1226 1227 /* Save history before exiting the shell. */ 1228 void 1229 rechist(Char *xfname, int ref) 1230 { 1231 Char *snum, *rs; 1232 int fp, ftmp, oldidfds, ophup_disabled; 1233 struct varent *shist; 1234 char path[MAXPATHLEN]; 1235 struct stat st; 1236 static Char *fname; 1237 static Char *dumphist[] = {STRhistory, STRmhT, 0, 0}; 1238 1239 if (fname == NULL && !ref) 1240 return; 1241 1242 fname = xfname; 1243 ophup_disabled = phup_disabled; 1244 phup_disabled = 1; 1245 1246 /* 1247 * If $savehist is just set, we use the value of $history 1248 * else we use the value in $savehist 1249 */ 1250 if (((snum = varval(STRsavehist)) == STRNULL) && 1251 ((snum = varval(STRhistory)) == STRNULL)) 1252 snum = STRmaxint; 1253 1254 1255 if (fname == NULL) { 1256 if ((fname = varval(STRhistfile)) == STRNULL) 1257 fname = Strspl(varval(STRhome), &STRtildothist[1]); 1258 else 1259 fname = Strsave(fname); 1260 } 1261 else 1262 fname = globone(fname, G_ERROR); 1263 cleanup_push(fname, xfree); 1264 1265 /* 1266 * The 'savehist merge' feature is intended for an environment 1267 * with numerous shells being in simultaneous use. Imagine 1268 * any kind of window system. All these shells 'share' the same 1269 * ~/.history file for recording their command line history. 1270 * We try to handle the case of multiple shells trying to merge 1271 * histories at the same time, by creating semi-unique filenames 1272 * and saving the history there first and then trying to rename 1273 * them in the proper history file. 1274 * 1275 * Users that like to nuke their environment require here an atomic 1276 * loadhist-creat-dohist(dumphist)-close sequence which is given 1277 * by optional lock parameter to savehist. 1278 * 1279 * jw. 1280 */ 1281 /* 1282 * We need the didfds stuff before loadhist otherwise 1283 * exec in a script will fail to print if merge is set. 1284 * From: mveksler@iil.intel.com (Veksler Michael) 1285 */ 1286 oldidfds = didfds; 1287 didfds = 0; 1288 if ((shist = adrof(STRsavehist)) != NULL && shist->vec != NULL) { 1289 size_t i; 1290 int merge = 0, lock = 0; 1291 1292 for (i = 1; shist->vec[i]; i++) { 1293 if (eq(shist->vec[i], STRmerge)) 1294 merge++; 1295 if (eq(shist->vec[i], STRlock)) 1296 lock++; 1297 } 1298 1299 if (merge) { 1300 jmp_buf_t osetexit; 1301 if (lock) { 1302 #ifndef WINNT_NATIVE 1303 char *lockpath = strsave(short2str(fname)); 1304 cleanup_push(lockpath, xfree); 1305 /* Poll in 100 miliseconds interval to obtain the lock. */ 1306 if ((dot_lock(lockpath, 100) == 0)) 1307 cleanup_push(lockpath, dotlock_cleanup); 1308 #endif 1309 } 1310 getexit(osetexit); 1311 if (setexit() == 0) 1312 loadhist(fname, 1); 1313 resexit(osetexit); 1314 } 1315 } 1316 rs = randsuf(); 1317 xsnprintf(path, sizeof(path), "%S.%S", fname, rs); 1318 xfree(rs); 1319 1320 fp = xcreat(path, 0600); 1321 if (fp == -1) { 1322 didfds = oldidfds; 1323 cleanup_until(fname); 1324 phup_disabled = ophup_disabled; 1325 return; 1326 } 1327 /* Try to preserve ownership and permissions of the original history file */ 1328 #ifndef WINNT_NATIVE 1329 if (stat(short2str(fname), &st) != -1) { 1330 TCSH_IGNORE(fchown(fp, st.st_uid, st.st_gid)); 1331 TCSH_IGNORE(fchmod(fp, st.st_mode)); 1332 } 1333 #else 1334 UNREFERENCED_PARAMETER(st); 1335 #endif 1336 ftmp = SHOUT; 1337 SHOUT = fp; 1338 dumphist[2] = snum; 1339 dohist(dumphist, NULL); 1340 xclose(fp); 1341 SHOUT = ftmp; 1342 didfds = oldidfds; 1343 #ifndef WINNT_NATIVE 1344 (void)rename(path, short2str(fname)); 1345 #else 1346 (void)ReplaceFile(short2str(fname), path, NULL, 0, NULL, NULL); 1347 #endif 1348 cleanup_until(fname); 1349 phup_disabled = ophup_disabled; 1350 } 1351 1352 1353 /* This is the entry point for loading history data from a file. */ 1354 void 1355 loadhist(Char *fname, int mflg) 1356 { 1357 static Char *loadhist_cmd[] = {STRsource, NULL, NULL, NULL}; 1358 loadhist_cmd[1] = mflg ? STRmm : STRmh; 1359 1360 if (fname != NULL) 1361 loadhist_cmd[2] = fname; 1362 else if ((fname = varval(STRhistfile)) != STRNULL) 1363 loadhist_cmd[2] = fname; 1364 else 1365 loadhist_cmd[2] = STRtildothist; 1366 1367 dosource(loadhist_cmd, NULL); 1368 1369 /* During history merging (enthist sees mflg set), we disable management of 1370 * Hnum and Href (because fastMergeErase is true). So now reset all the 1371 * values based on the final ordering of the history list. */ 1372 if (mflg) { 1373 int n = eventno; 1374 struct Hist *hp = &Histlist; 1375 while ((hp = hp->Hnext)) 1376 hp->Hnum = hp->Href = n--; 1377 } 1378 } 1379 1380 void 1381 sethistory(int n) 1382 { 1383 histlen = n; 1384 discardExcess(histlen); 1385 } 1386