Panda3D
|
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