Browse Source

CCAN: add cdump.

Used to print enums.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 9 years ago
parent
commit
1b2de5c830
  1. 1
      ccan/ccan/cdump/LICENSE
  2. 94
      ccan/ccan/cdump/_info
  3. 699
      ccan/ccan/cdump/cdump.c
  4. 106
      ccan/ccan/cdump/cdump.h
  5. 176
      ccan/ccan/cdump/test/run-CDUMP.c
  6. 54
      ccan/ccan/cdump/test/run-arraysize.c
  7. 68
      ccan/ccan/cdump/test/run-attributes.c
  8. 34
      ccan/ccan/cdump/test/run-enum-comma.c
  9. 43
      ccan/ccan/cdump/test/run-forward-decl.c
  10. 75
      ccan/ccan/cdump/test/run-multiline.c
  11. 130
      ccan/ccan/cdump/test/run-qualifiers.c
  12. 152
      ccan/ccan/cdump/test/run.c
  13. 27
      ccan/ccan/cdump/tools/Makefile
  14. 52
      ccan/ccan/cdump/tools/cdump-enumstr.c
  15. 63
      ccan/ccan/strmap/_info
  16. 247
      ccan/ccan/strmap/strmap.c
  17. 226
      ccan/ccan/strmap/strmap.h
  18. 32
      ccan/ccan/strmap/test/run-iterate-const.c
  19. 96
      ccan/ccan/strmap/test/run-order.c
  20. 97
      ccan/ccan/strmap/test/run-prefix.c
  21. 80
      ccan/ccan/strmap/test/run.c
  22. 1
      ccan/ccan/tcon/LICENSE
  23. 75
      ccan/ccan/tcon/_info
  24. 115
      ccan/ccan/tcon/tcon.h
  25. 29
      ccan/ccan/tcon/test/compile_fail-tcon_cast.c
  26. 25
      ccan/ccan/tcon/test/compile_fail.c
  27. 21
      ccan/ccan/tcon/test/compile_ok-void.c
  28. 27
      ccan/ccan/tcon/test/compile_ok.c

1
ccan/ccan/cdump/LICENSE

@ -0,0 +1 @@
../../licenses/BSD-MIT

94
ccan/ccan/cdump/_info

@ -0,0 +1,94 @@
#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;
}

699
ccan/ccan/cdump/cdump.c

@ -0,0 +1,699 @@
/* 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;
}
static size_t to_eol(const char *p)
{
size_t len = strcspn(p, "\n");
/* And any \ continuations. */
while (p[len] && p[len-1] == '\\')
len += strcspn(p+len+1, "\n") + 1;
return 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 = to_eol(code + i);
} else if (code[i] == '/' && code[i+1] == '/') {
/* One line comment. */
len = to_eol(code + i);
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 const struct token *tok_peek(const struct token **toks)
{
/* Ignore removed tokens (eg. comments) */
while (toks[0]->len == 0) {
if (!toks[0]->p)
return NULL;
(*toks)++;
}
return toks[0];
}
static bool tok_is(const struct token **toks, const char *target)
{
const struct token *t = tok_peek(toks);
return (t && t->len == strlen(target)
&& memcmp(t->p, target, t->len) == 0);
}
static const struct token *tok_take(const struct token **toks)
{
const struct token *t = tok_peek(toks);
if (t)
(*toks)++;
return t;
}
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)
{
char *str, *p;
/* Careful to skip erased tokens (eg. comments) */
str = p = tal_arr(ctx, char, until->p - first->p + 1);
while (first != until) {
const struct token *next = first + 1;
if (first->len) {
memcpy(p, first->p, first->len);
p += first->len;
/* Insert space if they weren't adjacent, unless last */
if (next != until) {
if (first->p + first->len != next->p)
*(p++) = ' ';
}
}
first = next;
}
*p = '\0';
return str;
}
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_expr(struct parse_state *ps, const char *term)
{
while (!tok_is(&ps->toks, term)) {
if (tok_take_if(&ps->toks, "(")) {
if (!tok_take_expr(ps, ")"))
return false;
} else if (tok_take_if(&ps->toks, "[")) {
if (!tok_take_expr(ps, "]"))
return false;
} else if (!tok_take(&ps->toks))
return false;
}
return tok_take(&ps->toks);
}
static char *tok_take_expr_str(const tal_t *ctx,
struct parse_state *ps,
const char *term)
{
const struct token *start = tok_peek(&ps->toks);
if (!tok_take_expr(ps, term))
return NULL;
return string_of_toks(ctx, start, ps->toks - 1);
}
/* [ ... */
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_expr_str(arr, ps, "]");
if (!arr->u.arr.size) {
complain(ps, "Could not find closing array size ]");
return false;
}
arr->u.arr.type = *type;
*type = arr;
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;
}
/* CDUMP */
static bool tok_maybe_take_cdump_note(const tal_t *ctx,
struct parse_state *ps, const char **note)
{
*note = NULL;
if (tok_take_if(&ps->toks, "CDUMP")) {
if (!tok_take_if(&ps->toks, "(")) {
complain(ps, "Expected ( after CDUMP");
return false;
}
*note = tok_take_expr_str(ctx, ps, ")");
if (!*note) {
complain(ps, "Expected ) after CDUMP(");
return false;
}
}
return true;
}
/* __attribute__((...)) */
static bool tok_ignore_attribute(struct parse_state *ps)
{
if (!tok_take_if(&ps->toks, "__attribute__"))
return true;
if (!tok_take_if(&ps->toks, "(") || !tok_take_if(&ps->toks, "(")) {
complain(ps, "Expected (( after __attribute__");
return false;
}
if (!tok_take_expr(ps, ")")) {
complain(ps, "Expected expression after __attribute__((");
return false;
}
if (!tok_take_if(&ps->toks, ")")) {
complain(ps, "Expected )) __attribute__((");
return false;
}
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_maybe_take_cdump_note(e, ps, &e->note))
return false;
if (!tok_ignore_attribute(ps))
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;
if (!tok_ignore_attribute(ps))
return false;
/* 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);
if (!tok_ignore_attribute(ps))
return false;
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;
}
/* CDUMP() */
if (!tok_maybe_take_cdump_note(e->u.members,
ps, &m->note))
return false;
if (!tok_ignore_attribute(ps))
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, "}")) {
complain(ps, "Expected } at end of struct/union");
return false;
}
if (!tok_ignore_attribute(ps))
return false;
if (!tok_take_if(&ps->toks, ";")) {
complain(ps, "Expected ; at end of struct/union");
return false;
}
return true;
}
/* 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;
}
/* CDUMP() */
if (!tok_maybe_take_cdump_note(e, ps, &e->note))
return false;
if (!tok_ignore_attribute(ps))
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;
/* GCC extension: comma and end of enum */
if (tok_is(&ps->toks, "}"))
break;
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;
}
/* CDUMP() */
if (!tok_maybe_take_cdump_note(e->u.enum_vals, ps, &v->note))
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, "}")) {
complain(ps, "Expected } at end of enum");
return false;
}
if (!tok_ignore_attribute(ps))
return false;
if (!tok_take_if(&ps->toks, ";")) {
complain(ps, "Expected ; at end of enum");
return false;
}
return true;
}
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_ignore_attribute(&ps))
goto fail;
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;
}

