diff options
137 files changed, 2254 insertions, 347 deletions
@@ -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 diff --git a/doc/ledger.texi b/doc/ledger.texi index 3b0785eb..10e80a60 100644 --- a/doc/ledger.texi +++ b/doc/ledger.texi @@ -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 diff --git a/lisp/ldg-post.el b/lisp/ldg-post.el new file mode 100644 index 00000000..30edd035 --- /dev/null +++ b/lisp/ldg-post.el @@ -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) diff --git a/lisp/ldg-regex.el b/lisp/ldg-regex.el new file mode 100644 index 00000000..14c41f63 --- /dev/null +++ b/lisp/ldg-regex.el @@ -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) diff --git a/lisp/ldg-texi.el b/lisp/ldg-texi.el new file mode 100644 index 00000000..982ea0ed --- /dev/null +++ b/lisp/ldg-texi.el @@ -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) diff --git a/lisp/ledger.el b/lisp/ledger.el index c2407261..9fa28781 100644 --- a/lisp/ledger.el +++ b/lisp/ledger.el @@ -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))) diff --git a/src/account.cc b/src/account.cc index f8729409..245d61fc 100644 --- a/src/account.cc +++ b/src/account.cc @@ -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 diff --git a/src/account.h b/src/account.h index 73cd35ac..0a3a75e4 100644 --- a/src/account.h +++ b/src/account.h @@ -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 diff --git a/src/accum.cc b/src/accum.cc index b918c76a..62e36deb 100644 --- a/src/accum.cc +++ b/src/accum.cc @@ -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 diff --git a/src/accum.h b/src/accum.h index 878c2b7c..94e79948 100644 --- a/src/accum.h +++ b/src/accum.h @@ -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 diff --git a/src/amount.cc b/src/amount.cc index eddbca18..3a64577f 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -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 diff --git a/src/amount.h b/src/amount.h index a37efdb8..5c1bca46 100644 --- a/src/amount.h +++ b/src/amount.h @@ -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 diff --git a/src/annotate.cc b/src/annotate.cc index 146a7afd..99c12fc3 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -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 diff --git a/src/annotate.h b/src/annotate.h index 38ebaeae..6ef26476 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -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 diff --git a/src/archive.cc b/src/archive.cc index 7306f8d3..e7fc072e 100644 --- a/src/archive.cc +++ b/src/archive.cc @@ -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 diff --git a/src/archive.h b/src/archive.h index 03fc970a..e954cd3e 100644 --- a/src/archive.h +++ b/src/archive.h @@ -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 diff --git a/src/balance.cc b/src/balance.cc index 4fcc83fa..9b39a49a 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -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 diff --git a/src/balance.h b/src/balance.h index 826de134..f8455d49 100644 --- a/src/balance.h +++ b/src/balance.h @@ -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 diff --git a/src/chain.cc b/src/chain.cc index ecb39e0b..86f639ad 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -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)); diff --git a/src/chain.h b/src/chain.h index a2b75b3a..94d54317 100644 --- a/src/chain.h +++ b/src/chain.h @@ -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 diff --git a/src/commodity.cc b/src/commodity.cc index 24016830..e5f10e34 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -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 diff --git a/src/commodity.h b/src/commodity.h index d2d8af21..10f209fa 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -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 diff --git a/src/compare.cc b/src/compare.cc index 36884a46..f3c13cea 100644 --- a/src/compare.cc +++ b/src/compare.cc @@ -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 diff --git a/src/compare.h b/src/compare.h index 740ba275..eeecd7cf 100644 --- a/src/compare.h +++ b/src/compare.h @@ -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 diff --git a/src/convert.cc b/src/convert.cc new file mode 100644 index 00000000..6c02cff3 --- /dev/null +++ b/src/convert.cc @@ -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 diff --git a/src/convert.h b/src/convert.h new file mode 100644 index 00000000..6d02f24a --- /dev/null +++ b/src/convert.h @@ -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 diff --git a/src/csv.cc b/src/csv.cc new file mode 100644 index 00000000..5a74232f --- /dev/null +++ b/src/csv.cc @@ -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 diff --git a/src/csv.h b/src/csv.h new file mode 100644 index 00000000..5ff8b59e --- /dev/null +++ b/src/csv.h @@ -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 diff --git a/src/draft.cc b/src/draft.cc index fc53e956..18075731 100644 --- a/src/draft.cc +++ b/src/draft.cc @@ -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; } diff --git a/src/draft.h b/src/draft.h index 93e98ff5..59039f77 100644 --- a/src/draft.h +++ b/src/draft.h @@ -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 diff --git a/src/emacs.cc b/src/emacs.cc index dc1a18ae..d47f04ad 100644 --- a/src/emacs.cc +++ b/src/emacs.cc @@ -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 diff --git a/src/emacs.h b/src/emacs.h index 4b2a452a..97292728 100644 --- a/src/emacs.h +++ b/src/emacs.h @@ -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 diff --git a/src/error.cc b/src/error.cc index d5abe4de..cfc91ff1 100644 --- a/src/error.cc +++ b/src/error.cc @@ -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 diff --git a/src/error.h b/src/error.h index 5369faf1..0d33f020 100644 --- a/src/error.h +++ b/src/error.h @@ -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 diff --git a/src/expr.cc b/src/expr.cc index c59f8a57..79fb3611 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -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 @@ -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 diff --git a/src/exprbase.h b/src/exprbase.h index 0b3466b0..0dbf3bff 100644 --- a/src/exprbase.h +++ b/src/exprbase.h @@ -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 diff --git a/src/filters.cc b/src/filters.cc index 2926eb08..0c45d356 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -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); } diff --git a/src/filters.h b/src/filters.h index 92148dbe..82fbf687 100644 --- a/src/filters.h +++ b/src/filters.h @@ -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() { diff --git a/src/flags.h b/src/flags.h index 69e40e4b..03f47ff9 100644 --- a/src/flags.h +++ b/src/flags.h @@ -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 diff --git a/src/format.cc b/src/format.cc index f26a86a1..c546926e 100644 --- a/src/format.cc +++ b/src/format.cc @@ -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 diff --git a/src/format.h b/src/format.h index a2bf1015..72d44ee5 100644 --- a/src/format.h +++ b/src/format.h @@ -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 diff --git a/src/generate.cc b/src/generate.cc index 3549adc8..c1eb1d14 100644 --- a/src/generate.cc +++ b/src/generate.cc @@ -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 diff --git a/src/generate.h b/src/generate.h index 66513fc8..3d9965a5 100644 --- a/src/generate.h +++ b/src/generate.h @@ -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 diff --git a/src/global.cc b/src/global.cc index e120e5d5..170509b0 100644 --- a/src/global.cc +++ b/src/global.cc @@ -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 diff --git a/src/global.h b/src/global.h index ab3afed4..05e3bdb7 100644 --- a/src/global.h +++ b/src/global.h @@ -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; diff --git a/src/interactive.cc b/src/interactive.cc index 35382e8f..d0baf07a 100644 --- a/src/interactive.cc +++ b/src/interactive.cc @@ -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 diff --git a/src/interactive.h b/src/interactive.h index 199b7b71..fbc4ffeb 100644 --- a/src/interactive.h +++ b/src/interactive.h @@ -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 diff --git a/src/item.cc b/src/item.cc index 8d1ba34f..14a0896f 100644 --- a/src/item.cc +++ b/src/item.cc @@ -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 { @@ -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; diff --git a/src/iterators.cc b/src/iterators.cc index 540ba8ae..f3a68978 100644 --- a/src/iterators.cc +++ b/src/iterators.cc @@ -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 diff --git a/src/iterators.h b/src/iterators.h index 1cbe4c25..5e9f5fbe 100644 --- a/src/iterators.h +++ b/src/iterators.h @@ -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 diff --git a/src/journal.cc b/src/journal.cc index 5aa2e7f7..158fac42 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -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 diff --git a/src/journal.h b/src/journal.h index 8d59e3b4..e54814aa 100644 --- a/src/journal.h +++ b/src/journal.h @@ -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); diff --git a/src/lookup.cc b/src/lookup.cc new file mode 100644 index 00000000..40b6c185 --- /dev/null +++ b/src/lookup.cc @@ -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 diff --git a/src/lookup.h b/src/lookup.h new file mode 100644 index 00000000..53cb5837 --- /dev/null +++ b/src/lookup.h @@ -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 diff --git a/src/main.cc b/src/main.cc index 0aec8886..23593533 100644 --- a/src/main.cc +++ b/src/main.cc @@ -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 diff --git a/src/mask.cc b/src/mask.cc index c1e66ced..cd516fe2 100644 --- a/src/mask.cc +++ b/src/mask.cc @@ -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 @@ -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; } @@ -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 @@ -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 diff --git a/src/option.cc b/src/option.cc index d2ec8808..b375dd8d 100644 --- a/src/option.cc +++ b/src/option.cc @@ -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 diff --git a/src/option.h b/src/option.h index b81c2ce7..9688171e 100644 --- a/src/option.h +++ b/src/option.h @@ -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 diff --git a/src/output.cc b/src/output.cc index bb7eff5c..30775310 100644 --- a/src/output.cc +++ b/src/output.cc @@ -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) diff --git a/src/output.h b/src/output.h index 778a9335..7618e567 100644 --- a/src/output.h +++ b/src/output.h @@ -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); diff --git a/src/parser.cc b/src/parser.cc index ef778411..5bb06f84 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -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 diff --git a/src/parser.h b/src/parser.h index 7a69fe08..5eba4ffd 100644 --- a/src/parser.h +++ b/src/parser.h @@ -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 diff --git a/src/pool.cc b/src/pool.cc index f895a8bc..8d9f0c28 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -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 @@ -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 diff --git a/src/post.cc b/src/post.cc index 43cfe55f..41ae04dd 100644 --- a/src/post.cc +++ b/src/post.cc @@ -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"); @@ -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; diff --git a/src/precmd.cc b/src/precmd.cc index 632caeae..e545a783 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -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 diff --git a/src/precmd.h b/src/precmd.h index 88d66ab2..277933c3 100644 --- a/src/precmd.h +++ b/src/precmd.h @@ -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 diff --git a/src/predicate.cc b/src/predicate.cc index 4da4decf..369120e6 100644 --- a/src/predicate.cc +++ b/src/predicate.cc @@ -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 diff --git a/src/predicate.h b/src/predicate.h index b3b81f9b..c21167d7 100644 --- a/src/predicate.h +++ b/src/predicate.h @@ -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 diff --git a/src/print.cc b/src/print.cc new file mode 100644 index 00000000..eb102a87 --- /dev/null +++ b/src/print.cc @@ -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 diff --git a/src/print.h b/src/print.h new file mode 100644 index 00000000..f323b153 --- /dev/null +++ b/src/print.h @@ -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 diff --git a/src/pstream.h b/src/pstream.h index 00caf4e0..bba2845e 100644 --- a/src/pstream.h +++ b/src/pstream.h @@ -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 diff --git a/src/py_account.cc b/src/py_account.cc index 056cd722..3114cc0b 100644 --- a/src/py_account.cc +++ b/src/py_account.cc @@ -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 diff --git a/src/py_amount.cc b/src/py_amount.cc index 8fb507a3..5afe8c6b 100644 --- a/src/py_amount.cc +++ b/src/py_amount.cc @@ -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 diff --git a/src/py_balance.cc b/src/py_balance.cc index 8c0c4c58..7be75444 100644 --- a/src/py_balance.cc +++ b/src/py_balance.cc @@ -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 diff --git a/src/py_commodity.cc b/src/py_commodity.cc index 984be5f0..22a4f153 100644 --- a/src/py_commodity.cc +++ b/src/py_commodity.cc @@ -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 diff --git a/src/py_expr.cc b/src/py_expr.cc index cad2c0fa..027125e2 100644 --- a/src/py_expr.cc +++ b/src/py_expr.cc @@ -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 diff --git a/src/py_format.cc b/src/py_format.cc index d4825b42..36f89ae5 100644 --- a/src/py_format.cc +++ b/src/py_format.cc @@ -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 diff --git a/src/py_item.cc b/src/py_item.cc index 8a6e495a..edebd532 100644 --- a/src/py_item.cc +++ b/src/py_item.cc @@ -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 diff --git a/src/py_journal.cc b/src/py_journal.cc index 5be9cbe1..81ce290d 100644 --- a/src/py_journal.cc +++ b/src/py_journal.cc @@ -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 diff --git a/src/py_post.cc b/src/py_post.cc index 20cdba6b..e4a50980 100644 --- a/src/py_post.cc +++ b/src/py_post.cc @@ -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 diff --git a/src/py_times.cc b/src/py_times.cc index a62bb9b6..39b0df7a 100644 --- a/src/py_times.cc +++ b/src/py_times.cc @@ -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 diff --git a/src/py_utils.cc b/src/py_utils.cc index 5203599f..4b364e5d 100644 --- a/src/py_utils.cc +++ b/src/py_utils.cc @@ -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 diff --git a/src/py_value.cc b/src/py_value.cc index 713dc3d4..449320ec 100644 --- a/src/py_value.cc +++ b/src/py_value.cc @@ -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 diff --git a/src/py_xact.cc b/src/py_xact.cc index 59c599d9..c4e1fd32 100644 --- a/src/py_xact.cc +++ b/src/py_xact.cc @@ -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 diff --git a/src/pyfstream.h b/src/pyfstream.h index 3da37523..02e4d240 100644 --- a/src/pyfstream.h +++ b/src/pyfstream.h @@ -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 diff --git a/src/pyinterp.cc b/src/pyinterp.cc index 0701176f..1330fc5c 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -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 diff --git a/src/pyinterp.h b/src/pyinterp.h index f2d7b760..e3a091ad 100644 --- a/src/pyinterp.h +++ b/src/pyinterp.h @@ -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 diff --git a/src/pyledger.cc b/src/pyledger.cc index cf7e1527..4a53532a 100644 --- a/src/pyledger.cc +++ b/src/pyledger.cc @@ -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 diff --git a/src/pyutils.h b/src/pyutils.h index 54d6fa28..e0f77003 100644 --- a/src/pyutils.h +++ b/src/pyutils.h @@ -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 diff --git a/src/query.cc b/src/query.cc index cfa321b0..1f086df8 100644 --- a/src/query.cc +++ b/src/query.cc @@ -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 diff --git a/src/query.h b/src/query.h index ebc14020..2b0bc75d 100644 --- a/src/query.h +++ b/src/query.h @@ -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 diff --git a/src/quotes.cc b/src/quotes.cc index f892e93f..4e3ec33e 100644 --- a/src/quotes.cc +++ b/src/quotes.cc @@ -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 diff --git a/src/quotes.h b/src/quotes.h index d00c5bfd..e981fdad 100644 --- a/src/quotes.h +++ b/src/quotes.h @@ -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 diff --git a/src/report.cc b/src/report.cc index b882ca92..2189812b 100644 --- a/src/report.cc +++ b/src/report.cc @@ -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 @@ -37,12 +37,14 @@ #include "format.h" #include "query.h" #include "output.h" +#include "print.h" #include "iterators.h" #include "filters.h" #include "precmd.h" #include "stats.h" #include "generate.h" #include "draft.h" +#include "convert.h" #include "xml.h" #include "emacs.h" @@ -91,6 +93,33 @@ void report_t::normalize_options(const string& verb) start_of_week = *weekday; } + long meta_width = -1; + + if (! HANDLED(prepend_format_) && HANDLED(meta_)) { + if (! HANDLED(meta_width_)) { + string::size_type i = HANDLER(meta_).str().find(':'); + if (i != string::npos) { + HANDLED(meta_width_).on_with + (string("?normalize"), + lexical_cast<long>(string(HANDLER(meta_).str(), i + 1))); + HANDLED(meta_).on(string("?normalize"), + string(HANDLER(meta_).str(), 0, i)); + } + } + if (HANDLED(meta_width_)) { + HANDLER(prepend_format_).on + (string("?normalize"), + string("%(justify(truncated(tag(\"") + + HANDLER(meta_).str() + "\"), " + + HANDLED(meta_width_).value.to_string() + " - 1), " + + HANDLED(meta_width_).value.to_string() + "))"); + meta_width = HANDLED(meta_width_).value.to_long(); + } else { + HANDLER(prepend_format_).on(string("?normalize"), string("%(tag(\"") + + HANDLER(meta_).str() + "\"))"); + } + } + if (verb == "print" || verb == "xact" || verb == "dump") { HANDLER(related).on_only(string("?normalize")); HANDLER(related_all).on_only(string("?normalize")); @@ -163,6 +192,9 @@ void report_t::normalize_options(const string& verb) else cols = 80L; + if (meta_width > 0) + cols -= meta_width; + if (cols > 0) { DEBUG("auto.columns", "cols = " << cols); @@ -185,11 +217,11 @@ void report_t::normalize_options(const string& verb) HANDLER(total_width_).value.to_long() : amount_width); - DEBUG("auto.columns", "date_width = " << date_width); - DEBUG("auto.columns", "payee_width = " << payee_width); + DEBUG("auto.columns", "date_width = " << date_width); + DEBUG("auto.columns", "payee_width = " << payee_width); DEBUG("auto.columns", "account_width = " << account_width); - DEBUG("auto.columns", "amount_width = " << amount_width); - DEBUG("auto.columns", "total_width = " << total_width); + DEBUG("auto.columns", "amount_width = " << amount_width); + DEBUG("auto.columns", "total_width = " << total_width); if (! HANDLER(date_width_).specified && ! HANDLER(payee_width_).specified && @@ -205,6 +237,8 @@ void report_t::normalize_options(const string& verb) } } + if (! HANDLED(meta_width_)) + HANDLER(meta_width_).on_with(string("?normalize"), 0L); if (! HANDLER(date_width_).specified) HANDLER(date_width_).on_with(string("?normalize"), date_width); if (! HANDLER(payee_width_).specified) @@ -409,15 +443,20 @@ value_t report_t::fn_trim(call_scope_t& args) } } -value_t report_t::fn_scrub(call_scope_t& args) +value_t report_t::scrub(value_t val) { - value_t temp(args.value().strip_annotations(what_to_keep())); + value_t temp(val.strip_annotations(what_to_keep())); if (HANDLED(base)) return temp; else return temp.unreduced(); } +value_t report_t::fn_scrub(call_scope_t& args) +{ + return scrub(args.value()); +} + value_t report_t::fn_rounded(call_scope_t& args) { return args.value().rounded(); @@ -469,11 +508,11 @@ value_t report_t::fn_justify(call_scope_t& scope) value_t report_t::fn_quoted(call_scope_t& scope) { - interactive_t args(scope, "s"); + interactive_t args(scope, "v"); std::ostringstream out; out << '"'; - foreach (const char ch, args.get<string>(0)) { + foreach (const char ch, args.value_at(0).to_string()) { if (ch == '"') out << "\\\""; else @@ -855,6 +894,8 @@ option_t<report_t> * report_t::lookup_option(const char * p) case 'm': OPT(market); else OPT(monthly); + else OPT(meta_); + else OPT(meta_width_); break; case 'n': OPT_CH(collapse); @@ -879,9 +920,9 @@ option_t<report_t> * report_t::lookup_option(const char * p) else OPT(price); else OPT(prices_format_); else OPT(pricedb_format_); - else OPT(print_format_); else OPT(payee_width_); else OPT(prepend_format_); + else OPT(print_virtual); break; case 'q': OPT(quantity); @@ -1199,21 +1240,21 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, else if (is_eq(p, "cleared")) { HANDLER(amount_).set_expr(string("#cleared"), "(amount, cleared ? amount : 0)"); - return expr_t::op_t::wrap_functor (reporter<account_t, acct_handler_ptr, &report_t::accounts_report> (new format_accounts(*this, report_format(HANDLER(cleared_format_)), maybe_format(HANDLER(prepend_format_))), *this, "#cleared")); } + else if (is_eq(p, "convert")) + return WRAP_FUNCTOR(convert_command); break; case 'e': - if (is_eq(p, "equity")) - return WRAP_FUNCTOR - (reporter<> - (new format_posts(*this, report_format(HANDLER(print_format_))), - *this, "#equity")); + if (is_eq(p, "equity")) { + HANDLER(print_virtual).on_only(string("#equity")); + return WRAP_FUNCTOR(reporter<>(new print_xacts(*this), *this, "#equity")); + } else if (is_eq(p, "entry")) return WRAP_FUNCTOR(xact_command); else if (is_eq(p, "emacs")) @@ -1226,9 +1267,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, case 'p': if (*(p + 1) == '\0' || is_eq(p, "print")) return WRAP_FUNCTOR - (reporter<> - (new format_posts(*this, report_format(HANDLER(print_format_)), - HANDLED(raw)), *this, "#print")); + (reporter<>(new print_xacts(*this, HANDLED(raw)), *this, "#print")); else if (is_eq(p, "prices")) return expr_t::op_t::wrap_functor (reporter<post_t, post_handler_ptr, &report_t::commodities_report> @@ -1248,7 +1287,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR (reporter<> (new format_posts(*this, report_format(HANDLER(register_format_)), - false, maybe_format(HANDLER(prepend_format_))), + maybe_format(HANDLER(prepend_format_))), *this, "#register")); else if (is_eq(p, "reload")) return MAKE_FUNCTOR(report_t::reload_command); @@ -1283,11 +1322,13 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(format_command); break; case 'g': - if (is_eq(p, "generate")) + if (is_eq(p, "generate")) { + HANDLER(print_virtual).on_only(string("#generate")); return expr_t::op_t::wrap_functor (reporter<post_t, post_handler_ptr, &report_t::generate_report> - (new format_posts(*this, report_format(HANDLER(print_format_)), - false), *this, "#generate")); + (new print_xacts(*this), *this, "#generate")); + } + break; case 'p': if (is_eq(p, "parse")) return WRAP_FUNCTOR(parse_command); diff --git a/src/report.h b/src/report.h index 08819e23..50071940 100644 --- a/src/report.h +++ b/src/report.h @@ -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 @@ -144,6 +144,7 @@ public: value_t fn_is_seq(call_scope_t& scope); value_t fn_strip(call_scope_t& scope); value_t fn_trim(call_scope_t& scope); + value_t scrub(value_t val); value_t fn_scrub(call_scope_t& scope); value_t fn_quantity(call_scope_t& scope); value_t fn_rounded(call_scope_t& scope); @@ -263,6 +264,7 @@ public: HANDLER(lots).report(out); HANDLER(lots_actual).report(out); HANDLER(market).report(out); + HANDLER(meta_).report(out); HANDLER(monthly).report(out); HANDLER(no_total).report(out); HANDLER(now_).report(out); @@ -280,7 +282,7 @@ public: HANDLER(price).report(out); HANDLER(prices_format_).report(out); HANDLER(pricedb_format_).report(out); - HANDLER(print_format_).report(out); + HANDLER(print_virtual).report(out); HANDLER(quantity).report(out); HANDLER(quarterly).report(out); HANDLER(raw).report(out); @@ -311,6 +313,7 @@ public: HANDLER(weekly).report(out); HANDLER(wide).report(out); HANDLER(yearly).report(out); + HANDLER(meta_width_).report(out); HANDLER(date_width_).report(out); HANDLER(payee_width_).report(out); HANDLER(account_width_).report(out); @@ -618,6 +621,8 @@ public: .set_expr(string("--market"), "market(total_expr, date, exchange)"); }); + OPTION(report_t, meta_); + OPTION_(report_t, monthly, DO() { // -M parent->HANDLER(period_).on(string("--monthly"), "monthly"); }); @@ -748,23 +753,7 @@ public: "P %(datetime) %(account) %(scrub(display_amount))\n"); }); - OPTION__(report_t, print_format_, CTOR(report_t, print_format_) { - on(none, - "%(xact.date)" - "%(!effective & xact.effective_date ?" - " \"=\" + xact.effective_date : \"\")" - "%(xact.cleared ? \" *\" : (xact.pending ? \" !\" : \"\"))" - "%(code ? \" (\" + code + \")\" :" - " \"\") %(payee)%(xact.comment)\n" - " %(xact.uncleared ?" - " (cleared ? \"* \" : (pending ? \"! \" : \"\")) : \"\")" - "%(calculated ? account : justify(account, 34, -1, false))" - "%(calculated ? \"\" : \" \" + justify(scrub(amount), 12, -1, true))" - "%(has_cost & !cost_calculated ?" - " \" @ \" + justify(scrub(abs(cost / amount)), 0) : \"\")" - "%(comment)\n%/" - " %$7%$8%$9%$A%$B\n%/\n"); - }); + OPTION(report_t, print_virtual); OPTION_(report_t, quantity, DO() { // -O parent->HANDLER(revalued).off(); @@ -791,12 +780,13 @@ public: " %(ansify_if(justify(truncated(account, account_width, abbrev_len), " " account_width), blue if color))" " %(justify(scrub(display_amount), amount_width, " - " 3 + date_width + payee_width + account_width + amount_width, " - " true, color))" + " 3 + meta_width + date_width + payee_width + account_width" + " + amount_width, true, color))" " %(justify(scrub(display_total), total_width, " - " 4 + date_width + payee_width + account_width + amount_width " - " + total_width, true, color))\n%/" - "%(justify(\" \", 2 + date_width + payee_width))%$3 %$4 %$5\n"); + " 4 + meta_width + date_width + payee_width + account_width" + " + amount_width + total_width, true, color))\n%/" + "%(justify(\" \", 2 + date_width + payee_width))" + "%$3 %$4 %$5\n"); }); OPTION(report_t, related); // -r @@ -906,6 +896,10 @@ public: parent->HANDLER(period_).on(string("--yearly"), "yearly"); }); + OPTION__(report_t, meta_width_, + bool specified; + CTOR(report_t, meta_width_) { specified = false; } + DO_(args) { value = args[1].to_long(); specified = true; }); OPTION__(report_t, date_width_, bool specified; CTOR(report_t, date_width_) { specified = false; } diff --git a/src/scope.cc b/src/scope.cc index 64736ca3..72cf4ad3 100644 --- a/src/scope.cc +++ b/src/scope.cc @@ -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 diff --git a/src/scope.h b/src/scope.h index 93f80230..675fa4e1 100644 --- a/src/scope.h +++ b/src/scope.h @@ -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 diff --git a/src/session.cc b/src/session.cc index 1882e554..8e5536b0 100644 --- a/src/session.cc +++ b/src/session.cc @@ -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 diff --git a/src/session.h b/src/session.h index 5c4612a0..de1771ad 100644 --- a/src/session.h +++ b/src/session.h @@ -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 diff --git a/src/stats.cc b/src/stats.cc index b89a5949..86a8338d 100644 --- a/src/stats.cc +++ b/src/stats.cc @@ -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 diff --git a/src/stats.h b/src/stats.h index d2d10de6..b7bf94c5 100644 --- a/src/stats.h +++ b/src/stats.h @@ -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 diff --git a/src/stream.cc b/src/stream.cc index e39b74e0..272d4f1c 100644 --- a/src/stream.cc +++ b/src/stream.cc @@ -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 diff --git a/src/stream.h b/src/stream.h index e87da67a..356df08c 100644 --- a/src/stream.h +++ b/src/stream.h @@ -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 diff --git a/src/system.hh.in b/src/system.hh.in index 341ce129..5de7150e 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -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 @@ -136,8 +136,7 @@ typedef std::ostream::pos_type ostream_pos_type; #include <editline/readline.h> #endif -#include <boost/algorithm/string/classification.hpp> -#include <boost/algorithm/string/predicate.hpp> +#include <boost/algorithm/string.hpp> #include <boost/bind.hpp> #include <boost/cast.hpp> #include <boost/current_function.hpp> diff --git a/src/temps.cc b/src/temps.cc index fd099e9a..68b9ffa0 100644 --- a/src/temps.cc +++ b/src/temps.cc @@ -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 diff --git a/src/temps.h b/src/temps.h index 4243079c..ac6d08cd 100644 --- a/src/temps.h +++ b/src/temps.h @@ -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 diff --git a/src/textual.cc b/src/textual.cc index 8953d2b8..dfca7943 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -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 @@ -144,6 +144,8 @@ namespace { void end_directive(char * line); void alias_directive(char * line); void fixed_directive(char * line); + void payee_mapping_directive(char * line); + void account_mapping_directive(char * line); void tag_directive(char * line); void define_directive(char * line); bool general_directive(char * line); @@ -663,14 +665,42 @@ void instance_t::include_directive(char * line) filename = resolve_path(filename); DEBUG("textual.include", "resolved path: " << filename.string()); - if (! exists(filename)) + mask_t glob; +#if BOOST_VERSION >= 103700 + path parent_path = filename.parent_path(); + glob.assign_glob(filename.filename()); +#else // BOOST_VERSION >= 103700 + path parent_path = filename.branch_path(); + glob.assign_glob(filename.leaf()); +#endif // BOOST_VERSION >= 103700 + + bool files_found = false; + if (exists(parent_path)) { + filesystem::directory_iterator end; + for (filesystem::directory_iterator iter(parent_path); + iter != end; + ++iter) { + if (is_regular_file(*iter)) { +#if BOOST_VERSION >= 103700 + string base = (*iter).filename(); +#else // BOOST_VERSION >= 103700 + string base = (*iter).leaf(); +#endif // BOOST_VERSION >= 103700 + if (glob.match(base)) { + path inner_file(*iter); + ifstream stream(inner_file); + instance_t instance(context, stream, master, &inner_file, this); + instance.parse(); + files_found = true; + } + } + } + } + + if (! files_found) throw_(std::runtime_error, _("File to include was not found: '%1'") << filename); - ifstream stream(filename); - - instance_t instance(context, stream, master, &filename, this); - instance.parse(); } void instance_t::master_account_directive(char * line) @@ -735,6 +765,52 @@ void instance_t::fixed_directive(char * line) } } +void instance_t::payee_mapping_directive(char * line) +{ + char * payee = skip_ws(line); + char * regex = next_element(payee, true); + + if (regex) + context.journal.payee_mappings.push_back + (payee_mapping_t(mask_t(regex), payee)); + + while (peek_whitespace_line()) { + std::streamsize len = read_line(line); + assert(len > 0); + + regex = skip_ws(line); + if (! *regex) + break; + + context.journal.payee_mappings.push_back + (payee_mapping_t(mask_t(regex), payee)); + } +} + +void instance_t::account_mapping_directive(char * line) +{ + char * account_name = skip_ws(line); + char * payee_regex = next_element(account_name, true); + + if (payee_regex) + context.journal.account_mappings.push_back + (account_mapping_t(mask_t(payee_regex), + context.top_account()->find_account(account_name))); + + while (peek_whitespace_line()) { + std::streamsize len = read_line(line); + assert(len > 0); + + payee_regex = skip_ws(line); + if (! *payee_regex) + break; + + context.journal.account_mappings.push_back + (account_mapping_t(mask_t(payee_regex), + context.top_account()->find_account(account_name))); + } +} + void instance_t::tag_directive(char * line) { string tag(trim_ws(line)); @@ -782,6 +858,13 @@ bool instance_t::general_directive(char * line) } break; + case 'c': + if (std::strcmp(p, "capture") == 0) { + account_mapping_directive(arg); + return true; + } + break; + case 'd': if (std::strcmp(p, "def") == 0 || std::strcmp(p, "define") == 0) { define_directive(arg); @@ -810,6 +893,13 @@ bool instance_t::general_directive(char * line) } break; + case 'p': + if (std::strcmp(p, "payee") == 0) { + payee_mapping_directive(arg); + return true; + } + break; + case 't': if (std::strcmp(p, "tag") == 0) { tag_directive(arg); @@ -929,6 +1019,15 @@ post_t * instance_t::parse_post(char * line, post->account->add_flags(ACCOUNT_KNOWN); } + if (post->account->name == _("Unknown")) { + foreach (account_mapping_t& value, context.journal.account_mappings) { + if (value.first.match(xact->payee)) { + post->account = value.second; + break; + } + } + } + // Parse the optional amount bool saw_amount = false; @@ -988,6 +1087,7 @@ post_t * instance_t::parse_post(char * line, if (*++next == '@') { per_unit = false; + post->add_flags(POST_COST_IN_FULL); DEBUG("textual.parse", "line " << linenum << ": " << "And it's for a total price"); } @@ -1127,7 +1227,7 @@ post_t * instance_t::parse_post(char * line, // Parse the optional note if (next && *next == ';') { - post->append_note(++next, current_year); + post->append_note(++next, true, current_year); next = line + len; DEBUG("textual.parse", "line " << linenum << ": " << "Parsed a posting note"); @@ -1146,7 +1246,7 @@ post_t * instance_t::parse_post(char * line, if (! context.state_stack.empty()) { foreach (const state_t& state, context.state_stack) if (state.type() == typeid(string)) - post->parse_tags(boost::get<string>(state).c_str()); + post->parse_tags(boost::get<string>(state).c_str(), true); } TRACE_STOP(post_details, 1); @@ -1242,7 +1342,14 @@ xact_t * instance_t::parse_xact(char * line, if (next && *next) { char * p = next_element(next, true); - xact->payee = next; + foreach (payee_mapping_t& value, context.journal.payee_mappings) { + if (value.first.match(next)) { + xact->payee = value.second; + break; + } + } + if (xact->payee.empty()) + xact->payee = next; next = p; } else { xact->payee = _("<Unspecified payee>"); @@ -1276,7 +1383,7 @@ xact_t * instance_t::parse_xact(char * line, item = xact.get(); // This is a trailing note, and possibly a metadata info tag - item->append_note(p + 1, current_year); + item->append_note(p + 1, true, current_year); item->pos->end_pos = curr_pos; item->pos->end_line++; } else { @@ -1310,7 +1417,7 @@ xact_t * instance_t::parse_xact(char * line, if (! context.state_stack.empty()) { foreach (const state_t& state, context.state_stack) if (state.type() == typeid(string)) - xact->parse_tags(boost::get<string>(state).c_str()); + xact->parse_tags(boost::get<string>(state).c_str(), false); } TRACE_STOP(xact_details, 1); diff --git a/src/timelog.cc b/src/timelog.cc index 25bf8e94..217aa581 100644 --- a/src/timelog.cc +++ b/src/timelog.cc @@ -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 diff --git a/src/timelog.h b/src/timelog.h index 83256dfa..caa23ec0 100644 --- a/src/timelog.h +++ b/src/timelog.h @@ -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 diff --git a/src/times.cc b/src/times.cc index 963639f1..be488baf 100644 --- a/src/times.cc +++ b/src/times.cc @@ -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 diff --git a/src/times.h b/src/times.h index 826937bb..48a9dd2a 100644 --- a/src/times.h +++ b/src/times.h @@ -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 diff --git a/src/token.cc b/src/token.cc index 81c54a82..a34cdcd0 100644 --- a/src/token.cc +++ b/src/token.cc @@ -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 diff --git a/src/token.h b/src/token.h index 670f16e3..8d70996b 100644 --- a/src/token.h +++ b/src/token.h @@ -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 diff --git a/src/unistring.h b/src/unistring.h index 7c433d9d..d42f2b28 100644 --- a/src/unistring.h +++ b/src/unistring.h @@ -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 @@ -55,6 +55,8 @@ namespace ledger { class unistring { public: + static const std::size_t npos = static_cast<std::size_t>(-1); + std::vector<boost::uint32_t> utf32chars; unistring() { @@ -96,6 +98,23 @@ public: return utf8result; } + + std::size_t find(const boost::uint32_t __s, std::size_t __pos = 0) const { + std::size_t idx = 0; + foreach (const boost::uint32_t& ch, utf32chars) { + if (idx >= __pos && ch == __s) + return idx; + idx++; + } + return npos; + } + + boost::uint32_t& operator[](const std::size_t index) { + return utf32chars[index]; + } + const boost::uint32_t& operator[](const std::size_t index) const { + return utf32chars[index]; + } }; inline void justify(std::ostream& out, diff --git a/src/utils.cc b/src/utils.cc index f2460ba1..c3fecca0 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -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 diff --git a/src/utils.h b/src/utils.h index ab8fb495..a0c3f49f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -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 @@ -659,6 +659,16 @@ inline string to_hex(uint_least32_t * message_digest, const int len = 1) return buf.str(); } +inline string sha1sum(const string& str) +{ + SHA1 sha; + sha.Reset(); + sha << str.c_str(); + uint_least32_t message_digest[5]; + sha.Result(message_digest); + return to_hex(message_digest, 5); +} + class push_xml { std::ostream& out; diff --git a/src/value.cc b/src/value.cc index 7d079caf..54798162 100644 --- a/src/value.cc +++ b/src/value.cc @@ -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 @@ -857,12 +857,12 @@ bool value_t::is_less_than(const value_t& val) const case INTEGER: return as_amount() < val.as_long(); case AMOUNT: - try { + if (as_amount().commodity() == val.as_amount().commodity() || + ! as_amount().has_commodity() || + ! val.as_amount().has_commodity()) return as_amount() < val.as_amount(); - } - catch (const amount_error&) { + else return commodity_t::compare_by_commodity()(&as_amount(), &val.as_amount()); - } default: break; } diff --git a/src/value.h b/src/value.h index ffbb89e8..1c1d8b6c 100644 --- a/src/value.h +++ b/src/value.h @@ -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 @@ -919,11 +919,11 @@ public: /** * Printing methods. */ - void print(std::ostream& out, - const int first_width = -1, - const int latter_width = -1, - const bool right_justify = false, - const bool colorize = false) const; + void print(std::ostream& out, + const int first_width = -1, + const int latter_width = -1, + const bool right_justify = false, + const bool colorize = false) const; void dump(std::ostream& out, const bool relaxed = true) const; /** diff --git a/src/xact.cc b/src/xact.cc index 623c5772..1a022387 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -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 @@ -454,12 +454,7 @@ string xact_t::idstring() const string xact_t::id() const { - SHA1 sha; - sha.Reset(); - sha << idstring().c_str(); - uint_least32_t message_digest[5]; - sha.Result(message_digest); - return to_hex(message_digest, 5); + return sha1sum(idstring()); } namespace { @@ -760,7 +755,7 @@ void to_xml(std::ostream& out, const xact_t& xact) if (xact.metadata) { push_xml y(out, "metadata"); foreach (const item_t::string_map::value_type& pair, *xact.metadata) { - if (pair.second) { + if (pair.second.first) { push_xml z(out, "variable"); { push_xml w(out, "key"); @@ -768,7 +763,7 @@ void to_xml(std::ostream& out, const xact_t& xact) } { push_xml w(out, "value"); - out << y.guard(*pair.second); + out << y.guard(*pair.second.first); } } else { push_xml z(out, "tag"); @@ -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 @@ -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 @@ -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 diff --git a/test/baseline/opt-by-payee.test b/test/baseline/opt-by-payee.test index ec8d0a73..2c70ecfb 100644 --- a/test/baseline/opt-by-payee.test +++ b/test/baseline/opt-by-payee.test @@ -192,29 +192,29 @@ reg --by-payee not @End Expenses:Books $120.00 Assets:Cash >>>1 -08-Jan-01 January Assets:Cash $-20.00 $-20.00 - Expenses:Books $20.00 0 +08-Apr-01 April Assets:Cash $-80.00 $-80.00 + Expenses:Books $80.00 0 +08-Aug-01 August Assets:Cash $-160.00 $-160.00 + Expenses:Books $160.00 0 +08-Dec-01 December Assets:Cash $-240.00 $-240.00 + Expenses:Books $240.00 0 08-Feb-01 February Assets:Cash $-40.00 $-40.00 Expenses:Books $40.00 0 +08-Jan-01 January Assets:Cash $-20.00 $-20.00 + Expenses:Books $20.00 0 +08-Jul-01 July Assets:Cash $-140.00 $-140.00 + Expenses:Books $140.00 0 +08-Jun-01 June Assets:Cash $-120.00 $-120.00 + Expenses:Books $120.00 0 08-Mar-01 March Assets:Cash $-60.00 $-60.00 Expenses:Books $60.00 0 -08-Apr-01 April Assets:Cash $-80.00 $-80.00 - Expenses:Books $80.00 0 08-May-01 May Assets:Cash $-100.00 $-100.00 Expenses:Books $100.00 0 -08-Jun-01 June Assets:Cash $-120.00 $-120.00 - Expenses:Books $120.00 0 -08-Jul-01 July Assets:Cash $-140.00 $-140.00 - Expenses:Books $140.00 0 -08-Aug-01 August Assets:Cash $-160.00 $-160.00 - Expenses:Books $160.00 0 -08-Sep-01 September Assets:Cash $-180.00 $-180.00 - Expenses:Books $180.00 0 -08-Oct-01 October Assets:Cash $-200.00 $-200.00 - Expenses:Books $200.00 0 08-Nov-01 November Assets:Cash $-220.00 $-220.00 Expenses:Books $220.00 0 -08-Dec-01 December Assets:Cash $-240.00 $-240.00 - Expenses:Books $240.00 0 +08-Oct-01 October Assets:Cash $-200.00 $-200.00 + Expenses:Books $200.00 0 +08-Sep-01 September Assets:Cash $-180.00 $-180.00 + Expenses:Books $180.00 0 >>>2 === 0 diff --git a/test/baseline/opt-effective.test b/test/baseline/opt-effective.test index adf9f8af..0ce08550 100644 --- a/test/baseline/opt-effective.test +++ b/test/baseline/opt-effective.test @@ -14,8 +14,8 @@ reg --effective >>>1 08-Jan-01 January Expenses:Books $10.00 $10.00 Assets:Cash $-10.00 0 -08-Jan-01 End of January Assets:Cash $-10.00 $-10.00 -08-Feb-01 End of January Expenses:Books $10.00 0 +08-Feb-01 End of January Expenses:Books $10.00 $10.00 +08-Jan-01 End of January Assets:Cash $-10.00 0 08-Feb-01 February Expenses:Books $20.00 $20.00 Assets:Cash $-20.00 0 >>>2 diff --git a/test/baseline/opt-forecast-while.test b/test/baseline/opt-forecast-while.test index aebb127f..c2563a75 100644 --- a/test/baseline/opt-forecast-while.test +++ b/test/baseline/opt-forecast-while.test @@ -229,29 +229,29 @@ reg --now=2009/03/21 --forecast-while='total < $3500' books 09-Apr-01 April Expenses:Books $40.00 $1720.00 09-Apr-30 End of April Expenses:Books $40.00 $1760.00 09-May-01 May Expenses:Books $50.00 $1810.00 -09-May-01 Forecast transaction Expenses:Books $10.00 $1820.00 -09-May-31 End of May Expenses:Books $50.00 $1870.00 -09-Jun-01 June Expenses:Books $60.00 $1930.00 -09-Jun-01 Forecast transaction Expenses:Books $10.00 $1940.00 -09-Jun-30 End of June Expenses:Books $60.00 $2000.00 -09-Jul-01 July Expenses:Books $70.00 $2070.00 -09-Jul-01 Forecast transaction Expenses:Books $10.00 $2080.00 -09-Jul-31 End of July Expenses:Books $70.00 $2150.00 -09-Aug-01 August Expenses:Books $80.00 $2230.00 -09-Aug-01 Forecast transaction Expenses:Books $10.00 $2240.00 -09-Aug-31 End of August Expenses:Books $80.00 $2320.00 -09-Sep-01 September Expenses:Books $90.00 $2410.00 -09-Sep-01 Forecast transaction Expenses:Books $10.00 $2420.00 -09-Sep-30 End of September Expenses:Books $90.00 $2510.00 -09-Oct-01 October Expenses:Books $100.00 $2610.00 -09-Oct-01 Forecast transaction Expenses:Books $10.00 $2620.00 -09-Oct-31 End of October Expenses:Books $100.00 $2720.00 -09-Nov-01 November Expenses:Books $110.00 $2830.00 -09-Nov-01 Forecast transaction Expenses:Books $10.00 $2840.00 -09-Nov-30 End of November Expenses:Books $110.00 $2950.00 -09-Dec-01 December Expenses:Books $120.00 $3070.00 -09-Dec-01 Forecast transaction Expenses:Books $10.00 $3080.00 -09-Dec-31 End of December Expenses:Books $120.00 $3200.00 +09-May-31 End of May Expenses:Books $50.00 $1860.00 +09-Jun-01 June Expenses:Books $60.00 $1920.00 +09-Jun-30 End of June Expenses:Books $60.00 $1980.00 +09-Jul-01 July Expenses:Books $70.00 $2050.00 +09-Jul-31 End of July Expenses:Books $70.00 $2120.00 +09-Aug-01 August Expenses:Books $80.00 $2200.00 +09-Aug-31 End of August Expenses:Books $80.00 $2280.00 +09-Sep-01 September Expenses:Books $90.00 $2370.00 +09-Sep-30 End of September Expenses:Books $90.00 $2460.00 +09-Oct-01 October Expenses:Books $100.00 $2560.00 +09-Oct-31 End of October Expenses:Books $100.00 $2660.00 +09-Nov-01 November Expenses:Books $110.00 $2770.00 +09-Nov-30 End of November Expenses:Books $110.00 $2880.00 +09-Dec-01 December Expenses:Books $120.00 $3000.00 +09-Dec-31 End of December Expenses:Books $120.00 $3120.00 +09-May-01 Forecast transaction Expenses:Books $10.00 $3130.00 +09-Jun-01 Forecast transaction Expenses:Books $10.00 $3140.00 +09-Jul-01 Forecast transaction Expenses:Books $10.00 $3150.00 +09-Aug-01 Forecast transaction Expenses:Books $10.00 $3160.00 +09-Sep-01 Forecast transaction Expenses:Books $10.00 $3170.00 +09-Oct-01 Forecast transaction Expenses:Books $10.00 $3180.00 +09-Nov-01 Forecast transaction Expenses:Books $10.00 $3190.00 +09-Dec-01 Forecast transaction Expenses:Books $10.00 $3200.00 10-Jan-01 Forecast transaction Expenses:Books $10.00 $3210.00 10-Feb-01 Forecast transaction Expenses:Books $10.00 $3220.00 10-Mar-01 Forecast transaction Expenses:Books $10.00 $3230.00 diff --git a/test/baseline/opt-lots_basis.test b/test/baseline/opt-lots_basis.test index 51235473..d392fe33 100644 --- a/test/baseline/opt-lots_basis.test +++ b/test/baseline/opt-lots_basis.test @@ -871,6 +871,12 @@ D 1.00G Expenses:Fees:Mail 30c 1.50G Expenses:Fees:Mail 30c 1.51G Assets:Tajer -1.51G 0 +06-Mar-27 Player Assets:Tajer 160.00G 160.00G + Assets:Tajer:Items -160.00G 0 + Expenses:Capital Loss 40.00G 40.00G + Equity:Capital Losses -40.00G 0 +06-Mar-27 Player Expenses:Fees:Mail 30c 30c + Assets:Tajer -30c 0 06-Mar-26 Player Expenses:Items 54.00G 54.00G Expenses:Items 10.00G 64.00G Expenses:Fees:Bank 10.00G 74.00G @@ -878,12 +884,6 @@ D 1.00G Expenses:Fees:Mail 3.30s 74.10G Expenses:Fees:Mail 30c 74.10G Assets:Tajer -74.10G 0 -06-Mar-27 Player Assets:Tajer 160.00G 160.00G - Assets:Tajer:Items -160.00G 0 - Expenses:Capital Loss 40.00G 40.00G - Equity:Capital Losses -40.00G 0 -06-Mar-27 Player Expenses:Fees:Mail 30c 30c - Assets:Tajer -30c 0 06-Apr-01 Auction House Assets:Tajer:Items 155.00G 155.00G Assets:Tajer -155.00G 0 >>>2 diff --git a/test/baseline/opt-lots_basis_base.test b/test/baseline/opt-lots_basis_base.test index 2fa1fdb1..e3aca02d 100644 --- a/test/baseline/opt-lots_basis_base.test +++ b/test/baseline/opt-lots_basis_base.test @@ -872,6 +872,12 @@ D 1.00G Expenses:Fees:Mail 30c 15030c Expenses:Fees:Mail 30c 15060c Assets:Tajer -15060c 0 +06-Mar-27 Player Assets:Tajer 1600000c 1600000c + Assets:Tajer:Items -1600000c 0 + Expenses:Capital Loss 400000c 400000c + Equity:Capital Losses -400000c 0 +06-Mar-27 Player Expenses:Fees:Mail 30c 30c + Assets:Tajer -30c 0 06-Mar-26 Player Expenses:Items 540000c 540000c Expenses:Items 100000c 640000c Expenses:Fees:Bank 100000c 740000c @@ -879,12 +885,6 @@ D 1.00G Expenses:Fees:Mail 330c 740960c Expenses:Fees:Mail 30c 740990c Assets:Tajer -740990c 0 -06-Mar-27 Player Assets:Tajer 1600000c 1600000c - Assets:Tajer:Items -1600000c 0 - Expenses:Capital Loss 400000c 400000c - Equity:Capital Losses -400000c 0 -06-Mar-27 Player Expenses:Fees:Mail 30c 30c - Assets:Tajer -30c 0 06-Apr-01 Auction House Assets:Tajer:Items 1550000c 1550000c Assets:Tajer -1550000c 0 >>>2 diff --git a/test/baseline/opt-print-format.test b/test/baseline/opt-print-format.test deleted file mode 100644 index 103ceb1e..00000000 --- a/test/baseline/opt-print-format.test +++ /dev/null @@ -1,10 +0,0 @@ -print --print-format='%(amount)\n' -<<< -2007/02/02 RD VMMXX - Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 - Income:Dividends:Vanguard:VMMXX $-0.35 ->>>1 -0.350 VMMXX {$1.00} [2007/02/02] -$-0.35 ->>>2 -=== 0 diff --git a/test/baseline/opt-strict.test b/test/baseline/opt-strict.test index 1917d946..a0e593b2 100644 --- a/test/baseline/opt-strict.test +++ b/test/baseline/opt-strict.test @@ -14,10 +14,10 @@ reg --strict >>>1 07-Feb-02 Foo Expenses:Food $10.00 $10.00 Assets:Cash $-10.00 0 -07-Feb-02 Baz Expenses:Foodx $30.00 $30.00 - Assets:Cash $-30.00 0 07-Feb-03 Bar Expenses:Food $20.00 $20.00 Assets:Cash $-20.00 0 +07-Feb-02 Baz Expenses:Foodx $30.00 $30.00 + Assets:Cash $-30.00 0 >>>2 Warning: "$FILE", line 10: Unknown account 'Expenses:Foodx' === 0 diff --git a/tools/Makefile.am b/tools/Makefile.am index 09daafe6..e821f150 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,9 +1,9 @@ VERSION = 3.0.0 ACLOCAL_AMFLAGS = -I m4 dist_man_MANS = doc/ledger.1 -SUBDIRS = po intl +SUBDIRS = po intl EXTRA_DIST = autogen.sh config.rpath contrib src/system.hh.in -DISTCLEANFILES = .timestamp +DISTCLEANFILES = .timestamp lib_LTLIBRARIES = \ libledger_report.la \ @@ -55,6 +55,7 @@ libledger_expr_la_CPPFLAGS = $(lib_cppflags) libledger_expr_la_LDFLAGS = -release $(VERSION) libledger_data_la_SOURCES = \ + src/lookup.cc \ src/compare.cc \ src/iterators.cc \ src/timelog.cc \ @@ -72,9 +73,12 @@ libledger_data_la_LDFLAGS = -release $(VERSION) libledger_report_la_SOURCES = \ src/stats.cc \ src/generate.cc \ + src/csv.cc \ + src/convert.cc \ src/draft.cc \ - src/emacs.cc \ - src/xml.cc \ + src/emacs.cc \ + src/xml.cc \ + src/print.cc \ src/output.cc \ src/precmd.cc \ src/chain.cc \ @@ -126,6 +130,7 @@ pkginclude_HEADERS = \ src/timelog.h \ src/iterators.h \ src/compare.h \ + src/lookup.h \ \ src/session.h \ src/report.h \ @@ -133,9 +138,12 @@ pkginclude_HEADERS = \ src/temps.h \ src/chain.h \ src/precmd.h \ + src/csv.h \ + src/convert.h \ src/draft.h \ src/generate.h \ src/stats.h \ + src/print.h \ src/output.h \ src/xml.h \ src/emacs.h \ @@ -167,6 +175,7 @@ BUILT_SOURCES += system.hh.gch CLEANFILES += system.hh.gch system.hh.gch: system.hh + -rm -f $@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(lib_cppflags) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) \ -o $@ $< @@ -190,7 +199,7 @@ dist_lisp_LISP = lisp/ledger.el lisp/timeclock.el ELCFILES = DISTCLEANFILES += ledger.elc timeclock.elc -all_sources = $(libledger_util_la_SOURCES) \ +all_sources = $(libledger_util_la_SOURCES) \ $(libledger_math_la_SOURCES) \ $(libledger_expr_la_SOURCES) \ $(libledger_data_la_SOURCES) \ @@ -198,7 +207,7 @@ all_sources = $(libledger_util_la_SOURCES) \ $(libledger_python_la_SOURCES) \ src/pyledger.cc -all_files = $(all_sources) $(pkginclude_HEADERS) +all_files = $(all_sources) $(pkginclude_HEADERS) ###################################################################### @@ -271,7 +280,7 @@ UtilTests_SOURCES = \ test/unit/t_times.h UtilTests_CPPFLAGS = -I$(srcdir)/test $(lib_cppflags) -UtilTests_LDADD = libledger_util.la -lcppunit +UtilTests_LDADD = libledger_util.la -lcppunit MathTests_SOURCES = \ test/UnitTests.cc \ @@ -285,7 +294,7 @@ MathTests_SOURCES = \ test/unit/t_balance.h MathTests_CPPFLAGS = -I$(srcdir)/test $(lib_cppflags) -MathTests_LDADD = libledger_math.la $(UtilTests_LDADD) +MathTests_LDADD = libledger_math.la $(UtilTests_LDADD) ExprTests_SOURCES = \ test/UnitTests.cc \ @@ -295,7 +304,7 @@ ExprTests_SOURCES = \ test/unit/t_expr.h ExprTests_CPPFLAGS = -I$(srcdir)/test $(lib_cppflags) -ExprTests_LDADD = libledger_expr.la $(MathTests_LDADD) +ExprTests_LDADD = libledger_expr.la $(MathTests_LDADD) DataTests_SOURCES = \ test/UnitTests.cc \ @@ -303,7 +312,7 @@ DataTests_SOURCES = \ test/DataTests.cc DataTests_CPPFLAGS = -I$(srcdir)/test $(lib_cppflags) -DataTests_LDADD = libledger_data.la $(ExprTests_LDADD) +DataTests_LDADD = libledger_data.la $(ExprTests_LDADD) ReportTests_SOURCES = \ test/UnitTests.cc \ @@ -393,7 +402,7 @@ test/input/mondo.dat: test/input/standard.dat @for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do \ for j in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do \ cat $< >> $@; \ - done; \ + done; \ done ConfirmTests: $(srcdir)/test/ConfirmTests.py |