Commit e5039c47 authored by Max Kellermann's avatar Max Kellermann

Path: new class "Path" wraps filesystem path strings

parent 89015145
......@@ -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);
......
......@@ -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);
......
......@@ -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
......
......@@ -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;
......
......@@ -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);
}
......@@ -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
......@@ -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
......@@ -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);
}
......@@ -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 *
......
......@@ -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;
......
......@@ -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);
......
......@@ -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;
}
......
......@@ -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));
......
......@@ -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();
......
......@@ -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;
......
......@@ -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);
......
......@@ -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);
......
......@@ -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
}
......@@ -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);
......
......@@ -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",
......
......@@ -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() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment