Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spurious syntax error on process substitution following redirection #418

Closed
McDutchie opened this issue Jan 11, 2022 · 4 comments
Closed
Labels
bug Something is not working

Comments

@McDutchie
Copy link

McDutchie commented Jan 11, 2022

Gramatically, redirections can occur anywhere within a command line, whereas a process substitution (<(commandlist) or >(commandlist)) is replaced by a file name, so should be treated as just another simple word. So the following should not be a syntax error:

$ cat </dev/null <(true)
-ksh: syntax error: `)' unexpected
$ cat </dev/null >(true)
-ksh: syntax error: `)' unexpected
$ cat >/dev/null <(true)
-ksh: syntax error: `)' unexpected
$ cat >/dev/null >(true)
-ksh: syntax error: `)' unexpected

This bug is in every ksh93 version.

@McDutchie McDutchie added the bug Something is not working label Jan 11, 2022
@McDutchie
Copy link
Author

Related: #215. Perhaps that bug can be fixed along with this one, once someone figures out the mystical inner workings of the shell parser.

@McDutchie
Copy link
Author

The problem appears to be that, after a redirection, the initial ( in a process substitution is simply ignored at the lexical level. The following should clearly be a syntax error, but outputs the contents of README.md:

$ cat </dev/null <(README.md

@McDutchie McDutchie added the 1.0 label Feb 17, 2022
@McDutchie
Copy link
Author

McDutchie commented Jul 5, 2022

The problem is actually in parse.c, not in lex.c. The process substitution is misparsed as a redirection due to inout(), called from here, recursively parsing multiple redirections without recognising process substitutions. On encountering a process substitution, it should return a special value that causes the caller to go back to where process substitutions are parsed. That's a rather ugly hack, but the alternative is a comprehensive redesign and that is just not going to be realistic any time soon, if ever.

@McDutchie
Copy link
Author

This is the best I can come up with, I've been unable to break it:

diff --git a/src/cmd/ksh93/sh/parse.c b/src/cmd/ksh93/sh/parse.c
index 9d290ef4f..17a714218 100644
--- a/src/cmd/ksh93/sh/parse.c
+++ b/src/cmd/ksh93/sh/parse.c
@@ -48,6 +48,7 @@ static Shnode_t	*makeparent(Lex_t*, int, Shnode_t*);
 static Shnode_t	*makelist(Lex_t*, int, Shnode_t*, Shnode_t*);
 static struct argnod	*qscan(struct comnod*, int);
 static struct ionod	*inout(Lex_t*,struct ionod*, int);
+static char		inout_found_procsub;
 static Shnode_t	*sh_cmd(Lex_t*,int,int);
 static Shnode_t	*term(Lex_t*,int);
 static Shnode_t	*list(Lex_t*,int);
@@ -1583,6 +1584,7 @@ static Shnode_t *simple(Lex_t *lexp,int flag, struct ionod *io)
 			lexp->token = tok = 0;
 		if((tok==IPROCSYM || tok==OPROCSYM))
 		{
+	procsub:
 			argp = process_sub(lexp,tok);
 			argno = -1;
 			*argtail = argp;
@@ -1655,6 +1657,8 @@ static Shnode_t *simple(Lex_t *lexp,int flag, struct ionod *io)
 			}
 			else
 				t->comio = io = inout(lexp,(struct ionod*)0,0);
+			if(inout_found_procsub)
+				goto procsub;
 		}
 	}
 	*argtail = 0;
@@ -1753,6 +1757,9 @@ static struct ionod	*inout(Lex_t *lexp,struct ionod *lastio,int flag)
 	Stk_t			*stkp = sh.stk;
 	char *iovname=0;
 	register int		errout=0;
+	/* return if a process substitution is found without a redirection */
+	if(inout_found_procsub = (token==IPROCSYM || token==OPROCSYM))
+		return(lastio);
 	if(token==IOVNAME)
 	{
 		iovname=lexp->arg->argval+1;

McDutchie added a commit that referenced this issue Jul 5, 2022
Grammatically, redirections may occur anywhere within a command
line and are removed after processing them, whereas a process
substitution (<(commandlist) or >(commandlist)) is replaced by a
file name which should be treated as just another simple word.
So the following should not be a syntax error:

$ cat </dev/null <(true)
-ksh: syntax error: `)' unexpected
$ cat </dev/null >(true)
-ksh: syntax error: `)' unexpected
$ cat >/dev/null <(true)
-ksh: syntax error: `)' unexpected
$ cat >/dev/null >(true)
-ksh: syntax error: `)' unexpected

This bug is in every ksh93 version.

The problem is in the parser (parse.c). The process substitution is
misparsed as a redirection due to inout() recursively parsing
multiple redirections without recognising process substitutions.
inout() is mistaking '<(' for '<' and '>(' for '>', which explains
the incorrect syntax error.

This also causes the following to fail to detect a syntax error:
$ cat >&1 <(README.md
[the contents of README.md are shown]

...and other syntax errors detected in the wrong spot, for example:
$ { true; } <(echo wrong)
-ksh: syntax error: `wrong' unexpected
which should be:
-ksh: syntax error: `<(' unexpected

src/cmd/ksh93/sh/parse.c:
- Add global inout_found_procsub flag.
- inout(): On encountering a process substitution, set this flag
  and return, otherwise clear the flag.
- simple(): After calling inout(), check this flag and, if set,
  jump back to where process substitutions are parsed.

Resolves: #418
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something is not working
Projects
None yet
Development

No branches or pull requests

1 participant