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