From 559691a3bfcb66ccf1cb59060d62d042e0265c00 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Sun, 5 Oct 2008 18:39:31 +0000 Subject: [PATCH] ash: implement ">&file" bashism. ~100 bytes. --- shell/ash.c | 158 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 111 insertions(+), 47 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 0878237bf..9ccb1c9ee 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -8,7 +8,6 @@ * Copyright (c) 1997-2005 Herbert Xu * was re-ported from NetBSD and debianized. * - * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * @@ -28,7 +27,6 @@ * used in busybox and size optimizations, * rewrote arith (see notes to this), added locale support, * rewrote dynamic variables. - * */ /* @@ -247,8 +245,17 @@ extern struct globals_misc *const ash_ptr_to_globals_misc; } 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 * 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' }; -#define NCMD 0 -#define NPIPE 1 -#define NREDIR 2 -#define NBACKGND 3 +#define NCMD 0 +#define NPIPE 1 +#define NREDIR 2 +#define NBACKGND 3 #define NSUBSHELL 4 -#define NAND 5 -#define NOR 6 -#define NSEMI 7 -#define NIF 8 -#define NWHILE 9 -#define NUNTIL 10 -#define NFOR 11 -#define NCASE 12 -#define NCLIST 13 -#define NDEFUN 14 -#define NARG 15 -#define NTO 16 -#define NCLOBBER 17 -#define NFROM 18 -#define NFROMTO 19 -#define NAPPEND 20 -#define NTOFD 21 -#define NFROMFD 22 -#define NHERE 23 -#define NXHERE 24 -#define NNOT 25 +#define NAND 5 +#define NOR 6 +#define NSEMI 7 +#define NIF 8 +#define NWHILE 9 +#define NUNTIL 10 +#define NFOR 11 +#define NCASE 12 +#define NCLIST 13 +#define NDEFUN 14 +#define NARG 15 +#define NTO 16 +#if ENABLE_ASH_BASH_COMPAT +#define NTO2 17 +#endif +#define NCLOBBER 18 +#define NFROM 19 +#define NFROMTO 20 +#define NAPPEND 21 +#define NTOFD 22 +#define NFROMFD 23 +#define NHERE 24 +#define NXHERE 25 +#define NNOT 26 union node; @@ -588,20 +598,26 @@ struct narg { 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 { smallint type; union node *next; int fd; + int _unused_dupfd; union node *fname; char *expfname; }; struct ndup { smallint type; - union node *next; /* must match nfile's layout */ - int fd; /* must match nfile's layout */ + union node *next; + int fd; int dupfd; union node *vname; + char *_unused_expfname; }; struct nhere { @@ -904,8 +920,11 @@ shcmd(union node *cmd, FILE *fp) case NTO: s = ">>"+1; dftfd = 1; break; case NCLOBBER: 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 NFROM: s = "<"; break; + case NFROM: s = "<"; break; case NFROMFD: s = "<&"; break; case NFROMTO: s = "<>"; break; default: s = "*error*"; break; @@ -4408,6 +4427,9 @@ cmdtxt(union node *n) case NAPPEND: p = ">>"; goto redir; +#if ENABLE_ASH_BASH_COMPAT + case NTO2: +#endif case NTOFD: p = ">&"; goto redir; @@ -4797,6 +4819,9 @@ openredirect(union node *redir) goto ecreate; break; case NTO: +#if ENABLE_ASH_BASH_COMPAT + case NTO2: +#endif /* Take care of noclobber mode. */ if (Cflag) { fname = redir->nfile.expfname; @@ -4959,6 +4984,10 @@ redirect(union node *redir, int flags) union node *tmp = redir; do { sv_pos++; +#if ENABLE_ASH_BASH_COMPAT + if (redir->nfile.type == NTO2) + sv_pos++; +#endif tmp = tmp->nfile.next; } while (tmp); sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0])); @@ -4997,6 +5026,9 @@ redirect(union node *redir, int flags) continue; } } +#if ENABLE_ASH_BASH_COMPAT + redirect_more: +#endif if (need_to_remember(sv, fd)) { /* Copy old descriptor */ i = fcntl(fd, F_DUPFD, 10); @@ -5039,8 +5071,19 @@ redirect(union node *redir, int flags) } } else if (fd != newfd) { /* move newfd to fd */ 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); INT_ON; @@ -7641,6 +7684,9 @@ calcsize(union node *n) calcsize(n->narg.next); break; case NTO: +#if ENABLE_ASH_BASH_COMPAT + case NTO2: +#endif case NCLOBBER: case NFROM: case NFROMTO: @@ -7754,6 +7800,9 @@ copynode(union node *n) new->narg.next = copynode(n->narg.next); break; case NTO: +#if ENABLE_ASH_BASH_COMPAT + case NTO2: +#endif case NCLOBBER: case NFROM: case NFROMTO: @@ -8175,17 +8224,33 @@ expredir(union node *n) case NFROMTO: case NFROM: case NTO: +#if ENABLE_ASH_BASH_COMPAT + case NTO2: +#endif case NCLOBBER: case NAPPEND: expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); +#if ENABLE_ASH_BASH_COMPAT + store_expfname: +#endif redir->nfile.expfname = fn.list->text; break; case NFROMFD: - case NTOFD: + case NTOFD: /* >& */ if (redir->ndup.vname) { expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE); if (fn.list == NULL) 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); } break; @@ -10126,7 +10191,7 @@ fixredir(union node *n, const char *text, int err) n->ndup.dupfd = -1; else { if (err) - raise_error_syntax("Bad fd number"); + raise_error_syntax("bad fd number"); n->ndup.vname = makename(); } } @@ -10169,7 +10234,7 @@ parsefname(void) n->type = NXHERE; TRACE(("Here document %d\n", n->type)); 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); here->eofmark = wordtext; here->next = NULL; @@ -10261,7 +10326,7 @@ simplecmd(void) if (!goodname(name) || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd)) ) { - raise_error_syntax("Bad function name"); + raise_error_syntax("bad function name"); } n->type = NDEFUN; checkkwd = CHKNL | CHKKWD | CHKALIAS; @@ -10346,7 +10411,7 @@ parse_command(void) } case TFOR: 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->type = NFOR; n1->nfor.var = wordtext; @@ -10748,25 +10813,21 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs) endword: #if ENABLE_ASH_MATH_SUPPORT if (syntax == ARISYNTAX) - raise_error_syntax("Missing '))'"); + raise_error_syntax("missing '))'"); #endif if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL) - raise_error_syntax("Unterminated quoted string"); + raise_error_syntax("unterminated quoted string"); if (varnest != 0) { startlinno = plinno; /* { */ - raise_error_syntax("Missing '}'"); + raise_error_syntax("missing '}'"); } USTPUTC('\0', out); len = out - (char *)stackblock(); out = stackblock(); if (eofmark == NULL) { if ((c == '>' || c == '<') && quotef == 0) { - int maxlen = 9 + 1; /* max 9 digit fd#: 999999999 */ - char *np = out; - while (--maxlen && isdigit(*np)) - np++; - if (*np == '\0') { + if (isdigit_str9(out)) { PARSEREDIR(); /* passed as params: out, c */ lasttoken = TREDIR; return lasttoken; @@ -10841,6 +10902,7 @@ parseredir: { np->type = NCLOBBER; else if (c == '&') np->type = NTOFD; + /* it also can be NTO2 (>&file), but we can't figure it out yet */ else { np->type = NTO; pungetc(); @@ -10954,8 +11016,10 @@ parsesub: { } else if (is_special(c)) { USTPUTC(c, out); c = pgetc(); - } else - badsub: raise_error_syntax("Bad substitution"); + } else { + badsub: + raise_error_syntax("bad substitution"); + } STPUTC('=', out); flags = 0; -- 2.25.1