2 * Copyright (C) 2003-2010 The Music Player Daemon Project
3 * 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.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "stored_playlist.h"
22 #include "playlist_save.h"
32 #include <sys/types.h>
39 static unsigned playlist_max_length
;
40 bool playlist_saveAbsolutePaths
= DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS
;
45 playlist_max_length
= config_get_positive(CONF_MAX_PLAYLIST_LENGTH
,
46 DEFAULT_PLAYLIST_MAX_LENGTH
);
48 playlist_saveAbsolutePaths
=
49 config_get_bool(CONF_SAVE_ABSOLUTE_PATHS
,
50 DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS
);
54 spl_valid_name(const char *name_utf8
)
57 * Not supporting '/' was done out of laziness, and we should
58 * really strive to support it in the future.
60 * Not supporting '\r' and '\n' is done out of protocol
61 * limitations (and arguably laziness), but bending over head
62 * over heels to modify the protocol (and compatibility with
63 * all clients) to support idiots who put '\r' and '\n' in
64 * filenames isn't going to happen, either.
67 return strchr(name_utf8
, '/') == NULL
&&
68 strchr(name_utf8
, '\n') == NULL
&&
69 strchr(name_utf8
, '\r') == NULL
;
72 static struct stored_playlist_info
*
73 load_playlist_info(const char *parent_path_fs
, const char *name_fs
)
75 size_t name_length
= strlen(name_fs
);
76 char *path_fs
, *name
, *name_utf8
;
79 struct stored_playlist_info
*playlist
;
81 if (name_length
< sizeof(PLAYLIST_FILE_SUFFIX
) ||
82 memchr(name_fs
, '\n', name_length
) != NULL
)
85 if (!g_str_has_suffix(name_fs
, PLAYLIST_FILE_SUFFIX
))
88 path_fs
= g_build_filename(parent_path_fs
, name_fs
, NULL
);
89 ret
= stat(path_fs
, &st
);
91 if (ret
< 0 || !S_ISREG(st
.st_mode
))
94 name
= g_strndup(name_fs
,
95 name_length
+ 1 - sizeof(PLAYLIST_FILE_SUFFIX
));
96 name_utf8
= fs_charset_to_utf8(name
);
98 if (name_utf8
== NULL
)
101 playlist
= g_new(struct stored_playlist_info
, 1);
102 playlist
->name
= name_utf8
;
103 playlist
->mtime
= st
.st_mtime
;
110 const char *parent_path_fs
= map_spl_path();
114 struct stored_playlist_info
*playlist
;
116 if (parent_path_fs
== NULL
)
119 dir
= opendir(parent_path_fs
);
123 list
= g_ptr_array_new();
125 while ((ent
= readdir(dir
)) != NULL
) {
126 playlist
= load_playlist_info(parent_path_fs
, ent
->d_name
);
127 if (playlist
!= NULL
)
128 g_ptr_array_add(list
, playlist
);
136 spl_list_free(GPtrArray
*list
)
138 for (unsigned i
= 0; i
< list
->len
; ++i
) {
139 struct stored_playlist_info
*playlist
=
140 g_ptr_array_index(list
, i
);
141 g_free(playlist
->name
);
145 g_ptr_array_free(list
, true);
148 static enum playlist_result
149 spl_save(GPtrArray
*list
, const char *utf8path
)
154 assert(utf8path
!= NULL
);
156 if (map_spl_path() == NULL
)
157 return PLAYLIST_RESULT_DISABLED
;
159 path_fs
= map_spl_utf8_to_fs(utf8path
);
161 return PLAYLIST_RESULT_BAD_NAME
;
163 while (!(file
= fopen(path_fs
, "w")) && errno
== EINTR
);
166 return PLAYLIST_RESULT_ERRNO
;
168 for (unsigned i
= 0; i
< list
->len
; ++i
) {
169 const char *uri
= g_ptr_array_index(list
, i
);
170 playlist_print_uri(file
, uri
);
173 while (fclose(file
) != 0 && errno
== EINTR
);
174 return PLAYLIST_RESULT_SUCCESS
;
178 spl_load(const char *utf8path
)
182 char buffer
[MPD_PATH_MAX
];
185 if (!spl_valid_name(utf8path
) || map_spl_path() == NULL
)
188 path_fs
= map_spl_utf8_to_fs(utf8path
);
192 while (!(file
= fopen(path_fs
, "r")) && errno
== EINTR
);
197 list
= g_ptr_array_new();
199 while (fgets(buffer
, sizeof(buffer
), file
)) {
202 if (*s
== PLAYLIST_COMMENT
)
207 if (!uri_has_scheme(s
)) {
211 path_utf8
= map_fs_to_utf8(s
);
212 if (path_utf8
== NULL
)
215 song
= db_get_song(path_utf8
);
220 s
= song_get_uri(song
);
224 g_ptr_array_add(list
, s
);
226 if (list
->len
>= playlist_max_length
)
230 while (fclose(file
) && errno
== EINTR
);
235 spl_free(GPtrArray
*list
)
237 for (unsigned i
= 0; i
< list
->len
; ++i
) {
238 char *uri
= g_ptr_array_index(list
, i
);
242 g_ptr_array_free(list
, true);
246 spl_remove_index_internal(GPtrArray
*list
, unsigned idx
)
250 assert(idx
< list
->len
);
252 uri
= g_ptr_array_remove_index(list
, idx
);
258 spl_insert_index_internal(GPtrArray
*list
, unsigned idx
, char *uri
)
260 assert(idx
<= list
->len
);
262 g_ptr_array_add(list
, uri
);
264 memmove(list
->pdata
+ idx
+ 1, list
->pdata
+ idx
,
265 (list
->len
- idx
- 1) * sizeof(list
->pdata
[0]));
266 g_ptr_array_index(list
, idx
) = uri
;
270 spl_move_index(const char *utf8path
, unsigned src
, unsigned dest
)
274 enum playlist_result result
;
277 /* this doesn't check whether the playlist exists, but
279 return PLAYLIST_RESULT_SUCCESS
;
281 if (!(list
= spl_load(utf8path
)))
282 return PLAYLIST_RESULT_NO_SUCH_LIST
;
284 if (src
>= list
->len
|| dest
>= list
->len
) {
286 return PLAYLIST_RESULT_BAD_RANGE
;
289 uri
= spl_remove_index_internal(list
, src
);
290 spl_insert_index_internal(list
, dest
, uri
);
292 result
= spl_save(list
, utf8path
);
296 idle_add(IDLE_STORED_PLAYLIST
);
301 spl_clear(const char *utf8path
)
306 if (map_spl_path() == NULL
)
307 return PLAYLIST_RESULT_DISABLED
;
309 if (!spl_valid_name(utf8path
))
310 return PLAYLIST_RESULT_BAD_NAME
;
312 path_fs
= map_spl_utf8_to_fs(utf8path
);
314 return PLAYLIST_RESULT_BAD_NAME
;
316 while (!(file
= fopen(path_fs
, "w")) && errno
== EINTR
);
319 return PLAYLIST_RESULT_ERRNO
;
321 while (fclose(file
) != 0 && errno
== EINTR
);
323 idle_add(IDLE_STORED_PLAYLIST
);
324 return PLAYLIST_RESULT_SUCCESS
;
328 spl_delete(const char *name_utf8
)
333 if (map_spl_path() == NULL
)
334 return PLAYLIST_RESULT_DISABLED
;
336 if (!spl_valid_name(name_utf8
))
337 return PLAYLIST_RESULT_BAD_NAME
;
339 path_fs
= map_spl_utf8_to_fs(name_utf8
);
341 return PLAYLIST_RESULT_BAD_NAME
;
343 ret
= unlink(path_fs
);
346 return errno
== ENOENT
347 ? PLAYLIST_RESULT_NO_SUCH_LIST
348 : PLAYLIST_RESULT_ERRNO
;
350 idle_add(IDLE_STORED_PLAYLIST
);
351 return PLAYLIST_RESULT_SUCCESS
;
355 spl_remove_index(const char *utf8path
, unsigned pos
)
359 enum playlist_result result
;
361 if (!(list
= spl_load(utf8path
)))
362 return PLAYLIST_RESULT_NO_SUCH_LIST
;
364 if (pos
>= list
->len
) {
366 return PLAYLIST_RESULT_BAD_RANGE
;
369 uri
= spl_remove_index_internal(list
, pos
);
371 result
= spl_save(list
, utf8path
);
375 idle_add(IDLE_STORED_PLAYLIST
);
380 spl_append_song(const char *utf8path
, struct song
*song
)
386 if (map_spl_path() == NULL
)
387 return PLAYLIST_RESULT_DISABLED
;
389 if (!spl_valid_name(utf8path
))
390 return PLAYLIST_RESULT_BAD_NAME
;
392 path_fs
= map_spl_utf8_to_fs(utf8path
);
394 return PLAYLIST_RESULT_BAD_NAME
;
396 while (!(file
= fopen(path_fs
, "a")) && errno
== EINTR
);
399 return PLAYLIST_RESULT_ERRNO
;
401 if (fstat(fileno(file
), &st
) < 0) {
402 int save_errno
= errno
;
403 while (fclose(file
) != 0 && errno
== EINTR
);
405 return PLAYLIST_RESULT_ERRNO
;
408 if (st
.st_size
/ (MPD_PATH_MAX
+ 1) >= (off_t
)playlist_max_length
) {
409 while (fclose(file
) != 0 && errno
== EINTR
);
410 return PLAYLIST_RESULT_TOO_LARGE
;
413 playlist_print_song(file
, song
);
415 while (fclose(file
) != 0 && errno
== EINTR
);
417 idle_add(IDLE_STORED_PLAYLIST
);
418 return PLAYLIST_RESULT_SUCCESS
;
422 spl_append_uri(const char *url
, const char *utf8file
)
426 if (uri_has_scheme(url
)) {
427 enum playlist_result ret
;
429 song
= song_remote_new(url
);
430 ret
= spl_append_song(utf8file
, song
);
434 song
= db_get_song(url
);
436 return PLAYLIST_RESULT_NO_SUCH_SONG
;
438 return spl_append_song(utf8file
, song
);
442 static enum playlist_result
443 spl_rename_internal(const char *from_path_fs
, const char *to_path_fs
)
445 if (!g_file_test(from_path_fs
, G_FILE_TEST_IS_REGULAR
))
446 return PLAYLIST_RESULT_NO_SUCH_LIST
;
448 if (g_file_test(to_path_fs
, G_FILE_TEST_EXISTS
))
449 return PLAYLIST_RESULT_LIST_EXISTS
;
451 if (rename(from_path_fs
, to_path_fs
) < 0)
452 return PLAYLIST_RESULT_ERRNO
;
454 idle_add(IDLE_STORED_PLAYLIST
);
455 return PLAYLIST_RESULT_SUCCESS
;
459 spl_rename(const char *utf8from
, const char *utf8to
)
461 char *from_path_fs
, *to_path_fs
;
462 static enum playlist_result ret
;
464 if (map_spl_path() == NULL
)
465 return PLAYLIST_RESULT_DISABLED
;
467 if (!spl_valid_name(utf8from
) || !spl_valid_name(utf8to
))
468 return PLAYLIST_RESULT_BAD_NAME
;
470 from_path_fs
= map_spl_utf8_to_fs(utf8from
);
471 to_path_fs
= map_spl_utf8_to_fs(utf8to
);
473 if (from_path_fs
!= NULL
&& to_path_fs
!= NULL
)
474 ret
= spl_rename_internal(from_path_fs
, to_path_fs
);
476 ret
= PLAYLIST_RESULT_BAD_NAME
;
478 g_free(from_path_fs
);