int b_hist(int argc,char *argv[], void *extra) { register History_t *hp; register char *arg; register int flag,fdo; register Shell_t *shp = ((Shbltin_t*)extra)->shp; Sfio_t *outfile; char *fname; int range[2], incr, index2, indx= -1; char *edit = 0; /* name of editor */ char *replace = 0; /* replace old=new */ int lflag = 0, nflag = 0, rflag = 0; #if SHOPT_HISTEXPAND int pflag = 0; #endif Histloc_t location; NOT_USED(argc); if(!sh_histinit((void*)shp)) errormsg(SH_DICT,ERROR_system(1),e_histopen); hp = shp->gd->hist_ptr; while((flag = optget(argv,sh_opthist))) switch(flag) { case 'e': edit = opt_info.arg; break; case 'n': nflag++; break; case 'l': lflag++; break; case 'r': rflag++; break; case 's': edit = "-"; break; #if SHOPT_HISTEXPAND case 'p': pflag++; break; #endif case 'N': if(indx<=0) { if((flag = hist_max(hp) - opt_info.num-1) < 0) flag = 1; range[++indx] = flag; break; } case ':': errormsg(SH_DICT,2, "%s", opt_info.arg); break; case '?': errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); break; } if(error_info.errors) errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); argv += (opt_info.index-1); #if SHOPT_HISTEXPAND if(pflag) { hist_cancel(hp); pflag = 0; while(arg=argv[1]) { flag = hist_expand(arg,&replace); if(!(flag & HIST_ERROR)) sfputr(sfstdout, replace, '\n'); else pflag = 1; if(replace) free(replace); argv++; } return pflag; } #endif flag = indx; while(flag<1 && (arg=argv[1])) { /* look for old=new argument */ if(!replace && strchr(arg+1,'=')) { replace = arg; argv++; continue; } else if(isdigit(*arg) || *arg == '-') { /* see if completely numeric */ do arg++; while(isdigit(*arg)); if(*arg==0) { arg = argv[1]; range[++flag] = (int)strtol(arg, (char**)0, 10); if(*arg == '-') range[flag] += (hist_max(hp)-1); argv++; continue; } } /* search for last line starting with string */ location = hist_find(hp,argv[1],hist_max(hp)-1,0,-1); if((range[++flag] = location.hist_command) < 0) errormsg(SH_DICT,ERROR_exit(1),e_found,argv[1]); argv++; } if(flag <0) { /* set default starting range */ if(lflag) { flag = hist_max(hp)-16; if(flag<1) flag = 1; } else flag = hist_max(hp)-2; range[0] = flag; flag = 0; } index2 = hist_min(hp); if(range[0]<index2) range[0] = index2; if(flag==0) /* set default termination range */ range[1] = ((lflag && !edit)?hist_max(hp)-1:range[0]); if(range[1]>=(flag=(hist_max(hp) - !lflag))) range[1] = flag; /* check for valid ranges */ if(range[1]<index2 || range[0]>=flag) errormsg(SH_DICT,ERROR_exit(1),e_badrange,range[0],range[1]); if(edit && *edit=='-' && range[0]!=range[1]) errormsg(SH_DICT,ERROR_exit(1),e_eneedsarg); /* now list commands from range[rflag] to range[1-rflag] */ incr = 1; flag = rflag>0; if(range[1-flag] < range[flag]) incr = -1; if(lflag) { outfile = sfstdout; arg = "\n\t"; } else { if(!(fname=pathtmp(NIL(char*),0,0,NIL(int*)))) errormsg(SH_DICT,ERROR_exit(1),e_create,""); if((fdo=open(fname,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) < 0) errormsg(SH_DICT,ERROR_system(1),e_create,fname); outfile= sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fdo,SF_WRITE); arg = "\n"; nflag++; } while(1) { if(nflag==0) sfprintf(outfile,"%d\t",range[flag]); else if(lflag) sfputc(outfile,'\t'); hist_list(shp->gd->hist_ptr,outfile,hist_tell(shp->gd->hist_ptr,range[flag]),0,arg); if(lflag) sh_sigcheck(shp); if(range[flag] == range[1-flag]) break; range[flag] += incr; } if(lflag) return(0); sfclose(outfile); hist_eof(hp); arg = edit; if(!arg && !(arg=nv_getval(sh_scoped(shp,HISTEDIT))) && !(arg=nv_getval(sh_scoped(shp,FCEDNOD)))) arg = (char*)e_defedit; #ifdef apollo /* * Code to support the FC using the pad editor. * Exampled of how to use: HISTEDIT=pad */ if (strcmp (arg, "pad") == 0) { extern int pad_create(char*); sh_close(fdo); fdo = pad_create(fname); pad_wait(fdo); unlink(fname); strcat(fname, ".bak"); unlink(fname); lseek(fdo,(off_t)0,SEEK_SET); } else { #endif /* apollo */ if(*arg != '-') { char *com[3]; com[0] = arg; com[1] = fname; com[2] = 0; error_info.errors = sh_eval(sh_sfeval(com),0); } fdo = sh_chkopen(fname); unlink(fname); free((void*)fname); #ifdef apollo } #endif /* apollo */ /* don't history fc itself unless forked */ error_info.flags |= ERROR_SILENT; if(!sh_isstate(SH_FORKED)) hist_cancel(hp); sh_onstate(SH_HISTORY); sh_onstate(SH_VERBOSE); /* echo lines as read */ if(replace) hist_subst(error_info.id,fdo,replace); else if(error_info.errors == 0) { char buff[IOBSIZE+1]; Sfio_t *iop = sfnew(NIL(Sfio_t*),buff,IOBSIZE,fdo,SF_READ); /* read in and run the command */ if(shp->hist_depth++ > HIST_RECURSE) errormsg(SH_DICT,ERROR_exit(1),e_toodeep,"history"); sh_eval(iop,1); shp->hist_depth--; }
int hist_expand(const char *ln, char **xp) { int off, /* stack offset */ q, /* quotation flags */ p, /* flag */ c, /* current char */ flag=0; /* HIST_* flags */ Sfoff_t n, /* history line number, counter, etc. */ i, /* counter */ w[2]; /* word range */ char *sp, /* stack pointer */ *cp, /* current char in ln */ *str, /* search string */ *evp, /* event/word designator string, for error msgs */ *cc=0, /* copy of current line up to cp; temp ptr */ hc[3], /* default histchars */ *qc="\'\"`"; /* quote characters */ Sfio_t *ref=0, /* line referenced by event designator */ *tmp=0, /* temporary line buffer */ *tmp2=0;/* temporary line buffer */ Histloc_t hl; /* history location */ static Namval_t *np = 0; /* histchars variable */ static struct subst sb = {0,0}; /* substition strings */ static Sfio_t *wm=0; /* word match from !?string? event designator */ if(!wm) wm = sfopen(NULL, NULL, "swr"); hc[0] = '!'; hc[1] = '^'; hc[2] = 0; if((np = nv_open("histchars",sh.var_tree,0)) && (cp = nv_getval(np))) { if(cp[0]) { hc[0] = cp[0]; if(cp[1]) { hc[1] = cp[1]; if(cp[2]) hc[2] = cp[2]; } } } /* save shell stack */ if(off = staktell()) sp = stakfreeze(0); cp = (char*)ln; while(cp && *cp) { /* read until event/quick substitution/comment designator */ if((*cp != hc[0] && *cp != hc[1] && *cp != hc[2]) || (*cp == hc[1] && cp != ln)) { if(*cp == '\\') /* skip escaped designators */ stakputc(*cp++); else if(*cp == '\'') /* skip quoted designators */ { do stakputc(*cp); while(*++cp && *cp != '\''); } stakputc(*cp++); continue; } if(hc[2] && *cp == hc[2]) /* history comment designator, skip rest of line */ { stakputc(*cp++); stakputs(cp); DONE(); } n = -1; str = 0; flag &= HIST_EVENT; /* save event flag for returning later */ evp = cp; ref = 0; if(*cp == hc[1]) /* shortcut substitution */ { flag |= HIST_QUICKSUBST; goto getline; } if(*cp == hc[0] && *(cp+1) == hc[0]) /* refer to line -1 */ { cp += 2; goto getline; } switch(c = *++cp) { case ' ': case '\t': case '\n': case '\0': case '=': case '(': stakputc(hc[0]); continue; case '#': /* the line up to current position */ flag |= HIST_HASH; cp++; n = staktell(); /* terminate string and dup */ stakputc('\0'); cc = strdup(stakptr(0)); stakseek(n); /* remove null byte again */ ref = sfopen(ref, cc, "s"); /* open as file */ n = 0; /* skip history file referencing */ break; case '-': /* back reference by number */ if(!isdigit(*(cp+1))) goto string_event; cp++; case '0': /* reference by number */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 0; while(isdigit(*cp)) n = n * 10 + (*cp++) - '0'; if(c == '-') n = -n; break; case '$': n = -1; case ':': break; case '?': cp++; flag |= HIST_QUESTION; string_event: default: /* read until end of string or word designator/modifier */ str = cp; while(*cp) { cp++; if((!(flag&HIST_QUESTION) && (*cp == ':' || isspace(*cp) || *cp == '^' || *cp == '$' || *cp == '*' || *cp == '-' || *cp == '%') ) || ((flag&HIST_QUESTION) && (*cp == '?' || *cp == '\n'))) { c = *cp; *cp = '\0'; } } break; } getline: flag |= HIST_EVENT; if(str) /* !string or !?string? event designator */ { /* search history for string */ hl = hist_find(shgd->hist_ptr, str, shgd->hist_ptr->histind, flag&HIST_QUESTION, -1); if((n = hl.hist_command) == -1) n = 0; /* not found */ } if(n) { if(n < 0) /* determine index for backref */ n = shgd->hist_ptr->histind + n; /* search and use history file if found */ if(n > 0 && hist_seek(shgd->hist_ptr, n) != -1) ref = shgd->hist_ptr->histfp; } if(!ref) { /* string not found or command # out of range */ c = *cp; *cp = '\0'; errormsg(SH_DICT, ERROR_ERROR, "%s: event not found", evp); *cp = c; DONE(); } if(str) /* string search: restore orig. line */ { if(flag&HIST_QUESTION) *cp++ = c; /* skip second question mark */ else *cp = c; } /* colon introduces either word designators or modifiers */ if(*(evp = cp) == ':') cp++; w[0] = 0; /* -1 means last word, -2 means match from !?string? */ w[1] = -1; /* -1 means last word, -2 means suppress last word */ if(flag & HIST_QUICKSUBST) /* shortcut substitution */ goto getsel; n = 0; while(n < 2) { switch(c = *cp++) { case '^': /* first word */ if(n == 0) { w[0] = w[1] = 1; goto skip; } else goto skip2; case '$': /* last word */ w[n] = -1; goto skip; case '%': /* match from !?string? event designator */ if(n == 0) { if(!str) { w[0] = 0; w[1] = -1; ref = wm; } else { w[0] = -2; w[1] = sftell(ref) + hl.hist_char; } sfseek(wm, 0, SEEK_SET); goto skip; } default: skip2: cp--; n = 2; break; case '*': /* until last word */ if(n == 0) w[0] = 1; w[1] = -1; skip: flag |= HIST_WORDDSGN; n = 2; break; case '-': /* until last word or specified index */ w[1] = -2; flag |= HIST_WORDDSGN; n = 1; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* specify index */ if((*evp == ':') || w[1] == -2) { w[n] = c - '0'; while(isdigit(c=*cp++)) w[n] = w[n] * 10 + c - '0'; flag |= HIST_WORDDSGN; if(n == 0) w[1] = w[0]; n++; } else n = 2; cp--; break; } } if(w[0] != -2 && w[1] > 0 && w[0] > w[1]) { c = *cp; *cp = '\0'; errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp); *cp = c; DONE(); } /* no valid word designator after colon, rewind */ if(!(flag & HIST_WORDDSGN) && (*evp == ':')) cp = evp; getsel: /* open temp buffer, let sfio do the (re)allocation */ tmp = sfopen(NULL, NULL, "swr"); /* push selected words into buffer, squash whitespace into single blank or a newline */ n = i = q = 0; while((c = sfgetc(ref)) > 0) { if(isspace(c)) { flag |= (c == '\n' ? HIST_NEWLINE : 0); continue; } if(n >= w[0] && ((w[0] != -2) ? (w[1] < 0 || n <= w[1]) : 1)) { if(w[0] < 0) sfseek(tmp, 0, SEEK_SET); else i = sftell(tmp); if(i > 0) sfputc(tmp, flag & HIST_NEWLINE ? '\n' : ' '); flag &= ~HIST_NEWLINE; p = 1; } else p = 0; do { cc = strchr(qc, c); q ^= cc ? 1<<(int)(cc - qc) : 0; if(p) sfputc(tmp, c); } while((c = sfgetc(ref)) > 0 && (!isspace(c) || q)); if(w[0] == -2 && sftell(ref) > w[1]) break; flag |= (c == '\n' ? HIST_NEWLINE : 0); n++; } if(w[0] != -2 && w[1] >= 0 && w[1] >= n) { c = *cp; *cp = '\0'; errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp); *cp = c; DONE(); } else if(w[1] == -2) /* skip last word */ sfseek(tmp, i, SEEK_SET); /* remove trailing newline */ if(sftell(tmp)) { sfseek(tmp, -1, SEEK_CUR); if(sfgetc(tmp) == '\n') sfungetc(tmp, '\n'); } sfputc(tmp, '\0'); if(str) { if(wm) sfclose(wm); wm = tmp; } if(cc && (flag&HIST_HASH)) { /* close !# temp file */ sfclose(ref); flag &= ~HIST_HASH; free(cc); cc = 0; } evp = cp; /* selected line/words are now in buffer, now go for the modifiers */ while(*cp == ':' || (flag & HIST_QUICKSUBST)) { if(flag & HIST_QUICKSUBST) { flag &= ~HIST_QUICKSUBST; c = 's'; cp--; } else c = *++cp; sfseek(tmp, 0, SEEK_SET); tmp2 = sfopen(tmp2, NULL, "swr"); if(c == 'g') /* global substitution */ { flag |= HIST_GLOBALSUBST; c = *++cp; } if(cc = strchr(modifiers, c)) flag |= mod_flags[cc - modifiers]; else { errormsg(SH_DICT, ERROR_ERROR, "%c: unrecognized history modifier", c); DONE(); } if(c == 'h' || c == 'r') /* head or base */ { n = -1; while((c = sfgetc(tmp)) > 0) { /* remember position of / or . */ if((c == '/' && *cp == 'h') || (c == '.' && *cp == 'r')) n = sftell(tmp2); sfputc(tmp2, c); } if(n > 0) { /* rewind to last / or . */ sfseek(tmp2, n, SEEK_SET); /* end string there */ sfputc(tmp2, '\0'); } } else if(c == 't' || c == 'e') /* tail or suffix */ { n = 0; while((c = sfgetc(tmp)) > 0) { /* remember position of / or . */ if((c == '/' && *cp == 't') || (c == '.' && *cp == 'e')) n = sftell(tmp); } /* rewind to last / or . */ sfseek(tmp, n, SEEK_SET); /* copy from there on */ while((c = sfgetc(tmp)) > 0) sfputc(tmp2, c); } else if(c == 's' || c == '&') { cp++; if(c == 's') { /* preset old with match from !?string? */ if(!sb.str[0] && wm) sb.str[0] = strdup(sfsetbuf(wm, (Void_t*)1, 0)); cp = parse_subst(cp, &sb); } if(!sb.str[0] || !sb.str[1]) { c = *cp; *cp = '\0'; errormsg(SH_DICT, ERROR_ERROR, "%s%s: no previous substitution", (flag & HIST_QUICKSUBST) ? ":s" : "", evp); *cp = c; DONE(); } /* need pointer for strstr() */ str = sfsetbuf(tmp, (Void_t*)1, 0); flag |= HIST_SUBSTITUTE; while(flag & HIST_SUBSTITUTE) { /* find string */ if(cc = strstr(str, sb.str[0])) { /* replace it */ c = *cc; *cc = '\0'; sfputr(tmp2, str, -1); sfputr(tmp2, sb.str[1], -1); *cc = c; str = cc + strlen(sb.str[0]); } else if(!sftell(tmp2)) { /* not successfull */ c = *cp; *cp = '\0'; errormsg(SH_DICT, ERROR_ERROR, "%s%s: substitution failed", (flag & HIST_QUICKSUBST) ? ":s" : "", evp); *cp = c; DONE(); } /* loop if g modifier specified */ if(!cc || !(flag & HIST_GLOBALSUBST)) flag &= ~HIST_SUBSTITUTE; } /* output rest of line */ sfputr(tmp2, str, -1); if(*cp) cp--; } if(sftell(tmp2)) { /* if any substitions done, swap buffers */ if(wm != tmp) sfclose(tmp); tmp = tmp2; tmp2 = 0; } cc = 0; if(*cp) cp++; } /* flush temporary buffer to stack */ if(tmp) { sfseek(tmp, 0, SEEK_SET); if(flag & HIST_QUOTE) stakputc('\''); while((c = sfgetc(tmp)) > 0) { if(isspace(c)) { flag = flag & ~HIST_NEWLINE; /* squash white space to either a blank or a newline */ do flag |= (c == '\n' ? HIST_NEWLINE : 0); while((c = sfgetc(tmp)) > 0 && isspace(c)); sfungetc(tmp, c); c = (flag & HIST_NEWLINE) ? '\n' : ' '; if(flag & HIST_QUOTE_BR) { stakputc('\''); stakputc(c); stakputc('\''); } else stakputc(c); } else if((c == '\'') && (flag & HIST_QUOTE)) { stakputc('\''); stakputc('\\'); stakputc(c); stakputc('\''); } else stakputc(c); } if(flag & HIST_QUOTE) stakputc('\''); } } stakputc('\0'); done: if(cc && (flag&HIST_HASH)) { /* close !# temp file */ sfclose(ref); free(cc); cc = 0; } /* error? */ if(staktell() && !(flag & HIST_ERROR)) *xp = strdup(stakfreeze(1)); /* restore shell stack */ if(off) stakset(sp,off); else stakseek(0); /* drop temporary files */ if(tmp && tmp != wm) sfclose(tmp); if(tmp2) sfclose(tmp2); return (flag & HIST_ERROR ? HIST_ERROR : flag & HIST_FLAG_RETURN_MASK); }