From 45c8a57497b960a51bbb7178c6eace46929b0727 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 21 Jan 2011 14:05:08 +1030 Subject: [PATCH] cdump: first cut of translation of Tridge's genstruct junkcode. Mainly, all new bugs. --- ccan/cdump/LICENSE | 1 + ccan/cdump/_info | 32 + ccan/cdump/cdump.c | 756 ++++++++++++++++++ ccan/cdump/cdump.h | 76 ++ ccan/cdump/cdump_internal.h | 81 ++ ccan/cdump/cdump_parse.c | 311 +++++++ ccan/cdump/cdump_parse.h | 21 + ccan/cdump/test/example_generated-decls.h | 9 + ccan/cdump/test/example_generated-defs.h | 61 ++ .../test/run-01-common-bundle-unbundle.c | 85 ++ ccan/cdump/test/run-02-parse.c | 59 ++ ccan/cdump/test/run-save-restore-test.c | 87 ++ ccan/cdump/test/test_header.h | 31 + 13 files changed, 1610 insertions(+) create mode 120000 ccan/cdump/LICENSE create mode 100644 ccan/cdump/_info create mode 100644 ccan/cdump/cdump.c create mode 100644 ccan/cdump/cdump.h create mode 100644 ccan/cdump/cdump_internal.h create mode 100644 ccan/cdump/cdump_parse.c create mode 100644 ccan/cdump/cdump_parse.h create mode 100644 ccan/cdump/test/example_generated-decls.h create mode 100644 ccan/cdump/test/example_generated-defs.h create mode 100644 ccan/cdump/test/run-01-common-bundle-unbundle.c create mode 100644 ccan/cdump/test/run-02-parse.c create mode 100644 ccan/cdump/test/run-save-restore-test.c create mode 100644 ccan/cdump/test/test_header.h diff --git a/ccan/cdump/LICENSE b/ccan/cdump/LICENSE new file mode 120000 index 0000000..190cfd5 --- /dev/null +++ b/ccan/cdump/LICENSE @@ -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 index 0000000..b04a644 --- /dev/null +++ b/ccan/cdump/_info @@ -0,0 +1,32 @@ +#include +#include +#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 + */ +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 index 0000000..f151cbf --- /dev/null +++ b/ccan/cdump/cdump.c @@ -0,0 +1,756 @@ +/* + Based on genstruct by Andrew Tridgell: + + Copyright (C) Andrew Tridgell 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 +#include +#include +#include +#include +#include +#include +#include +#include +#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>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;isize; + + /* 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 index 0000000..70def69 --- /dev/null +++ b/ccan/cdump/cdump.h @@ -0,0 +1,76 @@ +#ifndef CCAN_CDUMP_H +#define CCAN_CDUMP_H +#include + +#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 index 0000000..7e85f5f --- /dev/null +++ b/ccan/cdump/cdump_internal.h @@ -0,0 +1,81 @@ +#ifndef CCAN_CDUMP_INTERNAL +#define CCAN_CDUMP_INTERNAL +#include +#include + +/* + 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 index 0000000..fea7df1 --- /dev/null +++ b/ccan/cdump/cdump_parse.c @@ -0,0 +1,311 @@ +#include +#include +#include +#include +#include +#include + +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 index 0000000..867fa35 --- /dev/null +++ b/ccan/cdump/cdump_parse.h @@ -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 index 0000000..e2830ce --- /dev/null +++ b/ccan/cdump/test/example_generated-decls.h @@ -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 index 0000000..4f266ea --- /dev/null +++ b/ccan/cdump/test/example_generated-defs.h @@ -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 index 0000000..946658c --- /dev/null +++ b/ccan/cdump/test/run-01-common-bundle-unbundle.c @@ -0,0 +1,85 @@ +#include +/* Include the C files directly. */ +#include +#include + +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 index 0000000..c5c06c3 --- /dev/null +++ b/ccan/cdump/test/run-02-parse.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include + +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 index 0000000..a92c95b --- /dev/null +++ b/ccan/cdump/test/run-save-restore-test.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* This is as generated by cdump_parse: see run-parse.c */ +#include +#include + +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 index 0000000..719d40c --- /dev/null +++ b/ccan/cdump/test/test_header.h @@ -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); +}; -- 2.39.2