ash >& bug? [was: Re: svn commit: trunk/uClibc/test]

Bernhard Reutner-Fischer rep.dot.nop at gmail.com
Mon Oct 6 08:20:12 UTC 2008


On Sun, Oct 05, 2008 at 08:39:11PM +0200, Denys Vlasenko wrote:
>On Thursday 02 October 2008 04:25:45 pm Bernhard Reutner-Fischer wrote:
>> This one works reliably about everywhere:
>> $ (echo "out1" ; echo "err" >&2 ; echo "out2") > foo 2>&1
>> 
>> but ash rejects this:
>> $ (echo "out1" ; echo "err" >&2 ; echo "out2") 2>&1 >& foo
>> ash: syntax error: Bad fd number
>> 
>> It tries to use 'foo' as fd, which is not really appropriate as i think
>> a '>&' implies stdout, does it?
>
>No, it does not:

ok, it refers to the last "to" fd in the example above.
>
>[root at localhost tmp]# bash
>[root at localhost tmp]# echo HI >&
>bash: syntax error near unexpected token `newline'
>[root at localhost tmp]#
>
>Looks like in ">&file" "file" is mandatory.

Ok, but only if there was not previously a target-fd of a redirection.

>
>The attached patch implements it for ash. Please test.

I'll test ASAP and report back, thanks.
>--
>vda

>diff -d -urpN busybox.2/shell/ash.c busybox.3/shell/ash.c
>--- busybox.2/shell/ash.c	2008-09-27 01:49:35.000000000 +0200
>+++ busybox.3/shell/ash.c	2008-10-05 16:27:51.000000000 +0200
>@@ -8,7 +8,6 @@
>  * Copyright (c) 1997-2005 Herbert Xu <herbert at gondor.apana.org.au>
>  * 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_pt
> } 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,33 @@ 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
>+#define NTO2     17
>+#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 +596,26 @@ struct narg {
> 	struct nodelist *backquote;
> };
> 
>+/* nfile and ndup layout must match!
>+ * NTOFD (>&) 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 +918,9 @@ 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;
>+		case NTO2:
> 		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 +4423,7 @@ cmdtxt(union node *n)
> 	case NAPPEND:
> 		p = ">>";
> 		goto redir;
>+	case NTO2:
> 	case NTOFD:
> 		p = ">&";
> 		goto redir;
>@@ -4797,6 +4813,7 @@ openredirect(union node *redir)
> 			goto ecreate;
> 		break;
> 	case NTO:
>+	case NTO2:
> 		/* Take care of noclobber mode. */
> 		if (Cflag) {
> 			fname = redir->nfile.expfname;
>@@ -4959,6 +4976,8 @@ redirect(union node *redir, int flags)
> 		union node *tmp = redir;
> 		do {
> 			sv_pos++;
>+			if (redir->nfile.type == NTO2)
>+				sv_pos++;
> 			tmp = tmp->nfile.next;
> 		} while (tmp);
> 		sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
>@@ -4997,6 +5016,7 @@ redirect(union node *redir, int flags)
> 				continue;
> 			}
> 		}
>+ redirect_more:
> 		if (need_to_remember(sv, fd)) {
> 			/* Copy old descriptor */
> 			i = fcntl(fd, F_DUPFD, 10);
>@@ -5039,7 +5059,14 @@ redirect(union node *redir, int flags)
> 			}
> 		} else if (fd != newfd) { /* move newfd to fd */
> 			copyfd(newfd, fd | COPYFD_EXACT);
>-			close(newfd);
>+			if (!(redir->nfile.type == NTO2 && fd == 2))
>+				close(newfd);
>+		}
>+		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;
> 		}
> 	} while ((redir = redir->nfile.next) != NULL);
> 
>@@ -7641,6 +7668,7 @@ calcsize(union node *n)
> 		calcsize(n->narg.next);
> 		break;
> 	case NTO:
>+	case NTO2:
> 	case NCLOBBER:
> 	case NFROM:
> 	case NFROMTO:
>@@ -7754,6 +7782,7 @@ copynode(union node *n)
> 		new->narg.next = copynode(n->narg.next);
> 		break;
> 	case NTO:
>+	case NTO2:
> 	case NCLOBBER:
> 	case NFROM:
> 	case NFROMTO:
>@@ -8175,17 +8204,27 @@ expredir(union node *n)
> 		case NFROMTO:
> 		case NFROM:
> 		case NTO:
>+		case NTO2:
> 		case NCLOBBER:
> 		case NAPPEND:
> 			expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
>+ store_expfname:
> 			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");
>+//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;
>+				}
> 				fixredir(redir, fn.list->text, 1);
> 			}
> 			break;
>@@ -10126,7 +10165,7 @@ fixredir(union node *n, const char *text
> 		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 +10208,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 +10300,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 +10385,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 +10787,21 @@ readtoken1(int firstc, int syntax, char 
>  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 +10876,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 +10990,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;




More information about the busybox mailing list