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