digitalmars.D - I have a dream that...
- jicman (13/13) Mar 13 2005 ..someday std.process would have a system call that would return the out...
- Niall FitzGibbon (5/12) Mar 13 2005 Why char[][] and not char[]? How would you get the exit value of the
- jicman (16/30) Mar 13 2005 Hey, I take either. The reason why char[][] is better is that I have ev...
- Niall FitzGibbon (8/12) Mar 13 2005 Just interested, that's all. Sorry if my post seemed confrontational or...
- jicman (6/18) Mar 13 2005 It was not. I was just kidding... If you haven't figured it by now, I'm...
- Derek Parnell (11/28) Mar 13 2005 Now that would be a nice routine!
- Charles (8/20) Mar 15 2005 This would be very cool , as in C++ this annoyingly hard to do with win3...
- Unknown W. Brackets (4/5) Mar 13 2005 That's easy, add this to the end of the command:
- Unknown W. Brackets (9/33) Mar 13 2005 I posted a popen implementation a bit earlier on.
- jicman (9/42) Mar 13 2005 Unknown,
- J C Calvarese (289/298) Mar 13 2005 Here it is (watch out for word-wrap):
- Charles (7/306) Mar 15 2005 I've been bitten by this one :).
..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
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
Niall FitzGibbon says...jicman wrote: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. :-)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?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
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
Niall FitzGibbon says...jicman wrote:It was not. I was just kidding... If you haven't figured it by now, I'm a kidder. :-)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)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
On Sun, 13 Mar 2005 21:49:35 +0000, Niall FitzGibbon wrote:jicman wrote: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 AMThen, 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
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:no wouldThen, sorry to say it, but why even ask those questions? A nice yes orissuffice. ;-) Just kidding man, but this is one of the reasons why thisprovoke withgetting a little too much bandwidth. If you didn't care, then whythe 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
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
I posted a popen implementation a bit earlier on. news://news.digitalmars.com:119/cscp40$pee$1 digitaldaemon.com http://www.digitalmars.com/drn-bin/wwwnews?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
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 http://www.digitalmars.com/drn-bin/wwwnews?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
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, ©, 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
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:also beUnknown, this would work. I hadn't noticed this. Still, though, a systemOut()want tonice. thanks. Is there any way that you can zip this and attach it again? I don'treader.copy and paste it and I am using the web browser and not the newsHere 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, ©, 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