Merge branch 'next'

This commit is contained in:
John Wiegley 2009-10-31 04:17:40 -04:00
commit 97a9b42b2c
15 changed files with 222 additions and 60 deletions

View file

@ -13,13 +13,13 @@ BOOST_VERSION = 1_40_0
# architecture=combined
boost-release:
(cd $(BOOST_SOURCE) && \
bjam release -j16 --prefix=$(STOW_ROOT)/boost_$(BOOST_VERSION) \
bjam release --prefix=$(STOW_ROOT)/boost_$(BOOST_VERSION) \
--build-dir=$(HOME)/Products/boost_$(BOOST_VERSION) \
--toolset=darwin --build-type=complete --layout=versioned install)
boost-debug:
(cd $(BOOST_SOURCE) && \
bjam debug -j16 --prefix=$(STOW_ROOT)/boost_$(BOOST_VERSION) \
bjam debug --prefix=$(STOW_ROOT)/boost_$(BOOST_VERSION) \
--build-dir=$(HOME)/Products/boost_$(BOOST_VERSION) \
--toolset=darwin --build-type=complete --layout=versioned \
define=_GLIBCXX_DEBUG=1 install)

View file

@ -209,7 +209,11 @@ post_handler_ptr chain_post_handlers(report_t& report,
= new forecast_posts(handler,
item_predicate(report.HANDLER(forecast_while_).str(),
report.what_to_keep()),
report);
report,
report.HANDLED(forecast_years_) ?
static_cast<std::size_t>
(report.HANDLER(forecast_years_).value.to_long()) :
5UL);
forecast_handler->add_period_xacts(report.session.journal->period_xacts);
handler.reset(forecast_handler);

View file