106
ccan/ccan/cdump/cdump.h

@ -0,0 +1,106 @@
/* 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 char *note;
/* const, volatile */
const char *qualifiers;
struct cdump_type *type;
};
struct cdump_enum_val {
const char *name;
const char *note;
/* 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;
const char *note;
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.
*
* Annotations can be attached to structures, unions, enums, members
* and enum values using CDUMP(). This comes after the name (or
* after [] for array member declarations) and usually is removed from
* C compilation using "#define CDUMP(x)".
*
* 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);
* if (t->note)
* printf("Note on struct %s: %s\n", name, t->note);
* return t->u.members[0].name;
* }
*/
struct cdump_definitions *cdump_extract(const tal_t *ctx, const char *code,
char **problems);
#endif /* CCAN_CDUMP_H */

176
ccan/ccan/cdump/test/run-CDUMP.c

@ -0,0 +1,176 @@
#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(111);
defs = cdump_extract(ctx, "enum foo CDUMP(foo note) { BAR CDUMP(bar note) };", 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->note, "foo note"));
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);
ok1(streq(t->u.enum_vals[0].note, "bar note"));
defs = cdump_extract(ctx, "enum foo { BAR CDUMP(bar note) = 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"));
ok1(streq(t->u.enum_vals[0].note, "bar note"));
defs = cdump_extract(ctx, "enum foo {\n"
"BAR CDUMP(bar note) = 7,\n"
"BAZ CDUMP(baz note),\n"
"FUZZ CDUMP(fuzz note) };",
&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(t->note == NULL);
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[0].note, "bar note"));
ok1(streq(t->u.enum_vals[1].name, "BAZ"));
ok1(streq(t->u.enum_vals[1].note, "baz note"));
ok1(!t->u.enum_vals[1].value);
ok1(streq(t->u.enum_vals[2].name, "FUZZ"));
ok1(streq(t->u.enum_vals[2].note, "fuzz note"));
ok1(!t->u.enum_vals[2].value);
defs = cdump_extract(ctx, "struct foo CDUMP(foo note) { int x CDUMP(x note); };", &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(streq(t->note, "foo note"));
ok1(tal_count(t->u.members) == 1);
ok1(streq(t->u.members[0].name, "x"));
ok1(streq(t->u.members[0].note, "x note"));
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] CDUMP(x note); struct foo *next CDUMP(next note); struct unknown **ptrs[10] CDUMP(ptrs note); };", &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(streq(t->u.members[0].note, "x note"));
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(streq(t->u.members[1].note, "next note"));
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"));
ok1(streq(t->u.members[2].note, "ptrs note"));
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 */]CDUMP(x note/*nocomment*/); };\n"
"// One-line comment\n"
"union bar CDUMP(bar note) { enum sometype x CDUMP(x note// Comment\n"
"); 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(t->note == NULL);
ok1(tal_count(t->u.members) == 1);
ok1(streq(t->u.members[0].name, "x"));
ok1(streq(t->u.members[0].note, "x note"));
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(streq(t->note, "bar note"));
ok1(tal_count(t->u.members) == 2);
ok1(streq(t->u.members[0].name, "x"));
ok1(streq(t->u.members[0].note, "x note"));
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].note == NULL);
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();
}

54
ccan/ccan/cdump/test/run-arraysize.c

@ -0,0 +1,54 @@
#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;
char *ctx = tal(NULL, char), *problems;
/* This is how many tests you plan to run */
plan_tests(20);
/* unions and comments. */
defs = cdump_extract(ctx,
"struct foo {\n"
" int x[5 */* Comment */7];\n"
" int y[5// Comment\n"
" * 7];\n"
" int z[5 *\n"
"#ifdef FOO\n"
" 7\n"
"#endif\n"
"];\n"
"};\n", &problems);
ok1(defs);
ok1(tal_parent(defs) == ctx);
ok1(!problems);
t = strmap_get(&defs->structs, "foo");
ok1(t);
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 * 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"));
ok1(streq(t->u.members[1].name, "y"));
ok1(t->u.members[1].type->kind == CDUMP_ARRAY);
ok1(streq(t->u.members[1].type->u.arr.size, "5 * 7"));
ok1(t->u.members[1].type->u.arr.type->kind == CDUMP_UNKNOWN);
ok1(streq(t->u.members[1].type->u.arr.type->name, "int"));
ok1(streq(t->u.members[2].name, "z"));
ok1(t->u.members[2].type->kind == CDUMP_ARRAY);
ok1(streq(t->u.members[2].type->u.arr.size, "5 * 7"));
ok1(t->u.members[2].type->u.arr.type->kind == CDUMP_UNKNOWN);
ok1(streq(t->u.members[2].type->u.arr.type->name, "int"));
/* This exits depending on whether all tests passed */
return exit_status();
}

68
ccan/ccan/cdump/test/run-attributes.c

