#!/usr/bin/perl

#    This file is part of MKF.
#
#    MKF is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    MKF is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with MKF; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#    Copyright (c) 2004 Alexandre Becoulet <alexandre.becoulet@epita.fr>

#use strict;

my $mkf_file_gc = "mkf.info";

######################################################
#		temporary files
#

my @temp_files;
my $tmp_dir = "/tmp";

sub get_temp_file
{
    my ($ext_p) = @_;

    my $fname;

    do
    {
	my $random = int(rand(10000));
	$fname = "$tmp_dir/configure_".$random."_".$ext_p;
    }
    while (-f $fname);

    push (@temp_files, $fname);

    return $fname;
}

sub clear_temp_files
{
    unlink($_) foreach (@temp_files);
}

######################################################
#		Display functions
#

sub error
{
    $_ = @_[0];
#    s/'/'\033[01;31m/g;
#    s/`/\033[01;36m`/g;
    print "[ Error ] $_\n";
#    print "[ Error ] \033[01;31m$_\033[m\n";
    clear_temp_files;
    exit -1;
}

sub warning
{
    $_ = @_[0];
#    s/'/'\033[01;93m/g;
#    s/`/\033[01;36m`/g;
    print "[Warning] $_\n";
#    print "[Warning] \033[01;93m$_\033[m\n";
}

######################################################
#		strings replace functions
#

sub replace
{
    my ($str_p, $value_p, $keyword_p) = @_;

    $str_p =~ s/{$keyword_p}/$value_p/g;

    return $str_p;
}

sub replace_vars
{
    my ($str_p, $vars_p, $keyword_p) = @_;

    $str_p =~ s/{$keyword_p,([^\}]+)}/$$vars_p{$1}/g;

    return $str_p;
}

sub replace_vars_list
{
    my ($str_p, $vars_p, $keyword_p) = @_;

    while ($str_p =~ /{$keyword_p,([^\,]*),([^\}]+)}/)
    {
	my $var = $2;
	my $pre = $1;
	my $list;

	$list = "$list $pre$_" foreach (split(/ +/, $$vars_p{$var}));
	$str_p =~ s/{$keyword_p,$pre,$var}/$list/;
    }

    return $str_p;
}

sub replace_list
{
    my ($str_p, $list, $keyword_p) = @_;

    return $str_p if (! $list);

    while ($str_p =~ /{$keyword_p(!?),([^\},]+),?([^\}]*)}/)
    {
	my $val;
	my $strip = $1;
	my $match_p = $2;
	my $prefix = $3;

	foreach (@$list)
	{
	    if (/^$match_p$/)
	    {
		my $item = $_;
		$item =~ s/\..*$// if ($strip);
		$val .= " ".$prefix.$item;
	    }
	}

	$str_p =~ s/{$keyword_p$strip,[^\}]+}/$val/;
    }

    return $str_p;
}

sub replace_temp_file
{
    my ($str_p, $keyword_p) = @_;

    while ($str_p =~ /{$keyword_p,([^\,]+),([^\}]+)}/)
    {
	my $id = $1;
	my $ext = $2;
	my $fname = get_temp_file ($ext);

	$str_p =~ s/{$keyword_p,$id,$ext}/$fname/g;
    }

    return $str_p;
}

######################################################
#	    Mkf file commands functions
#

sub cmd_end_if
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: no parameter expected for `@$params_p[0]'"
	if (@$params_p != 1);

    my $if_depth = $$state_p{if_depth};
    my $if_bool = $$state_p{if_bool};

    error "line $line_num: no previous _if command to end" 
	if (! $if_depth);

    $$state_p{if_depth} = $if_depth - 1;
}

sub cmd_else
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: no parameter expected for `@$params_p[0]'"
	if (@$params_p != 1);

    my $if_depth = $$state_p{if_depth};
    my $if_bool = $$state_p{if_bool};

    error "line $line_num: no previous _if command" 
	if (! $if_depth);

    @{$if_bool}[$if_depth] = @{$if_bool}[$if_depth - 1] && ! @{$if_bool}[$if_depth];
}

