xref: /reactos/sdk/lib/ucrt/stdio/ungetc.cpp (revision e98e9000)
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