00001 // Filename: multiplexStreamBuf.cxx 00002 // Created by: drose (27Nov00) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved 00008 // 00009 // All use of this software is subject to the terms of the Panda 3d 00010 // Software license. You should have received a copy of this license 00011 // along with this source code; you will also find a current copy of 00012 // the license at http://www.panda3d.org/license.txt . 00013 // 00014 // To contact the maintainers of this program write to 00015 // panda3d@yahoogroups.com . 00016 // 00017 //////////////////////////////////////////////////////////////////// 00018 00019 #include "multiplexStreamBuf.h" 00020 00021 #if defined(WIN32_VC) 00022 #define WINDOWS_LEAN_AND_MEAN 00023 #include <windows.h> 00024 #undef WINDOWS_LEAN_AND_MEAN 00025 #endif 00026 00027 // We use real assert() instead of nassert(), because we're likely 00028 // to be invoked directly by notify here, and we don't want to 00029 // risk infinite recursion. 00030 #include <assert.h> 00031 00032 #ifndef HAVE_STREAMSIZE 00033 // Some compilers--notably SGI--don't define this for us. 00034 typedef int streamsize; 00035 #endif 00036 00037 //////////////////////////////////////////////////////////////////// 00038 // Function: MultiplexStreamBuf::Output::close 00039 // Access: Public 00040 // Description: Closes or deletes the relevant pointers, if _owns_obj 00041 // is true. 00042 //////////////////////////////////////////////////////////////////// 00043 void MultiplexStreamBuf::Output:: 00044 close() { 00045 if (_owns_obj) { 00046 switch (_output_type) { 00047 case OT_ostream: 00048 assert(_out != (ostream *)NULL); 00049 delete _out; 00050 break; 00051 00052 case OT_stdio: 00053 assert(_fout != (FILE *)NULL); 00054 fclose(_fout); 00055 break; 00056 00057 default: 00058 break; 00059 } 00060 } 00061 } 00062 00063 //////////////////////////////////////////////////////////////////// 00064 // Function: MultiplexStreamBuf::Output::write_string 00065 // Access: Public 00066 // Description: Dumps the indicated string to the appropriate place. 00067 //////////////////////////////////////////////////////////////////// 00068 void MultiplexStreamBuf::Output:: 00069 write_string(const string &str) { 00070 switch (_output_type) { 00071 case OT_ostream: 00072 assert(_out != (ostream *)NULL); 00073 _out->write(str.data(), str.length()); 00074 _out->flush(); 00075 break; 00076 00077 case OT_stdio: 00078 assert(_fout != (FILE *)NULL); 00079 fwrite(str.data(), str.length(), 1, _fout); 00080 fflush(_fout); 00081 break; 00082 00083 case OT_system_debug: 00084 #ifdef WIN32_VC 00085 OutputDebugString(str.c_str()); 00086 #endif 00087 break; 00088 } 00089 } 00090 00091 //////////////////////////////////////////////////////////////////// 00092 // Function: MultiplexStreamBuf::Constructor 00093 // Access: Public 00094 // Description: 00095 //////////////////////////////////////////////////////////////////// 00096 MultiplexStreamBuf:: 00097 MultiplexStreamBuf() { 00098 #ifndef HAVE_IOSTREAM 00099 // Older iostream implementations required this. 00100 allocate(); 00101 setp(base(), ebuf()); 00102 #endif 00103 } 00104 00105 //////////////////////////////////////////////////////////////////// 00106 // Function: MultiplexStreamBuf::Destructor 00107 // Access: Public, Virtual 00108 // Description: 00109 //////////////////////////////////////////////////////////////////// 00110 MultiplexStreamBuf:: 00111 ~MultiplexStreamBuf() { 00112 sync(); 00113 00114 // Make sure all of our owned pointers are freed. 00115 Outputs::iterator oi; 00116 for (oi = _outputs.begin(); oi != _outputs.end(); ++oi) { 00117 Output &out = (*oi); 00118 out.close(); 00119 } 00120 } 00121 00122 //////////////////////////////////////////////////////////////////// 00123 // Function: MultiplexStreamBuf::add_output 00124 // Access: Public 00125 // Description: Adds the indicated output destinition to the set of 00126 // things that will be written to when characters are 00127 // output to the MultiplexStream. 00128 //////////////////////////////////////////////////////////////////// 00129 void MultiplexStreamBuf:: 00130 add_output(MultiplexStreamBuf::BufferType buffer_type, 00131 MultiplexStreamBuf::OutputType output_type, 00132 ostream *out, FILE *fout, bool owns_obj) { 00133 #ifdef OLD_HAVE_IPC 00134 // Ensure that we have the mutex while we fiddle with the list of 00135 // outputs. 00136 mutex_lock m(_lock); 00137 #endif 00138 00139 Output o; 00140 o._buffer_type = buffer_type; 00141 o._output_type = output_type; 00142 o._out = out; 00143 o._fout = fout; 00144 o._owns_obj = owns_obj; 00145 _outputs.push_back(o); 00146 } 00147 00148 00149 //////////////////////////////////////////////////////////////////// 00150 // Function: MultiplexStreamBuf::flush 00151 // Access: Public 00152 // Description: Forces out all output that hasn't yet been written. 00153 //////////////////////////////////////////////////////////////////// 00154 void MultiplexStreamBuf:: 00155 flush() { 00156 #ifdef OLD_HAVE_IPC 00157 mutex_lock m(_lock); 00158 #endif 00159 00160 write_chars("", 0, true); 00161 } 00162 00163 //////////////////////////////////////////////////////////////////// 00164 // Function: MultiplexStreamBuf::overflow 00165 // Access: Public, Virtual 00166 // Description: Called by the system ostream implementation when its 00167 // internal buffer is filled, plus one character. 00168 //////////////////////////////////////////////////////////////////// 00169 int MultiplexStreamBuf:: 00170 overflow(int ch) { 00171 #ifdef OLD_HAVE_IPC 00172 mutex_lock m(_lock); 00173 #endif 00174 00175 streamsize n = pptr() - pbase(); 00176 00177 if (n != 0) { 00178 write_chars(pbase(), n, false); 00179 pbump(-n); // Reset pptr(). 00180 } 00181 00182 if (ch != EOF) { 00183 // Write one more character. 00184 char c = ch; 00185 write_chars(&c, 1, false); 00186 } 00187 00188 return 0; 00189 } 00190 00191 //////////////////////////////////////////////////////////////////// 00192 // Function: MultiplexStreamBuf::sync 00193 // Access: Public, Virtual 00194 // Description: Called by the system ostream implementation when the 00195 // buffer should be flushed to output (for instance, on 00196 // destruction). 00197 //////////////////////////////////////////////////////////////////// 00198 int MultiplexStreamBuf:: 00199 sync() { 00200 #ifdef OLD_HAVE_IPC 00201 mutex_lock m(_lock); 00202 #endif 00203 00204 streamsize n = pptr() - pbase(); 00205 00206 // We pass in false for the flush value, even though our 00207 // transmitting ostream said to sync. This allows us to get better 00208 // line buffering, since our transmitting ostream is often set 00209 // unitbuf, and might call sync multiple times in one line. We 00210 // still have an explicit flush() call to force the issue. 00211 write_chars(pbase(), n, false); 00212 pbump(-n); 00213 00214 return 0; // Return 0 for success, EOF to indicate write full. 00215 } 00216 00217 //////////////////////////////////////////////////////////////////// 00218 // Function: MultiplexStreamBuf::write_chars 00219 // Access: Private 00220 // Description: An internal function called by sync() and overflow() 00221 // to store one or more characters written to the stream 00222 // into the memory buffer. 00223 // 00224 // It is assumed that there is only one thread at a time 00225 // running this code; it is the responsibility of the 00226 // caller to grab the _lock mutex before calling this. 00227 //////////////////////////////////////////////////////////////////// 00228 void MultiplexStreamBuf:: 00229 write_chars(const char *start, int length, bool flush) { 00230 size_t orig = _line_buffer.length(); 00231 string latest; 00232 if (length != 0) { 00233 latest = string(start, length); 00234 } 00235 string line; 00236 00237 if (flush) { 00238 // If we're to flush the stream now, we dump the whole thing 00239 // regardless of whether we have reached end-of-line. 00240 line = _line_buffer + latest; 00241 _line_buffer = ""; 00242 00243 } else { 00244 // Otherwise, we check for the end-of-line character, for our 00245 // ostreams that only want a complete line at a time. 00246 _line_buffer += latest; 00247 size_t eol = _line_buffer.rfind('\n', orig); 00248 if (eol != string::npos) { 00249 line = _line_buffer.substr(0, eol + 1); 00250 _line_buffer = _line_buffer.substr(eol + 1); 00251 } 00252 } 00253 00254 Outputs::iterator oi; 00255 for (oi = _outputs.begin(); oi != _outputs.end(); ++oi) { 00256 Output &out = (*oi); 00257 switch (out._buffer_type) { 00258 case BT_none: 00259 // No buffering: send all new characters directly to the ostream. 00260 if (!latest.empty()) { 00261 out.write_string(latest); 00262 } 00263 break; 00264 00265 case BT_line: 00266 // Line buffering: send only when a complete line has been 00267 // received. 00268 if (!line.empty()) { 00269 out.write_string(line); 00270 } 00271 break; 00272 } 00273 } 00274 00275 }