1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2012 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Eclipse Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.eclipse.org/org/documents/epl-v10.html * 11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) * 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[], Shbltin_t *context){} 41 #endif 42 int b_hist(int argc,char *argv[], Shbltin_t *context) 43 { 44 register History_t *hp; 45 register char *arg; 46 register int flag,fdo; 47 register Shell_t *shp = context->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->gd->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 /* FALLTHROUGH */ 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->gd->hist_ptr,outfile,hist_tell(shp->gd->hist_ptr,range[flag]),0,arg); 207 if(lflag) 208 sh_sigcheck(shp); 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 { 220 arg = (char*)e_defedit; 221 if(*arg!='/') 222 errormsg(SH_DICT,ERROR_exit(1),"ed not found set FCEDIT"); 223 } 224 #ifdef apollo 225 /* 226 * Code to support the FC using the pad editor. 227 * Exampled of how to use: HISTEDIT=pad 228 */ 229 if (strcmp (arg, "pad") == 0) 230 { 231 extern int pad_create(char*); 232 sh_close(fdo); 233 fdo = pad_create(fname); 234 pad_wait(fdo); 235 unlink(fname); 236 strcat(fname, ".bak"); 237 unlink(fname); 238 lseek(fdo,(off_t)0,SEEK_SET); 239 } 240 else 241 { 242 #endif /* apollo */ 243 if(*arg != '-') 244 { 245 char *com[3]; 246 com[0] = arg; 247 com[1] = fname; 248 com[2] = 0; 249 error_info.errors = sh_eval(sh_sfeval(com),0); 250 } 251 fdo = sh_chkopen(fname); 252 unlink(fname); 253 free((void*)fname); 254 #ifdef apollo 255 } 256 #endif /* apollo */ 257 /* don't history fc itself unless forked */ 258 error_info.flags |= ERROR_SILENT; 259 if(!sh_isstate(SH_FORKED)) 260 hist_cancel(hp); 261 sh_onstate(SH_HISTORY); 262 sh_onstate(SH_VERBOSE); /* echo lines as read */ 263 if(replace) 264 hist_subst(error_info.id,fdo,replace); 265 else if(error_info.errors == 0) 266 { 267 char buff[IOBSIZE+1]; 268 Sfio_t *iop = sfnew(NIL(Sfio_t*),buff,IOBSIZE,fdo,SF_READ); 269 /* read in and run the command */ 270 if(shp->hist_depth++ > HIST_RECURSE) 271 errormsg(SH_DICT,ERROR_exit(1),e_toodeep,"history"); 272 sh_eval(iop,1); 273 shp->hist_depth--; 274 } 275 else 276 { 277 sh_close(fdo); 278 if(!sh_isoption(SH_VERBOSE)) 279 sh_offstate(SH_VERBOSE); 280 sh_offstate(SH_HISTORY); 281 } 282 return(shp->exitval); 283 } 284 285 286 /* 287 * given a file containing a command and a string of the form old=new, 288 * execute the command with the string old replaced by new 289 */ 290 291 static void hist_subst(const char *command,int fd,char *replace) 292 { 293 register char *newp=replace; 294 register char *sp; 295 register int c; 296 off_t size; 297 char *string; 298 while(*++newp != '='); /* skip to '=' */ 299 if((size = lseek(fd,(off_t)0,SEEK_END)) < 0) 300 return; 301 lseek(fd,(off_t)0,SEEK_SET); 302 c = (int)size; 303 string = stakalloc(c+1); 304 if(read(fd,string,c)!=c) 305 return; 306 string[c] = 0; 307 *newp++ = 0; 308 if((sp=sh_substitute(string,replace,newp))==0) 309 errormsg(SH_DICT,ERROR_exit(1),e_subst,command); 310 *(newp-1) = '='; 311 sh_eval(sfopen(NIL(Sfio_t*),sp,"s"),1); 312 } 313 314