1#!@PERL@
2eval 'exec @PERL@ -S $0 ${1+"$@"}'
3    if $running_under_some_shell;
4            # this emulates #! processing on NIH machines.
5            # (remove #! line above if indigestible)
6
7
8# accounting.pl.in,v 5.5 2000/06/09 21:55:12 papowell Exp
9# LPRng based accounting script.
10#  Version Thu Apr 13 21:00:20 PDT 2000
11# LPRng 3.6.14
12#
13# stdin = /dev/null
14# stdout = accounting file
15# stderr = log file
16#  
17#  command line format:
18#   accounting [start|end] [-options] [accounting file]
19#     -Tdebug will turn on debugging
20#     start - at start of job; scan accounting file, fix up,
21#         put in START entry
22#     end  - at end of job; scan accounting file, fix up,
23#         put in END entry
24#
25# Accounting File has format:
26# start '-ppagecounter' '-Athis' -P'that'  <- startpage
27# ...
28# end   '-ppagecounter'                    <- lastpage
29# 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'
30#               --- end of a job
31# START 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'
32# start '-ppagecounter' '-Athis' -P'that'    <- startpage
33# ...
34# end   '-ppagecounter'                        <- lastpage
35# 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'
36#
37# We implement a stack based FSM to do the following:
38# Stack = NULL, state = FIND_START
39#   
40#  FIND_START - find the first START
41#   START -> push START line
42#         -> FIND_start
43#
44#  FIND_start - find the first 'start' entry with page
45#   start -> push start
46#         -> FIND_end
47#
48#  FIND_end   - find the first 'end' entry for this job
49#   end   -> push end line
50#         -> FOUND_end
51#   START -> push START line
52#         -> FIND_start
53#
54#  FOUND_end  - keep looking for 'end' entries
55#   end   -> pop stack
56#         -> push end line
57#   START -> push START line
58#         -> FIND_start
59#
60# All states:
61#
62#   END   -> clear stack
63#         -> FIND_START
64#   START -> push START line
65#         -> FIND_start
66#
67#
68#  At end, if you do not have state FOUND_end you do not have a
69#  valid accounting file so you have to wait.
70#  Stack will have contents:
71#      ((START*  start )* end) *
72#                ^ start page count
73#                         ^ end page count
74#   we start from the bottom of the stack;
75#    - end entry sets end page count for job
76#    - start entry sets start page count for job
77#    - START assigns pagecount as difference between start and end
78#            end page count = start page count
79#
80# seq   START START START start end
81# pages   0     0      end - start       
82#
83# seq   START START start1         START START start2 end2
84# pages   0     start2-start1       0      start2-end2
85#
86
87use strict;
88use Getopt::Std;
89use FileHandle;
90
91my($JFAIL, $JABORT, $JREMOVE, $JHOLD) = ( 32, 33, 34, 37);
92my(%opt, $action, $acct_h, $debug,$state,@stack);
93my($FIND_START,$FIND_start,$FIND_end,$FOUND_end)=(0,1,2,3,4,5);
94
95
96$debug = 0;
97
98# print STDERR "$0: '" . join("' '",@ARGV) . "'\n" if $debug;
99$action = "";
100if( @ARGV ){
101    if( $ARGV[0] !~ /^-/ ){
102        $action = shift @ARGV;
103    }
104    print STDERR "action $action\n" if $debug;
105}
106if( $action ne "start" and $action ne "end" ){
107    print STDERR "$0: invalid action '$action'\n";
108    exit $JABORT;
109}
110
111# pull out the options
112
113getopts( '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:'
114. '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 );
115
116my($acct_file) = "";
117if( @ARGV ){
118    $acct_file = shift @ARGV;
119} else {
120    $acct_file = $opt{'a'};
121}
122if( exists( $opt{T} ) && $opt{T} =~ m/debug/ ){
123    $debug = 1;
124}
125if( !$acct_file ){
126    print STDERR "$0: no accounting file\n";
127    exit( $JABORT );
128}
129
130my($time) = time;
131print STDERR "$0: $action 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'\n" if $debug;
132
133print STDERR "accounting file '$acct_file'\n" if $debug;
134
135if( $action eq "start" ){
136    $acct_h = new FileHandle ">>$acct_file" ;
137    print $acct_h "START 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'\n";
138    print STDERR "START 'A=$opt{A}' 'P=$opt{P}' 'n=$opt{n}' 'H=$opt{H}' 'D=$time'\n" if $debug;
139    $acct_h->close();
140    exit 0;
141}
142
143$acct_h = new FileHandle "+<$acct_file" ;
144if( ! defined($acct_h) ){
145    print STDERR "$0: cannot open $acct_file r/w - $!\n";
146    exit( $JABORT );
147}
148
149# now we read the last line from the accounting file
150my($size) = -s $acct_file;
151my($max_seek) = 2048;
152my($read_all) = 0;
153
154if( $size > $max_seek  && not $acct_h->seek( -$max_seek, 2 )
155){
156    print STDERR "$0: lseek $acct_file failed - $!\n";
157    exit( $JABORT );
158}
159
160again:
161
162@stack = ();
163$state = $FIND_START;
164
165while( <$acct_h> ){
166    chomp;
167    print STDERR "STATE '$state' LINE '$_'\n" if $debug;
168    print STDERR "STACK\n " . join("\n ",@stack) . "\n" if $debug;
169    if( /^END / ){
170        $state = $FIND_START;
171        @stack = ();
172        next;
173    }
174    if( /^START / ){
175        push @stack, $_;
176        $state = $FIND_start;
177        next;
178    }
179    if( $state == $FIND_start ){
180        if( /^(file)?start .*-p\d+/ ){
181            push @stack, $_;
182            $state = $FIND_end;
183        }
184    } elsif( $state == $FIND_end ){
185        if( /^(file)?end .*-p\d+/ ){
186            push @stack, $_;
187            $state = $FOUND_end;
188        }
189    } elsif( $state == $FOUND_end ){
190        if( /^(file)?end .*-p\d+/ ){
191            pop @stack;
192            push @stack, $_;
193            $state = $FOUND_end;
194        }
195    }
196}
197
198if( $state != $FOUND_end ){
199    if( $read_all ){
200        print STDERR "$0: did not find end record in file";
201        exit 0;
202    }
203    $acct_h->close();
204    $acct_h = new FileHandle "+<$acct_file" ;
205    if( ! defined($acct_h) ){
206        print STDERR "$0: cannot open $acct_file r/w - $!\n";
207        exit( $JABORT );
208    }
209    $read_all = 1;
210}
211
212print STDERR "FINAL STATE '$state'\n" if $debug;
213print STDERR "FINAL STACK\n " . join("\n ",@stack) . "\n" if $debug;
214my($end_counter,$start_counter,$start_time,$count,$out,$elapsed);
215
216$end_counter = $start_counter = 0;
217$out = "";
218
219for( my $i = @stack -1; $i >= 0 ; --$i ){
220    $_ = $stack[$i];
221    print STDERR "stack [$i] '$_'\n" if $debug;
222    if( /^[a-z]*end .*-p(\d+)/ ){
223        $end_counter = $1;
224    } elsif( /^[a-z]*start .*-p(\d+)/ ){
225        $start_counter = $1;
226    } elsif( /^START/ ){
227        # we now update the accounting information
228        ($start_time) = /D=(\d+)/;
229        s/D=(\d+)/S=$1/;
230        $count = $end_counter - $start_counter;
231        $elapsed = $time - $start_time;
232        # you should put your make update record stuff here
233        s/^START/END 't=$elapsed' 'p=$count' 's=$start_counter' 'q=$end_counter' 'D=$time'/;
234        $out = $_ . "\n" . $out;
235        $end_counter = $start_counter;
236        $time = $start_time;
237    }
238}
239
240# update the file and the database at this point
241print STDERR "APPEND $out" if $debug;
242print $acct_h $out;
243