static char *fmtx(const char *string) { register const char *cp = string; register int n,c; unsigned char *state = (unsigned char*)sh_lexstates[2]; int offset = staktell(); if(*cp=='#' || *cp=='~') stakputc('\\'); while((c=mbchar(cp)),(c>UCHAR_MAX)||(n=state[c])==0 || n==S_EPAT); if(n==S_EOF && *string!='#') return((char*)string); stakwrite(string,--cp-string); for(string=cp;c=mbchar(cp);string=cp) { if((n=cp-string)==1) { if((n=state[c]) && n!=S_EPAT) stakputc('\\'); stakputc(c); } else stakwrite(string,n); } stakputc(0); return(stakptr(offset)); }
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); }
/* * print <str> quoting chars so that it can be read by the shell * puts null terminated result on stack, but doesn't freeze it * single!=0 limits quoting to '...' * fold>0 prints raw newlines and inserts appropriately * escaped newlines every (fold-x) chars */ char *sh_fmtqf(const char *string, int single, int fold) { register const char *cp = string; register const char *bp; register const char *vp; register int c; register int n; register int q; register int a; int offset; if (--fold < 8) fold = 0; if (!cp || !*cp || !single && !fold || fold && strlen(string) < fold) return sh_fmtq(cp); offset = staktell(); single = single ? 1 : 3; c = mbchar(string); a = isaletter(c) ? '=' : 0; vp = cp + 1; do { q = 0; n = fold; bp = cp; while ((!n || n-- > 0) && (c = mbchar(cp))) { if (a && !isaname(c)) a = 0; #if SHOPT_MULTIBYTE if (c >= 0x200) continue; if (c == '\'' || !iswprint(c)) #else if (c == '\'' || !isprint(c)) #endif /* SHOPT_MULTIBYTE */ { q = single; break; } if (c == '\n') q = 1; else if (c == a) { stakwrite(bp, cp - bp); bp = cp; vp = cp + 1; a = 0; } else if ((c == '#' || c == '~') && cp == vp || c == ']' || c != ':' && (c = sh_lexstates[ST_NORM][c]) && c != S_EPAT) q = 1; } if (q & 2) { stakputc('$'); stakputc('\''); cp = bp; n = fold - 3; q = 1; while (c = mbchar(cp)) { switch (c) { case ('a'==97?'\033':39): c = 'E'; break; case '\n': q = 0; n = fold - 1; break; case '\r': c = 'r'; break; case '\t': c = 't'; break; case '\f': c = 'f'; break; case '\b': c = 'b'; break; case '\a': c = 'a'; break; case '\\': if (*cp == 'n') { c = '\n'; q = 0; n = fold - 1; break; } case '\'': break; default: #if SHOPT_MULTIBYTE if(!iswprint(c)) #else if(!isprint(c)) #endif { if ((n -= 4) <= 0) { stakwrite("'\\\n$'", 5); n = fold - 7; } sfprintf(staksp, "\\%03o", c); continue; } q = 0; break; } if ((n -= q + 1) <= 0) { if (!q) { stakputc('\''); cp = bp; break; } stakwrite("'\\\n$'", 5); n = fold - 5; } if (q) stakputc('\\'); else q = 1; stakputc(c); bp = cp; } if (!c) stakputc('\''); } else if (q & 1) { stakputc('\''); cp = bp; n = fold ? (fold - 2) : 0; while (c = mbchar(cp)) { if (c == '\n') n = fold - 1; else if (n && --n <= 0) { n = fold - 2; stakwrite(bp, --cp - bp); bp = cp; stakwrite("'\\\n'", 4); } else if (n == 1 && *cp == '\'') { n = fold - 5; stakwrite(bp, --cp - bp); bp = cp; stakwrite("'\\\n\\''", 6); } else if (c == '\'') { stakwrite(bp, cp - bp - 1); bp = cp; if (n && (n -= 4) <= 0) { n = fold - 5; stakwrite("'\\\n\\''", 6); } else stakwrite("'\\''", 4); } } stakwrite(bp, cp - bp - 1); stakputc('\''); } else if (n = fold) { cp = bp; while (c = mbchar(cp)) { if (--n <= 0) { n = fold; stakwrite(bp, --cp - bp); bp = cp; stakwrite("\\\n", 2); } } stakwrite(bp, cp - bp - 1); } else stakwrite(bp, cp - bp); if (c) { stakputc('\\'); stakputc('\n'); } } while (c); stakputc(0); return(stakptr(offset)); }
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); }
/* * print <str> quoting chars so that it can be read by the shell * puts null terminated result on stack, but doesn't freeze it */ char *sh_fmtq(const char *string) { register const char *cp = string, *op; register int c, state; int offset; if(!cp) return((char*)0); offset = staktell(); #if SHOPT_MULTIBYTE state = ((c= mbchar(cp))==0); #else state = ((c= *(unsigned char*)cp++)==0); #endif if(isaletter(c)) { #if SHOPT_MULTIBYTE while((c=mbchar(cp)),isaname(c)); #else while((c = *(unsigned char*)cp++),isaname(c)); #endif if(c==0) return((char*)string); if(c=='=') { if(*cp==0) return((char*)string); c = cp - string; stakwrite(string,c); string = cp; #if SHOPT_MULTIBYTE c = mbchar(cp); #else c = *(unsigned char*)cp++; #endif } } if(c==0 || c=='#' || c=='~') state = 1; #if SHOPT_MULTIBYTE for(;c;c= mbchar(cp)) #else for(;c; c= *(unsigned char*)cp++) #endif { #if SHOPT_MULTIBYTE if(c=='\'' || !iswprint(c)) #else if(c=='\'' || !isprint(c)) #endif /* SHOPT_MULTIBYTE */ state = 2; else if(c==']' || (c!=':' && c<=0xff && (c=sh_lexstates[ST_NORM][c]) && c!=S_EPAT)) state |=1; } if(state<2) { if(state==1) stakputc('\''); if(c = --cp - string) stakwrite(string,c); if(state==1) stakputc('\''); } else { stakwrite("$'",2); cp = string; #if SHOPT_MULTIBYTE while(op = cp, c= mbchar(cp)) #else while(op = cp, c= *(unsigned char*)cp++) #endif { state=1; switch(c) { case ('a'==97?'\033':39): c = 'E'; break; case '\n': c = 'n'; break; case '\r': c = 'r'; break; case '\t': c = 't'; break; case '\f': c = 'f'; break; case '\b': c = 'b'; break; case '\a': c = 'a'; break; case '\\': case '\'': break; default: #if SHOPT_MULTIBYTE if(!iswprint(c)) { while(op<cp) sfprintf(staksp,"\\%.3o",*(unsigned char*)op++); continue; } #else if(!isprint(c)) { sfprintf(staksp,"\\%.3o",c); continue; } #endif state=0; break; } if(state) { stakputc('\\'); stakputc(c); } else stakwrite(op, cp-op); } stakputc('\''); } stakputc(0); return(stakptr(offset)); }