#!/usr/bin/perl
#
# htmldoc-sql -- simple script to generate HTML tables describing SQL table
#                definitions. (bugs: expects cleanly formatted SQL as input)
#
# Copyright (c) Timo Kokkonen <tjko@iki.fi>  2001.
# $Id: htmldoc-sql,v 1.3 2003/01/11 20:18:52 tjko Exp $
#
require 5;
use Getopt::Long;

my ($PG_DIR,$PG_NAME) = ($0 =~ /^(.*\/)(.*)$/);
$0 = $PG_NAME;

sub error($) {
  my ($msg)=@_;
  print STDERR "$0: $msg\n";
}


$print_table_stack=0;


$result = GetOptions("help|h","verbose|v","title=s","docbook");
$verbose = ($opt_verbose ? 1 : 0);
$title = ($opt_title ? $opt_title : "SQL table descriptions");

if ($opt_help || @ARGV < 1) {
  print "syntax: $0 [--help] [--verbose] [--title] [--docbook] <filenames> ...\n\n";
  exit(0);
}



while ($filename=$ARGV[0]) {
  shift;
  unless (open(FILE,$filename)) {
    error("cannot open file: $filename");
    exit(1);
  }
  print STDERR "Processing file: $filename\n" if ($verbose);

  $state=0;
  while (<FILE>) {
    s/\n//g;
    next if /^\s*$/;
    s/\&/&amp;/g if ($opt_docbook);
    s/\</&lt;/g;
#    s/\>/&gt;/g unless ($opt_docbook);


    if ($state==2) {
      if (/\s*(.*)\*\//) {
	push @ol, ($opt_docbook ? "$1</entry></row>" : "$1</TD></TR>\n");
	$state=1;
      } else {
	push @ol, "$_<BR>";
	
      }
      if (/-->(\S+)\.(\S+)/) {
	print STDERR "LINK: $table.$field --> $1.$2\n" if ($verbose);
	$links{$1}=[] unless ($links{$1});
	push @{$links{$1}}, [$table,"$table.$field -> $1.$2"];
      }
      next;
    }

    if (/CREATE\s+TABLE\s+(\S+)\s+\((.*)$/) {
      #print "TABLE '$1' '$2'\n";
      push @tables, $1;
      $table=$1;
      print STDERR "TABLE $table\n" if ($verbose);
      $state=1;
      $_=$2;
      $idtag="\L$table";
      $idtag=~ s/[^a-z0-9\-]/\-/g;
      if ($opt_docbook) {
	push @ol, 
	   "<section id=\"tsec-$idtag\"><title>$table</title>\n",
	   "<para>$c_str</para>\n",
	   "<informaltable frame=\"all\">\n",
	   #"<title>$1</title>\n",
	   "<tgroup cols=3>",
	   "<colspec colname=c1>",
	   "<colspec colname=c2>",
	   "<colspec colname=c3>",
	   "<spanspec spanname=hspan1 namest=c2 nameend=c3 align=center>",
           "<thead><row>\n",
	   "<entry>field</entry><entry>type</entry><entry>comments</entry>\n",
	   "</row></thead><tbody>\n";
      } else {
	push @ol,
	  "<P><BR><BR><A name=\"$table\"><H3>Table: $table</H3></A>" .
	    "$c_str<P><TABLE border=1>" .
	      "<TR><TH>field</TH><TH>type</TH><TH>comments</TH></TR>\n";
      }
      $c_str='';
    }

    if (/^\s*\)/) {
      $state=0;
      push @ol, ($opt_docbook ?
		 "</tbody></tgroup>\n</informaltable></section>\n\n" :
		 "</TABLE>\n\n");
    }

    if ($state > 0) {
      if (/CONSTRAINT\s+(.*)$/) {
	push @ol, ($opt_docbook ? 
          "<row><entry>CONSTRAINT</entry><entry spanname=hspan1>" .
	   "$1</entry></row>\n" :
	  "<TR></TR><TR><TD>CONSTRAINT</TD><TD colspan=2>$1</TD></TR>\n");
      }
      elsif (/^\s*\/\*(.+)\*\/\s*$/) {
	push @ol, "<TR><TD></TD><TD></TD><TD>$1</TD></TR>\n";
      }
      elsif (/^\s*(\S+)\s+(\S.*?)(,?\s*(\/\*(.*)|))?$/) {
	#print STDERR "record: 1:'$1' 2:'$2'  4:'$4'\n";
	$field=$1;
	$field_type=$2;
	push @ol, "<TR><TD>$1</TD><TD>$field_type</TD>";
	$rest=$4;
	if ($rest eq '') {
	  push @ol, "<TD>&nbsp;</TD></TR>\n";
	}
	elsif ($rest=~/\/\*(.+)\*\//) {
	  push @ol, "<TD>$1</TD></TR>\n";
	} else {
	  $rest=~ s/\/\*//;
	  push @ol, "<TD>$rest<BR>";
	  $state=2;
	}
      }

    }
    else {
      if ($comment) {
	$c_str.=$_;
	if ($c_str =~ /^(.*)\*\*\//) {
	  $comment=0;
	  $c_str=$1;
	  $c_str =~ s/\s+/ /g;
	}
      } elsif (/^\s*\/\*\*\s*(.*)$/) {
	$comment=1;
	$c_str=$1;
	if ($c_str =~ /^(.*)\*\*\//) {
	  $c_str=$1;
	  $c_str =~ s/\s+/ /g;
	  $comment=0;
	}
      }
    }
  }
}


