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