xref: /llvm-project/libc/src/stdio/scanf_core/string_converter.cpp (revision 5ff3ff33ff930e4ec49da7910612d8a41eb068cb)
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/__support/macros/config.h"
14 #include "src/stdio/scanf_core/core_structs.h"
15 #include "src/stdio/scanf_core/reader.h"
16 
17 #include <stddef.h>
18 
19 namespace LIBC_NAMESPACE_DECL {
20 namespace scanf_core {
21 
22 int convert_string(Reader *reader, const FormatSection &to_conv) {
23   // %s "Matches a sequence of non-white-space characters"
24 
25   // %c "Matches a sequence of characters of exactly the number specified by the
26   // field width (1 if no field width is present in the directive)"
27 
28   // %[ "Matches a nonempty sequence of characters from a set of expected
29   // characters (the scanset)."
30   size_t max_width = 0;
31   if (to_conv.max_width > 0) {
32     max_width = to_conv.max_width;
33   } else {
34     if (to_conv.conv_name == 'c') {
35       max_width = 1;
36     } else {
37       max_width = cpp::numeric_limits<size_t>::max();
38     }
39   }
40 
41   char *output = reinterpret_cast<char *>(to_conv.output_ptr);
42 
43   char cur_char = reader->getc();
44   size_t i = 0;
45   for (; i < max_width && cur_char != '\0'; ++i) {
46     // If this is %s and we've hit a space, or if this is %[] and we've found
47     // something not in the scanset.
48     if ((to_conv.conv_name == 's' && internal::isspace(cur_char)) ||
49         (to_conv.conv_name == '[' && !to_conv.scan_set.test(cur_char))) {
50       break;
51     }
52     // if the NO_WRITE flag is not set, write to the output.
53     if ((to_conv.flags & NO_WRITE) == 0)
54       output[i] = cur_char;
55     cur_char = reader->getc();
56   }
57 
58   // We always read one more character than will be used, so we have to put the
59   // last one back.
60   reader->ungetc(cur_char);
61 
62   // If this is %s or %[]
63   if (to_conv.conv_name != 'c' && (to_conv.flags & NO_WRITE) == 0) {
64     // Always null terminate the string. This may cause a write to the
65     // (max_width + 1) byte, which is correct. The max width describes the max
66     // number of characters read from the input string, and doesn't necessarily
67     // correspond to the output.
68     output[i] = '\0';
69   }
70 
71   if (i == 0)
72     return MATCHING_FAILURE;
73   return READ_OK;
74 }
75 
76 } // namespace scanf_core
77 } // namespace LIBC_NAMESPACE_DECL
78