directory: moved code to database.c
Taming the directory.c monster, part II: move the database management stuff to database. directory.c should only contain code which works on directory objects.
This commit is contained in:
		| @@ -54,6 +54,7 @@ mpd_headers = \ | ||||
| 	decoder_api.h \ | ||||
| 	decoder_internal.h \ | ||||
| 	directory.h \ | ||||
| 	database.h \ | ||||
| 	update.h \ | ||||
| 	dirvec.h \ | ||||
| 	gcc.h \ | ||||
| @@ -133,6 +134,7 @@ mpd_SOURCES = \ | ||||
| 	decoder_control.c \ | ||||
| 	decoder_api.c \ | ||||
| 	directory.c \ | ||||
| 	database.c \ | ||||
| 	dirvec.c \ | ||||
| 	update.c \ | ||||
| 	decoder_list.c \ | ||||
|   | ||||
| @@ -21,6 +21,7 @@ | ||||
| #include "playlist.h" | ||||
| #include "ls.h" | ||||
| #include "directory.h" | ||||
| #include "database.h" | ||||
| #include "update.h" | ||||
| #include "volume.h" | ||||
| #include "stats.h" | ||||
|   | ||||
							
								
								
									
										324
									
								
								src/database.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										324
									
								
								src/database.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,324 @@ | ||||
