#!/usr/bin/perl -w # # usage: dircp-mergeattr channel-file other-file > output-file # # Copyright (C) Andrew Francis, 2003. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of the author nor the names of any co-contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY Andrew Francis AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. # sub debugprint { # print $_[0] . "\n"; } #### simple wrappers to add / remove stuff from the nick list sub nickIndex { my ($nicklist, $nick) = @_; for(my $n = 0; $n < scalar @$nicklist; $n++) { if($nicklist->[$n] eq $nick) { return $n; } } return -1; } sub nickAdd { my ($nicklist, $nick) = @_; my $i = nickIndex($nicklist,$nick); if($i < 0) { push @$nicklist, $nick; debugprint "added $nick"; } } sub nickAddImplied { nickAdd(@_); } sub nickRemove { my ($nicklist, $nick) = @_; my $i = nickIndex($nicklist,$nick); if($i >= 0) { splice @$nicklist, $i, 1; debugprint "removed $nick"; } } sub nickExists { if(nickIndex(@_) >= 0) { return 1; } else { return 0; } } #### process a line from a channel, adding or removing nicks #### as necessary sub processChan { my ($nickref,$str) = @_; # first, try to catch people saying things if($str =~ /^<([^!>]+)![^>]+>/) { nickAddImplied($nickref, $1); return 1; } if($str =~ /^\[([^!>]+)![^>]+\] ACTION/) { nickAddImplied($nickref, $1); return 1; } # PART -- remove nick from channel. FIXME proper hostmask match if( $str =~ /^-[^- ]+- (\S+) \(.+\) left the channel/ ) { nickRemove($nickref, $1); return 1; } # JOIN -- add nick to the channel if( $str =~ /^-[^- ]+- (\S+) \(.+\) joined the channel/ ) { nickAdd($nickref, $1); return 1; } # more implied adds here for stuff like topic changes, etc # FIXME do later return 1; } #### much the same as processChan, but for global events sub processOther { my ($nickref,$str) = @_; # if someone quits, figure out if they're in the channel. if they are, # then we want to merge this line, then remove the link from the list if( $str =~ /^-[^- ]+- (\S+) \(.+\) quit from IRC: .*/ ) { if(nickExists($nickref,$1)) { nickRemove($nickref,$1); return 1; } else { return 0; } } # if someone changes nick, then check to see if the old nick was active # in the channel. if so, merge the line, and swap the new nick into the # active list if( $str =~ /^-[^- ]+- (\S+) \(.+\) changed nickname to (\S+)$/ ) { if(nickExists($nickref,$1)) { nickRemove($nickref,$1); nickAdd($nickref,$2); return 1; } else { return 0; } } # if we don't know what kind of line it is, it shouldn't matter. don't # merge it return 0; } ####################### nitty gritty file processing # january 2030 - this must be larger than any actual date we encounter $TIMESTAMP_FINISHED = 1896105600; # read a line from a logfile, and return the timestamp and the content # as a pair. if we can't read a line, return (TIMESTAMP_FINISHED,'') sub readLogLine { my $F = $_[0]; while(1) { if(my $ln = <$F>) { if( $ln =~ /^@([0-9]+) (.*)$/ ) { my $l = $ln; chomp $l; #debugprint $l; return ($1,$2); } else { # debugprint "invalid line: $ln"; } } else { # couldn't read a line from the file # debugprint "EOF!"; return ($TIMESTAMP_FINISHED, ""); } } } # print a log line in the format ($1,$2) sub printLogLine { print "\@$_[0] $_[1]\n"; } # open our files open CHANNEL, $ARGV[0] or die "couldn't open channel file, $ARGV[0]"; open OTHER, $ARGV[1] or die "couldn't open other file, $ARGV[1]"; # we keep track of an active nick list @nicks = (); my ($chan_time,$chan_str) = readLogLine(CHANNEL); my ($other_time,$other_str) = readLogLine(OTHER); # keep going while there's still lines to process while( ($chan_time != $TIMESTAMP_FINISHED) && ($other_time != $TIMESTAMP_FINISHED) ) { #print join ' ', @nicks; # whichever one has the earliest timestamp, process and read another one # as $TIMESTAMP_FINISHED is really high, if one is finished it will not # be processed if($chan_time < $other_time) { # possibly add or remove nicks processChan(\@nicks, $chan_str); # print this line printLogLine($chan_time,$chan_str); # fetch next ($chan_time,$chan_str) = readLogLine(CHANNEL); } else { # possibly add or remove nicks. returns true if we should # print this if(processOther(\@nicks, $other_str)) { printLogLine($other_time,$other_str); } ($other_time,$other_str) = readLogLine(OTHER); } } # all done! close CHANNEL; close OTHER;