#!/usr/bin/perl
#
# Generate a set of HTML files to index all files and functions in the code.
#

$srcdir = $ARGV[0];

$indexfile = "index.html";
$todofile =  "todo.html";
$filesfile = "files.html";
$funcsfile = "funcs.html";

print "Generating HTML code index\n";

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


# Current time
#
$now_string = localtime;


# cfilelist = list of .c files
#
open (FILE, "find $srcdir -type f -name '*.c'|");
@cfilelist = <FILE>;
close (FILE);


# hfilelist = list of .h files
#
open (FILE, "find $srcdir -type f -name '*.h'|");
@hfilelist = <FILE>;
close (FILE);


# filelist = list of .c and .h files
#
@filelist = sort (@cfilelist, @hfilelist);


# allfiles = list of files that have been scanned
#
@allfiles = ();


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


SCANFILE: foreach $file (@filelist) {	# read each file in turn

  $file =~ s/\n//;			# remove \n from filename

  open (FILE, $file);			# slurp file into memory
  @contents = <FILE>;
  close (FILE);

  next SCANFILE if ($contents[0] =~ /^\/\*!NOINDEX/);	# ignore file

  $header = 0;
  if ($file =~ /\.h$/) {$header = 1;}	# check whether it's a header file
  $file =~ s,^$srcdir/,,;		# remove "src/" prefix from filename

  push (@allfiles, $file);

  $lnum = 0;
  $in_header = 1;
  $in_comment = 0;
  @commentblock = ();

  foreach $line (@contents) {	# scan through lines of file

    $lnum ++;

    if ($line =~ /\/\*/) {		# comment opening
      $in_comment = 1;
      @commentblock = ();
    }

    if ($in_comment == 1) {		# inside comment
      $comment = $line;
      $comment =~ s/^.*\/\*/  /;	# remove preceding "/*"
      $comment =~ s/\*\/[ \t]*$//;	# and succeeding "*/"
      $comment =~ s/^ \*/  /;		# and preceding " *"

      $comment =~ s/&/&amp;/g;		# make safe for HTML
      $comment =~ s/</&lt;/g;		#
      $comment =~ s/>/&gt;/g;		#

      $comment =~ s/^ {1,3}//;		# strip first couple of spaces

      $comment =~ s/^ *$/<p>/;		# stick paragraph breaks in

      if ((length $comment) < 55) {	# add <br> to short lines
        $comment =~ s/$/<br>/;
        $comment =~ s/<p><br>/<p>/;
      }

      if ($comment =~ /^ /) {		# add <br> to indented text
        $comment =~ s/^ /&#160;/;
        $comment =~ s/^ /&#160;/;
        $comment =~ s/^ /&#160;/;
        $comment =~ s/^ /&#160;/;
        $comment =~ s/$/<br>/;
      }
				# make "mailto:" links from email addresses
      $comment =~ s|\b([A-Za-z0-9.]+@[A-Za-z0-9.]+[A-Za-z0-9])|<a href="mailto:$1">$1</a>|g;

				# make links from foo://bar.baz/grok/
      $comment =~ s|\b(\w+)://([0-9A-Za-z./?+%-_~]+)|<a href="$1://$2">$1://$2</a>|g;

				# put function references inside <code> tags
      $comment =~ s|\b(\w+)(\([^)]*\))|<code class="funcname">$1$2</code>|g;

      if ($comment =~ /^ *TODO:/) {	# TODO entry
        $todocomment = $comment;
        $todocomment =~ s/^ *TODO: *//;
        if ($in_header == 1) {			# header entries {file 0 line}
          $todo {"$file 0 $lnum"} = $todocomment;
        } else {
          $todo {"$file $lnum"} = $todocomment;	# other entries {file line}
        }
        $comment =~ s/^( *TODO: *)/$1<span class="todo">/;
        $comment =~ s/<br>//;
        $comment =~ s/$/<\/span><br>/;
      }

      $comment =~ s/<br>(<br>)+/<br>/;	# strip multiple <br>s

      push @commentblock, $comment;	# store the comment inside a block

    }	# end of "if inside comment" block

    if ($line =~ /\*\//) {		# comment closing
      if ($in_header == 1) {
        $filecomment{$file} = "$#allcomments $#commentblock";
        $commentblock[0] =~ s/^[^-]* -//;
        $commentblock[0] =~ s/<br>//;
        push (@allcomments, @commentblock);
        $in_header = 0;
      }
      $in_comment = 0;
    }

    if ($header == 1) {			# check for prototypes in .h files
      if (($in_comment == 0) && ($line =~ /^[^ \t#].*\(/) &&
          ($line =~ /\) *;/)) {
        $funcprot = $line;
        $funcprot =~ s/ *;//;
        $funcprot =~ /\W*(\w+)\W*\(/;
        $funcname = $1;
        if (!exists $functions{$file}) { $functions{$file} = ""; }
        @funcs = split (/:/, $functions{$file});
        $functions{$file} = join (':', @funcs, $funcname);
      }
    } else {				# check for functions in .c files
      if (($in_comment == 0) && ($line =~ /^[^ \t#].*\(/) &&
          ($line =~ /\)[ \t{]*$/) && ($#commentblock > 0)) {
        $funcdef = $line;
        $funcdef =~ s/ *{//;
        $funcdef =~ /\W*(\w+)\W*\(/;
        $funcname = $1;
        if (!exists $functions{$file}) { $functions{$file} = ""; }
        @funcs = split (/:/, $functions{$file});
        $functions{$file} = join (':', @funcs, $funcname);
        $funcfile{$funcname} = $file;
        chomp $funcdef;
        $funcdef{"$funcname-$file"} = $funcdef;
        $funccomment{"$funcname-$file"} = "$#allcomments $#commentblock";
        push (@allcomments, @commentblock);
        @commentblock = ();
      }
    }

  }	# end of foreach $line loop

}	# end of foreach $file loop


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


# Write the index of TODOs
#
open (FILE, ">$todofile");

print FILE "<html><head>\n<title>TODO list</title>\n";
print FILE "<link rev=\"made\" href=\"mailto:mconv\@ivarch.com\">\n";
print FILE "</head><body>\n<h1>TODO list</h1>\n<p align=\"right\">\n";
print FILE "<small>[<a href=\"#1\">Bottom of page</a>]</small>\n";
print FILE "<p align=\"left\">\n";
print FILE "<a name=\"0\">Things to do, as of $now_string:</a>\n<p>\n";

$curfile = "";
$firstfile = 1;

foreach $key (sort {
  ($file1, $line1, $dummy1) = split (' ', $a);
  ($file2, $line2, $dummy2) = split (' ', $b);
  $fileeq = $file1 cmp $file2;
  if ($fileeq != 0) {return $fileeq;}
  if ($line1 == 0) {$line1 = $dummy1;}
  if ($line2 == 0) {$line2 = $dummy2;}
  $line1 <=> $line2
} (keys %todo)) {
  ($file, $line, $dummy) = split (' ', $key);
  $dummy = "";
  if ($file ne $curfile) {
    $curfile = $file;
    if ($firstfile == 0) {
      print FILE "</ul></ul>\n";
    } else {
      $firstfile = 0;
    }
    print FILE "\n<ul><li><a name=\"$file\"></a>";
    print FILE "<a href=\"$filesfile#$file\">$file</a>\n<ul>\n";
  }
  if ($line == 0) {
    print FILE "<li>$todo{$key}";
  } else {
    print FILE "<li>[<b>$line</b>] $todo{$key}";
  }
}

if ($firstfile == 0) {
  print FILE "</ul></ul>\n";
}

print FILE "<p>\n<hr width=\"100%\">\n<p align=\"right\">\n";
print FILE "<small class=\"navbar\">\n";
print FILE "<a name=\"1\">[</a> <a href=\"#0\">Top</a>\n";
print FILE "| <a href=\"$filesfile\">Files</a>\n";
print FILE "| <a href=\"$funcsfile\">Functions</a>\n";
print FILE "]</small>\n<p align=\"left\">\n";

print FILE "</body></html>";
close (FILE);


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


# Write the index of files
#
open (FILE, ">$filesfile");

print FILE "<html><head>\n<title>Source file index</title>\n";
print FILE "<link rev=\"made\" href=\"mailto:mconv\@ivarch.com\">\n";
print FILE "</head><body>\n<h1>Source file index</h1>\n";
print FILE "<p align=\"left\">\n";
print FILE "<a name=\"0\">File index, as of $now_string:</a>\n<p>\n";
print FILE "<ul>\n";

foreach $file (@allfiles) {
  print FILE "<li><a href=\"#$file\">$file</a>\n";
}

print FILE "</ul>\n<p>\n";
print FILE "<hr width=\"100%\">\n<p>\n";

FILEINDEXLOOP: foreach $file (@allfiles) {

  chomp $file;
  $file =~ s,^$srcdir/,,;

  next FILEINDEXLOOP if (!exists $filecomment{$file}); 

  ($start, $length) = split (' ', $filecomment{$file});
  $start ++;

  print FILE "<table border=\"0\">\n<tr>\n<td valign=\"top\">";
  print FILE "<code class=\"filename\"><a name=\"$file\">$file</a></code>";
  print FILE "</td>\n<td valign=\"top\"> - </td>\n<td valign=\"top\">";
  print FILE "$allcomments[$start]</td>\n</tr></table>\n<p>\n";
  print FILE "<small>[<a href=\"$srcdir/$file\">View File</a>]</small>\n";
  print FILE "<p>\n<blockquote>\n";

  $start ++;
  for ($i = 0; $i < $length; $i ++) {
    print FILE $allcomments[$start];
    $start ++;
  }

  print FILE "</blockquote>\n";

  if (exists $functions{$file}) {
    %funclist = ();
    foreach $item (split (/:/, $functions{$file})) { $funclist{$item} = 1; }

    if ($file =~ /\.h$/) {		# treat header files differently
      print FILE "Functions prototyped:\n<p>\n<ul>\n";
      foreach $key (sort (keys %funclist)) {
        if (exists $funcfile{$key}) {
          print FILE "<li>";
          $fnam = $funcfile{$key};
          print FILE "<a href=\"$funcsfile#$key-$fnam\">$key</a>\n";
        }
      }
    } else {
      print FILE "Functions defined:\n<p>\n<ul>\n";
      foreach $key (sort (keys %funclist)) {
        print FILE "<li><a href=\"$funcsfile#$key-$file\">$key</a>\n";
      }
    }
    print FILE "</ul>\n";
  }

  print FILE "<p align=\"right\"><small class=\"navbar\">[\n";
  print FILE "<a href=\"#0\">Top</a> |\n";
  print FILE "<a href=\"$todofile#$file\">To Do</a> |\n";
  print FILE "<a href=\"$funcsfile\">Functions</a> ]</small>\n";
  print FILE "<hr width=\"100%\">\n<p align=\"left\">\n";

}

print FILE "</body></html>";
close (FILE);


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


# Write the index of functions
#
open (FILE, ">$funcsfile");

print FILE "<html><head>\n<title>Function index</title>\n";
print FILE "<link rev=\"made\" href=\"mailto:mconv\@ivarch.com\">\n";
print FILE "</head><body>\n<h1>Function index</h1>\n";
print FILE "<p align=\"left\">\n";
print FILE "<a name=\"0\">Function index, as of $now_string:</a>\n<p>\n";
print FILE "<ul>\n";

foreach $key (sort (keys %funcdef)) {
  ($funcname, $file) = split (/-/, $key);
  print FILE "<li><code class=\"funcname\"><a href=\"#$key\">$funcname</a>";
  print FILE "</code> [<code class=\"filename\">$file</code>]\n";
}

print FILE "</ul>\n<p>\n";
print FILE "<hr width=\"100%\">\n<p>\n";

foreach $key (sort (keys %funcdef)) {

  ($funcname, $file) = split (/-/, $key);

  ($start, $length) = split (' ', $funccomment{$key});
  $start ++;

  print FILE "<code class=\"funcname\"><a name=\"$key\">$funcname</a></code>";
  print FILE " [<code class=\"filename\">";
  print FILE "<a href=\"$filesfile#$file\">$file</a></code>]\n<p>\n";
  print FILE "<code class=\"funcdef\">&#160;$funcdef{$key}</code>\n<p>\n";
  print FILE "<blockquote>\n";

  for ($i = 0; $i <= $length; $i ++) {
    print FILE $allcomments[$start];
    $start ++;
  }

  print FILE "</blockquote>\n";
  print FILE "<p align=\"right\"><small class=\"navbar\">[\n";
  print FILE "<a href=\"#0\">Top</a> |\n";
  print FILE "<a href=\"$todofile#$key\">To Do</a> |\n";
  print FILE "<a href=\"$filesfile\">Files</a> ]</small>\n";
  print FILE "<hr width=\"100%\">\n<p align=\"left\">\n";
}

print FILE "</body></html>";
close (FILE);


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


# Write the main index
#
open (FILE, ">$indexfile");
print FILE << "EOF"
<html><head>
 <title>Source Documentation Index</title>
 <link rev="made" href="mailto:mconv\@ivarch.com">
</head>
<body>
<h1><a name="index">Source Documentation Index</a></h1>

For developers only. Remember to type `<code>make index</code>' to regenerate
these files.
<p>
<ul>
<li><a href="$todofile">TODO list</a>
<li><a href="$filesfile">File index</a>
<li><a href="$funcsfile">Function index</a>
</ul>
<p>
</body></html>
EOF
;
close (FILE);

# EOF
