#!/usr/bin/perl -w # 0; } # KWIC index. if ($options{'k'}) { $kwic = $options{'k'}; usage "no KWIC index supplied" unless length($kwic); } # Other logs. if ($options{'o'}) { $other = $options{'o'}; usage "no other-logs page supplied" unless length($other); } # Preformatted output stored in files. if ($options{'p'}) { $prefix = $options{'p'}; usage "no prefix supplied" unless length($prefix); } # Start date for output. if ($options{'s'}) { $sdate = $options{'s'}; usage "no start date supplied" unless length($sdate); $stime = str2time($sdate) || usage "$sdate: not valid"; } # End date for output. if ($options{'e'}) { $edate = $options{'e'}; usage "no end date supplied" unless length($edate); $etime = str2time($edate) || usage "$edate: not valid"; } manpage if $options{'m'}; myuuid if $options{'u'}; where if $options{'w'}; usage if $options{'h'}; $file = shift; usage "need a file" unless $file && length($file); usage "start date comes after end date" if $stime > $etime && $etime > 0; # # Read LOG-style file, print page content. # # Timestamp for a log entry looks like this: # Wed, 10 Mar 2004 20:40:26 # $day $dom $mon $yr $hms # # Unfortunately, we can't print anything until we read at least # the first chunk, or we could miss the desired title. # my ($day, $dom, $mon, $yr, $hms, $tz, $fullname, @x); my $en = 0; # entry number my $first = 1; # true if we've yet to print the first entry my %chunk; open($fh, "< $file") || die "$file: can't read: $!\n"; while (%chunk = getchunk($fh)) { $_ = $chunk{'str'}; # Title, if any. if ($chunk{'type'} eq 'begin') { $title = $_; if ($first) { $first = 0; prheader($title); pranchor("top"); otherpages(); cstart(); } } # If we have a timestamp, and entries have been written, # close the current entry row. Then open a new timestamp row. elsif ($chunk{'type'} eq 'time') { endentry() if $en > 0; ($day, $dom, $mon, $yr, $hms, $tz, @x) = split; $en++; $fullname = join(' ', @x); $fullname =~ s/\(.*//; if (defined($hms)) { prtime($day, $dom, $mon, $yr, $hms, $en, $fullname); } else { warn "$file, line $.: messed-up time entry?" } startentry(); } # Non-formatted portion of an entry. elsif ($chunk{'type'} eq 'entry') { # Handle inline images. s!\[Image:\s*(.*)\]!

$1

!g; print "

\n"; print "$_"; print "

\n"; } # Preformatted portion of an entry: remove last # newline to keep odd whitespace from bottom of listings. elsif ($chunk{'type'} eq 'pre') { chomp; print "
\n$_
\n"; } } close($fh); endentry(); cend(); # # Print page footer. # pranchor("bottom"); otherpages(); prfooter(); exit(0); #--------------------------------------------------------------------- # Print a usage message from the comments and exit. sub usage { my ($emsg) = @_; use Pod::Usage qw(pod2usage); warn "$emsg\n" if defined $emsg; pod2usage(-verbose => 99, -sections => "NAME|SYNOPSIS|OPTIONS"); } sub manpage { use Pod::Man(); my $parser = Pod::Man->new(); open(STDOUT, "| groff -T ascii -man | gcat -s | less") || die "groff\n"; $parser->parse_from_file($0); close STDOUT || die "$myname: can't close stdout: $!\n"; $? = 1 if $? == 255; # from die exit($?); } #--------------------------------------------------------------------- # Print the UUID, current version, or source location. sub myuuid { my $UUID = sprintf("%s", q$UUID: 488751a6-0460-38e7-9fc9-619c08b02c9e $ =~ /UUID: (.*) /); print "$UUID\n"; exit(0); } sub version { my $VERSION = sprintf("%d.%02d", q$Revision: 2.15 $ =~ /(\d+)\.(\d+)/); my $DATE = sprintf("%s", q$Date: 2009/10/09 17:30:41 $ =~ /Date: (.*) /); return "$myname $VERSION $DATE"; } sub where { my $SOURCE = sprintf("%s", /Source: (.*) /); print "$SOURCE\n"; exit(0); } #--------------------------------------------------------------------- # Print HTML header. sub prheader { my $title = shift || "Current logfile"; my $vers = version(); print <<"PageHeader"; $title

$title

PageHeader 1; } #--------------------------------------------------------------------- # Print HTML footer. sub prfooter { print <<"PageFooter";
PageFooter 1; } #--------------------------------------------------------------------- # Print internal anchor. sub pranchor { my ($name) = @_; die "no anchor name given" unless length($name); print " \n"; 1; } #--------------------------------------------------------------------- # Print pointers to other pages. sub otherpages { print << "OtherPages";
OtherPages 1; } #--------------------------------------------------------------------- # Start content. sub cstart { print <<"StartContent"; StartContent 1; } #--------------------------------------------------------------------- # End content. sub cend { print <<"EndContent";
EndContent 1; } #--------------------------------------------------------------------- # Left field is the timestamp plus entry pointers. # The first field written has the "previous" entry pointing to # the top, and the last one has the "next" entry pointing to # the bottom. sub prtime { my ($day, $dom, $mon, $yr, $hms, $en, $fullname) = @_; # $kn, $kp = next and previous numbers. # $sn, $sp, $sc = next, previous, and current name strings. my $fmt = "entry_%d"; my $kn = $en + 1; my $kp = $en - 1; my ($sn, $sp, $sc); if ($kp <= 0) { $sp = 'top'; } else { $sp = sprintf($fmt, $kp); } $sn = sprintf($fmt, $kn); $sc = sprintf($fmt, $en); $hms = substr($hms, 0, 5); print <<"EndLeft"; $day $dom $mon $yr $hms
$fullname

Top  Next  Prev  End EndLeft 1; } #--------------------------------------------------------------------- # Right field is the log entry. sub startentry { print <<"StartRight"; StartRight 1; } sub endentry { print <<"EndRight"; EndRight 1; } # ------------------------------------------------------------------------- # Read file in chunks. sub getchunk { my $fh = shift; my %result = (); # No more records. return (%result) if eof($fh); # Skip leading blank lines. my $pat = '^\s*$'; while (<$fh>) { last unless /$pat/; } # We've found a complete timestamp, or at least part of the # associated comment. A chunk can be either a paragraph # from the comment, or a complete piece of preformatted text. if (/^BEGIN/) { $result{'type'} = 'begin'; chomp; s/BEGINNING OF LOG FOR//; s/==*$//; $result{'str'} = $_; $pat = '^\s*$'; } elsif (/^\s+--+S$/) { $result{'type'} = 'pre'; $result{'str'} = ''; # don't keep this string. $pat = '^\s+--+E$'; } elsif (/^\s/) { $result{'type'} = 'entry'; $result{'str'} = $_; $pat = '^\s*$'; } else { $result{'type'} = 'time'; $result{'str'} = $_; $pat = '^\s*$'; } # Store strings until terminating pattern found. # Special strings like "root#", "you%", and "me%" indicate # commands where the entire line should be in bold. # Preformatted output can drop some leading spaces. my $emailhdr = 0; # Are we in a mail header? while (<$fh>) { last if /$pat/; if (length() > $maxpre) { warn "line $. too long:\n"; warn " [$_]\n"; } $_ = prep($_); # Emphasize typed commands. if (/root#|you\%|me\%|me\$|^\s*\$ /) { chomp; s!^!!; s!$!\n!; } # Emphasize email headers. if (/^\s*(date|message-id|from|sent|to|subject|reply-to|cc|org.*):/i) { $emailhdr = 1; } if ($_ eq "\n") { $emailhdr = 0; } if ($emailhdr) { chomp; s!^!!; s!$!\n!; } $result{'str'} .= $_; } return (%result); } # ------------------------------------------------------------------------- # Prepare string by fixing special characters. sub prep { local ($_) = shift; # Tabs? 1 while s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e; # Leading spaces? s/^\s\s\s\s//; # Special characters? s!\&!\&!g; s!!\>!g; # URLs? s#(http://\S+)#$1#g; s#(https://\S+)#$1#g; s#(ftp://\S+)#$1#g; return $_; } #--------------------------------------------------------------------- # To generate manpage: # pod2man -c 'User docs' -r "`date`" -d version site2html __END__ =head1 NAME site2html - Read LOG-style file in chunks, write HTML output =head1 SYNOPSIS site2html [-hmuvw] [-s start] [-e end] [-c max] [-p pfx] [-t title] file =head1 OPTIONS =over 4 =item B<-c> max Maximum number of lines in preformatted output before writing a "continued here..." link; default is unlimited. =item B<-h> Print a brief help message and exit. =item B<-k> kwic Page pointing to KWIC index. Defaults to "kwic.htm". =item B<-m> Print the manual page and exit. =item B<-o> otherlogs Page pointing to other sitelogs. Defaults to "../index.htm". =item B<-p> pfx Write preformatted data to files starting with "pfx". =item B<-s> start Date at which to start output; default is first entry. =item B<-e> end Date at which to end output; default is last entry. =item B<-t> title Sets the HTML title. =item B<-u> Print the script UUID and exit. =item B<-v> Print the version and exit. =item B<-w> Print the source location and exit. =back =head1 DESCRIPTION B will read a LOG file and print it in HTML with some decent navigation links and highlighting. =head1 AUTHOR Karl Vogel Sumaria Systems, Inc.