X-Git-Url: http://git.tuebingen.mpg.de/?a=blobdiff_plain;f=string.c;h=46b346235f17ffb696f43d22d3dcfde4c75ba4ca;hb=be5daec66f163725489f42d512659a8af5c3fd59;hp=f8b64b77c08d0c109c62129bdfeac04b775b3d6b;hpb=9a67f9e1e37589b548fc1823a21ffdf0b6faf4ea;p=paraslash.git diff --git a/string.c b/string.c index f8b64b77..46b34623 100644 --- a/string.c +++ b/string.c @@ -1,100 +1,138 @@ -/* - * Copyright (C) 2004 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ +/* Copyright (C) 2004 Andre Noll , see file COPYING. */ /** \file string.c Memory allocation and string handling functions. */ -#define _GNU_SOURCE +#include "para.h" #include #include /* uname() */ - -#include #include - #include #include #include -#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 newly allocated memory which is suitably aligned for - * any kind of variable and may be different from \a p. + * 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); } /** @@ -102,28 +140,29 @@ __must_check __malloc void *para_calloc(size_t 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. * @@ -145,7 +184,7 @@ __printf_2_0 unsigned xvasprintf(char **result, const char *fmt, va_list ap) 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); @@ -193,7 +232,7 @@ __printf_2_3 unsigned xasprintf(char **result, const char *fmt, ...) * \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, ...) { @@ -207,17 +246,22 @@ __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; + } } /** @@ -233,7 +277,7 @@ void freep(void *arg) * 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) { @@ -248,56 +292,6 @@ __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. * @@ -392,7 +386,7 @@ int for_each_line(unsigned flags, char *buf, size_t size, 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); @@ -496,7 +490,7 @@ __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...) 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; } @@ -551,7 +545,7 @@ __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...) * * \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) { @@ -562,10 +556,18 @@ 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; @@ -580,7 +582,7 @@ int para_atoi64(const char *str, int64_t *value) * * \return Standard. * - * \sa para_atoi64(). + * \sa \ref para_atoi64(). */ int para_atoi32(const char *str, int32_t *value) { @@ -597,37 +599,6 @@ 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, @@ -636,7 +607,7 @@ static int get_next_word(const char *buf, const char *delim, char **word) 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++) { @@ -768,7 +739,7 @@ void free_argv(char **argv) 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; @@ -780,7 +751,7 @@ static int create_argv_offset(int offset, const char *buf, const char *delim, 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; @@ -798,15 +769,18 @@ err: * 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) { @@ -832,27 +806,6 @@ int create_shifted_argv(const char *buf, const char *delim, char ***result) return create_argv_offset(1, buf, delim, result); } -/** - * Find out if the given string is contained in the arg vector. - * - * \param arg The string to look for. - * \param argv The array to search. - * - * \return The first index whose value equals \a arg, or \p -E_ARG_NOT_FOUND if - * arg was not found in \a argv. - */ -int find_arg(const char *arg, char **argv) -{ - int i; - - if (!argv) - return -E_ARG_NOT_FOUND; - for (i = 0; argv[i]; i++) - if (strcmp(arg, argv[i]) == 0) - return i; - return -E_ARG_NOT_FOUND; -} - /** * Compile a regular expression. * @@ -873,7 +826,7 @@ int para_regcomp(regex_t *preg, const char *regex, int cflags) 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); @@ -899,7 +852,7 @@ char *safe_strdup(const char *src, size_t len) 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'; @@ -947,36 +900,24 @@ static bool utf8_mode(void) 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; } @@ -1021,7 +962,7 @@ int skip_cells(const char *s, size_t cells_to_skip, size_t *bytes_to_skip) 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; @@ -1065,12 +1006,75 @@ __must_check int strwidth(const char *s, size_t *result) 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; +}