@ -0,0 +1,68 @@
#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;
char *ctx = tal(NULL, char), *problems;
/* This is how many tests you plan to run */
plan_tests(37);
defs = cdump_extract(ctx, "__attribute__((xxx)) enum foo __attribute__((xxx)) { BAR } __attribute__((xxx));", 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, "__attribute__((xxx)) struct foo __attribute__((xxx)) { int __attribute__((xxx)) x __attribute__((xxx)); } __attribute__((xxx));", &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, __attribute__((xxx)) y; };", &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, "x"));
ok1(t->u.members[0].type->kind == CDUMP_UNKNOWN);
ok1(streq(t->u.members[0].type->name, "int"));
ok1(streq(t->u.members[1].name, "y"));
ok1(t->u.members[1].type->kind == CDUMP_UNKNOWN);
ok1(streq(t->u.members[1].type->name, "int"));
/* This exits depending on whether all tests passed */
return exit_status();
}

34
ccan/ccan/cdump/test/run-enum-comma.c

@ -0,0 +1,34 @@
#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;
char *problems;
/* This is how many tests you plan to run */
plan_tests(12);
defs = cdump_extract(NULL, "enum foo { BAR, BAZ, };", &problems);
ok1(defs);
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) == 2);
ok1(streq(t->u.enum_vals[0].name, "BAR"));
ok1(!t->u.enum_vals[0].value);
ok1(streq(t->u.enum_vals[1].name, "BAZ"));
ok1(!t->u.enum_vals[1].value);
tal_free(defs);
/* This exits depending on whether all tests passed */
return exit_status();
}

43
ccan/ccan/cdump/test/run-forward-decl.c

@ -0,0 +1,43 @@
#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();
}

75
ccan/ccan/cdump/test/run-multiline.c

@ -0,0 +1,75 @@
#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;
char *ctx = tal(NULL, char), *problems;
/* This is how many tests you plan to run */
plan_tests(30);
/* Multi-line preprocessor statement. */
defs = cdump_extract(ctx,
"#if \\\n"
"SOME\\\n"
"THING\n"
"enum foo { BAR };", &problems);
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 {\n"
"#if \\\n"
"SOME\\\n"
"THING\n"
" BAR };", &problems);
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);
/* Multi-line "one-line" comment. */
defs = cdump_extract(ctx,
"enum foo {\n"
"// Comment \\\n"
"SOME\\\n"
"THING\n"
" BAR };", &problems);
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);
/* This exits depending on whether all tests passed */
return exit_status();
}

130
ccan/ccan/cdump/test/run-qualifiers.c

@ -0,0 +1,130 @@
#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();
}

152
ccan/ccan/cdump/test/run.c

@ -0,0 +1,152 @@
#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();
}

27
ccan/ccan/cdump/tools/Makefile

@ -0,0 +1,27 @@
CCAN_OBJS := ccan-tal.o ccan-tal-str.o ccan-tal-grab_file.o ccan-cdump.o ccan-take.o ccan-list.o ccan-read_write_all.o ccan-strmap.o ccan-noerr.o
CCANDIR:=../../..
CFLAGS := -I$(CCANDIR) -Wall
cdump-enumstr: cdump-enumstr.o $(CCAN_OBJS)
clean:
$(RM) cdump-enumstr.o $(CCAN_OBJS)
ccan-tal.o: $(CCANDIR)/ccan/tal/tal.c
$(CC) $(CFLAGS) -c -o $@ $<
ccan-strmap.o: $(CCANDIR)/ccan/strmap/strmap.c
$(CC) $(CFLAGS) -c -o $@ $<
ccan-noerr.o: $(CCANDIR)/ccan/noerr/noerr.c
$(CC) $(CFLAGS) -c -o $@ $<
ccan-cdump.o: $(CCANDIR)/ccan/cdump/cdump.c
$(CC) $(CFLAGS) -c -o $@ $<
ccan-tal-str.o: $(CCANDIR)/ccan/tal/str/str.c
$(CC) $(CFLAGS) -c -o $@ $<
ccan-tal-grab_file.o: $(CCANDIR)/ccan/tal/grab_file/grab_file.c
$(CC) $(CFLAGS) -c -o $@ $<
ccan-take.o: $(CCANDIR)/ccan/take/take.c
$(CC) $(CFLAGS) -c -o $@ $<
ccan-list.o: $(CCANDIR)/ccan/list/list.c
$(CC) $(CFLAGS) -c -o $@ $<
ccan-read_write_all.o: $(CCANDIR)/ccan/read_write_all/read_write_all.c
$(CC) $(CFLAGS) -c -o $@ $<

52
ccan/ccan/cdump/tools/cdump-enumstr.c

@ -0,0 +1,52 @@
#include <ccan/cdump/cdump.h>
#include <ccan/tal/grab_file/grab_file.h>
#include <ccan/err/err.h>
static bool dump_map(const char *name, struct cdump_type *t, void *unused)
{
size_t i;
printf("struct {\n"
" enum %s v;\n"
" const char *name;\n"
"} enum_%s_names[] = {\n", name, name);
for (i = 0; i < tal_count(t->u.enum_vals); i++)
printf(" { %s, \"%s\" },\n",
t->u.enum_vals[i].name,
t->u.enum_vals[i].name);
printf(" { 0, NULL } };\n");
return true;
}
int main(int argc, char *argv[])
{
char *code, *problems;
struct cdump_definitions *defs;
if (argc < 2)
errx(1, "Usage: cdump-enumstr <filename> [<enums>...]");
code = grab_file(NULL, streq(argv[1], "-") ? NULL : argv[1]);
if (!code)
err(1, "Reading %s", argv[1]);
defs = cdump_extract(code, code, &problems);
if (!defs)
errx(1, "Parsing %s:\n%s", argv[1], problems);
if (argc == 2)
strmap_iterate(&defs->enums, dump_map, NULL);
else {
unsigned int i;
struct cdump_type *t;
for (i = 2; i < argc; i++) {
t = strmap_get(&defs->enums, argv[i]);
if (!t)
errx(1, "Enum %s not found", argv[i]);
dump_map(argv[i], t, NULL);
}
}
return 0;
}

63
ccan/ccan/strmap/_info

