--- 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: <error_msg>
ERR_PCRE_EXEC, ///< pcre_exec: <error code>
+ /** str errors */
+ _ERR_STR = 0x000f00,
+
/** General errors */
_ERR_GENERAL = 0xffff00,
ERR_CMD_OPT,
--- 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 <stdio.h>
#include <stdarg.h>
#include <string.h>
+#include <stdbool.h>
+
#include <assert.h>
-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;
+}
--- 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 <sys/types.h>
+#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
--- 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 ),