aboutsummaryrefslogtreecommitdiffstats
path: root/src/fs
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs')
-rw-r--r--src/fs/AllocatedPath.cxx15
-rw-r--r--src/fs/AllocatedPath.hxx42
-rw-r--r--src/fs/Charset.cxx29
-rw-r--r--src/fs/Config.cxx6
-rw-r--r--src/fs/FileSystem.hxx35
-rw-r--r--src/fs/Path.cxx4
-rw-r--r--src/fs/Path.hxx10
-rw-r--r--src/fs/StandardDirectory.cxx294
-rw-r--r--src/fs/StandardDirectory.hxx64
-rw-r--r--src/fs/TextFile.cxx77
-rw-r--r--src/fs/TextFile.hxx61
-rw-r--r--src/fs/Traits.cxx94
-rw-r--r--src/fs/Traits.hxx148
13 files changed, 784 insertions, 95 deletions
diff --git a/src/fs/AllocatedPath.cxx b/src/fs/AllocatedPath.cxx
index 37b79a685..0d0aaacbe 100644
--- a/src/fs/AllocatedPath.cxx
+++ b/src/fs/AllocatedPath.cxx
@@ -26,7 +26,6 @@
#include <glib.h>
-#include <assert.h>
#include <string.h>
inline AllocatedPath::AllocatedPath(Donate, pointer _value)
@@ -38,12 +37,6 @@ inline AllocatedPath::AllocatedPath(Donate, pointer _value)
AllocatedPath::~AllocatedPath() {}
AllocatedPath
-AllocatedPath::Build(const_pointer a, const_pointer b)
-{
- return AllocatedPath(Donate(), g_build_filename(a, b, nullptr));
-}
-
-AllocatedPath
AllocatedPath::FromUTF8(const char *path_utf8)
{
return AllocatedPath(Donate(), ::PathFromUTF8(path_utf8));
@@ -64,7 +57,7 @@ AllocatedPath::FromUTF8(const char *path_utf8, Error &error)
AllocatedPath
AllocatedPath::GetDirectoryName() const
{
- return AllocatedPath(Donate(), g_path_get_dirname(c_str()));
+ return FromFS(PathTraitsFS::GetParent(c_str()));
}
std::string
@@ -82,14 +75,14 @@ AllocatedPath::RelativeFS(const char *other_fs) const
other_fs += l;
if (*other_fs != 0) {
- if (!PathTraits::IsSeparatorFS(*other_fs))
+ if (!PathTraitsFS::IsSeparator(*other_fs))
/* mismatch */
return nullptr;
/* skip remaining path separators */
do {
++other_fs;
- } while (PathTraits::IsSeparatorFS(*other_fs));
+ } while (PathTraitsFS::IsSeparator(*other_fs));
}
return other_fs;
@@ -101,7 +94,7 @@ AllocatedPath::ChopSeparators()
size_t l = length();
const char *p = data();
- while (l >= 2 && PathTraits::IsSeparatorFS(p[l - 1])) {
+ while (l >= 2 && PathTraitsFS::IsSeparator(p[l - 1])) {
--l;
#if GCC_CHECK_VERSION(4,7) && !defined(__clang__)
diff --git a/src/fs/AllocatedPath.hxx b/src/fs/AllocatedPath.hxx
index 36d8a1598..d44953cd6 100644
--- a/src/fs/AllocatedPath.hxx
+++ b/src/fs/AllocatedPath.hxx
@@ -28,8 +28,6 @@
#include <utility>
#include <string>
-#include <assert.h>
-
class Error;
/**
@@ -39,11 +37,10 @@ class Error;
* stored.
*/
class AllocatedPath {
- typedef std::string string;
-
- typedef PathTraits::value_type value_type;
- typedef PathTraits::pointer pointer;
- typedef PathTraits::const_pointer const_pointer;
+ typedef PathTraitsFS::string string;
+ typedef PathTraitsFS::value_type value_type;
+ typedef PathTraitsFS::pointer pointer;
+ typedef PathTraitsFS::const_pointer const_pointer;
string value;
@@ -56,6 +53,12 @@ class AllocatedPath {
AllocatedPath(const_pointer _value):value(_value) {}
+ AllocatedPath(string &&_value):value(std::move(_value)) {}
+
+ static AllocatedPath Build(const_pointer a, size_t a_size,
+ const_pointer b, size_t b_size) {
+ return AllocatedPath(PathTraitsFS::Build(a, a_size, b, b_size));
+ }
public:
/**
* Copy a #AllocatedPath object.
@@ -89,22 +92,28 @@ public:
* Join two path components with the path separator.
*/
gcc_pure gcc_nonnull_all
- static AllocatedPath Build(const_pointer a, const_pointer b);
+ static AllocatedPath Build(const_pointer a, const_pointer b) {
+ return Build(a, PathTraitsFS::GetLength(a),
+ b, PathTraitsFS::GetLength(b));
+ }
gcc_pure gcc_nonnull_all
static AllocatedPath Build(const_pointer a, const AllocatedPath &b) {
- return Build(a, b.c_str());
+ return Build(a, PathTraitsFS::GetLength(a),
+ b.value.c_str(), b.value.size());
}
gcc_pure gcc_nonnull_all
static AllocatedPath Build(const AllocatedPath &a, const_pointer b) {
- return Build(a.c_str(), b);
+ return Build(a.value.c_str(), a.value.size(),
+ b, PathTraitsFS::GetLength(b));
}
gcc_pure
static AllocatedPath Build(const AllocatedPath &a,
const AllocatedPath &b) {
- return Build(a.c_str(), b.c_str());
+ return Build(a.value.c_str(), a.value.size(),
+ b.value.c_str(), b.value.size());
}
/**
@@ -117,6 +126,15 @@ public:
}
/**
+ * Convert a C++ string that is already in the filesystem
+ * character set to a #Path instance.
+ */
+ gcc_pure
+ static AllocatedPath FromFS(string &&fs) {
+ return AllocatedPath(std::move(fs));
+ }
+
+ /**
* Convert a UTF-8 C string to a #AllocatedPath instance.
* Returns return a "nulled" instance on error.
*/
@@ -215,7 +233,7 @@ public:
gcc_pure
bool IsAbsolute() {
- return PathTraits::IsAbsoluteFS(c_str());
+ return PathTraitsFS::IsAbsolute(c_str());
}
};
diff --git a/src/fs/Charset.cxx b/src/fs/Charset.cxx
index dad5779f9..dcd291a2d 100644
--- a/src/fs/Charset.cxx
+++ b/src/fs/Charset.cxx
@@ -22,12 +22,13 @@
#include "Domain.hxx"
#include "Limits.hxx"
#include "system/FatalError.hxx"
-#include "util/Error.hxx"
-#include "util/Domain.hxx"
#include "Log.hxx"
+#include "Traits.hxx"
#include <glib.h>
+#include <algorithm>
+
#include <assert.h>
#include <string.h>
@@ -76,13 +77,29 @@ GetFSCharset()
return fs_charset.empty() ? "utf-8" : fs_charset.c_str();
}
+static inline void FixSeparators(std::string &s)
+{
+#ifdef WIN32
+ // For whatever reason GCC can't convert constexpr to value reference.
+ // This leads to link errors when passing separators directly.
+ auto from = PathTraitsFS::SEPARATOR;
+ auto to = PathTraitsUTF8::SEPARATOR;
+ std::replace(s.begin(), s.end(), from, to);
+#else
+ (void)s;
+#endif
+}
+
std::string
PathToUTF8(const char *path_fs)
{
assert(path_fs != nullptr);
- if (fs_charset.empty())
- return std::string(path_fs);
+ if (fs_charset.empty()) {
+ auto result = std::string(path_fs);
+ FixSeparators(result);
+ return result;
+ }
GIConv conv = g_iconv_open("utf-8", fs_charset.c_str());
if (conv == reinterpret_cast<GIConv>(-1))
@@ -103,7 +120,9 @@ PathToUTF8(const char *path_fs)
if (ret == static_cast<size_t>(-1) || in_left > 0)
return std::string();
- return std::string(path_utf8, sizeof(path_utf8) - out_left);
+ auto result_path = std::string(path_utf8, sizeof(path_utf8) - out_left);
+ FixSeparators(result_path);
+ return result_path;
}
char *
diff --git a/src/fs/Config.cxx b/src/fs/Config.cxx
index 63e64ef99..87e77512d 100644
--- a/src/fs/Config.cxx
+++ b/src/fs/Config.cxx
@@ -20,16 +20,10 @@
#include "config.h"
#include "Config.hxx"
#include "Charset.hxx"
-#include "Domain.hxx"
#include "ConfigGlobal.hxx"
-#include "Log.hxx"
-#include "Compiler.h"
#include <glib.h>
-#include <assert.h>
-#include <string.h>
-
#ifdef WIN32
#include <windows.h> // for GetACP()
#include <stdio.h> // for sprintf()
diff --git a/src/fs/FileSystem.hxx b/src/fs/FileSystem.hxx
index cb2f82d22..e9751c73b 100644
--- a/src/fs/FileSystem.hxx
+++ b/src/fs/FileSystem.hxx
@@ -28,7 +28,6 @@
#include <sys/stat.h>
#include <unistd.h>
-#include <assert.h>
#include <stdio.h>
class AllocatedPath;
@@ -37,39 +36,39 @@ namespace FOpenMode {
/**
* Open mode for reading text files.
*/
- constexpr PathTraits::const_pointer ReadText = "r";
+ constexpr PathTraitsFS::const_pointer ReadText = "r";
/**
* Open mode for reading binary files.
*/
- constexpr PathTraits::const_pointer ReadBinary = "rb";
+ constexpr PathTraitsFS::const_pointer ReadBinary = "rb";
/**
* Open mode for writing text files.
*/
- constexpr PathTraits::const_pointer WriteText = "w";
+ constexpr PathTraitsFS::const_pointer WriteText = "w";
/**
* Open mode for writing binary files.
*/
- constexpr PathTraits::const_pointer WriteBinary = "wb";
+ constexpr PathTraitsFS::const_pointer WriteBinary = "wb";
/**
* Open mode for appending text files.
*/
- constexpr PathTraits::const_pointer AppendText = "a";
+ constexpr PathTraitsFS::const_pointer AppendText = "a";
/**
* Open mode for appending binary files.
*/
- constexpr PathTraits::const_pointer AppendBinary = "ab";
+ constexpr PathTraitsFS::const_pointer AppendBinary = "ab";
}
/**
* Wrapper for fopen() that uses #Path names.
*/
static inline FILE *
-FOpen(Path file, PathTraits::const_pointer mode)
+FOpen(Path file, PathTraitsFS::const_pointer mode)
{
return fopen(file.c_str(), mode);
}
@@ -132,20 +131,28 @@ MakeFifo(Path path, mode_t mode)
return mkfifo(path.c_str(), mode) == 0;
}
-#endif
-
/**
* Wrapper for access() that uses #Path names.
*/
static inline bool
CheckAccess(Path path, int mode)
{
+ return access(path.c_str(), mode) == 0;
+}
+
+#endif
+
+/**
+ * Checks is specified path exists and accessible.
+ */
+static inline bool
+CheckAccess(Path path)
+{
#ifdef WIN32
- (void)path;
- (void)mode;
- return true;
+ struct stat buf;
+ return StatFile(path, buf);
#else
- return access(path.c_str(), mode) == 0;
+ return CheckAccess(path, F_OK);
#endif
}
diff --git a/src/fs/Path.cxx b/src/fs/Path.cxx
index 0ff0591fb..4b7fb9319 100644
--- a/src/fs/Path.cxx
+++ b/src/fs/Path.cxx
@@ -36,14 +36,14 @@ Path::RelativeFS(const char *other_fs) const
other_fs += l;
if (*other_fs != 0) {
- if (!PathTraits::IsSeparatorFS(*other_fs))
+ if (!PathTraitsFS::IsSeparator(*other_fs))
/* mismatch */
return nullptr;
/* skip remaining path separators */
do {
++other_fs;
- } while (PathTraits::IsSeparatorFS(*other_fs));
+ } while (PathTraitsFS::IsSeparator(*other_fs));
}
return other_fs;
diff --git a/src/fs/Path.hxx b/src/fs/Path.hxx
index 6ea954577..807677f38 100644
--- a/src/fs/Path.hxx
+++ b/src/fs/Path.hxx
@@ -29,8 +29,6 @@
#include <assert.h>
#include <string.h>
-class Error;
-
/**
* A path name in the native file system character set.
*
@@ -38,9 +36,9 @@ class Error;
* instance lives, the string must not be invalidated.
*/
class Path {
- typedef PathTraits::value_type value_type;
- typedef PathTraits::pointer pointer;
- typedef PathTraits::const_pointer const_pointer;
+ typedef PathTraitsFS::value_type value_type;
+ typedef PathTraitsFS::pointer pointer;
+ typedef PathTraitsFS::const_pointer const_pointer;
const char *value;
@@ -141,7 +139,7 @@ public:
gcc_pure
bool IsAbsolute() {
- return PathTraits::IsAbsoluteFS(c_str());
+ return PathTraitsFS::IsAbsolute(c_str());
}
};
diff --git a/src/fs/StandardDirectory.cxx b/src/fs/StandardDirectory.cxx
new file mode 100644
index 000000000..889f6b3ea
--- /dev/null
+++ b/src/fs/StandardDirectory.cxx
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+// Use X Desktop guidelines where applicable
+#if !defined(__APPLE__) && !defined(WIN32)
+#define USE_XDG
+#endif
+
+#include "StandardDirectory.hxx"
+#include "FileSystem.hxx"
+
+#include <array>
+
+#ifdef WIN32
+#include <windows.h>
+#include <shlobj.h>
+#else
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <pwd.h>
+#endif
+
+#ifdef USE_XDG
+#include "util/CharUtil.hxx"
+#include "util/StringUtil.hxx"
+#include "TextFile.hxx"
+#include <string.h>
+#include <utility>
+#endif
+
+#ifndef WIN32
+class PasswdEntry
+{
+#if defined(HAVE_GETPWNAM_R) || defined(HAVE_GETPWUID_R)
+ std::array<char, 16 * 1024> buf;
+ passwd pw;
+#endif
+
+ passwd *result;
+public:
+ PasswdEntry() : result(nullptr) { }
+
+ bool ReadByName(const char *name) {
+#ifdef HAVE_GETPWNAM_R
+ getpwnam_r(name, &pw, buf.data(), buf.size(), &result);
+#else
+ result = getpwnam(name);
+#endif
+ return result != nullptr;
+ }
+
+ bool ReadByUid(uid_t uid) {
+#ifdef HAVE_GETPWUID_R
+ getpwuid_r(uid, &pw, buf.data(), buf.size(), &result);
+#else
+ result = getpwuid(uid);
+#endif
+ return result != nullptr;
+ }
+
+ const passwd *operator->() {
+ assert(result != nullptr);
+ return result;
+ }
+};
+#endif
+
+static inline bool IsValidPathString(PathTraitsFS::const_pointer path)
+{
+ return path != nullptr && *path != '\0';
+}
+
+static inline bool IsValidDir(PathTraitsFS::const_pointer dir)
+{
+ return PathTraitsFS::IsAbsolute(dir) &&
+ DirectoryExists(Path::FromFS(dir));
+}
+
+static inline AllocatedPath SafePathFromFS(PathTraitsFS::const_pointer dir)
+{
+ if (IsValidPathString(dir) && IsValidDir(dir))
+ return AllocatedPath::FromFS(dir);
+ return AllocatedPath::Null();
+}
+
+#ifdef WIN32
+static AllocatedPath GetStandardDir(int folder_id)
+{
+ std::array<char, MAX_PATH> dir;
+ auto ret = SHGetFolderPath(nullptr, folder_id | CSIDL_FLAG_DONT_VERIFY,
+ nullptr, SHGFP_TYPE_CURRENT, dir.data());
+ if (FAILED(ret))
+ return AllocatedPath::Null();
+ return SafePathFromFS(dir.data());
+}
+#endif
+
+#ifdef USE_XDG
+
+static const char home_prefix[] = "$HOME/";
+
+static bool ParseConfigLine(const char *line, const char *dir_name,
+ AllocatedPath &result_dir)
+{
+ // strip leading white space
+ line = strchug_fast(line);
+
+ // check for end-of-line or comment
+ if (*line == '\0' || *line == '#')
+ return false;
+
+ // check if current setting is for requested dir
+ if (!StringStartsWith(line, dir_name))
+ return false;
+ line += strlen(dir_name);
+
+ // strip equals sign and spaces around it
+ line = strchug_fast(line);
+ if (*line != '=')
+ return false;
+ ++line;
+ line = strchug_fast(line);
+
+ // check if path is quoted
+ bool quoted = false;
+ if (*line == '"') {
+ ++line;
+ quoted = true;
+ }
+
+ // check if path is relative to $HOME
+ bool home_relative = false;
+ if (StringStartsWith(line, home_prefix)) {
+ line += strlen(home_prefix);
+ home_relative = true;
+ }
+
+
+ const char *line_end;
+ // find end of the string
+ if (quoted) {
+ line_end = strrchr(line, '"');
+ if (line_end == nullptr)
+ return true;
+ } else {
+ line_end = line + strlen(line);
+ while (line < line_end && IsWhitespaceNotNull(line_end[-1]))
+ --line_end;
+ }
+
+ // check for empty result
+ if (line == line_end)
+ return true;
+
+ // build the result path
+ std::string path(line, line_end);
+
+ auto result = AllocatedPath::Null();
+ if (home_relative) {
+ auto home = GetHomeDir();
+ if (home.IsNull())
+ return true;
+ result = AllocatedPath::Build(home, path.c_str());
+ } else {
+ result = AllocatedPath::FromFS(std::move(path));
+ }
+
+ if (IsValidDir(result.c_str())) {
+ result_dir = std::move(result);
+ return true;
+ }
+ return true;
+}
+
+static AllocatedPath GetUserDir(const char *name)
+{
+ auto result = AllocatedPath::Null();
+ auto config_dir = GetUserConfigDir();
+ if (config_dir.IsNull())
+ return result;
+ auto dirs_file = AllocatedPath::Build(config_dir, "user-dirs.dirs");
+ TextFile input(dirs_file);
+ if (input.HasFailed())
+ return result;
+ const char *line;
+ while ((line = input.ReadLine()) != nullptr)
+ if (ParseConfigLine(line, name, result))
+ return result;
+ return result;
+}
+
+#endif
+
+AllocatedPath GetUserConfigDir()
+{
+#if defined(WIN32)
+ return GetStandardDir(CSIDL_LOCAL_APPDATA);
+#elif defined(USE_XDG)
+ // Check for $XDG_CONFIG_HOME
+ auto config_home = getenv("XDG_CONFIG_HOME");
+ if (IsValidPathString(config_home) && IsValidDir(config_home))
+ return AllocatedPath::FromFS(config_home);
+
+ // Check for $HOME/.config
+ auto home = GetHomeDir();
+ if (!home.IsNull()) {
+ AllocatedPath fallback = AllocatedPath::Build(home, ".config");
+ if (IsValidDir(fallback.c_str()))
+ return fallback;
+ }
+
+ return AllocatedPath::Null();
+#else
+ return AllocatedPath::Null();
+#endif
+}
+
+AllocatedPath GetUserMusicDir()
+{
+#if defined(WIN32)
+ return GetStandardDir(CSIDL_MYMUSIC);
+#elif defined(USE_XDG)
+ return GetUserDir("XDG_MUSIC_DIR");
+#else
+ return AllocatedPath::Null();
+#endif
+}
+
+#ifdef WIN32
+
+AllocatedPath GetSystemConfigDir()
+{
+ return GetStandardDir(CSIDL_COMMON_APPDATA);
+}
+
+AllocatedPath GetAppBaseDir()
+{
+ std::array<char, MAX_PATH> app;
+ auto ret = GetModuleFileName(nullptr, app.data(), app.size());
+
+ // Check for error
+ if (ret == 0)
+ return AllocatedPath::Null();
+
+ // Check for truncation
+ if (ret == app.size() && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ return AllocatedPath::Null();
+
+ auto app_path = AllocatedPath::FromFS(app.data());
+ return app_path.GetDirectoryName().GetDirectoryName();
+}
+
+#else
+
+AllocatedPath GetHomeDir()
+{
+ auto home = getenv("HOME");
+ if (IsValidPathString(home) && IsValidDir(home))
+ return AllocatedPath::FromFS(home);
+ PasswdEntry pw;
+ if (pw.ReadByUid(getuid()))
+ return SafePathFromFS(pw->pw_dir);
+ return AllocatedPath::Null();
+}
+
+AllocatedPath GetHomeDir(const char *user_name)
+{
+ assert(user_name != nullptr);
+ PasswdEntry pw;
+ if (pw.ReadByName(user_name))
+ return SafePathFromFS(pw->pw_dir);
+ return AllocatedPath::Null();
+}
+
+#endif
diff --git a/src/fs/StandardDirectory.hxx b/src/fs/StandardDirectory.hxx
new file mode 100644
index 000000000..cc5c5ec2a
--- /dev/null
+++ b/src/fs/StandardDirectory.hxx
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_FS_STANDARD_DIRECTORY_HXX
+#define MPD_FS_STANDARD_DIRECTORY_HXX
+
+#include "check.h"
+#include "AllocatedPath.hxx"
+
+/**
+ * Obtains configuration directory for the current user.
+ */
+AllocatedPath GetUserConfigDir();
+
+/**
+ * Obtains music directory for the current user.
+ */
+AllocatedPath GetUserMusicDir();
+
+#ifdef WIN32
+
+/**
+ * Obtains system configuration directory.
+ */
+AllocatedPath GetSystemConfigDir();
+
+/**
+ * Obtains application application base directory.
+ * Application base directory is a directory that contains 'bin' folder
+ * for current executable.
+ */
+AllocatedPath GetAppBaseDir();
+
+#else
+
+/**
+ * Obtains home directory for the current user.
+ */
+AllocatedPath GetHomeDir();
+
+/**
+ * Obtains home directory for the specified user.
+ */
+AllocatedPath GetHomeDir(const char *user_name);
+
+#endif
+
+#endif
diff --git a/src/fs/TextFile.cxx b/src/fs/TextFile.cxx
new file mode 100644
index 000000000..4a64ee963
--- /dev/null
+++ b/src/fs/TextFile.cxx
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "TextFile.hxx"
+#include "fs/Path.hxx"
+#include "fs/FileSystem.hxx"
+
+#include <glib.h>
+
+#include <assert.h>
+#include <string.h>
+
+TextFile::TextFile(Path path_fs)
+ :file(FOpen(path_fs, FOpenMode::ReadText)),
+ buffer(g_string_sized_new(step)) {}
+
+TextFile::~TextFile()
+{
+ if (file != nullptr)
+ fclose(file);
+
+ g_string_free(buffer, true);
+}
+
+char *
+TextFile::ReadLine()
+{
+ gsize length = 0, i;
+ char *p;
+
+ assert(file != nullptr);
+ assert(buffer != nullptr);
+ assert(buffer->allocated_len >= step);
+
+ while (buffer->len < max_length) {
+ p = fgets(buffer->str + length,
+ buffer->allocated_len - length, file);
+ if (p == nullptr) {
+ if (length == 0 || ferror(file))
+ return nullptr;
+ break;
+ }
+
+ i = strlen(buffer->str + length);
+ length += i;
+ if (i < step - 1 || buffer->str[length - 1] == '\n')
+ break;
+
+ g_string_set_size(buffer, length + step);
+ }
+
+ /* remove the newline characters */
+ if (buffer->str[length - 1] == '\n')
+ --length;
+ if (buffer->str[length - 1] == '\r')
+ --length;
+
+ g_string_set_size(buffer, length);
+ return buffer->str;
+}
diff --git a/src/fs/TextFile.hxx b/src/fs/TextFile.hxx
new file mode 100644
index 000000000..9d8608711
--- /dev/null
+++ b/src/fs/TextFile.hxx
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_TEXT_FILE_HXX
+#define MPD_TEXT_FILE_HXX
+
+#include "Compiler.h"
+
+#include <stdio.h>
+
+class Path;
+typedef struct _GString GString;
+
+class TextFile {
+ static constexpr size_t max_length = 512 * 1024;
+ static constexpr size_t step = 1024;
+
+ FILE *const file;
+
+ GString *const buffer;
+
+public:
+ TextFile(Path path_fs);
+
+ TextFile(const TextFile &other) = delete;
+
+ ~TextFile();
+
+ bool HasFailed() const {
+ return gcc_unlikely(file == nullptr);
+ }
+
+ /**
+ * Reads a line from the input file, and strips trailing
+ * space. There is a reasonable maximum line length, only to
+ * prevent denial of service.
+ *
+ * @param file the source file, opened in text mode
+ * @param buffer an allocator for the buffer
+ * @return a pointer to the line, or nullptr on end-of-file or error
+ */
+ char *ReadLine();
+};
+
+#endif
diff --git a/src/fs/Traits.cxx b/src/fs/Traits.cxx
index 2c3ce075b..3e874a224 100644
--- a/src/fs/Traits.cxx
+++ b/src/fs/Traits.cxx
@@ -22,24 +22,96 @@
#include <string.h>
-const char *
-PathTraits::GetBaseUTF8(const char *p)
+template<typename Traits>
+typename Traits::string
+BuildPathImpl(typename Traits::const_pointer a, size_t a_size,
+ typename Traits::const_pointer b, size_t b_size)
+{
+ assert(a != nullptr);
+ assert(b != nullptr);
+
+ if (a_size == 0)
+ return typename Traits::string(b, b_size);
+ if (b_size == 0)
+ return typename Traits::string(a, a_size);
+
+ typename Traits::string result(a, a_size);
+
+ if (!Traits::IsSeparator(a[a_size - 1]))
+ result.push_back(Traits::SEPARATOR);
+
+ if (Traits::IsSeparator(b[0]))
+ result.append(b + 1, b_size - 1);
+ else
+ result.append(b, b_size);
+
+ return result;
+}
+
+template<typename Traits>
+typename Traits::const_pointer
+GetBasePathImpl(typename Traits::const_pointer p)
{
assert(p != nullptr);
- const char *slash = strrchr(p, SEPARATOR_UTF8);
- return slash != nullptr
- ? slash + 1
+ typename Traits::const_pointer sep = Traits::FindLastSeparator(p);
+ return sep != nullptr
+ ? sep + 1
: p;
}
-std::string
-PathTraits::GetParentUTF8(const char *p)
+template<typename Traits>
+typename Traits::string
+GetParentPathImpl(typename Traits::const_pointer p)
{
assert(p != nullptr);
- const char *slash = strrchr(p, SEPARATOR_UTF8);
- return slash != nullptr
- ? std::string(p, slash)
- : std::string(".");
+ typename Traits::const_pointer sep = Traits::FindLastSeparator(p);
+ if (sep == nullptr)
+ return typename Traits::string(".");
+ if (sep == p)
+ return typename Traits::string(p, p + 1);
+#ifdef WIN32
+ if (Traits::IsDrive(p) && sep == p + 2)
+ return typename Traits::string(p, p + 3);
+#endif
+ return typename Traits::string(p, sep);
+}
+
+PathTraitsFS::string
+PathTraitsFS::Build(PathTraitsFS::const_pointer a, size_t a_size,
+ PathTraitsFS::const_pointer b, size_t b_size)
+{
+ return BuildPathImpl<PathTraitsFS>(a, a_size, b, b_size);
+}
+
+PathTraitsFS::const_pointer
+PathTraitsFS::GetBase(PathTraitsFS::const_pointer p)
+{
+ return GetBasePathImpl<PathTraitsFS>(p);
+}
+
+PathTraitsFS::string
+PathTraitsFS::GetParent(PathTraitsFS::const_pointer p)
+{
+ return GetParentPathImpl<PathTraitsFS>(p);
+}
+
+PathTraitsUTF8::string
+PathTraitsUTF8::Build(PathTraitsUTF8::const_pointer a, size_t a_size,
+ PathTraitsUTF8::const_pointer b, size_t b_size)
+{
+ return BuildPathImpl<PathTraitsUTF8>(a, a_size, b, b_size);
+}
+
+PathTraitsUTF8::const_pointer
+PathTraitsUTF8::GetBase(PathTraitsUTF8::const_pointer p)
+{
+ return GetBasePathImpl<PathTraitsUTF8>(p);
+}
+
+PathTraitsUTF8::string
+PathTraitsUTF8::GetParent(PathTraitsUTF8::const_pointer p)
+{
+ return GetParentPathImpl<PathTraitsUTF8>(p);
}
diff --git a/src/fs/Traits.hxx b/src/fs/Traits.hxx
index 244ab8b5c..927496e53 100644
--- a/src/fs/Traits.hxx
+++ b/src/fs/Traits.hxx
@@ -24,67 +24,144 @@
#include "Compiler.h"
#ifdef WIN32
-#include <glib.h>
+#include "util/CharUtil.hxx"
#endif
#include <string>
+#include <string.h>
#include <assert.h>
-class Error;
-
/**
- * This class describes the nature of a filesystem path.
+ * This class describes the nature of a native filesystem path.
*/
-struct PathTraits {
+struct PathTraitsFS {
+ typedef std::string string;
typedef char value_type;
typedef char *pointer;
typedef const char *const_pointer;
#ifdef WIN32
- static constexpr value_type SEPARATOR_FS = '\\';
- static constexpr char SEPARATOR_UTF8 = '/';
+ static constexpr value_type SEPARATOR = '\\';
#else
- static constexpr value_type SEPARATOR_FS = '/';
- static constexpr char SEPARATOR_UTF8 = '/';
+ static constexpr value_type SEPARATOR = '/';
#endif
- static constexpr bool IsSeparatorFS(value_type ch) {
+ static constexpr bool IsSeparator(value_type ch) {
return
#ifdef WIN32
ch == '/' ||
#endif
- ch == SEPARATOR_FS;
+ ch == SEPARATOR;
}
- static constexpr bool IsSeparatorUTF8(char ch) {
- return
+ gcc_pure gcc_nonnull_all
+ static const_pointer FindLastSeparator(const_pointer p) {
+ assert(p != nullptr);
#ifdef WIN32
- ch == '/' ||
+ const_pointer pos = p + GetLength(p);
+ while (p != pos && !IsSeparator(*pos))
+ --pos;
+ return IsSeparator(*pos) ? pos : nullptr;
+#else
+ return strrchr(p, SEPARATOR);
#endif
- ch == SEPARATOR_UTF8;
}
- gcc_pure
- static bool IsAbsoluteFS(const_pointer p) {
- assert(p != nullptr);
+#ifdef WIN32
+ gcc_pure gcc_nonnull_all
+ static constexpr bool IsDrive(const_pointer p) {
+ return IsAlphaASCII(p[0]) && p[1] == ':';
+ }
+#endif
+ gcc_pure gcc_nonnull_all
+ static bool IsAbsolute(const_pointer p) {
+ assert(p != nullptr);
#ifdef WIN32
- return g_path_is_absolute(p);
-#else
- return IsSeparatorFS(*p);
+ if (IsDrive(p) && IsSeparator(p[2]))
+ return true;
#endif
+ return IsSeparator(*p);
+ }
+
+ gcc_pure gcc_nonnull_all
+ static size_t GetLength(const_pointer p) {
+ return strlen(p);
}
- gcc_pure
- static bool IsAbsoluteUTF8(const char *p) {
+ /**
+ * Determine the "base" file name of the given native path.
+ * The return value points inside the given string.
+ */
+ gcc_pure gcc_nonnull_all
+ static const_pointer GetBase(const_pointer p);
+
+ /**
+ * Determine the "parent" file name of the given native path.
+ * As a special case, returns the string "." if there is no
+ * separator in the given input string.
+ */
+ gcc_pure gcc_nonnull_all
+ static string GetParent(const_pointer p);
+
+ /**
+ * Constructs the path from the given components.
+ * If either of the components is empty string,
+ * remaining component is returned unchanged.
+ * If both components are empty strings, empty string is returned.
+ */
+ gcc_pure gcc_nonnull_all
+ static string Build(const_pointer a, size_t a_size,
+ const_pointer b, size_t b_size);
+
+ gcc_pure gcc_nonnull_all
+ static string Build(const_pointer a, const_pointer b) {
+ return Build(a, GetLength(a), b, GetLength(b));
+ }
+};
+
+/**
+ * This class describes the nature of a MPD internal filesystem path.
+ */
+struct PathTraitsUTF8 {
+ typedef std::string string;
+ typedef char value_type;
+ typedef char *pointer;
+ typedef const char *const_pointer;
+
+ static constexpr value_type SEPARATOR = '/';
+
+ static constexpr bool IsSeparator(value_type ch) {
+ return ch == SEPARATOR;
+ }
+
+ gcc_pure gcc_nonnull_all
+ static const_pointer FindLastSeparator(const_pointer p) {
assert(p != nullptr);
+ return strrchr(p, SEPARATOR);
+ }
#ifdef WIN32
- return g_path_is_absolute(p);
-#else
- return IsSeparatorUTF8(*p);
+ gcc_pure gcc_nonnull_all
+ static constexpr bool IsDrive(const_pointer p) {
+ return IsAlphaASCII(p[0]) && p[1] == ':';
+ }
+#endif
+
+ gcc_pure gcc_nonnull_all
+ static bool IsAbsolute(const_pointer p) {
+ assert(p != nullptr);
+#ifdef WIN32
+ if (IsDrive(p) && IsSeparator(p[2]))
+ return true;
#endif
+ return IsSeparator(*p);
+ }
+
+ gcc_pure gcc_nonnull_all
+ static size_t GetLength(const_pointer p) {
+ return strlen(p);
}
/**
@@ -92,7 +169,7 @@ struct PathTraits {
* The return value points inside the given string.
*/
gcc_pure gcc_nonnull_all
- static const char *GetBaseUTF8(const char *p);
+ static const_pointer GetBase(const_pointer p);
/**
* Determine the "parent" file name of the given UTF-8 path.
@@ -100,7 +177,22 @@ struct PathTraits {
* separator in the given input string.
*/
gcc_pure gcc_nonnull_all
- static std::string GetParentUTF8(const char *p);
+ static string GetParent(const_pointer p);
+
+ /**
+ * Constructs the path from the given components.
+ * If either of the components is empty string,
+ * remaining component is returned unchanged.
+ * If both components are empty strings, empty string is returned.
+ */
+ gcc_pure gcc_nonnull_all
+ static string Build(const_pointer a, size_t a_size,
+ const_pointer b, size_t b_size);
+
+ gcc_pure gcc_nonnull_all
+ static string Build(const_pointer a, const_pointer b) {
+ return Build(a, GetLength(a), b, GetLength(b));
+ }
};
#endif