Panda3D

pandaFileStreamBuf.cxx

00001 // Filename: pandaFileStreamBuf.cxx
00002 // Created by:  drose (08Sep08)
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 "pandaFileStreamBuf.h"
00016 #include "memoryHook.h"
00017 #include "filename.h"
00018 #include "textEncoder.h"
00019 
00020 #ifdef USE_PANDAFILESTREAM
00021 
00022 #ifndef _WIN32
00023 #include <sys/types.h>
00024 #include <sys/stat.h>
00025 #include <fcntl.h>
00026 #include <errno.h>
00027 #endif  // _WIN32
00028 
00029 PandaFileStreamBuf::NewlineMode PandaFileStreamBuf::_newline_mode = NM_native;
00030 
00031 static const size_t file_buffer_size = 4096;
00032 
00033 ////////////////////////////////////////////////////////////////////
00034 //     Function: PandaFileStreamBuf::Constructor
00035 //       Access: Public
00036 //  Description:
00037 ////////////////////////////////////////////////////////////////////
00038 PandaFileStreamBuf::
00039 PandaFileStreamBuf() {
00040   _is_open = false;
00041   _open_mode = (ios::openmode)0;
00042 
00043   _last_read_nl = 0;
00044   
00045 #ifdef _WIN32
00046   // Windows case.
00047   _handle = NULL;
00048 #else
00049   _fd = -1;
00050 #endif  // _WIN32
00051 
00052 #ifdef PHAVE_IOSTREAM
00053   _buffer = (char *)PANDA_MALLOC_ARRAY(file_buffer_size * 2);
00054   char *ebuf = _buffer + file_buffer_size * 2;
00055   char *mbuf = _buffer + file_buffer_size;
00056   setg(_buffer, mbuf, mbuf);
00057   setp(mbuf, ebuf);
00058 
00059 #else
00060   allocate();
00061   // Chop the buffer in half.  The bottom half goes to the get buffer;
00062   // the top half goes to the put buffer.
00063   char *b = base();
00064   char *t = ebuf();
00065   char *m = b + (t - b) / 2;
00066   setg(b, m, m);
00067   setp(b, m);
00068 #endif
00069 
00070   _gpos = 0;
00071   _ppos = 0;
00072 }
00073 
00074 ////////////////////////////////////////////////////////////////////
00075 //     Function: PandaFileStreamBuf::Destructor
00076 //       Access: Public, Virtual
00077 //  Description:
00078 ////////////////////////////////////////////////////////////////////
00079 PandaFileStreamBuf::
00080 ~PandaFileStreamBuf() {
00081   close();
00082 #ifdef PHAVE_IOSTREAM
00083   PANDA_FREE_ARRAY(_buffer);
00084 #endif
00085 }
00086 
00087 ////////////////////////////////////////////////////////////////////
00088 //     Function: PandaFileStreamBuf::open
00089 //       Access: Public
00090 //  Description: Attempts to open the file for input and/or output.
00091 ////////////////////////////////////////////////////////////////////
00092 void PandaFileStreamBuf::
00093 open(const char *filename, ios::openmode mode) {
00094   close();
00095 
00096   _filename = filename;
00097   _open_mode = mode;
00098   _is_open = false;
00099 
00100   if (_open_mode & ios::app) {
00101     // ios::app implies ios::out.
00102     _open_mode |= ios::out;
00103   }
00104 
00105 #ifdef _WIN32
00106   // Windows case.
00107   DWORD access = 0;
00108   if (_open_mode & ios::in) {
00109     access |= GENERIC_READ;
00110   }
00111   if (_open_mode & ios::out) {
00112     access |= GENERIC_WRITE;
00113   }
00114 
00115   DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
00116 
00117   DWORD creation_disposition = 0;
00118   if ((_open_mode & (ios::trunc | ios::out)) == (ios::trunc | ios::out)) {
00119     creation_disposition = CREATE_ALWAYS;
00120   } else if (_open_mode & ios::out) {
00121     creation_disposition = OPEN_ALWAYS;
00122   } else {
00123     creation_disposition = OPEN_EXISTING;
00124   }
00125 
00126   DWORD flags = 0;
00127 
00128   if (!(_open_mode & ios::out)) {
00129     flags |= FILE_ATTRIBUTE_READONLY;
00130   }
00131 
00132 #if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
00133   // In SIMPLE_THREADS mode, we use "overlapped" I/O.
00134   flags |= FILE_FLAG_OVERLAPPED;
00135 #endif
00136 
00137   TextEncoder encoder;
00138   encoder.set_encoding(Filename::get_filesystem_encoding());
00139   encoder.set_text(_filename);
00140   wstring wfilename = encoder.get_wtext();
00141   _handle = CreateFileW(wfilename.c_str(), access, share_mode,
00142                         NULL, creation_disposition, flags, NULL);
00143   if (_handle != INVALID_HANDLE_VALUE) {
00144     // The file was successfully opened and locked.
00145     _is_open = true;
00146   }
00147   
00148 #else
00149   // Posix case.
00150   int flags = 0;
00151 
00152   if ((_open_mode & (ios::in | ios::out)) == (ios::in | ios::out)) {
00153     flags |= O_RDWR | O_CREAT;
00154   } else if (_open_mode & ios::in) {
00155     flags |= O_RDONLY;
00156   } else if (_open_mode & ios::out) {
00157     flags |= O_WRONLY | O_CREAT;
00158   }
00159 
00160   if (_open_mode & ios::app) {
00161     flags |= O_APPEND;
00162   }
00163 
00164   if ((_open_mode & (ios::trunc | ios::out)) == (ios::trunc | ios::out)) {
00165     flags |= O_TRUNC;
00166   }
00167 
00168 #if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
00169   // In SIMPLE_THREADS mode, we use non-blocking I/O.
00170   flags |= O_NONBLOCK;
00171 #endif
00172 
00173   _fd = ::open(_filename.c_str(), flags, 0666);
00174   while (_fd == -1 && errno == EAGAIN) {
00175     thread_yield();
00176     _fd = ::open(_filename.c_str(), flags, 0666);
00177   }
00178 
00179   if (_fd != -1) {
00180     _is_open = true;
00181   }
00182 #endif  // _WIN32
00183 
00184 }
00185 
00186 #ifdef _WIN32
00187 ////////////////////////////////////////////////////////////////////
00188 //     Function: PandaFileStreamBuf::attach
00189 //       Access: Public
00190 //  Description: Connects the file stream to the existing OS-defined
00191 //               stream, presumably opened via a low-level OS call.
00192 //               The filename is for reporting only.  When the file
00193 //               stream is closed, it will also close the underlying
00194 //               OS handle.
00195 //
00196 //               This function is the Windows-specific variant.
00197 ////////////////////////////////////////////////////////////////////
00198 void PandaFileStreamBuf::
00199 attach(const char *filename, HANDLE handle, ios::openmode mode) {
00200   close();
00201 
00202   _filename = filename;
00203   _open_mode = mode;
00204   _is_open = false;
00205 
00206   if (_open_mode & ios::app) {
00207     // ios::app implies ios::out.
00208     _open_mode |= ios::out;
00209   }
00210 
00211   _handle = handle;
00212   if (_handle != INVALID_HANDLE_VALUE) {
00213     // Presumably the handle is valid.
00214     _is_open = true;
00215   }
00216 }
00217 #endif  // _WIN32
00218 
00219 #ifndef _WIN32
00220 ////////////////////////////////////////////////////////////////////
00221 //     Function: PandaFileStreamBuf::attach
00222 //       Access: Public
00223 //  Description: Connects the file stream to the existing OS-defined
00224 //               stream, presumably opened via a low-level OS call.
00225 //               The filename is for reporting only.  When the file
00226 //               stream is closed, it will also close the underlying
00227 //               OS handle.
00228 //
00229 //               This function is the Posix-specific variant.
00230 ////////////////////////////////////////////////////////////////////
00231 void PandaFileStreamBuf::
00232 attach(const char *filename, int fd, ios::openmode mode) {
00233   close();
00234 
00235   _filename = filename;
00236   _open_mode = mode;
00237   _is_open = false;
00238 
00239   if (_open_mode & ios::app) {
00240     // ios::app implies ios::out.
00241     _open_mode |= ios::out;
00242   }
00243 
00244   _fd = fd;
00245   if (_fd != -1) {
00246     // Presumably the file descriptor is valid.
00247     _is_open = true;
00248   }
00249 }
00250 #endif  // _WIN32
00251 
00252 ////////////////////////////////////////////////////////////////////
00253 //     Function: PandaFileStreamBuf::is_open
00254 //       Access: Public
00255 //  Description: Returns true if the file is open, false otherwise.
00256 ////////////////////////////////////////////////////////////////////
00257 bool PandaFileStreamBuf::
00258 is_open() const {
00259   return _is_open;
00260 }
00261 
00262 ////////////////////////////////////////////////////////////////////
00263 //     Function: PandaFileStreamBuf::close
00264 //       Access: Public
00265 //  Description: Empties the buffer and closes the file.
00266 ////////////////////////////////////////////////////////////////////
00267 void PandaFileStreamBuf::
00268 close() {
00269   // Make sure the write buffer is flushed.
00270   sync();
00271 
00272 #ifdef _WIN32
00273   if (_handle != NULL) {
00274     CloseHandle(_handle);
00275   }
00276   _handle = NULL;
00277 #else
00278   if (_fd != -1) {
00279     ::close(_fd);
00280   }
00281   _fd = -1;
00282 #endif  // _WIN32
00283 
00284   _is_open = false;
00285   _open_mode = (ios::openmode)0;
00286 
00287   _gpos = 0;
00288   _ppos = 0;
00289 
00290   pbump(pbase() - pptr());
00291   gbump(egptr() - gptr());
00292 }
00293 
00294 ////////////////////////////////////////////////////////////////////
00295 //     Function: PandaFileStreamBuf::seekoff
00296 //       Access: Public, Virtual
00297 //  Description: Implements seeking within the stream.
00298 ////////////////////////////////////////////////////////////////////
00299 streampos PandaFileStreamBuf::
00300 seekoff(streamoff off, ios_seekdir dir, ios_openmode which) {
00301   streampos result = -1;
00302 
00303   if (!(_open_mode & ios::binary)) {
00304     // Seeking on text files is only supported for seeks to the
00305     // beginning of the file.
00306     if (off != 0 || dir != ios::beg) {
00307       return -1;
00308     }
00309 
00310     _last_read_nl = 0;
00311   }
00312 
00313   // Sync the iostream buffer first.
00314   sync();
00315 
00316   if (which & ios::in) {
00317     // Determine the current file position.
00318     size_t n = egptr() - gptr();
00319     gbump(n);
00320     _gpos -= n;
00321     assert(_gpos >= 0);
00322     size_t cur_pos = _gpos;
00323     size_t new_pos = cur_pos;
00324     
00325     // Now adjust the data pointer appropriately.
00326     switch (dir) {
00327     case ios::beg:
00328       new_pos = (size_t)off;
00329       break;
00330       
00331     case ios::cur:
00332       new_pos = (size_t)((int)cur_pos + off);
00333       break;
00334       
00335     case ios::end:
00336 #ifdef _WIN32
00337       // Windows case.
00338       {
00339         LARGE_INTEGER li;
00340         GetFileSizeEx(_handle, &li);
00341         new_pos = (streampos)li.QuadPart + off;
00342       }
00343 #else
00344       // Posix case.
00345       {
00346         off_t li = lseek(_fd, off, SEEK_END);
00347         if (li == (size_t)-1) {
00348           return -1;
00349         }
00350         new_pos = (size_t)li;
00351       }
00352 #endif  // _WIN32
00353       break;
00354 
00355     default:
00356       // Shouldn't get here.
00357       break;
00358     }
00359 
00360     _gpos = new_pos;
00361     assert(_gpos >= 0);
00362     result = new_pos;
00363   }
00364 
00365   if (which & ios::out) {
00366     // Determine the current file position.
00367     size_t n = pptr() - pbase();
00368     streampos cur_pos = _ppos + (streamoff)n;
00369     streampos new_pos = cur_pos;
00370     
00371     // Now adjust the data pointer appropriately.
00372     switch (dir) {
00373     case ios::beg:
00374       new_pos = (streampos)off;
00375       break;
00376       
00377     case ios::cur:
00378       new_pos = (streampos)((int)cur_pos + off);
00379       break;
00380       
00381     case ios::end:
00382 #ifdef _WIN32
00383       // Windows case.
00384       {
00385         LARGE_INTEGER li;
00386         GetFileSizeEx(_handle, &li);
00387         new_pos = (streampos)li.QuadPart + off;
00388       }
00389 #else
00390       // Posix case.
00391       new_pos = lseek(_fd, off, SEEK_END);
00392       if (new_pos == (streampos)-1) {
00393         return -1;
00394       }
00395 #endif  // _WIN32
00396       break;
00397 
00398     default:
00399       // Shouldn't get here.
00400       break;
00401     }
00402 
00403     _ppos = new_pos;
00404     assert(_ppos >= 0);
00405     result = new_pos;
00406   }
00407 
00408   return result;
00409 }
00410 
00411 ////////////////////////////////////////////////////////////////////
00412 //     Function: PandaFileStreamBuf::seekpos
00413 //       Access: Public, Virtual
00414 //  Description: A variant on seekoff() to implement seeking within a
00415 //               stream.
00416 //
00417 //               The MSDN Library claims that it is only necessary to
00418 //               redefine seekoff(), and not seekpos() as well, as the
00419 //               default implementation of seekpos() is supposed to
00420 //               map to seekoff() exactly as I am doing here; but in
00421 //               fact it must do something else, because seeking
00422 //               didn't work on Windows until I redefined this
00423 //               function as well.
00424 ////////////////////////////////////////////////////////////////////
00425 streampos PandaFileStreamBuf::
00426 seekpos(streampos pos, ios_openmode which) {
00427   return seekoff(pos, ios::beg, which);
00428 }
00429 
00430 ////////////////////////////////////////////////////////////////////
00431 //     Function: PandaFileStreamBuf::overflow
00432 //       Access: Protected, Virtual
00433 //  Description: Called by the system ostream implementation when its
00434 //               internal buffer is filled, plus one character.
00435 ////////////////////////////////////////////////////////////////////
00436 int PandaFileStreamBuf::
00437 overflow(int ch) {
00438   bool okflag = true;
00439 
00440   size_t n = pptr() - pbase();
00441   if (n != 0) {
00442     size_t wrote = write_chars(pbase(), n);
00443     pbump(-(int)wrote);
00444     if (wrote != n) {
00445       okflag = false;
00446     }
00447   }
00448 
00449   if (okflag && ch != EOF) {
00450     if (pptr() != epptr()) {
00451       // Store the extra character back in the buffer.
00452       *(pptr()) = ch;
00453       pbump(1);
00454     } else {
00455       // No room to store ch.
00456       okflag = false;
00457     }
00458   }
00459 
00460   if (!okflag) {
00461     return EOF;
00462   }
00463   return 0;
00464 }
00465 
00466 ////////////////////////////////////////////////////////////////////
00467 //     Function: PandaFileStreamBuf::sync
00468 //       Access: Protected, Virtual
00469 //  Description: Called by the system iostream implementation to
00470 //               implement a flush operation.
00471 ////////////////////////////////////////////////////////////////////
00472 int PandaFileStreamBuf::
00473 sync() {
00474   size_t n = pptr() - pbase();
00475 
00476   size_t wrote = write_chars(pbase(), n);
00477   pbump(-(int)wrote);
00478 
00479   if (n != wrote) {
00480     return EOF;
00481   }
00482   return 0;
00483 }
00484 
00485 ////////////////////////////////////////////////////////////////////
00486 //     Function: PandaFileStreamBuf::underflow
00487 //       Access: Protected, Virtual
00488 //  Description: Called by the system istream implementation when its
00489 //               internal buffer needs more characters.
00490 ////////////////////////////////////////////////////////////////////
00491 int PandaFileStreamBuf::
00492 underflow() {
00493   // Sometimes underflow() is called even if the buffer is not empty.
00494   if (gptr() >= egptr()) {
00495     sync();
00496 
00497     // Mark the buffer filled (with buffer_size bytes).
00498     size_t buffer_size = egptr() - eback();
00499     gbump(-(int)buffer_size);
00500 
00501     size_t num_bytes = buffer_size;
00502     size_t read_count = read_chars(gptr(), buffer_size);
00503 
00504     if (read_count != num_bytes) {
00505       // Oops, we didn't read what we thought we would.
00506       if (read_count == 0) {
00507         gbump(num_bytes);
00508         return EOF;
00509       }
00510 
00511       // Slide what we did read to the top of the buffer.
00512       assert(read_count < num_bytes);
00513       size_t delta = num_bytes - read_count;
00514       memmove(gptr() + delta, gptr(), read_count);
00515       gbump(delta);
00516     }
00517   }
00518 
00519   return (unsigned char)*gptr();
00520 }
00521 
00522 ////////////////////////////////////////////////////////////////////
00523 //     Function: PandaFileStreamBuf::read_chars
00524 //       Access: Private
00525 //  Description: Attempts to extract the indicated number of
00526 //               characters from the current file position.  Returns
00527 //               the number of characters extracted.
00528 ////////////////////////////////////////////////////////////////////
00529 size_t PandaFileStreamBuf::
00530 read_chars(char *start, size_t length) {
00531   if (length == 0 || !_is_open) {
00532     // Trivial no-op.
00533     return 0;
00534   }
00535 
00536   // Make sure the write buffer is flushed.
00537   sync();
00538 
00539   if (_open_mode & ios::binary) {
00540     // If the file is opened in binary mode, just read the data in the
00541     // file.
00542     return read_chars_raw(start, length);
00543   }
00544 
00545   // The file is opened in text mode.  We have to decode newline
00546   // characters in the file.
00547   if (_newline_mode == NM_binary) {
00548     // Unless we're configured to always use binary mode.
00549     return read_chars_raw(start, length);
00550   }
00551 
00552   char *buffer = (char *)alloca(length);
00553 
00554   size_t read_length;
00555   size_t final_length;
00556   do {
00557     read_length = length - 1;
00558     if (_last_read_nl != 0) {
00559       // If we have a newline character to grow on, we might need to
00560       // expand the buffer we read from the file by one character.  In
00561       // that case, read one character less to make room for it.
00562       // (Otherwise, we are confident that the buffer will not expand
00563       // when we decode the newlines.)
00564       --read_length;
00565     }
00566     read_length = read_chars_raw(buffer, read_length);
00567     final_length = decode_newlines(start, length, buffer, read_length);
00568 
00569     // If we decoded all of the characters away, but we read nonzero
00570     // characters, go back and get some more.
00571   } while (read_length != 0 && final_length == 0);
00572 
00573   return final_length;
00574 }
00575 
00576 ////////////////////////////////////////////////////////////////////
00577 //     Function: PandaFileStreamBuf::write_chars
00578 //       Access: Private
00579 //  Description: Outputs the indicated stream of characters to the
00580 //               current file position.
00581 ////////////////////////////////////////////////////////////////////
00582 size_t PandaFileStreamBuf::
00583 write_chars(const char *start, size_t length) {
00584   if (length == 0) {
00585     // Trivial no-op.
00586     return 0;
00587   }
00588 
00589   // Make sure the read buffer is flushed.
00590   size_t n = egptr() - gptr();
00591   gbump(n);
00592   _gpos -= n;
00593   assert(_gpos >= 0);
00594 
00595   // Windows case.
00596   if (_open_mode & ios::binary) {
00597     // If the file is opened in binary mode, just write the data to the
00598     // file.
00599     return write_chars_raw(start, length);
00600   }
00601 
00602   // The file is opened in text mode.  We have to encode newline
00603   // characters to the file.
00604 
00605   NewlineMode this_newline_mode = _newline_mode;
00606   if (this_newline_mode == NM_native) {
00607 #ifdef _WIN32
00608     this_newline_mode = NM_msdos;
00609 #else
00610     // Even the Mac uses Unix-style EOL characters these days.
00611     this_newline_mode = NM_unix;
00612 #endif 
00613   }
00614 
00615   if (this_newline_mode == NM_binary) {
00616     return write_chars_raw(start, length);
00617   }
00618 
00619   size_t buffer_length = length;
00620   if (this_newline_mode == NM_msdos) {
00621     // Windows requires a larger buffer here, since we are writing two
00622     // newline characters for every one.
00623     buffer_length *= 2;
00624   }
00625   char *buffer = (char *)alloca(buffer_length);
00626 
00627   size_t write_length;
00628   switch (this_newline_mode) {
00629   case NM_msdos:
00630     write_length = encode_newlines_msdos(buffer, buffer_length, start, length);
00631     break;
00632 
00633   case NM_mac:
00634     write_length = encode_newlines_mac(buffer, buffer_length, start, length);
00635     break;
00636 
00637   default:
00638     write_length = encode_newlines_unix(buffer, buffer_length, start, length);
00639     break;
00640   }
00641 
00642   if (write_length == write_chars_raw(buffer, write_length)) {
00643     // Success.  Return the number of original characters.
00644     return length;
00645   }
00646 
00647   // Error.  Pretend we wrote nothing.
00648   return 0;
00649 }
00650 
00651 ////////////////////////////////////////////////////////////////////
00652 //     Function: PandaFileStreamBuf::read_chars_raw
00653 //       Access: Private
00654 //  Description: Reads raw data from the file directly into the
00655 //               indicated buffer.  Returns the number of characters
00656 //               read.
00657 ////////////////////////////////////////////////////////////////////
00658 size_t PandaFileStreamBuf::
00659 read_chars_raw(char *start, size_t length) {
00660   if (length == 0) {
00661     return 0;
00662   }
00663   
00664 #ifdef _WIN32
00665   // Windows case.
00666   OVERLAPPED overlapped;
00667   memset(&overlapped, 0, sizeof(overlapped));
00668   LARGE_INTEGER gpos;
00669   gpos.QuadPart = _gpos;
00670   overlapped.Offset = gpos.LowPart;
00671   overlapped.OffsetHigh = gpos.HighPart;
00672   
00673   DWORD bytes_read = 0;
00674   BOOL success = ReadFile(_handle, start, length, &bytes_read, &overlapped);
00675   int pass = 0;
00676   while (!success) {
00677     DWORD error = GetLastError();
00678     if (error == ERROR_IO_INCOMPLETE || error == ERROR_IO_PENDING) {
00679       // Wait for more later, but don't actually yield until we have
00680       // made the first call to GetOverlappedResult().  (Apparently,
00681       // Vista and Windows 7 *always* return ERROR_IO_INCOMPLETE after
00682       // the first call to ReadFile.)
00683       if (pass > 0) {
00684         thread_yield();
00685       }
00686     } else if (error == ERROR_HANDLE_EOF || error == ERROR_BROKEN_PIPE) {
00687       // End-of-file, normal result.
00688       break;
00689     } else {
00690       cerr
00691         << "Error reading " << length
00692         << " bytes from " << _filename << ", windows error code 0x" << hex
00693         << error << dec << ".\n";
00694       return 0;
00695     }
00696     ++pass;
00697     success = GetOverlappedResult(_handle, &overlapped, &bytes_read, false);
00698   }
00699 
00700   length = bytes_read;
00701   
00702 #else
00703   // Posix case.
00704   if (lseek(_fd, _gpos, SEEK_SET) == -1) {
00705     cerr
00706       << "Error seeking to position " << _gpos << " in " << _filename << "\n";
00707     return 0;
00708   }
00709   
00710   ssize_t result = ::read(_fd, start, length);
00711   while (result < 0) {
00712     if (errno == EAGAIN) {
00713       thread_yield();
00714     } else {
00715       cerr
00716         << "Error reading " << length << " bytes from " << _filename << "\n";
00717       return 0;
00718     }
00719     result = ::read(_fd, start, length);
00720   }
00721 
00722   length = result;
00723 #endif  // _WIN32
00724 
00725   _gpos += length;
00726   assert(_gpos >= 0);
00727   return length;
00728 }
00729 
00730 ////////////////////////////////////////////////////////////////////
00731 //     Function: PandaFileStreamBuf::write_chars_raw
00732 //       Access: Private
00733 //  Description: Writes the indicated buffer directly to the file
00734 //               stream.  Returns the number of characters written.
00735 ////////////////////////////////////////////////////////////////////
00736 size_t PandaFileStreamBuf::
00737 write_chars_raw(const char *start, size_t length) {
00738   if (length == 0 || !_is_open) {
00739     return 0;
00740   }
00741   
00742 #ifdef _WIN32
00743   // Windows case.
00744   OVERLAPPED overlapped;
00745   memset(&overlapped, 0, sizeof(overlapped));
00746   LARGE_INTEGER ppos;
00747   ppos.QuadPart = _ppos;
00748   overlapped.Offset = ppos.LowPart;
00749   overlapped.OffsetHigh = ppos.HighPart;
00750 
00751   if (_open_mode & ios::app) {
00752     overlapped.Offset = -1;
00753     overlapped.OffsetHigh = -1;
00754   }
00755   
00756   DWORD bytes_written = 0;
00757   BOOL success = WriteFile(_handle, start, length, &bytes_written, &overlapped);
00758   int pass = 0;
00759   while (!success) {
00760     DWORD error = GetLastError();
00761     if (error == ERROR_IO_INCOMPLETE || error == ERROR_IO_PENDING) {
00762       // Wait for more later, but don't actually yield until we have
00763       // made the first call to GetOverlappedResult().  (Apparently,
00764       // Vista and Windows 7 *always* return ERROR_IO_INCOMPLETE after
00765       // the first call to WriteFile.)
00766       if (pass > 0) {
00767         thread_yield();
00768       }
00769     } else if (error == ERROR_BROKEN_PIPE) {
00770       // Broken pipe, we're done.
00771       cerr << "Pipe closed on " << _filename << "\n";
00772       return bytes_written;
00773     } else {
00774       cerr
00775         << "Error writing " << length
00776         << " bytes to " << _filename << ", windows error code 0x" << hex
00777         << error << dec << ".  Disk full?\n";
00778       return bytes_written;
00779     }
00780     ++pass;
00781     success = GetOverlappedResult(_handle, &overlapped, &bytes_written, false);
00782   }
00783   assert(bytes_written == length);
00784   _ppos += bytes_written;
00785   assert(_ppos >= 0);
00786   
00787 #else
00788   // Posix case.
00789   if (!(_open_mode & ios::app)) {
00790     if (lseek(_fd, _ppos, SEEK_SET) == -1) {
00791       cerr
00792         << "Error seeking to position " << _ppos << " in " << _filename << "\n";
00793       return 0;
00794     }
00795   }
00796 
00797   size_t remaining = length;
00798   while (remaining > 0) {
00799     ssize_t result = ::write(_fd, start, remaining);
00800     if (result < 0) {
00801       if (errno == EAGAIN) {
00802         thread_yield();
00803       } else {
00804         cerr
00805           << "Error writing " << remaining << " bytes to " << _filename << "\n";
00806         return length - remaining;
00807       }
00808       continue;
00809     }
00810     
00811     start += result;
00812     remaining -= result;
00813     _ppos += result;
00814     assert(_ppos >= 0);
00815   }
00816 #endif  // _WIN32
00817 
00818   return length;
00819 }
00820 
00821 ////////////////////////////////////////////////////////////////////
00822 //     Function: PandaFileStreamBuf::decode_newlines
00823 //       Access: Private
00824 //  Description: Converts a buffer from universal newlines to \n.
00825 //
00826 //               Returns the number of characters placed in dest.
00827 //               This may also set (or read) the value of
00828 //               _last_read_nl, which is preserved from call-to-call
00829 //               to deal with newline combinations that straddle a
00830 //               read operation.
00831 ////////////////////////////////////////////////////////////////////
00832 size_t PandaFileStreamBuf::
00833 decode_newlines(char *dest, size_t dest_length,
00834                 const char *source, size_t source_length) {
00835   const char *p = source;  // Read from p
00836   char *q = dest;          // Write to q
00837 
00838   if (source_length == 0) {
00839     // A special case: this is at end-of-file.  Resolve the hanging
00840     // newline.
00841     switch (_last_read_nl) {
00842     case '\n':
00843     case '\r':
00844       // Close the newline to grow on.
00845       assert(q < dest + dest_length);
00846       *q++ = '\n';
00847       _last_read_nl = 0;
00848       break;
00849     default:
00850       break;
00851     }
00852   }
00853 
00854   while (p < source + source_length) {
00855     assert(q < dest + dest_length);
00856     switch (*p) {
00857     case '\n':
00858       switch (_last_read_nl) {
00859       case '\r':
00860         // \r\n is an MS-DOS newline.
00861         *q++ = '\n';
00862         _last_read_nl = 0;
00863         break;
00864       case '\n':
00865         // \n\n means one Unix newline, and one more to grow on.
00866         *q++ = '\n';
00867         _last_read_nl = '\n';
00868         break;
00869       default:
00870         // A lone \n is a newline to grow on.
00871         _last_read_nl = '\n';
00872         break;
00873       }
00874       break;
00875 
00876     case '\r':
00877       switch (_last_read_nl) {
00878       case '\n':
00879         // \n\r is an MS-DOS newline.
00880         *q++ = '\n';
00881         _last_read_nl = 0;
00882         break;
00883       case '\r':
00884         // \r\r means one Apple newline, and one more to grow on.
00885         *q++ = '\n';
00886         _last_read_nl = '\r';
00887         break;
00888       default:
00889         // A lone \r is a newline to grow on.
00890         _last_read_nl = '\r';
00891         break;
00892       }
00893       break;
00894 
00895     default:
00896       switch (_last_read_nl) {
00897       case '\n':
00898       case '\r':
00899         // Close the newline to grow on.
00900         *q++ = '\n';
00901         _last_read_nl = 0;
00902         break;
00903       default:
00904         break;
00905       }
00906       assert(q < dest + dest_length);
00907       *q++ = *p;
00908     }
00909     ++p;
00910   }
00911 
00912   return q - dest;
00913 }
00914 
00915 ////////////////////////////////////////////////////////////////////
00916 //     Function: PandaFileStreamBuf::encode_newlines_msdos
00917 //       Access: Private
00918 //  Description: Windows case: Converts a buffer from \n to \n\r.
00919 //
00920 //               To allow for full buffer expansion, dest_length
00921 //               should be at least 2*source_length.
00922 //
00923 //               Returns the number of characters placed in dest.
00924 ////////////////////////////////////////////////////////////////////
00925 size_t PandaFileStreamBuf::
00926 encode_newlines_msdos(char *dest, size_t dest_length,
00927                       const char *source, size_t source_length) {
00928   const char *p = source;  // Read from p
00929   char *q = dest;          // Write to q
00930 
00931   while (p < source + source_length) {
00932     assert(q < dest + dest_length);
00933     switch (*p) {
00934     case '\n':
00935       *q++ = '\r';
00936       assert(q < dest + dest_length);
00937       *q++ = '\n';
00938       break;
00939 
00940     case '\r':
00941       // Huh, shouldn't have one of these.
00942       break;
00943 
00944     default:
00945       *q++ = *p;
00946       break;
00947     }
00948 
00949     ++p;
00950   }
00951 
00952   return q - dest;
00953 }
00954 
00955 ////////////////////////////////////////////////////////////////////
00956 //     Function: PandaFileStreamBuf::encode_newlines_unix
00957 //       Access: Private
00958 //  Description: Unix case: Converts a buffer from \n to \n.
00959 //
00960 //               This is, of course, no conversion at all; but we do
00961 //               strip out \r characters if they appear in the buffer;
00962 //               this will help programmers to realize when they have
00963 //               incorrectly tagged a binary file with text mode, even
00964 //               on a Posix environment.
00965 //
00966 //               Returns the number of characters placed in dest.
00967 ////////////////////////////////////////////////////////////////////
00968 size_t PandaFileStreamBuf::
00969 encode_newlines_unix(char *dest, size_t dest_length,
00970                      const char *source, size_t source_length) {
00971   const char *p = source;  // Read from p
00972   char *q = dest;          // Write to q
00973 
00974   while (p < source + source_length) {
00975     assert(q < dest + dest_length);
00976     switch (*p) {
00977     case '\r':
00978       break;
00979 
00980     default:
00981       *q++ = *p;
00982       break;
00983     }
00984 
00985     ++p;
00986   }
00987 
00988   return q - dest;
00989 }
00990 
00991 ////////////////////////////////////////////////////////////////////
00992 //     Function: PandaFileStreamBuf::encode_newlines_mac
00993 //       Access: Private
00994 //  Description: Classic Mac case: Converts a buffer from \n to \r.
00995 //
00996 //               Returns the number of characters placed in dest.
00997 ////////////////////////////////////////////////////////////////////
00998 size_t PandaFileStreamBuf::
00999 encode_newlines_mac(char *dest, size_t dest_length,
01000                     const char *source, size_t source_length) {
01001   const char *p = source;  // Read from p
01002   char *q = dest;          // Write to q
01003 
01004   while (p < source + source_length) {
01005     assert(q < dest + dest_length);
01006     switch (*p) {
01007     case '\n':
01008       *q++ = '\r';
01009       break;
01010 
01011     case '\r':
01012       break;
01013 
01014     default:
01015       *q++ = *p;
01016       break;
01017     }
01018 
01019     ++p;
01020   }
01021 
01022   return q - dest;
01023 }
01024 
01025 ostream &
01026 operator << (ostream &out, PandaFileStreamBuf::NewlineMode newline_mode) {
01027   switch (newline_mode) {
01028   case PandaFileStreamBuf::NM_native:
01029     return out << "native";
01030 
01031   case PandaFileStreamBuf::NM_binary:
01032     return out << "binary";
01033 
01034   case PandaFileStreamBuf::NM_msdos:
01035     return out << "msdos";
01036 
01037   case PandaFileStreamBuf::NM_unix:
01038     return out << "unix";
01039 
01040   case PandaFileStreamBuf::NM_mac:
01041     return out << "mac";
01042   }
01043 
01044   cerr
01045     << "Invalid NewlineMode value: " << (int)newline_mode << "\n";
01046   return out;
01047 }
01048 
01049 istream &
01050 operator >> (istream &in, PandaFileStreamBuf::NewlineMode &newline_mode) {
01051   string word;
01052   in >> word;
01053 
01054   if (word == "native") {
01055     newline_mode = PandaFileStreamBuf::NM_native;
01056   } else if (word == "binary") {
01057     newline_mode = PandaFileStreamBuf::NM_binary;
01058   } else if (word == "msdos") {
01059     newline_mode = PandaFileStreamBuf::NM_msdos;
01060   } else if (word == "unix") {
01061     newline_mode = PandaFileStreamBuf::NM_unix;
01062   } else if (word == "mac") {
01063     newline_mode = PandaFileStreamBuf::NM_mac;
01064   } else {
01065     cerr
01066       << "Invalid NewlineMode value: " << word << "\n";
01067     newline_mode = PandaFileStreamBuf::NM_native;
01068   }
01069 
01070   return in;
01071 }
01072 
01073 #endif  // USE_PANDAFILESTREAM
 All Classes Functions Variables Enumerations