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