@ -0,0 +1,63 @@
#include "config.h"
#include <stdio.h>
#include <string.h>
/**
* strmap - an ordered map of strings to values
*
* This code implements an ordered map of strings as a critbit tree. See:
*
* http://cr.yp.to/critbit.html
* http://github.com/agl/critbit (which this code is based on)
*
* License: CC0 (but some dependencies are LGPL!)
* Author: Rusty Russell <rusty@rustcorp.com.au>
* Ccanlint:
* license_depends_compat FAIL
*
* Example:
* #include <ccan/strmap/strmap.h>
* #include <stdio.h>
*
* static bool dump(const char *member, size_t value, void *unused)
* {
* printf("%s at %zu. ", member, value);
* // true means keep going with iteration.
* return true;
* }
*
* int main(int argc, char *argv[])
* {
* size_t i;
* struct { STRMAP_MEMBERS(size_t); } map;
*
* strmap_init(&map);
* for (i = 1; i < argc; i++)
* // This only adds the first time for this arg.
* strmap_add(&map, argv[i], i);
*
* strmap_iterate(&map, dump, NULL);
* printf("\n");
* return 0;
* }
* // Given 'foo' outputs 'foo at 1. '
* // Given 'foo bar' outputs 'bar at 2. foo at 1. '
* // Given 'foo foo bar zebra' outputs 'bar at 3. foo at 1. zebra at 4. '
*/
int main(int argc, char *argv[])
{
/* Expect exactly one argument */
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0) {
printf("ccan/ilog\n"
"ccan/short_types\n"
"ccan/str\n"
"ccan/tcon\n"
"ccan/typesafe_cb\n");
return 0;
}
return 1;
}

247
ccan/ccan/strmap/strmap.c

@ -0,0 +1,247 @@
/* This code is based on ccan/strset.c. */
#include <ccan/strmap/strmap.h>
#include <ccan/short_types/short_types.h>
#include <ccan/str/str.h>
#include <ccan/ilog/ilog.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
struct node {
/* These point to strings or nodes. */
struct strmap child[2];
/* The byte number where first bit differs. */
size_t byte_num;
/* The bit where these children differ. */
u8 bit_num;
};
/* Closest member to this in a non-empty map. */
static struct strmap *closest(struct strmap *n, const char *member)
{
size_t len = strlen(member);
const u8 *bytes = (const u8 *)member;
/* Anything with NULL value is a node. */
while (!n->v) {
u8 direction = 0;
if (n->u.n->byte_num < len) {
u8 c = bytes[n->u.n->byte_num];
direction = (c >> n->u.n->bit_num) & 1;
}
n = &n->u.n->child[direction];
}
return n;
}
void *strmap_get_(const struct strmap *map, const char *member)
{
struct strmap *n;
/* Not empty map? */
if (map->u.n) {
n = closest((struct strmap *)map, member);
if (streq(member, n->u.s))
return n->v;
}
errno = ENOENT;
return NULL;
}
bool strmap_add_(struct strmap *map, const char *member, const void *value)
{
size_t len = strlen(member);
const u8 *bytes = (const u8 *)member;
struct strmap *n;
struct node *newn;
size_t byte_num;
u8 bit_num, new_dir;
assert(value);
/* Empty map? */
if (!map->u.n) {
map->u.s = member;
map->v = (void *)value;
return true;
}
/* Find closest existing member. */
n = closest(map, member);
/* Find where they differ. */
for (byte_num = 0; n->u.s[byte_num] == member[byte_num]; byte_num++) {
if (member[byte_num] == '\0') {
/* All identical! */
errno = EEXIST;
return false;
}
}
/* Find which bit differs (if we had ilog8, we'd use it) */
bit_num = ilog32_nz((u8)n->u.s[byte_num] ^ bytes[byte_num]) - 1;
assert(bit_num < CHAR_BIT);
/* Which direction do we go at this bit? */
new_dir = ((bytes[byte_num]) >> bit_num) & 1;
/* Allocate new node. */
newn = malloc(sizeof(*newn));
if (!newn) {
errno = ENOMEM;
return false;
}
newn->byte_num = byte_num;
newn->bit_num = bit_num;
newn->child[new_dir].v = (void *)value;
newn->child[new_dir].u.s = member;
/* Find where to insert: not closest, but first which differs! */
n = map;
while (!n->v) {
u8 direction = 0;
if (n->u.n->byte_num > byte_num)
break;
/* Subtle: bit numbers are "backwards" for comparison */
if (n->u.n->byte_num == byte_num && n->u.n->bit_num < bit_num)
break;
if (n->u.n->byte_num < len) {
u8 c = bytes[n->u.n->byte_num];
direction = (c >> n->u.n->bit_num) & 1;
}
n = &n->u.n->child[direction];
}
newn->child[!new_dir] = *n;
n->u.n = newn;
n->v = NULL;
return true;
}
char *strmap_del_(struct strmap *map, const char *member, void **valuep)
{
size_t len = strlen(member);
const u8 *bytes = (const u8 *)member;
struct strmap *parent = NULL, *n;
const char *ret = NULL;
u8 direction = 0; /* prevent bogus gcc warning. */
/* Empty map? */
if (!map->u.n) {
errno = ENOENT;
return NULL;
}
/* Find closest, but keep track of parent. */
n = map;
/* Anything with NULL value is a node. */
while (!n->v) {
u8 c = 0;
parent = n;
if (n->u.n->byte_num < len) {
c = bytes[n->u.n->byte_num];
direction = (c >> n->u.n->bit_num) & 1;
} else
direction = 0;
n = &n->u.n->child[direction];
}
/* Did we find it? */
if (!streq(member, n->u.s)) {
errno = ENOENT;
return NULL;
}
ret = n->u.s;
if (valuep)
*valuep = n->v;
if (!parent) {
/* We deleted last node. */
map->u.n = NULL;
} else {
struct node *old = parent->u.n;
/* Raise other node to parent. */
*parent = old->child[!direction];
free(old);
}
return (char *)ret;
}
static bool iterate(struct strmap n,
bool (*handle)(const char *, void *, void *),
const void *data)
{
if (n.v)
return handle(n.u.s, n.v, (void *)data);
return iterate(n.u.n->child[0], handle, data)
&& iterate(n.u.n->child[1], handle, data);
}
void strmap_iterate_(const struct strmap *map,
bool (*handle)(const char *, void *, void *),
const void *data)
{
/* Empty map? */
if (!map->u.n)
return;
iterate(*map, handle, data);
}
const struct strmap *strmap_prefix_(const struct strmap *map,
const char *prefix)
{
const struct strmap *n, *top;
size_t len = strlen(prefix);
const u8 *bytes = (const u8 *)prefix;
/* Empty map -> return empty map. */
if (!map->u.n)
return map;
top = n = map;
/* We walk to find the top, but keep going to check prefix matches. */
while (!n->v) {
u8 c = 0, direction;
if (n->u.n->byte_num < len)
c = bytes[n->u.n->byte_num];
direction = (c >> n->u.n->bit_num) & 1;
n = &n->u.n->child[direction];
if (c)
top = n;
}
if (!strstarts(n->u.s, prefix)) {
/* Convenient return for prefixes which do not appear in map. */
static const struct strmap empty_map;
return &empty_map;
}
return top;
}
static void clear(struct strmap n)
{
if (!n.v) {
clear(n.u.n->child[0]);
clear(n.u.n->child[1]);
free(n.u.n);
}
}
void strmap_clear_(struct strmap *map)
{
if (map->u.n)
clear(*map);
map->u.n = NULL;
}

