• 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: 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 $
@ -7,7 +7,7 @@
# http://www.research.att.com/~gsf/public/ifs.sh
expected-stdout:
@(#)MIRBSD KSH R34 2008/06/08
@(#)MIRBSD KSH R34 2008/06/28
description:
Check version of shell.
stdin:
@ -5000,4 +5000,129 @@ stdin:
expected-stdout:
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"
__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
@ -771,7 +771,7 @@ yylex(int cf)
state = SBASE;
dp = Xstring(ws, wp);
if ((c == '<' || c == '>') && state == SBASE &&
if ((c == '<' || c == '>' || c == '&') && state == SBASE &&
((c2 = Xlength(ws, wp)) == 0 ||
(c2 == 2 && dp[0] == CHAR && ksh_isdigit(dp[1])))) {
struct ioword *iop = (struct ioword *)alloc(sizeof (*iop),
@ -780,12 +780,22 @@ yylex(int cf)
if (c2 == 2)
iop->unit = dp[1] - '0';
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();
/* <<, >>, <> are ok, >< is not */
if (c == c2 || (c == '<' && c2 == '>')) {
iop->flag = c == c2 ?
iop->flag |= c == c2 ?
(c == '>' ? IOCAT : IOHERE) : IORDWR;
if (iop->flag == IOHERE) {
if ((c2 = getsc()) == '-')
@ -794,9 +804,9 @@ yylex(int cf)
ungetsc(c2);
}
} else if (c2 == '&')
iop->flag = IODUP | (c == '<' ? IORDUP : 0);
iop->flag |= IODUP | (c == '<' ? IORDUP : 0);
else {
iop->flag = c == '>' ? IOWRITE : IOREAD;
iop->flag |= c == '>' ? IOWRITE : IOREAD;
if (c == '>' && c2 == '|')
iop->flag |= IOCLOB;
else
@ -809,6 +819,8 @@ yylex(int cf)
Xfree(ws, wp); /* free word */
yylval.iop = iop;
return REDIR;
no_iop:
;
}
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 $
.\"-
.\" 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
..
.\"-
.Dd $Mdocdate: June 8 2008 $
.Dd $Mdocdate: June 28 2008 $
.Dt MKSH 1
.Os MirBSD
.Sh NAME
@ -252,6 +252,7 @@ The meta-characters are used in building the following
.Ql \*(Gt ,
.Ql \*(Gt& ,
.Ql \*(Gt\*(Gt ,
.Ql &\*(Gt ,
etc. are used to specify redirections (see
.Sx Input/output redirection
below);
@ -2015,6 +2016,37 @@ indicating standard input is to be closed.
Same as
.Ic \*(Lt& ,
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
.Pp
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 .
.Sh BUGS
This document attempts to describe
.Nm mksh\ R34
.Nm mksh\ R35
and up,
compiled without any options impacting functionality, such as
.Dv MKSH_SMALL ,

5
sh.h
View File

@ -100,9 +100,9 @@
#define __SCCSID(x) __IDSTRING(sccsid,x)
#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
#define MKSH_VERSION "R34 2008/06/08"
#define MKSH_VERSION "R34 2008/06/28"
#ifndef MKSH_INCLUDES_ONLY
@ -970,6 +970,7 @@ struct ioword {
#define IOCLOB BIT(6) /* >|, override -o noclobber */
#define IORDUP BIT(7) /* x<&y (as opposed to x>&y) */
#define IONAMEXP BIT(8) /* name has been expanded */
#define IOBASH BIT(9) /* &> etc. */
/* execute/exchild flags */
#define XEXEC BIT(0) /* execute without forking */

32
syn.c
View File

@ -2,7 +2,7 @@
#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 {
int start_token; /* token than began nesting (eg, FOR) */
@ -144,10 +144,17 @@ static struct ioword *
synio(int cf)
{
struct ioword *iop;
static struct ioword *nextiop = NULL;
int ishere;
if (nextiop != NULL) {
iop = nextiop;
nextiop = NULL;
return (iop);
}
if (tpeek(cf) != REDIR)
return NULL;
return (NULL);
ACCEPT;
iop = yylval.iop;
ishere = (iop->flag&IOTYPE) == IOHERE;
@ -161,7 +168,18 @@ synio(int cf)
*herep++ = iop;
} else
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 *
@ -211,9 +229,11 @@ get_command(int cf)
(XPsize(args) == 0 ? ALIAS|VARASN : CMDWORD);
switch (tpeek(cf)) {
case REDIR:
if (iopn >= NUFILE)
yyerror("too many redirections\n");
iops[iopn++] = synio(cf);
while ((iop = synio(cf)) != NULL) {
if (iopn >= NUFILE)
yyerror("too many redirections\n");
iops[iopn++] = iop;
}
break;
case LWORD: