// // Copy the last <n> commands to a new file and make this the history file. // static History_t *hist_trim(History_t *hp, int n) { char *cp; int incmd = 1, c = 0; History_t *hist_new, *hist_old = hp; char *buff, *endbuff, *tmpname = NULL; off_t oldp, newp; struct stat statb; unlink(hist_old->histname); if (access(hist_old->histname, F_OK) >= 0) { // The unlink can fail on windows 95. int fd; char *last, *name = hist_old->histname; sh_close(sffileno(hist_old->histfp)); last = strrchr(name, '/'); if (last) { *last = 0; tmpname = ast_temp_file(name, "hist", &fd, 0); *last = '/'; } else { tmpname = ast_temp_file(".", "hist", &fd, 0); } if (!tmpname) { errormsg(SH_DICT, ERROR_exit(1), e_create, "hist"); __builtin_unreachable(); } close(fd); if (rename(name, tmpname) < 0) { free(tmpname); tmpname = name; } fd = open(tmpname, O_RDONLY | O_CLOEXEC); // What happens if this fails and returns -1? Coverity Scan #310940. (void)sfsetfd(hist_old->histfp, fd); if (tmpname == name) { free(tmpname); tmpname = NULL; } } hist_ptr = NULL; if (fstat(sffileno(hist_old->histfp), &statb) >= 0) { histinit = 1; histmode = statb.st_mode; } if (!sh_histinit(hp->histshell)) { // Use the old history file. hist_ptr = hist_old; return hist_ptr; } hist_new = hist_ptr; hist_ptr = hist_old; if (--n < 0) n = 0; newp = hist_seek(hist_old, ++n); while (1) { if (!incmd) { c = hist_ind(hist_new, ++hist_new->histind); hist_new->histcmds[c] = hist_new->histcnt; if (hist_new->histcnt > hist_new->histmarker + HIST_BSIZE / 2) { char locbuff[HIST_MARKSZ]; hist_marker(locbuff, hist_new->histind); sfwrite(hist_new->histfp, locbuff, HIST_MARKSZ); hist_new->histcnt += HIST_MARKSZ; hist_new->histmarker = hist_new->histcmds[hist_ind(hist_new, c)] = hist_new->histcnt; } oldp = newp; newp = hist_seek(hist_old, ++n); if (newp <= oldp) break; } if (!(buff = (char *)sfreserve(hist_old->histfp, SF_UNBOUND, 0))) break; *(endbuff = (cp = buff) + sfvalue(hist_old->histfp)) = 0; // Copy to null byte. incmd = 0; cp += strlen(cp) + 1; // point past the terminating null if (cp > endbuff) { incmd = 1; } else if (*cp == 0) { cp++; } if (cp > endbuff) cp = endbuff; c = cp - buff; hist_new->histcnt += c; sfwrite(hist_new->histfp, buff, c); } hist_cancel(hist_new); sfclose(hist_old->histfp); if (tmpname) { unlink(tmpname); free(tmpname); } free(hist_old); hist_ptr = hist_new; return hist_ptr; }
// // This routine reads the history file from the present position to the end-of-file and puts the // information in the in-core history table. Note that HIST_CMDNO is only recognized at the // beginning of a command and that HIST_UNDO as the first character of a command is skipped unless // it is followed by 0. If followed by 0 then it cancels the previous command. // void hist_eof(History_t *hp) { char *cp, *first, *endbuff; int n; int incmd = 0; int skip = 0; int oldind = hp->histind; off_t count = hp->histcnt; off_t last = sfseek(hp->histfp, 0, SEEK_END); if (last < count) { last = -1; count = 2 + HIST_MARKSZ; if ((hp->histind -= hp->histsize) < 0) hp->histind = 1; } again: sfseek(hp->histfp, count, SEEK_SET); while ((cp = (char *)sfreserve(hp->histfp, SF_UNBOUND, 0))) { n = sfvalue(hp->histfp); endbuff = cp + n; first = cp += skip; while (1) { while (!incmd) { if (cp > first) { count += (cp - first); n = hist_ind(hp, ++hp->histind); #if 0 // TODO: Figure out if this should be enabled. Originally excluded via `#ifdef future`. if (count == hp->histcmds[n]) { sfprintf(sfstderr, "count match n=%d\n", n); if (histinit) { histinit = 0; return; } } else if (n >= histinit) #endif hp->histcmds[n] = count; first = cp; } switch (*((unsigned char *)(cp++))) { case HIST_CMDNO: { if (*cp == 0) { hp->histmarker = count + 2; cp += (HIST_MARKSZ - 1); hp->histind--; if (!histinit && (cp <= endbuff)) { unsigned char *marker = (unsigned char *)(cp - 4); hp->histind = ((marker[0] << 16) | (marker[1] << 8) | (marker[2] - 1)); } } break; } case HIST_UNDO: { if (*cp == 0) { cp += 1; hp->histind -= 2; } break; } default: { cp--; incmd = 1; } } if (cp >= endbuff) { goto refill; } } first = cp; while (*cp) { if (++cp >= endbuff) goto refill; } incmd = 0; while (*cp == 0) { if (++cp >= endbuff) goto refill; } } refill: count += (cp - first); skip = (cp - endbuff); if (!incmd && !skip) hp->histcmds[hist_ind(hp, ++hp->histind)] = count; } hp->histcnt = count; if (incmd && last) { sfputc(hp->histfp, 0); hist_cancel(hp); count = 2; skip = 0; oldind -= hp->histind; hp->histind = hp->histind - hp->histsize + oldind + 2; if (hp->histind < 0) hp->histind = 1; if (last < 0) { char buff[HIST_MARKSZ]; int fd = open(hp->histname, O_RDWR | O_CLOEXEC); if (fd >= 0) { hist_marker(buff, hp->histind); write(fd, (char *)hist_stamp, 2); write(fd, buff, HIST_MARKSZ); sh_close(fd); } } last = 0; goto again; } }
int b_hist(int argc,char *argv[], void *extra) { register History_t *hp; register char *arg; register int flag,fdo; register Shell_t *shp = ((Shbltin_t*)extra)->shp; Sfio_t *outfile; char *fname; int range[2], incr, index2, indx= -1; char *edit = 0; /* name of editor */ char *replace = 0; /* replace old=new */ int lflag = 0, nflag = 0, rflag = 0; #if SHOPT_HISTEXPAND int pflag = 0; #endif Histloc_t location; NOT_USED(argc); if(!sh_histinit((void*)shp)) errormsg(SH_DICT,ERROR_system(1),e_histopen); hp = shp->gd->hist_ptr; while((flag = optget(argv,sh_opthist))) switch(flag) { case 'e': edit = opt_info.arg; break; case 'n': nflag++; break; case 'l': lflag++; break; case 'r': rflag++; break; case 's': edit = "-"; break; #if SHOPT_HISTEXPAND case 'p': pflag++; break; #endif case 'N': if(indx<=0) { if((flag = hist_max(hp) - opt_info.num-1) < 0) flag = 1; range[++indx] = flag; break; } case ':': errormsg(SH_DICT,2, "%s", opt_info.arg); break; case '?': errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); break; } if(error_info.errors) errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); argv += (opt_info.index-1); #if SHOPT_HISTEXPAND if(pflag) { hist_cancel(hp); pflag = 0; while(arg=argv[1]) { flag = hist_expand(arg,&replace); if(!(flag & HIST_ERROR)) sfputr(sfstdout, replace, '\n'); else pflag = 1; if(replace) free(replace); argv++; } return pflag; } #endif flag = indx; while(flag<1 && (arg=argv[1])) { /* look for old=new argument */ if(!replace && strchr(arg+1,'=')) { replace = arg; argv++; continue; } else if(isdigit(*arg) || *arg == '-') { /* see if completely numeric */ do arg++; while(isdigit(*arg)); if(*arg==0) { arg = argv[1]; range[++flag] = (int)strtol(arg, (char**)0, 10); if(*arg == '-') range[flag] += (hist_max(hp)-1); argv++; continue; } } /* search for last line starting with string */ location = hist_find(hp,argv[1],hist_max(hp)-1,0,-1); if((range[++flag] = location.hist_command) < 0) errormsg(SH_DICT,ERROR_exit(1),e_found,argv[1]); argv++; } if(flag <0) { /* set default starting range */ if(lflag) { flag = hist_max(hp)-16; if(flag<1) flag = 1; } else flag = hist_max(hp)-2; range[0] = flag; flag = 0; } index2 = hist_min(hp); if(range[0]<index2) range[0] = index2; if(flag==0) /* set default termination range */ range[1] = ((lflag && !edit)?hist_max(hp)-1:range[0]); if(range[1]>=(flag=(hist_max(hp) - !lflag))) range[1] = flag; /* check for valid ranges */ if(range[1]<index2 || range[0]>=flag) errormsg(SH_DICT,ERROR_exit(1),e_badrange,range[0],range[1]); if(edit && *edit=='-' && range[0]!=range[1]) errormsg(SH_DICT,ERROR_exit(1),e_eneedsarg); /* now list commands from range[rflag] to range[1-rflag] */ incr = 1; flag = rflag>0; if(range[1-flag] < range[flag]) incr = -1; if(lflag) { outfile = sfstdout; arg = "\n\t"; } else { if(!(fname=pathtmp(NIL(char*),0,0,NIL(int*)))) errormsg(SH_DICT,ERROR_exit(1),e_create,""); if((fdo=open(fname,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) < 0) errormsg(SH_DICT,ERROR_system(1),e_create,fname); outfile= sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fdo,SF_WRITE); arg = "\n"; nflag++; } while(1) { if(nflag==0) sfprintf(outfile,"%d\t",range[flag]); else if(lflag) sfputc(outfile,'\t'); hist_list(shp->gd->hist_ptr,outfile,hist_tell(shp->gd->hist_ptr,range[flag]),0,arg); if(lflag) sh_sigcheck(shp); if(range[flag] == range[1-flag]) break; range[flag] += incr; } if(lflag) return(0); sfclose(outfile); hist_eof(hp); arg = edit; if(!arg && !(arg=nv_getval(sh_scoped(shp,HISTEDIT))) && !(arg=nv_getval(sh_scoped(shp,FCEDNOD)))) arg = (char*)e_defedit; #ifdef apollo /* * Code to support the FC using the pad editor. * Exampled of how to use: HISTEDIT=pad */ if (strcmp (arg, "pad") == 0) { extern int pad_create(char*); sh_close(fdo); fdo = pad_create(fname); pad_wait(fdo); unlink(fname); strcat(fname, ".bak"); unlink(fname); lseek(fdo,(off_t)0,SEEK_SET); } else { #endif /* apollo */ if(*arg != '-') { char *com[3]; com[0] = arg; com[1] = fname; com[2] = 0; error_info.errors = sh_eval(sh_sfeval(com),0); } fdo = sh_chkopen(fname); unlink(fname); free((void*)fname); #ifdef apollo } #endif /* apollo */ /* don't history fc itself unless forked */ error_info.flags |= ERROR_SILENT; if(!sh_isstate(SH_FORKED)) hist_cancel(hp); sh_onstate(SH_HISTORY); sh_onstate(SH_VERBOSE); /* echo lines as read */ if(replace) hist_subst(error_info.id,fdo,replace); else if(error_info.errors == 0) { char buff[IOBSIZE+1]; Sfio_t *iop = sfnew(NIL(Sfio_t*),buff,IOBSIZE,fdo,SF_READ); /* read in and run the command */ if(shp->hist_depth++ > HIST_RECURSE) errormsg(SH_DICT,ERROR_exit(1),e_toodeep,"history"); sh_eval(iop,1); shp->hist_depth--; }