#!/usr/local/bin/perl

# Copyright (c) 2004-2005, Jan Stocker
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 
#     * Redistributions of source code must retain the above copyright notice,
#       this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of Jan Stocker nor the names of its contributors may
#       be used to endorse or promote products derived from this software
#       without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.


use strict;

use Getopt::Long;
use File::Find;
use File::Copy;

my $source       = '';
my $dest         = '';
my $framework    = ''; 
my $excludedirs  = '';
my $excludefiles = '';
my $htmlfiles    = '*.html;*.htm';
my $verbose      = 0;
my $copyfiles    = 1;
my $processfiles = 1;

my $dirmode  = '0775';
my $filemode = '0664';


#Get commandline options
if (!GetOptions(
            'dirmode|dm=s' => \$dirmode,
            'filemode|fm=s' => \$filemode,
            'excludedirs|ed=s' => \$excludedirs,
            'excludefiles|ef=s' => \$excludefiles,
            'htmlfiles|f=s' => \$htmlfiles,
            'copy!' => \$copyfiles,
            'process!' => \$processfiles,
            'verbose|v+' => \$verbose)
   )
{
    usage();
    exit;
}

$dirmode = oct($dirmode);
$filemode = oct($filemode);

if ($excludedirs)
{
    $excludedirs ="/$excludedirs/";
    $excludedirs =~s/\?/\[^\/\]/g;
    $excludedirs =~s/\*/\[^\/\]\*/g;
    $excludedirs =~s/;/\/|\//g;
}

if ($excludefiles)
{
    $excludefiles ="/$excludefiles\$";
    $excludefiles =~s/\?/\[^\/\]/g;
    $excludefiles =~s/\*/\[^\/\]\*/g;
    $excludefiles =~s/;/\$|\//g;
}

if ($htmlfiles)
{
    $htmlfiles ="^$htmlfiles\$";
    $htmlfiles =~s/\?/\[^\/\]/g;
    $htmlfiles =~s/\*/\[^\/\]\*/g;
    $htmlfiles =~s/;/\$|\//g;
}

if ($#ARGV != 1)
{
    usage();
    exit;
}

$source = File::Spec->rel2abs($ARGV[0]);
$dest   = File::Spec->rel2abs($ARGV[1]);

my $fw_use  = '';
my %fw_vars = {};
my $context = 'global';

reversemkdir($dest);
#process all files/dirs
finddepth(\&processfiles, $source);


#Print usage
sub usage
{
    print <<EOB
Usage: createsite [options] <source> <dest> 

where options can be:

-dm <dm> | -dirmode=<dm>       directory creation mode (0775)
-fm <fm> | -filemode=<fm>      file creation mode (0664)
-ed <ed> | -excludedirs=<ed>   exclude dirs, comma separated 
-ef <ef> | -excludefiles=<ef>  exclude files, comma separated
-f <f>   | -htmlfiles=<f>      html files, comma separated ("*.htm;*.html")
-noprocess                     ignore html files
-nocopy                        ignore non html files
-v                             verbose: copy and process msgs)
-v -v                          verbose: copy, process and ignore msgs
-v -v -v                       verbose: above and process tag msgs
EOB
}

#Create a given directory
sub reversemkdir
{
    my($dir) = @_;
    -e $dir && return;
    $_ = $dir;
    if (/(.*)\/[^\/]*\$/)
    {
        reversemkdir($1);
    }
    mkdir($dir) && chmod($dirmode, $dir);
}

