/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 | } |