1
0
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:
Bruno Haible
2024-09-24 18:31:49 +02:00
parent 413cbae0c2
commit 778a55c4a8
5 changed files with 78 additions and 37 deletions

View File

@@ -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> 2024-09-24 Bruno Haible <bruno@clisp.org>
string-buffer: Link to vasnprintf implementation only when needed. string-buffer: Link to vasnprintf implementation only when needed.

View File

@@ -25,6 +25,7 @@
extern int sb_ensure_more_bytes (struct string_buffer *buffer, extern int sb_ensure_more_bytes (struct string_buffer *buffer,
size_t increment); size_t increment);
#include <errno.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -35,14 +36,15 @@ sb_appendvf (struct string_buffer *buffer, const char *formatstring,
{ {
va_list list_copy; va_list list_copy;
/* Make a bit of room, so that the probability that the first vsnprintf() call /* Make a bit of room, so that the probability that the first vsnzprintf()
succeeds is high. */ call succeeds is high. */
size_t room = buffer->allocated - buffer->length; size_t room = buffer->allocated - buffer->length;
if (room < 64) if (room < 64)
{ {
if (sb_ensure_more_bytes (buffer, 64) < 0) if (sb_ensure_more_bytes (buffer, 64) < 0)
{ {
buffer->error = true; buffer->error = true;
errno = ENOMEM;
return -1; return -1;
} }
room = buffer->allocated - buffer->length; room = buffer->allocated - buffer->length;
@@ -50,17 +52,18 @@ sb_appendvf (struct string_buffer *buffer, const char *formatstring,
va_copy (list_copy, list); va_copy (list_copy, list);
/* First vsnprintf() call. */ /* First vsnzprintf() call. */
int ret = vsnprintf (buffer->data + buffer->length, room, formatstring, list); ptrdiff_t ret = vsnzprintf (buffer->data + buffer->length, room,
formatstring, list);
if (ret < 0) if (ret < 0)
{ {
/* Failed. */ /* Failed. errno is set. */
buffer->error = true; buffer->error = true;
ret = -1; ret = -1;
} }
else else
{ {
if ((size_t) ret <= room) if (ret <= room)
{ {
/* The result has fit into room bytes. */ /* The result has fit into room bytes. */
buffer->length += (size_t) ret; buffer->length += (size_t) ret;
@@ -68,35 +71,36 @@ sb_appendvf (struct string_buffer *buffer, const char *formatstring,
} }
else else
{ {
/* The result was truncated. Make more room, for a second vsnprintf() /* The result was truncated. Make more room, for a second
call. */ vsnzprintf() call. */
if (sb_ensure_more_bytes (buffer, (size_t) ret) < 0) if (sb_ensure_more_bytes (buffer, (size_t) ret) < 0)
{ {
buffer->error = true; buffer->error = true;
errno = ENOMEM;
ret = -1; ret = -1;
} }
else else
{ {
/* Second vsnprintf() call. */ /* Second vsnzprintf() call. */
room = buffer->allocated - buffer->length; room = buffer->allocated - buffer->length;
ret = vsnprintf (buffer->data + buffer->length, room, ret = vsnzprintf (buffer->data + buffer->length, room,
formatstring, list_copy); formatstring, list_copy);
if (ret < 0) if (ret < 0)
{ {
/* Failed. */ /* Failed. errno is set. */
buffer->error = true; buffer->error = true;
ret = -1; ret = -1;
} }
else else
{ {
if ((size_t) ret <= room) if (ret <= room)
{ {
/* The result has fit into room bytes. */ /* The result has fit into room bytes. */
buffer->length += (size_t) ret; buffer->length += (size_t) ret;
ret = 0; ret = 0;
} }
else else
/* The return values of the vsnprintf() calls are not /* The return values of the vsnzprintf() calls are not
consistent. */ consistent. */
abort (); abort ();
} }
@@ -113,14 +117,15 @@ sb_appendf (struct string_buffer *buffer, const char *formatstring, ...)
{ {
va_list args; va_list args;
/* Make a bit of room, so that the probability that the first vsnprintf() call /* Make a bit of room, so that the probability that the first vsnzprintf()
succeeds is high. */ call succeeds is high. */
size_t room = buffer->allocated - buffer->length; size_t room = buffer->allocated - buffer->length;
if (room < 64) if (room < 64)
{ {
if (sb_ensure_more_bytes (buffer, 64) < 0) if (sb_ensure_more_bytes (buffer, 64) < 0)
{ {
buffer->error = true; buffer->error = true;
errno = ENOMEM;
return -1; return -1;
} }
room = buffer->allocated - buffer->length; room = buffer->allocated - buffer->length;
@@ -128,17 +133,18 @@ sb_appendf (struct string_buffer *buffer, const char *formatstring, ...)
va_start (args, formatstring); va_start (args, formatstring);
/* First vsnprintf() call. */ /* First vsnzprintf() call. */
int ret = vsnprintf (buffer->data + buffer->length, room, formatstring, args); ptrdiff_t ret = vsnzprintf (buffer->data + buffer->length, room,
formatstring, args);
if (ret < 0) if (ret < 0)
{ {
/* Failed. */ /* Failed. errno is set. */
buffer->error = true; buffer->error = true;
ret = -1; ret = -1;
} }
else else
{ {
if ((size_t) ret <= room) if (ret <= room)
{ {
/* The result has fit into room bytes. */ /* The result has fit into room bytes. */
buffer->length += (size_t) ret; buffer->length += (size_t) ret;
@@ -146,37 +152,38 @@ sb_appendf (struct string_buffer *buffer, const char *formatstring, ...)
} }
else else
{ {
/* The result was truncated. Make more room, for a second vsnprintf() /* The result was truncated. Make more room, for a second
call. */ vsnzprintf() call. */
if (sb_ensure_more_bytes (buffer, (size_t) ret) < 0) if (sb_ensure_more_bytes (buffer, (size_t) ret) < 0)
{ {
buffer->error = true; buffer->error = true;
errno = ENOMEM;
ret = -1; ret = -1;
} }
else else
{ {
/* Second vsnprintf() call. */ /* Second vsnzprintf() call. */
room = buffer->allocated - buffer->length; room = buffer->allocated - buffer->length;
va_end (args); va_end (args);
va_start (args, formatstring); va_start (args, formatstring);
ret = vsnprintf (buffer->data + buffer->length, room, ret = vsnzprintf (buffer->data + buffer->length, room,
formatstring, args); formatstring, args);
if (ret < 0) if (ret < 0)
{ {
/* Failed. */ /* Failed. errno is set. */
buffer->error = true; buffer->error = true;
ret = -1; ret = -1;
} }
else else
{ {
if ((size_t) ret <= room) if (ret <= room)
{ {
/* The result has fit into room bytes. */ /* The result has fit into room bytes. */
buffer->length += (size_t) ret; buffer->length += (size_t) ret;
ret = 0; ret = 0;
} }
else else
/* The return values of the vsnprintf() calls are not /* The return values of the vsnzprintf() calls are not
consistent. */ consistent. */
abort (); abort ();
} }

View File

@@ -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 /* Appends the result of the printf-compatible FORMATSTRING with the argument
list LIST to BUFFER. 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, extern int sb_appendvf (struct string_buffer *buffer,
const char *formatstring, va_list list) const char *formatstring, va_list list)
#if (__GNUC__ + (__GNUC_MINOR__ >= 4) > 4) && !defined __clang__ #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 /* Appends the result of the printf-compatible FORMATSTRING with the following
arguments to BUFFER. 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, extern int sb_appendf (struct string_buffer *buffer,
const char *formatstring, ...) const char *formatstring, ...)
#if (__GNUC__ + (__GNUC_MINOR__ >= 4) > 4) && !defined __clang__ #if (__GNUC__ + (__GNUC_MINOR__ >= 4) > 4) && !defined __clang__

View File

@@ -10,7 +10,7 @@ Depends-on:
stdbool stdbool
attribute attribute
stdarg stdarg
vsnprintf-posix vsnzprintf-posix
configure.ac: configure.ac:

View File

@@ -20,6 +20,7 @@
#include "string-buffer.h" #include "string-buffer.h"
#include <errno.h>
#include <string.h> #include <string.h>
#include <wchar.h> #include <wchar.h>
@@ -94,19 +95,33 @@ main ()
/* Test printf-like formatting failure. /* Test printf-like formatting failure.
On all systems except AIX, trying to convert the wide-character 0x76543210 On all systems except AIX, trying to convert the wide-character 0x76543210
to a multibyte string (in the "C" locale) fails. to a multibyte string (in the "C" locale) fails.
On all systems where REPLACE_VSNPRINTF=1 (this includes AIX), i.e. where On all systems, invalid format directives make the vsnzprintf() call
the Gnulib implementation of vsnprintf() is used), invalid format fail. */
directives make the *printf call fail. */
{ {
struct string_buffer buffer; struct string_buffer buffer;
int ret;
sb_init (&buffer); sb_init (&buffer);
sb_append (&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_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_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, ">"); sb_append (&buffer, ">");
char *s = sb_dupfree (&buffer); char *s = sb_dupfree (&buffer);
ASSERT (s == NULL); ASSERT (s == NULL);