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