1package Onis::Parser::Persistent; 2 3=head1 Parser::Persistent 4 5This module provides routines used for ``statefull parsing'' or however 6you want to call what's going on. It is used to find an absolute time in 7the logfile and rewind the file or seek further, whichever is neccessary. 8 9=head1 Usage 10 11use Parser::Persistent qw#set_absolute_time add_relative_time get_state 12newfile %MONTHNAMES#; 13 14set_absolute_time ($year, $month, $day, $hour, $min, $sec); 15add_relative_time ($hour, $minute); 16get_state (); 17newfile (); 18 19=cut 20 21# This module was quite hard to write, so I guess it's hard to understand, 22# too. I'll try to explain as much as possible, but it twisted my mind 23# more than once since it actually worked. Good luck :) 24 25use strict; 26use warnings; 27 28use vars qw#%MONTHNAMES @MONTHNUMS#; 29 30use Exporter; 31use Time::Local; 32use Onis::Data::Persistent; 33 34@Onis::Parser::Persistent::EXPORT_OK = qw/set_absolute_time get_absolute_time add_relative_time get_state newfile %MONTHNAMES @MONTHNUMS/; 35@Onis::Parser::Persistent::ISA = ('Exporter'); 36 37%MONTHNAMES = 38( 39 Jan => 0, 40 Feb => 1, 41 Mar => 2, 42 Apr => 3, 43 May => 4, 44 Jun => 5, 45 Jul => 6, 46 Aug => 7, 47 Sep => 8, 48 Oct => 9, 49 Nov => 10, 50 Dec => 11 51); 52 53@MONTHNUMS = qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/; 54 55our $TimeNewest = Onis::Data::Persistent->new ('TimeNewest', 'inode', 'time'); 56our $AbsoluteTime = 0; 57our $TimeData = 58{ 59 Seeking => 1, 60 NeedsRewind => 1, 61 Duration => 0 62}; 63our $CurFile = 0; 64 65my $VERSION = '$Id: Persistent.pm,v 1.7 2004/01/07 20:31:17 octo Exp $'; 66print STDERR $/, __FILE__, ": $VERSION" if ($::DEBUG); 67 68return (1); 69 70=head1 Exported routines 71 72=head2 get_state (); 73 74This routine decides between four states: ``don't have time'', ``line is 75old'', ``parse this line'' and ``rewind file and begin again''. The last 76three imply that the time has been set. 77 78B<rewind file and begin again>: The parser should tell the main routine to 79rewind the file and start reading at the beginning again. This is 80neccessay if we went past the point where we left off during the last run. 81In this case zero is returned. 82 83B<parse this line>: If the parser should parse the line and call I<store> 84with the results this routine returns one. 85 86B<line is old>: If the parser should simply ignore this line and 87continue with the next one this routine returns three. 88 89B<don't have time>: No time is set. Ignore lines until a date is found. 90 91The desire is to pass the return code back to the main routine, unless it 92is equal to one. The parser should then return one upon success, two upon 93failure. 94 95=cut 96 97# Return values: 98# 0 == rewind file 99# 1 == line parsed 100# 2 == unable to parse 101# 3 == line old 102# 4 == don't have date 103sub get_state 104{ 105 my ($newest) = $TimeNewest->get ($CurFile); 106 $newest ||= 0; 107 108 # We're seeking for an absolute date. 109 if ($TimeData->{'Seeking'}) 110 { 111 # We're still seeking for a date.. 112 if (!$AbsoluteTime) 113 { 114 return (4); 115 } 116 117 # we're seeking past this date 118 elsif ($newest) 119 { 120 # We have a date and it's before the date we're seeking for. 121 # So we continue seeking.. 122 if ($AbsoluteTime <= $newest) 123 { 124 if ($::DEBUG & 0x40) 125 { 126 print STDERR $/, __FILE__, ": Absolute time found. Is earlier than the newest time. Disabling ``NeedsRewind''."; 127 } 128 $TimeData->{'NeedsRewind'} = 0; 129 130 # line old. ignore it 131 return (3); 132 } 133 134 # We went too far, so we have to go back. 135 # We substract the duration since the beginning 136 # of the file and tell the main routine to rewind 137 # the file. 138 elsif ($TimeData->{'NeedsRewind'}) 139 { 140 my $found; 141 my $set; 142 my $diff = $TimeData->{'Duration'}; 143 144 $found = localtime ($AbsoluteTime) if ($::DEBUG & 0x40); 145 146 $AbsoluteTime -= $diff; 147 148 if ($::DEBUG & 0x40) 149 { 150 $set = localtime ($AbsoluteTime); 151 print STDERR $/, __FILE__, ": Absolute time ``$found'' found. Setting back $diff seconds to ``$set''" if ($::DEBUG & 0x40); 152 } 153 154 $TimeData->{'NeedsRewind'} = 0; 155 delete ($TimeData->{'LastHourSeen'}); 156 delete ($TimeData->{'LastMinuteSeen'}); 157 158 # rewind file 159 return (0); 160 } 161 162 # This is the line we were looking for. 163 # It's past $newest, but not the first absolute time found. 164 else 165 { 166 print STDERR $/, __FILE__, ": Seeking done." if ($::DEBUG & 0x40); 167 $TimeData->{'Seeking'} = 0; 168 } 169 } 170 171 # $newest is not set but we have an absolute date. 172 else 173 { 174 print STDERR $/, __FILE__, ": \$newest not set. Setting it to \$AbsoluteTime." if ($::DEBUG & 0x40); 175 176 $TimeData->{'Seeking'} = 0; 177 178 # We had to read some lines to get an absolute date. 179 # Lets go back. 180 if ($TimeData->{'Duration'}) 181 { 182 my $diff = $TimeData->{'Duration'}; 183 if ($::DEBUG & 0x40) 184 { 185 my $time = localtime ($AbsoluteTime); 186 print STDERR $/, __FILE__, ": AbsolutTime found is ``$time'', but we are $diff seconds into the file."; 187 } 188 189 $AbsoluteTime -= $TimeData->{'Duration'}; 190 191 if ($::DEBUG & 0x40) 192 { 193 my $time = localtime ($AbsoluteTime); 194 print STDERR $/, __FILE__, ": Corrected AbsolutTime (set back $diff seconds) is ``$time''"; 195 } 196 197 delete ($TimeData->{'LastHourSeen'}); 198 delete ($TimeData->{'LastMinuteSeen'}); 199 200 # tell parser to rewind file 201 return (0); 202 } 203 204 # We didn't miss anything, so we don't need to rewind the file. 205 else 206 { 207 $newest = $AbsoluteTime; 208 $TimeNewest->put ($CurFile, $newest); 209 return (1); 210 } 211 } 212 } 213 214 # Ok, we're in the past. Let's skip that line.. 215 # This is NOT supposed to happen. If it does, it's a bug! 216 elsif ($AbsoluteTime < $newest) 217 { 218 my $now = localtime ($AbsoluteTime); 219 my $then = localtime ($newest); 220 print STDERR $/, __FILE__, ": Absolute time set, but we're in the past. Skipping. ($now < $then)" if ($::DEBUG & 0x40); 221 return (3); 222 } 223 224 # We're up to date. $TimeNewest needs to be set accordingly.. 225 elsif ($AbsoluteTime != $newest) 226 { 227 if ($::DEBUG & 0x40) 228 { 229 my $time = localtime ($AbsoluteTime); 230 print STDERR $/, __FILE__, ": Updating. Newest time is now ``$time''"; 231 } 232 233 $newest = $AbsoluteTime; 234 $TimeNewest->put ($CurFile, $newest); 235 } 236 237 return (1); 238} 239 240=head2 add_relative_time ($hour, $min); 241 242This routine does two different things, depending on wether or not the 243absolute time is known. 244 245If the absolute time is not known, it will add up the seconds since the 246start of the file. When we know the absolute time later we can subtract 247that value to get the absolute time of the beginning of the file. 248 249If the absolute time is known it simply adds to it to keep it up to date. 250 251=cut 252 253sub add_relative_time 254{ 255 my $this_hour = shift; 256 my $this_minute = shift; 257 258 my $this_seconds = ($this_hour * 3600) + ($this_minute * 60); 259 260 if ((defined ($TimeData->{'LastHourSeen'})) 261 and (defined ($TimeData->{'LastMinuteSeen'}))) 262 { 263 my $diff = 0; 264 my $last_hour = $TimeData->{'LastHourSeen'}; 265 my $last_minute = $TimeData->{'LastMinuteSeen'}; 266 my $last_seconds = ($last_hour * 3600) + ($last_minute * 60); 267 268 if ($last_seconds > $this_seconds) 269 { 270 $this_seconds += 86400; # one day 271 } 272 273 $diff = $this_seconds - $last_seconds; 274 275 if ($::DEBUG & 0x40) 276 { 277 print STDERR $/, __FILE__, ': '; 278 printf STDERR ("diff ('%02u:%02u', '%02u:%02u') = %u seconds", 279 $last_hour, $last_minute, 280 $this_hour, $this_minute, 281 $diff); 282 } 283 284 # FIXME needs testing! 285 if (!$AbsoluteTime) 286 { 287 $TimeData->{'Duration'} += $diff; 288 } 289 else 290 { 291 $AbsoluteTime += $diff; 292 } 293 } 294 295 $TimeData->{'LastHourSeen'} = $this_hour; 296 $TimeData->{'LastMinuteSeen'} = $this_minute; 297} 298 299=head2 set_absolute_time ($year, $month, $day, $hour, $min, $sec); 300 301As the name suggests this routine sets the absolute time. 302 303=cut 304 305sub set_absolute_time 306{ 307 my $year = shift; 308 my $month = shift; 309 my $day = shift; 310 my $hour = shift; 311 my $min = shift; 312 my $sec = shift; 313 314 $year -= 1900; 315 316 my $time = timelocal ($sec, $min, $hour, $day, $month, $year); 317 print STDERR $/, __FILE__, ": Set absolute time to ", scalar (localtime ($time)) if ($::DEBUG & 0x40); 318 319 # Add diff if this is the first 320 if (!$AbsoluteTime) 321 { 322 add_relative_time ($hour, $min); 323 } 324 else 325 { 326 # FIXME neccessary? 327 $TimeData->{'LastHourSeen'} = $hour; 328 $TimeData->{'LastMinuteSeen'} = $min; 329 } 330 331 $AbsoluteTime = $time; 332} 333 334sub get_absolute_time 335{ 336 return ($AbsoluteTime); 337} 338 339=head2 newfile (); 340 341Resets the internal counters to be ready for another file. 342 343=cut 344 345sub newfile 346{ 347 my $inode = shift; 348 349 my ($time) = $TimeNewest->get ($inode); 350 $time ||= 0; 351 $TimeNewest->put ($inode, $time); 352 353 $AbsoluteTime = 0; 354 $TimeData->{'Duration'} = 0; 355 $TimeData->{'NeedsRewind'} = 1; 356 $TimeData->{'Seeking'} = 1; 357 delete ($TimeData->{'LastHourSeen'}); 358 delete ($TimeData->{'LastMinuteSeen'}); 359} 360