several fixes
This commit is contained in:
parent
9d4f839263
commit
a9b207205f
10 changed files with 98 additions and 1664 deletions
1
Makefile
1
Makefile
|
|
@ -10,6 +10,7 @@ CODE = account.cc \
|
||||||
format.cc \
|
format.cc \
|
||||||
ledger.cc \
|
ledger.cc \
|
||||||
option.cc \
|
option.cc \
|
||||||
|
parser.cc \
|
||||||
qif.cc \
|
qif.cc \
|
||||||
quotes.cc \
|
quotes.cc \
|
||||||
textual.cc \
|
textual.cc \
|
||||||
|
|
|
||||||
70
NEWS
70
NEWS
|
|
@ -15,31 +15,28 @@
|
||||||
operators are supported, as well as a few useful functions. See the
|
operators are supported, as well as a few useful functions. See the
|
||||||
README.
|
README.
|
||||||
|
|
||||||
- If the environment variable LEDGER is used, a binary cache of that
|
- If the environment variable LEDGER (or LEDGER_FILE) is used, a
|
||||||
current ledger will be kept, to speed up later queries of the same
|
binary cache of that ledger will be kept in ~/.ledger (or
|
||||||
data. The default location is in ~/.ledger, but this can be changed
|
LEDGER_CACHE), to speed up later queries of the same data. This
|
||||||
with the environment variable LEDGER_CACHE. This only happens if no
|
happens only when "-f" or "--file" is not used.
|
||||||
"-f" flag was seen (i.e., if the LEDGER environment variable is
|
|
||||||
used).
|
|
||||||
|
|
||||||
- New "-o FILE" option will output data to the given FILE. If FILE is
|
- New options:
|
||||||
"-", the output is the same as the default (stdout).
|
|
||||||
|
|
||||||
- New -j and -J options replace the old -G (gnuplot) option. -j
|
"-o FILE" outputs data to the given FILE. If "-", the output is the
|
||||||
reports the values column in a way gnuplot can consume, and -J
|
same as the default (stdout).
|
||||||
reports the totals column. An example can be found in
|
|
||||||
scripts/report.
|
|
||||||
|
|
||||||
- New "-y DATEFMT" options will change the date format used throughout
|
-j and -J options replace previous -G (gnuplot) option. -j reports
|
||||||
ledger. The default is "%Y/%m/%d".
|
the values column in a way gnuplot can consume, and -J reports the
|
||||||
|
totals column. An example can be found in scripts/report.
|
||||||
|
|
||||||
- New -Y and -W options prints yearly and weekly subtotals, just as
|
"-y DATEFMT" changes the date format used in register reports. The
|
||||||
the -M option printed monthly subtotals in the previous version.
|
default is "%Y/%m/%d".
|
||||||
|
|
||||||
- New -w report will show cumulative totals for each of the days of
|
-Y and -W print yearly and weekly subtotals, just as the -M option
|
||||||
the week.
|
printed monthly subtotals in the previous version.
|
||||||
|
-w shows cumulative totals for each of the days of the week.
|
||||||
|
|
||||||
- New "-z INTERVAL" allows for more flexible interval reporting. The
|
"-z INTERVAL" allows more flexible interval reporting. The
|
||||||
sublanguage used will probably mature over time, but for now it
|
sublanguage used will probably mature over time, but for now it
|
||||||
supports expression like:
|
supports expression like:
|
||||||
|
|
||||||
|
|
@ -48,9 +45,14 @@
|
||||||
every 3 quarters
|
every 3 quarters
|
||||||
weekly from 12/20
|
weekly from 12/20
|
||||||
|
|
||||||
Note that when using the "from" date, this does not constrain the
|
-O shows base values (this is the default, and old behavior)
|
||||||
report. It is only used for aligning report dates, for example if
|
-B shows basis cost of commodities
|
||||||
you wish weekly reporting to start on Sundays.
|
-V show market value of commodities
|
||||||
|
-G reports net gain/loss (shows commodity changes only)
|
||||||
|
-A reports average value (arithmetic mean)
|
||||||
|
-D reports deviation from the average value
|
||||||
|
-X reports the trend (average rate of change)
|
||||||
|
-Z reports the trend, with older values affecting the trend less
|
||||||
|
|
||||||
- Regexps specified after the command name now apply to account names
|
- Regexps specified after the command name now apply to account names
|
||||||
only. To search on a payee, use "--" to separate the two kinds of
|
only. To search on a payee, use "--" to separate the two kinds of
|
||||||
|
|
@ -79,19 +81,6 @@
|
||||||
option also works for balance reports, where it will show all the
|
option also works for balance reports, where it will show all the
|
||||||
account totals related to your query.
|
account totals related to your query.
|
||||||
|
|
||||||
- There are several new default reporting styles, which work both in
|
|
||||||
the balance and register reports:
|
|
||||||
|
|
||||||
-O Show base values (this is the default, and old behavior)
|
|
||||||
-B Show the basis cost of commodities
|
|
||||||
-V Show the last known market value of commodities
|
|
||||||
-G Report net gain/loss (shows commodity changes only)
|
|
||||||
-A Report average value (arithmetic mean)
|
|
||||||
-D Report deviation from the average value
|
|
||||||
-Z Report the trend (average rate of change)
|
|
||||||
-W Report the trend, with older values affecting the trend less
|
|
||||||
-X Report expected amount for the next transaction
|
|
||||||
|
|
||||||
- Automated transactions now use a single value expression as a
|
- Automated transactions now use a single value expression as a
|
||||||
predicate. This means the new syntax is:
|
predicate. This means the new syntax is:
|
||||||
|
|
||||||
|
|
@ -105,15 +94,10 @@
|
||||||
- The use of "+" and "-" in ledger files (to specify permanent
|
- The use of "+" and "-" in ledger files (to specify permanent
|
||||||
regexps) has been removed.
|
regexps) has been removed.
|
||||||
|
|
||||||
- The -G switch no longer generates gnuplot-safe data. It now reports
|
- -l now takes a value expression as the "calculation predicate".
|
||||||
totals in terms of net gain/loss.
|
To mimic the old behavior of "-l \$100", use: -d "AT<{\$100}"
|
||||||
|
|
||||||
- The -l flag now takes an expression string as a "predicate".
|
- The -S flag takes a value expression string, which yields the value
|
||||||
Therefore, to equal the old behavior of "-l $100", you would use:
|
|
||||||
|
|
||||||
-l AT<{$100}
|
|
||||||
|
|
||||||
- The -S flag now takes an expression string, which yields the value
|
|
||||||
that will be sorted on.
|
that will be sorted on.
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
|
||||||
20
binary.cc
20
binary.cc
|
|
@ -1,4 +1,5 @@
|
||||||
#include "ledger.h"
|
#include "ledger.h"
|
||||||
|
#include "binary.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
@ -16,6 +17,15 @@ namespace ledger {
|
||||||
const unsigned long binary_magic_number = 0xFFEED765;
|
const unsigned long binary_magic_number = 0xFFEED765;
|
||||||
static const unsigned long format_version = 0x0002000b;
|
static const unsigned long format_version = 0x0002000b;
|
||||||
|
|
||||||
|
bool binary_parser_t::test(std::istream& in) const
|
||||||
|
{
|
||||||
|
unsigned long magic;
|
||||||
|
in.read((char *)&magic, sizeof(magic));
|
||||||
|
in.seekg(0);
|
||||||
|
|
||||||
|
return magic == binary_magic_number;
|
||||||
|
}
|
||||||
|
|
||||||
static std::vector<account_t *> accounts;
|
static std::vector<account_t *> accounts;
|
||||||
static account_t::ident_t ident;
|
static account_t::ident_t ident;
|
||||||
static std::vector<commodity_t *> commodities;
|
static std::vector<commodity_t *> commodities;
|
||||||
|
|
@ -118,7 +128,7 @@ entry_t * read_binary_entry(std::istream& in, journal_t * journal)
|
||||||
i < count;
|
i < count;
|
||||||
i++) {
|
i++) {
|
||||||
transaction_t * xact = read_binary_transaction(in, entry);
|
transaction_t * xact = read_binary_transaction(in, entry);
|
||||||
entry->transactions.push_back(xact);
|
entry->add_transaction(xact);
|
||||||
}
|
}
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
|
|
@ -252,6 +262,14 @@ unsigned int read_binary_journal(std::istream& in,
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int binary_parser_t::parse(std::istream& in,
|
||||||
|
journal_t * journal,
|
||||||
|
account_t * master,
|
||||||
|
const std::string * original_file)
|
||||||
|
{
|
||||||
|
return read_binary_journal(in, original_file ? *original_file : "",
|
||||||
|
journal, master);
|
||||||
|
}
|
||||||
|
|
||||||
#if RELEASE_LEVEL >= ALPHA
|
#if RELEASE_LEVEL >= ALPHA
|
||||||
#define write_binary_guard(in, id) { \
|
#define write_binary_guard(in, id) { \
|
||||||
|
|
|
||||||
1
format.h
1
format.h
|
|
@ -127,7 +127,6 @@ class format_transactions : public item_handler<transaction_t>
|
||||||
}
|
}
|
||||||
xact->dflags |= TRANSACTION_DISPLAYED;
|
xact->dflags |= TRANSACTION_DISPLAYED;
|
||||||
}
|
}
|
||||||
flush();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
30
ledger.cc
30
ledger.cc
|
|
@ -174,34 +174,4 @@ entry_t * journal_t::derive_entry(strings_list::iterator i,
|
||||||
return added.release();
|
return added.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_journal_file(const std::string& path,
|
|
||||||
journal_t * journal,
|
|
||||||
account_t * master,
|
|
||||||
const std::string * original_file)
|
|
||||||
{
|
|
||||||
journal->sources.push_back(path);
|
|
||||||
|
|
||||||
if (access(path.c_str(), R_OK) == -1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
std::ifstream stream(path.c_str());
|
|
||||||
|
|
||||||
char magic[sizeof(unsigned int) + 1];
|
|
||||||
stream.read(magic, sizeof(unsigned int));
|
|
||||||
magic[sizeof(unsigned int)] = '\0';
|
|
||||||
stream.seekg(0);
|
|
||||||
|
|
||||||
if (*((unsigned int *) magic) == binary_magic_number)
|
|
||||||
return read_binary_journal(stream, original_file ? *original_file : "",
|
|
||||||
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
|
|
||||||
return parse_textual_journal(stream, journal,
|
|
||||||
master ? master : journal->master);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
24
ledger.h
24
ledger.h
|
|
@ -223,30 +223,6 @@ class journal_t
|
||||||
strings_list::iterator end) const;
|
strings_list::iterator end) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
int parse_journal_file(const std::string& path,
|
|
||||||
journal_t * journal,
|
|
||||||
account_t * master = NULL,
|
|
||||||
const std::string * original_file = NULL);
|
|
||||||
|
|
||||||
unsigned int parse_textual_journal(std::istream& in,
|
|
||||||
journal_t * ledger,
|
|
||||||
account_t * master = NULL);
|
|
||||||
|
|
||||||
extern const unsigned long binary_magic_number;
|
|
||||||
|
|
||||||
unsigned int read_binary_journal(std::istream& in,
|
|
||||||
const std::string& file,
|
|
||||||
journal_t * journal,
|
|
||||||
account_t * master = NULL);
|
|
||||||
|
|
||||||
void write_binary_journal(std::ostream& out,
|
|
||||||
journal_t * journal,
|
|
||||||
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
|
||||||
|
|
|
||||||
27
main.cc
27
main.cc
|
|
@ -1,5 +1,8 @@
|
||||||
#include "ledger.h"
|
#include "ledger.h"
|
||||||
#include "error.h"
|
#include "parser.h"
|
||||||
|
#include "textual.h"
|
||||||
|
#include "binary.h"
|
||||||
|
#include "qif.h"
|
||||||
#include "valexpr.h"
|
#include "valexpr.h"
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include "walk.h"
|
#include "walk.h"
|
||||||
|
|
@ -7,6 +10,7 @@
|
||||||
#include "option.h"
|
#include "option.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "timing.h"
|
#include "timing.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
using namespace ledger;
|
using namespace ledger;
|
||||||
|
|
||||||
|
|
@ -178,16 +182,25 @@ int main(int argc, char * argv[], char * envp[])
|
||||||
|
|
||||||
TIMER_START(parse_files);
|
TIMER_START(parse_files);
|
||||||
|
|
||||||
|
// Setup the parsers
|
||||||
|
std::auto_ptr<binary_parser_t> bin_parser(new binary_parser_t);
|
||||||
|
std::auto_ptr<qif_parser_t> qif_parser(new qif_parser_t);
|
||||||
|
std::auto_ptr<textual_parser_t> text_parser(new textual_parser_t);
|
||||||
|
|
||||||
|
parser_t::parsers.push_back(bin_parser.get());
|
||||||
|
parser_t::parsers.push_back(qif_parser.get());
|
||||||
|
parser_t::parsers.push_back(text_parser.get());
|
||||||
|
|
||||||
int entry_count = 0;
|
int entry_count = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (! config->init_file.empty())
|
if (! config->init_file.empty())
|
||||||
if (parse_journal_file(config->init_file, journal.get()))
|
if (parser_t::parse(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() &&
|
||||||
! config->data_file.empty()) {
|
! config->data_file.empty()) {
|
||||||
entry_count += parse_journal_file(config->cache_file, journal.get(),
|
entry_count += parser_t::parse(config->cache_file, journal.get(),
|
||||||
NULL, &config->data_file);
|
NULL, &config->data_file);
|
||||||
journal->sources.pop_front(); // remove cache_file
|
journal->sources.pop_front(); // remove cache_file
|
||||||
|
|
||||||
|
|
@ -206,14 +219,14 @@ int main(int argc, char * argv[], char * envp[])
|
||||||
|
|
||||||
if (config->data_file == "-") {
|
if (config->data_file == "-") {
|
||||||
use_cache = false;
|
use_cache = false;
|
||||||
entry_count += parse_textual_journal(std::cin, journal.get(), account);
|
entry_count += text_parser->parse(std::cin, journal.get(), account);
|
||||||
} else {
|
} else {
|
||||||
entry_count += parse_journal_file(config->data_file, journal.get(),
|
entry_count += parser_t::parse(config->data_file, journal.get(),
|
||||||
account);
|
account);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! config->price_db.empty())
|
if (! config->price_db.empty())
|
||||||
if (parse_journal_file(config->price_db, journal.get()))
|
if (parser_t::parse(config->price_db, journal.get()))
|
||||||
throw error("Entries not allowed in price history file");
|
throw error("Entries not allowed in price history file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -226,7 +239,7 @@ int main(int argc, char * argv[], char * envp[])
|
||||||
if (i != -1) {
|
if (i != -1) {
|
||||||
conversion[i] = ' ';
|
conversion[i] = ' ';
|
||||||
std::istringstream stream(conversion);
|
std::istringstream stream(conversion);
|
||||||
parse_textual_journal(stream, journal.get(), journal->master);
|
text_parser->parse(stream, journal.get(), journal->master);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
qif.cc
27
qif.cc
|
|
@ -1,4 +1,5 @@
|
||||||
#include "ledger.h"
|
#include "ledger.h"
|
||||||
|
#include "qif.h"
|
||||||
#include "datetime.h"
|
#include "datetime.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
@ -22,14 +23,29 @@ static inline char * get_line(std::istream& in) {
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int parse_qif_file(std::istream& in, journal_t * journal,
|
bool qif_parser_t::test(std::istream& in) const
|
||||||
account_t * master, commodity_t * def_commodity)
|
{
|
||||||
|
char magic[sizeof(unsigned int) + 1];
|
||||||
|
in.read(magic, sizeof(unsigned int));
|
||||||
|
magic[sizeof(unsigned int)] = '\0';
|
||||||
|
in.seekg(0);
|
||||||
|
|
||||||
|
return (std::strcmp(magic, "!Typ") == 0 ||
|
||||||
|
std::strcmp(magic, "\n!Ty") == 0 ||
|
||||||
|
std::strcmp(magic, "\r\n!T") == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int qif_parser_t::parse(std::istream& in,
|
||||||
|
journal_t * journal,
|
||||||
|
account_t * master,
|
||||||
|
const std::string * original_file)
|
||||||
{
|
{
|
||||||
std::auto_ptr<entry_t> entry;
|
std::auto_ptr<entry_t> entry;
|
||||||
std::auto_ptr<amount_t> amount;
|
std::auto_ptr<amount_t> amount;
|
||||||
transaction_t * xact;
|
transaction_t * xact;
|
||||||
account_t * misc = journal->find_account("Miscellaneous");
|
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
|
account_t * misc = NULL;
|
||||||
|
commodity_t * def_commodity = NULL;
|
||||||
|
|
||||||
entry.reset(new entry_t);
|
entry.reset(new entry_t);
|
||||||
xact = new transaction_t(master);
|
xact = new transaction_t(master);
|
||||||
|
|
@ -78,7 +94,8 @@ unsigned int parse_qif_file(std::istream& in, journal_t * journal,
|
||||||
case '$':
|
case '$':
|
||||||
in >> line;
|
in >> line;
|
||||||
xact->amount.parse(line);
|
xact->amount.parse(line);
|
||||||
if (def_commodity)
|
if (! def_commodity)
|
||||||
|
def_commodity = commodity_t::find_commodity("$", true);
|
||||||
xact->amount.commodity = def_commodity;
|
xact->amount.commodity = def_commodity;
|
||||||
if (c == '$')
|
if (c == '$')
|
||||||
xact->amount.negate();
|
xact->amount.negate();
|
||||||
|
|
@ -143,6 +160,8 @@ unsigned int parse_qif_file(std::istream& in, journal_t * journal,
|
||||||
|
|
||||||
case '^':
|
case '^':
|
||||||
if (xact->account == master) {
|
if (xact->account == master) {
|
||||||
|
if (! misc)
|
||||||
|
misc = master->find_account("Miscellaneous");
|
||||||
transaction_t * nxact = new transaction_t(misc);
|
transaction_t * nxact = new transaction_t(misc);
|
||||||
entry->add_transaction(nxact);
|
entry->add_transaction(nxact);
|
||||||
nxact->amount = nxact->cost = - xact->amount;
|
nxact->amount = nxact->cost = - xact->amount;
|
||||||
|
|
|
||||||
12
textual.cc
12
textual.cc
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include "ledger.h"
|
||||||
|
#include "textual.h"
|
||||||
#include "datetime.h"
|
#include "datetime.h"
|
||||||
#include "autoxact.h"
|
#include "autoxact.h"
|
||||||
#include "valexpr.h"
|
#include "valexpr.h"
|
||||||
|
|
@ -292,8 +294,10 @@ struct push_var {
|
||||||
~push_var() { var = prev; }
|
~push_var() { var = prev; }
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned int parse_textual_journal(std::istream& in, journal_t * journal,
|
unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
account_t * master)
|
journal_t * journal,
|
||||||
|
account_t * master,
|
||||||
|
const std::string * original_file)
|
||||||
{
|
{
|
||||||
static char line[MAX_LINE + 1];
|
static char line[MAX_LINE + 1];
|
||||||
char c;
|
char c;
|
||||||
|
|
@ -529,8 +533,8 @@ unsigned int parse_textual_journal(std::istream& in, journal_t * journal,
|
||||||
|
|
||||||
push_var<unsigned int> save_linenum(linenum);
|
push_var<unsigned int> save_linenum(linenum);
|
||||||
push_var<std::string> save_path(path);
|
push_var<std::string> save_path(path);
|
||||||
count += parse_journal_file(skip_ws(line), journal,
|
count += parser_t::parse(skip_ws(line), journal,
|
||||||
account_stack.front());
|
account_stack.front());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue