]>
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 bool chars_intersect(const char *a
, const char *b
) {
354 /* Returns true if any of the chars in a are in b. */
362 bool string_has_cc(const char *p
, const char *ok
) {
368 * Check if a string contains control characters. If 'ok' is
369 * non-NULL it may be a string containing additional CCs to be
373 for (t
= p
; *t
; t
++) {
374 if (ok
&& strchr(ok
, *t
))
377 if (*t
> 0 && *t
< ' ')
387 static char *ascii_ellipsize_mem(const char *s
, size_t old_length
, size_t new_length
, unsigned percent
) {
392 assert(percent
<= 100);
393 assert(new_length
>= 3);
395 if (old_length
<= 3 || old_length
<= new_length
)
396 return strndup(s
, old_length
);
398 r
= new0(char, new_length
+1);
402 x
= (new_length
* percent
) / 100;
404 if (x
> new_length
- 3)
412 s
+ old_length
- (new_length
- x
- 3),
418 char *ellipsize_mem(const char *s
, size_t old_length
, size_t new_length
, unsigned percent
) {
422 unsigned k
, len
, len2
;
425 assert(percent
<= 100);
426 assert(new_length
>= 3);
428 /* if no multibyte characters use ascii_ellipsize_mem for speed */
429 if (ascii_is_valid(s
))
430 return ascii_ellipsize_mem(s
, old_length
, new_length
, percent
);
432 if (old_length
<= 3 || old_length
<= new_length
)
433 return strndup(s
, old_length
);
435 x
= (new_length
* percent
) / 100;
437 if (x
> new_length
- 3)
441 for (i
= s
; k
< x
&& i
< s
+ old_length
; i
= utf8_next_char(i
)) {
444 c
= utf8_encoded_to_unichar(i
);
447 k
+= unichar_iswide(c
) ? 2 : 1;
450 if (k
> x
) /* last character was wide and went over quota */
453 for (j
= s
+ old_length
; k
< new_length
&& j
> i
; ) {
456 j
= utf8_prev_char(j
);
457 c
= utf8_encoded_to_unichar(j
);
460 k
+= unichar_iswide(c
) ? 2 : 1;
464 /* we don't actually need to ellipsize */
466 return memdup(s
, old_length
+ 1);
468 /* make space for ellipsis */
469 j
= utf8_next_char(j
);
472 len2
= s
+ old_length
- j
;
473 e
= new(char, len
+ 3 + len2
+ 1);
478 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
479 old_length, new_length, x, len, len2, k);
483 e
[len
] = 0xe2; /* tri-dot ellipsis: … */
487 memcpy(e
+ len
+ 3, j
, len2
+ 1);
492 char *ellipsize(const char *s
, size_t length
, unsigned percent
) {
493 return ellipsize_mem(s
, strlen(s
), length
, percent
);
496 bool nulstr_contains(const char*nulstr
, const char *needle
) {
502 NULSTR_FOREACH(i
, nulstr
)
503 if (streq(i
, needle
))
509 char* strshorten(char *s
, size_t l
) {
518 char *strreplace(const char *text
, const char *old_string
, const char *new_string
) {
521 size_t l
, old_len
, new_len
;
527 old_len
= strlen(old_string
);
528 new_len
= strlen(new_string
);
541 if (!startswith(f
, old_string
)) {
547 nl
= l
- old_len
+ new_len
;
548 a
= realloc(r
, nl
+ 1);
556 t
= stpcpy(t
, new_string
);
568 char *strip_tab_ansi(char **ibuf
, size_t *_isz
) {
569 const char *i
, *begin
= NULL
;
574 } state
= STATE_OTHER
;
582 /* Strips ANSI color and replaces TABs by 8 spaces */
584 isz
= _isz
? *_isz
: strlen(*ibuf
);
586 f
= open_memstream(&obuf
, &osz
);
590 for (i
= *ibuf
; i
< *ibuf
+ isz
+ 1; i
++) {
595 if (i
>= *ibuf
+ isz
) /* EOT */
597 else if (*i
== '\x1B')
598 state
= STATE_ESCAPE
;
606 if (i
>= *ibuf
+ isz
) { /* EOT */
609 } else if (*i
== '[') {
610 state
= STATE_BRACKET
;
622 if (i
>= *ibuf
+ isz
|| /* EOT */
623 (!(*i
>= '0' && *i
<= '9') && *i
!= ';' && *i
!= 'm')) {
628 } else if (*i
== 'm')
651 char *strextend(char **x
, ...) {
658 l
= f
= *x
? strlen(*x
) : 0;
665 t
= va_arg(ap
, const char *);
670 if (n
> ((size_t) -1) - l
) {
679 r
= realloc(*x
, l
+1);
689 t
= va_arg(ap
, const char *);
703 char *strrep(const char *s
, unsigned n
) {
711 p
= r
= malloc(l
* n
+ 1);
715 for (i
= 0; i
< n
; i
++)
722 int split_pair(const char *s
, const char *sep
, char **l
, char **r
) {
737 a
= strndup(s
, x
- s
);
741 b
= strdup(x
+ strlen(sep
));
753 int free_and_strdup(char **p
, const char *s
) {
758 /* Replaces a string pointer with an strdup()ed new string,
759 * possibly freeing the old one. */
761 if (streq_ptr(*p
, s
))
777 #pragma GCC push_options
778 #pragma GCC optimize("O0")
780 void* memory_erase(void *p
, size_t l
) {
781 volatile uint8_t* x
= (volatile uint8_t*) p
;
783 /* This basically does what memset() does, but hopefully isn't
784 * optimized away by the compiler. One of those days, when
785 * glibc learns memset_s() we should replace this call by
786 * memset_s(), but until then this has to do. */
794 #pragma GCC pop_options
796 char* string_erase(char *x
) {
801 /* A delicious drop of snake-oil! To be called on memory where
802 * we stored passphrases or so, after we used them. */
804 return memory_erase(x
, strlen(x
));
807 char *string_free_erase(char *s
) {
808 return mfree(string_erase(s
));
811 bool string_is_safe(const char *p
) {
817 for (t
= p
; *t
; t
++) {
818 if (*t
> 0 && *t
< ' ') /* no control characters */
821 if (strchr(QUOTES
"\\\x7f", *t
))