+/* 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>
#include "gunicode.h"
#include "macro.h"
#include "string-util.h"
+#include "terminal-util.h"
#include "utf8.h"
#include "util.h"
}
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;
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 */
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))
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);
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 */
}
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)
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)) {
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,
STATE_BRACKET
} state = STATE_OTHER;
char *obuf = NULL;
- size_t osz = 0, isz;
+ size_t osz = 0, isz, shift[2] = {};
FILE *f;
assert(ibuf);
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) {
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;
} else {
fputc('\x1B', f);
fputc(*i, f);
+ advance_offsets(i - *ibuf, highlight, shift, 1);
state = STATE_OTHER;
}
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')
if (ferror(f)) {
fclose(f);
- free(obuf);
- return NULL;
+ return mfree(obuf);
}
fclose(f);
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;
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;
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;
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
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) {