A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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
30enum 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
49struct 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
59struct 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
72static 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
81static inline bool eof(struct context_t *ctx)
82{
83 return ctx->ptr == ctx->end;
84}
85
86static inline bool next_valid(struct context_t *ctx, int nr)
87{
88 return ctx->ptr + nr < ctx->end;
89}
90
91static inline char cur_char(struct context_t *ctx)
92{
93 return *ctx->ptr;
94}
95
96static inline char next_char(struct context_t *ctx, int nr)
97{
98 return ctx->ptr[nr];
99}
100
101static 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
107static 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
135static void __parse_string_emit(void *user, char c)
136{
137 char **pstr = (char **)user;
138 *(*pstr)++ = c;
139}
140
141static void __parse_string_count(void *user, char c)
142{
143 (void) c;
144 (*(int *)user)++;
145}
146
147static 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
164static 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
193static 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 uint8_t v;
211 if(convxdigit(cur_char(ctx), &v))
212 break;
213 lexem->num = base * lexem->num + v;
214 advance(ctx, 1);
215 }
216}
217
218static 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
232static 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
298static 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
319struct cmd_option_t *db_add_opt(struct cmd_option_t **opt, const char *identifier, bool is_str)
320{
321 while(*opt)
322 opt = &(*opt)->next;
323 *opt = xmalloc(sizeof(struct cmd_option_t));
324 memset(*opt, 0, sizeof(struct cmd_option_t));
325 (*opt)->name = strdup(identifier);
326 (*opt)->is_string = is_str;
327 return *opt;
328}
329
330void db_add_str_opt(struct cmd_option_t **opt, const char *name, const char *value)
331{
332 db_add_opt(opt, name, true)->str = strdup(value);
333}
334
335void db_add_int_opt(struct cmd_option_t **opt, const char *name, uint32_t value)
336{
337 db_add_opt(opt, name, false)->val = value;
338}
339
340static struct cmd_source_t *db_add_src(struct cmd_source_t **src, const char *identifier, bool is_extern)
341{
342 while(*src)
343 src = &(*src)->next;
344 *src = xmalloc(sizeof(struct cmd_source_t));
345 memset(*src, 0, sizeof(struct cmd_source_t));
346 (*src)->identifier = strdup(identifier);
347 (*src)->is_extern = is_extern;
348 return *src;
349}
350
351void db_add_source(struct cmd_file_t *cmd_file, const char *identifier, const char *filename)
352{
353 db_add_src(&cmd_file->source_list, identifier, false)->filename = strdup(filename);
354}
355
356void db_add_extern_source(struct cmd_file_t *cmd_file, const char *identifier, int extern_nr)
357{
358 db_add_src(&cmd_file->source_list, identifier, true)->extern_nr = extern_nr;
359}
360
361static struct cmd_inst_t *db_add_inst(struct cmd_inst_t **list, enum cmd_inst_type_t type,
362 uint32_t argument)
363{
364 while(*list)
365 list = &(*list)->next;
366 *list = xmalloc(sizeof(struct cmd_inst_t));
367 memset(*list, 0, sizeof(struct cmd_inst_t));
368 (*list)->type = type;
369 (*list)->argument = argument;
370 return *list;
371}
372
373void db_add_inst_id(struct cmd_section_t *cmd_section, enum cmd_inst_type_t type,
374 const char *identifier, uint32_t argument)
375{
376 db_add_inst(&cmd_section->inst_list, type, argument)->identifier = strdup(identifier);
377}
378
379void db_add_inst_addr(struct cmd_section_t *cmd_section, enum cmd_inst_type_t type,
380 uint32_t addr, uint32_t argument)
381{
382 db_add_inst(&cmd_section->inst_list, type, argument)->addr = addr;
383}
384
385struct cmd_section_t *db_add_section(struct cmd_file_t *cmd_file, uint32_t identifier, bool data)
386{
387 struct cmd_section_t **prev = &cmd_file->section_list;
388 while(*prev)
389 prev = &(*prev)->next;
390 *prev = xmalloc(sizeof(struct cmd_section_t));
391 memset(*prev, 0, sizeof(struct cmd_section_t));
392 (*prev)->identifier = identifier;
393 (*prev)->is_data = data;
394 return *prev;
395}
396
397struct cmd_source_t *db_find_source_by_id(struct cmd_file_t *cmd_file, const char *id)
398{
399 struct cmd_source_t *src = cmd_file->source_list;
400 while(src)
401 {
402 if(strcmp(src->identifier, id) == 0)
403 return src;
404 src = src->next;
405 }
406 return NULL;
407}
408
409struct cmd_option_t *db_find_option_by_id(struct cmd_option_t *opt, const char *name)
410{
411 while(opt)
412 {
413 if(strcmp(opt->name, name) == 0)
414 return opt;
415 opt = opt->next;
416 }
417 return NULL;
418}
419
420#define INVALID_SB_SUBVERSION 0xffff
421
422static const char *parse_sb_subversion(const char *str, uint16_t *v)
423{
424 int len = 0;
425 *v = 0;
426 while(isdigit(str[len]) && len < 3)
427 *v = (*v) << 4 | (str[len++] - '0');
428 if(len == 0)
429 *v = INVALID_SB_SUBVERSION;
430 return str + len;
431}
432
433bool db_parse_sb_version(struct sb_version_t *ver, const char *str)
434{
435 str = parse_sb_subversion(str, &ver->major);
436 if(ver->major == INVALID_SB_SUBVERSION || *str != '.')
437 return false;
438 str = parse_sb_subversion(str + 1, &ver->minor);
439 if(ver->minor == INVALID_SB_SUBVERSION || *str != '.')
440 return false;
441 str = parse_sb_subversion(str + 1, &ver->revision);
442 if(ver->revision == INVALID_SB_SUBVERSION || *str != 0)
443 return false;
444 return true;
445}
446
447static bool db_generate_sb_subversion(uint16_t subver, char *str)
448{
449 str[0] = '0' + ((subver >> 8) & 0xf);
450 str[1] = '0' + ((subver >> 4) & 0xf);
451 str[2] = '0' + (subver & 0xf);
452 return true;
453}
454
455bool db_generate_sb_version(struct sb_version_t *ver, char *str, int size)
456{
457 if(size < 12)
458 return false;
459 str[3] = '.';
460 str[7] = '.';
461 str[11] = 0;
462 return db_generate_sb_subversion(ver->major, str) &&
463 db_generate_sb_subversion(ver->minor, str + 4) &&
464 db_generate_sb_subversion(ver->revision, str + 8);
465}
466
467#undef parse_error
468#define parse_error(lexem, ...) \
469 do { fprintf(stderr, "%s:%d: ", lexem.file, lexem.line); \
470 fprintf(stderr, __VA_ARGS__); exit(2); } while(0)
471
472struct lex_ctx_t
473{
474 struct context_t ctx;
475 struct lexem_t lexem;
476};
477
478/* When lexems hold strings (like identifier), it might be useful to steal
479 * the pointer and don't clean the lexem but in other case, one don't want
480 * to keep the pointer to the string and just want to release the memory.
481 * Thus clean_lexem should be true except when one keeps a pointer */
482static inline void next(struct lex_ctx_t *ctx, bool clean_lexem)
483{
484 if(clean_lexem)
485 free(ctx->lexem.str);
486 memset(&ctx->lexem, 0, sizeof(struct lexem_t));
487 next_lexem(&ctx->ctx, &ctx->lexem);
488}
489
490static uint32_t parse_term_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list)
491{
492 uint32_t ret = 0;
493 if(ctx->lexem.type == LEX_NUMBER)
494 ret = ctx->lexem.num;
495 else if(ctx->lexem.type == LEX_IDENTIFIER)
496 {
497 struct cmd_option_t *c = db_find_option_by_id(const_list, ctx->lexem.str);
498 if(c == NULL)
499 parse_error(ctx->lexem, "Undefined reference to constant '%s'\n", ctx->lexem.str);
500 if(c->is_string)
501 parse_error(ctx->lexem, "Internal error: constant '%s' is not an integer\n", ctx->lexem.str);
502 ret = c->val;
503 }
504 else
505 parse_error(ctx->lexem, "Number or constant identifier expected\n");
506 next(ctx, true);
507 return ret;
508}
509
510static uint32_t parse_shift_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list)
511{
512 uint32_t v = parse_term_expr(ctx, const_list);
513 while(ctx->lexem.type == LEX_LSHIFT)
514 {
515 next(ctx, true);
516 v <<= parse_term_expr(ctx, const_list);
517 }
518 return v;
519}
520
521static uint32_t parse_or_expr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list)
522{
523 uint32_t v = parse_shift_expr(ctx, const_list);
524 while(ctx->lexem.type == LEX_OR)
525 {
526 next(ctx, true);
527 v |= parse_shift_expr(ctx, const_list);
528 }
529 return v;
530}
531
532static uint32_t parse_intexpr(struct lex_ctx_t *ctx, struct cmd_option_t *const_list)
533{
534 return parse_or_expr(ctx, const_list);
535}
536
537#define NR_INITIAL_CONSTANTS 4
538static char *init_const_name[NR_INITIAL_CONSTANTS] = {"true", "false", "yes", "no"};
539static uint32_t init_const_value[NR_INITIAL_CONSTANTS] = {1, 0, 1, 0};
540
541struct cmd_file_t *db_parse_file(const char *file)
542{
543 size_t size;
544 FILE *f = fopen(file, "r");
545 if(f == NULL)
546 {
547 if(g_debug)
548 perror("Cannot open db file");
549 return NULL;
550 }
551 fseek(f, 0, SEEK_END);
552 size = ftell(f);
553 fseek(f, 0, SEEK_SET);
554 char *buf = xmalloc(size);
555 if(fread(buf, size, 1, f) != 1)
556 {
557 if(g_debug)
558 perror("Cannot read db file");
559 return NULL;
560 }
561 fclose(f);
562
563 if(g_debug)
564 printf("Parsing db file '%s'\n", file);
565 struct cmd_file_t *cmd_file = xmalloc(sizeof(struct cmd_file_t));
566 memset(cmd_file, 0, sizeof(struct cmd_file_t));
567
568 /* add initial constants */
569 for(int i = 0; i < NR_INITIAL_CONSTANTS; i++)
570 db_add_int_opt(&cmd_file->constant_list, init_const_name[i], init_const_value[i]);
571
572 struct lex_ctx_t lctx;
573 lctx.ctx.file = file;
574 lctx.ctx.line = 1;
575 lctx.ctx.begin = buf;
576 lctx.ctx.ptr = buf;
577 lctx.ctx.end = buf + size;
578 #define next(clean_lexem) next(&lctx, clean_lexem)
579 #define lexem lctx.lexem
580 /* init lexer */
581 next(false); /* don't clean init lexem because it doesn't exist */
582 /* constants ? */
583 if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "constants"))
584 {
585 next(true);
586 if(lexem.type != LEX_LBRACE)
587 parse_error(lexem, "'{' expected after 'constants'\n");
588
589 while(true)
590 {
591 next(true);
592 if(lexem.type == LEX_RBRACE)
593 break;
594 if(lexem.type != LEX_IDENTIFIER)
595 parse_error(lexem, "Identifier expected in constants\n");
596 const char *name = lexem.str;
597 next(false); /* lexem string is kept as option name */
598 if(lexem.type != LEX_EQUAL)
599 parse_error(lexem, "'=' expected after identifier\n");
600 next(true);
601 db_add_int_opt(&cmd_file->constant_list, name, parse_intexpr(&lctx, cmd_file->constant_list));
602 if(lexem.type != LEX_SEMICOLON)
603 parse_error(lexem, "';' expected after string\n");
604 }
605 next(true);
606 }
607 /* options ? */
608 if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "options"))
609 {
610 next(true);
611 if(lexem.type != LEX_LBRACE)
612 parse_error(lexem, "'{' expected after 'options'\n");
613
614 while(true)
615 {
616 next(true);
617 if(lexem.type == LEX_RBRACE)
618 break;
619 if(lexem.type != LEX_IDENTIFIER)
620 parse_error(lexem, "Identifier expected in options\n");
621 const char *name = lexem.str;
622 next(false); /* lexem string is kept as option name */
623 if(lexem.type != LEX_EQUAL)
624 parse_error(lexem, "'=' expected after identifier\n");
625 next(true);
626 if(lexem.type == LEX_STRING)
627 {
628 db_add_str_opt(&cmd_file->opt_list, name, lexem.str);
629 next(true);
630 }
631 else
632 db_add_int_opt(&cmd_file->opt_list, name, parse_intexpr(&lctx, cmd_file->constant_list));
633 if(lexem.type != LEX_SEMICOLON)
634 parse_error(lexem, "';' expected after string\n");
635 }
636 next(true);
637 }
638 /* sources */
639 if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "sources"))
640 parse_error(lexem, "'sources' expected\n");
641 next(true);
642 if(lexem.type != LEX_LBRACE)
643 parse_error(lexem, "'{' expected after 'sources'\n");
644
645 while(true)
646 {
647 next(true);
648 if(lexem.type == LEX_RBRACE)
649 break;
650 if(lexem.type != LEX_IDENTIFIER)
651 parse_error(lexem, "identifier expected in sources\n");
652 const char *srcid = lexem.str;
653 if(db_find_source_by_id(cmd_file, srcid) != NULL)
654 parse_error(lexem, "Duplicate source identifier\n");
655 next(false); /* lexem string is kept as source name */
656 if(lexem.type != LEX_EQUAL)
657 parse_error(lexem, "'=' expected after identifier\n");
658 next(true);
659 if(lexem.type == LEX_STRING)
660 {
661 db_add_source(cmd_file, srcid, lexem.str);
662 next(true);
663 }
664 else if(lexem.type == LEX_IDENTIFIER && !strcmp(lexem.str, "extern"))
665 {
666 next(true);
667 if(lexem.type != LEX_LPAREN)
668 parse_error(lexem, "'(' expected after 'extern'\n");
669 next(true);
670 db_add_extern_source(cmd_file, srcid, parse_intexpr(&lctx, cmd_file->constant_list));
671 if(lexem.type != LEX_RPAREN)
672 parse_error(lexem, "')' expected\n");
673 next(true);
674 }
675 else
676 parse_error(lexem, "String or 'extern' expected after '='\n");
677 if(lexem.type != LEX_SEMICOLON)
678 parse_error(lexem, "';' expected\n");
679 }
680
681 /* sections */
682 while(true)
683 {
684 next(true);
685 if(lexem.type == LEX_EOF)
686 break;
687 if(lexem.type != LEX_IDENTIFIER || strcmp(lexem.str, "section") != 0)
688 parse_error(lexem, "'section' expected\n");
689 next(true);
690 if(lexem.type != LEX_LPAREN)
691 parse_error(lexem, "'(' expected after 'section'\n");
692 next(true);
693 /* can be any number */
694 struct cmd_section_t *sec = db_add_section(cmd_file, parse_intexpr(&lctx, cmd_file->constant_list), false);
695 /* options ? */
696 if(lexem.type == LEX_SEMICOLON)
697 {
698 do
699 {
700 next(true);
701 if(lexem.type != LEX_IDENTIFIER)
702 parse_error(lexem, "Identifier expected for section option\n");
703 const char *name = lexem.str;
704 next(false); /* lexem string is kept as option name */
705 if(lexem.type != LEX_EQUAL)
706 parse_error(lexem, "'=' expected after option identifier\n");
707 next(true);
708 if(lexem.type == LEX_STRING)
709 {
710 db_add_str_opt(&sec->opt_list, name, lexem.str);
711 next(true);
712 }
713 else
714 db_add_int_opt(&sec->opt_list, name, parse_intexpr(&lctx, cmd_file->constant_list));
715 }while(lexem.type == LEX_COLON);
716 }
717 if(lexem.type != LEX_RPAREN)
718 parse_error(lexem, "')' expected after section identifier\n");
719 next(true);
720 if(lexem.type == LEX_LBRACE)
721 {
722 sec->is_data = false;
723 /* commands */
724 while(true)
725 {
726 next(true);
727 if(lexem.type == LEX_RBRACE)
728 break;
729 struct cmd_inst_t *inst = db_add_inst(&sec->inst_list, CMD_LOAD, 0);
730 if(lexem.type != LEX_IDENTIFIER)
731 parse_error(lexem, "Instruction expected in section\n");
732 if(strcmp(lexem.str, "load") == 0)
733 inst->type = CMD_LOAD;
734 else if(strcmp(lexem.str, "call") == 0)
735 inst->type = CMD_CALL;
736 else if(strcmp(lexem.str, "jump") == 0)
737 inst->type = CMD_JUMP;
738 else if(strcmp(lexem.str, "mode") == 0)
739 inst->type = CMD_MODE;
740 else
741 parse_error(lexem, "Instruction expected in section\n");
742 next(true);
743
744 if(inst->type == CMD_LOAD)
745 {
746 if(lexem.type != LEX_IDENTIFIER)
747 parse_error(lexem, "Identifier expected after instruction\n");
748 inst->identifier = lexem.str;
749 if(db_find_source_by_id(cmd_file, inst->identifier) == NULL)
750 parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier);
751 next(false); /* lexem string kept as identifier */
752 if(lexem.type == LEX_RANGLE)
753 {
754 // load at
755 inst->type = CMD_LOAD_AT;
756 next(true);
757 inst->addr = parse_intexpr(&lctx, cmd_file->constant_list);
758 }
759 if(lexem.type != LEX_SEMICOLON)
760 parse_error(lexem, "';' expected after command\n");
761 }
762 else if(inst->type == CMD_CALL || inst->type == CMD_JUMP)
763 {
764 if(lexem.type == LEX_IDENTIFIER)
765 {
766 inst->identifier = lexem.str;
767 if(db_find_source_by_id(cmd_file, inst->identifier) == NULL)
768 parse_error(lexem, "Undefined reference to source '%s'\n", inst->identifier);
769 next(false); /* lexem string kept as identifier */
770 }
771 else
772 {
773 inst->type = (inst->type == CMD_CALL) ? CMD_CALL_AT : CMD_JUMP_AT;
774 inst->addr = parse_intexpr(&lctx, cmd_file->constant_list);
775 }
776
777 if(lexem.type == LEX_LPAREN)
778 {
779 next(true);
780 inst->argument = parse_intexpr(&lctx, cmd_file->constant_list);
781 if(lexem.type != LEX_RPAREN)
782 parse_error(lexem, "Expected closing brace\n");
783 next(true);
784 }
785 if(lexem.type != LEX_SEMICOLON)
786 parse_error(lexem, "';' expected after command\n");
787 }
788 else if(inst->type == CMD_MODE)
789 {
790 inst->argument = parse_intexpr(&lctx, cmd_file->constant_list);
791 if(lexem.type != LEX_SEMICOLON)
792 parse_error(lexem, "Expected ';' after command\n");
793 }
794 else
795 parse_error(lexem, "Internal error");
796 }
797 }
798 else if(lexem.type == LEX_LE)
799 {
800 sec->is_data = true;
801 next(true);
802 if(lexem.type != LEX_IDENTIFIER)
803 parse_error(lexem, "Identifier expected after '<='\n");
804 sec->source_id = lexem.str;
805 next(false); /* lexem string is kept as source id */
806 if(lexem.type != LEX_SEMICOLON)
807 parse_error(lexem, "';' expected after identifier\n");
808 }
809 else
810 parse_error(lexem, "'{' or '<=' expected after section directive\n");
811 }
812 #undef lexem
813 #undef next
814
815 free(buf);
816 return cmd_file;
817}
818
819void db_free_option_list(struct cmd_option_t *opt_list)
820{
821 while(opt_list)
822 {
823 struct cmd_option_t *next = opt_list->next;
824 fflush(stdout);
825 free(opt_list->name);
826 free(opt_list->str);
827 free(opt_list);
828 opt_list = next;
829 }
830}
831
832static bool db_generate_options(FILE *f, const char *secname, struct cmd_option_t *list)
833{
834 fprintf(f, "%s\n", secname);
835 fprintf(f, "{\n");
836 while(list)
837 {
838 fprintf(f, " %s = ", list->name);
839 if(list->is_string)
840 fprintf(f, "\"%s\";\n", list->str); // FIXME handle escape
841 else
842 fprintf(f, "0x%x;\n", list->val);
843 list = list->next;
844 }
845 fprintf(f, "}\n");
846 return true;
847}
848
849static bool db_generate_section_options(FILE *f, struct cmd_option_t *list)
850{
851 bool first = true;
852 while(list)
853 {
854 fprintf(f, "%c %s = ", first ? ';' : ',', list->name);
855 if(list->is_string)
856 fprintf(f, "\"%s\"", list->str); // FIXME handle escape
857 else
858 fprintf(f, "0x%x", list->val);
859 first = false;
860 list = list->next;
861 }
862 return true;
863}
864
865static bool db_generate_sources(FILE *f, struct cmd_source_t *list)
866{
867 fprintf(f, "sources\n"),
868 fprintf(f, "{\n");
869 while(list)
870 {
871 fprintf(f, " %s = ", list->identifier);
872 if(list->is_extern)
873 fprintf(f, "extern(%d);\n", list->extern_nr);
874 else
875 fprintf(f, "\"%s\";\n", list->filename); // FIXME handle escape
876 list = list->next;
877 }
878 fprintf(f, "}\n");
879 return true;
880}
881
882static bool db_generate_section(FILE *f, struct cmd_section_t *section)
883{
884 fprintf(f, "section(%#x", section->identifier);
885 db_generate_section_options(f, section->opt_list);
886 if(section->is_data)
887 {
888 fprintf(f, ") <= %s;\n", section->source_id);
889 return true;
890 }
891 fprintf(f, ")\n{\n");
892 struct cmd_inst_t *inst = section->inst_list;
893 while(inst)
894 {
895 fprintf(f, " ");
896 switch(inst->type)
897 {
898 case CMD_LOAD:
899 fprintf(f, "load %s;\n", inst->identifier);
900 break;
901 case CMD_LOAD_AT:
902 fprintf(f, "load %s > %#x;\n", inst->identifier, inst->addr);
903 break;
904 case CMD_CALL:
905 fprintf(f, "call %s(%#x);\n", inst->identifier, inst->argument);
906 break;
907 case CMD_CALL_AT:
908 fprintf(f, "call %#x(%#x);\n", inst->addr, inst->argument);
909 break;
910 case CMD_JUMP:
911 fprintf(f, "jump %s(%#x);\n", inst->identifier, inst->argument);
912 break;
913 case CMD_JUMP_AT:
914 fprintf(f, "jump %#x(%#x);\n", inst->addr, inst->argument);
915 break;
916 case CMD_MODE:
917 fprintf(f, "mode %#x;\n", inst->argument);
918 break;
919 default:
920 bug("die");
921 }
922 inst = inst->next;
923 }
924 fprintf(f, "}\n");
925 return true;
926}
927
928static bool db_generate_sections(FILE *f, struct cmd_section_t *section)
929{
930 while(section)
931 if(!db_generate_section(f, section))
932 return false;
933 else
934 section = section->next;
935 return true;
936}
937
938bool db_generate_file(struct cmd_file_t *file, const char *filename, void *user, db_color_printf printf)
939{
940 FILE *f = fopen(filename, "w");
941 if(f == NULL)
942 return printf(user, true, GREY, "Cannot open '%s' for writing: %m\n", filename), false;
943 if(!db_generate_options(f, "constants", file->constant_list))
944 goto Lerr;
945 if(!db_generate_options(f, "options", file->opt_list))
946 goto Lerr;
947 if(!db_generate_sources(f, file->source_list))
948 goto Lerr;
949 if(!db_generate_sections(f, file->section_list))
950 goto Lerr;
951
952 fclose(f);
953 return true;
954
955 Lerr:
956 fclose(f);
957 return false;
958}
959
960void db_free(struct cmd_file_t *file)
961{
962 db_free_option_list(file->opt_list);
963 db_free_option_list(file->constant_list);
964 struct cmd_source_t *src = file->source_list;
965 while(src)
966 {
967 struct cmd_source_t *next = src->next;
968 free(src->identifier);
969 fflush(stdout);
970 free(src->filename);
971 if(src->loaded)
972 {
973 if(src->type == CMD_SRC_BIN)
974 free(src->bin.data);
975 if(src->type == CMD_SRC_ELF)
976 elf_release(&src->elf);
977 }
978 free(src);
979 src = next;
980 }
981 struct cmd_section_t *sec = file->section_list;
982 while(sec)
983 {
984 struct cmd_section_t *next = sec->next;
985 db_free_option_list(sec->opt_list);
986 free(sec->source_id);
987 struct cmd_inst_t *inst = sec->inst_list;
988 while(inst)
989 {
990 struct cmd_inst_t *next = inst->next;
991 free(inst->identifier);
992 free(inst);
993 inst = next;
994 }
995 free(sec);
996 sec = next;
997 }
998 free(file);
999}