226
ccan/ccan/strmap/strmap.h

@ -0,0 +1,226 @@
#ifndef CCAN_STRMAP_H
#define CCAN_STRMAP_H
#include "config.h"
#include <ccan/tcon/tcon.h>
#include <ccan/typesafe_cb/typesafe_cb.h>
#include <stdlib.h>
#include <stdbool.h>
/**
* struct strmap - representation of a string map
*
* It's exposed here to allow you to embed it and so we can inline the
* trivial functions.
*/
struct strmap {
union {
struct node *n;
const char *s;
} u;
void *v;
};
/**
* STRMAP_MEMBERS - declare members for a type-specific strmap.
* @type: type for this map's values, or void * for any pointer.
*
* You use this to create your own typed strmap for a particular type.
* You can use an integer type, *but* remember you can't use "0" as a
* value!
*
* Example:
* struct strmap_intp {
* STRMAP_MEMBERS(int *);
* };
*/
#define STRMAP_MEMBERS(type) \
struct strmap raw; \
TCON(type canary)
/**
* strmap_init - initialize a string map (empty)
* @map: the typed strmap to initialize.
*
* For completeness; if you've arranged for it to be NULL already you don't
* need this.
*
* Example:
* struct strmap_intp map;
*
* strmap_init(&map);
*/
#define strmap_init(map) strmap_init_(&(map)->raw)
static inline void strmap_init_(struct strmap *map)
{
map->u.n = NULL;
}
/**
* strmap_empty - is this string map empty?
* @map: the typed strmap to check.
*
* Example:
* if (!strmap_empty(&map))
* abort();
*/
#define strmap_empty(map) strmap_empty_(&(map)->raw)
static inline bool strmap_empty_(const struct strmap *map)
{
return map->u.n == NULL;
}
/**
* strmap_get - get a value from a string map
* @map: the typed strmap to search.
* @member: the string to search for.
*
* Returns the value, or NULL if it isn't in the map (and sets errno = ENOENT).
*
* Example:
* int *val = strmap_get(&map, "hello");
* if (val)
* printf("hello => %i\n", *val);
*/
#define strmap_get(map, member) \
tcon_cast((map), canary, strmap_get_(&(map)->raw, (member)))
void *strmap_get_(const struct strmap *map, const char *member);
/**
* strmap_add - place a member in the string map.
* @map: the typed strmap to add to.
* @member: the string to place in the map.
* @v: the (non-NULL) value.
*
* This returns false if we run out of memory (errno = ENOMEM), or
* (more normally) if that string already appears in the map (EEXIST).
*
* Note that the pointer is placed in the map, the string is not copied. If
* you want a copy in the map, use strdup(). Similarly for the value.
*
* Example:
* val = malloc(sizeof *val);
* *val = 17;
* if (!strmap_add(&map, "goodbye", val))
* printf("goodbye was already in the map\n");
*/
#define strmap_add(map, member, value) \
strmap_add_(&tcon_check((map), canary, (value))->raw, \
(member), (void *)(value))
bool strmap_add_(struct strmap *map, const char *member, const void *value);
/**
* strmap_del - remove a member from the string map.
* @map: the typed strmap to delete from.
* @member: the string to remove from the map.
* @valuep: the value (if non-NULL)
*
* This returns the string which was passed to strmap_map(), or NULL if
* it was not in the map (and sets errno = ENOENT).
*
* This means that if you allocated a string (eg. using strdup()), you
* can free it here. Similarly, the value is returned in @valuep if
* @valuep is not NULL.
*
* Example:
* if (!strmap_del(&map, "goodbye", NULL))
* printf("goodbye was not in the map?\n");
*/
#define strmap_del(map, member, valuep) \
strmap_del_(&tcon_check_ptr((map), canary, valuep)->raw, \
(member), (void **)valuep)
char *strmap_del_(struct strmap *map, const char *member, void **valuep);
/**
* strmap_clear - remove every member from the map.
* @map: the typed strmap to clear.
*
* The map will be empty after this.
*
* Example:
* strmap_clear(&map);
*/
#define strmap_clear(map) strmap_clear_(&(map)->raw)
void strmap_clear_(struct strmap *map);
/**
* strmap_iterate - ordered iteration over a map
* @map: the typed strmap to iterate through.
* @handle: the function to call.
* @arg: the argument for the function (types should match).
*
* @handle's prototype should be:
* bool @handle(const char *member, type value, typeof(arg) arg)
*
* If @handle returns false, the iteration will stop.
* You should not alter the map within the @handle function!
*
* Example:
* struct strmap_intp {
* STRMAP_MEMBERS(int *);
* };
* static bool dump_some(const char *member, int *value, int *num)
* {
* // Only dump out num nodes.
* if (*(num--) == 0)
* return false;
* printf("%s=>%i\n", member, *value);
* return true;
* }
*
* static void dump_map(const struct strmap_intp *map)
* {
* int max = 100;
* strmap_iterate(map, dump_some, &max);
* if (max < 0)
* printf("... (truncated to 100 entries)\n");
* }
*/
#define strmap_iterate(map, handle, arg) \
strmap_iterate_(&(map)->raw, \
typesafe_cb_cast(bool (*)(const char *, \
void *, void *), \
bool (*)(const char *, \
tcon_type((map), canary), \
__typeof__(arg)), (handle)), \
(arg))
void strmap_iterate_(const struct strmap *map,
bool (*handle)(const char *, void *, void *),
const void *data);
/**
* strmap_prefix - return a submap matching a prefix
* @map: the map.
* @prefix: the prefix.
*
* This returns a pointer into @map, so don't alter @map while using
* the return value. You can use strmap_iterate(), strmap_get() or
* strmap_empty() on the returned pointer.
*
* Example:
* static void dump_prefix(const struct strmap_intp *map,
* const char *prefix)
* {
* int max = 100;
* printf("Nodes with prefix %s:\n", prefix);
* strmap_iterate(strmap_prefix(map, prefix), dump_some, &max);
* if (max < 0)
* printf("... (truncated to 100 entries)\n");
* }
*/
#if HAVE_TYPEOF
#define strmap_prefix(map, prefix) \
((const __typeof__(map))strmap_prefix_(&(map)->raw, (prefix)))
#else
#define strmap_prefix(map, prefix) \
((const void *)strmap_prefix_(&(map)->raw, (prefix)))
#endif
const struct strmap *strmap_prefix_(const struct strmap *map,
const char *prefix);
#endif /* CCAN_STRMAP_H */