sub cmd_if_exec_ok
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: more parameters expected for `@$params_p[0]'"
	if (@$params_p < 2);

    my $if_depth = $$state_p{if_depth};
    my $if_bool = $$state_p{if_bool};
    my $cmd = replace_temp_file(@$params_p[1], "TEMP");

    @{$if_bool}[$if_depth + 1] = @{$if_bool}[$if_depth]
	&& ! system($cmd . " > /dev/null 2>&1");

    $$state_p{if_depth} = $if_depth + 1;
}

sub cmd_if_file_exists
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: one parameters expected for `@$params_p[0]'"
	if (@$params_p != 2);

    my $file = @$params_p[1];
    my $if_depth = $$state_p{if_depth};
    my $if_bool = $$state_p{if_bool};

    @{$if_bool}[$if_depth + 1] = @{$if_bool}[$if_depth] && -e $file;

    $$state_p{if_depth} = $if_depth + 1;
}

sub cmd_if_in_path
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: one parameters expected for `@$params_p[0]'"
	if (@$params_p != 2);

    my $exec_file = @$params_p[1];
    my $if_depth = $$state_p{if_depth};
    my $if_bool = $$state_p{if_bool};

    @{$if_bool}[$if_depth + 1] = 0;

    if (@{$if_bool}[$if_depth])
    {
	if ($exec_file =~ /\//)
	{
	    @{$if_bool}[$if_depth + 1] = -x $exec_file;
	}
	else
	{
	    foreach (split(/:/, $ENV{"PATH"}))
	    {
		if (-x "$_/$exec_file")
		{
		    @{$if_bool}[$if_depth + 1] = 1;
		    last;
		}
	    }
	}
    }

    $$state_p{if_depth} = $if_depth + 1;
}

sub cmd_if_stdout_match
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: two parameters expected for `@$params_p[0]'"
	if (@$params_p != 3);

    my $str = @$params_p[1];
    my $exec_cmd = @$params_p[2];
    my $if_depth = $$state_p{if_depth};
    my $if_bool = $$state_p{if_bool};

    @{$if_bool}[$if_depth + 1] = @{$if_bool}[$if_depth]
	&& (`$exec_cmd 2>&1` =~ /$str/);

    $$state_p{if_depth} = $if_depth + 1;
}

sub cmd_if_var_eq
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: two parameters expected for `@$params_p[0]'"
	if (@$params_p < 2);

    my $vars = $$state_p{vars};
    my $var_name = @$params_p[1];
    my $var_value = @$params_p[2];
    my $if_depth = $$state_p{if_depth};
    my $if_bool = $$state_p{if_bool};

    @{$if_bool}[$if_depth + 1] = @{$if_bool}[$if_depth] && (${$vars}{$var_name} eq $var_value);

    $$state_p{if_depth} = $if_depth + 1;
}

sub cmd_command_add
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: three parameters expected for `@$params_p[0]'"
	if (@$params_p < 4);

    my $oper = "@$params_p[1]_@$params_p[2]";
    my $jobs = $$state_p{jobs};

    $$jobs{$oper} = [] if (! exists $$jobs{$oper});
    my $jobs_list = $$jobs{$oper};

    push (@$jobs_list, join (" ", @$params_p[3..@$params_p - 1]));
}

sub cmd_clean_add
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: three parameters expected for `@$params_p[0]'"
	if (@$params_p < 4);

    my $oper = "@$params_p[1]_@$params_p[2]";
    my $torm = $$state_p{torm};

    $$torm{$oper} = [] if (! exists $$torm{$oper});
    my $torm_list = $$torm{$oper};

    foreach my $item (split(/ +/, @$params_p[3]))
    {
	push (@$torm_list, $item);
    }
}

# parse C like source files to get included files
sub auto_dep_c
{
    my ($dir_p, $deps_p, $c_src_p, $inc_dirs) = @_;

    if (! open (C_SRC, "$dir_p/$c_src_p"))
    {
	warning "can't open `$c_src_p' c source file";
	return;
    }

    foreach my $c_line (<C_SRC>)
    {
	# find #include "" lines
	if ($c_line =~ /^[ \t]*\#[ \t]*include[ \t]*\"(.*)\"/)
	{
	    my $inc_file = $1;

	    # find included file in include directories
	    foreach my $inc_dir ((split(/ +/, $inc_dirs), "."))
	    {
		my $abs_path = $inc_dir =~ /^\// ? $inc_dir : "$dir_p/$inc_dir";

		if (! -d $abs_path)
		{
		    warning "can't find `$abs_path' include directory";
		    next;
		}

		next if (! -f "$abs_path/$inc_file");

		my $dep_hdr = "$inc_dir/$inc_file";
		$dep_hdr =~ s/^.\///;

		push(@$deps_p, $dep_hdr);
	    }
	}
    }
}

