commit d2ed7d2819772fd65b44195bfb5dfe822772610c
parent fae7c6227c961afe8c694c14d7a5511b58eb60bd
Author: nibo <nibo@relim.de>
Date: Sun, 23 Feb 2025 10:49:58 +0100
Imporove metadata substitution parsing
Diffstat:
| M | src/chordpro.c | | | 175 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------- |
1 file changed, 146 insertions(+), 29 deletions(-)
diff --git a/src/chordpro.c b/src/chordpro.c
@@ -1773,12 +1773,59 @@ cho_metadata_load_default(struct ChoContext *ctx)
}
static char *
-cho_metadata_substitution_replace(struct ChoMetadata **metadata, const char *name)
+cho_metadata_substitution_replace(
+ struct ChoContext *ctx,
+ const char *name,
+ const char *value,
+ int index
+)
{
- struct ChoMetadata **m;
- for (m = metadata; *m; m++) {
- if (!strcmp((*m)->name, name)) {
- return strdup((*m)->value);
+ struct ChoMetadata *m;
+ int n = 0;
+ int i;
+ if (index < 0) {
+ if (value[0] != 0) {
+ for (i = ctx->m-1; i >= 0; i--) {
+ m = ctx->songs[ctx->so]->metadata[i];
+ if (!strcmp(m->name, name)) {
+ n--;
+ if (!strcmp(m->value, value) && n == index) {
+ return strdup(m->value);
+ }
+ }
+ }
+ } else {
+ for (i = ctx->m-1; i >= 0; i--) {
+ m = ctx->songs[ctx->so]->metadata[i];
+ if (!strcmp(m->name, name)) {
+ n--;
+ if (n == index) {
+ return strdup(m->value);
+ }
+ }
+ }
+ }
+ } else if (index > 0) {
+ if (value[0] != 0) {
+ for (i = 0; i<ctx->m; i++) {
+ m = ctx->songs[ctx->so]->metadata[i];
+ if (!strcmp(m->name, name)) {
+ n++;
+ if (!strcmp(m->value, value) && n == index) {
+ return strdup(m->value);
+ }
+ }
+ }
+ } else {
+ for (i = 0; i<ctx->m; i++) {
+ m = ctx->songs[ctx->so]->metadata[i];
+ if (!strcmp(m->name, name)) {
+ n++;
+ if (n == index) {
+ return strdup(m->value);
+ }
+ }
+ }
}
}
return NULL;
@@ -1794,10 +1841,24 @@ static char *
cho_metadata_substitution_parse(
struct ChoContext *ctx,
const char *str,
- struct ChoMetadata **metadata,
+ const char *parent_name,
enum State state_before_substitution
)
{
+ int o = 0;
+ char *out = NULL;
+ int len = strlen(str);
+ int i;
+ for (i = 0; i<len; i++) {
+ if (str[i] == '%' && str[i+1] == '{') {
+ i += 2;
+ break;
+ }
+ out = erealloc(out, (o+1) * sizeof(char));
+ out[o] = str[i];
+ o++;
+ }
+
enum MetadataSubstitutionState state = MSS_NAME;
enum AttrValueSyntax avs = -1;
char name[128];
@@ -1813,8 +1874,20 @@ cho_metadata_substitution_parse(
char prev_c = 0;
char c = 0;
int n = 0;
- for (; *str; str++) {
- c = *str;
+ int nested_level = 0;
+ for (; i<len; i++) {
+ c = str[i];
+
+ if (prev_c == '%' && c == '{') {
+ nested_level++;
+ } else if (prev_c != '\\' && c == '}') {
+ if (nested_level == 0) {
+ i++;
+ break;
+ }
+ nested_level--;
+ }
+
switch (state) {
case MSS_NAME: {
if (c == '=') {
@@ -1853,7 +1926,6 @@ cho_metadata_substitution_parse(
break;
}
if (c != '-' && !isdigit(c)) {
- printf("THID: %c", c);
// TODO: Find a better term for 'metadata substitution name index'!
cho_log(ctx, LOG_ERR, "Specify a positive or negative number in metadata substitution name index.");
return NULL;
@@ -1919,7 +1991,7 @@ cho_metadata_substitution_parse(
break;
}
case MSS_TRUE_TEXT: {
- if (prev_c != '\\' && c == '|') {
+ if (prev_c != '\\' && c == '|' && nested_level == 0) {
true_text[n] = 0;
n = 0;
state = MSS_FALSE_TEXT;
@@ -1973,36 +2045,74 @@ cho_metadata_substitution_parse(
case MSS_FALSE_TEXT:
false_text[n] = 0;
break;
+ default:
+ }
+ char *replaced, *substituted, *ch;
+ int index = 1;
+ if (name_index[0] != 0) {
+ index = atoi(name_index);
+ if (index == 0) {
+ LOG_DEBUG("atoi failed.");
+ return NULL;
+ }
}
- char *replaced;
if (name[0] != 0) {
- replaced = cho_metadata_substitution_replace(metadata, name);
+ replaced = cho_metadata_substitution_replace(ctx, name, value, index);
if (replaced) {
if (true_text[0] != 0) {
- // return cho_metadata_substitution_parse();
+ substituted = cho_metadata_substitution_parse(ctx, true_text, name, STATE_METADATA_SUBSTITUTION);
+ for (ch = substituted; *ch; ch++) {
+ out = erealloc(out, (o+1) * sizeof(char));
+ out[o] = *ch;
+ o++;
+ }
+ free(substituted);
} else {
- return replaced;
+ for (ch = replaced; *ch; ch++) {
+ out = erealloc(out, (o+1) * sizeof(char));
+ out[o] = *ch;
+ o++;
+ }
}
+ free(replaced);
} else {
if (false_text[0] != 0) {
- // return cho_metadata_substitution_parse();
+ substituted = cho_metadata_substitution_parse(ctx, false_text, name, STATE_METADATA_SUBSTITUTION);
+ for (ch = substituted; *ch; ch++) {
+ out = erealloc(out, (o+1) * sizeof(char));
+ out[o] = *ch;
+ o++;
+ }
+ free(substituted);
} else {
- return strdup("");
+ cho_log(ctx, LOG_WARN, "There is no metadata item named '%s'.", name);
}
}
+ } else {
+ if (state_before_substitution != STATE_METADATA_SUBSTITUTION) {
+ cho_log(ctx, LOG_ERR, "An empty metadata substitution can only be used inside of another metadata substitution.");
+ return NULL;
+ }
+ replaced = cho_metadata_substitution_replace(ctx, parent_name, "", 1);
+ if (replaced) {
+ for (ch = replaced; *ch; ch++) {
+ out = erealloc(out, (o+1) * sizeof(char));
+ out[o] = *ch;
+ o++;
+ }
+ } else {
+ cho_log(ctx, LOG_WARN, "There is no metadata item named '%s'.", parent_name);
+ }
+ free(replaced);
+ }
+ for (; i<len; i++) {
+ out = erealloc(out, (o+1) * sizeof(char));
+ out[o] = str[i];
+ o++;
}
- /* if (name[0] != 0)
- printf("name '%s' ", name);
- if (name_index[0] != 0)
- printf("name_index '%s' ", name_index);
- if (value[0] != 0)
- printf("value '%s' ", value);
- if (true_text[0] != 0)
- printf("true_text '%s' ", true_text);
- if (false_text[0] != 0)
- printf("false_text '%s' ", false_text);
- printf("\n");
- return ""; */
+ out = erealloc(out, (o+1) * sizeof(char));
+ out[o] = 0;
+ return out;
}
static bool
@@ -5725,6 +5835,10 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c
}
case STATE_MAYBE_METADATA_SUBSTITUTION: {
if (c == '{') {
+ metadata_substitution[ctx.ms] = '%';
+ ctx.ms++;
+ metadata_substitution[ctx.ms] = '{';
+ ctx.ms++;
ctx.state = STATE_METADATA_SUBSTITUTION;
break;
}
@@ -5743,12 +5857,14 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c
case STATE_METADATA_SUBSTITUTION: {
if (prev_c != '\\' && c == '}') {
if (ctx.nested_level == 0) {
+ metadata_substitution[ctx.ms] = '}';
+ ctx.ms++;
metadata_substitution[ctx.ms] = 0;
ctx.ms = 0;
char *substituted = cho_metadata_substitution_parse(
&ctx,
metadata_substitution,
- ctx.songs[ctx.so]->metadata,
+ NULL,
ctx.state_before_metadata_substitution
);
if (!substituted) {
@@ -5765,6 +5881,7 @@ cho_songs_parse(const char *str, const char *chordpro_filepath, struct Config *c
ctx.te++;
}
break;
+ default:
}
free(substituted);
ctx.state = ctx.state_before_metadata_substitution;