/* * look for the substring <oldsp> in <string> and replace with <newsp> * The new string is put on top of the stack */ char *sh_substitute(const char *string,const char *oldsp,char *newsp) /*@ assume string!=NULL && oldsp!=NULL && newsp!=NULL; return x satisfying x==NULL || strlen(x)==(strlen(in string)+strlen(in newsp)-strlen(in oldsp)); @*/ { register const char *sp = string; register const char *cp; const char *savesp = 0; stakseek(0); if(*sp==0) return((char*)0); if(*(cp=oldsp) == 0) goto found; #if SHOPT_MULTIBYTE mbinit(); #endif /* SHOPT_MULTIBYTE */ do { /* skip to first character which matches start of oldsp */ while(*sp && (savesp==sp || *sp != *cp)) { #if SHOPT_MULTIBYTE /* skip a whole character at a time */ int c = mbsize(sp); if(c < 0) sp++; while(c-- > 0) #endif /* SHOPT_MULTIBYTE */ stakputc(*sp++); } if(*sp == 0) return((char*)0); savesp = sp; for(;*cp;cp++) { if(*cp != *sp++) break; } if(*cp==0) /* match found */ goto found; sp = savesp; cp = oldsp; } while(*sp); return((char*)0); found: /* copy new */ stakputs(newsp); /* copy rest of string */ stakputs(sp); return(stakfreeze(1)); }
static void addmatch(register glob_t* gp, const char* dir, const char* pat, register const char* rescan, char* endslash, int meta) { register globlist_t* ap; int offset; int type; stakseek(MATCHPATH(gp)); if (dir) { stakputs(dir); stakputc(gp->gl_delim); } if (endslash) *endslash = 0; stakputs(pat); if (rescan) { if ((*gp->gl_type)(gp, stakptr(MATCHPATH(gp)), 0) != GLOB_DIR) return; stakputc(gp->gl_delim); offset = staktell(); /* if null, reserve room for . */ if (*rescan) stakputs(rescan); else stakputc(0); stakputc(0); rescan = stakptr(offset); ap = (globlist_t*)stakfreeze(0); ap->gl_begin = (char*)rescan; ap->gl_next = gp->gl_rescan; gp->gl_rescan = ap; } else { if (!endslash && (gp->gl_flags & GLOB_MARK) && (type = (*gp->gl_type)(gp, stakptr(MATCHPATH(gp)), 0))) { if ((gp->gl_flags & GLOB_COMPLETE) && type != GLOB_EXE) { stakseek(0); return; } else if (type == GLOB_DIR && (gp->gl_flags & GLOB_MARK)) stakputc(gp->gl_delim); } ap = (globlist_t*)stakfreeze(1); ap->gl_next = gp->gl_match; gp->gl_match = ap; gp->gl_pathc++; } ap->gl_flags = MATCH_RAW|meta; if (gp->gl_flags & GLOB_COMPLETE) ap->gl_flags |= MATCH_MAKE; }
int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count) { struct comnod *comptr; struct argnod *ap; register char *out; char *av[2], *begin , *dir=0; int addstar=0, rval=0, var=0, strip=1; int nomarkdirs = !sh_isoption(SH_MARKDIRS); sh_onstate(SH_FCOMPLETE); if(ep->e_nlist) { if(mode=='=' && count>0) { if(count> ep->e_nlist) return(-1); mode = '?'; av[0] = ep->e_clist[count-1]; av[1] = 0; } else { stakset(ep->e_stkptr,ep->e_stkoff); ep->e_nlist = 0; } } comptr = (struct comnod*)stakalloc(sizeof(struct comnod)); ap = (struct argnod*)stakseek(ARGVAL); #if SHOPT_MULTIBYTE { register int c = *cur; register genchar *cp; /* adjust cur */ cp = (genchar *)outbuff + *cur; c = *cp; *cp = 0; *cur = ed_external((genchar*)outbuff,(char*)stakptr(0)); *cp = c; *eol = ed_external((genchar*)outbuff,outbuff); } #endif /* SHOPT_MULTIBYTE */ out = outbuff + *cur + (sh_isoption(SH_VI)!=0); if(out[-1]=='"' || out[-1]=='\'') { rval = -(sh_isoption(SH_VI)!=0); goto done; } comptr->comtyp = COMSCAN; comptr->comarg = ap; ap->argflag = (ARG_MAC|ARG_EXP); ap->argnxt.ap = 0; ap->argchn.cp = 0; { register int c; char *last = out; c = *(unsigned char*)out; var = mode; begin = out = find_begin(outbuff,last,0,&var); /* addstar set to zero if * should not be added */ if(var=='$') { stakputs("${!"); stakwrite(out,last-out); stakputs("@}"); out = last; } else { addstar = '*'; while(out < last) { c = *(unsigned char*)out; if(isexp(c)) addstar = 0; if (c == '/') { if(addstar == 0) strip = 0; dir = out+1; } stakputc(c); out++; } } if(mode=='?') mode = '*'; if(var!='$' && mode=='\\' && out[-1]!='*') addstar = '*'; if(*begin=='~' && !strchr(begin,'/')) addstar = 0; stakputc(addstar); ap = (struct argnod*)stakfreeze(1); } if(mode!='*') sh_onoption(SH_MARKDIRS); { register char **com; char *cp=begin, *left=0, *saveout="."; int nocase=0,narg,cmd_completion=0; register int size='x'; while(cp>outbuff && ((size=cp[-1])==' ' || size=='\t')) cp--; if(!var && !strchr(ap->argval,'/') && (((cp==outbuff&&ep->sh->nextprompt==1) || (strchr(";&|(",size)) && (cp==outbuff+1||size=='('||cp[-2]!='>') && *begin!='~' ))) { cmd_completion=1; sh_onstate(SH_COMPLETE); } if(ep->e_nlist) { narg = 1; com = av; if(dir) begin += (dir-begin); } else { com = sh_argbuild(ep->sh,&narg,comptr,0); /* special handling for leading quotes */ if(begin>outbuff && (begin[-1]=='"' || begin[-1]=='\'')) begin--; } sh_offstate(SH_COMPLETE); /* allow a search to be aborted */ if(ep->sh->trapnote&SH_SIGSET) { rval = -1; goto done; } /* match? */ if (*com==0 || (narg <= 1 && (strcmp(ap->argval,*com)==0) || (addstar && com[0][strlen(*com)-1]=='*'))) { rval = -1; goto done; } if(mode=='\\' && out[-1]=='/' && narg>1) mode = '='; if(mode=='=') { if (strip && !cmd_completion) { register char **ptrcom; for(ptrcom=com;*ptrcom;ptrcom++) /* trim directory prefix */ *ptrcom = path_basename(*ptrcom); } sfputc(sfstderr,'\n'); sh_menu(sfstderr,narg,com); sfsync(sfstderr); ep->e_nlist = narg; ep->e_clist = com; goto done; } /* see if there is enough room */ size = *eol - (out-begin); if(mode=='\\') { int c; if(dir) { c = *dir; *dir = 0; saveout = begin; } if(saveout=astconf("PATH_ATTRIBUTES",saveout,(char*)0)) nocase = (strchr(saveout,'c')!=0); if(dir) *dir = c; /* just expand until name is unique */ size += strlen(*com); } else { size += narg; { char **savcom = com; while (*com) size += strlen(cp=fmtx(*com++)); com = savcom; } } /* see if room for expansion */ if(outbuff+size >= &outbuff[MAXLINE]) { com[0] = ap->argval; com[1] = 0; } /* save remainder of the buffer */ if(*out) left=stakcopy(out); if(cmd_completion && mode=='\\') out = strcopy(begin,path_basename(cp= *com++)); else if(mode=='*') { if(ep->e_nlist && dir && var) { if(*cp==var) cp++; else *begin++ = var; out = strcopy(begin,cp); var = 0; } else out = strcopy(begin,fmtx(*com)); com++; } else out = strcopy(begin,*com++); if(mode=='\\') { saveout= ++out; while (*com && *begin) { if(cmd_completion) out = overlaid(begin,path_basename(*com++),nocase); else out = overlaid(begin,*com++,nocase); } mode = (out==saveout); if(out[-1]==0) out--; if(mode && out[-1]!='/') { if(cmd_completion) { Namval_t *np; /* add as tracked alias */ Pathcomp_t *pp; if(*cp=='/' && (pp=path_dirfind(ep->sh->pathlist,cp,'/')) && (np=nv_search(begin,ep->sh->track_tree,NV_ADD))) path_alias(np,pp); out = strcopy(begin,cp); } /* add quotes if necessary */ if((cp=fmtx(begin))!=begin) out = strcopy(begin,cp); if(var=='$' && begin[-1]=='{') *out = '}'; else *out = ' '; *++out = 0; } else if((cp=fmtx(begin))!=begin) { out = strcopy(begin,cp); if(out[-1] =='"' || out[-1]=='\'') *--out = 0; } if(*begin==0) ed_ringbell(); } else { while (*com) { *out++ = ' '; out = strcopy(out,fmtx(*com++)); } } if(ep->e_nlist) { cp = com[-1]; if(cp[strlen(cp)-1]!='/') { if(var=='$' && begin[-1]=='{') *out = '}'; else *out = ' '; out++; } else if(out[-1] =='"' || out[-1]=='\'') out--; *out = 0; } *cur = (out-outbuff); /* restore rest of buffer */ if(left) out = strcopy(out,left); *eol = (out-outbuff); } done: sh_offstate(SH_FCOMPLETE); if(!ep->e_nlist) stakset(ep->e_stkptr,ep->e_stkoff); if(nomarkdirs) sh_offoption(SH_MARKDIRS); #if SHOPT_MULTIBYTE { register int c,n=0; /* first re-adjust cur */ c = outbuff[*cur]; outbuff[*cur] = 0; for(out=outbuff; *out;n++) mbchar(out); outbuff[*cur] = c; *cur = n; outbuff[*eol+1] = 0; *eol = ed_internal(outbuff,(genchar*)outbuff); } #endif /* SHOPT_MULTIBYTE */ return(rval); }
/* * open the history file * if HISTNAME is not given and userid==0 then no history file. * if login_sh and HISTFILE is longer than HIST_MAX bytes then it is * cleaned up. * hist_open() returns 1, if history file is open */ int sh_histinit(void *sh_context) { Shell_t *shp = (Shell_t*)sh_context; register int fd; register History_t *hp; register char *histname; char *fname=0; int histmask, maxlines, hist_start=0; register char *cp; register off_t hsize = 0; if(shgd->hist_ptr=hist_ptr) return(1); if(!(histname = nv_getval(HISTFILE))) { int offset = staktell(); if(cp=nv_getval(HOME)) stakputs(cp); stakputs(hist_fname); stakputc(0); stakseek(offset); histname = stakptr(offset); } #ifdef future if(hp=wasopen) { /* reuse history file if same name */ wasopen = 0; shgd->hist_ptr = hist_ptr = hp; if(strcmp(histname,hp->histname)==0) return(1); else hist_free(); } #endif retry: cp = path_relative(shp,histname); if(!histinit) histmode = S_IRUSR|S_IWUSR; if((fd=open(cp,O_BINARY|O_APPEND|O_RDWR|O_CREAT,histmode))>=0) { hsize=lseek(fd,(off_t)0,SEEK_END); } if((unsigned)fd <=2) { int n; if((n=fcntl(fd,F_DUPFD,10))>=0) { close(fd); fd=n; } } /* make sure that file has history file format */ if(hsize && hist_check(fd)) { close(fd); hsize = 0; if(unlink(cp)>=0) goto retry; fd = -1; } if(fd < 0) { #if KSHELL /* don't allow root a history_file in /tmp */ if(shgd->userid) #endif /* KSHELL */ { if(!(fname = pathtmp(NIL(char*),0,0,NIL(int*)))) return(0); fd = open(fname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR); } } if(fd<0) return(0); /* set the file to close-on-exec */ fcntl(fd,F_SETFD,FD_CLOEXEC); if(cp=nv_getval(HISTSIZE)) maxlines = (unsigned)strtol(cp, (char**)0, 10); else maxlines = HIST_DFLT; for(histmask=16;histmask <= maxlines; histmask <<=1 ); if(!(hp=new_of(History_t,(--histmask)*sizeof(off_t)))) { close(fd); return(0); } shgd->hist_ptr = hist_ptr = hp; hp->histshell = (void*)shp; hp->histsize = maxlines; hp->histmask = histmask; hp->histfp= sfnew(NIL(Sfio_t*),hp->histbuff,HIST_BSIZE,fd,SF_READ|SF_WRITE|SF_APPENDWR|SF_SHARE); memset((char*)hp->histcmds,0,sizeof(off_t)*(hp->histmask+1)); hp->histind = 1; hp->histcmds[1] = 2; hp->histcnt = 2; hp->histname = strdup(histname); hp->histdisc = hist_disc; if(hsize==0) { /* put special characters at front of file */ sfwrite(hp->histfp,(char*)hist_stamp,2); sfsync(hp->histfp); } /* initialize history list */ else { int first,last; off_t mark,size = (HIST_MAX/4)+maxlines*HIST_LINE; hp->histind = first = hist_nearend(hp,hp->histfp,hsize-size); histinit = 1; hist_eof(hp); /* this sets histind to last command */ if((hist_start = (last=(int)hp->histind)-maxlines) <=0) hist_start = 1; mark = hp->histmarker; while(first > hist_start) { size += size; first = hist_nearend(hp,hp->histfp,hsize-size); hp->histind = first; } histinit = hist_start; hist_eof(hp); if(!histinit) { sfseek(hp->histfp,hp->histcnt=hsize,SEEK_SET); hp->histind = last; hp->histmarker = mark; } histinit = 0; } if(fname) { unlink(fname); free((void*)fname); } if(hist_clean(fd) && hist_start>1 && hsize > HIST_MAX) { #ifdef DEBUG sfprintf(sfstderr,"%d: hist_trim hsize=%d\n",getpid(),hsize); sfsync(sfstderr); #endif /* DEBUG */ hp = hist_trim(hp,(int)hp->histind-maxlines); } sfdisc(hp->histfp,&hp->histdisc); #if KSHELL (HISTCUR)->nvalue.lp = (&hp->histind); #endif /* KSHELL */ sh_timeradd(1000L*(HIST_RECENT-30), 1, hist_touch, (void*)hp->histname); #if SHOPT_ACCTFILE if(sh_isstate(SH_INTERACTIVE)) acctinit(hp); #endif /* SHOPT_ACCTFILE */ #if SHOPT_AUDIT { char buff[SF_BUFSIZE]; hp->auditfp = 0; if(sh_isstate(SH_INTERACTIVE) && (hp->auditmask=sh_checkaudit(hp,SHOPT_AUDITFILE, buff, sizeof(buff)))) { if((fd=sh_open(buff,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 && fd < 10) { int n; if((n = sh_fcntl(fd,F_DUPFD, 10)) >= 0) { sh_close(fd); fd = n; } } if(fd>=0) { fcntl(fd,F_SETFD,FD_CLOEXEC); hp->tty = strdup(ttyname(2)); hp->auditfp = sfnew((Sfio_t*)0,NULL,-1,fd,SF_WRITE); } } } #endif return(1); }
int test_unop(Shell_t *shp,register int op,register const char *arg) { struct stat statb; int f; switch(op) { case 'r': return(permission(arg, R_OK)); case 'w': return(permission(arg, W_OK)); case 'x': return(permission(arg, X_OK)); case 'V': #if SHOPT_FS_3D { register int offset = staktell(); if(stat(arg,&statb)<0 || !S_ISREG(statb.st_mode)) return(0); /* add trailing / */ stakputs(arg); stakputc('/'); stakputc(0); arg = (const char*)stakptr(offset); stakseek(offset); /* FALL THRU */ } #else return(0); #endif /* SHOPT_FS_3D */ case 'd': return(test_stat(arg,&statb)>=0 && S_ISDIR(statb.st_mode)); case 'c': return(test_stat(arg,&statb)>=0 && S_ISCHR(statb.st_mode)); case 'b': return(test_stat(arg,&statb)>=0 && S_ISBLK(statb.st_mode)); case 'f': return(test_stat(arg,&statb)>=0 && S_ISREG(statb.st_mode)); case 'u': return(test_mode(arg)&S_ISUID); case 'g': return(test_mode(arg)&S_ISGID); case 'k': #ifdef S_ISVTX return(test_mode(arg)&S_ISVTX); #else return(0); #endif /* S_ISVTX */ #if SHOPT_TEST_L case 'l': #endif case 'L': case 'h': /* undocumented, and hopefully will disappear */ if(*arg==0 || arg[strlen(arg)-1]=='/' || lstat(arg,&statb)<0) return(0); return(S_ISLNK(statb.st_mode)); case 'C': #ifdef S_ISCTG return(test_stat(arg,&statb)>=0 && S_ISCTG(statb.st_mode)); #else return(0); #endif /* S_ISCTG */ case 'H': #ifdef S_ISCDF { register int offset = staktell(); if(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode)) return(1); stakputs(arg); stakputc('+'); stakputc(0); arg = (const char*)stakptr(offset); stakseek(offset); return(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode)); } #else return(0); #endif /* S_ISCDF */ case 'S': return(isasock(arg,&statb)); case 'N': return(test_stat(arg,&statb)>=0 && tmxgetmtime(&statb) > tmxgetatime(&statb)); case 'p': return(isapipe(arg,&statb)); case 'n': return(*arg != 0); case 'z': return(*arg == 0); case 's': sfsync(sfstdout); case 'O': case 'G': if(*arg==0 || test_stat(arg,&statb)<0) return(0); if(op=='s') return(statb.st_size>0); else if(op=='O') return(statb.st_uid==shp->gd->userid); return(statb.st_gid==shp->gd->groupid); case 'a': case 'e': if(memcmp(arg,"/dev/",5)==0 && sh_open(arg,O_NONBLOCK)) return(1); return(permission(arg, F_OK)); case 'o': f=1; if(*arg=='?') return(sh_lookopt(arg+1,&f)>0); op = sh_lookopt(arg,&f); return(op && (f==(sh_isoption(op)!=0))); case 't': { char *last; op = strtol(arg,&last, 10); return(*last?0:tty_check(op)); } case 'v': case 'R': { Namval_t *np; Namarr_t *ap; int isref; if(!(np = nv_open(arg,shp->var_tree,NV_VARNAME|NV_NOFAIL|NV_NOADD|NV_NOREF))) return(0); isref = nv_isref(np); if(op=='R') return(isref); if(isref) { if(np->nvalue.cp) np = nv_refnode(np); else return(0); } if(ap = nv_arrayptr(np)) return(nv_arrayisset(np,ap)); return(!nv_isnull(np) || nv_isattr(np,NV_INTEGER)); } default: { static char a[3] = "-?"; a[1]= op; errormsg(SH_DICT,ERROR_exit(2),e_badop,a); /* NOTREACHED */ return(0); } } }
int wordexp(const char *string, wordexp_t *wdarg, register int flags) { register Sfio_t *iop; register char *cp=(char*)string; register int c,quoted=0,literal=0,ac=0; int offset; char *savebase,**av; if(offset=staktell()) savebase = stakfreeze(0); if(flags&WRDE_REUSE) wordfree(wdarg); else if(!(flags&WRDE_APPEND)) { wdarg->we_wordv = 0; wdarg->we_wordc = 0; } if(flags&WRDE_UNDEF) stakwrite("set -u\n",7); if(!(flags&WRDE_SHOWERR)) stakwrite("exec 2> /dev/null\n",18); stakwrite("print -f \"%q\\n\" ",16); if(*cp=='#') stakputc('\\'); while(c = *cp++) { if(c=='\'' && !quoted) literal = !literal; else if(!literal) { if(c=='\\' && (!quoted || strchr("\\\"`\n$",c))) { stakputc('\\'); if(c= *cp) cp++; else c = '\\'; } else if(c=='"') quoted = !quoted; else if(c=='`' || (c=='$' && *cp=='(')) { if(flags&WRDE_NOCMD) { c=WRDE_CMDSUB; goto err; } /* only the shell can parse the rest */ stakputs(cp-1); break; } else if(!quoted && strchr("|&\n;<>"+ac,c)) { c=WRDE_BADCHAR; goto err; } else if(c=='(') /* allow | and & inside pattern */ ac=2; } stakputc(c); } stakputc(0); if(!(iop = sfpopen((Sfio_t*)0,stakptr(0),"r"))) { c = WRDE_NOSHELL; goto err; } stakseek(0); ac = 0; while((c=sfgetc(iop)) != EOF) { if(c=='\'') quoted = ! quoted; else if(!quoted && (c==' ' || c=='\n')) { ac++; c = 0; } stakputc(c); } if(c=sfclose(iop)) { if(c==3 || !(flags&WRDE_UNDEF)) c=WRDE_SYNTAX; else c=WRDE_BADVAL; goto err; } c = ac+2; if(flags&WRDE_DOOFFS) c += wdarg->we_offs; if(flags&WRDE_APPEND) av = (char**)realloc((void*)&wdarg->we_wordv[-1], (wdarg->we_wordc+c)*sizeof(char*)); else if(av = (char**)malloc(c*sizeof(char*))) { if(flags&WRDE_DOOFFS) memset((void*)av,0,(wdarg->we_offs+1)*sizeof(char*)); else av[0] = 0; } if(!av) return(WRDE_NOSPACE); c = staktell(); if(!(cp = (char*)malloc(sizeof(char*)+c))) { c=WRDE_NOSPACE; goto err; } ((struct list*)cp)->next = (struct list*)(*av); *av++ = (char*)cp; cp += sizeof(char*); wdarg->we_wordv = av; if(flags&WRDE_APPEND) av += wdarg->we_wordc; wdarg->we_wordc += ac; if(flags&WRDE_DOOFFS) av += wdarg->we_offs; memcpy((void*)cp,stakptr(offset),c); while(ac-- > 0) { *av++ = cp; sh_unquote(cp); while(c= *cp++); } *av = 0; c=0; err: if(offset) stakset(savebase,offset); else stakseek(0); return(c); }
static char *parse_subst(const char *s, struct subst *sb) { char *cp,del; int off,n = 0; /* build the strings on the stack, mainly for '&' substition in "new" */ off = staktell(); /* init "new" with empty string */ if(sb->str[1]) free(sb->str[1]); sb->str[1] = strdup(""); /* get delimiter */ del = *s; cp = (char*) s + 1; while(n < 2) { if(*cp == del || *cp == '\n' || *cp == '\0') { /* delimiter or EOL */ if(staktell() != off) { /* dupe string on stack and rewind stack */ stakputc('\0'); if(sb->str[n]) free(sb->str[n]); sb->str[n] = strdup(stakptr(off)); stakseek(off); } n++; /* if not delimiter, we've reached EOL. Get outta here. */ if(*cp != del) break; } else if(*cp == '\\') { if(*(cp+1) == del) /* quote delimiter */ { stakputc(del); cp++; } else if(*(cp+1) == '&' && n == 1) { /* quote '&' only in "new" */ stakputc('&'); cp++; } else stakputc('\\'); } else if(*cp == '&' && n == 1 && sb->str[0]) /* substitute '&' with "old" in "new" */ stakputs(sb->str[0]); else stakputc(*cp); cp++; } /* rewind stack */ stakseek(off); return cp; }
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); }