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