1 #include <ccan/cdump/cdump_parse.h>
2 #include <ccan/talloc/talloc.h>
3 #include <ccan/str/str.h>
8 static void add_token(char ***toks, const char *tok, unsigned toklen)
10 size_t len = talloc_array_length(*toks);
12 *toks = talloc_realloc(NULL, *toks, char *, len+1);
13 (*toks)[len] = talloc_strndup(*toks, tok, toklen);
16 /* Simplified tokenizer: comments and preproc directives removed,
17 identifiers are a token, others are single char tokens. */
18 static char **tokenize(const void *ctx, const char *code)
20 unsigned int i, len, tok_start = -1;
21 bool start_of_line = true;
22 char **ret = talloc_array(ctx, char *, 0);
24 for (i = 0; code[i]; i += len) {
25 if (code[i] == '#' && start_of_line) {
26 /* Preprocessor line. */
27 len = strcspn(code+i, "\n");
28 } else if (code[i] == '/' && code[i+1] == '/') {
29 /* One line comment. */
30 len = strcspn(code+i, "\n");
31 if (tok_start != -1U) {
32 add_token(&ret, code+tok_start, i - tok_start);
35 } else if (code[i] == '/' && code[i+1] == '*') {
36 /* Multi-line comment. */
37 const char *end = strstr(code+i+2, "*/");
38 len = (end + 2) - (code + i);
40 len = strlen(code + i);
41 if (tok_start != -1U) {
42 add_token(&ret, code+tok_start, i - tok_start);
45 } else if (isalnum(code[i]) || code[i] == '_') {
46 /* Identifier or part thereof */
50 } else if (!isspace(code[i])) {
51 /* Punctuation: treat as single char token. */
52 if (tok_start != -1U) {
53 add_token(&ret, code+tok_start, i - tok_start);
56 add_token(&ret, code+i, 1);
60 if (tok_start != -1U) {
61 add_token(&ret, code+tok_start, i - tok_start);
68 else if (!isspace(code[i]))
69 start_of_line = false;
72 /* Add terminating NULL. */
73 ret = talloc_realloc(NULL, ret, char *, talloc_array_length(ret)+1);
74 ret[talloc_array_length(ret)-1] = NULL;
79 static size_t handle_general(const void *ctx, const char *outer_struct_name,
80 char **definitions, unsigned int ptr_count,
81 const char *size, char **tok, const char *flags,
82 const char *dynlen, const char *bundle,
86 char *array_len = NULL;
88 /* handle arrays, treat multidimensional arrays as 1 dimensional */
89 while (streq(tok[off], "[")) {
91 array_len = talloc_strdup(ctx, "(");
93 array_len = talloc_asprintf_append(array_len, " * (");
95 while (!streq(tok[off], "]")) {
96 array_len = talloc_asprintf_append(array_len,
100 array_len[strlen(array_len)-1] = ')';
104 *definitions = talloc_asprintf_append(*definitions,
105 "\t{ \"%s\", %u, %s, offsetof(struct %s, %s), %s, %s, %s, %s, %s },\n",
106 tok[0], ptr_count, size, outer_struct_name, tok[0],
107 array_len ? array_len : "0", dynlen ? dynlen : "NULL", flags,
113 static size_t parse_one(const void *ctx, const char *outer_struct_name,
114 char **definitions, char **type, unsigned typelen,
115 char **tok, const char *dynlen, const char *flags)
117 unsigned int i, ptr_count = 0;
119 char *bundle, *unbundle, *size;
121 while (streq(tok[off], "*")) {
126 bundle = talloc_strdup(ctx, "cdump_bundle");
127 unbundle = talloc_strdup(ctx, "cdump_unbundle");
128 size = talloc_strdup(ctx, "sizeof(");
129 for (i = 0; i < typelen; i++) {
130 /* Const is irrelevant for bundling. */
131 if (streq(type[i], "const"))
133 bundle = talloc_asprintf_append(bundle, "_%s", type[i]);
134 unbundle = talloc_asprintf_append(unbundle, "_%s", type[i]);
135 size = talloc_asprintf_append(size, "%s ", type[i]);
137 size[strlen(size)-1] = ')';
139 off += handle_general(ctx, outer_struct_name, definitions, ptr_count,
140 size, tok + off, flags, dynlen, bundle, unbundle);
144 static size_t parse_element(const void *ctx, const char *outer_struct_name,
145 char **definitions, char **tok)
147 const char *dynlen = NULL, *flags = "0";
149 unsigned int typelen;
152 if (streq(tok[0], "enum"))
153 flags = talloc_strdup(ctx, "CDUMP_FLAG_ALWAYS");
156 for (i = typelen = 0; tok[i]; i++) {
157 /* These mean we've passed the variable name */
158 if (streq(tok[i], "[")
159 || streq(tok[i], ",")) {
164 /* End of expression means we've passed variable name, too */
165 if (streq(tok[i], ";")) {
171 /* This marks the end of the type: parse_one swallows *s. */
172 if (streq(tok[i], "*")) {
177 if (streq(tok[i], "CDUMP_LEN")) {
178 dynlen = talloc_asprintf(ctx, "\"%s\"", tok[i+2]);
183 if (streq(tok[i], "CDUMP_IGNORE"))
188 /* They could be comma-separated, so process them all. */
190 i += parse_one(ctx, outer_struct_name, definitions,
191 type, typelen, tok+i, dynlen, flags);
192 if (tok[i] && streq(tok[i], ","))
194 } while (tok[i] && !streq(tok[i], ";") && !strstarts(tok[i], "CDUMP_"));
197 while (tok[i] && !streq(tok[i], ";"))
203 static unsigned parse_struct(const void *ctx,
204 const char *name, char **tok,
205 char **declarations, char **definitions)
207 unsigned int i = 1, len;
209 *declarations = talloc_asprintf_append(*declarations,
210 "bool cdump_bundle_struct_%s(struct cdump_string *, const void *, unsigned);\n"
211 "bool cdump_unbundle_struct_%s(const void *, void *, const char *);\n"
212 "extern const struct cdump_desc cdump_struct_%s[];\n",
215 *definitions = talloc_asprintf_append(*definitions,
216 "const struct cdump_desc cdump_struct_%s[] = {\n",
218 while (!streq(tok[i], "}")) {
219 len = parse_element(ctx, name, definitions, tok + i);
226 *definitions = talloc_asprintf_append(*definitions,
227 "\t{ NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL } };\n"
228 "bool cdump_bundle_struct_%s(struct cdump_string *p, const void *ptr, unsigned indent)\n"
230 " return cdump_bundle_struct(cdump_struct_%s, p, ptr, indent);\n"
232 "bool cdump_unbundle_struct_%s(const void *ctx, void *ptr, const char *str)\n"
234 " return cdump_unbundle_struct(ctx, cdump_struct_%s, ptr, str);\n"
237 name, name, name, name);
241 static unsigned parse_enum(const void *ctx,
242 const char *name, char **tok,
243 char **declarations, char **definitions)
247 *declarations = talloc_asprintf_append(*declarations,
248 "bool cdump_bundle_enum_%s(struct cdump_string *, const void *, unsigned);\n"
249 "bool cdump_unbundle_enum_%s(const void *, void *, const char *);\n"
250 "extern const struct cdump_enum cdump_enum_%s[];\n",
253 *definitions = talloc_asprintf_append(*definitions,
254 "const struct cdump_enum cdump_enum_%s[] = {\n",
256 while (!streq(tok[i], "}")) {
257 *definitions = talloc_asprintf_append(*definitions,
258 "\t{ \"%s\", %s },\n",
260 while (!streq(tok[i], ",")) {
261 if (streq(tok[i], "}")) {
270 *definitions = talloc_asprintf_append(*definitions,
272 "bool cdump_bundle_enum_%s(struct cdump_string *p, const void *ptr, unsigned indent)\n"
274 " return cdump_bundle_enum(cdump_enum_%s, p, ptr, indent);\n"
276 "bool cdump_unbundle_enum_%s(const void *ctx, void *ptr, const char *str)\n"
278 " return cdump_unbundle_enum(cdump_enum_%s, ptr, str);\n"
281 name, name, name, name);
285 /* World's hackiest parser, inspired by Tridge's genstruct.pl. */
286 char *cdump_parse(const void *ctx, const char *code,
287 char **declarations, char **definitions)
289 char **tokens = tokenize(ctx, code);
292 *declarations = talloc_strdup(ctx, "");
293 *definitions = talloc_strdup(ctx, "");
295 for (i = 0; i < talloc_array_length(tokens)-1; i++) {
296 if (!streq(tokens[i], "CDUMP_SAVED"))
298 if (i + 3 >= talloc_array_length(tokens)-1)
299 return talloc_strdup(ctx, "EOF after CDUMP_SAVED");
301 if (streq(tokens[i+1], "struct")) {
302 len = parse_struct(ctx, tokens[i+2], tokens + i + 3,
303 declarations, definitions);
304 } else if (streq(tokens[i+1], "enum")) {
305 len = parse_enum(ctx, tokens[i+2], tokens + i + 3,
306 declarations, definitions);
308 return talloc_asprintf(ctx, "Unknown saved type"
309 " '%s'", tokens[i+1]);
311 return talloc_asprintf(ctx, "Invalid %s '%s'",
312 tokens[i+1], tokens[i+2]);