]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/string-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include <stdio_ext.h>
29 #include "alloc-util.h"
32 #include "string-util.h"
33 #include "terminal-util.h"
37 int strcmp_ptr(const char *a
, const char *b
) {
39 /* Like strcmp(), but tries to make sense of NULL pointers */
52 char* endswith(const char *s
, const char *postfix
) {
62 return (char*) s
+ sl
;
67 if (memcmp(s
+ sl
- pl
, postfix
, pl
) != 0)
70 return (char*) s
+ sl
- pl
;
73 char* endswith_no_case(const char *s
, const char *postfix
) {
83 return (char*) s
+ sl
;
88 if (strcasecmp(s
+ sl
- pl
, postfix
) != 0)
91 return (char*) s
+ sl
- pl
;
94 char* first_word(const char *s
, const char *word
) {
101 /* Checks if the string starts with the specified word, either
102 * followed by NUL or by whitespace. Returns a pointer to the
103 * NUL or the first character after the whitespace. */
114 if (memcmp(s
, word
, wl
) != 0)
121 if (!strchr(WHITESPACE
, *p
))
124 p
+= strspn(p
, WHITESPACE
);
128 static size_t strcspn_escaped(const char *s
, const char *reject
) {
129 bool escaped
= false;
132 for (n
=0; s
[n
]; n
++) {
135 else if (s
[n
] == '\\')
137 else if (strchr(reject
, s
[n
]))
141 /* if s ends in \, return index of previous char */
145 /* Split a string into words. */
146 const char* split(const char **state
, size_t *l
, const char *separator
, bool quoted
) {
152 assert(**state
== '\0');
156 current
+= strspn(current
, separator
);
162 if (quoted
&& strchr("\'\"", *current
)) {
163 char quotechars
[2] = {*current
, '\0'};
165 *l
= strcspn_escaped(current
+ 1, quotechars
);
166 if (current
[*l
+ 1] == '\0' || current
[*l
+ 1] != quotechars
[0] ||
167 (current
[*l
+ 2] && !strchr(separator
, current
[*l
+ 2]))) {
168 /* right quote missing or garbage at the end */
172 *state
= current
++ + *l
+ 2;
174 *l
= strcspn_escaped(current
, separator
);
175 if (current
[*l
] && !strchr(separator
, current
[*l
])) {
176 /* unfinished escape */
180 *state
= current
+ *l
;
182 *l
= strcspn(current
, separator
);
183 *state
= current
+ *l
;
189 char *strnappend(const char *s
, const char *suffix
, size_t b
) {
197 return strndup(suffix
, b
);
206 if (b
> ((size_t) -1) - a
)
209 r
= new(char, a
+b
+1);
214 memcpy(r
+a
, suffix
, b
);
220 char *strappend(const char *s
, const char *suffix
) {
221 return strnappend(s
, suffix
, strlen_ptr(suffix
));
224 char *strjoin_real(const char *x
, ...) {
238 t
= va_arg(ap
, const char *);
243 if (n
> ((size_t) -1) - l
) {
267 t
= va_arg(ap
, const char *);
281 char *strstrip(char *s
) {
287 /* Drops trailing whitespace. Modifies the string in
288 * place. Returns pointer to first non-space character */
290 s
+= strspn(s
, WHITESPACE
);
292 for (e
= strchr(s
, 0); e
> s
; e
--)
293 if (!strchr(WHITESPACE
, e
[-1]))
301 char *delete_chars(char *s
, const char *bad
) {
304 /* Drops all specified bad characters, regardless where in the string */
312 for (f
= s
, t
= s
; *f
; f
++) {
324 char *delete_trailing_chars(char *s
, const char *bad
) {
327 /* Drops all specified bad characters, at the end of the string */
336 if (!strchr(bad
, *p
))
344 char *truncate_nl(char *s
) {
347 s
[strcspn(s
, NEWLINE
)] = 0;
351 char ascii_tolower(char x
) {
353 if (x
>= 'A' && x
<= 'Z')
354 return x
- 'A' + 'a';
359 char ascii_toupper(char x
) {
361 if (x
>= 'a' && x
<= 'z')
362 return x
- 'a' + 'A';
367 char *ascii_strlower(char *t
) {
373 *p
= ascii_tolower(*p
);
378 char *ascii_strupper(char *t
) {
384 *p
= ascii_toupper(*p
);
389 char *ascii_strlower_n(char *t
, size_t n
) {
395 for (i
= 0; i
< n
; i
++)
396 t
[i
] = ascii_tolower(t
[i
]);
401 int ascii_strcasecmp_n(const char *a
, const char *b
, size_t n
) {
403 for (; n
> 0; a
++, b
++, n
--) {
406 x
= (int) (uint8_t) ascii_tolower(*a
);
407 y
= (int) (uint8_t) ascii_tolower(*b
);
416 int ascii_strcasecmp_nn(const char *a
, size_t n
, const char *b
, size_t m
) {
419 r
= ascii_strcasecmp_n(a
, b
, MIN(n
, m
));
431 bool chars_intersect(const char *a
, const char *b
) {
434 /* Returns true if any of the chars in a are in b. */
442 bool string_has_cc(const char *p
, const char *ok
) {
448 * Check if a string contains control characters. If 'ok' is
449 * non-NULL it may be a string containing additional CCs to be
453 for (t
= p
; *t
; t
++) {
454 if (ok
&& strchr(ok
, *t
))
457 if (*t
> 0 && *t
< ' ')
467 static char *ascii_ellipsize_mem(const char *s
, size_t old_length
, size_t new_length
, unsigned percent
) {
472 assert(percent
<= 100);
473 assert(new_length
>= 3);
475 if (old_length
<= 3 || old_length
<= new_length
)
476 return strndup(s
, old_length
);
478 r
= new0(char, new_length
+3);
482 x
= (new_length
* percent
) / 100;
484 if (x
> new_length
- 3)
488 r
[x
] = 0xe2; /* tri-dot ellipsis: … */
492 s
+ old_length
- (new_length
- x
- 1),
498 char *ellipsize_mem(const char *s
, size_t old_length
, size_t new_length
, unsigned percent
) {
502 unsigned k
, len
, len2
;
506 assert(percent
<= 100);
508 if (new_length
== (size_t) -1)
509 return strndup(s
, old_length
);
511 assert(new_length
>= 3);
513 /* if no multibyte characters use ascii_ellipsize_mem for speed */
514 if (ascii_is_valid(s
))
515 return ascii_ellipsize_mem(s
, old_length
, new_length
, percent
);
517 if (old_length
<= 3 || old_length
<= new_length
)
518 return strndup(s
, old_length
);
520 x
= (new_length
* percent
) / 100;
522 if (x
> new_length
- 3)
526 for (i
= s
; k
< x
&& i
< s
+ old_length
; i
= utf8_next_char(i
)) {
529 r
= utf8_encoded_to_unichar(i
, &c
);
532 k
+= unichar_iswide(c
) ? 2 : 1;
535 if (k
> x
) /* last character was wide and went over quota */
538 for (j
= s
+ old_length
; k
< new_length
&& j
> i
; ) {
541 j
= utf8_prev_char(j
);
542 r
= utf8_encoded_to_unichar(j
, &c
);
545 k
+= unichar_iswide(c
) ? 2 : 1;
549 /* we don't actually need to ellipsize */
551 return memdup(s
, old_length
+ 1);
553 /* make space for ellipsis */
554 j
= utf8_next_char(j
);
557 len2
= s
+ old_length
- j
;
558 e
= new(char, len
+ 3 + len2
+ 1);
563 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
564 old_length, new_length, x, len, len2, k);
568 e
[len
] = 0xe2; /* tri-dot ellipsis: … */
572 memcpy(e
+ len
+ 3, j
, len2
+ 1);
577 char *ellipsize(const char *s
, size_t length
, unsigned percent
) {
579 if (length
== (size_t) -1)
582 return ellipsize_mem(s
, strlen(s
), length
, percent
);
585 bool nulstr_contains(const char *nulstr
, const char *needle
) {
591 NULSTR_FOREACH(i
, nulstr
)
592 if (streq(i
, needle
))
598 char* strshorten(char *s
, size_t l
) {
601 if (strnlen(s
, l
+1) > l
)
607 char *strreplace(const char *text
, const char *old_string
, const char *new_string
) {
608 size_t l
, old_len
, new_len
, allocated
= 0;
609 char *t
, *ret
= NULL
;
618 old_len
= strlen(old_string
);
619 new_len
= strlen(new_string
);
622 if (!GREEDY_REALLOC(ret
, allocated
, l
+1))
630 if (!startswith(f
, old_string
)) {
636 nl
= l
- old_len
+ new_len
;
638 if (!GREEDY_REALLOC(ret
, allocated
, nl
+ 1))
644 t
= stpcpy(t
, new_string
);
652 static void advance_offsets(ssize_t diff
, size_t offsets
[2], size_t shift
[2], size_t size
) {
656 if ((size_t) diff
< offsets
[0])
658 if ((size_t) diff
< offsets
[1])
662 char *strip_tab_ansi(char **ibuf
, size_t *_isz
, size_t highlight
[2]) {
663 const char *i
, *begin
= NULL
;
668 } state
= STATE_OTHER
;
670 size_t osz
= 0, isz
, shift
[2] = {};
676 /* Strips ANSI color and replaces TABs by 8 spaces */
678 isz
= _isz
? *_isz
: strlen(*ibuf
);
680 f
= open_memstream(&obuf
, &osz
);
684 /* Note we turn off internal locking on f for performance reasons. It's safe to do so since we created f here
685 * and it doesn't leave our scope. */
687 (void) __fsetlocking(f
, FSETLOCKING_BYCALLER
);
689 for (i
= *ibuf
; i
< *ibuf
+ isz
+ 1; i
++) {
694 if (i
>= *ibuf
+ isz
) /* EOT */
696 else if (*i
== '\x1B')
697 state
= STATE_ESCAPE
;
698 else if (*i
== '\t') {
700 advance_offsets(i
- *ibuf
, highlight
, shift
, 7);
707 if (i
>= *ibuf
+ isz
) { /* EOT */
709 advance_offsets(i
- *ibuf
, highlight
, shift
, 1);
711 } else if (*i
== '[') {
712 state
= STATE_BRACKET
;
717 advance_offsets(i
- *ibuf
, highlight
, shift
, 1);
725 if (i
>= *ibuf
+ isz
|| /* EOT */
726 (!(*i
>= '0' && *i
<= '9') && !IN_SET(*i
, ';', 'm'))) {
729 advance_offsets(i
- *ibuf
, highlight
, shift
, 2);
732 } else if (*i
== 'm')
752 highlight
[0] += shift
[0];
753 highlight
[1] += shift
[1];
759 char *strextend_with_separator(char **x
, const char *separator
, ...) {
761 size_t f
, l
, l_separator
;
767 l
= f
= strlen_ptr(*x
);
769 need_separator
= !isempty(*x
);
770 l_separator
= strlen_ptr(separator
);
772 va_start(ap
, separator
);
777 t
= va_arg(ap
, const char *);
786 if (n
> ((size_t) -1) - l
) {
792 need_separator
= true;
796 need_separator
= !isempty(*x
);
798 r
= realloc(*x
, l
+1);
804 va_start(ap
, separator
);
808 t
= va_arg(ap
, const char *);
812 if (need_separator
&& separator
)
813 p
= stpcpy(p
, separator
);
817 need_separator
= true;
829 char *strrep(const char *s
, unsigned n
) {
837 p
= r
= malloc(l
* n
+ 1);
841 for (i
= 0; i
< n
; i
++)
848 int split_pair(const char *s
, const char *sep
, char **l
, char **r
) {
863 a
= strndup(s
, x
- s
);
867 b
= strdup(x
+ strlen(sep
));
879 int free_and_strdup(char **p
, const char *s
) {
884 /* Replaces a string pointer with an strdup()ed new string,
885 * possibly freeing the old one. */
887 if (streq_ptr(*p
, s
))
903 #if !HAVE_EXPLICIT_BZERO
905 * Pointer to memset is volatile so that compiler must de-reference
906 * the pointer and can't assume that it points to any function in
907 * particular (such as memset, which it then might further "optimize")
908 * This approach is inspired by openssl's crypto/mem_clr.c.
910 typedef void *(*memset_t
)(void *,int,size_t);
912 static volatile memset_t memset_func
= memset
;
914 void explicit_bzero(void *p
, size_t l
) {
915 memset_func(p
, '\0', l
);
919 char* string_erase(char *x
) {
923 /* A delicious drop of snake-oil! To be called on memory where
924 * we stored passphrases or so, after we used them. */
925 explicit_bzero(x
, strlen(x
));
929 char *string_free_erase(char *s
) {
930 return mfree(string_erase(s
));
933 bool string_is_safe(const char *p
) {
939 for (t
= p
; *t
; t
++) {
940 if (*t
> 0 && *t
< ' ') /* no control characters */
943 if (strchr(QUOTES
"\\\x7f", *t
))