Coverage Report

Created: 2026-07-16 07:01

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