eol conversion

This commit is contained in:
David Rose 2008-09-09 21:12:41 +00:00
parent 35a643c737
commit 67c24c33e8
2 changed files with 290 additions and 11 deletions

View File

@ -34,6 +34,8 @@ PandaFileStreamBuf() {
_is_open = false;
_open_mode = (ios::openmode)0;
_last_read_nl = 0;
#ifdef _WIN32
// Windows case.
_handle = NULL;
@ -217,6 +219,16 @@ streampos PandaFileStreamBuf::
seekoff(streamoff off, ios_seekdir dir, ios_openmode which) {
streampos result = -1;
if (!(_open_mode & ios::binary)) {
// Seeking on text files is only supported for seeks to the
// beginning of the file.
if (off != 0 || dir != ios::beg) {
return -1;
}
_last_read_nl = 0;
}
// Sync the iostream buffer first.
sync();
@ -418,7 +430,6 @@ underflow() {
return (unsigned char)*gptr();
}
////////////////////////////////////////////////////////////////////
// Function: PandaFileStreamBuf::read_chars
// Access: Private
@ -429,12 +440,100 @@ underflow() {
size_t PandaFileStreamBuf::
read_chars(char *start, size_t length) {
if (length == 0) {
// Trivial no-op.
return 0;
}
// Make sure the write buffer is flushed.
sync();
if (_open_mode & ios::binary) {
// If the file is opened in binary mode, just read the data in the
// file.
return read_chars_raw(start, length);
}
// The file is opened in text mode. We have to decode newline
// characters in the file.
char *buffer = (char *)alloca(length);
size_t read_length;
size_t final_length;
do {
read_length = length - 1;
if (_last_read_nl != 0) {
// If we have a newline character to grow on, we might need to
// expand the buffer we read from the file by one character. In
// that case, read one character less to make room for it.
// (Otherwise, we are confident that the buffer will not expand
// when we decode the newlines.)
--read_length;
}
read_length = read_chars_raw(buffer, read_length);
final_length = decode_newlines(start, length, buffer, read_length);
// If we decoded all of the characters away, but we read nonzero
// characters, go back and get some more.
} while (read_length != 0 && final_length == 0);
return final_length;
}
////////////////////////////////////////////////////////////////////
// Function: PandaFileStreamBuf::write_chars
// Access: Private
// Description: Outputs the indicated stream of characters to the
// current file position.
////////////////////////////////////////////////////////////////////
size_t PandaFileStreamBuf::
write_chars(const char *start, size_t length) {
if (length == 0) {
// Trivial no-op.
return 0;
}
// Make sure the read buffer is flushed.
size_t n = egptr() - gptr();
gbump(n);
_gpos -= n;
// Windows case.
if (_open_mode & ios::binary) {
// If the file is opened in binary mode, just write the data to the
// file.
return write_chars_raw(start, length);
}
// The file is opened in text mode. We have to encode newline
// characters to the file.
#ifdef _WIN32
// Windows requires a larger buffer here, since we are writing two
// newline characters for every one.
size_t buffer_length = length * 2;
#else
size_t buffer_length = length;
#endif // _WIN32
char *buffer = (char *)alloca(buffer_length);
size_t write_length = encode_newlines(buffer, buffer_length, start, length);
return write_chars_raw(buffer, write_length);
}
////////////////////////////////////////////////////////////////////
// Function: PandaFileStreamBuf::read_chars_raw
// Access: Private
// Description: Reads raw data from the file directly into the
// indicated buffer. Returns the number of characters
// read.
////////////////////////////////////////////////////////////////////
size_t PandaFileStreamBuf::
read_chars_raw(char *start, size_t length) {
if (length == 0) {
return 0;
}
#ifdef _WIN32
// Windows case.
OVERLAPPED overlapped;
@ -494,23 +593,17 @@ read_chars(char *start, size_t length) {
}
////////////////////////////////////////////////////////////////////
// Function: PandaFileStreamBuf::write_chars
// Function: PandaFileStreamBuf::write_chars_raw
// Access: Private
// Description: Outputs the indicated stream of characters to the
// current file position.
// Description: Writes the indicated buffer directly to the file
// stream. Returns the number of characters written.
////////////////////////////////////////////////////////////////////
size_t PandaFileStreamBuf::
write_chars(const char *start, size_t length) {
write_chars_raw(const char *start, size_t length) {
if (length == 0) {
// Trivial no-op.
return 0;
}
// Make sure the read buffer is flushed.
size_t n = egptr() - gptr();
gbump(n);
_gpos -= n;
#ifdef _WIN32
// Windows case.
OVERLAPPED overlapped;
@ -571,3 +664,179 @@ write_chars(const char *start, size_t length) {
return length;
}
////////////////////////////////////////////////////////////////////
// Function: PandaFileStreamBuf::decode_newlines
// Access: Private
// Description: Converts a buffer from universal newlines to \n.
//
// Returns the number of characters placed in dest.
// This may also set (or read) the value of
// _last_read_nl, which is preserved from call-to-call
// to deal with newline combinations that straddle a
// read operation.
////////////////////////////////////////////////////////////////////
size_t PandaFileStreamBuf::
decode_newlines(char *dest, size_t dest_length,
const char *source, size_t source_length) {
const char *p = source; // Read from p
char *q = dest; // Write to q
if (source_length == 0) {
// A special case: this is at end-of-file. Resolve the hanging
// newline.
switch (_last_read_nl) {
case '\n':
case '\r':
// Close the newline to grow on.
assert(q < dest + dest_length);
*q++ = '\n';
_last_read_nl = 0;
break;
default:
break;
}
}
while (p < source + source_length) {
assert(q < dest + dest_length);
switch (*p) {
case '\n':
switch (_last_read_nl) {
case '\r':
// \r\n is an MS-DOS newline.
*q++ = '\n';
_last_read_nl = 0;
break;
case '\n':
// \n\n means one Unix newline, and one more to grow on.
*q++ = '\n';
_last_read_nl = '\n';
break;
default:
// A lone \n is a newline to grow on.
_last_read_nl = '\n';
break;
}
break;
case '\r':
switch (_last_read_nl) {
case '\n':
// \n\r is an MS-DOS newline.
*q++ = '\n';
_last_read_nl = 0;
break;
case '\r':
// \r\r means one Apple newline, and one more to grow on.
*q++ = '\n';
_last_read_nl = '\r';
break;
default:
// A lone \r is a newline to grow on.
_last_read_nl = '\r';
break;
}
break;
default:
switch (_last_read_nl) {
case '\n':
case '\r':
// Close the newline to grow on.
*q++ = '\n';
_last_read_nl = 0;
break;
default:
break;
}
assert(q < dest + dest_length);
*q++ = *p;
}
++p;
}
return q - dest;
}
#ifdef _WIN32
////////////////////////////////////////////////////////////////////
// Function: PandaFileStreamBuf::encode_newlines
// Access: Private
// Description: Windows case: Converts a buffer from \n to \n\r.
//
// To allow for full buffer expansion, dest_length
// should be at least 2*source_length.
//
// Returns the number of characters placed in dest.
////////////////////////////////////////////////////////////////////
size_t PandaFileStreamBuf::
encode_newlines(char *dest, size_t dest_length,
const char *source, size_t source_length) {
const char *p = source; // Read from p
char *q = dest; // Write to q
while (p < source + source_length) {
assert(q < dest + dest_length);
switch (*p) {
case '\n':
*q++ = '\r';
assert(q < dest + dest_length);
*q++ = '\n';
break;
case '\r':
// Huh, shouldn't have one of these.
break;
default:
*q++ = *p;
break;
}
++p;
}
return q - dest;
}
#else // _WIN32
////////////////////////////////////////////////////////////////////
// Function: PandaFileStreamBuf::encode_newlines
// Access: Private
// Description: Posix case: Converts a buffer from \n to \n.
//
// This is, of course, no conversion at all; but we do
// strip out \r characters if they appear in the buffer;
// this will help programmers to realize when they have
// incorrectly tagged a binary file with text mode, even
// on a Posix environment.
//
// Returns the number of characters placed in dest.
////////////////////////////////////////////////////////////////////
size_t PandaFileStreamBuf::
encode_newlines(char *dest, size_t dest_length,
const char *source, size_t source_length) {
const char *p = source; // Read from p
char *q = dest; // Write to q
while (p < source + source_length) {
assert(q < dest + dest_length);
switch (*p) {
case '\r':
break;
default:
*q++ = *p;
break;
}
++p;
}
return q - dest;
}
#endif // _WIN32

View File

@ -48,11 +48,21 @@ private:
size_t read_chars(char *start, size_t length);
size_t write_chars(const char *start, size_t length);
size_t read_chars_raw(char *start, size_t length);
size_t write_chars_raw(const char *start, size_t length);
size_t decode_newlines(char *dest, size_t dest_length,
const char *source, size_t source_length);
size_t encode_newlines(char *dest, size_t dest_length,
const char *source, size_t source_length);
private:
string _filename;
bool _is_open;
ios::openmode _open_mode;
char _last_read_nl;
#ifdef _WIN32
HANDLE _handle;
#else