]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Implement afs events.
authorAndre Noll <maan@systemlinux.org>
Thu, 18 Oct 2007 20:00:16 +0000 (22:00 +0200)
committerAndre Noll <maan@systemlinux.org>
Thu, 18 Oct 2007 20:00:16 +0000 (22:00 +0200)
afs.c
afs.cmd
afs.h
aft.c
attribute.c
blob.c
error.h
mood.c
osl.c
playlist.c
score.c

diff --git a/afs.c b/afs.c
index 95f6b311bb8597527f894d525f8cdd6c8eed5ca8..c4e8d16968ee2c3e484b49330df493bccc097b10 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -963,3 +963,30 @@ int com_check(int fd, int argc, char * const * const argv)
        }
        return 1;
 }
+
+void afs_event(enum afs_events event, struct para_buffer *pb,
+               void *data)
+{
+       int i, ret;
+
+       for (i = 0; i < NUM_AFS_TABLES; i++) {
+               struct afs_table *t = &afs_tables[i];
+               if (!t->event_handler)
+                       continue;
+               ret = t->event_handler(event, pb, data);
+               if (ret < 0)
+                       PARA_CRIT_LOG("%s\n", PARA_STRERROR(-ret));
+       }
+}
+
+int images_event_handler(__a_unused enum afs_events event,
+       __a_unused  struct para_buffer *pb, __a_unused void *data)
+{
+       return 1;
+}
+
+int lyrics_event_handler(__a_unused enum afs_events event,
+       __a_unused struct para_buffer *pb, __a_unused void *data)
+{
+       return 1;
+}
diff --git a/afs.cmd b/afs.cmd
index 2b031be211cbb14e36476e69c81642dba88ce379..15a30d0b3ec1a519a1969784db6a5d114ed3b2c5 100644 (file)
--- a/afs.cmd
+++ b/afs.cmd
@@ -124,6 +124,12 @@ U: addatt attribute1...
 H: This adds new attributes to the attribute table. At most 64
 H: attributes may be defined.
 ---
+N: mvatt
+P: AFS_READ | AFS_WRITE
+D: Rename an attribute.
+U: mvatt old new
+H: Rename attribute old to new.
+---
 N: check
 P: AFS_READ
 D: Run integrity checks against osl tables.
diff --git a/afs.h b/afs.h
index 1399b2aba2d1509fe45ab10c285ae632156dd2e4..57cecebdf648920dcf14c981a3f437632ebdf654 100644 (file)
--- a/afs.h
+++ b/afs.h
@@ -27,29 +27,60 @@ struct afs_info {
 };
 
 enum afs_events {
+       ATTRIBUTE_ADD,
+       ATTRIBUTE_RENAME,
+       ATTRIBUTE_REMOVE,
        AFSI_CHANGE,
        AFHI_CHANGE,
        AUDIO_FILE_RENAME,
        AUDIO_FILE_ADD,
        AUDIO_FILE_REMOVE,
-       AUDIO_FILE_STREAMED,
-       ATTRIBUTE_ADD,
-       ATTRIBUTE_RENAME,
-       ATTRIBUTE_DELETE,
-       LYRICS_DELETE,
-       IMAGE_DELETE,
+       LYRICS_ADD,
+       LYRICS_REMOVE,
        LYRICS_RENAME,
+       IMAGE_ADD,
+       IMAGE_REMOVE,
        IMAGE_RENAME,
 
 };
 
+struct rmatt_event_data {
+       const char *name;
+       unsigned char bitnum;
+};
+
+
+struct addatt_event_data {
+       const char *name;
+       unsigned char bitnum;
+};
+
+struct afsi_change_event_data {
+       const struct osl_row *aft_row;
+       struct afs_info *old_afsi;
+};
+
+union afs_event_data {
+       struct {
+               const char *name;
+               unsigned char bitnum;
+       } rmatt_event_data;
+       struct osl_row *row;
+       struct {
+               const struct osl_row *row;
+               struct afs_info *old_afsi;
+       } afsi_change;
+
+};
+
 struct afs_table {
        void (*init)(struct afs_table *t);
        const char *name;
        int (*open)(const char *base_dir);
        void (*close)(void);
        int (*create)(const char *);
-       int (*event_handler)(enum afs_events event, struct para_buffer *pb,  void *data);
+       int (*event_handler)(enum afs_events event, struct para_buffer *pb,
+               void *data);
        /* int *(check)() */
 };
 
