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 <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 
36 static 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[], Shbltin_t *context){}
41 #endif
b_hist(int argc,char * argv[],Shbltin_t * context)42 int	b_hist(int argc,char *argv[], Shbltin_t *context)
43 {
44 	register History_t *hp;
45 	register char *arg;
46 	register int flag,fdo;
47 	register Shell_t *shp = context->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 	{
219 		arg = (char*)e_defedit;
220 		if(*arg!='/')
221 			errormsg(SH_DICT,ERROR_exit(1),"ed not found set FCEDIT");
222 	}
223 #ifdef apollo
224 	/*
225 	 * Code to support the FC using the pad editor.
226 	 * Exampled of how to use: HISTEDIT=pad
227 	 */
228 	if (strcmp (arg, "pad") == 0)
229 	{
230 		extern int pad_create(char*);
231 		sh_close(fdo);
232 		fdo = pad_create(fname);
233 		pad_wait(fdo);
234 		unlink(fname);
235 		strcat(fname, ".bak");
236 		unlink(fname);
237 		lseek(fdo,(off_t)0,SEEK_SET);
238 	}
239 	else
240 	{
241 #endif /* apollo */
242 	if(*arg != '-')
243 	{
244 		char *com[3];
245 		com[0] =  arg;
246 		com[1] =  fname;
247 		com[2] = 0;
248 		error_info.errors = sh_eval(sh_sfeval(com),0);
249 	}
250 	fdo = sh_chkopen(fname);
251 	unlink(fname);
252 	free((void*)fname);
253 #ifdef apollo
254 	}
255 #endif /* apollo */
256 	/* don't history fc itself unless forked */
257 	error_info.flags |= ERROR_SILENT;
258 	if(!sh_isstate(SH_FORKED))
259 		hist_cancel(hp);
260 	sh_onstate(SH_HISTORY);
261 	sh_onstate(SH_VERBOSE);	/* echo lines as read */
262 	if(replace)
263 		hist_subst(error_info.id,fdo,replace);
264 	else if(error_info.errors == 0)
265 	{
266 		char buff[IOBSIZE+1];
267 		Sfio_t *iop = sfnew(NIL(Sfio_t*),buff,IOBSIZE,fdo,SF_READ);
268 		/* read in and run the command */
269 		if(shp->hist_depth++ > HIST_RECURSE)
270 			errormsg(SH_DICT,ERROR_exit(1),e_toodeep,"history");
271 		sh_eval(iop,1);
272 		shp->hist_depth--;
273 	}
274 	else
275 	{
276 		sh_close(fdo);
277 		if(!sh_isoption(SH_VERBOSE))
278 			sh_offstate(SH_VERBOSE);
279 		sh_offstate(SH_HISTORY);
280 	}
281 	return(shp->exitval);
282 }
283 
284 
285 /*
286  * given a file containing a command and a string of the form old=new,
287  * execute the command with the string old replaced by new
288  */
289 
hist_subst(const char * command,int fd,char * replace)290 static void hist_subst(const char *command,int fd,char *replace)
291 {
292 	register char *newp=replace;
293 	register char *sp;
294 	register int c;
295 	off_t size;
296 	char *string;
297 	while(*++newp != '='); /* skip to '=' */
298 	if((size = lseek(fd,(off_t)0,SEEK_END)) < 0)
299 		return;
300 	lseek(fd,(off_t)0,SEEK_SET);
301 	c =  (int)size;
302 	string = stakalloc(c+1);
303 	if(read(fd,string,c)!=c)
304 		return;
305 	string[c] = 0;
306 	*newp++ =  0;
307 	if((sp=sh_substitute(string,replace,newp))==0)
308 		errormsg(SH_DICT,ERROR_exit(1),e_subst,command);
309 	*(newp-1) =  '=';
310 	sh_eval(sfopen(NIL(Sfio_t*),sp,"s"),1);
311 }
312 
313