mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-02-24 06:54:13 +00:00
${var:-wor)d} or ${var+w(ord}. The parentheses now correctly lose their normal grammatical meaning within the braces. Fix by Eric Scrivner (@etscrivner) from July 2018 backported from ksh2020. This fix complies with POSIX: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02 src/cmd/ksh93/sh/lex.c: sh_lex(): - Set the ST_QUOTE state when analysing a modifier with parameter expansions using operators ':', '-', '+', '='. This state causes subsequent characters (including parentheses) to be considered quoted, suppressing their normal grammatical meaning. src/cmd/ksh93/sh/macro.c: varsub(): - Same for skipping the expansion. Fixes: https://github.com/ksh93/ksh/issues/126 Prior discussion: https://github.com/att/ast/issues/475
2838 lines
59 KiB
C
2838 lines
59 KiB
C
/***********************************************************************
|
|
* *
|
|
* 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 <regex.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 _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 byte of IFS */
|
|
char atmode; /* when processing $@ */
|
|
char quote; /* set within double quoted contexts */
|
|
char lit; /* set within single quotes */
|
|
char split; /* set when word splitting 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*);
|
|
#else
|
|
# define lastchar(string,endstring) (endstring)
|
|
#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, then 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(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
|
|
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(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
|
|
{
|
|
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(mbwide() && c > UCHAR_MAX)
|
|
{
|
|
int i;
|
|
unsigned char mb[8];
|
|
|
|
n = mbconv((char*)mb, c);
|
|
for(i=0;i<n;i++)
|
|
sfputc(stkp,mb[i]);
|
|
}
|
|
else
|
|
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 && mp->pattern && brace)
|
|
{
|
|
brace--;
|
|
if(*cp==LPAREN && mp->pattern!=2)
|
|
goto pattern;
|
|
continue;
|
|
}
|
|
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++;
|
|
}
|
|
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(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;
|
|
}
|
|
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;
|
|
/* Handle 'set -u'/'set -o nounset' for positional parameters */
|
|
if(!v && sh_isoption(SH_NOUNSET))
|
|
{
|
|
d=fcget();
|
|
fcseek(-1);
|
|
if(!(d && strchr(":+-?=",d)))
|
|
errormsg(SH_DICT,ERROR_exit(1),e_notset,ltos(c));
|
|
}
|
|
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;
|
|
/*
|
|
* Get a node pointer (np) to the parameter, if any.
|
|
*
|
|
* The IFS node always exists, so we must special-case IFS by checking if it has
|
|
* a value; otherwise it always follows the code path for a set parameter, so is
|
|
* not subject to 'set -u', and may test as set even after 'unset -v IFS'.
|
|
*/
|
|
if(!(*id=='I' && strcmp(id,"IFS")==0 && nv_getval(sh_scoped(mp->shp,IFSNOD)) == NULL))
|
|
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));
|
|
/*
|
|
* Check if the parameter is set or unset.
|
|
*/
|
|
if(np && (type==M_TREE || !c || !ap))
|
|
{
|
|
/* The parameter is set. */
|
|
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
|
|
{
|
|
#if SHOPT_OPTIMIZE
|
|
if(mp->shp->argaddr)
|
|
nv_optimize(np); /* avoid BUG_ISSETLOOP */
|
|
#endif /* SHOPT_OPTIMIZE */
|
|
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->let || (mp->arith&&nv_isattr(np,(NV_LJUST|NV_RJUST|NV_ZFILL)))) && !nv_isattr(np,NV_INTEGER) && (offset==0 || isspace(c) || strchr(",.+-*/=%&|^?!<>",c)))
|
|
mp->zeros = 1;
|
|
}
|
|
if(savptr==stakptr(0))
|
|
stkseek(stkp,offset);
|
|
else
|
|
stkset(stkp,savptr,offset);
|
|
}
|
|
else
|
|
{
|
|
/* The parameter is unset. */
|
|
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
|
|
{
|
|
ap->nelem &= ~ARRAY_SCAN;
|
|
dolg = 0;
|
|
|
|
}
|
|
}
|
|
break;
|
|
case S_EOF:
|
|
fcseek(-1);
|
|
default:
|
|
goto nosub;
|
|
}
|
|
if(type)
|
|
c = fcmbget(&LEN);
|
|
else
|
|
c = fcget();
|
|
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);
|
|
if(c!='=')
|
|
stkseek(stkp,stktell(stkp)-1);
|
|
}
|
|
else
|
|
{
|
|
int state = (!newops && mp->quote || c=='-' || c=='+' || c=='=') ? ST_QUOTE : ST_NESTED;
|
|
sh_lexskip(lp, RBRACE, 0, state);
|
|
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;
|
|
else if(mbwide())
|
|
{
|
|
mbinit();
|
|
for(c=type;c;c--)
|
|
mbchar(v);
|
|
c = ':';
|
|
}
|
|
else
|
|
v += type;
|
|
vsize = v?strlen(v):0;
|
|
}
|
|
if(*ptr==':')
|
|
{
|
|
if((type = (int)sh_strnum(ptr+1,&ptr,1)) <=0)
|
|
{
|
|
v = 0;
|
|
mp->atmode = 0;
|
|
}
|
|
else if(isastchar(mode))
|
|
{
|
|
if(dolg>=0)
|
|
{
|
|
if(dolg+type < dolmax)
|
|
dolmax = dolg+type;
|
|
}
|
|
else
|
|
dolmax = type;
|
|
}
|
|
else if(type < vsize)
|
|
{
|
|
if(mbwide())
|
|
{
|
|
char *vp = v;
|
|
mbinit();
|
|
while(type-->0)
|
|
{
|
|
if((c=mbsize(vp))<1)
|
|
c = 1;
|
|
vp += c;
|
|
}
|
|
type = vp-v;
|
|
c = ':';
|
|
}
|
|
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!='+')
|
|
{
|
|
int ofs_size = 0;
|
|
regoff_t match[2*(MATCH_MAX+1)];
|
|
int 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
|
|
{
|
|
Sfio_t *sfio_ptr = (mp->sp) ? mp->sp : stkp;
|
|
/*
|
|
* We're joining fields into one; write the output field separator, which may be multi-byte.
|
|
* For "$@" it's a space, for "$*" it's the 1st char of IFS (space if unset, none if empty).
|
|
*/
|
|
if(mode == '@' || !mp->ifsp) /* if expanding $@ or if IFS is unset... */
|
|
sfputc(sfio_ptr, ' ');
|
|
else if(mp->ifs) /* else if IFS is non-empty... */
|
|
{
|
|
if(!mbwide() || mp->ifs < 128) /* if single-byte char... */
|
|
sfputc(sfio_ptr, mp->ifs);
|
|
else
|
|
{
|
|
if(!ofs_size) /* only calculate this once per expansion */
|
|
{
|
|
ofs_size = mbsize(mp->ifsp);
|
|
if(ofs_size<0) /* invalid mb char: fall back to using first byte */
|
|
ofs_size = 1;
|
|
}
|
|
sfwrite(sfio_ptr, mp->ifsp, ofs_size);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
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;
|
|
Sfoff_t foff;
|
|
Namval_t *np;
|
|
mp->shp->argaddr = 0;
|
|
savemac = *mp;
|
|
mp->shp->st.staklist=0;
|
|
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;
|
|
}
|
|
if(!(sp=mp->shp->sftable[fd]))
|
|
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);
|
|
if((foff = sfseek(sp,(Sfoff_t)0,SEEK_END)) > 0)
|
|
{
|
|
size_t soff = stktell(stkp);
|
|
sfseek(sp,(Sfoff_t)0,SEEK_SET);
|
|
stkseek(stkp,soff+foff+64);
|
|
stkseek(stkp,soff);
|
|
}
|
|
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)
|
|
{
|
|
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 */
|
|
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)
|
|
{
|
|
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 becoming octal constants */
|
|
while(size>1 && *str=='0')
|
|
{
|
|
if(str[1]=='x' || str[1]=='X')
|
|
break;
|
|
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(mbwide() && (len=mbsize(cp))>1)
|
|
{
|
|
cp += len;
|
|
size -= (len-1);
|
|
continue;
|
|
}
|
|
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(mbwide() && n!=S_MBYTE && (len=mbsize(cp-1))>1)
|
|
{
|
|
sfwrite(stkp,cp-1, len);
|
|
cp += --len;
|
|
size -= len;
|
|
continue;
|
|
}
|
|
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(mbwide() && n==S_MBYTE)
|
|
{
|
|
if(sh_strchr(mp->ifsp,cp-1)<0)
|
|
{
|
|
/*
|
|
* The multi-byte character that was found has the same initial
|
|
* byte as the IFS delimiter, but it's a different character. Put
|
|
* the first byte onto the stack and continue; multi-byte characters
|
|
* otherwise lose their initial byte.
|
|
*/
|
|
sfputc(stkp,c);
|
|
continue;
|
|
}
|
|
n = mbsize(cp-1) - 1;
|
|
if(n==-2)
|
|
n = 0;
|
|
cp += n;
|
|
size -= n;
|
|
n= S_DELIM;
|
|
}
|
|
if(n==S_SPACE || n==S_NL)
|
|
{
|
|
while(size>0 && ((n=state[c= *(unsigned char*)cp++])==S_SPACE||n==S_NL))
|
|
size--;
|
|
if(mbwide() && 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 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 if 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(mbwide())
|
|
sp = lastchar(string,sp);
|
|
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(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
|
|
{
|
|
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;
|
|
unsigned int save;
|
|
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))
|
|
{
|
|
save = shp->subshell;
|
|
shp->subshell = 0;
|
|
nv_putval(np, pw->pw_dir,0);
|
|
shp->subshell = save;
|
|
}
|
|
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)
|
|
return(ltos(shp->bckpid));
|
|
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);
|
|
}
|
|
/* Handle 'set -u'/'set -o nounset' for special parameters */
|
|
if(sh_isoption(SH_NOUNSET))
|
|
{
|
|
int d=fcget();
|
|
fcseek(-1);
|
|
if(!(d && strchr(":+-?=",d)))
|
|
{
|
|
char c_str[2];
|
|
c_str[0]=(char)c;
|
|
c_str[1]='\0';
|
|
errormsg(SH_DICT,ERROR_exit(1),e_notset,c_str);
|
|
}
|
|
}
|
|
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);
|
|
}
|