|
|
@ -0,0 +1,231 @@ |
|
|
|
/* |
|
|
|
* sbuffer - Incenp.org Notch Library: string buffer module |
|
|
|
* Copyright (C) 2011 Damien Goutte-Gattat |
|
|
|
* |
|
|
|
* 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 3 of the License, 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 <http://www.gnu.org/licenses/>. |
|
|
|
*/ |
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H |
|
|
|
#include <config.h> |
|
|
|
#endif |
|
|
|
|
|
|
|
#include <sbuffer.h> |
|
|
|
#include <stdio.h> |
|
|
|
#include <errno.h> |
|
|
|
#include <string.h> |
|
|
|
|
|
|
|
#ifdef EXIT_ON_ENOMEM |
|
|
|
#include <xmem.h> |
|
|
|
#endif |
|
|
|
|
|
|
|
struct string_buffer_t |
|
|
|
{ |
|
|
|
char *buffer; |
|
|
|
size_t len; |
|
|
|
size_t size; |
|
|
|
size_t block; |
|
|
|
}; |
|
|
|
|
|
|
|
#define AVAILABLE_SIZE(s) ((s)->size - (s)->len) |
|
|
|
#define CURSOR(s) ((s)->buffer + (s)->len) |
|
|
|
|
|
|
|
#ifdef EXIT_ON_NOMEM |
|
|
|
|
|
|
|
#define GROW(s,l) \ |
|
|
|
do { \ |
|
|
|
(s)->size = ((((s)->len + (l)) / (s)->block) + 1) * (s)->block; \ |
|
|
|
(s)->buffer = xrealloc((s)->buffer, (s)->len); \ |
|
|
|
} while ( 0 ) |
|
|
|
|
|
|
|
#else |
|
|
|
|
|
|
|
#define GROW(s,l) \ |
|
|
|
do { \ |
|
|
|
size_t ns = ((((s)->len + (l)) / (s)->block) + 1) * (s)->block; \ |
|
|
|
char *np = realloc((s)->buffer, ns); \ |
|
|
|
if ( ! np ) \ |
|
|
|
return -1; \ |
|
|
|
(s)->buffer = np; \ |
|
|
|
(s)->size = ns; \ |
|
|
|
} while ( 0 ) |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
string_buffer_t * |
|
|
|
sb_new(size_t block_size) |
|
|
|
{ |
|
|
|
string_buffer_t *s; |
|
|
|
|
|
|
|
if ( ! block_size ) |
|
|
|
block_size = 128; |
|
|
|
|
|
|
|
#ifdef EXIT_ON_ENOMEM |
|
|
|
s = xmalloc(sizeof(*s)); |
|
|
|
s->block = s->size = block_size; |
|
|
|
s->buffer = xmalloc(block_size); |
|
|
|
sb_empty(s); |
|
|
|
#else |
|
|
|
if ( (s = malloc(sizeof(*s))) ) { |
|
|
|
s->block = s->size = block_size; |
|
|
|
if ( (s->buffer = malloc(block_size)) ) |
|
|
|
sb_empty(s); |
|
|
|
else { |
|
|
|
free(s); |
|
|
|
s = NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
return s; |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
sb_free(string_buffer_t *s) |
|
|
|
{ |
|
|
|
if ( s ) { |
|
|
|
free(s->buffer); |
|
|
|
free(s); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const char * |
|
|
|
sb_get(string_buffer_t *s) |
|
|
|
{ |
|
|
|
return s ? s->buffer : NULL; |
|
|
|
} |
|
|
|
|
|
|
|
char * |
|
|
|
sb_get_copy(string_buffer_t *s) |
|
|
|
{ |
|
|
|
char *str = NULL; |
|
|
|
|
|
|
|
if ( s ) { |
|
|
|
#ifdef EXIT_ON_ENOMEM |
|
|
|
str = xmalloc(s->len + 1); |
|
|
|
#else |
|
|
|
if ( ! (str = malloc(s->len + 1)) ) |
|
|
|
return NULL; |
|
|
|
#endif |
|
|
|
strncpy(str, s->buffer, s->len); |
|
|
|
} |
|
|
|
|
|
|
|
return str; |
|
|
|
} |
|
|
|
|
|
|
|
size_t |
|
|
|
sb_len(string_buffer_t *s) |
|
|
|
{ |
|
|
|
return s ? s->len : 0; |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
sb_empty(string_buffer_t *s) |
|
|
|
{ |
|
|
|
if ( ! s ) { |
|
|
|
errno = EINVAL; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
s->buffer[0] = '\0'; |
|
|
|
s->len = 0; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
sb_addc(string_buffer_t *s, char c) |
|
|
|
{ |
|
|
|
if ( ! s ) { |
|
|
|
errno = EINVAL; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
if ( AVAILABLE_SIZE(s) < 1 ) |
|
|
|
GROW(s, 1); |
|
|
|
|
|
|
|
*(s->buffer + s->len++) = c; |
|
|
|
*(s->buffer + s->len) = '\0'; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
sb_add(string_buffer_t *s, const char *buffer) |
|
|
|
{ |
|
|
|
if ( ! buffer ) { |
|
|
|
errno = EINVAL; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
return sb_addn(s, buffer, strlen(buffer)); |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
sb_addn(string_buffer_t *s, const char *buffer, size_t len) |
|
|
|
{ |
|
|
|
if ( ! s || ! buffer ) { |
|
|
|
errno = EINVAL; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
if ( AVAILABLE_SIZE(s) < len ) |
|
|
|
GROW(s, len); |
|
|
|
|
|
|
|
strncat(CURSOR(s), buffer, len); |
|
|
|
s->len += len; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
sb_vaddf(string_buffer_t *s, const char *fmt, va_list ap) |
|
|
|
{ |
|
|
|
int written; |
|
|
|
|
|
|
|
if ( ! s || ! fmt ) { |
|
|
|
errno = EINVAL; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
written = vsnprintf(CURSOR(s), AVAILABLE_SIZE(s), fmt, ap); |
|
|
|
if ( written >= AVAILABLE_SIZE(s) ) { |
|
|
|
va_list aq; |
|
|
|
|
|
|
|
GROW(s, written); |
|
|
|
va_copy(aq, ap); |
|
|
|
vsprintf(CURSOR(s), fmt, aq); |
|
|
|
va_end(aq); |
|
|
|
} |
|
|
|
s->len += written; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
sb_addf(string_buffer_t *s, const char *fmt, ...) |
|
|
|
{ |
|
|
|
int ret; |
|
|
|
va_list ap; |
|
|
|
|
|
|
|
if ( ! s || ! fmt ) { |
|
|
|
errno = EINVAL; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
va_start(ap, fmt); |
|
|
|
ret = sb_vaddf(s, fmt, ap); |
|
|
|
va_end(ap); |
|
|
|
|
|
|
|
return ret; |
|
|
|
} |