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/>.
22 #include "alloc-util.h"
24 #include "string-util.h"
28 int strcmp_ptr(const char *a, const char *b) {
30 /* Like strcmp(), but tries to make sense of NULL pointers */
43 char* endswith(const char *s, const char *postfix) {
53 return (char*) s + sl;
58 if (memcmp(s + sl - pl, postfix, pl) != 0)
61 return (char*) s + sl - pl;
64 char* endswith_no_case(const char *s, const char *postfix) {
74 return (char*) s + sl;
79 if (strcasecmp(s + sl - pl, postfix) != 0)
82 return (char*) s + sl - pl;
85 char* first_word(const char *s, const char *word) {
92 /* Checks if the string starts with the specified word, either
93 * followed by NUL or by whitespace. Returns a pointer to the
94 * NUL or the first character after the whitespace. */
105 if (memcmp(s, word, wl) != 0)
112 if (!strchr(WHITESPACE, *p))
115 p += strspn(p, WHITESPACE);
119 static size_t strcspn_escaped(const char *s, const char *reject) {
120 bool escaped = false;
123 for (n=0; s[n]; n++) {
126 else if (s[n] == '\\')
128 else if (strchr(reject, s[n]))
132 /* if s ends in \, return index of previous char */
136 /* Split a string into words. */
137 const char* split(const char **state, size_t *l, const char *separator, bool quoted) {
143 assert(**state == '\0');
147 current += strspn(current, separator);
153 if (quoted && strchr("\'\"", *current)) {
154 char quotechars[2] = {*current, '\0'};
156 *l = strcspn_escaped(current + 1, quotechars);
157 if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
158 (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
159 /* right quote missing or garbage at the end */
163 *state = current++ + *l + 2;
165 *l = strcspn_escaped(current, separator);
166 if (current[*l] && !strchr(separator, current[*l])) {
167 /* unfinished escape */
171 *state = current + *l;
173 *l = strcspn(current, separator);
174 *state = current + *l;
180 char *strnappend(const char *s, const char *suffix, size_t b) {
188 return strndup(suffix, b);
197 if (b > ((size_t) -1) - a)
200 r = new(char, a+b+1);
205 memcpy(r+a, suffix, b);
211 char *strappend(const char *s, const char *suffix) {
212 return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
215 char *strjoin(const char *x, ...) {
229 t = va_arg(ap, const char *);
234 if (n > ((size_t) -1) - l) {
258 t = va_arg(ap, const char *);
272 char *strstrip(char *s) {
275 /* Drops trailing whitespace. Modifies the string in
276 * place. Returns pointer to first non-space character */
278 s += strspn(s, WHITESPACE);
280 for (e = strchr(s, 0); e > s; e --)
281 if (!strchr(WHITESPACE, e[-1]))
289 char *delete_chars(char *s, const char *bad) {
292 /* Drops all whitespace, regardless where in the string */
294 for (f = s, t = s; *f; f++) {
306 char *truncate_nl(char *s) {
309 s[strcspn(s, NEWLINE)] = 0;
313 char ascii_tolower(char x) {
315 if (x >= 'A' && x <= 'Z')
316 return x - 'A' + 'a';
321 char *ascii_strlower(char *t) {
327 *p = ascii_tolower(*p);
332 char *ascii_strlower_n(char *t, size_t n) {
338 for (i = 0; i < n; i++)
339 t[i] = ascii_tolower(t[i]);
344 int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
346 for (; n > 0; a++, b++, n--) {
349 x = (int) (uint8_t) ascii_tolower(*a);
350 y = (int) (uint8_t) ascii_tolower(*b);
359 int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
362 r = ascii_strcasecmp_n(a, b, MIN(n, m));
374 bool chars_intersect(const char *a, const char *b) {
377 /* Returns true if any of the chars in a are in b. */
385 bool string_has_cc(const char *p, const char *ok) {
391 * Check if a string contains control characters. If 'ok' is
392 * non-NULL it may be a string containing additional CCs to be
396 for (t = p; *t; t++) {
397 if (ok && strchr(ok, *t))
400 if (*t > 0 && *t < ' ')
410 static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
415 assert(percent <= 100);
416 assert(new_length >= 3);
418 if (old_length <= 3 || old_length <= new_length)
419 return strndup(s, old_length);
421 r = new0(char, new_length+1);
425 x = (new_length * percent) / 100;
427 if (x > new_length - 3)
435 s + old_length - (new_length - x - 3),
441 char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
445 unsigned k, len, len2;
449 assert(percent <= 100);
450 assert(new_length >= 3);
452 /* if no multibyte characters use ascii_ellipsize_mem for speed */
453 if (ascii_is_valid(s))
454 return ascii_ellipsize_mem(s, old_length, new_length, percent);
456 if (old_length <= 3 || old_length <= new_length)
457 return strndup(s, old_length);
459 x = (new_length * percent) / 100;
461 if (x > new_length - 3)
465 for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
468 r = utf8_encoded_to_unichar(i, &c);
471 k += unichar_iswide(c) ? 2 : 1;
474 if (k > x) /* last character was wide and went over quota */
477 for (j = s + old_length; k < new_length && j > i; ) {
480 j = utf8_prev_char(j);
481 r = utf8_encoded_to_unichar(j, &c);
484 k += unichar_iswide(c) ? 2 : 1;
488 /* we don't actually need to ellipsize */
490 return memdup(s, old_length + 1);
492 /* make space for ellipsis */
493 j = utf8_next_char(j);
496 len2 = s + old_length - j;
497 e = new(char, len + 3 + len2 + 1);
502 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
503 old_length, new_length, x, len, len2, k);
507 e[len] = 0xe2; /* tri-dot ellipsis: … */
511 memcpy(e + len + 3, j, len2 + 1);
516 char *ellipsize(const char *s, size_t length, unsigned percent) {
517 return ellipsize_mem(s, strlen(s), length, percent);
520 bool nulstr_contains(const char*nulstr, const char *needle) {
526 NULSTR_FOREACH(i, nulstr)
527 if (streq(i, needle))
533 char* strshorten(char *s, size_t l) {
542 char *strreplace(const char *text, const char *old_string, const char *new_string) {
545 size_t l, old_len, new_len;
551 old_len = strlen(old_string);
552 new_len = strlen(new_string);
565 if (!startswith(f, old_string)) {
571 nl = l - old_len + new_len;
572 a = realloc(r, nl + 1);
580 t = stpcpy(t, new_string);
592 char *strip_tab_ansi(char **ibuf, size_t *_isz) {
593 const char *i, *begin = NULL;
598 } state = STATE_OTHER;
606 /* Strips ANSI color and replaces TABs by 8 spaces */
608 isz = _isz ? *_isz : strlen(*ibuf);
610 f = open_memstream(&obuf, &osz);
614 for (i = *ibuf; i < *ibuf + isz + 1; i++) {
619 if (i >= *ibuf + isz) /* EOT */
621 else if (*i == '\x1B')
622 state = STATE_ESCAPE;
630 if (i >= *ibuf + isz) { /* EOT */
633 } else if (*i == '[') {
634 state = STATE_BRACKET;
646 if (i >= *ibuf + isz || /* EOT */
647 (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
652 } else if (*i == 'm')
675 char *strextend(char **x, ...) {
682 l = f = *x ? strlen(*x) : 0;
689 t = va_arg(ap, const char *);
694 if (n > ((size_t) -1) - l) {
703 r = realloc(*x, l+1);
713 t = va_arg(ap, const char *);
727 char *strrep(const char *s, unsigned n) {
735 p = r = malloc(l * n + 1);
739 for (i = 0; i < n; i++)
746 int split_pair(const char *s, const char *sep, char **l, char **r) {
761 a = strndup(s, x - s);
765 b = strdup(x + strlen(sep));
777 int free_and_strdup(char **p, const char *s) {
782 /* Replaces a string pointer with an strdup()ed new string,
783 * possibly freeing the old one. */
785 if (streq_ptr(*p, s))
801 #pragma GCC push_options
802 #pragma GCC optimize("O0")
804 void* memory_erase(void *p, size_t l) {
805 volatile uint8_t* x = (volatile uint8_t*) p;
807 /* This basically does what memset() does, but hopefully isn't
808 * optimized away by the compiler. One of those days, when
809 * glibc learns memset_s() we should replace this call by
810 * memset_s(), but until then this has to do. */
818 #pragma GCC pop_options
820 char* string_erase(char *x) {
825 /* A delicious drop of snake-oil! To be called on memory where
826 * we stored passphrases or so, after we used them. */
828 return memory_erase(x, strlen(x));
831 char *string_free_erase(char *s) {
832 return mfree(string_erase(s));
835 bool string_is_safe(const char *p) {
841 for (t = p; *t; t++) {
842 if (*t > 0 && *t < ' ') /* no control characters */
845 if (strchr(QUOTES "\\\x7f", *t))