]> git.ipfire.org Git - thirdparty/glibc.git/blame - libio/vasprintf.c
manual: Remove '.info' suffix in manual names passed to @ref [BZ #32962].
[thirdparty/glibc.git] / libio / vasprintf.c
CommitLineData
26420023 1/* Copyright (C) 1995-2025 Free Software Foundation, Inc.
41bdb6e2 2 This file is part of the GNU C Library.
96aa2d94 3
41bdb6e2
AJ
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
96aa2d94 8
41bdb6e2
AJ
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
40a55d20 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41bdb6e2 12 Lesser General Public License for more details.
96aa2d94 13
41bdb6e2 14 You should have received a copy of the GNU Lesser General Public
59ba27a6 15 License along with the GNU C Library; if not, see
5a82c748 16 <https://www.gnu.org/licenses/>.
96aa2d94 17
41bdb6e2
AJ
18 As a special exception, if you link the code in this file with
19 files compiled with a GNU compiler to produce an executable,
20 that does not cause the resulting executable to be covered by
21 the GNU Lesser General Public License. This exception does not
22 however invalidate any other reasons why the executable file
23 might be covered by the GNU Lesser General Public License.
24 This exception applies to code released by its copyright holders
25 in files containing the exception. */
96aa2d94 26
af7f4165
FW
27#include <array_length.h>
28#include <errno.h>
29#include <limits.h>
30#include <math_ldbl_opt.h>
31#include <printf.h>
32#include <stdio.h>
698fb75b 33#include <stdlib.h>
af7f4165
FW
34#include <string.h>
35#include <printf_buffer.h>
36
37struct __printf_buffer_asprintf
38{
39 /* base.write_base points either to a heap-allocated buffer, or to
40 the direct array below. */
41 struct __printf_buffer base;
42
43 /* Initial allocation. 200 should be large enough to copy almost
44 all asprintf usages with just a single (final, correctly sized)
45 heap allocation. */
46 char direct[PRINTF_BUFFER_SIZE_ASPRINTF];
47};
48
49void
50__printf_buffer_flush_asprintf (struct __printf_buffer_asprintf *buf)
51{
52 size_t current_pos = buf->base.write_ptr - buf->base.write_base;
53 if (current_pos >= INT_MAX)
54 {
55 /* The result is not representable. No need to continue. */
56 __set_errno (EOVERFLOW);
57 __printf_buffer_mark_failed (&buf->base);
58 return;
59 }
60
61 size_t current_size = buf->base.write_end - buf->base.write_base;
62 /* Implement an exponentiation sizing policy. Keep the size
63 congruent 8 (mod 16), to account for the footer in glibc
64 malloc. */
65 size_t new_size = ALIGN_UP (current_size + current_size / 2, 16) | 8;
66 char *new_buffer;
67 if (buf->base.write_base == buf->direct)
68 {
69 new_buffer = malloc (new_size);
70 if (new_buffer == NULL)
71 {
72 __printf_buffer_mark_failed (&buf->base);
73 return;
74 }
75 memcpy (new_buffer, buf->direct, current_pos);
76 }
77 else
78 {
79 new_buffer = realloc (buf->base.write_base, new_size);
80 if (new_buffer == NULL)
81 {
82 __printf_buffer_mark_failed (&buf->base);
83 return;
84 }
85 }
86
87 /* Set up the new write area. */
88 buf->base.write_base = new_buffer;
89 buf->base.write_ptr = new_buffer + current_pos;
90 buf->base.write_end = new_buffer + new_size;
91}
92
96aa2d94
RM
93
94int
cb4692ce 95__vasprintf_internal (char **result, const char *format, va_list args,
698fb75b 96 unsigned int mode_flags)
96aa2d94 97{
af7f4165
FW
98 struct __printf_buffer_asprintf buf;
99 __printf_buffer_init (&buf.base, buf.direct, array_length (buf.direct),
100 __printf_buffer_mode_asprintf);
101
102 __printf_buffer (&buf.base, format, args, mode_flags);
103 int done = __printf_buffer_done (&buf.base);
104 if (done < 0)
383bd1c5 105 {
af7f4165
FW
106 if (buf.base.write_base != buf.direct)
107 free (buf.base.write_base);
cb4692ce 108 *result = NULL;
af7f4165
FW
109 return done;
110 }
111
112 /* Transfer to the final buffer. */
af7f4165
FW
113 size_t size = buf.base.write_ptr - buf.base.write_base;
114 if (buf.base.write_base == buf.direct)
115 {
cb4692ce
FW
116 *result = malloc (size + 1);
117 if (*result == NULL)
af7f4165 118 return -1;
cb4692ce 119 memcpy (*result, buf.direct, size);
383bd1c5 120 }
4100c5a0
UD
121 else
122 {
cb4692ce
FW
123 *result = realloc (buf.base.write_base, size + 1);
124 if (*result == NULL)
4100c5a0 125 {
af7f4165
FW
126 free (buf.base.write_base);
127 return -1;
4100c5a0 128 }
4100c5a0 129 }
af7f4165
FW
130
131 /* Add NUL termination. */
cb4692ce 132 (*result)[size] = '\0';
af7f4165
FW
133
134 return done;
96aa2d94 135}
698fb75b
ZW
136
137int
138__vasprintf (char **result_ptr, const char *format, va_list args)
139{
140 return __vasprintf_internal (result_ptr, format, args, 0);
141}
142ldbl_weak_alias (__vasprintf, vasprintf)