mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
List of changes: - Fixed some -Wuninitialized warnings and removed some unused variables. - Removed the unused extern for B_login (re:d8eba9d1
). - The libcmd builtins and the vmalloc memfatal function now handle memory errors with 'ERROR_SYSTEM|ERROR_PANIC' for consistency with how ksh itself handles out of memory errors. - Added usage of UNREACHABLE() where it was missing from error handling. - Extend many variables from short to int to prevent overflows (most variables involve file descriptors). - Backported a ksh2020 patch to fix unused value Coverity issues (https://github.com/att/ast/pull/740). - Note in src/cmd/ksh93/README that ksh compiles with Cygwin on Windows 10 and Windows 11, albeit with many test failures. - Add comments to detail some sections of code. Extensive list of commits related to this change:ca2443b5
,7e7f1372
,2db9953a
,7003aba4
,6f50ff64
,b1a41311
,222515bf
,a0dcdeea
,0aa9e03f
,61437b27
,352e68da
,88e8fa67
,bc8b36fa
,6e515f1d
,017d088c
,035a4cb3
,588a1ff7
,6d63b57d
,a2f13c19
,794d1c86
,ab98ec65
,1026006d
- Removed a lot of dead ifdef code. - edit/emacs.c: Hide an assignment to avoid a -Wunused warning. (See also https://github.com/att/ast/pull/753, which removed the assignment because ksh2020 removed the !SHOPT_MULTIBYTE code.) - sh/nvdisc.c: The sh_newof macro cannot return a null pointer because it will instead cause the shell to exit if memory cannot be allocated. That makes the if statement here a no-op, so remove it. - sh/xec.c: Fixed one unused variable warning in sh_funscope(). - sh/xec.c: Remove a fallthrough comment added in commited478ab7
because the TFORK code doesn't fall through (GCC also produces no -Wimplicit-fallthrough warning here). - data/builtins.c: The cd and pwd man pages state that these builtins default to -P if PATH_RESOLVE is 'physical', which isn't accurate: $ /opt/ast/bin/getconf PATH_RESOLVE physical $ mkdir /tmp/dir; ln -s /tmp/dir /tmp/sym $ cd /tmp/sym $ pwd /tmp/sym $ cd -P /tmp/sym $ pwd /tmp/dir The behavior described by these man pages isn't specified in the ksh man page or by POSIX, so to avoid changing these builtin's behavior the inaccurate PATH_RESOLVE information has been removed. - Mamfiles: Preserve multi-line errors by quoting the $x variable. This fix was backported from 93v-. (See also <a7e9cc82
>.) - sh/subshell.c: Remove set but not used sp->errcontext variable.
603 lines
13 KiB
C
603 lines
13 KiB
C
/***********************************************************************
|
|
* *
|
|
* This software is part of the ast package *
|
|
* Copyright (c) 1982-2011 AT&T Intellectual Property *
|
|
* Copyright (c) 2020-2021 Contributors to ksh 93u+m *
|
|
* 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
|
|
/*
|
|
* David Korn
|
|
* AT&T Labs
|
|
*
|
|
* shell deparser
|
|
*
|
|
*/
|
|
|
|
#include "defs.h"
|
|
#include "shnodes.h"
|
|
#include "test.h"
|
|
|
|
|
|
#define HUGE_INT (((unsigned)-1)>>1)
|
|
#define BEGIN 0
|
|
#define MIDDLE 1
|
|
#define END 2
|
|
#define PRE 1
|
|
#define POST 2
|
|
|
|
|
|
/* flags that can be specified with p_tree() */
|
|
#define NO_NEWLINE (1 << 0)
|
|
#define NEED_BRACE (1 << 1)
|
|
#define NO_BRACKET (1 << 2)
|
|
#define PROCSUBST (1 << 3)
|
|
|
|
static void p_comlist(const struct dolnod*,int);
|
|
static void p_arg(const struct argnod*, int endchar, int opts);
|
|
static void p_comarg(const struct comnod*);
|
|
static void p_keyword(const char*,int);
|
|
static void p_redirect(const struct ionod*);
|
|
static void p_switch(const struct regnod*);
|
|
static void here_body(const struct ionod*);
|
|
static void p_tree(const Shnode_t*,int);
|
|
|
|
static int level;
|
|
static int begin_line;
|
|
static int end_line;
|
|
static char io_op[7];
|
|
static char un_op[3] = "-?";
|
|
static const struct ionod *here_doc;
|
|
static Sfio_t *outfile;
|
|
static const char *forinit = "";
|
|
|
|
void sh_deparse(Sfio_t *out, const Shnode_t *t,int tflags)
|
|
{
|
|
outfile = out;
|
|
p_tree(t,tflags);
|
|
}
|
|
/*
|
|
* print script corresponding to shell tree <t>
|
|
*/
|
|
static void p_tree(register const Shnode_t *t,register int tflags)
|
|
{
|
|
register char *cp=0;
|
|
int save = end_line;
|
|
int needbrace = (tflags&NEED_BRACE);
|
|
int procsub = (tflags&PROCSUBST);
|
|
tflags &= ~NEED_BRACE;
|
|
if(tflags&NO_NEWLINE)
|
|
end_line = ' ';
|
|
else if(procsub)
|
|
end_line = ')';
|
|
else
|
|
end_line = '\n';
|
|
switch(t->tre.tretyp&COMMSK)
|
|
{
|
|
case TTIME:
|
|
if(t->tre.tretyp&COMSCAN)
|
|
p_keyword("!",BEGIN);
|
|
else
|
|
p_keyword("time",BEGIN);
|
|
if(t->par.partre)
|
|
p_tree(t->par.partre,tflags);
|
|
level--;
|
|
break;
|
|
|
|
case TCOM:
|
|
if(begin_line && level>0)
|
|
sfnputc(outfile,'\t',level);
|
|
begin_line = 0;
|
|
p_comarg((struct comnod*)t);
|
|
break;
|
|
|
|
case TSETIO:
|
|
if(t->tre.tretyp&FPCL)
|
|
tflags |= NEED_BRACE;
|
|
else
|
|
tflags = NO_NEWLINE|NEED_BRACE;
|
|
p_tree(t->fork.forktre,tflags);
|
|
p_redirect(t->fork.forkio);
|
|
break;
|
|
|
|
case TFORK:
|
|
if(needbrace)
|
|
tflags |= NEED_BRACE;
|
|
if((t->tre.tretyp&(FAMP|FCOOP)) && (t->tre.tretyp&(FAMP|FINT))!=FAMP)
|
|
{
|
|
tflags = NEED_BRACE|NO_NEWLINE;
|
|
end_line = ' ';
|
|
}
|
|
else if(t->fork.forkio)
|
|
tflags = NO_NEWLINE;
|
|
p_tree(t->fork.forktre,tflags);
|
|
if(t->fork.forkio)
|
|
p_redirect(t->fork.forkio);
|
|
if(t->tre.tretyp&FCOOP)
|
|
{
|
|
sfputr(outfile,"|&",procsub?')':'\n');
|
|
begin_line = 1;
|
|
}
|
|
else if((t->tre.tretyp&(FAMP|FINT))==(FAMP|FINT))
|
|
{
|
|
sfputr(outfile,"&",procsub?')':'\n');
|
|
begin_line = 1;
|
|
}
|
|
break;
|
|
|
|
case TIF:
|
|
p_keyword("if",BEGIN);
|
|
p_tree(t->if_.iftre,0);
|
|
p_keyword("then",MIDDLE);
|
|
p_tree(t->if_.thtre,0);
|
|
if(t->if_.eltre)
|
|
{
|
|
p_keyword("else",MIDDLE);
|
|
p_tree(t->if_.eltre,0);
|
|
}
|
|
p_keyword("fi",END);
|
|
break;
|
|
|
|
case TWH:
|
|
if(t->wh.whinc)
|
|
cp = "for";
|
|
else if(t->tre.tretyp&COMSCAN)
|
|
cp = "until";
|
|
else
|
|
cp = "while";
|
|
p_keyword(cp,BEGIN);
|
|
if(t->wh.whinc)
|
|
{
|
|
struct argnod *arg = (t->wh.whtre)->ar.arexpr;
|
|
sfprintf(outfile,"(( %s; ",forinit);
|
|
forinit = "";
|
|
sfputr(outfile,arg->argval,';');
|
|
arg = (t->wh.whinc)->arexpr;
|
|
sfprintf(outfile," %s))\n",arg->argval);
|
|
}
|
|
else
|
|
p_tree(t->wh.whtre,0);
|
|
t = t->wh.dotre;
|
|
goto dolist;
|
|
|
|
case TLST:
|
|
{
|
|
Shnode_t *tr = t->lst.lstrit;
|
|
if(tr->tre.tretyp==TWH && tr->wh.whinc && t->lst.lstlef->tre.tretyp==TARITH)
|
|
{
|
|
/* arithmetic for statement */
|
|
struct argnod *init = (t->lst.lstlef)->ar.arexpr;
|
|
forinit= init->argval;
|
|
p_tree(t->lst.lstrit,tflags);
|
|
break;
|
|
}
|
|
if(needbrace)
|
|
p_keyword("{",BEGIN);
|
|
p_tree(t->lst.lstlef,0);
|
|
if(needbrace)
|
|
tflags = 0;
|
|
p_tree(t->lst.lstrit,tflags);
|
|
if(needbrace)
|
|
p_keyword("}",END);
|
|
break;
|
|
}
|
|
|
|
case TAND:
|
|
cp = "&&";
|
|
goto andor;
|
|
case TORF:
|
|
cp = "||";
|
|
goto andor;
|
|
case TFIL:
|
|
cp = "|";
|
|
andor:
|
|
{
|
|
int bracket = 0;
|
|
if(t->tre.tretyp&TTEST)
|
|
{
|
|
tflags |= NO_NEWLINE;
|
|
if(!(tflags&NO_BRACKET))
|
|
{
|
|
p_keyword("[[",BEGIN);
|
|
tflags |= NO_BRACKET;
|
|
bracket=1;
|
|
}
|
|
}
|
|
p_tree(t->lst.lstlef,NEED_BRACE|NO_NEWLINE|(tflags&NO_BRACKET));
|
|
if(tflags&FALTPIPE)
|
|
{
|
|
Shnode_t *tt = t->lst.lstrit;
|
|
if(tt->tre.tretyp!=TFIL || !(tt->lst.lstlef->tre.tretyp&FALTPIPE))
|
|
{
|
|
sfputc(outfile,'\n');
|
|
return;
|
|
}
|
|
}
|
|
sfputr(outfile,cp,here_doc?'\n':' ');
|
|
if(here_doc)
|
|
{
|
|
here_body(here_doc);
|
|
here_doc = 0;
|
|
}
|
|
level++;
|
|
p_tree(t->lst.lstrit,tflags|NEED_BRACE);
|
|
if(bracket)
|
|
p_keyword("]]",END);
|
|
level--;
|
|
break;
|
|
}
|
|
|
|
case TPAR:
|
|
p_keyword("(",BEGIN);
|
|
p_tree(t->par.partre,0);
|
|
p_keyword(")",END);
|
|
break;
|
|
|
|
case TARITH:
|
|
{
|
|
register struct argnod *ap = t->ar.arexpr;
|
|
if(begin_line && level)
|
|
sfnputc(outfile,'\t',level);
|
|
sfprintf(outfile,"(( %s ))%c",ap->argval,end_line);
|
|
if(!(tflags&NO_NEWLINE))
|
|
begin_line=1;
|
|
break;
|
|
}
|
|
|
|
case TFOR:
|
|
cp = ((t->tre.tretyp&COMSCAN)?"select":"for");
|
|
p_keyword(cp,BEGIN);
|
|
sfputr(outfile,t->for_.fornam,' ');
|
|
if(t->for_.forlst)
|
|
{
|
|
sfputr(outfile,"in",' ');
|
|
tflags = end_line;
|
|
end_line = '\n';
|
|
p_comarg(t->for_.forlst);
|
|
end_line = tflags;
|
|
}
|
|
else
|
|
sfputc(outfile,'\n');
|
|
begin_line = 1;
|
|
t = t->for_.fortre;
|
|
dolist:
|
|
p_keyword("do",MIDDLE);
|
|
p_tree(t,0);
|
|
p_keyword("done",END);
|
|
break;
|
|
|
|
case TSW:
|
|
p_keyword("case",BEGIN);
|
|
p_arg(t->sw.swarg,' ',0);
|
|
if(t->sw.swlst)
|
|
{
|
|
begin_line = 1;
|
|
sfputr(outfile,"in",'\n');
|
|
tflags = end_line;
|
|
end_line = '\n';
|
|
p_switch(t->sw.swlst);
|
|
end_line = tflags;
|
|
}
|
|
p_keyword("esac",END);
|
|
break;
|
|
|
|
case TFUN:
|
|
if(t->tre.tretyp&FPOSIX)
|
|
{
|
|
sfprintf(outfile,"%s",t->funct.functnam);
|
|
p_keyword("()\n",BEGIN);
|
|
}
|
|
else
|
|
{
|
|
p_keyword("function",BEGIN);
|
|
tflags = (t->funct.functargs?' ':'\n');
|
|
sfputr(outfile,t->funct.functnam,tflags);
|
|
if(t->funct.functargs)
|
|
{
|
|
tflags = end_line;
|
|
end_line = '\n';
|
|
p_comarg(t->funct.functargs);
|
|
end_line = tflags;
|
|
}
|
|
}
|
|
begin_line = 1;
|
|
p_keyword("{\n",MIDDLE);
|
|
begin_line = 1;
|
|
p_tree(t->funct.functtre,0);
|
|
p_keyword("}",END);
|
|
break;
|
|
/* new test compound command */
|
|
case TTST:
|
|
if(!(tflags&NO_BRACKET))
|
|
p_keyword("[[",BEGIN);
|
|
if((t->tre.tretyp&TPAREN)==TPAREN)
|
|
{
|
|
p_keyword("(",BEGIN);
|
|
p_tree(t->lst.lstlef,NO_BRACKET|NO_NEWLINE);
|
|
p_keyword(")",END);
|
|
}
|
|
else
|
|
{
|
|
int flags = (t->tre.tretyp)>>TSHIFT;
|
|
if(t->tre.tretyp&TNEGATE)
|
|
sfputr(outfile,"!",' ');
|
|
if(t->tre.tretyp&TUNARY)
|
|
{
|
|
un_op[1] = flags;
|
|
sfputr(outfile,un_op,' ');
|
|
}
|
|
else
|
|
cp = ((char*)(shtab_testops+(flags&037)-1)->sh_name);
|
|
p_arg(&(t->lst.lstlef->arg),' ',0);
|
|
if(t->tre.tretyp&TBINARY)
|
|
{
|
|
sfputr(outfile,cp,' ');
|
|
p_arg(&(t->lst.lstrit->arg),' ',0);
|
|
}
|
|
}
|
|
if(!(tflags&NO_BRACKET))
|
|
p_keyword("]]",END);
|
|
}
|
|
while(begin_line && here_doc)
|
|
{
|
|
here_body(here_doc);
|
|
here_doc = 0;
|
|
}
|
|
end_line = save;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* print a keyword
|
|
* increment indent level for flag==BEGIN
|
|
* decrement indent level for flag==END
|
|
*/
|
|
static void p_keyword(const char *word,int flag)
|
|
{
|
|
register int sep;
|
|
if(flag==END)
|
|
sep = end_line;
|
|
else if(*word=='[' || *word=='(')
|
|
sep = ' ';
|
|
else
|
|
sep = '\t';
|
|
if(flag!=BEGIN)
|
|
level--;
|
|
if(begin_line && level)
|
|
sfnputc(outfile,'\t',level);
|
|
sfputr(outfile,word,sep);
|
|
if(sep=='\n')
|
|
begin_line=1;
|
|
else
|
|
begin_line=0;
|
|
if(flag!=END)
|
|
level++;
|
|
}
|
|
|
|
static void p_arg(register const struct argnod *arg,register int endchar,int opts)
|
|
{
|
|
register const char *cp;
|
|
register int flag=0;
|
|
do
|
|
{
|
|
if(!arg->argnxt.ap)
|
|
flag = endchar;
|
|
else if(opts&PRE)
|
|
{
|
|
/* case alternation lists in reverse order */
|
|
p_arg(arg->argnxt.ap,'|',opts);
|
|
flag = endchar;
|
|
}
|
|
else if(opts)
|
|
flag = ' ';
|
|
cp = arg->argval;
|
|
if(*cp==0 && (arg->argflag&ARG_EXP) && arg->argchn.ap)
|
|
{
|
|
/* process substitution */
|
|
int c = (arg->argflag&ARG_RAW)?'>':'<';
|
|
sfputc(outfile,c);
|
|
sfputc(outfile,'(');
|
|
p_tree((Shnode_t*)arg->argchn.ap,PROCSUBST);
|
|
}
|
|
else if(*cp==0 && opts==POST && arg->argchn.ap)
|
|
{
|
|
/* compound assignment */
|
|
struct fornod *fp=(struct fornod*)arg->argchn.ap;
|
|
sfprintf(outfile,"%s=(\n",fp->fornam);
|
|
sfnputc(outfile,'\t',++level);
|
|
p_tree(fp->fortre,0);
|
|
if(--level)
|
|
sfnputc(outfile,'\t',level);
|
|
sfputc(outfile,')');
|
|
}
|
|
else if((arg->argflag&ARG_RAW) && (cp[1] || (*cp!='[' && *cp!=']')))
|
|
cp = sh_fmtq(cp);
|
|
sfputr(outfile,cp,flag);
|
|
if(flag=='\n')
|
|
begin_line = 1;
|
|
arg = arg->argnxt.ap;
|
|
}
|
|
while((opts&POST) && arg);
|
|
return;
|
|
}
|
|
|
|
static void p_redirect(register const struct ionod *iop)
|
|
{
|
|
register char *cp;
|
|
register int iof,iof2;
|
|
for(;iop;iop=iop->ionxt)
|
|
{
|
|
iof=iop->iofile;
|
|
cp = io_op;
|
|
if(iop->iovname)
|
|
{
|
|
sfwrite(outfile,"(;",2);
|
|
sfputr(outfile,iop->iovname,')');
|
|
cp++;
|
|
}
|
|
else
|
|
*cp = '0'+(iof&IOUFD);
|
|
if(iof&IOPUT)
|
|
{
|
|
if(*cp == '1' && !iop->iovname)
|
|
cp++;
|
|
io_op[1] = '>';
|
|
}
|
|
else
|
|
{
|
|
if(*cp == '0' && !iop->iovname)
|
|
cp++;
|
|
io_op[1] = '<';
|
|
}
|
|
io_op[2] = 0;
|
|
io_op[3] = 0;
|
|
if(iof&IOLSEEK)
|
|
{
|
|
io_op[1] = '#';
|
|
if(iof&IOARITH)
|
|
strcpy(&io_op[3]," ((");
|
|
}
|
|
else if(iof&IOMOV)
|
|
io_op[2] = '&';
|
|
else if(iof&(IORDW|IOAPP))
|
|
io_op[2] = '>';
|
|
else if(iof&IOCLOB)
|
|
io_op[2] = '|';
|
|
if(iop->iodelim)
|
|
{
|
|
/* here document */
|
|
here_doc = iop;
|
|
io_op[2] = '<';
|
|
}
|
|
sfputr(outfile,cp,' ');
|
|
if(iop->ionxt)
|
|
iof = ' ';
|
|
else
|
|
{
|
|
if((iof=end_line)=='\n')
|
|
begin_line = 1;
|
|
}
|
|
if((iof&IOLSEEK) && (iof&IOARITH))
|
|
iof2 = iof, iof = ' ';
|
|
if((iop->iofile & IOPROCSUB) && !(iop->iofile & IOLSEEK))
|
|
{
|
|
/* process substitution as argument to redirection */
|
|
if(iop->iofile & IOPUT)
|
|
sfwrite(outfile,">(",2);
|
|
else
|
|
sfwrite(outfile,"<(",2);
|
|
p_tree((Shnode_t*)iop->ioname,PROCSUBST);
|
|
sfputc(outfile,iof);
|
|
}
|
|
else if(iop->iodelim)
|
|
{
|
|
if(!(iop->iofile&IODOC))
|
|
sfwrite(outfile,"''",2);
|
|
sfputr(outfile,sh_fmtq(iop->iodelim),iof);
|
|
}
|
|
else if(iop->iofile&IORAW)
|
|
sfputr(outfile,sh_fmtq(iop->ioname),iof);
|
|
else
|
|
sfputr(outfile,iop->ioname,iof);
|
|
if((iof&IOLSEEK) && (iof&IOARITH))
|
|
sfputr(outfile, "))", iof2);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void p_comarg(register const struct comnod *com)
|
|
{
|
|
register int flag = end_line;
|
|
if(com->comtyp&FAMP)
|
|
sfwrite(outfile,"& ",2);
|
|
if(com->comarg || com->comio)
|
|
flag = ' ';
|
|
if(com->comset)
|
|
p_arg(com->comset,flag,POST);
|
|
if(com->comarg)
|
|
{
|
|
if(!com->comio)
|
|
flag = end_line;
|
|
if(com->comtyp&COMSCAN)
|
|
p_arg(com->comarg,flag,POST);
|
|
else
|
|
p_comlist((struct dolnod*)com->comarg,flag);
|
|
}
|
|
if(com->comio)
|
|
p_redirect(com->comio);
|
|
return;
|
|
}
|
|
|
|
static void p_comlist(const struct dolnod *dol,int endchar)
|
|
{
|
|
register char *cp, *const*argv;
|
|
register int flag = ' ', special;
|
|
argv = dol->dolval+ARG_SPARE;
|
|
cp = *argv;
|
|
special = (*cp=='[' && cp[1]==0);
|
|
do
|
|
{
|
|
if(cp)
|
|
argv++;
|
|
else
|
|
cp = "";
|
|
if(*argv==0)
|
|
{
|
|
if((flag=endchar)=='\n')
|
|
begin_line = 1;
|
|
special = (*cp==']' && cp[1]==0);
|
|
}
|
|
sfputr(outfile,special?cp:sh_fmtq(cp),flag);
|
|
special = 0;
|
|
}
|
|
while(cp = *argv);
|
|
return;
|
|
}
|
|
|
|
static void p_switch(register const struct regnod *reg)
|
|
{
|
|
if(level>1)
|
|
sfnputc(outfile,'\t',level-1);
|
|
p_arg(reg->regptr,')',PRE);
|
|
begin_line = 0;
|
|
sfputc(outfile,'\t');
|
|
if(reg->regcom)
|
|
p_tree(reg->regcom,0);
|
|
level++;
|
|
if(reg->regflag)
|
|
p_keyword(";&",END);
|
|
else
|
|
p_keyword(";;",END);
|
|
if(reg->regnxt)
|
|
p_switch(reg->regnxt);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* output here documents
|
|
*/
|
|
static void here_body(register const struct ionod *iop)
|
|
{
|
|
Sfio_t *infile;
|
|
if(iop->iofile&IOSTRG)
|
|
infile = sfnew((Sfio_t*)0,iop->ioname,iop->iosize,-1,SF_STRING|SF_READ);
|
|
else
|
|
sfseek(infile=sh.heredocs,iop->iooffset,SEEK_SET);
|
|
sfmove(infile,outfile,iop->iosize,-1);
|
|
if(iop->iofile&IOSTRG)
|
|
sfclose(infile);
|
|
sfputr(outfile,iop->iodelim,'\n');
|
|
}
|