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 #include	"defs.h"
22*4887Schin #include	<stak.h>
23*4887Schin #include	<ls.h>
24*4887Schin #include	<error.h>
25*4887Schin #include	<ctype.h>
26*4887Schin #include	"variables.h"
27*4887Schin #include	"io.h"
28*4887Schin #include	"name.h"
29*4887Schin #include	"history.h"
30*4887Schin #include	"builtins.h"
31*4887Schin #if SHOPT_HISTEXPAND
32*4887Schin #   include	"edit.h"
33*4887Schin #endif
34*4887Schin 
35*4887Schin #define HIST_RECURSE	5
36*4887Schin 
37*4887Schin static void hist_subst(const char*, int fd, char*);
38*4887Schin 
39*4887Schin #if 0
40*4887Schin     /* for the benefit of the dictionary generator */
41*4887Schin     int	b_fc(int argc,char *argv[], void *extra){}
42*4887Schin #endif
43*4887Schin int	b_hist(int argc,char *argv[], void *extra)
44*4887Schin {
45*4887Schin 	register History_t *hp;
46*4887Schin 	register char *arg;
47*4887Schin 	register int flag,fdo;
48*4887Schin 	register Shell_t *shp = (Shell_t*)extra;
49*4887Schin 	Sfio_t *outfile;
50*4887Schin 	char *fname;
51*4887Schin 	int range[2], incr, index2, indx= -1;
52*4887Schin 	char *edit = 0;		/* name of editor */
53*4887Schin 	char *replace = 0;		/* replace old=new */
54*4887Schin 	int lflag = 0, nflag = 0, rflag = 0;
55*4887Schin #if SHOPT_HISTEXPAND
56*4887Schin 	int pflag = 0;
57*4887Schin #endif
58*4887Schin 	Histloc_t location;
59*4887Schin 	NOT_USED(argc);
60*4887Schin 	if(!sh_histinit())
61*4887Schin 		errormsg(SH_DICT,ERROR_system(1),e_histopen);
62*4887Schin 	hp = shp->hist_ptr;
63*4887Schin 	while((flag = optget(argv,sh_opthist))) switch(flag)
64*4887Schin 	{
65*4887Schin 	    case 'e':
66*4887Schin 		edit = opt_info.arg;
67*4887Schin 		break;
68*4887Schin 	    case 'n':
69*4887Schin 		nflag++;
70*4887Schin 		break;
71*4887Schin 	    case 'l':
72*4887Schin 		lflag++;
73*4887Schin 		break;
74*4887Schin 	    case 'r':
75*4887Schin 		rflag++;
76*4887Schin 		break;
77*4887Schin 	    case 's':
78*4887Schin 		edit = "-";
79*4887Schin 		break;
80*4887Schin #if SHOPT_HISTEXPAND
81*4887Schin 	    case 'p':
82*4887Schin 		pflag++;
83*4887Schin 		break;
84*4887Schin #endif
85*4887Schin 	    case 'N':
86*4887Schin 		if(indx<=0)
87*4887Schin 		{
88*4887Schin 			if((flag = hist_max(hp) - opt_info.num-1) < 0)
89*4887Schin 				flag = 1;
90*4887Schin 			range[++indx] = flag;
91*4887Schin 			break;
92*4887Schin 		}
93*4887Schin 	    case ':':
94*4887Schin 		errormsg(SH_DICT,2, "%s", opt_info.arg);
95*4887Schin 		break;
96*4887Schin 	    case '?':
97*4887Schin 		errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
98*4887Schin 		break;
99*4887Schin 	}
100*4887Schin 	if(error_info.errors)
101*4887Schin 		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
102*4887Schin 	argv += (opt_info.index-1);
103*4887Schin #if SHOPT_HISTEXPAND
104*4887Schin 	if(pflag)
105*4887Schin 	{
106*4887Schin 		hist_cancel(hp);
107*4887Schin 		pflag = 0;
108*4887Schin 		while(arg=argv[1])
109*4887Schin 		{
110*4887Schin 			flag = hist_expand(arg,&replace);
111*4887Schin 			if(!(flag & HIST_ERROR))
112*4887Schin 				sfputr(sfstdout, replace, '\n');
113*4887Schin 			else
114*4887Schin 				pflag = 1;
115*4887Schin 			if(replace)
116*4887Schin 				free(replace);
117*4887Schin 			argv++;
118*4887Schin 		}
119*4887Schin 		return pflag;
120*4887Schin 	}
121*4887Schin #endif
122*4887Schin 	flag = indx;
123*4887Schin 	while(flag<1 && (arg=argv[1]))
124*4887Schin 	{
125*4887Schin 		/* look for old=new argument */
126*4887Schin 		if(!replace && strchr(arg+1,'='))
127*4887Schin 		{
128*4887Schin 			replace = arg;
129*4887Schin 			argv++;
130*4887Schin 			continue;
131*4887Schin 		}
132*4887Schin 		else if(isdigit(*arg) || *arg == '-')
133*4887Schin 		{
134*4887Schin 			/* see if completely numeric */
135*4887Schin 			do	arg++;
136*4887Schin 			while(isdigit(*arg));
137*4887Schin 			if(*arg==0)
138*4887Schin 			{
139*4887Schin 				arg = argv[1];
140*4887Schin 				range[++flag] = (int)strtol(arg, (char**)0, 10);
141*4887Schin 				if(*arg == '-')
142*4887Schin 					range[flag] += (hist_max(hp)-1);
143*4887Schin 				argv++;
144*4887Schin 				continue;
145*4887Schin 			}
146*4887Schin 		}
147*4887Schin 		/* search for last line starting with string */
148*4887Schin 		location = hist_find(hp,argv[1],hist_max(hp)-1,0,-1);
149*4887Schin 		if((range[++flag] = location.hist_command) < 0)
150*4887Schin 			errormsg(SH_DICT,ERROR_exit(1),e_found,argv[1]);
151*4887Schin 		argv++;
152*4887Schin 	}
153*4887Schin 	if(flag <0)
154*4887Schin 	{
155*4887Schin 		/* set default starting range */
156*4887Schin 		if(lflag)
157*4887Schin 		{
158*4887Schin 			flag = hist_max(hp)-16;
159*4887Schin 			if(flag<1)
160*4887Schin 				flag = 1;
161*4887Schin 		}
162*4887Schin 		else
163*4887Schin 			flag = hist_max(hp)-2;
164*4887Schin 		range[0] = flag;
165*4887Schin 		flag = 0;
166*4887Schin 	}
167*4887Schin 	index2 = hist_min(hp);
168*4887Schin 	if(range[0]<index2)
169*4887Schin 		range[0] = index2;
170*4887Schin 	if(flag==0)
171*4887Schin 		/* set default termination range */
172*4887Schin 		range[1] = (lflag?hist_max(hp)-1:range[0]);
173*4887Schin 	if(range[1]>=(flag=(hist_max(hp) - !lflag)))
174*4887Schin 		range[1] = flag;
175*4887Schin 	/* check for valid ranges */
176*4887Schin 	if(range[1]<index2 || range[0]>=flag)
177*4887Schin 		errormsg(SH_DICT,ERROR_exit(1),e_badrange,range[0],range[1]);
178*4887Schin 	if(edit && *edit=='-' && range[0]!=range[1])
179*4887Schin 		errormsg(SH_DICT,ERROR_exit(1),e_eneedsarg);
180*4887Schin 	/* now list commands from range[rflag] to range[1-rflag] */
181*4887Schin 	incr = 1;
182*4887Schin 	flag = rflag>0;
183*4887Schin 	if(range[1-flag] < range[flag])
184*4887Schin 		incr = -1;
185*4887Schin 	if(lflag)
186*4887Schin 	{
187*4887Schin 		outfile = sfstdout;
188*4887Schin 		arg = "\n\t";
189*4887Schin 	}
190*4887Schin 	else
191*4887Schin 	{
192*4887Schin 		if(!(fname=pathtmp(NIL(char*),0,0,NIL(int*))))
193*4887Schin 			errormsg(SH_DICT,ERROR_exit(1),e_create,"");
194*4887Schin 		if((fdo=open(fname,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) < 0)
195*4887Schin 			errormsg(SH_DICT,ERROR_system(1),e_create,fname);
196*4887Schin 		outfile= sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fdo,SF_WRITE);
197*4887Schin 		arg = "\n";
198*4887Schin 		nflag++;
199*4887Schin 	}
200*4887Schin 	while(1)
201*4887Schin 	{
202*4887Schin 		if(nflag==0)
203*4887Schin 			sfprintf(outfile,"%d\t",range[flag]);
204*4887Schin 		else if(lflag)
205*4887Schin 			sfputc(outfile,'\t');
206*4887Schin 		hist_list(shp->hist_ptr,outfile,hist_tell(shp->hist_ptr,range[flag]),0,arg);
207*4887Schin 		if(lflag)
208*4887Schin 			sh_sigcheck();
209*4887Schin 		if(range[flag] == range[1-flag])
210*4887Schin 			break;
211*4887Schin 		range[flag] += incr;
212*4887Schin 	}
213*4887Schin 	if(lflag)
214*4887Schin 		return(0);
215*4887Schin 	sfclose(outfile);
216*4887Schin 	hist_eof(hp);
217*4887Schin 	arg = edit;
218*4887Schin 	if(!arg && !(arg=nv_getval(nv_scoped(HISTEDIT))) && !(arg=nv_getval(nv_scoped(FCEDNOD))))
219*4887Schin 		arg = (char*)e_defedit;
220*4887Schin #ifdef apollo
221*4887Schin 	/*
222*4887Schin 	 * Code to support the FC using the pad editor.
223*4887Schin 	 * Exampled of how to use: HISTEDIT=pad
224*4887Schin 	 */
225*4887Schin 	if (strcmp (arg, "pad") == 0)
226*4887Schin 	{
227*4887Schin 		extern int pad_create(char*);
228*4887Schin 		sh_close(fdo);
229*4887Schin 		fdo = pad_create(fname);
230*4887Schin 		pad_wait(fdo);
231*4887Schin 		unlink(fname);
232*4887Schin 		strcat(fname, ".bak");
233*4887Schin 		unlink(fname);
234*4887Schin 		lseek(fdo,(off_t)0,SEEK_SET);
235*4887Schin 	}
236*4887Schin 	else
237*4887Schin 	{
238*4887Schin #endif /* apollo */
239*4887Schin 	if(*arg != '-')
240*4887Schin 	{
241*4887Schin 		char *com[3];
242*4887Schin 		com[0] =  arg;
243*4887Schin 		com[1] =  fname;
244*4887Schin 		com[2] = 0;
245*4887Schin 		error_info.errors = sh_eval(sh_sfeval(com),0);
246*4887Schin 	}
247*4887Schin 	fdo = sh_chkopen(fname);
248*4887Schin 	unlink(fname);
249*4887Schin 	free((void*)fname);
250*4887Schin #ifdef apollo
251*4887Schin 	}
252*4887Schin #endif /* apollo */
253*4887Schin 	/* don't history fc itself unless forked */
254*4887Schin 	error_info.flags |= ERROR_SILENT;
255*4887Schin 	if(!sh_isstate(SH_FORKED))
256*4887Schin 		hist_cancel(hp);
257*4887Schin 	sh_onstate(SH_HISTORY);
258*4887Schin 	sh_onstate(SH_VERBOSE);	/* echo lines as read */
259*4887Schin 	if(replace)
260*4887Schin 		hist_subst(error_info.id,fdo,replace);
261*4887Schin 	else if(error_info.errors == 0)
262*4887Schin 	{
263*4887Schin 		char buff[IOBSIZE+1];
264*4887Schin 		Sfio_t *iop = sfnew(NIL(Sfio_t*),buff,IOBSIZE,fdo,SF_READ);
265*4887Schin 		/* read in and run the command */
266*4887Schin 		if(shp->hist_depth++ > HIST_RECURSE)
267*4887Schin 			errormsg(SH_DICT,ERROR_exit(1),e_toodeep,"history");
268*4887Schin 		sh_eval(iop,1);
269*4887Schin 		shp->hist_depth--;
270*4887Schin 	}
271*4887Schin 	else
272*4887Schin 	{
273*4887Schin 		sh_close(fdo);
274*4887Schin 		if(!sh_isoption(SH_VERBOSE))
275*4887Schin 			sh_offstate(SH_VERBOSE);
276*4887Schin 		sh_offstate(SH_HISTORY);
277*4887Schin 	}
278*4887Schin 	return(shp->exitval);
279*4887Schin }
280*4887Schin 
281*4887Schin 
282*4887Schin /*
283*4887Schin  * given a file containing a command and a string of the form old=new,
284*4887Schin  * execute the command with the string old replaced by new
285*4887Schin  */
286*4887Schin 
287*4887Schin static void hist_subst(const char *command,int fd,char *replace)
288*4887Schin {
289*4887Schin 	register char *newp=replace;
290*4887Schin 	register char *sp;
291*4887Schin 	register int c;
292*4887Schin 	off_t size;
293*4887Schin 	char *string;
294*4887Schin 	while(*++newp != '='); /* skip to '=' */
295*4887Schin 	if((size = lseek(fd,(off_t)0,SEEK_END)) < 0)
296*4887Schin 		return;
297*4887Schin 	lseek(fd,(off_t)0,SEEK_SET);
298*4887Schin 	c =  (int)size;
299*4887Schin 	string = stakalloc(c+1);
300*4887Schin 	if(read(fd,string,c)!=c)
301*4887Schin 		return;
302*4887Schin 	string[c] = 0;
303*4887Schin 	*newp++ =  0;
304*4887Schin 	if((sp=sh_substitute(string,replace,newp))==0)
305*4887Schin 		errormsg(SH_DICT,ERROR_exit(1),e_subst,command);
306*4887Schin 	*(newp-1) =  '=';
307*4887Schin 	sh_eval(sfopen(NIL(Sfio_t*),sp,"s"),1);
308*4887Schin }
309*4887Schin 
310