]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/string-util.c
2 This file is part of systemd.
4 Copyright 2010 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include "alloc-util.h"
30 #include "string-util.h"
34 int strcmp_ptr(const char *a
, const char *b
) {
36 /* Like strcmp(), but tries to make sense of NULL pointers */
49 char* endswith(const char *s
, const char *postfix
) {
59 return (char*) s
+ sl
;
64 if (memcmp(s
+ sl
- pl
, postfix
, pl
) != 0)
67 return (char*) s
+ sl
- pl
;
70 char* endswith_no_case(const char *s
, const char *postfix
) {
80 return (char*) s
+ sl
;
85 if (strcasecmp(s
+ sl
- pl
, postfix
) != 0)
88 return (char*) s
+ sl
- pl
;
91 char* first_word(const char *s
, const char *word
) {
98 /* Checks if the string starts with the specified word, either
99 * followed by NUL or by whitespace. Returns a pointer to the
100 * NUL or the first character after the whitespace. */
111 if (memcmp(s
, word
, wl
) != 0)
118 if (!strchr(WHITESPACE
, *p
))
121 p
+= strspn(p
, WHITESPACE
);
125 static size_t strcspn_escaped(const char *s
, const char *reject
) {
126 bool escaped
= false;
129 for (n
=0; s
[n
]; n
++) {
132 else if (s
[n
] == '\\')
134 else if (strchr(reject
, s
[n
]))
138 /* if s ends in \, return index of previous char */
142 /* Split a string into words. */
143 const char* split(const char **state
, size_t *l
, const char *separator
, bool quoted
) {
149 assert(**state
== '\0');
153 current
+= strspn(current
, separator
);
159 if (quoted
&& strchr("\'\"", *current
)) {
160 char quotechars
[2] = {*current
, '\0'};
162 *l
= strcspn_escaped(current
+ 1, quotechars
);
163 if (current
[*l
+ 1] == '\0' || current
[*l
+ 1] != quotechars
[0] ||
164 (current
[*l
+ 2] && !strchr(separator
, current
[*l
+ 2]))) {
165 /* right quote missing or garbage at the end */
169 *state
= current
++ + *l
+ 2;
171 *l
= strcspn_escaped(current
, separator
);
172 if (current
[*l
] && !strchr(separator
, current
[*l
])) {
173 /* unfinished escape */
177 *state
= current
+ *l
;
179 *l
= strcspn(current
, separator
);
180 *state
= current
+ *l
;
186 char *strnappend(const char *s
, const char *suffix
, size_t b
) {
194 return strndup(suffix
, b
);
203 if (b
> ((size_t) -1) - a
)
206 r
= new(char, a
+b
+1);
211 memcpy(r
+a
, suffix
, b
);
217 char *strappend(const char *s
, const char *suffix
) {
218 return strnappend(s
, suffix
, strlen_ptr(suffix
));
221 char *strjoin_real(const char *x
, ...) {
235 t
= va_arg(ap
, const char *);
240 if (n
> ((size_t) -1) - l
) {
264 t
= va_arg(ap
, const char *);
278 char *strstrip(char *s
) {
284 /* Drops trailing whitespace. Modifies the string in
285 * place. Returns pointer to first non-space character */
287 s
+= strspn(s
, WHITESPACE
);
289 for (e
= strchr(s
, 0); e
> s
; e
--)
290 if (!strchr(WHITESPACE
, e
[-1]))
298 char *delete_chars(char *s
, const char *bad
) {
301 /* Drops all specified bad characters, regardless where in the string */
309 for (f
= s
, t
= s
; *f
; f
++) {
321 char *delete_trailing_chars(char *s
, const char *bad
) {
324 /* Drops all specified bad characters, at the end of the string */
333 if (!strchr(bad
, *p
))
341 char *truncate_nl(char *s
) {
344 s
[strcspn(s
, NEWLINE
)] = 0;
348 char ascii_tolower(char x
) {
350 if (x
>= 'A' && x
<= 'Z')
351 return x
- 'A' + 'a';
356 char ascii_toupper(char x
) {
358 if (x
>= 'a' && x
<= 'z')
359 return x
- 'a' + 'A';
364 char *ascii_strlower(char *t
) {
370 *p
= ascii_tolower(*p
);
375 char *ascii_strupper(char *t
) {
381 *p
= ascii_toupper(*p
);
386 char *ascii_strlower_n(char *t
, size_t n
) {
392 for (i
= 0; i
< n
; i
++)
393 t
[i
] = ascii_tolower(t
[i
]);
398 int ascii_strcasecmp_n(const char *a
, const char *b
, size_t n
) {
400 for (; n
> 0; a
++, b
++, n
--) {
403 x
= (int) (uint8_t) ascii_tolower(*a
);
404 y
= (int) (uint8_t) ascii_tolower(*b
);
413 int ascii_strcasecmp_nn(const char *a
, size_t n
, const char *b
, size_t m
) {
416 r
= ascii_strcasecmp_n(a
, b
, MIN(n
, m
));
428 bool chars_intersect(const char *a
, const char *b
) {
431 /* Returns true if any of the chars in a are in b. */
439 bool string_has_cc(const char *p
, const char *ok
) {
445 * Check if a string contains control characters. If 'ok' is
446 * non-NULL it may be a string containing additional CCs to be
450 for (t
= p
; *t
; t
++) {
451 if (ok
&& strchr(ok
, *t
))
454 if (*t
> 0 && *t
< ' ')
464 static char *ascii_ellipsize_mem(const char *s
, size_t old_length
, size_t new_length
, unsigned percent
) {
469 assert(percent
<= 100);
470 assert(new_length
>= 3);
472 if (old_length
<= 3 || old_length
<= new_length
)
473 return strndup(s
, old_length
);
475 r
= new0(char, new_length
+3);
479 x
= (new_length
* percent
) / 100;
481 if (x
> new_length
- 3)
485 r
[x
] = 0xe2; /* tri-dot ellipsis: … */
489 s
+ old_length
- (new_length
- x
- 1),
495 char *ellipsize_mem(const char *s
, size_t old_length
, size_t new_length
, unsigned percent
) {
499 unsigned k
, len
, len2
;
503 assert(percent
<= 100);
505 if (new_length
== (size_t) -1)
506 return strndup(s
, old_length
);
508 assert(new_length
>= 3);
510 /* if no multibyte characters use ascii_ellipsize_mem for speed */
511 if (ascii_is_valid(s
))
512 return ascii_ellipsize_mem(s
, old_length
, new_length
, percent
);
514 if (old_length
<= 3 || old_length
<= new_length
)
515 return strndup(s
, old_length
);
517 x
= (new_length
* percent
) / 100;
519 if (x
> new_length
- 3)
523 for (i
= s
; k
< x
&& i
< s
+ old_length
; i
= utf8_next_char(i
)) {
526 r
= utf8_encoded_to_unichar(i
, &c
);
529 k
+= unichar_iswide(c
) ? 2 : 1;
532 if (k
> x
) /* last character was wide and went over quota */
535 for (j
= s
+ old_length
; k
< new_length
&& j
> i
; ) {
538 j
= utf8_prev_char(j
);
539 r
= utf8_encoded_to_unichar(j
, &c
);
542 k
+= unichar_iswide(c
) ? 2 : 1;
546 /* we don't actually need to ellipsize */
548 return memdup(s
, old_length
+ 1);
550 /* make space for ellipsis */
551 j
= utf8_next_char(j
);
554 len2
= s
+ old_length
- j
;
555 e
= new(char, len
+ 3 + len2
+ 1);
560 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
561 old_length, new_length, x, len, len2, k);
565 e
[len
] = 0xe2; /* tri-dot ellipsis: … */
569 memcpy(e
+ len
+ 3, j
, len2
+ 1);
574 char *ellipsize(const char *s
, size_t length
, unsigned percent
) {
576 if (length
== (size_t) -1)
579 return ellipsize_mem(s
, strlen(s
), length
, percent
);
582 bool nulstr_contains(const char *nulstr
, const char *needle
) {
588 NULSTR_FOREACH(i
, nulstr
)
589 if (streq(i
, needle
))
595 char* strshorten(char *s
, size_t l
) {
598 if (strnlen(s
, l
+1) > l
)
604 char *strreplace(const char *text
, const char *old_string
, const char *new_string
) {
607 size_t l
, old_len
, new_len
;
613 old_len
= strlen(old_string
);
614 new_len
= strlen(new_string
);
627 if (!startswith(f
, old_string
)) {
633 nl
= l
- old_len
+ new_len
;
634 a
= realloc(r
, nl
+ 1);
642 t
= stpcpy(t
, new_string
);
653 char *strip_tab_ansi(char **ibuf
, size_t *_isz
) {
654 const char *i
, *begin
= NULL
;
659 } state
= STATE_OTHER
;
667 /* Strips ANSI color and replaces TABs by 8 spaces */
669 isz
= _isz
? *_isz
: strlen(*ibuf
);
671 f
= open_memstream(&obuf
, &osz
);
675 /* Note we use the _unlocked() stdio variants on f for performance
676 * reasons. It's safe to do so since we created f here and it
677 * doesn't leave our scope.
680 for (i
= *ibuf
; i
< *ibuf
+ isz
+ 1; i
++) {
685 if (i
>= *ibuf
+ isz
) /* EOT */
687 else if (*i
== '\x1B')
688 state
= STATE_ESCAPE
;
690 fputs_unlocked(" ", f
);
692 fputc_unlocked(*i
, f
);
696 if (i
>= *ibuf
+ isz
) { /* EOT */
697 fputc_unlocked('\x1B', f
);
699 } else if (*i
== '[') {
700 state
= STATE_BRACKET
;
703 fputc_unlocked('\x1B', f
);
704 fputc_unlocked(*i
, f
);
712 if (i
>= *ibuf
+ isz
|| /* EOT */
713 (!(*i
>= '0' && *i
<= '9') && !IN_SET(*i
, ';', 'm'))) {
714 fputc_unlocked('\x1B', f
);
715 fputc_unlocked('[', f
);
718 } else if (*i
== 'm')
740 char *strextend(char **x
, ...) {
747 l
= f
= strlen_ptr(*x
);
754 t
= va_arg(ap
, const char *);
759 if (n
> ((size_t) -1) - l
) {
768 r
= realloc(*x
, l
+1);
778 t
= va_arg(ap
, const char *);
792 char *strrep(const char *s
, unsigned n
) {
800 p
= r
= malloc(l
* n
+ 1);
804 for (i
= 0; i
< n
; i
++)
811 int split_pair(const char *s
, const char *sep
, char **l
, char **r
) {
826 a
= strndup(s
, x
- s
);
830 b
= strdup(x
+ strlen(sep
));
842 int free_and_strdup(char **p
, const char *s
) {
847 /* Replaces a string pointer with an strdup()ed new string,
848 * possibly freeing the old one. */
850 if (streq_ptr(*p
, s
))
866 #if !HAVE_EXPLICIT_BZERO
868 * Pointer to memset is volatile so that compiler must de-reference
869 * the pointer and can't assume that it points to any function in
870 * particular (such as memset, which it then might further "optimize")
871 * This approach is inspired by openssl's crypto/mem_clr.c.
873 typedef void *(*memset_t
)(void *,int,size_t);
875 static volatile memset_t memset_func
= memset
;
877 void explicit_bzero(void *p
, size_t l
) {
878 memset_func(p
, '\0', l
);
882 char* string_erase(char *x
) {
886 /* A delicious drop of snake-oil! To be called on memory where
887 * we stored passphrases or so, after we used them. */
888 explicit_bzero(x
, strlen(x
));
892 char *string_free_erase(char *s
) {
893 return mfree(string_erase(s
));
896 bool string_is_safe(const char *p
) {
902 for (t
= p
; *t
; t
++) {
903 if (*t
> 0 && *t
< ' ') /* no control characters */
906 if (strchr(QUOTES
"\\\x7f", *t
))