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