-/*
- * Copyright (C) 2004-2014 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
+/* Copyright (C) 2004 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
/** \file string.c Memory allocation and string handling functions. */
-#define _GNU_SOURCE
+#include "para.h"
#include <pwd.h>
#include <sys/utsname.h> /* uname() */
-
-#include <string.h>
#include <regex.h>
-
#include <langinfo.h>
#include <wchar.h>
#include <wctype.h>
-#include "para.h"
#include "string.h"
#include "error.h"
/**
- * Paraslash's version of realloc().
+ * Reallocate an array, abort on failure or bugs.
*
- * \param p Pointer to the memory block, may be \p NULL.
- * \param size The desired new size.
+ * \param ptr Pointer to the memory block, may be NULL.
+ * \param nmemb Number of elements.
+ * \param size The size of one element in bytes.
*
- * A wrapper for realloc(3). It calls \p exit(\p EXIT_FAILURE) on errors,
- * i.e. there is no need to check the return value in the caller.
+ * A wrapper for realloc(3) which aborts on invalid arguments or integer
+ * overflow. The wrapper also terminates the current process on allocation
+ * errors, so the caller does not need to check for failure.
*
- * \return A pointer to the newly allocated memory, which is suitably aligned
- * for any kind of variable and may be different from \a p.
+ * \return A pointer to newly allocated memory which is suitably aligned for
+ * any kind of variable and may be different from ptr.
*
* \sa realloc(3).
*/
-__must_check void *para_realloc(void *p, size_t size)
+__must_check void *arr_realloc(void *ptr, size_t nmemb, size_t size)
{
+ size_t pr;
+
+ assert(size > 0);
+ assert(nmemb > 0);
+ assert(!__builtin_mul_overflow(nmemb, size, &pr));
+ assert(pr != 0);
+ ptr = realloc(ptr, pr);
+ assert(ptr);
+ return ptr;
+}
+
+/**
+ * Allocate an array, abort on failure or bugs.
+ *
+ * \param nmemb See \ref arr_realloc().
+ * \param size See \ref arr_realloc().
+ *
+ * Like \ref arr_realloc(), this aborts on invalid arguments, integer overflow
+ * and allocation errors.
+ *
+ * \return A pointer to newly allocated memory which is suitably aligned for
+ * any kind of variable.
+ *
+ * \sa See \ref arr_realloc().
+ */
+__must_check __malloc void *arr_alloc(size_t nmemb, size_t size)
+{
+ return arr_realloc(NULL, nmemb, size);
+}
+
+/**
+ * Allocate and initialize an array, abort on failure or bugs.
+ *
+ * \param nmemb See \ref arr_realloc().
+ * \param size See \ref arr_realloc().
+ *
+ * This calls \ref arr_alloc() and zeroes-out the array.
+ *
+ * \return See \ref arr_alloc().
+ */
+__must_check __malloc void *arr_zalloc(size_t nmemb, size_t size)
+{
+ void *ptr = arr_alloc(nmemb, size);
+
/*
- * No need to check for NULL pointers: If p is NULL, the call
- * to realloc is equivalent to malloc(size)
+ * This multiplication can not overflow because the above call to \ref
+ * arr_alloc() aborts on overflow.
*/
- assert(size);
- if (!(p = realloc(p, size))) {
- PARA_EMERG_LOG("realloc failed (size = %zu), aborting\n",
- size);
- exit(EXIT_FAILURE);
- }
- return p;
+ memset(ptr, 0, nmemb * size);
+ return ptr;
}
/**
- * Paraslash's version of malloc().
+ * Allocate and initialize memory.
*
* \param size The desired new size.
*
- * A wrapper for malloc(3) which exits on errors.
- *
- * \return A pointer to the allocated memory, which is suitably aligned for any
- * kind of variable.
+ * \return A pointer to the allocated and zeroed-out memory, which is suitably
+ * aligned for any kind of variable.
*
- * \sa malloc(3).
+ * \sa \ref alloc(), calloc(3).
*/
-__must_check __malloc void *para_malloc(size_t size)
+__must_check void *zalloc(size_t size)
{
- void *p;
-
- assert(size);
- p = malloc(size);
- if (!p) {
- PARA_EMERG_LOG("malloc failed (size = %zu), aborting\n",
- size);
- exit(EXIT_FAILURE);
- }
- return p;
+ return arr_zalloc(1, size);
}
/**
- * Paraslash's version of calloc().
+ * Paraslash's version of realloc().
*
+ * \param p Pointer to the memory block, may be \p NULL.
* \param size The desired new size.
*
- * A wrapper for calloc(3) which exits on errors.
+ * A wrapper for realloc(3). It calls \p exit(\p EXIT_FAILURE) on errors,
+ * i.e. there is no need to check the return value in the caller.
*
- * \return A pointer to the allocated and zeroed-out memory, which is suitably
- * aligned for any kind of variable.
+ * \return A pointer to newly allocated memory which is suitably aligned for
+ * any kind of variable and may be different from \a p.
*
- * \sa calloc(3)
+ * \sa realloc(3).
*/
-__must_check __malloc void *para_calloc(size_t size)
+__must_check void *para_realloc(void *p, size_t size)
{
- void *ret = para_malloc(size);
+ return arr_realloc(p, 1, size);
+}
- memset(ret, 0, size);
- return ret;
+/**
+ * Paraslash's version of malloc().
+ *
+ * \param size The desired new size.
+ *
+ * A wrapper for malloc(3) which exits on errors.
+ *
+ * \return A pointer to the allocated memory, which is suitably aligned for any
+ * kind of variable.
+ *
+ * \sa malloc(3).
+ */
+__must_check __malloc void *alloc(size_t size)
+{
+ return arr_alloc(1, size);
}
/**
*
* \param s The string to be duplicated.
*
- * A wrapper for strdup(3). It calls \p exit(EXIT_FAILURE) on errors, i.e.
- * there is no need to check the return value in the caller.
+ * A strdup(3)-like function which aborts if insufficient memory was available
+ * to allocate the duplicated string, absolving the caller from the
+ * responsibility to check for failure.
*
- * \return A pointer to the duplicated string. If \a s was the \p NULL pointer,
- * an pointer to an empty string is returned.
+ * \return A pointer to the duplicated string. Unlike strdup(3), the caller may
+ * pass NULL, in which case the function returns a pointer to an empty string.
+ * Regardless of whether or not NULL was passed, the returned string is
+ * allocated on the heap and has to be freed by the caller.
*
- * \sa strdup(3)
+ * \sa strdup(3).
*/
__must_check __malloc char *para_strdup(const char *s)
{
- char *ret;
+ char *dupped_string = strdup(s? s: "");
- if ((ret = strdup(s? s: "")))
- return ret;
- PARA_EMERG_LOG("strdup failed, aborting\n");
- exit(EXIT_FAILURE);
+ assert(dupped_string);
+ return dupped_string;
}
/**
- * Print a formated message to a dynamically allocated string.
+ * Print a formatted message to a dynamically allocated string.
*
- * \param result The formated string is returned here.
+ * \param result The formatted string is returned here.
* \param fmt The format string.
* \param ap Initialized list of arguments.
*
size_t size = 150;
va_list aq;
- *result = para_malloc(size + 1);
+ *result = alloc(size + 1);
va_copy(aq, ap);
ret = vsnprintf(*result, size, fmt, aq);
va_end(aq);
* \return This function either returns a pointer to a string that must be
* freed by the caller or aborts without returning.
*
- * \sa printf(3), xasprintf().
+ * \sa printf(3), \ref xasprintf().
*/
__must_check __printf_1_2 __malloc char *make_message(const char *fmt, ...)
{
}
/**
- * Free the content of a pointer and set it to \p NULL.
+ * Free the content of a pointer and set it to NULL.
*
- * This is equivalent to "free(*arg); *arg = NULL;".
+ * \param arg A pointer to the pointer whose content should be freed.
*
- * \param arg The pointer whose content should be freed.
+ * If arg is NULL, the function returns immediately. Otherwise it frees the
+ * memory pointed to by *arg and sets *arg to NULL. Hence callers have to pass
+ * the *address* of the pointer variable that points to the memory which should
+ * be freed.
*/
void freep(void *arg)
{
- void **ptr = (void **)arg;
- free(*ptr);
- *ptr = NULL;
+ if (arg) {
+ void **ptr = arg;
+ free(*ptr);
+ *ptr = NULL;
+ }
}
/**
* return \a a without making a copy of \a a. Otherwise, construct the
* concatenation \a c, free \a a (but not \a b) and return \a c.
*
- * \sa strcat(3)
+ * \sa strcat(3).
*/
__must_check __malloc char *para_strcat(char *a, const char *b)
{
return tmp;
}
-/**
- * Paraslash's version of dirname().
- *
- * \param name Pointer to the full path.
- *
- * Compute the directory component of \p name.
- *
- * \return If \a name is \p NULL or the empty string, return \p NULL.
- * Otherwise, Make a copy of \a name and return its directory component. Caller
- * is responsible to free the result.
- */
-__must_check __malloc char *para_dirname(const char *name)
-{
- char *p, *ret;
-
- if (!name || !*name)
- return NULL;
- ret = para_strdup(name);
- p = strrchr(ret, '/');
- if (!p)
- *ret = '\0';
- else
- *p = '\0';
- return ret;
-}
-
-/**
- * Paraslash's version of basename().
- *
- * \param name Pointer to the full path.
- *
- * Compute the filename component of \a name.
- *
- * \return \p NULL if (a) \a name is the empty string or \p NULL, or (b) name
- * ends with a slash. Otherwise, a pointer within \a name is returned. Caller
- * must not free the result.
- */
-__must_check char *para_basename(const char *name)
-{
- char *ret;
-
- if (!name || !*name)
- return NULL;
- ret = strrchr(name, '/');
- if (!ret)
- return (char *)name;
- ret++;
- return ret;
-}
-
/**
* Get the logname of the current user.
*
char *next_cr;
next_cr = memchr(start, '\n', buf + size - start);
- next_null = memchr(start, '\0', buf + size - start);
+ next_null = memchr(start, '\0', next_cr?
+ next_cr - start : buf + size - start);
if (!next_cr && !next_null)
break;
- if (next_cr && next_null) {
- end = next_cr < next_null? next_cr : next_null;
- } else if (next_null) {
+ if (next_null)
end = next_null;
- } else
+ else
end = next_cr;
num_lines++;
if (!(flags & FELF_DISCARD_FIRST) || start != buf) {
if (flags & FELF_READ_ONLY) {
size_t s = end - start;
- char *b = para_malloc(s + 1);
+ char *b = alloc(s + 1);
memcpy(b, start, s);
b[s] = '\0';
ret = line_handler(b, private_data);
int ret, sz_off = (b->flags & PBF_SIZE_PREFIX)? 5 : 0;
if (!b->buf) {
- b->buf = para_malloc(128);
+ b->buf = alloc(128);
b->size = 128;
b->offset = 0;
}
*
* \return Standard.
*
- * \sa para_atoi32(), strtol(3), atoi(3).
+ * \sa \ref para_atoi32(), strtol(3), atoi(3).
*/
int para_atoi64(const char *str, int64_t *value)
{
tmp = strtoll(str, &endptr, 10);
if (errno == ERANGE && (tmp == LLONG_MAX || tmp == LLONG_MIN))
return -E_ATOI_OVERFLOW;
- if (errno != 0 && tmp == 0) /* other error */
- return -E_STRTOLL;
+ /*
+ * If there were no digits at all, strtoll() stores the original value
+ * of str in *endptr.
+ */
if (endptr == str)
return -E_ATOI_NO_DIGITS;
+ /*
+ * The implementation may also set errno and return 0 in case no
+ * conversion was performed.
+ */
+ if (errno != 0 && tmp == 0)
+ return -E_ATOI_NO_DIGITS;
if (*endptr != '\0') /* Further characters after number */
return -E_ATOI_JUNK_AT_END;
*value = tmp;
*
* \return Standard.
*
- * \sa para_atoi64().
+ * \sa \ref para_atoi64().
*/
int para_atoi32(const char *str, int32_t *value)
{
return 1;
}
-static inline int loglevel_equal(const char *arg, const char * const ll)
-{
- return !strncasecmp(arg, ll, strlen(ll));
-}
-
-/**
- * Compute the loglevel number from its name.
- *
- * \param txt The name of the loglevel (debug, info, ...).
- *
- * \return The numeric representation of the loglevel name.
- */
-int get_loglevel_by_name(const char *txt)
-{
- if (loglevel_equal(txt, "debug"))
- return LL_DEBUG;
- if (loglevel_equal(txt, "info"))
- return LL_INFO;
- if (loglevel_equal(txt, "notice"))
- return LL_NOTICE;
- if (loglevel_equal(txt, "warning"))
- return LL_WARNING;
- if (loglevel_equal(txt, "error"))
- return LL_ERROR;
- if (loglevel_equal(txt, "crit"))
- return LL_CRIT;
- if (loglevel_equal(txt, "emerg"))
- return LL_EMERG;
- return -1;
-}
-
static int get_next_word(const char *buf, const char *delim, char **word)
{
enum line_state_flags {LSF_HAVE_WORD = 1, LSF_BACKSLASH = 2,
char *out;
int ret, state = 0;
- out = para_malloc(strlen(buf) + 1);
+ out = alloc(strlen(buf) + 1);
*out = '\0';
*word = out;
for (in = buf; *in; in++) {
static int create_argv_offset(int offset, const char *buf, const char *delim,
char ***result)
{
- char *word, **argv = para_malloc((offset + 1) * sizeof(char *));
+ char *word, **argv = arr_alloc(offset + 1, sizeof(char *));
const char *p;
int i, ret;
goto err;
if (!ret)
break;
- argv = para_realloc(argv, (i + 2) * sizeof(char*));
+ argv = arr_realloc(argv, i + 2, sizeof(char*));
argv[i] = word;
}
argv[i] = NULL;
* Split a buffer into words.
*
* This parser honors single and double quotes, backslash-escaped characters
- * and special characters like \p \\n. The result contains pointers to copies
- * of the words contained in \a buf and has to be freed by using \ref
- * free_argv().
+ * and special characters like \\n. The result contains pointers to copies of
+ * the words contained in buf and has to be freed by using \ref free_argv().
*
* \param buf The buffer to be split.
* \param delim Each character in this string is treated as a separator.
* \param result The array of words is returned here.
*
- * \return Number of words in \a buf, negative on errors.
+ * It's OK to pass NULL as the buffer argument. This is equivalent to passing
+ * the empty string.
+ *
+ * \return Number of words in buf, negative on errors. The array returned
+ * through the result pointer is NULL terminated.
*/
int create_argv(const char *buf, const char *delim, char ***result)
{
if (ret == 0)
return 1;
size = regerror(ret, preg, NULL, 0);
- buf = para_malloc(size);
+ buf = alloc(size);
regerror(ret, preg, buf, size);
PARA_ERROR_LOG("%s\n", buf);
free(buf);
char *p;
assert(len < (size_t)-1);
- p = para_malloc(len + 1);
+ p = alloc(len + 1);
if (len > 0)
memcpy(p, src, len);
p[len] = '\0';
return have_utf8;
}
-/*
- * glibc's wcswidth returns -1 if the string contains a tab character, which
- * makes the function next to useless. The two functions below are taken from
- * mutt.
- */
-
-#define IsWPrint(wc) (iswprint(wc) || wc >= 0xa0)
-
-static int mutt_wcwidth(wchar_t wc, size_t pos)
+static int xwcwidth(wchar_t wc, size_t pos)
{
int n;
+ /* special-case for tab */
if (wc == 0x09) /* tab */
return (pos | 7) + 1 - pos;
n = wcwidth(wc);
- if (IsWPrint(wc) && n > 0)
- return n;
- if (!(wc & ~0x7f))
- return 2;
- if (!(wc & ~0xffff))
- return 6;
- return 10;
+ /* wcswidth() returns -1 for non-printable characters */
+ return n >= 0? n : 1;
}
-static size_t mutt_wcswidth(const wchar_t *s, size_t n)
+static size_t xwcswidth(const wchar_t *s, size_t n)
{
size_t w = 0;
while (n--)
- w += mutt_wcwidth(*s++, w);
+ w += xwcwidth(*s++, w);
return w;
}
if (mbret == (size_t)-1 || mbret == (size_t)-2)
return -ERRNO_TO_PARA_ERROR(EILSEQ);
bytes_parsed += mbret;
- cells_skipped += mutt_wcwidth(wc, cells_skipped);
+ cells_skipped += xwcwidth(wc, cells_skipped);
}
*bytes_to_skip = bytes_parsed;
return 1;
return -ERRNO_TO_PARA_ERROR(errno);
if (num_wchars == 0)
return 0;
- dest = para_malloc(num_wchars * sizeof(*dest));
+ dest = arr_alloc(num_wchars + 1, sizeof(*dest));
src = s;
memset(&state, 0, sizeof(state));
num_wchars = mbsrtowcs(dest, &src, num_wchars, &state);
assert(num_wchars > 0 && num_wchars != (size_t)-1);
- *result = mutt_wcswidth(dest, num_wchars);
+ *result = xwcswidth(dest, num_wchars);
free(dest);
return 1;
}
+
+/**
+ * Truncate and sanitize a (wide character) string.
+ *
+ * This replaces all non-printable characters by spaces and makes sure that the
+ * modified string does not exceed the given maximal width.
+ *
+ * \param src The source string in multi-byte form.
+ * \param max_width The maximal number of cells the result may occupy.
+ * \param result Sanitized multi-byte string, must be freed by caller.
+ * \param width The width of the sanitized string, always <= max_width.
+ *
+ * The function is wide-character aware but falls back to C strings for
+ * non-UTF-8 locales.
+ *
+ * \return Standard. On success, *result points to a sanitized copy of the
+ * given string. This copy was allocated with malloc() and should hence be
+ * freed when the caller is no longer interested in the result.
+ *
+ * The function fails if the given string contains an invalid multibyte
+ * sequence. In this case, *result is set to NULL, and *width to zero.
+ */
+__must_check int sanitize_str(const char *src, size_t max_width,
+ char **result, size_t *width)
+{
+ mbstate_t state;
+ static wchar_t *wcs;
+ size_t num_wchars, n;
+
+ if (!utf8_mode()) {
+ *result = para_strdup(src);
+ /* replace non-printable characters by spaces */
+ for (n = 0; n < max_width && src[n]; n++) {
+ if (!isprint((unsigned char)src[n]))
+ (*result)[n] = ' ';
+ }
+ (*result)[n] = '\0';
+ *width = n;
+ return 0;
+ }
+ *result = NULL;
+ *width = 0;
+ memset(&state, 0, sizeof(state));
+ num_wchars = mbsrtowcs(NULL, &src, 0, &state);
+ if (num_wchars == (size_t)-1)
+ return -ERRNO_TO_PARA_ERROR(errno);
+ wcs = arr_alloc(num_wchars + 1, sizeof(*wcs));
+ memset(&state, 0, sizeof(state));
+ num_wchars = mbsrtowcs(wcs, &src, num_wchars + 1, &state);
+ assert(num_wchars != (size_t)-1);
+ for (n = 0; n < num_wchars && *width < max_width; n++) {
+ if (!iswprint(wcs[n]))
+ wcs[n] = L' ';
+ *width += xwcwidth(wcs[n], *width);
+ }
+ wcs[n] = L'\0';
+ n = wcstombs(NULL, wcs, 0) + 1;
+ *result = alloc(n);
+ num_wchars = wcstombs(*result, wcs, n);
+ assert(num_wchars != (size_t)-1);
+ free(wcs);
+ return 1;
+}