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