commit 9d9c20021fc5f2537a7057cc906a04f6155781e4
parent 6f39268da207807015347f5e5b248e556649dadb
Author: nibo <nibo@relim.de>
Date: Sun, 9 Jun 2024 19:42:30 +0200
Do further work
Diffstat:
| M | chordpro.c | | | 682 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- |
| M | chordpro.h | | | 33 | +++++++++++++++++++++++++++++++++ |
| M | lorid.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) {*/