summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/sh/macro.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/sh/macro.c
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/ksh93/sh/macro.c')
-rw-r--r--src/cmd/ksh93/sh/macro.c2814
1 files changed, 2814 insertions, 0 deletions
diff --git a/src/cmd/ksh93/sh/macro.c b/src/cmd/ksh93/sh/macro.c
new file mode 100644
index 0000000..9adf5f5
--- /dev/null
+++ b/src/cmd/ksh93/sh/macro.c
@@ -0,0 +1,2814 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * Shell macro expander
+ * expands ~
+ * expands ${...}
+ * expands $(...)
+ * expands $((...))
+ * expands `...`
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ */
+
+#include "defs.h"
+#include <fcin.h>
+#include <pwd.h>
+#include <ctype.h>
+#include "name.h"
+#include "variables.h"
+#include "shlex.h"
+#include "io.h"
+#include "jobs.h"
+#include "shnodes.h"
+#include "path.h"
+#include "national.h"
+#include "streval.h"
+
+#undef STR_GROUP
+#ifndef STR_GROUP
+# define STR_GROUP 0
+#endif
+
+#if SHOPT_MULTIBYTE
+# undef isascii
+# define isacii(c) ((c)<=UCHAR_MAX)
+#else
+# define mbchar(p) (*(unsigned char*)p++)
+#endif /* SHOPT_MULTIBYTE */
+
+#if _WINIX
+ static int Skip;
+#endif /*_WINIX */
+
+static int _c_;
+typedef struct _mac_
+{
+ Shell_t *shp; /* pointer to shell interpreter */
+ Sfio_t *sp; /* stream pointer for here-document */
+ struct argnod **arghead; /* address of head of argument list */
+ char *ifsp; /* pointer to IFS value */
+ int fields; /* number of fields */
+ short quoted; /* set when word has quotes */
+ unsigned char ifs; /* first char of IFS */
+ char atmode; /* when processing $@ */
+ char quote; /* set within double quoted contexts */
+ char lit; /* set within single quotes */
+ char split; /* set when word splittin is possible */
+ char pattern; /* set when file expansion follows */
+ char patfound; /* set if pattern character found */
+ char assign; /* set for assignments */
+ char arith; /* set for ((...)) */
+ char let; /* set when expanding let arguments */
+ char zeros; /* strip leading zeros when set */
+ char arrayok; /* $x[] ok for arrays */
+ char subcopy; /* set when copying subscript */
+ int dotdot; /* set for .. in subscript */
+ void *nvwalk; /* for name space walking*/
+} Mac_t;
+
+#undef ESCAPE
+#define ESCAPE '\\'
+#define isescchar(s) ((s)>S_QUOTE)
+#define isqescchar(s) ((s)>=S_QUOTE)
+#define isbracechar(c) ((c)==RBRACE || (_c_=sh_lexstates[ST_BRACE][c])==S_MOD1 ||_c_==S_MOD2)
+#define ltos(x) fmtbase((long)(x),0,0)
+
+/* type of macro expansions */
+#define M_BRACE 1 /* ${var} */
+#define M_TREE 2 /* ${var.} */
+#define M_SIZE 3 /* ${#var} */
+#define M_VNAME 4 /* ${!var} */
+#define M_SUBNAME 5 /* ${!var[sub]} */
+#define M_NAMESCAN 6 /* ${!var*} */
+#define M_NAMECOUNT 7 /* ${#var*} */
+#define M_TYPE 8 /* ${@var} */
+
+static int substring(const char*, const char*, int[], int);
+static void copyto(Mac_t*, int, int);
+static void comsubst(Mac_t*, Shnode_t*, int);
+static int varsub(Mac_t*);
+static void mac_copy(Mac_t*,const char*, int);
+static void tilde_expand2(Shell_t*,int);
+static char *sh_tilde(Shell_t*,const char*);
+static char *special(Shell_t *,int);
+static void endfield(Mac_t*,int);
+static void mac_error(Namval_t*);
+static char *mac_getstring(char*);
+static int charlen(const char*,int);
+#if SHOPT_MULTIBYTE
+ static char *lastchar(const char*,const char*);
+#endif /* SHOPT_MULTIBYTE */
+
+void *sh_macopen(Shell_t *shp)
+{
+ void *addr = newof(0,Mac_t,1,0);
+ Mac_t *mp = (Mac_t*)addr;
+ mp->shp = shp;
+ return(addr);
+}
+
+/*
+ * perform only parameter substitution and catch failures
+ */
+char *sh_mactry(Shell_t *shp,register char *string)
+{
+ if(string)
+ {
+ int jmp_val;
+ int savexit = shp->savexit;
+ struct checkpt buff;
+ sh_pushcontext(shp,&buff,SH_JMPSUB);
+ jmp_val = sigsetjmp(buff.buff,0);
+ if(jmp_val == 0)
+ string = sh_mactrim(shp,string,0);
+ sh_popcontext(shp,&buff);
+ shp->savexit = savexit;
+ return(string);
+ }
+ return("");
+}
+
+/*
+ * Perform parameter expansion, command substitution, and arithmetic
+ * expansion on <str>.
+ * If <mode> greater than 1 file expansion is performed if the result
+ * yields a single pathname.
+ * If <mode> negative, than expansion rules for assignment are applied.
+ */
+char *sh_mactrim(Shell_t *shp, char *str, register int mode)
+{
+ register Mac_t *mp = (Mac_t*)shp->mac_context;
+ Stk_t *stkp = shp->stk;
+ Mac_t savemac;
+ savemac = *mp;
+ stkseek(stkp,0);
+ mp->arith = (mode==3);
+ mp->let = 0;
+ shp->argaddr = 0;
+ mp->pattern = (mode==1||mode==2);
+ mp->patfound = 0;
+ mp->assign = 0;
+ if(mode<0)
+ mp->assign = -mode;
+ mp->quoted = mp->lit = mp->split = mp->quote = 0;
+ mp->sp = 0;
+ if(mp->ifsp=nv_getval(sh_scoped(shp,IFSNOD)))
+ mp->ifs = *mp->ifsp;
+ else
+ mp->ifs = ' ';
+ stkseek(stkp,0);
+ fcsopen(str);
+ copyto(mp,0,mp->arith);
+ str = stkfreeze(stkp,1);
+ if(mode==2)
+ {
+ /* expand only if unique */
+ struct argnod *arglist=0;
+ if((mode=path_expand(shp,str,&arglist))==1)
+ str = arglist->argval;
+ else if(mode>1)
+ errormsg(SH_DICT,ERROR_exit(1),e_ambiguous,str);
+ sh_trim(str);
+ }
+ *mp = savemac;
+ return(str);
+}
+
+/*
+ * Perform all the expansions on the argument <argp>
+ */
+int sh_macexpand(Shell_t* shp, register struct argnod *argp, struct argnod **arghead,int flag)
+{
+ register int flags = argp->argflag;
+ register char *str = argp->argval;
+ register Mac_t *mp = (Mac_t*)shp->mac_context;
+ char **saveargaddr = shp->argaddr;
+ Mac_t savemac;
+ Stk_t *stkp = shp->stk;
+ savemac = *mp;
+ mp->sp = 0;
+ if(mp->ifsp=nv_getval(sh_scoped(shp,IFSNOD)))
+ mp->ifs = *mp->ifsp;
+ else
+ mp->ifs = ' ';
+ if((flag&ARG_OPTIMIZE) && !shp->indebug && !(flags&ARG_MESSAGE))
+ shp->argaddr = (char**)&argp->argchn.ap;
+ else
+ shp->argaddr = 0;
+ mp->arghead = arghead;
+ mp->quoted = mp->lit = mp->quote = 0;
+ mp->arith = ((flag&ARG_ARITH)!=0);
+ mp->let = ((flag&ARG_LET)!=0);
+ mp->split = !(flag&ARG_ASSIGN);
+ mp->assign = !mp->split;
+ mp->pattern = mp->split && !(flag&ARG_NOGLOB) && !sh_isoption(SH_NOGLOB);
+ mp->arrayok = mp->arith || (flag&ARG_ARRAYOK);
+ str = argp->argval;
+ fcsopen(str);
+ mp->fields = 0;
+ mp->atmode = 0;
+ if(!arghead)
+ {
+ mp->split = 0;
+ mp->pattern = ((flag&ARG_EXP)!=0);
+ stkseek(stkp,0);
+ }
+ else
+ {
+ stkseek(stkp,ARGVAL);
+ *stkptr(stkp,ARGVAL-1) = 0;
+ }
+ mp->patfound = 0;
+ if(mp->pattern)
+ mp->arrayok = 0;
+ copyto(mp,0,mp->arith);
+ if(!arghead)
+ {
+ argp->argchn.cp = stkfreeze(stkp,1);
+ if(shp->argaddr)
+ argp->argflag |= ARG_MAKE;
+ }
+ else
+ {
+ endfield(mp,mp->quoted|mp->atmode);
+ flags = mp->fields;
+ if(flags==1 && shp->argaddr)
+ argp->argchn.ap = *arghead;
+ }
+ shp->argaddr = saveargaddr;
+ *mp = savemac;
+ return(flags);
+}
+
+/*
+ * Expand here document which is stored in <infile> or <string>
+ * The result is written to <outfile>
+ */
+void sh_machere(Shell_t *shp,Sfio_t *infile, Sfio_t *outfile, char *string)
+{
+ register int c,n;
+ register const char *state = sh_lexstates[ST_QUOTE];
+ register char *cp;
+ register Mac_t *mp = (Mac_t*)shp->mac_context;
+ Lex_t *lp = (Lex_t*)mp->shp->lex_context;
+ Fcin_t save;
+ Mac_t savemac;
+ Stk_t *stkp = shp->stk;
+ savemac = *mp;
+ stkseek(stkp,0);
+ shp->argaddr = 0;
+ mp->sp = outfile;
+ mp->split = mp->assign = mp->pattern = mp->patfound = mp->lit = mp->arith = mp->let = 0;
+ mp->quote = 1;
+ mp->ifsp = nv_getval(sh_scoped(shp,IFSNOD));
+ mp->ifs = ' ';
+ fcsave(&save);
+ if(infile)
+ fcfopen(infile);
+ else
+ fcsopen(string);
+ fcnotify(0,lp);
+ cp = fcseek(0);
+ while(1)
+ {
+#if SHOPT_MULTIBYTE
+ if(mbwide())
+ {
+ do
+ {
+ ssize_t len;
+ switch(len = mbsize(cp))
+ {
+ case -1: /* illegal multi-byte char */
+ case 0:
+ case 1:
+ n=state[*(unsigned char*)cp++];
+ break;
+ default:
+ /* use state of alpha character */
+ n=state['a'];
+ cp += len;
+ }
+ }
+ while(n == 0);
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ while((n=state[*(unsigned char*)cp++])==0);
+ if(n==S_NL || n==S_QUOTE || n==S_RBRA)
+ continue;
+ if(c=(cp-1)-fcseek(0))
+ sfwrite(outfile,fcseek(0),c);
+ cp = fcseek(c+1);
+ switch(n)
+ {
+ case S_EOF:
+ if((n=fcfill()) <=0)
+ {
+ /* ignore 0 byte when reading from file */
+ if(n==0 && fcfile())
+ continue;
+ fcrestore(&save);
+ *mp = savemac;
+ return;
+ }
+ cp = fcseek(-1);
+ continue;
+ case S_ESC:
+ fcgetc(c);
+ cp=fcseek(-1);
+ if(c>0)
+ cp++;
+ if(!isescchar(state[c]))
+ sfputc(outfile,ESCAPE);
+ continue;
+ case S_GRAVE:
+ comsubst(mp,(Shnode_t*)0,0);
+ break;
+ case S_DOL:
+ c = fcget();
+ if(c=='.')
+ goto regular;
+ again:
+ switch(n=sh_lexstates[ST_DOL][c])
+ {
+ case S_ALP: case S_SPC1: case S_SPC2:
+ case S_DIG: case S_LBRA:
+ {
+ Fcin_t save2;
+ int offset = stktell(stkp);
+ int offset2;
+ fcnotify(0,lp);
+ sfputc(stkp,c);
+ if(n==S_LBRA)
+ {
+ c = fcget();
+ fcseek(-1);
+ if(sh_lexstates[ST_NORM][c]==S_BREAK)
+ {
+ comsubst(mp,(Shnode_t*)0,2);
+ break;
+ }
+ sh_lexskip(lp,RBRACE,1,ST_BRACE);
+ }
+ else if(n==S_ALP)
+ {
+ while(fcgetc(c),isaname(c))
+ sfputc(stkp,c);
+ fcseek(-1);
+ }
+ sfputc(stkp,0);
+ offset2 = stktell(stkp);
+ fcsave(&save2);
+ fcsopen(stkptr(stkp,offset));
+ varsub(mp);
+ if(c=stktell(stkp)-offset2)
+ sfwrite(outfile,(char*)stkptr(stkp,offset2),c);
+ fcrestore(&save2);
+ stkseek(stkp,offset);
+ break;
+ }
+ case S_PAR:
+ comsubst(mp,(Shnode_t*)0,1);
+ break;
+ case S_EOF:
+ if((c=fcfill()) > 0)
+ goto again;
+ /* FALL THRU */
+ default:
+ regular:
+ sfputc(outfile,'$');
+ fcseek(-1);
+ break;
+ }
+ }
+ cp = fcseek(0);
+ }
+}
+
+/*
+ * expand argument but do not trim pattern characters
+ */
+char *sh_macpat(Shell_t *shp,register struct argnod *arg, int flags)
+{
+ register char *sp = arg->argval;
+ if((arg->argflag&ARG_RAW))
+ return(sp);
+ sh_stats(STAT_ARGEXPAND);
+ if(flags&ARG_OPTIMIZE)
+ arg->argchn.ap=0;
+ if(!(sp=arg->argchn.cp))
+ {
+ sh_macexpand(shp,arg,NIL(struct argnod**),flags|ARG_ARRAYOK);
+ sp = arg->argchn.cp;
+ if(!(flags&ARG_OPTIMIZE) || !(arg->argflag&ARG_MAKE))
+ arg->argchn.cp = 0;
+ arg->argflag &= ~ARG_MAKE;
+ }
+ else
+ sh_stats(STAT_ARGHITS);
+ return(sp);
+}
+
+/*
+ * Process the characters up to <endch> or end of input string
+ */
+static void copyto(register Mac_t *mp,int endch, int newquote)
+{
+ register int c,n;
+ register const char *state = sh_lexstates[ST_MACRO];
+ register char *cp,*first;
+ Lex_t *lp = (Lex_t*)mp->shp->lex_context;
+ int tilde = -1;
+ int oldquote = mp->quote;
+ int ansi_c = 0;
+ int paren = 0;
+ int ere = 0;
+ int brace = 0;
+ Sfio_t *sp = mp->sp;
+ Stk_t *stkp = mp->shp->stk;
+ char *resume = 0;
+ mp->sp = NIL(Sfio_t*);
+ mp->quote = newquote;
+ first = cp = fcseek(0);
+ if(!mp->quote && *cp=='~' && cp[1]!=LPAREN)
+ tilde = stktell(stkp);
+ /* handle // operator specially */
+ if(mp->pattern==2 && *cp=='/')
+ cp++;
+ while(1)
+ {
+#if SHOPT_MULTIBYTE
+ if(mbwide())
+ {
+ ssize_t len;
+ do
+ {
+ switch(len = mbsize(cp))
+ {
+ case -1: /* illegal multi-byte char */
+ case 0:
+ len = 1;
+ case 1:
+ n = state[*(unsigned char*)cp++];
+ break;
+ default:
+ /* treat as if alpha */
+ cp += len;
+ n=state['a'];
+ }
+ }
+ while(n == 0);
+ c = (cp-len) - first;
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ {
+ while((n=state[*(unsigned char*)cp++])==0);
+ c = (cp-1) - first;
+ }
+ switch(n)
+ {
+ case S_ESC:
+ if(ansi_c)
+ {
+ /* process ANSI-C escape character */
+ char *addr= --cp;
+ if(c)
+ sfwrite(stkp,first,c);
+ c = chresc(cp,&addr);
+ cp = addr;
+ first = fcseek(cp-first);
+#if SHOPT_MULTIBYTE
+ if(c > UCHAR_MAX && mbwide())
+ {
+ int i;
+ unsigned char mb[8];
+
+ n = mbconv((char*)mb, c);
+ for(i=0;i<n;i++)
+ sfputc(stkp,mb[i]);
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ sfputc(stkp,c);
+ if(c==ESCAPE && mp->pattern)
+ sfputc(stkp,ESCAPE);
+ break;
+ }
+ else if(sh_isoption(SH_BRACEEXPAND) && mp->pattern==4 && (*cp==',' || *cp==LBRACE || *cp==RBRACE || *cp=='.'))
+ break;
+ else if(mp->split && endch && !mp->quote && !mp->lit)
+ {
+ if(c)
+ mac_copy(mp,first,c);
+ cp = fcseek(c+2);
+ if(c= cp[-1])
+ {
+ sfputc(stkp,c);
+ if(c==ESCAPE)
+ sfputc(stkp,ESCAPE);
+ }
+ else
+ cp--;
+ first = cp;
+ break;
+ }
+ n = state[*(unsigned char*)cp];
+ if(n==S_ENDCH && *cp!=endch)
+ n = S_PAT;
+ if(mp->pattern)
+ {
+ /* preserve \digit for pattern matching */
+ /* also \alpha for extended patterns */
+ if(!mp->lit && !mp->quote)
+ {
+ int nc = *(unsigned char*)cp;
+ if((n==S_DIG || ((paren+ere) && (sh_lexstates[ST_DOL][nc]==S_ALP) || nc=='<' || nc=='>')))
+ break;
+ if(ere && mp->pattern==1 && strchr(".[()*+?{|^$&!",*cp))
+ break;
+ }
+ /* followed by file expansion */
+ if(!mp->lit && (n==S_ESC || (!mp->quote &&
+ (n==S_PAT||n==S_ENDCH||n==S_SLASH||n==S_BRACT||*cp=='-'))))
+ {
+ cp += (n!=S_EOF);
+ if(ere && n==S_ESC && *cp =='\\' && cp[1]=='$')
+ {
+ /* convert \\\$ into \$' */
+ sfwrite(stkp,first,c+1);
+ cp = first = fcseek(c+3);
+ }
+ break;
+ }
+ if(!(ere && *cp=='$') && (mp->lit || (mp->quote && !isqescchar(n) && n!=S_ENDCH)))
+ {
+ /* add \ for file expansion */
+ sfwrite(stkp,first,c+1);
+ first = fcseek(c);
+ break;
+ }
+ }
+ if(mp->lit)
+ break;
+ if(!mp->quote || isqescchar(n) || n==S_ENDCH)
+ {
+ /* eliminate \ */
+ if(c)
+ sfwrite(stkp,first,c);
+ /* check new-line joining */
+ first = fcseek(c+1);
+ }
+ cp += (n!=S_EOF);
+ break;
+ case S_GRAVE: case S_DOL:
+ if(mp->lit)
+ break;
+ if(c)
+ {
+ if(mp->split && !mp->quote && endch)
+ mac_copy(mp,first,c);
+ else
+ sfwrite(stkp,first,c);
+ }
+ first = fcseek(c+1);
+ c = mp->pattern;
+ if(n==S_GRAVE)
+ comsubst(mp,(Shnode_t*)0,0);
+ else if((n= *cp) == '"' && !mp->quote)
+ {
+ int off = stktell(stkp);
+ char *dp;
+ cp = first = fcseek(1);
+ mp->quote = 1;
+ if(!ERROR_translating())
+ break;
+ while(n=c, c= *++cp)
+ {
+ if(c=='\\' && n==c)
+ c = 0;
+ else if(c=='"' && n!='\\')
+ break;
+ }
+ n = cp-first;
+ sfwrite(stkp,first,n);
+ sfputc(stkp,0);
+ cp = stkptr(stkp,off);
+ dp = (char*)sh_translate(cp);
+ stkseek(stkp,off);
+ if(dp==cp)
+ {
+ cp = first;
+ break;
+ }
+ resume = fcseek(n);
+ fcclose();
+ fcsopen(dp);
+ cp = first = fcseek(0);
+ break;
+ }
+ else if(n==0 || !varsub(mp))
+ {
+ if(n=='\'' && !mp->quote)
+ ansi_c = 1;
+ else if(mp->quote || n!='"')
+ sfputc(stkp,'$');
+ }
+ cp = first = fcseek(0);
+ if(mp->quote && cp)
+ mp->pattern = c;
+ break;
+ case S_ENDCH:
+ if((mp->lit || cp[-1]!=endch || mp->quote!=newquote))
+ goto pattern;
+ if(endch==RBRACE && *cp==LPAREN && mp->pattern && brace)
+ goto pattern;
+ case S_EOF:
+ if(c)
+ {
+ if(mp->split && !mp->quote && !mp->lit && endch)
+ mac_copy(mp,first,c);
+ else
+ sfwrite(stkp,first,c);
+ }
+ if(n==S_EOF && resume)
+ {
+ fcclose();
+ fcsopen(resume);
+ resume = 0;
+ cp = first = fcseek(0);
+ continue;
+ }
+ c += (n!=S_EOF);
+ first = fcseek(c);
+ if(tilde>=0)
+ tilde_expand2(mp->shp,tilde);
+ goto done;
+ case S_QUOTE:
+ if(mp->lit || mp->arith)
+ break;
+ case S_LIT:
+ if(mp->arith)
+ {
+ if((*cp=='`' || *cp=='[') && cp[1]=='\'')
+ cp +=2;
+ break;
+ }
+ if(n==S_LIT && mp->quote)
+ break;
+ if(c)
+ {
+ if(mp->split && endch && !mp->quote && !mp->lit)
+ mac_copy(mp,first,c);
+ else
+ sfwrite(stkp,first,c);
+ }
+ first = fcseek(c+1);
+ if(n==S_LIT)
+ {
+ if(mp->quote)
+ continue;
+ if(mp->lit)
+ mp->lit = ansi_c = 0;
+ else
+ mp->lit = 1;
+ }
+ else
+ mp->quote = !mp->quote;
+ mp->quoted++;
+ break;
+ case S_BRACT:
+ if(mp->arith || (((mp->assign&1) || endch==RBRACT) &&
+ !(mp->quote || mp->lit)))
+ {
+ int offset=0,oldpat = mp->pattern;
+ int oldarith = mp->arith, oldsub=mp->subcopy;
+ sfwrite(stkp,first,++c);
+ if(mp->assign&1)
+ {
+ if(first[c-2]=='.')
+ offset = stktell(stkp);
+ if(isastchar(*cp) && cp[1]==']')
+ errormsg(SH_DICT,ERROR_exit(1),
+e_badsubscript,*cp);
+ }
+ first = fcseek(c);
+ mp->pattern = 4;
+ mp->arith = 0;
+ mp->subcopy = 0;
+ copyto(mp,RBRACT,0);
+ mp->subcopy = oldsub;
+ mp->arith = oldarith;
+ mp->pattern = oldpat;
+ sfputc(stkp,RBRACT);
+ if(offset)
+ {
+ cp = stkptr(stkp,stktell(stkp));
+ if(sh_checkid(stkptr(stkp,offset),cp)!=cp)
+ stkseek(stkp,stktell(stkp)-2);
+ }
+ cp = first = fcseek(0);
+ break;
+ }
+ case S_PAT:
+ if(mp->pattern && !(mp->quote || mp->lit))
+ {
+ mp->patfound = mp->pattern;
+ if((n=cp[-1])==LPAREN)
+ {
+ paren++;
+ if((cp-first)>1 && cp[-2]=='~')
+ {
+ char *p = cp;
+ while((c=mbchar(p)) && c!=RPAREN)
+ if(c=='A'||c=='E'||c=='K'||c=='P'||c=='X')
+ {
+ ere = 1;
+ break;
+ }
+ }
+ }
+ else if(n==RPAREN)
+ --paren;
+ }
+ goto pattern;
+ case S_COM:
+ if(mp->pattern==4 && (mp->quote || mp->lit))
+ {
+ if(c)
+ {
+ sfwrite(stkp,first,c);
+ first = fcseek(c);
+ }
+ sfputc(stkp,ESCAPE);
+ }
+ break;
+ case S_BRACE:
+ if(!(mp->quote || mp->lit))
+ {
+ mp->patfound = mp->split && sh_isoption(SH_BRACEEXPAND);
+ brace = 1;
+ }
+ pattern:
+ if(!mp->pattern || !(mp->quote || mp->lit))
+ {
+ /* mark beginning of {a,b} */
+ if(n==S_BRACE && endch==0 && mp->pattern)
+ mp->pattern=4;
+ if(n==S_SLASH && mp->pattern==2)
+ mp->pattern=3;
+ break;
+ }
+ if(mp->pattern==3)
+ break;
+ if(c)
+ sfwrite(stkp,first,c);
+ first = fcseek(c);
+ sfputc(stkp,ESCAPE);
+ break;
+ case S_EQ:
+ if(mp->assign==1)
+ {
+ if(*cp=='~' && !endch && !mp->quote && !mp->lit)
+ tilde = stktell(stkp)+(c+1);
+ mp->assign = 2;
+ }
+ break;
+ case S_SLASH:
+ case S_COLON:
+ if(tilde >=0)
+ {
+ if(c)
+ sfwrite(stkp,first,c);
+ first = fcseek(c);
+ tilde_expand2(mp->shp,tilde);
+#if _WINIX
+ if(Skip)
+ {
+ first = cp = fcseek(Skip);
+ Skip = 0;
+ }
+#endif /*_WINIX */
+ tilde = -1;
+ c=0;
+ }
+ if(n==S_COLON && mp->assign==2 && *cp=='~' && endch==0 && !mp->quote &&!mp->lit)
+ tilde = stktell(stkp)+(c+1);
+ else if(n==S_SLASH && mp->pattern==2)
+#if 0
+ goto pattern;
+#else
+ {
+ if(mp->quote || mp->lit)
+ goto pattern;
+ sfwrite(stkp,first,c+1);
+ first = fcseek(c+1);
+ c = stktell(stkp);
+ sh_lexskip(lp,RBRACE,0,ST_NESTED);
+ stkseek(stkp,c);
+ cp = fcseek(-1);
+ sfwrite(stkp,first,cp-first);
+ first=cp;
+ }
+#endif
+ break;
+ case S_DOT:
+ if(*cp=='.' && mp->subcopy==1)
+ {
+ sfwrite(stkp,first,c);
+ sfputc(stkp,0);
+ mp->dotdot = stktell(stkp);
+ cp = first = fcseek(c+2);
+ }
+ break;
+ }
+ }
+done:
+ mp->sp = sp;
+ mp->quote = oldquote;
+}
+
+/*
+ * copy <str> to stack performing sub-expression substitutions
+ */
+static void mac_substitute(Mac_t *mp, register char *cp,char *str,register int subexp[],int subsize)
+{
+ register int c,n;
+ register char *first=cp;
+ while(1)
+ {
+ while((c= *cp++) && c!=ESCAPE);
+ if(c==0)
+ break;
+ if((n= *cp++)=='\\' || n==RBRACE || (n>='0' && n<='9' && (n-='0')<subsize))
+ {
+ c = cp-first-2;
+ if(c)
+ mac_copy(mp,first,c);
+ first=cp;
+ if(n=='\\' || n==RBRACE)
+ {
+ first--;
+ continue;
+ }
+ if((c=subexp[2*n])>=0)
+ {
+ if((n=subexp[2*n+1]-c)>0)
+ mac_copy(mp,str+c,n);
+ }
+ }
+ else if(n==0)
+ break;
+ }
+ if(n=cp-first-1)
+ mac_copy(mp,first,n);
+}
+
+#if SHOPT_FILESCAN
+#define MAX_OFFSETS (sizeof(shp->offsets)/sizeof(shp->offsets[0]))
+#define MAX_ARGN (32*1024)
+
+/*
+ * compute the arguments $1 ... $n and $# from the current line as needed
+ * save line offsets in the offsets array.
+ */
+static char *getdolarg(Shell_t *shp, int n, int *size)
+{
+ register int c=S_DELIM, d=shp->ifstable['\\'];
+ register unsigned char *first,*last,*cp = (unsigned char*)shp->cur_line;
+ register int m=shp->offsets[0],delim=0;
+ if(m==0)
+ return(0);
+ if(m<0)
+ m = 0;
+ else if(n<=m)
+ m = n-1;
+ else
+ m--;
+ if(m >= MAX_OFFSETS-1)
+ m = MAX_OFFSETS-2;
+ cp += shp->offsets[m+1];
+ n -= m;
+ shp->ifstable['\\'] = 0;
+ shp->ifstable[0] = S_EOF;
+ while(1)
+ {
+ if(c==S_DELIM)
+ while(shp->ifstable[*cp++]==S_SPACE);
+ first = --cp;
+ if(++m < MAX_OFFSETS)
+ shp->offsets[m] = (first-(unsigned char*)shp->cur_line);
+ while((c=shp->ifstable[*cp++])==0);
+ last = cp-1;
+ if(c==S_SPACE)
+ while((c=shp->ifstable[*cp++])==S_SPACE);
+ if(--n==0 || c==S_EOF)
+ {
+ if(last==first && c==S_EOF && (!delim || (m>1)))
+ {
+ n++;
+ m--;
+ }
+ break;
+ }
+ delim = (c==S_DELIM);
+ }
+ shp->ifstable['\\'] = d;
+ if(m > shp->offsets[0])
+ shp->offsets[0] = m;
+ if(n)
+ first = last = 0;
+ if(size)
+ *size = last-first;
+ return((char*)first);
+}
+#endif /* SHOPT_FILESCAN */
+
+/*
+ * get the prefix after name reference resolution
+ */
+static char *prefix(Shell_t *shp, char *id)
+{
+ Namval_t *np;
+ register char *sub=0, *cp = strchr(id,'.');
+ if(cp)
+ {
+ *cp = 0;
+ np = nv_search(id, shp->var_tree,0);
+ *cp = '.';
+ if(isastchar(cp[1]))
+ cp[1] = 0;
+ if(np && nv_isref(np))
+ {
+ int n;
+ char *sp;
+ shp->argaddr = 0;
+ while(nv_isref(np) && np->nvalue.cp)
+ {
+ sub = nv_refsub(np);
+ np = nv_refnode(np);
+ if(sub)
+ nv_putsub(np,sub,0L);
+ }
+ id = (char*)malloc(strlen(cp)+1+(n=strlen(sp=nv_name(np)))+ (sub?strlen(sub)+3:1));
+ memcpy(id,sp,n);
+ if(sub)
+ {
+ id[n++] = '[';
+ strcpy(&id[n],sub);
+ n+= strlen(sub)+1;
+ id[n-1] = ']';
+ }
+ strcpy(&id[n],cp);
+ return(id);
+ }
+ }
+ return(strdup(id));
+}
+
+/*
+ * copy to ']' onto the stack and return offset to it
+ */
+static int subcopy(Mac_t *mp, int flag)
+{
+ int split = mp->split;
+ int xpattern = mp->pattern;
+ int loc = stktell(mp->shp->stk);
+ int xarith = mp->arith;
+ int arrayok = mp->arrayok;
+ mp->split = 0;
+ mp->arith = 0;
+ mp->pattern = flag?4:0;
+ mp->arrayok=1;
+ mp->subcopy++;
+ mp->dotdot = 0;
+ copyto(mp,RBRACT,0);
+ mp->subcopy = 0;
+ mp->pattern = xpattern;
+ mp->split = split;
+ mp->arith = xarith;
+ mp->arrayok = arrayok;
+ return(loc);
+}
+
+/*
+ * if name is a discipline function, run the function and put the results
+ * on the stack so that ${x.foo} behaves like ${ x.foo;}
+ */
+int sh_macfun(Shell_t *shp, const char *name, int offset)
+{
+ Namval_t *np, *nq;
+ np = nv_bfsearch(name,shp->fun_tree,&nq,(char**)0);
+ if(np)
+ {
+ /* treat ${x.foo} as ${x.foo;} */
+ union
+ {
+ struct comnod com;
+ Shnode_t node;
+ } t;
+ union
+ {
+ struct argnod arg;
+ struct dolnod dol;
+ char buff[sizeof(struct dolnod)+sizeof(char*)];
+ } d;
+ memset(&t,0,sizeof(t));
+ memset(&d,0,sizeof(d));
+ t.node.com.comarg = &d.arg;
+ t.node.com.comline = shp->inlineno;
+ d.dol.dolnum = 1;
+ d.dol.dolval[0] = strdup(name);
+ stkseek(shp->stk,offset);
+ comsubst((Mac_t*)shp->mac_context,&t.node,2);
+ free(d.dol.dolval[0]);
+ return(1);
+ }
+ return(0);
+}
+
+static int namecount(Mac_t *mp,const char *prefix)
+{
+ int count = 0;
+ mp->nvwalk = nv_diropen((Namval_t*)0,prefix);
+ while(nv_dirnext(mp->nvwalk))
+ count++;
+ nv_dirclose(mp->nvwalk);
+ return(count);
+}
+
+static char *nextname(Mac_t *mp,const char *prefix, int len)
+{
+ char *cp;
+ if(len==0)
+ {
+ mp->nvwalk = nv_diropen((Namval_t*)0,prefix);
+ return((char*)mp->nvwalk);
+ }
+ if(!(cp=nv_dirnext(mp->nvwalk)))
+ nv_dirclose(mp->nvwalk);
+ return(cp);
+}
+
+/*
+ * This routine handles $param, ${parm}, and ${param op word}
+ * The input stream is assumed to be a string
+ */
+static int varsub(Mac_t *mp)
+{
+ register int c;
+ register int type=0; /* M_xxx */
+ register char *v,*argp=0;
+ register Namval_t *np = NIL(Namval_t*);
+ register int dolg=0, mode=0;
+ Lex_t *lp = (Lex_t*)mp->shp->lex_context;
+ Namarr_t *ap=0;
+ int dolmax=0, vsize= -1, offset= -1, nulflg, replen=0, bysub=0;
+ char idbuff[3], *id = idbuff, *pattern=0, *repstr=0, *arrmax=0;
+ char *idx = 0;
+ int var=1,addsub=0,oldpat=mp->pattern,idnum=0,flag=0,d;
+ Stk_t *stkp = mp->shp->stk;
+retry1:
+ mp->zeros = 0;
+ idbuff[0] = 0;
+ idbuff[1] = 0;
+ c = fcmbget(&LEN);
+ switch(isascii(c)?sh_lexstates[ST_DOL][c]:S_ALP)
+ {
+ case S_RBRA:
+ if(type<M_SIZE)
+ goto nosub;
+ /* This code handles ${#} */
+ c = mode;
+ mode = type = 0;
+ /* FALL THRU */
+ case S_SPC1:
+ if(type==M_BRACE)
+ {
+ if(isaletter(mode=fcpeek(0)) || mode=='.')
+ {
+ if(c=='#')
+ type = M_SIZE;
+#ifdef SHOPT_TYPEDEF
+ else if(c=='@')
+ {
+ type = M_TYPE;
+ goto retry1;
+ }
+#endif /* SHOPT_TYPEDEF */
+ else
+ type = M_VNAME;
+ mode = c;
+ goto retry1;
+ }
+ else if(c=='#' && (isadigit(mode)||fcpeek(1)==RBRACE))
+ {
+ type = M_SIZE;
+ mode = c;
+ goto retry1;
+ }
+ }
+ /* FALL THRU */
+ case S_SPC2:
+ var = 0;
+ *id = c;
+ v = special(mp->shp,c);
+ if(isastchar(c))
+ {
+ mode = c;
+#if SHOPT_FILESCAN
+ if(mp->shp->cur_line)
+ {
+ v = getdolarg(mp->shp,1,(int*)0);
+ dolmax = MAX_ARGN;
+ }
+ else
+#endif /* SHOPT_FILESCAN */
+ dolmax = mp->shp->st.dolc+1;
+ mp->atmode = (v && mp->quoted && c=='@');
+ dolg = (v!=0);
+ }
+ break;
+ case S_LBRA:
+ if(type)
+ goto nosub;
+ type = M_BRACE;
+ goto retry1;
+ case S_PAR:
+ if(type)
+ goto nosub;
+ comsubst(mp,(Shnode_t*)0,1);
+ return(1);
+ case S_DIG:
+ var = 0;
+ c -= '0';
+ mp->shp->argaddr = 0;
+ if(type)
+ {
+ register int d;
+ while((d=fcget()),isadigit(d))
+ c = 10*c + (d-'0');
+ fcseek(-1);
+ }
+ idnum = c;
+ if(c==0)
+ v = special(mp->shp,c);
+#if SHOPT_FILESCAN
+ else if(mp->shp->cur_line)
+ {
+ mp->shp->used_pos = 1;
+ v = getdolarg(mp->shp,c,&vsize);
+ }
+#endif /* SHOPT_FILESCAN */
+ else if(c <= mp->shp->st.dolc)
+ {
+ mp->shp->used_pos = 1;
+ v = mp->shp->st.dolv[c];
+ }
+ else
+ v = 0;
+ break;
+ case S_ALP:
+ if(c=='.' && type==0)
+ goto nosub;
+ offset = stktell(stkp);
+ do
+ {
+ register int d;
+ np = 0;
+ do
+ {
+ if(LEN==1)
+ sfputc(stkp,c);
+ else
+ sfwrite(stkp,fcseek(0)-LEN,LEN);
+ }
+ while((d=c,(c=fcmbget(&LEN)),isaname(c))||type && c=='.');
+ while(c==LBRACT && (type||mp->arrayok))
+ {
+ mp->shp->argaddr=0;
+ if((c=fcmbget(&LEN),isastchar(c)) && fcpeek(0)==RBRACT && d!='.')
+ {
+ if(type==M_VNAME)
+ type = M_SUBNAME;
+ idbuff[0] = mode = c;
+ fcget();
+ c = fcmbget(&LEN);
+ if(c=='.' || c==LBRACT)
+ {
+ sfputc(stkp,LBRACT);
+ sfputc(stkp,mode);
+ sfputc(stkp,RBRACT);
+ }
+ else
+ flag = NV_ARRAY;
+ break;
+ }
+ else
+ {
+ fcseek(-LEN);
+ c = stktell(stkp);
+ if(d!='.')
+ sfputc(stkp,LBRACT);
+ v = stkptr(stkp,subcopy(mp,1));
+ if(type && mp->dotdot)
+ {
+ mode = '@';
+ v[-1] = 0;
+ if(type==M_VNAME)
+ type = M_SUBNAME;
+ else if(type==M_SIZE)
+ goto nosub;
+ }
+ else if(d!='.')
+ sfputc(stkp,RBRACT);
+ c = fcmbget(&LEN);
+ if(c==0 && type==M_VNAME)
+ type = M_SUBNAME;
+ }
+ }
+ }
+ while(type && c=='.');
+ if(type!=M_VNAME && c==RBRACE && type && fcpeek(-2)=='.')
+ {
+ /* ${x.} or ${x..} */
+ if(fcpeek(-3) == '.')
+ {
+ stkseek(stkp,stktell(stkp)-2);
+ nv_local = 1;
+ }
+ else
+ {
+ stkseek(stkp,stktell(stkp)-1);
+ type = M_TREE;
+ }
+ }
+ sfputc(stkp,0);
+ id=stkptr(stkp,offset);
+ if(isastchar(c) && type)
+ {
+ if(type==M_VNAME || type==M_SIZE)
+ {
+ idbuff[0] = mode = c;
+ if((d=fcpeek(0))==c)
+ idbuff[1] = fcget();
+ if(type==M_VNAME)
+ type = M_NAMESCAN;
+ else
+ type = M_NAMECOUNT;
+ break;
+ }
+ goto nosub;
+ }
+ flag |= NV_NOASSIGN|NV_VARNAME|NV_NOADD;
+ if(c=='=' || c=='?' || (c==':' && ((d=fcpeek(0))=='=' || d=='?')))
+ {
+ if(c=='=' || (c==':' && d=='='))
+ flag |= NV_ASSIGN;
+ flag &= ~NV_NOADD;
+ }
+#if SHOPT_FILESCAN
+ if(mp->shp->cur_line && *id=='R' && strcmp(id,"REPLY")==0)
+ {
+ mp->shp->argaddr=0;
+ np = REPLYNOD;
+ }
+ else
+#endif /* SHOPT_FILESCAN */
+ {
+ if(mp->shp->argaddr)
+ flag &= ~NV_NOADD;
+ np = nv_open(id,mp->shp->var_tree,flag|NV_NOFAIL);
+ if(!np)
+ {
+ sfprintf(mp->shp->strbuf,"%s%c",id,0);
+ id = sfstruse(mp->shp->strbuf);
+ }
+ }
+ if(isastchar(mode))
+ var = 0;
+ if((!np || nv_isnull(np)) && type==M_BRACE && c==RBRACE && !(flag&NV_ARRAY) && strchr(id,'.'))
+ {
+ if(sh_macfun(mp->shp,id,offset))
+ {
+ fcmbget(&LEN);
+ return(1);
+ }
+ }
+ if(np && (flag&NV_NOADD) && nv_isnull(np))
+ {
+ if(nv_isattr(np,NV_NOFREE))
+ nv_offattr(np,NV_NOFREE);
+#if SHOPT_FILESCAN
+ else if(np!=REPLYNOD || !mp->shp->cur_line)
+#else
+ else
+#endif /* SHOPT_FILESCAN */
+ np = 0;
+ }
+ ap = np?nv_arrayptr(np):0;
+ if(type)
+ {
+ if(mp->dotdot)
+ {
+ Namval_t *nq;
+#if SHOPT_FIXEDARRAY
+ if(ap && !ap->fixed && (nq=nv_opensub(np)))
+#else
+ if(ap && (nq=nv_opensub(np)))
+#endif /* SHOPT_FIXEDARRAY */
+ ap = nv_arrayptr(np=nq);
+ if(ap)
+ {
+ nv_putsub(np,v,ARRAY_SCAN);
+ v = stkptr(stkp,mp->dotdot);
+ dolmax =1;
+ if(array_assoc(ap))
+ arrmax = strdup(v);
+ else if((dolmax = (int)sh_arith(mp->shp,v))<0)
+ dolmax += array_maxindex(np);
+ if(type==M_SUBNAME)
+ bysub = 1;
+ }
+ else
+ {
+ if((int)sh_arith(mp->shp,v))
+ np = 0;
+ }
+ }
+ else if(ap && (isastchar(mode)||type==M_TREE) && !(ap->nelem&ARRAY_SCAN) && type!=M_SIZE)
+ nv_putsub(np,NIL(char*),ARRAY_SCAN);
+ if(!isbracechar(c))
+ goto nosub;
+ else
+ fcseek(-LEN);
+ }
+ else
+ fcseek(-1);
+ if(type<=1 && np && nv_isvtree(np) && mp->pattern==1 && !mp->split)
+ {
+ int cc=fcmbget(&LEN),peek=LEN;
+ if(type && cc=='}')
+ {
+ cc = fcmbget(&LEN);
+ peek++;
+ }
+ if(mp->quote && cc=='"')
+ {
+ cc = fcmbget(&LEN);
+ peek++;
+ }
+ fcseek(-peek);
+ if(cc==0)
+ mp->assign = 1;
+ }
+ if((type==M_VNAME||type==M_SUBNAME) && mp->shp->argaddr && strcmp(nv_name(np),id))
+ mp->shp->argaddr = 0;
+ c = (type>M_BRACE && isastchar(mode));
+ if(np && (type==M_TREE || !c || !ap))
+ {
+ char *savptr;
+ c = *((unsigned char*)stkptr(stkp,offset-1));
+ savptr = stkfreeze(stkp,0);
+ if(type==M_VNAME || (type==M_SUBNAME && ap))
+ {
+ type = M_BRACE;
+ v = nv_name(np);
+ if(ap && !mp->dotdot && !(ap->nelem&ARRAY_UNDEF))
+ addsub = 1;
+ }
+#ifdef SHOPT_TYPEDEF
+ else if(type==M_TYPE)
+ {
+ Namval_t *nq = nv_type(np);
+ type = M_BRACE;
+ if(nq)
+ nv_typename(nq,mp->shp->strbuf);
+ else
+ nv_attribute(np,mp->shp->strbuf,"typeset",1);
+ v = sfstruse(mp->shp->strbuf);
+ }
+#endif /* SHOPT_TYPEDEF */
+#if SHOPT_FILESCAN
+ else if(mp->shp->cur_line && np==REPLYNOD)
+ v = mp->shp->cur_line;
+#endif /* SHOPT_FILESCAN */
+ else if(type==M_TREE)
+ v = nv_getvtree(np,(Namfun_t*)0);
+ else
+ {
+ if(type && fcpeek(0)=='+')
+ {
+ if(ap)
+ v = nv_arrayisset(np,ap)?(char*)"x":0;
+ else
+ v = nv_isnull(np)?0:(char*)"x";
+ }
+ else
+ v = nv_getval(np);
+ mp->atmode = (v && mp->quoted && mode=='@');
+ /* special case --- ignore leading zeros */
+ if( (mp->arith||mp->let) && (np->nvfun || nv_isattr(np,(NV_LJUST|NV_RJUST|NV_ZFILL))) && !nv_isattr(np,NV_INTEGER) && (offset==0 || !isalnum(c)))
+ mp->zeros = 1;
+ }
+ if(savptr==stakptr(0))
+ stkseek(stkp,offset);
+ else
+ stkset(stkp,savptr,offset);
+ }
+ else
+ {
+ if(sh_isoption(SH_NOUNSET) && !isastchar(mode) && (type==M_VNAME || type==M_SIZE))
+ errormsg(SH_DICT,ERROR_exit(1),e_notset,id);
+ v = 0;
+ if(type==M_VNAME)
+ {
+ v = id;
+ type = M_BRACE;
+ }
+ else if(type==M_TYPE)
+ type = M_BRACE;
+ }
+ stkseek(stkp,offset);
+ if(ap)
+ {
+#if SHOPT_OPTIMIZE
+ if(mp->shp->argaddr)
+ nv_optimize(np);
+#endif
+ if(isastchar(mode) && array_elem(ap)> !c)
+ dolg = -1;
+ else
+ dolg = 0;
+ }
+ break;
+ case S_EOF:
+ fcseek(-1);
+ default:
+ goto nosub;
+ }
+ c = fcmbget(&LEN);
+ if(type>M_TREE)
+ {
+ if(c!=RBRACE)
+ mac_error(np);
+ if(type==M_NAMESCAN || type==M_NAMECOUNT)
+ {
+ mp->shp->last_root = mp->shp->var_tree;
+ id = idx = prefix(mp->shp,id);
+ stkseek(stkp,offset);
+ if(type==M_NAMECOUNT)
+ {
+ c = namecount(mp,id);
+ v = ltos(c);
+ }
+ else
+ {
+ dolmax = strlen(id);
+ dolg = -1;
+ nextname(mp,id,0);
+ v = nextname(mp,id,dolmax);
+ }
+ }
+ else if(type==M_SUBNAME)
+ {
+ if(dolg<0)
+ {
+ v = nv_getsub(np);
+ bysub=1;
+ }
+ else if(v)
+ {
+ if(!ap || isastchar(mode))
+ v = "0";
+ else
+ v = nv_getsub(np);
+ }
+ }
+ else
+ {
+ if(!isastchar(mode))
+ c = charlen(v,vsize);
+ else if(dolg>0)
+ {
+#if SHOPT_FILESCAN
+ if(mp->shp->cur_line)
+ {
+ getdolarg(mp->shp,MAX_ARGN,(int*)0);
+ c = mp->shp->offsets[0];
+ }
+ else
+#endif /* SHOPT_FILESCAN */
+ c = mp->shp->st.dolc;
+ }
+ else if(dolg<0)
+ c = array_elem(ap);
+ else
+ c = (v!=0);
+ dolg = dolmax = 0;
+ v = ltos(c);
+ }
+ c = RBRACE;
+ }
+ nulflg = 0;
+ if(type && c==':')
+ {
+ c = fcmbget(&LEN);
+ if(isascii(c) &&sh_lexstates[ST_BRACE][c]==S_MOD1 && c!='*' && c!= ':')
+ nulflg=1;
+ else if(c!='%' && c!='#')
+ {
+ fcseek(-LEN);
+ c = ':';
+ }
+ }
+ if(type)
+ {
+ if(!isbracechar(c))
+ {
+ if(!nulflg)
+ mac_error(np);
+ fcseek(-LEN);
+ c = ':';
+ }
+ if(c!=RBRACE)
+ {
+ int newops = (c=='#' || c == '%' || c=='/');
+ offset = stktell(stkp);
+ if(newops && sh_isoption(SH_NOUNSET) && *id && id!=idbuff && (!np || nv_isnull(np)))
+ errormsg(SH_DICT,ERROR_exit(1),e_notset,id);
+ if(c=='/' ||c==':' || ((!v || (nulflg && *v==0)) ^ (c=='+'||c=='#'||c=='%')))
+ {
+ int newquote = mp->quote;
+ int split = mp->split;
+ int quoted = mp->quoted;
+ int arith = mp->arith;
+ int zeros = mp->zeros;
+ int assign = mp->assign;
+ if(newops)
+ {
+ type = fcget();
+ if(type=='%' || type=='#')
+ {
+ int d = fcmbget(&LEN);
+ fcseek(-LEN);
+ if(d=='(')
+ type = 0;
+ }
+ fcseek(-1);
+ mp->pattern = 1+(c=='/');
+ mp->split = 0;
+ mp->quoted = 0;
+ mp->assign &= ~1;
+ mp->arith = mp->zeros = 0;
+ newquote = 0;
+ }
+ else if(c=='?' || c=='=')
+ mp->split = mp->pattern = 0;
+ copyto(mp,RBRACE,newquote);
+ if(!oldpat)
+ mp->patfound = 0;
+ mp->pattern = oldpat;
+ mp->split = split;
+ mp->quoted = quoted;
+ mp->arith = arith;
+ mp->zeros = zeros;
+ mp->assign = assign;
+ /* add null byte */
+ sfputc(stkp,0);
+ stkseek(stkp,stktell(stkp)-1);
+ }
+ else
+ {
+ sh_lexskip(lp,RBRACE,0,(!newops&&mp->quote)?ST_QUOTE:ST_NESTED);
+ stkseek(stkp,offset);
+ }
+ argp=stkptr(stkp,offset);
+ }
+ }
+ else
+ {
+ fcseek(-1);
+ c=0;
+ }
+ if(c==':') /* ${name:expr1[:expr2]} */
+ {
+ char *ptr;
+ type = (int)sh_strnum(argp,&ptr,1);
+ if(isastchar(mode))
+ {
+ if(id==idbuff) /* ${@} or ${*} */
+ {
+ if(type<0 && (type+= dolmax)<0)
+ type = 0;
+ if(type==0)
+ v = special(mp->shp,dolg=0);
+#if SHOPT_FILESCAN
+ else if(mp->shp->cur_line)
+ {
+ v = getdolarg(mp->shp,dolg=type,&vsize);
+ if(!v)
+ dolmax = type;
+ }
+#endif /* SHOPT_FILESCAN */
+ else if(type < dolmax)
+ v = mp->shp->st.dolv[dolg=type];
+ else
+ v = 0;
+ }
+ else if(ap)
+ {
+ if(type<0)
+ {
+ if(array_assoc(ap))
+ type = -type;
+ else
+ type += array_maxindex(np);
+ }
+ if(array_assoc(ap))
+ {
+ while(type-- >0 && (v=0,nv_nextsub(np)))
+ v = nv_getval(np);
+ }
+ else if(type > 0)
+ {
+ if(nv_putsub(np,NIL(char*),type|ARRAY_SCAN))
+ v = nv_getval(np);
+ else
+ v = 0;
+ }
+ }
+ else if(type>0)
+ v = 0;
+ if(!v)
+ mp->atmode = 0;
+ }
+ else if(v)
+ {
+ vsize = charlen(v,vsize);
+ if(type<0 && (type += vsize)<0)
+ type = 0;
+ if(vsize < type)
+ v = 0;
+#if SHOPT_MULTIBYTE
+ else if(mbwide())
+ {
+ mbinit();
+ for(c=type;c;c--)
+ mbchar(v);
+ c = ':';
+ }
+#endif /* SHOPT_MULTIBYTE */
+ else
+ v += type;
+ vsize = v?strlen(v):0;
+ }
+ if(*ptr==':')
+ {
+ if((type = (int)sh_strnum(ptr+1,&ptr,1)) <=0)
+ v = 0;
+ else if(isastchar(mode))
+ {
+ if(dolg>=0)
+ {
+ if(dolg+type < dolmax)
+ dolmax = dolg+type;
+ }
+ else
+ dolmax = type;
+ }
+ else if(type < vsize)
+ {
+#if SHOPT_MULTIBYTE
+ if(mbwide())
+ {
+ char *vp = v;
+ mbinit();
+ while(type-->0)
+ {
+ if((c=mbsize(vp))<1)
+ c = 1;
+ vp += c;
+ }
+ type = vp-v;
+ c = ':';
+ }
+#endif /* SHOPT_MULTIBYTE */
+ vsize = type;
+ }
+ else
+ vsize = v?strlen(v):0;
+ }
+ if(*ptr)
+ mac_error(np);
+ stkseek(stkp,offset);
+ argp = 0;
+ }
+ /* check for substring operations */
+ else if(c == '#' || c == '%' || c=='/')
+ {
+ if(c=='/')
+ {
+ if(type=='/' || type=='#' || type=='%')
+ {
+ c = type;
+ type = '/';
+ argp++;
+ }
+ else
+ type = 0;
+ }
+ else
+ {
+ if(type==c) /* ## or %% */
+ argp++;
+ else
+ type = 0;
+ }
+ pattern = strdup(argp);
+ if((type=='/' || c=='/') && (repstr = mac_getstring(pattern)))
+ {
+ Mac_t savemac;
+ char *first = fcseek(0);
+ int n = stktell(stkp);
+ savemac = *mp;
+ fcsopen(repstr);
+ mp->pattern = 3;
+ mp->split = 0;
+ copyto(mp,0,0);
+ sfputc(stkp,0);
+ repstr = strdup(stkptr(stkp,n));
+ replen = strlen(repstr);
+ stkseek(stkp,n);
+ *mp = savemac;
+ fcsopen(first);
+ }
+ if(v || c=='/' && offset>=0)
+ stkseek(stkp,offset);
+ }
+ /* check for quoted @ */
+ if(mode=='@' && mp->quote && !v && c!='-')
+ mp->quoted-=2;
+retry2:
+ if(v && (!nulflg || *v ) && c!='+')
+ {
+ register int d = (mode=='@'?' ':mp->ifs);
+ int match[2*(MATCH_MAX+1)], nmatch, nmatch_prev, vsize_last;
+ char *vlast;
+ while(1)
+ {
+ if(!v)
+ v= "";
+ if(c=='/' || c=='#' || c== '%')
+ {
+ int index = 0;
+ flag = (type || c=='/')?(STR_GROUP|STR_MAXIMAL):STR_GROUP;
+ if(c!='/')
+ flag |= STR_LEFT;
+ nmatch = 0;
+ while(1)
+ {
+ vsize = strlen(v);
+ nmatch_prev = nmatch;
+ if(c=='%')
+ nmatch=substring(v,pattern,match,flag&STR_MAXIMAL);
+ else
+ nmatch=strgrpmatch(v,pattern,match,elementsof(match)/2,flag);
+ if(nmatch && replen>0)
+ sh_setmatch(mp->shp,v,vsize,nmatch,match,index++);
+ if(nmatch)
+ {
+ vlast = v;
+ vsize_last = vsize;
+ vsize = match[0];
+ }
+ else if(c=='#')
+ vsize = 0;
+ if(vsize)
+ mac_copy(mp,v,vsize);
+ if(nmatch && replen>0 && (match[1] || !nmatch_prev))
+ mac_substitute(mp,repstr,v,match,nmatch);
+ if(nmatch==0)
+ v += vsize;
+ else
+ v += match[1];
+ if(*v && c=='/' && type)
+ {
+ /* avoid infinite loop */
+ if(nmatch && match[1]==0)
+ {
+ nmatch = 0;
+ mac_copy(mp,v,1);
+ v++;
+ }
+ continue;
+ }
+ vsize = -1;
+ break;
+ }
+ if(replen==0)
+ sh_setmatch(mp->shp,vlast,vsize_last,nmatch,match,index++);
+ }
+ if(vsize)
+ mac_copy(mp,v,vsize>0?vsize:strlen(v));
+ if(addsub)
+ {
+ mp->shp->instance++;
+ sfprintf(mp->shp->strbuf,"[%s]",nv_getsub(np));
+ mp->shp->instance--;
+ v = sfstruse(mp->shp->strbuf);
+ mac_copy(mp, v, strlen(v));
+ }
+ if(dolg==0 && dolmax==0)
+ break;
+ if(mp->dotdot)
+ {
+ if(nv_nextsub(np) == 0)
+ break;
+ if(bysub)
+ v = nv_getsub(np);
+ else
+ v = nv_getval(np);
+ if(array_assoc(ap))
+ {
+ if(strcmp(bysub?v:nv_getsub(np),arrmax)>0)
+ break;
+ }
+ else
+ {
+ if(nv_aindex(np) > dolmax)
+ break;
+ }
+ }
+ else if(dolg>=0)
+ {
+ if(++dolg >= dolmax)
+ break;
+#if SHOPT_FILESCAN
+ if(mp->shp->cur_line)
+ {
+ if(dolmax==MAX_ARGN && isastchar(mode))
+ break;
+ if(!(v=getdolarg(mp->shp,dolg,&vsize)))
+ {
+ dolmax = dolg;
+ break;
+ }
+ }
+ else
+#endif /* SHOPT_FILESCAN */
+ v = mp->shp->st.dolv[dolg];
+ }
+ else if(!np)
+ {
+ if(!(v = nextname(mp,id,dolmax)))
+ break;
+ }
+ else
+ {
+ if(dolmax && --dolmax <=0)
+ {
+ nv_putsub(np,NIL(char*),ARRAY_UNDEF);
+ break;
+ }
+ if(ap)
+ ap->nelem |= ARRAY_SCAN;
+ if(nv_nextsub(np) == 0)
+ break;
+ if(bysub)
+ v = nv_getsub(np);
+ else
+ v = nv_getval(np);
+ }
+ if(mp->split && (!mp->quote || mode=='@'))
+ {
+ if(!np)
+ mp->pattern = 0;
+ endfield(mp,mp->quoted);
+ mp->atmode = mode=='@';
+ mp->pattern = oldpat;
+ }
+ else if(d)
+ {
+ if(mp->sp)
+ sfputc(mp->sp,d);
+ else
+ sfputc(stkp,d);
+ }
+ }
+ if(arrmax)
+ free((void*)arrmax);
+ }
+ else if(argp)
+ {
+ if(c=='/' && replen>0 && pattern && strmatch("",pattern))
+ mac_substitute(mp,repstr,v,0,0);
+ if(c=='?')
+ {
+ if(np)
+ id = nv_name(np);
+ else if(idnum)
+ id = ltos(idnum);
+ if(*argp)
+ {
+ sfputc(stkp,0);
+ errormsg(SH_DICT,ERROR_exit(1),"%s: %s",id,argp);
+ }
+ else if(v)
+ errormsg(SH_DICT,ERROR_exit(1),e_nullset,id);
+ else
+ errormsg(SH_DICT,ERROR_exit(1),e_notset,id);
+ }
+ else if(c=='=')
+ {
+ if(np)
+ {
+ if(mp->shp->subshell)
+ np = sh_assignok(np,1);
+ nv_putval(np,argp,0);
+ v = nv_getval(np);
+ nulflg = 0;
+ stkseek(stkp,offset);
+ goto retry2;
+ }
+ else
+ mac_error(np);
+ }
+ }
+ else if(var && sh_isoption(SH_NOUNSET) && type<=M_TREE && (!np || nv_isnull(np) || (nv_isarray(np) && !np->nvalue.cp)))
+ {
+ if(np)
+ {
+ if(nv_isarray(np))
+ {
+ sfprintf(mp->shp->strbuf,"%s[%s]\0",nv_name(np),nv_getsub(np));
+ id = sfstruse(mp->shp->strbuf);
+ }
+ else
+ id = nv_name(np);
+ nv_close(np);
+ }
+ errormsg(SH_DICT,ERROR_exit(1),e_notset,id);
+ }
+ if(np)
+ nv_close(np);
+ if(pattern)
+ free(pattern);
+ if(repstr)
+ free(repstr);
+ if(idx)
+ free(idx);
+ return(1);
+nosub:
+ if(type==M_BRACE && sh_lexstates[ST_NORM][c]==S_BREAK)
+ {
+ fcseek(-1);
+ comsubst(mp,(Shnode_t*)0,2);
+ return(1);
+ }
+ if(type)
+ mac_error(np);
+ fcseek(-1);
+ nv_close(np);
+ return(0);
+}
+
+/*
+ * This routine handles command substitution
+ * <type> is 0 for older `...` version
+ */
+static void comsubst(Mac_t *mp,register Shnode_t* t, int type)
+{
+ Sfdouble_t num;
+ register int c;
+ register char *str;
+ Sfio_t *sp;
+ Stk_t *stkp = mp->shp->stk;
+ Fcin_t save;
+ struct slnod *saveslp = mp->shp->st.staklist;
+ struct _mac_ savemac;
+ int savtop = stktell(stkp);
+ char lastc=0, *savptr = stkfreeze(stkp,0);
+#if SHOPT_MULTIBYTE
+ wchar_t lastw=0;
+#endif /* SHOPT_MULTIBYTE */
+ int was_history = sh_isstate(SH_HISTORY);
+ int was_verbose = sh_isstate(SH_VERBOSE);
+ int was_interactive = sh_isstate(SH_INTERACTIVE);
+ int newlines,bufsize,nextnewlines;
+ Namval_t *np;
+ mp->shp->argaddr = 0;
+ savemac = *mp;
+ mp->shp->st.staklist=0;
+#ifdef SHOPT_COSHELL
+ if(mp->shp->inpool)
+ return;
+#endif /*SHOPT_COSHELL */
+ if(type)
+ {
+ sp = 0;
+ fcseek(-1);
+ if(!t)
+ t = sh_dolparen((Lex_t*)mp->shp->lex_context);
+ if(t && t->tre.tretyp==TARITH)
+ {
+ mp->shp->inarith = 1;
+ fcsave(&save);
+ if(t->ar.arcomp)
+ num = arith_exec(t->ar.arcomp);
+ else if((t->ar.arexpr->argflag&ARG_RAW))
+ num = sh_arith(mp->shp,t->ar.arexpr->argval);
+ else
+ num = sh_arith(mp->shp,sh_mactrim(mp->shp,t->ar.arexpr->argval,3));
+ mp->shp->inarith = 0;
+ out_offset:
+ stkset(stkp,savptr,savtop);
+ *mp = savemac;
+ if((Sflong_t)num!=num)
+ sfprintf(mp->shp->strbuf,"%.*Lg",LDBL_DIG,num);
+ else if(num)
+ sfprintf(mp->shp->strbuf,"%lld",(Sflong_t)num);
+ else
+ sfprintf(mp->shp->strbuf,"%Lg",num);
+ str = sfstruse(mp->shp->strbuf);
+ mac_copy(mp,str,strlen(str));
+ mp->shp->st.staklist = saveslp;
+ fcrestore(&save);
+ return;
+ }
+ }
+ else
+ {
+ while(fcgetc(c)!='`' && c)
+ {
+ if(c==ESCAPE)
+ {
+ fcgetc(c);
+ if(!(isescchar(sh_lexstates[ST_QUOTE][c]) ||
+ (c=='"' && mp->quote)))
+ sfputc(stkp,ESCAPE);
+ }
+ sfputc(stkp,c);
+ }
+ c = stktell(stkp);
+ str=stkfreeze(stkp,1);
+ /* disable verbose and don't save in history file */
+ sh_offstate(SH_HISTORY);
+ sh_offstate(SH_VERBOSE);
+ if(mp->sp)
+ sfsync(mp->sp); /* flush before executing command */
+ sp = sfnew(NIL(Sfio_t*),str,c,-1,SF_STRING|SF_READ);
+ c = mp->shp->inlineno;
+ mp->shp->inlineno = error_info.line+mp->shp->st.firstline;
+ t = (Shnode_t*)sh_parse(mp->shp, sp,SH_EOF|SH_NL);
+ mp->shp->inlineno = c;
+ type = 1;
+ }
+#if KSHELL
+ if(t)
+ {
+ fcsave(&save);
+ sfclose(sp);
+ if(t->tre.tretyp==0 && !t->com.comarg && !t->com.comset)
+ {
+ /* special case $(<file) and $(<#file) */
+ register int fd;
+ int r;
+ struct checkpt buff;
+ struct ionod *ip=0;
+ sh_pushcontext(mp->shp,&buff,SH_JMPIO);
+ if((ip=t->tre.treio) &&
+ ((ip->iofile&IOLSEEK) || !(ip->iofile&IOUFD)) &&
+ (r=sigsetjmp(buff.buff,0))==0)
+ fd = sh_redirect(mp->shp,ip,3);
+ else
+ fd = sh_chkopen(e_devnull);
+ sh_popcontext(mp->shp,&buff);
+ if(r==0 && ip && (ip->iofile&IOLSEEK))
+ {
+ if(sp=mp->shp->sftable[fd])
+ num = sftell(sp);
+ else
+ num = lseek(fd, (off_t)0, SEEK_CUR);
+ goto out_offset;
+ }
+ sp = sfnew(NIL(Sfio_t*),(char*)malloc(IOBSIZE+1),IOBSIZE,fd,SF_READ|SF_MALLOC);
+ type = 3;
+ }
+ else
+ sp = sh_subshell(mp->shp,t,sh_isstate(SH_ERREXIT),type);
+ fcrestore(&save);
+ }
+ else
+ sp = sfopen(NIL(Sfio_t*),"","sr");
+ sh_freeup(mp->shp);
+ mp->shp->st.staklist = saveslp;
+ if(was_history)
+ sh_onstate(SH_HISTORY);
+ if(was_verbose)
+ sh_onstate(SH_VERBOSE);
+#else
+ sp = sfpopen(NIL(Sfio_t*),str,"r");
+#endif
+ *mp = savemac;
+ np = sh_scoped(mp->shp,IFSNOD);
+ nv_putval(np,mp->ifsp,NV_RDONLY);
+ mp->ifsp = nv_getval(np);
+ stkset(stkp,savptr,savtop);
+ newlines = 0;
+ sfsetbuf(sp,(void*)sp,0);
+ bufsize = sfvalue(sp);
+ /* read command substitution output and put on stack or here-doc */
+ sfpool(sp, NIL(Sfio_t*), SF_WRITE);
+ sh_offstate(SH_INTERACTIVE);
+ while((str=(char*)sfreserve(sp,SF_UNBOUND,0)) && (c=bufsize=sfvalue(sp))>0)
+ {
+#if SHOPT_CRNL
+ /* eliminate <cr> */
+ register char *dp;
+ char *buff = str;
+ while(c>1 && (*str !='\r'|| str[1]!='\n'))
+ {
+ c--;
+ str++;
+ }
+ dp = str;
+ while(c>1)
+ {
+ str++;
+ c--;
+ while(c>1 && (*str!='\r' || str[1]!='\n'))
+ {
+ c--;
+ *dp++ = *str++;
+ }
+ }
+ if(c)
+ *dp++ = *str++;
+ str = buff;
+ c = dp-str;
+#endif /* SHOPT_CRNL */
+ /* delay appending trailing new-lines */
+ for(nextnewlines=0; c-->0 && str[c]=='\n'; nextnewlines++);
+ if(c < 0)
+ {
+ newlines += nextnewlines;
+ continue;
+ }
+ if(newlines >0)
+ {
+ if(mp->sp)
+ sfnputc(mp->sp,'\n',newlines);
+ else if(!mp->quote && mp->split && mp->shp->ifstable['\n'])
+ endfield(mp,0);
+ else
+ sfnputc(stkp,'\n',newlines);
+ }
+ else if(lastc)
+ {
+#if SHOPT_MULTIBYTE
+ if(lastw)
+ {
+ int n;
+ char mb[8];
+ n = mbconv(mb, lastw);
+ mac_copy(mp,mb,n);
+ lastw = 0;
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ mac_copy(mp,&lastc,1);
+ lastc = 0;
+ }
+ newlines = nextnewlines;
+ if(++c < bufsize)
+ str[c] = 0;
+ else
+ {
+ ssize_t len = 1;
+
+ /* can't write past buffer so save last character */
+#if SHOPT_MULTIBYTE
+ if ((len = mbsize(str))>1)
+ {
+ len = mb2wc(lastw,str,len);
+ if (len < 0)
+ {
+ lastw = 0;
+ len = 1;
+ }
+ }
+#endif /* SHOPT_MULTIBYTE */
+ c -= len;
+ lastc = str[c];
+ str[c] = 0;
+ }
+ mac_copy(mp,str,c);
+ }
+ if(was_interactive)
+ sh_onstate(SH_INTERACTIVE);
+ if(--newlines>0 && mp->shp->ifstable['\n']==S_DELIM)
+ {
+ if(mp->sp)
+ sfnputc(mp->sp,'\n',newlines);
+ else if(!mp->quote && mp->split)
+ while(newlines--)
+ endfield(mp,1);
+ else
+ sfnputc(stkp,'\n',newlines);
+ }
+ if(lastc)
+ {
+#if SHOPT_MULTIBYTE
+ if(lastw)
+ {
+ int n;
+ char mb[8];
+ n = mbconv(mb, lastw);
+ mac_copy(mp,mb,n);
+ lastw = 0;
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ mac_copy(mp,&lastc,1);
+ lastc = 0;
+ }
+ sfclose(sp);
+ return;
+}
+
+/*
+ * copy <str> onto the stack
+ */
+static void mac_copy(register Mac_t *mp,register const char *str, register int size)
+{
+ register char *state;
+ register const char *cp=str;
+ register int c,n,nopat,len;
+ Stk_t *stkp=mp->shp->stk;
+ int oldpat = mp->pattern;
+ nopat = (mp->quote||(mp->assign==1)||mp->arith);
+ if(mp->zeros)
+ {
+ /* prevent leading 0's from becomming octal constants */
+ while(size>1 && *str=='0')
+ str++,size--;
+ mp->zeros = 0;
+ cp = str;
+ }
+ if(mp->sp)
+ sfwrite(mp->sp,str,size);
+ else if(mp->pattern>=2 || (mp->pattern && nopat) || mp->assign==3)
+ {
+ state = sh_lexstates[ST_MACRO];
+ /* insert \ before file expansion characters */
+ while(size-->0)
+ {
+#if SHOPT_MULTIBYTE
+ if(mbwide() && (len=mbsize(cp))>1)
+ {
+ cp += len;
+ size -= (len-1);
+ continue;
+ }
+#endif
+ c = state[n= *(unsigned char*)cp++];
+ if(mp->assign==3 && mp->pattern!=4)
+ {
+ if(c==S_BRACT)
+ {
+ nopat = 0;
+ mp->pattern = 4;
+ }
+ continue;
+ }
+ if(nopat&&(c==S_PAT||c==S_ESC||c==S_BRACT||c==S_ENDCH) && mp->pattern!=3)
+ c=1;
+ else if(mp->pattern==4 && (c==S_ESC||c==S_BRACT||c==S_ENDCH || isastchar(n)))
+ {
+ if(c==S_ENDCH && oldpat!=4)
+ {
+ if(*cp==0 || *cp=='.' || *cp=='[')
+ {
+ mp->pattern = oldpat;
+ c=0;
+ }
+ else
+ c=1;
+ }
+ else
+ c=1;
+ }
+ else if(mp->pattern==2 && c==S_SLASH)
+ c=1;
+ else if(mp->pattern==3 && c==S_ESC && (state[*(unsigned char*)cp]==S_DIG||(*cp==ESCAPE)))
+ {
+ if(!(c=mp->quote))
+ cp++;
+ }
+ else
+ c=0;
+ if(c)
+ {
+ if(c = (cp-1) - str)
+ sfwrite(stkp,str,c);
+ sfputc(stkp,ESCAPE);
+ str = cp-1;
+ }
+ }
+ if(c = cp-str)
+ sfwrite(stkp,str,c);
+ }
+ else if(!mp->quote && mp->split && (mp->ifs||mp->pattern))
+ {
+ /* split words at ifs characters */
+ state = mp->shp->ifstable;
+ if(mp->pattern)
+ {
+ char *sp = "&|()";
+ while(c = *sp++)
+ {
+ if(state[c]==0)
+ state[c] = S_EPAT;
+ }
+ sp = "*?[{";
+ while(c = *sp++)
+ {
+ if(state[c]==0)
+ state[c] = S_PAT;
+ }
+ if(state[ESCAPE]==0)
+ state[ESCAPE] = S_ESC;
+ }
+ while(size-->0)
+ {
+ n=state[c= *(unsigned char*)cp++];
+#if SHOPT_MULTIBYTE
+ if(mbwide() && n!=S_MBYTE && (len=mbsize(cp-1))>1)
+ {
+ sfwrite(stkp,cp-1, len);
+ cp += --len;
+ size -= len;
+ continue;
+ }
+#endif
+ if(n==S_ESC || n==S_EPAT)
+ {
+ /* don't allow extended patterns in this case */
+ mp->patfound = mp->pattern;
+ sfputc(stkp,ESCAPE);
+ }
+ else if(n==S_PAT)
+ mp->patfound = mp->pattern;
+ else if(n && mp->ifs)
+ {
+#if SHOPT_MULTIBYTE
+ if(n==S_MBYTE)
+ {
+ if(sh_strchr(mp->ifsp,cp-1)<0)
+ continue;
+ n = mbsize(cp-1) - 1;
+ if(n==-2)
+ n = 0;
+ cp += n;
+ size -= n;
+ n= S_DELIM;
+ }
+#endif /* SHOPT_MULTIBYTE */
+ if(n==S_SPACE || n==S_NL)
+ {
+ while(size>0 && ((n=state[c= *(unsigned char*)cp++])==S_SPACE||n==S_NL))
+ size--;
+#if SHOPT_MULTIBYTE
+ if(n==S_MBYTE && sh_strchr(mp->ifsp,cp-1)>=0)
+ {
+ n = mbsize(cp-1) - 1;
+ if(n==-2)
+ n = 0;
+ cp += n;
+ size -= n;
+ n=S_DELIM;
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ if(n==S_DELIM)
+ size--;
+ }
+ endfield(mp,n==S_DELIM||mp->quoted);
+ mp->patfound = 0;
+ if(n==S_DELIM)
+ while(size>0 && ((n=state[c= *(unsigned char*)cp++])==S_SPACE||n==S_NL))
+ size--;
+ if(size<=0)
+ break;
+ cp--;
+ continue;
+
+ }
+ sfputc(stkp,c);
+ }
+ if(mp->pattern)
+ {
+ cp = "&|()";
+ while(c = *cp++)
+ {
+ if(state[c]==S_EPAT)
+ state[c] = 0;
+ }
+ cp = "*?[{";
+ while(c = *cp++)
+ {
+ if(state[c]==S_PAT)
+ state[c] = 0;
+ }
+ if(mp->shp->ifstable[ESCAPE]==S_ESC)
+ mp->shp->ifstable[ESCAPE] = 0;
+ }
+ }
+ else
+ sfwrite(stkp,str,size);
+}
+
+/*
+ * Terminate field.
+ * If field is null count field if <split> is non-zero
+ * Do filename expansion of required
+ */
+static void endfield(register Mac_t *mp,int split)
+{
+ register struct argnod *argp;
+ register int count=0;
+ Stk_t *stkp = mp->shp->stk;
+ if(stktell(stkp) > ARGVAL || split)
+ {
+ argp = (struct argnod*)stkfreeze(stkp,1);
+ argp->argnxt.cp = 0;
+ argp->argflag = 0;
+ mp->atmode = 0;
+ if(mp->patfound)
+ {
+ mp->shp->argaddr = 0;
+#if SHOPT_BRACEPAT
+ count = path_generate(mp->shp,argp,mp->arghead);
+#else
+ count = path_expand(mp->shp,argp->argval,mp->arghead);
+#endif /* SHOPT_BRACEPAT */
+ if(count)
+ mp->fields += count;
+ else if(split) /* pattern is null string */
+ *argp->argval = 0;
+ else /* pattern expands to nothing */
+ count = -1;
+ }
+ if(count==0)
+ {
+ argp->argchn.ap = *mp->arghead;
+ *mp->arghead = argp;
+ mp->fields++;
+ }
+ if(count>=0)
+ {
+ (*mp->arghead)->argflag |= ARG_MAKE;
+ if(mp->assign || sh_isoption(SH_NOGLOB))
+ argp->argflag |= ARG_RAW|ARG_EXP;
+ }
+ stkseek(stkp,ARGVAL);
+ }
+ mp->quoted = mp->quote;
+}
+
+/*
+ * Finds the right substring of STRING using the expression PAT
+ * the longest substring is found when FLAG is set.
+ */
+static int substring(register const char *string,const char *pat,int match[], int flag)
+{
+ register const char *sp=string;
+ register int size,len,nmatch,n;
+ int smatch[2*(MATCH_MAX+1)];
+ if(flag)
+ {
+ if(n=strgrpmatch(sp,pat,smatch,elementsof(smatch)/2,STR_RIGHT|STR_MAXIMAL))
+ {
+ memcpy(match,smatch,n*2*sizeof(smatch[0]));
+ return(n);
+ }
+ return(0);
+ }
+ size = len = strlen(sp);
+ sp += size;
+ while(sp>=string)
+ {
+#if SHOPT_MULTIBYTE
+ if(mbwide())
+ sp = lastchar(string,sp);
+#endif /* SHOPT_MULTIBYTE */
+ if(n=strgrpmatch(sp,pat,smatch,elementsof(smatch)/2,STR_RIGHT|STR_LEFT|STR_MAXIMAL))
+ {
+ nmatch = n;
+ memcpy(match,smatch,n*2*sizeof(smatch[0]));
+ size = sp-string;
+ break;
+ }
+ sp--;
+ }
+ if(size==len)
+ return(0);
+ if(nmatch)
+ {
+ nmatch *=2;
+ while(--nmatch>=0)
+ match[nmatch] += size;
+ }
+ return(n);
+}
+
+#if SHOPT_MULTIBYTE
+ static char *lastchar(const char *string, const char *endstring)
+ {
+ register char *str = (char*)string;
+ register int c;
+ mbinit();
+ while(*str)
+ {
+ if((c=mbsize(str))<0)
+ c = 1;
+ if(str+c > endstring)
+ break;
+ str += c;
+ }
+ return(str);
+ }
+#endif /* SHOPT_MULTIBYTE */
+static int charlen(const char *string,int len)
+{
+ if(!string)
+ return(0);
+#if SHOPT_MULTIBYTE
+ if(mbwide())
+ {
+ register const char *str = string, *strmax=string+len;
+ register int n=0;
+ mbinit();
+ if(len>0)
+ {
+ while(str<strmax && mbchar(str))
+ n++;
+ }
+ else while(mbchar(str))
+ n++;
+ return(n);
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ {
+ if(len<0)
+ return(strlen(string));
+ return(len);
+ }
+}
+
+/*
+ * This is the default tilde discipline function
+ */
+static int sh_btilde(int argc, char *argv[], Shbltin_t *context)
+{
+ Shell_t *shp = context->shp;
+ char *cp = sh_tilde(shp,argv[1]);
+ NOT_USED(argc);
+ if(!cp)
+ cp = argv[1];
+ sfputr(sfstdout, cp, '\n');
+ return(0);
+}
+
+/*
+ * <offset> is byte offset for beginning of tilde string
+ */
+static void tilde_expand2(Shell_t *shp, register int offset)
+{
+ char shtilde[10], *av[3], *ptr=stkfreeze(shp->stk,1);
+ Sfio_t *iop, *save=sfstdout;
+ Namval_t *np;
+ static int beenhere=0;
+ strcpy(shtilde,".sh.tilde");
+ np = nv_open(shtilde,shp->fun_tree, NV_VARNAME|NV_NOARRAY|NV_NOASSIGN|NV_NOFAIL);
+ if(np && !beenhere)
+ {
+ beenhere = 1;
+ sh_addbuiltin(shtilde,sh_btilde,0);
+ nv_onattr(np,NV_EXPORT);
+ }
+ av[0] = ".sh.tilde";
+ av[1] = &ptr[offset];
+ av[2] = 0;
+ iop = sftmp((IOBSIZE>PATH_MAX?IOBSIZE:PATH_MAX)+1);
+ sfset(iop,SF_READ,0);
+ sfstdout = iop;
+ if(np)
+ sh_fun(np, (Namval_t*)0, av);
+ else
+ sh_btilde(2, av, &shp->bltindata);
+ sfstdout = save;
+ stkset(shp->stk,ptr, offset);
+ sfseek(iop,(Sfoff_t)0,SEEK_SET);
+ sfset(iop,SF_READ,1);
+ if(ptr = sfreserve(iop, SF_UNBOUND, -1))
+ {
+ Sfoff_t n = sfvalue(iop);
+ while(ptr[n-1]=='\n')
+ n--;
+ if(n==1 && fcpeek(0)=='/' && ptr[n-1])
+ n--;
+ if(n)
+ sfwrite(shp->stk,ptr,n);
+ }
+ else
+ sfputr(shp->stk,av[1],0);
+ sfclose(iop);
+}
+
+/*
+ * This routine is used to resolve ~ expansion.
+ * A ~ by itself is replaced with the users login directory.
+ * A ~- is replaced by the previous working directory in shell.
+ * A ~+ is replaced by the present working directory in shell.
+ * If ~name is replaced with login directory of name.
+ * If string doesn't start with ~ or ~... not found then 0 returned.
+ */
+
+static char *sh_tilde(Shell_t *shp,register const char *string)
+{
+ register char *cp;
+ register int c;
+ register struct passwd *pw;
+ register Namval_t *np=0;
+ static Dt_t *logins_tree;
+ if(*string++!='~')
+ return(NIL(char*));
+ if((c = *string)==0)
+ {
+ if(!(cp=nv_getval(sh_scoped(shp,HOME))))
+ cp = getlogin();
+ return(cp);
+ }
+ if((c=='-' || c=='+') && string[1]==0)
+ {
+ if(c=='+')
+ cp = nv_getval(sh_scoped(shp,PWDNOD));
+ else
+ cp = nv_getval(sh_scoped(shp,OLDPWDNOD));
+ return(cp);
+ }
+#if _WINIX
+ if(fcgetc(c)=='/')
+ {
+ char *str;
+ int n=0,offset=staktell();
+ stakputs(string);
+ do
+ {
+ stakputc(c);
+ n++;
+ }
+ while (fcgetc(c) && c!='/');
+ stakputc(0);
+ if(c)
+ fcseek(-1);
+ str = stakseek(offset);
+ Skip = n;
+ if(logins_tree && (np=nv_search(str,logins_tree,0)))
+ return(nv_getval(np));
+ if(pw = getpwnam(str))
+ {
+ string = str;
+ goto skip;
+ }
+ Skip = 0;
+ }
+#endif /* _WINIX */
+ if(logins_tree && (np=nv_search(string,logins_tree,0)))
+ return(nv_getval(np));
+ if(!(pw = getpwnam(string)))
+ return(NIL(char*));
+#if _WINIX
+skip:
+#endif /* _WINIX */
+ if(!logins_tree)
+ logins_tree = dtopen(&_Nvdisc,Dtbag);
+ if(np=nv_search(string,logins_tree,NV_ADD))
+ {
+ c = shp->subshell;
+ shp->subshell = 0;
+ nv_putval(np, pw->pw_dir,0);
+ shp->subshell = c;
+ }
+ return(pw->pw_dir);
+}
+
+/*
+ * return values for special macros
+ */
+static char *special(Shell_t *shp,register int c)
+{
+ if(c!='$')
+ shp->argaddr = 0;
+ switch(c)
+ {
+ case '@':
+ case '*':
+ return(shp->st.dolc>0?shp->st.dolv[1]:NIL(char*));
+ case '#':
+#if SHOPT_FILESCAN
+ if(shp->cur_line)
+ {
+ getdolarg(shp,MAX_ARGN,(int*)0);
+ return(ltos(shp->offsets[0]));
+ }
+#endif /* SHOPT_FILESCAN */
+ return(ltos(shp->st.dolc));
+ case '!':
+ if(shp->bckpid)
+#if SHOPT_COSHELL
+ return(sh_pid2str(shp,shp->bckpid));
+#else
+ return(ltos(shp->bckpid));
+#endif /* SHOPT_COSHELL */
+ break;
+ case '$':
+ if(nv_isnull(SH_DOLLARNOD))
+ return(ltos(shp->gd->pid));
+ return(nv_getval(SH_DOLLARNOD));
+ case '-':
+ return(sh_argdolminus(shp->arg_context));
+ case '?':
+ return(ltos(shp->savexit));
+ case 0:
+ if(sh_isstate(SH_PROFILE) || shp->fn_depth==0 || !shp->st.cmdname)
+ return(shp->shname);
+ else
+ return(shp->st.cmdname);
+ }
+ return(NIL(char*));
+}
+
+/*
+ * Handle macro expansion errors
+ */
+static void mac_error(Namval_t *np)
+{
+ if(np)
+ nv_close(np);
+ errormsg(SH_DICT,ERROR_exit(1),e_subst,fcfirst());
+}
+
+/*
+ * Given pattern/string, replace / with 0 and return pointer to string
+ * \ characters are stripped from string. The \ are stripped in the
+ * replacement string unless followed by a digit or \.
+ */
+static char *mac_getstring(char *pattern)
+{
+ register char *cp=pattern, *rep=0, *dp;
+ register int c;
+ while(c = *cp++)
+ {
+ if(c==ESCAPE && (!rep || (*cp && strchr("&|()[]*?",*cp))))
+ {
+ c = *cp++;
+ }
+ else if(!rep && c=='/')
+ {
+ cp[-1] = 0;
+ rep = dp = cp;
+ continue;
+ }
+ if(rep)
+ *dp++ = c;
+ }
+ if(rep)
+ *dp = 0;
+ return(rep);
+}