lorid

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

commit b1413065e6893fb887be2de725dcfd50d259626b
parent 5379234e9b50541180b4a90a222af2d5fee174b2
Author: nibo <nibo@relim.de>
Date:   Mon, 14 Jul 2025 21:16:23 +0200

WIP: Free memory in case of error

in `src/chordpro.c` until including `cho_style_parse`

Diffstat:
Msrc/chordpro.c | 623++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/config.c | 5+++++
Msrc/core.h | 3++-
3 files changed, 319 insertions(+), 312 deletions(-)

diff --git a/src/chordpro.c b/src/chordpro.c @@ -405,8 +405,7 @@ cho_rgbcolor_parse(const char *str) tmp[1] = str[2]; primary_color = strtol((char *)&tmp, NULL, 16); if (primary_color == 0) { - free(color); - return NULL; + goto ERR; } else { color->red = primary_color; } @@ -418,8 +417,7 @@ cho_rgbcolor_parse(const char *str) tmp[1] = str[4]; primary_color = strtol((char *)&tmp, NULL, 16); if (primary_color == 0) { - free(color); - return NULL; + goto ERR; } else { color->green = primary_color; } @@ -431,8 +429,7 @@ cho_rgbcolor_parse(const char *str) tmp[1] = str[6]; primary_color = strtol((char *)&tmp, NULL, 16); if (primary_color == 0) { - free(color); - return NULL; + goto ERR; } else { color->blue = primary_color; } @@ -446,8 +443,7 @@ cho_rgbcolor_parse(const char *str) tmp[1] = str[1]; primary_color = strtol((char *)&tmp, NULL, 16); if (primary_color == 0) { - free(color); - return NULL; + goto ERR; } else { color->red = primary_color; } @@ -459,8 +455,7 @@ cho_rgbcolor_parse(const char *str) tmp[1] = str[2]; primary_color = strtol((char *)&tmp, NULL, 16); if (primary_color == 0) { - free(color); - return NULL; + goto ERR; } else { color->green = primary_color; } @@ -472,72 +467,73 @@ cho_rgbcolor_parse(const char *str) tmp[1] = str[3]; primary_color = strtol((char *)&tmp, NULL, 16); if (primary_color == 0) { - free(color); - return NULL; + goto ERR; } else { color->blue = primary_color; } } } else { - free(color); - return NULL; + goto ERR; } return color; + ERR: + free(color); + return NULL; } struct RGBColor * cho_color_parse(const char *str) { - struct RGBColor *color = emalloc(sizeof(struct RGBColor)); + struct RGBColor *color; + if (str[0] == '#') { - struct RGBColor *rgb_color = cho_rgbcolor_parse(str); - if (rgb_color) { - free(color); - color = rgb_color; - } else { - free(color); + color = cho_rgbcolor_parse(str); + if (!color) { LOG_DEBUG("cho_rgbcolor_parse failed."); return NULL; } - } else if (!strcmp(str, "red")) { - color->red = 255; - color->green = 48; - color->blue = 48; - } else if (!strcmp(str, "green")) { - color->red = 0; - color->green = 126; - color->blue = 0; - } else if (!strcmp(str, "blue")) { - color->red = 0; - color->green = 0; - color->blue = 255; - } else if (!strcmp(str, "yellow")) { - color->red = 255; - color->green = 255; - color->blue = 0; - } else if (!strcmp(str, "magenta")) { - color->red = 255; - color->green = 48; - color->blue = 255; - } else if (!strcmp(str, "cyan")) { - color->red = 82; - color->green = 255; - color->blue = 255; - } else if (!strcmp(str, "white")) { - color->red = 255; - color->green = 255; - color->blue = 255; - } else if (!strcmp(str, "grey")) { - color->red = 191; - color->green = 191; - color->blue = 191; - } else if (!strcmp(str, "black")) { - color->red = 0; - color->green = 0; - color->blue = 0; } else { - free(color); - return NULL; + color = emalloc(sizeof(struct RGBColor)); + if (!strcmp(str, "red")) { + color->red = 255; + color->green = 48; + color->blue = 48; + } else if (!strcmp(str, "green")) { + color->red = 0; + color->green = 126; + color->blue = 0; + } else if (!strcmp(str, "blue")) { + color->red = 0; + color->green = 0; + color->blue = 255; + } else if (!strcmp(str, "yellow")) { + color->red = 255; + color->green = 255; + color->blue = 0; + } else if (!strcmp(str, "magenta")) { + color->red = 255; + color->green = 48; + color->blue = 255; + } else if (!strcmp(str, "cyan")) { + color->red = 82; + color->green = 255; + color->blue = 255; + } else if (!strcmp(str, "white")) { + color->red = 255; + color->green = 255; + color->blue = 255; + } else if (!strcmp(str, "grey")) { + color->red = 191; + color->green = 191; + color->blue = 191; + } else if (!strcmp(str, "black")) { + color->red = 0; + color->green = 0; + color->blue = 0; + } else { + free(color); + return NULL; + } } return color; } @@ -564,10 +560,7 @@ struct Font * cho_font_copy(struct Font *font) { struct Font *copy = emalloc(sizeof(struct Font)); - if (font->name) - copy->name = strdup(font->name); - else - copy->name = NULL; + copy->name = font->name ? strdup(font->name) : NULL; copy->family = font->family; copy->style = font->style; copy->weight = font->weight; @@ -676,10 +669,11 @@ void cho_font_print(struct Font *font) { printf("---- BEGIN FONT ----\n"); - if (font->name) + if (font->name) { printf("font name: %s\n", font->name); - else + } else { printf("font name: NULL\n"); + } printf("font family: %s\n", cho_font_family_to_config_string(font->family)); printf("font style: %s\n", cho_font_style_to_config_string(font->style)); printf("font weight: %s\n", cho_font_weight_to_config_string(font->weight)); @@ -769,7 +763,6 @@ cho_debug_style_print(struct ChoStyle *style) static bool cho_style_property_apply_default( - struct ChoContext *ctx, enum TextType current_ttype, enum StylePropertyType ptype, struct ChoStyle *style @@ -808,9 +801,6 @@ cho_style_property_apply_default( return false; } break; - default: - cho_log(ctx, LOG_WARN, "Invalid style property type '%d'.", ptype); - return false; } } } @@ -818,22 +808,22 @@ cho_style_property_apply_default( } static void -cho_style_apply_default(struct ChoContext *ctx, enum TextType current_ttype, struct ChoStyle *style) +cho_style_apply_default(enum TextType current_ttype, struct ChoStyle *style) { if (current_ttype == TEXT_TYPE_CHORUS) { - if (!cho_style_property_apply_default(ctx, TEXT_TYPE_CHORUS, STYLE_PROPERTY_TYPE_FONT, style)) { - cho_style_property_apply_default(ctx, TEXT_TYPE_TEXT, STYLE_PROPERTY_TYPE_FONT, style); + if (!cho_style_property_apply_default(TEXT_TYPE_CHORUS, STYLE_PROPERTY_TYPE_FONT, style)) { + cho_style_property_apply_default(TEXT_TYPE_TEXT, STYLE_PROPERTY_TYPE_FONT, style); } - if (!cho_style_property_apply_default(ctx, TEXT_TYPE_CHORUS, STYLE_PROPERTY_TYPE_SIZE, style)) { - cho_style_property_apply_default(ctx, TEXT_TYPE_TEXT, STYLE_PROPERTY_TYPE_SIZE, style); + if (!cho_style_property_apply_default(TEXT_TYPE_CHORUS, STYLE_PROPERTY_TYPE_SIZE, style)) { + cho_style_property_apply_default(TEXT_TYPE_TEXT, STYLE_PROPERTY_TYPE_SIZE, style); } - if (!cho_style_property_apply_default(ctx, TEXT_TYPE_CHORUS, STYLE_PROPERTY_TYPE_COLOR, style)) { - cho_style_property_apply_default(ctx, TEXT_TYPE_TEXT, STYLE_PROPERTY_TYPE_COLOR, style); + if (!cho_style_property_apply_default(TEXT_TYPE_CHORUS, STYLE_PROPERTY_TYPE_COLOR, style)) { + cho_style_property_apply_default(TEXT_TYPE_TEXT, STYLE_PROPERTY_TYPE_COLOR, style); } } else { - cho_style_property_apply_default(ctx, current_ttype, STYLE_PROPERTY_TYPE_FONT, style); - cho_style_property_apply_default(ctx, current_ttype, STYLE_PROPERTY_TYPE_SIZE, style); - cho_style_property_apply_default(ctx, current_ttype, STYLE_PROPERTY_TYPE_COLOR, style); + cho_style_property_apply_default(current_ttype, STYLE_PROPERTY_TYPE_FONT, style); + cho_style_property_apply_default(current_ttype, STYLE_PROPERTY_TYPE_SIZE, style); + cho_style_property_apply_default(current_ttype, STYLE_PROPERTY_TYPE_COLOR, style); } } @@ -959,7 +949,7 @@ cho_style_new_default(struct ChoContext *ctx) LOG_DEBUG("cho_style_new_from_config failed."); return NULL; } - cho_style_apply_default(ctx, ctx->current_ttype, style); + cho_style_apply_default(ctx->current_ttype, style); return style; } @@ -981,89 +971,87 @@ cho_style_free(struct ChoStyle *style) } static struct Font * -cho_style_font_desc_parse(const char *str, struct ChoStylePresence *presence) +cho_style_font_desc_parse(const char *font_desc, struct ChoStylePresence *presence) { - if (strlen(str) == 0) { + size_t len; + int w, n; + int name_until = EMPTY_INT; + char *c, *word; + char **words = NULL; + double size = -1.0; + struct Font *font; + + len = strlen(font_desc); + if (len == 0) { return NULL; } - struct Font *font = cho_font_new(); - double size = -1.0; - char **words = emalloc(sizeof(char *)); - int w = 0; - words[w] = NULL; - int k = 0; - int i; - for (i = 0; str[i]; i++) { - if (str[i] == ' ') { - words[w] = erealloc(words[w], (k+1) * sizeof(char)); - words[w][k] = 0; - if (strlen(words[w]) == 0) { - free(words[w]); - } else { - w++; - } - k = 0; - words = erealloc(words, (w+1) * sizeof(char *)); - words[w] = NULL; - } else { - words[w] = erealloc(words[w], (k+1) * sizeof(char)); - words[w][k] = str[i]; - k++; - } + char str[len+1]; + strcpy((char *)&str, font_desc); + for (c = (char *)&str, w = 0; (word = strtok(c, " ")); w++) { + words = erealloc(words, (w+1) * sizeof(char *)); + words[w] = word; + c = NULL; } - words[w] = erealloc(words[w], (k+1) * sizeof(char)); - words[w][k] = 0; - w++; words = erealloc(words, (w+1) * sizeof(char *)); words[w] = NULL; - int stop_at = EMPTY_INT; + font = cho_font_new(); for (w = 0; words[w]; w++) { - if (strcasecmp(words[w], "italic") == 0) { + if (!strcasecmp(words[w], "italic")) { font->style = FONT_STYLE_ITALIC; presence->font.style = true; - if (stop_at == EMPTY_INT) { - stop_at = w; + if (name_until == EMPTY_INT) { + name_until = w; } - } else if (strcasecmp(words[w], "bold") == 0) { + } else if (!strcasecmp(words[w], "bold")) { font->weight = FONT_WEIGHT_BOLD; presence->font.weight = true; - if (stop_at == EMPTY_INT) { - stop_at = w; + if (name_until == EMPTY_INT) { + name_until = w; } - } else if (strcasecmp(words[w], "oblique") == 0) { + } else if (!strcasecmp(words[w], "oblique")) { font->style = FONT_STYLE_OBLIQUE; presence->font.style = true; - if (stop_at == EMPTY_INT) { - stop_at = w; - } - // TODO: Is that smart? - } else if (strcasecmp(words[w], "regular") == 0) { + if (name_until == EMPTY_INT) { + name_until = w; + } + /* + * TODO: Is it smart to treat 'regular' as a font weight + * and 'normal' as a font style. The words are very similar. + * But what is the alternative? + * */ + } else if (!strcasecmp(words[w], "regular")) { font->weight = FONT_WEIGHT_REGULAR; presence->font.weight = true; - if (stop_at == EMPTY_INT) { - stop_at = w; + if (name_until == EMPTY_INT) { + name_until = w; } - // TODO: Is that smart? - } else if (strcasecmp(words[w], "normal") == 0) { + } else if (!strcasecmp(words[w], "normal")) { font->style = FONT_STYLE_ROMAN; presence->font.style = true; - if (stop_at == EMPTY_INT) { - stop_at = w; - } - /* Commented because the family name sometimes contains 'sans' or 'serif' */ + if (name_until == EMPTY_INT) { + name_until = w; + } + /* + * TODO: The following code is commented because it is unclear whether the + * words 'sans' and 'serif' are part of the font name or not. It is clear that + * e.g. given the word 'sans' the user wants a font with a family of sans and + * not e.g. monospace but when searching for the font with fontconfig an exact + * font name has to be provided. Maybe I should try all possible font names + * and return the first successful finding. + */ /* } else if (strcasecmp(words[w], "sans") == 0) { font->family = FONT_FAMILY_SANS; - if (stop_at == EMPTY_INT) - stop_at = w; + if (name_until == EMPTY_INT) + name_until = w; } else if (strcasecmp(words[w], "serif") == 0) { font->family = FONT_FAMILY_SERIF; - if (stop_at == EMPTY_INT) - stop_at = w; */ - } else if (strcasecmp(words[w], "monospace") == 0) { + if (name_until == EMPTY_INT) + name_until = w; */ + } else if (!strcasecmp(words[w], "monospace")) { font->family = FONT_FAMILY_MONOSPACE; presence->font.family = true; - if (stop_at == EMPTY_INT) { - stop_at = w; + if (name_until == EMPTY_INT) { + name_until = w; } } else { size = strtod(words[w], NULL); @@ -1072,32 +1060,27 @@ cho_style_font_desc_parse(const char *str, struct ChoStylePresence *presence) } font->size = size; presence->font.size = true; - if (stop_at == EMPTY_INT) { - stop_at = w; + if (name_until == EMPTY_INT) { + name_until = w; } } } - if (stop_at == EMPTY_INT) { - stop_at = w; + if (name_until == EMPTY_INT) { + name_until = w; } - if (stop_at > 0) { - int n = 0; - for (i = 0; i<stop_at; i++) { - for (k = 0; words[i][k]; n++, k++) { - font->name = erealloc(font->name, (n+1) * sizeof(char)); - font->name[n] = words[i][k]; - } - font->name = erealloc(font->name, (n+1) * sizeof(char)); + if (name_until > 0) { + for (w = 0, n = 0; w<name_until; w++) { + len = strlen(words[w]); + font->name = erealloc(font->name, (n+len+1) * sizeof(char)); + memcpy(&font->name[n], words[w], len); + n += len; font->name[n] = ' '; n++; } n--; - font->name[n] = 0; + font->name[n] = '\0'; presence->font.name = true; } - for (w = 0; words[w]; w++) { - free(words[w]); - } free(words); return font; } @@ -1150,7 +1133,7 @@ cho_style_parse( presence->font.family = true; } else { cho_log(ctx, LOG_ERR, "Attribute 'font_family/face' of markup tag '%s' has an invalid value '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } } else if (!strcmp(attrs[a]->name, "size")) { value_len = strlen(attrs[a]->value); @@ -1164,11 +1147,11 @@ cho_style_parse( presence->font.size = true; } else { cho_log(ctx, LOG_ERR, "Attribute 'size' of markup tag '%s' has an invalid percentage '%d'.", tag_name, percentage); - return NULL; + goto ERR; } } else { cho_log(ctx, LOG_ERR, "Attribute 'size' of markup tag '%s' has an invalid percentage '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } } else if (isdigit(attrs[a]->value[0]) != 0) { double size = strtod(attrs[a]->value, NULL); @@ -1177,7 +1160,7 @@ cho_style_parse( presence->font.size = true; } else { cho_log(ctx, LOG_ERR, "Attribute 'size' of markup tag '%s' has an invalid number '%.1f'.", tag_name, size); - return NULL; + goto ERR; } } else if (!strcmp(attrs[a]->value, "xx-small")) { style->font->size *= 0.8; @@ -1212,7 +1195,7 @@ cho_style_parse( presence->font.size = true; } else { cho_log(ctx, LOG_ERR, "Attribute 'size' of markup tag '%s' has an invalid value '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } } else if (!strcmp(attrs[a]->name, "style")) { if (!strcmp(attrs[a]->value, "normal")) { @@ -1226,7 +1209,7 @@ cho_style_parse( presence->font.style = true; } else { cho_log(ctx, LOG_ERR, "Attribute 'style' of markup tag '%s' has an invalid value '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } } else if (!strcmp(attrs[a]->name, "weight")) { if (!strcmp(attrs[a]->value, "normal")) { @@ -1237,14 +1220,14 @@ cho_style_parse( presence->font.weight = true; } else { cho_log(ctx, LOG_ERR, "Attribute 'weight' of markup tag '%s' has an invalid value '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } } else if (!strcmp(attrs[a]->name, "foreground")) { rgb_color = cho_color_parse(attrs[a]->value); if (!rgb_color) { LOG_DEBUG("cho_color_parse failed."); cho_log(ctx, LOG_ERR, "Attribute 'foreground' of markup tag '%s' has an invalid value '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } else { free(style->foreground_color); style->foreground_color = rgb_color; @@ -1255,7 +1238,7 @@ cho_style_parse( if (!rgb_color) { LOG_DEBUG("cho_color_parse failed."); cho_log(ctx, LOG_ERR, "Attribute 'background' of markup tag '%s' has an invalid value '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } else { free(style->background_color); style->background_color = rgb_color; @@ -1273,14 +1256,14 @@ cho_style_parse( presence->underline_style = true; } else { cho_log(ctx, LOG_ERR, "Attribute 'underline' of markup tag '%s' has an invalid value '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } } else if (!strcmp(attrs[a]->name, "underline_colour")) { rgb_color = cho_color_parse(attrs[a]->value); if (!rgb_color) { LOG_DEBUG("cho_color_parse failed."); cho_log(ctx, LOG_ERR, "Attribute 'underline_colour' of markup tag '%s' has an invalid value '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } else { free(style->underline_color); style->underline_color = rgb_color; @@ -1298,14 +1281,14 @@ cho_style_parse( presence->overline_style = true; } else { cho_log(ctx, LOG_ERR, "Attribute 'overline' of markup tag '%s' has an invalid value '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } } else if (!strcmp(attrs[a]->name, "overline_colour")) { rgb_color = cho_color_parse(attrs[a]->value); if (!rgb_color) { LOG_DEBUG("cho_color_parse failed."); cho_log(ctx, LOG_ERR, "Attribute 'overline_colour' of markup tag '%s' has an invalid value '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } else { free(style->overline_color); style->overline_color = rgb_color; @@ -1324,7 +1307,7 @@ cho_style_parse( presence->rise = true; } else { cho_log(ctx, LOG_ERR, "Attribute 'rise' of markup tag '%s' has an invalid percentage '%d'.", tag_name, percentage); - return NULL; + goto ERR; } } } else if (isdigit(attrs[a]->value[1]) != 0) { @@ -1334,11 +1317,11 @@ cho_style_parse( presence->rise = true; } else { cho_log(ctx, LOG_ERR, "Attribute 'rise' of markup tag '%s' has an invalid number '%d'.", tag_name, rise); - return NULL; + goto ERR; } } else { cho_log(ctx, LOG_ERR, "Attribute 'rise' of markup tag '%s' has an invalid value '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } } else { if (attrs[a]->value[last_char] == '%') { @@ -1352,7 +1335,7 @@ cho_style_parse( presence->rise = true; } else { cho_log(ctx, LOG_ERR, "Attribute 'rise' of markup tag '%s' has an invalid percentage '%d'.", tag_name, percentage); - return NULL; + goto ERR; } } } else if (isdigit(attrs[a]->value[0]) != 0) { @@ -1362,11 +1345,11 @@ cho_style_parse( presence->rise = true; } else { cho_log(ctx, LOG_ERR, "Attribute 'rise' of markup tag '%s' has an invalid number '%d'.", tag_name, rise); - return NULL; + goto ERR; } } else { cho_log(ctx, LOG_ERR, "Attribute 'rise' of markup tag '%s' has an invalid value '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } } } else if (!strcmp(attrs[a]->name, "strikethrough")) { @@ -1378,14 +1361,14 @@ cho_style_parse( presence->strikethrough = true; } else { cho_log(ctx, LOG_ERR, "Attribute 'strikethrough' of markup tag '%s' has an invalid value '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } } else if (!strcmp(attrs[a]->name, "strikethrough_colour")) { rgb_color = cho_color_parse(attrs[a]->value); if (!rgb_color) { LOG_DEBUG("cho_color_parse failed."); cho_log(ctx, LOG_ERR, "Attribute 'strikethrough_colour' of markup tag '%s' has an invalid value '%s'.", tag_name, attrs[a]->value); - return NULL; + goto ERR; } else { free(style->strikethrough_color); style->strikethrough_color = rgb_color; @@ -1396,7 +1379,7 @@ cho_style_parse( presence->href = true; } else { cho_log(ctx, LOG_ERR, "Attribute '%s' of markup tag '%s' is invalid.", attrs[a]->name, tag_name); - return NULL; + goto ERR; } } } else if (!strcmp(tag_name, "b")) { @@ -1432,10 +1415,12 @@ cho_style_parse( presence->underline_style = true; } else { cho_log(ctx, LOG_ERR, "Markup tag '%s' is invalid.", tag_name); - cho_style_free(style); - return NULL; + goto ERR; } return style; + ERR: + cho_style_free(style); + return NULL; } void @@ -1711,14 +1696,25 @@ static struct ChoMetadata ** cho_metadata_load_default(struct ChoContext *ctx) { struct ChoMetadata **meta = NULL; - char *filename; - char *logged_in_user = getenv("USER"); + struct InstrumentInfo ins_info; + struct tm *tt; + time_t t; + char *filename, *logged_in_user; + char time_str[64]; + int i = 0; + + logged_in_user = getenv("USER"); if (!logged_in_user) { LOG_DEBUG("getenv(USER) failed."); return NULL; } - struct InstrumentInfo ins_info = config_instrument_get(ctx->config->output->diagram->instrument); - int i = 0; + t = time(NULL); + tt = localtime(&t); + if (strftime((char *)&time_str, 64, "%a, %d. %b %H:%M", tt) == 0) { + LOG_DEBUG("strftime failed."); + return NULL; + } + ins_info = config_instrument_get(ctx->config->output->diagram->instrument); meta = erealloc(meta, (i+1) * sizeof(struct ChoMetadata *)); meta[i] = emalloc(sizeof(struct ChoMetadata)); meta[i]->name = strdup("chordpro"); @@ -1758,13 +1754,6 @@ cho_metadata_load_default(struct ChoContext *ctx) meta[i]->value = strdup(ins_info.description); meta[i]->style = cho_style_new_default(ctx); i++; - const time_t t = time(NULL); - struct tm *tt = localtime(&t); - char time_str[64]; - if (strftime((char *)&time_str, 64, "%a, %d. %b %H:%M", tt) == 0) { - LOG_DEBUG("strftime failed."); - return NULL; - } meta = erealloc(meta, (i+1) * sizeof(struct ChoMetadata *)); meta[i] = emalloc(sizeof(struct ChoMetadata)); meta[i]->name = strdup("today"); @@ -4006,6 +3995,7 @@ cho_song_new(struct ChoContext *ctx) song->metadata = cho_metadata_load_default(ctx); if (!song->metadata) { LOG_DEBUG("cho_metadata_load_default failed."); + free(song); return NULL; } song->sections = NULL; @@ -4779,6 +4769,41 @@ cho_grid_token_parse(struct ChoContext *ctx, const char *token, bool *err) return GRID_TOKEN_CHORD; } +static void +cho_songs_close(struct ChoContext *ctx, struct ChoLine ***lines) +{ + if ((*lines)[ctx->li]->items[ctx->lii]->is_text) { + if ((*lines)[ctx->li]->items[ctx->lii]->u.text->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; + (*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->li++; + *lines = erealloc(*lines, (ctx->li+1) * sizeof(struct ChoLine *)); + (*lines)[ctx->li] = NULL; + } else { + cho_line_item_free((*lines)[ctx->li]->items[ctx->lii]); + free((*lines)[ctx->li]->items); + free((*lines)[ctx->li]->text_above); + 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; + ctx->so++; + ctx->songs = erealloc(ctx->songs, (ctx->so+1) * sizeof(struct ChoSong *)); + ctx->songs[ctx->so] = NULL; +} + static bool cho_context_init( struct ChoContext *ctx, @@ -4840,6 +4865,8 @@ cho_context_init( ctx->songs[ctx->so] = cho_song_new(ctx); if (!ctx->songs[ctx->so]) { LOG_DEBUG("cho_song_new failed."); + free(ctx->transpose_history); + free(ctx->songs); return false; } ctx->songs[ctx->so]->sections = emalloc((ctx->se+1) * sizeof(struct ChoSection *)); @@ -4849,20 +4876,36 @@ cho_context_init( return true; } +static void +cho_context_cleanup(struct ChoContext *ctx) +{ + int i; + + for (i = 0; i<=ctx->ta; i++) { + cho_tag_free(ctx->tags[i]); + } + free(ctx->tags); + free(ctx->transpose_history); + for (i = 0; i<ctx->ia; i++) { + cho_image_free(ctx->image_assets[i]); + } + free(ctx->image_assets); +} + struct ChoSong ** cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *config) { enum AttrValueSyntax avs = ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED; enum GridToken token; - struct Attr **attrs = NULL; + 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; - struct ChoMetadata *metadata; + struct ChoDirective *directive = NULL; + struct ChoMetadata *metadata = NULL; struct ChoLine ***lines; struct ChoContext ctx; bool err; @@ -4943,6 +4986,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c 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; } @@ -5078,7 +5122,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c } else { cho_log(&ctx, LOG_ERR, "Can't close a %s section that wasn't opened earlier.", section_type); } - return NULL; + goto ERR; } break; } @@ -5164,9 +5208,6 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c } break; } - default: - cho_log(&ctx, LOG_ERR, "Invalid position value '%d'.", directive->position); - return NULL; } break; } @@ -5178,7 +5219,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c break; case DIRECTIVE_TYPE_IMAGE: cho_log(&ctx, LOG_ERR, "Directive 'image' has no value."); - return NULL; + goto ERR; case DIRECTIVE_TYPE_PREAMBLE: { // INFO: The only preamble directive is 'new_song' cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); @@ -5194,7 +5235,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c ctx.songs[ctx.so]->diagrams[ctx.dia] = NULL; if (!cho_style_reset_default(&ctx)) { LOG_DEBUG("cho_style_reset_default failed."); - return NULL; + goto ERR; } for (int e = 0; e<ctx.ia; e++) { cho_image_free(ctx.image_assets[e]); @@ -5207,7 +5248,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c ctx.songs[ctx.so] = cho_song_new(&ctx); if (!ctx.songs[ctx.so]) { LOG_DEBUG("cho_song_new failed."); - return NULL; + goto ERR; } free(ctx.transpose_history); ctx.th = 0; @@ -5242,13 +5283,10 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c case STYLE_PROPERTY_TYPE_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; + goto ERR; } break; } @@ -5273,9 +5311,6 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c case DIRECTIVE_TYPE_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; } cho_directive_free(directive); directive = NULL; @@ -5283,7 +5318,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c } if (c == '{') { cho_log(&ctx, LOG_ERR, "Can't start a new directive if the previous one is not yet closed."); - return NULL; + goto ERR; } if (c == '\n') { ctx.line_no++; @@ -5294,7 +5329,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c break; } cho_log(&ctx, LOG_ERR, "Can't have a newline in a directive name."); - return NULL; + goto ERR; } if (c == ':' || c == ' ') { directive_name[ctx.dn] = 0; @@ -5339,18 +5374,18 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c ctx.songs[ctx.so]->sections[ctx.se]->type = directive->stype; if (strchr(stripped_directive_value, '=')) { - attrs = cho_attrs_parse(&ctx, stripped_directive_value); - if (!attrs) { + directive_attrs = cho_attrs_parse(&ctx, stripped_directive_value); + if (!directive_attrs) { LOG_DEBUG("cho_attrs_parse failed."); - return NULL; + goto ERR; } - label = cho_attrs_get(attrs, "label"); + label = cho_attrs_get(directive_attrs, "label"); if (directive->stype == SECTION_TYPE_GRID) { - shape = cho_attrs_get(attrs, "shape"); + 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."); - return NULL; + goto ERR; } } } @@ -5363,7 +5398,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c } if (!cho_grid_shape_parse_and_set(&ctx, stripped_directive_value)) { LOG_DEBUG("cho_grid_parse_and_set_shape failed."); - return NULL; + goto ERR; } } else { label = stripped_directive_value; @@ -5378,8 +5413,8 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c ctx.current_ttype = ctx.prev_ttype; label = NULL; } - cho_tag_attrs_free(attrs); - attrs = 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; @@ -5395,7 +5430,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c } case POSITION_END: { cho_log(&ctx, LOG_ERR, "A directive that closes a section can't have arguments."); - return NULL; + goto ERR; } case POSITION_NO: { /* INFO: {chorus: ...} */ @@ -5494,9 +5529,6 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c } break; } - default: - cho_log(&ctx, LOG_ERR, "Invalid position value '%d'.", directive->position); - return NULL; } break; } @@ -5531,7 +5563,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c metadata = cho_metadata_split(&ctx, directive_value); if (!metadata) { LOG_DEBUG("cho_metadata_split failed."); - return NULL; + 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; @@ -5581,7 +5613,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c image = cho_image_directive_parse(&ctx, directive_value); if (!image) { LOG_DEBUG("cho_image_directive_parse failed."); - return NULL; + goto ERR; } } else { image = cho_image_new(); @@ -5608,7 +5640,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c } case DIRECTIVE_TYPE_PREAMBLE: cho_log(&ctx, LOG_ERR, "Preamble directive '%s' can't have a value.", directive_name); - return NULL; + goto ERR; case DIRECTIVE_TYPE_FONT: { sprop.ttype = directive->ttype; char *dir_value = strdup(stripped_directive_value); @@ -5622,7 +5654,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c 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; + goto ERR; } sprop.type = STYLE_PROPERTY_TYPE_SIZE; break; @@ -5630,17 +5662,17 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c 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; + goto ERR; } sprop.type = STYLE_PROPERTY_TYPE_COLOR; break; default: cho_log(&ctx, LOG_ERR, "Invalid style property type '%d'.", directive->sprop); - return NULL; + goto ERR; } if (!cho_style_change_default(&ctx, sprop)) { LOG_DEBUG("cho_style_change_default failed."); - return NULL; + goto ERR; } if (sprop.type == STYLE_PROPERTY_TYPE_FONT) { free(sprop.u.font_name); @@ -5656,7 +5688,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c if (!transposition_parse(directive_value, &transpose)) { LOG_DEBUG("transposition_parse failed."); cho_log(&ctx, LOG_ERR, "Directive 'transpose' has an invalid value."); - return NULL; + 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; @@ -5667,7 +5699,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c 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; + goto ERR; } // debug_chord_diagram_print(diagram); ctx.songs[ctx.so]->diagrams = erealloc(ctx.songs[ctx.so]->diagrams, (ctx.dia+1) * sizeof(struct ChordDiagram *)); @@ -5679,16 +5711,13 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c } case DIRECTIVE_TYPE_OUTPUT: cho_log(&ctx, LOG_ERR, "Directive '%s' can't have a value.", directive_name); - return NULL; + 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; - default: - cho_log(&ctx, LOG_ERR, "Invalid directive type '%d'.", directive->dtype); - return NULL; } switch (directive->stype) { case SECTION_TYPE_TAB: @@ -5718,7 +5747,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c } if (c == '{') { cho_log(&ctx, LOG_ERR, "Can't start a new directive if the previous one is not yet closed."); - return NULL; + goto ERR; } if (c == '\n') { ctx.line_no++; @@ -5729,11 +5758,11 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c break; } cho_log(&ctx, LOG_ERR, "Newline character inside a directive value is invalid."); - return NULL; + goto ERR; } if (ctx.dv > 4094) { cho_log(&ctx, LOG_ERR, "Directive value can't be greater than 4095 bytes."); - return NULL; + goto ERR; } directive_value[ctx.dv] = c; ctx.dv++; @@ -5794,11 +5823,11 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c break; } cho_log(&ctx, LOG_ERR, "Newline character inside a chord is invalid."); - return NULL; + goto ERR; } if (c == '[') { cho_log(&ctx, LOG_ERR, "Can't start a new chord/annotation if the previous one is not yet closed."); - return NULL; + goto ERR; } if (c == '<') { if (prev_c == '[') { @@ -5814,7 +5843,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c } if (ctx.ch > CHORD_LEN-2) { cho_log(&ctx, LOG_ERR, "Chord can't be greater than %d bytes.", CHORD_LEN-1); - return NULL; + goto ERR; } chord[ctx.ch] = c; ctx.ch++; @@ -5845,7 +5874,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c break; } cho_log(&ctx, LOG_ERR, "Newline character inside an annotation is invalid."); - return NULL; + 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; @@ -5933,7 +5962,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c } 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; + 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)); @@ -6000,7 +6029,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c token = cho_grid_token_parse(&ctx, grid_token, &err); if (err) { cho_log(&ctx, LOG_ERR, "Invalid token '%s' in grid section.", grid_token); - return NULL; + goto ERR; } if (token == GRID_TOKEN_BAR_LINE_SYMBOL) { ctx.grid.bar_line_symbol_count++; @@ -6017,7 +6046,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c ctx.grid.tokens_per_cell, ctx.grid.expected_tokens_per_cell ); - return NULL; + goto ERR; } } ctx.grid.tokens_per_cell = 0; @@ -6059,7 +6088,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c token = cho_grid_token_parse(&ctx, grid_token, &err); if (err) { cho_log(&ctx, LOG_ERR, "Invalid token '%s' in grid section.", grid_token); - return NULL; + goto ERR; } if (token == GRID_TOKEN_BAR_LINE_SYMBOL) { ctx.grid.bar_line_symbol_count++; @@ -6076,7 +6105,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c ctx.grid.tokens_per_cell, ctx.grid.expected_tokens_per_cell ); - return NULL; + goto ERR; } } ctx.grid.tokens_per_cell = 0; @@ -6120,13 +6149,13 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c 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; + 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."); - return NULL; + goto ERR; } ctx.tags[ctx.ta]->style = tag_style; switch (ctx.state_before_tag) { @@ -6158,7 +6187,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c break; default: cho_log(&ctx, LOG_ERR, "Invalid state_before_tag '%s'.", state_enums[ctx.state_before_tag]); - return NULL; + goto ERR; } memset(tag_start, 0, strlen(tag_start)); ctx.state = ctx.state_before_tag; @@ -6182,11 +6211,11 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c break; } cho_log(&ctx, LOG_ERR, "Newline character inside a tag name is invalid."); - return NULL; + goto ERR; } if (ctx.t == 5) { cho_log(&ctx, LOG_ERR, "Start tag name is too long."); - return NULL; + goto ERR; } tag_start[ctx.t] = c; ctx.t++; @@ -6198,7 +6227,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c 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; + goto ERR; } memset(tag_end, 0, strlen(tag_end)); ctx.state = ctx.state_before_tag; @@ -6213,11 +6242,11 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c break; } cho_log(&ctx, LOG_ERR, "Newline character inside a tag name is invalid."); - return NULL; + goto ERR; } if (ctx.t == 5) { cho_log(&ctx, LOG_ERR, "End tag name is too long."); - return NULL; + goto ERR; } tag_end[ctx.t] = c; ctx.t++; @@ -6239,7 +6268,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *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; 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; + goto ERR; } } if (ctx.tags[ctx.ta]->attrs[ctx.at-1]->name && ctx.tags[ctx.ta]->attrs[ctx.at-1]->value) { @@ -6251,7 +6280,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *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; 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; + goto ERR; } if (c == '>') { if (ctx.tags[ctx.ta]->attrs[ctx.at-1]->value) { @@ -6264,7 +6293,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c image = cho_image_tag_parse(&ctx, ctx.tags[ctx.ta]->attrs); if (!image) { LOG_DEBUG("cho_image_tag_parse failed."); - return NULL; + goto ERR; } (*lines)[ctx.li]->items[ctx.lii]->u.image = image; ctx.lii++; @@ -6274,7 +6303,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c 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; + goto ERR; } ctx.tags[ctx.ta]->style = tag_style; switch (ctx.state_before_tag) { @@ -6295,7 +6324,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c break; default: cho_log(&ctx, LOG_ERR, "Invalid state_before_tag '%s'.", state_enums[ctx.state_before_tag]); - return NULL; + goto ERR; } } ctx.at = 0; @@ -6307,7 +6336,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c 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; + goto ERR; } } if (c == '\n') { @@ -6319,7 +6348,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c break; } cho_log(&ctx, LOG_ERR, "Newline character inside an tag attribute name is invalid."); - return NULL; + 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; @@ -6336,16 +6365,16 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c break; } cho_log(&ctx, LOG_ERR, "Newline character inside an attribute value is invalid."); - return NULL; + goto ERR; } if (avs == ATTRIBUTE_VALUE_SYNTAX_UNINITIALIZED) { if (is_whitespace(c)) { cho_log(&ctx, LOG_ERR, "Whitespace character after equals sign is invalid."); - return NULL; + 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); - return NULL; + goto ERR; } if (c == '\'') { avs = ATTRIBUTE_VALUE_SYNTAX_APOSTROPHE; @@ -6377,7 +6406,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c image = cho_image_tag_parse(&ctx, ctx.tags[ctx.ta]->attrs); if (!image) { LOG_DEBUG("cho_image_tag_parse failed."); - return NULL; + goto ERR; } (*lines)[ctx.li]->items[ctx.lii]->u.image = image; ctx.lii++; @@ -6387,7 +6416,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c 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; + goto ERR; } ctx.tags[ctx.ta]->style = tag_style; switch (ctx.state_before_tag) { @@ -6408,7 +6437,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c break; default: cho_log(&ctx, LOG_ERR, "Invalid state_before_tag '%s'.", state_enums[ctx.state_before_tag]); - return NULL; + goto ERR; } } ctx.at = 0; @@ -6494,7 +6523,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c ); if (!substituted) { LOG_DEBUG("cho_metadata_substitution_parse failed."); - return NULL; + goto ERR; } char *ch; switch (ctx.state_before_metadata_substitution) { @@ -6531,7 +6560,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c } if (ctx.ms > 4094) { cho_log(&ctx, LOG_ERR, "Metadata substitution can't be greater than 4095 bytes."); - return NULL; + goto ERR; } metadata_substitution[ctx.ms] = c; ctx.ms++; @@ -6540,51 +6569,12 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c } prev_c = c; } - int e = 0; - while (e <= ctx.ta) { - cho_tag_free(ctx.tags[e]); - e++; - } - free(ctx.tags); - if ((*lines)[ctx.li]->items[ctx.lii]->is_text) { - if ((*lines)[ctx.li]->items[ctx.lii]->u.text->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; - (*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.li++; - *lines = erealloc(*lines, (ctx.li+1) * sizeof(struct ChoLine *)); - (*lines)[ctx.li] = NULL; - } else { - cho_line_item_free((*lines)[ctx.li]->items[ctx.lii]); - free((*lines)[ctx.li]->items); - free((*lines)[ctx.li]->text_above); - free((*lines)[ctx.li]); - (*lines)[ctx.li] = NULL; - } - } if (!cho_style_reset_default(&ctx)) { LOG_DEBUG("cho_style_reset_default failed."); - return NULL; + 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] = 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; - ctx.so++; - ctx.songs = erealloc(ctx.songs, (ctx.so+1) * sizeof(struct ChoSong *)); - ctx.songs[ctx.so] = NULL; - free(ctx.transpose_history); - for (e = 0; e<ctx.ia; e++) { - cho_image_free(ctx.image_assets[e]); - } - free(ctx.image_assets); + 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++) { @@ -6600,11 +6590,22 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c /* 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."); - return NULL; + 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); + cho_directive_free(directive); + cho_tag_attrs_free(directive_attrs); + cho_metadata_free(metadata); + return NULL; } #ifdef DEBUG diff --git a/src/config.c b/src/config.c @@ -811,6 +811,7 @@ config_load_default(void) { struct Config *config = emalloc(sizeof(struct Config)); config->metadata_separator = strdup("; "); + config->verbose_logging = false; config->parser = emalloc(sizeof(struct ConfigParser)); config->parser->chords = emalloc(sizeof(struct ConfigChords)); @@ -900,6 +901,10 @@ config_load(toml_table_t *toml, const char *filepath) free(config->metadata_separator); config->metadata_separator = value.u.s; } + value = toml_table_bool(toml, "verbose_logging"); + if (value.ok) { + config->verbose_logging = value.u.b; + } output = toml_table_table(toml, "output"); if (output) { toml_table_t *styles, *notes, *chorus, *diagram, *toc, *page_no; diff --git a/src/core.h b/src/core.h @@ -180,7 +180,7 @@ struct ChoStylePresence { }; struct Font { - char *name; + char *name; // INFO: Exact font family name, e.g. from `fc-list : family` enum FontFamily family; enum FontStyle style; enum FontWeight weight; @@ -376,6 +376,7 @@ struct ConfigOutput { }; struct Config { + bool verbose_logging; char *metadata_separator; struct ConfigOutput *output; struct ConfigParser *parser;