From 6cfe421cd64278f85310a258ab42c372c8a847b3 Mon Sep 17 00:00:00 2001 From: "J. Alexander Treuman" Date: Wed, 16 May 2007 12:02:10 +0000 Subject: 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 --- src/storedPlaylist.c | 473 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 473 insertions(+) create mode 100644 src/storedPlaylist.c (limited to 'src/storedPlaylist.c') 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]); + } +} -- cgit v1.2.3