[libc-commits] [libc] [libc][stdio] Add support for the %m modifier (PR #218312)
via libc-commits
libc-commits at lists.llvm.org
Tue Aug 25 17:57:45 PDT 2026
https://github.com/afnrow updated https://github.com/llvm/llvm-project/pull/218312
>From 18114006584b959928a829a4f38af08ded970834 Mon Sep 17 00:00:00 2001
From: yahia ahmed <yahia.a.abdrabou at gmail.com>
Date: Mon, 24 Aug 2026 07:00:01 +0300
Subject: [PATCH] [libc][stdio] Add support for the %m modifier
Add support for the %m modifier in scanf by allocating memory
and recursively allocate more till the object is.
---
libc/config/config.json | 4 ++
libc/src/stdio/scanf_core/CMakeLists.txt | 4 ++
libc/src/stdio/scanf_core/parser.h | 11 +--
libc/src/stdio/scanf_core/string_converter.h | 67 +++++++++++++++++--
libc/test/src/stdio/scanf_core/CMakeLists.txt | 1 +
5 files changed, 77 insertions(+), 10 deletions(-)
diff --git a/libc/config/config.json b/libc/config/config.json
index fd7784d3d3e55..9aaf56b2d8c5d 100644
--- a/libc/config/config.json
+++ b/libc/config/config.json
@@ -77,6 +77,10 @@
"LIBC_CONF_SCANF_PROVIDE_ISOC99_ALIASES": {
"value": false,
"doc": "Add __isoc99_* aliases for scanf's functions."
+ },
+ "LIBC_CONF_SCANF_DISABLE_ALLOCATION": {
+ "value": false,
+ "doc": "Disable %m flag for allocating heap memory for the caller as per POSIX"
}
},
"str_to_float": {
diff --git a/libc/src/stdio/scanf_core/CMakeLists.txt b/libc/src/stdio/scanf_core/CMakeLists.txt
index fd68f5d21ae7f..47eef3b9badb6 100644
--- a/libc/src/stdio/scanf_core/CMakeLists.txt
+++ b/libc/src/stdio/scanf_core/CMakeLists.txt
@@ -4,6 +4,9 @@ endif()
if(LIBC_CONF_SCANF_DISABLE_INDEX_MODE)
libc_add_definition(scanf_config_copts "LIBC_COPT_SCANF_DISABLE_INDEX_MODE")
endif()
+if(LIBC_CONF_SCANF_DISABLE_ALLOCATION)
+ libc_add_definition(scanf_config_copts "LIBC_COPT_SCANF_DISABLE_ALLOCATION")
+endif()
if(scanf_config_copts)
list(PREPEND scanf_config_copts "COMPILE_OPTIONS")
endif()
@@ -105,6 +108,7 @@ add_header_library(
libc.src.__support.CPP.limits
libc.src.__support.char_vector
libc.src.__support.str_to_float
+ libc.src.__support.CPP.new
${use_system_file}
)
diff --git a/libc/src/stdio/scanf_core/parser.h b/libc/src/stdio/scanf_core/parser.h
index 1e2f26e0d3fdd..01d477f9f7789 100644
--- a/libc/src/stdio/scanf_core/parser.h
+++ b/libc/src/stdio/scanf_core/parser.h
@@ -81,11 +81,12 @@ template <typename ArgProvider> class Parser {
cur_pos = cur_pos + static_cast<size_t>(result.parsed_len);
}
- // TODO(michaelrj): add posix allocate flag support.
- // if (str[cur_pos] == 'm') {
- // ++cur_pos;
- // section.flags = FormatFlags::ALLOCATE;
- // }
+#ifndef LIBC_COPT_SCANF_DISABLE_ALLOCATION
+ if (str[cur_pos] == 'm') {
+ ++cur_pos;
+ section.flags = static_cast<FormatFlags>(FormatFlags::ALLOCATE);
+ }
+#endif
LengthModifier lm = parse_length_modifier(&cur_pos);
section.length_modifier = lm;
diff --git a/libc/src/stdio/scanf_core/string_converter.h b/libc/src/stdio/scanf_core/string_converter.h
index 3879f8c995899..79d39b55ec16a 100644
--- a/libc/src/stdio/scanf_core/string_converter.h
+++ b/libc/src/stdio/scanf_core/string_converter.h
@@ -10,13 +10,19 @@
#define LLVM_LIBC_SRC_STDIO_SCANF_CORE_STRING_CONVERTER_H
#include "src/__support/CPP/limits.h"
+#include "src/__support/CPP/new.h"
+#include "src/__support/alloc-checker.h"
#include "src/__support/ctype_utils.h"
#include "src/__support/macros/config.h"
#include "src/stdio/scanf_core/core_structs.h"
#include "src/stdio/scanf_core/reader.h"
+#include "src/string/memory_utils/inline_memcpy.h"
#include <stddef.h>
+constexpr int ALLOCATION_SCALE = 2;
+constexpr int ALLOCATION_BASE = 32;
+
namespace LIBC_NAMESPACE_DECL {
namespace scanf_core {
@@ -40,8 +46,26 @@ int convert_string(Reader<T> *reader, const FormatSection &to_conv) {
}
}
- char *output = reinterpret_cast<char *>(to_conv.output_ptr);
-
+ char *output;
+ AllocChecker ac;
+#ifndef LIBC_COPT_SCANF_DISABLE_ALLOCATION
+ size_t alloc_size;
+ if ((to_conv.flags & NO_WRITE) == 0 && (to_conv.flags & ALLOCATE) != 0) {
+ if (to_conv.conv_name == 'c')
+ alloc_size = max_width + 1;
+ else
+ alloc_size =
+ (max_width < ALLOCATION_BASE) ? max_width + 1 : ALLOCATION_BASE;
+ output = new (ac) char[alloc_size];
+ if (!ac) {
+ return ALLOCATION_FAILURE;
+ }
+ } else {
+ output = reinterpret_cast<char *>(to_conv.output_ptr);
+ }
+#else
+ output = reinterpret_cast<char *>(to_conv.output_ptr);
+#endif
char cur_char = reader->getc();
size_t i = 0;
for (; i < max_width && cur_char != '\0'; ++i) {
@@ -52,8 +76,26 @@ int convert_string(Reader<T> *reader, const FormatSection &to_conv) {
break;
}
// if the NO_WRITE flag is not set, write to the output.
- if ((to_conv.flags & NO_WRITE) == 0)
+ if ((to_conv.flags & NO_WRITE) == 0) {
output[i] = cur_char;
+#ifndef LIBC_COPT_SCANF_DISABLE_ALLOCATION
+ if (((to_conv.flags & ALLOCATE) != 0) && (i + 1) == alloc_size &&
+ alloc_size < max_width) {
+ alloc_size *= ALLOCATION_SCALE;
+ if (alloc_size > max_width + 1)
+ alloc_size = max_width + 1;
+ char *tmp = new (ac) char[alloc_size];
+ if (!ac) {
+ delete[] output;
+ reader->ungetc(cur_char);
+ return ALLOCATION_FAILURE;
+ }
+ inline_memcpy(tmp, output, i + 1);
+ delete[] output;
+ output = tmp;
+ }
+#endif
+ }
cur_char = reader->getc();
}
@@ -61,8 +103,11 @@ int convert_string(Reader<T> *reader, const FormatSection &to_conv) {
// last one back.
reader->ungetc(cur_char);
+ bool null_terminate =
+ (to_conv.conv_name != 'c') || ((to_conv.flags & ALLOCATE) != 0);
+
// If this is %s or %[]
- if (to_conv.conv_name != 'c' && (to_conv.flags & NO_WRITE) == 0) {
+ if (null_terminate && (to_conv.flags & NO_WRITE) == 0) {
// Always null terminate the string. This may cause a write to the
// (max_width + 1) byte, which is correct. The max width describes the max
// number of characters read from the input string, and doesn't necessarily
@@ -70,8 +115,20 @@ int convert_string(Reader<T> *reader, const FormatSection &to_conv) {
output[i] = '\0';
}
- if (i == 0)
+ if (i == 0) {
+#ifndef LIBC_COPT_SCANF_DISABLE_ALLOCATION
+ if ((to_conv.flags & ALLOCATE) != 0 && output)
+ delete[] output;
+#endif
return MATCHING_FAILURE;
+ }
+
+#ifndef LIBC_COPT_SCANF_DISABLE_ALLOCATION
+ if ((to_conv.flags & ALLOCATE) != 0) {
+ *reinterpret_cast<char **>(to_conv.output_ptr) = output;
+ }
+#endif
+
return READ_OK;
}
diff --git a/libc/test/src/stdio/scanf_core/CMakeLists.txt b/libc/test/src/stdio/scanf_core/CMakeLists.txt
index 6fdee7b3d5d51..0b234c6054721 100644
--- a/libc/test/src/stdio/scanf_core/CMakeLists.txt
+++ b/libc/test/src/stdio/scanf_core/CMakeLists.txt
@@ -47,6 +47,7 @@ add_libc_test(
libc.src.stdio.scanf_core.converter
libc.src.stdio.scanf_core.string_reader
libc.src.__support.CPP.string_view
+ libc.src.__support.CPP.new
COMPILE_OPTIONS
${use_system_file}
)
More information about the libc-commits
mailing list