Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
27.filebuf.codecvt.cpp
Go to the documentation of this file.
1
29// PROBLEM DESCRIPTION:
30// basic_ofstream<>::overflow() mangles a multibyte sequence when using
31// code conversion. This functionality of the class isn't currently (as
32// of the date of the creation of the test) being exercised by our
33// testsuite due to the lack of a suitable locale (e.g., ja_JP).
34
35// TEST DESCRIPTION:
36// test creates a temporary file and fills it with series of pairs
37// <offset, seq>, where `offset' is the offset from the beginning of
38// the file to the first (decimal) digit of offset, and `seq' is
39// a character in the range [1, CHAR_MAX] possibly converted to an
40// escape or trigraph sequence according to the rules described
41// in 2.3 and 2.13.2
42//
43// the program then exercises the stream's (actually, the stream
44// file buffer's) ability to extract and seek within such a file
45// thus testing the stream's ability to crrectly interact with
46// the codecvt facet installed in the imbued locale
47
48
49#include <rw/_defs.h>
50#if defined (__IBMCPP__) && !defined (_RWSTD_NO_IMPLICIT_INCLUSION)
51 // disable implicit inclusion to work around a limitation
52 // in IBM VisualAge
53# define _RWSTD_NO_IMPLICIT_INCLUSION
54#endif
55
56
57#include <fstream>
58#include <sstream>
59
60#include <climits> // for UCHAR_MAX
61#include <cstdio> // for fclose(), fopen(), fseek(), size_t
62#include <cstdlib> // for abort()
63#include <cstring> // for memset(), strcmp(), strlen()
64#include <cwchar> // for mbstate_t
65
66#include <rw_driver.h>
67#include <rw_file.h>
68
69
71// code conversion facet suitable for replacement of the default
72// codecvt<char, char, mbstate_t> facet
73// cformat::do_out() converts printable ASCII characters into themselves,
74// control characters are converted to standard C escape sequences
75// cformat::do_in() reverses the effect of do_out()
76class cformat: public std::codecvt <char, char, std::mbstate_t>
77{
78 enum {
79 new_line = 0x0001, // convert '\n' to "\n"
80 horizontal_tab = 0x0002, // convert '\t' to "\t"
81 vertical_tab = 0x0004, // convert '\v' to "\v"
82 backspace = 0x0008, // convert '\b' to "\b"
83 carriage_return = 0x0010, // convert '\r' to "\r"
84 form_feed = 0x0020, // convert '\f' to "\f"
85 alert = 0x0040, // convert '\a' to "\a"
86 backslash = 0x0080, // convert '\\' to "\\"
87 question_mark = 0x0100, // convert '?' to "\?"
88 single_quote = 0x0200, // convert '\\'' to "\'"
89 double_quote = 0x0400, // convert '"' to "\""
90 trigraphs = 0x8000, // convert to/from trigrap sequences
91 hex = 0x1000 // hex notation in external representation
92 };
93
94 const int mask; // bitmaps of flags above
95
96public:
97 explicit
98 cformat (std::size_t ref = 0, int m = 0)
99 : std::codecvt<char, char, std::mbstate_t> (ref),
100 mask (m) { /* empty */ }
101
102protected:
103
104 virtual result
105 do_out (state_type&,
106 const intern_type*, const intern_type*,
107 const intern_type*&,
108 extern_type*, extern_type*, extern_type*&) const;
109
110 virtual result
111 do_in (state_type&, const extern_type*,
112 const extern_type*, const extern_type*&,
113 intern_type*, intern_type*, intern_type*&) const;
114
115 virtual result
116 do_unshift (state_type&, extern_type*,
117 extern_type*, extern_type*&) const {
118 // stateless encoding, no conversion necessary
119 return noconv;
120 }
121
122 virtual int
123 do_encoding () const _THROWS (()) {
124 return 0; // variable number of external chars per single internal
125 }
126
127 virtual bool
128 do_always_noconv () const _THROWS (()) {
129 return false; // conversion always necessary
130 }
131
132 // returns the maximum `N' of extern chars in the range [from, from_end)
133 // such that N represents max or fewer internal chars
134 virtual int
135 do_length (state_type&, const extern_type*,
136 const extern_type*, std::size_t) const;
137
138 // returns the max value do_length (s, from, from_end, 1) can return
139 // for any valid range [from, from_end) - see LWG issue 74 (a DR)
140 virtual int
141 do_max_length () const _THROWS (()) {
142 // assume that an internal char occupies at most 4 external chars
143 // this won't hold for e.g. '\x00001' etc., but will hold for all
144 // chars in the hex notation of up to two digits and all chars in
145 // octal notation (which are required to fit in 4 by the standard)
146 return 4;
147 }
148};
149
150
151std::codecvt_base::result
152cformat::do_out ( state_type& /* unused */,
153 const intern_type *from,
154 const intern_type *from_end,
155 const intern_type *&from_next,
156 extern_type *to,
157 extern_type *to_end,
158 extern_type *&to_next) const
159{
160 // assert 22.2.1.5.2, p1 preconditions
161 rw_assert (from <= from_end, __FILE__, __LINE__,
162 "codecvt::do_out (..., from = %#p, from + %d, %#p, "
163 "to = %#p, to + %d, %#p): from <= from_end",
164 from, from_end - from, from_next, to, to_end - to, to_next);
165
166 rw_assert (to <= to_end, __FILE__, __LINE__,
167 "codecvt::do_out (..., from = %#p, from + %d, %#p, "
168 "to = %#p, to + %d, %#p): to <= to_end",
169 from, from_end - from, from_next, to, to_end - to, to_next);
170
171 // assume no conversion will be performed
172 result res = noconv;
173
174 for (from_next = from, to_next = to; from_next != from_end; ++from_next) {
175
176 // out of space
177 if (to_next == to_end) {
178 res = partial;
179 break;
180 }
181
182 // convert to unsigned to make sure comparison works
183 unsigned char ch = *from_next;
184 extern_type esc = extern_type ();
185
186 if (ch < ' ') {
187
188 // convert to a C escape sequence
189 switch (ch) {
190
191 case '\a':
192 if (!(mask & alert)) {
193 ch = 'a';
194 esc = '\\';
195 }
196 break;
197
198 case '\b':
199 if (!(mask & backspace)) {
200 ch = 'b';
201 esc = '\\';
202 }
203 break;
204
205 case '\t':
206 if (!(mask & horizontal_tab)) {
207 ch = 't';
208 esc = '\\';
209 }
210 break;
211
212 case '\n':
213 if (!(mask & new_line)) {
214 ch = 'n';
215 esc = '\\';
216 }
217 break;
218
219 case '\v':
220 if (!(mask & vertical_tab)) {
221 ch = 'v';
222 esc = '\\';
223 }
224 break;
225
226 case '\f':
227 if (!(mask & form_feed)) {
228 ch = 'f';
229 esc = '\\';
230 }
231 break;
232
233 case '\r':
234 if (!(mask & carriage_return)) {
235 ch = 'r';
236 esc = '\\';
237 }
238 break;
239
240 case '\\':
241 if (!(mask & backslash)) {
242 ch = '\\';
243 esc = '\\';
244 }
245 break;
246
247 default:
248 esc = '\\';
249 break;
250 }
251 }
252 else if (ch > '~') {
253 // convert to a C escape sequence (octal)
254 esc = '\\';
255 }
256 else {
257 // escape special characters
258 switch (ch) {
259 case '?':
260 if (!(mask & question_mark))
261 esc = '\\';
262 break;
263
264 case '\'':
265 if (!(mask & single_quote))
266 esc = '\\';
267 break;
268
269 case '"':
270 if (!(mask & double_quote))
271 esc = '\\';
272 break;
273
274 case '\\':
275 if (!(mask & backslash))
276 esc = '\\';
277 break;
278 }
279
280 if (!(mask & trigraphs)) {
281
282 // convert to a trigraph sequence
283 switch (ch) {
284 case '#': ch = '='; esc = '?'; break;
285 case '\\': ch = '/'; esc = '?'; break;
286 case '^': ch = '\''; esc = '?'; break;
287 case '[': ch = '('; esc = '?'; break;
288 case ']': ch = ')'; esc = '?'; break;
289 case '|': ch = '!'; esc = '?'; break;
290 case '{': ch = '<'; esc = '?'; break;
291 case '}': ch = '>'; esc = '?'; break;
292 case '~': ch = '-'; esc = '?'; break;
293 }
294 }
295 }
296
297 // process `ch' and `esc'
298
299 if ('\\' == esc) {
300
301 // conversion was performed
302 res = ok;
303
304 if (ch < ' ' || ch > '~') {
305
306 // need room for an escape followed by three ocal digits
307 if (4 > to_end - to_next) {
308 res = partial;
309 break;
310 }
311
312 static const char digits[] = "0123456789abcdef";
313
314 // add an escape character
315 *to_next++ = esc;
316
317 if (mask & hex) {
318 // add hex representation (exactly three chars)
319 *to_next++ = 'x';
320 *to_next++ = digits [(ch & 0xf0) >> 4];
321 *to_next++ = digits [ch & 0xf];
322 }
323 else {
324 // add octal representation (exactly three digits)
325 *to_next++ = digits [(ch & (7 << 6)) >> 6];
326 *to_next++ = digits [(ch & (7 << 3)) >> 3];
327 *to_next++ = digits [ch & 7];
328 }
329 }
330 else {
331 // need room for an escape followed by a single char
332 if (2 > to_end - to_next) {
333 res = partial;
334 break;
335 }
336
337 // add an escape char followed by the escaped char
338 *to_next++ = esc;
339 *to_next++ = ch;
340 }
341 }
342 else if ('?' == esc) {
343 // need room for a trigraph sequence
344 if (3 > to_end - to_next) {
345 res = partial;
346 break;
347 }
348
349 // conversion was performed
350 res = ok;
351
352 // add a trigraph sequence
353 *to_next++ = '?';
354 *to_next++ = '?';
355 *to_next++ = ch;
356 }
357 else {
358 // not escaped
359 *to_next++ = ch;
360 }
361 }
362
363 if (noconv == res) {
364 // 22.2.1.5.2, p2, Note: no conversion was necessary
365 from_next = from;
366 to_next = to;
367 }
368
369 rw_fatal (from_next >= from && from_next <= from_end, 0, __LINE__,
370 "user-defined codecvt: internal inconsistency");
371
372 rw_fatal (to_next >= to && to_next <= to_end, 0, __LINE__,
373 "user-defined codecvt: internal inconsistency");
374
375 return res;
376}
377
378
379std::codecvt_base::result
380cformat::do_in ( state_type& /* unused */,
381 const extern_type *from,
382 const extern_type *from_end,
383 const extern_type *&from_next,
384 intern_type *to,
385 intern_type *to_end,
386 intern_type *&to_next) const
387{
388 // assert 22.2.1.5.2, p1 preconditions
389 rw_assert (from <= from_end, __FILE__, __LINE__,
390 "codecvt::do_in (..., from = %#p, from + %d, %#p, "
391 "to = %#p, to + %d, %#p): from <= from_end",
392 from, from_end - from, from_next, to, to_end - to, to_next);
393
394 rw_assert (to <= to_end, __FILE__, __LINE__,
395 "codecvt::do_in (..., from = %#p, from + %d, %#p, "
396 "to = %#p, to + %d, %#p) to <= to_end",
397 from, from_end - from, from_next, to, to_end - to, to_next);
398
399 result res = ok;
400
401 for (from_next = from, to_next = to; from_next != from_end; ++from_next) {
402
403 unsigned char ch = *from_next;
404 intern_type c = intern_type ();
405
406 if ('\\' == ch) {
407
408 if (2 > from_end - from_next) {
409 // ok is the correct value to return in this case,
410 // but partial should be handled as well for robustness
411 res = (from_end - (extern_type*)0) % 2 ? ok : partial;
412 break;
413 }
414
415 ch = from_next [1];
416
417 if ('x' == ch) {
418 // interpret a hex escape sequence
419
420 // advance past '\x'
421 const extern_type *next = from_next + 2;
422
423 // parse hex digits until a non-hex digits is encountered
424 for (; ; ++next) {
425
426 if (next == from_end) {
427 // do not advance to the end since there may be
428 // more digits following it (e.g., '\x012' with
429 // from_end pointing at '1' or '2')
430 return partial;
431 }
432
433 ch = *next;
434 if (ch >= '0' && ch <= '9')
435 c = (c << 4) | (ch - '0');
436 else if (ch >= 'a' && ch <= 'f')
437 c = (c << 4) | (ch - 'a' + 10);
438 else if (ch >= 'A' && ch <= 'F')
439 c = (c << 4) | (ch - 'A' + 10);
440 else if (next - from_next > 2)
441 break;
442 else {
443 return error; // non-hex digit immediately after '\x'
444 }
445 }
446
447 // advance to the end of parsed number
448 from_next = next - 1;
449 }
450 else if ('0' <= ch && '7' >= ch) {
451 // interpret a oct escape sequence
452
453 // (tentatively) advance past '\'
454 const extern_type *next = from_next + 1;
455
456 // parse at most three oct digits
457 for (; next - from_next < 4; ++next) {
458
459 if (next == from_end) {
460 // do not advance to the end since there may be
461 // more digits following it (e.g., '\x012' with
462 // from_end pointing at '1' or '2')
463 return partial;
464 }
465
466 ch = *next;
467 if (ch >= '0' && ch <= '7')
468 c = (c << 3) | (ch - '0');
469 else if (next - from_next)
470 break;
471 else {
472 // advance to the offending char
473 from_next = next;
474 return error; // non-oct digit immediately after '\'
475 }
476 }
477
478 // advance to the end of parsed number
479 from_next = next - 1;
480 }
481 else {
482 // interpret standard C escape sequence
483 switch (ch) {
484 case 'a': c = '\a'; break;
485 case 'b': c = '\b'; break;
486 case 't': c = '\t'; break;
487 case 'n': c = '\n'; break;
488 case 'v': c = '\v'; break;
489 case 'f': c = '\f'; break;
490 case 'r': c = '\r'; break;
491
492 // optional but allowed and escaped backslash
493 case '?': case '"': case '\'': case '\\': c = ch ; break;
494
495 // bad escape sequence
496 default: return error;
497 }
498
499 // advance past the initial '\'
500 ++from_next;
501 }
502 }
503 else if ('?' == ch && !(mask & trigraphs)) {
504 // (try to) convert a trigraph sequence
505 if ( 2 > from_end - from_next
506 || ('?' == from_next [1] && 3 > from_end - from_next)) {
507 res = partial;
508 break;
509 }
510
511 if ('?' == from_next [1]) {
512
513 // "??" (potentilly) introduces a trigraph sequence
514 switch (from_next [2]) {
515
516 case '=': c = '#'; break;
517 case '/': c = '\\'; break;
518 case '\'': c = '^'; break;
519 case '(': c = '['; break;
520 case ')': c = ']'; break;
521 case '!': c = '|'; break;
522 case '<': c = '{'; break;
523 case '>': c = '}'; break;
524 case '-': c = '~'; break;
525
526 default:
527 // no a trigraph sequence, won't convert
528 c = from_next [0]; // i.e., '?'
529 }
530
531 // skip the leading "??" of a trigraph sequence
532 if (c != from_next [0])
533 from_next += 2;
534 }
535 else
536 // ordinary (not escaped) character
537 c = ch;
538 }
539 else
540 // ordinary (not escaped) character
541 c = ch;
542
543 // to_next may be 0 (when called from do_length())
544 // doing pointer math on invalid pointers (null) has undefined behavior
545 // but will probably work in most cases
546 if (to_next)
547 *to_next = c;
548
549 ++to_next;
550
551 // in case of of the inner loops has reached end
552 if (from_next == from_end)
553 break;
554 }
555
556 rw_fatal (from_next >= from && from_next <= from_end, 0, __LINE__,
557 "user-defined codecvt: internal inconsistency");
558
559 rw_fatal (to_next >= to && (to_next <= to_end || !to_end), 0, __LINE__,
560 "user-defined codecvt: internal inconsistency");
561
562 return res;
563}
564
565
566int
568 const extern_type *from,
569 const extern_type *from_end,
570 std::size_t max) const
571{
572 const extern_type *from_next;
573 intern_type *to_next = 0;
574
575 std::mbstate_t st;
576 std::memset (&st, 0, sizeof st);
577
578 // use do_in() with `to' of 0 to do the computation
579 // doing pointer math on invalid pointers (null) has undefined behavior
580 // but will probably work in most cases
581 do_in (st, from, from_end, from_next,
582 to_next, to_next + max, to_next);
583
584 return int (to_next - (intern_type*)0);
585}
586
587
589// determines file size in bytes
590static std::streamsize
591fsize (const char *fname)
592{
593 std::FILE* const f = std::fopen (fname, "r");
594 if (!f || std::fseek (f, 0, SEEK_END))
595 return -1;
596
597 const std::streamsize size = std::ftell (f);
598
599 std::fclose (f);
600
601 return size;
602}
603
604
606static void
607self_test ()
608{
609 rw_info (0, __FILE__, __LINE__,
610 "user-defined codecvt facet -- self test");
611
612 static const char* const result[] = {
613 "ok", "partial", "error", "noconv"
614 };
615
616 // user-defined code conversion facet
617 cformat fmt (1);
618
619 // original array of internal characters and one to which to convert
620 // an external representation back to (for comparison)
621 cformat::intern_type intrn [2][256] = { { '\0' } };
622
623 // array of external chars large enough to hold the internal array
624 // each internal char converts to at most 4 external chars
625 cformat::extern_type extrn [1024] = { '\0' };
626
627 // fill internal array with chars from '\1' to '\377'
628 for (std::size_t i = 0; i != sizeof intrn [0] - 1; ++i)
629 intrn [0][i] = cformat::intern_type (i + 1);
630
631 const cformat::intern_type *intrn_next_0 = 0;
632 cformat::intern_type *intrn_next_1 = 0;
633
634 cformat::extern_type *extrn_next = 0;
635
636
637 // dummy (state not used, conversion is stateless)
638 std::mbstate_t st;
639 std::memset (&st, 0, sizeof st);
640
641 // convert internal to external representation, substituting
642 // escape sequences for non-printable characters
643 std::codecvt_base::result res;
644
645 // convert array in internal representation to external representation
646 res = fmt.out (st,
647 intrn [0], intrn [0] + sizeof intrn [0], intrn_next_0,
648 extrn, extrn + sizeof extrn, extrn_next);
649
650 rw_assert (std::codecvt_base::ok == res, 0, __LINE__,
651 "codecvt::out (); result == codecvt_base::ok, "
652 "got codecvt_base::%s", result [res]);
653
654 // assert that the external sequence is longer than the internal one
655 rw_assert (extrn_next - extrn > intrn_next_0 - intrn [0], 0, __LINE__,
656 "codecvt::out (); converted size %d, expected > %d",
657 extrn_next - extrn, intrn_next_0 - intrn [0]);
658
659 // convert external to internal representation, parsing
660 // multi-char escape sequences into single chars
661
662 const cformat::intern_type *next = extrn;
663
664 intrn_next_1 = intrn [1];
665
666 for (; next != extrn_next; ) {
667
668 // allow only a small buffer space to exercise partial conversion
669 std::size_t step = std::size_t (extrn_next - next);
670 if (step > 12)
671 step = 5 + step % 8;
672
673 res = fmt.in (st,
674 next, next + step, next,
675 intrn_next_1, intrn [1] + sizeof intrn [1], intrn_next_1);
676
677 if (std::codecvt_base::error == res)
678 break;
679 }
680
681 // assert that entrire sequence converted ok
682 rw_assert (std::codecvt_base::ok == res, 0, __LINE__,
683 "codecvt::in (); result == codecvt_base::ok, got "
684 "codecvt_base::%s at offset %d", result [res], next - extrn);
685
686 rw_assert (intrn_next_1 == intrn [1] + sizeof intrn [1], 0, __LINE__,
687 "codecvt::in (); to_next == %#p, got %#p",
688 intrn [1] + sizeof intrn [1], intrn_next_1);
689
690 rw_assert (0 == std::strcmp (intrn [0], intrn [1]), 0, __LINE__,
691 "codecvt<>::out/in ()");
692}
693
694
696#define RW_ASSERT_STATE(strm, state) \
697 rw_assert ((strm).rdstate () == (state), 0, __LINE__, \
698 "rdstate () == %{Is}, got %{Is}", \
699 (state), (strm).rdstate ())
700
701static void
702test_noconv (const char *fname)
703{
704 rw_info (0, 0, __LINE__, "ifstream extraction without conversion");
705
706 std::ifstream f (fname);
707
708 // make sure file stream has been successfully opened
709 RW_ASSERT_STATE (f, std::ios::goodbit);
710
711 // gain public access to protected members
712 struct pubbuf: std::streambuf {
713
714 // working around an MSVC 6.0 bug (PR #26330)
715 typedef std::streambuf Base;
716
717 virtual std::streamsize showmanyc () {
718 return Base::showmanyc ();
719 }
720 char* pubgptr () {
721 return Base::gptr ();
722 }
723 char* pubegptr () {
724 return Base::egptr ();
725 }
726 };
727
728 // use static cast through void* to avoid using reinterpret_cast
729 pubbuf *rdbuf = _RWSTD_STATIC_CAST (pubbuf*, (void*)f.rdbuf ());
730
731 std::streamsize filesize = rdbuf->showmanyc ();
732
733 // exercise 27.5.2.4.3, p1: showmanyc() returns the "estimated"
734 // size of the sequence (i.e., the file size in this case)
735 rw_assert (filesize == fsize (fname), 0, __LINE__,
736 "streambuf::showmanyc () == %ld, got %ld",
737 fsize (fname), filesize);
738
739 // exercise 27.5.2.2.3, p1
740 filesize = f.rdbuf ()->in_avail ();
741
742 const char *gptr = rdbuf->pubgptr ();
743 const char *egptr = rdbuf->pubgptr ();
744
745 rw_assert (filesize == (gptr < egptr ? egptr - gptr : fsize (fname)),
746 0, __LINE__,
747 "streambuf::in_avail () == %ld, got %ld",
748 (gptr < egptr ? egptr - gptr : fsize (fname)), filesize);
749
750 // allocate buffer large enough to accomodate the converted
751 // (i.e. internal) sequence
752 const std::size_t bufsize = 0x10000; // 64k should do it
753 char *tmpbuf = new char [bufsize];
754
755 // fill with non-0 value to check for writes past the end
756 // (see also Onyx incident 14033)
757 std::memset (tmpbuf, '\x7f', bufsize);
758
759 // ecercise putback area
760 std::streamsize i;
761
762#ifndef _RWSTD_PBACK_SIZE
763# define _RWSTD_PBACK_SIZE 1
764#endif // _RWSTD_PBACK_SIZE
765
766 // _RWSTD_PBACK_SIZE is the size of the putback area the library
767 // was configured with; the macro expands to streamsize (N)
768 for (i = 0; i != _RWSTD_PBACK_SIZE + 1; ++i) {
769 // read a few characters, read must not append a '\0'
770 std::streamsize n = f.read (tmpbuf, i).gcount ();
771
772 // assert that read exactly `i' chars, buffer not null-terminared
773 rw_assert (i == n && '\x7f' == tmpbuf [i], 0, __LINE__,
774 "ifstream::read (%#p, %ld) read %ld,"
775 "buffer terminated with '\\%03o'",
776 tmpbuf, i, n, tmpbuf [i]);
777
778 // put back read characters; assert that they are the same
779 // as those in the corresponding positions in the buffer
780 for (std::streamsize j = 0; j != i; ++j) {
781 std::ifstream::int_type c = f.rdbuf ()->sungetc ();
782
783 typedef std::ifstream::traits_type Traits;
784
785 rw_assert (Traits::to_int_type (tmpbuf [n - j - 1]) == c,
786 0, __LINE__,
787 "filebuf::sungetc() == '\\%03o', got '\\%03o",
788 tmpbuf [n - j - 1], c);
789 }
790
791 // re-read characters just put back
792 char buf [_RWSTD_PBACK_SIZE + 1];
793 std::memset (buf, '\x7f', sizeof buf);
794
795 std::streamsize n2 = f.read (buf, i).gcount ();
796
797 // assert that the requested number of chars were read in
798 rw_assert (i == n2 && '\x7f' == buf [i], 0, __LINE__,
799 "ifstream::read (%#p, %ld) read %ld,"
800 "buffer terminated with '\\%03o'",
801 buf, i, n2, buf [i]);
802
803 // assert that the read chars are those that were put back
804 for (std::streamsize k = 0; k != i; ++k) {
805 rw_assert (buf [k] == tmpbuf [k], 0, __LINE__,
806 "buffer mismatch at offset %ld: got '\\%03o', "
807 "expected '\\%03o'", k, buf [k], tmpbuf [k]);
808 // put character back again so that it can be read back in
809 f.rdbuf ()->sungetc ();
810 }
811 }
812
813 // read file contents into buffer (apply no conversion)
814 const std::streamsize n = f.read (tmpbuf, bufsize).gcount ();
815
816 // 27.6.1.3, p28 - read() sets eofbit | failbit
817 // if end-of-file occurs on the input sequence
818 RW_ASSERT_STATE (f, std::ios::eofbit | std::ios::failbit);
819
820 // assert that the entiire file has been read
821 rw_assert (n == filesize && '\x7f' == tmpbuf [n], 0, __LINE__,
822 "ifstream::read (%#p, %ld) read %ld, expected %ld; "
823 "buffer terminated with '\\%03o' [%s]",
824 tmpbuf, bufsize, n, filesize, tmpbuf [n], fname);
825
826 tmpbuf [n] = '\0';
827
828 // assert that file contains no control characters
829 bool b = true;
830 for (i = 0; b && i != UCHAR_MAX + 1; ++i) {
831 if (i >= ' ' || i <= '~')
832 continue;
833 b = 0 == std::strchr (tmpbuf, char (i));
834 }
835
836 rw_assert (b, 0, __LINE__,
837 "unescaped non-printable character '\\#03o' at offset %ld",
838 tmpbuf [i], i);
839
840 delete[] tmpbuf;
841}
842
843
845static void
846test_error (const char *fname)
847{
848 rw_info (0, 0, __LINE__, "ifstream extraction with a conversion error");
849
850 const char outbuf[] = {
851 "abcdefghijklmnopqrstuvwxyz\\x20\\xzzABCDEFGHIJKLMNOPQRSTUVWXYZ"
852 // ^
853 // |
854 // error (invalid hex sequence) ---+
855 };
856
857 // write out a text file containing a conversion error
858 std::ofstream ostrm (fname);
859
860 ostrm << outbuf;
861
862 ostrm.close ();
863
864 // read the file back in using the conversion facet
865 std::ifstream istrm (fname);
866
867 // user-defined code conversion facet
868 const cformat fmt (1 /* prevent locale from deleting */);
869
870 // create a locale by combining the classic locale and our UD facet
871 // cformat; the facet will replace std::codecvt<char, char, mbstate_t>
872 std::locale l (std::locale::classic (), &fmt);
873
874 // imbue locale with formatting facet into streams and save previous
875 l = istrm.imbue (l);
876
877 char inbuf [sizeof outbuf * 4] = { 0 };
878
879 // try to read partial contents of the file
880 // including the conversion error into the buffer
881 istrm.read (inbuf, 26L + 4L /* "a..z" <space> <error> "AB" */);
882
883 // verify that the operation failed, eofbit is set since
884 // less than the requested number of characters have been read
885 RW_ASSERT_STATE (istrm, std::ios::eofbit | std::ios::failbit);
886
887 // verify that the 26 characters 'a' through 'z' plus
888 // the space (i.e., 27 chars) have been extracted
889 rw_assert (27 == istrm.gcount (), 0, __LINE__,
890 "ifstream::read () extracted %d, expected 27",
891 istrm.gcount ());
892
893 rw_assert ( 0 == std::ifstream::traits_type::compare (inbuf, outbuf, 26)
894 && ' ' == inbuf [26], 0, __LINE__,
895 "ifstream::read () got \"%s\", expected \"%.26s \"",
896 inbuf, outbuf);
897
898 istrm.clear ();
899
900 // try to read again, and verify that the operation fails
901 istrm.read (inbuf, sizeof inbuf);
902
903 RW_ASSERT_STATE (istrm, std::ios::eofbit | std::ios::failbit);
904
905 rw_assert (0 == istrm.gcount (), 0, __LINE__,
906 "ifstream::read () extracted %d, expected 0",
907 istrm.gcount ());
908
909 // replace the imbued locale
910 istrm.imbue (l);
911}
912
913
915static void
916test_seek (const char *fname,
917 std::size_t bufsize = std::size_t (-1))
918{
919 std::ifstream f (fname);
920
921 // make sure stream has been successfully opened
922 RW_ASSERT_STATE (f, std::ios::goodbit);
923
924 // set buffer size if specified
925 if (std::size_t (-1) != bufsize) {
926
927 rw_info (0, 0, __LINE__,
928 "ifstream::seekg()/tellg() - %zu byte buffer", bufsize);
929
930 RW_ASSERT_STATE (f, std::ios::goodbit);
931 f.rdbuf ()->pubsetbuf (0, bufsize);
932 }
933 else {
934 rw_info (0, 0, __LINE__,
935 "ifstream::seekg()/tellg() - default buffer size");
936 }
937
938 // user-defined code conversion facet
939 const cformat fmt (1 /* prevent locale from deleting */);
940
941 // create a locale by combining the classic locale and our UD facet
942 // cformat; the facet will replace std::codecvt<char, char, mbstate_t>
943 std::locale l (std::locale::classic (), &fmt);
944
945 // imbue locale with formatting facet into streams and save previous
946 l = f.imbue (l);
947
948 // seek to the beginning of stream (safe)
949 f.seekg (0);
950 RW_ASSERT_STATE (f, std::ios::goodbit);
951
952 const unsigned char max = UCHAR_MAX - '~';
953
954 for (std::size_t n = 0; n != std::size_t (max - 1); ++n) {
955
956 const char delim = char ('~' + n);
957
958 // skip over chars until the terminating delim (and extract it)
959 f.ignore (0x10000, std::fstream::traits_type::to_int_type (delim));
960 rw_assert (f.good (), 0, __LINE__,
961 "istream::ignore (0x10000, '\\%03o'); "
962 "rdstate() = %{Is}, gcount() = %ld",
963 delim, f.rdstate (), f.gcount ());
964
965 // alternate between exercising seekg() and pubsync()
966 if (n % 2) {
967 // seek inplace (offset must be 0 for MB encodings)
968 f.seekg (0, std::ios::cur);
969 RW_ASSERT_STATE (f, std::ios::goodbit);
970 }
971 else {
972 // filebuf::pubsync() must return 0
973 int syn = f.rdbuf ()->pubsync ();
974 rw_assert (0 == syn, 0, __LINE__,
975 "filebuf::pubsync () == 0, got %d", syn);
976 }
977
978 // skip exactly one char forward (retrieve a space)
979 char c = char ();
980 f.get (c);
981 RW_ASSERT_STATE (f, std::ios::goodbit);
982 rw_assert (' ' == c, 0, __LINE__,
983 "istream::get(char_type) got '\\%03o', expected ' '", c);
984
985 // get current file position
986 const std::ifstream::pos_type pos = f.tellg ();
987 RW_ASSERT_STATE (f, std::ios::goodbit);
988
989 // extract offset - should be the same as pos
990 long offset = 0;
991 f >> offset;
992
993 RW_ASSERT_STATE (f, std::ios::goodbit);
994
995 rw_assert (long (pos) == offset, 0, __LINE__,
996 "ifstream::operator>>() expected %ld, got %ld",
997 long (pos), offset);
998
999 // in_avail() must return a value > 0
1000 std::streamsize avail = f.rdbuf ()->in_avail ();
1001 rw_assert (avail > 0, 0, __LINE__,
1002 "filebuf::in_avail() expected > 0, got %ld", avail);
1003
1004 // "rewind" stream to the beginning
1005 f.seekg (0);
1006 RW_ASSERT_STATE (f, std::ios::goodbit);
1007
1008 // try seeking to the previous position
1009 f.seekg (pos);
1010 RW_ASSERT_STATE (f, std::ios::goodbit);
1011 rw_assert (f.tellg () == pos, 0, __LINE__,
1012 "istream::seekg (%ld); tellg () returns %ld",
1013 long (pos), long (f.tellg ()));
1014
1015 // re-read offset - should be the same as file pos
1016 f >> offset;
1017
1018 RW_ASSERT_STATE (f, std::ios::goodbit);
1019
1020 rw_assert (long (pos) == offset, 0, __LINE__,
1021 "ifstream::operator>>() expected %ld, got %ld",
1022 long (pos), offset);
1023 }
1024
1025 // ignore the rest of file, eofbit must be set
1026 f.ignore (0x10000);
1027 RW_ASSERT_STATE (f, std::ios::eofbit);
1028
1029 // in_avail() must return 0
1030 const std::streamsize avail = f.rdbuf ()->in_avail ();
1031 rw_assert (0 == avail, 0, __LINE__,
1032 "filebuf::in_avail() expected 0, got %ld", avail);
1033
1034 // imbue original locale (currently imbued locale
1035 // will be destroyed prior to the destruction of `f')
1036 f.imbue (l);
1037}
1038
1039
1041static int
1042run_test (int, char*[])
1043{
1044 // self-test make sure facet works
1045 self_test ();
1046
1047 // user-defined code conversion facet
1048 const cformat fmt (1 /* prevent locale from deleting */);
1049
1050 // create a locale by combining the classic locale and our UD facet
1051 // cformat; the facet will replace std::codecvt<char, char, mbstate_t>
1052 std::locale l (std::locale::classic (), &fmt);
1053
1054 const char *fname = rw_tmpnam (0);
1055
1056 if (!fname)
1057 return 1;
1058
1059 // will be populated with file offsets and escape sequences
1060 char buffer [4096] = { '\0' };
1061
1062 std::size_t buflen = 0;
1063
1064 // generate file contents using UD conversion
1065 if (1) {
1066 rw_info (0, 0, __LINE__,
1067 "ofstream insertion with multibyte conversion");
1068
1069 std::ofstream f (fname);
1070
1071 // make sure file stream has been successfully opened
1072 RW_ASSERT_STATE (f, std::ios::goodbit);
1073
1074 // imbue locale with formatting facet into stream
1075 f.imbue (l);
1076
1077 for (std::size_t i = 1; i != UCHAR_MAX + 1U; ++i) {
1078
1079 const std::ofstream::pos_type pos = f.tellp ();
1080
1081 RW_ASSERT_STATE (f, std::ios::goodbit);
1082
1083 buflen = std::strlen (buffer);
1084
1085 // append the file offset followed by a (possibly escaped) char
1086 std::sprintf (buffer + buflen, "%ld %c ", long (pos), char (i));
1087
1088 // write out the just appended portion of the buffer
1089 f << (buffer + buflen);
1090
1091 RW_ASSERT_STATE (f, std::ios::goodbit);
1092 }
1093
1094 buflen = std::strlen (buffer);
1095
1096 // file contains the contents of buffer with non-printable
1097 // chars replaced with escape sequences (e.g., tabs with '\t', etc.)
1098 }
1099
1100
1101 // read contents of file w/o conversion
1102 test_noconv (fname);
1103
1104 // read contents of file, apply conversion
1105 if (1) {
1106 rw_info (0, 0, __LINE__,
1107 "ifstream extraction with multibyte conversion");
1108
1109 std::ifstream f (fname);
1110
1111 // make sure file stream has been successfully opened
1112 RW_ASSERT_STATE (f, std::ios::goodbit);
1113
1114 // imbue locale with formatting facet into stream
1115 f.imbue (l);
1116
1117 // allocate buffer large enough to accomodate the converted
1118 // (i.e. internal) sequence
1119 char tmpbuf [sizeof buffer];
1120
1121 // read file contents into buffer, convert escape sequences
1122 // into the corresponding (perhaps unprintable) characters
1123 const std::streamsize n = f.read (tmpbuf, sizeof tmpbuf).gcount ();
1124
1125 // 27.6.1.3, p28 - read() sets eofbit | failbit
1126 // if end-of-file occurs on the input sequence
1127 RW_ASSERT_STATE (f, std::ios::eofbit | std::ios::failbit);
1128
1129 rw_assert (std::size_t (n) == buflen, 0, __LINE__,
1130 "ifstream::read (%#p, %d); read %ld, expected %d",
1131 tmpbuf, sizeof tmpbuf, long (n), buflen);
1132
1133 // assert that converted file contents are the same
1134 // as the originally generated buffer
1135 const long len = std::size_t (n) < buflen ? std::size_t (n) : buflen;
1136 for (std::size_t i = 0; i != len; ++i) {
1137 if (tmpbuf [i] != buffer [i]) {
1138 rw_assert (0, 0, __LINE__,
1139 "'\\%03o' == '\\%03o'; offset %zu",
1140 (unsigned char)buffer [i],
1141 (unsigned char)tmpbuf [i], i);
1142 break;
1143 }
1144 }
1145 }
1146
1147
1148 // test with default buffer
1149 test_seek (fname);
1150
1151 // retest with buffer of user-defined size
1152 for (std::size_t n = 4096; n != std::size_t (-1);
1153 n -= 1024 < n ? 1024 : 256 < n ? 256 : 16 < n ? 16 : 1)
1154 test_seek (fname, n);
1155
1156
1157 // test with errors during conversion
1158 test_error (fname);
1159
1160 // remove a temporary file
1161 std::remove (fname);
1162
1163 return 0;
1164}
1165
1166
1168int main (int argc, char *argv[])
1169{
1170 return rw_test (argc, argv, __FILE__,
1171 "lib.filebuf",
1172 0 /* no comment */,
1173 run_test,
1174 "", 0);
1175}
#define m
Definition 17.names.cpp:129
#define i
Definition 17.names.cpp:126
#define k
Definition 17.names.cpp:128
#define ostrm
Definition 17.names.cpp:512
#define res
Definition 17.names.cpp:531
#define n2
Definition 17.names.cpp:263
#define istrm
Definition 17.names.cpp:486
#define f
Definition 17.names.cpp:119
#define Traits
Definition 17.names.cpp:556
#define buf
Definition 17.names.cpp:452
#define j
Definition 17.names.cpp:127
#define buffer
Definition 17.names.cpp:453
#define offset
Definition 17.names.cpp:508
#define c
Definition 17.names.cpp:111
#define b
Definition 17.names.cpp:110
#define len
Definition 17.names.cpp:488
#define n
Definition 17.names.cpp:130
#define pos
Definition 17.names.cpp:518
volatile int result
void run_test(const ForwardIterator &dummy, T *, Predicate *pred)
#define _RWSTD_PBACK_SIZE
#define RW_ASSERT_STATE(strm, state)
#define main
#define _THROWS()
#define SEEK_END
#define _RWSTD_STATIC_CAST(T, x)
Definition _defs.h:534
virtual int do_max_length() const _THROWS(())
virtual bool do_always_noconv() const _THROWS(())
virtual int do_encoding() const _THROWS(())
virtual result do_out(state_type &, const intern_type *, const intern_type *, const intern_type *&, extern_type *, extern_type *, extern_type *&) const
cformat(std::size_t ref=0, int m=0)
virtual int do_length(state_type &, const extern_type *, const extern_type *, std::size_t) const
virtual result do_unshift(state_type &, extern_type *, extern_type *, extern_type *&) const
virtual result do_in(state_type &, const extern_type *, const extern_type *, const extern_type *&, intern_type *, intern_type *, intern_type *&) const
#define UCHAR_MAX
Definition limits.h:60
Definition json.h:5363
_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_fatal(int expr, const char *filename, int line, const char *fmtspec,...)
Definition driver.cpp:1596
_TEST_EXPORT int rw_assert(int, const char *, int, const char *,...)
Definition driver.cpp:1630
_TEST_EXPORT const char * rw_tmpnam(char *)
Definition file.cpp:206
#define max(a, b)