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_strlower(char *t) {
319 if (*p >= 'A' && *p <= 'Z')
325 bool chars_intersect(const char *a, const char *b) {
328 /* Returns true if any of the chars in a are in b. */
336 bool string_has_cc(const char *p, const char *ok) {
342 * Check if a string contains control characters. If 'ok' is
343 * non-NULL it may be a string containing additional CCs to be
347 for (t = p; *t; t++) {
348 if (ok && strchr(ok, *t))
351 if (*t > 0 && *t < ' ')
361 static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
366 assert(percent <= 100);
367 assert(new_length >= 3);
369 if (old_length <= 3 || old_length <= new_length)
370 return strndup(s, old_length);
372 r = new0(char, new_length+1);
376 x = (new_length * percent) / 100;
378 if (x > new_length - 3)
386 s + old_length - (new_length - x - 3),
392 char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
396 unsigned k, len, len2;
400 assert(percent <= 100);
401 assert(new_length >= 3);
403 /* if no multibyte characters use ascii_ellipsize_mem for speed */
404 if (ascii_is_valid(s))
405 return ascii_ellipsize_mem(s, old_length, new_length, percent);
407 if (old_length <= 3 || old_length <= new_length)
408 return strndup(s, old_length);
410 x = (new_length * percent) / 100;
412 if (x > new_length - 3)
416 for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
419 r = utf8_encoded_to_unichar(i, &c);
422 k += unichar_iswide(c) ? 2 : 1;
425 if (k > x) /* last character was wide and went over quota */
428 for (j = s + old_length; k < new_length && j > i; ) {
431 j = utf8_prev_char(j);
432 r = utf8_encoded_to_unichar(j, &c);
435 k += unichar_iswide(c) ? 2 : 1;
439 /* we don't actually need to ellipsize */
441 return memdup(s, old_length + 1);
443 /* make space for ellipsis */
444 j = utf8_next_char(j);
447 len2 = s + old_length - j;
448 e = new(char, len + 3 + len2 + 1);
453 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
454 old_length, new_length, x, len, len2, k);
458 e[len] = 0xe2; /* tri-dot ellipsis: … */
462 memcpy(e + len + 3, j, len2 + 1);
467 char *ellipsize(const char *s, size_t length, unsigned percent) {
468 return ellipsize_mem(s, strlen(s), length, percent);
471 bool nulstr_contains(const char*nulstr, const char *needle) {
477 NULSTR_FOREACH(i, nulstr)
478 if (streq(i, needle))
484 char* strshorten(char *s, size_t l) {
493 char *strreplace(const char *text, const char *old_string, const char *new_string) {
496 size_t l, old_len, new_len;
502 old_len = strlen(old_string);
503 new_len = strlen(new_string);
516 if (!startswith(f, old_string)) {
522 nl = l - old_len + new_len;
523 a = realloc(r, nl + 1);
531 t = stpcpy(t, new_string);
543 char *strip_tab_ansi(char **ibuf, size_t *_isz) {
544 const char *i, *begin = NULL;
549 } state = STATE_OTHER;
557 /* Strips ANSI color and replaces TABs by 8 spaces */
559 isz = _isz ? *_isz : strlen(*ibuf);
561 f = open_memstream(&obuf, &osz);
565 for (i = *ibuf; i < *ibuf + isz + 1; i++) {
570 if (i >= *ibuf + isz) /* EOT */
572 else if (*i == '\x1B')
573 state = STATE_ESCAPE;
581 if (i >= *ibuf + isz) { /* EOT */
584 } else if (*i == '[') {
585 state = STATE_BRACKET;
597 if (i >= *ibuf + isz || /* EOT */
598 (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
603 } else if (*i == 'm')
626 char *strextend(char **x, ...) {
633 l = f = *x ? strlen(*x) : 0;
640 t = va_arg(ap, const char *);
645 if (n > ((size_t) -1) - l) {
654 r = realloc(*x, l+1);
664 t = va_arg(ap, const char *);
678 char *strrep(const char *s, unsigned n) {
686 p = r = malloc(l * n + 1);
690 for (i = 0; i < n; i++)
697 int split_pair(const char *s, const char *sep, char **l, char **r) {
712 a = strndup(s, x - s);
716 b = strdup(x + strlen(sep));
728 int free_and_strdup(char **p, const char *s) {
733 /* Replaces a string pointer with an strdup()ed new string,
734 * possibly freeing the old one. */
736 if (streq_ptr(*p, s))
752 #pragma GCC push_options
753 #pragma GCC optimize("O0")
755 void* memory_erase(void *p, size_t l) {
756 volatile uint8_t* x = (volatile uint8_t*) p;
758 /* This basically does what memset() does, but hopefully isn't
759 * optimized away by the compiler. One of those days, when
760 * glibc learns memset_s() we should replace this call by
761 * memset_s(), but until then this has to do. */
769 #pragma GCC pop_options
771 char* string_erase(char *x) {
776 /* A delicious drop of snake-oil! To be called on memory where
777 * we stored passphrases or so, after we used them. */
779 return memory_erase(x, strlen(x));
782 char *string_free_erase(char *s) {
783 return mfree(string_erase(s));
786 bool string_is_safe(const char *p) {
792 for (t = p; *t; t++) {
793 if (*t > 0 && *t < ' ') /* no control characters */
796 if (strchr(QUOTES "\\\x7f", *t))