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 ::close(_fd);
271 }
272 _fd = -1;
273#endif // _WIN32
274
275 _is_open = false;
276 _open_mode = (ios::openmode)0;
277
278 _gpos = 0;
279 _ppos = 0;
280
281 pbump(pbase() - pptr());
282 gbump(egptr() - gptr());
283}
284
285/**
286 * Implements seeking within the stream.
287 */
288streampos PandaFileStreamBuf::
289seekoff(streamoff off, ios_seekdir dir, ios_openmode which) {
290 streampos result = -1;
291
292 if (!(_open_mode & ios::binary)) {
293 // Seeking on text files is only supported for seeks to the beginning of
294 // the file.
295 if (off != 0 || dir != ios::beg) {
296 return -1;
297 }
298
299 _last_read_nl = 0;
300 }
301
302 // Sync the iostream buffer first.
303 sync();
304
305 if (which & ios::in) {
306 // Determine the current file position.
307 size_t n = egptr() - gptr();
308 gbump(n);
309 _gpos -= n;
310 assert(_gpos >= 0);
311 streampos cur_pos = _gpos;
312 streampos new_pos = cur_pos;
313
314 // Now adjust the data pointer appropriately.
315 switch (dir) {
316 case ios::beg:
317 new_pos = (streampos)off;
318 break;
319
320 case ios::cur:
321 new_pos = (streampos)(cur_pos + off);
322 break;
323
324 case ios::end:
325#ifdef _WIN32
326 // Windows case.
327 {
328 LARGE_INTEGER li;
329 GetFileSizeEx(_handle, &li);
330 new_pos = (streampos)li.QuadPart + off;
331 }
332#else
333 // Posix case.
334 {
335 off_t li = lseek(_fd, off, SEEK_END);
336 if (li == (off_t)-1 || (sizeof(off_t) == 8 && li == 0x7fffffffffffffff)) {
337 return -1;
338 }
339 new_pos = (size_t)li;
340 }
341#endif // _WIN32
342 break;
343
344 default:
345 // Shouldn't get here.
346 break;
347 }
348
349 _gpos = new_pos;
350 assert(_gpos >= 0);
351 result = new_pos;
352 }
353
354 if (which & ios::out) {
355 // Determine the current file position.
356 size_t n = pptr() - pbase();
357 streampos cur_pos = _ppos + (streamoff)n;
358 streampos new_pos = cur_pos;
359
360 // Now adjust the data pointer appropriately.
361 switch (dir) {
362 case ios::beg:
363 new_pos = (streampos)off;
364 break;
365
366 case ios::cur:
367 new_pos = (streampos)(cur_pos + off);
368 break;
369
370 case ios::end:
371#ifdef _WIN32
372 // Windows case.
373 {
374 LARGE_INTEGER li;
375 GetFileSizeEx(_handle, &li);
376 new_pos = (streampos)li.QuadPart + off;
377 }
378#else
379 // Posix case.
380 new_pos = lseek(_fd, off, SEEK_END);
381 if (new_pos == (streampos)-1) {
382 return -1;
383 }
384#endif // _WIN32
385 break;
386
387 default:
388 // Shouldn't get here.
389 break;
390 }
391
392 _ppos = new_pos;
393 assert(_ppos >= 0);
394 result = new_pos;
395 }
396
397 return result;
398}
399
400/**
401 * A variant on seekoff() to implement seeking within a stream.
402 *
403 * The MSDN Library claims that it is only necessary to redefine seekoff(),
404 * and not seekpos() as well, as the default implementation of seekpos() is
405 * supposed to map to seekoff() exactly as I am doing here; but in fact it
406 * must do something else, because seeking didn't work on Windows until I
407 * redefined this function as well.
408 */
409streampos PandaFileStreamBuf::
410seekpos(streampos pos, ios_openmode which) {
411 return seekoff(pos, ios::beg, which);
412}
413
414/**
415 * Called by the system ostream implementation when its internal buffer is
416 * filled, plus one character.
417 */
418int PandaFileStreamBuf::
419overflow(int ch) {
420 bool okflag = true;
421
422 size_t n = pptr() - pbase();
423 if (n != 0) {
424 size_t wrote = write_chars(pbase(), n);
425 pbump(-(streamoff)wrote);
426 if (wrote != n) {
427 okflag = false;
428 }
429 }
430
431 if (okflag && ch != EOF) {
432 if (pptr() != epptr()) {
433 // Store the extra character back in the buffer.
434 *(pptr()) = ch;
435 pbump(1);
436 } else {
437 // No room to store ch.
438 okflag = false;
439 }
440 }
441
442 if (!okflag) {
443 return EOF;
444 }
445 return 0;
446}
447
448/**
449 * Called by the system iostream implementation to implement a flush
450 * operation.
451 */
452int PandaFileStreamBuf::
453sync() {
454 size_t n = pptr() - pbase();
455
456 size_t wrote = write_chars(pbase(), n);
457 pbump(-(streamoff)wrote);
458
459 if (n != wrote) {
460 return EOF;
461 }
462 return 0;
463}
464
465/**
466 * Called by the system istream implementation when its internal buffer needs
467 * more characters.
468 */
469int PandaFileStreamBuf::
470underflow() {
471 // Sometimes underflow() is called even if the buffer is not empty.
472 if (gptr() >= egptr()) {
473 sync();
474
475 // Mark the buffer filled (with buffer_size bytes).
476 size_t buffer_size = egptr() - eback();
477 gbump(-(streamoff)buffer_size);
478
479 size_t num_bytes = buffer_size;
480 size_t read_count = read_chars(gptr(), buffer_size);
481
482 if (read_count != num_bytes) {
483 // Oops, we didn't read what we thought we would.
484 if (read_count == 0) {
485 gbump(num_bytes);
486 return EOF;
487 }
488
489 // Slide what we did read to the top of the buffer.
490 assert(read_count < num_bytes);
491 size_t delta = num_bytes - read_count;
492 memmove(gptr() + delta, gptr(), read_count);
493 gbump(delta);
494 }
495 }
496
497 return (unsigned char)*gptr();
498}
499
500/**
501 * Attempts to extract the indicated number of characters from the current
502 * file position. Returns the number of characters extracted.
503 */
504size_t PandaFileStreamBuf::
505read_chars(char *start, size_t length) {
506 if (length == 0 || !_is_open) {
507 // Trivial no-op.
508 return 0;
509 }
510
511 // Make sure the write buffer is flushed.
512 sync();
513
514 if (_open_mode & ios::binary) {
515 // If the file is opened in binary mode, just read the data in the file.
516 return read_chars_raw(start, length);
517 }
518
519 // The file is opened in text mode. We have to decode newline characters in
520 // the file.
521 if (_newline_mode == NM_binary) {
522 // Unless we're configured to always use binary mode.
523 return read_chars_raw(start, length);
524 }
525
526 char *buffer = (char *)alloca(length);
527
528 size_t read_length;
529 size_t final_length;
530 do {
531 read_length = length - 1;
532 if (_last_read_nl != 0) {
533 // If we have a newline character to grow on, we might need to expand
534 // the buffer we read from the file by one character. In that case,
535 // read one character less to make room for it. (Otherwise, we are
536 // confident that the buffer will not expand when we decode the
537 // newlines.)
538 --read_length;
539 }
540 read_length = read_chars_raw(buffer, read_length);
541 final_length = decode_newlines(start, length, buffer, read_length);
542
543 // If we decoded all of the characters away, but we read nonzero
544 // characters, go back and get some more.
545 } while (read_length != 0 && final_length == 0);
546
547 return final_length;
548}
549
550/**
551 * Outputs the indicated stream of characters to the current file position.
552 */
553size_t PandaFileStreamBuf::
554write_chars(const char *start, size_t length) {
555 if (length == 0) {
556 // Trivial no-op.
557 return 0;
558 }
559
560 // Make sure the read buffer is flushed.
561 size_t n = egptr() - gptr();
562 gbump(n);
563 _gpos -= n;
564 assert(_gpos >= 0);
565
566 // Windows case.
567 if (_open_mode & ios::binary) {
568 // If the file is opened in binary mode, just write the data to the file.
569 return write_chars_raw(start, length);
570 }
571
572 // The file is opened in text mode. We have to encode newline characters to
573 // the file.
574
575 NewlineMode this_newline_mode = _newline_mode;
576 if (this_newline_mode == NM_native) {
577#ifdef _WIN32
578 this_newline_mode = NM_msdos;
579#else
580 // Even the Mac uses Unix-style EOL characters these days.
581 this_newline_mode = NM_unix;
582#endif
583 }
584
585 if (this_newline_mode == NM_binary) {
586 return write_chars_raw(start, length);
587 }
588
589 size_t buffer_length = length;
590 if (this_newline_mode == NM_msdos) {
591 // Windows requires a larger buffer here, since we are writing two newline
592 // characters for every one.
593 buffer_length *= 2;
594 }
595 char *buffer = (char *)alloca(buffer_length);
596
597 size_t write_length;
598 switch (this_newline_mode) {
599 case NM_msdos:
600 write_length = encode_newlines_msdos(buffer, buffer_length, start, length);
601 break;
602
603 case NM_mac:
604 write_length = encode_newlines_mac(buffer, buffer_length, start, length);
605 break;
606
607 default:
608 write_length = encode_newlines_unix(buffer, buffer_length, start, length);
609 break;
610 }
611
612 if (write_length == write_chars_raw(buffer, write_length)) {
613 // Success. Return the number of original characters.
614 return length;
615 }
616
617 // Error. Pretend we wrote nothing.
618 return 0;
619}
620
621/**
622 * Reads raw data from the file directly into the indicated buffer. Returns
623 * the number of characters read.
624 */
625size_t PandaFileStreamBuf::
626read_chars_raw(char *start, size_t length) {
627 if (length == 0) {
628 return 0;
629 }
630
631#ifdef _WIN32
632 // Windows case.
633 OVERLAPPED overlapped;
634 memset(&overlapped, 0, sizeof(overlapped));
635 LARGE_INTEGER gpos;
636 gpos.QuadPart = _gpos;
637 overlapped.Offset = gpos.LowPart;
638 overlapped.OffsetHigh = gpos.HighPart;
639
640 DWORD bytes_read = 0;
641 BOOL success = ReadFile(_handle, start, length, &bytes_read, &overlapped);
642 int pass = 0;
643 while (!success) {
644 DWORD error = GetLastError();
645 if (error == ERROR_IO_INCOMPLETE || error == ERROR_IO_PENDING) {
646 // Wait for more later, but don't actually yield until we have made the
647 // first call to GetOverlappedResult(). (Apparently, Vista and Windows
648 // 7 *always* return ERROR_IO_INCOMPLETE after the first call to
649 // ReadFile.)
650 if (pass > 0) {
651 thread_yield();
652 }
653 } else if (error == ERROR_HANDLE_EOF || error == ERROR_BROKEN_PIPE) {
654 // End-of-file, normal result.
655 break;
656 } else {
657 cerr
658 << "Error reading " << length
659 << " bytes from " << _filename << ", windows error code 0x" << hex
660 << error << dec << ".\n";
661 return 0;
662 }
663 ++pass;
664 success = GetOverlappedResult(_handle, &overlapped, &bytes_read, false);
665 }
666
667 length = bytes_read;
668
669#else
670 // Posix case.
671 if (lseek(_fd, _gpos, SEEK_SET) == -1) {
672 cerr
673 << "Error seeking to position " << _gpos << " in " << _filename << "\n";
674 return 0;
675 }
676
677 int result = ::read(_fd, start, length);
678 while (result < 0) {
679 if (errno == EAGAIN) {
680 thread_yield();
681 } else {
682 cerr
683 << "Error reading " << length << " bytes from " << _filename << "\n";
684 return 0;
685 }
686 result = ::read(_fd, start, length);
687 }
688
689 length = result;
690#endif // _WIN32
691
692 _gpos += length;
693 assert(_gpos >= 0);
694 return length;
695}
696
697/**
698 * Writes the indicated buffer directly to the file stream. Returns the
699 * number of characters written.
700 */
701size_t PandaFileStreamBuf::
702write_chars_raw(const char *start, size_t length) {
703 if (length == 0 || !_is_open) {
704 return 0;
705 }
706
707#ifdef _WIN32
708 // Windows case.
709 OVERLAPPED overlapped;
710 memset(&overlapped, 0, sizeof(overlapped));
711 LARGE_INTEGER ppos;
712 ppos.QuadPart = _ppos;
713 overlapped.Offset = ppos.LowPart;
714 overlapped.OffsetHigh = ppos.HighPart;
715
716 if (_open_mode & ios::app) {
717 overlapped.Offset = -1;
718 overlapped.OffsetHigh = -1;
719 }
720
721 DWORD bytes_written = 0;
722 BOOL success = WriteFile(_handle, start, length, &bytes_written, &overlapped);
723 int pass = 0;
724 while (!success) {
725 DWORD error = GetLastError();
726 if (error == ERROR_IO_INCOMPLETE || error == ERROR_IO_PENDING) {
727 // Wait for more later, but don't actually yield until we have made the
728 // first call to GetOverlappedResult(). (Apparently, Vista and Windows
729 // 7 *always* return ERROR_IO_INCOMPLETE after the first call to
730 // WriteFile.)
731 if (pass > 0) {
732 thread_yield();
733 }
734 } else if (error == ERROR_BROKEN_PIPE) {
735 // Broken pipe, we're done.
736 cerr << "Pipe closed on " << _filename << "\n";
737 return bytes_written;
738 } else {
739 cerr
740 << "Error writing " << length
741 << " bytes to " << _filename << ", windows error code 0x" << hex
742 << error << dec << ". Disk full?\n";
743 return bytes_written;
744 }
745 ++pass;
746 success = GetOverlappedResult(_handle, &overlapped, &bytes_written, false);
747 }
748 assert(bytes_written == length);
749 _ppos += bytes_written;
750 assert(_ppos >= 0);
751
752#else
753 // Posix case.
754 if (!(_open_mode & ios::app)) {
755 if (lseek(_fd, _ppos, SEEK_SET) == -1) {
756 cerr
757 << "Error seeking to position " << _ppos << " in " << _filename << "\n";
758 return 0;
759 }
760 }
761
762 size_t remaining = length;
763 while (remaining > 0) {
764 int result = ::write(_fd, start, remaining);
765 if (result < 0) {
766 if (errno == EAGAIN) {
767 thread_yield();
768 } else {
769 cerr
770 << "Error writing " << remaining << " bytes to " << _filename << "\n";
771 return length - remaining;
772 }
773 continue;
774 }
775
776 start += result;
777 remaining -= result;
778 _ppos += result;
779 assert(_ppos >= 0);
780 }
781#endif // _WIN32
782
783 return length;
784}
785
786/**
787 * Converts a buffer from universal newlines to \n.
788 *
789 * Returns the number of characters placed in dest. This may also set (or
790 * read) the value of _last_read_nl, which is preserved from call-to-call to
791 * deal with newline combinations that straddle a read operation.
792 */
793size_t PandaFileStreamBuf::
794decode_newlines(char *dest, size_t dest_length,
795 const char *source, size_t source_length) {
796 const char *p = source; // Read from p
797 char *q = dest; // Write to q
798
799 if (source_length == 0) {
800 // A special case: this is at end-of-file. Resolve the hanging newline.
801 switch (_last_read_nl) {
802 case '\n':
803 case '\r':
804 // Close the newline to grow on.
805 assert(q < dest + dest_length);
806 *q++ = '\n';
807 _last_read_nl = 0;
808 break;
809 default:
810 break;
811 }
812 }
813
814 while (p < source + source_length) {
815 assert(q < dest + dest_length);
816 switch (*p) {
817 case '\n':
818 switch (_last_read_nl) {
819 case '\r':
820 // \r\n is an MS-DOS newline.
821 *q++ = '\n';
822 _last_read_nl = 0;
823 break;
824 case '\n':
825 // \n\n means one Unix newline, and one more to grow on.
826 *q++ = '\n';
827 _last_read_nl = '\n';
828 break;
829 default:
830 // A lone \n is a newline to grow on.
831 _last_read_nl = '\n';
832 break;
833 }
834 break;
835
836 case '\r':
837 switch (_last_read_nl) {
838 case '\n':
839 // \n\r is an MS-DOS newline.
840 *q++ = '\n';
841 _last_read_nl = 0;
842 break;
843 case '\r':
844 // \r\r means one Apple newline, and one more to grow on.
845 *q++ = '\n';
846 _last_read_nl = '\r';
847 break;
848 default:
849 // A lone \r is a newline to grow on.
850 _last_read_nl = '\r';
851 break;
852 }
853 break;
854
855 default:
856 switch (_last_read_nl) {
857 case '\n':
858 case '\r':
859 // Close the newline to grow on.
860 *q++ = '\n';
861 _last_read_nl = 0;
862 break;
863 default:
864 break;
865 }
866 assert(q < dest + dest_length);
867 *q++ = *p;
868 }
869 ++p;
870 }
871
872 return q - dest;
873}
874
875/**
876 * Windows case: Converts a buffer from \n to \n\r.
877 *
878 * To allow for full buffer expansion, dest_length should be at least
879 * 2*source_length.
880 *
881 * Returns the number of characters placed in dest.
882 */
883size_t PandaFileStreamBuf::
884encode_newlines_msdos(char *dest, size_t dest_length,
885 const char *source, size_t source_length) {
886 const char *p = source; // Read from p
887 char *q = dest; // Write to q
888
889 while (p < source + source_length) {
890 assert(q < dest + dest_length);
891 switch (*p) {
892 case '\n':
893 *q++ = '\r';
894 assert(q < dest + dest_length);
895 *q++ = '\n';
896 break;
897
898 case '\r':
899 // Huh, shouldn't have one of these.
900 break;
901
902 default:
903 *q++ = *p;
904 break;
905 }
906
907 ++p;
908 }
909
910 return q - dest;
911}
912
913/**
914 * Unix case: Converts a buffer from \n to \n.
915 *
916 * This is, of course, no conversion at all; but we do strip out \r characters
917 * if they appear in the buffer; this will help programmers to realize when
918 * they have incorrectly tagged a binary file with text mode, even on a Posix
919 * environment.
920 *
921 * Returns the number of characters placed in dest.
922 */
923size_t PandaFileStreamBuf::
924encode_newlines_unix(char *dest, size_t dest_length,
925 const char *source, size_t source_length) {
926 const char *p = source; // Read from p
927 char *q = dest; // Write to q
928
929 while (p < source + source_length) {
930 assert(q < dest + dest_length);
931 switch (*p) {
932 case '\r':
933 break;
934
935 default:
936 *q++ = *p;
937 break;
938 }
939
940 ++p;
941 }
942
943 return q - dest;
944}
945
946/**
947 * Classic Mac case: Converts a buffer from \n to \r.
948 *
949 * Returns the number of characters placed in dest.
950 */
951size_t PandaFileStreamBuf::
952encode_newlines_mac(char *dest, size_t dest_length,
953 const char *source, size_t source_length) {
954 const char *p = source; // Read from p
955 char *q = dest; // Write to q
956
957 while (p < source + source_length) {
958 assert(q < dest + dest_length);
959 switch (*p) {
960 case '\n':
961 *q++ = '\r';
962 break;
963
964 case '\r':
965 break;
966
967 default:
968 *q++ = *p;
969 break;
970 }
971
972 ++p;
973 }
974
975 return q - dest;
976}
977
978ostream &
979operator << (ostream &out, PandaFileStreamBuf::NewlineMode newline_mode) {
980 switch (newline_mode) {
981 case PandaFileStreamBuf::NM_native:
982 return out << "native";
983
984 case PandaFileStreamBuf::NM_binary:
985 return out << "binary";
986
987 case PandaFileStreamBuf::NM_msdos:
988 return out << "msdos";
989
990 case PandaFileStreamBuf::NM_unix:
991 return out << "unix";
992
993 case PandaFileStreamBuf::NM_mac:
994 return out << "mac";
995 }
996
997 cerr
998 << "Invalid NewlineMode value: " << (int)newline_mode << "\n";
999 return out;
1000}
1001
1002istream &
1003operator >> (istream &in, PandaFileStreamBuf::NewlineMode &newline_mode) {
1004 string word;
1005 in >> word;
1006
1007 if (word == "native") {
1008 newline_mode = PandaFileStreamBuf::NM_native;
1009 } else if (word == "binary") {
1010 newline_mode = PandaFileStreamBuf::NM_binary;
1011 } else if (word == "msdos") {
1012 newline_mode = PandaFileStreamBuf::NM_msdos;
1013 } else if (word == "unix") {
1014 newline_mode = PandaFileStreamBuf::NM_unix;
1015 } else if (word == "mac") {
1016 newline_mode = PandaFileStreamBuf::NM_mac;
1017 } else {
1018 cerr
1019 << "Invalid NewlineMode value: " << word << "\n";
1020 newline_mode = PandaFileStreamBuf::NM_native;
1021 }
1022
1023 return in;
1024}
1025
1026#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.