# dummy function, nothing to parse in file
sub auto_dep_none
{
}

my %auto_src_cmd = 
(
 "c_src" => \&auto_dep_c,
 "none" => \&auto_dep_none,
);

sub cmd_auto_source_dep
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: three parameters expected for `@$params_p[0]'"
	if (@$params_p != 4);

    my $deps = $$state_p{deps};				# hash table for all target/deps of current Makefile
    my $src_type = @$params_p[1];
    my $ext = @$params_p[2];
    my $ext_out = @$params_p[3];

    foreach my $c_src (<$dir_p/*.$ext>)
    {
	$c_src = $1 if ($c_src =~ /.*\/(.*)/);

	my $target = $c_src;
	$target =~ s/\.$ext$/.$ext_out/g;

	next if (${$deps}{$target});

	${$deps}{$target} = [];

	push(@{${$deps}{$target}}, $c_src);

	# get pointer on function for source file parse
	if (my $func_ptr = $auto_src_cmd{$src_type})
	{
	    # call command function
	    $func_ptr -> ($dir_p, ${$deps}{$target}, $c_src, ${$$state_p{vars}}{"inc_dirs"});
	}
	else
	{
	    # error if unknow function
	    error "line $line_num: unknown source file type `$src_type'";
	}
    }
}

sub cmd_auto_dep
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: two parameters expected for `@$params_p[0]'"
	if (@$params_p != 3);

    my $deps = $$state_p{deps};				# hash table for all target/deps of current Makefile
    my $ext = @$params_p[1];
    my $ext_out = @$params_p[2];

    foreach my $dep (keys(%$deps))
    {
	# match file name extention in existing target

	next if (not ($dep =~ /[^.]*\.(.*)$/));
	next if ($1 ne $ext);

	# change extention to $ext_out
	my $target = $dep;
	$target =~ s/\.$ext$/.$ext_out/g;

	# skip if target already exist

	${$deps}{$target} = [];
	push(@{${$deps}{$dep}}, $target);
    }
}


sub cmd_target_dep
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: two parameters expected for `@$params_p[0]'"
	if (@$params_p != 3);

    my $deps = $$state_p{deps};				# hash table for all target/deps of current Makefile
    my $target = @$params_p[1];				# target name
    my @target_deps = split(/ +/, @$params_p[2]);	# target dependencies name list

#    error "line $line_num: redefinition of `$target' target dependencies"
#	if (${$deps}{$target});

    foreach (@target_deps)
    {
	${$deps}{$_} = [] if (! exists ${$deps}{$_});
    }

    push(@{${$deps}{$target}}, @target_deps);
}

sub cmd_target_dep_add
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: two parameters expected for `@$params_p[0]'"
	if (@$params_p != 3);

    my $deps = $$state_p{deps};				# hash table for all target/deps of current Makefile
    my $target = @$params_p[1];				# target name
    my @target_deps = split(/ +/, @$params_p[2]);	# target dependencies name list

    error "line $line_num: no previous dependencies for `$target' target"
	if (! ${$deps}{$target});

    foreach (@target_deps)
    {
	${$deps}{$_} = "0" if (! exists ${$deps}{$_});
    }

    push (@{${$deps}{$target}}, @target_deps);
}

