1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
7 #include "extract-word.h"
8 #include "glyph-util.h"
10 #include "locale-util.h"
12 #include "memory-util.h"
13 #include "memstream-util.h"
14 #include "path-util.h"
15 #include "string-util.h"
17 #include "terminal-util.h"
20 char* first_word(const char *s
, const char *word
) {
24 /* Checks if the string starts with the specified word, either followed by NUL or by whitespace.
25 * Returns a pointer to the NUL or the first character after the whitespace. */
30 const char *p
= startswith(s
, word
);
36 const char *nw
= skip_leading_chars(p
, WHITESPACE
);
43 char* strextendn(char **x
, const char *s
, size_t l
) {
48 l
= strnlen(s
, l
); /* ignore trailing noise */
55 m
= realloc(*x
, q
+ l
+ 1);
59 *mempcpy_typesafe(m
+ q
, s
, l
) = 0;
67 char* strstrip(char *s
) {
71 /* Drops trailing whitespace. Modifies the string in place. Returns pointer to first non-space character */
73 return delete_trailing_chars(skip_leading_chars(s
, WHITESPACE
), WHITESPACE
);
76 char* delete_chars(char *s
, const char *bad
) {
79 /* Drops all specified bad characters, regardless where in the string */
87 for (f
= s
, t
= s
; *f
; f
++) {
99 char* delete_trailing_chars(char *s
, const char *bad
) {
102 /* Drops all specified bad characters, at the end of the string */
110 for (char *p
= s
; *p
; p
++)
111 if (!strchr(bad
, *p
))
119 char* truncate_nl_full(char *s
, size_t *ret_len
) {
124 n
= strcspn(s
, NEWLINE
);
131 char ascii_tolower(char x
) {
133 if (x
>= 'A' && x
<= 'Z')
134 return x
- 'A' + 'a';
139 char ascii_toupper(char x
) {
141 if (x
>= 'a' && x
<= 'z')
142 return x
- 'a' + 'A';
147 char* ascii_strlower(char *t
) {
150 for (char *p
= t
; *p
; p
++)
151 *p
= ascii_tolower(*p
);
156 char* ascii_strupper(char *t
) {
159 for (char *p
= t
; *p
; p
++)
160 *p
= ascii_toupper(*p
);
165 char* ascii_strlower_n(char *t
, size_t n
) {
169 for (size_t i
= 0; i
< n
; i
++)
170 t
[i
] = ascii_tolower(t
[i
]);
175 int ascii_strcasecmp_n(const char *a
, const char *b
, size_t n
) {
177 for (; n
> 0; a
++, b
++, n
--) {
180 x
= (int) (uint8_t) ascii_tolower(*a
);
181 y
= (int) (uint8_t) ascii_tolower(*b
);
190 int ascii_strcasecmp_nn(const char *a
, size_t n
, const char *b
, size_t m
) {
193 r
= ascii_strcasecmp_n(a
, b
, MIN(n
, m
));
200 bool chars_intersect(const char *a
, const char *b
) {
201 /* Returns true if any of the chars in a are in b. */
202 for (const char *p
= a
; *p
; p
++)
209 bool string_has_cc(const char *p
, const char *ok
) {
213 * Check if a string contains control characters. If 'ok' is
214 * non-NULL it may be a string containing additional CCs to be
218 for (const char *t
= p
; *t
; t
++) {
219 if (ok
&& strchr(ok
, *t
))
229 static int write_ellipsis(char *buf
, bool unicode
) {
230 const char *s
= glyph_full(GLYPH_ELLIPSIS
, unicode
);
231 assert(strlen(s
) == 3);
236 static size_t ansi_sequence_length(const char *s
, size_t len
) {
242 if (s
[0] != 0x1B) /* ASCII 27, aka ESC, aka Ctrl-[ */
243 return 0; /* Not the start of a sequence */
245 if (s
[1] == 0x5B) { /* [, start of CSI sequence */
251 while (s
[i
] >= 0x30 && s
[i
] <= 0x3F) /* Parameter bytes */
254 while (s
[i
] >= 0x20 && s
[i
] <= 0x2F) /* Intermediate bytes */
257 if (s
[i
] >= 0x40 && s
[i
] <= 0x7E) /* Final byte */
259 return 0; /* Bad sequence */
261 } else if (s
[1] >= 0x40 && s
[1] <= 0x5F) /* other non-CSI Fe sequence */
264 return 0; /* Bad escape? */
267 static bool string_has_ansi_sequence(const char *s
, size_t len
) {
270 while ((t
= memchr(s
, 0x1B, len
- (t
- s
))))
271 if (ansi_sequence_length(t
, len
- (t
- s
)) > 0)
276 static size_t previous_ansi_sequence(const char *s
, size_t length
, const char **ret_where
) {
277 /* Locate the previous ANSI sequence and save its start in *ret_where and return length. */
279 for (size_t i
= length
- 2; i
> 0; i
--) { /* -2 because at least two bytes are needed */
280 size_t slen
= ansi_sequence_length(s
+ (i
- 1), length
- (i
- 1));
284 *ret_where
= s
+ (i
- 1);
292 static char *ascii_ellipsize_mem(const char *s
, size_t old_length
, size_t new_length
, unsigned percent
) {
293 size_t x
, need_space
, suffix_len
;
297 assert(percent
<= 100);
298 assert(new_length
!= SIZE_MAX
);
300 if (old_length
<= new_length
)
301 return strndup(s
, old_length
);
303 /* Special case short ellipsations */
304 switch (new_length
) {
310 if (is_locale_utf8())
311 return strdup("…");
316 if (!is_locale_utf8())
321 /* Calculate how much space the ellipsis will take up. If we are in UTF-8 mode we only need space for one
322 * character ("…"), otherwise for three characters ("..."). Note that in both cases we need 3 bytes of storage,
323 * either for the UTF-8 encoded character or for three ASCII characters. */
324 need_space
= is_locale_utf8() ? 1 : 3;
326 t
= new(char, new_length
+3);
330 assert(new_length
>= need_space
);
332 x
= ((new_length
- need_space
) * percent
+ 50) / 100;
333 assert(x
<= new_length
- need_space
);
335 write_ellipsis(mempcpy(t
, s
, x
), /* unicode = */ false);
336 suffix_len
= new_length
- x
- need_space
;
337 memcpy(t
+ x
+ 3, s
+ old_length
- suffix_len
, suffix_len
);
338 *(t
+ x
+ 3 + suffix_len
) = '\0';
343 char* ellipsize_mem(const char *s
, size_t old_length
, size_t new_length
, unsigned percent
) {
344 size_t x
, k
, len
, len2
;
348 /* Note that 'old_length' refers to bytes in the string, while 'new_length' refers to character cells taken up
349 * on screen. This distinction doesn't matter for ASCII strings, but it does matter for non-ASCII UTF-8
352 * Ellipsation is done in a locale-dependent way:
353 * 1. If the string passed in is fully ASCII and the current locale is not UTF-8, three dots are used ("...")
354 * 2. Otherwise, a unicode ellipsis is used ("…")
356 * In other words: you'll get a unicode ellipsis as soon as either the string contains non-ASCII characters or
357 * the current locale is UTF-8.
361 assert(percent
<= 100);
363 if (new_length
== SIZE_MAX
)
364 return strndup(s
, old_length
);
369 bool has_ansi_seq
= string_has_ansi_sequence(s
, old_length
);
371 /* If no multibyte characters or ANSI sequences, use ascii_ellipsize_mem for speed */
372 if (!has_ansi_seq
&& ascii_is_valid_n(s
, old_length
))
373 return ascii_ellipsize_mem(s
, old_length
, new_length
, percent
);
375 x
= (new_length
- 1) * percent
/ 100;
376 assert(x
<= new_length
- 1);
379 for (i
= s
; i
< s
+ old_length
; ) {
380 size_t slen
= has_ansi_seq
? ansi_sequence_length(i
, old_length
- (i
- s
)) : 0;
383 continue; /* ANSI sequences don't take up any space in output */
387 r
= utf8_encoded_to_unichar(i
, &c
);
391 int w
= unichar_iswide(c
) ? 2 : 1;
399 const char *ansi_start
= s
+ old_length
;
402 for (const char *t
= j
= s
+ old_length
; t
> i
&& k
< new_length
; ) {
407 if (has_ansi_seq
&& ansi_start
>= t
)
408 /* Figure out the previous ANSI sequence, if any */
409 ansi_len
= previous_ansi_sequence(s
, t
- s
, &ansi_start
);
411 /* If the sequence extends all the way to the current position, skip it. */
412 if (has_ansi_seq
&& ansi_len
> 0 && ansi_start
+ ansi_len
== t
) {
417 tt
= utf8_prev_char(t
);
418 r
= utf8_encoded_to_unichar(tt
, &c
);
422 w
= unichar_iswide(c
) ? 2 : 1;
423 if (k
+ w
> new_length
)
427 j
= t
= tt
; /* j should always point to the first "real" character */
430 /* We don't actually need to ellipsize */
432 return memdup_suffix0(s
, old_length
);
434 if (k
>= new_length
) {
435 /* Make space for ellipsis, if required and possible. We know that the edge character is not
436 * part of an ANSI sequence (because then we'd skip it). If the last character we looked at
437 * was wide, we don't need to make space. */
438 if (j
< s
+ old_length
)
439 j
= utf8_next_char(j
);
441 i
= utf8_prev_char(i
);
445 len2
= s
+ old_length
- j
;
447 /* If we have ANSI, allow the same length as the source string + ellipsis. It'd be too involved to
448 * figure out what exact space is needed. Strings with ANSI sequences are most likely to be fairly
450 size_t alloc_len
= has_ansi_seq
? old_length
+ 3 + 1 : len
+ 3 + len2
+ 1;
452 char *e
= new(char, alloc_len
);
456 memcpy_safe(e
, s
, len
);
457 write_ellipsis(e
+ len
, /* unicode = */ true);
459 char *dst
= e
+ len
+ 3;
462 /* Copy over any ANSI sequences in full */
463 for (const char *p
= s
+ len
; p
< j
; ) {
464 size_t slen
= ansi_sequence_length(p
, j
- p
);
466 dst
= mempcpy(dst
, p
, slen
);
469 p
= utf8_next_char(p
);
472 memcpy_safe(dst
, j
, len2
);
478 char* cellescape(char *buf
, size_t len
, const char *s
) {
479 /* Escape and ellipsize s into buffer buf of size len. Only non-control ASCII
480 * characters are copied as they are, everything else is escaped. The result
481 * is different then if escaping and ellipsization was performed in two
482 * separate steps, because each sequence is either stored in full or skipped.
484 * This function should be used for logging about strings which expected to
485 * be plain ASCII in a safe way.
487 * An ellipsis will be used if s is too long. It was always placed at the
491 size_t i
= 0, last_char_width
[4] = {}, k
= 0;
494 assert(len
> 0); /* at least a terminating NUL */
501 if (*s
== 0) /* terminating NUL detected? then we are done! */
504 w
= cescape_char(*s
, four
);
505 if (i
+ w
+ 1 > len
) /* This character doesn't fit into the buffer anymore? In that case let's
506 * ellipsize at the previous location */
509 /* OK, there was space, let's add this escaped character to the buffer */
510 memcpy(buf
+ i
, four
, w
);
513 /* And remember its width in the ring buffer */
514 last_char_width
[k
] = w
;
520 /* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4
521 * characters ideally, but the buffer is shorter than that in the first place take what we can get */
522 for (size_t j
= 0; j
< ELEMENTSOF(last_char_width
); j
++) {
524 if (i
+ 4 <= len
) /* nice, we reached our space goal */
527 k
= k
== 0 ? 3 : k
- 1;
528 if (last_char_width
[k
] == 0) /* bummer, we reached the beginning of the strings */
531 assert(i
>= last_char_width
[k
]);
532 i
-= last_char_width
[k
];
535 if (i
+ 4 <= len
) /* yay, enough space */
536 i
+= write_ellipsis(buf
+ i
, /* unicode = */ false);
537 else if (i
+ 3 <= len
) { /* only space for ".." */
540 } else if (i
+ 2 <= len
) /* only space for a single "." */
543 assert(i
+ 1 <= len
);
550 char* strshorten(char *s
, size_t l
) {
553 if (l
>= SIZE_MAX
-1) /* Would not change anything */
556 if (strnlen(s
, l
+1) > l
)
562 int strgrowpad0(char **s
, size_t l
) {
569 if (sz
>= l
) /* never shrink */
574 char *q
= realloc(*s
, l
);
580 memzero(*s
+ sz
, l
- sz
);
584 char* strreplace(const char *text
, const char *old_string
, const char *new_string
) {
585 size_t l
, old_len
, new_len
;
586 char *t
, *ret
= NULL
;
595 old_len
= strlen(old_string
);
596 new_len
= strlen(new_string
);
599 if (!GREEDY_REALLOC(ret
, l
+1))
607 if (!startswith(f
, old_string
)) {
613 nl
= l
- old_len
+ new_len
;
615 if (!GREEDY_REALLOC(ret
, nl
+ 1))
621 t
= stpcpy(t
, new_string
);
629 static void advance_offsets(
631 size_t offsets
[2], /* note: we can't use [static 2] here, since this may be NULL */
632 size_t shift
[static 2],
640 if ((size_t) diff
< offsets
[0])
642 if ((size_t) diff
< offsets
[1])
646 char* strip_tab_ansi(char **ibuf
, size_t *_isz
, size_t highlight
[2]) {
647 const char *begin
= NULL
;
654 } state
= STATE_OTHER
;
655 _cleanup_(memstream_done
) MemStream m
= {};
656 size_t isz
, shift
[2] = {}, n_carriage_returns
= 0;
662 /* This does three things:
664 * 1. Replaces TABs by 8 spaces
665 * 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences
666 * 3. Strips ANSI operating system sequences (OSC), i.e. ESC ']' … ST sequences
667 * 4. Strip trailing \r characters (since they would "move the cursor", but have no
670 * Everything else will be left as it is. In particular other ANSI sequences are left as they are, as
671 * are any other special characters. Truncated ANSI sequences are left-as is too. This call is
672 * supposed to suppress the most basic formatting noise, but nothing else.
674 * Why care for OSC sequences? Well, to undo what terminal_urlify() and friends generate. */
676 isz
= _isz
? *_isz
: strlen(*ibuf
);
678 /* Note we turn off internal locking on f for performance reasons. It's safe to do so since we
679 * created f here and it doesn't leave our scope. */
680 f
= memstream_init(&m
);
684 for (const char *i
= *ibuf
; i
< *ibuf
+ isz
+ 1; i
++) {
686 bool eot
= i
>= *ibuf
+ isz
;
695 n_carriage_returns
++;
697 } else if (*i
== '\n')
698 /* Ignore carriage returns before new line */
699 n_carriage_returns
= 0;
700 for (; n_carriage_returns
> 0; n_carriage_returns
--)
704 state
= STATE_ESCAPE
;
705 else if (*i
== '\t') {
707 advance_offsets(i
- *ibuf
, highlight
, shift
, 7);
714 assert(n_carriage_returns
== 0);
718 advance_offsets(i
- *ibuf
, highlight
, shift
, 1);
720 } else if (*i
== '[') { /* ANSI CSI */
723 } else if (*i
== ']') { /* ANSI OSC */
729 advance_offsets(i
- *ibuf
, highlight
, shift
, 1);
736 assert(n_carriage_returns
== 0);
738 if (eot
|| !strchr("01234567890;m", *i
)) { /* EOT or invalid chars in sequence */
741 advance_offsets(i
- *ibuf
, highlight
, shift
, 2);
744 } else if (*i
== 'm')
750 assert(n_carriage_returns
== 0);
752 /* There are three kinds of OSC terminators: \x07, \x1b\x5c or \x9c. We only support
753 * the first two, because the last one is a valid UTF-8 codepoint and hence creates
754 * an ambiguity (many Terminal emulators refuse to support it as well). */
755 if (eot
|| (!IN_SET(*i
, '\x07', '\x1b') && !osc_char_is_valid(*i
))) { /* EOT or invalid chars in sequence */
758 advance_offsets(i
- *ibuf
, highlight
, shift
, 2);
761 } else if (*i
== '\x07') /* Single character ST */
763 else if (*i
== '\x1B')
764 state
= STATE_OSC_CLOSING
;
768 case STATE_OSC_CLOSING
:
769 if (eot
|| *i
!= '\x5c') { /* EOT or incomplete two-byte ST in sequence */
772 advance_offsets(i
- *ibuf
, highlight
, shift
, 2);
775 } else if (*i
== '\x5c')
783 if (memstream_finalize(&m
, &obuf
, _isz
) < 0)
786 free_and_replace(*ibuf
, obuf
);
789 highlight
[0] += shift
[0];
790 highlight
[1] += shift
[1];
796 char* strextend_with_separator_internal(char **x
, const char *separator
, ...) {
797 _cleanup_free_
char *buffer
= NULL
;
798 size_t f
, l
, l_separator
;
806 l
= f
= strlen_ptr(*x
);
808 need_separator
= !isempty(*x
);
809 l_separator
= strlen_ptr(separator
);
811 va_start(ap
, separator
);
812 for (const char *t
;;) {
815 t
= va_arg(ap
, const char *);
818 if (t
== POINTER_MAX
)
826 if (n
>= SIZE_MAX
- l
) {
832 need_separator
= true;
836 need_separator
= !isempty(*x
);
838 nr
= realloc(*x
, GREEDY_ALLOC_ROUND_UP(l
+1));
845 va_start(ap
, separator
);
849 t
= va_arg(ap
, const char *);
852 if (t
== POINTER_MAX
)
855 if (need_separator
&& separator
)
856 p
= stpcpy(p
, separator
);
860 need_separator
= true;
867 /* If no buffer to extend was passed in return the start of the buffer */
869 return TAKE_PTR(buffer
);
871 /* Otherwise we extended the buffer: return the end */
875 int strextendf_with_separator(char **x
, const char *separator
, const char *format
, ...) {
876 size_t m
, a
, l_separator
;
880 /* Appends a formatted string to the specified string. Don't use this in inner loops, since then
881 * we'll spend a tonload of time in determining the length of the string passed in, over and over
887 l_separator
= isempty(*x
) ? 0 : strlen_ptr(separator
);
889 /* Let's try to use the allocated buffer, if there's room at the end still. Otherwise let's extend by 64 chars. */
892 a
= MALLOC_SIZEOF_SAFE(*x
);
897 if (a
- m
< 17 + l_separator
) { /* if there's less than 16 chars space, then enlarge the buffer first */
900 if (_unlikely_(l_separator
> SIZE_MAX
- 64)) /* overflow check #1 */
902 if (_unlikely_(m
> SIZE_MAX
- 64 - l_separator
)) /* overflow check #2 */
905 n
= realloc(*x
, m
+ 64 + l_separator
);
910 a
= MALLOC_SIZEOF_SAFE(*x
);
913 /* Now, let's try to format the string into it */
914 memcpy_safe(*x
+ m
, separator
, l_separator
);
915 va_start(ap
, format
);
916 l
= vsnprintf(*x
+ m
+ l_separator
, a
- m
- l_separator
, format
, ap
);
921 if ((size_t) l
< a
- m
- l_separator
) {
924 /* Nice! This worked. We are done. But first, let's return the extra space we don't
925 * need. This should be a cheap operation, since we only lower the allocation size here,
927 n
= realloc(*x
, m
+ (size_t) l
+ l_separator
+ 1);
933 /* Wasn't enough. Then let's allocate exactly what we need. */
935 if (_unlikely_((size_t) l
> SIZE_MAX
- (l_separator
+ 1))) /* overflow check #1 */
937 if (_unlikely_(m
> SIZE_MAX
- ((size_t) l
+ l_separator
+ 1))) /* overflow check #2 */
940 a
= m
+ (size_t) l
+ l_separator
+ 1;
946 va_start(ap
, format
);
947 l
= vsnprintf(*x
+ m
+ l_separator
, a
- m
- l_separator
, format
, ap
);
950 assert((size_t) l
< a
- m
- l_separator
);
956 /* truncate the bytes added after memcpy_safe() again */
961 char* strrep(const char *s
, unsigned n
) {
968 p
= r
= malloc(l
* n
+ 1);
972 for (unsigned i
= 0; i
< n
; i
++)
979 int split_pair(const char *s
, const char *sep
, char **ret_first
, char **ret_second
) {
981 assert(!isempty(sep
));
985 const char *x
= strstr(s
, sep
);
989 _cleanup_free_
char *a
= strndup(s
, x
- s
);
993 _cleanup_free_
char *b
= strdup(x
+ strlen(sep
));
997 *ret_first
= TAKE_PTR(a
);
998 *ret_second
= TAKE_PTR(b
);
1002 int free_and_strdup(char **p
, const char *s
) {
1007 /* Replaces a string pointer with a strdup()ed new string,
1008 * possibly freeing the old one. */
1010 if (streq_ptr(*p
, s
))
1020 free_and_replace(*p
, t
);
1025 int free_and_strdup_warn(char **p
, const char *s
) {
1028 r
= free_and_strdup(p
, s
);
1034 int free_and_strndup(char **p
, const char *s
, size_t l
) {
1038 assert(s
|| l
== 0);
1040 /* Replaces a string pointer with a strndup()ed new string,
1041 * freeing the old one. */
1046 if (*p
&& s
&& strneq(*p
, s
, l
) && (l
> strlen(*p
) || (*p
)[l
] == '\0'))
1056 free_and_replace(*p
, t
);
1060 int strdup_to_full(char **ret
, const char *src
) {
1068 char *t
= strdup(src
);
1078 bool string_is_safe(const char *p
) {
1082 /* Checks if the specified string contains no quotes or control characters */
1084 for (const char *t
= p
; *t
; t
++) {
1085 if (*t
> 0 && *t
< ' ') /* no control characters */
1088 if (strchr(QUOTES
"\\\x7f", *t
))
1095 bool string_is_safe_ascii(const char *p
) {
1096 return ascii_is_valid(p
) && string_is_safe(p
);
1099 char* str_realloc(char *p
) {
1100 /* Reallocate *p to actual size. Ignore failure, and return the original string on error. */
1105 return realloc(p
, strlen(p
) + 1) ?: p
;
1108 char* string_erase(char *x
) {
1112 /* A delicious drop of snake-oil! To be called on memory where we stored passphrases or so, after we
1114 explicit_bzero_safe(x
, strlen(x
));
1118 int string_truncate_lines(const char *s
, size_t n_lines
, char **ret
) {
1119 const char *p
= s
, *e
= s
;
1120 bool truncation_applied
= false;
1126 /* Truncate after the specified number of lines. Returns > 0 if a truncation was applied or == 0 if
1127 * there were fewer lines in the string anyway. Trailing newlines on input are ignored, and not
1128 * generated either. */
1133 k
= strcspn(p
, "\n");
1136 if (k
== 0) /* final empty line */
1139 if (n
>= n_lines
) /* above threshold */
1142 e
= p
+ k
; /* last line to include */
1146 assert(p
[k
] == '\n');
1158 /* e points after the last character we want to keep */
1162 if (!in_charset(e
, "\n")) /* We only consider things truncated if we remove something that
1163 * isn't a new-line or a series of them */
1164 truncation_applied
= true;
1166 copy
= strndup(s
, e
- s
);
1172 return truncation_applied
;
1175 int string_extract_line(const char *s
, size_t i
, char **ret
) {
1179 /* Extract the i'nth line from the specified string. Returns > 0 if there are more lines after that,
1180 * and == 0 if we are looking at the last line or already beyond the last line. As special
1181 * optimization, if the first line is requested and the string only consists of one line we return
1182 * NULL, indicating the input string should be used as is, and avoid a memory allocation for a very
1188 q
= strchr(p
, '\n');
1190 /* The line we are looking for! */
1195 m
= strndup(p
, q
- p
);
1200 return !isempty(q
+ 1); /* More coming? */
1202 /* Tell the caller to use the input string if equal */
1203 return strdup_to(ret
, p
!= s
? p
: NULL
);
1207 /* No more lines, return empty line */
1208 return strdup_to(ret
, "");
1215 int string_contains_word_strv(const char *string
, const char *separators
, char * const *words
, const char **ret_word
) {
1216 /* In the default mode with no separators specified, we split on whitespace and coalesce separators. */
1217 const ExtractFlags flags
= separators
? EXTRACT_DONT_COALESCE_SEPARATORS
: 0;
1218 const char *found
= NULL
;
1222 _cleanup_free_
char *w
= NULL
;
1224 r
= extract_first_word(&string
, &w
, separators
, flags
);
1230 found
= strv_find(words
, w
);
1240 bool streq_skip_trailing_chars(const char *s1
, const char *s2
, const char *ok
) {
1249 for (; *s1
&& *s2
; s1
++, s2
++)
1253 return in_charset(s1
, ok
) && in_charset(s2
, ok
);
1256 char* string_replace_char(char *str
, char old_char
, char new_char
) {
1258 assert(old_char
!= '\0');
1259 assert(new_char
!= '\0');
1260 assert(old_char
!= new_char
);
1262 for (char *p
= strchr(str
, old_char
); p
; p
= strchr(p
+ 1, old_char
))
1268 int make_cstring(const char *s
, size_t n
, MakeCStringMode mode
, char **ret
) {
1271 assert(s
|| n
== 0);
1273 assert(mode
< _MAKE_CSTRING_MODE_MAX
);
1275 /* Converts a sized character buffer into a NUL-terminated NUL string, refusing if there are embedded
1276 * NUL bytes. Whether to expect a trailing NUL byte can be specified via 'mode' */
1279 if (mode
== MAKE_CSTRING_REQUIRE_TRAILING_NUL
)
1289 nul
= memchr(s
, 0, n
);
1291 if (nul
< s
+ n
- 1 || /* embedded NUL? */
1292 mode
== MAKE_CSTRING_REFUSE_TRAILING_NUL
)
1296 } else if (mode
== MAKE_CSTRING_REQUIRE_TRAILING_NUL
)
1302 b
= memdup_suffix0(s
, n
);
1311 size_t strspn_from_end(const char *str
, const char *accept
) {
1317 if (isempty(accept
))
1320 for (const char *p
= str
+ strlen(str
); p
> str
&& strchr(accept
, p
[-1]); p
--)
1326 char* strdupspn(const char *a
, const char *accept
) {
1327 if (isempty(a
) || isempty(accept
))
1330 return strndup(a
, strspn(a
, accept
));
1333 char* strdupcspn(const char *a
, const char *reject
) {
1336 if (isempty(reject
))
1339 return strndup(a
, strcspn(a
, reject
));
1342 char* find_line_startswith(const char *haystack
, const char *needle
) {
1348 /* Finds the first line in 'haystack' that starts with the specified string. Returns a pointer to the
1349 * first character after it */
1351 p
= strstr(haystack
, needle
);
1356 while (p
[-1] != '\n') {
1357 p
= strstr(p
+ 1, needle
);
1362 return p
+ strlen(needle
);
1365 char* find_line(const char *haystack
, const char *needle
) {
1371 /* Finds the first line in 'haystack' that match the specified string. Returns a pointer to the
1372 * beginning of the line */
1374 p
= find_line_startswith(haystack
, needle
);
1378 if (*p
== 0 || strchr(NEWLINE
, *p
))
1379 return p
- strlen(needle
);
1384 char* find_line_after(const char *haystack
, const char *needle
) {
1390 /* Finds the first line in 'haystack' that match the specified string. Returns a pointer to the
1391 * next line after it */
1393 p
= find_line_startswith(haystack
, needle
);
1399 if (strchr(NEWLINE
, *p
))
1405 bool version_is_valid(const char *s
) {
1409 if (!filename_part_is_valid(s
))
1412 /* This is a superset of the characters used by semver. We additionally allow "," and "_". */
1413 if (!in_charset(s
, ALPHANUMERICAL
".,_-+"))
1419 bool version_is_valid_versionspec(const char *s
) {
1420 if (!filename_part_is_valid(s
))
1423 if (!in_charset(s
, ALPHANUMERICAL
"-.~^"))
1429 ssize_t
strlevenshtein(const char *x
, const char *y
) {
1430 _cleanup_free_
size_t *t0
= NULL
, *t1
= NULL
, *t2
= NULL
;
1433 /* This is inspired from the Linux kernel's Levenshtein implementation */
1435 if (streq_ptr(x
, y
))
1451 t0
= new0(size_t, yl
+ 1);
1454 t1
= new0(size_t, yl
+ 1);
1457 t2
= new0(size_t, yl
+ 1);
1461 for (size_t i
= 0; i
<= yl
; i
++)
1464 for (size_t i
= 0; i
< xl
; i
++) {
1467 for (size_t j
= 0; j
< yl
; j
++) {
1469 t2
[j
+1] = t1
[j
] + (x
[i
] != y
[j
]);
1472 if (i
> 0 && j
> 0 && x
[i
-1] == y
[j
] && x
[i
] == y
[j
-1] && t2
[j
+1] > t0
[j
-1] + 1)
1473 t2
[j
+1] = t0
[j
-1] + 1;
1476 if (t2
[j
+1] > t1
[j
+1] + 1)
1477 t2
[j
+1] = t1
[j
+1] + 1;
1480 if (t2
[j
+1] > t2
[j
] + 1)
1481 t2
[j
+1] = t2
[j
] + 1;
1493 char* strrstr(const char *haystack
, const char *needle
) {
1494 /* Like strstr() but returns the last rather than the first occurrence of "needle" in "haystack". */
1496 if (!haystack
|| !needle
)
1499 /* Special case: for the empty string we return the very last possible occurrence, i.e. *after* the
1500 * last char, not before. */
1502 return strchr(haystack
, 0);
1504 for (const char *p
= strstr(haystack
, needle
), *q
; p
; p
= q
) {
1505 q
= strstr(p
+ 1, needle
);
1512 size_t str_common_prefix(const char *a
, const char *b
) {
1516 /* Returns the length of the common prefix of the two specified strings, or SIZE_MAX in case the
1517 * strings are fully identical. */
1519 for (size_t n
= 0;; n
++) {