Committing pat's rewrite of the stored playlist code. This also adds two
new commands: playlistmove and playlistdelete. git-svn-id: https://svn.musicpd.org/mpd/trunk@6116 09075e82-0dd4-0310-85a5-a0d7c8717e4f
This commit is contained in:
		| @@ -18,6 +18,8 @@ ver 0.13.0 (2007/??/??) | |||||||
| * Fix a bug where no ACK was returned if loading a playlist failed | * Fix a bug where no ACK was returned if loading a playlist failed | ||||||
| * Fix a bug where db_update in stats would be 0 after initial database creation | * Fix a bug where db_update in stats would be 0 after initial database creation | ||||||
| * New count command for getting stats on found songs (similar to "find") | * New count command for getting stats on found songs (similar to "find") | ||||||
|  | * New playlistmove command for moving songs in stored playlists | ||||||
|  | * New playlistdelete command for deleting songs from stored playlists | ||||||
| * Lots of bug fixes, cleaned up code, and performance improvements | * Lots of bug fixes, cleaned up code, and performance improvements | ||||||
|  |  | ||||||
| ver 0.12.2 (2007/3/20) | ver 0.12.2 (2007/3/20) | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								TODO
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								TODO
									
									
									
									
									
								
							| @@ -40,12 +40,8 @@ | |||||||
