text: //
  // corecrt_internal_stdio.h
  //
  //      Copyright (c) Microsoft Corporation.  All rights reserved.
  //
  // This internal header defines internal utilities for working with the stdio
  // library.
  //
  #pragma once
  
  #include <corecrt_internal.h>
  #include <corecrt_internal_lowio.h>
  #include <corecrt_internal_traits.h>
  #include <io.h>
  #include <mbstring.h>
  #include <stdio.h>
  #include <stdint.h>
  #include <stdlib.h>
  #include <string.h>
  
  #pragma pack(push, _CRT_PACKING)
  
  
  
  //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //
  // Stream State Flags
  //
  //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  enum : long
  {
      // Mode bits:  These bits control the stream mode.  A stream may be in one
      // of three modes:  read mode, write mode, or update (read/write) mode.  At
      // least one of these bits will be set for any open stream.
      //
      // If the stream is open in read mode or write mode, then only the _IOREAD
      // or _IOWRITE bit will be set.
      //
      // If the stream is open in update (read/write) mode, then the _IOUPDATE bit
      // will be set.  Further state must also be tracked for update mode streams.
      // Read and write operations cannot be mixed willy-nilly:  in most cases, a
      // flush or reposition must take place in order to transition between reading
      // and writing.  So, for update mode streams, if the next operation must be
      // a read, the _IOREAD bit is set, and if the next operation must be a write,
      // the _IOWRITE bit is set.
      _IOREAD           = 0x0001,
      _IOWRITE          = 0x0002,
      _IOUPDATE         = 0x0004,
  
      // Stream state bits:  These bits track the state of the stream.  The _IOEOF
      // and _IOERROR flags track the end-of-file and error states, respectively,
      // which are reported by feof() and ferror().  The _IOCTRLZ flag is when the
      // last read ended because a Ctrl+Z was read; it corresponds to the lowio
      // FEOFLAG state.
      _IOEOF            = 0x0008,
      _IOERROR          = 0x0010,
      _IOCTRLZ          = 0x0020,
  
      // Buffering state bits:  These track the buffering mode of the stream:
      //
      // (*) CRT:      The buffer was allocated by the CRT via the usual mechanism
      //               (typically via __acrt_stdio_allocate_buffer_nolock, and of
      //               size _INTERNAL_BUFSIZ).
      //
      // (*) USER:     The buffer was allocated by the user and was configured via
      //               the setvbuf() function.
      //
      // (*) SETVBUF:  The buffer was set via the setvbuf() function.  This flag
      //               may be combined with either the CRT or USER flag, depending
      //               on who owns the buffer (based on how setvbuf() was called).
      //
      // (*) STBUF:    The buffer was set via a call to the
      //               __acrt_stdio_begin_temporary_buffering_nolock() function,
      //               which provides a temporary buffer for console I/O operations
      //               to improve the performance of bulk read or write operations.
      //
      // (*) NONE:     Buffering is disabled, either because it was explicitly
      //               disabled or because the CRT attempted to allocate a buffer
      //               but allocation failed.  When this flag is set, the internal
      //               two-byte character buffer is used.
      //
      // Note that these flags are related to, but distinct from, the public stdio
      // buffering flags that are used with setvbuf (_IOFBF, _IOLBF, and _IONBF).
      // Specifically, note that those flags are never or'ed into the flags for a
      // stream.
      _IOBUFFER_CRT     = 0x0040,
      _IOBUFFER_USER    = 0x0080,
      _IOBUFFER_SETVBUF = 0x0100,
      _IOBUFFER_STBUF   = 0x0200,
      _IOBUFFER_NONE    = 0x0400,
  
      // Commit-on-flush state bit:  When this flag is set, every flush operation
      // on the stream also commits the file to disk.
      _IOCOMMIT         = 0x0800,
  
      // String state bit:  When this flag is set, it indicates that the stream is
      // backed by a string, not a file.  String-backed streams are not exposed to
      // user code; they are created internally to support formatted I/O to string
      // buffers (e.g. the sprintf and sscanf families of functions).  If a stream
      // is backed by a string, its lock is not initialized and no synchronization
      // is required.
      _IOSTRING         = 0x1000,
  
      // Allocation state bit:  When this flag is set it indicates that the stream
      // is currently allocated and in-use.  If this flag is not set, it indicates
      // that the stream is free and available for use.
      _IOALLOCATED      = 0x2000,
  };
  
  
  
  #ifndef _M_CEE
  
  
  
  //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //
  // Internal Stream Types (__crt_stdio_stream and friends)
  //
  //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  struct __crt_stdio_stream_data
  {
      union
      {
          FILE  _public_file;
          char* _ptr;
      };
  
      char*            _base;
      int              _cnt;
      long             _flags;
      long             _file;
      int              _charbuf;
      int              _bufsiz;
      char*            _tmpfname;
      CRITICAL_SECTION _lock;
  };
  
  // Ensure that __crt_stdio_stream_data* and FILE* pointers are freely convertible:
  static_assert(
      offsetof(__crt_stdio_stream_data, _public_file) == 0,
      "FILE member of __crt_stdio_stream_data is not at offset zero."
      );
  
  static_assert(
      sizeof(FILE) == sizeof(void*),
      "FILE structure has unexpected size."
      );
  
  
  class __crt_stdio_stream
  {
  public:
  
      __crt_stdio_stream() throw()
          : _stream(nullptr)
      {
      }
  
      explicit __crt_stdio_stream(FILE* const stream) throw()
          : _stream(reinterpret_cast<__crt_stdio_stream_data*>(stream))
      {
      }
  
      explicit __crt_stdio_stream(__crt_stdio_stream_data* const stream) throw()
          : _stream(stream)
      {
      }
  
      bool  valid()         const throw() { return _stream != nullptr;     }
      FILE* public_stream() const throw() { return &_stream->_public_file; }
  
  
  
      // Tests whether this stream is allocated.  Returns true if the stream is
      // currently in use; returns false if the stream is free for allocation.
      bool is_in_use() const throw()
      {
          return (get_flags() & _IOALLOCATED) != 0;
      }
  
      // Attempts to allocate this stream for use.  Returns true if this stream was
      // free and has been allocated for the caller.  Returns false if the stream
      // was in-use and could not be allocated for the caller.  If it returns true,
      // the caller gains ownership of the stream and is responsible for deallocating
      // it.
      bool try_allocate() throw()
      {
          return (_InterlockedOr(&_stream->_flags, _IOALLOCATED) & _IOALLOCATED) == 0;
      }
  
      // Deallocates the stream, freeing it for use by another client.  It is
      // assumed that the caller owns the stream before calling this function.
      void deallocate() throw()
      {
          // Note:  We clear all flags intentionally, so that the stream object
          // is "clean" the next time it is allocated.
          _InterlockedExchange(&_stream->_flags, 0);
      }
  
  
  
      void lock()   const throw() { _lock_file  (public_stream()); }
      void unlock() const throw() { _unlock_file(public_stream()); }
  
      bool has_any_of(long const flags) const throw() { return (get_flags() & flags) != 0;     }
      bool has_all_of(long const flags) const throw() { return (get_flags() & flags) == flags; }
  
      bool set_flags  (long const flags) const throw() { return (_InterlockedOr(&_stream->_flags,   flags) & flags) != 0; }
      bool unset_flags(long const flags) const throw() { return (_InterlockedAnd(&_stream->_flags, ~flags) & flags) != 0; }
  
      bool eof()    const throw() { return has_any_of(_IOEOF);   }
      bool error()  const throw() { return has_any_of(_IOERROR); }
      bool ctrl_z() const throw() { return has_any_of(_IOCTRLZ); }
  
      bool has_crt_buffer()       const throw() { return has_any_of(_IOBUFFER_CRT);                                   }
      bool has_user_buffer()      const throw() { return has_any_of(_IOBUFFER_USER);                                  }
      bool has_temporary_buffer() const throw() { return has_any_of(_IOBUFFER_STBUF);                                 }
      bool has_setvbuf_buffer()   const throw() { return has_any_of(_IOBUFFER_SETVBUF);                               }
      bool has_big_buffer()       const throw() { return has_any_of(_IOBUFFER_CRT | _IOBUFFER_USER);                  }
      bool has_any_buffer()       const throw() { return has_any_of(_IOBUFFER_CRT | _IOBUFFER_USER | _IOBUFFER_NONE); }
  
  
  
      int lowio_handle() const throw() { return __crt_interlocked_read(&_stream->_file); }
  
      bool is_string_backed() const throw() { return (get_flags() & _IOSTRING) != 0; }
  
      __crt_stdio_stream_data* operator->() const throw() { return _stream; }
  
      long get_flags() const throw()
      {
          return __crt_interlocked_read(&_stream->_flags);
      }
  
  private:
  
      __crt_stdio_stream_data* _stream;
  };
  
  // These cannot have C linkage because they use __crt_stdio_stream, which has
  // a destructor.
  __crt_stdio_stream __cdecl __acrt_stdio_allocate_stream() throw();
  void __cdecl __acrt_stdio_free_stream(__crt_stdio_stream _Stream) throw();
  
  
  
  template <typename Action>
  auto __acrt_lock_stream_and_call(FILE* const stream, Action&& action) throw()
      -> decltype(action())
  {
      return __crt_seh_guarded_call<decltype(action())>()(
          [stream]() { _lock_file(stream); },
          action,
          [stream]() { _unlock_file(stream); });
  }
  
  
  
  /*
   * Number of entries supported in the array pointed to by __piob[]. That is,
   * the number of stdio-level files which may be open simultaneously. This
   * is normally set to _NSTREAM_ by the stdio initialization code.
   */
  extern "C" extern int _nstream;
  
  /*
   * Pointer to the array of pointers to FILE structures that are used
   * to manage stdio-level files.
   */
  extern "C" extern __crt_stdio_stream_data** __piob;
  
  // __acrt_stdio_is_initialized cannot be with the rest of
  // stdio initialization logic since referencing those symbols
  // pulls in the stdio initializers.
  inline bool __acrt_stdio_is_initialized() {
      return __piob != 0;
  }
  
  //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //
  // Deprecated stdio functionality
  //
  //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  extern "C" {
  __DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(
      _Success_(return != 0) char*, __RETURN_POLICY_SAME, _ACRTIMP, gets,
      _Pre_notnull_ _Post_z_ _Out_writes_z_(((size_t)-1)), char, _Buffer
      )
  
  // string[0] must contain the maximum length of the string.  The number of
  // characters written is stored in string[1].  The return value is a pointer to
  // string[2] on success; nullptr on failure.
  __DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0_CGETS(
      _Success_(return != 0) char*, _DCRTIMP, _cgets,
      _At_(_Buffer, _Pre_notnull_ _In_reads_(1))
      _At_(_Buffer + 1, _Pre_notnull_ _Out_writes_(1))
      _At_(_Buffer + 2, _Pre_notnull_ _Post_z_ _Out_writes_to_(_Buffer[0], _Buffer[1])),
      char, _Buffer
      )
  
  __DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(
      _Success_(return != 0)
      wchar_t*, __RETURN_POLICY_SAME, _ACRTIMP, _getws,
      _Pre_notnull_ _Post_z_, wchar_t, _Buffer
      )
  
  // string[0] must contain the maximum length of the string.  The number of
  // characters written is stored in string[1].  The return value is a pointer to
  // string[2] on success; nullptr on failure.
  __DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0_CGETS(
      _Post_satisfies_(return == 0 || return == _Buffer + 2)
      _Success_(return != 0) wchar_t*, _DCRTIMP, _cgetws,
      _At_(_Buffer, _In_reads_(1))
      _At_(_Buffer + 1, _Out_writes_(1))
      _At_(_Buffer + 2, _Post_z_ _Out_writes_to_(_Buffer[0], _Buffer[1])),
      wchar_t, _Buffer
      )
  
  } // extern "C"
  //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //
  // Internal stdio functionality
  //
  //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  extern "C" {
  
  
  _Check_return_
  FILE* __cdecl _openfile(
      _In_z_ char const* file_name,
      _In_z_ char const* mode,
      _In_   int         share_flag,
      _Out_  FILE*       stream
      );
  
  _Check_return_
  FILE* __cdecl _wopenfile(
      _In_z_ wchar_t const* file_name,
      _In_z_ wchar_t const* mode,
      _In_   int            share_flag,
      _Out_  FILE*          stream
      );
  
  _Check_return_
  int __cdecl __acrt_stdio_refill_and_read_narrow_nolock(
      _Inout_ FILE* stream
      );
  
  _Check_return_
  int __cdecl __acrt_stdio_refill_and_read_wide_nolock(
      _Inout_ FILE* stream
      );
  
  _Check_return_opt_
  int __cdecl __acrt_stdio_flush_and_write_narrow_nolock(
      _In_    int   c,
      _Inout_ FILE* stream
      );
  
  _Check_return_opt_
  int __cdecl __acrt_stdio_flush_and_write_wide_nolock(
      _In_    int   c,
      _Inout_ FILE* stream
      );
  
  void __cdecl __acrt_stdio_allocate_buffer_nolock(
      _Out_ FILE* stream
      );
  
  void __cdecl __acrt_stdio_free_buffer_nolock(
      _Inout_ FILE* stream
      );
  
  bool __cdecl __acrt_stdio_begin_temporary_buffering_nolock(
      _Inout_ FILE* stream
      );
  
  void __cdecl __acrt_stdio_end_temporary_buffering_nolock(
      _In_    bool  flag,
      _Inout_ FILE* stream
      );
  
  int  __cdecl __acrt_stdio_flush_nolock(_Inout_ FILE* stream);
  
  void __cdecl __acrt_stdio_free_tmpfile_name_buffers_nolock();
  
  #ifndef CRTDLL
      extern int _cflush;
  #endif
  
  extern unsigned int _tempoff;
  extern unsigned int _old_pfxlen;
  
  } // extern "C"
  
  
  
  class __acrt_stdio_temporary_buffering_guard
  {
  public:
  
      explicit __acrt_stdio_temporary_buffering_guard(FILE* const stream) throw()
          : _stream(stream)
      {
          _flag = __acrt_stdio_begin_temporary_buffering_nolock(stream);
      }
  
      __acrt_stdio_temporary_buffering_guard(__acrt_stdio_temporary_buffering_guard const&) throw() = delete;
      void operator=(__acrt_stdio_temporary_buffering_guard const&) throw() = delete;
  
      ~__acrt_stdio_temporary_buffering_guard() throw()
      {
          __acrt_stdio_end_temporary_buffering_nolock(_flag, _stream);
      }
  
  private:
  
      FILE* _stream;
      bool  _flag;
  };
  
  
  
  //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //
  // Character Traits
  //
  //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  template <typename Character>
  struct __acrt_stdio_char_traits;
  
  template <>
  struct __acrt_stdio_char_traits<char> : __crt_char_traits<char>
  {
      static int_type const eof = EOF;
  
      static bool validate_stream_is_ansi_if_required(FILE* const stream) throw()
      {
          _VALIDATE_STREAM_ANSI_RETURN(stream, EINVAL, false);
          return true;
      }
  };
  
  template <>
  struct __acrt_stdio_char_traits<wchar_t> : __crt_char_traits<wchar_t>
  {
      static int_type const eof = WEOF;
  
      static bool validate_stream_is_ansi_if_required(FILE* const stream) throw()
      {
          UNREFERENCED_PARAMETER(stream);
  
          return true; // This validation is only for ANSI functions.
      }
  };
  
  
  
  //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //
  // fopen mode string parser
  //
  //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  // Represents a {lowio, stdio} mode pair.  This is the result of parsing a
  // stdio mode string using the parser, defined below.
  struct __acrt_stdio_stream_mode
  {
      int  _lowio_mode;
      int  _stdio_mode;
      bool _success;
  };
  
  
  
  // This function and the following functions support __acrt_stdio_parse_mode.
  // They handle individual parts of the parsing.
  inline bool __acrt_stdio_parse_mode_plus(__acrt_stdio_stream_mode& result, bool& seen_plus) throw()
  {
      if (seen_plus) {
          return false;
      }
  
      seen_plus = true;
  
      if (result._lowio_mode & _O_RDWR) {
          return false;
      }
  
      result._lowio_mode |= _O_RDWR;
      result._lowio_mode &= ~(_O_RDONLY | _O_WRONLY);
      result._stdio_mode |= _IOUPDATE;
      result._stdio_mode &= ~(_IOREAD | _IOWRITE);
      return true;
  }
  
  inline bool __acrt_stdio_parse_mode_b(__acrt_stdio_stream_mode& result) throw()
  {
      if (result._lowio_mode & (_O_TEXT | _O_BINARY)) {
          return false;
      }
  
      result._lowio_mode |= _O_BINARY;
      return true;
  }
  
  inline bool __acrt_stdio_parse_mode_t(__acrt_stdio_stream_mode& result) throw()
  {
      if (result._lowio_mode & (_O_TEXT | _O_BINARY)) {
          return false;
      }
  
      result._lowio_mode |= _O_TEXT;
      return true;
  }
  
  inline bool __acrt_stdio_parse_mode_c(__acrt_stdio_stream_mode& result, bool& seen_commit_mode) throw()
  {
      if (seen_commit_mode) {
          return false;
      }
  
      seen_commit_mode = true;
      result._stdio_mode |= _IOCOMMIT;
      return true;
  }
  
  inline bool __acrt_stdio_parse_mode_n(__acrt_stdio_stream_mode& result, bool& seen_commit_mode) throw()
  {
      if (seen_commit_mode) {
          return false;
      }
  
      seen_commit_mode = true;
      result._stdio_mode &= ~_IOCOMMIT;
      return true;
  }
  
  inline bool __acrt_stdio_parse_mode_S(__acrt_stdio_stream_mode& result, bool& seen_scan_mode) throw()
  {
      if (seen_scan_mode) {
          return false;
      }
  
      seen_scan_mode = true;
      result._lowio_mode |= _O_SEQUENTIAL;
      return true;
  }
  
  inline bool __acrt_stdio_parse_mode_R(__acrt_stdio_stream_mode& result, bool& seen_scan_mode) throw()
  {
      if (seen_scan_mode) {
          return false;
      }
  
      seen_scan_mode = true;
      result._lowio_mode |= _O_RANDOM;
      return true;
  }
  
  inline bool __acrt_stdio_parse_mode_T(__acrt_stdio_stream_mode& result) throw()
  {
      if (result._lowio_mode & _O_SHORT_LIVED) {
          return false;
      }
  
      result._lowio_mode |= _O_SHORT_LIVED;
      return true;
  }
  
  inline bool __acrt_stdio_parse_mode_D(__acrt_stdio_stream_mode& result) throw()
  {
      if (result._lowio_mode & _O_TEMPORARY) {
          return false;
      }
  
      result._lowio_mode |= _O_TEMPORARY;
      return true;
  }
  
  inline bool __acrt_stdio_parse_mode_N(__acrt_stdio_stream_mode& result) throw()
  {
      result._lowio_mode |= _O_NOINHERIT;
      return true;
  }
  
  inline bool __acrt_stdio_parse_mode_x(__acrt_stdio_stream_mode& result) throw()
  {
      if (!(result._lowio_mode & _O_TRUNC)) {
          // 'x' only permitted with 'w'
          return false;
      }
  
      result._lowio_mode |= _O_EXCL;
      return true;
  }
  
  
  
  // Parses a stdio mode string, returning the corresponding pair of lowio and
  // stdio flags.  On success, sets the success flag in the result to true; on
  // failure, sets that flag to false.  All failures are logic errors.
  template <typename Character>
  __acrt_stdio_stream_mode __cdecl __acrt_stdio_parse_mode(
      Character const* const mode
      ) throw()
  {
      typedef __acrt_stdio_char_traits<Character> stdio_traits;
  
      // Note that we value initialize the result, so the success flag is false
      // by default.  This ensures that any premature return will return failure.
      __acrt_stdio_stream_mode result = __acrt_stdio_stream_mode();
      result._stdio_mode = _commode;
  
      // Advance past any leading spaces:
      Character const* it = mode;
      while (*it == ' ')
          ++it;
  
      // Read the first character.  It must be one of 'r', 'w' , or 'a':
      switch (*it)
      {
      case 'r':
          result._lowio_mode = _O_RDONLY;
          result._stdio_mode = _IOREAD;
          break;
  
      case 'w':
          result._lowio_mode = _O_WRONLY | _O_CREAT | _O_TRUNC;
          result._stdio_mode = _IOWRITE;
          break;
  
      case 'a':
          result._lowio_mode = _O_WRONLY | _O_CREAT | _O_APPEND;
          result._stdio_mode = _IOWRITE;
          break;
  
      default:
          _VALIDATE_RETURN(("Invalid file open mode", 0), EINVAL, result);
      }
  
      // Advance past the first character:
      ++it;
  
      // There can be up to seven more optional mode characters:
      // [1] A single '+' character
      // [2] One of 't' or 'b' (indicating text or binary, respectively)
      // [3] One of 'c' or 'n' (enable or disable auto-commit to disk on flush)
      // [4] One of 'S' or 'R' (optimize for sequential or random access)
      // [5] 'T' (indicating the file is short-lived)
      // [6] 'D' (indicating the file is temporary)
      // [7] 'N' (indicating the file should not be inherited by child processes)
      // [8] 'x' (indicating the file must be created and it is an error if it already exists)
      bool seen_commit_mode   = false;
      bool seen_plus          = false;
      bool seen_scan_mode     = false;
      bool seen_encoding_flag = false;
      for (bool continue_loop = true; continue_loop && *it != '\0'; it += (continue_loop ? 1 : 0))
      {
          switch (*it)
          {
          case '+': continue_loop = __acrt_stdio_parse_mode_plus(result, seen_plus);        break;
          case 'b': continue_loop = __acrt_stdio_parse_mode_b   (result);                   break;
          case 't': continue_loop = __acrt_stdio_parse_mode_t   (result);                   break;
          case 'c': continue_loop = __acrt_stdio_parse_mode_c   (result, seen_commit_mode); break;
          case 'n': continue_loop = __acrt_stdio_parse_mode_n   (result, seen_commit_mode); break;
          case 'S': continue_loop = __acrt_stdio_parse_mode_S   (result, seen_scan_mode  ); break;
          case 'R': continue_loop = __acrt_stdio_parse_mode_R   (result, seen_scan_mode  ); break;
          case 'T': continue_loop = __acrt_stdio_parse_mode_T   (result);                   break;
          case 'D': continue_loop = __acrt_stdio_parse_mode_D   (result);                   break;
          case 'N': continue_loop = __acrt_stdio_parse_mode_N   (result);                   break;
          case 'x': continue_loop = __acrt_stdio_parse_mode_x   (result);                   break;
  
          // If we encounter any spaces, skip them:
          case ' ':
              break;
  
          // If we encounter a comma, it begins the encoding specification; we
          // break out of the loop immediately and parse the encoding flag next:
          case ',':
              seen_encoding_flag = true;
              continue_loop = false;
              break;
  
          default:
              _VALIDATE_RETURN(("Invalid file open mode", 0), EINVAL, result);
          }
      }
  
      // Advance past the comma that terminated the loop:
      if (seen_encoding_flag)
          ++it;
  
      while (*it == ' ')
          ++it;
  
      // If we did not encounter the encoding introducer (a comma), make sure we
      // actually reached the end of the mode string.  We are done:
      if (!seen_encoding_flag)
      {
          _VALIDATE_RETURN(*it == '\0', EINVAL, result);
          result._success = true;
          return result;
      }
  
      // Otherwise, we saw the beginning of an encoding; parse it:
      static Character const ccs[]              = { 'c', 'c', 's' };
      static Character const utf8_encoding[]    = { 'U', 'T', 'F', '-', '8' };
      static Character const utf16_encoding[]   = { 'U', 'T', 'F', '-', '1', '6', 'L', 'E' };
      static Character const unicode_encoding[] = { 'U', 'N', 'I', 'C', 'O', 'D', 'E' };
  
      // Make sure it begins with "ccs" (all lowercase)...
      if (stdio_traits::tcsncmp(it, ccs, _countof(ccs)) != 0)
          _VALIDATE_RETURN(("Invalid file open mode", 0), EINVAL, result);
  
      it += _countof(ccs); // Advance past the "ccs"
  
      while (*it == ' ')
          ++it;
  
      if (*it != '=')
          _VALIDATE_RETURN(("Invalid file open mode", 0), EINVAL, result);
  
      ++it; // Advance past the "="
  
      while (*it == ' ')
          ++it;
  
      if (stdio_traits::tcsnicmp(it, utf8_encoding, _countof(utf8_encoding)) == 0)
      {
          it += _countof(utf8_encoding);
          result._lowio_mode |= _O_U8TEXT;
      }
      else if (stdio_traits::tcsnicmp(it, utf16_encoding, _countof(utf16_encoding)) == 0)
      {
          it += _countof(utf16_encoding);
          result._lowio_mode |= _O_U16TEXT;
      }
      else if (stdio_traits::tcsnicmp(it, unicode_encoding, _countof(unicode_encoding)) == 0)
      {
          it += _countof(unicode_encoding);
          result._lowio_mode |= _O_WTEXT;
      }
      else
      {
          _VALIDATE_RETURN(("Invalid file open mode", 0), EINVAL, result);
      }
  
      // Finally, skip any trailing spaces...
      while (*it == ' ')
          ++it;
  
      // ...and ensure there are no characters left:
      _VALIDATE_RETURN(*it == '\0', EINVAL, result);
  
      result._success = true;
      return result;
  }
  
  
  
  //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  //
  // __acrt_common_path_requires_backslash()
  //
  //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  inline bool __cdecl __crt_stdio_path_requires_backslash(char const* const first) throw()
  {
      char const* const last = first + strlen(first);
      if (first == last)
          return false;
  
      if (*(last - 1) == '\\')
      {
          return reinterpret_cast<unsigned char const*>(last - 1)
              != _mbsrchr(reinterpret_cast<unsigned char const*>(first), '\\');
      }
  
      if (*(last - 1) == '/')
          return false;
  
      return true;
  }
  
  inline bool __cdecl __crt_stdio_path_requires_backslash(wchar_t const* const first) throw()
  {
      wchar_t const* const last = first + wcslen(first);
      if (first == last)
          return false;
  
      if (*(last - 1) == L'\\')
          return false;
  
      if (*(last - 1) == L'/')
          return false;
  
      return true;
  }
  
  // Reset the file buffer to be empty and ready for reuse
  inline void __cdecl __acrt_stdio_reset_buffer(__crt_stdio_stream const stream) throw()
  {
      stream->_ptr = stream->_base;
      stream->_cnt = 0;
  }
  
  #endif // !_M_CEE
  
  #pragma pack(pop)
  