head 1.47;
access;
symbols
AS_CUI_0_6_5:1.47
AS_CUI_0_6_4:1.36
AS_CUI_0_6_3:1.30
AS_CUI_0_6_2:1.26
AS_CUI_0_6_1:1.21
AS_CUI_0_6_0:1.19
AS_CUI_0_5_6:1.17
AS_CUI_0_5_5:1.11
AS_CUI_0_5_4:1.8
AS_CUI_0_5_3:1.6
AS_CUI_0_5_2:1.4
AS_CUI_0_5_1:1.2;
locks; strict;
comment @# @;
1.47
date 2003.02.27.15.57.40; author thl; state Exp;
branches;
next 1.46;
1.46
date 2003.02.27.15.56.14; author thl; state Exp;
branches;
next 1.45;
1.45
date 2003.02.27.15.49.17; author thl; state Exp;
branches;
next 1.44;
1.44
date 2003.02.27.15.38.37; author thl; state Exp;
branches;
next 1.43;
1.43
date 2003.02.27.15.36.55; author thl; state Exp;
branches;
next 1.42;
1.42
date 2003.02.27.15.27.38; author thl; state Exp;
branches;
next 1.41;
1.41
date 2003.02.27.15.26.45; author thl; state Exp;
branches;
next 1.40;
1.40
date 2003.02.27.13.27.35; author thl; state Exp;
branches;
next 1.39;
1.39
date 2003.02.27.13.23.44; author thl; state Exp;
branches;
next 1.38;
1.38
date 2003.02.27.13.04.48; author thl; state Exp;
branches;
next 1.37;
1.37
date 2003.02.26.09.21.46; author thl; state Exp;
branches;
next 1.36;
1.36
date 2003.02.25.15.27.16; author thl; state Exp;
branches;
next 1.35;
1.35
date 2003.02.25.15.26.49; author thl; state Exp;
branches;
next 1.34;
1.34
date 2003.02.25.12.39.19; author thl; state Exp;
branches;
next 1.33;
1.33
date 2003.02.25.12.37.06; author thl; state Exp;
branches;
next 1.32;
1.32
date 2003.02.25.08.32.23; author thl; state Exp;
branches;
next 1.31;
1.31
date 2003.02.25.08.07.01; author thl; state Exp;
branches;
next 1.30;
1.30
date 2003.02.25.07.30.00; author thl; state Exp;
branches;
next 1.29;
1.29
date 2003.02.24.15.49.53; author thl; state Exp;
branches;
next 1.28;
1.28
date 2003.02.24.15.46.50; author thl; state Exp;
branches;
next 1.27;
1.27
date 2003.02.20.14.32.31; author rse; state Exp;
branches;
next 1.26;
1.26
date 2003.02.20.13.58.35; author thl; state Exp;
branches;
next 1.25;
1.25
date 2003.02.20.13.53.48; author thl; state Exp;
branches;
next 1.24;
1.24
date 2003.02.20.13.38.00; author thl; state Exp;
branches;
next 1.23;
1.23
date 2003.02.20.13.28.06; author thl; state Exp;
branches;
next 1.22;
1.22
date 2003.02.20.09.52.15; author thl; state Exp;
branches;
next 1.21;
1.21
date 2003.02.13.16.21.44; author thl; state Exp;
branches;
next 1.20;
1.20
date 2003.02.13.12.55.41; author thl; state Exp;
branches;
next 1.19;
1.19
date 2003.02.03.13.40.12; author thl; state Exp;
branches;
next 1.18;
1.18
date 2003.02.03.13.39.25; author thl; state Exp;
branches;
next 1.17;
1.17
date 2002.12.19.15.17.07; author thl; state Exp;
branches;
next 1.16;
1.16
date 2002.12.19.15.16.53; author thl; state Exp;
branches;
next 1.15;
1.15
date 2002.12.19.14.04.56; author thl; state Exp;
branches;
next 1.14;
1.14
date 2002.12.19.12.32.03; author thl; state Exp;
branches;
next 1.13;
1.13
date 2002.12.19.10.40.35; author thl; state Exp;
branches;
next 1.12;
1.12
date 2002.12.19.09.51.48; author thl; state Exp;
branches;
next 1.11;
1.11
date 2002.12.18.16.29.56; author rse; state Exp;
branches;
next 1.10;
1.10
date 2002.12.18.15.58.27; author rse; state Exp;
branches;
next 1.9;
1.9
date 2002.12.18.15.50.16; author rse; state Exp;
branches;
next 1.8;
1.8
date 2002.12.18.15.15.06; author thl; state Exp;
branches;
next 1.7;
1.7
date 2002.12.18.15.14.30; author thl; state Exp;
branches;
next 1.6;
1.6
date 2002.12.18.14.34.48; author thl; state Exp;
branches;
next 1.5;
1.5
date 2002.12.18.14.34.26; author thl; state Exp;
branches;
next 1.4;
1.4
date 2002.12.18.12.46.12; author thl; state Exp;
branches;
next 1.3;
1.3
date 2002.12.18.12.43.41; author thl; state Exp;
branches;
next 1.2;
1.2
date 2002.12.18.11.19.53; author rse; state Exp;
branches;
next 1.1;
1.1
date 2002.12.18.11.18.20; author rse; state Exp;
branches;
next ;
desc
@@
1.47
log
@flush pending changes
@
text
@#!perl
##
## AS -- Accounting System
## Copyright (c) 2002 Cable & Wireless Deutschland
## Copyright (c) 2002 Ralf S. Engelschall
##
## This file is part of AS, an accounting system which can be
## found at http://as.is.eu.cw.com/
##
## 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.0 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
## USA, or contact The OSSP Project .
##
## as-cui: Unix Command-Line Client
##
require 5.003;
use strict; # OpenPKG >= perl-5.6.1
use IO; # OpenPKG >= perl-5.6.1
use POSIX; # OpenPKG >= perl-5.8.0, possibly sooner
use Getopt::Long; # OpenPKG >= perl-5.6.1
use Time::Local; # OpenPKG >= perl-5.6.1
use Text::Balanced; # OpenPKG >= perl-parse-20021016
use Data::UUID; # OpenPKG >= perl-crypto-20021030
use String::CRC32; # OpenPKG >= perl-crypto-20021030
require "as-cui-matrix.pm";
import as_cui_matrix;
# program version
my $progname = "as-cui";
my $progvers = "0.6.5";
# data format version
my $datavers = $progvers;
$datavers =~ s/\.[0-9]+$//;
# options
my $opt_complete = '';
my $opt_define = {};
my $opt_verbose = 0;
my $opt_setup = 0;
my $opt_download = 0;
my $opt_update = [];
my $opt_commit = [];
my $opt_help = 0;
my $opt_version = 0;
# preset options
my @@localruntime = localtime(time());
# internal global structures
my $runtimecfg = {};
my $lockinfo = {};
my $accounts = [];
my $events = {};
my $matrixdata = {};
$matrixdata->{"progname"} = $progname;
$matrixdata->{"progvers"} = $progvers;
# exception handling support
$SIG{__DIE__} = sub {
my ($err) = @@_;
$err =~ s|\s+at\s+.*||s if (not $opt_verbose);
my $txt = "$err ". ($! ? "($!)" : "");
print STDERR "$progname:ERROR: $txt\n";
&unlinklock();
exit(1);
};
# verbose message printing
sub verbose {
my ($msg) = @@_;
print STDERR "$msg\n" if ($opt_verbose);
}
# command line parsing
Getopt::Long::Configure("bundling");
my $result = GetOptions(
'C|complete=s' => \$opt_complete,
'D|define=s' => $opt_define, #FIXME -Dfoo=bar works but --define foo=bar does not
'v|verbose' => \$opt_verbose,
's|setup' => \$opt_setup,
'd|download' => \$opt_download,
'u|update:s' => $opt_update,
'c|commit:s' => $opt_commit,
'h|help' => \$opt_help,
'V|version' => \$opt_version
) || die "option parsing failed";
# post-process parsed options
if (($#{$opt_update} >= 0) && ($opt_update->[0] ne "")) {
@@{$opt_update} = split(/,/,join(',',@@{$opt_update}));
}
if (($#{$opt_commit} >= 0) && ($opt_commit->[0] ne "")) {
@@{$opt_commit} = split(/,/,join(',',@@{$opt_commit}));
}
# read and set the runtime configuration options
$runtimecfg = &readrc();
foreach my $var (keys %{$opt_define}) {
$runtimecfg->{$var} = $opt_define->{$var};
}
# fallbacks
if (not defined($runtimecfg->{"user"})) {
$runtimecfg->{"user"} = $ENV{LOGNAME};
}
if (not defined($runtimecfg->{"date"})) {
$runtimecfg->{"date"} = &day(@@localruntime);
}
if (not defined($runtimecfg->{"time"})) {
$runtimecfg->{"time"} = &now(@@localruntime);
}
if (not defined($runtimecfg->{"hist"})) {
$runtimecfg->{"hist"} = 99;
}
# sanity checks on runtime configuration
if (not $runtimecfg->{"user"} =~ m/^[a-z0-9]+$/) {
die "sanity check of runtime configuration \"user\" failed for \"$runtimecfg->{user}\".\n";
}
if (not &isvalidyyyymmdd($runtimecfg->{"date"})) {
die "sanity check of runtime configuration \"date\" failed for \"$runtimecfg->{date}\".\n";
}
if (not &isvalidhhmm($runtimecfg->{"time"})) {
die "sanity check of runtime configuration \"time\" failed for \"$runtimecfg->{time}\".\n";
}
# read in the accounts list
$accounts = &readaccounts();
# short-circuit dispatch
if ($opt_complete ne "") {
&do_complete($ARGV[0]);
exit(0);
}
# locking
$lockinfo = &readlock();
if (defined $lockinfo) {
my $b = 0;
printf STDERR "$progname:ERROR: LOCK DETECTED! Additional information about the lock";
foreach my $k (sort keys %{$lockinfo}) {
printf STDERR ": " unless ($b);
printf STDERR ", " if ($b);
printf STDERR "%s=%s", "e($k), "e($lockinfo->{$k});
$b = 1;
};
printf STDERR " is not available." unless ($b);
printf STDERR "\n";
exit(1);
}
$lockinfo = {
"date" => &day(@@localruntime),
"time" => &now(@@localruntime),
"user" => $runtimecfg->{"user"},
"nodename" => [ POSIX::uname() ]->[1],
"pid" => $$,
"uid" => $<,
"pwname" => [ getpwuid($<) ]->[$1],
"text" => $progname . " (" . $progvers . ")"
};
&writelock();
# read in the events
$events = &readevents();
# dispatch into sub-routines
if ($opt_setup) {
&do_setup;
}
elsif ($opt_download) {
&do_unimplemented;
}
elsif ($#{$opt_update} >= 0) {
&do_unimplemented;
}
elsif ($#{$opt_commit} >= 0) {
&do_unimplemented;
}
elsif ($opt_help) {
print STDOUT "Usage: $progname [options] [arguments]\n" .
"Available options:\n" .
" -D,--define opt-name=opt-value\n" .
" -v,--verbose be chatty\n" .
" -s,--setup setup\n" .
" -d,--download download writeable accounts list from server\n" .
" -u,--update [uuid] update local database with information from server\n" .
" -c,--commit [uuid] commit pending changes to server\n" .
" -h,--help print out this usage page\n" .
" -V,--version print out program version\n";
}
elsif ($opt_version) {
print STDOUT "$progname $progvers\n";
}
elsif ($#ARGV == -1) {
# in CUI mode we do not want STDERR to clutter our screen.
($opt_verbose && open STDERR, ">>2.log") || (open STDERR, ">/dev/null");
&as_cui_matrix::ascuinew($matrixdata, $opt_verbose);
my $rc;
do {
&events2matrix();
$rc = &as_cui_matrix::ascuido();
&matrix2events();
&writeevents() if ($rc =~ m/^(save|exit)$/);
} while (not $rc =~ m/^(exit|quit)$/);
}
else {
my $timespec = shift @@ARGV;
if (not defined $timespec) {
die "CLI timespec missing";
}
my $account = shift @@ARGV;
if (not defined $account) {
die "CLI account missing";
}
if (not &isvalidaccount($account)) {
die "CLI invalid account \"$account\"";
}
if ($account =~ m|^(\.[-a-zA-Z0-9]+)+$|) {
$account = &dot2slash($account);
}
my $remark = '';
for my $i (@@ARGV) {
$remark .= $i . " ";
}
$remark =~ s/ $//;
&do_newevent($timespec, $account, $remark);
&writeevents();
}
&unlinklock();
exit(0);
#
# Command Line Argument Completion Utility
# (see as.bash for context)
#
# testsuite.sh
# echo 000; perl as.pl --complete time -- 22:33
# echo 001; perl as.pl --complete time -- =22:33
# echo 010; perl as.pl --complete time -- -11:22 #defeat leading dash
# echo 011; perl as.pl --complete time -- -11:22=22:33 #defeat leading dash
# echo 100; perl as.pl --complete time -- 00:11-
# echo 101; perl as.pl --complete time -- 00:11-=22:33 #defeat spaces
# echo 110; perl as.pl --complete time -- 00:11-11:22
# echo 111; perl as.pl --complete time -- 00:11-11:22=22:33
#
sub do_complete {
my ($arg) = @@_;
if ($opt_complete eq 'account') {
if ($arg eq '') {
print "/\n" . ".\n";
return;
}
else {
my $pattern = quotemeta($arg);
foreach my $ac (@@{$accounts}) {
if ($ac->{name} =~ m|^$pattern|) {
print "$ac->{name}\n";
}
}
return;
}
}
else {
die "invalid completion type \"$opt_complete\" (has to be 'account')";
}
}
sub isvalidhhmm {
my ($input) = @@_;
if ($input =~ m/([01][0-9]|2[0-4]):[0-5][0-9](:[0-5][0-9])?$/) {
return 1;
}
return 0;
}
sub isvalidyyyymmdd {
my ($input) = @@_;
if ($input =~ m/^[2-9][0-9]{3}-([0][1-9]|[1][0-2])-([0][1-9]|[12][0-9]|[3][01])$/) {
return 1;
}
return 0;
}
sub isvaliduser {
my ($user, $status) = (@@_);
return 1 if ($user =~ m|^[a-zA-Z][a-zA-Z0-9]*$|);
$status->{user} = "user" if (defined $status);
return 0;
}
sub isvaliduuid {
my ($uuid, $status) = (@@_);
return 1 if ($uuid =~ m|^[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}$|);
$status->{uuid} = "uuid" if (defined $status);
return 0;
}
sub isvalidcrc32 {
my ($crc32, $status) = (@@_);
return 1 if ($crc32 =~ m|^[0-9a-fA-F]{1,8}$|);
$status->{crc32} = "crc32" if (defined $status);
return 0;
}
sub isvalidrevision {
my ($revision, $status) = (@@_);
return 1 if ($revision =~ m|^[0-9]{1,5}$|);
$status->{revision} = "revision" if (defined $status);
return 0;
}
sub isvaliddate {
my ($date, $status) = (@@_);
return 1 if (&isvalidyyyymmdd($date));
$status->{date} = "date" if (defined $status);
return 0;
}
sub isvalidbegin {
my ($begin, $status) = (@@_);
return 1 if (&isvalidhhmm($begin));
$status->{begin} = "begin" if (defined $status);
return 0;
}
sub isvalidend {
my ($end, $status) = (@@_);
return 1 if (&isvalidhhmm($end));
$status->{end} = "end" if (defined $status);
return 0;
}
sub isvalidamount {
my ($amount, $status) = (@@_);
return 1 if (&isvalidhhmm($amount));
$status->{amount} = "amount" if (defined $status);
return 0;
}
sub isvalidaccount {
my ($account, $status) = (@@_);
if ($account =~ m|^\.|) {
$account = &dot2slash($account);
}
foreach my $element (@@{$accounts}) {
return 1 if ($element->{type} eq "R" and $account =~ m|^$element->{name}$|);
}
$status->{account} = "account" if (defined $status);
return 0;
}
sub isvalidremark {
my ($amount, $status) = (@@_);
return 1;
}
#
# make any input a valid date or wipe it out if no conversion possible
# dot is a valid input and means today
# today is taken from reality unless overridden by using a second optional parameter
#
sub anydate2yyyymmdd {
my ($input, @@localtime) = (@@_);
my $output;
if (&isvalidyyyymmdd($input)) {
$output = $input;
}
elsif ($input =~ m|^\.$|) {
$output = &day(@@localtime);
}
else {
$output = "";
}
return $output;
}
#
# make any input a valid time or wipe it out if no conversion possible
# dot is a valid input and means now
# now is taken from reality unless overridden by using a second optional parameter
#
sub anytime2hhmm {
my ($input, @@localtime) = (@@_);
my $output = "";
if (&isvalidhhmm($input)) {
$output = $input;
}
elsif ($input =~ m|^\.$|) {
$output = &now(@@localtime);
}
elsif ($input =~ m/^([1-9])?(:([0-9]|[0-5][0-9])?)?$/) {
# short
$output = sprintf("%02d:%02d", $1, $3);
}
elsif ($input =~ m/^([0-9]|[1][0-9]|2[0-4])?:([0-9]|[0-5][0-9])?$/) {
# short-hour
$output = sprintf("%02d:%02d", $1, $2);
}
elsif ($input =~ m/^([0-9]*\.[0-9]+|[0-9]+\.)$/) {
# frac-dec
my $f = "0".$1;
$f = int($f * 60 + 0.5);
my $h = $f / 60;
my $m = $f % 60;
$output = sprintf("%02d:%02d", $h, $m);
}
elsif ($input =~ m/^([0-9]*\/[1-9][0-9]*)$/) {
# frac-std
my $f = $1;
$f =~ s|^/|1/|s;
eval "\$f = int(($f) * 60 + 0.5);";
my $h = $f / 60;
my $m = $f % 60;
$output = sprintf("%02d:%02d", $h, $m);
}
elsif ($input =~ m/^0([0-9])$/) {
# force-min
$output = "00:0$1";
}
elsif ($input =~ m/^([1-9][0-9]+)$/) {
# short-min
my $h = int($1 / 60);
my $m = int($1 % 60);
$output = sprintf("%02d:%02d", $h, $m);
}
else {
$output = "";
}
return $output;
}
#
# Read lock file
#
sub readlock {
my $rv = undef;
my $file = "lock";
my $path;
my $io;
# in general, no access to the file is no reason to die; just return no data
($path, $io) = &openfile($file, "x");
return $rv if (not $io);
($path, $io) = &openfile($file, "r");
$rv = &file2hashandclose($path, $io);
return $rv;
}
#
# Write lock file
#
sub writelock {
my $file = "lock";
my $path;
my $io;
($path, $io) = &openfile($file, "w");
&hash2fileandclose($io, $lockinfo);
}
#
# Unlink lock file
#
sub unlinklock {
my $file = "lock";
my $path;
my $io;
# in general, no access to the file is no reason to die; just return
($path, $io) = &openfile($file, "x");
return if (not $io);
close($io);
unlink $path;
}
#
# Read rc file
#
sub readrc {
my $rv = undef;
my $file = "rc";
my $path;
my $io;
# in setup mode, no access to the file is no reason to die; just return no data
if ($opt_setup) {
($path, $io) = &openfile($file, "x");
return $rv if (not $io);
}
($path, $io) = &openfile($file, "r");
$rv = &file2hashandclose($path, $io);
return $rv;
}
#
# Write rc file
#
sub writerc {
my $file = "rc";
my $path;
my $io;
($path, $io) = &openfile($file, "w");
&hash2fileandclose($io, $runtimecfg);
}
#
# Read content of a file into a hash and close file
#
sub file2hashandclose {
my ($path, $io) = @@_;
my $rv = {};
my $line = 1;
my $ln;
while (defined($ln = <$io>)) {
$line++;
$ln =~ s|^\s*||s; #strip off leading spaces
$ln =~ s|\s*(#.*)?$||s; #strip off trailing spaces and comments
next if ( $ln =~ m|^\s*$| ); #ignore empty lines
my $remainder = $ln;
my $q = '"';
my $var;
if ($remainder =~ m|^$q|) {
($var, $remainder) = Text::Balanced::extract_delimited($remainder, $q);
$var =~ s|^$q(.*)$q$|$1|;
} else {
$_ = $remainder;
($var, $remainder) = m|^([^\s]*)(.*)$|;
}
$var =~ s|\\(.)|$1|g;
$remainder =~ s|^\s*(.*)$|$1|;
my $val;
if ($remainder =~ m|^$q|) {
($val, $remainder) = Text::Balanced::extract_delimited($remainder, $q);
$val =~ s|^$q(.*)$q$|$1|;
} else {
$_ = $remainder;
($val, $remainder) = m|^([^\s]*)(.*)$|;
}
$val =~ s|\\(.)|$1|g;
$remainder =~ s|^\s*(.*)$|$1|;
if ($remainder ne "") {
die "syntax error in $path, line $line: unexpected data \"$remainder\" found.\n";
}
$rv->{$var} = $val;
}
&closefile($io);
return $rv;
}
#
# Write content of a hash into a file and close file
#
sub hash2fileandclose {
my ($io, $iv) = @@_;
my $var;
my $val;
foreach my $k (sort keys %{$iv}) {
$var = "e($k);
$val = "e($iv->{$k});
print $io "$var $val\n";
}
&closefile($io);
}
#
# Read accounts file
#
sub readaccounts {
my $ac = [];
my $path;
my $io;
# in setup mode, no access to the file is no reason to die; just return no data
if ($opt_setup) {
($path, $io) = &openfile("accounts", "x");
if (not $io) {
return $ac;
}
}
($path, $io) = &openfile("accounts", "r");
my $line = 1;
my $ln;
while (defined($ln = <$io>)) {
$line++;
$ln =~ s|^\s*||s; #strip off leading spaces
$ln =~ s|\s*(#.*)?$||s; #strip off trailing spaces and comments
next if ( $ln =~ m|^\s*$| ); #ignore empty lines
my $remainder = $ln;
my $q = '"';
my $type;
if ($remainder =~ m|^$q|) {
($type, $remainder) = Text::Balanced::extract_delimited($remainder, $q);
$type =~ s|^$q(.*)$q$|$1|;
} else {
$_ = $remainder;
($type, $remainder) = m|^([^\s]*)(.*)$|;
}
$type =~ s|\\(.)|$1|g;
$remainder =~ s|^\s*(.*)$|$1|;
if (($type ne "A") && ($type ne "R")) {
die "syntax error in $path, line $line: unexpected type \"$type\" found.\n";
}
my $name;
if ($remainder =~ m|^$q|) {
($name, $remainder) = Text::Balanced::extract_delimited($remainder, $q);
$name =~ s|^$q(.*)$q$|$1|;
} else {
$_ = $remainder;
($name, $remainder) = m|^([^\s]*)(.*)$|;
}
$name =~ s|\\(.)|$1|g;
$remainder =~ s|^\s*(.*)$|$1|;
if (not $name =~ m|^/[-a-zA-Z0-9/]+[^/]$|) {
die "syntax error in $path, line $line: unexpected name \"$name\" found.\n";
}
my $desc;
if ($remainder =~ m|^$q|) {
($desc, $remainder) = Text::Balanced::extract_delimited($remainder, $q);
$desc =~ s|^$q(.*)$q$|$1|;
} else {
$_ = $remainder;
($desc, $remainder) = m|^([^\s]*)(.*)$|;
}
$desc =~ s|\\(.)|$1|g;
$remainder =~ s|^\s*(.*)$|$1|;
if ($remainder ne "") {
die "syntax error in $path, line $line: unexpected data \"$remainder\" found.\n";
}
my $sname = $name;
$sname .= "/" if ($type ne "R");
my $dname = &slash2dot($sname);
push (@@{$ac}, { type=>$type, name=>$sname, desc=>$desc });
push (@@{$ac}, { type=>$type, name=>$dname, desc=>$desc });
}
&closefile($io);
return $ac;
}
#
# Slash to dot account name conversion
#
sub slash2dot {
my ($sname) = @@_;
$sname =~ s|^/||;
my $dname = '';
foreach my $part (reverse(split(/\//, $sname))) {
$dname .= "." . $part;
}
return $dname;
}
#
# Dot to slash account name conversion
#
sub dot2slash {
my ($dname) = @@_;
$dname =~ s|^.||;
my $sname = '';
foreach my $part (reverse(split(/\./, $dname))) {
$sname .= "/" . $part;
}
return $sname;
}
#
# Complete possible account list based on given pattern
#
sub completeaccount {
my ($pattern) = @@_;
$pattern = quotemeta($pattern);
my $acs = [];
foreach my $ac (@@{$accounts}) {
if ($ac->{name} =~ m|^$pattern|) {
push @@{$acs}, $ac->{name};
}
}
return $acs;
}
#
# Read events file
#
sub readevents {
my $ev = {};
my $path;
my $io;
# in setup mode, no access to the file is no reason to die; just return no data
if ($opt_setup) {
($path, $io) = &openfile("events", "x");
if (not $io) {
return $ev;
}
}
($path, $io) = &openfile("events", "r");
my $line = 1;
my $ln;
while (defined($ln = <$io>)) {
$line++;
my $event = &ln2event($ln, $line);
next unless (defined $event);
&processeventfields($event, qw/status/);
if ($event->{status} eq "E") {
die "syntax error in $path, line $line: $event->{error}\n";
}
&processeventfields($event, qw/user uuid revision date begin end amount account remark status/);
my $uuid = $event->{uuid};
if (defined %{$ev}->{$uuid}) {
die "consistency error in $path, line $line: duplicate uuid \"$uuid\" first seen in line $ev->{$uuid}->{line}.\n";
}
if ($event->{status} eq "E") {
print STDERR "WARNING: bad event in $path, line $line: $event->{error}\n";
}
&setevent($ev, $event);
}
&closefile($io);
return $ev;
}
#
# Write events file
#
sub writeevents {
my $path;
my $io;
($path, $io) = &openfile("events", "w");
my $line = 1;
my $ln;
foreach my $uuid (sort bydateline keys %{$events}) {
$line++;
my $event = &getevent($events, $uuid);
&processeventfields($event, qw/crc32/);
setevent($events, $event);
$ln = &event2ln($event);
print $io "$ln\n";
};
&closefile($io);
return;
}
#
#
#
sub formattimespec {
my ($begin, $end, $amount) = (@@_);
return $begin . "-" . $end . "=" . $amount;
}
#
# optional quoting
# if arg is not empty and contains neither backslash, doublequote nor whitespace keep verbatim
# otherwise escape backslash and doublequotes with backslash and put doublequotes around result
#
sub quote {
my ($q) = (@@_);
return $q unless $q =~ m/[\\"\s]/ || $q eq '';
$q =~ s/([\\"])/\\$1/g;
return '"' . $q . '"';
}
#
# sort by date with fallback to begin/line and finally uuid
#
sub bydateline {
# by date
if ("$events->{$a}->{date}" ne "$events->{$b}->{date}") {
return "$events->{$a}->{date}" cmp "$events->{$b}->{date}";
}
# by begin time
if ("$events->{$a}->{begin}" ne "$events->{$b}->{begin}") {
return "$events->{$a}->{begin}" cmp "$events->{$b}->{begin}";
}
# by line number which was seen when read, new entries to the end
if ("$events->{$a}->{line}" eq "+") {
return 1;
}
if ("$events->{$b}->{line}" eq "+") {
return -1;
}
if ("$events->{$a}->{line}" ne "$events->{$b}->{line}") {
return "$events->{$a}->{line}" cmp "$events->{$b}->{line}";
}
# by uuid
return "$a" cmp "$b";
}
#
# split with support for balanced quotes
#
sub splitq {
my ($ln) = @@_;
my $q = '"';
my $remainder = $ln;
my $field = '';
if ($remainder =~ m|^$q|) {
($field, $remainder) = Text::Balanced::extract_delimited($remainder, $q);
$field =~ s|^$q(.*)$q$|$1|;
} else {
$_ = $remainder;
($field, $remainder) = m|^([^\s]*)(.*)$|;
}
$field =~ s|\\(.)|$1|g;
$remainder =~ s|^[ ]*||;
return $field, $remainder;
}
#
# Calculate CRC32 for an entry referenced by uuid
#
sub calccrc32 {
my ($event) = @@_;
my $crc32 = 0;
foreach my $f (qw/user uuid revision date begin end amount account remark/) {
my $field = $event->{$f};
$field = "" unless (defined $field);
$crc32 = &crc32($field, $crc32);
}
return sprintf("%08x", $crc32);
}
#
# open file and check/set for magic cookie on the first line
#
# INPUT
# $mode = "r" - open for reading and check cookie - die on error
# $mode = "w" - open for writing and set cookie - die on error
# $mode = "x" - test for existance and check cookie
#
# OUTPUT
# $path always returns the full path name of the file
# $io on read/write returns IO::File handle
# $io on existance test returns 0 (no) or 1 (yes)
#
sub openfile {
my ($file, $mode) = @@_;
my $path = "$ENV{HOME}/.as/$file";
my $magic = uc($file);
if ($mode eq "r") {
my $io = new IO::File "<$path";
if (not defined($io)) {
die "unable to open file \"$path\" for reading [hint: did you ever set up using -s?]";
}
my $rc = <$io>;
if (not $rc =~ m/^%!AS-$magic-$datavers$/ ) {
die "file \"$path\" fails magic cookie check for %!AS-$magic-$datavers";
}
return $path, $io;
}
elsif ($mode eq "w") {
# keep a history before overwriting
if (-f $path) {
my $f=".%0" . int(log($runtimecfg->{hist}) / log(10) + 1) . "d";
for(my $i = $runtimecfg->{hist}; $i >= 1; $i--) {
my $s = $i == 1 ? '' : sprintf($f, $i - 1);
my $t = sprintf($f, $i);
rename "$path$s", "$path$t" if (-f "$path$s");
}
}
my $io = new IO::File ">$path";
if (not defined($io)) {
die "unable to open file \"$path\" for writing";
}
my $rc = "%!AS-$magic-$datavers\n";
if (not defined (print $io $rc)) {
die "file \"$path\" fails magic cookie write for %!AS-$magic-$datavers";
}
return $path, $io;
}
elsif ($mode eq "x") {
my $flag = 0;
my $io = new IO::File "<$path";
if (defined($io)) {
my $rc = <$io>;
&closefile($io);
if ($rc =~ m/^%!AS-$magic-$datavers$/ ) {
$flag = 1;
}
}
return $path, $flag;
}
die "INTERNAL: openfile() called with unknown mode \"$mode\"";
}
#
# close file previously opened by openfile()
#
sub closefile {
my ($io) = @@_;
if (defined($io)) {
$io->close();
} else {
die "INTERNAL: closefile() called on undefined file handle\n";
}
return;
}
#
# setup $HOME/.as
#
sub do_setup {
my $path;
my $io;
# try an open just to figure out the path
($path, $io) = &openfile("rc", "x");
$path =~ s|/[^/]*$||;
# create the folder, if it doesn't exist
if (not -d $path) {
mkdir $path, 0750 || die "cannot create directory \"$path\"";
}
# write the "rc" file
&writerc();
# write the "accounts" file
($path, $io) = &openfile("accounts", "w");
print $io "A /example \"an example account\"\n";
print $io "R /example/account \"please get a real account list\"\n";
&closefile($io);
# write the "events" file
my $event = {
"account" => "/example/account",
"remark" => "guess what this is"
};
&newevent($event);
($path, $io) = &openfile("events", "w");
&writeevents();
&closefile($io);
}
#
# create an new event
#
sub do_newevent {
my ($timespec, $account, $remark) = @@_;
# time calculation and sanity check
my ($begin, $end, $amount) = &splittimespec($timespec);
die "unexpected begin \"$begin\" found.\n" if (not &isvalidhhmm($begin));
die "unexpected end \"$end\" found.\n" if (not &isvalidhhmm($end));
die "unexpected amount \"$amount\" found.\n" if (not &isvalidhhmm($amount));
# preset
my $event = {
"begin" => $begin,
"end" => $end,
"amount" => $amount,
"account" => $account,
"remark" => $remark
};
&newevent($event);
}
#
# create an new event from scratch or paste in data from optional given event
#
sub newevent {
my ($paste) = @@_;
my $event = {
"uuid" => $paste->{"uuid"} || &newuuid(),
"line" => $paste->{"line"} || "+",
"user" => $paste->{"user"} || $runtimecfg->{user},
"crc32" => $paste->{"crc32"} || undef,
"revision" => $paste->{"revision"} || 0,
"date" => $paste->{"date"} || $runtimecfg->{date},
"begin" => $paste->{"begin"} || "00:00",
"end" => $paste->{"end"} || "24:00",
"amount" => $paste->{"amount"} || &calcamount($paste->{"begin"}, $paste->{"end"}) || "08:00",
"account" => $paste->{"account"} || "",
"remark" => $paste->{"remark"} || "",
"error" => $paste->{"error"} || undef
};
&setevent($events, $event);
return $event;
}
#
# create uuid and check for uniqness in local database
#
sub newuuid {
my $ug = new Data::UUID;
my $uuidinternal = $ug->create();
my $uuid = lc $ug->to_string($uuidinternal);
if (defined %{$events}->{$uuid}) {
die "consistency error: duplicate uuid \"$uuid\" first seen in line $events->{$uuid}->{line}.\n";
}
return $uuid;
}
#
# splittimespec
#
sub splittimespec {
my ($timespec) = @@_;
my $begin = '00:00';
my $end = '24:00';
my $amount = '';
my $compute = '';
my $beginsec;
my $endsec;
my $amountsec;
if ($timespec =~ m/^(([^-]+)?-([^=]+)?)?=?(.*)$/) { #( begin? - end? )? =? amount*
($begin, $end, $amount) = ($2, $3, $4);
}
$begin = &anytime2hhmm($begin);
$end = &anytime2hhmm($end);
$amount = &anytime2hhmm($amount);
return $begin, $end, $amount;
}
#
# calculate missing time only if exactly two valid times are given
#
sub calctime {
my ($begin, $end, $amount) = @@_;
if ((not &isvalidhhmm($begin)) && &isvalidhhmm($end) && &isvalidhhmm($amount)) {
$begin = &calcbegin($end, $amount);
}
if (&isvalidhhmm($begin) && (not &isvalidhhmm($end)) && &isvalidhhmm($amount)) {
$end = &calcend($begin, $amount);
}
if (&isvalidhhmm($begin) && &isvalidhhmm($end) && (not &isvalidhhmm($amount))) {
$amount = &calcamount($begin, $end);
}
return $begin, $end, $amount;
}
sub calcbegin {
my ($end, $amount) = @@_;
my $begin = '';
if (($end ne '') && ($amount ne '')) {
$end = &anytime2hhmm($end);
$amount = &anytime2hhmm($amount);
$begin = &sec2hhmm(&hhmm2sec($end) - &hhmm2sec($amount));
}
return $begin;
}
sub calcend {
my ($begin, $amount) = @@_;
my $end = '';
if (($begin ne '') && ($amount ne '')) {
$begin = &anytime2hhmm($begin);
$amount = &anytime2hhmm($amount);
$end = &sec2hhmm(&hhmm2sec($begin) + &hhmm2sec($amount));
}
return $end;
}
sub calcamount {
my ($begin, $end) = @@_;
my $amount = '';
if (($begin ne '') && ($end ne '')) {
$begin = &anytime2hhmm($begin);
$end = &anytime2hhmm($end);
$amount = &sec2hhmm(&hhmm2sec($end) - &hhmm2sec($begin));
}
return $amount;
}
sub roundtime {
my ($time, $precision) = @@_;
$precision = "00:15" if (not defined $precision);
$precision = &hhmm2sec($precision);
$time = &hhmm2sec($time);
$time = int(($time + ($precision / 2)) / $precision) * $precision;
$time = &sec2hhmm($time);
return $time;
}
sub inctime {
my ($time, $step) = @@_;
if (not defined $step) {
if ($time eq &roundtime($time)) {
$step = "00:15";
}
else {
$step = "00:01";
}
}
$step = &hhmm2sec($step);
$time = &hhmm2sec($time);
$time += $step;
$time = &sec2hhmm($time);
return $time;
}
sub dectime {
my ($time, $step) = @@_;
if (not defined $step) {
if ($time eq &roundtime($time)) {
$step = "00:15";
}
else {
$step = "00:01";
}
}
$step = &hhmm2sec($step);
$time = &hhmm2sec($time);
$time -= $step;
$time = &sec2hhmm($time);
return $time;
}
sub hhmm2sec {
my ($hhmm) = (@@_);
my $rv = undef;
if ($hhmm =~ m/^([0-1]?[0-9]|2[0-4]):([0-5]?[0-9])$/) {
$rv = $1 * 3600 + $2 * 60;
}
return $rv;
}
sub sec2hhmm {
my ($sec) = (@@_);
$sec %= 86400; #sec/day
$sec = 86400 - $sec if ($sec < 0);
my $minutes = int($sec / 60);
my $hour = int($minutes / 60);
my $min = int($minutes % 60);
return sprintf("%02d:%02d", $hour, $min);
}
sub day {
my (@@localtime) = (@@_);
my $rv;
@@localtime = (localtime(time)) unless @@localtime;
my ($day, $month, $year) = (@@localtime[3,4,5]);
$month++;
$year += 1900;
$rv = sprintf("%04d-%02d-%02d", $year, $month, $day);
return $rv;
}
sub now {
my (@@localtime) = (@@_);
my $rv;
@@localtime = (localtime(time)) unless @@localtime;
my ($sec, $min, $hour) = (@@localtime[0,1,2]);
$rv = sprintf("%02d:%02d", $hour, $min);
return $rv;
}
sub yyyymmdd2sec {
my ($yyyymmdd) = (@@_);
my $rv = undef;
$yyyymmdd = &day() if (not &isvalidyyyymmdd($yyyymmdd));
$yyyymmdd =~ m|^(.*)-(.*)-(.*)$|;
my ($year, $month, $mday) = ($1, $2, $3);
$month--;
$rv = timegm("00", "00", "00", $mday, $month, $year);
return $rv;
}
sub sec2yyyymmdd {
my ($sec) = (@@_);
my $rv = undef;
$rv = &day(localtime($sec));
return $rv;
}
sub incdate {
my ($date, $step) = @@_;
$step = (24 * 60 * 60) if (not defined $step);
$date = &yyyymmdd2sec($date);
$date += $step;
$date = &sec2yyyymmdd($date);
return $date;
}
sub decdate {
my ($date, $step) = @@_;
$step = (24 * 60 * 60) if (not defined $step);
$date = &yyyymmdd2sec($date);
$date -= $step;
$date = &sec2yyyymmdd($date);
return $date;
}
#
# setup $HOME/.as
#
sub do_unimplemented {
die "Sorry, this function is currently not implemented\n";
}
#
# create a data matrix from events
#
sub events2matrix {
my $c;
my $r;
my $label;
# fill in information about column headers (top)
$c = -1;
$label = "status" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "#" , "Labelhide" => 0, "Widthmin" => 1, "Widthmax" => 1, "Width" => 1, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0, "Coleditable" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "uuid" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "UUID" , "Labelhide" => 0, "Widthmin" => 36, "Widthmax" => 36, "Width" => 36, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0, "Coleditable" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "line" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Line" , "Labelhide" => 0, "Widthmin" => 3, "Widthmax" => 5, "Width" => 4, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0, "Coleditable" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "user" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "User" , "Labelhide" => 0, "Widthmin" => 2, "Widthmax" => 8, "Width" => 8, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0, "Coleditable" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "crc32" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "CRC" , "Labelhide" => 0, "Widthmin" => 8, "Widthmax" => 8, "Width" => 8, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0, "Coleditable" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "revision"; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Rev." , "Labelhide" => 0, "Widthmin" => 1, "Widthmax" => 5, "Width" => 5, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0, "Coleditable" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "date" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Date" , "Labelhide" => 0, "Widthmin" => 8, "Widthmax" => 10, "Width" => 10, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 1, "Coleditable" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "begin" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Begin" , "Labelhide" => 0, "Widthmin" => 5, "Widthmax" => 5, "Width" => 5, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 1, "Coleditable" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "end" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "End" , "Labelhide" => 0, "Widthmin" => 5, "Widthmax" => 5, "Width" => 5, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 1, "Coleditable" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "amount" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Amnt." , "Labelhide" => 0, "Widthmin" => 5, "Widthmax" => 5, "Width" => 5, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 1, "Coleditable" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "account" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Account" , "Labelhide" => 0, "Widthmin" => 10, "Widthmax" => 44, "Width" => 21, "Widthweight" => 10, "Colgap" => 1, "Colhide" => 0, "Coljump" => 1, "Coleditable" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "remark" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Remark" , "Labelhide" => 0, "Widthmin" => 10, "Widthmax" => 88, "Width" => 21, "Widthweight" => 5, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0, "Coleditable" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "error" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Error" , "Labelhide" => 0, "Widthmin" => 10, "Widthmax" => 44, "Width" => 21, "Widthweight" => 1, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0, "Coleditable" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$matrixdata->{"Columns"} = $c;
# fill in information about row headers (left) and cell data
$r = -1;
&matrixrowstatus($r, "$r");
$r++;
# fill in status
foreach my $uuid (keys %{$events}) {
&matrixrowset($r, $uuid, $events->{$uuid}->{status});
$r++;
}
$matrixdata->{"Rows"} = $r;
# mark virgin
$matrixdata->{"dirty"} = 0;
# fill in callbacks
$matrixdata->{"onanewline"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $sc;
my $pred = $dr; #predecessor of new line is current line
my $succ = $dr + 1; #successor of new line is next line
my $date;
$sc = $matrixdata->{"CK.date"};
if ($sc < $matrixdata->{"Columns"} and &isvalidyyyymmdd($matrixdata->{"CD.$sc.$pred"}->{"Data"})) {
$date = $matrixdata->{"CD.$sc.$pred"}->{"Data"};
}
else {
$date = &day();
}
my $begin;
$sc = $matrixdata->{"CK.end"};
if ($sc < $matrixdata->{"Columns"} and &isvalidhhmm($matrixdata->{"CD.$sc.$pred"}->{"Data"})) {
$begin = $matrixdata->{"CD.$sc.$pred"}->{"Data"};
}
else {
$begin = "";
}
my $end;
$sc = $matrixdata->{"CK.begin"};
if ($sc < $matrixdata->{"Columns"} and &isvalidhhmm($matrixdata->{"CD.$sc.$succ"}->{"Data"})) {
$end = $matrixdata->{"CD.$sc.$succ"}->{"Data"};
}
else {
$end = ($begin eq "") ? "" : &now();
}
my $paste = {
"date" => $date,
"begin" => $begin,
"end" => $end,
"amount" => "",
};
my $event = &newevent($paste);
&matrixinsertafter($dr, $event->{uuid}, "N");
$matrixdata->{"dirty"} = 1;
};
$matrixdata->{"Onanewline"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $sc;
my $pred = $dr - 1; #predecessor of new line is previous line
my $succ = $dr; #successor of new line is current line
my $date;
$sc = $matrixdata->{"CK.date"};
if ($sc < $matrixdata->{"Columns"} and &isvalidyyyymmdd($matrixdata->{"CD.$sc.$pred"}->{"Data"})) {
$date = $matrixdata->{"CD.$sc.$pred"}->{"Data"};
}
else {
$date = &day();
}
my $begin;
$sc = $matrixdata->{"CK.end"};
if ($sc < $matrixdata->{"Columns"} and &isvalidhhmm($matrixdata->{"CD.$sc.$pred"}->{"Data"})) {
$begin = $matrixdata->{"CD.$sc.$pred"}->{"Data"};
}
else {
$begin = "";
}
my $end;
$sc = $matrixdata->{"CK.begin"};
if ($sc < $matrixdata->{"Columns"} and &isvalidhhmm($matrixdata->{"CD.$sc.$succ"}->{"Data"})) {
$end = $matrixdata->{"CD.$sc.$succ"}->{"Data"};
}
else {
$end = ($begin eq "") ? "" : &now();
}
my $paste = {
"date" => $date,
"begin" => $begin,
"end" => $end,
"amount" => "",
};
my $event = &newevent($paste);
&matrixinsertrowat($dr, $event->{uuid}, "N");
$matrixdata->{"dirty"} = 1;
};
$matrixdata->{"deletedelete"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $event = &matrixrow2event($dr);
push @@{$matrixdata->{"undobuffer"}}, $event;
$matrixdata->{"dirty"} = 1;
return &matrixdeleterow($dr);
};
$matrixdata->{"undo"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $paste = pop @@{$matrixdata->{"undobuffer"}};
if (defined $paste) {
my $event = &newevent($paste);
&matrixinsertrowat($dr, $event->{uuid}, "U");
$matrixdata->{"dirty"} = 1;
return 1;
}
return 0;
};
$matrixdata->{"completeaccount"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr, $pattern) = (@@_);
$matrixdata->{"dirty"} = 1;
return &completeaccount($pattern);
};
$matrixdata->{"completedate"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr, $text) = (@@_);
$matrixdata->{"dirty"} = 1;
return &anydate2yyyymmdd($text);
};
$matrixdata->{"completetime"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr, $text) = (@@_);
$matrixdata->{"dirty"} = 1;
return &anytime2hhmm($text);
};
$matrixdata->{"yankcell"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
if (defined $matrixdata->{"yankrow"}) {
&matrixrowstatus($matrixdata->{"yankrow"})
}
$matrixdata->{"clipboard"} = $matrixdata->{"CD.$dc.$dr"}->{"Data"};
$matrixdata->{"clipboardtype"} = "cell";
$matrixdata->{"yankrow"} = $dr;
&matrixrowstatus($dr, "y");
};
$matrixdata->{"Yankline"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
if (defined $matrixdata->{"yankrow"}) {
&matrixrowstatus($matrixdata->{"yankrow"});
}
my $event = &matrixrow2event($dr);
for my $k (keys %{$event}) {
delete $event->{$k} unless ($k =~ m/^(user|date|begin|end|amount|account|remark)$/);
}
$matrixdata->{"clipboard"} = $event;
$matrixdata->{"clipboardtype"} = "line";
$matrixdata->{"yankrow"} = $dr;
&matrixrowstatus($dr, "Y");
};
$matrixdata->{"paste"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
if ($matrixdata->{"clipboardtype"} eq "cell") {
$matrixdata->{"CD.$dc.$dr"}->{"Data"} = $matrixdata->{"clipboard"};
$matrixdata->{"dirty"} = 1;
return 1;
}
elsif ($matrixdata->{"clipboardtype"} eq "line") {
my $event = &newevent($matrixdata->{"clipboard"});
&matrixinsertafter($dr, $event->{uuid}, "N");
$matrixdata->{"dirty"} = 1;
return 1;
}
return 0;
};
$matrixdata->{"Paste"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
if ($matrixdata->{"clipboardtype"} eq "line") {
my $event = &newevent($matrixdata->{"clipboard"});
&matrixinsertrowat($dr, $event->{uuid}, "N");
$matrixdata->{"dirty"} = 1;
return 1;
}
return 0;
};
$matrixdata->{"currentdatetime"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $k = $matrixdata->{"CH.$dc"}->{"Keyname"};
my $text = undef;
if ($k =~ m/^(date)$/) {
$text = &day();
}
elsif ($k =~ m/^(begin|end)$/) {
$text = &now();
}
elsif ($k =~ m/^(amount)$/) {
my $event = &matrixrow2event($dr);
$event->{amount} = "";
&processeventfields($event, qw/amount/);
$text = $event->{amount};
}
if (defined $text and ($matrixdata->{"CD.$dc.$dr"}->{"Data"} ne $text)) {
$matrixdata->{"CD.$dc.$dr"}->{"Data"} = $text;
$matrixdata->{"dirty"} = 1;
return 1;
}
return 0;
};
$matrixdata->{"recalculatetime"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $k = $matrixdata->{"CH.$dc"}->{"Keyname"};
my $text = undef;
if ($k =~ m/^(begin|end|amount)$/) {
my $event = &matrixrow2event($dr);
$event->{$k} = "";
&processeventfields($event, qw/amount end begin/);
$text = $event->{$k};
}
if (defined $text and ($matrixdata->{"CD.$dc.$dr"}->{"Data"} ne $text)) {
$matrixdata->{"CD.$dc.$dr"}->{"Data"} = $text;
$matrixdata->{"dirty"} = 1;
return 1;
}
return 0;
};
$matrixdata->{"roundtime"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $k = $matrixdata->{"CH.$dc"}->{"Keyname"};
my $text = undef;
if ($k =~ m/^(begin|end|amount)$/) {
my $event = &matrixrow2event($dr);
$text = &roundtime($event->{$k});
$text = "24:00" if ($text eq "00:00" and $k =~ /^(end|amount)$/);
}
if (defined $text and ($matrixdata->{"CD.$dc.$dr"}->{"Data"} ne $text)) {
$matrixdata->{"CD.$dc.$dr"}->{"Data"} = $text;
$matrixdata->{"dirty"} = 1;
return 1;
}
return 0;
};
$matrixdata->{"incdatetime"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $k = $matrixdata->{"CH.$dc"}->{"Keyname"};
my $text = undef;
if ($k =~ m/^(date)$/) {
my $event = &matrixrow2event($dr);
$text = &incdate($event->{$k});
}
elsif ($k =~ m/^(begin|end|amount)$/) {
my $event = &matrixrow2event($dr);
$text = $event->{$k};
$text = "00:00" if ($text eq "24:00");
$text = &inctime($text);
$text = "24:00" if ($text eq "00:00" and $k =~ /^(end|amount)$/);
}
if (defined $text and ($matrixdata->{"CD.$dc.$dr"}->{"Data"} ne $text)) {
$matrixdata->{"CD.$dc.$dr"}->{"Data"} = $text;
$matrixdata->{"dirty"} = 1;
return 1;
}
return 0;
};
$matrixdata->{"decdatetime"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $k = $matrixdata->{"CH.$dc"}->{"Keyname"};
my $text = undef;
if ($k =~ m/^(date)$/) {
my $event = &matrixrow2event($dr);
$text = &decdate($event->{$k});
}
elsif ($k =~ m/^(begin|end|amount)$/) {
my $event = &matrixrow2event($dr);
$text = $event->{$k};
$text = "00:00" if ($text eq "24:00");
$text = &dectime($text);
$text = "24:00" if ($text eq "00:00" and $k =~ /^(end|amount)$/);
}
if (defined $text and ($matrixdata->{"CD.$dc.$dr"}->{"Data"} ne $text)) {
$matrixdata->{"CD.$dc.$dr"}->{"Data"} = $text;
$matrixdata->{"dirty"} = 1;
return 1;
}
return 0;
};
$matrixdata->{"copycellfromprevrow"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $drsource = $dr - 1;
if ($matrixdata->{"CH.$dc"}->{"Coleditable"} and $drsource >= 0) {
$matrixdata->{"CD.$dc.$dr"}->{"Data"} = $matrixdata->{"CD.$dc.$drsource"}->{"Data"};
$matrixdata->{"dirty"} = 1;
return 1;
}
return 0;
};
$matrixdata->{"copycellfromsuccrow"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $drsource = $dr + 1;
if ($matrixdata->{"CH.$dc"}->{"Coleditable"} and $drsource < $matrixdata->{"Rows"}) {
$matrixdata->{"CD.$dc.$dr"}->{"Data"} = $matrixdata->{"CD.$dc.$drsource"}->{"Data"};
$matrixdata->{"dirty"} = 1;
return 1;
}
return 0;
};
$matrixdata->{"sort"} = sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
&matrixsort($dr);
};
$matrixdata->{"Sortorder"} = [
$matrixdata->{"CK.date"},
$matrixdata->{"CK.begin"},
$matrixdata->{"CK.line"}
] if (not defined $matrixdata->{"Sortorder"});
&matrixsort();
}
sub matrixsort {
my $nextrow;
my $r;
my $current = {};
$nextrow = $matrixdata->{"Rows"};
for ($r = 0; $r < $nextrow; $r++) {
my $collect = [];
my $coindex = 0;
foreach my $criteria (@@{$matrixdata->{"Sortorder"}}) {
$collect->[$coindex] = $matrixdata->{"CD.$criteria.$r"}->{"Data"} . " ";
$coindex++;
}
$current->{"$r"} = $collect;
}
# copy the sorted cell data to a new temporary target
my $newmatrixdata = {};
my $targetrow = 0;
my $sourcerow;
foreach my $sourcerow (sort
{
my $result = 0;
my $coindex = 0;
while ($result == 0) {
last if ( (not defined $current->{$a}->[$coindex])
or (not defined $current->{$b}->[$coindex])
);
$result = $current->{$a}->[$coindex] cmp $current->{$b}->[$coindex];
$coindex++
}
return $result;
}
keys %{$current}) {
$newmatrixdata->{"RH.$targetrow"} = $matrixdata->{"RH.$sourcerow"};
for (my $c = 0; $c < $matrixdata->{"Columns"}; $c++) {
$newmatrixdata->{"CD.$c.$targetrow"} = $matrixdata->{"CD.$c.$sourcerow"};
#delete $matrixdata->{"CD.$c.$sourcerow"};
}
$targetrow++;
}
# copy the temporary data back to the original hash
foreach my $element (keys %{$newmatrixdata}) {
$matrixdata->{$element} = $newmatrixdata->{$element};
}
}
sub matrixrowstatus {
my ($r, $status) = (@@_);
my $event = &matrixrow2event($r);
if (defined $status) {
$event->{status} = $status;
}
else {
&processeventfields($event, qw/status/);
$status = $event->{$status};
}
my $label = sprintf("%1s", $status);
$matrixdata->{"RH.$r"} = { "Label" => "$label" , "Labelhide" => 0, "Heightmin" => 1, "Heightmax" => 1, "Height" => 1, "Rowgap" => 0, "Rowhide" => 0 };
}
sub matrixrowset {
my ($r, $uuid, $label) = (@@_);
my $c;
&matrixrowstatus($r, "$label");
$c = $matrixdata->{"CK.uuid"};
$label = sprintf("%36s", $uuid);
$matrixdata->{"CD.$c.$r"} = {
"Data" => "$label",
"Focuscallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
},
"Blurcallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
$matrixwidget->{"VC.$vc.$vr"}->{-text} = $matrixdata->{"CD.$dc.$dr"}->{"Data"}; #view original data = read only
return 0;
}
};
$c = $matrixdata->{"CK.line"};
$label = sprintf("%4s", $events->{$uuid}->{line});
$matrixdata->{"CD.$c.$r"} = {
"Data" => "$label",
"Focuscallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
},
"Blurcallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
$matrixwidget->{"VC.$vc.$vr"}->{-text} = $matrixdata->{"CD.$dc.$dr"}->{"Data"}; #view original data = read only
return 0;
}
};
$c = $matrixdata->{"CK.crc32"};
$label = sprintf("%8s", $events->{$uuid}->{crc32});
$matrixdata->{"CD.$c.$r"} = {
"Data" => "$label",
"Focuscallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
},
"Blurcallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
$matrixwidget->{"VC.$vc.$vr"}->{-text} = $matrixdata->{"CD.$dc.$dr"}->{"Data"}; #view original data = read only
return 0;
}
};
$c = $matrixdata->{"CK.user"};
$label = sprintf("%s", $events->{$uuid}->{user});
$matrixdata->{"CD.$c.$r"} = {
"Data" => "$label",
"Focuscallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
},
"Blurcallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
$matrixdata->{"CD.$dc.$dr"}->{"Data"} = $matrixwidget->{"VC.$vc.$vr"}->{-text}; #pull view data into real data
$matrixdata->{"dirty"} = 1;
return 0;
}
};
$c = $matrixdata->{"CK.revision"};
$label = sprintf("%3d", $events->{$uuid}->{revision});
$matrixdata->{"CD.$c.$r"} = {
"Data" => "$label",
"Focuscallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
},
"Blurcallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
$matrixwidget->{"VC.$vc.$vr"}->{-text} = $matrixdata->{"CD.$dc.$dr"}->{"Data"}; #view original data = read only
return 0;
}
};
$c = $matrixdata->{"CK.date"};
$label = sprintf("%s", $events->{$uuid}->{date});
$matrixdata->{"CD.$c.$r"} = {
"Data" => "$label",
"Focuscallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
},
"Blurcallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $text = $matrixwidget->{"VC.$vc.$vr"}->{-text}; #pull view data into real data
if ($text ne '') {
$text = &anydate2yyyymmdd($text);
}
$matrixdata->{"CD.$dc.$dr"}->{"Data"} = $text;
$matrixwidget->{"VC.$vc.$vr"}->{-text} = $text;
$matrixdata->{"dirty"} = 1;
return 0;
}
};
$c = $matrixdata->{"CK.begin"};
$label = sprintf("%s", $events->{$uuid}->{begin});
$matrixdata->{"CD.$c.$r"} = {
"Data" => "$label",
"Focuscallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
},
"Blurcallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $event = &matrixrow2event($dr);
my $text = $matrixwidget->{"VC.$vc.$vr"}->{-text}; #pull view data into real data
$text = &anytime2hhmm($text) if ($text ne "");
$event->{begin} = $text;
&processeventfields($event, qw/amount end begin/);
my $redrawflag = 0;
foreach my $k (qw/begin end amount/) {
my $kdc = $matrixdata->{"CK.$k"};
if ($matrixdata->{"CD.$kdc.$dr"}->{"Data"} ne $event->{$k}) {
$matrixdata->{"CD.$kdc.$dr"}->{"Data"} = $event->{$k};
$matrixdata->{"dirty"} = 1;
$redrawflag = 1;
}
}
if (not $redrawflag) {
$matrixwidget->{"VC.$vc.$vr"}->{-text} = $event->{begin};
}
return $redrawflag;
}
};
$c = $matrixdata->{"CK.end"};
$label = sprintf("%s", $events->{$uuid}->{end});
$matrixdata->{"CD.$c.$r"} = {
"Data" => "$label",
"Focuscallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
},
"Blurcallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $event = &matrixrow2event($dr);
my $text = $matrixwidget->{"VC.$vc.$vr"}->{-text}; #pull view data into real data
$text = &anytime2hhmm($text) if ($text ne "");
$event->{end} = $text;
&processeventfields($event, qw/amount end begin/);
my $redrawflag = 0;
foreach my $k (qw/begin end amount/) {
my $kdc = $matrixdata->{"CK.$k"};
if ($matrixdata->{"CD.$kdc.$dr"}->{"Data"} ne $event->{$k}) {
$matrixdata->{"CD.$kdc.$dr"}->{"Data"} = $event->{$k};
$matrixdata->{"dirty"} = 1;
$redrawflag = 1;
}
}
if (not $redrawflag) {
$matrixwidget->{"VC.$vc.$vr"}->{-text} = $event->{end};
}
return $redrawflag;
}
};
$c = $matrixdata->{"CK.amount"};
$label = sprintf("%s", $events->{$uuid}->{amount});
$matrixdata->{"CD.$c.$r"} = {
"Data" => "$label",
"Focuscallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
},
"Blurcallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $event = &matrixrow2event($dr);
my $text = $matrixwidget->{"VC.$vc.$vr"}->{-text}; #pull view data into real data
$text = &anytime2hhmm($text) if ($text ne "");
$event->{amount} = $text;
&processeventfields($event, qw/amount end begin/);
my $redrawflag = 0;
foreach my $k (qw/begin end amount/) {
my $kdc = $matrixdata->{"CK.$k"};
if ($matrixdata->{"CD.$kdc.$dr"}->{"Data"} ne $event->{$k}) {
$matrixdata->{"CD.$kdc.$dr"}->{"Data"} = $event->{$k};
$matrixdata->{"dirty"} = 1;
$redrawflag = 1;
}
}
if (not $redrawflag) {
$matrixwidget->{"VC.$vc.$vr"}->{-text} = $event->{amount};
}
return $redrawflag;
}
};
$c = $matrixdata->{"CK.account"};
$label = sprintf("%s", $events->{$uuid}->{account});
$matrixdata->{"CD.$c.$r"} = {
"Data" => "$label",
"Focuscallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
},
"Blurcallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
my $text = $matrixwidget->{"VC.$vc.$vr"}->{-text}; #pull view data into real data
my $accounts = &completeaccount($text);
if (@@{$accounts} == 1) {
$text = $accounts->[0];
$text = &dot2slash($text) if ($text =~ m/^\./);
}
$matrixwidget->{"VC.$vc.$vr"}->{-text} = $text;
$matrixdata->{"CD.$dc.$dr"}->{"Data"} = $text;
$matrixdata->{"dirty"} = 1;
return 0;
}
};
$c = $matrixdata->{"CK.remark"};
$label = sprintf("%s", $events->{$uuid}->{remark});
$matrixdata->{"CD.$c.$r"} = {
"Data" => "$label",
"Focuscallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
},
"Blurcallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
$matrixdata->{"CD.$dc.$dr"}->{"Data"} = $matrixwidget->{"VC.$vc.$vr"}->{-text}; #pull view data into real data
$matrixdata->{"dirty"} = 1;
return 0;
}
};
$c = $matrixdata->{"CK.error"};
$label = sprintf("%s", $events->{$uuid}->{error});
$matrixdata->{"CD.$c.$r"} = {
"Data" => "$label",
"Focuscallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
},
"Blurcallback" => sub {
my ($matrixwidget, $dc, $dr, $vc, $vr) = (@@_);
$matrixwidget->{"VC.$vc.$vr"}->{-text} = $matrixdata->{"CD.$dc.$dr"}->{"Data"}; #view original data = read only
return 0;
}
};
}
sub matrixappend {
my ($uuid, $label) = (@@_);
my $nextrow;
$nextrow = $matrixdata->{"Rows"};
$nextrow = 0 unless(defined $nextrow);
&matrixrowset($nextrow, $uuid, $label);
$nextrow++;
$matrixdata->{"Rows"} = $nextrow;
}
sub matrixinsertafter {
my ($insrow, $uuid, $label) = (@@_);
$insrow++;
return &matrixinsertrowat($insrow, $uuid, $label);
}
sub matrixinsertrowat {
my ($insrow, $uuid, $label) = (@@_);
my $nextrow;
my $lastrow;
$nextrow = $matrixdata->{"Rows"};
$lastrow = $nextrow - 1;
if ($insrow >= $nextrow) {
&matrixappend($uuid, $label);
}
else {
my $targetrow = $nextrow;
my $sourcerow = $lastrow;
while ($sourcerow >= $insrow) {
$matrixdata->{"RH.$targetrow"} = $matrixdata->{"RH.$sourcerow"};
for (my $c = 0; $c < $matrixdata->{"Columns"}; $c++) {
#printf "$targetrow.$c = $sourcerow.$c\n";
$matrixdata->{"CD.$c.$targetrow"} = $matrixdata->{"CD.$c.$sourcerow"};
}
$targetrow--;
$sourcerow--;
}
&matrixrowset($insrow, $uuid, $label);
$nextrow++;
$matrixdata->{"Rows"} = $nextrow;
}
}
#
# removes a row; returns 1 if the row was the trailer
#
sub matrixdeleterow {
my ($delrow) = (@@_);
my $nextrow;
my $lastrow;
$nextrow = $matrixdata->{"Rows"};
$lastrow = $nextrow - 1;
if ($delrow >= $lastrow) {
&matrixshrinkrow();
return 1;
}
else {
my $targetrow = $delrow;
my $sourcerow = $delrow + 1;
while ($sourcerow < $nextrow) {
$matrixdata->{"RH.$targetrow"} = $matrixdata->{"RH.$sourcerow"};
for (my $c = 0; $c < $matrixdata->{"Columns"}; $c++) {
#printf "$targetrow.$c = $sourcerow.$c\n";
$matrixdata->{"CD.$c.$targetrow"} = $matrixdata->{"CD.$c.$sourcerow"};
}
$targetrow++;
$sourcerow++;
}
&matrixshrinkrow();
return 0;
}
}
#
# shrinks the matrix by one row by removing the trailer
#
sub matrixshrinkrow {
my $nextrow;
my $lastrow;
$nextrow = $matrixdata->{"Rows"};
if ($nextrow > 0) {
$lastrow = $nextrow - 1;
delete $matrixdata->{"RH.$lastrow"};
for (my $c = 0; $c < $matrixdata->{"Columns"}; $c++) {
delete $matrixdata->{"CD.$c.$lastrow"};
}
}
$nextrow--;
$matrixdata->{"Rows"} = $nextrow;
}
#
# dump a event for debugging purposes
#
sub dumpevent {
my ($event, $prefix) = (@@_);
$prefix = "dumpevent($event)" unless (defined $prefix);
foreach my $k (keys %{$event}) {
printf STDERR "DEBUG: %s: %8s=%s\n", $prefix, $k, (defined $event->{$k}) ? $event->{$k} : "";
}
}
#
# pull out events from data matrix
#
sub matrix2events {
my $c;
my $r;
my $uuid;
my $event;
$events = {};
for ($r = 0; $r <$matrixdata->{"Rows"}; $r++) {
$event = &matrixrow2event($r);
&setevent($events, $event);
}
}
#
# pull out a row and transform into an event
#
sub matrixrow2event {
my ($r) = (@@_);
my $event = {};
for (my $c = 0; $c < $matrixdata->{"Columns"}; $c++) {
my $k = $matrixdata->{"CH.$c"}->{"Keyname"};
next unless ($k =~ m/^(status|uuid|line|user|revision|date|begin|end|amount|account|remark|error)$/);
my $field = $matrixdata->{"CD.$c.$r"}->{"Data"};
$field =~ s|^\s*||s; #strip off leading spaces from the field
$field =~ s|\s*$||s; #strip off trailing spaces from the field
$event->{$k} = $field;
}
return $event;
}
#
# transform a line into an event
#
# INPUT
# $ln - string representing event
# $line - line number for tracking
#
# OUTPUT
# undef - input was a empty or whitespace only line
# %event - all data fields; line number tracking and annotations added
#
sub ln2event {
my ($ln, $line) = (@@_);
my $event = {};
$ln =~ s|^\s*||s; #strip off leading spaces from the wholly line
$ln =~ s|#.*?$||s; #strip off comments
$ln =~ s|\s*$||s; #strip off trailing spaces from the wholly line
return undef if ( $ln =~ m|^\s*$| ); #ignore empty lines
my $remainder = $ln;
$event->{status} = "R";
$event->{line} = $line;
foreach my $f (qw/user uuid crc32 revision date begin end amount account remark/) {
my $field;
($field, $remainder) = &splitq($remainder);
if (not defined $field) {
$event->{annotation}->{$f} = "undefined";
last;
}
$field =~ s|^\s*||s; #strip off leading spaces from the field
$field =~ s|\s*$||s; #strip off trailing spaces from the field
$event->{$f} = $field;
}
return $event;
}
#
# transform an event into a line
#
# INPUT
# %event - all data fields; line number tracking and annotations ignored
#
# OUTPUT
# $ln - string representing event; line number tracking and annotations lost
#
sub event2ln {
my ($event) = (@@_);
my $ln = undef;
foreach my $f (qw/user uuid crc32 revision date begin end amount account remark/) {
my $fielddata;
$ln .= " " if (defined $ln);
$ln .= "e($event->{$f});
}
return $ln;
}
#
# processevent
#
sub processeventfields {
my ($event, @@fields) = (@@_);
foreach my $f (@@fields) {
if ($f eq "status") {
my $status = $event->{status};
$status = " " if (not defined $status or $status eq "R");
my $error = &annotations2string($event->{annotation});
if (defined $error and $error ne "") {
$event->{error} = $error;
$status = "E";
}
else {
delete $event->{error};
if ($event->{crc32} ne &calccrc32($event)) {
$status = "M";
}
}
$event->{status} = $status;
}
elsif ($f eq "uuid") {
my $uuid = $event->{uuid};
if ($uuid eq '.') {
$uuid = &newuuid();
$event->{uuid} = $uuid;
$event->{annotation}->{uuid} = "new";
}
if (&isvaliduuid($uuid)) {
delete $event->{annotation}->{uuid};
}
else {
$event->{annotation}->{uuid} = "invalid";
}
}
elsif ($f eq "user") {
my $user = $event->{user};
if (&isvaliduser($user)) {
delete $event->{annotation}->{user};
}
else {
$event->{annotation}->{user} = "invalid";
}
}
elsif ($f eq "crc32") {
my $crc32 = &calccrc32($event);
$event->{crc32} = $crc32;
delete $event->{annotation}->{crc32};
}
elsif ($f eq "revision") {
my $revision = $event->{revision};
if (&isvalidrevision($revision)) {
delete $event->{annotation}->{revision};
}
else {
$event->{annotation}->{revision} = "invalid";
}
}
elsif ($f eq "date") {
my $date = $event->{date};
if (&isvaliddate($date)) {
delete $event->{annotation}->{date};
}
else {
$event->{annotation}->{date} = "invalid";
}
}
elsif ($f eq "begin") {
my $begin = $event->{begin};
if (&isvalidbegin($begin)) {
delete $event->{annotation}->{begin};
}
else {
$event->{annotation}->{begin} = "invalid";
}
($begin, my $end, my $amount) = &calctime($begin, $event->{end}, $event->{amount});
$event->{begin} = $begin;
}
elsif ($f eq "end") {
my $end = $event->{end};
if (&isvalidend($end)) {
delete $event->{annotation}->{end};
}
else {
$event->{annotation}->{end} = "invalid";
}
(my $begin, $end, my $amount) = &calctime($event->{begin}, $end, $event->{amount});
$event->{end} = $end;
}
elsif ($f eq "amount") {
my $amount = $event->{amount};
if (&isvalidamount($amount)) {
delete $event->{annotation}->{amount};
}
else {
$event->{annotation}->{amount} = "invalid";
}
(my $begin, my $end, $amount) = &calctime($event->{begin}, $event->{end}, $amount);
$event->{amount} = $amount;
}
elsif ($f eq "account") {
my $account = $event->{account};
if ($account =~ m|^\.|) {
$account = &dot2slash($event->{account});
}
if (&isvalidaccount($account)) {
$event->{account} = $account;
delete $event->{annotation}->{account};
}
else {
$event->{annotation}->{account} = "invalid";
}
}
elsif ($f eq "remark") {
my $remark = $event->{remark};
if (&isvalidremark($remark)) {
$event->{annotation}->{remark};
}
else {
$event->{annotation}->{remark} = "invalid";
}
}
}
$event->{error} = &annotations2string($event->{annotation});
return;
}
#
# transform the given annotations into a single string
#
sub annotations2string {
my ($annotations) = (@@_);
my $string = "";
foreach my $f (sort keys %{$annotations}) {
$string .= " " if (defined $string);
$string .= sprintf("%s=%s;", $f, $annotations->{$f});
}
return $string;
}
#
# set single event into events hash, copying selected keys and their values
#
sub setevent {
my ($ev, $event) = (@@_);
my $uuid = $event->{uuid};
foreach my $k (keys %{$event}) {
next unless ($k =~ m/^(line|status|user|crc32|revision|date|begin|end|amount|account|remark|error)$/);
%{$ev}->{$uuid}->{$k} = $event->{$k};
}
}
#
# get single event from events hash, copying selected keys and their values
#
sub getevent {
my ($ev, $uuid) = (@@_);
my $event = {};
$event->{uuid} = $uuid;
foreach my $k (keys %{$ev->{$uuid}}) {
next unless ($k =~ m/^(line|status|user|crc32|revision|date|begin|end|amount|account|remark|error)$/);
$event->{$k} = %{$ev}->{$uuid}->{$k};
}
return $event;
}
@
1.46
log
@double size of remark field. DANGER: this increases the probability for
user to get lost if screenwidth is smaller than a single column, so keep
issue pending in 00TODO. It has been successfully tested that save, quit
and exit still work in that particular situation.
@
text
@d43 1
a43 1
my $progvers = "0.6.4";
@
1.45
log
@cosmetics
@
text
@d1265 1
a1265 1
$label = "remark" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Remark" , "Labelhide" => 0, "Widthmin" => 10, "Widthmax" => 44, "Width" => 21, "Widthweight" => 5, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0, "Coleditable" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
@
1.44
log
@do not lock for read-only short-circuit dispatch, so move code
@
text
@d77 1
a77 1
print STDERR "ERROR: $txt\n";
d155 1
a155 1
printf STDERR "LOCK DETECTED! Additional information about the lock";
@
1.43
log
@properly unlock when dying, too
@
text
@d142 9
a176 9
# read in the accounts list
$accounts = &readaccounts();
# short-circuit dispatch
if ($opt_complete ne "") {
&do_complete($ARGV[0]);
exit(0);
}
@
1.42
log
@create and use &writerc()
@
text
@d78 1
d155 1
a155 1
exit 1;
d175 1
a175 1
exit 0;
a243 1
@
1.41
log
@add locking support
@
text
@d502 1
a502 1
my $rv = {};
d519 12
d967 1
a967 2
($path, $io) = &openfile("rc", "w");
&hash2fileandclose($io, $runtimecfg);
@
1.40
log
@migrate setup to use &newevent() as well
@
text
@d31 1
d65 1
d141 27
d244 1
d450 46
@
1.39
log
@migrate &cuinewevent() to &newevent() and consolidate CLI/CUI usage
@
text
@d889 5
d895 1
a895 6
print $io "#user 12345678-9abc-def0-1234-56789abcdef0 01234567 00001 20021204 10:00-11:00=01:00 /example/account \"example entry showing complete timespec\" #comment\n";
print $io "#user 12345678-9abc-def0-1234-56789abcdef1 01234567 00001 20021204 10:00-11:00= /example/account \"example entry showing how to calc amount\" #comment\n";
print $io "#user 12345678-9abc-def0-1234-56789abcdef2 01234567 00001 20021204 10:00-=01:00 /example/account \"example entry showing how to calc end\" #comment\n";
print $io "#user 12345678-9abc-def0-1234-56789abcdef3 01234567 00001 20021204 -11:00=01:00 /example/account \"example entry showing how to calc begin\" #comment\n";
print $io "#user 12345678-9abc-def0-1234-56789abcdef4 01234567 00001 20021204 -.=01:00 /example/account \"example entry showing how to calc begin until now\" #comment\n";
print $io "#user 12345678-9abc-def0-1234-56789abcdef5 01234567 00001 20021204 .-=01:00 /example/account \"example entry showing how to calc end upto now\" #comment\n";
@
1.38
log
@split off file/hash read/write from readrc
@
text
@d905 6
d912 6
a917 39
my $uuid = &newuuid();
my $line = "+";
my $user = $runtimecfg->{user};
my $crc32 = undef;
my $revision = 0;
my $date = $runtimecfg->{date};
my ($begin,
$end,
$amount) = &splittimespec($timespec);
# time calculation
($begin, $end, $amount) = &calctime($begin, $end, $amount);
# sanity check
if (not &isvalidhhmm($begin)) {
die "unexpected begin \"$begin\" found.\n";
}
if (not &isvalidhhmm($end)) {
die "unexpected end \"$end\" found.\n";
}
if (not &isvalidhhmm($amount)) {
die "unexpected amount \"$amount\" found.\n";
}
# create new uuid and store new event in memory
%{$events}->{$uuid} = {
line => $line,
user => $user,
crc32 => $crc32,
revision => $revision,
date => $date,
begin => $begin,
end => $end,
amount => $amount,
account => $account,
remark => $remark,
error => undef
d919 1
a919 1
return $uuid;
d923 1
a923 1
# create an new cui event from scratch or paste in data from optional given event
d925 1
a925 1
sub cuinewevent {
d1238 1
a1238 1
my $event = &cuinewevent($paste);
d1281 1
a1281 1
my $event = &cuinewevent($paste);
d1296 1
a1296 1
my $event = &cuinewevent($paste);
d1350 1
a1350 1
my $event = &cuinewevent($matrixdata->{"clipboard"});
d1360 1
a1360 1
my $event = &cuinewevent($matrixdata->{"clipboard"});
@
1.37
log
@apply rse convenience patch
@
text
@d426 2
a427 1
my $rc = {};
d433 2
a434 4
($path, $io) = &openfile("rc", "x");
if (not $io) {
return $rc;
}
d437 11
a447 1
($path, $io) = &openfile("rc", "r");
d484 1
a484 1
$rc->{$var} = $val;
d487 2
d490 13
a502 1
return $rc;
a878 2
my $quote = '"';
my $backslash = '\\';
d880 1
a880 6
foreach my $var (keys %{$runtimecfg}) {
my $val = $runtimecfg->{$var};
$val =~ s/([\\$quote\s])/$backslash$1/g; #FIXME poor man's escaping - hint: try quotemeta()
print $io "$var $quote$val$quote\n";
}
&closefile($io);
@
1.36
log
@flush pending changes
@
text
@d947 1
a947 1
"amount" => $paste->{"amount"} || "08:00",
@
1.35
log
@new feature "+"/"-" on date fields increment/decrement value
@
text
@d42 1
a42 1
my $progvers = "0.6.3";
@
1.34
log
@bugfix missing conversion of precision would appear on input
@
text
@d32 1
d1124 36
d1434 1
a1434 1
$matrixdata->{"inctime"} = sub {
d1438 5
a1442 1
if ($k =~ m/^(begin|end|amount)$/) {
d1456 1
a1456 1
$matrixdata->{"dectime"} = sub {
d1460 5
a1464 1
if ($k =~ m/^(begin|end|amount)$/) {
@
1.33
log
@new feature "+"/"-" on time fields increment/decrement value
@
text
@d1043 2
a1044 1
$precision = &hhmm2sec("00:15") if (not defined $precision);
@
1.32
log
@new feature "~" on time fields rounds time
@
text
@d1050 32
d1387 36
@
1.31
log
@new feature "=" on time fields recalculates time
@
text
@d1041 9
d1340 16
@
1.30
log
@flush pending changes
@
text
@d1322 17
@
1.29
log
@flag dirty on change only
@
text
@d41 1
a41 1
my $progvers = "0.6.2";
@
1.28
log
@implement useful "." handling on amount field
@
text
@d1315 1
a1315 1
if (defined $text) {
@
1.27
log
@the beast is known as as-cui :-)
@
text
@d1306 1
a1306 1
elsif ($k =~ m/^(begin|end|amount)$/) {
d1308 6
@
1.26
log
@flush pending changes
@
text
@d40 1
a40 1
my $progname = "as";
@
1.25
log
@cosmetics (name/vers was not listed during initialization)
@
text
@d41 1
a41 1
my $progvers = "0.6.1";
@
1.24
log
@fix crc32 getting lost after "save"
@
text
@d66 2
d1126 1
a1126 3
# fill in global information
$matrixdata->{"progname"} = $progname;
$matrixdata->{"progvers"} = $progvers;
@
1.23
log
@introduce dirty handling, view in status, recognize when quit; cursor stays away from movement mode
@
text
@d664 1
@
1.22
log
@add &statusline(); view program name and version
@
text
@d1126 1
d1171 1
d1214 1
d1220 1
d1229 1
d1236 1
d1241 1
d1246 1
d1277 1
d1283 1
d1293 1
d1310 1
d1320 1
d1330 1
d1468 1
d1502 1
d1527 1
d1560 1
d1593 1
d1623 1
d1638 1
@
1.21
log
@flush pending changes
@
text
@d1123 5
@
1.20
log
@revamp sorting from number to strings and document criteria
@
text
@d41 1
a41 1
my $progvers = "0.6.0";
@
1.19
log
@handle negative seconds and day overflow
@
text
@d698 2
a699 2
if (%{$events}->{$a}->{date} != %{$events}->{$b}->{date}) {
return %{$events}->{$a}->{date} <=> %{$events}->{$b}->{date};
d702 2
a703 2
if (%{$events}->{$a}->{begin} != %{$events}->{$b}->{begin}) {
return %{$events}->{$a}->{begin} <=> %{$events}->{$b}->{begin};
d705 2
a706 2
# by line with commandline entries to the end
if (%{$events}->{$a}->{line} eq "commandline") {
d709 1
a709 1
if (%{$events}->{$b}->{line} eq "commandline") {
d712 2
a713 2
if (%{$events}->{$a}->{line} != %{$events}->{$b}->{line}) {
return %{$events}->{$a}->{line} <=> %{$events}->{$b}->{line};
d715 2
a716 2
# fallback to uuid
return $a <=> $b;
d887 1
a887 1
my $line = "cli";
@
1.18
log
@implement version 0.6 data format
@
text
@d1050 2
a1051 1
my $rv = undef;
d1055 1
a1055 2
$rv = sprintf("%02d:%02d", $hour, $min);
return $rv;
@
1.17
log
@flush pending changes
@
text
@d41 1
a41 1
my $progvers = "0.5.6";
d263 1
a263 1
if ($input =~ m/^[2-9][0-9]{3}([0][1-9]|[1][0-2])([0][1-9]|[12][0-9]|[3][01])$/) {
d680 3
a682 1
# quote
d685 7
a691 17
my ($arg) = (@@_);
my $q = quotemeta $arg;
$q =~ s|^(\\ )+||; #remove escaped leading spaces
$q =~ s|(\\ )+$||; #remove escaped trailing spaces
$q =~ s|\\([.:/=-])|\1|g; #do not escape dots, colons, slashes, equal signs and dashes (list could be larger)
# do not escape spaces but if one or more spaces exist put quotes around the wholly string
if ($q =~ s|\\ | |g) {
$q = "\"" . $q . "\"";
}
# handle the empty string
if ($q eq "") {
$q = '""';
}
return $q;
}
d896 3
a977 2
#f ($timespec =~ m/^(([^-]+)?-([^=]+)?)?=?(.*)$/) { #( begin? - end? )? =? amount*
#perl -e '$t = "-03:00="; $t =~ m/^(([^-]+)?-([^=]+)?)?=?(.*)$/; print "$2 - $3 = $4\n";'
a979 18
if (($begin ne '') && ($end ne '') && ($amount eq '')) {
$begin = &anytime2hhmm($begin);
$end = &anytime2hhmm($end);
$amount = &sec2hhmm(&hhmm2sec($end) - &hhmm2sec($begin));
}
elsif (($begin ne '') && ($end eq '') && ($amount ne '')) {
$begin = &anytime2hhmm($begin);
$amount = &anytime2hhmm($amount);
$end = &sec2hhmm(&hhmm2sec($begin) + &hhmm2sec($amount));
}
elsif (($begin eq '') && ($end ne '') && ($amount ne '')) {
$end = &anytime2hhmm($end);
$amount = &anytime2hhmm($amount);
$begin = &sec2hhmm(&hhmm2sec($end) - &hhmm2sec($amount));
}
$begin = "00:00" if ($begin eq '');
$end = "24:00" if ($end eq '');
$amount = "00:00" if ($amount eq '');
d987 52
d1065 1
a1065 1
$rv = sprintf("%04d%02d%02d", $year, $month, $day);
d1102 1
a1102 1
$label = "date" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Date" , "Labelhide" => 0, "Widthmin" => 8, "Widthmax" => 8, "Width" => 8, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 1, "Coleditable" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
d1106 3
a1108 3
$label = "account" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Account" , "Labelhide" => 0, "Widthmin" => 10, "Widthmax" => 44, "Width" => 22, "Widthweight" => 10, "Colgap" => 1, "Colhide" => 0, "Coljump" => 1, "Coleditable" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "remark" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Remark" , "Labelhide" => 0, "Widthmin" => 10, "Widthmax" => 44, "Width" => 22, "Widthweight" => 5, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0, "Coleditable" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "error" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Error" , "Labelhide" => 0, "Widthmin" => 10, "Widthmax" => 44, "Width" => 22, "Widthweight" => 1, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0, "Coleditable" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
d1791 1
a1791 1
foreach my $f (qw/user uuid crc32 revision date timespec account remark/) {
d1800 1
a1800 6
if ($f eq "timespec") {
($event->{begin}, $event->{end}, $event->{amount}) = &splittimespec($field);
}
else {
$event->{$f} = $field;
}
d1818 1
a1818 1
foreach my $f (qw/user uuid crc32 revision date timespec account remark/) {
a1819 6
if ($f eq "timespec") {
$fielddata = &formattimespec($event->{begin}, $event->{end}, $event->{amount});
}
else {
$fielddata = $event->{$f};
}
d1821 1
a1821 1
$ln .= "e($fielddata);
d1903 1
a1903 2
my $timespec = &formattimespec($event->{begin}, $event->{end}, $event->{amount});
($begin, my $end, my $amount) = &splittimespec($timespec);
d1914 1
a1914 2
my $timespec = &formattimespec($event->{begin}, $event->{end}, $event->{amount});
(my $begin, $end, my $amount) = &splittimespec($timespec);
d1925 1
a1925 2
my $timespec = &formattimespec($event->{begin}, $event->{end}, $event->{amount});
(my $begin, my $end, $amount) = &splittimespec($timespec);
@
1.16
log
@add save; add quit
@
text
@d41 1
a41 1
my $progvers = "0.5.5";
@
1.15
log
@resolve name/lookup problem for initial sort criteria
@
text
@d177 10
a186 4
&events2matrix();
&as_cui_matrix::ascui($matrixdata, $opt_verbose);
&matrix2events();
&writeevents();
@
1.14
log
@time completion in bash sucks so throw it away
@
text
@d1288 5
a1292 1
$matrixdata->{"Sortorder"} = ["4", "5", "6"] if (not defined $matrixdata->{"Sortorder"});
@
1.13
log
@support cell copying from prev/succ row
@
text
@a239 37
elsif ($opt_complete eq 'time') {
my ($begin, $end, $amount) = &splittimespec($arg);
my $input = $arg;
my $output = $begin . "-" . $end . "=" . $amount;
# Brain-Dead GNU Bash Completion Wor(l)d Breaking Feature
# ...
# see also: GNU Bash 2.05b, bashline.c, line 208,
# variable "bash_completer_word_break_characters"
my $breakers = quotemeta(" \t\n\"'\@@><=;|&(:");
my ($prefix, $input_rem, $output_rem) = &splitme($input, $output);
sub splitme {
my ($in, $out) = @@_;
my ($pre, $in_rem, $out_rem) = ("", "", "");
my $min = (length($in) < length($out) ? length($in) : length($out));
my $i;
for ($i = 0; $i < $min; $i++) {
last if (substr($in, $i, 1) ne substr($out, $i, $1));
$pre .= substr($in, $i, 1);
}
$in_rem = substr($in, $i);
$out_rem = substr($out, $i);
return ($pre, $in_rem, $out_rem);
}
if ($input !~ m|[$breakers]|s) {
# nop
}
elsif ($prefix =~ m|[$breakers]|s and $input_rem !~ m|[$breakers]|s) {
my $prefix_rem = $prefix;
$prefix_rem =~ s|^.*[$breakers]||s;
$output = $prefix_rem . $output_rem;
}
else {
$output = "";
}
print "$output\n";
}
d241 1
a241 1
die "invalid completion type \"$opt_complete\" (has to be 'account' or 'time')";
@
1.12
log
@support setting jump destinations
@
text
@d1100 13
a1112 13
$label = "status" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "#" , "Labelhide" => 0, "Widthmin" => 1, "Widthmax" => 1, "Width" => 1, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "uuid" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "UUID" , "Labelhide" => 0, "Widthmin" => 36, "Widthmax" => 36, "Width" => 36, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "line" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Line" , "Labelhide" => 0, "Widthmin" => 3, "Widthmax" => 5, "Width" => 4, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "user" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "User" , "Labelhide" => 0, "Widthmin" => 2, "Widthmax" => 8, "Width" => 8, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "crc32" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "CRC" , "Labelhide" => 0, "Widthmin" => 8, "Widthmax" => 8, "Width" => 8, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "revision"; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Rev." , "Labelhide" => 0, "Widthmin" => 1, "Widthmax" => 5, "Width" => 5, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "date" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Date" , "Labelhide" => 0, "Widthmin" => 8, "Widthmax" => 8, "Width" => 8, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "begin" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Begin" , "Labelhide" => 0, "Widthmin" => 5, "Widthmax" => 5, "Width" => 5, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "end" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "End" , "Labelhide" => 0, "Widthmin" => 5, "Widthmax" => 5, "Width" => 5, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "amount" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Amnt." , "Labelhide" => 0, "Widthmin" => 5, "Widthmax" => 5, "Width" => 5, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0, "Coljump" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "account" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Account" , "Labelhide" => 0, "Widthmin" => 10, "Widthmax" => 44, "Width" => 22, "Widthweight" => 10, "Colgap" => 1, "Colhide" => 0, "Coljump" => 1 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "remark" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Remark" , "Labelhide" => 0, "Widthmin" => 10, "Widthmax" => 44, "Width" => 22, "Widthweight" => 5, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "error" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Error" , "Labelhide" => 0, "Widthmin" => 10, "Widthmax" => 44, "Width" => 22, "Widthweight" => 1, "Colgap" => 1, "Colhide" => 0, "Coljump" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
d1298 18
@
1.11
log
@flush pending changes
@
text
@d1100 13
a1112 13
$label = "status" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "#" , "Labelhide" => 0, "Widthmin" => 1, "Widthmax" => 1, "Width" => 1, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "uuid" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "UUID" , "Labelhide" => 0, "Widthmin" => 36, "Widthmax" => 36, "Width" => 36, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "line" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Line" , "Labelhide" => 0, "Widthmin" => 3, "Widthmax" => 5, "Width" => 4, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "user" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "User" , "Labelhide" => 0, "Widthmin" => 2, "Widthmax" => 8, "Width" => 8, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "crc32" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "CRC" , "Labelhide" => 0, "Widthmin" => 8, "Widthmax" => 8, "Width" => 8, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "revision"; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Rev." , "Labelhide" => 0, "Widthmin" => 1, "Widthmax" => 5, "Width" => 5, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "date" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Date" , "Labelhide" => 0, "Widthmin" => 8, "Widthmax" => 8, "Width" => 8, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "begin" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Begin" , "Labelhide" => 0, "Widthmin" => 5, "Widthmax" => 5, "Width" => 5, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "end" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "End" , "Labelhide" => 0, "Widthmin" => 5, "Widthmax" => 5, "Width" => 5, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "amount" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Amnt." , "Labelhide" => 0, "Widthmin" => 5, "Widthmax" => 5, "Width" => 5, "Widthweight" => 0, "Colgap" => 1, "Colhide" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "account" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Account" , "Labelhide" => 0, "Widthmin" => 10, "Widthmax" => 44, "Width" => 22, "Widthweight" => 10, "Colgap" => 1, "Colhide" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "remark" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Remark" , "Labelhide" => 0, "Widthmin" => 10, "Widthmax" => 44, "Width" => 22, "Widthweight" => 5, "Colgap" => 1, "Colhide" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
$label = "error" ; $matrixdata->{"CH.$c"} = { "Keyname" => $label, "Label" => "Error" , "Labelhide" => 0, "Widthmin" => 10, "Widthmax" => 44, "Width" => 22, "Widthweight" => 1, "Colgap" => 1, "Colhide" => 0 }; $matrixdata->{"CK.$label"} = $c; $c++;
@
1.10
log
@cosmetics :-)
@
text
@d41 1
a41 1
my $progvers = "0.5.4";
@
1.9
log
@fix comment, fill last line 100%
@
text
@d25 1
a25 1
## as.pl: Unix Command-Line Client
d29 6
a34 6
use strict; #OpenPKG perl-5.6.1
use IO; #OpenPKG perl-5.6.1
use Getopt::Long; #OpenPKG perl-5.6.1
use Text::Balanced; #OpenPKG perl-parse-20021016
use Data::UUID; #OpenPKG perl-crypto-20021030
use String::CRC32; #OpenPKG perl-crypto-20021030
d36 1
a36 1
require "as-cui-matrix.pm"; #part of AS-CUI
d59 1
a59 1
my @@localruntime = localtime(time);
@
1.8
log
@flush pending changes
@
text
@d96 1
a96 1
# post-process parsed options
@
1.7
log
@&today was renamed to &day
@
text
@d41 1
a41 1
my $progvers = "0.5.3";
@
1.6
log
@flush pending changes
@
text
@d1140 1
a1140 1
$date = &today();
d1183 1
a1183 1
$date = &today();
@
1.5
log
@Transfer documentation fragments from code to POD
Allow F1/Help to read and display an as-cui.help file which can be created from POD
Remove unnecessary checks for $editcell in &keybinder as it's head is a short circuit
@
text
@d41 1
a41 1
my $progvers = "0.5.2";
@
1.4
log
@flush pending changes
@
text
@d1120 1
a1120 5
# "R" event was just read in and no status was ever computed
# " " event is good
# "E" event has one or more errors
# "M" event was modified (crc32 fails)
# "N" event is new
@
1.3
log
@modules was renamed
@
text
@d41 1
a41 1
my $progvers = "0.5.1";
@
1.2
log
@flush pending changes
@
text
@d178 1
a178 1
&matrix::ascui($matrixdata, $opt_verbose);
@
1.1
log
@polish for initial release
@
text
@d41 1
a41 1
my $progvers = "0.5.0";
@