home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World 2000 February
/
PCWorld_2000-02_cd.bin
/
live
/
usr
/
X11R6
/
lib
/
X11
/
cbb
/
engine.pl
< prev
next >
Wrap
Perl Script
|
1998-10-07
|
32KB
|
1,230 lines
#!/usr/bin/perl
# engine.pl - the CBB 'engine'.
# This script implements a transaction abstract data type
# It encapsulates a list a transactions and the functions
# required to manipulate the transactions.
#
# Written by Curtis Olson. Started August 22, 1994.
#
# Copyright (C) 1994 - 1997 Curtis L. Olson - curt@sledge.mn.org
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
# $Id: engine.pl,v 2.20 1998/08/14 14:30:11 curt Exp $
# (Log is kept at end of this file)
package CBB;
use strict; # don't take no guff
# @INC specifies the installed location of the necessary pieces.
# It should already be setup by wrapper.pl
require "common.pl";
require "log.pl";
$| = 1; # flush buffers after every write
if ( $CBB::logging != 0 && $CBB::logging != 1) {
# if not specified elsewhere, turn on logging
$CBB::logging = 1; # 0 = off, 1 = on
}
if ( $CBB::debug != 0 && $CBB::debug != 1) {
# if not specified elsewhere, turn off debugging.
$CBB::debug = 0; # 0 = off, 1 = on
}
# Global variables
# %CBB::TRANS - an associative array of transactions and transaction keys
# @CBB::KEYS - a sorted list of transaction keys (for traversing the trans list)
# $CBB::sorted_keys - specifies whether the list in @CBB::KEYS is valid
# $CBB::calced - specified whether the transactions have been properly calculated
# $CBB::current - specifies the "current" position in the @CBB::KEYS array
# $CBB::current_file - full name of currently opened transaction file
# %CBB::BALS - an associative array used to store account information
# $CBB::version - version number (set in common.pl)
&init_trans(); # initialize %CBB::TRANS, @CBB::KEYS, and $CBB::sorted_keys
open(DEBUG, ">debug") if $CBB::debug;
# toggle debugging
sub debug {
# in: flag
# out: flag
my($newdebug) = @_;
if ($newdebug == 1) {
# turning debugging on
if ($CBB::debug == 1) {
# already on, do nothing
} else {
$CBB::debug = 1;
open(DEBUG, ">debug");
}
} else {
# turning of debugging
if ($CBB::debug == 0) {
# already off, do nothing
} else {
$CBB::debug = 0;
close(DEBUG);
}
}
return $CBB::debug;
}
# get next available key for a specified date
sub get_next_key {
# in: date
# out: key
my($date) = @_;
my($count) = 0;
while ( $CBB::TRANS{"$date-".&pad($count)} ) {
$count++;
}
return "$date-".&pad($count);
}
# set @CBB::KEYS = sorted list of transaction keys
sub sort_keys {
$CBB::sorted_keys = 1;
$CBB::current = 0;
print DEBUG "sort_keys()\n" if $CBB::debug;
@CBB::KEYS = sort(keys %CBB::TRANS);
}
# recalculate the transactions
sub calc_trans {
my($total, $ntotal, $stotal, $ctotal) = (0.00, 0.00, 0.00, 0.00);
my($count, $ncount, $scount, $ccount) = (0, 0, 0, 0);
my($key);
my($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $junk);
my($current_date) = &raw_date();
$CBB::calced = 1;
print DEBUG "calc_trans()\n" if $CBB::debug;
if ($CBB::sorted_keys == 0) {
&sort_keys();
}
$CBB::BALS{"Current"} = 0.00;
foreach $key (@CBB::KEYS) {
($date, $check, $desc, $debit, $credit, $cat, $com, $cleared,
$junk) = split(/\t/, $CBB::TRANS{$key});
$total = $total + $credit - $debit;
$count++;
if ( $date <= $current_date ) {
$CBB::BALS{"Current"} = $total;
}
if ( ($cleared eq "x") || ($cleared eq "X") ) {
$ctotal = $ctotal + $credit - $debit;
$ccount++;
} elsif ( $cleared eq "*" ) {
$stotal = $stotal + $credit - $debit;
$scount++;
} else {
$ntotal = $ntotal + $credit - $debit;
$ncount++;
}
$CBB::TRANS{$key} =
"$date\t$check\t$desc\t$debit\t$credit\t$cat\t$com\t$cleared\t".
sprintf("%.2f", $total);
}
$CBB::BALS{"Amount"} = $total;
$CBB::BALS{"Count"} = $count;
$CBB::BALS{"Xamount"} = $ctotal;
$CBB::BALS{"Xcount"} = $ccount;
$CBB::BALS{"*amount"} = $stotal;
$CBB::BALS{"*count"} = $scount;
$CBB::BALS{"Namount"} = $ntotal;
$CBB::BALS{"Ncount"} = $ncount;
}
# create a transaction (and add to the transaction list)
sub create_trans {
# in: transaction
# out: keyed_transaction
my($trans) = @_;
my($key);
$CBB::sorted_keys = 0;
$CBB::calced = 0;
&insert_and_update_mem($trans);
my($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total) =
split(/\t/, $trans);
if ( length($date) == 6 ) {
# for backwards compatibility ... shouldn't be needed now.
$date = "19$date";
}
$key = &get_next_key($date);
$trans = "$date\t$check\t$desc\t$debit\t$credit\t$cat\t$com\t$cleared\t$total";
$CBB::TRANS{$key} = "$trans";
print DEBUG "created: $key\t$trans\n" if $CBB::debug;
return "$key\t$trans";
}
# create a transfer transaction in the current file and the transfer to file
sub create_xfer {
# in: transaction
# out: keyed_transaction
my($trans) = @_;
my($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total) =
split(/\t/, $trans);
my($orig_file) = $CBB::current_file;
my($to_trans, $to_file, $from_cat);
my($key, $result);
my($returned_result);
$CBB::sorted_keys = 0;
$CBB::calced = 0;
print DEBUG "(xfer) current_file = $CBB::current_file\n" if $CBB::debug;
# determine the "from" category
$from_cat = "[".&file_basename(&file_root($CBB::current_file))."]";
# determine the "to" file name
$to_file = $cat;
chop($to_file);
$to_file = substr($to_file, 1);
$to_file = &file_dirname($CBB::current_file)."/$to_file";
print DEBUG "to file = '$to_file' ($to_file.cbb)\n" if $CBB::debug;
if ( -e "$to_file.cbb" ) {
$to_file .= ".cbb";
} elsif ( -e "$to_file.dir" ) {
$to_file .= ".dir";
} else {
return "error";
}
print DEBUG "Transfer to $to_file\n" if $CBB::debug;
# create the "to" transaction
$to_trans = "$date\t$check\t$desc\t".$credit."\t".$debit."\t".
$from_cat."\t$com\t$cleared\t$total";
# we need special handling here to preserve the .cbb file
# save the current transactions to a temporary file
# before loading the "to" account
$result = &save_trans("$orig_file.$$.tmp");
return "error" if ( $result eq "error" );
%CBB::TRANS = (); # clear out any transactions from the current file
# open the "to" account
$result = &load_trans($to_file);
return "error" if ( $result eq "error" );
$result = &create_trans($to_trans);
$result = &save_trans($to_file);
$result = &load_cbb_trans("$orig_file.$$.tmp");
return "error" if ( $result eq "error" );
unlink("$orig_file.$$.tmp");
$CBB::current_file = $orig_file;
# create the "from" transaction
$returned_result = &create_trans($trans);
return "$returned_result";
}
# update a transaction (replace in the transaction list)
sub update_trans {
# in: keyed_transaction
# out: keyed_transaction
my($keyed_trans) = @_;
my($key, $trans, $result);
$CBB::sorted_keys = 0;
$CBB::calced = 0;
($key, $trans) = split(/\t/, $keyed_trans, 2);
&delete_trans($key);
$result = &create_trans($trans);
print DEBUG "updated: $key\n" if $CBB::debug;
print DEBUG " to: $result\n" if $CBB::debug;
return "$result";
}
# update a transfer transaction (replace in the transaction list)
sub update_xfer {
# in: keyed_transaction
# out: keyed_transaction
my($keyed_trans) = @_;
my($key, $trans, $result);
$CBB::sorted_keys = 0;
$CBB::calced = 0;
($key, $trans) = split(/\t/, $keyed_trans, 2);
&delete_xfer($key);
$result = &create_xfer($trans);
print DEBUG "updated: $key\n" if $CBB::debug;
print DEBUG " to: $result\n" if $CBB::debug;
return "$result";
}
# delete a transaction given the key
sub delete_trans {
# in: key
my($key) = @_;
$CBB::sorted_keys = 0;
$CBB::calced = 0;
delete $CBB::TRANS{$key};
if ($CBB::current > 0) {
--$CBB::current;
}
print DEBUG "deleted: $key\n" if $CBB::debug;
return "ok";
}
# delete an transfer transaction in the transfer to file
sub delete_xfer {
# in: key
my($key) = @_;
my($orig_file, $orig_current) = ($CBB::current_file, $CBB::current);
my($count) = 0;
my($to_file, $from_cat, $found_key, $found_trans);
my($result);
my($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total)
= split(/\t/, $CBB::TRANS{$key});
$CBB::sorted_keys = 0;
$CBB::calced = 0;
# determine the "from" category
$from_cat = "[".&file_basename(&file_root($CBB::current_file))."]";
# determine the "to" file name
$to_file = $cat;
chop($to_file);
$to_file = substr($to_file, 1);
$to_file = &file_dirname($CBB::current_file)."/$to_file";
print DEBUG "to file = '$to_file' ($to_file.cbb)\n" if $CBB::debug;
if ( -e "$to_file.cbb" ) {
$to_file .= ".cbb";
} else {
return "error";
}
print DEBUG "Deleting transfer to $to_file\n" if $CBB::debug;
# We need special handling here to preserve the .cbb file. Save
# the current transactions to a temporary file before loading the
# "to" account.
$result = &save_trans("$orig_file.$$.tmp");
return "error" if ( $result eq "error" );
# open the "to" account
$result = &load_trans($to_file);
return "error" if ( $result eq "error" );
# now search for the transaction
while ( $found_trans = $CBB::TRANS{"$date-".&pad($count)} ) {
my($found_date, $found_check, $found_desc, $found_debit,
$found_credit, $found_cat, $found_com, $found_cleared,
$found_total) = split(/\t/, $found_trans);
last if (($found_check eq $check) &&
($found_desc eq $desc) &&
($found_debit == $credit) &&
($found_credit == $debit) &&
($found_com eq $com) &&
($found_cat eq $from_cat) &&
($found_key = "$date-".&pad($count)) );
$count++;
}
print DEBUG "Found key: $found_key\n" if $CBB::debug;
if ( $found_key ) {
delete $CBB::TRANS{$found_key};
$CBB::calced = 0;
$CBB::sorted_keys = 0;
} else {
print DEBUG "Transaction not found in $to_file\n" if $CBB::debug;
}
# now save the "to" account
$result = &save_trans($to_file);
# revert to orig account
$result = &load_cbb_trans("$orig_file.$$.tmp");
return "error" if ( $result eq "error" );
unlink("$orig_file.$$.tmp");
# restore global variables
$CBB::current_file = $orig_file;
$CBB::current = $orig_current;
$CBB::calced = 0;
$CBB::sorted_keys = 0;
delete $CBB::TRANS{$key};
if ($CBB::current > 0) {
--$CBB::current;
}
print DEBUG "deleted: $key\n" if $CBB::debug;
return "ok";
}
# return the next transaction
sub next_trans {
my($trans);
if ($CBB::sorted_keys == 0) {
&sort_keys();
}
if ($CBB::calced == 0) {
&calc_trans();
}
++$CBB::current;
$trans = $CBB::TRANS{$CBB::KEYS[$CBB::current]};
if ( $trans ) {
return "$CBB::KEYS[$CBB::current]\t$trans";
} else {
return "none";
}
}
# return the transaction specified by a key
sub find_trans {
# uses a binary search so that we can keep $CBB::current current.
# Yeeeks! I have to think for a change.
# Hmmm, maybe I should rethink my data structures ... nah. :)
my($key) = @_;
my($left, $middle, $right) = (0, 0, $#CBB::KEYS);
my($trans);
if ($CBB::sorted_keys == 0) {
&sort_keys();
}
if ($CBB::calced == 0) {
&calc_trans();
}
$trans = "";
while ( $left <= $right ) {
$middle = int( ($left + $right) / 2 );
print DEBUG "$left < $middle < $right\n" if $CBB::debug;
if ( $CBB::KEYS[$middle] lt $key ) {
$left = $middle + 1;
print DEBUG " left = middle + 1\n" if $CBB::debug;
} elsif ( $CBB::KEYS[$middle] gt $key ) {
$right = $middle - 1;
print DEBUG " right = middle - 1\n" if $CBB::debug;
} else {
# we found it, set $trans to what we want and force an exit of
# the while loop
$trans = $CBB::TRANS{$CBB::KEYS[$middle]};
print DEBUG " found it: $trans\n" if $CBB::debug;
$CBB::current = $middle;
$left = $right + 1;
}
}
print DEBUG "found: $key\t$trans\n" if $CBB::debug;
if ( $trans ) {
return "$key\t$trans";
} else {
return "none";
}
}
# returns the current index
sub get_current_index {
return ($CBB::current + 1);
}
# return the first transaction
sub first_trans {
my($trans);
if ($CBB::sorted_keys == 0) {
&sort_keys();
}
if ($CBB::calced == 0) {
&calc_trans();
}
$CBB::current = 0;
$trans = $CBB::TRANS{$CBB::KEYS[$CBB::current]};
if ( $trans ) {
return "$CBB::KEYS[$CBB::current]\t$trans";
} else {
return "none";
}
}
# returns the entire transaction list in one big chunk.
sub all_trans {
# in: date
# out: result
my($date_fmt) = @_;
my($key, $nicecat, $cutcom, $cutdesc, $cutcheck, $nicedate, $checklen);
my($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total);
my($day, $month, $year);
$| = 0; # turn off buffer flushing
if ($CBB::calced == 0) {
&calc_trans();
}
if ($CBB::sorted_keys == 0) {
&sort_keys();
}
foreach $key (@CBB::KEYS) {
# print ("$key\t$CBB::TRANS{$key}\n");
($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total) =
split(/\t/, $CBB::TRANS{$key});
if ( length($date) == 6 ) {
# for backwards compatibility ... shouldn't be needed now.
($year, $month, $day) = $date =~ /(\d\d)(\d\d)(\d\d)/;
$year = "19" . $year;
} else {
($year, $month, $day) = $date =~ /(\d\d\d\d)(\d\d)(\d\d)/
}
$checklen = length($check);
if ( $checklen > 5 ) {
$cutcheck = substr($check, $checklen - 5, 5);
} else {
$cutcheck = $check;
}
if ( $date_fmt == 1 ) {
$nicedate = "$month/$day/" . substr($year, 2, 2);
} else {
$nicedate = "$day.$month." . substr($year, 2, 2);
}
$cutdesc = substr($desc, 0, 15);
$cutcom = substr($com, 0, 15);
if ( $cat =~ m/\|/ ) {
$nicecat = "-Splits-";
} else {
$nicecat = $cat;
}
$nicecat = substr($nicecat, 0, 9);
printf("%5s %-8s %-15s %9.2f %9.2f %-1s %10.2f %14s\n",
$cutcheck, $nicedate, $cutdesc, $debit, $credit, $cleared,
$total, $key);
printf("%5s %-8s %-15s %-9s %39s\n", "", "", $cutcom, $nicecat,
$key);
}
$| = 1; # turn buffer flushing back on
return "none";
}
# returns part of the transaction list in one big chunk. (since a date)
sub part_trans {
# in: date
# out: result
my($sdate_fmt) = @_;
my($left, $middle, $right) = (0, 0, $#CBB::KEYS);
my($date_fmt, $sdate);
my($key, $nicecat, $cutcom, $cutdesc, $cutcheck, $nicedate, $checklen);
my($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total);
my($day, $month, $year);
# two arguments: data_format and start date
($date_fmt, $sdate) = split(" ", $sdate_fmt, 2);
$| = 0; # turn off buffer flushing
if ($CBB::calced == 0) {
&calc_trans();
}
if ($CBB::sorted_keys == 0) {
&sort_keys();
}
# look for first key past starting with sdate (borrowed from find_trans)
$sdate = "$sdate-".&pad(0);
while ( $left <= $right ) {
$middle = int( ($left + $right) / 2 );
if ( $CBB::KEYS[$middle] lt $sdate ) {
$left = $middle + 1;
} elsif ( $CBB::KEYS[$middle] gt $sdate ) {
$right = $middle - 1;
} else {
# we found it, force an exit of the while loop
$left = $right + 1;
}
}
if ($CBB::KEYS[$middle] != $sdate) {
# we found the first past sdate
$middle = $left;
}
for (; $middle <= $#CBB::KEYS ; ++$middle) {
$key=$CBB::KEYS[$middle];
# print ("$key\t$CBB::TRANS{$key}\n");
($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total) =
split(/\t/, $CBB::TRANS{$key});
if ( length($date) == 6 ) {
# for backwards compatibility ... shouldn't be needed now.
($year, $month, $day) = $date =~ /(\d\d)(\d\d)(\d\d)/;
$year = "19" . $year;
} else {
($year, $month, $day) = $date =~ /(\d\d\d\d)(\d\d)(\d\d)/
}
$checklen = length($check);
if ( $checklen > 5 ) {
$cutcheck = substr($check, $checklen - 5, 5);
} else {
$cutcheck = $check;
}
if ( $date_fmt == 1 ) {
$nicedate = "$month/$day/" . substr($year, 2, 2);
} else {
$nicedate = "$day.$month." . substr($year, 2, 2);
}
$cutdesc = substr($desc, 0, 15);
$cutcom = substr($com, 0, 15);
if ( $cat =~ m/\|/ ) {
$nicecat = "-Splits-";
} else {
$nicecat = $cat;
}
$nicecat = substr($nicecat, 0, 9);
printf("%5s %-8s %-15s %9.2f %9.2f %-1s %10.2f %14s\n",
$cutcheck, $nicedate, $cutdesc, $debit, $credit, $cleared,
$total, $key);
printf("%5s %-8s %-15s %-9s %39s\n", "", "", $cutcom, $nicecat,
$key);
}
$| = 1; # turn buffer flushing back on
return "none";
}
# return the first uncleared transaction
sub first_uncleared_trans {
my($trans);
my($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $junk);
if ($CBB::sorted_keys == 0) {
&sort_keys();
}
if ($CBB::calced == 0) {
&calc_trans();
}
$CBB::current = 0;
$trans = $CBB::TRANS{$CBB::KEYS[$CBB::current]};
($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $junk) =
split(/\t/, $trans);
while ( $cleared eq "x" ) {
++$CBB::current;
$trans = $CBB::TRANS{$CBB::KEYS[$CBB::current]};
($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $junk) =
split(/\t/, $trans);
}
if ( $trans ) {
return "$CBB::KEYS[$CBB::current]\t$trans";
} else {
return "none";
}
}
# return the next uncleared transaction
sub next_uncleared_trans {
my($trans);
my($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $junk);
if ($CBB::sorted_keys == 0) {
&sort_keys();
}
if ($CBB::calced == 0) {
&calc_trans();
}
++$CBB::current;
$trans = $CBB::TRANS{$CBB::KEYS[$CBB::current]};
($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $junk) =
split(/\t/, $trans);
while ( $cleared eq "x" ) {
++$CBB::current;
$trans = $CBB::TRANS{$CBB::KEYS[$CBB::current]};
($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $junk) =
split(/\t/, $trans);
}
if ( $trans ) {
return "$CBB::KEYS[$CBB::current]\t$trans";
} else {
return "none";
}
}
# select transaction -- primes a transaction for future clearing
sub select_trans {
# in: key
# out: keyed_transaction
my($key) = @_;
my($trans);
my($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total);
$CBB::sorted_keys = 0;
$CBB::calced = 0;
$trans = $CBB::TRANS{$key};
($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total) =
split(/\t/, $trans);
$cleared = "*";
$CBB::TRANS{$key} =
"$date\t$check\t$desc\t$debit\t$credit\t$cat\t$com\t$cleared\t$total";
print DEBUG "selected: $key to be cleared\n" if $CBB::debug;
return "$key\t$CBB::TRANS{$key}";
}
# select transaction -- primes a transaction for future clearing
sub unselect_trans {
# in: key
# out: keyed_transaction
my($key) = @_;
my($trans);
my($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total);
$CBB::sorted_keys = 0;
$CBB::calced = 0;
$trans = $CBB::TRANS{$key};
($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total) =
split(/\t/, $trans);
$cleared = "";
$CBB::TRANS{$key} =
"$date\t$check\t$desc\t$debit\t$credit\t$cat\t$com\t$cleared\t$total";
print DEBUG "unselected: $key will not be cleared\n" if $CBB::debug;
return "$key\t$CBB::TRANS{$key}";
}
# clear all selected transactions
sub clear_trans {
my($key, $trans);
my($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total);
if ($CBB::calced == 0) {
&calc_trans();
}
if ($CBB::sorted_keys == 0) {
&sort_keys();
}
foreach $key (@CBB::KEYS) {
$trans = $CBB::TRANS{$key};
($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total) =
split(/\t/, $trans);
if ( $cleared eq "*" ) {
$cleared = "x";
$CBB::TRANS{$key} =
"$date\t$check\t$desc\t$debit\t$credit\t$cat\t$com\t$cleared\t$total";
}
}
}
# return the cleared balance (this should be the last statement ending bal)
sub get_cleared_bal {
return sprintf("%.2f", $CBB::BALS{"Xamount"});
}
# initialize the transactions data structure
sub init_trans {
# out: result
$CBB::sorted_keys = 0;
$CBB::calced = 0;
@CBB::KEYS = ();
return "ok";
}
# make a new account
sub make_acct {
# in: acct-name acct-desc acct-type
# out: result
my($name, $desc) = split(/ /, $_[0], 2);
my($pos, $short_name);
print DEBUG "Make account $name - $desc\n" if $CBB::debug;
# print "Make account $name - $desc\n";
print DEBUG "Making cbb account\n" if $CBB::debug;
open(SAVE, ">$name.cbb.new");
close(SAVE);
unlink("$name.cbb.bak");
rename("$name.cbb", "$name.cbb.bak");
rename("$name.cbb.new", "$name.cbb");
$CBB::current_file = "$name.cbb";
%CBB::TRANS = ();
# Assume we have category already open ... :| ??? :(
# strip leading path from $name
&insert_cat("[".&file_basename($name)."]\t$desc\t");
# save the categories file before it gets toasted
&save_cats(&file_dirname($name) . "/categories");
return "ok";
}
# determine the file type and call the correct load routine
sub load_trans {
# in: file base
# out: result
my($file) = @_;
my($ext) = &file_extension($file);
# print "$ext\n";
# print &file_root($file) . "\n";
print DEBUG "file extension is: $ext\n" if $CBB::debug;
if ($CBB::cache) {
no strict 'vars'; # necessary for this special hack
no strict 'refs';
# save current data to cache
my($hname) = "ACC_" . &file_basename($CBB::current_file);
print DEBUG "$hname $CBB::current_file\n" if $CBB::debug;
%$hname = %CBB::TRANS;
# test if new table already in cache
$hname = "ACC_" . &file_basename($file);
print DEBUG "$hname\n" if $CBB::debug;
if (scalar (%$hname) ) {
print DEBUG "$hname defined , load from cache\n" if $CBB::debug;
$CBB::sorted_keys = 0;
$CBB::calced = 0;
%CBB::TRANS = %$hname; # take values from the cache
&calc_trans();
$CBB::current_file = $file;
return "ok";
}
}
return &load_cbb_trans($file);
}
# load the data from a cbb file
sub load_cbb_trans {
# in: file name (including .cbb extension)
# out: result
my($file) = @_;
my($file_version) = "";
my($junk);
$CBB::sorted_keys = 0;
$CBB::calced = 0;
print DEBUG "Loading the cbb format file: $file\n" if $CBB::debug;
if ( $CBB::decrypt ne "" ) {
open(LOAD, "$CBB::decrypt < $file|") || return "error";
} else {
open(LOAD, "<$file") || return "error";
}
%CBB::TRANS = (); # clear out any transactions from the previous file
while ( <LOAD> ) {
if ( m/^#/ ) {
# toss the comment (but first check for any goodies.)
if ( m/version/i ) {
($junk, $junk, $junk, $file_version) = split;
print DEBUG "Data file version = $file_version\n" if $CBB::debug;
}
} else {
if ( $file_version eq "") {
print DEBUG "no data file version, file encrypted ?" if $CBB::debug;
close(LOAD);
return "error";
}
chop;
if ( ! m/\t/ ) {
s/:/\t/g;
$_ = &fix_splits($_);
}
&create_trans($_);
}
}
close(LOAD);
&calc_trans();
$CBB::current_file = $file;
return "ok";
}
sub fix_splits {
# in: transaction with old two field per record splits
# out: transaction with new three field per record splits
my($line) = @_;
my($date, $check, $desc, $debit, $credit, $cat, $com, $cleared, $total) =
split(/\t/, $line);
my(@cats, $i, $max, $newcat);
if ( $cat =~ m/\|/ ) {
@cats = split(/\|/, $cat);
$i = 0;
$max = ($#cats - 1) / 2;
$newcat = "|";
while ( $i < $max ) {
$newcat .= $cats[$i * 2 + 1] . "||" .
$cats[$i * 2 + 2] . "|";
$i++;
}
} else {
$newcat = $cat;
}
return "$date\t$check\t$desc\t$debit\t$credit\t$newcat\t$com\t$cleared\t$total";
}
# load the data from a dbm file
sub load_dbm_trans {
# in: file base name
# out: result
my($file) = @_;
print DEBUG "Loading the dbm format file: $file\n" if $CBB::debug;
if ( -e "$file" ) {
$CBB::current_file = $file;
$CBB::sorted_keys = 0;
$CBB::calced = 0;
dbmclose(%CBB::TRANS);
dbmopen(%CBB::TRANS, &file_root($file), 0666) || return "error";
# test to see if this file is <tab> delimited
&sort_keys();
# never ever call calc_trans() at this point (or call something that
# calls it
if (defined($CBB::TRANS{$CBB::KEYS[0]}) &&
!($CBB::TRANS{$CBB::KEYS[0]} =~ m/\t/) ) {
print DEBUG "'$CBB::TRANS{$CBB::KEYS[0]}' = old version of CBB dbm file\n"
if $CBB::debug;
return "error - old version of CBB dbm file";
} else {
print DEBUG "valid txn: '$CBB::TRANS{$CBB::KEYS[0]}'\n"
if $CBB::debug;
}
return "ok";
} else {
return "error";
}
}
# save all the precious data to a file
sub save_trans {
# in: file name (including .cbb extension)
# out: result
my($file) = @_;
my($auto_save_file, $key);
my(@trans);
print DEBUG "Saving the cbb format file: $file\n" if $CBB::debug;
if ($CBB::calced == 0) {
&calc_trans();
}
if ($CBB::sorted_keys == 0) {
&sort_keys();
}
if ( $CBB::encrypt ne "" ) {
open(SAVE, "|$CBB::encrypt > $file.new") || return "error";
} else {
open(SAVE, ">$file.new") || return "error";
}
# Print some header stuff
print (SAVE "# CBB Data File -- $file\n");
print (SAVE "#\n");
print (SAVE "# CBB Version = $CBB::version_num\n");
printf (SAVE "# Current Balance = %.2f\n", $CBB::BALS{Current});
printf (SAVE "# Ending Balance = %.2f\n", $CBB::BALS{Amount});
print (SAVE "# Transaction Count = $CBB::BALS{Count}\n");
printf (SAVE "# Cleared Balance = %.2f\n", $CBB::BALS{Xamount});
print (SAVE "# Cleared Txn Count = $CBB::BALS{Xcount}\n");
print (SAVE "# Saved on (US Date Fmt) " . &nice_date("1") . " ");
print (SAVE "by $CBB::user_name\n");
print (SAVE "#\n");
print (SAVE "# date check desc debit credit cat com cleared\n");
print (SAVE "# ---------------------------------------------------\n");
foreach $key (@CBB::KEYS) {
# strip off last total
@trans = split(/\t/, $CBB::TRANS{$key});
print SAVE join ("\t", @trans[0..7]) . "\n";
}
close(SAVE);
unlink("$file.bak");
rename("$file", "$file.bak");
rename("$file.new", "$file");
$auto_save_file = &file_dirname($file) . "#" . &file_basename($file) . "#";
print DEBUG "auto_save_file = $auto_save_file\n" if $CBB::debug;
if ( -e $auto_save_file ) {
unlink("$auto_save_file");
unlink("$auto_save_file.bak");
}
return "ok";
}
1;
# ----------------------------------------------------------------------------
# $Log: engine.pl,v $
# Revision 2.20 1998/08/14 14:30:11 curt
# Patches to the graphs/graphbal script to avoid divide by zero in certain
# circumstances.
#
# Revision 2.19 1998/08/14 14:28:35 curt
# Added desc-pie graph.
# Added option to eliminate splash screen.
# Other misc. tweaks and bug fixes.
#
# Revision 2.18 1997/05/06 02:35:14 curt
# Removed an extranious my()
#
# Revision 2.17 1997/05/06 01:00:27 curt
# Added patches contributed by Martin Schenk <schenkm@ping.at>
# - Default to umask of 066 so .CBB files get created rw by owner only
# - Added support for pgp encrypting data files
# - Added support for displaying only recent parts of files (avoids
# waiting to load in lots of old txns you don't currently need.)
# - Added a feature to "cache" whole accounts in the perl engine so
# that switching between accounts can be faster.
# - The above options can be turned on/off via the preferrences menu.
#
# Revision 2.16 1997/04/12 01:15:24 curt
# Display current balance rather than ending balance in the account list
# box. This makes a difference if you like to insert future transactions.
#
# Revision 2.15 1997/04/11 20:24:01 curt
# Automatically insert new transactions into the memorized list.
#
# Revision 2.14 1997/04/04 18:41:35 curt
# Fixed a small bug in editing transfer transactions.
#
# Revision 2.13 1997/03/04 03:23:00 curt
# Fixed bug which caused a transfer transaction to not show up in the list
# box even though it had been correctly inserted.
#
# Revision 2.12 1997/01/18 03:28:42 curt
# Added "use strict" pragma to enforce good scoping habits.
#
# Revision 2.11 1997/01/10 22:03:30 curt
# Transfer fixups and a few other misc. fixes contributed by
# Lionel Mallet <Lionel.Mallet@sophia.inria.fr>
#
# Revision 2.10 1997/01/02 04:38:32 curt
# Changes over the 1996 holidays:
# - Converted listbox to text widget. This allows us to do nice
# things with alternating background colors, highliting, red
# negative numbers, etc.
# - Negative transactions are now drawn in red.
# - Added a Goto <Today> option.
# - <Home> & <End> were double bound. Now, listbox can be traversed with
# <Meta-Home> and <Meta-End>
#
# Revision 2.9 1996/12/17 20:15:43 curt
# Version incremented to 0.70.
# No longer save running total in .cbb files.
# Miscellaneous tweaks.
#
# Revision 2.8 1996/12/17 14:53:54 curt
# Updated copyright date.
#
# Revision 2.7 1996/12/11 18:33:32 curt
# Ran a spell checker.
#
# Revision 2.6 1996/10/02 19:37:19 curt
# Replaced instances of hardcoded century (19) with a variable. We need to
# know the current century in cases where it is not provided and it is
# assumed to be the current century. Someday I need to figure out how
# to determine the current century, but I have a couple of years to do it. :-)
#
# I still need to fix conf-reports and reports.pl
#
# Revision 2.5 1996/09/26 19:48:44 curt
# Fixed some problems with the newly revamped tab completion code.
#
# Revision 2.4 1996/07/30 14:35:33 curt
# Fixed a typo introduced in previous (field width change).
#
# Revision 2.3 1996/07/24 20:17:14 curt
# Added Arlindo M. L. Oliveira's "total" field with fix for handling higher
# numbers.
#
# Revision 2.2 1996/07/13 02:57:41 curt
# Version 0.65
# Packing Changes
# Documentation changes
# Changes to handle a value in both debit and credit fields.
#
# Revision 2.1 1996/02/27 05:35:40 curt
# Just stumbling around a bit with cvs ... :-(
#
# Revision 2.0 1996/02/27 04:41:53 curt
# Initial 2.0 revision. (See "Log" files for old history.)