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