Merge branch 'next'

This commit is contained in:
John Wiegley 2010-03-17 06:22:43 -04:00
commit 7ca8149ec5
137 changed files with 2256 additions and 349 deletions

10
.gitignore vendored
View file

@ -53,6 +53,16 @@
/doc/ledger.info
/doc/refman.pdf
/doc/report/
/doc/*.aux
/doc/*.cp
/doc/*.fn
/doc/*.ky
/doc/*.log
/doc/*.pdf
/doc/*.pg
/doc/*.toc
/doc/*.tp
/doc/*.vr
/expr_tests
/libtool
/math_tests

View file

@ -5,7 +5,7 @@
@dircategory User Applications
@copying
Copyright (c) 2003-2009, John Wiegley. All rights reserved.
Copyright (c) 2003-2010, 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
@ -35,7 +35,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@end copying
@documentencoding iso-8859-1
@documentencoding utf-8
@iftex
@finalout
@ -4107,4 +4107,19 @@ parser_t
@section General Utility
@c data: foo
@smallexample
2004/05/01 * Checking balance
Assets:Bank:Checking $1,000.00
Equity:Opening Balances
@end smallexample
@c smex utility-1: $LEDGER -f $foo bal
@smallexample
$1,000.00 Assets:Bank:Checking
$-1,000.00 Equity:Opening Balances
--------------------
0
@end smallexample
@bye

122
lisp/ldg-post.el Normal file
View file

@ -0,0 +1,122 @@
(require 'ldg-regex)
(defgroup ledger-post nil
""
:group 'ledger)
(defcustom ledger-post-auto-adjust-amounts t
"If non-nil, ."
:type 'boolean
:group 'ledger-post)
(declare-function iswitchb-read-buffer "iswitchb"
(prompt &optional default require-match start matches-set))
(defvar iswitchb-temp-buflist)
(defvar ledger-post-current-list nil)
(defun ledger-post-find-all ()
(let ((origin (point))
(ledger-post-list nil)
account-path elements)
(save-excursion
(goto-char (point-min))
(while (re-search-forward
"^[ \t]+\\([*!]\\s-+\\)?[[(]?\\(.+?\\)\\(\t\\|\n\\| [ \t]\\)" nil t)
(unless (and (>= origin (match-beginning 0))
(< origin (match-end 0)))
(setq account-path (match-string-no-properties 2))
(unless (string-match "\\`\\s-*;" account-path)
(add-to-list 'ledger-post-list account-path))))
(setq ledger-post-current-list
(nreverse ledger-post-list)))))
(defun ledger-post-completing-read (prompt choices)
"Use iswitchb as a completing-read replacement to choose from choices.
PROMPT is a string to prompt with. CHOICES is a list of strings
to choose from."
(let* ((iswitchb-use-virtual-buffers nil)
(iswitchb-make-buflist-hook
(lambda ()
(setq iswitchb-temp-buflist choices))))
(iswitchb-read-buffer prompt)))
(defun ledger-post-pick-account ()
(interactive)
(let* ((account
(ledger-post-completing-read "Account: "
(or ledger-post-current-list
(ledger-post-find-all))))
(account-len (length account))
(pos (point)))
(goto-char (line-beginning-position))
(when (re-search-forward ledger-regex-post-line (line-end-position) t)
(let ((existing-len (length (match-string 3))))
(goto-char (match-beginning 3))
(delete-region (match-beginning 3) (match-end 3))
(insert account)
(cond
((> existing-len account-len)
(insert (make-string (- existing-len account-len) ? )))
((< existing-len account-len)
(dotimes (n (- account-len existing-len))
(if (looking-at "[ \t]\\( [ \t]\\|\t\\)")
(delete-char 1)))))))
(goto-char pos)))
(defun ledger-post-align-amount ()
(interactive)
(save-excursion
(set-mark (line-beginning-position))
(goto-char (1+ (line-end-position)))
(ledger-align-amounts)))
(defun ledger-post-maybe-align (beg end len)
(save-excursion
(goto-char beg)
(when (< end (line-end-position))
(goto-char (line-beginning-position))
(if (looking-at ledger-regex-post-line)
(ledger-post-align-amount)))))
(defun ledger-post-edit-amount ()
(interactive)
(goto-char (line-beginning-position))
(when (re-search-forward ledger-regex-post-line (line-end-position) t)
(goto-char (match-end 3))
(when (re-search-forward "[-.,0-9]+" (line-end-position) t)
(let ((val (match-string 0)))
(goto-char (match-beginning 0))
(delete-region (match-beginning 0) (match-end 0))
(calc)
(while (string-match "," val)
(setq val (replace-match "" nil nil val)))
(calc-eval val 'push)))))
(defun ledger-post-prev-xact ()
(interactive)
(backward-paragraph)
(when (re-search-backward ledger-regex-xact-line nil t)
(goto-char (match-beginning 0))
(re-search-forward ledger-regex-post-line)
(goto-char (match-end 3))))
(defun ledger-post-next-xact ()
(interactive)
(when (re-search-forward ledger-regex-xact-line nil t)
(goto-char (match-beginning 0))
(re-search-forward ledger-regex-post-line)
(goto-char (match-end 3))))
(defun ledger-post-setup ()
(let ((map (current-local-map)))
(define-key map [(meta ?p)] 'ledger-post-prev-xact)
(define-key map [(meta ?n)] 'ledger-post-next-xact)
(define-key map [(control ?c) (control ?c)] 'ledger-post-pick-account)
(define-key map [(control ?c) (control ?e)] 'ledger-post-edit-amount))
(if ledger-post-auto-adjust-amounts
(add-hook 'after-change-functions 'ledger-post-maybe-align t t)))
(add-hook 'ledger-mode-hook 'ledger-post-setup)
(provide 'ldg-post)

167
lisp/ldg-regex.el Normal file
View file

@ -0,0 +1,167 @@
(require 'rx)
(defconst ledger-regex-date
(let ((sep '(or ?- (any ?. ?/)))) ; can't do (any ?- ?. ?/) due to bug
(rx (group
(and (? (= 4 num)
(eval sep))
(and num (? num))
(eval sep)
(and num (? num))))))
"Match a single date, in its 'written' form.")
(defconst ledger-regex-date-group 1)
(defconst ledger-regex-date-group--count 1)
(defconst ledger-regex-full-date
(macroexpand
`(rx (and (regexp ,ledger-regex-date)
(? (and ?= (regexp ,ledger-regex-date))))))
"Match a compound date, of the form ACTUAL=EFFECTIVE")
(defconst ledger-regex-full-date-group-actual
ledger-regex-date-group)
(defconst ledger-regex-full-date-group-effective
(+ ledger-regex-date-group--count
ledger-regex-date-group))
(defconst ledger-regex-full-date-group--count
(* 2 ledger-regex-date-group--count))
(defconst ledger-regex-state
(rx (group (any ?! ?*))))
(defconst ledger-regex-state-group 1)
(defconst ledger-regex-state-group--count 1)
(defconst ledger-regex-code
(rx (and ?\( (group (+? (not (any ?\))))) ?\))))
(defconst ledger-regex-code-group 1)
(defconst ledger-regex-code-group--count 1)
(defconst ledger-regex-long-space
(rx (and (*? space)
(or (and ? (or ? ?\t)) ?\t))))
(defconst ledger-regex-note
(rx (group (+ nonl))))
(defconst ledger-regex-note-group 1)
(defconst ledger-regex-note-group--count 1)
(defconst ledger-regex-end-note
(macroexpand `(rx (and (regexp ,ledger-regex-long-space) ?\;
(regexp ,ledger-regex-note)))))
(defconst ledger-regex-end-note-group
ledger-regex-note-group)
(defconst ledger-regex-end-note-group--count
ledger-regex-note-group--count)
(defconst ledger-regex-full-note
(macroexpand `(rx (and line-start (+ space)
?\; (regexp ,ledger-regex-note)))))
(defconst ledger-regex-full-note-group
ledger-regex-note-group)
(defconst ledger-regex-full-note-group--count
ledger-regex-note-group--count)
(defconst ledger-regex-xact-line
(macroexpand
`(rx (and line-start
(regexp ,ledger-regex-full-date)
(? (and (+ space) (regexp ,ledger-regex-state)))
(? (and (+ space) (regexp ,ledger-regex-code)))
(+ space) (+? nonl)
(? (regexp ,ledger-regex-end-note))
line-end))))
(defconst ledger-regex-xact-line-group-actual-date
ledger-regex-full-date-group-actual)
(defconst ledger-regex-xact-line-group-effective-date
ledger-regex-full-date-group-effective)
(defconst ledger-regex-xact-line-group-state
(+ ledger-regex-full-date-group--count
ledger-regex-state-group))
(defconst ledger-regex-xact-line-group-code
(+ ledger-regex-full-date-group--count
ledger-regex-state-group--count
ledger-regex-code-group))
(defconst ledger-regex-xact-line-group-note
(+ ledger-regex-full-date-group--count
ledger-regex-state-group--count
ledger-regex-code-group--count
ledger-regex-note-group))
(defconst ledger-regex-full-note-group--count
(+ ledger-regex-full-date-group--count
ledger-regex-state-group--count
ledger-regex-code-group--count
ledger-regex-note-group--count))
(defun ledger-regex-xact-line-actual-date
(&optional string)
(match-string ledger-regex-xact-line-group-actual-date string))
(defconst ledger-regex-account
(rx (group (and (not (any ?:)) (*? nonl)))))
(defconst ledger-regex-full-account
(macroexpand
`(rx (and (group (? (any ?\[ ?\))))
(regexp ,ledger-regex-account)
(? (any ?\] ?\)))))))
(defconst ledger-regex-commodity
(rx (or (and ?\" (+ (not (any ?\"))) ?\")
(not (any space ?\n
digit
?- ?\[ ?\]
?. ?, ?\; ?+ ?* ?/ ?^ ?? ?: ?& ?| ?! ?=
?\< ?\> ?\{ ?\} ?\( ?\) ?@)))))
(defconst ledger-regex-amount
(rx (and (? ?-)
(and (+ digit)
(*? (and (any ?. ?,) (+ digit))))
(? (and (any ?. ?,) (+ digit))))))
(defconst ledger-regex-commoditized-amount
(macroexpand
`(rx (or (and (regexp ,ledger-regex-commodity)
(*? space)
(regexp ,ledger-regex-amount))
(and (regexp ,ledger-regex-amount)
(*? space)
(regexp ,ledger-regex-commodity))))))
(defconst ledger-regex-commodity-annotations
(macroexpand
`(rx (* (+ space)
(or (and ?\{ (regexp ,ledger-regex-commoditized-amount) ?\})
(and ?\[ (regexp ,ledger-regex-date) ?\])
(and ?\( (not (any ?\))) ?\)))))))
(defconst ledger-regex-cost
(macroexpand
`(rx (and (or "@" "@@") (+ space)
(regexp ,ledger-regex-commoditized-amount)))))
(defconst ledger-regex-balance-assertion
(macroexpand
`(rx (and ?= (+ space)
(regexp ,ledger-regex-commoditized-amount)))))
(defconst ledger-regex-full-amount
(macroexpand `(rx (group (+? (not (any ?\;)))))))
(defconst ledger-regex-post-line
(macroexpand
`(rx (and line-start
(? (and (+ space) (regexp ,ledger-regex-state)))
(+ space) (regexp ,ledger-regex-full-account)
(+ space) (regexp ,ledger-regex-full-amount)
(? (regexp ,ledger-regex-end-note))
line-end))))
(provide 'ldg-regex)

121
lisp/ldg-texi.el Normal file
View file

@ -0,0 +1,121 @@
(defvar ledger-path "/Users/johnw/bin/ledger")
(defvar ledger-sample-doc-path "/Users/johnw/src/ledger/doc/sample.dat")
(defvar ledger-normalization-args "--args-only --columns 80")
(defun ledger-texi-write-test (name command input output &optional category)
(let ((buf (current-buffer)))
(with-current-buffer (find-file-noselect
(expand-file-name (concat name ".test") category))
(erase-buffer)
(let ((case-fold-search nil))
(if (string-match "\\$LEDGER\\s-+" command)
(setq command (replace-match "" t t command)))
(if (string-match " -f \\$\\([-a-z]+\\)" command)
(setq command (replace-match "" t t command))))
(insert command ?\n)
(insert "<<<" ?\n)
(insert input)
(insert ">>>1" ?\n)
(insert output)
(insert ">>>2" ?\n)
(insert "=== 0" ?\n)
(save-buffer)
(unless (eq buf (current-buffer))
(kill-buffer (current-buffer))))))
(defun ledger-texi-update-test ()
(interactive)
(let ((details (ledger-texi-test-details))
(name (file-name-sans-extension
(file-name-nondirectory (buffer-file-name)))))
(ledger-texi-write-test
name (nth 0 details)
(nth 1 details)
(ledger-texi-invoke-command
(ledger-texi-expand-command
(nth 0 details)
(ledger-texi-write-test-data name (nth 1 details)))))))
(defun ledger-texi-test-details ()
(goto-char (point-min))
(let ((command (buffer-substring (point) (line-end-position)))
input output)
(re-search-forward "^<<<")
(let ((input-beg (1+ (match-end 0))))
(re-search-forward "^>>>1")
(let ((output-beg (1+ (match-end 0))))
(setq input (buffer-substring input-beg (match-beginning 0)))
(re-search-forward "^>>>2")
(setq output (buffer-substring output-beg (match-beginning 0)))
(list command input output)))))
(defun ledger-texi-expand-command (command data-file)
(if (string-match "\\$LEDGER" command)
(replace-match (format "%s -f \"%s\" %s" ledger-path
data-file ledger-normalization-args) t t command)
(concat (format "%s -f \"%s\" %s " ledger-path
data-file ledger-normalization-args) command)))
(defun ledger-texi-invoke-command (command)
(with-temp-buffer (shell-command command t (current-buffer))
(if (= (point-min) (point-max))
(progn
(push-mark nil t)
(message "Command '%s' yielded no result at %d" command (point))
(ding))
(buffer-string))))
(defun ledger-texi-write-test-data (name input)
(let ((path (expand-file-name name temporary-file-directory)))
(with-current-buffer (find-file-noselect path)
(erase-buffer)
(insert input)
(save-buffer))
path))
(defun ledger-texi-update-examples ()
(interactive)
(save-excursion
(goto-char (point-min))
(while (re-search-forward "^@c \\(\\(?:sm\\)?ex\\) \\(\\S-+\\): \\(.*\\)" nil t)
(let ((section (match-string 1))
(example-name (match-string 2))
(command (match-string 3)) expanded-command
(data-file ledger-sample-doc-path)
input output)
(goto-char (match-end 0))
(forward-line)
(when (looking-at "@\\(\\(?:small\\)?example\\)")
(let ((beg (point)))
(re-search-forward "^@end \\(\\(?:small\\)?example\\)")
(delete-region beg (1+ (point)))))
(when (let ((case-fold-search nil))
(string-match " -f \\$\\([-a-z]+\\)" command))
(let ((label (match-string 1 command)))
(setq command (replace-match "" t t command))
(save-excursion
(goto-char (point-min))
(search-forward (format "@c data: %s" label))
(re-search-forward "@\\(\\(?:small\\)?example\\)")
(forward-line)
(let ((beg (point)))
(re-search-forward "@end \\(\\(?:small\\)?example\\)")
(setq data-file (ledger-texi-write-test-data
(format "%s.dat" label)
(buffer-substring-no-properties
beg (match-beginning 0))))))))
(let ((section-name (if (string= section "smex")
"smallexample"
"example"))
(output (ledger-texi-invoke-command
(ledger-texi-expand-command command data-file))))
(insert "@" section-name ?\n output
"@end " section-name ?\n))
;; Update the regression test associated with this example
(ledger-texi-write-test example-name command input output
"../test/manual")))))
(provide 'ldg-texi)

View file

@ -73,7 +73,7 @@
(require 'esh-arg)
(require 'pcomplete)
(defvar ledger-version "1.2"
(defvar ledger-version "1.3"
"The version of ledger.el currently loaded")
(defgroup ledger nil
@ -1226,7 +1226,7 @@ the default."
"Align amounts in the current region.
This is done so that the last digit falls in COLUMN, which defaults to 52."
(interactive "p")
(if (= column 1)
(if (or (null column) (= column 1))
(setq column 52))
(save-excursion
(let* ((mark-first (< (mark) (point)))

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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
@ -112,10 +112,12 @@ post_handler_ptr chain_post_handlers(report_t& report,
else
handler.reset(new sort_posts(handler, report.HANDLER(sort_).str()));
}
#if 0
else if (! report.HANDLED(period_) &&
! report.HANDLED(unsorted)) {
handler.reset(new sort_posts(handler, "date"));
}
#endif
// collapse_posts causes xacts with multiple posts to appear as xacts
// with a subtotaled post for each commodity used.
@ -167,7 +169,7 @@ post_handler_ptr chain_post_handlers(report_t& report,
}
else if (report.HANDLED(pivot_)) {
string pivot = report.HANDLER(pivot_).str();
pivot = string("\"") + pivot + ":\" + tag(/" + pivot + "/)";
pivot = string("\"") + pivot + ":\" + tag(\"" + pivot + "\")";
handler.reset(new transfer_details(handler, transfer_details::SET_ACCOUNT,
report.session.journal->master, pivot,
report));

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

156
src/convert.cc Normal file
View file

@ -0,0 +1,156 @@
/*
* Copyright (c) 2003-2010, 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>
#include "convert.h"
#include "csv.h"
#include "scope.h"
#include "interactive.h"
#include "iterators.h"
#include "report.h"
#include "xact.h"
#include "print.h"
#include "lookup.h"
namespace ledger {
value_t convert_command(call_scope_t& scope)
{
interactive_t args(scope, "s");
report_t& report(find_scope<report_t>(scope));
journal_t& journal(*report.session.journal.get());
string bucket_name;
if (report.HANDLED(account_))
bucket_name = report.HANDLER(account_).str();
else
bucket_name = "Equity:Unknown";
account_t * bucket = journal.master->find_account(bucket_name);
account_t * unknown = journal.master->find_account(_("Expenses:Unknown"));
// Make an amounts mapping for the account under consideration
typedef std::map<value_t, std::list<post_t *> > post_map_t;
post_map_t post_map;
xacts_iterator journal_iter(journal);
while (xact_t * xact = journal_iter()) {
post_t * post = NULL;
xact_posts_iterator xact_iter(*xact);
while ((post = xact_iter()) != NULL) {
if (post->account == bucket)
break;
}
if (post) {
post_map_t::iterator i = post_map.find(post->amount);
if (i == post_map.end()) {
std::list<post_t *> post_list;
post_list.push_back(post);
post_map.insert(post_map_t::value_type(post->amount, post_list));
} else {
(*i).second.push_back(post);
}
}
}
// Create a flat list o
xacts_list current_xacts(journal.xacts_begin(), journal.xacts_end());
// Read in the series of transactions from the CSV file
print_xacts formatter(report);
ifstream data(path(args.get<string>(0)));
csv_reader reader(data);
while (xact_t * xact = reader.read_xact(journal, bucket)) {
bool matched = false;
post_map_t::iterator i = post_map.find(- xact->posts.front()->amount);
if (i != post_map.end()) {
std::list<post_t *>& post_list((*i).second);
foreach (post_t * post, post_list) {
if (xact->code && post->xact->code &&
*xact->code == *post->xact->code) {
matched = true;
break;
}
else if (xact->actual_date() == post->actual_date()) {
matched = true;
break;
}
}
}
if (matched) {
DEBUG("convert.csv", "Ignored xact with code: " << *xact->code);
delete xact; // ignore it
}
else {
if (xact->posts.front()->account == NULL) {
xacts_iterator xi;
xi.xacts_i = current_xacts.begin();
xi.xacts_end = current_xacts.end();
xi.xacts_uninitialized = false;
// jww (2010-03-07): Bind this logic to an option: --auto-match
if (account_t * acct =
lookup_probable_account(xact->payee, xi, bucket).second)
xact->posts.front()->account = acct;
else
xact->posts.front()->account = unknown;
}
if (! journal.add_xact(xact)) {
delete xact;
throw_(std::runtime_error,
_("Failed to finalize derived transaction (check commodities)"));
}
else {
xact_posts_iterator xact_iter(*xact);
while (post_t * post = xact_iter())
formatter(*post);
}
}
}
formatter.flush();
// If not, transform the payee according to regexps
// Set the account to a default vaule, then transform the account according
// to the payee
// Print out the final form of the transaction
return true;
}
} // namespace ledger

55
src/convert.h Normal file
View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2003-2010, 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.
*/
/**
* @addtogroup data
*/
/**
* @file convert.h
* @author John Wiegley
*
* @ingroup data
*/
#ifndef _CONVERT_H
#define _CONVERT_H
#include "value.h"
namespace ledger {
class call_scope_t;
value_t convert_command(call_scope_t& scope);
} // namespace ledger
#endif // _CONVERT_H

