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 bool chars_intersect(const char *a, const char *b) {
347 /* Returns true if any of the chars in a are in b. */
355 bool string_has_cc(const char *p, const char *ok) {
361 * Check if a string contains control characters. If 'ok' is
362 * non-NULL it may be a string containing additional CCs to be
366 for (t = p; *t; t++) {
367 if (ok && strchr(ok, *t))
370 if (*t > 0 && *t < ' ')
380 static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
385 assert(percent <= 100);
386 assert(new_length >= 3);
388 if (old_length <= 3 || old_length <= new_length)
389 return strndup(s, old_length);
391 r = new0(char, new_length+1);
395 x = (new_length * percent) / 100;
397 if (x > new_length - 3)
405 s + old_length - (new_length - x - 3),
411 char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
415 unsigned k, len, len2;
419 assert(percent <= 100);
420 assert(new_length >= 3);
422 /* if no multibyte characters use ascii_ellipsize_mem for speed */
423 if (ascii_is_valid(s))
424 return ascii_ellipsize_mem(s, old_length, new_length, percent);
426 if (old_length <= 3 || old_length <= new_length)
427 return strndup(s, old_length);
429 x = (new_length * percent) / 100;
431 if (x > new_length - 3)
435 for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
438 r = utf8_encoded_to_unichar(i, &c);
441 k += unichar_iswide(c) ? 2 : 1;
444 if (k > x) /* last character was wide and went over quota */
447 for (j = s + old_length; k < new_length && j > i; ) {
450 j = utf8_prev_char(j);
451 r = utf8_encoded_to_unichar(j, &c);
454 k += unichar_iswide(c) ? 2 : 1;
458 /* we don't actually need to ellipsize */
460 return memdup(s, old_length + 1);
462 /* make space for ellipsis */
463 j = utf8_next_char(j);
466 len2 = s + old_length - j;
467 e = new(char, len + 3 + len2 + 1);
472 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
473 old_length, new_length, x, len, len2, k);
477 e[len] = 0xe2; /* tri-dot ellipsis: … */
481 memcpy(e + len + 3, j, len2 + 1);
486 char *ellipsize(const char *s, size_t length, unsigned percent) {
487 return ellipsize_mem(s, strlen(s), length, percent);
490 bool nulstr_contains(const char*nulstr, const char *needle) {
496 NULSTR_FOREACH(i, nulstr)
497 if (streq(i, needle))
503 char* strshorten(char *s, size_t l) {
512 char *strreplace(const char *text, const char *old_string, const char *new_string) {
515 size_t l, old_len, new_len;
521 old_len = strlen(old_string);
522 new_len = strlen(new_string);
535 if (!startswith(f, old_string)) {
541 nl = l - old_len + new_len;
542 a = realloc(r, nl + 1);
550 t = stpcpy(t, new_string);
562 char *strip_tab_ansi(char **ibuf, size_t *_isz) {
563 const char *i, *begin = NULL;
568 } state = STATE_OTHER;
576 /* Strips ANSI color and replaces TABs by 8 spaces */
578 isz = _isz ? *_isz : strlen(*ibuf);
580 f = open_memstream(&obuf, &osz);
584 for (i = *ibuf; i < *ibuf + isz + 1; i++) {
589 if (i >= *ibuf + isz) /* EOT */
591 else if (*i == '\x1B')
592 state = STATE_ESCAPE;
600 if (i >= *ibuf + isz) { /* EOT */
603 } else if (*i == '[') {
604 state = STATE_BRACKET;
616 if (i >= *ibuf + isz || /* EOT */
617 (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
622 } else if (*i == 'm')
645 char *strextend(char **x, ...) {
652 l = f = *x ? strlen(*x) : 0;
659 t = va_arg(ap, const char *);
664 if (n > ((size_t) -1) - l) {
673 r = realloc(*x, l+1);
683 t = va_arg(ap, const char *);
697 char *strrep(const char *s, unsigned n) {
705 p = r = malloc(l * n + 1);
709 for (i = 0; i < n; i++)
716 int split_pair(const char *s, const char *sep, char **l, char **r) {
731 a = strndup(s, x - s);
735 b = strdup(x + strlen(sep));
747 int free_and_strdup(char **p, const char *s) {
752 /* Replaces a string pointer with an strdup()ed new string,
753 * possibly freeing the old one. */
755 if (streq_ptr(*p, s))
771 #pragma GCC push_options
772 #pragma GCC optimize("O0")
774 void* memory_erase(void *p, size_t l) {
775 volatile uint8_t* x = (volatile uint8_t*) p;
777 /* This basically does what memset() does, but hopefully isn't
778 * optimized away by the compiler. One of those days, when
779 * glibc learns memset_s() we should replace this call by
780 * memset_s(), but until then this has to do. */
788 #pragma GCC pop_options
790 char* string_erase(char *x) {
795 /* A delicious drop of snake-oil! To be called on memory where
796 * we stored passphrases or so, after we used them. */
798 return memory_erase(x, strlen(x));
801 char *string_free_erase(char *s) {
802 return mfree(string_erase(s));
805 bool string_is_safe(const char *p) {
811 for (t = p; *t; t++) {
812 if (*t > 0 && *t < ' ') /* no control characters */
815 if (strchr(QUOTES "\\\x7f", *t))