lorid

convert chordpro to pdf
git clone git://git.relim.de/lorid.git
Log | Files | Refs | README | LICENSE

commit 1339f74798fa7251bdee3d219a993ef2dbd6dacb
parent f628721585120e10549e36dc84ca68b042e08158
Author: nibo <nibo@relim.de>
Date:   Thu, 20 Feb 2025 14:23:56 +0100

Make image assets song specific

Also first read all input text, then parse it

Diffstat:
Msrc/chordpro.c | 2269+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/chordpro.h | 2+-
Msrc/lorid.c | 15+++++++++++----
Msrc/util.c | 24+++++++++++++++++++++++-
Msrc/util.h | 2+-
5 files changed, 1167 insertions(+), 1145 deletions(-)

diff --git a/src/chordpro.c b/src/chordpro.c @@ -1768,6 +1768,7 @@ cho_metadata_load_default(struct ChoContext *ctx) meta[i]->value = strdup(logged_in_user); meta[i]->style = cho_style_new_default(ctx); i++; + ctx->m = i; return meta; } @@ -4055,7 +4056,7 @@ cho_context_init( ctx->li = 0; ctx->lia = 0; ctx->lii = 0; - ctx->m = 11; + ctx->m = 0; ctx->ms = 0; ctx->se = 0; ctx->so = 0; @@ -4088,7 +4089,7 @@ cho_context_init( } struct ChoSong ** -cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) +cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *config) { enum AttrValueSyntax avs = -1; struct ChoStyle *tag_style; @@ -4112,7 +4113,6 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) char c = 0; char prev_c = '\n'; int transpose; - size_t read; if (!cho_context_init(&ctx, config, chordpro_filepath)) { LOG_DEBUG("cho_context_init failed."); @@ -4123,138 +4123,159 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); ctx.songs[ctx.so]->present_text_types[TT_TOC] = config->output->toc->show; - while (feof(fp) == 0) { - read = fread(&c, 1, 1, fp); - if (read == 1) { - if (c == '\n') { - ctx.line_number++; + for (; *str; str++) { + c = *str; + if (c == '\n') { + ctx.line_number++; + } + // printf("state: %s, buf: %c\n", state_enums[state], buf); + if (c == '\r') { + continue; + } + switch (ctx.state) { + case STATE_LYRICS: { + if (prev_c == '\n' && c == '#') { + ctx.state_before_comment = STATE_LYRICS; + ctx.state = STATE_COMMENT; + break; } - // printf("state: %s, buf: %c\n", state_enums[state], buf); - if (c == '\r') { - continue; + if (prev_c == '\n' && c == '{') { + ctx.state = STATE_DIRECTIVE_NAME; + break; } - switch (ctx.state) { - case STATE_LYRICS: { - if (prev_c == '\n' && c == '#') { - ctx.state_before_comment = STATE_LYRICS; - ctx.state = STATE_COMMENT; - break; - } - if (prev_c == '\n' && c == '{') { - ctx.state = STATE_DIRECTIVE_NAME; - break; - } - if (c == '[') { - ctx.state = STATE_CHORD; - break; - } - if (c == '<') { - if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; - if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { - cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); - } else { - ctx.lii++; - } + if (c == '[') { + ctx.state = STATE_CHORD; + break; + } + if (c == '<') { + if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; + if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { + cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); } else { ctx.lii++; } - ctx.te = 0; - (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - ctx.state_before_tag = STATE_LYRICS; - ctx.state = STATE_MARKUP_TAG; - break; + } else { + ctx.lii++; } - if (c == '%') { - ctx.state_before_metadata_substitution = STATE_LYRICS; - ctx.state = STATE_MAYBE_METADATA_SUBSTITUTION; + ctx.te = 0; + (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); + ctx.state_before_tag = STATE_LYRICS; + ctx.state = STATE_MARKUP_TAG; + break; + } + if (c == '%') { + ctx.state_before_metadata_substitution = STATE_LYRICS; + ctx.state = STATE_MAYBE_METADATA_SUBSTITUTION; + break; + } + if (c == '\n') { + if (prev_c == '\\') { + ctx.state_before_backslash = STATE_LYRICS; + ctx.state = STATE_BACKSLASH; + ctx.te--; // INFO: This will later overwrite the backslash break; } - if (c == '\n') { - if (prev_c == '\\') { - ctx.state_before_backslash = STATE_LYRICS; - ctx.state = STATE_BACKSLASH; - ctx.te--; // INFO: This will later overwrite the backslash - break; - } - if (ctx.ta > -1 && !ctx.tags[ctx.ta]->is_closed && strcmp(ctx.tags[ctx.ta]->name, "img")) { - cho_log(&ctx, LOG_ERR, "Tag has to be closed on same line."); - return NULL; - } - if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; - if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { - cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); - if (ctx.lii == 0) { - if ( - !(*lines)[ctx.li]->text_above && - (*lines)[ctx.li]->btype == BT_LINE - ) { - free((*lines)[ctx.li]->items); - free((*lines)[ctx.li]); - *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); - (*lines)[ctx.li] = cho_line_new(); - (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - break; - } + if (ctx.ta > -1 && !ctx.tags[ctx.ta]->is_closed && strcmp(ctx.tags[ctx.ta]->name, "img")) { + cho_log(&ctx, LOG_ERR, "Tag has to be closed on same line."); + return NULL; + } + if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; + if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { + cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); + if (ctx.lii == 0) { + if ( + !(*lines)[ctx.li]->text_above && + (*lines)[ctx.li]->btype == BT_LINE + ) { + free((*lines)[ctx.li]->items); + free((*lines)[ctx.li]); + *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); + (*lines)[ctx.li] = cho_line_new(); + (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); + break; } - } else { - ctx.lii++; } } else { ctx.lii++; } - (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = NULL; - ctx.lii = 0; - ctx.te = 0; - (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[ctx.li]->text_above[ctx.lia] = NULL; - ctx.lia = 0; - ctx.li++; - *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); - (*lines)[ctx.li] = cho_line_new(); - (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - break; + } else { + ctx.lii++; } - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = c; - ctx.te++; + (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = NULL; + ctx.lii = 0; + ctx.te = 0; + (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); + (*lines)[ctx.li]->text_above[ctx.lia] = NULL; + ctx.lia = 0; + ctx.li++; + *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); + (*lines)[ctx.li] = cho_line_new(); + (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); break; } - case STATE_BACKSLASH: { - if (!is_whitespace(c)) { - if (fseek(fp, -1, SEEK_CUR)) { - LOG_DEBUG("fseek failed."); - return NULL; - } - ctx.state = ctx.state_before_backslash; - break; - } + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = c; + ctx.te++; + break; + } + case STATE_BACKSLASH: { + if (!is_whitespace(c)) { + str--; + ctx.state = ctx.state_before_backslash; break; } - case STATE_DIRECTIVE_NAME: { - if (c == '}') { - directive_name[ctx.dn] = 0; - ctx.dn = 0; - directive = cho_directive_parse(&ctx, directive_name); - /* printf( - "directive: '%s'\ndtype: %s, stype: %s, position: %s\n", - directive_name, cho_debug_dtype(directive->dtype), cho_debug_the_stype(directive->stype), cho_debug_the_pos(directive->position) - ); */ - switch (directive->dtype) { - case DT_ENVIRONMENT: { - ctx.current_ttype = directive->ttype; - switch (directive->position) { - case POS_START: { + break; + } + case STATE_DIRECTIVE_NAME: { + if (c == '}') { + directive_name[ctx.dn] = 0; + ctx.dn = 0; + directive = cho_directive_parse(&ctx, directive_name); + /* printf( + "directive: '%s'\ndtype: %s, stype: %s, position: %s\n", + directive_name, cho_debug_dtype(directive->dtype), cho_debug_the_stype(directive->stype), cho_debug_the_pos(directive->position) + ); */ + switch (directive->dtype) { + case DT_ENVIRONMENT: { + ctx.current_ttype = directive->ttype; + switch (directive->position) { + case POS_START: { + if (directive->stype == ST_CUSTOM) { + memset(custom_directive, 0, sizeof(custom_directive)); + strcpy(custom_directive, &directive_name[9]); + } + cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); + free((*lines)[ctx.li]->items); + ctx.lii = 0; + free((*lines)[ctx.li]); + (*lines)[ctx.li] = NULL; + ctx.se++; + ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); + ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); + ctx.songs[ctx.so]->sections[ctx.se]->type = directive->stype; + ctx.li = 0; + lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; + *lines = emalloc(sizeof(struct ChoLine *)); + (*lines)[ctx.li] = cho_line_new(); + (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); + ctx.songs[ctx.so]->present_text_types[directive->ttype] = true; + break; + } + case POS_END: { + if (directive->stype == ctx.songs[ctx.so]->sections[ctx.se]->type) { if (directive->stype == ST_CUSTOM) { - memset(custom_directive, 0, sizeof(custom_directive)); - strcpy(custom_directive, &directive_name[9]); + if (strcmp(custom_directive, &directive_name[7]) != 0) { + break; + } } cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); free((*lines)[ctx.li]->items); @@ -4264,101 +4285,52 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) ctx.se++; ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); - ctx.songs[ctx.so]->sections[ctx.se]->type = directive->stype; ctx.li = 0; lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; *lines = emalloc(sizeof(struct ChoLine *)); (*lines)[ctx.li] = cho_line_new(); (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - ctx.songs[ctx.so]->present_text_types[directive->ttype] = true; - break; } - case POS_END: { - if (directive->stype == ctx.songs[ctx.so]->sections[ctx.se]->type) { - if (directive->stype == ST_CUSTOM) { - if (strcmp(custom_directive, &directive_name[7]) != 0) { - break; - } + break; + } + case POS_NO: { + /* INFO: {chorus} */ + chorus = cho_find_previous_chorus(ctx.songs[ctx.so]->sections, ctx.se); + if (chorus) { + if (config->output->chorus->quote) { + if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; } - cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); - free((*lines)[ctx.li]->items); + ctx.lii++; + (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = NULL; ctx.lii = 0; - free((*lines)[ctx.li]); + ctx.te = 0; + (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); + (*lines)[ctx.li]->text_above[ctx.lia] = NULL; + ctx.lia = 0; + cho_line_free((*lines)[ctx.li]); (*lines)[ctx.li] = NULL; + ctx.li = 0; + ctx.se++; + ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); + ctx.songs[ctx.so]->sections[ctx.se] = cho_section_copy(chorus); ctx.se++; ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); - ctx.li = 0; lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; *lines = emalloc(sizeof(struct ChoLine *)); (*lines)[ctx.li] = cho_line_new(); (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - } - break; - } - case POS_NO: { - /* INFO: {chorus} */ - chorus = cho_find_previous_chorus(ctx.songs[ctx.so]->sections, ctx.se); - if (chorus) { - if (config->output->chorus->quote) { - if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; - } - ctx.lii++; - (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = NULL; - ctx.lii = 0; - ctx.te = 0; - (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[ctx.li]->text_above[ctx.lia] = NULL; - ctx.lia = 0; - cho_line_free((*lines)[ctx.li]); - (*lines)[ctx.li] = NULL; - ctx.li = 0; - ctx.se++; - ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); - ctx.songs[ctx.so]->sections[ctx.se] = cho_section_copy(chorus); - ctx.se++; - ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); - ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); - lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; - *lines = emalloc(sizeof(struct ChoLine *)); - (*lines)[ctx.li] = cho_line_new(); - (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - } else { - if (chorus->label) { - label = strdup(chorus->label->text); - } else { - label = strdup(config->output->chorus->label); - } - if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; - if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { - cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); - } else { - ctx.lii++; - } - } else { - ctx.lii++; - } - (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); - ctx.current_ttype = TT_LABEL; - (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_new_default(&ctx); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = label; - ctx.te += strlen(label); - } } else { - if (config->output->chorus->quote) { - cho_log(&ctx, LOG_WARN, "Can't quote chorus because it's not defined previously."); + if (chorus->label) { + label = strdup(chorus->label->text); + } else { + label = strdup(config->output->chorus->label); } - label = strdup(config->output->chorus->label); if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; @@ -4378,299 +4350,302 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) (*lines)[ctx.li]->items[ctx.lii]->u.text->text = label; ctx.te += strlen(label); } - break; - } - default: - cho_log(&ctx, LOG_ERR, "Invalid position value '%d'.", directive->position); - return NULL; + } else { + if (config->output->chorus->quote) { + cho_log(&ctx, LOG_WARN, "Can't quote chorus because it's not defined previously."); + } + label = strdup(config->output->chorus->label); + if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; + if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { + cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); + } else { + ctx.lii++; + } + } else { + ctx.lii++; + } + (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); + cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); + ctx.current_ttype = TT_LABEL; + (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_new_default(&ctx); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = label; + ctx.te += strlen(label); } break; } - case DT_METADATA: - cho_log(&ctx, LOG_WARN, "Ignoring metadata directive '%s' because it has no value.", directive_name); - break; - case DT_FORMATTING: - cho_log(&ctx, LOG_WARN, "Formatting directive '%s' has no value.", directive_name); - break; - case DT_IMAGE: - cho_log(&ctx, LOG_ERR, "Directive 'image' has no value."); + default: + cho_log(&ctx, LOG_ERR, "Invalid position value '%d'.", directive->position); return NULL; - case DT_PREAMBLE: { - // INFO: The only preamble directive is 'new_song' - cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); - free((*lines)[ctx.li]->items); - free((*lines)[ctx.li]); - (*lines)[ctx.li] = NULL; - ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); - ctx.songs[ctx.so]->metadata[ctx.m] = NULL; - ctx.se++; - ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); - ctx.songs[ctx.so]->sections[ctx.se] = NULL; - ctx.songs[ctx.so]->diagrams = erealloc(ctx.songs[ctx.so]->diagrams, (ctx.dia+1) * sizeof(struct ChordDiagram *)); - ctx.songs[ctx.so]->diagrams[ctx.dia] = NULL; - if (!cho_style_reset_default(&ctx)) { - LOG_DEBUG("cho_style_reset_default failed."); - return NULL; - } - ctx.so++; - ctx.songs = erealloc(ctx.songs, (ctx.so+1) * sizeof(struct ChoSong *)); - ctx.songs[ctx.so] = cho_song_new(&ctx); - if (!ctx.songs[ctx.so]) { - LOG_DEBUG("cho_song_new failed."); - return NULL; - } - free(ctx.transpose_history); - ctx.th = 0; - ctx.transpose_history = emalloc((ctx.th+1) * sizeof(int *)); - ctx.transpose_history[ctx.th] = 0; - ctx.transpose = &ctx.transpose_history[ctx.th]; - ctx.th++; - ctx.se = 0; - ctx.li = 0; - ctx.lii = 0; - ctx.m = 11; - ctx.dia = 0; - ctx.songs[ctx.so]->sections = emalloc((ctx.se+1) * sizeof(struct ChoSection *)); - ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); - lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; - *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); - (*lines)[ctx.li] = cho_line_new(); - (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - break; } - case DT_FONT: { - sprop.ttype = directive->ttype; - sprop.type = directive->sprop; - switch (directive->sprop) { - case SPT_FONT: - sprop.u.font_name = NULL; - break; - case SPT_SIZE: - sprop.u.font_size = EMPTY_DOUBLE; - break; - case SPT_COLOR: - sprop.u.foreground_color = NULL; - break; - default: - cho_log(&ctx, LOG_ERR, "Invalid style property type '%d'.", directive->sprop); - return NULL; - } - if (!cho_style_change_default(&ctx, sprop)) { - LOG_DEBUG("cho_style_change_default failed."); - return NULL; - } - break; + break; + } + case DT_METADATA: + cho_log(&ctx, LOG_WARN, "Ignoring metadata directive '%s' because it has no value.", directive_name); + break; + case DT_FORMATTING: + cho_log(&ctx, LOG_WARN, "Formatting directive '%s' has no value.", directive_name); + break; + case DT_IMAGE: + cho_log(&ctx, LOG_ERR, "Directive 'image' has no value."); + return NULL; + case DT_PREAMBLE: { + // INFO: The only preamble directive is 'new_song' + cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); + free((*lines)[ctx.li]->items); + free((*lines)[ctx.li]); + (*lines)[ctx.li] = NULL; + ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); + ctx.songs[ctx.so]->metadata[ctx.m] = NULL; + ctx.se++; + ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); + ctx.songs[ctx.so]->sections[ctx.se] = NULL; + ctx.songs[ctx.so]->diagrams = erealloc(ctx.songs[ctx.so]->diagrams, (ctx.dia+1) * sizeof(struct ChordDiagram *)); + ctx.songs[ctx.so]->diagrams[ctx.dia] = NULL; + if (!cho_style_reset_default(&ctx)) { + LOG_DEBUG("cho_style_reset_default failed."); + return NULL; } - case DT_CHORD: { - switch (directive->ctype) { - case TRANSPOSE: - ctx.transpose--; - ctx.th--; - break; - case DEFINE: - cho_log(&ctx, LOG_WARN, "Ignoring chord directive '%s' because it has no value.", directive_name); - break; - } - break; + for (int e = 0; e<ctx.ia; e++) { + cho_image_free(ctx.image_assets[e]); } - case DT_OUTPUT: - if (directive->btype != -1) { - (*lines)[ctx.li]->btype = directive->btype; - } + free(ctx.image_assets); + ctx.image_assets = NULL; + ctx.ia = 0; + ctx.so++; + ctx.songs = erealloc(ctx.songs, (ctx.so+1) * sizeof(struct ChoSong *)); + ctx.songs[ctx.so] = cho_song_new(&ctx); + if (!ctx.songs[ctx.so]) { + LOG_DEBUG("cho_song_new failed."); + return NULL; + } + free(ctx.transpose_history); + ctx.th = 0; + ctx.transpose_history = emalloc((ctx.th+1) * sizeof(int *)); + ctx.transpose_history[ctx.th] = 0; + ctx.transpose = &ctx.transpose_history[ctx.th]; + ctx.th++; + ctx.se = 0; + ctx.li = 0; + ctx.lii = 0; + // ctx.m = 0; + ctx.dia = 0; + ctx.songs[ctx.so]->sections = emalloc((ctx.se+1) * sizeof(struct ChoSection *)); + ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); + lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; + *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); + (*lines)[ctx.li] = cho_line_new(); + (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); + break; + } + case DT_FONT: { + sprop.ttype = directive->ttype; + sprop.type = directive->sprop; + switch (directive->sprop) { + case SPT_FONT: + sprop.u.font_name = NULL; break; - case DT_EXTENSION: - // INFO: Such a directive should not be logged. + case SPT_SIZE: + sprop.u.font_size = EMPTY_DOUBLE; break; - case DT_CUSTOM: - cho_log(&ctx, LOG_INFO, "Ignoring custom directive '%s'.", directive_name); + case SPT_COLOR: + sprop.u.foreground_color = NULL; break; default: - cho_log(&ctx, LOG_ERR, "Invalid directive '%s'.", directive_name); + cho_log(&ctx, LOG_ERR, "Invalid style property type '%d'.", directive->sprop); return NULL; } - if (directive->stype == ST_TAB) { - ctx.state = STATE_TAB; - } else { - ctx.state = STATE_LYRICS; + if (!cho_style_change_default(&ctx, sprop)) { + LOG_DEBUG("cho_style_change_default failed."); + return NULL; } - cho_directive_free(directive); - directive = NULL; break; } - if (c == '{') { - cho_log(&ctx, LOG_ERR, "Can't start a new directive if the previous one is not yet closed."); - return NULL; - } - if (c == '\n') { - if (prev_c == '\\') { - ctx.state_before_backslash = STATE_DIRECTIVE_NAME; - ctx.state = STATE_BACKSLASH; - ctx.dn--; + case DT_CHORD: { + switch (directive->ctype) { + case TRANSPOSE: + ctx.transpose--; + ctx.th--; break; + case DEFINE: + cho_log(&ctx, LOG_WARN, "Ignoring chord directive '%s' because it has no value.", directive_name); + break; + } + break; + } + case DT_OUTPUT: + if (directive->btype != -1) { + (*lines)[ctx.li]->btype = directive->btype; } - cho_log(&ctx, LOG_ERR, "Can't have a newline in a directive name."); + break; + case DT_EXTENSION: + // INFO: Such a directive should not be logged. + break; + case DT_CUSTOM: + cho_log(&ctx, LOG_INFO, "Ignoring custom directive '%s'.", directive_name); + break; + default: + cho_log(&ctx, LOG_ERR, "Invalid directive '%s'.", directive_name); return NULL; } - if (c == ':' || c == ' ') { - directive_name[ctx.dn] = 0; - ctx.dn = 0; - ctx.state = STATE_DIRECTIVE_VALUE; + if (directive->stype == ST_TAB) { + ctx.state = STATE_TAB; + } else { + ctx.state = STATE_LYRICS; + } + cho_directive_free(directive); + directive = NULL; + break; + } + if (c == '{') { + cho_log(&ctx, LOG_ERR, "Can't start a new directive if the previous one is not yet closed."); + return NULL; + } + if (c == '\n') { + if (prev_c == '\\') { + ctx.state_before_backslash = STATE_DIRECTIVE_NAME; + ctx.state = STATE_BACKSLASH; + ctx.dn--; break; } - directive_name[ctx.dn] = c; - ctx.dn++; + cho_log(&ctx, LOG_ERR, "Can't have a newline in a directive name."); + return NULL; + } + if (c == ':' || c == ' ') { + directive_name[ctx.dn] = 0; + ctx.dn = 0; + ctx.state = STATE_DIRECTIVE_VALUE; break; } - case STATE_DIRECTIVE_VALUE: { - if (c == '}') { - directive_value[ctx.dv] = 0; - ctx.dv = 0; - stripped_directive_value = str_remove_leading_whitespace(directive_value); - directive = cho_directive_parse(&ctx, directive_name); - /* printf( - "directive: '%s'\ndtype: %s, stype: %s, position: %s\n", - directive_name, dtype(directive->dtype), the_stype(directive->stype), pos(directive->position) - ); */ - switch (directive->dtype) { - case DT_ENVIRONMENT: { - if (strlen(stripped_directive_value) > 0) { - ctx.songs[ctx.so]->present_text_types[TT_LABEL] = true; + directive_name[ctx.dn] = c; + ctx.dn++; + break; + } + case STATE_DIRECTIVE_VALUE: { + if (c == '}') { + directive_value[ctx.dv] = 0; + ctx.dv = 0; + stripped_directive_value = str_remove_leading_whitespace(directive_value); + directive = cho_directive_parse(&ctx, directive_name); + /* printf( + "directive: '%s'\ndtype: %s, stype: %s, position: %s\n", + directive_name, dtype(directive->dtype), the_stype(directive->stype), pos(directive->position) + ); */ + switch (directive->dtype) { + case DT_ENVIRONMENT: { + if (strlen(stripped_directive_value) > 0) { + ctx.songs[ctx.so]->present_text_types[TT_LABEL] = true; + } + ctx.current_ttype = directive->ttype; + switch (directive->position) { + case POS_START: { + if (directive->stype == ST_CUSTOM) { + memset(custom_directive, 0, sizeof(custom_directive)); + strcpy(custom_directive, &directive_name[9]); } - ctx.current_ttype = directive->ttype; - switch (directive->position) { - case POS_START: { - if (directive->stype == ST_CUSTOM) { - memset(custom_directive, 0, sizeof(custom_directive)); - strcpy(custom_directive, &directive_name[9]); - } - cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); - free((*lines)[ctx.li]->items); - ctx.lii = 0; - free((*lines)[ctx.li]); - (*lines)[ctx.li] = NULL; - ctx.se++; - ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); - ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); - ctx.songs[ctx.so]->sections[ctx.se]->type = directive->stype; - ctx.songs[ctx.so]->sections[ctx.se]->label = emalloc(sizeof(struct ChoText)); - if (strstr(directive_value, "=")) { - label = cho_directive_label_parse(&ctx, directive_name, directive_value); - if (!label) { - LOG_DEBUG("cho_directive_label_parse failed."); - cho_log(&ctx, LOG_ERR, "Failed to parse the section label. You have to ways of specifying a label:\n\t\t\t1. {start_of_*: label=\"Label name\"}\n\t\t\t2. {start_of*: Label name}"); - return NULL; - } - ctx.songs[ctx.so]->sections[ctx.se]->label->text = label; - } else { - ctx.songs[ctx.so]->sections[ctx.se]->label->text = strdup(stripped_directive_value); - } - ctx.songs[ctx.so]->sections[ctx.se]->label->style = cho_style_new_from_config(&ctx, TT_LABEL); - if (ctx.directive_has_tag) { - cho_style_complement(ctx.songs[ctx.so]->sections[ctx.se]->label->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); - ctx.directive_has_tag = false; + cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); + free((*lines)[ctx.li]->items); + ctx.lii = 0; + free((*lines)[ctx.li]); + (*lines)[ctx.li] = NULL; + ctx.se++; + ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); + ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); + ctx.songs[ctx.so]->sections[ctx.se]->type = directive->stype; + ctx.songs[ctx.so]->sections[ctx.se]->label = emalloc(sizeof(struct ChoText)); + if (strstr(directive_value, "=")) { + label = cho_directive_label_parse(&ctx, directive_name, directive_value); + if (!label) { + LOG_DEBUG("cho_directive_label_parse failed."); + cho_log(&ctx, LOG_ERR, "Failed to parse the section label. You have to ways of specifying a label:\n\t\t\t1. {start_of_*: label=\"Label name\"}\n\t\t\t2. {start_of*: Label name}"); + return NULL; } + ctx.songs[ctx.so]->sections[ctx.se]->label->text = label; + } else { + ctx.songs[ctx.so]->sections[ctx.se]->label->text = strdup(stripped_directive_value); + } + ctx.songs[ctx.so]->sections[ctx.se]->label->style = cho_style_new_from_config(&ctx, TT_LABEL); + if (ctx.directive_has_tag) { + cho_style_complement(ctx.songs[ctx.so]->sections[ctx.se]->label->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); + ctx.directive_has_tag = false; + } + ctx.li = 0; + lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; + *lines = emalloc(sizeof(struct ChoLine *)); + (*lines)[ctx.li] = cho_line_new(); + (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); + ctx.songs[ctx.so]->present_text_types[directive->ttype] = true; + break; + } + case POS_END: { + if (directive->stype == ctx.songs[ctx.so]->sections[ctx.se]->type) { + cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); + free((*lines)[ctx.li]->items); + ctx.lii = 0; + free((*lines)[ctx.li]); + (*lines)[ctx.li] = NULL; + ctx.se++; + ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); + ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); ctx.li = 0; lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; *lines = emalloc(sizeof(struct ChoLine *)); (*lines)[ctx.li] = cho_line_new(); (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - ctx.songs[ctx.so]->present_text_types[directive->ttype] = true; - break; } - case POS_END: { - if (directive->stype == ctx.songs[ctx.so]->sections[ctx.se]->type) { - cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); - free((*lines)[ctx.li]->items); + break; + } + case POS_NO: { + /* INFO: {chorus: ...} */ + label = strdup(stripped_directive_value); + chorus = cho_find_previous_chorus(ctx.songs[ctx.so]->sections, ctx.se); + if (chorus) { + if (config->output->chorus->quote) { + if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; + } + ctx.lii++; + (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = NULL; ctx.lii = 0; - free((*lines)[ctx.li]); + ctx.te = 0; + (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); + (*lines)[ctx.li]->text_above[ctx.lia] = NULL; + ctx.lia = 0; + cho_line_free((*lines)[ctx.li]); + // songs[so]->sections[se]->lines = erealloc(songs[so]->sections[se]->lines, (li+1) * sizeof(struct ChoLine *)); (*lines)[ctx.li] = NULL; + ctx.li = 0; + ctx.se++; + ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); + ctx.songs[ctx.so]->sections[ctx.se] = cho_section_copy(chorus); + if (ctx.songs[ctx.so]->sections[ctx.se]->label) { + free(ctx.songs[ctx.so]->sections[ctx.se]->label->text); + ctx.songs[ctx.so]->sections[ctx.se]->label->text = label; + } else { + ctx.current_ttype = TT_LABEL; + ctx.songs[ctx.so]->sections[ctx.se]->label = cho_text_new(&ctx); + ctx.songs[ctx.so]->sections[ctx.se]->label->text = label; + } + if (ctx.directive_has_tag) { + cho_style_complement(ctx.songs[ctx.so]->sections[ctx.se]->label->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); + ctx.directive_has_tag = false; + } ctx.se++; ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); - ctx.li = 0; lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; *lines = emalloc(sizeof(struct ChoLine *)); (*lines)[ctx.li] = cho_line_new(); (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - } - break; - } - case POS_NO: { - /* INFO: {chorus: ...} */ - label = strdup(stripped_directive_value); - chorus = cho_find_previous_chorus(ctx.songs[ctx.so]->sections, ctx.se); - if (chorus) { - if (config->output->chorus->quote) { - if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; - } - ctx.lii++; - (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = NULL; - ctx.lii = 0; - ctx.te = 0; - (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[ctx.li]->text_above[ctx.lia] = NULL; - ctx.lia = 0; - cho_line_free((*lines)[ctx.li]); - // songs[so]->sections[se]->lines = erealloc(songs[so]->sections[se]->lines, (li+1) * sizeof(struct ChoLine *)); - (*lines)[ctx.li] = NULL; - ctx.li = 0; - ctx.se++; - ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); - ctx.songs[ctx.so]->sections[ctx.se] = cho_section_copy(chorus); - if (ctx.songs[ctx.so]->sections[ctx.se]->label) { - free(ctx.songs[ctx.so]->sections[ctx.se]->label->text); - ctx.songs[ctx.so]->sections[ctx.se]->label->text = label; - } else { - ctx.current_ttype = TT_LABEL; - ctx.songs[ctx.so]->sections[ctx.se]->label = cho_text_new(&ctx); - ctx.songs[ctx.so]->sections[ctx.se]->label->text = label; - } - if (ctx.directive_has_tag) { - cho_style_complement(ctx.songs[ctx.so]->sections[ctx.se]->label->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); - ctx.directive_has_tag = false; - } - ctx.se++; - ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); - ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); - lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; - *lines = emalloc(sizeof(struct ChoLine *)); - (*lines)[ctx.li] = cho_line_new(); - (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - } else { - if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; - if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { - cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); - } else { - ctx.lii++; - } - } else { - ctx.lii++; - } - (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); - ctx.current_ttype = TT_LABEL; - (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_new_default(&ctx); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = label; - if (ctx.directive_has_tag) { - cho_style_complement((*lines)[ctx.li]->items[ctx.lii]->u.text->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); - ctx.directive_has_tag = false; - } - ctx.te += strlen(label); - } } else { - if (config->output->chorus->quote) { - cho_log(&ctx, LOG_WARN, "Can't quote chorus because it's not defined previously."); - } if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; @@ -4690,735 +4665,653 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) (*lines)[ctx.li]->items[ctx.lii]->u.text->text = label; if (ctx.directive_has_tag) { cho_style_complement((*lines)[ctx.li]->items[ctx.lii]->u.text->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); + ctx.directive_has_tag = false; } ctx.te += strlen(label); } - break; - } - default: - cho_log(&ctx, LOG_ERR, "Invalid position value '%d'.", directive->position); - return NULL; - } - break; - } - case DT_METADATA: { - metadata_value = strdup(stripped_directive_value); - if (strlen(metadata_value) == 0) { - cho_log(&ctx, LOG_WARN, "Ignoring metadata directive '%s' because it has no value.", directive_name); - free(metadata_value); - break; - } - switch (directive->meta) { - case TITLE: - ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); - ctx.songs[ctx.so]->metadata[ctx.m] = cho_metadata_new(&ctx); - ctx.songs[ctx.so]->metadata[ctx.m]->name = strdup("title"); - ctx.songs[ctx.so]->metadata[ctx.m]->value = metadata_value; - cho_style_free(ctx.songs[ctx.so]->metadata[ctx.m]->style); - ctx.songs[ctx.so]->metadata[ctx.m]->style = cho_style_copy(directive->style); - ctx.songs[ctx.so]->present_text_types[TT_TITLE] = true; - break; - case SUBTITLE: - ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); - ctx.songs[ctx.so]->metadata[ctx.m] = cho_metadata_new(&ctx); - ctx.songs[ctx.so]->metadata[ctx.m]->name = strdup("subtitle"); - ctx.songs[ctx.so]->metadata[ctx.m]->value = metadata_value; - cho_style_free(ctx.songs[ctx.so]->metadata[ctx.m]->style); - ctx.songs[ctx.so]->metadata[ctx.m]->style = cho_style_copy(directive->style); - ctx.songs[ctx.so]->present_text_types[TT_SUBTITLE] = true; - break; - default: - if (!strcmp(directive_name, "meta")) { - metadata = cho_metadata_split(&ctx, directive_value); - if (!metadata) { - LOG_DEBUG("cho_metadata_split failed."); - return NULL; + } else { + if (config->output->chorus->quote) { + cho_log(&ctx, LOG_WARN, "Can't quote chorus because it's not defined previously."); + } + if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; + if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { + cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); + } else { + ctx.lii++; } - ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); - ctx.songs[ctx.so]->metadata[ctx.m] = metadata; - free(metadata_value); } else { - ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); - ctx.songs[ctx.so]->metadata[ctx.m] = cho_metadata_new(&ctx); - ctx.songs[ctx.so]->metadata[ctx.m]->name = strdup(directive_name); - ctx.songs[ctx.so]->metadata[ctx.m]->value = metadata_value; + ctx.lii++; } + (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); + cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); + ctx.current_ttype = TT_LABEL; + (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_new_default(&ctx); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = label; + if (ctx.directive_has_tag) { + cho_style_complement((*lines)[ctx.li]->items[ctx.lii]->u.text->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); + } + ctx.te += strlen(label); } - if (ctx.directive_has_tag) { - cho_style_complement(ctx.songs[ctx.so]->metadata[ctx.m]->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); - ctx.directive_has_tag = false; - } - ctx.m++; break; } - case DT_FORMATTING: { - if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; - if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { - cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); - } else { - ctx.lii++; - } - } else { - ctx.lii++; - } - (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); - (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_copy(directive->style); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = strdup(stripped_directive_value); - if (ctx.directive_has_tag) { - cho_style_complement((*lines)[ctx.li]->items[ctx.lii]->u.text->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); - ctx.directive_has_tag = false; - } - ctx.te += strlen(stripped_directive_value); - ctx.songs[ctx.so]->present_text_types[directive->ttype] = true; + default: + cho_log(&ctx, LOG_ERR, "Invalid position value '%d'.", directive->position); + return NULL; + } + break; + } + case DT_METADATA: { + metadata_value = strdup(stripped_directive_value); + if (strlen(metadata_value) == 0) { + cho_log(&ctx, LOG_WARN, "Ignoring metadata directive '%s' because it has no value.", directive_name); + free(metadata_value); break; } - case DT_IMAGE: { - if (strstr(directive_value, "=")) { - image = cho_image_directive_parse(&ctx, directive_value); - if (!image) { - LOG_DEBUG("cho_image_directive_parse failed."); + switch (directive->meta) { + case TITLE: + ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); + ctx.songs[ctx.so]->metadata[ctx.m] = cho_metadata_new(&ctx); + ctx.songs[ctx.so]->metadata[ctx.m]->name = strdup("title"); + ctx.songs[ctx.so]->metadata[ctx.m]->value = metadata_value; + cho_style_free(ctx.songs[ctx.so]->metadata[ctx.m]->style); + ctx.songs[ctx.so]->metadata[ctx.m]->style = cho_style_copy(directive->style); + ctx.songs[ctx.so]->present_text_types[TT_TITLE] = true; + break; + case SUBTITLE: + ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); + ctx.songs[ctx.so]->metadata[ctx.m] = cho_metadata_new(&ctx); + ctx.songs[ctx.so]->metadata[ctx.m]->name = strdup("subtitle"); + ctx.songs[ctx.so]->metadata[ctx.m]->value = metadata_value; + cho_style_free(ctx.songs[ctx.so]->metadata[ctx.m]->style); + ctx.songs[ctx.so]->metadata[ctx.m]->style = cho_style_copy(directive->style); + ctx.songs[ctx.so]->present_text_types[TT_SUBTITLE] = true; + break; + default: + if (!strcmp(directive_name, "meta")) { + metadata = cho_metadata_split(&ctx, directive_value); + if (!metadata) { + LOG_DEBUG("cho_metadata_split failed."); return NULL; } + ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); + ctx.songs[ctx.so]->metadata[ctx.m] = metadata; + free(metadata_value); } else { - image = cho_image_new(); - image->src = strdup(stripped_directive_value); + ctx.songs[ctx.so]->metadata = erealloc(ctx.songs[ctx.so]->metadata, (ctx.m+1) * sizeof(struct ChoMetadata *)); + ctx.songs[ctx.so]->metadata[ctx.m] = cho_metadata_new(&ctx); + ctx.songs[ctx.so]->metadata[ctx.m]->name = strdup(directive_name); + ctx.songs[ctx.so]->metadata[ctx.m]->value = metadata_value; } - if (image->is_asset) { - ctx.image_assets = erealloc(ctx.image_assets, (ctx.ia+1) * sizeof(struct ChoImage *)); - ctx.image_assets[ctx.ia] = image; - ctx.ia++; + } + if (ctx.directive_has_tag) { + cho_style_complement(ctx.songs[ctx.so]->metadata[ctx.m]->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); + ctx.directive_has_tag = false; + } + ctx.m++; + break; + } + case DT_FORMATTING: { + if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; + if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { + cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); } else { - if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; - ctx.te = 0; - } ctx.lii++; - (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - cho_text_free((*lines)[ctx.li]->items[ctx.lii]->u.text); - (*lines)[ctx.li]->items[ctx.lii]->is_text = false; - (*lines)[ctx.li]->items[ctx.lii]->u.image = image; } - break; + } else { + ctx.lii++; } - case DT_PREAMBLE: - cho_log(&ctx, LOG_ERR, "Preamble directive '%s' can't have a value.", directive_name); - return NULL; - case DT_FONT: { - sprop.ttype = directive->ttype; - char *dir_value = strdup(stripped_directive_value); - switch (directive->sprop) { - case SPT_FONT: - sprop.u.font_name = emalloc((strlen(dir_value)+1) * sizeof(char)); - strcpy(sprop.u.font_name, dir_value); - sprop.type = SPT_FONT; - break; - case SPT_SIZE: - sprop.u.font_size = strtod(dir_value, NULL); - if (sprop.u.font_size == 0.0) { - cho_log(&ctx, LOG_ERR, "Font directive '%s' has an invalid value.", directive_name); - return NULL; - } - sprop.type = SPT_SIZE; - break; - case SPT_COLOR: - sprop.u.foreground_color = cho_color_parse(dir_value); - if (sprop.u.foreground_color == NULL) { - cho_log(&ctx, LOG_ERR, "Font directive '%s' has an invalid value.", directive_name); - return NULL; - } - sprop.type = SPT_COLOR; - break; - default: - cho_log(&ctx, LOG_ERR, "Invalid style property type '%d'.", directive->sprop); - return NULL; - } - if (!cho_style_change_default(&ctx, sprop)) { - LOG_DEBUG("cho_style_change_default failed."); + (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); + cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); + (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_copy(directive->style); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = strdup(stripped_directive_value); + if (ctx.directive_has_tag) { + cho_style_complement((*lines)[ctx.li]->items[ctx.lii]->u.text->style, ctx.tags[ctx.ta]->style, &ctx.tags[ctx.ta]->style_presence); + ctx.directive_has_tag = false; + } + ctx.te += strlen(stripped_directive_value); + ctx.songs[ctx.so]->present_text_types[directive->ttype] = true; + break; + } + case DT_IMAGE: { + if (strstr(directive_value, "=")) { + image = cho_image_directive_parse(&ctx, directive_value); + if (!image) { + LOG_DEBUG("cho_image_directive_parse failed."); return NULL; } - if (sprop.type == SPT_FONT) { - free(sprop.u.font_name); - } else if (sprop.type == SPT_COLOR) { - free(sprop.u.foreground_color); - } - free(dir_value); - break; + } else { + image = cho_image_new(); + image->src = strdup(stripped_directive_value); } - case DT_CHORD: { - switch (directive->ctype) { - case TRANSPOSE: - if (!transposition_parse(directive_value, &transpose)) { - LOG_DEBUG("transposition_parse failed."); - cho_log(&ctx, LOG_ERR, "Directive 'transpose' has an invalid value."); - return NULL; - } - ctx.transpose_history = erealloc(ctx.transpose_history, (ctx.th+1) * sizeof(int *)); - ctx.transpose_history[ctx.th] = ctx.transpose_history[ctx.th-1] + transpose; - ctx.transpose = &ctx.transpose_history[ctx.th]; - ctx.th++; - break; - case DEFINE: - diagram = cho_chord_diagram_parse(&ctx, directive_value, ctx.songs[ctx.so]->diagrams, ctx.dia); - if (!diagram) { - LOG_DEBUG("cho_chord_diagram_parse failed."); - return NULL; - } - ctx.songs[ctx.so]->diagrams = erealloc(ctx.songs[ctx.so]->diagrams, (ctx.dia+1) * sizeof(struct ChordDiagram *)); - ctx.songs[ctx.so]->diagrams[ctx.dia] = diagram; - ctx.dia++; - break; + if (image->is_asset) { + ctx.image_assets = erealloc(ctx.image_assets, (ctx.ia+1) * sizeof(struct ChoImage *)); + ctx.image_assets[ctx.ia] = image; + ctx.ia++; + } else { + if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; + ctx.te = 0; } - break; + ctx.lii++; + (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); + cho_text_free((*lines)[ctx.li]->items[ctx.lii]->u.text); + (*lines)[ctx.li]->items[ctx.lii]->is_text = false; + (*lines)[ctx.li]->items[ctx.lii]->u.image = image; } - case DT_OUTPUT: - cho_log(&ctx, LOG_ERR, "Directive '%s' can't have a value.", directive_name); - return NULL; - case DT_EXTENSION: - // INFO: Such a directive should not be logged. + break; + } + case DT_PREAMBLE: + cho_log(&ctx, LOG_ERR, "Preamble directive '%s' can't have a value.", directive_name); + return NULL; + case DT_FONT: { + sprop.ttype = directive->ttype; + char *dir_value = strdup(stripped_directive_value); + switch (directive->sprop) { + case SPT_FONT: + sprop.u.font_name = emalloc((strlen(dir_value)+1) * sizeof(char)); + strcpy(sprop.u.font_name, dir_value); + sprop.type = SPT_FONT; + break; + case SPT_SIZE: + sprop.u.font_size = strtod(dir_value, NULL); + if (sprop.u.font_size == 0.0) { + cho_log(&ctx, LOG_ERR, "Font directive '%s' has an invalid value.", directive_name); + return NULL; + } + sprop.type = SPT_SIZE; break; - case DT_CUSTOM: - cho_log(&ctx, LOG_INFO, "Ignoring custom directive '%s'.", directive_name); + case SPT_COLOR: + sprop.u.foreground_color = cho_color_parse(dir_value); + if (sprop.u.foreground_color == NULL) { + cho_log(&ctx, LOG_ERR, "Font directive '%s' has an invalid value.", directive_name); + return NULL; + } + sprop.type = SPT_COLOR; break; default: - cho_log(&ctx, LOG_ERR, "Invalid directive type '%d'.", directive->dtype); + cho_log(&ctx, LOG_ERR, "Invalid style property type '%d'.", directive->sprop); return NULL; } - memset(directive_value, 0, strlen(directive_value)); - free(stripped_directive_value); - cho_directive_free(directive); - directive = NULL; - ctx.state = STATE_LYRICS; - break; - } - if (c == '<') { - ctx.state_before_tag = STATE_DIRECTIVE_VALUE; - ctx.state = STATE_MARKUP_TAG; + if (!cho_style_change_default(&ctx, sprop)) { + LOG_DEBUG("cho_style_change_default failed."); + return NULL; + } + if (sprop.type == SPT_FONT) { + free(sprop.u.font_name); + } else if (sprop.type == SPT_COLOR) { + free(sprop.u.foreground_color); + } + free(dir_value); break; } - if (c == '{') { - cho_log(&ctx, LOG_ERR, "Can't start a new directive if the previous one is not yet closed."); - return NULL; - } - if (c == '\n') { - if (prev_c == '\\') { - ctx.state_before_backslash = STATE_DIRECTIVE_VALUE; - ctx.state = STATE_BACKSLASH; - ctx.dv--; + case DT_CHORD: { + switch (directive->ctype) { + case TRANSPOSE: + if (!transposition_parse(directive_value, &transpose)) { + LOG_DEBUG("transposition_parse failed."); + cho_log(&ctx, LOG_ERR, "Directive 'transpose' has an invalid value."); + return NULL; + } + ctx.transpose_history = erealloc(ctx.transpose_history, (ctx.th+1) * sizeof(int *)); + ctx.transpose_history[ctx.th] = ctx.transpose_history[ctx.th-1] + transpose; + ctx.transpose = &ctx.transpose_history[ctx.th]; + ctx.th++; + break; + case DEFINE: + diagram = cho_chord_diagram_parse(&ctx, directive_value, ctx.songs[ctx.so]->diagrams, ctx.dia); + if (!diagram) { + LOG_DEBUG("cho_chord_diagram_parse failed."); + return NULL; + } + ctx.songs[ctx.so]->diagrams = erealloc(ctx.songs[ctx.so]->diagrams, (ctx.dia+1) * sizeof(struct ChordDiagram *)); + ctx.songs[ctx.so]->diagrams[ctx.dia] = diagram; + ctx.dia++; break; } - cho_log(&ctx, LOG_ERR, "Newline character inside a directive value is invalid."); - return NULL; + break; } - if (ctx.dv > 4094) { - cho_log(&ctx, LOG_ERR, "Directive value can't be greater than 4095 bytes."); + case DT_OUTPUT: + cho_log(&ctx, LOG_ERR, "Directive '%s' can't have a value.", directive_name); + return NULL; + case DT_EXTENSION: + // INFO: Such a directive should not be logged. + break; + case DT_CUSTOM: + cho_log(&ctx, LOG_INFO, "Ignoring custom directive '%s'.", directive_name); + break; + default: + cho_log(&ctx, LOG_ERR, "Invalid directive type '%d'.", directive->dtype); return NULL; } - directive_value[ctx.dv] = c; - ctx.dv++; + memset(directive_value, 0, strlen(directive_value)); + free(stripped_directive_value); + cho_directive_free(directive); + directive = NULL; + ctx.state = STATE_LYRICS; break; } - case STATE_CHORD: { - if (c == ']') { - chord[ctx.ch] = 0; - ctx.ch = 0; - ctx.prev_ttype = ctx.current_ttype; - ctx.current_ttype = TT_CHORD; - if (ctx.is_chord_already_initialized) { - ctx.text_above_pos = cho_line_compute_text_above_position(ctx.songs[ctx.so]->sections[ctx.se]->lines[ctx.li], ctx.lii, ctx.te); - (*lines)[ctx.li]->text_above[ctx.lia]->position = ctx.text_above_pos; - tmp_chord = cho_chord_parse(&ctx, chord); - cho_chord_complete((*lines)[ctx.li]->text_above[ctx.lia]->u.chord, tmp_chord); - if (!(*lines)[ctx.li]->text_above[ctx.lia]->u.chord->is_canonical) { - cho_log(&ctx, LOG_INFO, "Didn't recognize the chord '%s'.", (*lines)[ctx.li]->text_above[ctx.lia]->u.chord->name); - } - cho_chord_free(tmp_chord); - ctx.is_chord_already_initialized = false; - } else { - (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[ctx.li]->text_above[ctx.lia] = emalloc(sizeof(struct ChoLineItemAbove)); - (*lines)[ctx.li]->text_above[ctx.lia]->is_chord = true; - ctx.text_above_pos = cho_line_compute_text_above_position((*lines)[ctx.li], ctx.lii, ctx.te); - (*lines)[ctx.li]->text_above[ctx.lia]->position = ctx.text_above_pos; - (*lines)[ctx.li]->text_above[ctx.lia]->u.chord = cho_chord_parse(&ctx, chord); - if (!(*lines)[ctx.li]->text_above[ctx.lia]->u.chord->is_canonical) { - cho_log(&ctx, LOG_INFO, "Didn't recognize the chord '%s'.", (*lines)[ctx.li]->text_above[ctx.lia]->u.chord->name); - } - } - ctx.songs[ctx.so]->present_text_types[TT_CHORD] = true; - memset(chord, 0, strlen(chord)); - ctx.lia++; - ctx.current_ttype = ctx.prev_ttype; - ctx.state = STATE_LYRICS; + if (c == '<') { + ctx.state_before_tag = STATE_DIRECTIVE_VALUE; + ctx.state = STATE_MARKUP_TAG; + break; + } + if (c == '{') { + cho_log(&ctx, LOG_ERR, "Can't start a new directive if the previous one is not yet closed."); + return NULL; + } + if (c == '\n') { + if (prev_c == '\\') { + ctx.state_before_backslash = STATE_DIRECTIVE_VALUE; + ctx.state = STATE_BACKSLASH; + ctx.dv--; break; } - if (prev_c == '[' && c == '*') { - ctx.prev_ttype = ctx.current_ttype; - ctx.current_ttype = TT_ANNOT; + cho_log(&ctx, LOG_ERR, "Newline character inside a directive value is invalid."); + return NULL; + } + if (ctx.dv > 4094) { + cho_log(&ctx, LOG_ERR, "Directive value can't be greater than 4095 bytes."); + return NULL; + } + directive_value[ctx.dv] = c; + ctx.dv++; + break; + } + case STATE_CHORD: { + if (c == ']') { + chord[ctx.ch] = 0; + ctx.ch = 0; + ctx.prev_ttype = ctx.current_ttype; + ctx.current_ttype = TT_CHORD; + if (ctx.is_chord_already_initialized) { + ctx.text_above_pos = cho_line_compute_text_above_position(ctx.songs[ctx.so]->sections[ctx.se]->lines[ctx.li], ctx.lii, ctx.te); + (*lines)[ctx.li]->text_above[ctx.lia]->position = ctx.text_above_pos; + tmp_chord = cho_chord_parse(&ctx, chord); + cho_chord_complete((*lines)[ctx.li]->text_above[ctx.lia]->u.chord, tmp_chord); + if (!(*lines)[ctx.li]->text_above[ctx.lia]->u.chord->is_canonical) { + cho_log(&ctx, LOG_INFO, "Didn't recognize the chord '%s'.", (*lines)[ctx.li]->text_above[ctx.lia]->u.chord->name); + } + cho_chord_free(tmp_chord); + ctx.is_chord_already_initialized = false; + } else { (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); (*lines)[ctx.li]->text_above[ctx.lia] = emalloc(sizeof(struct ChoLineItemAbove)); - (*lines)[ctx.li]->text_above[ctx.lia]->is_chord = false; + (*lines)[ctx.li]->text_above[ctx.lia]->is_chord = true; ctx.text_above_pos = cho_line_compute_text_above_position((*lines)[ctx.li], ctx.lii, ctx.te); (*lines)[ctx.li]->text_above[ctx.lia]->position = ctx.text_above_pos; - (*lines)[ctx.li]->text_above[ctx.lia]->u.annot = cho_text_new(&ctx); - ctx.state = STATE_ANNOTATION; - break; - } - if (c == '\n') { - if (prev_c == '\\') { - ctx.state_before_backslash = STATE_CHORD; - ctx.state = STATE_BACKSLASH; - ctx.ch--; - break; - } - cho_log(&ctx, LOG_ERR, "Newline character inside a chord is invalid."); - return NULL; - } - if (c == '[') { - cho_log(&ctx, LOG_ERR, "Can't start a new chord/annotation if the previous one is not yet closed."); - return NULL; - } - if (c == '<') { - if (prev_c == '[') { - (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[ctx.li]->text_above[ctx.lia] = emalloc(sizeof(struct ChoLineItemAbove)); - (*lines)[ctx.li]->text_above[ctx.lia]->is_chord = true; - (*lines)[ctx.li]->text_above[ctx.lia]->u.chord = cho_chord_new(&ctx); - ctx.is_chord_already_initialized = true; + (*lines)[ctx.li]->text_above[ctx.lia]->u.chord = cho_chord_parse(&ctx, chord); + if (!(*lines)[ctx.li]->text_above[ctx.lia]->u.chord->is_canonical) { + cho_log(&ctx, LOG_INFO, "Didn't recognize the chord '%s'.", (*lines)[ctx.li]->text_above[ctx.lia]->u.chord->name); } - ctx.state_before_tag = STATE_CHORD; - ctx.state = STATE_MARKUP_TAG; - break; } - chord[ctx.ch] = c; - ctx.ch++; + ctx.songs[ctx.so]->present_text_types[TT_CHORD] = true; + memset(chord, 0, strlen(chord)); + ctx.lia++; + ctx.current_ttype = ctx.prev_ttype; + ctx.state = STATE_LYRICS; break; } - case STATE_ANNOTATION: { - if (c == ']') { - (*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)); - (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text[ctx.ann] = 0; - ctx.songs[ctx.so]->present_text_types[TT_ANNOT] = true; - ctx.ann = 0; - ctx.lia++; - ctx.current_ttype = ctx.prev_ttype; - ctx.state = STATE_LYRICS; - break; - } - if (c == '<') { - ctx.state_before_tag = STATE_ANNOTATION; - ctx.state = STATE_MARKUP_TAG; - break; - } - if (c == '\n') { - if (prev_c == '\\') { - ctx.state_before_backslash = STATE_ANNOTATION; - ctx.state = STATE_BACKSLASH; - ctx.ann--; - break; - } - cho_log(&ctx, LOG_ERR, "Newline character inside an annotation is invalid."); - return NULL; - } - (*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)); - (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text[ctx.ann] = c; - ctx.ann++; + if (prev_c == '[' && c == '*') { + ctx.prev_ttype = ctx.current_ttype; + ctx.current_ttype = TT_ANNOT; + (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); + (*lines)[ctx.li]->text_above[ctx.lia] = emalloc(sizeof(struct ChoLineItemAbove)); + (*lines)[ctx.li]->text_above[ctx.lia]->is_chord = false; + ctx.text_above_pos = cho_line_compute_text_above_position((*lines)[ctx.li], ctx.lii, ctx.te); + (*lines)[ctx.li]->text_above[ctx.lia]->position = ctx.text_above_pos; + (*lines)[ctx.li]->text_above[ctx.lia]->u.annot = cho_text_new(&ctx); + ctx.state = STATE_ANNOTATION; break; } - case STATE_TAB: { - // INFO: similar to STATE_LYRICS but without markup and directives - if (prev_c == '\n' && c == '#') { - ctx.state_before_comment = STATE_TAB; - ctx.state = STATE_COMMENT; - break; - } - if (ctx.is_maybe_end_of_tab_directive) { - if (c == '}') { - directive_name[ctx.dn] = 0; - ctx.dn = 0; - ctx.is_maybe_end_of_tab_directive = false; - if (!strcmp(directive_name, "end_of_tab") || !strcmp(directive_name, "eot")) { - cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); - free((*lines)[ctx.li]->items); - ctx.lii = 0; - free((*lines)[ctx.li]); - (*lines)[ctx.li] = NULL; - ctx.se++; - ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); - ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); - ctx.li = 0; - lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; - *lines = emalloc(sizeof(struct ChoLine *)); - (*lines)[ctx.li] = cho_line_new(); - (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - ctx.current_ttype = TT_TEXT; - ctx.state = STATE_LYRICS; - break; - } else { - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = '{'; - ctx.te++; - char *k; - for (k = (char *)&directive_name; *k; k++, ctx.te++) { - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = *k; - } - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = '}'; - ctx.te++; - } - break; - } - if (c == ' ' || c == ':') { - directive_name[ctx.dn] = 0; - ctx.dn = 0; - ctx.is_maybe_end_of_tab_directive = false; - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = '{'; - ctx.te++; - char *k; - for (k = (char *)&directive_name; *k; k++, ctx.te++) { - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = *k; - } - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = c; - ctx.te++; - break; - } - directive_name[ctx.dn] = c; - ctx.dn++; - break; - } - if (prev_c == '\n' && c == '{') { - ctx.is_maybe_end_of_tab_directive = true; + if (c == '\n') { + if (prev_c == '\\') { + ctx.state_before_backslash = STATE_CHORD; + ctx.state = STATE_BACKSLASH; + ctx.ch--; break; } - if (c == '\n') { - if (prev_c == '\\') { - ctx.state_before_backslash = STATE_TAB; - ctx.state = STATE_BACKSLASH; - // INFO: This will later overwrite the backslash - ctx.te--; - break; - } - if (ctx.ta > -1 && !ctx.tags[ctx.ta]->is_closed && strcmp(ctx.tags[ctx.ta]->name, "img")) { - cho_log(&ctx, LOG_ERR, "Tag has to be closed on same line."); - return NULL; - } - if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; - if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { - cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); - if (ctx.lii == 0) { - if ( - !(*lines)[ctx.li]->text_above && - (*lines)[ctx.li]->btype == BT_LINE - ) { - free((*lines)[ctx.li]->items); - free((*lines)[ctx.li]); - *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); - (*lines)[ctx.li] = cho_line_new(); - (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - break; - } - } - } else { - ctx.lii++; - } - } else { - ctx.lii++; - } - (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = NULL; - ctx.lii = 0; - ctx.te = 0; + cho_log(&ctx, LOG_ERR, "Newline character inside a chord is invalid."); + return NULL; + } + if (c == '[') { + cho_log(&ctx, LOG_ERR, "Can't start a new chord/annotation if the previous one is not yet closed."); + return NULL; + } + if (c == '<') { + if (prev_c == '[') { (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[ctx.li]->text_above[ctx.lia] = NULL; - ctx.lia = 0; - ctx.li++; - *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); - (*lines)[ctx.li] = cho_line_new(); - (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - break; + (*lines)[ctx.li]->text_above[ctx.lia] = emalloc(sizeof(struct ChoLineItemAbove)); + (*lines)[ctx.li]->text_above[ctx.lia]->is_chord = true; + (*lines)[ctx.li]->text_above[ctx.lia]->u.chord = cho_chord_new(&ctx); + ctx.is_chord_already_initialized = true; } - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = c; - ctx.te++; + ctx.state_before_tag = STATE_CHORD; + ctx.state = STATE_MARKUP_TAG; + break; + } + chord[ctx.ch] = c; + ctx.ch++; + break; + } + case STATE_ANNOTATION: { + if (c == ']') { + (*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)); + (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text[ctx.ann] = 0; + ctx.songs[ctx.so]->present_text_types[TT_ANNOT] = true; + ctx.ann = 0; + ctx.lia++; + ctx.current_ttype = ctx.prev_ttype; + ctx.state = STATE_LYRICS; + break; + } + if (c == '<') { + ctx.state_before_tag = STATE_ANNOTATION; + ctx.state = STATE_MARKUP_TAG; break; } - case STATE_MARKUP_TAG: { - if (c == '/') { - ctx.state = STATE_MARKUP_TAG_END; + if (c == '\n') { + if (prev_c == '\\') { + ctx.state_before_backslash = STATE_ANNOTATION; + ctx.state = STATE_BACKSLASH; + ctx.ann--; break; } - ctx.ta++; - ctx.tags = erealloc(ctx.tags, (ctx.ta+1) * sizeof(struct Tag *)); - ctx.tags[ctx.ta] = cho_tag_new(); - ctx.state = STATE_MARKUP_TAG_START; - if (fseek(fp, -1, SEEK_CUR)) { - LOG_DEBUG("fseek failed."); - return NULL; - } + cho_log(&ctx, LOG_ERR, "Newline character inside an annotation is invalid."); + return NULL; + } + (*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)); + (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text[ctx.ann] = c; + ctx.ann++; + break; + } + case STATE_TAB: { + // INFO: similar to STATE_LYRICS but without markup and directives + if (prev_c == '\n' && c == '#') { + ctx.state_before_comment = STATE_TAB; + ctx.state = STATE_COMMENT; break; } - case STATE_MARKUP_TAG_START: { - if (c == '>') { - tag_start[ctx.t] = 0; - ctx.t = 0; - if (!strcmp(tag_start, "img")) { - cho_log(&ctx, LOG_ERR, "'img' tag has to have at least the 'src' attribute."); - return NULL; - } - ctx.tags[ctx.ta]->name = strdup(tag_start); - tag_style = cho_style_parse(&ctx, tag_start, NULL, cho_tag_style_inherit(ctx.tags, ctx.ta-1), &ctx.tags[ctx.ta]->style_presence); - if (!tag_style) { - LOG_DEBUG("cho_style_parse failed."); - return NULL; - } - ctx.tags[ctx.ta]->style = tag_style; - switch (ctx.state_before_tag) { - case STATE_LYRICS: - cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); - (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_copy(tag_style); - break; - case STATE_CHORD: - cho_style_free((*lines)[ctx.li]->text_above[ctx.lia]->u.chord->style); - (*lines)[ctx.li]->text_above[ctx.lia]->u.chord->style = cho_style_copy(tag_style); - break; - case STATE_ANNOTATION: - if (ctx.ann > 0) { - (*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)); - (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text[ctx.ann] = 0; - ctx.ann = 0; - ctx.lia++; - (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); - (*lines)[ctx.li]->text_above[ctx.lia] = emalloc(sizeof(struct ChoLineItemAbove)); - (*lines)[ctx.li]->text_above[ctx.lia]->is_chord = false; - (*lines)[ctx.li]->text_above[ctx.lia]->position = ctx.text_above_pos; - (*lines)[ctx.li]->text_above[ctx.lia]->u.annot = cho_text_new(&ctx); - } - cho_style_free((*lines)[ctx.li]->text_above[ctx.lia]->u.annot->style); - (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->style = cho_style_copy(tag_style); - break; - case STATE_DIRECTIVE_VALUE: - ctx.directive_has_tag = true; + if (ctx.is_maybe_end_of_tab_directive) { + if (c == '}') { + directive_name[ctx.dn] = 0; + ctx.dn = 0; + ctx.is_maybe_end_of_tab_directive = false; + if (!strcmp(directive_name, "end_of_tab") || !strcmp(directive_name, "eot")) { + cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); + free((*lines)[ctx.li]->items); + ctx.lii = 0; + free((*lines)[ctx.li]); + (*lines)[ctx.li] = NULL; + ctx.se++; + ctx.songs[ctx.so]->sections = erealloc(ctx.songs[ctx.so]->sections, (ctx.se+1) * sizeof(struct ChoSection *)); + ctx.songs[ctx.so]->sections[ctx.se] = cho_section_new(); + ctx.li = 0; + lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines; + *lines = emalloc(sizeof(struct ChoLine *)); + (*lines)[ctx.li] = cho_line_new(); + (*lines)[ctx.li]->items = emalloc(sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); + ctx.current_ttype = TT_TEXT; + ctx.state = STATE_LYRICS; break; - default: - cho_log(&ctx, LOG_ERR, "Invalid state_before_tag '%s'.", state_enums[ctx.state_before_tag]); - return NULL; + } else { + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = '{'; + ctx.te++; + char *k; + for (k = (char *)&directive_name; *k; k++, ctx.te++) { + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = *k; + } + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = '}'; + ctx.te++; } - memset(tag_start, 0, strlen(tag_start)); - ctx.state = ctx.state_before_tag; break; } - if (is_whitespace(c)) { - tag_start[ctx.t] = 0; - ctx.t = 0; - ctx.tags[ctx.ta]->name = strdup(tag_start); - ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); - ctx.tags[ctx.ta]->attrs[ctx.at] = cho_tag_attr_new(); - ctx.state = STATE_MARKUP_ATTR_NAME; + if (c == ' ' || c == ':') { + directive_name[ctx.dn] = 0; + ctx.dn = 0; + ctx.is_maybe_end_of_tab_directive = false; + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = '{'; + ctx.te++; + char *k; + for (k = (char *)&directive_name; *k; k++, ctx.te++) { + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = *k; + } + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = c; + ctx.te++; break; } - if (c == '\n') { - if (prev_c == '\\') { - ctx.state_before_backslash = STATE_MARKUP_TAG_START; - ctx.state = STATE_BACKSLASH; - ctx.t--; - break; - } - cho_log(&ctx, LOG_ERR, "Newline character inside a tag name is invalid."); - return NULL; + directive_name[ctx.dn] = c; + ctx.dn++; + break; + } + if (prev_c == '\n' && c == '{') { + ctx.is_maybe_end_of_tab_directive = true; + break; + } + if (c == '\n') { + if (prev_c == '\\') { + ctx.state_before_backslash = STATE_TAB; + ctx.state = STATE_BACKSLASH; + // INFO: This will later overwrite the backslash + ctx.te--; + break; } - if (ctx.t == 5) { - cho_log(&ctx, LOG_ERR, "Start tag name is too long."); + if (ctx.ta > -1 && !ctx.tags[ctx.ta]->is_closed && strcmp(ctx.tags[ctx.ta]->name, "img")) { + cho_log(&ctx, LOG_ERR, "Tag has to be closed on same line."); return NULL; } - tag_start[ctx.t] = c; - ctx.t++; + if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = 0; + if (strlen((*lines)[ctx.li]->items[ctx.lii]->u.text->text) == 0) { + cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); + if (ctx.lii == 0) { + if ( + !(*lines)[ctx.li]->text_above && + (*lines)[ctx.li]->btype == BT_LINE + ) { + free((*lines)[ctx.li]->items); + free((*lines)[ctx.li]); + *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); + (*lines)[ctx.li] = cho_line_new(); + (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); + break; + } + } + } else { + ctx.lii++; + } + } else { + ctx.lii++; + } + (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = NULL; + ctx.lii = 0; + ctx.te = 0; + (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); + (*lines)[ctx.li]->text_above[ctx.lia] = NULL; + ctx.lia = 0; + ctx.li++; + *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); + (*lines)[ctx.li] = cho_line_new(); + (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); break; } - case STATE_MARKUP_TAG_END: { - if (c == '>') { - tag_end[ctx.t] = 0; - ctx.t = 0; - if (!cho_tag_close_last_unclosed(&ctx, tag_end, ctx.tags, ctx.ta)) { - LOG_DEBUG("cho_tag_close_last_unclosed failed."); - return NULL; - } - memset(tag_end, 0, strlen(tag_end)); - ctx.state = ctx.state_before_tag; - break; + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = c; + ctx.te++; + break; + } + case STATE_MARKUP_TAG: { + if (c == '/') { + ctx.state = STATE_MARKUP_TAG_END; + break; + } + ctx.ta++; + ctx.tags = erealloc(ctx.tags, (ctx.ta+1) * sizeof(struct Tag *)); + ctx.tags[ctx.ta] = cho_tag_new(); + ctx.state = STATE_MARKUP_TAG_START; + str--; + break; + } + case STATE_MARKUP_TAG_START: { + if (c == '>') { + tag_start[ctx.t] = 0; + ctx.t = 0; + if (!strcmp(tag_start, "img")) { + cho_log(&ctx, LOG_ERR, "'img' tag has to have at least the 'src' attribute."); + return NULL; } - if (c == '\n') { - if (prev_c == '\\') { - ctx.state_before_backslash = STATE_MARKUP_TAG_END; - ctx.state = STATE_BACKSLASH; - ctx.t--; - break; - } - cho_log(&ctx, LOG_ERR, "Newline character inside a tag name is invalid."); + ctx.tags[ctx.ta]->name = strdup(tag_start); + tag_style = cho_style_parse(&ctx, tag_start, NULL, cho_tag_style_inherit(ctx.tags, ctx.ta-1), &ctx.tags[ctx.ta]->style_presence); + if (!tag_style) { + LOG_DEBUG("cho_style_parse failed."); return NULL; } - if (ctx.t == 5) { - cho_log(&ctx, LOG_ERR, "End tag name is too long."); + ctx.tags[ctx.ta]->style = tag_style; + switch (ctx.state_before_tag) { + case STATE_LYRICS: + cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); + (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_copy(tag_style); + break; + case STATE_CHORD: + cho_style_free((*lines)[ctx.li]->text_above[ctx.lia]->u.chord->style); + (*lines)[ctx.li]->text_above[ctx.lia]->u.chord->style = cho_style_copy(tag_style); + break; + case STATE_ANNOTATION: + if (ctx.ann > 0) { + (*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)); + (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->text[ctx.ann] = 0; + ctx.ann = 0; + ctx.lia++; + (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItemAbove *)); + (*lines)[ctx.li]->text_above[ctx.lia] = emalloc(sizeof(struct ChoLineItemAbove)); + (*lines)[ctx.li]->text_above[ctx.lia]->is_chord = false; + (*lines)[ctx.li]->text_above[ctx.lia]->position = ctx.text_above_pos; + (*lines)[ctx.li]->text_above[ctx.lia]->u.annot = cho_text_new(&ctx); + } + cho_style_free((*lines)[ctx.li]->text_above[ctx.lia]->u.annot->style); + (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->style = cho_style_copy(tag_style); + break; + case STATE_DIRECTIVE_VALUE: + ctx.directive_has_tag = true; + break; + default: + cho_log(&ctx, LOG_ERR, "Invalid state_before_tag '%s'.", state_enums[ctx.state_before_tag]); return NULL; } - tag_end[ctx.t] = c; - ctx.t++; + memset(tag_start, 0, strlen(tag_start)); + ctx.state = ctx.state_before_tag; break; } - case STATE_MARKUP_ATTR_NAME: { - if (c == '=') { - ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); - ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = 0; - ctx.atn = 0; - ctx.state = STATE_MARKUP_ATTR_VALUE; + if (is_whitespace(c)) { + tag_start[ctx.t] = 0; + ctx.t = 0; + ctx.tags[ctx.ta]->name = strdup(tag_start); + ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); + ctx.tags[ctx.ta]->attrs[ctx.at] = cho_tag_attr_new(); + ctx.state = STATE_MARKUP_ATTR_NAME; + break; + } + if (c == '\n') { + if (prev_c == '\\') { + ctx.state_before_backslash = STATE_MARKUP_TAG_START; + ctx.state = STATE_BACKSLASH; + ctx.t--; break; } - if (is_whitespace(c)) { - if (ctx.at == 0) { - if (!ctx.tags[ctx.ta]->attrs[ctx.at]->name) { - break; - } else { - ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); - ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = 0; - cho_log(&ctx, LOG_ERR, "Attribute with name '%s' of tag '%s' has no value.", ctx.tags[ctx.ta]->attrs[ctx.at]->name, tag_start); - return NULL; - } - } - if (ctx.tags[ctx.ta]->attrs[ctx.at-1]->name && ctx.tags[ctx.ta]->attrs[ctx.at-1]->value) { - break; - } - if (!ctx.tags[ctx.ta]->attrs[ctx.at-1]->name && !ctx.tags[ctx.ta]->attrs[ctx.at-1]->value) { - break; - } - ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); - ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = 0; - cho_log(&ctx, LOG_ERR, "Attribute with name '%s' of tag '%s' has no value.", ctx.tags[ctx.ta]->attrs[ctx.at]->name, tag_start); + cho_log(&ctx, LOG_ERR, "Newline character inside a tag name is invalid."); + return NULL; + } + if (ctx.t == 5) { + cho_log(&ctx, LOG_ERR, "Start tag name is too long."); + return NULL; + } + tag_start[ctx.t] = c; + ctx.t++; + break; + } + case STATE_MARKUP_TAG_END: { + if (c == '>') { + tag_end[ctx.t] = 0; + ctx.t = 0; + if (!cho_tag_close_last_unclosed(&ctx, tag_end, ctx.tags, ctx.ta)) { + LOG_DEBUG("cho_tag_close_last_unclosed failed."); return NULL; } - if (c == '>') { - if (ctx.tags[ctx.ta]->attrs[ctx.at-1]->value) { - cho_tag_attr_free(ctx.tags[ctx.ta]->attrs[ctx.at]); - ctx.tags[ctx.ta]->attrs[ctx.at] = NULL; - ctx.atn = 0; - if (!strcmp(ctx.tags[ctx.ta]->name, "img")) { - cho_text_free((*lines)[ctx.li]->items[ctx.lii]->u.text); - (*lines)[ctx.li]->items[ctx.lii]->is_text = false; - image = cho_image_tag_parse(&ctx, ctx.tags[ctx.ta]->attrs); - if (!image) { - LOG_DEBUG("cho_image_tag_parse failed."); - return NULL; - } - (*lines)[ctx.li]->items[ctx.lii]->u.image = image; - ctx.lii++; - (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); - (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); - } else { - 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); - if (!tag_style) { - LOG_DEBUG("cho_style_parse failed."); - return NULL; - } - ctx.tags[ctx.ta]->style = tag_style; - switch (ctx.state_before_tag) { - case STATE_LYRICS: - cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); - (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_copy(tag_style); - break; - case STATE_CHORD: - cho_style_free((*lines)[ctx.li]->text_above[ctx.lia]->u.chord->style); - (*lines)[ctx.li]->text_above[ctx.lia]->u.chord->style = cho_style_copy(tag_style); - break; - case STATE_ANNOTATION: - cho_style_free((*lines)[ctx.li]->text_above[ctx.lia]->u.annot->style); - (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->style = cho_style_copy(tag_style); - break; - case STATE_DIRECTIVE_VALUE: - ctx.directive_has_tag = true; - break; - default: - cho_log(&ctx, LOG_ERR, "Invalid state_before_tag '%s'.", state_enums[ctx.state_before_tag]); - return NULL; - } - } - ctx.at = 0; - memset(tag_start, 0, strlen(tag_start)); - ctx.state = ctx.state_before_tag; + memset(tag_end, 0, strlen(tag_end)); + ctx.state = ctx.state_before_tag; + break; + } + if (c == '\n') { + if (prev_c == '\\') { + ctx.state_before_backslash = STATE_MARKUP_TAG_END; + ctx.state = STATE_BACKSLASH; + ctx.t--; + break; + } + cho_log(&ctx, LOG_ERR, "Newline character inside a tag name is invalid."); + return NULL; + } + if (ctx.t == 5) { + cho_log(&ctx, LOG_ERR, "End tag name is too long."); + return NULL; + } + tag_end[ctx.t] = c; + ctx.t++; + break; + } + case STATE_MARKUP_ATTR_NAME: { + if (c == '=') { + ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); + ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = 0; + ctx.atn = 0; + ctx.state = STATE_MARKUP_ATTR_VALUE; + break; + } + if (is_whitespace(c)) { + if (ctx.at == 0) { + if (!ctx.tags[ctx.ta]->attrs[ctx.at]->name) { break; } else { ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = 0; - ctx.atn = 0; cho_log(&ctx, LOG_ERR, "Attribute with name '%s' of tag '%s' has no value.", ctx.tags[ctx.ta]->attrs[ctx.at]->name, tag_start); return NULL; } } - if (c == '\n') { - if (prev_c == '\\') { - ctx.state_before_backslash = STATE_MARKUP_ATTR_NAME; - ctx.state = STATE_BACKSLASH; - ctx.atn--; - break; - } - cho_log(&ctx, LOG_ERR, "Newline character inside an tag attribute name is invalid."); - return NULL; - } - ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); - ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = c; - ctx.atn++; - break; - } - case STATE_MARKUP_ATTR_VALUE: { - if (c == '\n') { - if (prev_c == '\\') { - ctx.state_before_backslash = STATE_MARKUP_ATTR_VALUE; - ctx.state = STATE_BACKSLASH; - ctx.atv--; - break; - } - cho_log(&ctx, LOG_ERR, "Newline character inside an attribute value is invalid."); - return NULL; + if (ctx.tags[ctx.ta]->attrs[ctx.at-1]->name && ctx.tags[ctx.ta]->attrs[ctx.at-1]->value) { + break; } - if (avs == -1) { - if (is_whitespace(c)) { - cho_log(&ctx, LOG_ERR, "Whitespace character after equals sign is invalid."); - return NULL; - } - if (c == '>') { - cho_log(&ctx, LOG_ERR, "Attribute with name '%s' of tag '%s' has no value.", ctx.tags[ctx.ta]->attrs[ctx.at]->name, tag_start); - return NULL; - } - if (c == '\'') { - avs = AVS_APOSTROPHE; - } else if (c == '"') { - avs = AVS_QUOTATION_MARK; - } else { - avs = AVS_UNQUOTED; - ctx.tags[ctx.ta]->attrs[ctx.at]->value = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->value, (ctx.atv+1) * sizeof(char)); - ctx.tags[ctx.ta]->attrs[ctx.at]->value[ctx.atv] = c; - ctx.atv++; - } + if (!ctx.tags[ctx.ta]->attrs[ctx.at-1]->name && !ctx.tags[ctx.ta]->attrs[ctx.at-1]->value) { break; } - if (avs == AVS_UNQUOTED && c == '>') { - ctx.tags[ctx.ta]->attrs[ctx.at]->value = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->value, (ctx.atv+1) * sizeof(char)); - ctx.tags[ctx.ta]->attrs[ctx.at]->value[ctx.atv] = 0; - ctx.atv = 0; - ctx.at++; - ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); + ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); + ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = 0; + cho_log(&ctx, LOG_ERR, "Attribute with name '%s' of tag '%s' has no value.", ctx.tags[ctx.ta]->attrs[ctx.at]->name, tag_start); + return NULL; + } + if (c == '>') { + if (ctx.tags[ctx.ta]->attrs[ctx.at-1]->value) { + cho_tag_attr_free(ctx.tags[ctx.ta]->attrs[ctx.at]); ctx.tags[ctx.ta]->attrs[ctx.at] = NULL; + ctx.atn = 0; if (!strcmp(ctx.tags[ctx.ta]->name, "img")) { cho_text_free((*lines)[ctx.li]->items[ctx.lii]->u.text); (*lines)[ctx.li]->items[ctx.lii]->is_text = false; @@ -5460,92 +5353,191 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) } } ctx.at = 0; - avs = -1; memset(tag_start, 0, strlen(tag_start)); ctx.state = ctx.state_before_tag; break; + } else { + ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); + ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = 0; + ctx.atn = 0; + cho_log(&ctx, LOG_ERR, "Attribute with name '%s' of tag '%s' has no value.", ctx.tags[ctx.ta]->attrs[ctx.at]->name, tag_start); + return NULL; } - if ( - (avs == AVS_APOSTROPHE && c == '\'') || - (avs == AVS_QUOTATION_MARK && c == '"') || - (avs == AVS_UNQUOTED && (c == ' ' || c == '\t')) - ) { - ctx.tags[ctx.ta]->attrs[ctx.at]->value = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->value, (ctx.atv+1) * sizeof(char)); - ctx.tags[ctx.ta]->attrs[ctx.at]->value[ctx.atv] = 0; - ctx.atv = 0; - ctx.at++; - ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); - ctx.tags[ctx.ta]->attrs[ctx.at] = cho_tag_attr_new(); - avs = -1; - ctx.state = STATE_MARKUP_ATTR_NAME; - break; - } - ctx.tags[ctx.ta]->attrs[ctx.at]->value = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->value, (ctx.atv+1) * sizeof(char)); - ctx.tags[ctx.ta]->attrs[ctx.at]->value[ctx.atv] = c; - ctx.atv++; - break; } - case STATE_COMMENT: { - if (c == '\n') { - ctx.state = ctx.state_before_comment; + if (c == '\n') { + if (prev_c == '\\') { + ctx.state_before_backslash = STATE_MARKUP_ATTR_NAME; + ctx.state = STATE_BACKSLASH; + ctx.atn--; break; } - break; + cho_log(&ctx, LOG_ERR, "Newline character inside an tag attribute name is invalid."); + return NULL; } - case STATE_MAYBE_METADATA_SUBSTITUTION: { - if (c == '{') { - ctx.state = STATE_METADATA_SUBSTITUTION; + ctx.tags[ctx.ta]->attrs[ctx.at]->name = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->name, (ctx.atn+1) * sizeof(char)); + ctx.tags[ctx.ta]->attrs[ctx.at]->name[ctx.atn] = c; + ctx.atn++; + break; + } + case STATE_MARKUP_ATTR_VALUE: { + if (c == '\n') { + if (prev_c == '\\') { + ctx.state_before_backslash = STATE_MARKUP_ATTR_VALUE; + ctx.state = STATE_BACKSLASH; + ctx.atv--; break; } - switch (ctx.state_before_metadata_substitution) { - case STATE_LYRICS: - (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); - (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = '%'; - ctx.te++; - break; - default: + cho_log(&ctx, LOG_ERR, "Newline character inside an attribute value is invalid."); + return NULL; + } + if (avs == -1) { + if (is_whitespace(c)) { + cho_log(&ctx, LOG_ERR, "Whitespace character after equals sign is invalid."); + return NULL; } - ctx.state = ctx.state_before_metadata_substitution; - if (fseek(fp, -1, SEEK_CUR)) { - LOG_DEBUG("fseek failed."); - perror("fseek"); + if (c == '>') { + cho_log(&ctx, LOG_ERR, "Attribute with name '%s' of tag '%s' has no value.", ctx.tags[ctx.ta]->attrs[ctx.at]->name, tag_start); return NULL; } + if (c == '\'') { + avs = AVS_APOSTROPHE; + } else if (c == '"') { + avs = AVS_QUOTATION_MARK; + } else { + avs = AVS_UNQUOTED; + ctx.tags[ctx.ta]->attrs[ctx.at]->value = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->value, (ctx.atv+1) * sizeof(char)); + ctx.tags[ctx.ta]->attrs[ctx.at]->value[ctx.atv] = c; + ctx.atv++; + } break; } - case STATE_METADATA_SUBSTITUTION: { - if (c == '}') { - if (ctx.nested_level == 0) { - metadata_substitution[ctx.ms] = 0; - ctx.ms = 0; - printf("substitution: '%s'\n", metadata_substitution); - /* char *substituted = cho_metadata_substitution_parse( - metadata_substitution, - songs[so]->metadata, - state_before_metadata_substitution - ); */ - // Append 'substituted' to whatever text - ctx.state = ctx.state_before_metadata_substitution; + if (avs == AVS_UNQUOTED && c == '>') { + ctx.tags[ctx.ta]->attrs[ctx.at]->value = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->value, (ctx.atv+1) * sizeof(char)); + ctx.tags[ctx.ta]->attrs[ctx.at]->value[ctx.atv] = 0; + ctx.atv = 0; + ctx.at++; + ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); + ctx.tags[ctx.ta]->attrs[ctx.at] = NULL; + if (!strcmp(ctx.tags[ctx.ta]->name, "img")) { + cho_text_free((*lines)[ctx.li]->items[ctx.lii]->u.text); + (*lines)[ctx.li]->items[ctx.lii]->is_text = false; + image = cho_image_tag_parse(&ctx, ctx.tags[ctx.ta]->attrs); + if (!image) { + LOG_DEBUG("cho_image_tag_parse failed."); + return NULL; + } + (*lines)[ctx.li]->items[ctx.lii]->u.image = image; + ctx.lii++; + (*lines)[ctx.li]->items = erealloc((*lines)[ctx.li]->items, (ctx.lii+1) * sizeof(struct ChoLineItem *)); + (*lines)[ctx.li]->items[ctx.lii] = cho_line_item_new(&ctx); + } else { + 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); + if (!tag_style) { + LOG_DEBUG("cho_style_parse failed."); + return NULL; + } + ctx.tags[ctx.ta]->style = tag_style; + switch (ctx.state_before_tag) { + case STATE_LYRICS: + cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style); + (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_copy(tag_style); + break; + case STATE_CHORD: + cho_style_free((*lines)[ctx.li]->text_above[ctx.lia]->u.chord->style); + (*lines)[ctx.li]->text_above[ctx.lia]->u.chord->style = cho_style_copy(tag_style); + break; + case STATE_ANNOTATION: + cho_style_free((*lines)[ctx.li]->text_above[ctx.lia]->u.annot->style); + (*lines)[ctx.li]->text_above[ctx.lia]->u.annot->style = cho_style_copy(tag_style); + break; + case STATE_DIRECTIVE_VALUE: + ctx.directive_has_tag = true; break; + default: + cho_log(&ctx, LOG_ERR, "Invalid state_before_tag '%s'.", state_enums[ctx.state_before_tag]); + return NULL; } - ctx.nested_level--; - } - if (prev_c == '%' && c == '{') { - ctx.nested_level++; } - if (ctx.ms > 4094) { - cho_log(&ctx, LOG_ERR, "Metadata substitution can't be greater than 4095 bytes."); - return NULL; - } - metadata_substitution[ctx.ms] = c; - ctx.ms++; + ctx.at = 0; + avs = -1; + memset(tag_start, 0, strlen(tag_start)); + ctx.state = ctx.state_before_tag; + break; + } + if ( + (avs == AVS_APOSTROPHE && c == '\'') || + (avs == AVS_QUOTATION_MARK && c == '"') || + (avs == AVS_UNQUOTED && (c == ' ' || c == '\t')) + ) { + ctx.tags[ctx.ta]->attrs[ctx.at]->value = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->value, (ctx.atv+1) * sizeof(char)); + ctx.tags[ctx.ta]->attrs[ctx.at]->value[ctx.atv] = 0; + ctx.atv = 0; + ctx.at++; + ctx.tags[ctx.ta]->attrs = erealloc(ctx.tags[ctx.ta]->attrs, (ctx.at+1) * sizeof(struct Attr *)); + ctx.tags[ctx.ta]->attrs[ctx.at] = cho_tag_attr_new(); + avs = -1; + ctx.state = STATE_MARKUP_ATTR_NAME; break; } + ctx.tags[ctx.ta]->attrs[ctx.at]->value = erealloc(ctx.tags[ctx.ta]->attrs[ctx.at]->value, (ctx.atv+1) * sizeof(char)); + ctx.tags[ctx.ta]->attrs[ctx.at]->value[ctx.atv] = c; + ctx.atv++; + break; + } + case STATE_COMMENT: { + if (c == '\n') { + ctx.state = ctx.state_before_comment; + break; } - prev_c = c; - } else { break; } + case STATE_MAYBE_METADATA_SUBSTITUTION: { + if (c == '{') { + ctx.state = STATE_METADATA_SUBSTITUTION; + break; + } + switch (ctx.state_before_metadata_substitution) { + case STATE_LYRICS: + (*lines)[ctx.li]->items[ctx.lii]->u.text->text = erealloc((*lines)[ctx.li]->items[ctx.lii]->u.text->text, (ctx.te+1) * sizeof(char)); + (*lines)[ctx.li]->items[ctx.lii]->u.text->text[ctx.te] = '%'; + ctx.te++; + break; + default: + } + ctx.state = ctx.state_before_metadata_substitution; + str--; + break; + } + case STATE_METADATA_SUBSTITUTION: { + if (c == '}') { + if (ctx.nested_level == 0) { + metadata_substitution[ctx.ms] = 0; + ctx.ms = 0; + printf("substitution: '%s'\n", metadata_substitution); + /* char *substituted = cho_metadata_substitution_parse( + metadata_substitution, + songs[so]->metadata, + state_before_metadata_substitution + ); */ + // Append 'substituted' to whatever text + ctx.state = ctx.state_before_metadata_substitution; + break; + } + ctx.nested_level--; + } + if (prev_c == '%' && c == '{') { + ctx.nested_level++; + } + if (ctx.ms > 4094) { + cho_log(&ctx, LOG_ERR, "Metadata substitution can't be greater than 4095 bytes."); + return NULL; + } + metadata_substitution[ctx.ms] = c; + ctx.ms++; + break; + } + } + prev_c = c; } int e = 0; while (e <= ctx.ta) { @@ -5604,6 +5596,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) bool exist_title = false; for (ctx.so = 0; ctx.songs[ctx.so]; ctx.so++) { for (ctx.m = 0; ctx.songs[ctx.so]->metadata[ctx.m]; ctx.m++) { + printf("so: %d, %s = %s\n", ctx.so, ctx.songs[ctx.so]->metadata[ctx.m]->name, ctx.songs[ctx.so]->metadata[ctx.m]->value); if ( !strcmp(ctx.songs[ctx.so]->metadata[ctx.m]->name, "title") && ctx.songs[ctx.so]->metadata[ctx.m]->value && diff --git a/src/chordpro.h b/src/chordpro.h @@ -174,7 +174,7 @@ struct ChoContext { void cho_log_enable_info_logs(void); -struct ChoSong **cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config); +struct ChoSong **cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *config); int cho_song_count(struct ChoSong **songs); int cho_song_compare(const void *a, const void *b); void cho_songs_free(struct ChoSong **song); diff --git a/src/lorid.c b/src/lorid.c @@ -25,6 +25,7 @@ main(int argc, char *argv[]) const char *chordpro_filepath = NULL; char *config_filepath = NULL; char *output = NULL; + char *input; struct ChoSong **so, **all_songs = NULL, **songs = NULL; int s = 0; FILE *fp; @@ -60,23 +61,27 @@ main(int argc, char *argv[]) free(config_filepath); if (argc == optind) { fp = stdin; - all_songs = cho_songs_parse(fp, NULL, config); + input = file_read(fp); + all_songs = cho_songs_parse(input, NULL, config); if (!all_songs) { LOG_DEBUG("cho_songs_parse failed."); return 1; } + free(input); } else if (argc == optind+1) { fp = fopen(argv[argc-1], "r"); if (!fp) { LOG_DEBUG("fopen failed."); return 1; } + input = file_read(fp); chordpro_filepath = argv[argc-1]; - all_songs = cho_songs_parse(fp, chordpro_filepath, config); + all_songs = cho_songs_parse(input, chordpro_filepath, config); if (!all_songs) { LOG_DEBUG("cho_songs_parse failed."); return 1; } + free(input); fclose(fp); } else { int file_count = argc - optind; @@ -87,11 +92,13 @@ main(int argc, char *argv[]) LOG_DEBUG("fopen failed."); return 1; } - songs = cho_songs_parse(fp, argv[i], config); + input = file_read(fp); + songs = cho_songs_parse(input, argv[i], config); if (!songs) { LOG_DEBUG("cho_songs_parse failed."); return 1; } + free(input); fclose(fp); for (so = songs; *so; s++, so++) { all_songs = erealloc(all_songs, (s+1) * sizeof(struct ChoSong *)); @@ -101,8 +108,8 @@ main(int argc, char *argv[]) } all_songs = erealloc(all_songs, (s+1) * sizeof(struct ChoSong *)); all_songs[s] = NULL; - qsort(all_songs, cho_song_count(all_songs), sizeof(struct ChoSong *), cho_song_compare); } + qsort(all_songs, cho_song_count(all_songs), sizeof(struct ChoSong *), cho_song_compare); char *pdf_filename = out_pdf_create(chordpro_filepath, output, all_songs, config); if (!pdf_filename) { LOG_DEBUG("out_pdf_new failed."); diff --git a/src/util.c b/src/util.c @@ -280,6 +280,28 @@ file_type(const char *path) } char * +file_read(FILE *fp) +{ + char *str = NULL; + char buf; + size_t read; + int i = 0; + while (1) { + read = fread(&buf, 1, 1, fp); + if (read == 1) { + str = erealloc(str, (i+1) * sizeof(char)); + str[i] = buf; + i++; + } else { + str = erealloc(str, (i+1) * sizeof(char)); + str[i] = 0; + break; + } + } + return str; +} + +/* char * file_read(const char *filepath) { char *str = NULL; @@ -305,7 +327,7 @@ file_read(const char *filepath) } fclose(fp); return str; -} +} */ char * file_extension_replace_or_add(const char *filepath, const char *extension) diff --git a/src/util.h b/src/util.h @@ -45,7 +45,7 @@ int str_compare(const char *a, const char *b); long str_to_number(const char *str); enum FileType file_type(const char *path); -char *file_read(const char *filepath); +char *file_read(FILE *fp); char *file_extension_replace_or_add(const char *filepath, const char *extension); bool file_extension_equals(const char *filepath, const char *extension);