448 lines
13 KiB
C++
448 lines
13 KiB
C++
// Filename: pfstreamBuf.cxx
|
|
// Created by: cary (12Dec00)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
|
|
//
|
|
// All use of this software is subject to the terms of the Panda 3d
|
|
// Software license. You should have received a copy of this license
|
|
// along with this source code; you will also find a current copy of
|
|
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
|
|
//
|
|
// To contact the maintainers of this program write to
|
|
// panda3d-general@lists.sourceforge.net .
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "pfstreamBuf.h"
|
|
#include <assert.h>
|
|
|
|
PipeStreamBuf::PipeStreamBuf(PipeStreamBuf::Direction dir) :
|
|
_dir(dir)
|
|
{
|
|
init_pipe();
|
|
|
|
#ifndef HAVE_IOSTREAM
|
|
// These lines, which are essential on older implementations of the
|
|
// iostream library, are not understood by more recent versions.
|
|
allocate();
|
|
assert((dir == Input) || (dir == Output));
|
|
if (dir == Input) {
|
|
setg(base(), ebuf(), ebuf());
|
|
} else {
|
|
setp(base(), ebuf());
|
|
}
|
|
#endif /* HAVE_IOSTREAM */
|
|
}
|
|
|
|
PipeStreamBuf::
|
|
~PipeStreamBuf(void) {
|
|
if (is_open()) {
|
|
sync();
|
|
flush();
|
|
close_pipe();
|
|
}
|
|
}
|
|
|
|
void PipeStreamBuf::flush(void) {
|
|
assert(is_open());
|
|
if (_dir == Output) {
|
|
write_chars("", 0, true);
|
|
}
|
|
}
|
|
|
|
void PipeStreamBuf::command(const string cmd) {
|
|
assert(!is_open());
|
|
open_pipe(cmd);
|
|
}
|
|
|
|
int PipeStreamBuf::overflow(int c) {
|
|
assert(is_open());
|
|
assert(_dir == Output);
|
|
streamsize n = pptr() - pbase();
|
|
if (n != 0) {
|
|
write_chars(pbase(), n, false);
|
|
pbump(-n); // reset pptr()
|
|
}
|
|
if (c != EOF) {
|
|
// write one more character
|
|
char ch = c;
|
|
write_chars(&ch, 1, false);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int PipeStreamBuf::sync(void) {
|
|
assert(is_open());
|
|
if (_dir == Output) {
|
|
streamsize n = pptr() - pbase();
|
|
write_chars(pbase(), n, false);
|
|
pbump(-n);
|
|
} else {
|
|
streamsize n = egptr() - gptr();
|
|
if (n != 0) {
|
|
gbump(n); // flush all our stored input away
|
|
#ifndef NDEBUG
|
|
cerr << "pfstream tossed out " << n << " bytes" << endl;
|
|
#endif
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int PipeStreamBuf::underflow(void) {
|
|
assert(_dir == Input);
|
|
if ((eback() == (char*)0L) || (gptr() == (char*)0L) ||
|
|
(egptr() == (char*)0L)) {
|
|
// must be new-style iostream library
|
|
char* buf = new char[4096];
|
|
char* ebuf = &(buf[4096]);
|
|
setg(buf, ebuf, ebuf);
|
|
}
|
|
if (gptr() < egptr()) {
|
|
char c = *(gptr());
|
|
return c;
|
|
}
|
|
if (eof_pipe()) {
|
|
return EOF;
|
|
}
|
|
#ifdef HAVE_IOSTREAM
|
|
size_t len = 4096;
|
|
#else /* HAVE_IOSTREAM */
|
|
size_t len = ebuf() - base();
|
|
#endif /* HAVE_IOSTREAM */
|
|
char* buf = new char[len];
|
|
size_t n = read_pipe(buf, len);
|
|
int ret = buf[0];
|
|
if (n == 0)
|
|
ret = EOF;
|
|
else {
|
|
#ifdef HAVE_IOSTREAM
|
|
memcpy(eback()+(len-n), buf, n);
|
|
#else /* HAVE_IOSTREAM */
|
|
memcpy(base()+(len-n), buf, n);
|
|
#endif /* HAVE_IOSTREAM */
|
|
gbump(-((int)n));
|
|
}
|
|
delete buf;
|
|
return ret;
|
|
}
|
|
|
|
void PipeStreamBuf::write_chars(const char* start, int length, bool flush) {
|
|
assert(_dir == Output);
|
|
size_t orig = _line_buffer.length();
|
|
string latest(start, length);
|
|
string line;
|
|
|
|
if (flush) {
|
|
// if we're going to flush the stream now, we dump the whole thing
|
|
// reguardless of whether we have reached end-of-line.
|
|
line = _line_buffer + latest;
|
|
_line_buffer = "";
|
|
} else {
|
|
// Otherwise, we check for the end-of-line character.
|
|
_line_buffer += latest;
|
|
size_t eol = _line_buffer.rfind('\n', orig);
|
|
if (eol != string::npos) {
|
|
line = _line_buffer.substr(0, eol+1);
|
|
_line_buffer = _line_buffer.substr(eol+1);
|
|
}
|
|
}
|
|
// now output 'line'
|
|
size_t wrote = write_pipe(line.c_str(), line.length());
|
|
#ifndef NDEBUG
|
|
if (wrote != line.length())
|
|
cerr << "wrote only " << wrote << " of " << line.length()
|
|
<< " bytes to pipe" << endl;
|
|
#endif
|
|
}
|
|
|
|
#ifndef WIN_PIPE_CALLS
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PipeStreamBuf::init_pipe
|
|
// Access: Private
|
|
// Description: Initializes whatever data structures store the child
|
|
// process information. This function is only called
|
|
// once at startup, by the constructor.
|
|
////////////////////////////////////////////////////////////////////
|
|
void PipeStreamBuf::
|
|
init_pipe() {
|
|
_pipe = NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PipeStreamBuf::is_open
|
|
// Access: Private
|
|
// Description: Returns true if the pipe has been opened, false
|
|
// otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool PipeStreamBuf::
|
|
is_open() const {
|
|
return _pipe != NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PipeStreamBuf::eof_pipe
|
|
// Access: Private
|
|
// Description: Returns true if there is an end-of-file condition on
|
|
// the input, or if the pipe was never opened.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool PipeStreamBuf::
|
|
eof_pipe() const {
|
|
return (_pipe == NULL) && feof(_pipe);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PipeStreamBuf::open_pipe
|
|
// Access: Private
|
|
// Description: Forks a child to run the indicated command, and
|
|
// according to the setting of _dir, binds either its
|
|
// input or output to this process for writing or
|
|
// reading.
|
|
//
|
|
// Returns true on success, false on failure.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool PipeStreamBuf::
|
|
open_pipe(const string &cmd) {
|
|
const char *typ = (_dir == Output)?"w":"r";
|
|
_pipe = popen(cmd.c_str(), typ);
|
|
return (_pipe != NULL);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PipeStreamBuf::close_pipe
|
|
// Access: Private
|
|
// Description: Closes the pipe opened previously.
|
|
////////////////////////////////////////////////////////////////////
|
|
void PipeStreamBuf::
|
|
close_pipe() {
|
|
if (_pipe != NULL) {
|
|
fclose(_pipe);
|
|
_pipe = NULL;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PipeStreamBuf::write_pipe
|
|
// Access: Private
|
|
// Description: Writes the indicated data out to the child process
|
|
// opened previously. Returns the number of bytes read.
|
|
////////////////////////////////////////////////////////////////////
|
|
size_t PipeStreamBuf::
|
|
write_pipe(const char *data, size_t len) {
|
|
size_t wrote_count = fwrite(data, 1, len, _pipe);
|
|
fflush(_pipe);
|
|
return wrote_count;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PipeStreamBuf::read_pipe
|
|
// Access: Private
|
|
// Description: Reads the indicated amount of data from the child
|
|
// process opened previously. Returns the number of
|
|
// bytes read.
|
|
////////////////////////////////////////////////////////////////////
|
|
size_t PipeStreamBuf::
|
|
read_pipe(char *data, size_t len) {
|
|
return fread(data, 1, len, _pipe);
|
|
}
|
|
|
|
#else // WIN_PIPE_CALLS
|
|
|
|
// The official Windows way of reading from a child process, without
|
|
// using a Unix-style convenience function like popen(), is similar in
|
|
// principle to the Unix pipe() method. We have to first redirect our
|
|
// own stdout to an anonymous pipe, then we spawn a child, who
|
|
// inherits this new stdout. Then we can restore our own stdout, and
|
|
// read from the other end of the pipe.
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PipeStreamBuf::init_pipe
|
|
// Access: Private
|
|
// Description: Initializes whatever data structures store the child
|
|
// process information. This function is only called
|
|
// once at startup, by the constructor.
|
|
////////////////////////////////////////////////////////////////////
|
|
void PipeStreamBuf::
|
|
init_pipe() {
|
|
_child_out = 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PipeStreamBuf::is_open
|
|
// Access: Private
|
|
// Description: Returns true if the pipe has been opened, false
|
|
// otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool PipeStreamBuf::
|
|
is_open() const {
|
|
return (_child_out != 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PipeStreamBuf::eof_pipe
|
|
// Access: Private
|
|
// Description: Returns true if there is an end-of-file condition on
|
|
// the input, or if the pipe was never opened.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool PipeStreamBuf::
|
|
eof_pipe() const {
|
|
return (_child_out == 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PipeStreamBuf::open_pipe
|
|
// Access: Private
|
|
// Description: Forks a child to run the indicated command, and
|
|
// according to the setting of _dir, binds either its
|
|
// input or output to this process for writing or
|
|
// reading.
|
|
//
|
|
// Returns true on success, false on failure.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool PipeStreamBuf::
|
|
open_pipe(const string &cmd) {
|
|
close_pipe();
|
|
|
|
// At the present, this only works for input pipes. We can add code
|
|
// to support output pipes later if anyone cares.
|
|
if (_dir == Output) {
|
|
return false;
|
|
}
|
|
|
|
// First, save our current stdout, so we can restore it after all of
|
|
// this nonsense.
|
|
HANDLE hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
// Now create a pipe to accept the child processes' output.
|
|
HANDLE hChildStdoutRd, hChildStdoutWr;
|
|
|
|
// Set the bInheritHandle flag so pipe handles are inherited.
|
|
SECURITY_ATTRIBUTES saAttr;
|
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
saAttr.bInheritHandle = TRUE;
|
|
saAttr.lpSecurityDescriptor = NULL;
|
|
if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
|
|
#ifndef NDEBUG
|
|
cerr << "Unable to create output pipe\n";
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
// Remap stdout to the "write" end of this pipe.
|
|
if (!SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) {
|
|
#ifndef NDEBUG
|
|
cerr << "Unable to redirect stdout\n";
|
|
#endif
|
|
CloseHandle(hChildStdoutRd);
|
|
CloseHandle(hChildStdoutWr);
|
|
return false;
|
|
}
|
|
|
|
// Create noninheritable read handle and close the inheritable read
|
|
// handle.
|
|
|
|
BOOL fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
|
|
GetCurrentProcess(), &_child_out,
|
|
0, FALSE, DUPLICATE_SAME_ACCESS);
|
|
|
|
if (!fSuccess) {
|
|
#ifndef NDEBUG
|
|
cerr << "DuplicateHandle failed\n";
|
|
#endif
|
|
CloseHandle(hChildStdoutRd);
|
|
CloseHandle(hChildStdoutWr);
|
|
return false;
|
|
}
|
|
CloseHandle(hChildStdoutRd);
|
|
|
|
// Now spawn the child process.
|
|
|
|
// Both WinExec() and CreateProcess() want a non-const char pointer.
|
|
// Maybe they change it, and maybe they don't. I'm not taking
|
|
// chances.
|
|
char *cmdline = new char[cmd.length() + 1];
|
|
strcpy(cmdline, cmd.c_str());
|
|
|
|
// We should be using CreateProcess() instead of WinExec(), but that
|
|
// seems to be likely to crash Win98. WinExec() seems better
|
|
// behaved, and it's all we need anyway.
|
|
if (!WinExec(cmdline, 0)) {
|
|
#ifndef NDEBUG
|
|
cerr << "Unable to spawn process.\n";
|
|
#endif
|
|
close_pipe();
|
|
// Don't return yet, since we still need to clean up.
|
|
}
|
|
|
|
delete[] cmdline;
|
|
|
|
// Now restore our own stdout, up here in the parent process.
|
|
if (!SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) {
|
|
#ifndef NDEBUG
|
|
cerr << "Unable to restore stdout\n";
|
|
#endif
|
|
}
|
|
|
|
// Close the write end of the pipe before reading from the
|
|
// read end of the pipe.
|
|
if (!CloseHandle(hChildStdoutWr)) {
|
|
#ifndef NDEBUG
|
|
cerr << "Unable to close write end of pipe\n";
|
|
#endif
|
|
}
|
|
|
|
return (_child_out != 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PipeStreamBuf::close_pipe
|
|
// Access: Private
|
|
// Description: Closes the pipe opened previously.
|
|
////////////////////////////////////////////////////////////////////
|
|
void PipeStreamBuf::
|
|
close_pipe() {
|
|
if (_child_out != 0) {
|
|
CloseHandle(_child_out);
|
|
_child_out = 0;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PipeStreamBuf::write_pipe
|
|
// Access: Private
|
|
// Description: Writes the indicated data out to the child process
|
|
// opened previously. Returns the number of bytes read.
|
|
////////////////////////////////////////////////////////////////////
|
|
size_t PipeStreamBuf::
|
|
write_pipe(const char *data, size_t len) {
|
|
return 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: PipeStreamBuf::read_pipe
|
|
// Access: Private
|
|
// Description: Reads the indicated amount of data from the child
|
|
// process opened previously. Returns the number of
|
|
// bytes read.
|
|
////////////////////////////////////////////////////////////////////
|
|
size_t PipeStreamBuf::
|
|
read_pipe(char *data, size_t len) {
|
|
if (_child_out == 0) {
|
|
return 0;
|
|
}
|
|
DWORD dwRead;
|
|
if (!ReadFile(_child_out, data, len, &dwRead, NULL)) {
|
|
close_pipe();
|
|
return 0;
|
|
}
|
|
|
|
return dwRead;
|
|
}
|
|
|
|
|
|
#endif // WIN_PIPE_CALLS
|