X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=string.c;h=bb63120184c79b44270a4ae1ba671e6dd74de5e1;hp=3c80d27cc6b6ad73a95392c17cdb041837ec6ba5;hb=57e0c93e87138fdf1c8793c3d1f3d2880d717548;hpb=00e4d4da1b2c00da139b09d3ed4ab9ad9fba2691 diff --git a/string.c b/string.c index 3c80d27c..bb631201 100644 --- a/string.c +++ b/string.c @@ -6,14 +6,14 @@ /** \file string.c Memory allocation and string handling functions. */ -#include "para.h" -#include "string.h" - #include /* gettimeofday */ #include #include /* uname() */ #include +#include +#include "para.h" +#include "string.h" #include "error.h" /** @@ -33,7 +33,7 @@ __must_check __malloc void *para_realloc(void *p, size_t size) { /* - * No need to check for NULL pointers: If p is NULL, the call + * No need to check for NULL pointers: If p is NULL, the call * to realloc is equivalent to malloc(size) */ assert(size); @@ -64,7 +64,7 @@ __must_check __malloc void *para_malloc(size_t size) assert(size); p = malloc(size); if (!p) { - PARA_EMERG_LOG("malloc failed (size = %zu), aborting\n", + PARA_EMERG_LOG("malloc failed (size = %zu), aborting\n", size); exit(EXIT_FAILURE); } @@ -99,7 +99,7 @@ __must_check __malloc void *para_calloc(size_t size) * 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. * - * \return A pointer to the duplicated string. If \p s was the NULL pointer, + * \return A pointer to the duplicated string. If \a s was the \p NULL pointer, * an pointer to an empty string is returned. * * \sa strdup(3) @@ -200,15 +200,15 @@ __must_check __malloc char *para_dirname(const char *name) * ends with a slash. Otherwise, a pointer within \a name is returned. Caller * must not free the result. */ -__must_check const char *para_basename(const char *name) +__must_check char *para_basename(const char *name) { - const char *ret; + char *ret; if (!name || !*name) return NULL; ret = strrchr(name, '/'); if (!ret) - return name; + return (char *)name; ret++; return ret; } @@ -218,44 +218,24 @@ __must_check const char *para_basename(const char *name) * * \param buf The string to be chopped. * - * Replace the last character in \p buf by zero if it is euqal to + * Replace the last character in \p buf by zero if it is equal to * the newline character. */ void chop(char *buf) { int n = strlen(buf); + if (!n) return; if (buf[n - 1] == '\n') buf[n - 1] = '\0'; } -/** - * Get a random filename. - * - * This is by no means a secure way to create temporary files in a hostile - * direcory like \p /tmp. However, it is OK to use for temp files, fifos, - * sockets that are created in ~/.paraslash. Result must be freed by the - * caller. - * - * \return A pointer to a random filename. - */ -__must_check __malloc char *para_tmpname(void) -{ - struct timeval now; - unsigned int seed; - - gettimeofday(&now, NULL); - seed = now.tv_usec; - srand(seed); - return make_message("%08i", rand()); -} - /** * Get the logname of the current user. * - * \return A dynammically allocated string that must be freed by the caller. On - * errors, the string "unknown user" is returned, i.e. this function never + * \return A dynamically allocated string that must be freed by the caller. On + * errors, the string "unknown_user" is returned, i.e. this function never * returns \p NULL. * * \sa getpwuid(3). @@ -269,7 +249,7 @@ __must_check __malloc char *para_logname(void) /** * Get the home directory of the current user. * - * \return A dynammically allocated string that must be freed by the caller. If + * \return A dynamically allocated string that must be freed by the caller. If * the home directory could not be found, this function returns "/tmp". */ __must_check __malloc char *para_homedir(void) @@ -278,60 +258,10 @@ __must_check __malloc char *para_homedir(void) return para_strdup(pw? pw->pw_dir : "/tmp"); } -/** - * Split string and return pointers to its parts. - * - * \param args The string to be split. - * \param argv_ptr Pointer to the list of substrings. - * \param delim Delimiter. - * - * This function modifies \a args by replacing each occurance of \a delim by - * zero. A \p NULL-terminated array of pointers to char* is allocated dynamically - * and these pointers are initialized to point to the broken-up substrings - * within \a args. A pointer to this array is returned via \a argv_ptr. - * - * \return The number of substrings found in \a args. - */ -__must_check unsigned split_args(char *args, char *** const argv_ptr, const char *delim) -{ - char *p = args; - char **argv; - size_t n = 0, i, j; - - p = args + strspn(args, delim); - for (;;) { - i = strcspn(p, delim); - if (!i) - break; - p += i; - n++; - p += strspn(p, delim); - } - *argv_ptr = para_malloc((n + 1) * sizeof(char *)); - argv = *argv_ptr; - i = 0; - p = args + strspn(args, delim); - while (p) { - argv[i] = p; - j = strcspn(p, delim); - if (!j) - break; - p += strcspn(p, delim); - if (*p) { - *p = '\0'; - p++; - p += strspn(p, delim); - } - i++; - } - argv[n] = NULL; - return n; -} - /** * Get the own hostname. * - * \return A dynammically allocated string containing the hostname. + * \return A dynamically allocated string containing the hostname. * * \sa uname(2). */ @@ -408,7 +338,7 @@ static int for_each_complete_line(enum for_each_line_modes mode, char *buf, /** * Call a custom function for each complete line. * - * \param buf The buffer containing data seperated by newlines. + * \param buf The buffer containing data separated by newlines. * \param size The number of bytes in \a buf. * \param line_handler The custom function. * \param private_data Pointer passed to \a line_handler. @@ -456,8 +386,42 @@ int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler, private_data); } +#define hex(a) (hexchar[(a) & 15]) +static void write_size_header(char *buf, int n) +{ + static char hexchar[] = "0123456789abcdef"; + + buf[0] = hex(n >> 12); + buf[1] = hex(n >> 8); + buf[2] = hex(n >> 4); + buf[3] = hex(n); + buf[4] = ' '; +} + +int read_size_header(const char *buf) +{ + int i, len = 0; + + for (i = 0; i < 4; i++) { + unsigned char c = buf[i]; + len <<= 4; + if (c >= '0' && c <= '9') { + len += c - '0'; + continue; + } + if (c >= 'a' && c <= 'f') { + len += c - 'a' + 10; + continue; + } + return -E_SIZE_PREFIX; + } + if (buf[4] != ' ') + return -E_SIZE_PREFIX; + return len; +} + /** - * Safely print into a buffer at a given offset + * Safely print into a buffer at a given offset. * * \param b Determines the buffer, its size, and the offset. * \param fmt The format string. @@ -465,13 +429,14 @@ int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler, * This function prints into the buffer given by \a b at the offset which is * also given by \a b. If there is not enough space to hold the result, the * buffer size is doubled until the underlying call to vsnprintf() succeeds - * or the size of the buffer exceeds the maximal size specified in \a pb. + * or the size of the buffer exceeds the maximal size specified in \a b. * * In the latter case the unmodified \a buf and \a offset values as well as the * private_data pointer of \a b are passed to the \a max_size_handler of \a b. * If this function succeeds, i.e. returns a non-negative value, the offset of * \a b is reset to zero and the given data is written to the beginning of the - * buffer. + * buffer. If \a max_size_handler() returns a negative value, this value is + * returned by \a para_printf(). * * Upon return, the offset of \a b is adjusted accordingly so that subsequent * calls to this function append data to what is already contained in the @@ -481,13 +446,15 @@ int for_each_line_ro(char *buf, size_t size, line_handler_t *line_handler, * initial buffer is allocated. * * \return The number of bytes printed into the buffer (not including the - * therminating \p NULL byte). + * terminating \p NULL byte) on success, negative on errors. If there is no + * size-bound on \a b, i.e. if \p b->max_size is zero, this function never + * fails. * * \sa make_message(), vsnprintf(3). */ __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...) { - int ret; + int ret, sz_off = (b->flags & PBF_SIZE_PREFIX)? 5 : 0; if (!b->buf) { b->buf = para_malloc(128); @@ -498,13 +465,16 @@ __printf_2_3 int para_printf(struct para_buffer *b, const char *fmt, ...) char *p = b->buf + b->offset; size_t size = b->size - b->offset; va_list ap; - if (size) { + + if (size > sz_off) { va_start(ap, fmt); - ret = vsnprintf(p, size, fmt, ap); + ret = vsnprintf(p + sz_off, size - sz_off, fmt, ap); va_end(ap); - if (ret > -1 && ret < size) { /* success */ - b->offset += ret; - return ret; + if (ret > -1 && ret < size - sz_off) { /* success */ + b->offset += ret + sz_off; + if (sz_off) + write_size_header(p, ret); + return ret + sz_off; } } /* check if we may grow the buffer */ @@ -587,21 +557,197 @@ 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 (!strcasecmp(txt, "debug")) + if (loglevel_equal(txt, "debug")) return LL_DEBUG; - if (!strcasecmp(txt, "info")) + if (loglevel_equal(txt, "info")) return LL_INFO; - if (!strcasecmp(txt, "notice")) + if (loglevel_equal(txt, "notice")) return LL_NOTICE; - if (!strcasecmp(txt, "warning")) + if (loglevel_equal(txt, "warning")) return LL_WARNING; - if (!strcasecmp(txt, "error")) + if (loglevel_equal(txt, "error")) return LL_ERROR; - if (!strcasecmp(txt, "crit")) + if (loglevel_equal(txt, "crit")) return LL_CRIT; - if (!strcasecmp(txt, "emerg")) + 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, + LSF_SINGLE_QUOTE = 4, LSF_DOUBLE_QUOTE = 8}; + const char *in; + char *out; + int ret, state = 0; + + out = para_malloc(strlen(buf) + 1); + *out = '\0'; + *word = out; + for (in = buf; *in; in++) { + const char *p; + + switch (*in) { + case '\\': + if (state & LSF_BACKSLASH) /* \\ */ + goto copy_char; + state |= LSF_BACKSLASH; + state |= LSF_HAVE_WORD; + continue; + case 'n': + case 't': + if (state & LSF_BACKSLASH) { /* \n or \t */ + *out++ = (*in == 'n')? '\n' : '\t'; + state &= ~LSF_BACKSLASH; + continue; + } + goto copy_char; + case '"': + if (state & LSF_BACKSLASH) /* \" */ + goto copy_char; + if (state & LSF_SINGLE_QUOTE) /* '" */ + goto copy_char; + if (state & LSF_DOUBLE_QUOTE) { + state &= ~LSF_DOUBLE_QUOTE; + continue; + } + state |= LSF_HAVE_WORD; + state |= LSF_DOUBLE_QUOTE; + continue; + case '\'': + if (state & LSF_BACKSLASH) /* \' */ + goto copy_char; + if (state & LSF_DOUBLE_QUOTE) /* "' */ + goto copy_char; + if (state & LSF_SINGLE_QUOTE) { + state &= ~LSF_SINGLE_QUOTE; + continue; + } + state |= LSF_HAVE_WORD; + state |= LSF_SINGLE_QUOTE; + continue; + } + for (p = delim; *p; p++) { + if (*in != *p) + continue; + if (state & LSF_BACKSLASH) + goto copy_char; + if (state & LSF_SINGLE_QUOTE) + goto copy_char; + if (state & LSF_DOUBLE_QUOTE) + goto copy_char; + if (state & LSF_HAVE_WORD) + goto success; + break; + } + if (*p) /* ignore delimiter at the beginning */ + continue; +copy_char: + state |= LSF_HAVE_WORD; + *out++ = *in; + state &= ~LSF_BACKSLASH; + } + ret = 0; + if (!(state & LSF_HAVE_WORD)) + goto out; + ret = -ERRNO_TO_PARA_ERROR(EINVAL); + if (state & LSF_BACKSLASH) { + PARA_ERROR_LOG("trailing backslash\n"); + goto out; + } + if ((state & LSF_SINGLE_QUOTE) || (state & LSF_DOUBLE_QUOTE)) { + PARA_ERROR_LOG("unmatched quote character\n"); + goto out; + } +success: + *out = '\0'; + return in - buf; +out: + free(*word); + *word = NULL; + return ret; +} + +/** + * Free an array of words created by create_argv(). + * + * \param argv A pointer previously obtained by \ref create_argv(). + */ +void free_argv(char **argv) +{ + int i; + + for (i = 0; argv[i]; i++) + free(argv[i]); + free(argv); +} + +/** + * 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(). + * + * \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. + */ +int create_argv(const char *buf, const char *delim, char ***result) +{ + char *word, **argv = para_malloc(2 * sizeof(char *)); + const char *p; + int ret, num_words; + + for (p = buf, num_words = 0; ; p += ret, num_words++) { + ret = get_next_word(p, delim, &word); + if (ret < 0) + goto err; + if (!ret) + break; + argv = para_realloc(argv, (num_words + 2) * sizeof(char*)); + argv[num_words] = word; + } + argv[num_words] = NULL; + *result = argv; + return num_words; +err: + while (num_words > 0) + free(argv[--num_words]); + free(argv); + return ret; +} + +int para_regcomp(regex_t *preg, const char *regex, int cflags) +{ + char *buf; + size_t size; + int ret = regcomp(preg, regex, cflags); + + if (ret == 0) + return 1; + size = regerror(ret, preg, NULL, 0); + buf = para_malloc(size); + regerror(ret, preg, buf, size); + PARA_ERROR_LOG("%s\n", buf); + free(buf); + return -E_REGEX; +}