1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2 
3 /* @UNSAFE: whole file */
4 
5 #include "lib.h"
6 #include "str.h"
7 #include "printf-format-fix.h"
8 #include "strfuncs.h"
9 #include "array.h"
10 
11 #include <stdio.h>
12 #include <limits.h>
13 #include <ctype.h>
14 
15 #define STRCONCAT_BUFSIZE 512
16 
17 enum _str_trim_sides {
18 	STR_TRIM_LEFT = BIT(0),
19 	STR_TRIM_RIGHT = BIT(1),
20 };
21 
22 const unsigned char uchar_nul = '\0';
23 const unsigned char *uchar_empty_ptr = &uchar_nul;
24 
25 volatile int timing_safety_unoptimization;
26 
i_snprintf(char * dest,size_t max_chars,const char * format,...)27 int i_snprintf(char *dest, size_t max_chars, const char *format, ...)
28 {
29 	va_list args;
30 	int ret;
31 
32 	i_assert(max_chars < INT_MAX);
33 
34 	va_start(args, format);
35 	ret = vsnprintf(dest, max_chars, printf_format_fix_unsafe(format),
36 			args);
37 	va_end(args);
38 
39 	i_assert(ret >= 0);
40 	return (unsigned int)ret < max_chars ? 0 : -1;
41 }
42 
p_strdup(pool_t pool,const char * str)43 char *p_strdup(pool_t pool, const char *str)
44 {
45 	void *mem;
46 	size_t len;
47 
48 	if (str == NULL)
49                 return NULL;
50 
51 	len = strlen(str) + 1;
52 	mem = p_malloc(pool, len);
53 	memcpy(mem, str, len);
54 	return mem;
55 }
56 
p_memdup(pool_t pool,const void * data,size_t size)57 void *p_memdup(pool_t pool, const void *data, size_t size)
58 {
59 	void *mem;
60 
61 	mem = p_malloc(pool, size);
62 	memcpy(mem, data, size);
63 	return mem;
64 }
65 
p_strdup_empty(pool_t pool,const char * str)66 char *p_strdup_empty(pool_t pool, const char *str)
67 {
68 	if (str == NULL || *str == '\0')
69                 return NULL;
70 
71 	return p_strdup(pool, str);
72 }
73 
p_strdup_until(pool_t pool,const void * start,const void * end)74 char *p_strdup_until(pool_t pool, const void *start, const void *end)
75 {
76 	size_t size;
77 	char *mem;
78 
79 	i_assert((const char *) start <= (const char *) end);
80 
81 	size = (size_t) ((const char *) end - (const char *) start);
82 
83 	mem = p_malloc(pool, size + 1);
84 	memcpy(mem, start, size);
85 	return mem;
86 }
87 
p_strndup(pool_t pool,const void * str,size_t max_chars)88 char *p_strndup(pool_t pool, const void *str, size_t max_chars)
89 {
90 	const char *p;
91 	char *mem;
92 	size_t len;
93 
94 	i_assert(str != NULL);
95 	i_assert(max_chars != SIZE_MAX);
96 
97 	p = memchr(str, '\0', max_chars);
98 	if (p == NULL)
99 		len = max_chars;
100 	else
101 		len = p - (const char *)str;
102 
103 	mem = p_malloc(pool, len+1);
104 	memcpy(mem, str, len);
105 	return mem;
106 }
107 
p_strdup_printf(pool_t pool,const char * format,...)108 char *p_strdup_printf(pool_t pool, const char *format, ...)
109 {
110 	va_list args;
111         char *ret;
112 
113 	va_start(args, format);
114         ret = p_strdup_vprintf(pool, format, args);
115 	va_end(args);
116 
117 	return ret;
118 }
119 
t_noalloc_strdup_vprintf(const char * format,va_list args,unsigned int * size_r)120 char *t_noalloc_strdup_vprintf(const char *format, va_list args,
121 			       unsigned int *size_r)
122 {
123 #define SNPRINTF_INITIAL_EXTRA_SIZE 256
124 	va_list args2;
125 	char *tmp;
126 	size_t init_size;
127 	int ret;
128 #ifdef DEBUG
129 	int old_errno = errno;
130 #endif
131 
132 	VA_COPY(args2, args);
133 
134 	/* the format string is modified only if %m exists in it. it happens
135 	   only in error conditions, so don't try to t_push() here since it'll
136 	   just slow down the normal code path. */
137 	format = printf_format_fix_get_len(format, &init_size);
138 	init_size += SNPRINTF_INITIAL_EXTRA_SIZE;
139 
140 	tmp = t_buffer_get(init_size);
141 	ret = vsnprintf(tmp, init_size, format, args);
142 	i_assert(ret >= 0);
143 
144 	*size_r = ret + 1;
145 	if ((unsigned int)ret >= init_size) {
146 		/* didn't fit with the first guess. now we know the size,
147 		   so try again. */
148 		tmp = t_buffer_get(*size_r);
149 		ret = vsnprintf(tmp, *size_r, format, args2);
150 		i_assert((unsigned int)ret == *size_r-1);
151 	}
152 #ifdef DEBUG
153 	/* we rely on errno not changing. it shouldn't. */
154 	i_assert(errno == old_errno);
155 #endif
156 	va_end(args2);
157 	return tmp;
158 }
159 
p_strdup_vprintf(pool_t pool,const char * format,va_list args)160 char *p_strdup_vprintf(pool_t pool, const char *format, va_list args)
161 {
162 	char *tmp, *buf;
163 	unsigned int size;
164 
165 	tmp = t_noalloc_strdup_vprintf(format, args, &size);
166 	if (pool->datastack_pool) {
167 		t_buffer_alloc(size);
168 		return tmp;
169 	} else {
170 		buf = p_malloc(pool, size);
171 		memcpy(buf, tmp, size - 1);
172 		return buf;
173 	}
174 }
175 
vstrconcat(const char * str1,va_list args,size_t * ret_len)176 char *vstrconcat(const char *str1, va_list args, size_t *ret_len)
177 {
178 	const char *str;
179         char *temp;
180 	size_t bufsize, i, len;
181 
182 	i_assert(str1 != NULL);
183 
184 	str = str1;
185 	bufsize = STRCONCAT_BUFSIZE;
186 	temp = t_buffer_get(bufsize);
187 
188 	i = 0;
189 	do {
190 		len = strlen(str);
191 
192 		if (i + len >= bufsize) {
193 			/* need more memory */
194 			bufsize = nearest_power(i + len + 1);
195 			temp = t_buffer_reget(temp, bufsize);
196 		}
197 
198 		memcpy(temp + i, str, len); i += len;
199 
200 		/* next string */
201 		str = va_arg(args, const char *);
202 	} while (str != NULL);
203 
204 	i_assert(i < bufsize);
205 
206 	temp[i++] = '\0';
207         *ret_len = i;
208         return temp;
209 }
210 
p_strconcat(pool_t pool,const char * str1,...)211 char *p_strconcat(pool_t pool, const char *str1, ...)
212 {
213 	va_list args;
214 	char *temp, *ret;
215         size_t len;
216 
217 	i_assert(str1 != NULL);
218 
219 	va_start(args, str1);
220 
221 	if (pool->datastack_pool) {
222 		ret = vstrconcat(str1, args, &len);
223 		t_buffer_alloc(len);
224 	} else {
225 		temp = vstrconcat(str1, args, &len);
226 		ret = p_malloc(pool, len);
227 		memcpy(ret, temp, len);
228 	}
229 
230 	va_end(args);
231         return ret;
232 }
233 
t_memdup(const void * data,size_t size)234 static void *t_memdup(const void *data, size_t size)
235 {
236 	void *mem = t_malloc_no0(size);
237 	memcpy(mem, data, size);
238 	return mem;
239 }
240 
t_strdup(const char * str)241 const char *t_strdup(const char *str)
242 {
243 	return t_strdup_noconst(str);
244 }
245 
t_strdup_noconst(const char * str)246 char *t_strdup_noconst(const char *str)
247 {
248 	if (str == NULL)
249 		return NULL;
250 	return t_memdup(str, strlen(str) + 1);
251 }
252 
t_strdup_empty(const char * str)253 const char *t_strdup_empty(const char *str)
254 {
255 	if (str == NULL || *str == '\0')
256 		return NULL;
257 
258 	return t_strdup(str);
259 }
260 
t_strdup_until(const void * start,const void * end)261 const char *t_strdup_until(const void *start, const void *end)
262 {
263 	char *mem;
264 	size_t size;
265 
266 	i_assert((const char *) start <= (const char *) end);
267 
268 	size = (size_t)((const char *)end - (const char *)start);
269 
270 	mem = t_malloc_no0(size + 1);
271 	memcpy(mem, start, size);
272 	mem[size] = '\0';
273 	return mem;
274 }
275 
t_strndup(const void * str,size_t max_chars)276 const char *t_strndup(const void *str, size_t max_chars)
277 {
278 	i_assert(str != NULL);
279 	return p_strndup(unsafe_data_stack_pool, str, max_chars);
280 }
281 
t_strdup_printf(const char * format,...)282 const char *t_strdup_printf(const char *format, ...)
283 {
284 	va_list args;
285 	const char *ret;
286 
287 	va_start(args, format);
288 	ret = p_strdup_vprintf(unsafe_data_stack_pool, format, args);
289 	va_end(args);
290 
291 	return ret;
292 }
293 
t_strdup_vprintf(const char * format,va_list args)294 const char *t_strdup_vprintf(const char *format, va_list args)
295 {
296 	return p_strdup_vprintf(unsafe_data_stack_pool, format, args);
297 }
298 
t_strconcat(const char * str1,...)299 const char *t_strconcat(const char *str1, ...)
300 {
301 	va_list args;
302 	const char *ret;
303         size_t len;
304 
305 	i_assert(str1 != NULL);
306 
307 	va_start(args, str1);
308 
309 	ret = vstrconcat(str1, args, &len);
310 	t_buffer_alloc(len);
311 
312 	va_end(args);
313         return ret;
314 }
315 
t_strcut(const char * str,char cutchar)316 const char *t_strcut(const char *str, char cutchar)
317 {
318 	const char *p;
319 
320 	for (p = str; *p != '\0'; p++) {
321 		if (*p == cutchar)
322                         return t_strdup_until(str, p);
323 	}
324 
325         return str;
326 }
327 
t_str_replace(const char * str,char from,char to)328 const char *t_str_replace(const char *str, char from, char to)
329 {
330 	char *out;
331 	size_t i, len;
332 
333 	if (strchr(str, from) == NULL)
334 		return str;
335 
336 	len = strlen(str);
337 	out = t_malloc_no0(len + 1);
338 	for (i = 0; i < len; i++) {
339 		if (str[i] == from)
340 			out[i] = to;
341 		else
342 			out[i] = str[i];
343 	}
344 	out[i] = '\0';
345 	return out;
346 }
347 
t_str_oneline(const char * str)348 const char *t_str_oneline(const char *str)
349 {
350 	string_t *out;
351 	size_t len;
352 	const char *p, *pend, *poff;
353 	bool new_line;
354 
355 	if (strpbrk(str, "\r\n") == NULL)
356 		return str;
357 
358 	len = strlen(str);
359 	out = t_str_new(len + 1);
360 	new_line = TRUE;
361 	p = poff = str;
362 	pend = str + len;
363 	while (p < pend) {
364 		switch (*p) {
365 		case '\r':
366 			if (p > poff)
367 				str_append_data(out,  poff, p - poff);
368 			/* just drop \r */
369 			poff = p + 1;
370 			break;
371 		case '\n':
372 			if (p > poff)
373 				str_append_data(out,  poff, p - poff);
374 			if (new_line) {
375 				/* coalesce multiple \n into a single space */
376 			} else {
377 				/* first \n after text */
378 				str_append_c(out, ' ');
379 				new_line = TRUE;
380 			}
381 			poff = p + 1;
382 			break;
383 		default:
384 			new_line = FALSE;
385 			break;
386 		}
387 		p++;
388 	}
389 
390 	if (new_line && str_len(out) > 0)
391 		str_truncate(out, str_len(out) - 1);
392 	else if (p > poff)
393 		str_append_data(out,  poff, p - poff);
394 	return str_c(out);
395 }
396 
i_strocpy(char * dest,const char * src,size_t dstsize)397 int i_strocpy(char *dest, const char *src, size_t dstsize)
398 {
399 	if (dstsize == 0)
400 		return -1;
401 
402 	while (*src != '\0' && dstsize > 1) {
403 		*dest++ = *src++;
404 		dstsize--;
405 	}
406 
407 	*dest = '\0';
408 	return *src == '\0' ? 0 : -1;
409 }
410 
str_ucase(char * str)411 char *str_ucase(char *str)
412 {
413 	char *p;
414 
415 	for (p = str; *p != '\0'; p++)
416 		*p = i_toupper(*p);
417         return str;
418 }
419 
str_lcase(char * str)420 char *str_lcase(char *str)
421 {
422 	char *p;
423 
424 	for (p = str; *p != '\0'; p++)
425 		*p = i_tolower(*p);
426         return str;
427 }
428 
t_str_lcase(const char * str)429 const char *t_str_lcase(const char *str)
430 {
431 	i_assert(str != NULL);
432 
433 	return str_lcase(t_strdup_noconst(str));
434 }
435 
t_str_ucase(const char * str)436 const char *t_str_ucase(const char *str)
437 {
438 	i_assert(str != NULL);
439 
440 	return str_ucase(t_strdup_noconst(str));
441 }
442 
i_strstr_arr(const char * haystack,const char * const * needles)443 const char *i_strstr_arr(const char *haystack, const char *const *needles)
444 {
445 	const char *ptr;
446 	for(; *needles != NULL; needles++)
447 		if ((ptr = strstr(haystack, *needles)) != NULL)
448 			return ptr;
449 	return NULL;
450 }
451 
str_trim_parse(const char * str,const char * chars,enum _str_trim_sides sides,const char ** begin_r,const char ** end_r)452 static void str_trim_parse(const char *str,
453 	const char *chars, enum _str_trim_sides sides,
454 	const char **begin_r, const char **end_r)
455 {
456 	const char *p, *pend, *begin, *end;
457 
458 	*begin_r = *end_r = NULL;
459 
460 	pend = str + strlen(str);
461 	if (pend == str)
462 		return;
463 
464 	p = str;
465 	if ((sides & STR_TRIM_LEFT) != 0) {
466 		while (p < pend && strchr(chars, *p) != NULL)
467 			p++;
468 		if (p == pend)
469 			return;
470 	}
471 	begin = p;
472 
473 	p = pend;
474 	if ((sides & STR_TRIM_RIGHT) != 0) {
475 		while (p > begin && strchr(chars, *(p-1)) != NULL)
476 			p--;
477 		if (p == begin)
478 			return;
479 	}
480 	end = p;
481 
482 	*begin_r = begin;
483 	*end_r = end;
484 }
485 
t_str_trim(const char * str,const char * chars)486 const char *t_str_trim(const char *str, const char *chars)
487 {
488 	const char *begin, *end;
489 
490 	str_trim_parse(str, chars,
491 		STR_TRIM_LEFT | STR_TRIM_RIGHT, &begin, &end);
492 	if (begin == NULL)
493 		return "";
494 	return t_strdup_until(begin, end);
495 }
496 
p_str_trim(pool_t pool,const char * str,const char * chars)497 const char *p_str_trim(pool_t pool, const char *str, const char *chars)
498 {
499 	const char *begin, *end;
500 
501 	str_trim_parse(str, chars,
502 		STR_TRIM_LEFT | STR_TRIM_RIGHT, &begin, &end);
503 	if (begin == NULL)
504 		return "";
505 	return p_strdup_until(pool, begin, end);
506 }
507 
str_ltrim(const char * str,const char * chars)508 const char *str_ltrim(const char *str, const char *chars)
509 {
510 	const char *begin, *end;
511 
512 	str_trim_parse(str, chars, STR_TRIM_LEFT, &begin, &end);
513 	if (begin == NULL)
514 		return "";
515 	return begin;
516 }
517 
t_str_ltrim(const char * str,const char * chars)518 const char *t_str_ltrim(const char *str, const char *chars)
519 {
520 	return t_strdup(str_ltrim(str, chars));
521 }
522 
p_str_ltrim(pool_t pool,const char * str,const char * chars)523 const char *p_str_ltrim(pool_t pool, const char *str, const char *chars)
524 {
525 	return p_strdup(pool, str_ltrim(str, chars));
526 }
527 
t_str_rtrim(const char * str,const char * chars)528 const char *t_str_rtrim(const char *str, const char *chars)
529 {
530 	const char *begin, *end;
531 
532 	str_trim_parse(str, chars, STR_TRIM_RIGHT, &begin, &end);
533 	if (begin == NULL)
534 		return "";
535 	return t_strdup_until(begin, end);
536 }
537 
p_str_rtrim(pool_t pool,const char * str,const char * chars)538 const char *p_str_rtrim(pool_t pool, const char *str, const char *chars)
539 {
540 	const char *begin, *end;
541 
542 	str_trim_parse(str, chars, STR_TRIM_RIGHT, &begin, &end);
543 	if (begin == NULL)
544 		return "";
545 	return p_strdup_until(pool, begin, end);
546 }
547 
null_strcmp(const char * s1,const char * s2)548 int null_strcmp(const char *s1, const char *s2)
549 {
550 	if (s1 == NULL)
551 		return s2 == NULL ? 0 : -1;
552 	if (s2 == NULL)
553 		return 1;
554 
555 	return strcmp(s1, s2);
556 }
557 
null_strcasecmp(const char * s1,const char * s2)558 int null_strcasecmp(const char *s1, const char *s2)
559 {
560         if (s1 == NULL)
561                 return s2 == NULL ? 0 : -1;
562         if (s2 == NULL)
563                 return 1;
564 
565         return strcasecmp(s1, s2);
566 }
567 
i_memcasecmp(const void * p1,const void * p2,size_t size)568 int i_memcasecmp(const void *p1, const void *p2, size_t size)
569 {
570 	const unsigned char *s1 = p1;
571 	const unsigned char *s2 = p2;
572 	int ret;
573 
574 	while (size > 0) {
575 		ret = i_toupper(*s1) - i_toupper(*s2);
576 		if (ret != 0)
577 			return ret;
578 
579 		s1++; s2++; size--;
580 	}
581 
582         return 0;
583 }
584 
i_strcmp_p(const char * const * p1,const char * const * p2)585 int i_strcmp_p(const char *const *p1, const char *const *p2)
586 {
587 	return strcmp(*p1, *p2);
588 }
589 
i_strcasecmp_p(const char * const * p1,const char * const * p2)590 int i_strcasecmp_p(const char *const *p1, const char *const *p2)
591 {
592 	return strcasecmp(*p1, *p2);
593 }
594 
mem_equals_timing_safe(const void * p1,const void * p2,size_t size)595 bool mem_equals_timing_safe(const void *p1, const void *p2, size_t size)
596 {
597 	const unsigned char *s1 = p1, *s2 = p2;
598 	size_t i;
599 	int ret = 0;
600 
601 	for (i = 0; i < size; i++)
602 		ret |= s1[i] ^ s2[i];
603 
604 	/* make sure the compiler optimizer doesn't try to break out of the
605 	   above loop early. */
606 	timing_safety_unoptimization = ret;
607 	return ret == 0;
608 }
609 
str_equals_timing_almost_safe(const char * s1,const char * s2)610 bool str_equals_timing_almost_safe(const char *s1, const char *s2)
611 {
612 	size_t i;
613 	int ret = 0;
614 
615 	for (i = 0; s1[i] != '\0' && s2[i] != '\0'; i++)
616 		ret |= s1[i] ^ s2[i];
617 	ret |= s1[i] ^ s2[i];
618 
619 	/* make sure the compiler optimizer doesn't try to break out of the
620 	   above loop early. */
621 	timing_safety_unoptimization = ret;
622 	return ret == 0;
623 }
624 
625 size_t
str_match(const char * p1,const char * p2)626 str_match(const char *p1, const char *p2)
627 {
628 	size_t i = 0;
629 
630 	while(p1[i] != '\0' && p1[i] == p2[i])
631 		i++;
632 
633 	return i;
634 }
635 
i_memspn(const void * data,size_t data_len,const void * accept,size_t accept_len)636 size_t i_memspn(const void *data, size_t data_len,
637 		const void *accept, size_t accept_len)
638 {
639 	const unsigned char *start = data;
640 	i_assert(data != NULL || data_len == 0);
641 	i_assert(accept != NULL || accept_len == 0);
642 	size_t pos = 0;
643 	/* nothing to accept */
644 	if (accept_len == 0)
645 		return 0;
646 	for (; pos < data_len; pos++) {
647 		if (memchr(accept, start[pos], accept_len) == NULL)
648 			break;
649 	}
650 	return pos;
651 }
652 
i_memcspn(const void * data,size_t data_len,const void * reject,size_t reject_len)653 size_t i_memcspn(const void *data, size_t data_len,
654 		 const void *reject, size_t reject_len)
655 {
656 	const unsigned char *start = data;
657 	const unsigned char *r = reject;
658 	const unsigned char *ptr = CONST_PTR_OFFSET(data, data_len);
659 	i_assert(data != NULL || data_len == 0);
660 	i_assert(reject != NULL || reject_len == 0);
661 	/* nothing to reject */
662 	if (reject_len == 0 || data_len == 0)
663 		return data_len;
664 	/* Doing repeated memchr's over the data is faster than
665 	   going over it once byte by byte, as long as reject
666 	   is reasonably short. */
667 	for (size_t i = 0; i < reject_len; i++) {
668 		const unsigned char *kand =
669 			memchr(start, r[i], data_len);
670 		if (kand != NULL && kand < ptr)
671 			ptr = kand;
672 	}
673 	return ptr - start;
674 }
675 
676 static char **
split_str_slow(pool_t pool,const char * data,const char * separators,bool spaces)677 split_str_slow(pool_t pool, const char *data, const char *separators, bool spaces)
678 {
679         char **array;
680 	char *str;
681         unsigned int count, alloc_count, new_alloc_count;
682 
683 	if (spaces) {
684 		/* skip leading separators */
685 		while (*data != '\0' && strchr(separators, *data) != NULL)
686 			data++;
687 	}
688 	if (*data == '\0')
689 		return p_new(pool, char *, 1);
690 
691 	str = p_strdup(pool, data);
692 
693 	alloc_count = 32;
694 	array = p_new(pool, char *, alloc_count);
695 
696 	array[0] = str; count = 1;
697 	while (*str != '\0') {
698 		if (strchr(separators, *str) != NULL) {
699 			/* separator found */
700 			if (count+1 >= alloc_count) {
701                                 new_alloc_count = nearest_power(alloc_count+1);
702 				array = p_realloc(pool, array,
703 						  sizeof(char *) * alloc_count,
704 						  sizeof(char *) *
705 						  new_alloc_count);
706 				alloc_count = new_alloc_count;
707 			}
708 
709 			*str = '\0';
710 			if (spaces) {
711 				while (str[1] != '\0' &&
712 				       strchr(separators, str[1]) != NULL)
713 					str++;
714 
715 				/* ignore trailing separators */
716 				if (str[1] == '\0')
717 					break;
718 			}
719 
720 			array[count++] = str+1;
721 		}
722 
723                 str++;
724 	}
725 
726 	i_assert(count < alloc_count);
727         array[count] = NULL;
728 
729         return array;
730 }
731 
732 static char **
split_str_fast(pool_t pool,const char * data,char sep)733 split_str_fast(pool_t pool, const char *data, char sep)
734 {
735 	char **array, *str;
736 	unsigned int count, alloc_count, new_alloc_count;
737 
738 	if (*data == '\0')
739 		return p_new(pool, char *, 1);
740 
741 	str = p_strdup(pool, data);
742 
743 	alloc_count = 32;
744 	array = p_new(pool, char *, alloc_count);
745 
746 	array[0] = str; count = 1;
747 	while ((str = strchr(str, sep)) != NULL) {
748 		/* separator found */
749 		if (count+1 >= alloc_count) {
750 			new_alloc_count = nearest_power(alloc_count+1);
751 			array = p_realloc(pool, array,
752 					  sizeof(char *) * alloc_count,
753 					  sizeof(char *) *
754 					  new_alloc_count);
755 			alloc_count = new_alloc_count;
756 		}
757 		*str++ = '\0';
758 		array[count++] = str;
759 	}
760 	i_assert(count < alloc_count);
761 	i_assert(array[count] == NULL);
762 
763 	return array;
764 }
765 
766 static char **
split_str(pool_t pool,const char * data,const char * separators,bool spaces)767 split_str(pool_t pool, const char *data, const char *separators, bool spaces)
768 {
769 	i_assert(*separators != '\0');
770 
771 	if (separators[1] == '\0' && !spaces)
772 		return split_str_fast(pool, data, separators[0]);
773 	else
774 		return split_str_slow(pool, data, separators, spaces);
775 }
776 
t_strsplit(const char * data,const char * separators)777 const char **t_strsplit(const char *data, const char *separators)
778 {
779 	return (const char **)split_str(unsafe_data_stack_pool, data,
780 					separators, FALSE);
781 }
782 
t_strsplit_spaces(const char * data,const char * separators)783 const char **t_strsplit_spaces(const char *data, const char *separators)
784 {
785 	return (const char **)split_str(unsafe_data_stack_pool, data,
786 					separators, TRUE);
787 }
788 
p_strsplit(pool_t pool,const char * data,const char * separators)789 char **p_strsplit(pool_t pool, const char *data, const char *separators)
790 {
791 	return split_str(pool, data, separators, FALSE);
792 }
793 
p_strsplit_spaces(pool_t pool,const char * data,const char * separators)794 char **p_strsplit_spaces(pool_t pool, const char *data,
795 			 const char *separators)
796 {
797 	return split_str(pool, data, separators, TRUE);
798 }
799 
p_strsplit_free(pool_t pool,char ** arr)800 void p_strsplit_free(pool_t pool, char **arr)
801 {
802 	p_free(pool, arr[0]);
803 	p_free(pool, arr);
804 }
805 
str_array_length(const char * const * arr)806 unsigned int str_array_length(const char *const *arr)
807 {
808 	unsigned int count;
809 
810 	if (arr == NULL)
811 		return 0;
812 
813 	for (count = 0; *arr != NULL; arr++)
814 		count++;
815 
816 	return count;
817 }
818 
819 static char *
p_strarray_join_n(pool_t pool,const char * const * arr,unsigned int arr_len,const char * separator)820 p_strarray_join_n(pool_t pool, const char *const *arr, unsigned int arr_len,
821 		  const char *separator)
822 {
823 	size_t alloc_len, sep_len, len, pos, needed_space;
824 	unsigned int i;
825 	char *str;
826 
827 	sep_len = strlen(separator);
828         alloc_len = 64;
829 	str = t_buffer_get(alloc_len);
830 	pos = 0;
831 
832 	for (i = 0; i < arr_len; i++) {
833 		len = strlen(arr[i]);
834 		needed_space = pos + len + sep_len + 1;
835 		if (needed_space > alloc_len) {
836 			alloc_len = nearest_power(needed_space);
837 			str = t_buffer_reget(str, alloc_len);
838 		}
839 
840 		if (i != 0) {
841 			memcpy(str + pos, separator, sep_len);
842 			pos += sep_len;
843 		}
844 
845 		memcpy(str + pos, arr[i], len);
846 		pos += len;
847 	}
848 	str[pos] = '\0';
849 	if (!pool->datastack_pool)
850 		return p_memdup(pool, str, pos + 1);
851 	t_buffer_alloc(pos + 1);
852 	return str;
853 }
854 
t_strarray_join(const char * const * arr,const char * separator)855 const char *t_strarray_join(const char *const *arr, const char *separator)
856 {
857 	return p_strarray_join_n(unsafe_data_stack_pool, arr,
858 				 str_array_length(arr), separator);
859 }
860 
str_array_remove(const char ** arr,const char * value)861 bool str_array_remove(const char **arr, const char *value)
862 {
863 	const char **dest;
864 
865 	for (; *arr != NULL; arr++) {
866 		if (strcmp(*arr, value) == 0) {
867 			/* found it. now move the rest. */
868 			for (dest = arr, arr++; *arr != NULL; arr++, dest++)
869 				*dest = *arr;
870 			*dest = NULL;
871 			return TRUE;
872 		}
873 	}
874 	return FALSE;
875 }
876 
str_array_find(const char * const * arr,const char * value)877 bool str_array_find(const char *const *arr, const char *value)
878 {
879 	for (; *arr != NULL; arr++) {
880 		if (strcmp(*arr, value) == 0)
881 			return TRUE;
882 	}
883 	return FALSE;
884 }
885 
str_array_icase_find(const char * const * arr,const char * value)886 bool str_array_icase_find(const char *const *arr, const char *value)
887 {
888 	for (; *arr != NULL; arr++) {
889 		if (strcasecmp(*arr, value) == 0)
890 			return TRUE;
891 	}
892 	return FALSE;
893 }
894 
p_strarray_dup(pool_t pool,const char * const * arr)895 const char **p_strarray_dup(pool_t pool, const char *const *arr)
896 {
897 	unsigned int i;
898 	const char **ret;
899 	char *p;
900 	size_t len, size = sizeof(const char *);
901 
902 	/* @UNSAFE: integer overflow checks are missing */
903 	for (i = 0; arr[i] != NULL; i++)
904 		size += sizeof(const char *) + strlen(arr[i]) + 1;
905 
906 	ret = p_malloc(pool, size);
907 	p = PTR_OFFSET(ret, sizeof(const char *) * (i + 1));
908 	for (i = 0; arr[i] != NULL; i++) {
909 		len = strlen(arr[i]) + 1;
910 		memcpy(p, arr[i], len);
911 		ret[i] = p;
912 		p += len;
913 	}
914 	i_assert(PTR_OFFSET(ret, size) == (void *)p);
915 	return ret;
916 }
917 
dec2str(uintmax_t number)918 const char *dec2str(uintmax_t number)
919 {
920 	return dec2str_buf(t_malloc_no0(MAX_INT_STRLEN), number);
921 }
922 
dec2str_buf(char buffer[STATIC_ARRAY MAX_INT_STRLEN],uintmax_t number)923 char *dec2str_buf(char buffer[STATIC_ARRAY MAX_INT_STRLEN], uintmax_t number)
924 {
925 	int pos;
926 
927 	pos = MAX_INT_STRLEN;
928 	buffer[--pos] = '\0';
929 	do {
930 		buffer[--pos] = (number % 10) + '0';
931 		number /= 10;
932 	} while (number != 0 && pos >= 0);
933 
934 	i_assert(pos >= 0);
935 	return buffer + pos;
936 }
937 
p_array_const_string_join(pool_t pool,const ARRAY_TYPE (const_string)* arr,const char * separator)938 char *p_array_const_string_join(pool_t pool, const ARRAY_TYPE(const_string) *arr,
939 				const char *separator)
940 {
941 	if (array_count(arr) == 0)
942 		return "";
943 	return p_strarray_join_n(pool, array_front(arr), array_count(arr),
944 				 separator);
945 }
946