From patchwork Wed Mar 18 21:47:47 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alejandro Colomar X-Patchwork-Id: 59829 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 41C254BCA42D for ; Wed, 18 Mar 2026 21:48:48 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 41C254BCA42D Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20201202 header.b=ESzRYxTg X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from sea.source.kernel.org (sea.source.kernel.org [IPv6:2600:3c0a:e001:78e:0:1991:8:25]) by sourceware.org (Postfix) with ESMTPS id 8E92C4BC898B for ; Wed, 18 Mar 2026 21:47:52 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 8E92C4BC898B Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=kernel.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=kernel.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 8E92C4BC898B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2600:3c0a:e001:78e:0:1991:8:25 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773870472; cv=none; b=aMXPCjCGI6FxM0s+oQCo2RA+7azL2ORG3R77i1CRuVjPcaOyRc/jPD3mr/yaeaFn+hswXRpBQ0Mfv5DIWT5dlFDkb2Yn1RLT84IBCMIKJ/7zEMrFPFSmR8TGe8cWR4yRUTN2LtmEiPVWfKuvuf7IdsU2kFFiVzKFJYg2l+F17n0= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1773870472; c=relaxed/simple; bh=hLsvSkoq0zeOvNPN/baXeHVS+j52btp8Nw+bO/Unq8s=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=Ihfq4Q+omFYuR5hb3rJB/+tXzQx1sTkAarpKFApLrnDsbxYilMbkMEZhpKtwJMFsM5IRnjz7ariFdueutbo6xs7wPmgRHf43FBWQ9uBpCpWo+ZlcwapLjOgJcX38tSSQo7wTbaGrORWWPz4+y1SF6jYCy4amLPV3wKzqFTczAYI= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8E92C4BC898B Received: from smtp.kernel.org (transwarp.subspace.kernel.org [100.75.92.58]) by sea.source.kernel.org (Postfix) with ESMTP id D26FF444B4; Wed, 18 Mar 2026 21:47:51 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 81D37C19421; Wed, 18 Mar 2026 21:47:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773870471; bh=hLsvSkoq0zeOvNPN/baXeHVS+j52btp8Nw+bO/Unq8s=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=ESzRYxTgSvYNISP7pcmzB+TzAjx8AcJ73abML65zQI1WjXcw6qZQMySm+BKlcmM7H enrmRBvVcLziAwgZOFMq8omdW8oW1ErVLnPhnLWEkQ+0q8MYanaiSkmqhxu4H//BQJ hh8gG2jv9ZUHx84err+9yT9Aj+OHoiAUGNhDNAEfN6Gge/JqJiJlheoPgWaxXc824L 5LDXqtv/BEyS7cPiog2HHFNIQZ3FYcYTwWzaGqW6LqEVIq9tpTRx9rLMUmI8SPhOKV cYETmt+aVuJymKzaHEm/ogzDlRNOgwRyeqHOqbOaCqkCReGZ8jgmRf5Qx3vLeB7ICJ qYsXX62xHpm+A== Date: Wed, 18 Mar 2026 22:47:47 +0100 From: Alejandro Colomar To: libc-alpha@sourceware.org Cc: Alejandro Colomar , Joseph Myers , Paul Eggert , Adhemerval Zanella Netto , Florian Weimer , "Dmitry V. Levin" , Archie Cobbs , Solar Designer , Sam James Subject: [RFC v4 0/2] Add [v]aprintf(3) Message-ID: X-Mailer: git-send-email 2.53.0 References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: X-Spam-Status: No, score=-4.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, KAM_SHORT, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org Hi! In v4: - I've written the code to resemble [v]asprintf(3), as Joseph suggested. - I fixed the 'lint-makefiles' issues, which I think were because I wasn't using alphabetic order. - I've also sorted [v]aprintf(3) before [v]asprintf(3) in source code. - I've written commit messages. See range-diff at the bottom. Regression testing now says ok. $ diff -u ../.tmp.master/tests.sum tests.sum --- ../.tmp.master/tests.sum 2026-03-18 18:52:49.914978699 +0100 +++ tests.sum 2026-03-18 22:36:56.877761477 +0100 @@ -1936,6 +1936,7 @@ PASS: libio/test-fputs-unbuffered-full PASS: libio/test-fputws-unbuffered-full PASS: libio/test-freopen +PASS: libio/tst-aprintf PASS: libio/tst-asprintf-null PASS: libio/tst-atime PASS: libio/tst-bz22415 Things I haven't done: - I still didn't write tests for the sysdeps _chk implementations, nor the Versions files under sysdeps/. I'm aware I need to do this. - I didn't update '.abilist' files. Is that something I should do? Is there anything else I'm missing? Have a lovely night! Alex Alejandro Colomar (2): Add [v]aprintf(3) manual/: Prefer aprintf(3) over asprintf(3) debug/Makefile | 4 ++ debug/Versions | 3 + debug/aprintf_chk.c | 51 +++++++++++++++++ debug/vaprintf_chk.c | 39 +++++++++++++ include/stdio.h | 8 +++ libio/Makefile | 4 +- libio/Versions | 3 + libio/bits/stdio-ldbl.h | 5 ++ libio/bits/stdio2-decl.h | 8 +++ libio/bits/stdio2.h | 42 ++++++++++++++ libio/stdio.h | 13 +++++ libio/tst-aprintf.c | 57 +++++++++++++++++++ libio/vaprintf.c | 39 +++++++++++++ manual/examples/rprintf.c | 4 +- manual/stdio.texi | 30 +++++++++- manual/string.texi | 2 +- stdio-common/Makefile | 2 + stdio-common/Versions | 2 + stdio-common/aprintf.c | 40 +++++++++++++ sysdeps/ieee754/ldbl-128ibm-compat/Makefile | 8 ++- .../ldbl-128ibm-compat/ieee128-aprintf.c | 36 ++++++++++++ .../ldbl-128ibm-compat/ieee128-aprintf_chk.c | 40 +++++++++++++ .../ldbl-128ibm-compat/ieee128-vaprintf.c | 29 ++++++++++ .../ldbl-128ibm-compat/ieee128-vaprintf_chk.c | 31 ++++++++++ sysdeps/ieee754/ldbl-opt/Makefile | 6 ++ sysdeps/ieee754/ldbl-opt/nldbl-aprintf.c | 17 ++++++ sysdeps/ieee754/ldbl-opt/nldbl-aprintf_chk.c | 15 +++++ sysdeps/ieee754/ldbl-opt/nldbl-vaprintf.c | 9 +++ sysdeps/ieee754/ldbl-opt/nldbl-vaprintf_chk.c | 8 +++ 29 files changed, 548 insertions(+), 7 deletions(-) create mode 100644 debug/aprintf_chk.c create mode 100644 debug/vaprintf_chk.c create mode 100644 libio/tst-aprintf.c create mode 100644 libio/vaprintf.c create mode 100644 stdio-common/aprintf.c create mode 100644 sysdeps/ieee754/ldbl-128ibm-compat/ieee128-aprintf.c create mode 100644 sysdeps/ieee754/ldbl-128ibm-compat/ieee128-aprintf_chk.c create mode 100644 sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vaprintf.c create mode 100644 sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vaprintf_chk.c create mode 100644 sysdeps/ieee754/ldbl-opt/nldbl-aprintf.c create mode 100644 sysdeps/ieee754/ldbl-opt/nldbl-aprintf_chk.c create mode 100644 sysdeps/ieee754/ldbl-opt/nldbl-vaprintf.c create mode 100644 sysdeps/ieee754/ldbl-opt/nldbl-vaprintf_chk.c Range-diff against v3: 1: f616528f ! 1: f927e1d9 Add [v]aprintf(3) @@ Metadata ## Commit message ## Add [v]aprintf(3) + This function is fundamentally the same as asprintf(3) --which was + standardized in POSIX.1-2024--. + + However, it has a key difference, which makes it a much better design. + Instead of returning the newly allocated pointer through a char** + parameter, it returns the pointer (like strdup(3)). The API is as + simple as it can be: + + char *aprintf(const char *restrict fmt, ...); + char *vaprintf(const char *restrict fmt, va_list ap); + + This difference has several benefits: + + - One less parameter. This means less source code for using this API, + which implicitly means less chances of making mistakes. It is also + less noise while reading code, which increases readability. + + Especially, we get rid of a pointer-to-pointer parameter, which are + especially weird. + + - The function has an prototype that resembles strdup(3). This + functionality is commonly paired with strdup(3) calls, because + they're very related. By having a similar prototype, the code is + more naturally paired with + + Compare: + + if (cond) { + p = strdup("foo"); + if (p == NULL) + goto fail; + } else { + if (asprintf(&p, "foo %d", 42) < 0) + goto fail; + } + vs + p = cond ? strdup("foo") : aprintf("foo %d", 42); + if (p == NULL) + goto fail; + + - We don't need to fail with EOVERFLOW. This initial implementation + still fails with EOVERFLOW, because it would require a lot of work + implementing it in a way that doesn't have such a failure point, but + the API has no inherent reasons to fail with EOVERFLOW. + + - This API returns a pointer, which makes it possible to use + __attribute__((__malloc__(free))). This would allow static analysers + to know that these APIs allocate memory, which would prevent memory + leaks, use-after-free errors, and other common issues of handling + memory. + + The name is similar to asprintf(3), removing the 's'. This is the same + name that gnulib has used for their implementation. It also matches the + name of the n3750 (alx-0007) proposal for ISO C2y. + + The C Committee was worried that such a non-reserved name might be + unlikely to be viable, as some existing code already uses it. However: + + - Most code using this name, uses it with the same exact semantics. + In those cases, it's not a problem. + + - In the remaining cases, the prototype is different, so they'll + eventually get a compiler error for the clash. This is entirely + safe. And by hiding the name under _GNU_SOURCE, we make that clash + less likely. Once/if this is eventually standard, I expect people + will adapt their code, which is entirely reasonable. Still, then the + APIs will be hidden under _ISOC2Y_SOURCE et al., so this will only + affect users when they opt-in to such a version. + + Link: + Cc: Joseph Myers + Cc: Paul Eggert + Cc: Bruno Haible + Cc: Adhemerval Zanella Netto + Cc: Florian Weimer + Cc: "Dmitry V. Levin" + Cc: Archie Cobbs + Cc: Solar Designer + Cc: Sam James Signed-off-by: Alejandro Colomar + ## debug/Makefile ## +@@ debug/Makefile: headers := execinfo.h + routines = \ + $(static-only-routines) \ + ____longjmp_chk \ ++ aprintf_chk \ + asprintf_chk \ + backtrace \ + backtracesyms \ +@@ debug/Makefile: routines = \ + strncpy_chk \ + swprintf_chk \ + ttyname_r_chk \ ++ vaprintf_chk \ + vasprintf_chk \ + vdprintf_chk \ + vfprintf_chk \ +@@ debug/Makefile: CFLAGS-sprintf_chk.c += $(libio-mtsafe) + CFLAGS-snprintf_chk.c += $(libio-mtsafe) + CFLAGS-vsprintf_chk.c += $(libio-mtsafe) + CFLAGS-vsnprintf_chk.c += $(libio-mtsafe) ++CFLAGS-aprintf_chk.c += $(libio-mtsafe) ++CFLAGS-vaprintf_chk.c += $(libio-mtsafe) + CFLAGS-asprintf_chk.c += $(libio-mtsafe) + CFLAGS-vasprintf_chk.c += $(libio-mtsafe) + CFLAGS-obprintf_chk.c += $(libio-mtsafe) + ## debug/Versions ## @@ debug/Versions: libc { GLIBC_2.43 { @@ debug/Versions: libc { __fortify_fail; } + ## debug/aprintf_chk.c (new) ## +@@ ++/* Copyright (C) 2026 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++ ++ ++/* Write formatted output from FORMAT to a string allocated with malloc. */ ++char * ++___aprintf_chk (int flag, const char *fmt, ...) ++{ ++ /* For flag > 0 (i.e. __USE_FORTIFY_LEVEL > 1) request that %n ++ can only come from read-only format strings. */ ++ unsigned int mode = (flag > 0) ? PRINTF_FORTIFY : 0; ++ va_list ap; ++ char *p; ++ ++ va_start (ap, fmt); ++ if (__vasprintf_internal (&p, fmt, ap, mode) < 0) ++ p = NULL; ++ va_end (ap); ++ ++ return p; ++} ++#if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1 ++/* This is needed since is included in this case, leading to ++ * multiple asm redirection of the same symbol ++ */ ++ldbl_hidden_def (___aprintf_chk, __aprintf_chk) ++ldbl_strong_alias (___aprintf_chk, __aprintf_chk) ++#else ++/* On some systems introduction of ldbl_* macros lead to ABI breakage due to the ++ * long_double_symbol aliasing. ++ */ ++strong_alias (___aprintf_chk, __aprintf_chk) ++libc_hidden_def (__aprintf_chk) ++#endif + + ## debug/vaprintf_chk.c (new) ## +@@ ++/* Copyright (C) 2026 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . ++ ++ As a special exception, if you link the code in this file with ++ files compiled with a GNU compiler to produce an executable, ++ that does not cause the resulting executable to be covered by ++ the GNU Lesser General Public License. This exception does not ++ however invalidate any other reasons why the executable file ++ might be covered by the GNU Lesser General Public License. ++ This exception applies to code released by its copyright holders ++ in files containing the exception. */ ++ ++#include ++ ++char * ++__vaprintf_chk (int flag, const char *fmt, va_list ap) ++{ ++ /* For flag > 0 (i.e. __USE_FORTIFY_LEVEL > 1) request that %n ++ can only come from read-only format strings. */ ++ unsigned int mode = (flag > 0) ? PRINTF_FORTIFY : 0; ++ char *p; ++ ++ return __vasprintf_internal (&p, fmt, ap, mode) < 0 ? NULL : p; ++} ++libc_hidden_def (__vaprintf_chk) + ## include/stdio.h ## -@@ include/stdio.h: extern const char *__get_errname (int) attribute_hidden; +@@ include/stdio.h: extern int __vfprintf_chk (FILE *, int, const char *, __gnuc_va_list); + stdio_hidden_ldbl_proto (__, vfprintf_chk) + extern char *__fgets_unlocked_chk (char *buf, size_t size, int n, FILE *fp); + extern char *__fgets_chk (char *buf, size_t size, int n, FILE *fp); ++extern char *__aprintf_chk (int, const char *, ...) ++ __THROW __attribute_malloc__; ++extern char *__vaprintf_chk (int, const char *, __gnuc_va_list) ++ __THROW __attribute_malloc__; ++stdio_hidden_ldbl_proto (__, vaprintf_chk) + extern int __asprintf_chk (char **, int, const char *, ...) __THROW; + extern int __vasprintf_chk (char **, int, const char *, __gnuc_va_list) __THROW; + stdio_hidden_ldbl_proto (__, vasprintf_chk) +@@ include/stdio.h: extern const size_t _sys_errlist_internal_len attribute_hidden; + extern const char *__get_errlist (int) attribute_hidden; + extern const char *__get_errname (int) attribute_hidden; ++libc_hidden_ldbl_proto (__aprintf) libc_hidden_ldbl_proto (__asprintf) -+extern __typeof (aprintf) __aprintf; -+libc_hidden_ldbl_proto (__aprintf) -+ # if IS_IN (libc) - extern FILE *_IO_new_fopen (const char*, const char*); - # define fopen(fname, mode) _IO_new_fopen (fname, mode) +@@ include/stdio.h: rtld_hidden_proto (__libc_fatal) + libc_hidden_proto (__fgets_unlocked_chk) + + #if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1 ++libc_hidden_ldbl_proto (__aprintf_chk) + libc_hidden_ldbl_proto (__asprintf_chk) + #else ++libc_hidden_proto (__aprintf_chk) + libc_hidden_proto (__asprintf_chk) + #endif + ## libio/Makefile ## @@ libio/Makefile: routines := \ @@ libio/Makefile: routines := \ \ __fbufsize __freading __fwriting __freadable __fwritable __flbf \ @@ libio/Makefile: routines_no_fortify += \ + iofgetws_u \ iovdprintf \ swprintf \ - vasprintf \ + vaprintf \ + vasprintf \ vsnprintf \ vswprintf \ - vwprintf \ @@ libio/Makefile: tests = \ + test-fmemopen \ test-fputs-unbuffered-full \ test-fputws-unbuffered-full \ - tst-asprintf-null \ + tst-aprintf \ + tst-asprintf-null \ tst-atime \ tst-bz22415 \ - tst-bz24051 \ ## libio/Versions ## @@ libio/Versions: libc { @@ libio/Versions: libc { __libc_fatal; ## libio/bits/stdio-ldbl.h ## -@@ libio/bits/stdio-ldbl.h: __LDBL_REDIR2_DECL (vdprintf_chk) +@@ libio/bits/stdio-ldbl.h: __LDBL_REDIR_DECL (dprintf) + #endif + + #ifdef __USE_GNU ++__LDBL_REDIR_DECL (vaprintf) ++__LDBL_REDIR2_DECL (aprintf) ++__LDBL_REDIR_DECL (aprintf) + __LDBL_REDIR_DECL (vasprintf) + __LDBL_REDIR2_DECL (asprintf) + __LDBL_REDIR_DECL (asprintf) +@@ libio/bits/stdio-ldbl.h: __LDBL_REDIR2_DECL (dprintf_chk) + __LDBL_REDIR2_DECL (vdprintf_chk) + # endif # ifdef __USE_GNU - __LDBL_REDIR2_DECL (asprintf_chk) - __LDBL_REDIR2_DECL (vasprintf_chk) +__LDBL_REDIR2_DECL (aprintf_chk) +__LDBL_REDIR2_DECL (vaprintf_chk) + __LDBL_REDIR2_DECL (asprintf_chk) + __LDBL_REDIR2_DECL (vasprintf_chk) __LDBL_REDIR2_DECL (obstack_printf_chk) - __LDBL_REDIR2_DECL (obstack_vprintf_chk) - # endif ## libio/bits/stdio2-decl.h ## -@@ libio/bits/stdio2-decl.h: extern int __asprintf_chk (char **__restrict __ptr, int __flag, - extern int __vasprintf_chk (char **__restrict __ptr, int __flag, - const char *__restrict __fmt, __gnuc_va_list __arg) - __THROW __attribute__ ((__format__ (__printf__, 3, 0))) __wur; +@@ libio/bits/stdio2-decl.h: extern int __vdprintf_chk (int __fd, int __flag, + + # ifdef __USE_GNU + +extern char *__aprintf_chk (int __flag, + const char *__restrict __fmt, ...) + __THROW __attribute__ ((__format__ (__printf__, 2, 3))) + __attribute_malloc__; +extern char *__vaprintf_chk (int __flag, -+ const char *__restrict __fmt, __gnuc_va_list __arg) ++ const char *__restrict __fmt, __gnuc_va_list __ap) + __THROW __attribute__ ((__format__ (__printf__, 2, 0))) + __attribute_malloc__; - extern int __obstack_printf_chk (struct obstack *__restrict __obstack, - int __flag, const char *__restrict __format, - ...) + extern int __asprintf_chk (char **__restrict __ptr, int __flag, + const char *__restrict __fmt, ...) + __THROW __attribute__ ((__format__ (__printf__, 3, 4))) __wur; ## libio/bits/stdio2.h ## -@@ libio/bits/stdio2.h: __NTH (__asprintf (char **__restrict __ptr, const char *__restrict __fmt, - __va_arg_pack ()); - } +@@ libio/bits/stdio2.h: vdprintf (int __fd, const char *__restrict __fmt, __gnuc_va_list __ap) + # ifdef __USE_GNU + # ifdef __va_arg_pack +__fortify_function char * +__NTH (aprintf (const char *__restrict __fmt, ...)) +{ @@ libio/bits/stdio2.h: __NTH (__asprintf (char **__restrict __ptr, const char *__r +} + __fortify_function int - __NTH (obstack_printf (struct obstack *__restrict __obstack, - const char *__restrict __fmt, ...)) -@@ libio/bits/stdio2.h: __NTH (__asprintf (__fortify_clang_overload_arg (char **, __restrict, __ptr), - return __r; + __NTH (asprintf (char **__restrict __ptr, const char *__restrict __fmt, ...)) + { +@@ libio/bits/stdio2.h: __NTH (obstack_printf (struct obstack *__restrict __obstack, + __va_arg_pack ()); } - + # elif __fortify_use_clang +__fortify_function char * +__NTH (aprintf (const char *__restrict __fmt, ...)) +{ @@ libio/bits/stdio2.h: __NTH (__asprintf (__fortify_clang_overload_arg (char **, _ +} + __fortify_function_error_function __attribute_overloadable__ int - __NTH (obstack_printf (__fortify_clang_overload_arg (struct obstack *, - __restrict, __obstack), + __NTH (asprintf (__fortify_clang_overload_arg (char **, __restrict, __ptr), + const char *__restrict __fmt, ...)) @@ libio/bits/stdio2.h: __NTH (obstack_printf (__fortify_clang_overload_arg (struct obstack *, - __asprintf_chk (ptr, __USE_FORTIFY_LEVEL - 1, __VA_ARGS__) - # define __asprintf(ptr, ...) \ - __asprintf_chk (ptr, __USE_FORTIFY_LEVEL - 1, __VA_ARGS__) + return __r; + } + # elif !defined __cplusplus +# define aprintf(...) \ + __aprintf_chk (__USE_FORTIFY_LEVEL - 1, __VA_ARGS__) +# define __aprintf(...) \ + __aprintf_chk (__USE_FORTIFY_LEVEL - 1, __VA_ARGS__) - # define obstack_printf(obstack, ...) \ + # define asprintf(ptr, ...) \ + __asprintf_chk (ptr, __USE_FORTIFY_LEVEL - 1, __VA_ARGS__) + # define __asprintf(ptr, ...) \ +@@ libio/bits/stdio2.h: __NTH (obstack_printf (__fortify_clang_overload_arg (struct obstack *, __obstack_printf_chk (obstack, __USE_FORTIFY_LEVEL - 1, __VA_ARGS__) # endif -@@ libio/bits/stdio2.h: __NTH (vasprintf (char **__restrict __ptr, const char *__restrict __fmt, - return __vasprintf_chk (__ptr, __USE_FORTIFY_LEVEL - 1, __fmt, __ap); - } +__fortify_function char * +__NTH (vaprintf (const char *__restrict __fmt, __gnuc_va_list __ap)) @@ libio/bits/stdio2.h: __NTH (vasprintf (char **__restrict __ptr, const char *__re +} + __fortify_function int - __NTH (obstack_vprintf (struct obstack *__restrict __obstack, - const char *__restrict __fmt, __gnuc_va_list __ap)) + __NTH (vasprintf (char **__restrict __ptr, const char *__restrict __fmt, + __gnuc_va_list __ap)) ## libio/stdio.h ## @@ libio/stdio.h: extern int asprintf (char **__restrict __ptr, @@ libio/stdio.h: extern int asprintf (char **__restrict __ptr, +#ifdef __USE_GNU +/* Write formatted output to a string dynamically allocated with `malloc'. */ -+extern char *vaprintf (const char *__restrict __f, __gnuc_va_list __arg) ++extern char *vaprintf (const char *__restrict __fmt, __gnuc_va_list __ap) + __THROWNL __attribute__ ((__format__ (__printf__, 1, 0))) + __attribute_malloc__; ++extern char *__aprintf (const char *__restrict __fmt, ...) ++ __THROWNL __attribute__ ((__format__ (__printf__, 1, 2))) ++ __attribute_malloc__; +extern char *aprintf (const char *__restrict __fmt, ...) + __THROWNL __attribute__ ((__format__ (__printf__, 1, 2))) + __attribute_malloc__; @@ libio/tst-aprintf.c (new) +do_test (void) +{ + char *buf; ++ ++ /* Success */ ++ buf = aprintf ("foo %d", 42); ++ TEST_COMPARE_STRING (buf, "foo 42"); ++ free(buf); ++ + { + /* Avoid -Wformat-overflow warning. */ + const char *volatile format = "%2000000000d %2000000000d"; @@ libio/tst-aprintf.c (new) + TEST_VERIFY (buf == NULL); + TEST_COMPARE (errno, ENOMEM); + -+ /* Success */ -+ buf = aprintf ("foo %d", 42); -+ TEST_COMPARE_STRING (buf, "foo 42"); -+ free(buf); -+ + return 0; +} + @@ libio/vaprintf.c (new) +#include + +char * -+__vaprintf (const char *fmt, va_list args) ++__vaprintf (const char *fmt, va_list ap) +{ + char *p; + -+ return __vasprintf_internal (&p, fmt, args, 0) < 0 ? NULL : p; ++ return __vasprintf_internal (&p, fmt, ap, 0) < 0 ? NULL : p; +} +ldbl_weak_alias (__vaprintf, vaprintf) @@ manual/stdio.texi: other systems offer this function as an async-signal-safe alt ## stdio-common/Makefile ## @@ stdio-common/Makefile: routines := \ + _fitoa_word \ _itoa \ _itowa \ - asprintf \ + aprintf \ + asprintf \ ctermid \ cuserid \ - dprintf \ @@ stdio-common/Makefile: routines := \ + # Exclude fortified routines from being built with _FORTIFY_SOURCE routines_no_fortify += \ - asprintf \ + aprintf \ + asprintf \ dprintf \ fprintf \ - printf \ ## stdio-common/Versions ## @@ stdio-common/Versions: libc { @@ stdio-common/aprintf.c (new) + +ldbl_strong_alias (___aprintf, __aprintf) +ldbl_weak_alias (___aprintf, aprintf) + + ## sysdeps/ieee754/ldbl-128ibm-compat/Makefile ## +@@ sysdeps/ieee754/ldbl-128ibm-compat/Makefile: ldbl-extra-routines += fwscanf \ + swscanf \ + swprintf \ + wscanf \ ++ vaprintf \ + vasprintf \ + vdprintf \ + vscanf \ +@@ sysdeps/ieee754/ldbl-128ibm-compat/Makefile: endif + + ifeq ($(subdir),stdio-common) + ldbl-extra-routines += printf_size \ ++ aprintf \ + asprintf \ + dprintf \ + fprintf \ +@@ sysdeps/ieee754/ldbl-128ibm-compat/Makefile: $(objpfx)test-printf-size-ibm128.out: \ + endif + + ifeq ($(subdir),debug) +-ldbl-extra-routines += asprintf_chk \ ++ldbl-extra-routines += aprintf_chk \ ++ asprintf_chk \ + dprintf_chk \ + fprintf_chk \ + printf_chk \ + snprintf_chk \ + sprintf_chk \ ++ vaprintf_chk \ + vasprintf_chk \ + vdprintf_chk \ + vfprintf_chk \ +@@ sysdeps/ieee754/ldbl-128ibm-compat/Makefile: endif + routines += $(foreach r,$(ldbl-extra-routines),ieee128-$(r)) + + routines_no_fortify += \ ++ ieee128-aprintf \ + ieee128-asprintf \ + ieee128-dprintf \ + ieee128-fprintf \ +@@ sysdeps/ieee754/ldbl-128ibm-compat/Makefile: routines_no_fortify += \ + ieee128-sprintf \ + ieee128-swprintf \ + ieee128-syslog \ ++ ieee128-vaprintf \ + ieee128-vasprintf \ + ieee128-vdprintf \ + ieee128-vfprintf \ + + ## sysdeps/ieee754/ldbl-128ibm-compat/ieee128-aprintf.c (new) ## +@@ ++/* Wrapper for aprintf. IEEE128 version. ++ Copyright (C) 2026 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++ ++extern char * ++___ieee128___aprintf (const char *fmt, ...) ++{ ++ va_list ap; ++ char *p; ++ ++ va_start (ap, fmt); ++ if (__vasprintf_internal (&p, fmt, ap, PRINTF_LDBL_USES_FLOAT128) < 0) ++ p = NULL; ++ va_end (ap); ++ ++ return p; ++} ++hidden_def (___ieee128___aprintf) ++strong_alias (___ieee128___aprintf, __aprintfieee128) + + ## sysdeps/ieee754/ldbl-128ibm-compat/ieee128-aprintf_chk.c (new) ## +@@ ++/* Wrapper for __aprintf_chk. IEEE128 version. ++ Copyright (C) 2026 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++#include ++ ++extern char * ++___ieee128___aprintf_chk (int flag, const char *fmt, ...) ++{ ++ va_list ap; ++ char *p; ++ ++ unsigned int mode = PRINTF_LDBL_USES_FLOAT128; ++ if (flag > 0) ++ mode |= PRINTF_FORTIFY; ++ ++ va_start (ap, fmt); ++ if (__vasprintf_internal (&p, fmt, ap, mode) < 0) ++ p = NULL; ++ va_end (ap); ++ ++ return p; ++} ++hidden_def (___ieee128___aprintf_chk) ++strong_alias (___ieee128___aprintf_chk, __aprintf_chkieee128) + + ## sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vaprintf.c (new) ## +@@ ++/* Wrapper for vaprintf. IEEE128 version. ++ Copyright (C) 2026 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++ ++extern char * ++___ieee128_vaprintf (const char *fmt, va_list ap) ++{ ++ char *p; ++ if (__vasprintf_internal (&p, fmt, ap, PRINTF_LDBL_USES_FLOAT128) < 0) ++ p = NULL; ++ return p; ++} ++strong_alias (___ieee128_vaprintf, __vaprintfieee128) + + ## sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vaprintf_chk.c (new) ## +@@ ++/* Wrapper for __vaprintf_chk. IEEE128 version. ++ Copyright (C) 2026 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#include ++ ++extern char * ++___ieee128___vaprintf_chk (int flag, const char *fmt, va_list ap) ++{ ++ char *p; ++ unsigned int mode = PRINTF_LDBL_USES_FLOAT128; ++ if (flag > 0) ++ mode |= PRINTF_FORTIFY; ++ ++ return __vasprintf_internal (&p, fmt, ap, mode) < 0 ? NULL : p; ++} ++strong_alias (___ieee128___vaprintf_chk, __vaprintf_chkieee128) + + ## sysdeps/ieee754/ldbl-opt/Makefile ## +@@ sysdeps/ieee754/ldbl-opt/Makefile: libnldbl-calls = \ + acos \ + acosh \ + acospi \ ++ aprintf \ ++ aprintf_chk \ + asin \ + asinh \ + asinpi \ +@@ sysdeps/ieee754/ldbl-opt/Makefile: libnldbl-calls = \ + trunc \ + ufromfp \ + ufromfpx \ ++ vaprintf \ ++ vaprintf_chk \ + vasprintf \ + vasprintf_chk \ + vdprintf \ +@@ sysdeps/ieee754/ldbl-opt/Makefile: CFLAGS-tst-nldbl-wscanf-binary-gnu89.c += -mlong-double-64 -std=gnu89 \ + endif + + routines_no_fortify += \ ++ nldbl-aprintf \ + nldbl-asprintf \ + nldbl-dprintf \ + nldbl-fprintf \ +@@ sysdeps/ieee754/ldbl-opt/Makefile: routines_no_fortify += \ + nldbl-sprintf \ + nldbl-swprintf \ + nldbl-syslog \ ++ nldbl-vaprintf \ + nldbl-vasprintf \ + nldbl-vdprintf \ + nldbl-vfprintf \ + + ## sysdeps/ieee754/ldbl-opt/nldbl-aprintf.c (new) ## +@@ ++#include "nldbl-compat.h" ++ ++attribute_hidden ++char * ++__aprintf (const char *fmt, ...) ++{ ++ va_list ap; ++ char *p; ++ ++ va_start (ap, fmt); ++ p = __nldbl_vaprintf (fmt, ap); ++ va_end (ap); ++ ++ return p; ++} ++extern __typeof (__aprintf) aprintf attribute_hidden; ++weak_alias (__aprintf, aprintf) + + ## sysdeps/ieee754/ldbl-opt/nldbl-aprintf_chk.c (new) ## +@@ ++#include "nldbl-compat.h" ++ ++attribute_hidden ++char * ++__aprintf_chk (int flag, const char *fmt, ...) ++{ ++ va_list ap; ++ char *p; ++ ++ va_start (ap, fmt); ++ p = __nldbl___vaprintf_chk (flag, fmt, ap); ++ va_end (ap); ++ ++ return p; ++} + + ## sysdeps/ieee754/ldbl-opt/nldbl-vaprintf.c (new) ## +@@ ++#include "nldbl-compat.h" ++ ++attribute_hidden ++weak_function ++char * ++vaprintf (const char *fmt, va_list ap) ++{ ++ return __nldbl_vaprintf (fmt, ap); ++} + + ## sysdeps/ieee754/ldbl-opt/nldbl-vaprintf_chk.c (new) ## +@@ ++#include "nldbl-compat.h" ++ ++attribute_hidden ++char * ++__vaprintf_chk (int flag, const char *fmt, va_list ap) ++{ ++ return __nldbl___vaprintf_chk (flag, fmt, ap); ++} 2: 131b1f0c ! 2: f1dca5c0 manual/: Prefer aprintf(3) over asprintf(3) @@ Metadata ## Commit message ## manual/: Prefer aprintf(3) over asprintf(3) + While it's less portable at the moment, it's a better API (simpler, + safer, and less error-prone). + + The C Committee has expressed strong support for this API, intending to + standardize it. Thus, it should be more portable in the long term. + Signed-off-by: Alejandro Colomar ## manual/examples/rprintf.c ## base-commit: 9da7ad6d74700811c9b4c82b5f5eb555e39241a7