digitalmars.D.learn - std.path.shell throws exception with garbage string
- Andrej Mitrovic (22/22) Mar 09 2011 import std.process;
- Andrej Mitrovic (13/13) Mar 09 2011 Found myself a solution. And probably the cause of the issue. shell()
- Jonathan M Davis (22/57) Mar 09 2011 How about not feeding any strings with stray '\0' characters in it. shel...
import std.process;
void main()
{
char[] chBuffer = new char[](256);
chBuffer[] = '\0';
chBuffer[0..3] = "dir".dup;
auto result = shell(chBuffer.idup);
}
It does two things:
1. It prints out the result of the shell invocation to stdout. This shouldn't
happen.
2. It throws this:
std.file.FileException std\file.d(295):
5a5785b9a9ef300e292f021170a6bb2e34b80c86bb8decbb6b9b8d3b5e852cd
This sample works:
import std.process;
void main()
{
string chBuffer = "dir";
auto result = shell(chBuffer);
}
Here the shell invocation isn't printed to the screen, but stored in result
like it should be.
The problem is I'm working with the win32 API and I can't use lovely D strings.
I've tried using to!string and .idup on the call to 'shell', I've tried
appending null's to the char[] but nothing seems to help. What am I doing wrong?
Mar 09 2011
Found myself a solution. And probably the cause of the issue. shell()
doesn't expect a null-terminated string, but just a string with the
shell command without any newlines or nulls.
So I can do this (importing std.algorithm for until):
auto command = to!string(chBuffer[].until('\n'));
auto result = shell(command);
Not too shabby.
Still that error message wasn't of much help. shell() internally
creates a temp file with a random file name, temporarily redirects
stdout to that file to catch the contents of a system() invocation,
and then reads that file with readtext(). Somehow, something errors
out if there's a newline or null in the string, and it all explodes
with a weird error message.
Mar 09 2011
On Wednesday, March 09, 2011 15:55:07 Andrej Mitrovic wrote:
import std.process;
void main()
{
char[] chBuffer = new char[](256);
chBuffer[] = '\0';
chBuffer[0..3] = "dir".dup;
auto result = shell(chBuffer.idup);
}
It does two things:
1. It prints out the result of the shell invocation to stdout. This
shouldn't happen.
2. It throws this:
std.file.FileException std\file.d(295):
5a5785b9a9ef300e292f021170a6bb2e34b80c86bb8decbb6b9b8d3b5e852cd
This sample works:
import std.process;
void main()
{
string chBuffer = "dir";
auto result = shell(chBuffer);
}
Here the shell invocation isn't printed to the screen, but stored in result
like it should be.
The problem is I'm working with the win32 API and I can't use lovely D
strings. I've tried using to!string and .idup on the call to 'shell', I've
tried appending null's to the char[] but nothing seems to help. What am I
doing wrong?
How about not feeding any strings with stray '\0' characters in it. shell takes
a D string, which is _not_ null terminated. Looking at the implementation for
cmd on Windows, it ultimately calls std.c.process.system which _does_ take a
null terminated string, but the string that is fed to system has stuff _after_
the string that you give it. That means that feeding it a string with '\0' in
it
like that is going to give a bad string to system, so it fails. In order to get
the output of the command that you run, shell attempts to redirect the output
to
file, but your nulls screw it up and it doesn't get redirected and the file
never
gets created. The filename is a randomly generated number - hence the bizarre
exception message.
If you put '\0' in the middle of a string, D will just treat it as data,
whereas
functions will cut off at that point and assume that the string ended there. If
you're passing a string to a C function, make sure it's null-terminated. If
you're passing a string to a D function, do _not_ null-terminate it. You're
passing a null-terminated string to a D function. That's just asking for
trouble.
What you need to do is only pass the string up to - but not including - the
null
terminator to shell. If you have a char*, then to!string should do the right
thing. But if you have a char[], then you either need to slice it right before
the null terminator or use its ptr property and use to!string on _that_.
- Jonathan M Davis
Mar 09 2011









Andrej Mitrovic <andrej.mitrovich gmail.com> 