aboutsummaryrefslogtreecommitdiffstats
path: root/src/directory.c
diff options
context:
space:
mode:
authorWarren Dukes <warren.dukes@gmail.com>2004-02-23 23:41:20 +0000
committerWarren Dukes <warren.dukes@gmail.com>2004-02-23 23:41:20 +0000
commitd35747a40c7dea12de95f78a4f283a45ef687597 (patch)
tree3a85f138e68c7ef3e5a9ece863ed7266ad1cd7ce /src/directory.c
downloadmpd-d35747a40c7dea12de95f78a4f283a45ef687597.tar.gz
mpd-d35747a40c7dea12de95f78a4f283a45ef687597.tar.xz
mpd-d35747a40c7dea12de95f78a4f283a45ef687597.zip
import from SF CVS
git-svn-id: https://svn.musicpd.org/mpd/trunk@1 09075e82-0dd4-0310-85a5-a0d7c8717e4f
Diffstat (limited to '')
-rw-r--r--src/directory.c841
1 files changed, 841 insertions, 0 deletions
diff --git a/src/directory.c b/src/directory.c
new file mode 100644
index 000000000..c933a9f0c
--- /dev/null
+++ b/src/directory.c
@@ -0,0 +1,841 @@
+/* the Music Player Daemon (MPD)
+ * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
+ * 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 "ls.h"
+#include "command.h"
+#include "tables.h"
+#include "utils.h"
+#include "path.h"
+#include "log.h"
+#include "playlist.h"
+#include "conf.h"
+#include "stats.h"
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.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_SEARCH_ALBUM "album"
+#define DIRECTORY_SEARCH_ARTIST "artist"
+#define DIRECTORY_SEARCH_TITLE "title"
+#define DIRECTORY_SEARCH_FILENAME "filename"
+
+typedef List DirectoryList;
+
+typedef struct _Directory {
+ char * utf8name;
+ DirectoryList * subDirectories;
+ struct _Directory * parentDirectory;
+ SongList * songs;
+ time_t mtime; /* modification time */
+} Directory;
+
+Directory * mp3rootDirectory;
+
+char directorydb[MAXPATHLEN+1];
+
+DirectoryList * newDirectoryList();
+
+int addToDirectory(Directory * directory, char * shortname, char * name);
+
+void freeDirectoryList(DirectoryList * list);
+
+void freeDirectory(Directory * directory);
+
+int exploreDirectory(Directory * directory);
+
+int updateDirectory(Directory * directory);
+
+int addSubDirectoryToDirectory(Directory * directory, char * shortname, char * name);
+
+Directory * newDirectory(Directory * parentDirectory, char * dirname, time_t mtime) {
+ Directory * directory;
+
+ directory = malloc(sizeof(Directory));
+
+ if(dirname!=NULL) directory->utf8name = strdup(dirname);
+ else directory->utf8name = NULL;
+ directory->parentDirectory = parentDirectory;
+ directory->subDirectories = newDirectoryList();
+ directory->songs = newSongList();
+ if(mtime<0) directory->mtime = isDir(dirname);
+ else directory->mtime = mtime;
+
+ return directory;
+}
+
+void freeDirectory(Directory * directory) {
+ freeDirectoryList(directory->subDirectories);
+ removeSongsFromTables(directory->songs);
+ deleteSongsFromPlaylist(directory->songs);
+ freeSongList(directory->songs);
+ if(directory->utf8name) free(directory->utf8name);
+ free(directory);
+}
+
+DirectoryList * newDirectoryList() {
+ return makeList((ListFreeDataFunc *)freeDirectory);
+}
+
+void freeDirectoryList(DirectoryList * directoryList) {
+ freeList(directoryList);
+}
+
+void removeSongFromDirectory(Directory * directory, char * shortname) {
+ void * song;
+
+ if(findInList(directory->songs,shortname,&song)) {
+ removeASongFromTables((Song *)song);
+ deleteASongFromPlaylist((Song *)song);
+ deleteFromList(directory->songs,shortname);
+ }
+}
+
+int updateInDirectory(Directory * directory, char * shortname, char * name) {
+ time_t mtime;
+ void * song;
+ void * subDir;
+
+ if((mtime = isMusic(name))) {
+ if(0==findInList(directory->songs,shortname,&song)) {
+ LOG("adding %s\n",name);
+ addToDirectory(directory,shortname,name);
+ }
+ else if(mtime>((Song *)song)->mtime) {
+ LOG("updating %s\n",name);
+ updateSongInfo((Song *)song);
+ }
+ }
+ else if((mtime = isDir(name))) {
+ if(findInList(directory->subDirectories,shortname,(void **)&subDir)) {
+ updateDirectory((Directory *)subDir);
+ }
+ else addSubDirectoryToDirectory(directory,shortname,name);
+ }
+
+ return 0;
+}
+
+int removeDeletedFromDirectory(Directory * directory) {
+ DIR * dir;
+ char cwd[2];
+ struct dirent * ent;
+ char * dirname = directory->utf8name;
+ List * entList = makeList(free);
+ void * name;
+ char * s;
+ char * utf8;
+ ListNode * node;
+ ListNode * tmpNode;
+
+ cwd[0] = '.';
+ cwd[1] = '\0';
+ if(dirname==NULL) dirname=cwd;
+
+ if((dir = opendir(rmp2amp(utf8ToFsCharset(dirname))))==NULL) return -1;
+
+ while((ent = readdir(dir))) {
+ if(ent->d_name[0]=='.') continue; /* hide hidden stuff */
+
+ utf8 = strdup(fsCharsetToUtf8(ent->d_name));
+
+ if(directory->utf8name) {
+ s = malloc(strlen(directory->utf8name)+strlen(utf8)+2);
+ sprintf(s,"%s/%s",directory->utf8name,utf8);
+ }
+ else s= strdup(utf8);
+ insertInList(entList,fsCharsetToUtf8(ent->d_name),s);
+ free(utf8);
+ }
+
+ closedir(dir);
+
+ node = directory->subDirectories->firstNode;
+ while(node) {
+ tmpNode = node->nextNode;
+ if(findInList(entList,node->key,&name)) {
+ if(!isDir((char *)name)) {
+ deleteFromList(directory->subDirectories,node->key);
+ }
+ }
+ else {
+ deleteFromList(directory->subDirectories,node->key);
+ }
+ node = tmpNode;
+ }
+
+ node = directory->songs->firstNode;
+ while(node) {
+ tmpNode = node->nextNode;
+ if(findInList(entList,node->key,(void **)&name)) {
+ if(!isMusic(name)) {
+ removeSongFromDirectory(directory,node->key);
+ }
+ }
+ else {
+ removeSongFromDirectory(directory,node->key);
+ }
+ node = tmpNode;
+ }
+
+ freeList(entList);
+
+ return 0;
+}
+
+int updateDirectory(Directory * directory) {
+ DIR * dir;
+ char cwd[2];
+ struct dirent * ent;
+ char * s;
+ char * utf8;
+ char * dirname = directory->utf8name;
+
+ cwd[0] = '.';
+ cwd[1] = '\0';
+ if(dirname==NULL) dirname=cwd;
+
+ removeDeletedFromDirectory(directory);
+
+ if((dir = opendir(rmp2amp(utf8ToFsCharset(dirname))))==NULL) return -1;
+
+ while((ent = readdir(dir))) {
+ if(ent->d_name[0]=='.') continue; /* hide hidden stuff */
+
+ utf8 = strdup(fsCharsetToUtf8(ent->d_name));
+
+ if(directory->utf8name) {
+ s = malloc(strlen(directory->utf8name)+strlen(utf8)+2);
+ sprintf(s,"%s/%s",directory->utf8name,utf8);
+ }
+ else s = strdup(utf8);
+ updateInDirectory(directory,utf8,s);
+ free(utf8);
+ free(s);
+ }
+
+ closedir(dir);
+
+ if(directory->utf8name) directory->mtime = isDir(directory->utf8name);
+
+ return 0;
+}
+
+int exploreDirectory(Directory * directory) {
+ DIR * dir;
+ char cwd[2];
+ struct dirent * ent;
+ char * s;
+ char * utf8;
+ char * dirname = directory->utf8name;
+
+ 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;
+
+ LOG("explore: %s\n",dirname);
+ while((ent = readdir(dir))) {
+ if(ent->d_name[0]=='.') continue; /* hide hidden stuff */
+
+ utf8 = strdup(fsCharsetToUtf8(ent->d_name));
+
+ DEBUG("explore: found: %s (%s)\n",ent->d_name,utf8);
+
+ if(directory->utf8name) {
+ s = malloc(strlen(directory->utf8name)+strlen(utf8)+2);
+ sprintf(s,"%s/%s",directory->utf8name,utf8);
+ }
+ else s = strdup(utf8);
+ addToDirectory(directory,utf8,s);
+ free(utf8);
+ free(s);
+ }
+
+ closedir(dir);
+
+ return 0;
+}
+
+int addSubDirectoryToDirectory(Directory * directory, char * shortname,
+ char * name)
+{
+ Directory * subDirectory = newDirectory(directory,name,-1);
+
+ insertInList(directory->subDirectories,shortname,subDirectory);
+ exploreDirectory(subDirectory);
+
+ return 0;
+}
+
+int addToDirectory(Directory * directory, char * shortname, char * name) {
+ if(isDir(name)) {
+ return addSubDirectoryToDirectory(directory,shortname,name);
+ }
+ else if(isMusic(name)) {
+ Song * song;
+ song = addSongToList(directory->songs,shortname,name);
+ if(!song) return -1;
+ addSongToTables(song);
+ return 0;
+ }
+
+ DEBUG("addToDirectory: %s is not a directory or music\n",name);
+
+ return -1;
+}
+
+void closeMp3Directory() {
+ freeDirectory(mp3rootDirectory);
+}
+
+Directory * findSubDirectory(Directory * directory,char * name) {
+ void * subDirectory;
+ char * dup = strdup(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;
+}
+
+Directory * getSubDirectory(Directory * directory,char * name) {
+ Directory * subDirectory;
+ int len;
+
+ if(name==NULL || name[0]=='\0' || strcmp(name,"/")==0) {
+ return directory;
+ }
+
+ if((subDirectory = findSubDirectory(directory,name))==NULL) return NULL;
+
+ len = 0;
+ while(name[len]!='/' && name[len]!='\0') len++;
+ while(name[len]=='/') len++;
+
+ return getSubDirectory(subDirectory,&(name[len]));
+}
+
+Directory * getDirectory(char * name) {
+ return getSubDirectory(mp3rootDirectory,name);
+}
+
+int printDirectoryList(FILE * fp, DirectoryList * directoryList) {
+ ListNode * node = directoryList->firstNode;
+ Directory * directory;
+
+ while(node!=NULL) {
+ directory = (Directory *)node->data;
+ myfprintf(fp,"%s%s\n",DIRECTORY_DIR,directory->utf8name);
+ node = node->nextNode;
+ }
+
+ return 0;
+}
+
+int printDirectoryInfo(FILE * fp, char * name) {
+ Directory * directory;
+
+ if((directory = getDirectory(name))==NULL) {
+ myfprintf(fp,"%s: directory not found\n",COMMAND_RESPOND_ERROR);
+ return -1;
+ }
+
+ printDirectoryList(fp,directory->subDirectories);
+ printSongInfoFromList(fp,directory->songs);
+
+ return 0;
+}
+
+void writeDirectoryInfo(FILE * fp, Directory * directory) {
+ ListNode * node = (directory->subDirectories)->firstNode;
+ Directory * subDirectory;
+
+ if(directory->utf8name) {
+ myfprintf(fp,"%s%s\n",DIRECTORY_BEGIN,directory->utf8name);
+ }
+
+ while(node!=NULL) {
+ subDirectory = (Directory *)node->data;
+ myfprintf(fp,"%s%s\n",DIRECTORY_DIR,node->key);
+ myfprintf(fp,"%s%li\n",DIRECTORY_MTIME,(long)subDirectory->mtime);
+ writeDirectoryInfo(fp,subDirectory);
+ node = node->nextNode;
+ }
+
+ writeSongInfoFromList(fp,directory->songs);
+
+ if(directory->utf8name) {
+ myfprintf(fp,"%s%s\n",DIRECTORY_END,directory->utf8name);
+ }
+}
+
+void readDirectoryInfo(FILE * fp,Directory * directory) {
+ char buffer[MAXPATHLEN*2];
+ int bufferSize = MAXPATHLEN*2;
+ char * key;
+ Directory * subDirectory;
+ char * name;
+ time_t mtime;
+
+ while(myFgets(buffer,bufferSize,fp) && 0!=strncmp(DIRECTORY_END,buffer,strlen(DIRECTORY_END))) {
+ if(0==strncmp(DIRECTORY_DIR,buffer,strlen(DIRECTORY_DIR))) {
+ key = strdup(&(buffer[strlen(DIRECTORY_DIR)]));
+ if(myFgets(buffer,bufferSize,fp)<0) {
+ ERROR("Error reading db\n");
+ exit(-1);
+ }
+ if(strncmp(DIRECTORY_MTIME,buffer,strlen(DIRECTORY_MTIME))) {
+ ERROR("Error reading db\n");
+ ERROR("%s\n",buffer);
+ exit(-1);
+ }
+ mtime = atoi(&(buffer[strlen(DIRECTORY_BEGIN)]));
+ if(myFgets(buffer,bufferSize,fp)<0) {
+ ERROR("Error reading db\n");
+ exit(-1);
+ }
+ if(strncmp(DIRECTORY_BEGIN,buffer,strlen(DIRECTORY_BEGIN))) {
+ ERROR("Error reading db\n");
+ exit(-1);
+ }
+ name = strdup(&(buffer[strlen(DIRECTORY_BEGIN)]));
+ subDirectory = newDirectory(directory,name,mtime);
+ insertInList(directory->subDirectories,key,(void *)subDirectory);
+ free(key);
+ free(name);
+ readDirectoryInfo(fp,subDirectory);
+ }
+ else if(0==strncmp(SONG_BEGIN,buffer,strlen(SONG_BEGIN))) {
+ readSongInfoIntoList(fp,directory->songs);
+ }
+ else {
+ ERROR("Unknown line in db: %s\n",buffer);
+ exit(-1);
+ }
+ }
+}
+
+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 writeDirectoryDB() {
+ FILE * fp;
+
+ sortDirectory(mp3rootDirectory);
+ stats.numberOfSongs = countSongsIn(stderr,NULL);
+
+ while(!(fp=fopen(directorydb,"w")) && errno==EINTR);
+ if(!fp) return -1;
+
+ myfprintf(fp,"%s\n",DIRECTORY_INFO_BEGIN);
+ myfprintf(fp,"%s%s\n",DIRECTORY_MPD_VERSION,VERSION);
+ myfprintf(fp,"%s%s\n",DIRECTORY_FS_CHARSET,getFsCharset());
+ myfprintf(fp,"%s\n",DIRECTORY_INFO_END);
+
+ writeDirectoryInfo(fp,mp3rootDirectory);
+ while(fclose(fp) && errno==EINTR);
+
+ return 0;
+}
+
+int readDirectoryDB() {
+ FILE * fp;
+
+ mp3rootDirectory = newDirectory(NULL,NULL,0);
+ while(!(fp=fopen(directorydb,"r")) && errno==EINTR);
+ if(!fp) return -1;
+
+ /* get initial info */
+ {
+ char buffer[100];
+ int bufferSize = 100;
+ int foundFsCharset = 0;
+ int foundVersion = 0;
+
+ if(myFgets(buffer,bufferSize,fp)<0) {
+ ERROR("Error reading db\n");
+ exit(-1);
+ }
+ 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) {
+ ERROR("already found "
+ "version in db\n");
+ exit(-1);
+ }
+ foundVersion = 1;
+ }
+ else if(0==strncmp(DIRECTORY_FS_CHARSET,buffer,
+ strlen(DIRECTORY_FS_CHARSET)))
+ {
+ char * fsCharset;
+
+ if(foundFsCharset) {
+ ERROR("already found "
+ "fs charset in db\n");
+ exit(-1);
+ }
+
+ foundFsCharset = 1;
+
+ fsCharset = &(buffer[strlen(
+ DIRECTORY_FS_CHARSET)]);
+ if(getConf()[CONF_FS_CHARSET] &&
+ strcmp(fsCharset,
+ getFsCharset()))
+ {
+ ERROR("Using \"%s\" for the "
+ "filesystem charset "
+ "instead of \"%s\"\n",
+ fsCharset,
+ getFsCharset());
+ ERROR("maybe you need to "
+ "recreate the db?\n");
+ setFsCharset(fsCharset);
+ }
+ }
+ else {
+ ERROR("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);
+ }
+ }
+
+ readDirectoryInfo(fp,mp3rootDirectory);
+ while(fclose(fp) && errno==EINTR);
+
+ stats.numberOfSongs = countSongsIn(stderr,NULL);
+
+ return 0;
+}
+
+int updateMp3Directory(FILE * fp) {
+ if(updateDirectory(mp3rootDirectory)<0) {
+ ERROR("problems updating music db\n");
+ myfprintf(fp,"%s problems updating music db\n",COMMAND_RESPOND_ERROR);
+ return -1;
+ }
+
+ if(writeDirectoryDB()<0) {
+ ERROR("problems writing music db file, \"%s\"\n",directorydb);
+ myfprintf(fp,"%s problems writing music db\n",COMMAND_RESPOND_ERROR);
+ return -1;
+ }
+
+ return 0;
+}
+
+int traverseAllInSubDirectory(FILE * fp, Directory * directory,
+ int (*forEachSong)(FILE *, Song *, void *),
+ int (*forEachDir)(FILE *, Directory *, void *),
+ void * data)
+{
+ ListNode * node = directory->songs->firstNode;
+ Song * song;
+ Directory * dir;
+ int errFlag = 0;
+
+ if(forEachDir) {
+ errFlag = forEachDir(fp,directory,data);
+ if(errFlag) return errFlag;
+ }
+
+ if(forEachSong) {
+ while(node!=NULL && !errFlag) {
+ song = (Song *)node->data;
+ errFlag = forEachSong(fp,song,data);
+ node = node->nextNode;
+ }
+ if(errFlag) return errFlag;
+ }
+
+ node = directory->subDirectories->firstNode;
+
+ while(node!=NULL && !errFlag) {
+ dir = (Directory *)node->data;
+ errFlag = traverseAllInSubDirectory(fp,dir,forEachSong,
+ forEachDir,data);
+ node = node->nextNode;
+ }
+
+ return errFlag;
+}
+
+int traverseAllIn(FILE * fp, char * name,
+ int (*forEachSong)(FILE *, Song *, void *),
+ int (*forEachDir)(FILE *, Directory *, void *),
+ void * data) {
+ Directory * directory;
+
+ if((directory = getDirectory(name))==NULL) {
+ Song * song;
+ if((song = getSong(name)) && forEachSong) {
+ return forEachSong(fp, song, data);
+ }
+ myfprintf(fp,"%s: directory or file not found\n",COMMAND_RESPOND_ERROR);
+ return -1;
+ }
+
+ return traverseAllInSubDirectory(fp,directory,forEachSong,forEachDir,
+ data);
+}
+
+int countSongsInDirectory(FILE * fp, Directory * directory, void * data) {
+ int * count = (int *)data;
+
+ *count+=directory->songs->numberOfNodes;
+
+ return 0;
+}
+
+int printDirectoryInDirectory(FILE * fp, Directory * directory, void * data) {
+ if(directory->utf8name) {
+ myfprintf(fp,"directory: %s\n",directory->utf8name);
+ }
+ return 0;
+}
+
+int printSongInDirectory(FILE * fp, Song * song, void * data) {
+ myfprintf(fp,"file: %s\n",song->utf8file);
+ return 0;
+}
+
+int searchForAlbumInDirectory(FILE * fp, Song * song, void * string) {
+ if(song->tag && song->tag->album) {
+ char * dup = strDupToUpper(song->tag->album);
+ if(strstr(dup,(char *)string)) printSongInfo(fp,song);
+ free(dup);
+ }
+ return 0;
+}
+
+int searchForArtistInDirectory(FILE * fp, Song * song, void * string) {
+ if(song->tag && song->tag->artist) {
+ char * dup = strDupToUpper(song->tag->artist);
+ if(strstr(dup,(char *)string)) printSongInfo(fp,song);
+ free(dup);
+ }
+ return 0;
+}
+
+int searchForTitleInDirectory(FILE * fp, Song * song, void * string) {
+ if(song->tag && song->tag->title) {
+ char * dup = strDupToUpper(song->tag->title);
+ if(strstr(dup,(char *)string)) printSongInfo(fp,song);
+ free(dup);
+ }
+ return 0;
+}
+
+int searchForFilenameInDirectory(FILE * fp, Song * song, void * string) {
+ char * dup = strDupToUpper(song->utf8file);
+ if(strstr(dup,(char *)string)) printSongInfo(fp,song);
+ free(dup);
+ return 0;
+}
+
+int searchForSongsIn(FILE * fp, char * name, char * item, char * string) {
+ char * dup = strDupToUpper(string);
+ int ret = -1;
+
+ if(strcmp(item,DIRECTORY_SEARCH_ALBUM)==0) {
+ ret = traverseAllIn(fp,name,searchForAlbumInDirectory,NULL,
+ (void *)dup);
+ }
+ else if(strcmp(item,DIRECTORY_SEARCH_ARTIST)==0) {
+ ret = traverseAllIn(fp,name,searchForArtistInDirectory,NULL,
+ (void *)dup);
+ }
+ else if(strcmp(item,DIRECTORY_SEARCH_TITLE)==0) {
+ ret = traverseAllIn(fp,name,searchForTitleInDirectory,NULL,
+ (void *)dup);
+ }
+ else if(strcmp(item,DIRECTORY_SEARCH_FILENAME)==0) {
+ ret = traverseAllIn(fp,name,searchForFilenameInDirectory,NULL,
+ (void *)dup);
+ }
+ else myfprintf(fp,"%s unknown table\n",COMMAND_RESPOND_ERROR);
+
+ free(dup);
+
+ return ret;
+}
+
+int findAlbumInDirectory(FILE * fp, Song * song, void * string) {
+ if(song->tag && song->tag->album &&
+ strcmp((char *)string,song->tag->album)==0)
+ {
+ printSongInfo(fp,song);
+ }
+
+ return 0;
+}
+
+int findArtistInDirectory(FILE * fp, Song * song, void * string) {
+ if(song->tag && song->tag->artist &&
+ strcmp((char *)string,song->tag->artist)==0)
+ {
+ printSongInfo(fp,song);
+ }
+
+ return 0;
+}
+
+int findSongsIn(FILE * fp, char * name, char * item, char * string) {
+ if(strcmp(item,DIRECTORY_SEARCH_ALBUM)==0) {
+ return traverseAllIn(fp,name,findAlbumInDirectory,NULL,
+ (void *)string);
+ }
+ else if(strcmp(item,DIRECTORY_SEARCH_ARTIST)==0) {
+ return traverseAllIn(fp,name,findArtistInDirectory,NULL,
+ (void *)string);
+ }
+
+ myfprintf(fp,"%s unknown table\n",COMMAND_RESPOND_ERROR);
+ return -1;
+}
+
+int printAllIn(FILE * fp, char * name) {
+ return traverseAllIn(fp,name,printSongInDirectory,
+ printDirectoryInDirectory,NULL);
+}
+
+int directoryAddSongToPlaylist(FILE * fp, Song * song, void * data) {
+ return addSongToPlaylist(fp,song);
+}
+
+int addAllIn(FILE * fp, char * name) {
+ return traverseAllIn(fp,name,directoryAddSongToPlaylist,NULL,NULL);
+}
+
+int directoryPrintSongInfo(FILE * fp, Song * song, void * data) {
+ return printSongInfo(fp,song);
+}
+
+int printInfoForAllIn(FILE * fp, char * name) {
+ return traverseAllIn(fp,name,directoryPrintSongInfo,NULL,NULL);
+}
+
+int countSongsIn(FILE * fp, char * name) {
+ int count = 0;
+ void * ptr = (void *)&count;
+
+ traverseAllIn(fp,name,NULL,countSongsInDirectory,ptr);
+
+ return count;
+}
+
+void initMp3Directory() {
+ mp3rootDirectory = newDirectory(NULL,NULL,0);
+ exploreDirectory(mp3rootDirectory);
+}
+
+Song * getSong(char * file) {
+ void * song;
+ Directory * directory;
+ char * dir = NULL;
+ char * dup = strdup(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 = getSubDirectory(mp3rootDirectory,dir))) {
+ free(dup);
+ return NULL;
+ }
+
+ if(!findInList(directory->songs,shortname,&song)) {
+ free(dup);
+ return NULL;
+ }
+
+ free(dup);
+ return (Song *)song;
+}
+
+time_t getDbModTime() {
+ time_t mtime = 0;
+ struct stat st;
+
+ if(stat(directorydb,&st)==0) mtime = st.st_mtime;
+
+ return mtime;
+}