lorid

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

commit 9d9c20021fc5f2537a7057cc906a04f6155781e4
parent 6f39268da207807015347f5e5b248e556649dadb
Author: nibo <nibo@relim.de>
Date:   Sun,  9 Jun 2024 19:42:30 +0200

Do further work

Diffstat:
Mchordpro.c | 682++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mchordpro.h | 33+++++++++++++++++++++++++++++++++
Mlorid.c | 1-
3 files changed, 655 insertions(+), 61 deletions(-)

diff --git a/chordpro.c b/chordpro.c @@ -3,6 +3,7 @@ #include <stdbool.h> #include <stdint.h> #include <string.h> +#include <ctype.h> #include "chordpro.h" static const char *environment_directives[] = { @@ -108,6 +109,65 @@ const char *the_pos(enum Position pos) return ""; } +const char *the_font_family(enum FontFamily font_family) +{ + switch (font_family) { + case FF_NORMAL: + return "FF_NORMAL"; + case FF_SANS: + return "FF_SANS"; + case FF_SERIF: + return "FF_SERIF"; + case FF_MONOSPACE: + return "FF_MONOSPACE"; + } + return ""; +} + +const char *the_font_style(enum FontStyle style) +{ + switch (style) { + case FS_NORMAL: + return "FS_NORMAL"; + case FS_OBLIQUE: + return "FS_OBLIQUE"; + case FS_ITALIC: + return "FS_ITALIC"; + } + return ""; +} + +const char *the_font_weight(enum FontWeight weight) +{ + switch (weight) { + case FW_NORMAL: + return "FW_NORMAL"; + case FW_BOLD: + return "FW_BOLD"; + } + return ""; +} + +const char *the_rgb_color(struct RGBColor *color) +{ + static char str[17]; + sprintf((char *)&str, "rgb(%d,%d,%d)", color->red, color->green, color->blue); + return str; +} + +const char *the_line_style(enum LineStyle style) +{ + switch (style) { + case LS_SINGLE: + return "LS_SINGLE"; + case LS_DOUBLE: + return "LS_DOUBLE"; + case LS_NONE: + return "LS_NONE"; + } + return ""; +} + static char *string_remove_leading_whitespace(const char *str) { int i = 0; @@ -130,21 +190,6 @@ static inline bool is_whitespace(char c) return false; } -/* const char *the_style(enum LineStyle style) -{ - switch (style) { - case LS_NORMAL: - return "LS_NORMAL"; - case LS_GREY_BACKGROUND: - return "LS_GREY_BACKGROUND"; - case LS_ITALIC: - return "LS_ITALIC"; - case LS_BOX: - return "LS_BOX"; - } - return ""; -} */ - struct RGBColor *cho_rgbcolor_new(uint8_t red, uint8_t green, uint8_t blue) { struct RGBColor *color = malloc(sizeof(struct RGBColor)); @@ -163,6 +208,125 @@ struct RGBColor *cho_rgbcolor_duplicate(struct RGBColor *color) return copy; } +struct RGBColor *cho_rgbcolor_parse(const char *str) +{ + struct RGBColor *color = malloc(sizeof(struct RGBColor)); + size_t len = strlen(str); + char tmp[3]; + long primary_color; + if (len == 7) { + tmp[2] = 0; + if (str[1] == '0' && str[2] == '0') { + color->red = 0; + } else { + tmp[0] = str[1]; + tmp[1] = str[2]; + primary_color = strtol((char *)&tmp, NULL, 16); + if (primary_color == 0) { + fprintf(stderr, "ERROR: Invalid primary color in rgb color.\n"); + free(color); + return NULL; + } else { + color->red = primary_color; + } + } + if (str[3] == '0' && str[4] == '0') { + color->green = 0; + } else { + tmp[0] = str[3]; + tmp[1] = str[4]; + primary_color = strtol((char *)&tmp, NULL, 16); + if (primary_color == 0) { + fprintf(stderr, "ERROR: Invalid primary color in rgb color.\n"); + free(color); + return NULL; + } else { + color->green = primary_color; + } + } + if (str[5] == '0' && str[6] == '0') { + color->blue = 0; + } else { + tmp[0] = str[5]; + tmp[1] = str[6]; + primary_color = strtol((char *)&tmp, NULL, 16); + if (primary_color == 0) { + fprintf(stderr, "ERROR: Invalid primary color in rgb color.\n"); + free(color); + return NULL; + } else { + color->blue = primary_color; + } + } + } else if (len == 4) { + tmp[1] = 0; + tmp[0] = str[1]; + primary_color = strtol((char *)&tmp, NULL, 16); + } else { + fprintf(stderr, "ERROR: Invalid rgb color.\n"); + free(color); + return NULL; + } + return color; +} + +struct RGBColor *cho_color_parse(const char *str) +{ + struct RGBColor *color = malloc(sizeof(struct RGBColor)); + if (str[0] == '#') { + struct RGBColor *rgb_color = cho_rgbcolor_parse(str); + if (rgb_color != NULL) { + free(color); + color = rgb_color; + } else { + free(color); + fprintf(stderr, "cho_rgbcolor_parse failed.\n"); + return NULL; + } + } else if (strcmp(str, "red") == 0) { + color->red = 255; + color->green = 48; + color->blue = 48; + } else if (strcmp(str, "green") == 0) { + color->red = 193; + color->green = 223; + color->blue = 193; + } else if (strcmp(str, "blue") == 0) { + color->red = 0; + color->green = 0; + color->blue = 255; + } else if (strcmp(str, "yellow") == 0) { + color->red = 255; + color->green = 255; + color->blue = 0; + } else if (strcmp(str, "magenta") == 0) { + color->red = 255; + color->green = 48; + color->blue = 255; + } else if (strcmp(str, "cyan") == 0) { + color->red = 82; + color->green = 255; + color->blue = 255; + } else if (strcmp(str, "white") == 0) { + color->red = 255; + color->green = 255; + color->blue = 255; + } else if (strcmp(str, "grey") == 0) { + color->red = 191; + color->green = 191; + color->blue = 191; + } else if (strcmp(str, "black") == 0) { + color->red = 0; + color->green = 0; + color->blue = 0; + } else { + fprintf(stderr, "INFO: Invalid color value '%s'.\n", str); + free(color); + return NULL; + } + return color; +} + struct Style *cho_style_new(void) { struct Style *style = malloc(sizeof(struct Style)); @@ -202,7 +366,7 @@ void cho_style_free(struct Style *style) struct Style *cho_style_duplicate(struct Style *style) { struct Style *copy = malloc(sizeof(struct Style)); - copy->font = style->font; + copy->font = style->font ? strdup(style->font) : NULL; copy->font_family = style->font_family; copy->font_size = style->font_size; copy->font_style = style->font_style; @@ -218,15 +382,225 @@ struct Style *cho_style_duplicate(struct Style *style) copy->boxed = style->boxed; copy->boxed_color = cho_rgbcolor_duplicate(style->boxed_color); copy->rise = style->rise; - copy->href = style->href; + copy->href = style->href ? strdup(style->href) : NULL; return copy; } -struct Style *cho_style_get(const char *tag_name, void *attrs)// struct Attr **attrs) +struct Style *cho_style_get(const char *tag_name, struct Attr **attrs, struct Style *prev_style) { - struct Style *style = cho_style_new(); + size_t value_len, last_char; + struct RGBColor *rgb_color; + struct Style *style; + if (prev_style) + style = cho_style_duplicate(prev_style); + else + style = cho_style_new(); if (strcmp(tag_name, "span") == 0) { - // check the attrs + int a = 0; + while (attrs[a] != NULL) { + if (strcmp(attrs[a]->name, "font_desc") == 0) { + style->font = strdup(attrs[a]->value); + } else if ( + strcmp(attrs[a]->name, "font_family") == 0 || + strcmp(attrs[a]->name, "face") == 0 + ) { + if (strcmp(attrs[a]->value, "normal") == 0) { + style->font_family = FF_NORMAL; + } else if (strcmp(attrs[a]->value, "sans") == 0) { + style->font_family = FF_SANS; + } else if (strcmp(attrs[a]->value, "serif") == 0) { + style->font_family = FF_SERIF; + } else if (strcmp(attrs[a]->value, "monospace") == 0) { + style->font_family = FF_MONOSPACE; + } else { + fprintf(stderr, "INFO: Invalid value in attribute 'font_family/face'.\n"); + } + } else if (strcmp(attrs[a]->name, "size") == 0) { + value_len = strlen(attrs[a]->value); + last_char = value_len - 1; + if (attrs[a]->value[last_char] == '%') { + if (value_len < 5) { + attrs[a]->value[last_char] = 0; + int percentage = atoi(attrs[a]->value); + if (percentage != 0 && percentage <= 100) { + style->font_size *= percentage / 100.0; + } else { + fprintf(stderr, "INFO: Invalid percentage in attribute 'size'.\n"); + } + } else { + fprintf(stderr, "INFO: Invalid percentage in attribute 'size'.\n"); + } + } else if (isdigit(attrs[a]->value[0]) != 0) { + float size = strtof(attrs[a]->value, NULL); + if (size != 0.0) + style->font_size = size; + else + fprintf(stderr, "INFO: Invalid number in attribute 'size'.\n"); + } else if (strcmp(attrs[a]->value, "xx-small") == 0) { + style->font_size *= 0.8; + style->font_size *= 0.8; + style->font_size *= 0.8; + } else if (strcmp(attrs[a]->value, "x-small") == 0) { + style->font_size *= 0.8; + style->font_size *= 0.8; + } else if (strcmp(attrs[a]->value, "small") == 0) { + style->font_size *= 0.8; + // } else if (strcmp(attrs[a]->value, "medium") == 0) { + } else if (strcmp(attrs[a]->value, "large") == 0) { + style->font_size *= 1.8; + } else if (strcmp(attrs[a]->value, "x-large") == 0) { + style->font_size *= 1.8; + style->font_size *= 1.8; + } else if (strcmp(attrs[a]->value, "xx-large") == 0) { + style->font_size *= 1.8; + style->font_size *= 1.8; + style->font_size *= 1.8; + } else if (strcmp(attrs[a]->value, "larger") == 0) { + style->font_size *= 1.8; + } else if (strcmp(attrs[a]->value, "smaller") == 0) { + style->font_size *= 0.8; + } else { + fprintf(stderr, "INFO: Invalid value '%s' for the attribute 'size'.\n", attrs[a]->value); + } + } else if (strcmp(attrs[a]->name, "style") == 0) { + if (strcmp(attrs[a]->value, "normal") == 0) { + style->font_style = FS_NORMAL; + } else if (strcmp(attrs[a]->value, "oblique") == 0) { + style->font_style = FS_OBLIQUE; + } else if (strcmp(attrs[a]->value, "italic") == 0) { + style->font_style = FS_ITALIC; + } else { + fprintf(stderr, "INFO: Invalid value in attribute 'style'.\n"); + } + } else if (strcmp(attrs[a]->name, "weight") == 0) { + if (strcmp(attrs[a]->value, "normal") == 0) { + style->font_style = FS_NORMAL; + } else if (strcmp(attrs[a]->value, "oblique") == 0) { + style->font_style = FS_OBLIQUE; + } else { + fprintf(stderr, "INFO: Invalid value in attribute 'weight'.\n"); + } + } else if (strcmp(attrs[a]->name, "foreground") == 0) { + rgb_color = cho_color_parse(attrs[a]->value); + if (rgb_color == NULL) { + fprintf(stderr, "cho_color_parse failed.\n"); + } else { + free(style->foreground_color); + style->foreground_color = rgb_color; + } + } else if (strcmp(attrs[a]->name, "background") == 0) { + rgb_color = cho_color_parse(attrs[a]->value); + if (rgb_color == NULL) { + fprintf(stderr, "cho_color_parse failed.\n"); + } else { + free(style->background_color); + style->background_color = rgb_color; + } + } else if (strcmp(attrs[a]->name, "underline") == 0) { + if (strcmp(attrs[a]->value, "single") == 0) { + style->underline_style = LS_SINGLE; + } else if (strcmp(attrs[a]->value, "double") == 0) { + style->underline_style = LS_DOUBLE; + } else if (strcmp(attrs[a]->value, "none") == 0) { + style->underline_style = LS_NONE; + } else { + fprintf(stderr, "INFO: Invalid value in attribute 'underline'.\n"); + } + } else if (strcmp(attrs[a]->name, "underline_colour") == 0) { + rgb_color = cho_color_parse(attrs[a]->value); + if (rgb_color == NULL) { + fprintf(stderr, "cho_color_parse failed.\n"); + } else { + free(style->underline_color); + style->underline_color = rgb_color; + } + } else if (strcmp(attrs[a]->name, "overline") == 0) { + if (strcmp(attrs[a]->value, "single") == 0) { + style->overline_style = LS_SINGLE; + } else if (strcmp(attrs[a]->value, "double") == 0) { + style->overline_style = LS_DOUBLE; + } else if (strcmp(attrs[a]->value, "none") == 0) { + style->overline_style = LS_NONE; + } else { + fprintf(stderr, "INFO: Invalid value in attribute 'overline'.\n"); + } + } else if (strcmp(attrs[a]->name, "overline_colour") == 0) { + rgb_color = cho_color_parse(attrs[a]->value); + if (rgb_color == NULL) { + fprintf(stderr, "cho_color_parse failed.\n"); + } else { + free(style->overline_color); + style->overline_color = rgb_color; + } + } else if (strcmp(attrs[a]->name, "rise") == 0) { + value_len = strlen(attrs[a]->value); + last_char = value_len - 1; + if (attrs[a]->value[0] == '-') { + if (attrs[a]->value[last_char] == '%') { + if (value_len < 6) { + attrs[a]->value[last_char] = 0; + int percentage = atoi(&attrs[a]->value[1]); + if (percentage != 0 && percentage <= 100) { + style->rise = (style->font_size / 2) * percentage / 100.0; + } else { + fprintf(stderr, "INFO: Invalid percentage in attribute 'rise'.\n"); + } + } + } else if (isdigit(attrs[a]->value[1]) != 0) { + float rise = strtof(attrs[a]->value, NULL); + if (rise != 0.0) + style->rise = rise; + else + fprintf(stderr, "INFO: Invalid number in attribute 'rise'.\n"); + } else { + fprintf(stderr, "INFO: Invalid value '%s' for the attribute 'rise'.\n", attrs[a]->value); + } + } else { + if (attrs[a]->value[last_char] == '%') { + if (value_len < 5) { + attrs[a]->value[last_char] = 0; + int percentage = atoi(attrs[a]->value); + if (percentage != 0 && percentage <= 100) { + // TODO: Test if that's right + float more = style->font_size / 2.0 * percentage / 100.0; + style->rise += more; + } else { + fprintf(stderr, "INFO: Invalid percentage in attribute 'rise'.\n"); + } + } + } else if (isdigit(attrs[a]->value[1]) != 0) { + float rise = strtof(attrs[a]->value, NULL); + if (rise != 0.0) + style->rise = rise; + else + fprintf(stderr, "INFO: Invalid number in attribute 'rise'.\n"); + } else { + fprintf(stderr, "INFO: Invalid value '%s' for the attribute 'rise'.\n", attrs[a]->value); + } + } + } else if (strcmp(attrs[a]->name, "strikethrough") == 0) { + if (strcmp(attrs[a]->value, "true") == 0) { + style->strikethrough = true; + } else if (strcmp(attrs[a]->value, "false") == 0) { + style->strikethrough = false; + } else { + fprintf(stderr, "INFO: Invalid value '%s' in attribute 'strikethrough'.\n", attrs[a]->value); + } + } else if (strcmp(attrs[a]->name, "strikethrough_colour") == 0) { + rgb_color = cho_color_parse(attrs[a]->value); + if (rgb_color == NULL) { + fprintf(stderr, "cho_color_parse failed.\n"); + } else { + free(style->strikethrough_color); + style->strikethrough_color = rgb_color; + } + } else if (strcmp(attrs[a]->name, "href") == 0) { + style->href = strdup(attrs[a]->value); + } else { + fprintf(stderr, "INFO: Invalid attribute '%s'.\n", attrs[a]->name); + } + a++; + } } else if (strcmp(tag_name, "b") == 0) { style->font_weight = FW_BOLD; } else if (strcmp(tag_name, "big") == 0) { @@ -241,6 +615,7 @@ struct Style *cho_style_get(const char *tag_name, void *attrs)// struct Attr **a } else if (strcmp(tag_name, "sup") == 0) { style->font_size *= 0.8; float thirty_percent = (style->font_size / 2) * 0.3; + // TODO: Is that right? style->rise = thirty_percent - thirty_percent * 2; } else if (strcmp(tag_name, "small") == 0) { style->font_size *= 0.8; @@ -249,21 +624,99 @@ struct Style *cho_style_get(const char *tag_name, void *attrs)// struct Attr **a } else if (strcmp(tag_name, "u") == 0) { style->underline_style = LS_SINGLE; } else { + fprintf(stderr, "ERROR: Invalid tag name '%s'.\n", tag_name); cho_style_free(style); return NULL; } return style; } +void cho_style_print(struct Style *style) +{ + printf("---- BEGIN STYLE ----\n"); + if (style->font) + printf("font: %s\n", style->font); + else + printf("font: NULL\n"); + printf("font_family: %s\n", the_font_family(style->font_family)); + printf("font_size: %f\n", style->font_size); + printf("font_style: %s\n", the_font_style(style->font_style)); + printf("font_weight: %s\n", the_font_weight(style->font_weight)); + printf("foreground_color: %s\n", the_rgb_color(style->foreground_color)); + printf("background_color: %s\n", the_rgb_color(style->background_color)); + printf("underline_style: %s\n", the_line_style(style->underline_style)); + printf("underline_color: %s\n", the_rgb_color(style->underline_color)); + printf("overline_style: %s\n", the_line_style(style->overline_style)); + printf("overline_color: %s\n", the_rgb_color(style->overline_color)); + printf("strikethrough: %d\n", style->strikethrough); + printf("strikethrough_color: %s\n", the_rgb_color(style->strikethrough_color)); + printf("boxed: %d\n", style->boxed); + printf("boxed_color: %s\n", the_rgb_color(style->boxed_color)); + printf("rise: %f\n", style->rise); + if (style->href) + printf("href: %s\n", style->href); + else + printf("href: NULL\n"); + printf("---- END STYLE ------\n"); +} + +struct Attr *cho_tag_attr_new(void) +{ + struct Attr *attr = malloc(sizeof(struct Attr)); + attr->name = NULL; + attr->value = NULL; + return attr; +} + +void cho_tag_attr_free(struct Attr *attr) +{ + free(attr->name); + free(attr->value); + free(attr); +} + +void cho_tag_attrs_free(struct Attr **attrs) +{ + int a = 0; + while (attrs[a] != NULL) { + cho_tag_attr_free(attrs[a]); + a++; + } + free(attrs); +} + struct Tag *cho_tag_new(void) { struct Tag *tag = malloc(sizeof(struct Tag)); tag->name = NULL; tag->style = NULL; + tag->attrs = NULL; tag->is_closed = false; return tag; } +void cho_tag_free(struct Tag *tag) +{ + free(tag->name); + cho_style_free(tag->style); + if (tag->attrs) + cho_tag_attrs_free(tag->attrs); + free(tag); +} + +void cho_tag_close_last_unclosed(const char *tag_name, struct Tag **tags, int last_index) +{ + int i = last_index; + while (i >= 0) { + if (strcmp(tags[i]->name, tag_name) == 0) { + tags[i]->is_closed = true; + return; + } + i--; + } + fprintf(stderr, "INFO: Didn't find a start tag for the end tag '%s'.\n", tag_name); +} + void cho_directive_free(struct ChoDirective *directive) { cho_style_free(directive->style); @@ -521,7 +974,6 @@ struct ChoDirective *directive_parse(const char *name) strcmp(formatting_directives[1], name) == 0 || strcmp(formatting_directives[2], name) == 0 ) { - // directive->style = LS_GREY_BACKGROUND; directive->style->background_color = cho_rgbcolor_new(228, 228, 228); directive->dtype = DT_FORMATTING; directive->stype = ST_NOTHING; @@ -531,7 +983,6 @@ struct ChoDirective *directive_parse(const char *name) strcmp(formatting_directives[3], name) == 0 || strcmp(formatting_directives[4], name) == 0 ) { - // directive->style = LS_ITALIC; directive->style->font_style = FS_ITALIC; directive->dtype = DT_FORMATTING; directive->stype = ST_NOTHING; @@ -571,10 +1022,9 @@ struct ChoSong **cho_parse(FILE *fp) char chord[15]; char tag_begin[6]; char tag_end[6]; - char attr_name[21]; - char attr_value[URL_MAX_LEN+1]; enum State state = STATE_LYRICS; enum State return_to_state; + unsigned int line_number = 1; int dn = 0; int dv = 0; int ch = 0; @@ -587,9 +1037,13 @@ struct ChoSong **cho_parse(FILE *fp) int so = 0; int se = 0; int li = 0; - int ta = 0; + int ta = -1; int te = 0; + int at = 0; + int atn = 0; + int atv = 0; size_t read; + enum AttrValueSyntax avs = AVS_NO; struct ChoDirective *directive = NULL; struct ChoMetadata *metadata = NULL; struct ChoSong **songs = malloc(sizeof(struct ChoSong *)); @@ -600,9 +1054,9 @@ struct ChoSong **cho_parse(FILE *fp) songs[so]->sections[se]->lines[li] = cho_line_new(); songs[so]->sections[se]->lines[li]->lyrics = malloc(sizeof(struct ChoLineItem *)); songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); - struct Tag **tags = malloc(sizeof(struct Tag *)); - tags[ta] = NULL; - ta++; + struct Tag **tags = NULL; + struct Style *prev_style; + struct Style *tag_style; while (feof(fp) == 0) { read = fread(&buf, 1, 1, fp); if (read == 1) { @@ -621,8 +1075,10 @@ struct ChoSong **cho_parse(FILE *fp) songs[so]->sections[se]->lines[li]->lyrics[ly]->text = realloc(songs[so]->sections[se]->lines[li]->lyrics[ly]->text, (te+1) * sizeof(char)); songs[so]->sections[se]->lines[li]->lyrics[ly]->text[te] = 0; ly++; + songs[so]->sections[se]->lines[li]->lyrics = realloc(songs[so]->sections[se]->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); return_to_state = STATE_LYRICS; - state = STATE_MARKUP_TAG_BEGIN; + state = STATE_MARKUP_TAG; break; } if (buf == '\n') { @@ -706,7 +1162,7 @@ struct ChoSong **cho_parse(FILE *fp) fprintf(stderr, "INFO: Formatting directive '%s' has no value.\n", directive_name); break; case DT_PREAMBLE: - // Only preamble directive is 'new_song' + // The only preamble directive is 'new_song' free(songs[so]->sections[se]->lines[li]); songs[so]->sections[se]->lines[li] = NULL; songs[so]->metadata = realloc(songs[so]->metadata, (m+1) * sizeof(struct ChoMetadata *)); @@ -807,9 +1263,6 @@ struct ChoSong **cho_parse(FILE *fp) } break; case DT_FORMATTING: - // songs[so]->sections[se]->lines[li]->style = directive->style; - // songs[so]->sections[se]->lines[li]->lyrics = string_remove_leading_whitespace(directive_value); - // ly = strlen(songs[so]->sections[se]->lines[li]->lyrics); ly++; songs[so]->sections[se]->lines[li]->lyrics = realloc(songs[so]->sections[se]->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); @@ -854,19 +1307,25 @@ struct ChoSong **cho_parse(FILE *fp) if (buf == '>') { tag_begin[t] = 0; t = 0; - tags = realloc(tags, (ta+1) * sizeof(struct Tag *)); - tags[ta-1] = cho_tag_new(); - tags[ta-1]->name = strdup(tag_begin); - tags[ta-1]->style = cho_style_get(tag_begin, NULL); - tags[ta] = NULL; - ta++; - state = STATE_MARKUP_TAG_INSIDE; + tags[ta]->name = strdup(tag_begin); + tag_style = cho_style_get(tag_begin, NULL, ta == 0 ? NULL : tags[ta-1]->style); + if (tag_style == NULL) { + fprintf(stderr, "cho_style_get failed.\n"); + return NULL; + } + tags[ta]->style = tag_style; + cho_style_free(songs[so]->sections[se]->lines[li]->lyrics[ly]->style); + songs[so]->sections[se]->lines[li]->lyrics[ly]->style = cho_style_duplicate(tag_style); + memset(tag_begin, 0, strlen(tag_begin)); + state = STATE_LYRICS; break; } if (is_whitespace(buf)) { tag_begin[t] = 0; - printf("tag_begin: %s\n", tag_begin); t = 0; + tags[ta]->name = strdup(tag_begin); + tags[ta]->attrs = realloc(tags[ta]->attrs, (at+1) * sizeof(struct Attr *)); + tags[ta]->attrs[at] = cho_tag_attr_new(); state = STATE_MARKUP_ATTR_NAME; break; } @@ -877,25 +1336,23 @@ struct ChoSong **cho_parse(FILE *fp) tag_begin[t] = buf; t++; break; - case STATE_MARKUP_TAG_INSIDE: - if (buf == '<') { - state = STATE_MARKUP_TAG; - break; - } - // append to the cho line item - break; case STATE_MARKUP_TAG: if (buf == '/') { state = STATE_MARKUP_TAG_END; break; } + ta++; + tags = realloc(tags, (ta+1) * sizeof(struct Tag *)); + tags[ta] = cho_tag_new(); + state = STATE_MARKUP_TAG_BEGIN; // If we not use 'goto' we loose the first character of the begin tag name goto MARKUP_TAG_BEGIN; case STATE_MARKUP_TAG_END: if (buf == '>') { tag_end[t] = 0; t = 0; - printf("tag_end: %s\n", tag_end); + cho_tag_close_last_unclosed(tag_end, tags, ta); + memset(tag_end, 0, strlen(tag_end)); state = return_to_state; break; } @@ -908,34 +1365,139 @@ struct ChoSong **cho_parse(FILE *fp) break; case STATE_MARKUP_ATTR_NAME: if (buf == '=') { - attr_name[an] = 0; - an = 0; + tags[ta]->attrs[at]->name = realloc(tags[ta]->attrs[at]->name, (atn+1) * sizeof(char)); + tags[ta]->attrs[at]->name[atn] = 0; + atn = 0; state = STATE_MARKUP_ATTR_VALUE; break; } - if (buf == '>' || is_whitespace(buf)) { - fprintf(stderr, "ERROR: Attribute name '%s' of tag '%s' has no value.\n", attr_name, tag_begin); + if (is_whitespace(buf)) { + if (at == 0) { + if (!tags[ta]->attrs[at]->name) { + break; + } else { + tags[ta]->attrs[at]->name = realloc(tags[ta]->attrs[at]->name, (atn+1) * sizeof(char)); + tags[ta]->attrs[at]->name[atn] = 0; + fprintf(stderr, "' ' ERROR: Attribute name '%s' of tag '%s' has no value.\n", tags[ta]->attrs[at]->name, tag_begin); + return NULL; + } + } + if (tags[ta]->attrs[at-1]->name && tags[ta]->attrs[at-1]->value) { + break; + } + if (!tags[ta]->attrs[at-1]->name && !tags[ta]->attrs[at-1]->value) { + break; + } + tags[ta]->attrs[at]->name = realloc(tags[ta]->attrs[at]->name, (atn+1) * sizeof(char)); + tags[ta]->attrs[at]->name[atn] = 0; + fprintf(stderr, "' ' ERROR: Attribute name '%s' of tag '%s' has no value.\n", tags[ta]->attrs[at]->name, tag_begin); return NULL; } - attr_name[an] = buf; - an++; + if (buf == '>') { + if (tags[ta]->attrs[at-1]->value) { + cho_tag_attr_free(tags[ta]->attrs[at]); + tags[ta]->attrs[at] = NULL; + tag_style = cho_style_get(tag_begin, tags[ta]->attrs, ta == 0 ? NULL : tags[ta-1]->style); + if (tag_style == NULL) { + fprintf(stderr, "cho_style_get failed.\n"); + return NULL; + } + tags[ta]->style = tag_style; + cho_style_free(songs[so]->sections[se]->lines[li]->lyrics[ly]->style); + songs[so]->sections[se]->lines[li]->lyrics[ly]->style = cho_style_duplicate(tag_style); + at = 0; + memset(tag_begin, 0, strlen(tag_begin)); + state = STATE_LYRICS; + break; + } else { + tags[ta]->attrs[at]->name = realloc(tags[ta]->attrs[at]->name, (atn+1) * sizeof(char)); + tags[ta]->attrs[at]->name[atn] = 0; + atn = 0; + fprintf(stderr, "> ERROR: Attribute name '%s' of tag '%s' has no value.\n", tags[ta]->attrs[at]->name, tag_begin); + return NULL; + } + } + tags[ta]->attrs[at]->name = realloc(tags[ta]->attrs[at]->name, (atn+1) * sizeof(char)); + tags[ta]->attrs[at]->name[atn] = buf; + atn++; break; case STATE_MARKUP_ATTR_VALUE: - if (buf == '>') { + if (avs == AVS_NO) { + if (is_whitespace(buf)) { + fprintf(stderr, "ERROR: Whitespace character after equals sign in line '%d' is invalid.\n", line_number); + return NULL; + } + if (buf == '>') { + fprintf(stderr, "ERROR: Attribute name '%s' of tag '%s' in line '%d' has no value.\n", tags[ta]->attrs[at]->name, tag_begin, line_number); + return NULL; + } + if (buf == '\'') { + avs = AVS_APOSTROPHE; + } else if (buf == '"') { + avs = AVS_QUOTATION_MARK; + } else { + avs = AVS_UNQUOTED; + tags[ta]->attrs[at]->value = realloc(tags[ta]->attrs[at]->value, (atv+1) * sizeof(char)); + tags[ta]->attrs[at]->value[atv] = buf; + atv++; + } + break; + } + if (buf == '\n') { + fprintf(stderr, "ERROR: Newline character inside an attribute value in line '%d' is invalid.\n", line_number); + return NULL; + } + if (avs == AVS_UNQUOTED && buf == '>') { + tags[ta]->attrs[at]->value = realloc(tags[ta]->attrs[at]->value, (atv+1) * sizeof(char)); + tags[ta]->attrs[at]->value[atv] = 0; + atv = 0; + at++; + tags[ta]->attrs = realloc(tags[ta]->attrs, (at+1) * sizeof(struct Attr *)); + tags[ta]->attrs[at] = NULL; + tag_style = cho_style_get(tag_begin, tags[ta]->attrs, ta == 0 ? NULL : tags[ta-1]->style); + if (tag_style == NULL) { + fprintf(stderr, "cho_style_get failed.\n"); + return NULL; + } + tags[ta]->style = tag_style; + cho_style_free(songs[so]->sections[se]->lines[li]->lyrics[ly]->style); + songs[so]->sections[se]->lines[li]->lyrics[ly]->style = cho_style_duplicate(tag_style); + at = 0; + avs = AVS_NO; + memset(tag_begin, 0, strlen(tag_begin)); + state = STATE_LYRICS; + break; + } + if ( + (avs == AVS_APOSTROPHE && buf == '\'') || + (avs == AVS_QUOTATION_MARK && buf == '"') || + (avs == AVS_UNQUOTED && (buf == ' ' || buf == '\t')) + ) { + tags[ta]->attrs[at]->value = realloc(tags[ta]->attrs[at]->value, (atv+1) * sizeof(char)); + tags[ta]->attrs[at]->value[atv] = 0; + atv = 0; + at++; + tags[ta]->attrs = realloc(tags[ta]->attrs, (at+1) * sizeof(struct Attr *)); + tags[ta]->attrs[at] = cho_tag_attr_new(); + avs = AVS_NO; + state = STATE_MARKUP_ATTR_NAME; break; } + tags[ta]->attrs[at]->value = realloc(tags[ta]->attrs[at]->value, (atv+1) * sizeof(char)); + tags[ta]->attrs[at]->value[atv] = buf; + atv++; break; } } else if (ferror(fp) != 0) { fprintf(stderr, "fread failed.\n"); return NULL; } + if (buf == '\n') + line_number++; } int e = 0; - while (tags[e] != NULL) { - printf("name: %s\n", tags[e]->name); - free(tags[e]->name); - free(tags[e]); + while (e <= ta) { + cho_tag_free(tags[e]); e++; } free(tags); diff --git a/chordpro.h b/chordpro.h @@ -55,12 +55,45 @@ struct Style { char *href; }; +/* struct StyleHelper { + bool exist_font; + bool exist_font_family; + bool exist_font_size; + bool exist_font_style; + bool exist_font_weight; + bool exist_foreground_color; + bool exist_background_color; + bool exist_underline_style; + bool exist_underline_color; + bool exist_overline_style; + bool exist_overline_color; + bool exist_strikethrough; + bool exist_strikethrough_color; + bool exist_boxed; + bool exist_boxed_color; + bool exist_rise; + bool exist_href; +}; */ + +struct Attr { + char *name; + char *value; +}; + struct Tag { char *name; struct Style *style; + struct Attr **attrs; bool is_closed; }; +enum AttrValueSyntax { + AVS_NO, + AVS_QUOTATION_MARK, + AVS_APOSTROPHE, + AVS_UNQUOTED +}; + enum State { STATE_LYRICS, STATE_DIRECTIVE_NAME, diff --git a/lorid.c b/lorid.c @@ -32,7 +32,6 @@ int main(int argc, char *argv[]) fprintf(stderr, "cho_parse failed.\n"); return 1; } - printf("Something\n"); /*int m = 0;*/ /*printf("---- BEGIN METADATA ----\n");*/ /*while (song->metadata[m] != NULL) {*/