str.c revision 513928e1d3eb3943c535732d693eadc387a4c2c3
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2002-2014 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "lib.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "buffer.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "printf-format-fix.h"
3c9562f749617f87f7402bd3aab2fc82ad7c3b61Timo Sirainen#include "str.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include <stdio.h>
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenstring_t *str_new(pool_t pool, size_t initial_size)
9740d55b228a670047c854484d5cc979a056a9afTimo Sirainen{
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* never allocate a 0 byte size buffer. this is especially important
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen when str_c() is called on an empty string from a different stack
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen frame (see the comment in buffer.c about this). */
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen return buffer_create_dynamic(pool, I_MAX(initial_size, 1));
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen}
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainen
0ea17cea21df405e6e74a167d08e1ff0ee95dd13Timo Sirainenstring_t *str_new_const(pool_t pool, const char *str, size_t len)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen{
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen string_t *ret;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
7fc0f80480063a9d4cb9e8c07b50db2a5627799eTimo Sirainen i_assert(str[len] == '\0');
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen ret = p_new(pool, buffer_t, 1);
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen buffer_create_from_const_data(ret, str, len + 1);
9740d55b228a670047c854484d5cc979a056a9afTimo Sirainen str_truncate(ret, len);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return ret;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen}
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenstring_t *t_str_new(size_t initial_size)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen{
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return str_new(pool_datastack_create(), initial_size);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen}
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenstring_t *t_str_new_const(const char *str, size_t len)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen{
return str_new_const(pool_datastack_create(), str, len);
}
void str_free(string_t **str)
{
buffer_free(str);
}
static void str_add_nul(string_t *str)
{
const unsigned char *data = str_data(str);
size_t len = str_len(str);
size_t alloc = buffer_get_size(str);
if (len == alloc || data[len] != '\0') {
buffer_write(str, len, "", 1);
/* remove the \0 - we don't want to keep it */
buffer_set_used_size(str, len);
}
}
char *str_free_without_data(string_t **str)
{
str_add_nul(*str);
return buffer_free_without_data(str);
}
const char *str_c(string_t *str)
{
str_add_nul(str);
return buffer_get_data(str, NULL);
}
const unsigned char *str_data(const string_t *str)
{
return buffer_get_data(str, NULL);
}
char *str_c_modifiable(string_t *str)
{
str_add_nul(str);
return buffer_get_modifiable_data(str, NULL);
}
size_t str_len(const string_t *str)
{
return buffer_get_used_size(str);
}
bool str_equals(const string_t *str1, const string_t *str2)
{
if (str1->used != str2->used)
return FALSE;
return memcmp(str1->data, str2->data, str1->used) == 0;
}
void str_append(string_t *str, const char *cstr)
{
buffer_append(str, cstr, strlen(cstr));
}
void str_append_n(string_t *str, const void *cstr, size_t max_len)
{
size_t len;
len = 0;
while (len < max_len && ((const char *)cstr)[len] != '\0')
len++;
buffer_append(str, cstr, len);
}
void str_append_data(string_t *str, const void *data, size_t len)
{
buffer_append(str, data, len);
}
void str_append_c(string_t *str, unsigned char chr)
{
buffer_append_c(str, chr);
}
void str_append_str(string_t *dest, const string_t *src)
{
const char *cstr;
size_t len;
cstr = buffer_get_data(src, &len);
buffer_append(dest, cstr, len);
}
void str_printfa(string_t *str, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
str_vprintfa(str, fmt, args);
va_end(args);
}
void str_vprintfa(string_t *str, const char *fmt, va_list args)
{
#define SNPRINTF_INITIAL_EXTRA_SIZE 128
va_list args2;
char *tmp;
unsigned int init_size;
size_t pos = str->used;
int ret, ret2;
VA_COPY(args2, args);
/* the format string is modified only if %m exists in it. it happens
only in error conditions, so don't try to t_push() here since it'll
just slow down the normal code path. */
fmt = printf_format_fix_get_len(fmt, &init_size);
init_size += SNPRINTF_INITIAL_EXTRA_SIZE;
/* @UNSAFE */
if (pos+init_size > buffer_get_size(str) &&
pos < buffer_get_size(str)) {
/* avoid growing buffer larger if possible. this is also
required if buffer isn't dynamically growing. */
init_size = buffer_get_size(str)-pos;
}
tmp = buffer_get_space_unsafe(str, pos, init_size);
ret = vsnprintf(tmp, init_size, fmt, args);
i_assert(ret >= 0);
if ((unsigned int)ret >= init_size) {
/* didn't fit with the first guess. now we know the size,
so try again. */
tmp = buffer_get_space_unsafe(str, pos, ret + 1);
ret2 = vsnprintf(tmp, ret + 1, fmt, args2);
i_assert(ret2 == ret);
}
/* drop the unused data, including terminating NUL */
buffer_set_used_size(str, pos + ret);
}
void str_insert(string_t *str, size_t pos, const char *cstr)
{
buffer_insert(str, pos, cstr, strlen(cstr));
}
void str_delete(string_t *str, size_t pos, size_t len)
{
buffer_delete(str, pos, len);
}
void str_truncate(string_t *str, size_t len)
{
buffer_set_used_size(str, len);
}