Mostly purge wxWidgets from non-UI stuff

Use boost::filesystem::path for all paths, and std::string for all other
strings, converting to/from wxString as close to the actual uses of wx
as possible.

Where possible, replace the uses of non-UI wxWidgets functionality with
the additions to the standard library in C++11, or the equivalents in
boost.

Move the path token management logic to libaegisub (and rewrite it in
the process).

Add a basic thread pool based on asio and std::thread to libaegisub.

This touches nearly every file in the project and a nontrivial amount of
code had to be rewritten entirely, so there's probably a lot of broken
stuff.
This commit is contained in:
Thomas Goyne 2013-01-04 07:01:50 -08:00
parent 10e06ac3f9
commit 1e0f08c0ed
297 changed files with 5702 additions and 6573 deletions

View file

@ -134,7 +134,6 @@
<ClInclude Include="$(SrcDir)avisynth_wrap.h" />
<ClInclude Include="$(SrcDir)base_grid.h" />
<ClInclude Include="$(SrcDir)block_cache.h" />
<ClInclude Include="$(SrcDir)charset_conv.h" />
<ClInclude Include="$(SrcDir)charset_detect.h" />
<ClInclude Include="$(SrcDir)colorspace.h" />
<ClInclude Include="$(SrcDir)colour_button.h" />
@ -321,7 +320,6 @@
<ClCompile Include="$(SrcDir)auto4_lua_scriptreader.cpp" />
<ClCompile Include="$(SrcDir)avisynth_wrap.cpp" />
<ClCompile Include="$(SrcDir)base_grid.cpp" />
<ClCompile Include="$(SrcDir)charset_conv.cpp" />
<ClCompile Include="$(SrcDir)charset_detect.cpp" />
<ClCompile Include="$(SrcDir)colorspace.cpp" />
<ClCompile Include="$(SrcDir)colour_button.cpp" />

View file

@ -525,9 +525,6 @@
<ClInclude Include="$(SrcDir)charset_detect.h">
<Filter>Features\Import</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)charset_conv.h">
<Filter>Features\Import</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)help_button.h">
<Filter>Features\Help</Filter>
</ClInclude>
@ -1076,9 +1073,6 @@
<ClCompile Include="$(SrcDir)subs_grid.cpp">
<Filter>Main UI\Grid</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)charset_conv.cpp">
<Filter>Features\Import</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)charset_detect.cpp">
<Filter>Features\Import</Filter>
</ClCompile>

View file

@ -17,20 +17,27 @@
<!-- Project specific configuration -->
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(SrcDir);$(SrcDir)include;$(AegisubContribBase)iconv\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>
$(SrcDir);
$(SrcDir)include;
$(AegisubContribBase)iconv\include;
%(AdditionalIncludeDirectories)
</AdditionalIncludeDirectories>
<PreprocessorDefinitions>
NOMINMAX;
_WIN32_WINNT=0x0501;
%(PreprocessorDefinitions)
</PreprocessorDefinitions>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>lagi_pre.h</PrecompiledHeaderFile>
<ForcedIncludeFiles>lagi_pre.h</ForcedIncludeFiles>
</ClCompile>
</ItemDefinitionGroup>
<!-- Source files -->
<ItemGroup>
<ClInclude Include="$(SrcDir)lagi_pre.h" />
<ClInclude Include="$(SrcDir)config.h" />
<ClInclude Include="$(SrcDir)common\charset_6937.h" />
<ClInclude Include="$(SrcDir)common\charset_ucd.h" />
<ClInclude Include="$(SrcDir)common\option_visit.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\access.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\charset.h" />
@ -64,8 +71,13 @@
<ClInclude Include="$(SrcDir)include\libaegisub\background_runner.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\color.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\spellchecker.h" />
<ClInclude Include="..\..\libaegisub\include\libaegisub\calltip_provider.h" />
<ClInclude Include="..\..\libaegisub\include\libaegisub\of_type_adaptor.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\calltip_provider.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\fs.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\of_type_adaptor.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\path.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\fs_fwd.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\dispatch.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\split.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(SrcDir)windows\lagi_pre.cpp">
@ -78,7 +90,6 @@
<ClCompile Include="$(SrcDir)common\charset.cpp" />
<ClCompile Include="$(SrcDir)common\charset_6937.cpp" />
<ClCompile Include="$(SrcDir)common\charset_conv.cpp" />
<ClCompile Include="$(SrcDir)common\charset_ucd.cpp" />
<ClCompile Include="$(SrcDir)common\hotkey.cpp" />
<ClCompile Include="$(SrcDir)common\json.cpp" />
<ClCompile Include="$(SrcDir)common\keyframe.cpp" />
@ -96,8 +107,13 @@
<ClCompile Include="$(SrcDir)ass\dialogue_parser.cpp" />
<ClCompile Include="$(SrcDir)common\color.cpp" />
<ClCompile Include="$(SrcDir)common\parser.cpp" />
<ClCompile Include="..\..\libaegisub\common\calltip_provider.cpp" />
<ClCompile Include="..\..\libaegisub\common\io.cpp" />
<ClCompile Include="$(SrcDir)common\calltip_provider.cpp" />
<ClCompile Include="$(SrcDir)common\fs.cpp" />
<ClCompile Include="$(SrcDir)common\io.cpp" />
<ClCompile Include="$(SrcDir)common\path.cpp" />
<ClCompile Include="$(SrcDir)windows\fs.cpp" />
<ClCompile Include="$(SrcDir)windows\path_win.cpp" />
<ClCompile Include="$(SrcDir)common\dispatch.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="$(SrcDir)include\libaegisub\charsets.def" />

View file

@ -26,9 +26,6 @@
<ClInclude Include="$(SrcDir)common\charset_6937.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)common\charset_ucd.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)common\option_visit.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -134,10 +131,25 @@
<ClInclude Include="$(SrcDir)common\parser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\libaegisub\include\libaegisub\of_type_adaptor.h">
<ClInclude Include="$(SrcDir)include\libaegisub\of_type_adaptor.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\libaegisub\include\libaegisub\calltip_provider.h">
<ClInclude Include="$(SrcDir)include\libaegisub\calltip_provider.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\path.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\fs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\fs_fwd.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\dispatch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\split.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@ -160,9 +172,6 @@
<ClCompile Include="$(SrcDir)windows\charset_conv_win.cpp">
<Filter>Source Files\Windows</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)common\charset_ucd.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)windows\log_win.cpp">
<Filter>Source Files\Windows</Filter>
</ClCompile>
@ -220,10 +229,25 @@
<ClCompile Include="$(SrcDir)common\parser.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="..\..\libaegisub\common\io.cpp">
<ClCompile Include="$(SrcDir)common\io.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="..\..\libaegisub\common\calltip_provider.cpp">
<ClCompile Include="$(SrcDir)common\calltip_provider.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)common\path.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)windows\path_win.cpp">
<Filter>Source Files\Windows</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)windows\fs.cpp">
<Filter>Source Files\Windows</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)common\fs.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)common\dispatch.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
</ItemGroup>
@ -232,4 +256,4 @@
<Filter>Header Files</Filter>
</None>
</ItemGroup>
</Project>
</Project>

View file

@ -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

View file

@ -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 <fstream>
#include <string>
#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<std::ifstream> 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; i<NUM_OF_CHARSET_PROBERS; i++) {
if (!mCharSetProbers[i]) continue;
float conf = mCharSetProbers[i]->GetConfidence();
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<float, std::string> 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();
}
} }

View file

@ -1,107 +0,0 @@
// Copyright (c) 2010, Amar Takhar <verm@aegisub.org>
//
// 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<std::ifstream> 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; i<NUM_OF_CHARSET_PROBERS; i++) {
if (!mCharSetProbers[i]) continue;
float conf = mCharSetProbers[i]->GetConfidence();
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<float, std::string> 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

View file

