Panda3D
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 
28 using std::cerr;
29 using std::dec;
30 using std::hex;
31 using std::ios;
32 using std::istream;
33 using std::ostream;
34 using std::streamoff;
35 using std::streampos;
36 using std::string;
37 
38 PandaFileStreamBuf::NewlineMode PandaFileStreamBuf::_newline_mode = NM_native;
39 
40 static const size_t file_buffer_size = 4096;
41 
42 /**
43  *
44  */
45 PandaFileStreamBuf::
46 PandaFileStreamBuf() {
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  */
84 PandaFileStreamBuf::
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  */
95 void PandaFileStreamBuf::
96 open(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;
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  */
197 void PandaFileStreamBuf::
198 attach(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  */
226 void PandaFileStreamBuf::
227 attach(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  */
250 bool PandaFileStreamBuf::
251 is_open() const {
252  return _is_open;
253 }
254 
255 /**
256  * Empties the buffer and closes the file.
257  */
258 void PandaFileStreamBuf::
259 close() {
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  */
288 streampos PandaFileStreamBuf::
289 seekoff(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) {
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  */
409 streampos PandaFileStreamBuf::
410 seekpos(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  */
418 int PandaFileStreamBuf::
419 overflow(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  */
452 int PandaFileStreamBuf::
453 sync() {
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  */
469 int PandaFileStreamBuf::
470 underflow() {
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  */
504 size_t PandaFileStreamBuf::
505 read_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  */
553 size_t PandaFileStreamBuf::
554 write_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  */
625 size_t PandaFileStreamBuf::
626 read_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  */
701 size_t PandaFileStreamBuf::
702 write_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  */
793 size_t PandaFileStreamBuf::
794 decode_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  */
883 size_t PandaFileStreamBuf::
884 encode_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  */
923 size_t PandaFileStreamBuf::
924 encode_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  */
951 size_t PandaFileStreamBuf::
952 encode_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 
978 ostream &
979 operator << (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 
1002 istream &
1003 operator >> (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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class can be used to convert text between multiple representations, e.g.
Definition: textEncoder.h:33
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
set_text
Changes the text that is stored in the encoder.
Definition: textEncoder.h:124
static TextEncoder::Encoding get_filesystem_encoding()
Specifies the default encoding to be used for all subsequent Filenames objects.
Definition: filename.I:639
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.
Definition: textEncoder.I:456