lorid

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

config.c (35169B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <stdarg.h>
      4 #include <string.h>
      5 #include <errno.h>
      6 #include <toml.h>
      7 #include "core.h"
      8 #include "config.h"
      9 #include "chordpro.h"
     10 #include "chord_diagram.h"
     11 
     12 extern const struct InstrumentInfo instruments[];
     13 
     14 static const char *notation_systems[] = {
     15 	"common",
     16 	"german",
     17 	"scandinavian",
     18 	"latin",
     19 	"roman",
     20 	"nashville"
     21 };
     22 
     23 static const char *parse_modes[] = {
     24 	"strict",
     25 	"relaxed"
     26 };
     27 
     28 static const char *text_types[] = {
     29 	"chord", "annotation", "chorus",
     30 	"footer", "grid", "tab", "toc", "toc_title", "text",
     31 	"title", "subtitle", "label", "comment",
     32 	"comment_italic", "comment_box"
     33 };
     34 
     35 static const char *alignments[] = {
     36 	"left", "center", "right"
     37 };
     38 
     39 static struct Note notes_common[] = {
     40 	{ .note = "C", .sharp = "C#", .flat = NULL },
     41 	{ .note = "D", .sharp = "D#", .flat = "Db" },
     42 	{ .note = "E", .sharp = NULL, .flat = "Eb" },
     43 	{ .note = "F", .sharp = "F#", .flat = NULL },
     44 	{ .note = "G", .sharp = "G#", .flat = "Gb" },
     45 	{ .note = "A", .sharp = "A#", .flat = "Ab" },
     46 	{ .note = "B", .sharp = NULL, .flat = "Bb" }
     47 };
     48 
     49 static struct Note notes_german[] = {
     50 	{ .note = "C", .sharp = "Cis", .flat = NULL },
     51 	{ .note = "D", .sharp = "Dis", .flat = "Des" },
     52 	{ .note = "E", .sharp = NULL, .flat = "Es" },
     53 	{ .note = "F", .sharp = "Fis", .flat = NULL },
     54 	{ .note = "G", .sharp = "Gis", .flat = "Ges" },
     55 	{ .note = "A", .sharp = "Ais", .flat = "As" },
     56 	{ .note = "H", .sharp = NULL, .flat = "B"},
     57 };
     58 
     59 static struct Note notes_scandinavian[] = {
     60 	{ .note = "C", .sharp = "C#", .flat = NULL },
     61 	{ .note = "D", .sharp = "D#", .flat = "Db"},
     62 	{ .note = "E", .sharp = NULL, .flat = "Eb"},
     63 	{ .note = "F", .sharp = "F#", .flat = NULL },
     64 	{ .note = "G", .sharp = "G#", .flat = "Gb"},
     65 	{ .note = "A", .sharp = "A#", .flat = "Ab"},
     66 	{ .note = "H", .sharp = NULL, .flat = "B"},
     67 };
     68 
     69 static struct Note notes_latin[] = {
     70 	{ .note = "Do", .sharp = "Do#", .flat = NULL },
     71 	{ .note = "Re", .sharp = "Re#", .flat = "Reb"},
     72 	{ .note = "Mi", .sharp = NULL, .flat = "Mib"},
     73 	{ .note = "Fa", .sharp = "Fa#", .flat = NULL },
     74 	{ .note = "Sol", .sharp = "Sol#", .flat = "Solb"},
     75 	{ .note = "La", .sharp = "La#", .flat = "Lab"},
     76 	{ .note = "Si", .sharp = NULL, .flat = "Sib"},
     77 };
     78 
     79 static struct Note notes_roman[] = {
     80 	{ .note = "I", .sharp = "I#", .flat = NULL },
     81 	{ .note = "II", .sharp = "II#", .flat = "IIb" },
     82 	{ .note = "III", .sharp = NULL, .flat = "IIIb" },
     83 	{ .note = "IV", .sharp = "IV#", .flat = NULL },
     84 	{ .note = "V", .sharp = "V#", .flat = "Vb" },
     85 	{ .note = "VI", .sharp = "VI#", .flat = "VIb" },
     86 	{ .note = "VII", .sharp = NULL, .flat = "VIIb" },
     87 };
     88 
     89 static struct Note notes_nashville[] = {
     90 	{ .note = "1", .sharp = "1#", .flat = NULL },
     91 	{ .note = "2", .sharp = "2#", .flat = "2b" },
     92 	{ .note = "3", .sharp = NULL, .flat = "3b" },
     93 	{ .note = "4", .sharp = "4#", .flat = NULL },
     94 	{ .note = "5", .sharp = "5#", .flat = "5b" },
     95 	{ .note = "6", .sharp = "6#", .flat = "6b" },
     96 	{ .note = "7", .sharp = NULL, .flat = "7b" },
     97 };
     98 
     99 static void
    100 config_log(
    101 	struct ConfigContext *ctx,
    102 	enum LogLevel level,
    103 	const char *toml_section,
    104 	const char *msg,
    105 	...
    106 )
    107 {
    108 	va_list va;
    109 	va_start(va, msg);
    110 	size_t size = 10 + strlen(toml_section) + strlen(msg) + 1;
    111 	char str[size];
    112 	snprintf((char *)&str, size, "section %s: %s", toml_section, msg);
    113 	util_vlog(ctx->config_filepath, 0, level, str, va);
    114 }
    115 
    116 static enum TextType
    117 config_text_type_parse(const char *str, bool *error)
    118 {
    119 	*error = false;
    120 	if (!strcmp(str, text_types[TEXT_TYPE_CHORD])) {
    121 		return TEXT_TYPE_CHORD;
    122 	} else
    123 	if (!strcmp(str, text_types[TEXT_TYPE_ANNOT])) {
    124 		return TEXT_TYPE_ANNOT;
    125 	} else
    126 	if (!strcmp(str, text_types[TEXT_TYPE_CHORUS])) {
    127 		return TEXT_TYPE_CHORUS;
    128 	} else
    129 	if (!strcmp(str, text_types[TEXT_TYPE_FOOTER])) {
    130 		return TEXT_TYPE_FOOTER;
    131 	} else
    132 	if (!strcmp(str, text_types[TEXT_TYPE_GRID])) {
    133 		return TEXT_TYPE_GRID;
    134 	} else
    135 	if (!strcmp(str, text_types[TEXT_TYPE_TAB])) {
    136 		return TEXT_TYPE_TAB;
    137 	} else
    138 	if (!strcmp(str, text_types[TEXT_TYPE_TOC])) {
    139 		return TEXT_TYPE_TOC;
    140 	} else
    141 	if (!strcmp(str, text_types[TEXT_TYPE_TOC_TITLE])) {
    142 		return TEXT_TYPE_TOC_TITLE;
    143 	} else
    144 	if (!strcmp(str, text_types[TEXT_TYPE_TEXT])) {
    145 		return TEXT_TYPE_TEXT;
    146 	} else
    147 	if (!strcmp(str, text_types[TEXT_TYPE_TITLE])) {
    148 		return TEXT_TYPE_TITLE;
    149 	} else
    150 	if (!strcmp(str, text_types[TEXT_TYPE_SUBTITLE])) {
    151 		return TEXT_TYPE_SUBTITLE;
    152 	} else
    153 	if (!strcmp(str, text_types[TEXT_TYPE_LABEL])) {
    154 		return TEXT_TYPE_LABEL;
    155 	} else
    156 	if (!strcmp(str, text_types[TEXT_TYPE_COMMENT])) {
    157 		return TEXT_TYPE_COMMENT;
    158 	} else
    159 	if (!strcmp(str, text_types[TEXT_TYPE_COMMENT_ITALIC])) {
    160 		return TEXT_TYPE_COMMENT_ITALIC;
    161 	} else
    162 	if (!strcmp(str, text_types[TEXT_TYPE_COMMENT_BOX])) {
    163 		return TEXT_TYPE_COMMENT_BOX;
    164 	}
    165 	*error = true;
    166 	return TEXT_TYPE_CHORD; // unused
    167 }
    168 
    169 static enum NotationSystem
    170 config_notation_system_parse(const char *str)
    171 {
    172 	if (!strcmp(str, notation_systems[NOTATION_SYSTEM_COMMON]) || !strcmp(str, "dutch")) {
    173 		return NOTATION_SYSTEM_COMMON;
    174 	} else if (!strcmp(str, notation_systems[NOTATION_SYSTEM_GERMAN])) {
    175 		return NOTATION_SYSTEM_GERMAN;
    176 	} else if (!strcmp(str, notation_systems[NOTATION_SYSTEM_SCANDINAVIAN])) {
    177 		return NOTATION_SYSTEM_SCANDINAVIAN;
    178 	} else if (!strcmp(str, notation_systems[NOTATION_SYSTEM_LATIN])) {
    179 		return NOTATION_SYSTEM_LATIN;
    180 	} else if (!strcmp(str, notation_systems[NOTATION_SYSTEM_ROMAN])) {
    181 		return NOTATION_SYSTEM_ROMAN;
    182 	} else if (!strcmp(str, notation_systems[NOTATION_SYSTEM_NASHVILLE])) {
    183 		return NOTATION_SYSTEM_NASHVILLE;
    184 	}
    185 	return NOTATION_SYSTEM_CUSTOM;
    186 }
    187 
    188 static const char *
    189 config_notation_system_to_config_string(enum NotationSystem system)
    190 {
    191 	return notation_systems[system];
    192 }
    193 
    194 static enum Instrument
    195 config_instrument_parse(const char *str, bool *error)
    196 {
    197 	*error = false;
    198 	if (!strcmp(str, instruments[INSTRUMENT_GUITAR].name)) {
    199 		return INSTRUMENT_GUITAR;
    200 	} else
    201 	/* if (!strcmp(str, instruments[INSTRUMENT_KEYBOARD].name)) {
    202 		return INSTRUMENT_KEYBOARD;
    203 	} else */
    204 	if (!strcmp(str, instruments[INSTRUMENT_MANDOLIN].name)) {
    205 		return INSTRUMENT_MANDOLIN;
    206 	} else
    207 	if (!strcmp(str, instruments[INSTRUMENT_UKULELE].name)) {
    208 		return INSTRUMENT_UKULELE;
    209 	}
    210 	*error = true;
    211 	return INSTRUMENT_GUITAR;
    212 }
    213 
    214 static const char *
    215 config_instrument_to_config_string(enum Instrument ins)
    216 {
    217 	return instruments[ins].name;
    218 }
    219 
    220 struct InstrumentInfo
    221 config_instrument_get(enum Instrument ins)
    222 {
    223 	return instruments[ins];
    224 }
    225 
    226 static struct Note *
    227 config_note_new(void)
    228 {
    229 	struct Note *note = emalloc(sizeof(struct Note));
    230 	note->note = NULL;
    231 	note->sharp = NULL;
    232 	note->flat = NULL;
    233 	return note;
    234 }
    235 
    236 static void
    237 config_note_free(struct Note *note)
    238 {
    239 	if (!note) {
    240 		return;
    241 	}
    242 	free(note->note);
    243 	free(note->sharp);
    244 	free(note->flat);
    245 	free(note);
    246 }
    247 
    248 static void
    249 config_notes_free(struct Note **notes)
    250 {
    251 	if (!notes) {
    252 		return;
    253 	}
    254 	int i;
    255 	for (i = 0; i<7; i++) {
    256 		config_note_free(notes[i]);
    257 	}
    258 	free(notes);
    259 }
    260 
    261 struct Note *
    262 config_notes_common(void)
    263 {
    264 	struct Note *notes = emalloc(sizeof(notes_common));
    265 	memcpy(notes, notes_common, sizeof(notes_common));
    266 	return notes;
    267 }
    268 
    269 static struct Note **
    270 config_notes_new_default(enum NotationSystem system)
    271 {
    272 	struct Note **notes_default = emalloc(8 * sizeof(struct Note *));
    273 	struct Note *notes;
    274 	switch (system) {
    275 	case NOTATION_SYSTEM_GERMAN:
    276 		notes = (struct Note *)&notes_german;
    277 		break;
    278 	case NOTATION_SYSTEM_SCANDINAVIAN:
    279 		notes = (struct Note *)&notes_scandinavian;
    280 		break;
    281 	case NOTATION_SYSTEM_LATIN:
    282 		notes = (struct Note *)&notes_latin;
    283 		break;
    284 	case NOTATION_SYSTEM_ROMAN:
    285 		notes = (struct Note *)&notes_roman;
    286 		break;
    287 	case NOTATION_SYSTEM_NASHVILLE:
    288 		notes = (struct Note *)&notes_nashville;
    289 		break;
    290 	default:
    291 		notes = (struct Note *)&notes_common;
    292 		break;
    293 	}
    294 	int i;
    295 	for (i = 0; i<7; i++) {
    296 		notes_default[i] = config_note_new();
    297 		if (notes[i].note) {
    298 			notes_default[i]->note = strdup(notes[i].note);
    299 		}
    300 		if (notes[i].sharp) {
    301 			notes_default[i]->sharp = strdup(notes[i].sharp);
    302 		}
    303 		if (notes[i].flat) {
    304 			notes_default[i]->flat = strdup(notes[i].flat);
    305 		}
    306 	}
    307 	return notes_default;
    308 }
    309 
    310 static struct Note **
    311 config_notes_load(
    312 	struct ConfigContext *ctx,
    313 	toml_table_t *notes,
    314 	const char *system
    315 )
    316 {
    317 	struct Note **custom_notes;
    318 	toml_table_t *note;
    319 	toml_value_t value;
    320 	toml_array_t *arr;
    321 	int arr_len, k;
    322 	int i = 0;
    323 
    324 	custom_notes = emalloc(8 * sizeof(struct Note *));
    325 	arr = toml_table_array(notes, system);
    326 	if (!arr) {
    327 		goto ERR;
    328 	}
    329 	arr_len = toml_array_len(arr);
    330 	if (arr_len != 7) {
    331 		config_log(ctx, LOG_ERR, "[notation_systems]", "Custom notation system '%s' has to have exactly 7 items. For an example see `lorid --print-default-config`.", system);
    332 		goto ERR;
    333 	}
    334 	for (i = 0; i<arr_len; i++) {
    335 		note = toml_array_table(arr, i);
    336 		custom_notes[i] = config_note_new();
    337 		value = toml_table_string(note, "note");
    338 		if (value.ok) {
    339 			custom_notes[i]->note = value.u.s;
    340 		}
    341 		value = toml_table_string(note, "sharp");
    342 		if (value.ok) {
    343 			custom_notes[i]->sharp = value.u.s;
    344 			if (i == 2 || i == 6) {
    345 				config_log(ctx, LOG_ERR, "[notation_systems]", "Custom notation system '%s' can't have sharp value at array index '%d'.", system, i);
    346 				goto ERR;
    347 			}
    348 		}
    349 		value = toml_table_string(note, "flat");
    350 		if (value.ok) {
    351 			custom_notes[i]->flat = value.u.s;
    352 			if (i == 0 || i == 3) {
    353 				config_log(ctx, LOG_ERR, "[notation_systems]", "Custom notation system '%s' can't have flat value at array index '%d'.", system, i);
    354 				goto ERR;
    355 			}
    356 		}
    357 	}
    358 	custom_notes[7] = NULL;
    359 	return custom_notes;
    360 	ERR:
    361 	for (k = 0; k<=i; k++) {
    362 		config_note_free(custom_notes[k]);
    363 	}
    364 	free(custom_notes);
    365 	return NULL;
    366 }
    367 
    368 static void
    369 config_notes_print_as_toml(enum NotationSystem system)
    370 {
    371 	struct Note *notes;
    372 	int i;
    373 
    374 	switch (system) {
    375 	case NOTATION_SYSTEM_COMMON:
    376 		notes = (struct Note *)&notes_common;
    377 		break;
    378 	case NOTATION_SYSTEM_GERMAN:
    379 		notes = (struct Note *)&notes_german;
    380 		break;
    381 	case NOTATION_SYSTEM_SCANDINAVIAN:
    382 		notes = (struct Note *)&notes_scandinavian;
    383 		break;
    384 	case NOTATION_SYSTEM_LATIN:
    385 		notes = (struct Note *)&notes_latin;
    386 		break;
    387 	case NOTATION_SYSTEM_ROMAN:
    388 		notes = (struct Note *)&notes_roman;
    389 		break;
    390 	case NOTATION_SYSTEM_NASHVILLE:
    391 		notes = (struct Note *)&notes_nashville;
    392 		break;
    393 	case NOTATION_SYSTEM_CUSTOM:
    394 		return;
    395 	}
    396 	printf("%s = [\n", config_notation_system_to_config_string(system));
    397 	for (i = 0; i<7; i++) {
    398 		printf("\t{ note = \"%s\",", notes[i].note);
    399 		if (notes[i].sharp) {
    400 			printf(" sharp = \"%s\",", notes[i].sharp);
    401 		}
    402 		if (notes[i].flat) {
    403 			printf(" flat = \"%s\"", notes[i].flat);
    404 		}
    405 		printf(" },\n");
    406 	}
    407 	printf("]\n\n");
    408 }
    409 
    410 static const char *
    411 config_parse_mode_to_config_string(enum ParseMode mode)
    412 {
    413 	return parse_modes[mode];
    414 }
    415 
    416 static enum Alignment
    417 config_alignment_parse(const char *str, bool *error)
    418 {
    419 	*error = false;
    420 	if (!strcmp(str, alignments[ALIGNMENT_LEFT])) {
    421 		return ALIGNMENT_LEFT;
    422 	} else
    423 	if (!strcmp(str, alignments[ALIGNMENT_CENTER])) {
    424 		return ALIGNMENT_CENTER;
    425 	} else
    426 	if (!strcmp(str, alignments[ALIGNMENT_RIGHT])) {
    427 		return ALIGNMENT_RIGHT;
    428 	}
    429 	*error = true;
    430 	return ALIGNMENT_LEFT; // unused
    431 }
    432 
    433 static const char *
    434 config_alignment_to_config_string(enum Alignment align)
    435 {
    436 	return alignments[align];
    437 }
    438 
    439 void
    440 config_print_default(void)
    441 {
    442 	struct Config *config = config_load_default();
    443 	int i;
    444 
    445 	printf("metadata_separator = \"%s\"\n\n", config->metadata_separator);
    446 	printf("[notation_systems]\n");
    447 	config_notes_print_as_toml(NOTATION_SYSTEM_COMMON);
    448 	config_notes_print_as_toml(NOTATION_SYSTEM_GERMAN);
    449 	config_notes_print_as_toml(NOTATION_SYSTEM_SCANDINAVIAN);
    450 	config_notes_print_as_toml(NOTATION_SYSTEM_LATIN);
    451 	config_notes_print_as_toml(NOTATION_SYSTEM_ROMAN);
    452 	config_notes_print_as_toml(NOTATION_SYSTEM_NASHVILLE);
    453 
    454 	printf("[parser]\n");
    455 	printf("[parser.chords]\n");
    456 	printf("mode = \"%s\"\n", config_parse_mode_to_config_string(config->parser->chords->mode));
    457 	printf("notation_system = \"%s\"\n\n", config_notation_system_to_config_string(config->parser->chords->notation_system));
    458 
    459 	printf("[output]\n");
    460 	printf("notation_system = \"%s\"\n", config_notation_system_to_config_string(config->output->notation_system));
    461 	printf("start_song_on_new_page = %s\n\n", config->output->start_song_on_new_page ? "true" : "false");
    462 	printf("[output.chorus]\n");
    463 	printf("label = \"Chorus\"\n");
    464 	printf("quote = false\n\n");
    465 	printf("[output.chord_diagram]\n");
    466 	printf("show = %s\n", config->output->diagram->show ? "true" : "false");
    467 	printf("instrument = \"%s\"\n\n", config_instrument_to_config_string(config->output->diagram->instrument));
    468 	printf("[output.page_no]\n");
    469 	printf("show = %s\n", config->output->page_no->show ? "true" : "false");
    470 	printf("alignment = \"%s\"\n\n", config_alignment_to_config_string(config->output->page_no->align));
    471 	printf("[output.toc]\n");
    472 	printf("show = %s\n", config->output->toc->show ? "true" : "false");
    473 	printf("title = \"%s\"\n\n", config->output->toc->title);
    474 	printf("[output.styles]\n");
    475 	for (i = 0; i<TEXT_TYPE_LENGTH; i++) {
    476 		printf("[output.styles.%s]\n\n", text_types[i]);
    477 		cho_style_print_as_toml(config->output->styles[i], text_types[i]);
    478 	}
    479 	config_free(config);
    480 }
    481 
    482 static bool
    483 config_load_font(
    484 	struct Font *font,
    485 	toml_table_t *table,
    486 	struct ChoStylePresence *presence,
    487 	char (*err_buf)[25]
    488 )
    489 {
    490 	enum FontStyle style;
    491 	enum FontWeight weight;
    492 	toml_value_t value;
    493 	bool error;
    494 
    495 	value = toml_table_string(table, "family");
    496 	if (value.ok) {
    497 		presence->font.family = true;
    498 		free(font->family);
    499 		font->family = value.u.s;
    500 	}
    501 	value = toml_table_string(table, "style");
    502 	if (value.ok) {
    503 		presence->font.style = true;
    504 		style = cho_font_style_parse(value.u.s, &error);
    505 		if (error) {
    506 			if (err_buf) {
    507 				strcpy((char *)err_buf, "style value is invalid.");
    508 			}
    509 			free(value.u.s);
    510 			return false;
    511 		} else {
    512 			font->style = style;
    513 		}
    514 		free(value.u.s);
    515 	}
    516 	value = toml_table_string(table, "weight");
    517 	if (value.ok) {
    518 		presence->font.weight = true;
    519 		weight = cho_font_weight_parse(value.u.s, &error);
    520 		if (error) {
    521 			if (err_buf) {
    522 				strcpy((char *)err_buf, "weight value is invalid.");
    523 			}
    524 			free(value.u.s);
    525 			return false;
    526 		} else {
    527 			font->weight = weight;
    528 		}
    529 		free(value.u.s);
    530 	}
    531 	value = toml_table_int(table, "size");
    532 	if (value.ok) {
    533 		presence->font.size = true;
    534 		font->size = value.u.i;
    535 	}
    536 	return true;
    537 }
    538 
    539 static bool
    540 config_load_style(
    541 	struct ChoStyle *style,
    542 	toml_table_t *table,
    543 	struct ChoStylePresence *presence,
    544 	char (*err_buf)[38]
    545 )
    546 {
    547 	bool error;
    548 	struct RGBColor *color;
    549 	enum LineStyle line_style;
    550 	toml_table_t *font_section;
    551 	toml_value_t value;
    552 
    553 	font_section = toml_table_table(table, "font");
    554 	if (font_section) {
    555 		char err[25];
    556 		if (!config_load_font(style->font, font_section, presence, &err)) {
    557 			DEBUG("config_load_font failed.");
    558 			snprintf((char *)err_buf, 38, "font.%s", err);
    559 			return false;
    560 		}
    561 	}
    562 	value = toml_table_string(table, "foreground_color");
    563 	if (value.ok) {
    564 		presence->foreground_color = true;
    565 		color = cho_color_parse(value.u.s);
    566 		if (color) {
    567 			free(style->foreground_color);
    568 			style->foreground_color = color;
    569 		} else {
    570 			strcpy((char *)err_buf, "foreground color value is invalid.");
    571 			free(value.u.s);
    572 			return false;
    573 		}
    574 		free(value.u.s);
    575 	}
    576 	value = toml_table_string(table, "background_color");
    577 	if (value.ok) {
    578 		presence->background_color = true;
    579 		color = cho_color_parse(value.u.s);
    580 		if (color) {
    581 			free(style->background_color);
    582 			style->background_color = color;
    583 		} else {
    584 			strcpy((char *)err_buf, "background color value is invalid.");
    585 			free(value.u.s);
    586 			return false;
    587 		}
    588 		free(value.u.s);
    589 	}
    590 	value = toml_table_string(table, "underline_style");
    591 	if (value.ok) {
    592 		presence->underline_style = true;
    593 		line_style = cho_linestyle_parse(value.u.s, &error);
    594 		if (error) {
    595 			strcpy((char *)err_buf, "underline style value is invalid.");
    596 			free(value.u.s);
    597 			return false;
    598 		} else {
    599 			style->underline_style = line_style;
    600 		}
    601 		free(value.u.s);
    602 	}
    603 	value = toml_table_string(table, "underline_color");
    604 	if (value.ok) {
    605 		presence->underline_color = true;
    606 		color = cho_color_parse(value.u.s);
    607 		if (color) {
    608 			free(style->underline_color);
    609 			style->underline_color = color;
    610 		} else {
    611 			strcpy((char *)err_buf, "underline color value is invalid.");
    612 			free(value.u.s);
    613 			return false;
    614 		}
    615 		free(value.u.s);
    616 	}
    617 	value = toml_table_string(table, "overline_style");
    618 	if (value.ok) {
    619 		presence->overline_style = true;
    620 		line_style = cho_linestyle_parse(value.u.s, &error);
    621 		if (error) {
    622 			strcpy((char *)err_buf, "overline style value is invalid.");
    623 			free(value.u.s);
    624 			return false;
    625 		} else {
    626 			style->overline_style = line_style;
    627 		}
    628 		free(value.u.s);
    629 	}
    630 	value = toml_table_string(table, "overline_color");
    631 	if (value.ok) {
    632 		presence->overline_color = true;
    633 		color = cho_color_parse(value.u.s);
    634 		if (color) {
    635 			free(style->overline_color);
    636 			style->overline_color = color;
    637 		} else {
    638 			strcpy((char *)err_buf, "overline color value is invalid.");
    639 			free(value.u.s);
    640 			return false;
    641 		}
    642 		free(value.u.s);
    643 	}
    644 	value = toml_table_bool(table, "strikethrough");
    645 	if (value.ok) {
    646 		presence->strikethrough = true;
    647 		style->strikethrough = value.u.b;
    648 	}
    649 	value = toml_table_string(table, "strikethrough_color");
    650 	if (value.ok) {
    651 		presence->strikethrough_color = true;
    652 		color = cho_color_parse(value.u.s);
    653 		if (color) {
    654 			free(style->strikethrough_color);
    655 			style->strikethrough_color = color;
    656 		} else {
    657 			strcpy((char *)err_buf, "strikethrough color value is invalid.");
    658 			free(value.u.s);
    659 			return false;
    660 		}
    661 		free(value.u.s);
    662 	}
    663 	value = toml_table_bool(table, "boxed");
    664 	if (value.ok) {
    665 		presence->boxed = true;
    666 		style->boxed = value.u.b;
    667 	}
    668 	value = toml_table_string(table, "boxed_color");
    669 	if (value.ok) {
    670 		presence->boxed_color = true;
    671 		color = cho_color_parse(value.u.s);
    672 		if (color) {
    673 			free(style->boxed_color);
    674 			style->boxed_color = color;
    675 		} else {
    676 			strcpy((char *)err_buf, "boxed color value is invalid.");
    677 			free(value.u.s);
    678 			return false;
    679 		}
    680 		free(value.u.s);
    681 	}
    682 	value = toml_table_double(table, "rise");
    683 	if (value.ok) {
    684 		presence->rise = true;
    685 		style->rise = value.u.d;
    686 	}
    687 	value = toml_table_string(table, "href");
    688 	if (value.ok) {
    689 		presence->href = true;
    690 		style->href = value.u.s;
    691 	}
    692 	return true;
    693 }
    694 
    695 #ifdef ENABLE_DEBUG
    696 
    697 static void
    698 debug_presence_print(const char *name, struct ChoStylePresence *presence)
    699 {
    700 	printf("---- BEGIN PRESENCE ----\n");
    701 	printf("style '%s'\n", name);
    702 	printf("font.family %d\n", presence->font.family);
    703 	printf("font.style %d\n", presence->font.style);
    704 	printf("font.weight %d\n", presence->font.weight);
    705 	printf("font.size %d\n", presence->font.size);
    706 	printf("foreground_color %d\n", presence->foreground_color);
    707 	printf("background_color %d\n", presence->background_color);
    708 	printf("underline_style %d\n", presence->underline_style);
    709 	printf("underline_color %d\n", presence->underline_color);
    710 	printf("overline_style %d\n", presence->overline_style);
    711 	printf("overline_color %d\n", presence->overline_color);
    712 	printf("strikethrough %d\n", presence->strikethrough);
    713 	printf("strikethrough_color %d\n", presence->strikethrough_color);
    714 	printf("boxed %d\n", presence->boxed);
    715 	printf("boxed_color %d\n", presence->boxed_color);
    716 	printf("rise %d\n", presence->rise);
    717 	printf("href %d\n", presence->href);
    718 	printf("---- END PRESENCE ------\n");
    719 }
    720 
    721 #endif /* ENABLE_DEBUG */
    722 
    723 static void
    724 set_text_style(
    725 	struct ChoStylePresence *text_presence,
    726 	struct ChoStyle *text_style,
    727 	struct ChoStylePresence *presence,
    728 	struct ChoStyle *style
    729 )
    730 {
    731 
    732 	if (!presence->font.family && text_presence->font.family) {
    733 		free(style->font->family);
    734 		style->font->family = strdup(text_style->font->family);
    735 	}
    736 	if (!presence->font.style && text_presence->font.style) {
    737 		style->font->style = text_style->font->style;
    738 	}
    739 	if (!presence->font.weight && text_presence->font.weight) {
    740 		style->font->weight = text_style->font->weight;
    741 	}
    742 	if (!presence->font.size && text_presence->font.size) {
    743 		style->font->size = text_style->font->size;
    744 	}
    745 	if (!presence->foreground_color && text_presence->foreground_color) {
    746 		free(style->foreground_color);
    747 		style->foreground_color = cho_color_copy(text_style->foreground_color);
    748 	}
    749 	if (!presence->background_color && text_presence->background_color) {
    750 		free(style->background_color);
    751 		style->background_color = cho_color_copy(text_style->background_color);
    752 	}
    753 	if (!presence->underline_style && text_presence->underline_style) {
    754 		style->underline_style = text_style->underline_style;
    755 	}
    756 	if (!presence->underline_color && text_presence->underline_color) {
    757 		free(style->underline_color);
    758 		style->underline_color = cho_color_copy(text_style->underline_color);
    759 	}
    760 	if (!presence->overline_style && text_presence->overline_style) {
    761 		style->overline_style = text_style->overline_style;
    762 	}
    763 	if (!presence->overline_color && text_presence->overline_color) {
    764 		free(style->overline_color);
    765 		style->overline_color = cho_color_copy(text_style->overline_color);
    766 	}
    767 	if (!presence->strikethrough && text_presence->strikethrough) {
    768 		style->strikethrough = text_style->strikethrough;
    769 	}
    770 	if (!presence->strikethrough_color && text_presence->strikethrough_color) {
    771 		free(style->strikethrough_color);
    772 		style->strikethrough_color = cho_color_copy(text_style->strikethrough_color);
    773 	}
    774 	if (!presence->boxed && text_presence->boxed) {
    775 		style->boxed = text_style->boxed;
    776 	}
    777 	if (!presence->boxed_color && text_presence->boxed_color) {
    778 		free(style->boxed_color);
    779 		style->boxed_color = cho_color_copy(text_style->boxed_color);
    780 	}
    781 	if (!presence->rise && text_presence->rise) {
    782 		style->rise = text_style->rise;
    783 	}
    784 	if (!presence->href && text_presence->href) {
    785 		free(style->href);
    786 		style->href = strdup(text_style->href);
    787 	}
    788 }
    789 
    790 static void
    791 lyrics_set_text_style_as_default(
    792 	struct ChoStylePresence presences[],
    793 	struct ChoStyle **styles
    794 )
    795 {
    796 	struct ChoStyle *style, *text_style;
    797 	struct ChoStylePresence *presence, *text_presence;
    798 	enum TextType lyric_types[] = {
    799 		TEXT_TYPE_CHORUS, TEXT_TYPE_COMMENT,
    800 		TEXT_TYPE_COMMENT_ITALIC, TEXT_TYPE_COMMENT_BOX
    801 	};
    802 	text_presence = &presences[TEXT_TYPE_TEXT];
    803 	text_style = styles[TEXT_TYPE_TEXT];
    804 	size_t i;
    805 	for (i = 0; i<LENGTH(lyric_types); i++) {
    806 		presence = &presences[lyric_types[i]];
    807 		style = styles[lyric_types[i]];
    808 		set_text_style(text_presence, text_style, presence, style);
    809 	}
    810 }
    811 
    812 struct Config *
    813 config_load_default(void)
    814 {
    815 	struct Config *config = emalloc(sizeof(struct Config));
    816 	config->metadata_separator = strdup("; ");
    817 	config->verbose_logging = false;
    818 
    819 	config->parser = emalloc(sizeof(struct ConfigParser));
    820 	config->parser->chords = emalloc(sizeof(struct ConfigChords));
    821 	config->parser->chords->notation_system = NOTATION_SYSTEM_COMMON;
    822 	config->parser->chords->mode = PARSE_MODE_STRICT;
    823 	config->parser->notes = config_notes_new_default(NOTATION_SYSTEM_COMMON);
    824 
    825 	config->output = emalloc(sizeof(struct ConfigOutput));
    826 	config->output->notation_system = NOTATION_SYSTEM_COMMON;
    827 	config->output->start_song_on_new_page = true;
    828 	config->output->notes = config_notes_new_default(NOTATION_SYSTEM_COMMON);
    829 	config->output->chorus = emalloc(sizeof(struct ConfigChorus));
    830 	config->output->chorus->label = strdup("Chorus");
    831 	config->output->chorus->quote = false;
    832 	config->output->diagram = emalloc(sizeof(struct ChordDiagram));
    833 	config->output->diagram->show = true;
    834 	config->output->diagram->instrument = INSTRUMENT_GUITAR;
    835 	config->output->page_no = emalloc(sizeof(struct ConfigPageNo));
    836 	config->output->page_no->show = true;
    837 	config->output->page_no->align = ALIGNMENT_CENTER;
    838 	config->output->toc = emalloc(sizeof(struct ConfigToc));
    839 	config->output->toc->show = false;
    840 	config->output->toc->title = strdup("Table Of Contents");
    841 
    842 	config->output->styles[TEXT_TYPE_CHORD] = cho_style_new();
    843 	config->output->styles[TEXT_TYPE_CHORD]->font->family = strdup(DEFAULT_FONT);
    844 	config->output->styles[TEXT_TYPE_CHORD]->font->weight = FONT_WEIGHT_BOLD;
    845 	config->output->styles[TEXT_TYPE_ANNOT] = cho_style_new();
    846 	config->output->styles[TEXT_TYPE_ANNOT]->font->family = strdup(DEFAULT_FONT);
    847 	config->output->styles[TEXT_TYPE_ANNOT]->font->style = FONT_STYLE_ITALIC;
    848 	config->output->styles[TEXT_TYPE_CHORUS] = cho_style_new();
    849 	config->output->styles[TEXT_TYPE_CHORUS]->font->family = strdup(DEFAULT_FONT);
    850 	config->output->styles[TEXT_TYPE_FOOTER] = cho_style_new();
    851 	config->output->styles[TEXT_TYPE_FOOTER]->font->family = strdup(DEFAULT_FONT);
    852 	config->output->styles[TEXT_TYPE_GRID] = cho_style_new();
    853 	config->output->styles[TEXT_TYPE_GRID]->font->family = strdup(DEFAULT_FONT);
    854 	config->output->styles[TEXT_TYPE_GRID]->font->weight = FONT_WEIGHT_BOLD;
    855 	config->output->styles[TEXT_TYPE_TAB] = cho_style_new();
    856 	config->output->styles[TEXT_TYPE_TAB]->font->family = strdup("Courier");
    857 	// config->output->styles[TEXT_TYPE_TAB]->font->family = FONT_FAMILY_MONOSPACE;
    858 	config->output->styles[TEXT_TYPE_TOC] = cho_style_new();
    859 	config->output->styles[TEXT_TYPE_TOC]->font->family = strdup(DEFAULT_FONT);
    860 	config->output->styles[TEXT_TYPE_TOC]->font->size = 12.0;
    861 	config->output->styles[TEXT_TYPE_TOC_TITLE] = cho_style_new();
    862 	config->output->styles[TEXT_TYPE_TOC_TITLE]->font->family = strdup(DEFAULT_FONT);
    863 	config->output->styles[TEXT_TYPE_TOC_TITLE]->font->weight = FONT_WEIGHT_BOLD;
    864 	config->output->styles[TEXT_TYPE_TOC_TITLE]->font->size = 18.0;
    865 	config->output->styles[TEXT_TYPE_TEXT] = cho_style_new();
    866 	config->output->styles[TEXT_TYPE_TEXT]->font->family = strdup(DEFAULT_FONT);
    867 	config->output->styles[TEXT_TYPE_TITLE] = cho_style_new();
    868 	config->output->styles[TEXT_TYPE_TITLE]->font->family = strdup(DEFAULT_FONT);
    869 	config->output->styles[TEXT_TYPE_TITLE]->font->weight = FONT_WEIGHT_BOLD;
    870 	config->output->styles[TEXT_TYPE_TITLE]->font->size = 18.0;
    871 	config->output->styles[TEXT_TYPE_SUBTITLE] = cho_style_new();
    872 	config->output->styles[TEXT_TYPE_SUBTITLE]->font->family = strdup(DEFAULT_FONT);
    873 	config->output->styles[TEXT_TYPE_SUBTITLE]->font->size = 12.0;
    874 	config->output->styles[TEXT_TYPE_LABEL] = cho_style_new();
    875 	config->output->styles[TEXT_TYPE_LABEL]->font->family = strdup(DEFAULT_FONT);
    876 	config->output->styles[TEXT_TYPE_LABEL]->font->style = FONT_STYLE_ITALIC;
    877 	config->output->styles[TEXT_TYPE_COMMENT] = cho_style_new();
    878 	config->output->styles[TEXT_TYPE_COMMENT]->font->family = strdup(DEFAULT_FONT);
    879 	config->output->styles[TEXT_TYPE_COMMENT]->background_color->red = 228;
    880 	config->output->styles[TEXT_TYPE_COMMENT]->background_color->green = 228;
    881 	config->output->styles[TEXT_TYPE_COMMENT]->background_color->blue = 228;
    882 	config->output->styles[TEXT_TYPE_COMMENT_ITALIC] = cho_style_new();
    883 	config->output->styles[TEXT_TYPE_COMMENT_ITALIC]->font->family = strdup(DEFAULT_FONT);
    884 	config->output->styles[TEXT_TYPE_COMMENT_ITALIC]->font->style = FONT_STYLE_ITALIC;
    885 	config->output->styles[TEXT_TYPE_COMMENT_BOX] = cho_style_new();
    886 	config->output->styles[TEXT_TYPE_COMMENT_BOX]->font->family = strdup(DEFAULT_FONT);
    887 	config->output->styles[TEXT_TYPE_COMMENT_BOX]->boxed = true;
    888 	return config;
    889 }
    890 
    891 static struct Config *
    892 config_load(toml_table_t *toml, const char *filepath)
    893 {
    894 	bool error;
    895 	struct Config *config;
    896 	struct ConfigContext ctx;
    897 	toml_value_t value;
    898 	toml_table_t *output, *parser;
    899 
    900 	ctx.config_filepath = filepath ? strdup(filepath) : NULL;
    901 	config = config_load_default();
    902 	value = toml_table_string(toml, "metadata_separator");
    903 	if (value.ok) {
    904 		free(config->metadata_separator);
    905 		config->metadata_separator = value.u.s;
    906 	}
    907 	value = toml_table_bool(toml, "verbose_logging");
    908 	if (value.ok) {
    909 		config->verbose_logging = value.u.b;
    910 	}
    911 	output = toml_table_table(toml, "output");
    912 	if (output) {
    913 		toml_table_t *styles, *notes, *chorus, *diagram, *toc, *page_no;
    914 		enum NotationSystem notation_system;
    915 		enum Instrument instrument;
    916 		enum Alignment align;
    917 		struct Note **custom_notes;
    918 		chorus = toml_table_table(output, "chorus");
    919 		if (chorus) {
    920 			value = toml_table_string(chorus, "label");
    921 			if (value.ok) {
    922 				free(config->output->chorus->label);
    923 				config->output->chorus->label = value.u.s;
    924 			}
    925 			value = toml_table_bool(chorus, "quote");
    926 			if (value.ok) {
    927 				config->output->chorus->quote = value.u.b;
    928 			}
    929 		}
    930 		toc = toml_table_table(output, "toc");
    931 		if (toc) {
    932 			value = toml_table_bool(toc, "show");
    933 			if (value.ok) {
    934 				config->output->toc->show = value.u.b;
    935 			}
    936 			value = toml_table_string(toc, "title");
    937 			if (value.ok) {
    938 				free(config->output->toc->title);
    939 				config->output->toc->title = value.u.s;
    940 			}
    941 		}
    942 		diagram = toml_table_table(output, "chord_diagram");
    943 		if (diagram) {
    944 			value = toml_table_bool(diagram, "show");
    945 			if (value.ok) {
    946 				config->output->diagram->show = value.u.b;
    947 			}
    948 			value = toml_table_string(diagram, "instrument");
    949 			if (value.ok) {
    950 				instrument = config_instrument_parse(value.u.s, &error);
    951 				if (error) {
    952 					DEBUG("config_instrument_parse failed.");
    953 					config_log(&ctx, LOG_ERR, "[output.chord_diagram]", "Unknown instrument '%s'.", value.u.s);
    954 					free(value.u.s);
    955 					goto ERR;
    956 				}
    957 				config->output->diagram->instrument = instrument;
    958 				free(value.u.s);
    959 			}
    960 		}
    961 		value = toml_table_string(output, "notation_system");
    962 		if (value.ok) {
    963 			notation_system = config_notation_system_parse(value.u.s);
    964 			if (notation_system == NOTATION_SYSTEM_CUSTOM) {
    965 				notes = toml_table_table(toml, "notation_systems");
    966 				if (!notes) {
    967 					config_log(&ctx, LOG_ERR, "[output]", "Custom notation system '%s' has no corresponding definition in [notation_systems]");
    968 					free(value.u.s);
    969 					goto ERR;
    970 				}
    971 				custom_notes = config_notes_load(&ctx, notes, value.u.s);
    972 				if (custom_notes) {
    973 					config_notes_free(config->output->notes);
    974 					config->output->notes = custom_notes;
    975 				} else {
    976 					DEBUG("config_notes_load failed.");
    977 					config_log(&ctx, LOG_ERR, "[output]", "Couldn't load custom notation system '%s' from [notation_systems] section.", value.u.s);
    978 					free(value.u.s);
    979 					goto ERR;
    980 				}
    981 			} else {
    982 				config_notes_free(config->output->notes);
    983 				config->output->notes = config_notes_new_default(notation_system);
    984 			}
    985 			free(value.u.s);
    986 		}
    987 		value = toml_table_bool(output, "start_song_on_new_page");
    988 		if (value.ok) {
    989 			config->output->start_song_on_new_page = value.u.b;
    990 		}
    991 		page_no = toml_table_table(output, "page_no");
    992 		if (page_no) {
    993 			value = toml_table_bool(page_no, "show");
    994 			if (value.ok) {
    995 				config->output->page_no->show = value.u.b;
    996 			}
    997 			value = toml_table_string(page_no, "alignment");
    998 			if (value.ok) {
    999 				align = config_alignment_parse(value.u.s, &error);
   1000 				if (error) {
   1001 					DEBUG("config_alignment_parse failed.");
   1002 					free(value.u.s);
   1003 					goto ERR;
   1004 				}
   1005 				config->output->page_no->align = align;
   1006 				free(value.u.s);
   1007 			}
   1008 		}
   1009 		styles = toml_table_table(output, "styles");
   1010 		if (styles) {
   1011 			int i, unused;
   1012 			const char *key_name;
   1013 			enum TextType ttype;
   1014 			struct ChoStyle *style;
   1015 			struct ChoStylePresence presences[TEXT_TYPE_LENGTH] = {0};
   1016 			toml_table_t *key;
   1017 
   1018 			for (i = 0; i<toml_table_len(styles); i++) {
   1019 				key_name = toml_table_key(styles, i, &unused);
   1020 				ttype = config_text_type_parse(key_name, &error);
   1021 				if (!error) {
   1022 					key = toml_table_table(styles, key_name);
   1023 					if (key) {
   1024 						style = config->output->styles[ttype];
   1025 						size_t size = 16 + strlen(key_name) + 1;
   1026 						char toml_section_name[size];
   1027 						char err[38];
   1028 						snprintf((char *)&toml_section_name, size, "[output.styles.%s]", key_name);
   1029 						if (!config_load_style(style, key, &presences[ttype], &err)) {
   1030 							DEBUG("config_load_style failed.");
   1031 							config_log(&ctx, LOG_ERR, toml_section_name, err);
   1032 							goto ERR;
   1033 						}
   1034 					}
   1035 				}
   1036 			}
   1037 			lyrics_set_text_style_as_default(presences, config->output->styles);
   1038 		}
   1039 	}
   1040 	parser = toml_table_table(toml, "parser");
   1041 	if (parser) {
   1042 		toml_table_t *chords = toml_table_table(parser, "chords");
   1043 		if (chords) {
   1044 			toml_table_t *notes;
   1045 			enum NotationSystem notation_system;
   1046 			struct Note **custom_notes;
   1047 
   1048 			value = toml_table_string(chords, "notation_system");
   1049 			if (value.ok) {
   1050 				notation_system = config_notation_system_parse(value.u.s);
   1051 				if (notation_system == NOTATION_SYSTEM_CUSTOM) {
   1052 					notes = toml_table_table(toml, "notation_systems");
   1053 					if (!notes) {
   1054 						config_log(&ctx, LOG_ERR, "[parser.chords]", "Custom notation system '%s' has no corresponding definition in [notation_systems].", value.u.s);
   1055 						free(value.u.s);
   1056 						goto ERR;
   1057 					}
   1058 					custom_notes = config_notes_load(&ctx, notes, value.u.s);
   1059 					if (custom_notes) {
   1060 						config_notes_free(config->parser->notes);
   1061 						config->parser->notes = custom_notes;
   1062 					} else {
   1063 						DEBUG("config_notes_load failed.");
   1064 						config_log(&ctx, LOG_ERR, "[parser.chords]", "Couldn't load custom notation system '%s' from [notation_systems] section.", value.u.s);
   1065 						free(value.u.s);
   1066 						goto ERR;
   1067 					}
   1068 				} else {
   1069 					config_notes_free(config->parser->notes);
   1070 					config->parser->notes = config_notes_new_default(notation_system);
   1071 				}
   1072 				free(value.u.s);
   1073 			}
   1074 			value = toml_table_string(chords, "mode");
   1075 			if (value.ok) {
   1076 				if (!strcmp(value.u.s, "strict")) {
   1077 					config->parser->chords->mode = PARSE_MODE_STRICT;
   1078 				} else if (!strcmp(value.u.s, "relaxed")) {
   1079 					config->parser->chords->mode = PARSE_MODE_RELAXED;
   1080 				}
   1081 				free(value.u.s);
   1082 			}
   1083 		}
   1084 	}
   1085 	free(ctx.config_filepath);
   1086 	return config;
   1087 	ERR:
   1088 	free(ctx.config_filepath);
   1089 	config_free(config);
   1090 	return NULL;
   1091 }
   1092 
   1093 struct Config *
   1094 config_load_from_file(const char *filepath)
   1095 {
   1096 	struct Config *config = NULL;
   1097 	toml_table_t *table = NULL;
   1098 	FILE *fp = NULL;
   1099 
   1100 	fp = fopen(filepath, "r");
   1101 	if (!fp) {
   1102 		DEBUG("fopen failed.");
   1103 		util_log(NULL, 0, LOG_ERR, "Cannot open file '%s': %s", filepath, strerror(errno));
   1104 		return NULL;
   1105 	}
   1106 	char errbuf[200];
   1107 	table = toml_parse_file(fp, (char *)&errbuf, sizeof(errbuf));
   1108 	if (!table) {
   1109 		DEBUG("toml_parse_file failed.");
   1110 		util_log(NULL, 0, LOG_ERR, "Config file '%s' is not a valid toml file: %s.", filepath, (char *)&errbuf);
   1111 		fclose(fp);
   1112 		return NULL;
   1113 	}
   1114 	config = config_load(table, filepath);
   1115 	if (!config) {
   1116 		DEBUG("config_load failed.");
   1117 		fclose(fp);
   1118 		toml_free(table);
   1119 		return NULL;
   1120 	}
   1121 	toml_free(table);
   1122 	fclose(fp);
   1123 	return config;
   1124 }
   1125 
   1126 struct Config *
   1127 config_load_from_data(const char *data)
   1128 {
   1129 	struct Config *config = NULL;
   1130 	toml_table_t *table = NULL;
   1131 	char errbuf[200];
   1132 
   1133 	table = toml_parse((char *)data, (char *)&errbuf, sizeof(errbuf));
   1134 	if (!table) {
   1135 		DEBUG("toml_parse failed.");
   1136 		util_log(NULL, 0, LOG_ERR, "Config data is not valid toml: %s.", (char *)&errbuf);
   1137 		return NULL;
   1138 	}
   1139 	config = config_load(table, NULL);
   1140 	if (!config) {
   1141 		DEBUG("config_load failed.");
   1142 	}
   1143 	toml_free(table);
   1144 	return config;
   1145 }
   1146 
   1147 void
   1148 config_free(struct Config *config)
   1149 {
   1150 	if (!config) {
   1151 		return;
   1152 	}
   1153 	int i;
   1154 
   1155 	free(config->metadata_separator);
   1156 	free(config->output->toc->title);
   1157 	free(config->output->toc);
   1158 	free(config->output->chorus->label);
   1159 	free(config->output->chorus);
   1160 	for (i = 0; i<TEXT_TYPE_LENGTH; i++) {
   1161 		cho_style_free(config->output->styles[i]);
   1162 	}
   1163 	config_notes_free(config->output->notes);
   1164 	free(config->output->diagram);
   1165 	free(config->output->page_no);
   1166 	free(config->output);
   1167 	free(config->parser->chords);
   1168 	config_notes_free(config->parser->notes);
   1169 	free(config->parser);
   1170 	free(config);
   1171 }