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