www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - I have a dream that...

reply jicman <jicman_member pathlink.com> writes:
..someday std.process would have a system call that would return the output of
a std.process.system() call.  Yes, I know I could pipe it a file and read the
file.  But perl has this beautiful thing,

 text = `somecmd opt1 opt2 etc`;

where the array  text now has all the output caused by the execution of

somecmd opt1 opt2 etc

there is no need to open a file or anything.  It's already there.  Oh, by the
way, I am not saying that it should be system, but something more like

char[][] systemOut()

instead of returning the always ambiguous int. :-)

Anyone agrees?  Or am I just in right field behind the first baseman?

thanks.

josť
Mar 13 2005
next sibling parent reply Niall FitzGibbon <billdoor gmail.com> writes:
jicman wrote:
 I am not saying that it should be system, but something more like
 
 char[][] systemOut()
 
 instead of returning the always ambiguous int. :-)
 
 Anyone agrees?  Or am I just in right field behind the first baseman?

Why char[][] and not char[]? How would you get the exit value of the process without the int return? What if you wanted to grab a stream other than stdout? I think a more generic solution would be better, but then I personally don't really need to use this feature that much.
Mar 13 2005
next sibling parent reply jicman <jicman_member pathlink.com> writes:
Niall FitzGibbon says...
jicman wrote:
 I am not saying that it should be system, but something more like
 
 char[][] systemOut()
 
 instead of returning the always ambiguous int. :-)
 
 Anyone agrees?  Or am I just in right field behind the first baseman?

Why char[][] and not char[]?

Hey, I take either. The reason why char[][] is better is that I have everything broken down in lines, for me. There is no more char[][] s = split(str,"\n\r"); But instead a beautiful foreach loop would give me all the lines. :-)
How would you get the exit value of the 
process without the int return?

That is the reason for the new add (systemOut) or whatever Walter wants. I am not picky. If you want the int return value then use the actual system() call. I very seldom care for the return, but more for the output. It gives me information, plus it tells me if it worked or not. If I ever need to get the int return, then I'd just use the now resident system function.
 What if you wanted to grab a stream other than stdout?

Good question, my friend. Perhaps, systemErr() is in order. ;-)
  I think a more generic solution would be better, but 
then I personally don't really need to use this feature that much.

Then, sorry to say it, but why even ask those questions? A nice yes or no would suffice. ;-) Just kidding man, but this is one of the reasons why this is getting a little too much bandwidth. If you didn't care, then why provoke with the questions? Sorry, but... ;-) josť
Mar 13 2005
parent reply Niall FitzGibbon <billdoor gmail.com> writes:
jicman wrote:

 Then, sorry to say it, but why even ask those questions?  A nice yes or no
would
 suffice. ;-)  Just kidding man, but this is one of the reasons why this is
 getting a little too much bandwidth.  If you didn't care, then why provoke with
 the questions?  Sorry, but... ;-)

Just interested, that's all. Sorry if my post seemed confrontational or unnecessary :) I think such a feature would be good, but as I said I think it would be nicer to be able to retrieve the exit value *and* the stream output(s) with a single call -- in some situations there would be a need for both. How about just an overload of the existing function? int system(char[] command, out char[] stdOut, out char[] stdErr)
Mar 13 2005
next sibling parent jicman <jicman_member pathlink.com> writes:
Niall FitzGibbon says...
jicman wrote:

 Then, sorry to say it, but why even ask those questions?  A nice yes or no
would
 suffice. ;-)  Just kidding man, but this is one of the reasons why this is
 getting a little too much bandwidth.  If you didn't care, then why provoke with
 the questions?  Sorry, but... ;-)

Just interested, that's all. Sorry if my post seemed confrontational or unnecessary :)

It was not. I was just kidding... If you haven't figured it by now, I'm a kidder. :-)
I think such a feature would be good, but as I said I think it would be 
nicer to be able to retrieve the exit value *and* the stream output(s) 
with a single call -- in some situations there would be a need for both.

How about just an overload of the existing function?

int system(char[] command, out char[] stdOut, out char[] stdErr)

This would be perfect, man. Perfect indeed. Now, if I could just convince Walter and the rest of the gang, I'll be in good shape. jic
Mar 13 2005
prev sibling next sibling parent Derek Parnell <derek psych.ward> writes:
On Sun, 13 Mar 2005 21:49:35 +0000, Niall FitzGibbon wrote:

 jicman wrote:
 
 Then, sorry to say it, but why even ask those questions?  A nice yes or no
