Coverage Report

Created: 2025-12-11 07:13

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