xref: /reactos/sdk/lib/ucrt/stdio/_flsbuf.cpp (revision 04e0dc4a)
1 //
2 // _flsbuf.cpp
3 //
4 //      Copyright (c) Microsoft Corporation.  All rights reserved.
5 //
6 // Functions that flush a stdio stream buffer and write a character.
7 //
8 #include <corecrt_internal_stdio.h>
9 #include <corecrt_internal_ptd_propagation.h>
10 
stream_is_at_end_of_file_nolock(__crt_stdio_stream const stream)11 static bool __cdecl stream_is_at_end_of_file_nolock(
12     __crt_stdio_stream const stream
13     ) throw()
14 {
15     if (stream.has_any_of(_IOEOF))
16     {
17         return true;
18     }
19 
20     // If there is any data in the buffer, then we are not at the end of the file.
21     if (stream.has_big_buffer() && (stream->_ptr == stream->_base))
22     {
23         return false;
24     }
25 
26     HANDLE const os_handle = reinterpret_cast<HANDLE>(_get_osfhandle(stream.lowio_handle()));
27 
28     // If we fail at querying for the file size, proceed as though we cannot
29     // gather that information. For example, this will fail with pipes.
30     if (os_handle == INVALID_HANDLE_VALUE)
31     {
32         return false;
33     }
34 
35     // Both SetFilePointerEx and GetFileSizeEx are valid ways to determine the
36     // length of a file. We can use that equivalence to check for end-of-file.
37 
38     // This is a racy condition to check - a write or read from another process
39     // could interfere with the size reported from GetFileSizeEx.
40     // In that case, the write function looking to switch from read to write mode
41     // will fail in the usual manner when the write was not possible.
42     LARGE_INTEGER current_position;
43     if (!SetFilePointerEx(os_handle, {}, &current_position, FILE_CURRENT))
44     {
45         return false;
46     }
47 
48     LARGE_INTEGER eof_position;
49     if (!GetFileSizeEx(os_handle, &eof_position))
50     {
51         return false;
52     }
53 
54     return current_position.QuadPart == eof_position.QuadPart;
55 }
56 
57 template <typename Character>
write_buffer_nolock(Character const c,__crt_stdio_stream const stream,__crt_cached_ptd_host & ptd)58 static bool __cdecl write_buffer_nolock(
59     Character          const c,
60     __crt_stdio_stream const stream,
61     __crt_cached_ptd_host&   ptd
62     ) throw()
63 {
64     typedef __acrt_stdio_char_traits<Character> stdio_traits;
65 
66     int const fh = _fileno(stream.public_stream());
67 
68     // If the stream is buffered, write the buffer if there are characters to
69     // be written, then push the character 'c' into the buffer:
70     if (stream.has_big_buffer())
71     {
72         _ASSERTE(("inconsistent IOB fields", stream->_ptr - stream->_base >= 0));
73 
74         int const chars_to_write = static_cast<int>(stream->_ptr - stream->_base);
75         stream->_ptr = stream->_base   + sizeof(Character);
76         stream->_cnt = stream->_bufsiz - static_cast<int>(sizeof(Character));
77 
78         int chars_written = 0;
79         if (chars_to_write > 0)
80         {
81             chars_written = _write_internal(fh, stream->_base, chars_to_write, ptd);
82         }
83         else
84         {
85             if (_osfile_safe(fh) & FAPPEND)
86             {
87                 if (_lseeki64(fh, 0, SEEK_END) == -1)
88                 {
89                     stream.set_flags(_IOERROR);
90                     return stdio_traits::eof;
91                 }
92             }
93         }
94 
95         *reinterpret_cast<Character*>(stream->_base) = c;
96         return chars_written == chars_to_write;
97     }
98     // Otherwise, perform a single character write (if we get here, either
99     // _IONBF is set or there is no buffering):
100     else
101     {
102         return _write_internal(fh, reinterpret_cast<char const*>(&c), sizeof(c), ptd) == sizeof(Character);
103     }
104 }
105 
106 // Flushes the buffer of the given stream and writes the character.  If the stream
107 // does not have a buffer, one is obtained for it.  The character 'c' is written
108 // into the buffer after the buffer is flushed.  This function is only intended
109 // to be called from within the library.  Returns (W)EOF on failure; otherwise
110 // returns the character 'c' that was written to the file (note that 'c' may be
111 // truncated if the int value cannot be represented by the Character type).
112 template <typename Character>
common_flush_and_write_nolock(int const c,__crt_stdio_stream const stream,__crt_cached_ptd_host & ptd)113 static int __cdecl common_flush_and_write_nolock(
114     int                const c,
115     __crt_stdio_stream const stream,
116     __crt_cached_ptd_host&   ptd
117     ) throw()
118 {
119     typedef __acrt_stdio_char_traits<Character> stdio_traits;
120 
121     unsigned const character_mask = (1 << (CHAR_BIT * sizeof(Character))) - 1;
122 
123     _ASSERTE(stream.valid());
124 
125     int const fh = _fileno(stream.public_stream());
126 
127     if (!stream.has_any_of(_IOWRITE | _IOUPDATE))
128     {
129         ptd.get_errno().set(EBADF);
130         stream.set_flags(_IOERROR);
131         return stdio_traits::eof;
132     }
133     else if (stream.is_string_backed())
134     {
135         ptd.get_errno().set(ERANGE);
136         stream.set_flags(_IOERROR);
137         return stdio_traits::eof;
138     }
139 
140     // Check that _IOREAD is not set or, if it is, then so is _IOEOF. Note
141     // that _IOREAD and IOEOF both being set implies switching from read to
142     // write at end-of-file, which is allowed by ANSI. Note that resetting
143     // the _cnt and _ptr fields amounts to doing an fflush() on the stream
144     // in this case. Note also that the _cnt field has to be reset to 0 for
145     // the error path as well (i.e., _IOREAD set but _IOEOF not set) as
146     // well as the non-error path.
147 
148     if (stream.has_any_of(_IOREAD))
149     {
150         bool const switch_to_write_mode = stream_is_at_end_of_file_nolock(stream);
151         stream->_cnt = 0; // in either case, flush buffer
152 
153         if (switch_to_write_mode)
154         {
155             stream->_ptr = stream->_base;
156             stream.unset_flags(_IOREAD);
157         }
158         else
159         {
160             stream.set_flags(_IOERROR);
161             return stdio_traits::eof;
162         }
163     }
164 
165     stream.set_flags(_IOWRITE);
166     stream.unset_flags(_IOEOF);
167 
168     stream->_cnt = 0;
169 
170     // Get a buffer for this stream, if one is necessary:
171     if (!stream.has_any_buffer())
172     {
173         // If the stream uses temporary buffering, we do not set up a single character buffer;
174         // this is so that later temporary buffering will not be thwarted by
175         // the _IONBF flag being set. (See _stbuf() and _ftbuf() for more
176         //information on stdout and stderr buffering.)
177         if (!__acrt_should_use_temporary_buffer(stream.public_stream()))
178         {
179             __acrt_stdio_allocate_buffer_nolock(stream.public_stream());
180         }
181     }
182 
183     // Write the character; return (W)EOF if it fails:
184     if (!write_buffer_nolock(static_cast<Character>(c & character_mask), stream, ptd))
185     {
186         stream.set_flags(_IOERROR);
187         return stdio_traits::eof;
188     }
189 
190     return c & character_mask;
191 }
192 
193 
194 
__acrt_stdio_flush_and_write_narrow_nolock(int const c,FILE * const stream,__crt_cached_ptd_host & ptd)195 extern "C" int __cdecl __acrt_stdio_flush_and_write_narrow_nolock(
196     int                const c,
197     FILE*              const stream,
198     __crt_cached_ptd_host&   ptd
199     )
200 {
201     return common_flush_and_write_nolock<char>(c, __crt_stdio_stream(stream), ptd);
202 }
203 
204 
205 
__acrt_stdio_flush_and_write_wide_nolock(int const c,FILE * const stream,__crt_cached_ptd_host & ptd)206 extern "C" int __cdecl __acrt_stdio_flush_and_write_wide_nolock(
207     int                const c,
208     FILE*              const stream,
209     __crt_cached_ptd_host&   ptd
210     )
211 {
212     return common_flush_and_write_nolock<wchar_t>(c, __crt_stdio_stream(stream), ptd);
213 }
214