• add code to support GNU bash’s “&> file” I/O redirection extension,

and make it fit into mksh’s model (also gives us a couple of things
  GNU bash doesn’t have
• add regression tests for all of these

Lukas “smultron” Upton from MidnightBSD spotted a script with /bin/sh
shebang invalidly using “&>” in some Apple backup toolkit, 10x

XXX why fds are limited to one digit?
This commit is contained in:
tg 2008-06-28 22:51:56 +00:00
parent fcb931ae2e
commit c77d67ef4d
5 changed files with 210 additions and 20 deletions

131
check.t
View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.200 2008/06/21 19:30:49 tg Exp $ # $MirOS: src/bin/mksh/check.t,v 1.201 2008/06/28 22:51:54 tg Exp $
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $ # $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $ # $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $ # $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
@ -7,7 +7,7 @@
# http://www.research.att.com/~gsf/public/ifs.sh # http://www.research.att.com/~gsf/public/ifs.sh
expected-stdout: expected-stdout:
@(#)MIRBSD KSH R34 2008/06/08 @(#)MIRBSD KSH R34 2008/06/28
description: description:
Check version of shell. Check version of shell.
stdin: stdin:
@ -5000,4 +5000,129 @@ stdin:
expected-stdout: expected-stdout:
okay okay
--- ---
name: bashiop-1
description:
Check if GNU bash-like I/O redirection works
Part 1: this is also supported by GNU bash
stdin:
exec 3>&1
function threeout {
echo ras
echo dwa >&2
echo tri >&3
}
threeout &>foo
echo ===
cat foo
expected-stdout:
tri
===
ras
dwa
---
name: bashiop-2a
description:
Check if GNU bash-like I/O redirection works
Part 2: this is *not* supported by GNU bash
stdin:
exec 3>&1
function threeout {
echo ras
echo dwa >&2
echo tri >&3
}
threeout 3&>foo
echo ===
cat foo
expected-stdout:
ras
===
dwa
tri
---
name: bashiop-2b
description:
Check if GNU bash-like I/O redirection works
Part 2: this is *not* supported by GNU bash
stdin:
exec 3>&1
function threeout {
echo ras
echo dwa >&2
echo tri >&3
}
threeout 3>foo &>&3
echo ===
cat foo
expected-stdout:
===
ras
dwa
tri
---
name: bashiop-2c
description:
Check if GNU bash-like I/O redirection works
Part 2: this is *not* supported by GNU bash
stdin:
echo mir >foo
set -o noclobber
exec 3>&1
function threeout {
echo ras
echo dwa >&2
echo tri >&3
}
threeout &>>foo
echo ===
cat foo
expected-stdout:
tri
===
mir
ras
dwa
---
name: bashiop-3a
description:
Check if GNU bash-like I/O redirection fails correctly
Part 1: this is also supported by GNU bash
stdin:
echo mir >foo
set -o noclobber
exec 3>&1
function threeout {
echo ras
echo dwa >&2
echo tri >&3
}
threeout &>foo
echo ===
cat foo
expected-stdout:
===
mir
expected-stderr-pattern: /.*: cannot (create|overwrite) .*/
---
name: bashiop-3b
description:
Check if GNU bash-like I/O redirection fails correctly
Part 2: this is *not* supported by GNU bash
stdin:
echo mir >foo
set -o noclobber
exec 3>&1
function threeout {
echo ras
echo dwa >&2
echo tri >&3
}
threeout &>|foo
echo ===
cat foo
expected-stdout:
tri
===
ras
dwa
---

24
lex.c
View File

@ -2,7 +2,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.61 2008/06/28 22:01:44 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/lex.c,v 1.62 2008/06/28 22:51:54 tg Exp $");
/* /*
* states while lexing word * states while lexing word
@ -771,7 +771,7 @@ yylex(int cf)
state = SBASE; state = SBASE;
dp = Xstring(ws, wp); dp = Xstring(ws, wp);
if ((c == '<' || c == '>') && state == SBASE && if ((c == '<' || c == '>' || c == '&') && state == SBASE &&
((c2 = Xlength(ws, wp)) == 0 || ((c2 = Xlength(ws, wp)) == 0 ||
(c2 == 2 && dp[0] == CHAR && ksh_isdigit(dp[1])))) { (c2 == 2 && dp[0] == CHAR && ksh_isdigit(dp[1])))) {
struct ioword *iop = (struct ioword *)alloc(sizeof (*iop), struct ioword *iop = (struct ioword *)alloc(sizeof (*iop),
@ -780,12 +780,22 @@ yylex(int cf)
if (c2 == 2) if (c2 == 2)
iop->unit = dp[1] - '0'; iop->unit = dp[1] - '0';
else else
iop->unit = c == '>' ? 1 : 0; iop->unit = c == '<' ? 0 : 1;
if (c == '&') {
if ((c2 = getsc()) != '>') {
ungetsc(c2);
goto no_iop;
}
c = c2;
iop->flag = IOBASH;
} else
iop->flag = 0;
c2 = getsc(); c2 = getsc();
/* <<, >>, <> are ok, >< is not */ /* <<, >>, <> are ok, >< is not */
if (c == c2 || (c == '<' && c2 == '>')) { if (c == c2 || (c == '<' && c2 == '>')) {
iop->flag = c == c2 ? iop->flag |= c == c2 ?
(c == '>' ? IOCAT : IOHERE) : IORDWR; (c == '>' ? IOCAT : IOHERE) : IORDWR;
if (iop->flag == IOHERE) { if (iop->flag == IOHERE) {
if ((c2 = getsc()) == '-') if ((c2 = getsc()) == '-')
@ -794,9 +804,9 @@ yylex(int cf)
ungetsc(c2); ungetsc(c2);
} }
} else if (c2 == '&') } else if (c2 == '&')
iop->flag = IODUP | (c == '<' ? IORDUP : 0); iop->flag |= IODUP | (c == '<' ? IORDUP : 0);
else { else {
iop->flag = c == '>' ? IOWRITE : IOREAD; iop->flag |= c == '>' ? IOWRITE : IOREAD;
if (c == '>' && c2 == '|') if (c == '>' && c2 == '|')
iop->flag |= IOCLOB; iop->flag |= IOCLOB;
else else
@ -809,6 +819,8 @@ yylex(int cf)
Xfree(ws, wp); /* free word */ Xfree(ws, wp); /* free word */
yylval.iop = iop; yylval.iop = iop;
return REDIR; return REDIR;
no_iop:
;
} }
if (wp == dp && state == SBASE) { if (wp == dp && state == SBASE) {

38
mksh.1
View File

@ -1,4 +1,4 @@
.\" $MirOS: src/bin/mksh/mksh.1,v 1.129 2008/06/08 16:57:52 tg Exp $ .\" $MirOS: src/bin/mksh/mksh.1,v 1.130 2008/06/28 22:51:55 tg Exp $
.\" $OpenBSD: ksh.1,v 1.122 2008/05/17 23:31:52 sobrado Exp $ .\" $OpenBSD: ksh.1,v 1.122 2008/05/17 23:31:52 sobrado Exp $
.\"- .\"-
.\" Try to make GNU groff and AT&T nroff more compatible .\" Try to make GNU groff and AT&T nroff more compatible
@ -30,7 +30,7 @@
.el .xD \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 .el .xD \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
.. ..
.\"- .\"-
.Dd $Mdocdate: June 8 2008 $ .Dd $Mdocdate: June 28 2008 $
.Dt MKSH 1 .Dt MKSH 1
.Os MirBSD .Os MirBSD
.Sh NAME .Sh NAME
@ -252,6 +252,7 @@ The meta-characters are used in building the following
.Ql \*(Gt , .Ql \*(Gt ,
.Ql \*(Gt& , .Ql \*(Gt& ,
.Ql \*(Gt\*(Gt , .Ql \*(Gt\*(Gt ,
.Ql &\*(Gt ,
etc. are used to specify redirections (see etc. are used to specify redirections (see
.Sx Input/output redirection .Sx Input/output redirection
below); below);
@ -2015,6 +2016,37 @@ indicating standard input is to be closed.
Same as Same as
.Ic \*(Lt& , .Ic \*(Lt& ,
except the operation is done on standard output. except the operation is done on standard output.
.It &\*(Gt Ar file
Same as
.Ic \*(Gt Ar file 2\*(Gt&1 .
This is a GNU
.Nm bash
extension supported by
.Nm
which also supports the preceding explicit fd digit, for example,
.Ic 3&\*(Gt Ar file
is the same as
.Ic 3\*(Gt Ar file 2\*(Gt&3
in
.Nm
but a syntax error in GNU
.Nm bash .
.It Xo
.No &\*(Gt| Ar file ,
.No &\*(Gt\*(Gt Ar file ,
.No &\*(Gt& Ar fd
.Xc
Same as
.Ic \*(Gt| Ar file ,
.Ic \*(Gt\*(Gt Ar file ,
or
.Ic \*(Gt& Ar fd ,
followed by
.Ic 2\*(Gt&1 ,
as above.
These are
.Nm
extensions.
.El .El
.Pp .Pp
In any of the above redirections, the file descriptor that is redirected In any of the above redirections, the file descriptor that is redirected
@ -5489,7 +5521,7 @@ and many other persons, and is currently maintained by
.An Thorsten Glaser Aq tg@mirbsd.de . .An Thorsten Glaser Aq tg@mirbsd.de .
.Sh BUGS .Sh BUGS
This document attempts to describe This document attempts to describe
.Nm mksh\ R34 .Nm mksh\ R35
and up, and up,
compiled without any options impacting functionality, such as compiled without any options impacting functionality, such as
.Dv MKSH_SMALL , .Dv MKSH_SMALL ,

5
sh.h
View File

@ -100,9 +100,9 @@
#define __SCCSID(x) __IDSTRING(sccsid,x) #define __SCCSID(x) __IDSTRING(sccsid,x)
#ifdef EXTERN #ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.219 2008/06/08 17:15:30 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/sh.h,v 1.220 2008/06/28 22:51:55 tg Exp $");
#endif #endif
#define MKSH_VERSION "R34 2008/06/08" #define MKSH_VERSION "R34 2008/06/28"
#ifndef MKSH_INCLUDES_ONLY #ifndef MKSH_INCLUDES_ONLY
@ -970,6 +970,7 @@ struct ioword {
#define IOCLOB BIT(6) /* >|, override -o noclobber */ #define IOCLOB BIT(6) /* >|, override -o noclobber */
#define IORDUP BIT(7) /* x<&y (as opposed to x>&y) */ #define IORDUP BIT(7) /* x<&y (as opposed to x>&y) */
#define IONAMEXP BIT(8) /* name has been expanded */ #define IONAMEXP BIT(8) /* name has been expanded */
#define IOBASH BIT(9) /* &> etc. */
/* execute/exchild flags */ /* execute/exchild flags */
#define XEXEC BIT(0) /* execute without forking */ #define XEXEC BIT(0) /* execute without forking */

32
syn.c
View File

@ -2,7 +2,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.21 2008/05/17 18:47:02 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/syn.c,v 1.22 2008/06/28 22:51:56 tg Exp $");
struct nesting_state { struct nesting_state {
int start_token; /* token than began nesting (eg, FOR) */ int start_token; /* token than began nesting (eg, FOR) */
@ -144,10 +144,17 @@ static struct ioword *
synio(int cf) synio(int cf)
{ {
struct ioword *iop; struct ioword *iop;
static struct ioword *nextiop = NULL;
int ishere; int ishere;
if (nextiop != NULL) {
iop = nextiop;
nextiop = NULL;
return (iop);
}
if (tpeek(cf) != REDIR) if (tpeek(cf) != REDIR)
return NULL; return (NULL);
ACCEPT; ACCEPT;
iop = yylval.iop; iop = yylval.iop;
ishere = (iop->flag&IOTYPE) == IOHERE; ishere = (iop->flag&IOTYPE) == IOHERE;
@ -161,7 +168,18 @@ synio(int cf)
*herep++ = iop; *herep++ = iop;
} else } else
iop->name = yylval.cp; iop->name = yylval.cp;
return iop;
if (iop->flag & IOBASH) {
nextiop = (struct ioword *)alloc(sizeof (*iop), ATEMP);
iop->flag &= ~IOBASH;
nextiop->unit = 2;
nextiop->flag = IODUP;
nextiop->name = shf_smprintf("%d", iop->unit);
nextiop->delim = NULL;
nextiop->heredoc = NULL;
}
return (iop);
} }
static struct op * static struct op *
@ -211,9 +229,11 @@ get_command(int cf)
(XPsize(args) == 0 ? ALIAS|VARASN : CMDWORD); (XPsize(args) == 0 ? ALIAS|VARASN : CMDWORD);
switch (tpeek(cf)) { switch (tpeek(cf)) {
case REDIR: case REDIR:
if (iopn >= NUFILE) while ((iop = synio(cf)) != NULL) {
yyerror("too many redirections\n"); if (iopn >= NUFILE)
iops[iopn++] = synio(cf); yyerror("too many redirections\n");
iops[iopn++] = iop;
}
break; break;
case LWORD: case LWORD: