diff options
Diffstat (limited to 'src/fs')
-rw-r--r-- | src/fs/AllocatedPath.cxx | 27 | ||||
-rw-r--r-- | src/fs/AllocatedPath.hxx | 56 | ||||
-rw-r--r-- | src/fs/Charset.cxx | 48 | ||||
-rw-r--r-- | src/fs/Charset.hxx | 2 | ||||
-rw-r--r-- | src/fs/CheckFile.cxx | 62 | ||||
-rw-r--r-- | src/fs/CheckFile.hxx | 34 | ||||
-rw-r--r-- | src/fs/Config.cxx | 16 | ||||
-rw-r--r-- | src/fs/Config.hxx | 2 | ||||
-rw-r--r-- | src/fs/DirectoryReader.hxx | 8 | ||||
-rw-r--r-- | src/fs/Domain.cxx | 2 | ||||
-rw-r--r-- | src/fs/Domain.hxx | 2 | ||||
-rw-r--r-- | src/fs/FileSystem.cxx | 2 | ||||
-rw-r--r-- | src/fs/FileSystem.hxx | 37 | ||||
-rw-r--r-- | src/fs/Limits.hxx | 2 | ||||
-rw-r--r-- | src/fs/Path.cxx | 24 | ||||
-rw-r--r-- | src/fs/Path.hxx | 16 | ||||
-rw-r--r-- | src/fs/StandardDirectory.cxx | 294 | ||||
-rw-r--r-- | src/fs/StandardDirectory.hxx | 64 | ||||
-rw-r--r-- | src/fs/TextFile.cxx | 81 | ||||
-rw-r--r-- | src/fs/TextFile.hxx | 62 | ||||
-rw-r--r-- | src/fs/Traits.cxx | 136 | ||||
-rw-r--r-- | src/fs/Traits.hxx | 168 |
22 files changed, 1007 insertions, 138 deletions
diff --git a/src/fs/AllocatedPath.cxx b/src/fs/AllocatedPath.cxx index 37b79a685..30ce7e3a9 100644 --- a/src/fs/AllocatedPath.cxx +++ b/src/fs/AllocatedPath.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,29 +24,32 @@ #include "util/Error.hxx" #include "Compiler.h" +#ifdef HAVE_GLIB #include <glib.h> +#endif -#include <assert.h> #include <string.h> +#ifdef HAVE_GLIB + inline AllocatedPath::AllocatedPath(Donate, pointer _value) :value(_value) { g_free(_value); } +#endif + /* no inlining, please */ 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) { +#ifdef HAVE_GLIB return AllocatedPath(Donate(), ::PathFromUTF8(path_utf8)); +#else + return FromFS(path_utf8); +#endif } AllocatedPath @@ -64,7 +67,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 +85,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 +104,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..4fb217547 100644 --- a/src/fs/AllocatedPath.hxx +++ b/src/fs/AllocatedPath.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -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. @@ -67,6 +70,8 @@ public: */ AllocatedPath(AllocatedPath &&other):value(std::move(other.value)) {} + explicit AllocatedPath(Path other):value(other.c_str()) {} + ~AllocatedPath(); /** @@ -89,22 +94,38 @@ 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) { + static AllocatedPath Build(Path a, const_pointer b) { + return Build(a.c_str(), b); + } + + gcc_pure gcc_nonnull_all + static AllocatedPath Build(Path a, Path b) { return Build(a, b.c_str()); } gcc_pure gcc_nonnull_all + static AllocatedPath Build(const_pointer a, const AllocatedPath &b) { + 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 +138,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 +245,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..232a9a23c 100644 --- a/src/fs/Charset.cxx +++ b/src/fs/Charset.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,11 +22,14 @@ #include "Domain.hxx" #include "Limits.hxx" #include "system/FatalError.hxx" -#include "util/Error.hxx" -#include "util/Domain.hxx" #include "Log.hxx" +#include "Traits.hxx" +#ifdef HAVE_GLIB #include <glib.h> +#endif + +#include <algorithm> #include <assert.h> #include <string.h> @@ -41,6 +44,7 @@ */ static constexpr size_t MPD_PATH_MAX_UTF8 = (MPD_PATH_MAX - 1) * 4 + 1; +#ifdef HAVE_GLIB static std::string fs_charset; gcc_pure @@ -70,10 +74,29 @@ SetFSCharset(const char *charset) "SetFSCharset: fs charset is: %s", fs_charset.c_str()); } +#endif + const char * GetFSCharset() { +#ifdef HAVE_GLIB return fs_charset.empty() ? "utf-8" : fs_charset.c_str(); +#else + return "utf-8"; +#endif +} + +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 @@ -81,8 +104,14 @@ PathToUTF8(const char *path_fs) { assert(path_fs != nullptr); - if (fs_charset.empty()) - return std::string(path_fs); +#ifdef HAVE_GLIB + if (fs_charset.empty()) { +#endif + auto result = std::string(path_fs); + FixSeparators(result); + return result; +#ifdef HAVE_GLIB + } GIConv conv = g_iconv_open("utf-8", fs_charset.c_str()); if (conv == reinterpret_cast<GIConv>(-1)) @@ -103,9 +132,14 @@ 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; +#endif } +#ifdef HAVE_GLIB + char * PathFromUTF8(const char *path_utf8) { @@ -118,3 +152,5 @@ PathFromUTF8(const char *path_utf8) fs_charset.c_str(), "utf-8", nullptr, nullptr, nullptr); } + +#endif diff --git a/src/fs/Charset.hxx b/src/fs/Charset.hxx index a89cb0459..0a71d7c58 100644 --- a/src/fs/Charset.hxx +++ b/src/fs/Charset.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/CheckFile.cxx b/src/fs/CheckFile.cxx new file mode 100644 index 000000000..a35443674 --- /dev/null +++ b/src/fs/CheckFile.cxx @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2003-2014 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 "CheckFile.hxx" +#include "Log.hxx" +#include "config/ConfigError.hxx" +#include "FileSystem.hxx" +#include "Path.hxx" +#include "AllocatedPath.hxx" +#include "DirectoryReader.hxx" + +#include <errno.h> +#include <sys/stat.h> + +void +CheckDirectoryReadable(Path path_fs) +{ + struct stat st; + if (!StatFile(path_fs, st)) { + FormatErrno(config_domain, + "Failed to stat directory \"%s\"", + path_fs.c_str()); + return; + } + + if (!S_ISDIR(st.st_mode)) { + FormatError(config_domain, + "Not a directory: %s", path_fs.c_str()); + return; + } + +#ifndef WIN32 + const auto x = AllocatedPath::Build(path_fs, "."); + if (!StatFile(x, st) && errno == EACCES) + FormatError(config_domain, + "No permission to traverse (\"execute\") directory: %s", + path_fs.c_str()); +#endif + + const DirectoryReader reader(path_fs); + if (reader.HasFailed() && errno == EACCES) + FormatError(config_domain, + "No permission to read directory: %s", path_fs.c_str()); + +} diff --git a/src/fs/CheckFile.hxx b/src/fs/CheckFile.hxx new file mode 100644 index 000000000..00559647d --- /dev/null +++ b/src/fs/CheckFile.hxx @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2003-2014 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_CHECK_FILE_HXX +#define MPD_FS_CHECK_FILE_HXX + +#include "check.h" + +class Path; + +/** + * Check whether the directory is readable and usable. Logs a warning + * if there is a problem. + */ +void +CheckDirectoryReadable(Path path_fs); + +#endif diff --git a/src/fs/Config.cxx b/src/fs/Config.cxx index 63e64ef99..6aa23005c 100644 --- a/src/fs/Config.cxx +++ b/src/fs/Config.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,24 +20,19 @@ #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> +#include "config/ConfigGlobal.hxx" #ifdef WIN32 #include <windows.h> // for GetACP() #include <stdio.h> // for sprintf() +#elif defined(HAVE_GLIB) +#include <glib.h> #endif void ConfigureFS() { +#if defined(HAVE_GLIB) || defined(WIN32) const char *charset = nullptr; charset = config_get_string(CONF_FS_CHARSET, nullptr); @@ -62,4 +57,5 @@ ConfigureFS() if (charset != nullptr) SetFSCharset(charset); +#endif } diff --git a/src/fs/Config.hxx b/src/fs/Config.hxx index 9d7035706..d4f1709f5 100644 --- a/src/fs/Config.hxx +++ b/src/fs/Config.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/DirectoryReader.hxx b/src/fs/DirectoryReader.hxx index c9d2c04b8..f77c0629f 100644 --- a/src/fs/DirectoryReader.hxx +++ b/src/fs/DirectoryReader.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,7 +21,7 @@ #define MPD_FS_DIRECTORY_READER_HXX #include "check.h" -#include "AllocatedPath.hxx" +#include "Path.hxx" #include <dirent.h> @@ -78,9 +78,9 @@ public: /** * Extracts directory entry that was previously read by #ReadEntry. */ - AllocatedPath GetEntry() const { + Path GetEntry() const { assert(HasEntry()); - return AllocatedPath::FromFS(ent->d_name); + return Path::FromFS(ent->d_name); } }; diff --git a/src/fs/Domain.cxx b/src/fs/Domain.cxx index 0877bca4c..4f3129219 100644 --- a/src/fs/Domain.cxx +++ b/src/fs/Domain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/Domain.hxx b/src/fs/Domain.hxx index b303570fc..1fd17b37f 100644 --- a/src/fs/Domain.hxx +++ b/src/fs/Domain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/FileSystem.cxx b/src/fs/FileSystem.cxx index 4cd9f33b2..4e7c87415 100644 --- a/src/fs/FileSystem.cxx +++ b/src/fs/FileSystem.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/FileSystem.hxx b/src/fs/FileSystem.hxx index cb2f82d22..4dbb064cb 100644 --- a/src/fs/FileSystem.hxx +++ b/src/fs/FileSystem.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -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/Limits.hxx b/src/fs/Limits.hxx index 480b08851..432897a69 100644 --- a/src/fs/Limits.hxx +++ b/src/fs/Limits.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/Path.cxx b/src/fs/Path.cxx index 0ff0591fb..8288a4fec 100644 --- a/src/fs/Path.cxx +++ b/src/fs/Path.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,25 +26,3 @@ Path::ToUTF8() const { return ::PathToUTF8(c_str()); } - -const char * -Path::RelativeFS(const char *other_fs) const -{ - const size_t l = length(); - if (memcmp(data(), other_fs, l) != 0) - return nullptr; - - other_fs += l; - if (*other_fs != 0) { - if (!PathTraits::IsSeparatorFS(*other_fs)) - /* mismatch */ - return nullptr; - - /* skip remaining path separators */ - do { - ++other_fs; - } while (PathTraits::IsSeparatorFS(*other_fs)); - } - - return other_fs; -} diff --git a/src/fs/Path.hxx b/src/fs/Path.hxx index 6ea954577..9e0fa5aeb 100644 --- a/src/fs/Path.hxx +++ b/src/fs/Path.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -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; @@ -137,11 +135,13 @@ public: * nullptr on mismatch. */ gcc_pure - const char *RelativeFS(const char *other_fs) const; + const char *RelativeFS(const char *other_fs) const { + return PathTraitsFS::Relative(value, other_fs); + } 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..4f0958995 --- /dev/null +++ b/src/fs/StandardDirectory.cxx @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2003-2014 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..dca35b486 --- /dev/null +++ b/src/fs/StandardDirectory.hxx @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2003-2014 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..b1a92b9cc --- /dev/null +++ b/src/fs/TextFile.cxx @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2003-2014 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 "util/Alloc.hxx" +#include "fs/Path.hxx" +#include "fs/FileSystem.hxx" + +#include <assert.h> +#include <string.h> +#include <stdlib.h> + +TextFile::TextFile(Path path_fs) + :file(FOpen(path_fs, FOpenMode::ReadText)), + buffer((char *)xalloc(step)), capacity(step), length(0) {} + +TextFile::~TextFile() +{ + free(buffer); + + if (file != nullptr) + fclose(file); +} + +char * +TextFile::ReadLine() +{ + assert(file != nullptr); + + while (true) { + if (length >= capacity) { + if (capacity >= max_length) + /* too large already - bail out */ + return nullptr; + + capacity <<= 1; + char *new_buffer = (char *)realloc(buffer, capacity); + if (new_buffer == nullptr) + /* out of memory - bail out */ + return nullptr; + } + + char *p = fgets(buffer + length, capacity - length, file); + if (p == nullptr) { + if (length == 0 || ferror(file)) + return nullptr; + break; + } + + length += strlen(buffer + length); + if (buffer[length - 1] == '\n') + break; + } + + /* remove the newline characters */ + if (buffer[length - 1] == '\n') + --length; + if (buffer[length - 1] == '\r') + --length; + + buffer[length] = 0; + length = 0; + return buffer; +} diff --git a/src/fs/TextFile.hxx b/src/fs/TextFile.hxx new file mode 100644 index 000000000..e3a712a88 --- /dev/null +++ b/src/fs/TextFile.hxx @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2003-2014 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> +#include <stddef.h> + +class Path; + +class TextFile { + static constexpr size_t max_length = 512 * 1024; + static constexpr size_t step = 1024; + + FILE *const file; + + char *buffer; + size_t capacity, length; + +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..d62987087 100644 --- a/src/fs/Traits.cxx +++ b/src/fs/Traits.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,24 +22,136 @@ #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); +} + +template<typename Traits> +typename Traits::const_pointer +RelativePathImpl(typename Traits::const_pointer base, + typename Traits::const_pointer other) +{ + assert(base != nullptr); + assert(other != nullptr); + + const auto base_length = Traits::GetLength(base); + if (memcmp(base, other, base_length * sizeof(*base)) != 0) + /* mismatch */ + return nullptr; + + other += base_length; + if (other != 0) { + if (!Traits::IsSeparator(*other)) + /* mismatch */ + return nullptr; + + /* skip remaining path separators */ + do { + ++other; + } while (Traits::IsSeparator(*other)); + } + + return other; +} + +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); +} + +PathTraitsFS::const_pointer +PathTraitsFS::Relative(const_pointer base, const_pointer other) +{ + return RelativePathImpl<PathTraitsFS>(base, other); +} + +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); +} + +PathTraitsUTF8::const_pointer +PathTraitsUTF8::Relative(const_pointer base, const_pointer other) +{ + return RelativePathImpl<PathTraitsUTF8>(base, other); } diff --git a/src/fs/Traits.hxx b/src/fs/Traits.hxx index 244ab8b5c..88715c3e8 100644 --- a/src/fs/Traits.hxx +++ b/src/fs/Traits.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 The Music Player Daemon Project + * Copyright (C) 2003-2014 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,67 +24,153 @@ #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 - static bool IsAbsoluteUTF8(const char *p) { + gcc_pure gcc_nonnull_all + static size_t GetLength(const_pointer p) { + return strlen(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); + + /** + * Determine the relative part of the given path to this + * object, not including the directory separator. Returns an + * empty string if the given path equals this object or + * nullptr on mismatch. + */ + gcc_pure gcc_nonnull_all + static const_pointer Relative(const_pointer base, const_pointer other); + + /** + * 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 +178,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 +186,31 @@ 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); + + /** + * Determine the relative part of the given path to this + * object, not including the directory separator. Returns an + * empty string if the given path equals this object or + * nullptr on mismatch. + */ + gcc_pure gcc_nonnull_all + static const_pointer Relative(const_pointer base, const_pointer other); + + /** + * 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 |