sub cmd_install_cmd
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: one parameters expected for `@$params_p[0]'"
	if (@$params_p != 2);

    my $jobs_list = $$state_p{install};

    push (@$jobs_list, @$params_p[1]);
}

sub cmd_msg_info
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    print "[ info ] ", @$params_p[1], "\n";
}

sub cmd_msg_error
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error join (" ", @$params_p[1..@$params_p - 1]);
}

sub cmd_msg_warning
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    warning join (" ", @$params_p[1..@$params_p - 1]);
}

sub cmd_mkf_include
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: one parameters expected for `@$params_p[0]'"
	if (@$params_p != 2);

    my $filename = @$params_p[1];

    local *MKF_FILE;
    error "line $line_num: can't open `$filename' included file"
	if (! open (MKF_FILE, "$dir_p/$filename"));

    process_dir_mkffile($dir_p, $state_p, *MKF_FILE);
}

sub cmd_path_parent
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: one parameters expected for `@$params_p[0]'"
	if (@$params_p != 2);

    my $vars = $$state_p{vars};

    foreach my $var_name (split(/ +/, @$params_p[1]))
    {
	${$vars}{$var_name} =~ s/(^| )([^\/][^ ]+)/$1..\/$2/g;
    }
}

sub cmd_var_append
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: two parameters expected for `@$params_p[0]'"
	if (@$params_p != 3);

    my $vars = $$state_p{vars};
    my $var_name = @$params_p[1];
    my $var_value = @$params_p[2];

#    warning "line $line_num: variable `$var_name' not defined before"
#	if (! defined $$vars{$var_name});

    ${$vars}{$var_name} .= " $var_value";
}

sub cmd_var_define
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: two parameters expected for `@$params_p[0]'"
	if (@$params_p != 3);

    my $vars = $$state_p{vars};
    my $var_name = @$params_p[1];
    my $var_value = @$params_p[2];

    ${$vars}{$var_name} = $var_value;
}

sub cmd_var_undefine
{
    my ($line_num, $dir_p, $params_p, $state_p) = @_;

    error "line $line_num: one parameters expected for `@$params_p[0]'"
	if (@$params_p != 2);

    my $vars = $$state_p{vars};
    my $var_name = @$params_p[1];

    ${$vars}{$var_name} = undef;
}

my %mkf_cmd = 
(
 "_end_if" => \&cmd_end_if,
 "_else" => \&cmd_else,
 "_if_exec_ok" => \&cmd_if_exec_ok,
 "_if_file_exists" => \&cmd_if_file_exists,
 "_if_in_path" => \&cmd_if_in_path,
 "_if_stdout_match" => \&cmd_if_stdout_match,
 "_if_var_eq" => \&cmd_if_var_eq,
 "command_add" => \&cmd_command_add,
 "clean_add" => \&cmd_clean_add,
 "auto_source_dep" => \&cmd_auto_source_dep,
 "auto_dep" => \&cmd_auto_dep,
 "target_dep" => \&cmd_target_dep,
 "target_dep_add" => \&cmd_target_dep_add,
 "install_cmd" => \&cmd_install_cmd,
 "msg_error" => \&cmd_msg_error,
 "msg_info" => \&cmd_msg_info,
 "msg_warning" => \&cmd_msg_warning,
 "mkf_include" => \&cmd_mkf_include,
 "path_parent" => \&cmd_path_parent,
 "var_define" => \&cmd_var_define,
 "var_append" => \&cmd_var_append,
 "var_undefine" => \&cmd_var_undefine
);

######################################################
#	    Main processing functions
#

