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