diff --git a/aegisub/build/Aegisub/Aegisub.vcxproj b/aegisub/build/Aegisub/Aegisub.vcxproj
index 3c80048e3..6f2a3f842 100644
--- a/aegisub/build/Aegisub/Aegisub.vcxproj
+++ b/aegisub/build/Aegisub/Aegisub.vcxproj
@@ -134,7 +134,6 @@
-
@@ -321,7 +320,6 @@
-
diff --git a/aegisub/build/Aegisub/Aegisub.vcxproj.filters b/aegisub/build/Aegisub/Aegisub.vcxproj.filters
index 4beabf456..da5ea5304 100644
--- a/aegisub/build/Aegisub/Aegisub.vcxproj.filters
+++ b/aegisub/build/Aegisub/Aegisub.vcxproj.filters
@@ -525,9 +525,6 @@
Features\Import
-
- Features\Import
-
Features\Help
@@ -1076,9 +1073,6 @@
Main UI\Grid
-
- Features\Import
-
Features\Import
diff --git a/aegisub/build/libaegisub/libaegisub.vcxproj b/aegisub/build/libaegisub/libaegisub.vcxproj
index 837691411..0f4655767 100644
--- a/aegisub/build/libaegisub/libaegisub.vcxproj
+++ b/aegisub/build/libaegisub/libaegisub.vcxproj
@@ -17,20 +17,27 @@
- $(SrcDir);$(SrcDir)include;$(AegisubContribBase)iconv\include;%(AdditionalIncludeDirectories)
- NOMINMAX;%(PreprocessorDefinitions)
+
+ $(SrcDir);
+ $(SrcDir)include;
+ $(AegisubContribBase)iconv\include;
+ %(AdditionalIncludeDirectories)
+
+
+ NOMINMAX;
+ _WIN32_WINNT=0x0501;
+ %(PreprocessorDefinitions)
+
Use
lagi_pre.h
lagi_pre.h
-
-
@@ -64,8 +71,13 @@
-
-
+
+
+
+
+
+
+
@@ -78,7 +90,6 @@
-
@@ -96,8 +107,13 @@
-
-
+
+
+
+
+
+
+
diff --git a/aegisub/build/libaegisub/libaegisub.vcxproj.filters b/aegisub/build/libaegisub/libaegisub.vcxproj.filters
index 4539c7db6..75b180c34 100644
--- a/aegisub/build/libaegisub/libaegisub.vcxproj.filters
+++ b/aegisub/build/libaegisub/libaegisub.vcxproj.filters
@@ -26,9 +26,6 @@
Header Files
-
- Header Files
-
Header Files
@@ -134,10 +131,25 @@
Header Files
-
+
Header Files
-
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
Header Files
@@ -160,9 +172,6 @@
Source Files\Windows
-
- Source Files\Common
-
Source Files\Windows
@@ -220,10 +229,25 @@
Source Files\Common
-
+
Source Files\Common
-
+
+ Source Files\Common
+
+
+ Source Files\Common
+
+
+ Source Files\Windows
+
+
+ Source Files\Windows
+
+
+ Source Files\Common
+
+
Source Files\Common
@@ -232,4 +256,4 @@
Header Files
-
\ No newline at end of file
+
diff --git a/aegisub/libaegisub/Makefile b/aegisub/libaegisub/Makefile
index 20d6e8ae9..040a439f8 100644
--- a/aegisub/libaegisub/Makefile
+++ b/aegisub/libaegisub/Makefile
@@ -25,23 +25,27 @@ SRC += \
common/charset.cpp \
common/charset_6937.cpp \
common/charset_conv.cpp \
- common/charset_ucd.cpp \
common/color.cpp \
+ common/dispatch.cpp \
+ common/fs.cpp \
common/hotkey.cpp \
common/io.cpp \
common/json.cpp \
common/keyframe.cpp \
+ common/log.cpp \
common/mru.cpp \
common/option.cpp \
common/option_visit.cpp \
common/parser.cpp \
- common/util.cpp \
- common/log.cpp \
+ common/path.cpp \
common/thesaurus.cpp \
+ common/util.cpp \
common/vfr.cpp \
- unix/util.cpp \
unix/access.cpp \
- unix/log.cpp
+ unix/fs.cpp \
+ unix/log.cpp \
+ unix/path.cpp \
+ unix/util.cpp
ifeq (yes, $(BUILD_DARWIN))
SRC += osx/util.mm
diff --git a/aegisub/libaegisub/common/charset.cpp b/aegisub/libaegisub/common/charset.cpp
index 9dc1f92be..8ad4df4b6 100644
--- a/aegisub/libaegisub/common/charset.cpp
+++ b/aegisub/libaegisub/common/charset.cpp
@@ -16,18 +16,118 @@
/// @brief Character set detection and manipulation utilities.
/// @ingroup libaegisub
-#include "charset_ucd.h"
+#include "libaegisub/charset.h"
-namespace agi {
- namespace charset {
+#include "libaegisub/io.h"
-std::string Detect(const std::string &file) {
- return UCDetect(file).Single();
+#include
+#include
+
+#ifndef _WIN32
+#define _X86_ 1
+#endif
+
+#include "../../universalchardet/nscore.h"
+#include "../../universalchardet/nsUniversalDetector.h"
+#include "../../universalchardet/nsMBCSGroupProber.h"
+#include "../../universalchardet/nsCharSetProber.h"
+
+namespace {
+using namespace agi::charset;
+
+class UCDetect : public nsUniversalDetector {
+ /// List of detected character sets
+ CharsetListDetected list;
+
+ void Report(const char* aCharset) {}
+
+public:
+ /// @brief Detect character set of a file using UniversalCharDetect
+ /// @param file File to check
+ UCDetect(agi::fs::path const& file)
+ : nsUniversalDetector(NS_FILTER_ALL)
+ {
+ {
+ std::unique_ptr fp(agi::io::Open(file, true));
+
+ // If it's over 100 MB it's either binary or big enough that we won't
+ // be able to do anything useful with it anyway
+ fp->seekg(0, std::ios::end);
+ if (fp->tellg() > 100 * 1024 * 1024) {
+ list.emplace_back(1.f, "binary");
+ return;
+ }
+ fp->seekg(0, std::ios::beg);
+
+ std::streamsize binaryish = 0;
+ std::streamsize bytes = 0;
+
+ while (!mDone && *fp) {
+ char buf[4096];
+ fp->read(buf, sizeof(buf));
+ std::streamsize read = fp->gcount();
+ HandleData(buf, (PRUint32)read);
+
+ // A dumb heuristic to detect binary files
+ if (!mDone) {
+ bytes += read;
+ for (std::streamsize i = 0; i < read; ++i) {
+ if ((unsigned char)buf[i] < 32 && (buf[i] != '\r' && buf[i] != '\n' && buf[i] != '\t'))
+ ++binaryish;
+ }
+
+ if (binaryish > bytes / 8) {
+ list.emplace_back(1.f, "binary");
+ return;
+ }
+ }
+ }
+ }
+
+ DataEnd();
+
+ if (mDetectedCharset)
+ list.emplace_back(1.f, mDetectedCharset);
+ else {
+ switch (mInputState) {
+ case eHighbyte: {
+ for (PRInt32 i=0; iGetConfidence();
+ if (conf > 0.01f)
+ list.emplace_back(conf, mCharSetProbers[i]->GetCharSetName());
+ }
+
+ break;
+ }
+ case ePureAscii:
+ list.emplace_back(1.f, "US-ASCII");
+ break;
+
+ default:
+ throw UnknownCharset("Unknown character set.");
+ }
+
+ if (list.empty() && (mInputState == eHighbyte))
+ throw UnknownCharset("Unknown character set.");
+
+ typedef std::pair const& result;
+ sort(begin(list), end(list), [](result lft, result rgt) { return lft.first > rgt.first; });
+ }
+ }
+
+ /// @brief Detect character set of a file using UniversalCharDet
+ CharsetListDetected List() const { return list; }
+};
}
-CharsetListDetected DetectAll(const std::string& file) {
- return UCDetect(file).List();
-}
+namespace agi { namespace charset {
+ std::string Detect(agi::fs::path const& file) {
+ return DetectAll(file).front().second;
+ }
- } // namespace util
-} // namespace agi
+ CharsetListDetected DetectAll(agi::fs::path const& file) {
+ return UCDetect(file).List();
+ }
+} }
diff --git a/aegisub/libaegisub/common/charset_ucd.cpp b/aegisub/libaegisub/common/charset_ucd.cpp
deleted file mode 100644
index cc96f1151..000000000
--- a/aegisub/libaegisub/common/charset_ucd.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (c) 2010, Amar Takhar
-//
-// Permission to use, copy, modify, and distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-/// @file charset_ucd.cpp
-/// @brief Character set detection using Universalchardet
-/// @ingroup libaegisub
-
-#include "charset_ucd.h"
-
-#include "libaegisub/io.h"
-#include "libaegisub/scoped_ptr.h"
-
-#include "../../universalchardet/nsCharSetProber.h"
-
-namespace agi {
- namespace charset {
-
-UCDetect::UCDetect(const std::string &file)
-: nsUniversalDetector(NS_FILTER_ALL)
-{
- {
- agi::scoped_ptr fp(io::Open(file, true));
-
- // If it's over 100 MB it's either binary or big enough that we won't
- // be able to do anything useful with it anyway
- fp->seekg(0, std::ios::end);
- if (fp->tellg() > 100 * 1024 * 1024) {
- list.emplace_back(1.f, "binary");
- return;
- }
- fp->seekg(0, std::ios::beg);
-
- std::streamsize binaryish = 0;
- std::streamsize bytes = 0;
-
- while (!mDone && *fp) {
- char buf[4096];
- fp->read(buf, sizeof(buf));
- std::streamsize read = fp->gcount();
- HandleData(buf, (PRUint32)read);
-
- // A dumb heuristic to detect binary files
- if (!mDone) {
- bytes += read;
- for (std::streamsize i = 0; i < read; ++i) {
- if ((unsigned char)buf[i] < 32 && (buf[i] != '\r' && buf[i] != '\n' && buf[i] != '\t'))
- ++binaryish;
- }
-
- if (binaryish > bytes / 8) {
- list.emplace_back(1.f, "binary");
- return;
- }
- }
- }
- }
-
- DataEnd();
-
- if (mDetectedCharset)
- list.emplace_back(1.f, mDetectedCharset);
- else {
- switch (mInputState) {
- case eHighbyte: {
- for (PRInt32 i=0; iGetConfidence();
- if (conf > 0.01f)
- list.emplace_back(conf, mCharSetProbers[i]->GetCharSetName());
- }
-
- break;
- }
- case ePureAscii:
- list.emplace_back(1.f, "US-ASCII");
- break;
-
- default:
- throw UnknownCharset("Unknown character set.");
- }
-
- if (list.empty() && (mInputState == eHighbyte))
- throw UnknownCharset("Unknown character set.");
-
- typedef std::pair const& result;
- sort(begin(list), end(list), [](result lft, result rgt) { return lft.first > rgt.first; });
- }
-}
-
-std::string UCDetect::Single() const {
- return list.front().second;
-}
-
- } // namespace util
-} // namespace agi
diff --git a/aegisub/libaegisub/common/charset_ucd.h b/aegisub/libaegisub/common/charset_ucd.h
deleted file mode 100644
index 8b144cfc0..000000000
--- a/aegisub/libaegisub/common/charset_ucd.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) 2010, Amar Takhar
-//
-// Permission to use, copy, modify, and distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-/// @file charset_ucd.h
-/// @brief Character set detection using Universalchardet
-/// @ingroup libaegisub
-
-#include "libaegisub/charset.h"
-
-#include
-
-#ifndef _WIN32
-#define _X86_ 1
-#endif
-
-#include "../../universalchardet/nscore.h"
-#include "../../universalchardet/nsUniversalDetector.h"
-#include "../../universalchardet/nsMBCSGroupProber.h"
-
-namespace agi {
- namespace charset {
-
-class UCDetect : public nsUniversalDetector {
- /// List of detected character sets.
- CharsetListDetected list;
-
- /// Stub.
- void Report(const char* aCharset) {};
-
-public:
-
- /// @brief Detect character set of a file using UniversalCharDetect
- /// @param file File to check
- UCDetect(const std::string &file);
-
- /// @brief Detect character set of a file using UniversalCharDet
- CharsetListDetected List() const { return list; }
-
- /// @brief Return a single character set (highest confidence)
- /// @return Character set
- std::string Single() const;
-};
-
- } // namespace util
-} // namespace agi
diff --git a/aegisub/libaegisub/common/dispatch.cpp b/aegisub/libaegisub/common/dispatch.cpp
new file mode 100644
index 000000000..fb04f6d49
--- /dev/null
+++ b/aegisub/libaegisub/common/dispatch.cpp
@@ -0,0 +1,98 @@
+// Copyright (c) 2013, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+#include "config.h"
+
+#include "libaegisub/dispatch.h"
+
+#include
+#include
+#include
+#include
+#include
+
+namespace {
+ boost::asio::io_service *service;
+ std::function invoke_main;
+
+ class MainQueue : public agi::dispatch::Queue {
+ void DoInvoke(agi::dispatch::Thunk thunk) {
+ invoke_main(thunk);
+ }
+ };
+
+ class BackgroundQueue : public agi::dispatch::Queue {
+ void DoInvoke(agi::dispatch::Thunk thunk) {
+ service->post(thunk);
+ }
+ };
+
+ class SerialQueue : public agi::dispatch::Queue {
+ boost::asio::io_service::strand strand;
+
+ void DoInvoke(agi::dispatch::Thunk thunk) {
+ strand.post(thunk);
+ }
+ public:
+ SerialQueue() : strand(*service) { }
+ };
+}
+
+namespace agi { namespace dispatch {
+
+void Init(std::function invoke_main) {
+ static boost::asio::io_service service;
+ static boost::asio::io_service::work work(service);
+ ::service = &service;
+ ::invoke_main = invoke_main;
+
+ for (unsigned i = 0; i < std::max(1, std::thread::hardware_concurrency()); ++i)
+ std::thread([]{ ::service->run(); }).detach();
+}
+
+void Queue::Async(Thunk thunk) {
+ DoInvoke(thunk);
+}
+
+void Queue::Sync(Thunk thunk) {
+ std::mutex m;
+ std::condition_variable cv;
+ std::unique_lock l(m);
+ bool done = false;
+ DoInvoke([&]{
+ std::unique_lock l(m);
+ thunk();
+ done = true;
+ cv.notify_all();
+ });
+ cv.wait(l, [&]{ return done; });
+}
+
+Queue& Main() {
+ static MainQueue q;
+ return q;
+}
+
+Queue& Background() {
+ static BackgroundQueue q;
+ return q;
+}
+
+std::unique_ptr Create() {
+ return std::unique_ptr(new SerialQueue);
+}
+
+} }
diff --git a/aegisub/libaegisub/common/fs.cpp b/aegisub/libaegisub/common/fs.cpp
new file mode 100644
index 000000000..ecdd30c6e
--- /dev/null
+++ b/aegisub/libaegisub/common/fs.cpp
@@ -0,0 +1,106 @@
+// Copyright (c) 2013, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+#include "config.h"
+
+#include "libaegisub/fs.h"
+
+#include "libaegisub/access.h"
+#include "libaegisub/exception.h"
+#include "libaegisub/log.h"
+
+#include
+#include
+
+namespace bfs = boost::filesystem;
+namespace ec = boost::system::errc;
+
+// boost::filesystem functions throw a single exception type for all
+// errors, which isn't really what we want, so do some crazy wrapper
+// shit to map error codes to more useful exceptions.
+#define CHECKED_CALL(exp, src_path, dst_path) \
+ boost::system::error_code ec; \
+ exp; \
+ switch (ec.value()) {\
+ case ec::success: break; \
+ case ec::no_such_file_or_directory: throw FileNotFound(src_path); \
+ case ec::is_a_directory: throw NotAFile(src_path); \
+ case ec::not_a_directory: throw NotADirectory(src_path); \
+ case ec::no_space_on_device: throw DriveFull(dst_path); \
+ case ec::permission_denied: \
+ if (!src_path.empty()) \
+ acs::CheckFileRead(src_path); \
+ if (!dst_path.empty()) \
+ acs::CheckFileWrite(dst_path); \
+ throw AccessDenied(src_path); \
+ default: \
+ LOG_D("filesystem") << "Unknown error when calling '" << #exp << "': " << ec << ": " << ec.message(); \
+ throw FileSystemUnknownError(ec.message()); \
+ }
+
+#define CHECKED_CALL_RETURN(exp, src_path) \
+ CHECKED_CALL(auto ret = exp, src_path, agi::fs::path()); \
+ return ret
+
+#define WRAP_BFS(bfs_name, agi_name) \
+ auto agi_name(path const& p) -> decltype(bfs::bfs_name(p)) { \
+ CHECKED_CALL_RETURN(bfs::bfs_name(p, ec), p); \
+ }
+
+#define WRAP_BFS_IGNORE_ERROR(bfs_name, agi_name) \
+ auto agi_name(path const& p) -> decltype(bfs::bfs_name(p)) { \
+ boost::system::error_code ec; \
+ return bfs::bfs_name(p, ec); \
+ }
+
+// sasuga windows.h
+#undef CreateDirectory
+
+namespace agi { namespace fs {
+ WRAP_BFS_IGNORE_ERROR(exists, Exists)
+ WRAP_BFS_IGNORE_ERROR(is_regular_file, FileExists)
+ WRAP_BFS_IGNORE_ERROR(is_directory, DirectoryExists)
+ WRAP_BFS(file_size, SizeImpl)
+ WRAP_BFS(last_write_time, ModifiedTime)
+ WRAP_BFS(create_directories, CreateDirectory)
+ WRAP_BFS(space, Space)
+ WRAP_BFS(remove, Remove)
+ WRAP_BFS(canonical, Canonicalize)
+
+ uintmax_t Size(path const& p) {
+ if (DirectoryExists(p))
+ throw NotAFile(p);
+ return SizeImpl(p);
+ }
+
+ uintmax_t FreeSpace(path const& p) {
+ return Space(p).available;
+ }
+
+ void Rename(const path& from, const path& to) {
+ CHECKED_CALL(bfs::rename(from, to, ec), from, to);
+ }
+
+ void Copy(path const& from, path const& to) {
+ CreateDirectory(to.parent_path());
+ CHECKED_CALL(bfs::copy_file(from, to, bfs::copy_option::overwrite_if_exists, ec), from, to);
+ }
+
+ bool HasExtension(path const& p, std::string const& ext) {
+ if (!p.has_extension()) return ext.empty();
+ return boost::iequals(p.extension().string().substr(1), ext);
+ }
+} }
diff --git a/aegisub/libaegisub/common/hotkey.cpp b/aegisub/libaegisub/common/hotkey.cpp
index efde67227..b42153ccf 100644
--- a/aegisub/libaegisub/common/hotkey.cpp
+++ b/aegisub/libaegisub/common/hotkey.cpp
@@ -18,11 +18,6 @@
#include "../config.h"
-#include
-#include
-#include
-#include
-
#include "libaegisub/hotkey.h"
#include "libaegisub/cajun/writer.h"
@@ -31,8 +26,12 @@
#include "libaegisub/json.h"
#include "libaegisub/log.h"
+#include
#include
#include
+#include
+#include
+#include
namespace agi {
namespace hotkey {
@@ -50,12 +49,12 @@ void Hotkey::ComboInsert(Combo const& combo) {
cmd_map.insert(make_pair(combo.CmdName(), combo));
}
-Hotkey::Hotkey(const std::string &file, const std::string &default_config)
+Hotkey::Hotkey(agi::fs::path const& file, const std::string &default_config)
: config_file(file)
{
LOG_D("hotkey/init") << "Generating hotkeys.";
- json::Object object(agi::json_util::file(config_file, default_config));
+ const json::Object object(agi::json_util::file(config_file, default_config));
for (auto const& hotkey_context : object)
BuildHotkey(hotkey_context.first, hotkey_context.second);
}
@@ -118,7 +117,7 @@ std::vector Hotkey::GetHotkeys(const std::string &context, const st
for (std::tie(it, end) = cmd_map.equal_range(command); it != end; ++it) {
std::string ctext = it->second.Context();
if (ctext == "Always" || ctext == "Default" || ctext == context)
- ret.push_back(it->second.StrMenu());
+ ret.emplace_back(it->second.StrMenu());
}
sort(ret.begin(), ret.end());
diff --git a/aegisub/libaegisub/common/io.cpp b/aegisub/libaegisub/common/io.cpp
index ebd7bfe46..798b65923 100644
--- a/aegisub/libaegisub/common/io.cpp
+++ b/aegisub/libaegisub/common/io.cpp
@@ -16,49 +16,25 @@
/// @brief Windows IO methods.
/// @ingroup libaegisub
-#include
-#include
-
-#include
-#include
+#include "libaegisub/io.h"
#include
-#include
-#include "libaegisub/io.h"
+#include "libaegisub/fs.h"
#include "libaegisub/log.h"
+#include "libaegisub/path.h"
#include "libaegisub/util.h"
-#ifdef _WIN32
-#define snprintf sprintf_s
-#endif
-
-namespace {
- std::string make_temp_name(std::string const& filename) {
- char tmp[1024];
- snprintf(tmp, sizeof tmp, "_tmp_%lld", (long long)time(0));
-
- std::string::size_type pos = filename.rfind('.');
- if (pos == std::string::npos)
- return filename + tmp;
-
- return filename.substr(0, pos) + tmp + filename.substr(pos);
- }
-}
+#include
+#include
namespace agi {
namespace io {
-using agi::charset::ConvertW;
-
-#ifndef _WIN32
-#define ConvertW
-#endif
-
-std::ifstream* Open(const std::string &file, bool binary) {
+std::ifstream* Open(fs::path const& file, bool binary) {
LOG_D("agi/io/open/file") << file;
acs::CheckFileRead(file);
- std::ifstream *stream = new std::ifstream(ConvertW(file).c_str(), (binary ? std::ios::binary : std::ios::in));
+ auto stream = new boost::filesystem::ifstream(file, (binary ? std::ios::binary : std::ios::in));
if (stream->fail()) {
delete stream;
@@ -68,34 +44,30 @@ std::ifstream* Open(const std::string &file, bool binary) {
return stream;
}
-Save::Save(const std::string& file, bool binary)
+Save::Save(fs::path const& file, bool binary)
: file_name(file)
-, tmp_name(make_temp_name(file))
+, tmp_name(unique_path(file.parent_path()/(file.stem().string() + "_tmp_%%%%." + file.extension().string())))
{
LOG_D("agi/io/save/file") << file;
- const std::string pwd = util::DirName(file);
-
- acs::CheckDirWrite(pwd);
+ acs::CheckDirWrite(file.parent_path());
try {
acs::CheckFileWrite(file);
- } catch (FileNotFoundError const&) {
- // If the file doesn't exist we create a 0 byte file, this so so
- // util::Rename will find it, and to let users know something went
- // wrong by leaving a 0 byte file.
- std::ofstream(ConvertW(file).c_str());
+ }
+ catch (fs::FileNotFound const&) {
+ // Not an error
}
- fp = new std::ofstream(ConvertW(tmp_name).c_str(), binary ? std::ios::binary : std::ios::out);
+ fp = new boost::filesystem::ofstream(tmp_name, binary ? std::ios::binary : std::ios::out);
if (!fp->good()) {
delete fp;
- throw agi::FileNotAccessibleError("Could not create temporary file at: " + tmp_name);
+ throw fs::WriteDenied(tmp_name);
}
}
Save::~Save() {
- delete fp;
- util::Rename(tmp_name, file_name);
+ delete fp; // Explicitly delete to unlock file on Windows
+ fs::Rename(tmp_name, file_name);
}
std::ofstream& Save::Get() {
diff --git a/aegisub/libaegisub/common/json.cpp b/aegisub/libaegisub/common/json.cpp
index 8f9f725f8..dfa40e7d5 100644
--- a/aegisub/libaegisub/common/json.cpp
+++ b/aegisub/libaegisub/common/json.cpp
@@ -18,21 +18,22 @@
#include "../config.h"
+#include "libaegisub/json.h"
+
#include
+#include
#include
+#include "libaegisub/fs.h"
#include "libaegisub/io.h"
-#include "libaegisub/json.h"
#include "libaegisub/log.h"
-#include "libaegisub/scoped_ptr.h"
-
namespace agi {
namespace json_util {
json::UnknownElement parse(std::istream *stream) {
try {
- agi::scoped_ptr stream_deleter(stream);
+ std::unique_ptr stream_deleter(stream);
json::UnknownElement root;
json::Reader::Read(root, *stream);
@@ -46,15 +47,15 @@ json::UnknownElement parse(std::istream *stream) {
}
}
-json::UnknownElement file(const std::string &file) {
+json::UnknownElement file(agi::fs::path const& file) {
return parse(io::Open(file));
}
-json::UnknownElement file(const std::string &file, const std::string &default_config) {
+json::UnknownElement file(agi::fs::path const& file, const std::string &default_config) {
try {
return parse(io::Open(file));
}
- catch (FileNotFoundError const&) {
+ catch (fs::FileNotFound const&) {
// Not an error
return parse(new std::istringstream(default_config));
}
diff --git a/aegisub/libaegisub/common/keyframe.cpp b/aegisub/libaegisub/common/keyframe.cpp
index 3dbcda834..fa98c41ca 100644
--- a/aegisub/libaegisub/common/keyframe.cpp
+++ b/aegisub/libaegisub/common/keyframe.cpp
@@ -17,16 +17,15 @@
/// @ingroup libaegisub
///
-
#include "../config.h"
+#include "libaegisub/keyframe.h"
+
#include
#include
#include "libaegisub/io.h"
#include "libaegisub/line_iterator.h"
-#include "libaegisub/keyframe.h"
-#include "libaegisub/vfr.h"
#include
#include
@@ -77,7 +76,7 @@ char x264(std::string const& line) {
}
namespace agi { namespace keyframe {
-void Save(std::string const& filename, std::vector const& keyframes) {
+void Save(agi::fs::path const& filename, std::vector const& keyframes) {
io::Save file(filename);
std::ofstream& of = file.Get();
of << "# keyframe format v1" << std::endl;
@@ -85,7 +84,7 @@ void Save(std::string const& filename, std::vector const& keyframes) {
boost::copy(keyframes, std::ostream_iterator(of, "\n"));
}
-std::vector Load(std::string const& filename) {
+std::vector Load(agi::fs::path const& filename) {
std::unique_ptr file(io::Open(filename));
std::istream &is(*file);
diff --git a/aegisub/libaegisub/common/log.cpp b/aegisub/libaegisub/common/log.cpp
index 016870ed2..8486a575e 100644
--- a/aegisub/libaegisub/common/log.cpp
+++ b/aegisub/libaegisub/common/log.cpp
@@ -18,21 +18,20 @@
#include "../config.h"
-#include
-#include
-#include
-#include
-#include
-#include
+#include "libaegisub/log.h"
#include "libaegisub/cajun/elements.h"
#include "libaegisub/cajun/writer.h"
#include "libaegisub/io.h"
-#include "libaegisub/log.h"
#include "libaegisub/types.h"
#include "libaegisub/util.h"
+#include
#include
+#include
+#include
+#include
+#include
namespace agi {
namespace log {
@@ -109,7 +108,7 @@ Message::~Message() {
agi::log::log->log(sm);
}
-JsonEmitter::JsonEmitter(std::string const& directory, const agi::log::LogSink *log_sink)
+JsonEmitter::JsonEmitter(agi::fs::path const& directory, const agi::log::LogSink *log_sink)
: directory(directory)
, log_sink(log_sink)
{
@@ -146,9 +145,7 @@ JsonEmitter::~JsonEmitter() {
timeval_close.push_back((int64_t)time_close.tv_sec);
timeval_close.push_back((int64_t)time_close.tv_usec);
- std::stringstream str;
- str << directory << time_start.tv_sec << ".json";
- json::Writer::Write(root, io::Save(str.str()).Get());
+ json::Writer::Write(root, io::Save(directory/(std::to_string(time_start.tv_sec) + ".json")).Get());
}
} // namespace log
diff --git a/aegisub/libaegisub/common/mru.cpp b/aegisub/libaegisub/common/mru.cpp
index 8f5729c27..6ca4b8b95 100644
--- a/aegisub/libaegisub/common/mru.cpp
+++ b/aegisub/libaegisub/common/mru.cpp
@@ -30,7 +30,7 @@
namespace agi {
-MRUManager::MRUManager(std::string const& config, std::string const& default_config, agi::Options *options)
+MRUManager::MRUManager(agi::fs::path const& config, std::string const& default_config, agi::Options *options)
: config_name(config)
, options(options)
{
@@ -60,16 +60,22 @@ MRUManager::MRUListMap &MRUManager::Find(std::string const& key) {
return index->second;
}
-void MRUManager::Add(std::string const& key, std::string const& entry) {
+void MRUManager::Add(std::string const& key, agi::fs::path const& entry) {
MRUListMap &map = Find(key);
- map.remove(entry);
- map.push_front(entry);
- Prune(key, map);
+ auto it = find(begin(map), end(map), entry);
+ if (it == begin(map) && it != end(map))
+ return;
+ if (it != end(map))
+ map.splice(begin(map), map, it);
+ else {
+ map.push_front(entry);
+ Prune(key, map);
+ }
Flush();
}
-void MRUManager::Remove(std::string const& key, std::string const& entry) {
+void MRUManager::Remove(std::string const& key, agi::fs::path const& entry) {
Find(key).remove(entry);
Flush();
@@ -79,7 +85,7 @@ const MRUManager::MRUListMap* MRUManager::Get(std::string const& key) {
return &Find(key);
}
-std::string const& MRUManager::GetEntry(std::string const& key, const size_t entry) {
+agi::fs::path const& MRUManager::GetEntry(std::string const& key, const size_t entry) {
const MRUManager::MRUListMap *const map = Get(key);
if (entry >= map->size())
@@ -93,7 +99,8 @@ void MRUManager::Flush() {
for (auto const& mru_map : mru) {
json::Array &array = out[mru_map.first];
- array.insert(array.end(), mru_map.second.begin(), mru_map.second.end());
+ transform(begin(mru_map.second), end(mru_map.second),
+ back_inserter(array), [](agi::fs::path const& p) { return p.string(); });
}
json::Writer::Write(out, io::Save(config_name).Get());
@@ -116,7 +123,8 @@ void MRUManager::Prune(std::string const& key, MRUListMap& map) const {
/// @param array json::Array of values.
void MRUManager::Load(std::string const& key, const json::Array& array) {
try {
- copy(array.begin(), array.end(), back_inserter(mru[key]));
+ transform(begin(array), end(array),
+ back_inserter(mru[key]), [](std::string const& s) { return s; });
}
catch (json::Exception const&) {
// Out of date MRU file; just discard the data and skip it
diff --git a/aegisub/libaegisub/common/option.cpp b/aegisub/libaegisub/common/option.cpp
index 14147c11e..f57bd6a91 100644
--- a/aegisub/libaegisub/common/option.cpp
+++ b/aegisub/libaegisub/common/option.cpp
@@ -20,14 +20,11 @@
#include "libaegisub/option.h"
-#include
-#include
-#include
-
#include "libaegisub/cajun/reader.h"
#include "libaegisub/cajun/writer.h"
#include "libaegisub/cajun/elements.h"
+#include "libaegisub/fs.h"
#include "libaegisub/io.h"
#include "libaegisub/log.h"
#include "libaegisub/option_value.h"
@@ -35,6 +32,9 @@
#include "option_visit.h"
#include
+#include
+#include
+#include
namespace {
/// @brief Write an option to a json object
@@ -66,7 +66,7 @@ namespace {
namespace agi {
-Options::Options(const std::string &file, const std::string& default_config, const OptionSetting setting)
+Options::Options(agi::fs::path const& file, const std::string& default_config, const OptionSetting setting)
: config_file(file)
, setting(setting)
{
@@ -88,21 +88,17 @@ void Options::ConfigNext(std::istream& stream) {
}
void Options::ConfigUser() {
- std::unique_ptr stream;
-
try {
- stream.reset(agi::io::Open(config_file));
- } catch (const FileNotFoundError&) {
+ std::unique_ptr stream(io::Open(config_file));
+ LoadConfig(*stream, true);
+ }
+ catch (fs::FileNotFound const&) {
return;
}
-
/// @todo Handle other errors such as parsing and notifying the user.
- LoadConfig(*stream, true);
}
void Options::LoadConfig(std::istream& stream, bool ignore_errors) {
- /// @todo Store all previously loaded configs in an array for bug report purposes,
- /// this is just a temp stub.
json::UnknownElement config_root;
try {
diff --git a/aegisub/libaegisub/common/path.cpp b/aegisub/libaegisub/common/path.cpp
new file mode 100644
index 000000000..680495b8a
--- /dev/null
+++ b/aegisub/libaegisub/common/path.cpp
@@ -0,0 +1,150 @@
+// Copyright (c) 2013, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+/// @file path.cpp
+/// @brief Platform-independent path code
+/// @ingroup libaegisub
+
+#include "../config.h"
+
+#include "libaegisub/path.h"
+
+#include "libaegisub/fs.h"
+#include "libaegisub/util.h"
+
+#include
+#include
+#include
+
+namespace {
+ template
+ typename T::const_iterator last_less_than(T const& cont, U const& value) {
+ auto it = cont.upper_bound(value);
+ if (it != cont.begin())
+ --it;
+ return it;
+ }
+}
+
+namespace agi {
+
+Path::Path() {
+ tokens["?user"];
+ tokens["?local"];
+ tokens["?data"];
+ tokens["?temp"];
+ tokens["?dictionary"];
+ tokens["?docs"];
+
+ FillPlatformSpecificPaths();
+
+ tokens["?audio"];
+ tokens["?script"];
+ tokens["?video"];
+}
+
+fs::path Path::Decode(std::string const& path) const {
+ const auto it = last_less_than(tokens, path);
+
+ if (!it->second.empty() && boost::starts_with(path, it->first))
+ return (it->second/path.substr(it->first.size())).make_preferred();
+ return fs::path(path).make_preferred();
+}
+
+fs::path Path::MakeRelative(fs::path const& path, std::string const& token) const {
+ const auto it = tokens.find(token);
+ if (it == tokens.end()) throw agi::InternalError("Bad token: " + token, 0);
+
+ return MakeRelative(path, it->second);
+}
+
+fs::path Path::MakeRelative(fs::path const& path, fs::path const& base) const {
+ if (path.empty() || base.empty()) return path;
+
+ const auto str = path.string();
+ if (boost::starts_with(str, "?dummy") || boost::starts_with(str, "dummy-audio:"))
+ return path;
+
+ // Paths on different volumes can't be made relative to each other
+ if (path.has_root_name() && path.root_name() != base.root_name())
+ return path.string();
+
+ auto path_it = path.begin();
+ auto ref_it = base.begin();
+ for (; path_it != path.end() && ref_it != base.end() && *path_it == *ref_it; ++path_it, ++ref_it) ;
+
+ agi::fs::path result;
+ for (; ref_it != base.end(); ++ref_it)
+ result /= "..";
+ for (; path_it != path.end(); ++path_it)
+ result /= *path_it;
+
+ return result;
+}
+
+fs::path Path::MakeAbsolute(fs::path path, std::string const& token) const {
+ const auto it = tokens.find(token);
+ if (it == tokens.end()) throw agi::InternalError("Bad token: " + token, 0);
+ if (path.empty()) return path;
+
+ path.make_preferred();
+ const auto str = path.string();
+ if (boost::starts_with(str, "?dummy") || boost::starts_with(str, "dummy-audio:"))
+ return path;
+ return (it->second.empty() || path.is_absolute()) ? path : it->second/path;
+}
+
+std::string Path::Encode(fs::path const& path) const {
+ // Find the shortest encoding of path made relative to each token
+ std::string shortest = path.string();
+ size_t length = boost::distance(path);
+ for (auto const& tok : tokens) {
+ if (tok.second.empty()) continue;
+
+ const auto p = MakeRelative(path, tok.first);
+ const size_t d = boost::distance(p);
+ if (d < length) {
+ length = d;
+ shortest = (tok.first/p).string();
+ }
+ }
+
+ return shortest;
+}
+
+void Path::SetToken(std::string const& token_name, fs::path const& token_value) {
+ const auto it = tokens.find(token_name);
+ if (it == tokens.end()) throw agi::InternalError("Bad token: " + token_name, 0);
+
+ if (token_value.empty())
+ it->second = token_value;
+ else if (!token_value.is_absolute())
+ it->second.clear();
+ else {
+ it->second = token_value;
+ it->second.make_preferred();
+ if (fs::FileExists(it->second))
+ it->second = it->second.parent_path();
+ }
+
+ paths.clear();
+ for (auto const& tok : tokens) {
+ if (!tok.second.empty())
+ paths[tok.second] = tok.first;
+ }
+}
+
+}
diff --git a/aegisub/libaegisub/common/thesaurus.cpp b/aegisub/libaegisub/common/thesaurus.cpp
index 956fb144a..0c66368c5 100644
--- a/aegisub/libaegisub/common/thesaurus.cpp
+++ b/aegisub/libaegisub/common/thesaurus.cpp
@@ -26,16 +26,16 @@
#include
#include
-#include
+#include
using boost::phoenix::placeholders::_1;
namespace agi {
-Thesaurus::Thesaurus(std::string const& dat_path, std::string const& idx_path)
+Thesaurus::Thesaurus(agi::fs::path const& dat_path, agi::fs::path const& idx_path)
: dat(io::Open(dat_path))
{
- scoped_ptr idx(io::Open(idx_path));
+ std::unique_ptr idx(io::Open(idx_path));
std::string encoding_name;
getline(*idx, encoding_name);
diff --git a/aegisub/libaegisub/common/util.cpp b/aegisub/libaegisub/common/util.cpp
index 52ddb0c12..b1b738e23 100644
--- a/aegisub/libaegisub/common/util.cpp
+++ b/aegisub/libaegisub/common/util.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011, Amar Takhar
+// Copyright (c) 2013, Thomas Goyne
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@@ -11,36 +11,26 @@
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
-/// @file util.cpp
-/// @brief Unix utility methods.
-/// @ingroup libaegisub
-
-#include
-
-#include
-#include
+#include "../config.h"
#include "libaegisub/util.h"
-#include
+#include
-namespace agi {
- namespace util {
+namespace agi { namespace util {
-void str_lower(std::string &str) {
- boost::to_lower(str);
+std::string strftime(const char *fmt, const tm *tmptr) {
+ if (!tmptr) {
+ time_t t = time(nullptr);
+ tmptr = localtime(&t);
+ }
+
+ char buff[65536];
+ ::strftime(buff, sizeof buff, fmt, tmptr);
+ return buff;
}
-int strtoi(std::string const& str) {
- errno = 0;
- long l = strtol(str.c_str(), nullptr, 10);
-
- if ((errno == ERANGE) || (l < INT_MIN) || (l > INT_MAX))
- return 0;
-
- return (int)l;
-}
-
- } // namespace util
-} // namespace agi
+} }
diff --git a/aegisub/libaegisub/common/vfr.cpp b/aegisub/libaegisub/common/vfr.cpp
index 897c26850..39288741f 100644
--- a/aegisub/libaegisub/common/vfr.cpp
+++ b/aegisub/libaegisub/common/vfr.cpp
@@ -16,10 +16,13 @@
/// @brief Framerate handling of all sorts
/// @ingroup libaegisub video_input
+#include "../config.h"
+
#include "libaegisub/vfr.h"
#include
#include
+#include
#include
#include
#include
@@ -199,7 +202,7 @@ Framerate &Framerate::operator=(double fps) {
return *this = Framerate(fps);
}
-Framerate::Framerate(std::string const& filename)
+Framerate::Framerate(fs::path const& filename)
: denominator(default_denominator)
, numerator(0)
{
@@ -221,7 +224,7 @@ Framerate::Framerate(std::string const& filename)
throw UnknownFormat(line);
}
-void Framerate::Save(std::string const& filename, int length) const {
+void Framerate::Save(fs::path const& filename, int length) const {
agi::io::Save file(filename);
std::ofstream &out = file.Get();
diff --git a/aegisub/libaegisub/include/libaegisub/access.h b/aegisub/libaegisub/include/libaegisub/access.h
index ee5ecb01e..f58d2d0fa 100644
--- a/aegisub/libaegisub/include/libaegisub/access.h
+++ b/aegisub/libaegisub/include/libaegisub/access.h
@@ -17,17 +17,11 @@
/// @ingroup libaegisub
#include
+#include
namespace agi {
namespace acs {
-DEFINE_SIMPLE_EXCEPTION_NOINNER(Fatal, FileSystemError, "filesystem/fatal");
-DEFINE_SIMPLE_EXCEPTION_NOINNER(NotAFile, FileSystemError, "filesystem/not_a_file")
-DEFINE_SIMPLE_EXCEPTION_NOINNER(NotADirectory, FileSystemError, "filesystem/not_a_directory")
-
-DEFINE_SIMPLE_EXCEPTION_NOINNER(Read, FileNotAccessibleError, "filesystem/not_accessible/read_permission")
-DEFINE_SIMPLE_EXCEPTION_NOINNER(Write, FileNotAccessibleError, "filesystem/not_accessible/write_permission")
-
enum Type {
FileRead,
DirRead,
@@ -35,13 +29,13 @@ enum Type {
DirWrite
};
-void Check(const std::string &file, acs::Type);
+void Check(fs::path const& file, acs::Type);
-void CheckFileRead(const std::string &file);
-void CheckDirRead(const std::string &dir);
+inline void CheckFileRead(fs::path const& file) { Check(file, acs::FileRead); }
+inline void CheckFileWrite(fs::path const& file) { Check(file, acs::FileWrite); }
-void CheckFileWrite(const std::string &file);
-void CheckDirWrite(const std::string &dir);
+inline void CheckDirRead(fs::path const& dir) { Check(dir, acs::DirRead); }
+inline void CheckDirWrite(fs::path const& dir) { Check(dir, acs::DirWrite); }
} // namespace axs
} // namespace agi
diff --git a/aegisub/libaegisub/include/libaegisub/charset.h b/aegisub/libaegisub/include/libaegisub/charset.h
index 971a3ee08..0c6cdbd17 100644
--- a/aegisub/libaegisub/include/libaegisub/charset.h
+++ b/aegisub/libaegisub/include/libaegisub/charset.h
@@ -16,11 +16,11 @@
/// @brief Character set detection and manipulation utilities.
/// @ingroup libaegisub
-#include
-#include
+#include
+#include
+
#include
#include
-#include
namespace agi {
/// Character set conversion and detection.
@@ -35,12 +35,12 @@ typedef std::vector> CharsetListDetected;
/// @brief Return a complete list of detected character sets ordered by precedence.
/// @param file File to check
/// @return List of possible charsets sorted by probability
-CharsetListDetected DetectAll(std::string const& file);
+CharsetListDetected DetectAll(agi::fs::path const& file);
/// @brief Returns the character set with the highest confidence
/// @param file File to check
/// @return Detected character set.
-std::string Detect(const std::string &file);
+std::string Detect(agi::fs::path const& file);
} // namespace util
} // namespace agi
diff --git a/aegisub/libaegisub/include/libaegisub/charset_conv_win.h b/aegisub/libaegisub/include/libaegisub/charset_conv_win.h
index f7551daa9..ad89cf3d8 100644
--- a/aegisub/libaegisub/include/libaegisub/charset_conv_win.h
+++ b/aegisub/libaegisub/include/libaegisub/charset_conv_win.h
@@ -23,5 +23,8 @@ namespace agi {
/// Convert a UTF-8 string to a string suitable for use with Win32 API functions
std::wstring ConvertW(std::string const& src);
std::string ConvertW(std::wstring const& src);
+
+ /// Convert a UTF-16 string to the local charset
+ std::string ConvertLocal(std::wstring const& src);
}
}
diff --git a/aegisub/libaegisub/include/libaegisub/dispatch.h b/aegisub/libaegisub/include/libaegisub/dispatch.h
new file mode 100644
index 000000000..d8be32fe3
--- /dev/null
+++ b/aegisub/libaegisub/include/libaegisub/dispatch.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2013, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+#include
+#include
+
+namespace agi {
+ namespace dispatch {
+ typedef std::function Thunk;
+
+ class Queue {
+ virtual void DoInvoke(Thunk thunk)=0;
+ public:
+ virtual ~Queue() { }
+
+ /// Invoke the thunk on this processing queue, returning immediately
+ void Async(Thunk thunk);
+
+ /// Invoke the thunk on this processing queue, returning only when
+ /// it's complete
+ void Sync(Thunk thunk);
+ };
+
+ /// Initialize the dispatch thread pools
+ /// @param invoke_main A function which invokes the thunk on the GUI thread
+ void Init(std::function invoke_main);
+
+ /// Get the main queue, which runs on the GUI thread
+ Queue& Main();
+
+ /// Get the generic background queue, which runs thunks in parallel
+ Queue& Background();
+
+ /// Create a new serial queue
+ std::unique_ptr Create();
+ }
+}
diff --git a/aegisub/libaegisub/include/libaegisub/exception.h b/aegisub/libaegisub/include/libaegisub/exception.h
index cd4f34693..2290d80ad 100644
--- a/aegisub/libaegisub/include/libaegisub/exception.h
+++ b/aegisub/libaegisub/include/libaegisub/exception.h
@@ -37,10 +37,7 @@
#include
#include
-/// @see aegisub.h
namespace agi {
-
-
/// @class Exception
/// @brief Base class for all exceptions in Aegisub.
///
@@ -90,7 +87,6 @@ namespace agi {
/// could not be opened for reading". This is the purpose of the "inner"
/// exceptions.
class Exception {
-
/// The error message
std::string message;
@@ -98,16 +94,13 @@ namespace agi {
std::shared_ptr inner;
protected:
-
/// @brief Protected constructor initialising members
/// @param msg The error message
/// @param inr The inner exception, optional
///
/// Deriving classes should always use this constructor for initialising
/// the base class.
- Exception(const std::string &msg, const Exception *inr = 0)
- : message(msg)
- {
+ Exception(const std::string &msg, const Exception *inr = 0) : message(msg) {
if (inr)
inner.reset(inr->Copy());
}
@@ -120,9 +113,7 @@ namespace agi {
public:
/// @brief Destructor
- virtual ~Exception()
- {
- }
+ virtual ~Exception() { }
/// @brief Get the outer exception error message
/// @return Error message
@@ -147,7 +138,6 @@ namespace agi {
/// level are [a-z0-9_].
virtual const char * GetName() const = 0;
-
/// @brief Convert to char array as the error message
/// @return The error message
operator const char * () { return GetMessage().c_str(); }
@@ -164,16 +154,12 @@ namespace agi {
virtual Exception *Copy() const = 0;
};
-
-
/// @brief Convenience macro to include the current location in code
///
/// Intended for use in error messages where it can sometimes be convenient to
/// indicate the exact position the error occurred at.
#define AG_WHERE " (at " __FILE__ ":" #__LINE__ ")"
-
-
/// @brief Convenience macro for declaring exceptions with no support for inner exception
/// @param classname Name of the exception class to declare
/// @param baseclass Class to derive from
@@ -231,7 +217,6 @@ namespace agi {
classname(const std::string &msg, Exception *inner) : baseclass(msg, inner) { } \
};
-
/// @class agi::UserCancelException
/// @extends agi::Exception
/// @brief Exception for "user cancel" events
@@ -246,7 +231,6 @@ namespace agi {
/// code in question.
DEFINE_SIMPLE_EXCEPTION_NOINNER(UserCancelException,Exception,"nonerror/user_cancel")
-
/// @class agi::InternalError
/// @extends agi:Exception
/// @brief Errors that should never happen and point to some invalid assumption in the code
@@ -258,42 +242,17 @@ namespace agi {
/// and eventually cause an abort().
DEFINE_SIMPLE_EXCEPTION(InternalError, Exception, "internal_error")
-
- /// @class agi::FileSystemError
- /// @extends agi::Exception
- /// @brief Base class for errors related to the file system
+ /// @class agi::EnvironmentError
+ /// @extends agi:Exception
+ /// @brief The execution environment is broken in some fundamental way
///
- /// This base class can not be instantiated.
- /// File system errors do not support inner exceptions, as they are always originating
- /// causes for errors.
- DEFINE_BASE_EXCEPTION_NOINNER(FileSystemError,Exception)
-
- /// @class agi::FileNotAccessibleError
- /// @extends agi::FileSystemError
- /// @brief A file can't be accessed for some reason
- DEFINE_SIMPLE_EXCEPTION_NOINNER(FileNotAccessibleError,FileSystemError,"filesystem/not_accessible")
-
-
- /// @class FileNotFoundError
- /// @brief A file can't be accessed because there's no file by the given name
- class FileNotFoundError : public FileNotAccessibleError {
- public:
-
- /// @brief Constructor, automatically builds the error message
- /// @param filename Name of the file that could not be found
- FileNotFoundError(const std::string &filename) : FileNotAccessibleError(std::string("File not found: ") + filename) { }
-
- // Not documented, see agi::Exception class
- const char * GetName() const { return "filesystem/not_accessible/not_found"; }
-
- // Not documented, see agi::Exception class
- Exception * Copy() const { return new FileNotFoundError(*this); }
- };
-
+ /// Throw an environment error when a call to the platform API has failed
+ /// in some way that should normally never happen or suggests that the
+ /// runtime environment is too insane to support.
+ DEFINE_SIMPLE_EXCEPTION_NOINNER(EnvironmentError, Exception, "environment_error")
/// @class agi::InvalidInputException
/// @extends agi::Exception
/// @brief Some input data were invalid and could not be processed
- DEFINE_BASE_EXCEPTION(InvalidInputException,Exception)
-
+ DEFINE_BASE_EXCEPTION(InvalidInputException, Exception)
}
diff --git a/aegisub/libaegisub/include/libaegisub/fs.h b/aegisub/libaegisub/include/libaegisub/fs.h
new file mode 100644
index 000000000..c1516e971
--- /dev/null
+++ b/aegisub/libaegisub/include/libaegisub/fs.h
@@ -0,0 +1,177 @@
+// Copyright (c) 2013, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#undef CreateDirectory
+
+namespace agi {
+ namespace fs {
+ /// Define a filesystem error which takes a path or a string
+#define DEFINE_FS_EXCEPTION(type, base, message) \
+ struct type : public base { \
+ type(path const& p) : base(message + p.string()) { } \
+ type(std::string const& s) : base(s) { } \
+ const char *GetName() const { return ""; } \
+ Exception *Copy() const { return new type(*this); } \
+ }
+
+ /// @class agi::FileSystemError
+ /// @extends agi::Exception
+ /// @brief Base class for errors related to the file system
+ ///
+ /// This base class can not be instantiated.
+ /// File system errors do not support inner exceptions, as they
+ /// are always originating causes for errors.
+ DEFINE_BASE_EXCEPTION_NOINNER(FileSystemError, Exception)
+
+ /// A file can't be accessed for some reason
+ DEFINE_FS_EXCEPTION(FileNotAccessible, FileSystemError, "File is not accessible: ");
+
+ /// A file can't be accessed because there's no file by the given name
+ DEFINE_FS_EXCEPTION(FileNotFound, FileNotAccessible, "File not found: ");
+
+ /// An error of some unknown type has occured
+ DEFINE_SIMPLE_EXCEPTION_NOINNER(FileSystemUnknownError, FileSystemError, "filesystem/unknown");
+
+ /// The path exists, but isn't a file
+ DEFINE_FS_EXCEPTION(NotAFile, FileNotAccessible, "Path is not a file (and should be): ");
+
+ /// The path exists, but isn't a directory
+ DEFINE_FS_EXCEPTION(NotADirectory, FileNotAccessible, "Path is not a directory (and should be): ");
+
+ /// The given path is too long for the filesystem
+ DEFINE_FS_EXCEPTION(PathTooLog, FileSystemError, "Path is too long: ");
+
+ /// Insufficient free space to complete operation
+ DEFINE_FS_EXCEPTION(DriveFull, FileSystemError, "Insufficient free space to write file: ");
+
+ /// Base class for access denied errors
+ DEFINE_FS_EXCEPTION(AccessDenied, FileNotAccessible, "Access denied to path: ");
+
+ /// Trying to read the file gave an access denied error
+ DEFINE_FS_EXCEPTION(ReadDenied, AccessDenied, "Access denied when trying to read: ");
+
+ /// Trying to write the file gave an access denied error
+ DEFINE_FS_EXCEPTION(WriteDenied, AccessDenied, "Access denied when trying to write: ");
+
+ /// File exists and cannot be overwritten due to being read-only
+ DEFINE_FS_EXCEPTION(ReadOnlyFile, WriteDenied, "File is read-only: ");
+
+ bool Exists(path const& p);
+ bool FileExists(path const& file);
+ bool DirectoryExists(path const& dir);
+
+ /// Get the local-charset encoded shortname for a file
+ ///
+ /// This is purely for compatibility with external libraries which do
+ /// not support unicode filenames on Windows. On all other platforms,
+ /// it is a no-op.
+ std::string ShortName(path const& file_path);
+
+ /// Check for amount of free space on a path
+ uintmax_t FreeSpace(path const& dir_path);
+
+ /// Get the size in bytes of the file at path
+ ///
+ /// @throws agi::FileNotFound if path does not exist
+ /// @throws agi::acs::NotAFile if path is a directory
+ /// @throws agi::acs::Read if path exists but could not be read
+ uintmax_t Size(path const& file_path);
+
+ /// Get the modification time of the file at path
+ ///
+ /// @throws agi::FileNotFound if path does not exist
+ /// @throws agi::acs::NotAFile if path is a directory
+ /// @throws agi::acs::Read if path exists but could not be read
+ time_t ModifiedTime(path const& file_path);
+
+ /// Create a directory and all required intermediate directories
+ /// @throws agi::acs::Write if the directory could not be created.
+ ///
+ /// Trying to create a directory which already exists is not an error.
+ bool CreateDirectory(path const& dir_path);
+
+ /// Touch the given path
+ ///
+ /// Creates the file if it does not exist, or updates the modified
+ /// time if it does
+ void Touch(path const& file_path);
+
+ /// Rename a file or directory
+ /// @param from Source path
+ /// @param to Destination path
+ void Rename(path const& from, path const& to);
+
+ /// Copy a file
+ /// @param from Source path
+ /// @param to Destination path
+ ///
+ /// The destination path will be created if it does not exist.
+ void Copy(path const& from, path const& to);
+
+ /// Delete a file
+ /// @param path Path to file to delete
+ /// @throws agi::FileNotAccessibleError if file exists but could not be deleted
+ bool Remove(path const& file);
+
+ /// Check if the file has the given extension
+ /// @param p Path to check
+ /// @param ext Case-insensitive extension, without leading dot
+ bool HasExtension(path const& p, std::string const& ext);
+
+ agi::fs::path Canonicalize(agi::fs::path const& path);
+
+ class DirectoryIterator {
+ struct PrivData;
+ std::shared_ptr privdata;
+ std::string value;
+ public:
+ typedef path value_type;
+ typedef path* pointer;
+ typedef path& reference;
+ typedef size_t difference_type;
+ typedef std::forward_iterator_tag iterator_category;
+
+ bool operator==(DirectoryIterator const&) const;
+ bool operator!=(DirectoryIterator const& rhs) const { return !(*this == rhs); }
+ DirectoryIterator& operator++();
+ std::string const& operator*() const { return value; }
+
+ DirectoryIterator(path const& p, std::string const& filter);
+ DirectoryIterator();
+ ~DirectoryIterator();
+
+ template void GetAll(T& cont);
+ };
+
+ inline DirectoryIterator& begin(DirectoryIterator &it) { return it; }
+ inline DirectoryIterator end(DirectoryIterator &) { return DirectoryIterator(); }
+
+ template
+ inline void DirectoryIterator::GetAll(T& cont) {
+ copy(*this, end(*this), std::back_inserter(cont));
+ }
+ }
+}
diff --git a/aegisub/libaegisub/include/libaegisub/fs_fwd.h b/aegisub/libaegisub/include/libaegisub/fs_fwd.h
new file mode 100644
index 000000000..c2d886964
--- /dev/null
+++ b/aegisub/libaegisub/include/libaegisub/fs_fwd.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2013, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+namespace boost { namespace filesystem { class path; } }
+namespace agi { namespace fs { typedef boost::filesystem::path path; } }
diff --git a/aegisub/libaegisub/include/libaegisub/hotkey.h b/aegisub/libaegisub/include/libaegisub/hotkey.h
index 6fd0dcff9..eaaa73dea 100644
--- a/aegisub/libaegisub/include/libaegisub/hotkey.h
+++ b/aegisub/libaegisub/include/libaegisub/hotkey.h
@@ -16,11 +16,13 @@
/// @brief Hotkey handler
/// @ingroup hotkey menu event window
+#include
#include