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