#!@PERL@
eval 'exec @PERL@ -S $0 ${1+"$@"}'
    if $running_under_some_shell;
            # this emulates #! processing on NIH machines.
            # (remove #! line above if indigestible)


# accounting.pl.in,v 5.5 2000/06/09 21:55:12 papowell Exp
# LPRng based accounting script.
#  Version Thu Apr 13 21:00:20 PDT 2000
# LPRng 3.6.14
#
# stdin = /dev/null
# stdout = accounting file
# stderr = log file
#  
#  command line format:
#   accounting [start|end] [-options] [accounting file]
#     -Tdebug will turn on debugging
#     start - at start of job; scan accounting file, fix up,
#         put in START entry
#     end  - at end of job; scan accounting file, fix up,
#         put in END entry
#
# Accounting File has format:
# start '-ppagecounter' '-Athis' -P'that'  <- startpage
# ...
# end   '-ppagecounter'                    <- lastpage
# END 't=$elapsed' 'p=$pages' 'q=$lastpage' 's=$startpage' 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time' 'S=$starttime'
#               --- end of a job
# START 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'
# start '-ppagecounter' '-Athis' -P'that'    <- startpage
# ...
# end   '-ppagecounter'                        <- lastpage
# END 't=$elapsed' 'p=$pages' 'q=$lastpage' 's=$startpage' 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time' 'S=$starttime'
#
# We implement a stack based FSM to do the following:
# Stack = NULL, state = FIND_START
#   
#  FIND_START - find the first START
#   START -> push START line
#         -> FIND_start
#
#  FIND_start - find the first 'start' entry with page
#   start -> push start
#         -> FIND_end
#
#  FIND_end   - find the first 'end' entry for this job
#   end   -> push end line
#         -> FOUND_end
#   START -> push START line
#         -> FIND_start
#
#  FOUND_end  - keep looking for 'end' entries
#   end   -> pop stack
#         -> push end line
#   START -> push START line
#         -> FIND_start
#
# All states:
#
#   END   -> clear stack
#         -> FIND_START
#   START -> push START line
#         -> FIND_start
#
#
#  At end, if you do not have state FOUND_end you do not have a
#  valid accounting file so you have to wait.
#  Stack will have contents:
#      ((START*  start )* end) *
#                ^ start page count
#                         ^ end page count
#   we start from the bottom of the stack;
#    - end entry sets end page count for job
#    - start entry sets start page count for job
#    - START assigns pagecount as difference between start and end
#            end page count = start page count
#
# seq   START START START start end
# pages   0     0      end - start       
#
# seq   START START start1         START START start2 end2
# pages   0     start2-start1       0      start2-end2
#

use strict;
use Getopt::Std;
use FileHandle;

my($JFAIL, $JABORT, $JREMOVE, $JHOLD) = ( 32, 33, 34, 37);
my(%opt, $action, $acct_h, $debug,$state,@stack);
my($FIND_START,$FIND_start,$FIND_end,$FOUND_end)=(0,1,2,3,4,5);


$debug = 0;

# print STDERR "$0: '" . join("' '",@ARGV) . "'\n" if $debug;
$action = "";
if( @ARGV ){
    if( $ARGV[0] !~ /^-/ ){
        $action = shift @ARGV;
    }
    print STDERR "action $action\n" if $debug;
}
if( $action ne "start" and $action ne "end" ){
    print STDERR "$0: invalid action '$action'\n";
    exit $JABORT;
}

# pull out the options

getopts( 'A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q:R:T:S:U:V:W:X:Y:Z:'
. 'a:b:cd:e:f:g:h:i:j:k:l:m:n:o:p:q:r:t:s:u:v:w:x:y:z:', \%opt );

my($acct_file) = "";
if( @ARGV ){
    $acct_file = shift @ARGV;
} else {
    $acct_file = $opt{'a'};
}
if( exists( $opt{T} ) && $opt{T} =~ m/debug/ ){
    $debug = 1;
}
if( !$acct_file ){
    print STDERR "$0: no accounting file\n";
    exit( $JABORT );
}

my($time) = time;
print STDERR "$0: $action 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'\n" if $debug;

print STDERR "accounting file '$acct_file'\n" if $debug;

if( $action eq "start" ){
    $acct_h = new FileHandle ">>$acct_file" ;
    print $acct_h "START 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'\n";
    print STDERR "START 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'\n" if $debug;
    $acct_h->close();
    exit 0;
}

$acct_h = new FileHandle "+<$acct_file" ;
if( ! defined($acct_h) ){
    print STDERR "$0: cannot open $acct_file r/w - $!\n";
    exit( $JABORT );
}

# now we read the last line from the accounting file
my($size) = -s $acct_file;
my($max_seek) = 2048;
my($read_all) = 0;

if( $size > $max_seek  && not $acct_h->seek( -$max_seek, 2 )
){
    print STDERR "$0: lseek $acct_file failed - $!\n";
    exit( $JABORT );
}

again:

@stack = ();
$state = $FIND_START;

while( <$acct_h> ){
    chomp;
    print STDERR "STATE '$state' LINE '$_'\n" if $debug;
    print STDERR "STACK\n " . join("\n ",@stack) . "\n" if $debug;
    if( /^END / ){
        $state = $FIND_START;
        @stack = ();
        next;
    }
    if( /^START / ){
        push @stack, $_;
        $state = $FIND_start;
        next;
    }
    if( $state == $FIND_start ){
        if( /^(file)?start .*-p\d+/ ){
            push @stack, $_;
            $state = $FIND_end;
        }
    } elsif( $state == $FIND_end ){
        if( /^(file)?end .*-p\d+/ ){
            push @stack, $_;
            $state = $FOUND_end;
        }
    } elsif( $state == $FOUND_end ){
        if( /^(file)?end .*-p\d+/ ){
            pop @stack;
            push @stack, $_;
            $state = $FOUND_end;
        }
    }
}

if( $state != $FOUND_end ){
    if( $read_all ){
        print STDERR "$0: did not find end record in file";
        exit 0;
    }
    $acct_h->close();
    $acct_h = new FileHandle "+<$acct_file" ;
    if( ! defined($acct_h) ){
        print STDERR "$0: cannot open $acct_file r/w - $!\n";
        exit( $JABORT );
    }
    $read_all = 1;
}

print STDERR "FINAL STATE '$state'\n" if $debug;
print STDERR "FINAL STACK\n " . join("\n ",@stack) . "\n" if $debug;
my($end_counter,$start_counter,$start_time,$count,$out,$elapsed);

$end_counter = $start_counter = 0;
$out = "";

for( my $i = @stack -1; $i >= 0 ; --$i ){
    $_ = $stack[$i];
    print STDERR "stack [$i] '$_'\n" if $debug;
    if( /^[a-z]*end .*-p(\d+)/ ){
        $end_counter = $1;
    } elsif( /^[a-z]*start .*-p(\d+)/ ){
        $start_counter = $1;
    } elsif( /^START/ ){
        # we now update the accounting information
        ($start_time) = /D=(\d+)/;
        s/D=(\d+)/S=$1/;
        $count = $end_counter - $start_counter;
        $elapsed = $time - $start_time;
        # you should put your make update record stuff here
        s/^START/END 't=$elapsed' 'p=$count' 's=$start_counter' 'q=$end_counter' 'D=$time'/;
        $out = $_ . "\n" . $out;
        $end_counter = $start_counter;
        $time = $start_time;
    }
}

# update the file and the database at this point
print STDERR "APPEND $out" if $debug;
print $acct_h $out;
