Version 2 moods.
[paraslash.git] / mood.c
diff --git a/mood.c b/mood.c
index 40228be515678effb04587f61d0fbc75acbc44c1..584c46b8fa375f24d2f87492d44f67ac902fa98b 100644 (file)
--- a/mood.c
+++ b/mood.c
 #include "mm.h"
 #include "mood.h"
 
+/*
+ * Mood parser API. It's overkill to have an own header file for
+ * these declarations as they are only needed in this .c file.
+ */
+struct mp_context;
+int mp_init(const char *definition, int nbytes, struct mp_context **result,
+                char **errmsg);
+bool mp_eval_row(const struct osl_row *aft_row, struct mp_context *ctx);
+void mp_shutdown(struct mp_context *ctx);
+
 /**
  * Contains statistical data of the currently admissible audio files.
  *
@@ -73,6 +83,8 @@ struct mood {
        struct list_head deny_list;
        /** The list of mood items of type \p score. */
        struct list_head score_list;
+       /* Only used for version 2 moods. */
+       struct mp_context *parser_context;
 };
 
 /*
@@ -150,6 +162,10 @@ static int row_is_admissible(const struct osl_row *aft_row, struct mood *m,
 
        if (!m)
                return -E_NO_MOOD;
+       if (m->parser_context) {
+               *scorep = 0;
+               return mp_eval_row(aft_row, m->parser_context);
+       }
        ret = get_afsi_of_row(aft_row, &afsi);
        if (ret < 0)
                return ret;
@@ -214,6 +230,7 @@ static void destroy_mood(struct mood *m)
        list_for_each_entry_safe(item, tmp, &m->score_list, mood_item_node)
                cleanup_list_entry(item);
        free(m->name);
+       mp_shutdown(m->parser_context);
        free(m);
 }
 
@@ -367,7 +384,8 @@ out:
        return ret;
 }
 
-static int load_mood(const struct osl_row *mood_row, struct mood **m)
+static int load_mood(const struct osl_row *mood_row, struct mood **m,
+               char **errmsg)
 {
        char *mood_name;
        struct osl_object mood_def;
@@ -383,15 +401,21 @@ static int load_mood(const struct osl_row *mood_row, struct mood **m)
        mlpd.m = alloc_new_mood(mood_name);
        ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
                parse_mood_line, &mlpd);
-       osl_close_disk_object(&mood_def);
        if (ret < 0) {
-               PARA_ERROR_LOG("unable to load mood %s: %s\n", mlpd.m->name,
-                       para_strerror(-ret));
-               destroy_mood(mlpd.m);
-               return ret;
+               PARA_INFO_LOG("opening version 2 mood %s\n", mlpd.m->name);
+               ret = mp_init(mood_def.data, mood_def.size, &mlpd.m->parser_context,
+                       errmsg);
+               if (ret < 0)
+                       destroy_mood(mlpd.m);
+       } else {
+               PARA_WARNING_LOG("loaded version 1 mood %s\n", mlpd.m->name);
+               PARA_WARNING_LOG("please convert to version 2\n");
+               ret = 1;
        }
-       *m = mlpd.m;
-       return 1;
+       osl_close_disk_object(&mood_def);
+       if (ret >= 0)
+               *m = mlpd.m;
+       return ret;
 }
 
 static int check_mood(struct osl_row *mood_row, void *data)
@@ -409,12 +433,24 @@ static int check_mood(struct osl_row *mood_row, void *data)
        }
        if (!*mood_name) /* ignore dummy row */
                goto out;
-       para_printf(pb, "checking mood %s...\n", mood_name);
        ret = for_each_line(FELF_READ_ONLY, mood_def.data, mood_def.size,
                parse_mood_line, &mlpd);
-       if (ret < 0)
-               para_printf(pb, "mood %s: error in line %u: %s\n", mood_name,
-                       mlpd.line_num, para_strerror(-ret));
+       if (ret < 0) {
+               char *errmsg;
+               struct mood *m = alloc_new_mood("check");
+               ret = mp_init(mood_def.data, mood_def.size, &m->parser_context,
+                       &errmsg);
+               if (ret < 0) {
+                       para_printf(pb, "%s: %s\n", mood_name, errmsg);
+                       free(errmsg);
+                       para_printf(pb, "%s\n", para_strerror(-ret));
+               } else
+                       destroy_mood(m);
+       } else {
+               para_printf(pb, "%s: v1 mood, please convert to v2\n",
+                       mood_name);
+
+       }
        ret = 1; /* don't fail the loop on invalid mood definitions */
 out:
        osl_close_disk_object(&mood_def);
@@ -784,18 +820,22 @@ void close_current_mood(void)
  * Change the current mood.
  *
  * \param mood_name The name of the mood to open.
+ * \param errmsg Error description is returned here.
  *
  * If \a mood_name is \a NULL, load the dummy mood that accepts every audio file
  * and uses a scoring method based only on the \a last_played information.
  *
+ * The errmsg pointer may be NULL, in which case no error message will be
+ * returned. If a non-NULL pointer is given, the caller must free *errmsg.
+ *
  * If there is already an open mood, it will be closed first.
  *
  * \return Positive on success, negative on errors. Loading the dummy mood
  * always succeeds.
  *
- * \sa struct \ref afs_info::last_played.
+ * \sa struct \ref afs_info::last_played, \ref mp_eval_row().
  */
-int change_current_mood(const char *mood_name)
+int change_current_mood(const char *mood_name, char **errmsg)
 {
        int i, ret;
        struct admissible_array aa = {
@@ -815,7 +855,7 @@ int change_current_mood(const char *mood_name)
                        PARA_NOTICE_LOG("no such mood: %s\n", mood_name);
                        return ret;
                }
-               ret = load_mood(row, &m);
+               ret = load_mood(row, &m, errmsg);
                if (ret < 0)
                        return ret;
                close_current_mood();
@@ -866,7 +906,7 @@ static int reload_current_mood(void)
        if (current_mood->name)
                mood_name = para_strdup(current_mood->name);
        close_current_mood();
-       ret = change_current_mood(mood_name);
+       ret = change_current_mood(mood_name, NULL);
        free(mood_name);
        return ret;
 }