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