]>
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_strlower(char *t
) {
326 if (*p
>= 'A' && *p
<= 'Z')
332 bool chars_intersect(const char *a
, const char *b
) {
335 /* Returns true if any of the chars in a are in b. */
343 bool string_has_cc(const char *p
, const char *ok
) {
349 * Check if a string contains control characters. If 'ok' is
350 * non-NULL it may be a string containing additional CCs to be
354 for (t
= p
; *t
; t
++) {
355 if (ok
&& strchr(ok
, *t
))
358 if (*t
> 0 && *t
< ' ')
368 static char *ascii_ellipsize_mem(const char *s
, size_t old_length
, size_t new_length
, unsigned percent
) {
373 assert(percent
<= 100);
374 assert(new_length
>= 3);
376 if (old_length
<= 3 || old_length
<= new_length
)
377 return strndup(s
, old_length
);
379 r
= new0(char, new_length
+1);
383 x
= (new_length
* percent
) / 100;
385 if (x
> new_length
- 3)
393 s
+ old_length
- (new_length
- x
- 3),
399 char *ellipsize_mem(const char *s
, size_t old_length
, size_t new_length
, unsigned percent
) {
403 unsigned k
, len
, len2
;
406 assert(percent
<= 100);
407 assert(new_length
>= 3);
409 /* if no multibyte characters use ascii_ellipsize_mem for speed */
410 if (ascii_is_valid(s
))
411 return ascii_ellipsize_mem(s
, old_length
, new_length
, percent
);
413 if (old_length
<= 3 || old_length
<= new_length
)
414 return strndup(s
, old_length
);
416 x
= (new_length
* percent
) / 100;
418 if (x
> new_length
- 3)
422 for (i
= s
; k
< x
&& i
< s
+ old_length
; i
= utf8_next_char(i
)) {
425 c
= utf8_encoded_to_unichar(i
);
428 k
+= unichar_iswide(c
) ? 2 : 1;
431 if (k
> x
) /* last character was wide and went over quota */
434 for (j
= s
+ old_length
; k
< new_length
&& j
> i
; ) {
437 j
= utf8_prev_char(j
);
438 c
= utf8_encoded_to_unichar(j
);
441 k
+= unichar_iswide(c
) ? 2 : 1;
445 /* we don't actually need to ellipsize */
447 return memdup(s
, old_length
+ 1);
449 /* make space for ellipsis */
450 j
= utf8_next_char(j
);
453 len2
= s
+ old_length
- j
;
454 e
= new(char, len
+ 3 + len2
+ 1);
459 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
460 old_length, new_length, x, len, len2, k);
464 e
[len
] = 0xe2; /* tri-dot ellipsis: … */
468 memcpy(e
+ len
+ 3, j
, len2
+ 1);
473 char *ellipsize(const char *s
, size_t length
, unsigned percent
) {
474 return ellipsize_mem(s
, strlen(s
), length
, percent
);
477 bool nulstr_contains(const char*nulstr
, const char *needle
) {
483 NULSTR_FOREACH(i
, nulstr
)
484 if (streq(i
, needle
))
490 char* strshorten(char *s
, size_t l
) {
499 char *strreplace(const char *text
, const char *old_string
, const char *new_string
) {
502 size_t l
, old_len
, new_len
;
508 old_len
= strlen(old_string
);
509 new_len
= strlen(new_string
);
522 if (!startswith(f
, old_string
)) {
528 nl
= l
- old_len
+ new_len
;
529 a
= realloc(r
, nl
+ 1);
537 t
= stpcpy(t
, new_string
);
549 char *strip_tab_ansi(char **ibuf
, size_t *_isz
) {
550 const char *i
, *begin
= NULL
;
555 } state
= STATE_OTHER
;
563 /* Strips ANSI color and replaces TABs by 8 spaces */
565 isz
= _isz
? *_isz
: strlen(*ibuf
);
567 f
= open_memstream(&obuf
, &osz
);
571 for (i
= *ibuf
; i
< *ibuf
+ isz
+ 1; i
++) {
576 if (i
>= *ibuf
+ isz
) /* EOT */
578 else if (*i
== '\x1B')
579 state
= STATE_ESCAPE
;
587 if (i
>= *ibuf
+ isz
) { /* EOT */
590 } else if (*i
== '[') {
591 state
= STATE_BRACKET
;
603 if (i
>= *ibuf
+ isz
|| /* EOT */
604 (!(*i
>= '0' && *i
<= '9') && *i
!= ';' && *i
!= 'm')) {
609 } else if (*i
== 'm')
632 char *strextend(char **x
, ...) {
639 l
= f
= *x
? strlen(*x
) : 0;
646 t
= va_arg(ap
, const char *);
651 if (n
> ((size_t) -1) - l
) {
660 r
= realloc(*x
, l
+1);
670 t
= va_arg(ap
, const char *);
684 char *strrep(const char *s
, unsigned n
) {
692 p
= r
= malloc(l
* n
+ 1);
696 for (i
= 0; i
< n
; i
++)
703 int split_pair(const char *s
, const char *sep
, char **l
, char **r
) {
718 a
= strndup(s
, x
- s
);
722 b
= strdup(x
+ strlen(sep
));
734 int free_and_strdup(char **p
, const char *s
) {
739 /* Replaces a string pointer with an strdup()ed new string,
740 * possibly freeing the old one. */
742 if (streq_ptr(*p
, s
))
758 #pragma GCC push_options
759 #pragma GCC optimize("O0")
761 void* memory_erase(void *p
, size_t l
) {
762 volatile uint8_t* x
= (volatile uint8_t*) p
;
764 /* This basically does what memset() does, but hopefully isn't
765 * optimized away by the compiler. One of those days, when
766 * glibc learns memset_s() we should replace this call by
767 * memset_s(), but until then this has to do. */
775 #pragma GCC pop_options
777 char* string_erase(char *x
) {
782 /* A delicious drop of snake-oil! To be called on memory where
783 * we stored passphrases or so, after we used them. */
785 return memory_erase(x
, strlen(x
));
788 char *string_free_erase(char *s
) {
789 return mfree(string_erase(s
));
792 bool string_is_safe(const char *p
) {
798 for (t
= p
; *t
; t
++) {
799 if (*t
> 0 && *t
< ' ') /* no control characters */
802 if (strchr(QUOTES
"\\\x7f", *t
))