Reactos
1//
2// setvbuf.cpp
3//
4// Copyright (c) Microsoft Corporation. All rights reserved.
5//
6// Defines setvbuf(), which is used to set buffering mode for a stream.
7//
8#include <corecrt_internal_stdio.h>
9#include <corecrt_internal_ptd_propagation.h>
10
11
12
13// Helper for setvbuf() that sets various buffer properties and return zero.
14static int __cdecl set_buffer(
15 __crt_stdio_stream const stream,
16 _In_reads_opt_(buffer_size_in_bytes) char* const buffer,
17 size_t const buffer_size_in_bytes,
18 int const new_flag_bits
19 ) throw()
20{
21 stream.set_flags(new_flag_bits);
22 stream->_bufsiz = static_cast<int>(buffer_size_in_bytes);
23 stream->_ptr = buffer;
24 stream->_base = buffer;
25 stream->_cnt = 0;
26
27 return 0;
28}
29
30
31
32// Controls buffering and buffer size for the specified stream. The array
33// pointed to by 'buffer' is used as a buffer for the stream. If 'buffer' is
34// null, the CRT allocates a buffer of the requested size. The 'type' specifies
35// the type of buffering, which must be one of _IONBF (no buffering) or _IOLBF
36// or _IOFBF (both of which mean full buffering).
37//
38// Returns zero on success; nonzero on failure.
39static int __cdecl _setvbuf_internal(
40 FILE* const public_stream,
41 char* const buffer,
42 int const type,
43 size_t const buffer_size_in_bytes,
44 __crt_cached_ptd_host& ptd
45 )
46{
47 __crt_stdio_stream const stream(public_stream);
48
49 _UCRT_VALIDATE_RETURN(ptd, stream.valid(), EINVAL, -1);
50
51 // Make sure 'type' is one of the three allowed values, and if we are
52 // buffering, make sure the size is between 2 and INT_MAX:
53 _UCRT_VALIDATE_RETURN(ptd, type == _IONBF || type == _IOFBF || type == _IOLBF, EINVAL, -1);
54
55 if (type == _IOFBF || type == _IOLBF)
56 {
57 _UCRT_VALIDATE_RETURN(ptd, 2 <= buffer_size_in_bytes && buffer_size_in_bytes <= INT_MAX, EINVAL, -1);
58 }
59
60 return __acrt_lock_stream_and_call(stream.public_stream(), [&]
61 {
62 // Force the buffer size to be even by masking the low order bit:
63 size_t const usable_buffer_size = buffer_size_in_bytes & ~static_cast<size_t>(1);
64
65 // Flush the current buffer and free it, if it is ours:
66 __acrt_stdio_flush_nolock(stream.public_stream(), ptd);
67 __acrt_stdio_free_buffer_nolock(stream.public_stream());
68
69 // Clear the stream state bits related to buffering. Most of these
70 // should never be set when setvbuf() is called, but it doesn't cost
71 // anything to be safe.
72 stream.unset_flags(_IOBUFFER_CRT | _IOBUFFER_USER | _IOBUFFER_NONE |
73 _IOBUFFER_SETVBUF | _IOBUFFER_STBUF | _IOCTRLZ);
74
75 // Case 1: No buffering:
76 if (type & _IONBF)
77 {
78 return set_buffer(stream, reinterpret_cast<char*>(&stream->_charbuf), 2, _IOBUFFER_NONE);
79 }
80
81 // Cases 2 and 3 (below) cover the _IOFBF and _IOLBF types of buffering.
82 // Line buffering is treated the same as full buffering, so the _IOLBF
83 // bit in the flag is never set. Finally, since _IOFBF is defined to
84 // be zero, full buffering is simply assumed whenever _IONBF is not set.
85
86 // Case 2: Default buffering, CRT-allocated buffer:
87 if (buffer == nullptr)
88 {
89 char* const crt_buffer = _calloc_crt_t(char, usable_buffer_size).detach();
90 if (!crt_buffer)
91 {
92 #ifndef CRTDLL
93 // Force library pre-termination procedure (this is placed here
94 // because the code path should almost never be hit):
95 ++_cflush;
96 #endif
97
98 return -1;
99 }
100
101 return set_buffer(stream, crt_buffer, usable_buffer_size, _IOBUFFER_CRT | _IOBUFFER_SETVBUF);
102 }
103
104 // Case 3: Default buffering, user-provided buffer:
105 return set_buffer(stream, buffer, usable_buffer_size, _IOBUFFER_USER | _IOBUFFER_SETVBUF);
106 });
107}
108
109extern "C" int __cdecl setvbuf(
110 FILE* const public_stream,
111 char* const buffer,
112 int const type,
113 size_t const buffer_size_in_bytes
114 )
115{
116 __crt_cached_ptd_host ptd;
117 return _setvbuf_internal(public_stream, buffer, type, buffer_size_in_bytes, ptd);
118}