From 6cfe421cd64278f85310a258ab42c372c8a847b3 Mon Sep 17 00:00:00 2001 From: "J. Alexander Treuman" Date: Wed, 16 May 2007 12:02:10 +0000 Subject: [PATCH] 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 --- ChangeLog | 2 + TODO | 8 +- src/Makefile.am | 6 +- src/command.c | 42 ++++ src/dbUtils.c | 5 +- src/playlist.c | 359 +++++++------------------------- src/playlist.h | 20 +- src/storedPlaylist.c | 473 +++++++++++++++++++++++++++++++++++++++++++ src/storedPlaylist.h | 46 +++++ 9 files changed, 660 insertions(+), 301 deletions(-) create mode 100644 src/storedPlaylist.c create mode 100644 src/storedPlaylist.h diff --git a/ChangeLog b/ChangeLog index 1d71bb317..6bfc82c0e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -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 db_update in stats would be 0 after initial database creation * 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 ver 0.12.2 (2007/3/20) diff --git a/TODO b/TODO index 378a948b1..6c0454159 100644 --- a/TODO +++ b/TODO @@ -40,12 +40,8 @@ *) abstract out state code from playlist.c *) put MPD Version in statefile -*) rewrite saved playlist code - *) abstract out saved playlists from playlist.c - *) new commands - *) replace /* replace current playlist - with saved playlist and - keep playing */ +*) add playlistreplace command (replace current playlist with saved playlist + and keep playing) *) add command for inserting songs in a specific position diff --git a/src/Makefile.am b/src/Makefile.am index ab2f2d80c..6cf4b033e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -74,7 +74,8 @@ mpd_headers = \ utils.h \ volume.h \ zeroconf.h \ - locate.h + locate.h \ + storedPlaylist.h mpd_SOURCES = \ @@ -125,7 +126,8 @@ mpd_SOURCES = \ volume.c \ utf8.c \ zeroconf.c \ - locate.c + locate.c \ + storedPlaylist.c mpd_CFLAGS = $(MPD_CFLAGS) diff --git a/src/command.c b/src/command.c index 519f6ff19..107df3275 100644 --- a/src/command.c +++ b/src/command.c @@ -30,6 +30,7 @@ #include "log.h" #include "tag.h" #include "utils.h" +#include "storedPlaylist.h" #include #include @@ -95,6 +96,8 @@ #define COMMAND_PLAYLISTADD "playlistadd" #define COMMAND_PLAYLISTFIND "playlistfind" #define COMMAND_PLAYLISTSEARCH "playlistsearch" +#define COMMAND_PLAYLISTMOVE "playlistmove" +#define COMMAND_PLAYLISTDELETE "playlistdelete" #define COMMAND_TAGTYPES "tagtypes" #define COMMAND_COUNT "count" @@ -578,6 +581,43 @@ static int handlePlaylistSearch(int fd, int *permission, int argc, char *argv[]) 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, int *permission, int argc, @@ -1072,6 +1112,8 @@ void initCommands(void) addCommand(COMMAND_PLAYLISTADD, PERMISSION_CONTROL, 2, 2, handlePlaylistAdd, NULL); addCommand(COMMAND_PLAYLISTFIND, PERMISSION_READ, 2, -1, handlePlaylistFind, 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_COUNT, PERMISSION_READ, 2, -1, handleCount, NULL); diff --git a/src/dbUtils.c b/src/dbUtils.c index 654867601..f83ae4c21 100644 --- a/src/dbUtils.c +++ b/src/dbUtils.c @@ -26,6 +26,7 @@ #include "tag.h" #include "tagTracker.h" #include "log.h" +#include "storedPlaylist.h" typedef struct _ListCommandItem { 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) { - return addSongToStoredPlaylist(fd, song, (char *)data); + if (appendSongToStoredPlaylistByPath(fd, (char *)data, song) != 0) + return -1; + return 0; } int addAllIn(int fd, char *name) diff --git a/src/playlist.c b/src/playlist.c index cb2be91d7..d389b1db8 100644 --- a/src/playlist.c +++ b/src/playlist.c @@ -28,6 +28,7 @@ #include "utils.h" #include "sig_handlers.h" #include "state_file.h" +#include "storedPlaylist.h" #include #include @@ -37,8 +38,6 @@ #include #include -#define PLAYLIST_COMMENT '#' - #define PLAYLIST_STATE_STOP 0 #define PLAYLIST_STATE_PLAY 1 @@ -64,21 +63,6 @@ #define DEFAULT_PLAYLIST_MAX_LENGTH (1024*16) #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 int playlist_state = PLAYLIST_STATE_STOP; static int playlist_max_length = DEFAULT_PLAYLIST_MAX_LENGTH; @@ -87,7 +71,7 @@ static int playlist_errorCount; static int playlist_queueError; 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 int playPlaylistOrderNumber(int fd, int orderNum); @@ -238,39 +222,7 @@ int clearPlaylist(int fd) int clearStoredPlaylist(int fd, char *utf8file) { - int fileD; - 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); - + removeAllFromStoredPlaylistByPath(fd, utf8file); return 0; } @@ -643,7 +595,8 @@ int addToStoredPlaylist(int fd, char *url, char *utf8file) return -1; } - return addSongToStoredPlaylist(fd, song, utf8file); + appendSongToStoredPlaylistByPath(fd, utf8file, song); + return 0; } int addSongToPlaylist(int fd, Song * song, int printId) @@ -698,52 +651,6 @@ int addSongToPlaylist(int fd, Song * song, int printId) 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 queuedSong = -1; @@ -1407,59 +1314,17 @@ int deletePlaylist(int fd, char *utf8file) int savePlaylist(int fd, char *utf8file) { - FILE *fileP; - int i; - struct stat st; - char *file; - char *rfile; - char *actualFile; + StoredPlaylist *sp = newStoredPlaylist(utf8file, fd, 0); + if (!sp) + return -1; - if (strstr(utf8file, "/")) { - commandError(fd, ACK_ERROR_ARG, - "cannot save \"%s\", saving playlists to " - "subdirectories is not supported", utf8file); + appendPlaylistToStoredPlaylist(sp, &playlist); + if (writeStoredPlaylist(sp) != 0) { + freeStoredPlaylist(sp); 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); - - 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) ; - + freeStoredPlaylist(sp); return 0; } @@ -1528,156 +1393,70 @@ int getPlaylistSongId(int song) return playlist.positionToId[song]; } -static int PlaylistIterFunc(int fd, char *utf8file, - void (*IterFunc) (int fd, char *utf8_file, - char **errored_File)) -{ - FILE *fileP; - char s[MAXPATHLEN + 1]; - int slength = 0; - 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; - } - - while (!(fileP = fopen(actualFile, "r")) && errno == EINTR) ; - if (fileP == NULL) { - commandError(fd, ACK_ERROR_SYSTEM, - "problems opening file \"%s\"", utf8file); - return -1; - } - - while ((tempInt = fgetc(fileP)) != 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) { - 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; - } - - return 0; -} - -static void PlaylistInfoPrintInfo(int fd, char *utf8file, char **erroredfile) -{ - Song *song = getSongFromDB(utf8file); - if (song) { - printSongInfo(fd, song); - } else { - 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) -{ - if (!getSongFromDB(temp) && !isRemoteUrl(temp)) { - - } else if ((addToPlaylist(STDERR_FILENO, temp, 0)) < 0) { - /* for windows compatibility, convert slashes */ - char *temp2 = xstrdup(temp); - char *p = temp2; - while (*p) { - if (*p == '\\') - *p = '/'; - p++; - } - if ((addToPlaylist(STDERR_FILENO, temp2, 0)) < 0) { - if (!*erroredFile) { - *erroredFile = xstrdup(temp); - } - } - free(temp2); - } -} - int PlaylistInfo(int fd, char *utf8file, int detail) { - if (detail) { - return PlaylistIterFunc(fd, utf8file, PlaylistInfoPrintInfo); + ListNode *node; + StoredPlaylist *sp = loadStoredPlaylist(utf8file, fd); + if (sp == NULL) + 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; + } + } + + if (!wrote) { + fdprintf(fd, SONG_FILE "%s\n", temp); + } + + node = node->nextNode; } - return PlaylistIterFunc(fd, utf8file, PlaylistInfoPrint); + + freeStoredPlaylist(sp); + return 0; } int loadPlaylist(int fd, char *utf8file) { - return PlaylistIterFunc(fd, utf8file, PlaylistLoadIterFunc); + ListNode *node; + StoredPlaylist *sp = loadStoredPlaylist(utf8file, fd); + if (sp == NULL) + return -1; + + node = sp->list->firstNode; + while (node != NULL) { + char *temp = node->data; + if (!getSongFromDB(temp) && !isRemoteUrl(temp)) { + + } else if ((addToPlaylist(STDERR_FILENO, temp, 0)) < 0) { + /* for windows compatibility, convert slashes */ + char *temp2 = xstrdup(temp); + char *p = temp2; + while (*p) { + if (*p == '\\') + *p = '/'; + p++; + } + if ((addToPlaylist(STDERR_FILENO, temp2, 0)) < 0) { + commandError(fd, ACK_ERROR_PLAYLIST_LOAD, + "can't add file \"%s\"", temp2); + } + free(temp2); + } + + node = node->nextNode; + } + + freeStoredPlaylist(sp); + return 0; } void searchForSongsInPlaylist(int fd, int numItems, LocateTagItem * items) diff --git a/src/playlist.h b/src/playlist.h index ce937fb6d..0ae3a677f 100644 --- a/src/playlist.h +++ b/src/playlist.h @@ -28,6 +28,24 @@ #include #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); @@ -47,8 +65,6 @@ int addToStoredPlaylist(int fd, char *file, char *utf8file); int addSongToPlaylist(int fd, Song * song, int printId); -int addSongToStoredPlaylist(int fd, Song *song, char *utf8file); - int showPlaylist(int fd); int deleteFromPlaylist(int fd, int song); diff --git a/src/storedPlaylist.c b/src/storedPlaylist.c new file mode 100644 index 000000000..b36bb10a7 --- /dev/null +++ b/src/storedPlaylist.c @@ -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 +#include + +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]); + } +} diff --git a/src/storedPlaylist.h b/src/storedPlaylist.h new file mode 100644 index 000000000..044d44ea3 --- /dev/null +++ b/src/storedPlaylist.h @@ -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