32
ccan/ccan/strmap/test/run-iterate-const.c

@ -0,0 +1,32 @@
#include <ccan/strmap/strmap.h>
#include <ccan/strmap/strmap.c>
#include <ccan/tap/tap.h>
static bool found = false;
/* Make sure const args work. */
static bool find_string(const char *str, char *member, const char *cmp)
{
if (strcmp(member, cmp) == 0)
found = true;
return false;
}
int main(void)
{
struct strmap_charp {
STRMAP_MEMBERS(char *);
} map;
plan_tests(3);
strmap_init(&map);
ok1(strmap_add(&map, "hello", "hello"));
ok1(strmap_add(&map, "world", "world"));
strmap_iterate(&map, find_string, (const char *)"hello");
ok1(found);
strmap_clear(&map);
/* This exits depending on whether all tests passed */
return exit_status();
}

96
ccan/ccan/strmap/test/run-order.c

@ -0,0 +1,96 @@
#include <ccan/strmap/strmap.h>
#include <ccan/strmap/strmap.c>
#include <ccan/tap/tap.h>
#include <stdio.h>
#define NUM 1000
static bool in_order(const char *member, char *value, unsigned int *count)
{
int i = atoi(member);
ok1(i == atoi(value));
ok1(*count == i);
(*count)++;
return true;
}
static bool in_order_by_2(const char *member, char *value, unsigned int *count)
{
int i = atoi(member);
ok1(i == atoi(value));
ok1(*count == i);
(*count) += 2;
return true;
}
static bool dump(const char *member, char *value, bool *ok)
{
diag("%s=>%s", member, value);
if (value != member + 1)
*ok = false;
return true;
}
int main(void)
{
struct strmap_charp {
STRMAP_MEMBERS(char *);
} map;
unsigned int i;
char *str[NUM];
bool dump_ok;
plan_tests(1 + NUM * 4 + 3 * NUM);
strmap_init(&map);
for (i = 0; i < NUM; i++) {
char template[10];
sprintf(template, "%08u", i);
str[i] = strdup(template);
}
for (i = 0; i < NUM; i++)
strmap_add(&map, str[i], str[i]+1);
dump_ok = true;
strmap_iterate(&map, dump, &dump_ok);
ok1(dump_ok);
/* Iterate. */
i = 0;
strmap_iterate(&map, in_order, &i);
/* Preserve order after deletion. */
for (i = 0; i < NUM; i += 2) {
char *v;
ok1(strmap_del(&map, str[i], &v) == str[i]);
ok1(v == str[i] + 1);
}
i = 1;
strmap_iterate(&map, in_order_by_2, &i);
for (i = 1; i < NUM; i += 2) {
char *v;
ok1(strmap_del(&map, str[i], &v) == str[i]);
ok1(v == str[i] + 1);
}
/* empty traverse. */
strmap_iterate(&map, in_order_by_2, (unsigned int *)NULL);
/* insert backwards, should be fine. */
for (i = 0; i < NUM; i++)
strmap_add(&map, str[NUM-1-i], str[NUM-1-i]+1);
i = 0;
strmap_iterate(&map, in_order, &i);
strmap_clear(&map);
for (i = 0; i < NUM; i++)
free(str[i]);
/* This exits depending on whether all tests passed */
return exit_status();
}

97
ccan/ccan/strmap/test/run-prefix.c