@@ -97,6 +128,8 @@ struct pattern_match_data {
 /* afs */
 typedef int callback_function(const struct osl_object *, struct osl_object *);
 __noreturn void afs_init(uint32_t cookie, int socket_fd);
+void afs_event(enum afs_events event, struct para_buffer *pb,
+       void *data);
 int send_callback_request(callback_function *f, struct osl_object *query,
        struct osl_object *result);
 int send_standard_callback_request(int argc, char * const * const argv,
@@ -121,7 +154,7 @@ int score_add(const struct osl_row *row, long score);
 int score_update(const struct osl_row *aft_row, long new_score);
 int get_num_admissible_files(unsigned *num);
 int score_delete(const struct osl_row *aft_row);
-int row_belongs_to_score_table(const struct osl_row *aft_row);
+int row_belongs_to_score_table(const struct osl_row *aft_row, unsigned *rank);
 
 /* attribute */
 void attribute_init(struct afs_table *t);
@@ -145,9 +178,7 @@ int aft_check_callback(const struct osl_object *query, struct osl_object *result
 
 /* mood */
 int change_current_mood(char *mood_name);
-int mood_update_audio_file(const struct osl_row *aft_row, struct afs_info *old_afsi);
 int reload_current_mood(void);
-int mood_delete_audio_file(const struct osl_row *aft_row);
 int mood_check_callback(__a_unused const struct osl_object *query,
        struct osl_object *result);
 
@@ -166,8 +197,11 @@ int playlist_check_callback(__a_unused const struct osl_object *query,
        void table_name ## _init(struct afs_table *t); \
        int cmd_prefix ## _get_name_by_id(uint32_t id, char **name); \
        int cmd_prefix ## _get_def_by_id(uint32_t id, struct osl_object *def); \
+       int cmd_prefix ## _get_def_by_name(char *name, struct osl_object *def); \
        int cmd_prefix ## _get_name_and_def_by_row(const struct osl_row *row, \
                char **name, struct osl_object *def); \
+       int table_name ##_event_handler(enum afs_events event, \
+               struct para_buffer *pb, void *data); \
        extern struct osl_table *table_name ## _table;
 
 DECLARE_BLOB_SYMBOLS(lyrics, lyr);
diff --git a/aft.c b/aft.c
index c63ab47106866dd5caa9e99062377c742b999a68..46d6729280cf8b90ce47c5d9194c2cace22c2f81 100644 (file)
--- a/aft.c
+++ b/aft.c
@@ -647,8 +647,11 @@ int open_and_update_audio_file(struct osl_row *aft_row, struct audio_file_data *
        save_afsi(&new_afsi, &afsi_obj); /* in-place update */
        if (afd->current_play_mode == PLAY_MODE_PLAYLIST)
                ret = playlist_update_audio_file(aft_row);
-       else
-               ret = mood_update_audio_file(aft_row, &afd->afsi);
+       else {
+               struct afsi_change_event_data aced = {.aft_row = aft_row,
+                       .old_afsi = &afd->afsi};
+               afs_event(AFSI_CHANGE, NULL, &aced);
+       }
        return ret;
 err:
        free(afd->afhi.chunk_table);
@@ -774,7 +777,6 @@ static int print_list_item(struct ls_data *d, struct ls_options *opts,
                        sprintf(score_buf, "%li ", d->score);
        }
 
-       PARA_NOTICE_LOG("id: %s, %d\n", d->path, afsi->audio_format_id);
        if (opts->mode == LS_MODE_LONG) {
                para_printf(b,
                        "%s"    /* score */
@@ -1096,13 +1098,11 @@ static int com_ls_callback(const struct osl_object *query,
        time_t current_time;
 
 
-       PARA_NOTICE_LOG("%d patterns\n", opts->num_patterns);
        if (opts->num_patterns) {
                opts->patterns = para_malloc(opts->num_patterns * sizeof(char *));
                for (i = 0, p = pattern_start; i < opts->num_patterns; i++) {
                        opts->patterns[i] = p;
                        p += strlen(p) + 1;
-                       PARA_NOTICE_LOG("pattern %d: %s\n", i, opts->patterns[i]);
                }
        } else
                opts->patterns = NULL;
@@ -1114,10 +1114,8 @@ static int com_ls_callback(const struct osl_object *query,
        if (ret < 0)
                goto out;
        ret = opts->num_patterns? -E_NO_MATCH : 0;
-       if (!opts->num_matching_paths) {
-               PARA_NOTICE_LOG("no match, ret: %d\n", ret);
+       if (!opts->num_matching_paths)
                goto out;
-       }
        ret = sort_matching_paths(opts);
        if (ret < 0)
                goto out;
@@ -1137,7 +1135,6 @@ static int com_ls_callback(const struct osl_object *query,
        ret = 1;
 out:
        ls_output->data = b.buf;
-       PARA_NOTICE_LOG("ls_outoute.data: %p\n", ls_output->data);
        ls_output->size = b.size;
        free(opts->data);
        free(opts->data_ptr);
@@ -1388,7 +1385,7 @@ static int com_add_callback(const struct osl_object *query,
 {
        char *buf = query->data, *path;
        struct osl_row *pb, *aft_row;
-       const struct osl_row *hs;
+       struct osl_row *hs;
        struct osl_object objs[NUM_AFT_COLUMNS];
        HASH_TYPE *hash;
        char asc[2 * HASH_SIZE + 1];
@@ -1429,13 +1426,18 @@ static int com_add_callback(const struct osl_object *query,
                        pb = NULL;
                }
                /* file rename, update hs' path */
-               ret = osl_get_object(audio_file_table, hs, AFTCOL_PATH, &obj);
-               if (flags & ADD_FLAG_VERBOSE)
+               if (flags & ADD_FLAG_VERBOSE) {
+                       ret = osl_get_object(audio_file_table, hs,
+                               AFTCOL_PATH, &obj);
+                       if (ret < 0)
+                               goto out;
                        para_printf(&msg, "renamed from %s\n", (char *)obj.data);
+               }
                ret = osl_update_object(audio_file_table, hs, AFTCOL_PATH,
                        &objs[AFTCOL_PATH]);
                if (ret < 0)
                        goto out;
+               afs_event(AUDIO_FILE_RENAME, &msg, hs);
                if (!(flags & ADD_FLAG_FORCE))
                        goto out;
        }
@@ -1466,7 +1468,7 @@ static int com_add_callback(const struct osl_object *query,
                        goto out;
        }
        if (hs || pb) { /* (hs != NULL and pb != NULL) implies hs == pb */
-               const struct osl_row *row = pb? pb : hs;
+               struct osl_row *row = pb? pb : hs;
                /* update afhi and chunk_table */
                if (flags & ADD_FLAG_VERBOSE)
                        para_printf(&msg, "updating afhi and chunk table\n");
@@ -1476,6 +1478,9 @@ static int com_add_callback(const struct osl_object *query,
                        goto out;
                ret = osl_update_object(audio_file_table, row, AFTCOL_CHUNKS,
                        &objs[AFTCOL_CHUNKS]);
+               if (ret < 0)
+                       goto out;
+               afs_event(AFHI_CHANGE, &msg, row);
                goto out;
        }
        /* new entry, use default afsi */
@@ -1728,6 +1733,7 @@ static int touch_audio_file(__a_unused struct osl_table *table,
        struct afs_info old_afsi, new_afsi;
        int ret, no_options = tad->cto->num_played < 0 && tad->cto->last_played < 0 &&
                tad->cto->lyrics_id < 0 && tad->cto->image_id < 0;
+       struct afsi_change_event_data aced;
 
        ret = get_afsi_object_of_row(row, &obj);
        if (ret < 0) {
@@ -1760,9 +1766,9 @@ static int touch_audio_file(__a_unused struct osl_table *table,
                        new_afsi.last_played = tad->cto->last_played;
        }
        save_afsi(&new_afsi, &obj); /* in-place update */
-       ret = mood_update_audio_file(row, &old_afsi);
-       if (ret < 0)
-               para_printf(&tad->pb, "%s: %s\n", name, PARA_STRERROR(-ret));
+       aced.aft_row = row;
+       aced.old_afsi = &old_afsi;
+       afs_event(AFSI_CHANGE, &tad->pb, &aced);
        return 1;
 }
 
@@ -1880,9 +1886,7 @@ static int remove_audio_file(__a_unused struct osl_table *table,
 
        if (crd->flags & RM_FLAG_VERBOSE)
                para_printf(&crd->pb, "removing %s\n", name);
-       ret = mood_delete_audio_file(row);
-       if (ret < 0)
-               para_printf(&crd->pb, "%s: %s\n", name, PARA_STRERROR(-ret));
+       afs_event(AUDIO_FILE_REMOVE, &crd->pb, row);
        ret = osl_del_row(audio_file_table, row);
        if (ret < 0)
                para_printf(&crd->pb, "%s: %s\n", name, PARA_STRERROR(-ret));
@@ -1999,12 +2003,14 @@ static int copy_selector_info(__a_unused struct osl_table *table,
        struct cpsi_action_data *cad = data;
        struct osl_object target_afsi_obj;
        int ret;
-       struct afs_info target_afsi;
+       struct afs_info old_afsi, target_afsi;
+       struct afsi_change_event_data aced;
 
        ret = get_afsi_object_of_row(row, &target_afsi_obj);
        if (ret < 0)
                return ret;
        load_afsi(&target_afsi, &target_afsi_obj);
+       old_afsi = target_afsi;
        if (cad->flags & CPSI_FLAG_COPY_LYRICS_ID)
                target_afsi.lyrics_id = cad->source_afsi.lyrics_id;
        if (cad->flags & CPSI_FLAG_COPY_IMAGE_ID)
@@ -2019,6 +2025,9 @@ static int copy_selector_info(__a_unused struct osl_table *table,
        cad->num_copied++;
        if (cad->flags & CPSI_FLAG_VERBOSE)
                para_printf(&cad->pb, "copied afsi to %s\n", name);
+       aced.aft_row = row;
+       aced.old_afsi = &old_afsi;
+       afs_event(AFSI_CHANGE, &cad->pb, &aced);
        return 1;
 }
 
@@ -2222,10 +2231,45 @@ static int aft_create(const char *dir)
        return osl_create_table(&audio_file_table_desc);
 }
 
+static int clear_attribute(struct osl_row *row, void *data)
+{
+       struct rmatt_event_data *red = data;
+       struct afs_info afsi;
+       struct osl_object obj;
+       int ret = get_afsi_object_of_row(row, &obj);
+       uint64_t mask = ~(1ULL << red->bitnum);
+
+       if (ret < 0)
+               return ret;
+       ret = load_afsi(&afsi, &obj);
+       if (ret < 0)
+               return ret;
+       afsi.attributes &= mask;
+       save_afsi(&afsi, &obj);
+       return 1;
+}
+
+static int aft_event_handler(enum afs_events event, struct para_buffer *pb,
+               void *data)
+{
+       switch(event) {
+       case ATTRIBUTE_REMOVE: {
+               const struct rmatt_event_data *red = data;
+               para_printf(pb, "clearing attribute %s (bit %u) from all "
+                       "entries in the audio file table\n", red->name,
+                       red->bitnum);
+               return audio_file_loop(data, clear_attribute);
+               }
+       default:
+               return 1;
+       }
+}
+
 void aft_init(struct afs_table *t)
 {
        t->name = audio_file_table_desc.name;
        t->open = aft_open;
        t->close = aft_close;
        t->create = aft_create;
+       t->event_handler = aft_event_handler;
 }
index 48f108c7fe8be8a96ac6b4d77b7d7737bb46db1c..b130ab2989a0a3f0e439fe4942d9cdb5387da7a7 100644 (file)
@@ -281,23 +281,6 @@ int com_setatt(__a_unused int fd, int argc, char * const * const argv)
                NULL);
 }
 
-/* TODO: make it faster by only extracting the attribute member from afsi */
-static int logical_and_attribute(struct osl_row *aft_row, void *attribute_ptr)
-{
-       struct afs_info afsi;
-       uint64_t *att = attribute_ptr;
-       struct osl_object obj;
-       int ret = get_afsi_object_of_row(aft_row, &obj);
-       if (ret < 0)
-               return ret;
-       ret = load_afsi(&afsi, &obj);
-       if (ret < 0)
-               return ret;
-       afsi.attributes &= *att;
-       save_afsi(&afsi, &obj);
-       return 1;
-}
-
 static int com_addatt_callback(const struct osl_object *query,
                struct osl_object *result)
 {
@@ -311,6 +294,7 @@ static int com_addatt_callback(const struct osl_object *query,
                struct osl_row *row;
                unsigned char bitnum;
                len = strlen(p);
+               struct addatt_event_data aed;
 
                if (!len || p[len - 1] == '-' || p[len - 1] == '+') {
                        para_printf(&pb, "invalid attribute name: %s\n", p);
@@ -320,14 +304,14 @@ static int com_addatt_callback(const struct osl_object *query,
                objs[ATTCOL_NAME].data = p;
                objs[ATTCOL_NAME].size = len + 1;
                ret = osl_get_row(attribute_table, ATTCOL_NAME,
-                       &objs[ATTCOL_NAME], &row); /* expected to fail */
+                       &objs[ATTCOL_NAME], &row); /* expected to fail FIXME: Use get_attribute_bitnum_by_name() */
                if (ret >= 0) {
                        para_printf(&pb, "attribute %s already exists\n", p);
                        continue;
                }
                if (ret != -E_RB_KEY_NOT_FOUND) /* error */
                        goto out;
-               /* find smallest non-used attribute */
+               /* find smallest non-used attribute FIXME: Use find_greatest_att_bitnum() */
                for (bitnum = 0; bitnum < 64; bitnum++) {
                        objs[ATTCOL_BITNUM].data = &bitnum;
                        ret = osl_get_row(attribute_table, ATTCOL_BITNUM,
@@ -345,6 +329,9 @@ static int com_addatt_callback(const struct osl_object *query,
                ret = osl_add_row(attribute_table, objs);
                if (ret < 0)
                        goto out;
+               aed.name = p;
+               aed.bitnum = bitnum;
+               afs_event(ATTRIBUTE_ADD, &pb, &aed);
                greatest_att_bitnum = PARA_MAX(greatest_att_bitnum, bitnum);
        }
 out:
@@ -375,6 +362,56 @@ int com_addatt(int fd, int argc, char * const * const argv)
        return ret;
 }
 
+static int com_mvatt_callback(const struct osl_object *query,
+               struct osl_object *result)
+{
+       char *old = query->data;
+       size_t size = strlen(old) + 1;
+       char *new = old + size;
+       struct osl_object obj = {.data = old, .size = size};
+       struct osl_row *row;
+       struct para_buffer pb = {.size = 0};
+       int ret;
+
+       ret = osl_get_row(attribute_table, ATTCOL_NAME, &obj, &row);
+       if (ret < 0)
+               goto out;
+       obj.data = new;
+       obj.size = strlen(new) + 1;
+       ret = osl_update_object(attribute_table, row, ATTCOL_NAME, &obj);
+out:
+       if (ret < 0)
+               para_printf(&pb, "%s\n", PARA_STRERROR(-ret));
+       else
+               afs_event(ATTRIBUTE_RENAME, &pb, NULL);
+       if (!pb.buf)
+               return 0;
+       result->data = pb.buf;
+       result->size = pb.size;
+       return 1;
+}
+
+int com_mvatt(int fd, int argc, char * const * const argv)
+{
+       struct osl_object result;
+       int ret;
+
+       if (argc != 3)
+               return -E_ATTR_SYNTAX;
+       ret = send_standard_callback_request(argc - 1, argv + 1, com_mvatt_callback,
+               &result);
+       if (!ret)
+               return 1;
+       if (ret < 0)
+               return ret;
+       if (!result.data || !result.size)
+               return 1;
+       ret =  send_va_buffer(fd, "%s", (char *) result.data);
+       free(result.data);
+       return ret;
+}
+
+
 struct remove_attribute_action_data {
        struct para_buffer pb;
        int num_removed;
@@ -385,10 +422,10 @@ static int remove_attribute(struct osl_table *table, struct osl_row *row,
                const char *name, void *data)
 {
        struct remove_attribute_action_data *raad = data;
-       unsigned char bitnum;
        int ret;
+       struct rmatt_event_data red = {.name = name};
 
-       ret = get_attribute_bitnum_by_name(name, &bitnum);
+       ret = get_attribute_bitnum_by_name(name, &red.bitnum);
        if (ret < 0) {
                para_printf(&raad->pb, "%s: %s\n", name, PARA_STRERROR(-ret));
                return 1;
@@ -398,9 +435,10 @@ static int remove_attribute(struct osl_table *table, struct osl_row *row,
                para_printf(&raad->pb, "%s: %s\n", name, PARA_STRERROR(-ret));
                return 1;
        }
-       para_printf(&raad->pb, "removed %s\n", name);
+       para_printf(&raad->pb, "removed attribute %s\n", name);
        raad->num_removed++;
-       raad->mask_of_removed_atts |= (1 << bitnum);
+       raad->mask_of_removed_atts |= (1 << red.bitnum);
+       afs_event(ATTRIBUTE_REMOVE, &raad->pb, &red);
        return 1;
 }
 
@@ -420,17 +458,7 @@ static int com_rmatt_callback(const struct osl_object *query,
        ret = for_each_matching_row(&pmd);
        if (ret < 0)
                para_printf(&raad.pb, "%s\n", PARA_STRERROR(-ret));
-       if (raad.num_removed) {
-               uint64_t and_mask = ~raad.mask_of_removed_atts;
-               ret = audio_file_loop(&and_mask, logical_and_attribute);
-               if (ret < 0)
-                       para_printf(&raad.pb, "%s\n", PARA_STRERROR(-ret));
-               find_greatest_att_bitnum();
-               ret = reload_current_mood();
-               if (ret < 0)
-                       para_printf(&raad.pb, "%s\n", PARA_STRERROR(-ret));
-       }
-       if (!raad.pb.buf)
+       if (!raad.num_removed)
                para_printf(&raad.pb, "no match -- nothing removed\n");
        result->data = raad.pb.buf;
        result->size = raad.pb.size;
@@ -455,7 +483,7 @@ int com_rmatt(int fd, int argc, char * const * const argv)
 }
 
 /**
- * Return a binary representation of the geiven attribute value.
+ * Return a binary representation of the given attribute value.
  *
  * \param atts Pointer to the attribute value.
  * \param buf Result.
diff --git a/blob.c b/blob.c
index 3c8777449179a8a117f84c2ccca4fc16088bdec7..2abf26283f599327398c0558270e0cb29f4f611a 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -410,6 +410,30 @@ static int blob_get_name_by_id(struct osl_table *table, uint32_t id,
                return blob_get_name_by_id(table_name ## _table, id, name); \
        }
 
+
+static int blob_get_def_by_name(struct osl_table *table, char *name,
+               struct osl_object *def)
+{
+       struct osl_row *row;
+       struct osl_object obj = {.data = name, .size = strlen(name) + 1};
+       int ret;
+
+       def->data = NULL;
+       if (!*name)
+               return 1;
+       ret = osl_get_row(table, BLOBCOL_NAME, &obj, &row);
+       if (ret < 0)
+               return ret;
+       return osl_open_disk_object(table, row, BLOBCOL_DEF, def);
+}
+
+/** Define the \p get_def_by_id function for this blob type. */
+#define DEFINE_GET_DEF_BY_NAME(table_name, cmd_prefix) \
+       int cmd_prefix ## _get_def_by_name(char *name, struct osl_object *def) \
+       { \
+               return blob_get_def_by_name(table_name ## _table, name, def); \
+       }
+
 static int blob_get_def_by_id(struct osl_table *table, uint32_t id,
                struct osl_object *def)
 {
@@ -499,6 +523,7 @@ static int blob_open(struct osl_table **table,
                t->open = table_name ## _open; \
                t->close = table_name ## _close; \
                t->create = table_name ## _create;\
+               t->event_handler = table_name ##_event_handler; \
        }
 
 
@@ -515,6 +540,7 @@ static int blob_open(struct osl_table **table,
        DEFINE_BLOB_COMMAND(mv, table_name, cmd_prefix) \
        DEFINE_GET_NAME_BY_ID(table_name, cmd_prefix); \
        DEFINE_GET_DEF_BY_ID(table_name, cmd_prefix); \
+       DEFINE_GET_DEF_BY_NAME(table_name, cmd_prefix); \
        DEFINE_GET_NAME_AND_DEF_BY_ROW(table_name, cmd_prefix); \
 
 /** \cond doxygen isn't smart enough to recognize these */
diff --git a/error.h b/error.h
index d4bbf85c398094dc0893c329a0026ba767b8f40e..a3f80bb9674f470daef6ef6f3b49dc318984d79c 100644 (file)
--- a/error.h
+++ b/error.h
@@ -107,6 +107,7 @@ extern const char **para_errlist[];
 #define PLAYLIST_ERRORS \
        PARA_ERROR(NO_PLAYLIST, "no valid playlist found"), \
        PARA_ERROR(PLAYLIST_LOADED, ""), /* not really an error */ \
+       PARA_ERROR(PATH_FOUND, ""), /* not really an error */ \
        PARA_ERROR(PLAYLIST_EMPTY, "attempted to load empty playlist"), \
 
 
diff --git a/mood.c b/mood.c
index 64cfe835ece10c98210613f57a1178fceaaee121..a8d87cde122c9bc03d6cab301695fe97bf2896e8 100644 (file)
--- a/mood.c
+++ b/mood.c
@@ -852,13 +852,13 @@ static int delete_from_statistics_and_score_table(const struct osl_row *aft_row)
  *
  * \return Positive on success, negative on errors.
  *
- * \sa score_delete(), mood_update_audio_file().
+ * \sa score_delete().
  */
-int mood_delete_audio_file(const struct osl_row *aft_row)
+static int mood_delete_audio_file(const struct osl_row *aft_row)
 {
        int ret;
 
-       ret = row_belongs_to_score_table(aft_row);
+       ret = row_belongs_to_score_table(aft_row, NULL);
        if (ret < 0)
                return ret;
        if (!ret) /* not admissible, nothing to do */
@@ -877,15 +877,17 @@ int mood_delete_audio_file(const struct osl_row *aft_row)
  *
  * \return Positive on success, negative on errors.
  */
-int mood_update_audio_file(const struct osl_row *aft_row, struct afs_info *old_afsi)
+static int mood_update_audio_file(const struct osl_row *aft_row,
+               struct afs_info *old_afsi)
 {
        long score, percent;
        int ret, is_admissible, was_admissible = 0;
        struct afs_info afsi;
+       unsigned rank;
 
        if (!current_mood)
                return 1; /* nothing to do */
-       ret = row_belongs_to_score_table(aft_row);
+       ret = row_belongs_to_score_table(aft_row, &rank);
        if (ret < 0)
                return ret;
        was_admissible = ret;
@@ -919,7 +921,7 @@ int mood_update_audio_file(const struct osl_row *aft_row, struct afs_info *old_a
                percent = 100;
        else if (percent < 0)
                percent = 0;
-       PARA_DEBUG_LOG("re-inserting at %lu%%\n", percent);
+       PARA_DEBUG_LOG("moving from rank %u to %lu%%\n", rank, percent);
        return score_update(aft_row, percent);
 }
 
@@ -1047,10 +1049,34 @@ int reload_current_mood(void)
 
        if (!current_mood)
                return 1;
-//     score_close(0);
        mood_name = para_strdup(current_mood->name);
        close_current_mood();
        ret = change_current_mood(mood_name);
        free(mood_name);
        return ret;
 }
+
+int moods_event_handler(enum afs_events event, struct para_buffer *pb,
+               void *data)
+{
+       switch(event) {
+       case ATTRIBUTE_ADD:
+       case ATTRIBUTE_REMOVE:
+       case ATTRIBUTE_RENAME:
+               return reload_current_mood();
+       case AFSI_CHANGE: {
+               struct afsi_change_event_data *aced = data;
+               return mood_update_audio_file(aced->aft_row, aced->old_afsi);
+               }
+       case AFHI_CHANGE:
+       case AUDIO_FILE_RENAME:
+       case AUDIO_FILE_ADD:
+               return mood_update_audio_file(data, NULL);
+       case AUDIO_FILE_REMOVE:
+               return mood_delete_audio_file(data);
+       default:
+               return 1;
+       }
+       return 1;
+}
+
diff --git a/osl.c b/osl.c
index 19d7961ecc261dfe497e2a81361ae24ba53eb529..36da808af853aa58495382c2c6fd1afe3893b5e8 100644 (file)
--- a/osl.c
+++ b/osl.c
@@ -366,7 +366,7 @@ static int init_column_descriptions(struct osl_table *t)
        if (ret < 0)
                goto err;
        ret = -E_BAD_DB_DIR;
-       if (!t->desc->dir)
+       if (!t->desc->dir && (t->num_disk_storage_columns || t->num_mapped_columns))
                goto err;
        /* the size of the index header without column descriptions */
        t->index_header_size = IDX_COLUMN_DESCRIPTIONS;
@@ -659,10 +659,7 @@ static int create_table_index(struct osl_table *t)
  *
  * \param desc Pointer to the table description.
  *
- * \return Positive on success, negative on errors. Possible errors include: \p
- * E_BAD_TABLE_DESC, \p E_BAD_DB_DIR, \p E_BAD_NAME, \p E_NO_COMPARE_FUNC, \p
- * E_NO_COLUMN_NAME, \p E_DUPLICATE_COL_NAME, \p E_MKDIR, any errors returned
- * by para_open().
+ * \return Standard.
  */
 int osl_create_table(const struct osl_table_description *desc)
 {
@@ -1203,10 +1200,7 @@ int init_rbtrees(struct osl_table *t)
  * The table description given by \a desc should coincide with the
  * description used at creation time.
  *
- * \return Positive on success, negative on errors. Possible errors include:
- * errors returned by init_table_structure(), \p E_NOENT, \p E_STAT, \p \p
- * E_NOTDIR, \p E_BAD_TABLE_DESC, \p E_BAD_DB_DIR, \p E_NO_COMPARE_FUNC, \p
- * E_NO_COLUMN_NAME, errors returned by init_rbtrees().
+ * \return Standard.
  */
 int osl_open_table(const struct osl_table_description *table_desc,
                struct osl_table **result)
@@ -1836,9 +1830,7 @@ out:
  * mapped columns of constant size (which may be updated directly if \p
  * OSL_RBTREE is not set).  Otherwise the rbtree might become corrupted.
  *
- * \return Positive on success, negative on errors. Possible errors include: \p
- * E_BAD_TABLE, \p E_RB_KEY_EXISTS, \p E_BAD_SIZE, \p E_NOENT, \p E_UNLINK,
- * errors returned by para_write_file(), \p E_MKDIR.
+ * \return Standard
  */
 int osl_update_object(struct osl_table *t, const struct osl_row *r,
                unsigned col_num, struct osl_object *obj)
@@ -2007,7 +1999,7 @@ int osl_get_num_rows(const struct osl_table *t, unsigned *num_rows)
  * \param rank Result pointer.
  *
  * The rank is, by definition, the position of the row in the linear order
- * determined by an inorder tree walk of the rbtree associated with column
+ * determined by an in-order tree walk of the rbtree associated with column
  * number \a col_num of \a table.
  *
  * \return Positive on success, negative on errors.
index ca427bc7379b43d3fac2fb567c17419d836ebc37..56e19fed307459fb7fd487bfa82c02db7c7b2f8b 100644 (file)
@@ -40,7 +40,7 @@ static int add_playlist_entry(char *path, void *data)
        }
        ret = score_add(aft_row, -playlist->length);
        if (ret < 0) {
-               PARA_ERROR_LOG("failed to add %s: %d\n", path, ret);
+               PARA_ERROR_LOG("failed to add %s: %s\n", path, PARA_STRERROR(-ret));
                return ret;
        }
        playlist->length++;
@@ -181,5 +181,73 @@ int playlist_open(char *name)
                PARA_NOTICE_LOG("failed to load playlist %s\n", name);
                return ret;
        }
-       return load_playlist(row, &current_playlist);
+       ret = load_playlist(row, &current_playlist);
+       return (ret == -E_PLAYLIST_LOADED)? 1 : ret;
+}
+
+static int search_path(char *path, void *data)
+{
+       if (strcmp(path, data))
+               return 1;
+       return -E_PATH_FOUND;
+}
+
+static int handle_audio_file_event(enum afs_events event, void *data)
+{
+       int ret, was_admissible = 0, is_admissible;
+       struct osl_object playlist_def;
+       char *new_path;
+       const struct osl_row *row = data;
+
+       if (!current_playlist.name)
+               return 1;
+       if (event == AUDIO_FILE_RENAME) {
+               ret = row_belongs_to_score_table(row, NULL);
+               if (ret < 0)
+                       return ret;
+               was_admissible = ret;
+       }
+       ret = get_audio_file_path_of_row(row, &new_path);
+       if (ret < 0)
+               return ret;
+       ret = pl_get_def_by_name(current_playlist.name, &playlist_def);
+       if (ret < 0)
+               return ret;
+       ret = for_each_line_ro(playlist_def.data, playlist_def.size,
+               search_path, new_path);
+       osl_close_disk_object(&playlist_def);
+       is_admissible = (ret < 0);
+       if (was_admissible && is_admissible)
+               return 1;
+       if (!was_admissible && !is_admissible)
+               return 1;
+       if (was_admissible && !is_admissible) {
+               current_playlist.length--;
+               return score_delete(row);
+       }
+       /* !was_admissible && is_admissible */
+       current_playlist.length++;
+       return score_add(row, 0); /* play it immediately */
+}
+
+int playlists_event_handler(enum afs_events event, struct para_buffer *pb,
+               void *data)
+{
+       int ret;
+
+       switch(event) {
+       case AUDIO_FILE_RENAME:
+       case AUDIO_FILE_ADD:
+               return handle_audio_file_event(event, data);
+       case AUDIO_FILE_REMOVE:
+               ret = row_belongs_to_score_table(data, NULL);
+               if (ret < 0)
+                       return ret;
+               if (!ret)
+                       return 1;
+               current_playlist.length--;
+               return score_delete(data);
+       default:
+               return 1;
+       }
 }
diff --git a/score.c b/score.c
index e233f7352c19af0c6f23a47030d024e334cb0239..abccb28fe5082df36e88a5e3ba6a01888c808cb8 100644 (file)
--- a/score.c
+++ b/score.c
@@ -28,7 +28,7 @@ static int ptr_compare(const struct osl_object *obj1, const struct osl_object *o
  * \param obj2 Pointer to the second score object.
  *
  * This function first compares the score values as usual integers. If they compare as
- * equal, the addresss of \a obj1 and \a obj2 are compared. So this compare function
+ * equal, the address of \a obj1 and \a obj2 are compared. So this compare function
  * returns zero if and only if \a obj1 and \a obj2 point to the same memory area.
  *
  * \sa osl_compare_function.
@@ -45,7 +45,7 @@ static int score_compare(const struct osl_object *obj1, const struct osl_object
 }
 
 /**
- * The score table consists of two colums: The \a aft_row column contains
+ * The score table consists of two columns: The \a aft_row column contains
  * pointers to the rows of the audio file table,  and the score column contains
  * the current score of the audio file associated with that row.
  */
@@ -144,7 +144,7 @@ int score_add(const struct osl_row *aft_row, long score)
 //     PARA_DEBUG_LOG("adding %p\n", *(void **) (score_objs[SCORECOL_AFT_ROW].data));
        ret = osl_add_row(score_table, score_objs);
        if (ret < 0) {
-               PARA_ERROR_LOG("failed to add row: %d\n", ret);
+               PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
                free(score_objs[SCORECOL_AFT_ROW].data);
                free(score_objs[SCORECOL_SCORE].data);
        }
@@ -167,7 +167,7 @@ static int get_nth_score(unsigned n, long *score)
  * \param aft_row Determines the audio file to change.
  * \param percent The position to re-insert the audio file.
  *
- * The percent paramenter must be between \p 0 and 100 and. A value of zero
+ * The percent parameter must be between \p 0 and 100 and. A value of zero
  * means to re-insert the audio file into the score table with a score lower
  * than any other admissible file.
  *
@@ -196,8 +196,7 @@ int score_update(const struct osl_row *aft_row, long percent)
        obj.size = sizeof(long);
        obj.data = para_malloc(obj.size);
        *(long *)obj.data = new_score;
-       PARA_DEBUG_LOG("new score: %ld, position: %u/%u\n", new_score,
-               new_pos, n);
+       PARA_DEBUG_LOG("new score: %ld, rank %u/%u\n", new_score, new_pos, n);
        return osl_update_object(score_table, row, SCORECOL_SCORE, &obj);
 }
 
@@ -315,19 +314,27 @@ int score_delete(const struct osl_row *aft_row)
  * Find out whether an audio file is contained in the score table.
  *
  * \param aft_row The row of the audio file table.
+ * \param rank Result pointer
  *
  * \return Positive, if \a aft_row belongs to the audio file table,
- * zero if not, negative on errors.
+ * zero if not, negative on errors. If \a aft_row was found, and \a rank
+ * is not \p NULL, the rank of \a aft_row is returned in \a rank.
  */
-int row_belongs_to_score_table(const struct osl_row *aft_row)
+int row_belongs_to_score_table(const struct osl_row *aft_row, unsigned *rank)
 {
        struct osl_row *score_row;
        int ret = get_score_row_from_aft_row(aft_row, &score_row);
-       if (ret >= 0)
-               return 1;
+
        if (ret == -E_RB_KEY_NOT_FOUND)
                return 0;
-       return ret;
+       if (ret < 0)
+               return ret;
+       if (!rank)
+               return 1;
+       ret = osl_get_rank(score_table, score_row, SCORECOL_SCORE, rank);
+       if (ret < 0)
+               return ret;
+       return 1;
 }
 
 /* Close the score table. */
@@ -340,26 +347,41 @@ static void score_close(void)
 /**
  * Open the score table.
  *
- * \param dir The database directory.
+ * \param dir Unused.
  *
  * \return The return value of the underlying call to osl_open_table().
- *
- * \sa score_shutdown().
  */
-static int score_open(const char *dir)
+static int score_open(__a_unused const char *dir)
 {
-       score_table_desc.dir = dir;
+       score_table_desc.dir = NULL; /* this table has only volatile columns */
        return osl_open_table(&score_table_desc, &score_table);
 }
 
+static int score_event_handler(enum afs_events event, struct para_buffer *pb,
+               void *data)
+{
+       int ret;
+
+       switch(event) {
+       case ATTRIBUTE_ADD:
+       case ATTRIBUTE_REMOVE:
+       case ATTRIBUTE_RENAME: {
+               score_close();
+               return score_open(NULL);
+               }
+       default: return 1;
+       }
+}
+
 /**
  * Initialize the scoring subsystem.
  *
- * \param t Members Gets filled in by the function.
+ * \param t The members of \t are filled in by the function.
  */
 void score_init(struct afs_table *t)
 {
        t->name = score_table_desc.name;
        t->open = score_open;
        t->close = score_close;
+       t->event_handler = score_event_handler;
 }