btree \
bytestring \
ccan_tokenizer \
+ cdump \
charset \
ciniparser \
crc \
--- /dev/null
+../../licenses/BSD-MIT
\ No newline at end of file
--- /dev/null
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * cdump - routines to parse simple C structures.
+ *
+ * This code is designed to produce data structures summarizing C code.
+ * It only operates on simple, well-formed C code (eg. specific headers
+ * which you want to autogenerate from), but it should be fairly easy to
+ * enhance if desired.
+ *
+ * Author: Rusty Russell <rusty@rustcorp.com.au>
+ * License: BSD-MIT
+ *
+ * Example:
+ * // Creates a simple print function for a structure.
+ * #include <ccan/cdump/cdump.h>
+ * #include <ccan/tal/grab_file/grab_file.h>
+ * #include <ccan/err/err.h>
+ *
+ * static void print_as(const char *fmt, const char *member_name)
+ * {
+ * printf("\tprintf(\"%%s:%s\\n\", \"%s\", s->%s);\n",
+ * fmt, member_name, member_name);
+ * }
+ *
+ * int main(int argc, char *argv[])
+ * {
+ * char *code, *problems;
+ * struct cdump_definitions *defs;
+ * int i, j;
+ *
+ * // Read code from stdin.
+ * code = grab_file(NULL, NULL);
+ *
+ * defs = cdump_extract(NULL, code, &problems);
+ * if (!defs)
+ * errx(1, "Parsing stdin: %s", problems);
+ *
+ * for (i = 1; i < argc; i++) {
+ * struct cdump_type *t = strmap_get(&defs->structs, argv[i]);
+ * if (!t)
+ * errx(1, "Could not find struct %s", argv[i]);
+ *
+ * printf("void print_struct_%s(const struct %s *s)\n"
+ * "{\n", argv[i], argv[i]);
+ * for (j = 0; j < tal_count(t->u.members); j++) {
+ * const struct cdump_member *m = t->u.members + j;
+ * switch (m->type->kind) {
+ * case CDUMP_STRUCT:
+ * case CDUMP_UNION:
+ * case CDUMP_ARRAY:
+ * // Too hard for this simple example.
+ * printf("\tprintf(\"%%s:???\\n\", \"%s\");\n",
+ * m->name);
+ * break;
+ * case CDUMP_ENUM:
+ * print_as("%i", m->name);
+ * break;
+ * case CDUMP_POINTER:
+ * print_as("%p", m->name);
+ * break;
+ * case CDUMP_UNKNOWN:
+ * if (!strcmp(m->type->name, "int"))
+ * print_as("%i", m->name);
+ * else if (!strcmp(m->type->name, "long int"))
+ * print_as("%li", m->name);
+ * else if (!strcmp(m->type->name, "unsigned int"))
+ * print_as("%u", m->name);
+ * // etc...
+ * break;
+ * }
+ * }
+ * printf("}\n");
+ * }
+ * return 0;
+ * }
+ */
+int main(int argc, char *argv[])
+{
+ /* Expect exactly one argument */
+ if (argc != 2)
+ return 1;
+
+ if (strcmp(argv[1], "depends") == 0) {
+ printf("ccan/tal\n");
+ printf("ccan/tal/str\n");
+ printf("ccan/strmap\n");
+ return 0;
+ }
+
+ return 1;
+}
--- /dev/null
+/* MIT (BSD) license - see LICENSE file for details */
+#include "cdump.h"
+#include <ccan/tal/str/str.h>
+#include <assert.h>
+
+struct token {
+ const char *p;
+ size_t len;
+};
+
+static void add_token(struct token **toks, const char *p, size_t len)
+{
+ size_t n = tal_count(*toks);
+ tal_resize(toks, n+1);
+ (*toks)[n].p = p;
+ (*toks)[n].len = len;
+}
+
+/* Simplified tokenizer: comments and preproc directives removed,
+ identifiers are a token, others are single char tokens. */
+static struct token *tokenize(const void *ctx, const char *code)
+{
+ unsigned int i, len, tok_start = -1;
+ bool start_of_line = true;
+ struct token *toks = tal_arr(ctx, struct token, 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(&toks, 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(&toks, code+tok_start, i - tok_start);
+ tok_start = -1U;
+ }
+ } else if (cisalnum(code[i]) || code[i] == '_') {
+ /* Identifier or part thereof */
+ if (tok_start == -1U)
+ tok_start = i;
+ len = 1;
+ } else if (!cisspace(code[i])) {
+ /* Punctuation: treat as single char token. */
+ if (tok_start != -1U) {
+ add_token(&toks, code+tok_start, i - tok_start);
+ tok_start = -1U;
+ }
+ add_token(&toks, code+i, 1);
+ len = 1;
+ } else {
+ /* Whitespace. */
+ if (tok_start != -1U) {
+ add_token(&toks, code+tok_start, i - tok_start);
+ tok_start = -1U;
+ }
+ len = 1;
+ }
+ if (code[i] == '\n')
+ start_of_line = true;
+ else if (!cisspace(code[i]))
+ start_of_line = false;
+ }
+
+ /* Add terminating NULL. */
+ tal_resizez(&toks, tal_count(toks) + 1);
+ return toks;
+}
+
+struct parse_state {
+ const char *code;
+ const struct token *toks;
+ struct cdump_definitions *defs;
+ char *complaints;
+};
+
+static bool tok_is(const struct token **toks, const char *target)
+{
+ return (*toks)->p && (*toks)->len == strlen(target)
+ && memcmp((*toks)->p, target, (*toks)->len) == 0;
+}
+
+static const struct token *tok_peek(const struct token **toks)
+{
+ if (toks[0]->p)
+ return toks[0];
+ return NULL;
+}
+
+static const struct token *tok_take(const struct token **toks)
+{
+ if (!toks[0]->p)
+ return NULL;
+
+ return (*toks)++;
+}
+
+static const struct token *tok_take_if(const struct token **toks,
+ const char *target)
+{
+ if (tok_is(toks, target))
+ return tok_take(toks);
+ return NULL;
+}
+
+static const char *tok_take_ident(const tal_t *ctx, const struct token **toks)
+{
+ const struct token *t = tok_peek(toks);
+
+ if (!t)
+ return NULL;
+
+ if (strspn(t->p, "_0123456789"
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ") < t->len)
+ return NULL;
+
+ t = tok_take(toks);
+ return tal_strndup(ctx, t->p, t->len);
+}
+
+static char *string_of_toks(const tal_t *ctx,
+ const struct token *first,
+ const struct token *until)
+{
+ const struct token *end = until - 1;
+ return tal_strndup(ctx, first->p, end->p - first->p + end->len);
+}
+
+static char *tok_take_until(const tal_t *ctx,
+ const struct token **toks,
+ const char *delims)
+{
+ const struct token *t, *start;
+
+ start = tok_peek(toks);
+ while ((t = tok_peek(toks)) != NULL) {
+ /* If this contains a delimiter, copy up to prev token. */
+ if (strcspn(t->p, delims) < t->len)
+ return string_of_toks(ctx, start, t);
+ tok_take(toks);
+ };
+
+ /* EOF without finding delimiter */
+ return NULL;
+}
+
+static bool type_defined(const struct cdump_type *t)
+{
+ switch (t->kind) {
+ case CDUMP_STRUCT:
+ case CDUMP_UNION:
+ return (t->u.members != NULL);
+ case CDUMP_ENUM:
+ return (t->u.enum_vals != NULL);
+
+ /* These shouldn't happen; we don't try to define them. */
+ case CDUMP_UNKNOWN:
+ case CDUMP_ARRAY:
+ case CDUMP_POINTER:
+ break;
+ }
+ abort();
+}
+
+/* May allocate a new type if not already found (steals @name) */
+static struct cdump_type *get_type(struct cdump_definitions *defs,
+ enum cdump_type_kind kind,
+ const char *name)
+{
+ struct cdump_map *m;
+ struct cdump_type *t;
+
+ switch (kind) {
+ case CDUMP_STRUCT:
+ m = &defs->structs;
+ break;
+ case CDUMP_UNION:
+ m = &defs->unions;
+ break;
+ case CDUMP_ENUM:
+ m = &defs->enums;
+ break;
+ case CDUMP_UNKNOWN:
+ case CDUMP_ARRAY:
+ case CDUMP_POINTER:
+ m = NULL;
+ }
+
+ /* Do we already have it? */
+ if (m) {
+ t = strmap_get(m, name);
+ if (t)
+ return t;
+ }
+
+ t = tal(defs, struct cdump_type);
+ t->kind = kind;
+ t->name = name ? tal_steal(t, name) : NULL;
+ /* These are actually the same, but be thorough */
+ t->u.members = NULL;
+ t->u.enum_vals = NULL;
+ if (m)
+ strmap_add(m, t->name, t);
+
+ return t;
+}
+
+static void complain(struct parse_state *ps, const char *complaint)
+{
+ unsigned int linenum;
+ const char *p = ps->code;
+
+ for (linenum = 1; p < ps->toks[0].p; linenum++) {
+ p = strchr(p+1, '\n');
+ if (!p)
+ break;
+ }
+
+ tal_append_fmt(&ps->complaints,
+ "Line %u: '%.*s': %s\n",
+ linenum, (int)ps->toks[0].len,
+ ps->toks[0].p, complaint);
+}
+
+static void tok_take_unknown_statement(struct parse_state *ps)
+{
+ complain(ps, "Ignoring unknown statement until next semicolon");
+ tal_free(tok_take_until(NULL, &ps->toks, ";"));
+ tok_take_if(&ps->toks, ";");
+}
+
+/* [ ... */
+static bool tok_take_array(struct parse_state *ps, struct cdump_type **type)
+{
+ /* This will be some arbitrary expression! */
+ struct cdump_type *arr = get_type(ps->defs, CDUMP_ARRAY, NULL);
+
+ arr->u.arr.size = tok_take_until(arr, &ps->toks, "]");
+ if (!arr->u.arr.size) {
+ complain(ps, "Could not find closing array size ]");
+ return false;
+ }
+
+ arr->u.arr.type = *type;
+ *type = arr;
+
+ /* Swallow ] */
+ tok_take(&ps->toks);
+ return true;
+}
+
+static struct cdump_type *ptr_of(struct parse_state *ps,
+ const struct cdump_type *ptr_to)
+{
+ struct cdump_type *ptr = get_type(ps->defs, CDUMP_POINTER, NULL);
+ ptr->u.ptr = ptr_to;
+ return ptr;
+}
+
+static bool tok_take_type(struct parse_state *ps, struct cdump_type **type)
+{
+ const char *name;
+ const struct token *types;
+ enum cdump_type_kind kind;
+
+ /* Ignoring weird typedefs, only these can be combined. */
+ types = ps->toks;
+ while (tok_take_if(&ps->toks, "int")
+ || tok_take_if(&ps->toks, "long")
+ || tok_take_if(&ps->toks, "short")
+ || tok_take_if(&ps->toks, "double")
+ || tok_take_if(&ps->toks, "float")
+ || tok_take_if(&ps->toks, "char")
+ || tok_take_if(&ps->toks, "signed")
+ || tok_take_if(&ps->toks, "unsigned"));
+
+ /* Did we get some? */
+ if (ps->toks != types) {
+ name = string_of_toks(NULL, types, tok_peek(&ps->toks));
+ kind = CDUMP_UNKNOWN;
+ } else {
+ /* Try normal types (or simple typedefs, etc). */
+ if (tok_take_if(&ps->toks, "struct")) {
+ kind = CDUMP_STRUCT;
+ } else if (tok_take_if(&ps->toks, "union")) {
+ kind = CDUMP_UNION;
+ } else if (tok_take_if(&ps->toks, "enum")) {
+ kind = CDUMP_ENUM;
+ } else
+ kind = CDUMP_UNKNOWN;
+
+ name = tok_take_ident(ps->defs, &ps->toks);
+ if (!name) {
+ complain(ps, "Invalid typename");
+ return false;
+ }
+ }
+
+ *type = get_type(ps->defs, kind, name);
+ return true;
+}
+
+/* struct|union ... */
+static bool tok_take_conglom(struct parse_state *ps,
+ enum cdump_type_kind conglom_kind)
+{
+ struct cdump_type *e;
+ const char *name;
+ size_t n;
+
+ assert(conglom_kind == CDUMP_STRUCT || conglom_kind == CDUMP_UNION);
+
+ name = tok_take_ident(ps->defs, &ps->toks);
+ if (!name) {
+ complain(ps, "Invalid struct/union name");
+ return false;
+ }
+
+ e = get_type(ps->defs, conglom_kind, name);
+ if (type_defined(e)) {
+ complain(ps, "Type already defined");
+ return false;
+ }
+
+ if (!tok_take_if(&ps->toks, "{")) {
+ complain(ps, "Expected { for struct/union");
+ return false;
+ }
+
+ e->u.members = tal_arr(e, struct cdump_member, n = 0);
+ while (!tok_is(&ps->toks, "}")) {
+ struct cdump_type *basetype;
+ const struct token *quals;
+ unsigned int num_quals = 0;
+
+ /* Anything can have these prepended. */
+ quals = ps->toks;
+ while (tok_take_if(&ps->toks, "const")
+ || tok_take_if(&ps->toks, "volatile"))
+ num_quals++;
+
+ /* eg. "struct foo" or "varint_t" */
+ if (!tok_take_type(ps, &basetype)) {
+ complain(ps, "Expected typename inside struct/union");
+ return false;
+ }
+
+ do {
+ struct cdump_member *m;
+
+ tal_resize(&e->u.members, n+1);
+ m = &e->u.members[n++];
+ m->type = basetype;
+ if (num_quals) {
+ m->qualifiers
+ = string_of_toks(e, quals,
+ quals + num_quals);
+ } else
+ m->qualifiers = NULL;
+
+ /* May have multiple asterisks. */
+ while (tok_take_if(&ps->toks, "*"))
+ m->type = ptr_of(ps, m->type);
+
+ m->name = tok_take_ident(e, &ps->toks);
+ if (!m->name) {
+ complain(ps, "Expected name for member");
+ return false;
+ }
+
+ /* May be an array. */
+ while (tok_take_if(&ps->toks, "[")) {
+ if (!tok_take_array(ps, &m->type))
+ return false;
+ }
+ } while (tok_take_if(&ps->toks, ","));
+
+ if (!tok_take_if(&ps->toks, ";")) {
+ complain(ps, "Expected ; at end of member");
+ return false;
+ }
+ }
+
+ if (tok_take_if(&ps->toks, "}") && tok_take_if(&ps->toks, ";"))
+ return true;
+ complain(ps, "Expected }; at end of struct/union");
+ return false;
+}
+
+/* enum ... */
+static bool tok_take_enum(struct parse_state *ps)
+{
+ size_t n = 0;
+ struct cdump_type *e;
+ const char *name;
+
+ name = tok_take_ident(ps->defs, &ps->toks);
+ if (!name) {
+ complain(ps, "Expected enum name");
+ return false;
+ }
+
+ e = get_type(ps->defs, CDUMP_ENUM, name);
+
+ /* Duplicate name? */
+ if (type_defined(e)) {
+ complain(ps, "enum already defined");
+ return false;
+ }
+
+ if (!tok_take_if(&ps->toks, "{")) {
+ complain(ps, "Expected { after enum name");
+ return false;
+ }
+
+ e->u.enum_vals = tal_arr(e, struct cdump_enum_val, n);
+ do {
+ struct cdump_enum_val *v;
+
+ tal_resize(&e->u.enum_vals, n+1);
+ v = &e->u.enum_vals[n++];
+
+ v->name = tok_take_ident(e, &ps->toks);
+ if (!v->name) {
+ complain(ps, "Expected enum value name");
+ return false;
+ }
+ if (tok_take_if(&ps->toks, "=")) {
+ v->value = tok_take_until(e, &ps->toks, ",}");
+ if (!v->value) {
+ complain(ps, "Expected , or } to end value");
+ return false;
+ }
+ } else
+ v->value = NULL;
+ } while (tok_take_if(&ps->toks, ","));
+
+ if (tok_take_if(&ps->toks, "}") && tok_take_if(&ps->toks, ";"))
+ return true;
+
+ complain(ps, "Expected }; at end of enum");
+ return false;
+}
+
+static bool gather_undefines(const char *name,
+ struct cdump_type *t,
+ struct cdump_map *undefs)
+{
+ if (!type_defined(t))
+ strmap_add(undefs, name, t);
+ return true;
+}
+
+static bool remove_from_map(const char *name,
+ struct cdump_type *t,
+ struct cdump_map *map)
+{
+ strmap_del(map, name, NULL);
+ return true;
+}
+
+static void remove_undefined(struct cdump_map *map)
+{
+ struct cdump_map undefs;
+
+ /* We can't delete inside iterator, so gather all the undefs
+ * then remove them. */
+ strmap_init(&undefs);
+
+ strmap_iterate(map, gather_undefines, &undefs);
+ strmap_iterate(&undefs, remove_from_map, map);
+ strmap_clear(&undefs);
+}
+
+static void destroy_definitions(struct cdump_definitions *defs)
+{
+ strmap_clear(&defs->enums);
+ strmap_clear(&defs->structs);
+ strmap_clear(&defs->unions);
+}
+
+/* Simple LL(1) parser, inspired by Tridge's genstruct.pl. */
+struct cdump_definitions *cdump_extract(const tal_t *ctx, const char *code,
+ char **complaints)
+{
+ struct parse_state ps;
+ const struct token *toks;
+
+ ps.defs = tal(ctx, struct cdump_definitions);
+ ps.complaints = tal_strdup(ctx, "");
+ ps.code = code;
+
+ strmap_init(&ps.defs->enums);
+ strmap_init(&ps.defs->structs);
+ strmap_init(&ps.defs->unions);
+ tal_add_destructor(ps.defs, destroy_definitions);
+
+ toks = ps.toks = tokenize(ps.defs, code);
+ while (tok_peek(&ps.toks)) {
+ if (tok_take_if(&ps.toks, "struct")) {
+ if (!tok_take_conglom(&ps, CDUMP_STRUCT))
+ goto fail;
+ } else if (tok_take_if(&ps.toks, "union")) {
+ if (!tok_take_conglom(&ps, CDUMP_UNION))
+ goto fail;
+ } else if (tok_take_if(&ps.toks, "enum")) {
+ if (!tok_take_enum(&ps))
+ goto fail;
+ } else
+ tok_take_unknown_statement(&ps);
+ }
+
+ /* Now, remove any undefined types! */
+ remove_undefined(&ps.defs->enums);
+ remove_undefined(&ps.defs->structs);
+ remove_undefined(&ps.defs->unions);
+ tal_free(toks);
+
+out:
+ if (streq(ps.complaints, ""))
+ ps.complaints = tal_free(ps.complaints);
+
+ if (complaints)
+ *complaints = ps.complaints;
+ else
+ tal_free(ps.complaints);
+ return ps.defs;
+
+fail:
+ ps.defs = tal_free(ps.defs);
+ goto out;
+}
--- /dev/null
+/* MIT (BSD) license - see LICENSE file for details */
+#ifndef CCAN_CDUMP_H
+#define CCAN_CDUMP_H
+#include <ccan/strmap/strmap.h>
+#include <ccan/tal/tal.h>
+
+enum cdump_type_kind {
+ CDUMP_STRUCT,
+ CDUMP_UNION,
+ CDUMP_ENUM,
+ CDUMP_ARRAY,
+ CDUMP_POINTER,
+ CDUMP_UNKNOWN
+};
+
+struct cdump_member {
+ const char *name;
+ /* const, volatile */
+ const char *qualifiers;
+ struct cdump_type *type;
+};
+
+struct cdump_enum_val {
+ const char *name;
+ /* Either NULL, or whatever follows '=' sign */
+ const char *value;
+};
+
+struct cdump_array {
+ const char *size;
+ struct cdump_type *type;
+};
+
+struct cdump_type {
+ enum cdump_type_kind kind;
+ const char *name;
+ union {
+ /* CDUMP_STRUCT / CDUMP_UNION: array */
+ struct cdump_member *members;
+ /* CDUMP_ENUM: array */
+ struct cdump_enum_val *enum_vals;
+ /* CDUMP_ARRAY */
+ struct cdump_array arr;
+ /* CDUMP_POINTER */
+ const struct cdump_type *ptr;
+ } u;
+};
+
+/* The map of typenames to definitions */
+struct cdump_map {
+ STRMAP_MEMBERS(struct cdump_type *);
+};
+
+struct cdump_definitions {
+ struct cdump_map enums;
+ struct cdump_map structs;
+ struct cdump_map unions;
+};
+
+/**
+ * cdump_extract - extract definitions from simple C code.
+ * @ctx: context to tal() the return and @problems from (or NULL)
+ * @code: a nul-terminated string of C definitions
+ * @problems: a pointer to a char * to report problems (or NULL)
+ *
+ * This function parses @code and extracts enum, struct and union definitions
+ * into the return. If there is a parse error, it will return NULL and
+ * allocate a problem string for human consumption.
+ *
+ * Example:
+ * // Returns name of first field of 'struct @name' in @code.
+ * static const char *first_field_of_struct(const char *code,
+ * const char *name)
+ * {
+ * char *problems;
+ * struct cdump_definitions *defs;
+ * struct cdump_type *t;
+ *
+ * defs = cdump_extract(NULL, code, &problems);
+ * if (!defs) {
+ * fprintf(stderr, "%s", problems);
+ * tal_free(problems);
+ * return NULL;
+ * }
+ * t = strmap_get(&defs->structs, name);
+ * if (!t) {
+ * fprintf(stderr, "Couldn't find struct %s", name);
+ * return NULL;
+ * }
+ * assert(t->kind == CDUMP_STRUCT);
+ * return t->u.members[0].name;
+ * }
+ */
+struct cdump_definitions *cdump_extract(const tal_t *ctx, const char *code,
+ char **problems);
+#endif /* CCAN_CDUMP_H */
--- /dev/null
+#include <ccan/cdump/cdump.h>
+/* Include the C files directly. */
+#include <ccan/cdump/cdump.c>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+ struct cdump_definitions *defs;
+ const struct cdump_type *t, *t2;
+ char *ctx = tal(NULL, char), *problems;
+
+ /* This is how many tests you plan to run */
+ plan_tests(16);
+
+ defs = cdump_extract(ctx, "struct foo { struct bar *bar; };\n"
+ "struct bar { int x; };", &problems);
+ ok1(defs);
+ ok1(tal_parent(defs) == ctx);
+ ok1(!problems);
+
+ t = strmap_get(&defs->structs, "foo");
+ ok1(t);
+ t2 = strmap_get(&defs->structs, "bar");
+ ok1(t2);
+
+ ok1(t2->kind == CDUMP_STRUCT);
+ ok1(streq(t2->name, "bar"));
+ ok1(tal_count(t2->u.members) == 1);
+ ok1(t2->u.members[0].type->kind == CDUMP_UNKNOWN);
+ ok1(streq(t2->u.members[0].type->name, "int"));
+
+ ok1(t->kind == CDUMP_STRUCT);
+ ok1(streq(t->name, "foo"));
+ ok1(tal_count(t->u.members) == 1);
+ ok1(streq(t->u.members[0].name, "bar"));
+ ok1(t->u.members[0].type->kind == CDUMP_POINTER);
+ ok1(t->u.members[0].type->u.ptr == t2);
+
+ tal_free(ctx);
+
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}
--- /dev/null
+#include <ccan/cdump/cdump.h>
+/* Include the C files directly. */
+#include <ccan/cdump/cdump.c>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+ struct cdump_definitions *defs;
+ const struct cdump_type *t, *p;
+ char *ctx = tal(NULL, char), *problems;
+
+ /* This is how many tests you plan to run */
+ plan_tests(63);
+
+ defs = cdump_extract(ctx,
+ "struct foo {\n"
+ " long l;\n"
+ " long int li;\n"
+ " unsigned long *ulp;\n"
+ " unsigned long int *ulip;\n"
+ "};", &problems);
+ ok1(defs);
+ ok1(tal_parent(defs) == ctx);
+ ok1(!problems);
+
+ ok1(strmap_empty(&defs->enums));
+ ok1(strmap_empty(&defs->unions));
+ t = strmap_get(&defs->structs, "foo");
+ ok1(t);
+ ok1(t->kind == CDUMP_STRUCT);
+ ok1(streq(t->name, "foo"));
+ ok1(tal_count(t->u.members) == 4);
+
+ ok1(streq(t->u.members[0].name, "l"));
+ p = t->u.members[0].type;
+ ok1(p->kind == CDUMP_UNKNOWN);
+ ok1(streq(p->name, "long"));
+
+ ok1(streq(t->u.members[1].name, "li"));
+ p = t->u.members[1].type;
+ ok1(p->kind == CDUMP_UNKNOWN);
+ ok1(streq(p->name, "long int"));
+
+ ok1(streq(t->u.members[2].name, "ulp"));
+ p = t->u.members[2].type;
+ ok1(p->kind == CDUMP_POINTER);
+ p = p->u.ptr;
+ ok1(p->kind == CDUMP_UNKNOWN);
+ ok1(streq(p->name, "unsigned long"));
+
+ ok1(streq(t->u.members[3].name, "ulip"));
+ p = t->u.members[3].type;
+ ok1(p->kind == CDUMP_POINTER);
+ p = p->u.ptr;
+ ok1(p->kind == CDUMP_UNKNOWN);
+ ok1(streq(p->name, "unsigned long int"));
+
+ defs = cdump_extract(ctx,
+ "struct foo {\n"
+ " volatile long vl;\n"
+ " const long cl;\n"
+ " volatile const long long int *vclli;\n"
+ "};", &problems);
+ ok1(defs);
+ ok1(tal_parent(defs) == ctx);
+ ok1(!problems);
+
+ ok1(strmap_empty(&defs->enums));
+ ok1(strmap_empty(&defs->unions));
+ t = strmap_get(&defs->structs, "foo");
+ ok1(t);
+ ok1(t->kind == CDUMP_STRUCT);
+ ok1(streq(t->name, "foo"));
+ ok1(tal_count(t->u.members) == 3);
+
+ ok1(streq(t->u.members[0].name, "vl"));
+ ok1(streq(t->u.members[0].qualifiers, "volatile"));
+ p = t->u.members[0].type;
+ ok1(p->kind == CDUMP_UNKNOWN);
+ ok1(streq(p->name, "long"));
+
+ ok1(streq(t->u.members[1].name, "cl"));
+ ok1(streq(t->u.members[1].qualifiers, "const"));
+ p = t->u.members[1].type;
+ ok1(p->kind == CDUMP_UNKNOWN);
+ ok1(streq(p->name, "long"));
+
+ ok1(streq(t->u.members[2].name, "vclli"));
+ ok1(streq(t->u.members[2].qualifiers, "volatile const"));
+ p = t->u.members[2].type;
+ ok1(p->kind == CDUMP_POINTER);
+ p = p->u.ptr;
+ ok1(p->kind == CDUMP_UNKNOWN);
+ ok1(streq(p->name, "long long int"));
+
+ defs = cdump_extract(ctx,
+ "struct foo {\n"
+ " volatile struct bar *a, b;\n"
+ "};", &problems);
+ ok1(defs);
+ ok1(tal_parent(defs) == ctx);
+ ok1(!problems);
+
+ ok1(strmap_empty(&defs->enums));
+ ok1(strmap_empty(&defs->unions));
+ t = strmap_get(&defs->structs, "foo");
+ ok1(t);
+ ok1(t->kind == CDUMP_STRUCT);
+ ok1(streq(t->name, "foo"));
+ ok1(tal_count(t->u.members) == 2);
+
+ ok1(streq(t->u.members[0].name, "a"));
+ ok1(streq(t->u.members[0].qualifiers, "volatile"));
+ p = t->u.members[0].type;
+ ok1(p->kind == CDUMP_POINTER);
+ p = p->u.ptr;
+ ok1(p->kind == CDUMP_STRUCT);
+ ok1(streq(p->name, "bar"));
+
+ ok1(streq(t->u.members[1].name, "b"));
+ ok1(streq(t->u.members[1].qualifiers, "volatile"));
+ p = t->u.members[1].type;
+ ok1(p->kind == CDUMP_STRUCT);
+ ok1(streq(p->name, "bar"));
+
+ tal_free(ctx);
+
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}
--- /dev/null
+#include <ccan/cdump/cdump.h>
+/* Include the C files directly. */
+#include <ccan/cdump/cdump.c>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+ struct cdump_definitions *defs;
+ const struct cdump_type *t, *p;
+ char *ctx = tal(NULL, char), *problems;
+
+ /* This is how many tests you plan to run */
+ plan_tests(94);
+
+ defs = cdump_extract(ctx, "enum foo { BAR };", NULL);
+ ok1(defs);
+ ok1(tal_parent(defs) == ctx);
+
+ ok1(strmap_empty(&defs->structs));
+ ok1(strmap_empty(&defs->unions));
+ t = strmap_get(&defs->enums, "foo");
+ ok1(t);
+ ok1(t->kind == CDUMP_ENUM);
+ ok1(streq(t->name, "foo"));
+ ok1(tal_count(t->u.enum_vals) == 1);
+ ok1(streq(t->u.enum_vals[0].name, "BAR"));
+ ok1(!t->u.enum_vals[0].value);
+
+ defs = cdump_extract(ctx, "enum foo { BAR = 7 };", &problems);
+ ok1(defs);
+ ok1(tal_parent(defs) == ctx);
+ ok1(!problems);
+
+ ok1(strmap_empty(&defs->structs));
+ ok1(strmap_empty(&defs->unions));
+ t = strmap_get(&defs->enums, "foo");
+ ok1(t);
+ ok1(t->kind == CDUMP_ENUM);
+ ok1(streq(t->name, "foo"));
+ ok1(tal_count(t->u.enum_vals) == 1);
+ ok1(streq(t->u.enum_vals[0].name, "BAR"));
+ ok1(streq(t->u.enum_vals[0].value, "7"));
+
+ defs = cdump_extract(ctx, "enum foo { BAR = 7, BAZ, FUZZ };", &problems);
+ ok1(defs);
+ ok1(tal_parent(defs) == ctx);
+ ok1(!problems);
+
+ ok1(strmap_empty(&defs->structs));
+ ok1(strmap_empty(&defs->unions));
+ t = strmap_get(&defs->enums, "foo");
+ ok1(t);
+ ok1(t->kind == CDUMP_ENUM);
+ ok1(streq(t->name, "foo"));
+ ok1(tal_count(t->u.enum_vals) == 3);
+ ok1(streq(t->u.enum_vals[0].name, "BAR"));
+ ok1(streq(t->u.enum_vals[0].value, "7"));
+ ok1(streq(t->u.enum_vals[1].name, "BAZ"));
+ ok1(!t->u.enum_vals[1].value);
+ ok1(streq(t->u.enum_vals[2].name, "FUZZ"));
+ ok1(!t->u.enum_vals[2].value);
+
+ defs = cdump_extract(ctx, "struct foo { int x; };", &problems);
+ ok1(defs);
+ ok1(tal_parent(defs) == ctx);
+ ok1(!problems);
+
+ ok1(strmap_empty(&defs->enums));
+ ok1(strmap_empty(&defs->unions));
+ t = strmap_get(&defs->structs, "foo");
+ ok1(t);
+ ok1(t->kind == CDUMP_STRUCT);
+ ok1(streq(t->name, "foo"));
+ ok1(tal_count(t->u.members) == 1);
+ ok1(streq(t->u.members[0].name, "x"));
+ ok1(t->u.members[0].type->kind == CDUMP_UNKNOWN);
+ ok1(streq(t->u.members[0].type->name, "int"));
+
+ defs = cdump_extract(ctx, "struct foo { int x[5<< 1]; struct foo *next; struct unknown **ptrs[10]; };", &problems);
+ ok1(defs);
+ ok1(tal_parent(defs) == ctx);
+ ok1(!problems);
+
+ ok1(strmap_empty(&defs->enums));
+ ok1(strmap_empty(&defs->unions));
+ t = strmap_get(&defs->structs, "foo");
+ ok1(t);
+ ok1(t->kind == CDUMP_STRUCT);
+ ok1(streq(t->name, "foo"));
+ ok1(tal_count(t->u.members) == 3);
+
+ ok1(streq(t->u.members[0].name, "x"));
+ ok1(t->u.members[0].type->kind == CDUMP_ARRAY);
+ ok1(streq(t->u.members[0].type->u.arr.size, "5<< 1"));
+ ok1(t->u.members[0].type->u.arr.type->kind == CDUMP_UNKNOWN);
+ ok1(streq(t->u.members[0].type->u.arr.type->name, "int"));
+
+ ok1(streq(t->u.members[1].name, "next"));
+ ok1(t->u.members[1].type->kind == CDUMP_POINTER);
+ ok1(t->u.members[1].type->u.ptr == t);
+
+ ok1(streq(t->u.members[2].name, "ptrs"));
+ p = t->u.members[2].type;
+ ok1(p->kind == CDUMP_ARRAY);
+ ok1(streq(p->u.arr.size, "10"));
+ p = p->u.arr.type;
+ ok1(p->kind == CDUMP_POINTER);
+ p = p->u.ptr;
+ ok1(p->kind == CDUMP_POINTER);
+ p = p->u.ptr;
+ ok1(p->kind == CDUMP_STRUCT);
+ ok1(streq(p->name, "unknown"));
+ ok1(p->u.members == NULL);
+
+ /* We don't put undefined structs into definition maps. */
+ ok1(!strmap_get(&defs->structs, "unknown"));
+
+ /* unions and comments. */
+ defs = cdump_extract(ctx, "#if 0\n"
+ "/* Normal comment */\n"
+ "struct foo { int x[5 * 7/* Comment */]; };\n"
+ "// One-line comment\n"
+ "union bar { enum sometype x; union yun// Comment\n"
+ " y;};\n"
+ "#endif", &problems);
+ ok1(defs);
+ ok1(tal_parent(defs) == ctx);
+ ok1(!problems);
+ t = strmap_get(&defs->structs, "foo");
+ ok1(t);
+ ok1(tal_count(t->u.members) == 1);
+ ok1(streq(t->u.members[0].name, "x"));
+ ok1(t->u.members[0].type->kind == CDUMP_ARRAY);
+ ok1(streq(t->u.members[0].type->u.arr.size, "5 * 7"));
+ ok1(t->u.members[0].type->u.arr.type->kind == CDUMP_UNKNOWN);
+ ok1(streq(t->u.members[0].type->u.arr.type->name, "int"));
+
+ t = strmap_get(&defs->unions, "bar");
+ ok1(t);
+ ok1(tal_count(t->u.members) == 2);
+ ok1(streq(t->u.members[0].name, "x"));
+ ok1(t->u.members[0].type->kind == CDUMP_ENUM);
+ ok1(streq(t->u.members[0].type->name, "sometype"));
+ ok1(!t->u.members[0].type->u.enum_vals);
+ ok1(streq(t->u.members[1].name, "y"));
+ ok1(t->u.members[1].type->kind == CDUMP_UNION);
+ ok1(streq(t->u.members[1].type->name, "yun"));
+ ok1(!t->u.members[1].type->u.members);
+
+ /* This exits depending on whether all tests passed */
+ return exit_status();
+}