14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*8462SApril.Chin@Sun.COM *          Copyright (c) 1982-2008 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
7*8462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                  David Korn <dgk@research.att.com>                   *
184887Schin *                                                                      *
194887Schin ***********************************************************************/
204887Schin #pragma prototyped
214887Schin #include	"defs.h"
224887Schin #include	<stak.h>
234887Schin #include	<ls.h>
244887Schin #include	<error.h>
254887Schin #include	<ctype.h>
264887Schin #include	"variables.h"
274887Schin #include	"io.h"
284887Schin #include	"name.h"
294887Schin #include	"history.h"
304887Schin #include	"builtins.h"
314887Schin #if SHOPT_HISTEXPAND
324887Schin #   include	"edit.h"
334887Schin #endif
344887Schin 
354887Schin #define HIST_RECURSE	5
364887Schin 
374887Schin static void hist_subst(const char*, int fd, char*);
384887Schin 
394887Schin #if 0
404887Schin     /* for the benefit of the dictionary generator */
414887Schin     int	b_fc(int argc,char *argv[], void *extra){}
424887Schin #endif
434887Schin int	b_hist(int argc,char *argv[], void *extra)
444887Schin {
454887Schin 	register History_t *hp;
464887Schin 	register char *arg;
474887Schin 	register int flag,fdo;
48*8462SApril.Chin@Sun.COM 	register Shell_t *shp = ((Shbltin_t*)extra)->shp;
494887Schin 	Sfio_t *outfile;
504887Schin 	char *fname;
514887Schin 	int range[2], incr, index2, indx= -1;
524887Schin 	char *edit = 0;		/* name of editor */
534887Schin 	char *replace = 0;		/* replace old=new */
544887Schin 	int lflag = 0, nflag = 0, rflag = 0;
554887Schin #if SHOPT_HISTEXPAND
564887Schin 	int pflag = 0;
574887Schin #endif
584887Schin 	Histloc_t location;
594887Schin 	NOT_USED(argc);
60*8462SApril.Chin@Sun.COM 	if(!sh_histinit((void*)shp))
614887Schin 		errormsg(SH_DICT,ERROR_system(1),e_histopen);
624887Schin 	hp = shp->hist_ptr;
634887Schin 	while((flag = optget(argv,sh_opthist))) switch(flag)
644887Schin 	{
654887Schin 	    case 'e':
664887Schin 		edit = opt_info.arg;
674887Schin 		break;
684887Schin 	    case 'n':
694887Schin 		nflag++;
704887Schin 		break;
714887Schin 	    case 'l':
724887Schin 		lflag++;
734887Schin 		break;
744887Schin 	    case 'r':
754887Schin 		rflag++;
764887Schin 		break;
774887Schin 	    case 's':
784887Schin 		edit = "-";
794887Schin 		break;
804887Schin #if SHOPT_HISTEXPAND
814887Schin 	    case 'p':
824887Schin 		pflag++;
834887Schin 		break;
844887Schin #endif
854887Schin 	    case 'N':
864887Schin 		if(indx<=0)
874887Schin 		{
884887Schin 			if((flag = hist_max(hp) - opt_info.num-1) < 0)
894887Schin 				flag = 1;
904887Schin 			range[++indx] = flag;
914887Schin 			break;
924887Schin 		}
934887Schin 	    case ':':
944887Schin 		errormsg(SH_DICT,2, "%s", opt_info.arg);
954887Schin 		break;
964887Schin 	    case '?':
974887Schin 		errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
984887Schin 		break;
994887Schin 	}
1004887Schin 	if(error_info.errors)
1014887Schin 		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
1024887Schin 	argv += (opt_info.index-1);
1034887Schin #if SHOPT_HISTEXPAND
1044887Schin 	if(pflag)
1054887Schin 	{
1064887Schin 		hist_cancel(hp);
1074887Schin 		pflag = 0;
1084887Schin 		while(arg=argv[1])
1094887Schin 		{
1104887Schin 			flag = hist_expand(arg,&replace);
1114887Schin 			if(!(flag & HIST_ERROR))
1124887Schin 				sfputr(sfstdout, replace, '\n');
1134887Schin 			else
1144887Schin 				pflag = 1;
1154887Schin 			if(replace)
1164887Schin 				free(replace);
1174887Schin 			argv++;
1184887Schin 		}
1194887Schin 		return pflag;
1204887Schin 	}
1214887Schin #endif
1224887Schin 	flag = indx;
1234887Schin 	while(flag<1 && (arg=argv[1]))
1244887Schin 	{
1254887Schin 		/* look for old=new argument */
1264887Schin 		if(!replace && strchr(arg+1,'='))
1274887Schin 		{
1284887Schin 			replace = arg;
1294887Schin 			argv++;
1304887Schin 			continue;
1314887Schin 		}
1324887Schin 		else if(isdigit(*arg) || *arg == '-')
1334887Schin 		{
1344887Schin 			/* see if completely numeric */
1354887Schin 			do	arg++;
1364887Schin 			while(isdigit(*arg));
1374887Schin 			if(*arg==0)
1384887Schin 			{
1394887Schin 				arg = argv[1];
1404887Schin 				range[++flag] = (int)strtol(arg, (char**)0, 10);
1414887Schin 				if(*arg == '-')
1424887Schin 					range[flag] += (hist_max(hp)-1);
1434887Schin 				argv++;
1444887Schin 				continue;
1454887Schin 			}
1464887Schin 		}
1474887Schin 		/* search for last line starting with string */
1484887Schin 		location = hist_find(hp,argv[1],hist_max(hp)-1,0,-1);
1494887Schin 		if((range[++flag] = location.hist_command) < 0)
1504887Schin 			errormsg(SH_DICT,ERROR_exit(1),e_found,argv[1]);
1514887Schin 		argv++;
1524887Schin 	}
1534887Schin 	if(flag <0)
1544887Schin 	{
1554887Schin 		/* set default starting range */
1564887Schin 		if(lflag)
1574887Schin 		{
1584887Schin 			flag = hist_max(hp)-16;
1594887Schin 			if(flag<1)
1604887Schin 				flag = 1;
1614887Schin 		}
1624887Schin 		else
1634887Schin 			flag = hist_max(hp)-2;
1644887Schin 		range[0] = flag;
1654887Schin 		flag = 0;
1664887Schin 	}
1674887Schin 	index2 = hist_min(hp);
1684887Schin 	if(range[0]<index2)
1694887Schin 		range[0] = index2;
1704887Schin 	if(flag==0)
1714887Schin 		/* set default termination range */
172*8462SApril.Chin@Sun.COM 		range[1] = ((lflag && !edit)?hist_max(hp)-1:range[0]);
1734887Schin 	if(range[1]>=(flag=(hist_max(hp) - !lflag)))
1744887Schin 		range[1] = flag;
1754887Schin 	/* check for valid ranges */
1764887Schin 	if(range[1]<index2 || range[0]>=flag)
1774887Schin 		errormsg(SH_DICT,ERROR_exit(1),e_badrange,range[0],range[1]);
1784887Schin 	if(edit && *edit=='-' && range[0]!=range[1])
1794887Schin 		errormsg(SH_DICT,ERROR_exit(1),e_eneedsarg);
1804887Schin 	/* now list commands from range[rflag] to range[1-rflag] */
1814887Schin 	incr = 1;
1824887Schin 	flag = rflag>0;
1834887Schin 	if(range[1-flag] < range[flag])
1844887Schin 		incr = -1;
1854887Schin 	if(lflag)
1864887Schin 	{
1874887Schin 		outfile = sfstdout;
1884887Schin 		arg = "\n\t";
1894887Schin 	}
1904887Schin 	else
1914887Schin 	{
1924887Schin 		if(!(fname=pathtmp(NIL(char*),0,0,NIL(int*))))
1934887Schin 			errormsg(SH_DICT,ERROR_exit(1),e_create,"");
1944887Schin 		if((fdo=open(fname,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) < 0)
1954887Schin 			errormsg(SH_DICT,ERROR_system(1),e_create,fname);
1964887Schin 		outfile= sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fdo,SF_WRITE);
1974887Schin 		arg = "\n";
1984887Schin 		nflag++;
1994887Schin 	}
2004887Schin 	while(1)
2014887Schin 	{
2024887Schin 		if(nflag==0)
2034887Schin 			sfprintf(outfile,"%d\t",range[flag]);
2044887Schin 		else if(lflag)
2054887Schin 			sfputc(outfile,'\t');
2064887Schin 		hist_list(shp->hist_ptr,outfile,hist_tell(shp->hist_ptr,range[flag]),0,arg);
2074887Schin 		if(lflag)
2084887Schin 			sh_sigcheck();
2094887Schin 		if(range[flag] == range[1-flag])
2104887Schin 			break;
2114887Schin 		range[flag] += incr;
2124887Schin 	}
2134887Schin 	if(lflag)
2144887Schin 		return(0);
2154887Schin 	sfclose(outfile);
2164887Schin 	hist_eof(hp);
2174887Schin 	arg = edit;
218*8462SApril.Chin@Sun.COM 	if(!arg && !(arg=nv_getval(sh_scoped(shp,HISTEDIT))) && !(arg=nv_getval(sh_scoped(shp,FCEDNOD))))
2194887Schin 		arg = (char*)e_defedit;
2204887Schin #ifdef apollo
2214887Schin 	/*
2224887Schin 	 * Code to support the FC using the pad editor.
2234887Schin 	 * Exampled of how to use: HISTEDIT=pad
2244887Schin 	 */
2254887Schin 	if (strcmp (arg, "pad") == 0)
2264887Schin 	{
2274887Schin 		extern int pad_create(char*);
2284887Schin 		sh_close(fdo);
2294887Schin 		fdo = pad_create(fname);
2304887Schin 		pad_wait(fdo);
2314887Schin 		unlink(fname);
2324887Schin 		strcat(fname, ".bak");
2334887Schin 		unlink(fname);
2344887Schin 		lseek(fdo,(off_t)0,SEEK_SET);
2354887Schin 	}
2364887Schin 	else
2374887Schin 	{
2384887Schin #endif /* apollo */
2394887Schin 	if(*arg != '-')
2404887Schin 	{
2414887Schin 		char *com[3];
2424887Schin 		com[0] =  arg;
2434887Schin 		com[1] =  fname;
2444887Schin 		com[2] = 0;
2454887Schin 		error_info.errors = sh_eval(sh_sfeval(com),0);
2464887Schin 	}
2474887Schin 	fdo = sh_chkopen(fname);
2484887Schin 	unlink(fname);
2494887Schin 	free((void*)fname);
2504887Schin #ifdef apollo
2514887Schin 	}
2524887Schin #endif /* apollo */
2534887Schin 	/* don't history fc itself unless forked */
2544887Schin 	error_info.flags |= ERROR_SILENT;
2554887Schin 	if(!sh_isstate(SH_FORKED))
2564887Schin 		hist_cancel(hp);
2574887Schin 	sh_onstate(SH_HISTORY);
2584887Schin 	sh_onstate(SH_VERBOSE);	/* echo lines as read */
2594887Schin 	if(replace)
2604887Schin 		hist_subst(error_info.id,fdo,replace);
2614887Schin 	else if(error_info.errors == 0)
2624887Schin 	{
2634887Schin 		char buff[IOBSIZE+1];
2644887Schin 		Sfio_t *iop = sfnew(NIL(Sfio_t*),buff,IOBSIZE,fdo,SF_READ);
2654887Schin 		/* read in and run the command */
2664887Schin 		if(shp->hist_depth++ > HIST_RECURSE)
2674887Schin 			errormsg(SH_DICT,ERROR_exit(1),e_toodeep,"history");
2684887Schin 		sh_eval(iop,1);
2694887Schin 		shp->hist_depth--;
2704887Schin 	}
2714887Schin 	else
2724887Schin 	{
2734887Schin 		sh_close(fdo);
2744887Schin 		if(!sh_isoption(SH_VERBOSE))
2754887Schin 			sh_offstate(SH_VERBOSE);
2764887Schin 		sh_offstate(SH_HISTORY);
2774887Schin 	}
2784887Schin 	return(shp->exitval);
2794887Schin }
2804887Schin 
2814887Schin 
2824887Schin /*
2834887Schin  * given a file containing a command and a string of the form old=new,
2844887Schin  * execute the command with the string old replaced by new
2854887Schin  */
2864887Schin 
2874887Schin static void hist_subst(const char *command,int fd,char *replace)
2884887Schin {
2894887Schin 	register char *newp=replace;
2904887Schin 	register char *sp;
2914887Schin 	register int c;
2924887Schin 	off_t size;
2934887Schin 	char *string;
2944887Schin 	while(*++newp != '='); /* skip to '=' */
2954887Schin 	if((size = lseek(fd,(off_t)0,SEEK_END)) < 0)
2964887Schin 		return;
2974887Schin 	lseek(fd,(off_t)0,SEEK_SET);
2984887Schin 	c =  (int)size;
2994887Schin 	string = stakalloc(c+1);
3004887Schin 	if(read(fd,string,c)!=c)
3014887Schin 		return;
3024887Schin 	string[c] = 0;
3034887Schin 	*newp++ =  0;
3044887Schin 	if((sp=sh_substitute(string,replace,newp))==0)
3054887Schin 		errormsg(SH_DICT,ERROR_exit(1),e_subst,command);
3064887Schin 	*(newp-1) =  '=';
3074887Schin 	sh_eval(sfopen(NIL(Sfio_t*),sp,"s"),1);
3084887Schin }
3094887Schin 
310