# print out the HTML (or DocBook SGML)...

if ($opt_docbook) {
  print "<section><title>$title</title><para>\n",
        "Tables:\n<itemizedlist>\n";
} else {
  print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n",
        "<HTML>\n<HEAD><TITLE>$title</TITLE></HEAD>\n<BODY>\n\n",
        "<!-- Generated by htmldoc-sql -- ",
        "Copyright (C) 2001 Timo Kokkonen -->\n";
  print "<H1>$title</H1><P>\n";
  print "<H2>Tables</H2>\n<UL>\n";
}

for $i (0..$#tables) {
  $t=$tables[$i];
  print_table($t,'') unless ($printed{$t});

}

if ($opt_docbook) {
  print "</itemizedlist>\n</para>\n",
        "<section><title>Table Descriptions</title>\n";
} else {
  print "</UL><BR>\n";
  print "<HR><H2>Table Descriptions</H2>\n";
}

for $i (0..$#ol) {
  $tmp=$ol[$i];
  if ($opt_docbook) {
    $tmp =~ s/\<TR\>/\<row\>/g;
    $tmp =~ s/\<\/TR\>/\<\/row\>/g;
    $tmp =~ s/\<TD\>/\<entry\>/g;
    $tmp =~ s/\<\/TD\>/\<\/entry\>/g;
    $tmp =~ s/\<BR\>//g;
  }
  print "$tmp";
}

$timenow=localtime(time);

if ($opt_docbook) {
  print "</section></section>\n\n";
} else {
  print "<P><HR>\nAutomagically generated by htmldoc-sql<BR>$timenow\n";
  print "</BODY></HTML>\n";
}

exit(0);

##################



sub print_table($$) {
  my($t,$info) = @_;
  my($j,$l,$foobar,$pad);

  $print_table_stack++;
  $pad=" " x $print_table_stack;

  print $pad;
  print ($opt_docbook ? "<listitem><para>" : "<LI>");

  $foobar = ($printed{$t} ? 1 : 0);

  if ($foobar > 0 && $opt_singlelinks) {
    print "$t";
  } else {
    unless ($opt_docbook) {  print "<A HREF=\"#$t\">$t</A>"; }
    else {
      $idtag="\L$t";
      $idtag=~ s/[^a-z0-9\-]/\-/g;
      print "<link linkend=\"tsec-$idtag\">$t</link>";
    }
  }

  if ($info ne '') {
    print " &nbsp;&nbsp;<FONT size=-1> " unless ($opt_docbook);
    print "($info)";
    print "</FONT>" unless ($opt_docbook);
  }
  print "</para>" if ($opt_docbook);

  $printed{$t}++;

  $l=$links{$t};
  if (@{$l} > 0 && $foobar==0) {
    print ($opt_docbook ? "\n$pad <itemizedlist>\n" : "<UL>\n");
    for $j (0..$#{$l}) {
      $t=$$l[$j][0];
      $info=$$l[$j][1];
      print_table($t,$info) unless ($foobar);
    }
    print ($opt_docbook ? "$pad </itemizedlist>" : "</UL>");
  }

  print $pad;
  print ($opt_docbook ? "</listitem>\n" : "</LI>\n");

  $print_table_stack--;
}
