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