@ -0,0 +1,97 @@
#include <ccan/strmap/strmap.h>
#include <ccan/strmap/strmap.c>
#include <ccan/tap/tap.h>
#include <stdio.h>
/* Must be > 100, see below. */
#define NUM 200
static bool in_order(const char *index, char *value, unsigned int *count)
{
int i = atoi(index);
ok1(i == atoi(value));
ok1(*count == i);
(*count)++;
return true;
}
static bool find_empty(const char *index, char *value, char *empty)
{
if (index == empty)
pass("Found empty entry!");
return true;
}
int main(void)
{
struct map {
STRMAP_MEMBERS(char *);
};
struct map map;
const struct map *sub;
unsigned int i;
char *str[NUM], *empty;
plan_tests(8 + 2 * (1 + 10 + 100) + 1);
strmap_init(&map);
for (i = 0; i < NUM; i++) {
char template[10];
sprintf(template, "%08u", i);
str[i] = strdup(template);
}
/* All prefixes of an empty map are empty. */
sub = strmap_prefix(&map, "a");
ok1(strmap_empty(sub));
sub = strmap_prefix(&map, "");
ok1(strmap_empty(sub));
for (i = 0; i < NUM; i++)
strmap_add(&map, str[i], str[i]+1);
/* Nothing */
sub = strmap_prefix(&map, "a");
ok1(strmap_empty(sub));
/* Everything */
sub = strmap_prefix(&map, "0");
ok1(sub->raw.u.n == map.raw.u.n);
sub = strmap_prefix(&map, "");
ok1(sub->raw.u.n == map.raw.u.n);
/* Single. */
sub = strmap_prefix(&map, "00000000");
i = 0;
strmap_iterate(sub, in_order, &i);
ok1(i == 1);
/* First 10. */
sub = strmap_prefix(&map, "0000000");
i = 0;
strmap_iterate(sub, in_order, &i);
ok1(i == 10);
/* First 100. */
sub = strmap_prefix(&map, "000000");
i = 0;
strmap_iterate(sub, in_order, &i);
ok1(i == 100);
/* Everything, *plus* empty string. */
empty = strdup("");
strmap_add(&map, empty, empty);
sub = strmap_prefix(&map, "");
/* Check we get *our* empty string back! */
strmap_iterate(sub, find_empty, empty);
strmap_clear(&map);
for (i = 0; i < NUM; i++)
free(str[i]);
free(empty);
/* This exits depending on whether all tests passed */
return exit_status();
}

80
ccan/ccan/strmap/test/run.c

@ -0,0 +1,80 @@
#include <ccan/strmap/strmap.h>
#include <ccan/strmap/strmap.c>
#include <ccan/tap/tap.h>
int main(void)
{
struct strmap_charp {
STRMAP_MEMBERS(char *);
} map;
const char str[] = "hello";
const char val[] = "there";
const char none[] = "";
char *dup = strdup(str);
char *v;
/* This is how many tests you plan to run */
plan_tests(42);
strmap_init(&map);
ok1(!strmap_get(&map, str));
ok1(errno == ENOENT);
ok1(!strmap_get(&map, none));
ok1(errno == ENOENT);
ok1(!strmap_del(&map, str, NULL));
ok1(errno == ENOENT);
ok1(!strmap_del(&map, none, NULL));
ok1(errno == ENOENT);
ok1(strmap_add(&map, str, val));
ok1(strmap_get(&map, str) == val);
/* We compare the string, not the pointer. */
ok1(strmap_get(&map, dup) == val);
ok1(!strmap_get(&map, none));
ok1(errno == ENOENT);
/* Add a duplicate should fail. */
ok1(!strmap_add(&map, dup, val));
ok1(errno == EEXIST);
ok1(strmap_get(&map, dup) == val);
/* Delete should return original string. */
ok1(strmap_del(&map, dup, &v) == str);
ok1(v == val);
ok1(!strmap_get(&map, str));
ok1(errno == ENOENT);
ok1(!strmap_get(&map, none));
ok1(errno == ENOENT);
/* Try insert and delete of empty string. */
ok1(strmap_add(&map, none, none));
ok1(strmap_get(&map, none) == none);
ok1(!strmap_get(&map, str));
ok1(errno == ENOENT);
/* Delete should return original string. */
ok1(strmap_del(&map, "", &v) == none);
ok1(v == none);
ok1(!strmap_get(&map, str));
ok1(errno == ENOENT);
ok1(!strmap_get(&map, none));
ok1(errno == ENOENT);
/* Both at once... */
ok1(strmap_add(&map, none, none));
ok1(strmap_add(&map, str, val));
ok1(strmap_get(&map, str) == val);
ok1(strmap_get(&map, none) == none);
ok1(strmap_del(&map, "does not exist", NULL) == NULL);
ok1(strmap_del(&map, "", NULL) == none);
ok1(strmap_get(&map, str) == val);
ok1(strmap_del(&map, dup, &v) == str);
ok1(v == val);
ok1(strmap_empty(&map));
free(dup);
/* This exits depending on whether all tests passed */
return exit_status();
}

1
ccan/ccan/tcon/LICENSE

@ -0,0 +1 @@
../../licenses/CC0

75
ccan/ccan/tcon/_info

@ -0,0 +1,75 @@
#include "config.h"
#include <stdio.h>
#include <string.h>
/**
* tcon - routines for creating typesafe generic containers
*
* This code lets users create a structure with a typecanary; your API
* is then a set of macros which check the type canary before calling
* the generic routines.
*
* Example:
* #include <ccan/tcon/tcon.h>
* #include <stdio.h>
*
* // A simple container class. Can only contain one thing though!
* struct container {
* void *contents;
* };
* static inline void container_add_raw(struct container *c, void *p)
* {
* c->contents = p;
* }
* static inline void *container_get_raw(struct container *c)
* {
* return c->contents;
* }
*
* // This lets the user define their container type; includes a
* // "type canary" to check types against.
* #define DEFINE_TYPED_CONTAINER_STRUCT(name, type) \
* struct name { struct container raw; TCON(type canary); }
*
* // These macros make sure the container type and pointer match.
* #define container_add(c, p) \
* container_add_raw(&tcon_check((c), canary, (p))->raw, (p))
* #define container_get(c) \
* tcon_cast((c), canary, container_get_raw(&(c)->raw))
*
* // Now, let's define two different containers.
* DEFINE_TYPED_CONTAINER_STRUCT(int_container, int *);
* DEFINE_TYPED_CONTAINER_STRUCT(string_container, char *);
*
* int main(int argc, char *argv[])
* {
* struct int_container ic;
* struct string_container sc;
*
* // We would get a warning if we used the wrong types...
* container_add(&ic, &argc);
* container_add(&sc, argv[argc-1]);
*
* printf("Last arg is %s of %i arguments\n",
* container_get(&sc), *container_get(&ic) - 1);
* return 0;
* }
* // Given "foo" outputs "Last arg is foo of 1 arguments"
* // Given "foo bar" outputs "Last arg is bar of 2 arguments"
*
* License: CC0 (Public domain)
*
* Author: Rusty Russell <rusty@rustcorp.com.au>
*/
int main(int argc, char *argv[])
{
/* Expect exactly one argument */
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0) {
return 0;
}
return 1;
}

115
ccan/ccan/tcon/tcon.h

