A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 999 lines 31 kB view raw
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}