Panda3D
|
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