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