]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/string-util.c
Merge pull request #9201 from poettering/integration-test-unfuck
[thirdparty/systemd.git] / src / basic / string-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6 ***/
7
8 #include <errno.h>
9 #include <stdarg.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdio_ext.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include "alloc-util.h"
17 #include "escape.h"
18 #include "gunicode.h"
19 #include "locale-util.h"
20 #include "macro.h"
21 #include "string-util.h"
22 #include "terminal-util.h"
23 #include "utf8.h"
24 #include "util.h"
25 #include "fileio.h"
26
27 int strcmp_ptr(const char *a, const char *b) {
28
29 /* Like strcmp(), but tries to make sense of NULL pointers */
30 if (a && b)
31 return strcmp(a, b);
32
33 if (!a && b)
34 return -1;
35
36 if (a && !b)
37 return 1;
38
39 return 0;
40 }
41
42 char* endswith(const char *s, const char *postfix) {
43 size_t sl, pl;
44
45 assert(s);
46 assert(postfix);
47
48 sl = strlen(s);
49 pl = strlen(postfix);
50
51 if (pl == 0)
52 return (char*) s + sl;
53
54 if (sl < pl)
55 return NULL;
56
57 if (memcmp(s + sl - pl, postfix, pl) != 0)
58 return NULL;
59
60 return (char*) s + sl - pl;
61 }
62
63 char* endswith_no_case(const char *s, const char *postfix) {
64 size_t sl, pl;
65
66 assert(s);
67 assert(postfix);
68
69 sl = strlen(s);
70 pl = strlen(postfix);
71
72 if (pl == 0)
73 return (char*) s + sl;
74
75 if (sl < pl)
76 return NULL;
77
78 if (strcasecmp(s + sl - pl, postfix) != 0)
79 return NULL;
80
81 return (char*) s + sl - pl;
82 }
83
84 char* first_word(const char *s, const char *word) {
85 size_t sl, wl;
86 const char *p;
87
88 assert(s);
89 assert(word);
90
91 /* Checks if the string starts with the specified word, either
92 * followed by NUL or by whitespace. Returns a pointer to the
93 * NUL or the first character after the whitespace. */
94
95 sl = strlen(s);
96 wl = strlen(word);
97
98 if (sl < wl)
99 return NULL;
100
101 if (wl == 0)
102 return (char*) s;
103
104 if (memcmp(s, word, wl) != 0)
105 return NULL;
106
107 p = s + wl;
108 if (*p == 0)
109 return (char*) p;
110
111 if (!strchr(WHITESPACE, *p))
112 return NULL;
113
114 p += strspn(p, WHITESPACE);
115 return (char*) p;
116 }
117
118 static size_t strcspn_escaped(const char *s, const char *reject) {
119 bool escaped = false;
120 int n;
121
122 for (n=0; s[n]; n++) {
123 if (escaped)
124 escaped = false;
125 else if (s[n] == '\\')
126 escaped = true;
127 else if (strchr(reject, s[n]))
128 break;
129 }
130
131 /* if s ends in \, return index of previous char */
132 return n - escaped;
133 }
134
135 /* Split a string into words. */
136 const char* split(const char **state, size_t *l, const char *separator, bool quoted) {
137 const char *current;
138
139 current = *state;
140
141 if (!*current) {
142 assert(**state == '\0');
143 return NULL;
144 }
145
146 current += strspn(current, separator);
147 if (!*current) {
148 *state = current;
149 return NULL;
150 }
151
152 if (quoted && strchr("\'\"", *current)) {
153 char quotechars[2] = {*current, '\0'};
154
155 *l = strcspn_escaped(current + 1, quotechars);
156 if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
157 (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
158 /* right quote missing or garbage at the end */
159 *state = current;
160 return NULL;
161 }
162 *state = current++ + *l + 2;
163 } else if (quoted) {
164 *l = strcspn_escaped(current, separator);
165 if (current[*l] && !strchr(separator, current[*l])) {
166 /* unfinished escape */
167 *state = current;
168 return NULL;
169 }
170 *state = current + *l;
171 } else {
172 *l = strcspn(current, separator);
173 *state = current + *l;
174 }
175
176 return current;
177 }
178
179 char *strnappend(const char *s, const char *suffix, size_t b) {
180 size_t a;
181 char *r;
182
183 if (!s && !suffix)
184 return strdup("");
185
186 if (!s)
187 return strndup(suffix, b);
188
189 if (!suffix)
190 return strdup(s);
191
192 assert(s);
193 assert(suffix);
194
195 a = strlen(s);
196 if (b > ((size_t) -1) - a)
197 return NULL;
198
199 r = new(char, a+b+1);
200 if (!r)
201 return NULL;
202
203 memcpy(r, s, a);
204 memcpy(r+a, suffix, b);
205 r[a+b] = 0;
206
207 return r;
208 }
209
210 char *strappend(const char *s, const char *suffix) {
211 return strnappend(s, suffix, strlen_ptr(suffix));
212 }
213
214 char *strjoin_real(const char *x, ...) {
215 va_list ap;
216 size_t l;
217 char *r, *p;
218
219 va_start(ap, x);
220
221 if (x) {
222 l = strlen(x);
223
224 for (;;) {
225 const char *t;
226 size_t n;
227
228 t = va_arg(ap, const char *);
229 if (!t)
230 break;
231
232 n = strlen(t);
233 if (n > ((size_t) -1) - l) {
234 va_end(ap);
235 return NULL;
236 }
237
238 l += n;
239 }
240 } else
241 l = 0;
242
243 va_end(ap);
244
245 r = new(char, l+1);
246 if (!r)
247 return NULL;
248
249 if (x) {
250 p = stpcpy(r, x);
251
252 va_start(ap, x);
253
254 for (;;) {
255 const char *t;
256
257 t = va_arg(ap, const char *);
258 if (!t)
259 break;
260
261 p = stpcpy(p, t);
262 }
263
264 va_end(ap);
265 } else
266 r[0] = 0;
267
268 return r;
269 }
270
271 char *strstrip(char *s) {
272 if (!s)
273 return NULL;
274
275 /* Drops trailing whitespace. Modifies the string in place. Returns pointer to first non-space character */
276
277 return delete_trailing_chars(skip_leading_chars(s, WHITESPACE), WHITESPACE);
278 }
279
280 char *delete_chars(char *s, const char *bad) {
281 char *f, *t;
282
283 /* Drops all specified bad characters, regardless where in the string */
284
285 if (!s)
286 return NULL;
287
288 if (!bad)
289 bad = WHITESPACE;
290
291 for (f = s, t = s; *f; f++) {
292 if (strchr(bad, *f))
293 continue;
294
295 *(t++) = *f;
296 }
297
298 *t = 0;
299
300 return s;
301 }
302
303 char *delete_trailing_chars(char *s, const char *bad) {
304 char *p, *c = s;
305
306 /* Drops all specified bad characters, at the end of the string */
307
308 if (!s)
309 return NULL;
310
311 if (!bad)
312 bad = WHITESPACE;
313
314 for (p = s; *p; p++)
315 if (!strchr(bad, *p))
316 c = p + 1;
317
318 *c = 0;
319
320 return s;
321 }
322
323 char *truncate_nl(char *s) {
324 assert(s);
325
326 s[strcspn(s, NEWLINE)] = 0;
327 return s;
328 }
329
330 char ascii_tolower(char x) {
331
332 if (x >= 'A' && x <= 'Z')
333 return x - 'A' + 'a';
334
335 return x;
336 }
337
338 char ascii_toupper(char x) {
339
340 if (x >= 'a' && x <= 'z')
341 return x - 'a' + 'A';
342
343 return x;
344 }
345
346 char *ascii_strlower(char *t) {
347 char *p;
348
349 assert(t);
350
351 for (p = t; *p; p++)
352 *p = ascii_tolower(*p);
353
354 return t;
355 }
356
357 char *ascii_strupper(char *t) {
358 char *p;
359
360 assert(t);
361
362 for (p = t; *p; p++)
363 *p = ascii_toupper(*p);
364
365 return t;
366 }
367
368 char *ascii_strlower_n(char *t, size_t n) {
369 size_t i;
370
371 if (n <= 0)
372 return t;
373
374 for (i = 0; i < n; i++)
375 t[i] = ascii_tolower(t[i]);
376
377 return t;
378 }
379
380 int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
381
382 for (; n > 0; a++, b++, n--) {
383 int x, y;
384
385 x = (int) (uint8_t) ascii_tolower(*a);
386 y = (int) (uint8_t) ascii_tolower(*b);
387
388 if (x != y)
389 return x - y;
390 }
391
392 return 0;
393 }
394
395 int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
396 int r;
397
398 r = ascii_strcasecmp_n(a, b, MIN(n, m));
399 if (r != 0)
400 return r;
401
402 if (n < m)
403 return -1;
404 else if (n > m)
405 return 1;
406 else
407 return 0;
408 }
409
410 bool chars_intersect(const char *a, const char *b) {
411 const char *p;
412
413 /* Returns true if any of the chars in a are in b. */
414 for (p = a; *p; p++)
415 if (strchr(b, *p))
416 return true;
417
418 return false;
419 }
420
421 bool string_has_cc(const char *p, const char *ok) {
422 const char *t;
423
424 assert(p);
425
426 /*
427 * Check if a string contains control characters. If 'ok' is
428 * non-NULL it may be a string containing additional CCs to be
429 * considered OK.
430 */
431
432 for (t = p; *t; t++) {
433 if (ok && strchr(ok, *t))
434 continue;
435
436 if (*t > 0 && *t < ' ')
437 return true;
438
439 if (*t == 127)
440 return true;
441 }
442
443 return false;
444 }
445
446 static int write_ellipsis(char *buf, bool unicode) {
447 if (unicode || is_locale_utf8()) {
448 buf[0] = 0xe2; /* tri-dot ellipsis: … */
449 buf[1] = 0x80;
450 buf[2] = 0xa6;
451 } else {
452 buf[0] = '.';
453 buf[1] = '.';
454 buf[2] = '.';
455 }
456
457 return 3;
458 }
459
460 static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
461 size_t x, need_space, suffix_len;
462 char *t;
463
464 assert(s);
465 assert(percent <= 100);
466 assert(new_length != (size_t) -1);
467
468 if (old_length <= new_length)
469 return strndup(s, old_length);
470
471 /* Special case short ellipsations */
472 switch (new_length) {
473
474 case 0:
475 return strdup("");
476
477 case 1:
478 if (is_locale_utf8())
479 return strdup("…");
480 else
481 return strdup(".");
482
483 case 2:
484 if (!is_locale_utf8())
485 return strdup("..");
486
487 break;
488
489 default:
490 break;
491 }
492
493 /* Calculate how much space the ellipsis will take up. If we are in UTF-8 mode we only need space for one
494 * character ("…"), otherwise for three characters ("..."). Note that in both cases we need 3 bytes of storage,
495 * either for the UTF-8 encoded character or for three ASCII characters. */
496 need_space = is_locale_utf8() ? 1 : 3;
497
498 t = new(char, new_length+3);
499 if (!t)
500 return NULL;
501
502 assert(new_length >= need_space);
503
504 x = ((new_length - need_space) * percent + 50) / 100;
505 assert(x <= new_length - need_space);
506
507 memcpy(t, s, x);
508 write_ellipsis(t + x, false);
509 suffix_len = new_length - x - need_space;
510 memcpy(t + x + 3, s + old_length - suffix_len, suffix_len);
511 *(t + x + 3 + suffix_len) = '\0';
512
513 return t;
514 }
515
516 char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
517 size_t x, k, len, len2;
518 const char *i, *j;
519 char *e;
520 int r;
521
522 /* Note that 'old_length' refers to bytes in the string, while 'new_length' refers to character cells taken up
523 * on screen. This distinction doesn't matter for ASCII strings, but it does matter for non-ASCII UTF-8
524 * strings.
525 *
526 * Ellipsation is done in a locale-dependent way:
527 * 1. If the string passed in is fully ASCII and the current locale is not UTF-8, three dots are used ("...")
528 * 2. Otherwise, a unicode ellipsis is used ("…")
529 *
530 * In other words: you'll get a unicode ellipsis as soon as either the string contains non-ASCII characters or
531 * the current locale is UTF-8.
532 */
533
534 assert(s);
535 assert(percent <= 100);
536
537 if (new_length == (size_t) -1)
538 return strndup(s, old_length);
539
540 if (new_length == 0)
541 return strdup("");
542
543 /* If no multibyte characters use ascii_ellipsize_mem for speed */
544 if (ascii_is_valid(s))
545 return ascii_ellipsize_mem(s, old_length, new_length, percent);
546
547 x = ((new_length - 1) * percent) / 100;
548 assert(x <= new_length - 1);
549
550 k = 0;
551 for (i = s; i < s + old_length; i = utf8_next_char(i)) {
552 char32_t c;
553 int w;
554
555 r = utf8_encoded_to_unichar(i, &c);
556 if (r < 0)
557 return NULL;
558
559 w = unichar_iswide(c) ? 2 : 1;
560 if (k + w <= x)
561 k += w;
562 else
563 break;
564 }
565
566 for (j = s + old_length; j > i; ) {
567 char32_t c;
568 int w;
569 const char *jj;
570
571 jj = utf8_prev_char(j);
572 r = utf8_encoded_to_unichar(jj, &c);
573 if (r < 0)
574 return NULL;
575
576 w = unichar_iswide(c) ? 2 : 1;
577 if (k + w <= new_length) {
578 k += w;
579 j = jj;
580 } else
581 break;
582 }
583 assert(i <= j);
584
585 /* we don't actually need to ellipsize */
586 if (i == j)
587 return memdup_suffix0(s, old_length);
588
589 /* make space for ellipsis, if possible */
590 if (j < s + old_length)
591 j = utf8_next_char(j);
592 else if (i > s)
593 i = utf8_prev_char(i);
594
595 len = i - s;
596 len2 = s + old_length - j;
597 e = new(char, len + 3 + len2 + 1);
598 if (!e)
599 return NULL;
600
601 /*
602 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
603 old_length, new_length, x, len, len2, k);
604 */
605
606 memcpy(e, s, len);
607 write_ellipsis(e + len, true);
608 memcpy(e + len + 3, j, len2);
609 *(e + len + 3 + len2) = '\0';
610
611 return e;
612 }
613
614 char *cellescape(char *buf, size_t len, const char *s) {
615 /* Escape and ellipsize s into buffer buf of size len. Only non-control ASCII
616 * characters are copied as they are, everything else is escaped. The result
617 * is different then if escaping and ellipsization was performed in two
618 * separate steps, because each sequence is either stored in full or skipped.
619 *
620 * This function should be used for logging about strings which expected to
621 * be plain ASCII in a safe way.
622 *
623 * An ellipsis will be used if s is too long. It was always placed at the
624 * very end.
625 */
626
627 size_t i = 0, last_char_width[4] = {}, k = 0, j;
628
629 assert(len > 0); /* at least a terminating NUL */
630
631 for (;;) {
632 char four[4];
633 int w;
634
635 if (*s == 0) /* terminating NUL detected? then we are done! */
636 goto done;
637
638 w = cescape_char(*s, four);
639 if (i + w + 1 > len) /* This character doesn't fit into the buffer anymore? In that case let's
640 * ellipsize at the previous location */
641 break;
642
643 /* OK, there was space, let's add this escaped character to the buffer */
644 memcpy(buf + i, four, w);
645 i += w;
646
647 /* And remember its width in the ring buffer */
648 last_char_width[k] = w;
649 k = (k + 1) % 4;
650
651 s++;
652 }
653
654 /* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4
655 * characters ideally, but the buffer is shorter than that in the first place take what we can get */
656 for (j = 0; j < ELEMENTSOF(last_char_width); j++) {
657
658 if (i + 4 <= len) /* nice, we reached our space goal */
659 break;
660
661 k = k == 0 ? 3 : k - 1;
662 if (last_char_width[k] == 0) /* bummer, we reached the beginning of the strings */
663 break;
664
665 assert(i >= last_char_width[k]);
666 i -= last_char_width[k];
667 }
668
669 if (i + 4 <= len) /* yay, enough space */
670 i += write_ellipsis(buf + i, false);
671 else if (i + 3 <= len) { /* only space for ".." */
672 buf[i++] = '.';
673 buf[i++] = '.';
674 } else if (i + 2 <= len) /* only space for a single "." */
675 buf[i++] = '.';
676 else
677 assert(i + 1 <= len);
678
679 done:
680 buf[i] = '\0';
681 return buf;
682 }
683
684 bool nulstr_contains(const char *nulstr, const char *needle) {
685 const char *i;
686
687 if (!nulstr)
688 return false;
689
690 NULSTR_FOREACH(i, nulstr)
691 if (streq(i, needle))
692 return true;
693
694 return false;
695 }
696
697 char* strshorten(char *s, size_t l) {
698 assert(s);
699
700 if (strnlen(s, l+1) > l)
701 s[l] = 0;
702
703 return s;
704 }
705
706 char *strreplace(const char *text, const char *old_string, const char *new_string) {
707 size_t l, old_len, new_len, allocated = 0;
708 char *t, *ret = NULL;
709 const char *f;
710
711 assert(old_string);
712 assert(new_string);
713
714 if (!text)
715 return NULL;
716
717 old_len = strlen(old_string);
718 new_len = strlen(new_string);
719
720 l = strlen(text);
721 if (!GREEDY_REALLOC(ret, allocated, l+1))
722 return NULL;
723
724 f = text;
725 t = ret;
726 while (*f) {
727 size_t d, nl;
728
729 if (!startswith(f, old_string)) {
730 *(t++) = *(f++);
731 continue;
732 }
733
734 d = t - ret;
735 nl = l - old_len + new_len;
736
737 if (!GREEDY_REALLOC(ret, allocated, nl + 1))
738 return mfree(ret);
739
740 l = nl;
741 t = ret + d;
742
743 t = stpcpy(t, new_string);
744 f += old_len;
745 }
746
747 *t = 0;
748 return ret;
749 }
750
751 static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) {
752 if (!offsets)
753 return;
754
755 if ((size_t) diff < offsets[0])
756 shift[0] += size;
757 if ((size_t) diff < offsets[1])
758 shift[1] += size;
759 }
760
761 char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
762 const char *i, *begin = NULL;
763 enum {
764 STATE_OTHER,
765 STATE_ESCAPE,
766 STATE_CSI,
767 STATE_CSO,
768 } state = STATE_OTHER;
769 char *obuf = NULL;
770 size_t osz = 0, isz, shift[2] = {};
771 FILE *f;
772
773 assert(ibuf);
774 assert(*ibuf);
775
776 /* This does three things:
777 *
778 * 1. Replaces TABs by 8 spaces
779 * 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences
780 * 3. Strips ANSI operating system sequences (CSO), i.e. ESC ']' … BEL sequences
781 *
782 * Everything else will be left as it is. In particular other ANSI sequences are left as they are, as are any
783 * other special characters. Truncated ANSI sequences are left-as is too. This call is supposed to suppress the
784 * most basic formatting noise, but nothing else.
785 *
786 * Why care for CSO sequences? Well, to undo what terminal_urlify() and friends generate. */
787
788 isz = _isz ? *_isz : strlen(*ibuf);
789
790 f = open_memstream(&obuf, &osz);
791 if (!f)
792 return NULL;
793
794 /* Note we turn off internal locking on f for performance reasons. It's safe to do so since we created f here
795 * and it doesn't leave our scope. */
796
797 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
798
799 for (i = *ibuf; i < *ibuf + isz + 1; i++) {
800
801 switch (state) {
802
803 case STATE_OTHER:
804 if (i >= *ibuf + isz) /* EOT */
805 break;
806 else if (*i == '\x1B')
807 state = STATE_ESCAPE;
808 else if (*i == '\t') {
809 fputs(" ", f);
810 advance_offsets(i - *ibuf, highlight, shift, 7);
811 } else
812 fputc(*i, f);
813
814 break;
815
816 case STATE_ESCAPE:
817 if (i >= *ibuf + isz) { /* EOT */
818 fputc('\x1B', f);
819 advance_offsets(i - *ibuf, highlight, shift, 1);
820 break;
821 } else if (*i == '[') { /* ANSI CSI */
822 state = STATE_CSI;
823 begin = i + 1;
824 } else if (*i == ']') { /* ANSI CSO */
825 state = STATE_CSO;
826 begin = i + 1;
827 } else {
828 fputc('\x1B', f);
829 fputc(*i, f);
830 advance_offsets(i - *ibuf, highlight, shift, 1);
831 state = STATE_OTHER;
832 }
833
834 break;
835
836 case STATE_CSI:
837
838 if (i >= *ibuf + isz || /* EOT … */
839 !strchr("01234567890;m", *i)) { /* … or invalid chars in sequence */
840 fputc('\x1B', f);
841 fputc('[', f);
842 advance_offsets(i - *ibuf, highlight, shift, 2);
843 state = STATE_OTHER;
844 i = begin-1;
845 } else if (*i == 'm')
846 state = STATE_OTHER;
847
848 break;
849
850 case STATE_CSO:
851
852 if (i >= *ibuf + isz || /* EOT … */
853 (*i != '\a' && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */
854 fputc('\x1B', f);
855 fputc(']', f);
856 advance_offsets(i - *ibuf, highlight, shift, 2);
857 state = STATE_OTHER;
858 i = begin-1;
859 } else if (*i == '\a')
860 state = STATE_OTHER;
861
862 break;
863 }
864 }
865
866 if (fflush_and_check(f) < 0) {
867 fclose(f);
868 return mfree(obuf);
869 }
870
871 fclose(f);
872
873 free(*ibuf);
874 *ibuf = obuf;
875
876 if (_isz)
877 *_isz = osz;
878
879 if (highlight) {
880 highlight[0] += shift[0];
881 highlight[1] += shift[1];
882 }
883
884 return obuf;
885 }
886
887 char *strextend_with_separator(char **x, const char *separator, ...) {
888 bool need_separator;
889 size_t f, l, l_separator;
890 char *r, *p;
891 va_list ap;
892
893 assert(x);
894
895 l = f = strlen_ptr(*x);
896
897 need_separator = !isempty(*x);
898 l_separator = strlen_ptr(separator);
899
900 va_start(ap, separator);
901 for (;;) {
902 const char *t;
903 size_t n;
904
905 t = va_arg(ap, const char *);
906 if (!t)
907 break;
908
909 n = strlen(t);
910
911 if (need_separator)
912 n += l_separator;
913
914 if (n > ((size_t) -1) - l) {
915 va_end(ap);
916 return NULL;
917 }
918
919 l += n;
920 need_separator = true;
921 }
922 va_end(ap);
923
924 need_separator = !isempty(*x);
925
926 r = realloc(*x, l+1);
927 if (!r)
928 return NULL;
929
930 p = r + f;
931
932 va_start(ap, separator);
933 for (;;) {
934 const char *t;
935
936 t = va_arg(ap, const char *);
937 if (!t)
938 break;
939
940 if (need_separator && separator)
941 p = stpcpy(p, separator);
942
943 p = stpcpy(p, t);
944
945 need_separator = true;
946 }
947 va_end(ap);
948
949 assert(p == r + l);
950
951 *p = 0;
952 *x = r;
953
954 return r + l;
955 }
956
957 char *strrep(const char *s, unsigned n) {
958 size_t l;
959 char *r, *p;
960 unsigned i;
961
962 assert(s);
963
964 l = strlen(s);
965 p = r = malloc(l * n + 1);
966 if (!r)
967 return NULL;
968
969 for (i = 0; i < n; i++)
970 p = stpcpy(p, s);
971
972 *p = 0;
973 return r;
974 }
975
976 int split_pair(const char *s, const char *sep, char **l, char **r) {
977 char *x, *a, *b;
978
979 assert(s);
980 assert(sep);
981 assert(l);
982 assert(r);
983
984 if (isempty(sep))
985 return -EINVAL;
986
987 x = strstr(s, sep);
988 if (!x)
989 return -EINVAL;
990
991 a = strndup(s, x - s);
992 if (!a)
993 return -ENOMEM;
994
995 b = strdup(x + strlen(sep));
996 if (!b) {
997 free(a);
998 return -ENOMEM;
999 }
1000
1001 *l = a;
1002 *r = b;
1003
1004 return 0;
1005 }
1006
1007 int free_and_strdup(char **p, const char *s) {
1008 char *t;
1009
1010 assert(p);
1011
1012 /* Replaces a string pointer with an strdup()ed new string,
1013 * possibly freeing the old one. */
1014
1015 if (streq_ptr(*p, s))
1016 return 0;
1017
1018 if (s) {
1019 t = strdup(s);
1020 if (!t)
1021 return -ENOMEM;
1022 } else
1023 t = NULL;
1024
1025 free(*p);
1026 *p = t;
1027
1028 return 1;
1029 }
1030
1031 #if !HAVE_EXPLICIT_BZERO
1032 /*
1033 * Pointer to memset is volatile so that compiler must de-reference
1034 * the pointer and can't assume that it points to any function in
1035 * particular (such as memset, which it then might further "optimize")
1036 * This approach is inspired by openssl's crypto/mem_clr.c.
1037 */
1038 typedef void *(*memset_t)(void *,int,size_t);
1039
1040 static volatile memset_t memset_func = memset;
1041
1042 void explicit_bzero(void *p, size_t l) {
1043 memset_func(p, '\0', l);
1044 }
1045 #endif
1046
1047 char* string_erase(char *x) {
1048 if (!x)
1049 return NULL;
1050
1051 /* A delicious drop of snake-oil! To be called on memory where
1052 * we stored passphrases or so, after we used them. */
1053 explicit_bzero(x, strlen(x));
1054 return x;
1055 }
1056
1057 char *string_free_erase(char *s) {
1058 return mfree(string_erase(s));
1059 }
1060
1061 bool string_is_safe(const char *p) {
1062 const char *t;
1063
1064 if (!p)
1065 return false;
1066
1067 for (t = p; *t; t++) {
1068 if (*t > 0 && *t < ' ') /* no control characters */
1069 return false;
1070
1071 if (strchr(QUOTES "\\\x7f", *t))
1072 return false;
1073 }
1074
1075 return true;
1076 }