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 bool chars_intersect(const char *a, const char *b) {
362 /* Returns true if any of the chars in a are in b. */
370 bool string_has_cc(const char *p, const char *ok) {
376 * Check if a string contains control characters. If 'ok' is
377 * non-NULL it may be a string containing additional CCs to be
381 for (t = p; *t; t++) {
382 if (ok && strchr(ok, *t))
385 if (*t > 0 && *t < ' ')
395 static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
400 assert(percent <= 100);
401 assert(new_length >= 3);
403 if (old_length <= 3 || old_length <= new_length)
404 return strndup(s, old_length);
406 r = new0(char, new_length+1);
410 x = (new_length * percent) / 100;
412 if (x > new_length - 3)
420 s + old_length - (new_length - x - 3),
426 char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
430 unsigned k, len, len2;
434 assert(percent <= 100);
435 assert(new_length >= 3);
437 /* if no multibyte characters use ascii_ellipsize_mem for speed */
438 if (ascii_is_valid(s))
439 return ascii_ellipsize_mem(s, old_length, new_length, percent);
441 if (old_length <= 3 || old_length <= new_length)
442 return strndup(s, old_length);
444 x = (new_length * percent) / 100;
446 if (x > new_length - 3)
450 for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
453 r = utf8_encoded_to_unichar(i, &c);
456 k += unichar_iswide(c) ? 2 : 1;
459 if (k > x) /* last character was wide and went over quota */
462 for (j = s + old_length; k < new_length && j > i; ) {
465 j = utf8_prev_char(j);
466 r = utf8_encoded_to_unichar(j, &c);
469 k += unichar_iswide(c) ? 2 : 1;
473 /* we don't actually need to ellipsize */
475 return memdup(s, old_length + 1);
477 /* make space for ellipsis */
478 j = utf8_next_char(j);
481 len2 = s + old_length - j;
482 e = new(char, len + 3 + len2 + 1);
487 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
488 old_length, new_length, x, len, len2, k);
492 e[len] = 0xe2; /* tri-dot ellipsis: … */
496 memcpy(e + len + 3, j, len2 + 1);
501 char *ellipsize(const char *s, size_t length, unsigned percent) {
502 return ellipsize_mem(s, strlen(s), length, percent);
505 bool nulstr_contains(const char*nulstr, const char *needle) {
511 NULSTR_FOREACH(i, nulstr)
512 if (streq(i, needle))
518 char* strshorten(char *s, size_t l) {
527 char *strreplace(const char *text, const char *old_string, const char *new_string) {
530 size_t l, old_len, new_len;
536 old_len = strlen(old_string);
537 new_len = strlen(new_string);
550 if (!startswith(f, old_string)) {
556 nl = l - old_len + new_len;
557 a = realloc(r, nl + 1);
565 t = stpcpy(t, new_string);
577 char *strip_tab_ansi(char **ibuf, size_t *_isz) {
578 const char *i, *begin = NULL;
583 } state = STATE_OTHER;
591 /* Strips ANSI color and replaces TABs by 8 spaces */
593 isz = _isz ? *_isz : strlen(*ibuf);
595 f = open_memstream(&obuf, &osz);
599 for (i = *ibuf; i < *ibuf + isz + 1; i++) {
604 if (i >= *ibuf + isz) /* EOT */
606 else if (*i == '\x1B')
607 state = STATE_ESCAPE;
615 if (i >= *ibuf + isz) { /* EOT */
618 } else if (*i == '[') {
619 state = STATE_BRACKET;
631 if (i >= *ibuf + isz || /* EOT */
632 (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
637 } else if (*i == 'm')
660 char *strextend(char **x, ...) {
667 l = f = *x ? strlen(*x) : 0;
674 t = va_arg(ap, const char *);
679 if (n > ((size_t) -1) - l) {
688 r = realloc(*x, l+1);
698 t = va_arg(ap, const char *);
712 char *strrep(const char *s, unsigned n) {
720 p = r = malloc(l * n + 1);
724 for (i = 0; i < n; i++)
731 int split_pair(const char *s, const char *sep, char **l, char **r) {
746 a = strndup(s, x - s);
750 b = strdup(x + strlen(sep));
762 int free_and_strdup(char **p, const char *s) {
767 /* Replaces a string pointer with an strdup()ed new string,
768 * possibly freeing the old one. */
770 if (streq_ptr(*p, s))
786 #pragma GCC push_options
787 #pragma GCC optimize("O0")
789 void* memory_erase(void *p, size_t l) {
790 volatile uint8_t* x = (volatile uint8_t*) p;
792 /* This basically does what memset() does, but hopefully isn't
793 * optimized away by the compiler. One of those days, when
794 * glibc learns memset_s() we should replace this call by
795 * memset_s(), but until then this has to do. */
803 #pragma GCC pop_options
805 char* string_erase(char *x) {
810 /* A delicious drop of snake-oil! To be called on memory where
811 * we stored passphrases or so, after we used them. */
813 return memory_erase(x, strlen(x));
816 char *string_free_erase(char *s) {
817 return mfree(string_erase(s));
820 bool string_is_safe(const char *p) {
826 for (t = p; *t; t++) {
827 if (*t > 0 && *t < ' ') /* no control characters */
830 if (strchr(QUOTES "\\\x7f", *t))