sub process_makefile_rule
{
    (my $dir_p, my $target, my $state_p, local *MAKEFILE) = @_;

    return if (${$$state_p{done}}{$target});
    ${$$state_p{done}}{$target} = "1";

    # get dependencies
    my $deps = ${$$state_p{deps}}{$target};
    my $target_name = $target;

    # get jobs list
    my $hash;

    if (-d "$dir_p/$target")
    {
	$hash = "enter_dir";
	$target_name = "_".$target;
    }
    else
    {
	if (($deps && (@$deps[0] =~ /[^.]*\.(.*)/)) || ($target =~ /(all)/))
	{ $hash = $1; }
	else
	{ $hash = "noext"; }
	
	if (($target =~ /[^.]*\.(.*)/) || ($target =~ /(all)/))
	{ $hash .= "_$1"; }
	else
	{ $hash .= "_noext"; }
    }

    my $job_list = ${$$state_p{jobs}}{$hash};
    my $rm_list = ${$$state_p{torm}}{$hash};

    if ((!$deps) && (!$job_list))
    {
	print MAKEFILE "all:\n\n" if ($target =~ /^all$/);
	return;
    }

    # write target
    print MAKEFILE "$target_name:";

    if ($deps)
    {
	foreach (@$deps)
	{
	    if (-d "$dir_p/$_")
	    { print MAKEFILE " _$_"; }
	    else
	    { print MAKEFILE " $_"; }
	}
    }
    print MAKEFILE "\n";

    foreach my $job (@$job_list)
    {
	my $job_line = $job;

	$job_line = replace_vars($job_line, $$state_p{vars}, "JVAR");
	$job_line = replace_vars_list($job_line, $$state_p{vars}, "JLIST");
	$job_line = replace_list($job_line, $deps, "SRC");
	$job_line = replace($job_line, $target, "OUT");

	$job_line =~ s/[\t\n ]+/ /g;

	print MAKEFILE "\t$job_line\n";
    }
    print MAKEFILE "\n";

    push (@{$$state_p{outs}}, $target) if (@$job_list);	# add to out files target list

    foreach my $rm (@$rm_list)
    {
	my $rm_line = $rm;

	$rm_line = replace_vars($rm_line, $$state_p{vars}, "JVAR");
	$rm_line = replace_list($rm_line, $deps, "SRC");

	$target =~ /^([^.]*)/;
	$rm_line = replace($rm_line, $1, "OUT!");

	push (@{$$state_p{outs}}, $rm_line);
    }

    if ($deps)
    {
	process_makefile_rule ($dir_p, "$_", $state_p) foreach (@$deps);
    }
}

sub process_makefile_clean
{
    (my $dir_p, my $target, my $state_p, local *MAKEFILE) = @_;

    print MAKEFILE "clean:\n";

    my @files, my @dirs;

    foreach (@{$$state_p{outs}})
    {
	if (-d "$dir_p/$_")
	{ push (@dirs, $_) }
	else
	{ push (@files, $_) }
    }

    foreach my $job (@{${$$state_p{jobs}}{"clean_file"}})
    {
	foreach (@files)
	{
	    my $job_line = $job;
	    $job_line = replace($job_line, $_, "FILE");

	    print MAKEFILE "\t$job_line\n";
	}
    }

    foreach my $job (@{${$$state_p{jobs}}{"clean_dir"}})
    {
	foreach (@dirs)
	{
	    my $job_line = $job;
	    $job_line = replace($job_line, $_, "DIR");

	    print MAKEFILE "\t$job_line\n";
	}
    }

    print MAKEFILE "\n";
    print MAKEFILE "re: clean all\n\n";
}

sub process_makefile_install
{
    (my $dir_p, my $target, my $state_p, local *MAKEFILE) = @_;

    print MAKEFILE "install:\n";

    foreach my $job (@{${$$state_p{jobs}}{"install_dir"}})
    {
	foreach (@{$$state_p{outs}})
	{
	    next if (! -d "$dir_p/$_");

	    my $job_line = $job;
	    $job_line = replace($job_line, $_, "DIR");

	    print MAKEFILE "\t$job_line\n";
	}
    }

    foreach (@{$$state_p{install}})
    {
	    print MAKEFILE "\t$_\n";
    }
    print MAKEFILE "\n";
}

sub process_makefile
{
    my ($dir_p, $state_p) = @_;

    open (MAKEFILE, ">$dir_p/Makefile.mkf");

    print MAKEFILE
	"#\n".
	"# Makefile generated by MKF configure script\n".
	"#\n\n";

    process_makefile_rule ($dir_p, "all", $state_p, *MAKEFILE);
    process_makefile_clean ($dir_p, "clean", $state_p, *MAKEFILE);
    process_makefile_install ($dir_p, "install", $state_p, *MAKEFILE);

    close (MAKEFILE);
}

