Path: new class "Path" wraps filesystem path strings

This commit is contained in:
Max Kellermann 2013-01-17 00:56:57 +01:00
parent 8901514506
commit e5039c478a
21 changed files with 380 additions and 238 deletions

View File

@ -26,6 +26,7 @@
#include "song.h" #include "song.h"
#include "mpd_error.h" #include "mpd_error.h"
#include "Mapper.hxx" #include "Mapper.hxx"
#include "Path.hxx"
#include "decoder_api.h" #include "decoder_api.h"
#include "tag.h" #include "tag.h"
#include "input_stream.h" #include "input_stream.h"
@ -431,7 +432,7 @@ decoder_run(struct decoder_control *dc)
assert(song != NULL); assert(song != NULL);
if (song_is_file(song)) if (song_is_file(song))
uri = map_song_fs(song); uri = map_song_fs(song).Steal();
else else
uri = song_get_uri(song); uri = song_get_uri(song);

View File

@ -32,14 +32,12 @@
#include <errno.h> #include <errno.h>
bool bool
ExcludeList::LoadFile(const char *path_fs) ExcludeList::LoadFile(const Path &path_fs)
{ {
assert(path_fs != NULL); FILE *file = fopen(path_fs.c_str(), "r");
FILE *file = fopen(path_fs, "r");
if (file == NULL) { if (file == NULL) {
if (errno != ENOENT) { 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", g_debug("Failed to open %s: %s",
path_utf8, g_strerror(errno)); path_utf8, g_strerror(errno));
g_free(path_utf8); g_free(path_utf8);

View File

@ -31,6 +31,8 @@
#include <glib.h> #include <glib.h>
class Path;
class ExcludeList { class ExcludeList {
class Pattern { class Pattern {
GPatternSpec *pattern; GPatternSpec *pattern;
@ -65,7 +67,7 @@ public:
/** /**
* Loads and parses a .mpdignore file. * 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 * Checks whether one of the patterns in the .mpdignore file matches

View File

@ -238,7 +238,8 @@ glue_state_file_init(GError **error_r)
return true; 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); g_free(path);
state_file->Read(); state_file->Read();
return true; return true;

View File

@ -156,67 +156,54 @@ map_to_relative_path(const char *path_utf8)
: path_utf8; : path_utf8;
} }
char * Path
map_uri_fs(const char *uri) map_uri_fs(const char *uri)
{ {
char *uri_fs, *path_fs;
assert(uri != NULL); assert(uri != NULL);
assert(*uri != '/'); assert(*uri != '/');
if (music_dir_fs == NULL) if (music_dir_fs == NULL)
return NULL; return Path::Null();
uri_fs = utf8_to_fs_charset(uri); const Path uri_fs = Path::FromUTF8(uri);
if (uri_fs == NULL) if (uri_fs.IsNull())
return NULL; return Path::Null();
path_fs = g_build_filename(music_dir_fs, uri_fs, NULL); return Path::Build(music_dir_fs, uri_fs);
g_free(uri_fs);
return path_fs;
} }
char * Path
map_directory_fs(const Directory *directory) map_directory_fs(const Directory *directory)
{ {
assert(music_dir_utf8 != NULL); assert(music_dir_utf8 != NULL);
assert(music_dir_fs != NULL); assert(music_dir_fs != NULL);
if (directory->IsRoot()) if (directory->IsRoot())
return g_strdup(music_dir_fs); return Path::FromFS(music_dir_fs);
return map_uri_fs(directory->GetPath()); return map_uri_fs(directory->GetPath());
} }
char * Path
map_directory_child_fs(const Directory *directory, const char *name) map_directory_child_fs(const Directory *directory, const char *name)
{ {
assert(music_dir_utf8 != NULL); assert(music_dir_utf8 != NULL);
assert(music_dir_fs != NULL); assert(music_dir_fs != NULL);
char *name_fs, *parent_fs, *path;
/* check for invalid or unauthorized base names */ /* check for invalid or unauthorized base names */
if (*name == 0 || strchr(name, '/') != NULL || if (*name == 0 || strchr(name, '/') != NULL ||
strcmp(name, ".") == 0 || strcmp(name, "..") == 0) strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
return NULL; return Path::Null();
parent_fs = map_directory_fs(directory); const Path parent_fs = map_directory_fs(directory);
if (parent_fs == NULL) if (parent_fs.IsNull())
return NULL; return Path::Null();
name_fs = utf8_to_fs_charset(name); const Path name_fs = Path::FromUTF8(name);
if (name_fs == NULL) { if (name_fs.IsNull())
g_free(parent_fs); return Path::Null();
return NULL;
}
path = g_build_filename(parent_fs, name_fs, NULL); return Path::Build(parent_fs, name_fs);
g_free(parent_fs);
g_free(name_fs);
return path;
} }
/** /**
@ -224,19 +211,17 @@ map_directory_child_fs(const Directory *directory, const char *name)
* not have a real parent directory, only the dummy object * not have a real parent directory, only the dummy object
* #detached_root. * #detached_root.
*/ */
static char * static Path
map_detached_song_fs(const char *uri_utf8) map_detached_song_fs(const char *uri_utf8)
{ {
char *uri_fs = utf8_to_fs_charset(uri_utf8); Path uri_fs = Path::FromUTF8(uri_utf8);
if (uri_fs == NULL) if (uri_fs.IsNull())
return NULL; return Path::Null();
char *path = g_build_filename(music_dir_fs, uri_fs, NULL); return Path::Build(music_dir_fs, uri_fs);
g_free(uri_fs);
return path;
} }
char * Path
map_song_fs(const struct song *song) map_song_fs(const struct song *song)
{ {
assert(song_is_file(song)); assert(song_is_file(song));
@ -246,7 +231,7 @@ map_song_fs(const struct song *song)
? map_detached_song_fs(song->uri) ? map_detached_song_fs(song->uri)
: map_directory_child_fs(song->parent, song->uri); : map_directory_child_fs(song->parent, song->uri);
else else
return utf8_to_fs_charset(song->uri); return Path::FromUTF8(song->uri);
} }
char * char *
@ -273,22 +258,17 @@ map_spl_path(void)
return playlist_dir_fs; return playlist_dir_fs;
} }
char * Path
map_spl_utf8_to_fs(const char *name) map_spl_utf8_to_fs(const char *name)
{ {
char *filename_utf8, *filename_fs, *path;
if (playlist_dir_fs == NULL) if (playlist_dir_fs == NULL)
return NULL; return Path::Null();
filename_utf8 = g_strconcat(name, PLAYLIST_FILE_SUFFIX, NULL); char *filename_utf8 = g_strconcat(name, PLAYLIST_FILE_SUFFIX, NULL);
filename_fs = utf8_to_fs_charset(filename_utf8); const Path filename_fs = Path::FromUTF8(filename_utf8);
g_free(filename_utf8); g_free(filename_utf8);
if (filename_fs == NULL) if (filename_fs.IsNull())
return NULL; return Path::Null();
path = g_build_filename(playlist_dir_fs, filename_fs, NULL); return Path::Build(playlist_dir_fs, filename_fs);
g_free(filename_fs);
return path;
} }

View File

@ -29,6 +29,7 @@
#define PLAYLIST_FILE_SUFFIX ".m3u" #define PLAYLIST_FILE_SUFFIX ".m3u"
class Path;
struct Directory; struct Directory;
struct song; 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 * is basically done by converting the URI to the file system charset
* and prepending the music directory. * and prepending the music directory.
*/ */
gcc_malloc gcc_pure
char * Path
map_uri_fs(const char *uri); map_uri_fs(const char *uri);
/** /**
@ -85,8 +86,8 @@ map_uri_fs(const char *uri);
* @param directory the directory object * @param directory the directory object
* @return the path in file system encoding, or nullptr if mapping failed * @return the path in file system encoding, or nullptr if mapping failed
*/ */
gcc_malloc gcc_pure
char * Path
map_directory_fs(const Directory *directory); 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 * @param name the child's name in UTF-8
* @return the path in file system encoding, or nullptr if mapping failed * @return the path in file system encoding, or nullptr if mapping failed
*/ */
gcc_malloc gcc_pure
char * Path
map_directory_child_fs(const Directory *directory, const char *name); 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 * @param song the song object
* @return the path in file system encoding, or nullptr if mapping failed * @return the path in file system encoding, or nullptr if mapping failed
*/ */
gcc_malloc gcc_pure
char * Path
map_song_fs(const struct song *song); 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 * @return the path in file system encoding, or nullptr if mapping failed
*/ */
gcc_pure gcc_pure
char * Path
map_spl_utf8_to_fs(const char *name); map_spl_utf8_to_fs(const char *name);
#endif #endif

View File

@ -21,7 +21,14 @@
#define MPD_PATH_HXX #define MPD_PATH_HXX
#include "check.h" #include "check.h"
#include "gcc.h"
#include <glib.h>
#include <algorithm>
#include <assert.h>
#include <string.h>
#include <limits.h> #include <limits.h>
#if !defined(MPD_PATH_MAX) #if !defined(MPD_PATH_MAX)
@ -54,4 +61,204 @@ utf8_to_fs_charset(const char *path_utf8);
const char *path_get_fs_charset(); 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 #endif

View File

@ -106,15 +106,15 @@ spl_check_name(const char *name_utf8, GError **error_r)
return true; return true;
} }
static char * static Path
spl_map_to_fs(const char *name_utf8, GError **error_r) spl_map_to_fs(const char *name_utf8, GError **error_r)
{ {
if (spl_map(error_r) == NULL || if (spl_map(error_r) == NULL ||
!spl_check_name(name_utf8, error_r)) !spl_check_name(name_utf8, error_r))
return NULL; return Path::Null();
char *path_fs = map_spl_utf8_to_fs(name_utf8); Path path_fs = map_spl_utf8_to_fs(name_utf8);
if (path_fs == NULL) if (path_fs.IsNull())
g_set_error_literal(error_r, playlist_quark(), g_set_error_literal(error_r, playlist_quark(),
PLAYLIST_RESULT_BAD_NAME, PLAYLIST_RESULT_BAD_NAME,
"Bad playlist name"); "Bad playlist name");
@ -209,12 +209,11 @@ SavePlaylistFile(const PlaylistFileContents &contents, const char *utf8path,
if (spl_map(error_r) == NULL) if (spl_map(error_r) == NULL)
return false; return false;
char *path_fs = spl_map_to_fs(utf8path, error_r); const Path path_fs = spl_map_to_fs(utf8path, error_r);
if (path_fs == NULL) if (path_fs.IsNull())
return false; return false;
FILE *file = fopen(path_fs, "w"); FILE *file = fopen(path_fs.c_str(), "w");
g_free(path_fs);
if (file == NULL) { if (file == NULL) {
playlist_errno(error_r); playlist_errno(error_r);
return false; return false;
@ -235,8 +234,8 @@ LoadPlaylistFile(const char *utf8path, GError **error_r)
if (spl_map(error_r) == NULL) if (spl_map(error_r) == NULL)
return contents; return contents;
char *path_fs = spl_map_to_fs(utf8path, error_r); const Path path_fs = spl_map_to_fs(utf8path, error_r);
if (path_fs == NULL) if (path_fs.IsNull())
return contents; return contents;
TextFile file(path_fs); TextFile file(path_fs);
@ -308,17 +307,14 @@ spl_move_index(const char *utf8path, unsigned src, unsigned dest,
bool bool
spl_clear(const char *utf8path, GError **error_r) spl_clear(const char *utf8path, GError **error_r)
{ {
FILE *file;
if (spl_map(error_r) == NULL) if (spl_map(error_r) == NULL)
return false; return false;
char *path_fs = spl_map_to_fs(utf8path, error_r); const Path path_fs = spl_map_to_fs(utf8path, error_r);
if (path_fs == NULL) if (path_fs.IsNull())
return false; return false;
file = fopen(path_fs, "w"); FILE *file = fopen(path_fs.c_str(), "w");
g_free(path_fs);
if (file == NULL) { if (file == NULL) {
playlist_errno(error_r); playlist_errno(error_r);
return false; return false;
@ -333,12 +329,11 @@ spl_clear(const char *utf8path, GError **error_r)
bool bool
spl_delete(const char *name_utf8, GError **error_r) spl_delete(const char *name_utf8, GError **error_r)
{ {
char *path_fs = spl_map_to_fs(name_utf8, error_r); const Path path_fs = spl_map_to_fs(name_utf8, error_r);
if (path_fs == NULL) if (path_fs.IsNull())
return false; return false;
int ret = unlink(path_fs); int ret = unlink(path_fs.c_str());
g_free(path_fs);
if (ret < 0) { if (ret < 0) {
playlist_errno(error_r); playlist_errno(error_r);
return false; return false;
@ -376,17 +371,14 @@ spl_remove_index(const char *utf8path, unsigned pos, GError **error_r)
bool bool
spl_append_song(const char *utf8path, struct song *song, GError **error_r) spl_append_song(const char *utf8path, struct song *song, GError **error_r)
{ {
FILE *file;
if (spl_map(error_r) == NULL) if (spl_map(error_r) == NULL)
return false; return false;
char *path_fs = spl_map_to_fs(utf8path, error_r); const Path path_fs = spl_map_to_fs(utf8path, error_r);
if (path_fs == NULL) if (path_fs.IsNull())
return false; return false;
file = fopen(path_fs, "a"); FILE *file = fopen(path_fs.c_str(), "a");
g_free(path_fs);
if (file == NULL) { if (file == NULL) {
playlist_errno(error_r); playlist_errno(error_r);
return false; return false;
@ -439,24 +431,24 @@ spl_append_uri(const char *url, const char *utf8file, GError **error_r)
} }
static bool 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) 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(), g_set_error_literal(error_r, playlist_quark(),
PLAYLIST_RESULT_NO_SUCH_LIST, PLAYLIST_RESULT_NO_SUCH_LIST,
"No such playlist"); "No such playlist");
return false; 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(), g_set_error_literal(error_r, playlist_quark(),
PLAYLIST_RESULT_LIST_EXISTS, PLAYLIST_RESULT_LIST_EXISTS,
"Playlist exists already"); "Playlist exists already");
return false; 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); playlist_errno(error_r);
return false; return false;
} }
@ -471,20 +463,13 @@ spl_rename(const char *utf8from, const char *utf8to, GError **error_r)
if (spl_map(error_r) == NULL) if (spl_map(error_r) == NULL)
return false; return false;
char *from_path_fs = spl_map_to_fs(utf8from, error_r); Path from_path_fs = spl_map_to_fs(utf8from, error_r);
if (from_path_fs == NULL) if (from_path_fs.IsNull())
return false; return false;
char *to_path_fs = spl_map_to_fs(utf8to, error_r); Path to_path_fs = spl_map_to_fs(utf8to, error_r);
if (to_path_fs == NULL) { if (to_path_fs.IsNull())
g_free(from_path_fs);
return false; return false;
}
bool success = spl_rename_internal(from_path_fs, to_path_fs, error_r); return spl_rename_internal(from_path_fs, to_path_fs, error_r);
g_free(from_path_fs);
g_free(to_path_fs);
return success;
} }

View File

@ -21,6 +21,7 @@
#include "PlaylistMapper.hxx" #include "PlaylistMapper.hxx"
#include "PlaylistFile.hxx" #include "PlaylistFile.hxx"
#include "Mapper.hxx" #include "Mapper.hxx"
#include "Path.hxx"
extern "C" { extern "C" {
#include "playlist_list.h" #include "playlist_list.h"
@ -75,19 +76,13 @@ static struct playlist_provider *
playlist_open_in_music_dir(const char *uri, GMutex *mutex, GCond *cond, playlist_open_in_music_dir(const char *uri, GMutex *mutex, GCond *cond,
struct input_stream **is_r) struct input_stream **is_r)
{ {
char *path_fs;
assert(uri_safe_local(uri)); assert(uri_safe_local(uri));
path_fs = map_uri_fs(uri); Path path = map_uri_fs(uri);
if (path_fs == NULL) if (path.IsNull())
return NULL; return NULL;
struct playlist_provider *playlist = return playlist_open_path(path.c_str(), mutex, cond, is_r);
playlist_open_path(path_fs, mutex, cond, is_r);
g_free(path_fs);
return playlist;
} }
struct playlist_provider * struct playlist_provider *

View File

@ -38,62 +38,47 @@ void
playlist_print_song(FILE *file, const struct song *song) playlist_print_song(FILE *file, const struct song *song)
{ {
if (playlist_saveAbsolutePaths && song_in_database(song)) { if (playlist_saveAbsolutePaths && song_in_database(song)) {
char *path = map_song_fs(song); const Path path = map_song_fs(song);
if (path != NULL) { if (!path.IsNull())
fprintf(file, "%s\n", path); fprintf(file, "%s\n", path.c_str());
g_free(path);
}
} else { } else {
char *uri = song_get_uri(song), *uri_fs; char *uri = song_get_uri(song);
const Path uri_fs = Path::FromUTF8(uri);
uri_fs = utf8_to_fs_charset(uri);
g_free(uri); g_free(uri);
fprintf(file, "%s\n", uri_fs); fprintf(file, "%s\n", uri_fs.c_str());
g_free(uri_fs);
} }
} }
void void
playlist_print_uri(FILE *file, const char *uri) playlist_print_uri(FILE *file, const char *uri)
{ {
char *s; Path path = playlist_saveAbsolutePaths && !uri_has_scheme(uri) &&
!g_path_is_absolute(uri)
? map_uri_fs(uri)
: Path::FromUTF8(uri);
if (playlist_saveAbsolutePaths && !uri_has_scheme(uri) && if (!path.IsNull())
!g_path_is_absolute(uri)) fprintf(file, "%s\n", path.c_str());
s = map_uri_fs(uri);
else
s = utf8_to_fs_charset(uri);
if (s != NULL) {
fprintf(file, "%s\n", s);
g_free(s);
}
} }
enum playlist_result enum playlist_result
spl_save_queue(const char *name_utf8, const struct queue *queue) spl_save_queue(const char *name_utf8, const struct queue *queue)
{ {
char *path_fs;
FILE *file;
if (map_spl_path() == NULL) if (map_spl_path() == NULL)
return PLAYLIST_RESULT_DISABLED; return PLAYLIST_RESULT_DISABLED;
if (!spl_valid_name(name_utf8)) if (!spl_valid_name(name_utf8))
return PLAYLIST_RESULT_BAD_NAME; return PLAYLIST_RESULT_BAD_NAME;
path_fs = map_spl_utf8_to_fs(name_utf8); const Path path_fs = map_spl_utf8_to_fs(name_utf8);
if (path_fs == NULL) if (path_fs.IsNull())
return PLAYLIST_RESULT_BAD_NAME; return PLAYLIST_RESULT_BAD_NAME;
if (g_file_test(path_fs, G_FILE_TEST_EXISTS)) { if (g_file_test(path_fs.c_str(), G_FILE_TEST_EXISTS))
g_free(path_fs);
return PLAYLIST_RESULT_LIST_EXISTS; return PLAYLIST_RESULT_LIST_EXISTS;
}
file = fopen(path_fs, "w"); FILE *file = fopen(path_fs.c_str(), "w");
g_free(path_fs);
if (file == NULL) if (file == NULL)
return PLAYLIST_RESULT_ERRNO; return PLAYLIST_RESULT_ERRNO;

View File

@ -65,8 +65,8 @@ apply_song_metadata(struct song *dest, const struct song *src)
return dest; return dest;
if (song_in_database(dest)) { if (song_in_database(dest)) {
char *path_fs = map_song_fs(dest); char *path_fs = map_song_fs(dest).Steal();
if (path_fs == NULL) if (path_fs == nullptr)
return dest; return dest;
char *path_utf8 = fs_charset_to_utf8(path_fs); char *path_utf8 = fs_charset_to_utf8(path_fs);

View File

@ -26,6 +26,7 @@ extern "C" {
#include "Directory.hxx" #include "Directory.hxx"
#include "Mapper.hxx" #include "Mapper.hxx"
#include "Path.hxx"
#include "tag.h" #include "tag.h"
#include "input_stream.h" #include "input_stream.h"
@ -85,7 +86,6 @@ bool
song_file_update(struct song *song) song_file_update(struct song *song)
{ {
const char *suffix; const char *suffix;
char *path_fs;
const struct decoder_plugin *plugin; const struct decoder_plugin *plugin;
struct stat st; struct stat st;
struct input_stream *is = NULL; struct input_stream *is = NULL;
@ -102,8 +102,8 @@ song_file_update(struct song *song)
if (plugin == NULL) if (plugin == NULL)
return false; return false;
path_fs = map_song_fs(song); const Path path_fs = map_song_fs(song);
if (path_fs == NULL) if (path_fs.IsNull())
return false; return false;
if (song->tag != NULL) { if (song->tag != NULL) {
@ -111,8 +111,7 @@ song_file_update(struct song *song)
song->tag = NULL; song->tag = NULL;
} }
if (stat(path_fs, &st) < 0 || !S_ISREG(st.st_mode)) { if (stat(path_fs.c_str(), &st) < 0 || !S_ISREG(st.st_mode)) {
g_free(path_fs);
return false; return false;
} }
@ -129,7 +128,7 @@ song_file_update(struct song *song)
do { do {
/* load file tag */ /* load file tag */
song->tag = tag_new(); 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)) &full_tag_handler, song->tag))
break; break;
@ -143,7 +142,8 @@ song_file_update(struct song *song)
if (is == NULL) { if (is == NULL) {
mutex = g_mutex_new(); mutex = g_mutex_new();
cond = g_cond_new(); cond = g_cond_new();
is = input_stream_open(path_fs, mutex, cond, is = input_stream_open(path_fs.c_str(),
mutex, cond,
NULL); NULL);
} }
@ -174,9 +174,9 @@ song_file_update(struct song *song)
} }
if (song->tag != NULL && tag_is_empty(song->tag)) 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; return song->tag != NULL;
} }

View File

@ -34,8 +34,8 @@
#undef G_LOG_DOMAIN #undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "state_file" #define G_LOG_DOMAIN "state_file"
StateFile::StateFile(const char *_path, Partition &_partition, EventLoop &_loop) StateFile::StateFile(Path &&_path, Partition &_partition, EventLoop &_loop)
:TimeoutMonitor(_loop), path(_path), partition(_partition), :TimeoutMonitor(_loop), path(std::move(_path)), partition(_partition),
prev_volume_version(0), prev_output_version(0), prev_volume_version(0), prev_output_version(0),
prev_playlist_version(0) prev_playlist_version(0)
{ {
@ -73,7 +73,7 @@ StateFile::Read()
g_debug("Loading state file %s", path.c_str()); g_debug("Loading state file %s", path.c_str());
TextFile file(path.c_str()); TextFile file(path);
if (file.HasFailed()) { if (file.HasFailed()) {
g_warning("failed to open %s: %s", g_warning("failed to open %s: %s",
path.c_str(), g_strerror(errno)); path.c_str(), g_strerror(errno));

View File

@ -21,6 +21,7 @@
#define MPD_STATE_FILE_HXX #define MPD_STATE_FILE_HXX
#include "event/TimeoutMonitor.hxx" #include "event/TimeoutMonitor.hxx"
#include "Path.hxx"
#include "gcc.h" #include "gcc.h"
#include <string> #include <string>
@ -28,7 +29,7 @@
struct Partition; struct Partition;
class StateFile final : private TimeoutMonitor { class StateFile final : private TimeoutMonitor {
std::string path; Path path;
Partition &partition; Partition &partition;
@ -40,7 +41,7 @@ class StateFile final : private TimeoutMonitor {
prev_playlist_version; prev_playlist_version;
public: public:
StateFile(const char *path, Partition &partition, EventLoop &loop); StateFile(Path &&path, Partition &partition, EventLoop &loop);
void Read(); void Read();
void Write(); void Write();

View File

@ -21,6 +21,7 @@
#define MPD_TEXT_FILE_HXX #define MPD_TEXT_FILE_HXX
#include "gcc.h" #include "gcc.h"
#include "Path.hxx"
#include <glib.h> #include <glib.h>
@ -35,8 +36,9 @@ class TextFile {
GString *const buffer; GString *const buffer;
public: public:
TextFile(const char *path_fs) TextFile(const Path &path_fs)
:file(fopen(path_fs, "r")), buffer(g_string_sized_new(step)) {} :file(fopen(path_fs.c_str(), "r")),
buffer(g_string_sized_new(step)) {}
TextFile(const TextFile &other) = delete; TextFile(const TextFile &other) = delete;

View File

@ -24,6 +24,7 @@
#include "Directory.hxx" #include "Directory.hxx"
#include "song.h" #include "song.h"
#include "Mapper.hxx" #include "Mapper.hxx"
#include "Path.hxx"
extern "C" { extern "C" {
#include "archive_list.h" #include "archive_list.h"
@ -96,20 +97,19 @@ update_archive_file2(Directory *parent, const char *name,
changed since - don't consider updating it */ changed since - don't consider updating it */
return; return;
char *path_fs = map_directory_child_fs(parent, name); const Path path_fs = map_directory_child_fs(parent, name);
/* open archive */ /* open archive */
GError *error = NULL; 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) { if (file == NULL) {
g_free(path_fs);
g_warning("%s", error->message); g_warning("%s", error->message);
g_error_free(error); g_error_free(error);
return; return;
} }
g_debug("archive %s opened", path_fs); g_debug("archive %s opened", path_fs.c_str());
g_free(path_fs);
if (directory == NULL) { if (directory == NULL) {
g_debug("creating archive directory: %s", name); g_debug("creating archive directory: %s", name);

View File

@ -26,6 +26,7 @@
#include "song.h" #include "song.h"
#include "decoder_plugin.h" #include "decoder_plugin.h"
#include "Mapper.hxx" #include "Mapper.hxx"
#include "Path.hxx"
extern "C" { extern "C" {
#include "tag_handler.h" #include "tag_handler.h"
@ -84,22 +85,22 @@ update_container_file(Directory *directory,
contdir->device = DEVICE_CONTAINER; contdir->device = DEVICE_CONTAINER;
db_unlock(); db_unlock();
char *const pathname = map_directory_child_fs(directory, name); const Path pathname = map_directory_child_fs(directory, name);
char *vtrack; char *vtrack;
unsigned int tnum = 0; 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); struct song *song = song_file_new(vtrack, contdir);
// shouldn't be necessary but it's there.. // shouldn't be necessary but it's there..
song->mtime = st->st_mtime; 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(); 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); &add_tag_handler, song->tag);
g_free(child_path_fs);
db_lock(); db_lock();
contdir->AddSong(song); contdir->AddSong(song);
@ -111,8 +112,6 @@ update_container_file(Directory *directory,
g_free(vtrack); g_free(vtrack);
} }
g_free(pathname);
if (tnum == 1) { if (tnum == 1) {
db_lock(); db_lock();
delete_directory(contdir); delete_directory(contdir);

View File

@ -21,6 +21,7 @@
#include "UpdateIO.hxx" #include "UpdateIO.hxx"
#include "Directory.hxx" #include "Directory.hxx"
#include "Mapper.hxx" #include "Mapper.hxx"
#include "Path.hxx"
#include "glib_compat.h" #include "glib_compat.h"
#include <glib.h> #include <glib.h>
@ -31,15 +32,15 @@
int int
stat_directory(const Directory *directory, struct stat *st) stat_directory(const Directory *directory, struct stat *st)
{ {
char *path_fs = map_directory_fs(directory); const Path path_fs = map_directory_fs(directory);
if (path_fs == NULL) if (path_fs.IsNull())
return -1; return -1;
int ret = stat(path_fs, st); int ret = stat(path_fs.c_str(), st);
if (ret < 0) 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; return ret;
} }
@ -47,23 +48,23 @@ int
stat_directory_child(const Directory *parent, const char *name, stat_directory_child(const Directory *parent, const char *name,
struct stat *st) struct stat *st)
{ {
char *path_fs = map_directory_child_fs(parent, name); const Path path_fs = map_directory_child_fs(parent, name);
if (path_fs == NULL) if (path_fs.IsNull())
return -1; return -1;
int ret = stat(path_fs, st); int ret = stat(path_fs.c_str(), st);
if (ret < 0) 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; return ret;
} }
bool bool
directory_exists(const Directory *directory) directory_exists(const Directory *directory)
{ {
char *path_fs = map_directory_fs(directory); const Path path_fs = map_directory_fs(directory);
if (path_fs == NULL) if (path_fs.IsNull())
/* invalid path: cannot exist */ /* invalid path: cannot exist */
return false; return false;
@ -72,25 +73,19 @@ directory_exists(const Directory *directory)
? G_FILE_TEST_IS_REGULAR ? G_FILE_TEST_IS_REGULAR
: G_FILE_TEST_IS_DIR; : G_FILE_TEST_IS_DIR;
bool exists = g_file_test(path_fs, test); return g_file_test(path_fs.c_str(), test);
g_free(path_fs);
return exists;
} }
bool bool
directory_child_is_regular(const Directory *directory, directory_child_is_regular(const Directory *directory,
const char *name_utf8) const char *name_utf8)
{ {
char *path_fs = map_directory_child_fs(directory, name_utf8); const Path path_fs = map_directory_child_fs(directory, name_utf8);
if (path_fs == NULL) if (path_fs.IsNull())
return false; return false;
struct stat st; struct stat st;
bool is_regular = stat(path_fs, &st) == 0 && S_ISREG(st.st_mode); return stat(path_fs.c_str(), &st) == 0 && S_ISREG(st.st_mode);
g_free(path_fs);
return is_regular;
} }
bool bool
@ -104,14 +99,12 @@ directory_child_access(const Directory *directory,
(void)mode; (void)mode;
return true; return true;
#else #else
char *path = map_directory_child_fs(directory, name); const Path path = map_directory_child_fs(directory, name);
if (path == NULL) if (path.IsNull())
/* something went wrong, but that isn't a permission /* something went wrong, but that isn't a permission
problem */ problem */
return true; return true;
bool success = access(path, mode) == 0 || errno != EACCES; return access(path.c_str(), mode) == 0 || errno != EACCES;
g_free(path);
return success;
#endif #endif
} }

View File

@ -102,27 +102,23 @@ remove_excluded_from_directory(Directory *directory,
Directory *child, *n; Directory *child, *n;
directory_for_each_child_safe(child, n, directory) { 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); delete_directory(child);
modified = true; modified = true;
} }
g_free(name_fs);
} }
struct song *song, *ns; struct song *song, *ns;
directory_for_each_song_safe(song, ns, directory) { directory_for_each_song_safe(song, ns, directory) {
assert(song->parent == directory); assert(song->parent == directory);
char *name_fs = utf8_to_fs_charset(song->uri); const Path name_fs = Path::FromUTF8(song->uri);
if (exclude_list.Check(name_fs)) { if (exclude_list.Check(name_fs.c_str())) {
delete_song(directory, song); delete_song(directory, song);
modified = true; modified = true;
} }
g_free(name_fs);
} }
db_unlock(); db_unlock();
@ -145,18 +141,16 @@ purge_deleted_from_directory(Directory *directory)
struct song *song, *ns; struct song *song, *ns;
directory_for_each_song_safe(song, ns, directory) { directory_for_each_song_safe(song, ns, directory) {
char *path;
struct stat st; struct stat st;
if ((path = map_song_fs(song)) == NULL || const Path path = map_song_fs(song);
stat(path, &st) < 0 || !S_ISREG(st.st_mode)) { if (path.IsNull() ||
stat(path.c_str(), &st) < 0 || !S_ISREG(st.st_mode)) {
db_lock(); db_lock();
delete_song(directory, song); delete_song(directory, song);
db_unlock(); db_unlock();
modified = true; modified = true;
} }
g_free(path);
} }
for (auto i = directory->playlists.begin(), for (auto i = directory->playlists.begin(),
@ -283,13 +277,12 @@ static bool
skip_symlink(const Directory *directory, const char *utf8_name) skip_symlink(const Directory *directory, const char *utf8_name)
{ {
#ifndef WIN32 #ifndef WIN32
char *path_fs = map_directory_child_fs(directory, utf8_name); const Path path_fs = map_directory_child_fs(directory, utf8_name);
if (path_fs == NULL) if (path_fs.IsNull())
return true; return true;
char buffer[MPD_PATH_MAX]; char buffer[MPD_PATH_MAX];
ssize_t length = readlink(path_fs, buffer, sizeof(buffer)); ssize_t length = readlink(path_fs.c_str(), buffer, sizeof(buffer));
g_free(path_fs);
if (length < 0) if (length < 0)
/* don't skip if this is not a symlink */ /* don't skip if this is not a symlink */
return errno != EINVAL; return errno != EINVAL;
@ -359,24 +352,19 @@ update_directory(Directory *directory, const struct stat *st)
directory_set_stat(directory, st); directory_set_stat(directory, st);
char *path_fs = map_directory_fs(directory); const Path path_fs = map_directory_fs(directory);
if (path_fs == NULL) if (path_fs.IsNull())
return false; return false;
DIR *dir = opendir(path_fs); DIR *dir = opendir(path_fs.c_str());
if (!dir) { if (!dir) {
g_warning("Failed to open directory %s: %s", g_warning("Failed to open directory %s: %s",
path_fs, g_strerror(errno)); path_fs.c_str(), g_strerror(errno));
g_free(path_fs);
return false; return false;
} }
char *exclude_path_fs = g_build_filename(path_fs, ".mpdignore", NULL);
ExcludeList exclude_list; ExcludeList exclude_list;
exclude_list.LoadFile(exclude_path_fs); exclude_list.LoadFile(Path::Build(path_fs, ".mpdignore"));
g_free(exclude_path_fs);
g_free(path_fs);
if (!exclude_list.IsEmpty()) if (!exclude_list.IsEmpty())
remove_excluded_from_directory(directory, exclude_list); remove_excluded_from_directory(directory, exclude_list);

View File

@ -68,7 +68,7 @@ SimpleDatabase::Configure(const struct config_param *param, GError **error_r)
return false; return false;
} }
path = _path; path = Path::FromUTF8(_path);
free(_path); free(_path);
return true; return true;
@ -77,6 +77,7 @@ SimpleDatabase::Configure(const struct config_param *param, GError **error_r)
bool bool
SimpleDatabase::Check(GError **error_r) const SimpleDatabase::Check(GError **error_r) const
{ {
assert(!path.IsNull());
assert(!path.empty()); assert(!path.empty());
/* Check if the file exists */ /* Check if the file exists */
@ -153,7 +154,7 @@ SimpleDatabase::Load(GError **error_r)
assert(!path.empty()); assert(!path.empty());
assert(root != NULL); assert(root != NULL);
TextFile file(path.c_str()); TextFile file(path);
if (file.HasFailed()) { if (file.HasFailed()) {
g_set_error(error_r, simple_db_quark(), errno, g_set_error(error_r, simple_db_quark(), errno,
"Failed to open database file \"%s\": %s", "Failed to open database file \"%s\": %s",

View File

@ -21,17 +21,17 @@
#define MPD_SIMPLE_DATABASE_PLUGIN_HXX #define MPD_SIMPLE_DATABASE_PLUGIN_HXX
#include "DatabasePlugin.hxx" #include "DatabasePlugin.hxx"
#include "Path.hxx"
#include "gcc.h" #include "gcc.h"
#include <cassert> #include <cassert>
#include <string>
#include <time.h> #include <time.h>
struct Directory; struct Directory;
class SimpleDatabase : public Database { class SimpleDatabase : public Database {
std::string path; Path path;
Directory *root; Directory *root;
@ -41,6 +41,9 @@ class SimpleDatabase : public Database {
unsigned borrowed_song_count; unsigned borrowed_song_count;
#endif #endif
SimpleDatabase()
:path(Path::Null()) {}
public: public:
gcc_pure gcc_pure
Directory *GetRoot() { Directory *GetRoot() {