#!/usr/bin/perl

# rem2html
#
# $Id: rem2html,v 1.13 2005/04/12 00:51:36 dfs Exp $
#
# A script to convert from the output of "remind -p" to Hyper-Text Markup
# Language (HTML), the text format used in WWW documents.  By default, it
# outputs a stand-alone file which can be fed directly into a web browser.
# The output uses nested <TABLE> blocks, so it will only work in a browser
# which supports tables (Netscape, MSIE, etc, NOT Lynx).
#
# This script works well on my computer (Linux 2.0.27) under Perl 5.003 and
# 5.004.  It should work fine on other unices but I have no idea whether
# it will run under VMS, OS/2, Windows, etc.
#
# [Note from David:  The REMIND license prohibits you from using REMIND
#  under Windows.]
#
# Rem2html puts "normal" CAL or MSG-type reminders in a <P></P> pair,
# and escapes any special HTML characters.
#
# If you want to put actual HTML code in the calendar (hyper-links, for
# example), use the "SPECIAL HTML" type.  For example:
#
# REM Wed SPECIAL HTML <P> Meeting at \
# <A HREF="http://www.linuxhq.com">Linux HQ</A> </P>
#
# This file is Copyright (C) 1997-8 by Don Schwarz <darkowl@mcs.net>
# and David F. Skoll

use Getopt::Long;

@months = (January,February,March,April,May,June,July,August,September,October,November,December);

 $DefaultImageDir = "%IMAGEBASE%";
if ($DefaultImageDir =~ m@/$@) {
    chop $DefaultImageDir;
}

$rem2html_version = "1.0";

&parse_options();

# Backgound color -- unfortunately, most Web browsers (as of 14 October 1999)
# do not correctly handle transparency in PNG images, so I had to make the
# moon image background white.
$Options{'bgcolor'} ||= "BGCOLOR=\"#FFFFFF\"";

if (-t STDIN) {
    print STDERR "(Rem2HTML: Input should not come from a terminal.)\n";
    $Options{'help'} = 1;
}

if ($Options{'help'}) {
  &show_usage();
} elsif ($Options{'version'}) {
  print "Rem2HTML Version $rem2html_version.\n";
} else {
  $successes = 0;
  while(1) {  
    last if (!parse_input());
    $successes++;
    &output_header();
    &output_data();
    &output_footer();
  }
  print STDERR "Rem2HTML: Couldn't find any calendar data.\n" if (!$successes);
}

exit(0);

sub show_usage {
  print STDERR <<EndOfUsage;
Rem2HTML: Produce a HTML calendar from the output of Remind.

Usage: rem2html [options]

Options:

--help, -h          Print this information
--version           Version information
-p file             Prepend the specified HTML file to the beginning of the
                    output
-a file             Append the specified HTML file to the end of the output
-f[shted] font      Set font for small cal, hdr, title, cal entries,day numbers
-s[hted] size       Set size for header, title, calendar entries and/or day
                    numbers
--backurl url       Make the title on the previous month's small calendar entry
                    a hyperlink to <url>
--forwurl url       Same as --backurl, but with the next month's small calendar
--tableonly         Output the results as a <TABLE> block only, no <HTML>, etc.
--border,-b size    Set the border thickness of the table
--cellspace,-t size Set the line thickness of the table
--bgcolor,-g color  Set the background color for the day entries

EndOfUsage

}

sub escape_html {
    my($in) = @_;
    $in =~ s/\&/\&amp;/g;
    $in =~ s/\</\&lt;/g;
    $in =~ s/\>/\&gt;/g;
    return $in;
}

sub parse_options {
  %Options = ();
  
  GetOptions (\%Options, "help|h",
                         "version",
                         "border|b=i",
                         "cellspace|t=i",
                         "backurl|bu:s", "forwurl|fu:s",
                         "tableonly|to",
                         "prologue|p=s",
	                 "append|a=s",
                         "bgcolor|g=s",
                         "fs=s", "fh=s", "ft=s", "fe=s", "fd=s",
                                 "sh=i", "st=i", "se=i", "sd=i",
             );

   $Options{'border'} = "BORDER=" . ($Options{'border'} || 1);
   $Options{'cellspace'} &&= "CELLSPACING=$Options{'cellspace'}";
   $Options{'bgcolor'} &&= "BGCOLOR=$Options{'bgcolor'}";
}

