Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
27.filebuf.cpp
Go to the documentation of this file.
1
29#include <cerrno> // for errno
30#include <csignal> // for signal()
31#include <cstdio> // for FILE, fopen(), remove()
32#include <cstring> // for memcmp()
33#include <cwchar> // for mbstate_t
34
35#include <fstream>
36
37#ifndef _WIN32
38
39# ifdef __SUNPRO_CC
40 // working around a SunOS/SunPro bug (PR #26255)
41# undef _TIME_T
42# endif
43
44# include <cstdlib> // for exit()
45# include <fcntl.h> // for open(), O_XXX constants
46# include <unistd.h> // for fork()
47# include <sys/stat.h> // for mkfifo()
48# include <sys/types.h> // for pid_t
49# include <sys/wait.h> // for wait()
50#else
51# include <fcntl.h> // for O_XXX constants
52# include <io.h> // for open()
53#endif // _WIN32
54
55#ifndef SIGPIPE
56# define SIGPIPE 13 /* HP-UX, Linux, and SunOS value */
57#endif // SIGPIPE
58
59#include <rw_driver.h>
60#include <rw_file.h>
61#include <rw_valcmp.h>
62
63
64#define REMOVE_FILE(tmpfname) \
65 rw_warn (0 == std::remove (tmpfname), __FILE__, __LINE__, \
66 "std::remove(\"%s\") failed: %m", tmpfname)
67
68
71template <class charT>
72static void
73test_ctors (const char* tname)
74{
75 typedef std::basic_filebuf<charT, std::char_traits<charT> > Filebuf;
76
77 int fdcount [2];
78 int next_fd [2];
79
81 rw_info (0, __FILE__, __LINE__,
82 "std::basic_filebuf<%s>::basic_filebuf()", tname);
83
84 next_fd [0] = rw_nextfd (fdcount + 0);
85
86 {
87 Filebuf fb;
88
89 // verify the postcondition in 27.8.1.2, p2
90 rw_assert (!fb.is_open (), __FILE__, __LINE__,
91 "basic_filebuf<%s>::basic_filebuf().is_open() == "
92 "false, got true", tname);
93
94 // verify that no file descriptor has been allocated
95 next_fd [1] = rw_nextfd (fdcount + 1);
96
97 rw_assert (next_fd [0] == next_fd [1] && fdcount [0] == fdcount [1],
98 __FILE__, __LINE__,
99 "%d file descriptor leak(s) detected after construction",
100 fdcount [1] - fdcount [0]);
101 }
102
103 // verify that no file descriptor has been closed
104 next_fd [1] = rw_nextfd (fdcount + 1);
105
107 rw_info (0, __FILE__, __LINE__,
108 "std::basic_filebuf<%s>::~basic_filebuf()", tname);
109
110 rw_assert (next_fd [0] == next_fd [1] && fdcount [0] == fdcount [1],
111 __FILE__, __LINE__,
112 "%d file descriptor leak(s) detected after destruction",
113 fdcount [1] - fdcount [0]);
114
115
116 const char* const tmpfname = rw_tmpnam (0);
117 if (!tmpfname) {
118 return;
119 }
120
121 {
122 Filebuf fb;
123
124 next_fd [0] = rw_nextfd (fdcount + 0);
125
126 Filebuf *tmp = fb.open (tmpfname, std::ios::out);
127
128 // verify that open() succeeds and allocates a file descriptor
129 rw_assert (0 != tmp, __FILE__, __LINE__,
130 "basic_filebuf<%s>::open(\"%s\", std::ios_base::out) "
131 "failed", tname, tmpfname);
132
133 next_fd [1] = rw_nextfd (fdcount + 1);
134
135 rw_assert (next_fd [0] != next_fd [1]
136 && fdcount [0] + 1 == fdcount [1], __FILE__, __LINE__,
137 "%d file descriptor mismatch detected after open()",
138 fdcount [1] - fdcount [0]);
139 }
140
141 // verify that dtor closes the file descriptor
142 next_fd [1] = rw_nextfd (fdcount + 1);
143
144 rw_assert (next_fd [0] == next_fd [1] && fdcount [0] == fdcount [1],
145 __FILE__, __LINE__,
146 "%d file descriptor leak(s) detected after destruction",
147 fdcount [1] - fdcount [0]);
148
149
150#ifndef _RWSTD_NO_EXT_FILEBUF
151
153 rw_info (0, __FILE__, __LINE__,
154 "std::basic_filebuf<%s>::basic_filebuf(FILE*) "
155 "[extension]", tname);
156
157# ifdef stdin
158
159 {
160 std::FILE* const fp = std::fopen (tmpfname, "w");
161
162 next_fd [0] = rw_nextfd (fdcount + 0);
163
164 // object takes over the ownership of the file pointer
165 Filebuf fb (fp);
166
167 rw_assert (fb.is_open (), __FILE__, __LINE__,
168 "basic_filebuf<%s>::basic_filebuf (FILE*).is_open() "
169 "== true, got false", tname);
170
171 next_fd [1] = rw_nextfd (fdcount + 1);
172
173 rw_assert (next_fd [0] == next_fd [1] && fdcount [0] == fdcount [1],
174 __FILE__, __LINE__,
175 "%d file descriptor mismatch detected after open()",
176 fdcount [1] - fdcount [0]);
177 }
178
179 // verify that dtor closes the file descriptor
180 next_fd [1] = rw_nextfd (fdcount + 1);
181
182 rw_assert (fdcount [0] == fdcount [1] + 1, __FILE__, __LINE__,
183 "%d file descriptor leak(s) detected after destruction",
184 fdcount [1] - fdcount [0]);
185
186# else // if !defined (stdin)
187
188 rw_assert (false, __FILE__, __LINE__,
189 "macro stdin unexpectedly not #defined, "
190 "basic_filebuf<%s>::basic_filebuf (FILE*) "
191 "extension not tested", tname);
192
193# endif // stdin
194
196 rw_info (0, __FILE__, __LINE__,
197 "std::basic_filebuf<%s>::basic_filebuf(int) "
198 "[extension]", tname);
199
200 Filebuf *pfb = 0;
201
202 {
203 std::FILE* const fp = std::fopen (tmpfname, "w");
204
205 // object takes over the ownership of the file pointer
206 pfb = new Filebuf (fp);
207
208 next_fd [0] = rw_nextfd (fdcount + 0);
209
210 // object takes over the ownership of the file descriptor
211 Filebuf fb (pfb->fd ());
212
213 next_fd [1] = rw_nextfd (fdcount + 1);
214
215 rw_assert (fb.is_open (), __FILE__, __LINE__,
216 "basic_filebuf<%s>::basic_filebuf (int).is_open() "
217 "== true, got false", tname);
218
219 rw_assert (next_fd [0] == next_fd [1] && fdcount [0] == fdcount [1],
220 __FILE__, __LINE__,
221 "%d file descriptor mismatch detected after open()",
222 fdcount [1] - fdcount [0]);
223 }
224
225 // verify that dtor closes the file descriptor
226 next_fd [1] = rw_nextfd (fdcount + 1);
227
228 rw_assert (fdcount [0] == fdcount [1] + 1, __FILE__, __LINE__,
229 "%d file descriptor leak(s) detected after destruction",
230 fdcount [1] - fdcount [0]);
231
232 delete pfb;
233
234#endif // _RWSTD_NO_EXT_FILEBUF
235
236 REMOVE_FILE (tmpfname);
237}
238
239
241extern "C" {
242
243// instead of calling signal(SIGPIPE, SIG_IGN)
245{
246 std::signal (SIGPIPE, ignore_signal);
247}
248
249} // extern "C"
250
251
252template <class charT>
253static void
254test_open (const char* tname)
255{
256 typedef std::basic_filebuf<charT, std::char_traits<charT> > Filebuf;
257
258 const char* const tmpfname = rw_tmpnam (0);
259
260 if (!tmpfname) {
261 return;
262 }
263
264 rw_info (0, __FILE__, __LINE__,
265 "std::basic_filebuf<%s>::is_open ()", tname);
266
267 rw_info (0, __FILE__, __LINE__,
268 "std::basic_filebuf<%s>::open (const char*, "
269 "ios_base::openmode)", tname);
270
271
318#define BEGIN_MODE(openmode, ext) \
319 mode = (openmode), \
320 rw_info (0, __FILE__, __LINE__, "%{Io} %s", mode, ext)
321
322#define ASSERT_OPEN(expr, mode, txt) \
323 rw_assert (expr, __FILE__, __LINE__, \
324 "basic_filebuf<%s>::open (\"%s\", %{Io}) %s ", \
325 tname, tmpfname, mode, txt) \
326
327 std::ios_base::openmode mode;
328
329 const std::ios::openmode iomodes[] = {
330 std::ios::openmode ()
331
332#ifndef _RWSTD_NO_EXT_STDIO
333
334 , std::ios::stdio
335
336#endif // _RWSTD_NO_EXT_STDIO
337
338 };
339
340 const std::size_t niomodes = sizeof iomodes / sizeof *iomodes;
341
342 const char* buf = 0;
343 std::size_t size = 0;
344
346 // exercise open (..., ios::out)
347
348 for (std::size_t minx = 0; minx != niomodes; ++minx) {
349
350 BEGIN_MODE (std::ios::out | iomodes [minx], "");
351
352 {
353 Filebuf ().open (tmpfname, mode);
354 }
355
356 buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
357
358 ASSERT_OPEN (0 != buf, mode, "failed to create a new file");
359
360 if (buf)
362 "unexpectedly created a non-empty file");
363
364 rw_fwrite (tmpfname, "foobar");
365
366 {
367 Filebuf ().open (tmpfname, mode);
368 }
369
370 buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
371
372 ASSERT_OPEN (0 != buf, mode, "failed to open an existing file");
373
374 if (buf)
376 "unexpectedly created a non-empty file");
377 }
378
380 // exercise open (..., ios::out | ios::app)
381
382 for (std::size_t minx = 0; minx != niomodes; ++minx) {
383
384 BEGIN_MODE (std::ios::out | std::ios::app | iomodes [minx], "");
385
386 REMOVE_FILE (tmpfname);
387
388 {
389 Filebuf ().open (tmpfname, mode);
390 }
391
392 buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
393
394 ASSERT_OPEN (0 != buf, mode, "failed to create a new file");
395
396 if (buf)
397 ASSERT_OPEN (!size, mode, "unexpectedly created a non-empty file");
398
399 rw_fwrite (tmpfname, "foobar");
400
401 {
402 Filebuf ().open (tmpfname, mode);
403 }
404
405 buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
406
407 ASSERT_OPEN (0 != buf, mode, "failed to open an existing file");
408
409 if (buf)
410 ASSERT_OPEN (6 == size, mode,
411 "unexpectedly truncated a non-empty file");
412 }
413
415 // exercise open (..., ios::out | ios::trunc)
416
417 for (std::size_t minx = 0; minx != niomodes; ++minx) {
418
419 BEGIN_MODE (std::ios::out | std::ios::trunc | iomodes [minx], "");
420
421 rw_fwrite (tmpfname, "foobar");
422
423 {
424 // open a file for writing and truncate it to 0 size
425 Filebuf ().open (tmpfname, mode);
426 }
427
428 // read the contents of the file and verify that they are empty
429 buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
430
431 // file must exist
432 ASSERT_OPEN (0 != buf, mode, "failed to open an existing file");
433
434 // the size of the file must be 0
435 if (buf)
436 ASSERT_OPEN (!size, mode, "failed to truncate a non-empty file");
437 }
438
439
441 // exercise open (..., ios::nocreate)
442
443 for (std::size_t minx = 0; minx != niomodes; ++minx) {
444
445 // ios::nocreate is meaningless by itself
446 BEGIN_MODE (std::ios::nocreate | iomodes [minx], "[extension]");
447
448 Filebuf fb;
449
450 fb.open (tmpfname, mode);
451
452 // verify that the call failed
453 ASSERT_OPEN (!fb.is_open (), mode,
454 "unexpectedly succeeded to open a non-existent file");
455 }
456
458 // exercise open (..., ios::out | ios::nocreate)
459
460 BEGIN_MODE (std::ios::out | std::ios::nocreate, "[extension]");
461
462 // ios::nocreate will prevent the call to open from creating
463 // the file if it doesn't exist
464
465 REMOVE_FILE (tmpfname);
466
467 {
468 Filebuf fb;
469
470 // try to open a file that doesn't exist for output but prevent
471 // the call from creating it by setting the nocreate bit
472 fb.open (tmpfname, mode);
473
474 // verify that the call failed
475 ASSERT_OPEN (!fb.is_open (), mode,
476 "unexpectedly succeeded to open a non-existent file");
477 }
478
479 buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
480
481 ASSERT_OPEN (0 == buf, mode, "unexpectedly created a file");
482
483 // the same as above but with an existing file
484 rw_fwrite (tmpfname, "foobar");
485
486 {
487 Filebuf fb;
488
489 // try to open an existing file
490 fb.open (tmpfname, mode);
491
492 // verify that the call succeeded
493 ASSERT_OPEN (fb.is_open (), mode,
494 "unexpectedly succeeded to open an existing file");
495 }
496
497 buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
498
499 ASSERT_OPEN (0 != buf, mode, "failed to open an existing file");
500
501#ifndef _RWSTD_NO_EXT_STDIO
502
504 // exercise open (..., ios::stdio| ios::out | ios::nocreate)
505
506 BEGIN_MODE (std::ios::stdio | std::ios::out | std::ios::nocreate,
507 "[extension]");
508
509 REMOVE_FILE (tmpfname);
510
511 // ios::nocreate can't be implemented with ios::stdio
512 // and the call must fail
513 {
514 Filebuf fb;
515
516 fb.open (tmpfname, mode);
517
518 // verify that the call failed
519 ASSERT_OPEN (!fb.is_open (), mode,
520 "unexpectedly succeeded to open a non-existent file");
521 }
522
523 // verify that the file wasn't created
524 buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
525
526 ASSERT_OPEN (0 == buf, mode, "unexpectedly created a file");
527
528#endif // _RWSTD_NO_EXT_STDIO
529
531 // exercise open (..., ios::out | ios::nocreate | ios::trunc)
532
533 // ios::trunc has no effect on ios::nocreate
534 BEGIN_MODE (std::ios::out | std::ios::nocreate | std::ios::trunc,
535 "[extension]");
536
537 // remove file in case it was unexpectedly created above
538 std::remove (tmpfname);
539
540 {
541 Filebuf fb;
542
543 // try to open a file that doesn't exist for output but prevent
544 // the call from creating it by setting the nocreate bit
545 fb.open (tmpfname, mode);
546
547 // verify that the call failed
548 ASSERT_OPEN (!fb.is_open (), mode,
549 "unexpectedly succeeded to open a non-existent file");
550 }
551
552 buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
553
554 ASSERT_OPEN (0 == buf, mode, "unexpectedly created a file");
555
557 // exercise open (..., ios::noreplace)
558
559 // remove file in case it was unexpectedly created above
560 std::remove (tmpfname);
561
562 for (std::size_t minx = 0; minx != niomodes; ++minx) {
563
564 // ios::noreplace is meaningless by itself
565 BEGIN_MODE (std::ios::noreplace | iomodes [minx], "[extension]");
566
567 Filebuf fb;
568
569 fb.open (tmpfname, mode);
570
571 // verify that the call failed
572 ASSERT_OPEN (!fb.is_open (), mode,
573 "unexpectedly succeeded to open a non-existent file");
574 }
575
577 // exercise open (..., ios::out | ios::noreplace)
578
579 // ios::noreplace will cause the call to open to succeed only
580 // if the file doesn't exist, otherwise the call will fail
581 BEGIN_MODE (std::ios::out | std::ios::noreplace, "[extension]");
582
583 rw_fwrite (tmpfname, "foobar");
584
585 {
586 Filebuf fb;
587
588 // try to open an existing file for output but prevent
589 // the call from replacing it by setting the noreplace bit
590 fb.open (tmpfname, mode);
591
592 // verify that the call failed
593 ASSERT_OPEN (!fb.is_open (), mode,
594 "unexpectedly succeeded to open an existing file");
595 }
596
597 buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
598
599 ASSERT_OPEN (0 != buf, mode, "unexpectedly removed an existing file");
600
601 if (buf)
602 ASSERT_OPEN (0 != size, mode, "unexpectdly truncated a non-empty file");
603
605 // exercise open (..., ios::out | ios::trunc | ios::noreplace)
606
607 BEGIN_MODE (std::ios::out | std::ios::trunc | std::ios::noreplace,
608 "[extension]");
609
610 rw_fwrite (tmpfname, "foobar");
611
612 {
613 Filebuf fb;
614
615 // try to open an existing file for output
616 fb.open (tmpfname, mode);
617
618 // verify that the call failed
619 ASSERT_OPEN (!fb.is_open (), mode,
620 "failed to open an existing file");
621 }
622
623 buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size));
624
625 ASSERT_OPEN (0 != buf, mode, "unexpectedly removed an existing file");
626
627 if (buf)
628 ASSERT_OPEN (0 != size, mode, "failed to truncate a non-empty file");
629
630
632 // exercise open (..., ios::in)
633
634 charT readbuf [256];
635 const std::size_t readbuf_size = sizeof readbuf / sizeof *readbuf;
636
637 for (std::size_t minx = 0; minx != niomodes; ++minx) {
638
639 BEGIN_MODE (std::ios::in | iomodes [minx], "");
640
641 rw_fwrite (tmpfname, "foobar");
642
643 Filebuf fb;
644
645 const charT foobar[] = { 'f', 'o', 'o', 'b', 'a', 'r' };
646
647 if (fb.open (tmpfname, mode)) {
648
649 rw_assert (fb.is_open (), __FILE__, __LINE__,
650 "basic_filebuf<%s>::is_open()", tname);
651
652 std::memset (readbuf, 0, sizeof readbuf);
653 fb.sgetn (readbuf, readbuf_size);
654
655 ASSERT_OPEN (!std::memcmp (readbuf, foobar, sizeof foobar),
656 mode, "failed to open an existing file for reading");
657 }
658 else {
659 ASSERT_OPEN (0, mode, "failed to open an existing file");
660 }
661 }
662
664 // exercise open (..., ios::in | ios::out)
665
666 for (std::size_t minx = 0; minx != niomodes; ++minx) {
667
668 BEGIN_MODE (std::ios::in | std::ios::out | iomodes [minx], "");
669
670 rw_fwrite (tmpfname, "foobar");
671
672 Filebuf fb;
673
674 if (fb.open (tmpfname, mode)) {
675
676 rw_assert (fb.is_open (), __FILE__, __LINE__,
677 "basic_filebuf<%s>::is_open()", tname);
678
679 const charT FOO[] = { 'F', 'O', 'O' };
680 const charT bar[] = { 'b', 'a', 'r' };
681
682 if (3 != fb.sputn (FOO, sizeof FOO / sizeof *FOO))
683 ASSERT_OPEN (false, mode,
684 "failed to open an existing file for writing "
685 "(sputn() failed)");
686
687 // an input operation must be separated from a
688 // preceding output operation by a seek() or flush()
689 fb.pubsync ();
690
691 std::memset (readbuf, 0, sizeof readbuf);
692 const std::streamsize got = fb.sgetn (readbuf, readbuf_size);
693
694 if (3 == got) {
695 rw_assert (!std::memcmp (readbuf, bar, sizeof bar),
696 __FILE__, __LINE__,
697 "sgetn() retrieved the wrong data)");
698 }
699 else
700 rw_assert (false, __FILE__, __LINE__,
701 "sgetn() == 3, got %td: %{*.*Ac}, "
702 "expected %{*.*Ac}",
703 got, int (sizeof (charT)), got, readbuf,
704 int (sizeof (charT)),
705 int (sizeof bar / sizeof *bar), bar);
706 }
707 else {
708 ASSERT_OPEN (0, mode, "failed to open an existing file");
709 }
710 }
711
712
714 // exercise open (..., ios::in | ios::out | ios::trunc)
715
716 for (std::size_t minx = 0; minx != niomodes; ++minx) {
717
718 BEGIN_MODE ( std::ios::in | std::ios::out | std::ios::trunc
719 | iomodes [minx], "");
720
721 rw_fwrite (tmpfname, "foobar");
722
723 Filebuf fb;
724
725 if (fb.open (tmpfname, mode)) {
726
727 rw_assert (fb.is_open (), __FILE__, __LINE__,
728 "basic_filebuf<%s>::is_open()", tname);
729
730 const charT FOO[] = { 'F', 'O', 'O' };
731
732 std::memset (readbuf, 0, sizeof readbuf);
733 if (0 != fb.sgetn (readbuf, readbuf_size))
734 ASSERT_OPEN (false, mode,
735 "failed to truncate a non-empty file");
736
737 if (3 != fb.sputn (FOO, sizeof FOO / sizeof *FOO))
738 ASSERT_OPEN (false, mode,
739 "failed to open an existing file for writing "
740 "(sputn() failed)");
741
742 // an input operation must be separated from a
743 // preceding output operation by a seek() or flush()
744 fb.pubseekoff (0, std::ios::beg);
745
746 std::memset (readbuf, 0, sizeof readbuf);
747 if (3 != fb.sgetn (readbuf, readbuf_size))
748 ASSERT_OPEN (false, mode,
749 "failed to open an existing file for reading "
750 "(sgetn() failed after a sputn())");
751
752 ASSERT_OPEN (!std::memcmp (readbuf, FOO, sizeof FOO), mode,
753 "failed to open an existing file for reading "
754 "and writing (sgetn() retrieved the wrong data)");
755 }
756 else {
757 ASSERT_OPEN (0, mode, "failed to open an existing file");
758 }
759 }
760
761
763 // exercise open (..., ios::binary | ios::in)
764
765 for (std::size_t minx = 0; minx != niomodes; ++minx) {
766
767 BEGIN_MODE (std::ios::binary | std::ios::in | iomodes [minx], "");
768
769 // create a file containing various combinations of new-line
770 // and line-feed and read the file in using the ios::binary
771 // flag, expecting the original contents of the file on
772 // output (i.e., no CR/LF conversion on Win32)
773 const char bindata[] = "\nf\ro\n\ro\r\n\b\n\ra\r\rr";
774
775 // rw_fwrite() returns the number of bytes successfully written
776 size = rw_fwrite (tmpfname, bindata, sizeof bindata - 1, "wb");
777
778 Filebuf fb;
779
780 if (fb.open (tmpfname, mode)) {
781
782 rw_assert (fb.is_open (), __FILE__, __LINE__,
783 "basic_filebuf<%s>::is_open()", tname);
784
785 std::memset (readbuf, 0, sizeof readbuf);
786 const std::streamsize N = fb.sgetn (readbuf, readbuf_size);
787
788 if (N != std::streamsize (size))
789 rw_assert (false, __FILE__, __LINE__,
790 "basic_filebuf<%s>::sgetn (%p, %d) == %d, "
791 "got %d", tname, readbuf,
792 readbuf_size, size, N);
793
794 for (std::streamsize i = 0; i != N; ++i) {
795
796 if (char (readbuf [i]) != bindata [i]) {
797
798 rw_assert (false, __FILE__, __LINE__,
799 "basic_filebuf<%s>::sgetn() data "
800 "mismatch: got \"%s\", expected \"%s\"",
801 tname, readbuf, bindata);
802 break;
803 }
804 }
805 }
806 else {
807 ASSERT_OPEN (0, mode, "failed to open an existing file");
808 }
809 }
810
812 // exercise open (..., ios::binary | ios::out)
813
814 for (std::size_t minx = 0; minx != niomodes; ++minx) {
815
816 BEGIN_MODE (std::ios::binary | std::ios::out | iomodes [minx], "");
817
818 Filebuf fb;
819
820 if (fb.open (tmpfname, mode)) {
821
822 rw_assert (fb.is_open (), __FILE__, __LINE__,
823 "basic_filebuf<%s>::is_open()", tname);
824
825 const charT XYZ[] = {
826 '\n', 'X', '\r', 'Y', '\n', '\r', 'Z', '\r', '\n', '\0'
827 };
828
829 const std::streamsize N = sizeof XYZ / sizeof *XYZ - 1;
830
831 if (N != fb.sputn (XYZ, N))
832 ASSERT_OPEN (false, mode,
833 "failed to open an existing file for writing "
834 "(sputn() failed)");
835
836 fb.close ();
837
838 size = 0;
839 buf = _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &size, "rb"));
840
841 rw_assert (std::size_t (N) == size, __FILE__, __LINE__,
842 "basic_filebuf<%s>::sputn() wrote %d bytes, "
843 "%d expected",
844 tname, size, N * sizeof (charT));
845
846 for (std::streamsize i = 0; i != N; ++i) {
847
848 if (buf [i] != char (XYZ [i])) {
849
850 rw_assert (false, __FILE__, __LINE__,
851 "basic_filebuf<%s>::sputn() data "
852 "mismatch: wrote \"%s\", expected \"%s\"",
853 tname, buf, XYZ);
854 break;
855 }
856 }
857 }
858 else {
859 ASSERT_OPEN (0, mode, "failed to open an existing file");
860 }
861 }
862
863 REMOVE_FILE (tmpfname);
864
866 // exercise open ((const char*)0, ios::in)
867
868 for (std::size_t minx = 0; minx != niomodes; ++minx) {
869
870 BEGIN_MODE (std::ios::in | iomodes [minx],
871 ", file name = 0 [extension]");
872
873 // verify that open() succeeds when the first argument
874 // is the null pointer (the call creates a temporary
875 // file and opens it for reading -- such a file may not
876 // be very useful but since it's harmless there's no
877 // reason it shouldn't be possible)
878
879 Filebuf fb;
880 fb.open ((const char*)0, mode);
881
882 rw_assert (fb.is_open (), __FILE__, __LINE__,
883 "basic_filebuf<%s>::is_open()", tname);
884
885 // FIXME: verify that the call to close removes the file
886 fb.close ();
887 }
888
890 // exercise open ((const char*)0, ios::out)
891
892 for (std::size_t minx = 0; minx != niomodes; ++minx) {
893
894 BEGIN_MODE (std::ios::out | iomodes [minx],
895 ", file name = 0 [extension]");
896
897 // verify that open() succeeds when the first argument
898 // is the null pointer (the call creates a temporary
899 // file and opens it for writing)
900
901 Filebuf fb;
902 fb.open ((const char*)0, mode);
903
904 rw_assert (fb.is_open (), __FILE__, __LINE__,
905 "basic_filebuf<%s>::is_open()", tname);
906
907 // FIXME: verify that the call to close removes the file
908 fb.close ();
909 }
910
912 // exercise open ((const char*)0, ios::in | ios::out)
913
914 for (std::size_t minx = 0; minx != niomodes; ++minx) {
915
916 BEGIN_MODE (std::ios::in | std::ios::out | iomodes [minx],
917 ", file name = 0 [extension]");
918
919 Filebuf fb;
920 fb.open ((const char*)0, mode);
921
922 rw_assert (fb.is_open (), __FILE__, __LINE__,
923 "basic_filebuf<%s>::is_open()", tname);
924
925 // FIXME: verify that the call to close removes the file
926 fb.close ();
927 }
928
929
930#ifndef _WIN32
931
933 // exercise open (..., ios::ate)
934
935 BEGIN_MODE (std::ios::ate | std::ios::out, "");
936
937 std::signal (SIGPIPE, ignore_signal);
938
939 if (mkfifo (tmpfname, S_IRWXU))
940 rw_assert (false, __FILE__, __LINE__,
941 "mkfifo (\"%s\", S_IRWXU) failed", tmpfname);
942 else {
943
944 const pid_t childpid = fork ();
945
946 if (childpid > 0) { // parent process
947
948 Filebuf fb;
949
950 const Filebuf* const fbp = fb.open (tmpfname, mode);
951
952 // verify that open(ate) fails for an existing but unseekable file
953 if (fbp) {
954 rw_assert (false, __FILE__, __LINE__,
955 "basic_filebuf<%s>::open(\"%s\", ios::ate | "
956 "ios::out) == 0 after a failed seek to end",
957 tname, tmpfname);
958 }
959
960 // verify that is_open() returns false after a failed open
961 if (fb.is_open ()) {
962 rw_assert (false, __FILE__, __LINE__,
963 "basic_filebuf<%s>::is_open() == false"
964 " after a failed call to open", tname);
965 }
966
967 // reap our child's exit status
968 wait (0);
969 }
970 else if (0 == childpid) { // child process
971
972 Filebuf fb;
973
974 if (fb.open (tmpfname, std::ios::in))
975 fb.sgetc ();
976 else
977 rw_assert (false, __FILE__, __LINE__,
978 "basic_filebuf<%s>::open(\"%s\", "
979 "ios::in) failed in child process",
980 tname, tmpfname);
981
982 std::exit (0);
983 }
984 else
985 rw_assert (false, __FILE__, __LINE__, "fork() failed");
986
987 REMOVE_FILE (tmpfname);
988 }
989
991 // exercise close ()
992
993 rw_info (0, __FILE__, __LINE__,
994 "std::basic_filebuf<%s>::close ()", tname);
995
996 std::signal (SIGPIPE, ignore_signal);
997
998 if (mkfifo (tmpfname, S_IRWXU))
999 rw_assert (false, __FILE__, __LINE__,
1000 "mkfifo (\"%s\", S_IRWXU) failed", tmpfname);
1001 else {
1002
1003 // verify that close() returns 0 on failure by opening and
1004 // writing into a pipe in the parent process that the child
1005 // prematurely closes
1006
1007 const pid_t childpid = fork ();
1008
1009 if (childpid > 0) { // parent process
1010
1011 Filebuf fb;
1012
1013 if (fb.open (tmpfname, std::ios::out)) {
1014
1015 // write into the file buffer
1016 fb.sputc (charT ());
1017
1018 // wait for child to open and close the pipe
1019 wait (0);
1020
1021 // close() will try to flush the contents of the buffer
1022 // into the pipe which should fail, since the other end
1023 // of the pipe has already been closed
1024 if (fb.close ())
1025 rw_assert (false, __FILE__, __LINE__,
1026 "basic_filebuf<%s>::close() unexpectedly "
1027 "suceeded", tname);
1028 }
1029 else
1030 rw_assert (false, __FILE__, __LINE__,
1031 "basic_filebuf<%s>::open (\"%s\", ios::out) "
1032 " != 0; got 0", tname, tmpfname);
1033 }
1034 else if (0 == childpid) { // child process
1035
1036 Filebuf fb;
1037
1038 if (!fb.open (tmpfname, std::ios::in))
1039 rw_assert (false, __FILE__, __LINE__,
1040 "basic_filebuf<%s>::open(\"%s\", "
1041 "ios::in) failed in child process",
1042 tname, tmpfname);
1043
1044 std::exit (0);
1045 }
1046 else
1047 rw_assert (false, __FILE__, __LINE__, "fork() failed");
1048
1049 REMOVE_FILE (tmpfname);
1050 }
1051
1052#endif // _WIN32
1053
1054}
1055
1056
1058template <class charT>
1059static void
1060test_sync (const char* tname)
1061{
1062 typedef std::basic_filebuf<charT, std::char_traits<charT> > Filebuf;
1063
1064 rw_info (0, __FILE__, __LINE__,
1065 "std::basic_filebuf<%s>::sync ()", tname);
1066
1067 const char* const tmpfname = rw_tmpnam (0);
1068 if (!tmpfname) {
1069 return;
1070 }
1071
1072 {
1073 Filebuf fb;
1074
1075 static const charT foo[] = { 'f', 'o', 'o' };
1076 static const charT BAR[] = { 'B', 'A', 'R' };
1077
1078 // create a non-empty file
1079 rw_fwrite (tmpfname, "foobar");
1080
1081 if (fb.open (tmpfname, std::ios::in)) {
1082
1083 rw_assert (fb.is_open (), __FILE__, __LINE__,
1084 "basic_filebuf<%s>::is_open()", tname);
1085
1086 charT readbuf [256] = { 0 };
1087
1088 // read the first part of the file; filebuf will actually
1089 // end up reading and caching the entire contents of the
1090 // file
1091 std::streamsize got = fb.sgetn (readbuf, 3);
1092
1093 // verify the contents of buffer match that of the file
1094 if (3 != got)
1095 rw_assert (false, __FILE__, __LINE__,
1096 "sgetn() == 3, got %d", got);
1097
1098 rw_assert (!std::memcmp (readbuf, foo, sizeof foo),
1099 __FILE__, __LINE__,
1100 "sgetn() retrieved \"%s\", expected \"%s\")",
1101 readbuf, foo);
1102
1103 // overwrite the contents of the file
1104 rw_fwrite (tmpfname, "FOOBAR");
1105
1106 // call sync, excpecting filebuf to synchromnize with
1107 // the new contents of the file while maintaining
1108 // the correct position
1109 const int res = fb.pubsync ();
1110
1111 rw_assert (0 == res, __FILE__, __LINE__,
1112 "pubsync() == 0, got %d", res);
1113
1114 std::memset (readbuf, 0, sizeof readbuf);
1115
1116 // continue to read the file where the first sgetn()
1117 // call left off
1118 got = fb.sgetn (readbuf, 3);
1119
1120 // verify that the new contents of the file have been read
1121 if (3 != got)
1122 rw_assert (false, __FILE__, __LINE__,
1123 "sgetn() == 3, got %d", got);
1124
1125 rw_assert (!std::memcmp (readbuf, BAR, sizeof BAR),
1126 __FILE__, __LINE__,
1127 "sgetn() retrieved \"%s\", expected \"%s\")",
1128 readbuf, BAR);
1129 }
1130 else {
1131 rw_assert (false, __FILE__, __LINE__,
1132 "failed to open an existing file");
1133 }
1134 }
1135
1136 {
1137 Filebuf fb;
1138
1139 static const charT foo[] = { 'f', 'o', 'o' };
1140
1141 // create a non-empty file
1142 rw_fwrite (tmpfname, "foobar");
1143
1144 if (fb.open (tmpfname, std::ios::in)) {
1145
1146 rw_assert (fb.is_open (), __FILE__, __LINE__,
1147 "basic_filebuf<%s>::is_open()", tname);
1148
1149 charT readbuf [256] = { 0 };
1150
1151 // read the first part of the file; filebuf will actually
1152 // end up reading and caching the entire contents of the
1153 // file
1154 std::streamsize got = fb.sgetn (readbuf, 3);
1155
1156 // verify the contents of buffer match that of the file
1157 if (3 != got)
1158 rw_assert (false, __FILE__, __LINE__,
1159 "sgetn() == 3, got %d", got);
1160
1161 rw_assert (!std::memcmp (readbuf, foo, sizeof foo),
1162 __FILE__, __LINE__,
1163 "sgetn() retrieved \"%s\", expected \"%s\")",
1164 readbuf, foo);
1165
1166 // overwrite the contents of the file with less data
1167 rw_fwrite (tmpfname, "BA");
1168
1169 // call sync, excpecting filebuf to synchromnize with
1170 // the new contents of the file
1171 // since there is less data in the file than the current
1172 // offset, verify that the offset is adjusted to be the
1173 // same as the end of the actual file
1174 const int res = fb.pubsync ();
1175
1176 rw_assert (0 == res, __FILE__, __LINE__,
1177 "pubsync() == 0, got %d", res);
1178
1179 std::memset (readbuf, 0, sizeof readbuf);
1180
1181 // try to continue to read the file where the first sgetn()
1182 // call left off, expecting a failure
1183 got = fb.sgetn (readbuf, 3);
1184
1185 // verify that nothing has been read
1186 if (0 != got)
1187 rw_assert (false, __FILE__, __LINE__,
1188 "sgetn() == 0, got %d", got);
1189
1190 // verify that the end of the file is correctly reported
1191 const typename Filebuf::off_type off =
1192 fb.pubseekoff (0, std::ios::end);
1193
1194 rw_assert (off == 2, __FILE__, __LINE__,
1195 "pubseekoff(0, ios::end) == 2, got %d", off);
1196 }
1197 else {
1198 rw_assert (false, __FILE__, __LINE__,
1199 "failed to open an existing file");
1200 }
1201 }
1202}
1203
1204
1207template <class charT>
1208static void
1209test_attach (const char* tname)
1210{
1211#ifndef _RWSTD_NO_EXT_FILEBUF
1212
1214 // exercise attach(int) and fd()
1215
1216 rw_info (0, __FILE__, __LINE__,
1217 "std::basic_filebuf<%s>::attach (int) [extension]", tname);
1218
1219 rw_info (0, __FILE__, __LINE__,
1220 "std::basic_filebuf<%s>::fd () [extension]", tname);
1221
1222 typedef std::basic_filebuf<charT, std::char_traits<charT> > Filebuf;
1223
1224 // Extension:
1225 // basic_filebuf* attach(int fd)
1226 // Connects *this to an open file descriptor, fd. Unless detach() is
1227 // called, the file descriptor will be closed during the first call
1228 // to close() or when the object is destroyed. Returns this on success,
1229 // 0 on failure (e.g., when this->is_open() evaluates to true).
1230
1231 int lastfd = -1;
1232
1233 {
1234 Filebuf fb;
1235
1236 // get a new valid file descriptor
1237 const int fd = open (DEV_NULL, O_RDWR);
1238
1239 if (fd < 0) {
1240 rw_assert (false, __FILE__, __LINE__,
1241 "open (\"%s\", O_RDWR) < 0; "
1242 "aborting test", DEV_NULL);
1243 return;
1244 }
1245
1246 lastfd = fd;
1247
1248 // attach the filebuf object to the file descriptor
1249 Filebuf *pfb = fb.attach (fd);
1250
1251 // verify that attach() succeeded
1252 if (&fb != pfb)
1253 rw_assert (false, __FILE__, __LINE__,
1254 "basic_filebuf<%s>::attach(int = %d) == %p, got 0",
1255 tname, fd, &fb);
1256
1257 // verify that fd() returns the attached file descriptor
1258 if (fd != fb.fd ())
1259 rw_assert (false, __FILE__, __LINE__,
1260 "basic_filebuf<%s>::fd() == %d, got %d",
1261 tname, fd, fb.fd ());
1262
1263 // verify that the filebuf object is open
1264 if (!fb.is_open ())
1265 rw_assert (false, __FILE__, __LINE__,
1266 "basic_filebuf<%s>::is_open() == true, "
1267 "got false after a successful call to attach(%d)",
1268 tname, fd);
1269
1270 // Filebuf dtor should close the file descriptor (checked below)
1271 }
1272
1273 {
1274 Filebuf fb;
1275
1276 // create a new file descriptor
1277 const int fd = open (DEV_NULL, O_RDWR);
1278
1279 // verify that the dtor in the block above closed the filebuf's fd
1280 if (fd != lastfd)
1281 rw_assert (false, __FILE__, __LINE__,
1282 "basic_filebuf<%s>::attach(int = %d) caused "
1283 "a file descriptor leak after object destruction",
1284 tname, lastfd);
1285
1286 lastfd = fd;
1287
1288 // attach this object to the new file descriptor
1289 Filebuf *pfb = fb.attach (fd);
1290
1291 // verify that attach() succeeded
1292 if (&fb != pfb)
1293 rw_assert (false, __FILE__, __LINE__,
1294 "basic_filebuf<%s>::attach(int = %d) == %p, got 0",
1295 tname, fd, &fb);
1296
1297 // verify that fd() returns the attached file descriptor
1298 if (fd != fb.fd ())
1299 rw_assert (false, __FILE__, __LINE__,
1300 "basic_filebuf<%s>::fd() == %d, got %d",
1301 tname, fd, fb.fd ());
1302
1303 // verify that the filebuf object is open
1304 if (!fb.is_open ())
1305 rw_assert (false, __FILE__, __LINE__,
1306 "basic_filebuf<%s>::is_open() == true, "
1307 "got false after a successful call to attach(%d)",
1308 tname, fd);
1309
1310 // call close
1311 pfb = fb.close ();
1312
1313 // verify that the call to close() succeeded
1314 if (&fb != pfb)
1315 rw_assert (false, __FILE__, __LINE__,
1316 "basic_filebuf<%s>::close() == %p, got 0",
1317 tname, &fb);
1318
1319 // verify that fd() returns an invalid file descriptor after
1320 // a successful call to close()
1321 if (0 <= fb.fd ())
1322 rw_assert (false, __FILE__, __LINE__,
1323 "basic_filebuf<%s>::fd() < 0, got %d after "
1324 "a call to close()",
1325 tname, fb.fd ());
1326
1327 // verify that is_open() returns false after the call to close()
1328 if (fb.is_open ())
1329 rw_assert (false, __FILE__, __LINE__,
1330 "basic_filebuf<%s>::is_open() == false, "
1331 "got true after a successful call to close()",
1332 tname);
1333 }
1334
1335 {
1336 Filebuf fb;
1337
1338 // get a new valid file descriptor
1339 const int fd = open (DEV_NULL, O_RDWR);
1340
1341 // verify that the dtor in the block above closed the filebuf's fd
1342 if (fd != lastfd)
1343 rw_assert (false, __FILE__, __LINE__,
1344 "basic_filebuf<%s>::attach(int = %d) caused "
1345 "a file descriptor leak after object destruction",
1346 tname, lastfd);
1347
1348 lastfd = fd;
1349
1350 // attach the filebuf object to the file descriptor
1351 Filebuf *pfb = fb.attach (fd);
1352
1353 // verify that attach() succeeded
1354 if (&fb != pfb)
1355 rw_assert (false, __FILE__, __LINE__,
1356 "basic_filebuf<%s>::attach(int = %d) == %p, got 0",
1357 tname, fd, &fb);
1358
1359 pfb = fb.attach (fd);
1360
1361 // verify that a subsequent call to attach() (with the same
1362 // or different file descriptor) failed
1363 if (&fb == pfb)
1364 rw_assert (false, __FILE__, __LINE__,
1365 "basic_filebuf<%s>::attach(int = %d) == 0, got %p",
1366 tname, fd, &fb);
1367
1368 const int new_fd = open (DEV_NULL, O_RDWR);
1369
1370 pfb = fb.attach (fd);
1371
1372 if (&fb == pfb)
1373 rw_assert (false, __FILE__, __LINE__,
1374 "basic_filebuf<%s>::attach(int = %d) == 0, got %p",
1375 tname, fd, &fb);
1376
1377 close (new_fd);
1378
1379 // Filebuf dtor should close the file descriptor (checked below)
1380 }
1381
1383 // exercise detach()
1384
1385 rw_info (0, __FILE__, __LINE__,
1386 "std::basic_filebuf<%s>::detach () [extension]", tname);
1387
1388 // Extension:
1389 // int detach()
1390 // Flushes any waiting output to the file associated with the file
1391 // descriptor, and disconnects the file descriptor from *this so
1392 // that subsequent calls to close() will not close the file
1393 // descriptor. Returns the detached file descriptor on success,
1394 // -1 on failure.
1395
1396 {
1397 Filebuf fb;
1398
1399 // get a new valid file descriptor
1400 const int fd = open (DEV_NULL, O_RDWR);
1401
1402 // verify that the dtor in the block above closed the filebuf's fd
1403 if (fd != lastfd)
1404 rw_assert (false, __FILE__, __LINE__,
1405 "basic_filebuf<%s>::attach(int = %d) caused "
1406 "a file descriptor leak after object destruction",
1407 tname, lastfd);
1408
1409 lastfd = fd;
1410
1411 // attach the filebuf object to the file descriptor
1412 fb.attach (fd);
1413
1414 const int old_fd = fb.detach ();
1415
1416 // verify that detach() returned the original file descriptor
1417 if (fd != old_fd)
1418 rw_assert (false, __FILE__, __LINE__,
1419 "basic_filebuf<%s>::detach() == %d, got %d",
1420 tname, fd, old_fd);
1421
1422 // verify that is_open() returns false after the call to detach()
1423 if (fb.is_open ())
1424 rw_assert (false, __FILE__, __LINE__,
1425 "basic_filebuf<%s>::is_open() == false, "
1426 "got true after a call to detach()", tname);
1427
1428 // verify that fd() returns an invalid file descriptor after
1429 // a successful call to detach()
1430 if (0 <= fb.fd ())
1431 rw_assert (false, __FILE__, __LINE__,
1432 "basic_filebuf<%s>::fd() < 0, got %d after "
1433 "a call to detach()()", tname, fb.fd ());
1434
1435 close (old_fd);
1436
1437 errno = 0;
1438 }
1439
1440 // verify that the dtor in the block above didn't try to close
1441 // the already explicitly closed file descriptor
1442 if (errno)
1443 rw_assert (false, __FILE__, __LINE__,
1444 "basic_filebuf<%s>::~basic_filebuf() attempted to "
1445 "close a detached file descriptor", tname);
1446
1447 {
1448 Filebuf fb;
1449
1450 if (fb.fd () >= 0)
1451 rw_assert (false, __FILE__, __LINE__,
1452 "basic_filebuf<%s>::fd() < 0, got %d for an "
1453 "object that's not open", tname, fb.fd ());
1454
1455 const int old_fd = fb.detach ();
1456
1457 // verify detach() returns an invalid file descriptor when called
1458 // on a filebuf object that's not open
1459 if (0 <= old_fd)
1460 rw_assert (false, __FILE__, __LINE__,
1461 "basic_filebuf<%s>::detach() < 0, got %d",
1462 tname, old_fd);
1463 }
1464
1465 const char* const tmpfname = rw_tmpnam (0);
1466
1467 if (!tmpfname) {
1468 return;
1469 }
1470
1471 {
1472 Filebuf fb;
1473
1474 // open a writeable file
1475 const int fd = open (tmpfname, O_CREAT | O_WRONLY, 0666);
1476
1477 if (fd < 0) {
1478 rw_warn (false, __FILE__, __LINE__,
1479 "open (\"%s\", O_CREAT | O_WRONLY, 0666) "
1480 "failed: %m", tmpfname);
1481 }
1482
1483 // attach filebuf to the file descriptor
1484 fb.attach (fd);
1485
1486 const charT data[] = { '0', '1', '2', '\0' };
1487 const std::size_t nelems = sizeof data / sizeof *data - 1;
1488
1489 // write into the filebuf object
1490 const std::size_t wrote = fb.sputn (data, nelems);
1491
1492 if (wrote != nelems)
1493 rw_assert (false, __FILE__, __LINE__,
1494 "sputn(\"%s\", %d) == %d, got %d",
1495 data, nelems, nelems, wrote);
1496
1497 // and detach
1498 const int old_fd = fb.detach ();
1499 if (fd != old_fd)
1500 rw_assert (false, __FILE__, __LINE__,
1501 "basic_filebuf<%s>::detach() == %d, got %d",
1502 fd, old_fd);
1503
1504 // verify by reading the contents of the named file that
1505 // the call to detach() wrote the contents of the filebuf
1506 // object's data buffer into the file before detaching
1507 std::size_t nbytes = 0;
1508 const char* const buf =
1509 _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, &nbytes));
1510
1511 if ( !buf || nbytes != nelems
1512 || rw_strncmp (buf, data, nelems))
1513 rw_assert (false, __FILE__, __LINE__,
1514 "basic_filebuf<%s>::detach() failed to flush "
1515 "data to the attached file descriptor; "
1516 "read %lu bytes: %s, expected \"012\"",
1517 tname, nbytes, buf);
1518
1519 REMOVE_FILE (tmpfname);
1520 }
1521
1522 {
1523 Filebuf fb;
1524
1525 // create a new file and open it for writing
1526 const int fd = open (tmpfname, O_CREAT | O_WRONLY, 0666);
1527
1528 // attach filebuf to the file descriptor (will set mode to ios::out
1529 // based on the O_WRONLY open mode of the file descriptor)
1530 fb.attach (fd);
1531
1532 // close and reopen the same file descriptor as read-only
1533 close (fd);
1534 if (fd != open (tmpfname, O_RDONLY)) {
1535 rw_assert (false, __FILE__, __LINE__,
1536 "open (\"%s\", O_RDONLY) != %d; aborting test",
1537 tmpfname, fd);
1538 return;
1539 }
1540
1541 static const charT data[] = { '0', '1', '2', '\0' };
1542 static const std::size_t nelems = sizeof data / sizeof *data - 1;
1543
1544 // write into the filebuf object's buffer
1545 if (nelems != std::size_t (fb.sputn (data, nelems)))
1546 rw_assert (false, __FILE__, __LINE__, "sputn() failed");
1547
1548 // and try to detach (must try to flush data into file)
1549 const int bad_fd = fb.detach ();
1550
1551 // verify that detach() failed (due to the failure
1552 // to write to a read-only file descriptor)
1553 if (bad_fd >= 0)
1554 rw_assert (false, __FILE__, __LINE__,
1555 "basic_filebuf<%s>::detach() < 0, got %d after "
1556 "a failure to write to read-only file descriptor",
1557 tname, bad_fd);
1558
1559 // verify that the call to detach() failed to flush the contents
1560 // of the filebuf object's data buffer into a read-only file by
1561 // testing that the size of the file is unchanged (i.e., 0)
1562 std::size_t nbytes = 0;
1563
1564 const char* buf =
1565 _RWSTD_STATIC_CAST (char*, rw_fread (tmpfname, 0));
1566
1567 if (buf && nbytes)
1568 rw_assert (false, __FILE__, __LINE__,
1569 "basic_filebuf<%s>::detach() unexpectedly wrote "
1570 "%u bytes to a read-only file: \"%s\"",
1571 tname, nbytes, buf);
1572
1573 // close and reopen the same file descriptor as write-only
1574 close (fd);
1575 if (fd != open (tmpfname, O_WRONLY)) {
1576 rw_assert (false, __FILE__, __LINE__,
1577 "open (\"%s\", O_WRONLY) != %d; aborting test",
1578 tmpfname, fd);
1579 return;
1580 }
1581
1582 // and detach (must successfully flush data into file)
1583 const int good_fd = fb.detach ();
1584
1585 // verify that the call to detach() was successful
1586 if (good_fd < 0)
1587 rw_assert (false, __FILE__, __LINE__,
1588 "basic_filebuf<%s>::detach() == %d, got %d",
1589 tname, fd, good_fd);
1590
1591 // verify that the filebuf object is no longer open
1592 if (fb.is_open ())
1593 rw_assert (false, __FILE__, __LINE__,
1594 "basic_filebuf<%s>::is_open() unexpectedly "
1595 "true after a successful call to detach()",
1596 tname);
1597
1598 // verify that this call to detach() wrote out the contents of the
1599 // filebuf object's data buffer into the writeable file by checking
1600 // the size and contents of the file
1601 buf = (char*)rw_fread (tmpfname, &nbytes);
1602
1603 if ( !buf || nbytes != nelems
1604 || rw_strncmp (buf, data, nelems))
1605 rw_assert (false, __FILE__, __LINE__,
1606 "basic_filebuf<%s>::detach() failed to flush "
1607 "data to the attached file descriptor; "
1608 "read %lu bytes: %s, expected %s",
1609 tname, nbytes, buf, data);
1610
1611 // close the file descriptor before removing the file
1612 close (good_fd);
1613
1614 REMOVE_FILE (tmpfname);
1615 }
1616
1617#else // if defined (_RWSTD_NO_EXT_FILEBUF)
1618
1619 _RWSTD_UNUSED (tname);
1620
1621#endif // _RWSTD_NO_EXT_FILEBUF
1622
1623}
1624
1625
1627// CodeCvt<charT> performs a state-dependent conversion
1628// the first byte of the state_type object encodes the state,
1629// any remaining bytes must be 0
1630// internal characters are externally represented as sequences
1631// of 1 to 22 chars, escape sequences are 11 to 13 chars long
1632// and all start with the "<ESC-" prefix
1633//
1634// for example, the string "Hello, World!\n" will be externally
1635// represented by the following sequence of narrow characters:
1636//
1637// "<ESC-UPPER>H<ESC-LOWER>ello<ESC-PUNCT><comma><SP><ESC-UPPER>"
1638// "W<ESC-LOWER>orld<ESC-PUNCT><exclamation-mark><ESC-CNTRL>\n"
1639
1640template <class charT>
1641struct CodeCvt: std::codecvt<charT, char, std::mbstate_t>
1642{
1643 typedef std::codecvt<charT, char, std::mbstate_t> Base;
1644
1646
1647public:
1648
1649 typedef typename Base::intern_type intern_type;
1650 typedef typename Base::extern_type extern_type;
1651 typedef typename Base::state_type state_type;
1652
1653 explicit CodeCvt (std::size_t ref = 0)
1654 : Base (ref) { }
1655
1656protected:
1657
1658 virtual std::codecvt_base::result
1660 const intern_type*, const intern_type*, const intern_type*&,
1661 extern_type*, extern_type*, extern_type*&) const;
1662
1663 virtual std::codecvt_base::result
1664 do_in (state_type&,
1665 const extern_type*, const extern_type*, const extern_type*&,
1666 intern_type*, intern_type*, intern_type*&) const;
1667
1668 virtual std::codecvt_base::result
1670
1671 virtual int do_encoding () const _THROWS (()) {
1672 return -1; // state-dependent encoding
1673 }
1674
1675 virtual bool do_always_noconv () const _THROWS (()) {
1676 return false; // conversion always necessary
1677 }
1678
1679 // returns the maximum `N' of extern chars in the range [from, from_end)
1680 // such that N represents max or fewer internal chars
1681 virtual int
1683 const extern_type*, std::size_t) const;
1684
1685 // returns the max value do_length (s, from, from_end, 1) can return
1686 // for any valid range [from, from_end) - see LWG issue 74
1687 virtual int do_max_length () const _THROWS (()) {
1688 return -1;
1689 }
1690};
1691
1692
1693extern const char* const escapes[] = {
1694 "<ESC-CNTRL>", "<ESC-PUNCT>", "<ESC-DIGIT>",
1695 "<ESC-UPPER>", "<ESC-LOWER>", "<ESC-HEXCODE>"
1696};
1697
1698
1699extern const char* const charnames[] = {
1700 // control characters (<ESC-CNTRL>)
1701 "<NUL>", "<SOH>", "<STX>", "<ETX>", "<EOT>", "<ENQ>", "<ACK>", "<BEL>",
1702 "<BS>", "<HT>", "\n", "<VT>", "<FF>", "<CR>" /* '\r' */, "<SO>", "<SI>",
1703 "<DLE>", "<DC1>", "<DC2>", "<DC3>", "<DC4>", "<NAK>", "<SYN>", "<ETB>",
1704 "<CAN>", "<EM>", "<SUB>", "<ESC>", "<FS>", "<GS>", "<RS>", "<US>",
1705 // punctuators (<ESC-PUNCT>)
1706 "<SP>",
1707 "<exclamation-mark>",
1708 "<quotation-mark>",
1709 "<number-sign>",
1710 "<dollar-sign>",
1711 "<percent-sign>",
1712 "<ampersand>",
1713 "<apostrophe>",
1714 "<left-parenthesis>",
1715 "<right-parenthesis>",
1716 "<asterisk>",
1717 "<plus-sign>",
1718 "<comma>",
1719 "<hyphen>",
1720 "<period>",
1721 "<slash>",
1722 // digits (<ESC-DIGIT>)
1723 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
1724 // punctuators (<ESC-PUNCT>)
1725 "<colon>",
1726 "<semicolon>",
1727 "<less-than-sign>",
1728 "<equals-sign>",
1729 "<greater-than-sign>",
1730 "<question-mark>",
1731 "<commercial-at>",
1732 // uppercase letters (<ESC_UPPER>)
1733 "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
1734 "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
1735 // punctuators (<ESC-PUNCT>)
1736 "<left-square-bracket>",
1737 "<backslash>",
1738 "<right-square-bracket>",
1739 "<circumflex>",
1740 "<underscore>",
1741 "<grave-accent>",
1742 // lowercase letters (<ESC_LOWER>)
1743 "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
1744 "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
1745 // punctuators (<ESC-PUNCT>)
1746 "<left-curly-bracket>",
1747 "<vertical-line>",
1748 "<right-curly-bracket>",
1749 "<tilde>",
1750 // control characters (<ESC-CNTRL>)
1751 "<DEL>"
1752 // characters above '\x7f' are represented in hex (<ESC-HEXCODE>)
1753};
1754
1755
1756template <class charT>
1757std::codecvt_base::result
1759 const intern_type *from,
1760 const intern_type *from_end,
1761 const intern_type *&from_next,
1762 extern_type *to,
1763 extern_type *to_end,
1764 extern_type *&to_next) const
1765{
1766 std::codecvt_base::result res = std::codecvt_base::ok;
1767
1768 char hexbuf [5] = "";
1769
1770 int int_state = hexcode;
1771
1772 for (from_next = from, to_next = to; from_next != from_end; ++from_next) {
1773
1774 typedef std::char_traits<intern_type> Traits;
1775
1776 const std::size_t uch = std::size_t (Traits::to_int_type (*from_next));
1777
1778 if (uch < ' ') {
1779 int_state = cntrl;
1780 }
1781 else if (uch >= ' ' && uch <= '/') {
1782 int_state = punct;
1783 }
1784 else if (uch >= '0' && uch <= '9') {
1785 int_state = digit;
1786 }
1787 else if (uch >= ':' && uch <= '@') {
1788 int_state = punct;
1789 }
1790 else if (uch >= 'A' && uch <= 'Z') {
1791 int_state = upper;
1792 }
1793 else if (uch >= '[' && uch <= '`') {
1794 int_state = punct;
1795 }
1796 else if (uch >= 'a' && uch <= 'z') {
1797 int_state = lower;
1798 }
1799 else if (uch >= '{' && uch <= '~') {
1800 int_state = punct;
1801 }
1802 else if (uch == '\x7f') {
1803 int_state = cntrl;
1804 }
1805 else if (uch <= 0xff) {
1806 int_state = hexcode;
1807
1808 if (to_end - to_next < 4) {
1809 res = std::codecvt_base::partial;
1810 break;
1811 }
1812
1813 // convert `uch' to a hexadecimal escape sequence
1814 static const char hexdigits[] = "0123456789abcdef";
1815
1816 hexbuf [0] = '\\';
1817 hexbuf [1] = 'x';
1818 hexbuf [2] = hexdigits [uch >> 8];
1819 hexbuf [3] = hexdigits [uch & 0x0fU];
1820 hexbuf [4] = '\0';
1821 }
1822 else {
1823 res = std::codecvt_base::error;
1824 break;
1825 }
1826
1827 if (int (*(char*)&state) != int_state) {
1828
1829 const std::size_t len = std::strlen (escapes [int_state]);
1830
1831 if (len > std::size_t (to_end - to_next)) {
1832 res = std::codecvt_base::partial;
1833 break;
1834 }
1835
1836 std::memcpy (to_next, escapes [int_state], len);
1837 to_next += len;
1838
1839 *(char*)&state = char (int_state);
1840 }
1841
1842 const char* const outs = *hexbuf ? hexbuf : charnames [uch];
1843
1844 const std::size_t len = std::strlen (outs);
1845
1846 if (len > std::size_t (to_end - to_next)) {
1847 res = std::codecvt_base::partial;
1848 break;
1849 }
1850
1851 std::memcpy (to_next, outs, len);
1852 to_next += len;
1853 }
1854
1855 return res;
1856}
1857
1858
1859// looks for a character whose name starts at `*pbeg' in the table
1860// of character names, `chartbl' containing `tblsize' elements
1861// if a unique (even partial) match is found, returns the index of
1862// the character in the table; if the unique match is complete,
1863// also advances `*pbeg' by the length of the match
1864std::size_t find_char (const char **pbeg, const char *end,
1865 const char* const chartbl[], std::size_t tblsize)
1866{
1867 const std::size_t nchars = sizeof charnames / sizeof *charnames;
1868
1869 std::size_t hits [nchars] = { 0 };
1870
1871 const char* cur = *pbeg;
1872
1873 for (; cur != end; ++cur) {
1874
1875 const std::size_t inx = cur - *pbeg;
1876
1877 std::size_t nmatches = 0;
1878
1879 for (std::size_t i = 0; i != tblsize; ++i) {
1880 if (hits [i] == inx && chartbl [i] && *cur == chartbl [i][inx]) {
1881 ++hits [i];
1882 ++nmatches;
1883 }
1884 }
1885
1886 if (!nmatches)
1887 break;
1888 }
1889
1890 std::size_t nmatches = 0;
1891 std::size_t imaxhits = 0;
1892 std::size_t inx = std::size_t (-1);
1893
1894 for (std::size_t i = 0; i != tblsize; ++i) {
1895
1896 if (!chartbl [i])
1897 continue;
1898
1899 if (hits [i] > hits [imaxhits])
1900 imaxhits = i;
1901
1902 if (hits [i] == std::strlen (chartbl [i])) {
1903
1904 if (std::size_t (-1) == inx)
1905 inx = 0;
1906
1907 if (hits [i] == hits [inx]) {
1908 ++nmatches;
1909 inx = i;
1910 }
1911 else if (hits [i] > hits [inx]) {
1912 nmatches = 1;
1913 inx = i;
1914 }
1915 }
1916 else if (std::size_t (-1) != inx && hits [i] > hits [inx]) {
1917 nmatches = 0;
1918 inx = i;
1919 }
1920 }
1921
1922 if (inx != std::size_t (-1) && 1 == nmatches)
1923 *pbeg = cur;
1924
1925 if (hits [imaxhits] == std::size_t (end - *pbeg))
1926 inx = imaxhits;
1927
1928 return inx;
1929}
1930
1931
1932template <class charT>
1933std::codecvt_base::result
1935 const extern_type *from,
1936 const extern_type *from_end,
1937 const extern_type *&from_next,
1938 intern_type *to,
1939 intern_type *to_end,
1940 intern_type *&to_next) const
1941{
1942 std::codecvt_base::result res = std::codecvt_base::ok;
1943
1944 for (to_next = to, from_next = from; to_next != to_end; ++to_next) {
1945
1946 top_of_loop:
1947
1948 if (from_next == from_end)
1949 break;
1950
1951 const std::size_t navail = from_end - from_next;
1952
1953 // check the beginning of the sequence to see if
1954 // it may possibly start with an escape sequence
1955
1956 if (navail <= 5 && !std::memcmp (from_next, "<ESC-", navail))
1957 return std::codecvt_base::partial;
1958
1959 if (navail > 5 && !std::memcmp (from_next, "<ESC-", 5)) {
1960
1961 // found the beginning of what might be an escape sequence
1962
1963 if (navail >= 11) {
1964
1965 const std::size_t nescapes = sizeof escapes / sizeof *escapes;
1966
1967 for (std::size_t i = 0; i != nescapes; ++i) {
1968
1969 const std::size_t len = std::strlen (escapes [i]);
1970
1971 if (!std::memcmp (escapes [i], from_next, len)) {
1972
1973 // found an escape sequence
1974
1975 // set the state variable
1976 *(char*)&state = char (i);
1977
1978 // advance past the escape sequence
1979 from_next += len;
1980
1981 // continue iterating over the rest of the sequence
1982 goto top_of_loop;
1983 }
1984 }
1985 }
1986 else {
1987 // not enough external elements to convert
1988 // to an internal character
1989 return std::codecvt_base::partial;
1990 }
1991 }
1992
1993 std::size_t chinx = std::size_t (-1); // character index
1994 std::size_t choff = 0; // character offset
1995
1996 const char* const from_next_save = from_next;
1997
1998 switch (*(char*)&state) {
1999
2000 case cntrl:
2001 chinx = find_char (&from_next, from_end, charnames, ' ' + 1U);
2002 if ( std::size_t (-1) == chinx
2003 && 5 <= from_end - from_next
2004 && !std::memcmp (from_next, "<DEL>", 5)) {
2005 chinx = '\x7f';
2006 from_next += 5;
2007 }
2008 break;
2009
2010 case punct: {
2011
2012 static const char* const pun[] = {
2013 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2014 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2015 "<SP>",
2016 "<exclamation-mark>",
2017 "<quotation-mark>",
2018 "<number-sign>",
2019 "<dollar-sign>",
2020 "<percent-sign>",
2021 "<ampersand>",
2022 "<apostrophe>",
2023 "<left-parenthesis>",
2024 "<right-parenthesis>",
2025 "<asterisk>",
2026 "<plus-sign>",
2027 "<comma>",
2028 "<hyphen>",
2029 "<period>",
2030 "<slash>",
2031 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2032 "<colon>",
2033 "<semicolon>",
2034 "<less-than-sign>",
2035 "<equals-sign>",
2036 "<greater-than-sign>",
2037 "<question-mark>",
2038 "<commercial-at>",
2039 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2040 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2041 "<left-square-bracket>",
2042 "<backslash>",
2043 "<right-square-bracket>",
2044 "<circumflex>",
2045 "<underscore>",
2046 "<grave-accent>",
2047 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2048 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2049 "<left-curly-bracket>",
2050 "<vertical-line>",
2051 "<right-curly-bracket>",
2052 "<tilde>",
2053 0
2054 };
2055
2056 chinx = find_char (&from_next, from_end,
2057 pun, sizeof pun / sizeof *pun);
2058
2059 break;
2060 }
2061
2062 case digit:
2063 choff = '0';
2064
2065 if (*from_next >= '0' && *from_next <= '9')
2066 chinx = *from_next++ - '0';
2067
2068 // commented out code replaced by the above for efficiency
2069 // chinx = find_char (&from_next, from_end, charnames + choff, 10);
2070 break;
2071
2072 case lower:
2073 choff = 'a';
2074
2075 // assume ASCII
2076 if (*from_next >= 'a' && *from_next <= 'z')
2077 chinx = *from_next++ - 'a';
2078
2079 // commented out code replaced by the above for efficiency
2080 // chinx = find_char (&from_next, from_end, charnames + choff, 26);
2081 break;
2082
2083 case upper:
2084 choff = 'A';
2085
2086 // assume ASCII
2087 if (*from_next >= 'A' && *from_next <= 'Z')
2088 chinx = *from_next++ - 'A';
2089
2090 // commented out code replaced by the above for efficiency
2091 // chinx = find_char (&from_next, from_end, charnames + choff, 26);
2092 break;
2093
2094 case hexcode: {
2095 if (from_end - from_next < 4)
2096 return std::codecvt_base::partial;
2097
2098 if ( from_next [0] != '\\' || from_next [1] != 'x'
2099 || !( (from_next [2] >= '0' && from_next [2] <= '9')
2100 || (from_next [2] >= 'A' && from_next [2] <= 'F')
2101 || (from_next [2] >= 'a' && from_next [2] <= 'f'))
2102 || !( (from_next [3] >= '0' && from_next [3] <= '9')
2103 || (from_next [3] >= 'A' && from_next [3] <= 'F')
2104 || (from_next [3] >= 'a' && from_next [3] <= 'f')))
2105 return std::codecvt_base::error;
2106
2107 // convert a hex literal to a number
2108 chinx = 0;
2109 for (unsigned i = 2; i != 4; ++i) {
2110 chinx <<= 4;
2111 if (from_next [i] >= '0' && from_next [i] <= '9')
2112 chinx += from_next [i] - '0';
2113 else if (from_next [i] >= 'A' && from_next [i] <= 'F')
2114 chinx += 10 + (from_next [i] - 'A');
2115 else
2116 chinx += 10 + (from_next [i] - 'a');
2117 }
2118
2119 break;
2120 }
2121
2122 default:
2123 chinx = std::size_t (-1);
2124 break;
2125 }
2126
2127 if (std::size_t (-1) == chinx) {
2128 res = std::codecvt_base::error;
2129 break;
2130 }
2131
2132 // the combination of a valid `chinx' and an unmodified
2133 // `from_next' pointer indicates a partial match
2134 if (from_next == from_next_save) {
2135 res = std::codecvt_base::partial;
2136 break;
2137 }
2138
2139 *to_next = charT (chinx + choff);
2140 }
2141
2142 return res;
2143}
2144
2145
2146template <class charT>
2147std::codecvt_base::result
2149do_unshift (state_type &state,
2150 extern_type *to,
2151 extern_type *to_end,
2152 extern_type *&to_next) const
2153{
2154 to_next = to;
2155
2156 if (!*(char*)&state)
2157 return std::codecvt_base::noconv;
2158
2159 const std::size_t len = std::strlen (escapes [0]);
2160
2161 if (len > std::size_t (to_end - to_next))
2162 return std::codecvt_base::partial;
2163
2164 std::memcpy (to_next, escapes [0], len);
2165 to_next += len;
2166
2167 std::memset (&state, 0, sizeof state);
2168
2169 return std::codecvt_base::ok;
2170}
2171
2172
2173template <class charT>
2174int
2177 const extern_type*, std::size_t) const
2178{
2179 return 0;
2180}
2181
2182
2184static const char*
2185get_codecvt_result (std::codecvt_base::result res)
2186{
2187 switch (res) {
2188 case std::codecvt_base::error: return "std::codecvt_base::error";
2189 case std::codecvt_base::noconv: return "std::codecvt_base::noconv";
2190 case std::codecvt_base::ok: return "std::codecvt_base::ok";
2191 case std::codecvt_base::partial: return "std::codecvt_base::partial";
2192 }
2193
2194 return "unknown";
2195}
2196
2197
2198template <class charT>
2199static void
2200test_codecvt (const char* tname)
2201{
2202 rw_info (0, __FILE__, __LINE__,
2203 "std::basic_filebuf<%s> with a state-dependent encoding",
2204 tname);
2205
2206 rw_info (0, __FILE__, __LINE__,
2207 "sgetn() with state-dependent code conversion");
2208
2209 // read in the text of this source file as plain text
2210
2211 typedef std::basic_filebuf<charT, std::char_traits<charT> > Filebuf;
2212 typedef typename Filebuf::off_type off_type;
2213
2214 Filebuf noconv_in;
2215
2216 // use ios_base::binary to avoid CR/LF conversion issues
2217 if (!noconv_in.open (__FILE__, std::ios::binary | std::ios::in)) {
2218 rw_assert (false, __FILE__, __LINE__,
2219 "basic_filebuf<%s>::open (\"%s\", "
2220 "ios_base::binary | ios_base::in) failed",
2221 tname, __FILE__);
2222 return;
2223 }
2224
2225 // seek to the end of the file to get its size in bytes
2226 const std::streamsize noconv_fsize =
2227 noconv_in.pubseekoff (0, std::ios::end);
2228
2229 if (noconv_fsize <= 0) {
2230 rw_assert (false, __FILE__, __LINE__,
2231 "basic_filebuf<%s>::pubseekoff "
2232 "(0, ios_base::end) >= 0, got %i",
2233 tname, noconv_fsize);
2234 return;
2235 }
2236
2237 if (0 != noconv_in.pubseekoff (0, std::ios::beg)) {
2238 rw_assert (false, __FILE__, __LINE__,
2239 "basic_filebuf<%s>::pubseekoff "
2240 "(0, ios_base::beg) failed", tname);
2241 return;
2242 }
2243
2244 const char* const tmpfname = rw_tmpnam (0);
2245
2246 if (!tmpfname) {
2247 return;
2248 }
2249
2250 // allocate a buffer large enough to hold the contents of the
2251 // file in internal representation without codeset conversion
2252 charT *noconv_intbuf = new charT [noconv_fsize];
2253
2254 // sgetn() returns the number of internal characters read in
2255 // which must equal the number of external characters read by
2256 // the `noconv_in' file buffer (since it does no conversion)
2257 std::streamsize nread = noconv_in.sgetn (noconv_intbuf, noconv_fsize);
2258
2259 if (nread != noconv_fsize) {
2260 rw_assert (false, __FILE__, __LINE__,
2261 "basic_filebuf<%s>::sgetn (%p, %i) == %i), got %i",
2262 tname, noconv_intbuf,
2263 noconv_fsize, noconv_fsize, nread);
2264 delete[] noconv_intbuf;
2265 return;
2266 }
2267
2268 const CodeCvt<charT> cvt (1);
2269
2270 // write out the text of this file using the test codecvt facet
2271 // that performs state-dependent encoding
2272 Filebuf conv_out;
2273
2274 conv_out.pubimbue (std::locale (std::locale::classic (), &cvt));
2275
2276 if (!conv_out.open (tmpfname, std::ios::binary | std::ios::out)) {
2277 rw_assert (false, __FILE__, __LINE__,
2278 "basic_filebuf<%s>::open (\"%s\", ios_base::out) "
2279 "failed", tname, tmpfname);
2280 delete[] noconv_intbuf;
2281 return;
2282 }
2283
2284 // sputn() returns the number of internal characters written out
2285 // which must equal the number of external characters read by
2286 // the `noconv_in' file buffer (since it does no conversion)
2287 const std::streamsize nwrote = conv_out.sputn (noconv_intbuf, nread);
2288
2289 if (nread != nwrote) {
2290 rw_assert (false, __FILE__, __LINE__,
2291 "basic_filebuf<%s>::sputn (\"%s\", %i) == %i), "
2292 "got %i", tname,
2293 noconv_intbuf, nread, nread, nwrote);
2294 delete[] noconv_intbuf;
2295 return;
2296 }
2297
2298 // close file buffer to flush it out
2299 if (!conv_out.close ())
2300 rw_warn (false, __FILE__, __LINE__,
2301 "basic_filebuf<%s>(\"%s\").close() failed",
2302 tname, tmpfname);
2303
2304 std::filebuf nin; // narrow input stream buffer
2305
2306 if (!nin.open (tmpfname, std::ios::binary | std::ios::in)) {
2307 rw_assert (false, __FILE__, __LINE__,
2308 "basic_filebuf<%s>::open (\"%s\", ios_base::in) failed",
2309 tname, tmpfname);
2310 delete[] noconv_intbuf;
2311 return;
2312 }
2313
2314 // get the size of the converted file in external characters
2315 const std::streamsize conv_fsize = nin.pubseekoff (0, std::ios::end);
2316
2317 if (conv_fsize <= noconv_fsize) {
2318 rw_assert (false, __FILE__, __LINE__,
2319 "filebuf::pubseekoff (0, ios_base::end) > %i, got %i",
2320 noconv_fsize, conv_fsize);
2321 delete[] noconv_intbuf;
2322 return;
2323 }
2324
2325 if (0 != nin.pubseekoff (0, std::ios::beg)) {
2326 rw_assert (false, __FILE__, __LINE__,
2327 "filebuf::pubseekoff (0, ios_base::beg) failed");
2328 delete[] noconv_intbuf;
2329 return;
2330 }
2331
2332 // allocate a buffer large enough to hold the contents of the
2333 // file in external representation without codeset conversion
2334 char *noconv_extbuf = new char [conv_fsize];
2335 std::memset (noconv_extbuf, 0, conv_fsize);
2336
2337 // read in the (unconverted) contents of the file
2338 nread = nin.sgetn (noconv_extbuf, conv_fsize);
2339
2340 if (nread != conv_fsize) {
2341 rw_assert (false, __FILE__, __LINE__,
2342 "filebuf::sgetn (%p, %i) == %i, got %i",
2343 noconv_extbuf, conv_fsize, conv_fsize, nread);
2344 delete[] noconv_intbuf;
2345 delete[] noconv_extbuf;
2346 return;
2347 }
2348
2349 if (!nin.close ())
2350 rw_warn (false, __FILE__, __LINE__,
2351 "basic_filebuf<%s>(\"%s\").close() failed",
2352 tname, tmpfname);
2353
2354 std::mbstate_t state;
2355 std::memset (&state, 0, sizeof state);
2356
2357 const char *from = noconv_extbuf;
2358 const char *from_end = noconv_extbuf + nread;
2359 const char *from_next = 0;
2360
2361 // allocate a buffer large enough to hold the contents of the
2362 // file in internal representation with codeset conversion
2363 charT *conv_intbuf = new charT [noconv_fsize];
2364 std::memset (conv_intbuf, 0, noconv_fsize * sizeof *conv_intbuf);
2365
2366 charT *to = conv_intbuf;
2367 charT *to_end = conv_intbuf + noconv_fsize;
2368 charT *to_next = 0;
2369
2370 // convert the contents of the file using the test codecvt facet
2371 const std::codecvt_base::result cvtres =
2372 cvt.in (state, from, from_end, from_next, to, to_end, to_next);
2373
2374 if (std::codecvt_base::ok != cvtres) {
2375 rw_assert (false, __FILE__, __LINE__,
2376 "CodeCvt<%s>::in ({ %d, ... }, %p, %p, %p, %p, %p, %p) "
2377 "== std::codecvt_base::ok, got %s",
2378 tname, *(unsigned char*)&state,
2379 from, from_end, from_next, to, to_end, to_next,
2380 get_codecvt_result (cvtres));
2381 delete[] noconv_intbuf;
2382 delete[] noconv_extbuf;
2383 delete[] conv_intbuf;
2384 return;
2385 }
2386
2387 const std::streamsize n_ext_cvt = from_next - from;
2388 const std::streamsize n_int_cvt = to_next - to;
2389
2390 rw_assert (n_ext_cvt == conv_fsize, __FILE__, __LINE__,
2391 "CodeCvt<%s>::in() converted %i external characters, "
2392 "expected %i", tname, n_ext_cvt, conv_fsize);
2393
2394 rw_assert (n_int_cvt == noconv_fsize, __FILE__, __LINE__,
2395 "CodeCvt<%s>::in() produced %i internal characters, "
2396 "expected %i", tname, n_int_cvt, noconv_fsize);
2397
2398 // verify that the contents of the unconverted internal buffer
2399 // (i.e., the text of this file in internal representation) are
2400 // the same as the contents of the converted internal buffer
2401 // (i.e., the text of this file converted to the external
2402 // state-dependent representation and then converted back to
2403 // the internal encoding)
2404 rw_assert (!std::memcmp (noconv_intbuf, conv_intbuf, n_int_cvt),
2405 __FILE__, __LINE__, "code conversion mismatch");
2406
2407
2408 // read in the text of the temporary file encoded in
2409 // a state-dependent encoding and convert it to its
2410 // internal representation
2411 Filebuf conv_in;
2412
2413 conv_in.pubimbue (std::locale (std::locale::classic (), &cvt));
2414
2415 conv_in.open (tmpfname, std::ios::in);
2416
2417 std::memset (conv_intbuf, 0, noconv_fsize * sizeof *conv_intbuf);
2418
2419 nread = conv_in.sgetn (conv_intbuf, noconv_fsize);
2420
2421 if (nread != noconv_fsize) {
2422 rw_assert (false, __FILE__, __LINE__,
2423 "basic_filebuf<%s>::sgetn (%p, %i) == %i, got %i",
2424 tname, conv_intbuf, noconv_fsize,
2425 noconv_fsize, nread);
2426 delete[] noconv_intbuf;
2427 delete[] noconv_extbuf;
2428 delete[] conv_intbuf;
2429 return;
2430 }
2431
2432 rw_assert (!std::memcmp (noconv_intbuf, conv_intbuf,
2433 n_int_cvt * sizeof *noconv_intbuf),
2434 __FILE__, __LINE__, "code conversion mismatch");
2435
2436 if (!noconv_in.close ())
2437 rw_warn (false, __FILE__, __LINE__,
2438 "basic_filebuf<%s>(" __FILE__ ").close() failed",
2439 tname);
2440
2441 if (!conv_in.close ())
2442 rw_warn (false, __FILE__, __LINE__,
2443 "basic_filebuf<%s>(\"%s\").close() failed",
2444 tname, tmpfname);
2445
2446 rw_info (0, __FILE__, __LINE__,
2447 "pubseekoff() and pubseekpos() with state-dependent "
2448 "code conversion");
2449
2450 noconv_in.open (__FILE__, std::ios::binary | std::ios::in);
2451 conv_in.open (tmpfname, std::ios::binary | std::ios::in);
2452
2453 for (std::streamsize i = 0; i < noconv_fsize; i += noconv_fsize / 13) {
2454
2455 typedef typename Filebuf::pos_type pos_type;
2456
2457 noconv_in.pubseekoff (0, std::ios::beg);
2458 conv_in.pubseekoff (0, std::ios::beg);
2459
2460 std::memset (noconv_intbuf, 0, noconv_fsize * sizeof *noconv_intbuf);
2461 std::memset (conv_intbuf, 0, noconv_fsize * sizeof *conv_intbuf);
2462
2463 nread = noconv_in.sgetn (noconv_intbuf, i);
2464
2465 if (i != nread) {
2466 rw_assert (false, __FILE__, __LINE__,
2467 "basic_filebuf<%s>::sgetn (%p, %d) == %d, "
2468 "got %d", tname, noconv_intbuf, i, i, nread);
2469 break;
2470 }
2471
2472 nread = conv_in.sgetn (conv_intbuf, i);
2473
2474 if (i != nread) {
2475 rw_assert (false, __FILE__, __LINE__,
2476 "basic_filebuf<%s>::sgetn (%p, %d) == %d, "
2477 "got %d", tname, conv_intbuf, i, i, nread);
2478 break;
2479 }
2480
2481 if (std::memcmp (noconv_intbuf, conv_intbuf,
2482 i * sizeof *conv_intbuf)) {
2483 rw_assert (false, __FILE__, __LINE__,
2484 "%d. code conversion mismatch", i);
2485 break;
2486 }
2487
2488 const pos_type noconv_pos =
2489 noconv_in.pubseekoff (0, std::ios::cur);
2490
2491 const pos_type conv_pos =
2492 conv_in.pubseekoff (0, std::ios::cur);
2493
2494 std::memset (noconv_intbuf, 0, noconv_fsize * sizeof *noconv_intbuf);
2495 std::memset (conv_intbuf, 0, noconv_fsize * sizeof *conv_intbuf);
2496
2497 nread = noconv_in.sgetn (noconv_intbuf, noconv_fsize);
2498
2499 if (noconv_fsize - i != nread) {
2500 rw_assert (false, __FILE__, __LINE__,
2501 "basic_filebuf<%s>::sgetn (%p, %d) == %d, "
2502 "got %d", tname, noconv_intbuf,
2503 noconv_fsize, noconv_fsize - i, nread);
2504 break;
2505 }
2506
2507 nread = conv_in.sgetn (conv_intbuf, noconv_fsize);
2508
2509 if (noconv_fsize - i != nread) {
2510 rw_assert (false, __FILE__, __LINE__,
2511 "basic_filebuf<%s>::sgetn (%p, %d) == %d, "
2512 "got %d", tname, conv_intbuf,
2513 noconv_fsize, noconv_fsize - i, nread);
2514 break;
2515 }
2516
2517 if (std::memcmp (noconv_intbuf, conv_intbuf,
2518 nread * sizeof *noconv_intbuf)) {
2519 rw_assert (false, __FILE__, __LINE__,
2520 "%d. data mismatch after conversion", i);
2521 break;
2522 }
2523
2524 if (!noconv_in.close ())
2525 rw_warn (false, __FILE__, __LINE__,
2526 "basic_filebuf<%s>(" __FILE__ ").close() failed",
2527 tname);
2528
2529 if (!conv_in.close ())
2530 rw_warn (false, __FILE__, __LINE__,
2531 "basic_filebuf<%s>(\"%s\").close() failed",
2532 tname, tmpfname);
2533
2534 noconv_in.open (__FILE__, std::ios::binary | std::ios::in);
2535 conv_in.open (tmpfname, std::ios::binary | std::ios::in);
2536
2537 const pos_type noconv_pos_new = noconv_in.pubseekpos (noconv_pos);
2538
2539 if (noconv_pos_new != noconv_pos) {
2540
2541 std::mbstate_t noconv_state = noconv_pos.state ();
2542 std::mbstate_t noconv_state_new = noconv_pos_new.state ();
2543
2544 rw_assert (false, __FILE__, __LINE__,
2545 "basic_filebuf<%s>::pubseekpos ({ %d, %d }, "
2546 "ios_base::cur) == { %d, %d }, got { %d, %d }",
2547 tname,
2548 off_type (noconv_pos), *(char*)&noconv_state,
2549 off_type (noconv_pos), *(char*)&noconv_state,
2550 off_type (noconv_pos_new),
2551 *(char*)&noconv_state_new);
2552 break;
2553 }
2554
2555 const pos_type conv_pos_new = conv_in.pubseekpos (conv_pos);
2556
2557 if (conv_pos_new != conv_pos) {
2558 std::mbstate_t conv_state = noconv_pos.state ();
2559 std::mbstate_t conv_state_new = noconv_pos_new.state ();
2560
2561 rw_assert (false, __FILE__, __LINE__,
2562 "basic_filebuf<%s>::pubseekpos ({ %d, %d }, "
2563 "ios_base::cur) == { %d, %d }, got { %d, %d }",
2564 tname,
2565 off_type (conv_pos), *(char*)&conv_state,
2566 off_type (conv_pos), *(char*)&conv_state,
2567 off_type (conv_pos_new),
2568 *(char*)&conv_state_new);
2569 break;
2570 }
2571
2572 std::memset (noconv_intbuf, 0, noconv_fsize * sizeof *noconv_intbuf);
2573 nread = noconv_in.sgetn (noconv_intbuf, noconv_fsize);
2574
2575 if (noconv_fsize - i != nread) {
2576 rw_assert (false, __FILE__, __LINE__,
2577 "basic_filebuf<%s>::sgetn (%p, %d) == %d, "
2578 "got %d (no code conversion)",
2579 tname, noconv_intbuf,
2580 noconv_fsize, noconv_fsize - i, nread);
2581 break;
2582 }
2583
2584 if (std::memcmp (noconv_intbuf, conv_intbuf,
2585 nread * sizeof *noconv_intbuf)) {
2586 rw_assert (false, __FILE__, __LINE__,
2587 "data mismatch (no code conversion)");
2588 break;
2589 }
2590
2591 std::memset (conv_intbuf, 0, noconv_fsize * sizeof *conv_intbuf);
2592 nread = conv_in.sgetn (conv_intbuf, noconv_fsize);
2593
2594 if (noconv_fsize - i != nread) {
2595 rw_assert (false, __FILE__, __LINE__,
2596 "basic_filebuf<%s>::sgetn (%p, %d) == %d, "
2597 "got %d (with code conversion)",
2598 tname, conv_intbuf,
2599 noconv_fsize, noconv_fsize - i, nread);
2600 break;
2601 }
2602
2603 if (std::memcmp (noconv_intbuf, conv_intbuf,
2604 nread * sizeof *noconv_intbuf)) {
2605 rw_assert (false, __FILE__, __LINE__,
2606 "code conversion mismatch");
2607 break;
2608 }
2609 }
2610
2611 delete[] noconv_intbuf;
2612 delete[] noconv_extbuf;
2613 delete[] conv_intbuf;
2614
2615 if (!noconv_in.close ())
2616 rw_warn (false, __FILE__, __LINE__,
2617 "basic_filebuf<%s>(" __FILE__ ").close() failed",
2618 tname);
2619
2620 if (!conv_in.close ())
2621 rw_warn (false, __FILE__, __LINE__,
2622 "basic_filebuf<%s>(\"%s\").close() failed",
2623 tname, tmpfname);
2624
2625 REMOVE_FILE (tmpfname);
2626}
2627
2628
2631template <class charT>
2632static void
2633test_codecvt_with_seek ()
2634{
2635 rw_info (0, __FILE__, __LINE__,
2636 "interleaved input, output, and seeks in ios_base::app "
2637 "mode with state-dependent code conversion");
2638
2639 typedef std::basic_filebuf<charT, std::char_traits<charT> > FileBuf;
2640
2641 FileBuf inbuf;
2642
2643 // open this source file
2644 inbuf.open (__FILE__, std::ios::in);
2645
2646 const std::streamsize N = 0xfffff;
2647 charT *buf = new charT [N];
2648
2649 // read the text of the file into a local buffer
2650 const std::streamsize nchars = inbuf.sgetn (buf, N);
2651
2652 if (nchars <= 0) {
2653 rw_assert (false, __FILE__, __LINE__,
2654 "sgetn(..., %d) > 0, got %d", N, nchars);
2655 delete[] buf;
2656 return;
2657 }
2658
2659 inbuf.close ();
2660
2661 FileBuf iobuf;
2662
2663 // imbue a locale containing a CodeCvt<charT> facet into the filebuf
2664 iobuf.pubimbue (std::locale (std::locale::classic (), new CodeCvt<charT>));
2665
2666 const char* const tmpfname = rw_tmpnam (0);
2667
2668 if (!tmpfname) {
2669 delete[] buf;
2670 return;
2671 }
2672
2673 // remove file just in case it exists (it shouldn't)
2674 std::remove (tmpfname);
2675
2676 // open the filebuf on a temporary file
2677 if (!iobuf.open (tmpfname, std::ios::in | std::ios::out | std::ios::app)) {
2678 rw_assert (false, __FILE__, __LINE__,
2679 "open (\"%s\", ios::in | ios::out | ios::app) failed",
2680 tmpfname);
2681 delete[] buf;
2682 return;
2683 }
2684
2685 charT *pb = buf;
2686
2687 // alterante writes, seeks, and reads to/from the filebuf
2688 for (std::streamsize n = nchars, i = nchars; n; i = n / 2) {
2689
2690 const std::streamsize nput = iobuf.sputn (pb, n - i);
2691
2692 iobuf.pubseekoff (0, std::ios::beg);
2693
2694 charT *localbuf = new charT [N];
2695 const std::streamsize nget = iobuf.sgetn (localbuf, n - i);
2696
2697 // verify the consistency of the read data
2698 if (nget != n - i || std::memcmp (buf, localbuf, nget * sizeof *buf)) {
2699
2700 rw_assert (false, __FILE__, __LINE__,
2701 "sgetn(..., %d) == %d, got %d (or inconsistent data)",
2702 n - i, n - i, nget);
2703 delete[] localbuf;
2704 break;
2705 }
2706
2707 delete[] localbuf;
2708
2709 iobuf.pubseekoff (0, std::ios::beg);
2710
2711 pb += nput;
2712 n -= nput;
2713
2714 }
2715
2716 iobuf.close ();
2717
2718 // imbue the filebuf object with a locale with the conversion facet
2719 inbuf.pubimbue (std::locale (std::locale::classic (),
2720 new CodeCvt<charT>));
2721
2722 // and open the temporary file one more time
2723 if (!inbuf.open (tmpfname, std::ios::in)) {
2724 rw_assert (false, __FILE__, __LINE__, "");
2725 delete[] buf;
2726
2727 REMOVE_FILE (tmpfname);
2728 return;
2729 }
2730
2731 charT *cvtbuf = new charT [N];
2732
2733 // read the whole file into a temporary buffer
2734 std::streamsize ncvt = inbuf.sgetn (cvtbuf, N);
2735
2736 // verify that the contents are consistent with the original data
2737 if (nchars != ncvt || std::memcmp (buf, cvtbuf, nchars * sizeof *buf)) {
2738 rw_assert (false, __FILE__, __LINE__,
2739 "sgetn(..., %d) == %d, got %d (or inconsistent data)",
2740 N, nchars, ncvt);
2741
2742 }
2743
2744 delete[] buf;
2745 delete[] cvtbuf;
2746
2747 // close file before removing it
2748 inbuf.close ();
2749
2750 REMOVE_FILE (tmpfname);
2751}
2752
2753
2756template <class charT>
2757static void
2758do_test (const char* tname)
2759{
2760 rw_info (0, __FILE__, __LINE__, "std::basic_filebuf<%s>", tname);
2761
2762 // exercise basic_filebuf constructors
2763 test_ctors<charT> (tname);
2764
2765 // exercise basic_filebuf::open()
2766 test_open<charT> (tname);
2767
2768 // exercise basic_filebuf::sync()
2769 test_sync<charT> (tname);
2770
2771 // exercise the basic_filebuf::attach() and detach() extensions
2772 test_attach<charT> (tname);
2773
2774 // exercise basic_filebuf functionality with a non-trivial
2775 // codecvt facet that implements stateful conversion
2776 test_codecvt<charT> (tname);
2777 test_codecvt_with_seek<charT> ();
2778}
2779
2780
2781static int
2782run_test (int /*argc*/, char* /*argv*/ [])
2783{
2784 do_test<char> ("char");
2785
2786#ifndef _RWSTD_NO_WCHAR_T
2787
2788 do_test<wchar_t> ("wchar_t");
2789
2790#endif // _RWSTD_NO_WCHAR_T
2791
2792 return 0;
2793}
2794
2795
2796/*extern*/ int
2797main (int argc, char* argv [])
2798{
2799 return rw_test (argc, argv, __FILE__,
2800 "[lib.filebuf]",
2801 "", // no comment
2802 run_test, "", 0);
2803}
2804
const char lower[]
const char upper[]
const char cntrl[]
const char punct[]
#define N
Definition 17.names.cpp:157
#define i
Definition 17.names.cpp:126
#define nchars
Definition 17.names.cpp:502
#define res
Definition 17.names.cpp:531
#define Traits
Definition 17.names.cpp:556
#define buf
Definition 17.names.cpp:452
#define off
Definition 17.names.cpp:507
#define charT
Definition 17.names.cpp:456
#define tmp
Definition 17.names.cpp:553
#define len
Definition 17.names.cpp:488
#define n
Definition 17.names.cpp:130
#define inx
Definition 17.names.cpp:484
#define mode
Definition 17.names.cpp:497
const char *const data[]
#define open(f, m)
#define close(f)
void test_ctors()
void do_test(int line, const char *src, std::size_t resoff, ForwardIterator dummy_iter, const T *, const char *predname)
void run_test(const ForwardIterator &dummy, T *, Predicate *pred)
const char *const escapes[]
#define ASSERT_OPEN(expr, mode, txt)
const char *const charnames[]
#define SIGPIPE
#define BEGIN_MODE(openmode, ext)
void ignore_signal(int)
std::size_t find_char(const char **pbeg, const char *end, const char *const chartbl[], std::size_t tblsize)
#define REMOVE_FILE(tmpfname)
const int end
const int cur
#define main
void foobar()
#define _THROWS()
#define _RWSTD_UNUSED(arg)
Definition _defs.h:972
#define _RWSTD_STATIC_CAST(T, x)
Definition _defs.h:534
_TEST_EXPORT int rw_test(int argc, char *argv[], const char *filename, const char *clause, const char *comment, int(*testfun)(int, char **), const char *optspec,...)
_TEST_EXPORT int rw_info(int, const char *, int, const char *,...)
Definition driver.cpp:1681
_TEST_EXPORT int rw_assert(int, const char *, int, const char *,...)
Definition driver.cpp:1630
_TEST_EXPORT int rw_warn(int, const char *, int, const char *,...)
Definition driver.cpp:1647
_TEST_EXPORT const char * rw_tmpnam(char *)
Definition file.cpp:206
#define DEV_NULL
Definition rw_file.h:40
_TEST_EXPORT _RWSTD_SIZE_T rw_fwrite(const char *, const void *, _RWSTD_SIZE_T=~0, const char *="w")
_TEST_EXPORT int rw_nextfd(int *)
Definition file.cpp:478
_TEST_EXPORT void * rw_fread(const char *, _RWSTD_SIZE_T *=0, const char *="r")
_TEST_EXPORT int rw_strncmp(const char *, const char *, _RWSTD_SIZE_T=_RWSTD_SIZE_MAX, int=CMP_NULTERM)
std::codecvt< charT, char, std::mbstate_t > Base
virtual bool do_always_noconv() const _THROWS(())
CodeCvt(std::size_t ref=0)
Base::intern_type intern_type
Base::extern_type extern_type
virtual int do_length(state_type &, const extern_type *, const extern_type *, std::size_t) const
virtual int do_encoding() const _THROWS(())
Base::state_type state_type
virtual std::codecvt_base::result do_in(state_type &, const extern_type *, const extern_type *, const extern_type *&, intern_type *, intern_type *, intern_type *&) const
virtual std::codecvt_base::result do_out(state_type &, const intern_type *, const intern_type *, const intern_type *&, extern_type *, extern_type *, extern_type *&) const
virtual int do_max_length() const _THROWS(())
virtual std::codecvt_base::result do_unshift(state_type &, extern_type *, extern_type *, extern_type *&) const