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