sub parse_input {
  local $where = 0;
  local $msg;
  local $type;
  local $day;
  @days = ();
  @shades = ();
  @moons = ();
  while (<>) {
    chomp($_);
    if (/rem2(html|ps) begin/) {
    } elsif (!$where) {
      next;
    } elsif ($where == 1) {
      local ($month, $year);
      ($month, $year, $month_length, $firstday, $mfirst) = split(" ");
      $caption = "$month, $year";
      for $i ( 1 .. $month_length) { push(@days, ""); }
    } elsif ($where == 2) {
      @DayNames = split(" ");
    } elsif ($where == 3) {
      @prevsc = split(" ");
    } elsif ($where == 4) {
      @nextsc = split(" ");
    } else {
      return 1 if /rem2(html|ps) end/;
      next if /^\#/;
      next unless m/^(\d*).(\d*).(\d*)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*)$/;
      $type = $4;
      $msg = $8;
      $day = $3;
      if ($type eq "HTML") {
	  $days[$day] .= "$msg ";
      } elsif ($type eq "MOON") {
	  my($phase, $text);
	  if ($msg =~ /^\s*(\d+)\s+\S+\s+\S+\s+(.*)$/) {
	      $phase = $1;
	      $text = $2;
	  } elsif ($msg =~ /^\s*(\d+)/) {
	      $phase = $1;
	      $text = "";
	  } else {
	      next;
	  }
	  next if ($phase > 3);
	  if ($phase == 0) {
	      $text = "<P ALIGN=RIGHT><IMAGE SRC=\"$DefaultImageDir/newmoon.png\" ALT=\"New Moon\"" .
		  "WIDTH=16 HEIGHT=16> <FONT SIZE=\"-2\">" . escape_html($text);
	  } elsif ($phase == 1) {
	      $text = "<P ALIGN=RIGHT><IMAGE SRC=\"$DefaultImageDir/firstquarter.png\" ALT=\"First Quarter\"" .
		  "WIDTH=16 HEIGHT=16> <FONT SIZE=\"-2\">" . escape_html($text);
	  } elsif ($phase == 2) {
	      $text = "<P ALIGN=RIGHT><IMAGE SRC=\"$DefaultImageDir/fullmoon.png\" ALT=\"Full Moon\"" .
		  "WIDTH=16 HEIGHT=16> <FONT SIZE=\"-2\">" . escape_html($text);
	  } else {
	      $text = "<P ALIGN=RIGHT><IMAGE SRC=\"$DefaultImageDir/lastquarter.png\" ALT=\"Last Quarter\"" .
		  "WIDTH=16 HEIGHT=16> <FONT SIZE=\"-2\">" . escape_html($text);
	  }
	  $moons[$day] = $text . "</FONT> ";
      } elsif ($type eq "SHADE") {
	  my($red, $green, $blue);
	  if ($msg =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s*$/) {
	      $red = $1;
	      $green = $2;
	      $blue = $3;
	  } elsif ($msg =~ /^\s*(\d+)\s*$/) {
	      $red = $1;
	      $green = $1;
	      $blue = $1;
	  } else {
	      next;
	  }
	  next if ($red > 255 || $green > 255 || $blue > 255);
	  $shades[$day] = sprintf(" BGCOLOR=\"#%02X%02X%02X\"",
				  $red, $green, $blue);
      } elsif ($type eq "COLOR") {
	  my($red, $green, $blue, $stuff);
	  if ($msg =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s*(.*)$/) {
	      $red = $1;
	      $green = $2;
	      $blue = $3;
	      $stuff = $4;
	      next if ($red > 255 || $green > 255 || $blue > 255);
	      $days[$day] .=
		  sprintf("<P><font color=\"#%02x%02x%02x\">",
			  $red, $green, $blue) .
			  escape_html($stuff) . "</font></P>";
	  } else {
	      next;
	  }
      } elsif ($type eq "*") {
	  $msg = &escape_html($msg);
	  $days[$day] .= "<P>$msg</P>";
      }
    }
    $where++;
  }
  if ($where) {
    return 1;
  }
  return 0;      
}

sub output_header {
  local ($title, $dayheader);

  if (!$Options{'tableonly'}) {
    print <<EndOfHTML;
<HTML>
 <HEAD><TITLE>Reminders for $caption</TITLE></HEAD>
 <BODY>
EndOfHTML
  }

  print <<EndOfHTML;
<!-- This output was produced by Rem2HTML $rem2html_version (written by
Don Schwarz <darkowl\@mcs.net>) and Remind (written by David F. Skoll).  -->
EndOfHTML

  if ($Options{'prologue'}) {
    open(PROLOGUE, "< $Options{'prologue'}");
    print while ( <PROLOGUE> );
    close(PROLOGUE);
  }

  $caption = &format_font($caption, $Options{'ft'}, $Options{'st'} || "+1");

  print <<EndOfHTML;
<TABLE $Options{'border'} $Options{'cellspace'} $Options{'bgcolor'} WIDTH=100%>
 <CAPTION><STRONG>$caption</STRONG></CAPTION>
 <TR>
EndOfHTML

  $mfirst || &print_day_header($DayNames[0]);
  
  for($i=1; $i<7; $i++) {
    &print_day_header($DayNames[$i]);
  }

  $mfirst && &print_day_header($DayNames[0]);
  print " </TR>\n";
}