would
 suffice. ;-)  Just kidding man, but this is one of the reasons why this is
 getting a little too much bandwidth.  If you didn't care, then why provoke with
 the questions?  Sorry, but... ;-)

Just interested, that's all. Sorry if my post seemed confrontational or unnecessary :) I think such a feature would be good, but as I said I think it would be nicer to be able to retrieve the exit value *and* the stream output(s) with a single call -- in some situations there would be a need for both. How about just an overload of the existing function? int system(char[] command, out char[] stdOut, out char[] stdErr)

Now that would be a nice routine! It would simplify the process of capturing output to the point where it would be a trivial exercise, and thus more people would bother to incorporate it into their application design. I know I could use it in Build ;-) -- Derek Parnell Melbourne, Australia http://www.dsource.org/projects/build 14/03/2005 9:27:47 AM
Mar 13 2005
prev sibling parent "Charles" <cee-lo green.com> writes:
This would be very cool , as in C++ this annoyingly hard to do with win32
:S.

Charlie


"Niall FitzGibbon" <billdoor gmail.com> wrote in message
news:d12chg$312o$1 digitaldaemon.com...
 jicman wrote:

 Then, sorry to say it, but why even ask those questions?  A nice yes or


 suffice. ;-)  Just kidding man, but this is one of the reasons why this


 getting a little too much bandwidth.  If you didn't care, then why


 the questions?  Sorry, but... ;-)

Just interested, that's all. Sorry if my post seemed confrontational or unnecessary :) I think such a feature would be good, but as I said I think it would be nicer to be able to retrieve the exit value *and* the stream output(s) with a single call -- in some situations there would be a need for both. How about just an overload of the existing function? int system(char[] command, out char[] stdOut, out char[] stdErr)

Mar 15 2005
prev sibling parent "Unknown W. Brackets" <unknown simplemachines.org> writes:
That's easy, add this to the end of the command:

2&>1

If I remember correctly, that redirects stderr to stdout...

-[Unknown]


 What if you wanted to grab a stream other than stdout?

Mar 13 2005
prev sibling parent reply "Unknown W. Brackets" <unknown simplemachines.org> writes:
I posted a popen implementation a bit earlier on.

news://news.digitalmars.com:119/cscp40$pee$1 digitaldaemon.com
digitalmars.D/14470

I think this is like... close to what you want.  It allows you to read 
from the popen (Linux or Windows) as if it were a file.  Without the 
intermediate temporary file ;).

Any function that returned a string or array thereof would use something 
like this, likely, I would think.

-[Unknown]


 ..someday std.process would have a system call that would return the output of
 a std.process.system() call.  Yes, I know I could pipe it a file and read the
 file.  But perl has this beautiful thing,
 
  text = `somecmd opt1 opt2 etc`;
 
 where the array  text now has all the output caused by the execution of
 
 somecmd opt1 opt2 etc
 
 there is no need to open a file or anything.  It's already there.  Oh, by the
 way, I am not saying that it should be system, but something more like
 
 char[][] systemOut()
 
 instead of returning the always ambiguous int. :-)
 
 Anyone agrees?  Or am I just in right field behind the first baseman?
 
 thanks.
 
 josť
 
 

Mar 13 2005
parent reply jicman <jicman_member pathlink.com> writes:
Unknown,

this would work.  I hadn't noticed this.  Still, though, a systemOut() also be
nice.

thanks.

Is there any way that you can zip this and attach it again?  I don't want to
copy and paste it and I am using the web browser and not the news reader.

thanks again.

jic

Unknown W. Brackets says...
I posted a popen implementation a bit earlier on.

news://news.digitalmars.com:119/cscp40$pee$1 digitaldaemon.com
digitalmars.D/14470

I think this is like... close to what you want.  It allows you to read 
from the popen (Linux or Windows) as if it were a file.  Without the 
intermediate temporary file ;).

Any function that returned a string or array thereof would use something 
like this, likely, I would think.

-[Unknown]


 ..someday std.process would have a system call that would return the output of
 a std.process.system() call.  Yes, I know I could pipe it a file and read the
 file.  But perl has this beautiful thing,
 
  text = `somecmd opt1 opt2 etc`;
 
 where the array  text now has all the output caused by the execution of
 
 somecmd opt1 opt2 etc
 
 there is no need to open a file or anything.  It's already there.  Oh, by the
 way, I am not saying that it should be system, but something more like
 
 char[][] systemOut()
 
 instead of returning the always ambiguous int. :-)
 
 Anyone agrees?  Or am I just in right field behind the first baseman?
 
 thanks.
 
 josť
 
 


