1*76748ca4Srillig /* $NetBSD: history.c,v 1.20 2024/09/08 17:28:36 rillig Exp $ */ 22ab2e20cStls 3e1b2664cSjtc /* 4e1b2664cSjtc * command history 5e1b2664cSjtc * 6e1b2664cSjtc * only implements in-memory history. 7e1b2664cSjtc */ 8e1b2664cSjtc 9e1b2664cSjtc /* 10e1b2664cSjtc * This file contains 11e1b2664cSjtc * a) the original in-memory history mechanism 12e1b2664cSjtc * b) a simple file saving history mechanism done by sjg@zen 13e1b2664cSjtc * define EASY_HISTORY to get this 14e1b2664cSjtc * c) a more complicated mechanism done by pc@hillside.co.uk 15e1b2664cSjtc * that more closely follows the real ksh way of doing 16e1b2664cSjtc * things. You need to have the mmap system call for this 17e1b2664cSjtc * to work on your system 18e1b2664cSjtc */ 196377cac7Sagc #include <sys/cdefs.h> 206377cac7Sagc 216377cac7Sagc #ifndef lint 22*76748ca4Srillig __RCSID("$NetBSD: history.c,v 1.20 2024/09/08 17:28:36 rillig Exp $"); 236377cac7Sagc #endif 246377cac7Sagc 25fac4b394Skamil #include <sys/stat.h> 26e1b2664cSjtc 27e1b2664cSjtc #include "sh.h" 28e1b2664cSjtc 29e1b2664cSjtc #ifdef HISTORY 30e1b2664cSjtc # ifdef EASY_HISTORY 31e1b2664cSjtc 32e1b2664cSjtc # ifndef HISTFILE 3348ee8d12Shubertf # define HISTFILE ".pdksh_history" 34e1b2664cSjtc # endif 35e1b2664cSjtc 36e1b2664cSjtc # else 37e1b2664cSjtc /* Defines and includes for the complicated case */ 38e1b2664cSjtc 39e1b2664cSjtc # include <sys/file.h> 40e1b2664cSjtc # include <sys/mman.h> 41e1b2664cSjtc 42e1b2664cSjtc /* 43e1b2664cSjtc * variables for handling the data file 44e1b2664cSjtc */ 45e1b2664cSjtc static int histfd; 46e1b2664cSjtc static int hsize; 47e1b2664cSjtc 48e1b2664cSjtc static int hist_count_lines ARGS((unsigned char *, int)); 49e1b2664cSjtc static int hist_shrink ARGS((unsigned char *, int)); 50e1b2664cSjtc static unsigned char *hist_skip_back ARGS((unsigned char *,int *,int)); 51e1b2664cSjtc static void histload ARGS((Source *, unsigned char *, int)); 52e1b2664cSjtc static void histinsert ARGS((Source *, int, unsigned char *)); 53e1b2664cSjtc static void writehistfile ARGS((int, char *)); 54e1b2664cSjtc static int sprinkle ARGS((int)); 55e1b2664cSjtc 56e1b2664cSjtc # ifdef MAP_FILE 57e1b2664cSjtc # define MAP_FLAGS (MAP_FILE|MAP_PRIVATE) 58e1b2664cSjtc # else 59e1b2664cSjtc # define MAP_FLAGS MAP_PRIVATE 60e1b2664cSjtc # endif 61e1b2664cSjtc 62e1b2664cSjtc # endif /* of EASY_HISTORY */ 63e1b2664cSjtc 64a397ec1fSchristos static int hist_execute ARGS((char *)); 65a397ec1fSchristos static int hist_replace ARGS((char **, const char *, const char *, int)); 66a397ec1fSchristos static char **hist_get ARGS((const char *, int, int)); 67a397ec1fSchristos static char **hist_get_newest ARGS((int)); 6890647eb8Schristos static char **hist_get_oldest ARGS((void)); 69e1b2664cSjtc static void histbackup ARGS((void)); 70e1b2664cSjtc 71f662a744Smycroft static char **current; /* current position in history[] */ 72e1b2664cSjtc static int curpos; /* current index in history[] */ 73e1b2664cSjtc static char *hname; /* current name of history file */ 74e1b2664cSjtc static int hstarted; /* set after hist_init() called */ 75e1b2664cSjtc static Source *hist_source; 76e1b2664cSjtc 77e1b2664cSjtc 78e1b2664cSjtc int 79e1b2664cSjtc c_fc(wp) 80e1b2664cSjtc char **wp; 81e1b2664cSjtc { 82e1b2664cSjtc struct shf *shf; 83e1b2664cSjtc struct temp UNINITIALIZED(*tf); 84e1b2664cSjtc char *p, *editor = (char *) 0; 85e1b2664cSjtc int gflag = 0, lflag = 0, nflag = 0, sflag = 0, rflag = 0; 86e1b2664cSjtc int optc; 87e1b2664cSjtc char *first = (char *) 0, *last = (char *) 0; 88e1b2664cSjtc char **hfirst, **hlast, **hp; 89e1b2664cSjtc 90f6f36c8dSchristos if (hist_source == NULL) { 91f6f36c8dSchristos bi_errorf("not interactive"); 92f6f36c8dSchristos return 1; 93f6f36c8dSchristos } 94f6f36c8dSchristos 95e1b2664cSjtc while ((optc = ksh_getopt(wp, &builtin_opt, "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != EOF) 96e1b2664cSjtc switch (optc) { 97e1b2664cSjtc case 'e': 98e1b2664cSjtc p = builtin_opt.optarg; 99e1b2664cSjtc if (strcmp(p, "-") == 0) 100e1b2664cSjtc sflag++; 101e1b2664cSjtc else { 102f662a744Smycroft size_t len = strlen(p) + 4; 103f662a744Smycroft editor = str_nsave(p, len, ATEMP); 104f662a744Smycroft strlcat(editor, " $_", len); 105e1b2664cSjtc } 106e1b2664cSjtc break; 107e1b2664cSjtc case 'g': /* non-at&t ksh */ 108e1b2664cSjtc gflag++; 109e1b2664cSjtc break; 110e1b2664cSjtc case 'l': 111e1b2664cSjtc lflag++; 112e1b2664cSjtc break; 113e1b2664cSjtc case 'n': 114e1b2664cSjtc nflag++; 115e1b2664cSjtc break; 116e1b2664cSjtc case 'r': 117e1b2664cSjtc rflag++; 118e1b2664cSjtc break; 119e1b2664cSjtc case 's': /* posix version of -e - */ 120e1b2664cSjtc sflag++; 121e1b2664cSjtc break; 122e1b2664cSjtc /* kludge city - accept -num as -- -num (kind of) */ 123e1b2664cSjtc case '0': case '1': case '2': case '3': case '4': 124e1b2664cSjtc case '5': case '6': case '7': case '8': case '9': 125e1b2664cSjtc p = shf_smprintf("-%c%s", 126e1b2664cSjtc optc, builtin_opt.optarg); 127e1b2664cSjtc if (!first) 128e1b2664cSjtc first = p; 129e1b2664cSjtc else if (!last) 130e1b2664cSjtc last = p; 131e1b2664cSjtc else { 132e1b2664cSjtc bi_errorf("too many arguments"); 133e1b2664cSjtc return 1; 134e1b2664cSjtc } 135e1b2664cSjtc break; 136e1b2664cSjtc case '?': 137e1b2664cSjtc return 1; 138e1b2664cSjtc } 139e1b2664cSjtc wp += builtin_opt.optind; 140e1b2664cSjtc 141e1b2664cSjtc /* Substitute and execute command */ 142e1b2664cSjtc if (sflag) { 143e1b2664cSjtc char *pat = (char *) 0, *rep = (char *) 0; 144e1b2664cSjtc 145e1b2664cSjtc if (editor || lflag || nflag || rflag) { 146e1b2664cSjtc bi_errorf("can't use -e, -l, -n, -r with -s (-e -)"); 147e1b2664cSjtc return 1; 148e1b2664cSjtc } 149e1b2664cSjtc 150e1b2664cSjtc /* Check for pattern replacement argument */ 151e1b2664cSjtc if (*wp && **wp && (p = strchr(*wp + 1, '='))) { 152e1b2664cSjtc pat = str_save(*wp, ATEMP); 153e1b2664cSjtc p = pat + (p - *wp); 154e1b2664cSjtc *p++ = '\0'; 155e1b2664cSjtc rep = p; 156e1b2664cSjtc wp++; 157e1b2664cSjtc } 158e1b2664cSjtc /* Check for search prefix */ 159e1b2664cSjtc if (!first && (first = *wp)) 160e1b2664cSjtc wp++; 161e1b2664cSjtc if (last || *wp) { 162e1b2664cSjtc bi_errorf("too many arguments"); 163e1b2664cSjtc return 1; 164e1b2664cSjtc } 165e1b2664cSjtc 166dd8a75d5Skamil hp = first ? hist_get(first, false, false) 167dd8a75d5Skamil : hist_get_newest(false); 168e1b2664cSjtc if (!hp) 169e1b2664cSjtc return 1; 170e1b2664cSjtc return hist_replace(hp, pat, rep, gflag); 171e1b2664cSjtc } 172e1b2664cSjtc 173e1b2664cSjtc if (editor && (lflag || nflag)) { 174e1b2664cSjtc bi_errorf("can't use -l, -n with -e"); 175e1b2664cSjtc return 1; 176e1b2664cSjtc } 177e1b2664cSjtc 178e1b2664cSjtc if (!first && (first = *wp)) 179e1b2664cSjtc wp++; 180e1b2664cSjtc if (!last && (last = *wp)) 181e1b2664cSjtc wp++; 182e1b2664cSjtc if (*wp) { 183e1b2664cSjtc bi_errorf("too many arguments"); 184e1b2664cSjtc return 1; 185e1b2664cSjtc } 186e1b2664cSjtc if (!first) { 187dd8a75d5Skamil hfirst = lflag ? hist_get("-16", true, true) 188dd8a75d5Skamil : hist_get_newest(false); 189e1b2664cSjtc if (!hfirst) 190e1b2664cSjtc return 1; 191e1b2664cSjtc /* can't fail if hfirst didn't fail */ 192dd8a75d5Skamil hlast = hist_get_newest(false); 193e1b2664cSjtc } else { 194e1b2664cSjtc /* POSIX says not an error if first/last out of bounds 195e1b2664cSjtc * when range is specified; at&t ksh and pdksh allow out of 196e1b2664cSjtc * bounds for -l as well. 197e1b2664cSjtc */ 198dd8a75d5Skamil hfirst = hist_get(first, (lflag || last) ? true : false, 199dd8a75d5Skamil lflag ? true : false); 200e1b2664cSjtc if (!hfirst) 201e1b2664cSjtc return 1; 202dd8a75d5Skamil hlast = last ? hist_get(last, true, lflag ? true : false) 203dd8a75d5Skamil : (lflag ? hist_get_newest(false) : hfirst); 204e1b2664cSjtc if (!hlast) 205e1b2664cSjtc return 1; 206e1b2664cSjtc } 207e1b2664cSjtc if (hfirst > hlast) { 208e1b2664cSjtc char **temp; 209e1b2664cSjtc 210e1b2664cSjtc temp = hfirst; hfirst = hlast; hlast = temp; 211e1b2664cSjtc rflag = !rflag; /* POSIX */ 212e1b2664cSjtc } 213e1b2664cSjtc 214e1b2664cSjtc /* List history */ 215e1b2664cSjtc if (lflag) { 216e1b2664cSjtc char *s, *t; 217e1b2664cSjtc const char *nfmt = nflag ? "\t" : "%d\t"; 218e1b2664cSjtc 219e1b2664cSjtc for (hp = rflag ? hlast : hfirst; 220e1b2664cSjtc hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) 221e1b2664cSjtc { 222e1b2664cSjtc shf_fprintf(shl_stdout, nfmt, 223e1b2664cSjtc hist_source->line - (int) (histptr - hp)); 224e1b2664cSjtc /* print multi-line commands correctly */ 225e1b2664cSjtc for (s = *hp; (t = strchr(s, '\n')); s = t) 226e1b2664cSjtc shf_fprintf(shl_stdout, "%.*s\t", ++t - s, s); 227e1b2664cSjtc shf_fprintf(shl_stdout, "%s\n", s); 228e1b2664cSjtc } 229e1b2664cSjtc shf_flush(shl_stdout); 230e1b2664cSjtc return 0; 231e1b2664cSjtc } 232e1b2664cSjtc 233e1b2664cSjtc /* Run editor on selected lines, then run resulting commands */ 234e1b2664cSjtc 23548ee8d12Shubertf tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps); 236e1b2664cSjtc if (!(shf = tf->shf)) { 237e1b2664cSjtc bi_errorf("cannot create temp file %s - %s", 238e1b2664cSjtc tf->name, strerror(errno)); 239e1b2664cSjtc return 1; 240e1b2664cSjtc } 241e1b2664cSjtc for (hp = rflag ? hlast : hfirst; 242e1b2664cSjtc hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) 243e1b2664cSjtc shf_fprintf(shf, "%s\n", *hp); 244e1b2664cSjtc if (shf_close(shf) == EOF) { 245e1b2664cSjtc bi_errorf("error writing temporary file - %s", strerror(errno)); 246e1b2664cSjtc return 1; 247e1b2664cSjtc } 248e1b2664cSjtc 24948ee8d12Shubertf /* Ignore setstr errors here (arbitrary) */ 250dd8a75d5Skamil setstr(local("_", false), tf->name, KSH_RETURN_ERROR); 251e1b2664cSjtc 252e1b2664cSjtc /* XXX: source should not get trashed by this.. */ 253e1b2664cSjtc { 254e1b2664cSjtc Source *sold = source; 255e1b2664cSjtc int ret; 256e1b2664cSjtc 257e1b2664cSjtc ret = command(editor ? editor : "${FCEDIT:-/bin/ed} $_"); 258e1b2664cSjtc source = sold; 259e1b2664cSjtc if (ret) 260e1b2664cSjtc return ret; 261e1b2664cSjtc } 262e1b2664cSjtc 263e1b2664cSjtc { 264e1b2664cSjtc struct stat statb; 265e1b2664cSjtc XString xs; 266e1b2664cSjtc char *xp; 267e1b2664cSjtc int n; 268e1b2664cSjtc 269e1b2664cSjtc if (!(shf = shf_open(tf->name, O_RDONLY, 0, 0))) { 270e1b2664cSjtc bi_errorf("cannot open temp file %s", tf->name); 271e1b2664cSjtc return 1; 272e1b2664cSjtc } 273e1b2664cSjtc 274e1b2664cSjtc n = fstat(shf_fileno(shf), &statb) < 0 ? 128 275e1b2664cSjtc : statb.st_size + 1; 276e1b2664cSjtc Xinit(xs, xp, n, hist_source->areap); 277e1b2664cSjtc while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) { 278e1b2664cSjtc xp += n; 279e1b2664cSjtc if (Xnleft(xs, xp) <= 0) 280e1b2664cSjtc XcheckN(xs, xp, Xlength(xs, xp)); 281e1b2664cSjtc } 282e1b2664cSjtc if (n < 0) { 283e1b2664cSjtc bi_errorf("error reading temp file %s - %s", 284e1b2664cSjtc tf->name, strerror(shf_errno(shf))); 285e1b2664cSjtc shf_close(shf); 286e1b2664cSjtc return 1; 287e1b2664cSjtc } 288e1b2664cSjtc shf_close(shf); 289e1b2664cSjtc *xp = '\0'; 290e1b2664cSjtc strip_nuls(Xstring(xs, xp), Xlength(xs, xp)); 291e1b2664cSjtc return hist_execute(Xstring(xs, xp)); 292e1b2664cSjtc } 293e1b2664cSjtc } 294e1b2664cSjtc 295e1b2664cSjtc /* Save cmd in history, execute cmd (cmd gets trashed) */ 296e1b2664cSjtc static int 297e1b2664cSjtc hist_execute(cmd) 298e1b2664cSjtc char *cmd; 299e1b2664cSjtc { 300e1b2664cSjtc Source *sold; 301e1b2664cSjtc int ret; 302e1b2664cSjtc char *p, *q; 303e1b2664cSjtc 304e1b2664cSjtc histbackup(); 305e1b2664cSjtc 306e1b2664cSjtc for (p = cmd; p; p = q) { 307e1b2664cSjtc if ((q = strchr(p, '\n'))) { 308e1b2664cSjtc *q++ = '\0'; /* kill the newline */ 309e1b2664cSjtc if (!*q) /* ignore trailing newline */ 310e1b2664cSjtc q = (char *) 0; 311e1b2664cSjtc } 312e1b2664cSjtc #ifdef EASY_HISTORY 313e1b2664cSjtc if (p != cmd) 314dd8a75d5Skamil histappend(p, true); 315e1b2664cSjtc else 316e1b2664cSjtc #endif /* EASY_HISTORY */ 317e1b2664cSjtc histsave(++(hist_source->line), p, 1); 318e1b2664cSjtc 319e1b2664cSjtc shellf("%s\n", p); /* POSIX doesn't say this is done... */ 320e1b2664cSjtc if ((p = q)) /* restore \n (trailing \n not restored) */ 321e1b2664cSjtc q[-1] = '\n'; 322e1b2664cSjtc } 323e1b2664cSjtc 324e1b2664cSjtc /* Commands are executed here instead of pushing them onto the 325e1b2664cSjtc * input 'cause posix says the redirection and variable assignments 326e1b2664cSjtc * in 327e1b2664cSjtc * X=y fc -e - 42 2> /dev/null 328*76748ca4Srillig * are to affect the repeated commands environment. 329e1b2664cSjtc */ 330e1b2664cSjtc /* XXX: source should not get trashed by this.. */ 331e1b2664cSjtc sold = source; 332e1b2664cSjtc ret = command(cmd); 333e1b2664cSjtc source = sold; 334e1b2664cSjtc return ret; 335e1b2664cSjtc } 336e1b2664cSjtc 337e1b2664cSjtc static int 338a397ec1fSchristos hist_replace(hp, pat, rep, globalv) 339e1b2664cSjtc char **hp; 340e1b2664cSjtc const char *pat; 341e1b2664cSjtc const char *rep; 342a397ec1fSchristos int globalv; 343e1b2664cSjtc { 344e1b2664cSjtc char *line; 345e1b2664cSjtc 346e1b2664cSjtc if (!pat) 347e1b2664cSjtc line = str_save(*hp, ATEMP); 348e1b2664cSjtc else { 349e1b2664cSjtc char *s, *s1; 350e1b2664cSjtc int pat_len = strlen(pat); 351e1b2664cSjtc int rep_len = strlen(rep); 352e1b2664cSjtc int len; 353e1b2664cSjtc XString xs; 354e1b2664cSjtc char *xp; 355e1b2664cSjtc int any_subst = 0; 356e1b2664cSjtc 357e1b2664cSjtc Xinit(xs, xp, 128, ATEMP); 358e1b2664cSjtc for (s = *hp; (s1 = strstr(s, pat)) 359a397ec1fSchristos && (!any_subst || globalv) ; s = s1 + pat_len) 360e1b2664cSjtc { 361e1b2664cSjtc any_subst = 1; 362e1b2664cSjtc len = s1 - s; 363e1b2664cSjtc XcheckN(xs, xp, len + rep_len); 364e1b2664cSjtc memcpy(xp, s, len); /* first part */ 365e1b2664cSjtc xp += len; 366e1b2664cSjtc memcpy(xp, rep, rep_len); /* replacement */ 367e1b2664cSjtc xp += rep_len; 368e1b2664cSjtc } 369e1b2664cSjtc if (!any_subst) { 370e1b2664cSjtc bi_errorf("substitution failed"); 371e1b2664cSjtc return 1; 372e1b2664cSjtc } 373e1b2664cSjtc len = strlen(s) + 1; 374e1b2664cSjtc XcheckN(xs, xp, len); 375e1b2664cSjtc memcpy(xp, s, len); 376e1b2664cSjtc xp += len; 377e1b2664cSjtc line = Xclose(xs, xp); 378e1b2664cSjtc } 379e1b2664cSjtc return hist_execute(line); 380e1b2664cSjtc } 381e1b2664cSjtc 382e1b2664cSjtc /* 383e1b2664cSjtc * get pointer to history given pattern 384e1b2664cSjtc * pattern is a number or string 385e1b2664cSjtc */ 386e1b2664cSjtc static char ** 387e1b2664cSjtc hist_get(str, approx, allow_cur) 388e1b2664cSjtc const char *str; 389e1b2664cSjtc int approx; 390e1b2664cSjtc int allow_cur; 391e1b2664cSjtc { 392e1b2664cSjtc char **hp = (char **) 0; 393e1b2664cSjtc int n; 394e1b2664cSjtc 395e1b2664cSjtc if (getn(str, &n)) { 396e1b2664cSjtc hp = histptr + (n < 0 ? n : (n - hist_source->line)); 39747b5291bSjdolecek if (hp < histlist) { 398e1b2664cSjtc if (approx) 399e1b2664cSjtc hp = hist_get_oldest(); 400e1b2664cSjtc else { 401e1b2664cSjtc bi_errorf("%s: not in history", str); 402e1b2664cSjtc hp = (char **) 0; 403e1b2664cSjtc } 404e1b2664cSjtc } else if (hp > histptr) { 405e1b2664cSjtc if (approx) 406e1b2664cSjtc hp = hist_get_newest(allow_cur); 407e1b2664cSjtc else { 408e1b2664cSjtc bi_errorf("%s: not in history", str); 409e1b2664cSjtc hp = (char **) 0; 410e1b2664cSjtc } 411e1b2664cSjtc } else if (!allow_cur && hp == histptr) { 412e1b2664cSjtc bi_errorf("%s: invalid range", str); 413e1b2664cSjtc hp = (char **) 0; 414e1b2664cSjtc } 415e1b2664cSjtc } else { 416e1b2664cSjtc int anchored = *str == '?' ? (++str, 0) : 1; 417e1b2664cSjtc 418e1b2664cSjtc /* the -1 is to avoid the current fc command */ 41947b5291bSjdolecek n = findhist(histptr - histlist - 1, 0, str, anchored); 420e1b2664cSjtc if (n < 0) { 421e1b2664cSjtc bi_errorf("%s: not in history", str); 422e1b2664cSjtc hp = (char **) 0; 423e1b2664cSjtc } else 42447b5291bSjdolecek hp = &histlist[n]; 425e1b2664cSjtc } 426e1b2664cSjtc return hp; 427e1b2664cSjtc } 428e1b2664cSjtc 429e1b2664cSjtc /* Return a pointer to the newest command in the history */ 430e1b2664cSjtc static char ** 431e1b2664cSjtc hist_get_newest(allow_cur) 432e1b2664cSjtc int allow_cur; 433e1b2664cSjtc { 43447b5291bSjdolecek if (histptr < histlist || (!allow_cur && histptr == histlist)) { 435e1b2664cSjtc bi_errorf("no history (yet)"); 436e1b2664cSjtc return (char **) 0; 437e1b2664cSjtc } 438e1b2664cSjtc if (allow_cur) 439e1b2664cSjtc return histptr; 440e1b2664cSjtc return histptr - 1; 441e1b2664cSjtc } 442e1b2664cSjtc 443e1b2664cSjtc /* Return a pointer to the newest command in the history */ 444e1b2664cSjtc static char ** 445e1b2664cSjtc hist_get_oldest() 446e1b2664cSjtc { 44747b5291bSjdolecek if (histptr <= histlist) { 448e1b2664cSjtc bi_errorf("no history (yet)"); 449e1b2664cSjtc return (char **) 0; 450e1b2664cSjtc } 45147b5291bSjdolecek return histlist; 452e1b2664cSjtc } 453e1b2664cSjtc 454e1b2664cSjtc /******************************/ 455e1b2664cSjtc /* Back up over last histsave */ 456e1b2664cSjtc /******************************/ 457e1b2664cSjtc static void 458e1b2664cSjtc histbackup() 459e1b2664cSjtc { 460e1b2664cSjtc static int last_line = -1; 461e1b2664cSjtc 46247b5291bSjdolecek if (histptr >= histlist && last_line != hist_source->line) { 463e1b2664cSjtc hist_source->line--; 464e1b2664cSjtc afree((void*)*histptr, APERM); 465e1b2664cSjtc histptr--; 466e1b2664cSjtc last_line = hist_source->line; 467e1b2664cSjtc } 468e1b2664cSjtc } 469e1b2664cSjtc 470e1b2664cSjtc /* 471e1b2664cSjtc * Return the current position. 472e1b2664cSjtc */ 473e1b2664cSjtc char ** 474e1b2664cSjtc histpos() 475e1b2664cSjtc { 476e1b2664cSjtc return current; 477e1b2664cSjtc } 478e1b2664cSjtc 479e1b2664cSjtc int 480e1b2664cSjtc histN() 481e1b2664cSjtc { 482e1b2664cSjtc return curpos; 483e1b2664cSjtc } 484e1b2664cSjtc 485e1b2664cSjtc int 486e1b2664cSjtc histnum(n) 487e1b2664cSjtc int n; 488e1b2664cSjtc { 48947b5291bSjdolecek int last = histptr - histlist; 490e1b2664cSjtc 491e1b2664cSjtc if (n < 0 || n >= last) { 492e1b2664cSjtc current = histptr; 493e1b2664cSjtc curpos = last; 494e1b2664cSjtc return last; 495e1b2664cSjtc } else { 49647b5291bSjdolecek current = &histlist[n]; 497e1b2664cSjtc curpos = n; 498e1b2664cSjtc return n; 499e1b2664cSjtc } 500e1b2664cSjtc } 501e1b2664cSjtc 502e1b2664cSjtc /* 503f662a744Smycroft * This will become unnecessary if hist_get is modified to allow 504e1b2664cSjtc * searching from positions other than the end, and in either 505e1b2664cSjtc * direction. 506e1b2664cSjtc */ 507e1b2664cSjtc int 508e1b2664cSjtc findhist(start, fwd, str, anchored) 509e1b2664cSjtc int start; 510e1b2664cSjtc int fwd; 511e1b2664cSjtc const char *str; 512e1b2664cSjtc int anchored; 513e1b2664cSjtc { 514e1b2664cSjtc char **hp; 51547b5291bSjdolecek int maxhist = histptr - histlist; 516e1b2664cSjtc int incr = fwd ? 1 : -1; 517e1b2664cSjtc int len = strlen(str); 518e1b2664cSjtc 519e1b2664cSjtc if (start < 0 || start >= maxhist) 520e1b2664cSjtc start = maxhist; 521e1b2664cSjtc 52247b5291bSjdolecek hp = &histlist[start]; 52347b5291bSjdolecek for (; hp >= histlist && hp <= histptr; hp += incr) 524e1b2664cSjtc if ((anchored && strncmp(*hp, str, len) == 0) 525e1b2664cSjtc || (!anchored && strstr(*hp, str))) 52647b5291bSjdolecek return hp - histlist; 527e1b2664cSjtc 528e1b2664cSjtc return -1; 529e1b2664cSjtc } 530e1b2664cSjtc 531e1b2664cSjtc /* 532e1b2664cSjtc * set history 533e1b2664cSjtc * this means reallocating the dataspace 534e1b2664cSjtc */ 535e1b2664cSjtc void 536e1b2664cSjtc sethistsize(n) 537e1b2664cSjtc int n; 538e1b2664cSjtc { 539e1b2664cSjtc if (n > 0 && n != histsize) { 54047b5291bSjdolecek int cursize = histptr - histlist; 541e1b2664cSjtc 542e1b2664cSjtc /* save most recent history */ 543e1b2664cSjtc if (n < cursize) { 54447b5291bSjdolecek memmove(histlist, histptr - n, n * sizeof(char *)); 545e1b2664cSjtc cursize = n; 546e1b2664cSjtc } 547e1b2664cSjtc 54847b5291bSjdolecek histlist = (char **)aresize(histlist, n*sizeof(char *), APERM); 549e1b2664cSjtc 550e1b2664cSjtc histsize = n; 55147b5291bSjdolecek histptr = histlist + cursize; 552e1b2664cSjtc } 553e1b2664cSjtc } 554e1b2664cSjtc 555e1b2664cSjtc /* 556e1b2664cSjtc * set history file 557e1b2664cSjtc * This can mean reloading/resetting/starting history file 558e1b2664cSjtc * maintenance 559e1b2664cSjtc */ 560e1b2664cSjtc void 561e1b2664cSjtc sethistfile(name) 562e1b2664cSjtc const char *name; 563e1b2664cSjtc { 564e1b2664cSjtc /* if not started then nothing to do */ 565e1b2664cSjtc if (hstarted == 0) 566e1b2664cSjtc return; 567e1b2664cSjtc 568e1b2664cSjtc /* if the name is the same as the name we have */ 569e1b2664cSjtc if (hname && strcmp(hname, name) == 0) 570e1b2664cSjtc return; 571e1b2664cSjtc 572e1b2664cSjtc /* 573e1b2664cSjtc * its a new name - possibly 574e1b2664cSjtc */ 575e1b2664cSjtc # ifdef EASY_HISTORY 576e1b2664cSjtc if (hname) { 577e1b2664cSjtc afree(hname, APERM); 578e1b2664cSjtc hname = NULL; 579e1b2664cSjtc } 580e1b2664cSjtc # else 581e1b2664cSjtc if (histfd) { 582e1b2664cSjtc /* yes the file is open */ 583e1b2664cSjtc (void) close(histfd); 584e1b2664cSjtc histfd = 0; 585e1b2664cSjtc hsize = 0; 586e1b2664cSjtc afree(hname, APERM); 587e1b2664cSjtc hname = NULL; 588e1b2664cSjtc /* let's reset the history */ 58947b5291bSjdolecek histptr = histlist - 1; 590e1b2664cSjtc hist_source->line = 0; 591e1b2664cSjtc } 592e1b2664cSjtc # endif 593e1b2664cSjtc 594e1b2664cSjtc hist_init(hist_source); 595e1b2664cSjtc } 596e1b2664cSjtc 597e1b2664cSjtc /* 598e1b2664cSjtc * initialise the history vector 599e1b2664cSjtc */ 600e1b2664cSjtc void 601e1b2664cSjtc init_histvec() 602e1b2664cSjtc { 6039f61b804Splunky if (histlist == NULL) { 604e1b2664cSjtc histsize = HISTORYSIZE; 60547b5291bSjdolecek histlist = (char **)alloc(histsize*sizeof (char *), APERM); 60647b5291bSjdolecek histptr = histlist - 1; 607e1b2664cSjtc } 608e1b2664cSjtc } 609e1b2664cSjtc 610e1b2664cSjtc # ifdef EASY_HISTORY 611e1b2664cSjtc /* 612e1b2664cSjtc * save command in history 613e1b2664cSjtc */ 614e1b2664cSjtc void 615e1b2664cSjtc histsave(lno, cmd, dowrite) 616e1b2664cSjtc int lno; /* ignored (compatibility with COMPLEX_HISTORY) */ 617e1b2664cSjtc const char *cmd; 618e1b2664cSjtc int dowrite; /* ignored (compatibility with COMPLEX_HISTORY) */ 619e1b2664cSjtc { 6200f2b5450Skamil char **hp = histptr; 621e1b2664cSjtc char *cp; 622e1b2664cSjtc 62347b5291bSjdolecek if (++hp >= histlist + histsize) { /* remove oldest command */ 62447b5291bSjdolecek afree((void*)histlist[0], APERM); 62547b5291bSjdolecek memmove(histlist, histlist + 1, 62647b5291bSjdolecek sizeof(histlist[0]) * (histsize - 1)); 62747b5291bSjdolecek hp = &histlist[histsize - 1]; 628e1b2664cSjtc } 629e1b2664cSjtc *hp = str_save(cmd, APERM); 630e1b2664cSjtc /* trash trailing newline but allow imbedded newlines */ 631e1b2664cSjtc cp = *hp + strlen(*hp); 632e1b2664cSjtc if (cp > *hp && cp[-1] == '\n') 633e1b2664cSjtc cp[-1] = '\0'; 634e1b2664cSjtc histptr = hp; 635e1b2664cSjtc } 636e1b2664cSjtc 637e1b2664cSjtc /* 638e1b2664cSjtc * Append an entry to the last saved command. Used for multiline 639e1b2664cSjtc * commands 640e1b2664cSjtc */ 641e1b2664cSjtc void 64248ee8d12Shubertf histappend(cmd, nl_separate) 643e1b2664cSjtc const char *cmd; 64448ee8d12Shubertf int nl_separate; 645e1b2664cSjtc { 646e1b2664cSjtc int hlen, clen; 647e1b2664cSjtc char *p; 648e1b2664cSjtc 649e1b2664cSjtc hlen = strlen(*histptr); 650e1b2664cSjtc clen = strlen(cmd); 651e1b2664cSjtc if (clen > 0 && cmd[clen-1] == '\n') 652e1b2664cSjtc clen--; 653e1b2664cSjtc p = *histptr = (char *) aresize(*histptr, hlen + clen + 2, APERM); 654e1b2664cSjtc p += hlen; 65548ee8d12Shubertf if (nl_separate) 656e1b2664cSjtc *p++ = '\n'; 657e1b2664cSjtc memcpy(p, cmd, clen); 658e1b2664cSjtc p[clen] = '\0'; 659e1b2664cSjtc } 660e1b2664cSjtc 661e1b2664cSjtc /* 662e1b2664cSjtc * 92-04-25 <sjg@zen> 663e1b2664cSjtc * A simple history file implementation. 664e1b2664cSjtc * At present we only save the history when we exit. 665e1b2664cSjtc * This can cause problems when there are multiple shells are 666e1b2664cSjtc * running under the same user-id. The last shell to exit gets 667e1b2664cSjtc * to save its history. 668e1b2664cSjtc */ 669e1b2664cSjtc void 670e1b2664cSjtc hist_init(s) 671e1b2664cSjtc Source *s; 672e1b2664cSjtc { 673e1b2664cSjtc char *f; 674e1b2664cSjtc FILE *fh; 675e1b2664cSjtc 676e1b2664cSjtc if (Flag(FTALKING) == 0) 677e1b2664cSjtc return; 678e1b2664cSjtc 679e1b2664cSjtc hstarted = 1; 680e1b2664cSjtc 681e1b2664cSjtc hist_source = s; 682e1b2664cSjtc 683e1b2664cSjtc if ((f = str_val(global("HISTFILE"))) == NULL || *f == '\0') { 684e1b2664cSjtc # if 1 /* Don't use history file unless the user asks for it */ 685e1b2664cSjtc hname = NULL; 686e1b2664cSjtc return; 687e1b2664cSjtc # else 688e1b2664cSjtc char *home = str_val(global("HOME")); 689e1b2664cSjtc int len; 690e1b2664cSjtc 691e1b2664cSjtc if (home == NULL) 692e1b2664cSjtc home = null; 693e1b2664cSjtc f = HISTFILE; 694e1b2664cSjtc hname = alloc(len = strlen(home) + strlen(f) + 2, APERM); 695e1b2664cSjtc shf_snprintf(hname, len, "%s/%s", home, f); 696e1b2664cSjtc # endif 697e1b2664cSjtc } else 698e1b2664cSjtc hname = str_save(f, APERM); 699e1b2664cSjtc 700e1b2664cSjtc if ((fh = fopen(hname, "r"))) { 701e1b2664cSjtc int pos = 0, nread = 0; 702e1b2664cSjtc int contin = 0; /* continuation of previous command */ 703e1b2664cSjtc char *end; 704e1b2664cSjtc char hline[LINE + 1]; 705e1b2664cSjtc 706e1b2664cSjtc while (1) { 707e1b2664cSjtc if (pos >= nread) { 708e1b2664cSjtc pos = 0; 709e1b2664cSjtc nread = fread(hline, 1, LINE, fh); 710e1b2664cSjtc if (nread <= 0) 711e1b2664cSjtc break; 712e1b2664cSjtc hline[nread] = '\0'; 713e1b2664cSjtc } 714e1b2664cSjtc end = strchr(hline + pos, 0); /* will always succeed */ 715e1b2664cSjtc if (contin) 716e1b2664cSjtc histappend(hline + pos, 0); 717e1b2664cSjtc else { 718e1b2664cSjtc hist_source->line++; 719e1b2664cSjtc histsave(0, hline + pos, 0); 720e1b2664cSjtc } 721e1b2664cSjtc pos = end - hline + 1; 722e1b2664cSjtc contin = end == &hline[nread]; 723e1b2664cSjtc } 724e1b2664cSjtc fclose(fh); 725e1b2664cSjtc } 726e1b2664cSjtc } 727e1b2664cSjtc 728e1b2664cSjtc /* 729e1b2664cSjtc * save our history. 730e1b2664cSjtc * We check that we do not have more than we are allowed. 731e1b2664cSjtc * If the history file is read-only we do nothing. 732e1b2664cSjtc * Handy for having all shells start with a useful history set. 733e1b2664cSjtc */ 734e1b2664cSjtc 735e1b2664cSjtc void 736e1b2664cSjtc hist_finish() 737e1b2664cSjtc { 738e1b2664cSjtc static int once; 739b392d545Sdsl int fd; 740e1b2664cSjtc FILE *fh; 7410f2b5450Skamil int i; 7420f2b5450Skamil char **hp; 743e1b2664cSjtc 744e1b2664cSjtc if (once++) 745e1b2664cSjtc return; 746b392d545Sdsl if (hname == NULL || hname[0] == 0) 747b392d545Sdsl return; 748b392d545Sdsl 749e1b2664cSjtc /* check how many we have */ 75047b5291bSjdolecek i = histptr - histlist; 751e1b2664cSjtc if (i >= histsize) 752e1b2664cSjtc hp = &histptr[-histsize]; 753e1b2664cSjtc else 75447b5291bSjdolecek hp = histlist; 755b392d545Sdsl 756ee3bb853Smaya if ((fd = open(hname, O_WRONLY | O_CREAT | O_TRUNC | O_EXLOCK, 0600)) != -1) { 757b392d545Sdsl /* Remove anything written before we got the lock */ 758b392d545Sdsl ftruncate(fd, 0); 759f959afb2Smaya if ((fh = fdopen(fd, "w")) != NULL) { 760e1b2664cSjtc for (i = 0; hp + i <= histptr && hp[i]; i++) 761e1b2664cSjtc fprintf(fh, "%s%c", hp[i], '\0'); 762e1b2664cSjtc fclose(fh); 763e1b2664cSjtc } 764e1b2664cSjtc } 765f959afb2Smaya } 766e1b2664cSjtc 767e1b2664cSjtc # else /* EASY_HISTORY */ 768e1b2664cSjtc 769e1b2664cSjtc /* 770e1b2664cSjtc * Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to 771e1b2664cSjtc * a) permit HISTSIZE to control number of lines of history stored 772e1b2664cSjtc * b) maintain a physical history file 773e1b2664cSjtc * 774e1b2664cSjtc * It turns out that there is a lot of ghastly hackery here 775e1b2664cSjtc */ 776e1b2664cSjtc 777e1b2664cSjtc 778e1b2664cSjtc /* 779e1b2664cSjtc * save command in history 780e1b2664cSjtc */ 781e1b2664cSjtc void 782e1b2664cSjtc histsave(lno, cmd, dowrite) 783e1b2664cSjtc int lno; 784e1b2664cSjtc const char *cmd; 785e1b2664cSjtc int dowrite; 786e1b2664cSjtc { 7870f2b5450Skamil char **hp; 788e1b2664cSjtc char *c, *cp; 789e1b2664cSjtc 790e1b2664cSjtc c = str_save(cmd, APERM); 791e1b2664cSjtc if ((cp = strchr(c, '\n')) != NULL) 792e1b2664cSjtc *cp = '\0'; 793e1b2664cSjtc 794e1b2664cSjtc if (histfd && dowrite) 795e1b2664cSjtc writehistfile(lno, c); 796e1b2664cSjtc 797e1b2664cSjtc hp = histptr; 798e1b2664cSjtc 79947b5291bSjdolecek if (++hp >= histlist + histsize) { /* remove oldest command */ 80047b5291bSjdolecek afree((void*)*histlist, APERM); 80147b5291bSjdolecek for (hp = histlist; hp < histlist + histsize - 1; hp++) 802e1b2664cSjtc hp[0] = hp[1]; 803e1b2664cSjtc } 804e1b2664cSjtc *hp = c; 805e1b2664cSjtc histptr = hp; 806e1b2664cSjtc } 807e1b2664cSjtc 808e1b2664cSjtc /* 809e1b2664cSjtc * Write history data to a file nominated by HISTFILE 810e1b2664cSjtc * if HISTFILE is unset then history still happens, but 811e1b2664cSjtc * the data is not written to a file 812e1b2664cSjtc * All copies of ksh looking at the file will maintain the 813e1b2664cSjtc * same history. This is ksh behaviour. 814e1b2664cSjtc * 815e1b2664cSjtc * This stuff uses mmap() 816e1b2664cSjtc * if your system ain't got it - then you'll have to undef HISTORYFILE 817e1b2664cSjtc */ 818e1b2664cSjtc 819e1b2664cSjtc /* 820e1b2664cSjtc * Open a history file 821e1b2664cSjtc * Format is: 822e1b2664cSjtc * Bytes 1, 2: HMAGIC - just to check that we are dealing with 823e1b2664cSjtc * the correct object 824e1b2664cSjtc * Then follows a number of stored commands 825e1b2664cSjtc * Each command is 826e1b2664cSjtc * <command byte><command number(4 bytes)><bytes><null> 827e1b2664cSjtc */ 828e1b2664cSjtc # define HMAGIC1 0xab 829e1b2664cSjtc # define HMAGIC2 0xcd 830e1b2664cSjtc # define COMMAND 0xff 831e1b2664cSjtc 832e1b2664cSjtc void 833e1b2664cSjtc hist_init(s) 834e1b2664cSjtc Source *s; 835e1b2664cSjtc { 836e1b2664cSjtc unsigned char *base; 837e1b2664cSjtc int lines; 838e1b2664cSjtc int fd; 839e1b2664cSjtc 840e1b2664cSjtc if (Flag(FTALKING) == 0) 841e1b2664cSjtc return; 842e1b2664cSjtc 843e1b2664cSjtc hstarted = 1; 844e1b2664cSjtc 845e1b2664cSjtc hist_source = s; 846e1b2664cSjtc 847e1b2664cSjtc hname = str_val(global("HISTFILE")); 848e1b2664cSjtc if (hname == NULL) 849e1b2664cSjtc return; 850e1b2664cSjtc hname = str_save(hname, APERM); 851e1b2664cSjtc 852e1b2664cSjtc retry: 853e1b2664cSjtc /* we have a file and are interactive */ 854e1b2664cSjtc if ((fd = open(hname, O_RDWR|O_CREAT|O_APPEND, 0600)) < 0) 855e1b2664cSjtc return; 856e1b2664cSjtc 857e1b2664cSjtc histfd = savefd(fd, 0); 858e1b2664cSjtc 859e1b2664cSjtc (void) flock(histfd, LOCK_EX); 860e1b2664cSjtc 861e1b2664cSjtc hsize = lseek(histfd, 0L, SEEK_END); 862e1b2664cSjtc 863e1b2664cSjtc if (hsize == 0) { 864e1b2664cSjtc /* add magic */ 865e1b2664cSjtc if (sprinkle(histfd)) { 866e1b2664cSjtc hist_finish(); 867e1b2664cSjtc return; 868e1b2664cSjtc } 869e1b2664cSjtc } 870e1b2664cSjtc else if (hsize > 0) { 871e1b2664cSjtc /* 872e1b2664cSjtc * we have some data 873e1b2664cSjtc */ 874e1b2664cSjtc base = (unsigned char *)mmap(0, hsize, PROT_READ, MAP_FLAGS, histfd, 0); 875e1b2664cSjtc /* 876e1b2664cSjtc * check on its validity 877e1b2664cSjtc */ 878f662a744Smycroft if (base == MAP_FAILED || *base != HMAGIC1 || base[1] != HMAGIC2) { 879f662a744Smycroft if (base != MAP_FAILED) 880e1b2664cSjtc munmap((caddr_t)base, hsize); 881e1b2664cSjtc hist_finish(); 882e1b2664cSjtc unlink(hname); 883e1b2664cSjtc goto retry; 884e1b2664cSjtc } 885e1b2664cSjtc if (hsize > 2) { 886e1b2664cSjtc lines = hist_count_lines(base+2, hsize-2); 887e1b2664cSjtc if (lines > histsize) { 888e1b2664cSjtc /* we need to make the file smaller */ 889e1b2664cSjtc if (hist_shrink(base, hsize)) 890e1b2664cSjtc unlink(hname); 891e1b2664cSjtc munmap((caddr_t)base, hsize); 892e1b2664cSjtc hist_finish(); 893e1b2664cSjtc goto retry; 894e1b2664cSjtc } 895e1b2664cSjtc } 896e1b2664cSjtc histload(hist_source, base+2, hsize-2); 897e1b2664cSjtc munmap((caddr_t)base, hsize); 898e1b2664cSjtc } 899e1b2664cSjtc (void) flock(histfd, LOCK_UN); 900e1b2664cSjtc hsize = lseek(histfd, 0L, SEEK_END); 901e1b2664cSjtc } 902e1b2664cSjtc 903e1b2664cSjtc typedef enum state { 904e1b2664cSjtc shdr, /* expecting a header */ 905e1b2664cSjtc sline, /* looking for a null byte to end the line */ 906e1b2664cSjtc sn1, /* bytes 1 to 4 of a line no */ 907f662a744Smycroft sn2, sn3, sn4 908e1b2664cSjtc } State; 909e1b2664cSjtc 910e1b2664cSjtc static int 911e1b2664cSjtc hist_count_lines(base, bytes) 9120f2b5450Skamil unsigned char *base; 9130f2b5450Skamil int bytes; 914e1b2664cSjtc { 915e1b2664cSjtc State state = shdr; 916f662a744Smycroft int lines = 0; 917e1b2664cSjtc 918e1b2664cSjtc while (bytes--) { 919e1b2664cSjtc switch (state) 920e1b2664cSjtc { 921e1b2664cSjtc case shdr: 922e1b2664cSjtc if (*base == COMMAND) 923e1b2664cSjtc state = sn1; 924e1b2664cSjtc break; 925e1b2664cSjtc case sn1: 926e1b2664cSjtc state = sn2; break; 927e1b2664cSjtc case sn2: 928e1b2664cSjtc state = sn3; break; 929e1b2664cSjtc case sn3: 930e1b2664cSjtc state = sn4; break; 931e1b2664cSjtc case sn4: 932e1b2664cSjtc state = sline; break; 933e1b2664cSjtc case sline: 934e1b2664cSjtc if (*base == '\0') 935e1b2664cSjtc lines++, state = shdr; 936e1b2664cSjtc } 937e1b2664cSjtc base++; 938e1b2664cSjtc } 939e1b2664cSjtc return lines; 940e1b2664cSjtc } 941e1b2664cSjtc 942e1b2664cSjtc /* 943e1b2664cSjtc * Shrink the history file to histsize lines 944e1b2664cSjtc */ 945e1b2664cSjtc static int 946e1b2664cSjtc hist_shrink(oldbase, oldbytes) 947e1b2664cSjtc unsigned char *oldbase; 948e1b2664cSjtc int oldbytes; 949e1b2664cSjtc { 950e1b2664cSjtc int fd; 951e1b2664cSjtc char nfile[1024]; 952e1b2664cSjtc struct stat statb; 953e1b2664cSjtc unsigned char *nbase = oldbase; 954e1b2664cSjtc int nbytes = oldbytes; 955e1b2664cSjtc 956e1b2664cSjtc nbase = hist_skip_back(nbase, &nbytes, histsize); 957e1b2664cSjtc if (nbase == NULL) 958e1b2664cSjtc return 1; 959e1b2664cSjtc if (nbase == oldbase) 960e1b2664cSjtc return 0; 961e1b2664cSjtc 962e1b2664cSjtc /* 963e1b2664cSjtc * create temp file 964e1b2664cSjtc */ 965e1b2664cSjtc (void) shf_snprintf(nfile, sizeof(nfile), "%s.%d", hname, procpid); 966e1b2664cSjtc if ((fd = creat(nfile, 0600)) < 0) 967e1b2664cSjtc return 1; 968e1b2664cSjtc 969e1b2664cSjtc if (sprinkle(fd)) { 970e1b2664cSjtc close(fd); 971e1b2664cSjtc unlink(nfile); 972e1b2664cSjtc return 1; 973e1b2664cSjtc } 974e1b2664cSjtc if (write(fd, nbase, nbytes) != nbytes) { 975e1b2664cSjtc close(fd); 976e1b2664cSjtc unlink(nfile); 977e1b2664cSjtc return 1; 978e1b2664cSjtc } 979e1b2664cSjtc /* 980e1b2664cSjtc * worry about who owns this file 981e1b2664cSjtc */ 982e1b2664cSjtc if (fstat(histfd, &statb) >= 0) 983e1b2664cSjtc fchown(fd, statb.st_uid, statb.st_gid); 984e1b2664cSjtc close(fd); 985e1b2664cSjtc 986e1b2664cSjtc /* 987e1b2664cSjtc * rename 988e1b2664cSjtc */ 989e1b2664cSjtc if (rename(nfile, hname) < 0) 990e1b2664cSjtc return 1; 991e1b2664cSjtc return 0; 992e1b2664cSjtc } 993e1b2664cSjtc 994e1b2664cSjtc 995e1b2664cSjtc /* 996e1b2664cSjtc * find a pointer to the data `no' back from the end of the file 997e1b2664cSjtc * return the pointer and the number of bytes left 998e1b2664cSjtc */ 999e1b2664cSjtc static unsigned char * 1000e1b2664cSjtc hist_skip_back(base, bytes, no) 1001e1b2664cSjtc unsigned char *base; 1002e1b2664cSjtc int *bytes; 1003e1b2664cSjtc int no; 1004e1b2664cSjtc { 10050f2b5450Skamil int lines = 0; 10060f2b5450Skamil unsigned char *ep; 1007e1b2664cSjtc 1008e1b2664cSjtc for (ep = base + *bytes; --ep > base; ) { 1009e1b2664cSjtc /* this doesn't really work: the 4 byte line number that is 1010e1b2664cSjtc * encoded after the COMMAND byte can itself contain the 1011e1b2664cSjtc * COMMAND byte.... 1012e1b2664cSjtc */ 1013e1b2664cSjtc for (; ep > base && *ep != COMMAND; ep--) 1014e1b2664cSjtc ; 1015e1b2664cSjtc if (ep == base) 1016e1b2664cSjtc break; 1017e1b2664cSjtc if (++lines == no) { 1018e1b2664cSjtc *bytes = *bytes - ((char *)ep - (char *)base); 1019e1b2664cSjtc return ep; 1020e1b2664cSjtc } 1021e1b2664cSjtc } 1022e1b2664cSjtc return NULL; 1023e1b2664cSjtc } 1024e1b2664cSjtc 1025e1b2664cSjtc /* 1026e1b2664cSjtc * load the history structure from the stored data 1027e1b2664cSjtc */ 1028e1b2664cSjtc static void 1029e1b2664cSjtc histload(s, base, bytes) 1030e1b2664cSjtc Source *s; 10310f2b5450Skamil unsigned char *base; 10320f2b5450Skamil int bytes; 1033e1b2664cSjtc { 1034e1b2664cSjtc State state; 1035f662a744Smycroft int lno = 0; 1036f662a744Smycroft unsigned char *line = NULL; 1037e1b2664cSjtc 1038e1b2664cSjtc for (state = shdr; bytes-- > 0; base++) { 1039e1b2664cSjtc switch (state) { 1040e1b2664cSjtc case shdr: 1041e1b2664cSjtc if (*base == COMMAND) 1042e1b2664cSjtc state = sn1; 1043e1b2664cSjtc break; 1044e1b2664cSjtc case sn1: 1045e1b2664cSjtc lno = (((*base)&0xff)<<24); 1046e1b2664cSjtc state = sn2; 1047e1b2664cSjtc break; 1048e1b2664cSjtc case sn2: 1049e1b2664cSjtc lno |= (((*base)&0xff)<<16); 1050e1b2664cSjtc state = sn3; 1051e1b2664cSjtc break; 1052e1b2664cSjtc case sn3: 1053e1b2664cSjtc lno |= (((*base)&0xff)<<8); 1054e1b2664cSjtc state = sn4; 1055e1b2664cSjtc break; 1056e1b2664cSjtc case sn4: 1057e1b2664cSjtc lno |= (*base)&0xff; 1058e1b2664cSjtc line = base+1; 1059e1b2664cSjtc state = sline; 1060e1b2664cSjtc break; 1061e1b2664cSjtc case sline: 1062e1b2664cSjtc if (*base == '\0') { 1063e1b2664cSjtc /* worry about line numbers */ 106447b5291bSjdolecek if (histptr >= histlist && lno-1 != s->line) { 1065e1b2664cSjtc /* a replacement ? */ 1066e1b2664cSjtc histinsert(s, lno, line); 1067e1b2664cSjtc } 1068e1b2664cSjtc else { 1069e1b2664cSjtc s->line = lno; 1070e1b2664cSjtc histsave(lno, (char *)line, 0); 1071e1b2664cSjtc } 1072e1b2664cSjtc state = shdr; 1073e1b2664cSjtc } 1074e1b2664cSjtc } 1075e1b2664cSjtc } 1076e1b2664cSjtc } 1077e1b2664cSjtc 1078e1b2664cSjtc /* 1079e1b2664cSjtc * Insert a line into the history at a specified number 1080e1b2664cSjtc */ 1081e1b2664cSjtc static void 1082e1b2664cSjtc histinsert(s, lno, line) 1083e1b2664cSjtc Source *s; 1084e1b2664cSjtc int lno; 1085e1b2664cSjtc unsigned char *line; 1086e1b2664cSjtc { 10870f2b5450Skamil char **hp; 1088e1b2664cSjtc 108947b5291bSjdolecek if (lno >= s->line-(histptr-histlist) && lno <= s->line) { 1090e1b2664cSjtc hp = &histptr[lno-s->line]; 1091e1b2664cSjtc if (*hp) 1092e1b2664cSjtc afree((void*)*hp, APERM); 1093e1b2664cSjtc *hp = str_save((char *)line, APERM); 1094e1b2664cSjtc } 1095e1b2664cSjtc } 1096e1b2664cSjtc 1097e1b2664cSjtc /* 1098e1b2664cSjtc * write a command to the end of the history file 1099e1b2664cSjtc * This *MAY* seem easy but it's also necessary to check 1100e1b2664cSjtc * that the history file has not changed in size. 1101e1b2664cSjtc * If it has - then some other shell has written to it 1102e1b2664cSjtc * and we should read those commands to update our history 1103e1b2664cSjtc */ 1104e1b2664cSjtc static void 1105e1b2664cSjtc writehistfile(lno, cmd) 1106e1b2664cSjtc int lno; 1107e1b2664cSjtc char *cmd; 1108e1b2664cSjtc { 1109e1b2664cSjtc int sizenow; 1110e1b2664cSjtc unsigned char *base; 1111e1b2664cSjtc unsigned char *new; 1112e1b2664cSjtc int bytes; 1113f662a744Smycroft unsigned char hdr[5]; 1114e1b2664cSjtc 1115e1b2664cSjtc (void) flock(histfd, LOCK_EX); 1116e1b2664cSjtc sizenow = lseek(histfd, 0L, SEEK_END); 1117e1b2664cSjtc if (sizenow != hsize) { 1118e1b2664cSjtc /* 1119e1b2664cSjtc * Things have changed 1120e1b2664cSjtc */ 1121e1b2664cSjtc if (sizenow > hsize) { 1122e1b2664cSjtc /* someone has added some lines */ 1123e1b2664cSjtc bytes = sizenow - hsize; 1124e1b2664cSjtc base = (unsigned char *)mmap(0, sizenow, PROT_READ, MAP_FLAGS, histfd, 0); 1125f662a744Smycroft if (base == MAP_FAILED) 1126e1b2664cSjtc goto bad; 1127e1b2664cSjtc new = base + hsize; 1128e1b2664cSjtc if (*new != COMMAND) { 1129e1b2664cSjtc munmap((caddr_t)base, sizenow); 1130e1b2664cSjtc goto bad; 1131e1b2664cSjtc } 1132e1b2664cSjtc hist_source->line--; 1133e1b2664cSjtc histload(hist_source, new, bytes); 1134e1b2664cSjtc hist_source->line++; 1135e1b2664cSjtc lno = hist_source->line; 1136e1b2664cSjtc munmap((caddr_t)base, sizenow); 1137e1b2664cSjtc hsize = sizenow; 1138e1b2664cSjtc } else { 1139e1b2664cSjtc /* it has shrunk */ 1140e1b2664cSjtc /* but to what? */ 1141e1b2664cSjtc /* we'll give up for now */ 1142e1b2664cSjtc goto bad; 1143e1b2664cSjtc } 1144e1b2664cSjtc } 1145e1b2664cSjtc /* 1146e1b2664cSjtc * we can write our bit now 1147e1b2664cSjtc */ 1148e1b2664cSjtc hdr[0] = COMMAND; 1149e1b2664cSjtc hdr[1] = (lno>>24)&0xff; 1150e1b2664cSjtc hdr[2] = (lno>>16)&0xff; 1151e1b2664cSjtc hdr[3] = (lno>>8)&0xff; 1152e1b2664cSjtc hdr[4] = lno&0xff; 1153e1b2664cSjtc (void) write(histfd, hdr, 5); 1154e1b2664cSjtc (void) write(histfd, cmd, strlen(cmd)+1); 1155e1b2664cSjtc hsize = lseek(histfd, 0L, SEEK_END); 1156e1b2664cSjtc (void) flock(histfd, LOCK_UN); 1157e1b2664cSjtc return; 1158e1b2664cSjtc bad: 1159e1b2664cSjtc hist_finish(); 1160e1b2664cSjtc } 1161e1b2664cSjtc 1162e1b2664cSjtc void 1163e1b2664cSjtc hist_finish() 1164e1b2664cSjtc { 1165e1b2664cSjtc (void) flock(histfd, LOCK_UN); 1166e1b2664cSjtc (void) close(histfd); 1167e1b2664cSjtc histfd = 0; 1168e1b2664cSjtc } 1169e1b2664cSjtc 1170e1b2664cSjtc /* 1171e1b2664cSjtc * add magic to the history file 1172e1b2664cSjtc */ 1173e1b2664cSjtc static int 1174e1b2664cSjtc sprinkle(fd) 1175e1b2664cSjtc int fd; 1176e1b2664cSjtc { 1177f662a744Smycroft static unsigned char mag[] = { HMAGIC1, HMAGIC2 }; 1178e1b2664cSjtc 1179e1b2664cSjtc return(write(fd, mag, 2) != 2); 1180e1b2664cSjtc } 1181e1b2664cSjtc 1182e1b2664cSjtc # endif 1183e1b2664cSjtc #else /* HISTORY */ 1184e1b2664cSjtc 1185e1b2664cSjtc /* No history to be compiled in: dummy routines to avoid lots more ifdefs */ 1186e1b2664cSjtc void 1187e1b2664cSjtc init_histvec() 1188e1b2664cSjtc { 1189e1b2664cSjtc } 1190e1b2664cSjtc void 1191e1b2664cSjtc hist_init(s) 1192e1b2664cSjtc Source *s; 1193e1b2664cSjtc { 1194e1b2664cSjtc } 1195e1b2664cSjtc void 1196e1b2664cSjtc hist_finish() 1197e1b2664cSjtc { 1198e1b2664cSjtc } 1199e1b2664cSjtc void 1200e1b2664cSjtc histsave(lno, cmd, dowrite) 1201e1b2664cSjtc int lno; 1202e1b2664cSjtc const char *cmd; 1203e1b2664cSjtc int dowrite; 1204e1b2664cSjtc { 1205e1b2664cSjtc errorf("history not enabled"); 1206e1b2664cSjtc } 1207e1b2664cSjtc #endif /* HISTORY */ 1208