| /* 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 "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 <assert.h> | ||||
| #include <string.h> | ||||
|  | ||||
| static struct directory *music_root; | ||||
|  | ||||
| static time_t directory_dbModTime; | ||||
|  | ||||
| void directory_init(void) | ||||
| { | ||||
| 	music_root = newDirectory(NULL, NULL); | ||||
| 	updateDirectory(music_root); | ||||
| 	stats.numberOfSongs = countSongsIn(NULL); | ||||
| 	stats.dbPlayTime = sumSongTimesIn(NULL); | ||||
| } | ||||
|  | ||||
| void directory_finish(void) | ||||
| { | ||||
| 	freeDirectory(music_root); | ||||
| } | ||||
|  | ||||
| struct directory * | ||||
| directory_get_root(void) | ||||
| { | ||||
| 	assert(music_root != NULL); | ||||
|  | ||||
| 	return music_root; | ||||
| } | ||||
|  | ||||
| struct directory * | ||||
| getDirectory(const char *name) | ||||
| { | ||||
| 	if (name == NULL) | ||||
| 		return music_root; | ||||
|  | ||||
| 	return getSubDirectory(music_root, name); | ||||
| } | ||||
|  | ||||
| struct song * | ||||
| getSongFromDB(const char *file) | ||||
| { | ||||
| 	struct song *song = NULL; | ||||
| 	struct 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->parent == directory); | ||||
|  | ||||
| out: | ||||
| 	free(duplicated); | ||||
| 	return song; | ||||
| } | ||||
|  | ||||
| int | ||||
| traverseAllIn(const char *name, | ||||
| 	      int (*forEachSong) (struct song *, void *), | ||||
| 	      int (*forEachDir) (struct directory *, void *), void *data) | ||||
| { | ||||
| 	struct directory *directory; | ||||
|  | ||||
| 	if ((directory = getDirectory(name)) == NULL) { | ||||
| 		struct song *song; | ||||
| 		if ((song = getSongFromDB(name)) && forEachSong) { | ||||
| 			return forEachSong(song, data); | ||||
| 		} | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return traverseAllInSubDirectory(directory, forEachSong, forEachDir, | ||||
| 					 data); | ||||
| } | ||||
|  | ||||
| int printDirectoryInfo(struct client *client, const char *name) | ||||
| { | ||||
| 	struct directory *directory; | ||||
|  | ||||
| 	if ((directory = getDirectory(name)) == NULL) | ||||
| 		return -1; | ||||
|  | ||||
| 	return directory_print(client, directory); | ||||
| } | ||||
|  | ||||
| static char *getDbFile(void) | ||||
| { | ||||
| 	ConfigParam *param = parseConfigFilePath(CONF_DB_FILE, 1); | ||||
|  | ||||
| 	assert(param); | ||||
| 	assert(param->value); | ||||
|  | ||||
| 	return param->value; | ||||
| } | ||||
|  | ||||
| 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) | ||||
| { | ||||
| 	FILE *fp; | ||||
| 	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"); | ||||
|  | ||||
| 	fp = fopen(dbFile, "w"); | ||||
| 	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); | ||||
|  | ||||
| 	if (writeDirectoryInfo(fp, music_root) < 0) { | ||||
| 		ERROR("Failed to write to database file: %s\n", | ||||
| 		      strerror(errno)); | ||||
| 		while (fclose(fp) && errno == EINTR); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	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 (!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; | ||||
| } | ||||
|  | ||||
| time_t getDbModTime(void) | ||||
| { | ||||
| 	return directory_dbModTime; | ||||
| } | ||||
							
								
								
									
										55
									
								
								src/database.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/database.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| /* 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 <sys/time.h> | ||||
|  | ||||
| struct directory; | ||||
| struct client; | ||||
|  | ||||
| void directory_init(void); | ||||
|  | ||||
| void directory_finish(void); | ||||
|  | ||||
| struct directory * | ||||
| directory_get_root(void); | ||||
|  | ||||
| struct directory * | ||||
| getDirectory(const char *name); | ||||
|  | ||||
| struct song * | ||||
| getSongFromDB(const char *file); | ||||
|  | ||||
| int traverseAllIn(const char *name, | ||||
| 		  int (*forEachSong) (struct song *, void *), | ||||
| 		  int (*forEachDir) (struct directory *, void *), void *data); | ||||
|  | ||||
| int printDirectoryInfo(struct client *client, const char *name); | ||||
|  | ||||
| int checkDirectoryDB(void); | ||||
|  | ||||
| int writeDirectoryDB(void); | ||||
|  | ||||
| int readDirectoryDB(void); | ||||
|  | ||||
| time_t getDbModTime(void); | ||||
|  | ||||
| #endif | ||||
| @@ -19,6 +19,7 @@ | ||||
| #include "dbUtils.h" | ||||
|  | ||||
| #include "directory.h" | ||||
| #include "database.h" | ||||
| #include "client.h" | ||||
| #include "utils.h" | ||||
| #include "playlist.h" | ||||
|   | ||||
							
								
								
									
										318
									
								
								src/directory.c
									
									
									
									
									
								
							
							
						
						
									
										318
									
								
								src/directory.c
									
									
									
									
									
								
							| @@ -17,44 +17,15 @@ | ||||
|  */ | ||||
|  | ||||
| #include "directory.h" | ||||
| #include "database.h" | ||||
| #include "song.h" | ||||
| #include "conf.h" | ||||
| #include "log.h" | ||||
| #include "ls.h" | ||||
| #include "path.h" | ||||
| #include "stats.h" | ||||
| #include "utils.h" | ||||
| #include "client.h" | ||||
| #include "dbUtils.h" | ||||
| #include "song_print.h" | ||||
| #include "song_save.h" | ||||
| #include "dirvec.h" | ||||
| #include "update.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: " | ||||
|  | ||||
| static struct directory *music_root; | ||||
|  | ||||
| static time_t directory_dbModTime; | ||||
|  | ||||
| static void deleteEmptyDirectoriesInDirectory(struct directory * directory); | ||||
|  | ||||
| static char *getDbFile(void) | ||||
| { | ||||
| 	ConfigParam *param = parseConfigFilePath(CONF_DB_FILE, 1); | ||||
|  | ||||
| 	assert(param); | ||||
| 	assert(param->value); | ||||
|  | ||||
| 	return param->value; | ||||
| } | ||||
|  | ||||
| struct directory * | ||||
| newDirectory(const char *dirname, struct directory * parent) | ||||
| @@ -82,7 +53,8 @@ freeDirectory(struct directory * directory) | ||||
| 	/*getDirectoryPath(NULL); */ | ||||
| } | ||||
|  | ||||
| static void deleteEmptyDirectoriesInDirectory(struct directory * directory) | ||||
| void | ||||
| deleteEmptyDirectoriesInDirectory(struct directory *directory) | ||||
| { | ||||
| 	int i; | ||||
| 	struct dirvec *dv = &directory->children; | ||||
| @@ -96,20 +68,7 @@ static void deleteEmptyDirectoriesInDirectory(struct directory * directory) | ||||
| 		dirvec_destroy(dv); | ||||
| } | ||||
|  | ||||
| void directory_finish(void) | ||||
| { | ||||
| 	freeDirectory(music_root); | ||||
| } | ||||
|  | ||||
| struct directory * | ||||
| directory_get_root(void) | ||||
| { | ||||
| 	assert(music_root != NULL); | ||||
|  | ||||
| 	return music_root; | ||||
| } | ||||
|  | ||||
| static struct directory * | ||||
| getSubDirectory(struct directory * directory, const char *name) | ||||
| { | ||||
| 	struct directory *cur = directory; | ||||
| @@ -142,16 +101,8 @@ getSubDirectory(struct directory * directory, const char *name) | ||||
| 	return found; | ||||
| } | ||||
|  | ||||
| struct directory * | ||||
| getDirectory(const char *name) | ||||
| { | ||||
| 	if (name == NULL) | ||||
| 		return music_root; | ||||
|  | ||||
| 	return getSubDirectory(music_root, name); | ||||
| } | ||||
|  | ||||
| static int printDirectoryList(struct client *client, struct dirvec *dv) | ||||
| static int | ||||
| printDirectoryList(struct client *client, const struct dirvec *dv) | ||||
| { | ||||
| 	size_t i; | ||||
|  | ||||
| @@ -163,13 +114,9 @@ static int printDirectoryList(struct client *client, struct dirvec *dv) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int printDirectoryInfo(struct client *client, const char *name) | ||||
| int | ||||
| directory_print(struct client *client, const struct directory *directory) | ||||
| { | ||||
| 	struct directory *directory; | ||||
|  | ||||
| 	if ((directory = getDirectory(name)) == NULL) | ||||
| 		return -1; | ||||
|  | ||||
| 	printDirectoryList(client, &directory->children); | ||||
| 	songvec_print(client, &directory->songs); | ||||
|  | ||||
| @@ -177,7 +124,7 @@ int printDirectoryInfo(struct client *client, const char *name) | ||||
| } | ||||
|  | ||||
| /* TODO error checking */ | ||||
| static int | ||||
| int | ||||
| writeDirectoryInfo(FILE * fp, struct directory * directory) | ||||
| { | ||||
| 	struct dirvec *children = &directory->children; | ||||
| @@ -211,7 +158,7 @@ writeDirectoryInfo(FILE * fp, struct directory * directory) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void | ||||
| void | ||||
| readDirectoryInfo(FILE * fp, struct directory * directory) | ||||
| { | ||||
| 	char buffer[MPD_PATH_MAX * 2]; | ||||
| @@ -263,190 +210,7 @@ sortDirectory(struct directory * directory) | ||||
| 		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) | ||||
| { | ||||
| 	FILE *fp; | ||||
| 	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"); | ||||
|  | ||||
| 	fp = fopen(dbFile, "w"); | ||||
| 	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); | ||||
|  | ||||
| 	if (writeDirectoryInfo(fp, music_root) < 0) { | ||||
| 		ERROR("Failed to write to database file: %s\n", | ||||
| 		      strerror(errno)); | ||||
| 		while (fclose(fp) && errno == EINTR); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	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 (!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; | ||||
| } | ||||
|  | ||||
| static int | ||||
| int | ||||
| traverseAllInSubDirectory(struct directory * directory, | ||||
| 			  int (*forEachSong) (struct song *, void *), | ||||
| 			  int (*forEachDir) (struct directory *, void *), | ||||
| @@ -471,65 +235,3 @@ traverseAllInSubDirectory(struct directory * directory, | ||||
|  | ||||
| 	return err; | ||||
| } | ||||
|  | ||||
| int | ||||
| traverseAllIn(const char *name, | ||||
| 	      int (*forEachSong) (struct song *, void *), | ||||
| 	      int (*forEachDir) (struct directory *, void *), void *data) | ||||
| { | ||||
| 	struct directory *directory; | ||||
|  | ||||
| 	if ((directory = getDirectory(name)) == NULL) { | ||||
| 		struct 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); | ||||
| 	updateDirectory(music_root); | ||||
| 	stats.numberOfSongs = countSongsIn(NULL); | ||||
| 	stats.dbPlayTime = sumSongTimesIn(NULL); | ||||
| } | ||||
|  | ||||
| struct song * | ||||
| getSongFromDB(const char *file) | ||||
| { | ||||
| 	struct song *song = NULL; | ||||
| 	struct 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->parent == directory); | ||||
|  | ||||
| out: | ||||
| 	free(duplicated); | ||||
| 	return song; | ||||
| } | ||||
|  | ||||
| time_t getDbModTime(void) | ||||
| { | ||||
| 	return directory_dbModTime; | ||||
| } | ||||
|   | ||||
| @@ -23,8 +23,18 @@ | ||||
| #include "list.h" | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <stdio.h> | ||||
| #include <sys/stat.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: " | ||||
|  | ||||
| struct client; | ||||
|  | ||||
| struct dirvec { | ||||
| @@ -42,19 +52,12 @@ struct directory { | ||||
| 	unsigned stat; /* not needed if ino_t == dev_t == 0 is impossible */ | ||||
| }; | ||||
|  | ||||
| void directory_init(void); | ||||
|  | ||||
| void directory_finish(void); | ||||
|  | ||||
| static inline bool | ||||
| isRootDirectory(const char *name) | ||||
| { | ||||
| 	return name[0] == 0 || (name[0] == '/' && name[1] == 0); | ||||
| } | ||||
|  | ||||
| struct directory * | ||||
| directory_get_root(void); | ||||
|  | ||||
| struct directory * | ||||
| newDirectory(const char *dirname, struct directory * parent); | ||||
|  | ||||
| @@ -67,28 +70,28 @@ directory_is_empty(struct directory *directory) | ||||
| 	return directory->children.nr == 0 && directory->songs.nr == 0; | ||||
| } | ||||
|  | ||||
| void | ||||
| deleteEmptyDirectoriesInDirectory(struct directory *directory); | ||||
|  | ||||
| struct directory * | ||||
| getDirectory(const char *name); | ||||
| getSubDirectory(struct directory *directory, const char *name); | ||||
|  | ||||
| int | ||||
| directory_print(struct client *client, const struct directory *directory); | ||||
|  | ||||
| int | ||||
| writeDirectoryInfo(FILE *fp, struct directory *directory); | ||||
|  | ||||
| void | ||||
| readDirectoryInfo(FILE *fp, struct directory *directory); | ||||
|  | ||||
| void | ||||
| sortDirectory(struct directory * directory); | ||||
|  | ||||
| int printDirectoryInfo(struct client *client, const char *dirname); | ||||
|  | ||||
| int checkDirectoryDB(void); | ||||
|  | ||||
| int writeDirectoryDB(void); | ||||
|  | ||||
| int readDirectoryDB(void); | ||||
|  | ||||
| struct song * | ||||
| getSongFromDB(const char *file); | ||||
|  | ||||
| time_t getDbModTime(void); | ||||
|  | ||||
| int traverseAllIn(const char *name, | ||||
| 		  int (*forEachSong) (struct song *, void *), | ||||
| 		  int (*forEachDir) (struct directory *, void *), void *data); | ||||
| int | ||||
| traverseAllInSubDirectory(struct directory * directory, | ||||
| 			  int (*forEachSong) (struct song *, void *), | ||||
| 			  int (*forEachDir) (struct directory *, void *), | ||||
| 			  void *data); | ||||
|  | ||||
| #define getDirectoryPath(dir) ((dir && dir->path) ? dir->path : "") | ||||
|  | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
| #include "client.h" | ||||
| #include "command.h" | ||||
| #include "playlist.h" | ||||
| #include "directory.h" | ||||
| #include "database.h" | ||||
| #include "update.h" | ||||
| #include "player_thread.h" | ||||
| #include "listen.h" | ||||
|   | ||||
| @@ -25,7 +25,7 @@ | ||||
| #include "song_print.h" | ||||
| #include "client.h" | ||||
| #include "conf.h" | ||||
| #include "directory.h" | ||||
| #include "database.h" | ||||
| #include "log.h" | ||||
| #include "path.h" | ||||
| #include "utils.h" | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
|  | ||||
| #include "sig_handlers.h" | ||||
| #include "playlist.h" | ||||
| #include "directory.h" | ||||
| #include "database.h" | ||||
| #include "update.h" | ||||
| #include "command.h" | ||||
| #include "signal_check.h" | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
|  | ||||
| #include "stats.h" | ||||
|  | ||||
| #include "directory.h" | ||||
| #include "database.h" | ||||
| #include "tag.h" | ||||
| #include "song.h" | ||||
| #include "client.h" | ||||
|   | ||||
| @@ -21,7 +21,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) | ||||
|   | ||||
| @@ -18,6 +18,7 @@ | ||||
|  */ | ||||
|  | ||||
| #include "update.h" | ||||
| #include "database.h" | ||||
| #include "directory.h" | ||||
| #include "song.h" | ||||
| #include "log.h" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann