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>
|
2021-02-21 Bruno Haible <bruno@clisp.org>
|
||||||
|
|
||||||
scratch_buffer: Document the exported API.
|
scratch_buffer: Document the exported API.
|
||||||
|
@@ -16,3 +16,6 @@ Portability problems not fixed by Gnulib:
|
|||||||
This function is missing on some platforms:
|
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.
|
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
|
@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