1 //
2 // ungetc.cpp
3 //
4 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Defines ungetc(), which pushes a character back into a stream.
7 //
8 #include <corecrt_internal_stdio.h>
9
10
11
12 // Pushes a character ("ungets" it) back into a stream. It is possible to push
13 // back one character. It may not be possible to push back more than one
14 // character in a row. Returns the pushed-back character on success; returns
15 // EOF on failure. Ungetting EOF is expressly forbidden.
ungetc(int const c,FILE * const stream)16 extern "C" int __cdecl ungetc(int const c, FILE* const stream)
17 {
18 _VALIDATE_RETURN(stream != nullptr, EINVAL, EOF);
19
20 int return_value = EOF;
21
22 _lock_file(stream);
23 __try
24 {
25 return_value = _ungetc_nolock(c, stream);
26 }
27 __finally
28 {
29 _unlock_file(stream);
30 }
31 __endtry
32
33 return return_value;
34 }
35
36
37
_ungetc_nolock(int const c,FILE * public_stream)38 extern "C" int __cdecl _ungetc_nolock(int const c, FILE* public_stream)
39 {
40 __crt_stdio_stream const stream(public_stream);
41
42 _VALIDATE_STREAM_ANSI_RETURN(stream, EINVAL, EOF);
43
44 // Ungetting EOF is expressly forbidden:
45 if (c == EOF)
46 return EOF;
47
48 // The stream must either be open in read-only mode, or must be open in
49 // read-write mode and must not currently be in write mode:
50 bool const is_in_read_only_mode = stream.has_all_of(_IOREAD);
51 bool const is_in_rw_write_mode = stream.has_all_of(_IOUPDATE | _IOWRITE);
52
53 if (!is_in_read_only_mode && !is_in_rw_write_mode)
54 return EOF;
55
56 // If the stream is currently unbuffered, buffer it:
57 if (stream->_base == nullptr)
58 __acrt_stdio_allocate_buffer_nolock(stream.public_stream());
59
60 // At this point, we know that _base is not null, since the file is buffered.
61
62 if (stream->_ptr == stream->_base)
63 {
64 // If we've already buffered a pushed-back character, there's no room for
65 // another, and there's nothing we can do:
66 if (stream->_cnt)
67 return EOF;
68
69 ++stream->_ptr;
70 }
71
72 // If the stream is string-backed (and not file-backed), do not modify the buffer:
73 if (stream.is_string_backed())
74 {
75 --stream->_ptr;
76 if (*stream->_ptr != static_cast<char>(c))
77 {
78 ++stream->_ptr;
79 return EOF;
80 }
81 }
82 else
83 {
84 --stream->_ptr;
85 *stream->_ptr = static_cast<char>(c);
86 }
87
88 ++stream->_cnt;
89 stream.unset_flags(_IOEOF);
90 stream.set_flags(_IOREAD);
91
92 return c & 0xff;
93 }
94