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