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 || (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  */
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
static TextEncoder::Encoding get_filesystem_encoding()
Specifies the default encoding to be used for all subsequent Filenames objects.
Definition: filename.I:639
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.
Definition: textEncoder.h:124
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
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.