sub output_footer {
  print "</TABLE>\n";

  if ($Options{'append'}) {
    open(EPILOGUE, "< $Options{'append'}");
    print while ( <EPILOGUE> );
    close(EPILOGUE);
  }

  unless ($Options{'tableonly'}) {
    print <<EndOfHTML;
 </BODY>
</HTML>
EndOfHTML
  }
}

sub output_data {
  local ($endday, $prevday, $nextday, $week, $weekday);
  local ($element, $day, $msg, $fday);
  $firstday -= $mfirst;
  if ($firstday < 0) { $firstday += 7; }
  $endday = $firstday + $month_length;
  $endweek = $endday + (6 - ($endday % 7));
  $endday %= 7;

  if ( $firstday > 1 ) {
    $prevday = 0;
    $nextday = 1;
  } elsif ($endday ? ($endday < 5) : !$firstday) {
    $prevday = $endweek - 1;
    $nextday = $endweek;
  } else {
    $prevday = 0;
    $nextday = $endweek;
  }

  for $week ( 0..5 ) {
    print " <TR>\n";
    for $weekday ( 0..6 ) {
      $element = ($week * 7) + ($weekday * 1);
      $day = $element - $firstday + 1;
      $msg = $days[$day];
      $msg = $msg ? &format_font($msg, $Options{'fe'}, $Options{'se'})
                  : " <BR> <BR> <BR> <BR>";

      $fday = &format_font($day, $Options{'fd'}, $Options{'sd'} || -1);
      if ($day > 0 && $day <= $month_length) {

        print <<EndOfHTML;
  <TD HEIGHT="120" VALIGN=TOP WIDTH=14%$shades[$day]>
   <P ALIGN=RIGHT>$moons[$day]$fday</P>
   $msg
  </TD>
EndOfHTML

      } elsif ($element == $prevday) {
        &small_calendar(@prevsc, 1, $Options{'backurl'});
      } elsif ($element == $nextday) {
        &small_calendar(@nextsc, 2, $Options{'forwurl'});
      } else {
        print "  <TD WIDTH=14%><BR></TD>";
      }
    }
    print " </TR>\n";
    last if $day >= $month_length && $element >= $nextday;
  }
}

sub small_calendar {
  local ($scname, $scn, $which, $url) = @_;
  local ($scstart, $l, $week, $weekday, $tday);
  
  $scname = "<A HREF=\"$url\">$scname</A>", if $url;
  $scname = &format_font($scname, $Options{'fs'}, -2);
  
  if ($which == 1) {
    $scstart = $firstday - ($scn % 7);
    if ($scstart < 0) { $scstart += 7; }
  } else {
    $scstart = $firstday + ($month_length % 7);
    if ($scstart > 6) { $scstart -= 7; }
  }

  print <<EndOfHTML;
  <TD WIDTH=14% VALIGN=TOP>
<TABLE WIDTH=100%>
 <CAPTION><STRONG>$scname</STRONG></CAPTION>
 <TR>
EndOfHTML

  $mfirst || &print_day_header(substr($DayNames[0], 0, 1), 1);
  
  for ($i=1; $i<7; $i++) {
    &print_day_header(substr($DayNames[$i], 0, 1), 1);
  }

  $mfirst && &print_day_header(substr($DayNames[0], 0, 1), 1);

  print "</TR>\n";

  for $week ( 0..5 ) {
    print " <TR>\n";
    for $weekday ( 0..6 ) {
      $tday = ($week * 7) + ($weekday * 1) - $scstart + 1;
      $tday = "", if $tday <= 0 || $tday > $scn;
      print "  <TD WIDTH=14%><FONT SIZE=-2>$tday</FONT></TD>\n";
    }
    print " </TR>\n";
    last if $tday >= $scn;
  }

  print <<EndOfHTML;
</TABLE>
  </TD>
EndOfHTML

}

sub format_font {
  local ($text, $font, $size) = @_;

  if (!$text) {
    return "";
  } elsif ($font && $size) {
    return "<FONT FACE=$font SIZE=$size>$text</FONT>";
  } elsif ($font) {
    return "<FONT FACE=$font>$text</FONT>";
  } elsif ($size) {
    return "<FONT SIZE=$size>$text</FONT>";
  } else {
    return $text;
  }
}

sub print_day_header {
  local ($dheader, $small) = @_;

  if ($small) {
    $dheader = &format_font($dheader, $Options{'fs'}, -2);
  } else {
    $dheader = &format_font($dheader, $Options{'fh'}, $Options{'sh'});
  }

  print "  <TH WIDTH=14%>$dheader</TH>\n";
}