sub process_dir_mkffile
{
    (my $dir_p, my $state_p, local *MKF_FILE) = @_;

    # line counter for error repporting
    my $line_num = 0;

    my $enter_if_depth = $$state_p{if_depth};

    # used to append lines when ended with \
    my $last = "";

    # parse mkf.info file line at once
    foreach my $line (<MKF_FILE>)
    {
	$line_num++;

	# keep lines and concat lines ending with \
	if ($line =~ /^(.*)\\$/)
	{
	    $last .= $1;
	    next;
	}

	# add last line ending with \ if any
	if ($last)
	{
	    $line = $last.$line;
	    $last = "";
	}

	# remove leading spaces
	$line =~ s/^[ \t]*//g;

	# skip empty lines and comment lines
	next if ($line =~ /^[ \t]*(\#.*)?$/);

	# skip if conditional state is false
	next if ((not $line =~ /^_/) && (! @{$$state_p{if_bool}}[$$state_p{if_depth}]));

	# replace variables in command line
	$line = replace_vars($line, $$state_p{vars}, "PVAR");
	$line = replace($line, $dir_p, "PWD");

	# split fields
	my @line_l = split(/[\n\t]+/, $line);

	# get pointer on function for command
	if (my $func_ptr = $mkf_cmd{@line_l[0]})
	{
	    # call command function
	    $func_ptr -> ($line_num, $dir_p, \@line_l, $state_p);
	}
	else
	{
	    # error if unknow function
	    error "line $line_num: unknown command `@line_l[0]'";
	}
    }

    error "line $line_num: if not closed at end of file"
	if ($$state_p{if_depth} != $enter_if_depth);
}

sub process_dir
{
    my ($dir_p, %state_p) = @_;

    # add local field to state structure
    $state_p{deps} = {};		# target/dependencies for current Makefile
    $state_p{done} = {};		# already done targets

    $state_p{if_bool} = ["1"];		# conditional state stack
    $state_p{if_depth} = 0;		# conditional state stack index
    $state_p{outs} = [];		# output files
    $state_p{install} = [];		# install commands

    # undefine previous local variable
    foreach (keys(%{$state_p{vars}}))
    {
	${$state_p{vars}}{$_} = undef if (/^_/);
    }

    ${$state_p{vars}}{_dir} = $dir_p;

    print "entering $dir_p\n";

    # open mkf.info file in directory
    local *MKF_FILE;
    open (MKF_FILE, "$dir_p/$mkf_file_gc") || error "no $mkf_file_gc file in directory";

    process_dir_mkffile($dir_p, \%state_p, *MKF_FILE);

    foreach my $subdir (keys(%{$state_p{deps}}))
    {
	next if (! -d "$dir_p/$subdir");

	my %state = 
	    (
	     "vars" => {%{$state_p{vars}}},
	     "jobs" => {%{$state_p{jobs}}},
	     "torm" => {%{$state_p{torm}}}
	     );

	process_dir ("$dir_p/$subdir", %state);
    }

    # close file
    close (MKF_FILE);

    process_makefile ("$dir_p", \%state_p);
}

sub main
{
    # param passed to configure script on command line
    my %param_h;

    # extract variable name and value from parameter
    foreach my $param (@ARGV)
    {
	if ($param =~ /--help/)
	{
	    -f "mkf.help" && system("cat mkf.help");
	    return 0;
	}

	if ($param =~ /--enable-(.*)/)
	{
	    $param_h{$1} = "yes";
	    next;
	}

	if ($param =~ /--disable-(.*)/)
	{
	    $param_h{$1} = "no";
	    next;
	}

	error "bad command line parameter `$param'"
	    if (! ($param =~ /--(.*)=(.*)/));

	error "command line parameter `--$1' used twice"
	    if (defined ($param_h{$1}));

	$param_h{$1} = $2;
    }

    # state structure used during configure process
    my %state =
    (
     "vars" => {%param_h},
     "jobs" => {},
     "torm" => {}
    );

    # process current directory
    return process_dir (".", %state);
}

main;
clear_temp_files;
print "Done.\n";
exit 0;

