295 lines
8.1 KiB
C++
295 lines
8.1 KiB
C++
/*
|
|
* Copyright (c) 2003-2012, John Wiegley. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* - Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* - Neither the name of New Artisans LLC nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <system.hh>
|
|
|
|
#if HAVE_BOOST_SERIALIZATION
|
|
|
|
#include "archive.h"
|
|
#include "amount.h"
|
|
#include "commodity.h"
|
|
#include "pool.h"
|
|
#include "scope.h"
|
|
#include "account.h"
|
|
#include "post.h"
|
|
#include "xact.h"
|
|
|
|
#define LEDGER_MAGIC 0x4c454447
|
|
#define ARCHIVE_VERSION 0x03000006
|
|
|
|
//BOOST_IS_ABSTRACT(ledger::scope_t)
|
|
BOOST_CLASS_EXPORT(ledger::scope_t)
|
|
BOOST_CLASS_EXPORT(ledger::child_scope_t)
|
|
BOOST_CLASS_EXPORT(ledger::symbol_scope_t)
|
|
BOOST_CLASS_EXPORT(ledger::call_scope_t)
|
|
BOOST_CLASS_EXPORT(ledger::account_t)
|
|
BOOST_CLASS_EXPORT(ledger::item_t)
|
|
BOOST_CLASS_EXPORT(ledger::post_t)
|
|
BOOST_CLASS_EXPORT(ledger::xact_base_t)
|
|
BOOST_CLASS_EXPORT(ledger::xact_t)
|
|
BOOST_CLASS_EXPORT(ledger::auto_xact_t)
|
|
BOOST_CLASS_EXPORT(ledger::period_xact_t)
|
|
|
|
template void ledger::journal_t::serialize(boost::archive::binary_oarchive&,
|
|
const unsigned int);
|
|
template void ledger::journal_t::serialize(boost::archive::binary_iarchive&,
|
|
const unsigned int);
|
|
namespace ledger {
|
|
|
|
namespace {
|
|
bool read_header_bits(std::istream& in) {
|
|
uint32_t bytes;
|
|
|
|
assert(sizeof(uint32_t) == 4);
|
|
in.read(reinterpret_cast<char *>(&bytes), sizeof(uint32_t));
|
|
if (bytes != LEDGER_MAGIC) {
|
|
DEBUG("archive.journal", "Magic bytes not present");
|
|
return false;
|
|
}
|
|
|
|
in.read(reinterpret_cast<char *>(&bytes), sizeof(uint32_t));
|
|
if (bytes != ARCHIVE_VERSION) {
|
|
DEBUG("archive.journal", "Archive version mismatch");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void write_header_bits(std::ostream& out) {
|
|
uint32_t bytes;
|
|
|
|
assert(sizeof(uint32_t) == 4);
|
|
bytes = LEDGER_MAGIC;
|
|
out.write(reinterpret_cast<char *>(&bytes), sizeof(uint32_t));
|
|
|
|
bytes = ARCHIVE_VERSION;
|
|
out.write(reinterpret_cast<char *>(&bytes), sizeof(uint32_t));
|
|
}
|
|
}
|
|
|
|
bool archive_t::read_header()
|
|
{
|
|
uintmax_t size = file_size(file);
|
|
if (size < 8)
|
|
return false;
|
|
|
|
// Open the stream, read the version number and the list of sources
|
|
ifstream stream(file, std::ios::binary);
|
|
if (! read_header_bits(stream))
|
|
return false;
|
|
|
|
boost::archive::binary_iarchive iarchive(stream);
|
|
|
|
DEBUG("archive.journal", "Reading header from archive");
|
|
iarchive >> *this;
|
|
|
|
DEBUG("archive.journal",
|
|
"Version number: " << std::hex << ARCHIVE_VERSION << std::dec);
|
|
DEBUG("archive.journal", "Number of sources: " << sources.size());
|
|
|
|
#if DEBUG_ON
|
|
foreach (const journal_t::fileinfo_t& i, sources)
|
|
DEBUG("archive.journal", "Loaded source: " << *i.filename);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
bool archive_t::should_load(const std::list<path>& data_files)
|
|
{
|
|
std::size_t found = 0;
|
|
|
|
DEBUG("archive.journal", "Should the archive be loaded?");
|
|
|
|
if (! exists(file)) {
|
|
DEBUG("archive.journal", "No, it does not exist");
|
|
return false;
|
|
}
|
|
|
|
if (! read_header()) {
|
|
DEBUG("archive.journal", "No, header failed to read");
|
|
return false;
|
|
}
|
|
|
|
if (data_files.empty()) {
|
|
DEBUG("archive.journal", "No, there were no data files!");
|
|
return false;
|
|
}
|
|
|
|
if (sources.empty()) {
|
|
DEBUG("archive.journal", "No, there were no sources!");
|
|
return false;
|
|
}
|
|
|
|
if (data_files.size() != sources.size()) {
|
|
DEBUG("archive.journal", "No, number of sources doesn't match: "
|
|
<< data_files.size() << " != " << sources.size());
|
|
return false;
|
|
}
|
|
|
|
foreach (const path& p, data_files) {
|
|
DEBUG("archive.journal", "Scanning for data file: " << p);
|
|
|
|
if (! exists(p)) {
|
|
DEBUG("archive.journal", "No, an input source no longer exists: " << p);
|
|
return false;
|
|
}
|
|
|
|
foreach (const journal_t::fileinfo_t& i, sources) {
|
|
assert(! i.from_stream);
|
|
assert(i.filename);
|
|
|
|
DEBUG("archive.journal", "Comparing against source file: " << *i.filename);
|
|
|
|
if (*i.filename == p) {
|
|
if (! exists(*i.filename)) {
|
|
DEBUG("archive.journal",
|
|
"No, a referent source no longer exists: " << *i.filename);
|
|
return false;
|
|
}
|
|
|
|
if (i.modtime != posix_time::from_time_t(last_write_time(p))) {
|
|
DEBUG("archive.journal", "No, a source's modtime has changed: " << p);
|
|
return false;
|
|
}
|
|
|
|
if (i.size != file_size(p)) {
|
|
DEBUG("archive.journal", "No, a source's size has changed: " << p);
|
|
return false;
|
|
}
|
|
|
|
found++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found != data_files.size()) {
|
|
DEBUG("archive.journal", "No, not every source's name matched");
|
|
return false;
|
|
}
|
|
|
|
DEBUG("archive.journal", "Yes, it should be loaded!");
|
|
return true;
|
|
}
|
|
|
|
bool archive_t::should_save(journal_t& journal)
|
|
{
|
|
std::list<path> data_files;
|
|
|
|
DEBUG("archive.journal", "Should the archive be saved?");
|
|
|
|
if (journal.was_loaded) {
|
|
DEBUG("archive.journal", "No, it's one we loaded before");
|
|
return false;
|
|
}
|
|
|
|
if (journal.sources.empty()) {
|
|
DEBUG("archive.journal", "No, there were no sources!");
|
|
return false;
|
|
}
|
|
|
|
foreach (const journal_t::fileinfo_t& i, journal.sources) {
|
|
if (i.from_stream) {
|
|
DEBUG("archive.journal", "No, one source was from a stream");
|
|
return false;
|
|
}
|
|
|
|
if (! exists(*i.filename)) {
|
|
DEBUG("archive.journal",
|
|
"No, a source no longer exists: " << *i.filename);
|
|
return false;
|
|
}
|
|
|
|
data_files.push_back(*i.filename);
|
|
}
|
|
|
|
if (should_load(data_files)) {
|
|
DEBUG("archive.journal", "No, because it's still loadable");
|
|
return false;
|
|
}
|
|
|
|
DEBUG("archive.journal", "Yes, it should be saved!");
|
|
return true;
|
|
}
|
|
|
|
void archive_t::save(journal_t& journal)
|
|
{
|
|
INFO_START(archive, "Saved journal file cache");
|
|
|
|
ofstream stream(file, std::ios::binary);
|
|
|
|
write_header_bits(stream);
|
|
sources = journal.sources;
|
|
|
|
#if DEBUG_ON
|
|
foreach (const journal_t::fileinfo_t& i, sources)
|
|
DEBUG("archive.journal", "Saving source: " << *i.filename);
|
|
#endif
|
|
|
|
boost::archive::binary_oarchive oa(stream);
|
|
|
|
DEBUG("archive.journal", "Creating archive with version "
|
|
<< std::hex << ARCHIVE_VERSION << std::dec);
|
|
oa << *this;
|
|
|
|
DEBUG("archive.journal",
|
|
"Archiving journal with " << sources.size() << " sources");
|
|
oa << journal;
|
|
|
|
INFO_FINISH(archive);
|
|
}
|
|
|
|
bool archive_t::load(journal_t& journal)
|
|
{
|
|
INFO_START(archive, "Read cached journal file");
|
|
|
|
ifstream stream(file, std::ios::binary);
|
|
if (! read_header_bits(stream))
|
|
return false;
|
|
|
|
boost::archive::binary_iarchive iarchive(stream);
|
|
|
|
// Skip past the archive header, it was already read in before
|
|
archive_t temp;
|
|
iarchive >> temp;
|
|
|
|
iarchive >> journal;
|
|
journal.was_loaded = true;
|
|
|
|
INFO_FINISH(archive);
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace ledger
|
|
|
|
#endif // HAVE_BOOST_SERIALIZATION
|