exec problem is JDK 1.7.0_21

S

Sven Köhler

Am 21.04.2013 14:08, schrieb Martin Gregorie:
It says *some* operating systems. No mention of Windows.

True, from which you conclude, that the following does not apply to
Windows. That the documentation is dangerously vague - but that's
another issue.
It also says
"there are operating systems where programs are expected to tokenize
command line strings themselves" which is not Windows IME - the C, C++
and Java CLI programming interface is the same for these in UNIX, Linux,
OS-9 and Windows.

That is just wrong!

If you use the main method instead of the WinMain as an entry point in
your programs, that is just fine. But that the CRT does the tokenization
for you, doesn't mean that the program doesn't do the tokenization by
itself. It does. This becomes even more clear, if one compares the
native Windows API (i.e. CreateProcess) for launching programs to the
unix equivalents like execv.
 
S

Sven Köhler

Am 21.04.2013 16:30, schrieb Chris Uppal:
Or, to put it another way: the only way to find out what works is to experiment
(it probably depends on the application you're launching as well as the OS and
Java platform). A right pain in the bum...

So I experimented. I found, that using a list of arguments works just
fine, unless these arguments contain quotes, spaces, or the empty string
is one of the arguments. I found, that ProcessBuilder (as well as
Runtime.exec) do a VERY BAD job at converting the argument array to
something, that CommandLineToArgv would tokenize back into something
close to what the argument array was.

At the same time, ProcessBuilder doesn't give me full control over the
command line parameter string. Instead, it sometimes adds quotes - but
not in all cases that would require them. Also any of that is
undocumented, such that any workaround I implement may or may not break
in the future.

Also, some here think that documentation as vague as "on some operating
systems, something MIGHT be required" would actually contain a concrete
statement about how to use ProcessBuilder on Windows. (Claiming, that
this statement does not apply to Windows is just as bad as the opposite.)


Regards,
Sven
 
S

Sven Köhler

Am 21.04.2013 19:20, schrieb Knute Johnson:
One quoted argument "hello world"

new ProcessBuilder("test.exe", "hello world");
new ProcessBuilder("test.exe", "\"hello world\"");
new ProcessBuilder("test.exe", "\"hello\" \"world\"");

Run that on Windows as well as on something UNIX like - e.g. Linux.


Regards,
Sven
 
S

Sven Köhler

Am 21.04.2013 23:29, schrieb Sven Köhler:
Am 21.04.2013 19:20, schrieb Knute Johnson:

new ProcessBuilder("test.exe", "hello world");
new ProcessBuilder("test.exe", "\"hello world\"");
new ProcessBuilder("test.exe", "\"hello\" \"world\"");

Let me add
new ProcessBuilder("test.exe", "\"\\\"hello world\\\"\"");
 
K

Knute Johnson

Am 21.04.2013 19:20, schrieb Knute Johnson:

new ProcessBuilder("test.exe", "hello world");
new ProcessBuilder("test.exe", "\"hello world\"");
new ProcessBuilder("test.exe", "\"hello\" \"world\"");

Run that on Windows as well as on something UNIX like - e.g. Linux.


Regards,
Sven

I think ProcessBuilder strips out the quotes inside the String. The
little C program I wrote deals with them just fine from the command line
in XP.
 
S

Sven Köhler

Am 22.04.2013 04:49, schrieb Knute Johnson:
I think ProcessBuilder strips out the quotes inside the String. The
little C program I wrote deals with them just fine from the command line
in XP.

The output should have been:

1) argv[1]=hello world
2) argv[1]=hello world
3) argv[1]=hello
argv[2]=world

And in the fourth example I added, the output should have been

4) argv[1]="hello world"


ProcessBuilder does not strip any quotes. The logic it implements is as
follows: If the argument contains spaces, add quotes. If it already
starts/ends with a quote, don't modify it. ProcessBuilder does not
check, escape, or do anything else with quotes inside the argument
string. You can confirm that by looking at the sources available in the JDK.


Regards,
Sven
 
S

Steven Simpson

Am 22.04.2013 04:49, schrieb Knute Johnson:The output should have been:

1) argv[1]=hello world
2) argv[1]=hello world
3) argv[1]=hello
argv[2]=world

And in the fourth example I added,
new ProcessBuilder("test.exe", "\"\\\"hello world\\\"\"");
the output should have been

4) argv[1]="hello world"


I tested by cross-compiling a C program 'showargs' from Linux (with
mingw32):

#include <stdio.h>

int main(int argc, char **argv)
{
int i;
printf("Arg count: %d\n", argc);
for (i = 0; i < argc; i++)
printf(" argv[%d]=[%s]\n", i, argv);
return 0;
}


To invoke from Java, I used:

import java.io.*;
import java.util.*;

public class TestProcessBuilder {
public static void main(String[] args) throws Exception {
test("showargs", "hello world");
test("showargs", "\"hello world\"");
test("showargs", "\"hello\" \"world\"");
test("showargs", "\"\\\"hello world\\\"\"");
}

private static void test(String... args)
throws IOException, InterruptedException {
System.out.println("Java: " + Arrays.asList(args));
ProcessBuilder pb = new ProcessBuilder(args);
pb.redirectErrorStream(true);
final Process proc = pb.start();
Thread t = new Thread() {
public void run() {
try (InputStream in = proc.getInputStream()) {
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > -1) {
System.out.write(buf, 0, len);
}
} catch (IOException ex) {
System.err.println("error: " + ex);
}
}
};
t.start();
proc.getOutputStream().close();
int rc = proc.waitFor();
t.join();
}
}

(I compiled this on Linux.)


On a Windows VM, I got this:

E:\>java -cp . TestProcessBuilder
Java: [showargs, hello world]
Arg count: 2
argv[0]=[showargs]
argv[1]=[hello world]
Java: [showargs, "hello world"]
Arg count: 2
argv[0]=[showargs]
argv[1]=[hello world]
Java: [showargs, "hello" "world"]
Arg count: 3
argv[0]=[showargs]
argv[1]=[hello]
argv[2]=[world]
Java: [showargs, "\"hello world\""]
Arg count: 2
argv[0]=[showargs]
argv[1]=["hello world"]

E:\>

'java -version' reports:

java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)



On Linux, I got this:

$ java -cp . TestProcessBuilder
Java: [showargs, hello world]
Arg count: 2
argv[0]=[showargs]
argv[1]=[hello world]
Java: [showargs, "hello world"]
Arg count: 2
argv[0]=[showargs]
argv[1]=["hello world"]
Java: [showargs, "hello" "world"]
Arg count: 2
argv[0]=[showargs]
argv[1]=["hello" "world"]
Java: [showargs, "\"hello world\""]
Arg count: 2
argv[0]=[showargs]
argv[1]=["\"hello world\""]
$

'java -version' report:

java version "1.7.0_15"
OpenJDK Runtime Environment (IcedTea7 2.3.7) (7u15-2.3.7-0ubuntu1~12.04.1)
OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)
 
D

Daniel Pitts

Am 22.04.2013 04:49, schrieb Knute Johnson:
On 4/21/2013 1:29 PM, Sven Köhler wrote:
Am 21.04.2013 19:20, schrieb Knute Johnson:
One quoted argument "hello world"
new ProcessBuilder("test.exe", "hello world");
new ProcessBuilder("test.exe", "\"hello world\"");
new ProcessBuilder("test.exe", "\"hello\" \"world\"");

Run that on Windows as well as on something UNIX like - e.g. Linux.
The output should have been:

1) argv[1]=hello world
2) argv[1]=hello world
3) argv[1]=hello
argv[2]=world

And in the fourth example I added,
new ProcessBuilder("test.exe", "\"\\\"hello world\\\"\"");
the output should have been

4) argv[1]="hello world"


I tested by cross-compiling a C program 'showargs' from Linux (with
mingw32):

#include <stdio.h>

int main(int argc, char **argv)
{
int i;
printf("Arg count: %d\n", argc);
for (i = 0; i < argc; i++)
printf(" argv[%d]=[%s]\n", i, argv);
return 0;
}


To invoke from Java, I used:

import java.io.*;
import java.util.*;

public class TestProcessBuilder {
public static void main(String[] args) throws Exception {
test("showargs", "hello world");
test("showargs", "\"hello world\"");
test("showargs", "\"hello\" \"world\"");
test("showargs", "\"\\\"hello world\\\"\"");
}

private static void test(String... args)
throws IOException, InterruptedException {
System.out.println("Java: " + Arrays.asList(args));
ProcessBuilder pb = new ProcessBuilder(args);
pb.redirectErrorStream(true);
final Process proc = pb.start();
Thread t = new Thread() {
public void run() {
try (InputStream in = proc.getInputStream()) {
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > -1) {
System.out.write(buf, 0, len);
}
} catch (IOException ex) {
System.err.println("error: " + ex);
}
}
};
t.start();
proc.getOutputStream().close();
int rc = proc.waitFor();
t.join();
}
}

(I compiled this on Linux.)


On a Windows VM, I got this:

E:\>java -cp . TestProcessBuilder
Java: [showargs, hello world]
Arg count: 2
argv[0]=[showargs]
argv[1]=[hello world]
Java: [showargs, "hello world"]
Arg count: 2
argv[0]=[showargs]
argv[1]=[hello world]
Java: [showargs, "hello" "world"]
Arg count: 3
argv[0]=[showargs]
argv[1]=[hello]
argv[2]=[world]
Java: [showargs, "\"hello world\""]
Arg count: 2
argv[0]=[showargs]
argv[1]=["hello world"]

E:\>

'java -version' reports:

java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)



On Linux, I got this:

$ java -cp . TestProcessBuilder
Java: [showargs, hello world]
Arg count: 2
argv[0]=[showargs]
argv[1]=[hello world]
Java: [showargs, "hello world"]
Arg count: 2
argv[0]=[showargs]
argv[1]=["hello world"]
Java: [showargs, "hello" "world"]
Arg count: 2
argv[0]=[showargs]
argv[1]=["hello" "world"]
Java: [showargs, "\"hello world\""]
Arg count: 2
argv[0]=[showargs]
argv[1]=["\"hello world\""]
$

'java -version' report:

java version "1.7.0_15"
OpenJDK Runtime Environment (IcedTea7 2.3.7) (7u15-2.3.7-0ubuntu1~12.04.1)
OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)


Interesting, I expected the argv[1]=["hello world"] for the input "hello
world"]. It seems to be that way for Linux, but not on windows.
 
S

Sven Köhler

Am 22.04.2013 17:43, schrieb Daniel Pitts:
Here are several other interesting ones:

test("showargs", "hello\" \" world");
test("showargs", "hello", "", "world");
test("showargs", "hello", "\"\"", "world");
test("showargs", "c:\\program files\\", "world");
test("showargs", "c:\\program files\\\\", "world");
test("showargs", "c:\\program files\\\\\\", "world");

Interesting, I expected the argv[1]=["hello world"] for the input "hello
world"]. It seems to be that way for Linux, but not on windows.

On Linux, the strings can be passed between processes "as-is" - without
adding quotes, escapes, or whatever. But on Windows, adding quotes and
escaping quotes that are part of the argument may be necessary. The
current ProcessBuilder implementation sucks at it. Also, no definitive
way of escaping quotes or backslashes exists, as each program could use
a different tokenizer. In practise, it's usually something compatible to
Microsoft's CommandLineToArgv function.


Regards,
Sven
 
S

Steven Simpson

Here are several other interesting ones:

test("showargs", "hello\" \" world");
test("showargs", "hello", "", "world");
test("showargs", "hello", "\"\"", "world");
test("showargs", "c:\\program files\\", "world");
test("showargs", "c:\\program files\\\\", "world");
test("showargs", "c:\\program files\\\\\\", "world");



