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