# HG changeset patch # User Tero Marttila # Date 1239418988 -10800 # Node ID 361740b82fe5f096211f1f7ec8ea327c22f603f1 # Parent 6c5a5fdfd1d514a9e5926cc331c97239f021f2e7 implement str_format with tests diff -r 6c5a5fdfd1d5 -r 361740b82fe5 src/error.h --- a/src/error.h Sat Apr 11 04:41:44 2009 +0300 +++ b/src/error.h Sat Apr 11 06:03:08 2009 +0300 @@ -113,6 +113,9 @@ ERR_PCRE_COMPILE, ///< pcre_compile: ERR_PCRE_EXEC, ///< pcre_exec: + /** str errors */ + _ERR_STR = 0x000f00, + /** General errors */ _ERR_GENERAL = 0xffff00, ERR_CMD_OPT, diff -r 6c5a5fdfd1d5 -r 361740b82fe5 src/str.c --- a/src/str.c Sat Apr 11 04:41:44 2009 +0300 +++ b/src/str.c Sat Apr 11 06:03:08 2009 +0300 @@ -4,18 +4,21 @@ #include #include #include +#include + #include -size_t str_append (char *buf, size_t buf_size, const char *str) +size_t str_append_num (char *buf, size_t buf_size, const char *str, ssize_t len) { size_t str_len; // copy the bytes - for (str_len = 0; *str && buf_size > 1; str_len++, buf_size--) + // len will either be >0 or <0 + for (str_len = 0; *str && buf_size > 1 && len != 0; str_len++, buf_size--, len--) *buf++ = *str++; // count the rest - for (; *str; str++) + for (; *str && len != 0; str++, len--) str_len++; if (buf_size) @@ -24,6 +27,12 @@ // ok return str_len; + +} + +size_t str_append (char *buf, size_t buf_size, const char *str) +{ + return str_append_num(buf, buf_size, str, -1); } size_t str_append_char (char *buf, size_t buf_size, char c) @@ -158,3 +167,140 @@ return data_size; } +/** + * Output the data for a single parameter + */ +static err_t str_format_param (char **buf, size_t *buf_size, const char *name, const char *flags, str_format_cb func, void *arg) +{ + const char *value; + ssize_t value_len = -1; + char flag; + bool use_quote = false; + err_t err; + + // look it up + if ((err = func(name, &value, &value_len, arg))) + return err; + + // not found? + if (!value) + return ERR_STR_FMT_NAME; + + // parse flags + while ((flag = *flags++)) { + switch (flag) { + case 'r': + // quote + use_quote = true; + + break; + + default: + // unknown flag + return ERR_STR_FMT_FLAG; + + } + } + + // output value + if (use_quote) + *buf += str_advance(NULL, buf_size, str_quote(*buf, *buf_size, value, value_len)); + + else + *buf += str_advance(NULL, buf_size, str_append_num(*buf, *buf_size, value, value_len)); + + // ok + return SUCCESS; +} + +err_t str_format (char *buf, size_t buf_size, const char *format, str_format_cb func, void *arg) +{ + char name_buf[STR_FORMAT_PARAM_MAX + 1], *name_ptr; + size_t name_size; + char flags_buf[STR_FORMAT_FLAGS_MAX + 1], *flags_ptr; + size_t flags_size; + bool in_param = false, in_param_flags = false; + err_t err; + + // iterate over the format string + do { + // check buffer state + if (!buf_size) + return ERR_STR_FMT_BUF_LEN; + + // inspect this char + switch (*format) { + case '{': + // syntax + if (in_param) + return ERR_STR_FMT_TAG; + + // init state + in_param = true; + name_ptr = name_buf; + name_size = sizeof(name_buf); + flags_ptr = flags_buf; + flags_size = sizeof(flags_buf); + + *name_ptr = *flags_ptr = '\0'; + + break; + + + case '}': + // syntax + if (!in_param) + return ERR_STR_FMT_TAG; + + // reset state + in_param = false; + in_param_flags = false; + + // output token + if ((err = str_format_param(&buf, &buf_size, name_buf, flags_buf, func, arg))) + return err; + + break; + + case ':': + if (in_param) { + // set state + in_param_flags = true; + + break; + } + + /* Fallthrough */ + + default: + if (in_param && in_param_flags ) { + // add to param flags + flags_ptr += str_advance(NULL, &flags_size, str_append_char(flags_ptr, flags_size, *format)); + + if (!flags_size) + return ERR_STR_FMT_FLAGS_LEN; + + + } else if (in_param) { + // add to param name + name_ptr += str_advance(NULL, &name_size, str_append_char(name_ptr, name_size, *format)); + + if (!name_size) + return ERR_STR_FMT_NAME_LEN; + } else { + // add to output + buf += str_advance(NULL, &buf_size, str_append_char(buf, buf_size, *format)); + + } + + break; + } + } while (*format++); + + // syntax + if (in_param) + return ERR_STR_FMT_TAG; + + // ok + return SUCCESS; +} diff -r 6c5a5fdfd1d5 -r 361740b82fe5 src/str.h --- a/src/str.h Sat Apr 11 04:41:44 2009 +0300 +++ b/src/str.h Sat Apr 11 06:03:08 2009 +0300 @@ -7,12 +7,34 @@ * Miscellaneous string utility functions */ #include +#include "error.h" /** - * Writes the given string into the given buffer, writing at most buf_size bytes, including the terminating NUL. + * Error codes + */ +enum str_error_code { + _ERR_STR_BEGIN = _ERR_STR, + ERR_STR_FMT_TAG, ///< invalid parameter tag syntax + ERR_STR_FMT_NAME_LEN, ///< invalid parameter name length + ERR_STR_FMT_NAME, ///< invalid/unknown parameter name + ERR_STR_FMT_FLAGS_LEN, ///< invalid paramter flags length + ERR_STR_FMT_FLAG, ///< invalid paramter flag + ERR_STR_FMT_BUF_LEN, ///< output buffer ran out +}; + +/** + * Writes the given string into the given buffer, reading at most len bytes (or up to NUL if -1), and writing at most + * buf_size bytes, including the terminating NUL. * * Returns the number of input bytes that would have been written, not including the terminating NUL. */ +size_t str_append_num (char *buf, size_t buf_size, const char *str, ssize_t len); + +/** + * Like str_append_num with len = -1. + * + * @see str_append_num() + */ size_t str_append (char *buf, size_t buf_size, const char *str); /** @@ -60,6 +82,32 @@ * @param str_len number of bytes of input to process, or -1 to use strlen() * @return the total number of bytes that would have been written out, may be more than buf_size */ -size_t str_quote (char *buf_ptr, size_t buf_size, const char *str, ssize_t str_len); +size_t str_quote (char *buf, size_t buf_size, const char *str, ssize_t str_len); + +/** + * Callback function used by str_format to look up a value for a parameter. + * + * @param name the name of the paramter in the format string + * @param value returned pointer to param value + * @param value_len returned param value length, or -1 for strlen + * @param arg the context arg given to str_format + * @return the parameter value, or NULL to error out + */ +typedef err_t (*str_format_cb) (const char *name, const char **value, ssize_t *value_len, void *arg); + +/** + * Maximum length of a parameter name + */ +#define STR_FORMAT_PARAM_MAX 32 + +/** + * Maximum length of a parameter flags section + */ +#define STR_FORMAT_FLAGS_MAX 8 + +/** + * Format an output string based on the given template, filling in parameter values using a callback function. + */ +err_t str_format (char *buf, size_t buf_size, const char *format, str_format_cb func, void *arg); #endif diff -r 6c5a5fdfd1d5 -r 361740b82fe5 src/test.c --- a/src/test.c Sat Apr 11 04:41:44 2009 +0300 +++ b/src/test.c Sat Apr 11 06:03:08 2009 +0300 @@ -250,6 +250,44 @@ assert_str_quote(9, "1234567890", -1, "'123'...", 12 ); } +struct str_format_ctx { + const char *name; + + const char *value; +}; + +err_t test_str_format_cb (const char *name, const char **value, ssize_t *value_len, void *arg) +{ + struct str_format_ctx *ctx = arg; + + assert_strcmp(name, ctx->name); + + *value = ctx->value; + *value_len = -1; + + return SUCCESS; +} + +void assert_str_format (const char *format, const char *name, const char *value, const char *out) +{ + struct str_format_ctx ctx = { name, value }; + char buf[512]; + + assert_success(str_format(buf, sizeof(buf), format, test_str_format_cb, &ctx)); + + log_debug("str_format(%s), { %s:%s } -> %s / %s", format, name, value, buf, out); + + assert_strcmp(buf, out); +} + +void test_str_format (void) +{ + log_info("test str_format()"); + + assert_str_format("foo", NULL, NULL, "foo"); + assert_str_format("foo {bar} quux", "bar", "XXX", "foo XXX quux"); +} + void test_dump_str (void) { log_info("dumping example strings on stdout:"); @@ -1166,6 +1204,7 @@ static struct test _tests[] = { DEF_TEST( str_quote ), + DEF_TEST( str_format ), DEF_TEST( dump_str ), DEF_TEST( sock_test ), DEF_TEST( line_proto ),