Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members  

pfstreamBuf.cxx

Go to the documentation of this file.
00001 // Filename: pfstreamBuf.cxx
00002 // Created by:  cary (12Dec00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "pfstreamBuf.h"
00020 #include <assert.h>
00021 
00022 PipeStreamBuf::PipeStreamBuf(PipeStreamBuf::Direction dir) : 
00023   _dir(dir)
00024 {
00025   init_pipe();
00026 
00027 #ifndef HAVE_IOSTREAM
00028   // These lines, which are essential on older implementations of the
00029   // iostream library, are not understood by more recent versions.
00030   allocate();
00031   assert((dir == Input) || (dir == Output));
00032   if (dir == Input) {
00033     setg(base(), ebuf(), ebuf());
00034   } else {
00035     setp(base(), ebuf());
00036   }
00037 #endif /* HAVE_IOSTREAM */
00038 }
00039 
00040 PipeStreamBuf::
00041 ~PipeStreamBuf(void) {
00042   if (is_open()) {
00043     sync();
00044     flush();
00045     close_pipe();
00046   }
00047 }
00048 
00049 void PipeStreamBuf::flush(void) {
00050   assert(is_open());
00051   if (_dir == Output) {
00052     write_chars("", 0, true);
00053   }
00054 }
00055 
00056 void PipeStreamBuf::command(const string cmd) {
00057   assert(!is_open());
00058   open_pipe(cmd);
00059 }
00060 
00061 int PipeStreamBuf::overflow(int c) {
00062   assert(is_open());
00063   assert(_dir == Output);
00064   streamsize n = pptr() - pbase();
00065   if (n != 0) {
00066     write_chars(pbase(), n, false);
00067     pbump(-n);  // reset pptr()
00068   }
00069   if (c != EOF) {
00070     // write one more character
00071     char ch = c;
00072     write_chars(&ch, 1, false);
00073   }
00074   return 0;
00075 }
00076 
00077 int PipeStreamBuf::sync(void) {
00078   assert(is_open());
00079   if (_dir == Output) {
00080     streamsize n = pptr() - pbase();
00081     write_chars(pbase(), n, false);
00082     pbump(-n);
00083   } else {
00084     streamsize n = egptr() - gptr();
00085     if (n != 0) {
00086       gbump(n);  // flush all our stored input away
00087 #ifndef NDEBUG
00088       cerr << "pfstream tossed out " << n << " bytes" << endl;
00089 #endif
00090     }
00091   }
00092   return 0;
00093 }
00094 
00095 int PipeStreamBuf::underflow(void) {
00096   assert(_dir == Input);
00097   if ((eback() == (char*)0L) || (gptr() == (char*)0L) ||
00098       (egptr() == (char*)0L)) {
00099     // must be new-style iostream library
00100     char* buf = new char[4096];
00101     char* ebuf = &(buf[4096]);
00102     setg(buf, ebuf, ebuf);
00103   }
00104   if (gptr() < egptr()) {
00105     char c = *(gptr());
00106     return c;
00107   }
00108   if (eof_pipe()) {
00109     return EOF;
00110   }
00111 #ifdef HAVE_IOSTREAM
00112   size_t len = 4096;
00113 #else /* HAVE_IOSTREAM */
00114   size_t len = ebuf() - base();
00115 #endif /* HAVE_IOSTREAM */
00116   char* buf = new char[len];
00117   size_t n = read_pipe(buf, len);
00118   int ret = buf[0];
00119   if (n == 0)
00120     ret = EOF;
00121   else {
00122 #ifdef HAVE_IOSTREAM
00123     memcpy(eback()+(len-n), buf, n);
00124 #else /* HAVE_IOSTREAM */
00125     memcpy(base()+(len-n), buf, n);
00126 #endif /* HAVE_IOSTREAM */
00127     gbump(-((int)n));
00128   }
00129   delete buf;
00130   return ret;
00131 }
00132 
00133 void PipeStreamBuf::write_chars(const char* start, int length, bool flush) {
00134   assert(_dir == Output);
00135   size_t orig = _line_buffer.length();
00136   string latest(start, length);
00137   string line;
00138 
00139   if (flush) {
00140     // if we're going to flush the stream now, we dump the whole thing
00141     // reguardless of whether we have reached end-of-line.
00142     line = _line_buffer + latest;
00143     _line_buffer = "";
00144   } else {
00145     // Otherwise, we check for the end-of-line character.
00146     _line_buffer += latest;
00147     size_t eol = _line_buffer.rfind('\n', orig);
00148     if (eol != string::npos) {
00149       line = _line_buffer.substr(0, eol+1);
00150       _line_buffer = _line_buffer.substr(eol+1);
00151     }
00152   }
00153   // now output 'line'
00154   size_t wrote = write_pipe(line.c_str(), line.length());
00155 #ifndef NDEBUG
00156   if (wrote != line.length())
00157     cerr << "wrote only " << wrote << " of " << line.length()
00158          << " bytes to pipe" << endl;
00159 #endif
00160 }
00161 
00162 #ifndef WIN_PIPE_CALLS
00163 
00164 ////////////////////////////////////////////////////////////////////
00165 //     Function: PipeStreamBuf::init_pipe
00166 //       Access: Private
00167 //  Description: Initializes whatever data structures store the child
00168 //               process information.  This function is only called
00169 //               once at startup, by the constructor.
00170 ////////////////////////////////////////////////////////////////////
00171 void PipeStreamBuf::
00172 init_pipe() {
00173   _pipe = NULL;
00174 }
00175 
00176 ////////////////////////////////////////////////////////////////////
00177 //     Function: PipeStreamBuf::is_open
00178 //       Access: Private
00179 //  Description: Returns true if the pipe has been opened, false
00180 //               otherwise.
00181 ////////////////////////////////////////////////////////////////////
00182 bool PipeStreamBuf::
00183 is_open() const {
00184   return _pipe != NULL;
00185 }
00186 
00187 ////////////////////////////////////////////////////////////////////
00188 //     Function: PipeStreamBuf::eof_pipe
00189 //       Access: Private
00190 //  Description: Returns true if there is an end-of-file condition on
00191 //               the input, or if the pipe was never opened.
00192 ////////////////////////////////////////////////////////////////////
00193 bool PipeStreamBuf::
00194 eof_pipe() const {
00195   return (_pipe == NULL) && feof(_pipe);
00196 }
00197 
00198 ////////////////////////////////////////////////////////////////////
00199 //     Function: PipeStreamBuf::open_pipe
00200 //       Access: Private
00201 //  Description: Forks a child to run the indicated command, and
00202 //               according to the setting of _dir, binds either its
00203 //               input or output to this process for writing or
00204 //               reading.
00205 //
00206 //               Returns true on success, false on failure.
00207 ////////////////////////////////////////////////////////////////////
00208 bool PipeStreamBuf::
00209 open_pipe(const string &cmd) {
00210   const char *typ = (_dir == Output)?"w":"r";
00211   _pipe = popen(cmd.c_str(), typ);
00212   return (_pipe != NULL);
00213 }
00214 
00215 ////////////////////////////////////////////////////////////////////
00216 //     Function: PipeStreamBuf::close_pipe
00217 //       Access: Private
00218 //  Description: Closes the pipe opened previously.
00219 ////////////////////////////////////////////////////////////////////
00220 void PipeStreamBuf::
00221 close_pipe() {
00222   if (_pipe != NULL) {
00223     fclose(_pipe);
00224     _pipe = NULL;
00225   }
00226 }
00227 
00228 ////////////////////////////////////////////////////////////////////
00229 //     Function: PipeStreamBuf::write_pipe
00230 //       Access: Private
00231 //  Description: Writes the indicated data out to the child process
00232 //               opened previously.  Returns the number of bytes read.
00233 ////////////////////////////////////////////////////////////////////
00234 size_t PipeStreamBuf::
00235 write_pipe(const char *data, size_t len) {
00236   size_t wrote_count = fwrite(data, 1, len, _pipe);
00237   fflush(_pipe);
00238   return wrote_count;
00239 }
00240 
00241 ////////////////////////////////////////////////////////////////////
00242 //     Function: PipeStreamBuf::read_pipe
00243 //       Access: Private
00244 //  Description: Reads the indicated amount of data from the child
00245 //               process opened previously.  Returns the number of
00246 //               bytes read.
00247 ////////////////////////////////////////////////////////////////////
00248 size_t PipeStreamBuf::
00249 read_pipe(char *data, size_t len) {
00250   return fread(data, 1, len, _pipe);
00251 }
00252 
00253 #else  // WIN_PIPE_CALLS
00254 
00255 // The official Windows way of reading from a child process, without
00256 // using a Unix-style convenience function like popen(), is similar in
00257 // principle to the Unix pipe() method.  We have to first redirect our
00258 // own stdout to an anonymous pipe, then we spawn a child, who
00259 // inherits this new stdout.  Then we can restore our own stdout, and
00260 // read from the other end of the pipe.
00261 
00262 ////////////////////////////////////////////////////////////////////
00263 //     Function: PipeStreamBuf::init_pipe
00264 //       Access: Private
00265 //  Description: Initializes whatever data structures store the child
00266 //               process information.  This function is only called
00267 //               once at startup, by the constructor.
00268 ////////////////////////////////////////////////////////////////////
00269 void PipeStreamBuf::
00270 init_pipe() {
00271   _child_out = 0;
00272 }
00273 
00274 ////////////////////////////////////////////////////////////////////
00275 //     Function: PipeStreamBuf::is_open
00276 //       Access: Private
00277 //  Description: Returns true if the pipe has been opened, false
00278 //               otherwise.
00279 ////////////////////////////////////////////////////////////////////
00280 bool PipeStreamBuf::
00281 is_open() const {
00282   return (_child_out != 0);
00283 }
00284 
00285 ////////////////////////////////////////////////////////////////////
00286 //     Function: PipeStreamBuf::eof_pipe
00287 //       Access: Private
00288 //  Description: Returns true if there is an end-of-file condition on
00289 //               the input, or if the pipe was never opened.
00290 ////////////////////////////////////////////////////////////////////
00291 bool PipeStreamBuf::
00292 eof_pipe() const {
00293   return (_child_out == 0);
00294 }
00295 
00296 ////////////////////////////////////////////////////////////////////
00297 //     Function: PipeStreamBuf::open_pipe
00298 //       Access: Private
00299 //  Description: Forks a child to run the indicated command, and
00300 //               according to the setting of _dir, binds either its
00301 //               input or output to this process for writing or
00302 //               reading.
00303 //
00304 //               Returns true on success, false on failure.
00305 ////////////////////////////////////////////////////////////////////
00306 bool PipeStreamBuf::
00307 open_pipe(const string &cmd) {
00308   close_pipe();
00309 
00310   // At the present, this only works for input pipes.  We can add code
00311   // to support output pipes later if anyone cares.
00312   if (_dir == Output) {
00313     return false;
00314   }
00315 
00316   // First, save our current stdout, so we can restore it after all of
00317   // this nonsense.
00318   HANDLE hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE); 
00319  
00320   // Now create a pipe to accept the child processes' output.
00321   HANDLE hChildStdoutRd, hChildStdoutWr;
00322   
00323   // Set the bInheritHandle flag so pipe handles are inherited. 
00324   SECURITY_ATTRIBUTES saAttr; 
00325   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
00326   saAttr.bInheritHandle = TRUE; 
00327   saAttr.lpSecurityDescriptor = NULL; 
00328   if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
00329 #ifndef NDEBUG
00330     cerr << "Unable to create output pipe\n";
00331 #endif
00332     return false;
00333   }
00334  
00335   // Remap stdout to the "write" end of this pipe.
00336   if (!SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) {
00337 #ifndef NDEBUG
00338     cerr << "Unable to redirect stdout\n";
00339 #endif
00340     CloseHandle(hChildStdoutRd);
00341     CloseHandle(hChildStdoutWr);
00342     return false;
00343   }
00344  
00345   // Create noninheritable read handle and close the inheritable read 
00346   // handle. 
00347 
00348   BOOL fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
00349                                   GetCurrentProcess(), &_child_out,
00350                                   0, FALSE, DUPLICATE_SAME_ACCESS);
00351   
00352   if (!fSuccess) {
00353 #ifndef NDEBUG
00354     cerr << "DuplicateHandle failed\n";
00355 #endif
00356     CloseHandle(hChildStdoutRd);
00357     CloseHandle(hChildStdoutWr);
00358     return false;
00359   }
00360   CloseHandle(hChildStdoutRd);
00361  
00362   // Now spawn the child process.
00363   
00364   // Both WinExec() and CreateProcess() want a non-const char pointer.
00365   // Maybe they change it, and maybe they don't.  I'm not taking
00366   // chances.
00367   char *cmdline = new char[cmd.length() + 1];
00368   strcpy(cmdline, cmd.c_str());
00369 
00370   // We should be using CreateProcess() instead of WinExec(), but that
00371   // seems to be likely to crash Win98.  WinExec() seems better
00372   // behaved, and it's all we need anyway.
00373   if (!WinExec(cmdline, 0)) {
00374 #ifndef NDEBUG
00375     cerr << "Unable to spawn process.\n";
00376 #endif
00377     close_pipe();
00378     // Don't return yet, since we still need to clean up.
00379   }
00380 
00381   delete[] cmdline;
00382  
00383   // Now restore our own stdout, up here in the parent process.
00384   if (!SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) {
00385 #ifndef NDEBUG
00386     cerr << "Unable to restore stdout\n";
00387 #endif
00388   }
00389 
00390   // Close the write end of the pipe before reading from the 
00391   // read end of the pipe. 
00392   if (!CloseHandle(hChildStdoutWr)) {
00393 #ifndef NDEBUG
00394     cerr << "Unable to close write end of pipe\n";
00395 #endif
00396   }
00397 
00398   return (_child_out != 0);
00399 }
00400 
00401 ////////////////////////////////////////////////////////////////////
00402 //     Function: PipeStreamBuf::close_pipe
00403 //       Access: Private
00404 //  Description: Closes the pipe opened previously.
00405 ////////////////////////////////////////////////////////////////////
00406 void PipeStreamBuf::
00407 close_pipe() {
00408   if (_child_out != 0) {
00409     CloseHandle(_child_out);
00410     _child_out = 0;
00411   }
00412 }
00413 
00414 ////////////////////////////////////////////////////////////////////
00415 //     Function: PipeStreamBuf::write_pipe
00416 //       Access: Private
00417 //  Description: Writes the indicated data out to the child process
00418 //               opened previously.  Returns the number of bytes read.
00419 ////////////////////////////////////////////////////////////////////
00420 size_t PipeStreamBuf::
00421 write_pipe(const char *data, size_t len) {
00422   return 0;
00423 }
00424 
00425 ////////////////////////////////////////////////////////////////////
00426 //     Function: PipeStreamBuf::read_pipe
00427 //       Access: Private
00428 //  Description: Reads the indicated amount of data from the child
00429 //               process opened previously.  Returns the number of
00430 //               bytes read.
00431 ////////////////////////////////////////////////////////////////////
00432 size_t PipeStreamBuf::
00433 read_pipe(char *data, size_t len) {
00434   if (_child_out == 0) {
00435     return 0;
00436   }
00437   DWORD dwRead; 
00438   if (!ReadFile(_child_out, data, len, &dwRead, NULL)) {
00439     close_pipe();
00440     return 0;
00441   }
00442 
00443   return dwRead;
00444 }
00445 
00446 
00447 #endif  // WIN_PIPE_CALLS

Generated on Fri Apr 18 01:33:58 2003 for DTool by doxygen1.3