]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/basic/string-util.c
Merge pull request #8575 from keszybz/non-absolute-paths
[thirdparty/systemd.git] / src / basic / string-util.c
index dc7de5dab82538e4cfd969956d3eecb926845bb7..7c75970d7ba1c4156de31ea8ff7bb2f0a293c39e 100644 (file)
@@ -1,26 +1,15 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
   Copyright 2010 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
 #include <errno.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <stdio_ext.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -28,6 +17,7 @@
 #include "gunicode.h"
 #include "macro.h"
 #include "string-util.h"
+#include "terminal-util.h"
 #include "utf8.h"
 #include "util.h"
 
@@ -215,10 +205,10 @@ char *strnappend(const char *s, const char *suffix, size_t b) {
 }
 
 char *strappend(const char *s, const char *suffix) {
-        return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
+        return strnappend(s, suffix, strlen_ptr(suffix));
 }
 
-char *strjoin(const char *x, ...) {
+char *strjoin_real(const char *x, ...) {
         va_list ap;
         size_t l;
         char *r, *p;
@@ -278,6 +268,9 @@ char *strjoin(const char *x, ...) {
 char *strstrip(char *s) {
         char *e;
 
+        if (!s)
+                return NULL;
+
         /* Drops trailing whitespace. Modifies the string in
          * place. Returns pointer to first non-space character */
 
@@ -295,7 +288,13 @@ char *strstrip(char *s) {
 char *delete_chars(char *s, const char *bad) {
         char *f, *t;
 
-        /* Drops all whitespace, regardless where in the string */
+        /* Drops all specified bad characters, regardless where in the string */
+
+        if (!s)
+                return NULL;
+
+        if (!bad)
+                bad = WHITESPACE;
 
         for (f = s, t = s; *f; f++) {
                 if (strchr(bad, *f))
@@ -309,6 +308,26 @@ char *delete_chars(char *s, const char *bad) {
         return s;
 }
 
+char *delete_trailing_chars(char *s, const char *bad) {
+        char *p, *c = s;
+
+        /* Drops all specified bad characters, at the end of the string */
+
+        if (!s)
+                return NULL;
+
+        if (!bad)
+                bad = WHITESPACE;
+
+        for (p = s; *p; p++)
+                if (!strchr(bad, *p))
+                        c = p + 1;
+
+        *c = 0;
+
+        return s;
+}
+
 char *truncate_nl(char *s) {
         assert(s);
 
@@ -472,6 +491,10 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
 
         assert(s);
         assert(percent <= 100);
+
+        if (new_length == (size_t) -1)
+                return strndup(s, old_length);
+
         assert(new_length >= 3);
 
         /* if no multibyte characters use ascii_ellipsize_mem for speed */
@@ -539,10 +562,14 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
 }
 
 char *ellipsize(const char *s, size_t length, unsigned percent) {
+
+        if (length == (size_t) -1)
+                return strdup(s);
+
         return ellipsize_mem(s, strlen(s), length, percent);
 }
 
-bool nulstr_contains(const char*nulstr, const char *needle) {
+bool nulstr_contains(const char *nulstr, const char *needle) {
         const char *i;
 
         if (!nulstr)
@@ -558,33 +585,33 @@ bool nulstr_contains(const char*nulstr, const char *needle) {
 char* strshorten(char *s, size_t l) {
         assert(s);
 
-        if (l < strlen(s))
+        if (strnlen(s, l+1) > l)
                 s[l] = 0;
 
         return s;
 }
 
 char *strreplace(const char *text, const char *old_string, const char *new_string) {
+        size_t l, old_len, new_len, allocated = 0;
+        char *t, *ret = NULL;
         const char *f;
-        char *t, *r;
-        size_t l, old_len, new_len;
 
-        assert(text);
         assert(old_string);
         assert(new_string);
 
+        if (!text)
+                return NULL;
+
         old_len = strlen(old_string);
         new_len = strlen(new_string);
 
         l = strlen(text);
-        r = new(char, l+1);
-        if (!r)
+        if (!GREEDY_REALLOC(ret, allocated, l+1))
                 return NULL;
 
         f = text;
-        t = r;
+        t = ret;
         while (*f) {
-                char *a;
                 size_t d, nl;
 
                 if (!startswith(f, old_string)) {
@@ -592,29 +619,34 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
                         continue;
                 }
 
-                d = t - r;
+                d = t - ret;
                 nl = l - old_len + new_len;
-                a = realloc(r, nl + 1);
-                if (!a)
-                        goto oom;
+
+                if (!GREEDY_REALLOC(ret, allocated, nl + 1))
+                        return mfree(ret);
 
                 l = nl;
-                r = a;
-                t = r + d;
+                t = ret + d;
 
                 t = stpcpy(t, new_string);
                 f += old_len;
         }
 
         *t = 0;
-        return r;
+        return ret;
+}
+
+static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) {
+        if (!offsets)
+                return;
 
-oom:
-        free(r);
-        return NULL;
+        if ((size_t) diff < offsets[0])
+                shift[0] += size;
+        if ((size_t) diff < offsets[1])
+                shift[1] += size;
 }
 
-char *strip_tab_ansi(char **ibuf, size_t *_isz) {
+char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
         const char *i, *begin = NULL;
         enum {
                 STATE_OTHER,
@@ -622,7 +654,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
                 STATE_BRACKET
         } state = STATE_OTHER;
         char *obuf = NULL;
-        size_t osz = 0, isz;
+        size_t osz = 0, isz, shift[2] = {};
         FILE *f;
 
         assert(ibuf);
@@ -636,6 +668,11 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
         if (!f)
                 return NULL;
 
+        /* Note we turn off internal locking on f for performance reasons.  It's safe to do so since we created f here
+         * and it doesn't leave our scope. */
+
+        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+
         for (i = *ibuf; i < *ibuf + isz + 1; i++) {
 
                 switch (state) {
@@ -645,15 +682,18 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
                                 break;
                         else if (*i == '\x1B')
                                 state = STATE_ESCAPE;
-                        else if (*i == '\t')
+                        else if (*i == '\t') {
                                 fputs("        ", f);
-                        else
+                                advance_offsets(i - *ibuf, highlight, shift, 7);
+                        } else
                                 fputc(*i, f);
+
                         break;
 
                 case STATE_ESCAPE:
                         if (i >= *ibuf + isz) { /* EOT */
                                 fputc('\x1B', f);
+                                advance_offsets(i - *ibuf, highlight, shift, 1);
                                 break;
                         } else if (*i == '[') {
                                 state = STATE_BRACKET;
@@ -661,6 +701,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
                         } else {
                                 fputc('\x1B', f);
                                 fputc(*i, f);
+                                advance_offsets(i - *ibuf, highlight, shift, 1);
                                 state = STATE_OTHER;
                         }
 
@@ -669,9 +710,10 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
                 case STATE_BRACKET:
 
                         if (i >= *ibuf + isz || /* EOT */
-                            (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
+                            (!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) {
                                 fputc('\x1B', f);
                                 fputc('[', f);
+                                advance_offsets(i - *ibuf, highlight, shift, 2);
                                 state = STATE_OTHER;
                                 i = begin-1;
                         } else if (*i == 'm')
@@ -682,8 +724,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
 
         if (ferror(f)) {
                 fclose(f);
-                free(obuf);
-                return NULL;
+                return mfree(obuf);
         }
 
         fclose(f);
@@ -694,19 +735,28 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
         if (_isz)
                 *_isz = osz;
 
+        if (highlight) {
+                highlight[0] += shift[0];
+                highlight[1] += shift[1];
+        }
+
         return obuf;
 }
 
-char *strextend(char **x, ...) {
-        va_list ap;
-        size_t f, l;
+char *strextend_with_separator(char **x, const char *separator, ...) {
+        bool need_separator;
+        size_t f, l, l_separator;
         char *r, *p;
+        va_list ap;
 
         assert(x);
 
-        l = f = *x ? strlen(*x) : 0;
+        l = f = strlen_ptr(*x);
 
-        va_start(ap, x);
+        need_separator = !isempty(*x);
+        l_separator = strlen_ptr(separator);
+
+        va_start(ap, separator);
         for (;;) {
                 const char *t;
                 size_t n;
@@ -716,22 +766,29 @@ char *strextend(char **x, ...) {
                         break;
 
                 n = strlen(t);
+
+                if (need_separator)
+                        n += l_separator;
+
                 if (n > ((size_t) -1) - l) {
                         va_end(ap);
                         return NULL;
                 }
 
                 l += n;
+                need_separator = true;
         }
         va_end(ap);
 
+        need_separator = !isempty(*x);
+
         r = realloc(*x, l+1);
         if (!r)
                 return NULL;
 
         p = r + f;
 
-        va_start(ap, x);
+        va_start(ap, separator);
         for (;;) {
                 const char *t;
 
@@ -739,10 +796,17 @@ char *strextend(char **x, ...) {
                 if (!t)
                         break;
 
+                if (need_separator && separator)
+                        p = stpcpy(p, separator);
+
                 p = stpcpy(p, t);
+
+                need_separator = true;
         }
         va_end(ap);
 
+        assert(p == r + l);
+
         *p = 0;
         *x = r;
 
@@ -823,6 +887,7 @@ int free_and_strdup(char **p, const char *s) {
         return 1;
 }
 
+#if !HAVE_EXPLICIT_BZERO
 /*
  * Pointer to memset is volatile so that compiler must de-reference
  * the pointer and can't assume that it points to any function in
@@ -833,19 +898,19 @@ typedef void *(*memset_t)(void *,int,size_t);
 
 static volatile memset_t memset_func = memset;
 
-void* memory_erase(void *p, size_t l) {
-        return memset_func(p, 'x', l);
+void explicit_bzero(void *p, size_t l) {
+        memset_func(p, '\0', l);
 }
+#endif
 
 char* string_erase(char *x) {
-
         if (!x)
                 return NULL;
 
         /* A delicious drop of snake-oil! To be called on memory where
          * we stored passphrases or so, after we used them. */
-
-        return memory_erase(x, strlen(x));
+        explicit_bzero(x, strlen(x));
+        return x;
 }
 
 char *string_free_erase(char *s) {