aboutsummaryrefslogtreecommitdiffstats
path: root/src/directory.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/directory.c')
-rw-r--r--src/directory.c236
1 files changed, 173 insertions, 63 deletions
diff --git a/src/directory.c b/src/directory.c
index 1e3a4cf28..930881129 100644
--- a/src/directory.c
+++ b/src/directory.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,7 +20,12 @@
#include "config.h"
#include "directory.h"
#include "song.h"
+#include "song_sort.h"
+#include "playlist_vector.h"
#include "path.h"
+#include "util/list_sort.h"
+#include "db_visitor.h"
+#include "db_lock.h"
#include <glib.h>
@@ -39,11 +44,13 @@ directory_new(const char *path, struct directory *parent)
directory = g_malloc0(sizeof(*directory) -
sizeof(directory->path) + pathlen + 1);
+ INIT_LIST_HEAD(&directory->children);
+ INIT_LIST_HEAD(&directory->songs);
+ INIT_LIST_HEAD(&directory->playlists);
+
directory->parent = parent;
memcpy(directory->path, path, pathlen + 1);
- playlist_vector_init(&directory->playlists);
-
return directory;
}
@@ -52,19 +59,30 @@ directory_free(struct directory *directory)
{
playlist_vector_deinit(&directory->playlists);
- for (unsigned i = 0; i < directory->songs.nr; ++i)
- song_free(directory->songs.base[i]);
+ struct song *song, *ns;
+ directory_for_each_song_safe(song, ns, directory)
+ song_free(song);
- for (unsigned i = 0; i < directory->children.nr; ++i)
- directory_free(directory->children.base[i]);
+ struct directory *child, *n;
+ directory_for_each_child_safe(child, n, directory)
+ directory_free(child);
- dirvec_destroy(&directory->children);
- songvec_destroy(&directory->songs);
g_free(directory);
/* this resets last dir returned */
/*directory_get_path(NULL); */
}
+void
+directory_delete(struct directory *directory)
+{
+ assert(holding_db_lock());
+ assert(directory != NULL);
+ assert(directory->parent != NULL);
+
+ list_del(&directory->siblings);
+ directory_free(directory);
+}
+
const char *
directory_get_name(const struct directory *directory)
{
@@ -79,65 +97,137 @@ directory_get_name(const struct directory *directory)
: directory->path;
}
+struct directory *
+directory_new_child(struct directory *parent, const char *name_utf8)
+{
+ assert(holding_db_lock());
+ assert(parent != NULL);
+ assert(name_utf8 != NULL);
+ assert(*name_utf8 != 0);
+
+ char *allocated;
+ const char *path_utf8;
+ if (directory_is_root(parent)) {
+ allocated = NULL;
+ path_utf8 = name_utf8;
+ } else {
+ allocated = g_strconcat(directory_get_path(parent),
+ "/", name_utf8, NULL);
+ path_utf8 = allocated;
+ }
+
+ struct directory *directory = directory_new(path_utf8, parent);
+ g_free(allocated);
+
+ list_add_tail(&directory->siblings, &parent->children);
+ return directory;
+}
+
+struct directory *
+directory_get_child(const struct directory *directory, const char *name)
+{
+ assert(holding_db_lock());
+
+ struct directory *child;
+ directory_for_each_child(child, directory)
+ if (strcmp(directory_get_name(child), name) == 0)
+ return child;
+
+ return NULL;
+}
+
void
directory_prune_empty(struct directory *directory)
{
- int i;
- struct dirvec *dv = &directory->children;
-
- for (i = dv->nr; --i >= 0; ) {
- struct directory *child = dv->base[i];
+ assert(holding_db_lock());
+ struct directory *child, *n;
+ directory_for_each_child_safe(child, n, directory) {
directory_prune_empty(child);
- if (directory_is_empty(child)) {
- dirvec_delete(dv, child);
- directory_free(child);
- }
+ if (directory_is_empty(child))
+ directory_delete(child);
}
- if (!dv->nr)
- dirvec_destroy(dv);
}
struct directory *
directory_lookup_directory(struct directory *directory, const char *uri)
{
- struct directory *cur = directory;
- struct directory *found = NULL;
- char *duplicated;
- char *locate;
-
+ assert(holding_db_lock());
assert(uri != NULL);
if (isRootDirectory(uri))
return directory;
- duplicated = g_strdup(uri);
- locate = strchr(duplicated, '/');
+ char *duplicated = g_strdup(uri), *name = duplicated;
+
while (1) {
- if (locate)
- *locate = '\0';
- if (!(found = directory_get_child(cur, duplicated)))
+ char *slash = strchr(name, '/');
+ if (slash == name) {
+ directory = NULL;
break;
- assert(cur == found->parent);
- cur = found;
- if (!locate)
+ }
+
+ if (slash != NULL)
+ *slash = '\0';
+
+ directory = directory_get_child(directory, name);
+ if (directory == NULL || slash == NULL)
break;
- *locate = '/';
- locate = strchr(locate + 1, '/');
+
+ name = slash + 1;
}
g_free(duplicated);
- return found;
+ return directory;
+}
+
+void
+directory_add_song(struct directory *directory, struct song *song)
+{
+ assert(directory != NULL);
+ assert(song != NULL);
+ assert(song->parent == directory);
+
+ list_add_tail(&song->siblings, &directory->songs);
+}
+
+void
+directory_remove_song(G_GNUC_UNUSED struct directory *directory,
+ struct song *song)
+{
+ assert(directory != NULL);
+ assert(song != NULL);
+ assert(song->parent == directory);
+
+ list_del(&song->siblings);
+}
+
+struct song *
+directory_get_song(const struct directory *directory, const char *name_utf8)
+{
+ assert(holding_db_lock());
+ assert(directory != NULL);
+ assert(name_utf8 != NULL);
+
+ struct song *song;
+ directory_for_each_song(song, directory) {
+ assert(song->parent == directory);
+
+ if (strcmp(song->uri, name_utf8) == 0)
+ return song;
+ }
+
+ return NULL;
}
struct song *
directory_lookup_song(struct directory *directory, const char *uri)
{
char *duplicated, *base;
- struct song *song;
+ assert(holding_db_lock());
assert(directory != NULL);
assert(uri != NULL);
@@ -154,7 +244,7 @@ directory_lookup_song(struct directory *directory, const char *uri)
} else
base = duplicated;
- song = songvec_find(&directory->songs, base);
+ struct song *song = directory_get_song(directory, base);
assert(song == NULL || song->parent == directory);
g_free(duplicated);
@@ -162,41 +252,61 @@ directory_lookup_song(struct directory *directory, const char *uri)
}
+static int
+directory_cmp(G_GNUC_UNUSED void *priv,
+ struct list_head *_a, struct list_head *_b)
+{
+ const struct directory *a = (const struct directory *)_a;
+ const struct directory *b = (const struct directory *)_b;
+ return g_utf8_collate(a->path, b->path);
+}
+
void
directory_sort(struct directory *directory)
{
- int i;
- struct dirvec *dv = &directory->children;
+ assert(holding_db_lock());
- dirvec_sort(dv);
- songvec_sort(&directory->songs);
+ list_sort(NULL, &directory->children, directory_cmp);
+ song_list_sort(&directory->songs);
- for (i = dv->nr; --i >= 0; )
- directory_sort(dv->base[i]);
+ struct directory *child;
+ directory_for_each_child(child, directory)
+ directory_sort(child);
}
-int
-directory_walk(struct directory *directory,
- int (*forEachSong)(struct song *, void *),
- int (*forEachDir)(struct directory *, void *),
- void *data)
+bool
+directory_walk(const struct directory *directory, bool recursive,
+ const struct db_visitor *visitor, void *ctx,
+ GError **error_r)
{
- struct dirvec *dv = &directory->children;
- int err = 0;
- size_t j;
-
- if (forEachDir && (err = forEachDir(directory, data)) < 0)
- return err;
+ assert(directory != NULL);
+ assert(visitor != NULL);
+ assert(error_r == NULL || *error_r == NULL);
+
+ if (visitor->song != NULL) {
+ struct song *song;
+ directory_for_each_song(song, directory)
+ if (!visitor->song(song, ctx, error_r))
+ return false;
+ }
- if (forEachSong) {
- err = songvec_for_each(&directory->songs, forEachSong, data);
- if (err < 0)
- return err;
+ if (visitor->playlist != NULL) {
+ struct playlist_metadata *i;
+ directory_for_each_playlist(i, directory)
+ if (!visitor->playlist(i, directory, ctx, error_r))
+ return false;
}
- for (j = 0; err >= 0 && j < dv->nr; ++j)
- err = directory_walk(dv->base[j], forEachSong,
- forEachDir, data);
+ struct directory *child;
+ directory_for_each_child(child, directory) {
+ if (visitor->directory != NULL &&
+ !visitor->directory(child, ctx, error_r))
+ return false;
+
+ if (recursive &&
+ !directory_walk(child, recursive, visitor, ctx, error_r))
+ return false;
+ }
- return err;
+ return true;
}