/* * 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 #include #include #include #include #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; }