298
src/csv.cc Normal file
View file

@ -0,0 +1,298 @@
/*
* Copyright (c) 2003-2010, 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>
#include "csv.h"
#include "xact.h"
#include "post.h"
#include "account.h"
#include "journal.h"
#include "pool.h"
namespace ledger {
string csv_reader::read_field(std::istream& in)
{
string field;
char c;
if (in.peek() == '"' || in.peek() == '|') {
in.get(c);
char x;
while (in.good() && ! in.eof()) {
in.get(x);
if (x == '\\') {
in.get(x);
}
else if (x == '"' && in.peek() == '"') {
in.get(x);
}
else if (x == c) {
if (x == '|')
in.unget();
else if (in.peek() == ',')
in.get(c);
break;
}
if (x != '\0')
field += x;
}
}
else {
while (in.good() && ! in.eof()) {
in.get(c);
if (c == ',')
break;
if (c != '\0')
field += c;
}
}
trim(field);
return field;
}
char * csv_reader::next_line(std::istream& in)
{
static char linebuf[MAX_LINE + 1];
while (in.good() && ! in.eof() && in.peek() == '#')
in.getline(linebuf, MAX_LINE);
if (! in.good() || in.eof())
return NULL;
in.getline(linebuf, MAX_LINE);
return linebuf;
}
void csv_reader::read_index(std::istream& in)
{
char * line = next_line(in);
if (! line)
return;
std::istringstream instr(line);
while (instr.good() && ! instr.eof()) {
string field = read_field(instr);
names.push_back(field);
if (date_mask.match(field))
index.push_back(FIELD_DATE);
else if (date_eff_mask.match(field))
index.push_back(FIELD_DATE_EFF);
else if (code_mask.match(field))
index.push_back(FIELD_CODE);
else if (payee_mask.match(field))
index.push_back(FIELD_PAYEE);
else if (amount_mask.match(field))
index.push_back(FIELD_AMOUNT);
else if (cost_mask.match(field))
index.push_back(FIELD_COST);
else if (total_mask.match(field))
index.push_back(FIELD_TOTAL);
else if (note_mask.match(field))
index.push_back(FIELD_NOTE);
else
index.push_back(FIELD_UNKNOWN);
DEBUG("csv.parse", "Header field: " << field);
}
}
xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket)
{
restart:
char * line = next_line(in);
if (! line || index.empty())
return NULL;
std::istringstream instr(line);
std::auto_ptr<xact_t> xact(new xact_t);
std::auto_ptr<post_t> post(new post_t);
xact->set_state(item_t::CLEARED);
xact->pos = position_t();
xact->pos->pathname = "jww (2010-03-05): unknown";
xact->pos->beg_pos = in.tellg();
xact->pos->beg_line = 0;
xact->pos->sequence = 0;
post->xact = xact.get();
#if 0
post->pos = position_t();
post->pos->pathname = pathname;
post->pos->beg_pos = line_beg_pos;
post->pos->beg_line = linenum;
post->pos->sequence = context.sequence++;
#endif
post->set_state(item_t::CLEARED);
post->account = NULL;
int n = 0;
amount_t amt;
string total;
while (instr.good() && ! instr.eof()) {
string field = read_field(instr);
switch (index[n]) {
case FIELD_DATE:
if (field.empty())
goto restart;
try {
xact->_date = parse_date(field);
}
catch (date_error&) {
goto restart;
}
break;
case FIELD_DATE_EFF:
xact->_date_eff = parse_date(field);
break;
case FIELD_CODE:
if (! field.empty())
xact->code = field;
break;
case FIELD_PAYEE: {
bool found = false;
foreach (payee_mapping_t& value, journal.payee_mappings) {
DEBUG("csv.mappings", "Looking for payee mapping: " << value.first);
if (value.first.match(field)) {
xact->payee = value.second;
found = true;
break;
}
}
if (! found)
xact->payee = field;
break;
}
case FIELD_AMOUNT: {
std::istringstream amount_str(field);
amt.parse(amount_str, PARSE_NO_REDUCE);
if (! amt.has_commodity() &&
commodity_pool_t::current_pool->default_commodity)
amt.set_commodity(*commodity_pool_t::current_pool->default_commodity);
post->amount = amt;
break;
}
case FIELD_COST: {
std::istringstream amount_str(field);
amt.parse(amount_str, PARSE_NO_REDUCE);
if (! amt.has_commodity() &&
commodity_pool_t::current_pool->default_commodity)
amt.set_commodity
(*commodity_pool_t::current_pool->default_commodity);
post->cost = amt;
break;
}
case FIELD_TOTAL:
total = field;
break;
case FIELD_NOTE:
xact->note = field;
break;
case FIELD_UNKNOWN:
if (! names[n].empty() && ! field.empty())
xact->set_tag(names[n], field);
break;
}
n++;
}
#if 0
xact->set_tag(_("Imported"),
string(format_date(CURRENT_DATE(), FMT_WRITTEN)));
xact->set_tag(_("Original"), string(line));
xact->set_tag(_("SHA1"), string(sha1sum(line)));
#endif
// Translate the account name, if we have enough information to do so
foreach (account_mapping_t& value, journal.account_mappings) {
if (value.first.match(xact->payee)) {
post->account = value.second;
break;
}
}
xact->add_post(post.release());
// Create the "balancing post", which refers to the account for this data
post.reset(new post_t);
post->xact = xact.get();
#if 0
post->pos = position_t();
post->pos->pathname = pathname;
post->pos->beg_pos = line_beg_pos;
post->pos->beg_line = linenum;
post->pos->sequence = context.sequence++;
#endif
post->set_state(item_t::CLEARED);
post->account = bucket;
if (! amt.is_null())
post->amount = - amt;
if (! total.empty()) {
std::istringstream assigned_amount_str(total);
amt.parse(assigned_amount_str, PARSE_NO_REDUCE);
if (! amt.has_commodity() &&
commodity_pool_t::current_pool->default_commodity)
amt.set_commodity(*commodity_pool_t::current_pool->default_commodity);
post->assigned_amount = amt;
}
xact->add_post(post.release());
return xact.release();
}
} // namespace ledger

110
src/csv.h Normal file
View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2003-2010, 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.
*/
/**
* @addtogroup data
*/
/**
* @file csv.h
* @author John Wiegley
*
* @ingroup data
*/
#ifndef _CSV_H
#define _CSV_H
#include "value.h"
namespace ledger {
class xact_t;
class journal_t;
class account_t;
class csv_reader
{
static const std::size_t MAX_LINE = 1024;
std::istream& in;
enum headers_t {
FIELD_DATE = 0,
FIELD_DATE_EFF,
FIELD_CODE,
FIELD_PAYEE,
FIELD_AMOUNT,
FIELD_COST,
FIELD_TOTAL,
FIELD_NOTE,
FIELD_UNKNOWN
};
mask_t date_mask;
mask_t date_eff_mask;
mask_t code_mask;
mask_t payee_mask;
mask_t amount_mask;
mask_t cost_mask;
mask_t total_mask;
mask_t note_mask;
std::vector<int> index;
std::vector<string> names;
std::vector<string> fields;
typedef std::map<string, string> string_map;
public:
csv_reader(std::istream& _in)
: in(_in),
date_mask("date"),
date_eff_mask("posted( ?date)?"),
code_mask("code"),
payee_mask("(payee|desc(ription)?|title)"),
amount_mask("amount"),
cost_mask("cost"),
total_mask("total"),
note_mask("note") {
read_index(in);
}
string read_field(std::istream& in);
char * next_line(std::istream& in);
void read_index(std::istream& in);
xact_t * read_xact(journal_t& journal, account_t * bucket);
};
} // namespace ledger
#endif // _CSV_H

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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
@ -38,7 +38,8 @@
#include "journal.h"
#include "session.h"
#include "report.h"
#include "output.h"
#include "lookup.h"
#include "print.h"
namespace ledger {
@ -240,20 +241,27 @@ void draft_t::parse_args(const value_t& args)
xact_t * draft_t::insert(journal_t& journal)
{
if (tmpl->payee_mask.empty())
throw std::runtime_error(_("xact' command requires at least a payee"));
throw std::runtime_error(_("'xact' command requires at least a payee"));
xact_t * matching = NULL;
xact_t * matching = NULL;
std::auto_ptr<xact_t> added(new xact_t);
for (xacts_list::reverse_iterator j = journal.xacts.rbegin();
j != journal.xacts.rend();
j++) {
if (tmpl->payee_mask.match((*j)->payee)) {
matching = *j;
DEBUG("derive.xact",
"Found payee match: transaction on line " << (*j)->pos->beg_line);
break;
xacts_iterator xi(journal);
if (xact_t * xact = lookup_probable_account(tmpl->payee_mask.str(), xi).first) {
DEBUG("derive.xact", "Found payee by lookup: transaction on line "
<< xact->pos->beg_line);
matching = xact;
} else {
for (xacts_list::reverse_iterator j = journal.xacts.rbegin();
j != journal.xacts.rend();
j++) {
if (tmpl->payee_mask.match((*j)->payee)) {
matching = *j;
DEBUG("derive.xact",
"Found payee match: transaction on line " << (*j)->pos->beg_line);
break;
}
}
}
@ -269,15 +277,15 @@ xact_t * draft_t::insert(journal_t& journal)
if (matching) {
added->payee = matching->payee;
added->code = matching->code;
added->note = matching->note;
//added->code = matching->code;
//added->note = matching->note;
#if defined(DEBUG_ON)
DEBUG("derive.xact", "Setting payee from match: " << added->payee);
if (added->code)
DEBUG("derive.xact", "Setting code from match: " << *added->code);
if (added->note)
DEBUG("derive.xact", "Setting note from match: " << *added->note);
//if (added->code)
// DEBUG("derive.xact", "Setting code from match: " << *added->code);
//if (added->note)
// DEBUG("derive.xact", "Setting note from match: " << *added->note);
#endif
} else {
added->payee = tmpl->payee_mask.str();
@ -520,10 +528,7 @@ value_t xact_command(call_scope_t& args)
// Only consider actual postings for the "xact" command
report.HANDLER(limit_).on(string("#xact"), "actual");
report.xact_report(post_handler_ptr
(new format_posts(report,
report.HANDLER(print_format_).str())),
*new_xact);
report.xact_report(post_handler_ptr(new print_xacts(report)), *new_xact);
return true;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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
@ -107,6 +107,9 @@ void truncate_xacts::flush()
void truncate_xacts::operator()(post_t& post)
{
if (completed)
return;
if (last_xact != post.xact) {
if (last_xact)
xacts_seen++;
@ -114,8 +117,11 @@ void truncate_xacts::operator()(post_t& post)
}
if (tail_count == 0 && head_count > 0 &&
static_cast<int>(xacts_seen) >= head_count)
static_cast<int>(xacts_seen) >= head_count) {
flush();
completed = true;
return;
}
posts.push_back(&post);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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
@ -125,8 +125,9 @@ public:
class truncate_xacts : public item_handler<post_t>
{
int head_count;
int tail_count;
int head_count;
int tail_count;
bool completed;
posts_list posts;
std::size_t xacts_seen;
@ -139,7 +140,7 @@ public:
int _head_count, int _tail_count)
: item_handler<post_t>(handler),
head_count(_head_count), tail_count(_tail_count),
xacts_seen(0), last_xact(NULL) {
completed(false), xacts_seen(0), last_xact(NULL) {
TRACE_CTOR(truncate_xacts, "post_handler_ptr, int, int");
}
virtual ~truncate_xacts() {

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -110,7 +110,7 @@ public:
out <<
"Ledger " << ledger::version << _(", the command-line accounting tool");
out <<
_("\n\nCopyright (c) 2003-2009, John Wiegley. All rights reserved.\n\n\
_("\n\nCopyright (c) 2003-2010, John Wiegley. All rights reserved.\n\n\
This program is made available under the terms of the BSD Public License.\n\
See LICENSE file included with the distribution for details and disclaimer.");
out << std::endl;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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
@ -65,8 +65,8 @@ bool item_t::has_tag(const mask_t& tag_mask,
if (tag_mask.match(data.first)) {
if (! value_mask)
return true;
else if (data.second)
return value_mask->match(*data.second);
else if (data.second.first)
return value_mask->match(*data.second.first);
}
}
}
@ -81,7 +81,7 @@ optional<string> item_t::get_tag(const string& tag) const
string_map::const_iterator i = metadata->find(tag);
if (i != metadata->end()) {
DEBUG("item.meta", "Found the item!");
return (*i).second;
return (*i).second.first;
}
}
return none;
@ -94,28 +94,44 @@ optional<string> item_t::get_tag(const mask_t& tag_mask,
foreach (const string_map::value_type& data, *metadata) {
if (tag_mask.match(data.first) &&
(! value_mask ||
(data.second && value_mask->match(*data.second))))
return data.second;
(data.second.first && value_mask->match(*data.second.first))))
return data.second.first;
}
}
return none;
}
void item_t::set_tag(const string& tag,
const optional<string>& value)
item_t::string_map::iterator
item_t::set_tag(const string& tag,
const optional<string>& value,
const bool overwrite_existing)
{
assert(! tag.empty());
if (! metadata)
metadata = string_map();
DEBUG("item.meta", "Setting tag '" << tag << "' to value '"
<< (value ? *value : string("<none>")) << "'");
std::pair<string_map::iterator, bool> result
= metadata->insert(string_map::value_type(tag, value));
assert(result.second);
optional<string> data = value;
if (data && data->empty())
data = none;
string_map::iterator i = metadata->find(tag);
if (i == metadata->end()) {
std::pair<string_map::iterator, bool> result
= metadata->insert(string_map::value_type(tag, tag_data_t(data, false)));
assert(result.second);
return result.first;
} else {
if (overwrite_existing)
(*i).second = tag_data_t(data, false);
return i;
}
}
void item_t::parse_tags(const char * p,
void item_t::parse_tags(const char * p, bool overwrite_existing,
optional<date_t::year_type> current_year)
{
if (const char * b = std::strchr(p, '[')) {
@ -149,15 +165,18 @@ void item_t::parse_tags(const char * p,
q = std::strtok(NULL, " \t")) {
const string::size_type len = std::strlen(q);
if (! tag.empty()) {
if (! has_tag(tag))
set_tag(tag, string(p + (q - buf.get())));
string_map::iterator i = set_tag(tag, string(p + (q - buf.get())),
overwrite_existing);
(*i).second.second = true;
break;
}
else if (q[0] == ':' && q[len - 1] == ':') { // a series of tags
for (char * r = std::strtok(q + 1, ":");
r;
r = std::strtok(NULL, ":"))
set_tag(r);
r = std::strtok(NULL, ":")) {
string_map::iterator i = set_tag(r, none, overwrite_existing);
(*i).second.second = true;
}
}
else if (q[len - 1] == ':') { // a metadata setting
tag = string(q, len - 1);
@ -165,7 +184,7 @@ void item_t::parse_tags(const char * p,
}
}
void item_t::append_note(const char * p,
void item_t::append_note(const char * p, bool overwrite_existing,
optional<date_t::year_type> current_year)
{
if (note) {
@ -175,7 +194,7 @@ void item_t::append_note(const char * p,
note = p;
}
parse_tags(p, current_year);
parse_tags(p, overwrite_existing, current_year);
}
namespace {

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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
@ -106,7 +106,8 @@ public:
enum state_t { UNCLEARED = 0, CLEARED, PENDING };
typedef std::map<string, optional<string> > string_map;
typedef std::pair<optional<string>, bool> tag_data_t;
typedef std::map<string, tag_data_t> string_map;
state_t _state;
optional<date_t> _date;
@ -156,12 +157,14 @@ public:
virtual optional<string> get_tag(const mask_t& tag_mask,
const optional<mask_t>& value_mask = none) const;
virtual void set_tag(const string& tag,
const optional<string>& value = none);
virtual string_map::iterator
set_tag(const string& tag,
const optional<string>& value = none,
const bool overwrite_existing = true);
virtual void parse_tags(const char * p,
virtual void parse_tags(const char * p, bool overwrite_existing = true,
optional<date_t::year_type> current_year = none);
virtual void append_note(const char * p,
virtual void append_note(const char * p, bool overwrite_existing = true,
optional<date_t::year_type> current_year = none);
static bool use_effective_date;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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
@ -44,6 +44,7 @@
#include "utils.h"
#include "times.h"
#include "mask.h"
namespace ledger {
@ -58,6 +59,11 @@ typedef std::list<xact_t *> xacts_list;
typedef std::list<auto_xact_t *> auto_xacts_list;
typedef std::list<period_xact_t *> period_xacts_list;
typedef std::pair<mask_t, string> payee_mapping_t;
typedef std::list<payee_mapping_t> payee_mappings_t;
typedef std::pair<mask_t, account_t *> account_mapping_t;
typedef std::list<account_mapping_t> account_mappings_t;
class journal_t : public noncopyable
{
public:
@ -110,6 +116,8 @@ public:
period_xacts_list period_xacts;
std::list<fileinfo_t> sources;
bool was_loaded;
payee_mappings_t payee_mappings;
account_mappings_t account_mappings;
journal_t();
journal_t(const path& pathname);

283
src/lookup.cc Normal file
View file

@ -0,0 +1,283 @@
/*
* Copyright (c) 2003-2010, 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>
#include "lookup.h"
#include "unistring.h"
namespace ledger {
namespace {
typedef std::pair<xact_t *, int> score_entry_t;
typedef std::deque<score_entry_t> scorecard_t;
typedef std::map<uint32_t, std::size_t> char_positions_map;
struct score_sorter {
bool operator()(const score_entry_t& left,
const score_entry_t& right) const {
return left.second > right.second;
}
};
typedef std::map<account_t *, int> account_use_map;
typedef std::pair<account_t *, int> account_use_pair;
struct usage_sorter {
bool operator()(const account_use_pair& left,
const account_use_pair& right) const {
return left.second > right.second;
}
};
}
std::pair<xact_t *, account_t *>
lookup_probable_account(const string& ident,
xacts_iterator& iter_func,
account_t * ref_account)
{
scorecard_t scores;
#if !defined(HAVE_BOOST_REGEX_UNICODE)
string lident = ident;
to_lower(lident);
unistring lowered_ident(lident);
#else
// jww (2010-03-07): Not yet implemented
unistring lowered_ident(ident);
#endif
DEBUG("lookup.account",
"Looking up identifier '" << lowered_ident.extract() << "'");
#if defined(DEBUG_ON)
if (ref_account != NULL)
DEBUG("lookup.account",
" with reference account: " << ref_account->fullname());
#endif
while (xact_t * xact = iter_func()) {
#if 0
// Only consider transactions from the last two years (jww (2010-03-07):
// make this an option)
if ((CURRENT_DATE() - xact->date()).days() > 700)
continue;
#endif
// An exact match is worth a score of 100 and terminates the search
if (ident == xact->payee) {
DEBUG("lookup", " we have an exact match, score = 100");
scores.push_back(score_entry_t(xact, 100));
break;
}
#if !defined(HAVE_BOOST_REGEX_UNICODE)
string payee = xact->payee;
to_lower(payee);
unistring value_key(payee);
#else
// jww (2010-03-07): Not yet implemented
unistring value_key(xact->payee);
#endif
DEBUG("lookup", "Considering payee: " << value_key.extract());
std::size_t index = 0;
std::size_t last_match_pos = unistring::npos;
int bonus = 0;
int score = 0;
std::size_t pos;
char_positions_map positions;
// Walk each letter in the source identifier
foreach (const uint32_t& ch, lowered_ident.utf32chars) {
int addend = 0;
bool added_bonus = false;
std::size_t value_len = value_key.length();
pos = value_key.find(ch);
// Ensure that a letter which has been matched is not matched twice, so
// that the two x's of Exxon don't both match to the single x in Oxford.
// This part of the loop is very expensive, but avoids a lot of bogus
// matches.
char_positions_map::iterator pi = positions.find(ch);
while (pi != positions.end() &&
pos != unistring::npos && pos <= (*pi).second &&
(*pi).second + 1 < value_len)
pos = value_key.find(ch, (*pi).second + 1);
if (pos != unistring::npos) {
if (pi != positions.end())
(*pi).second = pos;
else
positions.insert(char_positions_map::value_type(ch, pos));
// If it occurs in the same order as the source identifier -- that is,
// without intervening letters to break the pattern -- it's worth 10
// points. Plus, an extra point is added for every letter in chains
// of 3 or more.
if (last_match_pos == unistring::npos ?
index == 0 && pos == 0 : pos == last_match_pos + 1) {
DEBUG("lookup",
" char " << index << " in-sequence match with bonus " << bonus);
addend += 10;
if (bonus > 2)
addend += bonus - 2;
bonus++;
added_bonus = true;
last_match_pos = pos;
}
// If it occurs in the same general sequence as the source identifier,
// it's worth 5 points, plus an extra point if it's within the next 3
// characters, and an extra point if it's preceded by a non-alphabetic
// character.
//
// If the letter occurs at all in the target identifier, it's worth 1
// point, plus an extra point if it's within 3 characters, and an
// extra point if it's preceded by a non-alphabetic character.
else {
bool in_order_match = (last_match_pos != unistring::npos &&
pos > last_match_pos);
DEBUG("lookup", " char " << index << " " <<
(in_order_match ? "in-order" : "out-of-order")
<< " match" << (in_order_match && pos - index < 3 ?
" with proximity bonus of 1" : ""));
if (pos < index)
addend += 1;
else
addend += 5;
if (in_order_match && pos - index < 3)
addend++;
#if !defined(HAVE_BOOST_REGEX_UNICODE)
if (pos == 0 || (pos > 0 && !std::isalnum(value_key[pos - 1])))
addend++;
#else
// jww (2010-03-07): Not yet implemented
#endif
last_match_pos = pos;
}
// If the letter does not appear at all, decrease the score by 1
} else {
last_match_pos = unistring::npos;
DEBUG("lookup", " char " << index << " does not match");
addend--;
}
// Finally, decay what is to be added to the score based on its position
// in the word. Since credit card payees in particular often share
// information at the end (such as the location where the purchase was
// made), we want to give much more credence to what occurs at the
// beginning. Every 5 character positions from the beginning becomes a
// divisor for the addend.
if ((int(index / 5) + 1) > 1) {
DEBUG("lookup",
" discounting the addend by / " << (int(index / 5) + 1));
addend = int(double(addend) / (int(index / 5) + 1));
}
DEBUG("lookup", " final addend is " << addend);
score += addend;
DEBUG("lookup", " score is " << score);
if (! added_bonus)
bonus = 0;
index++;
}
// Only consider payees with a score of 30 or greater
if (score >= 30)
scores.push_back(score_entry_t(xact, score));
}
// Sort the results by descending score, then look at every account ever
// used among the top five. Rank these by number of times used. Lastly,
// "decay" any latter accounts, so that we give recently used accounts a
// slightly higher rating in case of a tie.
std::stable_sort(scores.begin(), scores.end(), score_sorter());
scorecard_t::iterator si = scores.begin();
int decay = 0;
xact_t * best_xact = si != scores.end() ? (*si).first : NULL;
account_use_map account_usage;
for (int i = 0; i < 5 && si != scores.end(); i++, si++) {
DEBUG("lookup.account",
"Payee: " << std::setw(5) << std::right << (*si).second <<
" - " << (*si).first->payee);
foreach (post_t * post, (*si).first->posts) {
if (! post->has_flags(ITEM_TEMP | ITEM_GENERATED) &&
post->account != ref_account &&
! post->account->has_flags(ACCOUNT_TEMP | ACCOUNT_GENERATED)) {
account_use_map::iterator x = account_usage.find(post->account);
if (x == account_usage.end())
account_usage.insert(account_use_pair(post->account,
((*si).second - decay)));
else
(*x).second += ((*si).second - decay);
}
decay++;
}
}
if (account_usage.size() > 0) {
#if defined(DEBUG_ON)
if (SHOW_DEBUG("lookup.account")) {
foreach (const account_use_pair& value, account_usage) {
DEBUG("lookup.account",
"Account: " << value.second << " - " << value.first->fullname());
}
}
#endif
return std::pair<xact_t *, account_t *>
(best_xact, (*std::max_element(account_usage.begin(), account_usage.end(),
usage_sorter())).first);
} else {
return std::pair<xact_t *, account_t *>(best_xact, NULL);
}
}
} // namespace ledger

56
src/lookup.h Normal file
View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2003-2010, 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.
*/
/**
* @addtogroup data
*/
/**
* @file lookup.h
* @author John Wiegley
*
* @ingroup data
*/
#ifndef _LOOKUP_H
#define _LOOKUP_H
#include "iterators.h"
namespace ledger {
std::pair<xact_t *, account_t *>
lookup_probable_account(const string& ident,
xacts_iterator& iter_func,
account_t * ref_account = NULL);
} // namespace ledger
#endif // _LOOKUP_H

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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
@ -52,4 +52,38 @@ mask_t& mask_t::operator=(const string& pat)
return *this;
}
mask_t& mask_t::assign_glob(const string& pat)
{
string re_pat = "";
string::size_type len = pat.length();
for (string::size_type i = 0; i < len; i++) {
switch (pat[i]) {
case '?':
re_pat += '.';
break;
case '*':
re_pat += ".*";
break;
case '[':
while (i < len && pat[i] != ']')
re_pat += pat[i++];
if (i < len)
re_pat += pat[i];
break;
case '\\':
if (i + 1 < len) {
re_pat += pat[++i];
break;
} else {
// fallthrough...
}
default:
re_pat += pat[i];
break;
}
}
return (*this = re_pat);
}
} // namespace ledger

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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
@ -73,7 +73,11 @@ public:
}
mask_t& operator=(const string& other);
mask_t& assign_glob(const string& other);
bool operator<(const mask_t& other) const {
return expr < other.expr;
}
bool operator==(const mask_t& other) const {
return expr == other.expr;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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
@ -42,10 +42,8 @@ namespace ledger {
format_posts::format_posts(report_t& _report,
const string& format,
bool _print_raw,
const optional<string>& _prepend_format)
: report(_report), last_xact(NULL), last_post(NULL),
print_raw(_print_raw)
: report(_report), last_xact(NULL), last_post(NULL)
{
TRACE_CTOR(format_posts, "report&, const string&, bool");
@ -80,24 +78,8 @@ void format_posts::operator()(post_t& post)
{
std::ostream& out(report.output_stream);
if (print_raw) {
if (! post.has_xdata() ||
! post.xdata().has_flags(POST_EXT_DISPLAYED)) {
if (last_xact != post.xact) {
if (last_xact) {
bind_scope_t xact_scope(report, *last_xact);
out << between_format(xact_scope);
}
print_item(out, *post.xact);
out << '\n';
last_xact = post.xact;
}
post.xdata().add_flags(POST_EXT_DISPLAYED);
last_post = &post;
}
}
else if (! post.has_xdata() ||
! post.xdata().has_flags(POST_EXT_DISPLAYED)) {
if (! post.has_xdata() ||
! post.xdata().has_flags(POST_EXT_DISPLAYED)) {
bind_scope_t bound_scope(report, post);
if (prepend_format)

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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
@ -63,11 +63,9 @@ protected:
format_t prepend_format;
xact_t * last_xact;
post_t * last_post;
bool print_raw;
public:
format_posts(report_t& _report, const string& format,
bool _print_raw = false,
const optional<string>& _prepend_format = none);
virtual ~format_posts() {
TRACE_DTOR(format_posts);

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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
@ -576,7 +576,7 @@ void to_xml(std::ostream& out, const post_t& post)
if (post.metadata) {
push_xml y(out, "metadata");
foreach (const item_t::string_map::value_type& pair, *post.metadata) {
if (pair.second) {
if (pair.second.first) {
push_xml z(out, "variable");
{
push_xml z(out, "key");
@ -584,7 +584,7 @@ void to_xml(std::ostream& out, const post_t& post)
}
{
push_xml z(out, "value");
out << y.guard(*pair.second);
out << y.guard(*pair.second.first);
}
} else {
push_xml z(out, "tag");

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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
@ -52,10 +52,11 @@ class account_t;
class post_t : public item_t
{
public:
#define POST_VIRTUAL 0x10 // the account was specified with (parens)
#define POST_MUST_BALANCE 0x20 // posting must balance in the transaction
#define POST_CALCULATED 0x40 // posting's amount was calculated
#define POST_COST_CALCULATED 0x80 // posting's cost was calculated
#define POST_VIRTUAL 0x08 // the account was specified with (parens)
#define POST_MUST_BALANCE 0x10 // posting must balance in the transaction
#define POST_CALCULATED 0x20 // posting's amount was calculated
#define POST_COST_CALCULATED 0x40 // posting's cost was calculated
#define POST_COST_IN_FULL 0x80 // cost specified using @@
xact_t * xact; // only set for posts of regular xacts
account_t * account;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

208
src/print.cc Normal file
View file

@ -0,0 +1,208 @@
/*
* Copyright (c) 2003-2010, 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>
#include "print.h"
#include "xact.h"
#include "post.h"
#include "account.h"
#include "session.h"
#include "report.h"
namespace ledger {
print_xacts::print_xacts(report_t& _report,
bool _print_raw)
: report(_report), print_raw(_print_raw)
{
TRACE_CTOR(print_xacts, "report&, bool");
}
namespace {
void print_note(std::ostream& out, const string& note)
{
if (note.length() > 15)
out << "\n ;";
else
out << " ;";
bool need_separator = false;
for (const char * p = note.c_str(); *p; p++) {
if (*p == '\n') {
need_separator = true;
} else {
if (need_separator) {
out << "\n ;";
need_separator = false;
}
out << *p;
}
}
}
void print_xact(report_t& report, std::ostream& out, xact_t& xact)
{
out << format_date(item_t::use_effective_date ?
xact.date() : xact.actual_date(),
FMT_WRITTEN);
if (! item_t::use_effective_date && xact.effective_date())
out << '=' << format_date(*xact.effective_date(), FMT_WRITTEN);
out << ' ';
out << (xact.state() == item_t::CLEARED ? "* " :
(xact.state() == item_t::PENDING ? "! " : ""));
if (xact.code)
out << '(' << *xact.code << ") ";
out << xact.payee;
if (xact.note)
print_note(out, *xact.note);
out << '\n';
if (xact.metadata) {
foreach (const item_t::string_map::value_type& data, *xact.metadata) {
if (! data.second.second) {
out << " ; ";
if (data.second.first)
out << data.first << ": " << *data.second.first;
else
out << ':' << data.first << ":";
out << '\n';
}
}
}
foreach (post_t * post, xact.posts) {
if (post->has_flags(ITEM_TEMP | ITEM_GENERATED) &&
! report.HANDLED(print_virtual))
continue;
out << " ";
std::ostringstream buf;
if (xact.state() == item_t::UNCLEARED)
buf << (post->state() == item_t::CLEARED ? "* " :
(post->state() == item_t::PENDING ? "! " : ""));
if (post->has_flags(POST_VIRTUAL)) {
if (post->has_flags(POST_MUST_BALANCE))
buf << '[';
else
buf << '(';
}
buf << post->account->fullname();
if (post->has_flags(POST_VIRTUAL)) {
if (post->has_flags(POST_MUST_BALANCE))
buf << ']';
else
buf << ')';
}
if (! post->has_flags(POST_CALCULATED) || report.HANDLED(print_virtual)) {
unistring name(buf.str());
out << name.extract();
int slip = 36 - static_cast<int>(name.length());
if (slip > 0)
out << string(slip, ' ');
std::ostringstream amt_str;
report.scrub(post->amount).print(amt_str, 12, -1, true);
string amt = amt_str.str();
string trimmed_amt(amt);
trim_left(trimmed_amt);
int amt_slip = (static_cast<int>(amt.length()) -
static_cast<int>(trimmed_amt.length()));
if (slip + amt_slip < 2)
out << string(2 - (slip + amt_slip), ' ');
out << amt;
if (post->cost && ! post->has_flags(POST_CALCULATED)) {
if (post->has_flags(POST_COST_IN_FULL))
out << " @@ " << report.scrub(post->cost->abs());
else
out << " @ " << report.scrub((*post->cost / post->amount).abs());
}
if (post->assigned_amount)
out << " = " << report.scrub(*post->assigned_amount);
} else {
out << buf.str();
}
if (post->note)
print_note(out, *post->note);
out << '\n';
}
}
}
void print_xacts::flush()
{
std::ostream& out(report.output_stream);
bool first = true;
foreach (xact_t * xact, xacts) {
if (first)
first = false;
else
out << '\n';
if (print_raw) {
print_item(out, *xact);
out << '\n';
} else {
print_xact(report, out, *xact);
}
}
out.flush();
}
void print_xacts::operator()(post_t& post)
{
if (! post.has_xdata() ||
! post.xdata().has_flags(POST_EXT_DISPLAYED)) {
if (xacts_present.find(post.xact) == xacts_present.end()) {
xacts_present.insert(xacts_present_map::value_type(post.xact, true));
xacts.push_back(post.xact);
}
post.xdata().add_flags(POST_EXT_DISPLAYED);
}
}
} // namespace ledger

79
src/print.h Normal file
View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2003-2010, 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.
*/
/**
* @addtogroup data
*/
/**
* @file convert.h
* @author John Wiegley
*
* @ingroup data
*/
#ifndef _PRINT_H
#define _PRINT_H
#include "chain.h"
#include "predicate.h"
#include "format.h"
namespace ledger {
class xact_t;
class post_t;
class report_t;
class print_xacts : public item_handler<post_t>
{
protected:
typedef std::list<xact_t *> xacts_list;
typedef std::map<xact_t *, bool> xacts_present_map;
report_t& report;
xacts_present_map xacts_present;
xacts_list xacts;
bool print_raw;
public:
print_xacts(report_t& _report, bool _print_raw = false);
virtual ~print_xacts() {
TRACE_DTOR(print_xacts);
}
virtual void flush();
virtual void operator()(post_t& post);
};
} // namespace ledger
#endif // _PRINT_H

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
* Copyright (c) 2003-2010, 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

Some files were not shown because too many files have changed in this diff Show more