--- a/examples/unbourne.c +++ b/examples/unbourne.c @@ -59,8 +59,8 @@ for decades thanks to a spark of brilliance from Ken Thompson. git://git.kernel.org/pub/scm/utils/dash/dash.git - fba95e9e4a5d0f1f1ac9f7d86557e47bc0e2656c - Tue Jun 19 11:27:37 2018 -0400 + 057cd650a4edd5856213d431a974ff35c6594489 + Fri Sep 03 15:00:58 2021 +0800 The UNBOURNE SHELL pays HOMAGE to the Stewards of House California: @@ -68,7 +68,7 @@ Derived from software contributed to Berkeley by Kenneth Almquist. Copyright 1991,1993 The Regents of the University of California - Copyright 1997-2018 Herbert Xu + Copyright 1997-2021 Herbert Xu Copyright 1997 Christos Zoulas Redistribution and use in source and binary forms, with or without @@ -183,6 +183,7 @@ /* exceptions */ #define EXINT 0 #define EXERROR 1 +#define EXEND 3 #define EXEXIT 4 /* @@ -207,7 +208,6 @@ #define CEOF 11 #define CCTL 12 #define CSPCL 13 -#define CIGN 14 /* Syntax classes for is_ functions */ #define ISDIGIT 01 @@ -216,13 +216,11 @@ #define ISUNDER 010 #define ISSPECL 020 -#define SYNBASE 130 -#define PEOF -130 +#define SYNBASE 129 +#define PEOF -129 #define EOF_NLEFT -99 -#define PEOA -129 - #define BASESYNTAX (basesyntax + SYNBASE) #define DQSYNTAX (dqsyntax + SYNBASE) #define SQSYNTAX (sqsyntax + SYNBASE) @@ -345,6 +343,7 @@ #define VSTRIMLEFT 0x8 #define VSTRIMLEFTMAX 0x9 #define VSLENGTH 0xa +/* VSLENGTH must come last. */ /* values of checkkwd variable */ #define CHKALIAS 0x1 @@ -484,9 +483,10 @@ #define CUR_STOPPED 0 /* mode flags for dowait */ -#define DOWAIT_NORMAL 0 -#define DOWAIT_BLOCK 1 -#define DOWAIT_WAITCMD 2 +#define DOWAIT_NONBLOCK 0 +#define DOWAIT_BLOCK 1 +#define DOWAIT_WAITCMD 2 +#define DOWAIT_WAITCMD_ALL 4 /* _rmescape() flags */ #define RMESCAPE_ALLOC 0x01 @@ -570,7 +570,6 @@ #define CD_PHYSICAL 1 #define CD_PRINT 2 -#define REALLY_CLOSED -3 #define EMPTY -2 #define CLOSED -1 #define PIPESIZE 4096 @@ -711,10 +710,11 @@ struct strpush *prev; /* preceding string on stack */ char *prevstring; int prevnleft; - struct alias *ap; /* if push was associated with an alias */ - char *string; /* remember the string since it may change */ - int lastc[2]; /* Remember last two characters for pungetc. */ - int unget; /* Number of outstanding calls to pungetc. */ + struct alias *ap; /* if push was associated with an alias */ + char *string; /* remember the string since it may change */ + struct strpush *spfree; /* Delay freeing so we can stop nested aliases. */ + int lastc[2]; /* Remember last two characters for pungetc. */ + int unget; /* Number of outstanding calls to pungetc. */ }; /* @@ -731,6 +731,7 @@ char *buf; /* input buffer */ struct strpush *strpush; /* for pushing strings at this level */ struct strpush basestrpush; /* so pushing one is fast */ + struct strpush *spfree; /* Delay freeing so we can stop nested aliases. */ int lastc[2]; /* Remember last two characters for pungetc. */ int unget; /* Number of outstanding calls to pungetc. */ }; @@ -1055,8 +1056,8 @@ static int funcblocksize; /* size of structures in function */ static int funcline; /* start line of function, or 0 if not in one */ static int funcstringsize; /* size of strings in node */ -static int gotsigchld; /* received SIGCHLD */ static int initialpgrp; /* pgrp of shell on invocation */ +static int inps4; /* Prevent PS4 nesting. */ static int job_warning; static int jobctl; static int last_token; @@ -1083,6 +1084,7 @@ static struct ifsregion *ifslastp; /* last struct in list */ static struct ifsregion ifsfirst; /* first struct in list of ifs regions */ static struct jmploc *handler; +static struct jmploc main_handler; static struct job *curjob; /* current job */ static struct job *jobtab; /* array of jobs */ static struct localvar_list *localvar_stack; @@ -1099,8 +1101,10 @@ static struct Var *vartab[VTABSIZE]; static union node *redirnode; static union yystype yylval; +static unsigned closed_redirs; /* Bit map of currently closed file descriptors. */ static unsigned expdir_max; static unsigned njobs; /* size of array */ +static volatile sig_atomic_t gotsigchld; /* received SIGCHLD */ static volatile sig_atomic_t intpending; static volatile sig_atomic_t pending_sig; /* last pending signal */ static struct alias *atab[ATABSIZE]; @@ -1217,117 +1221,135 @@ /* syntax table used when not in quotes */ static const char basesyntax[] /* clang-format off */ = { - CEOF, CSPCL, CWORD, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, - CCTL, CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CSPCL, CNL, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CSPCL, CWORD, CDQUOTE, CWORD, CVAR, CWORD, CSPCL, CSQUOTE, CSPCL, - CSPCL, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CSPCL, CSPCL, CWORD, CSPCL, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CBACK, CWORD, CWORD, - CWORD, CBQUOTE, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CSPCL, CENDVAR, CWORD, CWORD + CEOF, CWORD, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, + CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CSPCL, CNL, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CSPCL, + CWORD, CDQUOTE, CWORD, CVAR, CWORD, CSPCL, CSQUOTE, CSPCL, CSPCL, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CSPCL, + CSPCL, CWORD, CSPCL, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CBACK, CWORD, CWORD, CWORD, + CBQUOTE, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CSPCL, CENDVAR, CWORD, CWORD } /* clang-format on */; /* syntax table used when in double quotes */ static const char dqsyntax[] /* clang-format off */ = { - CEOF, CIGN, CWORD, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, - CCTL, CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CNL, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CCTL, CENDQUOTE, CWORD, CVAR, CWORD, CWORD, CWORD, CWORD, - CWORD, CCTL, CWORD, CWORD, CCTL, CWORD, CCTL, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CCTL, - CWORD, CWORD, CCTL, CWORD, CCTL, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CCTL, CBACK, CCTL, CWORD, - CWORD, CBQUOTE, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CENDVAR, CCTL, CWORD + CEOF, CWORD, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, + CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CNL, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CCTL, CENDQUOTE, CWORD, CVAR, CWORD, CWORD, CWORD, CWORD, CWORD, + CCTL, CWORD, CWORD, CCTL, CWORD, CCTL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CCTL, CWORD, + CWORD, CCTL, CWORD, CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CCTL, CBACK, CCTL, CWORD, CWORD, + CBQUOTE, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CENDVAR, CCTL, CWORD } /* clang-format on */; /* syntax table used when in single quotes */ -static const char sqsyntax[] = { - CEOF, CIGN, CWORD, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CNL, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, - CENDQUOTE, CWORD, CWORD, CCTL, CWORD, CWORD, CCTL, CWORD, CCTL, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CCTL, CWORD, CWORD, CCTL, CWORD, CCTL, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CCTL, CCTL, CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CCTL, CWORD, -}; +static const char sqsyntax[] /* clang-format off */ = { + CEOF, CWORD, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, + CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CNL, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, CENDQUOTE, CWORD, CWORD, + CCTL, CWORD, CWORD, CCTL, CWORD, CCTL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CCTL, CWORD, + CWORD, CCTL, CWORD, CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CCTL, CCTL, CCTL, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CCTL, CWORD +} /* clang-format on */; /* syntax table used when in arithmetic */ -static const char arisyntax[] = { - CEOF, CIGN, CWORD, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CNL, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CVAR, CWORD, CWORD, - CWORD, CLP, CRP, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CBACK, CWORD, CWORD, CWORD, CBQUOTE, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CENDVAR, CWORD, CWORD, -}; +static const char arisyntax[] /* clang-format off */ = { + CEOF, CWORD, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, CCTL, + CCTL, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CNL, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CVAR, CWORD, CWORD, CWORD, CLP, CRP, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CBACK, CWORD, CWORD, CWORD, + CBQUOTE, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CENDVAR, CWORD, CWORD +} /* clang-format on */; /* character classification table */ static const char is_type[] /* clang-format off */ = { @@ -1351,19 +1373,19 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, ISSPECL, 0, ISSPECL, ISSPECL, 0, - 0, 0, 0, 0, ISSPECL, 0, 0, ISSPECL, - 0, 0, ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, - ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, 0, 0, 0, 0, - 0, ISSPECL, ISSPECL, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, + 0, 0, ISSPECL, 0, ISSPECL, ISSPECL, 0, 0, + 0, 0, 0, ISSPECL, 0, 0, ISSPECL, 0, + 0, ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, + ISDIGIT, ISDIGIT, ISDIGIT, 0, 0, 0, 0, 0, + ISSPECL, ISSPECL, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, - ISUPPER, ISUPPER, ISUPPER, ISUPPER, ISUPPER, 0, 0, 0, - 0, ISUNDER, 0, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, 0, 0, 0, 0, + ISUNDER, 0, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, - ISLOWER, ISLOWER, ISLOWER, ISLOWER, ISLOWER, 0, 0, 0, - 0, 0 + ISLOWER, ISLOWER, ISLOWER, ISLOWER, 0, 0, 0, 0, + 0 } /* clang-format on */; static int aliascmd(); @@ -2141,9 +2163,7 @@ static int options(int); static int padvance_magic(const char **, const char *, int); static int patmatch(char *, const char *); -static int peektoken(void); static int pgetc(void); -static int pgetc2(void); static int pgetc_eatbnl(); static int pmatch(const char *, const char *); static int preadbuffer(void); @@ -2155,6 +2175,7 @@ static int redirectsafe(union node *, int); static int savefd(int, int); static int setinputfile(const char *, int); +static int sh_open(const char *pathname, int flags, int mayfail); static int showvars(const char *, int, int); static int stoppedjobs(void); static int test_file_access(const char *, int); @@ -2195,21 +2216,20 @@ static void addcmdentry(char *, struct cmdentry *); static void addfname(char *); static void check_conversion(const char *, const char *); -static void clear_traps(void); static void clearcmdentry(void); -static void closescript(void); static void defun(union node *); static void delete_cmd_entry(void); static void dotrap(void); static void dupredirect(union node *, int); static void exitreset(void); static void expandarg(union node *arg, struct arglist *arglist, int flag); -static void expandmeta(struct strlist *, int); +static void expandmeta(struct strlist *); static void expbackq(union node *, int); static void expmeta(char *, unsigned, unsigned); static void expredir(union node *); static void find_command(char *, struct cmdentry *, int, const char *); static void fixredir(union node *, const char *, int); +static void forkreset(void); static void freeparam(volatile struct shparam *); static void hashcd(void); static void ignoresig(int); @@ -2222,7 +2242,7 @@ static void parseheredoc(void); static void popallfiles(void); static void popfile(void); -static void poplocalvars(int); +static void poplocalvars(void); static void popredir(int); static void popstring(void); static void prehash(union node *); @@ -3083,8 +3103,11 @@ static int evaltree(union node *n, int flags) { int checkexit = 0; int (*evalfn)(union node *, int); + struct stackmark smark; unsigned isor; int status = 0; + setstackmark(&smark); + if (nflag) goto out; if (n == NULL) { TRACE(("evaltree(NULL) called\n")); goto out; @@ -3108,7 +3131,7 @@ case NCMD: evalfn = evalcommand; checkexit: - if (eflag && !(flags & EV_TESTED)) checkexit = ~0; + checkexit = ~flags & EV_TESTED; goto calleval; case NFOR: evalfn = evalfor; @@ -3132,8 +3155,6 @@ case NSEMI: isor = n->type - NAND; status = evaltree(n->nbinary.ch1, (flags | ((isor >> 1) - 1)) & EV_TESTED); - /* XXX: -Wlogical-not-parentheses: - if (!status == isor || evalskip) break; */ if ((!status) == isor || evalskip) break; n = n->nbinary.ch2; evaln: @@ -3160,12 +3181,13 @@ break; } out: - if (checkexit & status) goto exexit; dotrap(); + if (eflag && checkexit & status) goto exexit; if (flags & EV_EXIT) { exexit: - exraise(EXEXIT); + exraise(EXEND); } + popstackmark(&smark); return exitstatus; } @@ -3261,11 +3283,9 @@ struct arglist arglist; union node *argp; struct strlist *sp; - struct stackmark smark; int status; errlinno = lineno = n->nfor.linno; if (funcline) lineno -= funcline - 1; - setstackmark(&smark); arglist.lastp = &arglist.list; for (argp = n->nfor.args; argp; argp = argp->narg.next) { expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); @@ -3280,7 +3300,6 @@ if (skiploop() & ~SKIPCONT) break; } loopnest--; - popstackmark(&smark); return status; } @@ -3304,11 +3323,9 @@ union node *cp; union node *patp; struct arglist arglist; - struct stackmark smark; int status = 0; errlinno = lineno = n->ncase.linno; if (funcline) lineno -= funcline - 1; - setstackmark(&smark); arglist.lastp = &arglist.list; expandarg(n->ncase.expr, &arglist, EXP_TILDE); for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) { @@ -3326,7 +3343,6 @@ } } out: - popstackmark(&smark); return status; } @@ -3340,14 +3356,17 @@ errlinno = lineno = n->nredir.linno; if (funcline) lineno -= funcline - 1; expredir(n->nredir.redirect); - if (!backgnd && flags & EV_EXIT && !have_traps()) goto nofork; INTOFF; + if (!backgnd && flags & EV_EXIT && !have_traps()) { + forkreset(); + goto nofork; + } jp = makejob(n, 1); if (forkshell(jp, n, backgnd) == 0) { - INTON; flags |= EV_EXIT; if (backgnd) flags &= ~EV_TESTED; nofork: + INTON; redirect(n->nredir.redirect, 0); evaltreenr(n->nredir.n, flags); /* never returns */ @@ -3378,7 +3397,7 @@ case NFROMFD: case NTOFD: if (redir->ndup.vname) { - expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); + expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR); fixredir(redir, fn.list->text, 1); } break; @@ -3527,7 +3546,6 @@ struct localvar_list *localvar_stop; struct parsefile *file_stop; struct redirtab *redir_stop; - struct stackmark smark; union node *argp; struct arglist arglist; struct arglist varlist; @@ -3550,7 +3568,6 @@ if (funcline) lineno -= funcline - 1; /* First expand the arguments. */ TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); - setstackmark(&smark); file_stop = parsefile; back_exitstatus = 0; cmdentry.cmdtype = CMDBUILTIN; @@ -3624,11 +3641,13 @@ setvareq((*spp)->text, vflags); } /* Print the command if xflag is set. */ - if (xflag) { + if (xflag && !inps4) { struct output *out; int sep; out = &preverrout; + inps4 = 1; outstr(expandstr(ps4val()), out); + inps4 = 0; sep = 0; sep = eprintlist(out, varlist.list, sep); eprintlist(out, osp, sep); @@ -3679,7 +3698,6 @@ */ setvar("_", lastarg, 0); } - popstackmark(&smark); return status; } @@ -3702,6 +3720,7 @@ else status = (*cmd->builtin)(argc, argv); flushall(); + if (out1->flags) sh_warnx("%s: I/O error", commandname); status |= out1->flags; exitstatus = status; cmddone: @@ -3893,7 +3912,7 @@ exerrno = (e == ELOOP || e == ENAMETOOLONG || e == ENOENT || e == ENOTDIR) ? 127 : 126; exitstatus = exerrno; TRACE(("shellexec failed for %s, errno %d, suppressint %d\n", argv[0], e, suppressint)); - exerror(EXEXIT, "%s: %s", argv[0], errmsg(e, E_EXEC)); + exerror(EXEND, "%s: %s", argv[0], errmsg(e, E_EXEC)); } static void tryexec(char *cmd, char **argv, char **envp) { @@ -4498,7 +4517,7 @@ ifsbreakup(p, -1, &exparg); *exparg.lastp = NULL; exparg.lastp = &exparg.list; - expandmeta(exparg.list, flag); + expandmeta(exparg.list); } else { sp = (struct strlist *)stalloc(sizeof(struct strlist)); sp->text = p; @@ -4560,7 +4579,7 @@ q = stnputs(p, length, expdest); q[-1] &= end - 1; expdest = q - (flag & EXP_WORD ? end : 0); - newloc = expdest - (char *)stackblock() - end; + newloc = q - (char *)stackblock() - end; if (breakall && !inquotes && newloc > startloc) { recordregion(startloc, newloc, 0); } @@ -4756,7 +4775,7 @@ INTON; /* Eat all trailing newlines */ dest = expdest; - for (; dest > (char *)stackblock() && dest[-1] == '\n';) { + for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';) { STUNPUTC(dest); } expdest = dest; @@ -4889,6 +4908,7 @@ int patloc; int startloc; long varlen; + int discard; int quoted; varflags = *p++; subtype = varflags & VSTYPE; @@ -4899,31 +4919,32 @@ again: varlen = varvalue(var_, varflags, flag, quoted); if (varflags & VSNUL) varlen--; + discard = varlen < 0 ? EXP_DISCARD : 0; switch (subtype) { case VSPLUS: - varlen = -1 - varlen; + discard ^= EXP_DISCARD; /* fall through */ case 0: case VSMINUS: - p = argstr(p, flag | EXP_TILDE | EXP_WORD); - if (varlen < 0) return p; + p = argstr(p, flag | EXP_TILDE | EXP_WORD | (discard ^ EXP_DISCARD)); goto record; case VSASSIGN: case VSQUESTION: - if (varlen >= 0) goto record; - p = subevalvar(p, var_, 0, startloc, varflags, flag & ~QUOTES_ESC); - if (flag & EXP_DISCARD) return p; + p = subevalvar(p, var_, 0, startloc, varflags, (flag & ~QUOTES_ESC) | (discard ^ EXP_DISCARD)); + if ((flag | ~discard) & EXP_DISCARD) goto record; varflags &= ~VSNUL; + subtype = VSNORMAL; goto again; } - if (varlen < 0 && uflag) varunset(p, var_, 0, 0); + if ((discard & ~flag) && uflag) varunset(p, var_, 0, 0); if (subtype == VSLENGTH) { + p++; if (flag & EXP_DISCARD) return p; cvtnum(varlen > 0 ? varlen : 0, flag); - goto record; + goto really_record; } if (subtype == VSNORMAL) goto record; - flag |= varlen < 0 ? EXP_DISCARD : 0; + flag |= discard; if (!(flag & EXP_DISCARD)) { /* * Terminate the string and start recording the pattern @@ -4934,7 +4955,8 @@ patloc = expdest - (char *)stackblock(); p = subevalvar(p, NULL, patloc, startloc, varflags, flag); record: - if (flag & EXP_DISCARD) return p; + if ((flag | discard) & EXP_DISCARD) return p; +really_record: if (quoted) { quoted = *var_ == '@' && shellparam.nparam; if (!quoted) return p; @@ -5212,7 +5234,7 @@ * Expand shell metacharacters. At this point, the only control characters * should be escapes. The results are stored in the list exparg. */ -static void expandmeta(struct strlist *str, int flag) { +static void expandmeta(struct strlist *str) { static const char metachars[] = {'*', '?', '[', 0}; /* TODO - EXP_REDIR */ while (str) { @@ -5595,11 +5617,23 @@ return memtodest(buf, len, flags); } -/* - * Read a character from the script, returning PEOF on end of file. - * Nul characters in the input are silently discarded. - */ -static int pgetc(void) { +static void freestrings(struct strpush *sp) { + INTOFF; + do { + struct strpush *psp; + if (sp->ap) { + sp->ap->flag &= ~ALIASINUSE; + if (sp->ap->flag & ALIASDEAD) unalias(sp->ap->name); + } + psp = sp; + sp = sp->spfree; + if (psp != &(parsefile->basestrpush)) ckfree(psp); + } while (sp); + parsefile->spfree = NULL; + INTON; +} + +static int pgetc_nofree(void) { int c; if (parsefile->unget) return parsefile->lastc[--parsefile->unget]; if (--parsefile->nleft >= 0) @@ -5612,14 +5646,13 @@ } /* - * Same as pgetc(), but ignores PEOA. + * Read a character from the script, returning PEOF on end of file. + * Nul characters in the input are silently discarded. */ -static int pgetc2() { - int c; - do { - c = pgetc(); - } while (c == PEOA); - return c; +int pgetc(void) { + struct strpush *sp = parsefile->spfree; + if (unlikely(sp)) freestrings(sp); + return pgetc_nofree(); } static void AddUniqueCompletion(linenoiseCompletions *c, char *s) { @@ -5774,12 +5807,8 @@ int more; char savec; if (unlikely(parsefile->strpush)) { - if (parsefile->nleft == -1 && parsefile->strpush->ap && parsefile->nextc[-1] != ' ' && - parsefile->nextc[-1] != '\t') { - return PEOA; - } popstring(); - return pgetc(); + return pgetc_nofree(); } if (unlikely(parsefile->nleft == EOF_NLEFT || parsefile->buf == NULL)) return PEOF; flushall(); @@ -5840,7 +5869,7 @@ len = strlen(s); INTOFF; /*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ - if (parsefile->strpush) { + if ((unsigned long)parsefile->strpush | (unsigned long)parsefile->spfree) { sp = ckmalloc(sizeof(struct strpush)); sp->prev = parsefile->strpush; parsefile->strpush = sp; @@ -5849,6 +5878,7 @@ sp->prevstring = parsefile->nextc; sp->prevnleft = parsefile->nleft; sp->unget = parsefile->unget; + sp->spfree = parsefile->spfree; memcpy(sp->lastc, parsefile->lastc, sizeof(sp->lastc)); sp->ap = (struct alias *)ap; if (ap) { @@ -5858,6 +5888,7 @@ parsefile->nextc = s; parsefile->nleft = len; parsefile->unget = 0; + parsefile->spfree = NULL; INTON; } @@ -5871,10 +5902,6 @@ if (sp->string != sp->ap->val) { ckfree(sp->string); } - sp->ap->flag &= ~ALIASINUSE; - if (sp->ap->flag & ALIASDEAD) { - unalias(sp->ap->name); - } } parsefile->nextc = sp->prevstring; parsefile->nleft = sp->prevnleft; @@ -5882,7 +5909,7 @@ memcpy(parsefile->lastc, sp->lastc, sizeof(sp->lastc)); /*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ parsefile->strpush = sp->prev; - if (sp != &(parsefile->basestrpush)) ckfree(sp); + parsefile->spfree = sp; INTON; } @@ -5893,11 +5920,8 @@ static int setinputfile(const char *fname, int flags) { int fd; INTOFF; - if ((fd = open(fname, O_RDONLY, 0)) < 0) { - if (flags & INPUT_NOFILE_OK) goto out; - exitstatus = 127; - exerror(EXERROR, "Can't open %s", fname); - } + fd = sh_open(fname, O_RDONLY, flags & INPUT_NOFILE_OK); + if (fd < 0) goto out; if (fd < 10) fd = savefd(fd, fd); setinputfd(fd, flags & INPUT_PUSH_FILE); out: @@ -5943,6 +5967,7 @@ pf->prev = parsefile; pf->fd = -1; pf->strpush = NULL; + pf->spfree = NULL; pf->basestrpush.prev = NULL; pf->unget = 0; parsefile = pf; @@ -5953,7 +5978,11 @@ INTOFF; if (pf->fd >= 0) close(pf->fd); if (pf->buf) ckfree(pf->buf); - while (pf->strpush) popstring(); + if (parsefile->spfree) freestrings(parsefile->spfree); + while (pf->strpush) { + popstring(); + freestrings(parsefile->spfree); + } parsefile = pf->prev; ckfree(pf); INTON; @@ -5970,18 +5999,6 @@ unwindfiles(&basepf); } -/* - * Close the file(s) that the shell is reading commands from. Called - * after a fork is done. - */ -static void closescript(void) { - popallfiles(); - if (parsefile->fd > 0) { - close(parsefile->fd); - parsefile->fd = 0; - } -} - static char *commandtext(union node *); static int dowait(int, struct job *); static int getstatus(struct job *); @@ -6052,7 +6069,7 @@ if (on == jobctl || rootshell == 0) return; if (on) { int ofd; - ofd = fd = open(_PATH_TTY, O_RDWR, 0); + ofd = fd = sh_open(_PATH_TTY, O_RDWR, 1); if (fd < 0) { fd += 3; while (!isatty(fd)) @@ -6322,8 +6339,8 @@ static void showjobs(struct output *out, int mode) { struct job *jp; TRACE(("showjobs(%x) called\n", mode)); - /* If not even one one job changed, there is nothing to do */ - dowait(DOWAIT_NORMAL, NULL); + /* If not even one job changed, there is nothing to do */ + dowait(DOWAIT_NONBLOCK, NULL); for (jp = curjob; jp; jp = jp->prev_job) { if (!(mode & SHOW_CHANGED) || jp->changed) showjob(out, jp, mode); } @@ -6365,7 +6382,7 @@ jp->waited = 1; jp = jp->prev_job; } - if (!dowait(DOWAIT_WAITCMD, 0)) goto sigout; + if (!dowait(DOWAIT_WAITCMD_ALL, 0)) goto sigout; } } retval = 127; @@ -6546,8 +6563,7 @@ lvforked = vforked; if (!lvforked) { shlvl++; - closescript(); - clear_traps(); + forkreset(); /* do job control only in root shell */ jobctl = 0; } @@ -6567,14 +6583,12 @@ ignoresig(SIGQUIT); if (jp->nprocs == 0) { close(0); - if (open(_PATH_DEVNULL, O_RDONLY, 0) != 0) sh_error("Can't open %s", _PATH_DEVNULL); + sh_open(_PATH_DEVNULL, O_RDONLY, 0); } } if (!oldlvl && iflag) { - if (mode != FORK_BG) { - setsignal(SIGINT); - setsignal(SIGQUIT); - } + setsignal(SIGINT); + setsignal(SIGQUIT); setsignal(SIGTERM); } if (lvforked) return; @@ -6666,7 +6680,7 @@ static int waitforjob(struct job *jp) { int st; TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0)); - dowait(jp ? DOWAIT_BLOCK : DOWAIT_NORMAL, jp); + dowait(jp ? DOWAIT_BLOCK : DOWAIT_NONBLOCK, jp); if (!jp) return exitstatus; st = getstatus(jp); if (jp->jobctl) { @@ -6750,12 +6764,19 @@ } static int dowait(int block, struct job *jp) { - int pid = block == DOWAIT_NORMAL ? gotsigchld : 1; - while (jp ? jp->state == JOBRUNNING : pid > 0) { - if (!jp) gotsigchld = 0; + int gotchld = *(volatile int *)&gotsigchld; + int rpid; + int pid; + if (jp && jp->state != JOBRUNNING) block = DOWAIT_NONBLOCK; + if (block == DOWAIT_NONBLOCK && !gotchld) return 1; + rpid = 1; + do { pid = waitone(block, jp); - } - return pid; + rpid &= !!pid; + block &= ~DOWAIT_WAITCMD_ALL; + if (!pid || (jp && jp->state != JOBRUNNING)) block = DOWAIT_NONBLOCK; + } while (pid >= 0); + return rpid; } /* @@ -6778,12 +6799,13 @@ int err; if (jobctl) flags |= WUNTRACED; do { - err = wait3(status, flags, NULL); + gotsigchld = 0; + do err = wait3(status, flags, NULL); + while (err < 0 && errno == EINTR); if (err || (err = -!block)) break; sigblockall(&oldmask); while (!gotsigchld && !pending_sig) sigsuspend(&oldmask); sigclearmask(); - err = 0; } while (gotsigchld); return err; } @@ -7062,7 +7084,11 @@ } static void xtcsetpgrp(int fd, int pgrp) { - if (tcsetpgrp(fd, pgrp)) sh_error("Cannot set tty process group (%s)", strerror(errno)); + int err; + sigblockall(NULL); + err = tcsetpgrp(fd, pgrp); + sigclearmask(); + if (err) sh_error("Cannot set tty process group (%s)", strerror(errno)); } static int getstatus(struct job *job) { @@ -7458,7 +7484,6 @@ setinputfile(*xargv, 0); setarg0: arg0 = *xargv++; - commandname = arg0; } shellparam.p = xargv; shellparam.optind = 1; @@ -7803,22 +7828,27 @@ } static union node *list(int nlflag) { + int chknl = nlflag & 1 ? 0 : CHKNL; union node *n1, *n2, *n3; int tok; n1 = NULL; for (;;) { - switch (peektoken()) { + checkkwd = chknl | CHKKWD | CHKALIAS; + tok = readtoken(); + switch (tok) { case TNL: - if (!(nlflag & 1)) break; parseheredoc(); return n1; case TEOF: - if (!n1 && (nlflag & 1)) n1 = NEOF; + if (!n1 && !chknl) n1 = NEOF; + out_eof: parseheredoc(); + tokpushback++; + lasttoken = TEOF; return n1; } - checkkwd = CHKNL | CHKKWD | CHKALIAS; - if (nlflag == 2 && tokendlist[peektoken()]) return n1; + tokpushback++; + if (nlflag == 2 && tokendlist[tok]) return n1; nlflag |= 2; n2 = andor(); tok = readtoken(); @@ -7845,15 +7875,16 @@ n1 = n3; } switch (tok) { - case TNL: case TEOF: + goto out_eof; + case TNL: tokpushback++; /* fall through */ case TBACKGND: case TSEMI: break; default: - if ((nlflag & 1)) synexpect(-1); + if (!chknl) synexpect(-1); tokpushback++; return n1; } @@ -8248,13 +8279,6 @@ } } -static int peektoken(void) { - int t; - t = readtoken(); - tokpushback++; - return (t); -} - static int readtoken(void) { int t; int kwd = checkkwd; @@ -8266,9 +8290,12 @@ if (kwd & CHKNL) { while (t == TNL) { parseheredoc(); + checkkwd = 0; t = xxreadtoken(); } } + kwd |= checkkwd; + checkkwd = 0; if (t != TWORD || quoteflag) { goto out; } @@ -8284,7 +8311,7 @@ goto out; } } - if (checkkwd & CHKALIAS) { + if (kwd & CHKALIAS) { struct alias *ap; if ((ap = lookupalias(wordtext, 1)) != NULL) { if (*ap->val) { @@ -8294,7 +8321,6 @@ } } out: - checkkwd = 0; return (t); } @@ -8338,7 +8364,6 @@ switch (c) { case ' ': case '\t': - case PEOA: continue; case '#': while ((c = pgetc()) != '\n' && c != PEOF) @@ -8376,7 +8401,7 @@ static int pgetc_eatbnl(void) { int c; while ((c = pgetc()) == '\\') { - if (pgetc2() != '\n') { + if (pgetc() != '\n') { pungetc(); break; } @@ -8481,7 +8506,7 @@ break; /* backslash */ case CBACK: - c = pgetc2(); + c = pgetc(); if (c == PEOF) { USTPUTC(CTLESC, out); USTPUTC('\\', out); @@ -8568,13 +8593,9 @@ break; case CEOF: goto endword; /* exit outer loop */ - case CIGN: - break; default: if (synstack->varnest == 0) goto endword; /* exit outer loop */ - if (c != PEOA) { - USTPUTC(c, out); - } + USTPUTC(c, out); } c = pgetc_top(synstack); } @@ -8613,18 +8634,13 @@ if (realeofmark(eofmark)) { int markloc; char *p; - if (c == PEOA) { - c = pgetc2(); - } if (striptabs) { - while (c == '\t') { - c = pgetc2(); - } + while (c == '\t') c = pgetc(); } markloc = out - (char *)stackblock(); for (p = eofmark; STPUTC(c, out), *p; p++) { if (c != *p) goto more_heredoc; - c = pgetc2(); + c = pgetc(); } if (c == '\n' || c == PEOF) { c = PEOF; @@ -8717,8 +8733,7 @@ char *p; static const char types[] = "}-+?="; c = pgetc_eatbnl(); - if ((checkkwd & CHKEOFMARK) || c <= PEOA || - (c != '(' && c != '{' && !is_name(c) && !is_special(c))) { + if ((checkkwd & CHKEOFMARK) || (c != '(' && c != '{' && !is_name(c) && !is_special(c))) { USTPUTC('$', out); pungetc(); } else if (c == '(') { /* $(command) or $((arith)) */ @@ -8748,7 +8763,7 @@ do { STPUTC(c, out); c = pgetc_eatbnl(); - } while (is_digit(c)); + } while ((subtype <= 0 || subtype >= VSLENGTH) && is_digit(c)); } else if (c != '}') { int cc = c; c = pgetc_eatbnl(); @@ -8795,6 +8810,7 @@ break; } } else { + if (subtype == VSLENGTH && c != '}') subtype = 0; badsub: pungetc(); } @@ -8850,15 +8866,11 @@ case '`': goto done; case '\\': - pc = pgetc_eatbnl(); + pc = pgetc(); if (pc != '\\' && pc != '`' && pc != '$' && (!synstack->dblquote || pc != '"')) STPUTC('\\', pout); - if (pc > PEOA) { - break; - } - /* fall through */ + break; case PEOF: - case PEOA: synerror("EOF in backquote substitution"); case '\n': nlnoprompt(); @@ -8892,8 +8904,8 @@ else { if (readtoken() != TRP) synexpect(TRP); setinputstring(nullstr); - parseheredoc(); } + parseheredoc(); heredoclist = saveheredoclist; (*nlpp)->n = n; /* Start reading from old file again. */ @@ -8938,21 +8950,38 @@ } static const char *expandstr(const char *ps) { - union node n; + struct parsefile *file_stop; + struct jmploc *volatile savehandler; + struct heredoc *saveheredoclist; + const char *result; int saveprompt; - /* XXX Fix (char *) cast. */ - setinputstring((char *)ps); + struct jmploc jmploc; + union node n; + int err; + file_stop = parsefile; + setinputstring((char *)ps); /* XXX Fix (char *) cast. */ + saveheredoclist = heredoclist; + heredoclist = NULL; saveprompt = doprompt; doprompt = 0; + result = ps; + savehandler = handler; + if (unlikely(err = setjmp(jmploc.loc))) goto out; + handler = &jmploc; readtoken1(pgetc_eatbnl(), DQSYNTAX, FAKEEOFMARK, 0); - doprompt = saveprompt; - popfile(); n.narg.type = NARG; n.narg.next = NULL; n.narg.text = wordtext; n.narg.backquote = backquotelist; expandarg(&n, NULL, EXP_QUOTED); - return stackblock(); + result = stackblock(); +out: + handler = savehandler; + if (err && exception != EXERROR) longjmp(handler->loc, 1); + doprompt = saveprompt; + unwindfiles(file_stop); + heredoclist = saveheredoclist; + return result; } /* @@ -8979,6 +9008,16 @@ return findstring(s, parsekwd, sizeof(parsekwd) / sizeof(const char *)); } +static unsigned update_closed_redirs(int fd, int nfd) { + unsigned val = closed_redirs; + unsigned bit = 1 << fd; + if (nfd >= 0) + closed_redirs &= ~bit; + else + closed_redirs |= bit; + return val & bit; +} + /* * Process a list of redirection commands. If the REDIR_PUSH flag is set, * old file descriptors are stashed away so that the redirection can be @@ -9003,17 +9042,17 @@ if (newfd < -1) continue; fd = n->nfile.fd; if (sv) { + int closed; p = &sv->renamed[fd]; i = *p; + closed = update_closed_redirs(fd, newfd); if (likely(i == EMPTY)) { i = CLOSED; - if (fd != newfd) { + if (fd != newfd && !closed) { i = savefd(fd, fd); fd = -1; } } - if (i == newfd) /* Can only happen if i == newfd == CLOSED */ - i = REALLY_CLOSED; *p = i; } if (fd == newfd) continue; @@ -9023,47 +9062,66 @@ if (flags & REDIR_SAVEFD2 && sv->renamed[2] >= 0) preverrout.fd = sv->renamed[2]; } +wontreturn static int sh_open_fail(const char *pathname, int flags, int e) { + const char *word; + int action; + word = "open"; + action = E_OPEN; + if (flags & O_CREAT) { + word = "create"; + action = E_CREAT; + } + sh_error("cannot %s %s: %s", word, pathname, errmsg(e, action)); +} + +static int sh_open(const char *pathname, int flags, int mayfail) { + int fd; + int e; + do { + fd = open(pathname, flags, 0666); + e = errno; + } while (fd < 0 && e == EINTR && !pending_sig); + if (mayfail || fd >= 0) return fd; + sh_open_fail(pathname, flags, e); +} + static int openredirect(union node *redir) { struct stat sb; char *fname; + int flags; int f; switch (redir->nfile.type) { case NFROM: - fname = redir->nfile.expfname; - if ((f = open(fname, O_RDONLY, 0)) < 0) goto eopen; + flags = O_RDONLY; + do_open: + f = sh_open(redir->nfile.expfname, flags, 0); break; case NFROMTO: - fname = redir->nfile.expfname; - if ((f = open(fname, O_RDWR | O_CREAT, 0666)) < 0) goto ecreate; - break; + flags = O_RDWR|O_CREAT; + goto do_open; case NTO: /* Take care of noclobber mode. */ if (Cflag) { fname = redir->nfile.expfname; if (stat(fname, &sb) < 0) { - if ((f = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0) goto ecreate; - } else if (!S_ISREG(sb.st_mode)) { - if ((f = open(fname, O_WRONLY, 0666)) < 0) goto ecreate; - if (!fstat(f, &sb) && S_ISREG(sb.st_mode)) { - close(f); - errno = EEXIST; - goto ecreate; - } - } else { - errno = EEXIST; + flags = O_WRONLY|O_CREAT|O_EXCL; + goto do_open; + } + if (S_ISREG(sb.st_mode)) goto ecreate; + f = sh_open(fname, O_WRONLY, 0); + if (!fstat(f, &sb) && S_ISREG(sb.st_mode)) { + close(f); goto ecreate; } break; } /* FALLTHROUGH */ case NCLOBBER: - fname = redir->nfile.expfname; - if ((f = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) goto ecreate; - break; + flags = O_WRONLY|O_CREAT|O_TRUNC; + goto do_open; case NAPPEND: - fname = redir->nfile.expfname; - if ((f = open(fname, O_WRONLY | O_CREAT | O_APPEND, 0666)) < 0) goto ecreate; - break; + flags = O_WRONLY|O_CREAT|O_APPEND; + goto do_open; case NTOFD: case NFROMFD: f = redir->ndup.dupfd; @@ -9078,9 +9136,7 @@ } return f; ecreate: - sh_error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); -eopen: - sh_error("cannot open %s: %s", fname, errmsg(errno, E_OPEN)); + sh_open_fail(fname, O_CREAT, EEXIST); } static void dupredirect(union node *redir, int f) { @@ -9149,12 +9205,12 @@ INTOFF; rp = redirlist; for (i = 0; i < 10; i++) { + int closed; + if (rp->renamed[i] == EMPTY) continue; + closed = drop ? 1 : update_closed_redirs(i, rp->renamed[i]); switch (rp->renamed[i]) { case CLOSED: - if (!drop) close(i); - break; - case EMPTY: - case REALLY_CLOSED: + if (!closed) close(i); break; default: if (!drop) dup2(rp->renamed[i], i); @@ -9268,23 +9324,6 @@ return 0; } -/* - * Clear traps on a fork. - */ -static void clear_traps(void) { - char **tp; - INTOFF; - for (tp = trap; tp < &trap[NSIG]; tp++) { - if (*tp && **tp) { /* trap not NULL or SIG_IGN */ - ckfree(*tp); - *tp = NULL; - if (tp != &trap[0]) setsignal(tp - trap); - } - } - trapcnt = 0; - INTON; -} - /* * Set the signal handler for the specified signal. The routine figures * out what it should be set to. @@ -9437,15 +9476,17 @@ trap[0] = NULL; evalskip = 0; evalstring(p, 0); + evalskip = SKIPFUNCDEF; } out: + exitreset(); /* * Disable job control so that whoever had the foreground before we * started can get it back. */ if (likely(!setjmp(loc.loc))) setjobctl(0); flushall(); - _exit(savestatus); + _exit(exitstatus); } static int decode_signal(const char *string, int minsig) { @@ -10104,12 +10145,23 @@ static int timescmd() { struct tms buf; long int clk_tck = sysconf(_SC_CLK_TCK); + int mutime, mstime, mcutime, mcstime; + double utime, stime, cutime, cstime; times(&buf); - Printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n", (int)(buf.tms_utime / clk_tck / 60), - ((double)buf.tms_utime) / clk_tck, (int)(buf.tms_stime / clk_tck / 60), - ((double)buf.tms_stime) / clk_tck, (int)(buf.tms_cutime / clk_tck / 60), - ((double)buf.tms_cutime) / clk_tck, (int)(buf.tms_cstime / clk_tck / 60), - ((double)buf.tms_cstime) / clk_tck); + utime = (double)buf.tms_utime / clk_tck; + mutime = utime / 60; + utime -= mutime * 60.0; + stime = (double)buf.tms_stime / clk_tck; + mstime = stime / 60; + stime -= mstime * 60.0; + cutime = (double)buf.tms_cutime / clk_tck; + mcutime = cutime / 60; + cutime -= mcutime * 60.0; + cstime = (double)buf.tms_cstime / clk_tck; + mcstime = cstime / 60; + cstime -= mcstime * 60.0; + Printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n", mutime, utime, mstime, stime, + mcutime, cutime, mcstime, cstime); return 0; } @@ -10402,7 +10454,7 @@ * Called after a function returns. * Interrupts must be off. */ -static void poplocalvars(int keep) { +static void poplocalvars(void) { struct localvar_list *ll; struct localvar *lvp, *next; struct Var *vp; @@ -10415,18 +10467,7 @@ next = lvp->next; vp = lvp->vp; TRACE(("poplocalvar %s\n", vp ? vp->text : "-")); - if (keep) { - int bits = VSTRFIXED; - if (lvp->flags != VUNSET) { - if (vp->text == lvp->text) - bits |= VTEXTFIXED; - else if (!(lvp->flags & (VTEXTFIXED | VSTACK))) - ckfree(lvp->text); - } - vp->flags &= ~bits; - vp->flags |= (lvp->flags & bits); - if ((vp->flags & (VEXPORT | VREADONLY | VSTRFIXED | VUNSET)) == VUNSET) unsetvar(vp->text); - } else if (vp == NULL) { /* $- saved */ + if (vp == NULL) { /* $- saved */ memcpy(optlist, lvp->text, sizeof(optlist)); ckfree(lvp->text); optschanged(); @@ -10463,7 +10504,7 @@ } static void unwindlocalvars(struct localvar_list *stop) { - while (localvar_stack != stop) poplocalvars(0); + while (localvar_stack != stop) poplocalvars(); } /* @@ -10506,8 +10547,7 @@ sigmode[SIGCHLD - 1] = S_DFL; setsignal(SIGCHLD); } - /* from output.c: */ - {} /* from var.c: */ + /* from var.c: */ { char **envp; static char ppid[32] = "PPID="; @@ -10543,12 +10583,13 @@ static void exitreset() { /* from eval.c: */ { - evalskip = 0; - loopnest = 0; if (savestatus >= 0) { - exitstatus = savestatus; + if (exception == EXEXIT || evalskip == SKIPFUNCDEF) exitstatus = savestatus; savestatus = -1; } + evalskip = 0; + loopnest = 0; + inps4 = 0; } /* from expand.c: */ { ifsfree(); } @@ -10561,6 +10602,42 @@ } } +/* + * This routine is called when we enter a subshell. + */ +static void forkreset() { + /* from input.c: */ + { + popallfiles(); + if (parsefile->fd > 0) { + close(parsefile->fd); + parsefile->fd = 0; + } + } + /* from main.c: */ + { + handler = &main_handler; + } + /* from redir.c: */ + { + redirlist = NULL; + } + /* from trap.c: */ + { + char **tp; + INTOFF; + for (tp = trap ; tp < &trap[NSIG] ; tp++) { + if (*tp && **tp) { /* trap not NULL or SIG_IGN */ + ckfree(*tp); + *tp = NULL; + if (tp != &trap[0]) setsignal(tp - trap); + } + } + trapcnt = 0; + INTON; + } +} + /* * This routine is called when an error or an interrupt occurs in an * interactive shell and control is returned to the main command loop. @@ -10570,10 +10647,10 @@ { /* clear input buffer */ basepf.lleft = basepf.nleft = 0; + basepf.unget = 0; popallfiles(); } - /* from output.c: */ - {} /* from var.c: */ + /* from var.c: */ { unwindlocalvars(0); } @@ -10815,11 +10892,14 @@ if (n == NEOF) { if (!top || numeof >= 50) break; if (!stoppedjobs()) { - if (!Iflag) break; + if (!Iflag) { + if (iflag) outcslow('\n', out2); + break; + } outstr("\nUse \"exit\" to leave shell.\n", out2); } numeof++; - } else if (nflag == 0) { + } else { int i; job_warning = (job_warning == 2) ? 1 : 0; numeof = 0; @@ -10899,17 +10979,16 @@ int main(int argc, char **argv) { char *shinit; volatile int state; - struct jmploc jmploc; struct stackmark smark; int login; state = 0; - if (unlikely(setjmp(jmploc.loc))) { + if (unlikely(setjmp(main_handler.loc))) { int e; int s; exitreset(); e = exception; s = state; - if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) exitshell(); + if (e == EXEND || e == EXEXIT || s == 0 || iflag == 0 || shlvl) exitshell(); reset(); if (e == EXINT) { outcslow('\n', out2); @@ -10926,7 +11005,7 @@ goto state4; } } - handler = &jmploc; + handler = &main_handler; rootpid = getpid(); init(); setstackmark(&smark);