Skip to content

Commit 6669389

Browse files
Leroy van Engelenlemmy
authored andcommitted
Change IOExec interface
It now passes its arguments almost directly to Java's `ProcessBuilder`. This avoids problems with passing arguments that include spaces and does not assume (incorrectly) that there is a shell handling quoting.
1 parent 46543b4 commit 6669389

File tree

3 files changed

+37
-33
lines changed

3 files changed

+37
-33
lines changed

modules/IOUtils.tla

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ IODeserialize(absoluteFilename, compressed) == CHOOSE val : TRUE
1212

1313
----------------------------------------------------------------------------
1414

15-
IOExec(command, parameters) ==
16-
(*************************************************************************)
17-
(* Spawns the given printf-style command as a sub-process of TLC. The *)
18-
(* n-th flag in command is substituted for the n-th element of the *)
19-
(* sequence parameters: IOExec("ls %s %s", <<"-lah", "/tmp">>) *)
20-
(* see http://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html *)
21-
(*************************************************************************)
15+
IOExec(command) ==
16+
(*******************************************************************************)
17+
(* Spawns the given command as a sub-process of TLC. The sequence of sequence *)
18+
(* of strings `command' signifies the external program file to be invoked and *)
19+
(* its arguments: IOExec(<<"ls", "-lah", "/tmp">>) *)
20+
(* see https://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html *)
21+
(*******************************************************************************)
2222
CHOOSE r \in [exitValue : Int, stdout : STRING, stderr : STRING] : TRUE
2323

2424
============================================================================

modules/tlc2/overrides/IOUtils.java

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,33 +69,41 @@ public static final IValue serialize(final IValue value, final StringValue absol
6969
}
7070

7171
@TLAPlusOperator(identifier = "IOExec", module = "IOUtils", minLevel = 1, warn = false)
72-
public static Value exec(final Value command, final Value parameter) throws IOException, InterruptedException {
72+
public static Value ioExec(final Value parameter) throws IOException, InterruptedException {
7373
// 1. Check parameters and covert.
74-
if (!(command instanceof StringValue)) {
75-
throw new EvalException(EC.TLC_MODULE_ONE_ARGUMENT_ERROR,
76-
new String[] { "IOExec", "string", Values.ppr(command.toString()) });
77-
}
7874
if (!(parameter instanceof TupleValue)) {
7975
throw new EvalException(EC.TLC_MODULE_ONE_ARGUMENT_ERROR,
8076
new String[] { "IOExec", "sequence", Values.ppr(parameter.toString()) });
8177
}
82-
final StringValue sv = (StringValue) command;
8378
final TupleValue tv = (TupleValue) parameter;
84-
85-
// 2. Build actual command-line by merging command and parameter.
86-
final String cmd = String.format(sv.toUnquotedString(),
87-
Arrays.asList(tv.getElems()).stream().map(e -> e.toUnquotedString()).toArray(size -> new Object[size]));
88-
79+
80+
// 2. Build actual command by converting each parameter element to a string.
81+
// No escaping or quoting is done so the process receives the exact string.
82+
final String[] command = Arrays.asList(tv.getElems()).stream()
83+
.map(IOUtils::convert)
84+
.toArray(size -> new String[size]);
85+
8986
// 3. Run command-line and receive its output.
90-
final Process process = new ProcessBuilder(cmd.split(" "))/*.inheritIO()*/.start();
91-
87+
final Process process = new ProcessBuilder(command)/*.inheritIO()*/.start();
88+
9289
final StringValue stdout = new StringValue(new String(process.getInputStream().readAllBytes()));
9390
final StringValue stderr = new StringValue(new String(process.getErrorStream().readAllBytes()));
9491
final IntValue exitCode = IntValue.gen(process.waitFor());
95-
92+
9693
return new RecordValue(EXEC_NAMES, new Value[] {exitCode, stdout, stderr}, false);
9794
}
9895

96+
private static String convert(IValue v) {
97+
if (! (v instanceof StringValue)) {
98+
// XXX Proper exception
99+
throw new EvalException(EC.TLC_MODULE_ONE_ARGUMENT_ERROR,
100+
new String[] { "IOExec", "sequence", Values.ppr(v.toString()) });
101+
}
102+
final StringValue sv = (StringValue) v;
103+
104+
return sv.val.toString();
105+
}
106+
99107
private static final UniqueString EXITVALUE = UniqueString.uniqueStringOf("exitValue");
100108
private static final UniqueString STDOUT = UniqueString.uniqueStringOf("stdout");
101109
private static final UniqueString STDERR = UniqueString.uniqueStringOf("stderr");

tests/IOUtilsTests.tla

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
---------------------------- MODULE IOUtilsTests ----------------------------
22
EXTENDS IOUtils, TLC
33

4-
ASSUME(LET ret == IOExec("echo %s %s", <<"foo", "bar">>) IN /\ ret.exitValue = 0
5-
/\ ret.stdout = "foo bar\n"
6-
/\ ret.stderr = "")
7-
ASSUME(LET ret == IOExec("cat /does/not/exist", <<>>) IN /\ ret.exitValue = 1
8-
/\ ret.stdout = ""
9-
/\ ret.stderr = "cat: /does/not/exist: No such file or directory\n")
10-
ASSUME(LET ret == IOExec("echo \"' %s", <<"\"'">>) IN /\ ret.exitValue = 0
11-
/\ ret.stdout = "\"' \"'\n"
12-
/\ ret.stderr = "")
13-
ASSUME(LET ret == IOExec("grep %s /dev/null", <<"foo bar">>) IN /\ ret.exitValue = 1
14-
/\ ret.stdout = ""
15-
/\ ret.stderr = "")
4+
\* Spaces and quotes should be passed directly to the program.
5+
ASSUME(LET ret == IOExec(<<"echo", "'foo' ", " \"bar\"">>) IN /\ ret.exitValue = 0
6+
/\ ret.stdout = "'foo' \"bar\"\n"
7+
/\ ret.stderr = "")
8+
\* Exit values and standard error should be returned properly.
9+
ASSUME(LET ret == IOExec(<<"cat", "/does/not/exist">>) IN /\ ret.exitValue = 1
10+
/\ ret.stdout = ""
11+
/\ ret.stderr = "cat: /does/not/exist: No such file or directory\n")
1612

1713
=============================================================================

0 commit comments

Comments
 (0)