Java: [showargs, hello" " world]
Arg count: 3
argv[0]=[showargs]
argv[1]=[hello]
argv[2]=[ world]
Java: [showargs, hello, , world]
Arg count: 3
argv[0]=[showargs]
argv[1]=[hello]
argv[2]=[world]
Java: [showargs, hello, "", world]
Arg count: 4
argv[0]=[showargs]
argv[1]=[hello]
argv[2]=[]
argv[3]=[world]
Java: [showargs, c:\program files\, world]
Arg count: 3
argv[0]=[showargs]
argv[1]=[c:\program files\]
argv[2]=[world]
Java: [showargs, c:\program files\\, world]
Arg count: 2
argv[0]=[showargs]
argv[1]=[c:\program files\" world]
Java: [showargs, c:\program files\\\, world]
Arg count: 3
argv[0]=[showargs]
argv[1]=[c:\program files\\]
argv[2]=[world]
 
S

Steven Simpson

Oh, and of course the ProcessBuilder doesn't behave as it is supposed
to. As mentioned in my first post, the String "\"a b\"" would be passed
unmodified to the program invoked. However, clearly, the string passed
to the program should have been
"\"\\\"a b\\\"\""

Only with the quotes and backslashes added, CommandLineToArgv would
decode it to "\"a b\"". With the current ProcessBuilder implementation,
a Windows program will see the parameter "a b" while on UNIX the program
will see "\"a b\"".

See
http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx
for details.

You're expecting Java to build its Windows command string something like
this?:

import java.util.*;
import java.util.regex.*;

public final class WindowsArgumentGenerator {
private WindowsArgumentGenerator() { }

private static final Pattern slashSequence =
Pattern.compile("\\\\*\"");

private static boolean needsQuotes(String arg) {
return arg.indexOf(' ') > -1;
}

public static String generateWindowsArgument(List<? extends String> args) {
StringBuilder out = new StringBuilder();
String sep = "";

for (String arg : args) {
out.append(sep);
sep = " ";

final boolean quoted = needsQuotes(arg);

if (quoted)
out.append('"');

Matcher m = slashSequence.matcher(arg);
int lastEnd = 0;
while (m.find()) {
out.append(arg.substring(lastEnd, m.start()));
final String slashes = m.group();
final int len = slashes.length() - 1;
out.append(slashes.substring(0, len))
.append('\\')
.append(slashes);
lastEnd = m.end();
}
out.append(arg.substring(lastEnd));

if (quoted)
out.append('"');
}

return out.toString();
}

public static void main(String[] args) throws Exception {
for (int i = 0; i < args.length; i++)
System.out.printf("argv[%d]=[%s]%n", i, args);
System.out.println(generateWindowsArgument(Arrays.asList(args)));
}
}

Does that work correctly for anything to be thrown at CommandLineToArgvW?

Note, that I assume that the program invoked uses CommandLineToArgv to
decode the command line. Which is by no means clear, as any program can
implement their own tokenizer.

Don't you find that a bit strange?

Perhaps that assumption is too risky for Java to make, e.g. there are
enough 'native' Windows/DOS commands around that the programmer is
likely to want to invoke, but don't use CommandLineToArgvW, and so would
be confused if they received a string escaped as above. Not a very
satisfactory situation.
 
S

Steven Simpson

23.04.2013 12:48, Steven Simpson kirjoitti:
<offtopic>java.lang.String is final, so nothing can extend it. </offtopic>

It's a matter of habit drawn from the general principle that if I don't
need to modify the list, I don't impose the additional, unnecessary
constraint on the caller, regardless of the element type.

Also, just because a class is final now, it might not be in the future.
It's harder to make an already published class final than to make a
final one non-final, because a currently final class won't have any
derivations that could be affected by its change of finality.
<potentially-dodgy-generalization>Finality is not final; non-finality
is.</potentially-dodgy-generalization> That said, it's not likely to
happen to String, and maybe there would be other consequences that keep
it unlikely. Nevertheless, my code will remain neutral on this matter,
whatever happens.

Furthermore, the class could be re-factored to use (say) CharSequence.
By sticking to the principle that is correct whether String or
CharSequence is used, I worry less about the specifics of the type in
use as I make the change.

import java.util.*;
import java.util.regex.*;

public final class WindowsArgumentGenerator {
private WindowsArgumentGenerator() { }

private static final Pattern slashSequence =
Pattern.compile("\\\\*\"");

private static final Pattern spaces = Pattern.compile(" ");

private static boolean needsQuotes(CharSequence arg) {
return spaces.matcher(arg).find();
}

public static String
generateWindowsArgument(List<? extends CharSequence> args) {
StringBuilder out = new StringBuilder();
String sep = "";

for (CharSequence arg : args) {
out.append(sep);
sep = " ";

final boolean quoted = needsQuotes(arg);

if (quoted)
out.append('"');

Matcher m = slashSequence.matcher(arg);
int lastEnd = 0;
while (m.find()) {
out.append(arg.subSequence(lastEnd, m.start()));
final String slashes = m.group();
final int len = slashes.length() - 1;
out.append(slashes.substring(0, len))
.append('\\')
.append(slashes);
lastEnd = m.end();
}
out.append(arg.subSequence(lastEnd, arg.length()));

if (quoted)
out.append('"');
}

return out.toString();
}

public static void main(String[] args) throws Exception {
for (int i = 0; i < args.length; i++)
System.out.printf("argv[%d]=[%s]%n", i, args);
System.out.println(generateWindowsArgument(Arrays.asList(args)));
}
}
 
D

Daniel Pitts

I don't understand this. In the first place List<? extends String> says
nothing about your ability to modify the list, you can add and remove
Strings to your hearts content,
Wrong.

List<? extends String> listOfStuff = new ArrayList<String>();

listOfStuff.add("foo"); // compiler error here!
secondly the only type you can pass as
argument to generateWindowsArgument is List<String> so the construct is
redundant anyway In fact, given that >>extends String<< is effectively
meaningless I'm surprised the compiler doesn't at least issue a warning.
In general, it is only meaningless in that String cannot be extended in
the current implementation of String. As Steven pointed out, that may
not be the case for future implementations, and the compiler will never
know that.
I suppose you get away with it because compile time type erasure gets
rid of any type arguments and as there can be only one type of argument
anyway a warning would be superfluous.
Erasure is a runtime phenomenon, don't get it confused with compile-time
static type safety. the <? extends String> is an edge case that makes
less sense, but if you treat it like you'd treat any other construct,
which does no harm BTW, then you simplify your reasoning for the general
case.
 
D

Daniel Pitts

Wrong.

List<? extends String> listOfStuff = new ArrayList<String>();

listOfStuff.add("foo"); // compiler error here!

erk, you're right, well I never, however it doesn't say anything about
the list outside of the method, how therefor does it avoid 'impose[ing]
the additional, unnecessary constraint on the caller' what constraint
does it avoid ?
In *this* case none. In the General case.

List<? extends MyFoo> fooList1;
List<MyFoo> fooList2;

fooList1 can be assigned an instance of List<MyFoo>, or List<MySubFoo>.
fooList2 can *only* be be assigned an instance of List<MyFoo>, not
List<MySubFoo>. This is an unnecessary constraint on the caller.

Again, in the case of "String", there isn't a difference. but that is a
special edge case which needn't change the general behavior.
In general, it is only meaningless in that String cannot be extended in
the current implementation of String. As Steven pointed out, that may
not be the case for future implementations, and the compiler will never
know that.

Well I reckon there's more chance of finding Elvis serving me with my
weekly fish and chips but OK, I suppose there's a rempote possibility it
might happen one day.
Erasure is a runtime phenomenon,

Erasure happens at compile time, generics are all about compile time
type checking, erasure removes type arguments at compile time after type
checking has occurred, the runtime system doesn't know anything about
type erasure or generics

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

[snip]

lipska
 
S

Steven Simpson

Does that work correctly for anything to be thrown at CommandLineToArgvW?

No, you idiot.

<http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx>

If the argument is:

C:\foo bar\

....quotes are needed around it to escape the space:

"C:\foo bar\"

....but a quote at the end will be misinterpreted as a literal quote, and
result in:

C:\foo bar"

....if it's allowed at all. The documentation was not clear on whether
quotes needed to be around the whole argument, or could be used within:

C:\foo" "bar

But then you have to watch for:

C:\foo\ bar\

....which has to be escaped as something like these:

C:\foo"\ "bar\
"C:\foo\ bar"\

The simplest option might be to do as before, but just put the 'final'
quote immediately before any trailing backslashes:

import java.util.*;
import java.util.regex.*;

public final class WindowsArgumentGenerator {
private WindowsArgumentGenerator() { }

private static final Pattern slashSequence = Pattern.compile("(\\\\*)\"");

private static final Pattern spaces = Pattern.compile("\\s");

private static boolean needsQuotes(CharSequence s) {
return spaces.matcher(s).find();
}

public static String
generateWindowsArgument(List<? extends CharSequence> args) {
StringBuilder out = new StringBuilder();
String sep = "";

for (final CharSequence arg : args) {
out.append(sep);
sep = " ";

final boolean quoted = needsQuotes(arg);
if (quoted)
out.append('"');

final int startOffset = out.length();

Matcher m = slashSequence.matcher(arg);
int lastEnd = 0;
while (m.find()) {
out.append(arg.subSequence(lastEnd, m.start()));
lastEnd = m.end();

final String slashes = m.group(1);

/* Double the slashes and add one, then add the
* quote. */
out.append(slashes)
.append(slashes)
.append('\\')
.append("\"");
}
out.append(arg.subSequence(lastEnd, arg.length()));

if (quoted) {
int i = out.length();
while (i > startOffset && out.charAt(i - 1) == '\\')
i--;
out.insert(i, '"');
}
}

return out.toString();
}

public static void main(String[] args) throws Exception {
for (int i = 0; i < args.length; i++)
System.out.printf("argv[%d]=[%s]%n", i, args);
System.out.println(generateWindowsArgument(Arrays.asList(args)));
}
}


Trying it out (in bash):

$ java WindowsArgumentGenerator 'hello world'
argv[0]=[hello world]
"hello world"
$ java WindowsArgumentGenerator '"hello world"'
argv[0]=["hello world"]
"\"hello world\""
$ java WindowsArgumentGenerator '"hello" "world"'
argv[0]=["hello" "world"]
"\"hello\" \"world\""
$ java WindowsArgumentGenerator 'hello" " world'
argv[0]=[hello" " world]
"hello\" \" world"
$ java WindowsArgumentGenerator 'hello' '""' 'world'
argv[0]=[hello]
argv[1]=[""]
argv[2]=[world]
hello \"\" world
$ java WindowsArgumentGenerator 'c:\program files\' 'world'
argv[0]=[c:\program files\]
argv[1]=[world]
"c:\program files"\ world
$ java WindowsArgumentGenerator 'c:\program files\\' 'world'
argv[0]=[c:\program files\\]
argv[1]=[world]
"c:\program files"\\ world
$ java WindowsArgumentGenerator 'c:\program files\\\' 'world'
argv[0]=[c:\program files\\\]
argv[1]=[world]
"c:\program files"\\\ world
 
D

Daniel Pitts

On 23/04/13 16:50, Daniel Pitts wrote:
On 4/23/13 8:37 AM, lipska the kat wrote:
On 23/04/13 14:55, Steven Simpson wrote:
On 23/04/13 11:31, Donkey Hottie wrote:
23.04.2013 12:48, Steven Simpson kirjoitti:
public static String generateWindowsArgument(List<? extends String>
args) {
<offtopic>java.lang.String is final, so nothing can extend it.
</offtopic>

It's a matter of habit drawn from the general principle that if I
don't
need to modify the list, I don't impose the additional, unnecessary
constraint on the caller, regardless of the element type.

I don't understand this. In the first place List<? extends String>
says
nothing about your ability to modify the list, you can add and remove
Strings to your hearts content,
Wrong.

List<? extends String> listOfStuff = new ArrayList<String>();

listOfStuff.add("foo"); // compiler error here!

erk, you're right, well I never, however it doesn't say anything about
the list outside of the method, how therefor does it avoid 'impose[ing]
the additional, unnecessary constraint on the caller' what constraint
does it avoid ?
In *this* case none.

Actually, in a way, List<? extends String> as a parameter to a method
might be considered as breaking encapsulation ... you are actually
exposing part of the internal workings of a component.

What about the internal workings? That it accepts a List of things it
can treat as Strings. I'm not seeing how that's breaking any encapsulation.

You're actually providing a guarantee that you won't be adding stuff to
this List (since it is a compiler error to do so).
Yes well this isn't the general case and I'm not convinced that writing
List<? extends String> as a parameter type because one day in the
distant future String might be mutable is a reason to do it, it's nasty
pointless and unnecessary complexity.
Sometimes if a general rule works for a special case, then it is easier
to follow that rule than to "optimize" for that special case.

The general rule here is
if you aren't writing to it said:
Just my opinion of course. :)

Of course. Same goes for so much that is posted here by everyone. Some
are better opinions than others, but always on a case-by-case basis :)
 
