]>
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
;
455 assert(percent
<= 100);
456 assert(new_length
>= 3);
458 /* if no multibyte characters use ascii_ellipsize_mem for speed */
459 if (ascii_is_valid(s
))
460 return ascii_ellipsize_mem(s
, old_length
, new_length
, percent
);
462 if (old_length
<= 3 || old_length
<= new_length
)
463 return strndup(s
, old_length
);
465 x
= (new_length
* percent
) / 100;
467 if (x
> new_length
- 3)
471 for (i
= s
; k
< x
&& i
< s
+ old_length
; i
= utf8_next_char(i
)) {
474 c
= utf8_encoded_to_unichar(i
);
477 k
+= unichar_iswide(c
) ? 2 : 1;
480 if (k
> x
) /* last character was wide and went over quota */
483 for (j
= s
+ old_length
; k
< new_length
&& j
> i
; ) {
486 j
= utf8_prev_char(j
);
487 c
= utf8_encoded_to_unichar(j
);
490 k
+= unichar_iswide(c
) ? 2 : 1;
494 /* we don't actually need to ellipsize */
496 return memdup(s
, old_length
+ 1);
498 /* make space for ellipsis */
499 j
= utf8_next_char(j
);
502 len2
= s
+ old_length
- j
;
503 e
= new(char, len
+ 3 + len2
+ 1);
508 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
509 old_length, new_length, x, len, len2, k);
513 e
[len
] = 0xe2; /* tri-dot ellipsis: … */
517 memcpy(e
+ len
+ 3, j
, len2
+ 1);
522 char *ellipsize(const char *s
, size_t length
, unsigned percent
) {
523 return ellipsize_mem(s
, strlen(s
), length
, percent
);
526 bool nulstr_contains(const char*nulstr
, const char *needle
) {
532 NULSTR_FOREACH(i
, nulstr
)
533 if (streq(i
, needle
))
539 char* strshorten(char *s
, size_t l
) {
548 char *strreplace(const char *text
, const char *old_string
, const char *new_string
) {
551 size_t l
, old_len
, new_len
;
557 old_len
= strlen(old_string
);
558 new_len
= strlen(new_string
);
571 if (!startswith(f
, old_string
)) {
577 nl
= l
- old_len
+ new_len
;
578 a
= realloc(r
, nl
+ 1);
586 t
= stpcpy(t
, new_string
);
598 char *strip_tab_ansi(char **ibuf
, size_t *_isz
) {
599 const char *i
, *begin
= NULL
;
604 } state
= STATE_OTHER
;
612 /* Strips ANSI color and replaces TABs by 8 spaces */
614 isz
= _isz
? *_isz
: strlen(*ibuf
);
616 f
= open_memstream(&obuf
, &osz
);
620 for (i
= *ibuf
; i
< *ibuf
+ isz
+ 1; i
++) {
625 if (i
>= *ibuf
+ isz
) /* EOT */
627 else if (*i
== '\x1B')
628 state
= STATE_ESCAPE
;
636 if (i
>= *ibuf
+ isz
) { /* EOT */
639 } else if (*i
== '[') {
640 state
= STATE_BRACKET
;
652 if (i
>= *ibuf
+ isz
|| /* EOT */
653 (!(*i
>= '0' && *i
<= '9') && *i
!= ';' && *i
!= 'm')) {
658 } else if (*i
== 'm')
681 char *strextend(char **x
, ...) {
688 l
= f
= *x
? strlen(*x
) : 0;
695 t
= va_arg(ap
, const char *);
700 if (n
> ((size_t) -1) - l
) {
709 r
= realloc(*x
, l
+1);
719 t
= va_arg(ap
, const char *);
733 char *strrep(const char *s
, unsigned n
) {
741 p
= r
= malloc(l
* n
+ 1);
745 for (i
= 0; i
< n
; i
++)
752 int split_pair(const char *s
, const char *sep
, char **l
, char **r
) {
767 a
= strndup(s
, x
- s
);
771 b
= strdup(x
+ strlen(sep
));
783 int free_and_strdup(char **p
, const char *s
) {
788 /* Replaces a string pointer with an strdup()ed new string,
789 * possibly freeing the old one. */
791 if (streq_ptr(*p
, s
))
807 #pragma GCC push_options
808 #pragma GCC optimize("O0")
810 void* memory_erase(void *p
, size_t l
) {
811 volatile uint8_t* x
= (volatile uint8_t*) p
;
813 /* This basically does what memset() does, but hopefully isn't
814 * optimized away by the compiler. One of those days, when
815 * glibc learns memset_s() we should replace this call by
816 * memset_s(), but until then this has to do. */
824 #pragma GCC pop_options
826 char* string_erase(char *x
) {
831 /* A delicious drop of snake-oil! To be called on memory where
832 * we stored passphrases or so, after we used them. */
834 return memory_erase(x
, strlen(x
));
837 char *string_free_erase(char *s
) {
838 return mfree(string_erase(s
));
841 bool string_is_safe(const char *p
) {
847 for (t
= p
; *t
; t
++) {
848 if (*t
> 0 && *t
< ' ') /* no control characters */
851 if (strchr(QUOTES
"\\\x7f", *t
))