]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/string-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include "alloc-util.h"
31 #include "string-util.h"
35 int strcmp_ptr(const char *a
, const char *b
) {
37 /* Like strcmp(), but tries to make sense of NULL pointers */
50 char* endswith(const char *s
, const char *postfix
) {
60 return (char*) s
+ sl
;
65 if (memcmp(s
+ sl
- pl
, postfix
, pl
) != 0)
68 return (char*) s
+ sl
- pl
;
71 char* endswith_no_case(const char *s
, const char *postfix
) {
81 return (char*) s
+ sl
;
86 if (strcasecmp(s
+ sl
- pl
, postfix
) != 0)
89 return (char*) s
+ sl
- pl
;
92 char* first_word(const char *s
, const char *word
) {
99 /* Checks if the string starts with the specified word, either
100 * followed by NUL or by whitespace. Returns a pointer to the
101 * NUL or the first character after the whitespace. */
112 if (memcmp(s
, word
, wl
) != 0)
119 if (!strchr(WHITESPACE
, *p
))
122 p
+= strspn(p
, WHITESPACE
);
126 static size_t strcspn_escaped(const char *s
, const char *reject
) {
127 bool escaped
= false;
130 for (n
=0; s
[n
]; n
++) {
133 else if (s
[n
] == '\\')
135 else if (strchr(reject
, s
[n
]))
139 /* if s ends in \, return index of previous char */
143 /* Split a string into words. */
144 const char* split(const char **state
, size_t *l
, const char *separator
, bool quoted
) {
150 assert(**state
== '\0');
154 current
+= strspn(current
, separator
);
160 if (quoted
&& strchr("\'\"", *current
)) {
161 char quotechars
[2] = {*current
, '\0'};
163 *l
= strcspn_escaped(current
+ 1, quotechars
);
164 if (current
[*l
+ 1] == '\0' || current
[*l
+ 1] != quotechars
[0] ||
165 (current
[*l
+ 2] && !strchr(separator
, current
[*l
+ 2]))) {
166 /* right quote missing or garbage at the end */
170 *state
= current
++ + *l
+ 2;
172 *l
= strcspn_escaped(current
, separator
);
173 if (current
[*l
] && !strchr(separator
, current
[*l
])) {
174 /* unfinished escape */
178 *state
= current
+ *l
;
180 *l
= strcspn(current
, separator
);
181 *state
= current
+ *l
;
187 char *strnappend(const char *s
, const char *suffix
, size_t b
) {
195 return strndup(suffix
, b
);
204 if (b
> ((size_t) -1) - a
)
207 r
= new(char, a
+b
+1);
212 memcpy(r
+a
, suffix
, b
);
218 char *strappend(const char *s
, const char *suffix
) {
219 return strnappend(s
, suffix
, suffix
? strlen(suffix
) : 0);
222 char *strjoin(const char *x
, ...) {
236 t
= va_arg(ap
, const char *);
241 if (n
> ((size_t) -1) - l
) {
265 t
= va_arg(ap
, const char *);
279 char *strstrip(char *s
) {
282 /* Drops trailing whitespace. Modifies the string in
283 * place. Returns pointer to first non-space character */
285 s
+= strspn(s
, WHITESPACE
);
287 for (e
= strchr(s
, 0); e
> s
; e
--)
288 if (!strchr(WHITESPACE
, e
[-1]))
296 char *delete_chars(char *s
, const char *bad
) {
299 /* Drops all whitespace, regardless where in the string */
301 for (f
= s
, t
= s
; *f
; f
++) {
313 char *truncate_nl(char *s
) {
316 s
[strcspn(s
, NEWLINE
)] = 0;
320 char ascii_tolower(char x
) {
322 if (x
>= 'A' && x
<= 'Z')
323 return x
- 'A' + 'a';
328 char *ascii_strlower(char *t
) {
334 *p
= ascii_tolower(*p
);
339 char *ascii_strlower_n(char *t
, size_t n
) {
345 for (i
= 0; i
< n
; i
++)
346 t
[i
] = ascii_tolower(t
[i
]);
351 int ascii_strcasecmp_n(const char *a
, const char *b
, size_t n
) {
353 for (; n
> 0; a
++, b
++, n
--) {
356 x
= (int) (uint8_t) ascii_tolower(*a
);
357 y
= (int) (uint8_t) ascii_tolower(*b
);
366 int ascii_strcasecmp_nn(const char *a
, size_t n
, const char *b
, size_t m
) {
369 r
= ascii_strcasecmp_n(a
, b
, MIN(n
, m
));
381 bool chars_intersect(const char *a
, const char *b
) {
384 /* Returns true if any of the chars in a are in b. */
392 bool string_has_cc(const char *p
, const char *ok
) {
398 * Check if a string contains control characters. If 'ok' is
399 * non-NULL it may be a string containing additional CCs to be
403 for (t
= p
; *t
; t
++) {
404 if (ok
&& strchr(ok
, *t
))
407 if (*t
> 0 && *t
< ' ')
417 static char *ascii_ellipsize_mem(const char *s
, size_t old_length
, size_t new_length
, unsigned percent
) {
422 assert(percent
<= 100);
423 assert(new_length
>= 3);
425 if (old_length
<= 3 || old_length
<= new_length
)
426 return strndup(s
, old_length
);
428 r
= new0(char, new_length
+1);
432 x
= (new_length
* percent
) / 100;
434 if (x
> new_length
- 3)
442 s
+ old_length
- (new_length
- x
- 3),
448 char *ellipsize_mem(const char *s
, size_t old_length
, size_t new_length
, unsigned percent
) {
452 unsigned k
, len
, len2
;
456 assert(percent
<= 100);
457 assert(new_length
>= 3);
459 /* if no multibyte characters use ascii_ellipsize_mem for speed */
460 if (ascii_is_valid(s
))
461 return ascii_ellipsize_mem(s
, old_length
, new_length
, percent
);
463 if (old_length
<= 3 || old_length
<= new_length
)
464 return strndup(s
, old_length
);
466 x
= (new_length
* percent
) / 100;
468 if (x
> new_length
- 3)
472 for (i
= s
; k
< x
&& i
< s
+ old_length
; i
= utf8_next_char(i
)) {
475 r
= utf8_encoded_to_unichar(i
, &c
);
478 k
+= unichar_iswide(c
) ? 2 : 1;
481 if (k
> x
) /* last character was wide and went over quota */
484 for (j
= s
+ old_length
; k
< new_length
&& j
> i
; ) {
487 j
= utf8_prev_char(j
);
488 r
= utf8_encoded_to_unichar(j
, &c
);
491 k
+= unichar_iswide(c
) ? 2 : 1;
495 /* we don't actually need to ellipsize */
497 return memdup(s
, old_length
+ 1);
499 /* make space for ellipsis */
500 j
= utf8_next_char(j
);
503 len2
= s
+ old_length
- j
;
504 e
= new(char, len
+ 3 + len2
+ 1);
509 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
510 old_length, new_length, x, len, len2, k);
514 e
[len
] = 0xe2; /* tri-dot ellipsis: … */
518 memcpy(e
+ len
+ 3, j
, len2
+ 1);
523 char *ellipsize(const char *s
, size_t length
, unsigned percent
) {
524 return ellipsize_mem(s
, strlen(s
), length
, percent
);
527 bool nulstr_contains(const char*nulstr
, const char *needle
) {
533 NULSTR_FOREACH(i
, nulstr
)
534 if (streq(i
, needle
))
540 char* strshorten(char *s
, size_t l
) {
549 char *strreplace(const char *text
, const char *old_string
, const char *new_string
) {
552 size_t l
, old_len
, new_len
;
558 old_len
= strlen(old_string
);
559 new_len
= strlen(new_string
);
572 if (!startswith(f
, old_string
)) {
578 nl
= l
- old_len
+ new_len
;
579 a
= realloc(r
, nl
+ 1);
587 t
= stpcpy(t
, new_string
);
599 char *strip_tab_ansi(char **ibuf
, size_t *_isz
) {
600 const char *i
, *begin
= NULL
;
605 } state
= STATE_OTHER
;
613 /* Strips ANSI color and replaces TABs by 8 spaces */
615 isz
= _isz
? *_isz
: strlen(*ibuf
);
617 f
= open_memstream(&obuf
, &osz
);
621 for (i
= *ibuf
; i
< *ibuf
+ isz
+ 1; i
++) {
626 if (i
>= *ibuf
+ isz
) /* EOT */
628 else if (*i
== '\x1B')
629 state
= STATE_ESCAPE
;
637 if (i
>= *ibuf
+ isz
) { /* EOT */
640 } else if (*i
== '[') {
641 state
= STATE_BRACKET
;
653 if (i
>= *ibuf
+ isz
|| /* EOT */
654 (!(*i
>= '0' && *i
<= '9') && *i
!= ';' && *i
!= 'm')) {
659 } else if (*i
== 'm')
682 char *strextend(char **x
, ...) {
689 l
= f
= *x
? strlen(*x
) : 0;
696 t
= va_arg(ap
, const char *);
701 if (n
> ((size_t) -1) - l
) {
710 r
= realloc(*x
, l
+1);
720 t
= va_arg(ap
, const char *);
734 char *strrep(const char *s
, unsigned n
) {
742 p
= r
= malloc(l
* n
+ 1);
746 for (i
= 0; i
< n
; i
++)
753 int split_pair(const char *s
, const char *sep
, char **l
, char **r
) {
768 a
= strndup(s
, x
- s
);
772 b
= strdup(x
+ strlen(sep
));
784 int free_and_strdup(char **p
, const char *s
) {
789 /* Replaces a string pointer with an strdup()ed new string,
790 * possibly freeing the old one. */
792 if (streq_ptr(*p
, s
))
808 #pragma GCC push_options
809 #pragma GCC optimize("O0")
811 void* memory_erase(void *p
, size_t l
) {
812 volatile uint8_t* x
= (volatile uint8_t*) p
;
814 /* This basically does what memset() does, but hopefully isn't
815 * optimized away by the compiler. One of those days, when
816 * glibc learns memset_s() we should replace this call by
817 * memset_s(), but until then this has to do. */
825 #pragma GCC pop_options
827 char* string_erase(char *x
) {
832 /* A delicious drop of snake-oil! To be called on memory where
833 * we stored passphrases or so, after we used them. */
835 return memory_erase(x
, strlen(x
));
838 char *string_free_erase(char *s
) {
839 return mfree(string_erase(s
));
842 bool string_is_safe(const char *p
) {
848 for (t
= p
; *t
; t
++) {
849 if (*t
> 0 && *t
< ' ') /* no control characters */
852 if (strchr(QUOTES
"\\\x7f", *t
))