Coverage Report

Created: 2025-07-23 06:45

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