xref: /reactos/sdk/lib/ucrt/stdio/fgets.cpp (revision e3e520d1)
1 //
2 // fgets.cpp
3 //
4 //      Copyright (c) Microsoft Corporation.  All rights reserved.
5 //
6 // Functions that read a string from a file.
7 //
8 #include <corecrt_internal_stdio.h>
9 
10 
11 
12 // Reads a string from a stream.  This function reads a string, up to 'count - 1'
13 // characters in length, or until a '\n', whichever is reached first.  The string
14 // is always null-terminated on return.  The '\n' _is_ written to the string if
15 // it is encountered before space is exhausted.  If EOF is encountered immediately,
16 // null is returned.  If EOF is encountered after some characters are read, EOF
17 // terminates input just as '\n' would.
18 //
19 // Returns null if the count is nonpositive or if EOF is encountered immediately.
20 // Otherwise, returns the string.
21 template <typename Character>
22 _Success_(return != 0)
23 static Character* __cdecl common_fgets(
24     _Out_writes_z_(count) Character*    const string,
25     int                                 const count,
26     __crt_stdio_stream                  const stream
27     ) throw()
28 {
29     typedef __acrt_stdio_char_traits<Character> stdio_traits;
30 
31     _VALIDATE_RETURN(string != nullptr || count == 0, EINVAL, nullptr);
32     _VALIDATE_RETURN(count >= 0,                      EINVAL, nullptr);
33     _VALIDATE_RETURN(stream.valid(),                  EINVAL, nullptr);
34 
35     if (count == 0)
36         return nullptr;
37 
38     Character* return_value = nullptr;
39 
40     _lock_file(stream.public_stream());
41     __try
42     {
43         if (!stdio_traits::validate_stream_is_ansi_if_required(stream.public_stream()))
44             __leave;
45 
46         // Note that we start iterating at 1, so we read at most 'count - 1'
47         // characters from the stream, leaving room for the null terminator:
48         Character* it = string;
49         for (int i = 1; i != count; ++i)
50         {
51             int const c = stdio_traits::getc_nolock(stream.public_stream());
52             if (c == stdio_traits::eof)
53             {
54                 // If we immediately reach EOF before reading any characters,
55                 // the C Language Standard mandates that the input buffer should
56                 // be left unmodified, so we return immediately, without writing
57                 // anything to the buffer:
58                 if (it == string)
59                     __leave;
60 
61                 // Otherwise, when we reach EOF, we just need to stop iterating:
62                 break;
63             }
64 
65             // We stop reading when we reach a newline.  We do copy the newline:
66             *it++ = static_cast<Character>(c);
67             if (static_cast<Character>(c) == '\n')
68                 break;
69         }
70 
71         *it = '\0';
72         return_value = string;
73     }
74     __finally
75     {
76         _unlock_file(stream.public_stream());
77     }
78     __endtry
79 
80     return return_value;
81 }
82 
83 
84 
85 extern "C" char* __cdecl fgets(
86     char* const string,
87     int   const count,
88     FILE* const stream
89     )
90 {
91     return common_fgets(string, count, __crt_stdio_stream(stream));
92 }
93 
94 extern "C" wchar_t* __cdecl fgetws(
95     wchar_t* const string,
96     int      const count,
97     FILE*    const stream
98     )
99 {
100     return common_fgets(string, count, __crt_stdio_stream(stream));
101 }
102