]> git.ozlabs.org Git - ccan-lca-2011.git/commitdiff
cdump: first cut of translation of Tridge's genstruct junkcode.
authorRusty Russell <rusty@rustcorp.com.au>
Fri, 21 Jan 2011 03:35:08 +0000 (14:05 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Fri, 21 Jan 2011 03:35:08 +0000 (14:05 +1030)
Mainly, all new bugs.

13 files changed:
ccan/cdump/LICENSE [new symlink]
ccan/cdump/_info [new file with mode: 0644]
ccan/cdump/cdump.c [new file with mode: 0644]
ccan/cdump/cdump.h [new file with mode: 0644]
ccan/cdump/cdump_internal.h [new file with mode: 0644]
ccan/cdump/cdump_parse.c [new file with mode: 0644]
ccan/cdump/cdump_parse.h [new file with mode: 0644]
ccan/cdump/test/example_generated-decls.h [new file with mode: 0644]
ccan/cdump/test/example_generated-defs.h [new file with mode: 0644]
ccan/cdump/test/run-01-common-bundle-unbundle.c [new file with mode: 0644]
ccan/cdump/test/run-02-parse.c [new file with mode: 0644]
ccan/cdump/test/run-save-restore-test.c [new file with mode: 0644]
ccan/cdump/test/test_header.h [new file with mode: 0644]

diff --git a/ccan/cdump/LICENSE b/ccan/cdump/LICENSE
new file mode 120000 (symlink)
index 0000000..190cfd5
--- /dev/null
@@ -0,0 +1 @@
+../../licenses/GPL-3
\ No newline at end of file
diff --git a/ccan/cdump/_info b/ccan/cdump/_info
new file mode 100644 (file)
index 0000000..b04a644
--- /dev/null
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+
+/**
+ * cdump - bundling and unbundling of C data structures
+ *
+ * cdump contains routines to bundle and unbundle C data structures,
+ * especially enums and structs, into a human readable format.
+ *
+ * The cdump_parse tool in the tools directory creates the data structure
+ * descriptions by parsing header files which are annotated with CDUMP_SAVED
+ * (see cdump.h).
+ *
+ * This is based on Andrew Tridgell's "genstruct" project.
+ *
+ * License: GPL
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ */
+int main(int argc, char *argv[])
+{
+       if (argc != 2)
+               return 1;
+
+       if (strcmp(argv[1], "depends") == 0) {
+               printf("ccan/str\n");
+               printf("ccan/talloc\n");
+               return 0;
+       }
+
+       return 1;
+}
diff --git a/ccan/cdump/cdump.c b/ccan/cdump/cdump.c
new file mode 100644 (file)
index 0000000..f151cbf
--- /dev/null
@@ -0,0 +1,756 @@
+/*
+  Based on genstruct by Andrew Tridgell:
+
+   Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 2002
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <ctype.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <ccan/talloc/talloc.h>
+#include "cdump.h"
+#include "cdump_internal.h"
+
+/* intermediate dumps are stored in one of these */
+struct cdump_string {
+       size_t length;
+       char *s;
+};
+
+/* see if a range of memory is all zero. Used to prevent dumping of zero elements */
+static bool all_zero(const char *ptr, size_t size)
+{
+       int i;
+
+       for (i=0;i<size;i++) {
+               if (ptr[i]) return false;
+       }
+       return true;
+}
+
+/* encode a buffer of bytes into a escaped string */
+static char *encode_bytes(const void *ctx, const char *ptr, size_t len)
+{
+       const char *hexdig = "0123456789abcdef";
+       char *ret, *p;
+       size_t i;
+       ret = talloc_array(ctx, char, len*3 + 1); /* worst case size */
+       if (!ret)
+               return NULL;
+       for (p=ret,i=0;i<len;i++) {
+               if (isalnum(ptr[i]) || isspace(ptr[i]) ||
+                   (ispunct(ptr[i]) && !strchr("\\{}", ptr[i]))) {
+                       *p++ = ptr[i];
+               } else {
+                       unsigned char c = *(unsigned char *)(ptr+i);
+                       if (c == 0 && all_zero(ptr+i, len-i)) break;
+                       p[0] = '\\';
+                       p[1] = hexdig[c>>4];
+                       p[2] = hexdig[c&0xF];
+                       p += 3;
+               }
+       }
+
+       *p = 0;
+
+       return ret;
+}
+
+/* decode an escaped string from encode_bytes() into a buffer */
+static char *decode_bytes(const void *ctx, const char *s)
+{
+       char *ret, *p;
+       size_t i;
+
+       ret = talloc_array(ctx, char, strlen(s)+1); /* worst case length */
+       if (!ret)
+               return NULL;
+
+       if (*s == '{') s++;
+
+       for (p=ret,i=0;s[i];i++) {
+               if (s[i] == '}') {
+                       break;
+               } else if (s[i] == '\\') {
+                       unsigned v;
+                       if (sscanf(&s[i+1], "%02x", &v) != 1 || v > 255) {
+                               talloc_free(ret);
+                               return NULL;
+                       }
+                       *(unsigned char *)p = v;
+                       p++;
+                       i += 2;
+               } else {
+                       *p++ = s[i];
+               }
+       }
+       /* Nul-terminate in case we're being used as a string. */
+       *p = 0;
+       return ret;
+}
+
+static char *bundle(const void *ctx,
+                   const struct cdump_desc *info,
+                   const void *data,
+                   unsigned indent);
+
+/* the add*() functions deal with adding things to a struct
+   cdump_string */
+
+/* allocate more space if needed */
+static bool addgen_alloc(struct cdump_string *p, int n)
+{
+       if (p->length + n <= talloc_get_size(p->s)) return true;
+       p->s = talloc_realloc(p, p->s, char, p->length + n + 200);
+       return p->s != NULL;
+}
+
+/* add a character to the buffer */
+static bool addchar(struct cdump_string *p, char c)
+{
+       if (!addgen_alloc(p, 2)) {
+               return false;
+       }
+       p->s[p->length++] = c;
+       p->s[p->length] = 0;
+       return true;
+}
+
+/* add a string to the buffer */
+static bool addstr(struct cdump_string *p, const char *s)
+{
+       int len = strlen(s);
+       if (!addgen_alloc(p, len+1)) {
+               return false;
+       }
+       memcpy(p->s + p->length, s, len+1);
+       p->length += len;
+       return true;
+}
+
+/* add a string to the buffer with a tab prefix */
+static bool addtabbed(struct cdump_string *p, const char *s, unsigned indent)
+{
+       int len = strlen(s);
+       if (!addgen_alloc(p, indent+len+1)) {
+               return false;
+       }
+       while (indent--) {
+               p->s[p->length++] = '\t';
+       }
+       memcpy(p->s + p->length, s, len+1);
+       p->length += len;
+       return true;
+}
+
+/* note! this can only be used for results up to 60 chars wide! */
+static bool addshort(struct cdump_string *p, const char *fmt, ...)
+{
+       char buf[60];
+       int n;
+       va_list ap;
+       va_start(ap, fmt);
+       n = vsnprintf(buf, sizeof(buf), fmt, ap);
+       va_end(ap);
+       if (!addgen_alloc(p, n + 1)) {
+               return false;
+       }
+       memcpy(p->s + p->length, buf, n);
+       p->length += n;
+       p->s[p->length] = 0;
+       return true;
+}
+
+/*
+   this is here to make it easier for people to write dump functions
+   for their own types
+ */
+bool cdump_addstr(struct cdump_string *p, const char *fmt, ...)
+{
+       char *buf;
+       bool ret;
+       va_list ap;
+       va_start(ap, fmt);
+       buf = talloc_vasprintf(NULL, fmt, ap);
+       va_end(ap);
+       if (!buf)
+               return false;
+       ret = addstr(p, buf);
+       talloc_free(buf);
+       return ret;
+}
+
+/* dump a enumerated type */
+bool cdump_bundle_enum(const struct cdump_enum *einfo,
+                      struct cdump_string *p,
+                      const void *ptr,
+                      unsigned indent)
+{
+       unsigned v = *(unsigned *)ptr;
+       int i;
+       for (i=0;einfo[i].name;i++) {
+               if (v == einfo[i].value) {
+                       return addstr(p, einfo[i].name);
+               }
+       }
+       /* hmm, maybe we should just fail? */
+       return cdump_bundle_unsigned(p, ptr, indent);
+}
+
+/* dump a single non-array element, handling struct and enum */
+static bool bundle_one(struct cdump_string *p,
+                        const struct cdump_desc *info,
+                        const void *ptr,
+                        unsigned indent)
+{
+       if (info->bundle == cdump_bundle_char && info->ptr_count == 1) {
+               char *s = encode_bytes(p, ptr, strlen(ptr));
+               if (!s)
+                       return false;
+               if (!addchar(p,'{') ||
+                   !addstr(p, s) ||
+                   !addstr(p, "}")) {
+                       return false;
+               }
+               return true;
+       }
+
+       return info->bundle(p, ptr, indent);
+}
+
+/* handle dumping of an array of arbitrary type */
+static bool bundle_array(struct cdump_string *p,
+                        const struct cdump_desc *info,
+                        const void *ptr,
+                        size_t array_len,
+                        unsigned indent)
+{
+       size_t i, count=0;
+
+       /* special handling of fixed length strings */
+       if (array_len != 0 &&
+           info->ptr_count == 0 &&
+           info->bundle == cdump_bundle_char) {
+               char *s = encode_bytes(p, ptr, array_len);
+               if (!s) return false;
+               if (!addtabbed(p, info->name, indent) ||
+                   !addstr(p, " = {") ||
+                   !addstr(p, s) ||
+                   !addstr(p, "}\n")) {
+                       return false;
+               }
+               return true;
+       }
+
+       for (i=0;i<array_len;i++) {
+               const char *p2 = ptr;
+               size_t size = info->size;
+
+               /* generic pointer dereference */
+               if (info->ptr_count) {
+                       p2 = *(const char **)ptr;
+                       size = sizeof(void *);
+               }
+               
+               if ((count || info->ptr_count) &&
+                   !(info->flags & CDUMP_FLAG_ALWAYS) &&
+                   all_zero(ptr, size)) {
+                       ptr += size;
+                       continue;
+               }
+               if (count == 0) {
+                       if (!addtabbed(p, info->name, indent) ||
+                           !addshort(p, " = %u:", i)) {
+                               return false;
+                       }
+               } else {
+                       if (!addshort(p, ", %u:", i) != 0) {
+                               return false;
+                       }
+               }
+               if (!bundle_one(p, info, p2, indent)) {
+                       return false;
+               }
+               ptr += size;
+               count++;
+       }
+       if (count) {
+               return addstr(p, "\n");
+       }
+       return true;
+}
+
+/* find a variable by name in a loaded structure and return its value
+   as an integer. Used to support dynamic arrays */
+static ssize_t find_var(const struct cdump_desc *info,
+                       const char *data,
+                       const char *var)
+{
+       unsigned int i;
+       const char *ptr;
+
+       /* this allows for constant lengths */
+       if (isdigit(*var)) {
+               return atol(var);
+       }
+
+       for (i=0;info[i].name;i++) {
+               if (strcmp(info[i].name, var) == 0) break;
+       }
+       if (!info[i].name) return -1;
+
+       ptr = data + info[i].offset;
+
+       if (info[i].size == sizeof(int))
+               return *(int *)ptr;
+       else if (info[i].size == sizeof(size_t))
+               return *(ssize_t *)ptr;
+       else if (info[i].size == sizeof(char))
+               return *(char *)ptr;
+
+       return -1;
+}
+
+
+bool cdump_bundle_struct(const struct cdump_desc *info,
+                        struct cdump_string *p,
+                        const void *ptr,
+                        unsigned indent)
+{
+       char *s = bundle(p, info, ptr, indent+1);
+       if (!s)
+               return false;
+
+       return addstr(p, "{\n") && addstr(p,s) && addtabbed(p, "}", indent);
+}
+
+static bool bundle_string(struct cdump_string *p,
+                         const struct cdump_desc *info,
+                         const char *data,
+                         unsigned indent)
+{
+       const char *ptr = *(char **)data;
+       char *s = encode_bytes(p, ptr, strlen(ptr));
+       if (!s)
+               return false;
+
+       return addtabbed(p, info->name, indent)
+               && addstr(p, " = ")
+               && addchar(p,'{')
+               && addstr(p, s)
+               && addstr(p, "}\n");
+}
+
+/* the generic dump routine. Scans the parse information for this structure
+   and processes it recursively */
+static char *bundle(const void *ctx,
+                   const struct cdump_desc *info,
+                   const void *data,
+                   unsigned indent)
+{
+       struct cdump_string *p;
+       char *s = NULL;
+       int i;
+
+       p = talloc(ctx, struct cdump_string);
+       if (!p)
+               return NULL;
+       p->length = 0;
+       p->s = NULL;
+
+       for (i=0;info[i].name;i++) {
+               const void *ptr = (char *)data + info[i].offset;
+               unsigned size = info[i].size;
+
+               if (info[i].ptr_count) {
+                       size = sizeof(void *);
+               }
+
+               /* special handling for array types */
+               if (info[i].array_len) {
+                       unsigned len = info[i].array_len;
+                       if (!bundle_array(p, &info[i], ptr,  len, indent)) {
+                               goto out;
+                       }
+                       continue;
+               }
+
+               /* and dynamically sized arrays */
+               if (info[i].dynamic_len) {
+                       ssize_t len = find_var(info, data, info[i].dynamic_len);
+                       struct cdump_desc p2 = info[i];
+                       if (len < 0) {
+                               goto out;
+                       }
+                       if (len > 0) {
+                               p2.ptr_count--;
+                               p2.dynamic_len = NULL;
+                               if (!bundle_array(p, &p2, *(void **)ptr,
+                                                   len, indent)) {
+                                       goto out;
+                               }
+                       }
+                       continue;
+               }
+
+               /* don't dump zero elements */
+               if (!(info[i].flags & CDUMP_FLAG_ALWAYS) && all_zero(ptr, size))
+                       continue;
+
+               /* assume char* is a null terminated string */
+               if (info[i].size == 1 && info[i].ptr_count == 1 &&
+                   info[i].bundle == cdump_bundle_char) {
+                       if (!bundle_string(p, &info[i], ptr, indent)) {
+                               goto out;
+                       }
+                       continue;
+               }
+
+               /* generic pointer dereference */
+               if (info[i].ptr_count) {
+                       ptr = *(const void **)ptr;
+               }
+
+               if (!addtabbed(p, info[i].name, indent) ||
+                   !addstr(p, " = ") ||
+                   !bundle_one(p, &info[i], ptr, indent) ||
+                   !addstr(p, "\n")) {
+                       goto out;
+               }
+       }
+       s = talloc_steal(ctx, p->s);
+out:
+       talloc_free(p);
+       return s;
+}
+
+char *cdump_bundle(const void *ctx,
+                  const struct cdump_desc *info, const void *data)
+{
+       return bundle(ctx, info, data, 0);
+}
+
+/* parse routine for enumerated types */
+bool cdump_unbundle_enum(const struct cdump_enum *einfo,
+                        void *ptr,
+                        const char *str)
+{
+       unsigned v;
+       int i;
+
+       if (isdigit(*str)) {
+               if (sscanf(str, "%u", &v) != 1) {
+                       errno = EINVAL;
+                       return false;
+               }
+               *(unsigned *)ptr = v;
+               return 0;
+       }
+
+       for (i=0;einfo[i].name;i++) {
+               if (strcmp(einfo[i].name, str) == 0) {
+                       *(unsigned *)ptr = einfo[i].value;
+                       return true;
+               }
+       }
+
+       /* unknown enum value?? */
+       return false;
+}
+
+
+/* parse all base types */
+static bool unbundle_base(const void *ctx,
+                         const struct cdump_desc *info,
+                         void *ptr,
+                         const char *str)
+{
+       if (info->unbundle == cdump_unbundle_char && info->ptr_count==1) {
+               char *s = decode_bytes(ctx, str);
+               if (!s)
+                       return false;
+               *(char **)ptr = s;
+               return true;
+       }
+
+       if (info->ptr_count) {
+               struct cdump_desc p2 = *info;
+               *(void **)ptr = talloc_zero_size(ctx,
+                                                info->ptr_count>1?sizeof(void *):info->size);
+               if (! *(void **)ptr) {
+                       return false;
+               }
+               ptr = *(char **)ptr;
+               p2.ptr_count--;
+               return unbundle_base(ctx, &p2, ptr, str);
+       }
+
+       return info->unbundle(ctx, ptr, str);
+}
+
+/* search for a character in a string, skipping over sections within
+   matching braces */
+static char *match_braces(char *s, char c)
+{
+       int depth = 0;
+       while (*s) {
+               switch (*s) {
+               case '}':
+                       depth--;
+                       break;
+               case '{':
+                       depth++;
+                       break;
+               }
+               if (depth == 0 && *s == c) {
+                       return s;
+               }
+               s++;
+       }
+       return s;
+}
+
+/* parse a generic array */
+static bool unbundle_array(const void *ctx,
+                          const struct cdump_desc *info,
+                          char *ptr,
+                          const char *str,
+                          size_t array_len)
+{
+       char *p, *p2;
+       size_t size = info->size;
+
+       /* special handling of fixed length strings */
+       if (array_len != 0 &&
+           info->ptr_count == 0 &&
+           info->bundle == cdump_bundle_char) {
+               char *s = decode_bytes(ctx, str);
+               if (!s)
+                       return false;
+               memset(ptr, 0, array_len);
+               memcpy(ptr, s, array_len);
+               talloc_free(s);
+               return true;
+       }
+
+       if (info->ptr_count) {
+               size = sizeof(void *);
+       }
+
+       while (*str) {
+               size_t idx;
+               bool done;
+
+               idx = atol(str);
+               p = strchr(str,':');
+               if (!p) break;
+               p++;
+               p2 = match_braces(p, ',');
+               done = (*p2 != ',');
+               *p2 = 0;
+
+               if (*p == '{') {
+                       p++;
+                       p[strlen(p)-1] = 0;
+               }
+
+               if (!unbundle_base(ctx, info, ptr + idx*size, p)) {
+                       return false;
+               }
+
+               if (done)
+                       break;
+               str = p2+1;
+       }
+
+       return true;
+}
+
+/* parse one element, handling dynamic and static arrays */
+static bool unbundle_one(const void *ctx,
+                        const struct cdump_desc *info,
+                        const char *name,
+                        void *data,
+                        const char *str)
+{
+       int i;
+       for (i=0;info[i].name;i++) {
+               if (strcmp(info[i].name, name) == 0) {
+                       break;
+               }
+       }
+       if (info[i].name == NULL) {
+               return false;
+       }
+
+       if (info[i].array_len) {
+               return unbundle_array(ctx, &info[i], data+info[i].offset,
+                                      str, info[i].array_len);
+       }
+
+       if (info[i].dynamic_len) {
+               ssize_t len = find_var(info, data, info[i].dynamic_len);
+               if (len < 0) {
+                       errno = EINVAL;
+                       return false;
+               }
+               if (len > 0) {
+                       unsigned size;
+                       struct cdump_desc p2 = info[i];
+                       char *ptr;
+                       size = info[i].ptr_count>1?sizeof(void*):info[i].size;
+                       ptr = talloc_zero_size(ctx, len * size);
+                       if (!ptr)
+                               return false;
+                       *((char **)(data + info[i].offset)) = ptr;
+                       p2.ptr_count--;
+                       p2.dynamic_len = NULL;
+                       return unbundle_array(ctx, &p2, ptr, str, len);
+               }
+               return true;
+       }
+
+       return unbundle_base(ctx, &info[i], data + info[i].offset, str);
+}
+
+/* the main parse routine */
+bool cdump_unbundle_struct(const void *ctx,
+                          const struct cdump_desc *info,
+                          void *data, const char *s)
+{
+       char *str, *s0;
+       
+       s0 = talloc_strdup(ctx, s);
+       str = s0;
+
+       while (*str) {
+               char *p;
+               char *name;
+               char *value;
+
+               /* skip leading whitespace */
+               while (isspace(*str)) str++;
+
+               p = strchr(str, '=');
+               if (!p) break;
+               value = p+1;
+               while (p > str && isspace(*(p-1))) {
+                       p--;
+               }
+
+               *p = 0;
+               name = str;
+
+               while (isspace(*value)) value++;
+
+               if (*value == '{') {
+                       str = match_braces(value, '}');
+                       value++;
+               } else {
+                       str = match_braces(value, '\n');
+               }
+
+               *str++ = 0;
+
+               if (!unbundle_one(ctx, info, name, data, value)) {
+                       talloc_free(s0);
+                       return false;
+               }
+       }
+
+       talloc_free(s0);
+       return true;
+}
+
+bool cdump_unbundle(const void *ctx,
+                   const struct cdump_desc *info,
+                   void *data, const char *s)
+{
+       return cdump_unbundle_struct(ctx, info, data, s);
+}
+
+/* for convenience supply some standard dumpers and parsers here */
+bool cdump_unbundle_char(const void *ctx, void *ptr, const char *str)
+{
+       *(unsigned char *)ptr = atoi(str);
+       return true;
+}
+
+bool cdump_unbundle_int(const void *ctx, void *ptr, const char *str)
+{
+       *(int *)ptr = atoi(str);
+       return true;
+}
+
+bool cdump_unbundle_unsigned(const void *ctx, void *ptr, const char *str)
+{
+       *(unsigned *)ptr = strtoul(str, NULL, 10);
+       return true;
+}
+
+bool cdump_unbundle_time_t(const void *ctx, void *ptr, const char *str)
+{
+       *(time_t *)ptr = strtoul(str, NULL, 10);
+       return true;
+}
+
+bool cdump_unbundle_double(const void *ctx, void *ptr, const char *str)
+{
+       *(double *)ptr = atof(str);
+       return true;
+}
+
+bool cdump_unbundle_float(const void *ctx, void *ptr, const char *str)
+{
+       *(float *)ptr = atof(str);
+       return true;
+}
+
+bool cdump_bundle_char(struct cdump_string *p, const void *ptr, unsigned indent)
+{
+       return addshort(p, "%u", *(unsigned char *)ptr);
+}
+
+bool cdump_bundle_int(struct cdump_string *p, const void *ptr, unsigned indent)
+{
+       return addshort(p, "%d", *(int *)ptr);
+}
+
+bool cdump_bundle_unsigned(struct cdump_string *p, const void *ptr, unsigned indent)
+{
+       return addshort(p, "%u", *(unsigned *)ptr);
+}
+
+bool cdump_bundle_time_t(struct cdump_string *p, const void *ptr, unsigned indent)
+{
+       return addshort(p, "%lu", (long int)*(time_t *)ptr);
+}
+
+bool cdump_bundle_double(struct cdump_string *p, const void *ptr, unsigned indent)
+{
+       return addshort(p, "%lg", *(double *)ptr);
+}
+
+bool cdump_bundle_float(struct cdump_string *p, const void *ptr, unsigned indent)
+{
+       return addshort(p, "%g", *(float *)ptr);
+}
diff --git a/ccan/cdump/cdump.h b/ccan/cdump/cdump.h
new file mode 100644 (file)
index 0000000..70def69
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef CCAN_CDUMP_H
+#define CCAN_CDUMP_H
+#include <stdbool.h>
+
+#ifndef CDUMP_PARSING
+/**
+ * CDUMP_SAVED - prefix for a structure or enum to be parsed by cdump_parse.
+ *
+ * A structure or enum tagged with this will be parsed by cdump_parse;
+ * others are ignored.  Note that the parser is very primitive, so
+ * your definitions should be formatted simply.
+ *
+ * Example:
+ *     CDUMP_SAVED struct foo {
+ *             int x;
+ *     };
+ */
+#define CDUMP_SAVED
+
+/**
+ * CDUMP_LEN - annotation for a pointer parsed by cdump_parse.
+ * @member: the structure memeber which defines the length.
+ *
+ * This marks a pointer as having a known length, either a constant or
+ * another structure member.
+ *
+ * Example:
+ *     CDUMP_SAVED struct bar {
+ *             int len;
+ *             char *p CDUMP_LEN(len);
+ *     };
+ */
+#define CDUMP_LEN(x)
+
+/**
+ * CDUMP_ZEROTERM - annotation for a pointer parsed by cdump_parse.
+ *
+ * This marks a pointer as being an array terminated by a zero entry, such
+ * as a C string.
+ *
+ * Example:
+ *     CDUMP_SAVED struct baz {
+ *             char *p CDUMP_ZEROTERM;
+ *     };
+ */
+#define CDUMP_ZEROTERM
+#endif
+
+struct cdump_desc;
+
+/**
+ * cdump_bundle - linearize a given datastructure
+ * @ctx: the context to tallocate the returned string off.
+ * @info: a cdump_desc definition created by cdump_parse()
+ * @data: a pointer to the struct described by @info.
+ *
+ * This tallocates a string which is a description of the talloc
+ * pointer @data and everything it references.
+ */
+char *cdump_bundle(const void *ctx,
+                  const struct cdump_desc *info, const void *data);
+
+/**
+ * cdump_unbundle - extract a datastructure from a linearized description
+ * @ctx: the context to tallocate the returned datastructure off.
+ * @info: a cdump_desc definition created by cdump_parse()
+ * @data: a pointer to the structute to unbundle.
+ * @str: the string created by cdump_bundle().
+ *
+ * Returns false on failure.  Fields not mentioned in @str are zero-filled.
+ */
+bool cdump_unbundle(const void *ctx,
+                   const struct cdump_desc *info,
+                   void *data, const char *str);
+
+#endif /* CCAN_CDUMP_H */
diff --git a/ccan/cdump/cdump_internal.h b/ccan/cdump/cdump_internal.h
new file mode 100644 (file)
index 0000000..7e85f5f
--- /dev/null
@@ -0,0 +1,81 @@
+#ifndef CCAN_CDUMP_INTERNAL
+#define CCAN_CDUMP_INTERNAL
+#include <stdbool.h>
+#include <stdlib.h>
+
+/*
+  automatic marshalling/unmarshalling system for C structures
+*/
+
+/* Always printed even if it's zero (for enums). */
+#define CDUMP_FLAG_ALWAYS 1
+
+struct cdump_enum {
+       const char *name;
+       unsigned value;
+};
+
+struct cdump_string;
+
+typedef bool (*cdump_bundle_fn)(struct cdump_string *, const void *ptr, unsigned indent);
+typedef bool (*cdump_unbundle_fn)(const void *ctx, void *ptr, const char *str);
+
+/* genstruct.pl generates arrays of these */
+struct cdump_desc {
+       /* Name of the member. */
+       const char *name;
+       /* Number of levels of pointers. */
+       size_t ptr_count;
+       /* Size of the underlying type. */
+       size_t size;
+       /* Offset within the struct. */
+       size_t offset;
+       /* Non-zero if it's a fixed-length array. */
+       size_t array_len;
+       /* Set to field name or literal number, from CDUMP_LEN. */
+       const char *dynamic_len;
+       /* CDUMP_FLAG_ALWAYS */
+       unsigned flags;
+       /* Function to dump this type. */
+       cdump_bundle_fn bundle;
+       /* Function to restore this type. */
+       cdump_unbundle_fn unbundle;
+};
+
+/* For writing your own dump types. */
+bool cdump_addstr(struct cdump_string *p, const char *fmt, ...);
+
+bool cdump_bundle_enum(const struct cdump_enum *einfo,
+                      struct cdump_string *p,
+                      const void *ptr,
+                      unsigned indent);
+bool cdump_unbundle_enum(const struct cdump_enum *einfo,
+                        void *ptr, const char *str);
+
+bool cdump_bundle_struct(const struct cdump_desc *pinfo,
+                        struct cdump_string *p,
+                        const void *ptr,
+                        unsigned indent);
+bool cdump_unbundle_struct(const void *ctx,
+                          const struct cdump_desc *info,
+                          void *data, const char *s);
+
+/* for convenience supply some standard dumpers and parsers here */
+bool cdump_bundle_char(struct cdump_string *p, const void *ptr, unsigned ind);
+bool cdump_bundle_int(struct cdump_string *p, const void *ptr, unsigned ind);
+bool cdump_bundle_unsigned(struct cdump_string *p, const void *ptr, unsigned);
+bool cdump_bundle_time_t(struct cdump_string *p, const void *ptr, unsigned ind);
+bool cdump_bundle_double(struct cdump_string *p, const void *ptr, unsigned ind);
+bool cdump_bundle_float(struct cdump_string *p, const void *ptr, unsigned ind);
+
+bool cdump_unbundle_char(const void *ctx, void *ptr, const char *str);
+bool cdump_unbundle_int(const void *ctx, void *ptr, const char *str);
+bool cdump_unbundle_unsigned(const void *ctx, void *ptr, const char *str);
+bool cdump_unbundle_time_t(const void *ctx, void *ptr, const char *str);
+bool cdump_unbundle_double(const void *ctx, void *ptr, const char *str);
+bool cdump_unbundle_float(const void *ctx, void *ptr, const char *str);
+
+#define cdump_bundle_unsigned_char cdump_bundle_char
+#define cdump_unbundle_unsigned_char cdump_unbundle_char
+
+#endif /* CCAN_CDUMP_INTERNAL */
diff --git a/ccan/cdump/cdump_parse.c b/ccan/cdump/cdump_parse.c
new file mode 100644 (file)
index 0000000..fea7df1
--- /dev/null
@@ -0,0 +1,311 @@
+#include <ccan/cdump/cdump_parse.h>
+#include <ccan/talloc/talloc.h>
+#include <ccan/str/str.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <ctype.h>
+
+static void add_token(char ***toks, const char *tok, unsigned toklen)
+{
+       size_t len = talloc_array_length(*toks);
+
+       *toks = talloc_realloc(NULL, *toks, char *, len+1);
+       (*toks)[len] = talloc_strndup(*toks, tok, toklen);
+}
+
+/* Simplified tokenizer: comments and preproc directives removed,
+   identifiers are a token, others are single char tokens. */
+static char **tokenize(const void *ctx, const char *code)
+{
+       unsigned int i, len, tok_start = -1;
+       bool start_of_line = true;
+       char **ret = talloc_array(ctx, char *, 0);
+
+       for (i = 0; code[i]; i += len) {
+               if (code[i] == '#' && start_of_line) {
+                       /* Preprocessor line. */
+                       len = strcspn(code+i, "\n");
+               } else if (code[i] == '/' && code[i+1] == '/') {
+                       /* One line comment. */
+                       len = strcspn(code+i, "\n");
+                       if (tok_start != -1U) {
+                               add_token(&ret, code+tok_start, i - tok_start);
+                               tok_start = -1U;
+                       }
+               } else if (code[i] == '/' && code[i+1] == '*') {
+                       /* Multi-line comment. */
+                       const char *end = strstr(code+i+2, "*/");
+                       len = (end + 2) - (code + i);
+                       if (!end)
+                               len = strlen(code + i);
+                       if (tok_start != -1U) {
+                               add_token(&ret, code+tok_start, i - tok_start);
+                               tok_start = -1U;
+                       }
+               } else if (isalnum(code[i]) || code[i] == '_') {
+                       /* Identifier or part thereof */
+                       if (tok_start == -1U)
+                               tok_start = i;
+                       len = 1;
+               } else if (!isspace(code[i])) {
+                       /* Punctuation: treat as single char token. */
+                       if (tok_start != -1U) {
+                               add_token(&ret, code+tok_start, i - tok_start);
+                               tok_start = -1U;
+                       }
+                       add_token(&ret, code+i, 1);
+                       len = 1;
+               } else {
+                       /* Whitespace. */
+                       if (tok_start != -1U) {
+                               add_token(&ret, code+tok_start, i - tok_start);
+                               tok_start = -1U;
+                       }
+                       len = 1;
+               }
+               if (code[i] == '\n')
+                       start_of_line = true;
+               else if (!isspace(code[i]))
+                       start_of_line = false;
+       }
+
+       /* Add terminating NULL. */
+       ret = talloc_realloc(NULL, ret, char *, talloc_array_length(ret)+1);
+       ret[talloc_array_length(ret)-1] = NULL;
+
+       return ret;
+}
+
+static size_t handle_general(const void *ctx, const char *outer_struct_name,
+                            char **definitions, unsigned int ptr_count,
+                            const char *size, char **tok, const char *flags,
+                            const char *dynlen, const char *bundle,
+                            const char *unbundle)
+{
+       size_t off = 1;
+       char *array_len = NULL;
+
+       /* handle arrays, treat multidimensional arrays as 1 dimensional */
+       while (streq(tok[off], "[")) {
+               if (!array_len)
+                       array_len = talloc_strdup(ctx, "(");
+               else
+                       array_len = talloc_asprintf_append(array_len, " * (");
+               off++;
+               while (!streq(tok[off], "]")) {
+                       array_len = talloc_asprintf_append(array_len,
+                                                          "%s ", tok[off]);
+                       off++;
+               }
+               array_len[strlen(array_len)-1] = ')';
+               off++;
+       }
+
+       *definitions = talloc_asprintf_append(*definitions,
+       "\t{ \"%s\", %u, %s, offsetof(struct %s, %s), %s, %s, %s, %s, %s },\n",
+               tok[0], ptr_count, size, outer_struct_name, tok[0],
+               array_len ? array_len : "0", dynlen ? dynlen : "NULL", flags,
+               bundle, unbundle);
+
+       return off;
+}
+
+static size_t parse_one(const void *ctx, const char *outer_struct_name,
+                       char **definitions, char **type, unsigned typelen,
+                       char **tok, const char *dynlen, const char *flags)
+{
+       unsigned int i, ptr_count = 0;
+       size_t off = 0;
+       char *bundle, *unbundle, *size;
+
+       while (streq(tok[off], "*")) {
+               ptr_count++;
+               off++;
+       }
+
+       bundle = talloc_strdup(ctx, "cdump_bundle");
+       unbundle = talloc_strdup(ctx, "cdump_unbundle");
+       size = talloc_strdup(ctx, "sizeof(");
+       for (i = 0; i < typelen; i++) {
+               bundle = talloc_asprintf_append(bundle, "_%s", type[i]);
+               unbundle = talloc_asprintf_append(unbundle, "_%s", type[i]);
+               size = talloc_asprintf_append(size, "%s ", type[i]);
+       }
+       size[strlen(size)-1] = ')';
+
+       off += handle_general(ctx, outer_struct_name, definitions, ptr_count,
+                             size, tok + off, flags, dynlen, bundle, unbundle);
+       return off;
+}
+
+static size_t parse_element(const void *ctx, const char *outer_struct_name,
+                           char **definitions, char **tok)
+{
+       const char *dynlen = NULL, *flags = "0";
+       char **type;
+       unsigned int typelen;
+       size_t i;
+
+       if (streq(tok[0], "enum"))
+               flags = talloc_strdup(ctx, "CDUMP_FLAG_ALWAYS");
+
+       type = tok;
+       for (i = typelen = 0; tok[i]; i++) {
+               /* These mean we've passed the variable name */
+               if (streq(tok[i], "[")
+                   || streq(tok[i], ",")) {
+                       if (typelen == 0) {
+                               typelen = i - 1;
+                       }
+               }
+               /* End of expression means we've passed variable name, too */
+               if (streq(tok[i], ";")) {
+                       if (typelen == 0) {
+                               typelen = i - 1;
+                       }
+                       break;
+               }
+               /* This marks the end of the type: parse_one swallows *s. */
+               if (streq(tok[i], "*")) {
+                       if (typelen == 0) {
+                               typelen = i;
+                       }
+               }
+               if (streq(tok[i], "CDUMP_LEN")) {
+                       dynlen = talloc_asprintf(ctx, "\"%s\"", tok[i+2]);
+                       if (typelen == 0) {
+                               typelen = i - 1;
+                       }
+               }
+       }
+       i = typelen;
+
+       /* They could be comma-separated, so process them all. */
+       do {
+               i += parse_one(ctx, outer_struct_name, definitions,
+                              type, typelen, tok+i, dynlen, flags);
+               if (tok[i] && streq(tok[i], ","))
+                       i++;
+       } while (tok[i] && !streq(tok[i], ";") && !strstarts(tok[i], "CDUMP_"));
+
+       while (tok[i] && !streq(tok[i], ";"))
+               i++;
+
+       return i + 1;
+}
+
+static unsigned parse_struct(const void *ctx,
+                            const char *name, char **tok,
+                            char **declarations, char **definitions)
+{
+       unsigned int i = 1, len;
+
+       *declarations = talloc_asprintf_append(*declarations,
+"bool cdump_bundle_struct_%s(struct cdump_string *, const void *, unsigned);\n"
+"bool cdump_unbundle_struct_%s(const void *, void *, const char *);\n"
+"extern const struct cdump_desc cdump_struct_%s[];\n",
+                                              name, name, name);
+
+       *definitions = talloc_asprintf_append(*definitions,
+"const struct cdump_desc cdump_struct_%s[] = {\n",
+                                             name);
+       while (!streq(tok[i], "}")) {
+               len = parse_element(ctx, name, definitions, tok + i);
+               if (!len)
+                       return 0;
+               i += len;
+               if (!tok[i])
+                       return 0;
+       }
+       *definitions = talloc_asprintf_append(*definitions,
+       "\t{ NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL } };\n"
+       "bool cdump_bundle_struct_%s(struct cdump_string *p, const void *ptr, unsigned indent)\n"
+       "{\n"
+       "       return cdump_bundle_struct(cdump_struct_%s, p, ptr, indent);\n"
+       "}\n"
+       "bool cdump_unbundle_struct_%s(const void *ctx, void *ptr, const char *str)\n"
+       "{\n"
+       "       return cdump_unbundle_struct(ctx, cdump_struct_%s, ptr, str);\n"
+       "}\n"
+       "\n",
+                                             name, name, name, name);
+       return i + 1;
+}
+
+static unsigned parse_enum(const void *ctx,
+                          const char *name, char **tok,
+                          char **declarations, char **definitions)
+{
+       unsigned int i = 1;
+       
+       *declarations = talloc_asprintf_append(*declarations,
+"bool cdump_bundle_enum_%s(struct cdump_string *, const void *, unsigned);\n"
+"bool cdump_unbundle_enum_%s(const void *, void *, const char *);\n"
+"extern const struct cdump_enum cdump_enum_%s[];\n",
+                                              name, name, name);
+
+       *definitions = talloc_asprintf_append(*definitions,
+"const struct cdump_enum cdump_enum_%s[] = {\n",
+                                             name);
+       while (!streq(tok[i], "}")) {
+               *definitions = talloc_asprintf_append(*definitions,
+                                                     "\t{ \"%s\", %s },\n",
+                                                     tok[i], tok[i]);
+               while (!streq(tok[i], ",")) {
+                       if (streq(tok[i], "}")) {
+                               i--;
+                               break;
+                       }
+                       i++;
+               }
+               i++;
+       }
+
+       *definitions = talloc_asprintf_append(*definitions,
+       "\t{ NULL, 0 } };\n"
+       "bool cdump_bundle_enum_%s(struct cdump_string *p, const void *ptr, unsigned indent)\n"
+       "{\n"
+       "       return cdump_bundle_enum(cdump_enum_%s, p, ptr, indent);\n"
+       "}\n"
+       "bool cdump_unbundle_enum_%s(const void *ctx, void *ptr, const char *str)\n"
+       "{\n"
+       "       return cdump_unbundle_enum(cdump_enum_%s, ptr, str);\n"
+       "}\n"
+       "\n",
+                                             name, name, name, name);
+       return i + 1;
+}
+
+/* World's hackiest parser, inspired by Tridge's genstruct.pl. */
+char *cdump_parse(const void *ctx, const char *code,
+                 char **declarations, char **definitions)
+{
+       char **tokens = tokenize(ctx, code);
+       unsigned int i, len;
+
+       *declarations = talloc_strdup(ctx, "");
+       *definitions = talloc_strdup(ctx, "");
+
+       for (i = 0; i < talloc_array_length(tokens)-1; i++) {
+               if (!streq(tokens[i], "CDUMP_SAVED"))
+                       continue;
+               if (i + 3 >= talloc_array_length(tokens)-1)
+                       return talloc_strdup(ctx, "EOF after CDUMP_SAVED");
+
+               if (streq(tokens[i+1], "struct")) {
+                       len = parse_struct(ctx, tokens[i+2], tokens + i + 3,
+                                          declarations, definitions);
+               } else if (streq(tokens[i+1], "enum")) {
+                       len = parse_enum(ctx, tokens[i+2], tokens + i + 3,
+                                        declarations, definitions);
+               } else
+                       return talloc_asprintf(ctx, "Unknown saved type"
+                                              " '%s'", tokens[i+1]);
+               if (len == 0)
+                       return talloc_asprintf(ctx, "Invalid %s '%s'",
+                                              tokens[i+1], tokens[i+2]);
+               i += len + 2;
+       }
+
+       return NULL;
+}
diff --git a/ccan/cdump/cdump_parse.h b/ccan/cdump/cdump_parse.h
new file mode 100644 (file)
index 0000000..867fa35
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef CCAN_CDUMP_PARSE_H
+#define CCAN_CDUMP_PARSE_H
+/**
+ * cdump_parse - code generator for cdump
+ * @ctx: talloc context to use for allocations
+ * @code: the code to parse for CDUMP_SAVED definitions
+ * @declarations: pointer to string to hold declarations
+ * @definitions: pointer to string to hold definitions
+ *
+ * This defines a struct cdump_desc for every structure and enum which
+ * has the CDUMP_SAVED label.  The declarations and definitions are separated,
+ * as they usually are placed in the header file and source file respectively.
+ *
+ * A non-NULL return indicates a human-readable error message.  Usually it
+ * means that the code couldn't be parsed.
+ */
+char *cdump_parse(const void *ctx, const char *code,
+                 char **declarations, char **definitions);
+
+#endif /* CCAN_CDUMP_PARSE_H */
+
diff --git a/ccan/cdump/test/example_generated-decls.h b/ccan/cdump/test/example_generated-decls.h
new file mode 100644 (file)
index 0000000..e2830ce
--- /dev/null
@@ -0,0 +1,9 @@
+bool cdump_bundle_enum_fruit(struct cdump_string *, const void *, unsigned);
+bool cdump_unbundle_enum_fruit(const void *, void *, const char *);
+extern const struct cdump_enum cdump_enum_fruit[];
+bool cdump_bundle_struct_test2(struct cdump_string *, const void *, unsigned);
+bool cdump_unbundle_struct_test2(const void *, void *, const char *);
+extern const struct cdump_desc cdump_struct_test2[];
+bool cdump_bundle_struct_test1(struct cdump_string *, const void *, unsigned);
+bool cdump_unbundle_struct_test1(const void *, void *, const char *);
+extern const struct cdump_desc cdump_struct_test1[];
diff --git a/ccan/cdump/test/example_generated-defs.h b/ccan/cdump/test/example_generated-defs.h
new file mode 100644 (file)
index 0000000..4f266ea
--- /dev/null
@@ -0,0 +1,61 @@
+const struct cdump_enum cdump_enum_fruit[] = {
+       { "APPLE", APPLE },
+       { "ORANGE", ORANGE },
+       { "PEAR", PEAR },
+       { "RASBERRY", RASBERRY },
+       { "PEACH", PEACH },
+       { NULL, 0 } };
+bool cdump_bundle_enum_fruit(struct cdump_string *p, const void *ptr, unsigned indent)
+{
+       return cdump_bundle_enum(cdump_enum_fruit, p, ptr, indent);
+}
+bool cdump_unbundle_enum_fruit(const void *ctx, void *ptr, const char *str)
+{
+       return cdump_unbundle_enum(cdump_enum_fruit, ptr, str);
+}
+
+const struct cdump_desc cdump_struct_test2[] = {
+       { "x1", 0, sizeof(int), offsetof(struct test2, x1), 0, NULL, 0, cdump_bundle_int, cdump_unbundle_int },
+       { "foo", 1, sizeof(char), offsetof(struct test2, foo), 0, NULL, 0, cdump_bundle_char, cdump_unbundle_char },
+       { "fstring", 0, sizeof(char), offsetof(struct test2, fstring), (20), NULL, 0, cdump_bundle_char, cdump_unbundle_char },
+       { "dlen", 0, sizeof(int), offsetof(struct test2, dlen), 0, NULL, 0, cdump_bundle_int, cdump_unbundle_int },
+       { "dfoo", 1, sizeof(char), offsetof(struct test2, dfoo), 0, "dlen", 0, cdump_bundle_char, cdump_unbundle_char },
+       { "fvalue", 0, sizeof(enum fruit), offsetof(struct test2, fvalue), 0, NULL, CDUMP_FLAG_ALWAYS, cdump_bundle_enum_fruit, cdump_unbundle_enum_fruit },
+       { "next", 1, sizeof(struct test2), offsetof(struct test2, next), 0, NULL, 0, cdump_bundle_struct_test2, cdump_unbundle_struct_test2 },
+       { NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL } };
+bool cdump_bundle_struct_test2(struct cdump_string *p, const void *ptr, unsigned indent)
+{
+       return cdump_bundle_struct(cdump_struct_test2, p, ptr, indent);
+}
+bool cdump_unbundle_struct_test2(const void *ctx, void *ptr, const char *str)
+{
+       return cdump_unbundle_struct(ctx, cdump_struct_test2, ptr, str);
+}
+
+const struct cdump_desc cdump_struct_test1[] = {
+       { "foo", 0, sizeof(char), offsetof(struct test1, foo), (100), NULL, 0, cdump_bundle_char, cdump_unbundle_char },
+       { "foo2", 1, sizeof(char), offsetof(struct test1, foo2), (20), NULL, 0, cdump_bundle_char, cdump_unbundle_char },
+       { "xlen", 0, sizeof(int), offsetof(struct test1, xlen), 0, NULL, 0, cdump_bundle_int, cdump_unbundle_int },
+       { "iarray", 1, sizeof(int), offsetof(struct test1, iarray), 0, "xlen", 0, cdump_bundle_int, cdump_unbundle_int },
+       { "slen", 0, sizeof(unsigned), offsetof(struct test1, slen), 0, NULL, 0, cdump_bundle_unsigned, cdump_unbundle_unsigned },
+       { "strings", 2, sizeof(char), offsetof(struct test1, strings), 0, "slen", 0, cdump_bundle_char, cdump_unbundle_char },
+       { "s2", 1, sizeof(char), offsetof(struct test1, s2), (5), NULL, 0, cdump_bundle_char, cdump_unbundle_char },
+       { "d1", 0, sizeof(double), offsetof(struct test1, d1), 0, NULL, 0, cdump_bundle_double, cdump_unbundle_double },
+       { "d2", 0, sizeof(double), offsetof(struct test1, d2), 0, NULL, 0, cdump_bundle_double, cdump_unbundle_double },
+       { "d3", 0, sizeof(double), offsetof(struct test1, d3), 0, NULL, 0, cdump_bundle_double, cdump_unbundle_double },
+       { "test2", 1, sizeof(struct test2), offsetof(struct test1, test2), 0, NULL, 0, cdump_bundle_struct_test2, cdump_unbundle_struct_test2 },
+       { "alen", 0, sizeof(int), offsetof(struct test1, alen), 0, NULL, 0, cdump_bundle_int, cdump_unbundle_int },
+       { "test2_array", 1, sizeof(struct test2), offsetof(struct test1, test2_array), 0, "alen", 0, cdump_bundle_struct_test2, cdump_unbundle_struct_test2 },
+       { "test2_fixed", 1, sizeof(struct test2), offsetof(struct test1, test2_fixed), (2), NULL, 0, cdump_bundle_struct_test2, cdump_unbundle_struct_test2 },
+       { "plen", 0, sizeof(int), offsetof(struct test1, plen), 0, NULL, 0, cdump_bundle_int, cdump_unbundle_int },
+       { "test2_parray", 2, sizeof(struct test2), offsetof(struct test1, test2_parray), 0, "plen", 0, cdump_bundle_struct_test2, cdump_unbundle_struct_test2 },
+       { NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL } };
+bool cdump_bundle_struct_test1(struct cdump_string *p, const void *ptr, unsigned indent)
+{
+       return cdump_bundle_struct(cdump_struct_test1, p, ptr, indent);
+}
+bool cdump_unbundle_struct_test1(const void *ctx, void *ptr, const char *str)
+{
+       return cdump_unbundle_struct(ctx, cdump_struct_test1, ptr, str);
+}
+
diff --git a/ccan/cdump/test/run-01-common-bundle-unbundle.c b/ccan/cdump/test/run-01-common-bundle-unbundle.c
new file mode 100644 (file)
index 0000000..946658c
--- /dev/null
@@ -0,0 +1,85 @@
+#include <ccan/cdump/cdump.h>
+/* Include the C files directly. */
+#include <ccan/cdump/cdump.c>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+       char c = 'A';
+       unsigned char uc = 66;
+       unsigned u = 0xdeadbeef;
+       int i = -54;
+       time_t t = 1000000;
+       float f = 2.0;
+       double d = 3.0;
+       struct cdump_string *s;
+
+       /* This is how many tests you plan to run */
+       plan_tests(7 * 5);
+
+       s = talloc(NULL, struct cdump_string);
+       s->length = 0;
+       s->s = NULL;
+
+       ok1(cdump_bundle_char(s, &c, 0));
+       ok1(strcmp(s->s, "65") == 0);
+       ok1(s->length == strlen(s->s));
+       ok1(cdump_unbundle_char(NULL, &c, s->s));
+       ok1(c == 'A');
+       s->length = 0;
+       s->s = NULL;
+
+       ok1(cdump_bundle_unsigned_char(s, &uc, 0));
+       ok1(strcmp(s->s, "66") == 0);
+       ok1(s->length == strlen(s->s));
+       ok1(cdump_unbundle_unsigned_char(NULL, &uc, s->s));
+       ok1(uc == 66);
+       s->length = 0;
+       s->s = NULL;
+
+       ok1(cdump_bundle_int(s, &i, 0));
+       ok1(strcmp(s->s, "-54") == 0);
+       ok1(s->length == strlen(s->s));
+       ok1(cdump_unbundle_int(NULL, &i, s->s));
+       ok1(i == -54);
+       s->length = 0;
+       s->s = NULL;
+
+       ok1(cdump_bundle_unsigned(s, &u, 0));
+       ok1(strcmp(s->s, "3735928559") == 0);
+       ok1(s->length == strlen(s->s));
+       ok1(cdump_unbundle_unsigned(NULL, &u, s->s));
+       ok1(u == 0xdeadbeef);
+       s->length = 0;
+       s->s = NULL;
+
+       ok1(cdump_bundle_time_t(s, &t, 0));
+       ok1(strcmp(s->s, "1000000") == 0);
+       ok1(s->length == strlen(s->s));
+       ok1(cdump_unbundle_time_t(NULL, &t, s->s));
+       ok1(t == 1000000);
+       s->length = 0;
+       s->s = NULL;
+
+       ok1(cdump_bundle_double(s, &d, 0));
+       ok1(strcmp(s->s, "3") == 0);
+       ok1(s->length == strlen(s->s));
+       ok1(cdump_unbundle_double(NULL, &d, s->s));
+       ok1(d == 3.0);
+       s->length = 0;
+       s->s = NULL;
+
+       ok1(cdump_bundle_float(s, &f, 0));
+       ok1(strcmp(s->s, "2") == 0);
+       ok1(s->length == strlen(s->s));
+       ok1(cdump_unbundle_float(NULL, &f, s->s));
+       ok1(f == 2.0);
+       s->length = 0;
+       s->s = NULL;
+
+       /* Everything should have been allocated off this, so no leaks. */
+       talloc_free(s);
+
+       /* This exits depending on whether all tests passed */
+       return exit_status();
+}
diff --git a/ccan/cdump/test/run-02-parse.c b/ccan/cdump/test/run-02-parse.c
new file mode 100644 (file)
index 0000000..c5c06c3
--- /dev/null
@@ -0,0 +1,59 @@
+#include <ccan/cdump/cdump_parse.c>
+#include <ccan/tap/tap.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+static size_t diffoff(const char *a, const char *b)
+{
+       size_t i;
+
+       for (i = 0; a[i]; i++)
+               if (b[i] != a[i])
+                       break;
+       return i;
+}
+
+static char *read_file(const void *ctx, const char *fname)
+{
+       int fd = open(fname, O_RDONLY);
+       off_t len = lseek(fd, 0, SEEK_END);
+       char *ret = talloc_array(ctx, char, len + 1);
+       lseek(fd, 0, SEEK_SET);
+       read(fd, ret, len);
+       ret[len] = '\0';
+       close(fd);
+       return ret;
+}
+
+int main(int argc, char *argv[])
+{
+       char *ret, *decls, *defs;
+       char *toplevel = talloc_strdup(NULL, "toplevel");
+       char *header, *definitions, *declarations;
+
+       plan_tests(5);
+       header = read_file(toplevel, "test/test_header.h");
+       declarations = read_file(toplevel, "test/example_generated-decls.h");
+       definitions = read_file(toplevel, "test/example_generated-defs.h");
+
+       ret = cdump_parse(toplevel, header, &decls, &defs);
+       ok1(ret == NULL);
+       ok(streq(decls, declarations),
+          "Declarations differ at %zu: ...'%.*s' vs ...'%.*s'",
+          diffoff(decls, declarations),
+          30, decls + diffoff(decls, declarations),
+          30, declarations + diffoff(decls, declarations));
+       ok(streq(defs, definitions),
+          "Definitions differ at %zu: ...'%.*s' vs ...'%.*s'",
+          diffoff(defs, definitions),
+          30, defs + diffoff(defs, definitions),
+          30, definitions + diffoff(defs, definitions));
+
+       ok1(talloc_find_parent_byname(decls, "toplevel") == toplevel);
+       ok1(talloc_find_parent_byname(defs, "toplevel") == toplevel);
+
+       talloc_free(toplevel);
+       return exit_status();
+}
diff --git a/ccan/cdump/test/run-save-restore-test.c b/ccan/cdump/test/run-save-restore-test.c
new file mode 100644 (file)
index 0000000..a92c95b
--- /dev/null
@@ -0,0 +1,87 @@
+#include <ccan/cdump/cdump.h>
+#include <ccan/cdump/cdump.c>
+#include <ccan/cdump/cdump_internal.h>
+#include <ccan/cdump/test/test_header.h>
+#include <ccan/str/str.h>
+#include <ccan/tap/tap.h>
+#include <stddef.h>
+#include <string.h>
+
+/* This is as generated by cdump_parse: see run-parse.c */
+#include <ccan/cdump/test/example_generated-decls.h>
+#include <ccan/cdump/test/example_generated-defs.h>
+
+static struct test2 *new_test2(const void *ctx, struct test2 *next)
+{
+       struct test2 *t;
+
+       t = talloc(ctx, struct test2);
+       t->x1 = 1;
+       t->foo = talloc_strdup(t, "foo");
+       memset(t->fstring, 'a', 20);
+       t->dlen = 100;
+       t->dfoo = talloc_array(t, char, t->dlen);
+       memset(t->dfoo, 7, t->dlen);
+       t->fvalue = RASBERRY;
+       t->next = next;
+       return t;
+}
+
+static bool test2eq(const struct test2 *a, const struct test2 *b)
+{
+       if (a->x1 != b->x1)
+               return false;
+       if (!a->foo != !b->foo)
+               return false;
+       if (a->foo && !streq(a->foo, b->foo))
+               return false;
+       if (memcmp(a->fstring, b->fstring, 20) != 0)
+               return false;
+       if (a->dlen != b->dlen)
+               return false;
+       if (!a->dfoo != !b->dfoo)
+               return false;
+       if (memcmp(a->dfoo, b->dfoo, a->dlen) != 0)
+               return false;
+       if (a->fvalue != b->fvalue)
+               return false;
+       if (!a->next != !b->next)
+               return false;
+       if (a->next)
+               return test2eq(a->next, b->next);
+       return true;
+}
+
+int main(void)
+{
+       struct test2 *a, *b;
+       char *astr;
+       char *toplevel = talloc_strdup(NULL, "toplevel");
+
+       plan_tests(9);
+       a = new_test2(toplevel, NULL);
+       astr = cdump_bundle(toplevel, cdump_struct_test2, a);
+       ok1(astr);
+       ok1(talloc_find_parent_byname(astr, "toplevel") == toplevel);
+
+       b = talloc_zero(toplevel, struct test2);
+       ok1(cdump_unbundle(toplevel, cdump_struct_test2, b, astr));
+       ok1(test2eq(a, b));
+
+       /* Test chaining. */
+       b->x1++;
+       a->next = b;
+
+       astr = cdump_bundle(toplevel, cdump_struct_test2, a);
+       ok1(astr);
+       ok1(talloc_find_parent_byname(astr, "toplevel") == toplevel);
+
+       b = talloc_zero(toplevel, struct test2);
+       ok1(cdump_unbundle(toplevel, cdump_struct_test2, b, astr));
+       ok1(talloc_find_parent_byname(b, "toplevel") == toplevel);
+
+       ok1(test2eq(a, b));
+       talloc_free(toplevel);
+       
+       return exit_status();
+}
diff --git a/ccan/cdump/test/test_header.h b/ccan/cdump/test/test_header.h
new file mode 100644 (file)
index 0000000..719d40c
--- /dev/null
@@ -0,0 +1,31 @@
+CDUMP_SAVED enum fruit {APPLE, ORANGE=2, PEAR,
+                     RASBERRY, PEACH};
+
+CDUMP_SAVED
+struct test2 
+{
+       int x1;
+       char *foo;
+       char fstring[20];
+       int dlen;
+       char *dfoo CDUMP_LEN(dlen);
+       enum fruit fvalue;
+       struct test2 *next;
+};
+
+CDUMP_SAVED struct test1 {
+       char foo[100];
+       char *foo2[20];
+       int xlen;
+       int *iarray CDUMP_LEN(xlen);
+       unsigned slen;
+       char **strings CDUMP_LEN(slen);
+       char *s2[5];
+       double d1, d2, d3;
+       struct test2 *test2;
+       int alen;
+       struct test2 *test2_array CDUMP_LEN(alen);
+       struct test2 *test2_fixed[2];
+       int plen;
+       struct test2 **test2_parray CDUMP_LEN(plen);
+};