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;
399 assert(percent <= 100);
400 assert(new_length >= 3);
402 /* if no multibyte characters use ascii_ellipsize_mem for speed */
403 if (ascii_is_valid(s))
404 return ascii_ellipsize_mem(s, old_length, new_length, percent);
406 if (old_length <= 3 || old_length <= new_length)
407 return strndup(s, old_length);
409 x = (new_length * percent) / 100;
411 if (x > new_length - 3)
415 for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
418 c = utf8_encoded_to_unichar(i);
421 k += unichar_iswide(c) ? 2 : 1;
424 if (k > x) /* last character was wide and went over quota */
427 for (j = s + old_length; k < new_length && j > i; ) {
430 j = utf8_prev_char(j);
431 c = utf8_encoded_to_unichar(j);
434 k += unichar_iswide(c) ? 2 : 1;
438 /* we don't actually need to ellipsize */
440 return memdup(s, old_length + 1);
442 /* make space for ellipsis */
443 j = utf8_next_char(j);
446 len2 = s + old_length - j;
447 e = new(char, len + 3 + len2 + 1);
452 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
453 old_length, new_length, x, len, len2, k);
457 e[len] = 0xe2; /* tri-dot ellipsis: … */
461 memcpy(e + len + 3, j, len2 + 1);
466 char *ellipsize(const char *s, size_t length, unsigned percent) {
467 return ellipsize_mem(s, strlen(s), length, percent);
470 bool nulstr_contains(const char*nulstr, const char *needle) {
476 NULSTR_FOREACH(i, nulstr)
477 if (streq(i, needle))
483 char* strshorten(char *s, size_t l) {
492 char *strreplace(const char *text, const char *old_string, const char *new_string) {
495 size_t l, old_len, new_len;
501 old_len = strlen(old_string);
502 new_len = strlen(new_string);
515 if (!startswith(f, old_string)) {
521 nl = l - old_len + new_len;
522 a = realloc(r, nl + 1);
530 t = stpcpy(t, new_string);
542 char *strip_tab_ansi(char **ibuf, size_t *_isz) {
543 const char *i, *begin = NULL;
548 } state = STATE_OTHER;
556 /* Strips ANSI color and replaces TABs by 8 spaces */
558 isz = _isz ? *_isz : strlen(*ibuf);
560 f = open_memstream(&obuf, &osz);
564 for (i = *ibuf; i < *ibuf + isz + 1; i++) {
569 if (i >= *ibuf + isz) /* EOT */
571 else if (*i == '\x1B')
572 state = STATE_ESCAPE;
580 if (i >= *ibuf + isz) { /* EOT */
583 } else if (*i == '[') {
584 state = STATE_BRACKET;
596 if (i >= *ibuf + isz || /* EOT */
597 (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
602 } else if (*i == 'm')
625 char *strextend(char **x, ...) {
632 l = f = *x ? strlen(*x) : 0;
639 t = va_arg(ap, const char *);
644 if (n > ((size_t) -1) - l) {
653 r = realloc(*x, l+1);
663 t = va_arg(ap, const char *);
677 char *strrep(const char *s, unsigned n) {
685 p = r = malloc(l * n + 1);
689 for (i = 0; i < n; i++)
696 int split_pair(const char *s, const char *sep, char **l, char **r) {
711 a = strndup(s, x - s);
715 b = strdup(x + strlen(sep));
727 int free_and_strdup(char **p, const char *s) {
732 /* Replaces a string pointer with an strdup()ed new string,
733 * possibly freeing the old one. */
735 if (streq_ptr(*p, s))
751 #pragma GCC push_options
752 #pragma GCC optimize("O0")
754 void* memory_erase(void *p, size_t l) {
755 volatile uint8_t* x = (volatile uint8_t*) p;
757 /* This basically does what memset() does, but hopefully isn't
758 * optimized away by the compiler. One of those days, when
759 * glibc learns memset_s() we should replace this call by
760 * memset_s(), but until then this has to do. */
768 #pragma GCC pop_options
770 char* string_erase(char *x) {
775 /* A delicious drop of snake-oil! To be called on memory where
776 * we stored passphrases or so, after we used them. */
778 return memory_erase(x, strlen(x));
781 char *string_free_erase(char *s) {
782 return mfree(string_erase(s));
785 bool string_is_safe(const char *p) {
791 for (t = p; *t; t++) {
792 if (*t > 0 && *t < ' ') /* no control characters */
795 if (strchr(QUOTES "\\\x7f", *t))