summaryrefslogtreecommitdiffstats
path: root/bin/sh
diff options
context:
space:
mode:
authorsjg <sjg@FreeBSD.org>2014-11-19 01:07:58 +0000
committersjg <sjg@FreeBSD.org>2014-11-19 01:07:58 +0000
commitb137080f19736ee33fede2e88bb54438604cf86b (patch)
tree377ac0ac449528621eb192cd245adadb5fd53668 /bin/sh
parentab21a29eb607d4dfe389b965fbdee27558e791aa (diff)
parent4a8d07956d121238d006d34ffe7d6269744e8b1a (diff)
downloadFreeBSD-src-b137080f19736ee33fede2e88bb54438604cf86b.zip
FreeBSD-src-b137080f19736ee33fede2e88bb54438604cf86b.tar.gz
Merge from head@274682
Diffstat (limited to 'bin/sh')
-rw-r--r--bin/sh/arith_yacc.c4
-rw-r--r--bin/sh/eval.c15
-rw-r--r--bin/sh/expand.c74
-rw-r--r--bin/sh/histedit.c5
-rw-r--r--bin/sh/input.c29
-rw-r--r--bin/sh/input.h3
-rw-r--r--bin/sh/jobs.c159
-rw-r--r--bin/sh/mystring.c14
-rw-r--r--bin/sh/options.c31
-rw-r--r--bin/sh/options.h1
-rw-r--r--bin/sh/parser.c291
-rw-r--r--bin/sh/redir.c29
-rw-r--r--bin/sh/sh.146
-rw-r--r--bin/sh/tests/Makefile17
-rw-r--r--bin/sh/tests/builtins/Makefile13
-rw-r--r--bin/sh/tests/builtins/eval7.09
-rw-r--r--bin/sh/tests/builtins/eval8.77
-rw-r--r--bin/sh/tests/builtins/getopts9.09
-rw-r--r--bin/sh/tests/builtins/getopts9.0.stdout3
-rw-r--r--bin/sh/tests/builtins/lineno3.06
-rw-r--r--bin/sh/tests/builtins/lineno3.0.stdout2
-rw-r--r--bin/sh/tests/errors/Makefile8
-rw-r--r--bin/sh/tests/errors/bad-parm-exp2.22
-rw-r--r--bin/sh/tests/errors/bad-parm-exp2.2.stderr2
-rw-r--r--bin/sh/tests/errors/bad-parm-exp3.22
-rw-r--r--bin/sh/tests/errors/bad-parm-exp3.2.stderr2
-rw-r--r--bin/sh/tests/errors/bad-parm-exp4.22
-rw-r--r--bin/sh/tests/errors/bad-parm-exp4.2.stderr2
-rw-r--r--bin/sh/tests/errors/bad-parm-exp5.22
-rw-r--r--bin/sh/tests/errors/bad-parm-exp5.2.stderr2
-rw-r--r--bin/sh/tests/errors/bad-parm-exp6.22
-rw-r--r--bin/sh/tests/errors/bad-parm-exp6.2.stderr2
-rw-r--r--bin/sh/tests/execution/Makefile8
-rw-r--r--bin/sh/tests/expansion/Makefile10
-rw-r--r--bin/sh/tests/expansion/arith14.040
-rw-r--r--bin/sh/tests/expansion/redir1.026
-rwxr-xr-xbin/sh/tests/functional_test.sh72
-rw-r--r--bin/sh/tests/legacy_test.sh46
-rw-r--r--bin/sh/tests/parameters/Makefile10
-rw-r--r--bin/sh/tests/parameters/positional6.07
-rw-r--r--bin/sh/tests/parameters/positional7.08
-rw-r--r--bin/sh/tests/parser/Makefile20
-rw-r--r--bin/sh/tests/parser/heredoc12.047
-rw-r--r--bin/sh/tests/parser/line-cont1.016
-rw-r--r--bin/sh/tests/parser/line-cont10.018
-rw-r--r--bin/sh/tests/parser/line-cont11.023
-rw-r--r--bin/sh/tests/parser/line-cont2.04
-rw-r--r--bin/sh/tests/parser/line-cont3.07
-rw-r--r--bin/sh/tests/parser/line-cont4.08
-rw-r--r--bin/sh/tests/parser/line-cont5.014
-rw-r--r--bin/sh/tests/parser/line-cont6.023
-rw-r--r--bin/sh/tests/parser/line-cont7.07
-rw-r--r--bin/sh/tests/parser/line-cont8.06
-rw-r--r--bin/sh/tests/parser/line-cont9.06
-rw-r--r--bin/sh/tests/set-e/Makefile8
-rw-r--r--bin/sh/trap.c35
56 files changed, 845 insertions, 419 deletions
diff --git a/bin/sh/arith_yacc.c b/bin/sh/arith_yacc.c
index 815d885..5000c6b 100644
--- a/bin/sh/arith_yacc.c
+++ b/bin/sh/arith_yacc.c
@@ -139,9 +139,9 @@ static arith_t do_binop(int op, arith_t a, arith_t b)
case ARITH_SUB:
return (uintmax_t)a - (uintmax_t)b;
case ARITH_LSHIFT:
- return (uintmax_t)a << b;
+ return (uintmax_t)a << (b & (sizeof(uintmax_t) * CHAR_BIT - 1));
case ARITH_RSHIFT:
- return a >> b;
+ return a >> (b & (sizeof(uintmax_t) * CHAR_BIT - 1));
case ARITH_LT:
return a < b;
case ARITH_LE:
diff --git a/bin/sh/eval.c b/bin/sh/eval.c
index 3fd3050..c1a9cdb 100644
--- a/bin/sh/eval.c
+++ b/bin/sh/eval.c
@@ -168,6 +168,8 @@ evalstring(char *s, int flags)
else
evaltree(n, flags);
any = 1;
+ if (evalskip)
+ break;
}
popstackmark(&smark);
setstackmark(&smark);
@@ -316,9 +318,10 @@ evalloop(union node *n, int flags)
loopnest++;
status = 0;
for (;;) {
- evaltree(n->nbinary.ch1, EV_TESTED);
+ if (!evalskip)
+ evaltree(n->nbinary.ch1, EV_TESTED);
if (evalskip) {
-skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
+ if (evalskip == SKIPCONT && --skipcount <= 0) {
evalskip = 0;
continue;
}
@@ -337,8 +340,6 @@ skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
}
evaltree(n->nbinary.ch2, flags);
status = exitstatus;
- if (evalskip)
- goto skipping;
}
loopnest--;
exitstatus = status;
@@ -648,15 +649,15 @@ evalbackcmd(union node *n, struct backcmd *result)
struct jmploc *savehandler;
struct localvar *savelocalvars;
- setstackmark(&smark);
result->fd = -1;
result->buf = NULL;
result->nleft = 0;
result->jp = NULL;
if (n == NULL) {
exitstatus = 0;
- goto out;
+ return;
}
+ setstackmark(&smark);
exitstatus = oexitstatus;
if (is_valid_fast_cmdsubst(n)) {
savelocalvars = localvars;
@@ -698,7 +699,6 @@ evalbackcmd(union node *n, struct backcmd *result)
result->fd = pip[0];
result->jp = jp;
}
-out:
popstackmark(&smark);
TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n",
result->fd, result->buf, result->nleft, result->jp));
@@ -1039,6 +1039,7 @@ evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
shellparam.reset = 1;
shellparam.nparam = argc - 1;
shellparam.p = argv + 1;
+ shellparam.optp = NULL;
shellparam.optnext = NULL;
INTOFF;
savelocalvars = localvars;
diff --git a/bin/sh/expand.c b/bin/sh/expand.c
index 15afc9c..d4c4c5a 100644
--- a/bin/sh/expand.c
+++ b/bin/sh/expand.c
@@ -328,24 +328,19 @@ exptilde(char *p, int flag)
done:
*p = '\0';
if (*(startp+1) == '\0') {
- if ((home = lookupvar("HOME")) == NULL)
- goto lose;
+ home = lookupvar("HOME");
} else {
- if ((pw = getpwnam(startp+1)) == NULL)
- goto lose;
- home = pw->pw_dir;
+ pw = getpwnam(startp+1);
+ home = pw != NULL ? pw->pw_dir : NULL;
}
- if (*home == '\0')
- goto lose;
*p = c;
+ if (home == NULL || *home == '\0')
+ return (startp);
if (quotes)
STPUTS_QUOTES(home, SQSYNTAX, expdest);
else
STPUTS(home, expdest);
return (p);
-lose:
- *p = c;
- return (startp);
}
@@ -867,7 +862,7 @@ varisset(const char *name, int nulok)
static void
strtodest(const char *p, int flag, int subtype, int quoted)
{
- if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH)
+ if (flag & (EXP_FULL | EXP_CASE | EXP_REDIR) && subtype != VSLENGTH)
STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest);
else
STPUTS(p, expdest);
@@ -883,30 +878,28 @@ varvalue(const char *name, int quoted, int subtype, int flag)
int num;
char *p;
int i;
- char sep;
+ char sep[2];
char **ap;
switch (*name) {
case '$':
num = rootpid;
- goto numvar;
+ break;
case '?':
num = oexitstatus;
- goto numvar;
+ break;
case '#':
num = shellparam.nparam;
- goto numvar;
+ break;
case '!':
num = backgndpidval();
-numvar:
- expdest = cvtnum(num, expdest);
break;
case '-':
for (i = 0 ; i < NOPTS ; i++) {
if (optlist[i].val)
STPUTC(optlist[i].letter, expdest);
}
- break;
+ return;
case '@':
if (flag & EXP_FULL && quoted) {
for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
@@ -914,22 +907,25 @@ numvar:
if (*ap)
STPUTC('\0', expdest);
}
- break;
+ return;
}
/* FALLTHROUGH */
case '*':
if (ifsset())
- sep = ifsval()[0];
+ sep[0] = ifsval()[0];
else
- sep = ' ';
+ sep[0] = ' ';
+ sep[1] = '\0';
for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
strtodest(p, flag, subtype, quoted);
if (!*ap)
break;
- if (sep || (flag & EXP_FULL && !quoted && **ap != '\0'))
- STPUTC(sep, expdest);
+ if (sep[0])
+ strtodest(sep, flag, subtype, quoted);
+ else if (flag & EXP_FULL && !quoted && **ap != '\0')
+ STPUTC('\0', expdest);
}
- break;
+ return;
default:
if (is_digit(*name)) {
num = atoi(name);
@@ -938,11 +934,12 @@ numvar:
else if (num > 0 && num <= shellparam.nparam)
p = shellparam.p[num - 1];
else
- break;
+ return;
strtodest(p, flag, subtype, quoted);
}
- break;
+ return;
}
+ expdest = cvtnum(num, expdest);
}
@@ -1110,24 +1107,23 @@ expandmeta(struct strlist *str, int flag __unused)
/* TODO - EXP_REDIR */
while (str) {
- if (fflag)
- goto nometa;
- p = str->text;
- for (;;) { /* fast check for meta chars */
- if ((c = *p++) == '\0')
- goto nometa;
- if (c == '*' || c == '?' || c == '[')
- break;
- }
savelastp = exparg.lastp;
- INTOFF;
- expmeta(expdir, str->text);
- INTON;
+ if (!fflag) {
+ p = str->text;
+ for (; (c = *p) != '\0'; p++) {
+ /* fast check for meta chars */
+ if (c == '*' || c == '?' || c == '[') {
+ INTOFF;
+ expmeta(expdir, str->text);
+ INTON;
+ break;
+ }
+ }
+ }
if (exparg.lastp == savelastp) {
/*
* no matches
*/
-nometa:
*exparg.lastp = str;
rmescapes(str->text);
exparg.lastp = &str->next;
diff --git a/bin/sh/histedit.c b/bin/sh/histedit.c
index a8c376a..c65d1c7 100644
--- a/bin/sh/histedit.c
+++ b/bin/sh/histedit.c
@@ -166,9 +166,10 @@ sethistsize(const char *hs)
HistEvent he;
if (hist != NULL) {
- if (hs == NULL || *hs == '\0' ||
- (histsize = atoi(hs)) < 0)
+ if (hs == NULL || !is_number(hs))
histsize = 100;
+ else
+ histsize = atoi(hs);
history(hist, &he, H_SETSIZE, histsize);
history(hist, &he, H_SETUNIQUE, 1);
}
diff --git a/bin/sh/input.c b/bin/sh/input.c
index 05cc1eb..412f144 100644
--- a/bin/sh/input.c
+++ b/bin/sh/input.c
@@ -116,33 +116,6 @@ resetinput(void)
}
-/*
- * Read a line from the script.
- */
-
-char *
-pfgets(char *line, int len)
-{
- char *p = line;
- int nleft = len;
- int c;
-
- while (--nleft > 0) {
- c = pgetc_macro();
- if (c == PEOF) {
- if (p == line)
- return NULL;
- break;
- }
- *p++ = c;
- if (c == '\n')
- break;
- }
- *p = '\0';
- return line;
-}
-
-
/*
* Read a character from the script, returning PEOF on end of file.
@@ -338,7 +311,7 @@ pungetc(void)
* We handle aliases this way.
*/
void
-pushstring(char *s, int len, struct alias *ap)
+pushstring(const char *s, int len, struct alias *ap)
{
struct strpush *sp;
diff --git a/bin/sh/input.h b/bin/sh/input.h
index cc54eed..cb0af77 100644
--- a/bin/sh/input.h
+++ b/bin/sh/input.h
@@ -48,12 +48,11 @@ struct alias;
struct parsefile;
void resetinput(void);
-char *pfgets(char *, int);
int pgetc(void);
int preadbuffer(void);
int preadateof(void);
void pungetc(void);
-void pushstring(char *, int, struct alias *);
+void pushstring(const char *, int, struct alias *);
void setinputfile(const char *, int);
void setinputfd(int, int);
void setinputstring(const char *, int);
diff --git a/bin/sh/jobs.c b/bin/sh/jobs.c
index 93553c1..765e6a2 100644
--- a/bin/sh/jobs.c
+++ b/bin/sh/jobs.c
@@ -118,6 +118,24 @@ static void showjob(struct job *, int);
static int jobctl;
#if JOBS
+static void
+jobctl_notty(void)
+{
+ if (ttyfd >= 0) {
+ close(ttyfd);
+ ttyfd = -1;
+ }
+ if (!iflag) {
+ setsignal(SIGTSTP);
+ setsignal(SIGTTOU);
+ setsignal(SIGTTIN);
+ jobctl = 1;
+ return;
+ }
+ out2fmt_flush("sh: can't access tty; job control turned off\n");
+ mflag = 0;
+}
+
void
setjobctl(int on)
{
@@ -133,8 +151,10 @@ setjobctl(int on)
while (i <= 2 && !isatty(i))
i++;
if (i > 2 ||
- (ttyfd = fcntl(i, F_DUPFD_CLOEXEC, 10)) < 0)
- goto out;
+ (ttyfd = fcntl(i, F_DUPFD_CLOEXEC, 10)) < 0) {
+ jobctl_notty();
+ return;
+ }
}
if (ttyfd < 10) {
/*
@@ -142,9 +162,8 @@ setjobctl(int on)
* the user's redirections.
*/
if ((i = fcntl(ttyfd, F_DUPFD_CLOEXEC, 10)) < 0) {
- close(ttyfd);
- ttyfd = -1;
- goto out;
+ jobctl_notty();
+ return;
}
close(ttyfd);
ttyfd = i;
@@ -152,11 +171,15 @@ setjobctl(int on)
do { /* while we are in the background */
initialpgrp = tcgetpgrp(ttyfd);
if (initialpgrp < 0) {
-out: out2fmt_flush("sh: can't access tty; job control turned off\n");
- mflag = 0;
+ jobctl_notty();
return;
}
if (initialpgrp != getpgrp()) {
+ if (!iflag) {
+ initialpgrp = -1;
+ jobctl_notty();
+ return;
+ }
kill(0, SIGTTIN);
continue;
}
@@ -168,9 +191,11 @@ out: out2fmt_flush("sh: can't access tty; job control turned off\n");
tcsetpgrp(ttyfd, rootpid);
} else { /* turning job control off */
setpgid(0, initialpgrp);
- tcsetpgrp(ttyfd, initialpgrp);
- close(ttyfd);
- ttyfd = -1;
+ if (ttyfd >= 0) {
+ tcsetpgrp(ttyfd, initialpgrp);
+ close(ttyfd);
+ ttyfd = -1;
+ }
setsignal(SIGTSTP);
setsignal(SIGTTOU);
setsignal(SIGTTIN);
@@ -195,7 +220,8 @@ fgcmd(int argc __unused, char **argv __unused)
printjobcmd(jp);
flushout(&output);
pgrp = jp->ps[0].pid;
- tcsetpgrp(ttyfd, pgrp);
+ if (ttyfd >= 0)
+ tcsetpgrp(ttyfd, pgrp);
restartjob(jp);
jp->foreground = 1;
INTOFF;
@@ -347,13 +373,13 @@ showjob(struct job *jp, int mode)
strcat(statestr, " (core dumped)");
}
- for (ps = jp->ps ; ; ps++) { /* for each process */
+ for (ps = jp->ps ; procno > 0 ; ps++, procno--) { /* for each process */
if (mode == SHOWJOBS_PIDS || mode == SHOWJOBS_PGIDS) {
out1fmt("%d\n", (int)ps->pid);
- goto skip;
+ continue;
}
if (mode != SHOWJOBS_VERBOSE && ps != jp->ps)
- goto skip;
+ continue;
if (jobno == curr && ps == jp->ps)
c = '+';
else if (jobno == prev && ps == jp->ps)
@@ -384,8 +410,6 @@ showjob(struct job *jp, int mode)
out1c('\n');
} else
printjobcmd(jp);
-skip: if (--procno <= 0)
- break;
}
}
@@ -568,23 +592,23 @@ getjob_nonotfound(const char *name)
if (name == NULL) {
#if JOBS
-currentjob: if ((jp = getcurjob(NULL)) == NULL)
- error("No current job");
- return (jp);
+ name = "%+";
#else
error("No current job");
#endif
- } else if (name[0] == '%') {
+ }
+ if (name[0] == '%') {
if (is_digit(name[1])) {
jobno = number(name + 1);
if (jobno > 0 && jobno <= njobs
&& jobtab[jobno - 1].used != 0)
return &jobtab[jobno - 1];
#if JOBS
- } else if (name[1] == '%' && name[2] == '\0') {
- goto currentjob;
- } else if (name[1] == '+' && name[2] == '\0') {
- goto currentjob;
+ } else if ((name[1] == '%' || name[1] == '+') &&
+ name[2] == '\0') {
+ if ((jp = getcurjob(NULL)) == NULL)
+ error("No current job");
+ return (jp);
} else if (name[1] == '-' && name[2] == '\0') {
if ((jp = getcurjob(NULL)) == NULL ||
(jp = getcurjob(jp)) == NULL)
@@ -847,7 +871,8 @@ forkshell(struct job *jp, union node *n, int mode)
pgrp = getpid();
else
pgrp = jp->ps[0].pid;
- if (setpgid(0, pgrp) == 0 && mode == FORK_FG) {
+ if (setpgid(0, pgrp) == 0 && mode == FORK_FG &&
+ ttyfd >= 0) {
/*** this causes superfluous TIOCSPGRPS ***/
if (tcsetpgrp(ttyfd, pgrp) < 0)
error("tcsetpgrp failed, errno=%d", errno);
@@ -1007,7 +1032,7 @@ waitforjob(struct job *jp, int *origstatus)
dotrap();
#if JOBS
if (jp->jobctl) {
- if (tcsetpgrp(ttyfd, rootpid) < 0)
+ if (ttyfd >= 0 && tcsetpgrp(ttyfd, rootpid) < 0)
error("tcsetpgrp failed, errno=%d\n", errno);
}
if (jp->state == JOBSTOPPED)
@@ -1263,13 +1288,43 @@ commandtext(union node *n)
static void
+cmdtxtdogroup(union node *n)
+{
+ cmdputs("; do ");
+ cmdtxt(n);
+ cmdputs("; done");
+}
+
+
+static void
+cmdtxtredir(union node *n, const char *op, int deffd)
+{
+ char s[2];
+
+ if (n->nfile.fd != deffd) {
+ s[0] = n->nfile.fd + '0';
+ s[1] = '\0';
+ cmdputs(s);
+ }
+ cmdputs(op);
+ if (n->type == NTOFD || n->type == NFROMFD) {
+ if (n->ndup.dupfd >= 0)
+ s[0] = n->ndup.dupfd + '0';
+ else
+ s[0] = '-';
+ s[1] = '\0';
+ cmdputs(s);
+ } else {
+ cmdtxt(n->nfile.fname);
+ }
+}
+
+
+static void
cmdtxt(union node *n)
{
union node *np;
struct nodelist *lp;
- const char *p;
- int i;
- char s[2];
if (n == NULL)
return;
@@ -1314,14 +1369,13 @@ cmdtxt(union node *n)
break;
case NWHILE:
cmdputs("while ");
- goto until;
+ cmdtxt(n->nbinary.ch1);
+ cmdtxtdogroup(n->nbinary.ch2);
+ break;
case NUNTIL:
cmdputs("until ");
-until:
cmdtxt(n->nbinary.ch1);
- cmdputs("; do ");
- cmdtxt(n->nbinary.ch2);
- cmdputs("; done");
+ cmdtxtdogroup(n->nbinary.ch2);
break;
case NFOR:
cmdputs("for ");
@@ -1356,36 +1410,25 @@ until:
cmdputs(n->narg.text);
break;
case NTO:
- p = ">"; i = 1; goto redir;
+ cmdtxtredir(n, ">", 1);
+ break;
case NAPPEND:
- p = ">>"; i = 1; goto redir;
+ cmdtxtredir(n, ">>", 1);
+ break;
case NTOFD:
- p = ">&"; i = 1; goto redir;
+ cmdtxtredir(n, ">&", 1);
+ break;
case NCLOBBER:
- p = ">|"; i = 1; goto redir;
+ cmdtxtredir(n, ">|", 1);
+ break;
case NFROM:
- p = "<"; i = 0; goto redir;
+ cmdtxtredir(n, "<", 0);
+ break;
case NFROMTO:
- p = "<>"; i = 0; goto redir;
+ cmdtxtredir(n, "<>", 0);
+ break;
case NFROMFD:
- p = "<&"; i = 0; goto redir;
-redir:
- if (n->nfile.fd != i) {
- s[0] = n->nfile.fd + '0';
- s[1] = '\0';
- cmdputs(s);
- }
- cmdputs(p);
- if (n->type == NTOFD || n->type == NFROMFD) {
- if (n->ndup.dupfd >= 0)
- s[0] = n->ndup.dupfd + '0';
- else
- s[0] = '-';
- s[1] = '\0';
- cmdputs(s);
- } else {
- cmdtxt(n->nfile.fname);
- }
+ cmdtxtredir(n, "<&", 0);
break;
case NHERE:
case NXHERE:
diff --git a/bin/sh/mystring.c b/bin/sh/mystring.c
index 03ea8ba..19de78d 100644
--- a/bin/sh/mystring.c
+++ b/bin/sh/mystring.c
@@ -82,9 +82,17 @@ number(const char *s)
int
is_number(const char *p)
{
- do {
- if (! is_digit(*p))
+ const char *q;
+
+ if (*p == '\0')
+ return 0;
+ while (*p == '0')
+ p++;
+ for (q = p; *q != '\0'; q++)
+ if (! is_digit(*q))
return 0;
- } while (*++p != '\0');
+ if (q - p > 10 ||
+ (q - p == 10 && memcmp(p, "2147483647", 10) > 0))
+ return 0;
return 1;
}
diff --git a/bin/sh/options.c b/bin/sh/options.c
index bf00a4e..860cf6c 100644
--- a/bin/sh/options.c
+++ b/bin/sh/options.c
@@ -325,6 +325,7 @@ setparam(char **argv)
shellparam.malloc = 1;
shellparam.nparam = nparam;
shellparam.p = newparam;
+ shellparam.optp = NULL;
shellparam.reset = 1;
shellparam.optnext = NULL;
}
@@ -344,6 +345,11 @@ freeparam(struct shparam *param)
ckfree(*ap);
ckfree(param->p);
}
+ if (param->optp) {
+ for (ap = param->optp ; *ap ; ap++)
+ ckfree(*ap);
+ ckfree(param->optp);
+ }
}
@@ -417,20 +423,33 @@ getoptsreset(const char *value)
int
getoptscmd(int argc, char **argv)
{
- char **optbase = NULL;
+ char **optbase = NULL, **ap;
+ int i;
if (argc < 3)
error("usage: getopts optstring var [arg]");
- else if (argc == 3)
- optbase = shellparam.p;
- else
- optbase = &argv[3];
if (shellparam.reset == 1) {
+ INTOFF;
+ if (shellparam.optp) {
+ for (ap = shellparam.optp ; *ap ; ap++)
+ ckfree(*ap);
+ ckfree(shellparam.optp);
+ shellparam.optp = NULL;
+ }
+ if (argc > 3) {
+ shellparam.optp = ckmalloc((argc - 2) * sizeof *ap);
+ memset(shellparam.optp, '\0', (argc - 2) * sizeof *ap);
+ for (i = 0; i < argc - 3; i++)
+ shellparam.optp[i] = savestr(argv[i + 3]);
+ }
+ INTON;
+ optbase = argc == 3 ? shellparam.p : shellparam.optp;
shellparam.optnext = optbase;
shellparam.optptr = NULL;
shellparam.reset = 0;
- }
+ } else
+ optbase = shellparam.optp ? shellparam.optp : shellparam.p;
return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
&shellparam.optptr);
diff --git a/bin/sh/options.h b/bin/sh/options.h
index acc2a11..0994862 100644
--- a/bin/sh/options.h
+++ b/bin/sh/options.h
@@ -38,6 +38,7 @@ struct shparam {
unsigned char malloc; /* if parameter list dynamically allocated */
unsigned char reset; /* if getopts has been reset */
char **p; /* parameter list */
+ char **optp; /* parameter list for getopts */
char **optnext; /* next parameter to be processed by getopts */
char *optptr; /* used by getopts */
};
diff --git a/bin/sh/parser.c b/bin/sh/parser.c
index e6e9f82..7fae29e 100644
--- a/bin/sh/parser.c
+++ b/bin/sh/parser.c
@@ -66,7 +66,6 @@ __FBSDID("$FreeBSD$");
* Shell command parser.
*/
-#define EOFMARKLEN 79
#define PROMPTLEN 128
/* values of checkkwd variable */
@@ -126,6 +125,7 @@ static void consumetoken(int);
static void synexpect(int) __dead2;
static void synerror(const char *) __dead2;
static void setprompt(int);
+static int pgetc_linecont(void);
static void *
@@ -718,7 +718,6 @@ parsefname(void)
if (n->type == NHERE) {
struct heredoc *here = heredoc;
struct heredoc *p;
- int i;
if (quoteflag == 0)
n->type = NXHERE;
@@ -727,7 +726,7 @@ parsefname(void)
while (*wordtext == '\t')
wordtext++;
}
- if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
+ if (! noexpand(wordtext))
synerror("Illegal eof marker for << redirection");
rmescapes(wordtext);
here->eofmark = wordtext;
@@ -891,7 +890,9 @@ xxreadtoken(void)
continue;
}
pungetc();
- goto breakloop;
+ /* FALLTHROUGH */
+ default:
+ return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
case '\n':
plinno++;
needprompt = doprompt;
@@ -899,17 +900,17 @@ xxreadtoken(void)
case PEOF:
RETURN(TEOF);
case '&':
- if (pgetc() == '&')
+ if (pgetc_linecont() == '&')
RETURN(TAND);
pungetc();
RETURN(TBACKGND);
case '|':
- if (pgetc() == '|')
+ if (pgetc_linecont() == '|')
RETURN(TOR);
pungetc();
RETURN(TPIPE);
case ';':
- c = pgetc();
+ c = pgetc_linecont();
if (c == ';')
RETURN(TENDCASE);
else if (c == '&')
@@ -920,12 +921,8 @@ xxreadtoken(void)
RETURN(TLP);
case ')':
RETURN(TRP);
- default:
- goto breakloop;
}
}
-breakloop:
- return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
#undef RETURN
}
@@ -946,6 +943,98 @@ struct tokenstate
/*
+ * Check to see whether we are at the end of the here document. When this
+ * is called, c is set to the first character of the next input line. If
+ * we are at the end of the here document, this routine sets the c to PEOF.
+ * The new value of c is returned.
+ */
+
+static int
+checkend(int c, const char *eofmark, int striptabs)
+{
+ if (striptabs) {
+ while (c == '\t')
+ c = pgetc();
+ }
+ if (c == *eofmark) {
+ int c2;
+ const char *q;
+
+ for (q = eofmark + 1; c2 = pgetc(), *q != '\0' && c2 == *q; q++)
+ ;
+ if ((c2 == PEOF || c2 == '\n') && *q == '\0') {
+ c = PEOF;
+ if (c2 == '\n') {
+ plinno++;
+ needprompt = doprompt;
+ }
+ } else {
+ pungetc();
+ pushstring(eofmark + 1, q - (eofmark + 1), NULL);
+ }
+ }
+ return (c);
+}
+
+
+/*
+ * Parse a redirection operator. The variable "out" points to a string
+ * specifying the fd to be redirected. The variable "c" contains the
+ * first character of the redirection operator.
+ */
+
+static void
+parseredir(char *out, int c)
+{
+ char fd = *out;
+ union node *np;
+
+ np = (union node *)stalloc(sizeof (struct nfile));
+ if (c == '>') {
+ np->nfile.fd = 1;
+ c = pgetc_linecont();
+ if (c == '>')
+ np->type = NAPPEND;
+ else if (c == '&')
+ np->type = NTOFD;
+ else if (c == '|')
+ np->type = NCLOBBER;
+ else {
+ np->type = NTO;
+ pungetc();
+ }
+ } else { /* c == '<' */
+ np->nfile.fd = 0;
+ c = pgetc_linecont();
+ if (c == '<') {
+ if (sizeof (struct nfile) != sizeof (struct nhere)) {
+ np = (union node *)stalloc(sizeof (struct nhere));
+ np->nfile.fd = 0;
+ }
+ np->type = NHERE;
+ heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
+ heredoc->here = np;
+ if ((c = pgetc_linecont()) == '-') {
+ heredoc->striptabs = 1;
+ } else {
+ heredoc->striptabs = 0;
+ pungetc();
+ }
+ } else if (c == '&')
+ np->type = NFROMFD;
+ else if (c == '>')
+ np->type = NFROMTO;
+ else {
+ np->type = NFROM;
+ pungetc();
+ }
+ }
+ if (fd != '\0')
+ np->nfile.fd = digit_val(fd);
+ redirnode = np;
+}
+
+/*
* Called to parse command substitutions.
*/
@@ -1006,25 +1095,12 @@ parsebackq(char *out, struct nodelist **pbqlist,
needprompt = 0;
}
CHECKSTRSPACE(2, oout);
- switch (c = pgetc()) {
- case '`':
- goto done;
-
+ c = pgetc_linecont();
+ if (c == '`')
+ break;
+ switch (c) {
case '\\':
- if ((c = pgetc()) == '\n') {
- plinno++;
- if (doprompt)
- setprompt(2);
- else
- setprompt(0);
- /*
- * If eating a newline, avoid putting
- * the newline into the new character
- * stream (via the USTPUTC after the
- * switch).
- */
- continue;
- }
+ c = pgetc();
if (c != '\\' && c != '`' && c != '$'
&& (!dblquote || c != '"'))
USTPUTC('\\', oout);
@@ -1045,7 +1121,6 @@ parsebackq(char *out, struct nodelist **pbqlist,
}
USTPUTC(c, oout);
}
-done:
USTPUTC('\0', oout);
olen = oout - stackblock();
INTOFF;
@@ -1246,6 +1321,13 @@ readcstyleesc(char *out)
c = pgetc();
if (c == PEOF)
synerror("Unterminated quoted string");
+ if (c == '\n') {
+ plinno++;
+ if (doprompt)
+ setprompt(2);
+ else
+ setprompt(0);
+ }
}
pungetc();
return out;
@@ -1269,8 +1351,6 @@ readcstyleesc(char *out)
* will run code that appears at the end of readtoken1.
*/
-#define CHECKEND() {goto checkend; checkend_return:;}
-#define PARSEREDIR() {goto parseredir; parseredir_return:;}
#define PARSESUB() {goto parsesub; parsesub_return:;}
#define PARSEARITH() {goto parsearith; parsearith_return:;}
@@ -1281,7 +1361,6 @@ readtoken1(int firstc, char const *initialsyntax, const char *eofmark,
int c = firstc;
char *out;
int len;
- char line[EOFMARKLEN + 1];
struct nodelist *bqlist;
int quotef;
int newvarnest;
@@ -1303,7 +1382,9 @@ readtoken1(int firstc, char const *initialsyntax, const char *eofmark,
STARTSTACKSTR(out);
loop: { /* for each line, until end of word */
- CHECKEND(); /* set c to PEOF if at end of here document */
+ if (eofmark)
+ /* set c to PEOF if at end of here document */
+ c = checkend(c, eofmark, striptabs);
for (;;) { /* until end of line or end of word */
CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
@@ -1414,7 +1495,7 @@ readtoken1(int firstc, char const *initialsyntax, const char *eofmark,
USTPUTC(c, out);
--state[level].parenlevel;
} else {
- if (pgetc() == ')') {
+ if (pgetc_linecont() == ')') {
if (level > 0 &&
state[level].category == TSTATE_ARITH) {
level--;
@@ -1469,7 +1550,7 @@ endword:
&& quotef == 0
&& len <= 2
&& (*out == '\0' || is_digit(*out))) {
- PARSEREDIR();
+ parseredir(out, c);
return lasttoken = TREDIR;
} else {
pungetc();
@@ -1484,97 +1565,6 @@ endword:
/*
- * Check to see whether we are at the end of the here document. When this
- * is called, c is set to the first character of the next input line. If
- * we are at the end of the here document, this routine sets the c to PEOF.
- */
-
-checkend: {
- if (eofmark) {
- if (striptabs) {
- while (c == '\t')
- c = pgetc();
- }
- if (c == *eofmark) {
- if (pfgets(line, sizeof line) != NULL) {
- const char *p, *q;
-
- p = line;
- for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
- if ((*p == '\0' || *p == '\n') && *q == '\0') {
- c = PEOF;
- if (*p == '\n') {
- plinno++;
- needprompt = doprompt;
- }
- } else {
- pushstring(line, strlen(line), NULL);
- }
- }
- }
- }
- goto checkend_return;
-}
-
-
-/*
- * Parse a redirection operator. The variable "out" points to a string
- * specifying the fd to be redirected. The variable "c" contains the
- * first character of the redirection operator.
- */
-
-parseredir: {
- char fd = *out;
- union node *np;
-
- np = (union node *)stalloc(sizeof (struct nfile));
- if (c == '>') {
- np->nfile.fd = 1;
- c = pgetc();
- if (c == '>')
- np->type = NAPPEND;
- else if (c == '&')
- np->type = NTOFD;
- else if (c == '|')
- np->type = NCLOBBER;
- else {
- np->type = NTO;
- pungetc();
- }
- } else { /* c == '<' */
- np->nfile.fd = 0;
- c = pgetc();
- if (c == '<') {
- if (sizeof (struct nfile) != sizeof (struct nhere)) {
- np = (union node *)stalloc(sizeof (struct nhere));
- np->nfile.fd = 0;
- }
- np->type = NHERE;
- heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
- heredoc->here = np;
- if ((c = pgetc()) == '-') {
- heredoc->striptabs = 1;
- } else {
- heredoc->striptabs = 0;
- pungetc();
- }
- } else if (c == '&')
- np->type = NFROMFD;
- else if (c == '>')
- np->type = NFROMTO;
- else {
- np->type = NFROM;
- pungetc();
- }
- }
- if (fd != '\0')
- np->nfile.fd = digit_val(fd);
- redirnode = np;
- goto parseredir_return;
-}
-
-
-/*
* Parse a substitution. At this point, we have read the dollar sign
* and nothing else.
*/
@@ -1591,9 +1581,9 @@ parsesub: {
int length;
int c1;
- c = pgetc();
+ c = pgetc_linecont();
if (c == '(') { /* $(command) or $((arith)) */
- if (pgetc() == '(') {
+ if (pgetc_linecont() == '(') {
PARSEARITH();
} else {
pungetc();
@@ -1611,7 +1601,7 @@ parsesub: {
flags = 0;
if (c == '{') {
bracketed_name = 1;
- c = pgetc();
+ c = pgetc_linecont();
subtype = 0;
}
varname:
@@ -1619,7 +1609,7 @@ varname:
length = 0;
do {
STPUTC(c, out);
- c = pgetc();
+ c = pgetc_linecont();
length++;
} while (!is_eof(c) && is_in_name(c));
if (length == 6 &&
@@ -1638,22 +1628,22 @@ varname:
if (bracketed_name) {
do {
STPUTC(c, out);
- c = pgetc();
+ c = pgetc_linecont();
} while (is_digit(c));
} else {
STPUTC(c, out);
- c = pgetc();
+ c = pgetc_linecont();
}
} else if (is_special(c)) {
c1 = c;
- c = pgetc();
+ c = pgetc_linecont();
if (subtype == 0 && c1 == '#') {
subtype = VSLENGTH;
if (strchr(types, c) == NULL && c != ':' &&
c != '#' && c != '%')
goto varname;
c1 = c;
- c = pgetc();
+ c = pgetc_linecont();
if (c1 != '}' && c == '}') {
pungetc();
c = c1;
@@ -1678,7 +1668,7 @@ varname:
switch (c) {
case ':':
flags |= VSNUL;
- c = pgetc();
+ c = pgetc_linecont();
/*FALLTHROUGH*/
default:
p = strchr(types, c);
@@ -1698,7 +1688,7 @@ varname:
int cc = c;
subtype = c == '#' ? VSTRIMLEFT :
VSTRIMRIGHT;
- c = pgetc();
+ c = pgetc_linecont();
if (c == cc)
subtype++;
else
@@ -1907,6 +1897,29 @@ setprompt(int which)
}
}
+static int
+pgetc_linecont(void)
+{
+ int c;
+
+ while ((c = pgetc_macro()) == '\\') {
+ c = pgetc();
+ if (c == '\n') {
+ plinno++;
+ if (doprompt)
+ setprompt(2);
+ else
+ setprompt(0);
+ } else {
+ pungetc();
+ /* Allow the backslash to be pushed back. */
+ pushstring("\\", 1, NULL);
+ return (pgetc());
+ }
+ }
+ return (c);
+}
+
/*
* called by editline -- any expansions to the prompt
* should be added here.
@@ -1915,7 +1928,7 @@ char *
getprompt(void *unused __unused)
{
static char ps[PROMPTLEN];
- char *fmt;
+ const char *fmt;
const char *pwd;
int i, trim;
static char internal_error[] = "??";
@@ -2029,7 +2042,7 @@ expandstr(const char *ps)
parser_temp = NULL;
setinputstring(ps, 1);
doprompt = 0;
- readtoken1(pgetc(), DQSYNTAX, "\n\n", 0);
+ readtoken1(pgetc(), DQSYNTAX, "", 0);
if (backquotelist != NULL)
error("Command substitution not allowed here");
diff --git a/bin/sh/redir.c b/bin/sh/redir.c
index 6127e86..95d3238 100644
--- a/bin/sh/redir.c
+++ b/bin/sh/redir.c
@@ -173,21 +173,12 @@ openredirect(union node *redir, char memory[10])
fname = redir->nfile.expfname;
if ((f = open(fname, O_RDONLY)) < 0)
error("cannot open %s: %s", fname, strerror(errno));
-movefd:
- if (f != fd) {
- if (dup2(f, fd) == -1) {
- e = errno;
- close(f);
- error("%d: %s", fd, strerror(e));
- }
- close(f);
- }
break;
case NFROMTO:
fname = redir->nfile.expfname;
if ((f = open(fname, O_RDWR|O_CREAT, 0666)) < 0)
error("cannot create %s: %s", fname, strerror(errno));
- goto movefd;
+ break;
case NTO:
if (Cflag) {
fname = redir->nfile.expfname;
@@ -205,19 +196,19 @@ movefd:
} else
error("cannot create %s: %s", fname,
strerror(EEXIST));
- goto movefd;
+ break;
}
/* FALLTHROUGH */
case NCLOBBER:
fname = redir->nfile.expfname;
if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
error("cannot create %s: %s", fname, strerror(errno));
- goto movefd;
+ break;
case NAPPEND:
fname = redir->nfile.expfname;
if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
error("cannot create %s: %s", fname, strerror(errno));
- goto movefd;
+ break;
case NTOFD:
case NFROMFD:
if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
@@ -231,14 +222,22 @@ movefd:
} else {
close(fd);
}
- break;
+ return;
case NHERE:
case NXHERE:
f = openhere(redir);
- goto movefd;
+ break;
default:
abort();
}
+ if (f != fd) {
+ if (dup2(f, fd) == -1) {
+ e = errno;
+ close(f);
+ error("%d: %s", fd, strerror(e));
+ }
+ close(f);
+ }
}
diff --git a/bin/sh/sh.1 b/bin/sh/sh.1
index 1c3f8fb..fa68720 100644
--- a/bin/sh/sh.1
+++ b/bin/sh/sh.1
@@ -32,7 +32,7 @@
.\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95
.\" $FreeBSD$
.\"
-.Dd January 26, 2014
+.Dd November 14, 2014
.Dt SH 1
.Os
.Sh NAME
@@ -259,6 +259,12 @@ from input when in interactive mode.
Force the shell to behave interactively.
.It Fl m Li monitor
Turn on job control (set automatically when interactive).
+A new process group is created for each pipeline (called a job).
+It is possible to suspend jobs or to have them run in the foreground or
+in the background.
+In a non-interactive shell,
+this option can be set even if no terminal is available
+and is useful to place processes in separate process groups.
.It Fl n Li noexec
If not interactive, read commands but do not
execute them.
@@ -584,7 +590,8 @@ the following actions:
Leading words of the form
.Dq Li name=value
are stripped off and assigned to the environment of
-the simple command.
+the simple command
+(they do not affect expansions).
Redirection operators and
their arguments (as described below) are stripped
off and saved for processing.
@@ -788,10 +795,13 @@ should indicate the various exit codes and what they mean.
Additionally, the built-in commands return exit codes, as does
an executed shell function.
.Pp
-If a command is terminated by a signal, its exit status is 128 plus
-the signal number.
-Signal numbers are defined in the header file
-.In sys/signal.h .
+If a command is terminated by a signal, its exit status is greater than 128.
+The signal name can be found by passing the exit status to
+.Li kill -l .
+.Pp
+If there is no command word,
+the exit status is the exit status of the last command substitution executed,
+or zero if the command does not contain any command substitutions.
.Ss Complex Commands
Complex commands are combinations of simple commands
with control operators or keywords, together creating a larger complex
@@ -811,7 +821,8 @@ function definition
.El
.Pp
Unless otherwise stated, the exit status of a command is
-that of the last simple command executed by the command.
+that of the last simple command executed by the command,
+or zero if no simple command was executed.
.Ss Pipelines
A pipeline is a sequence of one or more commands separated
by the control operator
@@ -895,6 +906,8 @@ The format for running a command in background is:
If the shell is not interactive, the standard input of an
asynchronous command is set to
.Pa /dev/null .
+.Pp
+The exit status is zero.
.Ss Lists (Generally Speaking)
A list is a sequence of zero or more commands separated by
newlines, semicolons, or ampersands,
@@ -933,6 +946,13 @@ command is:
.Ic fi
.Ed
.Pp
+The exit status is that of selected
+.Ic then
+or
+.Ic else
+list,
+or zero if no list was selected.
+.Pp
The syntax of the
.Ic while
command is:
@@ -953,6 +973,9 @@ in place of
which causes it to
repeat until the exit status of the first list is zero.
.Pp
+The exit status is that of the last execution of the second list,
+or zero if it was never executed.
+.Pp
The syntax of the
.Ic for
command is:
@@ -1033,10 +1056,6 @@ continuing until a list terminated with
or the end of the
.Ic case
command.
-The exit code of the
-.Ic case
-command is the exit code of the last command executed in the list or
-zero if no patterns were matched.
.Ss Grouping Commands Together
Commands may be grouped by writing either
.Pp
@@ -1124,6 +1143,8 @@ and the syntax is:
The
.Ic local
command is implemented as a built-in command.
+The exit status is zero
+unless the command is not in a function or a variable name is invalid.
.Pp
When a variable is made local, it inherits the initial
value and exported and readonly flags from the variable
@@ -2515,7 +2536,8 @@ and so on,
decreasing the value of
.Li $#
by one.
-If there are zero positional parameters, shifting does not do anything.
+For portability, shifting if there are zero positional parameters
+should be avoided, since the shell may abort.
.It Ic test
A built-in equivalent of
.Xr test 1 .
diff --git a/bin/sh/tests/Makefile b/bin/sh/tests/Makefile
index 51c6dc4..c092962 100644
--- a/bin/sh/tests/Makefile
+++ b/bin/sh/tests/Makefile
@@ -4,15 +4,12 @@
TESTSDIR= ${TESTSBASE}/bin/sh
-TAP_TESTS_SH= legacy_test
-TAP_TESTS_SH_SED_legacy_test= -e 's,__SH__,/bin/sh,g'
-# Some tests in here are silently not run when the tests are executed as
-# root. Explicitly tell Kyua to drop privileges.
-#
-# TODO(jmmv): Kyua needs to do this by default, not only when explicitly
-# requested. See https://code.google.com/p/kyua/issues/detail?id=6
-TEST_METADATA.legacy_test+= required_user="unprivileged"
-
-SUBDIR+= builtins errors execution expansion parameters parser set-e
+TESTS_SUBDIRS+= builtins
+TESTS_SUBDIRS+= errors
+TESTS_SUBDIRS+= execution
+TESTS_SUBDIRS+= expansion
+TESTS_SUBDIRS+= parameters
+TESTS_SUBDIRS+= parser
+TESTS_SUBDIRS+= set-e
.include <bsd.test.mk>
diff --git a/bin/sh/tests/builtins/Makefile b/bin/sh/tests/builtins/Makefile
index 4c988da..5f5da4a 100644
--- a/bin/sh/tests/builtins/Makefile
+++ b/bin/sh/tests/builtins/Makefile
@@ -1,9 +1,13 @@
# $FreeBSD$
-.include <bsd.own.mk>
+.include <src.opts.mk>
-FILESDIR= ${TESTSBASE}/bin/sh/builtins
-KYUAFILE= no
+TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T}
+
+.PATH: ${.CURDIR:H}
+ATF_TESTS_SH= functional_test
+
+FILESDIR= ${TESTSDIR}
FILES= alias.0 alias.0.stdout
FILES+= alias.1 alias.1.stderr
@@ -68,6 +72,8 @@ FILES+= eval3.0
FILES+= eval4.0
FILES+= eval5.0
FILES+= eval6.0
+FILES+= eval7.0
+FILES+= eval8.7
FILES+= exec1.0
FILES+= exec2.0
FILES+= exit1.0
@@ -96,6 +102,7 @@ FILES+= jobid2.0
FILES+= kill1.0 kill2.0
FILES+= lineno.0 lineno.0.stdout
FILES+= lineno2.0
+FILES+= lineno3.0 lineno3.0.stdout
FILES+= local1.0
FILES+= local2.0
FILES+= local3.0
diff --git a/bin/sh/tests/builtins/eval7.0 b/bin/sh/tests/builtins/eval7.0
new file mode 100644
index 0000000..a309c91
--- /dev/null
+++ b/bin/sh/tests/builtins/eval7.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+# Assumes that break can break out of a loop outside eval.
+
+while :; do
+ eval "break
+echo bad1"
+ echo bad2
+ exit 3
+done
diff --git a/bin/sh/tests/builtins/eval8.7 b/bin/sh/tests/builtins/eval8.7
new file mode 100644
index 0000000..af6064c
--- /dev/null
+++ b/bin/sh/tests/builtins/eval8.7
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+f() {
+ eval "return 7
+echo bad2"
+}
+f
diff --git a/bin/sh/tests/builtins/getopts9.0 b/bin/sh/tests/builtins/getopts9.0
new file mode 100644
index 0000000..d23fc43
--- /dev/null
+++ b/bin/sh/tests/builtins/getopts9.0
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+args='-ab'
+getopts ab opt $args
+echo $?:$opt:$OPTARG
+for dummy in dummy1 dummy2; do
+ getopts ab opt $args
+ echo $?:$opt:$OPTARG
+done
diff --git a/bin/sh/tests/builtins/getopts9.0.stdout b/bin/sh/tests/builtins/getopts9.0.stdout
new file mode 100644
index 0000000..4d32063
--- /dev/null
+++ b/bin/sh/tests/builtins/getopts9.0.stdout
@@ -0,0 +1,3 @@
+0:a:
+0:b:
+1:?:
diff --git a/bin/sh/tests/builtins/lineno3.0 b/bin/sh/tests/builtins/lineno3.0
new file mode 100644
index 0000000..eb8f9ab
--- /dev/null
+++ b/bin/sh/tests/builtins/lineno3.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+echo before: $LINENO
+dummy=$'a\0
+'
+echo after: $LINENO
diff --git a/bin/sh/tests/builtins/lineno3.0.stdout b/bin/sh/tests/builtins/lineno3.0.stdout
new file mode 100644
index 0000000..6e0e4ac
--- /dev/null
+++ b/bin/sh/tests/builtins/lineno3.0.stdout
@@ -0,0 +1,2 @@
+before: 3
+after: 6
diff --git a/bin/sh/tests/errors/Makefile b/bin/sh/tests/errors/Makefile
index 9f8b0f2..ace9a01 100644
--- a/bin/sh/tests/errors/Makefile
+++ b/bin/sh/tests/errors/Makefile
@@ -1,9 +1,11 @@
# $FreeBSD$
-.include <bsd.own.mk>
+TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T}
-FILESDIR= ${TESTSBASE}/bin/sh/errors
-KYUAFILE= no
+.PATH: ${.CURDIR:H}
+ATF_TESTS_SH= functional_test
+
+FILESDIR= ${TESTSDIR}
FILES= assignment-error1.0
FILES+= assignment-error2.0
diff --git a/bin/sh/tests/errors/bad-parm-exp2.2 b/bin/sh/tests/errors/bad-parm-exp2.2
index 7e13d2b..a0826ec 100644
--- a/bin/sh/tests/errors/bad-parm-exp2.2
+++ b/bin/sh/tests/errors/bad-parm-exp2.2
@@ -1,2 +1,2 @@
# $FreeBSD$
-${}
+eval '${}'
diff --git a/bin/sh/tests/errors/bad-parm-exp2.2.stderr b/bin/sh/tests/errors/bad-parm-exp2.2.stderr
index d027a5a..51ea69c 100644
--- a/bin/sh/tests/errors/bad-parm-exp2.2.stderr
+++ b/bin/sh/tests/errors/bad-parm-exp2.2.stderr
@@ -1 +1 @@
-./errors/bad-parm-exp2.2: ${}: Bad substitution
+eval: ${}: Bad substitution
diff --git a/bin/sh/tests/errors/bad-parm-exp3.2 b/bin/sh/tests/errors/bad-parm-exp3.2
index a5ecba5..bb41208 100644
--- a/bin/sh/tests/errors/bad-parm-exp3.2
+++ b/bin/sh/tests/errors/bad-parm-exp3.2
@@ -1,2 +1,2 @@
# $FreeBSD$
-${foo/}
+eval '${foo/}'
diff --git a/bin/sh/tests/errors/bad-parm-exp3.2.stderr b/bin/sh/tests/errors/bad-parm-exp3.2.stderr
index ef40251..70473f9 100644
--- a/bin/sh/tests/errors/bad-parm-exp3.2.stderr
+++ b/bin/sh/tests/errors/bad-parm-exp3.2.stderr
@@ -1 +1 @@
-./errors/bad-parm-exp3.2: ${foo/}: Bad substitution
+eval: ${foo/}: Bad substitution
diff --git a/bin/sh/tests/errors/bad-parm-exp4.2 b/bin/sh/tests/errors/bad-parm-exp4.2
index 9eec8d0..2837f9b 100644
--- a/bin/sh/tests/errors/bad-parm-exp4.2
+++ b/bin/sh/tests/errors/bad-parm-exp4.2
@@ -1,2 +1,2 @@
# $FreeBSD$
-${foo:@abc}
+eval '${foo:@abc}'
diff --git a/bin/sh/tests/errors/bad-parm-exp4.2.stderr b/bin/sh/tests/errors/bad-parm-exp4.2.stderr
index 89bd80f..3363f51 100644
--- a/bin/sh/tests/errors/bad-parm-exp4.2.stderr
+++ b/bin/sh/tests/errors/bad-parm-exp4.2.stderr
@@ -1 +1 @@
-./errors/bad-parm-exp4.2: ${foo:@...}: Bad substitution
+eval: ${foo:@...}: Bad substitution
diff --git a/bin/sh/tests/errors/bad-parm-exp5.2 b/bin/sh/tests/errors/bad-parm-exp5.2
index 459281f..1ba343b 100644
--- a/bin/sh/tests/errors/bad-parm-exp5.2
+++ b/bin/sh/tests/errors/bad-parm-exp5.2
@@ -1,2 +1,2 @@
# $FreeBSD$
-${/}
+eval '${/}'
diff --git a/bin/sh/tests/errors/bad-parm-exp5.2.stderr b/bin/sh/tests/errors/bad-parm-exp5.2.stderr
index 89b1997..13763f8 100644
--- a/bin/sh/tests/errors/bad-parm-exp5.2.stderr
+++ b/bin/sh/tests/errors/bad-parm-exp5.2.stderr
@@ -1 +1 @@
-./errors/bad-parm-exp5.2: ${/}: Bad substitution
+eval: ${/}: Bad substitution
diff --git a/bin/sh/tests/errors/bad-parm-exp6.2 b/bin/sh/tests/errors/bad-parm-exp6.2
index ba51442..b53a91b 100644
--- a/bin/sh/tests/errors/bad-parm-exp6.2
+++ b/bin/sh/tests/errors/bad-parm-exp6.2
@@ -1,2 +1,2 @@
# $FreeBSD$
-${#foo^}
+eval '${#foo^}'
diff --git a/bin/sh/tests/errors/bad-parm-exp6.2.stderr b/bin/sh/tests/errors/bad-parm-exp6.2.stderr
index dbf14b5..cc56f65 100644
--- a/bin/sh/tests/errors/bad-parm-exp6.2.stderr
+++ b/bin/sh/tests/errors/bad-parm-exp6.2.stderr
@@ -1 +1 @@
-./errors/bad-parm-exp6.2: ${foo...}: Bad substitution
+eval: ${foo...}: Bad substitution
diff --git a/bin/sh/tests/execution/Makefile b/bin/sh/tests/execution/Makefile
index 302d0d8..2653d5f 100644
--- a/bin/sh/tests/execution/Makefile
+++ b/bin/sh/tests/execution/Makefile
@@ -1,9 +1,11 @@
# $FreeBSD$
-.include <bsd.own.mk>
+TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T}
-FILESDIR= ${TESTSBASE}/bin/sh/execution
-KYUAFILE= no
+.PATH: ${.CURDIR:H}
+ATF_TESTS_SH= functional_test
+
+FILESDIR= ${TESTSDIR}
FILES= bg1.0
FILES+= bg2.0
diff --git a/bin/sh/tests/expansion/Makefile b/bin/sh/tests/expansion/Makefile
index bd24319..027bc95 100644
--- a/bin/sh/tests/expansion/Makefile
+++ b/bin/sh/tests/expansion/Makefile
@@ -1,9 +1,11 @@
# $FreeBSD$
-.include <bsd.own.mk>
+TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T}
-FILESDIR= ${TESTSBASE}/bin/sh/expansion
-KYUAFILE= no
+.PATH: ${.CURDIR:H}
+ATF_TESTS_SH= functional_test
+
+FILESDIR= ${TESTSDIR}
FILES= arith1.0
FILES+= arith2.0
@@ -18,6 +20,7 @@ FILES+= arith10.0
FILES+= arith11.0
FILES+= arith12.0
FILES+= arith13.0
+FILES+= arith14.0
FILES+= assign1.0
FILES+= cmdsubst1.0
FILES+= cmdsubst2.0
@@ -69,6 +72,7 @@ FILES+= plus-minus7.0
FILES+= plus-minus8.0
FILES+= question1.0
FILES+= readonly1.0
+FILES+= redir1.0
FILES+= set-u1.0
FILES+= set-u2.0
FILES+= set-u3.0
diff --git a/bin/sh/tests/expansion/arith14.0 b/bin/sh/tests/expansion/arith14.0
new file mode 100644
index 0000000..8369043
--- /dev/null
+++ b/bin/sh/tests/expansion/arith14.0
@@ -0,0 +1,40 @@
+# $FreeBSD$
+# Check that <</>> use the low bits of the shift count.
+
+if [ $((1<<16<<16)) = 0 ]; then
+ width=32
+elif [ $((1<<32<<32)) = 0 ]; then
+ width=64
+elif [ $((1<<64<<64)) = 0 ]; then
+ width=128
+elif [ $((1<<64>>64)) = 1 ]; then
+ # Integers are wider than 128 bits; assume arbitrary precision.
+ # Nothing to test here.
+ exit 0
+else
+ echo "Cannot determine integer width"
+ exit 2
+fi
+
+twowidth=$((width * 2))
+j=43 k=$((1 << (width - 2))) r=0
+
+i=0
+while [ $i -lt $twowidth ]; do
+ if [ "$((j << i))" != "$((j << (i + width)))" ]; then
+ echo "Problem with $j << $i"
+ r=2
+ fi
+ i=$((i + 1))
+done
+
+i=0
+while [ $i -lt $twowidth ]; do
+ if [ "$((k >> i))" != "$((k >> (i + width)))" ]; then
+ echo "Problem with $k >> $i"
+ r=2
+ fi
+ i=$((i + 1))
+done
+
+exit $r
diff --git a/bin/sh/tests/expansion/redir1.0 b/bin/sh/tests/expansion/redir1.0
new file mode 100644
index 0000000..aa13e15
--- /dev/null
+++ b/bin/sh/tests/expansion/redir1.0
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+bad=0
+for i in 0 1 2 3; do
+ for j in 0 1 2 3 4 5 6 7; do
+ for k in 0 1 2 3 4 5 6 7; do
+ case $i$j$k in
+ 000) continue ;;
+ esac
+ set -- "$(printf \\$i$j$k@)"
+ set -- "${1%@}"
+ ff=
+ for f in /dev/null /dev/zero /; do
+ if [ -e "$f" ] && [ ! -e "$f$1" ]; then
+ ff=$f
+ fi
+ done
+ [ -n "$ff" ] || continue
+ if { true <$ff$1; } 2>/dev/null; then
+ echo "Bad: $i$j$k ($ff)" >&2
+ : $((bad += 1))
+ fi
+ done
+ done
+done
+exit $((bad ? 2 : 0))
diff --git a/bin/sh/tests/functional_test.sh b/bin/sh/tests/functional_test.sh
new file mode 100755
index 0000000..6980538
--- /dev/null
+++ b/bin/sh/tests/functional_test.sh
@@ -0,0 +1,72 @@
+#
+# Copyright 2014 EMC Corp.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# $FreeBSD$
+
+SRCDIR=$(atf_get_srcdir)
+
+check()
+{
+ local tc=${1}; shift
+
+ export SH=$(atf_config_get bin.sh.test_shell /bin/sh)
+
+ local err_file="${SRCDIR}/${tc}.stderr"
+ [ -f "${err_file}" ] && err_flag="-e file:${err_file}"
+ local out_file="${SRCDIR}/${tc}.stdout"
+ [ -f "${out_file}" ] && out_flag="-o file:${out_file}"
+
+ atf_check -s exit:${tc##*.} ${err_flag} ${out_flag} ${SH} "${SRCDIR}/${tc}"
+}
+
+add_testcase()
+{
+ local tc=${1}
+ local tc_escaped word
+
+ case "${tc%.*}" in
+ *-*)
+ local IFS="-"
+ for word in ${tc%.*}; do
+ tc_escaped="${tc_escaped:+${tc_escaped}_}${word}"
+ done
+ ;;
+ *)
+ tc_escaped=${tc%.*}
+ ;;
+ esac
+
+ atf_test_case ${tc_escaped}
+ eval "${tc_escaped}_body() { check ${tc}; }"
+ atf_add_test_case ${tc_escaped}
+}
+
+atf_init_test_cases()
+{
+ for path in $(find -Es "${SRCDIR}" -regex '.*\.[0-9]+$'); do
+ add_testcase ${path##*/}
+ done
+}
diff --git a/bin/sh/tests/legacy_test.sh b/bin/sh/tests/legacy_test.sh
deleted file mode 100644
index d43f5dd..0000000
--- a/bin/sh/tests/legacy_test.sh
+++ /dev/null
@@ -1,46 +0,0 @@
-# $FreeBSD$
-
-: ${SH:="__SH__"}
-export SH
-
-# TODO(jmmv): The Kyua TAP interface should be passing us the value of
-# "srcdir" as an environment variable, just as it does with the ATF
-# interface in the form of a configuration variable. For now, just try
-# to guess this.
-: ${TESTS_DATA:=$(dirname ${0})}
-
-COUNTER=1
-
-do_test() {
- c=${COUNTER}
- COUNTER=$((COUNTER+1))
- ${SH} $1 > tmp.stdout 2> tmp.stderr
- if [ $? -ne $2 ]; then
- echo "not ok ${c} - ${1} # wrong exit status"
- rm tmp.stdout tmp.stderr
- return
- fi
- sed -I '' -e "s|^${TESTS_DATA}|.|" tmp.stderr
- for i in stdout stderr; do
- if [ -f ${1}.${i} ]; then
- if ! cmp -s tmp.${i} ${1}.${i}; then
- echo "not ok ${c} - ${1} # wrong output on ${i}"
- rm tmp.stdout tmp.stderr
- return
- fi
- elif [ -s tmp.${i} ]; then
- echo "not ok ${c} - ${1} # wrong output on ${i}"
- rm tmp.stdout tmp.stderr
- return
- fi
- done
- echo "ok ${c} - ${1}"
- rm tmp.stdout tmp.stderr
-}
-
-TESTS=$(find -Es ${TESTS_DATA} -regex ".*\.[0-9]+")
-printf "1..%d\n" $(echo ${TESTS} | wc -w)
-
-for i in ${TESTS} ; do
- do_test ${i} ${i##*.}
-done
diff --git a/bin/sh/tests/parameters/Makefile b/bin/sh/tests/parameters/Makefile
index da49d14..231ed4d 100644
--- a/bin/sh/tests/parameters/Makefile
+++ b/bin/sh/tests/parameters/Makefile
@@ -1,9 +1,11 @@
# $FreeBSD$
-.include <bsd.own.mk>
+TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T}
-FILESDIR= ${TESTSBASE}/bin/sh/parameters
-KYUAFILE= no
+.PATH: ${.CURDIR:H}
+ATF_TESTS_SH= functional_test
+
+FILESDIR= ${TESTSDIR}
FILES= env1.0
FILES+= exitstatus1.0
@@ -16,6 +18,8 @@ FILES+= positional2.0
FILES+= positional3.0
FILES+= positional4.0
FILES+= positional5.0
+FILES+= positional6.0
+FILES+= positional7.0
FILES+= pwd1.0
FILES+= pwd2.0
diff --git a/bin/sh/tests/parameters/positional6.0 b/bin/sh/tests/parameters/positional6.0
new file mode 100644
index 0000000..1410668
--- /dev/null
+++ b/bin/sh/tests/parameters/positional6.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+IFS=?
+set p r
+v=pqrs
+r=${v#"$*"}
+[ "$r" = pqrs ]
diff --git a/bin/sh/tests/parameters/positional7.0 b/bin/sh/tests/parameters/positional7.0
new file mode 100644
index 0000000..f170ad3
--- /dev/null
+++ b/bin/sh/tests/parameters/positional7.0
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+set -- / ''
+IFS=*
+set -- "$*"
+IFS=:
+args="$*"
+[ "$#:$args" = "1:/*" ]
diff --git a/bin/sh/tests/parser/Makefile b/bin/sh/tests/parser/Makefile
index 03650b1..3c9e62c 100644
--- a/bin/sh/tests/parser/Makefile
+++ b/bin/sh/tests/parser/Makefile
@@ -1,9 +1,11 @@
# $FreeBSD$
-.include <bsd.own.mk>
+TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T}
-FILESDIR= ${TESTSBASE}/bin/sh/parser
-KYUAFILE= no
+.PATH: ${.CURDIR:H}
+ATF_TESTS_SH= functional_test
+
+FILESDIR= ${TESTSDIR}
FILES= alias1.0
FILES+= alias2.0
@@ -52,6 +54,18 @@ FILES+= heredoc8.0
FILES+= heredoc9.0
FILES+= heredoc10.0
FILES+= heredoc11.0
+FILES+= heredoc12.0
+FILES+= line-cont1.0
+FILES+= line-cont2.0
+FILES+= line-cont3.0
+FILES+= line-cont4.0
+FILES+= line-cont5.0
+FILES+= line-cont6.0
+FILES+= line-cont7.0
+FILES+= line-cont8.0
+FILES+= line-cont9.0
+FILES+= line-cont10.0
+FILES+= line-cont11.0
FILES+= no-space1.0
FILES+= no-space2.0
FILES+= only-redir1.0
diff --git a/bin/sh/tests/parser/heredoc12.0 b/bin/sh/tests/parser/heredoc12.0
new file mode 100644
index 0000000..9648384
--- /dev/null
+++ b/bin/sh/tests/parser/heredoc12.0
@@ -0,0 +1,47 @@
+# $FreeBSD$
+
+failures=0
+
+check() {
+ if ! eval "[ $* ]"; then
+ echo "Failed: $*"
+ : $((failures += 1))
+ fi
+}
+
+longmark=`printf %01000d 4`
+longmarkstripped=`printf %0999d 0`
+
+check '"$(cat <<'"$longmark
+$longmark"'
+echo yes)" = "yes"'
+
+check '"$(cat <<\'"$longmark
+$longmark"'
+echo yes)" = "yes"'
+
+check '"$(cat <<'"$longmark
+yes
+$longmark"'
+)" = "yes"'
+
+check '"$(cat <<\'"$longmark
+yes
+$longmark"'
+)" = "yes"'
+
+check '"$(cat <<'"$longmark
+$longmarkstripped
+$longmark.
+$longmark"'
+)" = "'"$longmarkstripped
+$longmark."'"'
+
+check '"$(cat <<\'"$longmark
+$longmarkstripped
+$longmark.
+$longmark"'
+)" = "'"$longmarkstripped
+$longmark."'"'
+
+exit $((failures != 0))
diff --git a/bin/sh/tests/parser/line-cont1.0 b/bin/sh/tests/parser/line-cont1.0
new file mode 100644
index 0000000..7ef5eba
--- /dev/null
+++ b/bin/sh/tests/parser/line-cont1.0
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+i\
+f
+t\
+r\
+u\
+e
+t\
+h\
+e\
+n
+:
+\
+f\
+i
diff --git a/bin/sh/tests/parser/line-cont10.0 b/bin/sh/tests/parser/line-cont10.0
new file mode 100644
index 0000000..1e74108
--- /dev/null
+++ b/bin/sh/tests/parser/line-cont10.0
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+v=XaaaXbbbX
+[ "${v\
+#\
+*\
+a}.${v\
+#\
+#\
+*\
+a}.${v\
+%\
+b\
+*}.${v\
+%\
+%\
+b\
+*}" = aaXbbbX.XbbbX.XaaaXbb.XaaaX ]
diff --git a/bin/sh/tests/parser/line-cont11.0 b/bin/sh/tests/parser/line-cont11.0
new file mode 100644
index 0000000..22e4975
--- /dev/null
+++ b/bin/sh/tests/parser/line-cont11.0
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+T=$(mktemp "${TMPDIR:-/tmp}/sh-test.XXXXXXXX") || exit
+trap 'rm -f -- "$T"' 0
+w='#A'
+# A naive pgetc_linecont() would push back two characters here, which
+# fails if a new buffer is read between the two characters.
+c='${w#\#}'
+c=$c$c$c$c
+c=$c$c$c$c
+c=$c$c$c$c
+c=$c$c$c$c
+c=$c$c$c$c
+c=$c$c$c$c
+printf 'v=%s\n' "$c" >"$T"
+. "$T"
+if [ "${#v}" != 4096 ]; then
+ echo "Length is bad (${#v})"
+ exit 3
+fi
+case $v in
+*[!A]*) echo "Content is bad"; exit 3 ;;
+esac
diff --git a/bin/sh/tests/parser/line-cont2.0 b/bin/sh/tests/parser/line-cont2.0
new file mode 100644
index 0000000..9a293fa
--- /dev/null
+++ b/bin/sh/tests/parser/line-cont2.0
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+[ "a\
+b" = ab ]
diff --git a/bin/sh/tests/parser/line-cont3.0 b/bin/sh/tests/parser/line-cont3.0
new file mode 100644
index 0000000..09d3aa8
--- /dev/null
+++ b/bin/sh/tests/parser/line-cont3.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+v=`printf %s 'a\
+b'`
+w="`printf %s 'c\
+d'`"
+[ "$v$w" = abcd ]
diff --git a/bin/sh/tests/parser/line-cont4.0 b/bin/sh/tests/parser/line-cont4.0
new file mode 100644
index 0000000..5803276
--- /dev/null
+++ b/bin/sh/tests/parser/line-cont4.0
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+v=abcd
+[ "$\
+v.$\
+{v}.${\
+v}.${v\
+}" = abcd.abcd.abcd.abcd ]
diff --git a/bin/sh/tests/parser/line-cont5.0 b/bin/sh/tests/parser/line-cont5.0
new file mode 100644
index 0000000..a7aa026
--- /dev/null
+++ b/bin/sh/tests/parser/line-cont5.0
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+bad=1
+case x in
+x\
+) ;\
+; *) exit 7
+esac &\
+& bad= &\
+& : >\
+>/dev/null
+
+false |\
+| [ -z "$bad" ]
diff --git a/bin/sh/tests/parser/line-cont6.0 b/bin/sh/tests/parser/line-cont6.0
new file mode 100644
index 0000000..b12125b
--- /dev/null
+++ b/bin/sh/tests/parser/line-cont6.0
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+v0\
+=abc
+
+v=$(cat <\
+<\
+E\
+O\
+F
+${v0}d
+EOF
+)
+
+w=$(cat <\
+<\
+-\
+EOF
+ efgh
+EOF
+)
+
+[ "$v.$w" = "abcd.efgh" ]
diff --git a/bin/sh/tests/parser/line-cont7.0 b/bin/sh/tests/parser/line-cont7.0
new file mode 100644
index 0000000..27f8aec
--- /dev/null
+++ b/bin/sh/tests/parser/line-cont7.0
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+[ "$(\
+(
+1\
++ 1)\
+)" = 2 ]
diff --git a/bin/sh/tests/parser/line-cont8.0 b/bin/sh/tests/parser/line-cont8.0
new file mode 100644
index 0000000..88667760
--- /dev/null
+++ b/bin/sh/tests/parser/line-cont8.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+set -- a b c d e f g h i j
+[ "${1\
+0\
+}" = j ]
diff --git a/bin/sh/tests/parser/line-cont9.0 b/bin/sh/tests/parser/line-cont9.0
new file mode 100644
index 0000000..4e73c8f
--- /dev/null
+++ b/bin/sh/tests/parser/line-cont9.0
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+[ "${$\
+:\
++\
+xyz}" = xyz ]
diff --git a/bin/sh/tests/set-e/Makefile b/bin/sh/tests/set-e/Makefile
index 55d7917..f733b60 100644
--- a/bin/sh/tests/set-e/Makefile
+++ b/bin/sh/tests/set-e/Makefile
@@ -1,9 +1,11 @@
# $FreeBSD$
-.include <bsd.own.mk>
+TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T}
-FILESDIR= ${TESTSBASE}/bin/sh/set-e
-KYUAFILE= no
+.PATH: ${.CURDIR:H}
+ATF_TESTS_SH= functional_test
+
+FILESDIR= ${TESTSDIR}
FILES= and1.0
FILES+= and2.1
diff --git a/bin/sh/trap.c b/bin/sh/trap.c
index e5a2a91..8ea3b12 100644
--- a/bin/sh/trap.c
+++ b/bin/sh/trap.c
@@ -510,28 +510,25 @@ exitshell_savedstatus(void)
exiting_exitstatus = oexitstatus;
}
exitstatus = oexitstatus = exiting_exitstatus;
- if (setjmp(loc1.loc)) {
- goto l1;
- }
- if (setjmp(loc2.loc)) {
- goto l2;
- }
- handler = &loc1;
- if ((p = trap[0]) != NULL && *p != '\0') {
- /*
- * Reset evalskip, or the trap on EXIT could be
- * interrupted if the last command was a "return".
- */
- evalskip = 0;
- trap[0] = NULL;
- evalstring(p, 0);
+ if (!setjmp(loc1.loc)) {
+ handler = &loc1;
+ if ((p = trap[0]) != NULL && *p != '\0') {
+ /*
+ * Reset evalskip, or the trap on EXIT could be
+ * interrupted if the last command was a "return".
+ */
+ evalskip = 0;
+ trap[0] = NULL;
+ evalstring(p, 0);
+ }
}
-l1: handler = &loc2; /* probably unnecessary */
- flushall();
+ if (!setjmp(loc2.loc)) {
+ handler = &loc2; /* probably unnecessary */
+ flushall();
#if JOBS
- setjobctl(0);
+ setjobctl(0);
#endif
-l2:
+ }
if (sig != 0 && sig != SIGSTOP && sig != SIGTSTP && sig != SIGTTIN &&
sig != SIGTTOU) {
signal(sig, SIG_DFL);
OpenPOWER on IntegriCloud