This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
libcfu/src/cfustring.c

628 lines
13 KiB
C

/*
* cfustring.c - This file is part of the libcfu library
*
* Copyright (c) 2005 Don Owens. All rights reserved.
*
* This code is released under the BSD license:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* * Neither the name of the author nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "cfu.h"
#include "cfustring.h"
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#ifndef HAVE_VSNPRINTF
int rpl_vsnprintf(char *, size_t, const char *, va_list);
# define vsnprintf rpl_vsnprintf
#endif
struct cfustring {
libcfu_type type;
size_t max_size;
size_t used_size;
char *str;
};
cfustring_t *
cfustring_new(void) {
return cfustring_new_with_initial_size(0);
}
cfustring_t *
cfustring_new_with_initial_size(size_t initial_size) {
cfustring_t *cfu_str = calloc(1, sizeof(cfustring_t));
cfu_str->type = libcfu_t_string;
if (initial_size > 0) {
cfu_str->str = calloc(initial_size, 1);
cfu_str->max_size = initial_size;
cfu_str->used_size = 1;
}
return cfu_str;
}
cfustring_t *
cfustring_new_from_string(const char *string) {
cfustring_t *cfu_str = cfustring_new();
cfustring_append(cfu_str, string);
return cfu_str;
}
int
cfustring_dup(cfustring_t *cfu_str, const char *string) {
if (!string) {
cfu_str->max_size = 0;
cfu_str->used_size = 0;
free(cfu_str->str);
cfu_str->str = NULL;
return 1;
}
cfustring_clear(cfu_str);
cfustring_append(cfu_str, string);
return 1;
}
int
cfustring_clear(cfustring_t *cfu_str) {
if (cfu_str->str) {
cfu_str->str[0] = '\000';
cfu_str->used_size = 1;
}
return 1;
}
int
cfustring_append_n(cfustring_t *cfu_str, const char *string, size_t n) {
size_t str_len = 0;
if (!string) return 1;
if (n) {
size_t i = 0;
const char *ptr = string;
for (i = 0; i < n; i++) {
if (*ptr) {
str_len++;
} else {
break;
}
}
} else {
str_len = strlen(string);
}
if (!cfu_str->str) {
cfu_str->str = malloc(str_len + 1);
cfu_str->max_size = str_len + 1;
cfu_str->used_size = 1;
cfu_str->str[0] = '\000';
}
if (cfu_str->used_size + str_len + 1 > cfu_str->max_size) {
/* allocate more memory and copy over */
char *tmp = NULL;
if (cfu_str->max_size * 2 >= cfu_str->used_size + str_len + 1) {
cfu_str->max_size *= 2;
} else {
cfu_str->max_size = cfu_str->used_size + str_len + 1;
}
tmp = malloc(cfu_str->max_size);
memcpy(tmp, cfu_str->str, cfu_str->used_size);
free(cfu_str->str);
cfu_str->str = tmp;
}
memcpy(&cfu_str->str[cfu_str->used_size - 1], string, str_len);
/* strcat(cfu_str->str, string); */
cfu_str->used_size += str_len;
cfu_str->str[cfu_str->used_size - 1] = '\000';
return 1;
}
int
cfustring_append(cfustring_t *cfu_str, const char *string) {
return cfustring_append_n(cfu_str, string, 0);
}
char *
cfustring_get_buffer(cfustring_t *cfu_str) {
return cfu_str->str;
}
char *
cfustring_get_buffer_copy(cfustring_t *cfu_str) {
char *buffer = NULL;
if (!cfu_str->str) return NULL;
buffer = calloc(cfu_str->used_size, 1);
memcpy(buffer, cfu_str->str, cfu_str->used_size);
return buffer;
}
static char *
_dup_str(const char *str) {
size_t len = strlen(str) + 1;
char *ns = calloc(len, 1);
memcpy(ns, str, len);
return ns;
}
static char *
_dup_str_n(const char *str, size_t n) {
size_t len = n;
char *ns;
if (n == 0) return NULL;
ns = calloc(len + 1, 1);
memcpy(ns, str, len);
ns[len] = '\000';
return ns;
}
char *
cfustring_dup_c_str(const char *str) {
return _dup_str(str);
}
char *
cfustring_dup_c_str_n(const char *str, size_t n) {
return _dup_str_n(str, n);
}
static char *
_check_sep(char **start, char **seps, size_t num_seps, char **sep_chk_ptrs, int last) {
size_t i;
char *end = *start;
for (i = 0; i < num_seps; i++) sep_chk_ptrs[i] = seps[i];
if (!*start || !*(*start)) return NULL;
if (last) {
for(; *end; end++);
if (end != *start) {
char *rv = _dup_str_n(*start, end - *start);
*start = end;
return rv;
}
return NULL;
}
for (end = *start; *end; end++) {
for (i = 0; i < num_seps; i++) {
if (*sep_chk_ptrs[i] == *end) {
sep_chk_ptrs[i]++;
if (!*sep_chk_ptrs[i]) {
/* got a match */
size_t size = end - *start + 1 - (sep_chk_ptrs[i] - seps[i]);
char *rv = _dup_str_n(*start, size);
*start = end + 1;
return rv;
}
} else {
sep_chk_ptrs[i] = seps[i];
}
}
}
if (end != *start) {
char *rv = _dup_str_n(*start, end - *start);
*start = end;
return rv;
}
return NULL;
}
static char **
__cfustring_split_to_raw(cfustring_t *cfu_str, size_t *num_strings, size_t num_seps, size_t limit,
va_list ap) {
char *sep = NULL;
size_t i = 0;
char **sep_array = calloc(num_seps, sizeof(char *));
char **sep_chk_ptrs = calloc(num_seps, sizeof(char *));
char **ret_strings = calloc(2, sizeof(char *));
unsigned int max_ret_strings = 2;
unsigned int used_ret_strings = 0;
char *end = NULL;
char *next_str = NULL;
int last = 0;
*num_strings = 0;
if (limit == 1) {
ret_strings[0] = cfustring_get_buffer_copy(cfu_str);
*num_strings = 1;
return ret_strings;
}
for (i = 0; i < num_seps; i++) {
sep = va_arg(ap, char *);
sep_array[i] = _dup_str(sep);
}
end = cfustring_get_buffer(cfu_str);
if (!end) {
*num_strings = 0;
free(ret_strings);
for (i = 0; i < num_seps; i++) {
free(sep_array[i]);
}
free(sep_array);
return NULL;
}
while ( (next_str = _check_sep(&end, sep_array, num_seps, sep_chk_ptrs, last)) ) {
if (used_ret_strings == max_ret_strings) {
/* allocate more space */
size_t new_size = max_ret_strings << 1;
char **tmp = calloc(new_size, sizeof(char *));
for (i = 0; i < used_ret_strings; i++) tmp[i] = ret_strings[i];
free(ret_strings);
ret_strings = tmp;
max_ret_strings = new_size;
}
ret_strings[used_ret_strings] = next_str;
used_ret_strings++;
if (limit > 0 && used_ret_strings == limit - 1) {
last = 1;
}
}
for (i = 0; i < num_seps; i++) {
free(sep_array[i]);
}
free(sep_array);
free(sep_chk_ptrs);
*num_strings = used_ret_strings;
return ret_strings;
}
cfustring_t **
cfustring_split(cfustring_t *cfu_str, size_t *num_strings, size_t limit, ...) {
va_list ap;
char **strings = NULL;
char *sep = NULL;
cfustring_t **rv = NULL;
size_t i = 0;
size_t num_seps = 0;
va_start(ap, limit);
sep = va_arg(ap, char *);
while (sep) {
num_seps++;
sep = va_arg(ap, char *);
}
va_end(ap);
va_start(ap, limit);
strings = __cfustring_split_to_raw(cfu_str, num_strings, num_seps, limit, ap);
va_end(ap);
if (!*num_strings) return NULL;
rv = malloc(*num_strings * sizeof(cfustring_t *));
for (i = 0; i < *num_strings; i++) {
rv[i] = cfustring_new_from_string(strings[i]);
free(strings[i]);
}
free(strings);
return rv;
}
char **
cfustring_split_to_c_str(cfustring_t *cfu_str, size_t *num_strings, size_t limit, ...) {
char **rv = NULL;
va_list ap;
size_t num_seps = 0;
char *sep = NULL;
va_start(ap, limit);
sep = va_arg(ap, char *);
while (sep) {
num_seps++;
sep = va_arg(ap, char *);
}
va_end(ap);
va_start(ap, limit);
rv = __cfustring_split_to_raw(cfu_str, num_strings, num_seps, limit, ap);
va_end(ap);
return rv;
}
char *
cfustring_sprintf_c_str(const char *fmt, ...) {
va_list ap;
char *str = NULL;
cfustring_t *cfu_str = cfustring_new();
va_start(ap, fmt);
cfustring_vsprintf(cfu_str, fmt, ap);
va_end(ap);
str = cfustring_get_buffer_copy(cfu_str);
cfustring_destroy(cfu_str);
return str;
}
size_t
cfustring_sprintf(cfustring_t *cfu_str, const char *fmt, ...) {
va_list ap;
size_t rv = 0;
va_start(ap, fmt);
rv = cfustring_vsprintf(cfu_str, fmt, ap);
va_end(ap);
return rv;
}
static int
_safe_sprintf(char **buf, size_t *buf_size, const char *fmt, ...) {
va_list ap;
int rv = 0;
int done = 0;
if (!(*buf) || *buf_size == 0) {
*buf_size = 128;
if (*buf) free(*buf);
*buf = malloc(*buf_size);
}
while (!done) {
va_start(ap, fmt);
rv = vsnprintf(*buf, *buf_size, fmt, ap);
va_end(ap);
if (rv >= (int)(*buf_size) - 1) {
*buf_size *= 2;
free(*buf);
*buf = malloc(*buf_size);
}
else {
done = 1;
}
}
return rv;
}
static char *
_safe_strncpy(char **buf, size_t *buf_size, const char *src, size_t size) {
char *rv = NULL;
if (!(*buf) || *buf_size == 0) {
*buf_size = size + 1;
if (*buf) free(*buf);
*buf = malloc(*buf_size);
(*buf)[0] = '\000';
}
if (size > *buf_size - 1) {
*buf_size = size + 1;
if (*buf) free(*buf);
*buf = malloc(*buf_size);
(*buf)[0] = '\000';
}
rv = strncpy(*buf, src, size);
return rv;
}
size_t
cfustring_vsprintf(cfustring_t *cfu_str, const char *fmt_in, va_list ap) {
const char *ptr = NULL;
const char *start = NULL;
const char *end = NULL;
size_t byte_count = 0;
size_t buf_size = 128;
size_t buf2_size = 128;
char *buf = NULL;
char *buf2 = NULL;
const char *fmt = NULL;
buf = malloc(buf_size);
buf2 = malloc(buf2_size);
cfustring_clear(cfu_str);
ptr = start = end = fmt = fmt_in;
while (*fmt) {
if (*fmt == '%') {
ptr = fmt;
fmt++;
if (*fmt == '%') {
end++;
cfustring_append_n(cfu_str, start, ptr - start + 1);
byte_count += ptr - start + 1;
fmt++;
start = end = fmt;
continue;
}
while (*fmt && *fmt != 'u' && *fmt != 's' && *fmt != 'd' && *fmt != 'f' && *fmt != 'F'
&& *fmt != 'x' && *fmt != 'X' && *fmt != 'p'
&& *fmt != ' ' && *fmt != '\t') fmt++;
if (*fmt == 'u' || *fmt == 'd' || *fmt == 'f' || *fmt == 'F'
|| *fmt == 'x' || *fmt == 'X' || *fmt == 'p') {
unsigned num = 0;
double f = 0;
int d = 0;
void *p = NULL;
size_t size = fmt - ptr + 1;
_safe_strncpy(&buf2, &buf2_size, ptr, size);
buf2[size] = '\000';
switch (*fmt) {
case 'u':
case 'x':
case 'X':
case 'o':
num = va_arg(ap, unsigned);
_safe_sprintf(&buf, &buf_size, buf2, num);
break;
case 'd':
case 'c':
d = va_arg(ap, int);
_safe_sprintf(&buf, &buf_size, buf2, d);
break;
case 'f':
case 'F':
case 'e':
case 'E':
case 'g':
case 'G':
case 'a':
case 'A':
f = va_arg(ap, double);
_safe_sprintf(&buf, &buf_size, buf2, f);
break;
case 'p':
p = va_arg(ap, void *);
_safe_sprintf(&buf, &buf_size, buf2, p);
break;
default:
break;
}
cfustring_append_n(cfu_str, start, end - start + 1);
byte_count += end - start + 1;
cfustring_append(cfu_str, buf);
byte_count += strlen(buf);
fmt++;
start = end = fmt;
continue;
} else if (*fmt == 's') {
char *s = NULL;
size_t size = fmt - ptr + 1;
s = va_arg(ap, char *);
buf[0] = '\000';
_safe_strncpy(&buf2, &buf2_size, ptr, size);
buf2[size] = '\000';
_safe_sprintf(&buf, &buf_size, buf2, s);
if (ptr != start) {
cfustring_append_n(cfu_str, start, end - start + 1);
byte_count += end - start + 1;
}
if (s) {
cfustring_append(cfu_str, buf);
byte_count += strlen(buf);
} else {
s = "(null)";
cfustring_append(cfu_str, s);
byte_count += strlen(s);
}
fmt++;
start = end = fmt;
continue;
} else if (*fmt == ' ' || *fmt == '\t') {
cfustring_append_n(cfu_str, start, end - start + 1);
byte_count += end - start + 1;
fmt++;
start = end = fmt;
continue;
}
} else {
byte_count++;
end = fmt;
}
fmt++;
}
if (end != start) {
cfustring_append_n(cfu_str, start, end - start + 1);
byte_count += end - start + 1;
}
free(buf);
free(buf2);
return byte_count;
}
int
cfustring_destroy(cfustring_t *cfu_str) {
free(cfu_str->str);
free(cfu_str);
return 1;
}
char **
cfustring_c_str_split(const char *c_str, size_t *num_strings, size_t limit, ...) {
cfustring_t *cfu_str = cfustring_new_from_string(c_str);
char **rv = NULL;
va_list ap;
size_t num_seps = 0;
char *sep = NULL;
va_start(ap, limit);
sep = va_arg(ap, char *);
while (sep) {
num_seps++;
sep = va_arg(ap, char *);
}
va_end(ap);
va_start(ap, limit);
rv = __cfustring_split_to_raw(cfu_str, num_strings, num_seps, limit, ap);
va_end(ap);
cfustring_destroy(cfu_str);
return rv;
}