diff options
Diffstat (limited to 'src/directory.c')
-rw-r--r-- | src/directory.c | 236 |
1 files changed, 173 insertions, 63 deletions
diff --git a/src/directory.c b/src/directory.c index fa15d41b1..662b8907f 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,84 +59,167 @@ 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) { return g_basename(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); @@ -146,7 +236,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); @@ -154,41 +244,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; } |