1*4887Schin /*********************************************************************** 2*4887Schin * * 3*4887Schin * This software is part of the ast package * 4*4887Schin * Copyright (c) 1982-2007 AT&T Knowledge Ventures * 5*4887Schin * and is licensed under the * 6*4887Schin * Common Public License, Version 1.0 * 7*4887Schin * by AT&T Knowledge Ventures * 8*4887Schin * * 9*4887Schin * A copy of the License is available at * 10*4887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 11*4887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12*4887Schin * * 13*4887Schin * Information and Software Systems Research * 14*4887Schin * AT&T Research * 15*4887Schin * Florham Park NJ * 16*4887Schin * * 17*4887Schin * David Korn <dgk@research.att.com> * 18*4887Schin * * 19*4887Schin ***********************************************************************/ 20*4887Schin #pragma prototyped 21*4887Schin /* 22*4887Schin * History file manipulation routines 23*4887Schin * 24*4887Schin * David Korn 25*4887Schin * AT&T Labs 26*4887Schin * 27*4887Schin */ 28*4887Schin 29*4887Schin /* 30*4887Schin * Each command in the history file starts on an even byte is null terminated. 31*4887Schin * The first byte must contain the special character HIST_UNDO and the second 32*4887Schin * byte is the version number. The sequence HIST_UNDO 0, following a command, 33*4887Schin * nullifies the previous command. A six byte sequence starting with 34*4887Schin * HIST_CMDNO is used to store the command number so that it is not necessary 35*4887Schin * to read the file from beginning to end to get to the last block of 36*4887Schin * commands. This format of this sequence is different in version 1 37*4887Schin * then in version 0. Version 1 allows commands to use the full 8 bit 38*4887Schin * character set. It can understand version 0 format files. 39*4887Schin */ 40*4887Schin 41*4887Schin 42*4887Schin #define HIST_MAX (sizeof(int)*HIST_BSIZE) 43*4887Schin #define HIST_BIG (0100000-1024) /* 1K less than maximum short */ 44*4887Schin #define HIST_LINE 32 /* typical length for history line */ 45*4887Schin #define HIST_MARKSZ 6 46*4887Schin #define HIST_RECENT 600 47*4887Schin #define HIST_UNDO 0201 /* invalidate previous command */ 48*4887Schin #define HIST_CMDNO 0202 /* next 3 bytes give command number */ 49*4887Schin #define HIST_BSIZE 4096 /* size of history file buffer */ 50*4887Schin #define HIST_DFLT 512 /* default size of history list */ 51*4887Schin 52*4887Schin #define _HIST_PRIVATE \ 53*4887Schin off_t histcnt; /* offset into history file */\ 54*4887Schin off_t histmarker; /* offset of last command marker */ \ 55*4887Schin int histflush; /* set if flushed outside of hflush() */\ 56*4887Schin int histmask; /* power of two mask for histcnt */ \ 57*4887Schin char histbuff[HIST_BSIZE+1]; /* history file buffer */ \ 58*4887Schin int histwfail; \ 59*4887Schin off_t histcmds[2]; /* offset for recent commands, must be last */ 60*4887Schin 61*4887Schin #define hist_ind(hp,c) ((int)((c)&(hp)->histmask)) 62*4887Schin 63*4887Schin #include <ast.h> 64*4887Schin #include <sfio.h> 65*4887Schin #include "FEATURE/time" 66*4887Schin #include <error.h> 67*4887Schin #include <ctype.h> 68*4887Schin #include <ls.h> 69*4887Schin #if KSHELL 70*4887Schin # include "defs.h" 71*4887Schin # include "variables.h" 72*4887Schin # include "path.h" 73*4887Schin # include "builtins.h" 74*4887Schin # include "io.h" 75*4887Schin #endif /* KSHELL */ 76*4887Schin #include "history.h" 77*4887Schin 78*4887Schin #if !KSHELL 79*4887Schin # define new_of(type,x) ((type*)malloc((unsigned)sizeof(type)+(x))) 80*4887Schin # define NIL(type) ((type)0) 81*4887Schin # define path_relative(x) (x) 82*4887Schin # ifdef __STDC__ 83*4887Schin # define nv_getval(s) getenv(#s) 84*4887Schin # else 85*4887Schin # define nv_getval(s) getenv("s") 86*4887Schin # endif /* __STDC__ */ 87*4887Schin # define e_unknown "unknown" 88*4887Schin # define sh_translate(x) (x) 89*4887Schin char login_sh = 0; 90*4887Schin char hist_fname[] = "/.history"; 91*4887Schin #endif /* KSHELL */ 92*4887Schin 93*4887Schin #ifndef O_BINARY 94*4887Schin # define O_BINARY 0 95*4887Schin #endif /* O_BINARY */ 96*4887Schin 97*4887Schin int _Hist = 0; 98*4887Schin static void hist_marker(char*,long); 99*4887Schin static void hist_trim(History_t*, int); 100*4887Schin static int hist_nearend(History_t*,Sfio_t*, off_t); 101*4887Schin static int hist_check(int); 102*4887Schin static int hist_clean(int); 103*4887Schin #ifdef SF_BUFCONST 104*4887Schin static ssize_t hist_write(Sfio_t*, const void*, size_t, Sfdisc_t*); 105*4887Schin static int hist_exceptf(Sfio_t*, int, void*, Sfdisc_t*); 106*4887Schin #else 107*4887Schin static int hist_write(Sfio_t*, const void*, int, Sfdisc_t*); 108*4887Schin static int hist_exceptf(Sfio_t*, int, Sfdisc_t*); 109*4887Schin #endif 110*4887Schin 111*4887Schin 112*4887Schin static int histinit; 113*4887Schin static mode_t histmode; 114*4887Schin static History_t *wasopen; 115*4887Schin static History_t *hist_ptr; 116*4887Schin 117*4887Schin #if SHOPT_ACCTFILE 118*4887Schin static int acctfd; 119*4887Schin static char *logname; 120*4887Schin # include <pwd.h> 121*4887Schin 122*4887Schin int acctinit(void) 123*4887Schin { 124*4887Schin register char *cp, *acctfile; 125*4887Schin Namval_t *np = nv_search("ACCTFILE",sh.var_tree,0); 126*4887Schin 127*4887Schin if(!np || !(acctfile=nv_getval(np))) 128*4887Schin return(0); 129*4887Schin if(!(cp = getlogin())) 130*4887Schin { 131*4887Schin struct passwd *userinfo = getpwuid(getuid()); 132*4887Schin if(userinfo) 133*4887Schin cp = userinfo->pw_name; 134*4887Schin else 135*4887Schin cp = "unknown"; 136*4887Schin } 137*4887Schin logname = strdup(cp); 138*4887Schin 139*4887Schin if((acctfd=sh_open(acctfile, 140*4887Schin O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 && 141*4887Schin (unsigned)acctfd < 10) 142*4887Schin { 143*4887Schin int n; 144*4887Schin if((n = fcntl(acctfd, F_DUPFD, 10)) >= 0) 145*4887Schin { 146*4887Schin close(acctfd); 147*4887Schin acctfd = n; 148*4887Schin } 149*4887Schin } 150*4887Schin if(acctfd < 0) 151*4887Schin { 152*4887Schin acctfd = 0; 153*4887Schin return(0); 154*4887Schin } 155*4887Schin if(strmatch(acctfile,e_devfdNN)) 156*4887Schin { 157*4887Schin char newfile[16]; 158*4887Schin sfsprintf(newfile,sizeof(newfile),"%.8s%d\0",e_devfdNN,acctfd); 159*4887Schin nv_putval(np,newfile,NV_RDONLY); 160*4887Schin } 161*4887Schin else 162*4887Schin fcntl(acctfd,F_SETFD,FD_CLOEXEC); 163*4887Schin return(1); 164*4887Schin } 165*4887Schin #endif /* SHOPT_ACCTFILE */ 166*4887Schin 167*4887Schin static const unsigned char hist_stamp[2] = { HIST_UNDO, HIST_VERSION }; 168*4887Schin static const Sfdisc_t hist_disc = { NULL, hist_write, NULL, hist_exceptf, NULL}; 169*4887Schin 170*4887Schin static void hist_touch(void *handle) 171*4887Schin { 172*4887Schin touch((char*)handle, (time_t)0, (time_t)0, 0); 173*4887Schin } 174*4887Schin 175*4887Schin /* 176*4887Schin * open the history file 177*4887Schin * if HISTNAME is not given and userid==0 then no history file. 178*4887Schin * if login_sh and HISTFILE is longer than HIST_MAX bytes then it is 179*4887Schin * cleaned up. 180*4887Schin * hist_open() returns 1, if history file is open 181*4887Schin */ 182*4887Schin int sh_histinit(void) 183*4887Schin { 184*4887Schin register int fd; 185*4887Schin register History_t *hp; 186*4887Schin register char *histname; 187*4887Schin char *fname=0; 188*4887Schin int histmask, maxlines, hist_start=0; 189*4887Schin register char *cp; 190*4887Schin register off_t hsize = 0; 191*4887Schin 192*4887Schin if(sh.hist_ptr=hist_ptr) 193*4887Schin return(1); 194*4887Schin if(!(histname = nv_getval(HISTFILE))) 195*4887Schin { 196*4887Schin int offset = staktell(); 197*4887Schin if(cp=nv_getval(HOME)) 198*4887Schin stakputs(cp); 199*4887Schin stakputs(hist_fname); 200*4887Schin stakputc(0); 201*4887Schin stakseek(offset); 202*4887Schin histname = stakptr(offset); 203*4887Schin } 204*4887Schin #ifdef future 205*4887Schin if(hp=wasopen) 206*4887Schin { 207*4887Schin /* reuse history file if same name */ 208*4887Schin wasopen = 0; 209*4887Schin sh.hist_ptr = hist_ptr = hp; 210*4887Schin if(strcmp(histname,hp->histname)==0) 211*4887Schin return(1); 212*4887Schin else 213*4887Schin hist_free(); 214*4887Schin } 215*4887Schin #endif 216*4887Schin retry: 217*4887Schin cp = path_relative(histname); 218*4887Schin if(!histinit) 219*4887Schin histmode = S_IRUSR|S_IWUSR; 220*4887Schin if((fd=open(cp,O_BINARY|O_APPEND|O_RDWR|O_CREAT,histmode))>=0) 221*4887Schin { 222*4887Schin hsize=lseek(fd,(off_t)0,SEEK_END); 223*4887Schin } 224*4887Schin if((unsigned)fd <=2) 225*4887Schin { 226*4887Schin int n; 227*4887Schin if((n=fcntl(fd,F_DUPFD,10))>=0) 228*4887Schin { 229*4887Schin close(fd); 230*4887Schin fd=n; 231*4887Schin } 232*4887Schin } 233*4887Schin /* make sure that file has history file format */ 234*4887Schin if(hsize && hist_check(fd)) 235*4887Schin { 236*4887Schin close(fd); 237*4887Schin hsize = 0; 238*4887Schin if(unlink(cp)>=0) 239*4887Schin goto retry; 240*4887Schin fd = -1; 241*4887Schin } 242*4887Schin if(fd < 0) 243*4887Schin { 244*4887Schin #if KSHELL 245*4887Schin /* don't allow root a history_file in /tmp */ 246*4887Schin if(sh.userid) 247*4887Schin #endif /* KSHELL */ 248*4887Schin { 249*4887Schin if(!(fname = pathtmp(NIL(char*),0,0,NIL(int*)))) 250*4887Schin return(0); 251*4887Schin fd = open(fname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR); 252*4887Schin } 253*4887Schin } 254*4887Schin if(fd<0) 255*4887Schin return(0); 256*4887Schin /* set the file to close-on-exec */ 257*4887Schin fcntl(fd,F_SETFD,FD_CLOEXEC); 258*4887Schin if(cp=nv_getval(HISTSIZE)) 259*4887Schin maxlines = (unsigned)strtol(cp, (char**)0, 10); 260*4887Schin else 261*4887Schin maxlines = HIST_DFLT; 262*4887Schin for(histmask=16;histmask <= maxlines; histmask <<=1 ); 263*4887Schin if(!(hp=new_of(History_t,(--histmask)*sizeof(off_t)))) 264*4887Schin { 265*4887Schin close(fd); 266*4887Schin return(0); 267*4887Schin } 268*4887Schin sh.hist_ptr = hist_ptr = hp; 269*4887Schin hp->histsize = maxlines; 270*4887Schin hp->histmask = histmask; 271*4887Schin hp->histfp= sfnew(NIL(Sfio_t*),hp->histbuff,HIST_BSIZE,fd,SF_READ|SF_WRITE|SF_APPENDWR|SF_SHARE); 272*4887Schin memset((char*)hp->histcmds,0,sizeof(off_t)*(hp->histmask+1)); 273*4887Schin hp->histind = 1; 274*4887Schin hp->histcmds[1] = 2; 275*4887Schin hp->histcnt = 2; 276*4887Schin hp->histname = strdup(histname); 277*4887Schin hp->histdisc = hist_disc; 278*4887Schin if(hsize==0) 279*4887Schin { 280*4887Schin /* put special characters at front of file */ 281*4887Schin sfwrite(hp->histfp,(char*)hist_stamp,2); 282*4887Schin sfsync(hp->histfp); 283*4887Schin } 284*4887Schin /* initialize history list */ 285*4887Schin else 286*4887Schin { 287*4887Schin int first,last; 288*4887Schin off_t mark,size = (HIST_MAX/4)+maxlines*HIST_LINE; 289*4887Schin hp->histind = first = hist_nearend(hp,hp->histfp,hsize-size); 290*4887Schin hist_eof(hp); /* this sets histind to last command */ 291*4887Schin if((hist_start = (last=(int)hp->histind)-maxlines) <=0) 292*4887Schin hist_start = 1; 293*4887Schin mark = hp->histmarker; 294*4887Schin while(first > hist_start) 295*4887Schin { 296*4887Schin size += size; 297*4887Schin first = hist_nearend(hp,hp->histfp,hsize-size); 298*4887Schin hp->histind = first; 299*4887Schin } 300*4887Schin histinit = hist_start; 301*4887Schin hist_eof(hp); 302*4887Schin if(!histinit) 303*4887Schin { 304*4887Schin sfseek(hp->histfp,hp->histcnt=hsize,SEEK_SET); 305*4887Schin hp->histind = last; 306*4887Schin hp->histmarker = mark; 307*4887Schin } 308*4887Schin histinit = 0; 309*4887Schin } 310*4887Schin if(fname) 311*4887Schin { 312*4887Schin unlink(fname); 313*4887Schin free((void*)fname); 314*4887Schin } 315*4887Schin if(hist_clean(fd) && hist_start>1 && hsize > HIST_MAX) 316*4887Schin { 317*4887Schin #ifdef DEBUG 318*4887Schin sfprintf(sfstderr,"%d: hist_trim hsize=%d\n",getpid(),hsize); 319*4887Schin sfsync(sfstderr); 320*4887Schin #endif /* DEBUG */ 321*4887Schin hist_trim(hp,(int)hp->histind-maxlines); 322*4887Schin } 323*4887Schin sfdisc(hp->histfp,&hp->histdisc); 324*4887Schin #if KSHELL 325*4887Schin (HISTCUR)->nvalue.lp = (&hp->histind); 326*4887Schin #endif /* KSHELL */ 327*4887Schin sh_timeradd(1000L*(HIST_RECENT-30), 1, hist_touch, (void*)hp->histname); 328*4887Schin #if SHOPT_ACCTFILE 329*4887Schin if(sh_isstate(SH_INTERACTIVE)) 330*4887Schin acctinit(); 331*4887Schin #endif /* SHOPT_ACCTFILE */ 332*4887Schin return(1); 333*4887Schin } 334*4887Schin 335*4887Schin /* 336*4887Schin * close the history file and free the space 337*4887Schin */ 338*4887Schin 339*4887Schin void hist_close(register History_t *hp) 340*4887Schin { 341*4887Schin sfclose(hp->histfp); 342*4887Schin free((char*)hp); 343*4887Schin hist_ptr = 0; 344*4887Schin sh.hist_ptr = 0; 345*4887Schin #if SHOPT_ACCTFILE 346*4887Schin if(acctfd) 347*4887Schin { 348*4887Schin close(acctfd); 349*4887Schin acctfd = 0; 350*4887Schin } 351*4887Schin #endif /* SHOPT_ACCTFILE */ 352*4887Schin } 353*4887Schin 354*4887Schin /* 355*4887Schin * check history file format to see if it begins with special byte 356*4887Schin */ 357*4887Schin static int hist_check(register int fd) 358*4887Schin { 359*4887Schin unsigned char magic[2]; 360*4887Schin lseek(fd,(off_t)0,SEEK_SET); 361*4887Schin if((read(fd,(char*)magic,2)!=2) || (magic[0]!=HIST_UNDO)) 362*4887Schin return(1); 363*4887Schin return(0); 364*4887Schin } 365*4887Schin 366*4887Schin /* 367*4887Schin * clean out history file OK if not modified in HIST_RECENT seconds 368*4887Schin */ 369*4887Schin static int hist_clean(int fd) 370*4887Schin { 371*4887Schin struct stat statb; 372*4887Schin return(fstat(fd,&statb)>=0 && (time((time_t*)0)-statb.st_mtime) >= HIST_RECENT); 373*4887Schin } 374*4887Schin 375*4887Schin /* 376*4887Schin * Copy the last <n> commands to a new file and make this the history file 377*4887Schin */ 378*4887Schin 379*4887Schin static void hist_trim(History_t *hp, int n) 380*4887Schin { 381*4887Schin register char *cp; 382*4887Schin register int incmd=1, c=0; 383*4887Schin register History_t *hist_new, *hist_old = hp; 384*4887Schin char *buff, *endbuff, *tmpname=0; 385*4887Schin off_t oldp,newp; 386*4887Schin struct stat statb; 387*4887Schin unlink(hist_old->histname); 388*4887Schin if(access(hist_old->histname,F_OK) >= 0) 389*4887Schin { 390*4887Schin /* The unlink can fail on windows 95 */ 391*4887Schin int fd; 392*4887Schin char *last, *name=hist_old->histname; 393*4887Schin close(sffileno(hist_old->histfp)); 394*4887Schin tmpname = (char*)malloc(strlen(name)+14); 395*4887Schin if(last = strrchr(name,'/')) 396*4887Schin { 397*4887Schin *last = 0; 398*4887Schin pathtmp(tmpname,name,"hist",NIL(int*)); 399*4887Schin *last = '/'; 400*4887Schin } 401*4887Schin else 402*4887Schin pathtmp(tmpname,".","hist",NIL(int*)); 403*4887Schin if(rename(name,tmpname) < 0) 404*4887Schin tmpname = name; 405*4887Schin fd = open(tmpname,O_RDONLY); 406*4887Schin sfsetfd(hist_old->histfp,fd); 407*4887Schin if(tmpname==name) 408*4887Schin tmpname = 0; 409*4887Schin } 410*4887Schin hp = hist_ptr = 0; 411*4887Schin if(fstat(sffileno(hist_old->histfp),&statb)>=0) 412*4887Schin { 413*4887Schin histinit = 1; 414*4887Schin histmode = statb.st_mode; 415*4887Schin } 416*4887Schin if(!sh_histinit()) 417*4887Schin { 418*4887Schin /* use the old history file */ 419*4887Schin hist_ptr = hist_old; 420*4887Schin return; 421*4887Schin } 422*4887Schin hist_new = hist_ptr; 423*4887Schin hist_ptr = hist_old; 424*4887Schin if(--n < 0) 425*4887Schin n = 0; 426*4887Schin newp = hist_seek(hist_old,++n); 427*4887Schin while(1) 428*4887Schin { 429*4887Schin if(!incmd) 430*4887Schin { 431*4887Schin c = hist_ind(hist_new,++hist_new->histind); 432*4887Schin hist_new->histcmds[c] = hist_new->histcnt; 433*4887Schin if(hist_new->histcnt > hist_new->histmarker+HIST_BSIZE/2) 434*4887Schin { 435*4887Schin char locbuff[HIST_MARKSZ]; 436*4887Schin hist_marker(locbuff,hist_new->histind); 437*4887Schin sfwrite(hist_new->histfp,locbuff,HIST_MARKSZ); 438*4887Schin hist_new->histcnt += HIST_MARKSZ; 439*4887Schin hist_new->histmarker = hist_new->histcmds[hist_ind(hist_new,c)] = hist_new->histcnt; 440*4887Schin } 441*4887Schin oldp = newp; 442*4887Schin newp = hist_seek(hist_old,++n); 443*4887Schin if(newp <=oldp) 444*4887Schin break; 445*4887Schin } 446*4887Schin if(!(buff=(char*)sfreserve(hist_old->histfp,SF_UNBOUND,0))) 447*4887Schin break; 448*4887Schin *(endbuff=(cp=buff)+sfvalue(hist_old->histfp)) = 0; 449*4887Schin /* copy to null byte */ 450*4887Schin incmd = 0; 451*4887Schin while(*cp++); 452*4887Schin if(cp > endbuff) 453*4887Schin incmd = 1; 454*4887Schin else if(*cp==0) 455*4887Schin cp++; 456*4887Schin if(cp > endbuff) 457*4887Schin cp = endbuff; 458*4887Schin c = cp-buff; 459*4887Schin hist_new->histcnt += c; 460*4887Schin sfwrite(hist_new->histfp,buff,c); 461*4887Schin } 462*4887Schin hist_ptr = hist_new; 463*4887Schin hist_cancel(hist_ptr); 464*4887Schin sfclose(hist_old->histfp); 465*4887Schin if(tmpname) 466*4887Schin { 467*4887Schin unlink(tmpname); 468*4887Schin free(tmpname); 469*4887Schin } 470*4887Schin free((char*)hist_old); 471*4887Schin } 472*4887Schin 473*4887Schin /* 474*4887Schin * position history file at size and find next command number 475*4887Schin */ 476*4887Schin static int hist_nearend(History_t *hp, Sfio_t *iop, register off_t size) 477*4887Schin { 478*4887Schin register unsigned char *cp, *endbuff; 479*4887Schin register int n, incmd=1; 480*4887Schin unsigned char *buff, marker[4]; 481*4887Schin if(size <= 2L || sfseek(iop,size,SEEK_SET)<0) 482*4887Schin goto begin; 483*4887Schin /* skip to marker command and return the number */ 484*4887Schin /* numbering commands occur after a null and begin with HIST_CMDNO */ 485*4887Schin while(cp=buff=(unsigned char*)sfreserve(iop,SF_UNBOUND,SF_LOCKR)) 486*4887Schin { 487*4887Schin n = sfvalue(iop); 488*4887Schin *(endbuff=cp+n) = 0; 489*4887Schin while(1) 490*4887Schin { 491*4887Schin /* check for marker */ 492*4887Schin if(!incmd && *cp++==HIST_CMDNO && *cp==0) 493*4887Schin { 494*4887Schin n = cp+1 - buff; 495*4887Schin incmd = -1; 496*4887Schin break; 497*4887Schin } 498*4887Schin incmd = 0; 499*4887Schin while(*cp++); 500*4887Schin if(cp>endbuff) 501*4887Schin { 502*4887Schin incmd = 1; 503*4887Schin break; 504*4887Schin } 505*4887Schin if(*cp==0 && ++cp>endbuff) 506*4887Schin break; 507*4887Schin } 508*4887Schin size += n; 509*4887Schin sfread(iop,(char*)buff,n); 510*4887Schin if(incmd < 0) 511*4887Schin { 512*4887Schin if((n=sfread(iop,(char*)marker,4))==4) 513*4887Schin { 514*4887Schin n = (marker[0]<<16)|(marker[1]<<8)|marker[2]; 515*4887Schin if(n < size/2) 516*4887Schin { 517*4887Schin hp->histmarker = hp->histcnt = size+4; 518*4887Schin return(n); 519*4887Schin } 520*4887Schin n=4; 521*4887Schin } 522*4887Schin if(n >0) 523*4887Schin size += n; 524*4887Schin incmd = 0; 525*4887Schin } 526*4887Schin } 527*4887Schin begin: 528*4887Schin sfseek(iop,(off_t)2,SEEK_SET); 529*4887Schin hp->histmarker = hp->histcnt = 2L; 530*4887Schin return(1); 531*4887Schin } 532*4887Schin 533*4887Schin /* 534*4887Schin * This routine reads the history file from the present position 535*4887Schin * to the end-of-file and puts the information in the in-core 536*4887Schin * history table 537*4887Schin * Note that HIST_CMDNO is only recognized at the beginning of a command 538*4887Schin * and that HIST_UNDO as the first character of a command is skipped 539*4887Schin * unless it is followed by 0. If followed by 0 then it cancels 540*4887Schin * the previous command. 541*4887Schin */ 542*4887Schin 543*4887Schin void hist_eof(register History_t *hp) 544*4887Schin { 545*4887Schin register char *cp,*first,*endbuff; 546*4887Schin register int incmd = 0; 547*4887Schin register off_t count = hp->histcnt; 548*4887Schin int n,skip=0; 549*4887Schin sfseek(hp->histfp,count,SEEK_SET); 550*4887Schin while(cp=(char*)sfreserve(hp->histfp,SF_UNBOUND,0)) 551*4887Schin { 552*4887Schin n = sfvalue(hp->histfp); 553*4887Schin *(endbuff = cp+n) = 0; 554*4887Schin first = cp += skip; 555*4887Schin while(1) 556*4887Schin { 557*4887Schin while(!incmd) 558*4887Schin { 559*4887Schin if(cp>first) 560*4887Schin { 561*4887Schin count += (cp-first); 562*4887Schin n = hist_ind(hp, ++hp->histind); 563*4887Schin #ifdef future 564*4887Schin if(count==hp->histcmds[n]) 565*4887Schin { 566*4887Schin sfprintf(sfstderr,"count match n=%d\n",n); 567*4887Schin if(histinit) 568*4887Schin { 569*4887Schin histinit = 0; 570*4887Schin return; 571*4887Schin } 572*4887Schin } 573*4887Schin else if(n>=histinit) 574*4887Schin #endif 575*4887Schin hp->histcmds[n] = count; 576*4887Schin first = cp; 577*4887Schin } 578*4887Schin switch(*((unsigned char*)(cp++))) 579*4887Schin { 580*4887Schin case HIST_CMDNO: 581*4887Schin if(*cp==0) 582*4887Schin { 583*4887Schin hp->histmarker=count+2; 584*4887Schin cp += (HIST_MARKSZ-1); 585*4887Schin hp->histind--; 586*4887Schin #ifdef future 587*4887Schin if(cp <= endbuff) 588*4887Schin { 589*4887Schin unsigned char *marker = (unsigned char*)(cp-4); 590*4887Schin int n = ((marker[0]<<16) 591*4887Schin |(marker[1]<<8)|marker[2]); 592*4887Schin if((n<count/2) && n != (hp->histind+1)) 593*4887Schin errormsg(SH_DICT,2,"index=%d marker=%d", hp->histind, n); 594*4887Schin } 595*4887Schin #endif 596*4887Schin } 597*4887Schin break; 598*4887Schin case HIST_UNDO: 599*4887Schin if(*cp==0) 600*4887Schin { 601*4887Schin cp+=1; 602*4887Schin hp->histind-=2; 603*4887Schin } 604*4887Schin break; 605*4887Schin default: 606*4887Schin cp--; 607*4887Schin incmd = 1; 608*4887Schin } 609*4887Schin if(cp > endbuff) 610*4887Schin { 611*4887Schin cp++; 612*4887Schin goto refill; 613*4887Schin } 614*4887Schin } 615*4887Schin first = cp; 616*4887Schin while(*cp++); 617*4887Schin if(cp > endbuff) 618*4887Schin break; 619*4887Schin incmd = 0; 620*4887Schin while(*cp==0) 621*4887Schin { 622*4887Schin if(++cp > endbuff) 623*4887Schin goto refill; 624*4887Schin } 625*4887Schin } 626*4887Schin refill: 627*4887Schin count += (--cp-first); 628*4887Schin skip = (cp-endbuff); 629*4887Schin if(!incmd && !skip) 630*4887Schin hp->histcmds[hist_ind(hp,++hp->histind)] = count; 631*4887Schin } 632*4887Schin hp->histcnt = count; 633*4887Schin } 634*4887Schin 635*4887Schin /* 636*4887Schin * This routine will cause the previous command to be cancelled 637*4887Schin */ 638*4887Schin 639*4887Schin void hist_cancel(register History_t *hp) 640*4887Schin { 641*4887Schin register int c; 642*4887Schin if(!hp) 643*4887Schin return; 644*4887Schin sfputc(hp->histfp,HIST_UNDO); 645*4887Schin sfputc(hp->histfp,0); 646*4887Schin sfsync(hp->histfp); 647*4887Schin hp->histcnt += 2; 648*4887Schin c = hist_ind(hp,--hp->histind); 649*4887Schin hp->histcmds[c] = hp->histcnt; 650*4887Schin } 651*4887Schin 652*4887Schin /* 653*4887Schin * flush the current history command 654*4887Schin */ 655*4887Schin 656*4887Schin void hist_flush(register History_t *hp) 657*4887Schin { 658*4887Schin register char *buff; 659*4887Schin if(hp) 660*4887Schin { 661*4887Schin if(buff=(char*)sfreserve(hp->histfp,0,SF_LOCKR)) 662*4887Schin { 663*4887Schin hp->histflush = sfvalue(hp->histfp)+1; 664*4887Schin sfwrite(hp->histfp,buff,0); 665*4887Schin } 666*4887Schin else 667*4887Schin hp->histflush=0; 668*4887Schin if(sfsync(hp->histfp)<0) 669*4887Schin { 670*4887Schin hist_close(hp); 671*4887Schin if(!sh_histinit()) 672*4887Schin sh_offoption(SH_HISTORY); 673*4887Schin } 674*4887Schin hp->histflush = 0; 675*4887Schin } 676*4887Schin } 677*4887Schin 678*4887Schin /* 679*4887Schin * This is the write discipline for the history file 680*4887Schin * When called from hist_flush(), trailing newlines are deleted and 681*4887Schin * a zero byte. Line sequencing is added as required 682*4887Schin */ 683*4887Schin 684*4887Schin #ifdef SF_BUFCONST 685*4887Schin static ssize_t hist_write(Sfio_t *iop,const void *buff,register size_t insize,Sfdisc_t* handle) 686*4887Schin #else 687*4887Schin static int hist_write(Sfio_t *iop,const void *buff,register int insize,Sfdisc_t* handle) 688*4887Schin #endif 689*4887Schin { 690*4887Schin register History_t *hp = (History_t*)handle; 691*4887Schin register char *bufptr = ((char*)buff)+insize; 692*4887Schin register int c,size = insize; 693*4887Schin register off_t cur; 694*4887Schin int saved=0; 695*4887Schin char saveptr[HIST_MARKSZ]; 696*4887Schin if(!hp->histflush) 697*4887Schin return(write(sffileno(iop),(char*)buff,size)); 698*4887Schin if((cur = lseek(sffileno(iop),(off_t)0,SEEK_END)) <0) 699*4887Schin { 700*4887Schin errormsg(SH_DICT,2,"hist_flush: EOF seek failed errno=%d",errno); 701*4887Schin return(-1); 702*4887Schin } 703*4887Schin hp->histcnt = cur; 704*4887Schin /* remove whitespace from end of commands */ 705*4887Schin while(--bufptr >= (char*)buff) 706*4887Schin { 707*4887Schin c= *bufptr; 708*4887Schin if(!isspace(c)) 709*4887Schin { 710*4887Schin if(c=='\\' && *(bufptr+1)!='\n') 711*4887Schin bufptr++; 712*4887Schin break; 713*4887Schin } 714*4887Schin } 715*4887Schin /* don't count empty lines */ 716*4887Schin if(++bufptr <= (char*)buff) 717*4887Schin return(insize); 718*4887Schin *bufptr++ = '\n'; 719*4887Schin *bufptr++ = 0; 720*4887Schin size = bufptr - (char*)buff; 721*4887Schin #if SHOPT_ACCTFILE 722*4887Schin if(acctfd) 723*4887Schin { 724*4887Schin int timechars, offset; 725*4887Schin offset = staktell(); 726*4887Schin stakputs(buff); 727*4887Schin stakseek(staktell() - 1); 728*4887Schin timechars = sfprintf(staksp, "\t%s\t%x\n",logname,time(NIL(long *))); 729*4887Schin lseek(acctfd, (off_t)0, SEEK_END); 730*4887Schin write(acctfd, stakptr(offset), size - 2 + timechars); 731*4887Schin stakseek(offset); 732*4887Schin 733*4887Schin } 734*4887Schin #endif /* SHOPT_ACCTFILE */ 735*4887Schin if(size&01) 736*4887Schin { 737*4887Schin size++; 738*4887Schin *bufptr++ = 0; 739*4887Schin } 740*4887Schin hp->histcnt += size; 741*4887Schin c = hist_ind(hp,++hp->histind); 742*4887Schin hp->histcmds[c] = hp->histcnt; 743*4887Schin if(hp->histflush>HIST_MARKSZ && hp->histcnt > hp->histmarker+HIST_BSIZE/2) 744*4887Schin { 745*4887Schin memcpy((void*)saveptr,(void*)bufptr,HIST_MARKSZ); 746*4887Schin saved=1; 747*4887Schin hp->histcnt += HIST_MARKSZ; 748*4887Schin hist_marker(bufptr,hp->histind); 749*4887Schin hp->histmarker = hp->histcmds[hist_ind(hp,c)] = hp->histcnt; 750*4887Schin size += HIST_MARKSZ; 751*4887Schin } 752*4887Schin errno = 0; 753*4887Schin size = write(sffileno(iop),(char*)buff,size); 754*4887Schin if(saved) 755*4887Schin memcpy((void*)bufptr,(void*)saveptr,HIST_MARKSZ); 756*4887Schin if(size>=0) 757*4887Schin { 758*4887Schin hp->histwfail = 0; 759*4887Schin return(insize); 760*4887Schin } 761*4887Schin return(-1); 762*4887Schin } 763*4887Schin 764*4887Schin /* 765*4887Schin * Put history sequence number <n> into buffer <buff> 766*4887Schin * The buffer must be large enough to hold HIST_MARKSZ chars 767*4887Schin */ 768*4887Schin 769*4887Schin static void hist_marker(register char *buff,register long cmdno) 770*4887Schin { 771*4887Schin *buff++ = HIST_CMDNO; 772*4887Schin *buff++ = 0; 773*4887Schin *buff++ = (cmdno>>16); 774*4887Schin *buff++ = (cmdno>>8); 775*4887Schin *buff++ = cmdno; 776*4887Schin *buff++ = 0; 777*4887Schin } 778*4887Schin 779*4887Schin /* 780*4887Schin * return byte offset in history file for command <n> 781*4887Schin */ 782*4887Schin off_t hist_tell(register History_t *hp, int n) 783*4887Schin { 784*4887Schin return(hp->histcmds[hist_ind(hp,n)]); 785*4887Schin } 786*4887Schin 787*4887Schin /* 788*4887Schin * seek to the position of command <n> 789*4887Schin */ 790*4887Schin off_t hist_seek(register History_t *hp, int n) 791*4887Schin { 792*4887Schin return(sfseek(hp->histfp,hp->histcmds[hist_ind(hp,n)],SEEK_SET)); 793*4887Schin } 794*4887Schin 795*4887Schin /* 796*4887Schin * write the command starting at offset <offset> onto file <outfile>. 797*4887Schin * if character <last> appears before newline it is deleted 798*4887Schin * each new-line character is replaced with string <nl>. 799*4887Schin */ 800*4887Schin 801*4887Schin void hist_list(register History_t *hp,Sfio_t *outfile, off_t offset,int last, char *nl) 802*4887Schin { 803*4887Schin register int oldc=0; 804*4887Schin register int c; 805*4887Schin if(offset<0 || !hp) 806*4887Schin { 807*4887Schin sfputr(outfile,sh_translate(e_unknown),'\n'); 808*4887Schin return; 809*4887Schin } 810*4887Schin sfseek(hp->histfp,offset,SEEK_SET); 811*4887Schin while((c = sfgetc(hp->histfp)) != EOF) 812*4887Schin { 813*4887Schin if(c && oldc=='\n') 814*4887Schin sfputr(outfile,nl,-1); 815*4887Schin else if(last && (c==0 || (c=='\n' && oldc==last))) 816*4887Schin return; 817*4887Schin else if(oldc) 818*4887Schin sfputc(outfile,oldc); 819*4887Schin oldc = c; 820*4887Schin if(c==0) 821*4887Schin return; 822*4887Schin } 823*4887Schin return; 824*4887Schin } 825*4887Schin 826*4887Schin /* 827*4887Schin * find index for last line with given string 828*4887Schin * If flag==0 then line must begin with string 829*4887Schin * direction < 1 for backwards search 830*4887Schin */ 831*4887Schin 832*4887Schin Histloc_t hist_find(register History_t*hp,char *string,register int index1,int flag,int direction) 833*4887Schin { 834*4887Schin register int index2; 835*4887Schin off_t offset; 836*4887Schin int *coffset=0; 837*4887Schin Histloc_t location; 838*4887Schin location.hist_command = -1; 839*4887Schin location.hist_char = 0; 840*4887Schin location.hist_line = 0; 841*4887Schin if(!hp) 842*4887Schin return(location); 843*4887Schin /* leading ^ means beginning of line unless escaped */ 844*4887Schin if(flag) 845*4887Schin { 846*4887Schin index2 = *string; 847*4887Schin if(index2=='\\') 848*4887Schin string++; 849*4887Schin else if(index2=='^') 850*4887Schin { 851*4887Schin flag=0; 852*4887Schin string++; 853*4887Schin } 854*4887Schin } 855*4887Schin if(flag) 856*4887Schin coffset = &location.hist_char; 857*4887Schin index2 = (int)hp->histind; 858*4887Schin if(direction<0) 859*4887Schin { 860*4887Schin index2 -= hp->histsize; 861*4887Schin if(index2<1) 862*4887Schin index2 = 1; 863*4887Schin if(index1 <= index2) 864*4887Schin return(location); 865*4887Schin } 866*4887Schin else if(index1 >= index2) 867*4887Schin return(location); 868*4887Schin while(index1!=index2) 869*4887Schin { 870*4887Schin direction>0?++index1:--index1; 871*4887Schin offset = hist_tell(hp,index1); 872*4887Schin if((location.hist_line=hist_match(hp,offset,string,coffset))>=0) 873*4887Schin { 874*4887Schin location.hist_command = index1; 875*4887Schin return(location); 876*4887Schin } 877*4887Schin #if KSHELL 878*4887Schin /* allow a search to be aborted */ 879*4887Schin if(sh.trapnote&SH_SIGSET) 880*4887Schin break; 881*4887Schin #endif /* KSHELL */ 882*4887Schin } 883*4887Schin return(location); 884*4887Schin } 885*4887Schin 886*4887Schin /* 887*4887Schin * search for <string> in history file starting at location <offset> 888*4887Schin * If coffset==0 then line must begin with string 889*4887Schin * returns the line number of the match if successful, otherwise -1 890*4887Schin */ 891*4887Schin 892*4887Schin int hist_match(register History_t *hp,off_t offset,char *string,int *coffset) 893*4887Schin { 894*4887Schin register unsigned char *first, *cp; 895*4887Schin register int m,n,c=1,line=0; 896*4887Schin #if SHOPT_MULTIBYTE 897*4887Schin mbinit(); 898*4887Schin #endif /* SHOPT_MULTIBYTE */ 899*4887Schin sfseek(hp->histfp,offset,SEEK_SET); 900*4887Schin if(!(cp = first = (unsigned char*)sfgetr(hp->histfp,0,0))) 901*4887Schin return(-1); 902*4887Schin m = sfvalue(hp->histfp); 903*4887Schin n = strlen(string); 904*4887Schin while(m > n) 905*4887Schin { 906*4887Schin if(*cp==*string && memcmp(cp,string,n)==0) 907*4887Schin { 908*4887Schin if(coffset) 909*4887Schin *coffset = (cp-first); 910*4887Schin return(line); 911*4887Schin } 912*4887Schin if(!coffset) 913*4887Schin break; 914*4887Schin if(*cp=='\n') 915*4887Schin line++; 916*4887Schin #if SHOPT_MULTIBYTE 917*4887Schin if((c=mbsize(cp)) < 0) 918*4887Schin c = 1; 919*4887Schin #endif /* SHOPT_MULTIBYTE */ 920*4887Schin cp += c; 921*4887Schin m -= c; 922*4887Schin } 923*4887Schin return(-1); 924*4887Schin } 925*4887Schin 926*4887Schin 927*4887Schin #if SHOPT_ESH || SHOPT_VSH 928*4887Schin /* 929*4887Schin * copy command <command> from history file to s1 930*4887Schin * at most <size> characters copied 931*4887Schin * if s1==0 the number of lines for the command is returned 932*4887Schin * line=linenumber for emacs copy and only this line of command will be copied 933*4887Schin * line < 0 for full command copy 934*4887Schin * -1 returned if there is no history file 935*4887Schin */ 936*4887Schin 937*4887Schin int hist_copy(char *s1,int size,int command,int line) 938*4887Schin { 939*4887Schin register int c; 940*4887Schin register History_t *hp = sh_getinterp()->hist_ptr; 941*4887Schin register int count = 0; 942*4887Schin register char *s1max = s1+size; 943*4887Schin if(!hp) 944*4887Schin return(-1); 945*4887Schin hist_seek(hp,command); 946*4887Schin while ((c = sfgetc(hp->histfp)) && c!=EOF) 947*4887Schin { 948*4887Schin if(c=='\n') 949*4887Schin { 950*4887Schin if(count++ ==line) 951*4887Schin break; 952*4887Schin else if(line >= 0) 953*4887Schin continue; 954*4887Schin } 955*4887Schin if(s1 && (line<0 || line==count)) 956*4887Schin { 957*4887Schin if(s1 >= s1max) 958*4887Schin { 959*4887Schin *--s1 = 0; 960*4887Schin break; 961*4887Schin } 962*4887Schin *s1++ = c; 963*4887Schin } 964*4887Schin 965*4887Schin } 966*4887Schin sfseek(hp->histfp,(off_t)0,SEEK_END); 967*4887Schin if(s1==0) 968*4887Schin return(count); 969*4887Schin if(count && (c= *(s1-1)) == '\n') 970*4887Schin s1--; 971*4887Schin *s1 = '\0'; 972*4887Schin return(count); 973*4887Schin } 974*4887Schin 975*4887Schin /* 976*4887Schin * return word number <word> from command number <command> 977*4887Schin */ 978*4887Schin 979*4887Schin char *hist_word(char *string,int size,int word) 980*4887Schin { 981*4887Schin register int c; 982*4887Schin register char *s1 = string; 983*4887Schin register unsigned char *cp = (unsigned char*)s1; 984*4887Schin register int flag = 0; 985*4887Schin History_t *hp = hist_ptr; 986*4887Schin if(!hp) 987*4887Schin #if KSHELL 988*4887Schin { 989*4887Schin strncpy(string,sh.lastarg,size); 990*4887Schin return(string); 991*4887Schin } 992*4887Schin #else 993*4887Schin return(NIL(char*)); 994*4887Schin #endif /* KSHELL */ 995*4887Schin hist_copy(string,size,(int)hp->histind-1,-1); 996*4887Schin for(;c = *cp;cp++) 997*4887Schin { 998*4887Schin c = isspace(c); 999*4887Schin if(c && flag) 1000*4887Schin { 1001*4887Schin *cp = 0; 1002*4887Schin if(--word==0) 1003*4887Schin break; 1004*4887Schin flag = 0; 1005*4887Schin } 1006*4887Schin else if(c==0 && flag==0) 1007*4887Schin { 1008*4887Schin s1 = (char*)cp; 1009*4887Schin flag++; 1010*4887Schin } 1011*4887Schin } 1012*4887Schin *cp = 0; 1013*4887Schin if(s1 != string) 1014*4887Schin strcpy(string,s1); 1015*4887Schin return(string); 1016*4887Schin } 1017*4887Schin 1018*4887Schin #endif /* SHOPT_ESH */ 1019*4887Schin 1020*4887Schin #if SHOPT_ESH 1021*4887Schin /* 1022*4887Schin * given the current command and line number, 1023*4887Schin * and number of lines back or foward, 1024*4887Schin * compute the new command and line number. 1025*4887Schin */ 1026*4887Schin 1027*4887Schin Histloc_t hist_locate(History_t *hp,register int command,register int line,int lines) 1028*4887Schin { 1029*4887Schin Histloc_t next; 1030*4887Schin line += lines; 1031*4887Schin if(!hp) 1032*4887Schin { 1033*4887Schin command = -1; 1034*4887Schin goto done; 1035*4887Schin } 1036*4887Schin if(lines > 0) 1037*4887Schin { 1038*4887Schin register int count; 1039*4887Schin while(command <= hp->histind) 1040*4887Schin { 1041*4887Schin count = hist_copy(NIL(char*),0, command,-1); 1042*4887Schin if(count > line) 1043*4887Schin goto done; 1044*4887Schin line -= count; 1045*4887Schin command++; 1046*4887Schin } 1047*4887Schin } 1048*4887Schin else 1049*4887Schin { 1050*4887Schin register int least = (int)hp->histind-hp->histsize; 1051*4887Schin while(1) 1052*4887Schin { 1053*4887Schin if(line >=0) 1054*4887Schin goto done; 1055*4887Schin if(--command < least) 1056*4887Schin break; 1057*4887Schin line += hist_copy(NIL(char*),0, command,-1); 1058*4887Schin } 1059*4887Schin command = -1; 1060*4887Schin } 1061*4887Schin done: 1062*4887Schin next.hist_line = line; 1063*4887Schin next.hist_command = command; 1064*4887Schin return(next); 1065*4887Schin } 1066*4887Schin #endif /* SHOPT_ESH */ 1067*4887Schin 1068*4887Schin 1069*4887Schin /* 1070*4887Schin * Handle history file exceptions 1071*4887Schin */ 1072*4887Schin #ifdef SF_BUFCONST 1073*4887Schin static int hist_exceptf(Sfio_t* fp, int type, void *data, Sfdisc_t *handle) 1074*4887Schin #else 1075*4887Schin static int hist_exceptf(Sfio_t* fp, int type, Sfdisc_t *handle) 1076*4887Schin #endif 1077*4887Schin { 1078*4887Schin register int newfd,oldfd; 1079*4887Schin History_t *hp = (History_t*)handle; 1080*4887Schin if(type==SF_WRITE) 1081*4887Schin { 1082*4887Schin if(errno==ENOSPC || hp->histwfail++ >= 10) 1083*4887Schin return(0); 1084*4887Schin /* write failure could be NFS problem, try to re-open */ 1085*4887Schin close(oldfd=sffileno(fp)); 1086*4887Schin if((newfd=open(hp->histname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) >= 0) 1087*4887Schin { 1088*4887Schin if(fcntl(newfd, F_DUPFD, oldfd) !=oldfd) 1089*4887Schin return(-1); 1090*4887Schin fcntl(oldfd,F_SETFD,FD_CLOEXEC); 1091*4887Schin close(newfd); 1092*4887Schin if(lseek(oldfd,(off_t)0,SEEK_END) < hp->histcnt) 1093*4887Schin { 1094*4887Schin register int index = hp->histind; 1095*4887Schin lseek(oldfd,(off_t)2,SEEK_SET); 1096*4887Schin hp->histcnt = 2; 1097*4887Schin hp->histind = 1; 1098*4887Schin hp->histcmds[1] = 2; 1099*4887Schin hist_eof(hp); 1100*4887Schin hp->histmarker = hp->histcnt; 1101*4887Schin hp->histind = index; 1102*4887Schin } 1103*4887Schin return(1); 1104*4887Schin } 1105*4887Schin errormsg(SH_DICT,2,"History file write error-%d %s: file unrecoverable",errno,hp->histname); 1106*4887Schin return(-1); 1107*4887Schin } 1108*4887Schin return(0); 1109*4887Schin } 1110