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