#Parse html files / copy non html files
sub processfiles
{
    -d $File::Find::name && return;
 
    my($filename) = $_;

    $_ = $File::Find::name;
    if (  ($excludedirs && /$excludedirs/)
        ||($excludefiles && /$excludefiles/))
    {
        $verbose > 1 and print "Ignore file '".$File::Find::name."'\n";
        return;
    }
    
    my($relpath) = $File::Find::dir;
    $relpath=~s/^$source//;
    $relpath=~s/^\///;
    $relpath && reversemkdir($dest.'/'.$relpath);

    my($filepath) = $File::Find::name;
    $filepath=~s/^$source//;
    $filepath=~s/^\///;
    
    $_ = $filename;
    #Process HTML files
    if (/$htmlfiles/)
    {
        not $processfiles and return;
        
        $fw_use  = '';
        %fw_vars = {};
        $context = 'global';
        
        $verbose > 0 and print "Process HTML file '$source/$filepath' --> '$dest/$filepath'\n";

        my($html) = loadhtml($source.'/'.$filepath);

        if ($fw_use)
        {
            $fw_vars{'global'} = '';
            my($htmlframework) = loadhtml($fw_use);
            $html = compilehtml($html, $htmlframework);
        }
     
        my($destfile) = $dest.'/'.$filepath;
        open(HTMLDEST, ">$destfile") or die "File open failed: '$destfile'!";
        print(HTMLDEST $html);
        close(HTMLDEST);
        chmod($filemode, $destfile);
    }
    #All other files will be copied
    else
    {
        not $copyfiles and return;

        $verbose > 0 and print "Copy file '$source/$filepath' --> '$dest/$filepath'\n";

        copy($source.'/'.$filepath,
             $dest.'/'.$filepath) || die "Copy failed: '$source/$filepath' --> '$dest/$filepath'";
        chmod($filemode, $dest.'/'.$filepath);
    }
}

#Load file and parse for <FW> tags
sub loadhtml
{
    my($htmlfile) = @_;

    open(HTMLSOURCE, $htmlfile);
    while (<HTMLSOURCE>)
    {
        while ($_)
        {
            if(/(<FW[^>]*>)/i)
            {
               $fw_vars{$context}.=$`;
               my($tail) = $';
               $_ = $1;
               if (/DEFINE[ ]*=[ ]*"([^"]*)".*/i)
               {
                   $context = $1;
                   if (/\/>\$/)
                   {
                       $context = 'global';
                   }
                   $verbose > 2 and print "CMS: File='$htmlfile' DEFINE='$context'\n";
               }
               elsif (/INCLUDE[ ]*=[ ]*"([^"]*)".*/i)
               {
                   my($includefile) = $htmlfile;
                   $includefile =~s/\/[^\/]*$//;
                   $includefile.='/'.$1;
                   $fw_vars{$context}.=loadhtml($includefile);
                   $verbose > 2 and print "CMS: File='$htmlfile' Context='$context' INCLUDE='$includefile'\n";
               }
               elsif (/USE[ ]*=[ ]*"([^"]*)".*/i)
               {
                   my($usefile) = $1;
                   my($absusefile) = $htmlfile;
                   $absusefile =~s/\/[^\/]*$//;
                   $absusefile.='/'.$usefile;
                   $fw_use = $absusefile; 
                   $verbose > 2 and print "CMS: File='$htmlfile' USE='$absusefile'\n";
               }
               else
               {
                   $fw_vars{$context}.=$1;
               }
               $_= $tail;
            }
            elsif(/<\/FW>/i)
            {
               $fw_vars{$context}.=$`;
               my($tail) = $';
               $context = 'global';
               $_= $tail;
            }
            else
            {
               $fw_vars{$context}.=$_; 
               $_='';
            }
        }
    }
    close(HTMLSOURCE);
    
    return $fw_vars{'global'};
}

#Create HTML file with a framework template
sub compilehtml
{
    my($html, $htmlframework) = @_;
    my($complete) = '';

    $_ =$htmlframework;
    
    while($_)
    {
        if(/(<FW[^>]*>)/i)
        {
           $complete.=$`;
           my($tail) = $';
           $_ = $1;
           if (/PRINT[ ]*=[ ]*"([^"]*)".*/i)
           {
               $complete.=$fw_vars{$1};
               $verbose > 2 and print "CMS: PRINT='$1'\n";
           }
           else
           {
               $complete.=$1;
           }
           $_= $tail;
        }
        elsif(/<\/FW>/i)
        {
           $complete.=$`;
           $_ = $';
        }
        else
        {
           $complete.=$_; 
           $_='';
        }
    }

    return $complete;
}
