aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2008-10-12 05:28:25 -0700
committerEric Wong <normalperson@yhbt.net>2008-10-12 05:29:27 -0700
commit7278e3b2ee49a853b01dc6532bd7067a264f235d (patch)
tree28e83de310f0c58df226e253a82b0d43ff784497
parentc87ce02575e0a9d5ac3e50938688187ea28ad400 (diff)
parentc7579ca2d8422f0172537e1ca7d1bd46edfc4f9d (diff)
downloadmpd-7278e3b2ee49a853b01dc6532bd7067a264f235d.tar.gz
mpd-7278e3b2ee49a853b01dc6532bd7067a264f235d.tar.xz
mpd-7278e3b2ee49a853b01dc6532bd7067a264f235d.zip
Merge branch 'ew/directory'
* ew/directory: (21 commits) update: fix multiple deletes from *vec iterators directory: children leave parents before being free()ed directory: always maintain sorted properties vectors update: simplify the serialized_delete usage a bit update: remove delete_each_song and clear_directory directory: directory_free kills all that it contains update: serialize directory deletions update: serialize song_free in main thread dirvec: introduce locking for all iterators dirvec: use dirvec_for_each where it makes sense dirvec: add dirvec_for_each iterator songvec: avoid holding nr_lock during free(3) update: allow music_root updates to be queued update: validate in command.c and fix small memory leak directory: rename isRootDirectory => path_is_music_root Avoid calling isRootDirectory when we have a directory object directory: make music_root global and avoid runtime initialization directory: use mpd_sizeof_str_flex_array for path, too tag_item: avoid wasting space when struct is unpackable song: use mpd_sizeof_str_flex_array for song.url ... [ew: fixed up merge errors with myself when isRootDirectory went away]
-rw-r--r--src/command.c14
-rw-r--r--src/database.c30
-rw-r--r--src/dbUtils.c4
-rw-r--r--src/directory.c91
-rw-r--r--src/directory.h15
-rw-r--r--src/directory_print.c22
-rw-r--r--src/directory_save.c4
-rw-r--r--src/dirvec.c67
-rw-r--r--src/dirvec.h5
-rw-r--r--src/gcc.h5
-rw-r--r--src/song.c4
-rw-r--r--src/song.h4
-rw-r--r--src/songvec.c29
-rw-r--r--src/songvec.h2
-rw-r--r--src/tag.h2
-rw-r--r--src/tag_pool.c4
-rw-r--r--src/update.c107
17 files changed, 230 insertions, 179 deletions
diff --git a/src/command.c b/src/command.c
index 518bf5f0d..04e85c6fe 100644
--- a/src/command.c
+++ b/src/command.c
@@ -571,7 +571,7 @@ static int handleLsInfo(int fd, mpd_unused int *permission,
directory_print(fd, dir);
- if (isRootDirectory(path))
+ if (dir == &music_root)
return lsPlaylists(fd, path);
return 0;
@@ -802,9 +802,15 @@ static int handleUpdate(int fd, mpd_unused int *permission,
char *path = NULL;
assert(argc <= 2);
- if (argc == 2 && !(path = sanitizePathDup(argv[1]))) {
- commandError(fd, ACK_ERROR_ARG, "invalid path");
- return -1;
+ if (argc == 2) {
+ if (!(path = sanitizePathDup(argv[1]))) {
+ commandError(fd, ACK_ERROR_ARG, "invalid path");
+ return -1;
+ }
+ if (path_is_music_root(path)) {
+ free(path);
+ path = NULL;
+ }
}
return print_update_result(fd, directory_update_init(path));
}
diff --git a/src/database.c b/src/database.c
index dde57ce6a..733180fea 100644
--- a/src/database.c
+++ b/src/database.c
@@ -32,14 +32,10 @@
#include "os_compat.h"
#include "myfprintf.h"
-static struct directory *music_root;
-
static time_t directory_dbModTime;
void db_init(void)
{
- music_root = directory_new("", NULL);
-
if (!directory_update_init(NULL))
FATAL("directory update failed\n");
@@ -54,22 +50,12 @@ void db_init(void)
void db_finish(void)
{
- directory_free(music_root);
-}
-
-struct directory * db_get_root(void)
-{
- assert(music_root != NULL);
-
- return music_root;
+ directory_free(&music_root);
}
struct directory * db_get_directory(const char *name)
{
- if (name == NULL)
- return music_root;
-
- return directory_get_subdir(music_root, name);
+ return name ? directory_get_subdir(&music_root, name) : &music_root;
}
struct mpd_song *db_get_song(const char *file)
@@ -195,11 +181,7 @@ int db_save(void)
struct stat st;
DEBUG("removing empty directories from DB\n");
- directory_prune_empty(music_root);
-
- DEBUG("sorting DB\n");
-
- directory_sort(music_root);
+ directory_prune_empty(&music_root);
DEBUG("writing DB\n");
@@ -220,7 +202,7 @@ int db_save(void)
DIRECTORY_FS_CHARSET "%s\n"
DIRECTORY_INFO_END "\n", getFsCharset());
- if (directory_save(fd, music_root) < 0) {
+ if (directory_save(fd, &music_root) < 0) {
ERROR("Failed to write to database file: %s\n",
strerror(errno));
xclose(fd);
@@ -243,8 +225,6 @@ int db_load(void)
int foundFsCharset = 0;
int foundVersion = 0;
- if (!music_root)
- music_root = directory_new("", NULL);
while (!(fp = fopen(dbFile, "r")) && errno == EINTR) ;
if (fp == NULL) {
ERROR("unable to open db file \"%s\": %s\n",
@@ -296,7 +276,7 @@ int db_load(void)
DEBUG("reading DB\n");
- directory_load(fp, music_root);
+ directory_load(fp, &music_root);
while (fclose(fp) && errno == EINTR) ;
stats.numberOfSongs = countSongsIn(NULL);
diff --git a/src/dbUtils.c b/src/dbUtils.c
index 1fadb232e..418020d9e 100644
--- a/src/dbUtils.c
+++ b/src/dbUtils.c
@@ -57,7 +57,7 @@ static int countSongsInDirectory(struct directory *dir, void *data)
static int printDirectoryInDirectory(struct directory *dir, void *data)
{
int fd = (int)(size_t)data;
- if (!isRootDirectory(dir->path))
+ if (dir != &music_root)
fdprintf(fd, "directory: %s\n", directory_get_path(dir));
return 0;
}
@@ -339,7 +339,7 @@ static int sumSavedFilenameMemoryInDirectory(struct directory *dir, void *data)
{
int *sum = data;
- if (!isRootDirectory(dir->path))
+ if (dir != &music_root)
return 0;
*sum += (strlen(directory_get_path(dir)) + 1
diff --git a/src/directory.c b/src/directory.c
index b90b477fd..40be57855 100644
--- a/src/directory.c
+++ b/src/directory.c
@@ -23,42 +23,61 @@
#include "myfprintf.h"
#include "dirvec.h"
-struct directory * directory_new(const char *dirname, struct directory * parent)
+struct directory music_root;
+
+struct directory * directory_new(const char *path, struct directory * parent)
{
struct directory *dir;
+ size_t pathlen;
- assert(dirname != NULL);
- assert((*dirname == 0) == (parent == NULL));
+ assert(path);
+ assert(*path);
+ assert(parent);
- dir = xcalloc(1, sizeof(struct directory));
- dir->path = xstrdup(dirname);
+ pathlen = strlen(path);
+ dir = xcalloc(1, sizeof(*dir) - sizeof(dir->path) + pathlen + 1);
+ memcpy(dir->path, path, pathlen + 1);
dir->parent = parent;
return dir;
}
+static int free_each_song(struct mpd_song *song, mpd_unused void *arg)
+{
+ song_free(song);
+ return 0;
+}
+
+static int free_each_dir(struct directory *dir, void *arg)
+{
+ if (arg != dir)
+ directory_free(dir);
+ return 0;
+}
+
void directory_free(struct directory *dir)
{
+ directory_walk(dir, free_each_song, free_each_dir, dir);
dirvec_destroy(&dir->children);
songvec_destroy(&dir->songs);
- free(dir->path);
- free(dir);
- /* this resets last dir returned */
- /*directory_get_path(NULL); */
+ if (dir != &music_root) {
+ assert(dir->parent);
+ dirvec_delete(&dir->parent->children, dir);
+ free(dir);
+ }
}
-void directory_prune_empty(struct directory *dir)
+static int dir_pruner(struct directory *dir, void *_dv)
{
- int i;
- struct dirvec *dv = &dir->children;
+ directory_prune_empty(dir);
+ if (directory_is_empty(dir))
+ dirvec_delete((struct dirvec *)_dv, dir);
+ return 0;
+}
- for (i = dv->nr; --i >= 0; ) {
- directory_prune_empty(dv->base[i]);
- if (directory_is_empty(dv->base[i]))
- dirvec_delete(dv, dv->base[i]);
- }
- if (!dv->nr)
- dirvec_destroy(dv);
+void directory_prune_empty(struct directory *dir)
+{
+ dirvec_for_each(&dir->children, dir_pruner, &dir->children);
}
struct directory *
@@ -71,7 +90,7 @@ directory_get_subdir(struct directory *dir, const char *name)
assert(name != NULL);
- if (isRootDirectory(name))
+ if (path_is_music_root(name))
return dir;
duplicated = xstrdup(name);
@@ -94,27 +113,25 @@ directory_get_subdir(struct directory *dir, const char *name)
return found;
}
-void directory_sort(struct directory *dir)
-{
- int i;
- struct dirvec *dv = &dir->children;
+struct dirwalk_arg {
+ int (*each_song) (struct mpd_song *, void *);
+ int (*each_dir) (struct directory *, void *);
+ void *data;
+};
- dirvec_sort(dv);
- songvec_sort(&dir->songs);
+static int dirwalk_x(struct directory *dir, void *_arg)
+{
+ struct dirwalk_arg *arg = _arg;
- for (i = dv->nr; --i >= 0; )
- directory_sort(dv->base[i]);
+ return directory_walk(dir, arg->each_song, arg->each_dir, arg->data);
}
-int
-directory_walk(struct directory *dir,
+int directory_walk(struct directory *dir,
int (*forEachSong) (struct mpd_song *, void *),
int (*forEachDir) (struct directory *, void *),
void *data)
{
- struct dirvec *dv = &dir->children;
int err = 0;
- size_t j;
if (forEachDir && (err = forEachDir(dir, data)) < 0)
return err;
@@ -125,9 +142,13 @@ directory_walk(struct directory *dir,
return err;
}
- for (j = 0; err >= 0 && j < dv->nr; ++j)
- err = directory_walk(dv->base[j], forEachSong,
- forEachDir, data);
+ if (forEachDir) {
+ struct dirwalk_arg dw_arg;
+ dw_arg.each_song = forEachSong;
+ dw_arg.each_dir = forEachDir;
+ dw_arg.data = data;
+ err = dirvec_for_each(&dir->children, dirwalk_x, &dw_arg);
+ }
return err;
}
diff --git a/src/directory.h b/src/directory.h
index 949df0c0e..8f0d797a8 100644
--- a/src/directory.h
+++ b/src/directory.h
@@ -22,6 +22,7 @@
#include "song.h"
#include "dirvec.h"
#include "songvec.h"
+#include "gcc.h"
#define DIRECTORY_DIR "directory: "
#define DIRECTORY_MTIME "mtime: " /* DEPRECATED, noop-read-only */
@@ -33,19 +34,21 @@
#define DIRECTORY_FS_CHARSET "fs_charset: "
struct directory {
- char *path;
struct dirvec children;
struct songvec songs;
struct directory *parent;
ino_t inode;
dev_t device;
unsigned stat; /* not needed if ino_t == dev_t == 0 is impossible */
-};
+ char path[mpd_sizeof_str_flex_array];
+} mpd_packed;
-static inline int isRootDirectory(const char *name)
+extern struct directory music_root;
+
+static inline int path_is_music_root(const char *name)
{
- /* TODO: verify and remove !name check */
- return (!name || *name == '\0' || !strcmp(name, "/"));
+ assert(name);
+ return (!*name || !strcmp(name, "/"));
}
struct directory * directory_new(const char *dirname, struct directory *parent);
@@ -89,8 +92,6 @@ int directory_save(int fd, struct directory *dir);
void directory_load(FILE *fp, struct directory *dir);
-void directory_sort(struct directory *dir);
-
int db_walk(const char *name,
int (*forEachSong) (struct mpd_song *, void *),
int (*forEachDir) (struct directory *, void *), void *data);
diff --git a/src/directory_print.c b/src/directory_print.c
index 1c30f1608..d7b42ec59 100644
--- a/src/directory_print.c
+++ b/src/directory_print.c
@@ -23,25 +23,23 @@
#include "songvec.h"
#include "myfprintf.h"
-static int dirvec_print(int fd, const struct dirvec *dv)
+static int directory_print_info(struct directory *dir, int fd)
{
- size_t i;
-
- for (i = 0; i < dv->nr; ++i) {
- if (fdprintf(fd, DIRECTORY_DIR "%s\n",
- directory_get_path(dv->base[i])) < 0)
- return -1;
- }
+ return fdprintf(fd, DIRECTORY_DIR "%s\n", directory_get_path(dir));
+}
- return 0;
+static int directory_print_info_x(struct directory *dir, void *data)
+{
+ return directory_print_info(dir, (int)(size_t)data);
}
int directory_print(int fd, const struct directory *dir)
{
- if (dirvec_print(fd, &dir->children) < 0)
+ void *arg = (void *)(size_t)fd;
+
+ if (dirvec_for_each(&dir->children, directory_print_info_x, arg) < 0)
return -1;
- if (songvec_for_each(&dir->songs, song_print_info_x,
- (void *)(size_t)fd) < 0)
+ if (songvec_for_each(&dir->songs, song_print_info_x, arg) < 0)
return -1;
return 0;
}
diff --git a/src/directory_save.c b/src/directory_save.c
index c39ece58a..e12de0019 100644
--- a/src/directory_save.c
+++ b/src/directory_save.c
@@ -45,7 +45,7 @@ int directory_save(int fd, struct directory *dir)
struct dirvec *children = &dir->children;
size_t i;
- if (!isRootDirectory(dir->path) &&
+ if ((dir != &music_root) &&
fdprintf(fd, DIRECTORY_BEGIN "%s\n",
directory_get_path(dir)) < 0)
return -1;
@@ -70,7 +70,7 @@ int directory_save(int fd, struct directory *dir)
if (fdprintf(fd, SONG_END "\n") < 0)
return -1;
- if (!isRootDirectory(dir->path) &&
+ if ((dir != &music_root) &&
fdprintf(fd, DIRECTORY_END "%s\n",
directory_get_path(dir)) < 0)
return -1;
diff --git a/src/dirvec.c b/src/dirvec.c
index fdfbb3434..a5eb6d54e 100644
--- a/src/dirvec.c
+++ b/src/dirvec.c
@@ -3,6 +3,8 @@
#include "os_compat.h"
#include "utils.h"
+static pthread_mutex_t nr_lock = PTHREAD_MUTEX_INITIALIZER;
+
static size_t dv_size(struct dirvec *dv)
{
return dv->nr * sizeof(struct directory *);
@@ -16,55 +18,92 @@ static int dirvec_cmp(const void *d1, const void *d2)
return strcmp(a->path, b->path);
}
-void dirvec_sort(struct dirvec *dv)
-{
- qsort(dv->base, dv->nr, sizeof(struct directory *), dirvec_cmp);
-}
-
struct directory *dirvec_find(const struct dirvec *dv, const char *path)
{
int i;
+ struct directory *ret = NULL;
- for (i = dv->nr; --i >= 0; )
- if (!strcmp(dv->base[i]->path, path))
- return dv->base[i];
- return NULL;
+ pthread_mutex_lock(&nr_lock);
+ for (i = dv->nr; --i >= 0; ) {
+ if (strcmp(dv->base[i]->path, path))
+ continue;
+ ret = dv->base[i];
+ break;
+ }
+ pthread_mutex_unlock(&nr_lock);
+ return ret;
}
int dirvec_delete(struct dirvec *dv, struct directory *del)
{
int i;
+ pthread_mutex_lock(&nr_lock);
for (i = dv->nr; --i >= 0; ) {
if (dv->base[i] != del)
continue;
/* we _don't_ call directory_free() here */
if (!--dv->nr) {
+ pthread_mutex_unlock(&nr_lock);
free(dv->base);
dv->base = NULL;
+ return i;
} else {
memmove(&dv->base[i], &dv->base[i + 1],
(dv->nr - i + 1) * sizeof(struct directory *));
dv->base = xrealloc(dv->base, dv_size(dv));
}
- return i;
+ break;
}
+ pthread_mutex_unlock(&nr_lock);
- return -1; /* not found */
+ return i;
}
void dirvec_add(struct dirvec *dv, struct directory *add)
{
- ++dv->nr;
+ size_t old_nr;
+
+ pthread_mutex_lock(&nr_lock);
+ old_nr = dv->nr++;
dv->base = xrealloc(dv->base, dv_size(dv));
- dv->base[dv->nr - 1] = add;
+ dv->base[old_nr] = add;
+ if (old_nr && dirvec_cmp(&dv->base[old_nr - 1], &add) >= 0)
+ qsort(dv->base, dv->nr, sizeof(struct directory *), dirvec_cmp);
+ pthread_mutex_unlock(&nr_lock);
}
void dirvec_destroy(struct dirvec *dv)
{
+ pthread_mutex_lock(&nr_lock);
+ dv->nr = 0;
+ pthread_mutex_unlock(&nr_lock);
if (dv->base) {
free(dv->base);
dv->base = NULL;
}
- dv->nr = 0;
+}
+
+int dirvec_for_each(const struct dirvec *dv,
+ int (*fn)(struct directory *, void *), void *arg)
+{
+ size_t i;
+ size_t prev_nr;
+
+ pthread_mutex_lock(&nr_lock);
+ for (i = 0; i < dv->nr; ) {
+ struct directory *dir = dv->base[i];
+
+ assert(dir);
+ prev_nr = dv->nr;
+ pthread_mutex_unlock(&nr_lock);
+ if (fn(dir, arg) < 0)
+ return -1;
+ pthread_mutex_lock(&nr_lock); /* dv->nr may change in fn() */
+ if (prev_nr == dv->nr)
+ ++i;
+ }
+ pthread_mutex_unlock(&nr_lock);
+
+ return 0;
}
diff --git a/src/dirvec.h b/src/dirvec.h
index 02496cd2b..6709537b8 100644
--- a/src/dirvec.h
+++ b/src/dirvec.h
@@ -8,8 +8,6 @@ struct dirvec {
size_t nr;
};
-void dirvec_sort(struct dirvec *dv);
-
struct directory *dirvec_find(const struct dirvec *dv, const char *path);
int dirvec_delete(struct dirvec *dv, struct directory *del);
@@ -23,4 +21,7 @@ static inline void dirvec_clear(struct dirvec *dv)
void dirvec_destroy(struct dirvec *dv);
+int dirvec_for_each(const struct dirvec *dv,
+ int (*fn)(struct directory *, void *), void *arg);
+
#endif /* DIRVEC_H */
diff --git a/src/gcc.h b/src/gcc.h
index 7492e6cad..6bd85663d 100644
--- a/src/gcc.h
+++ b/src/gcc.h
@@ -32,6 +32,10 @@
# define mpd_must_check __attribute__ ((warn_unused_result))
# define mpd_noreturn __attribute__ ((noreturn))
# define mpd_packed __attribute__ ((packed))
+
+/* str_flex_array is always >= 1 because we always 0 terminate strings */
+# define mpd_sizeof_str_flex_array 1
+
/* these are very useful for type checking */
# define mpd_printf __attribute__ ((format(printf,1,2)))
# define mpd_fprintf __attribute__ ((format(printf,2,3)))
@@ -52,6 +56,7 @@
# define mpd_must_check
# define mpd_noreturn
# define mpd_packed
+# define mpd_sizeof_str_flex_array (sizeof(size_t))
# define mpd_printf
# define mpd_fprintf
# define mpd_fprintf_
diff --git a/src/song.c b/src/song.c
index 77d113fd4..a524dca93 100644
--- a/src/song.c
+++ b/src/song.c
@@ -85,7 +85,7 @@ void song_free(struct mpd_song * song)
ssize_t song_print_url(struct mpd_song *song, int fd)
{
- if (song->parent && !isRootDirectory(song->parent->path))
+ if (song->parent && song->parent != &music_root)
return fdprintf(fd, "%s%s/%s\n", SONG_FILE,
directory_get_path(song->parent), song->url);
return fdprintf(fd, "%s%s\n", SONG_FILE, song->url);
@@ -240,7 +240,7 @@ char *song_get_url(struct mpd_song *song, char *path_max_tmp)
assert(*song->url);
- if (!song->parent || isRootDirectory(song->parent->path))
+ if (!song->parent || song->parent == &music_root)
strcpy(path_max_tmp, song->url);
else
pfx_dir(path_max_tmp, song->url, strlen(song->url),
diff --git a/src/song.h b/src/song.h
index 338bcf6c1..e29c034ef 100644
--- a/src/song.h
+++ b/src/song.h
@@ -35,8 +35,8 @@ struct mpd_song {
struct mpd_tag *tag;
struct directory *parent;
time_t mtime;
- char url[sizeof(size_t)];
-};
+ char url[mpd_sizeof_str_flex_array];
+} mpd_packed;
void song_free(struct mpd_song *);
diff --git a/src/songvec.c b/src/songvec.c
index ce04a4373..19433a222 100644
--- a/src/songvec.c
+++ b/src/songvec.c
@@ -17,13 +17,6 @@ static size_t sv_size(struct songvec *sv)
return sv->nr * sizeof(struct mpd_song *);
}
-void songvec_sort(struct songvec *sv)
-{
- pthread_mutex_lock(&nr_lock);
- qsort(sv->base, sv->nr, sizeof(struct mpd_song *), songvec_cmp);
- pthread_mutex_unlock(&nr_lock);
-}
-
struct mpd_song *songvec_find(const struct songvec *sv, const char *url)
{
int i;
@@ -48,10 +41,12 @@ int songvec_delete(struct songvec *sv, const struct mpd_song *del)
for (i = sv->nr; --i >= 0; ) {
if (sv->base[i] != del)
continue;
- /* we _don't_ call freeSong() here */
+ /* we _don't_ call song_free() here */
if (!--sv->nr) {
+ pthread_mutex_unlock(&nr_lock);
free(sv->base);
sv->base = NULL;
+ return i;
} else {
memmove(&sv->base[i], &sv->base[i + 1],
(sv->nr - i + 1) * sizeof(struct mpd_song *));
@@ -66,40 +61,48 @@ int songvec_delete(struct songvec *sv, const struct mpd_song *del)
void songvec_add(struct songvec *sv, struct mpd_song *add)
{
+ size_t old_nr;
+
pthread_mutex_lock(&nr_lock);
- ++sv->nr;
+ old_nr = sv->nr++;
sv->base = xrealloc(sv->base, sv_size(sv));
- sv->base[sv->nr - 1] = add;
+ sv->base[old_nr] = add;
+ if (old_nr && songvec_cmp(&sv->base[old_nr - 1], &add) >= 0)
+ qsort(sv->base, sv->nr, sizeof(struct mpd_song *), songvec_cmp);
pthread_mutex_unlock(&nr_lock);
}
void songvec_destroy(struct songvec *sv)
{
pthread_mutex_lock(&nr_lock);
+ sv->nr = 0;
+ pthread_mutex_unlock(&nr_lock);
if (sv->base) {
free(sv->base);
sv->base = NULL;
}
- sv->nr = 0;
- pthread_mutex_unlock(&nr_lock);
}
int songvec_for_each(const struct songvec *sv,
int (*fn)(struct mpd_song *, void *), void *arg)
{
size_t i;
+ size_t prev_nr;
pthread_mutex_lock(&nr_lock);
- for (i = 0; i < sv->nr; ++i) {
+ for (i = 0; i < sv->nr; ) {
struct mpd_song *song = sv->base[i];
assert(song);
assert(*song->url);
+ prev_nr = sv->nr;
pthread_mutex_unlock(&nr_lock); /* fn() may block */
if (fn(song, arg) < 0)
return -1;
pthread_mutex_lock(&nr_lock); /* sv->nr may change in fn() */
+ if (prev_nr == sv->nr)
+ ++i;
}
pthread_mutex_unlock(&nr_lock);
diff --git a/src/songvec.h b/src/songvec.h
index 633cf8d66..10a896052 100644
--- a/src/songvec.h
+++ b/src/songvec.h
@@ -9,8 +9,6 @@ struct songvec {
size_t nr;
};
-void songvec_sort(struct songvec *sv);
-
struct mpd_song *songvec_find(const struct songvec *sv, const char *url);
int songvec_delete(struct songvec *sv, const struct mpd_song *del);
diff --git a/src/tag.h b/src/tag.h
index 744b82e83..58df5b3d1 100644
--- a/src/tag.h
+++ b/src/tag.h
@@ -42,7 +42,7 @@ extern const char *mpdTagItemKeys[];
struct tag_item {
enum tag_type type;
- char value[1];
+ char value[mpd_sizeof_str_flex_array];
} mpd_packed;
struct mpd_tag {
diff --git a/src/tag_pool.c b/src/tag_pool.c
index d227a2988..1d92502cc 100644
--- a/src/tag_pool.c
+++ b/src/tag_pool.c
@@ -67,7 +67,9 @@ static struct slot *slot_alloc(struct slot *next,
enum tag_type type,
const char *value, int length)
{
- struct slot *slot = xmalloc(sizeof(*slot) + length);
+ struct slot *slot;
+
+ slot = xmalloc(sizeof(*slot) - sizeof(slot->item.value) + length + 1);
slot->next = next;
slot->ref = 1;
slot->item.type = type;
diff --git a/src/update.c b/src/update.c
index ca3640b73..9ede72759 100644
--- a/src/update.c
+++ b/src/update.c
@@ -46,7 +46,9 @@ static const unsigned update_task_id_max = 1 << 15;
static unsigned update_task_id;
-static struct mpd_song *delete;
+static void (*delete_fn)(void *);
+
+static void *delete;
static struct condition delete_cond;
@@ -62,44 +64,33 @@ static void directory_set_stat(struct directory *dir, const struct stat *st)
dir->stat = 1;
}
-static void delete_song(struct directory *dir, struct mpd_song *del)
+static void serialized_delete(void (*fn)(void *), void *del)
{
- /* first, prevent traversers in main task from getting this */
- songvec_delete(&dir->songs, del);
-
- /* now take it out of the playlist (in the main_task) */
cond_enter(&delete_cond);
assert(!delete);
+ assert(!delete_fn);
+ delete_fn = fn;
delete = del;
wakeup_main_task();
do { cond_wait(&delete_cond); } while (delete);
cond_leave(&delete_cond);
-
- /* finally, all possible references gone, free it */
- song_free(del);
}
-static int delete_each_song(struct mpd_song *song, mpd_unused void *data)
+static void delete_song_safely(struct mpd_song *song)
{
- struct directory *dir = data;
- assert(song->parent == dir);
- delete_song(dir, song);
- return 0;
+ char tmp[MPD_PATH_MAX];
+ LOG("removing: %s\n", song_get_url(song, tmp));
+ deleteASongFromPlaylist(song);
+ song_free(song);
}
-/**
- * Recursively remove all sub directories and songs from a directory,
- * leaving an empty directory.
- */
-static void clear_directory(struct directory *dir)
+static void delete_song(struct directory *dir, struct mpd_song *del)
{
- int i;
-
- for (i = dir->children.nr; --i >= 0;)
- clear_directory(dir->children.base[i]);
- dirvec_clear(&dir->children);
+ /* first, prevent traversers in main task from getting this */
+ songvec_delete(&dir->songs, del);
- songvec_for_each(&dir->songs, delete_each_song, dir);
+ /* now take it out of the playlist (in the main_task) and free */
+ serialized_delete((void (*)(void *))delete_song_safely, del);
}
/**
@@ -109,9 +100,11 @@ static void delete_directory(struct directory *dir)
{
assert(dir->parent != NULL);
- clear_directory(dir);
+ /* first, prevent traversers in main task from getting this */
dirvec_delete(&dir->parent->children, dir);
- directory_free(dir);
+
+ /* now we let the main task recursively delete everything under us */
+ serialized_delete((void (*)(void *))directory_free, dir);
}
struct delete_data {
@@ -149,23 +142,27 @@ static void delete_path(const char *path)
}
}
-static void
-removeDeletedFromDirectory(char *path_max_tmp, struct directory *dir)
+/* passed to dirvec_for_each */
+static int delete_directory_if_removed(struct directory *dir, void *_data)
{
- int i;
- struct dirvec *dv = &dir->children;
- struct delete_data data;
+ if (!isDir(dir->path)) {
+ struct delete_data *data = _data;
- for (i = dv->nr; --i >= 0; ) {
- if (isDir(dv->base[i]->path))
- continue;
- LOG("removing directory: %s\n", dv->base[i]->path);
- dirvec_delete(dv, dv->base[i]);
+ LOG("removing directory: %s\n", directory_get_path(dir));
+ dirvec_delete(&data->dir->children, dir);
modified = 1;
}
+ return 0;
+}
+
+static void
+removeDeletedFromDirectory(char *path_max_tmp, struct directory *dir)
+{
+ struct delete_data data;
data.dir = dir;
data.tmp = path_max_tmp;
+ dirvec_for_each(&dir->children, delete_directory_if_removed, &data);
songvec_for_each(&dir->songs, delete_song_if_removed, &data);
}
@@ -278,7 +275,7 @@ static int updateDirectory(struct directory *dir, const struct stat *st)
if (!utf8)
continue;
- if (!isRootDirectory(dir->path))
+ if (dir != &music_root)
utf8 = pfx_dir(path_max_tmp, utf8, strlen(utf8),
dirname, strlen(dirname));
@@ -317,10 +314,9 @@ directory_make_child_checked(struct directory *parent, const char *path)
return dir;
}
-static struct directory *
-addParentPathToDB(const char *utf8path)
+static struct directory * addParentPathToDB(const char *utf8path)
{
- struct directory *dir = db_get_root();
+ struct directory *dir = &music_root;
char *duplicated = xstrdup(utf8path);
char *slash = duplicated;
@@ -350,15 +346,17 @@ static void updatePath(const char *utf8path)
static void * update_task(void *_path)
{
- if (_path != NULL && !isRootDirectory(_path)) {
- updatePath((char *)_path);
- free(_path);
+ char *utf8path = _path;
+
+ if (utf8path) {
+ assert(*utf8path);
+ updatePath(utf8path);
+ free(utf8path);
} else {
- struct directory *dir = db_get_root();
struct stat st;
- if (myStat(directory_get_path(dir), &st) == 0)
- updateDirectory(dir, &st);
+ if (myStat(directory_get_path(&music_root), &st) == 0)
+ updateDirectory(&music_root, &st);
}
if (modified)
@@ -388,13 +386,14 @@ unsigned directory_update_init(char *path)
{
assert(pthread_equal(pthread_self(), main_task));
+ assert(!path || (path && *path));
+
if (progress != UPDATE_PROGRESS_IDLE) {
unsigned next_task_id;
- if (!path)
- return 0;
if (update_paths_nr == ARRAY_SIZE(update_paths)) {
- free(path);
+ if (path)
+ free(path);
return 0;
}
@@ -416,11 +415,9 @@ void reap_update_task(void)
return;
cond_enter(&delete_cond);
- if (delete) {
- char tmp[MPD_PATH_MAX];
- LOG("removing: %s\n", song_get_url(delete, tmp));
- deleteASongFromPlaylist(delete);
- delete = NULL;
+ if (delete && delete_fn) {
+ delete_fn(delete);
+ delete = delete_fn = NULL;
cond_signal(&delete_cond);
}
cond_leave(&delete_cond);