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