1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2010 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 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