Mar 13 2005
parent reply J C Calvarese <jcc7 cox.net> writes:
jicman wrote:
 Unknown,
 
 this would work.  I hadn't noticed this.  Still, though, a systemOut() also be
 nice.
 
 thanks.
 
 Is there any way that you can zip this and attach it again?  I don't want to
 copy and paste it and I am using the web browser and not the news reader.

Here it is (watch out for word-wrap): module popen; private import std.c.stdio; private import std.stream; unittest { // Just some old command; testing the stderr piping. ProcessStream test = new ProcessStream("diff 2>&1"); std.stream.stdout.writeLine(test.readLine()); std.stream.stdout.writeLine(test.readLine()); std.stream.stdout.flush(); // Talk about a useless waste :P! test = new ProcessStream("cat", FileMode.Out); test.writeLine("There's no place like stdout."); } // ProcessStream class - used to read or write (can't do both) from/to a process. class ProcessStream: Stream { // The FILE* handle to the process's output. private FILE* handle = null; // Seems this is private, so I had to override it; whether the file is open. private override bool isopen = false; // Create a new ProcessStream without any actual command/arguments. this() { super(); // Clean up, this sets all the same variables anyway. this.close(); } // A quick way to create the class and open it at the same time... this(char[] command, FileMode mode = FileMode.In) { super(); this.open(command, mode); } ~this() { this.close(); } void open(char[] command, FileMode mode = FileMode.In) { // Make sure we're all closed off ;) . this.close(); // These are pretty simple and come nearly straight from the File class. this.readable = cast(bit) (mode & FileMode.In); this.writeable = cast(bit) (mode & FileMode.Out); this.seekable = false; // TODO: Binary flag? this.handle = popen(command, mode == FileMode.In ? "r" : "w"); // Throw an OpenException if anything goes arye. if (this.handle == null) { this.isopen = false; throw new OpenException("couldn't pipe " ~ command); } } // Close the stream, or make sure everything is clean if the stream isn't open. override void close() { if (this.isopen && this.handle != null) pclose(this.handle); this.handle = null; this.isopen = false; this.readable = false; this.writeable = false; this.seekable = false; } override uint readBlock(void* buffer, uint size) in { // Not only must it be readable, but the handle must not be null (isopen might be equivalent.) assert(this.readable); assert(this.handle != null); } body { // Map straight to fread. return fread(buffer, 1, size, this.handle); } override uint writeBlock(void* buffer, uint size) in { // Check to make sure we're not writing to an unwritable stream. assert(this.writeable); assert(this.handle != null); } body { // Again, fwrite works perfectly for FILEs. return fwrite(buffer, 1, size, this.handle); } override ulong seek(long offset, SeekPos whence) { // If I throw an error here, nothing works, so zero will have to do. return 0; } } version (linux) { // Blissfully easy. extern (C) { FILE* popen(char* command, char* type); int pclose(FILE* stream); } } else version (Windows) { private import std.c.windows.windows; // For memset. private import std.string; // Some of this come from winbase.h. extern (Windows) { struct PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } struct STARTUPINFO { DWORD cb; LPTSTR lpReserved; LPTSTR lpDesktop; LPTSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } alias HANDLE* PHANDLE, LPHANDLE; alias STARTUPINFO* LPSTARTUPINFO; alias PROCESS_INFORMATION* LPPROCESS_INFORMATION; alias int intptr_t; const int O_BINARY = 0x8000, O_TEXT = 0x4000; const int O_RDONLY = 0, O_WRONLY = 1; const uint STD_INPUT_HANDLE = cast(uint) -10; const uint STD_OUTPUT_HANDLE = cast(uint) -11; const int STARTF_USESTDHANDLES = 0x00000100; const int DUPLICATE_CLOSE_SOURCE = 0x00000001; const int DUPLICATE_SAME_ACCESS = 0x00000002; const int NORMAL_PRIORITY_CLASS = 0x00000020; const int INFINITE = 0x0FFFFFFF; BOOL CloseHandle(HANDLE hObject); BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize); BOOL CreateProcessA(LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation); BOOL CreateProcessW(LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation); BOOL DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions); BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode); HANDLE GetStdHandle(DWORD nStdHandle); DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); } // Grab some useful C functions too. extern (C) { int _open_osfhandle(intptr_t osfhandle, int flags); FILE* fdopen(int fd, char *mode); } /* Okay, this is essentially a map from FILE pointer to HANDLE. It's needed so the HANDLE can be freed at the same time the FILE is, without changing the syntax of popen(). Alternatively, it could be encapsulated within a class; but I like popen ^_^. */ private HANDLE[FILE*] popenBuffer; /* This is the actual implementation of popen. I put this together from various examples on Google. */ FILE* popen(char* command, char* type) { STARTUPINFO startup; PROCESS_INFORMATION process; SECURITY_ATTRIBUTES security; HANDLE inH, outH; // The handle needs to be inherited... security.nLength = SECURITY_ATTRIBUTES.sizeof; security.bInheritHandle = true; security.lpSecurityDescriptor = null; // Attempt to create the pipe between our two handles. if (!CreatePipe(&inH, &outH, &security, 2048L)) return null; memset(&startup, 0, STARTUPINFO.sizeof); memset(&process, 0, PROCESS_INFORMATION.sizeof); startup.cb = STARTUPINFO.sizeof; startup.dwFlags = STARTF_USESTDHANDLES; // TODO: Check consistency on Linux (no stderr.) startup.hStdError = find(toString(command), "2>&1") != -1 ? outH : INVALID_HANDLE_VALUE; // A quick nested function to simplify the calls... void duplicateHandle(HANDLE fix) { HANDLE copy, self = GetCurrentProcess(); if (!DuplicateHandle(self, fix, self, &copy, 0, FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) fix = null; else fix = copy; } // We're going to use our handles - so, if we're reading we want to capture its stdout. if (type[0] == 'r') { duplicateHandle(inH); startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE); startup.hStdOutput = outH; } // Otherwise, we're writing, so we want to capture stdin. else { duplicateHandle(outH); startup.hStdInput = inH; startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); } // TODO: Why won't CreateProcessW work here at all? if (!CreateProcessA(null, command, &security, &security, security.bInheritHandle, NORMAL_PRIORITY_CLASS, null, null, &startup, &process)) return null; CloseHandle(process.hThread); // Now, we want to actually get the FILE (to emulate popen) so... int fno, mode = strlen(type) == 2 && type[1] == 'b' ? O_BINARY : O_TEXT; if (type[0] == 'r') { fno = _open_osfhandle(cast(long) inH, O_RDONLY | mode); CloseHandle(outH); } else { fno = _open_osfhandle(cast(long) outH, O_WRONLY | mode); CloseHandle(inH); } // Now it's child's play. Open 'er up and stick the process handle in the lookup. // TODO: Why won't using plain type work? FILE* stream = fdopen(fno, "r+"); popenBuffer[stream] = process.hProcess; return stream; } int pclose(FILE* stream) { DWORD termstat = 0; HANDLE process = popenBuffer[stream]; // Flush any buffer(s) and say sianara. fflush(stream); fclose(stream); // Wait for it to quit and get its exit code... I hope INFINITE is okay here. WaitForSingleObject(process, INFINITE); GetExitCodeProcess(process, &termstat); CloseHandle(process); // Get rid of useless clutter explicitly. delete popenBuffer[stream]; return termstat; } } else { // This probably isn't needed; I'm sure most UNIX based operating systems have popen, no? Does Mac OS X? static assert(0); } -- Justin (a/k/a jcc7) http://jcc_7.tripod.com/d/
Mar 13 2005
parent "Charles" <cee-lo green.com> writes:
 WaitForSingleObject(process, INFINITE);

I've been bitten by this one :). Charlie "J C Calvarese" <jcc7 cox.net> wrote in message news:d12i3m$4gk$1 digitaldaemon.com...
 jicman wrote:
 Unknown,

 this would work.  I hadn't noticed this.  Still, though, a systemOut()


 nice.

 thanks.

 Is there any way that you can zip this and attach it again?  I don't


 copy and paste it and I am using the web browser and not the news


 Here it is (watch out for word-wrap):

 module popen;

 private import std.c.stdio;
 private import std.stream;

 unittest
 {
 // Just some old command; testing the stderr piping.
 ProcessStream test = new ProcessStream("diff 2>&1");

 std.stream.stdout.writeLine(test.readLine());
 std.stream.stdout.writeLine(test.readLine());
 std.stream.stdout.flush();

 // Talk about a useless waste :P!
 test = new ProcessStream("cat", FileMode.Out);

 test.writeLine("There's no place like stdout.");
 }

 // ProcessStream class - used to read or write (can't do both) from/to a
 process.
 class ProcessStream: Stream
 {
 // The FILE* handle to the process's output.
 private FILE* handle = null;
 // Seems this is private, so I had to override it; whether the file is
 open.
 private override bool isopen = false;

 // Create a new ProcessStream without any actual command/arguments.
 this()
 {
 super();

 // Clean up, this sets all the same variables anyway.
 this.close();
 }

 // A quick way to create the class and open it at the same time...
 this(char[] command, FileMode mode = FileMode.In)
 {
 super();

 this.open(command, mode);
 }

 ~this()
 {
 this.close();
 }

 void open(char[] command, FileMode mode = FileMode.In)
 {
 // Make sure we're all closed off  ;) .
 this.close();

 // These are pretty simple and come nearly straight from the File class.
 this.readable = cast(bit) (mode & FileMode.In);
 this.writeable = cast(bit) (mode & FileMode.Out);
 this.seekable = false;

 // TODO: Binary flag?
 this.handle = popen(command, mode == FileMode.In ? "r" : "w");

 // Throw an OpenException if anything goes arye.
 if (this.handle == null)
 {
 this.isopen = false;
 throw new OpenException("couldn't pipe " ~ command);
 }
 }

 // Close the stream, or make sure everything is clean if the stream
 isn't open.
 override void close()
 {
 if (this.isopen && this.handle != null)
 pclose(this.handle);

 this.handle = null;
 this.isopen = false;
 this.readable = false;
 this.writeable = false;
 this.seekable = false;
 }

 override uint readBlock(void* buffer, uint size)
 in
 {
 // Not only must it be readable, but the handle must not be null
 (isopen might be equivalent.)
 assert(this.readable);
 assert(this.handle != null);
 }
 body
 {
 // Map straight to fread.
 return fread(buffer, 1, size, this.handle);
 }

 override uint writeBlock(void* buffer, uint size)
 in
 {
 // Check to make sure we're not writing to an unwritable stream.
 assert(this.writeable);
 assert(this.handle != null);
 }
 body
 {
 // Again, fwrite works perfectly for FILEs.
 return fwrite(buffer, 1, size, this.handle);
 }

 override ulong seek(long offset, SeekPos whence)
 {
 // If I throw an error here, nothing works, so zero will have to do.
 return 0;
 }
 }

 version (linux)
 {
 // Blissfully easy.
 extern (C)
 {
 FILE* popen(char* command, char* type);
 int pclose(FILE* stream);
 }
 }
 else version (Windows)
 {
 private import std.c.windows.windows;

 // For memset.
 private import std.string;

 // Some of this come from winbase.h.
 extern (Windows)
 {
 struct PROCESS_INFORMATION
 {
 HANDLE hProcess;
 HANDLE hThread;
 DWORD dwProcessId;
 DWORD dwThreadId;
 }
 struct STARTUPINFO
 {
 DWORD cb;
 LPTSTR lpReserved;
 LPTSTR lpDesktop;
 LPTSTR lpTitle;
 DWORD dwX;
 DWORD dwY;
 DWORD dwXSize;
 DWORD dwYSize;
 DWORD dwXCountChars;
 DWORD dwYCountChars;
 DWORD dwFillAttribute;
 DWORD dwFlags;
 WORD wShowWindow;
 WORD cbReserved2;
 LPBYTE lpReserved2;
 HANDLE hStdInput;
 HANDLE hStdOutput;
 HANDLE hStdError;
 }

 alias HANDLE* PHANDLE, LPHANDLE;
 alias STARTUPINFO* LPSTARTUPINFO;
 alias PROCESS_INFORMATION* LPPROCESS_INFORMATION;
 alias int intptr_t;

 const int O_BINARY = 0x8000, O_TEXT = 0x4000;
 const int O_RDONLY = 0, O_WRONLY = 1;
 const uint STD_INPUT_HANDLE = cast(uint) -10;
 const uint STD_OUTPUT_HANDLE = cast(uint) -11;
 const int STARTF_USESTDHANDLES = 0x00000100;
 const int DUPLICATE_CLOSE_SOURCE = 0x00000001;
 const int DUPLICATE_SAME_ACCESS = 0x00000002;
 const int NORMAL_PRIORITY_CLASS = 0x00000020;
 const int INFINITE = 0x0FFFFFFF;

 BOOL CloseHandle(HANDLE hObject);
 BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe,
 LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize);
 BOOL CreateProcessA(LPCTSTR lpApplicationName, LPTSTR lpCommandLine,
 LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES
 lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID
 lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo,
 LPPROCESS_INFORMATION lpProcessInformation);
 BOOL CreateProcessW(LPCTSTR lpApplicationName, LPTSTR lpCommandLine,
 LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES
 lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID
 lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo,
 LPPROCESS_INFORMATION lpProcessInformation);
 BOOL DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE
 hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle,
 DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions);
 BOOL GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode);
 HANDLE GetStdHandle(DWORD nStdHandle);
 DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
 }

 // Grab some useful C functions too.
 extern (C)
 {
 int _open_osfhandle(intptr_t osfhandle, int flags);
 FILE* fdopen(int fd, char *mode);
 }

 /* Okay, this is essentially a map from FILE pointer to HANDLE.
    It's needed so the HANDLE can be freed at the same time the FILE
    is, without changing the syntax of popen().  Alternatively, it
    could be encapsulated within a class; but I like popen ^_^. */
 private HANDLE[FILE*] popenBuffer;

 /* This is the actual implementation of popen.  I put this together
    from various examples on Google. */
 FILE* popen(char* command, char* type)
 {
 STARTUPINFO startup;
 PROCESS_INFORMATION process;
 SECURITY_ATTRIBUTES security;
 HANDLE inH, outH;

 // The handle needs to be inherited...
 security.nLength = SECURITY_ATTRIBUTES.sizeof;
 security.bInheritHandle = true;
 security.lpSecurityDescriptor = null;

 // Attempt to create the pipe between our two handles.
 if (!CreatePipe(&inH, &outH, &security, 2048L))
 return null;

 memset(&startup, 0, STARTUPINFO.sizeof);
 memset(&process, 0, PROCESS_INFORMATION.sizeof);

 startup.cb = STARTUPINFO.sizeof;
 startup.dwFlags = STARTF_USESTDHANDLES;
 // TODO: Check consistency on Linux (no stderr.)
 startup.hStdError = find(toString(command), "2>&1") != -1 ? outH :
 INVALID_HANDLE_VALUE;

 // A quick nested function to simplify the calls...
 void duplicateHandle(HANDLE fix)
 {
 HANDLE copy, self = GetCurrentProcess();

 if (!DuplicateHandle(self, fix, self, &copy, 0, FALSE,
 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
 fix = null;
 else
 fix = copy;
 }

 // We're going to use our handles - so, if we're reading we want to
 capture its stdout.
 if (type[0] == 'r')
 {
 duplicateHandle(inH);
 startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
 startup.hStdOutput = outH;
 }
 // Otherwise, we're writing, so we want to capture stdin.
 else
 {
 duplicateHandle(outH);
 startup.hStdInput = inH;
 startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
 }

 // TODO: Why won't CreateProcessW work here at all?
 if (!CreateProcessA(null, command, &security, &security,
 security.bInheritHandle, NORMAL_PRIORITY_CLASS, null, null, &startup,
 &process))
 return null;
 CloseHandle(process.hThread);

 // Now, we want to actually get the FILE (to emulate popen) so...
 int fno, mode = strlen(type) == 2 && type[1] == 'b' ? O_BINARY : O_TEXT;
 if (type[0] == 'r')
 {
 fno = _open_osfhandle(cast(long) inH, O_RDONLY | mode);
 CloseHandle(outH);
 }
 else
 {
 fno = _open_osfhandle(cast(long) outH, O_WRONLY | mode);
 CloseHandle(inH);
 }

 // Now it's child's play.  Open 'er up and stick the process handle in
 the lookup.
 // TODO: Why won't using plain type work?
 FILE* stream = fdopen(fno, "r+");
 popenBuffer[stream] = process.hProcess;

 return stream;
 }

 int pclose(FILE* stream)
 {
 DWORD termstat = 0;
 HANDLE process = popenBuffer[stream];

 // Flush any buffer(s) and say sianara.
 fflush(stream);
 fclose(stream);

 // Wait for it to quit and get its exit code... I hope INFINITE is
 okay here.
 WaitForSingleObject(process, INFINITE);
 GetExitCodeProcess(process, &termstat);
 CloseHandle(process);

 // Get rid of useless clutter explicitly.
 delete popenBuffer[stream];

 return termstat;
 }
 }
 else
 {
 // This probably isn't needed; I'm sure most UNIX based operating
 systems have popen, no?  Does Mac OS X?
 static assert(0);
 }

 --
 Justin (a/k/a jcc7)
 http://jcc_7.tripod.com/d/

Mar 15 2005