chiark / gitweb /
1f8bf3d4de5cf46a72fc24c33efc7a54cb4128da
[elogind.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 #if 0 /// UNNEEDED by elogind
281 char *delete_chars(char *s, const char *bad) {
282         char *f, *t;
283
284         /* Drops all specified bad characters, regardless where in the string */
285
286         if (!s)
287                 return NULL;
288
289         if (!bad)
290                 bad = WHITESPACE;
291
292         for (f = s, t = s; *f; f++) {
293                 if (strchr(bad, *f))
294                         continue;
295
296                 *(t++) = *f;
297         }
298
299         *t = 0;
300
301         return s;
302 }
303 #endif // 0
304
305 char *delete_trailing_chars(char *s, const char *bad) {
306         char *p, *c = s;
307
308         /* Drops all specified bad characters, at the end of the string */
309
310         if (!s)
311                 return NULL;
312
313         if (!bad)
314                 bad = WHITESPACE;
315
316         for (p = s; *p; p++)
317                 if (!strchr(bad, *p))
318                         c = p + 1;
319
320         *c = 0;
321
322         return s;
323 }
324
325 char *truncate_nl(char *s) {
326         assert(s);
327
328         s[strcspn(s, NEWLINE)] = 0;
329         return s;
330 }
331
332 #if 0 /// UNNEEDED by elogind
333 char ascii_tolower(char x) {
334
335         if (x >= 'A' && x <= 'Z')
336                 return x - 'A' + 'a';
337
338         return x;
339 }
340
341 char ascii_toupper(char x) {
342
343         if (x >= 'a' && x <= 'z')
344                 return x - 'a' + 'A';
345
346         return x;
347 }
348
349 char *ascii_strlower(char *t) {
350         char *p;
351
352         assert(t);
353
354         for (p = t; *p; p++)
355                 *p = ascii_tolower(*p);
356
357         return t;
358 }
359
360 char *ascii_strupper(char *t) {
361         char *p;
362
363         assert(t);
364
365         for (p = t; *p; p++)
366                 *p = ascii_toupper(*p);
367
368         return t;
369 }
370
371 char *ascii_strlower_n(char *t, size_t n) {
372         size_t i;
373
374         if (n <= 0)
375                 return t;
376
377         for (i = 0; i < n; i++)
378                 t[i] = ascii_tolower(t[i]);
379
380         return t;
381 }
382
383 int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
384
385         for (; n > 0; a++, b++, n--) {
386                 int x, y;
387
388                 x = (int) (uint8_t) ascii_tolower(*a);
389                 y = (int) (uint8_t) ascii_tolower(*b);
390
391                 if (x != y)
392                         return x - y;
393         }
394
395         return 0;
396 }
397
398 int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
399         int r;
400
401         r = ascii_strcasecmp_n(a, b, MIN(n, m));
402         if (r != 0)
403                 return r;
404
405         if (n < m)
406                 return -1;
407         else if (n > m)
408                 return 1;
409         else
410                 return 0;
411 }
412
413 bool chars_intersect(const char *a, const char *b) {
414         const char *p;
415
416         /* Returns true if any of the chars in a are in b. */
417         for (p = a; *p; p++)
418                 if (strchr(b, *p))
419                         return true;
420
421         return false;
422 }
423 #endif // 0
424
425 bool string_has_cc(const char *p, const char *ok) {
426         const char *t;
427
428         assert(p);
429
430         /*
431          * Check if a string contains control characters. If 'ok' is
432          * non-NULL it may be a string containing additional CCs to be
433          * considered OK.
434          */
435
436         for (t = p; *t; t++) {
437                 if (ok && strchr(ok, *t))
438                         continue;
439
440                 if (*t > 0 && *t < ' ')
441                         return true;
442
443                 if (*t == 127)
444                         return true;
445         }
446
447         return false;
448 }
449
450 static int write_ellipsis(char *buf, bool unicode) {
451         if (unicode || is_locale_utf8()) {
452                 buf[0] = 0xe2; /* tri-dot ellipsis: … */
453                 buf[1] = 0x80;
454                 buf[2] = 0xa6;
455         } else {
456                 buf[0] = '.';
457                 buf[1] = '.';
458                 buf[2] = '.';
459         }
460
461         return 3;
462 }
463
464 static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
465         size_t x, need_space, suffix_len;
466         char *t;
467
468         assert(s);
469         assert(percent <= 100);
470         assert(new_length != (size_t) -1);
471
472         if (old_length <= new_length)
473                 return strndup(s, old_length);
474
475         /* Special case short ellipsations */
476         switch (new_length) {
477
478         case 0:
479                 return strdup("");
480
481         case 1:
482                 if (is_locale_utf8())
483                         return strdup("…");
484                 else
485                         return strdup(".");
486
487         case 2:
488                 if (!is_locale_utf8())
489                         return strdup("..");
490
491                 break;
492
493         default:
494                 break;
495         }
496
497         /* Calculate how much space the ellipsis will take up. If we are in UTF-8 mode we only need space for one
498          * character ("…"), otherwise for three characters ("..."). Note that in both cases we need 3 bytes of storage,
499          * either for the UTF-8 encoded character or for three ASCII characters. */
500         need_space = is_locale_utf8() ? 1 : 3;
501
502         t = new(char, new_length+3);
503         if (!t)
504                 return NULL;
505
506         assert(new_length >= need_space);
507
508         x = ((new_length - need_space) * percent + 50) / 100;
509         assert(x <= new_length - need_space);
510
511         memcpy(t, s, x);
512         write_ellipsis(t + x, false);
513         suffix_len = new_length - x - need_space;
514         memcpy(t + x + 3, s + old_length - suffix_len, suffix_len);
515         *(t + x + 3 + suffix_len) = '\0';
516
517         return t;
518 }
519
520 char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
521         size_t x, k, len, len2;
522         const char *i, *j;
523         char *e;
524         int r;
525
526         /* Note that 'old_length' refers to bytes in the string, while 'new_length' refers to character cells taken up
527          * on screen. This distinction doesn't matter for ASCII strings, but it does matter for non-ASCII UTF-8
528          * strings.
529          *
530          * Ellipsation is done in a locale-dependent way:
531          * 1. If the string passed in is fully ASCII and the current locale is not UTF-8, three dots are used ("...")
532          * 2. Otherwise, a unicode ellipsis is used ("…")
533          *
534          * In other words: you'll get a unicode ellipsis as soon as either the string contains non-ASCII characters or
535          * the current locale is UTF-8.
536          */
537
538         assert(s);
539         assert(percent <= 100);
540
541         if (new_length == (size_t) -1)
542                 return strndup(s, old_length);
543
544         if (new_length == 0)
545                 return strdup("");
546
547         /* If no multibyte characters use ascii_ellipsize_mem for speed */
548         if (ascii_is_valid_n(s, old_length))
549                 return ascii_ellipsize_mem(s, old_length, new_length, percent);
550
551         x = ((new_length - 1) * percent) / 100;
552         assert(x <= new_length - 1);
553
554         k = 0;
555         for (i = s; i < s + old_length; i = utf8_next_char(i)) {
556                 char32_t c;
557                 int w;
558
559                 r = utf8_encoded_to_unichar(i, &c);
560                 if (r < 0)
561                         return NULL;
562
563                 w = unichar_iswide(c) ? 2 : 1;
564                 if (k + w <= x)
565                         k += w;
566                 else
567                         break;
568         }
569
570         for (j = s + old_length; j > i; ) {
571                 char32_t c;
572                 int w;
573                 const char *jj;
574
575                 jj = utf8_prev_char(j);
576                 r = utf8_encoded_to_unichar(jj, &c);
577                 if (r < 0)
578                         return NULL;
579
580                 w = unichar_iswide(c) ? 2 : 1;
581                 if (k + w <= new_length) {
582                         k += w;
583                         j = jj;
584                 } else
585                         break;
586         }
587         assert(i <= j);
588
589         /* we don't actually need to ellipsize */
590         if (i == j)
591                 return memdup_suffix0(s, old_length);
592
593         /* make space for ellipsis, if possible */
594         if (j < s + old_length)
595                 j = utf8_next_char(j);
596         else if (i > s)
597                 i = utf8_prev_char(i);
598
599         len = i - s;
600         len2 = s + old_length - j;
601         e = new(char, len + 3 + len2 + 1);
602         if (!e)
603                 return NULL;
604
605         /*
606         printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
607                old_length, new_length, x, len, len2, k);
608         */
609
610         memcpy(e, s, len);
611         write_ellipsis(e + len, true);
612         memcpy(e + len + 3, j, len2);
613         *(e + len + 3 + len2) = '\0';
614
615         return e;
616 }
617
618 char *cellescape(char *buf, size_t len, const char *s) {
619         /* Escape and ellipsize s into buffer buf of size len. Only non-control ASCII
620          * characters are copied as they are, everything else is escaped. The result
621          * is different then if escaping and ellipsization was performed in two
622          * separate steps, because each sequence is either stored in full or skipped.
623          *
624          * This function should be used for logging about strings which expected to
625          * be plain ASCII in a safe way.
626          *
627          * An ellipsis will be used if s is too long. It was always placed at the
628          * very end.
629          */
630
631         size_t i = 0, last_char_width[4] = {}, k = 0, j;
632
633         assert(len > 0); /* at least a terminating NUL */
634
635         for (;;) {
636                 char four[4];
637                 int w;
638
639                 if (*s == 0) /* terminating NUL detected? then we are done! */
640                         goto done;
641
642                 w = cescape_char(*s, four);
643                 if (i + w + 1 > len) /* This character doesn't fit into the buffer anymore? In that case let's
644                                       * ellipsize at the previous location */
645                         break;
646
647                 /* OK, there was space, let's add this escaped character to the buffer */
648                 memcpy(buf + i, four, w);
649                 i += w;
650
651                 /* And remember its width in the ring buffer */
652                 last_char_width[k] = w;
653                 k = (k + 1) % 4;
654
655                 s++;
656         }
657
658         /* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4
659          * characters ideally, but the buffer is shorter than that in the first place take what we can get */
660         for (j = 0; j < ELEMENTSOF(last_char_width); j++) {
661
662                 if (i + 4 <= len) /* nice, we reached our space goal */
663                         break;
664
665                 k = k == 0 ? 3 : k - 1;
666                 if (last_char_width[k] == 0) /* bummer, we reached the beginning of the strings */
667                         break;
668
669                 assert(i >= last_char_width[k]);
670                 i -= last_char_width[k];
671         }
672
673         if (i + 4 <= len) /* yay, enough space */
674                 i += write_ellipsis(buf + i, false);
675         else if (i + 3 <= len) { /* only space for ".." */
676                 buf[i++] = '.';
677                 buf[i++] = '.';
678         } else if (i + 2 <= len) /* only space for a single "." */
679                 buf[i++] = '.';
680         else
681                 assert(i + 1 <= len);
682
683  done:
684         buf[i] = '\0';
685         return buf;
686 }
687
688 bool nulstr_contains(const char *nulstr, const char *needle) {
689         const char *i;
690
691         if (!nulstr)
692                 return false;
693
694         NULSTR_FOREACH(i, nulstr)
695                 if (streq(i, needle))
696                         return true;
697
698         return false;
699 }
700
701 char* strshorten(char *s, size_t l) {
702         assert(s);
703
704         if (strnlen(s, l+1) > l)
705                 s[l] = 0;
706
707         return s;
708 }
709
710 char *strreplace(const char *text, const char *old_string, const char *new_string) {
711         size_t l, old_len, new_len, allocated = 0;
712         char *t, *ret = NULL;
713         const char *f;
714
715         assert(old_string);
716         assert(new_string);
717
718         if (!text)
719                 return NULL;
720
721         old_len = strlen(old_string);
722         new_len = strlen(new_string);
723
724         l = strlen(text);
725         if (!GREEDY_REALLOC(ret, allocated, l+1))
726                 return NULL;
727
728         f = text;
729         t = ret;
730         while (*f) {
731                 size_t d, nl;
732
733                 if (!startswith(f, old_string)) {
734                         *(t++) = *(f++);
735                         continue;
736                 }
737
738                 d = t - ret;
739                 nl = l - old_len + new_len;
740
741                 if (!GREEDY_REALLOC(ret, allocated, nl + 1))
742                         return mfree(ret);
743
744                 l = nl;
745                 t = ret + d;
746
747                 t = stpcpy(t, new_string);
748                 f += old_len;
749         }
750
751         *t = 0;
752         return ret;
753 }
754
755 static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) {
756         if (!offsets)
757                 return;
758
759         if ((size_t) diff < offsets[0])
760                 shift[0] += size;
761         if ((size_t) diff < offsets[1])
762                 shift[1] += size;
763 }
764
765 char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
766         const char *i, *begin = NULL;
767         enum {
768                 STATE_OTHER,
769                 STATE_ESCAPE,
770                 STATE_CSI,
771                 STATE_CSO,
772         } state = STATE_OTHER;
773         char *obuf = NULL;
774         size_t osz = 0, isz, shift[2] = {};
775         FILE *f;
776
777         assert(ibuf);
778         assert(*ibuf);
779
780         /* This does three things:
781          *
782          * 1. Replaces TABs by 8 spaces
783          * 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences
784          * 3. Strips ANSI operating system sequences (CSO), i.e. ESC ']' … BEL sequences
785          *
786          * Everything else will be left as it is. In particular other ANSI sequences are left as they are, as are any
787          * other special characters. Truncated ANSI sequences are left-as is too. This call is supposed to suppress the
788          * most basic formatting noise, but nothing else.
789          *
790          * Why care for CSO sequences? Well, to undo what terminal_urlify() and friends generate. */
791
792         isz = _isz ? *_isz : strlen(*ibuf);
793
794         f = open_memstream(&obuf, &osz);
795         if (!f)
796                 return NULL;
797
798         /* Note we turn off internal locking on f for performance reasons.  It's safe to do so since we created f here
799          * and it doesn't leave our scope. */
800
801         (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
802
803         for (i = *ibuf; i < *ibuf + isz + 1; i++) {
804
805                 switch (state) {
806
807                 case STATE_OTHER:
808                         if (i >= *ibuf + isz) /* EOT */
809                                 break;
810                         else if (*i == '\x1B')
811                                 state = STATE_ESCAPE;
812                         else if (*i == '\t') {
813                                 fputs("        ", f);
814                                 advance_offsets(i - *ibuf, highlight, shift, 7);
815                         } else
816                                 fputc(*i, f);
817
818                         break;
819
820                 case STATE_ESCAPE:
821                         if (i >= *ibuf + isz) { /* EOT */
822                                 fputc('\x1B', f);
823                                 advance_offsets(i - *ibuf, highlight, shift, 1);
824                                 break;
825                         } else if (*i == '[') { /* ANSI CSI */
826                                 state = STATE_CSI;
827                                 begin = i + 1;
828                         } else if (*i == ']') { /* ANSI CSO */
829                                 state = STATE_CSO;
830                                 begin = i + 1;
831                         } else {
832                                 fputc('\x1B', f);
833                                 fputc(*i, f);
834                                 advance_offsets(i - *ibuf, highlight, shift, 1);
835                                 state = STATE_OTHER;
836                         }
837
838                         break;
839
840                 case STATE_CSI:
841
842                         if (i >= *ibuf + isz || /* EOT … */
843                             !strchr("01234567890;m", *i)) { /* … or invalid chars in sequence */
844                                 fputc('\x1B', f);
845                                 fputc('[', f);
846                                 advance_offsets(i - *ibuf, highlight, shift, 2);
847                                 state = STATE_OTHER;
848                                 i = begin-1;
849                         } else if (*i == 'm')
850                                 state = STATE_OTHER;
851
852                         break;
853
854                 case STATE_CSO:
855
856                         if (i >= *ibuf + isz || /* EOT … */
857                             (*i != '\a' && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */
858                                 fputc('\x1B', f);
859                                 fputc(']', f);
860                                 advance_offsets(i - *ibuf, highlight, shift, 2);
861                                 state = STATE_OTHER;
862                                 i = begin-1;
863                         } else if (*i == '\a')
864                                 state = STATE_OTHER;
865
866                         break;
867                 }
868         }
869
870         if (fflush_and_check(f) < 0) {
871                 fclose(f);
872                 return mfree(obuf);
873         }
874
875         fclose(f);
876
877         free(*ibuf);
878         *ibuf = obuf;
879
880         if (_isz)
881                 *_isz = osz;
882
883         if (highlight) {
884                 highlight[0] += shift[0];
885                 highlight[1] += shift[1];
886         }
887
888         return obuf;
889 }
890
891 char *strextend_with_separator(char **x, const char *separator, ...) {
892         bool need_separator;
893         size_t f, l, l_separator;
894         char *r, *p;
895         va_list ap;
896
897         assert(x);
898
899         l = f = strlen_ptr(*x);
900
901         need_separator = !isempty(*x);
902         l_separator = strlen_ptr(separator);
903
904         va_start(ap, separator);
905         for (;;) {
906                 const char *t;
907                 size_t n;
908
909                 t = va_arg(ap, const char *);
910                 if (!t)
911                         break;
912
913                 n = strlen(t);
914
915                 if (need_separator)
916                         n += l_separator;
917
918                 if (n > ((size_t) -1) - l) {
919                         va_end(ap);
920                         return NULL;
921                 }
922
923                 l += n;
924                 need_separator = true;
925         }
926         va_end(ap);
927
928         need_separator = !isempty(*x);
929
930         r = realloc(*x, l+1);
931         if (!r)
932                 return NULL;
933
934         p = r + f;
935
936         va_start(ap, separator);
937         for (;;) {
938                 const char *t;
939
940                 t = va_arg(ap, const char *);
941                 if (!t)
942                         break;
943
944                 if (need_separator && separator)
945                         p = stpcpy(p, separator);
946
947                 p = stpcpy(p, t);
948
949                 need_separator = true;
950         }
951         va_end(ap);
952
953         assert(p == r + l);
954
955         *p = 0;
956         *x = r;
957
958         return r + l;
959 }
960
961 char *strrep(const char *s, unsigned n) {
962         size_t l;
963         char *r, *p;
964         unsigned i;
965
966         assert(s);
967
968         l = strlen(s);
969         p = r = malloc(l * n + 1);
970         if (!r)
971                 return NULL;
972
973         for (i = 0; i < n; i++)
974                 p = stpcpy(p, s);
975
976         *p = 0;
977         return r;
978 }
979
980 int split_pair(const char *s, const char *sep, char **l, char **r) {
981         char *x, *a, *b;
982
983         assert(s);
984         assert(sep);
985         assert(l);
986         assert(r);
987
988         if (isempty(sep))
989                 return -EINVAL;
990
991         x = strstr(s, sep);
992         if (!x)
993                 return -EINVAL;
994
995         a = strndup(s, x - s);
996         if (!a)
997                 return -ENOMEM;
998
999         b = strdup(x + strlen(sep));
1000         if (!b) {
1001                 free(a);
1002                 return -ENOMEM;
1003         }
1004
1005         *l = a;
1006         *r = b;
1007
1008         return 0;
1009 }
1010
1011 int free_and_strdup(char **p, const char *s) {
1012         char *t;
1013
1014         assert(p);
1015
1016         /* Replaces a string pointer with an strdup()ed new string,
1017          * possibly freeing the old one. */
1018
1019         if (streq_ptr(*p, s))
1020                 return 0;
1021
1022         if (s) {
1023                 t = strdup(s);
1024                 if (!t)
1025                         return -ENOMEM;
1026         } else
1027                 t = NULL;
1028
1029         free(*p);
1030         *p = t;
1031
1032         return 1;
1033 }
1034
1035 #if !HAVE_EXPLICIT_BZERO
1036 /*
1037  * Pointer to memset is volatile so that compiler must de-reference
1038  * the pointer and can't assume that it points to any function in
1039  * particular (such as memset, which it then might further "optimize")
1040  * This approach is inspired by openssl's crypto/mem_clr.c.
1041  */
1042 typedef void *(*memset_t)(void *,int,size_t);
1043
1044 static volatile memset_t memset_func = memset;
1045
1046 void explicit_bzero(void *p, size_t l) {
1047         memset_func(p, '\0', l);
1048 }
1049 #endif
1050
1051 char* string_erase(char *x) {
1052         if (!x)
1053                 return NULL;
1054
1055         /* A delicious drop of snake-oil! To be called on memory where
1056          * we stored passphrases or so, after we used them. */
1057         explicit_bzero(x, strlen(x));
1058         return x;
1059 }
1060
1061 char *string_free_erase(char *s) {
1062         return mfree(string_erase(s));
1063 }
1064
1065 bool string_is_safe(const char *p) {
1066         const char *t;
1067
1068         if (!p)
1069                 return false;
1070
1071         for (t = p; *t; t++) {
1072                 if (*t > 0 && *t < ' ') /* no control characters */
1073                         return false;
1074
1075                 if (strchr(QUOTES "\\\x7f", *t))
1076                         return false;
1077         }
1078
1079         return true;
1080 }