@ -54,6 +54,8 @@ namespace {
bool from;
optional<mask_t> account_mask;
optional<amount_t> amount;
optional<string> cost_operator;
optional<amount_t> cost;
post_template_t() : from(false) {}
};
@ -111,6 +113,10 @@ namespace {
if (post.amount)
out << _(" Amount: ") << *post.amount << std::endl;
if (post.cost)
out << _(" Cost: ") << *post.cost_operator
<< " " << *post.cost << std::endl;
}
}
}
@ -148,6 +154,8 @@ namespace {
string arg = (*begin).to_string();
if (arg == "at") {
if (begin == end)
throw std::runtime_error(_("Invalid xact command arguments"));
tmpl.payee_mask = (*++begin).to_string();
}
else if (arg == "to" || arg == "from") {
@ -155,22 +163,41 @@ namespace {
tmpl.posts.push_back(xact_template_t::post_template_t());
post = &tmpl.posts.back();
}
if (begin == end)
throw std::runtime_error(_("Invalid xact command arguments"));
post->account_mask = mask_t((*++begin).to_string());
post->from = arg == "from";
}
else if (arg == "on") {
if (begin == end)
throw std::runtime_error(_("Invalid xact command arguments"));
tmpl.date = parse_date((*++begin).to_string());
check_for_date = false;
}
else if (arg == "code") {
if (begin == end)
throw std::runtime_error(_("Invalid xact command arguments"));
tmpl.code = (*++begin).to_string();
}
else if (arg == "note") {
if (begin == end)
throw std::runtime_error(_("Invalid xact command arguments"));
tmpl.note = (*++begin).to_string();
}
else if (arg == "rest") {
; // just ignore this argument
}
else if (arg == "@" || arg == "@@") {
amount_t cost;
post->cost_operator = arg;
if (begin == end)
throw std::runtime_error(_("Invalid xact command arguments"));
arg = (*++begin).to_string();
if (! cost.parse(arg, amount_t::PARSE_SOFT_FAIL |
amount_t::PARSE_NO_MIGRATE))
throw std::runtime_error(_("Invalid xact command arguments"));
post->cost = cost;
}
else {
// Without a preposition, it is either:
//
@ -443,6 +470,25 @@ namespace {
}
}
if (post.cost) {
if (post.cost->sign() < 0)
throw parse_error(_("A posting's cost may not be negative"));
post.cost->in_place_unround();
if (*post.cost_operator == "@") {
// For the sole case where the cost might be uncommoditized,
// guarantee that the commodity of the cost after multiplication
// is the same as it was before.
commodity_t& cost_commodity(post.cost->commodity());
*post.cost *= new_post->amount;
post.cost->set_commodity(cost_commodity);
}
new_post->cost = *post.cost;
DEBUG("derive.xact", "Copied over posting cost");
}
if (found_commodity &&
! new_post->amount.is_null() &&
! new_post->amount.has_commodity()) {

View file

@ -41,7 +41,7 @@ namespace ledger {
void format_emacs_posts::write_xact(xact_t& xact)
{
out << "\"" << xact.pos->pathname << "\" "
<< (xact.pos->beg_line + 1) << " ";
<< xact.pos->beg_line << " ";
tm when = gregorian::to_tm(xact.date());
std::time_t date = std::mktime(&when); // jww (2008-04-20): Is this GMT or local?
@ -77,7 +77,7 @@ void format_emacs_posts::operator()(post_t& post)
out << "\n";
}
out << " (" << (post.pos->beg_line + 1) << " ";
out << " (" << post.pos->beg_line << " ";
out << "\"" << post.reported_account()->fullname() << "\" \""
<< post.amount << "\"";

View file

@ -871,9 +871,11 @@ void forecast_posts::flush()
date_t next = *(*least).first.next;
assert(next > begin);
if ((next - last).days() > 365 * 5) {
if (static_cast<std::size_t>((next - last).days()) >
static_cast<std::size_t>(365U) * forecast_years) {
DEBUG("filters.forecast",
"Forecast transaction exceeds 5 years beyond today");
"Forecast transaction exceeds " << forecast_years
<< " years beyond today");
pending_posts.erase(least);
continue;
}

View file

@ -764,16 +764,19 @@ public:
*/
class forecast_posts : public generate_posts
{
item_predicate pred;
scope_t& context;
item_predicate pred;
scope_t& context;
const std::size_t forecast_years;
public:
forecast_posts(post_handler_ptr handler,
const item_predicate& predicate,
scope_t& _context)
: generate_posts(handler), pred(predicate), context(_context) {
scope_t& _context,
const std::size_t _forecast_years)
: generate_posts(handler), pred(predicate), context(_context),
forecast_years(_forecast_years) {
TRACE_CTOR(forecast_posts,
"post_handler_ptr, const item_predicate&, scope_t&");
"post_handler_ptr, item_predicate, scope_t&, std::size_t");
}
virtual ~forecast_posts() throw() {
TRACE_DTOR(forecast_posts);

View file

@ -32,6 +32,7 @@
#include <system.hh>
#include "item.h"
#include "interactive.h"
namespace ledger {
@ -210,15 +211,31 @@ namespace {
return item.has_tag(args[0].as_string());
else if (args[0].is_mask())
return item.has_tag(args[0].as_mask());
} else {
return item.has_tag(args[0].to_mask(), args[1].to_mask());
else
throw_(std::logic_error,
_("Expected string for argument 1, but received %1")
<< args[0].label());
}
else if (args.size() == 2) {
if (args[0].is_mask() && args[1].is_mask())
return item.has_tag(args[0].to_mask(), args[1].to_mask());
else
throw_(std::logic_error,
_("Expected masks for arguments 1 and 2, but received %1 and %2")
<< args[0].label() << args[1].label());
}
else if (args.size() == 0) {
throw_(std::logic_error, _("Too few arguments to function"));
}
else {
throw_(std::logic_error, _("Too many arguments to function"));
}
return false;
}
value_t get_tag(call_scope_t& args) {
item_t& item(find_scope<item_t>(args));
if (optional<string> value = item.get_tag(args[0].as_string()))
value_t get_tag(call_scope_t& scope) {
in_context_t<item_t> env(scope, "s");
if (optional<string> value = env->get_tag(env.get<string>(0)))
return string_value(*value);
return false;
}

View file

@ -90,37 +90,48 @@ void posts_commodities_iterator::reset(journal_t& journal)
std::map<string, xact_t *> xacts_by_commodity;
foreach (commodity_t * comm, commodities) {
optional<commodity_t::varied_history_t&> history = comm->varied_history();
if (! history)
continue;
if (optional<commodity_t::varied_history_t&> history =
comm->varied_history()) {
account_t * account = journal.master->find_account(comm->symbol());
account_t * account = journal.master->find_account(comm->symbol());
foreach (commodity_t::base_t::history_by_commodity_map::value_type pair,
history->histories) {
foreach (commodity_t::base_t::history_map::value_type hpair,
pair.second.prices) {
xact_t * xact;
string symbol = hpair.second.commodity().symbol();
foreach (commodity_t::base_t::history_by_commodity_map::value_type pair,
history->histories) {
foreach (commodity_t::base_t::history_map::value_type hpair,
pair.second.prices) {
xact_t * xact;
string symbol = hpair.second.commodity().symbol();
std::map<string, xact_t *>::iterator i =
xacts_by_commodity.find(symbol);
if (i != xacts_by_commodity.end()) {
xact = (*i).second;
} else {
xact = &temps.create_xact();
xact_temps.push_back(xact);
xact->payee = symbol;
xact->_date = hpair.first.date();
xacts_by_commodity.insert
(std::pair<string, xact_t *>(symbol, xact));
}
std::map<string, xact_t *>::iterator i =
xacts_by_commodity.find(symbol);
if (i != xacts_by_commodity.end()) {
xact = (*i).second;
} else {
xact = &temps.create_xact();
xact_temps.push_back(xact);
xact->payee = symbol;
xact->_date = hpair.first.date();
xacts_by_commodity.insert
(std::pair<string, xact_t *>(symbol, xact));
bool post_already_exists = false;
foreach (post_t * post, xact->posts) {
if (post->_date == hpair.first.date() &&
post->amount == hpair.second) {
post_already_exists = true;
break;
}
}
if (! post_already_exists) {
post_t& temp = temps.create_post(*xact, account);
temp._date = hpair.first.date();
temp.amount = hpair.second;
temp.xdata().datetime = hpair.first;
}
}
post_t& temp = temps.create_post(*xact, account);
temp._date = hpair.first.date();
temp.amount = hpair.second;
temp.xdata().datetime = hpair.first;
}
}
}

View file

@ -76,11 +76,13 @@ int main(int argc, char * argv[], char * envp[])
::textdomain("ledger");
#endif
// Create the session object, which maintains nearly all state relating to
// this invocation of Ledger; and register all known journal parsers.
std::auto_ptr<global_scope_t> global_scope(new global_scope_t(envp));
std::auto_ptr<global_scope_t> global_scope;
try {
// Create the session object, which maintains nearly all state relating to
// this invocation of Ledger; and register all known journal parsers.
global_scope.reset(new global_scope_t(envp));
global_scope->session().set_flush_on_next_data_file(true);
// Construct an STL-style argument list from the process command arguments
@ -181,7 +183,11 @@ int main(int argc, char * argv[], char * envp[])
}
}
catch (const std::exception& err) {
global_scope->report_error(err);
if (global_scope.get())
global_scope->report_error(err);
else
std::cerr << "Exception during initialization: " << err.what()
<< std::endl;
}
catch (int _status) {
status = _status; // used for a "quick" exit, and is used only

View file

@ -116,13 +116,16 @@ void process_environment(const char ** envp, const string& tag,
const char * tag_p = tag.c_str();
string::size_type tag_len = tag.length();
assert(tag_p);
assert(tag_len > 0);
for (const char ** p = envp; *p; p++) {
if (! tag_p || std::strncmp(*p, tag_p, tag_len) == 0) {
char buf[128];
if (std::strlen(*p) >= tag_len && std::strncmp(*p, tag_p, tag_len) == 0) {
char buf[8192];
char * r = buf;
const char * q;
for (q = *p + tag_len;
*q && *q != '=' && r - buf < 128;
*q && *q != '=' && r - buf < 8191;
q++)
if (*q == '_')
*r++ = '-';

View file

@ -162,10 +162,21 @@ public:
virtual void handler(call_scope_t& args) {
if (wants_arg) {
if (args.empty() || args.size() == 1)
if (args.size() < 2)
throw_(std::runtime_error, _("No argument provided for %1") << desc());
else if (args.size() > 2)
throw_(std::runtime_error, _("To many arguments provided for %1") << desc());
else if (! args[0].is_string())
throw_(std::runtime_error, _("Context argument for %1 not a string") << desc());
on_with(args[0].as_string(), args[1]);
} else {
}
else if (args.size() < 1) {
throw_(std::runtime_error, _("No argument provided for %1") << desc());
}
else if (! args[0].is_string()) {
throw_(std::runtime_error, _("Context argument for %1 not a string") << desc());
}
else {
on_only(args[0].as_string());
}

View file

@ -235,12 +235,13 @@ value_t report_t::fn_justify(call_scope_t& scope)
return string_value(out.str());
}
value_t report_t::fn_quoted(call_scope_t& args)
value_t report_t::fn_quoted(call_scope_t& scope)
{
interactive_t args(scope, "s");
std::ostringstream out;
out << '"';
foreach (const char ch, args[0].to_string()) {
foreach (const char ch, args.get<string>(0)) {
if (ch == '"')
out << "\\\"";
else
@ -253,8 +254,7 @@ value_t report_t::fn_quoted(call_scope_t& args)
value_t report_t::fn_join(call_scope_t& scope)
{
interactive_t args(scope, "s");
interactive_t args(scope, "s");
std::ostringstream out;
foreach (const char ch, args.get<string>(0)) {
@ -315,6 +315,39 @@ value_t report_t::fn_price(call_scope_t& scope)
return args.value_at(0).price();
}
value_t report_t::fn_lot_date(call_scope_t& scope)
{
interactive_t args(scope, "v");
if (args.value_at(0).is_annotated()) {
const annotation_t& details(args.value_at(0).annotation());
if (details.date)
return *details.date;
}
return NULL_VALUE;
}
value_t report_t::fn_lot_price(call_scope_t& scope)
{
interactive_t args(scope, "v");
if (args.value_at(0).is_annotated()) {
const annotation_t& details(args.value_at(0).annotation());
if (details.price)
return *details.price;
}
return NULL_VALUE;
}
value_t report_t::fn_lot_tag(call_scope_t& scope)
{
interactive_t args(scope, "v");
if (args.value_at(0).is_annotated()) {
const annotation_t& details(args.value_at(0).annotation());
if (details.tag)
return string_value(*details.tag);
}
return NULL_VALUE;
}
namespace {
value_t fn_black(call_scope_t&) {
return string_value("black");
@ -559,6 +592,7 @@ option_t<report_t> * report_t::lookup_option(const char * p)
case 'f':
OPT(flat);
else OPT_ALT(forecast_while_, forecast_);
else OPT(forecast_years_);
else OPT(format_);
else OPT(force_color);
else OPT(force_pager);

View file

@ -160,6 +160,9 @@ public:
value_t fn_ansify_if(call_scope_t& scope);
value_t fn_percent(call_scope_t& scope);
value_t fn_price(call_scope_t& scope);
value_t fn_lot_date(call_scope_t& scope);
value_t fn_lot_price(call_scope_t& scope);
value_t fn_lot_tag(call_scope_t& scope);
value_t fn_now(call_scope_t&) {
return terminus;
@ -240,6 +243,7 @@ public:
HANDLER(force_color).report(out);
HANDLER(force_pager).report(out);
HANDLER(forecast_while_).report(out);
HANDLER(forecast_years_).report(out);
HANDLER(format_).report(out);
HANDLER(gain).report(out);
HANDLER(head_).report(out);
@ -544,6 +548,7 @@ public:
OPTION(report_t, force_color);
OPTION(report_t, force_pager);
OPTION(report_t, forecast_while_);
OPTION(report_t, forecast_years_);
OPTION(report_t, format_); // -F
OPTION_(report_t, gain, DO() { // -G

View file

@ -146,9 +146,11 @@ std::size_t session_t::read_data(const string& master_account)
cache->should_load(HANDLER(file_).data_files) &&
cache->load(journal))) {
if (price_db_path) {
if (exists(*price_db_path) && read_journal(*price_db_path) > 0)
throw_(parse_error, _("Transactions not allowed in price history file"));
journal->sources.push_back(journal_t::fileinfo_t(*price_db_path));
if (exists(*price_db_path)) {
if (read_journal(*price_db_path) > 0)
throw_(parse_error, _("Transactions not allowed in price history file"));
journal->sources.push_back(journal_t::fileinfo_t(*price_db_path));
}
HANDLER(file_).data_files.remove(*price_db_path);
}

View file

@ -580,6 +580,15 @@ inline char peek_next_nonws(std::istream& in) {
str.get(var); \
if (in.eof()) \
break; \
switch (var) { \
case 'b': var = '\b'; break; \
case 'f': var = '\f'; break; \
case 'n': var = '\n'; break; \
case 'r': var = '\r'; break; \
case 't': var = '\t'; break; \
case 'v': var = '\v'; break; \
default: break; \
} \
} \
*_p++ = var; \
var = static_cast<char>(str.peek()); \
@ -600,6 +609,15 @@ inline char peek_next_nonws(std::istream& in) {
str.get(var); \
if (in.eof()) \
break; \
switch (var) { \
case 'b': var = '\b'; break; \
case 'f': var = '\f'; break; \
case 'n': var = '\n'; break; \
case 'r': var = '\r'; break; \
case 't': var = '\t'; break; \
case 'v': var = '\v'; break; \
default: break; \
} \
idx++; \
} \
*_p++ = var; \