1 /* the Music Player Daemon (MPD)
2 * Copyright (C) 2007 by Warren Dukes (warren.dukes@gmail.com)
3 * This project's homepage is: http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "storedPlaylist.h"
27 #include "directory.h"
32 static char *utf8pathToFsPathInStoredPlaylist(const char *utf8path
, int fd
)
38 if (strstr(utf8path
, "/")) {
39 commandError(fd
, ACK_ERROR_ARG
, "playlist name \"%s\" is "
40 "invalid: playlist names may not contain slashes",
45 file
= utf8ToFsCharset((char *)utf8path
);
47 rfile
= xmalloc(strlen(file
) + strlen(".") +
48 strlen(PLAYLIST_FILE_SUFFIX
) + 1);
52 strcat(rfile
, PLAYLIST_FILE_SUFFIX
);
54 actualFile
= rpp2app(rfile
);
61 static unsigned int lengthOfStoredPlaylist(StoredPlaylist
*sp
)
63 return sp
->list
->numberOfNodes
;
66 static ListNode
*nodeOfStoredPlaylist(StoredPlaylist
*sp
, int index
)
72 if (index
>= lengthOfStoredPlaylist(sp
) || index
< 0)
75 if (index
> lengthOfStoredPlaylist(sp
)/2) {
77 node
= sp
->list
->lastNode
;
78 i
= lengthOfStoredPlaylist(sp
) - 1;
81 node
= sp
->list
->firstNode
;
85 while (node
!= NULL
) {
91 node
= node
->nextNode
;
94 node
= node
->prevNode
;
101 static void appendSongToStoredPlaylist(StoredPlaylist
*sp
, Song
*song
)
103 insertInListWithoutKey(sp
->list
, xstrdup(getSongUrl(song
)));
106 StoredPlaylist
*newStoredPlaylist(const char *utf8name
, int fd
, int ignoreExisting
)
109 char *filename
= NULL
;
110 StoredPlaylist
*sp
= calloc(1, sizeof(*sp
));
115 filename
= utf8pathToFsPathInStoredPlaylist(utf8name
, fd
);
117 if (filename
&& stat(filename
, &buf
) == 0 &&
118 ignoreExisting
== 0) {
119 commandError(fd
, ACK_ERROR_EXIST
,
120 "a file or directory already exists with "
121 "the name \"%s\"", utf8name
);
127 sp
->list
= makeList(DEFAULT_FREE_DATA_FUNC
, 0);
131 sp
->fspath
= xstrdup(filename
);
136 StoredPlaylist
*loadStoredPlaylist(const char *utf8path
, int fd
)
141 char s
[MAXPATHLEN
+ 1];
143 char *temp
= utf8ToFsCharset((char *)utf8path
);
144 char *parent
= parentPath(temp
);
145 int parentlen
= strlen(parent
);
147 int commentCharFound
= 0;
150 filename
= utf8pathToFsPathInStoredPlaylist(utf8path
, fd
);
154 while (!(file
= fopen(filename
, "r")) && errno
== EINTR
);
156 commandError(fd
, ACK_ERROR_NO_EXIST
, "could not open file "
157 "\"%s\": %s", filename
, strerror(errno
));
161 sp
= newStoredPlaylist(utf8path
, fd
, 1);
165 while ((tempInt
= fgetc(file
)) != EOF
) {
166 s
[slength
] = tempInt
;
167 if (s
[slength
] == '\n' || s
[slength
] == '\0') {
168 commentCharFound
= 0;
170 if (s
[0] == PLAYLIST_COMMENT
)
171 commentCharFound
= 1;
172 if (strncmp(s
, musicDir
, strlen(musicDir
)) == 0) {
173 strcpy(s
, &(s
[strlen(musicDir
)]));
174 } else if (parentlen
) {
176 memset(s
, 0, MAXPATHLEN
+ 1);
178 strncat(s
, "/", MAXPATHLEN
- parentlen
);
179 strncat(s
, temp
, MAXPATHLEN
- parentlen
- 1);
180 if (strlen(s
) >= MAXPATHLEN
) {
182 ACK_ERROR_PLAYLIST_LOAD
,
183 "\"%s\" is too long", temp
);
185 freeStoredPlaylist(sp
);
192 temp
= fsCharsetToUtf8(s
);
193 if (temp
&& !commentCharFound
) {
194 song
= getSongFromDB(temp
);
196 appendSongToStoredPlaylist(sp
, song
);
200 if (!isValidRemoteUtf8Url(temp
))
203 song
= newSong(temp
, SONG_TYPE_URL
, NULL
);
205 appendSongToStoredPlaylist(sp
, song
);
209 } else if (slength
== MAXPATHLEN
) {
211 commandError(sp
->fd
, ACK_ERROR_PLAYLIST_LOAD
,
212 "line \"%s\" in playlist \"%s\" "
213 "is too long", s
, utf8path
);
214 freeStoredPlaylist(sp
);
217 } else if (s
[slength
] != '\r') {
223 while (fclose(file
) && errno
== EINTR
);
227 void freeStoredPlaylist(StoredPlaylist
*sp
)
237 static int moveSongInStoredPlaylist(int fd
, StoredPlaylist
*sp
, int src
, int dest
)
239 ListNode
*srcNode
, *destNode
;
241 if (src
>= lengthOfStoredPlaylist(sp
) || dest
>= lengthOfStoredPlaylist(sp
) || src
< 0 || dest
< 0 || src
== dest
) {
242 commandError(fd
, ACK_ERROR_ARG
, "argument out of range");
246 srcNode
= nodeOfStoredPlaylist(sp
, src
);
250 destNode
= nodeOfStoredPlaylist(sp
, dest
);
253 if (srcNode
->prevNode
)
254 srcNode
->prevNode
->nextNode
= srcNode
->nextNode
;
256 sp
->list
->firstNode
= srcNode
->nextNode
;
258 if (srcNode
->nextNode
)
259 srcNode
->nextNode
->prevNode
= srcNode
->prevNode
;
261 sp
->list
->lastNode
= srcNode
->prevNode
;
263 /* this is all a bit complicated - but I tried to
264 * maintain the same order stuff is moved as in the
267 sp
->list
->firstNode
->prevNode
= srcNode
;
268 srcNode
->nextNode
= sp
->list
->firstNode
;
269 srcNode
->prevNode
= NULL
;
270 sp
->list
->firstNode
= srcNode
;
271 } else if ((dest
+ 1) == lengthOfStoredPlaylist(sp
)) {
272 sp
->list
->lastNode
->nextNode
= srcNode
;
273 srcNode
->nextNode
= NULL
;
274 srcNode
->prevNode
= sp
->list
->lastNode
;
275 sp
->list
->lastNode
= srcNode
;
277 if (destNode
== NULL
) {
278 /* this shouldn't be happening. */
283 destNode
->prevNode
->nextNode
= srcNode
;
284 srcNode
->prevNode
= destNode
->prevNode
;
285 srcNode
->nextNode
= destNode
;
286 destNode
->prevNode
= srcNode
;
288 destNode
->nextNode
->prevNode
= srcNode
;
289 srcNode
->prevNode
= destNode
;
290 srcNode
->nextNode
= destNode
->nextNode
;
291 destNode
->nextNode
= srcNode
;
298 int moveSongInStoredPlaylistByPath(int fd
, const char *utf8path
, int src
, int dest
)
300 StoredPlaylist
*sp
= loadStoredPlaylist(utf8path
, fd
);
302 commandError(fd
, ACK_ERROR_UNKNOWN
, "could not open playlist");
306 if (moveSongInStoredPlaylist(fd
, sp
, src
, dest
) != 0) {
307 freeStoredPlaylist(sp
);
311 if (writeStoredPlaylist(sp
) != 0) {
312 commandError(fd
, ACK_ERROR_UNKNOWN
, "failed to save playlist");
313 freeStoredPlaylist(sp
);
317 freeStoredPlaylist(sp
);
321 /* Not used currently
322 static void removeAllFromStoredPlaylist(StoredPlaylist *sp)
325 sp->list = makeList(DEFAULT_FREE_DATA_FUNC, 0);
329 int removeAllFromStoredPlaylistByPath(int fd
, const char *utf8path
)
334 filename
= utf8pathToFsPathInStoredPlaylist(utf8path
, fd
);
338 while (!(file
= fopen(filename
, "w")) && errno
== EINTR
);
340 commandError(fd
, ACK_ERROR_NO_EXIST
, "could not open file "
341 "\"%s\": %s", filename
, strerror(errno
));
345 while (fclose(file
) != 0 && errno
== EINTR
);
349 static int removeOneSongFromStoredPlaylist(int fd
, StoredPlaylist
*sp
, int pos
)
351 ListNode
*node
= nodeOfStoredPlaylist(sp
, pos
);
353 commandError(fd
, ACK_ERROR_ARG
,
354 "could not find song at position");
358 deleteNodeFromList(sp
->list
, node
);
363 int removeOneSongFromStoredPlaylistByPath(int fd
, const char *utf8path
, int pos
)
365 StoredPlaylist
*sp
= loadStoredPlaylist(utf8path
, fd
);
367 commandError(fd
, ACK_ERROR_UNKNOWN
, "could not open playlist");
371 if (removeOneSongFromStoredPlaylist(fd
, sp
, pos
) != 0) {
372 freeStoredPlaylist(sp
);
376 if (writeStoredPlaylist(sp
) != 0) {
377 commandError(fd
, ACK_ERROR_UNKNOWN
, "failed to save playlist");
378 freeStoredPlaylist(sp
);
382 freeStoredPlaylist(sp
);
386 static int writeStoredPlaylistToPath(StoredPlaylist
*sp
, const char *fspath
)
395 while (!(file
= fopen(fspath
, "w")) && errno
== EINTR
);
397 commandError(sp
->fd
, ACK_ERROR_NO_EXIST
, "could not open file "
398 "\"%s\": %s", fspath
, strerror(errno
));
402 node
= sp
->list
->firstNode
;
403 while (node
!= NULL
) {
404 s
= (char *)node
->data
;
405 if (isValidRemoteUtf8Url(s
) || !playlist_saveAbsolutePaths
)
406 s
= utf8ToFsCharset(s
);
408 s
= rmp2amp(utf8ToFsCharset(s
));
409 fprintf(file
, "%s\n", s
);
410 node
= node
->nextNode
;
413 while (fclose(file
) != 0 && errno
== EINTR
);
417 int writeStoredPlaylist(StoredPlaylist
*sp
)
419 return writeStoredPlaylistToPath(sp
, sp
->fspath
);
422 int appendSongToStoredPlaylistByPath(int fd
, const char *utf8path
, Song
*song
)
428 filename
= utf8pathToFsPathInStoredPlaylist(utf8path
, fd
);
432 while (!(file
= fopen(filename
, "a")) && errno
== EINTR
);
434 commandError(fd
, ACK_ERROR_NO_EXIST
, "could not open file "
435 "\"%s\": %s", filename
, strerror(errno
));
439 if (playlist_saveAbsolutePaths
&& song
->type
== SONG_TYPE_FILE
)
440 s
= rmp2amp(utf8ToFsCharset(getSongUrl(song
)));
442 s
= utf8ToFsCharset(getSongUrl(song
));
444 fprintf(file
, "%s\n", s
);
446 while (fclose(file
) != 0 && errno
== EINTR
);
450 void appendPlaylistToStoredPlaylist(StoredPlaylist
*sp
, Playlist
*playlist
)
453 for (i
= 0; i
< playlist
->length
; i
++)
454 appendSongToStoredPlaylist(sp
, playlist
->songs
[i
]);
457 int renameStoredPlaylist(int fd
, const char *utf8from
, const char *utf8to
)
464 from
= xstrdup(utf8pathToFsPathInStoredPlaylist(utf8from
, fd
));
468 to
= xstrdup(utf8pathToFsPathInStoredPlaylist(utf8to
, fd
));
474 if (stat(from
, &st
) != 0) {
475 commandError(fd
, ACK_ERROR_NO_EXIST
,
476 "no playlist named \"%s\"", utf8from
);
481 if (stat(to
, &st
) == 0) {
482 commandError(fd
, ACK_ERROR_EXIST
, "a file or directory "
483 "already exists with the name \"%s\"", utf8to
);
488 if (rename(from
, to
) < 0) {
489 commandError(fd
, ACK_ERROR_UNKNOWN
,
490 "could not rename playlist \"%s\" to \"%s\": %s",
491 utf8from
, utf8to
, strerror(errno
));