Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/ie/history.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1984-2011 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
* Pat Sullivan *
19
* *
20
***********************************************************************/
21
/*
22
* History file manipulation routines
23
*
24
* David Korn
25
* AT&T Bell Laboratories
26
* Room 3C-526B
27
* Murray Hill, N. J. 07974
28
* Tel. x7975
29
*
30
*/
31
32
/*
33
* Each command in the history file starts on an even byte is null terminated.
34
* The first byte must contain the special character H_UNDO and the second
35
* byte is the version number. The sequence H_UNDO 0, following a command,
36
* nullifies the previous command. A six byte sequence starting with
37
* H_CMDNO is used to store the command number so that it is not necessary
38
* to read the file from beginning to end to get to the last block of
39
* commands. This format of this sequence is different in version 1
40
* then in version 0. Version 1 allows commands to use the full 8 bit
41
* character set. It can understand version 0 format files.
42
*/
43
44
45
#ifdef KSHELL
46
# include "defs.h"
47
# include "builtins.h"
48
#else
49
# include "io.h"
50
# include "edit.h"
51
# include <signal.h>
52
# include <ctype.h>
53
#endif /* KSHELL */
54
#include "history.h"
55
#ifdef MULTIBYTE
56
# include "national.h"
57
#endif /* MULTIBYTE */
58
#include <tm.h>
59
60
#ifndef KSHELL
61
# define sh_heap(s) strcpy(malloc(strlen(s)+1),(s))
62
# define sh_arith(str) (int)strtol(str,(char**)0,10)
63
# define new_of(type,x) ((type*)malloc((unsigned)sizeof(type)+(x)))
64
# define e_unknown "unknown"
65
# define e_create "cannot create"
66
# define path_relative(x) (x)
67
static int io_readbuff();
68
static off_t io_seek();
69
static void io_fclose();
70
static int io_getc();
71
static void io_init();
72
static int io_renumber();
73
static int io_mktmp();
74
const char hist_fname[] = "/.history";
75
struct history *hist_ptr = 0;
76
#endif /* KSHELL */
77
78
79
static int fixmask;
80
static int noclean;
81
static void hist_trim();
82
static int hist_nearend();
83
static int hist_check();
84
static int hist_clean();
85
static void hist_free();
86
static int hist_version;
87
static struct history *wasopen;
88
static off_t hist_marker; /* offset of last command marker */
89
static char htrim; /* set while in hist_trim */
90
static const unsigned char hist_stamp[2] = { H_UNDO, H_VERSION };
91
92
#define HIST_RECENT 600
93
94
/*
95
* open the history file
96
* if HISTNAME is not given and userid==0 then no history file.
97
* if login_sh and HISTFILE is longer than HISTMAX bytes then it is
98
* cleaned up.
99
* hist_open() returns 1, if histor file is open
100
*/
101
102
int hist_open()
103
{
104
register int fd;
105
register struct history *fp;
106
register char *histname;
107
char fname[TMPSIZ];
108
char hname[256];
109
int maxlines;
110
register char *cp;
111
register off_t hsize = 0;
112
int his_start;
113
114
if(hist_ptr)
115
return(1);
116
histname = nam_strval(HISTFILE);
117
if(!histname)
118
{
119
120
if(cp=nam_strval(HOME))
121
cp = sh_copy(cp,hname);
122
else
123
cp = hname;
124
sh_copy(hist_fname,cp);
125
histname = hname;
126
}
127
*fname = 0;
128
if(fp=wasopen)
129
{
130
/* reuse history file if same name */
131
wasopen = 0;
132
hist_ptr = fp;
133
if(strcmp(histname,fp->fixname)==0)
134
return(1);
135
else
136
hist_free();
137
}
138
retry:
139
histname = path_relative(histname);
140
if((fd=open(histname,O_APPEND|O_RDWR|O_CREAT|O_BINARY,S_IRUSR|S_IWUSR))>=0)
141
{
142
hsize=io_seek(fd,(off_t)0,SEEK_END);
143
}
144
/* make sure that file has history file format */
145
if(hsize && hist_check(fd))
146
{
147
io_fclose(fd);
148
hsize = 0;
149
if(unlink(histname)>=0)
150
goto retry;
151
fd = -1;
152
}
153
if(fd < 0)
154
{
155
#ifdef KSHELL
156
/* don't allow root a history_file in /tmp */
157
if(sh.userid)
158
#endif /* KSHELL */
159
fd = io_mktmp(fname, sizeof(fname));
160
}
161
if(fd<0)
162
return(0);
163
fd = io_renumber(fd,FCIO);
164
if(cp=nam_strval(HISTSIZE))
165
maxlines = (unsigned)sh_arith(cp);
166
else
167
maxlines = HIS_DFLT;
168
for(fixmask=16;fixmask <= maxlines; fixmask <<=1 );
169
if(!(fp=new_of(struct history,(--fixmask)*sizeof(off_t))))
170
{
171
io_fclose(fd);
172
return(0);
173
}
174
hist_ptr = fp;
175
fp->fixfd = fd;
176
fp->fixmax = maxlines;
177
io_init(fd,(struct fileblk*)0,(char*)0);
178
fp->fixfp = io_ftable[fd];
179
fp->fixind = 1;
180
fp->fixcmds[1] = 2;
181
fp->fixcnt = 2;
182
fp->fixname = sh_heap(histname);
183
if(hsize==0)
184
/* put special characters at front of file */
185
{
186
write(fd,(char*)hist_stamp,2);
187
p_setout(fd);
188
}
189
/* initialize history list */
190
else
191
{
192
int first;
193
off_t size = (HISMAX/4)+maxlines*HISLINE;
194
first = fp->fixind = hist_nearend(fd,hsize-size);
195
hist_eof(); /* this sets fixind to last command */
196
his_start = fp->fixind-maxlines;
197
if(his_start <=0)
198
his_start = 1;
199
while(first > his_start)
200
{
201
size += size;
202
first = hist_nearend(fd,hsize-size);
203
fp->fixind = first;
204
}
205
hist_eof();
206
}
207
if(*fname)
208
unlink(fname);
209
if(hist_clean(fp->fixfd) && his_start>1 && hsize > HISMAX)
210
{
211
#ifdef DEBUG
212
p_setout(ERRIO);
213
p_num(getpid(),':');
214
p_str("hist_trim hsize",'=');
215
p_num(hsize,NL);
216
p_flush();
217
#endif /* DEBUG */
218
hist_trim(fp->fixind-maxlines);
219
}
220
return(1);
221
}
222
223
/*
224
* close the history file and free the space
225
*/
226
227
static void hist_free()
228
{
229
register struct history *fp = hist_ptr;
230
io_fclose(fp->fixfd);
231
free((char*)fp);
232
hist_ptr = 0;
233
}
234
235
/*
236
* check history file format to see if it begins with special byte
237
*/
238
239
static int hist_check(fd)
240
register int fd;
241
{
242
unsigned char magic[2];
243
io_seek(fd,(off_t)0,SEEK_SET);
244
if((read(fd,(char*)magic,2)!=2) || (magic[0]!=H_UNDO))
245
return(1);
246
hist_version = magic[1];
247
return(0);
248
}
249
250
/*
251
* Decide whether to clean out the history file
252
*/
253
254
#ifdef YELLOWP
255
/* NFS systems do not handle file locking correctly */
256
# undef F_SETLK
257
#endif /* YELLOWP */
258
#ifdef _FLOCK
259
# undef F_SETLK
260
# ifndef LOCK_EX
261
# include <sys/file.h>
262
# endif
263
#endif /*_FLOCK */
264
#ifdef F_SETLK
265
# if defined(F_SETLK) && !defined(F_WRLCK)
266
# undef F_SETLK
267
# endif
268
static struct flock hislock = { F_WRLCK, 0, (off_t)0, (off_t)2, (pid_t)0 };
269
#endif
270
static int hist_clean(fd)
271
register int fd;
272
{
273
register int r = 0;
274
struct stat statb;
275
if(noclean)
276
return(0);
277
#ifdef _FLOCK
278
r = (flock(fd,LOCK_NB|LOCK_EX)==0);
279
#else
280
# ifdef F_SETLK
281
r = (fcntl(fd,F_SETLK,&hislock)==0);
282
# else
283
# ifdef KSHELL
284
r = sh.login_sh;
285
# endif /* KSHELL */
286
# endif
287
#endif
288
if(fstat(fd,&statb)>=0)
289
{
290
/* see if history file was recently accessed */
291
if(r)
292
{
293
if((time((time_t*)0)-statb.st_mtime) < HIST_RECENT)
294
r = 0;
295
}
296
}
297
return(r);
298
}
299
300
/*
301
* Copy the last <n> commands to a new file and make this the history file
302
*/
303
304
static void hist_trim(n)
305
int n;
306
{
307
register int c;
308
register struct fileblk *fp;
309
register struct history *hist_new;
310
struct history *hist_old = hist_ptr;
311
off_t old,new=0;
312
int fdo;
313
/* move to an available descriptor >= USERIO */
314
fdo= io_renumber(hist_ptr->fixfd,fcntl(hist_ptr->fixfd,F_DUPFD,USERIO));
315
hist_ptr->fixfd = fdo;
316
unlink(hist_ptr->fixname);
317
hist_ptr = 0;
318
if(!hist_open())
319
{
320
/* use the old history file */
321
hist_ptr = hist_old;
322
hist_ptr->fixfd = io_renumber(fdo,FCIO);
323
return;
324
}
325
hist_new = hist_ptr;
326
p_setout(hist_new->fixfd);
327
fp = hist_new->fixfp;
328
if(n < 0)
329
n = 0;
330
htrim++;
331
do
332
{
333
hist_ptr = hist_old;
334
old = new;
335
new = io_seek(fdo,hist_position(++n),SEEK_SET);
336
hist_ptr = hist_new;
337
while((c=io_getc(fdo))!=EOF && c)
338
{
339
if(fp->ptr >= fp->last)
340
p_flush();
341
*fp->ptr++ = c;
342
hist_new->fixcnt++;
343
}
344
#ifdef KSHELL
345
st.states |= FIXFLG;
346
#endif /* KSHELL */
347
hist_flush();
348
}
349
while(new>old && c!=EOF);
350
htrim = 0;
351
hist_cancel();
352
io_fclose(fdo);
353
free((char*)hist_old);
354
}
355
356
/*
357
* position history file at size and find next command number
358
*/
359
360
static int hist_nearend(fd,size)
361
register int fd;
362
off_t size;
363
{
364
register int n = 0;
365
register int state = 0;
366
register int c;
367
if(size <=0)
368
goto begin;
369
io_seek(fd,size,SEEK_SET);
370
/* skip to numbered command and return the number */
371
/* numbering commands occur after a null and begin with H_CMDNO */
372
while((c=io_getc(fd))!=EOF) switch(state)
373
{
374
case 1:
375
if(c==H_CMDNO)
376
{
377
hist_ptr->fixcnt = size + n + 6;
378
state = 2;
379
}
380
break;
381
382
case 2:
383
/* see if H_CMDNO is followed by 0 */
384
if(hist_version && c)
385
{
386
n += 2;
387
state = 0;
388
break;
389
}
390
n = 0;
391
392
case 3:
393
case 4:
394
case 5:
395
if(hist_version)
396
n = (n<<8) + c;
397
else if(state<4)
398
n = (n<<7) + (c&0177);
399
state++;
400
break;
401
402
case 6:
403
return(n);
404
405
default:
406
state = (c==0);
407
n++;
408
}
409
begin:
410
io_seek(fd,(off_t)2,SEEK_SET);
411
hist_ptr->fixcnt = 2;
412
return(1);
413
}
414
415
/*
416
* This routine marks the history file as closed. The file actually
417
* closes when the program exits, or when you open a history file
418
* with a different name.
419
*/
420
421
void hist_close()
422
{
423
wasopen = hist_ptr;
424
hist_ptr = 0;
425
}
426
427
/*
428
* This routine reads the history file from the present position
429
* to the end-of-file and puts the information in the in-core
430
* history table
431
* Note that H_CMDNO is only recognized at the beginning of a command
432
* and that H_UNDO as the first character of a command is skipped
433
* unless it is followed by 0. If followed by 0 then it cancels
434
* the previous command.
435
*/
436
437
void hist_eof()
438
{
439
register struct history *fp = hist_ptr;
440
register int c;
441
register int incr = 0;
442
register int oldc = 0;
443
register off_t count = fp->fixcnt;
444
int skip = 0;
445
io_seek(fp->fixfd,count,SEEK_SET);
446
#ifdef INT16
447
while((c=io_getc(fp->fixfd))!=EOF)
448
{
449
#else
450
/* could use io_getc() but this is faster */
451
while(1)
452
{
453
c = *(fp->fixfp->ptr)++;
454
if(c==0 && (fp->fixfp->ptr > fp->fixfp->last))
455
{
456
c = io_readbuff(fp->fixfp);
457
if(c == EOF)
458
break;
459
}
460
c &= STRIP;
461
#endif /* INT16 */
462
count++;
463
if(skip-- > 0)
464
{
465
if(skip==2)
466
hist_marker = count;
467
oldc = 0;
468
continue;
469
}
470
if(c == 0)
471
{
472
if(oldc==H_CMDNO && incr==0)
473
skip = 3;
474
fp->fixind += incr;
475
fp->fixcmds[fp->fixind&fixmask] = count;
476
incr = 0;
477
}
478
else if(oldc == 0)
479
{
480
if(c == H_CMDNO)
481
{
482
/* old format history file */
483
if(hist_version==0)
484
skip = 4;
485
incr = 0;
486
}
487
else if(c==H_UNDO)
488
incr = -1;
489
}
490
else
491
incr = 1;
492
oldc = c;
493
}
494
fp->fixcnt = count;
495
p_setout(fp->fixfd);
496
}
497
498
/*
499
* This routine will cause the previous command to be cancelled
500
*/
501
502
void hist_cancel()
503
{
504
register struct history *fp = hist_ptr;
505
register int c;
506
if(!fp)
507
return;
508
p_setout(fp->fixfd);
509
p_char(H_UNDO);
510
p_char(0);
511
p_flush();
512
fp->fixcnt += 2;
513
c = (--fp->fixind)&fixmask;
514
fp->fixcmds[c] = fp->fixcnt;
515
}
516
517
/*
518
* This routine adds one or two null bytes and flushes the history buffer
519
*/
520
521
void hist_flush()
522
{
523
register struct history *fp = hist_ptr;
524
register struct fileblk *fd;
525
register int c;
526
int flush = 0;
527
int savcount;
528
if(!fp)
529
return;
530
#ifdef KSHELL
531
if(!(st.states&FIXFLG))
532
return;
533
st.states &= ~FIXFLG;
534
#endif /* KSHELL */
535
fd = fp->fixfp;
536
if(htrim)
537
{
538
p_char(0);
539
fp->fixcnt++;
540
goto set_count;
541
}
542
if((fp->fixcnt=lseek(fp->fixfd,(off_t)0,SEEK_END)) <0)
543
{
544
#ifdef DEBUG
545
p_setout(ERRIO);
546
p_num(getpid(),':');
547
p_str("hist_flush: EOF seek failed errno",'=');
548
p_num(errno,NL);
549
p_flush();
550
#endif /* DEBUG */
551
hist_free();
552
noclean++;
553
if(!htrim)
554
hist_open();
555
noclean = 0;
556
return;
557
}
558
p_setout(fp->fixfd);
559
/* remove whitespace from end of commands */
560
while(--fd->ptr >= fd->base)
561
{
562
c= *fd->ptr;
563
if(!isspace(c))
564
{
565
if(c=='\\' && *(fd->ptr+1)!='\n')
566
fd->ptr++;
567
break;
568
}
569
}
570
/* don't count empty lines */
571
if(++fd->ptr <= fd->base && !fp->fixflush)
572
{
573
fp->fixind--;
574
goto set_count;
575
}
576
p_char('\n');
577
p_char(0);
578
flush++;
579
fp->fixcnt += (fd->ptr-fd->base);
580
set_count:
581
/* start each command on an even byte boundary */
582
if(fp->fixcnt&01)
583
{
584
fp->fixcnt++;
585
p_char(0);
586
flush++;
587
}
588
c = (++fp->fixind)&fixmask;
589
savcount = fp->fixcmds[c];
590
fp->fixcmds[c] = fp->fixcnt;
591
if(fp->fixcnt > hist_marker+IOBSIZE/2)
592
{
593
/* put line number in file */
594
fp->fixcnt += 6;
595
p_char(H_CMDNO);
596
p_char(0);
597
c = (fp->fixind>>16);
598
p_char(c);
599
c = (fp->fixind>>8);
600
p_char(c);
601
c = fp->fixind;
602
p_char(c);
603
p_char(0);
604
flush++;
605
fp->fixcmds[c&fixmask] = fp->fixcnt;
606
hist_marker = fp->fixcnt;
607
}
608
if(flush && !htrim)
609
{
610
p_flush();
611
/* check for write errors, like ulimit */
612
if(fd->flag&IOERR)
613
{
614
c = (fp->fixind--)&fixmask;
615
fp->fixcmds[c] = savcount;
616
}
617
}
618
fp->fixflush = 0;
619
}
620
621
/*
622
* return byte offset in history file for command <n>
623
*/
624
625
off_t hist_position(n)
626
int n;
627
{
628
register struct history *fp = hist_ptr;
629
return(fp->fixcmds[n&fixmask]);
630
}
631
632
/*
633
* write the command starting at offset <offset> onto file <fd>.
634
* if character <last> appears before newline it is deleted
635
* each new-line character is replaced with string <nl>.
636
*/
637
638
void hist_list(offset,last,nl)
639
off_t offset;
640
int last;
641
char *nl;
642
{
643
register int oldc=0;
644
register int c;
645
register struct history *fp = hist_ptr;
646
if(offset<0 || !fp)
647
{
648
p_str(e_unknown,'\n');
649
return;
650
}
651
io_seek(fp->fixfd,offset,SEEK_SET);
652
while((c = io_getc(fp->fixfd)) != EOF)
653
{
654
if(c && oldc=='\n')
655
p_str(nl,0);
656
else if(oldc==last && c=='\n')
657
return;
658
else if(c==0 && last)
659
return;
660
else if(oldc)
661
p_char(oldc);
662
oldc = c;
663
if(c==0)
664
return;
665
}
666
return;
667
}
668
669
/*
670
* find index for last line with given string
671
* If flag==0 then line must begin with string
672
* direction < 1 for backwards search
673
*/
674
675
histloc hist_find(string,index1,flag,direction)
676
char *string;
677
register int index1;
678
int flag;
679
int direction;
680
{
681
register struct history *fp = hist_ptr;
682
register int index2;
683
off_t offset;
684
histloc location;
685
location.his_command = -1;
686
if(!fp)
687
return(location);
688
/* leading ^ means beginning of line unless escaped */
689
if(flag)
690
{
691
index2 = *string;
692
if(index2=='\\')
693
string++;
694
else if(index2=='^')
695
{
696
flag=0;
697
string++;
698
}
699
}
700
index2 = fp->fixind;
701
if(direction<0)
702
{
703
index2 -= fp->fixmax;
704
if(index2<1)
705
index2 = 1;
706
if(index1 <= index2)
707
return(location);
708
}
709
else if(index1 >= index2)
710
return(location);
711
while(index1!=index2)
712
{
713
direction>0?++index1:--index1;
714
offset = hist_position(index1);
715
if((location.his_line=hist_match(offset,string,flag))>=0)
716
{
717
location.his_command = index1;
718
return(location);
719
}
720
#ifdef KSHELL
721
/* allow a search to be aborted */
722
if(sh.trapnote&SIGSET)
723
break;
724
#endif /* KSHELL */
725
}
726
return(location);
727
}
728
729
/*
730
* search for <string> in history file starting at location <offset>
731
* If flag==0 then line must begin with string
732
* returns the line number of the match if successful, otherwise -1
733
*/
734
735
int hist_match(offset,string,flag)
736
off_t offset;
737
char *string;
738
int flag;
739
{
740
register char *cp;
741
register int c;
742
register struct history *fp = hist_ptr;
743
register off_t count;
744
int line = 0;
745
#ifdef MULTIBYTE
746
int nbytes = 0;
747
#endif /* MULTIBYTE */
748
do
749
{
750
if(offset>=0)
751
{
752
io_seek(fp->fixfd,offset,SEEK_SET);
753
count = offset;
754
}
755
offset = -1;
756
for(cp=string;*cp;cp++)
757
{
758
if((c=io_getc(fp->fixfd)) == EOF || c ==0)
759
break;
760
count++;
761
#ifdef MULTIBYTE
762
/* always position at character boundary */
763
if(--nbytes > 0)
764
{
765
if(cp==string)
766
{
767
cp--;
768
continue;
769
}
770
}
771
else
772
{
773
nbytes = echarset(c);
774
nbytes = in_csize(nbytes) + (nbytes>=2);
775
}
776
#endif /* MULTIBYTE */
777
if(c == '\n')
778
line++;
779
/* save earliest possible matching character */
780
if(flag && c == *string && offset<0)
781
offset = count;
782
if(*cp != c )
783
break;
784
}
785
if(*cp==0) /* match found */
786
return(line);
787
}
788
while(flag && c && c != EOF);
789
return(-1);
790
}
791
792
793
#if ESH || VSH
794
/*
795
* copy command <command> from history file to s1
796
* at most MAXLINE characters copied
797
* if s1==0 the number of lines for the command is returned
798
* line=linenumber for emacs copy and only this line of command will be copied
799
* line < 0 for full command copy
800
* -1 returned if there is no history file
801
*/
802
803
int hist_copy(s1,command,line)
804
register char *s1;
805
int command, line;
806
{
807
register int c;
808
register struct history *fp = hist_ptr;
809
register int count = 0;
810
register char *s1max = s1+MAXLINE;
811
off_t offset;
812
if(!fp)
813
return(-1);
814
offset = hist_position(command);
815
io_seek(fp->fixfd,offset,SEEK_SET);
816
while ((c = io_getc(fp->fixfd)) && c!=EOF)
817
{
818
if(c=='\n')
819
{
820
if(count++ ==line)
821
break;
822
else if(line >= 0)
823
continue;
824
}
825
if(s1 && (line<0 || line==count))
826
{
827
if(s1 >= s1max)
828
{
829
*--s1 = 0;
830
break;
831
}
832
*s1++ = c;
833
}
834
835
}
836
io_seek(fp->fixfd,(off_t)0,SEEK_END);
837
if(s1==0)
838
return(count);
839
if(count && (c= *(s1-1)) == '\n')
840
s1--;
841
*s1 = '\0';
842
return(count);
843
}
844
845
/*
846
* return word number <word> from command number <command>
847
*/
848
849
char *hist_word(s1,word)
850
char *s1;
851
int word;
852
{
853
register int c;
854
register char *cp = s1;
855
register int flag = 0;
856
if(hist_ptr==0)
857
#ifdef KSHELL
858
return(sh.lastarg);
859
#else
860
return((char*)0);
861
#endif /* KSHELL */
862
hist_copy(s1,hist_ptr->fixind-1,-1);
863
for(;c = *cp;cp++)
864
{
865
c = isspace(c);
866
if(c && flag)
867
{
868
*cp = 0;
869
if(--word==0)
870
break;
871
flag = 0;
872
}
873
else if(c==0 && flag==0)
874
{
875
s1 = cp;
876
flag++;
877
}
878
}
879
*cp = 0;
880
return(s1);
881
}
882
883
#endif /* ESH */
884
885
#ifdef ESH
886
/*
887
* given the current command and line number,
888
* and number of lines back or foward,
889
* compute the new command and line number.
890
*/
891
892
histloc hist_locate(command,line,lines)
893
register int command;
894
register int line;
895
int lines;
896
{
897
histloc next;
898
line += lines;
899
if(!hist_ptr)
900
{
901
command = -1;
902
goto done;
903
}
904
if(lines > 0)
905
{
906
register int count;
907
while(command <= hist_ptr->fixind)
908
{
909
count = hist_copy(NIL,command,-1);
910
if(count > line)
911
goto done;
912
line -= count;
913
command++;
914
}
915
}
916
else
917
{
918
register int least = hist_ptr->fixind-hist_ptr->fixmax;
919
while(1)
920
{
921
if(line >=0)
922
goto done;
923
if(--command < least)
924
break;
925
line += hist_copy(NIL,command,-1);
926
}
927
command = -1;
928
}
929
next.his_command = command;
930
return(next);
931
done:
932
next.his_line = line;
933
next.his_command = command;
934
return(next);
935
}
936
#endif /* ESH */
937
938
#ifdef KSHELL
939
940
/*
941
* given a file containing a command and a string of the form old=new,
942
* execute the command with the string old replaced by new
943
*/
944
void hist_subst(command,fd,replace)
945
const char *command;
946
int fd;
947
char *replace;
948
{
949
register char *new=replace;
950
register char *sp;
951
register int c;
952
struct fileblk fb;
953
char inbuff[IOBSIZE+1];
954
char *string;
955
while(*++new != '='); /* skip to '=' */
956
io_init(fd,&fb,inbuff);
957
stakseek(0);
958
while ((c=io_getc(fd)) != EOF)
959
stakputc(c);
960
string = stakfreeze(1);
961
io_fclose(fd);
962
*new++ = 0;
963
if((sp=sh_substitute(string,replace,new))==0)
964
sh_fail(command,e_subst);
965
*(new-1) = '=';
966
p_setout(hist_ptr->fixfd);
967
p_str(sp,0);
968
hist_flush();
969
p_setout(ERRIO);
970
p_str(sp,0);
971
sh_eval(sp);
972
}
973
974
#else /* !KSHELL */
975
976
/*
977
* initialize file structure
978
*/
979
980
static void io_init(fd,fp,buf)
981
int fd;
982
register struct fileblk *fp;
983
char *buf;
984
{
985
if(!fp)
986
{
987
fp = new_of(struct fileblk,IOBSIZE+1);
988
buf = (char*)(fp+1);
989
fp->flag = IOFREE;
990
}
991
else
992
fp->flag = 0;
993
fp->fdes = fd;
994
fp->fseek = 0;
995
fp->base = fp->ptr = fp->last = buf;
996
*fp->ptr = 0;
997
fp->flag |= (IORW|IOREAD);
998
io_ftable[fd] = fp;
999
}
1000
1001
/*
1002
* returns the next character from file <fd>
1003
*/
1004
1005
static int io_getc(fd)
1006
int fd;
1007
{
1008
register struct fileblk *fp = io_ftable[fd];
1009
register int c;
1010
if(!fp)
1011
return(EOF);
1012
if(c= *fp->ptr++)
1013
return(c&STRIP);
1014
if(fp->ptr <= fp->last)
1015
return(0);
1016
return(io_readbuff(fp));
1017
}
1018
1019
/*
1020
* This special version does not handle ptrname==1
1021
* It also saves a lot of real seeks on history file
1022
*/
1023
1024
static off_t hoffset;
1025
1026
static off_t io_seek(fd, offset, ptrname)
1027
int fd;
1028
off_t offset;
1029
register int ptrname;
1030
{
1031
register struct fileblk *fp;
1032
register int c;
1033
off_t p;
1034
1035
if(!(fp=io_ftable[fd]))
1036
return(lseek(fd,offset,ptrname));
1037
fp->flag &= ~IOEOF;
1038
if(!(fp->flag&IOREAD))
1039
{
1040
p_flush();
1041
}
1042
c = 0;
1043
/* check history file to see if already in the buffer */
1044
if(fd==FCIO && ptrname==0 && (fp->flag&IOREAD) && offset<hoffset)
1045
{
1046
p = hoffset - (fp->last - fp->base);
1047
if(offset >= p)
1048
{
1049
fp->ptr = fp->base + (int)(offset-p);
1050
return(offset);
1051
}
1052
else
1053
{
1054
c = offset&(IOBSIZE-1);
1055
offset -= c;
1056
}
1057
}
1058
if(fp->flag&IORW)
1059
{
1060
fp->flag &= ~(IOWRT|IOREAD);
1061
fp->last = fp->ptr = fp->base;
1062
*fp->last = 0;
1063
}
1064
p = lseek(fd, offset, ptrname);
1065
if(fd==FCIO)
1066
{
1067
if(ptrname==0)
1068
hoffset = p;
1069
else
1070
hoffset = -1;
1071
if(c)
1072
{
1073
io_readbuff(fp);
1074
fp->ptr += (c-1);
1075
}
1076
}
1077
return(p);
1078
}
1079
1080
/*
1081
* Read from file into fp
1082
*/
1083
1084
static int io_readbuff(fp)
1085
register struct fileblk *fp;
1086
{
1087
register int n;
1088
1089
if (fp->flag & IORW)
1090
fp->flag |= IOREAD;
1091
if (!(fp->flag&IOREAD))
1092
return(EOF);
1093
1094
fp->ptr = fp->last;
1095
#ifdef SYSCALL
1096
n = syscall(3, filenum(fp),fp->base, IOBSIZE);
1097
#else
1098
n = rEAd(filenum(fp),fp->base, IOBSIZE);
1099
#endif /* SYSCALL */
1100
fp->ptr = fp->base;
1101
if(n > 0)
1102
fp->last = fp->base + n;
1103
else
1104
fp->last = fp->ptr;
1105
*fp->last = 0;
1106
if (n <= 0)
1107
{
1108
if (n == 0)
1109
{
1110
fp->flag |= IOEOF;
1111
if (fp->flag & IORW)
1112
fp->flag &= ~IOREAD;
1113
}
1114
else
1115
fp->flag |= IOERR;
1116
return(-1);
1117
}
1118
if(fp->fdes==FCIO)
1119
hoffset += n;
1120
return(*fp->ptr++&STRIP);
1121
}
1122
1123
1124
/*
1125
* close file stream and reopen for reading and writing
1126
*/
1127
1128
static void io_fclose(fd)
1129
register int fd;
1130
{
1131
register struct fileblk *fp = io_ftable[fd];
1132
/* reposition seek pointer if necessary */
1133
if(fp && !(fp->flag&IOREAD))
1134
p_flush();
1135
close(fd);
1136
if(fp && (fp->flag&IOFREE))
1137
free(fp);
1138
io_ftable[fd] = 0;
1139
}
1140
1141
/*
1142
* move the file number fa to unit fb
1143
*/
1144
1145
static int io_renumber(fa, fb)
1146
register int fa;
1147
register int fb;
1148
{
1149
if(fa >= 0)
1150
{
1151
close(fb);
1152
fcntl(fa,0,fb); /* normal dup */
1153
if(io_ftable[fb] = io_ftable[fa])
1154
io_ftable[fb]->fdes = fb;
1155
io_ftable[fa] = 0;
1156
close(fa);
1157
/* set fb close-on-exec */
1158
#ifdef F_SETFD
1159
fcntl(fb,F_SETFD,1);
1160
#else
1161
# ifdef FIOCLEX
1162
ioctl(fb, FIOCLEX, NULL);
1163
# endif /* FIOCLEX */
1164
#endif /* F_SETFD */
1165
}
1166
return(fb);
1167
}
1168
1169
/*
1170
* return file descriptor for an open file
1171
*/
1172
1173
static int io_mktmp(fname, len)
1174
register char *fname;
1175
int len;
1176
{
1177
int fd;
1178
if(!pathtemp(fname,len,NiL,"hist",&fd))
1179
{
1180
sh_fail("tmp-file",e_create);
1181
fd = -1;
1182
}
1183
return(fd);
1184
}
1185
1186
#endif /* KSHELL */
1187
1188