diff options
author | Max Kellermann <max@duempel.org> | 2013-01-17 00:56:57 +0100 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2013-01-18 15:34:01 +0100 |
commit | e5039c478aa93e3e107ee9031a3cf27a6c203fea (patch) | |
tree | 5d9fecc644b915fe02634911d65c29123e03c1b8 /src | |
parent | 890151450663abd581cab56e853e8e713d822851 (diff) |
Path: new class "Path" wraps filesystem path strings
Diffstat (limited to 'src')
-rw-r--r-- | src/DecoderThread.cxx | 3 | ||||
-rw-r--r-- | src/ExcludeList.cxx | 8 | ||||
-rw-r--r-- | src/ExcludeList.hxx | 4 | ||||
-rw-r--r-- | src/Main.cxx | 3 | ||||
-rw-r--r-- | src/Mapper.cxx | 82 | ||||
-rw-r--r-- | src/Mapper.hxx | 19 | ||||
-rw-r--r-- | src/Path.hxx | 207 | ||||
-rw-r--r-- | src/PlaylistFile.cxx | 69 | ||||
-rw-r--r-- | src/PlaylistMapper.cxx | 13 | ||||
-rw-r--r-- | src/PlaylistSave.cxx | 47 | ||||
-rw-r--r-- | src/PlaylistSong.cxx | 4 | ||||
-rw-r--r-- | src/SongUpdate.cxx | 18 | ||||
-rw-r--r-- | src/StateFile.cxx | 6 | ||||
-rw-r--r-- | src/StateFile.hxx | 5 | ||||
-rw-r--r-- | src/TextFile.hxx | 6 | ||||
-rw-r--r-- | src/UpdateArchive.cxx | 10 | ||||
-rw-r--r-- | src/UpdateContainer.cxx | 13 | ||||
-rw-r--r-- | src/UpdateIO.cxx | 47 | ||||
-rw-r--r-- | src/UpdateWalk.cxx | 42 | ||||
-rw-r--r-- | src/db/SimpleDatabasePlugin.cxx | 5 | ||||
-rw-r--r-- | src/db/SimpleDatabasePlugin.hxx | 7 |
21 files changed, 380 insertions, 238 deletions
diff --git a/src/DecoderThread.cxx b/src/DecoderThread.cxx index 21653830b..9ca478fae 100644 --- a/src/DecoderThread.cxx +++ b/src/DecoderThread.cxx @@ -26,6 +26,7 @@ #include "song.h" #include "mpd_error.h" #include "Mapper.hxx" +#include "Path.hxx" #include "decoder_api.h" #include "tag.h" #include "input_stream.h" @@ -431,7 +432,7 @@ decoder_run(struct decoder_control *dc) assert(song != NULL); if (song_is_file(song)) - uri = map_song_fs(song); + uri = map_song_fs(song).Steal(); else uri = song_get_uri(song); diff --git a/src/ExcludeList.cxx b/src/ExcludeList.cxx index 2c2dc952f..3f929b93c 100644 --- a/src/ExcludeList.cxx +++ b/src/ExcludeList.cxx @@ -32,14 +32,12 @@ #include <errno.h> bool -ExcludeList::LoadFile(const char *path_fs) +ExcludeList::LoadFile(const Path &path_fs) { - assert(path_fs != NULL); - - FILE *file = fopen(path_fs, "r"); + FILE *file = fopen(path_fs.c_str(), "r"); if (file == NULL) { if (errno != ENOENT) { - char *path_utf8 = fs_charset_to_utf8(path_fs); + char *path_utf8 = path_fs.ToUTF8(); g_debug("Failed to open %s: %s", path_utf8, g_strerror(errno)); g_free(path_utf8); diff --git a/src/ExcludeList.hxx b/src/ExcludeList.hxx index 4d678b085..f3dc1f057 100644 --- a/src/ExcludeList.hxx +++ b/src/ExcludeList.hxx @@ -31,6 +31,8 @@ #include <glib.h> +class Path; + class ExcludeList { class Pattern { GPatternSpec *pattern; @@ -65,7 +67,7 @@ public: /** * Loads and parses a .mpdignore file. */ - bool LoadFile(const char *path_fs); + bool LoadFile(const Path &path_fs); /** * Checks whether one of the patterns in the .mpdignore file matches diff --git a/src/Main.cxx b/src/Main.cxx index dd269354d..ef78c92b2 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -238,7 +238,8 @@ glue_state_file_init(GError **error_r) return true; } - state_file = new StateFile(path, *global_partition, *main_loop); + state_file = new StateFile(Path::FromUTF8(path), + *global_partition, *main_loop); g_free(path); state_file->Read(); return true; diff --git a/src/Mapper.cxx b/src/Mapper.cxx index 1a5ebe111..09fa190f3 100644 --- a/src/Mapper.cxx +++ b/src/Mapper.cxx @@ -156,67 +156,54 @@ map_to_relative_path(const char *path_utf8) : path_utf8; } -char * +Path map_uri_fs(const char *uri) { - char *uri_fs, *path_fs; - assert(uri != NULL); assert(*uri != '/'); if (music_dir_fs == NULL) - return NULL; + return Path::Null(); - uri_fs = utf8_to_fs_charset(uri); - if (uri_fs == NULL) - return NULL; - - path_fs = g_build_filename(music_dir_fs, uri_fs, NULL); - g_free(uri_fs); + const Path uri_fs = Path::FromUTF8(uri); + if (uri_fs.IsNull()) + return Path::Null(); - return path_fs; + return Path::Build(music_dir_fs, uri_fs); } -char * +Path map_directory_fs(const Directory *directory) { assert(music_dir_utf8 != NULL); assert(music_dir_fs != NULL); if (directory->IsRoot()) - return g_strdup(music_dir_fs); + return Path::FromFS(music_dir_fs); return map_uri_fs(directory->GetPath()); } -char * +Path map_directory_child_fs(const Directory *directory, const char *name) { assert(music_dir_utf8 != NULL); assert(music_dir_fs != NULL); - char *name_fs, *parent_fs, *path; - /* check for invalid or unauthorized base names */ if (*name == 0 || strchr(name, '/') != NULL || strcmp(name, ".") == 0 || strcmp(name, "..") == 0) - return NULL; + return Path::Null(); - parent_fs = map_directory_fs(directory); - if (parent_fs == NULL) - return NULL; + const Path parent_fs = map_directory_fs(directory); + if (parent_fs.IsNull()) + return Path::Null(); - name_fs = utf8_to_fs_charset(name); - if (name_fs == NULL) { - g_free(parent_fs); - return NULL; - } - - path = g_build_filename(parent_fs, name_fs, NULL); - g_free(parent_fs); - g_free(name_fs); + const Path name_fs = Path::FromUTF8(name); + if (name_fs.IsNull()) + return Path::Null(); - return path; + return Path::Build(parent_fs, name_fs); } /** @@ -224,19 +211,17 @@ map_directory_child_fs(const Directory *directory, const char *name) * not have a real parent directory, only the dummy object * #detached_root. */ -static char * +static Path map_detached_song_fs(const char *uri_utf8) { - char *uri_fs = utf8_to_fs_charset(uri_utf8); - if (uri_fs == NULL) - return NULL; + Path uri_fs = Path::FromUTF8(uri_utf8); + if (uri_fs.IsNull()) + return Path::Null(); - char *path = g_build_filename(music_dir_fs, uri_fs, NULL); - g_free(uri_fs); - return path; + return Path::Build(music_dir_fs, uri_fs); } -char * +Path map_song_fs(const struct song *song) { assert(song_is_file(song)); @@ -246,7 +231,7 @@ map_song_fs(const struct song *song) ? map_detached_song_fs(song->uri) : map_directory_child_fs(song->parent, song->uri); else - return utf8_to_fs_charset(song->uri); + return Path::FromUTF8(song->uri); } char * @@ -273,22 +258,17 @@ map_spl_path(void) return playlist_dir_fs; } -char * +Path map_spl_utf8_to_fs(const char *name) { - char *filename_utf8, *filename_fs, *path; - if (playlist_dir_fs == NULL) - return NULL; + return Path::Null(); - filename_utf8 = g_strconcat(name, PLAYLIST_FILE_SUFFIX, NULL); - filename_fs = utf8_to_fs_charset(filename_utf8); + char *filename_utf8 = g_strconcat(name, PLAYLIST_FILE_SUFFIX, NULL); + const Path filename_fs = Path::FromUTF8(filename_utf8); g_free(filename_utf8); - if (filename_fs == NULL) - return NULL; - - path = g_build_filename(playlist_dir_fs, filename_fs, NULL); - g_free(filename_fs); + if (filename_fs.IsNull()) + return Path::Null(); - return path; + return Path::Build(playlist_dir_fs, filename_fs); } diff --git a/src/Mapper.hxx b/src/Mapper.hxx index 2ced38a10..01c947b5a 100644 --- a/src/Mapper.hxx +++ b/src/Mapper.hxx @@ -29,6 +29,7 @@ #define PLAYLIST_FILE_SUFFIX ".m3u" +class Path; struct Directory; struct song; @@ -75,8 +76,8 @@ map_to_relative_path(const char *path_utf8); * is basically done by converting the URI to the file system charset * and prepending the music directory. */ -gcc_malloc -char * +gcc_pure +Path map_uri_fs(const char *uri); /** @@ -85,8 +86,8 @@ map_uri_fs(const char *uri); * @param directory the directory object * @return the path in file system encoding, or nullptr if mapping failed */ -gcc_malloc -char * +gcc_pure +Path map_directory_fs(const Directory *directory); /** @@ -97,8 +98,8 @@ map_directory_fs(const Directory *directory); * @param name the child's name in UTF-8 * @return the path in file system encoding, or nullptr if mapping failed */ -gcc_malloc -char * +gcc_pure +Path map_directory_child_fs(const Directory *directory, const char *name); /** @@ -108,8 +109,8 @@ map_directory_child_fs(const Directory *directory, const char *name); * @param song the song object * @return the path in file system encoding, or nullptr if mapping failed */ -gcc_malloc -char * +gcc_pure +Path map_song_fs(const struct song *song); /** @@ -138,7 +139,7 @@ map_spl_path(void); * @return the path in file system encoding, or nullptr if mapping failed */ gcc_pure -char * +Path map_spl_utf8_to_fs(const char *name); #endif diff --git a/src/Path.hxx b/src/Path.hxx index db3f95961..5c76e4b87 100644 --- a/src/Path.hxx +++ b/src/Path.hxx @@ -21,7 +21,14 @@ #define MPD_PATH_HXX #include "check.h" +#include "gcc.h" +#include <glib.h> + +#include <algorithm> + +#include <assert.h> +#include <string.h> #include <limits.h> #if !defined(MPD_PATH_MAX) @@ -54,4 +61,204 @@ utf8_to_fs_charset(const char *path_utf8); const char *path_get_fs_charset(); +/** + * A path name in the native file system character set. + */ +class Path { +public: + typedef char value_type; + typedef value_type *pointer; + typedef const value_type *const_pointer; + +private: + pointer value; + + struct Donate {}; + + /** + * Donate the allocated pointer to a new #Path object. + */ + constexpr Path(Donate, pointer _value):value(_value) {} + + /** + * Release memory allocated by the value, but do not clear the + * value pointer. + */ + void Free() { + /* free() can be optimized by gcc, while g_free() can + not: when the compiler knows that the value is + nullptr, it will not emit a free() call in the + inlined destructor; however on Windows, we need to + call g_free(), because the value has been allocated + by GLib, and on Windows, this matters */ +#ifdef WIN32 + g_free(value); +#else + free(value); +#endif + } + +public: + /** + * Copy a #Path object. + */ + Path(const Path &other) + :value(g_strdup(other.value)) {} + + /** + * Move a #Path object. + */ + Path(Path &&other):value(other.value) { + other.value = nullptr; + } + + ~Path() { + Free(); + } + + /** + * Return a "nulled" instance. Its IsNull() method will + * return true. Such an object must not be used. + * + * @see IsNull() + */ + gcc_const + static Path Null() { + return Path(Donate(), nullptr); + } + + /** + * Join two path components with the path separator. + */ + gcc_pure gcc_nonnull_all + static Path Build(const_pointer a, const_pointer b) { + return Path(Donate(), g_build_filename(a, b, nullptr)); + } + + gcc_pure gcc_nonnull_all + static Path Build(const_pointer a, const Path &b) { + return Build(a, b.c_str()); + } + + gcc_pure gcc_nonnull_all + static Path Build(const Path &a, const_pointer b) { + return Build(a.c_str(), b); + } + + gcc_pure + static Path Build(const Path &a, const Path &b) { + return Build(a.c_str(), b.c_str()); + } + + /** + * Convert a C string that is already in the filesystem + * character set to a #Path instance. + */ + gcc_pure + static Path FromFS(const_pointer fs) { + return Path(Donate(), g_strdup(fs)); + } + + /** + * Convert a UTF-8 C string to a #Path instance. + * + * TODO: return a "nulled" instance on error and add checks to + * all callers + */ + gcc_pure + static Path FromUTF8(const char *utf8) { + return Path(Donate(), utf8_to_fs_charset(utf8)); + } + + /** + * Copy a #Path object. + */ + Path &operator=(const Path &other) { + if (this != &other) { + Free(); + value = g_strdup(other.value); + } + + return *this; + } + + /** + * Move a #Path object. + */ + Path &operator=(Path &&other) { + std::swap(value, other.value); + return *this; + } + + /** + * Steal the allocated value. This object has an undefined + * value, and the caller is response for freeing this method's + * return value. + */ + pointer Steal() { + pointer result = value; + value = nullptr; + return result; + } + + /** + * Check if this is a "nulled" instance. A "nulled" instance + * must not be used. + */ + bool IsNull() const { + return value == nullptr; + } + + /** + * Clear this object's value, make it "nulled". + * + * @see IsNull() + */ + void SetNull() { + Free(); + value = nullptr; + } + + gcc_pure + bool empty() const { + assert(value != nullptr); + + return *value == 0; + } + + /** + * @return the length of this string in number of "value_type" + * elements (which may not be the number of characters). + */ + gcc_pure + size_t length() const { + assert(value != nullptr); + + return strlen(value); + } + + /** + * Returns the value as a const C string. The returned + * pointer is invalidated whenever the value of life of this + * instance ends. + */ + gcc_pure + const_pointer c_str() const { + assert(value != nullptr); + + return value; + } + + /** + * Convert the path to UTF-8. The caller is responsible for + * freeing the return value with g_free(). Returns nullptr on + * error. + */ + char *ToUTF8() const { + return value != nullptr + ? fs_charset_to_utf8(value) + : nullptr; + } +}; + #endif diff --git a/src/PlaylistFile.cxx b/src/PlaylistFile.cxx index 486c93994..ea81540cf 100644 --- a/src/PlaylistFile.cxx +++ b/src/PlaylistFile.cxx @@ -106,15 +106,15 @@ spl_check_name(const char *name_utf8, GError **error_r) return true; } -static char * +static Path spl_map_to_fs(const char *name_utf8, GError **error_r) { if (spl_map(error_r) == NULL || !spl_check_name(name_utf8, error_r)) - return NULL; + return Path::Null(); - char *path_fs = map_spl_utf8_to_fs(name_utf8); - if (path_fs == NULL) + Path path_fs = map_spl_utf8_to_fs(name_utf8); + if (path_fs.IsNull()) g_set_error_literal(error_r, playlist_quark(), PLAYLIST_RESULT_BAD_NAME, "Bad playlist name"); @@ -209,12 +209,11 @@ SavePlaylistFile(const PlaylistFileContents &contents, const char *utf8path, if (spl_map(error_r) == NULL) return false; - char *path_fs = spl_map_to_fs(utf8path, error_r); - if (path_fs == NULL) + const Path path_fs = spl_map_to_fs(utf8path, error_r); + if (path_fs.IsNull()) return false; - FILE *file = fopen(path_fs, "w"); - g_free(path_fs); + FILE *file = fopen(path_fs.c_str(), "w"); if (file == NULL) { playlist_errno(error_r); return false; @@ -235,8 +234,8 @@ LoadPlaylistFile(const char *utf8path, GError **error_r) if (spl_map(error_r) == NULL) return contents; - char *path_fs = spl_map_to_fs(utf8path, error_r); - if (path_fs == NULL) + const Path path_fs = spl_map_to_fs(utf8path, error_r); + if (path_fs.IsNull()) return contents; TextFile file(path_fs); @@ -308,17 +307,14 @@ spl_move_index(const char *utf8path, unsigned src, unsigned dest, bool spl_clear(const char *utf8path, GError **error_r) { - FILE *file; - if (spl_map(error_r) == NULL) return false; - char *path_fs = spl_map_to_fs(utf8path, error_r); - if (path_fs == NULL) + const Path path_fs = spl_map_to_fs(utf8path, error_r); + if (path_fs.IsNull()) return false; - file = fopen(path_fs, "w"); - g_free(path_fs); + FILE *file = fopen(path_fs.c_str(), "w"); if (file == NULL) { playlist_errno(error_r); return false; @@ -333,12 +329,11 @@ spl_clear(const char *utf8path, GError **error_r) bool spl_delete(const char *name_utf8, GError **error_r) { - char *path_fs = spl_map_to_fs(name_utf8, error_r); - if (path_fs == NULL) + const Path path_fs = spl_map_to_fs(name_utf8, error_r); + if (path_fs.IsNull()) return false; - int ret = unlink(path_fs); - g_free(path_fs); + int ret = unlink(path_fs.c_str()); if (ret < 0) { playlist_errno(error_r); return false; @@ -376,17 +371,14 @@ spl_remove_index(const char *utf8path, unsigned pos, GError **error_r) bool spl_append_song(const char *utf8path, struct song *song, GError **error_r) { - FILE *file; - if (spl_map(error_r) == NULL) return false; - char *path_fs = spl_map_to_fs(utf8path, error_r); - if (path_fs == NULL) + const Path path_fs = spl_map_to_fs(utf8path, error_r); + if (path_fs.IsNull()) return false; - file = fopen(path_fs, "a"); - g_free(path_fs); + FILE *file = fopen(path_fs.c_str(), "a"); if (file == NULL) { playlist_errno(error_r); return false; @@ -439,24 +431,24 @@ spl_append_uri(const char *url, const char *utf8file, GError **error_r) } static bool -spl_rename_internal(const char *from_path_fs, const char *to_path_fs, +spl_rename_internal(const Path &from_path_fs, const Path &to_path_fs, GError **error_r) { - if (!g_file_test(from_path_fs, G_FILE_TEST_IS_REGULAR)) { + if (!g_file_test(from_path_fs.c_str(), G_FILE_TEST_IS_REGULAR)) { g_set_error_literal(error_r, playlist_quark(), PLAYLIST_RESULT_NO_SUCH_LIST, "No such playlist"); return false; } - if (g_file_test(to_path_fs, G_FILE_TEST_EXISTS)) { + if (g_file_test(to_path_fs.c_str(), G_FILE_TEST_EXISTS)) { g_set_error_literal(error_r, playlist_quark(), PLAYLIST_RESULT_LIST_EXISTS, "Playlist exists already"); return false; } - if (rename(from_path_fs, to_path_fs) < 0) { + if (rename(from_path_fs.c_str(), to_path_fs.c_str()) < 0) { playlist_errno(error_r); return false; } @@ -471,20 +463,13 @@ spl_rename(const char *utf8from, const char *utf8to, GError **error_r) if (spl_map(error_r) == NULL) return false; - char *from_path_fs = spl_map_to_fs(utf8from, error_r); - if (from_path_fs == NULL) + Path from_path_fs = spl_map_to_fs(utf8from, error_r); + if (from_path_fs.IsNull()) return false; - char *to_path_fs = spl_map_to_fs(utf8to, error_r); - if (to_path_fs == NULL) { - g_free(from_path_fs); + Path to_path_fs = spl_map_to_fs(utf8to, error_r); + if (to_path_fs.IsNull()) return false; - } - - bool success = spl_rename_internal(from_path_fs, to_path_fs, error_r); - - g_free(from_path_fs); - g_free(to_path_fs); - return success; + return spl_rename_internal(from_path_fs, to_path_fs, error_r); } diff --git a/src/PlaylistMapper.cxx b/src/PlaylistMapper.cxx index 01b8f7dd8..e6b8ee439 100644 --- a/src/PlaylistMapper.cxx +++ b/src/PlaylistMapper.cxx @@ -21,6 +21,7 @@ #include "PlaylistMapper.hxx" #include "PlaylistFile.hxx" #include "Mapper.hxx" +#include "Path.hxx" extern "C" { #include "playlist_list.h" @@ -75,19 +76,13 @@ static struct playlist_provider * playlist_open_in_music_dir(const char *uri, GMutex *mutex, GCond *cond, struct input_stream **is_r) { - char *path_fs; - assert(uri_safe_local(uri)); - path_fs = map_uri_fs(uri); - if (path_fs == NULL) + Path path = map_uri_fs(uri); + if (path.IsNull()) return NULL; - struct playlist_provider *playlist = - playlist_open_path(path_fs, mutex, cond, is_r); - g_free(path_fs); - - return playlist; + return playlist_open_path(path.c_str(), mutex, cond, is_r); } struct playlist_provider * diff --git a/src/PlaylistSave.cxx b/src/PlaylistSave.cxx index 5f47d73c0..89feebbde 100644 --- a/src/PlaylistSave.cxx +++ b/src/PlaylistSave.cxx @@ -38,62 +38,47 @@ void playlist_print_song(FILE *file, const struct song *song) { if (playlist_saveAbsolutePaths && song_in_database(song)) { - char *path = map_song_fs(song); - if (path != NULL) { - fprintf(file, "%s\n", path); - g_free(path); - } + const Path path = map_song_fs(song); + if (!path.IsNull()) + fprintf(file, "%s\n", path.c_str()); } else { - char *uri = song_get_uri(song), *uri_fs; - - uri_fs = utf8_to_fs_charset(uri); + char *uri = song_get_uri(song); + const Path uri_fs = Path::FromUTF8(uri); g_free(uri); - fprintf(file, "%s\n", uri_fs); - g_free(uri_fs); + fprintf(file, "%s\n", uri_fs.c_str()); } } void playlist_print_uri(FILE *file, const char *uri) { - char *s; - - if (playlist_saveAbsolutePaths && !uri_has_scheme(uri) && - !g_path_is_absolute(uri)) - s = map_uri_fs(uri); - else - s = utf8_to_fs_charset(uri); + Path path = playlist_saveAbsolutePaths && !uri_has_scheme(uri) && + !g_path_is_absolute(uri) + ? map_uri_fs(uri) + : Path::FromUTF8(uri); - if (s != NULL) { - fprintf(file, "%s\n", s); - g_free(s); - } + if (!path.IsNull()) + fprintf(file, "%s\n", path.c_str()); } enum playlist_result spl_save_queue(const char *name_utf8, const struct queue *queue) { - char *path_fs; - FILE *file; - if (map_spl_path() == NULL) return PLAYLIST_RESULT_DISABLED; if (!spl_valid_name(name_utf8)) return PLAYLIST_RESULT_BAD_NAME; - path_fs = map_spl_utf8_to_fs(name_utf8); - if (path_fs == NULL) + const Path path_fs = map_spl_utf8_to_fs(name_utf8); + if (path_fs.IsNull()) return PLAYLIST_RESULT_BAD_NAME; - if (g_file_test(path_fs, G_FILE_TEST_EXISTS)) { - g_free(path_fs); + if (g_file_test(path_fs.c_str(), G_FILE_TEST_EXISTS)) return PLAYLIST_RESULT_LIST_EXISTS; - } - file = fopen(path_fs, "w"); - g_free(path_fs); + FILE *file = fopen(path_fs.c_str(), "w"); if (file == NULL) return PLAYLIST_RESULT_ERRNO; diff --git a/src/PlaylistSong.cxx b/src/PlaylistSong.cxx index ec27656f2..0d60c1413 100644 --- a/src/PlaylistSong.cxx +++ b/src/PlaylistSong.cxx @@ -65,8 +65,8 @@ apply_song_metadata(struct song *dest, const struct song *src) return dest; if (song_in_database(dest)) { - char *path_fs = map_song_fs(dest); - if (path_fs == NULL) + char *path_fs = map_song_fs(dest).Steal(); + if (path_fs == nullptr) return dest; char *path_utf8 = fs_charset_to_utf8(path_fs); diff --git a/src/SongUpdate.cxx b/src/SongUpdate.cxx index f84a37cea..ed7293f14 100644 --- a/src/SongUpdate.cxx +++ b/src/SongUpdate.cxx @@ -26,6 +26,7 @@ extern "C" { #include "Directory.hxx" #include "Mapper.hxx" +#include "Path.hxx" #include "tag.h" #include "input_stream.h" @@ -85,7 +86,6 @@ bool song_file_update(struct song *song) { const char *suffix; - char *path_fs; const struct decoder_plugin *plugin; struct stat st; struct input_stream *is = NULL; @@ -102,8 +102,8 @@ song_file_update(struct song *song) if (plugin == NULL) return false; - path_fs = map_song_fs(song); - if (path_fs == NULL) + const Path path_fs = map_song_fs(song); + if (path_fs.IsNull()) return false; if (song->tag != NULL) { @@ -111,8 +111,7 @@ song_file_update(struct song *song) song->tag = NULL; } - if (stat(path_fs, &st) < 0 || !S_ISREG(st.st_mode)) { - g_free(path_fs); + if (stat(path_fs.c_str(), &st) < 0 || !S_ISREG(st.st_mode)) { return false; } @@ -129,7 +128,7 @@ song_file_update(struct song *song) do { /* load file tag */ song->tag = tag_new(); - if (decoder_plugin_scan_file(plugin, path_fs, + if (decoder_plugin_scan_file(plugin, path_fs.c_str(), &full_tag_handler, song->tag)) break; @@ -143,7 +142,8 @@ song_file_update(struct song *song) if (is == NULL) { mutex = g_mutex_new(); cond = g_cond_new(); - is = input_stream_open(path_fs, mutex, cond, + is = input_stream_open(path_fs.c_str(), + mutex, cond, NULL); } @@ -174,9 +174,9 @@ song_file_update(struct song *song) } if (song->tag != NULL && tag_is_empty(song->tag)) - tag_scan_fallback(path_fs, &full_tag_handler, song->tag); + tag_scan_fallback(path_fs.c_str(), &full_tag_handler, + song->tag); - g_free(path_fs); return song->tag != NULL; } diff --git a/src/StateFile.cxx b/src/StateFile.cxx index b78056520..6f93137a7 100644 --- a/src/StateFile.cxx +++ b/src/StateFile.cxx @@ -34,8 +34,8 @@ #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "state_file" -StateFile::StateFile(const char *_path, Partition &_partition, EventLoop &_loop) - :TimeoutMonitor(_loop), path(_path), partition(_partition), +StateFile::StateFile(Path &&_path, Partition &_partition, EventLoop &_loop) + :TimeoutMonitor(_loop), path(std::move(_path)), partition(_partition), prev_volume_version(0), prev_output_version(0), prev_playlist_version(0) { @@ -73,7 +73,7 @@ StateFile::Read() g_debug("Loading state file %s", path.c_str()); - TextFile file(path.c_str()); + TextFile file(path); if (file.HasFailed()) { g_warning("failed to open %s: %s", path.c_str(), g_strerror(errno)); diff --git a/src/StateFile.hxx b/src/StateFile.hxx index 39c3fcdf6..72d71e105 100644 --- a/src/StateFile.hxx +++ b/src/StateFile.hxx @@ -21,6 +21,7 @@ #define MPD_STATE_FILE_HXX #include "event/TimeoutMonitor.hxx" +#include "Path.hxx" #include "gcc.h" #include <string> @@ -28,7 +29,7 @@ struct Partition; class StateFile final : private TimeoutMonitor { - std::string path; + Path path; Partition &partition; @@ -40,7 +41,7 @@ class StateFile final : private TimeoutMonitor { prev_playlist_version; public: - StateFile(const char *path, Partition &partition, EventLoop &loop); + StateFile(Path &&path, Partition &partition, EventLoop &loop); void Read(); void Write(); diff --git a/src/TextFile.hxx b/src/TextFile.hxx index 25f6ea7a8..b24889f61 100644 --- a/src/TextFile.hxx +++ b/src/TextFile.hxx @@ -21,6 +21,7 @@ #define MPD_TEXT_FILE_HXX #include "gcc.h" +#include "Path.hxx" #include <glib.h> @@ -35,8 +36,9 @@ class TextFile { GString *const buffer; public: - TextFile(const char *path_fs) - :file(fopen(path_fs, "r")), buffer(g_string_sized_new(step)) {} + TextFile(const Path &path_fs) + :file(fopen(path_fs.c_str(), "r")), + buffer(g_string_sized_new(step)) {} TextFile(const TextFile &other) = delete; diff --git a/src/UpdateArchive.cxx b/src/UpdateArchive.cxx index 72f7aaf19..c45e1b733 100644 --- a/src/UpdateArchive.cxx +++ b/src/UpdateArchive.cxx @@ -24,6 +24,7 @@ #include "Directory.hxx" #include "song.h" #include "Mapper.hxx" +#include "Path.hxx" extern "C" { #include "archive_list.h" @@ -96,20 +97,19 @@ update_archive_file2(Directory *parent, const char *name, changed since - don't consider updating it */ return; - char *path_fs = map_directory_child_fs(parent, name); + const Path path_fs = map_directory_child_fs(parent, name); /* open archive */ GError *error = NULL; - struct archive_file *file = archive_file_open(plugin, path_fs, &error); + struct archive_file *file = archive_file_open(plugin, path_fs.c_str(), + &error); if (file == NULL) { - g_free(path_fs); g_warning("%s", error->message); g_error_free(error); return; } - g_debug("archive %s opened", path_fs); - g_free(path_fs); + g_debug("archive %s opened", path_fs.c_str()); if (directory == NULL) { g_debug("creating archive directory: %s", name); diff --git a/src/UpdateContainer.cxx b/src/UpdateContainer.cxx index daa7f1ec4..d59fa96c0 100644 --- a/src/UpdateContainer.cxx +++ b/src/UpdateContainer.cxx @@ -26,6 +26,7 @@ #include "song.h" #include "decoder_plugin.h" #include "Mapper.hxx" +#include "Path.hxx" extern "C" { #include "tag_handler.h" @@ -84,22 +85,22 @@ update_container_file(Directory *directory, contdir->device = DEVICE_CONTAINER; db_unlock(); - char *const pathname = map_directory_child_fs(directory, name); + const Path pathname = map_directory_child_fs(directory, name); char *vtrack; unsigned int tnum = 0; - while ((vtrack = plugin->container_scan(pathname, ++tnum)) != NULL) { + while ((vtrack = plugin->container_scan(pathname.c_str(), ++tnum)) != NULL) { struct song *song = song_file_new(vtrack, contdir); // shouldn't be necessary but it's there.. song->mtime = st->st_mtime; - char *child_path_fs = map_directory_child_fs(contdir, vtrack); + const Path child_path_fs = + map_directory_child_fs(contdir, vtrack); song->tag = tag_new(); - decoder_plugin_scan_file(plugin, child_path_fs, + decoder_plugin_scan_file(plugin, child_path_fs.c_str(), &add_tag_handler, song->tag); - g_free(child_path_fs); db_lock(); contdir->AddSong(song); @@ -111,8 +112,6 @@ update_container_file(Directory *directory, g_free(vtrack); } - g_free(pathname); - if (tnum == 1) { db_lock(); delete_directory(contdir); diff --git a/src/UpdateIO.cxx b/src/UpdateIO.cxx index 2aee56514..cbf05b6fc 100644 --- a/src/UpdateIO.cxx +++ b/src/UpdateIO.cxx @@ -21,6 +21,7 @@ #include "UpdateIO.hxx" #include "Directory.hxx" #include "Mapper.hxx" +#include "Path.hxx" #include "glib_compat.h" #include <glib.h> @@ -31,15 +32,15 @@ int stat_directory(const Directory *directory, struct stat *st) { - char *path_fs = map_directory_fs(directory); - if (path_fs == NULL) + const Path path_fs = map_directory_fs(directory); + if (path_fs.IsNull()) return -1; - int ret = stat(path_fs, st); + int ret = stat(path_fs.c_str(), st); if (ret < 0) - g_warning("Failed to stat %s: %s", path_fs, g_strerror(errno)); + g_warning("Failed to stat %s: %s", + path_fs.c_str(), g_strerror(errno)); - g_free(path_fs); return ret; } @@ -47,23 +48,23 @@ int stat_directory_child(const Directory *parent, const char *name, struct stat *st) { - char *path_fs = map_directory_child_fs(parent, name); - if (path_fs == NULL) + const Path path_fs = map_directory_child_fs(parent, name); + if (path_fs.IsNull()) return -1; - int ret = stat(path_fs, st); + int ret = stat(path_fs.c_str(), st); if (ret < 0) - g_warning("Failed to stat %s: %s", path_fs, g_strerror(errno)); + g_warning("Failed to stat %s: %s", + path_fs.c_str(), g_strerror(errno)); - g_free(path_fs); return ret; } bool directory_exists(const Directory *directory) { - char *path_fs = map_directory_fs(directory); - if (path_fs == NULL) + const Path path_fs = map_directory_fs(directory); + if (path_fs.IsNull()) /* invalid path: cannot exist */ return false; @@ -72,25 +73,19 @@ directory_exists(const Directory *directory) ? G_FILE_TEST_IS_REGULAR : G_FILE_TEST_IS_DIR; - bool exists = g_file_test(path_fs, test); - g_free(path_fs); - - return exists; + return g_file_test(path_fs.c_str(), test); } bool directory_child_is_regular(const Directory *directory, const char *name_utf8) { - char *path_fs = map_directory_child_fs(directory, name_utf8); - if (path_fs == NULL) + const Path path_fs = map_directory_child_fs(directory, name_utf8); + if (path_fs.IsNull()) return false; struct stat st; - bool is_regular = stat(path_fs, &st) == 0 && S_ISREG(st.st_mode); - g_free(path_fs); - - return is_regular; + return stat(path_fs.c_str(), &st) == 0 && S_ISREG(st.st_mode); } bool @@ -104,14 +99,12 @@ directory_child_access(const Directory *directory, (void)mode; return true; #else - char *path = map_directory_child_fs(directory, name); - if (path == NULL) + const Path path = map_directory_child_fs(directory, name); + if (path.IsNull()) /* something went wrong, but that isn't a permission problem */ return true; - bool success = access(path, mode) == 0 || errno != EACCES; - g_free(path); - return success; + return access(path.c_str(), mode) == 0 || errno != EACCES; #endif } diff --git a/src/UpdateWalk.cxx b/src/UpdateWalk.cxx index 1716862b5..d0e9281a7 100644 --- a/src/UpdateWalk.cxx +++ b/src/UpdateWalk.cxx @@ -102,27 +102,23 @@ remove_excluded_from_directory(Directory *directory, Directory *child, *n; directory_for_each_child_safe(child, n, directory) { - char *name_fs = utf8_to_fs_charset(child->GetName()); + const Path name_fs = Path::FromUTF8(child->GetName()); - if (exclude_list.Check(name_fs)) { + if (exclude_list.Check(name_fs.c_str())) { delete_directory(child); modified = true; } - - g_free(name_fs); } struct song *song, *ns; directory_for_each_song_safe(song, ns, directory) { assert(song->parent == directory); - char *name_fs = utf8_to_fs_charset(song->uri); - if (exclude_list.Check(name_fs)) { + const Path name_fs = Path::FromUTF8(song->uri); + if (exclude_list.Check(name_fs.c_str())) { delete_song(directory, song); modified = true; } - - g_free(name_fs); } db_unlock(); @@ -145,18 +141,16 @@ purge_deleted_from_directory(Directory *directory) struct song *song, *ns; directory_for_each_song_safe(song, ns, directory) { - char *path; struct stat st; - if ((path = map_song_fs(song)) == NULL || - stat(path, &st) < 0 || !S_ISREG(st.st_mode)) { + const Path path = map_song_fs(song); + if (path.IsNull() || + stat(path.c_str(), &st) < 0 || !S_ISREG(st.st_mode)) { db_lock(); delete_song(directory, song); db_unlock(); modified = true; } - - g_free(path); } for (auto i = directory->playlists.begin(), @@ -283,13 +277,12 @@ static bool skip_symlink(const Directory *directory, const char *utf8_name) { #ifndef WIN32 - char *path_fs = map_directory_child_fs(directory, utf8_name); - if (path_fs == NULL) + const Path path_fs = map_directory_child_fs(directory, utf8_name); + if (path_fs.IsNull()) return true; char buffer[MPD_PATH_MAX]; - ssize_t length = readlink(path_fs, buffer, sizeof(buffer)); - g_free(path_fs); + ssize_t length = readlink(path_fs.c_str(), buffer, sizeof(buffer)); if (length < 0) /* don't skip if this is not a symlink */ return errno != EINVAL; @@ -359,24 +352,19 @@ update_directory(Directory *directory, const struct stat *st) directory_set_stat(directory, st); - char *path_fs = map_directory_fs(directory); - if (path_fs == NULL) + const Path path_fs = map_directory_fs(directory); + if (path_fs.IsNull()) return false; - DIR *dir = opendir(path_fs); + DIR *dir = opendir(path_fs.c_str()); if (!dir) { g_warning("Failed to open directory %s: %s", - path_fs, g_strerror(errno)); - g_free(path_fs); + path_fs.c_str(), g_strerror(errno)); return false; } - char *exclude_path_fs = g_build_filename(path_fs, ".mpdignore", NULL); ExcludeList exclude_list; - exclude_list.LoadFile(exclude_path_fs); - g_free(exclude_path_fs); - - g_free(path_fs); + exclude_list.LoadFile(Path::Build(path_fs, ".mpdignore")); if (!exclude_list.IsEmpty()) remove_excluded_from_directory(directory, exclude_list); diff --git a/src/db/SimpleDatabasePlugin.cxx b/src/db/SimpleDatabasePlugin.cxx index 8eea81e30..b7c60d9d8 100644 --- a/src/db/SimpleDatabasePlugin.cxx +++ b/src/db/SimpleDatabasePlugin.cxx @@ -68,7 +68,7 @@ SimpleDatabase::Configure(const struct config_param *param, GError **error_r) return false; } - path = _path; + path = Path::FromUTF8(_path); free(_path); return true; @@ -77,6 +77,7 @@ SimpleDatabase::Configure(const struct config_param *param, GError **error_r) bool SimpleDatabase::Check(GError **error_r) const { + assert(!path.IsNull()); assert(!path.empty()); /* Check if the file exists */ @@ -153,7 +154,7 @@ SimpleDatabase::Load(GError **error_r) assert(!path.empty()); assert(root != NULL); - TextFile file(path.c_str()); + TextFile file(path); if (file.HasFailed()) { g_set_error(error_r, simple_db_quark(), errno, "Failed to open database file \"%s\": %s", diff --git a/src/db/SimpleDatabasePlugin.hxx b/src/db/SimpleDatabasePlugin.hxx index 789dcdae9..3549aa98c 100644 --- a/src/db/SimpleDatabasePlugin.hxx +++ b/src/db/SimpleDatabasePlugin.hxx @@ -21,17 +21,17 @@ #define MPD_SIMPLE_DATABASE_PLUGIN_HXX #include "DatabasePlugin.hxx" +#include "Path.hxx" #include "gcc.h" #include <cassert> -#include <string> #include <time.h> struct Directory; class SimpleDatabase : public Database { - std::string path; + Path path; Directory *root; @@ -41,6 +41,9 @@ class SimpleDatabase : public Database { unsigned borrowed_song_count; #endif + SimpleDatabase() + :path(Path::Null()) {} + public: gcc_pure Directory *GetRoot() { |