@ -0,0 +1,115 @@
/* CC0 (Public domain) - see LICENSE file for details */
#ifndef CCAN_TCON_H
#define CCAN_TCON_H
#include "config.h"
/**
* TCON - declare a _tcon type containing canary variables.
* @decls: the semi-colon separated list of type canaries.
*
* This declares a _tcon member for a structure. It should be the
* last element in your structure; with sufficient compiler support it
* will not use any actual storage. tcon_check() will compare
* expressions with one of these "type canaries" to cause warnings if
* the container is misused.
*
* A type of "void *" will allow tcon_check() to pass on any (pointer) type.
*
* Example:
* // Simply typesafe linked list.
* struct list_head {
* struct list_head *prev, *next;
* };
*
* struct string_list {
* struct list_head raw;
* TCON(char *canary);
* };
*
* // More complex: mapping from one type to another.
* struct map {
* void *contents;
* };
*
* struct int_to_string_map {
* struct map raw;
* TCON(char *charp_canary; int int_canary);
* };
*/
#if HAVE_FLEXIBLE_ARRAY_MEMBER
#define TCON(decls) struct { decls; } _tcon[]
#else
#define TCON(decls) struct { decls; } _tcon[1]
#endif
/**
* tcon_check - typecheck a typed container
* @x: the structure containing the TCON.
* @canary: which canary to check against.
* @expr: the expression whose type must match the TCON (not evaluated)
*
* This macro is used to check that the expression is the type
* expected for this structure (note the "useless" sizeof() argument
* which contains this comparison with the type canary).
*
* It evaluates to @x so you can chain it.
*
* Example:
* #define tlist_add(h, n, member) \
* list_add(&tcon_check((h), canary, (n))->raw, &(n)->member)
*/
#define tcon_check(x, canary, expr) \
(sizeof((x)->_tcon[0].canary == (expr)) ? (x) : (x))
/**
* tcon_check_ptr - typecheck a typed container
* @x: the structure containing the TCON.
* @canary: which canary to check against.
* @expr: the expression whose type must match &TCON (not evaluated)
*
* This macro is used to check that the expression is a pointer to the type
* expected for this structure (note the "useless" sizeof() argument
* which contains this comparison with the type canary), or NULL.
*
* It evaluates to @x so you can chain it.
*/
#define tcon_check_ptr(x, canary, expr) \
(sizeof(&(x)->_tcon[0].canary == (expr)) ? (x) : (x))
/**
* tcon_type - the type within a container (or void *)
* @x: the structure containing the TCON.
* @canary: which canary to check against.
*/
#if HAVE_TYPEOF
#define tcon_type(x, canary) __typeof__((x)->_tcon[0].canary)
#else
#define tcon_type(x, canary) void *
#endif
/**
* tcon_ptr_type - pointer to the type within a container (or void *)
* @x: the structure containing the TCON.
* @canary: which canary to check against.
*/
#if HAVE_TYPEOF
#define tcon_ptr_type(x, canary) __typeof__(&(x)->_tcon[0].canary)
#else
#define tcon_ptr_type(x, canary) void *
#endif
/**
* tcon_cast - cast to a canary type for this container (or void *)
* @x: a structure containing the TCON.
* @canary: which canary to cast to.
* @expr: the value to cast
*
* This is used to cast to the correct type for this container. If the
* platform doesn't HAVE_TYPEOF, then it casts to void * (which will
* cause a warning if the user doesn't expect a pointer type).
*/
#define tcon_cast(x, canary, expr) ((tcon_type((x), canary))(expr))
#define tcon_cast_ptr(x, canary, expr) ((tcon_ptr_type((x), canary))(expr))
#endif /* CCAN_TCON_H */

29
ccan/ccan/tcon/test/compile_fail-tcon_cast.c

@ -0,0 +1,29 @@
#include <ccan/tcon/tcon.h>
#include <stdlib.h>
struct container {
void *p;
};
struct int_and_charp_container {
struct container raw;
TCON(int *tc1; char *tc2);
};
int main(int argc, char *argv[])
{
struct int_and_charp_container icon;
#ifdef FAIL
#if !HAVE_TYPEOF
#error We cannot detect type problems without HAVE_TYPEOF
#endif
char *
#else
int *
#endif
x;
icon.raw.p = NULL;
x = tcon_cast(&icon, tc1, icon.raw.p);
return x != NULL ? 0 : 1;
}

25
ccan/ccan/tcon/test/compile_fail.c

@ -0,0 +1,25 @@
#include <ccan/tcon/tcon.h>
#include <stdlib.h>
struct container {
void *p;
};
struct int_container {
struct container raw;
TCON(int *canary);
};
int main(int argc, char *argv[])
{
struct int_container icon;
#ifdef FAIL
char *
#else
int *
#endif
x = NULL;
tcon_check(&icon, canary, x)->raw.p = x;
return 0;
}

21
ccan/ccan/tcon/test/compile_ok-void.c

@ -0,0 +1,21 @@
#include <ccan/tcon/tcon.h>
#include <stdlib.h>
struct container {
void *p;
};
struct void_container {
struct container raw;
TCON(void *canary);
};
int main(int argc, char *argv[])
{
struct void_container vcon;
tcon_check(&vcon, canary, NULL)->raw.p = NULL;
tcon_check(&vcon, canary, argv[0])->raw.p = NULL;
tcon_check(&vcon, canary, main)->raw.p = NULL;
return 0;
}

27
ccan/ccan/tcon/test/compile_ok.c

@ -0,0 +1,27 @@
#include <ccan/tcon/tcon.h>
#include <stdlib.h>
struct container {
void *p;
};
struct int_container {
struct container raw;
TCON(int tc);
};
struct charp_and_int_container {
struct container raw;
TCON(int tc1; char *tc2);
};
int main(int argc, char *argv[])
{
struct int_container icon;
struct charp_and_int_container cicon;
tcon_check(&icon, tc, 7)->raw.p = NULL;
tcon_check(&cicon, tc1, 7)->raw.p = argv[0];
tcon_check(&cicon, tc2, argv[0])->raw.p = argv[0];
return 0;
}
Loading…
Cancel
Save