Panda3D
pfstreamBuf.cxx
1 // Filename: pfstreamBuf.cxx
2 // Created by: cary (12Dec00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pfstreamBuf.h"
16 #include <assert.h>
17 
18 PipeStreamBuf::PipeStreamBuf(PipeStreamBuf::Direction dir) :
19  _dir(dir)
20 {
21  init_pipe();
22 
23 #ifndef PHAVE_IOSTREAM
24  // These lines, which are essential on older implementations of the
25  // iostream library, are not understood by more recent versions.
26  allocate();
27  assert((dir == Input) || (dir == Output));
28  if (dir == Input) {
29  setg(base(), ebuf(), ebuf());
30  } else {
31  setp(base(), ebuf());
32  }
33 #endif /* PHAVE_IOSTREAM */
34 }
35 
36 PipeStreamBuf::
37 ~PipeStreamBuf(void) {
38  if (is_open()) {
39  sync();
40  flush();
41  close_pipe();
42  }
43 }
44 
45 void PipeStreamBuf::flush(void) {
46  assert(is_open());
47  if (_dir == Output) {
48  write_chars("", 0, true);
49  }
50 }
51 
52 void PipeStreamBuf::command(const string cmd) {
53  assert(!is_open());
54  open_pipe(cmd);
55 }
56 
57 int PipeStreamBuf::overflow(int c) {
58  assert(is_open());
59  assert(_dir == Output);
60  streamsize n = pptr() - pbase();
61  if (n != 0) {
62  write_chars(pbase(), n, false);
63  pbump(-n); // reset pptr()
64  }
65  if (c != EOF) {
66  // write one more character
67  char ch = c;
68  write_chars(&ch, 1, false);
69  }
70  return 0;
71 }
72 
73 int PipeStreamBuf::sync(void) {
74  assert(is_open());
75  if (_dir == Output) {
76  streamsize n = pptr() - pbase();
77  write_chars(pbase(), n, false);
78  pbump(-n);
79  } else {
80  streamsize n = egptr() - gptr();
81  if (n != 0) {
82  gbump(n); // flush all our stored input away
83 #ifndef NDEBUG
84  cerr << "pfstream tossed out " << n << " bytes" << endl;
85 #endif
86  }
87  }
88  return 0;
89 }
90 
91 int PipeStreamBuf::underflow(void) {
92  assert(_dir == Input);
93  if ((eback() == (char*)0L) || (gptr() == (char*)0L) ||
94  (egptr() == (char*)0L)) {
95  // must be new-style iostream library
96  char* buf = new char[4096];
97  char* ebuf = &(buf[4096]);
98  setg(buf, ebuf, ebuf);
99  }
100  if (gptr() < egptr()) {
101  char c = *(gptr());
102  return c;
103  }
104  if (eof_pipe()) {
105  return EOF;
106  }
107 #ifdef PHAVE_IOSTREAM
108  size_t len = 4096;
109 #else /* PHAVE_IOSTREAM */
110  size_t len = ebuf() - base();
111 #endif /* PHAVE_IOSTREAM */
112  char* buf = new char[len];
113  size_t n = read_pipe(buf, len);
114  int ret = buf[0];
115  if (n == 0)
116  ret = EOF;
117  else {
118 #ifdef PHAVE_IOSTREAM
119  memcpy(eback()+(len-n), buf, n);
120 #else /* PHAVE_IOSTREAM */
121  memcpy(base()+(len-n), buf, n);
122 #endif /* PHAVE_IOSTREAM */
123  gbump(-((int)n));
124  }
125  delete[] buf;
126  return ret;
127 }
128 
129 void PipeStreamBuf::write_chars(const char* start, int length, bool flush) {
130  assert(_dir == Output);
131  size_t orig = _line_buffer.length();
132  string latest(start, length);
133  string line;
134 
135  if (flush) {
136  // if we're going to flush the stream now, we dump the whole thing
137  // reguardless of whether we have reached end-of-line.
138  line = _line_buffer + latest;
139  _line_buffer = "";
140  } else {
141  // Otherwise, we check for the end-of-line character.
142  _line_buffer += latest;
143  size_t eol = _line_buffer.rfind('\n', orig);
144  if (eol != string::npos) {
145  line = _line_buffer.substr(0, eol+1);
146  _line_buffer = _line_buffer.substr(eol+1);
147  }
148  }
149  // now output 'line'
150  size_t wrote = write_pipe(line.c_str(), line.length());
151 #ifndef NDEBUG
152  if (wrote != line.length())
153  cerr << "wrote only " << wrote << " of " << line.length()
154  << " bytes to pipe" << endl;
155 #endif
156 }
157 
158 #ifndef WIN_PIPE_CALLS
159 
160 ////////////////////////////////////////////////////////////////////
161 // Function: PipeStreamBuf::init_pipe
162 // Access: Private
163 // Description: Initializes whatever data structures store the child
164 // process information. This function is only called
165 // once at startup, by the constructor.
166 ////////////////////////////////////////////////////////////////////
167 void PipeStreamBuf::
168 init_pipe() {
169  _pipe = NULL;
170 }
171 
172 ////////////////////////////////////////////////////////////////////
173 // Function: PipeStreamBuf::is_open
174 // Access: Private
175 // Description: Returns true if the pipe has been opened, false
176 // otherwise.
177 ////////////////////////////////////////////////////////////////////
178 bool PipeStreamBuf::
179 is_open() const {
180  return _pipe != NULL;
181 }
182 
183 ////////////////////////////////////////////////////////////////////
184 // Function: PipeStreamBuf::eof_pipe
185 // Access: Private
186 // Description: Returns true if there is an end-of-file condition on
187 // the input, or if the pipe was never opened.
188 ////////////////////////////////////////////////////////////////////
189 bool PipeStreamBuf::
190 eof_pipe() const {
191  return (_pipe == NULL) && feof(_pipe);
192 }
193 
194 ////////////////////////////////////////////////////////////////////
195 // Function: PipeStreamBuf::open_pipe
196 // Access: Private
197 // Description: Forks a child to run the indicated command, and
198 // according to the setting of _dir, binds either its
199 // input or output to this process for writing or
200 // reading.
201 //
202 // Returns true on success, false on failure.
203 ////////////////////////////////////////////////////////////////////
204 bool PipeStreamBuf::
205 open_pipe(const string &cmd) {
206  const char *typ = (_dir == Output)?"w":"r";
207  _pipe = popen(cmd.c_str(), typ);
208  return (_pipe != NULL);
209 }
210 
211 ////////////////////////////////////////////////////////////////////
212 // Function: PipeStreamBuf::close_pipe
213 // Access: Private
214 // Description: Closes the pipe opened previously.
215 ////////////////////////////////////////////////////////////////////
216 void PipeStreamBuf::
217 close_pipe() {
218  if (_pipe != NULL) {
219  fclose(_pipe);
220  _pipe = NULL;
221  }
222 }
223 
224 ////////////////////////////////////////////////////////////////////
225 // Function: PipeStreamBuf::write_pipe
226 // Access: Private
227 // Description: Writes the indicated data out to the child process
228 // opened previously. Returns the number of bytes read.
229 ////////////////////////////////////////////////////////////////////
230 size_t PipeStreamBuf::
231 write_pipe(const char *data, size_t len) {
232  size_t wrote_count = fwrite(data, 1, len, _pipe);
233  fflush(_pipe);
234  return wrote_count;
235 }
236 
237 ////////////////////////////////////////////////////////////////////
238 // Function: PipeStreamBuf::read_pipe
239 // Access: Private
240 // Description: Reads the indicated amount of data from the child
241 // process opened previously. Returns the number of
242 // bytes read.
243 ////////////////////////////////////////////////////////////////////
244 size_t PipeStreamBuf::
245 read_pipe(char *data, size_t len) {
246  return fread(data, 1, len, _pipe);
247 }
248 
249 #else // WIN_PIPE_CALLS
250 
251 // The official Windows way of reading from a child process, without
252 // using a Unix-style convenience function like popen(), is similar in
253 // principle to the Unix pipe() method. We have to first redirect our
254 // own stdout to an anonymous pipe, then we spawn a child, who
255 // inherits this new stdout. Then we can restore our own stdout, and
256 // read from the other end of the pipe.
257 
258 ////////////////////////////////////////////////////////////////////
259 // Function: PipeStreamBuf::init_pipe
260 // Access: Private
261 // Description: Initializes whatever data structures store the child
262 // process information. This function is only called
263 // once at startup, by the constructor.
264 ////////////////////////////////////////////////////////////////////
265 void PipeStreamBuf::
266 init_pipe() {
267  _child_out = 0;
268 }
269 
270 ////////////////////////////////////////////////////////////////////
271 // Function: PipeStreamBuf::is_open
272 // Access: Private
273 // Description: Returns true if the pipe has been opened, false
274 // otherwise.
275 ////////////////////////////////////////////////////////////////////
276 bool PipeStreamBuf::
277 is_open() const {
278  return (_child_out != 0);
279 }
280 
281 ////////////////////////////////////////////////////////////////////
282 // Function: PipeStreamBuf::eof_pipe
283 // Access: Private
284 // Description: Returns true if there is an end-of-file condition on
285 // the input, or if the pipe was never opened.
286 ////////////////////////////////////////////////////////////////////
287 bool PipeStreamBuf::
288 eof_pipe() const {
289  return (_child_out == 0);
290 }
291 
292 ////////////////////////////////////////////////////////////////////
293 // Function: PipeStreamBuf::open_pipe
294 // Access: Private
295 // Description: Forks a child to run the indicated command, and
296 // according to the setting of _dir, binds either its
297 // input or output to this process for writing or
298 // reading.
299 //
300 // Returns true on success, false on failure.
301 ////////////////////////////////////////////////////////////////////
302 bool PipeStreamBuf::
303 open_pipe(const string &cmd) {
304  close_pipe();
305 
306  // At the present, this only works for input pipes. We can add code
307  // to support output pipes later if anyone cares.
308  if (_dir == Output) {
309  return false;
310  }
311 
312  // First, save our current stdout, so we can restore it after all of
313  // this nonsense.
314  HANDLE hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
315 
316  // Now create a pipe to accept the child processes' output.
317  HANDLE hChildStdoutRd, hChildStdoutWr;
318 
319  // Set the bInheritHandle flag so pipe handles are inherited.
320  SECURITY_ATTRIBUTES saAttr;
321  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
322  saAttr.bInheritHandle = TRUE;
323  saAttr.lpSecurityDescriptor = NULL;
324  if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
325 #ifndef NDEBUG
326  cerr << "Unable to create output pipe\n";
327 #endif
328  return false;
329  }
330 
331  // Remap stdout to the "write" end of this pipe.
332  if (!SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr)) {
333 #ifndef NDEBUG
334  cerr << "Unable to redirect stdout\n";
335 #endif
336  CloseHandle(hChildStdoutRd);
337  CloseHandle(hChildStdoutWr);
338  return false;
339  }
340 
341  // Create noninheritable read handle and close the inheritable read
342  // handle.
343 
344  BOOL fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
345  GetCurrentProcess(), &_child_out,
346  0, FALSE, DUPLICATE_SAME_ACCESS);
347 
348  if (!fSuccess) {
349 #ifndef NDEBUG
350  cerr << "DuplicateHandle failed\n";
351 #endif
352  CloseHandle(hChildStdoutRd);
353  CloseHandle(hChildStdoutWr);
354  return false;
355  }
356  CloseHandle(hChildStdoutRd);
357 
358  // Now spawn the child process.
359 
360  // Both WinExec() and CreateProcess() want a non-const char pointer.
361  // Maybe they change it, and maybe they don't. I'm not taking
362  // chances.
363  char *cmdline = new char[cmd.length() + 1];
364  strcpy(cmdline, cmd.c_str());
365 
366  // We should be using CreateProcess() instead of WinExec(), but that
367  // seems to be likely to crash Win98. WinExec() seems better
368  // behaved, and it's all we need anyway.
369  if (!WinExec(cmdline, 0)) {
370 #ifndef NDEBUG
371  cerr << "Unable to spawn process.\n";
372 #endif
373  close_pipe();
374  // Don't return yet, since we still need to clean up.
375  }
376 
377  delete[] cmdline;
378 
379  // Now restore our own stdout, up here in the parent process.
380  if (!SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout)) {
381 #ifndef NDEBUG
382  cerr << "Unable to restore stdout\n";
383 #endif
384  }
385 
386  // Close the write end of the pipe before reading from the
387  // read end of the pipe.
388  if (!CloseHandle(hChildStdoutWr)) {
389 #ifndef NDEBUG
390  cerr << "Unable to close write end of pipe\n";
391 #endif
392  }
393 
394  return (_child_out != 0);
395 }
396 
397 ////////////////////////////////////////////////////////////////////
398 // Function: PipeStreamBuf::close_pipe
399 // Access: Private
400 // Description: Closes the pipe opened previously.
401 ////////////////////////////////////////////////////////////////////
402 void PipeStreamBuf::
403 close_pipe() {
404  if (_child_out != 0) {
405  CloseHandle(_child_out);
406  _child_out = 0;
407  }
408 }
409 
410 ////////////////////////////////////////////////////////////////////
411 // Function: PipeStreamBuf::write_pipe
412 // Access: Private
413 // Description: Writes the indicated data out to the child process
414 // opened previously. Returns the number of bytes read.
415 ////////////////////////////////////////////////////////////////////
416 size_t PipeStreamBuf::
417 write_pipe(const char *data, size_t len) {
418  return 0;
419 }
420 
421 ////////////////////////////////////////////////////////////////////
422 // Function: PipeStreamBuf::read_pipe
423 // Access: Private
424 // Description: Reads the indicated amount of data from the child
425 // process opened previously. Returns the number of
426 // bytes read.
427 ////////////////////////////////////////////////////////////////////
428 size_t PipeStreamBuf::
429 read_pipe(char *data, size_t len) {
430  if (_child_out == 0) {
431  return 0;
432  }
433  DWORD dwRead;
434  if (!ReadFile(_child_out, data, len, &dwRead, NULL)) {
435  close_pipe();
436  return 0;
437  }
438 
439  return dwRead;
440 }
441 
442 
443 #endif // WIN_PIPE_CALLS