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