📄 history.c
字号:
histptr = history - 1; }}# ifdef EASY_HISTORY/* * save command in history */voidhistsave(lno, cmd, dowrite) int lno; /* ignored (compatibility with COMPLEX_HISTORY) */ const char *cmd; int dowrite; /* ignored (compatibility with COMPLEX_HISTORY) */{ register char **hp = histptr; char *cp; if (++hp >= history + histsize) { /* remove oldest command */ afree((void*)history[0], APERM); memmove(history, history + 1, sizeof(history[0]) * (histsize - 1)); hp = &history[histsize - 1]; } *hp = str_save(cmd, APERM); /* trash trailing newline but allow imbedded newlines */ cp = *hp + strlen(*hp); if (cp > *hp && cp[-1] == '\n') cp[-1] = '\0'; histptr = hp;}/* * Append an entry to the last saved command. Used for multiline * commands */voidhistappend(cmd, nl_separate) const char *cmd; int nl_separate;{ int hlen, clen; char *p; hlen = strlen(*histptr); clen = strlen(cmd); if (clen > 0 && cmd[clen-1] == '\n') clen--; p = *histptr = (char *) aresize(*histptr, hlen + clen + 2, APERM); p += hlen; if (nl_separate) *p++ = '\n'; memcpy(p, cmd, clen); p[clen] = '\0';}/* * 92-04-25 <sjg@zen> * A simple history file implementation. * At present we only save the history when we exit. * This can cause problems when there are multiple shells are * running under the same user-id. The last shell to exit gets * to save its history. */voidhist_init(s) Source *s;{ char *f; FILE *fh; if (Flag(FTALKING) == 0) return; hstarted = 1; hist_source = s; if ((f = str_val(global("HISTFILE"))) == NULL || *f == '\0') {# if 1 /* Don't use history file unless the user asks for it */ hname = NULL; return;# else char *home = str_val(global("HOME")); int len; if (home == NULL) home = null; f = HISTFILE; hname = alloc(len = strlen(home) + strlen(f) + 2, APERM); shf_snprintf(hname, len, "%s/%s", home, f);# endif } else hname = str_save(f, APERM); if ((fh = fopen(hname, "r"))) { int pos = 0, nread = 0; int contin = 0; /* continuation of previous command */ char *end; char hline[LINE + 1]; while (1) { if (pos >= nread) { pos = 0; nread = fread(hline, 1, LINE, fh); if (nread <= 0) break; hline[nread] = '\0'; } end = strchr(hline + pos, 0); /* will always succeed */ if (contin) histappend(hline + pos, 0); else { hist_source->line++; histsave(0, hline + pos, 0); } pos = end - hline + 1; contin = end == &hline[nread]; } fclose(fh); }}/* * save our history. * We check that we do not have more than we are allowed. * If the history file is read-only we do nothing. * Handy for having all shells start with a useful history set. */voidhist_finish(){ static int once; FILE *fh; register int i; register char **hp; if (once++) return; /* check how many we have */ i = histptr - history; if (i >= histsize) hp = &histptr[-histsize]; else hp = history; if (hname && (fh = fopen(hname, "w"))) { for (i = 0; hp + i <= histptr && hp[i]; i++) fprintf(fh, "%s%c", hp[i], '\0'); fclose(fh); }}# else /* EASY_HISTORY *//* * Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to * a) permit HISTSIZE to control number of lines of history stored * b) maintain a physical history file * * It turns out that there is a lot of ghastly hackery here *//* * save command in history */voidhistsave(lno, cmd, dowrite) int lno; const char *cmd; int dowrite;{ register char **hp; char *c, *cp; c = str_save(cmd, APERM); if ((cp = strchr(c, '\n')) != NULL) *cp = '\0'; if (histfd && dowrite) writehistfile(lno, c); hp = histptr; if (++hp >= history + histsize) { /* remove oldest command */ afree((void*)*history, APERM); for (hp = history; hp < history + histsize - 1; hp++) hp[0] = hp[1]; } *hp = c; histptr = hp;}/* * Write history data to a file nominated by HISTFILE * if HISTFILE is unset then history still happens, but * the data is not written to a file * All copies of ksh looking at the file will maintain the * same history. This is ksh behaviour. * * This stuff uses mmap() * if your system ain't got it - then you'll have to undef HISTORYFILE *//* * Open a history file * Format is: * Bytes 1, 2: HMAGIC - just to check that we are dealing with * the correct object * Then follows a number of stored commands * Each command is * <command byte><command number(4 bytes)><bytes><null> */# define HMAGIC1 0xab# define HMAGIC2 0xcd# define COMMAND 0xffvoidhist_init(s) Source *s;{ unsigned char *base; int lines; int fd; if (Flag(FTALKING) == 0) return; hstarted = 1; hist_source = s; hname = str_val(global("HISTFILE")); if (hname == NULL) return; hname = str_save(hname, APERM); retry: /* we have a file and are interactive */ if ((fd = open(hname, O_RDWR|O_CREAT|O_APPEND, 0600)) < 0) return; histfd = savefd(fd, 0); (void) flock(histfd, LOCK_EX); hsize = lseek(histfd, 0L, SEEK_END); if (hsize == 0) { /* add magic */ if (sprinkle(histfd)) { hist_finish(); return; } } else if (hsize > 0) { /* * we have some data */ base = (unsigned char *)mmap(0, hsize, PROT_READ, MAP_FLAGS, histfd, 0); /* * check on its validity */ if ((int)base == -1 || *base != HMAGIC1 || base[1] != HMAGIC2) { if ((int)base != -1) munmap((caddr_t)base, hsize); hist_finish(); unlink(hname); goto retry; } if (hsize > 2) { lines = hist_count_lines(base+2, hsize-2); if (lines > histsize) { /* we need to make the file smaller */ if (hist_shrink(base, hsize)) unlink(hname); munmap((caddr_t)base, hsize); hist_finish(); goto retry; } } histload(hist_source, base+2, hsize-2); munmap((caddr_t)base, hsize); } (void) flock(histfd, LOCK_UN); hsize = lseek(histfd, 0L, SEEK_END);}typedef enum state { shdr, /* expecting a header */ sline, /* looking for a null byte to end the line */ sn1, /* bytes 1 to 4 of a line no */ sn2, sn3, sn4,} State;static inthist_count_lines(base, bytes) register unsigned char *base; register int bytes;{ State state = shdr; register lines = 0; while (bytes--) { switch (state) { case shdr: if (*base == COMMAND) state = sn1; break; case sn1: state = sn2; break; case sn2: state = sn3; break; case sn3: state = sn4; break; case sn4: state = sline; break; case sline: if (*base == '\0') lines++, state = shdr; } base++; } return lines;}/* * Shrink the history file to histsize lines */static inthist_shrink(oldbase, oldbytes) unsigned char *oldbase; int oldbytes;{ int fd; char nfile[1024]; struct stat statb; unsigned char *nbase = oldbase; int nbytes = oldbytes; nbase = hist_skip_back(nbase, &nbytes, histsize); if (nbase == NULL) return 1; if (nbase == oldbase) return 0; /* * create temp file */ (void) shf_snprintf(nfile, sizeof(nfile), "%s.%d", hname, procpid); if ((fd = creat(nfile, 0600)) < 0) return 1; if (sprinkle(fd)) { close(fd); unlink(nfile); return 1; } if (write(fd, nbase, nbytes) != nbytes) { close(fd); unlink(nfile); return 1; } /* * worry about who owns this file */ if (fstat(histfd, &statb) >= 0) fchown(fd, statb.st_uid, statb.st_gid); close(fd); /* * rename */ if (rename(nfile, hname) < 0) return 1; return 0;}/* * find a pointer to the data `no' back from the end of the file * return the pointer and the number of bytes left */static unsigned char *hist_skip_back(base, bytes, no) unsigned char *base; int *bytes; int no;{ register int lines = 0; register unsigned char *ep; for (ep = base + *bytes; --ep > base; ) { /* this doesn't really work: the 4 byte line number that is * encoded after the COMMAND byte can itself contain the * COMMAND byte.... */ for (; ep > base && *ep != COMMAND; ep--) ; if (ep == base) break; if (++lines == no) { *bytes = *bytes - ((char *)ep - (char *)base); return ep; } } return NULL;}/* * load the history structure from the stored data */static voidhistload(s, base, bytes) Source *s; register unsigned char *base; register int bytes;{ State state; int lno; unsigned char *line; for (state = shdr; bytes-- > 0; base++) { switch (state) { case shdr: if (*base == COMMAND) state = sn1; break; case sn1: lno = (((*base)&0xff)<<24); state = sn2; break; case sn2: lno |= (((*base)&0xff)<<16); state = sn3; break; case sn3: lno |= (((*base)&0xff)<<8); state = sn4; break; case sn4: lno |= (*base)&0xff; line = base+1; state = sline; break; case sline: if (*base == '\0') { /* worry about line numbers */ if (histptr >= history && lno-1 != s->line) { /* a replacement ? */ histinsert(s, lno, line); } else { s->line = lno; histsave(lno, (char *)line, 0); } state = shdr; } } }}/* * Insert a line into the history at a specified number */static voidhistinsert(s, lno, line) Source *s; int lno; unsigned char *line;{ register char **hp; if (lno >= s->line-(histptr-history) && lno <= s->line) { hp = &histptr[lno-s->line]; if (*hp) afree((void*)*hp, APERM); *hp = str_save((char *)line, APERM); }}/* * write a command to the end of the history file * This *MAY* seem easy but it's also necessary to check * that the history file has not changed in size. * If it has - then some other shell has written to it * and we should read those commands to update our history */static voidwritehistfile(lno, cmd) int lno; char *cmd;{ int sizenow; unsigned char *base; unsigned char *new; int bytes; char hdr[5]; (void) flock(histfd, LOCK_EX); sizenow = lseek(histfd, 0L, SEEK_END); if (sizenow != hsize) { /* * Things have changed */ if (sizenow > hsize) { /* someone has added some lines */ bytes = sizenow - hsize; base = (unsigned char *)mmap(0, sizenow, PROT_READ, MAP_FLAGS, histfd, 0); if ((int)base == -1) goto bad; new = base + hsize; if (*new != COMMAND) { munmap((caddr_t)base, sizenow); goto bad; } hist_source->line--; histload(hist_source, new, bytes); hist_source->line++; lno = hist_source->line; munmap((caddr_t)base, sizenow); hsize = sizenow; } else { /* it has shrunk */ /* but to what? */ /* we'll give up for now */ goto bad; } } /* * we can write our bit now */ hdr[0] = COMMAND; hdr[1] = (lno>>24)&0xff; hdr[2] = (lno>>16)&0xff; hdr[3] = (lno>>8)&0xff; hdr[4] = lno&0xff; (void) write(histfd, hdr, 5); (void) write(histfd, cmd, strlen(cmd)+1); hsize = lseek(histfd, 0L, SEEK_END); (void) flock(histfd, LOCK_UN); return;bad: hist_finish();}voidhist_finish(){ (void) flock(histfd, LOCK_UN); (void) close(histfd); histfd = 0;}/* * add magic to the history file */static intsprinkle(fd) int fd;{ static char mag[] = { HMAGIC1, HMAGIC2 }; return(write(fd, mag, 2) != 2);}# endif#else /* HISTORY *//* No history to be compiled in: dummy routines to avoid lots more ifdefs */voidinit_histvec(){}voidhist_init(s) Source *s;{}voidhist_finish(){}voidhistsave(lno, cmd, dowrite) int lno; const char *cmd; int dowrite;{ errorf("history not enabled");}#endif /* HISTORY */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -