mirror of
https://git.savannah.gnu.org/git/gnulib.git
synced 2025-08-08 17:22:05 +03:00
string-buffer: Remove INT_MAX limitation.
* lib/string-buffer.h (sb_appendvf, sb_appendf): Document that errno is set upon failure. * lib/string-buffer-printf.c: Include <errno.h>. (sb_appendvf): Call vsnzprintf instead of vsnprintf. Ensure errno is set upon failure. (sb_appendf): Likewise. * modules/string-buffer (Depends-on): Add vsnzprintf-posix. Remove vsnprintf-posix. * tests/test-string-buffer.c (main): Test error code from sb_appendf.
This commit is contained in:
13
ChangeLog
13
ChangeLog
@@ -1,3 +1,16 @@
|
||||
2024-09-24 Bruno Haible <bruno@clisp.org>
|
||||
|
||||
string-buffer: Remove INT_MAX limitation.
|
||||
* lib/string-buffer.h (sb_appendvf, sb_appendf): Document that errno is
|
||||
set upon failure.
|
||||
* lib/string-buffer-printf.c: Include <errno.h>.
|
||||
(sb_appendvf): Call vsnzprintf instead of vsnprintf. Ensure errno is
|
||||
set upon failure.
|
||||
(sb_appendf): Likewise.
|
||||
* modules/string-buffer (Depends-on): Add vsnzprintf-posix. Remove
|
||||
vsnprintf-posix.
|
||||
* tests/test-string-buffer.c (main): Test error code from sb_appendf.
|
||||
|
||||
2024-09-24 Bruno Haible <bruno@clisp.org>
|
||||
|
||||
string-buffer: Link to vasnprintf implementation only when needed.
|
||||
|
@@ -25,6 +25,7 @@
|
||||
extern int sb_ensure_more_bytes (struct string_buffer *buffer,
|
||||
size_t increment);
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -35,14 +36,15 @@ sb_appendvf (struct string_buffer *buffer, const char *formatstring,
|
||||
{
|
||||
va_list list_copy;
|
||||
|
||||
/* Make a bit of room, so that the probability that the first vsnprintf() call
|
||||
succeeds is high. */
|
||||
/* Make a bit of room, so that the probability that the first vsnzprintf()
|
||||
call succeeds is high. */
|
||||
size_t room = buffer->allocated - buffer->length;
|
||||
if (room < 64)
|
||||
{
|
||||
if (sb_ensure_more_bytes (buffer, 64) < 0)
|
||||
{
|
||||
buffer->error = true;
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
room = buffer->allocated - buffer->length;
|
||||
@@ -50,17 +52,18 @@ sb_appendvf (struct string_buffer *buffer, const char *formatstring,
|
||||
|
||||
va_copy (list_copy, list);
|
||||
|
||||
/* First vsnprintf() call. */
|
||||
int ret = vsnprintf (buffer->data + buffer->length, room, formatstring, list);
|
||||
/* First vsnzprintf() call. */
|
||||
ptrdiff_t ret = vsnzprintf (buffer->data + buffer->length, room,
|
||||
formatstring, list);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* Failed. */
|
||||
/* Failed. errno is set. */
|
||||
buffer->error = true;
|
||||
ret = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((size_t) ret <= room)
|
||||
if (ret <= room)
|
||||
{
|
||||
/* The result has fit into room bytes. */
|
||||
buffer->length += (size_t) ret;
|
||||
@@ -68,35 +71,36 @@ sb_appendvf (struct string_buffer *buffer, const char *formatstring,
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The result was truncated. Make more room, for a second vsnprintf()
|
||||
call. */
|
||||
/* The result was truncated. Make more room, for a second
|
||||
vsnzprintf() call. */
|
||||
if (sb_ensure_more_bytes (buffer, (size_t) ret) < 0)
|
||||
{
|
||||
buffer->error = true;
|
||||
errno = ENOMEM;
|
||||
ret = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Second vsnprintf() call. */
|
||||
/* Second vsnzprintf() call. */
|
||||
room = buffer->allocated - buffer->length;
|
||||
ret = vsnprintf (buffer->data + buffer->length, room,
|
||||
ret = vsnzprintf (buffer->data + buffer->length, room,
|
||||
formatstring, list_copy);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* Failed. */
|
||||
/* Failed. errno is set. */
|
||||
buffer->error = true;
|
||||
ret = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((size_t) ret <= room)
|
||||
if (ret <= room)
|
||||
{
|
||||
/* The result has fit into room bytes. */
|
||||
buffer->length += (size_t) ret;
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
/* The return values of the vsnprintf() calls are not
|
||||
/* The return values of the vsnzprintf() calls are not
|
||||
consistent. */
|
||||
abort ();
|
||||
}
|
||||
@@ -113,14 +117,15 @@ sb_appendf (struct string_buffer *buffer, const char *formatstring, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
/* Make a bit of room, so that the probability that the first vsnprintf() call
|
||||
succeeds is high. */
|
||||
/* Make a bit of room, so that the probability that the first vsnzprintf()
|
||||
call succeeds is high. */
|
||||
size_t room = buffer->allocated - buffer->length;
|
||||
if (room < 64)
|
||||
{
|
||||
if (sb_ensure_more_bytes (buffer, 64) < 0)
|
||||
{
|
||||
buffer->error = true;
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
room = buffer->allocated - buffer->length;
|
||||
@@ -128,55 +133,57 @@ sb_appendf (struct string_buffer *buffer, const char *formatstring, ...)
|
||||
|
||||
va_start (args, formatstring);
|
||||
|
||||
/* First vsnprintf() call. */
|
||||
int ret = vsnprintf (buffer->data + buffer->length, room, formatstring, args);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* Failed. */
|
||||
buffer->error = true;
|
||||
ret = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((size_t) ret <= room)
|
||||
{
|
||||
/* The result has fit into room bytes. */
|
||||
buffer->length += (size_t) ret;
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The result was truncated. Make more room, for a second vsnprintf()
|
||||
call. */
|
||||
if (sb_ensure_more_bytes (buffer, (size_t) ret) < 0)
|
||||
{
|
||||
buffer->error = true;
|
||||
ret = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Second vsnprintf() call. */
|
||||
room = buffer->allocated - buffer->length;
|
||||
va_end (args);
|
||||
va_start (args, formatstring);
|
||||
ret = vsnprintf (buffer->data + buffer->length, room,
|
||||
/* First vsnzprintf() call. */
|
||||
ptrdiff_t ret = vsnzprintf (buffer->data + buffer->length, room,
|
||||
formatstring, args);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* Failed. */
|
||||
/* Failed. errno is set. */
|
||||
buffer->error = true;
|
||||
ret = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((size_t) ret <= room)
|
||||
if (ret <= room)
|
||||
{
|
||||
/* The result has fit into room bytes. */
|
||||
buffer->length += (size_t) ret;
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
/* The return values of the vsnprintf() calls are not
|
||||
{
|
||||
/* The result was truncated. Make more room, for a second
|
||||
vsnzprintf() call. */
|
||||
if (sb_ensure_more_bytes (buffer, (size_t) ret) < 0)
|
||||
{
|
||||
buffer->error = true;
|
||||
errno = ENOMEM;
|
||||
ret = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Second vsnzprintf() call. */
|
||||
room = buffer->allocated - buffer->length;
|
||||
va_end (args);
|
||||
va_start (args, formatstring);
|
||||
ret = vsnzprintf (buffer->data + buffer->length, room,
|
||||
formatstring, args);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* Failed. errno is set. */
|
||||
buffer->error = true;
|
||||
ret = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ret <= room)
|
||||
{
|
||||
/* The result has fit into room bytes. */
|
||||
buffer->length += (size_t) ret;
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
/* The return values of the vsnzprintf() calls are not
|
||||
consistent. */
|
||||
abort ();
|
||||
}
|
||||
|
@@ -52,7 +52,10 @@ extern int sb_append (struct string_buffer *buffer, const char *str);
|
||||
|
||||
/* Appends the result of the printf-compatible FORMATSTRING with the argument
|
||||
list LIST to BUFFER.
|
||||
Returns 0, or -1 in case of error. */
|
||||
Returns 0, or -1 with errno set in case of error.
|
||||
Error code EOVERFLOW can only occur when a width > INT_MAX is used.
|
||||
Therefore, if the format string is valid and does not use %ls/%lc
|
||||
directives nor widths, the only possible error code is ENOMEM. */
|
||||
extern int sb_appendvf (struct string_buffer *buffer,
|
||||
const char *formatstring, va_list list)
|
||||
#if (__GNUC__ + (__GNUC_MINOR__ >= 4) > 4) && !defined __clang__
|
||||
@@ -64,7 +67,10 @@ extern int sb_appendvf (struct string_buffer *buffer,
|
||||
|
||||
/* Appends the result of the printf-compatible FORMATSTRING with the following
|
||||
arguments to BUFFER.
|
||||
Returns 0, or -1 in case of error. */
|
||||
Returns 0, or -1 with errno set in case of error.
|
||||
Error code EOVERFLOW can only occur when a width > INT_MAX is used.
|
||||
Therefore, if the format string is valid and does not use %ls/%lc
|
||||
directives nor widths, the only possible error code is ENOMEM. */
|
||||
extern int sb_appendf (struct string_buffer *buffer,
|
||||
const char *formatstring, ...)
|
||||
#if (__GNUC__ + (__GNUC_MINOR__ >= 4) > 4) && !defined __clang__
|
||||
|
@@ -10,7 +10,7 @@ Depends-on:
|
||||
stdbool
|
||||
attribute
|
||||
stdarg
|
||||
vsnprintf-posix
|
||||
vsnzprintf-posix
|
||||
|
||||
configure.ac:
|
||||
|
||||
|
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "string-buffer.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
@@ -94,19 +95,33 @@ main ()
|
||||
/* Test printf-like formatting failure.
|
||||
On all systems except AIX, trying to convert the wide-character 0x76543210
|
||||
to a multibyte string (in the "C" locale) fails.
|
||||
On all systems where REPLACE_VSNPRINTF=1 (this includes AIX), i.e. where
|
||||
the Gnulib implementation of vsnprintf() is used), invalid format
|
||||
directives make the *printf call fail. */
|
||||
On all systems, invalid format directives make the vsnzprintf() call
|
||||
fail. */
|
||||
{
|
||||
struct string_buffer buffer;
|
||||
int ret;
|
||||
|
||||
sb_init (&buffer);
|
||||
sb_append (&buffer, "<");
|
||||
sb_appendf (&buffer, "%lc", (wint_t) 0x76543210);
|
||||
|
||||
ret = sb_appendf (&buffer, "%lc", (wint_t) 0x76543210);
|
||||
#if !defined _AIX
|
||||
ASSERT (ret < 0);
|
||||
ASSERT (errno == EILSEQ);
|
||||
#endif
|
||||
|
||||
sb_append (&buffer, "|");
|
||||
sb_appendf (&buffer, invalid_format_string_1, 1);
|
||||
|
||||
ret = sb_appendf (&buffer, invalid_format_string_1, 1);
|
||||
ASSERT (ret < 0);
|
||||
ASSERT (errno == EINVAL);
|
||||
|
||||
sb_append (&buffer, "|");
|
||||
sb_appendf (&buffer, invalid_format_string_2, 2);
|
||||
|
||||
ret = sb_appendf (&buffer, invalid_format_string_2, 2);
|
||||
ASSERT (ret < 0);
|
||||
ASSERT (errno == EINVAL);
|
||||
|
||||
sb_append (&buffer, ">");
|
||||
char *s = sb_dupfree (&buffer);
|
||||
ASSERT (s == NULL);
|
||||
|
Reference in New Issue
Block a user