A

Arved Sandstrom

Wrong.

List<? extends String> listOfStuff = new ArrayList<String>();

listOfStuff.add("foo"); // compiler error here!
[ SNIP ]

One reason (good simple example, btw) of why I don't even bugger about
with constructs like this, when

List<Parent> listOfStuff = new ArrayList<Parent>();

listOfStuff.add(new Child());

(with Child being a class that extends Parent) works just fine and is
easier to intuitively understand.

This is just my opinion, but there is something wrong about a language
when the example you presented fails to compile, which it does fail to
do. It's counter-intuitive, bespeaks mediocre design, and I avoid it.

AHS
 
S

Sven Köhler

Am 23.04.2013 12:48, schrieb Steven Simpson:
You're expecting Java to build its Windows command string something like
this?:
...code..

Does that work correctly for anything to be thrown at CommandLineToArgvW?

I haven't checked your code, but I have written the inverse to
CommandLineToArgv in Java. It's not that hard. Passing the result to
ProcessBuilder works fine - even though that it works is based on the
two silly undocumented facts: (1) ProcessBuilder adds quotes if and only
if the argument doesn't start and end with quotes and (2) ProcessBuilder
doesn't mess with quotes and backslashes inside the arguments.

The problem with Java's ProcessBuilder is, that people use it "willy
nilly" being completely oblivous about the fact that it doesn't do what
might be best for them. (Recall the case "c:\\program files\\", "world"
where the two strings are basically merged to "c:\\program files\"
world". This is probably the scariest.)
Secondly, there is no way to pass the arguments correctly by only
relying on documented stuff.
Perhaps that assumption is too risky for Java to make, e.g. there are
enough 'native' Windows/DOS commands around that the programmer is
likely to want to invoke, but don't use CommandLineToArgvW, and so would
be confused if they received a string escaped as above. Not a very
satisfactory situation.

Some assumption about the program's tokenizer are already part of
ProcessBuilder's implementation (namely that they understand quotes
around arguments with spaces). Isn't that risky already?

