// // _sftbuf.cpp // // Copyright (c) Microsoft Corporation. All rights reserved. // // Defines functions that enable and disable temporary buffering and flushing // for stdout and stderr. When __acrt_stdio_begin_temporary_buffering_nolock() // is called for one of these streams, it tests whether the stream is buffered. // If the stream is not buffered, it gives it a temporary buffer, so that we can // avoid making sequences of one-character writes. // // __acrt_stdio_end_temporary_buffering_nolock() must be called to disable // temporary buffering when it is no longer needed. This function flushes the // stream before tearing down the buffer. // // Note that these functions are only to be used for output streams--note that // __acrt_stdio_begin_temporary_buffering_nolock() sets the _IOWRITE flag. These // functions are intended for internal library use only. // #include // Buffer pointers for stdout and stderr extern "C" void* __acrt_stdout_buffer = nullptr; extern "C" void* __acrt_stderr_buffer = nullptr; // If the stream is stdout or stderr and the stream is not buffered, this function // creates and initializes a temporary buffer for the stream. On success, the // buffer is initialized for the stream and 1 is returned. On failure, 0 is // returned. extern "C" bool __cdecl __acrt_stdio_begin_temporary_buffering_nolock( FILE* const public_stream ) { _ASSERTE(public_stream != nullptr); __crt_stdio_stream const stream(public_stream); // Do nothing if the stream is not backed by a TTY device if (!_isatty(_fileno(stream.public_stream()))) return false; void** buffer; if (stream.public_stream() == stdout) buffer = &__acrt_stdout_buffer; else if (stream.public_stream() == stderr) buffer = &__acrt_stderr_buffer; else return false; #ifndef CRTDLL _cflush++; // Force library pre-termination procedure to run #endif // Make sure the stream is not already buffered: if (stream.has_any_buffer()) return false; stream.set_flags(_IOWRITE | _IOBUFFER_USER | _IOBUFFER_STBUF); if (*buffer == nullptr) *buffer = _malloc_crt_t(char, _INTERNAL_BUFSIZ).detach(); if (*buffer == nullptr) { // If we failed to allocate a buffer, use the small character buffer: stream->_base = reinterpret_cast(&stream->_charbuf); stream->_ptr = reinterpret_cast(&stream->_charbuf); stream->_cnt = 2; stream->_bufsiz = 2; return true; } // Otherwise, we have a new buffer, so set it up for use: stream->_base = reinterpret_cast(*buffer); stream->_ptr = reinterpret_cast(*buffer); stream->_cnt = _INTERNAL_BUFSIZ; stream->_bufsiz = _INTERNAL_BUFSIZ; return true; } // If the stream currently has a temporary buffer that was set via a call to // __acrt_stdio_begin_temporary_buffering_nolock(), and if the flag value is // 1, this function flushes the stream and disables buffering of the stream. extern "C" void __cdecl __acrt_stdio_end_temporary_buffering_nolock( bool const flag, FILE* const public_stream ) { __crt_stdio_stream const stream(public_stream); if (!flag) return; if (stream.has_temporary_buffer()) { // Flush the stream and tear down temporary buffering: __acrt_stdio_flush_nolock(stream.public_stream()); stream.unset_flags(_IOBUFFER_USER | _IOBUFFER_STBUF); stream->_bufsiz = 0; stream->_base = nullptr; stream->_ptr = nullptr; } // Note: If we expand the functionality of the _IOBUFFER_STBUF bit to // include other streams, we may want to clear that bit here under an // 'else' clause (i.e., clear bit in the case that we leave the buffer // permanently assigned. Given our current use of the bit, the extra // code is not needed. }