#!/usr/bin/perl my $rcsid = '$Id: tip2html,v 1.1 2004/06/17 21:11:30 vogelke Exp $'; =head1 NAME tip2html =head1 SYNOPSIS tip2html [-m max] [-p pre] [-t title] file tip2html [-hv] =head1 DESCRIPTION Read tip file in "chunks", write HTML output. Tip files are similar to LOG files, but lines starting in column 1 are short section headers instead of timestamps. =head1 OPTIONS -h prints help and exits. -v prints the version and exits. -t title sets the HTML title. -m max maximum number of lines in preformatted output before writing a "continued here..." link; default is unlimited. -p pfx write preformatted data to files starting with "pfx". =head1 AUTHOR Karl Vogel Sumaria Systems, Inc. =cut # To generate manpage: # pod2man -c 'User docs' -r "`date`" -d version tip2html use strict; use warnings; use File::Basename; use Date::Parse; use Date::Format qw(time2str); use subs qw/cend cstart endentry getchunk pranchor prep prfooter prheader prsec otherpages startentry usage version/; $ENV{"PATH"} = "/bin:/usr/bin:/usr/local/bin"; my $myname = basename ($0, ".pl"); my $maxlines = 0; # max lines allowed before continuation link. my $prefix = ''; # prefix for storing preformatted output. my $file = ''; # input file. my $title = ''; # # Handle any arguments. # ARG: while (@ARGV) { $_ = shift @ARGV; next ARG unless length; # Title. /^-t/ and do { $title = shift @ARGV || ''; usage ("no title supplied") unless length ($title); next ARG; }; # Max lines in preformatted output. /^-m/ and do { $maxlines = shift @ARGV || ''; usage ("no max lines supplied") unless length ($maxlines); usage ("max lines must be a number") unless $maxlines =~ m/^\d+$/; $maxlines = 0 unless $maxlines > 0; next ARG; }; # Preformatted output stored in files. /^-p/ and do { $prefix = shift @ARGV || ''; usage ("no prefix supplied") unless length ($prefix); next ARG; }; # Version. /^-v/ and do { my $vers = version(); warn "$vers\n"; exit (0); }; # Bad option. /^-.*/ and do { usage ("unrecognized option: [$_]"); }; # Non-option argument. if (length ($file) == 0) { $file = "$_"; } else { usage ("extra arg: [$_]"); } } usage ("need a file") unless length ($file); open (F, "< $file") || die "$file: can't read: $!\n"; # # 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 # my ($section, @x); my $en = 0; # entry number my %chunk; while (%chunk = getchunk (*F)) { $_ = $chunk{'str'}; # BEGIN line. Print page header. if ($chunk{'type'} eq 'begin') { $title = $_; prheader(); 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 'section') { endentry() if $en > 0; $section = $_; $en++; prsec ($section, $en); startentry(); } # Non-formatted portion of an entry. elsif ($chunk{'type'} eq 'entry') { print "

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

\n"; } # Preformatted portion of an entry. elsif ($chunk{'type'} eq 'pre') { print "
\n";
        print "$_";
        print "     
\n"; } } close (F); endentry(); cend(); # # Print page footer. # pranchor("bottom"); otherpages(); prfooter(); exit (0); #--------------------------------------------------------------------- # Print a usage message from the comment header and exit. sub usage { my ($emsg) = @_; require Pod::Text; import Pod::Text; my $formatter = 'Pod::Text'; my $parser = $formatter->new (); warn "$emsg\n"; $parser->parse_from_file ($0); exit (1); } #--------------------------------------------------------------------- # Return the current version. sub version { $_ = $rcsid; s/,v / /; @_ = split; return "$_[1] v$_[2] $_[3] $_[4]"; } #--------------------------------------------------------------------- # Print HTML header. sub prheader { 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";
Home | Other Tips
OtherPages 1; } #--------------------------------------------------------------------- # Start content. sub cstart { print <<"StartContent";

StartContent 1; } #--------------------------------------------------------------------- # End content. sub cend { print <<"EndContent";
EndContent 1; } #--------------------------------------------------------------------- # Left field is the tip section 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 prsec { my ($section, $en) = @_; # $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); print <<"EndLeft"; $section


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'; if (/BEGINNING OF LOG FOR (.*) ==+/) { $result{'str'} = $1; } else { $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'} = 'section'; $result{'str'} = $_; $pat = '^\s*$'; } # Store strings until terminating pattern found. # Special strings like "root#" and "me%" indicate commands # where the entire line should be in bold. # Preformatted output can drop some leading spaces. while (<$fh>) { last if /$pat/; $_ = prep ($_); if (/\sroot#|\sme\%|\sme\$|^\s+\$ /) { 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 $_; }