commit 84d3e643712b914f2c74244dcaf72f876ec2133b
parent 6b9e0a31f4ba113e1d99921344c2eab1d28bd3f4
Author: nibo <nibo@relim.de>
Date: Fri, 1 Aug 2025 18:00:33 +0200
Remove by accident added file src/some.c
Diffstat:
| D | src/some.c | | | 1715 | ------------------------------------------------------------------------------- |
1 file changed, 0 insertions(+), 1715 deletions(-)
diff --git a/src/some.c b/src/some.c
@@ -1,1715 +0,0 @@
-{
- enum AttrValueSyntax avs = ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED;
- enum GridToken token;
- struct Attr **directive_attrs = NULL;
- struct ChoStyle *tag_style;
- struct StyleProperty sprop;
- struct ChoChord *tmp_chord;
- struct ChoSection *chorus;
- struct ChoImage *image;
- struct ChordDiagram *diagram;
- struct ChoDirective *directive = NULL;
- struct ChoMetadata *metadata = NULL;
- struct ChoLine ***lines;
- struct ChoContext ctx;
- bool err;
- char *metadata_value, *shape;
- char *stripped_directive_value = NULL;
- char *label = NULL;
- char directive_name[128];
- char directive_value[4096];
- char chord[CHORD_LEN];
- char tag_start[6];
- char tag_end[6];
- char custom_directive[64];
- char metadata_substitution[4096];
- char grid_token[CHORD_LEN];
- char c = 0;
- char prev_c = '\n';
- int transpose;
-
- if (!cho_context_init(&ctx, config, chordpro_filepath)) {
- LOG_DEBUG("cho_context_init failed.");
- return NULL;
- }
-
- lines = &ctx.songs[ctx.so]->sections[ctx.se]->lines;
- (*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[TEXT_TYPE_TOC] = config->output->toc->show;
- for (; *str; str++) {
- c = *str;
- // printf("state: %s, c: %c\n", state_enums[ctx.state], c);
- 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;
- }
- 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++;
- }
- } 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;
- }
- if (c == '%') {
- ctx.state_before_metadata_substitution = STATE_LYRICS;
- ctx.state = STATE_MAYBE_METADATA_SUBSTITUTION;
- break;
- }
- if (c == '\n') {
- ctx.line_no++;
- 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")) {
- // TODO: This seems to be unreachable
- 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 == BREAK_TYPE_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;
- }
- (*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;
- }
- 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 DIRECTIVE_TYPE_ENVIRONMENT: {
- ctx.current_ttype = directive->ttype;
- switch (directive->position) {
- case POSITION_START: {
- if (directive->stype == SECTION_TYPE_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;
- switch (directive->stype) {
- case SECTION_TYPE_TAB:
- ctx.state = STATE_TAB;
- break;
- case SECTION_TYPE_GRID:
- ctx.state = STATE_GRID;
- break;
- default:
- ctx.state = STATE_LYRICS;
- }
- break;
- }
- case POSITION_END: {
- if (directive->stype == ctx.songs[ctx.so]->sections[ctx.se]->type) {
- if (directive->stype == SECTION_TYPE_CUSTOM) {
- if (strcmp(custom_directive, &directive_name[7]) != 0) {
- break;
- }
- }
- if (directive->stype == SECTION_TYPE_GRID) {
- ctx.grid.bar_line_symbol_count = 0;
- ctx.grid.tokens_per_cell = 0;
- ctx.grid.expected_tokens_per_cell = 0;
- }
- 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.state = STATE_LYRICS;
- } else {
- const char *section_type = section_types[directive->stype];
- if (section_type[0] == 0) {
- cho_log(&ctx, LOG_ERR, "Can't close a section that wasn't opened earlier.");
- } else {
- cho_log(&ctx, LOG_ERR, "Can't close a %s section that wasn't opened earlier.", section_type);
- }
- goto ERR;
- }
- break;
- }
- case POSITION_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 = TEXT_TYPE_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.");
- }
- 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 = TEXT_TYPE_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;
- }
- }
- break;
- }
- case DIRECTIVE_TYPE_METADATA:
- cho_log(&ctx, LOG_WARN, "Ignoring metadata directive '%s' because it has no value.", directive_name);
- break;
- case DIRECTIVE_TYPE_FORMATTING:
- cho_log(&ctx, LOG_WARN, "Formatting directive '%s' has no value.", directive_name);
- break;
- case DIRECTIVE_TYPE_IMAGE:
- cho_log(&ctx, LOG_ERR, "Directive 'image' has no value.");
- goto ERR;
- case DIRECTIVE_TYPE_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()) {
- LOG_DEBUG("cho_style_reset_default failed.");
- goto ERR;
- }
- for (int e = 0; e<ctx.ia; e++) {
- cho_image_free(ctx.image_assets[e]);
- }
- 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.");
- goto ERR;
- }
- 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 DIRECTIVE_TYPE_FONT: {
- sprop.ttype = directive->ttype;
- sprop.type = directive->sprop;
- switch (directive->sprop) {
- case STYLE_PROPERTY_TYPE_FONT:
- sprop.u.font_name = NULL;
- break;
- case STYLE_PROPERTY_TYPE_SIZE:
- sprop.u.font_size = EMPTY_DOUBLE;
- break;
- case STYLE_PROPERTY_TYPE_COLOR:
- sprop.u.foreground_color = NULL;
- break;
- }
- if (!cho_style_change_default(sprop)) {
- LOG_DEBUG("cho_style_change_default failed.");
- goto ERR;
- }
- break;
- }
- case DIRECTIVE_TYPE_CHORD: {
- switch (directive->ctype) {
- case CHORD_DIRECTIVE_TRANSPOSE:
- ctx.transpose--;
- ctx.th--;
- break;
- case CHORD_DIRECTIVE_DEFINE:
- cho_log(&ctx, LOG_WARN, "Ignoring chord directive '%s' because it has no value.", directive_name);
- break;
- }
- break;
- }
- case DIRECTIVE_TYPE_OUTPUT:
- (*lines)[ctx.li]->btype = directive->btype;
- break;
- case DIRECTIVE_TYPE_EXTENSION:
- // INFO: Such a directive should not be logged.
- break;
- case DIRECTIVE_TYPE_CUSTOM:
- cho_log(&ctx, LOG_INFO, "Ignoring custom directive '%s'.", directive_name);
- break;
- }
- 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.");
- goto ERR;
- }
- if (c == '\n') {
- ctx.line_no++;
- if (prev_c == '\\') {
- ctx.state_before_backslash = STATE_DIRECTIVE_NAME;
- ctx.state = STATE_BACKSLASH;
- ctx.dn--;
- break;
- }
- cho_log(&ctx, LOG_ERR, "Can't have a newline in a directive name.");
- goto ERR;
- }
- if (c == ':' || c == ' ') {
- directive_name[ctx.dn] = 0;
- ctx.dn = 0;
- ctx.state = STATE_DIRECTIVE_VALUE;
- break;
- }
- 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 DIRECTIVE_TYPE_ENVIRONMENT: {
- if (strlen(stripped_directive_value) > 0) {
- ctx.songs[ctx.so]->present_text_types[TEXT_TYPE_LABEL] = true;
- }
- ctx.current_ttype = directive->ttype;
- switch (directive->position) {
- case POSITION_START: {
- if (directive->stype == SECTION_TYPE_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;
-
- if (strchr(stripped_directive_value, '=')) {
- directive_attrs = cho_attrs_parse(&ctx, stripped_directive_value, directive_name);
- if (!directive_attrs) {
- LOG_DEBUG("cho_attrs_parse failed.");
- goto ERR;
- }
- label = cho_attrs_get(directive_attrs, "label");
- if (directive->stype == SECTION_TYPE_GRID) {
- shape = cho_attrs_get(directive_attrs, "shape");
- if (shape) {
- if (!cho_grid_shape_parse_and_set(&ctx, shape)) {
- LOG_DEBUG("cho_grid_parse_and_set_shape failed.");
- goto ERR;
- }
- }
- }
- } else {
- if (directive->stype == SECTION_TYPE_GRID) {
- int index = str_index_of(stripped_directive_value, ' ');
- if (index != -1) {
- stripped_directive_value[index] = 0;
- label = &stripped_directive_value[index+1];
- }
- if (!cho_grid_shape_parse_and_set(&ctx, stripped_directive_value)) {
- LOG_DEBUG("cho_grid_parse_and_set_shape failed.");
- goto ERR;
- }
- } else {
- label = stripped_directive_value;
- }
- }
- if (label) {
- ctx.songs[ctx.so]->sections[ctx.se]->label = emalloc(sizeof(struct ChoText));
- ctx.songs[ctx.so]->sections[ctx.se]->label->text = strdup(label);
- ctx.prev_ttype = ctx.current_ttype;
- ctx.current_ttype = TEXT_TYPE_LABEL;
- ctx.songs[ctx.so]->sections[ctx.se]->label->style = cho_style_new_default(&ctx);
- ctx.current_ttype = ctx.prev_ttype;
- label = NULL;
- }
- cho_tag_attrs_free(directive_attrs);
- directive_attrs = NULL;
- 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 POSITION_END: {
- cho_log(&ctx, LOG_ERR, "A directive that closes a section can't have arguments.");
- goto ERR;
- }
- case POSITION_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 = TEXT_TYPE_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 = TEXT_TYPE_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;
- 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 = TEXT_TYPE_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);
- }
- break;
- }
- }
- break;
- }
- case DIRECTIVE_TYPE_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 METADATA_DIRECTIVE_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[TEXT_TYPE_TITLE] = true;
- break;
- case METADATA_DIRECTIVE_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[TEXT_TYPE_SUBTITLE] = true;
- break;
- case METADATA_DIRECTIVE_OTHER: {
- if (!strcmp(directive_name, "meta")) {
- metadata = cho_metadata_split(&ctx, directive_value);
- if (!metadata) {
- LOG_DEBUG("cho_metadata_split failed.");
- goto ERR;
- }
- 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;
- }
- }
- }
- 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 DIRECTIVE_TYPE_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;
- break;
- }
- case DIRECTIVE_TYPE_IMAGE: {
- if (strchr(directive_value, '=')) {
- image = cho_image_directive_parse(&ctx, directive_value);
- if (!image) {
- LOG_DEBUG("cho_image_directive_parse failed.");
- goto ERR;
- }
- } else {
- image = cho_image_new();
- image->src = strdup(stripped_directive_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++;
- } 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;
- }
- case DIRECTIVE_TYPE_PREAMBLE:
- cho_log(&ctx, LOG_ERR, "Preamble directive '%s' can't have a value.", directive_name);
- goto ERR;
- case DIRECTIVE_TYPE_FONT: {
- sprop.ttype = directive->ttype;
- char *dir_value = strdup(stripped_directive_value);
- switch (directive->sprop) {
- case STYLE_PROPERTY_TYPE_FONT:
- sprop.u.font_name = emalloc((strlen(dir_value)+1) * sizeof(char));
- strcpy(sprop.u.font_name, dir_value);
- sprop.type = STYLE_PROPERTY_TYPE_FONT;
- break;
- case STYLE_PROPERTY_TYPE_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);
- goto ERR;
- }
- sprop.type = STYLE_PROPERTY_TYPE_SIZE;
- break;
- case STYLE_PROPERTY_TYPE_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);
- goto ERR;
- }
- sprop.type = STYLE_PROPERTY_TYPE_COLOR;
- break;
- default:
- cho_log(&ctx, LOG_ERR, "Invalid style property type '%d'.", directive->sprop);
- goto ERR;
- }
- if (!cho_style_change_default(sprop)) {
- LOG_DEBUG("cho_style_change_default failed.");
- goto ERR;
- }
- if (sprop.type == STYLE_PROPERTY_TYPE_FONT) {
- free(sprop.u.font_name);
- } else if (sprop.type == STYLE_PROPERTY_TYPE_COLOR) {
- free(sprop.u.foreground_color);
- }
- free(dir_value);
- break;
- }
- case DIRECTIVE_TYPE_CHORD: {
- switch (directive->ctype) {
- case CHORD_DIRECTIVE_TRANSPOSE:
- if (!transposition_parse(directive_value, &transpose)) {
- LOG_DEBUG("transposition_parse failed.");
- cho_log(&ctx, LOG_ERR, "Directive 'transpose' has an invalid value.");
- goto ERR;
- }
- 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 CHORD_DIRECTIVE_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.");
- goto ERR;
- }
- // debug_chord_diagram_print(diagram);
- 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;
- }
- break;
- }
- case DIRECTIVE_TYPE_OUTPUT:
- cho_log(&ctx, LOG_ERR, "Directive '%s' can't have a value.", directive_name);
- goto ERR;
- case DIRECTIVE_TYPE_EXTENSION:
- // INFO: Such a directive should not be logged.
- break;
- case DIRECTIVE_TYPE_CUSTOM:
- cho_log(&ctx, LOG_INFO, "Ignoring custom directive '%s'.", directive_name);
- break;
- }
- switch (directive->stype) {
- case SECTION_TYPE_TAB:
- ctx.state = STATE_TAB;
- break;
- case SECTION_TYPE_GRID:
- ctx.state = STATE_GRID;
- break;
- default:
- ctx.state = STATE_LYRICS;
- }
- memset(directive_value, 0, strlen(directive_value));
- free(stripped_directive_value);
- stripped_directive_value = NULL;
- cho_directive_free(directive);
- directive = NULL;
- break;
- }
- if (c == '%') {
- ctx.state_before_metadata_substitution = STATE_DIRECTIVE_VALUE;
- ctx.state = STATE_MAYBE_METADATA_SUBSTITUTION;
- break;
- }
- 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.");
- goto ERR;
- }
- if (c == '\n') {
- ctx.line_no++;
- if (prev_c == '\\') {
- ctx.state_before_backslash = STATE_DIRECTIVE_VALUE;
- ctx.state = STATE_BACKSLASH;
- ctx.dv--;
- break;
- }
- cho_log(&ctx, LOG_ERR, "Newline character inside a directive value is invalid.");
- goto ERR;
- }
- if (ctx.dv > 4094) {
- cho_log(&ctx, LOG_ERR, "Directive value can't be greater than 4095 bytes.");
- goto ERR;
- }
- 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 = TEXT_TYPE_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[TEXT_TYPE_CHORD] = true;
- memset(chord, 0, strlen(chord));
- ctx.lia++;
- ctx.current_ttype = ctx.prev_ttype;
- ctx.state = STATE_LYRICS;
- break;
- }
- if (prev_c == '[' && c == '*') {
- ctx.prev_ttype = ctx.current_ttype;
- ctx.current_ttype = TEXT_TYPE_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;
- }
- if (c == '\n') {
- ctx.line_no++;
- 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.");
- goto ERR;
- }
- if (c == '[') {
- cho_log(&ctx, LOG_ERR, "Can't start a new chord/annotation if the previous one is not yet closed.");
- goto ERR;
- }
- 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;
- }
- ctx.state_before_tag = STATE_CHORD;
- ctx.state = STATE_MARKUP_TAG;
- break;
- }
- if (ctx.ch > CHORD_LEN-2) {
- cho_log(&ctx, LOG_ERR, "Chord can't be greater than %d bytes.", CHORD_LEN-1);
- goto ERR;
- }
- 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[TEXT_TYPE_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') {
- ctx.line_no++;
- 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.");
- goto ERR;
- }
- (*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;
- }
- 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 = TEXT_TYPE_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;
- break;
- }
- if (c == '\n') {
- ctx.line_no++;
- 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.");
- goto ERR;
- }
- 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 == BREAK_TYPE_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;
- }
- (*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_GRID: {
- if (prev_c == '\n' && c == '#') {
- ctx.state_before_comment = STATE_GRID;
- ctx.state = STATE_COMMENT;
- break;
- }
- if (prev_c == '\n' && c == '{') {
- ctx.state = STATE_DIRECTIVE_NAME;
- break;
- }
- if (c == '\n') {
- ctx.line_no++;
- if (prev_c != '}') {
- grid_token[ctx.gt] = 0;
- ctx.gt = 0;
- if (grid_token[0] == 0) {
- break;
- }
- token = cho_grid_token_parse(&ctx, grid_token, &err);
- if (err) {
- cho_log(&ctx, LOG_ERR, "Invalid token '%s' in grid section.", grid_token);
- goto ERR;
- }
- if (token == GRID_TOKEN_BAR_LINE_SYMBOL) {
- ctx.grid.bar_line_symbol_count++;
- ctx.grid.bar_line_symbol_in_line_count++;
- if (ctx.grid.bar_line_symbol_count == 2) {
- ctx.grid.expected_tokens_per_cell = ctx.grid.tokens_per_cell;
- } else {
- if (ctx.grid.bar_line_symbol_in_line_count > 1 && ctx.grid.expected_tokens_per_cell != ctx.grid.tokens_per_cell) {
- cho_log(
- &ctx,
- LOG_ERR,
- "Grid cell no. %d has %d tokens but should have %d tokens.",
- ctx.grid.bar_line_symbol_in_line_count - 1,
- ctx.grid.tokens_per_cell,
- ctx.grid.expected_tokens_per_cell
- );
- goto ERR;
- }
- }
- ctx.grid.tokens_per_cell = 0;
- } else {
- ctx.grid.tokens_per_cell++;
- }
- if (token == GRID_TOKEN_PLACEHOLDER) {
- (*lines)[ctx.li]->items[ctx.lii]->u.text->text = strdup(" ");
- } else {
- ctx.prev_ttype = ctx.current_ttype;
- ctx.current_ttype = TEXT_TYPE_GRID;
- cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style);
- (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_new_default(&ctx);
- (*lines)[ctx.li]->items[ctx.lii]->u.text->text = strdup(grid_token);
- ctx.current_ttype = ctx.prev_ttype;
- }
- 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;
- (*lines)[ctx.li]->text_above = erealloc((*lines)[ctx.li]->text_above, (ctx.lia+1) * sizeof(struct ChoLineItem *));
- (*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);
- ctx.grid.bar_line_symbol_in_line_count = 0;
- }
- break;
- }
- if (c == ' ') {
- grid_token[ctx.gt] = 0;
- ctx.gt = 0;
- if (grid_token[0] == 0) {
- break;
- }
- token = cho_grid_token_parse(&ctx, grid_token, &err);
- if (err) {
- cho_log(&ctx, LOG_ERR, "Invalid token '%s' in grid section.", grid_token);
- goto ERR;
- }
- if (token == GRID_TOKEN_BAR_LINE_SYMBOL) {
- ctx.grid.bar_line_symbol_count++;
- ctx.grid.bar_line_symbol_in_line_count++;
- if (ctx.grid.bar_line_symbol_count == 2) {
- ctx.grid.expected_tokens_per_cell = ctx.grid.tokens_per_cell;
- } else {
- if (ctx.grid.bar_line_symbol_in_line_count > 1 && ctx.grid.expected_tokens_per_cell != ctx.grid.tokens_per_cell) {
- cho_log(
- &ctx,
- LOG_ERR,
- "Grid cell no. %d has %d tokens but should have %d tokens.",
- ctx.grid.bar_line_symbol_in_line_count - 1,
- ctx.grid.tokens_per_cell,
- ctx.grid.expected_tokens_per_cell
- );
- goto ERR;
- }
- }
- ctx.grid.tokens_per_cell = 0;
- } else {
- ctx.grid.tokens_per_cell++;
- }
- if (token == GRID_TOKEN_PLACEHOLDER) {
- (*lines)[ctx.li]->items[ctx.lii]->u.text->text = strdup(" ");
- } else {
- ctx.prev_ttype = ctx.current_ttype;
- ctx.current_ttype = TEXT_TYPE_GRID;
- cho_style_free((*lines)[ctx.li]->items[ctx.lii]->u.text->style);
- (*lines)[ctx.li]->items[ctx.lii]->u.text->style = cho_style_new_default(&ctx);
- (*lines)[ctx.li]->items[ctx.lii]->u.text->text = strdup(grid_token);
- ctx.current_ttype = ctx.prev_ttype;
- }
- 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);
- break;
- }
- grid_token[ctx.gt] = c;
- ctx.gt++;
- 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.");
- goto ERR;
- }
- 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.");
- goto ERR;
- }
- 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]);
- goto ERR;
- }
- 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;
- break;
- }
- if (c == '\n') {
- ctx.line_no++;
- 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.");
- goto ERR;
- }
- if (ctx.t == 5) {
- cho_log(&ctx, LOG_ERR, "Start tag name is too long.");
- goto ERR;
- }
- 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.");
- goto ERR;
- }
- memset(tag_end, 0, strlen(tag_end));
- ctx.state = ctx.state_before_tag;
- break;
- }
- if (c == '\n') {
- ctx.line_no++;
- 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.");
- goto ERR;
- }
- if (ctx.t == 5) {
- cho_log(&ctx, LOG_ERR, "End tag name is too long.");
- goto ERR;
- }
- 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;
- 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);
- goto ERR;
- }
- }
- 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);
- goto ERR;
- }
- 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.");
- goto ERR;
- }
- (*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.");
- goto ERR;
- }
- 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]);
- goto ERR;
- }
- }
- ctx.at = 0;
- 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);
- goto ERR;
- }
- }
- if (c == '\n') {
- ctx.line_no++;
- 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.");
- goto ERR;
- }
- 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') {
- ctx.line_no++;
- 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.");
- goto ERR;
- }
- if (avs == ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED) {
- if (is_whitespace(c)) {
- cho_log(&ctx, LOG_ERR, "Whitespace character after equals sign is invalid.");
- goto ERR;
- }
- 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);
- goto ERR;
- }
- if (c == '\'') {
- avs = ATTRIBUTE_VALUE_SYNTAX_APOSTROPHE;
- } else if (c == '"') {
- avs = ATTRIBUTE_VALUE_SYNTAX_QUOTATION_MARK;
- } else {
- avs = ATTRIBUTE_VALUE_SYNTAX_UNQUOTED;
- if (c == '%') {
- ctx.state_before_metadata_substitution = STATE_MARKUP_ATTR_VALUE;
- ctx.state = STATE_MAYBE_METADATA_SUBSTITUTION;
- 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;
- }
- if (avs == ATTRIBUTE_VALUE_SYNTAX_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.");
- goto ERR;
- }
- (*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.");
- goto ERR;
- }
- 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]);
- goto ERR;
- }
- }
- ctx.at = 0;
- avs = ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED;
- memset(tag_start, 0, strlen(tag_start));
- ctx.state = ctx.state_before_tag;
- break;
- }
- if (
- (avs == ATTRIBUTE_VALUE_SYNTAX_APOSTROPHE && c == '\'') ||
- (avs == ATTRIBUTE_VALUE_SYNTAX_QUOTATION_MARK && c == '"') ||
- (avs == ATTRIBUTE_VALUE_SYNTAX_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 = ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED;
- ctx.state = STATE_MARKUP_ATTR_NAME;
- break;
- }
- if (c == '%') {
- ctx.state_before_metadata_substitution = STATE_MARKUP_ATTR_VALUE;
- ctx.state = STATE_MAYBE_METADATA_SUBSTITUTION;
- 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.line_no++;
- ctx.state = ctx.state_before_comment;
- break;
- }
- break;
- }
- case STATE_MAYBE_METADATA_SUBSTITUTION: {
- if (c == '{') {
- metadata_substitution[ctx.ms] = '%';
- ctx.ms++;
- metadata_substitution[ctx.ms] = '{';
- ctx.ms++;
- 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;
- case STATE_DIRECTIVE_VALUE:
- directive_value[ctx.dv] = '%';
- ctx.dv++;
- break;
- case STATE_MARKUP_ATTR_VALUE:
- 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] = '%';
- break;
- default:
- }
- ctx.state = ctx.state_before_metadata_substitution;
- str--;
- break;
- }
- case STATE_METADATA_SUBSTITUTION: {
- if (prev_c != '\\' && c == '}') {
- if (ctx.nested_level == 0) {
- metadata_substitution[ctx.ms] = '}';
- ctx.ms++;
- metadata_substitution[ctx.ms] = 0;
- ctx.ms = 0;
- char *substituted = cho_metadata_substitution_parse(
- &ctx,
- metadata_substitution,
- NULL,
- ctx.state_before_metadata_substitution
- );
- if (!substituted) {
- LOG_DEBUG("cho_metadata_substitution_parse failed.");
- goto ERR;
- }
- char *ch;
- switch (ctx.state_before_metadata_substitution) {
- case STATE_LYRICS:
- for (ch = substituted; *ch; ch++) {
- (*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] = *ch;
- ctx.te++;
- }
- break;
- case STATE_DIRECTIVE_VALUE:
- for (ch = substituted; *ch; ch++) {
- directive_value[ctx.dv] = *ch;
- ctx.dv++;
- }
- break;
- case STATE_MARKUP_ATTR_VALUE:
- for (ch = substituted; *ch; ch++) {
- 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] = *ch;
- ctx.atv++;
- }
- break;
- default:
- }
- free(substituted);
- 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.");
- goto ERR;
- }
- metadata_substitution[ctx.ms] = c;
- ctx.ms++;
- break;
- }
- }
- prev_c = c;
- }
- if (!cho_style_reset_default()) {
- LOG_DEBUG("cho_style_reset_default failed.");
- goto ERR;
- }
- cho_songs_close(&ctx, lines);
- cho_context_cleanup(&ctx);
- 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++) {
- if (
- !strcmp(ctx.songs[ctx.so]->metadata[ctx.m]->name, "title") &&
- ctx.songs[ctx.so]->metadata[ctx.m]->value &&
- strcmp(ctx.songs[ctx.so]->metadata[ctx.m]->value, "") != 0
- ) {
- exist_title = true;
- }
- }
- if (!exist_title) {
- /* INFO: This cho_log() is not line specific. It's a workaround. */
- ctx.line_no = 0;
- cho_log(&ctx, LOG_ERR, "Song has no title.");
- goto ERR;
- }
- exist_title = false;
- }
- return ctx.songs;
- ERR:
- cho_songs_close(&ctx, lines);
- cho_context_cleanup(&ctx);
- for (int e = 0; e<=ctx.so; e++) {
- cho_song_free(ctx.songs[e]);
- }
- free(ctx.songs);
- free(stripped_directive_value);
- cho_directive_free(directive);
- cho_tag_attrs_free(directive_attrs);
- return NULL;
-}