aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2008-10-11 23:39:50 -0700
committerEric Wong <normalperson@yhbt.net>2008-10-11 23:39:50 -0700
commit0df62a2c3cb7af88347d40a17cc336b5d1740f62 (patch)
tree25040833144c3e24f6b4702d9b745cd068b5371a
parent3456e2de5bf90207d8149a842bb12c3f9bdd218f (diff)
parent6e2b0ca9edaed200f250ef487701ad161aa4a168 (diff)
downloadmpd-0df62a2c3cb7af88347d40a17cc336b5d1740f62.tar.gz
mpd-0df62a2c3cb7af88347d40a17cc336b5d1740f62.tar.xz
mpd-0df62a2c3cb7af88347d40a17cc336b5d1740f62.zip
Merge branch 'mk/directory'
* mk/directory: (59 commits) directory: don't use identical struct and variable names update: replaced update_return with global "modified" flag update: make the variable "progress" static update: don't print debug message when song was not modified update: fix memory leak in directory_update_init() update: make the job id unsigned update: job ID must be positive update: check progress!=IDLE in reap_update_task() update: fixed stack corruption due to pthread_join() call updated: always call removeDeletedFromDirectory() update: eliminated addSubDirectoryToDirectory() update: make the "song" variable more local update: do the recursive directory check only once update: copy stat to new directory update: avoid duplicate stat() calls update: rewrote updatePath() using updateInDirectory() update: don't export updateDirectory() update: pass const pointer to addSubDirectoryToDirectory() update: never pass root path to updatePath() update: merged addDirectoryPathToDB() into addParentPathToDB() ... Conflicts: src/song.c
-rw-r--r--src/Makefile.am9
-rw-r--r--src/command.c16
-rw-r--r--src/database.c314
-rw-r--r--src/database.h48
-rw-r--r--src/dbUtils.c63
-rw-r--r--src/directory.c1041
-rw-r--r--src/directory.h83
-rw-r--r--src/directory_print.c47
-rw-r--r--src/directory_print.h26
-rw-r--r--src/directory_save.c120
-rw-r--r--src/directory_save.h30
-rw-r--r--src/dirvec.c70
-rw-r--r--src/dirvec.h69
-rw-r--r--src/inputPlugin.c1
-rw-r--r--src/inputPlugin.h2
-rw-r--r--src/locate.c16
-rw-r--r--src/locate.h6
-rw-r--r--src/log.h2
-rw-r--r--src/main.c18
-rw-r--r--src/playlist.c61
-rw-r--r--src/playlist.h4
-rw-r--r--src/sig_handlers.c5
-rw-r--r--src/song.c139
-rw-r--r--src/song.h45
-rw-r--r--src/songvec.c23
-rw-r--r--src/songvec.h11
-rw-r--r--src/stats.c8
-rw-r--r--src/storedPlaylist.c12
-rw-r--r--src/storedPlaylist.h2
-rw-r--r--src/update.c444
-rw-r--r--src/update.h34
31 files changed, 1471 insertions, 1298 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index bfefd5eec..6a7497286 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -39,9 +39,13 @@ mpd_headers = \
command.h \
condition.h \
conf.h \
+ database.h \
dbUtils.h \
decode.h \
directory.h \
+ directory_print.h \
+ directory_save.h \
+ update.h \
dirvec.h \
gcc.h \
inputPlugin.h \
@@ -110,9 +114,14 @@ mpd_SOURCES = \
command.c \
condition.c \
conf.c \
+ database.c \
dbUtils.c \
decode.c \
directory.c \
+ directory_print.c \
+ directory_save.c \
+ dirvec.c \
+ update.c \
inputPlugin.c \
inputStream.c \
inputStream_file.c \
diff --git a/src/command.c b/src/command.c
index f60f4e69e..518bf5f0d 100644
--- a/src/command.c
+++ b/src/command.c
@@ -19,7 +19,10 @@
#include "command.h"
#include "playlist.h"
#include "ls.h"
+#include "database.h"
#include "directory.h"
+#include "directory_print.h"
+#include "update.h"
#include "volume.h"
#include "stats.h"
#include "myfprintf.h"
@@ -556,15 +559,18 @@ static int handleLsInfo(int fd, mpd_unused int *permission,
int argc, char *argv[])
{
const char *path = "";
+ const struct directory *dir;
if (argc == 2)
path = argv[1];
- if (printDirectoryInfo(fd, path) < 0) {
+ if (!(dir = db_get_directory(path))) {
commandError(fd, ACK_ERROR_NO_EXIST, "directory not found");
return -1;
}
+ directory_print(fd, dir);
+
if (isRootDirectory(path))
return lsPlaylists(fd, path);
@@ -781,14 +787,12 @@ static int handlePlaylistMove(int fd, mpd_unused int *permission,
static int print_update_result(int fd, int ret)
{
- if (ret >= 0) {
+ if (ret > 0) {
fdprintf(fd, "updating_db: %i\n", ret);
return 0;
}
- if (ret == -2)
- commandError(fd, ACK_ERROR_ARG, "invalid path");
- else
- commandError(fd, ACK_ERROR_UPDATE_ALREADY, "already updating");
+ assert(!ret);
+ commandError(fd, ACK_ERROR_UPDATE_ALREADY, "already updating");
return -1;
}
diff --git a/src/database.c b/src/database.c
new file mode 100644
index 000000000..dde57ce6a
--- /dev/null
+++ b/src/database.c
@@ -0,0 +1,314 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * Copyright (C) 2008 Max Kellermann <max@duempel.org>
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "database.h"
+#include "directory.h"
+#include "directory_save.h"
+#include "song.h"
+#include "conf.h"
+#include "log.h"
+#include "ls.h"
+#include "path.h"
+#include "stats.h"
+#include "utils.h"
+#include "dbUtils.h"
+#include "update.h"
+#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");
+
+ do {
+ my_usleep(100000);
+ reap_update_task();
+ } while (isUpdatingDB());
+
+ stats.numberOfSongs = countSongsIn(NULL);
+ stats.dbPlayTime = sumSongTimesIn(NULL);
+}
+
+void db_finish(void)
+{
+ directory_free(music_root);
+}
+
+struct directory * db_get_root(void)
+{
+ assert(music_root != NULL);
+
+ return music_root;
+}
+
+struct directory * db_get_directory(const char *name)
+{
+ if (name == NULL)
+ return music_root;
+
+ return directory_get_subdir(music_root, name);
+}
+
+struct mpd_song *db_get_song(const char *file)
+{
+ struct mpd_song *song = NULL;
+ struct directory *dir;
+ char *dirpath = NULL;
+ char *duplicated = xstrdup(file);
+ char *shortname = strrchr(duplicated, '/');
+
+ DEBUG("get song: %s\n", file);
+
+ if (!shortname) {
+ shortname = duplicated;
+ } else {
+ *shortname = '\0';
+ ++shortname;
+ dirpath = duplicated;
+ }
+
+ if (!(dir = db_get_directory(dirpath)))
+ goto out;
+ if (!(song = songvec_find(&dir->songs, shortname)))
+ goto out;
+ assert(song->parent == dir);
+
+out:
+ free(duplicated);
+ return song;
+}
+
+int db_walk(const char *name,
+ int (*forEachSong) (struct mpd_song *, void *),
+ int (*forEachDir) (struct directory *, void *), void *data)
+{
+ struct directory *dir;
+
+ if ((dir = db_get_directory(name)) == NULL) {
+ struct mpd_song *song;
+ if ((song = db_get_song(name)) && forEachSong) {
+ return forEachSong(song, data);
+ }
+ return -1;
+ }
+
+ return directory_walk(dir, forEachSong, forEachDir, data);
+}
+
+static char *db_get_file(void)
+{
+ ConfigParam *param = parseConfigFilePath(CONF_DB_FILE, 1);
+
+ assert(param);
+ assert(param->value);
+
+ return param->value;
+}
+
+int db_check(void)
+{
+ struct stat st;
+ char *dbFile = db_get_file();
+
+ /* Check if the file exists */
+ if (access(dbFile, F_OK)) {
+ /* If the file doesn't exist, we can't check if we can write
+ * it, so we are going to try to get the directory path, and
+ * see if we can write a file in that */
+ char dirPath[MPD_PATH_MAX];
+ parent_path(dirPath, dbFile);
+ if (*dirPath == '\0')
+ strcpy(dirPath, "/");
+
+ /* Check that the parent part of the path is a directory */
+ if (stat(dirPath, &st) < 0) {
+ ERROR("Couldn't stat parent directory of db file "
+ "\"%s\": %s\n", dbFile, strerror(errno));
+ return -1;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ ERROR("Couldn't create db file \"%s\" because the "
+ "parent path is not a directory\n", dbFile);
+ return -1;
+ }
+
+ /* Check if we can write to the directory */
+ if (access(dirPath, R_OK | W_OK)) {
+ ERROR("Can't create db file in \"%s\": %s\n", dirPath,
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+ }
+
+ /* Path exists, now check if it's a regular file */
+ if (stat(dbFile, &st) < 0) {
+ ERROR("Couldn't stat db file \"%s\": %s\n", dbFile,
+ strerror(errno));
+ return -1;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ ERROR("db file \"%s\" is not a regular file\n", dbFile);
+ return -1;
+ }
+
+ /* And check that we can write to it */
+ if (access(dbFile, R_OK | W_OK)) {
+ ERROR("Can't open db file \"%s\" for reading/writing: %s\n",
+ dbFile, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int db_save(void)
+{
+ int fd;
+ char *dbFile = db_get_file();
+ struct stat st;
+
+ DEBUG("removing empty directories from DB\n");
+ directory_prune_empty(music_root);
+
+ DEBUG("sorting DB\n");
+
+ directory_sort(music_root);
+
+ DEBUG("writing DB\n");
+
+ fd = open(dbFile, O_WRONLY|O_TRUNC|O_CREAT, 0666);
+ if (fd < 0) {
+ ERROR("unable to write to db file \"%s\": %s\n",
+ dbFile, strerror(errno));
+ return -1;
+ }
+
+ /*
+ * TODO: block signals when writing the db so we don't get a corrupted
+ * db (or unexpected failures). fdprintf() needs better error handling
+ */
+ fdprintf(fd,
+ DIRECTORY_INFO_BEGIN "\n"
+ DIRECTORY_MPD_VERSION VERSION "\n"
+ DIRECTORY_FS_CHARSET "%s\n"
+ DIRECTORY_INFO_END "\n", getFsCharset());
+
+ if (directory_save(fd, music_root) < 0) {
+ ERROR("Failed to write to database file: %s\n",
+ strerror(errno));
+ xclose(fd);
+ return -1;
+ }
+ xclose(fd);
+
+ if (stat(dbFile, &st) == 0)
+ directory_dbModTime = st.st_mtime;
+
+ return 0;
+}
+
+int db_load(void)
+{
+ FILE *fp = NULL;
+ char *dbFile = db_get_file();
+ struct stat st;
+ char buffer[100];
+ 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",
+ dbFile, strerror(errno));
+ return -1;
+ }
+
+ /* get initial info */
+ if (!myFgets(buffer, sizeof(buffer), fp))
+ FATAL("Error reading db, fgets\n");
+
+ if (0 != strcmp(DIRECTORY_INFO_BEGIN, buffer)) {
+ ERROR("db info not found in db file\n");
+ ERROR("you should recreate the db using --create-db\n");
+ while (fclose(fp) && errno == EINTR) ;
+ return -1;
+ }
+
+ while (myFgets(buffer, sizeof(buffer), fp) &&
+ 0 != strcmp(DIRECTORY_INFO_END, buffer)) {
+ if (!prefixcmp(buffer, DIRECTORY_MPD_VERSION)) {
+ if (foundVersion)
+ FATAL("already found version in db\n");
+ foundVersion = 1;
+ } else if (!prefixcmp(buffer, DIRECTORY_FS_CHARSET)) {
+ char *fsCharset;
+ char *tempCharset;
+
+ if (foundFsCharset)
+ FATAL("already found fs charset in db\n");
+
+ foundFsCharset = 1;
+
+ fsCharset = &(buffer[strlen(DIRECTORY_FS_CHARSET)]);
+ if ((tempCharset = getConfigParamValue(CONF_FS_CHARSET))
+ && strcmp(fsCharset, tempCharset)) {
+ WARNING("Using \"%s\" for the "
+ "filesystem charset "
+ "instead of \"%s\"\n",
+ fsCharset, tempCharset);
+ WARNING("maybe you need to "
+ "recreate the db?\n");
+ setFsCharset(fsCharset);
+ }
+ } else
+ FATAL("directory: unknown line in db info: %s\n",
+ buffer);
+ }
+
+ DEBUG("reading DB\n");
+
+ directory_load(fp, music_root);
+ while (fclose(fp) && errno == EINTR) ;
+
+ stats.numberOfSongs = countSongsIn(NULL);
+ stats.dbPlayTime = sumSongTimesIn(NULL);
+
+ if (stat(dbFile, &st) == 0)
+ directory_dbModTime = st.st_mtime;
+
+ return 0;
+}
+
+time_t db_get_mtime(void)
+{
+ return directory_dbModTime;
+}
diff --git a/src/database.h b/src/database.h
new file mode 100644
index 000000000..0eb7d535d
--- /dev/null
+++ b/src/database.h
@@ -0,0 +1,48 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * Copyright (C) 2008 Max Kellermann <max@duempel.org>
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DATABASE_H
+#define DATABASE_H
+
+#include "os_compat.h"
+#include "directory.h"
+
+void db_init(void);
+
+void db_finish(void);
+
+struct directory * db_get_root(void);
+
+struct directory * db_get_directory(const char *name);
+
+struct mpd_song * db_get_song(const char *file);
+
+int db_walk(const char *name,
+ int (*forEachSong) (struct mpd_song *, void *),
+ int (*forEachDir) (struct directory *, void *), void *data);
+
+int db_check(void);
+
+int db_save(void);
+
+int db_load(void);
+
+time_t db_get_mtime(void);
+
+#endif /* DATABASE_H */
diff --git a/src/dbUtils.c b/src/dbUtils.c
index bd990e96d..1fadb232e 100644
--- a/src/dbUtils.c
+++ b/src/dbUtils.c
@@ -18,7 +18,7 @@
#include "dbUtils.h"
-#include "directory.h"
+#include "database.h"
#include "myfprintf.h"
#include "utils.h"
#include "playlist.h"
@@ -45,22 +45,20 @@ typedef struct _SearchStats {
unsigned long playTime;
} SearchStats;
-static int countSongsInDirectory(Directory * directory,
- void *data)
+static int countSongsInDirectory(struct directory *dir, void *data)
{
int *count = (int *)data;
- *count += directory->songs.nr;
+ *count += dir->songs.nr;
return 0;
}
-static int printDirectoryInDirectory(Directory * directory, void *data)
+static int printDirectoryInDirectory(struct directory *dir, void *data)
{
int fd = (int)(size_t)data;
- if (directory->path) {
- fdprintf(fd, "directory: %s\n", getDirectoryPath(directory));
- }
+ if (!isRootDirectory(dir->path))
+ fdprintf(fd, "directory: %s\n", directory_get_path(dir));
return 0;
}
@@ -69,7 +67,7 @@ struct search_data {
LocateTagItemArray array;
};
-static int searchInDirectory(Song * song, void *_data)
+static int searchInDirectory(struct mpd_song * song, void *_data)
{
struct search_data *data = _data;
int fd = data->fd;
@@ -99,7 +97,7 @@ int searchForSongsIn(int fd, const char *name, int numItems,
data.array.numItems = numItems;
data.array.items = items;
- ret = traverseAllIn(name, searchInDirectory, NULL, &data);
+ ret = db_walk(name, searchInDirectory, NULL, &data);
for (i = 0; i < numItems; i++) {
free(items[i].needle);
@@ -111,7 +109,7 @@ int searchForSongsIn(int fd, const char *name, int numItems,
return ret;
}
-static int findInDirectory(Song * song, void *_data)
+static int findInDirectory(struct mpd_song * song, void *_data)
{
struct search_data *data = _data;
int fd = data->fd;
@@ -131,7 +129,7 @@ int findSongsIn(int fd, const char *name, int numItems, LocateTagItem * items)
data.array.numItems = numItems;
data.array.items = items;
- return traverseAllIn(name, findInDirectory, NULL, &data);
+ return db_walk(name, findInDirectory, NULL, &data);
}
static void printSearchStats(int fd, SearchStats *stats)
@@ -140,7 +138,7 @@ static void printSearchStats(int fd, SearchStats *stats)
fdprintf(fd, "playtime: %li\n", stats->playTime);
}
-static int searchStatsInDirectory(Song * song, void *data)
+static int searchStatsInDirectory(struct mpd_song * song, void *data)
{
SearchStats *stats = data;
@@ -165,7 +163,7 @@ int searchStatsForSongsIn(int fd, const char *name, int numItems,
stats.numberOfSongs = 0;
stats.playTime = 0;
- ret = traverseAllIn(name, searchStatsInDirectory, NULL, &stats);
+ ret = db_walk(name, searchStatsInDirectory, NULL, &stats);
if (ret == 0)
printSearchStats(fd, &stats);
@@ -174,11 +172,12 @@ int searchStatsForSongsIn(int fd, const char *name, int numItems,
int printAllIn(int fd, const char *name)
{
- return traverseAllIn(name, song_print_url_x,
+ return db_walk(name, song_print_url_x,
printDirectoryInDirectory, (void*)(size_t)fd);
}
-static int directoryAddSongToPlaylist(Song * song, mpd_unused void *data)
+static int
+directoryAddSongToPlaylist(struct mpd_song * song, mpd_unused void *data)
{
return addSongToPlaylist(song, NULL);
}
@@ -187,7 +186,7 @@ struct add_data {
const char *path;
};
-static int directoryAddSongToStoredPlaylist(Song *song, void *_data)
+static int directoryAddSongToStoredPlaylist(struct mpd_song *song, void *_data)
{
struct add_data *data = _data;
@@ -198,7 +197,7 @@ static int directoryAddSongToStoredPlaylist(Song *song, void *_data)
int addAllIn(const char *name)
{
- return traverseAllIn(name, directoryAddSongToPlaylist, NULL, NULL);
+ return db_walk(name, directoryAddSongToPlaylist, NULL, NULL);
}
int addAllInToStoredPlaylist(const char *name, const char *utf8file)
@@ -206,11 +205,11 @@ int addAllInToStoredPlaylist(const char *name, const char *utf8file)
struct add_data data;
data.path = utf8file;
- return traverseAllIn(name, directoryAddSongToStoredPlaylist, NULL,
+ return db_walk(name, directoryAddSongToStoredPlaylist, NULL,
&data);
}
-static int sumSongTime(Song * song, void *data)
+static int sumSongTime(struct mpd_song * song, void *data)
{
unsigned long *sum_time = (unsigned long *)data;
@@ -222,7 +221,7 @@ static int sumSongTime(Song * song, void *data)
int printInfoForAllIn(int fd, const char *name)
{
- return traverseAllIn(name, song_print_info_x,
+ return db_walk(name, song_print_info_x,
printDirectoryInDirectory, (void*)(size_t)fd);
}
@@ -231,7 +230,7 @@ int countSongsIn(const char *name)
int count = 0;
void *ptr = (void *)&count;
- traverseAllIn(name, NULL, countSongsInDirectory, ptr);
+ db_walk(name, NULL, countSongsInDirectory, ptr);
return count;
}
@@ -241,7 +240,7 @@ unsigned long sumSongTimesIn(const char *name)
unsigned long dbPlayTime = 0;
void *ptr = (void *)&dbPlayTime;
- traverseAllIn(name, sumSongTime, NULL, ptr);
+ db_walk(name, sumSongTime, NULL, ptr);
return dbPlayTime;
}
@@ -264,7 +263,7 @@ static void freeListCommandItem(ListCommandItem * item)
}
static void visitTag(int fd, struct strset *set,
- Song * song, enum tag_type tagType)
+ struct mpd_song * song, enum tag_type tagType)
{
int i;
struct mpd_tag *tag = song->tag;
@@ -292,7 +291,7 @@ struct list_tags_data {
struct strset *set;
};
-static int listUniqueTagsInDirectory(Song * song, void *_data)
+static int listUniqueTagsInDirectory(struct mpd_song * song, void *_data)
{
struct list_tags_data *data = _data;
ListCommandItem *item = data->item;
@@ -318,7 +317,7 @@ int listAllUniqueTags(int fd, int type, int numConditionals,
data.set = strset_new();
}
- ret = traverseAllIn(NULL, listUniqueTagsInDirectory, NULL, &data);
+ ret = db_walk(NULL, listUniqueTagsInDirectory, NULL, &data);
if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
const char *value;
@@ -336,20 +335,20 @@ int listAllUniqueTags(int fd, int type, int numConditionals,
return ret;
}
-static int sumSavedFilenameMemoryInDirectory(Directory * dir, void *data)
+static int sumSavedFilenameMemoryInDirectory(struct directory *dir, void *data)
{
int *sum = data;
- if (!dir->path)
+ if (!isRootDirectory(dir->path))
return 0;
- *sum += (strlen(getDirectoryPath(dir)) + 1 - sizeof(Directory *)) *
- dir->songs.nr;
+ *sum += (strlen(directory_get_path(dir)) + 1
+ - sizeof(struct directory *)) * dir->songs.nr;
return 0;
}
-static int sumSavedFilenameMemoryInSong(Song * song, void *data)
+static int sumSavedFilenameMemoryInSong(struct mpd_song * song, void *data)
{
int *sum = data;
@@ -362,7 +361,7 @@ void printSavedMemoryFromFilenames(void)
{
int sum = 0;
- traverseAllIn(NULL, sumSavedFilenameMemoryInSong,
+ db_walk(NULL, sumSavedFilenameMemoryInSong,
sumSavedFilenameMemoryInDirectory, (void *)&sum);
DEBUG("saved memory from filenames: %i\n", sum);
diff --git a/src/directory.c b/src/directory.c
index 6dbf7a638..b90b477fd 100644
--- a/src/directory.c
+++ b/src/directory.c
@@ -18,664 +18,68 @@
#include "directory.h"
-#include "conf.h"
-#include "log.h"
-#include "ls.h"
-#include "path.h"
-#include "playlist.h"
-#include "stats.h"
#include "utils.h"
#include "ack.h"
#include "myfprintf.h"
-#include "dbUtils.h"
-#include "main_notify.h"
#include "dirvec.h"
-#define DIRECTORY_DIR "directory: "
-#define DIRECTORY_MTIME "mtime: " /* DEPRECATED, noop-read-only */
-#define DIRECTORY_BEGIN "begin: "
-#define DIRECTORY_END "end: "
-#define DIRECTORY_INFO_BEGIN "info_begin"
-#define DIRECTORY_INFO_END "info_end"
-#define DIRECTORY_MPD_VERSION "mpd_version: "
-#define DIRECTORY_FS_CHARSET "fs_charset: "
-
-enum update_return {
- UPDATE_RETURN_ERROR = -1,
- UPDATE_RETURN_NOUPDATE = 0,
- UPDATE_RETURN_UPDATED = 1
-};
-
-enum update_progress {
- UPDATE_PROGRESS_IDLE = 0,
- UPDATE_PROGRESS_RUNNING = 1,
- UPDATE_PROGRESS_DONE = 2
-} progress;
-
-/* make this dynamic?, or maybe this is big enough... */
-static char *update_paths[32];
-static size_t update_paths_nr;
-
-static Directory *music_root;
-
-static time_t directory_dbModTime;
-
-static pthread_t update_thr;
-
-static const int update_task_id_max = 1 << 15;
-
-static int update_task_id;
-
-static Song *delete;
-
-static struct condition delete_cond;
-
-static int addToDirectory(Directory * directory, const char *name);
-
-static void freeDirectory(Directory * directory);
-
-static enum update_return exploreDirectory(Directory * directory);
-
-static enum update_return updateDirectory(Directory * directory);
-
-static void deleteEmptyDirectoriesInDirectory(Directory * directory);
-
-static void delete_song(Directory *dir, Song *del);
-
-static enum update_return addSubDirectoryToDirectory(Directory * directory,
- const char *name, struct stat *st);
-
-static Directory *getDirectory(const char *name);
-
-static enum update_return updatePath(const char *utf8path);
-
-static void sortDirectory(Directory * directory);
-
-static int inodeFoundInParent(Directory * parent, ino_t inode, dev_t device);
-
-static int statDirectory(Directory * dir);
-
-static char *getDbFile(void)
-{
- ConfigParam *param = parseConfigFilePath(CONF_DB_FILE, 1);
-
- assert(param);
- assert(param->value);
-
- return param->value;
-}
-
-int isUpdatingDB(void)
+struct directory * directory_new(const char *dirname, struct directory * parent)
{
- return (progress != UPDATE_PROGRESS_IDLE) ? update_task_id : 0;
-}
+ struct directory *dir;
-static void * update_task(void *_path)
-{
- enum update_return ret = UPDATE_RETURN_NOUPDATE;
+ assert(dirname != NULL);
+ assert((*dirname == 0) == (parent == NULL));
- if (_path) {
- ret = updatePath((char *)_path);
- free(_path);
- } else {
- ret = updateDirectory(music_root);
- }
+ dir = xcalloc(1, sizeof(struct directory));
+ dir->path = xstrdup(dirname);
+ dir->parent = parent;
- if (ret == UPDATE_RETURN_UPDATED && writeDirectoryDB() < 0)
- ret = UPDATE_RETURN_ERROR;
- progress = UPDATE_PROGRESS_DONE;
- wakeup_main_task();
- return (void *)ret;
+ return dir;
}
-static void spawn_update_task(char *path)
+void directory_free(struct directory *dir)
{
- pthread_attr_t attr;
-
- assert(pthread_equal(pthread_self(), main_task));
-
- progress = UPDATE_PROGRESS_RUNNING;
- pthread_attr_init(&attr);
- if (pthread_create(&update_thr, &attr, update_task, path))
- FATAL("Failed to spawn update task: %s\n", strerror(errno));
- if (++update_task_id > update_task_id_max)
- update_task_id = 1;
- DEBUG("spawned thread for update job id %i\n", update_task_id);
-}
-
-void reap_update_task(void)
-{
- enum update_return ret;
-
- assert(pthread_equal(pthread_self(), main_task));
-
- cond_enter(&delete_cond);
- if (delete) {
- char tmp[MPD_PATH_MAX];
- LOG("removing: %s\n", get_song_url(tmp, delete));
- deleteASongFromPlaylist(delete);
- delete = NULL;
- cond_signal(&delete_cond);
- }
- cond_leave(&delete_cond);
-
- if (progress != UPDATE_PROGRESS_DONE)
- return;
- if (pthread_join(update_thr, (void **)&ret))
- FATAL("error joining update thread: %s\n", strerror(errno));
- if (ret == UPDATE_RETURN_UPDATED)
- playlistVersionChange();
-
- if (update_paths_nr) {
- char *path = update_paths[0];
- memmove(&update_paths[0], &update_paths[1],
- --update_paths_nr * sizeof(char *));
- spawn_update_task(path);
- } else {
- progress = UPDATE_PROGRESS_IDLE;
- }
-}
-
-int directory_update_init(char *path)
-{
- assert(pthread_equal(pthread_self(), main_task));
-
- if (progress != UPDATE_PROGRESS_IDLE) {
- int next_task_id;
-
- if (!path)
- return -1;
- if (update_paths_nr == ARRAY_SIZE(update_paths))
- return -1;
- assert(update_paths_nr < ARRAY_SIZE(update_paths));
- update_paths[update_paths_nr++] = path;
- next_task_id = update_task_id + update_paths_nr;
-
- return next_task_id > update_task_id_max ? 1 : next_task_id;
- }
- spawn_update_task(path);
- return update_task_id;
-}
-
-static void directory_set_stat(Directory * dir, const struct stat *st)
-{
- dir->inode = st->st_ino;
- dir->device = st->st_dev;
- dir->stat = 1;
-}
-
-static Directory *newDirectory(const char *dirname, Directory * parent)
-{
- Directory *directory;
-
- directory = xcalloc(1, sizeof(Directory));
-
- if (dirname && strlen(dirname))
- directory->path = xstrdup(dirname);
- directory->parent = parent;
-
- return directory;
-}
-
-static void freeDirectory(Directory * directory)
-{
- dirvec_destroy(&directory->children);
- songvec_destroy(&directory->songs);
- if (directory->path)
- free(directory->path);
- free(directory);
+ dirvec_destroy(&dir->children);
+ songvec_destroy(&dir->songs);
+ free(dir->path);
+ free(dir);
/* this resets last dir returned */
- /*getDirectoryPath(NULL); */
-}
-
-static void delete_song(Directory *dir, Song *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);
- delete = del;
- wakeup_main_task();
- do { cond_wait(&delete_cond); } while (delete);
- cond_leave(&delete_cond);
-
- /* finally, all possible references gone, free it */
- freeJustSong(del);
+ /*directory_get_path(NULL); */
}
-static void deleteEmptyDirectoriesInDirectory(Directory * directory)
+void directory_prune_empty(struct directory *dir)
{
int i;
- struct dirvec *dv = &directory->children;
+ struct dirvec *dv = &dir->children;
for (i = dv->nr; --i >= 0; ) {
- deleteEmptyDirectoriesInDirectory(dv->base[i]);
- if (!dv->base[i]->children.nr && !dv->base[i]->songs.nr)
+ 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);
}
-static enum update_return
-updateInDirectory(Directory * directory, const char *name)
+struct directory *
+directory_get_subdir(struct directory *dir, const char *name)
{
- Song *song;
- struct stat st;
-
- if (myStat(name, &st))
- return UPDATE_RETURN_ERROR;
-
- if (S_ISREG(st.st_mode) && hasMusicSuffix(name, 0)) {
- const char *shortname = mpd_basename(name);
-
- if (!(song = songvec_find(&directory->songs, shortname))) {
- addToDirectory(directory, name);
- return UPDATE_RETURN_UPDATED;
- } else if (st.st_mtime != song->mtime) {
- LOG("updating %s\n", name);
- if (updateSongInfo(song) < 0)
- delete_song(directory, song);
- return UPDATE_RETURN_UPDATED;
- }
- } else if (S_ISDIR(st.st_mode)) {
- Directory *subdir = dirvec_find(&directory->children, name);
- if (subdir) {
- assert(directory == subdir->parent);
- directory_set_stat(subdir, &st);
- return updateDirectory(subdir);
- } else {
- return addSubDirectoryToDirectory(directory, name, &st);
- }
- }
-
- return UPDATE_RETURN_NOUPDATE;
-}
-
-/* we don't look at hidden files nor files with newlines in them */
-static int skip_path(const char *path)
-{
- return (path[0] == '.' || strchr(path, '\n')) ? 1 : 0;
-}
-
-struct delete_data {
- char *tmp;
- Directory *dir;
- enum update_return ret;
-};
-
-/* passed to songvec_for_each */
-static int delete_song_if_removed(Song *song, void *_data)
-{
- struct delete_data *data = _data;
-
- data->tmp = get_song_url(data->tmp, song);
- assert(data->tmp);
-
- if (!isFile(data->tmp, NULL)) {
- delete_song(data->dir, song);
- data->ret = UPDATE_RETURN_UPDATED;
- }
- return 0;
-}
-
-static enum update_return
-removeDeletedFromDirectory(char *path_max_tmp, Directory * directory)
-{
- enum update_return ret = UPDATE_RETURN_NOUPDATE;
- int i;
- struct dirvec *dv = &directory->children;
- struct delete_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]);
- ret = UPDATE_RETURN_UPDATED;
- }
-
- data.dir = directory;
- data.tmp = path_max_tmp;
- data.ret = ret;
- songvec_for_each(&directory->songs, delete_song_if_removed, &data);
-
- return data.ret;
-}
-
-static Directory *addDirectoryPathToDB(const char *utf8path)
-{
- char path_max_tmp[MPD_PATH_MAX];
- char *parent;
- Directory *parentDirectory;
- Directory *directory;
- Song *conflicting;
-
- parent = parent_path(path_max_tmp, utf8path);
-
- if (strlen(parent) == 0)
- parentDirectory = music_root;
- else
- parentDirectory = addDirectoryPathToDB(parent);
-
- if (!parentDirectory)
- return NULL;
-
- if ((directory = dirvec_find(&parentDirectory->children, utf8path))) {
- assert(parentDirectory == directory->parent);
- } else {
- struct stat st;
- if (myStat(utf8path, &st) < 0 ||
- inodeFoundInParent(parentDirectory, st.st_ino, st.st_dev))
- return NULL;
- else {
- directory = newDirectory(utf8path, parentDirectory);
- dirvec_add(&parentDirectory->children, directory);
- }
- }
-
- /* if we're adding directory paths, make sure to delete filenames
- with potentially the same name */
- conflicting = songvec_find(&parentDirectory->songs,
- mpd_basename(directory->path));
- if (conflicting)
- delete_song(parentDirectory, conflicting);
-
- return directory;
-}
-
-static Directory *addParentPathToDB(const char *utf8path)
-{
- char *parent;
- char path_max_tmp[MPD_PATH_MAX];
- Directory *parentDirectory;
-
- parent = parent_path(path_max_tmp, utf8path);
-
- if (strlen(parent) == 0)
- parentDirectory = music_root;
- else
- parentDirectory = addDirectoryPathToDB(parent);
-
- if (!parentDirectory)
- return NULL;
-
- return (Directory *) parentDirectory;
-}
-
-static enum update_return updatePath(const char *utf8path)
-{
- Directory *directory;
- Directory *parentDirectory;
- Song *song;
- time_t mtime;
- enum update_return ret = UPDATE_RETURN_NOUPDATE;
- char path_max_tmp[MPD_PATH_MAX];
-
- assert(utf8path);
-
- /* if path is in the DB try to update it, or else delete it */
- if ((directory = getDirectory(utf8path))) {
- parentDirectory = directory->parent;
-
- /* if this update directory is successfull, we are done */
- if ((ret = updateDirectory(directory)) >= 0) {
- sortDirectory(directory);
- return ret;
- }
- /* we don't want to delete the root directory */
- else if (directory == music_root) {
- return UPDATE_RETURN_NOUPDATE;
- }
- /* if updateDirectory fails, means we should delete it */
- else {
- LOG("removing directory: %s\n", utf8path);
- dirvec_delete(&parentDirectory->children, directory);
- ret = UPDATE_RETURN_UPDATED;
- /* don't return, path maybe a song now */
- }
- } else if ((song = getSongFromDB(utf8path))) {
- parentDirectory = song->parentDir;
- if (!parentDirectory->stat
- && statDirectory(parentDirectory) < 0) {
- return UPDATE_RETURN_NOUPDATE;
- }
- /* if this song update is successful, we are done */
- else if (!inodeFoundInParent(parentDirectory->parent,
- parentDirectory->inode,
- parentDirectory->device) &&
- isMusic(get_song_url(path_max_tmp, song), &mtime, 0)) {
- if (song->mtime == mtime)
- return UPDATE_RETURN_NOUPDATE;
- else if (updateSongInfo(song) == 0)
- return UPDATE_RETURN_UPDATED;
- else {
- delete_song(parentDirectory, song);
- return UPDATE_RETURN_UPDATED;
- }
- }
- /* if updateDirectory fails, means we should delete it */
- else {
- delete_song(parentDirectory, song);
- ret = UPDATE_RETURN_UPDATED;
- /* don't return, path maybe a directory now */
- }
- }
-
- /* path not found in the db, see if it actually exists on the fs.
- * Also, if by chance a directory was replaced by a file of the same
- * name or vice versa, we need to add it to the db
- */
- if (isDir(utf8path) || isMusic(utf8path, NULL, 0)) {
- parentDirectory = addParentPathToDB(utf8path);
- if (!parentDirectory || (!parentDirectory->stat &&
- statDirectory(parentDirectory) < 0)) {
- } else if (0 == inodeFoundInParent(parentDirectory->parent,
- parentDirectory->inode,
- parentDirectory->device)
- && addToDirectory(parentDirectory, utf8path)
- > 0) {
- ret = UPDATE_RETURN_UPDATED;
- }
- }
-
- return ret;
-}
-
-static const char *opendir_path(char *path_max_tmp, const char *dirname)
-{
- if (*dirname != '\0')
- return rmp2amp_r(path_max_tmp,
- utf8_to_fs_charset(path_max_tmp, dirname));
- return musicDir;
-}
-
-static enum update_return updateDirectory(Directory * directory)
-{
- DIR *dir;
- const char *dirname = getDirectoryPath(directory);
- struct dirent *ent;
- char path_max_tmp[MPD_PATH_MAX];
- enum update_return ret = UPDATE_RETURN_NOUPDATE;
-
- if (!directory->stat && statDirectory(directory) < 0)
- return UPDATE_RETURN_ERROR;
- else if (inodeFoundInParent(directory->parent,
- directory->inode,
- directory->device))
- return UPDATE_RETURN_ERROR;
-
- dir = opendir(opendir_path(path_max_tmp, dirname));
- if (!dir)
- return UPDATE_RETURN_ERROR;
-
- if (removeDeletedFromDirectory(path_max_tmp, directory) > 0)
- ret = UPDATE_RETURN_UPDATED;
-
- while ((ent = readdir(dir))) {
- char *utf8;
- if (skip_path(ent->d_name))
- continue;
-
- utf8 = fs_charset_to_utf8(path_max_tmp, ent->d_name);
- if (!utf8)
- continue;
-
- if (directory->path)
- utf8 = pfx_dir(path_max_tmp, utf8, strlen(utf8),
- dirname, strlen(dirname));
- if (updateInDirectory(directory, path_max_tmp) > 0)
- ret = UPDATE_RETURN_UPDATED;
- }
-
- closedir(dir);
-
- return ret;
-}
-
-static enum update_return exploreDirectory(Directory * directory)
-{
- DIR *dir;
- const char *dirname = getDirectoryPath(directory);
- struct dirent *ent;
- char path_max_tmp[MPD_PATH_MAX];
- enum update_return ret = UPDATE_RETURN_NOUPDATE;
-
- DEBUG("explore: attempting to opendir: %s\n", dirname);
-
- dir = opendir(opendir_path(path_max_tmp, dirname));
- if (!dir)
- return UPDATE_RETURN_ERROR;
-
- DEBUG("explore: %s\n", dirname);
-
- while ((ent = readdir(dir))) {
- char *utf8;
- if (skip_path(ent->d_name))
- continue;
-
- utf8 = fs_charset_to_utf8(path_max_tmp, ent->d_name);
- if (!utf8)
- continue;
-
- DEBUG("explore: found: %s (%s)\n", ent->d_name, utf8);
-
- if (directory->path)
- utf8 = pfx_dir(path_max_tmp, utf8, strlen(utf8),
- dirname, strlen(dirname));
- if (addToDirectory(directory, path_max_tmp) > 0)
- ret = UPDATE_RETURN_UPDATED;
- }
-
- closedir(dir);
-
- return ret;
-}
-
-static int statDirectory(Directory * dir)
-{
- struct stat st;
-
- if (myStat(getDirectoryPath(dir), &st) < 0)
- return -1;
-
- directory_set_stat(dir, &st);
-
- return 0;
-}
-
-static int inodeFoundInParent(Directory * parent, ino_t inode, dev_t device)
-{
- while (parent) {
- if (!parent->stat && statDirectory(parent) < 0)
- return -1;
- if (parent->inode == inode && parent->device == device) {
- DEBUG("recursive directory found\n");
- return 1;
- }
- parent = parent->parent;
- }
-
- return 0;
-}
-
-static enum update_return addSubDirectoryToDirectory(Directory * directory,
- const char *name, struct stat *st)
-{
- Directory *subDirectory;
-
- if (inodeFoundInParent(directory, st->st_ino, st->st_dev))
- return UPDATE_RETURN_NOUPDATE;
-
- subDirectory = newDirectory(name, directory);
- directory_set_stat(subDirectory, st);
-
- if (exploreDirectory(subDirectory) < 1) {
- freeDirectory(subDirectory);
- return UPDATE_RETURN_NOUPDATE;
- }
-
- dirvec_add(&directory->children, subDirectory);
-
- return UPDATE_RETURN_UPDATED;
-}
-
-static int addToDirectory(Directory * directory, const char *name)
-{
- struct stat st;
-
- if (myStat(name, &st)) {
- DEBUG("failed to stat %s: %s\n", name, strerror(errno));
- return -1;
- }
-
- if (S_ISREG(st.st_mode) &&
- hasMusicSuffix(name, 0) && isMusic(name, NULL, 0)) {
- Song *song;
- const char *shortname = mpd_basename(name);
-
- if (!(song = newSong(shortname, directory)))
- return -1;
- songvec_add(&directory->songs, song);
- LOG("added %s\n", name);
- return 1;
- } else if (S_ISDIR(st.st_mode)) {
- return addSubDirectoryToDirectory(directory, name, &st);
- }
-
- DEBUG("addToDirectory: %s is not a directory or music\n", name);
-
- return -1;
-}
-
-void directory_finish(void)
-{
- freeDirectory(music_root);
-}
-
-int isRootDirectory(const char *name)
-{
- return (!name || name[0] == '\0' || !strcmp(name, "/"));
-}
-
-static Directory *getSubDirectory(Directory * directory, const char *name)
-{
- Directory *cur = directory;
- Directory *found = NULL;
+ struct directory *cur = dir;
+ struct directory *found = NULL;
char *duplicated;
char *locate;
+ assert(name != NULL);
+
if (isRootDirectory(name))
- return directory;
+ return dir;
duplicated = xstrdup(name);
locate = strchr(duplicated, '/');
while (1) {
if (locate)
*locate = '\0';
- if (!(found = dirvec_find(&cur->children, duplicated)))
+ if (!(found = directory_get_child(cur, duplicated)))
break;
assert(cur == found->parent);
cur = found;
@@ -690,407 +94,40 @@ static Directory *getSubDirectory(Directory * directory, const char *name)
return found;
}
-static Directory *getDirectory(const char *name)
-{
- return getSubDirectory(music_root, name);
-}
-
-static int printDirectoryList(int fd, struct dirvec *dv)
-{
- size_t i;
-
- for (i = 0; i < dv->nr; ++i) {
- if (fdprintf(fd, DIRECTORY_DIR "%s\n",
- getDirectoryPath(dv->base[i])) < 0)
- return -1;
- }
-
- return 0;
-}
-
-int printDirectoryInfo(int fd, const char *name)
-{
- Directory *directory;
-
- if ((directory = getDirectory(name)) == NULL)
- return -1;
-
- printDirectoryList(fd, &directory->children);
- songvec_for_each(&directory->songs,
- song_print_info_x, (void *)(size_t)fd);
-
- return 0;
-}
-
-static int directory_song_write(Song *song, void *data)
-{
- int fd = (int)(size_t)data;
-
- if (fdprintf(fd, SONG_KEY "%s\n", song->url) < 0)
- return -1;
- if (song_print_info(song, fd) < 0)
- return -1;
- if (fdprintf(fd, SONG_MTIME "%li\n", (long)song->mtime) < 0)
- return -1;
-
- return 0;
-}
-
-/* TODO error checking */
-static int writeDirectoryInfo(int fd, Directory * directory)
-{
- struct dirvec *children = &directory->children;
- size_t i;
-
- if (directory->path &&
- fdprintf(fd, DIRECTORY_BEGIN "%s\n",
- getDirectoryPath(directory)) < 0)
- return -1;
-
- for (i = 0; i < children->nr; ++i) {
- Directory *cur = children->base[i];
- const char *base = mpd_basename(cur->path);
-
- if (fdprintf(fd, DIRECTORY_DIR "%s\n", base) < 0)
- return -1;
- if (writeDirectoryInfo(fd, cur) < 0)
- return -1;
- }
-
- if (fdprintf(fd, SONG_BEGIN "\n") < 0)
- return -1;
-
- if (songvec_for_each(&directory->songs,
- directory_song_write, (void *)(size_t)fd) < 0)
- return -1;
-
- if (fdprintf(fd, SONG_END "\n") < 0)
- return -1;
-
- if (directory->path &&
- fdprintf(fd, DIRECTORY_END "%s\n",
- getDirectoryPath(directory)) < 0)
- return -1;
- return 0;
-}
-
-static void readDirectoryInfo(FILE * fp, Directory * directory)
-{
- char buffer[MPD_PATH_MAX * 2];
- int bufferSize = MPD_PATH_MAX * 2;
- char key[MPD_PATH_MAX * 2];
- char *name;
-
- while (myFgets(buffer, bufferSize, fp)
- && prefixcmp(buffer, DIRECTORY_END)) {
- if (!prefixcmp(buffer, DIRECTORY_DIR)) {
- Directory *subdir;
-
- strcpy(key, &(buffer[strlen(DIRECTORY_DIR)]));
- if (!myFgets(buffer, bufferSize, fp))
- FATAL("Error reading db, fgets\n");
- /* for compatibility with db's prior to 0.11 */
- if (!prefixcmp(buffer, DIRECTORY_MTIME)) {
- if (!myFgets(buffer, bufferSize, fp))
- FATAL("Error reading db, fgets\n");
- }
- if (prefixcmp(buffer, DIRECTORY_BEGIN))
- FATAL("Error reading db at line: %s\n", buffer);
- name = &(buffer[strlen(DIRECTORY_BEGIN)]);
- if ((subdir = getDirectory(name))) {
- assert(subdir->parent == directory);
- } else {
- subdir = newDirectory(name, directory);
- dirvec_add(&directory->children, subdir);
- }
- readDirectoryInfo(fp, subdir);
- } else if (!prefixcmp(buffer, SONG_BEGIN)) {
- readSongInfoIntoList(fp, directory);
- } else {
- FATAL("Unknown line in db: %s\n", buffer);
- }
- }
-}
-
-static void sortDirectory(Directory * directory)
+void directory_sort(struct directory *dir)
{
int i;
- struct dirvec *dv = &directory->children;
+ struct dirvec *dv = &dir->children;
dirvec_sort(dv);
- songvec_sort(&directory->songs);
+ songvec_sort(&dir->songs);
for (i = dv->nr; --i >= 0; )
- sortDirectory(dv->base[i]);
-}
-
-int checkDirectoryDB(void)
-{
- struct stat st;
- char *dbFile = getDbFile();
-
- /* Check if the file exists */
- if (access(dbFile, F_OK)) {
- /* If the file doesn't exist, we can't check if we can write
- * it, so we are going to try to get the directory path, and
- * see if we can write a file in that */
- char dirPath[MPD_PATH_MAX];
- parent_path(dirPath, dbFile);
- if (*dirPath == '\0')
- strcpy(dirPath, "/");
-
- /* Check that the parent part of the path is a directory */
- if (stat(dirPath, &st) < 0) {
- ERROR("Couldn't stat parent directory of db file "
- "\"%s\": %s\n", dbFile, strerror(errno));
- return -1;
- }
-
- if (!S_ISDIR(st.st_mode)) {
- ERROR("Couldn't create db file \"%s\" because the "
- "parent path is not a directory\n", dbFile);
- return -1;
- }
-
- /* Check if we can write to the directory */
- if (access(dirPath, R_OK | W_OK)) {
- ERROR("Can't create db file in \"%s\": %s\n", dirPath,
- strerror(errno));
- return -1;
- }
-
- return 0;
- }
-
- /* Path exists, now check if it's a regular file */
- if (stat(dbFile, &st) < 0) {
- ERROR("Couldn't stat db file \"%s\": %s\n", dbFile,
- strerror(errno));
- return -1;
- }
-
- if (!S_ISREG(st.st_mode)) {
- ERROR("db file \"%s\" is not a regular file\n", dbFile);
- return -1;
- }
-
- /* And check that we can write to it */
- if (access(dbFile, R_OK | W_OK)) {
- ERROR("Can't open db file \"%s\" for reading/writing: %s\n",
- dbFile, strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-int writeDirectoryDB(void)
-{
- int fd;
- char *dbFile = getDbFile();
- struct stat st;
-
- DEBUG("removing empty directories from DB\n");
- deleteEmptyDirectoriesInDirectory(music_root);
-
- DEBUG("sorting DB\n");
-
- sortDirectory(music_root);
-
- DEBUG("writing DB\n");
-
- fd = open(dbFile, O_WRONLY|O_TRUNC|O_CREAT, 0666);
- if (fd < 0) {
- ERROR("unable to write to db file \"%s\": %s\n",
- dbFile, strerror(errno));
- return -1;
- }
-
- /*
- * TODO: block signals when writing the db so we don't get a corrupted
- * db (or unexpected failures). fdprintf() needs better error handling
- */
- fdprintf(fd,
- DIRECTORY_INFO_BEGIN "\n"
- DIRECTORY_MPD_VERSION VERSION "\n"
- DIRECTORY_FS_CHARSET "%s\n"
- DIRECTORY_INFO_END "\n", getFsCharset());
-
- if (writeDirectoryInfo(fd, music_root) < 0) {
- ERROR("Failed to write to database file: %s\n",
- strerror(errno));
- xclose(fd);
- return -1;
- }
- xclose(fd);
-
- if (stat(dbFile, &st) == 0)
- directory_dbModTime = st.st_mtime;
-
- return 0;
-}
-
-int readDirectoryDB(void)
-{
- FILE *fp = NULL;
- char *dbFile = getDbFile();
- struct stat st;
-
- if (!music_root)
- music_root = newDirectory(NULL, NULL);
- while (!(fp = fopen(dbFile, "r")) && errno == EINTR) ;
- if (fp == NULL) {
- ERROR("unable to open db file \"%s\": %s\n",
- dbFile, strerror(errno));
- return -1;
- }
-
- /* get initial info */
- {
- char buffer[100];
- int bufferSize = 100;
- int foundFsCharset = 0;
- int foundVersion = 0;
-
- if (!myFgets(buffer, bufferSize, fp))
- FATAL("Error reading db, fgets\n");
- if (0 == strcmp(DIRECTORY_INFO_BEGIN, buffer)) {
- while (myFgets(buffer, bufferSize, fp) &&
- 0 != strcmp(DIRECTORY_INFO_END, buffer)) {
- if (!prefixcmp(buffer, DIRECTORY_MPD_VERSION))
- {
- if (foundVersion)
- FATAL("already found version in db\n");
- foundVersion = 1;
- } else if (!prefixcmp(buffer,
- DIRECTORY_FS_CHARSET)) {
- char *fsCharset;
- char *tempCharset;
-
- if (foundFsCharset)
- FATAL("already found fs charset in db\n");
-
- foundFsCharset = 1;
-
- fsCharset = &(buffer[strlen(DIRECTORY_FS_CHARSET)]);
- if ((tempCharset = getConfigParamValue(CONF_FS_CHARSET))
- && strcmp(fsCharset, tempCharset)) {
- WARNING("Using \"%s\" for the "
- "filesystem charset "
- "instead of \"%s\"\n",
- fsCharset, tempCharset);
- WARNING("maybe you need to "
- "recreate the db?\n");
- setFsCharset(fsCharset);
- }
- } else {
- FATAL("directory: unknown line in db info: %s\n",
- buffer);
- }
- }
- } else {
- ERROR("db info not found in db file\n");
- ERROR("you should recreate the db using --create-db\n");
- while (fclose(fp) && errno == EINTR) ;
- return -1;
- }
- }
-
- DEBUG("reading DB\n");
-
- readDirectoryInfo(fp, music_root);
- while (fclose(fp) && errno == EINTR) ;
-
- stats.numberOfSongs = countSongsIn(NULL);
- stats.dbPlayTime = sumSongTimesIn(NULL);
-
- if (stat(dbFile, &st) == 0)
- directory_dbModTime = st.st_mtime;
-
- return 0;
+ directory_sort(dv->base[i]);
}
-static int traverseAllInSubDirectory(Directory * directory,
- int (*forEachSong) (Song *, void *),
- int (*forEachDir) (Directory *, void *),
- void *data)
+int
+directory_walk(struct directory *dir,
+ int (*forEachSong) (struct mpd_song *, void *),
+ int (*forEachDir) (struct directory *, void *),
+ void *data)
{
- struct dirvec *dv = &directory->children;
+ struct dirvec *dv = &dir->children;
int err = 0;
size_t j;
- if (forEachDir && (err = forEachDir(directory, data)) < 0)
+ if (forEachDir && (err = forEachDir(dir, data)) < 0)
return err;
if (forEachSong) {
- err = songvec_for_each(&directory->songs, forEachSong, data);
+ err = songvec_for_each(&dir->songs, forEachSong, data);
if (err < 0)
return err;
}
for (j = 0; err >= 0 && j < dv->nr; ++j)
- err = traverseAllInSubDirectory(dv->base[j], forEachSong,
+ err = directory_walk(dv->base[j], forEachSong,
forEachDir, data);
return err;
}
-
-int traverseAllIn(const char *name,
- int (*forEachSong) (Song *, void *),
- int (*forEachDir) (Directory *, void *), void *data)
-{
- Directory *directory;
-
- if ((directory = getDirectory(name)) == NULL) {
- Song *song;
- if ((song = getSongFromDB(name)) && forEachSong) {
- return forEachSong(song, data);
- }
- return -1;
- }
-
- return traverseAllInSubDirectory(directory, forEachSong, forEachDir,
- data);
-}
-
-void directory_init(void)
-{
- music_root = newDirectory(NULL, NULL);
- exploreDirectory(music_root);
- stats.numberOfSongs = countSongsIn(NULL);
- stats.dbPlayTime = sumSongTimesIn(NULL);
-}
-
-Song *getSongFromDB(const char *file)
-{
- Song *song = NULL;
- Directory *directory;
- char *dir = NULL;
- char *duplicated = xstrdup(file);
- char *shortname = strrchr(duplicated, '/');
-
- DEBUG("get song: %s\n", file);
-
- if (!shortname) {
- shortname = duplicated;
- } else {
- *shortname = '\0';
- ++shortname;
- dir = duplicated;
- }
-
- if (!(directory = getDirectory(dir)))
- goto out;
- if (!(song = songvec_find(&directory->songs, shortname)))
- goto out;
- assert(song->parentDir == directory);
-
-out:
- free(duplicated);
- return song;
-}
-
-time_t getDbModTime(void)
-{
- return directory_dbModTime;
-}
diff --git a/src/directory.h b/src/directory.h
index 20b784166..949df0c0e 100644
--- a/src/directory.h
+++ b/src/directory.h
@@ -20,56 +20,83 @@
#define DIRECTORY_H
#include "song.h"
+#include "dirvec.h"
#include "songvec.h"
-struct dirvec {
- struct _Directory **base;
- size_t nr;
-};
+#define DIRECTORY_DIR "directory: "
+#define DIRECTORY_MTIME "mtime: " /* DEPRECATED, noop-read-only */
+#define DIRECTORY_BEGIN "begin: "
+#define DIRECTORY_END "end: "
+#define DIRECTORY_INFO_BEGIN "info_begin"
+#define DIRECTORY_INFO_END "info_end"
+#define DIRECTORY_MPD_VERSION "mpd_version: "
+#define DIRECTORY_FS_CHARSET "fs_charset: "
-typedef struct _Directory {
+struct directory {
char *path;
struct dirvec children;
struct songvec songs;
- struct _Directory *parent;
+ struct directory *parent;
ino_t inode;
dev_t device;
unsigned stat; /* not needed if ino_t == dev_t == 0 is impossible */
-} Directory;
+};
-void reap_update_task(void);
+static inline int isRootDirectory(const char *name)
+{
+ /* TODO: verify and remove !name check */
+ return (!name || *name == '\0' || !strcmp(name, "/"));
+}
-int isUpdatingDB(void);
+struct directory * directory_new(const char *dirname, struct directory *parent);
-/*
- * returns the non-negative update job ID on success,
- * returns -1 if busy
- * @path will be freed by this function and should not be reused
- */
-int directory_update_init(char *path);
+void directory_free(struct directory *dir);
+
+static inline int directory_is_empty(struct directory *dir)
+{
+ return dir->children.nr == 0 && dir->songs.nr == 0;
+}
+
+static inline const char * directory_get_path(struct directory *dir)
+{
+ return dir->path;
+}
-void directory_init(void);
+static inline struct directory *
+directory_get_child(const struct directory *dir, const char *name)
+{
+ return dirvec_find(&dir->children, name);
+}
-void directory_finish(void);
+static inline struct directory *
+directory_new_child(struct directory *dir, const char *name)
+{
+ struct directory *subdir = directory_new(name, dir);
+ dirvec_add(&dir->children, subdir);
+ return subdir;
+}
-int isRootDirectory(const char *name);
+void directory_prune_empty(struct directory *dir);
-int printDirectoryInfo(int fd, const char *dirname);
+struct directory *
+directory_get_subdir(struct directory *dir, const char *name);
-int checkDirectoryDB(void);
+int directory_print(int fd, const struct directory *dir);
-int writeDirectoryDB(void);
+struct mpd_song *db_get_song(const char *file);
-int readDirectoryDB(void);
+int directory_save(int fd, struct directory *dir);
-Song *getSongFromDB(const char *file);
+void directory_load(FILE *fp, struct directory *dir);
-time_t getDbModTime(void);
+void directory_sort(struct directory *dir);
-int traverseAllIn(const char *name,
- int (*forEachSong) (Song *, void *),
- int (*forEachDir) (Directory *, void *), void *data);
+int db_walk(const char *name,
+ int (*forEachSong) (struct mpd_song *, void *),
+ int (*forEachDir) (struct directory *, void *), void *data);
-#define getDirectoryPath(dir) ((dir && dir->path) ? dir->path : "")
+int directory_walk(struct directory *dir,
+ int (*forEachSong) (struct mpd_song *, void *),
+ int (*forEachDir) (struct directory *, void *), void *data);
#endif
diff --git a/src/directory_print.c b/src/directory_print.c
new file mode 100644
index 000000000..1c30f1608
--- /dev/null
+++ b/src/directory_print.c
@@ -0,0 +1,47 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * Copyright (C) 2008 Max Kellermann <max@duempel.org>
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "directory_print.h"
+#include "directory.h"
+#include "dirvec.h"
+#include "songvec.h"
+#include "myfprintf.h"
+
+static int dirvec_print(int fd, const struct dirvec *dv)
+{
+ 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 0;
+}
+
+int directory_print(int fd, const struct directory *dir)
+{
+ if (dirvec_print(fd, &dir->children) < 0)
+ return -1;
+ if (songvec_for_each(&dir->songs, song_print_info_x,
+ (void *)(size_t)fd) < 0)
+ return -1;
+ return 0;
+}
diff --git a/src/directory_print.h b/src/directory_print.h
new file mode 100644
index 000000000..a55c3672d
--- /dev/null
+++ b/src/directory_print.h
@@ -0,0 +1,26 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DIRECTORY_PRINT_H
+#define DIRECTORY_PRINT_H
+
+struct directory;
+
+int directory_print(int fd, const struct directory *dir);
+
+#endif
diff --git a/src/directory_save.c b/src/directory_save.c
new file mode 100644
index 000000000..c39ece58a
--- /dev/null
+++ b/src/directory_save.c
@@ -0,0 +1,120 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "directory_save.h"
+#include "directory.h"
+#include "song.h"
+#include "log.h"
+#include "path.h"
+#include "utils.h"
+#include "dirvec.h"
+#include "myfprintf.h"
+
+static int directory_song_write(struct mpd_song *song, void *data)
+{
+ int fd = (int)(size_t)data;
+
+ if (fdprintf(fd, SONG_KEY "%s\n", song->url) < 0)
+ return -1;
+ if (song_print_info(song, fd) < 0)
+ return -1;
+ if (fdprintf(fd, SONG_MTIME "%li\n", (long)song->mtime) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* TODO error checking */
+int directory_save(int fd, struct directory *dir)
+{
+ struct dirvec *children = &dir->children;
+ size_t i;
+
+ if (!isRootDirectory(dir->path) &&
+ fdprintf(fd, DIRECTORY_BEGIN "%s\n",
+ directory_get_path(dir)) < 0)
+ return -1;
+
+ for (i = 0; i < children->nr; ++i) {
+ struct directory *cur = children->base[i];
+ const char *base = mpd_basename(cur->path);
+
+ if (fdprintf(fd, DIRECTORY_DIR "%s\n", base) < 0)
+ return -1;
+ if (directory_save(fd, cur) < 0)
+ return -1;
+ }
+
+ if (fdprintf(fd, SONG_BEGIN "\n") < 0)
+ return -1;
+
+ if (songvec_for_each(&dir->songs,
+ directory_song_write, (void *)(size_t)fd) < 0)
+ return -1;
+
+ if (fdprintf(fd, SONG_END "\n") < 0)
+ return -1;
+
+ if (!isRootDirectory(dir->path) &&
+ fdprintf(fd, DIRECTORY_END "%s\n",
+ directory_get_path(dir)) < 0)
+ return -1;
+ return 0;
+}
+
+void directory_load(FILE * fp, struct directory *dir)
+{
+ char buffer[MPD_PATH_MAX * 2];
+ int bufferSize = MPD_PATH_MAX * 2;
+ char key[MPD_PATH_MAX * 2];
+ char *name;
+
+ while (myFgets(buffer, bufferSize, fp)
+ && prefixcmp(buffer, DIRECTORY_END)) {
+ if (!prefixcmp(buffer, DIRECTORY_DIR)) {
+ struct directory *subdir;
+
+ strcpy(key, &(buffer[strlen(DIRECTORY_DIR)]));
+ if (!myFgets(buffer, bufferSize, fp))
+ FATAL("Error reading db, fgets\n");
+ /* for compatibility with db's prior to 0.11 */
+ if (!prefixcmp(buffer, DIRECTORY_MTIME)) {
+ if (!myFgets(buffer, bufferSize, fp))
+ FATAL("Error reading db, fgets\n");
+ }
+ if (prefixcmp(buffer, DIRECTORY_BEGIN))
+ FATAL("Error reading db at line: %s\n", buffer);
+ name = &(buffer[strlen(DIRECTORY_BEGIN)]);
+ if (prefixcmp(name, dir->path) != 0)
+ FATAL("Wrong path in database: '%s' in '%s'\n",
+ name, dir->path);
+
+ if ((subdir = directory_get_child(dir, name))) {
+ assert(subdir->parent == dir);
+ } else {
+ subdir = directory_new(name, dir);
+ dirvec_add(&dir->children, subdir);
+ }
+ directory_load(fp, subdir);
+ } else if (!prefixcmp(buffer, SONG_BEGIN)) {
+ readSongInfoIntoList(fp, dir);
+ } else {
+ FATAL("Unknown line in db: %s\n", buffer);
+ }
+ }
+}
diff --git a/src/directory_save.h b/src/directory_save.h
new file mode 100644
index 000000000..64364d82d
--- /dev/null
+++ b/src/directory_save.h
@@ -0,0 +1,30 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef DIRECTORY_SAVE_H
+#define DIRECTORY_SAVE_H
+
+#include "os_compat.h"
+
+struct directory;
+
+int directory_save(int fd, struct directory *dir);
+
+void directory_load(FILE *, struct directory *dir);
+
+#endif /* DIRECTORY_SAVE_H */
diff --git a/src/dirvec.c b/src/dirvec.c
new file mode 100644
index 000000000..fdfbb3434
--- /dev/null
+++ b/src/dirvec.c
@@ -0,0 +1,70 @@
+#include "dirvec.h"
+#include "directory.h"
+#include "os_compat.h"
+#include "utils.h"
+
+static size_t dv_size(struct dirvec *dv)
+{
+ return dv->nr * sizeof(struct directory *);
+}
+
+/* Only used for sorting/searching a dirvec, not general purpose compares */
+static int dirvec_cmp(const void *d1, const void *d2)
+{
+ const struct directory *a = ((const struct directory * const *)d1)[0];
+ const struct directory *b = ((const struct directory * const *)d2)[0];
+ 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;
+
+ for (i = dv->nr; --i >= 0; )
+ if (!strcmp(dv->base[i]->path, path))
+ return dv->base[i];
+ return NULL;
+}
+
+int dirvec_delete(struct dirvec *dv, struct directory *del)
+{
+ int i;
+
+ for (i = dv->nr; --i >= 0; ) {
+ if (dv->base[i] != del)
+ continue;
+ /* we _don't_ call directory_free() here */
+ if (!--dv->nr) {
+ free(dv->base);
+ dv->base = NULL;
+ } 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;
+ }
+
+ return -1; /* not found */
+}
+
+void dirvec_add(struct dirvec *dv, struct directory *add)
+{
+ ++dv->nr;
+ dv->base = xrealloc(dv->base, dv_size(dv));
+ dv->base[dv->nr - 1] = add;
+}
+
+void dirvec_destroy(struct dirvec *dv)
+{
+ if (dv->base) {
+ free(dv->base);
+ dv->base = NULL;
+ }
+ dv->nr = 0;
+}
diff --git a/src/dirvec.h b/src/dirvec.h
index 8b2f634e2..02496cd2b 100644
--- a/src/dirvec.h
+++ b/src/dirvec.h
@@ -1,73 +1,26 @@
#ifndef DIRVEC_H
#define DIRVEC_H
-#include "directory.h"
#include "os_compat.h"
-#include "utils.h"
-static size_t dv_size(struct dirvec *dv)
-{
- return dv->nr * sizeof(Directory *);
-}
+struct dirvec {
+ struct directory **base;
+ size_t nr;
+};
-/* Only used for sorting/searching a dirvec, not general purpose compares */
-static int dirvec_cmp(const void *d1, const void *d2)
-{
- const Directory *a = ((const Directory * const *)d1)[0];
- const Directory *b = ((const Directory * const *)d2)[0];
- return strcmp(a->path, b->path);
-}
+void dirvec_sort(struct dirvec *dv);
-static void dirvec_sort(struct dirvec *dv)
-{
- qsort(dv->base, dv->nr, sizeof(Directory *), dirvec_cmp);
-}
+struct directory *dirvec_find(const struct dirvec *dv, const char *path);
-static Directory *dirvec_find(struct dirvec *dv, const char *path)
-{
- int i;
+int dirvec_delete(struct dirvec *dv, struct directory *del);
- for (i = dv->nr; --i >= 0; )
- if (!strcmp(dv->base[i]->path, path))
- return dv->base[i];
- return NULL;
-}
+void dirvec_add(struct dirvec *dv, struct directory *add);
-static int dirvec_delete(struct dirvec *dv, Directory *del)
+static inline void dirvec_clear(struct dirvec *dv)
{
- int i;
-
- for (i = dv->nr; --i >= 0; ) {
- if (dv->base[i] != del)
- continue;
- /* we _don't_ call freeDirectory() here */
- if (!--dv->nr) {
- free(dv->base);
- dv->base = NULL;
- } else {
- memmove(&dv->base[i], &dv->base[i + 1],
- (dv->nr - i + 1) * sizeof(Directory *));
- dv->base = xrealloc(dv->base, dv_size(dv));
- }
- return i;
- }
-
- return -1; /* not found */
+ dv->nr = 0;
}
-static void dirvec_add(struct dirvec *dv, Directory *add)
-{
- ++dv->nr;
- dv->base = xrealloc(dv->base, dv_size(dv));
- dv->base[dv->nr - 1] = add;
-}
+void dirvec_destroy(struct dirvec *dv);
-static void dirvec_destroy(struct dirvec *dv)
-{
- if (dv->base) {
- free(dv->base);
- dv->base = NULL;
- }
- dv->nr = 0;
-}
#endif /* DIRVEC_H */
diff --git a/src/inputPlugin.c b/src/inputPlugin.c
index 1ab118ceb..2bb05c018 100644
--- a/src/inputPlugin.c
+++ b/src/inputPlugin.c
@@ -17,6 +17,7 @@
*/
#include "inputPlugin.h"
+#include "list.h"
static List *inputPlugin_list;
diff --git a/src/inputPlugin.h b/src/inputPlugin.h
index 0fd39ea9f..4338ce471 100644
--- a/src/inputPlugin.h
+++ b/src/inputPlugin.h
@@ -23,6 +23,8 @@
#include "outputBuffer.h"
#include "metadata_pipe.h"
#include "decode.h"
+#include "tag.h"
+#include "tag_id3.h"
/* valid values for streamTypes in the InputPlugin struct: */
#define INPUT_PLUGIN_STREAM_FILE 0x01
diff --git a/src/locate.c b/src/locate.c
index 6101dd95b..96fa4ee58 100644
--- a/src/locate.c
+++ b/src/locate.c
@@ -121,7 +121,8 @@ void freeLocateTagItem(LocateTagItem * item)
free(item);
}
-static int strstrSearchTag(Song * song, enum tag_type type, char *str)
+static int strstrSearchTag(struct mpd_song * song,
+ enum tag_type type, char *str)
{
int i;
char *duplicate;
@@ -131,7 +132,7 @@ static int strstrSearchTag(Song * song, enum tag_type type, char *str)
if (type == LOCATE_TAG_FILE_TYPE || type == LOCATE_TAG_ANY_TYPE) {
char path_max_tmp[MPD_PATH_MAX];
- string_toupper(get_song_url(path_max_tmp, song));
+ string_toupper(song_get_url(song, path_max_tmp));
if (strstr(path_max_tmp, str))
ret = 1;
if (ret == 1 || type == LOCATE_TAG_FILE_TYPE)
@@ -166,7 +167,8 @@ static int strstrSearchTag(Song * song, enum tag_type type, char *str)
return ret;
}
-int strstrSearchTags(Song * song, int numItems, LocateTagItem * items)
+int
+strstrSearchTags(struct mpd_song * song, int numItems, LocateTagItem * items)
{
int i;
@@ -180,14 +182,15 @@ int strstrSearchTags(Song * song, int numItems, LocateTagItem * items)
return 1;
}
-static int tagItemFoundAndMatches(Song * song, enum tag_type type, char *str)
+static int
+tagItemFoundAndMatches(struct mpd_song * song, enum tag_type type, char *str)
{
int i;
int8_t visitedTypes[TAG_NUM_OF_ITEM_TYPES] = { 0 };
if (type == LOCATE_TAG_FILE_TYPE || type == LOCATE_TAG_ANY_TYPE) {
char path_max_tmp[MPD_PATH_MAX];
- if (0 == strcmp(str, get_song_url(path_max_tmp, song)))
+ if (0 == strcmp(str, song_get_url(song, path_max_tmp)))
return 1;
if (type == LOCATE_TAG_FILE_TYPE)
return 0;
@@ -220,7 +223,8 @@ static int tagItemFoundAndMatches(Song * song, enum tag_type type, char *str)
}
-int tagItemsFoundAndMatches(Song * song, int numItems, LocateTagItem * items)
+int tagItemsFoundAndMatches(struct mpd_song * song,
+ int numItems, LocateTagItem * items)
{
int i;
diff --git a/src/locate.h b/src/locate.h
index 7a817828a..0ccc497ff 100644
--- a/src/locate.h
+++ b/src/locate.h
@@ -44,8 +44,10 @@ void freeLocateTagItemArray(int count, LocateTagItem * array);
void freeLocateTagItem(LocateTagItem * item);
-int strstrSearchTags(Song * song, int numItems, LocateTagItem * items);
+int strstrSearchTags(struct mpd_song * song,
+ int numItems, LocateTagItem * items);
-int tagItemsFoundAndMatches(Song * song, int numItems, LocateTagItem * items);
+int tagItemsFoundAndMatches(struct mpd_song * song,
+ int numItems, LocateTagItem * items);
#endif
diff --git a/src/log.h b/src/log.h
index 4026df282..734c4756c 100644
--- a/src/log.h
+++ b/src/log.h
@@ -19,9 +19,7 @@
#ifndef LOG_H
#define LOG_H
-#include "../config.h"
#include "gcc.h"
-#include "os_compat.h"
#define LOG_LEVEL_LOW 0
#define LOG_LEVEL_SECURE 1
diff --git a/src/main.c b/src/main.c
index c0acc260b..f1c288dd5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -19,7 +19,8 @@
#include "client.h"
#include "command.h"
#include "playlist.h"
-#include "directory.h"
+#include "database.h"
+#include "update.h"
#include "listen.h"
#include "conf.h"
#include "path.h"
@@ -268,17 +269,17 @@ static void changeToUser(void)
static void openDB(Options * options, char *argv0)
{
- if (options->createDB > 0 || readDirectoryDB() < 0) {
+ if (options->createDB > 0 || db_load() < 0) {
if (options->createDB < 0) {
FATAL("can't open db file and using "
"\"--no-create-db\" command line option\n"
"try running \"%s --create-db\"\n", argv0);
}
flushWarningLog();
- if (checkDirectoryDB() < 0)
+ if (db_check() < 0)
exit(EXIT_FAILURE);
- directory_init();
- if (writeDirectoryDB() < 0)
+ db_init();
+ if (db_save() < 0)
exit(EXIT_FAILURE);
if (options->createDB)
exit(EXIT_SUCCESS);
@@ -408,6 +409,8 @@ int main(int argc, char *argv[])
initPlaylist();
initInputPlugins();
+ init_main_notify();
+
openDB(&options, argv[0]);
initCommands();
@@ -423,7 +426,6 @@ int main(int argc, char *argv[])
daemonize(&options);
- init_main_notify();
init_output_buffer();
setup_log_output(options.stdOutput);
@@ -450,8 +452,8 @@ int main(int argc, char *argv[])
finishPlaylist();
start = clock();
- directory_finish();
- DEBUG("directory_finish took %f seconds\n",
+ db_finish();
+ DEBUG("db_finish took %f seconds\n",
((float)(clock()-start))/CLOCKS_PER_SEC);
finishNormalization();
diff --git a/src/playlist.c b/src/playlist.c
index 696636993..8a7b5cf7f 100644
--- a/src/playlist.c
+++ b/src/playlist.c
@@ -22,7 +22,7 @@
#include "ls.h"
#include "tag.h"
#include "conf.h"
-#include "directory.h"
+#include "database.h"
#include "log.h"
#include "path.h"
#include "utils.h"
@@ -41,7 +41,7 @@ enum _playlist_state {
static enum _playlist_state playlist_state;
struct _playlist {
- Song **songs;
+ struct mpd_song **songs;
/* holds version a song was modified on */
uint32_t *songMod;
int *order;
@@ -157,7 +157,8 @@ void initPlaylist(void)
playlist_saveAbsolutePaths =
DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS;
- playlist.songs = xcalloc(playlist_max_length, sizeof(Song *));
+ playlist.songs = xcalloc(playlist_max_length,
+ sizeof(struct mpd_song *));
playlist.songMod = xmalloc(sizeof(uint32_t) * playlist_max_length);
playlist.order = xmalloc(sizeof(int) * playlist_max_length);
playlist.idToPosition = xmalloc(sizeof(int) * playlist_max_length *
@@ -188,7 +189,7 @@ void finishPlaylist(void)
for (i = playlist.length; --i >= 0; ) {
if (!song_is_file(playlist.songs[i]))
- freeJustSong(playlist.songs[i]);
+ song_free(playlist.songs[i]);
}
playlist.length = 0;
@@ -213,7 +214,7 @@ void clearPlaylist(void)
for (i = playlist.length; --i >= 0 ; ) {
if (!song_is_file(playlist.songs[i]))
- freeJustSong(playlist.songs[i]);
+ song_free(playlist.songs[i]);
playlist.idToPosition[playlist.positionToId[i]] = -1;
playlist.songs[i] = NULL;
}
@@ -235,7 +236,7 @@ void showPlaylist(int fd)
for (i = 0; i < playlist.length; i++)
fdprintf(fd, "%i:%s\n", i,
- get_song_url(path_max_tmp, playlist.songs[i]));
+ song_get_url(playlist.songs[i], path_max_tmp));
}
void savePlaylistState(int fd)
@@ -440,7 +441,7 @@ enum playlist_result playlistId(int fd, int id)
static void swapSongs(int song1, int song2)
{
- Song *sTemp;
+ struct mpd_song *sTemp;
int iTemp;
assert(song1 < playlist.length);
@@ -463,7 +464,7 @@ static void swapSongs(int song1, int song2)
playlist.positionToId[song2] = iTemp;
}
-static Song *song_at(int order_num)
+static struct mpd_song *song_at(int order_num)
{
if (order_num >= 0 && order_num < playlist.length) {
assert(playlist.songs[playlist.order[order_num]]);
@@ -536,13 +537,13 @@ void playlist_queue_next(void)
char *playlist_queued_url(char utf8url[MPD_PATH_MAX])
{
- Song *song;
+ struct mpd_song *song;
assert(pthread_equal(pthread_self(), dc.thread));
pthread_mutex_lock(&queue_lock);
song = song_at(playlist.queued);
- return song ? get_song_url(utf8url, song) : NULL;
+ return song ? song_get_url(song, utf8url) : NULL;
}
static void queue_song_locked(int order_num)
@@ -570,13 +571,13 @@ static int clear_queue(void)
enum playlist_result addToPlaylist(const char *url, int *added_id)
{
- Song *song;
+ struct mpd_song *song;
DEBUG("add to playlist: %s\n", url);
- if ((song = getSongFromDB(url))) {
+ if ((song = db_get_song(url))) {
} else if (!(isValidRemoteUtf8Url(url) &&
- (song = newSong(url, NULL)))) {
+ (song = song_remote_new(url)))) {
return PLAYLIST_RESULT_NO_SUCH_SONG;
}
@@ -585,26 +586,26 @@ enum playlist_result addToPlaylist(const char *url, int *added_id)
int addToStoredPlaylist(const char *url, const char *utf8file)
{
- Song *song;
+ struct mpd_song *song;
DEBUG("add to stored playlist: %s\n", url);
- if ((song = getSongFromDB(url)))
+ if ((song = db_get_song(url)))
return appendSongToStoredPlaylistByPath(utf8file, song);
if (!isValidRemoteUtf8Url(url))
return ACK_ERROR_NO_EXIST;
- if ((song = newSong(url, NULL))) {
+ if ((song = song_remote_new(url))) {
int ret = appendSongToStoredPlaylistByPath(utf8file, song);
- freeJustSong(song);
+ song_free(song);
return ret;
}
return ACK_ERROR_NO_EXIST;
}
-enum playlist_result addSongToPlaylist(Song * song, int *added_id)
+enum playlist_result addSongToPlaylist(struct mpd_song * song, int *added_id)
{
int id;
@@ -740,7 +741,7 @@ enum playlist_result deleteFromPlaylist(int song)
}
if (!song_is_file(playlist.songs[song]))
- freeJustSong(playlist.songs[song]);
+ song_free(playlist.songs[song]);
playlist.idToPosition[playlist.positionToId[song]] = -1;
@@ -798,7 +799,7 @@ enum playlist_result deleteFromPlaylistById(int id)
return deleteFromPlaylist(playlist.idToPosition[id]);
}
-void deleteASongFromPlaylist(const Song * song)
+void deleteASongFromPlaylist(const struct mpd_song * song)
{
int i;
@@ -841,7 +842,7 @@ static void play_order_num(int order_num, float seek_time)
assert(song_at(order_num));
DEBUG("playlist: play %i:\"%s\"\n", order_num,
- get_song_url(path, song_at(order_num)));
+ song_get_url(song_at(order_num), path));
dc_trigger_action(DC_ACTION_STOP, 0);
queue_song_locked(order_num);
@@ -917,7 +918,7 @@ enum playlist_result playPlaylistById(int id, int stopOnError)
/* This is used when we stream data out to shout while playing static files */
struct mpd_tag *playlist_current_tag(void)
{
- Song *song = song_at(playlist.current);
+ struct mpd_song *song = song_at(playlist.current);
/* Non-file song tags can get swept out from under us */
return (song && song_is_file(song)) ? song->tag : NULL;
@@ -926,7 +927,7 @@ struct mpd_tag *playlist_current_tag(void)
/* This receives dynamic metadata updates from streams */
static void sync_metadata(void)
{
- Song *song;
+ struct mpd_song *song;
struct mpd_tag *tag;
if (!(tag = metadata_pipe_current()))
@@ -998,7 +999,7 @@ void setPlaylistRepeatStatus(int status)
enum playlist_result moveSongInPlaylist(int from, int to)
{
int i;
- Song *tmpSong;
+ struct mpd_song *tmpSong;
int tmpId;
int currentSong;
int queued_is_current = (playlist.queued == playlist.current);
@@ -1145,11 +1146,7 @@ static void randomizeOrder(int start, int end)
void setPlaylistRandomStatus(int status)
{
- int statusWas = playlist.random;
-
- playlist.random = status;
-
- if (status != statusWas) {
+ if (status != playlist.random) {
if (playlist.random)
randomizeOrder(0, playlist.length - 1);
else
@@ -1262,7 +1259,7 @@ enum playlist_result savePlaylist(const char *utf8file)
for (i = 0; i < playlist.length; i++) {
char tmp[MPD_PATH_MAX];
- get_song_url(path_max_tmp, playlist.songs[i]);
+ song_get_url(playlist.songs[i], path_max_tmp);
utf8_to_fs_charset(tmp, path_max_tmp);
if (playlist_saveAbsolutePaths &&
@@ -1329,7 +1326,7 @@ enum playlist_result seekSongInPlaylist(int song, float seek_time)
*/
}
- DEBUG("playlist: seek %i:\"%s\"\n", i, get_song_url(path, song_at(i)));
+ DEBUG("playlist: seek %i:\"%s\"\n", i, song_get_url(song_at(i), path));
play_order_num(i, seek_time);
return PLAYLIST_RESULT_SUCCESS;
}
@@ -1361,7 +1358,7 @@ int PlaylistInfo(int fd, const char *utf8file, int detail)
int wrote = 0;
if (detail) {
- Song *song = getSongFromDB(temp);
+ struct mpd_song *song = db_get_song(temp);
if (song) {
song_print_info(song, fd);
wrote = 1;
diff --git a/src/playlist.h b/src/playlist.h
index 2be2711e5..f4df8ce41 100644
--- a/src/playlist.h
+++ b/src/playlist.h
@@ -57,7 +57,7 @@ enum playlist_result addToPlaylist(const char *file, int *added_id);
int addToStoredPlaylist(const char *file, const char *utf8file);
-enum playlist_result addSongToPlaylist(Song * song, int *added_id);
+enum playlist_result addSongToPlaylist(struct mpd_song * song, int *added_id);
void showPlaylist(int fd);
@@ -93,7 +93,7 @@ enum playlist_result savePlaylist(const char *utf8file);
enum playlist_result deletePlaylist(const char *utf8file);
-void deleteASongFromPlaylist(const Song * song);
+void deleteASongFromPlaylist(const struct mpd_song * song);
enum playlist_result moveSongInPlaylist(int from, int to);
diff --git a/src/sig_handlers.c b/src/sig_handlers.c
index e4ac21f22..5a4ebe57d 100644
--- a/src/sig_handlers.c
+++ b/src/sig_handlers.c
@@ -19,7 +19,8 @@
#include "sig_handlers.h"
#include "playlist.h"
-#include "directory.h"
+#include "database.h"
+#include "update.h"
#include "command.h"
#include "signal_check.h"
#include "log.h"
@@ -35,7 +36,7 @@ int handlePendingSignals(void)
DEBUG("got SIGHUP, rereading DB\n");
signal_clear(SIGHUP);
if (!isUpdatingDB()) {
- readDirectoryDB();
+ db_load();
playlistVersionChange();
}
if (cycle_log_files() < 0)
diff --git a/src/song.c b/src/song.c
index ddc3bc4bd..aa6641020 100644
--- a/src/song.c
+++ b/src/song.c
@@ -29,74 +29,69 @@
#include "os_compat.h"
-static Song * song_alloc(const char *url, Directory *parent)
+static struct mpd_song * song_alloc(const char *url, struct directory *parent)
{
size_t urllen;
- Song *song;
+ struct mpd_song *song;
assert(url);
urllen = strlen(url);
assert(urllen);
- song = xmalloc(sizeof(Song) + urllen);
+ song = xmalloc(sizeof(*song) - sizeof(song->url) + urllen + 1);
song->tag = NULL;
memcpy(song->url, url, urllen + 1);
- song->parentDir = parent;
+ song->parent = parent;
return song;
}
-Song *newSong(const char *url, Directory * parentDir)
+struct mpd_song * song_remote_new(const char *url)
{
- Song *song;
- assert(*url);
+ return song_alloc(url, NULL);
+}
- if (strchr(url, '\n')) {
- DEBUG("newSong: '%s' is not a valid uri\n", url);
- return NULL;
- }
+struct mpd_song * song_file_new(const char *path, struct directory *parent)
+{
+ assert(parent != NULL);
- song = song_alloc(url, parentDir);
+ return song_alloc(path, parent);
+}
- if (song_is_file(song)) {
- InputPlugin *plugin;
- unsigned int next = 0;
- char path_max_tmp[MPD_PATH_MAX];
- char abs_path[MPD_PATH_MAX];
+struct mpd_song * song_file_load(const char *path, struct directory *parent)
+{
+ struct mpd_song *song;
- utf8_to_fs_charset(abs_path, get_song_url(path_max_tmp, song));
- rmp2amp_r(abs_path, abs_path);
+ if (strchr(path, '\n')) {
+ DEBUG("song_file_load: '%s' is not a valid uri\n", path);
+ return NULL;
+ }
- while (!song->tag && (plugin = isMusic(abs_path,
- &(song->mtime),
- next++))) {
- song->tag = plugin->tagDupFunc(abs_path);
- }
- if (!song->tag || song->tag->time < 0) {
- freeJustSong(song);
- song = NULL;
- }
+ song = song_file_new(path, parent);
+ if (!song_file_update(song)) {
+ song_free(song);
+ return NULL;
}
return song;
}
-void freeJustSong(Song * song)
+void song_free(struct mpd_song * song)
{
if (song->tag)
tag_free(song->tag);
free(song);
}
-ssize_t song_print_url(Song *song, int fd)
+ssize_t song_print_url(struct mpd_song *song, int fd)
{
- if (song->parentDir && song->parentDir->path)
+ if (song->parent && song->parent->path)
return fdprintf(fd, "%s%s/%s\n", SONG_FILE,
- getDirectoryPath(song->parentDir), song->url);
+ directory_get_path(song->parent), song->url);
return fdprintf(fd, "%s%s\n", SONG_FILE, song->url);
}
-ssize_t song_print_info(Song *song, int fd)
+ssize_t song_print_info(struct mpd_song *song, int fd)
{
ssize_t ret = song_print_url(song, fd);
@@ -108,19 +103,19 @@ ssize_t song_print_info(Song *song, int fd)
return ret;
}
-int song_print_info_x(Song * song, void *data)
+int song_print_info_x(struct mpd_song * song, void *data)
{
return song_print_info(song, (int)(size_t)data);
}
-int song_print_url_x(Song * song, void *data)
+int song_print_url_x(struct mpd_song * song, void *data)
{
return song_print_url(song, (int)(size_t)data);
}
-static void insertSongIntoList(struct songvec *sv, Song *newsong)
+static void insertSongIntoList(struct songvec *sv, struct mpd_song *newsong)
{
- Song *existing = songvec_find(sv, newsong->url);
+ struct mpd_song *existing = songvec_find(sv, newsong->url);
if (!existing) {
songvec_add(sv, newsong);
@@ -141,10 +136,10 @@ static void insertSongIntoList(struct songvec *sv, Song *newsong)
if (old_tag)
tag_free(old_tag);
}
- /* prevent tag_free in freeJustSong */
+ /* prevent tag_free in song_free */
newsong->tag = NULL;
}
- freeJustSong(newsong);
+ song_free(newsong);
}
}
@@ -162,19 +157,19 @@ static int matchesAnMpdTagItemKey(char *buffer, int *itemType)
return 0;
}
-void readSongInfoIntoList(FILE * fp, Directory * parentDir)
+void readSongInfoIntoList(FILE * fp, struct directory * parent)
{
char buffer[MPD_PATH_MAX + 1024];
int bufferSize = MPD_PATH_MAX + 1024;
- Song *song = NULL;
- struct songvec *sv = &parentDir->songs;
+ struct mpd_song *song = NULL;
+ struct songvec *sv = &parent->songs;
int itemType;
while (myFgets(buffer, bufferSize, fp) && 0 != strcmp(SONG_END, buffer)) {
if (!prefixcmp(buffer, SONG_KEY)) {
if (song)
insertSongIntoList(sv, song);
- song = song_alloc(buffer + strlen(SONG_KEY), parentDir);
+ song = song_file_new(buffer + strlen(SONG_KEY), parent);
} else if (*buffer == 0) {
/* ignore empty lines (starting with '\0') */
} else if (song == NULL) {
@@ -209,49 +204,47 @@ void readSongInfoIntoList(FILE * fp, Directory * parentDir)
insertSongIntoList(sv, song);
}
-int updateSongInfo(Song * song)
+int song_file_update(struct mpd_song * song)
{
- if (song_is_file(song)) {
- InputPlugin *plugin;
- unsigned int next = 0;
- char path_max_tmp[MPD_PATH_MAX];
- char abs_path[MPD_PATH_MAX];
- struct mpd_tag *old_tag = song->tag;
- struct mpd_tag *new_tag = NULL;
-
- utf8_to_fs_charset(abs_path, get_song_url(path_max_tmp, song));
- rmp2amp_r(abs_path, abs_path);
-
- while ((plugin = isMusic(abs_path, &song->mtime, next++))) {
- if ((new_tag = plugin->tagDupFunc(abs_path)))
- break;
- }
- if (new_tag && tag_equal(new_tag, old_tag)) {
- tag_free(new_tag);
- } else {
- song->tag = new_tag;
- if (old_tag)
- tag_free(old_tag);
- }
- if (!song->tag || song->tag->time < 0)
- return -1;
+ InputPlugin *plugin;
+ unsigned int next = 0;
+ char path_max_tmp[MPD_PATH_MAX];
+ char abs_path[MPD_PATH_MAX];
+ struct mpd_tag *old_tag = song->tag;
+ struct mpd_tag *new_tag = NULL;
+
+ assert(song_is_file(song));
+
+ utf8_to_fs_charset(abs_path, song_get_url(song, path_max_tmp));
+ rmp2amp_r(abs_path, abs_path);
+
+ while ((plugin = isMusic(abs_path, &song->mtime, next++))) {
+ if ((new_tag = plugin->tagDupFunc(abs_path)))
+ break;
+ }
+ if (new_tag && tag_equal(new_tag, old_tag)) {
+ tag_free(new_tag);
+ } else {
+ song->tag = new_tag;
+ if (old_tag)
+ tag_free(old_tag);
}
- return 0;
+ return (song->tag && song->tag->time >= 0);
}
-char *get_song_url(char *path_max_tmp, Song *song)
+char *song_get_url(struct mpd_song *song, char *path_max_tmp)
{
if (!song)
return NULL;
assert(*song->url);
- if (!song->parentDir || !song->parentDir->path)
+ if (!song->parent || !song->parent->path)
strcpy(path_max_tmp, song->url);
else
pfx_dir(path_max_tmp, song->url, strlen(song->url),
- getDirectoryPath(song->parentDir),
- strlen(getDirectoryPath(song->parentDir)));
+ directory_get_path(song->parent),
+ strlen(directory_get_path(song->parent)));
return path_max_tmp;
}
diff --git a/src/song.h b/src/song.h
index 9aa9efa94..338bcf6c1 100644
--- a/src/song.h
+++ b/src/song.h
@@ -22,8 +22,6 @@
#include "../config.h"
#include "os_compat.h"
#include "tag.h"
-#include "list.h"
-#include "gcc.h"
#define SONG_KEY "key: "
#define SONG_MTIME "mtime: "
@@ -33,42 +31,53 @@
#define SONG_FILE "file: "
#define SONG_TIME "Time: "
-typedef struct _Song {
+struct mpd_song {
struct mpd_tag *tag;
- struct _Directory *parentDir;
+ struct directory *parent;
time_t mtime;
- char url[1];
-} mpd_packed Song;
+ char url[sizeof(size_t)];
+};
-Song *newSong(const char *url, struct _Directory *parentDir);
+void song_free(struct mpd_song *);
-void freeJustSong(Song *);
+/** allocate a new song with a remote URL */
+struct mpd_song * song_remote_new(const char *url);
-ssize_t song_print_info(Song * song, int fd);
+/** allocate a new song with a local file name */
+struct mpd_song * song_file_new(const char *path, struct directory *parent);
+
+/**
+ * allocate a new song structure with a local file name and attempt to
+ * load its metadata. If all decoder plugin fail to read its meta
+ * data, NULL is returned.
+ */
+struct mpd_song * song_file_load(const char *path, struct directory *parent);
+
+ssize_t song_print_info(struct mpd_song * song, int fd);
/* like song_print_info, but casts data into an fd first */
-int song_print_info_x(Song * song, void *data);
+int song_print_info_x(struct mpd_song * song, void *data);
-void readSongInfoIntoList(FILE * fp, struct _Directory *parent);
+void readSongInfoIntoList(FILE * fp, struct directory *parent);
-int updateSongInfo(Song * song);
+int song_file_update(struct mpd_song * song);
-ssize_t song_print_url(Song * song, int fd);
+ssize_t song_print_url(struct mpd_song * song, int fd);
/* like song_print_url_x, but casts data into an fd first */
-int song_print_url_x(Song * song, void *data);
+int song_print_url_x(struct mpd_song * song, void *data);
/*
- * get_song_url - Returns a path of a song in UTF8-encoded form
+ * song_get_url - Returns a path of a song in UTF8-encoded form
* path_max_tmp is the argument that the URL is written to, this
* buffer is assumed to be MPD_PATH_MAX or greater (including
* terminating '\0').
*/
-char *get_song_url(char *path_max_tmp, Song * song);
+char *song_get_url(struct mpd_song * song, char *path_max_tmp);
-static inline int song_is_file(const Song *song)
+static inline int song_is_file(const struct mpd_song *song)
{
- return !!song->parentDir;
+ return !!song->parent;
}
#endif
diff --git a/src/songvec.c b/src/songvec.c
index cf0991029..ce04a4373 100644
--- a/src/songvec.c
+++ b/src/songvec.c
@@ -7,27 +7,27 @@ static pthread_mutex_t nr_lock = PTHREAD_MUTEX_INITIALIZER;
/* Only used for sorting/searchin a songvec, not general purpose compares */
static int songvec_cmp(const void *s1, const void *s2)
{
- const Song *a = ((const Song * const *)s1)[0];
- const Song *b = ((const Song * const *)s2)[0];
+ const struct mpd_song *a = ((const struct mpd_song * const *)s1)[0];
+ const struct mpd_song *b = ((const struct mpd_song * const *)s2)[0];
return strcmp(a->url, b->url);
}
static size_t sv_size(struct songvec *sv)
{
- return sv->nr * sizeof(Song *);
+ return sv->nr * sizeof(struct mpd_song *);
}
void songvec_sort(struct songvec *sv)
{
pthread_mutex_lock(&nr_lock);
- qsort(sv->base, sv->nr, sizeof(Song *), songvec_cmp);
+ qsort(sv->base, sv->nr, sizeof(struct mpd_song *), songvec_cmp);
pthread_mutex_unlock(&nr_lock);
}
-Song *songvec_find(struct songvec *sv, const char *url)
+struct mpd_song *songvec_find(const struct songvec *sv, const char *url)
{
int i;
- Song *ret = NULL;
+ struct mpd_song *ret = NULL;
pthread_mutex_lock(&nr_lock);
for (i = sv->nr; --i >= 0; ) {
@@ -40,7 +40,7 @@ Song *songvec_find(struct songvec *sv, const char *url)
return ret;
}
-int songvec_delete(struct songvec *sv, const Song *del)
+int songvec_delete(struct songvec *sv, const struct mpd_song *del)
{
int i;
@@ -54,7 +54,7 @@ int songvec_delete(struct songvec *sv, const Song *del)
sv->base = NULL;
} else {
memmove(&sv->base[i], &sv->base[i + 1],
- (sv->nr - i + 1) * sizeof(Song *));
+ (sv->nr - i + 1) * sizeof(struct mpd_song *));
sv->base = xrealloc(sv->base, sv_size(sv));
}
break;
@@ -64,7 +64,7 @@ int songvec_delete(struct songvec *sv, const Song *del)
return i;
}
-void songvec_add(struct songvec *sv, Song *add)
+void songvec_add(struct songvec *sv, struct mpd_song *add)
{
pthread_mutex_lock(&nr_lock);
++sv->nr;
@@ -84,13 +84,14 @@ void songvec_destroy(struct songvec *sv)
pthread_mutex_unlock(&nr_lock);
}
-int songvec_for_each(struct songvec *sv, int (*fn)(Song *, void *), void *arg)
+int songvec_for_each(const struct songvec *sv,
+ int (*fn)(struct mpd_song *, void *), void *arg)
{
size_t i;
pthread_mutex_lock(&nr_lock);
for (i = 0; i < sv->nr; ++i) {
- Song *song = sv->base[i];
+ struct mpd_song *song = sv->base[i];
assert(song);
assert(*song->url);
diff --git a/src/songvec.h b/src/songvec.h
index dbe6be508..633cf8d66 100644
--- a/src/songvec.h
+++ b/src/songvec.h
@@ -5,20 +5,21 @@
#include "os_compat.h"
struct songvec {
- Song **base;
+ struct mpd_song **base;
size_t nr;
};
void songvec_sort(struct songvec *sv);
-Song *songvec_find(struct songvec *sv, const char *url);
+struct mpd_song *songvec_find(const struct songvec *sv, const char *url);
-int songvec_delete(struct songvec *sv, const Song *del);
+int songvec_delete(struct songvec *sv, const struct mpd_song *del);
-void songvec_add(struct songvec *sv, Song *add);
+void songvec_add(struct songvec *sv, struct mpd_song *add);
void songvec_destroy(struct songvec *sv);
-int songvec_for_each(struct songvec *sv, int (*fn)(Song *, void *), void *arg);
+int songvec_for_each(const struct songvec *sv,
+ int (*fn)(struct mpd_song *, void *), void *arg);
#endif /* SONGVEC_H */
diff --git a/src/stats.c b/src/stats.c
index 4555907db..abe03444a 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -19,7 +19,7 @@
#include "stats.h"
-#include "directory.h"
+#include "database.h"
#include "myfprintf.h"
#include "outputBuffer.h"
#include "tag.h"
@@ -40,7 +40,7 @@ struct visit_data {
struct strset *set;
};
-static int visit_tag_items(Song *song, void *_data)
+static int visit_tag_items(struct mpd_song *song, void *_data)
{
const struct visit_data *data = _data;
unsigned i;
@@ -65,7 +65,7 @@ static unsigned int getNumberOfTagItems(int type)
data.type = type;
data.set = strset_new();
- traverseAllIn(NULL, visit_tag_items, NULL, &data);
+ db_walk(NULL, visit_tag_items, NULL, &data);
ret = strset_size(data.set);
strset_free(data.set);
@@ -88,6 +88,6 @@ int printStats(int fd)
time(NULL) - stats.daemonStart,
(long)(ob_get_total_time() + 0.5),
stats.dbPlayTime,
- getDbModTime());
+ db_get_mtime());
return 0;
}
diff --git a/src/storedPlaylist.c b/src/storedPlaylist.c
index 265301392..2e6828483 100644
--- a/src/storedPlaylist.c
+++ b/src/storedPlaylist.c
@@ -20,7 +20,7 @@
#include "path.h"
#include "utils.h"
#include "ls.h"
-#include "directory.h"
+#include "database.h"
#include "os_compat.h"
static ListNode *nodeOfStoredPlaylist(List *list, int idx)
@@ -110,7 +110,7 @@ List *loadStoredPlaylist(const char *utf8path)
while (myFgets(buffer, sizeof(buffer), file)) {
char *s = buffer;
- Song *song;
+ struct mpd_song *song;
if (*s == PLAYLIST_COMMENT)
continue;
@@ -118,8 +118,8 @@ List *loadStoredPlaylist(const char *utf8path)
!strncmp(s, musicDir, musicDir_len))
memmove(s, s + musicDir_len + 1,
strlen(s + musicDir_len + 1) + 1);
- if ((song = getSongFromDB(s))) {
- get_song_url(path_max_tmp, song);
+ if ((song = db_get_song(s))) {
+ song_get_url(song, path_max_tmp);
insertInListWithoutKey(list, xstrdup(path_max_tmp));
} else if (isValidRemoteUtf8Url(s))
insertInListWithoutKey(list, xstrdup(s));
@@ -263,7 +263,7 @@ removeOneSongFromStoredPlaylistByPath(const char *utf8path, int pos)
}
enum playlist_result
-appendSongToStoredPlaylistByPath(const char *utf8path, Song *song)
+appendSongToStoredPlaylistByPath(const char *utf8path, struct mpd_song *song)
{
FILE *file;
char *s;
@@ -295,7 +295,7 @@ appendSongToStoredPlaylistByPath(const char *utf8path, Song *song)
return PLAYLIST_RESULT_TOO_LARGE;
}
- s = utf8_to_fs_charset(path_max_tmp2, get_song_url(path_max_tmp, song));
+ s = utf8_to_fs_charset(path_max_tmp2, song_get_url(song, path_max_tmp));
if (playlist_saveAbsolutePaths && song_is_file(song))
s = rmp2amp_r(path_max_tmp, s);
diff --git a/src/storedPlaylist.h b/src/storedPlaylist.h
index 964669d35..4721bcc98 100644
--- a/src/storedPlaylist.h
+++ b/src/storedPlaylist.h
@@ -35,7 +35,7 @@ enum playlist_result
removeOneSongFromStoredPlaylistByPath(const char *utf8path, int pos);
enum playlist_result
-appendSongToStoredPlaylistByPath(const char *utf8path, Song *song);
+appendSongToStoredPlaylistByPath(const char *utf8path, struct mpd_song *song);
enum playlist_result
renameStoredPlaylist(const char *utf8from, const char *utf8to);
diff --git a/src/update.c b/src/update.c
new file mode 100644
index 000000000..ca3640b73
--- /dev/null
+++ b/src/update.c
@@ -0,0 +1,444 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * Copyright (C) 2008 Max Kellermann <max@duempel.org>
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "update.h"
+#include "database.h"
+#include "log.h"
+#include "ls.h"
+#include "path.h"
+#include "playlist.h"
+#include "utils.h"
+#include "main_notify.h"
+#include "condition.h"
+#include "update.h"
+
+static enum update_progress {
+ UPDATE_PROGRESS_IDLE = 0,
+ UPDATE_PROGRESS_RUNNING = 1,
+ UPDATE_PROGRESS_DONE = 2
+} progress;
+
+static int modified;
+
+/* make this dynamic?, or maybe this is big enough... */
+static char *update_paths[32];
+static size_t update_paths_nr;
+
+static pthread_t update_thr;
+
+static const unsigned update_task_id_max = 1 << 15;
+
+static unsigned update_task_id;
+
+static struct mpd_song *delete;
+
+static struct condition delete_cond;
+
+unsigned isUpdatingDB(void)
+{
+ return (progress != UPDATE_PROGRESS_IDLE) ? update_task_id : 0;
+}
+
+static void directory_set_stat(struct directory *dir, const struct stat *st)
+{
+ dir->inode = st->st_ino;
+ dir->device = st->st_dev;
+ dir->stat = 1;
+}
+
+static void delete_song(struct directory *dir, struct mpd_song *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);
+ 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)
+{
+ struct directory *dir = data;
+ assert(song->parent == dir);
+ delete_song(dir, song);
+ return 0;
+}
+
+/**
+ * Recursively remove all sub directories and songs from a directory,
+ * leaving an empty directory.
+ */
+static void clear_directory(struct directory *dir)
+{
+ int i;
+
+ for (i = dir->children.nr; --i >= 0;)
+ clear_directory(dir->children.base[i]);
+ dirvec_clear(&dir->children);
+
+ songvec_for_each(&dir->songs, delete_each_song, dir);
+}
+
+/**
+ * Recursively free a directory and all its contents.
+ */
+static void delete_directory(struct directory *dir)
+{
+ assert(dir->parent != NULL);
+
+ clear_directory(dir);
+ dirvec_delete(&dir->parent->children, dir);
+ directory_free(dir);
+}
+
+struct delete_data {
+ char *tmp;
+ struct directory *dir;
+};
+
+/* passed to songvec_for_each */
+static int delete_song_if_removed(struct mpd_song *song, void *_data)
+{
+ struct delete_data *data = _data;
+
+ data->tmp = song_get_url(song, data->tmp);
+ assert(data->tmp);
+
+ if (!isFile(data->tmp, NULL)) {
+ delete_song(data->dir, song);
+ modified = 1;
+ }
+ return 0;
+}
+
+static void delete_path(const char *path)
+{
+ struct directory *dir = db_get_directory(path);
+ struct mpd_song *song = db_get_song(path);
+
+ if (dir) {
+ delete_directory(dir);
+ modified = 1;
+ }
+ if (song) {
+ delete_song(song->parent, song);
+ modified = 1;
+ }
+}
+
+static void
+removeDeletedFromDirectory(char *path_max_tmp, struct directory *dir)
+{
+ int i;
+ struct dirvec *dv = &dir->children;
+ struct delete_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]);
+ modified = 1;
+ }
+
+ data.dir = dir;
+ data.tmp = path_max_tmp;
+ songvec_for_each(&dir->songs, delete_song_if_removed, &data);
+}
+
+static const char *opendir_path(char *path_max_tmp, const char *dirname)
+{
+ if (*dirname != '\0')
+ return rmp2amp_r(path_max_tmp,
+ utf8_to_fs_charset(path_max_tmp, dirname));
+ return musicDir;
+}
+
+static int statDirectory(struct directory *dir)
+{
+ struct stat st;
+
+ if (myStat(directory_get_path(dir), &st) < 0)
+ return -1;
+
+ directory_set_stat(dir, &st);
+
+ return 0;
+}
+
+static int
+inodeFoundInParent(struct directory *parent, ino_t inode, dev_t device)
+{
+ while (parent) {
+ if (!parent->stat && statDirectory(parent) < 0)
+ return -1;
+ if (parent->inode == inode && parent->device == device) {
+ DEBUG("recursive directory found\n");
+ return 1;
+ }
+ parent = parent->parent;
+ }
+
+ return 0;
+}
+
+static int updateDirectory(struct directory *dir, const struct stat *st);
+
+static void
+updateInDirectory(struct directory *dir,
+ const char *name, const struct stat *st)
+{
+ if (S_ISREG(st->st_mode) && hasMusicSuffix(name, 0)) {
+ const char *shortname = mpd_basename(name);
+ struct mpd_song *song;
+
+ if (!(song = songvec_find(&dir->songs, shortname))) {
+ if (!(song = song_file_load(shortname, dir)))
+ return;
+ songvec_add(&dir->songs, song);
+ modified = 1;
+ LOG("added %s\n", name);
+ } else if (st->st_mtime != song->mtime) {
+ LOG("updating %s\n", name);
+ if (!song_file_update(song))
+ delete_song(dir, song);
+ modified = 1;
+ }
+ } else if (S_ISDIR(st->st_mode)) {
+ struct directory *subdir;
+
+ if (inodeFoundInParent(dir, st->st_ino, st->st_dev))
+ return;
+
+ if (!(subdir = directory_get_child(dir, name)))
+ subdir = directory_new_child(dir, name);
+
+ assert(dir == subdir->parent);
+
+ if (!updateDirectory(subdir, st))
+ delete_directory(subdir);
+ } else {
+ DEBUG("update: %s is not a directory or music\n", name);
+ }
+}
+
+/* we don't look at hidden files nor files with newlines in them */
+static int skip_path(const char *path)
+{
+ return (path[0] == '.' || strchr(path, '\n')) ? 1 : 0;
+}
+
+static int updateDirectory(struct directory *dir, const struct stat *st)
+{
+ DIR *fs_dir;
+ const char *dirname = directory_get_path(dir);
+ struct dirent *ent;
+ char path_max_tmp[MPD_PATH_MAX];
+
+ assert(S_ISDIR(st->st_mode));
+
+ directory_set_stat(dir, st);
+
+ if (!(fs_dir = opendir(opendir_path(path_max_tmp, dirname))))
+ return 0;
+
+ removeDeletedFromDirectory(path_max_tmp, dir);
+
+ while ((ent = readdir(fs_dir))) {
+ char *utf8;
+ struct stat st2;
+
+ if (skip_path(ent->d_name))
+ continue;
+
+ utf8 = fs_charset_to_utf8(path_max_tmp, ent->d_name);
+ if (!utf8)
+ continue;
+
+ if (!isRootDirectory(dir->path))
+ utf8 = pfx_dir(path_max_tmp, utf8, strlen(utf8),
+ dirname, strlen(dirname));
+
+ if (myStat(path_max_tmp, &st2) == 0)
+ updateInDirectory(dir, path_max_tmp, &st2);
+ else
+ delete_path(path_max_tmp);
+ }
+
+ closedir(fs_dir);
+
+ return 1;
+}
+
+static struct directory *
+directory_make_child_checked(struct directory *parent, const char *path)
+{
+ struct directory *dir;
+ struct stat st;
+ struct mpd_song *conflicting;
+
+ if ((dir = directory_get_child(parent, path)))
+ return dir;
+
+ if (myStat(path, &st) < 0 ||
+ inodeFoundInParent(parent, st.st_ino, st.st_dev))
+ return NULL;
+
+ /* if we're adding directory paths, make sure to delete filenames
+ with potentially the same name */
+ if ((conflicting = songvec_find(&parent->songs, mpd_basename(path))))
+ delete_song(parent, conflicting);
+
+ dir = directory_new_child(parent, path);
+ directory_set_stat(dir, &st);
+ return dir;
+}
+
+static struct directory *
+addParentPathToDB(const char *utf8path)
+{
+ struct directory *dir = db_get_root();
+ char *duplicated = xstrdup(utf8path);
+ char *slash = duplicated;
+
+ while ((slash = strchr(slash, '/'))) {
+ *slash = 0;
+
+ dir = directory_make_child_checked(dir, duplicated);
+ if (!dir || !slash)
+ break;
+
+ *slash++ = '/';
+ }
+
+ free(duplicated);
+ return dir;
+}
+
+static void updatePath(const char *utf8path)
+{
+ struct stat st;
+
+ if (myStat(utf8path, &st) < 0)
+ delete_path(utf8path);
+ else
+ updateInDirectory(addParentPathToDB(utf8path), utf8path, &st);
+}
+
+static void * update_task(void *_path)
+{
+ if (_path != NULL && !isRootDirectory(_path)) {
+ updatePath((char *)_path);
+ free(_path);
+ } else {
+ struct directory *dir = db_get_root();
+ struct stat st;
+
+ if (myStat(directory_get_path(dir), &st) == 0)
+ updateDirectory(dir, &st);
+ }
+
+ if (modified)
+ db_save();
+ progress = UPDATE_PROGRESS_DONE;
+ wakeup_main_task();
+ return NULL;
+}
+
+static void spawn_update_task(char *path)
+{
+ pthread_attr_t attr;
+
+ assert(pthread_equal(pthread_self(), main_task));
+
+ progress = UPDATE_PROGRESS_RUNNING;
+ modified = 0;
+ pthread_attr_init(&attr);
+ if (pthread_create(&update_thr, &attr, update_task, path))
+ FATAL("Failed to spawn update task: %s\n", strerror(errno));
+ if (++update_task_id > update_task_id_max)
+ update_task_id = 1;
+ DEBUG("spawned thread for update job id %i\n", update_task_id);
+}
+
+unsigned directory_update_init(char *path)
+{
+ assert(pthread_equal(pthread_self(), main_task));
+
+ if (progress != UPDATE_PROGRESS_IDLE) {
+ unsigned next_task_id;
+
+ if (!path)
+ return 0;
+ if (update_paths_nr == ARRAY_SIZE(update_paths)) {
+ free(path);
+ return 0;
+ }
+
+ assert(update_paths_nr < ARRAY_SIZE(update_paths));
+ update_paths[update_paths_nr++] = path;
+ next_task_id = update_task_id + update_paths_nr;
+
+ return next_task_id > update_task_id_max ? 1 : next_task_id;
+ }
+ spawn_update_task(path);
+ return update_task_id;
+}
+
+void reap_update_task(void)
+{
+ assert(pthread_equal(pthread_self(), main_task));
+
+ if (progress == UPDATE_PROGRESS_IDLE)
+ 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;
+ cond_signal(&delete_cond);
+ }
+ cond_leave(&delete_cond);
+
+ if (progress != UPDATE_PROGRESS_DONE)
+ return;
+ if (pthread_join(update_thr, NULL))
+ FATAL("error joining update thread: %s\n", strerror(errno));
+
+ if (modified)
+ playlistVersionChange();
+
+ if (update_paths_nr) {
+ char *path = update_paths[0];
+ memmove(&update_paths[0], &update_paths[1],
+ --update_paths_nr * sizeof(char *));
+ spawn_update_task(path);
+ } else {
+ progress = UPDATE_PROGRESS_IDLE;
+ }
+}
diff --git a/src/update.h b/src/update.h
new file mode 100644
index 000000000..0842c0df0
--- /dev/null
+++ b/src/update.h
@@ -0,0 +1,34 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * Copyright (C) 2008 Max Kellermann <max@duempel.org>
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UPDATE_H
+#define UPDATE_H
+
+unsigned isUpdatingDB(void);
+
+/*
+ * returns the positive update job ID on success,
+ * returns 0 if busy
+ * @path will be freed by this function and should not be reused
+ */
+unsigned directory_update_init(char *path);
+
+void reap_update_task(void);
+
+#endif