aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/src/directory.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/directory.c')
-rw-r--r--trunk/src/directory.c1362
1 files changed, 0 insertions, 1362 deletions
diff --git a/trunk/src/directory.c b/trunk/src/directory.c
deleted file mode 100644
index 560c04b7b..000000000
--- a/trunk/src/directory.c
+++ /dev/null
@@ -1,1362 +0,0 @@
-/* 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.h"
-
-#include "command.h"
-#include "conf.h"
-#include "dbUtils.h"
-#include "interface.h"
-#include "list.h"
-#include "listen.h"
-#include "log.h"
-#include "ls.h"
-#include "mpd_types.h"
-#include "path.h"
-#include "player.h"
-#include "playlist.h"
-#include "sig_handlers.h"
-#include "stats.h"
-#include "tagTracker.h"
-#include "utils.h"
-#include "volume.h"
-
-#include <sys/wait.h>
-#include <dirent.h>
-#include <errno.h>
-#include <assert.h>
-#include <libgen.h>
-
-#define DIRECTORY_DIR "directory: "
-#define DIRECTORY_MTIME "mtime: "
-#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: "
-
-#define DIRECTORY_UPDATE_EXIT_NOUPDATE 0
-#define DIRECTORY_UPDATE_EXIT_UPDATE 1
-#define DIRECTORY_UPDATE_EXIT_ERROR 2
-
-#define DIRECTORY_RETURN_NOUPDATE 0
-#define DIRECTORY_RETURN_UPDATE 1
-#define DIRECTORY_RETURN_ERROR -1
-
-static Directory *mp3rootDirectory;
-
-static time_t directory_dbModTime;
-
-static volatile int directory_updatePid;
-
-static volatile int directory_reReadDB;
-
-static volatile mpd_uint16 directory_updateJobId;
-
-static DirectoryList *newDirectoryList();
-
-static int addToDirectory(Directory * directory, char *shortname, char *name);
-
-static void freeDirectoryList(DirectoryList * list);
-
-static void freeDirectory(Directory * directory);
-
-static int exploreDirectory(Directory * directory);
-
-static int updateDirectory(Directory * directory);
-
-static void deleteEmptyDirectoriesInDirectory(Directory * directory);
-
-static void removeSongFromDirectory(Directory * directory, char *shortname);
-
-static int addSubDirectoryToDirectory(Directory * directory, char *shortname,
- char *name, struct stat *st);
-
-static Directory *getDirectoryDetails(char *name, char **shortname);
-
-static Directory *getDirectory(char *name);
-
-static Song *getSongDetails(char *file, char **shortnameRet,
- Directory ** directoryRet);
-
-static int updatePath(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;
-}
-
-static void clearUpdatePid(void)
-{
- directory_updatePid = 0;
-}
-
-int isUpdatingDB(void)
-{
- if (directory_updatePid > 0 || directory_reReadDB) {
- return directory_updateJobId;
- }
- return 0;
-}
-
-void directory_sigChldHandler(int pid, int status)
-{
- if (directory_updatePid == pid) {
- if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM) {
- ERROR("update process died from a "
- "non-TERM signal: %i\n", WTERMSIG(status));
- } else if (!WIFSIGNALED(status)) {
- switch (WEXITSTATUS(status)) {
- case DIRECTORY_UPDATE_EXIT_UPDATE:
- directory_reReadDB = 1;
- DEBUG("directory_sigChldHandler: "
- "updated db\n");
- case DIRECTORY_UPDATE_EXIT_NOUPDATE:
- DEBUG("directory_sigChldHandler: "
- "update exited succesffully\n");
- break;
- default:
- ERROR("error updating db\n");
- }
- }
- clearUpdatePid();
- }
-}
-
-void readDirectoryDBIfUpdateIsFinished(void)
-{
- if (directory_reReadDB && 0 == directory_updatePid) {
- DEBUG("readDirectoryDB since update finished successfully\n");
- readDirectoryDB();
- playlistVersionChange();
- directory_reReadDB = 0;
- }
-}
-
-int updateInit(int fd, List * pathList)
-{
- if (directory_updatePid > 0) {
- commandError(fd, ACK_ERROR_UPDATE_ALREADY, "already updating");
- return -1;
- }
-
- /* need to block CHLD signal, cause it can exit before we
- even get a chance to assign directory_updatePID */
- blockSignals();
- directory_updatePid = fork();
- if (directory_updatePid == 0) {
- /* child */
- int dbUpdated = 0;
- clearPlayerPid();
-
- unblockSignals();
-
- finishSigHandlers();
- closeAllListenSockets();
- freeAllInterfaces();
- finishPlaylist();
- finishVolume();
-
- if (pathList) {
- ListNode *node = pathList->firstNode;
-
- while (node) {
- switch (updatePath(node->key)) {
- case 1:
- dbUpdated = 1;
- break;
- case 0:
- break;
- default:
- exit(DIRECTORY_UPDATE_EXIT_ERROR);
- }
- node = node->nextNode;
- }
- } else {
- if ((dbUpdated = updateDirectory(mp3rootDirectory)) < 0) {
- exit(DIRECTORY_UPDATE_EXIT_ERROR);
- }
- }
-
- if (!dbUpdated)
- exit(DIRECTORY_UPDATE_EXIT_NOUPDATE);
-
- /* ignore signals since we don't want them to corrupt the db */
- ignoreSignals();
- if (writeDirectoryDB() < 0) {
- exit(DIRECTORY_UPDATE_EXIT_ERROR);
- }
- exit(DIRECTORY_UPDATE_EXIT_UPDATE);
- } else if (directory_updatePid < 0) {
- unblockSignals();
- ERROR("updateInit: Problems forking()'ing\n");
- commandError(fd, ACK_ERROR_SYSTEM,
- "problems trying to update");
- directory_updatePid = 0;
- return -1;
- }
- unblockSignals();
-
- directory_updateJobId++;
- if (directory_updateJobId > 1 << 15)
- directory_updateJobId = 1;
- DEBUG("updateInit: fork()'d update child for update job id %i\n",
- (int)directory_updateJobId);
- fdprintf(fd, "updating_db: %i\n", (int)directory_updateJobId);
-
- return 0;
-}
-
-static DirectoryStat *newDirectoryStat(struct stat *st)
-{
- DirectoryStat *ret = xmalloc(sizeof(DirectoryStat));
- ret->inode = st->st_ino;
- ret->device = st->st_dev;
- return ret;
-}
-
-static void freeDirectoryStatFromDirectory(Directory * dir)
-{
- if (dir->stat)
- free(dir->stat);
- dir->stat = NULL;
-}
-
-static DirectoryList *newDirectoryList(void)
-{
- return makeList((ListFreeDataFunc *) freeDirectory, 1);
-}
-
-static Directory *newDirectory(char *dirname, Directory * parent)
-{
- Directory *directory;
-
- directory = xmalloc(sizeof(Directory));
-
- if (dirname && strlen(dirname))
- directory->path = xstrdup(dirname);
- else
- directory->path = NULL;
- directory->subDirectories = newDirectoryList();
- directory->songs = newSongList();
- directory->stat = NULL;
- directory->parent = parent;
-
- return directory;
-}
-
-static void freeDirectory(Directory * directory)
-{
- freeDirectoryList(directory->subDirectories);
- freeSongList(directory->songs);
- if (directory->path)
- free(directory->path);
- freeDirectoryStatFromDirectory(directory);
- free(directory);
- /* this resets last dir returned */
- /*getDirectoryPath(NULL); */
-}
-
-static void freeDirectoryList(DirectoryList * directoryList)
-{
- freeList(directoryList);
-}
-
-static void removeSongFromDirectory(Directory * directory, char *shortname)
-{
- void *song;
-
- if (findInList(directory->songs, shortname, &song)) {
- LOG("removing: %s\n", getSongUrl((Song *) song));
- deleteFromList(directory->songs, shortname);
- }
-}
-
-static void deleteEmptyDirectoriesInDirectory(Directory * directory)
-{
- ListNode *node = directory->subDirectories->firstNode;
- ListNode *nextNode;
- Directory *subDir;
-
- while (node) {
- subDir = (Directory *) node->data;
- deleteEmptyDirectoriesInDirectory(subDir);
- nextNode = node->nextNode;
- if (subDir->subDirectories->numberOfNodes == 0 &&
- subDir->songs->numberOfNodes == 0) {
- deleteNodeFromList(directory->subDirectories, node);
- }
- node = nextNode;
- }
-}
-
-/* return values:
- -1 -> error
- 0 -> no error, but nothing updated
- 1 -> no error, and stuff updated
- */
-static int updateInDirectory(Directory * directory, char *shortname, char *name)
-{
- void *song;
- void *subDir;
- struct stat st;
-
- if (myStat(name, &st))
- return -1;
-
- if (S_ISREG(st.st_mode) && hasMusicSuffix(name, 0)) {
- if (0 == findInList(directory->songs, shortname, &song)) {
- addToDirectory(directory, shortname, name);
- return DIRECTORY_RETURN_UPDATE;
- } else if (st.st_mtime != ((Song *) song)->mtime) {
- LOG("updating %s\n", name);
- if (updateSongInfo((Song *) song) < 0) {
- removeSongFromDirectory(directory, shortname);
- }
- return 1;
- }
- } else if (S_ISDIR(st.st_mode)) {
- if (findInList
- (directory->subDirectories, shortname, (void **)&subDir)) {
- freeDirectoryStatFromDirectory(subDir);
- ((Directory *) subDir)->stat = newDirectoryStat(&st);
- return updateDirectory((Directory *) subDir);
- } else {
- return addSubDirectoryToDirectory(directory, shortname,
- name, &st);
- }
- }
-
- return 0;
-}
-
-/* return values:
- -1 -> error
- 0 -> no error, but nothing removed
- 1 -> no error, and stuff removed
- */
-static int removeDeletedFromDirectory(Directory * directory, DIR * dir)
-{
- char cwd[2];
- struct dirent *ent;
- char *dirname = getDirectoryPath(directory);
- List *entList = makeList(free, 1);
- void *name;
- char *s;
- char *utf8;
- ListNode *node;
- ListNode *tmpNode;
- int ret = 0;
-
- cwd[0] = '.';
- cwd[1] = '\0';
- if (dirname == NULL)
- dirname = cwd;
-
- while ((ent = readdir(dir))) {
- if (ent->d_name[0] == '.')
- continue; /* hide hidden stuff */
- if (strchr(ent->d_name, '\n'))
- continue;
-
- utf8 = fsCharsetToUtf8(ent->d_name);
-
- if (!utf8)
- continue;
-
- if (directory->path) {
- s = xmalloc(strlen(getDirectoryPath(directory))
- + strlen(utf8) + 2);
- sprintf(s, "%s/%s", getDirectoryPath(directory), utf8);
- } else
- s = xstrdup(utf8);
- insertInList(entList, utf8, s);
- }
-
- node = directory->subDirectories->firstNode;
- while (node) {
- tmpNode = node->nextNode;
- if (findInList(entList, node->key, &name)) {
- if (!isDir((char *)name)) {
- LOG("removing directory: %s\n", (char *)name);
- deleteFromList(directory->subDirectories,
- node->key);
- ret = 1;
- }
- } else {
- LOG("removing directory: %s/%s\n",
- getDirectoryPath(directory), node->key);
- deleteFromList(directory->subDirectories, node->key);
- ret = 1;
- }
- node = tmpNode;
- }
-
- node = directory->songs->firstNode;
- while (node) {
- tmpNode = node->nextNode;
- if (findInList(entList, node->key, (void **)&name)) {
- if (!isMusic(name, NULL, 0)) {
- removeSongFromDirectory(directory, node->key);
- ret = 1;
- }
- } else {
- removeSongFromDirectory(directory, node->key);
- ret = 1;
- }
- node = tmpNode;
- }
-
- freeList(entList);
-
- return ret;
-}
-
-static Directory *addDirectoryPathToDB(char *utf8path, char **shortname)
-{
- char *parent;
- Directory *parentDirectory;
- void *directory;
-
- parent = xstrdup(parentPath(utf8path));
-
- if (strlen(parent) == 0)
- parentDirectory = (void *)mp3rootDirectory;
- else
- parentDirectory = addDirectoryPathToDB(parent, shortname);
-
- if (!parentDirectory) {
- free(parent);
- return NULL;
- }
-
- *shortname = utf8path + strlen(parent);
- while (*(*shortname) && *(*shortname) == '/')
- (*shortname)++;
-
- if (!findInList
- (parentDirectory->subDirectories, *shortname, &directory)) {
- struct stat st;
- if (myStat(utf8path, &st) < 0 ||
- inodeFoundInParent(parentDirectory, st.st_ino, st.st_dev)) {
- free(parent);
- return NULL;
- } else {
- directory = newDirectory(utf8path, parentDirectory);
- insertInList(parentDirectory->subDirectories,
- *shortname, directory);
- }
- }
-
- /* if we're adding directory paths, make sure to delete filenames
- with potentially the same name */
- removeSongFromDirectory(parentDirectory, *shortname);
-
- free(parent);
-
- return (Directory *) directory;
-}
-
-static Directory *addParentPathToDB(char *utf8path, char **shortname)
-{
- char *parent;
- Directory *parentDirectory;
-
- parent = xstrdup(parentPath(utf8path));
-
- if (strlen(parent) == 0)
- parentDirectory = (void *)mp3rootDirectory;
- else
- parentDirectory = addDirectoryPathToDB(parent, shortname);
-
- if (!parentDirectory) {
- free(parent);
- return NULL;
- }
-
- *shortname = utf8path + strlen(parent);
- while (*(*shortname) && *(*shortname) == '/')
- (*shortname)++;
-
- free(parent);
-
- return (Directory *) parentDirectory;
-}
-
-/* return values:
- -1 -> error
- 0 -> no error, but nothing updated
- 1 -> no error, and stuff updated
- */
-static int updatePath(char *utf8path)
-{
- Directory *directory;
- Directory *parentDirectory;
- Song *song;
- char *shortname;
- char *path = sanitizePathDup(utf8path);
- time_t mtime;
- int ret = 0;
-
- if (NULL == path)
- return -1;
-
- /* if path is in the DB try to update it, or else delete it */
- if ((directory = getDirectoryDetails(path, &shortname))) {
- parentDirectory = directory->parent;
-
- /* if this update directory is successfull, we are done */
- if ((ret = updateDirectory(directory)) >= 0) {
- free(path);
- sortDirectory(directory);
- return ret;
- }
- /* we don't want to delete the root directory */
- else if (directory == mp3rootDirectory) {
- free(path);
- return 0;
- }
- /* if updateDirectory fails, means we should delete it */
- else {
- LOG("removing directory: %s\n", path);
- deleteFromList(parentDirectory->subDirectories,
- shortname);
- ret = 1;
- /* don't return, path maybe a song now */
- }
- } else if ((song = getSongDetails(path, &shortname, &parentDirectory))) {
- if (!parentDirectory->stat
- && statDirectory(parentDirectory) < 0) {
- free(path);
- return 0;
- }
- /* if this song update is successfull, we are done */
- else if (0 == inodeFoundInParent(parentDirectory->parent,
- parentDirectory->stat->inode,
- parentDirectory->stat->device)
- && song && isMusic(getSongUrl(song), &mtime, 0)) {
- free(path);
- if (song->mtime == mtime)
- return 0;
- else if (updateSongInfo(song) == 0)
- return 1;
- else {
- removeSongFromDirectory(parentDirectory,
- shortname);
- return 1;
- }
- }
- /* if updateDirectory fails, means we should delete it */
- else {
- removeSongFromDirectory(parentDirectory, shortname);
- ret = 1;
- /* 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(path) || isMusic(path, NULL, 0)) {
- parentDirectory = addParentPathToDB(path, &shortname);
- if (!parentDirectory || (!parentDirectory->stat &&
- statDirectory(parentDirectory) < 0)) {
- } else if (0 == inodeFoundInParent(parentDirectory->parent,
- parentDirectory->stat->inode,
- parentDirectory->stat->
- device)
- && addToDirectory(parentDirectory, shortname, path)
- > 0) {
- ret = 1;
- }
- }
-
- free(path);
-
- return ret;
-}
-
-/* return values:
- -1 -> error
- 0 -> no error, but nothing updated
- 1 -> no error, and stuff updated
- */
-static int updateDirectory(Directory * directory)
-{
- DIR *dir;
- char cwd[2];
- struct dirent *ent;
- char *s;
- char *utf8;
- char *dirname = getDirectoryPath(directory);
- int ret = 0;
-
- {
- if (!directory->stat && statDirectory(directory) < 0) {
- return -1;
- } else if (inodeFoundInParent(directory->parent,
- directory->stat->inode,
- directory->stat->device)) {
- return -1;
- }
- }
-
- cwd[0] = '.';
- cwd[1] = '\0';
- if (dirname == NULL)
- dirname = cwd;
-
- if ((dir = opendir(rmp2amp(utf8ToFsCharset(dirname)))) == NULL)
- return -1;
-
- if (removeDeletedFromDirectory(directory, dir) > 0)
- ret = 1;
-
- rewinddir(dir);
-
- while ((ent = readdir(dir))) {
- if (ent->d_name[0] == '.')
- continue; /* hide hidden stuff */
- if (strchr(ent->d_name, '\n'))
- continue;
-
- utf8 = fsCharsetToUtf8(ent->d_name);
-
- if (!utf8)
- continue;
-
- utf8 = xstrdup(utf8);
-
- if (directory->path) {
- s = xmalloc(strlen(getDirectoryPath(directory)) +
- strlen(utf8) + 2);
- sprintf(s, "%s/%s", getDirectoryPath(directory), utf8);
- } else
- s = xstrdup(utf8);
- if (updateInDirectory(directory, utf8, s) > 0)
- ret = 1;
- free(utf8);
- free(s);
- }
-
- closedir(dir);
-
- return ret;
-}
-
-/* return values:
- -1 -> error
- 0 -> no error, but nothing found
- 1 -> no error, and stuff found
- */
-static int exploreDirectory(Directory * directory)
-{
- DIR *dir;
- char cwd[2];
- struct dirent *ent;
- char *s;
- char *utf8;
- char *dirname = getDirectoryPath(directory);
- int ret = 0;
-
- cwd[0] = '.';
- cwd[1] = '\0';
- if (dirname == NULL)
- dirname = cwd;
-
- DEBUG("explore: attempting to opendir: %s\n", dirname);
- if ((dir = opendir(rmp2amp(utf8ToFsCharset(dirname)))) == NULL)
- return -1;
-
- DEBUG("explore: %s\n", dirname);
- while ((ent = readdir(dir))) {
- if (ent->d_name[0] == '.')
- continue; /* hide hidden stuff */
- if (strchr(ent->d_name, '\n'))
- continue;
-
- utf8 = fsCharsetToUtf8(ent->d_name);
-
- if (!utf8)
- continue;
-
- utf8 = xstrdup(utf8);
-
- DEBUG("explore: found: %s (%s)\n", ent->d_name, utf8);
-
- if (directory->path) {
- s = xmalloc(strlen(getDirectoryPath(directory)) +
- strlen(utf8) + 2);
- sprintf(s, "%s/%s", getDirectoryPath(directory), utf8);
- } else
- s = xstrdup(utf8);
- if (addToDirectory(directory, utf8, s) > 0)
- ret = 1;
- free(utf8);
- free(s);
- }
-
- closedir(dir);
-
- return ret;
-}
-
-static int statDirectory(Directory * dir)
-{
- struct stat st;
-
- if (myStat(getDirectoryPath(dir) ? getDirectoryPath(dir) : "", &st) < 0)
- {
- return -1;
- }
-
- dir->stat = newDirectoryStat(&st);
-
- return 0;
-}
-
-static int inodeFoundInParent(Directory * parent, ino_t inode, dev_t device)
-{
- while (parent) {
- if (!parent->stat) {
- if (statDirectory(parent) < 0)
- return -1;
- }
- if (parent->stat->inode == inode &&
- parent->stat->device == device) {
- DEBUG("recursive directory found\n");
- return 1;
- }
- parent = parent->parent;
- }
-
- return 0;
-}
-
-static int addSubDirectoryToDirectory(Directory * directory, char *shortname,
- char *name, struct stat *st)
-{
- Directory *subDirectory;
-
- if (inodeFoundInParent(directory, st->st_ino, st->st_dev))
- return 0;
-
- subDirectory = newDirectory(name, directory);
- subDirectory->stat = newDirectoryStat(st);
-
- if (exploreDirectory(subDirectory) < 1) {
- freeDirectory(subDirectory);
- return 0;
- }
-
- insertInList(directory->subDirectories, shortname, subDirectory);
-
- return 1;
-}
-
-static int addToDirectory(Directory * directory, char *shortname, 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)) {
- Song *song;
- song = addSongToList(directory->songs, shortname, name,
- SONG_TYPE_FILE, directory);
- if (!song)
- return -1;
- LOG("added %s\n", name);
- return 1;
- } else if (S_ISDIR(st.st_mode)) {
- return addSubDirectoryToDirectory(directory, shortname, name,
- &st);
- }
-
- DEBUG("addToDirectory: %s is not a directory or music\n", name);
-
- return -1;
-}
-
-void closeMp3Directory(void)
-{
- freeDirectory(mp3rootDirectory);
-}
-
-static Directory *findSubDirectory(Directory * directory, char *name)
-{
- void *subDirectory;
- char *dup = xstrdup(name);
- char *key;
-
- key = strtok(dup, "/");
- if (!key) {
- free(dup);
- return NULL;
- }
-
- if (findInList(directory->subDirectories, key, &subDirectory)) {
- free(dup);
- return (Directory *) subDirectory;
- }
-
- free(dup);
- return NULL;
-}
-
-int isRootDirectory(char *name)
-{
- if (name == NULL || name[0] == '\0' || strcmp(name, "/") == 0) {
- return 1;
- }
- return 0;
-}
-
-static Directory *getSubDirectory(Directory * directory, char *name,
- char **shortname)
-{
- Directory *subDirectory;
- int len;
-
- if (isRootDirectory(name)) {
- return directory;
- }
-
- if ((subDirectory = findSubDirectory(directory, name)) == NULL)
- return NULL;
-
- *shortname = name;
-
- len = 0;
- while (name[len] != '/' && name[len] != '\0')
- len++;
- while (name[len] == '/')
- len++;
-
- return getSubDirectory(subDirectory, &(name[len]), shortname);
-}
-
-static Directory *getDirectoryDetails(char *name, char **shortname)
-{
- *shortname = NULL;
-
- return getSubDirectory(mp3rootDirectory, name, shortname);
-}
-
-static Directory *getDirectory(char *name)
-{
- char *shortname;
-
- return getSubDirectory(mp3rootDirectory, name, &shortname);
-}
-
-static int printDirectoryList(int fd, DirectoryList * directoryList)
-{
- ListNode *node = directoryList->firstNode;
- Directory *directory;
-
- while (node != NULL) {
- directory = (Directory *) node->data;
- fdprintf(fd, "%s%s\n", DIRECTORY_DIR,
- getDirectoryPath(directory));
- node = node->nextNode;
- }
-
- return 0;
-}
-
-int printDirectoryInfo(int fd, char *name)
-{
- Directory *directory;
-
- if ((directory = getDirectory(name)) == NULL) {
- commandError(fd, ACK_ERROR_NO_EXIST, "directory not found");
- return -1;
- }
-
- printDirectoryList(fd, directory->subDirectories);
- printSongInfoFromList(fd, directory->songs);
-
- return 0;
-}
-
-static void writeDirectoryInfo(FILE * fp, Directory * directory)
-{
- ListNode *node = (directory->subDirectories)->firstNode;
- Directory *subDirectory;
-
- if (directory->path) {
- fprintf(fp, "%s%s\n", DIRECTORY_BEGIN,
- getDirectoryPath(directory));
- }
-
- while (node != NULL) {
- subDirectory = (Directory *) node->data;
- fprintf(fp, "%s%s\n", DIRECTORY_DIR, node->key);
- writeDirectoryInfo(fp, subDirectory);
- node = node->nextNode;
- }
-
- writeSongInfoFromList(fp, directory->songs);
-
- if (directory->path) {
- fprintf(fp, "%s%s\n", DIRECTORY_END,
- getDirectoryPath(directory));
- }
-}
-
-static void readDirectoryInfo(FILE * fp, Directory * directory)
-{
- char buffer[MAXPATHLEN * 2];
- int bufferSize = MAXPATHLEN * 2;
- char *key;
- Directory *subDirectory;
- int strcmpRet;
- char *name;
- ListNode *nextDirNode = directory->subDirectories->firstNode;
- ListNode *nodeTemp;
-
- while (myFgets(buffer, bufferSize, fp)
- && 0 != strncmp(DIRECTORY_END, buffer, strlen(DIRECTORY_END))) {
- if (0 == strncmp(DIRECTORY_DIR, buffer, strlen(DIRECTORY_DIR))) {
- key = xstrdup(&(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 (0 == strncmp(DIRECTORY_MTIME, buffer,
- strlen(DIRECTORY_MTIME))) {
- if (!myFgets(buffer, bufferSize, fp))
- FATAL("Error reading db, fgets\n");
- }
- if (strncmp
- (DIRECTORY_BEGIN, buffer,
- strlen(DIRECTORY_BEGIN))) {
- FATAL("Error reading db at line: %s\n", buffer);
- }
- name = xstrdup(&(buffer[strlen(DIRECTORY_BEGIN)]));
-
- while (nextDirNode && (strcmpRet =
- strcmp(key,
- nextDirNode->key)) > 0) {
- nodeTemp = nextDirNode->nextNode;
- deleteNodeFromList(directory->subDirectories,
- nextDirNode);
- nextDirNode = nodeTemp;
- }
-
- if (NULL == nextDirNode) {
- subDirectory = newDirectory(name, directory);
- insertInList(directory->subDirectories,
- key, (void *)subDirectory);
- } else if (strcmpRet == 0) {
- subDirectory = (Directory *) nextDirNode->data;
- nextDirNode = nextDirNode->nextNode;
- } else {
- subDirectory = newDirectory(name, directory);
- insertInListBeforeNode(directory->
- subDirectories,
- nextDirNode, -1, key,
- (void *)subDirectory);
- }
-
- free(name);
- free(key);
- readDirectoryInfo(fp, subDirectory);
- } else if (0 == strncmp(SONG_BEGIN, buffer, strlen(SONG_BEGIN))) {
- readSongInfoIntoList(fp, directory->songs, directory);
- } else {
- FATAL("Unknown line in db: %s\n", buffer);
- }
- }
-
- while (nextDirNode) {
- nodeTemp = nextDirNode->nextNode;
- deleteNodeFromList(directory->subDirectories, nextDirNode);
- nextDirNode = nodeTemp;
- }
-}
-
-static void sortDirectory(Directory * directory)
-{
- ListNode *node = directory->subDirectories->firstNode;
- Directory *subDir;
-
- sortList(directory->subDirectories);
- sortList(directory->songs);
-
- while (node != NULL) {
- subDir = (Directory *) node->data;
- sortDirectory(subDir);
- node = node->nextNode;
- }
-}
-
-int checkDirectoryDB(void)
-{
- struct stat st;
- char *dbFile;
- char *dirPath;
- char *dbPath;
-
- 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 */
- dbPath = xstrdup(dbFile);
- dirPath = dirname(dbPath);
-
- /* 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));
- free(dbPath);
- 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);
- free(dbPath);
- 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));
- free(dbPath);
- return -1;
-
- }
-
- free(dbPath);
- 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)
-{
- FILE *fp;
- char *dbFile = getDbFile();
- struct stat st;
-
- DEBUG("removing empty directories from DB\n");
- deleteEmptyDirectoriesInDirectory(mp3rootDirectory);
-
- DEBUG("sorting DB\n");
-
- sortDirectory(mp3rootDirectory);
-
- DEBUG("writing DB\n");
-
- while (!(fp = fopen(dbFile, "w")) && errno == EINTR);
- if (!fp) {
- ERROR("unable to write to db file \"%s\": %s\n",
- dbFile, strerror(errno));
- return -1;
- }
-
- /* block signals when writing the db so we don't get a corrupted db */
- fprintf(fp, "%s\n", DIRECTORY_INFO_BEGIN);
- fprintf(fp, "%s%s\n", DIRECTORY_MPD_VERSION, VERSION);
- fprintf(fp, "%s%s\n", DIRECTORY_FS_CHARSET, getFsCharset());
- fprintf(fp, "%s\n", DIRECTORY_INFO_END);
-
- writeDirectoryInfo(fp, mp3rootDirectory);
-
- while (fclose(fp) && errno == EINTR);
-
- 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 (!mp3rootDirectory)
- mp3rootDirectory = 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 (0 == strncmp(DIRECTORY_MPD_VERSION, buffer,
- strlen(DIRECTORY_MPD_VERSION)))
- {
- if (foundVersion)
- FATAL("already found version in db\n");
- foundVersion = 1;
- } else if (0 ==
- strncmp(DIRECTORY_FS_CHARSET, buffer,
- strlen
- (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");
- fseek(fp, 0, SEEK_SET);
- return -1;
- }
- }
-
- DEBUG("reading DB\n");
-
- readDirectoryInfo(fp, mp3rootDirectory);
- while (fclose(fp) && errno == EINTR) ;
-
- stats.numberOfSongs = countSongsIn(STDERR_FILENO, NULL);
- stats.dbPlayTime = sumSongTimesIn(STDERR_FILENO, NULL);
-
- if (stat(dbFile, &st) == 0)
- directory_dbModTime = st.st_mtime;
-
- return 0;
-}
-
-void updateMp3Directory(void)
-{
- switch (updateDirectory(mp3rootDirectory)) {
- case 0:
- /* nothing updated */
- return;
- case 1:
- if (writeDirectoryDB() < 0)
- exit(EXIT_FAILURE);
- break;
- default:
- /* something was updated and db should be written */
- FATAL("problems updating music db\n");
- }
-
- return;
-}
-
-static int traverseAllInSubDirectory(int fd, Directory * directory,
- int (*forEachSong) (int, Song *,
- void *),
- int (*forEachDir) (int, Directory *,
- void *), void *data)
-{
- ListNode *node = directory->songs->firstNode;
- Song *song;
- Directory *dir;
- int errFlag = 0;
-
- if (forEachDir) {
- errFlag = forEachDir(fd, directory, data);
- if (errFlag)
- return errFlag;
- }
-
- if (forEachSong) {
- while (node != NULL && !errFlag) {
- song = (Song *) node->data;
- errFlag = forEachSong(fd, song, data);
- node = node->nextNode;
- }
- if (errFlag)
- return errFlag;
- }
-
- node = directory->subDirectories->firstNode;
-
- while (node != NULL && !errFlag) {
- dir = (Directory *) node->data;
- errFlag = traverseAllInSubDirectory(fd, dir, forEachSong,
- forEachDir, data);
- node = node->nextNode;
- }
-
- return errFlag;
-}
-
-int traverseAllIn(int fd, char *name,
- int (*forEachSong) (int, Song *, void *),
- int (*forEachDir) (int, Directory *, void *), void *data)
-{
- Directory *directory;
-
- if ((directory = getDirectory(name)) == NULL) {
- Song *song;
- if ((song = getSongFromDB(name)) && forEachSong) {
- return forEachSong(fd, song, data);
- }
- commandError(fd, ACK_ERROR_NO_EXIST,
- "directory or file not found");
- return -1;
- }
-
- return traverseAllInSubDirectory(fd, directory, forEachSong, forEachDir,
- data);
-}
-
-static void freeAllDirectoryStats(Directory * directory)
-{
- ListNode *node = directory->subDirectories->firstNode;
-
- while (node != NULL) {
- freeAllDirectoryStats((Directory *) node->data);
- node = node->nextNode;
- }
-
- freeDirectoryStatFromDirectory(directory);
-}
-
-void initMp3Directory(void)
-{
- mp3rootDirectory = newDirectory(NULL, NULL);
- exploreDirectory(mp3rootDirectory);
- freeAllDirectoryStats(mp3rootDirectory);
- stats.numberOfSongs = countSongsIn(STDERR_FILENO, NULL);
- stats.dbPlayTime = sumSongTimesIn(STDERR_FILENO, NULL);
-}
-
-static Song *getSongDetails(char *file, char **shortnameRet,
- Directory ** directoryRet)
-{
- void *song = NULL;
- Directory *directory;
- char *dir = NULL;
- char *dup = xstrdup(file);
- char *shortname = dup;
- char *c = strtok(dup, "/");
-
- DEBUG("get song: %s\n", file);
-
- while (c) {
- shortname = c;
- c = strtok(NULL, "/");
- }
-
- if (shortname != dup) {
- for (c = dup; c < shortname - 1; c++) {
- if (*c == '\0')
- *c = '/';
- }
- dir = dup;
- }
-
- if (!(directory = getDirectory(dir))) {
- free(dup);
- return NULL;
- }
-
- if (!findInList(directory->songs, shortname, &song)) {
- free(dup);
- return NULL;
- }
-
- free(dup);
- if (shortnameRet)
- *shortnameRet = shortname;
- if (directoryRet)
- *directoryRet = directory;
- return (Song *) song;
-}
-
-Song *getSongFromDB(char *file)
-{
- return getSongDetails(file, NULL, NULL);
-}
-
-time_t getDbModTime(void)
-{
- return directory_dbModTime;
-}