Panda3D
multiplexStreamBuf.cxx
1 // Filename: multiplexStreamBuf.cxx
2 // Created by: drose (27Nov00)
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 "multiplexStreamBuf.h"
16 
17 #if defined(WIN32_VC) || defined(WIN64_VC)
18 #define WINDOWS_LEAN_AND_MEAN
19 #include <windows.h>
20 #undef WINDOWS_LEAN_AND_MEAN
21 #endif
22 
23 // We use real assert() instead of nassert(), because we're likely
24 // to be invoked directly by pnotify.here, and we don't want to
25 // risk infinite recursion.
26 #include <assert.h>
27 
28 #ifndef HAVE_STREAMSIZE
29 // Some compilers--notably SGI--don't define this for us.
30 typedef int streamsize;
31 #endif
32 
33 ////////////////////////////////////////////////////////////////////
34 // Function: MultiplexStreamBuf::Output::close
35 // Access: Public
36 // Description: Closes or deletes the relevant pointers, if _owns_obj
37 // is true.
38 ////////////////////////////////////////////////////////////////////
39 void MultiplexStreamBuf::Output::
40 close() {
41  if (_owns_obj) {
42  switch (_output_type) {
43  case OT_ostream:
44  assert(_out != (ostream *)NULL);
45  delete _out;
46  break;
47 
48  case OT_stdio:
49  assert(_fout != (FILE *)NULL);
50  fclose(_fout);
51  break;
52 
53  default:
54  break;
55  }
56  }
57 }
58 
59 ////////////////////////////////////////////////////////////////////
60 // Function: MultiplexStreamBuf::Output::write_string
61 // Access: Public
62 // Description: Dumps the indicated string to the appropriate place.
63 ////////////////////////////////////////////////////////////////////
64 void MultiplexStreamBuf::Output::
65 write_string(const string &str) {
66  switch (_output_type) {
67  case OT_ostream:
68  assert(_out != (ostream *)NULL);
69  _out->write(str.data(), str.length());
70  _out->flush();
71  break;
72 
73  case OT_stdio:
74  assert(_fout != (FILE *)NULL);
75  fwrite(str.data(), str.length(), 1, _fout);
76  fflush(_fout);
77  break;
78 
79  case OT_system_debug:
80 #if defined(WIN32_VC) || defined(WIN64_VC)
81  OutputDebugString(str.c_str());
82 #endif
83  break;
84  }
85 }
86 
87 ////////////////////////////////////////////////////////////////////
88 // Function: MultiplexStreamBuf::Constructor
89 // Access: Public
90 // Description:
91 ////////////////////////////////////////////////////////////////////
92 MultiplexStreamBuf::
93 MultiplexStreamBuf() {
94 #ifndef PHAVE_IOSTREAM
95  // Older iostream implementations required this.
96  allocate();
97  setp(base(), ebuf());
98 #endif
99 }
100 
101 ////////////////////////////////////////////////////////////////////
102 // Function: MultiplexStreamBuf::Destructor
103 // Access: Public, Virtual
104 // Description:
105 ////////////////////////////////////////////////////////////////////
106 MultiplexStreamBuf::
107 ~MultiplexStreamBuf() {
108  sync();
109 
110  // Make sure all of our owned pointers are freed.
111  Outputs::iterator oi;
112  for (oi = _outputs.begin(); oi != _outputs.end(); ++oi) {
113  Output &out = (*oi);
114  out.close();
115  }
116 }
117 
118 ////////////////////////////////////////////////////////////////////
119 // Function: MultiplexStreamBuf::add_output
120 // Access: Public
121 // Description: Adds the indicated output destinition to the set of
122 // things that will be written to when characters are
123 // output to the MultiplexStream.
124 ////////////////////////////////////////////////////////////////////
126 add_output(MultiplexStreamBuf::BufferType buffer_type,
127  MultiplexStreamBuf::OutputType output_type,
128  ostream *out, FILE *fout, bool owns_obj) {
129 #ifdef OLD_HAVE_IPC
130  // Ensure that we have the mutex while we fiddle with the list of
131  // outputs.
132  mutex_lock m(_lock);
133 #endif
134 
135  Output o;
136  o._buffer_type = buffer_type;
137  o._output_type = output_type;
138  o._out = out;
139  o._fout = fout;
140  o._owns_obj = owns_obj;
141  _outputs.push_back(o);
142 }
143 
144 
145 ////////////////////////////////////////////////////////////////////
146 // Function: MultiplexStreamBuf::flush
147 // Access: Public
148 // Description: Forces out all output that hasn't yet been written.
149 ////////////////////////////////////////////////////////////////////
151 flush() {
152 #ifdef OLD_HAVE_IPC
153  mutex_lock m(_lock);
154 #endif
155 
156  write_chars("", 0, true);
157 }
158 
159 ////////////////////////////////////////////////////////////////////
160 // Function: MultiplexStreamBuf::overflow
161 // Access: Public, Virtual
162 // Description: Called by the system ostream implementation when its
163 // internal buffer is filled, plus one character.
164 ////////////////////////////////////////////////////////////////////
165 int MultiplexStreamBuf::
166 overflow(int ch) {
167 #ifdef OLD_HAVE_IPC
168  mutex_lock m(_lock);
169 #endif
170 
171  streamsize n = pptr() - pbase();
172 
173  if (n != 0) {
174  write_chars(pbase(), n, false);
175  pbump(-n); // Reset pptr().
176  }
177 
178  if (ch != EOF) {
179  // Write one more character.
180  char c = ch;
181  write_chars(&c, 1, false);
182  }
183 
184  return 0;
185 }
186 
187 ////////////////////////////////////////////////////////////////////
188 // Function: MultiplexStreamBuf::sync
189 // Access: Public, Virtual
190 // Description: Called by the system ostream implementation when the
191 // buffer should be flushed to output (for instance, on
192 // destruction).
193 ////////////////////////////////////////////////////////////////////
194 int MultiplexStreamBuf::
195 sync() {
196 #ifdef OLD_HAVE_IPC
197  mutex_lock m(_lock);
198 #endif
199 
200  streamsize n = pptr() - pbase();
201 
202  // We pass in false for the flush value, even though our
203  // transmitting ostream said to sync. This allows us to get better
204  // line buffering, since our transmitting ostream is often set
205  // unitbuf, and might call sync multiple times in one line. We
206  // still have an explicit flush() call to force the issue.
207  write_chars(pbase(), n, false);
208  pbump(-n);
209 
210  return 0; // Return 0 for success, EOF to indicate write full.
211 }
212 
213 ////////////////////////////////////////////////////////////////////
214 // Function: MultiplexStreamBuf::write_chars
215 // Access: Private
216 // Description: An internal function called by sync() and overflow()
217 // to store one or more characters written to the stream
218 // into the memory buffer.
219 //
220 // It is assumed that there is only one thread at a time
221 // running this code; it is the responsibility of the
222 // caller to grab the _lock mutex before calling this.
223 ////////////////////////////////////////////////////////////////////
224 void MultiplexStreamBuf::
225 write_chars(const char *start, int length, bool flush) {
226  size_t orig = _line_buffer.length();
227  string latest;
228  if (length != 0) {
229  latest = string(start, length);
230  }
231  string line;
232 
233  if (flush) {
234  // If we're to flush the stream now, we dump the whole thing
235  // regardless of whether we have reached end-of-line.
236  line = _line_buffer + latest;
237  _line_buffer = "";
238 
239  } else {
240  // Otherwise, we check for the end-of-line character, for our
241  // ostreams that only want a complete line at a time.
242  _line_buffer += latest;
243  size_t eol = _line_buffer.rfind('\n', orig);
244  if (eol != string::npos) {
245  line = _line_buffer.substr(0, eol + 1);
246  _line_buffer = _line_buffer.substr(eol + 1);
247  }
248  }
249 
250  Outputs::iterator oi;
251  for (oi = _outputs.begin(); oi != _outputs.end(); ++oi) {
252  Output &out = (*oi);
253  switch (out._buffer_type) {
254  case BT_none:
255  // No buffering: send all new characters directly to the ostream.
256  if (!latest.empty()) {
257  out.write_string(latest);
258  }
259  break;
260 
261  case BT_line:
262  // Line buffering: send only when a complete line has been
263  // received.
264  if (!line.empty()) {
265  out.write_string(line);
266  }
267  break;
268  }
269  }
270 
271 }
void flush()
Forces out all output that hasn&#39;t yet been written.
void add_output(BufferType buffer_type, OutputType output_type, ostream *out=(ostream *) NULL, FILE *fout=(FILE *) NULL, bool owns_obj=false)
Adds the indicated output destinition to the set of things that will be written to when characters ar...