mirror of
https://git.savannah.gnu.org/git/gnulib.git
synced 2025-08-08 17:22:05 +03:00
string-buffer: New module.
* lib/string-buffer.h: New file. * lib/string-buffer.c: New file. * modules/string-buffer: New file. * doc/posix-functions/open_memstream.texi: Mention the new module.
This commit is contained in:
@@ -1,3 +1,11 @@
|
||||
2021-02-21 Bruno Haible <bruno@clisp.org>
|
||||
|
||||
string-buffer: New module.
|
||||
* lib/string-buffer.h: New file.
|
||||
* lib/string-buffer.c: New file.
|
||||
* modules/string-buffer: New file.
|
||||
* doc/posix-functions/open_memstream.texi: Mention the new module.
|
||||
|
||||
2021-02-21 Bruno Haible <bruno@clisp.org>
|
||||
|
||||
scratch_buffer: Document the exported API.
|
||||
|
@@ -16,3 +16,6 @@ Portability problems not fixed by Gnulib:
|
||||
This function is missing on some platforms:
|
||||
Mac OS X 10.5, FreeBSD 6.0, NetBSD 7.1, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, Solaris 11.3, Cygwin 1.5.x, mingw, MSVC 14, Android 5.1.
|
||||
@end itemize
|
||||
|
||||
An alternative to the @code{open_memstream} function is the Gnulib module
|
||||
@code{string-buffer}.
|
||||
|
297
lib/string-buffer.c
Normal file
297
lib/string-buffer.c
Normal file
@@ -0,0 +1,297 @@
|
||||
/* A buffer that accumulates a string by piecewise concatenation.
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Bruno Haible <bruno@clisp.org>, 2021. */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* Specification. */
|
||||
#include "string-buffer.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
sb_init (struct string_buffer *buffer)
|
||||
{
|
||||
buffer->data = buffer->space;
|
||||
buffer->length = 0;
|
||||
buffer->allocated = sizeof (buffer->space);
|
||||
buffer->error = false;
|
||||
}
|
||||
|
||||
/* Ensures that INCREMENT bytes are available beyond the current used length
|
||||
of BUFFER.
|
||||
Returns 0, or -1 in case of out-of-memory error. */
|
||||
static int
|
||||
sb_ensure_more_bytes (struct string_buffer *buffer, size_t increment)
|
||||
{
|
||||
size_t incremented_length = buffer->length + increment;
|
||||
if (incremented_length < increment)
|
||||
/* Overflow. */
|
||||
return -1;
|
||||
|
||||
if (buffer->allocated < incremented_length)
|
||||
{
|
||||
size_t new_allocated = 2 * buffer->allocated;
|
||||
if (new_allocated < buffer->allocated)
|
||||
/* Overflow. */
|
||||
return -1;
|
||||
if (new_allocated < incremented_length)
|
||||
new_allocated = incremented_length;
|
||||
|
||||
char *new_data;
|
||||
if (buffer->data == buffer->space)
|
||||
{
|
||||
new_data = (char *) malloc (new_allocated);
|
||||
if (new_data == NULL)
|
||||
/* Out-of-memory. */
|
||||
return -1;
|
||||
memcpy (new_data, buffer->data, buffer->length);
|
||||
}
|
||||
else
|
||||
{
|
||||
new_data = (char *) realloc (buffer->data, new_allocated);
|
||||
if (new_data == NULL)
|
||||
/* Out-of-memory. */
|
||||
return -1;
|
||||
}
|
||||
buffer->data = new_data;
|
||||
buffer->allocated = new_allocated;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sb_append (struct string_buffer *buffer, const char *str)
|
||||
{
|
||||
size_t len = strlen (str);
|
||||
if (sb_ensure_more_bytes (buffer, len) < 0)
|
||||
{
|
||||
buffer->error = true;
|
||||
return -1;
|
||||
}
|
||||
memcpy (buffer->data + buffer->length, str, len);
|
||||
buffer->length += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
sb_appendvf (struct string_buffer *buffer, const char *formatstring,
|
||||
va_list list)
|
||||
{
|
||||
va_list list_copy;
|
||||
|
||||
/* Make a bit of room, so that the probability that the first vsnprintf() 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;
|
||||
return -1;
|
||||
}
|
||||
room = buffer->allocated - buffer->length;
|
||||
}
|
||||
|
||||
va_copy (list_copy, list);
|
||||
|
||||
/* First vsnprintf() call. */
|
||||
int ret = vsnprintf (buffer->data + buffer->length, room, formatstring, list);
|
||||
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;
|
||||
ret = vsnprintf (buffer->data + buffer->length, room,
|
||||
formatstring, list_copy);
|
||||
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 return values of the vsnprintf() calls are not
|
||||
consistent. */
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
va_end (list_copy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
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. */
|
||||
size_t room = buffer->allocated - buffer->length;
|
||||
if (room < 64)
|
||||
{
|
||||
if (sb_ensure_more_bytes (buffer, 64) < 0)
|
||||
{
|
||||
buffer->error = true;
|
||||
return -1;
|
||||
}
|
||||
room = buffer->allocated - buffer->length;
|
||||
}
|
||||
|
||||
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,
|
||||
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 return values of the vsnprintf() calls are not
|
||||
consistent. */
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
va_end (args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
sb_free (struct string_buffer *buffer)
|
||||
{
|
||||
if (buffer->data != buffer->space)
|
||||
free (buffer->data);
|
||||
}
|
||||
|
||||
/* Returns the contents of BUFFER, and frees all other memory held
|
||||
by BUFFER. Returns NULL upon failure or if there was an error earlier. */
|
||||
char *
|
||||
sb_dupfree (struct string_buffer *buffer)
|
||||
{
|
||||
if (buffer->error)
|
||||
goto fail;
|
||||
|
||||
if (sb_ensure_more_bytes (buffer, 1) < 0)
|
||||
goto fail;
|
||||
buffer->data[buffer->length] = '\0';
|
||||
buffer->length++;
|
||||
|
||||
if (buffer->data == buffer->space)
|
||||
{
|
||||
char *copy = (char *) malloc (buffer->length);
|
||||
if (copy == NULL)
|
||||
goto fail;
|
||||
memcpy (copy, buffer->data, buffer->length);
|
||||
return copy;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Shrink the string before returning it. */
|
||||
char *contents = buffer->data;
|
||||
if (buffer->length < buffer->allocated)
|
||||
{
|
||||
contents = realloc (contents, buffer->length);
|
||||
if (contents == NULL)
|
||||
goto fail;
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
fail:
|
||||
sb_free (buffer);
|
||||
return NULL;
|
||||
}
|
85
lib/string-buffer.h
Normal file
85
lib/string-buffer.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* A buffer that accumulates a string by piecewise concatenation.
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Written by Bruno Haible <bruno@clisp.org>, 2021. */
|
||||
|
||||
#ifndef _STRING_BUFFER_H
|
||||
#define _STRING_BUFFER_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "attribute.h"
|
||||
|
||||
/* A string buffer type. */
|
||||
struct string_buffer
|
||||
{
|
||||
char *data;
|
||||
size_t length; /* used bytes, <= allocated */
|
||||
size_t allocated; /* allocated bytes */
|
||||
bool error; /* true if there was an error */
|
||||
char space[1024]; /* stack allocated space */
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializes BUFFER to the empty string. */
|
||||
extern void sb_init (struct string_buffer *buffer);
|
||||
|
||||
/* Appends the contents of STR to BUFFER.
|
||||
Returns 0, or -1 in case of out-of-memory error. */
|
||||
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. */
|
||||
extern int sb_appendvf (struct string_buffer *buffer,
|
||||
const char *formatstring, va_list list)
|
||||
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)
|
||||
ATTRIBUTE_FORMAT ((__gnu_printf__, 2, 0))
|
||||
#else
|
||||
ATTRIBUTE_FORMAT ((__printf__, 2, 0))
|
||||
#endif
|
||||
;
|
||||
|
||||
/* Appends the result of the printf-compatible FORMATSTRING with the following
|
||||
arguments to BUFFER.
|
||||
Returns 0, or -1 in case of error. */
|
||||
extern int sb_appendf (struct string_buffer *buffer,
|
||||
const char *formatstring, ...)
|
||||
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)
|
||||
ATTRIBUTE_FORMAT ((__gnu_printf__, 2, 3))
|
||||
#else
|
||||
ATTRIBUTE_FORMAT ((__printf__, 2, 3))
|
||||
#endif
|
||||
;
|
||||
|
||||
/* Frees the memory held by BUFFER. */
|
||||
extern void sb_free (struct string_buffer *buffer);
|
||||
|
||||
/* Returns the contents of BUFFER, and frees all other memory held
|
||||
by BUFFER. Returns NULL upon failure or if there was an error earlier.
|
||||
It is the responsibility of the caller to free() the result. */
|
||||
extern char * sb_dupfree (struct string_buffer *buffer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _STRING_BUFFER_H */
|
26
modules/string-buffer
Normal file
26
modules/string-buffer
Normal file
@@ -0,0 +1,26 @@
|
||||
Description:
|
||||
A buffer that accumulates a string by piecewise concatenation.
|
||||
|
||||
Files:
|
||||
lib/string-buffer.h
|
||||
lib/string-buffer.c
|
||||
|
||||
Depends-on:
|
||||
stdbool
|
||||
attribute
|
||||
stdarg
|
||||
vsnprintf-posix
|
||||
|
||||
configure.ac:
|
||||
|
||||
Makefile.am:
|
||||
lib_SOURCES += string-buffer.c
|
||||
|
||||
Include:
|
||||
"string-buffer.h"
|
||||
|
||||
License:
|
||||
LGPLv2+
|
||||
|
||||
Maintainer:
|
||||
all
|
Reference in New Issue
Block a user