added support for parsing QIF files
This commit is contained in:
parent
bf923ab33e
commit
aba3d3037a
13 changed files with 269 additions and 85 deletions
1
Makefile
1
Makefile
|
|
@ -10,6 +10,7 @@ CODE = account.cc \
|
||||||
format.cc \
|
format.cc \
|
||||||
ledger.cc \
|
ledger.cc \
|
||||||
option.cc \
|
option.cc \
|
||||||
|
qif.cc \
|
||||||
quotes.cc \
|
quotes.cc \
|
||||||
textual.cc \
|
textual.cc \
|
||||||
valexpr.cc \
|
valexpr.cc \
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ void automated_transaction_t::extend_entry(entry_t * entry)
|
||||||
amt = (*t)->amount;
|
amt = (*t)->amount;
|
||||||
|
|
||||||
transaction_t * xact
|
transaction_t * xact
|
||||||
= new transaction_t(entry, (*t)->account, amt, amt,
|
= new transaction_t((*t)->account, amt, amt,
|
||||||
(*t)->flags | TRANSACTION_AUTO);
|
(*t)->flags | TRANSACTION_AUTO);
|
||||||
entry->add_transaction(xact);
|
entry->add_transaction(xact);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
39
binary.cc
39
binary.cc
|
|
@ -92,7 +92,7 @@ void read_binary_amount(std::istream& in, amount_t& amt)
|
||||||
|
|
||||||
transaction_t * read_binary_transaction(std::istream& in, entry_t * entry)
|
transaction_t * read_binary_transaction(std::istream& in, entry_t * entry)
|
||||||
{
|
{
|
||||||
transaction_t * xact = new transaction_t(entry, NULL);
|
transaction_t * xact = new transaction_t(NULL);
|
||||||
|
|
||||||
xact->account = accounts[read_binary_number<account_t::ident_t>(in)];
|
xact->account = accounts[read_binary_number<account_t::ident_t>(in)];
|
||||||
xact->account->add_transaction(xact);
|
xact->account->add_transaction(xact);
|
||||||
|
|
@ -193,9 +193,10 @@ account_t * read_binary_account(std::istream& in, account_t * master = NULL)
|
||||||
return acct;
|
return acct;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int read_binary_journal(std::istream& in,
|
unsigned int read_binary_journal(std::istream& in,
|
||||||
journal_t * journal,
|
const std::string& file,
|
||||||
account_t * master)
|
journal_t * journal,
|
||||||
|
account_t * master)
|
||||||
{
|
{
|
||||||
ident = 0;
|
ident = 0;
|
||||||
c_ident = 0;
|
c_ident = 0;
|
||||||
|
|
@ -204,18 +205,24 @@ unsigned int read_binary_journal(std::istream& in,
|
||||||
read_binary_number<unsigned long>(in) != format_version)
|
read_binary_number<unsigned long>(in) != format_version)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (unsigned short i = 0,
|
if (! file.empty()) {
|
||||||
count = read_binary_number<unsigned short>(in);
|
for (unsigned short i = 0,
|
||||||
i < count;
|
count = read_binary_number<unsigned short>(in);
|
||||||
i++) {
|
i < count;
|
||||||
std::string path = read_binary_string(in);
|
i++) {
|
||||||
std::time_t old_mtime;
|
std::string path = read_binary_string(in);
|
||||||
read_binary_number(in, old_mtime);
|
if (i == 0 && path != file)
|
||||||
struct stat info;
|
return 0;
|
||||||
stat(path.c_str(), &info);
|
|
||||||
if (std::difftime(info.st_mtime, old_mtime) > 0)
|
std::time_t old_mtime;
|
||||||
return 0;
|
read_binary_number(in, old_mtime);
|
||||||
journal->sources.push_back(path);
|
struct stat info;
|
||||||
|
stat(path.c_str(), &info);
|
||||||
|
if (std::difftime(info.st_mtime, old_mtime) > 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
journal->sources.push_back(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
journal->master = read_binary_account(in, master);
|
journal->master = read_binary_account(in, master);
|
||||||
|
|
|
||||||
15
config.cc
15
config.cc
|
|
@ -59,7 +59,8 @@ Basic options:\n\
|
||||||
-i, --init FILE initialize ledger by loading FILE (def: ~/.ledgerrc)\n\
|
-i, --init FILE initialize ledger by loading FILE (def: ~/.ledgerrc)\n\
|
||||||
-f, --file FILE read ledger data from FILE\n\
|
-f, --file FILE read ledger data from FILE\n\
|
||||||
-o, --output FILE write output to FILE\n\
|
-o, --output FILE write output to FILE\n\
|
||||||
-p, --set-price CONV specify a commodity conversion: \"COMM=AMOUNT\"\n\n\
|
-p, --set-price CONV specify a commodity conversion: \"COMM=AMOUNT\"\n\
|
||||||
|
-a, --account NAME specify the default account (useful with QIF files)\n\n\
|
||||||
Report filtering:\n\
|
Report filtering:\n\
|
||||||
-b, --begin-date DATE set report begin date\n\
|
-b, --begin-date DATE set report begin date\n\
|
||||||
-e, --end-date DATE set report end date\n\
|
-e, --end-date DATE set report end date\n\
|
||||||
|
|
@ -132,13 +133,7 @@ OPT_BEGIN(init, "i:") {
|
||||||
} OPT_END(init);
|
} OPT_END(init);
|
||||||
|
|
||||||
OPT_BEGIN(file, "f:") {
|
OPT_BEGIN(file, "f:") {
|
||||||
char * buf = new char[std::strlen(optarg) + 1];
|
config->data_file = optarg;
|
||||||
std::strcpy(buf, optarg);
|
|
||||||
for (char * p = std::strtok(buf, ":");
|
|
||||||
p;
|
|
||||||
p = std::strtok(NULL, ":"))
|
|
||||||
config->files.push_back(p);
|
|
||||||
delete[] buf;
|
|
||||||
} OPT_END(file);
|
} OPT_END(file);
|
||||||
|
|
||||||
OPT_BEGIN(cache, ":") {
|
OPT_BEGIN(cache, ":") {
|
||||||
|
|
@ -157,6 +152,10 @@ OPT_BEGIN(set_price, "p:") {
|
||||||
std::cerr << "Error: Invalid price setting: " << optarg << std::endl;
|
std::cerr << "Error: Invalid price setting: " << optarg << std::endl;
|
||||||
} OPT_END(set_price);
|
} OPT_END(set_price);
|
||||||
|
|
||||||
|
OPT_BEGIN(account, "a:") {
|
||||||
|
config->account = optarg;
|
||||||
|
} OPT_END(account);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Report filtering
|
// Report filtering
|
||||||
|
|
|
||||||
3
config.h
3
config.h
|
|
@ -17,12 +17,13 @@ extern const std::string equity_fmt;
|
||||||
|
|
||||||
struct config_t
|
struct config_t
|
||||||
{
|
{
|
||||||
strings_list files;
|
|
||||||
strings_list price_settings;
|
strings_list price_settings;
|
||||||
std::string init_file;
|
std::string init_file;
|
||||||
|
std::string data_file;
|
||||||
std::string cache_file;
|
std::string cache_file;
|
||||||
std::string price_db;
|
std::string price_db;
|
||||||
std::string output_file;
|
std::string output_file;
|
||||||
|
std::string account;
|
||||||
std::string predicate;
|
std::string predicate;
|
||||||
std::string display_predicate;
|
std::string display_predicate;
|
||||||
std::string interval_text;
|
std::string interval_text;
|
||||||
|
|
|
||||||
1
format.h
1
format.h
|
|
@ -127,6 +127,7 @@ class format_transactions : public item_handler<transaction_t>
|
||||||
}
|
}
|
||||||
xact->dflags |= TRANSACTION_DISPLAYED;
|
xact->dflags |= TRANSACTION_DISPLAYED;
|
||||||
}
|
}
|
||||||
|
flush();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
|
#include "ledger.h"
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "ledger.h"
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <xmlparse.h> // expat XML parser
|
#include <xmlparse.h> // expat XML parser
|
||||||
}
|
}
|
||||||
|
|
@ -85,7 +85,7 @@ static void startElement(void *userData, const char *name, const char **atts)
|
||||||
action = ENTRY_DESC;
|
action = ENTRY_DESC;
|
||||||
else if (std::strcmp(name, "trn:split") == 0) {
|
else if (std::strcmp(name, "trn:split") == 0) {
|
||||||
assert(curr_entry);
|
assert(curr_entry);
|
||||||
curr_entry->add_transaction(new transaction_t(curr_entry, curr_account));
|
curr_entry->add_transaction(new transaction_t(curr_account));
|
||||||
}
|
}
|
||||||
else if (std::strcmp(name, "split:reconciled-state") == 0)
|
else if (std::strcmp(name, "split:reconciled-state") == 0)
|
||||||
action = XACT_STATE;
|
action = XACT_STATE;
|
||||||
|
|
|
||||||
39
ledger.cc
39
ledger.cc
|
|
@ -98,7 +98,7 @@ entry_t * journal_t::derive_entry(strings_list::iterator i,
|
||||||
m_xact = matching->transactions.front();
|
m_xact = matching->transactions.front();
|
||||||
|
|
||||||
amount_t amt(*i++);
|
amount_t amt(*i++);
|
||||||
first = xact = new transaction_t(added.get(), m_xact->account, amt, amt);
|
first = xact = new transaction_t(m_xact->account, amt, amt);
|
||||||
added->add_transaction(xact);
|
added->add_transaction(xact);
|
||||||
|
|
||||||
if (xact->amount.commodity->symbol.empty()) {
|
if (xact->amount.commodity->symbol.empty()) {
|
||||||
|
|
@ -108,8 +108,7 @@ entry_t * journal_t::derive_entry(strings_list::iterator i,
|
||||||
|
|
||||||
m_xact = matching->transactions.back();
|
m_xact = matching->transactions.back();
|
||||||
|
|
||||||
xact = new transaction_t(added.get(), m_xact->account,
|
xact = new transaction_t(m_xact->account, - first->amount, - first->amount);
|
||||||
- first->amount, - first->amount);
|
|
||||||
added->add_transaction(xact);
|
added->add_transaction(xact);
|
||||||
|
|
||||||
if (i != end && std::string(*i++) == "-from" && i != end)
|
if (i != end && std::string(*i++) == "-from" && i != end)
|
||||||
|
|
@ -150,7 +149,7 @@ entry_t * journal_t::derive_entry(strings_list::iterator i,
|
||||||
}
|
}
|
||||||
|
|
||||||
amount_t amt(*i++);
|
amount_t amt(*i++);
|
||||||
transaction_t * xact = new transaction_t(added.get(), acct, amt, amt);
|
transaction_t * xact = new transaction_t(acct, amt, amt);
|
||||||
added->add_transaction(xact);
|
added->add_transaction(xact);
|
||||||
|
|
||||||
if (! xact->amount.commodity)
|
if (! xact->amount.commodity)
|
||||||
|
|
@ -158,10 +157,8 @@ entry_t * journal_t::derive_entry(strings_list::iterator i,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i != end && std::string(*i++) == "-from" && i != end) {
|
if (i != end && std::string(*i++) == "-from" && i != end) {
|
||||||
if (account_t * acct = find_account(*i++)) {
|
if (account_t * acct = find_account(*i++))
|
||||||
transaction_t * xact = new transaction_t(NULL, acct);
|
added->add_transaction(new transaction_t(acct));
|
||||||
added->add_transaction(xact);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (! matching) {
|
if (! matching) {
|
||||||
std::cerr << "Error: Could not figure out the account to draw from."
|
std::cerr << "Error: Could not figure out the account to draw from."
|
||||||
|
|
@ -169,8 +166,7 @@ entry_t * journal_t::derive_entry(strings_list::iterator i,
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
transaction_t * xact
|
transaction_t * xact
|
||||||
= new transaction_t(added.get(),
|
= new transaction_t(matching->transactions.back()->account);
|
||||||
matching->transactions.back()->account);
|
|
||||||
added->add_transaction(xact);
|
added->add_transaction(xact);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -178,9 +174,10 @@ entry_t * journal_t::derive_entry(strings_list::iterator i,
|
||||||
return added.release();
|
return added.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_journal_file(const std::string& path,
|
int parse_journal_file(const std::string& path,
|
||||||
journal_t * journal,
|
journal_t * journal,
|
||||||
account_t * master)
|
account_t * master,
|
||||||
|
const std::string * original_file)
|
||||||
{
|
{
|
||||||
journal->sources.push_back(path);
|
journal->sources.push_back(path);
|
||||||
|
|
||||||
|
|
@ -189,13 +186,19 @@ int parse_journal_file(const std::string& path,
|
||||||
|
|
||||||
std::ifstream stream(path.c_str());
|
std::ifstream stream(path.c_str());
|
||||||
|
|
||||||
unsigned long magic;
|
char magic[sizeof(unsigned int) + 1];
|
||||||
stream.read((char *)&magic, sizeof(magic));
|
stream.read(magic, sizeof(unsigned int));
|
||||||
|
magic[sizeof(unsigned int)] = '\0';
|
||||||
stream.seekg(0);
|
stream.seekg(0);
|
||||||
|
|
||||||
if (magic == binary_magic_number)
|
if (*((unsigned int *) magic) == binary_magic_number)
|
||||||
return read_binary_journal(stream, journal,
|
return read_binary_journal(stream, original_file ? *original_file : "",
|
||||||
master ? master : journal->master);
|
journal, master ? master : journal->master);
|
||||||
|
else if (std::strcmp(magic, "!Typ") == 0 ||
|
||||||
|
std::strcmp(magic, "\n!Ty") == 0 ||
|
||||||
|
std::strcmp(magic, "\r\n!T") == 0)
|
||||||
|
return parse_qif_file(stream, journal, master ? master : journal->master,
|
||||||
|
commodity_t::find_commodity("$", true));
|
||||||
else
|
else
|
||||||
return parse_textual_journal(stream, journal,
|
return parse_textual_journal(stream, journal,
|
||||||
master ? master : journal->master);
|
master ? master : journal->master);
|
||||||
|
|
|
||||||
28
ledger.h
28
ledger.h
|
|
@ -57,17 +57,16 @@ class transaction_t
|
||||||
mutable unsigned int index;
|
mutable unsigned int index;
|
||||||
mutable unsigned short dflags;
|
mutable unsigned short dflags;
|
||||||
|
|
||||||
transaction_t(entry_t * _entry, account_t * _account)
|
transaction_t(account_t * _account)
|
||||||
: entry(_entry), account(_account), flags(TRANSACTION_NORMAL),
|
: entry(NULL), account(_account), flags(TRANSACTION_NORMAL),
|
||||||
index(0), dflags(0) {}
|
index(0), dflags(0) {}
|
||||||
|
|
||||||
transaction_t(entry_t * _entry,
|
transaction_t(account_t * _account,
|
||||||
account_t * _account,
|
|
||||||
const amount_t& _amount,
|
const amount_t& _amount,
|
||||||
const amount_t& _cost,
|
const amount_t& _cost,
|
||||||
unsigned int _flags = TRANSACTION_NORMAL,
|
unsigned int _flags = TRANSACTION_NORMAL,
|
||||||
const std::string& _note = "")
|
const std::string& _note = "")
|
||||||
: entry(_entry), account(_account), amount(_amount),
|
: entry(NULL), account(_account), amount(_amount),
|
||||||
cost(_cost), flags(_flags), note(_note), index(0), dflags(0) {}
|
cost(_cost), flags(_flags), note(_note), index(0), dflags(0) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -97,6 +96,7 @@ class entry_t
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_transaction(transaction_t * xact) {
|
void add_transaction(transaction_t * xact) {
|
||||||
|
xact->entry = this;
|
||||||
transactions.push_back(xact);
|
transactions.push_back(xact);
|
||||||
}
|
}
|
||||||
bool remove_transaction(transaction_t * xact) {
|
bool remove_transaction(transaction_t * xact) {
|
||||||
|
|
@ -223,9 +223,10 @@ class journal_t
|
||||||
strings_list::iterator end) const;
|
strings_list::iterator end) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
int parse_journal_file(const std::string& path,
|
int parse_journal_file(const std::string& path,
|
||||||
journal_t * journal,
|
journal_t * journal,
|
||||||
account_t * master = NULL);
|
account_t * master = NULL,
|
||||||
|
const std::string * original_file = NULL);
|
||||||
|
|
||||||
unsigned int parse_textual_journal(std::istream& in,
|
unsigned int parse_textual_journal(std::istream& in,
|
||||||
journal_t * ledger,
|
journal_t * ledger,
|
||||||
|
|
@ -233,14 +234,19 @@ unsigned int parse_textual_journal(std::istream& in,
|
||||||
|
|
||||||
extern const unsigned long binary_magic_number;
|
extern const unsigned long binary_magic_number;
|
||||||
|
|
||||||
unsigned int read_binary_journal(std::istream& in,
|
unsigned int read_binary_journal(std::istream& in,
|
||||||
journal_t * journal,
|
const std::string& file,
|
||||||
account_t * master = NULL);
|
journal_t * journal,
|
||||||
|
account_t * master = NULL);
|
||||||
|
|
||||||
void write_binary_journal(std::ostream& out,
|
void write_binary_journal(std::ostream& out,
|
||||||
journal_t * journal,
|
journal_t * journal,
|
||||||
strings_list * files = NULL);
|
strings_list * files = NULL);
|
||||||
|
|
||||||
|
unsigned int parse_qif_file(std::istream& in, journal_t * journal,
|
||||||
|
account_t * master,
|
||||||
|
commodity_t * def_commodity = NULL);
|
||||||
|
|
||||||
extern const std::string version;
|
extern const std::string version;
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
38
main.cc
38
main.cc
|
|
@ -153,7 +153,7 @@ int main(int argc, char * argv[], char * envp[])
|
||||||
|
|
||||||
TIMER_STOP(process_args);
|
TIMER_STOP(process_args);
|
||||||
|
|
||||||
bool use_cache = config->files.empty();
|
bool use_cache = config->data_file.empty();
|
||||||
|
|
||||||
// Process options from the environment
|
// Process options from the environment
|
||||||
|
|
||||||
|
|
@ -185,8 +185,12 @@ int main(int argc, char * argv[], char * envp[])
|
||||||
if (parse_journal_file(config->init_file, journal.get()))
|
if (parse_journal_file(config->init_file, journal.get()))
|
||||||
throw error("Entries not allowed in initialization file");
|
throw error("Entries not allowed in initialization file");
|
||||||
|
|
||||||
if (use_cache && ! config->cache_file.empty()) {
|
if (use_cache && ! config->cache_file.empty() &&
|
||||||
entry_count += parse_journal_file(config->cache_file, journal.get());
|
! config->data_file.empty()) {
|
||||||
|
entry_count += parse_journal_file(config->cache_file, journal.get(),
|
||||||
|
NULL, &config->data_file);
|
||||||
|
journal->sources.pop_front(); // remove cache_file
|
||||||
|
|
||||||
if (entry_count == 0) {
|
if (entry_count == 0) {
|
||||||
journal.reset(new journal_t);
|
journal.reset(new journal_t);
|
||||||
cache_dirty = true;
|
cache_dirty = true;
|
||||||
|
|
@ -195,17 +199,18 @@ int main(int argc, char * argv[], char * envp[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry_count == 0) {
|
if (entry_count == 0 && ! config->data_file.empty()) {
|
||||||
for (strings_list::iterator i = config->files.begin();
|
account_t * account = NULL;
|
||||||
i != config->files.end();
|
if (! config->account.empty())
|
||||||
i++)
|
account = journal->find_account(config->account);
|
||||||
if (*i == "-") {
|
|
||||||
use_cache = false;
|
if (config->data_file == "-") {
|
||||||
entry_count += parse_textual_journal(std::cin, journal.get(),
|
use_cache = false;
|
||||||
journal->master);
|
entry_count += parse_textual_journal(std::cin, journal.get(), account);
|
||||||
} else {
|
} else {
|
||||||
entry_count += parse_journal_file(*i, journal.get());
|
entry_count += parse_journal_file(config->data_file, journal.get(),
|
||||||
}
|
account);
|
||||||
|
}
|
||||||
|
|
||||||
if (! config->price_db.empty())
|
if (! config->price_db.empty())
|
||||||
if (parse_journal_file(config->price_db, journal.get()))
|
if (parse_journal_file(config->price_db, journal.get()))
|
||||||
|
|
@ -265,9 +270,8 @@ int main(int argc, char * argv[], char * envp[])
|
||||||
bool show_all_related = false;
|
bool show_all_related = false;
|
||||||
|
|
||||||
if (command == "p" || command == "e") {
|
if (command == "p" || command == "e") {
|
||||||
config->show_related =
|
config->show_related =
|
||||||
show_all_related =
|
show_all_related = true;
|
||||||
config->show_subtotal = true;
|
|
||||||
}
|
}
|
||||||
else if (command == "E") {
|
else if (command == "E") {
|
||||||
config->show_subtotal = true;
|
config->show_subtotal = true;
|
||||||
|
|
|
||||||
160
qif.cc
Normal file
160
qif.cc
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
#include "ledger.h"
|
||||||
|
#include "datetime.h"
|
||||||
|
#include "error.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
#define MAX_LINE 1024
|
||||||
|
|
||||||
|
static char line[MAX_LINE + 1];
|
||||||
|
static std::string path;
|
||||||
|
static unsigned int linenum;
|
||||||
|
|
||||||
|
static inline char * get_line(std::istream& in) {
|
||||||
|
in.getline(line, MAX_LINE);
|
||||||
|
int len = std::strlen(line);
|
||||||
|
if (line[len - 1] == '\r')
|
||||||
|
line[len - 1] = '\0';
|
||||||
|
linenum++;
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int parse_qif_file(std::istream& in, journal_t * journal,
|
||||||
|
account_t * master, commodity_t * def_commodity)
|
||||||
|
{
|
||||||
|
std::auto_ptr<entry_t> entry;
|
||||||
|
std::auto_ptr<amount_t> amount;
|
||||||
|
transaction_t * xact;
|
||||||
|
account_t * misc = journal->find_account("Miscellaneous");
|
||||||
|
unsigned int count;
|
||||||
|
|
||||||
|
path = journal->sources.back();
|
||||||
|
linenum = 1;
|
||||||
|
|
||||||
|
while (! in.eof()) {
|
||||||
|
char c;
|
||||||
|
in.get(c);
|
||||||
|
switch (c) {
|
||||||
|
case ' ':
|
||||||
|
case '\t':
|
||||||
|
if (peek_next_nonws(in) != '\n') {
|
||||||
|
get_line(in);
|
||||||
|
throw parse_error(path, linenum, "Line begins with whitespace");
|
||||||
|
}
|
||||||
|
// fall through...
|
||||||
|
|
||||||
|
case '\n':
|
||||||
|
linenum++;
|
||||||
|
case '\r': // skip blank lines
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '!':
|
||||||
|
in >> line;
|
||||||
|
|
||||||
|
// jww (2004-08-19): these types are not supported yet
|
||||||
|
assert(std::strcmp(line, "Type:Invst") != 0 &&
|
||||||
|
std::strcmp(line, "Account") != 0 &&
|
||||||
|
std::strcmp(line, "Type:Cat") != 0 &&
|
||||||
|
std::strcmp(line, "Type:Class") != 0 &&
|
||||||
|
std::strcmp(line, "Type:Memorized") != 0);
|
||||||
|
|
||||||
|
get_line(in);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'D':
|
||||||
|
entry.reset(new entry_t);
|
||||||
|
xact = new transaction_t(master);
|
||||||
|
entry->add_transaction(xact);
|
||||||
|
|
||||||
|
in >> line;
|
||||||
|
if (! parse_date(line, &entry->date))
|
||||||
|
throw parse_error(path, linenum, "Failed to parse date");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'T':
|
||||||
|
case '$':
|
||||||
|
in >> line;
|
||||||
|
xact->amount.parse(line);
|
||||||
|
if (def_commodity)
|
||||||
|
xact->amount.commodity = def_commodity;
|
||||||
|
if (c == '$')
|
||||||
|
xact->amount.negate();
|
||||||
|
xact->cost = xact->amount;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'C':
|
||||||
|
if (in.peek() == '*') {
|
||||||
|
in.get(c);
|
||||||
|
entry->state = entry_t::CLEARED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'N':
|
||||||
|
if (std::isdigit(in.peek())) {
|
||||||
|
in >> c >> line;
|
||||||
|
entry->code = line;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'P':
|
||||||
|
case 'M':
|
||||||
|
case 'L':
|
||||||
|
case 'S':
|
||||||
|
case 'E': {
|
||||||
|
char b = c;
|
||||||
|
int len;
|
||||||
|
c = in.peek();
|
||||||
|
if (! std::isspace(c) && c != '\n') {
|
||||||
|
get_line(in);
|
||||||
|
|
||||||
|
switch (b) {
|
||||||
|
case 'P':
|
||||||
|
entry->payee = line;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'S':
|
||||||
|
xact = new transaction_t(NULL);
|
||||||
|
entry->add_transaction(xact);
|
||||||
|
// fall through...
|
||||||
|
case 'L':
|
||||||
|
len = std::strlen(line);
|
||||||
|
if (line[len - 1] == ']')
|
||||||
|
line[len - 1] = '\0';
|
||||||
|
xact->account = journal->find_account(line[0] == '[' ?
|
||||||
|
line + 1 : line);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'M':
|
||||||
|
case 'E':
|
||||||
|
xact->note = line;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'A':
|
||||||
|
// jww (2004-08-19): these are ignored right now
|
||||||
|
get_line(in);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '^':
|
||||||
|
if (xact->account == master) {
|
||||||
|
transaction_t * nxact = new transaction_t(misc);
|
||||||
|
entry->add_transaction(nxact);
|
||||||
|
nxact->amount = nxact->cost = - xact->amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (journal->add_entry(entry.release()))
|
||||||
|
count++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
|
|
@ -18,8 +18,8 @@ namespace ledger {
|
||||||
|
|
||||||
#define MAX_LINE 1024
|
#define MAX_LINE 1024
|
||||||
|
|
||||||
std::string path;
|
static std::string path;
|
||||||
unsigned int linenum;
|
static unsigned int linenum;
|
||||||
|
|
||||||
#ifdef TIMELOG_SUPPORT
|
#ifdef TIMELOG_SUPPORT
|
||||||
static std::time_t time_in;
|
static std::time_t time_in;
|
||||||
|
|
@ -54,7 +54,7 @@ transaction_t * parse_transaction_text(char * line, account_t * account,
|
||||||
{
|
{
|
||||||
// The account will be determined later...
|
// The account will be determined later...
|
||||||
|
|
||||||
std::auto_ptr<transaction_t> xact(new transaction_t(entry, NULL));
|
std::auto_ptr<transaction_t> xact(new transaction_t(NULL));
|
||||||
|
|
||||||
// The call to `next_element' will skip past the account name,
|
// The call to `next_element' will skip past the account name,
|
||||||
// and return a pointer to the beginning of the amount. Once
|
// and return a pointer to the beginning of the amount. Once
|
||||||
|
|
@ -392,8 +392,7 @@ unsigned int parse_textual_journal(std::istream& in, journal_t * journal,
|
||||||
time_commodity = amt.commodity;
|
time_commodity = amt.commodity;
|
||||||
|
|
||||||
transaction_t * xact
|
transaction_t * xact
|
||||||
= new transaction_t(curr.get(), last_account, amt, amt,
|
= new transaction_t(last_account, amt, amt, TRANSACTION_VIRTUAL);
|
||||||
TRANSACTION_VIRTUAL);
|
|
||||||
curr->add_transaction(xact);
|
curr->add_transaction(xact);
|
||||||
|
|
||||||
if (! finalize_entry(curr.get()) ||
|
if (! finalize_entry(curr.get()) ||
|
||||||
|
|
|
||||||
13
walk.cc
13
walk.cc
|
|
@ -59,10 +59,10 @@ void collapse_transactions::report_cumulative_subtotal()
|
||||||
for (amounts_map::const_iterator i = result.amounts.begin();
|
for (amounts_map::const_iterator i = result.amounts.begin();
|
||||||
i != result.amounts.end();
|
i != result.amounts.end();
|
||||||
i++) {
|
i++) {
|
||||||
transaction_t * total_xact
|
transaction_t * total_xact = new transaction_t(totals_account);
|
||||||
= new transaction_t(last_entry, totals_account);
|
|
||||||
xact_temps.push_back(total_xact);
|
xact_temps.push_back(total_xact);
|
||||||
|
|
||||||
|
total_xact->entry = last_entry;
|
||||||
total_xact->amount = (*i).second;
|
total_xact->amount = (*i).second;
|
||||||
total_xact->cost = (*i).second;
|
total_xact->cost = (*i).second;
|
||||||
|
|
||||||
|
|
@ -98,9 +98,10 @@ void changed_value_transactions::operator()(transaction_t * xact)
|
||||||
for (amounts_map::const_iterator i = diff.amounts.begin();
|
for (amounts_map::const_iterator i = diff.amounts.begin();
|
||||||
i != diff.amounts.end();
|
i != diff.amounts.end();
|
||||||
i++) {
|
i++) {
|
||||||
transaction_t * temp_xact = new transaction_t(entry, NULL);
|
transaction_t * temp_xact = new transaction_t(NULL);
|
||||||
xact_temps.push_back(temp_xact);
|
xact_temps.push_back(temp_xact);
|
||||||
|
|
||||||
|
temp_xact->entry = entry;
|
||||||
temp_xact->amount = (*i).second;
|
temp_xact->amount = (*i).second;
|
||||||
temp_xact->dflags |= TRANSACTION_NO_TOTAL;
|
temp_xact->dflags |= TRANSACTION_NO_TOTAL;
|
||||||
|
|
||||||
|
|
@ -145,7 +146,8 @@ void subtotal_transactions::flush(const char * spec_fmt)
|
||||||
i != balances.end();
|
i != balances.end();
|
||||||
i++) {
|
i++) {
|
||||||
entry->date = finish;
|
entry->date = finish;
|
||||||
transaction_t temp(entry, (*i).first);
|
transaction_t temp((*i).first);
|
||||||
|
temp.entry = entry;
|
||||||
temp.total = (*i).second;
|
temp.total = (*i).second;
|
||||||
balance_t result;
|
balance_t result;
|
||||||
format_t::compute_total(result, details_t(&temp));
|
format_t::compute_total(result, details_t(&temp));
|
||||||
|
|
@ -154,9 +156,10 @@ void subtotal_transactions::flush(const char * spec_fmt)
|
||||||
for (amounts_map::const_iterator j = result.amounts.begin();
|
for (amounts_map::const_iterator j = result.amounts.begin();
|
||||||
j != result.amounts.end();
|
j != result.amounts.end();
|
||||||
j++) {
|
j++) {
|
||||||
transaction_t * xact = new transaction_t(entry, (*i).first);
|
transaction_t * xact = new transaction_t((*i).first);
|
||||||
xact_temps.push_back(xact);
|
xact_temps.push_back(xact);
|
||||||
|
|
||||||
|
xact->entry = entry;
|
||||||
xact->amount = (*j).second;
|
xact->amount = (*j).second;
|
||||||
xact->cost = (*j).second;
|
xact->cost = (*j).second;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue