str.c revision bcb4e51a409d94ae670de96afb8483a4f7855294
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "lib.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "buffer.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "printf-format-fix.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include "str.h"
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#include <stdio.h>
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstring_t *str_new(pool_t pool, size_t initial_size)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* never allocate a 0 byte size buffer. this is especially important
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen when str_c() is called on an empty string from a different stack
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen frame (see the comment in buffer.c about this). */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return buffer_create_dynamic(pool, I_MAX(initial_size, 1));
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
0db42260be85e797aa9909a29b20296996f52e75Timo Sirainen
0db42260be85e797aa9909a29b20296996f52e75Timo Sirainenstring_t *str_new_const(pool_t pool, const char *str, size_t len)
0db42260be85e797aa9909a29b20296996f52e75Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen string_t *ret;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(str[len] == '\0');
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ret = p_new(pool, buffer_t, 1);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen buffer_create_from_const_data(ret, str, len + 1);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen str_truncate(ret, len);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return ret;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstring_t *t_str_new(size_t initial_size)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return str_new(pool_datastack_create(), initial_size);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstring_t *t_str_new_const(const char *str, size_t len)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return str_new_const(pool_datastack_create(), str, len);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid str_free(string_t **str)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen buffer_free(str);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenstatic void str_add_nul(string_t *str)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen const unsigned char *data = str_data(str);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen size_t len = str_len(str);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen size_t alloc = buffer_get_size(str);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (len == alloc || data[len] != '\0') {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen buffer_write(str, len, "", 1);
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen /* remove the \0 - we don't want to keep it */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen buffer_set_used_size(str, len);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenchar *str_free_without_data(string_t **str)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen str_add_nul(*str);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return buffer_free_without_data(str);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
0ca3b9cb0f2a322a25ce7f229dc3d3a0b46be17bTimo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenconst char *str_c(string_t *str)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen str_add_nul(str);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return str->data;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenchar *str_c_modifiable(string_t *str)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen str_add_nul(str);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return buffer_get_modifiable_data(str, NULL);
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainenbool str_equals(const string_t *str1, const string_t *str2)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (str1->used != str2->used)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return FALSE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen return memcmp(str1->data, str2->data, str1->used) == 0;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
16cb5d65265dd0b216542803fd80c4b999ae118eTimo Sirainenvoid str_append_n(string_t *str, const void *cstr, size_t max_len)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen const char *p;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen size_t len;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen p = memchr(cstr, '\0', max_len);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (p == NULL)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen len = max_len;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen else
56aa97d74071f3a2987140c2ff1cfd5a59cb35aaTimo Sirainen len = p - (const char *)cstr;
56aa97d74071f3a2987140c2ff1cfd5a59cb35aaTimo Sirainen buffer_append(str, cstr, len);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid str_printfa(string_t *str, const char *fmt, ...)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen va_list args;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen va_start(args, fmt);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen str_vprintfa(str, fmt, args);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen va_end(args);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainenvoid str_vprintfa(string_t *str, const char *fmt, va_list args)
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen{
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen#define SNPRINTF_INITIAL_EXTRA_SIZE 128
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen va_list args2;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen char *tmp;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen size_t init_size;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen size_t pos = str->used;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen int ret, ret2;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen VA_COPY(args2, args);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* the format string is modified only if %m exists in it. it happens
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen only in error conditions, so don't try to t_push() here since it'll
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen just slow down the normal code path. */
8c909e451d14075c05d90382cf8eebc4e354f569Timo Sirainen fmt = printf_format_fix_get_len(fmt, &init_size);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen init_size += SNPRINTF_INITIAL_EXTRA_SIZE;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* @UNSAFE */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if (pos+init_size > buffer_get_writable_size(str) &&
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen pos < buffer_get_writable_size(str)) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* avoid growing buffer larger if possible. this is also
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen required if buffer isn't dynamically growing. */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen init_size = buffer_get_writable_size(str)-pos;
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen tmp = buffer_get_space_unsafe(str, pos, init_size);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ret = vsnprintf(tmp, init_size, fmt, args);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(ret >= 0);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen if ((unsigned int)ret >= init_size) {
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* didn't fit with the first guess. now we know the size,
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen so try again. */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen tmp = buffer_get_space_unsafe(str, pos, ret + 1);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen ret2 = vsnprintf(tmp, ret + 1, fmt, args2);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen i_assert(ret2 == ret);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen }
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen va_end(args2);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen /* drop the unused data, including terminating NUL */
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen buffer_set_used_size(str, pos + ret);
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen}
01435c38e7d671d5a892c4b802cfb204881cd454Timo Sirainen