It still puzzles me: The documentation explicitly mentions operating
systems, where programs perform the tokenization themselves. It even
suggests that the String-list should exist of exactly two elements
(which could maybe interpretated as a hint that the second element could
be the raw command line parameter string). But instead, what we get, is
an imperfect, error prone, undocumented mangling of arguments: adding
quotes without taking care of the quotes within the strings is
something, that shouldn't slip any programmer's attention.


Regards,
Sven
 
S

Sven Köhler

Am 23.04.2013 00:07, schrieb Martin Gregorie:
My testprog works exactly the same when run from within my
TestProcessBuilder test class as it does when run stand-alone from the
command line:

$ java TestProcessBuilder testprog "hello world" "\"hello world\""
"\"hello\" \"world\"" "hello ""double quoted"" world"
argc=5
argv[0]=testprog
argv[1]=hello world
argv[2]="hello world"
argv[3]="hello" "world"

Please look at the results that Steven posted. If the String "hello\"
\"world" is passed to the ProcessBuilder, the result was:
argv[1]=[hello]
argv[2]=[world]

Oh, and then there was the neat case, of passing the two argument
strings "c:\program files\" and "world" to the ProcessBuilder. The case
is neat, because the external process started by ProcessBuilder is
invoked with the argument string representing the path
c:\program files\" world


Regards,
Sven
 
S

Sven Köhler

Am 23.04.2013 21:56, schrieb Steven Simpson:
$ java WindowsArgumentGenerator 'c:\program files\\\' 'world'
argv[0]=[c:\program files\\\]
argv[1]=[world]
"c:\program files"\\\ world

That doesn't look right. A correct escaping would be
"c:\program files\\\\\\" world

You have to double the number of backslashes if they preceed a quote.
Also, you have to add another backslash if the quote does not terminate
the argument.

Here's my attempt to the do the escaping:
http://sourceforge.net/p/lejos/code...org/lejos/nxt/ldt/util/LeJOSNXJUtil.java#l421
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,172
Latest member
NFTPRrAgenncy
Top