Home | History | Annotate | Download | only in bltins
      1 /***********************************************************************
      2 *                                                                      *
      3 *               This software is part of the ast package               *
      4 *          Copyright (c) 1982-2009 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 (at) 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[], void *extra){}
     41 #endif
     42 int	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->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->hist_ptr,outfile,hist_tell(shp->hist_ptr,range[flag]),0,arg);
    206 		if(lflag)
    207 			sh_sigcheck();
    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 
    286 static 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