1 //
2 // corecrt_internal_stdio.h
3 //
4 //      Copyright (c) Microsoft Corporation.  All rights reserved.
5 //
6 // This internal header defines internal utilities for working with the stdio
7 // library.
8 //
9 #pragma once
10 
11 #include <corecrt_internal.h>
12 #include <corecrt_internal_lowio.h>
13 #include <corecrt_internal_traits.h>
14 #include <io.h>
15 #include <mbstring.h>
16 #include <stdio.h>
17 #include <stdint.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #pragma pack(push, _CRT_PACKING)
22 
23 
24 
25 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
26 //
27 // Stream State Flags
28 //
29 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
30 enum : long
31 {
32     // Mode bits:  These bits control the stream mode.  A stream may be in one
33     // of three modes:  read mode, write mode, or update (read/write) mode.  At
34     // least one of these bits will be set for any open stream.
35     //
36     // If the stream is open in read mode or write mode, then only the _IOREAD
37     // or _IOWRITE bit will be set.
38     //
39     // If the stream is open in update (read/write) mode, then the _IOUPDATE bit
40     // will be set.  Further state must also be tracked for update mode streams.
41     // Read and write operations cannot be mixed willy-nilly:  in most cases, a
42     // flush or reposition must take place in order to transition between reading
43     // and writing.  So, for update mode streams, if the next operation must be
44     // a read, the _IOREAD bit is set, and if the next operation must be a write,
45     // the _IOWRITE bit is set.
46     _IOREAD           = 0x0001,
47     _IOWRITE          = 0x0002,
48     _IOUPDATE         = 0x0004,
49 
50     // Stream state bits:  These bits track the state of the stream.  The _IOEOF
51     // and _IOERROR flags track the end-of-file and error states, respectively,
52     // which are reported by feof() and ferror().  The _IOCTRLZ flag is when the
53     // last read ended because a Ctrl+Z was read; it corresponds to the lowio
54     // FEOFLAG state.
55     _IOEOF            = 0x0008,
56     _IOERROR          = 0x0010,
57     _IOCTRLZ          = 0x0020,
58 
59     // Buffering state bits:  These track the buffering mode of the stream:
60     //
61     // (*) CRT:      The buffer was allocated by the CRT via the usual mechanism
62     //               (typically via __acrt_stdio_allocate_buffer_nolock, and of
63     //               size _INTERNAL_BUFSIZ).
64     //
65     // (*) USER:     The buffer was allocated by the user and was configured via
66     //               the setvbuf() function.
67     //
68     // (*) SETVBUF:  The buffer was set via the setvbuf() function.  This flag
69     //               may be combined with either the CRT or USER flag, depending
70     //               on who owns the buffer (based on how setvbuf() was called).
71     //
72     // (*) STBUF:    The buffer was set via a call to the
73     //               __acrt_stdio_begin_temporary_buffering_nolock() function,
74     //               which provides a temporary buffer for console I/O operations
75     //               to improve the performance of bulk read or write operations.
76     //
77     // (*) NONE:     Buffering is disabled, either because it was explicitly
78     //               disabled or because the CRT attempted to allocate a buffer
79     //               but allocation failed.  When this flag is set, the internal
80     //               two-byte character buffer is used.
81     //
82     // Note that these flags are related to, but distinct from, the public stdio
83     // buffering flags that are used with setvbuf (_IOFBF, _IOLBF, and _IONBF).
84     // Specifically, note that those flags are never or'ed into the flags for a
85     // stream.
86     _IOBUFFER_CRT     = 0x0040,
87     _IOBUFFER_USER    = 0x0080,
88     _IOBUFFER_SETVBUF = 0x0100,
89     _IOBUFFER_STBUF   = 0x0200,
90     _IOBUFFER_NONE    = 0x0400,
91 
92     // Commit-on-flush state bit:  When this flag is set, every flush operation
93     // on the stream also commits the file to disk.
94     _IOCOMMIT         = 0x0800,
95 
96     // String state bit:  When this flag is set, it indicates that the stream is
97     // backed by a string, not a file.  String-backed streams are not exposed to
98     // user code; they are created internally to support formatted I/O to string
99     // buffers (e.g. the sprintf and sscanf families of functions).  If a stream
100     // is backed by a string, its lock is not initialized and no synchronization
101     // is required.
102     _IOSTRING         = 0x1000,
103 
104     // Allocation state bit:  When this flag is set it indicates that the stream
105     // is currently allocated and in-use.  If this flag is not set, it indicates
106     // that the stream is free and available for use.
107     _IOALLOCATED      = 0x2000,
108 };
109 
110 
111 
112 #ifndef _M_CEE
113 
114 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
115 //
116 // Internal stdio functions with PTD propagation
117 //
118 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
119 _Check_return_opt_
120 extern "C" int __cdecl _putch_nolock_internal(
121     _In_    int                    _Ch,
122     _Inout_ __crt_cached_ptd_host& _Ptd
123     );
124 
125 _Check_return_opt_
126 extern "C" wint_t __cdecl _putwch_nolock_internal(
127     _In_    wchar_t                _Ch,
128     _Inout_ __crt_cached_ptd_host& _Ptd
129     );
130 
131 _Check_return_opt_
132 extern "C" wint_t __cdecl _fputwc_nolock_internal(
133     _In_    wchar_t                _Character,
134     _Inout_ FILE*                  _Stream,
135     _Inout_ __crt_cached_ptd_host& _Ptd
136     );
137 
138 _Success_(return != EOF)
139 _Check_return_opt_
140 extern "C" int __cdecl _fputc_nolock_internal(
141     _In_    int                    _Character,
142     _Inout_ FILE*                  _Stream,
143     _Inout_ __crt_cached_ptd_host& _Ptd
144     );
145 
146 _Check_return_opt_
147 extern "C" size_t __cdecl _fwrite_nolock_internal(
148     _In_reads_bytes_(_ElementSize * _ElementCount) void const*            _Buffer,
149     _In_                                           size_t                 _ElementSize,
150     _In_                                           size_t                 _ElementCount,
151     _Inout_                                        FILE*                  _Stream,
152     _Inout_                                        __crt_cached_ptd_host& _Ptd
153     );
154 
155 _Check_return_
156 extern "C" __int64 __cdecl _ftelli64_nolock_internal(
157     _Inout_ FILE*                  _Stream,
158     _Inout_ __crt_cached_ptd_host& _Ptd
159     );
160 
161 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
162 //
163 // Internal Stream Types (__crt_stdio_stream and friends)
164 //
165 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
166 struct __crt_stdio_stream_data
167 {
168     union
169     {
170         FILE  _public_file;
171         char* _ptr;
172     };
173 
174     char*            _base;
175     int              _cnt;
176     long             _flags;
177     long             _file;
178     int              _charbuf;
179     int              _bufsiz;
180     char*            _tmpfname;
181     CRITICAL_SECTION _lock;
182 };
183 
184 // Ensure that __crt_stdio_stream_data* and FILE* pointers are freely convertible:
185 static_assert(
186     offsetof(__crt_stdio_stream_data, _public_file) == 0,
187     "FILE member of __crt_stdio_stream_data is not at offset zero."
188     );
189 
190 static_assert(
191     sizeof(FILE) == sizeof(void*),
192     "FILE structure has unexpected size."
193     );
194 
195 
196 class __crt_stdio_stream
197 {
198 public:
199 
__crt_stdio_stream()200     __crt_stdio_stream() throw()
201         : _stream(nullptr)
202     {
203     }
204 
__crt_stdio_stream(FILE * const stream)205     explicit __crt_stdio_stream(FILE* const stream) throw()
206         : _stream(reinterpret_cast<__crt_stdio_stream_data*>(stream))
207     {
208     }
209 
__crt_stdio_stream(__crt_stdio_stream_data * const stream)210     explicit __crt_stdio_stream(__crt_stdio_stream_data* const stream) throw()
211         : _stream(stream)
212     {
213     }
214 
valid()215     bool  valid()         const throw() { return _stream != nullptr;     }
public_stream()216     FILE* public_stream() const throw() { return &_stream->_public_file; }
217 
218 
219 
220     // Tests whether this stream is allocated.  Returns true if the stream is
221     // currently in use; returns false if the stream is free for allocation.
is_in_use()222     bool is_in_use() const throw()
223     {
224         return (get_flags() & _IOALLOCATED) != 0;
225     }
226 
227     // Attempts to allocate this stream for use.  Returns true if this stream was
228     // free and has been allocated for the caller.  Returns false if the stream
229     // was in-use and could not be allocated for the caller.  If it returns true,
230     // the caller gains ownership of the stream and is responsible for deallocating
231     // it.
try_allocate()232     bool try_allocate() throw()
233     {
234         return (_InterlockedOr(&_stream->_flags, _IOALLOCATED) & _IOALLOCATED) == 0;
235     }
236 
237     // Deallocates the stream, freeing it for use by another client.  It is
238     // assumed that the caller owns the stream before calling this function.
deallocate()239     void deallocate() throw()
240     {
241         // Note:  We clear all flags intentionally, so that the stream object
242         // is "clean" the next time it is allocated.
243         _InterlockedExchange(&_stream->_flags, 0);
244     }
245 
246 
247 
lock()248     void lock()   const throw() { _lock_file  (public_stream()); }
unlock()249     void unlock() const throw() { _unlock_file(public_stream()); }
250 
has_any_of(long const flags)251     bool has_any_of(long const flags) const throw() { return (get_flags() & flags) != 0;     }
has_all_of(long const flags)252     bool has_all_of(long const flags) const throw() { return (get_flags() & flags) == flags; }
253 
set_flags(long const flags)254     bool set_flags  (long const flags) const throw() { return (_InterlockedOr(&_stream->_flags,   flags) & flags) != 0; }
unset_flags(long const flags)255     bool unset_flags(long const flags) const throw() { return (_InterlockedAnd(&_stream->_flags, ~flags) & flags) != 0; }
256 
eof()257     bool eof()    const throw() { return has_any_of(_IOEOF);   }
error()258     bool error()  const throw() { return has_any_of(_IOERROR); }
ctrl_z()259     bool ctrl_z() const throw() { return has_any_of(_IOCTRLZ); }
260 
has_crt_buffer()261     bool has_crt_buffer()       const throw() { return has_any_of(_IOBUFFER_CRT);                                   }
has_user_buffer()262     bool has_user_buffer()      const throw() { return has_any_of(_IOBUFFER_USER);                                  }
has_temporary_buffer()263     bool has_temporary_buffer() const throw() { return has_any_of(_IOBUFFER_STBUF);                                 }
has_setvbuf_buffer()264     bool has_setvbuf_buffer()   const throw() { return has_any_of(_IOBUFFER_SETVBUF);                               }
has_big_buffer()265     bool has_big_buffer()       const throw() { return has_any_of(_IOBUFFER_CRT | _IOBUFFER_USER);                  }
has_any_buffer()266     bool has_any_buffer()       const throw() { return has_any_of(_IOBUFFER_CRT | _IOBUFFER_USER | _IOBUFFER_NONE); }
267 
268 
269 
lowio_handle()270     int lowio_handle() const throw() { return __crt_interlocked_read(&_stream->_file); }
271 
is_string_backed()272     bool is_string_backed() const throw() { return (get_flags() & _IOSTRING) != 0; }
273 
274     __crt_stdio_stream_data* operator->() const throw() { return _stream; }
275 
get_flags()276     long get_flags() const throw()
277     {
278         return __crt_interlocked_read(&_stream->_flags);
279     }
280 
281 private:
282 
283     __crt_stdio_stream_data* _stream;
284 };
285 
286 // These cannot have C linkage because they use __crt_stdio_stream, which has
287 // a destructor.
288 __crt_stdio_stream __cdecl __acrt_stdio_allocate_stream() throw();
289 void __cdecl __acrt_stdio_free_stream(__crt_stdio_stream _Stream) throw();
290 
291 
292 
293 template <typename Action>
294 auto __acrt_lock_stream_and_call(FILE* const stream, Action&& action) throw()
295     -> decltype(action())
296 {
297     return __crt_seh_guarded_call<decltype(action())>()(
298         [stream]() { _lock_file(stream); },
299         action,
300         [stream]() { _unlock_file(stream); });
301 }
302 
303 
304 
305 /*
306  * Number of entries supported in the array pointed to by __piob[]. That is,
307  * the number of stdio-level files which may be open simultaneously. This
308  * is normally set to _NSTREAM_ by the stdio initialization code.
309  */
310 extern "C" int _nstream;
311 
312 /*
313  * Pointer to the array of pointers to FILE structures that are used
314  * to manage stdio-level files.
315  */
316 extern "C" __crt_stdio_stream_data** __piob;
317 
318 // __acrt_stdio_is_initialized cannot be with the rest of
319 // stdio initialization logic since referencing those symbols
320 // pulls in the stdio initializers.
__acrt_stdio_is_initialized()321 inline bool __acrt_stdio_is_initialized() {
322     return __piob != 0;
323 }
324 
325 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
326 //
327 // Deprecated stdio functionality
328 //
329 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
330 extern "C" {
331 __DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(
332     _Success_(return != 0) char*, __RETURN_POLICY_SAME, _ACRTIMP, gets,
333     _Pre_notnull_ _Post_z_ _Out_writes_z_(((size_t)-1)), char, _Buffer
334     )
335 
336 // string[0] must contain the maximum length of the string.  The number of
337 // characters written is stored in string[1].  The return value is a pointer to
338 // string[2] on success; nullptr on failure.
339 __DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0_CGETS(
340     _Success_(return != 0) char*, _DCRTIMP, _cgets,
341     _At_(_Buffer, _Pre_notnull_ _In_reads_(1))
342     _At_(_Buffer + 1, _Pre_notnull_ _Out_writes_(1))
343     _At_(_Buffer + 2, _Pre_notnull_ _Post_z_ _Out_writes_to_(_Buffer[0], _Buffer[1])),
344     char, _Buffer
345     )
346 
347 __DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0(
348     _Success_(return != 0)
349     wchar_t*, __RETURN_POLICY_SAME, _ACRTIMP, _getws,
350     _Pre_notnull_ _Post_z_, wchar_t, _Buffer
351     )
352 
353 // string[0] must contain the maximum length of the string.  The number of
354 // characters written is stored in string[1].  The return value is a pointer to
355 // string[2] on success; nullptr on failure.
356 __DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_0_CGETS(
357     _Post_satisfies_(return == 0 || return == _Buffer + 2)
358     _Success_(return != 0) wchar_t*, _DCRTIMP, _cgetws,
359     _At_(_Buffer, _In_reads_(1))
360     _At_(_Buffer + 1, _Out_writes_(1))
361     _At_(_Buffer + 2, _Post_z_ _Out_writes_to_(_Buffer[0], _Buffer[1])),
362     wchar_t, _Buffer
363     )
364 
365 } // extern "C"
366 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
367 //
368 // Internal stdio functionality
369 //
370 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
371 extern "C" {
372 
373 
374 _Check_return_
375 FILE* __cdecl _openfile(
376     _In_z_ char const* file_name,
377     _In_z_ char const* mode,
378     _In_   int         share_flag,
379     _Out_  FILE*       stream
380     );
381 
382 _Check_return_
383 FILE* __cdecl _wopenfile(
384     _In_z_ wchar_t const* file_name,
385     _In_z_ wchar_t const* mode,
386     _In_   int            share_flag,
387     _Out_  FILE*          stream
388     );
389 
390 _Check_return_
391 int __cdecl __acrt_stdio_refill_and_read_narrow_nolock(
392     _Inout_ FILE* stream
393     );
394 
395 _Check_return_
396 int __cdecl __acrt_stdio_refill_and_read_wide_nolock(
397     _Inout_ FILE* stream
398     );
399 
400 _Check_return_opt_
401 int __cdecl __acrt_stdio_flush_and_write_narrow_nolock(
402     _In_    int                    c,
403     _Inout_ FILE*                  stream,
404     _Inout_ __crt_cached_ptd_host& ptd
405     );
406 
407 _Check_return_opt_
408 int __cdecl __acrt_stdio_flush_and_write_wide_nolock(
409     _In_    int                    c,
410     _Inout_ FILE*                  stream,
411     _Inout_ __crt_cached_ptd_host& ptd
412     );
413 
414 void __cdecl __acrt_stdio_allocate_buffer_nolock(
415     _Out_ FILE* stream
416     );
417 
418 void __cdecl __acrt_stdio_free_buffer_nolock(
419     _Inout_ FILE* stream
420     );
421 
422 bool __cdecl __acrt_stdio_begin_temporary_buffering_nolock(
423     _Inout_ FILE* stream
424     );
425 
426 bool __cdecl __acrt_should_use_temporary_buffer(
427     _In_ FILE* stream
428 );
429 
430 void __cdecl __acrt_stdio_end_temporary_buffering_nolock(
431     _In_    bool                 flag,
432     _Inout_ FILE*                  stream,
433     _Inout_ __crt_cached_ptd_host& ptd
434     );
435 
436 int  __cdecl __acrt_stdio_flush_nolock(
437     _Inout_ FILE*                  stream,
438     _Inout_ __crt_cached_ptd_host& ptd
439     );
440 
441 void __cdecl __acrt_stdio_free_tmpfile_name_buffers_nolock();
442 
443 #ifndef CRTDLL
444     extern int _cflush;
445 #endif
446 
447 extern unsigned int _tempoff;
448 extern unsigned int _old_pfxlen;
449 
450 } // extern "C"
451 
452 
453 
454 class __acrt_stdio_temporary_buffering_guard
455 {
456 public:
457 
__acrt_stdio_temporary_buffering_guard(FILE * const stream,__crt_cached_ptd_host & ptd)458     explicit __acrt_stdio_temporary_buffering_guard(FILE* const stream, __crt_cached_ptd_host& ptd) throw()
459         : _stream(stream), _ptd(ptd)
460     {
461         _flag = __acrt_stdio_begin_temporary_buffering_nolock(stream);
462     }
463 
464     __acrt_stdio_temporary_buffering_guard(__acrt_stdio_temporary_buffering_guard const&) throw() = delete;
465     void operator=(__acrt_stdio_temporary_buffering_guard const&) throw() = delete;
466 
throw()467     ~__acrt_stdio_temporary_buffering_guard() throw()
468     {
469         __acrt_stdio_end_temporary_buffering_nolock(_flag, _stream, _ptd);
470     }
471 
472 private:
473     FILE*                  _stream;
474     __crt_cached_ptd_host& _ptd;
475     bool                   _flag;
476 };
477 
478 
479 
480 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
481 //
482 // Character Traits
483 //
484 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
485 template <typename Character>
486 struct __acrt_stdio_char_traits;
487 
488 template <>
489 struct __acrt_stdio_char_traits<char> : __crt_char_traits<char>
490 {
491     static int_type const eof = EOF;
492 
493     static bool validate_stream_is_ansi_if_required(FILE* const stream) throw()
494     {
495         _VALIDATE_STREAM_ANSI_RETURN(stream, EINVAL, false);
496         return true;
497     }
498 };
499 
500 template <>
501 struct __acrt_stdio_char_traits<wchar_t> : __crt_char_traits<wchar_t>
502 {
503     static int_type const eof = WEOF;
504 
505     static bool validate_stream_is_ansi_if_required(FILE* const stream) throw()
506     {
507         UNREFERENCED_PARAMETER(stream);
508 
509         return true; // This validation is only for ANSI functions.
510     }
511 };
512 
513 
514 
515 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
516 //
517 // fopen mode string parser
518 //
519 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
520 // Represents a {lowio, stdio} mode pair.  This is the result of parsing a
521 // stdio mode string using the parser, defined below.
522 struct __acrt_stdio_stream_mode
523 {
524     int  _lowio_mode;
525     int  _stdio_mode;
526     bool _success;
527 };
528 
529 
530 
531 // This function and the following functions support __acrt_stdio_parse_mode.
532 // They handle individual parts of the parsing.
533 inline bool __acrt_stdio_parse_mode_plus(__acrt_stdio_stream_mode& result, bool& seen_plus) throw()
534 {
535     if (seen_plus) {
536         return false;
537     }
538 
539     seen_plus = true;
540 
541     if (result._lowio_mode & _O_RDWR) {
542         return false;
543     }
544 
545     result._lowio_mode |= _O_RDWR;
546     result._lowio_mode &= ~(_O_RDONLY | _O_WRONLY);
547     result._stdio_mode |= _IOUPDATE;
548     result._stdio_mode &= ~(_IOREAD | _IOWRITE);
549     return true;
550 }
551 
552 inline bool __acrt_stdio_parse_mode_b(__acrt_stdio_stream_mode& result) throw()
553 {
554     if (result._lowio_mode & (_O_TEXT | _O_BINARY)) {
555         return false;
556     }
557 
558     result._lowio_mode |= _O_BINARY;
559     return true;
560 }
561 
562 inline bool __acrt_stdio_parse_mode_t(__acrt_stdio_stream_mode& result) throw()
563 {
564     if (result._lowio_mode & (_O_TEXT | _O_BINARY)) {
565         return false;
566     }
567 
568     result._lowio_mode |= _O_TEXT;
569     return true;
570 }
571 
572 inline bool __acrt_stdio_parse_mode_c(__acrt_stdio_stream_mode& result, bool& seen_commit_mode) throw()
573 {
574     if (seen_commit_mode) {
575         return false;
576     }
577 
578     seen_commit_mode = true;
579     result._stdio_mode |= _IOCOMMIT;
580     return true;
581 }
582 
583 inline bool __acrt_stdio_parse_mode_n(__acrt_stdio_stream_mode& result, bool& seen_commit_mode) throw()
584 {
585     if (seen_commit_mode) {
586         return false;
587     }
588 
589     seen_commit_mode = true;
590     result._stdio_mode &= ~_IOCOMMIT;
591     return true;
592 }
593 
594 inline bool __acrt_stdio_parse_mode_S(__acrt_stdio_stream_mode& result, bool& seen_scan_mode) throw()
595 {
596     if (seen_scan_mode) {
597         return false;
598     }
599 
600     seen_scan_mode = true;
601     result._lowio_mode |= _O_SEQUENTIAL;
602     return true;
603 }
604 
605 inline bool __acrt_stdio_parse_mode_R(__acrt_stdio_stream_mode& result, bool& seen_scan_mode) throw()
606 {
607     if (seen_scan_mode) {
608         return false;
609     }
610 
611     seen_scan_mode = true;
612     result._lowio_mode |= _O_RANDOM;
613     return true;
614 }
615 
616 inline bool __acrt_stdio_parse_mode_T(__acrt_stdio_stream_mode& result) throw()
617 {
618     if (result._lowio_mode & _O_SHORT_LIVED) {
619         return false;
620     }
621 
622     result._lowio_mode |= _O_SHORT_LIVED;
623     return true;
624 }
625 
626 inline bool __acrt_stdio_parse_mode_D(__acrt_stdio_stream_mode& result) throw()
627 {
628     if (result._lowio_mode & _O_TEMPORARY) {
629         return false;
630     }
631 
632     result._lowio_mode |= _O_TEMPORARY;
633     return true;
634 }
635 
636 inline bool __acrt_stdio_parse_mode_N(__acrt_stdio_stream_mode& result) throw()
637 {
638     result._lowio_mode |= _O_NOINHERIT;
639     return true;
640 }
641 
642 inline bool __acrt_stdio_parse_mode_x(__acrt_stdio_stream_mode& result) throw()
643 {
644     if (!(result._lowio_mode & _O_TRUNC)) {
645         // 'x' only permitted with 'w'
646         return false;
647     }
648 
649     result._lowio_mode |= _O_EXCL;
650     return true;
651 }
652 
653 
654 
655 // Parses a stdio mode string, returning the corresponding pair of lowio and
656 // stdio flags.  On success, sets the success flag in the result to true; on
657 // failure, sets that flag to false.  All failures are logic errors.
658 template <typename Character>
659 __acrt_stdio_stream_mode __cdecl __acrt_stdio_parse_mode(
660     Character const* const mode
661     ) throw()
662 {
663     typedef __acrt_stdio_char_traits<Character> stdio_traits;
664 
665     // Note that we value initialize the result, so the success flag is false
666     // by default.  This ensures that any premature return will return failure.
667     __acrt_stdio_stream_mode result = __acrt_stdio_stream_mode();
668     result._stdio_mode = _commode;
669 
670     // Advance past any leading spaces:
671     Character const* it = mode;
672     while (*it == ' ')
673         ++it;
674 
675     // Read the first character.  It must be one of 'r', 'w' , or 'a':
676     switch (*it)
677     {
678     case 'r':
679         result._lowio_mode = _O_RDONLY;
680         result._stdio_mode = _IOREAD;
681         break;
682 
683     case 'w':
684         result._lowio_mode = _O_WRONLY | _O_CREAT | _O_TRUNC;
685         result._stdio_mode = _IOWRITE;
686         break;
687 
688     case 'a':
689         result._lowio_mode = _O_WRONLY | _O_CREAT | _O_APPEND;
690         result._stdio_mode = _IOWRITE;
691         break;
692 
693     default:
694         _VALIDATE_RETURN(("Invalid file open mode", 0), EINVAL, result);
695     }
696 
697     // Advance past the first character:
698     ++it;
699 
700     // There can be up to seven more optional mode characters:
701     // [1] A single '+' character
702     // [2] One of 't' or 'b' (indicating text or binary, respectively)
703     // [3] One of 'c' or 'n' (enable or disable auto-commit to disk on flush)
704     // [4] One of 'S' or 'R' (optimize for sequential or random access)
705     // [5] 'T' (indicating the file is short-lived)
706     // [6] 'D' (indicating the file is temporary)
707     // [7] 'N' (indicating the file should not be inherited by child processes)
708     // [8] 'x' (indicating the file must be created and it is an error if it already exists)
709     bool seen_commit_mode   = false;
710     bool seen_plus          = false;
711     bool seen_scan_mode     = false;
712     bool seen_encoding_flag = false;
713     for (bool continue_loop = true; continue_loop && *it != '\0'; it += (continue_loop ? 1 : 0))
714     {
715         switch (*it)
716         {
717         case '+': continue_loop = __acrt_stdio_parse_mode_plus(result, seen_plus);        break;
718         case 'b': continue_loop = __acrt_stdio_parse_mode_b   (result);                   break;
719         case 't': continue_loop = __acrt_stdio_parse_mode_t   (result);                   break;
720         case 'c': continue_loop = __acrt_stdio_parse_mode_c   (result, seen_commit_mode); break;
721         case 'n': continue_loop = __acrt_stdio_parse_mode_n   (result, seen_commit_mode); break;
722         case 'S': continue_loop = __acrt_stdio_parse_mode_S   (result, seen_scan_mode  ); break;
723         case 'R': continue_loop = __acrt_stdio_parse_mode_R   (result, seen_scan_mode  ); break;
724         case 'T': continue_loop = __acrt_stdio_parse_mode_T   (result);                   break;
725         case 'D': continue_loop = __acrt_stdio_parse_mode_D   (result);                   break;
726         case 'N': continue_loop = __acrt_stdio_parse_mode_N   (result);                   break;
727         case 'x': continue_loop = __acrt_stdio_parse_mode_x   (result);                   break;
728 
729         // If we encounter any spaces, skip them:
730         case ' ':
731             break;
732 
733         // If we encounter a comma, it begins the encoding specification; we
734         // break out of the loop immediately and parse the encoding flag next:
735         case ',':
736             seen_encoding_flag = true;
737             continue_loop = false;
738             break;
739 
740         default:
741             _VALIDATE_RETURN(("Invalid file open mode", 0), EINVAL, result);
742         }
743     }
744 
745     // Advance past the comma that terminated the loop:
746     if (seen_encoding_flag)
747         ++it;
748 
749     while (*it == ' ')
750         ++it;
751 
752     // If we did not encounter the encoding introducer (a comma), make sure we
753     // actually reached the end of the mode string.  We are done:
754     if (!seen_encoding_flag)
755     {
756         _VALIDATE_RETURN(*it == '\0', EINVAL, result);
757         result._success = true;
758         return result;
759     }
760 
761     // Otherwise, we saw the beginning of an encoding; parse it:
762     static Character const ccs[]              = { 'c', 'c', 's' };
763     static Character const utf8_encoding[]    = { 'U', 'T', 'F', '-', '8' };
764     static Character const utf16_encoding[]   = { 'U', 'T', 'F', '-', '1', '6', 'L', 'E' };
765     static Character const unicode_encoding[] = { 'U', 'N', 'I', 'C', 'O', 'D', 'E' };
766 
767     // Make sure it begins with "ccs" (all lowercase)...
768     if (stdio_traits::tcsncmp(it, ccs, _countof(ccs)) != 0)
769         _VALIDATE_RETURN(("Invalid file open mode", 0), EINVAL, result);
770 
771     it += _countof(ccs); // Advance past the "ccs"
772 
773     while (*it == ' ')
774         ++it;
775 
776     if (*it != '=')
777         _VALIDATE_RETURN(("Invalid file open mode", 0), EINVAL, result);
778 
779     ++it; // Advance past the "="
780 
781     while (*it == ' ')
782         ++it;
783 
784     if (stdio_traits::tcsnicmp(it, utf8_encoding, _countof(utf8_encoding)) == 0)
785     {
786         it += _countof(utf8_encoding);
787         result._lowio_mode |= _O_U8TEXT;
788     }
789     else if (stdio_traits::tcsnicmp(it, utf16_encoding, _countof(utf16_encoding)) == 0)
790     {
791         it += _countof(utf16_encoding);
792         result._lowio_mode |= _O_U16TEXT;
793     }
794     else if (stdio_traits::tcsnicmp(it, unicode_encoding, _countof(unicode_encoding)) == 0)
795     {
796         it += _countof(unicode_encoding);
797         result._lowio_mode |= _O_WTEXT;
798     }
799     else
800     {
801         _VALIDATE_RETURN(("Invalid file open mode", 0), EINVAL, result);
802     }
803 
804     // Finally, skip any trailing spaces...
805     while (*it == ' ')
806         ++it;
807 
808     // ...and ensure there are no characters left:
809     _VALIDATE_RETURN(*it == '\0', EINVAL, result);
810 
811     result._success = true;
812     return result;
813 }
814 
815 
816 
817 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
818 //
819 // __acrt_common_path_requires_backslash()
820 //
821 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
822 inline bool __cdecl __crt_stdio_path_requires_backslash(char const* const first) throw()
823 {
824     char const* const last = first + strlen(first);
825     if (first == last)
826         return false;
827 
828     if (*(last - 1) == '\\')
829     {
830         return reinterpret_cast<unsigned char const*>(last - 1)
831             != _mbsrchr(reinterpret_cast<unsigned char const*>(first), '\\');
832     }
833 
834     if (*(last - 1) == '/')
835         return false;
836 
837     return true;
838 }
839 
840 inline bool __cdecl __crt_stdio_path_requires_backslash(wchar_t const* const first) throw()
841 {
842     wchar_t const* const last = first + wcslen(first);
843     if (first == last)
844         return false;
845 
846     if (*(last - 1) == L'\\')
847         return false;
848 
849     if (*(last - 1) == L'/')
850         return false;
851 
852     return true;
853 }
854 
855 // Reset the file buffer to be empty and ready for reuse
856 inline void __cdecl __acrt_stdio_reset_buffer(__crt_stdio_stream const stream) throw()
857 {
858     stream->_ptr = stream->_base;
859     stream->_cnt = 0;
860 }
861 
862 #endif // !_M_CEE
863 
864 #pragma pack(pop)
865