| 	*) abstract out state code from playlist.c | 	*) abstract out state code from playlist.c | ||||||
| 	*) put MPD Version in statefile | 	*) put MPD Version in statefile | ||||||
|  |  | ||||||
| *) rewrite saved playlist code | *) add playlistreplace command (replace current playlist with saved playlist | ||||||
| 	*) abstract out saved playlists from playlist.c |                                 and keep playing) | ||||||
| 	*) new commands |  | ||||||
| 		*) replace <playlist> /* replace current playlist |  | ||||||
| 						with saved playlist and |  | ||||||
| 						keep playing */ |  | ||||||
|  |  | ||||||
| *) add command for inserting songs in a specific position | *) add command for inserting songs in a specific position | ||||||
|  |  | ||||||
|   | |||||||
| @@ -74,7 +74,8 @@ mpd_headers = \ | |||||||
| 	utils.h \ | 	utils.h \ | ||||||
| 	volume.h \ | 	volume.h \ | ||||||
| 	zeroconf.h \ | 	zeroconf.h \ | ||||||
| 	locate.h | 	locate.h \ | ||||||
|  | 	storedPlaylist.h | ||||||
|  |  | ||||||
|  |  | ||||||
| mpd_SOURCES = \ | mpd_SOURCES = \ | ||||||
| @@ -125,7 +126,8 @@ mpd_SOURCES = \ | |||||||
| 	volume.c \ | 	volume.c \ | ||||||
| 	utf8.c \ | 	utf8.c \ | ||||||
| 	zeroconf.c \ | 	zeroconf.c \ | ||||||
| 	locate.c | 	locate.c \ | ||||||
|  | 	storedPlaylist.c | ||||||
|  |  | ||||||
|  |  | ||||||
| mpd_CFLAGS = $(MPD_CFLAGS) | mpd_CFLAGS = $(MPD_CFLAGS) | ||||||
|   | |||||||
| @@ -30,6 +30,7 @@ | |||||||
| #include "log.h" | #include "log.h" | ||||||
| #include "tag.h" | #include "tag.h" | ||||||
| #include "utils.h" | #include "utils.h" | ||||||
|  | #include "storedPlaylist.h" | ||||||
|  |  | ||||||
| #include <assert.h> | #include <assert.h> | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
| @@ -95,6 +96,8 @@ | |||||||
| #define COMMAND_PLAYLISTADD	"playlistadd" | #define COMMAND_PLAYLISTADD	"playlistadd" | ||||||
| #define COMMAND_PLAYLISTFIND	"playlistfind" | #define COMMAND_PLAYLISTFIND	"playlistfind" | ||||||
| #define COMMAND_PLAYLISTSEARCH	"playlistsearch" | #define COMMAND_PLAYLISTSEARCH	"playlistsearch" | ||||||
|  | #define COMMAND_PLAYLISTMOVE	"playlistmove" | ||||||
|  | #define COMMAND_PLAYLISTDELETE	"playlistdelete" | ||||||
| #define COMMAND_TAGTYPES	"tagtypes" | #define COMMAND_TAGTYPES	"tagtypes" | ||||||
| #define COMMAND_COUNT		"count" | #define COMMAND_COUNT		"count" | ||||||
|  |  | ||||||
| @@ -578,6 +581,43 @@ static int handlePlaylistSearch(int fd, int *permission, int argc, char *argv[]) | |||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int handlePlaylistDelete(int fd, int *permission, int argc, char *argv[]) { | ||||||
|  | 	char *playlist = argv[1]; | ||||||
|  | 	int from; | ||||||
|  | 	char *test; | ||||||
|  |  | ||||||
|  | 	from = strtol(argv[2], &test, 10); | ||||||
|  | 	if (*test != '\0') { | ||||||
|  | 		commandError(fd, ACK_ERROR_ARG, | ||||||
|  | 			     "\"%s\" is not a integer", argv[2]); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return removeOneSongFromStoredPlaylistByPath(fd, playlist, from); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int handlePlaylistMove(int fd, int *permission, int argc, char *argv[]) | ||||||
|  | { | ||||||
|  | 	char *playlist = argv[1]; | ||||||
|  | 	int from, to; | ||||||
|  | 	char *test; | ||||||
|  |  | ||||||
|  | 	from = strtol(argv[2], &test, 10); | ||||||
|  | 	if (*test != '\0') { | ||||||
|  | 		commandError(fd, ACK_ERROR_ARG, | ||||||
|  | 			     "\"%s\" is not a integer", argv[2]); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 	to = strtol(argv[3], &test, 10); | ||||||
|  | 	if (*test != '\0') { | ||||||
|  | 		commandError(fd, ACK_ERROR_ARG, | ||||||
|  | 			     "\"%s\" is not a integer", argv[3]); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return moveSongInStoredPlaylistByPath(fd, playlist, from, to); | ||||||
|  | } | ||||||
|  |  | ||||||
| static int listHandleUpdate(int fd, | static int listHandleUpdate(int fd, | ||||||
| 			    int *permission, | 			    int *permission, | ||||||
| 			    int argc, | 			    int argc, | ||||||
| @@ -1072,6 +1112,8 @@ void initCommands(void) | |||||||
| 	addCommand(COMMAND_PLAYLISTADD,      PERMISSION_CONTROL, 2,   2,   handlePlaylistAdd,          NULL); | 	addCommand(COMMAND_PLAYLISTADD,      PERMISSION_CONTROL, 2,   2,   handlePlaylistAdd,          NULL); | ||||||
| 	addCommand(COMMAND_PLAYLISTFIND,     PERMISSION_READ,    2,   -1,  handlePlaylistFind,         NULL); | 	addCommand(COMMAND_PLAYLISTFIND,     PERMISSION_READ,    2,   -1,  handlePlaylistFind,         NULL); | ||||||
| 	addCommand(COMMAND_PLAYLISTSEARCH,   PERMISSION_READ,    2,   -1,  handlePlaylistSearch,       NULL); | 	addCommand(COMMAND_PLAYLISTSEARCH,   PERMISSION_READ,    2,   -1,  handlePlaylistSearch,       NULL); | ||||||
|  | 	addCommand(COMMAND_PLAYLISTMOVE,     PERMISSION_CONTROL, 3,   3,   handlePlaylistMove,         NULL); | ||||||
|  | 	addCommand(COMMAND_PLAYLISTDELETE,   PERMISSION_CONTROL, 2,   2,   handlePlaylistDelete,       NULL); | ||||||
| 	addCommand(COMMAND_TAGTYPES,         PERMISSION_READ,    0,   0,   handleTagTypes,             NULL); | 	addCommand(COMMAND_TAGTYPES,         PERMISSION_READ,    0,   0,   handleTagTypes,             NULL); | ||||||
| 	addCommand(COMMAND_COUNT,            PERMISSION_READ,    2,   -1,  handleCount,                NULL); | 	addCommand(COMMAND_COUNT,            PERMISSION_READ,    2,   -1,  handleCount,                NULL); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,6 +26,7 @@ | |||||||
| #include "tag.h" | #include "tag.h" | ||||||
| #include "tagTracker.h" | #include "tagTracker.h" | ||||||
| #include "log.h" | #include "log.h" | ||||||
|  | #include "storedPlaylist.h" | ||||||
|  |  | ||||||
| typedef struct _ListCommandItem { | typedef struct _ListCommandItem { | ||||||
| 	mpd_sint8 tagType; | 	mpd_sint8 tagType; | ||||||
| @@ -177,7 +178,9 @@ static int directoryAddSongToPlaylist(int fd, Song * song, void *data) | |||||||
|  |  | ||||||
| static int directoryAddSongToStoredPlaylist(int fd, Song *song, void *data) | static int directoryAddSongToStoredPlaylist(int fd, Song *song, void *data) | ||||||
| { | { | ||||||
| 	return addSongToStoredPlaylist(fd, song, (char *)data); | 	if (appendSongToStoredPlaylistByPath(fd, (char *)data, song) != 0) | ||||||
|  | 		return -1; | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int addAllIn(int fd, char *name) | int addAllIn(int fd, char *name) | ||||||
|   | |||||||
							
								
								
									
										311
									
								
								src/playlist.c
									
									
									
									
									
								
							
							
						
						
									
										311
									
								
								src/playlist.c
									
									
									
									
									
								
							| @@ -28,6 +28,7 @@ | |||||||
| #include "utils.h" | #include "utils.h" | ||||||
| #include "sig_handlers.h" | #include "sig_handlers.h" | ||||||
| #include "state_file.h" | #include "state_file.h" | ||||||
|  | #include "storedPlaylist.h" | ||||||
|  |  | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| @@ -37,8 +38,6 @@ | |||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
|  |  | ||||||
| #define PLAYLIST_COMMENT	'#' |  | ||||||
|  |  | ||||||
| #define PLAYLIST_STATE_STOP		0 | #define PLAYLIST_STATE_STOP		0 | ||||||
| #define PLAYLIST_STATE_PLAY		1 | #define PLAYLIST_STATE_PLAY		1 | ||||||
|  |  | ||||||
| @@ -64,21 +63,6 @@ | |||||||
| #define DEFAULT_PLAYLIST_MAX_LENGTH		(1024*16) | #define DEFAULT_PLAYLIST_MAX_LENGTH		(1024*16) | ||||||
| #define DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS	0 | #define DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS	0 | ||||||
|  |  | ||||||
| typedef struct _Playlist { |  | ||||||
| 	Song **songs; |  | ||||||
| 	/* holds version a song was modified on */ |  | ||||||
| 	mpd_uint32 *songMod; |  | ||||||
| 	int *order; |  | ||||||
| 	int *positionToId; |  | ||||||
| 	int *idToPosition; |  | ||||||
| 	int length; |  | ||||||
| 	int current; |  | ||||||
| 	int queued; |  | ||||||
| 	int repeat; |  | ||||||
| 	int random; |  | ||||||
| 	mpd_uint32 version; |  | ||||||
| } Playlist; |  | ||||||
|  |  | ||||||
| static Playlist playlist; | static Playlist playlist; | ||||||
| static int playlist_state = PLAYLIST_STATE_STOP; | static int playlist_state = PLAYLIST_STATE_STOP; | ||||||
| static int playlist_max_length = DEFAULT_PLAYLIST_MAX_LENGTH; | static int playlist_max_length = DEFAULT_PLAYLIST_MAX_LENGTH; | ||||||
| @@ -87,7 +71,7 @@ static int playlist_errorCount; | |||||||
| static int playlist_queueError; | static int playlist_queueError; | ||||||
| static int playlist_noGoToNext; | static int playlist_noGoToNext; | ||||||
|  |  | ||||||
| static int playlist_saveAbsolutePaths = DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS; | int playlist_saveAbsolutePaths = DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS; | ||||||
|  |  | ||||||
| static void swapOrder(int a, int b); | static void swapOrder(int a, int b); | ||||||
| static int playPlaylistOrderNumber(int fd, int orderNum); | static int playPlaylistOrderNumber(int fd, int orderNum); | ||||||
| @@ -238,39 +222,7 @@ int clearPlaylist(int fd) | |||||||
|  |  | ||||||
| int clearStoredPlaylist(int fd, char *utf8file) | int clearStoredPlaylist(int fd, char *utf8file) | ||||||
| { | { | ||||||
| 	int fileD; | 	removeAllFromStoredPlaylistByPath(fd, utf8file); | ||||||
| 	char *file; |  | ||||||
| 	char *rfile; |  | ||||||
| 	char *actualFile; |  | ||||||
|  |  | ||||||
| 	if (strstr(utf8file, "/")) { |  | ||||||
| 		commandError(fd, ACK_ERROR_ARG, |  | ||||||
| 		             "cannot clear \"%s\", saving playlists to " |  | ||||||
| 		             "subdirectories is not supported", utf8file); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	file = utf8ToFsCharset(utf8file); |  | ||||||
|  |  | ||||||
| 	rfile = xmalloc(strlen(file) + strlen(".") + |  | ||||||
| 	                strlen(PLAYLIST_FILE_SUFFIX) + 1); |  | ||||||
|  |  | ||||||
| 	strcpy(rfile, file); |  | ||||||
| 	strcat(rfile, "."); |  | ||||||
| 	strcat(rfile, PLAYLIST_FILE_SUFFIX); |  | ||||||
|  |  | ||||||
| 	actualFile = rpp2app(rfile); |  | ||||||
|  |  | ||||||
| 	free(rfile); |  | ||||||
|  |  | ||||||
| 	while ((fileD = open(actualFile, O_WRONLY | O_TRUNC | O_CREAT)) == -1 |  | ||||||
| 	       && errno == EINTR); |  | ||||||
| 	if (fileD == -1) { |  | ||||||
| 		commandError(fd, ACK_ERROR_SYSTEM, "problems opening file"); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
| 	while (close(fileD) == -1 && errno == EINTR); |  | ||||||
|  |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -643,7 +595,8 @@ int addToStoredPlaylist(int fd, char *url, char *utf8file) | |||||||
| 		return -1; | 		return -1; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return addSongToStoredPlaylist(fd, song, utf8file); | 	appendSongToStoredPlaylistByPath(fd, utf8file, song); | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int addSongToPlaylist(int fd, Song * song, int printId) | int addSongToPlaylist(int fd, Song * song, int printId) | ||||||
| @@ -698,52 +651,6 @@ int addSongToPlaylist(int fd, Song * song, int printId) | |||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| int addSongToStoredPlaylist(int fd, Song *song, char *utf8file) |  | ||||||
| { |  | ||||||
| 	FILE *fileP; |  | ||||||
| 	char *file; |  | ||||||
| 	char *rfile; |  | ||||||
| 	char *actualFile; |  | ||||||
| 	char *url; |  | ||||||
|  |  | ||||||
| 	if (strstr(utf8file, "/")) { |  | ||||||
| 		commandError(fd, ACK_ERROR_ARG, |  | ||||||
| 		             "cannot add to \"%s\", saving playlists to " |  | ||||||
| 		             "subdirectories is not supported", utf8file); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	file = utf8ToFsCharset(utf8file); |  | ||||||
|  |  | ||||||
| 	rfile = xmalloc(strlen(file) + strlen(".") + |  | ||||||
| 	                strlen(PLAYLIST_FILE_SUFFIX) + 1); |  | ||||||
|  |  | ||||||
| 	strcpy(rfile, file); |  | ||||||
| 	strcat(rfile, "."); |  | ||||||
| 	strcat(rfile, PLAYLIST_FILE_SUFFIX); |  | ||||||
|  |  | ||||||
| 	actualFile = rpp2app(rfile); |  | ||||||
|  |  | ||||||
| 	free(rfile); |  | ||||||
|  |  | ||||||
| 	while (!(fileP = fopen(actualFile, "a")) && errno == EINTR); |  | ||||||
| 	if (fileP == NULL) { |  | ||||||
| 		commandError(fd, ACK_ERROR_SYSTEM, "problems opening file"); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	url = utf8ToFsCharset(getSongUrl(song)); |  | ||||||
|  |  | ||||||
| 	if (playlist_saveAbsolutePaths && song->type == SONG_TYPE_FILE) |  | ||||||
| 		fprintf(fileP, "%s\n", rmp2amp(url)); |  | ||||||
| 	else |  | ||||||
| 		fprintf(fileP, "%s\n", url); |  | ||||||
|  |  | ||||||
| 	while (fclose(fileP) && errno == EINTR); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int swapSongsInPlaylist(int fd, int song1, int song2) | int swapSongsInPlaylist(int fd, int song1, int song2) | ||||||
| { | { | ||||||
| 	int queuedSong = -1; | 	int queuedSong = -1; | ||||||
| @@ -1407,59 +1314,17 @@ int deletePlaylist(int fd, char *utf8file) | |||||||
|  |  | ||||||
| int savePlaylist(int fd, char *utf8file) | int savePlaylist(int fd, char *utf8file) | ||||||
| { | { | ||||||
| 	FILE *fileP; | 	StoredPlaylist *sp = newStoredPlaylist(utf8file, fd, 0); | ||||||
| 	int i; | 	if (!sp) | ||||||
| 	struct stat st; | 		return -1; | ||||||
| 	char *file; |  | ||||||
| 	char *rfile; |  | ||||||
| 	char *actualFile; |  | ||||||
|  |  | ||||||
| 	if (strstr(utf8file, "/")) { | 	appendPlaylistToStoredPlaylist(sp, &playlist); | ||||||
| 		commandError(fd, ACK_ERROR_ARG, | 	if (writeStoredPlaylist(sp) != 0) { | ||||||
| 			     "cannot save \"%s\", saving playlists to " | 		freeStoredPlaylist(sp); | ||||||
| 			     "subdirectories is not supported", utf8file); |  | ||||||
| 		return -1; | 		return -1; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	file = utf8ToFsCharset(utf8file); | 	freeStoredPlaylist(sp); | ||||||
|  |  | ||||||
| 	rfile = xmalloc(strlen(file) + strlen(".") + |  | ||||||
| 		       strlen(PLAYLIST_FILE_SUFFIX) + 1); |  | ||||||
|  |  | ||||||
| 	strcpy(rfile, file); |  | ||||||
| 	strcat(rfile, "."); |  | ||||||
| 	strcat(rfile, PLAYLIST_FILE_SUFFIX); |  | ||||||
|  |  | ||||||
| 	actualFile = rpp2app(rfile); |  | ||||||
|  |  | ||||||
| 	free(rfile); |  | ||||||
|  |  | ||||||
| 	if (0 == stat(actualFile, &st)) { |  | ||||||
| 		commandError(fd, ACK_ERROR_EXIST, "a file or directory already " |  | ||||||
| 			     "exists with the name \"%s\"", utf8file); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	while (!(fileP = fopen(actualFile, "w")) && errno == EINTR) ; |  | ||||||
| 	if (fileP == NULL) { |  | ||||||
| 		commandError(fd, ACK_ERROR_SYSTEM, "problems opening file"); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < playlist.length; i++) { |  | ||||||
| 		if (playlist_saveAbsolutePaths && |  | ||||||
| 		    playlist.songs[i]->type == SONG_TYPE_FILE) { |  | ||||||
| 			fprintf(fileP, "%s\n", |  | ||||||
| 				  rmp2amp(utf8ToFsCharset |  | ||||||
| 					  ((getSongUrl(playlist.songs[i]))))); |  | ||||||
| 		} else { |  | ||||||
| 			fprintf(fileP, "%s\n", |  | ||||||
| 			        utf8ToFsCharset(getSongUrl(playlist.songs[i]))); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	while (fclose(fileP) && errno == EINTR) ; |  | ||||||
|  |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1528,125 +1393,47 @@ int getPlaylistSongId(int song) | |||||||
| 	return playlist.positionToId[song]; | 	return playlist.positionToId[song]; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int PlaylistIterFunc(int fd, char *utf8file, | int PlaylistInfo(int fd, char *utf8file, int detail) | ||||||
| 			    void (*IterFunc) (int fd, char *utf8_file, |  | ||||||
| 					      char **errored_File)) |  | ||||||
| { | { | ||||||
| 	FILE *fileP; | 	ListNode *node; | ||||||
| 	char s[MAXPATHLEN + 1]; | 	StoredPlaylist *sp = loadStoredPlaylist(utf8file, fd); | ||||||
| 	int slength = 0; | 	if (sp == NULL) | ||||||
| 	char *temp = utf8ToFsCharset(utf8file); |  | ||||||
| 	char *rfile = xmalloc(strlen(temp) + strlen(".") + |  | ||||||
| 			     strlen(PLAYLIST_FILE_SUFFIX) + 1); |  | ||||||
| 	char *actualFile; |  | ||||||
| 	char *parent = parentPath(temp); |  | ||||||
| 	int parentlen = strlen(parent); |  | ||||||
| 	char *erroredFile = NULL; |  | ||||||
| 	int tempInt; |  | ||||||
| 	int commentCharFound = 0; |  | ||||||
|  |  | ||||||
| 	strcpy(rfile, temp); |  | ||||||
| 	strcat(rfile, "."); |  | ||||||
| 	strcat(rfile, PLAYLIST_FILE_SUFFIX); |  | ||||||
|  |  | ||||||
| 	if ((actualFile = rpp2app(rfile)) && isPlaylist(actualFile)) |  | ||||||
| 		free(rfile); |  | ||||||
| 	else { |  | ||||||
| 		free(rfile); |  | ||||||
| 		commandError(fd, ACK_ERROR_NO_EXIST, |  | ||||||
| 			     "playlist \"%s\" not found", utf8file); |  | ||||||
| 		return -1; | 		return -1; | ||||||
|  |  | ||||||
|  | 	node = sp->list->firstNode; | ||||||
|  | 	while (node != NULL) { | ||||||
|  | 		char *temp = node->data; | ||||||
|  | 		int wrote = 0; | ||||||
|  |  | ||||||
|  | 		if (detail) { | ||||||
|  | 			Song *song = getSongFromDB(temp); | ||||||
|  | 			if (song) { | ||||||
|  | 				printSongInfo(fd, song); | ||||||
|  | 				wrote = 1; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	while (!(fileP = fopen(actualFile, "r")) && errno == EINTR) ; | 		if (!wrote) { | ||||||
| 	if (fileP == NULL) { | 			fdprintf(fd, SONG_FILE "%s\n", temp); | ||||||
| 		commandError(fd, ACK_ERROR_SYSTEM, |  | ||||||
| 			     "problems opening file \"%s\"", utf8file); |  | ||||||
| 		return -1; |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	while ((tempInt = fgetc(fileP)) != EOF) { | 		node = node->nextNode; | ||||||
| 		s[slength] = tempInt; |  | ||||||
| 		if (s[slength] == '\n' || s[slength] == '\0') { |  | ||||||
| 			commentCharFound = 0; |  | ||||||
| 			s[slength] = '\0'; |  | ||||||
| 			if (s[0] == PLAYLIST_COMMENT) { |  | ||||||
| 				commentCharFound = 1; |  | ||||||
| 			} |  | ||||||
| 			if (strncmp(s, musicDir, strlen(musicDir)) == 0) { |  | ||||||
| 				strcpy(s, &(s[strlen(musicDir)])); |  | ||||||
| 			} else if (parentlen) { |  | ||||||
| 				temp = xstrdup(s); |  | ||||||
| 				memset(s, 0, MAXPATHLEN + 1); |  | ||||||
| 				strcpy(s, parent); |  | ||||||
| 				strncat(s, "/", MAXPATHLEN - parentlen); |  | ||||||
| 				strncat(s, temp, MAXPATHLEN - parentlen - 1); |  | ||||||
| 				if (strlen(s) >= MAXPATHLEN) { |  | ||||||
| 					commandError(fd, |  | ||||||
| 						     ACK_ERROR_PLAYLIST_LOAD, |  | ||||||
| 						     "\"%s\" too long", temp); |  | ||||||
| 					free(temp); |  | ||||||
| 					while (fclose(fileP) |  | ||||||
| 					       && errno == EINTR) ; |  | ||||||
| 					if (erroredFile) |  | ||||||
| 						free(erroredFile); |  | ||||||
| 					return -1; |  | ||||||
| 				} |  | ||||||
| 				free(temp); |  | ||||||
| 			} |  | ||||||
| 			slength = 0; |  | ||||||
| 			temp = fsCharsetToUtf8(s); |  | ||||||
| 			if (!temp) |  | ||||||
| 				continue; |  | ||||||
| 			if (!commentCharFound) { |  | ||||||
| 				/* using temp directly should be safe, |  | ||||||
| 				 * for our current IterFunction set |  | ||||||
| 				 * but just in case, we copy to s */ |  | ||||||
| 				strcpy(s, temp); |  | ||||||
| 				IterFunc(fd, s, &erroredFile); |  | ||||||
| 			} |  | ||||||
| 		} else if (slength == MAXPATHLEN) { |  | ||||||
| 			s[slength] = '\0'; |  | ||||||
| 			commandError(fd, ACK_ERROR_PLAYLIST_LOAD, |  | ||||||
| 				     "line in \"%s\" is too long", utf8file); |  | ||||||
| 			ERROR("line \"%s\" in playlist \"%s\" is too long\n", |  | ||||||
| 			      s, utf8file); |  | ||||||
| 			while (fclose(fileP) && errno == EINTR) ; |  | ||||||
| 			if (erroredFile) |  | ||||||
| 				free(erroredFile); |  | ||||||
| 			return -1; |  | ||||||
| 		} else if (s[slength] != '\r') |  | ||||||
| 			slength++; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	while (fclose(fileP) && errno == EINTR) ; |  | ||||||
|  |  | ||||||
| 	if (erroredFile) { |  | ||||||
| 		commandError(fd, ACK_ERROR_PLAYLIST_LOAD, |  | ||||||
| 			     "can't add file \"%s\"", erroredFile); |  | ||||||
| 		free(erroredFile); |  | ||||||
| 		return -1; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	freeStoredPlaylist(sp); | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void PlaylistInfoPrintInfo(int fd, char *utf8file, char **erroredfile) | int loadPlaylist(int fd, char *utf8file) | ||||||
| { | { | ||||||
| 	Song *song = getSongFromDB(utf8file); | 	ListNode *node; | ||||||
| 	if (song) { | 	StoredPlaylist *sp = loadStoredPlaylist(utf8file, fd); | ||||||
| 		printSongInfo(fd, song); | 	if (sp == NULL) | ||||||
| 	} else { | 		return -1; | ||||||
| 		fdprintf(fd, SONG_FILE "%s\n", utf8file); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| static void PlaylistInfoPrint(int fd, char *utf8file, char **erroredfile) |  | ||||||
| { |  | ||||||
| 	fdprintf(fd, SONG_FILE "%s\n", utf8file); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void PlaylistLoadIterFunc(int fd, char *temp, char **erroredFile) | 	node = sp->list->firstNode; | ||||||
| { | 	while (node != NULL) { | ||||||
|  | 		char *temp = node->data; | ||||||
| 		if (!getSongFromDB(temp) && !isRemoteUrl(temp)) { | 		if (!getSongFromDB(temp) && !isRemoteUrl(temp)) { | ||||||
|  |  | ||||||
| 		} else if ((addToPlaylist(STDERR_FILENO, temp, 0)) < 0) { | 		} else if ((addToPlaylist(STDERR_FILENO, temp, 0)) < 0) { | ||||||
| @@ -1659,25 +1446,17 @@ static void PlaylistLoadIterFunc(int fd, char *temp, char **erroredFile) | |||||||
| 				p++; | 				p++; | ||||||
| 			} | 			} | ||||||
| 			if ((addToPlaylist(STDERR_FILENO, temp2, 0)) < 0) { | 			if ((addToPlaylist(STDERR_FILENO, temp2, 0)) < 0) { | ||||||
| 			if (!*erroredFile) { | 				commandError(fd, ACK_ERROR_PLAYLIST_LOAD, | ||||||
| 				*erroredFile = xstrdup(temp); | 							"can't add file \"%s\"", temp2); | ||||||
| 			} |  | ||||||
| 			} | 			} | ||||||
| 			free(temp2); | 			free(temp2); | ||||||
| 		} | 		} | ||||||
| } |  | ||||||
|  |  | ||||||
| int PlaylistInfo(int fd, char *utf8file, int detail) | 		node = node->nextNode; | ||||||
| { |  | ||||||
| 	if (detail) { |  | ||||||
| 		return PlaylistIterFunc(fd, utf8file, PlaylistInfoPrintInfo); |  | ||||||
| 	} | 	} | ||||||
| 	return PlaylistIterFunc(fd, utf8file, PlaylistInfoPrint); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int loadPlaylist(int fd, char *utf8file) | 	freeStoredPlaylist(sp); | ||||||
| { | 	return 0; | ||||||
| 	return PlaylistIterFunc(fd, utf8file, PlaylistLoadIterFunc); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void searchForSongsInPlaylist(int fd, int numItems, LocateTagItem * items) | void searchForSongsInPlaylist(int fd, int numItems, LocateTagItem * items) | ||||||
|   | |||||||
| @@ -28,6 +28,24 @@ | |||||||
| #include <time.h> | #include <time.h> | ||||||
|  |  | ||||||
| #define PLAYLIST_FILE_SUFFIX 	"m3u" | #define PLAYLIST_FILE_SUFFIX 	"m3u" | ||||||
|  | #define PLAYLIST_COMMENT	'#' | ||||||
|  |  | ||||||
|  | typedef struct _Playlist { | ||||||
|  | 	Song **songs; | ||||||
|  | 	/* holds version a song was modified on */ | ||||||
|  | 	mpd_uint32 *songMod; | ||||||
|  | 	int *order; | ||||||
|  | 	int *positionToId; | ||||||
|  | 	int *idToPosition; | ||||||
|  | 	int length; | ||||||
|  | 	int current; | ||||||
|  | 	int queued; | ||||||
|  | 	int repeat; | ||||||
|  | 	int random; | ||||||
|  | 	mpd_uint32 version; | ||||||
|  | } Playlist; | ||||||
|  |  | ||||||
|  | extern int playlist_saveAbsolutePaths; | ||||||
|  |  | ||||||
| void initPlaylist(void); | void initPlaylist(void); | ||||||
|  |  | ||||||
| @@ -47,8 +65,6 @@ int addToStoredPlaylist(int fd, char *file, char *utf8file); | |||||||
|  |  | ||||||
| int addSongToPlaylist(int fd, Song * song, int printId); | int addSongToPlaylist(int fd, Song * song, int printId); | ||||||
|  |  | ||||||
| int addSongToStoredPlaylist(int fd, Song *song, char *utf8file); |  | ||||||
|  |  | ||||||
| int showPlaylist(int fd); | int showPlaylist(int fd); | ||||||
|  |  | ||||||
| int deleteFromPlaylist(int fd, int song); | int deleteFromPlaylist(int fd, int song); | ||||||
|   | |||||||
							
								
								
									
										473
									
								
								src/storedPlaylist.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										473
									
								
								src/storedPlaylist.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,473 @@ | |||||||
|  | /* the Music Player Daemon (MPD) | ||||||
|  |  * Copyright (C) 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 "storedPlaylist.h" | ||||||
|  | #include "log.h" | ||||||
|  | #include "path.h" | ||||||
|  | #include "utils.h" | ||||||
|  | #include "playlist.h" | ||||||
|  | #include "ack.h" | ||||||
|  | #include "command.h" | ||||||
|  |  | ||||||
|  | #include <string.h> | ||||||
|  | #include <errno.h> | ||||||
|  |  | ||||||
|  | static char *utf8pathToFsPathInStoredPlaylist(const char *utf8path, int fd) | ||||||
|  | { | ||||||
|  | 	char *file; | ||||||
|  | 	char *rfile; | ||||||
|  | 	char *actualFile; | ||||||
|  |  | ||||||
|  | 	if (strstr(utf8path, "/")) { | ||||||
|  | 		if (fd != -1) { | ||||||
|  | 			commandError(fd, ACK_ERROR_ARG, | ||||||
|  | 					"Cannot clear \"%s\", saving playlists to " | ||||||
|  | 					"subdirectories is not supported", utf8path); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		ERROR("Cannot clear \"%s\", saving playlists to " | ||||||
|  | 			  "subdirectories is not supported", utf8path); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	file = utf8ToFsCharset((char *)utf8path); | ||||||
|  |  | ||||||
|  | 	rfile = xmalloc(strlen(file) + strlen(".") + | ||||||
|  | 	                strlen(PLAYLIST_FILE_SUFFIX) + 1); | ||||||
|  |  | ||||||
|  | 	strcpy(rfile, file); | ||||||
|  | 	strcat(rfile, "."); | ||||||
|  | 	strcat(rfile, PLAYLIST_FILE_SUFFIX); | ||||||
|  |  | ||||||
|  | 	actualFile = rpp2app(rfile); | ||||||
|  |  | ||||||
|  | 	free(rfile); | ||||||
|  |  | ||||||
|  | 	return actualFile; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static unsigned int lengthOfStoredPlaylist(StoredPlaylist *sp) | ||||||
|  | { | ||||||
|  | 	return sp->list->numberOfNodes; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static ListNode *nodeOfStoredPlaylist(StoredPlaylist *sp, int index) | ||||||
|  | { | ||||||
|  | 	int forward; | ||||||
|  | 	ListNode *node; | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	if (index >= lengthOfStoredPlaylist(sp) || index < 0) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	if (index > lengthOfStoredPlaylist(sp)/2) { | ||||||
|  | 		forward = 0; | ||||||
|  | 		node = sp->list->lastNode; | ||||||
|  | 		i = lengthOfStoredPlaylist(sp) - 1; | ||||||
|  | 	} else { | ||||||
|  | 		forward = 1; | ||||||
|  | 		node = sp->list->firstNode; | ||||||
|  | 		i = 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	while (node != NULL) { | ||||||
|  | 		if (i == index) | ||||||
|  | 			return node; | ||||||
|  |  | ||||||
|  | 		if (forward) { | ||||||
|  | 			i++; | ||||||
|  | 			node = node->nextNode; | ||||||
|  | 		} else { | ||||||
|  | 			i--; | ||||||
|  | 			node = node->prevNode; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | StoredPlaylist *newStoredPlaylist(const char *utf8name, int fd, int ignoreExisting) | ||||||
|  | { | ||||||
|  | 	struct stat buf; | ||||||
|  | 	char *filename = NULL; | ||||||
|  | 	StoredPlaylist *sp = calloc(1, sizeof(*sp)); | ||||||
|  | 	if (!sp) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	if (utf8name) { | ||||||
|  | 		filename = utf8pathToFsPathInStoredPlaylist(utf8name, fd); | ||||||
|  |  | ||||||
|  | 		if (filename && stat(filename, &buf) == 0 && ignoreExisting == 0) { | ||||||
|  | 			if (fd != -1) | ||||||
|  | 				commandError(fd, ACK_ERROR_EXIST, | ||||||
|  | 						"a file or directory already " | ||||||
|  | 						"exists with the name \"%s\"", utf8name); | ||||||
|  |  | ||||||
|  | 			ERROR("a file or directory already " | ||||||
|  | 					"exists with the name \"%s\"", utf8name); | ||||||
|  |  | ||||||
|  | 			free(sp); | ||||||
|  | 			return NULL; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	sp->list = makeList(DEFAULT_FREE_DATA_FUNC, 0); | ||||||
|  | 	sp->fd = fd; | ||||||
|  |  | ||||||
|  | 	if (filename) | ||||||
|  | 		sp->fspath = strdup(filename); | ||||||
|  |  | ||||||
|  | 	return sp; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | StoredPlaylist *loadStoredPlaylist(const char *utf8path, int fd) | ||||||
|  | { | ||||||
|  | 	char *filename; | ||||||
|  | 	StoredPlaylist *sp; | ||||||
|  | 	FILE *file; | ||||||
|  | 	char s[MAXPATHLEN + 1]; | ||||||
|  | 	int slength = 0; | ||||||
|  | 	char *temp = utf8ToFsCharset((char *)utf8path); | ||||||
|  | 	char *parent = parentPath(temp); | ||||||
|  | 	int parentlen = strlen(parent); | ||||||
|  | 	int tempInt; | ||||||
|  | 	int commentCharFound = 0; | ||||||
|  |  | ||||||
|  | 	filename = utf8pathToFsPathInStoredPlaylist(utf8path, fd); | ||||||
|  | 	if (!filename) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	while (!(file = fopen(filename, "r")) && errno == EINTR); | ||||||
|  | 	if (file == NULL) { | ||||||
|  | 		if (fd != -1) { | ||||||
|  | 			commandError(fd, ACK_ERROR_NO_EXIST, | ||||||
|  | 					"Could not open file \"%s\": %s", filename, strerror(errno)); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		ERROR("Could not open file \"%s\": %s", filename, strerror(errno)); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	sp = newStoredPlaylist(utf8path, fd, 1); | ||||||
|  | 	if (!sp) { | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	while ((tempInt = fgetc(file)) != EOF) { | ||||||
|  | 		s[slength] = tempInt; | ||||||
|  | 		if (s[slength] == '\n' || s[slength] == '\0') { | ||||||
|  | 			commentCharFound = 0; | ||||||
|  | 			s[slength] = '\0'; | ||||||
|  | 			if (s[0] == PLAYLIST_COMMENT) { | ||||||
|  | 				commentCharFound = 1; | ||||||
|  | 			} | ||||||
|  | 			if (strncmp(s, musicDir, strlen(musicDir)) == 0) { | ||||||
|  | 				strcpy(s, &(s[strlen(musicDir)])); | ||||||
|  | 			} else if (parentlen) { | ||||||
|  | 				temp = xstrdup(s); | ||||||
|  | 				memset(s, 0, MAXPATHLEN + 1); | ||||||
|  | 				strcpy(s, parent); | ||||||
|  | 				strncat(s, "/", MAXPATHLEN - parentlen); | ||||||
|  | 				strncat(s, temp, MAXPATHLEN - parentlen - 1); | ||||||
|  | 				if (strlen(s) >= MAXPATHLEN) { | ||||||
|  | 					if (sp->fd != -1) { | ||||||
|  | 						commandError(sp->fd, ACK_ERROR_PLAYLIST_LOAD, | ||||||
|  | 								"\"%s\" too long", temp); | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					ERROR("\"%s\" too long", temp); | ||||||
|  | 					free(temp); | ||||||
|  | 					goto out; | ||||||
|  | 				} | ||||||
|  | 				free(temp); | ||||||
|  | 			} | ||||||
|  | 			slength = 0; | ||||||
|  | 			temp = fsCharsetToUtf8(s); | ||||||
|  | 			if (!temp) | ||||||
|  | 				continue; | ||||||
|  | 			if (!commentCharFound) { | ||||||
|  | 				insertInListWithoutKey(sp->list, strdup(s)); | ||||||
|  | 			} | ||||||
|  | 		} else if (slength == MAXPATHLEN) { | ||||||
|  | 			s[slength] = '\0'; | ||||||
|  | 			if (sp->fd != -1) { | ||||||
|  | 				commandError(sp->fd, ACK_ERROR_PLAYLIST_LOAD, | ||||||
|  | 						"line \"%s\" in playlist \"%s\" is too long\n", | ||||||
|  | 						s, utf8path); | ||||||
|  | 			} | ||||||
|  | 			ERROR("line \"%s\" in playlist \"%s\" is too long\n", s, utf8path); | ||||||
|  | 			goto out; | ||||||
|  | 		} else if (s[slength] != '\r') | ||||||
|  | 			slength++; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | out: | ||||||
|  | 	while (fclose(file) && errno == EINTR); | ||||||
|  | 	return sp; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void freeStoredPlaylist(StoredPlaylist *sp) | ||||||
|  | { | ||||||
|  | 	if (sp->list) | ||||||
|  | 		freeList(sp->list); | ||||||
|  | 	if (sp->fspath) | ||||||
|  | 		free(sp->fspath); | ||||||
|  |  | ||||||
|  | 	free(sp); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int moveSongInStoredPlaylist(int fd, StoredPlaylist *sp, int src, int dest) | ||||||
|  | { | ||||||
|  | 	ListNode *srcNode, *destNode; | ||||||
|  |  | ||||||
|  | 	if (src >= lengthOfStoredPlaylist(sp) || dest >= lengthOfStoredPlaylist(sp) || src < 0 || dest < 0 || src == dest) { | ||||||
|  | 		commandError(fd, ACK_ERROR_ARG, "argument out of range."); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	srcNode = nodeOfStoredPlaylist(sp, src); | ||||||
|  | 	if (!srcNode) | ||||||
|  | 		return -1; | ||||||
|  |  | ||||||
|  | 	destNode = nodeOfStoredPlaylist(sp, dest); | ||||||
|  |  | ||||||
|  | 	/* remove src */ | ||||||
|  | 	if (srcNode->prevNode) | ||||||
|  | 		srcNode->prevNode->nextNode = srcNode->nextNode; | ||||||
|  | 	else | ||||||
|  | 		sp->list->firstNode = srcNode->nextNode; | ||||||
|  |  | ||||||
|  | 	if (srcNode->nextNode) | ||||||
|  | 		srcNode->nextNode->prevNode = srcNode->prevNode; | ||||||
|  | 	else | ||||||
|  | 		sp->list->lastNode = srcNode->prevNode; | ||||||
|  |  | ||||||
|  | 	/* this is all a bit complicated - but I tried to | ||||||
|  | 	 * maintain the same order stuff is moved as in the | ||||||
|  | 	 * real playlist */ | ||||||
|  | 	if (dest == 0) { | ||||||
|  | 		sp->list->firstNode->prevNode = srcNode; | ||||||
|  | 		srcNode->nextNode = sp->list->firstNode; | ||||||
|  | 		srcNode->prevNode = NULL; | ||||||
|  | 		sp->list->firstNode = srcNode; | ||||||
|  | 	} else if ((dest + 1) == lengthOfStoredPlaylist(sp)) { | ||||||
|  | 		sp->list->lastNode->nextNode = srcNode; | ||||||
|  | 		srcNode->nextNode = NULL; | ||||||
|  | 		srcNode->prevNode = sp->list->lastNode; | ||||||
|  | 		sp->list->lastNode = srcNode; | ||||||
|  | 	} else { | ||||||
|  | 		if (destNode == NULL) { | ||||||
|  | 			/* this shouldn't be happening. */ | ||||||
|  | 			return -1; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (src > dest) { | ||||||
|  | 			destNode->prevNode->nextNode = srcNode; | ||||||
|  | 			srcNode->prevNode = destNode->prevNode; | ||||||
|  | 			srcNode->nextNode = destNode; | ||||||
|  | 			destNode->prevNode = srcNode; | ||||||
|  | 		} else { | ||||||
|  | 			destNode->nextNode->prevNode = srcNode; | ||||||
|  | 			srcNode->prevNode = destNode; | ||||||
|  | 			srcNode->nextNode = destNode->nextNode; | ||||||
|  | 			destNode->nextNode = srcNode; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int moveSongInStoredPlaylistByPath(int fd, const char *utf8path, int src, int dest) | ||||||
|  | { | ||||||
|  | 	StoredPlaylist *sp = loadStoredPlaylist(utf8path, fd); | ||||||
|  | 	if (!sp) { | ||||||
|  | 		commandError(fd, ACK_ERROR_UNKNOWN, "could not open playlist."); | ||||||
|  | 		freeStoredPlaylist(sp); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (moveSongInStoredPlaylist(fd, sp, src, dest) != 0) { | ||||||
|  | 		freeStoredPlaylist(sp); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (writeStoredPlaylist(sp) != 0) { | ||||||
|  | 		commandError(fd, ACK_ERROR_UNKNOWN, "failed to save playlist."); | ||||||
|  | 		freeStoredPlaylist(sp); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	freeStoredPlaylist(sp); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Not used currently | ||||||
|  | static void removeAllFromStoredPlaylist(StoredPlaylist *sp) | ||||||
|  | { | ||||||
|  | 	freeList(sp->list); | ||||||
|  | 	sp->list = makeList(DEFAULT_FREE_DATA_FUNC, 0); | ||||||
|  | } | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | int removeAllFromStoredPlaylistByPath(int fd, const char *utf8path) | ||||||
|  | { | ||||||
|  | 	char *filename; | ||||||
|  | 	FILE *file; | ||||||
|  |  | ||||||
|  | 	filename = utf8pathToFsPathInStoredPlaylist(utf8path, fd); | ||||||
|  | 	if (!filename) { | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	while (!(file = fopen(filename, "w")) && errno == EINTR); | ||||||
|  | 	if (file == NULL) { | ||||||
|  | 		if (fd != -1) { | ||||||
|  | 			commandError(fd, ACK_ERROR_NO_EXIST, | ||||||
|  | 					"Could not open file \"%s\": %s", filename, strerror(errno)); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		ERROR("Could not open file \"%s\": %s", filename, strerror(errno)); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	while (fclose(file) != 0 && errno == EINTR); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int removeOneSongFromStoredPlaylist(int fd, StoredPlaylist *sp, int pos) | ||||||
|  | { | ||||||
|  | 	ListNode *node = nodeOfStoredPlaylist(sp, pos); | ||||||
|  | 	if (!node) { | ||||||
|  | 		commandError(fd, ACK_ERROR_ARG, "could not find song at position."); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	deleteNodeFromList(sp->list, node); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int removeOneSongFromStoredPlaylistByPath(int fd, const char *utf8path, int pos) | ||||||
|  | { | ||||||
|  | 	StoredPlaylist *sp = loadStoredPlaylist(utf8path, fd); | ||||||
|  | 	if (!sp) { | ||||||
|  | 		commandError(fd, ACK_ERROR_UNKNOWN, "could not open playlist."); | ||||||
|  | 		freeStoredPlaylist(sp); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (removeOneSongFromStoredPlaylist(fd, sp, pos) != 0) { | ||||||
|  | 		freeStoredPlaylist(sp); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (writeStoredPlaylist(sp) != 0) { | ||||||
|  | 		commandError(fd, ACK_ERROR_UNKNOWN, "failed to save playlist."); | ||||||
|  | 		freeStoredPlaylist(sp); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	freeStoredPlaylist(sp); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int writeStoredPlaylistToPath(StoredPlaylist *sp, const char *fspath) | ||||||
|  | { | ||||||
|  | 	FILE *file; | ||||||
|  | 	ListNode *node; | ||||||
|  |  | ||||||
|  | 	if (fspath == NULL) | ||||||
|  | 		return -1; | ||||||
|  |  | ||||||
|  | 	while (!(file = fopen(fspath, "w")) && errno == EINTR); | ||||||
|  | 	if (file == NULL) { | ||||||
|  | 		if (sp->fd != -1) { | ||||||
|  | 			commandError(sp->fd, ACK_ERROR_NO_EXIST, | ||||||
|  | 					"Could not open file \"%s\": %s", fspath, strerror(errno)); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		ERROR("Couldn't open file \"%s\": %s", fspath, strerror(errno)); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	node = sp->list->firstNode; | ||||||
|  | 	while (node != NULL) { | ||||||
|  | 		fprintf(file, "%s\n", (char *)node->data); | ||||||
|  | 		node = node->nextNode; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	while (fclose(file) != 0 && errno == EINTR); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int writeStoredPlaylist(StoredPlaylist *sp) | ||||||
|  | { | ||||||
|  | 	return writeStoredPlaylistToPath(sp, sp->fspath); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void appendSongToStoredPlaylist(StoredPlaylist *sp, Song *song) | ||||||
|  | { | ||||||
|  | 	char *s; | ||||||
|  |  | ||||||
|  | 	if (playlist_saveAbsolutePaths && song->type == SONG_TYPE_FILE) { | ||||||
|  | 		s = rmp2amp(utf8ToFsCharset(getSongUrl(song))); | ||||||
|  | 	} else { | ||||||
|  | 		s = utf8ToFsCharset(getSongUrl(song)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	insertInListWithoutKey(sp->list, strdup(s)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int appendSongToStoredPlaylistByPath(int fd, const char *utf8path, Song *song) | ||||||
|  | { | ||||||
|  | 	char *filename; | ||||||
|  | 	FILE *file; | ||||||
|  |  | ||||||
|  | 	filename = utf8pathToFsPathInStoredPlaylist(utf8path, fd); | ||||||
|  | 	if (!filename) { | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	while (!(file = fopen(filename, "a")) && errno == EINTR); | ||||||
|  | 	if (file == NULL) { | ||||||
|  | 		if (fd != -1) { | ||||||
|  | 			commandError(fd, ACK_ERROR_NO_EXIST, | ||||||
|  | 					"Could not open file \"%s\": %s", filename, strerror(errno)); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		ERROR("Could not open file \"%s\": %s", filename, strerror(errno)); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (playlist_saveAbsolutePaths && song->type == SONG_TYPE_FILE) { | ||||||
|  | 		fprintf(file, "%s\n", rmp2amp(utf8ToFsCharset(getSongUrl(song)))); | ||||||
|  | 	} else { | ||||||
|  | 		fprintf(file, "%s\n", utf8ToFsCharset(getSongUrl(song))); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	while (fclose(file) != 0 && errno == EINTR); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void appendPlaylistToStoredPlaylist(StoredPlaylist *sp, Playlist *playlist) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	for (i = 0; i < playlist->length; i++) { | ||||||
|  | 		appendSongToStoredPlaylist(sp, playlist->songs[i]); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								src/storedPlaylist.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/storedPlaylist.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | |||||||
|  | /* the Music Player Daemon (MPD) | ||||||
|  |  * Copyright (C) 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 STORED_PLAYLIST_H | ||||||
|  | #define STORED_PLAYLIST_H | ||||||
|  |  | ||||||
|  | #include "song.h" | ||||||
|  | #include "list.h" | ||||||
|  | #include "playlist.h" | ||||||
|  |  | ||||||
|  | typedef struct _storedPlaylist { | ||||||
|  | 	List *list; | ||||||
|  | 	unsigned int length; | ||||||
|  | 	char *fspath; | ||||||
|  | 	int fd; | ||||||
|  | } StoredPlaylist; | ||||||
|  |  | ||||||
|  | StoredPlaylist *newStoredPlaylist(const char *filename, int fd, int ignoreExisting); | ||||||
|  | StoredPlaylist *loadStoredPlaylist(const char *utf8path, int fd); | ||||||
|  | void freeStoredPlaylist(StoredPlaylist *sp); | ||||||
|  |  | ||||||
|  | int moveSongInStoredPlaylistByPath(int fd, const char *utf8path, int src, int dest); | ||||||
|  | int removeAllFromStoredPlaylistByPath(int fd, const char *utf8path); | ||||||
|  | int removeOneSongFromStoredPlaylistByPath(int fd, const char *utf8path, int pos); | ||||||
|  |  | ||||||
|  | int writeStoredPlaylist(StoredPlaylist *sp); | ||||||
|  |  | ||||||
|  | int appendSongToStoredPlaylistByPath(int fd, const char *utf8path, Song *song); | ||||||
|  | void appendPlaylistToStoredPlaylist(StoredPlaylist *sp, Playlist *playlist); | ||||||
|  |  | ||||||
|  | #endif | ||||||
		Reference in New Issue
	
	Block a user
	 J. Alexander Treuman
					J. Alexander Treuman