xref: /csrg-svn/local/toolchest/ksh/sh/history.c (revision 35144)
1*35144Smarc /*
2*35144Smarc 
3*35144Smarc  *      Copyright (c) 1984, 1985, 1986 AT&T
4*35144Smarc  *      All Rights Reserved
5*35144Smarc 
6*35144Smarc  *      THIS IS UNPUBLISHED PROPRIETARY SOURCE
7*35144Smarc  *      CODE OF AT&T.
8*35144Smarc  *      The copyright notice above does not
9*35144Smarc  *      evidence any actual or intended
10*35144Smarc  *      publication of such source code.
11*35144Smarc 
12*35144Smarc  */
13*35144Smarc /* @(#)history.c	1.1 */
14*35144Smarc 
15*35144Smarc /*
16*35144Smarc  *   History file manipulation routines
17*35144Smarc  *
18*35144Smarc  *   David Korn
19*35144Smarc  *   AT&T Bell Laboratories
20*35144Smarc  *   Room 5D-112
21*35144Smarc  *   Murray Hill, N. J. 07974
22*35144Smarc  *   Tel. x7975
23*35144Smarc  *
24*35144Smarc  */
25*35144Smarc 
26*35144Smarc 
27*35144Smarc /*
28*35144Smarc  * Each command in the history file starts on an even byte is null terminated.
29*35144Smarc  * The first byte must contain the special character H_UNDO and the second
30*35144Smarc  * byte is the version number.  The sequence H_UNDO 0, following a command,
31*35144Smarc  * nullifies the previous command. A six byte sequence starting with
32*35144Smarc  * H_CMDNO is used to store the command number so that it is not necessary
33*35144Smarc  * to read the file from beginning to end to get to the last block of
34*35144Smarc  * commands.  This format of this sequence is different in version 1
35*35144Smarc  * then in version 0.  Version 1 allows commands to use the full 8 bit
36*35144Smarc  * character set.  It can understand version 0 format files.
37*35144Smarc  */
38*35144Smarc 
39*35144Smarc 
40*35144Smarc #ifdef KSHELL
41*35144Smarc #include	"defs.h"
42*35144Smarc #include	"io.h"
43*35144Smarc #include	"flags.h"
44*35144Smarc #include	"name.h"
45*35144Smarc #include	"shtype.h"
46*35144Smarc #include	"stak.h"
47*35144Smarc #include	"brkincr.h"
48*35144Smarc #include	"builtins.h"
49*35144Smarc #else
50*35144Smarc #include	<stdio.h>
51*35144Smarc #include	<setjmp.h>
52*35144Smarc #include	<signal.h>
53*35144Smarc #include	<ctype.h>
54*35144Smarc #endif	/* KSHELL */
55*35144Smarc 
56*35144Smarc #include	"history.h"
57*35144Smarc #ifdef MULTIBYTE
58*35144Smarc #include	"national.h"
59*35144Smarc #endif /* MULTIBYTE */
60*35144Smarc 
61*35144Smarc int	hist_open();
62*35144Smarc void	hist_close();
63*35144Smarc long	hist_list();
64*35144Smarc void	hist_flush();
65*35144Smarc void	hist_cancel();
66*35144Smarc void	hist_eof();
67*35144Smarc histloc	hist_find();
68*35144Smarc #ifdef ESH
69*35144Smarc histloc	hist_locate();
70*35144Smarc #endif	/* ESH */
71*35144Smarc long	hist_position();
72*35144Smarc #ifdef KSHELL
73*35144Smarc void	hist_subst();
74*35144Smarc #endif	/* KSHELL */
75*35144Smarc 
76*35144Smarc #ifdef KSHELL
77*35144Smarc extern char	*valup();
78*35144Smarc extern long	aeval();
79*35144Smarc extern FILE	*chkrdwr();
80*35144Smarc extern FILE	*create();
81*35144Smarc extern void	failed();
82*35144Smarc extern void	p_str();
83*35144Smarc #else
84*35144Smarc #define frenumber		hist_rename
85*35144Smarc #define tmp_open(s)	tmpfile()
86*35144Smarc #define p_str(s,c)	(fputs(s,stderr),putc(c,stderr))
87*35144Smarc #define closefd(f)	fclose(f)
88*35144Smarc #define	aeval(str)	atoi(str)
89*35144Smarc #define TMPSIZ	20
90*35144Smarc #define NL	'\n'
91*35144Smarc #define output	stderr
92*35144Smarc struct fixcmd *fc_fix;
93*35144Smarc extern char	*getenv();
94*35144Smarc extern FILE	*hist_rename();
95*35144Smarc char login_sh = 0;
96*35144Smarc MSG histfname = "./history";
97*35144Smarc #define unknown "unknown"
98*35144Smarc #endif	/* KSHELL */
99*35144Smarc extern char	*substitute();
100*35144Smarc extern FILE	*tmp_open();
101*35144Smarc extern long	lseek();
102*35144Smarc extern char	*malloc();
103*35144Smarc extern char	*movstr();
104*35144Smarc extern void	free();
105*35144Smarc 
106*35144Smarc static int fixmask;
107*35144Smarc static void hist_trim();
108*35144Smarc static int hist_nearend();
109*35144Smarc static int hist_check();
110*35144Smarc static int hist_version;
111*35144Smarc static int heof;
112*35144Smarc 
113*35144Smarc 
114*35144Smarc /*
115*35144Smarc  * open the history file
116*35144Smarc  * if HISTNAME is not given and userid==0 then no history file.
117*35144Smarc  * if login_sh and HISTFILE is longer than HISTMAX bytes then it is
118*35144Smarc  * cleaned up.
119*35144Smarc  */
hist_open()120*35144Smarc int  hist_open()
121*35144Smarc {
122*35144Smarc 	register FILE *fd;
123*35144Smarc 	register struct fixcmd *fp;
124*35144Smarc 	register char *histname;
125*35144Smarc 	char fname[TMPSIZ];
126*35144Smarc 	char hname[256];
127*35144Smarc 	int maxlines;
128*35144Smarc 	register char *cp;
129*35144Smarc 	register long hsize = 0;
130*35144Smarc 	int his_start;
131*35144Smarc 
132*35144Smarc 	if(fc_fix)
133*35144Smarc 		return(0);
134*35144Smarc 	histname = valup(HISTFILE);
135*35144Smarc 	if(histname==NULL)
136*35144Smarc 	{
137*35144Smarc #ifdef KSHELL
138*35144Smarc 		if(userid==0 && login_sh)
139*35144Smarc 			return(-1);
140*35144Smarc #endif	/* KSHELL */
141*35144Smarc 		cp = movstr(valup(HOME),hname);
142*35144Smarc 		movstr(histfname,cp);
143*35144Smarc 		histname = hname;
144*35144Smarc 	}
145*35144Smarc 	*fname = 0;
146*35144Smarc retry:
147*35144Smarc 	/* first try to open the current file */
148*35144Smarc #ifdef KSHELL
149*35144Smarc 	if((fd=fdopen(open(histname,012),"a+"))==NULL)
150*35144Smarc 	{
151*35144Smarc 		/* if you can't then try to create it */
152*35144Smarc 		if(fd=create(histname))
153*35144Smarc 		{
154*35144Smarc 			fd = chkrdwr(histname,fd);
155*35144Smarc 			chmod(histname,0600);
156*35144Smarc 		}
157*35144Smarc 	}
158*35144Smarc 	else
159*35144Smarc 		hsize=lseek(fileno(fd),0L,2);
160*35144Smarc #else
161*35144Smarc 	if(fd=fopen(histname,"a+"))
162*35144Smarc 	{
163*35144Smarc 		chmod(histname,0600);
164*35144Smarc 		hsize=lseek(fileno(fd),0L,2);
165*35144Smarc 	}
166*35144Smarc #endif	/* KSHELL */
167*35144Smarc 	/* make sure that file has history file format */
168*35144Smarc 	if(hsize && hist_check(fd))
169*35144Smarc 	{
170*35144Smarc 		fclose(fd);
171*35144Smarc 		unlink(histname);
172*35144Smarc 		hsize = 0;
173*35144Smarc 		goto retry;
174*35144Smarc 	}
175*35144Smarc 	if(fd == NULL)
176*35144Smarc 		fd = tmp_open(fname);
177*35144Smarc 	if(fd==NULL)
178*35144Smarc 		return(-1);
179*35144Smarc 	fd = frenumber(fd,FCIO);
180*35144Smarc 	if(cp=valup(HISTSIZE))
181*35144Smarc 		maxlines = (unsigned)aeval(cp);
182*35144Smarc 	else
183*35144Smarc 		maxlines = HIS_DFLT;
184*35144Smarc 	for(fixmask=16;fixmask <= maxlines; fixmask <<=1 );
185*35144Smarc 	if((fp=(struct fixcmd*)malloc(sizeof(struct fixcmd)+ (--fixmask)*sizeof(long)))==NULL)
186*35144Smarc 	{
187*35144Smarc 		fclose(fd);
188*35144Smarc 		return(-1);
189*35144Smarc 	}
190*35144Smarc 	fc_fix = fp;
191*35144Smarc 	fp->fixfd = fd;
192*35144Smarc 	fp->fixmax = maxlines;
193*35144Smarc 	setbuf(fd,malloc(BUFSIZ));
194*35144Smarc 	fp->fixind = 1;
195*35144Smarc 	fp->fixline = 0;
196*35144Smarc 	fp->fixcmds[1] = 2;
197*35144Smarc 	fp->fixcnt = 2;
198*35144Smarc 	if(hsize==0)
199*35144Smarc 	{
200*35144Smarc 		/* put special characters at front of file */
201*35144Smarc 		putc(H_UNDO,fd);
202*35144Smarc 		putc(H_VERSION,fd);
203*35144Smarc 		fflush(fd);
204*35144Smarc 	}
205*35144Smarc 	/* initialize history list */
206*35144Smarc 	if(hsize)
207*35144Smarc 	{
208*35144Smarc 		int nlines = maxlines;
209*35144Smarc 		long size = hsize - (HISMAX/4);
210*35144Smarc 		do
211*35144Smarc 		{
212*35144Smarc 			size -= ((HISMAX/4) + nlines*HISLINE);
213*35144Smarc 			his_start = fp->fixind = hist_nearend(fd,size);
214*35144Smarc 			hist_eof();
215*35144Smarc 			nlines = maxlines - (fp->fixind-his_start);
216*35144Smarc 		}
217*35144Smarc 		while(his_start >1 && nlines>0);
218*35144Smarc 	}
219*35144Smarc 	if(*fname)
220*35144Smarc 		unlink(fname);
221*35144Smarc 	if(login_sh && his_start>1 && hsize > HISMAX)
222*35144Smarc 	{
223*35144Smarc 		FILE *fdo;
224*35144Smarc 		if((fdo=fdopen(open(histname,0),"r"))==NULL)
225*35144Smarc 			return(0);
226*35144Smarc 		unlink(histname);
227*35144Smarc 		hist_trim(fdo,fp->fixind-maxlines);
228*35144Smarc 	}
229*35144Smarc 	return(0);
230*35144Smarc }
231*35144Smarc 
232*35144Smarc /*
233*35144Smarc  * check history file format to see if it begins with special byte
234*35144Smarc  */
235*35144Smarc 
hist_check(fd)236*35144Smarc static int hist_check(fd)
237*35144Smarc register FILE *fd;
238*35144Smarc {
239*35144Smarc 	setbuf(fd,NULL);
240*35144Smarc 	fseek(fd,0L,0);
241*35144Smarc 	if(getc(fd) != H_UNDO)
242*35144Smarc 		return(1);
243*35144Smarc 	hist_version = getc(fd);
244*35144Smarc 	return(0);
245*35144Smarc }
246*35144Smarc 
247*35144Smarc /*
248*35144Smarc  * Copy the last <n> commands to a new file and make this the history file
249*35144Smarc  */
250*35144Smarc 
hist_trim(fdo,n)251*35144Smarc static void hist_trim(fdo,n)
252*35144Smarc register FILE *fdo;
253*35144Smarc {
254*35144Smarc 	register FILE *fd;
255*35144Smarc 	register int c;
256*35144Smarc 	register struct fixcmd *fp = fc_fix;
257*35144Smarc 	struct fixcmd *fsave;
258*35144Smarc 	/* use the old history I/O buffer for fdo */
259*35144Smarc 	setbuf(fdo,fp->fixfd->_base);
260*35144Smarc 	setbuf(fp->fixfd,NULL);
261*35144Smarc 	fc_fix = 0;
262*35144Smarc 	hist_open();
263*35144Smarc 	if(fc_fix==0)
264*35144Smarc 		return;
265*35144Smarc 	fsave = fc_fix;
266*35144Smarc 	fd = fc_fix->fixfd;
267*35144Smarc 	do
268*35144Smarc 	{
269*35144Smarc 		fc_fix = fp;
270*35144Smarc 		fseek(fdo,hist_position(++n),0);
271*35144Smarc 		fc_fix = fsave;
272*35144Smarc 		while((c=getc(fdo))!=EOF && c)
273*35144Smarc 		{
274*35144Smarc 			putc(c,fd);
275*35144Smarc 		}
276*35144Smarc #ifdef KSHELL
277*35144Smarc 		states |= FIXFLG;
278*35144Smarc #endif	/* KSHELL */
279*35144Smarc 		hist_flush();
280*35144Smarc 	}
281*35144Smarc 	while(c!=EOF);
282*35144Smarc 	fclose(fdo);
283*35144Smarc 	free((char*)fdo->_base);
284*35144Smarc 	free((char*)fp);
285*35144Smarc }
286*35144Smarc 
287*35144Smarc /*
288*35144Smarc  * position history file at size and find next command number
289*35144Smarc  */
290*35144Smarc 
hist_nearend(fd,size)291*35144Smarc static int hist_nearend(fd,size)
292*35144Smarc register FILE *fd;
293*35144Smarc long size;
294*35144Smarc {
295*35144Smarc 	register int n = 0;
296*35144Smarc 	register int state = -1;
297*35144Smarc 	register int c;
298*35144Smarc 	if(size <=0)
299*35144Smarc 		goto begin;
300*35144Smarc 	fseek(fd,size,0);
301*35144Smarc 	/* skip to numbered command and return the number */
302*35144Smarc 	/* numbering commands occur after a null and begin with H_CMDNO */
303*35144Smarc 	while((c=getc(fd))!=EOF)
304*35144Smarc 	{
305*35144Smarc 		if(state==5)
306*35144Smarc 		{
307*35144Smarc 			return(n);
308*35144Smarc 		}
309*35144Smarc 		else if(state>0)
310*35144Smarc 		{
311*35144Smarc 			if(state==1)
312*35144Smarc 			{
313*35144Smarc 				/* see if H_CMDNO is followed by 0 */
314*35144Smarc 				if(hist_version && c)
315*35144Smarc 				{
316*35144Smarc 					n += 2;
317*35144Smarc 					state = -1;
318*35144Smarc 					continue;
319*35144Smarc 				}
320*35144Smarc 				n = 0;
321*35144Smarc 			}
322*35144Smarc 			if(hist_version)
323*35144Smarc 				n = (n<<8) + c;
324*35144Smarc 			else if(state < 3)
325*35144Smarc 				n = (n<<7) + (c&0177);
326*35144Smarc 			state++;
327*35144Smarc 		}
328*35144Smarc 		else if(state==0 && c==H_CMDNO)
329*35144Smarc 		{
330*35144Smarc 			fc_fix->fixcnt = size + n + 6;
331*35144Smarc 			state = 1;
332*35144Smarc 		}
333*35144Smarc 		else
334*35144Smarc 		{
335*35144Smarc 			state = (c==0?0:-1);
336*35144Smarc 			n++;
337*35144Smarc 		}
338*35144Smarc 	}
339*35144Smarc begin:
340*35144Smarc 	fseek(fd,2L,0);
341*35144Smarc 	fc_fix->fixcnt = 2;
342*35144Smarc 	return(1);
343*35144Smarc }
344*35144Smarc 
345*35144Smarc /*
346*35144Smarc  * This routine unlinks the history file if the file is a temp file
347*35144Smarc  */
348*35144Smarc 
hist_close()349*35144Smarc void hist_close()
350*35144Smarc {
351*35144Smarc 	if(fc_fix)
352*35144Smarc 		fclose(fc_fix->fixfd);
353*35144Smarc }
354*35144Smarc 
355*35144Smarc /*
356*35144Smarc  * This routine reads the history file from the present position
357*35144Smarc  * to the end-of-file and puts the information in the in-core
358*35144Smarc  * history table
359*35144Smarc  * Note that H_CMDNO is only recognized at the beginning of a command
360*35144Smarc  * and that H_UNDO as the first character of a command is skipped
361*35144Smarc  * unless it is followed by 0.  If followed by 0 then it cancels
362*35144Smarc  * the previous command.
363*35144Smarc  */
364*35144Smarc 
hist_eof()365*35144Smarc void hist_eof()
366*35144Smarc {
367*35144Smarc 	register struct fixcmd *fp = fc_fix;
368*35144Smarc 	register int c;
369*35144Smarc 	register int incr = 0;
370*35144Smarc 	register int oldc = 0;
371*35144Smarc 	register long count = fp->fixcnt;
372*35144Smarc 	int skip = 0;
373*35144Smarc 	heof++;		/* don't add line number markers */
374*35144Smarc 	fseek(fp->fixfd,count,0);
375*35144Smarc 	while((c=getc(fp->fixfd))!=EOF)
376*35144Smarc 	{
377*35144Smarc 		count++;
378*35144Smarc 		if(skip-- > 0)
379*35144Smarc 		{
380*35144Smarc 			oldc = 0;
381*35144Smarc 			continue;
382*35144Smarc 		}
383*35144Smarc 		if(c == 0)
384*35144Smarc 		{
385*35144Smarc 			if(oldc==H_CMDNO && incr==0)
386*35144Smarc 				skip = 3;
387*35144Smarc 			fp->fixind += incr;
388*35144Smarc 			fp->fixcmds[fp->fixind&fixmask] = count;
389*35144Smarc 			incr = 0;
390*35144Smarc 		}
391*35144Smarc 		else if(oldc == 0)
392*35144Smarc 		{
393*35144Smarc 			if(c == H_CMDNO)
394*35144Smarc 			{
395*35144Smarc 				/* old format history file */
396*35144Smarc 				if(hist_version==0)
397*35144Smarc 					skip = 4;
398*35144Smarc 				incr = 0;
399*35144Smarc 			}
400*35144Smarc 			else if(c==H_UNDO)
401*35144Smarc 				incr = -1;
402*35144Smarc 		}
403*35144Smarc 		else
404*35144Smarc 			incr = 1;
405*35144Smarc 		oldc = c;
406*35144Smarc 	}
407*35144Smarc 	fp->fixline = 0;
408*35144Smarc 	fp->fixcnt = count;
409*35144Smarc 	heof = 0;
410*35144Smarc }
411*35144Smarc 
412*35144Smarc /*
413*35144Smarc  * This routine will cause the previous command to be cancelled
414*35144Smarc  */
415*35144Smarc 
hist_cancel()416*35144Smarc void hist_cancel()
417*35144Smarc {
418*35144Smarc 	register struct fixcmd *fp = fc_fix;
419*35144Smarc 	register FILE *fd;
420*35144Smarc 	register int c;
421*35144Smarc 	if(fp==NULL)
422*35144Smarc 		return;
423*35144Smarc 	fd = fp->fixfd;
424*35144Smarc 	putc(H_UNDO,fd);
425*35144Smarc 	putc(0,fd);
426*35144Smarc 	fflush(fd);
427*35144Smarc 	fp->fixcnt += 2;
428*35144Smarc 	c = (--fp->fixind)&fixmask;
429*35144Smarc 	fp->fixcmds[c] = fp->fixcnt;
430*35144Smarc }
431*35144Smarc 
432*35144Smarc /*
433*35144Smarc  * This routine adds one or two null bytes and flushes the history buffer
434*35144Smarc  */
435*35144Smarc 
hist_flush()436*35144Smarc void hist_flush()
437*35144Smarc {
438*35144Smarc 	register struct fixcmd *fp = fc_fix;
439*35144Smarc 	register FILE *fd;
440*35144Smarc 	register int c;
441*35144Smarc 	if(fp==NULL)
442*35144Smarc 		return;
443*35144Smarc #ifdef KSHELL
444*35144Smarc 	if((states&FIXFLG) == 0)
445*35144Smarc 		return;
446*35144Smarc 	states &= ~FIXFLG;
447*35144Smarc #endif	/* KSHELL */
448*35144Smarc 	fd = fp->fixfd;
449*35144Smarc 	fp->fixline = 0;
450*35144Smarc 	/* remove whitespace from end of commands */
451*35144Smarc 	while(--fd->_ptr >= fd->_base)
452*35144Smarc 	{
453*35144Smarc 		if((c= *fd->_ptr)!=NL && !isspace(c))
454*35144Smarc 			break;
455*35144Smarc 	}
456*35144Smarc 	fd->_cnt = ++fd->_ptr - fd->_base;
457*35144Smarc 	if(fd->_cnt<=0)
458*35144Smarc 	{
459*35144Smarc 		fp->fixind--;
460*35144Smarc 		goto set_count;
461*35144Smarc 	}
462*35144Smarc 	putc(NL,fd);
463*35144Smarc 	putc('\0',fd);
464*35144Smarc 	fflush(fd);
465*35144Smarc set_count:
466*35144Smarc 	fp->fixcnt = lseek(fileno(fd),0L,2);
467*35144Smarc 	/* start each command on an even byte boundary */
468*35144Smarc 	if(fp->fixcnt&01)
469*35144Smarc 	{
470*35144Smarc 		fp->fixcnt++;
471*35144Smarc 		putc('\0',fd);
472*35144Smarc 		fflush(fd);
473*35144Smarc 	}
474*35144Smarc 	c = (++fp->fixind)&fixmask;
475*35144Smarc 	fp->fixcmds[c] = fp->fixcnt;
476*35144Smarc 	if((c = fp->fixcmds[c])> (HISMAX/4) && !heof)
477*35144Smarc 	{
478*35144Smarc 		/* put line number in file */
479*35144Smarc 		fp->fixcnt += 6;
480*35144Smarc 		putc(H_CMDNO,fd);
481*35144Smarc 		putc(0,fd);
482*35144Smarc 		c = (fp->fixind>>16);
483*35144Smarc 		putc(c,fd);
484*35144Smarc 		c = (fp->fixind>>8);
485*35144Smarc 		putc(c,fd);
486*35144Smarc 		c = fp->fixind;
487*35144Smarc 		putc(c,fd);
488*35144Smarc 		putc(0,fd);
489*35144Smarc 		fflush(fd);
490*35144Smarc 		fp->fixcmds[c&fixmask] = fp->fixcnt;
491*35144Smarc 	}
492*35144Smarc }
493*35144Smarc 
494*35144Smarc /*
495*35144Smarc  * return byte offset in history file for command <n>
496*35144Smarc  */
497*35144Smarc 
hist_position(n)498*35144Smarc long hist_position(n)
499*35144Smarc int n;
500*35144Smarc {
501*35144Smarc 	register struct fixcmd *fp = fc_fix;
502*35144Smarc 	return(fp->fixcmds[n&fixmask]);
503*35144Smarc }
504*35144Smarc 
505*35144Smarc /*
506*35144Smarc  * write the command starting at offset <offset> onto file <fd>.
507*35144Smarc  * listing stops when character <last> is encountered or end-of-string.
508*35144Smarc  * each new-line character is replaced with string <nl>.
509*35144Smarc  */
510*35144Smarc 
hist_list(offset,last,nl)511*35144Smarc long hist_list(offset,last,nl)
512*35144Smarc long offset;
513*35144Smarc int last;
514*35144Smarc char *nl;
515*35144Smarc {
516*35144Smarc 	register int oldc;
517*35144Smarc 	register FILE *fd;
518*35144Smarc 	register int c;
519*35144Smarc 	if(offset<0)
520*35144Smarc 	{
521*35144Smarc 		p_str(unknown,NL);
522*35144Smarc 		return(-1);
523*35144Smarc 	}
524*35144Smarc 	fd = fc_fix->fixfd;
525*35144Smarc 	fseek(fd,offset,0);
526*35144Smarc 	oldc=getc(fd);
527*35144Smarc 	for(offset++;oldc && oldc!=last;oldc=c,offset++)
528*35144Smarc 	{
529*35144Smarc 		if((c = getc(fd)) == EOF)
530*35144Smarc 			return(offset);
531*35144Smarc 		if(oldc=='\n')
532*35144Smarc 		{
533*35144Smarc 			if(c)
534*35144Smarc 			{
535*35144Smarc 				fputs(nl,output);
536*35144Smarc 				continue;
537*35144Smarc 			}
538*35144Smarc 			/* don't print trailing newline for job control */
539*35144Smarc 			else if(last=='&')
540*35144Smarc 				return(offset);
541*35144Smarc 		}
542*35144Smarc 		putc(oldc,output);
543*35144Smarc 	}
544*35144Smarc 	return(offset);
545*35144Smarc }
546*35144Smarc 
547*35144Smarc /*
548*35144Smarc  * find index for last line with given string
549*35144Smarc  * If flag==0 then line must begin with string
550*35144Smarc  * direction < 1 for backwards search
551*35144Smarc */
552*35144Smarc 
hist_find(string,index1,flag,direction)553*35144Smarc histloc hist_find(string,index1,flag,direction)
554*35144Smarc char *string;
555*35144Smarc int index1;
556*35144Smarc int direction;
557*35144Smarc {
558*35144Smarc 	register struct fixcmd *fp = fc_fix;
559*35144Smarc 	register char *cp;
560*35144Smarc 	register int c;
561*35144Smarc 	long offset;
562*35144Smarc 	int count;
563*35144Smarc 	int index2;
564*35144Smarc 	histloc location;
565*35144Smarc #ifdef MULTIBYTE
566*35144Smarc 	int nbytes = 0;
567*35144Smarc #endif /* MULTIBYTE */
568*35144Smarc 	location.his_command = -1;
569*35144Smarc 	if(fp==NULL)
570*35144Smarc 		return(location);
571*35144Smarc 	index2 = fp->fixind;
572*35144Smarc 	if(direction<0)
573*35144Smarc 	{
574*35144Smarc 		index2 -= fp->fixmax;
575*35144Smarc 		if(index2<1)
576*35144Smarc 			index2 = 1;
577*35144Smarc 		if(index1 <= index2)
578*35144Smarc 			return(location);
579*35144Smarc 	}
580*35144Smarc 	else if(index1 >= index2)
581*35144Smarc 		return(location);
582*35144Smarc 	while(index1!=index2)
583*35144Smarc 	{
584*35144Smarc 		direction>0?++index1:--index1;
585*35144Smarc 		offset = hist_position(index1);
586*35144Smarc 		location.his_line = 0;
587*35144Smarc #ifdef KSHELL
588*35144Smarc 		/* allow a search to be aborted */
589*35144Smarc 		if(trapnote&SIGSET)
590*35144Smarc 			exitsh(SIGFAIL);
591*35144Smarc #endif /* KSHELL */
592*35144Smarc 		do
593*35144Smarc 		{
594*35144Smarc 			if(offset>=0)
595*35144Smarc 			{
596*35144Smarc 				fseek(fp->fixfd,offset,0);
597*35144Smarc 				count = offset;
598*35144Smarc 			}
599*35144Smarc 			offset = -1;
600*35144Smarc 			for(cp=string;*cp;cp++)
601*35144Smarc 			{
602*35144Smarc 				if((c=getc(fp->fixfd)) == EOF)
603*35144Smarc 					break;
604*35144Smarc 				if(c == 0)
605*35144Smarc 					break;
606*35144Smarc 				count++;
607*35144Smarc #ifdef MULTIBYTE
608*35144Smarc 				/* always position at character boundary */
609*35144Smarc 				if(--nbytes > 0)
610*35144Smarc 				{
611*35144Smarc 					if(cp==string)
612*35144Smarc 					{
613*35144Smarc 						cp--;
614*35144Smarc 						continue;
615*35144Smarc 					}
616*35144Smarc 				}
617*35144Smarc 				else
618*35144Smarc 				{
619*35144Smarc 					nbytes = echarset(c);
620*35144Smarc 					nbytes = in_csize(nbytes) + (nbytes>=2);
621*35144Smarc 				}
622*35144Smarc #endif /* MULTIBYTE */
623*35144Smarc 				if(c == '\n')
624*35144Smarc 					location.his_line++;
625*35144Smarc 				/* save earliest possible matching character */
626*35144Smarc 				if(flag && c == *string && offset<0)
627*35144Smarc 					offset = count;
628*35144Smarc 				if(*cp != c )
629*35144Smarc 					break;
630*35144Smarc 			}
631*35144Smarc 			if(*cp==0)
632*35144Smarc 				/* match found */
633*35144Smarc 			{
634*35144Smarc 				location.his_command = index1;
635*35144Smarc 				return(location);
636*35144Smarc 			}
637*35144Smarc 		}
638*35144Smarc 		while(flag && c && c != EOF);
639*35144Smarc 	}
640*35144Smarc 	fseek(fp->fixfd,0L,2);
641*35144Smarc 	return(location);
642*35144Smarc }
643*35144Smarc 
644*35144Smarc #if ESH || VSH
645*35144Smarc /*
646*35144Smarc  * copy command <command> from history file to s1
647*35144Smarc  * at most MAXLINE characters copied
648*35144Smarc  * if s1==0 the number of lines for the command is returned
649*35144Smarc  * line=linenumber  for emacs copy and only this line of command will be copied
650*35144Smarc  * line < 0 for full command copy
651*35144Smarc  * -1 returned if there is no history file
652*35144Smarc  */
653*35144Smarc 
hist_copy(s1,command,line)654*35144Smarc int hist_copy(s1,command,line)
655*35144Smarc register char *s1;
656*35144Smarc {
657*35144Smarc 	register int c;
658*35144Smarc 	register struct fixcmd *fp = fc_fix;
659*35144Smarc 	register int count = 0;
660*35144Smarc 	register char *s1max = s1+MAXLINE;
661*35144Smarc 	long offset;
662*35144Smarc 	if(fp==NULL)
663*35144Smarc 		return(-1);
664*35144Smarc 	offset =  hist_position(command);
665*35144Smarc 	fseek(fp->fixfd,offset,0);
666*35144Smarc 	while ((c = getc(fp->fixfd)) && c!=EOF)
667*35144Smarc 	{
668*35144Smarc 		if(c=='\n')
669*35144Smarc 		{
670*35144Smarc 			if(count++ ==line)
671*35144Smarc 				break;
672*35144Smarc 			else if(line >= 0)
673*35144Smarc 				continue;
674*35144Smarc 		}
675*35144Smarc 		if(s1 && (line<0 || line==count))
676*35144Smarc 		{
677*35144Smarc 			if(s1 >= s1max)
678*35144Smarc 			{
679*35144Smarc 				*--s1 = 0;
680*35144Smarc 				break;
681*35144Smarc 			}
682*35144Smarc 			*s1++ = c;
683*35144Smarc 		}
684*35144Smarc 
685*35144Smarc 	}
686*35144Smarc 	if(s1==0)
687*35144Smarc 		return(count);
688*35144Smarc 	if((c= *(s1-1)) == '\n')
689*35144Smarc 		s1--;
690*35144Smarc 	*s1 = '\0';
691*35144Smarc 	fseek(fp->fixfd,0L,2);
692*35144Smarc 	return(count);
693*35144Smarc }
694*35144Smarc 
695*35144Smarc /*
696*35144Smarc  * return word number <word> from command number <command>
697*35144Smarc  */
698*35144Smarc 
hist_word(s1,word)699*35144Smarc char *hist_word(s1,word)
700*35144Smarc char *s1;
701*35144Smarc {
702*35144Smarc 	register int c;
703*35144Smarc 	register char *cp = s1;
704*35144Smarc 	register int flag = 0;
705*35144Smarc 	if(fc_fix==0)
706*35144Smarc #ifdef KSHELL
707*35144Smarc 		return(lastarg);
708*35144Smarc #else
709*35144Smarc 		return(NULL);
710*35144Smarc #endif /* KSHELL */
711*35144Smarc 	hist_copy(s1,fc_fix->fixind-1,-1);
712*35144Smarc 	for(;c = *cp;cp++)
713*35144Smarc 	{
714*35144Smarc 		c = isspace(c);
715*35144Smarc 		if(c && flag)
716*35144Smarc 		{
717*35144Smarc 			*cp = 0;
718*35144Smarc 			if(--word==0)
719*35144Smarc 				break;
720*35144Smarc 			flag = 0;
721*35144Smarc 		}
722*35144Smarc 		else if(c==0 && flag==0)
723*35144Smarc 		{
724*35144Smarc 			s1 = cp;
725*35144Smarc 			flag++;
726*35144Smarc 		}
727*35144Smarc 	}
728*35144Smarc 	*cp = 0;
729*35144Smarc 	return(s1);
730*35144Smarc }
731*35144Smarc 
732*35144Smarc #endif	/* ESH */
733*35144Smarc 
734*35144Smarc #ifdef ESH
735*35144Smarc /*
736*35144Smarc  * given the current command and line number,
737*35144Smarc  * and number of lines back or foward,
738*35144Smarc  * compute the new command and line number.
739*35144Smarc  */
740*35144Smarc 
hist_locate(command,line,lines)741*35144Smarc histloc hist_locate(command,line,lines)
742*35144Smarc register int command;
743*35144Smarc register int line;
744*35144Smarc int lines;
745*35144Smarc {
746*35144Smarc 	histloc next;
747*35144Smarc 	line += lines;
748*35144Smarc 	if(fc_fix==NULL)
749*35144Smarc 	{
750*35144Smarc 		command = -1;
751*35144Smarc 		goto done;
752*35144Smarc 	}
753*35144Smarc 	if(lines > 0)
754*35144Smarc 	{
755*35144Smarc 		register int count;
756*35144Smarc 		while(command <= fc_fix->fixind)
757*35144Smarc 		{
758*35144Smarc 			count = hist_copy(NIL,command,-1);
759*35144Smarc 			if(count > line)
760*35144Smarc 				goto done;
761*35144Smarc 			line -= count;
762*35144Smarc 			command++;
763*35144Smarc 		}
764*35144Smarc 	}
765*35144Smarc 	else
766*35144Smarc 	{
767*35144Smarc 		register int least = fc_fix->fixind-fc_fix->fixmax;
768*35144Smarc 		while(1)
769*35144Smarc 		{
770*35144Smarc 			if(line >=0)
771*35144Smarc 				goto done;
772*35144Smarc 			if(--command < least)
773*35144Smarc 				break;
774*35144Smarc 			line += hist_copy(NIL,command,-1);
775*35144Smarc 		}
776*35144Smarc 		command = -1;
777*35144Smarc 	}
778*35144Smarc 	next.his_command = command;
779*35144Smarc 	return(next);
780*35144Smarc done:
781*35144Smarc 	next.his_line = line;
782*35144Smarc 	next.his_command = command;
783*35144Smarc 	return(next);
784*35144Smarc }
785*35144Smarc #endif	/* ESH */
786*35144Smarc 
787*35144Smarc #ifdef KSHELL
788*35144Smarc 
789*35144Smarc /*
790*35144Smarc  * given a file containing a command and a string of the form old=new,
791*35144Smarc  * execute the command with the string old replaced by new
792*35144Smarc  */
hist_subst(command,fd,replace)793*35144Smarc void hist_subst(command,fd,replace)
794*35144Smarc char *command;
795*35144Smarc FILE *fd;
796*35144Smarc char *replace;
797*35144Smarc {
798*35144Smarc 	register char *new=replace;
799*35144Smarc 	register char *sp = locstak();
800*35144Smarc 	register int c;
801*35144Smarc 	char *string;
802*35144Smarc 	while(*++new != '='); /* skip to '=' */
803*35144Smarc 	while ((c=getc(fd)) != EOF)
804*35144Smarc 		*sp++ = c;
805*35144Smarc 	string = endstak(sp);
806*35144Smarc 	fclose(fd);
807*35144Smarc 	*new++ =  0;
808*35144Smarc 	if(substitute(string,replace,new,(sp=locstak())))
809*35144Smarc 		endstak(sp+strlen(sp));
810*35144Smarc 	else
811*35144Smarc 		failed(command,badsub);
812*35144Smarc 	*(new-1) =  '=';
813*35144Smarc 	fputs(sp,fc_fix->fixfd);
814*35144Smarc 	hist_flush();
815*35144Smarc 	fputs(sp,output);
816*35144Smarc 	execexp(sp,(FILE*)0);
817*35144Smarc }
818*35144Smarc #endif	/* KSHELL */
819