@ -1,56 +0,0 @@
// Copyright (c) 2010, Amar Takhar <verm@aegisub.org>
//
// 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 <string>
#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

View file

@ -0,0 +1,98 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <boost/asio/io_service.hpp>
#include <boost/asio/strand.hpp>
#include <condition_variable>
#include <mutex>
#include <thread>
namespace {
boost::asio::io_service *service;
std::function<void (agi::dispatch::Thunk)> 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<void (Thunk)> 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<unsigned>(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<std::mutex> l(m);
bool done = false;
DoInvoke([&]{
std::unique_lock<std::mutex> 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<Queue> Create() {
return std::unique_ptr<Queue>(new SerialQueue);
}
} }

View file

@ -0,0 +1,106 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
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);
}
} }

View file

@ -18,11 +18,6 @@
#include "../config.h"
#include <algorithm>
#include <cmath>
#include <memory>
#include <tuple>
#include "libaegisub/hotkey.h"
#include "libaegisub/cajun/writer.h"
@ -31,8 +26,12 @@
#include "libaegisub/json.h"
#include "libaegisub/log.h"
#include <algorithm>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/map.hpp>
#include <cmath>
#include <fstream>
#include <tuple>
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<std::string> 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());

View file

@ -16,49 +16,25 @@
/// @brief Windows IO methods.
/// @ingroup libaegisub
#include <sys/stat.h>
#include <errno.h>
#include <iostream>
#include <fstream>
#include "libaegisub/io.h"
#include <libaegisub/access.h>
#include <libaegisub/charset_conv_win.h>
#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 <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
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() {

View file

@ -18,21 +18,22 @@
#include "../config.h"
#include "libaegisub/json.h"
#include <fstream>
#include <memory>
#include <sstream>
#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<std::istream> stream_deleter(stream);
std::unique_ptr<std::istream> 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));
}

View file

@ -17,16 +17,15 @@
/// @ingroup libaegisub
///
#include "../config.h"
#include "libaegisub/keyframe.h"
#include <algorithm>
#include <fstream>
#include "libaegisub/io.h"
#include "libaegisub/line_iterator.h"
#include "libaegisub/keyframe.h"
#include "libaegisub/vfr.h"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/range/algorithm.hpp>
@ -77,7 +76,7 @@ char x264(std::string const& line) {
}
namespace agi { namespace keyframe {
void Save(std::string const& filename, std::vector<int> const& keyframes) {
void Save(agi::fs::path const& filename, std::vector<int> 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<int> const& keyframes) {
boost::copy(keyframes, std::ostream_iterator<int>(of, "\n"));
}
std::vector<int> Load(std::string const& filename) {
std::vector<int> Load(agi::fs::path const& filename) {
std::unique_ptr<std::ifstream> file(io::Open(filename));
std::istream &is(*file);

View file

@ -18,21 +18,20 @@
#include "../config.h"
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <functional>
#include <memory>
#include <sstream>
#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 <algorithm>
#include <boost/range/algorithm.hpp>
#include <cstring>
#include <fstream>
#include <functional>
#include <memory>
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

View file

@ -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

View file

@ -20,14 +20,11 @@
#include "libaegisub/option.h"
#include <cassert>
#include <memory>
#include <sstream>
#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 <boost/range/adaptor/map.hpp>
#include <cassert>
#include <memory>
#include <sstream>
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<std::istream> stream;
try {
stream.reset(agi::io::Open(config_file));
} catch (const FileNotFoundError&) {
std::unique_ptr<std::istream> 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 {

View file

@ -0,0 +1,150 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// 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 <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
#include <boost/range/distance.hpp>
namespace {
template<class T, class U>
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;
}
}
}

View file

@ -26,16 +26,16 @@
#include <boost/phoenix/operator/comparison.hpp>
#include <boost/phoenix/core/argument.hpp>
#include <cstdlib>
#include <fstream>
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<std::ifstream> idx(io::Open(idx_path));
std::unique_ptr<std::ifstream> idx(io::Open(idx_path));
std::string encoding_name;
getline(*idx, encoding_name);