chordpro.c (195581B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <stdbool.h> 4 #include <stdarg.h> 5 #include <unistd.h> 6 #include <string.h> 7 #include <strings.h> 8 #include <ctype.h> 9 #include <errno.h> 10 #include <limits.h> 11 #include <time.h> 12 #include "core.h" 13 #include "chordpro.h" 14 #include "chord_diagram.h" 15 #include "config.h" 16 17 static const char *font_styles[] = { "normal", "oblique", "italic" }; 18 static const char *font_weights[] = { "normal", "bold" }; 19 static const char *line_styles[] = { "single", "double", "none" }; 20 static const char *chord_qualifiers[] = { "m", "", "+", "°" }; 21 static const char *section_types[] = { "", "", "chorus", "verse", "bridge", "tab", "grid", "custom" }; 22 extern const struct InstrumentInfo instruments[]; 23 24 static const char *state_enums[] = { 25 "STATE_LYRICS", 26 "STATE_BACKSLASH", 27 "STATE_DIRECTIVE_NAME", 28 "STATE_DIRECTIVE_VALUE", 29 "STATE_CHORD", 30 "STATE_ANNOTATION", 31 "STATE_TAB", 32 "STATE_GRID", 33 "STATE_MARKUP_TAG", 34 "STATE_MARKUP_TAG_START", 35 "STATE_MARKUP_TAG_END", 36 "STATE_MARKUP_ATTR_NAME", 37 "STATE_MARKUP_ATTR_VALUE", 38 "STATE_COMMENT", 39 "STATE_MAYBE_METADATA_SUBSTITUTION", 40 "STATE_METADATA_SUBSTITUTION" 41 }; 42 43 struct StyleProperty default_style_properties[] = { 44 // TODO: footer are missing 45 { TEXT_TYPE_CHORD, STYLE_PROPERTY_TYPE_FONT, { .font_name = NULL } }, 46 { TEXT_TYPE_CHORD, STYLE_PROPERTY_TYPE_SIZE, { .font_size = EMPTY_DOUBLE } }, 47 { TEXT_TYPE_CHORD, STYLE_PROPERTY_TYPE_COLOR, { .foreground_color = NULL } }, 48 { TEXT_TYPE_CHORUS, STYLE_PROPERTY_TYPE_FONT, { .font_name = NULL } }, 49 { TEXT_TYPE_CHORUS, STYLE_PROPERTY_TYPE_SIZE, { .font_size = EMPTY_DOUBLE } }, 50 { TEXT_TYPE_CHORUS, STYLE_PROPERTY_TYPE_COLOR, { .foreground_color = NULL } }, 51 { TEXT_TYPE_GRID, STYLE_PROPERTY_TYPE_FONT, { .font_name = NULL } }, 52 { TEXT_TYPE_GRID, STYLE_PROPERTY_TYPE_SIZE, { .font_size = EMPTY_DOUBLE } }, 53 { TEXT_TYPE_GRID, STYLE_PROPERTY_TYPE_COLOR, { .foreground_color = NULL } }, 54 { TEXT_TYPE_TAB, STYLE_PROPERTY_TYPE_FONT, { .font_name = NULL } }, 55 { TEXT_TYPE_TAB, STYLE_PROPERTY_TYPE_SIZE, { .font_size = EMPTY_DOUBLE } }, 56 { TEXT_TYPE_TAB, STYLE_PROPERTY_TYPE_COLOR, { .foreground_color = NULL } }, 57 { TEXT_TYPE_TEXT, STYLE_PROPERTY_TYPE_FONT, { .font_name = NULL } }, 58 { TEXT_TYPE_TEXT, STYLE_PROPERTY_TYPE_SIZE, { .font_size = EMPTY_DOUBLE } }, 59 { TEXT_TYPE_TEXT, STYLE_PROPERTY_TYPE_COLOR, { .foreground_color = NULL } }, 60 { TEXT_TYPE_TITLE, STYLE_PROPERTY_TYPE_FONT, { .font_name = NULL } }, 61 { TEXT_TYPE_TITLE, STYLE_PROPERTY_TYPE_SIZE, { .font_size = EMPTY_DOUBLE } }, 62 { TEXT_TYPE_TITLE, STYLE_PROPERTY_TYPE_COLOR, { .foreground_color = NULL } }, 63 { TEXT_TYPE_TOC, STYLE_PROPERTY_TYPE_FONT, { .font_name = NULL } }, 64 { TEXT_TYPE_TOC, STYLE_PROPERTY_TYPE_SIZE, { .font_size = EMPTY_DOUBLE } }, 65 { TEXT_TYPE_TOC, STYLE_PROPERTY_TYPE_COLOR, { .foreground_color = NULL } }, 66 { TEXT_TYPE_LABEL, STYLE_PROPERTY_TYPE_FONT, { .font_name = NULL } }, 67 { TEXT_TYPE_LABEL, STYLE_PROPERTY_TYPE_SIZE, { .font_size = EMPTY_DOUBLE } }, 68 { TEXT_TYPE_LABEL, STYLE_PROPERTY_TYPE_COLOR, { .foreground_color = NULL } }, 69 }; 70 71 static const char *chord_extensions_major[] = { 72 "2", "3", "4", "5", "6", "69", "7", "7-5", 73 "7#5", "7#9", "7#9#5", "7#9b5", "7#9#11", 74 "7b5", "7b9", "7b9#5", "7b9#9", "7b9#11", "7b9b13", "7b9b5", "7b9sus", "7b13", "7b13sus", 75 "7-9", "7-9#11", "7-9#5", "7-9#9", "7-9-13", "7-9-5", "7-9sus", 76 "711", 77 "7#11", 78 "7-13", "7-13sus", 79 "7sus", "7susadd3", 80 "7+", 81 "7alt", 82 "9", 83 "9+", 84 "9#5", 85 "9b5", 86 "9-5", 87 "9sus", 88 "9add6", 89 "maj7", "maj711", "maj7#11", "maj13", "maj7#5", "maj7sus2", "maj7sus4", 90 "^7", "^711", "^7#11", "^7#5", "^7sus2", "^7sus4", 91 "maj9", "maj911", 92 "^9", "^911", 93 "^13", 94 "^9#11", 95 "11", 96 "911", 97 "9#11", 98 "13", 99 "13#11", 100 "13#9", 101 "13b9", 102 "alt", 103 "add2", "add4", "add9", 104 "sus2", "sus4", "sus9", 105 "6sus2", "6sus4", 106 "7sus2", "7sus4", 107 "13sus2", "13sus4", 108 NULL 109 }; 110 111 static const char *chord_extensions_minor[] = { 112 // INFO: https://www.chordpro.org/beta/chordpro-chords/#extensions-for-minor-chords 113 "#5", 114 "#5", 115 "m11", 116 "-11", 117 "m6", 118 "-6", 119 "m69", 120 "-69", 121 "m7b5", 122 "-7b5", 123 "m7-5", 124 "-7-5", 125 "mmaj7", 126 "-maj7", 127 "mmaj9", 128 "-maj9", 129 "m9maj7", 130 "-9maj7", 131 "m9^7", 132 "-9^7", 133 "madd9", 134 "-add9", 135 "mb6", 136 "-b6", 137 "m#7", 138 "-#7", 139 "msus4", "msus9", 140 "-sus4", "-sus9", 141 "m7sus4", 142 "-7sus4", 143 // INFO: custom 144 "m7", 145 "m9", 146 "m", 147 NULL 148 }; 149 150 static bool g_show_info_logs = false; 151 152 #ifdef ENABLE_DEBUG 153 154 static const char *alignment_enums[] = { 155 "ALIGNMENT_LEFT", 156 "ALIGNMENT_CENTER", 157 "ALIGNMENT_RIGHT" 158 }; 159 160 static const char *anchor_enums[] = { 161 "ANCHOR_PAPER", 162 "ANCHOR_PAGE", 163 "ANCHOR_COLUMN", 164 "ANCHOR_LINE", 165 "ANCHOR_FLOAT" 166 }; 167 168 static const char *chord_qualifier_enums[] = { 169 "CHORD_QUALIFIER_MIN", 170 "CHORD_QUALIFIER_MAJ", 171 "CHORD_QUALIFIER_AUG", 172 "CHORD_QUALIFIER_DIM" 173 }; 174 175 static const char *directive_type_enums[] = { 176 "DIRECTIVE_TYPE_ENVIRONMENT", 177 "DIRECTIVE_TYPE_METADATA", 178 "DIRECTIVE_TYPE_FORMATTING", 179 "DIRECTIVE_TYPE_IMAGE", 180 "DIRECTIVE_TYPE_PREAMBLE", 181 "DIRECTIVE_TYPE_FONT", 182 "DIRECTIVE_TYPE_CHORD", 183 "DIRECTIVE_TYPE_OUTPUT", 184 "DIRECTIVE_TYPE_EXTENSION", 185 "DIRECTIVE_TYPE_CUSTOM" 186 }; 187 188 static const char *section_type_enums[] = { 189 "SECTION_TYPE_UNINITIALIZED", 190 "SECTION_TYPE_NEWSONG", 191 "SECTION_TYPE_CHORUS", 192 "SECTION_TYPE_VERSE", 193 "SECTION_TYPE_BRIDGE", 194 "SECTION_TYPE_TAB", 195 "SECTION_TYPE_GRID", 196 "SECTION_TYPE_CUSTOM" 197 }; 198 199 static const char *position_enums[] = { 200 "POSITION_START", 201 "POSITION_END", 202 "POSITION_NO" 203 }; 204 205 static const char *text_type_enums[] = { 206 "TEXT_TYPE_CHORD", 207 "TEXT_TYPE_ANNOT", 208 "TEXT_TYPE_CHORUS", 209 "TEXT_TYPE_FOOTER", 210 "TEXT_TYPE_GRID", 211 "TEXT_TYPE_TAB", 212 "TEXT_TYPE_TOC", 213 "TEXT_TYPE_TOC_TITLE", 214 "TEXT_TYPE_TEXT", 215 "TEXT_TYPE_TITLE", 216 "TEXT_TYPE_SUBTITLE", 217 "TEXT_TYPE_LABEL", 218 "TEXT_TYPE_COMMENT", 219 "TEXT_TYPE_COMMENT_ITALIC", 220 "TEXT_TYPE_COMMENT_BOX" 221 }; 222 223 static const char *style_property_type_enums[] = { 224 "STYLE_PROPERTY_TYPE_FONT", 225 "STYLE_PROPERTY_TYPE_SIZE", 226 "STYLE_PROPERTY_TYPE_COLOR" 227 }; 228 229 void 230 cho_debug_chord_print(struct ChoChord *chord) 231 { 232 printf("---- BEGIN CHORD ----\n"); 233 printf("is_canonical: %d\n", chord->is_canonical); 234 printf("name: '%s'\n", chord->name); 235 if (chord->root) { 236 printf("root: '%s'\n", chord->root); 237 } else { 238 printf("root: 'NULL'\n"); 239 } 240 printf("qual: '%s'\n", chord_qualifier_enums[chord->qual]); 241 if (chord->ext) { 242 printf("ext: '%s'\n", chord->ext); 243 } else { 244 printf("ext: 'NULL'\n"); 245 } 246 if (chord->bass) { 247 printf("bass: '%s'\n", chord->bass); 248 } else { 249 printf("bass: 'NULL'\n"); 250 } 251 printf("---- END CHORD ------\n"); 252 } 253 254 #endif /* ENABLE_DEBUG */ 255 256 void 257 cho_log_enable_info_logs(void) 258 { 259 g_show_info_logs = true; 260 } 261 262 static void 263 cho_log(struct ChoContext *ctx, enum LogLevel level, const char *msg, ...) 264 { 265 va_list va; 266 267 va_start(va, msg); 268 util_vlog(ctx->chordpro_filepath, ctx->line_no, level, msg, va); 269 } 270 271 static inline bool 272 is_whitespace(char c) 273 { 274 if (c == '\t' || c == ' ') { 275 return true; 276 } 277 return false; 278 } 279 280 struct RGBColor * 281 cho_rgbcolor_new(uint8_t red, uint8_t green, uint8_t blue) 282 { 283 struct RGBColor *color = emalloc(sizeof(struct RGBColor)); 284 color->red = red; 285 color->green = green; 286 color->blue = blue; 287 return color; 288 } 289 290 static struct RGBColor * 291 cho_rgbcolor_copy(struct RGBColor *color) 292 { 293 struct RGBColor *copy = emalloc(sizeof(struct RGBColor)); 294 copy->red = color->red; 295 copy->green = color->green; 296 copy->blue = color->blue; 297 return copy; 298 } 299 300 static const char * 301 cho_rgbcolor_to_string(struct RGBColor *color) 302 { 303 constexpr int str_len = 8; 304 static char str[str_len]; 305 snprintf((char *)&str, str_len, "#%02X%02X%02X", color->red, color->green, color->blue); 306 return (const char *)&str; 307 } 308 309 static struct RGBColor * 310 cho_rgbcolor_parse(const char *str) 311 { 312 struct RGBColor *color = emalloc(sizeof(struct RGBColor)); 313 size_t len = strlen(str); 314 char tmp[3]; 315 long primary_color; 316 if (len == 7) { 317 tmp[2] = 0; 318 if (str[1] == '0' && str[2] == '0') { 319 color->red = 0; 320 } else { 321 tmp[0] = str[1]; 322 tmp[1] = str[2]; 323 primary_color = strtol((char *)&tmp, NULL, 16); 324 if (primary_color == 0) { 325 goto ERR; 326 } else { 327 color->red = primary_color; 328 } 329 } 330 if (str[3] == '0' && str[4] == '0') { 331 color->green = 0; 332 } else { 333 tmp[0] = str[3]; 334 tmp[1] = str[4]; 335 primary_color = strtol((char *)&tmp, NULL, 16); 336 if (primary_color == 0) { 337 goto ERR; 338 } else { 339 color->green = primary_color; 340 } 341 } 342 if (str[5] == '0' && str[6] == '0') { 343 color->blue = 0; 344 } else { 345 tmp[0] = str[5]; 346 tmp[1] = str[6]; 347 primary_color = strtol((char *)&tmp, NULL, 16); 348 if (primary_color == 0) { 349 goto ERR; 350 } else { 351 color->blue = primary_color; 352 } 353 } 354 } else if (len == 4) { 355 tmp[2] = 0; 356 if (str[1] == '0') { 357 color->red = 0; 358 } else { 359 tmp[0] = str[1]; 360 tmp[1] = str[1]; 361 primary_color = strtol((char *)&tmp, NULL, 16); 362 if (primary_color == 0) { 363 goto ERR; 364 } else { 365 color->red = primary_color; 366 } 367 } 368 if (str[2] == '0') { 369 color->green = 0; 370 } else { 371 tmp[0] = str[2]; 372 tmp[1] = str[2]; 373 primary_color = strtol((char *)&tmp, NULL, 16); 374 if (primary_color == 0) { 375 goto ERR; 376 } else { 377 color->green = primary_color; 378 } 379 } 380 if (str[3] == '0') { 381 color->blue = 0; 382 } else { 383 tmp[0] = str[3]; 384 tmp[1] = str[3]; 385 primary_color = strtol((char *)&tmp, NULL, 16); 386 if (primary_color == 0) { 387 goto ERR; 388 } else { 389 color->blue = primary_color; 390 } 391 } 392 } else { 393 goto ERR; 394 } 395 return color; 396 ERR: 397 free(color); 398 return NULL; 399 } 400 401 struct RGBColor * 402 cho_color_parse(const char *str) 403 { 404 struct RGBColor *color; 405 406 if (str[0] == '#') { 407 color = cho_rgbcolor_parse(str); 408 if (!color) { 409 DEBUG("cho_rgbcolor_parse failed."); 410 return NULL; 411 } 412 } else { 413 color = emalloc(sizeof(struct RGBColor)); 414 if (!strcmp(str, "red")) { 415 color->red = 255; 416 color->green = 48; 417 color->blue = 48; 418 } else if (!strcmp(str, "green")) { 419 color->red = 0; 420 color->green = 126; 421 color->blue = 0; 422 } else if (!strcmp(str, "blue")) { 423 color->red = 0; 424 color->green = 0; 425 color->blue = 255; 426 } else if (!strcmp(str, "yellow")) { 427 color->red = 255; 428 color->green = 255; 429 color->blue = 0; 430 } else if (!strcmp(str, "magenta")) { 431 color->red = 255; 432 color->green = 48; 433 color->blue = 255; 434 } else if (!strcmp(str, "cyan")) { 435 color->red = 82; 436 color->green = 255; 437 color->blue = 255; 438 } else if (!strcmp(str, "white")) { 439 color->red = 255; 440 color->green = 255; 441 color->blue = 255; 442 } else if (!strcmp(str, "grey")) { 443 color->red = 191; 444 color->green = 191; 445 color->blue = 191; 446 } else if (!strcmp(str, "black")) { 447 color->red = 0; 448 color->green = 0; 449 color->blue = 0; 450 } else { 451 free(color); 452 return NULL; 453 } 454 } 455 return color; 456 } 457 458 inline struct RGBColor * 459 cho_color_copy(struct RGBColor *color) 460 { 461 return cho_rgbcolor_copy(color); 462 } 463 464 static struct Font * 465 cho_font_new(void) 466 { 467 struct Font *font = emalloc(sizeof(struct Font)); 468 font->family = NULL; 469 font->style = FONT_STYLE_ROMAN; 470 font->weight = FONT_WEIGHT_REGULAR; 471 font->size = DEFAULT_FONT_SIZE; 472 return font; 473 } 474 475 struct Font * 476 cho_font_copy(struct Font *font) 477 { 478 struct Font *copy = emalloc(sizeof(struct Font)); 479 copy->family = font->family ? strdup(font->family) : NULL; 480 copy->style = font->style; 481 copy->weight = font->weight; 482 copy->size = font->size; 483 return copy; 484 } 485 486 void 487 cho_font_free(struct Font *font) 488 { 489 if (!font) { 490 return; 491 } 492 free(font->family); 493 free(font); 494 } 495 496 void 497 cho_fonts_free(struct Font **fonts) 498 { 499 if (!fonts) { 500 return; 501 } 502 struct Font **f; 503 for (f = fonts; *f; f++) { 504 cho_font_free(*f); 505 } 506 free(fonts); 507 } 508 509 enum FontStyle 510 cho_font_style_parse(const char *str, bool *error) 511 { 512 *error = false; 513 if (!strcmp(str, font_styles[FONT_STYLE_ITALIC])) { 514 return FONT_STYLE_ITALIC; 515 } else if (!strcmp(str, font_styles[FONT_STYLE_OBLIQUE])) { 516 return FONT_STYLE_OBLIQUE; 517 } else if (!strcmp(str, font_styles[FONT_STYLE_ROMAN])) { 518 return FONT_STYLE_ROMAN; 519 } 520 *error = true; 521 return FONT_STYLE_ITALIC; // unused 522 } 523 524 const char * 525 cho_font_style_to_config_string(enum FontStyle style) 526 { 527 return font_styles[style]; 528 } 529 530 enum FontWeight 531 cho_font_weight_parse(const char *str, bool *error) 532 { 533 *error = false; 534 if (!strcmp(str, font_weights[FONT_WEIGHT_BOLD])) { 535 return FONT_WEIGHT_BOLD; 536 } else if (!strcmp(str, font_weights[FONT_WEIGHT_REGULAR])) { 537 return FONT_WEIGHT_REGULAR; 538 } 539 *error = true; 540 return FONT_WEIGHT_BOLD; // unused 541 } 542 543 const char * 544 cho_font_weight_to_config_string(enum FontWeight weight) 545 { 546 return font_weights[weight]; 547 } 548 549 static void 550 cho_font_print_as_toml(struct Font *font, const char *section) 551 { 552 printf("[output.styles.%s.font]\n", section); 553 printf("family = \"%s\"\n", font->family); 554 printf("style = \"%s\"\n", cho_font_style_to_config_string(font->style)); 555 printf("weight = \"%s\"\n", cho_font_weight_to_config_string(font->weight)); 556 printf("size = %.1f\n\n", font->size); 557 } 558 559 void 560 cho_font_print(struct Font *font) 561 { 562 printf("---- BEGIN FONT ----\n"); 563 if (font->family) { 564 printf("font family: %s\n", font->family); 565 } else { 566 printf("font family: NULL\n"); 567 } 568 printf("font style: %s\n", cho_font_style_to_config_string(font->style)); 569 printf("font weight: %s\n", cho_font_weight_to_config_string(font->weight)); 570 printf("font size: %f\n", font->size); 571 printf("---- END FONT ------\n"); 572 } 573 574 enum LineStyle 575 cho_linestyle_parse(const char *str, bool *error) 576 { 577 *error = false; 578 if (!strcmp(str, line_styles[LINE_STYLE_SINGLE])) { 579 return LINE_STYLE_SINGLE; 580 } else if (!strcmp(str, line_styles[LINE_STYLE_DOUBLE])) { 581 return LINE_STYLE_DOUBLE; 582 } else if (!strcmp(str, line_styles[LINE_STYLE_NONE])) { 583 return LINE_STYLE_NONE; 584 } 585 *error = true; 586 return LINE_STYLE_SINGLE; // unused 587 } 588 589 const char * 590 cho_linestyle_to_config_string(enum LineStyle linestyle) 591 { 592 return line_styles[linestyle]; 593 } 594 595 #ifdef ENABLE_DEBUG 596 597 static void 598 cho_debug_the_default_style_properties(void) 599 { 600 unsigned int i; 601 for (i = 0; i<LENGTH(default_style_properties); i++) { 602 printf( 603 "%s %s ", 604 text_type_enums[default_style_properties[i].ttype], 605 style_property_type_enums[default_style_properties[i].type] 606 ); 607 switch (default_style_properties[i].type) { 608 case STYLE_PROPERTY_TYPE_FONT: 609 if (default_style_properties[i].u.font_name) 610 printf("%s\n", default_style_properties[i].u.font_name); 611 else 612 printf("NULL\n"); 613 break; 614 case STYLE_PROPERTY_TYPE_SIZE: 615 printf("%.1f\n", default_style_properties[i].u.font_size); 616 break; 617 case STYLE_PROPERTY_TYPE_COLOR: 618 if (default_style_properties[i].u.foreground_color) 619 printf("%s\n", cho_rgbcolor_to_string(default_style_properties[i].u.foreground_color)); 620 else 621 printf("NULL\n"); 622 break; 623 default: 624 printf("Invalid StylePropertyType value '%d'.\n", default_style_properties[i].type); 625 } 626 } 627 } 628 629 void 630 cho_debug_style_print(struct ChoStyle *style) 631 { 632 printf("---- BEGIN STYLE ----\n"); 633 cho_font_print(style->font); 634 printf("foreground_color: %s\n", cho_rgbcolor_to_string(style->foreground_color)); 635 printf("background_color: %s\n", cho_rgbcolor_to_string(style->background_color)); 636 printf("underline_style: %s\n", cho_linestyle_to_config_string(style->underline_style)); 637 printf("underline_color: %s\n", cho_rgbcolor_to_string(style->underline_color)); 638 printf("overline_style: %s\n", cho_linestyle_to_config_string(style->overline_style)); 639 printf("overline_color: %s\n", cho_rgbcolor_to_string(style->overline_color)); 640 printf("strikethrough: %d\n", style->strikethrough); 641 printf("strikethrough_color: %s\n", cho_rgbcolor_to_string(style->strikethrough_color)); 642 printf("boxed: %d\n", style->boxed); 643 printf("boxed_color: %s\n", cho_rgbcolor_to_string(style->boxed_color)); 644 printf("rise: %f\n", style->rise); 645 if (style->href) 646 printf("href: %s\n", style->href); 647 else 648 printf("href: NULL\n"); 649 printf("---- END STYLE ------\n\n"); 650 } 651 652 #endif /* ENABLE_DEBUG */ 653 654 static bool 655 cho_style_property_apply_default( 656 enum TextType current_ttype, 657 enum StylePropertyType ptype, 658 struct ChoStyle *style 659 ) 660 { 661 unsigned int i; 662 for (i = 0; i<LENGTH(default_style_properties); i++) { 663 if ( 664 default_style_properties[i].ttype == current_ttype && 665 default_style_properties[i].type == ptype 666 ) { 667 switch (ptype) { 668 case STYLE_PROPERTY_TYPE_FONT: 669 if (default_style_properties[i].u.font_name) { 670 free(style->font->family); 671 style->font->family = strdup(default_style_properties[i].u.font_name); 672 return true; 673 } else { 674 return false; 675 } 676 break; 677 case STYLE_PROPERTY_TYPE_SIZE: 678 if (default_style_properties[i].u.font_size != EMPTY_DOUBLE) { 679 style->font->size = default_style_properties[i].u.font_size; 680 return true; 681 } else { 682 return false; 683 } 684 break; 685 case STYLE_PROPERTY_TYPE_COLOR: 686 if (default_style_properties[i].u.foreground_color) { 687 free(style->foreground_color); 688 style->foreground_color = cho_rgbcolor_copy(default_style_properties[i].u.foreground_color); 689 return true; 690 } else { 691 return false; 692 } 693 break; 694 } 695 } 696 } 697 return false; 698 } 699 700 static void 701 cho_style_apply_default(enum TextType current_ttype, struct ChoStyle *style) 702 { 703 if (current_ttype == TEXT_TYPE_CHORUS) { 704 if (!cho_style_property_apply_default(TEXT_TYPE_CHORUS, STYLE_PROPERTY_TYPE_FONT, style)) { 705 cho_style_property_apply_default(TEXT_TYPE_TEXT, STYLE_PROPERTY_TYPE_FONT, style); 706 } 707 if (!cho_style_property_apply_default(TEXT_TYPE_CHORUS, STYLE_PROPERTY_TYPE_SIZE, style)) { 708 cho_style_property_apply_default(TEXT_TYPE_TEXT, STYLE_PROPERTY_TYPE_SIZE, style); 709 } 710 if (!cho_style_property_apply_default(TEXT_TYPE_CHORUS, STYLE_PROPERTY_TYPE_COLOR, style)) { 711 cho_style_property_apply_default(TEXT_TYPE_TEXT, STYLE_PROPERTY_TYPE_COLOR, style); 712 } 713 } else { 714 cho_style_property_apply_default(current_ttype, STYLE_PROPERTY_TYPE_FONT, style); 715 cho_style_property_apply_default(current_ttype, STYLE_PROPERTY_TYPE_SIZE, style); 716 cho_style_property_apply_default(current_ttype, STYLE_PROPERTY_TYPE_COLOR, style); 717 } 718 } 719 720 struct ChoStyle * 721 cho_style_new(void) 722 { 723 struct ChoStyle *style = emalloc(sizeof(struct ChoStyle)); 724 style->font = cho_font_new(); 725 style->foreground_color = cho_rgbcolor_new(20, 20, 20); 726 style->background_color = cho_rgbcolor_new(255, 255, 255); 727 style->underline_style = LINE_STYLE_NONE; 728 style->underline_color = cho_rgbcolor_new(20, 20, 20); 729 style->overline_style = LINE_STYLE_NONE; 730 style->overline_color = cho_rgbcolor_new(20, 20, 20); 731 style->strikethrough = false; 732 style->strikethrough_color = cho_rgbcolor_new(20, 20, 20); 733 style->boxed = false; 734 style->boxed_color = cho_rgbcolor_new(20, 20, 20); 735 style->rise = 0.0; 736 style->href = NULL; 737 return style; 738 } 739 740 struct ChoStyle * 741 cho_style_copy(struct ChoStyle *style) 742 { 743 struct ChoStyle *copy = emalloc(sizeof(struct ChoStyle)); 744 copy->font = cho_font_copy(style->font); 745 copy->foreground_color = cho_rgbcolor_copy(style->foreground_color); 746 copy->background_color = cho_rgbcolor_copy(style->background_color); 747 copy->underline_style = style->underline_style; 748 copy->underline_color = cho_rgbcolor_copy(style->underline_color); 749 copy->overline_style = style->overline_style; 750 copy->overline_color = cho_rgbcolor_copy(style->overline_color); 751 copy->strikethrough = style->strikethrough; 752 copy->strikethrough_color = cho_rgbcolor_copy(style->strikethrough_color); 753 copy->boxed = style->boxed; 754 copy->boxed_color = cho_rgbcolor_copy(style->boxed_color); 755 copy->rise = style->rise; 756 copy->href = style->href ? strdup(style->href) : NULL; 757 return copy; 758 } 759 760 static void 761 cho_style_complement( 762 struct ChoStyle *old, 763 struct ChoStyle *new, 764 struct ChoStylePresence *presence 765 ) 766 { 767 if (presence->font.family) { 768 free(old->font->family); 769 old->font->family = strdup(new->font->family); 770 } 771 if (presence->font.style) { 772 old->font->style = new->font->style; 773 } 774 if (presence->font.weight) { 775 old->font->weight = new->font->weight; 776 } 777 if (presence->font.size) { 778 old->font->size = new->font->size; 779 } 780 if (presence->foreground_color) { 781 free(old->foreground_color); 782 old->foreground_color = cho_rgbcolor_copy(new->foreground_color); 783 } 784 if (presence->background_color) { 785 free(old->background_color); 786 old->background_color = cho_rgbcolor_copy(new->background_color); 787 } 788 if (presence->underline_style) { 789 old->underline_style = new->underline_style; 790 } 791 if (presence->underline_color) { 792 free(old->underline_color); 793 old->underline_color = cho_rgbcolor_copy(new->underline_color); 794 } 795 if (presence->overline_style) { 796 old->overline_style = new->overline_style; 797 } 798 if (presence->overline_color) { 799 free(old->overline_color); 800 old->overline_color = cho_rgbcolor_copy(new->overline_color); 801 } 802 if (presence->strikethrough) { 803 old->strikethrough = new->strikethrough; 804 } 805 if (presence->strikethrough_color) { 806 free(old->strikethrough_color); 807 old->strikethrough_color = cho_rgbcolor_copy(new->strikethrough_color); 808 } 809 if (presence->boxed) { 810 old->boxed = new->boxed; 811 } 812 if (presence->boxed_color) { 813 free(old->boxed_color); 814 old->boxed_color = cho_rgbcolor_copy(new->boxed_color); 815 } 816 if (presence->rise) { 817 old->rise = new->rise; 818 } 819 if (presence->href) { 820 free(old->href); 821 old->href = strdup(new->href); 822 } 823 } 824 825 static struct ChoStyle * 826 cho_style_new_from_config(struct ChoContext *ctx, enum TextType ttype) 827 { 828 return cho_style_copy(ctx->config->output->styles[ttype]); 829 } 830 831 static struct ChoStyle * 832 cho_style_new_default(struct ChoContext *ctx) 833 { 834 struct ChoStyle *style = cho_style_new_from_config(ctx, ctx->current_ttype); 835 if (!style) { 836 DEBUG("cho_style_new_from_config failed."); 837 return NULL; 838 } 839 cho_style_apply_default(ctx->current_ttype, style); 840 return style; 841 } 842 843 void 844 cho_style_free(struct ChoStyle *style) 845 { 846 if (!style) { 847 return; 848 } 849 cho_font_free(style->font); 850 free(style->foreground_color); 851 free(style->background_color); 852 free(style->underline_color); 853 free(style->overline_color); 854 free(style->strikethrough_color); 855 free(style->boxed_color); 856 free(style->href); 857 free(style); 858 } 859 860 static struct Font * 861 cho_style_font_desc_parse(const char *font_desc, struct ChoStylePresence *presence) 862 { 863 size_t len; 864 int w, n; 865 int name_until = EMPTY_INT; 866 char *c, *word; 867 char **words = NULL; 868 double size = -1.0; 869 struct Font *font; 870 871 len = strlen(font_desc); 872 if (len == 0) { 873 return NULL; 874 } 875 char str[len+1]; 876 strcpy((char *)&str, font_desc); 877 for (c = (char *)&str, w = 0; (word = strtok(c, " ")); w++) { 878 words = erealloc(words, (w+1) * sizeof(char *)); 879 words[w] = word; 880 c = NULL; 881 } 882 words = erealloc(words, (w+1) * sizeof(char *)); 883 words[w] = NULL; 884 font = cho_font_new(); 885 for (w = 0; words[w]; w++) { 886 if (!strcasecmp(words[w], "italic")) { 887 font->style = FONT_STYLE_ITALIC; 888 presence->font.style = true; 889 if (name_until == EMPTY_INT) { 890 name_until = w; 891 } 892 } else if (!strcasecmp(words[w], "bold")) { 893 font->weight = FONT_WEIGHT_BOLD; 894 presence->font.weight = true; 895 if (name_until == EMPTY_INT) { 896 name_until = w; 897 } 898 } else if (!strcasecmp(words[w], "oblique")) { 899 font->style = FONT_STYLE_OBLIQUE; 900 presence->font.style = true; 901 if (name_until == EMPTY_INT) { 902 name_until = w; 903 } 904 /* 905 * TODO: Is it smart to treat 'regular' as a font weight 906 * and 'normal' as a font style. The words are very similar. 907 * But what is the alternative? 908 * */ 909 } else if (!strcasecmp(words[w], "regular")) { 910 font->weight = FONT_WEIGHT_REGULAR; 911 presence->font.weight = true; 912 if (name_until == EMPTY_INT) { 913 name_until = w; 914 } 915 } else if (!strcasecmp(words[w], "normal")) { 916 font->style = FONT_STYLE_ROMAN; 917 presence->font.style = true; 918 if (name_until == EMPTY_INT) { 919 name_until = w; 920 } 921 /* 922 * TODO: The following code is commented because it is unclear whether the 923 * words 'sans' and 'serif' are part of the font name or not. It is clear that 924 * e.g. given the word 'sans' the user wants a font with a family of sans and 925 * not e.g. monospace but when searching for the font with fontconfig an exact 926 * font name has to be provided. Maybe I should try all possible font names 927 * and return the first successful finding. 928 */ 929 /* } else if (strcasecmp(words[w], "sans") == 0) { 930 font->family = FONT_FAMILY_SANS; 931 if (name_until == EMPTY_INT) 932 name_until = w; 933 } else if (strcasecmp(words[w], "serif") == 0) { 934 font->family = FONT_FAMILY_SERIF; 935 if (name_until == EMPTY_INT) 936 name_until = w; 937 } else if (!strcasecmp(words[w], "monospace")) { 938 font->family = FONT_FAMILY_MONOSPACE; 939 presence->font.family = true; 940 if (name_until == EMPTY_INT) { 941 name_until = w; 942 } */ 943 } else { 944 size = strtod(words[w], NULL); 945 if (size == 0.0) { 946 continue; 947 } 948 font->size = size; 949 presence->font.size = true; 950 if (name_until == EMPTY_INT) { 951 name_until = w; 952 } 953 } 954 } 955 if (name_until == EMPTY_INT) { 956 name_until = w; 957 } 958 if (name_until > 0) { 959 for (w = 0, n = 0; w<name_until; w++) { 960 len = strlen(words[w]); 961 font->family = erealloc(font->family, (n+len+1) * sizeof(char)); 962 memcpy(&font->family[n], words[w], len); 963 n += len; 964 font->family[n] = ' '; 965 n++; 966 } 967 n--; 968 font->family[n] = '\0'; 969 presence->font.family = true; 970 } 971 free(words); 972 return font; 973 } 974 975 static struct ChoStyle * 976 cho_style_parse( 977 struct ChoContext *ctx, 978 const char *tag_name, 979 struct Attr **attrs, 980 struct ChoStyle *inherited_style, 981 struct ChoStylePresence *presence 982 ) 983 { 984 size_t value_len, last_char; 985 struct RGBColor *rgb_color; 986 struct ChoStyle *style; 987 struct Font *font; 988 struct Attr **a; 989 990 if (inherited_style) { 991 style = cho_style_copy(inherited_style); 992 } else { 993 style = cho_style_new_default(ctx); 994 } 995 if (!strcmp(tag_name, "span")) { 996 if (attrs) { 997 for (a = attrs; *a; a++) { 998 if (!strcmp((*a)->name, "font_desc")) { 999 font = cho_style_font_desc_parse((*a)->value, presence); 1000 if (font) { 1001 if (!font->family) { 1002 font->family = strdup(style->font->family); 1003 } 1004 cho_font_free(style->font); 1005 style->font = font; 1006 } 1007 } else if ( 1008 !strcmp((*a)->name, "font_family") || 1009 !strcmp((*a)->name, "face") 1010 ) { 1011 free(style->font->family); 1012 style->font->family = strdup((*a)->value); 1013 } else if (!strcmp((*a)->name, "size")) { 1014 value_len = strlen((*a)->value); 1015 last_char = value_len - 1; 1016 if ((*a)->value[last_char] == '%') { 1017 if (value_len < 5) { 1018 (*a)->value[last_char] = 0; 1019 int percentage = atoi((*a)->value); 1020 if (percentage != 0 && percentage <= 100) { 1021 style->font->size *= percentage / 100.0; 1022 presence->font.size = true; 1023 } else { 1024 cho_log(ctx, LOG_ERR, "Attribute 'size' of markup tag '%s' has an invalid percentage '%d'.", tag_name, percentage); 1025 goto ERR; 1026 } 1027 } else { 1028 cho_log(ctx, LOG_ERR, "Attribute 'size' of markup tag '%s' has an invalid percentage '%s'.", tag_name, (*a)->value); 1029 goto ERR; 1030 } 1031 } else if (isdigit((*a)->value[0]) != 0) { 1032 double size = strtod((*a)->value, NULL); 1033 if (size != 0.0) { 1034 style->font->size = size; 1035 presence->font.size = true; 1036 } else { 1037 cho_log(ctx, LOG_ERR, "Attribute 'size' of markup tag '%s' has an invalid number '%.1f'.", tag_name, size); 1038 goto ERR; 1039 } 1040 } else if (!strcmp((*a)->value, "xx-small")) { 1041 style->font->size *= 0.8; 1042 style->font->size *= 0.8; 1043 style->font->size *= 0.8; 1044 presence->font.size = true; 1045 } else if (!strcmp((*a)->value, "x-small")) { 1046 style->font->size *= 0.8; 1047 style->font->size *= 0.8; 1048 presence->font.size = true; 1049 } else if (!strcmp((*a)->value, "small")) { 1050 style->font->size *= 0.8; 1051 presence->font.size = true; 1052 // } else if (!strcmp((*a)->value, "medium")) { 1053 } else if (!strcmp((*a)->value, "large")) { 1054 style->font->size *= 1.8; 1055 presence->font.size = true; 1056 } else if (!strcmp((*a)->value, "x-large")) { 1057 style->font->size *= 1.8; 1058 style->font->size *= 1.8; 1059 presence->font.size = true; 1060 } else if (!strcmp((*a)->value, "xx-large")) { 1061 style->font->size *= 1.8; 1062 style->font->size *= 1.8; 1063 style->font->size *= 1.8; 1064 presence->font.size = true; 1065 } else if (!strcmp((*a)->value, "larger")) { 1066 style->font->size *= 1.8; 1067 presence->font.size = true; 1068 } else if (!strcmp((*a)->value, "smaller")) { 1069 style->font->size *= 0.8; 1070 presence->font.size = true; 1071 } else { 1072 cho_log(ctx, LOG_ERR, "Attribute 'size' of markup tag '%s' has an invalid value '%s'.", tag_name, (*a)->value); 1073 goto ERR; 1074 } 1075 } else if (!strcmp((*a)->name, "style")) { 1076 if (!strcmp((*a)->value, "normal")) { 1077 style->font->style = FONT_STYLE_ROMAN; 1078 presence->font.style = true; 1079 } else if (!strcmp((*a)->value, "oblique")) { 1080 style->font->style = FONT_STYLE_OBLIQUE; 1081 presence->font.style = true; 1082 } else if (!strcmp((*a)->value, "italic")) { 1083 style->font->style = FONT_STYLE_ITALIC; 1084 presence->font.style = true; 1085 } else { 1086 cho_log(ctx, LOG_ERR, "Attribute 'style' of markup tag '%s' has an invalid value '%s'.", tag_name, (*a)->value); 1087 goto ERR; 1088 } 1089 } else if (!strcmp((*a)->name, "weight")) { 1090 if (!strcmp((*a)->value, "normal")) { 1091 style->font->weight = FONT_WEIGHT_REGULAR; 1092 presence->font.weight = true; 1093 } else if (!strcmp((*a)->value, "bold")) { 1094 style->font->weight = FONT_WEIGHT_BOLD; 1095 presence->font.weight = true; 1096 } else { 1097 cho_log(ctx, LOG_ERR, "Attribute 'weight' of markup tag '%s' has an invalid value '%s'.", tag_name, (*a)->value); 1098 goto ERR; 1099 } 1100 } else if (!strcmp((*a)->name, "foreground")) { 1101 rgb_color = cho_color_parse((*a)->value); 1102 if (!rgb_color) { 1103 DEBUG("cho_color_parse failed."); 1104 cho_log(ctx, LOG_ERR, "Attribute 'foreground' of markup tag '%s' has an invalid value '%s'.", tag_name, (*a)->value); 1105 goto ERR; 1106 } else { 1107 free(style->foreground_color); 1108 style->foreground_color = rgb_color; 1109 presence->foreground_color = true; 1110 } 1111 } else if (!strcmp((*a)->name, "background")) { 1112 rgb_color = cho_color_parse((*a)->value); 1113 if (!rgb_color) { 1114 DEBUG("cho_color_parse failed."); 1115 cho_log(ctx, LOG_ERR, "Attribute 'background' of markup tag '%s' has an invalid value '%s'.", tag_name, (*a)->value); 1116 goto ERR; 1117 } else { 1118 free(style->background_color); 1119 style->background_color = rgb_color; 1120 presence->background_color = true; 1121 } 1122 } else if (!strcmp((*a)->name, "underline")) { 1123 if (!strcmp((*a)->value, "single")) { 1124 style->underline_style = LINE_STYLE_SINGLE; 1125 presence->underline_style = true; 1126 } else if (!strcmp((*a)->value, "double")) { 1127 style->underline_style = LINE_STYLE_DOUBLE; 1128 presence->underline_style = true; 1129 } else if (!strcmp((*a)->value, "none")) { 1130 style->underline_style = LINE_STYLE_NONE; 1131 presence->underline_style = true; 1132 } else { 1133 cho_log(ctx, LOG_ERR, "Attribute 'underline' of markup tag '%s' has an invalid value '%s'.", tag_name, (*a)->value); 1134 goto ERR; 1135 } 1136 } else if (!strcmp((*a)->name, "underline_colour")) { 1137 rgb_color = cho_color_parse((*a)->value); 1138 if (!rgb_color) { 1139 DEBUG("cho_color_parse failed."); 1140 cho_log(ctx, LOG_ERR, "Attribute 'underline_colour' of markup tag '%s' has an invalid value '%s'.", tag_name, (*a)->value); 1141 goto ERR; 1142 } else { 1143 free(style->underline_color); 1144 style->underline_color = rgb_color; 1145 presence->underline_color = true; 1146 } 1147 } else if (!strcmp((*a)->name, "overline")) { 1148 if (!strcmp((*a)->value, "single")) { 1149 style->overline_style = LINE_STYLE_SINGLE; 1150 presence->overline_style = true; 1151 } else if (!strcmp((*a)->value, "double")) { 1152 style->overline_style = LINE_STYLE_DOUBLE; 1153 presence->overline_style = true; 1154 } else if (!strcmp((*a)->value, "none")) { 1155 style->overline_style = LINE_STYLE_NONE; 1156 presence->overline_style = true; 1157 } else { 1158 cho_log(ctx, LOG_ERR, "Attribute 'overline' of markup tag '%s' has an invalid value '%s'.", tag_name, (*a)->value); 1159 goto ERR; 1160 } 1161 } else if (!strcmp((*a)->name, "overline_colour")) { 1162 rgb_color = cho_color_parse((*a)->value); 1163 if (!rgb_color) { 1164 DEBUG("cho_color_parse failed."); 1165 cho_log(ctx, LOG_ERR, "Attribute 'overline_colour' of markup tag '%s' has an invalid value '%s'.", tag_name, (*a)->value); 1166 goto ERR; 1167 } else { 1168 free(style->overline_color); 1169 style->overline_color = rgb_color; 1170 presence->overline_color = true; 1171 } 1172 } else if (!strcmp((*a)->name, "rise")) { 1173 value_len = strlen((*a)->value); 1174 last_char = value_len - 1; 1175 if ((*a)->value[0] == '-') { 1176 if ((*a)->value[last_char] == '%') { 1177 if (value_len < 6) { 1178 (*a)->value[last_char] = 0; 1179 int percentage = atoi(&(*a)->value[1]); 1180 if (percentage != 0 && percentage <= 100) { 1181 style->rise = (style->font->size / 2) * percentage / 100.0; 1182 presence->rise = true; 1183 } else { 1184 cho_log(ctx, LOG_ERR, "Attribute 'rise' of markup tag '%s' has an invalid percentage '%d'.", tag_name, percentage); 1185 goto ERR; 1186 } 1187 } 1188 } else if (isdigit((*a)->value[1]) != 0) { 1189 double rise = strtod((*a)->value, NULL); 1190 if (rise != 0.0) { 1191 style->rise = rise; 1192 presence->rise = true; 1193 } else { 1194 cho_log(ctx, LOG_ERR, "Attribute 'rise' of markup tag '%s' has an invalid number '%d'.", tag_name, rise); 1195 goto ERR; 1196 } 1197 } else { 1198 cho_log(ctx, LOG_ERR, "Attribute 'rise' of markup tag '%s' has an invalid value '%s'.", tag_name, (*a)->value); 1199 goto ERR; 1200 } 1201 } else { 1202 if ((*a)->value[last_char] == '%') { 1203 if (value_len < 5) { 1204 (*a)->value[last_char] = 0; 1205 int percentage = atoi((*a)->value); 1206 if (percentage != 0 && percentage <= 100) { 1207 // TODO: Test if that's right 1208 double more = style->font->size / 2.0 * percentage / 100.0; 1209 style->rise += more; 1210 presence->rise = true; 1211 } else { 1212 cho_log(ctx, LOG_ERR, "Attribute 'rise' of markup tag '%s' has an invalid percentage '%d'.", tag_name, percentage); 1213 goto ERR; 1214 } 1215 } 1216 } else if (isdigit((*a)->value[0]) != 0) { 1217 double rise = strtod((*a)->value, NULL); 1218 if (rise != 0.0) { 1219 style->rise = rise; 1220 presence->rise = true; 1221 } else { 1222 cho_log(ctx, LOG_ERR, "Attribute 'rise' of markup tag '%s' has an invalid number '%d'.", tag_name, rise); 1223 goto ERR; 1224 } 1225 } else { 1226 cho_log(ctx, LOG_ERR, "Attribute 'rise' of markup tag '%s' has an invalid value '%s'.", tag_name, (*a)->value); 1227 goto ERR; 1228 } 1229 } 1230 } else if (!strcmp((*a)->name, "strikethrough")) { 1231 if (!strcmp((*a)->value, "true")) { 1232 style->strikethrough = true; 1233 presence->strikethrough = true; 1234 } else if (!strcmp((*a)->value, "false")) { 1235 style->strikethrough = false; 1236 presence->strikethrough = true; 1237 } else { 1238 cho_log(ctx, LOG_ERR, "Attribute 'strikethrough' of markup tag '%s' has an invalid value '%s'.", tag_name, (*a)->value); 1239 goto ERR; 1240 } 1241 } else if (!strcmp((*a)->name, "strikethrough_colour")) { 1242 rgb_color = cho_color_parse((*a)->value); 1243 if (!rgb_color) { 1244 DEBUG("cho_color_parse failed."); 1245 cho_log(ctx, LOG_ERR, "Attribute 'strikethrough_colour' of markup tag '%s' has an invalid value '%s'.", tag_name, (*a)->value); 1246 goto ERR; 1247 } else { 1248 free(style->strikethrough_color); 1249 style->strikethrough_color = rgb_color; 1250 presence->strikethrough_color = true; 1251 } 1252 } else if (!strcmp((*a)->name, "href")) { 1253 style->href = strdup((*a)->value); 1254 presence->href = true; 1255 } else { 1256 cho_log(ctx, LOG_ERR, "Attribute '%s' of markup tag '%s' is invalid.", (*a)->name, tag_name); 1257 goto ERR; 1258 } 1259 } 1260 } else { 1261 cho_log(ctx, LOG_INFO, "'span' markup tag has no attributes and therefore no effect."); 1262 } 1263 } else if (!strcmp(tag_name, "b")) { 1264 style->font->weight = FONT_WEIGHT_BOLD; 1265 presence->font.weight = true; 1266 } else if (!strcmp(tag_name, "big")) { 1267 style->font->size *= 0.8; 1268 presence->font.size = true; 1269 } else if (!strcmp(tag_name, "i")) { 1270 style->font->style = FONT_STYLE_ITALIC; 1271 presence->font.style = true; 1272 } else if (!strcmp(tag_name, "s")) { 1273 style->strikethrough = true; 1274 presence->strikethrough = true; 1275 } else if (!strcmp(tag_name, "sub")) { 1276 style->font->size *= 0.8; 1277 style->rise = -style->font->size * 0.3; 1278 presence->font.size = true; 1279 presence->rise = true; 1280 } else if (!strcmp(tag_name, "sup")) { 1281 style->font->size *= 0.8; 1282 style->rise = style->font->size * 0.3; 1283 presence->font.size = true; 1284 presence->rise = true; 1285 } else if (!strcmp(tag_name, "small")) { 1286 style->font->size *= 0.8; 1287 presence->font.size = true; 1288 } else if (!strcmp(tag_name, "tt")) { 1289 style->font->family = strdup("monospace"); 1290 presence->font.family = true; 1291 } else if (!strcmp(tag_name, "u")) { 1292 style->underline_style = LINE_STYLE_SINGLE; 1293 presence->underline_style = true; 1294 } else { 1295 cho_log(ctx, LOG_ERR, "Markup tag '%s' is invalid.", tag_name); 1296 goto ERR; 1297 } 1298 return style; 1299 ERR: 1300 cho_style_free(style); 1301 return NULL; 1302 } 1303 1304 void 1305 cho_style_print_as_toml(struct ChoStyle *style, const char *section) 1306 { 1307 printf("foreground_color = \"%s\"\n", cho_rgbcolor_to_string(style->foreground_color)); 1308 printf("background_color = \"%s\"\n", cho_rgbcolor_to_string(style->background_color)); 1309 printf("underline_style = \"%s\"\n", cho_linestyle_to_config_string(style->underline_style)); 1310 printf("underline_color = \"%s\"\n", cho_rgbcolor_to_string(style->underline_color)); 1311 printf("overline_style = \"%s\"\n", cho_linestyle_to_config_string(style->overline_style)); 1312 printf("overline_color = \"%s\"\n", cho_rgbcolor_to_string(style->overline_color)); 1313 printf("strikethrough = %s\n", style->strikethrough ? "true" : "false"); 1314 printf("strikethrough_color = \"%s\"\n", cho_rgbcolor_to_string(style->strikethrough_color)); 1315 printf("boxed = %s\n", style->boxed ? "true" : "false"); 1316 printf("boxed_color = \"%s\"\n", cho_rgbcolor_to_string(style->boxed_color)); 1317 printf("rise = %.1f\n", style->rise); 1318 printf("href = \"%s\"\n\n", style->href ? style->href : ""); 1319 cho_font_print_as_toml(style->font, section); 1320 } 1321 1322 static bool 1323 cho_style_change_default(struct StyleProperty sprop) 1324 { 1325 unsigned int i; 1326 for (i = 0; i<LENGTH(default_style_properties); i++) { 1327 if ( 1328 default_style_properties[i].ttype == sprop.ttype && 1329 default_style_properties[i].type == sprop.type 1330 ) { 1331 switch (sprop.type) { 1332 case STYLE_PROPERTY_TYPE_FONT: 1333 free(default_style_properties[i].u.font_name); 1334 if (sprop.u.font_name) { 1335 default_style_properties[i].u.font_name = strdup(sprop.u.font_name); 1336 } else { 1337 default_style_properties[i].u.font_name = NULL; 1338 } 1339 return true; 1340 case STYLE_PROPERTY_TYPE_SIZE: 1341 default_style_properties[i].u.font_size = sprop.u.font_size; 1342 return true; 1343 case STYLE_PROPERTY_TYPE_COLOR: 1344 free(default_style_properties[i].u.foreground_color); 1345 if (sprop.u.foreground_color) { 1346 default_style_properties[i].u.foreground_color = cho_rgbcolor_copy(sprop.u.foreground_color); 1347 } else { 1348 default_style_properties[i].u.foreground_color = NULL; 1349 } 1350 return true; 1351 } 1352 } 1353 } 1354 return false; 1355 } 1356 1357 static bool 1358 cho_style_reset_default(void) 1359 { 1360 unsigned int i; 1361 for (i = 0; i<LENGTH(default_style_properties); i++) { 1362 switch (default_style_properties[i].type) { 1363 case STYLE_PROPERTY_TYPE_FONT: 1364 free(default_style_properties[i].u.font_name); 1365 default_style_properties[i].u.font_name = NULL; 1366 return true; 1367 case STYLE_PROPERTY_TYPE_SIZE: 1368 default_style_properties[i].u.font_size = EMPTY_DOUBLE; 1369 return true; 1370 case STYLE_PROPERTY_TYPE_COLOR: 1371 free(default_style_properties[i].u.foreground_color); 1372 default_style_properties[i].u.foreground_color = NULL; 1373 return true; 1374 } 1375 } 1376 return false; 1377 } 1378 1379 static struct Attr * 1380 cho_tag_attr_new(void) 1381 { 1382 struct Attr *attr = emalloc(sizeof(struct Attr)); 1383 attr->name = NULL; 1384 attr->value = NULL; 1385 return attr; 1386 } 1387 1388 static void 1389 cho_tag_attr_free(struct Attr *attr) 1390 { 1391 if (!attr) { 1392 return; 1393 } 1394 free(attr->name); 1395 free(attr->value); 1396 free(attr); 1397 } 1398 1399 static void 1400 cho_tag_attrs_free(struct Attr **attrs) 1401 { 1402 if (!attrs) { 1403 return; 1404 } 1405 struct Attr **a; 1406 for (a = attrs; *a; a++) { 1407 cho_tag_attr_free(*a); 1408 } 1409 free(attrs); 1410 } 1411 1412 static struct Tag * 1413 cho_tag_new(void) 1414 { 1415 struct Tag *tag = emalloc(sizeof(struct Tag)); 1416 tag->name = NULL; 1417 tag->style = NULL; 1418 tag->attrs = NULL; 1419 tag->is_closed = false; 1420 memset(&tag->style_presence, 0, sizeof(tag->style_presence)); 1421 return tag; 1422 } 1423 1424 static void 1425 cho_tag_free(struct Tag *tag) 1426 { 1427 if (!tag) { 1428 return; 1429 } 1430 free(tag->name); 1431 cho_style_free(tag->style); 1432 if (tag->attrs) { 1433 cho_tag_attrs_free(tag->attrs); 1434 } 1435 free(tag); 1436 } 1437 1438 static bool 1439 cho_tag_close_last_unclosed(struct ChoContext *ctx, const char *tag_name, struct Tag **tags, int last_index) 1440 { 1441 int i; 1442 for (i = last_index; i >= 0; i--) { 1443 if (!strcmp(tags[i]->name, tag_name) && !tags[i]->is_closed) { 1444 tags[i]->is_closed = true; 1445 return true; 1446 } 1447 } 1448 cho_log(ctx, LOG_ERR, "Didn't find a start tag for the end tag '%s'.", tag_name); 1449 return false; 1450 } 1451 1452 static struct ChoStyle * 1453 cho_tag_style_inherit(struct Tag **tags, int prev_index) 1454 { 1455 int i; 1456 for (i = prev_index; i >= 0; i--) { 1457 if (!tags[i]->is_closed) { 1458 return tags[i]->style; 1459 } 1460 } 1461 /* 1462 INFO: Doesn't mean there is an error. 1463 If no style can be inherited the 1464 default style should be used. 1465 */ 1466 return NULL; 1467 } 1468 1469 static struct ChoMetadata * 1470 cho_metadata_new(struct ChoContext *ctx) 1471 { 1472 struct ChoMetadata *meta = emalloc(sizeof(struct ChoMetadata)); 1473 meta->name = NULL; 1474 meta->value = NULL; 1475 meta->style = cho_style_new_default(ctx); 1476 return meta; 1477 } 1478 1479 static void 1480 cho_metadata_free(struct ChoMetadata *meta) 1481 { 1482 if (!meta) { 1483 return; 1484 } 1485 free(meta->name); 1486 free(meta->value); 1487 cho_style_free(meta->style); 1488 free(meta); 1489 } 1490 1491 bool 1492 cho_metadata_value( 1493 struct ChoMetadata **metadata, 1494 const char *name, 1495 const char *separator, 1496 char **value, 1497 struct ChoStyle **value_style 1498 ) 1499 { 1500 *value = NULL; 1501 const char *c; 1502 struct ChoMetadata **m; 1503 int v = 0; 1504 for (m = metadata; *m; m++) { 1505 if (!strcmp((*m)->name, name)) { 1506 if (*value) { 1507 for (c = separator; *c; c++, v++) { 1508 *value = erealloc(*value, (v+1) * sizeof(char)); 1509 (*value)[v] = *c; 1510 } 1511 } 1512 for (c = (*m)->value; *c; c++, v++) { 1513 *value = erealloc(*value, (v+1) * sizeof(char)); 1514 (*value)[v] = *c; 1515 } 1516 *value_style = (*m)->style; 1517 } 1518 } 1519 if (*value) { 1520 *value = erealloc(*value, (v+1) * sizeof(char)); 1521 (*value)[v] = 0; 1522 return true; 1523 } 1524 return false; 1525 } 1526 1527 static struct ChoMetadata * 1528 cho_metadata_split(struct ChoContext *ctx, const char *directive_value) 1529 { 1530 struct ChoMetadata *meta = cho_metadata_new(ctx); 1531 bool is_name = true; 1532 char *value = str_remove_leading_whitespace(directive_value); 1533 int n = 0; 1534 int v = 0; 1535 int i; 1536 for (i = 0; value[i]; i++) { 1537 if (is_name) { 1538 if (value[i] == ' ') { 1539 meta->name = erealloc(meta->name, (n+1) * sizeof(char)); 1540 meta->name[n] = 0; 1541 is_name = false; 1542 } else { 1543 meta->name = erealloc(meta->name, (n+1) * sizeof(char)); 1544 meta->name[n] = value[i]; 1545 n++; 1546 } 1547 } else { 1548 meta->value = erealloc(meta->value, (v+1) * sizeof(char)); 1549 meta->value[v] = value[i]; 1550 v++; 1551 } 1552 } 1553 meta->value = erealloc(meta->value, (v+1) * sizeof(char)); 1554 meta->value[v] = 0; 1555 free(value); 1556 if (v > 1) { 1557 return meta; 1558 } else { 1559 cho_metadata_free(meta); 1560 cho_log(ctx, LOG_ERR, "Failed to parse directive 'meta'."); 1561 return NULL; 1562 } 1563 } 1564 1565 static struct ChoMetadata ** 1566 cho_metadata_load_default(struct ChoContext *ctx) 1567 { 1568 struct ChoMetadata **meta = NULL; 1569 struct InstrumentInfo ins_info; 1570 struct tm *tt; 1571 time_t t; 1572 char *filename, *logged_in_user; 1573 char time_str[64]; 1574 int i = 0; 1575 1576 logged_in_user = getenv("USER"); 1577 if (!logged_in_user) { 1578 DEBUG("getenv(USER) failed."); 1579 return NULL; 1580 } 1581 t = time(NULL); 1582 tt = localtime(&t); 1583 if (strftime((char *)&time_str, 64, "%a, %d. %b %H:%M", tt) == 0) { 1584 DEBUG("strftime failed."); 1585 return NULL; 1586 } 1587 ins_info = config_instrument_get(ctx->config->output->diagram->instrument); 1588 meta = erealloc(meta, (i+1) * sizeof(struct ChoMetadata *)); 1589 meta[i] = emalloc(sizeof(struct ChoMetadata)); 1590 meta[i]->name = strdup("chordpro"); 1591 meta[i]->value = strdup("ChordPro"); 1592 meta[i]->style = cho_style_new_default(ctx); 1593 i++; 1594 if (ctx->chordpro_filepath) { 1595 filename = filepath_basename(ctx->chordpro_filepath); 1596 meta = erealloc(meta, (i+1) * sizeof(struct ChoMetadata *)); 1597 meta[i] = emalloc(sizeof(struct ChoMetadata)); 1598 meta[i]->name = strdup("chordpro.songsource"); 1599 meta[i]->value = filename; 1600 meta[i]->style = cho_style_new_default(ctx); 1601 i++; 1602 } 1603 meta = erealloc(meta, (i+1) * sizeof(struct ChoMetadata *)); 1604 meta[i] = emalloc(sizeof(struct ChoMetadata)); 1605 meta[i]->name = strdup("chordpro.version"); 1606 meta[i]->value = strdup("6"); 1607 meta[i]->style = cho_style_new_default(ctx); 1608 i++; 1609 meta = erealloc(meta, (i+1) * sizeof(struct ChoMetadata *)); 1610 meta[i] = emalloc(sizeof(struct ChoMetadata)); 1611 meta[i]->name = strdup("instrument"); 1612 meta[i]->value = strdup(ins_info.name); 1613 meta[i]->style = cho_style_new_default(ctx); 1614 i++; 1615 meta = erealloc(meta, (i+1) * sizeof(struct ChoMetadata *)); 1616 meta[i] = emalloc(sizeof(struct ChoMetadata)); 1617 meta[i]->name = strdup("instrument.type"); 1618 meta[i]->value = strdup(ins_info.name); 1619 meta[i]->style = cho_style_new_default(ctx); 1620 i++; 1621 meta = erealloc(meta, (i+1) * sizeof(struct ChoMetadata *)); 1622 meta[i] = emalloc(sizeof(struct ChoMetadata)); 1623 meta[i]->name = strdup("instrument.description"); 1624 meta[i]->value = strdup(ins_info.description); 1625 meta[i]->style = cho_style_new_default(ctx); 1626 i++; 1627 meta = erealloc(meta, (i+1) * sizeof(struct ChoMetadata *)); 1628 meta[i] = emalloc(sizeof(struct ChoMetadata)); 1629 meta[i]->name = strdup("today"); 1630 meta[i]->value = strdup(time_str); 1631 meta[i]->style = cho_style_new_default(ctx); 1632 i++; 1633 meta = erealloc(meta, (i+1) * sizeof(struct ChoMetadata *)); 1634 meta[i] = emalloc(sizeof(struct ChoMetadata)); 1635 meta[i]->name = strdup("tuning"); 1636 meta[i]->value = strdup(ins_info.tuning); 1637 meta[i]->style = cho_style_new_default(ctx); 1638 i++; 1639 meta = erealloc(meta, (i+1) * sizeof(struct ChoMetadata *)); 1640 meta[i] = emalloc(sizeof(struct ChoMetadata)); 1641 meta[i]->name = strdup("user"); 1642 meta[i]->value = strdup(logged_in_user); 1643 meta[i]->style = cho_style_new_default(ctx); 1644 i++; 1645 meta = erealloc(meta, (i+1) * sizeof(struct ChoMetadata *)); 1646 meta[i] = emalloc(sizeof(struct ChoMetadata)); 1647 meta[i]->name = strdup("user.name"); 1648 meta[i]->value = strdup(logged_in_user); 1649 meta[i]->style = cho_style_new_default(ctx); 1650 i++; 1651 meta = erealloc(meta, (i+1) * sizeof(struct ChoMetadata *)); 1652 meta[i] = emalloc(sizeof(struct ChoMetadata)); 1653 meta[i]->name = strdup("user.fullname"); 1654 meta[i]->value = strdup(logged_in_user); 1655 meta[i]->style = cho_style_new_default(ctx); 1656 i++; 1657 ctx->m = i; 1658 return meta; 1659 } 1660 1661 static char * 1662 cho_metadata_substitution_replace( 1663 struct ChoContext *ctx, 1664 const char *name, 1665 const char *value, 1666 int index 1667 ) 1668 { 1669 struct ChoMetadata *m; 1670 int n = 0; 1671 int i; 1672 if (index == 0) { 1673 char *c; 1674 char *out = NULL; 1675 int o = 0; 1676 if (value[0] != 0) { 1677 for (i = 0; i<ctx->m; i++) { 1678 m = ctx->songs[ctx->so]->metadata[i]; 1679 if (!strcmp(m->name, name) && !strcmp(m->value, value)) { 1680 if (out) { 1681 for (c = ctx->config->metadata_separator; *c; c++, o++) { 1682 out = erealloc(out, (o+1) * sizeof(char)); 1683 out[o] = *c; 1684 } 1685 } 1686 for (c = m->value; *c; c++, o++) { 1687 out = erealloc(out, (o+1) * sizeof(char)); 1688 out[o] = *c; 1689 } 1690 } 1691 } 1692 } else { 1693 for (i = 0; i<ctx->m; i++) { 1694 m = ctx->songs[ctx->so]->metadata[i]; 1695 if (!strcmp(m->name, name)) { 1696 if (out) { 1697 for (c = ctx->config->metadata_separator; *c; c++, o++) { 1698 out = erealloc(out, (o+1) * sizeof(char)); 1699 out[o] = *c; 1700 } 1701 } 1702 for (c = m->value; *c; c++, o++) { 1703 out = erealloc(out, (o+1) * sizeof(char)); 1704 out[o] = *c; 1705 } 1706 } 1707 } 1708 } 1709 out = erealloc(out, (o+1) * sizeof(char)); 1710 out[o] = 0; 1711 return out; 1712 } else if (index < 0) { 1713 if (value[0] != 0) { 1714 for (i = ctx->m-1; i >= 0; i--) { 1715 m = ctx->songs[ctx->so]->metadata[i]; 1716 if (!strcmp(m->name, name)) { 1717 n--; 1718 if (!strcmp(m->value, value) && n == index) { 1719 return strdup(m->value); 1720 } 1721 } 1722 } 1723 } else { 1724 for (i = ctx->m-1; i >= 0; i--) { 1725 m = ctx->songs[ctx->so]->metadata[i]; 1726 if (!strcmp(m->name, name)) { 1727 n--; 1728 if (n == index) { 1729 return strdup(m->value); 1730 } 1731 } 1732 } 1733 } 1734 } else if (index > 0) { 1735 if (value[0] != 0) { 1736 for (i = 0; i<ctx->m; i++) { 1737 m = ctx->songs[ctx->so]->metadata[i]; 1738 if (!strcmp(m->name, name)) { 1739 n++; 1740 if (!strcmp(m->value, value) && n == index) { 1741 return strdup(m->value); 1742 } 1743 } 1744 } 1745 } else { 1746 for (i = 0; i<ctx->m; i++) { 1747 m = ctx->songs[ctx->so]->metadata[i]; 1748 if (!strcmp(m->name, name)) { 1749 n++; 1750 if (n == index) { 1751 return strdup(m->value); 1752 } 1753 } 1754 } 1755 } 1756 } 1757 return NULL; 1758 } 1759 1760 static char * 1761 cho_metadata_substitution_parse( 1762 struct ChoContext *ctx, 1763 const char *str, 1764 const char *parent_name, 1765 enum State state_before_substitution 1766 ) 1767 { 1768 bool substitution_exist = false; 1769 int o = 0; 1770 char *out = NULL; 1771 int len = strlen(str); 1772 int i; 1773 for (i = 0; i<len; i++) { 1774 if (str[i] == '%' && str[i+1] == '{') { 1775 substitution_exist = true; 1776 i += 2; 1777 break; 1778 } 1779 out = erealloc(out, (o+1) * sizeof(char)); 1780 out[o] = str[i]; 1781 o++; 1782 } 1783 1784 enum MetadataSubstitutionState state = METADATA_SUBSTITUTION_STATE_NAME; 1785 enum AttrValueSyntax avs = ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED; 1786 char name[128]; 1787 char name_index[8]; 1788 char value[4096]; 1789 char true_text[4096]; 1790 char false_text[4096]; 1791 name[0] = 0; 1792 name_index[0] = 0; 1793 value[0] = 0; 1794 true_text[0] = 0; 1795 false_text[0] = 0; 1796 char prev_c = 0; 1797 char c = 0; 1798 int n = 0; 1799 int nested_level = 0; 1800 for (; i<len; i++) { 1801 c = str[i]; 1802 1803 if (prev_c == '%' && c == '{') { 1804 nested_level++; 1805 } else if (prev_c != '\\' && c == '}') { 1806 if (nested_level == 0) { 1807 i++; 1808 break; 1809 } 1810 nested_level--; 1811 } 1812 1813 switch (state) { 1814 case METADATA_SUBSTITUTION_STATE_NAME: { 1815 if (c == '=') { 1816 name[n] = 0; 1817 n = 0; 1818 state = METADATA_SUBSTITUTION_STATE_VALUE; 1819 break; 1820 } 1821 if (c == '.') { 1822 name[n] = 0; 1823 state = METADATA_SUBSTITUTION_STATE_NAME_OR_INDEX; 1824 break; 1825 } 1826 if (prev_c != '\\' && c == '|') { 1827 name[n] = 0; 1828 n = 0; 1829 state = METADATA_SUBSTITUTION_STATE_TRUE_TEXT; 1830 break; 1831 } 1832 name[n] = c; 1833 n++; 1834 break; 1835 } 1836 case METADATA_SUBSTITUTION_STATE_NAME_OR_INDEX: { 1837 if (c == '-' || isdigit(c)) { 1838 n = 0; 1839 name_index[n] = c; 1840 n++; 1841 state = METADATA_SUBSTITUTION_STATE_INDEX; 1842 break; 1843 } else 1844 if (islower(c)) { 1845 name[n] = '.'; 1846 n++; 1847 name[n] = c; 1848 n++; 1849 state = METADATA_SUBSTITUTION_STATE_NAME; 1850 break; 1851 } 1852 cho_log(ctx, LOG_ERR, "Invalid character after the dot."); 1853 goto ERR; 1854 } 1855 case METADATA_SUBSTITUTION_STATE_INDEX: { 1856 if (c == '=') { 1857 name_index[n] = 0; 1858 n = 0; 1859 state = METADATA_SUBSTITUTION_STATE_VALUE; 1860 break; 1861 } 1862 if (prev_c != '\\' && c == '|') { 1863 name_index[n] = 0; 1864 n = 0; 1865 state = METADATA_SUBSTITUTION_STATE_TRUE_TEXT; 1866 break; 1867 } 1868 if (!isdigit(c)) { 1869 // TODO: Find a better term for 'metadata substitution name index'! 1870 cho_log(ctx, LOG_ERR, "Specify a positive or negative number in metadata substitution name index."); 1871 goto ERR; 1872 } 1873 name_index[n] = c; 1874 n++; 1875 break; 1876 } 1877 case METADATA_SUBSTITUTION_STATE_VALUE: { 1878 if (avs == ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED) { 1879 if (is_whitespace(c)) { 1880 cho_log(ctx, LOG_ERR, "Whitespace character after equals sign is invalid."); 1881 goto ERR; 1882 } 1883 if (c == '|') { 1884 cho_log(ctx, LOG_ERR, "If you specify an equals sign then you have to provide a value."); 1885 goto ERR; 1886 } 1887 if (c == '\'') { 1888 avs = ATTRIBUTE_VALUE_SYNTAX_APOSTROPHE; 1889 } else if (c == '"') { 1890 avs = ATTRIBUTE_VALUE_SYNTAX_QUOTATION_MARK; 1891 } else { 1892 avs = ATTRIBUTE_VALUE_SYNTAX_UNQUOTED; 1893 value[n] = c; 1894 n++; 1895 } 1896 break; 1897 } 1898 if (prev_c != '\\' && c == '|') { 1899 if (avs == ATTRIBUTE_VALUE_SYNTAX_APOSTROPHE) { 1900 cho_log(ctx, LOG_ERR, "Can't find a matching \"\'\"."); 1901 goto ERR; 1902 } 1903 if (avs == ATTRIBUTE_VALUE_SYNTAX_QUOTATION_MARK) { 1904 cho_log(ctx, LOG_ERR, "Can't find a matching '\"'."); 1905 goto ERR; 1906 } 1907 if (avs == ATTRIBUTE_VALUE_SYNTAX_UNQUOTED) { 1908 value[n] = 0; 1909 n = 0; 1910 state = METADATA_SUBSTITUTION_STATE_TRUE_TEXT; 1911 break; 1912 } 1913 if (avs == ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED) { 1914 state = METADATA_SUBSTITUTION_STATE_TRUE_TEXT; 1915 break; 1916 } 1917 break; 1918 } 1919 if ( 1920 (avs == ATTRIBUTE_VALUE_SYNTAX_APOSTROPHE && c == '\'') || 1921 (avs == ATTRIBUTE_VALUE_SYNTAX_QUOTATION_MARK && c == '"') || 1922 (avs == ATTRIBUTE_VALUE_SYNTAX_UNQUOTED && c == ' ') 1923 ) { 1924 value[n] = 0; 1925 n = 0; 1926 state = METADATA_SUBSTITUTION_STATE_WAIT_FOR_PIPE; 1927 break; 1928 } 1929 value[n] = c; 1930 n++; 1931 break; 1932 } 1933 case METADATA_SUBSTITUTION_STATE_TRUE_TEXT: { 1934 if (prev_c != '\\' && c == '|' && nested_level == 0) { 1935 true_text[n] = 0; 1936 n = 0; 1937 state = METADATA_SUBSTITUTION_STATE_FALSE_TEXT; 1938 break; 1939 } 1940 true_text[n] = c; 1941 n++; 1942 break; 1943 } 1944 case METADATA_SUBSTITUTION_STATE_FALSE_TEXT: { 1945 false_text[n] = c; 1946 n++; 1947 break; 1948 } 1949 case METADATA_SUBSTITUTION_STATE_WAIT_FOR_PIPE: { 1950 if (is_whitespace(c)) { 1951 break; 1952 } 1953 if (c == '|') { 1954 state = METADATA_SUBSTITUTION_STATE_TRUE_TEXT; 1955 break; 1956 } 1957 break; 1958 } 1959 } 1960 prev_c = c; 1961 } 1962 switch (state) { 1963 case METADATA_SUBSTITUTION_STATE_NAME: 1964 name[n] = 0; 1965 break; 1966 case METADATA_SUBSTITUTION_STATE_INDEX: 1967 name_index[n] = 0; 1968 break; 1969 case METADATA_SUBSTITUTION_STATE_VALUE: 1970 switch (avs) { 1971 case ATTRIBUTE_VALUE_SYNTAX_APOSTROPHE: 1972 cho_log(ctx, LOG_ERR, "Can't find a matching \"\'\"."); 1973 goto ERR; 1974 case ATTRIBUTE_VALUE_SYNTAX_QUOTATION_MARK: 1975 cho_log(ctx, LOG_ERR, "Can't find a matching '\"'."); 1976 goto ERR; 1977 case ATTRIBUTE_VALUE_SYNTAX_UNQUOTED: 1978 value[n] = 0; 1979 break; 1980 default: 1981 } 1982 break; 1983 case METADATA_SUBSTITUTION_STATE_TRUE_TEXT: 1984 true_text[n] = 0; 1985 break; 1986 case METADATA_SUBSTITUTION_STATE_FALSE_TEXT: 1987 false_text[n] = 0; 1988 break; 1989 default: 1990 } 1991 char *replaced, *substituted, *ch; 1992 int index = 0; 1993 if (name_index[0] != 0) { 1994 index = atoi(name_index); 1995 if (index == 0) { 1996 DEBUG("atoi failed."); 1997 goto ERR; 1998 } 1999 } 2000 if (name[0] != 0) { 2001 replaced = cho_metadata_substitution_replace(ctx, name, value, index); 2002 if (replaced[0] != 0) { 2003 if (true_text[0] != 0) { 2004 substituted = cho_metadata_substitution_parse(ctx, true_text, name, STATE_METADATA_SUBSTITUTION); 2005 for (ch = substituted; *ch; ch++) { 2006 out = erealloc(out, (o+1) * sizeof(char)); 2007 out[o] = *ch; 2008 o++; 2009 } 2010 free(substituted); 2011 } else { 2012 for (ch = replaced; *ch; ch++) { 2013 out = erealloc(out, (o+1) * sizeof(char)); 2014 out[o] = *ch; 2015 o++; 2016 } 2017 } 2018 } else { 2019 if (false_text[0] != 0) { 2020 substituted = cho_metadata_substitution_parse(ctx, false_text, name, STATE_METADATA_SUBSTITUTION); 2021 for (ch = substituted; *ch; ch++) { 2022 out = erealloc(out, (o+1) * sizeof(char)); 2023 out[o] = *ch; 2024 o++; 2025 } 2026 free(substituted); 2027 } else { 2028 if (index != 0) { 2029 cho_log(ctx, LOG_WARN, "There is no metadata item named '%s.%d'.", name, index); 2030 } else { 2031 cho_log(ctx, LOG_WARN, "There is no metadata item named '%s'.", name); 2032 } 2033 } 2034 } 2035 free(replaced); 2036 } else { 2037 if (state_before_substitution != STATE_METADATA_SUBSTITUTION) { 2038 cho_log(ctx, LOG_ERR, "An empty metadata substitution can only be used inside of another metadata substitution."); 2039 goto ERR; 2040 } 2041 if (substitution_exist) { 2042 replaced = cho_metadata_substitution_replace(ctx, parent_name, "", 0); 2043 if (replaced[0] != 0) { 2044 for (ch = replaced; *ch; ch++) { 2045 out = erealloc(out, (o+1) * sizeof(char)); 2046 out[o] = *ch; 2047 o++; 2048 } 2049 } else { 2050 cho_log(ctx, LOG_WARN, "There is no metadata item named '%s'.", parent_name); 2051 } 2052 free(replaced); 2053 } 2054 } 2055 for (; i<len; i++) { 2056 out = erealloc(out, (o+1) * sizeof(char)); 2057 out[o] = str[i]; 2058 o++; 2059 } 2060 out = erealloc(out, (o+1) * sizeof(char)); 2061 out[o] = 0; 2062 return out; 2063 ERR: 2064 free(out); 2065 return NULL; 2066 } 2067 2068 static bool 2069 transposition_parse(const char *str, int *transpose) 2070 { 2071 long i; 2072 char *endptr; 2073 i = strtol(str, &endptr, 10); 2074 if (str == endptr) { 2075 return false; 2076 } 2077 if (i == LONG_MIN || i == LONG_MAX) { 2078 return false; 2079 } 2080 if (i > INT_MAX) { 2081 return false; 2082 } 2083 *transpose = (int)i; 2084 return true; 2085 } 2086 2087 static char * 2088 transposition_calc_chord_root(struct ChoContext *ctx, int index, enum NoteType type) 2089 { 2090 int transpose = *ctx->transpose; 2091 if (transpose == 0) { 2092 switch (type) { 2093 case NOTE_TYPE_NOTE: 2094 return strdup(ctx->config->output->notes[index]->note); 2095 case NOTE_TYPE_SHARP: 2096 return strdup(ctx->config->output->notes[index]->sharp); 2097 case NOTE_TYPE_FLAT: 2098 return strdup(ctx->config->output->notes[index]->flat); 2099 } 2100 } 2101 int new_index = index; 2102 enum NoteType note_type = type; 2103 int i; 2104 if (transpose > 0) { 2105 for (i = 0; i<transpose; i++) { 2106 switch (note_type) { 2107 case NOTE_TYPE_NOTE: 2108 switch (new_index) { 2109 case 2: 2110 new_index++; 2111 break; 2112 case 6: 2113 new_index = 0; 2114 break; 2115 default: 2116 note_type = NOTE_TYPE_SHARP; 2117 } 2118 break; 2119 case NOTE_TYPE_SHARP: 2120 new_index++; 2121 note_type = NOTE_TYPE_NOTE; 2122 break; 2123 case NOTE_TYPE_FLAT: 2124 note_type = NOTE_TYPE_NOTE; 2125 break; 2126 } 2127 } 2128 } else { 2129 for (i = transpose; i<0; i++) { 2130 switch (note_type) { 2131 case NOTE_TYPE_NOTE: 2132 switch (new_index) { 2133 case 0: 2134 new_index = 6; 2135 break; 2136 case 3: 2137 new_index--; 2138 break; 2139 default: 2140 note_type = NOTE_TYPE_FLAT; 2141 } 2142 break; 2143 case NOTE_TYPE_SHARP: 2144 note_type = NOTE_TYPE_NOTE; 2145 break; 2146 case NOTE_TYPE_FLAT: 2147 new_index--; 2148 note_type = NOTE_TYPE_NOTE; 2149 break; 2150 } 2151 } 2152 } 2153 switch (note_type) { 2154 case NOTE_TYPE_NOTE: 2155 return strdup(ctx->config->output->notes[new_index]->note); 2156 case NOTE_TYPE_SHARP: 2157 return strdup(ctx->config->output->notes[new_index]->sharp); 2158 case NOTE_TYPE_FLAT: 2159 return strdup(ctx->config->output->notes[new_index]->flat); 2160 } 2161 cho_log(ctx, LOG_ERR, "Invalid NoteType '%d'.", note_type); 2162 return NULL; 2163 } 2164 2165 static struct ChoChord * 2166 cho_chord_new(struct ChoContext *ctx) 2167 { 2168 struct ChoChord *chord = emalloc(sizeof(struct ChoChord)); 2169 chord->style = cho_style_new_default(ctx); 2170 chord->name = NULL; 2171 chord->is_canonical = false; 2172 chord->root = NULL; 2173 chord->qual = -1; 2174 chord->ext = NULL; 2175 chord->bass = NULL; 2176 chord->display = NULL; 2177 return chord; 2178 } 2179 2180 /* INFO: copy every field except for 'style' */ 2181 static void 2182 cho_chord_complete(struct ChoChord *first, struct ChoChord *second) 2183 { 2184 first->name = strdup(second->name); 2185 first->is_canonical = second->is_canonical; 2186 first->qual = second->qual; 2187 if (second->root) { 2188 first->root = strdup(second->root); 2189 } 2190 if (second->ext) { 2191 first->ext = strdup(second->ext); 2192 } 2193 if (second->bass) { 2194 first->bass = strdup(second->bass); 2195 } 2196 } 2197 2198 2199 static void 2200 cho_chord_free(struct ChoChord *chord) 2201 { 2202 if (!chord) { 2203 return; 2204 } 2205 cho_style_free(chord->style); 2206 free(chord->name); 2207 free(chord->root); 2208 free(chord->ext); 2209 free(chord->bass); 2210 free(chord->display); 2211 free(chord); 2212 } 2213 2214 static struct ChoChord * 2215 cho_chord_copy(struct ChoChord *chord) 2216 { 2217 struct ChoChord *copy = emalloc(sizeof(struct ChoChord)); 2218 copy->style = cho_style_copy(chord->style); 2219 copy->is_canonical = chord->is_canonical; 2220 copy->qual = chord->qual; 2221 copy->name = strdup(chord->name); 2222 copy->root = chord->root ? strdup(chord->root) : NULL; 2223 copy->ext = chord->ext ? strdup(chord->ext) : NULL; 2224 copy->bass = chord->bass ? strdup(chord->bass) : NULL; 2225 copy->display = chord->display ? strdup(chord->display) : NULL; 2226 return copy; 2227 } 2228 2229 bool 2230 cho_chords_has(struct ChoChord **chords, struct ChoChord *chord) 2231 { 2232 if (!chords) { 2233 return false; 2234 } 2235 struct ChoChord **c; 2236 for (c = chords; *c; c++) { 2237 if ( 2238 !str_compare((*c)->name, chord->name) && 2239 !str_compare((*c)->root, chord->root) && 2240 !str_compare((*c)->ext, chord->ext) && 2241 !str_compare((*c)->bass, chord->bass) && 2242 (*c)->qual == chord->qual 2243 ) { 2244 return true; 2245 } 2246 } 2247 return false; 2248 } 2249 2250 int 2251 cho_chord_compare(const void *a, const void *b) 2252 { 2253 struct ChoChord **aa = (struct ChoChord **)a; 2254 struct ChoChord **bb = (struct ChoChord **)b; 2255 return str_compare((*aa)->root, (*bb)->root); 2256 } 2257 2258 size_t 2259 cho_chord_count(struct ChoChord **chords) 2260 { 2261 if (!chords) { 2262 return 0; 2263 } 2264 int i; 2265 for (i = 0; chords[i]; i++); 2266 return i; 2267 } 2268 2269 void 2270 cho_chords_add(struct ChoChord ***chords, struct ChoChord *chord) 2271 { 2272 int i = 0; 2273 if (*chords) { 2274 struct ChoChord **c; 2275 for (c = *chords; *c; c++); 2276 i = (int)(c - *chords); 2277 } 2278 *chords = erealloc(*chords, (i+2) * sizeof(struct ChoChord *)); 2279 (*chords)[i] = cho_chord_copy(chord); 2280 (*chords)[i+1] = NULL; 2281 } 2282 2283 void 2284 cho_chords_free(struct ChoChord **chords) 2285 { 2286 if (!chords) { 2287 return; 2288 } 2289 struct ChoChord **c; 2290 for (c = chords; *c; c++) { 2291 cho_chord_free(*c); 2292 } 2293 free(chords); 2294 } 2295 2296 /* returns how many bytes make up the root; returns 0 if no root was found */ 2297 static int 2298 cho_chord_root_parse(struct ChoContext *ctx, const char *str, struct ChoChord *chord) 2299 { 2300 const char *note = NULL; 2301 const char *sharp = NULL; 2302 const char *flat = NULL; 2303 char *transposed_root; 2304 int i; 2305 for (i = 0; i<7; i++) { 2306 sharp = ctx->config->parser->notes[i]->sharp; 2307 if (sharp && str_starts_with(str, sharp)) { 2308 transposed_root = transposition_calc_chord_root(ctx, i, NOTE_TYPE_SHARP); 2309 if (!transposed_root) { 2310 DEBUG("transposition_calc_chord_root failed."); 2311 return 0; 2312 } 2313 chord->root = transposed_root; 2314 return strlen(sharp); 2315 } 2316 flat = ctx->config->parser->notes[i]->flat; 2317 if (flat && str_starts_with(str, flat)) { 2318 transposed_root = transposition_calc_chord_root(ctx, i, NOTE_TYPE_FLAT); 2319 if (!transposed_root) { 2320 DEBUG("transposition_calc_chord_root failed."); 2321 return 0; 2322 } 2323 chord->root = transposed_root; 2324 return strlen(flat); 2325 } 2326 note = ctx->config->parser->notes[i]->note; 2327 if (str_starts_with(str, note)) { 2328 transposed_root = transposition_calc_chord_root(ctx, i, NOTE_TYPE_NOTE); 2329 if (!transposed_root) { 2330 DEBUG("transposition_calc_chord_root failed."); 2331 return 0; 2332 } 2333 chord->root = transposed_root; 2334 return strlen(note); 2335 } 2336 } 2337 return 0; 2338 } 2339 2340 static char * 2341 cho_chord_qualifier_strip(const char *str) 2342 { 2343 if (str_starts_with(str, "m") || str_starts_with(str, "-")) { 2344 return strdup((char *)&str[1]); 2345 } 2346 return strdup(str); 2347 } 2348 2349 static int 2350 cho_chord_qualifier_and_extension_parse(const char *str, struct ChoChord *chord) 2351 { 2352 int i; 2353 for (i = 0; chord_extensions_major[i]; i++) { 2354 if (str_starts_with(str, chord_extensions_major[i])) { 2355 chord->ext = strdup(chord_extensions_major[i]); 2356 chord->qual = CHORD_QUALIFIER_MAJ; 2357 return strlen(chord_extensions_major[i]); 2358 } 2359 } 2360 for (i = 0; chord_extensions_minor[i]; i++) { 2361 if (str_starts_with(str, chord_extensions_minor[i])) { 2362 chord->ext = cho_chord_qualifier_strip(chord_extensions_minor[i]); 2363 chord->qual = CHORD_QUALIFIER_MIN; 2364 return strlen(chord_extensions_minor[i]); 2365 } 2366 } 2367 if (str_starts_with(str, "aug")) { 2368 chord->qual = CHORD_QUALIFIER_AUG; 2369 return 3; 2370 } 2371 if (str_starts_with(str, "+")) { 2372 chord->qual = CHORD_QUALIFIER_AUG; 2373 return 1; 2374 } 2375 if (str_starts_with(str, "dim")) { 2376 chord->qual = CHORD_QUALIFIER_DIM; 2377 return 3; 2378 } 2379 if (str_starts_with(str, "0")) { 2380 chord->qual = CHORD_QUALIFIER_DIM; 2381 return 1; 2382 } 2383 if (str_starts_with(str, "ø")) { 2384 chord->qual = CHORD_QUALIFIER_DIM; 2385 return 1; 2386 } 2387 // TODO: What about 'ø', 'h', 'h7' and 'h9'? 2388 // TODO: What about extensions after 'aug', '+', 'dim', '0'? 2389 return 0; 2390 } 2391 2392 static int 2393 cho_chord_bass_parse(struct ChoContext *ctx, const char *str, struct ChoChord *chord) 2394 { 2395 if (str[0] == '/') { 2396 const char *note = NULL; 2397 const char *sharp = NULL; 2398 const char *flat = NULL; 2399 const char *out_note = NULL; 2400 const char *out_sharp = NULL; 2401 const char *out_flat = NULL; 2402 int i; 2403 for (i = 0; i<7; i++) { 2404 sharp = ctx->config->parser->notes[i]->sharp; 2405 out_sharp = ctx->config->output->notes[i]->sharp; 2406 if (sharp && !strcmp((char *)&str[1], sharp)) { 2407 chord->bass = strdup(out_sharp); 2408 return strlen(sharp) + 1; 2409 } 2410 flat = ctx->config->parser->notes[i]->flat; 2411 out_flat = ctx->config->output->notes[i]->flat; 2412 if (flat && !strcmp((char *)&str[1], flat)) { 2413 chord->bass = strdup(out_flat); 2414 return strlen(flat) + 1; 2415 } 2416 note = ctx->config->parser->notes[i]->note; 2417 out_note = ctx->config->parser->notes[i]->note; 2418 if (!strcmp((char *)&str[1], note)) { 2419 chord->bass = strdup(out_note); 2420 return strlen(note) + 1; 2421 } 2422 } 2423 } 2424 return 0; 2425 } 2426 2427 static struct ChoChord * 2428 cho_chord_parse(struct ChoContext *ctx, const char *str) 2429 { 2430 struct ChoChord *chord = cho_chord_new(ctx); 2431 struct ChordDiagram *diagram; 2432 int i, ret; 2433 size_t str_len = strlen(str); 2434 size_t bytes_parsed = 0; 2435 2436 for (i = 0; i<ctx->dia; i++) { 2437 diagram = ctx->songs[ctx->so]->diagrams[i]; 2438 if ( 2439 diagram->type == CHORD_DIAGRAM_CONTENT_CHORD_MAP && 2440 !strcmp(str, diagram->u.cm->name) 2441 ) { 2442 chord->display = strdup(diagram->u.cm->display); 2443 } 2444 } 2445 chord->name = strdup(str); 2446 ret = cho_chord_root_parse(ctx, str, chord); 2447 if (ret == 0) { 2448 return chord; 2449 } 2450 chord->qual = CHORD_QUALIFIER_MAJ; 2451 bytes_parsed += ret; 2452 if (bytes_parsed == str_len) { 2453 chord->is_canonical = true; 2454 return chord; 2455 } 2456 ret = cho_chord_qualifier_and_extension_parse((const char *)&str[bytes_parsed], chord); 2457 bytes_parsed += ret; 2458 if (bytes_parsed == str_len) { 2459 chord->is_canonical = true; 2460 return chord; 2461 } 2462 ret = cho_chord_bass_parse(ctx, (const char *)&str[bytes_parsed], chord); 2463 bytes_parsed += ret; 2464 if (bytes_parsed == str_len) { 2465 chord->is_canonical = true; 2466 return chord; 2467 } 2468 return chord; 2469 } 2470 2471 char * 2472 cho_chord_name_generate_common(struct ChoChord *chord, struct Config *config) 2473 { 2474 struct Note **notes = config->output->notes; 2475 struct Note *notes_common; 2476 char *root = NULL; 2477 char *name = NULL; 2478 const char *qual; 2479 int i; 2480 int n = 0; 2481 size_t name_len = 0; 2482 2483 if (chord->is_canonical) { 2484 notes_common = config_notes_common(); 2485 for (i = 0; i<7; i++) { 2486 if (!strcmp(notes[i]->note, chord->root)) { 2487 root = notes_common[i].note; 2488 break; 2489 } 2490 if (notes[i]->sharp && !strcmp(notes[i]->sharp, chord->root)) { 2491 root = notes_common[i].sharp; 2492 break; 2493 } 2494 if (notes[i]->flat && !strcmp(notes[i]->flat, chord->root)) { 2495 root = notes_common[i].flat; 2496 break; 2497 } 2498 } 2499 free(notes_common); 2500 name_len += strlen(root); 2501 name = erealloc(name, name_len * sizeof(char)); 2502 for (i = 0; root[i]; i++, n++) { 2503 name[n] = root[i]; 2504 } 2505 qual = chord_qualifiers[chord->qual]; 2506 name_len += strlen(qual); 2507 name = erealloc(name, name_len * sizeof(char)); 2508 for (i = 0; qual[i]; i++, n++) { 2509 name[n] = qual[i]; 2510 } 2511 if (chord->ext) { 2512 name_len += strlen(chord->ext); 2513 name = erealloc(name, name_len * sizeof(char)); 2514 for (i = 0; chord->ext[i]; i++, n++) { 2515 name[n] = chord->ext[i]; 2516 } 2517 } 2518 if (chord->bass) { 2519 name_len++; 2520 name_len += strlen(chord->bass); 2521 name = erealloc(name, name_len * sizeof(char)); 2522 name[n] = '/'; 2523 n++; 2524 for (i = 0; chord->bass[i]; i++, n++) { 2525 name[n] = chord->bass[i]; 2526 } 2527 } 2528 name_len++; 2529 name = erealloc(name, name_len * sizeof(char)); 2530 name[n] = 0; 2531 return name; 2532 } 2533 return strdup(chord->name); 2534 } 2535 2536 char * 2537 cho_chord_name_generate(struct ChoChord *chord) 2538 { 2539 int n = 0; 2540 int i; 2541 char *name = NULL; 2542 const char *qual; 2543 size_t name_len = 0; 2544 2545 if (chord->display) { 2546 return strdup(chord->display); 2547 } 2548 if (chord->is_canonical) { 2549 name_len += strlen(chord->root); 2550 name = erealloc(name, name_len * sizeof(char)); 2551 for (i = 0; chord->root[i]; i++, n++) { 2552 name[n] = chord->root[i]; 2553 } 2554 qual = chord_qualifiers[chord->qual]; 2555 name_len += strlen(qual); 2556 name = erealloc(name, name_len * sizeof(char)); 2557 for (i = 0; qual[i]; i++, n++) { 2558 name[n] = qual[i]; 2559 } 2560 if (chord->ext) { 2561 name_len += strlen(chord->ext); 2562 name = erealloc(name, name_len * sizeof(char)); 2563 for (i = 0; chord->ext[i]; i++, n++) { 2564 name[n] = chord->ext[i]; 2565 } 2566 } 2567 if (chord->bass) { 2568 name_len++; 2569 name_len += strlen(chord->bass); 2570 name = erealloc(name, name_len * sizeof(char)); 2571 name[n] = '/'; 2572 n++; 2573 for (i = 0; chord->bass[i]; i++, n++) { 2574 name[n] = chord->bass[i]; 2575 } 2576 } 2577 name_len++; 2578 name = erealloc(name, name_len * sizeof(char)); 2579 name[n] = 0; 2580 return name; 2581 } 2582 return strdup(chord->name); 2583 } 2584 2585 static struct ChoImage * 2586 cho_image_new(void) 2587 { 2588 struct ChoImage *image = emalloc(sizeof(struct ChoImage)); 2589 image->is_asset = false; 2590 image->id = NULL; 2591 image->src = NULL; 2592 image->width = NULL; 2593 image->height = NULL; 2594 image->width_scale = NULL; 2595 image->height_scale = NULL; 2596 image->align = ALIGNMENT_CENTER; 2597 image->border = EMPTY_DOUBLE; 2598 image->spread_space = NULL; 2599 image->href = NULL; 2600 image->x = NULL; 2601 image->y = NULL; 2602 image->anchor = ANCHOR_FLOAT; 2603 image->dx = NULL; 2604 image->dy = NULL; 2605 image->w = NULL; 2606 image->h = NULL; 2607 image->bbox = false; 2608 return image; 2609 } 2610 2611 static void 2612 cho_image_free(struct ChoImage *image) 2613 { 2614 if (!image) { 2615 return; 2616 } 2617 free(image->id); 2618 free(image->src); 2619 free(image->width); 2620 free(image->height); 2621 free(image->width_scale); 2622 free(image->height_scale); 2623 free(image->spread_space); 2624 free(image->href); 2625 free(image->x); 2626 free(image->y); 2627 free(image->dx); 2628 free(image->dy); 2629 free(image->w); 2630 free(image->h); 2631 free(image); 2632 } 2633 2634 static struct ChoImage * 2635 cho_image_copy(struct ChoImage *image) 2636 { 2637 struct ChoImage *copy = emalloc(sizeof(struct ChoImage)); 2638 copy->is_asset = image->is_asset; 2639 copy->id = strdup(image->id); 2640 copy->src = strdup(image->src); 2641 copy->width = size_copy(image->width); 2642 copy->height = size_copy(image->height); 2643 copy->width_scale = size_copy(image->width_scale); 2644 copy->height_scale = size_copy(image->height_scale); 2645 copy->align = image->align; 2646 copy->border = image->border; 2647 copy->spread_space = size_copy(image->spread_space); 2648 copy->href = strdup(image->href); 2649 copy->x = size_copy(image->x); 2650 copy->y = size_copy(image->y); 2651 copy->anchor = image->anchor; 2652 copy->dx = size_copy(image->dx); 2653 copy->dy = size_copy(image->dy); 2654 copy->w = size_copy(image->w); 2655 copy->h = size_copy(image->h); 2656 copy->bbox = image->bbox; 2657 return copy; 2658 } 2659 2660 static struct ChoImage * 2661 cho_image_find_asset(struct ChoContext *ctx, const char *id) 2662 { 2663 int i; 2664 2665 for (i = 0; i<ctx->ia; i++) { 2666 if (!strcmp(ctx->image_assets[i]->id, id)) { 2667 return ctx->image_assets[i]; 2668 } 2669 } 2670 return NULL; 2671 } 2672 2673 #ifdef ENABLE_DEBUG 2674 void 2675 cho_debug_image_print(struct ChoImage *image) 2676 { 2677 printf("---- BEGIN IMAGE ----\n"); 2678 printf("is_asset: %d\n", image->is_asset); 2679 if (image->id) { 2680 printf("id: %s\n", image->id); 2681 } else { 2682 printf("id: NULL\n"); 2683 } 2684 if (image->src) { 2685 printf("src: %s\n", image->src); 2686 } else { 2687 printf("src: NULL\n"); 2688 } 2689 if (image->width) { 2690 printf("width: %s\n", size_to_string(image->width)); 2691 } else { 2692 printf("width: NULL\n"); 2693 } 2694 if (image->height) { 2695 printf("height: %s\n", size_to_string(image->height)); 2696 } else { 2697 printf("height: NULL\n"); 2698 } 2699 if (image->width_scale) { 2700 printf("width_scale: %s\n", size_to_string(image->width_scale)); 2701 } else { 2702 printf("width_scale: NULL\n"); 2703 } 2704 if (image->height_scale) { 2705 printf("height_scale: %s\n", size_to_string(image->height_scale)); 2706 } else { 2707 printf("height_scale: NULL\n"); 2708 } 2709 printf("align: %s\n", alignment_enums[image->align]); 2710 printf("border: %.1f\n", image->border); 2711 if (image->spread_space) { 2712 printf("spread_space: %s\n", size_to_string(image->spread_space)); 2713 } else { 2714 printf("spread_space: NULL\n"); 2715 } 2716 if (image->href) { 2717 printf("href: %s\n", image->href); 2718 } else { 2719 printf("href: NULL\n"); 2720 } 2721 if (image->x) { 2722 printf("x: %s\n", size_to_string(image->x)); 2723 } else { 2724 printf("x: NULL\n"); 2725 } 2726 if (image->y) { 2727 printf("y: %s\n", size_to_string(image->y)); 2728 } else { 2729 printf("y: NULL\n"); 2730 } 2731 printf("anchor: %s\n", anchor_enums[image->anchor]); 2732 if (image->dx) { 2733 printf("dx: %s\n", size_to_string(image->dx)); 2734 } else { 2735 printf("dx: NULL\n"); 2736 } 2737 if (image->dy) { 2738 printf("dy: %s\n", size_to_string(image->dy)); 2739 } else { 2740 printf("dy: NULL\n"); 2741 } 2742 if (image->w) { 2743 printf("w: %s\n", size_to_string(image->w)); 2744 } else { 2745 printf("w: NULL\n"); 2746 } 2747 if (image->h) { 2748 printf("h: %s\n", size_to_string(image->h)); 2749 } else { 2750 printf("h: NULL\n"); 2751 } 2752 printf("bbox: %d\n", image->bbox); 2753 printf("---- END IMAGE ------\n"); 2754 } 2755 #endif /* ENABLE_DEBUG */ 2756 2757 static bool 2758 cho_image_option_parse(struct ChoContext *ctx, struct ChoImage *image, const char *name, const char *value) 2759 { 2760 char *endptr; 2761 struct Size *size = NULL; 2762 2763 if (!strcmp(name, "id")) { 2764 image->id = strdup(value); 2765 } else 2766 if (!strcmp(name, "src")) { 2767 image->src = filepath_resolve_tilde(value); 2768 if (!image->src) { 2769 DEBUG("filepath_resolve_tilde failed."); 2770 goto ERR; 2771 } 2772 } else 2773 if (!strcmp(name, "width")) { 2774 size = size_create(value); 2775 if (!size) { 2776 cho_log(ctx, LOG_ERR, "Invalid value in option 'width' in image directive."); 2777 goto ERR; 2778 } 2779 if (size->type == SIZE_TYPE_EM || size->type == SIZE_TYPE_EX) { 2780 cho_log(ctx, LOG_ERR, "Invalid type of value in option 'width' in image directive. Allowed types are: points, percentage."); 2781 goto ERR; 2782 } 2783 image->width = size; 2784 } else 2785 if (!strcmp(name, "height")) { 2786 size = size_create(value); 2787 if (!size) { 2788 cho_log(ctx, LOG_ERR, "Invalid value in option 'height' in image directive."); 2789 goto ERR; 2790 } 2791 if (size->type == SIZE_TYPE_EM || size->type == SIZE_TYPE_EX) { 2792 cho_log(ctx, LOG_ERR, "Invalid type of value in option 'height' in image directive. Allowed types are: point, percentage."); 2793 goto ERR; 2794 } 2795 image->height = size; 2796 } else 2797 if (!strcmp(name, "scale")) { 2798 char *comma; 2799 if ((comma = strchr(value, ','))) { 2800 *comma = 0; 2801 size = size_create(value); 2802 if (!size) { 2803 cho_log(ctx, LOG_ERR, "Invalid value in option 'scale' in image directive."); 2804 goto ERR; 2805 } 2806 if (size->type == SIZE_TYPE_EM || size->type == SIZE_TYPE_EX) { 2807 cho_log(ctx, LOG_ERR, "Invalid type of value in option 'scale' in image directive. Allowed types are: point, percentage."); 2808 goto ERR; 2809 } 2810 image->width_scale = size; 2811 size = size_create(++comma); 2812 if (!size) { 2813 cho_log(ctx, LOG_ERR, "Invalid value in option 'scale' in image directive."); 2814 goto ERR; 2815 } 2816 if (size->type == SIZE_TYPE_EM || size->type == SIZE_TYPE_EX) { 2817 cho_log(ctx, LOG_ERR, "Invalid type of value in option 'scale' in image directive. Allowed types are: point, percentage."); 2818 goto ERR; 2819 } 2820 image->height_scale = size; 2821 } else { 2822 size = size_create(value); 2823 if (!size) { 2824 cho_log(ctx, LOG_ERR, "Invalid value in option 'scale' in image directive."); 2825 goto ERR; 2826 } 2827 if (size->type == SIZE_TYPE_EM || size->type == SIZE_TYPE_EX) { 2828 cho_log(ctx, LOG_ERR, "Invalid type of value in option 'scale' in image directive. Allowed types are: point, percentage."); 2829 goto ERR; 2830 } 2831 image->width_scale = size; 2832 image->height_scale = size_copy(size); 2833 } 2834 } else 2835 if (!strcmp(name, "align")) { 2836 if (!strcmp(value, "left")) { 2837 image->align = ALIGNMENT_LEFT; 2838 } else 2839 if (!strcmp(value, "right")) { 2840 image->align = ALIGNMENT_RIGHT; 2841 } else 2842 if (!strcmp(value, "center")) { 2843 image->align = ALIGNMENT_CENTER; 2844 } else { 2845 cho_log(ctx, LOG_ERR, "Invalid value in option 'align' in image directive."); 2846 goto ERR; 2847 } 2848 } else 2849 if (!strcmp(name, "border")) { 2850 image->border = strtod(value, &endptr); 2851 if (value == endptr || errno == ERANGE) { 2852 DEBUG("strtod failed."); 2853 goto ERR; 2854 } 2855 } else 2856 if (!strcmp(name, "spread")) { 2857 size = size_create(value); 2858 if (!size) { 2859 cho_log(ctx, LOG_ERR, "Invalid value in option 'spread' in image directive."); 2860 goto ERR; 2861 } 2862 if (size->type == SIZE_TYPE_EM || size->type == SIZE_TYPE_EX) { 2863 cho_log(ctx, LOG_ERR, "Invalid type of value in option 'spread' in image directive. Allowed types are: point, percentage."); 2864 goto ERR; 2865 } 2866 image->spread_space = size; 2867 } else 2868 if (!strcmp(name, "href")) { 2869 image->href = strdup(value); 2870 } else 2871 if (!strcmp(name, "x")) { 2872 size = size_create(value); 2873 if (!size) { 2874 cho_log(ctx, LOG_ERR, "Invalid value in option 'x' in image directive."); 2875 goto ERR; 2876 } 2877 if (size->type == SIZE_TYPE_EM || size->type == SIZE_TYPE_EX) { 2878 cho_log(ctx, LOG_ERR, "Invalid type of value in option 'x' in image directive. Allowed types are: point, percentage."); 2879 goto ERR; 2880 } 2881 image->x = size; 2882 } else 2883 if (!strcmp(name, "y")) { 2884 size = size_create(value); 2885 if (!size) { 2886 cho_log(ctx, LOG_ERR, "Invalid value in option 'y' in image directive."); 2887 goto ERR; 2888 } 2889 if (size->type == SIZE_TYPE_EM || size->type == SIZE_TYPE_EX) { 2890 cho_log(ctx, LOG_ERR, "Invalid type of value in option 'y' in image directive. Allowed types are: point, percentage."); 2891 goto ERR; 2892 } 2893 image->y = size; 2894 } else 2895 if (!strcmp(name, "anchor")) { 2896 if (!strcmp(value, "paper")) { 2897 image->anchor = ANCHOR_PAPER; 2898 } else 2899 if (!strcmp(value, "page")) { 2900 image->anchor = ANCHOR_PAGE; 2901 } else 2902 if (!strcmp(value, "column")) { 2903 image->anchor = ANCHOR_COLUMN; 2904 } else 2905 if (!strcmp(value, "line")) { 2906 image->anchor = ANCHOR_LINE; 2907 } else 2908 if (!strcmp(value, "float")) { 2909 image->anchor = ANCHOR_FLOAT; 2910 } else { 2911 cho_log(ctx, LOG_ERR, "Invalid value in option 'anchor' in image directive."); 2912 goto ERR; 2913 } 2914 } 2915 return true; 2916 ERR: 2917 free(size); 2918 return false; 2919 } 2920 2921 static struct ChoImage * 2922 cho_image_directive_parse(struct ChoContext *ctx, const char *str) 2923 { 2924 struct ChoImage *image = cho_image_new(); 2925 struct ChoImage *asset; 2926 enum OptionState state = OPTION_STATE_NAME; 2927 enum AttrValueSyntax avs = ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED; 2928 char c; 2929 char name[6+1]; 2930 char value[URL_MAX_LEN+1]; 2931 int n = 0; 2932 int v = 0; 2933 int option_count = 0; 2934 int i; 2935 2936 memset(name, 0, sizeof(name)); 2937 memset(value, 0, sizeof(value)); 2938 for (i = 0; str[i]; i++) { 2939 c = str[i]; 2940 switch (state) { 2941 case OPTION_STATE_NAME: 2942 if (is_whitespace(c)) { 2943 if (n == 0) { 2944 break; 2945 } else { 2946 name[n] = 0; 2947 cho_log(ctx, LOG_ERR, "Option with name '%s' in image directive has no value.", name); 2948 goto ERR; 2949 } 2950 } 2951 if (c == '=') { 2952 name[n] = 0; 2953 state = OPTION_STATE_VALUE; 2954 break; 2955 } 2956 if (n > 5) { 2957 cho_log(ctx, LOG_ERR, "Option name in image directive is too long."); 2958 goto ERR; 2959 } 2960 name[n] = c; 2961 n++; 2962 break; 2963 case OPTION_STATE_VALUE: 2964 if (avs == ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED) { 2965 if (is_whitespace(c)) { 2966 cho_log(ctx, LOG_ERR, "Whitespace character after equals sign in image directive is invalid."); 2967 goto ERR; 2968 } 2969 if (c == '\'') { 2970 avs = ATTRIBUTE_VALUE_SYNTAX_APOSTROPHE; 2971 } else if (c == '"') { 2972 avs = ATTRIBUTE_VALUE_SYNTAX_QUOTATION_MARK; 2973 } else { 2974 avs = ATTRIBUTE_VALUE_SYNTAX_UNQUOTED; 2975 value[v] = c; 2976 v++; 2977 } 2978 break; 2979 } 2980 if (c == '\n') { 2981 cho_log(ctx, LOG_ERR, "Newline character inside an option value in image directive is invalid."); 2982 goto ERR; 2983 } 2984 if ( 2985 (avs == ATTRIBUTE_VALUE_SYNTAX_APOSTROPHE && c == '\'') || 2986 (avs == ATTRIBUTE_VALUE_SYNTAX_QUOTATION_MARK && c == '"') || 2987 (avs == ATTRIBUTE_VALUE_SYNTAX_UNQUOTED && (c == ' ' || c == '\t')) 2988 ) { 2989 value[v] = 0; 2990 if (!cho_image_option_parse(ctx, image, name, value)) { 2991 DEBUG("cho_image_option_parse failed."); 2992 goto ERR; 2993 } 2994 option_count++; 2995 memset(name, 0, n); 2996 memset(value, 0, v); 2997 n = 0; 2998 v = 0; 2999 avs = ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED; 3000 state = OPTION_STATE_NAME; 3001 break; 3002 } 3003 value[v] = c; 3004 v++; 3005 break; 3006 } 3007 } 3008 if (avs == ATTRIBUTE_VALUE_SYNTAX_UNQUOTED) { 3009 value[v] = 0; 3010 if (!cho_image_option_parse(ctx, image, name, value)) { 3011 DEBUG("cho_image_option_parse failed."); 3012 goto ERR; 3013 } 3014 option_count++; 3015 } 3016 if (image->id) { 3017 if (image->src) { 3018 if (option_count > 2) { 3019 cho_log(ctx, LOG_ERR, "Defining an image asset disallows any options other than 'id' and 'src'."); 3020 goto ERR; 3021 } 3022 image->is_asset = true; 3023 } else { 3024 asset = cho_image_find_asset(ctx, image->id); 3025 if (!asset) { 3026 cho_log(ctx, LOG_ERR, "There is no image asset with the id '%s'.", image->id); 3027 goto ERR; 3028 } 3029 image->src = strdup(asset->src); 3030 } 3031 } 3032 return image; 3033 ERR: 3034 cho_image_free(image); 3035 return NULL; 3036 } 3037 3038 static struct ChoImage * 3039 cho_image_tag_parse(struct ChoContext *ctx, struct Attr **attrs) 3040 { 3041 struct ChoImage *image = cho_image_new(); 3042 struct ChoImage *asset; 3043 struct Size *size = NULL; 3044 int a; 3045 3046 for (a = 0; attrs[a]; a++) { 3047 if (!strcmp(attrs[a]->name, "src")) { 3048 image->src = filepath_resolve_tilde(attrs[a]->value); 3049 if (!image->src) { 3050 DEBUG("filepath_resolve_tilde failed."); 3051 goto ERR; 3052 } 3053 } else 3054 if (!strcmp(attrs[a]->name, "id")) { 3055 image->id = strdup(attrs[a]->value); 3056 } else 3057 if (!strcmp(attrs[a]->name, "width")) { 3058 size = size_create(attrs[a]->value); 3059 if (!size) { 3060 cho_log(ctx, LOG_ERR, "Invalid value in attribute 'width' in 'img' tag."); 3061 goto ERR; 3062 } 3063 if (size->type == SIZE_TYPE_PERCENT) { 3064 cho_log(ctx, LOG_ERR, "Invalid type of value in attribute 'width' in 'img' tag. Allowed types are: point, em, ex."); 3065 goto ERR; 3066 } 3067 image->width = size; 3068 } else 3069 if (!strcmp(attrs[a]->name, "height")) { 3070 size = size_create(attrs[a]->value); 3071 if (!size) { 3072 cho_log(ctx, LOG_ERR, "Invalid value in attribute 'height' in 'img' tag."); 3073 goto ERR; 3074 } 3075 if (size->type == SIZE_TYPE_PERCENT) { 3076 cho_log(ctx, LOG_ERR, "Invalid type of value in attribute 'height' in 'img' tag. Allowed types are: point, em, ex."); 3077 goto ERR; 3078 } 3079 image->height = size; 3080 } else 3081 if (!strcmp(attrs[a]->name, "dx")) { 3082 size = size_create(attrs[a]->value); 3083 if (!size) { 3084 cho_log(ctx, LOG_ERR, "Invalid value in attribute 'dx' in 'img' tag."); 3085 goto ERR; 3086 } 3087 if (size->type == SIZE_TYPE_PERCENT) { 3088 cho_log(ctx, LOG_ERR, "Invalid type of value in attribute 'dx' in 'img' tag. Allowed types are: point, em, ex."); 3089 goto ERR; 3090 } 3091 image->dx = size; 3092 } else 3093 if (!strcmp(attrs[a]->name, "dy")) { 3094 size = size_create(attrs[a]->value); 3095 if (!size) { 3096 cho_log(ctx, LOG_ERR, "Invalid value in attribute 'dy' in 'img' tag."); 3097 goto ERR; 3098 } 3099 if (size->type == SIZE_TYPE_PERCENT) { 3100 cho_log(ctx, LOG_ERR, "Invalid type of value in attribute 'dy' in 'img' tag. Allowed types are: point, em, ex."); 3101 goto ERR; 3102 } 3103 image->dy = size; 3104 } else 3105 if (!strcmp(attrs[a]->name, "scale")) { 3106 char *comma; 3107 if ((comma = strchr(attrs[a]->value, ','))) { 3108 *comma = 0; 3109 size = size_create(attrs[a]->value); 3110 if (!size) { 3111 cho_log(ctx, LOG_ERR, "Invalid value in attribute 'scale' in 'img' tag."); 3112 goto ERR; 3113 } 3114 if (size->type == SIZE_TYPE_EM || size->type == SIZE_TYPE_EX) { 3115 cho_log(ctx, LOG_ERR, "Invalid type of value in attribute 'scale' in 'img' tag. Allowed types are: point, percent"); 3116 goto ERR; 3117 } 3118 image->width_scale = size; 3119 size = size_create(++comma); 3120 if (!size) { 3121 cho_log(ctx, LOG_ERR, "Invalid value in attribute 'scale' in 'img' tag."); 3122 goto ERR; 3123 } 3124 if (size->type == SIZE_TYPE_EM || size->type == SIZE_TYPE_EX) { 3125 cho_log(ctx, LOG_ERR, "Invalid type of value in attribute 'scale' in 'img' tag. Allowed types are: point, percent"); 3126 goto ERR; 3127 } 3128 image->height_scale = size; 3129 } else { 3130 size = size_create(attrs[a]->value); 3131 if (!size) { 3132 cho_log(ctx, LOG_ERR, "Invalid value in attribute 'scale' in 'img' tag."); 3133 goto ERR; 3134 } 3135 if (size->type == SIZE_TYPE_EM || size->type == SIZE_TYPE_EX) { 3136 cho_log(ctx, LOG_ERR, "Invalid type of value in attribute 'scale' in 'img' tag. Allowed types are: point, percent"); 3137 goto ERR; 3138 } 3139 image->width_scale = size; 3140 image->height_scale = size_copy(size); 3141 } 3142 } else 3143 if (!strcmp(attrs[a]->name, "align")) { 3144 if (!strcmp(attrs[a]->value, "left")) { 3145 image->align = ALIGNMENT_LEFT; 3146 } else 3147 if (!strcmp(attrs[a]->value, "right")) { 3148 image->align = ALIGNMENT_RIGHT; 3149 } else 3150 if (!strcmp(attrs[a]->value, "center")) { 3151 image->align = ALIGNMENT_CENTER; 3152 } else { 3153 cho_log(ctx, LOG_ERR, "Invalid value in attribute 'align' in 'img' tag."); 3154 goto ERR; 3155 } 3156 } else 3157 if (!strcmp(attrs[a]->name, "bbox")) { 3158 if (!strcmp(attrs[a]->value, "1")) { 3159 image->bbox = true; 3160 } else 3161 if (!strcmp(attrs[a]->value, "0")) { 3162 image->bbox = false; 3163 } else { 3164 cho_log(ctx, LOG_ERR, "Invalid value in attribute 'bbox' in 'img' tag."); 3165 goto ERR; 3166 } 3167 } else 3168 if (!strcmp(attrs[a]->name, "w")) { 3169 size = size_create(attrs[a]->value); 3170 if (!size) { 3171 cho_log(ctx, LOG_ERR, "Invalid value in attribute 'w' in 'img' tag."); 3172 goto ERR; 3173 } 3174 if (size->type != SIZE_TYPE_POINT) { 3175 cho_log(ctx, LOG_ERR, "Invalid type of value in attribute 'w' in 'img' tag. Allowed type is: point"); 3176 goto ERR; 3177 } 3178 image->w = size; 3179 } else 3180 if (!strcmp(attrs[a]->name, "h")) { 3181 size = size_create(attrs[a]->value); 3182 if (!size) { 3183 cho_log(ctx, LOG_ERR, "Invalid value in attribute 'h' in 'img' tag."); 3184 goto ERR; 3185 } 3186 if (size->type != SIZE_TYPE_POINT) { 3187 cho_log(ctx, LOG_ERR, "Invalid type of value in attribute 'h' in 'img' tag. Allowed type is: point"); 3188 goto ERR; 3189 } 3190 image->h = size; 3191 } else { 3192 cho_log(ctx, LOG_ERR, "Invalid attribute '%s' in 'img' tag.", attrs[a]->name); 3193 goto ERR; 3194 } 3195 } 3196 if (image->id) { 3197 if (image->src) { 3198 cho_log(ctx, LOG_ERR, "'img' tag can't have both attributes 'id' and 'src' at the same time."); 3199 goto ERR; 3200 } else { 3201 asset = cho_image_find_asset(ctx, image->id); 3202 if (!asset) { 3203 cho_log(ctx, LOG_ERR, "There is no image asset with the id '%s'.", image->id); 3204 goto ERR; 3205 } 3206 image->src = strdup(asset->src); 3207 } 3208 } else { 3209 if (!image->src) { 3210 cho_log(ctx, LOG_ERR, "'img' tag has to have at least either the attribute 'id' or 'src'."); 3211 goto ERR; 3212 } 3213 } 3214 return image; 3215 ERR: 3216 cho_image_free(image); 3217 free(size); 3218 return NULL; 3219 } 3220 3221 static int8_t 3222 char_to_positive_int(char c) 3223 { 3224 if (c >= '0' && c <= '9') { 3225 return c - 48; 3226 } 3227 return -1; 3228 } 3229 3230 static int8_t 3231 finger_to_int(char c) 3232 { 3233 if (c == '-' || c == 'x' || c == 'X' || c == 'N') { 3234 return -1; 3235 } 3236 if (c >= '0' && c <= '9') { 3237 return c - 48; 3238 } 3239 if (c >= 'A' && c <= 'Z') { 3240 return c - 64; 3241 } 3242 return -2; 3243 } 3244 3245 static struct ChordDiagram * 3246 cho_chord_diagram_parse( 3247 struct ChoContext *ctx, 3248 const char *str, 3249 struct ChordDiagram **custom_diagrams, 3250 int custom_diagrams_len 3251 ) 3252 { 3253 struct ChordDiagram *diagram = chord_diagram_new(); 3254 enum ChordDiagramState state = CHORD_DIAGRAM_STATE_NAME; 3255 enum ChordDiagramContent current_content = CHORD_DIAGRAM_CONTENT_UNINITIALIZED; 3256 enum ChordDiagramContent future_content = CHORD_DIAGRAM_CONTENT_UNINITIALIZED; 3257 bool is_maybe_minus_one = false; 3258 char name[20]; 3259 char option[10]; 3260 char key[3]; 3261 char base_fret[3]; 3262 char diagram_value[7+1]; 3263 char chord_to_copy[20]; 3264 char display[20]; 3265 int i = 0; 3266 int f = 0; 3267 int fret_count = 0; 3268 int finger_count = 0; 3269 int8_t number = -2; 3270 long l; 3271 const char *c; 3272 3273 for (c = str; *c; c++){ 3274 // printf("c '%c' state '%d'\n", c, state); 3275 switch (state) { 3276 case CHORD_DIAGRAM_STATE_NAME: { 3277 if (is_whitespace(*c)) { 3278 if (i == 0) { 3279 break; 3280 } 3281 name[i] = 0; 3282 i = 0; 3283 state = CHORD_DIAGRAM_STATE_OPTION_NAME; 3284 break; 3285 } 3286 if (i > 18) { 3287 cho_log(ctx, LOG_ERR, "Chord name in chord diagram is too long."); 3288 goto ERR; 3289 } 3290 name[i] = *c; 3291 i++; 3292 break; 3293 } 3294 case CHORD_DIAGRAM_STATE_OPTION_NAME: { 3295 if (is_whitespace(*c)) { 3296 if (i == 0) { 3297 break; 3298 } 3299 option[i] = 0; 3300 future_content = CHORD_DIAGRAM_CONTENT_UNINITIALIZED; 3301 if (!strcmp(option, "base-fret")) { 3302 state = CHORD_DIAGRAM_STATE_BASE_FRET; 3303 future_content = CHORD_DIAGRAM_CONTENT_STRING; 3304 } else 3305 if (!strcmp(option, "frets")) { 3306 state = CHORD_DIAGRAM_STATE_FRETS; 3307 future_content = CHORD_DIAGRAM_CONTENT_STRING; 3308 } else 3309 if (!strcmp(option, "fingers")) { 3310 state = CHORD_DIAGRAM_STATE_FINGERS; 3311 future_content = CHORD_DIAGRAM_CONTENT_STRING; 3312 } else 3313 if (!strcmp(option, "keys")) { 3314 state = CHORD_DIAGRAM_STATE_KEYS; 3315 future_content = CHORD_DIAGRAM_CONTENT_KEYBOARD; 3316 } else 3317 if (!strcmp(option, "diagram")) { 3318 state = CHORD_DIAGRAM_STATE_DIAGRAM; 3319 } else 3320 if (!strcmp(option, "copy")) { 3321 state = CHORD_DIAGRAM_STATE_COPY; 3322 } else 3323 if (!strcmp(option, "display")) { 3324 state = CHORD_DIAGRAM_STATE_DISPLAY; 3325 future_content = CHORD_DIAGRAM_CONTENT_CHORD_MAP; 3326 } else { 3327 cho_log(ctx, LOG_ERR, "Invalid option '%s' in define directive.", option); 3328 goto ERR; 3329 } 3330 memset(option, 0, i); 3331 i = 0; 3332 if ( 3333 current_content == CHORD_DIAGRAM_CONTENT_UNINITIALIZED && 3334 future_content != CHORD_DIAGRAM_CONTENT_UNINITIALIZED 3335 ) { 3336 current_content = future_content; 3337 switch (future_content) { 3338 case CHORD_DIAGRAM_CONTENT_STRING: 3339 diagram->type = CHORD_DIAGRAM_CONTENT_STRING; 3340 diagram->u.sd = string_diagram_new(); 3341 diagram->u.sd->name = strdup(name); 3342 break; 3343 case CHORD_DIAGRAM_CONTENT_KEYBOARD: 3344 diagram->type = CHORD_DIAGRAM_CONTENT_KEYBOARD; 3345 diagram->u.kd = keyboard_diagram_new(); 3346 diagram->u.kd->name = strdup(name); 3347 break; 3348 case CHORD_DIAGRAM_CONTENT_CHORD_MAP: 3349 diagram->type = CHORD_DIAGRAM_CONTENT_CHORD_MAP; 3350 diagram->u.cm = chord_map_new(); 3351 diagram->u.cm->name = strdup(name); 3352 break; 3353 default: 3354 DEBUG("'future_content' cannot be empty at this point.\n"); 3355 goto ERR; 3356 } 3357 } 3358 break; 3359 } 3360 if (i > 8) { 3361 cho_log(ctx, LOG_ERR, "Option in chord diagram is too long."); 3362 goto ERR; 3363 } 3364 option[i] = *c; 3365 i++; 3366 break; 3367 } 3368 case CHORD_DIAGRAM_STATE_BASE_FRET: { 3369 if (is_whitespace(*c)) { 3370 if (i == 0) { 3371 break; 3372 } 3373 base_fret[i] = 0; 3374 i = 0; 3375 l = str_to_number(base_fret); 3376 if (l == -1) { 3377 DEBUG("str_to_number failed."); 3378 cho_log(ctx, LOG_ERR, "Invalid base-fret value '%s' in chord diagram.", base_fret); 3379 goto ERR; 3380 } 3381 if (l == 0) { 3382 cho_log(ctx, LOG_ERR, "Invalid base-fret value '%s' in chord diagram.", base_fret); 3383 goto ERR; 3384 } 3385 diagram->u.sd->base_fret = (int8_t)l; 3386 state = CHORD_DIAGRAM_STATE_OPTION_NAME; 3387 break; 3388 } 3389 if (i > 1) { 3390 cho_log(ctx, LOG_ERR, "base-fret value is too long."); 3391 goto ERR; 3392 } 3393 base_fret[i] = *c; 3394 i++; 3395 break; 3396 } 3397 case CHORD_DIAGRAM_STATE_FRETS: { 3398 number = -2; 3399 if (is_whitespace(*c)) { 3400 break; 3401 } 3402 if (*c == '-') { 3403 is_maybe_minus_one = true; 3404 break; 3405 } 3406 if (is_maybe_minus_one) { 3407 if (*c == '1') { 3408 number = -1; 3409 is_maybe_minus_one = false; 3410 } else { 3411 cho_log(ctx, LOG_ERR, "Invalid frets value '-%c' in chord diagram.", *c); 3412 goto ERR; 3413 } 3414 } 3415 if (*c == 'N' || *c == 'x') { 3416 number = -1; 3417 } else 3418 if (isalpha(*c)) { 3419 f = 0; 3420 state = CHORD_DIAGRAM_STATE_OPTION_NAME; 3421 c--; 3422 break; 3423 } 3424 if (number == -2) { 3425 number = char_to_positive_int(*c); 3426 if (number == -1) { 3427 DEBUG("char_to_positive_int failed."); 3428 cho_log(ctx, LOG_ERR, "Invalid frets value '%c' in chord diagram.", *c); 3429 goto ERR; 3430 } 3431 } 3432 if (f > 11) { 3433 cho_log(ctx, LOG_ERR, "Too many fret values in chord diagram."); 3434 goto ERR; 3435 } 3436 diagram->u.sd->frets[f] = number; 3437 f++; 3438 fret_count++; 3439 break; 3440 } 3441 case CHORD_DIAGRAM_STATE_FINGERS: { 3442 if (is_whitespace(*c)) { 3443 break; 3444 } 3445 if (*c == 'b' || *c == 'f') { 3446 f = 0; 3447 state = CHORD_DIAGRAM_STATE_OPTION_NAME; 3448 c--; 3449 break; 3450 } 3451 number = finger_to_int(*c); 3452 if (number == -2) { 3453 cho_log(ctx, LOG_ERR, "Invalid fingers value '%c' in chord diagram.", *c); 3454 goto ERR; 3455 } 3456 if (f > 11) { 3457 cho_log(ctx, LOG_ERR, "Too many finger values in chord diagram."); 3458 goto ERR; 3459 } 3460 diagram->u.sd->fingers[f] = number; 3461 f++; 3462 finger_count++; 3463 break; 3464 } 3465 case CHORD_DIAGRAM_STATE_KEYS: { 3466 if (is_whitespace(*c)) { 3467 if (i == 0) { 3468 break; 3469 } 3470 key[i] = 0; 3471 i = 0; 3472 l = str_to_number(key); 3473 if (l == -1) { 3474 DEBUG("str_to_number failed."); 3475 cho_log(ctx, LOG_ERR, "Invalid number in keys in chord diagram."); 3476 goto ERR; 3477 } 3478 if (f > 23) { 3479 cho_log(ctx, LOG_ERR, "Too many key values in chord diagram."); 3480 goto ERR; 3481 } 3482 diagram->u.kd->keys[f] = (int8_t)l; 3483 f++; 3484 break; 3485 } 3486 if (isalpha(*c)) { 3487 state = CHORD_DIAGRAM_STATE_OPTION_NAME; 3488 c--; 3489 break; 3490 } 3491 if (i > 1) { 3492 cho_log(ctx, LOG_ERR, "Too high key value in chord diagram. '%d'", i); 3493 goto ERR; 3494 } 3495 key[i] = *c; 3496 i++; 3497 break; 3498 } 3499 case CHORD_DIAGRAM_STATE_DIAGRAM: { 3500 if (is_whitespace(*c)) { 3501 if (i == 0) { 3502 break; 3503 } 3504 diagram_value[i] = 0; 3505 i = 0; 3506 if (!strcmp(diagram_value, "off")) { 3507 diagram->show = false; 3508 } else 3509 if (!strcmp(diagram_value, "on")) { 3510 // INFO: but this is already the default 3511 diagram->show = true; 3512 } else { 3513 diagram->color = cho_color_parse(diagram_value); 3514 if (!diagram->color) { 3515 DEBUG("cho_color_parse failed."); 3516 goto ERR; 3517 } 3518 } 3519 state = CHORD_DIAGRAM_STATE_OPTION_NAME; 3520 break; 3521 } 3522 diagram_value[i] = *c; 3523 i++; 3524 break; 3525 } 3526 case CHORD_DIAGRAM_STATE_COPY: { 3527 if (is_whitespace(*c)) { 3528 if (i == 0) { 3529 break; 3530 } 3531 chord_to_copy[i] = 0; 3532 if (current_content != CHORD_DIAGRAM_CONTENT_UNINITIALIZED) { 3533 cho_log(ctx, LOG_ERR, "The define options 'base-fret', 'frets', 'fingers' and 'keys' are not allowed before the 'copy' option."); 3534 goto ERR; 3535 } 3536 enum Instrument ins = ctx->config->output->diagram->instrument; 3537 current_content = chord_diagram_duplicate(diagram, custom_diagrams, custom_diagrams_len, name, chord_to_copy, ins); 3538 if (current_content == CHORD_DIAGRAM_CONTENT_UNINITIALIZED) { 3539 cho_log(ctx, LOG_ERR, "Can't copy the diagram for the chord '%s'" 3540 "because no previous definition was found and also" 3541 "no predefined chord diagram for the instrument '%s'" 3542 "was found.", chord_to_copy, instruments[ins].name); 3543 goto ERR; 3544 } 3545 i = 0; 3546 state = CHORD_DIAGRAM_STATE_OPTION_NAME; 3547 break; 3548 } 3549 chord_to_copy[i] = *c; 3550 i++; 3551 break; 3552 } 3553 case CHORD_DIAGRAM_STATE_DISPLAY: { 3554 if (is_whitespace(*c)) { 3555 display[i] = 0; 3556 i = 0; 3557 diagram->u.cm->display = strdup(display); 3558 break; 3559 } 3560 display[i] = *c; 3561 i++; 3562 break; 3563 } 3564 } 3565 } 3566 switch (state) { 3567 case CHORD_DIAGRAM_STATE_BASE_FRET: { 3568 base_fret[i] = 0; 3569 i = 0; 3570 l = str_to_number(base_fret); 3571 if (l == -1) { 3572 DEBUG("str_to_number failed."); 3573 cho_log(ctx, LOG_ERR, "Invalid base-fret value '%s' in chord diagram.", base_fret); 3574 goto ERR; 3575 } 3576 if (l == 0) { 3577 cho_log(ctx, LOG_ERR, "Invalid base-fret value '%s' in chord diagram.", base_fret); 3578 goto ERR; 3579 } 3580 diagram->u.sd->base_fret = (int8_t)l; 3581 break; 3582 } 3583 case CHORD_DIAGRAM_STATE_KEYS: { 3584 key[i] = 0; 3585 if (strlen(key) > 0) { 3586 l = str_to_number(key); 3587 if (l == -1) { 3588 DEBUG("str_to_number failed."); 3589 cho_log(ctx, LOG_ERR, "Invalid number in keys in chord diagram."); 3590 goto ERR; 3591 } 3592 if (f > 23) { 3593 cho_log(ctx, LOG_ERR, "Too many key values in chord diagram."); 3594 goto ERR; 3595 } 3596 diagram->u.kd->keys[f] = (int8_t)l; 3597 } 3598 break; 3599 } 3600 case CHORD_DIAGRAM_STATE_DIAGRAM: { 3601 diagram_value[i] = 0; 3602 if (!strcmp(diagram_value, "off")) { 3603 diagram->show = false; 3604 } else 3605 if (!strcmp(diagram_value, "on")) { 3606 // INFO: but this is already the default 3607 diagram->show = true; 3608 } else { 3609 free(diagram->color); 3610 diagram->color = cho_color_parse(diagram_value); 3611 if (!diagram->color) { 3612 DEBUG("cho_color_parse failed."); 3613 goto ERR; 3614 } 3615 } 3616 break; 3617 } 3618 case CHORD_DIAGRAM_STATE_COPY: { 3619 chord_to_copy[i] = 0; 3620 if (current_content != CHORD_DIAGRAM_CONTENT_UNINITIALIZED) { 3621 cho_log(ctx, LOG_ERR, "The define options 'base-fret', 'frets'," 3622 "'fingers' and 'keys' are not allowed before the 'copy'" 3623 "option."); 3624 goto ERR; 3625 } 3626 enum Instrument ins = ctx->config->output->diagram->instrument; 3627 current_content = chord_diagram_duplicate(diagram, custom_diagrams, custom_diagrams_len, name, chord_to_copy, ins); 3628 if (current_content == CHORD_DIAGRAM_CONTENT_UNINITIALIZED) { 3629 cho_log(ctx, LOG_ERR, "Can't copy the diagram for the chord '%s' because" 3630 "no previous definition was found and also no predefined" 3631 "chord diagram for the instrument '%s' was found.", 3632 chord_to_copy, instruments[ins].name); 3633 goto ERR; 3634 } 3635 break; 3636 } 3637 case CHORD_DIAGRAM_STATE_DISPLAY: { 3638 display[i] = 0; 3639 diagram->u.cm->display = strdup(display); 3640 break; 3641 } 3642 default: 3643 } 3644 if ( 3645 current_content == CHORD_DIAGRAM_CONTENT_STRING && 3646 fret_count > 0 && 3647 finger_count > 0 && 3648 fret_count != finger_count 3649 ) { 3650 cho_log(ctx, LOG_ERR, "The number of frets (%d) and fingers (%d) in the chord diagram must be equal.", fret_count, finger_count); 3651 goto ERR; 3652 } 3653 if (current_content == CHORD_DIAGRAM_CONTENT_UNINITIALIZED) { 3654 cho_log(ctx, LOG_ERR, "The chord diagram is invalid."); 3655 goto ERR; 3656 } 3657 return diagram; 3658 ERR: 3659 chord_diagram_free(diagram); 3660 return NULL; 3661 } 3662 3663 static struct ChoText * 3664 cho_text_new(struct ChoContext *ctx) 3665 { 3666 struct ChoText *text = emalloc(sizeof(struct ChoText)); 3667 text->style = cho_style_new_default(ctx); 3668 text->text = NULL; 3669 return text; 3670 } 3671 3672 static void 3673 cho_text_free(struct ChoText *text) 3674 { 3675 if (!text) { 3676 return; 3677 } 3678 cho_style_free(text->style); 3679 free(text->text); 3680 free(text); 3681 } 3682 3683 void 3684 cho_texts_free(struct ChoText **texts) 3685 { 3686 if (!texts) { 3687 return; 3688 } 3689 struct ChoText **t; 3690 for (t = texts; *t; t++) { 3691 cho_text_free(*t); 3692 } 3693 free(texts); 3694 } 3695 3696 static struct ChoText * 3697 cho_text_copy(struct ChoText *text) 3698 { 3699 struct ChoText *copy = emalloc(sizeof(struct ChoText)); 3700 copy->style = cho_style_copy(text->style); 3701 copy->text = strdup(text->text); 3702 return copy; 3703 } 3704 3705 static void 3706 cho_text_above_free(struct ChoLineItemAbove *text_above) 3707 { 3708 if (!text_above) { 3709 return; 3710 } 3711 if (text_above->is_chord) { 3712 cho_chord_free(text_above->u.chord); 3713 } else { 3714 cho_text_free(text_above->u.annot); 3715 } 3716 free(text_above); 3717 } 3718 3719 static struct ChoLineItemAbove * 3720 cho_text_above_copy(struct ChoLineItemAbove *text_above) 3721 { 3722 struct ChoLineItemAbove *copy = emalloc(sizeof(struct ChoLineItemAbove)); 3723 copy->position = text_above->position; 3724 copy->is_chord = text_above->is_chord; 3725 if (copy->is_chord) { 3726 copy->u.chord = cho_chord_copy(text_above->u.chord); 3727 } else { 3728 copy->u.annot = cho_text_copy(text_above->u.annot); 3729 } 3730 return copy; 3731 } 3732 3733 static struct ChoLineItem * 3734 cho_line_item_new(struct ChoContext *ctx) 3735 { 3736 struct ChoLineItem *item = emalloc(sizeof(struct ChoLineItem)); 3737 item->is_text = true; 3738 item->u.text = cho_text_new(ctx); 3739 return item; 3740 } 3741 3742 static void 3743 cho_line_item_free(struct ChoLineItem *item) 3744 { 3745 if (!item) { 3746 return; 3747 } 3748 if (item->is_text) { 3749 cho_text_free(item->u.text); 3750 } else { 3751 if (item->u.image) { 3752 cho_image_free(item->u.image); 3753 } 3754 } 3755 free(item); 3756 } 3757 3758 static struct ChoLineItem * 3759 cho_line_item_copy(struct ChoLineItem *item) 3760 { 3761 struct ChoLineItem *copy = emalloc(sizeof(struct ChoLineItem)); 3762 if (item->is_text) { 3763 copy->is_text = true; 3764 copy->u.text = cho_text_copy(item->u.text); 3765 } else { 3766 copy->is_text = false; 3767 copy->u.image = cho_image_copy(item->u.image); 3768 } 3769 return copy; 3770 } 3771 3772 static struct ChoLine * 3773 cho_line_new(void) 3774 { 3775 struct ChoLine *line = emalloc(sizeof(struct ChoLine)); 3776 line->text_above = NULL; 3777 line->items = NULL; 3778 line->btype = BREAK_TYPE_LINE; 3779 return line; 3780 } 3781 3782 static void 3783 cho_line_free(struct ChoLine *line) 3784 { 3785 if (!line) { 3786 return; 3787 } 3788 struct ChoLineItem **it; 3789 for (it = line->items; *it; it++) { 3790 cho_line_item_free(*it); 3791 } 3792 struct ChoLineItemAbove **above; 3793 for (above = line->text_above; *above; above++) { 3794 cho_text_above_free(*above); 3795 } 3796 free(line->items); 3797 free(line->text_above); 3798 free(line); 3799 } 3800 3801 static int 3802 cho_line_compute_text_above_position(struct ChoLine *line, int ly, int te) 3803 { 3804 if (ly == 0) { 3805 return te; 3806 } 3807 size_t lyrics_len = 0; 3808 int i; 3809 for (i = ly-1; i >= 0; i--) { 3810 if (line->items[i]->is_text) { 3811 lyrics_len += strlen(line->items[i]->u.text->text); 3812 } 3813 } 3814 return lyrics_len + te; 3815 } 3816 3817 static struct ChoSection * 3818 cho_section_new(void) 3819 { 3820 struct ChoSection *section = emalloc(sizeof(struct ChoSection)); 3821 section->type = SECTION_TYPE_UNINITIALIZED; 3822 section->label = NULL; 3823 section->lines = NULL; 3824 section->is_closed = false; 3825 return section; 3826 } 3827 3828 static void 3829 cho_section_free(struct ChoSection *section) 3830 { 3831 if (!section) { 3832 return; 3833 } 3834 if (section->label) { 3835 cho_text_free(section->label); 3836 } 3837 if (section->lines) { 3838 struct ChoLine **li; 3839 for (li = section->lines; *li; li++) { 3840 cho_line_free(*li); 3841 } 3842 free(section->lines); 3843 } 3844 free(section); 3845 } 3846 3847 static struct ChoSection * 3848 cho_section_copy(struct ChoSection *section) 3849 { 3850 struct ChoSection *copy = emalloc(sizeof(struct ChoSection)); 3851 copy->type = section->type; 3852 if (section->label) { 3853 copy->label = cho_text_copy(section->label); 3854 } else { 3855 copy->label = NULL; 3856 } 3857 copy->lines = NULL; 3858 int li, c, ly; 3859 for (li = 0; section->lines[li]; li++) { 3860 copy->lines = erealloc(copy->lines, (li+1) * sizeof(struct ChoLine *)); 3861 copy->lines[li] = cho_line_new(); 3862 copy->lines[li]->btype = section->lines[li]->btype; 3863 for (c = 0; section->lines[li]->text_above[c]; c++) { 3864 copy->lines[li]->text_above = erealloc(copy->lines[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); 3865 copy->lines[li]->text_above[c] = cho_text_above_copy(section->lines[li]->text_above[c]); 3866 } 3867 copy->lines[li]->text_above = erealloc(copy->lines[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); 3868 copy->lines[li]->text_above[c] = NULL; 3869 for (ly = 0; section->lines[li]->items[ly]; ly++) { 3870 copy->lines[li]->items = erealloc(copy->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); 3871 copy->lines[li]->items[ly] = cho_line_item_copy(section->lines[li]->items[ly]); 3872 } 3873 copy->lines[li]->items = erealloc(copy->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); 3874 copy->lines[li]->items[ly] = NULL; 3875 } 3876 copy->lines = erealloc(copy->lines, (li+1) * sizeof(struct ChoLine *)); 3877 copy->lines[li] = NULL; 3878 return copy; 3879 } 3880 3881 static struct ChoSong * 3882 cho_song_new(struct ChoContext *ctx) 3883 { 3884 struct ChoSong *song = emalloc(sizeof(struct ChoSong)); 3885 song->metadata = cho_metadata_load_default(ctx); 3886 if (!song->metadata) { 3887 DEBUG("cho_metadata_load_default failed."); 3888 free(song); 3889 return NULL; 3890 } 3891 song->sections = NULL; 3892 song->diagrams = NULL; 3893 memset(song->present_text_types, 0, TEXT_TYPE_LENGTH); 3894 return song; 3895 } 3896 3897 int 3898 cho_song_count(struct ChoSong **songs) 3899 { 3900 int i; 3901 for (i = 0; songs[i]; i++); 3902 return i; 3903 } 3904 3905 static const char * 3906 cho_song_get_title(struct ChoSong *song) 3907 { 3908 struct ChoMetadata **m; 3909 for (m = song->metadata; *m; m++) { 3910 if (!strcmp((*m)->name, "sorttitle")) { 3911 return (*m)->value; 3912 } 3913 } 3914 for (m = song->metadata; *m; m++) { 3915 if (!strcmp((*m)->name, "title")) { 3916 return (*m)->value; 3917 } 3918 } 3919 return NULL; 3920 } 3921 3922 int 3923 cho_song_compare(const void *a, const void *b) 3924 { 3925 struct ChoSong *song; 3926 const char *a_title, *b_title; 3927 3928 song = *(struct ChoSong **)a; 3929 a_title = cho_song_get_title(song); 3930 song = *(struct ChoSong **)b; 3931 b_title = cho_song_get_title(song); 3932 return str_compare(a_title, b_title); 3933 } 3934 3935 static void 3936 cho_song_free(struct ChoSong *song) 3937 { 3938 if (!song) { 3939 return; 3940 } 3941 struct ChoMetadata **m; 3942 struct ChoSection **se; 3943 struct ChordDiagram **dia; 3944 for (m = song->metadata; *m; m++) { 3945 cho_metadata_free(*m); 3946 } 3947 for (se = song->sections; *se; se++) { 3948 cho_section_free(*se); 3949 } 3950 for (dia = song->diagrams; *dia; dia++) { 3951 chord_diagram_free(*dia); 3952 } 3953 free(song->metadata); 3954 free(song->sections); 3955 free(song->diagrams); 3956 free(song); 3957 } 3958 3959 void 3960 cho_songs_free(struct ChoSong **songs) 3961 { 3962 if (!songs) { 3963 return; 3964 } 3965 struct ChoSong **so; 3966 for (so = songs; *so; so++) { 3967 cho_song_free(*so); 3968 } 3969 free(songs); 3970 } 3971 3972 static struct ChoSection * 3973 cho_find_previous_chorus(struct ChoSection **sections, int se) 3974 { 3975 int i; 3976 for (i = se; i>=0; i--) { 3977 if (sections[i]->type == SECTION_TYPE_CHORUS) { 3978 return sections[i]; 3979 } 3980 } 3981 return NULL; 3982 } 3983 3984 static struct ChoDirective * 3985 cho_directive_new(struct ChoContext *ctx) 3986 { 3987 struct ChoDirective *directive = emalloc(sizeof(struct ChoDirective)); 3988 directive->stype = SECTION_TYPE_UNINITIALIZED; 3989 directive->style = cho_style_new_default(ctx); 3990 return directive; 3991 } 3992 3993 static void 3994 cho_directive_free(struct ChoDirective *directive) 3995 { 3996 if (!directive) { 3997 return; 3998 } 3999 cho_style_free(directive->style); 4000 free(directive); 4001 } 4002 4003 static struct ChoDirective * 4004 cho_directive_parse(struct ChoContext *ctx, const char *name) 4005 { 4006 struct ChoDirective *directive = cho_directive_new(ctx); 4007 if ( 4008 !strcmp(name, "start_of_chorus") || 4009 !strcmp(name, "soc") 4010 ) { 4011 directive->dtype = DIRECTIVE_TYPE_ENVIRONMENT; 4012 directive->position = POSITION_START; 4013 directive->stype = SECTION_TYPE_CHORUS; 4014 directive->ttype = TEXT_TYPE_CHORUS; 4015 } else 4016 if ( 4017 !strcmp(name, "end_of_chorus") || 4018 !strcmp(name, "eoc") 4019 ) { 4020 directive->dtype = DIRECTIVE_TYPE_ENVIRONMENT; 4021 directive->position = POSITION_END; 4022 directive->stype = SECTION_TYPE_CHORUS; 4023 directive->ttype = TEXT_TYPE_TEXT; 4024 } else 4025 if (!strcmp(name, "chorus")) { 4026 directive->dtype = DIRECTIVE_TYPE_ENVIRONMENT; 4027 directive->position = POSITION_NO; 4028 directive->ttype = TEXT_TYPE_LABEL; 4029 } else 4030 if ( 4031 !strcmp(name, "start_of_verse") || 4032 !strcmp(name, "sov") 4033 ) { 4034 directive->dtype = DIRECTIVE_TYPE_ENVIRONMENT; 4035 directive->position = POSITION_START; 4036 directive->stype = SECTION_TYPE_VERSE; 4037 directive->ttype = TEXT_TYPE_TEXT; 4038 } else 4039 if ( 4040 !strcmp(name, "end_of_verse") || 4041 !strcmp(name, "eov") 4042 ) { 4043 directive->dtype = DIRECTIVE_TYPE_ENVIRONMENT; 4044 directive->position = POSITION_END; 4045 directive->stype = SECTION_TYPE_VERSE; 4046 directive->ttype = TEXT_TYPE_TEXT; 4047 } else 4048 if ( 4049 !strcmp(name, "start_of_bridge") || 4050 !strcmp(name, "sob") 4051 ) { 4052 directive->dtype = DIRECTIVE_TYPE_ENVIRONMENT; 4053 directive->position = POSITION_START; 4054 directive->stype = SECTION_TYPE_BRIDGE; 4055 directive->ttype = TEXT_TYPE_TEXT; 4056 } else 4057 if ( 4058 !strcmp(name, "end_of_bridge") || 4059 !strcmp(name, "eob") 4060 ) { 4061 directive->dtype = DIRECTIVE_TYPE_ENVIRONMENT; 4062 directive->position = POSITION_END; 4063 directive->stype = SECTION_TYPE_BRIDGE; 4064 directive->ttype = TEXT_TYPE_TEXT; 4065 } else 4066 if ( 4067 !strcmp(name, "start_of_tab") || 4068 !strcmp(name, "sot") 4069 ) { 4070 directive->dtype = DIRECTIVE_TYPE_ENVIRONMENT; 4071 directive->position = POSITION_START; 4072 directive->stype = SECTION_TYPE_TAB; 4073 directive->ttype = TEXT_TYPE_TAB; 4074 } else 4075 if ( 4076 !strcmp(name, "end_of_tab") || 4077 !strcmp(name, "eot") 4078 ) { 4079 directive->dtype = DIRECTIVE_TYPE_ENVIRONMENT; 4080 directive->position = POSITION_END; 4081 directive->stype = SECTION_TYPE_TAB; 4082 directive->ttype = TEXT_TYPE_TEXT; 4083 } else 4084 if ( 4085 !strcmp(name, "start_of_grid") || 4086 !strcmp(name, "sog") 4087 ) { 4088 directive->dtype = DIRECTIVE_TYPE_ENVIRONMENT; 4089 directive->position = POSITION_START; 4090 directive->stype = SECTION_TYPE_GRID; 4091 directive->ttype = TEXT_TYPE_GRID; 4092 } else 4093 if ( 4094 !strcmp(name, "end_of_grid") || 4095 !strcmp(name, "eog") 4096 ) { 4097 directive->dtype = DIRECTIVE_TYPE_ENVIRONMENT; 4098 directive->position = POSITION_END; 4099 directive->stype = SECTION_TYPE_GRID; 4100 directive->ttype = TEXT_TYPE_TEXT; 4101 } else 4102 if ( 4103 !strcmp(name, "title") || 4104 !strcmp(name, "t") 4105 ) { 4106 directive->dtype = DIRECTIVE_TYPE_METADATA; 4107 directive->meta = METADATA_DIRECTIVE_TITLE; 4108 cho_style_free(directive->style); 4109 ctx->prev_ttype = ctx->current_ttype; 4110 ctx->current_ttype = TEXT_TYPE_TITLE; 4111 directive->style = cho_style_new_default(ctx); 4112 ctx->current_ttype = ctx->prev_ttype; 4113 4114 } else 4115 if ( 4116 !strcmp(name, "subtitle") || 4117 !strcmp(name, "st") 4118 ) { 4119 directive->dtype = DIRECTIVE_TYPE_METADATA; 4120 directive->meta = METADATA_DIRECTIVE_SUBTITLE; 4121 cho_style_free(directive->style); 4122 ctx->prev_ttype = ctx->current_ttype; 4123 ctx->current_ttype = TEXT_TYPE_SUBTITLE; 4124 directive->style = cho_style_new_default(ctx); 4125 ctx->current_ttype = ctx->prev_ttype; 4126 } else 4127 if ( 4128 !strcmp(name, "sorttitle") || 4129 !strcmp(name, "artist") || 4130 !strcmp(name, "composer") || 4131 !strcmp(name, "lyricist") || 4132 !strcmp(name, "copyright") || 4133 !strcmp(name, "album") || 4134 !strcmp(name, "year") || 4135 !strcmp(name, "key") || 4136 !strcmp(name, "time") || 4137 !strcmp(name, "tempo") || 4138 !strcmp(name, "duration") || 4139 !strcmp(name, "capo") || 4140 !strcmp(name, "meta") || 4141 !strcmp(name, "arranger") 4142 ) { 4143 directive->dtype = DIRECTIVE_TYPE_METADATA; 4144 directive->meta = METADATA_DIRECTIVE_OTHER; 4145 } else 4146 if ( 4147 !strcmp(name, "comment") || 4148 !strcmp(name, "c") || 4149 !strcmp(name, "highlight") 4150 ) { 4151 directive->dtype = DIRECTIVE_TYPE_FORMATTING; 4152 ctx->prev_ttype = ctx->current_ttype; 4153 ctx->current_ttype = TEXT_TYPE_COMMENT; 4154 cho_style_free(directive->style); 4155 directive->style = cho_style_new_default(ctx); 4156 ctx->current_ttype = ctx->prev_ttype; 4157 directive->ttype = TEXT_TYPE_COMMENT; 4158 } else 4159 if ( 4160 !strcmp(name, "comment_italic") || 4161 !strcmp(name, "ci") 4162 ) { 4163 directive->dtype = DIRECTIVE_TYPE_FORMATTING; 4164 ctx->prev_ttype = ctx->current_ttype; 4165 ctx->current_ttype = TEXT_TYPE_COMMENT_ITALIC; 4166 cho_style_free(directive->style); 4167 directive->style = cho_style_new_default(ctx); 4168 ctx->current_ttype = ctx->prev_ttype; 4169 directive->ttype = TEXT_TYPE_COMMENT_ITALIC; 4170 } else 4171 if ( 4172 !strcmp(name, "comment_box") || 4173 !strcmp(name, "cb") 4174 ) { 4175 directive->dtype = DIRECTIVE_TYPE_FORMATTING; 4176 ctx->prev_ttype = ctx->current_ttype; 4177 ctx->current_ttype = TEXT_TYPE_COMMENT_BOX; 4178 cho_style_free(directive->style); 4179 directive->style = cho_style_new_default(ctx); 4180 ctx->current_ttype = ctx->prev_ttype; 4181 directive->ttype = TEXT_TYPE_COMMENT_BOX; 4182 } else 4183 if (!strcmp(name, "image")) { 4184 directive->dtype = DIRECTIVE_TYPE_IMAGE; 4185 } else 4186 if ( 4187 !strcmp(name, "new_song") || 4188 !strcmp(name, "ns") 4189 ) { 4190 directive->dtype = DIRECTIVE_TYPE_PREAMBLE; 4191 directive->stype = SECTION_TYPE_NEWSONG; 4192 } else 4193 if ( 4194 !strcmp(name, "chordfont") || 4195 !strcmp(name, "cf") 4196 ) { 4197 directive->dtype = DIRECTIVE_TYPE_FONT; 4198 directive->sprop = STYLE_PROPERTY_TYPE_FONT; 4199 directive->ttype = TEXT_TYPE_CHORD; 4200 } else 4201 if ( 4202 !strcmp(name, "chordsize") || 4203 !strcmp(name, "cs") 4204 ) { 4205 directive->dtype = DIRECTIVE_TYPE_FONT; 4206 directive->sprop = STYLE_PROPERTY_TYPE_SIZE; 4207 directive->ttype = TEXT_TYPE_CHORD; 4208 } else 4209 if (!strcmp(name, "chordcolour")) { 4210 directive->dtype = DIRECTIVE_TYPE_FONT; 4211 directive->sprop = STYLE_PROPERTY_TYPE_COLOR; 4212 directive->ttype = TEXT_TYPE_CHORD; 4213 } else 4214 if (!strcmp(name, "chorusfont")) { 4215 directive->dtype = DIRECTIVE_TYPE_FONT; 4216 directive->sprop = STYLE_PROPERTY_TYPE_FONT; 4217 directive->ttype = TEXT_TYPE_CHORUS; 4218 } else 4219 if (!strcmp(name, "chorussize")) { 4220 directive->dtype = DIRECTIVE_TYPE_FONT; 4221 directive->sprop = STYLE_PROPERTY_TYPE_SIZE; 4222 directive->ttype = TEXT_TYPE_CHORUS; 4223 } else 4224 if (!strcmp(name, "choruscolour")) { 4225 directive->dtype = DIRECTIVE_TYPE_FONT; 4226 directive->sprop = STYLE_PROPERTY_TYPE_COLOR; 4227 directive->ttype = TEXT_TYPE_CHORUS; 4228 } else 4229 if (!strcmp(name, "gridfont")) { 4230 directive->dtype = DIRECTIVE_TYPE_FONT; 4231 directive->sprop = STYLE_PROPERTY_TYPE_FONT; 4232 directive->ttype = TEXT_TYPE_GRID; 4233 } else 4234 if (!strcmp(name, "gridsize")) { 4235 directive->dtype = DIRECTIVE_TYPE_FONT; 4236 directive->sprop = STYLE_PROPERTY_TYPE_SIZE; 4237 directive->ttype = TEXT_TYPE_GRID; 4238 } else 4239 if (!strcmp(name, "gridcolour")) { 4240 directive->dtype = DIRECTIVE_TYPE_FONT; 4241 directive->sprop = STYLE_PROPERTY_TYPE_COLOR; 4242 directive->ttype = TEXT_TYPE_GRID; 4243 } else 4244 if (!strcmp(name, "tabfont")) { 4245 directive->dtype = DIRECTIVE_TYPE_FONT; 4246 directive->sprop = STYLE_PROPERTY_TYPE_FONT; 4247 directive->ttype = TEXT_TYPE_TAB; 4248 } else 4249 if (!strcmp(name, "tabsize")) { 4250 directive->dtype = DIRECTIVE_TYPE_FONT; 4251 directive->sprop = STYLE_PROPERTY_TYPE_SIZE; 4252 directive->ttype = TEXT_TYPE_TAB; 4253 } else 4254 if (!strcmp(name, "tabcolour")) { 4255 directive->dtype = DIRECTIVE_TYPE_FONT; 4256 directive->sprop = STYLE_PROPERTY_TYPE_COLOR; 4257 directive->ttype = TEXT_TYPE_TAB; 4258 } else 4259 if ( 4260 !strcmp(name, "textfont") || 4261 !strcmp(name, "tf") 4262 ) { 4263 directive->dtype = DIRECTIVE_TYPE_FONT; 4264 directive->sprop = STYLE_PROPERTY_TYPE_FONT; 4265 directive->ttype = TEXT_TYPE_TEXT; 4266 } else 4267 if ( 4268 !strcmp(name, "textsize") || 4269 !strcmp(name, "ts") 4270 ) { 4271 directive->dtype = DIRECTIVE_TYPE_FONT; 4272 directive->sprop = STYLE_PROPERTY_TYPE_SIZE; 4273 directive->ttype = TEXT_TYPE_TEXT; 4274 } else 4275 if (!strcmp(name, "textcolour")) { 4276 directive->dtype = DIRECTIVE_TYPE_FONT; 4277 directive->sprop = STYLE_PROPERTY_TYPE_COLOR; 4278 directive->ttype = TEXT_TYPE_TEXT; 4279 } else 4280 if (!strcmp(name, "titlefont")) { 4281 directive->dtype = DIRECTIVE_TYPE_FONT; 4282 directive->sprop = STYLE_PROPERTY_TYPE_FONT; 4283 directive->ttype = TEXT_TYPE_TITLE; 4284 } else 4285 if (!strcmp(name, "titlesize")) { 4286 directive->dtype = DIRECTIVE_TYPE_FONT; 4287 directive->sprop = STYLE_PROPERTY_TYPE_SIZE; 4288 directive->ttype = TEXT_TYPE_TITLE; 4289 } else 4290 if (!strcmp(name, "titlecolour")) { 4291 directive->dtype = DIRECTIVE_TYPE_FONT; 4292 directive->sprop = STYLE_PROPERTY_TYPE_COLOR; 4293 directive->ttype = TEXT_TYPE_TITLE; 4294 } else 4295 if (!strcmp(name, "tocfont")) { 4296 directive->dtype = DIRECTIVE_TYPE_FONT; 4297 directive->sprop = STYLE_PROPERTY_TYPE_FONT; 4298 directive->ttype = TEXT_TYPE_TOC; 4299 } else 4300 if (!strcmp(name, "tocsize")) { 4301 directive->dtype = DIRECTIVE_TYPE_FONT; 4302 directive->sprop = STYLE_PROPERTY_TYPE_SIZE; 4303 directive->ttype = TEXT_TYPE_TOC; 4304 } else 4305 if (!strcmp(name, "toccolour")) { 4306 directive->dtype = DIRECTIVE_TYPE_FONT; 4307 directive->sprop = STYLE_PROPERTY_TYPE_COLOR; 4308 directive->ttype = TEXT_TYPE_TOC; 4309 /* } else if (!strcmp(name, "footerfont")) { 4310 } else if (!strcmp(name, "footersize")) { 4311 } else if (!strcmp(name, "footercolour")) { */ 4312 } else 4313 if (!strcmp(name, "labelfont")) { 4314 directive->dtype = DIRECTIVE_TYPE_FONT; 4315 directive->sprop = STYLE_PROPERTY_TYPE_FONT; 4316 directive->ttype = TEXT_TYPE_LABEL; 4317 } else 4318 if (!strcmp(name, "labelsize")) { 4319 directive->dtype = DIRECTIVE_TYPE_FONT; 4320 directive->sprop = STYLE_PROPERTY_TYPE_SIZE; 4321 directive->ttype = TEXT_TYPE_LABEL; 4322 } else 4323 if (!strcmp(name, "labelcolour")) { 4324 directive->dtype = DIRECTIVE_TYPE_FONT; 4325 directive->sprop = STYLE_PROPERTY_TYPE_COLOR; 4326 directive->ttype = TEXT_TYPE_LABEL; 4327 } else 4328 if (!strcmp(name, "transpose")) { 4329 directive->dtype = DIRECTIVE_TYPE_CHORD; 4330 directive->ctype = CHORD_DIRECTIVE_TRANSPOSE; 4331 } else 4332 if (!strcmp(name, "define")) { 4333 directive->dtype = DIRECTIVE_TYPE_CHORD; 4334 directive->ctype = CHORD_DIRECTIVE_DEFINE; 4335 /* } else if (!strcmp(name, "chord")) { */ 4336 } else 4337 if ( 4338 !strcmp(name, "new_page") || 4339 !strcmp(name, "np") 4340 ) { 4341 directive->dtype = DIRECTIVE_TYPE_OUTPUT; 4342 directive->btype = BREAK_TYPE_PAGE; 4343 } else 4344 if ( 4345 !strcmp(name, "column_break") || 4346 !strcmp(name, "colb") 4347 ) { 4348 directive->dtype = DIRECTIVE_TYPE_OUTPUT; 4349 directive->btype = BREAK_TYPE_COLUMN; 4350 } else 4351 if (str_starts_with(name, "start_of_")) { 4352 directive->dtype = DIRECTIVE_TYPE_ENVIRONMENT; 4353 directive->position = POSITION_START; 4354 directive->stype = SECTION_TYPE_CUSTOM; 4355 directive->ttype = TEXT_TYPE_TEXT; 4356 } else 4357 if (str_starts_with(name, "end_of_")) { 4358 directive->dtype = DIRECTIVE_TYPE_ENVIRONMENT; 4359 directive->position = POSITION_END; 4360 directive->stype = SECTION_TYPE_CUSTOM; 4361 directive->ttype = TEXT_TYPE_TEXT; 4362 } else 4363 if (str_starts_with(name, "x_")) { 4364 directive->dtype = DIRECTIVE_TYPE_EXTENSION; 4365 } else { 4366 directive->dtype = DIRECTIVE_TYPE_CUSTOM; 4367 } 4368 return directive; 4369 } 4370 4371 static struct Attr ** 4372 cho_attrs_parse(struct ChoContext *ctx, const char *str, const char *directive_name) 4373 { 4374 struct Attr **attrs = NULL; 4375 int a = 0; 4376 enum OptionState state = OPTION_STATE_NAME; 4377 enum AttrValueSyntax avs = ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED; 4378 char name[5+1]; 4379 char value[URL_MAX_LEN+1]; 4380 int n = 0; 4381 int v = 0; 4382 memset(name, 0, sizeof(name)); 4383 memset(value, 0, sizeof(value)); 4384 const char *c; 4385 for (c = str; *c; c++) { 4386 switch (state) { 4387 case OPTION_STATE_NAME: 4388 if (is_whitespace(*c)) { 4389 if (n == 0) { 4390 break; 4391 } else { 4392 name[n] = 0; 4393 cho_log(ctx, LOG_ERR, "Option with name '%s' in environment directive '%s' has no value.", name, directive_name); 4394 goto ERR; 4395 } 4396 } 4397 if (*c == '=') { 4398 name[n] = 0; 4399 state = OPTION_STATE_VALUE; 4400 break; 4401 } 4402 if (n > 4) { 4403 cho_log(ctx, LOG_ERR, "Option name in environment directive '%s' is too long.", directive_name); 4404 goto ERR; 4405 } 4406 name[n] = *c; 4407 n++; 4408 break; 4409 case OPTION_STATE_VALUE: 4410 if (avs == ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED) { 4411 if (is_whitespace(*c)) { 4412 cho_log(ctx, LOG_ERR, "Whitespace character after equals sign in environment directive '%s' is invalid.", directive_name); 4413 goto ERR; 4414 } 4415 if (*c == '\'') { 4416 avs = ATTRIBUTE_VALUE_SYNTAX_APOSTROPHE; 4417 } else if (*c == '"') { 4418 avs = ATTRIBUTE_VALUE_SYNTAX_QUOTATION_MARK; 4419 } else { 4420 avs = ATTRIBUTE_VALUE_SYNTAX_UNQUOTED; 4421 value[v] = *c; 4422 v++; 4423 } 4424 break; 4425 } 4426 if (*c == '\n') { 4427 cho_log(ctx, LOG_ERR, "Newline character inside an option value in environment directive '%s' is invalid.", directive_name); 4428 goto ERR; 4429 } 4430 if ( 4431 (avs == ATTRIBUTE_VALUE_SYNTAX_APOSTROPHE && *c == '\'') || 4432 (avs == ATTRIBUTE_VALUE_SYNTAX_QUOTATION_MARK && *c == '"') || 4433 (avs == ATTRIBUTE_VALUE_SYNTAX_UNQUOTED && (*c == ' ' || *c == '\t')) 4434 ) { 4435 value[v] = 0; 4436 attrs = erealloc(attrs, (a+1) * sizeof(struct Attr *)); 4437 attrs[a] = emalloc(sizeof(struct Attr)); 4438 attrs[a]->name = strdup(name); 4439 attrs[a]->value = strdup(value); 4440 a++; 4441 memset(name, 0, n); 4442 n = 0; 4443 memset(value, 0, v); 4444 v = 0; 4445 avs = ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED; 4446 state = OPTION_STATE_NAME; 4447 break; 4448 } 4449 value[v] = *c; 4450 v++; 4451 break; 4452 } 4453 } 4454 if (avs == ATTRIBUTE_VALUE_SYNTAX_UNQUOTED) { 4455 value[v] = 0; 4456 attrs = erealloc(attrs, (a+1) * sizeof(struct Attr *)); 4457 attrs[a] = emalloc(sizeof(struct Attr)); 4458 attrs[a]->name = strdup(name); 4459 attrs[a]->value = strdup(value); 4460 a++; 4461 } 4462 attrs = erealloc(attrs, (a+1) * sizeof(struct Attr *)); 4463 attrs[a] = NULL; 4464 return attrs; 4465 ERR: 4466 for (n = 0; n<a; n++) { 4467 cho_tag_attr_free(attrs[n]); 4468 } 4469 free(attrs); 4470 return NULL; 4471 } 4472 4473 static char * 4474 cho_attrs_get(struct Attr **attrs, const char *name) 4475 { 4476 struct Attr **a; 4477 for (a = attrs; *a; a++) { 4478 if (!strcmp((*a)->name, name)) { 4479 return (*a)->value; 4480 } 4481 } 4482 return NULL; 4483 } 4484 4485 /* TODO: Only providing a label name like '{sog: Interlude}' is an error according to how I understand the spec. */ 4486 static bool 4487 cho_grid_shape_parse_and_set(struct ChoContext *ctx, const char *str) 4488 { 4489 enum { 4490 BEFORE_FIRST_PLUS, 4491 AFTER_FIRST_PLUS, 4492 AFTER_SECOND_PLUS, 4493 AFTER_X 4494 } state = BEFORE_FIRST_PLUS; 4495 const char *c; 4496 char tmp[6+1]; 4497 int t = 0; 4498 int measures = 0; 4499 int beats = 0; 4500 int i; 4501 for (c = str; *c; c++) { 4502 switch (state) { 4503 case BEFORE_FIRST_PLUS: 4504 if (*c == '+') { 4505 tmp[t] = 0; 4506 i = atoi(tmp); 4507 if (i == 0) { 4508 DEBUG("atoi failed."); 4509 return false; 4510 } 4511 ctx->grid.left = i; 4512 memset(tmp, 0, t); 4513 t = 0; 4514 state = AFTER_FIRST_PLUS; 4515 break; 4516 } 4517 if (*c == 'x') { 4518 tmp[t] = 0; 4519 i = atoi(tmp); 4520 if (i == 0) { 4521 DEBUG("atoi failed."); 4522 return false; 4523 } 4524 measures = i; 4525 memset(tmp, 0, t); 4526 t = 0; 4527 state = AFTER_X; 4528 break; 4529 } 4530 if (t > 5) { 4531 cho_log(ctx, LOG_ERR, "Failed to parse the grid shape '%s'.", str); 4532 return false; 4533 } 4534 tmp[t] = *c; 4535 t++; 4536 break; 4537 case AFTER_FIRST_PLUS: 4538 if (*c == '+') { 4539 tmp[t] = 0; 4540 i = atoi(tmp); 4541 if (i == 0) { 4542 DEBUG("atoi failed."); 4543 return false; 4544 } 4545 ctx->grid.cells = i; 4546 memset(tmp, 0, t); 4547 t = 0; 4548 state = AFTER_SECOND_PLUS; 4549 break; 4550 } 4551 if (*c == 'x') { 4552 tmp[t] = 0; 4553 i = atoi(tmp); 4554 if (i == 0) { 4555 DEBUG("atoi failed."); 4556 return false; 4557 } 4558 measures = i; 4559 memset(tmp, 0, t); 4560 t = 0; 4561 state = AFTER_X; 4562 break; 4563 } 4564 tmp[t] = *c; 4565 t++; 4566 break; 4567 case AFTER_SECOND_PLUS: 4568 tmp[t] = *c; 4569 t++; 4570 break; 4571 case AFTER_X: 4572 if (*c == '+') { 4573 tmp[t] = 0; 4574 i = atoi(tmp); 4575 if (i == 0) { 4576 DEBUG("atoi failed."); 4577 return false; 4578 } 4579 beats = i; 4580 ctx->grid.cells = measures * beats; 4581 memset(tmp, 0, t); 4582 t = 0; 4583 state = AFTER_SECOND_PLUS; 4584 break; 4585 } 4586 tmp[t] = *c; 4587 t++; 4588 break; 4589 } 4590 } 4591 tmp[t] = 0; 4592 i = atoi(tmp); 4593 if (i == 0) { 4594 DEBUG("atoi failed."); 4595 return false; 4596 } 4597 switch (state) { 4598 case BEFORE_FIRST_PLUS: 4599 /* fallthrough */ 4600 case AFTER_FIRST_PLUS: 4601 ctx->grid.cells = i; 4602 break; 4603 case AFTER_SECOND_PLUS: 4604 ctx->grid.right = i; 4605 break; 4606 case AFTER_X: 4607 beats = i; 4608 ctx->grid.cells = measures * beats; 4609 break; 4610 } 4611 return true; 4612 } 4613 4614 static enum GridToken 4615 cho_grid_token_parse(struct ChoContext *ctx, const char *token, bool *err) 4616 { 4617 struct ChoChord *chord; 4618 4619 *err = false; 4620 if (!strcmp(token, ".")) { 4621 return GRID_TOKEN_PLACEHOLDER; 4622 } else 4623 if (!strcmp(token, "/")) { 4624 return GRID_TOKEN_PLACEHOLDER; 4625 } else 4626 if (!strcmp(token, "|")) { 4627 return GRID_TOKEN_BAR_LINE_SYMBOL; 4628 } else 4629 if (!strcmp(token, "||")) { 4630 return GRID_TOKEN_BAR_LINE_SYMBOL; 4631 } else 4632 if (!strcmp(token, "|.")) { 4633 return GRID_TOKEN_BAR_LINE_SYMBOL; 4634 } else 4635 if (!strcmp(token, "|:")) { 4636 return GRID_TOKEN_BAR_LINE_SYMBOL; 4637 } else 4638 if (!strcmp(token, ":|")) { 4639 return GRID_TOKEN_BAR_LINE_SYMBOL; 4640 } else 4641 if (!strcmp(token, ":|:")) { 4642 return GRID_TOKEN_BAR_LINE_SYMBOL; 4643 } else 4644 if (token[0] == '|' && isdigit(token[1])) { 4645 return GRID_TOKEN_BAR_LINE_SYMBOL; 4646 } else 4647 if (!strcmp(token, "|2>")) { 4648 return GRID_TOKEN_BAR_LINE_SYMBOL; 4649 } 4650 chord = cho_chord_parse(ctx, token); 4651 if (chord->is_canonical) { 4652 cho_chord_free(chord); 4653 return GRID_TOKEN_CHORD; 4654 } 4655 cho_chord_free(chord); 4656 *err = true; 4657 return GRID_TOKEN_CHORD; // unused 4658 } 4659 4660 static void 4661 cho_songs_close(struct ChoContext *ctx, struct ChoLine ***lines) 4662 { 4663 if (!(*lines)[ctx->li]->items[ctx->lii]->u.text->text && !(*lines)[ctx->li]->text_above) { 4664 if (ctx->lii == 0) { 4665 cho_line_item_free((*lines)[ctx->li]->items[ctx->lii]); 4666 free((*lines)[ctx->li]->items); 4667 free((*lines)[ctx->li]); 4668 (*lines)[ctx->li] = NULL; 4669 } else { 4670 cho_line_item_free((*lines)[ctx->li]->items[ctx->lii]); 4671 (*lines)[ctx->li]->items[ctx->lii] = NULL; 4672 (*lines)[ctx->li]->text_above = erealloc((*lines)[ctx->li]->text_above, (ctx->lia+1) * sizeof(struct ChoLineItemAbove *)); 4673 (*lines)[ctx->li]->text_above[ctx->lia] = NULL; 4674 ctx->li++; 4675 *lines = erealloc(*lines, (ctx->li+1) * sizeof(struct ChoLine *)); 4676 (*lines)[ctx->li] = NULL; 4677 } 4678 } else { 4679 (*lines)[ctx->li]->items[ctx->lii]->u.text->text = erealloc((*lines)[ctx->li]->items[ctx->lii]->u.text->text, (ctx->te+1) * sizeof(char)); 4680 (*lines)[ctx->li]->items[ctx->lii]->u.text->text[ctx->te] = 0; 4681 ctx->lii++; 4682 (*lines)[ctx->li]->items = erealloc((*lines)[ctx->li]->items, (ctx->lii+1) * sizeof(struct ChoLineItem *)); 4683 (*lines)[ctx->li]->items[ctx->lii] = NULL; 4684 (*lines)[ctx->li]->text_above = erealloc((*lines)[ctx->li]->text_above, (ctx->lia+1) * sizeof(struct ChoLineItemAbove *)); 4685 (*lines)[ctx->li]->text_above[ctx->lia] = NULL; 4686 ctx->li++; 4687 *lines = erealloc(*lines, (ctx->li+1) * sizeof(struct ChoLine *)); 4688 (*lines)[ctx->li] = NULL; 4689 } 4690 ctx->songs[ctx->so]->metadata = erealloc(ctx->songs[ctx->so]->metadata, (ctx->m+1) * sizeof(struct ChoMetadata *)); 4691 ctx->songs[ctx->so]->metadata[ctx->m] = NULL; 4692 ctx->se++; 4693 ctx->songs[ctx->so]->sections = erealloc(ctx->songs[ctx->so]->sections, (ctx->se+1) * sizeof(struct ChoSection *)); 4694 ctx->songs[ctx->so]->sections[ctx->se] = NULL; 4695 ctx->songs[ctx->so]->diagrams = erealloc(ctx->songs[ctx->so]->diagrams, (ctx->dia+1) * sizeof(struct ChordDiagram *)); 4696 ctx->songs[ctx->so]->diagrams[ctx->dia] = NULL; 4697 ctx->so++; 4698 ctx->songs = erealloc(ctx->songs, (ctx->so+1) * sizeof(struct ChoSong *)); 4699 ctx->songs[ctx->so] = NULL; 4700 } 4701 4702 static bool 4703 cho_context_init( 4704 struct ChoContext *ctx, 4705 struct Config *config, 4706 const char *chordpro_filepath 4707 ) 4708 { 4709 ctx->is_chord_already_initialized = false; 4710 ctx->is_maybe_end_of_tab_directive = false; 4711 ctx->directive_has_tag = false; 4712 ctx->chordpro_filepath = chordpro_filepath; 4713 ctx->state = STATE_LYRICS; 4714 ctx->state_before_comment = STATE_LYRICS; 4715 ctx->state_before_tag = STATE_LYRICS; 4716 ctx->state_before_backslash = STATE_LYRICS; 4717 ctx->state_before_metadata_substitution = STATE_LYRICS; 4718 ctx->current_ttype = TEXT_TYPE_TEXT; 4719 ctx->prev_ttype = TEXT_TYPE_TEXT; 4720 ctx->ann = 0; 4721 ctx->at = 0; 4722 ctx->atn = 0; 4723 ctx->atv = 0; 4724 ctx->ch = 0; 4725 ctx->dia = 0; 4726 ctx->dn = 0; 4727 ctx->dv = 0; 4728 ctx->gt = 0; 4729 ctx->ia = 0; 4730 ctx->li = 0; 4731 ctx->lia = 0; 4732 ctx->lii = 0; 4733 ctx->m = 0; 4734 ctx->ms = 0; 4735 ctx->se = 0; 4736 ctx->so = 0; 4737 ctx->t = 0; 4738 ctx->ta = -1; 4739 ctx->te = 0; 4740 ctx->th = 0; 4741 ctx->nested_level = 0; 4742 ctx->line_no = 1; 4743 ctx->tags = NULL; 4744 ctx->image_assets = NULL; 4745 ctx->grid.cells = 8; 4746 ctx->grid.left = 1; 4747 ctx->grid.right = 1; 4748 ctx->grid.bar_line_symbol_count = 0; 4749 ctx->grid.bar_line_symbol_in_line_count = 0; 4750 ctx->grid.tokens_per_cell = 0; 4751 ctx->grid.expected_tokens_per_cell = 0; 4752 ctx->config = config; 4753 ctx->songs = emalloc(sizeof(struct ChoSong *)); 4754 ctx->songs[ctx->so] = cho_song_new(ctx); 4755 if (!ctx->songs[ctx->so]) { 4756 DEBUG("cho_song_new failed."); 4757 free(ctx->songs); 4758 return false; 4759 } 4760 ctx->transpose_history = emalloc((ctx->th+1) * sizeof(int *)); 4761 ctx->transpose_history[ctx->th] = 0; 4762 ctx->transpose = &ctx->transpose_history[ctx->th]; 4763 ctx->th++; 4764 ctx->songs[ctx->so]->sections = emalloc((ctx->se+1) * sizeof(struct ChoSection *)); 4765 ctx->songs[ctx->so]->sections[ctx->se] = cho_section_new(); 4766 // INFO: The first section has no start and end directive 4767 ctx->songs[ctx->so]->sections[ctx->se]->is_closed = true; 4768 ctx->songs[ctx->so]->sections[ctx->se]->lines = emalloc(sizeof(struct ChoLine *)); 4769 ctx->songs[ctx->so]->sections[ctx->se]->lines[ctx->li] = cho_line_new(); 4770 return true; 4771 } 4772 4773 static void 4774 cho_context_cleanup(struct ChoContext *ctx) 4775 { 4776 int i; 4777 4778 for (i = 0; i<=ctx->ta; i++) { 4779 cho_tag_free(ctx->tags[i]); 4780 } 4781 free(ctx->tags); 4782 free(ctx->transpose_history); 4783 for (i = 0; i<ctx->ia; i++) { 4784 cho_image_free(ctx->image_assets[i]); 4785 } 4786 free(ctx->image_assets); 4787 } 4788 4789 static bool 4790 is_previous_section_closed(struct ChoContext *ctx) 4791 { 4792 int i; 4793 4794 for (i = ctx->se; i>=0; i--) { 4795 if (ctx->songs[ctx->so]->sections[i]->type == SECTION_TYPE_UNINITIALIZED) { 4796 continue; 4797 } 4798 return ctx->songs[ctx->so]->sections[i]->is_closed ? true : false; 4799 } 4800 return true; 4801 } 4802 4803 struct ChoSong ** 4804 cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *config) 4805 { 4806 enum AttrValueSyntax avs = ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED; 4807 enum GridToken token; 4808 struct Attr **directive_attrs = NULL; 4809 struct ChoStyle *tag_style; 4810 struct StyleProperty sprop; 4811 struct ChoChord *tmp_chord; 4812 struct ChoSection *chorus; 4813 struct ChoImage *image; 4814 struct ChordDiagram *diagram; 4815 struct ChoDirective *directive = NULL; 4816 struct ChoMetadata *metadata = NULL; 4817 struct ChoLine ***lines; 4818 struct ChoContext ctx; 4819 bool err; 4820 char *metadata_value, *shape; 4821 char *stripped_directive_value = NULL; 4822 char *label = NULL; 4823 char directive_name[128]; 4824 char directive_value[4096]; 4825 char chord[CHORD_LEN]; 4826 char tag_start[6]; 4827 char tag_end[6]; 4828 char custom_directive[64]; 4829 char metadata_substitution[4096]; 4830 char grid_token[CHORD_LEN]; 4831 char c = 0; 4832 char prev_c = '\n'; 4833 int transpose; 4834 4835 if (!cho_context_init(&ctx, config, chordpro_filepath)) { 4836 DEBUG("cho_context_init failed."); 4837 return NULL; 4838 } 4839 4840 lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; 4841 (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); 4842 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 4843 ctx.songs[ctx.so]->present_text_types[TEXT_TYPE_TOC] = config->output->toc->show; 4844 for (; *str; str++) { 4845 c = *str; 4846 // printf("state: %s, c: %c\n", state_enums[ctx.state], c); 4847 if (c == '\r') { 4848 continue; 4849 } 4850 switch (ctx.state) { 4851 case STATE_LYRICS: { 4852 if (prev_c == '\n' && c == '#') { 4853 ctx.state_before_comment = STATE_LYRICS; 4854 ctx.state = STATE_COMMENT; 4855 break; 4856 } 4857 if (prev_c == '\n' && c == '{') { 4858 ctx.state = STATE_DIRECTIVE_NAME; 4859 break; 4860 } 4861 if (c == '[') { 4862 ctx.state = STATE_CHORD; 4863 break; 4864 } 4865 if (c == '<') { 4866 if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { 4867 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 4868 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; 4869 if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { 4870 cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); 4871 } else { 4872 ctx.lii++; 4873 } 4874 } else { 4875 ctx.lii++; 4876 } 4877 ctx.te = 0; 4878 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 4879 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 4880 ctx.state_before_tag = STATE_LYRICS; 4881 ctx.state = STATE_MARKUP_TAG; 4882 break; 4883 } 4884 if (c == '%') { 4885 ctx.state_before_metadata_substitution = STATE_LYRICS; 4886 ctx.state = STATE_MAYBE_METADATA_SUBSTITUTION; 4887 break; 4888 } 4889 if (c == '\n') { 4890 ctx.line_no++; 4891 if (prev_c == '\\') { 4892 ctx.state_before_backslash = STATE_LYRICS; 4893 ctx.state = STATE_BACKSLASH; 4894 ctx.te--; // INFO: This will later overwrite the backslash 4895 break; 4896 } 4897 if (ctx.ta > -1 && !ctx.tags[ctx.ta]->is_closed && strcmp(ctx.tags[ctx.ta]->name, "img")) { 4898 cho_log(&ctx, LOG_ERR, "Tag has to be closed on same line."); 4899 DEBUG("Tag has to be closed on same line."); 4900 goto ERR; 4901 } 4902 if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { 4903 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 4904 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; 4905 if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { 4906 cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); 4907 if (ctx.lii == 0) { 4908 if ( 4909 !(*lines)[ctx.li]->text_above && 4910 (*lines)[ctx.li]->btype == BREAK_TYPE_LINE 4911 ) { 4912 free((*lines)[ctx.li]->items); 4913 free((*lines)[ctx.li]); 4914 *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); 4915 (*lines)[ctx.li] = cho_line_new(); 4916 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 4917 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 4918 break; 4919 } 4920 } 4921 } else { 4922 ctx.lii++; 4923 } 4924 } else { 4925 ctx.lii++; 4926 } 4927 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 4928 (*lines)[ctx.li]->items[ctx.lii] = NULL; 4929 ctx.lii = 0; 4930 ctx.te = 0; 4931 (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); 4932 (*lines)[ctx.li]->text_above[ctx.lia] = NULL; 4933 ctx.lia = 0; 4934 ctx.li++; 4935 *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); 4936 (*lines)[ctx.li] = cho_line_new(); 4937 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 4938 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 4939 break; 4940 } 4941 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 4942 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = c; 4943 ctx.te++; 4944 break; 4945 } 4946 case STATE_BACKSLASH: { 4947 if (!is_whitespace(c)) { 4948 str--; 4949 ctx.state = ctx.state_before_backslash; 4950 break; 4951 } 4952 break; 4953 } 4954 case STATE_DIRECTIVE_NAME: { 4955 if (c == '}') { 4956 directive_name[ctx.dn] = 0; 4957 ctx.dn = 0; 4958 directive = cho_directive_parse(&ctx, directive_name); 4959 /* printf( 4960 "directive: '%s'\ndtype: %s, stype: %s, position: %s\n", 4961 directive_name, cho_debug_dtype(directive->dtype), cho_debug_the_stype(directive->stype), cho_debug_the_pos(directive->position) 4962 ); */ 4963 switch (directive->dtype) { 4964 case DIRECTIVE_TYPE_ENVIRONMENT: { 4965 ctx.current_ttype = directive->ttype; 4966 switch (directive->position) { 4967 case POSITION_START: { 4968 if (!is_previous_section_closed(&ctx)) { 4969 cho_log(&ctx, LOG_ERR, "Can't start a new section when the previous one is not yet closed."); 4970 goto ERR; 4971 } 4972 if (directive->stype == SECTION_TYPE_CUSTOM) { 4973 memset(custom_directive, 0, sizeof(custom_directive)); 4974 strcpy(custom_directive, &directive_name[9]); 4975 } 4976 cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); 4977 free((*lines)[ctx.li]->items); 4978 ctx.lii = 0; 4979 free((*lines)[ctx.li]); 4980 (*lines)[ctx.li] = NULL; 4981 ctx.se++; 4982 ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); 4983 ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); 4984 ctx.songs[ctx.so]->sections[ctx.se]->type = directive->stype; 4985 ctx.li = 0; 4986 lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; 4987 *lines = emalloc(sizeof(struct ChoLine *)); 4988 (*lines)[ctx.li] = cho_line_new(); 4989 (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); 4990 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 4991 ctx.songs[ctx.so]->present_text_types[directive->ttype] = true; 4992 switch (directive->stype) { 4993 case SECTION_TYPE_TAB: 4994 ctx.state = STATE_TAB; 4995 break; 4996 case SECTION_TYPE_GRID: 4997 ctx.state = STATE_GRID; 4998 break; 4999 default: 5000 ctx.state = STATE_LYRICS; 5001 } 5002 break; 5003 } 5004 case POSITION_END: { 5005 if (directive->stype == ctx.songs[ctx.so]->sections[ctx.se]->type) { 5006 if (directive->stype == SECTION_TYPE_CUSTOM) { 5007 if (strcmp(custom_directive, &directive_name[7]) != 0) { 5008 break; 5009 } 5010 } 5011 if (directive->stype == SECTION_TYPE_GRID) { 5012 ctx.grid.bar_line_symbol_count = 0; 5013 ctx.grid.tokens_per_cell = 0; 5014 ctx.grid.expected_tokens_per_cell = 0; 5015 } 5016 cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); 5017 free((*lines)[ctx.li]->items); 5018 ctx.lii = 0; 5019 free((*lines)[ctx.li]); 5020 (*lines)[ctx.li] = NULL; 5021 ctx.songs[ctx.so]->sections[ctx.se]->is_closed = true; 5022 ctx.se++; 5023 ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); 5024 ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); 5025 ctx.li = 0; 5026 lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; 5027 *lines = emalloc(sizeof(struct ChoLine *)); 5028 (*lines)[ctx.li] = cho_line_new(); 5029 (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); 5030 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5031 ctx.state = STATE_LYRICS; 5032 } else { 5033 cho_log(&ctx, LOG_ERR, "Can't close a %s section that wasn't opened earlier.", section_types[directive->stype]); 5034 goto ERR; 5035 } 5036 break; 5037 } 5038 case POSITION_NO: { 5039 /* INFO: {chorus} */ 5040 chorus = cho_find_previous_chorus(ctx.songs[ctx.so]->sections, ctx.se); 5041 if (chorus) { 5042 if (config->output->chorus->quote) { 5043 if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { 5044 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5045 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; 5046 } 5047 ctx.lii++; 5048 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 5049 (*lines)[ctx.li]->items[ctx.lii] = NULL; 5050 ctx.lii = 0; 5051 ctx.te = 0; 5052 (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); 5053 (*lines)[ctx.li]->text_above[ctx.lia] = NULL; 5054 ctx.lia = 0; 5055 cho_line_free((*lines)[ctx.li]); 5056 (*lines)[ctx.li] = NULL; 5057 ctx.li = 0; 5058 ctx.se++; 5059 ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); 5060 ctx.songs[ctx.so]->sections[ctx.se] = cho_section_copy(chorus); 5061 ctx.se++; 5062 ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); 5063 ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); 5064 lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; 5065 *lines = emalloc(sizeof(struct ChoLine *)); 5066 (*lines)[ctx.li] = cho_line_new(); 5067 (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); 5068 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5069 } else { 5070 if (chorus->label) { 5071 label = strdup(chorus->label->text); 5072 } else { 5073 label = strdup(config->output->chorus->label); 5074 } 5075 if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { 5076 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5077 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; 5078 if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { 5079 cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); 5080 } else { 5081 ctx.lii++; 5082 } 5083 } else { 5084 ctx.lii++; 5085 } 5086 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 5087 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5088 cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); 5089 ctx.current_ttype = TEXT_TYPE_LABEL; 5090 (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_new_default(&ctx); 5091 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = label; 5092 ctx.te += strlen(label); 5093 } 5094 } else { 5095 if (config->output->chorus->quote) { 5096 cho_log(&ctx, LOG_WARN, "Can't quote chorus because it's not defined previously."); 5097 } 5098 label = strdup(config->output->chorus->label); 5099 if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { 5100 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5101 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; 5102 if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { 5103 cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); 5104 } else { 5105 ctx.lii++; 5106 } 5107 } else { 5108 ctx.lii++; 5109 } 5110 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 5111 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5112 cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); 5113 ctx.current_ttype = TEXT_TYPE_LABEL; 5114 (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_new_default(&ctx); 5115 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = label; 5116 ctx.te += strlen(label); 5117 } 5118 break; 5119 } 5120 } 5121 break; 5122 } 5123 case DIRECTIVE_TYPE_METADATA: 5124 cho_log(&ctx, LOG_WARN, "Ignoring metadata directive '%s' because it has no value.", directive_name); 5125 break; 5126 case DIRECTIVE_TYPE_FORMATTING: 5127 cho_log(&ctx, LOG_WARN, "Formatting directive '%s' has no value.", directive_name); 5128 break; 5129 case DIRECTIVE_TYPE_IMAGE: 5130 cho_log(&ctx, LOG_ERR, "Directive 'image' has no value."); 5131 goto ERR; 5132 case DIRECTIVE_TYPE_PREAMBLE: { 5133 // INFO: The only preamble directive is 'new_song' 5134 cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); 5135 free((*lines)[ctx.li]->items); 5136 free((*lines)[ctx.li]); 5137 (*lines)[ctx.li] = NULL; 5138 ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); 5139 ctx.songs[ctx.so]->metadata[ctx.m] = NULL; 5140 ctx.se++; 5141 ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); 5142 ctx.songs[ctx.so]->sections[ctx.se] = NULL; 5143 ctx.songs[ctx.so]->diagrams = erealloc(ctx.songs[ctx.so]->diagrams, (ctx.dia+1) * sizeof(struct ChordDiagram *)); 5144 ctx.songs[ctx.so]->diagrams[ctx.dia] = NULL; 5145 if (!cho_style_reset_default()) { 5146 DEBUG("cho_style_reset_default failed."); 5147 goto ERR; 5148 } 5149 for (int e = 0; e<ctx.ia; e++) { 5150 cho_image_free(ctx.image_assets[e]); 5151 } 5152 free(ctx.image_assets); 5153 ctx.image_assets = NULL; 5154 ctx.ia = 0; 5155 ctx.so++; 5156 ctx.songs = erealloc(ctx.songs, (ctx.so+1) * sizeof(struct ChoSong *)); 5157 ctx.songs[ctx.so] = cho_song_new(&ctx); 5158 if (!ctx.songs[ctx.so]) { 5159 DEBUG("cho_song_new failed."); 5160 goto ERR; 5161 } 5162 free(ctx.transpose_history); 5163 ctx.th = 0; 5164 ctx.transpose_history = emalloc((ctx.th+1) * sizeof(int *)); 5165 ctx.transpose_history[ctx.th] = 0; 5166 ctx.transpose = &ctx.transpose_history[ctx.th]; 5167 ctx.th++; 5168 ctx.se = 0; 5169 ctx.li = 0; 5170 ctx.lii = 0; 5171 // ctx.m = 0; 5172 ctx.dia = 0; 5173 ctx.songs[ctx.so]->sections = emalloc((ctx.se+1) * sizeof(struct ChoSection *)); 5174 ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); 5175 lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; 5176 *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); 5177 (*lines)[ctx.li] = cho_line_new(); 5178 (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); 5179 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5180 break; 5181 } 5182 case DIRECTIVE_TYPE_FONT: { 5183 sprop.ttype = directive->ttype; 5184 sprop.type = directive->sprop; 5185 switch (directive->sprop) { 5186 case STYLE_PROPERTY_TYPE_FONT: 5187 sprop.u.font_name = NULL; 5188 break; 5189 case STYLE_PROPERTY_TYPE_SIZE: 5190 sprop.u.font_size = EMPTY_DOUBLE; 5191 break; 5192 case STYLE_PROPERTY_TYPE_COLOR: 5193 sprop.u.foreground_color = NULL; 5194 break; 5195 } 5196 if (!cho_style_change_default(sprop)) { 5197 DEBUG("cho_style_change_default failed."); 5198 goto ERR; 5199 } 5200 break; 5201 } 5202 case DIRECTIVE_TYPE_CHORD: { 5203 switch (directive->ctype) { 5204 case CHORD_DIRECTIVE_TRANSPOSE: 5205 ctx.transpose--; 5206 ctx.th--; 5207 break; 5208 case CHORD_DIRECTIVE_DEFINE: 5209 cho_log(&ctx, LOG_WARN, "Ignoring chord directive '%s' because it has no value.", directive_name); 5210 break; 5211 } 5212 break; 5213 } 5214 case DIRECTIVE_TYPE_OUTPUT: 5215 (*lines)[ctx.li]->btype = directive->btype; 5216 break; 5217 case DIRECTIVE_TYPE_EXTENSION: 5218 // INFO: Such a directive should not be logged. 5219 break; 5220 case DIRECTIVE_TYPE_CUSTOM: 5221 cho_log(&ctx, LOG_INFO, "Ignoring custom directive '%s'.", directive_name); 5222 break; 5223 } 5224 cho_directive_free(directive); 5225 directive = NULL; 5226 break; 5227 } 5228 if (c == '{') { 5229 cho_log(&ctx, LOG_ERR, "Can't start a new directive if the previous one is not yet closed."); 5230 goto ERR; 5231 } 5232 if (c == '\n') { 5233 ctx.line_no++; 5234 if (prev_c == '\\') { 5235 ctx.state_before_backslash = STATE_DIRECTIVE_NAME; 5236 ctx.state = STATE_BACKSLASH; 5237 ctx.dn--; 5238 break; 5239 } 5240 cho_log(&ctx, LOG_ERR, "Can't have a newline in a directive name."); 5241 goto ERR; 5242 } 5243 if (c == ':' || c == ' ') { 5244 directive_name[ctx.dn] = 0; 5245 ctx.dn = 0; 5246 ctx.state = STATE_DIRECTIVE_VALUE; 5247 break; 5248 } 5249 directive_name[ctx.dn] = c; 5250 ctx.dn++; 5251 break; 5252 } 5253 case STATE_DIRECTIVE_VALUE: { 5254 if (c == '}') { 5255 directive_value[ctx.dv] = 0; 5256 ctx.dv = 0; 5257 stripped_directive_value = str_remove_leading_whitespace(directive_value); 5258 directive = cho_directive_parse(&ctx, directive_name); 5259 /* printf( 5260 "directive: '%s'\ndtype: %s, stype: %s, position: %s\n", 5261 directive_name, dtype(directive->dtype), the_stype(directive->stype), pos(directive->position) 5262 ); */ 5263 switch (directive->dtype) { 5264 case DIRECTIVE_TYPE_ENVIRONMENT: { 5265 if (strlen(stripped_directive_value) > 0) { 5266 ctx.songs[ctx.so]->present_text_types[TEXT_TYPE_LABEL] = true; 5267 } 5268 ctx.current_ttype = directive->ttype; 5269 switch (directive->position) { 5270 case POSITION_START: { 5271 if (!is_previous_section_closed(&ctx)) { 5272 cho_log(&ctx, LOG_ERR, "Can't start a new section when the previous one is not yet closed."); 5273 goto ERR; 5274 } 5275 if (directive->stype == SECTION_TYPE_CUSTOM) { 5276 memset(custom_directive, 0, sizeof(custom_directive)); 5277 strcpy(custom_directive, &directive_name[9]); 5278 } 5279 cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); 5280 free((*lines)[ctx.li]->items); 5281 ctx.lii = 0; 5282 free((*lines)[ctx.li]); 5283 (*lines)[ctx.li] = NULL; 5284 ctx.se++; 5285 ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); 5286 ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); 5287 ctx.songs[ctx.so]->sections[ctx.se]->type = directive->stype; 5288 5289 if (strchr(stripped_directive_value, '=')) { 5290 directive_attrs = cho_attrs_parse(&ctx, stripped_directive_value, directive_name); 5291 if (!directive_attrs) { 5292 DEBUG("cho_attrs_parse failed."); 5293 goto ERR; 5294 } 5295 label = cho_attrs_get(directive_attrs, "label"); 5296 if (directive->stype == SECTION_TYPE_GRID) { 5297 shape = cho_attrs_get(directive_attrs, "shape"); 5298 if (shape) { 5299 if (!cho_grid_shape_parse_and_set(&ctx, shape)) { 5300 DEBUG("cho_grid_parse_and_set_shape failed."); 5301 goto ERR; 5302 } 5303 } 5304 } 5305 } else { 5306 if (directive->stype == SECTION_TYPE_GRID) { 5307 int index = str_index_of(stripped_directive_value, ' '); 5308 if (index != -1) { 5309 stripped_directive_value[index] = 0; 5310 label = &stripped_directive_value[index+1]; 5311 } 5312 if (!cho_grid_shape_parse_and_set(&ctx, stripped_directive_value)) { 5313 DEBUG("cho_grid_parse_and_set_shape failed."); 5314 goto ERR; 5315 } 5316 } else { 5317 label = stripped_directive_value; 5318 } 5319 } 5320 if (label) { 5321 ctx.songs[ctx.so]->sections[ctx.se]->label = emalloc(sizeof(struct ChoText)); 5322 ctx.songs[ctx.so]->sections[ctx.se]->label->text = strdup(label); 5323 ctx.prev_ttype = ctx.current_ttype; 5324 ctx.current_ttype = TEXT_TYPE_LABEL; 5325 ctx.songs[ctx.so]->sections[ctx.se]->label->style = cho_style_new_default(&ctx); 5326 ctx.current_ttype = ctx.prev_ttype; 5327 label = NULL; 5328 } 5329 cho_tag_attrs_free(directive_attrs); 5330 directive_attrs = NULL; 5331 if (ctx.directive_has_tag) { 5332 cho_style_complement(ctx.songs[ctx.so]->sections[ctx.se]->label->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); 5333 ctx.directive_has_tag = false; 5334 } 5335 ctx.li = 0; 5336 lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; 5337 *lines = emalloc(sizeof(struct ChoLine *)); 5338 (*lines)[ctx.li] = cho_line_new(); 5339 (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); 5340 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5341 ctx.songs[ctx.so]->present_text_types[directive->ttype] = true; 5342 break; 5343 } 5344 case POSITION_END: { 5345 cho_log(&ctx, LOG_ERR, "A directive that closes a section can't have arguments."); 5346 goto ERR; 5347 } 5348 case POSITION_NO: { 5349 /* INFO: {chorus: ...} */ 5350 label = strdup(stripped_directive_value); 5351 chorus = cho_find_previous_chorus(ctx.songs[ctx.so]->sections, ctx.se); 5352 if (chorus) { 5353 if (config->output->chorus->quote) { 5354 if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { 5355 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5356 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; 5357 } 5358 ctx.lii++; 5359 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 5360 (*lines)[ctx.li]->items[ctx.lii] = NULL; 5361 ctx.lii = 0; 5362 ctx.te = 0; 5363 (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); 5364 (*lines)[ctx.li]->text_above[ctx.lia] = NULL; 5365 ctx.lia = 0; 5366 cho_line_free((*lines)[ctx.li]); 5367 // songs[so]->sections[se]->lines = erealloc(songs[so]->sections[se]->lines, (li+1) * sizeof(struct ChoLine *)); 5368 (*lines)[ctx.li] = NULL; 5369 ctx.li = 0; 5370 ctx.se++; 5371 ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); 5372 ctx.songs[ctx.so]->sections[ctx.se] = cho_section_copy(chorus); 5373 if (ctx.songs[ctx.so]->sections[ctx.se]->label) { 5374 free(ctx.songs[ctx.so]->sections[ctx.se]->label->text); 5375 ctx.songs[ctx.so]->sections[ctx.se]->label->text = label; 5376 } else { 5377 ctx.current_ttype = TEXT_TYPE_LABEL; 5378 ctx.songs[ctx.so]->sections[ctx.se]->label = cho_text_new(&ctx); 5379 ctx.songs[ctx.so]->sections[ctx.se]->label->text = label; 5380 } 5381 if (ctx.directive_has_tag) { 5382 cho_style_complement(ctx.songs[ctx.so]->sections[ctx.se]->label->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); 5383 ctx.directive_has_tag = false; 5384 } 5385 ctx.se++; 5386 ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); 5387 ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); 5388 lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; 5389 *lines = emalloc(sizeof(struct ChoLine *)); 5390 (*lines)[ctx.li] = cho_line_new(); 5391 (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); 5392 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5393 } else { 5394 if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { 5395 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5396 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; 5397 if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { 5398 cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); 5399 } else { 5400 ctx.lii++; 5401 } 5402 } else { 5403 ctx.lii++; 5404 } 5405 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 5406 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5407 cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); 5408 ctx.current_ttype = TEXT_TYPE_LABEL; 5409 (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_new_default(&ctx); 5410 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = label; 5411 if (ctx.directive_has_tag) { 5412 cho_style_complement((*lines)[ctx.li]->items[ctx.lii]->u.text->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); 5413 ctx.directive_has_tag = false; 5414 } 5415 ctx.te += strlen(label); 5416 } 5417 } else { 5418 if (config->output->chorus->quote) { 5419 cho_log(&ctx, LOG_WARN, "Can't quote chorus because it's not defined previously."); 5420 } 5421 if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { 5422 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5423 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; 5424 if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { 5425 cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); 5426 } else { 5427 ctx.lii++; 5428 } 5429 } else { 5430 ctx.lii++; 5431 } 5432 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 5433 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5434 cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); 5435 ctx.current_ttype = TEXT_TYPE_LABEL; 5436 (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_new_default(&ctx); 5437 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = label; 5438 if (ctx.directive_has_tag) { 5439 cho_style_complement((*lines)[ctx.li]->items[ctx.lii]->u.text->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); 5440 } 5441 ctx.te += strlen(label); 5442 } 5443 break; 5444 } 5445 } 5446 break; 5447 } 5448 case DIRECTIVE_TYPE_METADATA: { 5449 metadata_value = strdup(stripped_directive_value); 5450 if (strlen(metadata_value) == 0) { 5451 cho_log(&ctx, LOG_WARN, "Ignoring metadata directive '%s' because it has no value.", directive_name); 5452 free(metadata_value); 5453 break; 5454 } 5455 switch (directive->meta) { 5456 case METADATA_DIRECTIVE_TITLE: 5457 ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); 5458 ctx.songs[ctx.so]->metadata[ctx.m] = cho_metadata_new(&ctx); 5459 ctx.songs[ctx.so]->metadata[ctx.m]->name = strdup("title"); 5460 ctx.songs[ctx.so]->metadata[ctx.m]->value = metadata_value; 5461 cho_style_free(ctx.songs[ctx.so]->metadata[ctx.m]->style); 5462 ctx.songs[ctx.so]->metadata[ctx.m]->style = cho_style_copy(directive->style); 5463 ctx.songs[ctx.so]->present_text_types[TEXT_TYPE_TITLE] = true; 5464 break; 5465 case METADATA_DIRECTIVE_SUBTITLE: 5466 ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); 5467 ctx.songs[ctx.so]->metadata[ctx.m] = cho_metadata_new(&ctx); 5468 ctx.songs[ctx.so]->metadata[ctx.m]->name = strdup("subtitle"); 5469 ctx.songs[ctx.so]->metadata[ctx.m]->value = metadata_value; 5470 cho_style_free(ctx.songs[ctx.so]->metadata[ctx.m]->style); 5471 ctx.songs[ctx.so]->metadata[ctx.m]->style = cho_style_copy(directive->style); 5472 ctx.songs[ctx.so]->present_text_types[TEXT_TYPE_SUBTITLE] = true; 5473 break; 5474 case METADATA_DIRECTIVE_OTHER: { 5475 if (!strcmp(directive_name, "meta")) { 5476 metadata = cho_metadata_split(&ctx, directive_value); 5477 if (!metadata) { 5478 DEBUG("cho_metadata_split failed."); 5479 goto ERR; 5480 } 5481 ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); 5482 ctx.songs[ctx.so]->metadata[ctx.m] = metadata; 5483 free(metadata_value); 5484 } else { 5485 ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); 5486 ctx.songs[ctx.so]->metadata[ctx.m] = cho_metadata_new(&ctx); 5487 ctx.songs[ctx.so]->metadata[ctx.m]->name = strdup(directive_name); 5488 ctx.songs[ctx.so]->metadata[ctx.m]->value = metadata_value; 5489 } 5490 } 5491 } 5492 if (ctx.directive_has_tag) { 5493 cho_style_complement(ctx.songs[ctx.so]->metadata[ctx.m]->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); 5494 ctx.directive_has_tag = false; 5495 } 5496 ctx.m++; 5497 break; 5498 } 5499 case DIRECTIVE_TYPE_FORMATTING: { 5500 if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { 5501 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5502 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; 5503 if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { 5504 cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); 5505 } else { 5506 ctx.lii++; 5507 } 5508 } else { 5509 ctx.lii++; 5510 } 5511 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 5512 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5513 cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); 5514 (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_copy(directive->style); 5515 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = strdup(stripped_directive_value); 5516 if (ctx.directive_has_tag) { 5517 cho_style_complement((*lines)[ctx.li]->items[ctx.lii]->u.text->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); 5518 ctx.directive_has_tag = false; 5519 } 5520 ctx.te += strlen(stripped_directive_value); 5521 ctx.songs[ctx.so]->present_text_types[directive->ttype] = true; 5522 break; 5523 } 5524 case DIRECTIVE_TYPE_IMAGE: { 5525 if (strchr(directive_value, '=')) { 5526 image = cho_image_directive_parse(&ctx, directive_value); 5527 if (!image) { 5528 DEBUG("cho_image_directive_parse failed."); 5529 goto ERR; 5530 } 5531 } else { 5532 image = cho_image_new(); 5533 image->src = strdup(stripped_directive_value); 5534 } 5535 if (image->is_asset) { 5536 ctx.image_assets = erealloc(ctx.image_assets, (ctx.ia+1) * sizeof(struct ChoImage *)); 5537 ctx.image_assets[ctx.ia] = image; 5538 ctx.ia++; 5539 } else { 5540 if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { 5541 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5542 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; 5543 ctx.te = 0; 5544 } 5545 ctx.lii++; 5546 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 5547 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5548 cho_text_free((*lines)[ctx.li]->items[ctx.lii]->u.text); 5549 (*lines)[ctx.li]->items[ctx.lii]->is_text = false; 5550 (*lines)[ctx.li]->items[ctx.lii]->u.image = image; 5551 } 5552 break; 5553 } 5554 case DIRECTIVE_TYPE_PREAMBLE: 5555 cho_log(&ctx, LOG_ERR, "Preamble directive '%s' can't have a value.", directive_name); 5556 goto ERR; 5557 case DIRECTIVE_TYPE_FONT: { 5558 sprop.ttype = directive->ttype; 5559 switch (directive->sprop) { 5560 case STYLE_PROPERTY_TYPE_FONT: 5561 sprop.u.font_name = emalloc((strlen(stripped_directive_value)+1) * sizeof(char)); 5562 strcpy(sprop.u.font_name, stripped_directive_value); 5563 sprop.type = STYLE_PROPERTY_TYPE_FONT; 5564 break; 5565 case STYLE_PROPERTY_TYPE_SIZE: 5566 sprop.u.font_size = strtod(stripped_directive_value, NULL); 5567 if (sprop.u.font_size <= 0.0) { 5568 cho_log(&ctx, LOG_ERR, "Font directive '%s' has to contain a positive number.", directive_name); 5569 goto ERR; 5570 } 5571 sprop.type = STYLE_PROPERTY_TYPE_SIZE; 5572 break; 5573 case STYLE_PROPERTY_TYPE_COLOR: 5574 sprop.u.foreground_color = cho_color_parse(stripped_directive_value); 5575 if (sprop.u.foreground_color == NULL) { 5576 cho_log(&ctx, LOG_ERR, "Font directive '%s' has an invalid value.", directive_name); 5577 goto ERR; 5578 } 5579 sprop.type = STYLE_PROPERTY_TYPE_COLOR; 5580 break; 5581 } 5582 if (!cho_style_change_default(sprop)) { 5583 DEBUG("cho_style_change_default failed."); 5584 goto ERR; 5585 } 5586 if (sprop.type == STYLE_PROPERTY_TYPE_FONT) { 5587 free(sprop.u.font_name); 5588 } else if (sprop.type == STYLE_PROPERTY_TYPE_COLOR) { 5589 free(sprop.u.foreground_color); 5590 } 5591 break; 5592 } 5593 case DIRECTIVE_TYPE_CHORD: { 5594 switch (directive->ctype) { 5595 case CHORD_DIRECTIVE_TRANSPOSE: 5596 if (!transposition_parse(directive_value, &transpose)) { 5597 DEBUG("transposition_parse failed."); 5598 cho_log(&ctx, LOG_ERR, "Directive 'transpose' has an invalid value."); 5599 goto ERR; 5600 } 5601 ctx.transpose_history = erealloc(ctx.transpose_history, (ctx.th+1) * sizeof(int *)); 5602 ctx.transpose_history[ctx.th] = ctx.transpose_history[ctx.th-1] + transpose; 5603 ctx.transpose = &ctx.transpose_history[ctx.th]; 5604 ctx.th++; 5605 break; 5606 case CHORD_DIRECTIVE_DEFINE: 5607 diagram = cho_chord_diagram_parse(&ctx, directive_value, ctx.songs[ctx.so]->diagrams, ctx.dia); 5608 if (!diagram) { 5609 DEBUG("cho_chord_diagram_parse failed."); 5610 goto ERR; 5611 } 5612 // debug_chord_diagram_print(diagram); 5613 ctx.songs[ctx.so]->diagrams = erealloc(ctx.songs[ctx.so]->diagrams, (ctx.dia+1) * sizeof(struct ChordDiagram *)); 5614 ctx.songs[ctx.so]->diagrams[ctx.dia] = diagram; 5615 ctx.dia++; 5616 break; 5617 } 5618 break; 5619 } 5620 case DIRECTIVE_TYPE_OUTPUT: 5621 cho_log(&ctx, LOG_ERR, "Directive '%s' can't have a value.", directive_name); 5622 goto ERR; 5623 case DIRECTIVE_TYPE_EXTENSION: 5624 // INFO: Such a directive should not be logged. 5625 break; 5626 case DIRECTIVE_TYPE_CUSTOM: 5627 cho_log(&ctx, LOG_INFO, "Ignoring custom directive '%s'.", directive_name); 5628 break; 5629 } 5630 switch (directive->stype) { 5631 case SECTION_TYPE_TAB: 5632 ctx.state = STATE_TAB; 5633 break; 5634 case SECTION_TYPE_GRID: 5635 ctx.state = STATE_GRID; 5636 break; 5637 default: 5638 ctx.state = STATE_LYRICS; 5639 } 5640 memset(directive_value, 0, strlen(directive_value)); 5641 free(stripped_directive_value); 5642 stripped_directive_value = NULL; 5643 cho_directive_free(directive); 5644 directive = NULL; 5645 break; 5646 } 5647 if (c == '%') { 5648 ctx.state_before_metadata_substitution = STATE_DIRECTIVE_VALUE; 5649 ctx.state = STATE_MAYBE_METADATA_SUBSTITUTION; 5650 break; 5651 } 5652 if (c == '<') { 5653 ctx.state_before_tag = STATE_DIRECTIVE_VALUE; 5654 ctx.state = STATE_MARKUP_TAG; 5655 break; 5656 } 5657 if (c == '{') { 5658 cho_log(&ctx, LOG_ERR, "Can't start a new directive if the previous one is not yet closed."); 5659 goto ERR; 5660 } 5661 if (c == '\n') { 5662 ctx.line_no++; 5663 if (prev_c == '\\') { 5664 ctx.state_before_backslash = STATE_DIRECTIVE_VALUE; 5665 ctx.state = STATE_BACKSLASH; 5666 ctx.dv--; 5667 break; 5668 } 5669 cho_log(&ctx, LOG_ERR, "Newline character inside a directive value is invalid."); 5670 goto ERR; 5671 } 5672 if (ctx.dv > 4094) { 5673 cho_log(&ctx, LOG_ERR, "Directive value can't be greater than 4095 bytes."); 5674 goto ERR; 5675 } 5676 directive_value[ctx.dv] = c; 5677 ctx.dv++; 5678 break; 5679 } 5680 case STATE_CHORD: { 5681 if (c == ']') { 5682 chord[ctx.ch] = 0; 5683 ctx.ch = 0; 5684 ctx.prev_ttype = ctx.current_ttype; 5685 ctx.current_ttype = TEXT_TYPE_CHORD; 5686 if (ctx.is_chord_already_initialized) { 5687 ctx.text_above_pos = cho_line_compute_text_above_position(ctx.songs[ctx.so]->sections[ctx.se]->lines[ctx.li], ctx.lii, ctx.te); 5688 (*lines)[ctx.li]->text_above[ctx.lia]->position = ctx.text_above_pos; 5689 tmp_chord = cho_chord_parse(&ctx, chord); 5690 cho_chord_complete((*lines)[ctx.li]->text_above[ctx.lia]->u.chord, tmp_chord); 5691 if (!(*lines)[ctx.li]->text_above[ctx.lia]->u.chord->is_canonical) { 5692 cho_log(&ctx, LOG_INFO, "Didn't recognize the chord '%s'.", (*lines)[ctx.li]->text_above[ctx.lia]->u.chord->name); 5693 } 5694 cho_chord_free(tmp_chord); 5695 ctx.is_chord_already_initialized = false; 5696 } else { 5697 (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); 5698 (*lines)[ctx.li]->text_above[ctx.lia] = emalloc(sizeof(struct ChoLineItemAbove)); 5699 (*lines)[ctx.li]->text_above[ctx.lia]->is_chord = true; 5700 ctx.text_above_pos = cho_line_compute_text_above_position((*lines)[ctx.li], ctx.lii, ctx.te); 5701 (*lines)[ctx.li]->text_above[ctx.lia]->position = ctx.text_above_pos; 5702 (*lines)[ctx.li]->text_above[ctx.lia]->u.chord = cho_chord_parse(&ctx, chord); 5703 if (!(*lines)[ctx.li]->text_above[ctx.lia]->u.chord->is_canonical) { 5704 cho_log(&ctx, LOG_INFO, "Didn't recognize the chord '%s'.", (*lines)[ctx.li]->text_above[ctx.lia]->u.chord->name); 5705 } 5706 } 5707 ctx.songs[ctx.so]->present_text_types[TEXT_TYPE_CHORD] = true; 5708 memset(chord, 0, strlen(chord)); 5709 ctx.lia++; 5710 ctx.current_ttype = ctx.prev_ttype; 5711 ctx.state = STATE_LYRICS; 5712 break; 5713 } 5714 if (prev_c == '[' && c == '*') { 5715 ctx.prev_ttype = ctx.current_ttype; 5716 ctx.current_ttype = TEXT_TYPE_ANNOT; 5717 (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); 5718 (*lines)[ctx.li]->text_above[ctx.lia] = emalloc(sizeof(struct ChoLineItemAbove)); 5719 (*lines)[ctx.li]->text_above[ctx.lia]->is_chord = false; 5720 ctx.text_above_pos = cho_line_compute_text_above_position((*lines)[ctx.li], ctx.lii, ctx.te); 5721 (*lines)[ctx.li]->text_above[ctx.lia]->position = ctx.text_above_pos; 5722 (*lines)[ctx.li]->text_above[ctx.lia]->u.annot = cho_text_new(&ctx); 5723 ctx.state = STATE_ANNOTATION; 5724 break; 5725 } 5726 if (c == '\n') { 5727 ctx.line_no++; 5728 if (prev_c == '\\') { 5729 ctx.state_before_backslash = STATE_CHORD; 5730 ctx.state = STATE_BACKSLASH; 5731 ctx.ch--; 5732 break; 5733 } 5734 cho_log(&ctx, LOG_ERR, "Newline character inside a chord is invalid."); 5735 goto ERR; 5736 } 5737 if (c == '[') { 5738 cho_log(&ctx, LOG_ERR, "Can't start a new chord/annotation if the previous one is not yet closed."); 5739 goto ERR; 5740 } 5741 if (c == '<') { 5742 if (prev_c == '[') { 5743 (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); 5744 (*lines)[ctx.li]->text_above[ctx.lia] = emalloc(sizeof(struct ChoLineItemAbove)); 5745 (*lines)[ctx.li]->text_above[ctx.lia]->is_chord = true; 5746 (*lines)[ctx.li]->text_above[ctx.lia]->u.chord = cho_chord_new(&ctx); 5747 ctx.is_chord_already_initialized = true; 5748 } 5749 ctx.state_before_tag = STATE_CHORD; 5750 ctx.state = STATE_MARKUP_TAG; 5751 break; 5752 } 5753 if (ctx.ch > CHORD_LEN-2) { 5754 cho_log(&ctx, LOG_ERR, "Chord can't be greater than %d bytes.", CHORD_LEN-1); 5755 goto ERR; 5756 } 5757 chord[ctx.ch] = c; 5758 ctx.ch++; 5759 break; 5760 } 5761 case STATE_ANNOTATION: { 5762 if (c == ']') { 5763 (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text = erealloc((*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text, (ctx.ann+1) * sizeof(char)); 5764 (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text[ctx.ann] = 0; 5765 ctx.songs[ctx.so]->present_text_types[TEXT_TYPE_ANNOT] = true; 5766 ctx.ann = 0; 5767 ctx.lia++; 5768 ctx.current_ttype = ctx.prev_ttype; 5769 ctx.state = STATE_LYRICS; 5770 break; 5771 } 5772 if (c == '<') { 5773 ctx.state_before_tag = STATE_ANNOTATION; 5774 ctx.state = STATE_MARKUP_TAG; 5775 break; 5776 } 5777 if (c == '\n') { 5778 ctx.line_no++; 5779 if (prev_c == '\\') { 5780 ctx.state_before_backslash = STATE_ANNOTATION; 5781 ctx.state = STATE_BACKSLASH; 5782 ctx.ann--; 5783 break; 5784 } 5785 cho_log(&ctx, LOG_ERR, "Newline character inside an annotation is invalid."); 5786 ctx.lia++; 5787 goto ERR; 5788 } 5789 (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text = erealloc((*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text, (ctx.ann+1) * sizeof(char)); 5790 (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text[ctx.ann] = c; 5791 ctx.ann++; 5792 break; 5793 } 5794 case STATE_TAB: { 5795 // INFO: similar to STATE_LYRICS but without markup and directives 5796 if (prev_c == '\n' && c == '#') { 5797 ctx.state_before_comment = STATE_TAB; 5798 ctx.state = STATE_COMMENT; 5799 break; 5800 } 5801 if (ctx.is_maybe_end_of_tab_directive) { 5802 if (c == '}') { 5803 directive_name[ctx.dn] = 0; 5804 ctx.dn = 0; 5805 ctx.is_maybe_end_of_tab_directive = false; 5806 if (!strcmp(directive_name, "end_of_tab") || !strcmp(directive_name, "eot")) { 5807 cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); 5808 free((*lines)[ctx.li]->items); 5809 ctx.lii = 0; 5810 free((*lines)[ctx.li]); 5811 (*lines)[ctx.li] = NULL; 5812 ctx.se++; 5813 ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); 5814 ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); 5815 ctx.li = 0; 5816 lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; 5817 *lines = emalloc(sizeof(struct ChoLine *)); 5818 (*lines)[ctx.li] = cho_line_new(); 5819 (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); 5820 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5821 ctx.current_ttype = TEXT_TYPE_TEXT; 5822 ctx.state = STATE_LYRICS; 5823 break; 5824 } else { 5825 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5826 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = '{'; 5827 ctx.te++; 5828 char *k; 5829 for (k = (char *)&directive_name; *k; k++, ctx.te++) { 5830 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5831 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = *k; 5832 } 5833 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5834 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = '}'; 5835 ctx.te++; 5836 } 5837 break; 5838 } 5839 if (c == ' ' || c == ':') { 5840 directive_name[ctx.dn] = 0; 5841 ctx.dn = 0; 5842 ctx.is_maybe_end_of_tab_directive = false; 5843 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5844 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = '{'; 5845 ctx.te++; 5846 char *k; 5847 for (k = (char *)&directive_name; *k; k++, ctx.te++) { 5848 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5849 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = *k; 5850 } 5851 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5852 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = c; 5853 ctx.te++; 5854 break; 5855 } 5856 directive_name[ctx.dn] = c; 5857 ctx.dn++; 5858 break; 5859 } 5860 if (prev_c == '\n' && c == '{') { 5861 ctx.is_maybe_end_of_tab_directive = true; 5862 break; 5863 } 5864 if (c == '\n') { 5865 ctx.line_no++; 5866 if (prev_c == '\\') { 5867 ctx.state_before_backslash = STATE_TAB; 5868 ctx.state = STATE_BACKSLASH; 5869 // INFO: This will later overwrite the backslash 5870 ctx.te--; 5871 break; 5872 } 5873 if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { 5874 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5875 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; 5876 if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { 5877 cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); 5878 if (ctx.lii == 0) { 5879 if ( 5880 !(*lines)[ctx.li]->text_above && 5881 (*lines)[ctx.li]->btype == BREAK_TYPE_LINE 5882 ) { 5883 free((*lines)[ctx.li]->items); 5884 free((*lines)[ctx.li]); 5885 *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); 5886 (*lines)[ctx.li] = cho_line_new(); 5887 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 5888 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5889 break; 5890 } 5891 } 5892 } else { 5893 ctx.lii++; 5894 } 5895 } else { 5896 ctx.lii++; 5897 } 5898 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 5899 (*lines)[ctx.li]->items[ctx.lii] = NULL; 5900 ctx.lii = 0; 5901 ctx.te = 0; 5902 (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); 5903 (*lines)[ctx.li]->text_above[ctx.lia] = NULL; 5904 ctx.lia = 0; 5905 ctx.li++; 5906 *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); 5907 (*lines)[ctx.li] = cho_line_new(); 5908 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 5909 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5910 break; 5911 } 5912 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 5913 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = c; 5914 ctx.te++; 5915 break; 5916 } 5917 case STATE_GRID: { 5918 if (prev_c == '\n' && c == '#') { 5919 ctx.state_before_comment = STATE_GRID; 5920 ctx.state = STATE_COMMENT; 5921 break; 5922 } 5923 if (prev_c == '\n' && c == '{') { 5924 ctx.state = STATE_DIRECTIVE_NAME; 5925 break; 5926 } 5927 if (c == '\n') { 5928 ctx.line_no++; 5929 if (prev_c != '}') { 5930 grid_token[ctx.gt] = 0; 5931 ctx.gt = 0; 5932 if (grid_token[0] == 0) { 5933 break; 5934 } 5935 token = cho_grid_token_parse(&ctx, grid_token, &err); 5936 if (err) { 5937 cho_log(&ctx, LOG_ERR, "Invalid token '%s' in grid section.", grid_token); 5938 goto ERR; 5939 } 5940 if (token == GRID_TOKEN_BAR_LINE_SYMBOL) { 5941 ctx.grid.bar_line_symbol_count++; 5942 ctx.grid.bar_line_symbol_in_line_count++; 5943 if (ctx.grid.bar_line_symbol_count == 2) { 5944 ctx.grid.expected_tokens_per_cell = ctx.grid.tokens_per_cell; 5945 } else { 5946 if (ctx.grid.bar_line_symbol_in_line_count > 1 && ctx.grid.expected_tokens_per_cell != ctx.grid.tokens_per_cell) { 5947 cho_log( 5948 &ctx, 5949 LOG_ERR, 5950 "Grid cell no. %d has %d tokens but should have %d tokens.", 5951 ctx.grid.bar_line_symbol_in_line_count - 1, 5952 ctx.grid.tokens_per_cell, 5953 ctx.grid.expected_tokens_per_cell 5954 ); 5955 goto ERR; 5956 } 5957 } 5958 ctx.grid.tokens_per_cell = 0; 5959 } else { 5960 ctx.grid.tokens_per_cell++; 5961 } 5962 if (token == GRID_TOKEN_PLACEHOLDER) { 5963 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = strdup(" "); 5964 } else { 5965 ctx.prev_ttype = ctx.current_ttype; 5966 ctx.current_ttype = TEXT_TYPE_GRID; 5967 cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); 5968 (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_new_default(&ctx); 5969 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = strdup(grid_token); 5970 ctx.current_ttype = ctx.prev_ttype; 5971 } 5972 ctx.lii++; 5973 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 5974 (*lines)[ctx.li]->items[ctx.lii] = NULL; 5975 ctx.lii = 0; 5976 (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItem *)); 5977 (*lines)[ctx.li]->text_above[ctx.lia] = NULL; 5978 ctx.lia = 0; 5979 ctx.li++; 5980 *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); 5981 (*lines)[ctx.li] = cho_line_new(); 5982 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 5983 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 5984 ctx.grid.bar_line_symbol_in_line_count = 0; 5985 } 5986 break; 5987 } 5988 if (c == ' ') { 5989 grid_token[ctx.gt] = 0; 5990 ctx.gt = 0; 5991 if (grid_token[0] == 0) { 5992 break; 5993 } 5994 token = cho_grid_token_parse(&ctx, grid_token, &err); 5995 if (err) { 5996 cho_log(&ctx, LOG_ERR, "Invalid token '%s' in grid section.", grid_token); 5997 goto ERR; 5998 } 5999 if (token == GRID_TOKEN_BAR_LINE_SYMBOL) { 6000 ctx.grid.bar_line_symbol_count++; 6001 ctx.grid.bar_line_symbol_in_line_count++; 6002 if (ctx.grid.bar_line_symbol_count == 2) { 6003 ctx.grid.expected_tokens_per_cell = ctx.grid.tokens_per_cell; 6004 } else { 6005 if (ctx.grid.bar_line_symbol_in_line_count > 1 && ctx.grid.expected_tokens_per_cell != ctx.grid.tokens_per_cell) { 6006 cho_log( 6007 &ctx, 6008 LOG_ERR, 6009 "Grid cell no. %d has %d tokens but should have %d tokens.", 6010 ctx.grid.bar_line_symbol_in_line_count - 1, 6011 ctx.grid.tokens_per_cell, 6012 ctx.grid.expected_tokens_per_cell 6013 ); 6014 goto ERR; 6015 } 6016 } 6017 ctx.grid.tokens_per_cell = 0; 6018 } else { 6019 ctx.grid.tokens_per_cell++; 6020 } 6021 if (token == GRID_TOKEN_PLACEHOLDER) { 6022 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = strdup(" "); 6023 } else { 6024 ctx.prev_ttype = ctx.current_ttype; 6025 ctx.current_ttype = TEXT_TYPE_GRID; 6026 cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); 6027 (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_new_default(&ctx); 6028 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = strdup(grid_token); 6029 ctx.current_ttype = ctx.prev_ttype; 6030 } 6031 ctx.lii++; 6032 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 6033 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 6034 break; 6035 } 6036 grid_token[ctx.gt] = c; 6037 ctx.gt++; 6038 break; 6039 } 6040 case STATE_MARKUP_TAG: { 6041 if (c == '/') { 6042 ctx.state = STATE_MARKUP_TAG_END; 6043 break; 6044 } 6045 ctx.ta++; 6046 ctx.tags = erealloc(ctx.tags, (ctx.ta+1) * sizeof(struct Tag *)); 6047 ctx.tags[ctx.ta] = cho_tag_new(); 6048 ctx.state = STATE_MARKUP_TAG_START; 6049 str--; 6050 break; 6051 } 6052 case STATE_MARKUP_TAG_START: { 6053 if (c == '>') { 6054 tag_start[ctx.t] = 0; 6055 ctx.t = 0; 6056 if (!strcmp(tag_start, "img")) { 6057 cho_log(&ctx, LOG_ERR, "'img' tag has to have at least the 'src' attribute."); 6058 goto ERR; 6059 } 6060 ctx.tags[ctx.ta]->name = strdup(tag_start); 6061 tag_style = cho_style_parse(&ctx, tag_start, NULL, cho_tag_style_inherit(ctx.tags, ctx.ta-1), &ctx.tags[ctx.ta]->style_presence); 6062 if (!tag_style) { 6063 DEBUG("cho_style_parse failed."); 6064 goto ERR; 6065 } 6066 ctx.tags[ctx.ta]->style = tag_style; 6067 switch (ctx.state_before_tag) { 6068 case STATE_LYRICS: 6069 cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); 6070 (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_copy(tag_style); 6071 break; 6072 case STATE_CHORD: 6073 cho_style_free((*lines)[ctx.li]->text_above[ctx.lia]->u.chord->style); 6074 (*lines)[ctx.li]->text_above[ctx.lia]->u.chord->style = cho_style_copy(tag_style); 6075 break; 6076 case STATE_ANNOTATION: 6077 if (ctx.ann > 0) { 6078 (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text = erealloc((*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text, (ctx.ann+1) * sizeof(char)); 6079 (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text[ctx.ann] = 0; 6080 ctx.ann = 0; 6081 ctx.lia++; 6082 (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); 6083 (*lines)[ctx.li]->text_above[ctx.lia] = emalloc(sizeof(struct ChoLineItemAbove)); 6084 (*lines)[ctx.li]->text_above[ctx.lia]->is_chord = false; 6085 (*lines)[ctx.li]->text_above[ctx.lia]->position = ctx.text_above_pos; 6086 (*lines)[ctx.li]->text_above[ctx.lia]->u.annot = cho_text_new(&ctx); 6087 } 6088 cho_style_free((*lines)[ctx.li]->text_above[ctx.lia]->u.annot->style); 6089 (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->style = cho_style_copy(tag_style); 6090 break; 6091 case STATE_DIRECTIVE_VALUE: 6092 ctx.directive_has_tag = true; 6093 break; 6094 default: 6095 cho_log(&ctx, LOG_ERR, "Invalid state_before_tag '%s'.", state_enums[ctx.state_before_tag]); 6096 goto ERR; 6097 } 6098 memset(tag_start, 0, strlen(tag_start)); 6099 ctx.state = ctx.state_before_tag; 6100 break; 6101 } 6102 if (is_whitespace(c)) { 6103 tag_start[ctx.t] = 0; 6104 ctx.t = 0; 6105 ctx.tags[ctx.ta]->name = strdup(tag_start); 6106 ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); 6107 ctx.tags[ctx.ta]->attrs[ctx.at] = cho_tag_attr_new(); 6108 ctx.state = STATE_MARKUP_ATTR_NAME; 6109 break; 6110 } 6111 if (c == '\n') { 6112 ctx.line_no++; 6113 if (prev_c == '\\') { 6114 ctx.state_before_backslash = STATE_MARKUP_TAG_START; 6115 ctx.state = STATE_BACKSLASH; 6116 ctx.t--; 6117 break; 6118 } 6119 cho_log(&ctx, LOG_ERR, "Newline character inside a tag name is invalid."); 6120 goto ERR; 6121 } 6122 if (ctx.t == 5) { 6123 cho_log(&ctx, LOG_ERR, "Start tag name is too long."); 6124 goto ERR; 6125 } 6126 tag_start[ctx.t] = c; 6127 ctx.t++; 6128 break; 6129 } 6130 case STATE_MARKUP_TAG_END: { 6131 if (c == '>') { 6132 tag_end[ctx.t] = 0; 6133 ctx.t = 0; 6134 if (!cho_tag_close_last_unclosed(&ctx, tag_end, ctx.tags, ctx.ta)) { 6135 DEBUG("cho_tag_close_last_unclosed failed."); 6136 goto ERR; 6137 } 6138 memset(tag_end, 0, strlen(tag_end)); 6139 ctx.state = ctx.state_before_tag; 6140 break; 6141 } 6142 if (c == '\n') { 6143 ctx.line_no++; 6144 if (prev_c == '\\') { 6145 ctx.state_before_backslash = STATE_MARKUP_TAG_END; 6146 ctx.state = STATE_BACKSLASH; 6147 ctx.t--; 6148 break; 6149 } 6150 cho_log(&ctx, LOG_ERR, "Newline character inside a tag name is invalid."); 6151 goto ERR; 6152 } 6153 if (ctx.t == 5) { 6154 cho_log(&ctx, LOG_ERR, "End tag name is too long."); 6155 goto ERR; 6156 } 6157 tag_end[ctx.t] = c; 6158 ctx.t++; 6159 break; 6160 } 6161 case STATE_MARKUP_ATTR_NAME: { 6162 if (c == '=') { 6163 ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); 6164 ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = 0; 6165 ctx.atn = 0; 6166 ctx.state = STATE_MARKUP_ATTR_VALUE; 6167 break; 6168 } 6169 if (is_whitespace(c)) { 6170 if (ctx.at == 0) { 6171 if (!ctx.tags[ctx.ta]->attrs[ctx.at]->name) { 6172 break; 6173 } else { 6174 ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); 6175 ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = 0; 6176 cho_log(&ctx, LOG_ERR, "1 Attribute with name '%s' of tag '%s' has no value.", ctx.tags[ctx.ta]->attrs[ctx.at]->name, tag_start); 6177 ctx.at++; 6178 ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); 6179 ctx.tags[ctx.ta]->attrs[ctx.at] = NULL; 6180 goto ERR; 6181 } 6182 } 6183 if (ctx.tags[ctx.ta]->attrs[ctx.at-1]->name && ctx.tags[ctx.ta]->attrs[ctx.at-1]->value) { 6184 break; 6185 } 6186 if (!ctx.tags[ctx.ta]->attrs[ctx.at-1]->name && !ctx.tags[ctx.ta]->attrs[ctx.at-1]->value) { 6187 break; 6188 } 6189 ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); 6190 ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = 0; 6191 cho_log(&ctx, LOG_ERR, "2 Attribute with name '%s' of tag '%s' has no value.", ctx.tags[ctx.ta]->attrs[ctx.at]->name, tag_start); 6192 ctx.at++; 6193 ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); 6194 ctx.tags[ctx.ta]->attrs[ctx.at] = NULL; 6195 goto ERR; 6196 } 6197 if (c == '>') { 6198 if (ctx.tags[ctx.ta]->attrs[ctx.at]->name && !ctx.tags[ctx.ta]->attrs[ctx.at]->value) { 6199 ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); 6200 ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = 0; 6201 cho_log(&ctx, LOG_ERR, "3 Attribute with name '%s' of tag '%s' has no value.", ctx.tags[ctx.ta]->attrs[ctx.at]->name, tag_start); 6202 ctx.at++; 6203 ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); 6204 ctx.tags[ctx.ta]->attrs[ctx.at] = NULL; 6205 goto ERR; 6206 } 6207 if (ctx.at > 0 && ctx.tags[ctx.ta]->attrs[ctx.at-1]->value) { 6208 cho_tag_attr_free(ctx.tags[ctx.ta]->attrs[ctx.at]); 6209 ctx.tags[ctx.ta]->attrs[ctx.at] = NULL; 6210 ctx.atn = 0; 6211 if (!strcmp(ctx.tags[ctx.ta]->name, "img")) { 6212 cho_text_free((*lines)[ctx.li]->items[ctx.lii]->u.text); 6213 (*lines)[ctx.li]->items[ctx.lii]->is_text = false; 6214 image = cho_image_tag_parse(&ctx, ctx.tags[ctx.ta]->attrs); 6215 if (!image) { 6216 DEBUG("cho_image_tag_parse failed."); 6217 goto ERR; 6218 } 6219 (*lines)[ctx.li]->items[ctx.lii]->u.image = image; 6220 ctx.lii++; 6221 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 6222 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 6223 } else { 6224 tag_style = cho_style_parse(&ctx, tag_start, ctx.tags[ctx.ta]->attrs, cho_tag_style_inherit(ctx.tags, ctx.ta-1), &ctx.tags[ctx.ta]->style_presence); 6225 if (!tag_style) { 6226 DEBUG("cho_style_parse failed."); 6227 goto ERR; 6228 } 6229 ctx.tags[ctx.ta]->style = tag_style; 6230 switch (ctx.state_before_tag) { 6231 case STATE_LYRICS: 6232 cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); 6233 (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_copy(tag_style); 6234 break; 6235 case STATE_CHORD: 6236 cho_style_free((*lines)[ctx.li]->text_above[ctx.lia]->u.chord->style); 6237 (*lines)[ctx.li]->text_above[ctx.lia]->u.chord->style = cho_style_copy(tag_style); 6238 break; 6239 case STATE_ANNOTATION: 6240 cho_style_free((*lines)[ctx.li]->text_above[ctx.lia]->u.annot->style); 6241 (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->style = cho_style_copy(tag_style); 6242 break; 6243 case STATE_DIRECTIVE_VALUE: 6244 ctx.directive_has_tag = true; 6245 break; 6246 default: 6247 cho_log(&ctx, LOG_ERR, "Invalid state_before_tag '%s'.", state_enums[ctx.state_before_tag]); 6248 goto ERR; 6249 } 6250 } 6251 ctx.at = 0; 6252 memset(tag_start, 0, strlen(tag_start)); 6253 ctx.state = ctx.state_before_tag; 6254 break; 6255 } else { 6256 ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); 6257 ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = 0; 6258 ctx.atn = 0; 6259 cho_log(&ctx, LOG_ERR, "4 Attribute with name '%s' of tag '%s' has no value.", ctx.tags[ctx.ta]->attrs[ctx.at]->name, tag_start); 6260 ctx.at++; 6261 ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); 6262 ctx.tags[ctx.ta]->attrs[ctx.at] = NULL; 6263 goto ERR; 6264 } 6265 } 6266 if (c == '\n') { 6267 ctx.line_no++; 6268 if (prev_c == '\\') { 6269 ctx.state_before_backslash = STATE_MARKUP_ATTR_NAME; 6270 ctx.state = STATE_BACKSLASH; 6271 ctx.atn--; 6272 break; 6273 } 6274 cho_log(&ctx, LOG_ERR, "Newline character inside an tag attribute name is invalid."); 6275 ctx.at++; 6276 ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); 6277 ctx.tags[ctx.ta]->attrs[ctx.at] = NULL; 6278 goto ERR; 6279 } 6280 ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); 6281 ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = c; 6282 ctx.atn++; 6283 break; 6284 } 6285 case STATE_MARKUP_ATTR_VALUE: { 6286 if (c == '\n') { 6287 ctx.line_no++; 6288 if (prev_c == '\\') { 6289 ctx.state_before_backslash = STATE_MARKUP_ATTR_VALUE; 6290 ctx.state = STATE_BACKSLASH; 6291 ctx.atv--; 6292 break; 6293 } 6294 cho_log(&ctx, LOG_ERR, "Newline character inside an attribute value is invalid."); 6295 ctx.at++; 6296 ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); 6297 ctx.tags[ctx.ta]->attrs[ctx.at] = NULL; 6298 goto ERR; 6299 } 6300 if (avs == ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED) { 6301 if (is_whitespace(c)) { 6302 cho_log(&ctx, LOG_ERR, "Whitespace character after equals sign is invalid."); 6303 ctx.at++; 6304 ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); 6305 ctx.tags[ctx.ta]->attrs[ctx.at] = NULL; 6306 goto ERR; 6307 } 6308 if (c == '>') { 6309 cho_log(&ctx, LOG_ERR, "5 Attribute with name '%s' of tag '%s' has no value.", ctx.tags[ctx.ta]->attrs[ctx.at]->name, tag_start); 6310 ctx.at++; 6311 ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); 6312 ctx.tags[ctx.ta]->attrs[ctx.at] = NULL; 6313 goto ERR; 6314 } 6315 if (c == '\'') { 6316 avs = ATTRIBUTE_VALUE_SYNTAX_APOSTROPHE; 6317 } else if (c == '"') { 6318 avs = ATTRIBUTE_VALUE_SYNTAX_QUOTATION_MARK; 6319 } else { 6320 avs = ATTRIBUTE_VALUE_SYNTAX_UNQUOTED; 6321 if (c == '%') { 6322 ctx.state_before_metadata_substitution = STATE_MARKUP_ATTR_VALUE; 6323 ctx.state = STATE_MAYBE_METADATA_SUBSTITUTION; 6324 break; 6325 } 6326 ctx.tags[ctx.ta]->attrs[ctx.at]->value = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->value, (ctx.atv+1) * sizeof(char)); 6327 ctx.tags[ctx.ta]->attrs[ctx.at]->value[ctx.atv] = c; 6328 ctx.atv++; 6329 } 6330 break; 6331 } 6332 if (avs == ATTRIBUTE_VALUE_SYNTAX_UNQUOTED && c == '>') { 6333 ctx.tags[ctx.ta]->attrs[ctx.at]->value = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->value, (ctx.atv+1) * sizeof(char)); 6334 ctx.tags[ctx.ta]->attrs[ctx.at]->value[ctx.atv] = 0; 6335 ctx.atv = 0; 6336 ctx.at++; 6337 ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); 6338 ctx.tags[ctx.ta]->attrs[ctx.at] = NULL; 6339 if (!strcmp(ctx.tags[ctx.ta]->name, "img")) { 6340 cho_text_free((*lines)[ctx.li]->items[ctx.lii]->u.text); 6341 (*lines)[ctx.li]->items[ctx.lii]->is_text = false; 6342 image = cho_image_tag_parse(&ctx, ctx.tags[ctx.ta]->attrs); 6343 if (!image) { 6344 DEBUG("cho_image_tag_parse failed."); 6345 goto ERR; 6346 } 6347 (*lines)[ctx.li]->items[ctx.lii]->u.image = image; 6348 ctx.lii++; 6349 (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); 6350 (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); 6351 } else { 6352 tag_style = cho_style_parse(&ctx, tag_start, ctx.tags[ctx.ta]->attrs, cho_tag_style_inherit(ctx.tags, ctx.ta-1), &ctx.tags[ctx.ta]->style_presence); 6353 if (!tag_style) { 6354 DEBUG("cho_style_parse failed."); 6355 goto ERR; 6356 } 6357 ctx.tags[ctx.ta]->style = tag_style; 6358 switch (ctx.state_before_tag) { 6359 case STATE_LYRICS: 6360 cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); 6361 (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_copy(tag_style); 6362 break; 6363 case STATE_CHORD: 6364 cho_style_free((*lines)[ctx.li]->text_above[ctx.lia]->u.chord->style); 6365 (*lines)[ctx.li]->text_above[ctx.lia]->u.chord->style = cho_style_copy(tag_style); 6366 break; 6367 case STATE_ANNOTATION: 6368 cho_style_free((*lines)[ctx.li]->text_above[ctx.lia]->u.annot->style); 6369 (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->style = cho_style_copy(tag_style); 6370 break; 6371 case STATE_DIRECTIVE_VALUE: 6372 ctx.directive_has_tag = true; 6373 break; 6374 default: 6375 cho_log(&ctx, LOG_ERR, "Invalid state_before_tag '%s'.", state_enums[ctx.state_before_tag]); 6376 goto ERR; 6377 } 6378 } 6379 ctx.at = 0; 6380 avs = ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED; 6381 memset(tag_start, 0, strlen(tag_start)); 6382 ctx.state = ctx.state_before_tag; 6383 break; 6384 } 6385 if ( 6386 (avs == ATTRIBUTE_VALUE_SYNTAX_APOSTROPHE && c == '\'') || 6387 (avs == ATTRIBUTE_VALUE_SYNTAX_QUOTATION_MARK && c == '"') || 6388 (avs == ATTRIBUTE_VALUE_SYNTAX_UNQUOTED && (c == ' ' || c == '\t')) 6389 ) { 6390 ctx.tags[ctx.ta]->attrs[ctx.at]->value = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->value, (ctx.atv+1) * sizeof(char)); 6391 ctx.tags[ctx.ta]->attrs[ctx.at]->value[ctx.atv] = 0; 6392 ctx.atv = 0; 6393 ctx.at++; 6394 ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); 6395 ctx.tags[ctx.ta]->attrs[ctx.at] = cho_tag_attr_new(); 6396 avs = ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED; 6397 ctx.state = STATE_MARKUP_ATTR_NAME; 6398 break; 6399 } 6400 if (c == '%') { 6401 ctx.state_before_metadata_substitution = STATE_MARKUP_ATTR_VALUE; 6402 ctx.state = STATE_MAYBE_METADATA_SUBSTITUTION; 6403 break; 6404 } 6405 ctx.tags[ctx.ta]->attrs[ctx.at]->value = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->value, (ctx.atv+1) * sizeof(char)); 6406 ctx.tags[ctx.ta]->attrs[ctx.at]->value[ctx.atv] = c; 6407 ctx.atv++; 6408 break; 6409 } 6410 case STATE_COMMENT: { 6411 if (c == '\n') { 6412 ctx.line_no++; 6413 ctx.state = ctx.state_before_comment; 6414 break; 6415 } 6416 break; 6417 } 6418 case STATE_MAYBE_METADATA_SUBSTITUTION: { 6419 if (c == '{') { 6420 metadata_substitution[ctx.ms] = '%'; 6421 ctx.ms++; 6422 metadata_substitution[ctx.ms] = '{'; 6423 ctx.ms++; 6424 ctx.state = STATE_METADATA_SUBSTITUTION; 6425 break; 6426 } 6427 switch (ctx.state_before_metadata_substitution) { 6428 case STATE_LYRICS: 6429 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 6430 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = '%'; 6431 ctx.te++; 6432 break; 6433 case STATE_DIRECTIVE_VALUE: 6434 directive_value[ctx.dv] = '%'; 6435 ctx.dv++; 6436 break; 6437 case STATE_MARKUP_ATTR_VALUE: 6438 ctx.tags[ctx.ta]->attrs[ctx.at]->value = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->value, (ctx.atv+1) * sizeof(char)); 6439 ctx.tags[ctx.ta]->attrs[ctx.at]->value[ctx.atv] = '%'; 6440 break; 6441 default: 6442 } 6443 ctx.state = ctx.state_before_metadata_substitution; 6444 str--; 6445 break; 6446 } 6447 case STATE_METADATA_SUBSTITUTION: { 6448 if (prev_c != '\\' && c == '}') { 6449 if (ctx.nested_level == 0) { 6450 metadata_substitution[ctx.ms] = '}'; 6451 ctx.ms++; 6452 metadata_substitution[ctx.ms] = 0; 6453 ctx.ms = 0; 6454 char *substituted = cho_metadata_substitution_parse( 6455 &ctx, 6456 metadata_substitution, 6457 NULL, 6458 ctx.state_before_metadata_substitution 6459 ); 6460 if (!substituted) { 6461 DEBUG("cho_metadata_substitution_parse failed."); 6462 goto ERR; 6463 } 6464 char *ch; 6465 switch (ctx.state_before_metadata_substitution) { 6466 case STATE_LYRICS: 6467 for (ch = substituted; *ch; ch++) { 6468 (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); 6469 (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = *ch; 6470 ctx.te++; 6471 } 6472 break; 6473 case STATE_DIRECTIVE_VALUE: 6474 for (ch = substituted; *ch; ch++) { 6475 directive_value[ctx.dv] = *ch; 6476 ctx.dv++; 6477 } 6478 break; 6479 case STATE_MARKUP_ATTR_VALUE: 6480 for (ch = substituted; *ch; ch++) { 6481 ctx.tags[ctx.ta]->attrs[ctx.at]->value = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->value, (ctx.atv+1) * sizeof(char)); 6482 ctx.tags[ctx.ta]->attrs[ctx.at]->value[ctx.atv] = *ch; 6483 ctx.atv++; 6484 } 6485 break; 6486 default: 6487 } 6488 free(substituted); 6489 ctx.state = ctx.state_before_metadata_substitution; 6490 break; 6491 } 6492 ctx.nested_level--; 6493 } 6494 if (prev_c == '%' && c == '{') { 6495 ctx.nested_level++; 6496 } 6497 if (ctx.ms > 4094) { 6498 cho_log(&ctx, LOG_ERR, "Metadata substitution can't be greater than 4095 bytes."); 6499 goto ERR; 6500 } 6501 metadata_substitution[ctx.ms] = c; 6502 ctx.ms++; 6503 break; 6504 } 6505 } 6506 prev_c = c; 6507 } 6508 if (!cho_style_reset_default()) { 6509 DEBUG("cho_style_reset_default failed."); 6510 goto ERR; 6511 } 6512 cho_songs_close(&ctx, lines); 6513 bool exist_title = false; 6514 for (ctx.so = 0; ctx.songs[ctx.so]; ctx.so++) { 6515 for (ctx.m = 0; ctx.songs[ctx.so]->metadata[ctx.m]; ctx.m++) { 6516 if ( 6517 !strcmp(ctx.songs[ctx.so]->metadata[ctx.m]->name, "title") && 6518 ctx.songs[ctx.so]->metadata[ctx.m]->value && 6519 strcmp(ctx.songs[ctx.so]->metadata[ctx.m]->value, "") != 0 6520 ) { 6521 exist_title = true; 6522 } 6523 } 6524 if (!exist_title) { 6525 /* INFO: This cho_log() is not line specific. It's a workaround. */ 6526 ctx.line_no = 0; 6527 cho_log(&ctx, LOG_ERR, "Song has no title."); 6528 goto ERR; 6529 } 6530 exist_title = false; 6531 } 6532 cho_context_cleanup(&ctx); 6533 return ctx.songs; 6534 ERR: 6535 cho_songs_close(&ctx, lines); 6536 cho_context_cleanup(&ctx); 6537 for (int e = 0; e<=ctx.so; e++) { 6538 cho_song_free(ctx.songs[e]); 6539 } 6540 free(ctx.songs); 6541 free(stripped_directive_value); 6542 cho_directive_free(directive); 6543 cho_tag_attrs_free(directive_attrs); 6544 return NULL; 6545 } 6546 6547 #ifdef ENABLE_DEBUG 6548 void 6549 cho_debug_songs_print(struct ChoSong **songs) 6550 { 6551 struct ChoSong **s; 6552 struct ChoSection **se; 6553 struct ChoLine **li; 6554 struct ChoLineItem **it; 6555 struct ChoLineItemAbove **above; 6556 char *name; 6557 for (s = songs; *s; s++) { 6558 for (se = (*s)->sections; *se; se++) { 6559 printf("## Section"); 6560 if ((*se)->label) { 6561 printf(": %s\n", (*se)->label->text); 6562 } else { 6563 printf("\n"); 6564 } 6565 for (li = (*se)->lines; *li; li++) { 6566 printf("## Line\n"); 6567 it = (*li)->items; 6568 for (above = (*li)->text_above; *above; above++) { 6569 if ((*above)->is_chord) { 6570 name = cho_chord_name_generate((*above)->u.chord); 6571 printf("chord: %s\n", name); 6572 free(name); 6573 } else { 6574 printf("annotation: %s\n", (*above)->u.annot->text); 6575 } 6576 } 6577 for (it = (*li)->items; *it; it++) { 6578 if ((*it)->is_text) { 6579 printf("text: %s\n", (*it)->u.text->text); 6580 } else { 6581 printf("image: %s\n", (*it)->u.image->src); 6582 } 6583 } 6584 } 6585 } 6586 } 6587 } 6588 #endif /* ENABLE_DEBUG */