config.c (35169B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <stdarg.h> 4 #include <string.h> 5 #include <errno.h> 6 #include <toml.h> 7 #include "core.h" 8 #include "config.h" 9 #include "chordpro.h" 10 #include "chord_diagram.h" 11 12 extern const struct InstrumentInfo instruments[]; 13 14 static const char *notation_systems[] = { 15 "common", 16 "german", 17 "scandinavian", 18 "latin", 19 "roman", 20 "nashville" 21 }; 22 23 static const char *parse_modes[] = { 24 "strict", 25 "relaxed" 26 }; 27 28 static const char *text_types[] = { 29 "chord", "annotation", "chorus", 30 "footer", "grid", "tab", "toc", "toc_title", "text", 31 "title", "subtitle", "label", "comment", 32 "comment_italic", "comment_box" 33 }; 34 35 static const char *alignments[] = { 36 "left", "center", "right" 37 }; 38 39 static struct Note notes_common[] = { 40 { .note = "C", .sharp = "C#", .flat = NULL }, 41 { .note = "D", .sharp = "D#", .flat = "Db" }, 42 { .note = "E", .sharp = NULL, .flat = "Eb" }, 43 { .note = "F", .sharp = "F#", .flat = NULL }, 44 { .note = "G", .sharp = "G#", .flat = "Gb" }, 45 { .note = "A", .sharp = "A#", .flat = "Ab" }, 46 { .note = "B", .sharp = NULL, .flat = "Bb" } 47 }; 48 49 static struct Note notes_german[] = { 50 { .note = "C", .sharp = "Cis", .flat = NULL }, 51 { .note = "D", .sharp = "Dis", .flat = "Des" }, 52 { .note = "E", .sharp = NULL, .flat = "Es" }, 53 { .note = "F", .sharp = "Fis", .flat = NULL }, 54 { .note = "G", .sharp = "Gis", .flat = "Ges" }, 55 { .note = "A", .sharp = "Ais", .flat = "As" }, 56 { .note = "H", .sharp = NULL, .flat = "B"}, 57 }; 58 59 static struct Note notes_scandinavian[] = { 60 { .note = "C", .sharp = "C#", .flat = NULL }, 61 { .note = "D", .sharp = "D#", .flat = "Db"}, 62 { .note = "E", .sharp = NULL, .flat = "Eb"}, 63 { .note = "F", .sharp = "F#", .flat = NULL }, 64 { .note = "G", .sharp = "G#", .flat = "Gb"}, 65 { .note = "A", .sharp = "A#", .flat = "Ab"}, 66 { .note = "H", .sharp = NULL, .flat = "B"}, 67 }; 68 69 static struct Note notes_latin[] = { 70 { .note = "Do", .sharp = "Do#", .flat = NULL }, 71 { .note = "Re", .sharp = "Re#", .flat = "Reb"}, 72 { .note = "Mi", .sharp = NULL, .flat = "Mib"}, 73 { .note = "Fa", .sharp = "Fa#", .flat = NULL }, 74 { .note = "Sol", .sharp = "Sol#", .flat = "Solb"}, 75 { .note = "La", .sharp = "La#", .flat = "Lab"}, 76 { .note = "Si", .sharp = NULL, .flat = "Sib"}, 77 }; 78 79 static struct Note notes_roman[] = { 80 { .note = "I", .sharp = "I#", .flat = NULL }, 81 { .note = "II", .sharp = "II#", .flat = "IIb" }, 82 { .note = "III", .sharp = NULL, .flat = "IIIb" }, 83 { .note = "IV", .sharp = "IV#", .flat = NULL }, 84 { .note = "V", .sharp = "V#", .flat = "Vb" }, 85 { .note = "VI", .sharp = "VI#", .flat = "VIb" }, 86 { .note = "VII", .sharp = NULL, .flat = "VIIb" }, 87 }; 88 89 static struct Note notes_nashville[] = { 90 { .note = "1", .sharp = "1#", .flat = NULL }, 91 { .note = "2", .sharp = "2#", .flat = "2b" }, 92 { .note = "3", .sharp = NULL, .flat = "3b" }, 93 { .note = "4", .sharp = "4#", .flat = NULL }, 94 { .note = "5", .sharp = "5#", .flat = "5b" }, 95 { .note = "6", .sharp = "6#", .flat = "6b" }, 96 { .note = "7", .sharp = NULL, .flat = "7b" }, 97 }; 98 99 static void 100 config_log( 101 struct ConfigContext *ctx, 102 enum LogLevel level, 103 const char *toml_section, 104 const char *msg, 105 ... 106 ) 107 { 108 va_list va; 109 va_start(va, msg); 110 size_t size = 10 + strlen(toml_section) + strlen(msg) + 1; 111 char str[size]; 112 snprintf((char *)&str, size, "section %s: %s", toml_section, msg); 113 util_vlog(ctx->config_filepath, 0, level, str, va); 114 } 115 116 static enum TextType 117 config_text_type_parse(const char *str, bool *error) 118 { 119 *error = false; 120 if (!strcmp(str, text_types[TEXT_TYPE_CHORD])) { 121 return TEXT_TYPE_CHORD; 122 } else 123 if (!strcmp(str, text_types[TEXT_TYPE_ANNOT])) { 124 return TEXT_TYPE_ANNOT; 125 } else 126 if (!strcmp(str, text_types[TEXT_TYPE_CHORUS])) { 127 return TEXT_TYPE_CHORUS; 128 } else 129 if (!strcmp(str, text_types[TEXT_TYPE_FOOTER])) { 130 return TEXT_TYPE_FOOTER; 131 } else 132 if (!strcmp(str, text_types[TEXT_TYPE_GRID])) { 133 return TEXT_TYPE_GRID; 134 } else 135 if (!strcmp(str, text_types[TEXT_TYPE_TAB])) { 136 return TEXT_TYPE_TAB; 137 } else 138 if (!strcmp(str, text_types[TEXT_TYPE_TOC])) { 139 return TEXT_TYPE_TOC; 140 } else 141 if (!strcmp(str, text_types[TEXT_TYPE_TOC_TITLE])) { 142 return TEXT_TYPE_TOC_TITLE; 143 } else 144 if (!strcmp(str, text_types[TEXT_TYPE_TEXT])) { 145 return TEXT_TYPE_TEXT; 146 } else 147 if (!strcmp(str, text_types[TEXT_TYPE_TITLE])) { 148 return TEXT_TYPE_TITLE; 149 } else 150 if (!strcmp(str, text_types[TEXT_TYPE_SUBTITLE])) { 151 return TEXT_TYPE_SUBTITLE; 152 } else 153 if (!strcmp(str, text_types[TEXT_TYPE_LABEL])) { 154 return TEXT_TYPE_LABEL; 155 } else 156 if (!strcmp(str, text_types[TEXT_TYPE_COMMENT])) { 157 return TEXT_TYPE_COMMENT; 158 } else 159 if (!strcmp(str, text_types[TEXT_TYPE_COMMENT_ITALIC])) { 160 return TEXT_TYPE_COMMENT_ITALIC; 161 } else 162 if (!strcmp(str, text_types[TEXT_TYPE_COMMENT_BOX])) { 163 return TEXT_TYPE_COMMENT_BOX; 164 } 165 *error = true; 166 return TEXT_TYPE_CHORD; // unused 167 } 168 169 static enum NotationSystem 170 config_notation_system_parse(const char *str) 171 { 172 if (!strcmp(str, notation_systems[NOTATION_SYSTEM_COMMON]) || !strcmp(str, "dutch")) { 173 return NOTATION_SYSTEM_COMMON; 174 } else if (!strcmp(str, notation_systems[NOTATION_SYSTEM_GERMAN])) { 175 return NOTATION_SYSTEM_GERMAN; 176 } else if (!strcmp(str, notation_systems[NOTATION_SYSTEM_SCANDINAVIAN])) { 177 return NOTATION_SYSTEM_SCANDINAVIAN; 178 } else if (!strcmp(str, notation_systems[NOTATION_SYSTEM_LATIN])) { 179 return NOTATION_SYSTEM_LATIN; 180 } else if (!strcmp(str, notation_systems[NOTATION_SYSTEM_ROMAN])) { 181 return NOTATION_SYSTEM_ROMAN; 182 } else if (!strcmp(str, notation_systems[NOTATION_SYSTEM_NASHVILLE])) { 183 return NOTATION_SYSTEM_NASHVILLE; 184 } 185 return NOTATION_SYSTEM_CUSTOM; 186 } 187 188 static const char * 189 config_notation_system_to_config_string(enum NotationSystem system) 190 { 191 return notation_systems[system]; 192 } 193 194 static enum Instrument 195 config_instrument_parse(const char *str, bool *error) 196 { 197 *error = false; 198 if (!strcmp(str, instruments[INSTRUMENT_GUITAR].name)) { 199 return INSTRUMENT_GUITAR; 200 } else 201 /* if (!strcmp(str, instruments[INSTRUMENT_KEYBOARD].name)) { 202 return INSTRUMENT_KEYBOARD; 203 } else */ 204 if (!strcmp(str, instruments[INSTRUMENT_MANDOLIN].name)) { 205 return INSTRUMENT_MANDOLIN; 206 } else 207 if (!strcmp(str, instruments[INSTRUMENT_UKULELE].name)) { 208 return INSTRUMENT_UKULELE; 209 } 210 *error = true; 211 return INSTRUMENT_GUITAR; 212 } 213 214 static const char * 215 config_instrument_to_config_string(enum Instrument ins) 216 { 217 return instruments[ins].name; 218 } 219 220 struct InstrumentInfo 221 config_instrument_get(enum Instrument ins) 222 { 223 return instruments[ins]; 224 } 225 226 static struct Note * 227 config_note_new(void) 228 { 229 struct Note *note = emalloc(sizeof(struct Note)); 230 note->note = NULL; 231 note->sharp = NULL; 232 note->flat = NULL; 233 return note; 234 } 235 236 static void 237 config_note_free(struct Note *note) 238 { 239 if (!note) { 240 return; 241 } 242 free(note->note); 243 free(note->sharp); 244 free(note->flat); 245 free(note); 246 } 247 248 static void 249 config_notes_free(struct Note **notes) 250 { 251 if (!notes) { 252 return; 253 } 254 int i; 255 for (i = 0; i<7; i++) { 256 config_note_free(notes[i]); 257 } 258 free(notes); 259 } 260 261 struct Note * 262 config_notes_common(void) 263 { 264 struct Note *notes = emalloc(sizeof(notes_common)); 265 memcpy(notes, notes_common, sizeof(notes_common)); 266 return notes; 267 } 268 269 static struct Note ** 270 config_notes_new_default(enum NotationSystem system) 271 { 272 struct Note **notes_default = emalloc(8 * sizeof(struct Note *)); 273 struct Note *notes; 274 switch (system) { 275 case NOTATION_SYSTEM_GERMAN: 276 notes = (struct Note *)¬es_german; 277 break; 278 case NOTATION_SYSTEM_SCANDINAVIAN: 279 notes = (struct Note *)¬es_scandinavian; 280 break; 281 case NOTATION_SYSTEM_LATIN: 282 notes = (struct Note *)¬es_latin; 283 break; 284 case NOTATION_SYSTEM_ROMAN: 285 notes = (struct Note *)¬es_roman; 286 break; 287 case NOTATION_SYSTEM_NASHVILLE: 288 notes = (struct Note *)¬es_nashville; 289 break; 290 default: 291 notes = (struct Note *)¬es_common; 292 break; 293 } 294 int i; 295 for (i = 0; i<7; i++) { 296 notes_default[i] = config_note_new(); 297 if (notes[i].note) { 298 notes_default[i]->note = strdup(notes[i].note); 299 } 300 if (notes[i].sharp) { 301 notes_default[i]->sharp = strdup(notes[i].sharp); 302 } 303 if (notes[i].flat) { 304 notes_default[i]->flat = strdup(notes[i].flat); 305 } 306 } 307 return notes_default; 308 } 309 310 static struct Note ** 311 config_notes_load( 312 struct ConfigContext *ctx, 313 toml_table_t *notes, 314 const char *system 315 ) 316 { 317 struct Note **custom_notes; 318 toml_table_t *note; 319 toml_value_t value; 320 toml_array_t *arr; 321 int arr_len, k; 322 int i = 0; 323 324 custom_notes = emalloc(8 * sizeof(struct Note *)); 325 arr = toml_table_array(notes, system); 326 if (!arr) { 327 goto ERR; 328 } 329 arr_len = toml_array_len(arr); 330 if (arr_len != 7) { 331 config_log(ctx, LOG_ERR, "[notation_systems]", "Custom notation system '%s' has to have exactly 7 items. For an example see `lorid --print-default-config`.", system); 332 goto ERR; 333 } 334 for (i = 0; i<arr_len; i++) { 335 note = toml_array_table(arr, i); 336 custom_notes[i] = config_note_new(); 337 value = toml_table_string(note, "note"); 338 if (value.ok) { 339 custom_notes[i]->note = value.u.s; 340 } 341 value = toml_table_string(note, "sharp"); 342 if (value.ok) { 343 custom_notes[i]->sharp = value.u.s; 344 if (i == 2 || i == 6) { 345 config_log(ctx, LOG_ERR, "[notation_systems]", "Custom notation system '%s' can't have sharp value at array index '%d'.", system, i); 346 goto ERR; 347 } 348 } 349 value = toml_table_string(note, "flat"); 350 if (value.ok) { 351 custom_notes[i]->flat = value.u.s; 352 if (i == 0 || i == 3) { 353 config_log(ctx, LOG_ERR, "[notation_systems]", "Custom notation system '%s' can't have flat value at array index '%d'.", system, i); 354 goto ERR; 355 } 356 } 357 } 358 custom_notes[7] = NULL; 359 return custom_notes; 360 ERR: 361 for (k = 0; k<=i; k++) { 362 config_note_free(custom_notes[k]); 363 } 364 free(custom_notes); 365 return NULL; 366 } 367 368 static void 369 config_notes_print_as_toml(enum NotationSystem system) 370 { 371 struct Note *notes; 372 int i; 373 374 switch (system) { 375 case NOTATION_SYSTEM_COMMON: 376 notes = (struct Note *)¬es_common; 377 break; 378 case NOTATION_SYSTEM_GERMAN: 379 notes = (struct Note *)¬es_german; 380 break; 381 case NOTATION_SYSTEM_SCANDINAVIAN: 382 notes = (struct Note *)¬es_scandinavian; 383 break; 384 case NOTATION_SYSTEM_LATIN: 385 notes = (struct Note *)¬es_latin; 386 break; 387 case NOTATION_SYSTEM_ROMAN: 388 notes = (struct Note *)¬es_roman; 389 break; 390 case NOTATION_SYSTEM_NASHVILLE: 391 notes = (struct Note *)¬es_nashville; 392 break; 393 case NOTATION_SYSTEM_CUSTOM: 394 return; 395 } 396 printf("%s = [\n", config_notation_system_to_config_string(system)); 397 for (i = 0; i<7; i++) { 398 printf("\t{ note = \"%s\",", notes[i].note); 399 if (notes[i].sharp) { 400 printf(" sharp = \"%s\",", notes[i].sharp); 401 } 402 if (notes[i].flat) { 403 printf(" flat = \"%s\"", notes[i].flat); 404 } 405 printf(" },\n"); 406 } 407 printf("]\n\n"); 408 } 409 410 static const char * 411 config_parse_mode_to_config_string(enum ParseMode mode) 412 { 413 return parse_modes[mode]; 414 } 415 416 static enum Alignment 417 config_alignment_parse(const char *str, bool *error) 418 { 419 *error = false; 420 if (!strcmp(str, alignments[ALIGNMENT_LEFT])) { 421 return ALIGNMENT_LEFT; 422 } else 423 if (!strcmp(str, alignments[ALIGNMENT_CENTER])) { 424 return ALIGNMENT_CENTER; 425 } else 426 if (!strcmp(str, alignments[ALIGNMENT_RIGHT])) { 427 return ALIGNMENT_RIGHT; 428 } 429 *error = true; 430 return ALIGNMENT_LEFT; // unused 431 } 432 433 static const char * 434 config_alignment_to_config_string(enum Alignment align) 435 { 436 return alignments[align]; 437 } 438 439 void 440 config_print_default(void) 441 { 442 struct Config *config = config_load_default(); 443 int i; 444 445 printf("metadata_separator = \"%s\"\n\n", config->metadata_separator); 446 printf("[notation_systems]\n"); 447 config_notes_print_as_toml(NOTATION_SYSTEM_COMMON); 448 config_notes_print_as_toml(NOTATION_SYSTEM_GERMAN); 449 config_notes_print_as_toml(NOTATION_SYSTEM_SCANDINAVIAN); 450 config_notes_print_as_toml(NOTATION_SYSTEM_LATIN); 451 config_notes_print_as_toml(NOTATION_SYSTEM_ROMAN); 452 config_notes_print_as_toml(NOTATION_SYSTEM_NASHVILLE); 453 454 printf("[parser]\n"); 455 printf("[parser.chords]\n"); 456 printf("mode = \"%s\"\n", config_parse_mode_to_config_string(config->parser->chords->mode)); 457 printf("notation_system = \"%s\"\n\n", config_notation_system_to_config_string(config->parser->chords->notation_system)); 458 459 printf("[output]\n"); 460 printf("notation_system = \"%s\"\n", config_notation_system_to_config_string(config->output->notation_system)); 461 printf("start_song_on_new_page = %s\n\n", config->output->start_song_on_new_page ? "true" : "false"); 462 printf("[output.chorus]\n"); 463 printf("label = \"Chorus\"\n"); 464 printf("quote = false\n\n"); 465 printf("[output.chord_diagram]\n"); 466 printf("show = %s\n", config->output->diagram->show ? "true" : "false"); 467 printf("instrument = \"%s\"\n\n", config_instrument_to_config_string(config->output->diagram->instrument)); 468 printf("[output.page_no]\n"); 469 printf("show = %s\n", config->output->page_no->show ? "true" : "false"); 470 printf("alignment = \"%s\"\n\n", config_alignment_to_config_string(config->output->page_no->align)); 471 printf("[output.toc]\n"); 472 printf("show = %s\n", config->output->toc->show ? "true" : "false"); 473 printf("title = \"%s\"\n\n", config->output->toc->title); 474 printf("[output.styles]\n"); 475 for (i = 0; i<TEXT_TYPE_LENGTH; i++) { 476 printf("[output.styles.%s]\n\n", text_types[i]); 477 cho_style_print_as_toml(config->output->styles[i], text_types[i]); 478 } 479 config_free(config); 480 } 481 482 static bool 483 config_load_font( 484 struct Font *font, 485 toml_table_t *table, 486 struct ChoStylePresence *presence, 487 char (*err_buf)[25] 488 ) 489 { 490 enum FontStyle style; 491 enum FontWeight weight; 492 toml_value_t value; 493 bool error; 494 495 value = toml_table_string(table, "family"); 496 if (value.ok) { 497 presence->font.family = true; 498 free(font->family); 499 font->family = value.u.s; 500 } 501 value = toml_table_string(table, "style"); 502 if (value.ok) { 503 presence->font.style = true; 504 style = cho_font_style_parse(value.u.s, &error); 505 if (error) { 506 if (err_buf) { 507 strcpy((char *)err_buf, "style value is invalid."); 508 } 509 free(value.u.s); 510 return false; 511 } else { 512 font->style = style; 513 } 514 free(value.u.s); 515 } 516 value = toml_table_string(table, "weight"); 517 if (value.ok) { 518 presence->font.weight = true; 519 weight = cho_font_weight_parse(value.u.s, &error); 520 if (error) { 521 if (err_buf) { 522 strcpy((char *)err_buf, "weight value is invalid."); 523 } 524 free(value.u.s); 525 return false; 526 } else { 527 font->weight = weight; 528 } 529 free(value.u.s); 530 } 531 value = toml_table_int(table, "size"); 532 if (value.ok) { 533 presence->font.size = true; 534 font->size = value.u.i; 535 } 536 return true; 537 } 538 539 static bool 540 config_load_style( 541 struct ChoStyle *style, 542 toml_table_t *table, 543 struct ChoStylePresence *presence, 544 char (*err_buf)[38] 545 ) 546 { 547 bool error; 548 struct RGBColor *color; 549 enum LineStyle line_style; 550 toml_table_t *font_section; 551 toml_value_t value; 552 553 font_section = toml_table_table(table, "font"); 554 if (font_section) { 555 char err[25]; 556 if (!config_load_font(style->font, font_section, presence, &err)) { 557 DEBUG("config_load_font failed."); 558 snprintf((char *)err_buf, 38, "font.%s", err); 559 return false; 560 } 561 } 562 value = toml_table_string(table, "foreground_color"); 563 if (value.ok) { 564 presence->foreground_color = true; 565 color = cho_color_parse(value.u.s); 566 if (color) { 567 free(style->foreground_color); 568 style->foreground_color = color; 569 } else { 570 strcpy((char *)err_buf, "foreground color value is invalid."); 571 free(value.u.s); 572 return false; 573 } 574 free(value.u.s); 575 } 576 value = toml_table_string(table, "background_color"); 577 if (value.ok) { 578 presence->background_color = true; 579 color = cho_color_parse(value.u.s); 580 if (color) { 581 free(style->background_color); 582 style->background_color = color; 583 } else { 584 strcpy((char *)err_buf, "background color value is invalid."); 585 free(value.u.s); 586 return false; 587 } 588 free(value.u.s); 589 } 590 value = toml_table_string(table, "underline_style"); 591 if (value.ok) { 592 presence->underline_style = true; 593 line_style = cho_linestyle_parse(value.u.s, &error); 594 if (error) { 595 strcpy((char *)err_buf, "underline style value is invalid."); 596 free(value.u.s); 597 return false; 598 } else { 599 style->underline_style = line_style; 600 } 601 free(value.u.s); 602 } 603 value = toml_table_string(table, "underline_color"); 604 if (value.ok) { 605 presence->underline_color = true; 606 color = cho_color_parse(value.u.s); 607 if (color) { 608 free(style->underline_color); 609 style->underline_color = color; 610 } else { 611 strcpy((char *)err_buf, "underline color value is invalid."); 612 free(value.u.s); 613 return false; 614 } 615 free(value.u.s); 616 } 617 value = toml_table_string(table, "overline_style"); 618 if (value.ok) { 619 presence->overline_style = true; 620 line_style = cho_linestyle_parse(value.u.s, &error); 621 if (error) { 622 strcpy((char *)err_buf, "overline style value is invalid."); 623 free(value.u.s); 624 return false; 625 } else { 626 style->overline_style = line_style; 627 } 628 free(value.u.s); 629 } 630 value = toml_table_string(table, "overline_color"); 631 if (value.ok) { 632 presence->overline_color = true; 633 color = cho_color_parse(value.u.s); 634 if (color) { 635 free(style->overline_color); 636 style->overline_color = color; 637 } else { 638 strcpy((char *)err_buf, "overline color value is invalid."); 639 free(value.u.s); 640 return false; 641 } 642 free(value.u.s); 643 } 644 value = toml_table_bool(table, "strikethrough"); 645 if (value.ok) { 646 presence->strikethrough = true; 647 style->strikethrough = value.u.b; 648 } 649 value = toml_table_string(table, "strikethrough_color"); 650 if (value.ok) { 651 presence->strikethrough_color = true; 652 color = cho_color_parse(value.u.s); 653 if (color) { 654 free(style->strikethrough_color); 655 style->strikethrough_color = color; 656 } else { 657 strcpy((char *)err_buf, "strikethrough color value is invalid."); 658 free(value.u.s); 659 return false; 660 } 661 free(value.u.s); 662 } 663 value = toml_table_bool(table, "boxed"); 664 if (value.ok) { 665 presence->boxed = true; 666 style->boxed = value.u.b; 667 } 668 value = toml_table_string(table, "boxed_color"); 669 if (value.ok) { 670 presence->boxed_color = true; 671 color = cho_color_parse(value.u.s); 672 if (color) { 673 free(style->boxed_color); 674 style->boxed_color = color; 675 } else { 676 strcpy((char *)err_buf, "boxed color value is invalid."); 677 free(value.u.s); 678 return false; 679 } 680 free(value.u.s); 681 } 682 value = toml_table_double(table, "rise"); 683 if (value.ok) { 684 presence->rise = true; 685 style->rise = value.u.d; 686 } 687 value = toml_table_string(table, "href"); 688 if (value.ok) { 689 presence->href = true; 690 style->href = value.u.s; 691 } 692 return true; 693 } 694 695 #ifdef ENABLE_DEBUG 696 697 static void 698 debug_presence_print(const char *name, struct ChoStylePresence *presence) 699 { 700 printf("---- BEGIN PRESENCE ----\n"); 701 printf("style '%s'\n", name); 702 printf("font.family %d\n", presence->font.family); 703 printf("font.style %d\n", presence->font.style); 704 printf("font.weight %d\n", presence->font.weight); 705 printf("font.size %d\n", presence->font.size); 706 printf("foreground_color %d\n", presence->foreground_color); 707 printf("background_color %d\n", presence->background_color); 708 printf("underline_style %d\n", presence->underline_style); 709 printf("underline_color %d\n", presence->underline_color); 710 printf("overline_style %d\n", presence->overline_style); 711 printf("overline_color %d\n", presence->overline_color); 712 printf("strikethrough %d\n", presence->strikethrough); 713 printf("strikethrough_color %d\n", presence->strikethrough_color); 714 printf("boxed %d\n", presence->boxed); 715 printf("boxed_color %d\n", presence->boxed_color); 716 printf("rise %d\n", presence->rise); 717 printf("href %d\n", presence->href); 718 printf("---- END PRESENCE ------\n"); 719 } 720 721 #endif /* ENABLE_DEBUG */ 722 723 static void 724 set_text_style( 725 struct ChoStylePresence *text_presence, 726 struct ChoStyle *text_style, 727 struct ChoStylePresence *presence, 728 struct ChoStyle *style 729 ) 730 { 731 732 if (!presence->font.family && text_presence->font.family) { 733 free(style->font->family); 734 style->font->family = strdup(text_style->font->family); 735 } 736 if (!presence->font.style && text_presence->font.style) { 737 style->font->style = text_style->font->style; 738 } 739 if (!presence->font.weight && text_presence->font.weight) { 740 style->font->weight = text_style->font->weight; 741 } 742 if (!presence->font.size && text_presence->font.size) { 743 style->font->size = text_style->font->size; 744 } 745 if (!presence->foreground_color && text_presence->foreground_color) { 746 free(style->foreground_color); 747 style->foreground_color = cho_color_copy(text_style->foreground_color); 748 } 749 if (!presence->background_color && text_presence->background_color) { 750 free(style->background_color); 751 style->background_color = cho_color_copy(text_style->background_color); 752 } 753 if (!presence->underline_style && text_presence->underline_style) { 754 style->underline_style = text_style->underline_style; 755 } 756 if (!presence->underline_color && text_presence->underline_color) { 757 free(style->underline_color); 758 style->underline_color = cho_color_copy(text_style->underline_color); 759 } 760 if (!presence->overline_style && text_presence->overline_style) { 761 style->overline_style = text_style->overline_style; 762 } 763 if (!presence->overline_color && text_presence->overline_color) { 764 free(style->overline_color); 765 style->overline_color = cho_color_copy(text_style->overline_color); 766 } 767 if (!presence->strikethrough && text_presence->strikethrough) { 768 style->strikethrough = text_style->strikethrough; 769 } 770 if (!presence->strikethrough_color && text_presence->strikethrough_color) { 771 free(style->strikethrough_color); 772 style->strikethrough_color = cho_color_copy(text_style->strikethrough_color); 773 } 774 if (!presence->boxed && text_presence->boxed) { 775 style->boxed = text_style->boxed; 776 } 777 if (!presence->boxed_color && text_presence->boxed_color) { 778 free(style->boxed_color); 779 style->boxed_color = cho_color_copy(text_style->boxed_color); 780 } 781 if (!presence->rise && text_presence->rise) { 782 style->rise = text_style->rise; 783 } 784 if (!presence->href && text_presence->href) { 785 free(style->href); 786 style->href = strdup(text_style->href); 787 } 788 } 789 790 static void 791 lyrics_set_text_style_as_default( 792 struct ChoStylePresence presences[], 793 struct ChoStyle **styles 794 ) 795 { 796 struct ChoStyle *style, *text_style; 797 struct ChoStylePresence *presence, *text_presence; 798 enum TextType lyric_types[] = { 799 TEXT_TYPE_CHORUS, TEXT_TYPE_COMMENT, 800 TEXT_TYPE_COMMENT_ITALIC, TEXT_TYPE_COMMENT_BOX 801 }; 802 text_presence = &presences[TEXT_TYPE_TEXT]; 803 text_style = styles[TEXT_TYPE_TEXT]; 804 size_t i; 805 for (i = 0; i<LENGTH(lyric_types); i++) { 806 presence = &presences[lyric_types[i]]; 807 style = styles[lyric_types[i]]; 808 set_text_style(text_presence, text_style, presence, style); 809 } 810 } 811 812 struct Config * 813 config_load_default(void) 814 { 815 struct Config *config = emalloc(sizeof(struct Config)); 816 config->metadata_separator = strdup("; "); 817 config->verbose_logging = false; 818 819 config->parser = emalloc(sizeof(struct ConfigParser)); 820 config->parser->chords = emalloc(sizeof(struct ConfigChords)); 821 config->parser->chords->notation_system = NOTATION_SYSTEM_COMMON; 822 config->parser->chords->mode = PARSE_MODE_STRICT; 823 config->parser->notes = config_notes_new_default(NOTATION_SYSTEM_COMMON); 824 825 config->output = emalloc(sizeof(struct ConfigOutput)); 826 config->output->notation_system = NOTATION_SYSTEM_COMMON; 827 config->output->start_song_on_new_page = true; 828 config->output->notes = config_notes_new_default(NOTATION_SYSTEM_COMMON); 829 config->output->chorus = emalloc(sizeof(struct ConfigChorus)); 830 config->output->chorus->label = strdup("Chorus"); 831 config->output->chorus->quote = false; 832 config->output->diagram = emalloc(sizeof(struct ChordDiagram)); 833 config->output->diagram->show = true; 834 config->output->diagram->instrument = INSTRUMENT_GUITAR; 835 config->output->page_no = emalloc(sizeof(struct ConfigPageNo)); 836 config->output->page_no->show = true; 837 config->output->page_no->align = ALIGNMENT_CENTER; 838 config->output->toc = emalloc(sizeof(struct ConfigToc)); 839 config->output->toc->show = false; 840 config->output->toc->title = strdup("Table Of Contents"); 841 842 config->output->styles[TEXT_TYPE_CHORD] = cho_style_new(); 843 config->output->styles[TEXT_TYPE_CHORD]->font->family = strdup(DEFAULT_FONT); 844 config->output->styles[TEXT_TYPE_CHORD]->font->weight = FONT_WEIGHT_BOLD; 845 config->output->styles[TEXT_TYPE_ANNOT] = cho_style_new(); 846 config->output->styles[TEXT_TYPE_ANNOT]->font->family = strdup(DEFAULT_FONT); 847 config->output->styles[TEXT_TYPE_ANNOT]->font->style = FONT_STYLE_ITALIC; 848 config->output->styles[TEXT_TYPE_CHORUS] = cho_style_new(); 849 config->output->styles[TEXT_TYPE_CHORUS]->font->family = strdup(DEFAULT_FONT); 850 config->output->styles[TEXT_TYPE_FOOTER] = cho_style_new(); 851 config->output->styles[TEXT_TYPE_FOOTER]->font->family = strdup(DEFAULT_FONT); 852 config->output->styles[TEXT_TYPE_GRID] = cho_style_new(); 853 config->output->styles[TEXT_TYPE_GRID]->font->family = strdup(DEFAULT_FONT); 854 config->output->styles[TEXT_TYPE_GRID]->font->weight = FONT_WEIGHT_BOLD; 855 config->output->styles[TEXT_TYPE_TAB] = cho_style_new(); 856 config->output->styles[TEXT_TYPE_TAB]->font->family = strdup("Courier"); 857 // config->output->styles[TEXT_TYPE_TAB]->font->family = FONT_FAMILY_MONOSPACE; 858 config->output->styles[TEXT_TYPE_TOC] = cho_style_new(); 859 config->output->styles[TEXT_TYPE_TOC]->font->family = strdup(DEFAULT_FONT); 860 config->output->styles[TEXT_TYPE_TOC]->font->size = 12.0; 861 config->output->styles[TEXT_TYPE_TOC_TITLE] = cho_style_new(); 862 config->output->styles[TEXT_TYPE_TOC_TITLE]->font->family = strdup(DEFAULT_FONT); 863 config->output->styles[TEXT_TYPE_TOC_TITLE]->font->weight = FONT_WEIGHT_BOLD; 864 config->output->styles[TEXT_TYPE_TOC_TITLE]->font->size = 18.0; 865 config->output->styles[TEXT_TYPE_TEXT] = cho_style_new(); 866 config->output->styles[TEXT_TYPE_TEXT]->font->family = strdup(DEFAULT_FONT); 867 config->output->styles[TEXT_TYPE_TITLE] = cho_style_new(); 868 config->output->styles[TEXT_TYPE_TITLE]->font->family = strdup(DEFAULT_FONT); 869 config->output->styles[TEXT_TYPE_TITLE]->font->weight = FONT_WEIGHT_BOLD; 870 config->output->styles[TEXT_TYPE_TITLE]->font->size = 18.0; 871 config->output->styles[TEXT_TYPE_SUBTITLE] = cho_style_new(); 872 config->output->styles[TEXT_TYPE_SUBTITLE]->font->family = strdup(DEFAULT_FONT); 873 config->output->styles[TEXT_TYPE_SUBTITLE]->font->size = 12.0; 874 config->output->styles[TEXT_TYPE_LABEL] = cho_style_new(); 875 config->output->styles[TEXT_TYPE_LABEL]->font->family = strdup(DEFAULT_FONT); 876 config->output->styles[TEXT_TYPE_LABEL]->font->style = FONT_STYLE_ITALIC; 877 config->output->styles[TEXT_TYPE_COMMENT] = cho_style_new(); 878 config->output->styles[TEXT_TYPE_COMMENT]->font->family = strdup(DEFAULT_FONT); 879 config->output->styles[TEXT_TYPE_COMMENT]->background_color->red = 228; 880 config->output->styles[TEXT_TYPE_COMMENT]->background_color->green = 228; 881 config->output->styles[TEXT_TYPE_COMMENT]->background_color->blue = 228; 882 config->output->styles[TEXT_TYPE_COMMENT_ITALIC] = cho_style_new(); 883 config->output->styles[TEXT_TYPE_COMMENT_ITALIC]->font->family = strdup(DEFAULT_FONT); 884 config->output->styles[TEXT_TYPE_COMMENT_ITALIC]->font->style = FONT_STYLE_ITALIC; 885 config->output->styles[TEXT_TYPE_COMMENT_BOX] = cho_style_new(); 886 config->output->styles[TEXT_TYPE_COMMENT_BOX]->font->family = strdup(DEFAULT_FONT); 887 config->output->styles[TEXT_TYPE_COMMENT_BOX]->boxed = true; 888 return config; 889 } 890 891 static struct Config * 892 config_load(toml_table_t *toml, const char *filepath) 893 { 894 bool error; 895 struct Config *config; 896 struct ConfigContext ctx; 897 toml_value_t value; 898 toml_table_t *output, *parser; 899 900 ctx.config_filepath = filepath ? strdup(filepath) : NULL; 901 config = config_load_default(); 902 value = toml_table_string(toml, "metadata_separator"); 903 if (value.ok) { 904 free(config->metadata_separator); 905 config->metadata_separator = value.u.s; 906 } 907 value = toml_table_bool(toml, "verbose_logging"); 908 if (value.ok) { 909 config->verbose_logging = value.u.b; 910 } 911 output = toml_table_table(toml, "output"); 912 if (output) { 913 toml_table_t *styles, *notes, *chorus, *diagram, *toc, *page_no; 914 enum NotationSystem notation_system; 915 enum Instrument instrument; 916 enum Alignment align; 917 struct Note **custom_notes; 918 chorus = toml_table_table(output, "chorus"); 919 if (chorus) { 920 value = toml_table_string(chorus, "label"); 921 if (value.ok) { 922 free(config->output->chorus->label); 923 config->output->chorus->label = value.u.s; 924 } 925 value = toml_table_bool(chorus, "quote"); 926 if (value.ok) { 927 config->output->chorus->quote = value.u.b; 928 } 929 } 930 toc = toml_table_table(output, "toc"); 931 if (toc) { 932 value = toml_table_bool(toc, "show"); 933 if (value.ok) { 934 config->output->toc->show = value.u.b; 935 } 936 value = toml_table_string(toc, "title"); 937 if (value.ok) { 938 free(config->output->toc->title); 939 config->output->toc->title = value.u.s; 940 } 941 } 942 diagram = toml_table_table(output, "chord_diagram"); 943 if (diagram) { 944 value = toml_table_bool(diagram, "show"); 945 if (value.ok) { 946 config->output->diagram->show = value.u.b; 947 } 948 value = toml_table_string(diagram, "instrument"); 949 if (value.ok) { 950 instrument = config_instrument_parse(value.u.s, &error); 951 if (error) { 952 DEBUG("config_instrument_parse failed."); 953 config_log(&ctx, LOG_ERR, "[output.chord_diagram]", "Unknown instrument '%s'.", value.u.s); 954 free(value.u.s); 955 goto ERR; 956 } 957 config->output->diagram->instrument = instrument; 958 free(value.u.s); 959 } 960 } 961 value = toml_table_string(output, "notation_system"); 962 if (value.ok) { 963 notation_system = config_notation_system_parse(value.u.s); 964 if (notation_system == NOTATION_SYSTEM_CUSTOM) { 965 notes = toml_table_table(toml, "notation_systems"); 966 if (!notes) { 967 config_log(&ctx, LOG_ERR, "[output]", "Custom notation system '%s' has no corresponding definition in [notation_systems]"); 968 free(value.u.s); 969 goto ERR; 970 } 971 custom_notes = config_notes_load(&ctx, notes, value.u.s); 972 if (custom_notes) { 973 config_notes_free(config->output->notes); 974 config->output->notes = custom_notes; 975 } else { 976 DEBUG("config_notes_load failed."); 977 config_log(&ctx, LOG_ERR, "[output]", "Couldn't load custom notation system '%s' from [notation_systems] section.", value.u.s); 978 free(value.u.s); 979 goto ERR; 980 } 981 } else { 982 config_notes_free(config->output->notes); 983 config->output->notes = config_notes_new_default(notation_system); 984 } 985 free(value.u.s); 986 } 987 value = toml_table_bool(output, "start_song_on_new_page"); 988 if (value.ok) { 989 config->output->start_song_on_new_page = value.u.b; 990 } 991 page_no = toml_table_table(output, "page_no"); 992 if (page_no) { 993 value = toml_table_bool(page_no, "show"); 994 if (value.ok) { 995 config->output->page_no->show = value.u.b; 996 } 997 value = toml_table_string(page_no, "alignment"); 998 if (value.ok) { 999 align = config_alignment_parse(value.u.s, &error); 1000 if (error) { 1001 DEBUG("config_alignment_parse failed."); 1002 free(value.u.s); 1003 goto ERR; 1004 } 1005 config->output->page_no->align = align; 1006 free(value.u.s); 1007 } 1008 } 1009 styles = toml_table_table(output, "styles"); 1010 if (styles) { 1011 int i, unused; 1012 const char *key_name; 1013 enum TextType ttype; 1014 struct ChoStyle *style; 1015 struct ChoStylePresence presences[TEXT_TYPE_LENGTH] = {0}; 1016 toml_table_t *key; 1017 1018 for (i = 0; i<toml_table_len(styles); i++) { 1019 key_name = toml_table_key(styles, i, &unused); 1020 ttype = config_text_type_parse(key_name, &error); 1021 if (!error) { 1022 key = toml_table_table(styles, key_name); 1023 if (key) { 1024 style = config->output->styles[ttype]; 1025 size_t size = 16 + strlen(key_name) + 1; 1026 char toml_section_name[size]; 1027 char err[38]; 1028 snprintf((char *)&toml_section_name, size, "[output.styles.%s]", key_name); 1029 if (!config_load_style(style, key, &presences[ttype], &err)) { 1030 DEBUG("config_load_style failed."); 1031 config_log(&ctx, LOG_ERR, toml_section_name, err); 1032 goto ERR; 1033 } 1034 } 1035 } 1036 } 1037 lyrics_set_text_style_as_default(presences, config->output->styles); 1038 } 1039 } 1040 parser = toml_table_table(toml, "parser"); 1041 if (parser) { 1042 toml_table_t *chords = toml_table_table(parser, "chords"); 1043 if (chords) { 1044 toml_table_t *notes; 1045 enum NotationSystem notation_system; 1046 struct Note **custom_notes; 1047 1048 value = toml_table_string(chords, "notation_system"); 1049 if (value.ok) { 1050 notation_system = config_notation_system_parse(value.u.s); 1051 if (notation_system == NOTATION_SYSTEM_CUSTOM) { 1052 notes = toml_table_table(toml, "notation_systems"); 1053 if (!notes) { 1054 config_log(&ctx, LOG_ERR, "[parser.chords]", "Custom notation system '%s' has no corresponding definition in [notation_systems].", value.u.s); 1055 free(value.u.s); 1056 goto ERR; 1057 } 1058 custom_notes = config_notes_load(&ctx, notes, value.u.s); 1059 if (custom_notes) { 1060 config_notes_free(config->parser->notes); 1061 config->parser->notes = custom_notes; 1062 } else { 1063 DEBUG("config_notes_load failed."); 1064 config_log(&ctx, LOG_ERR, "[parser.chords]", "Couldn't load custom notation system '%s' from [notation_systems] section.", value.u.s); 1065 free(value.u.s); 1066 goto ERR; 1067 } 1068 } else { 1069 config_notes_free(config->parser->notes); 1070 config->parser->notes = config_notes_new_default(notation_system); 1071 } 1072 free(value.u.s); 1073 } 1074 value = toml_table_string(chords, "mode"); 1075 if (value.ok) { 1076 if (!strcmp(value.u.s, "strict")) { 1077 config->parser->chords->mode = PARSE_MODE_STRICT; 1078 } else if (!strcmp(value.u.s, "relaxed")) { 1079 config->parser->chords->mode = PARSE_MODE_RELAXED; 1080 } 1081 free(value.u.s); 1082 } 1083 } 1084 } 1085 free(ctx.config_filepath); 1086 return config; 1087 ERR: 1088 free(ctx.config_filepath); 1089 config_free(config); 1090 return NULL; 1091 } 1092 1093 struct Config * 1094 config_load_from_file(const char *filepath) 1095 { 1096 struct Config *config = NULL; 1097 toml_table_t *table = NULL; 1098 FILE *fp = NULL; 1099 1100 fp = fopen(filepath, "r"); 1101 if (!fp) { 1102 DEBUG("fopen failed."); 1103 util_log(NULL, 0, LOG_ERR, "Cannot open file '%s': %s", filepath, strerror(errno)); 1104 return NULL; 1105 } 1106 char errbuf[200]; 1107 table = toml_parse_file(fp, (char *)&errbuf, sizeof(errbuf)); 1108 if (!table) { 1109 DEBUG("toml_parse_file failed."); 1110 util_log(NULL, 0, LOG_ERR, "Config file '%s' is not a valid toml file: %s.", filepath, (char *)&errbuf); 1111 fclose(fp); 1112 return NULL; 1113 } 1114 config = config_load(table, filepath); 1115 if (!config) { 1116 DEBUG("config_load failed."); 1117 fclose(fp); 1118 toml_free(table); 1119 return NULL; 1120 } 1121 toml_free(table); 1122 fclose(fp); 1123 return config; 1124 } 1125 1126 struct Config * 1127 config_load_from_data(const char *data) 1128 { 1129 struct Config *config = NULL; 1130 toml_table_t *table = NULL; 1131 char errbuf[200]; 1132 1133 table = toml_parse((char *)data, (char *)&errbuf, sizeof(errbuf)); 1134 if (!table) { 1135 DEBUG("toml_parse failed."); 1136 util_log(NULL, 0, LOG_ERR, "Config data is not valid toml: %s.", (char *)&errbuf); 1137 return NULL; 1138 } 1139 config = config_load(table, NULL); 1140 if (!config) { 1141 DEBUG("config_load failed."); 1142 } 1143 toml_free(table); 1144 return config; 1145 } 1146 1147 void 1148 config_free(struct Config *config) 1149 { 1150 if (!config) { 1151 return; 1152 } 1153 int i; 1154 1155 free(config->metadata_separator); 1156 free(config->output->toc->title); 1157 free(config->output->toc); 1158 free(config->output->chorus->label); 1159 free(config->output->chorus); 1160 for (i = 0; i<TEXT_TYPE_LENGTH; i++) { 1161 cho_style_free(config->output->styles[i]); 1162 } 1163 config_notes_free(config->output->notes); 1164 free(config->output->diagram); 1165 free(config->output->page_no); 1166 free(config->output); 1167 free(config->parser->chords); 1168 config_notes_free(config->parser->notes); 1169 free(config->parser); 1170 free(config); 1171 }