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:
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);