Panda3D
Loading...
Searching...
No Matches
pandaFileStreamBuf.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file pandaFileStreamBuf.cxx
10 * @author drose
11 * @date 2008-09-08
12 */
13
14#include "pandaFileStreamBuf.h"
15#include "memoryHook.h"
16#include "filename.h"
17#include "textEncoder.h"
18
19#ifdef USE_PANDAFILESTREAM
20
21#ifndef _WIN32
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <fcntl.h>
25#include <errno.h>
26#endif // _WIN32
27
28using std::cerr;
29using std::dec;
30using std::hex;
31using std::ios;
32using std::istream;
33using std::ostream;
34using std::streamoff;
35using std::streampos;
36using std::string;
37
38PandaFileStreamBuf::NewlineMode PandaFileStreamBuf::_newline_mode = NM_native;
39
40static const size_t file_buffer_size = 4096;
41
42/**
43 *
44 */
45PandaFileStreamBuf::
46PandaFileStreamBuf() {
47 _is_open = false;
48 _open_mode = (ios::openmode)0;
49
50 _last_read_nl = 0;
51
52#ifdef _WIN32
53 // Windows case.
54 _handle = nullptr;
55#else
56 _fd = -1;
57#endif // _WIN32
58
59#ifdef PHAVE_IOSTREAM
60 _buffer = (char *)PANDA_MALLOC_ARRAY(file_buffer_size * 2);
61 char *ebuf = _buffer + file_buffer_size * 2;
62 char *mbuf = _buffer + file_buffer_size;
63 setg(_buffer, mbuf, mbuf);
64 setp(mbuf, ebuf);
65
66#else
67 allocate();
68 // Chop the buffer in half. The bottom half goes to the get buffer; the top
69 // half goes to the put buffer.
70 char *b = base();
71 char *t = ebuf();
72 char *m = b + (t - b) / 2;
73 setg(b, m, m);
74 setp(b, m);
75#endif
76
77 _gpos = 0;
78 _ppos = 0;
79}
80
81/**
82 *
83 */
84PandaFileStreamBuf::
85~PandaFileStreamBuf() {
86 close();
87#ifdef PHAVE_IOSTREAM
88 PANDA_FREE_ARRAY(_buffer);
89#endif
90}
91
92/**
93 * Attempts to open the file for input and/or output.
94 */
95void PandaFileStreamBuf::
96open(const char *filename, ios::openmode mode) {
97 close();
98
99 _filename = filename;
100 _open_mode = mode;
101 _is_open = false;
102
103 if (_open_mode & ios::app) {
104 // ios::app implies ios::out.
105 _open_mode |= ios::out;
106 }
107
108#ifdef _WIN32
109 // Windows case.
110 DWORD access = 0;
111 if (_open_mode & ios::in) {
112 access |= GENERIC_READ;
113 }
114 if (_open_mode & ios::out) {
115 access |= GENERIC_WRITE;
116 }
117
118 DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
119
120 DWORD creation_disposition = 0;
121 if ((_open_mode & (ios::trunc | ios::out)) == (ios::trunc | ios::out)) {
122 creation_disposition = CREATE_ALWAYS;
123 } else if (_open_mode & ios::out) {
124 creation_disposition = OPEN_ALWAYS;
125 } else {
126 creation_disposition = OPEN_EXISTING;
127 }
128
129 DWORD flags = 0;
130
131 if (!(_open_mode & ios::out)) {
132 flags |= FILE_ATTRIBUTE_READONLY;
133 }
134
135#if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
136 // In SIMPLE_THREADS mode, we use "overlapped" IO.
137 flags |= FILE_FLAG_OVERLAPPED;
138#endif
139
140 TextEncoder encoder;
141 encoder.set_encoding(Filename::get_filesystem_encoding());
142 encoder.set_text(_filename);
143 std::wstring wfilename = encoder.get_wtext();
144 _handle = CreateFileW(wfilename.c_str(), access, share_mode,
145 nullptr, creation_disposition, flags, nullptr);
146 if (_handle != INVALID_HANDLE_VALUE) {
147 // The file was successfully opened and locked.
148 _is_open = true;
149 }
150
151#else
152 // Posix case.
153 int flags = 0;
154
155 if ((_open_mode & (ios::in | ios::out)) == (ios::in | ios::out)) {
156 flags |= O_RDWR | O_CREAT;
157 } else if (_open_mode & ios::in) {
158 flags |= O_RDONLY;
159 } else if (_open_mode & ios::out) {
160 flags |= O_WRONLY | O_CREAT;
161 }
162
163 if (_open_mode & ios::app) {
164 flags |= O_APPEND;
165 }
166
167 if ((_open_mode & (ios::trunc | ios::out)) == (ios::trunc | ios::out)) {
168 flags |= O_TRUNC;
169 }
170
171#if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
172 // In SIMPLE_THREADS mode, we use non-blocking IO.
173 flags |= O_NONBLOCK;
174#endif
175
176 _fd = ::open(_filename.c_str(), flags, 0666);
177 while (_fd == -1 && errno == EAGAIN) {
178 thread_yield();
179 _fd = ::open(_filename.c_str(), flags, 0666);
180 }
181
182 if (_fd != -1) {
183 _is_open = true;
184 }
185#endif // _WIN32
186
187}
188
189#ifdef _WIN32
190/**
191 * Connects the file stream to the existing OS-defined stream, presumably
192 * opened via a low-level OS call. The filename is for reporting only. When
193 * the file stream is closed, it will also close the underlying OS handle.
194 *
195 * This function is the Windows-specific variant.
196 */
197void PandaFileStreamBuf::
198attach(const char *filename, HANDLE handle, ios::openmode mode) {
199 close();
200
201 _filename = filename;
202 _open_mode = mode;
203 _is_open = false;
204
205 if (_open_mode & ios::app) {
206 // ios::app implies ios::out.
207 _open_mode |= ios::out;
208 }
209
210 _handle = handle;
211 if (_handle != INVALID_HANDLE_VALUE) {
212 // Presumably the handle is valid.
213 _is_open = true;
214 }
215}
216#endif // _WIN32
217
218#ifndef _WIN32
219/**
220 * Connects the file stream to the existing OS-defined stream, presumably
221 * opened via a low-level OS call. The filename is for reporting only. When
222 * the file stream is closed, it will also close the underlying OS handle.
223 *
224 * This function is the Posix-specific variant.
225 */
226void PandaFileStreamBuf::
227attach(const char *filename, int fd, ios::openmode mode) {
228 close();
229
230 _filename = filename;
231 _open_mode = mode;
232 _is_open = false;
233
234 if (_open_mode & ios::app) {
235 // ios::app implies ios::out.
236 _open_mode |= ios::out;
237 }
238
239 _fd = fd;
240 if (_fd != -1) {
241 // Presumably the file descriptor is valid.
242 _is_open = true;
243 }
244}
245#endif // _WIN32
246
247/**
248 * Returns true if the file is open, false otherwise.
249 */
250bool PandaFileStreamBuf::
251is_open() const {
252 return _is_open;
253}
254
255/**
256 * Empties the buffer and closes the file.
257 */
258void PandaFileStreamBuf::
259close() {
260 // Make sure the write buffer is flushed.
261 sync();
262
263#ifdef _WIN32
264 if (_handle != nullptr) {
265 CloseHandle(_handle);
266 }
267 _handle = nullptr;
268#else
269 if (_fd != -1) {
270 if (::close(_fd) < 0) {
271#ifdef NDEBUG
272 perror("close");
273#else
274 char *str = (char *)alloca(_filename.size() + 32);
275 sprintf(str, "close(%d \"%s\")", _fd, _filename.c_str());
276 perror(str);
277#endif
278 }
279 }
280 _fd = -1;
281#endif // _WIN32
282
283 _is_open = false;
284 _open_mode = (ios::openmode)0;
285
286 _gpos = 0;
287 _ppos = 0;
288
289 pbump(pbase() - pptr());
290 gbump(egptr() - gptr());
291}
292
293/**
294 * Implements seeking within the stream.
295 */
296streampos PandaFileStreamBuf::
297seekoff(streamoff off, ios_seekdir dir, ios_openmode which) {
298 streampos result = -1;
299
300 if (!(_open_mode & ios::binary)) {
301 // Seeking on text files is only supported for seeks to the beginning of
302 // the file.
303 if (off != 0 || dir != ios::beg) {
304 return -1;
305 }
306
307 _last_read_nl = 0;
308 }
309
310 // Sync the iostream buffer first.
311 sync();
312
313 if (which & ios::in) {
314 // Determine the current file position.
315 size_t n = egptr() - gptr();
316 gbump(n);
317 _gpos -= n;
318 assert(_gpos >= 0);
319 streampos cur_pos = _gpos;
320 streampos new_pos = cur_pos;
321
322 // Now adjust the data pointer appropriately.
323 switch (dir) {
324 case ios::beg:
325 new_pos = (streampos)off;
326 break;
327
328 case ios::cur:
329 new_pos = (streampos)(cur_pos + off);
330 break;
331
332 case ios::end:
333#ifdef _WIN32
334 // Windows case.
335 {
336 LARGE_INTEGER li;
337 GetFileSizeEx(_handle, &li);
338 new_pos = (streampos)li.QuadPart + off;
339 }
340#else
341 // Posix case.
342 {
343 off_t li = lseek(_fd, off, SEEK_END);
344 if (li == (off_t)-1 || (sizeof(off_t) == 8 && li == 0x7fffffffffffffff)) {
345 return -1;
346 }
347 new_pos = (size_t)li;
348 }
349#endif // _WIN32
350 break;
351
352 default:
353 // Shouldn't get here.
354 break;
355 }
356
357 _gpos = new_pos;
358 assert(_gpos >= 0);
359 result = new_pos;
360 }
361
362 if (which & ios::out) {
363 // Determine the current file position.
364 size_t n = pptr() - pbase();
365 streampos cur_pos = _ppos + (streamoff)n;
366 streampos new_pos = cur_pos;
367
368 // Now adjust the data pointer appropriately.
369 switch (dir) {
370 case ios::beg:
371 new_pos = (streampos)off;
372 break;
373
374 case ios::cur:
375 new_pos = (streampos)(cur_pos + off);
376 break;
377
378 case ios::end:
379#ifdef _WIN32
380 // Windows case.
381 {
382 LARGE_INTEGER li;
383 GetFileSizeEx(_handle, &li);
384 new_pos = (streampos)li.QuadPart + off;
385 }
386#else
387 // Posix case.
388 new_pos = lseek(_fd, off, SEEK_END);
389 if (new_pos == (streampos)-1) {
390 return -1;
391 }
392#endif // _WIN32
393 break;
394
395 default:
396 // Shouldn't get here.
397 break;
398 }
399
400 _ppos = new_pos;
401 assert(_ppos >= 0);
402 result = new_pos;
403 }
404
405 return result;
406}
407
408/**
409 * A variant on seekoff() to implement seeking within a stream.
410 *
411 * The MSDN Library claims that it is only necessary to redefine seekoff(),
412 * and not seekpos() as well, as the default implementation of seekpos() is
413 * supposed to map to seekoff() exactly as I am doing here; but in fact it
414 * must do something else, because seeking didn't work on Windows until I
415 * redefined this function as well.
416 */
417streampos PandaFileStreamBuf::
418seekpos(streampos pos, ios_openmode which) {
419 return seekoff(pos, ios::beg, which);
420}
421
422/**
423 * Called by the system ostream implementation when its internal buffer is
424 * filled, plus one character.
425 */
426int PandaFileStreamBuf::
427overflow(int ch) {
428 bool okflag = true;
429
430 size_t n = pptr() - pbase();
431 if (n != 0) {
432 size_t wrote = write_chars(pbase(), n);
433 pbump(-(streamoff)wrote);
434 if (wrote != n) {
435 okflag = false;
436 }
437 }
438
439 if (okflag && ch != EOF) {
440 if (pptr() != epptr()) {
441 // Store the extra character back in the buffer.
442 *(pptr()) = ch;
443 pbump(1);
444 } else {
445 // No room to store ch.
446 okflag = false;
447 }
448 }
449
450 if (!okflag) {
451 return EOF;
452 }
453 return 0;
454}
455
456/**
457 * Called by the system iostream implementation to implement a flush
458 * operation.
459 */
460int PandaFileStreamBuf::
461sync() {
462 size_t n = pptr() - pbase();
463
464 size_t wrote = write_chars(pbase(), n);
465 pbump(-(streamoff)wrote);
466
467 if (n != wrote) {
468 return EOF;
469 }
470 return 0;
471}
472
473/**
474 * Called by the system istream implementation when its internal buffer needs
475 * more characters.
476 */
477int PandaFileStreamBuf::
478underflow() {
479 // Sometimes underflow() is called even if the buffer is not empty.
480 if (gptr() >= egptr()) {
481 sync();
482
483 // Mark the buffer filled (with buffer_size bytes).
484 size_t buffer_size = egptr() - eback();
485 gbump(-(streamoff)buffer_size);
486
487 size_t num_bytes = buffer_size;
488 size_t read_count = read_chars(gptr(), buffer_size);
489
490 if (read_count != num_bytes) {
491 // Oops, we didn't read what we thought we would.
492 if (read_count == 0) {
493 gbump(num_bytes);
494 return EOF;
495 }
496
497 // Slide what we did read to the top of the buffer.
498 assert(read_count < num_bytes);
499 size_t delta = num_bytes - read_count;
500 memmove(gptr() + delta, gptr(), read_count);
501 gbump(delta);
502 }
503 }
504
505 return (unsigned char)*gptr();
506}
507
508/**
509 * Attempts to extract the indicated number of characters from the current
510 * file position. Returns the number of characters extracted.
511 */
512size_t PandaFileStreamBuf::
513read_chars(char *start, size_t length) {
514 if (length == 0 || !_is_open) {
515 // Trivial no-op.
516 return 0;
517 }
518
519 // Make sure the write buffer is flushed.
520 sync();
521
522 if (_open_mode & ios::binary) {
523 // If the file is opened in binary mode, just read the data in the file.
524 return read_chars_raw(start, length);
525 }
526
527 // The file is opened in text mode. We have to decode newline characters in
528 // the file.
529 if (_newline_mode == NM_binary) {
530 // Unless we're configured to always use binary mode.
531 return read_chars_raw(start, length);
532 }
533
534 char *buffer = (char *)alloca(length);
535
536 size_t read_length;
537 size_t final_length;
538 do {
539 read_length = length - 1;
540 if (_last_read_nl != 0) {
541 // If we have a newline character to grow on, we might need to expand
542 // the buffer we read from the file by one character. In that case,
543 // read one character less to make room for it. (Otherwise, we are
544 // confident that the buffer will not expand when we decode the
545 // newlines.)
546 --read_length;
547 }
548 read_length = read_chars_raw(buffer, read_length);
549 final_length = decode_newlines(start, length, buffer, read_length);
550
551 // If we decoded all of the characters away, but we read nonzero
552 // characters, go back and get some more.
553 } while (read_length != 0 && final_length == 0);
554
555 return final_length;
556}
557
558/**
559 * Outputs the indicated stream of characters to the current file position.
560 */
561size_t PandaFileStreamBuf::
562write_chars(const char *start, size_t length) {
563 if (length == 0) {
564 // Trivial no-op.
565 return 0;
566 }
567
568 // Make sure the read buffer is flushed.
569 size_t n = egptr() - gptr();
570 gbump(n);
571 _gpos -= n;
572 assert(_gpos >= 0);
573
574 // Windows case.
575 if (_open_mode & ios::binary) {
576 // If the file is opened in binary mode, just write the data to the file.
577 return write_chars_raw(start, length);
578 }
579
580 // The file is opened in text mode. We have to encode newline characters to
581 // the file.
582
583 NewlineMode this_newline_mode = _newline_mode;
584 if (this_newline_mode == NM_native) {
585#ifdef _WIN32
586 this_newline_mode = NM_msdos;
587#else
588 // Even the Mac uses Unix-style EOL characters these days.
589 this_newline_mode = NM_unix;
590#endif
591 }
592
593 if (this_newline_mode == NM_binary) {
594 return write_chars_raw(start, length);
595 }
596
597 size_t buffer_length = length;
598 if (this_newline_mode == NM_msdos) {
599 // Windows requires a larger buffer here, since we are writing two newline
600 // characters for every one.
601 buffer_length *= 2;
602 }
603 char *buffer = (char *)alloca(buffer_length);
604
605 size_t write_length;
606 switch (this_newline_mode) {
607 case NM_msdos:
608 write_length = encode_newlines_msdos(buffer, buffer_length, start, length);
609 break;
610
611 case NM_mac:
612 write_length = encode_newlines_mac(buffer, buffer_length, start, length);
613 break;
614
615 default:
616 write_length = encode_newlines_unix(buffer, buffer_length, start, length);
617 break;
618 }
619
620 if (write_length == write_chars_raw(buffer, write_length)) {
621 // Success. Return the number of original characters.
622 return length;
623 }
624
625 // Error. Pretend we wrote nothing.
626 return 0;
627}
628
629/**
630 * Reads raw data from the file directly into the indicated buffer. Returns
631 * the number of characters read.
632 */
633size_t PandaFileStreamBuf::
634read_chars_raw(char *start, size_t length) {
635 if (length == 0) {
636 return 0;
637 }
638
639#ifdef _WIN32
640 // Windows case.
641 OVERLAPPED overlapped;
642 memset(&overlapped, 0, sizeof(overlapped));
643 LARGE_INTEGER gpos;
644 gpos.QuadPart = _gpos;
645 overlapped.Offset = gpos.LowPart;
646 overlapped.OffsetHigh = gpos.HighPart;
647
648 DWORD bytes_read = 0;
649 BOOL success = ReadFile(_handle, start, length, &bytes_read, &overlapped);
650 int pass = 0;
651 while (!success) {
652 DWORD error = GetLastError();
653 if (error == ERROR_IO_INCOMPLETE || error == ERROR_IO_PENDING) {
654 // Wait for more later, but don't actually yield until we have made the
655 // first call to GetOverlappedResult(). (Apparently, Vista and Windows
656 // 7 *always* return ERROR_IO_INCOMPLETE after the first call to
657 // ReadFile.)
658 if (pass > 0) {
659 thread_yield();
660 }
661 } else if (error == ERROR_HANDLE_EOF || error == ERROR_BROKEN_PIPE) {
662 // End-of-file, normal result.
663 break;
664 } else {
665 cerr
666 << "Error reading " << length
667 << " bytes from " << _filename << ", windows error code 0x" << hex
668 << error << dec << ".\n";
669 return 0;
670 }
671 ++pass;
672 success = GetOverlappedResult(_handle, &overlapped, &bytes_read, false);
673 }
674
675 length = bytes_read;
676
677#else
678 // Posix case.
679 if (lseek(_fd, _gpos, SEEK_SET) == -1) {
680 cerr
681 << "Error seeking to position " << _gpos << " in " << _filename << "\n";
682 return 0;
683 }
684
685 int result = ::read(_fd, start, length);
686 while (result < 0) {
687 if (errno == EAGAIN) {
688 thread_yield();
689 } else {
690 cerr
691 << "Error reading " << length << " bytes from " << _filename << "\n";
692 return 0;
693 }
694 result = ::read(_fd, start, length);
695 }
696
697 length = result;
698#endif // _WIN32
699
700 _gpos += length;
701 assert(_gpos >= 0);
702 return length;
703}
704
705/**
706 * Writes the indicated buffer directly to the file stream. Returns the
707 * number of characters written.
708 */
709size_t PandaFileStreamBuf::
710write_chars_raw(const char *start, size_t length) {
711 if (length == 0 || !_is_open) {
712 return 0;
713 }
714
715#ifdef _WIN32
716 // Windows case.
717 OVERLAPPED overlapped;
718 memset(&overlapped, 0, sizeof(overlapped));
719 LARGE_INTEGER ppos;
720 ppos.QuadPart = _ppos;
721 overlapped.Offset = ppos.LowPart;
722 overlapped.OffsetHigh = ppos.HighPart;
723
724 if (_open_mode & ios::app) {
725 overlapped.Offset = -1;
726 overlapped.OffsetHigh = -1;
727 }
728
729 DWORD bytes_written = 0;
730 BOOL success = WriteFile(_handle, start, length, &bytes_written, &overlapped);
731 int pass = 0;
732 while (!success) {
733 DWORD error = GetLastError();
734 if (error == ERROR_IO_INCOMPLETE || error == ERROR_IO_PENDING) {
735 // Wait for more later, but don't actually yield until we have made the
736 // first call to GetOverlappedResult(). (Apparently, Vista and Windows
737 // 7 *always* return ERROR_IO_INCOMPLETE after the first call to
738 // WriteFile.)
739 if (pass > 0) {
740 thread_yield();
741 }
742 } else if (error == ERROR_BROKEN_PIPE) {
743 // Broken pipe, we're done.
744 cerr << "Pipe closed on " << _filename << "\n";
745 return bytes_written;
746 } else {
747 cerr
748 << "Error writing " << length
749 << " bytes to " << _filename << ", windows error code 0x" << hex
750 << error << dec << ". Disk full?\n";
751 return bytes_written;
752 }
753 ++pass;
754 success = GetOverlappedResult(_handle, &overlapped, &bytes_written, false);
755 }
756 assert(bytes_written == length);
757 _ppos += bytes_written;
758 assert(_ppos >= 0);
759
760#else
761 // Posix case.
762 if (!(_open_mode & ios::app)) {
763 if (lseek(_fd, _ppos, SEEK_SET) == -1) {
764 cerr
765 << "Error seeking to position " << _ppos << " in " << _filename << "\n";
766 return 0;
767 }
768 }
769
770 size_t remaining = length;
771 while (remaining > 0) {
772 int result = ::write(_fd, start, remaining);
773 if (result < 0) {
774 if (errno == EAGAIN) {
775 thread_yield();
776 } else {
777 cerr
778 << "Error writing " << remaining << " bytes to " << _filename << "\n";
779 return length - remaining;
780 }
781 continue;
782 }
783
784 start += result;
785 remaining -= result;
786 _ppos += result;
787 assert(_ppos >= 0);
788 }
789#endif // _WIN32
790
791 return length;
792}
793
794/**
795 * Converts a buffer from universal newlines to \n.
796 *
797 * Returns the number of characters placed in dest. This may also set (or
798 * read) the value of _last_read_nl, which is preserved from call-to-call to
799 * deal with newline combinations that straddle a read operation.
800 */
801size_t PandaFileStreamBuf::
802decode_newlines(char *dest, size_t dest_length,
803 const char *source, size_t source_length) {
804 const char *p = source; // Read from p
805 char *q = dest; // Write to q
806
807 if (source_length == 0) {
808 // A special case: this is at end-of-file. Resolve the hanging newline.
809 switch (_last_read_nl) {
810 case '\n':
811 case '\r':
812 // Close the newline to grow on.
813 assert(q < dest + dest_length);
814 *q++ = '\n';
815 _last_read_nl = 0;
816 break;
817 default:
818 break;
819 }
820 }
821
822 while (p < source + source_length) {
823 assert(q < dest + dest_length);
824 switch (*p) {
825 case '\n':
826 switch (_last_read_nl) {
827 case '\r':
828 // \r\n is an MS-DOS newline.
829 *q++ = '\n';
830 _last_read_nl = 0;
831 break;
832 case '\n':
833 // \n\n means one Unix newline, and one more to grow on.
834 *q++ = '\n';
835 _last_read_nl = '\n';
836 break;
837 default:
838 // A lone \n is a newline to grow on.
839 _last_read_nl = '\n';
840 break;
841 }
842 break;
843
844 case '\r':
845 switch (_last_read_nl) {
846 case '\n':
847 // \n\r is an MS-DOS newline.
848 *q++ = '\n';
849 _last_read_nl = 0;
850 break;
851 case '\r':
852 // \r\r means one Apple newline, and one more to grow on.
853 *q++ = '\n';
854 _last_read_nl = '\r';
855 break;
856 default:
857 // A lone \r is a newline to grow on.
858 _last_read_nl = '\r';
859 break;
860 }
861 break;
862
863 default:
864 switch (_last_read_nl) {
865 case '\n':
866 case '\r':
867 // Close the newline to grow on.
868 *q++ = '\n';
869 _last_read_nl = 0;
870 break;
871 default:
872 break;
873 }
874 assert(q < dest + dest_length);
875 *q++ = *p;
876 }
877 ++p;
878 }
879
880 return q - dest;
881}
882
883/**
884 * Windows case: Converts a buffer from \n to \n\r.
885 *
886 * To allow for full buffer expansion, dest_length should be at least
887 * 2*source_length.
888 *
889 * Returns the number of characters placed in dest.
890 */
891size_t PandaFileStreamBuf::
892encode_newlines_msdos(char *dest, size_t dest_length,
893 const char *source, size_t source_length) {
894 const char *p = source; // Read from p
895 char *q = dest; // Write to q
896
897 while (p < source + source_length) {
898 assert(q < dest + dest_length);
899 switch (*p) {
900 case '\n':
901 *q++ = '\r';
902 assert(q < dest + dest_length);
903 *q++ = '\n';
904 break;
905
906 case '\r':
907 // Huh, shouldn't have one of these.
908 break;
909
910 default:
911 *q++ = *p;
912 break;
913 }
914
915 ++p;
916 }
917
918 return q - dest;
919}
920
921/**
922 * Unix case: Converts a buffer from \n to \n.
923 *
924 * This is, of course, no conversion at all; but we do strip out \r characters
925 * if they appear in the buffer; this will help programmers to realize when
926 * they have incorrectly tagged a binary file with text mode, even on a Posix
927 * environment.
928 *
929 * Returns the number of characters placed in dest.
930 */
931size_t PandaFileStreamBuf::
932encode_newlines_unix(char *dest, size_t dest_length,
933 const char *source, size_t source_length) {
934 const char *p = source; // Read from p
935 char *q = dest; // Write to q
936
937 while (p < source + source_length) {
938 assert(q < dest + dest_length);
939 switch (*p) {
940 case '\r':
941 break;
942
943 default:
944 *q++ = *p;
945 break;
946 }
947
948 ++p;
949 }
950
951 return q - dest;
952}
953
954/**
955 * Classic Mac case: Converts a buffer from \n to \r.
956 *
957 * Returns the number of characters placed in dest.
958 */
959size_t PandaFileStreamBuf::
960encode_newlines_mac(char *dest, size_t dest_length,
961 const char *source, size_t source_length) {
962 const char *p = source; // Read from p
963 char *q = dest; // Write to q
964
965 while (p < source + source_length) {
966 assert(q < dest + dest_length);
967 switch (*p) {
968 case '\n':
969 *q++ = '\r';
970 break;
971
972 case '\r':
973 break;
974
975 default:
976 *q++ = *p;
977 break;
978 }
979
980 ++p;
981 }
982
983 return q - dest;
984}
985
986ostream &
987operator << (ostream &out, PandaFileStreamBuf::NewlineMode newline_mode) {
988 switch (newline_mode) {
989 case PandaFileStreamBuf::NM_native:
990 return out << "native";
991
992 case PandaFileStreamBuf::NM_binary:
993 return out << "binary";
994
995 case PandaFileStreamBuf::NM_msdos:
996 return out << "msdos";
997
998 case PandaFileStreamBuf::NM_unix:
999 return out << "unix";
1000
1001 case PandaFileStreamBuf::NM_mac:
1002 return out << "mac";
1003 }
1004
1005 cerr
1006 << "Invalid NewlineMode value: " << (int)newline_mode << "\n";
1007 return out;
1008}
1009
1010istream &
1011operator >> (istream &in, PandaFileStreamBuf::NewlineMode &newline_mode) {
1012 string word;
1013 in >> word;
1014
1015 if (word == "native") {
1016 newline_mode = PandaFileStreamBuf::NM_native;
1017 } else if (word == "binary") {
1018 newline_mode = PandaFileStreamBuf::NM_binary;
1019 } else if (word == "msdos") {
1020 newline_mode = PandaFileStreamBuf::NM_msdos;
1021 } else if (word == "unix") {
1022 newline_mode = PandaFileStreamBuf::NM_unix;
1023 } else if (word == "mac") {
1024 newline_mode = PandaFileStreamBuf::NM_mac;
1025 } else {
1026 cerr
1027 << "Invalid NewlineMode value: " << word << "\n";
1028 newline_mode = PandaFileStreamBuf::NM_native;
1029 }
1030
1031 return in;
1032}
1033
1034#endif // USE_PANDAFILESTREAM
This class can be used to convert text between multiple representations, e.g.
Definition textEncoder.h:33
set_text
Changes the text that is stored in the encoder.
void set_encoding(Encoding encoding)
Specifies how the string set via set_text() is to be interpreted.
Definition textEncoder.I:48
const std::wstring & get_wtext() const
Returns the text associated with the TextEncoder, as a wide-character string.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.