]>
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/>.
25 #include "string-util.h"
27 int strcmp_ptr(const char *a
, const char *b
) {
29 /* Like strcmp(), but tries to make sense of NULL pointers */
42 char* endswith(const char *s
, const char *postfix
) {
52 return (char*) s
+ sl
;
57 if (memcmp(s
+ sl
- pl
, postfix
, pl
) != 0)
60 return (char*) s
+ sl
- pl
;
63 char* endswith_no_case(const char *s
, const char *postfix
) {
73 return (char*) s
+ sl
;
78 if (strcasecmp(s
+ sl
- pl
, postfix
) != 0)
81 return (char*) s
+ sl
- pl
;
84 char* first_word(const char *s
, const char *word
) {
91 /* Checks if the string starts with the specified word, either
92 * followed by NUL or by whitespace. Returns a pointer to the
93 * NUL or the first character after the whitespace. */
104 if (memcmp(s
, word
, wl
) != 0)
111 if (!strchr(WHITESPACE
, *p
))
114 p
+= strspn(p
, WHITESPACE
);
118 static size_t strcspn_escaped(const char *s
, const char *reject
) {
119 bool escaped
= false;
122 for (n
=0; s
[n
]; n
++) {
125 else if (s
[n
] == '\\')
127 else if (strchr(reject
, s
[n
]))
131 /* if s ends in \, return index of previous char */
135 /* Split a string into words. */
136 const char* split(const char **state
, size_t *l
, const char *separator
, bool quoted
) {
142 assert(**state
== '\0');
146 current
+= strspn(current
, separator
);
152 if (quoted
&& strchr("\'\"", *current
)) {
153 char quotechars
[2] = {*current
, '\0'};
155 *l
= strcspn_escaped(current
+ 1, quotechars
);
156 if (current
[*l
+ 1] == '\0' || current
[*l
+ 1] != quotechars
[0] ||
157 (current
[*l
+ 2] && !strchr(separator
, current
[*l
+ 2]))) {
158 /* right quote missing or garbage at the end */
162 *state
= current
++ + *l
+ 2;
164 *l
= strcspn_escaped(current
, separator
);
165 if (current
[*l
] && !strchr(separator
, current
[*l
])) {
166 /* unfinished escape */
170 *state
= current
+ *l
;
172 *l
= strcspn(current
, separator
);
173 *state
= current
+ *l
;
179 char *strnappend(const char *s
, const char *suffix
, size_t b
) {
187 return strndup(suffix
, b
);
196 if (b
> ((size_t) -1) - a
)
199 r
= new(char, a
+b
+1);
204 memcpy(r
+a
, suffix
, b
);
210 char *strappend(const char *s
, const char *suffix
) {
211 return strnappend(s
, suffix
, suffix
? strlen(suffix
) : 0);
214 char *strjoin(const char *x
, ...) {
228 t
= va_arg(ap
, const char *);
233 if (n
> ((size_t) -1) - l
) {
257 t
= va_arg(ap
, const char *);
271 char *strstrip(char *s
) {
274 /* Drops trailing whitespace. Modifies the string in
275 * place. Returns pointer to first non-space character */
277 s
+= strspn(s
, WHITESPACE
);
279 for (e
= strchr(s
, 0); e
> s
; e
--)
280 if (!strchr(WHITESPACE
, e
[-1]))
288 char *delete_chars(char *s
, const char *bad
) {
291 /* Drops all whitespace, regardless where in the string */
293 for (f
= s
, t
= s
; *f
; f
++) {
305 char *truncate_nl(char *s
) {
308 s
[strcspn(s
, NEWLINE
)] = 0;
312 char *ascii_strlower(char *t
) {
318 if (*p
>= 'A' && *p
<= 'Z')
324 bool chars_intersect(const char *a
, const char *b
) {
327 /* Returns true if any of the chars in a are in b. */
335 bool string_has_cc(const char *p
, const char *ok
) {
341 * Check if a string contains control characters. If 'ok' is
342 * non-NULL it may be a string containing additional CCs to be
346 for (t
= p
; *t
; t
++) {
347 if (ok
&& strchr(ok
, *t
))
350 if (*t
> 0 && *t
< ' ')
360 static char *ascii_ellipsize_mem(const char *s
, size_t old_length
, size_t new_length
, unsigned percent
) {
365 assert(percent
<= 100);
366 assert(new_length
>= 3);
368 if (old_length
<= 3 || old_length
<= new_length
)
369 return strndup(s
, old_length
);
371 r
= new0(char, new_length
+1);
375 x
= (new_length
* percent
) / 100;
377 if (x
> new_length
- 3)
385 s
+ old_length
- (new_length
- x
- 3),
391 char *ellipsize_mem(const char *s
, size_t old_length
, size_t new_length
, unsigned percent
) {
395 unsigned k
, len
, len2
;
398 assert(percent
<= 100);
399 assert(new_length
>= 3);
401 /* if no multibyte characters use ascii_ellipsize_mem for speed */
402 if (ascii_is_valid(s
))
403 return ascii_ellipsize_mem(s
, old_length
, new_length
, percent
);
405 if (old_length
<= 3 || old_length
<= new_length
)
406 return strndup(s
, old_length
);
408 x
= (new_length
* percent
) / 100;
410 if (x
> new_length
- 3)
414 for (i
= s
; k
< x
&& i
< s
+ old_length
; i
= utf8_next_char(i
)) {
417 c
= utf8_encoded_to_unichar(i
);
420 k
+= unichar_iswide(c
) ? 2 : 1;
423 if (k
> x
) /* last character was wide and went over quota */
426 for (j
= s
+ old_length
; k
< new_length
&& j
> i
; ) {
429 j
= utf8_prev_char(j
);
430 c
= utf8_encoded_to_unichar(j
);
433 k
+= unichar_iswide(c
) ? 2 : 1;
437 /* we don't actually need to ellipsize */
439 return memdup(s
, old_length
+ 1);
441 /* make space for ellipsis */
442 j
= utf8_next_char(j
);
445 len2
= s
+ old_length
- j
;
446 e
= new(char, len
+ 3 + len2
+ 1);
451 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
452 old_length, new_length, x, len, len2, k);
456 e
[len
] = 0xe2; /* tri-dot ellipsis: … */
460 memcpy(e
+ len
+ 3, j
, len2
+ 1);
465 char *ellipsize(const char *s
, size_t length
, unsigned percent
) {
466 return ellipsize_mem(s
, strlen(s
), length
, percent
);
469 bool nulstr_contains(const char*nulstr
, const char *needle
) {
475 NULSTR_FOREACH(i
, nulstr
)
476 if (streq(i
, needle
))
482 char* strshorten(char *s
, size_t l
) {
491 char *strreplace(const char *text
, const char *old_string
, const char *new_string
) {
494 size_t l
, old_len
, new_len
;
500 old_len
= strlen(old_string
);
501 new_len
= strlen(new_string
);
514 if (!startswith(f
, old_string
)) {
520 nl
= l
- old_len
+ new_len
;
521 a
= realloc(r
, nl
+ 1);
529 t
= stpcpy(t
, new_string
);
541 char *strip_tab_ansi(char **ibuf
, size_t *_isz
) {
542 const char *i
, *begin
= NULL
;
547 } state
= STATE_OTHER
;
555 /* Strips ANSI color and replaces TABs by 8 spaces */
557 isz
= _isz
? *_isz
: strlen(*ibuf
);
559 f
= open_memstream(&obuf
, &osz
);
563 for (i
= *ibuf
; i
< *ibuf
+ isz
+ 1; i
++) {
568 if (i
>= *ibuf
+ isz
) /* EOT */
570 else if (*i
== '\x1B')
571 state
= STATE_ESCAPE
;
579 if (i
>= *ibuf
+ isz
) { /* EOT */
582 } else if (*i
== '[') {
583 state
= STATE_BRACKET
;
595 if (i
>= *ibuf
+ isz
|| /* EOT */
596 (!(*i
>= '0' && *i
<= '9') && *i
!= ';' && *i
!= 'm')) {
601 } else if (*i
== 'm')
624 char *strextend(char **x
, ...) {
631 l
= f
= *x
? strlen(*x
) : 0;
638 t
= va_arg(ap
, const char *);
643 if (n
> ((size_t) -1) - l
) {
652 r
= realloc(*x
, l
+1);
662 t
= va_arg(ap
, const char *);
676 char *strrep(const char *s
, unsigned n
) {
684 p
= r
= malloc(l
* n
+ 1);
688 for (i
= 0; i
< n
; i
++)
695 int split_pair(const char *s
, const char *sep
, char **l
, char **r
) {
710 a
= strndup(s
, x
- s
);
714 b
= strdup(x
+ strlen(sep
));
726 int free_and_strdup(char **p
, const char *s
) {
731 /* Replaces a string pointer with an strdup()ed new string,
732 * possibly freeing the old one. */
734 if (streq_ptr(*p
, s
))
750 void string_erase(char *x
) {
755 /* A delicious drop of snake-oil! To be called on memory where
756 * we stored passphrases or so, after we used them. */
758 memory_erase(x
, strlen(x
));
761 char *string_free_erase(char *s
) {