str.c revision 88bcf81213b292e58ddaa41643e4146299cc750f
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "lib.h"
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen#include "buffer.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "printf-format-fix.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "str.h"
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include <stdio.h>
9511a40d933181045343110c8101b75887062aaeTimo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstring_t *str_new(pool_t pool, size_t initial_size)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen{
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen /* never allocate a 0 byte size buffer. this is especially important
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen when str_c() is called on an empty string from a different stack
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen frame (see the comment in buffer.c about this). */
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen return buffer_create_dynamic(pool, I_MAX(initial_size, 1));
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen}
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainenstring_t *str_new_const(pool_t pool, const char *str, size_t len)
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen{
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen string_t *ret;
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen i_assert(str[len] == '\0');
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen ret = p_new(pool, buffer_t, 1);
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen buffer_create_from_const_data(ret, str, len + 1);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen str_truncate(ret, len);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return ret;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen}
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenstring_t *t_str_new(size_t initial_size)
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen{
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return str_new(pool_datastack_create(), initial_size);
3f8303bae0f70df6db9337ad1d1476d290f9b1a3Timo Sirainen}
3f8303bae0f70df6db9337ad1d1476d290f9b1a3Timo Sirainen
3f8303bae0f70df6db9337ad1d1476d290f9b1a3Timo Sirainenstring_t *t_str_new_const(const char *str, size_t len)
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen{
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen return str_new_const(pool_datastack_create(), str, len);
859cc94211b759825db5e15b0c88754da902ca14Timo Sirainen}
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
859cc94211b759825db5e15b0c88754da902ca14Timo Sirainenvoid str_free(string_t **str)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen{
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen buffer_free(str);
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen}
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainenstatic void str_add_nul(string_t *str)
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen{
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen const unsigned char *data = str_data(str);
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen size_t len = str_len(str);
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen size_t alloc = buffer_get_size(str);
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen if (len == alloc || data[len] != '\0') {
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen buffer_write(str, len, "", 1);
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen /* remove the \0 - we don't want to keep it */
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen buffer_set_used_size(str, len);
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen }
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen}
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainenchar *str_free_without_data(string_t **str)
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen{
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen str_add_nul(*str);
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen return buffer_free_without_data(str);
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen}
1117aa7adc2909c750031fd7551a58a486d100d8Timo Sirainen
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainenconst char *str_c(string_t *str)
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen{
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen str_add_nul(str);
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen return str->data;
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen}
8887a9bb6d2e3f664cf741b763643a0e5610fa4dTimo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenchar *str_c_modifiable(string_t *str)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen{
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen str_add_nul(str);
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen return buffer_get_modifiable_data(str, NULL);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen}
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainenbool str_equals(const string_t *str1, const string_t *str2)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen{
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (str1->used != str2->used)
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen return FALSE;
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen return memcmp(str1->data, str2->data, str1->used) == 0;
2c0f1cb7a0564d48ec43c7315ea46ea38d2abd19Timo Sirainen}
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainenvoid str_append_n(string_t *str, const void *cstr, size_t max_len)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen{
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen const char *p;
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen size_t len;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen p = memchr(cstr, '\0', max_len);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if (p == NULL)
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen len = max_len;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen else
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen len = p - (const char *)cstr;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen buffer_append(str, cstr, len);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen}
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainenvoid str_printfa(string_t *str, const char *fmt, ...)
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen{
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen va_list args;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen va_start(args, fmt);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen str_vprintfa(str, fmt, args);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen va_end(args);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen}
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainenvoid str_vprintfa(string_t *str, const char *fmt, va_list args)
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen{
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen#define SNPRINTF_INITIAL_EXTRA_SIZE 128
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen va_list args2;
10c96a244935de4add8213ba0b894178dfb889a5Timo Sirainen char *tmp;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen unsigned int init_size;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen size_t pos = str->used;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen int ret, ret2;
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen VA_COPY(args2, args);
597dba3488c648ffb375ee4a552bd52ac4346979Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen /* the format string is modified only if %m exists in it. it happens
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainen only in error conditions, so don't try to t_push() here since it'll
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen just slow down the normal code path. */
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen fmt = printf_format_fix_get_len(fmt, &init_size);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen init_size += SNPRINTF_INITIAL_EXTRA_SIZE;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen /* @UNSAFE */
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen if (pos+init_size > buffer_get_writable_size(str) &&
411d6baa37f31d90730e90c4a28c43e1974bbe58Timo Sirainen pos < buffer_get_writable_size(str)) {
9511a40d933181045343110c8101b75887062aaeTimo Sirainen /* avoid growing buffer larger if possible. this is also
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen required if buffer isn't dynamically growing. */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen init_size = buffer_get_writable_size(str)-pos;
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen }
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen tmp = buffer_get_space_unsafe(str, pos, init_size);
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen ret = vsnprintf(tmp, init_size, fmt, args);
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen i_assert(ret >= 0);
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen if ((unsigned int)ret >= init_size) {
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen /* didn't fit with the first guess. now we know the size,
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen so try again. */
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen tmp = buffer_get_space_unsafe(str, pos, ret + 1);
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen ret2 = vsnprintf(tmp, ret + 1, fmt, args2);
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen i_assert(ret2 == ret);
bdcb00145ad87765e3fd22d4ebc4d2c029a326b9Timo Sirainen }
va_end(args2);
/* drop the unused data, including terminating NUL */
buffer_set_used_size(str, pos + ret);
}