aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/DecoderThread.cxx3
-rw-r--r--src/ExcludeList.cxx8
-rw-r--r--src/ExcludeList.hxx4
-rw-r--r--src/Main.cxx3
-rw-r--r--src/Mapper.cxx82
-rw-r--r--src/Mapper.hxx19
-rw-r--r--src/Path.hxx207
-rw-r--r--src/PlaylistFile.cxx69
-rw-r--r--src/PlaylistMapper.cxx13
-rw-r--r--src/PlaylistSave.cxx47
-rw-r--r--src/PlaylistSong.cxx4
-rw-r--r--src/SongUpdate.cxx18
-rw-r--r--src/StateFile.cxx6
-rw-r--r--src/StateFile.hxx5
-rw-r--r--src/TextFile.hxx6
-rw-r--r--src/UpdateArchive.cxx10
-rw-r--r--src/UpdateContainer.cxx13
-rw-r--r--src/UpdateIO.cxx47
-rw-r--r--src/UpdateWalk.cxx42
-rw-r--r--src/db/SimpleDatabasePlugin.cxx5
-rw-r--r--src/db/SimpleDatabasePlugin.hxx7
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() {