1 | //===-- String type specifier converters for scanf --------------*- C++ -*-===// |
---|---|
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "src/stdio/scanf_core/string_converter.h" |
10 | |
11 | #include "src/__support/CPP/limits.h" |
12 | #include "src/__support/ctype_utils.h" |
13 | #include "src/stdio/scanf_core/core_structs.h" |
14 | #include "src/stdio/scanf_core/reader.h" |
15 | |
16 | #include <stddef.h> |
17 | |
18 | namespace LIBC_NAMESPACE { |
19 | namespace scanf_core { |
20 | |
21 | int convert_string(Reader *reader, const FormatSection &to_conv) { |
22 | // %s "Matches a sequence of non-white-space characters" |
23 | |
24 | // %c "Matches a sequence of characters of exactly the number specified by the |
25 | // field width (1 if no field width is present in the directive)" |
26 | |
27 | // %[ "Matches a nonempty sequence of characters from a set of expected |
28 | // characters (the scanset)." |
29 | size_t max_width = 0; |
30 | if (to_conv.max_width > 0) { |
31 | max_width = to_conv.max_width; |
32 | } else { |
33 | if (to_conv.conv_name == 'c') { |
34 | max_width = 1; |
35 | } else { |
36 | max_width = cpp::numeric_limits<size_t>::max(); |
37 | } |
38 | } |
39 | |
40 | char *output = reinterpret_cast<char *>(to_conv.output_ptr); |
41 | |
42 | char cur_char = reader->getc(); |
43 | size_t i = 0; |
44 | for (; i < max_width && cur_char != '\0'; ++i) { |
45 | // If this is %s and we've hit a space, or if this is %[] and we've found |
46 | // something not in the scanset. |
47 | if ((to_conv.conv_name == 's' && internal::isspace(ch: cur_char)) || |
48 | (to_conv.conv_name == '[' && !to_conv.scan_set.test(Index: cur_char))) { |
49 | break; |
50 | } |
51 | // if the NO_WRITE flag is not set, write to the output. |
52 | if ((to_conv.flags & NO_WRITE) == 0) |
53 | output[i] = cur_char; |
54 | cur_char = reader->getc(); |
55 | } |
56 | |
57 | // We always read one more character than will be used, so we have to put the |
58 | // last one back. |
59 | reader->ungetc(c: cur_char); |
60 | |
61 | // If this is %s or %[] |
62 | if (to_conv.conv_name != 'c' && (to_conv.flags & NO_WRITE) == 0) { |
63 | // Always null terminate the string. This may cause a write to the |
64 | // (max_width + 1) byte, which is correct. The max width describes the max |
65 | // number of characters read from the input string, and doesn't necessarily |
66 | // correspond to the output. |
67 | output[i] = '\0'; |
68 | } |
69 | |
70 | if (i == 0) |
71 | return MATCHING_FAILURE; |
72 | return READ_OK; |
73 | } |
74 | |
75 | } // namespace scanf_core |
76 | } // namespace LIBC_NAMESPACE |
77 |