diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2012-05-23 11:03:35 +0200 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2012-05-31 13:57:25 +0200 |
commit | a87a9ef37372b4380808ec2efa7c762e137668f1 (patch) | |
tree | 9c759088f0f9cf6717d96789b6805812f6b187ea /utils/imxtools/sbtools/dbparser.c | |
parent | ba8e4367fb4d116ffc01c12cc619bfc714e582c9 (diff) | |
download | rockbox-a87a9ef37372b4380808ec2efa7c762e137668f1.tar.gz rockbox-a87a9ef37372b4380808ec2efa7c762e137668f1.zip |
imxtools: move tools to a new sbtools/ subdirectory
Change-Id: I0d8d6831b35037725486f61fc363de87bc8ba92e
Diffstat (limited to 'utils/imxtools/sbtools/dbparser.c')
-rw-r--r-- | utils/imxtools/sbtools/dbparser.c | 857 |
1 files changed, 857 insertions, 0 deletions
diff --git a/utils/imxtools/sbtools/dbparser.c b/utils/imxtools/sbtools/dbparser.c new file mode 100644 index 0000000000..89a63b3767 --- /dev/null +++ b/utils/imxtools/sbtools/dbparser.c | |||
@@ -0,0 +1,857 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2011 Amaury Pouly | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #define _POSIX_C_SOURCE 200809L /* for strdup */ | ||
23 | #include <stdio.h> | ||
24 | #include <ctype.h> | ||
25 | #include <stdint.h> | ||
26 | #include <string.h> | ||
27 | #include "dbparser.h" | ||
28 | #include "misc.h" | ||
29 | |||
30 | enum lexem_type_t | ||
31 | { | ||
32 | LEX_IDENTIFIER, | ||
33 | LEX_LPAREN, | ||
34 | LEX_RPAREN, | ||
35 | LEX_NUMBER, | ||
36 | LEX_STRING, /* double-quoted string */ | ||
37 | LEX_EQUAL, | ||
38 | LEX_SEMICOLON, | ||
39 | LEX_LBRACE, | ||
40 | LEX_RBRACE, | ||
41 | LEX_RANGLE, | ||
42 | LEX_OR, | ||
43 | LEX_LSHIFT, | ||
44 | LEX_COLON, | ||
45 | LEX_LE, | ||
46 | LEX_EOF | ||
47 | }; | ||
48 | |||
49 | struct lexem_t | ||
50 | { | ||
51 | enum lexem_type_t type; | ||
52 | /* if str is not NULL, it must be a malloc'd pointer */ | ||
53 | char *str; | ||
54 | uint32_t num; | ||
55 | int line; | ||
56 | const char *file; | ||
57 | }; | ||
58 | |||
59 | struct context_t | ||
60 | { | ||
61 | const char *file; | ||
62 | char *begin; | ||
63 | char *end; | ||
64 | char *ptr; | ||
65 | int line; | ||
66 | }; | ||
67 | |||
68 | #define parse_error(ctx, ...) \ | ||
69 | do { fprintf(stderr, "%s:%d: ", ctx->file, ctx->line); \ | ||
70 | fprintf(stderr, __VA_ARGS__); exit(2); } while(0) | ||
71 | |||
72 | static void advance(struct context_t *ctx, int nr_chars) | ||
73 | { | ||
74 | while(nr_chars--) | ||
75 | { | ||
76 | if(*(ctx->ptr++) == '\n') | ||
77 | ctx->line++; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | static inline bool eof(struct context_t *ctx) | ||
82 | { | ||
83 | return ctx->ptr == ctx->end; | ||
84 | } | ||
85 | |||
86 | static inline bool next_valid(struct context_t *ctx, int nr) | ||
87 | { | ||
88 | return ctx->ptr + nr < ctx->end; | ||
89 | } | ||
90 | |||
91 | static inline char cur_char(struct context_t *ctx) | ||
92 | { | ||
93 | return *ctx->ptr; | ||
94 | } | ||
95 | |||
96 | static inline char next_char(struct context_t *ctx, int nr) | ||
97 | { | ||
98 | return ctx->ptr[nr]; | ||
99 | } | ||
100 | |||
101 | static inline void locate_lexem(struct lexem_t *lex, struct context_t *ctx) | ||
102 | { | ||
103 | lex->file = ctx->file; | ||
104 | lex->line = ctx->line; | ||
105 | } | ||
106 | |||
107 | static void __parse_string(struct context_t *ctx, void *user, void (*emit_fn)(void *user, char c)) | ||
108 | { | ||
109 | while(!eof(ctx)) | ||
110 | { | ||
111 | if(cur_char(ctx) == '"') | ||
112 | break; | ||
113 | else if(cur_char(ctx) == '\\') | ||
114 | { | ||
115 | advance(ctx, 1); | ||
116 | if(eof(ctx)) | ||
117 | parse_error(ctx, "Unfinished string\n"); | ||
118 | if(cur_char(ctx) == '\\') emit_fn(user, '\\'); | ||
119 | else if(cur_char(ctx) == '\'') emit_fn(user, '\''); | ||
120 | else if(cur_char(ctx) == '\"') emit_fn(user, '\"'); | ||
121 | else parse_error(ctx, "Unknown escape sequence \\%c\n", cur_char(ctx)); | ||
122 | advance(ctx, 1); | ||
123 | } | ||
124 | else | ||
125 | { | ||
126 | emit_fn(user, cur_char(ctx)); | ||
127 | advance(ctx, 1); | ||
128 | } | ||
129 | } | ||
130 | if(eof(ctx) || cur_char(ctx) != '"') | ||
131 | parse_error(ctx, "Unfinished string\n"); | ||
132 | advance(ctx, 1); | ||
133 | } | ||
134 | |||
135 | static void __parse_string_emit(void *user, char c) | ||
136 | { | ||
137 | char **pstr = (char **)user; | ||
138 | *(*pstr)++ = c; | ||
139 | } | ||
140 | |||
141 | static void __parse_string_count(void *user, char c) | ||
142 | { | ||
143 | (void) c; | ||
144 | (*(int *)user)++; | ||
145 | } | ||
146 | |||
147 | static void parse_string(struct context_t *ctx, struct lexem_t *lexem) | ||
148 | { | ||
149 | locate_lexem(lexem, ctx); | ||
150 | /* skip " */ | ||
151 | advance(ctx, 1); | ||
152 | /* compute length */ | ||
153 | struct context_t cpy_ctx = *ctx; | ||
154 | int length = 0; | ||
155 | __parse_string(&cpy_ctx, (void *)&length, __parse_string_count); | ||
156 | /* parse again */ | ||
157 | lexem->type = LEX_STRING; | ||
158 | lexem->str = xmalloc(length + 1); | ||
159 | lexem->str[length] = 0; | ||
160 | char *pstr = lexem->str; | ||
161 | __parse_string(ctx, (void *)&pstr, __parse_string_emit); | ||
162 | } | ||
163 | |||
164 | static void parse_ascii_number(struct context_t *ctx, struct lexem_t *lexem) | ||
165 | { | ||
166 | locate_lexem(lexem, ctx); | ||
167 | /* skip ' */ | ||
168 | advance(ctx, 1); | ||
169 | /* we expect n<=4 character and then ' */ | ||
170 | int len = 0; | ||
171 | uint32_t value = 0; | ||
172 | while(!eof(ctx)) | ||
173 | { | ||
174 | if(cur_char(ctx) != '\'') | ||
175 | { | ||
176 | value = value << 8 | cur_char(ctx); | ||
177 | len++; | ||
178 | advance(ctx, 1); | ||
179 | } | ||
180 | else | ||
181 | break; | ||
182 | } | ||
183 | if(eof(ctx) || cur_char(ctx) != '\'') | ||
184 | parse_error(ctx, "Unterminated ascii number literal\n"); | ||
185 | if(len == 0 || len > 4) | ||
186 | parse_error(ctx, "Invalid ascii number literal length: only 1 to 4 characters allowed\n"); | ||
187 | /* skip ' */ | ||
188 | advance(ctx, 1); | ||
189 | lexem->type = LEX_NUMBER; | ||
190 | lexem->num = value; | ||
191 | } | ||
192 | |||
193 | static void parse_number(struct context_t *ctx, struct lexem_t *lexem) | ||
194 | { | ||
195 | locate_lexem(lexem, ctx); | ||
196 | /* check base */ | ||
197 | int base = 10; | ||
198 | if(cur_char(ctx) == '0' && next_valid(ctx, 1) && next_char(ctx, 1) == 'x') | ||
199 | { | ||
200 | advance(ctx, 2); | ||
201 | base = 16; | ||
202 | } | ||
203 | |||
204 | lexem->type = LEX_NUMBER; | ||
205 | lexem->num = 0; | ||
206 | while(!eof(ctx) && isxdigit(cur_char(ctx))) | ||
207 | { | ||
208 | if(base == 10 && !isdigit(cur_char(ctx))) | ||
209 | break; | ||
210 | byte v; | ||
211 | if(convxdigit(cur_char(ctx), &v)) | ||
212 | break; | ||
213 | lexem->num = base * lexem->num + v; | ||
214 | advance(ctx, 1); | ||
215 | } | ||
216 | } | ||
217 | |||
218 | static void parse_identifier(struct context_t *ctx, struct lexem_t *lexem) | ||
219 | { | ||
220 | locate_lexem(lexem, ctx); | ||
221 | /* remember position */ | ||
222 | char *old = ctx->ptr; | ||
223 | while(!eof(ctx) && (isalnum(cur_char(ctx)) || cur_char(ctx) == '_')) | ||
224 | advance(ctx, 1); | ||
225 | lexem->type = LEX_IDENTIFIER; | ||
226 | int len = ctx->ptr - old; | ||
227 | lexem->str = xmalloc(len + 1); | ||
228 | lexem->str[len] = 0; | ||
229 | memcpy(lexem->str, old, len); | ||
230 | } | ||
231 | |||
232 | static void next_lexem(struct context_t *ctx, struct lexem_t *lexem) | ||
233 | { | ||
234 | #define ret_simple(t, adv) \ | ||
235 | do {locate_lexem(lexem, ctx); \ | ||
236 | lexem->type = t; \ | ||
237 | advance(ctx, adv); \ | ||
238 | return;} while(0) | ||
239 | while(!eof(ctx)) | ||
240 | { | ||
241 | char c = cur_char(ctx); | ||
242 | /* skip whitespace */ | ||
243 | if(c == ' ' || c == '\t' || c == '\n' || c == '\r') | ||
244 | { | ||
245 | advance(ctx, 1); | ||
246 | continue; | ||
247 | } | ||
248 | /* skip C++ style comments */ | ||
249 | if(c == '/' && next_valid(ctx, 1) && next_char(ctx, 1) == '/') | ||
250 | { | ||
251 | while(!eof(ctx) && cur_char(ctx) != '\n') | ||
252 | advance(ctx, 1); | ||
253 | continue; | ||
254 | } | ||
255 | /* skip C-style comments */ | ||
256 | if(c == '/' && next_valid(ctx, 1) && next_char(ctx, 1) == '*') | ||
257 | { | ||
258 | advance(ctx, 2); | ||
259 | while(true) | ||
260 | { | ||
261 | if(!next_valid(ctx, 1)) | ||
262 | parse_error(ctx, "Unterminated comment"); | ||
263 | if(cur_char(ctx) == '*' && next_char(ctx, 1) == '/') | ||
264 | { | ||
265 | advance(ctx, 2); | ||
266 | break; | ||
267 | } | ||
268 | advance(ctx, 1); | ||
269 | } | ||
270 | continue; | ||
271 | } | ||
272 | break; | ||
273 | } | ||
274 | if(eof(ctx)) ret_simple(LEX_EOF, 0); | ||
275 | char c = cur_char(ctx); | ||
276 | bool nv = next_valid(ctx, 1); | ||
277 | char nc = nv ? next_char(ctx, 1) : 0; | ||
278 | if(c == '(') ret_simple(LEX_LPAREN, 1); | ||
279 | if(c == ')') ret_simple(LEX_RPAREN, 1); | ||
280 | if(c == '{') ret_simple(LEX_LBRACE, 1); | ||
281 | if(c == '}') ret_simple(LEX_RBRACE, 1); | ||
282 | if(c == '>') ret_simple(LEX_RANGLE, 1); | ||
283 | if(c == '=') ret_simple(LEX_EQUAL, 1); | ||
284 | if(c == ';') ret_simple(LEX_SEMICOLON, 1); | ||
285 | if(c == ',') ret_simple(LEX_COLON, 1); | ||
286 | if(c == '|') ret_simple(LEX_OR, 1); | ||
287 | if(c == '<' && nv && nc == '<') ret_simple(LEX_LSHIFT, 2); | ||
288 | if(c == '<' && nv && nc == '=') ret_simple(LEX_LE, 2); | ||
289 | if(c == '"') return parse_string(ctx, lexem); | ||
290 | if(c == '\'') return parse_ascii_number(ctx, lexem); | ||
291 | if(isdigit(c)) return parse_number(ctx, lexem); | ||
292 | if(isalpha(c) || c == '_') return parse_identifier(ctx, lexem); | ||
293 | parse_error(ctx, "Unexpected character '%c'\n", c); | ||
294 | #undef ret_simple | ||
295 | } | ||
296 | |||
297 | #if 0 | ||
298 | static void log_lexem(struct lexem_t *lexem) | ||
299 | { | ||
300 | switch(lexem->type) | ||
301 | { | ||
302 | case LEX_EOF: printf("<eof>"); break; | ||
303 | case LEX_EQUAL: printf("="); break; | ||
304 | case LEX_IDENTIFIER: printf("id(%s)", lexem->str); break; | ||
305 | case LEX_LPAREN: printf("("); break; | ||
306 | case LEX_RPAREN: printf(")"); break; | ||
307 | case LEX_LBRACE: printf("{"); break; | ||
308 | case LEX_RBRACE: printf("}"); break; | ||
309 | case LEX_SEMICOLON: printf(";"); break; | ||
310 | case LEX_NUMBER: printf("num(%d)", lexem->num); break; | ||
311 | case LEX_STRING: printf("str(%s)", lexem->str); break; | ||
312 | case LEX_OR: printf("|"); break; | ||
313 | case LEX_LSHIFT: printf("<<"); break; | ||
314 | default: printf("<unk>"); | ||
315 | } | ||
316 | } | ||
317 | #endif | ||
318 | |||
319 | struct cmd_source_t *db_find_source_by_id(struct cmd_file_t *cmd_file, const char *id) | ||
320 | { | ||
321 | struct cmd_source_t *src = cmd_file->source_list; | ||
322 | while(src) | ||
323 | { | ||
324 | if(strcmp(src->identifier, id) == 0) | ||
325 | return src; | ||
326 | src = src->next; | ||
327 | } | ||
328 | return NULL; | ||
329 | } | ||
330 | |||
331 | struct cmd_option_t *db_find_option_by_id(struct cmd_option_t *opt, const char *name) | ||
332 | { | ||
333 | while(opt) | ||
334 | { | ||
335 | if(strcmp(opt->name, name) == 0) | ||
336 | return opt; | ||
337 | opt = opt->next; | ||
338 | } | ||
339 | return NULL; | ||
340 | } | ||
341 | |||
342 | #define INVALID_SB_SUBVERSION 0xffff | ||
343 | |||
344 | static uint16_t parse_sb_subversion(char *str) | ||
345 | { | ||
346 | int len = strlen(str); | ||
347 | uint16_t n = 0; | ||
348 | if(len == 0 || len > 4) | ||
349 | return INVALID_SB_SUBVERSION; | ||
350 | for(int i = 0; i < len; i++) | ||
351 | { | ||
352 | if(!isdigit(str[i])) | ||
353 | return INVALID_SB_SUBVERSION; | ||
354 | n = n << 4 | (str[i] - '0'); | ||
355 | } | ||
356 | return n; | ||
357 | } | ||
358 | |||
359 | bool db_parse_sb_version(struct sb_version_t *ver, char *str) | ||
360 | { | ||
361 | int len = strlen(str); | ||
362 | int cnt = 0; | ||
363 | int pos[2]; | ||
364 | |||
365 | for(int i = 0; i < len; i++) | ||
366 | { | ||
367 | if(str[i] != '.') | ||
368 | continue; | ||
369 | if(cnt == 2) | ||
370 | return false; | ||
371 | pos[cnt++] = i + 1; | ||
372 | str[i] = 0; | ||
373 | } | ||
374 | if(cnt != 2) | ||
375 | return false; | ||
376 | ver->major = parse_sb_subversion(str); | ||
377 | ver->minor = parse_sb_subversion(str + pos[0]); | ||
378 | ver->revision = parse_sb_subversion(str + pos[1]); | ||
379 | return ver->major != INVALID_SB_SUBVERSION && | ||
380 | ver->minor != INVALID_SB_SUBVERSION && | ||
381 | ver->revision != INVALID_SB_SUBVERSION; | ||
382 | } | ||
383 | |||
384 | #undef parse_error | ||
385 | #define parse_error(lexem, ...) \ | ||
386 | do { fprintf(stderr, "%s:%d: ", lexem.file, lexem.line); \ | ||
387 | fprintf(stderr, __VA_ARGS__); exit(2); } while(0) | ||
388 | |||
389 | struct lex_ctx_t | ||
390 | { | ||
391 | struct context_t ctx; | ||
392 | struct lexem_t lexem; | ||
393 | }; | ||
394 | |||
395 | /* When lexems hold strings (like identifier), it might be useful to steal | ||
396 | * the pointer and don't clean the lexem but in other case, one don't want | ||
397 | * to keep the pointer to the string and just want to release the memory. | ||
398 | * Thus clean_lexem should be true except when one keeps a pointer */ | ||
399 | static inline void next(struct lex_ctx_t *ctx, bool clean_lexem) | ||
400 | { | ||
401 | if(clean_lexem) | ||
402 | free(ctx->lexem.str); | ||
403 | memset(&ctx->lexem, 0, sizeof(struct lexem_t)); | ||
404 | next_lexem(&ctx->ctx, &ctx->lexem); | ||
405 | } | ||
406 | |||
407 | static uint32_t parse_term_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) | ||
408 | { | ||
409 | uint32_t ret = 0; | ||
410 | if(ctx->lexem.type == LEX_NUMBER) | ||
411 | ret = ctx->lexem.num; | ||
412 | else if(ctx->lexem.type == LEX_IDENTIFIER) | ||
413 | { | ||
414 | struct cmd_option_t *c = db_find_option_by_id(const_list, ctx->lexem.str); | ||
415 | if(c == NULL) | ||
416 | parse_error(ctx->lexem, "Undefined reference to constant '%s'\n", ctx->lexem.str); | ||
417 | if(c->is_string) | ||
418 | parse_error(ctx->lexem, "Internal error: constant '%s' is not an integer\n", ctx->lexem.str); | ||
419 | ret = c->val; | ||
420 | } | ||
421 | else | ||
422 | parse_error(ctx->lexem, "Number or constant identifier expected\n"); | ||
423 | next(ctx, true); | ||
424 | return ret; | ||
425 | } | ||
426 | |||
427 | static uint32_t parse_shift_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) | ||
428 | { | ||
429 | uint32_t v = parse_term_expr(ctx, const_list); | ||
430 | while(ctx->lexem.type == LEX_LSHIFT) | ||
431 | { | ||
432 | next(ctx, true); | ||
433 | v <<= parse_term_expr(ctx, const_list); | ||
434 | } | ||
435 | return v; | ||
436 | } | ||
437 | |||
438 | static uint32_t parse_or_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) | ||
439 | { | ||
440 | uint32_t v = parse_shift_expr(ctx, const_list); | ||
441 | while(ctx->lexem.type == LEX_OR) | ||
442 | { | ||
443 | next(ctx, true); | ||
444 | v |= parse_shift_expr(ctx, const_list); | ||
445 | } | ||
446 | return v; | ||
447 | } | ||
448 | |||
449 | static uint32_t parse_intexpr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list) | ||
450 | { | ||
451 | return parse_or_expr(ctx, const_list); | ||
452 | } | ||
453 | |||
454 | #define NR_INITIAL_CONSTANTS 4 | ||
455 | static char *init_const_name[NR_INITIAL_CONSTANTS] = {"true", "false", "yes", "no"}; | ||
456 | static uint32_t init_const_value[NR_INITIAL_CONSTANTS] = {1, 0, 1, 0}; | ||
457 | |||
458 | struct cmd_file_t *db_parse_file(const char *file) | ||
459 | { | ||
460 | size_t size; | ||
461 | FILE *f = fopen(file, "r"); | ||
462 | if(f == NULL) | ||
463 | { | ||
464 | if(g_debug) | ||
465 | perror("Cannot open db file"); | ||
466 | return NULL; | ||
467 | } | ||
468 | fseek(f, 0, SEEK_END); | ||
469 | size = ftell(f); | ||
470 | fseek(f, 0, SEEK_SET); | ||
471 | char *buf = xmalloc(size); | ||
472 | if(fread(buf, size, 1, f) != 1) | ||
473 | { | ||
474 | if(g_debug) | ||
475 | perror("Cannot read db file"); | ||
476 | return NULL; | ||
477 | } | ||
478 | fclose(f); | ||
479 | |||
480 | if(g_debug) | ||
481 | printf("Parsing db file '%s'\n", file); | ||
482 | struct cmd_file_t *cmd_file = xmalloc(sizeof(struct cmd_file_t)); | ||
483 | memset(cmd_file, 0, sizeof(struct cmd_file_t)); | ||
484 | |||
485 | /* add initial constants */ | ||
486 | for(int i = 0; i < NR_INITIAL_CONSTANTS; i++) | ||
487 | { | ||
488 | struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t)); | ||
489 | memset(opt, 0, sizeof(struct cmd_option_t)); | ||
490 | opt->name = strdup(init_const_name[i]); | ||
491 | opt->is_string = false; | ||
492 | opt->val = init_const_value[i]; | ||
493 | opt->next = cmd_file->constant_list; | ||
494 | cmd_file->constant_list = opt; | ||
495 | } | ||
496 | |||
497 | struct lex_ctx_t lctx; | ||
498 | lctx.ctx.file = file; | ||
499 | lctx.ctx.line = 1; | ||
500 | lctx.ctx.begin = buf; | ||
501 | lctx.ctx.ptr = buf; | ||
502 | lctx.ctx.end = buf + size; | ||
503 | #define next(clean_lexem) next(&lctx, clean_lexem) | ||
504 | #define lexem lctx.lexem | ||
505 | /* init lexer */ | ||
506 | next(false); /* don't clean init lexem because it doesn't exist */ | ||
507 | /* constants ? */ | ||
508 | if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "constants")) | ||
509 | { | ||
510 | next(true); | ||
511 | if(lexem.type != LEX_LBRACE) | ||
512 | parse_error(lexem, "'{' expected after 'constants'\n"); | ||
513 | |||
514 | while(true) | ||
515 | { | ||
516 | struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t)); | ||
517 | memset(opt, 0, sizeof(struct cmd_option_t)); | ||
518 | next(true); | ||
519 | if(lexem.type == LEX_RBRACE) | ||
520 | break; | ||
521 | if(lexem.type != LEX_IDENTIFIER) | ||
522 | parse_error(lexem, "Identifier expected in constants\n"); | ||
523 | opt->name = lexem.str; | ||
524 | next(false); /* lexem string is kept as option name */ | ||
525 | if(lexem.type != LEX_EQUAL) | ||
526 | parse_error(lexem, "'=' expected after identifier\n"); | ||
527 | next(true); | ||
528 | opt->is_string = false; | ||
529 | opt->val = parse_intexpr(&lctx, cmd_file->constant_list); | ||
530 | opt->next = cmd_file->constant_list; | ||
531 | cmd_file->constant_list = opt; | ||
532 | if(lexem.type != LEX_SEMICOLON) | ||
533 | parse_error(lexem, "';' expected after string\n"); | ||
534 | } | ||
535 | next(true); | ||
536 | } | ||
537 | /* options ? */ | ||
538 | if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "options")) | ||
539 | { | ||
540 | next(true); | ||
541 | if(lexem.type != LEX_LBRACE) | ||
542 | parse_error(lexem, "'{' expected after 'options'\n"); | ||
543 | |||
544 | while(true) | ||
545 | { | ||
546 | next(true); | ||
547 | if(lexem.type == LEX_RBRACE) | ||
548 | break; | ||
549 | struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t)); | ||
550 | memset(opt, 0, sizeof(struct cmd_option_t)); | ||
551 | if(lexem.type != LEX_IDENTIFIER) | ||
552 | parse_error(lexem, "Identifier expected in options\n"); | ||
553 | opt->name = lexem.str; | ||
554 | next(false); /* lexem string is kept as option name */ | ||
555 | if(lexem.type != LEX_EQUAL) | ||
556 | parse_error(lexem, "'=' expected after identifier\n"); | ||
557 | next(true); | ||
558 | if(lexem.type == LEX_STRING) | ||
559 | { | ||
560 | opt->is_string = true; | ||
561 | opt->str = lexem.str; | ||
562 | next(false); /* lexem string is kept as option name */ | ||
563 | } | ||
564 | else | ||
565 | { | ||
566 | opt->is_string = false; | ||
567 | opt->val = parse_intexpr(&lctx, cmd_file->constant_list); | ||
568 | } | ||
569 | opt->next = cmd_file->opt_list; | ||
570 | cmd_file->opt_list = opt; | ||
571 | if(lexem.type != LEX_SEMICOLON) | ||
572 | parse_error(lexem, "';' expected after string\n"); | ||
573 | } | ||
574 | next(true); | ||
575 | } | ||
576 | /* sources */ | ||
577 | if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "sources")) | ||
578 | parse_error(lexem, "'sources' expected\n"); | ||
579 | next(true); | ||
580 | if(lexem.type != LEX_LBRACE) | ||
581 | parse_error(lexem, "'{' expected after 'sources'\n"); | ||
582 | |||
583 | while(true) | ||
584 | { | ||
585 | next(true); | ||
586 | if(lexem.type == LEX_RBRACE) | ||
587 | break; | ||
588 | struct cmd_source_t *src = xmalloc(sizeof(struct cmd_source_t)); | ||
589 | memset(src, 0, sizeof(struct cmd_source_t)); | ||
590 | if(lexem.type != LEX_IDENTIFIER) | ||
591 | parse_error(lexem, "identifier expected in sources\n"); | ||
592 | src->identifier = lexem.str; | ||
593 | next(false); /* lexem string is kept as source name */ | ||
594 | if(lexem.type != LEX_EQUAL) | ||
595 | parse_error(lexem, "'=' expected after identifier\n"); | ||
596 | next(true); | ||
597 | if(lexem.type == LEX_STRING) | ||
598 | { | ||
599 | src->is_extern = false; | ||
600 | src->filename = lexem.str; | ||
601 | next(false); /* lexem string is kept as file name */ | ||
602 | } | ||
603 | else if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "extern")) | ||
604 | { | ||
605 | src->is_extern = true; | ||
606 | src->filename = strdup("<extern>"); /* duplicate because it will be free'd */ | ||
607 | next(true); | ||
608 | if(lexem.type != LEX_LPAREN) | ||
609 | parse_error(lexem, "'(' expected after 'extern'\n"); | ||
610 | next(true); | ||
611 | src->extern_nr = parse_intexpr(&lctx, cmd_file->constant_list); | ||
612 | if(lexem.type != LEX_RPAREN) | ||
613 | parse_error(lexem, "')' expected\n"); | ||
614 | next(true); | ||
615 | } | ||
616 | else | ||
617 | parse_error(lexem, "String or 'extern' expected after '='\n"); | ||
618 | if(lexem.type != LEX_SEMICOLON) | ||
619 | parse_error(lexem, "';' expected\n"); | ||
620 | if(db_find_source_by_id(cmd_file, src->identifier) != NULL) | ||
621 | parse_error(lexem, "Duplicate source identifier\n"); | ||
622 | /* type filled later */ | ||
623 | src->type = CMD_SRC_UNK; | ||
624 | src->next = cmd_file->source_list; | ||
625 | cmd_file->source_list = src; | ||
626 | } | ||
627 | |||
628 | /* sections */ | ||
629 | struct cmd_section_t *end_sec = NULL; | ||
630 | while(true) | ||
631 | { | ||
632 | next(true); | ||
633 | if(lexem.type == LEX_EOF) | ||
634 | break; | ||
635 | struct cmd_section_t *sec = xmalloc(sizeof(struct cmd_section_t)); | ||
636 | struct cmd_inst_t *end_list = NULL; | ||
637 | memset(sec, 0, sizeof(struct cmd_section_t)); | ||
638 | if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "section") != 0) | ||
639 | parse_error(lexem, "'section' expected\n"); | ||
640 | next(true); | ||
641 | if(lexem.type != LEX_LPAREN) | ||
642 | parse_error(lexem, "'(' expected after 'section'\n"); | ||
643 | next(true); | ||
644 | /* can be any number */ | ||
645 | sec->identifier = parse_intexpr(&lctx, cmd_file->constant_list); | ||
646 | /* options ? */ | ||
647 | if(lexem.type == LEX_SEMICOLON) | ||
648 | { | ||
649 | do | ||
650 | { | ||
651 | next(true); | ||
652 | struct cmd_option_t *opt = xmalloc(sizeof(struct cmd_option_t)); | ||
653 | memset(opt, 0, sizeof(struct cmd_option_t)); | ||
654 | if(lexem.type != LEX_IDENTIFIER) | ||
655 | parse_error(lexem, "Identifier expected for section option\n"); | ||
656 | opt->name = lexem.str; | ||
657 | next(false); /* lexem string is kept as option name */ | ||
658 | if(lexem.type != LEX_EQUAL) | ||
659 | parse_error(lexem, "'=' expected after option identifier\n"); | ||
660 | next(true); | ||
661 | if(lexem.type == LEX_STRING) | ||
662 | { | ||
663 | opt->is_string = true; | ||
664 | opt->str = lexem.str; | ||
665 | next(false); /* lexem string is kept as option string */ | ||
666 | } | ||
667 | else | ||
668 | { | ||
669 | opt->is_string = false; | ||
670 | opt->val = parse_intexpr(&lctx, cmd_file->constant_list); | ||
671 | } | ||
672 | opt->next = sec->opt_list; | ||
673 | sec->opt_list = opt; | ||
674 | }while(lexem.type == LEX_COLON); | ||
675 | } | ||
676 | if(lexem.type != LEX_RPAREN) | ||
677 | parse_error(lexem, "')' expected after section identifier\n"); | ||
678 | next(true); | ||
679 | if(lexem.type == LEX_LBRACE) | ||
680 | { | ||
681 | sec->is_data = false; | ||
682 | /* commands */ | ||
683 | while(true) | ||
684 | { | ||
685 | next(true); | ||
686 | if(lexem.type == LEX_RBRACE) | ||
687 | break; | ||
688 | struct cmd_inst_t *inst = xmalloc(sizeof(struct cmd_inst_t)); | ||
689 | memset(inst, 0, sizeof(struct cmd_inst_t)); | ||
690 | if(lexem.type != LEX_IDENTIFIER) | ||
691 | parse_error(lexem, "Instruction expected in section\n"); | ||
692 | if(strcmp(lexem.str, "load") == 0) | ||
693 | inst->type = CMD_LOAD; | ||
694 | else if(strcmp(lexem.str, "call") == 0) | ||
695 | inst->type = CMD_CALL; | ||
696 | else if(strcmp(lexem.str, "jump") == 0) | ||
697 | inst->type = CMD_JUMP; | ||
698 | else if(strcmp(lexem.str, "mode") == 0) | ||
699 | inst->type = CMD_MODE; | ||
700 | else | ||
701 | parse_error(lexem, "Instruction expected in section\n"); | ||
702 | next(true); | ||
703 | |||
704 | if(inst->type == CMD_LOAD) | ||
705 | { | ||
706 | if(lexem.type != LEX_IDENTIFIER) | ||
707 | parse_error(lexem, "Identifier expected after instruction\n"); | ||
708 | inst->identifier = lexem.str; | ||
709 | if(db_find_source_by_id(cmd_file, inst->identifier) == NULL) | ||
710 | parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier); | ||
711 | next(false); /* lexem string kept as identifier */ | ||
712 | if(lexem.type == LEX_RANGLE) | ||
713 | { | ||
714 | // load at | ||
715 | inst->type = CMD_LOAD_AT; | ||
716 | next(true); | ||
717 | inst->addr = parse_intexpr(&lctx, cmd_file->constant_list); | ||
718 | } | ||
719 | if(lexem.type != LEX_SEMICOLON) | ||
720 | parse_error(lexem, "';' expected after command\n"); | ||
721 | } | ||
722 | else if(inst->type == CMD_CALL || inst->type == CMD_JUMP) | ||
723 | { | ||
724 | if(lexem.type == LEX_IDENTIFIER) | ||
725 | { | ||
726 | inst->identifier = lexem.str; | ||
727 | if(db_find_source_by_id(cmd_file, inst->identifier) == NULL) | ||
728 | parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier); | ||
729 | next(false); /* lexem string kept as identifier */ | ||
730 | } | ||
731 | else | ||
732 | { | ||
733 | inst->type = (inst->type == CMD_CALL) ? CMD_CALL_AT : CMD_JUMP_AT; | ||
734 | inst->addr = parse_intexpr(&lctx, cmd_file->constant_list); | ||
735 | } | ||
736 | |||
737 | if(lexem.type == LEX_LPAREN) | ||
738 | { | ||
739 | next(true); | ||
740 | inst->argument = parse_intexpr(&lctx, cmd_file->constant_list); | ||
741 | if(lexem.type != LEX_RPAREN) | ||
742 | parse_error(lexem, "Expected closing brace\n"); | ||
743 | next(true); | ||
744 | } | ||
745 | if(lexem.type != LEX_SEMICOLON) | ||
746 | parse_error(lexem, "';' expected after command\n"); | ||
747 | } | ||
748 | else if(inst->type == CMD_MODE) | ||
749 | { | ||
750 | inst->argument = parse_intexpr(&lctx, cmd_file->constant_list); | ||
751 | if(lexem.type != LEX_SEMICOLON) | ||
752 | parse_error(lexem, "Expected ';' after command\n"); | ||
753 | } | ||
754 | else | ||
755 | parse_error(lexem, "Internal error"); | ||
756 | if(end_list == NULL) | ||
757 | { | ||
758 | sec->inst_list = inst; | ||
759 | end_list = inst; | ||
760 | } | ||
761 | else | ||
762 | { | ||
763 | end_list->next = inst; | ||
764 | end_list = inst; | ||
765 | } | ||
766 | } | ||
767 | } | ||
768 | else if(lexem.type == LEX_LE) | ||
769 | { | ||
770 | sec->is_data = true; | ||
771 | next(true); | ||
772 | if(lexem.type != LEX_IDENTIFIER) | ||
773 | parse_error(lexem, "Identifier expected after '<='\n"); | ||
774 | sec->source_id = lexem.str; | ||
775 | next(false); /* lexem string is kept as source id */ | ||
776 | if(lexem.type != LEX_SEMICOLON) | ||
777 | parse_error(lexem, "';' expected after identifier\n"); | ||
778 | } | ||
779 | else | ||
780 | parse_error(lexem, "'{' or '<=' expected after section directive\n"); | ||
781 | |||
782 | if(end_sec == NULL) | ||
783 | { | ||
784 | cmd_file->section_list = sec; | ||
785 | end_sec = sec; | ||
786 | } | ||
787 | else | ||
788 | { | ||
789 | end_sec->next = sec; | ||
790 | end_sec = sec; | ||
791 | } | ||
792 | } | ||
793 | #undef lexem | ||
794 | #undef next | ||
795 | |||
796 | free(buf); | ||
797 | return cmd_file; | ||
798 | } | ||
799 | |||
800 | void db_generate_default_sb_version(struct sb_version_t *ver) | ||
801 | { | ||
802 | ver->major = ver->minor = ver->revision = 0x999; | ||
803 | } | ||
804 | |||
805 | void db_free_option_list(struct cmd_option_t *opt_list) | ||
806 | { | ||
807 | while(opt_list) | ||
808 | { | ||
809 | struct cmd_option_t *next = opt_list->next; | ||
810 | fflush(stdout); | ||
811 | free(opt_list->name); | ||
812 | free(opt_list->str); | ||
813 | free(opt_list); | ||
814 | opt_list = next; | ||
815 | } | ||
816 | } | ||
817 | |||
818 | void db_free(struct cmd_file_t *file) | ||
819 | { | ||
820 | db_free_option_list(file->opt_list); | ||
821 | db_free_option_list(file->constant_list); | ||
822 | struct cmd_source_t *src = file->source_list; | ||
823 | while(src) | ||
824 | { | ||
825 | struct cmd_source_t *next = src->next; | ||
826 | free(src->identifier); | ||
827 | fflush(stdout); | ||
828 | free(src->filename); | ||
829 | if(src->loaded) | ||
830 | { | ||
831 | if(src->type == CMD_SRC_BIN) | ||
832 | free(src->bin.data); | ||
833 | if(src->type == CMD_SRC_ELF) | ||
834 | elf_release(&src->elf); | ||
835 | } | ||
836 | free(src); | ||
837 | src = next; | ||
838 | } | ||
839 | struct cmd_section_t *sec = file->section_list; | ||
840 | while(sec) | ||
841 | { | ||
842 | struct cmd_section_t *next = sec->next; | ||
843 | db_free_option_list(sec->opt_list); | ||
844 | free(sec->source_id); | ||
845 | struct cmd_inst_t *inst = sec->inst_list; | ||
846 | while(inst) | ||
847 | { | ||
848 | struct cmd_inst_t *next = inst->next; | ||
849 | free(inst->identifier); | ||
850 | free(inst); | ||
851 | inst = next; | ||
852 | } | ||
853 | free(sec); | ||
854 | sec = next; | ||
855 | } | ||
856 | free(file); | ||
857 | } | ||