ash: implement ">&file" bashism. ~100 bytes.

This commit is contained in:
Denis Vlasenko 2008-10-05 18:39:31 +00:00
parent 93d0776a96
commit 559691a3bf

View File

@ -8,7 +8,6 @@
* Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au> * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
* was re-ported from NetBSD and debianized. * was re-ported from NetBSD and debianized.
* *
*
* This code is derived from software contributed to Berkeley by * This code is derived from software contributed to Berkeley by
* Kenneth Almquist. * Kenneth Almquist.
* *
@ -28,7 +27,6 @@
* used in busybox and size optimizations, * used in busybox and size optimizations,
* rewrote arith (see notes to this), added locale support, * rewrote arith (see notes to this), added locale support,
* rewrote dynamic variables. * rewrote dynamic variables.
*
*/ */
/* /*
@ -247,8 +245,17 @@ extern struct globals_misc *const ash_ptr_to_globals_misc;
} while (0) } while (0)
/* ============ Interrupts / exceptions */ /* ============ Utility functions */
static int isdigit_str9(const char *str)
{
int maxlen = 9 + 1; /* max 9 digits: 999999999 */
while (--maxlen && isdigit(*str))
str++;
return (*str == '\0');
}
/* ============ Interrupts / exceptions */
/* /*
* These macros allow the user to suspend the handling of interrupt signals * These macros allow the user to suspend the handling of interrupt signals
* over a period of time. This is similar to SIGHOLD or to sigblock, but * over a period of time. This is similar to SIGHOLD or to sigblock, but
@ -500,32 +507,35 @@ static const char dolatstr[] ALIGN1 = {
CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
}; };
#define NCMD 0 #define NCMD 0
#define NPIPE 1 #define NPIPE 1
#define NREDIR 2 #define NREDIR 2
#define NBACKGND 3 #define NBACKGND 3
#define NSUBSHELL 4 #define NSUBSHELL 4
#define NAND 5 #define NAND 5
#define NOR 6 #define NOR 6
#define NSEMI 7 #define NSEMI 7
#define NIF 8 #define NIF 8
#define NWHILE 9 #define NWHILE 9
#define NUNTIL 10 #define NUNTIL 10
#define NFOR 11 #define NFOR 11
#define NCASE 12 #define NCASE 12
#define NCLIST 13 #define NCLIST 13
#define NDEFUN 14 #define NDEFUN 14
#define NARG 15 #define NARG 15
#define NTO 16 #define NTO 16
#define NCLOBBER 17 #if ENABLE_ASH_BASH_COMPAT
#define NFROM 18 #define NTO2 17
#define NFROMTO 19 #endif
#define NAPPEND 20 #define NCLOBBER 18
#define NTOFD 21 #define NFROM 19
#define NFROMFD 22 #define NFROMTO 20
#define NHERE 23 #define NAPPEND 21
#define NXHERE 24 #define NTOFD 22
#define NNOT 25 #define NFROMFD 23
#define NHERE 24
#define NXHERE 25
#define NNOT 26
union node; union node;
@ -588,20 +598,26 @@ struct narg {
struct nodelist *backquote; struct nodelist *backquote;
}; };
/* nfile and ndup layout must match!
* NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
* that it is actually NTO2 (>&file), and change its type.
*/
struct nfile { struct nfile {
smallint type; smallint type;
union node *next; union node *next;
int fd; int fd;
int _unused_dupfd;
union node *fname; union node *fname;
char *expfname; char *expfname;
}; };
struct ndup { struct ndup {
smallint type; smallint type;
union node *next; /* must match nfile's layout */ union node *next;
int fd; /* must match nfile's layout */ int fd;
int dupfd; int dupfd;
union node *vname; union node *vname;
char *_unused_expfname;
}; };
struct nhere { struct nhere {
@ -904,8 +920,11 @@ shcmd(union node *cmd, FILE *fp)
case NTO: s = ">>"+1; dftfd = 1; break; case NTO: s = ">>"+1; dftfd = 1; break;
case NCLOBBER: s = ">|"; dftfd = 1; break; case NCLOBBER: s = ">|"; dftfd = 1; break;
case NAPPEND: s = ">>"; dftfd = 1; break; case NAPPEND: s = ">>"; dftfd = 1; break;
#if ENABLE_ASH_BASH_COMPAT
case NTO2:
#endif
case NTOFD: s = ">&"; dftfd = 1; break; case NTOFD: s = ">&"; dftfd = 1; break;
case NFROM: s = "<"; break; case NFROM: s = "<"; break;
case NFROMFD: s = "<&"; break; case NFROMFD: s = "<&"; break;
case NFROMTO: s = "<>"; break; case NFROMTO: s = "<>"; break;
default: s = "*error*"; break; default: s = "*error*"; break;
@ -4408,6 +4427,9 @@ cmdtxt(union node *n)
case NAPPEND: case NAPPEND:
p = ">>"; p = ">>";
goto redir; goto redir;
#if ENABLE_ASH_BASH_COMPAT
case NTO2:
#endif
case NTOFD: case NTOFD:
p = ">&"; p = ">&";
goto redir; goto redir;
@ -4797,6 +4819,9 @@ openredirect(union node *redir)
goto ecreate; goto ecreate;
break; break;
case NTO: case NTO:
#if ENABLE_ASH_BASH_COMPAT
case NTO2:
#endif
/* Take care of noclobber mode. */ /* Take care of noclobber mode. */
if (Cflag) { if (Cflag) {
fname = redir->nfile.expfname; fname = redir->nfile.expfname;
@ -4959,6 +4984,10 @@ redirect(union node *redir, int flags)
union node *tmp = redir; union node *tmp = redir;
do { do {
sv_pos++; sv_pos++;
#if ENABLE_ASH_BASH_COMPAT
if (redir->nfile.type == NTO2)
sv_pos++;
#endif
tmp = tmp->nfile.next; tmp = tmp->nfile.next;
} while (tmp); } while (tmp);
sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0])); sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
@ -4997,6 +5026,9 @@ redirect(union node *redir, int flags)
continue; continue;
} }
} }
#if ENABLE_ASH_BASH_COMPAT
redirect_more:
#endif
if (need_to_remember(sv, fd)) { if (need_to_remember(sv, fd)) {
/* Copy old descriptor */ /* Copy old descriptor */
i = fcntl(fd, F_DUPFD, 10); i = fcntl(fd, F_DUPFD, 10);
@ -5039,8 +5071,19 @@ redirect(union node *redir, int flags)
} }
} else if (fd != newfd) { /* move newfd to fd */ } else if (fd != newfd) { /* move newfd to fd */
copyfd(newfd, fd | COPYFD_EXACT); copyfd(newfd, fd | COPYFD_EXACT);
close(newfd); #if ENABLE_ASH_BASH_COMPAT
if (!(redir->nfile.type == NTO2 && fd == 2))
#endif
close(newfd);
} }
#if ENABLE_ASH_BASH_COMPAT
if (redir->nfile.type == NTO2 && fd == 1) {
/* We already redirected it to fd 1, now copy it to 2 */
newfd = 1;
fd = 2;
goto redirect_more;
}
#endif
} while ((redir = redir->nfile.next) != NULL); } while ((redir = redir->nfile.next) != NULL);
INT_ON; INT_ON;
@ -7641,6 +7684,9 @@ calcsize(union node *n)
calcsize(n->narg.next); calcsize(n->narg.next);
break; break;
case NTO: case NTO:
#if ENABLE_ASH_BASH_COMPAT
case NTO2:
#endif
case NCLOBBER: case NCLOBBER:
case NFROM: case NFROM:
case NFROMTO: case NFROMTO:
@ -7754,6 +7800,9 @@ copynode(union node *n)
new->narg.next = copynode(n->narg.next); new->narg.next = copynode(n->narg.next);
break; break;
case NTO: case NTO:
#if ENABLE_ASH_BASH_COMPAT
case NTO2:
#endif
case NCLOBBER: case NCLOBBER:
case NFROM: case NFROM:
case NFROMTO: case NFROMTO:
@ -8175,17 +8224,33 @@ expredir(union node *n)
case NFROMTO: case NFROMTO:
case NFROM: case NFROM:
case NTO: case NTO:
#if ENABLE_ASH_BASH_COMPAT
case NTO2:
#endif
case NCLOBBER: case NCLOBBER:
case NAPPEND: case NAPPEND:
expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
#if ENABLE_ASH_BASH_COMPAT
store_expfname:
#endif
redir->nfile.expfname = fn.list->text; redir->nfile.expfname = fn.list->text;
break; break;
case NFROMFD: case NFROMFD:
case NTOFD: case NTOFD: /* >& */
if (redir->ndup.vname) { if (redir->ndup.vname) {
expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
if (fn.list == NULL) if (fn.list == NULL)
ash_msg_and_raise_error("redir error"); ash_msg_and_raise_error("redir error");
#if ENABLE_ASH_BASH_COMPAT
//FIXME: we used expandarg with different args!
if (!isdigit_str9(fn.list->text)) {
/* >&file, not >&fd */
if (redir->nfile.fd != 1) /* 123>&file - BAD */
ash_msg_and_raise_error("redir error");
redir->type = NTO2;
goto store_expfname;
}
#endif
fixredir(redir, fn.list->text, 1); fixredir(redir, fn.list->text, 1);
} }
break; break;
@ -10126,7 +10191,7 @@ fixredir(union node *n, const char *text, int err)
n->ndup.dupfd = -1; n->ndup.dupfd = -1;
else { else {
if (err) if (err)
raise_error_syntax("Bad fd number"); raise_error_syntax("bad fd number");
n->ndup.vname = makename(); n->ndup.vname = makename();
} }
} }
@ -10169,7 +10234,7 @@ parsefname(void)
n->type = NXHERE; n->type = NXHERE;
TRACE(("Here document %d\n", n->type)); TRACE(("Here document %d\n", n->type));
if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
raise_error_syntax("Illegal eof marker for << redirection"); raise_error_syntax("illegal eof marker for << redirection");
rmescapes(wordtext); rmescapes(wordtext);
here->eofmark = wordtext; here->eofmark = wordtext;
here->next = NULL; here->next = NULL;
@ -10261,7 +10326,7 @@ simplecmd(void)
if (!goodname(name) if (!goodname(name)
|| ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd)) || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
) { ) {
raise_error_syntax("Bad function name"); raise_error_syntax("bad function name");
} }
n->type = NDEFUN; n->type = NDEFUN;
checkkwd = CHKNL | CHKKWD | CHKALIAS; checkkwd = CHKNL | CHKKWD | CHKALIAS;
@ -10346,7 +10411,7 @@ parse_command(void)
} }
case TFOR: case TFOR:
if (readtoken() != TWORD || quoteflag || !goodname(wordtext)) if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
raise_error_syntax("Bad for loop variable"); raise_error_syntax("bad for loop variable");
n1 = stzalloc(sizeof(struct nfor)); n1 = stzalloc(sizeof(struct nfor));
n1->type = NFOR; n1->type = NFOR;
n1->nfor.var = wordtext; n1->nfor.var = wordtext;
@ -10748,25 +10813,21 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
endword: endword:
#if ENABLE_ASH_MATH_SUPPORT #if ENABLE_ASH_MATH_SUPPORT
if (syntax == ARISYNTAX) if (syntax == ARISYNTAX)
raise_error_syntax("Missing '))'"); raise_error_syntax("missing '))'");
#endif #endif
if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL) if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
raise_error_syntax("Unterminated quoted string"); raise_error_syntax("unterminated quoted string");
if (varnest != 0) { if (varnest != 0) {
startlinno = plinno; startlinno = plinno;
/* { */ /* { */
raise_error_syntax("Missing '}'"); raise_error_syntax("missing '}'");
} }
USTPUTC('\0', out); USTPUTC('\0', out);
len = out - (char *)stackblock(); len = out - (char *)stackblock();
out = stackblock(); out = stackblock();
if (eofmark == NULL) { if (eofmark == NULL) {
if ((c == '>' || c == '<') && quotef == 0) { if ((c == '>' || c == '<') && quotef == 0) {
int maxlen = 9 + 1; /* max 9 digit fd#: 999999999 */ if (isdigit_str9(out)) {
char *np = out;
while (--maxlen && isdigit(*np))
np++;
if (*np == '\0') {
PARSEREDIR(); /* passed as params: out, c */ PARSEREDIR(); /* passed as params: out, c */
lasttoken = TREDIR; lasttoken = TREDIR;
return lasttoken; return lasttoken;
@ -10841,6 +10902,7 @@ parseredir: {
np->type = NCLOBBER; np->type = NCLOBBER;
else if (c == '&') else if (c == '&')
np->type = NTOFD; np->type = NTOFD;
/* it also can be NTO2 (>&file), but we can't figure it out yet */
else { else {
np->type = NTO; np->type = NTO;
pungetc(); pungetc();
@ -10954,8 +11016,10 @@ parsesub: {
} else if (is_special(c)) { } else if (is_special(c)) {
USTPUTC(c, out); USTPUTC(c, out);
c = pgetc(); c = pgetc();
} else } else {
badsub: raise_error_syntax("Bad substitution"); badsub:
raise_error_syntax("bad substitution");
}
STPUTC('=', out); STPUTC('=', out);
flags = 0; flags = 0;