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