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 bundle = talloc_asprintf_append(bundle, "_%s", type[i]);
131 unbundle = talloc_asprintf_append(unbundle, "_%s", type[i]);
132 size = talloc_asprintf_append(size, "%s ", type[i]);
134 size[strlen(size)-1] = ')';
136 off += handle_general(ctx, outer_struct_name, definitions, ptr_count,
137 size, tok + off, flags, dynlen, bundle, unbundle);
141 static size_t parse_element(const void *ctx, const char *outer_struct_name,
142 char **definitions, char **tok)
144 const char *dynlen = NULL, *flags = "0";
146 unsigned int typelen;
149 if (streq(tok[0], "enum"))
150 flags = talloc_strdup(ctx, "CDUMP_FLAG_ALWAYS");
153 for (i = typelen = 0; tok[i]; i++) {
154 /* These mean we've passed the variable name */
155 if (streq(tok[i], "[")
156 || streq(tok[i], ",")) {
161 /* End of expression means we've passed variable name, too */
162 if (streq(tok[i], ";")) {
168 /* This marks the end of the type: parse_one swallows *s. */
169 if (streq(tok[i], "*")) {
174 if (streq(tok[i], "CDUMP_LEN")) {
175 dynlen = talloc_asprintf(ctx, "\"%s\"", tok[i+2]);
183 /* They could be comma-separated, so process them all. */
185 i += parse_one(ctx, outer_struct_name, definitions,
186 type, typelen, tok+i, dynlen, flags);
187 if (tok[i] && streq(tok[i], ","))
189 } while (tok[i] && !streq(tok[i], ";") && !strstarts(tok[i], "CDUMP_"));
191 while (tok[i] && !streq(tok[i], ";"))
197 static unsigned parse_struct(const void *ctx,
198 const char *name, char **tok,
199 char **declarations, char **definitions)
201 unsigned int i = 1, len;
203 *declarations = talloc_asprintf_append(*declarations,
204 "bool cdump_bundle_struct_%s(struct cdump_string *, const void *, unsigned);\n"
205 "bool cdump_unbundle_struct_%s(const void *, void *, const char *);\n"
206 "extern const struct cdump_desc cdump_struct_%s[];\n",
209 *definitions = talloc_asprintf_append(*definitions,
210 "const struct cdump_desc cdump_struct_%s[] = {\n",
212 while (!streq(tok[i], "}")) {
213 len = parse_element(ctx, name, definitions, tok + i);
220 *definitions = talloc_asprintf_append(*definitions,
221 "\t{ NULL, 0, 0, 0, 0, NULL, 0, NULL, NULL } };\n"
222 "bool cdump_bundle_struct_%s(struct cdump_string *p, const void *ptr, unsigned indent)\n"
224 " return cdump_bundle_struct(cdump_struct_%s, p, ptr, indent);\n"
226 "bool cdump_unbundle_struct_%s(const void *ctx, void *ptr, const char *str)\n"
228 " return cdump_unbundle_struct(ctx, cdump_struct_%s, ptr, str);\n"
231 name, name, name, name);
235 static unsigned parse_enum(const void *ctx,
236 const char *name, char **tok,
237 char **declarations, char **definitions)
241 *declarations = talloc_asprintf_append(*declarations,
242 "bool cdump_bundle_enum_%s(struct cdump_string *, const void *, unsigned);\n"
243 "bool cdump_unbundle_enum_%s(const void *, void *, const char *);\n"
244 "extern const struct cdump_enum cdump_enum_%s[];\n",
247 *definitions = talloc_asprintf_append(*definitions,
248 "const struct cdump_enum cdump_enum_%s[] = {\n",
250 while (!streq(tok[i], "}")) {
251 *definitions = talloc_asprintf_append(*definitions,
252 "\t{ \"%s\", %s },\n",
254 while (!streq(tok[i], ",")) {
255 if (streq(tok[i], "}")) {
264 *definitions = talloc_asprintf_append(*definitions,
266 "bool cdump_bundle_enum_%s(struct cdump_string *p, const void *ptr, unsigned indent)\n"
268 " return cdump_bundle_enum(cdump_enum_%s, p, ptr, indent);\n"
270 "bool cdump_unbundle_enum_%s(const void *ctx, void *ptr, const char *str)\n"
272 " return cdump_unbundle_enum(cdump_enum_%s, ptr, str);\n"
275 name, name, name, name);
279 /* World's hackiest parser, inspired by Tridge's genstruct.pl. */
280 char *cdump_parse(const void *ctx, const char *code,
281 char **declarations, char **definitions)
283 char **tokens = tokenize(ctx, code);
286 *declarations = talloc_strdup(ctx, "");
287 *definitions = talloc_strdup(ctx, "");
289 for (i = 0; i < talloc_array_length(tokens)-1; i++) {
290 if (!streq(tokens[i], "CDUMP_SAVED"))
292 if (i + 3 >= talloc_array_length(tokens)-1)
293 return talloc_strdup(ctx, "EOF after CDUMP_SAVED");
295 if (streq(tokens[i+1], "struct")) {
296 len = parse_struct(ctx, tokens[i+2], tokens + i + 3,
297 declarations, definitions);
298 } else if (streq(tokens[i+1], "enum")) {
299 len = parse_enum(ctx, tokens[i+2], tokens + i + 3,
300 declarations, definitions);
302 return talloc_asprintf(ctx, "Unknown saved type"
303 " '%s'", tokens[i+1]);
305 return talloc_asprintf(ctx, "Invalid %s '%s'",
306 tokens[i+1], tokens[i+2]);