1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "buffer.h"
5 #include "printf-format-fix.h"
6 #include "unichar.h"
7 #include "str.h"
8
9 #include <stdio.h>
10
str_new(pool_t pool,size_t initial_size)11 string_t *str_new(pool_t pool, size_t initial_size)
12 {
13 /* never allocate a 0 byte size buffer. this is especially important
14 when str_c() is called on an empty string from a different stack
15 frame (see the comment in buffer.c about this). */
16 return buffer_create_dynamic(pool, I_MAX(initial_size, 1));
17 }
18
str_new_const(pool_t pool,const char * str,size_t len)19 string_t *str_new_const(pool_t pool, const char *str, size_t len)
20 {
21 string_t *ret;
22
23 i_assert(str[len] == '\0');
24
25 ret = p_new(pool, buffer_t, 1);
26 buffer_create_from_const_data(ret, str, len + 1);
27 str_truncate(ret, len);
28 return ret;
29 }
30
t_str_new(size_t initial_size)31 string_t *t_str_new(size_t initial_size)
32 {
33 return str_new(pool_datastack_create(), initial_size);
34 }
35
t_str_new_const(const char * str,size_t len)36 string_t *t_str_new_const(const char *str, size_t len)
37 {
38 return str_new_const(pool_datastack_create(), str, len);
39 }
40
str_free(string_t ** str)41 void str_free(string_t **str)
42 {
43 if (str == NULL || *str == NULL)
44 return;
45
46 buffer_free(str);
47 }
48
str_add_nul(string_t * str)49 static void str_add_nul(string_t *str)
50 {
51 const unsigned char *data = str_data(str);
52 size_t len = str_len(str);
53 size_t alloc = buffer_get_size(str);
54
55 if (len == alloc || data[len] != '\0') {
56 buffer_write(str, len, "", 1);
57 /* remove the \0 - we don't want to keep it */
58 buffer_set_used_size(str, len);
59 }
60 }
61
str_free_without_data(string_t ** str)62 char *str_free_without_data(string_t **str)
63 {
64 str_add_nul(*str);
65 return buffer_free_without_data(str);
66 }
67
str_c(string_t * str)68 const char *str_c(string_t *str)
69 {
70 str_add_nul(str);
71 return str->data;
72 }
73
str_c_modifiable(string_t * str)74 char *str_c_modifiable(string_t *str)
75 {
76 str_add_nul(str);
77 return buffer_get_modifiable_data(str, NULL);
78 }
79
str_equals(const string_t * str1,const string_t * str2)80 bool str_equals(const string_t *str1, const string_t *str2)
81 {
82 if (str1->used != str2->used)
83 return FALSE;
84
85 return memcmp(str1->data, str2->data, str1->used) == 0;
86 }
87
str_append_max(string_t * str,const char * cstr,size_t max_len)88 void str_append_max(string_t *str, const char *cstr, size_t max_len)
89 {
90 const char *p;
91 size_t len;
92
93 p = memchr(cstr, '\0', max_len);
94 if (p == NULL)
95 len = max_len;
96 else
97 len = p - (const char *)cstr;
98 buffer_append(str, cstr, len);
99 }
100
str_printfa(string_t * str,const char * fmt,...)101 void str_printfa(string_t *str, const char *fmt, ...)
102 {
103 va_list args;
104
105 va_start(args, fmt);
106 str_vprintfa(str, fmt, args);
107 va_end(args);
108 }
109
str_vprintfa(string_t * str,const char * fmt,va_list args)110 void str_vprintfa(string_t *str, const char *fmt, va_list args)
111 {
112 #define SNPRINTF_INITIAL_EXTRA_SIZE 128
113 va_list args2;
114 char *tmp;
115 size_t init_size;
116 size_t pos = str->used;
117 int ret, ret2;
118
119 VA_COPY(args2, args);
120
121 /* the format string is modified only if %m exists in it. it happens
122 only in error conditions, so don't try to t_push() here since it'll
123 just slow down the normal code path. */
124 fmt = printf_format_fix_get_len(fmt, &init_size);
125 init_size += SNPRINTF_INITIAL_EXTRA_SIZE;
126
127 /* @UNSAFE */
128 if (pos+init_size > buffer_get_writable_size(str) &&
129 pos < buffer_get_writable_size(str)) {
130 /* avoid growing buffer larger if possible. this is also
131 required if buffer isn't dynamically growing. */
132 init_size = buffer_get_writable_size(str)-pos;
133 }
134 tmp = buffer_get_space_unsafe(str, pos, init_size);
135 ret = vsnprintf(tmp, init_size, fmt, args);
136 i_assert(ret >= 0);
137
138 if ((unsigned int)ret >= init_size) {
139 /* didn't fit with the first guess. now we know the size,
140 so try again. */
141 tmp = buffer_get_space_unsafe(str, pos, ret + 1);
142 ret2 = vsnprintf(tmp, ret + 1, fmt, args2);
143 i_assert(ret2 == ret);
144 }
145 va_end(args2);
146
147 /* drop the unused data, including terminating NUL */
148 buffer_set_used_size(str, pos + ret);
149 }
150
str_truncate_utf8(string_t * str,size_t len)151 void str_truncate_utf8(string_t *str, size_t len)
152 {
153 size_t size = str_len(str);
154
155 if (size <= len)
156 return;
157 str_truncate(str, uni_utf8_data_truncate(str_data(str), size, len));
158 }
159