mirror of
https://github.com/aljazceru/lightning.git
synced 2026-02-18 04:24:28 +01:00
CCAN: add cdump.
Used to print enums. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
1
ccan/ccan/cdump/LICENSE
Symbolic link
1
ccan/ccan/cdump/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../licenses/BSD-MIT
|
||||
94
ccan/ccan/cdump/_info
Normal file
94
ccan/ccan/cdump/_info
Normal file
@@ -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
Normal file
699
ccan/ccan/cdump/cdump.c
Normal file
@@ -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
Normal file
106
ccan/ccan/cdump/cdump.h
Normal file
@@ -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
Normal file
176
ccan/ccan/cdump/test/run-CDUMP.c
Normal file
@@ -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
Normal file
54
ccan/ccan/cdump/test/run-arraysize.c
Normal file
@@ -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
Normal file
68
ccan/ccan/cdump/test/run-attributes.c
Normal file
@@ -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
Normal file
34
ccan/ccan/cdump/test/run-enum-comma.c
Normal file
@@ -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
Normal file
43
ccan/ccan/cdump/test/run-forward-decl.c
Normal file
@@ -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
Normal file
75
ccan/ccan/cdump/test/run-multiline.c
Normal file
@@ -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
Normal file
130
ccan/ccan/cdump/test/run-qualifiers.c
Normal file
@@ -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
Normal file
152
ccan/ccan/cdump/test/run.c
Normal file
@@ -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
Normal file
27
ccan/ccan/cdump/tools/Makefile
Normal file
@@ -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
Normal file
52
ccan/ccan/cdump/tools/cdump-enumstr.c
Normal file
@@ -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
Normal file
63
ccan/ccan/strmap/_info
Normal file
@@ -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
Normal file
247
ccan/ccan/strmap/strmap.c
Normal file
@@ -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
Normal file
226
ccan/ccan/strmap/strmap.h
Normal file
@@ -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
Normal file
32
ccan/ccan/strmap/test/run-iterate-const.c
Normal file
@@ -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
Normal file
96
ccan/ccan/strmap/test/run-order.c
Normal file
@@ -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
Normal file
97
ccan/ccan/strmap/test/run-prefix.c
Normal file
@@ -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
Normal file
80
ccan/ccan/strmap/test/run.c
Normal file
@@ -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
Symbolic link
1
ccan/ccan/tcon/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../../licenses/CC0
|
||||
75
ccan/ccan/tcon/_info
Normal file
75
ccan/ccan/tcon/_info
Normal file
@@ -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
Normal file
115
ccan/ccan/tcon/tcon.h
Normal file
@@ -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
Normal file
29
ccan/ccan/tcon/test/compile_fail-tcon_cast.c
Normal file
@@ -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
Normal file
25
ccan/ccan/tcon/test/compile_fail.c
Normal file
@@ -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
Normal file
21
ccan/ccan/tcon/test/compile_ok-void.c
Normal file
@@ -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
Normal file
27
ccan/ccan/tcon/test/compile_ok.c
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user