From 5b71d5f6f707a35a7b7a47b33ef2e1417ea6bcc6 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 14 Oct 2008 11:10:49 +0200 Subject: mapper: new song-to-filesystem mapper library The mapper library maps directory and song objects to file system paths. With this central library, the code mixture in path.c should be cleaned up, and we will be able to add neat features like aliasing. --- src/Makefile.am | 2 ++ src/decoder_thread.c | 21 +++++++------- src/mapper.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/mapper.h | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/playlist.c | 1 + src/playlist_save.c | 22 +++++++++------ src/song.c | 5 ++-- src/storedPlaylist.c | 26 ++++++++++++------ src/update.c | 66 ++++++++++++++++++++++++++++---------------- 9 files changed, 241 insertions(+), 53 deletions(-) create mode 100644 src/mapper.c create mode 100644 src/mapper.h diff --git a/src/Makefile.am b/src/Makefile.am index ca6f8f5ab..2151daa77 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -79,6 +79,7 @@ mpd_headers = \ os_compat.h \ outputBuffer.h \ path.h \ + mapper.h \ pcm_utils.h \ permission.h \ player_thread.h \ @@ -158,6 +159,7 @@ mpd_SOURCES = \ compress.c \ outputBuffer.c \ path.c \ + mapper.c \ pcm_utils.c \ permission.c \ player_thread.c \ diff --git a/src/decoder_thread.c b/src/decoder_thread.c index 594bfbc77..f0f315f3a 100644 --- a/src/decoder_thread.c +++ b/src/decoder_thread.c @@ -22,27 +22,28 @@ #include "decoder_internal.h" #include "player_control.h" #include "song.h" - +#include "mapper.h" #include "path.h" #include "log.h" #include "ls.h" static void decodeStart(void) { + struct song *song = dc.next_song; struct decoder decoder; int ret; bool close_instream = true; InputStream inStream; struct decoder_plugin *plugin = NULL; char path_max_fs[MPD_PATH_MAX]; - char path_max_utf8[MPD_PATH_MAX]; - song_get_url(dc.next_song, path_max_utf8); - if (!isRemoteUrl(path_max_utf8)) { - rmp2amp_r(path_max_fs, - utf8_to_fs_charset(path_max_fs, path_max_utf8)); - } else + if (song_is_file(song)) + map_song_fs(song, path_max_fs); + else { + char path_max_utf8[MPD_PATH_MAX]; + song_get_url(song, path_max_utf8); pathcpy_trunc(path_max_fs, path_max_utf8); + } dc.current_song = dc.next_song; /* NEED LOCK */ if (openInputStream(&inStream, path_max_fs) < 0) { @@ -74,7 +75,7 @@ static void decodeStart(void) goto stop; ret = DECODE_ERROR_UNKTYPE; - if (isRemoteUrl(path_max_utf8)) { + if (!song_is_file(song)) { unsigned int next = 0; /* first we try mime types: */ @@ -92,7 +93,7 @@ static void decodeStart(void) /* if that fails, try suffix matching the URL: */ if (plugin == NULL) { - const char *s = getSuffix(path_max_utf8); + const char *s = getSuffix(path_max_fs); next = 0; while (ret && (plugin = decoder_plugin_from_suffix(s, next++))) { if (plugin->stream_decode == NULL) @@ -123,7 +124,7 @@ static void decodeStart(void) } } else { unsigned int next = 0; - const char *s = getSuffix(path_max_utf8); + const char *s = getSuffix(path_max_fs); while (ret && (plugin = decoder_plugin_from_suffix(s, next++))) { if (!plugin->stream_types & INPUT_PLUGIN_STREAM_FILE) continue; diff --git a/src/mapper.c b/src/mapper.c new file mode 100644 index 000000000..392211214 --- /dev/null +++ b/src/mapper.c @@ -0,0 +1,77 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2008 Max Kellermann + * 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 + */ + +/* + * Maps directory and song objects to file system paths. + */ + +#include "mapper.h" +#include "directory.h" +#include "song.h" +#include "path.h" + +const char * +map_directory_fs(const struct directory *directory, char *buffer) +{ + const char *dirname = directory_get_path(directory); + if (isRootDirectory(dirname)) + return musicDir; + + return rmp2amp_r(buffer, utf8_to_fs_charset(buffer, dirname)); +} + +const char * +map_directory_child_fs(const struct directory *directory, const char *name, + char *buffer) +{ + char buffer2[MPD_PATH_MAX]; + const char *parent_fs; + + parent_fs = map_directory_fs(directory, buffer2); + if (parent_fs == NULL) + return NULL; + + utf8_to_fs_charset(buffer, name); + pfx_dir(buffer, name, strlen(name), + parent_fs, strlen(parent_fs)); + return buffer; +} + +const char * +map_song_fs(const struct song *song, char *buffer) +{ + assert(song->parent != NULL); + + return map_directory_child_fs(song->parent, song->url, buffer); +} + +const char * +map_fs_to_utf8(const char *path_fs, char *buffer) +{ + size_t music_path_length = strlen(musicDir); + + if (strncmp(path_fs, musicDir, music_path_length) == 0 && + path_fs[music_path_length] == '/') + /* remove musicDir prefix */ + path_fs += music_path_length; + else if (path_fs[0] == '/') + /* not within musicDir */ + return NULL; + + return fs_charset_to_utf8(buffer, path_fs); +} diff --git a/src/mapper.h b/src/mapper.h new file mode 100644 index 000000000..2c21a68e8 --- /dev/null +++ b/src/mapper.h @@ -0,0 +1,74 @@ +/* the Music Player Daemon (MPD) + * Copyright (C) 2008 Max Kellermann + * 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 + */ + +/* + * Maps directory and song objects to file system paths. + */ + +#ifndef MAPPER_H +#define MAPPER_H + +struct directory; +struct song; + +/** + * Determines the file system path of a directory object. + * + * @param directory the directory object + * @param a buffer which is MPD_PATH_MAX bytes long + * @return the path in file system encoding, or NULL if mapping failed + */ +const char * +map_directory_fs(const struct directory *directory, char *buffer); + +/** + * Determines the file system path of a directory's child (may be a + * sub directory or a song). + * + * @param directory the parent directory object + * @param name the child's name in UTF-8 + * @param a buffer which is MPD_PATH_MAX bytes long + * @return the path in file system encoding, or NULL if mapping failed + */ +const char * +map_directory_child_fs(const struct directory *directory, const char *name, + char *buffer); + +/** + * Determines the file system path of a song. This must not be a + * remote song. + * + * @param song the song object + * @param a buffer which is MPD_PATH_MAX bytes long + * @return the path in file system encoding, or NULL if mapping failed + */ +const char * +map_song_fs(const struct song *song, char *buffer); + +/** + * Maps a file system path (relative to the music directory or + * absolute) to a relative path in UTF-8 encoding. + * + * @param path_fs a path in file system encoding + * @param buffer a buffer which is MPD_PATH_MAX bytes long + * @return the relative path in UTF-8, or NULL if mapping failed + */ +const char * +map_fs_to_utf8(const char *path_fs, char *buffer); + +#endif diff --git a/src/playlist.c b/src/playlist.c index 65e7d6391..b160202bf 100644 --- a/src/playlist.c +++ b/src/playlist.c @@ -28,6 +28,7 @@ #include "conf.h" #include "database.h" #include "log.h" +#include "mapper.h" #include "path.h" #include "utils.h" #include "state_file.h" diff --git a/src/playlist_save.c b/src/playlist_save.c index 98979e2a2..55cd6bd87 100644 --- a/src/playlist_save.c +++ b/src/playlist_save.c @@ -19,21 +19,25 @@ #include "playlist_save.h" #include "playlist.h" #include "song.h" +#include "mapper.h" #include "path.h" #include "ls.h" +#include "database.h" void playlist_print_song(FILE *file, const struct song *song) { char tmp1[MPD_PATH_MAX], tmp2[MPD_PATH_MAX]; - song_get_url(song, tmp1); - utf8_to_fs_charset(tmp2, tmp1); - - if (playlist_saveAbsolutePaths && song_is_file(song)) - fprintf(file, "%s\n", rmp2amp_r(tmp2, tmp2)); - else + if (playlist_saveAbsolutePaths && song_is_file(song)) { + const char *path = map_song_fs(song, tmp1); + if (path != NULL) + fprintf(file, "%s\n", path); + } else { + song_get_url(song, tmp1); + utf8_to_fs_charset(tmp2, tmp1); fprintf(file, "%s\n", tmp2); + } } void @@ -42,8 +46,10 @@ playlist_print_uri(FILE *file, const char *uri) char tmp[MPD_PATH_MAX]; const char *s; - s = utf8_to_fs_charset(tmp, uri); if (playlist_saveAbsolutePaths && !isValidRemoteUtf8Url(s)) - s = rmp2amp_r(tmp, s); + s = map_directory_child_fs(db_get_root(), uri, tmp); + else + s = utf8_to_fs_charset(tmp, uri); + fprintf(file, "%s\n", s); } diff --git a/src/song.c b/src/song.c index 2de9e52c7..b4ab28cab 100644 --- a/src/song.c +++ b/src/song.c @@ -21,6 +21,7 @@ #include "directory.h" #include "utils.h" #include "log.h" +#include "mapper.h" #include "path.h" #include "playlist.h" #include "decoder_list.h" @@ -95,14 +96,12 @@ song_file_update(struct song *song) { struct decoder_plugin *plugin; unsigned int next = 0; - char path_max_tmp[MPD_PATH_MAX]; char abs_path[MPD_PATH_MAX]; struct stat st; assert(song_is_file(song)); - utf8_to_fs_charset(abs_path, song_get_url(song, path_max_tmp)); - rmp2amp_r(abs_path, abs_path); + map_song_fs(song, abs_path); if (song->tag != NULL) { tag_free(song->tag); diff --git a/src/storedPlaylist.c b/src/storedPlaylist.c index 4e20a6de4..3d5b8286f 100644 --- a/src/storedPlaylist.c +++ b/src/storedPlaylist.c @@ -19,6 +19,7 @@ #include "storedPlaylist.h" #include "playlist_save.h" #include "song.h" +#include "mapper.h" #include "path.h" #include "utils.h" #include "ls.h" @@ -91,7 +92,6 @@ List *loadStoredPlaylist(const char *utf8path) FILE *file; char buffer[MPD_PATH_MAX]; char path_max_tmp[MPD_PATH_MAX]; - const size_t musicDir_len = strlen(musicDir); if (!is_valid_playlist_name(utf8path)) return NULL; @@ -105,19 +105,27 @@ List *loadStoredPlaylist(const char *utf8path) while (myFgets(buffer, sizeof(buffer), file)) { char *s = buffer; - struct song *song; + const char *path_utf8; if (*s == PLAYLIST_COMMENT) continue; - if (s[musicDir_len] == '/' && - !strncmp(s, musicDir, musicDir_len)) - memmove(s, s + musicDir_len + 1, - strlen(s + musicDir_len + 1) + 1); - if ((song = db_get_song(s))) { + + if (isValidRemoteUtf8Url(s)) + insertInListWithoutKey(list, xstrdup(s)); + else { + struct song *song; + + path_utf8 = map_fs_to_utf8(s, path_max_tmp); + if (path_utf8 == NULL) + continue; + + song = db_get_song(path_utf8); + if (song == NULL) + continue; + song_get_url(song, path_max_tmp); insertInListWithoutKey(list, xstrdup(path_max_tmp)); - } else if (isValidRemoteUtf8Url(s)) - insertInListWithoutKey(list, xstrdup(s)); + } if (list->numberOfNodes >= playlist_max_length) break; diff --git a/src/update.c b/src/update.c index 93ba1668c..e36a980dd 100644 --- a/src/update.c +++ b/src/update.c @@ -23,6 +23,7 @@ #include "song.h" #include "log.h" #include "ls.h" +#include "mapper.h" #include "path.h" #include "playlist.h" #include "utils.h" @@ -150,11 +151,11 @@ static int delete_song_if_removed(struct song *song, void *_data) { struct delete_data *data = _data; + const char *path; + struct stat st; - data->tmp = song_get_url(song, data->tmp); - assert(data->tmp); - - if (!isFile(data->tmp, NULL)) { + if ((path = map_song_fs(song, data->tmp)) == NULL || + stat(data->tmp, &st) < 0 || !S_ISREG(st.st_mode)) { delete_song(data->dir, song); modified = true; } @@ -169,7 +170,12 @@ removeDeletedFromDirectory(char *path_max_tmp, struct directory *directory) struct delete_data data; for (i = dv->nr; --i >= 0; ) { - if (isDir(dv->base[i]->path)) + const char *path_fs; + struct stat st; + + path_fs = map_directory_fs(dv->base[i], path_max_tmp); + if (path_fs == NULL || (stat(path_fs, &st) == 0 && + S_ISDIR(st.st_mode))) continue; LOG("removing directory: %s\n", dv->base[i]->path); dirvec_delete(dv, dv->base[i]); @@ -181,12 +187,26 @@ removeDeletedFromDirectory(char *path_max_tmp, struct directory *directory) songvec_for_each(&directory->songs, delete_song_if_removed, &data); } -static const char *opendir_path(char *path_max_tmp, const char *dirname) +static int +stat_directory(const struct directory *directory, struct stat *st) { - if (*dirname != '\0') - return rmp2amp_r(path_max_tmp, - utf8_to_fs_charset(path_max_tmp, dirname)); - return musicDir; + char buffer[MPD_PATH_MAX]; + const char *path_fs; + + path_fs = map_directory_fs(directory, buffer); + if (path_fs == NULL) + return -1; + return stat(path_fs, st); +} + +static int +stat_directory_child(const struct directory *parent, const char *name, + struct stat *st) +{ + char path_fs[MPD_PATH_MAX]; + + map_directory_child_fs(parent, name, path_fs); + return stat(path_fs, st); } static int @@ -194,7 +214,7 @@ statDirectory(struct directory *dir) { struct stat st; - if (myStat(directory_get_path(dir), &st) < 0) + if (stat_directory(dir, &st) < 0) return -1; directory_set_stat(dir, &st); @@ -296,15 +316,19 @@ static bool updateDirectory(struct directory *directory, const struct stat *st) { DIR *dir; - const char *dirname = directory_get_path(directory); struct dirent *ent; char path_max_tmp[MPD_PATH_MAX]; + const char *path_fs; assert(S_ISDIR(st->st_mode)); directory_set_stat(directory, st); - dir = opendir(opendir_path(path_max_tmp, dirname)); + path_fs = map_directory_fs(directory, path_max_tmp); + if (path_fs == NULL) + return false; + + dir = opendir(path_fs); if (!dir) return false; @@ -321,15 +345,11 @@ updateDirectory(struct directory *directory, const struct stat *st) if (!utf8) continue; - if (!isRootDirectory(directory->path)) - utf8 = pfx_dir(path_max_tmp, utf8, strlen(utf8), - dirname, strlen(dirname)); - - if (myStat(path_max_tmp, &st2) == 0) + if (stat_directory_child(directory, utf8, &st2) == 0) updateInDirectory(directory, - mpd_basename(path_max_tmp), &st2); + path_max_tmp, &st2); else - delete_name_in(directory, mpd_basename(path_max_tmp)); + delete_name_in(directory, path_max_tmp); } closedir(dir); @@ -348,7 +368,7 @@ directory_make_child_checked(struct directory *parent, const char *path) if (directory != NULL) return directory; - if (myStat(path, &st) < 0 || + if (stat_directory_child(parent, mpd_basename(path), &st) < 0 || inodeFoundInParent(parent, st.st_ino, st.st_dev)) return NULL; @@ -398,7 +418,7 @@ updatePath(const char *path) name = mpd_basename(path); - if (myStat(path, &st) == 0) + if (stat_directory_child(parent, name, &st) == 0) updateInDirectory(parent, name, &st); else delete_name_in(parent, name); @@ -413,7 +433,7 @@ static void * update_task(void *_path) struct directory *directory = db_get_root(); struct stat st; - if (myStat(directory_get_path(directory), &st) == 0) + if (stat_directory(directory, &st) == 0) updateDirectory(directory, &st); } -- cgit v1.2.3