# ###########################################################################################
# NAME: amsrcs02.pl
# AIM: MAKE DSP FILES - Given a folder, trace the makefile.am files, collecting
# libraries, applications and sources
# Show 'projects' found, and SHOW the sources of each.
# AND write a DSP file for each!!!
# While it confidently outputs a set of DSP files, and a DSW file, in
# complex cases these will not work very well, as it MISSES lots of includes, and libraries
# ================================
# 10/02/2010 - Review of this script
# ONLY GENERATES TEMPORARY DSP FILE(S), and 'temp_list.dsw' if the option $write_temp_ds is set to 1
# Add DSP 'sources' relative to a $target_dir, Remove a warning about no GROUP
# 
# 2009-06-09 - change error exit to WARNING only, and continue processing
# 2009-05-09 - Add or ensure _CRT_SECURE_NO_DEPRECATE is added to defines
# 22/11/2008 - Problem, perhaps, in that it 'splits' fgfs into MANY libraries
# which is perhpas how it should be???
# Also see am2dsp[n].pl as well, which is the opposite, in that is COMBINES 
# everything, including 'test' and 'utilties' into ONE ... and puts ALL the objects
# into Debug or Release, which would NOT account for duplicates!
# Also scan the 'top' directory for ALL FILE,
# and show files NOT in a project
# Add projects NOT addressed by main configure.ac and makefile.am
# 17/11/2007 - geoff mclane - http://geoffair.net/mperl
#
# The process
#
# Read the configure.ac file, and build a set of contents
# and put inputs into @make_input_list, or @other_input_files
# which are then put in @input_files ... set $dgb20 to see list as processed
#
# Each @input_files, and later @other_inputs (makefile.am) is processed in process_am_file()
# Sources are added to @msvc_c_files in form 'Lib/Project | Source | Title'
# extract_am_project then separates these into $am_projects{$lib} .= ['|'.]$lsc;
#
# The %am_project's are shown by show_am_projects( $main_root_dir );
# Then the 'other' projects are shown - show_other_projects( $main_root_dir );
# and finally all source files found show_all_sources( "makefile.am set", $main_root_dir );
# 04/04/2008 - add read of sub-directory 'configure.ac' if present ...
# 22/11/2008 - add /GR to DSP, and removed all \r output
# ###########################################################################################
use strict;
use warnings;
use File::Basename;	# to split path into ($name, $dir) = fileparse($ff); or ($nm,$dir,$ext) = fileparse( $fil, qr/\.[^.]*/ );

require 'logfile.pl' or die "Unable to load logfile.pl ...\n";
require 'relative.pl' or die "Unable to load relative.pl ...\n";
require 'chkmain.pl' or die "Unable to load chkmain.pl ...\n";
require 'utils.pl' or die "Unable to load utils.pl ...\n";

# log file stuff
my ($LF);
my $pgmname = $0;
if ($pgmname =~ /\w{1}:\\.*/) {
	my @tmpsp = split(/\\/,$pgmname);
	$pgmname = $tmpsp[-1];
}
my $outfile = "temp.$pgmname.txt";
my $pack = $pgmname;
open_log($outfile);
prt( "$0 ... Hello, World ...\n" );

my $main_root_dir = "C:\\FG\\32\\Atlas\\";
#my $main_root_dir = "C:\\FGCVS\\SimGear\\source\\";
my $target_dir = $main_root_dir."projects\\msvc";
#my $main_root_dir = "C:\\FG\\SimGear\\";
#my $main_root_dir = "C:\\FGCVS\\SimGear\\source\\";
#my $target_dir = $main_root_dir."projects\\msvc";

# features
my $load_log = 0; # load logfile into notepad at end
my $write_temp_ds = 1;

my $add_CRT_SECURE_NO_DEPRECATE = 1;
my $add_NOMINMAX = 1;
#my $add_SOURCE_REL = 0;
#my $src_relative = "..\\..";
my $add_CONFIG_H = 1;

my $recheck_am = 0;	# a debug RE-CHECK of makefile.am files for a reference
my $check_full = 1;	# check for the FULL file name
my $long_line = " C:\\FGCVS\\FlightGear\\source\\scripts\\example\\fgfsclient.cxx ";
my $min_line = length($long_line);
my $check4main = 1;	# check if the NOT FOUND modules have a main() service
my $check4main2 = 1;	# check if the source has a main() service
my $show_main = 0;		# actually SHOW the main
my $show_cond = 0;		# actually SHOW the condition for main
my $one_line = 0;		# output sources in ONE line
my $add_rel_src = 1;    # add the relative source to output
###############################

####### ONLY DSP STUFF ########
my $out_dsp  = 1;	# output a MAIN DSP and DSW files
my $out_dsp2 = 1;	# output a secondary DSP files

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

my $msvc_cflags = '';	# see %am_includes and %am_cppflags # hash by project, the CPPFLAGS, if ANY
my $msvc_libs   = '';	# see %am_libadds  = ();	# hash by project, the LDADD, if ANY
my $msvc_dlibs  = '';
my $msvc_rlibs  = '';
my $last_project = '';
my $last_dsp_file = '';
my @dspinfo = ();
############################

######################
# constants
my $IGNORE_PATTERN = "^##([^#].*)?\$";
my $WHITE_PATTERN = "^[ \t]*\$";
my $COMMENT_PATTERN = "^#";
my $RULE_PATTERN = "^([\$a-zA-Z_.][-.a-zA-Z0-9_(){}/\$]*) *:([^=].*|)\$";
my $SUFFIX_RULE_PATTERN = "^\\.([a-zA-Z]+)\\.([a-zA-Z]+)\$";
# like say bin_PROGRAMS = abc xyz
my $MACRO_PATTERN = "^([A-Za-z][A-Za-z0-9_]*)[ \t]*([:+]?)=[ \t]*(.*)\$";
my $BOGUS_MACRO_PATTERN = "^([^ \t]*)[ \t]*([:+]?)=[ \t]*(.*)\$";
my $IF_PATTERN = "^if[ \t]+\([A-Za-z][A-Za-z0-9_]*\)[ \t]*\(#.*\)?\$";
my $ELSE_PATTERN = "^else[ \t]*\(#.*\)?\$";
my $ENDIF_PATTERN = "^endif[ \t]*\(#.*\)?\$";
my $PATH_PATTERN='(\\w|/|\\.)+';
# This will pass through anything not of the prescribed form.
my $INCLUDE_PATTERN = "^include[ \t]+((\\\$\\\(top_srcdir\\\)/${PATH_PATTERN})|(\\\$\\\(srcdir\\\)/${PATH_PATTERN})|([^/\\\$]${PATH_PATTERN}))[ \t]*(#.*)?\$";

my $AM_CONDITIONAL_PATTERN = "AM_CONDITIONAL\\((\\w+)";
my $AM_INIT_AUTOMAKE = "AM_INIT_AUTOMAKE\\(([^,]+),[ \t]*([^)]+)";
#### type constants
my $TYPE_NONE = 0;
my $TYPE_C    = 1;
my $TYPE_H    = 2;
my $TYPE_DSW  = 3;
my $TYPE_SLN  = 4;
my $TYPE_AM   = 5;
my $TYPE_MAK  = 6;

######################
# program variables
my $exit_status = 0;
my @input_files = ();
my @make_input_list = ();
my @other_input_files = ();
my %output_files = ();
my %make_list = ();
my %am_vars = ();
my %def_type = ();
my @excluded_dirs = ();
my @var_list = ();
my $dsp_package = '';
my $dsp_version = '';
my %configure_cond = ();
my %configure_vars = ();
my %cfg_defines = ();
my @config_fullnames = ();
my @config_names = ();
my @config_headers = ();
my $config_header_line = '';
####my $am_file = '';
my @msvc_c_extra = ();
my @msvc_h_extra = ();
my @msvc_o_extra = ();
my @done_am = ();	# list of AM files read
my @warnings = ();
my @ac_scanned = ();
my %config_ac_macros = ();
my %inc_hash = ();

# These two variables are used when generating each Makefile.in.
# They hold the Makefile.in until it is ready to be printed.
my $output_vars = '';
my $output_trailer = '';
# This holds the contents of a Makefile.am, as parsed by
# read_am_file.
my %contents = ();
# This holds the names which are targets.  These also appear in
# %contents.
my %targets = ();
# For a variable or target which is defined conditionally, this
# holds an array of the conditional values.  The array is composed
# of pairs of condition strings (the variables which configure
# will substitute) and values (the value of a target is
# meaningless).  For an unconditional variable, this is empty.
my %conditional = ();
# This holds the line numbers at which various elements of
# %contents are defined.
my %content_lines = ();
# This holds a 1 if a particular variable was examined.
my %content_seen = ();
# This is the conditional stack.
my @conditional_stack = ();
# This holds the set of included files.
my @include_stack = ();
# This holds the "relative directory" of the current Makefile.in.
# Eg for src/Makefile.in, this is "src".
my $relative_dir = '';
# This maps the source extension of a suffix rule to its
# corresponding output extension.
my %suffix_rules = ();
# per AM file sources
my @am_c_files = ();
my @am_h_files = ();
my @am_o_files = ();

#################
### final source lists
my @msvc_c_files = ();
my @msvc_titles  = ();
my @msvc_h_files = ();
my @msvc_o_files = ();
my %other_projs = ();   # store C/C++ sources under project names
my %other_deps = ();    # and store LIBRARY dependencies ..._LDADD, under project names
my %am_projects = ();   # store C/C++ sources under project names
my %am_headers = ();   # store H sources under project names
my %am_includes = ();	# hash by project, the INCLUDES, if ANY
my %am_cppflags = ();	# hash by project, the CPPFLAGS, if ANY
my %am_libadds  = ();	# hash by project, the LDADD, if ANY
my @all_files = ();
my $active_am_file = '';
my $current_am_file = '';
my $next_am_file = '';
my ($fil_name, $fil_dir, $full_fil_path);
my @dspprojs = ();      # list of PROJECTS created

#######################
# debug
my $dbg1 = 0;	# show EACH configure.ac line
my $dbg2 = 0;	# show "Storing configure_cond key $1 ... value=? ..."
my $dbg2a = 0;	# show if (/$AM_CONDITIONAL_PATTERN/o) { prt( "$aclnnum: [$acline]\n" ) if ($dbg2a);
my $dbg3 = 0;	# show "Substute $key=$nval ... setting $var_hash{$key} = $nval;
my $dbg3a = 0;	# show "MACRO [$key]=[$nval]
my $dbg4 = 0;	# show "Got ac_output_line = [$ac_output_line] ...
my $dbg5 = 0;	# show "Adding $input [$ff] to make_input_list ...
my $dbg6 = 0;	# list Makefile list found
my $dbg7 = 0;	# show each 'makefile' processed ...
my $dbg8g = 1;	# show "Group set to [$group] ...
my $dbg8 = 0;	# show "Group set to [$group] ... *AND* "Expanding variable $varname ...
my $dbg9 = 0;	# show "Added [$file] to C EXTRA list...
my $dbg10 = 0;	# show each makefile.am line ...
my $dbg11 = 0;	# show "NO contents for [$last_var_name]! ...
my $dbg12 = 0;	# show "End $last_var_name = ["... if !$saw_bk
my $dbg13 = 0;	# show "Found if $1, and condition is TRUE (see cfg_defines) ...
my $dbg14 = 0;	# show "Found a macro definition. 1[$1] 2[$2] 3[$3] ... something=something in Makefile.am
my $dbg15 = 0;	# show macro "NB: [$last_var_name] = [$contents{$last_var_name}]" . ...
my $dbg16 = 1;	# show "$pack: reading $am_file ...
my $dbg17 = 0;	# show "Sources ending in .$source_suffix become .$object_suffix ...
my $dbg18 = 0;	# show "Add $src to C list
my $dbg19 = 0;	# show %contents after makefile.am parse
my $dbg20 = 1;	# show "\nProcessing [$am_file] from input_files array ...
my $dbg21 = 1;	# show "Project: [$proj] - sources [$srcs] ...
my $dbg22 = 1;	# show CHECK ME always ...
my $dbg23 = 0;	# show directory content count - "Found ".scalar @dir_cont." items in [$fil]
my $dbg24 = 0;	# show "Processing AM file [" . $makefile . ".am]
my $dbg25 = 0;	# show "Got INCLUDES [$incs] ...
my $dbg26 = 1;	# show "For $group, saved INCLUDES [$incs]
my $dbg27 = 0;	# show checking for substitution macros ...
my $dbg28 = 1;  # show ALL %contents ... see $dbg19 also above to show more ...
# that is show like "CT: Got $cnt items in \%contents ... file $file_am ...
my $dbg29 = 0; # prt("[dbg_29] Found dependencies [$uproj]=[$srcs].\n") if ($dbg29);
my $dbg30 = 0; # prt( "[dbg30] Ending last DSP - [$fil] ...\n" ) if ($dbg30);

my $dbg_comm = 0;   # show sifting of comments out of file lines
my $dbg_incs = 0;   # show sifting of 'include' out of file lines

# Normally it was intended to read an am2cfg.def file, but set some anyway

$cfg_defines{"HAVE_GLUT"} = 1;
$cfg_defines{"HAVE_THREADS"} = 1;

# forward references
sub get_top_files($);
sub fix_rel($);

# FUNCTIONS
# =====================================================
# general
sub prtw($) {
    my ($wm) = shift;
    prt($wm);
    $wm =~ s/\n$//;
    push(@warnings,$wm);
}

sub prtd($) {
    my ($tx) = shift;
    prt($tx);
    $tx =~ s/\n$//;
    push(@dspinfo, $tx);
}

sub pgm_exit($$) {
   my ($val,$msg) = @_;
   if (@warnings) {
      prt( "\nREPEAT OF ".scalar @warnings." WARNINGS ...\n" );
      foreach my $mg (@warnings) {
         prt("$mg\n");
      }
      prt("\n");
   } else {
      prt("No WARNINGS issued...\n");
   }
   if (length($msg)) {
      $msg .= "\n" if (! ($msg =~ /\n$/) );
      prt($msg);
   }
   close_log($outfile,$load_log);
   exit($val);
}

sub subract_target_dir($) {
   my ($fil) = shift;
   my $td = $target_dir;
   my $len = length($fil);
   if ($len > length($td)) {
      $len = length($td);
      my ($i);
      for ($i = 0; $i < $len; $i++) {
         last if (substr($td,$i,1) ne substr($fil,$i,1));
      }
      $fil = substr($fil,$i);
      $fil =~ s/^(\\|\/)//;
   }
   return $fil;
}

sub has_no_extent($) {
	my ($fil) = shift;
	my ($fnm, $fdir, $fe) = fileparse( $fil, qr/\.[^.]*/ );
	if (length($fe) == 0) {
		return 1;
	}
	return 0;
}

# specific
sub exclude_cvs_svn($) {
	my ($dir) = shift;
	$dir = lc($dir);
	return 1 if ($dir eq '.svn');
	return 1 if ($dir eq 'cvs');
	return 0;
}

sub exclude_dir($) {
    my ($dir) = shift;
    foreach my $d (@excluded_dirs) {
		if ($dir =~ "/$d/") {
			return 1;
		}
    }
    return 0;
}


sub get_top_files($) {
	my ($td) = shift;
	my @dirs = ();
   my ($ff);
	$td = unix_2_dos($td);	# ensure DOS form
	#$td .= "\\" if (substr($td,length($td)-1) ne "\\"); # add trailing, if none
	$td .= "\\" if !($td =~ /\\$/); # add trailing, if none
	if (opendir(DIR, $td)) {
		my @dfiles = readdir(DIR);
		close DIR;
		foreach my $df (@dfiles) {
			next if (($df eq '.') || ($df eq '..'));
			$ff = $td.$df;
			if (-f $ff) {
				my $typ = is_my_type($df);
				my ($nm,$dir) = fileparse($ff);
				#my $rd = get_relative_path( $dir, $td );
				my $rd = get_relative_path_2( $dir, $td );
				##my $rd = unix_2_dos($rp);
				push(@all_files, [$df, $ff, 0, $typ, $rd]) if ($typ);
			} elsif (-d $ff) {
				push(@dirs,$ff) if (!exclude_cvs_svn($df));
			} else {
				prtw( "WARNING: What is THIS [$ff] ???\n" );
			}
		}
	} else {
		prtw( "WARNING: Unable to OPEN directory $td ...\n" );
	}
	foreach $ff (@dirs) {
		get_top_files($ff);
	}
}

# fix relative directory - fix relative path - path fix
# Remove any DOT or DOUBLE DOT from the PATH
sub fix_rel($) {
	my ($path) = shift;
	$path = unix_2_dos($path);	# ensure DOS separator
	my @a = split(/\\/, $path);	# split on DOS separator
	my $npath = '';
	my $wmsg = '';
	my $max = scalar @a;
	my @na = ();
	for (my $i = 0; $i < $max; $i++) {
		my $p = $a[$i];
		if ($p eq '.') {
			# ignore this
		} elsif ($p eq '..') {
			if (@na) {
				pop @na;	# discard previous
			} else {
				prtw( "WARNING: Got relative .. without previous!!! [$path]" );
			}
		} else {
			push(@na,$p);
		}
	}
	foreach my $pt (@na) {
		$npath .= "\\" if length($npath);
		$npath .= $pt;
	}
	return $npath;
}

sub is_c_source_ext($) {
    my ($ext) = shift;
    my $fe = lc($ext);
    if (($fe eq '.c')||($fe eq '.cxx')||($fe eq '.cpp')) {
        return 1;
    }
    return 0;
}

sub is_h_source_ext($) {
    my ($ext) = shift;
    my $fe = lc($ext);
    if (($fe eq '.h')||($fe eq '.hxx')||($fe eq '.hpp')) {
        return 1;
    }
    return 0;
}

sub is_am_source_ext($) {
	my ($ext) = shift;
	$ext = lc($ext);
	if ($ext eq '.am') {
		return 1;
	}
	return 0;
}

sub is_am_file($) {
	my ($fil) = shift;
	my ($fnm, $fdir, $fe) = fileparse( $fil, qr/\.[^.]*/ );
	return is_am_source_ext($fe);
}

sub process_one_file($) {
   my $afile = shift;
	($fil_name, $fil_dir) = fileparse($afile);
	#$full_fil_path = unix_2_dos($main_root_dir . $fil_dir);
	$full_fil_path = fix_rel($main_root_dir . $fil_dir);	# also returns DOS, minus trailing '\'
	$full_fil_path .= "\\" if ( !($full_fil_path =~ /\\$/) );		# add trailing '\' IF
	$next_am_file = $afile;
	$next_am_file .= ".am" if (has_no_extent($afile) && !is_am_file($afile));
	prt( "\nProcessing [$next_am_file] from input_files array ...\n" ) if ($dbg20);
   process_am_file($dsp_package, $afile, $main_root_dir) if !exclude_dir($afile);
	my $mk = mark_all_files(unix_2_dos($main_root_dir . $afile . '.am'));
	prtw( "WARNING: NOT FOUND IN MARK [".unix_2_dos($main_root_dir.$afile.".am")."]!\n" ) if (!$mk);
}


sub lib_in_projects($$) {
   my ($lib,$rp) = @_;
   foreach my $p (@{$rp}) {
  		my ($dsp,$name) = split ',', $p;
      if ($lib eq $name) {
         return 1;
      }
   }
   return 0;
}

# 10/02/2010 Add dependencies
# Package=<4>
#{{{
#    Begin Project Dependency
#    Project_Dep_Name SimGear
#    End Project Dependency
#}}}

sub generate_dsw($$$) {
   my ($dsw_name, $rpacks, $rdeps) = @_;
   my ($proj,@arr,$lib,$deps,$lib2);
   prtd( "Creating $dsw_name ...\nrenaming any original to OLD or BAK ...\n" );
	rename_2_old_bak( $dsw_name );
   open(DSW, ">$dsw_name") || mydie( "Can't create $dsw_name: $!\n" );

   print DSW <<EOF;
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!

EOF

   foreach my $p (@{$rpacks}) {
		print DSW "###############################################################################\n\n";
		my ($dsp,$name) = split ',', $p;
		print DSW "Project: \"$name\"=\"$dsp\" - Package Owner=<4>\n\n";
		print DSW <<EOF;
Package=<5>
{{{
}}}

EOF
      print DSW "Package=<4>\n";
      print DSW "{{{\n";
      foreach $proj (keys %{$rdeps}) {
         if ($proj eq $name) {
            $deps = ${$rdeps}{$proj};
            @arr = split(/\*/,$deps);
            if (@arr) {
               foreach $lib (@arr) {
                  $lib2 = "lib".$lib;
                  if (lib_in_projects($lib2,$rpacks)) {
                     print DSW "    Begin Project Dependency\n";
                     print DSW "    Project_Dep_Name $lib2\n";
                     print DSW "    End Project Dependency\n";
                  } else {
                     prtw("WARNING: [$lib2] NO FOUND in PROJECTS!\n");
                  }
               }
            }
            last;
         }

      }
      print DSW "}}}\n\n";
   }

   print DSW <<EOF;
###############################################################################

Global:

Package=<5>
{{{
}}}

Package=<3>
{{{
}}}

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

EOF

    close(DSW);
    prtd("Written $dsw_name...\n");
}

sub gen_temp_dsw($) {
   my ($rd) = shift;   # = $main_root_dir
	$rd =~ s/(\\|\/)$//; # s/\\$//;
   my $out_file = 'temp_list.dsw';
   # ONLY generate a TEMP DSW
   ##########################
   if ($write_temp_ds) {
      if (length($dsp_package)) {
         $out_file = 'temp.'.$dsp_package.'.dsw';
      } else {
         $out_file = 'temp_list.dsw';
      }
    } else {
      $out_file = $target_dir;
      $out_file .= "\\" if !($out_file =~ /(\\|\/)$/);
      if (length($dsp_package)) {
         $out_file .= $dsp_package;
      } else {
         my ($f,$d) = fileparse($rd);
         $out_file .= $f;
      }
      $out_file .= '.dsw';
   }
   #$out_file = $outdir.$in_file.'.dsw';
	# DSW file, @packs
	generate_dsw( $out_file, \@dspprojs, \%other_deps );
}

# =====================================================

prt( "Moment, getting full file list of $main_root_dir ...\n" );
get_top_files( $main_root_dir );
prt( "Got ".scalar @all_files." files ...\n" );

scan_root_folder( $main_root_dir );

if (@input_files) {
   my @inf = @input_files;
   prt( "Processing ".scalar @inf." files...\n" );
   foreach my $fil (@inf) {
      process_one_file($fil);
   }
}

my @other_inputs = get_missed_makes($main_root_dir);
if (@other_inputs) {
	prt( "Other AM files, not used = ".scalar @other_inputs." ...\n" );
	foreach my $afile (@other_inputs) {
		($fil_name, $fil_dir) = fileparse($afile);
		##$full_fil_path = unix_2_dos($main_root_dir . $fil_dir);
		$full_fil_path = fix_rel($main_root_dir . $fil_dir);	# also returns DOS, minus trailing '\'
		$full_fil_path .= "\\" if ( !($full_fil_path =~ /\\$/) );	# add trailing '\' IF
		$next_am_file = $afile;
		$next_am_file .= ".am" if (has_no_extent($afile) && !is_am_file($afile));
		prt( "\nProcessing other [$next_am_file] in ($full_fil_path) ...\n" );
      process_am_file('Others', $afile, $main_root_dir) if !exclude_dir($afile);
		my $mk = mark_all_files(unix_2_dos($main_root_dir . $afile ));
		prtw( "WARNING: NOT FOUND IN MARK [".unix_2_dos($main_root_dir.$afile)."]!\n" ) if (!$mk);
	}
}

my $c_cnt = scalar @msvc_c_files;
my $t_cnt = scalar @msvc_titles;
my $h_cnt = scalar @msvc_h_files;
my $o_cnt = scalar keys(%other_projs);
prt( "c_cnt = $c_cnt, h_cnt = $h_cnt, t_cnt = $t_cnt, o_cnt = $o_cnt ...\n" );

extract_am_projects();

my $p_cnt = scalar keys(%am_projects);

prt( "Moment, generating inc_hash ... from $p_cnt AM projects ...\n" );
gen_inc_hash_am( $main_root_dir );
gen_inc_hash_others( $main_root_dir );
### show_inc_hash();
prt( "Done generating inc_hash ...\n" );

show_am_projects( $main_root_dir );
show_other_projects( $main_root_dir );

show_all_sources( "makefile.am set", $main_root_dir );

gen_temp_dsw( $main_root_dir );

if (@dspinfo) {
    prt( "\nRepeat show of ".scalar @dspinfo." lines of DSP info ...\n" );
    foreach my $di (@dspinfo) {
        prt("$di\n");
    }
}

pgm_exit($exit_status,"");


#######################################################################
##### all subs below

sub extract_am_projects {
   my ($file, @arr, $lib, $lsc);
	foreach $file (@msvc_c_files) {
		##prt( "$file\n" );
		@arr = split(/\|/ , $file);	# get Lib | Source | Title
		if (scalar @arr == 3) {
			$lib = $arr[0];
			$lsc = $arr[1];
			if (defined $am_projects{$lib}) {
				$am_projects{$lib} .= '|'.$lsc;
			} else {
				$am_projects{$lib} = $lsc;
			}
		} else {
			prtw( "WARNING: Did NOT split as EXPECTED!!! [$file] ...\n\n" );
		}
	}

   # 2009-06-09 - also extract the HEADERS
	foreach $file (@msvc_h_files) {
		##prt( "$file\n" );
		@arr = split(/\|/ , $file);	# get Lib | Source | Title
		if (scalar @arr == 3) {
			$lib = $arr[0];
			$lsc = $arr[1];
			if (defined $am_headers{$lib}) {
				$am_headers{$lib} .= '|'.$lsc;
			} else {
				$am_headers{$lib} = $lsc;
			}
		} else {
			prtw( "WARNING: Did NOT split as EXPECTED!!! [$file] ...\n\n" );
		}
	}
}


sub gen_inc_hash_others {
	my ($rd) = shift;
	my ($lsrcs, @arr, $s, $ind, $ff, $mk, $om);
	my ($in_file, $in_dir, $rel_src);
	my $out_file = '';
   my @incs = ();
	$last_dsp_file = '';
	foreach my $key (keys %other_projs) {
		$lsrcs = $other_projs{$key};  # extract LIST of SOURCES
		@arr = split(/\*/, $lsrcs);	# get sources
        foreach $s (@arr) {
            $ind = index($s, '$(top_srcdir)');
            $s = substr($s, $ind + 14) if ($ind >= 0);
            $s = fix_rel($s);
            $ff = $rd . $s;
            @incs = ();
            get_include_list($ff, \@incs);
            if (defined $inc_hash{$s}) {
                prtw( "WARNING: [$s] ALREADY IN inc_hash!\n" );
                $inc_hash{$s} .= join(' ', @incs);
            } else {
                $inc_hash{$s} = join(' ', @incs);
            }
		}
	}
}

sub gen_inc_hash_am {
   my ($rd) = shift;
	my ($s, $ff, $mk, $om);
   my ($lsrcs, @arr);
   my @incs = ();
	foreach my $key (keys %am_projects) {
		$lsrcs = $am_projects{$key};
		@arr = split(/\|/ , $lsrcs);	# Sources
        foreach $s (@arr) {
            $s = fix_rel($s);
            $ff = $rd . $s;
            @incs = ();
            get_include_list($ff, \@incs);
            if (defined $inc_hash{$s}) {
                prtw( "WARNING: [$s] ALREADY IN inc_hash!\n" );
                $inc_hash{$s} .= join(' ',@incs);
            } else {
                $inc_hash{$s} = join(' ',@incs);
            }
        }
	}
}

sub show_other_projects {
	my ($rd) = shift;
	my ($lsrcs, @arr, $s, $ind, $ff, $mk, $om);
	my ($in_file, $in_dir, $rel_src);
	my $out_file = '';
	$last_dsp_file = '';
	prt( "\nFound $o_cnt OTHER projects ...root = $rd\n" );
	foreach my $key (keys %other_projs) {
		$lsrcs = $other_projs{$key};
      # ONLY GENERATE TEMPORARY DSP FILE
      if ($write_temp_ds) {
         $out_file = 'temp.'.$key.'.dsp';
      } else {
         $out_file = $target_dir;
         $out_file .= "\\" if !($out_file =~ /(\\|\/)$/);
         $out_file .= $key.'.dsp';
      }
		push(@dspprojs, ".\\$key.dsp,$key");
		@arr = split(/\*/, $lsrcs);	# get sources SPLIT
        if ($one_line) {
            $lsrcs =~ s/\*/, /g;
            prt( "Project: $key = $lsrcs\n" );
        } else {
            prt( "Project: $key\n" );
        }
        foreach $s (@arr) {
            $ind = index($s, '$(top_srcdir)');
            $s = substr($s, $ind + 14) if ($ind >= 0);
            $s = fix_rel($s);
            $ff = $rd . $s;
            $mk = mark_all_files($ff);
            ($in_file, $in_dir) = fileparse($ff);
            #                             source, target
            #$rel_src = get_relative_path( $in_dir, $outdir );
            #$rel_src = get_relative_path_2( $in_dir, $outdir );
            $rel_src = get_relative_path_2( $in_dir, $target_dir );
            $rel_src .= $in_file;
            $rel_src = unix_2_dos($rel_src);
            if (is_c_source($s)) {
                if ($check4main2) {
                    $om = get_main_msg($ff,$s,0,$rel_src); # check if a main()
                } else {
                    $om = "C  $s ";
                    $om .= "(rs=$rel_src)" if ($add_rel_src);
                }
                $om .= "NOT FOUND IN MARK!" if (!$mk);
                prt("$om\n");
                if ($out_dsp2) {
                    # generate DSP file
                    out_dsp_file($key, $out_file, $rel_src, $rd, 0);
                }
            } else {
                $om = "H  $s ";
                $om .= "(rs=$rel_src)" if ($add_rel_src);
                $om .= "NOT FOUND IN MARK!" if (!$mk);
                prt("$om\n");
            }
		}
	}
	if ( length($last_dsp_file) ) {
		end_dsp_file($last_dsp_file);
	}
}

# need 'root' to do DSP/DSW
sub show_am_projects {
	my ($rd) = shift;
	prt( "\nFound $p_cnt LIBRARY projects ...root = $rd\n" );
	my $out_file = '';
	my ($in_file, $in_dir, $rel_src);
	my ($s, $ff, $mk, $om);
    my ($lsrcs, @arr);
	foreach my $key (keys %am_projects) {
		$lsrcs = $am_projects{$key};
		# ONLY GENERATE A TEMPORARY DSP FILE
      #$out_file = $outdir.$key.'.dsp';
      #$out_file = 'temp.'.$key.'.dsp';
      if ($write_temp_ds) {
         $out_file = 'temp.'.$key.'.dsp';
      } else {
         $out_file = $target_dir;
         $out_file .= "\\" if !($out_file =~ /(\\|\/)$/);
         $out_file .= $key.'.dsp';
      }
      # and store the relative actual DSP file name, for DSW generation
		push(@dspprojs, ".\\$key.dsp,$key");
		@arr = split(/\|/ , $lsrcs);	# Sources
		if ($one_line) {
			foreach $s (@arr) {
				$ff = fix_rel($rd . $s);
				$mk = mark_all_files($ff);
				if ($out_dsp) {
					($in_file, $in_dir) = fileparse($ff);
					#                             source, target
					#$rel_src = get_relative_path( $in_dir, $outdir );
					#$rel_src = get_relative_path_2( $in_dir, $outdir );
					$rel_src = get_relative_path_2( $in_dir, $target_dir );
					$rel_src .= $in_file;
					$rel_src = unix_2_dos($rel_src);
               # generate TEMP DSP file
					out_dsp_file($key, $out_file, $rel_src, $rd, 1);
				}
			}
			$lsrcs =~ s/\|/, /g;
			prt( "Project: $key = $lsrcs\n" );
		} else {
			prt( "Project: $key\n" );
			foreach $s (@arr) {
            $s = fix_rel($s);
				$ff = $rd . $s;
            ($in_file, $in_dir) = fileparse($ff);
            #                             source, target
            #$rel_src = get_relative_path( $in_dir, $outdir );
            #$rel_src = get_relative_path_2( $in_dir, $outdir );
            $rel_src = get_relative_path_2( $in_dir, $target_dir );
            $rel_src .= $in_file;
            $rel_src = unix_2_dos($rel_src);
				$mk = mark_all_files($ff);
            if ($check4main2) {
    				$om = get_main_msg($ff,$s,0,$rel_src); # check if a main()
            } else {
    				$om =  "  $s ";
               $om .= "(rs=$rel_src)" if ($add_rel_src);
            }
				if (!$mk) {
					$om .= " NOT FOUND in MARK!";
				}
				prt("$om\n");
				if ($out_dsp) {
               # genarate TEMP DSP file
					out_dsp_file($key, $out_file, $rel_src, $rd, 1);
				}
			}
		}
	}

	if ( length($last_dsp_file) ) {
		end_dsp_file($last_dsp_file);
		$last_dsp_file = '';
		$last_project = '';
	}
}

sub get_main_msg {
	my ($ffn,$sfn,$no,$rs) = @_;
	my $fom = " $sfn ";
    $fom .=  "(rs=$rs)" if ($add_rel_src);
	my @cm = ();
	if (chk_main($ffn,\@cm)) {
		while (length($fom) < $min_line) {
			$fom .= ' ';
		}
		$fom .= "HAS main()";
		my $cmc = scalar @cm;
		for (my $t = 0; $t < $cmc; $t++) {
			$fom .= "\n  ".$cm[$t][0].": ".$cm[$t][1] if ($show_main);
			if (length($cm[$t][2])) {
				if ($show_cond) {
					$fom .= " cond ".$cm[$t][2];
				} else {
					$fom .= " cond";
					last;
				}
			}
		}
	} elsif ($no) {
		while (length($fom) < $min_line) {
			$fom .= ' ';
		}
		$fom .= "NO main() FOUND";
	}
	return $fom;
}

sub scan_root_folder {
   my ($rd) = shift;
   $rd .= "\\" if !(substr($rd,-1,1) =~ /(\\|\/)/);
   my $ff = $rd;	
   $ff .= 'configure.ac';
   prt( "Scanning [$ff] file ...\n" );
   scan_one_configure_file($ff,$rd);
   $ff = $rd.'aclocal.m4';
   scan_one_configure_file($ff,$rd) if -f $ff;
   if (!@input_files) {
      prt( "Copying " . scalar @make_input_list . " make_input_list to input_files.\n" );
      @input_files = @make_input_list;
      %output_files = %make_list;
   } else {
      prt( "input_files has list of " . scalar @input_files . " ...\n" );
   }
	if ($dbg6) {
		my $cnt = scalar @input_files;
		prt( "\nOutput of $cnt input_files list ...\n" );
		$cnt = 0;
		foreach $ff (@input_files) {
			$cnt++;
			prt( "$cnt $ff\n" );
		}
		prt( "Done $cnt input_files list ...\n" );
	}
}

sub is_in_ac_done {
	my ($fil) = shift;
	foreach my $sd (@ac_scanned) {
		if( uc($fil) eq uc($sd) ) {
			return 1;
		}
	}
	return 0;
}

## my %config_ac_macros = ();
sub do_macro_sub2 {
	my ($item) = shift;
	prt( "Checking substitution for [$item] ...\n" ) if ($dbg27);
	if (defined $config_ac_macros{$item}) {	# if it is IN the MACROS
		my $ritem = $config_ac_macros{$item};	# extract the substitute value
		prt( "Found: returning [$ritem] ...\n" ) if ($dbg27);
		return $ritem;
	}
	return $item;
}


sub do_macro_sub {
	my ($item) = shift;
	if ($item =~ /^\$/) {
		my $msub = substr($item,1);	# remove leading '$'
		my $ritem = do_macro_sub2($msub);
		if ($ritem ne $msub) {
			return $ritem;
		}
	}
	return $item;
}

sub scan_one_configure_file {
   my ($filename, $rd) = @_;
	if( is_in_ac_done($filename) ) {
		return;
	}
   if (! open(CONFIGURE, $filename) ) {
      prtw( "WARNING: can't open '$filename': $!\n" );
      return;
   }

	push(@ac_scanned, $filename);
   prt( "Processing configure file - $filename ...\n" );
   my $in_ac_output = 0;
   my $ac_output_line = '';
   my $ff = '';
	my $fline = '';
	my $cline = '';
	my $acline = '';
	my $aclnum = 0;
	my %var_hash = ();
	my $key  = '';
	my $nval = '';
   while (<CONFIGURE>) {
		$fline = $_;	# get current file line
		$cline = $fline;	# copy current file line
		chomp $cline;
		$acline = trim_all($cline);
		$aclnum++;
		###prt( "$cline\n" );
		# Remove comments from current line.
		s/\bdnl\b.*$//;
		s/\#.*$//;
		$cline =~ s/\bdnl\b.*$//;
		$cline =~ s/\#.*$//;
		next if (length($cline) == 0);
		prt( "$cline\n" ) if ($dbg1);
		if ($cline =~ /^(\w+)="(\d+)"$/) {
			prt( "Num Variable $1=$2\n" ) if ($dbg3);
			$var_hash{$1} = $2;
		##} elsif ($cline =~ /^(\w+)="(.+)"$/) {
		##} elsif ($cline =~ /^(\w+)="{0,1}(.+)"{0,1}$/) {
		##} elsif ($cline =~ /^(\w+)=(.+)$/) {
		} elsif ($cline =~ /^\s*(\w+)=(.+)$/) {
			$key  = $1;
			$nval = $2;
			if (substr($nval,0,1) eq '"') {
				$nval = substr($nval,1, length($nval)-2);
			}
			prt( "MACRO [$key]=[$nval]\n" ) if ($dbg3a);
			if (index($nval,'.') != -1) {
				my @varr = split(/\./,$nval);
				$nval = '';
				my $vlen = scalar @varr;
				prt( "Split to $vlen components ...\n" ) if ($dbg3);
				for (my $i = 0; $i < $vlen; $i++) {
					my $ky = trim_line($varr[$i]);
					prt( "Conponent $ky\n" ) if ($dbg3);
					if (substr($ky,0,1) eq '$') {
						$ky = substr($ky,1);
						if (defined $var_hash{$ky}) {
							$nval .= '.' if (length($nval));
							$nval .= $var_hash{$ky};
						} else {
							$nval .= '.' if (length($nval));
							$nval .= $ky;
						}
					} else {
						$nval .= '.' if (length($nval));
						$nval .= $ky;
					}
				}
			}
			prt( "Available substitute [$key]=[$nval]\n" ) if ($dbg3);
			$var_hash{$key} = $nval;
			$config_ac_macros{$key} = $nval;
		} elsif ($cline =~ /^(\w+)=(.+)$/) {
			$key  = $1;
			$nval = $2;
			prtw( "WARNING: This should have already been processed!\nWARNING: Variable [$key]=[$nval] no quotes\n\n" );
		}

		# Skip macro definitions.  Otherwise we might be confused into
		# thinking that a macro that was only defined was actually
		# used.
		next if /AC_DEFUN/;

		# Follow includes.  This is a weirdness commonly in use at
		# Cygnus and hopefully nowhere else.
		if (/sinclude\((.*)\)/ && -f ($rd . $1)) {
			$ff = $rd . $1;
			scan_one_configure_file ($ff, $rd);
		}

		if (! $in_ac_output && ( s/AC_OUTPUT\s*\(\[?// || s/AC_CONFIG_FILES\s*\(\[?// ) ) {
			$in_ac_output = 1;
			$ac_output_line = $.;	# get LINE number
			prt( "Got ac_output_line = [$ac_output_line] ...\n" ) if ($dbg4);
		}

		if ($in_ac_output) {
			my $closing = 0;
			if (s/[\]\),].*$//) {
				$in_ac_output = 0;
				$closing = 1;
			}

			# Look at potential Makefile.am's
			foreach (split) {
				# Must skip empty string for Perl 4.
				next if $_ eq "\\" || $_ eq '';

				my ($local,$input,@rest) = split(/:/);
				if (! $input) {
					$input = $local;
				} else {
					$input =~ s/\.in$//;
				}
				$ff = $rd . $input . '.am';
				if (-f $ff) {
					prt( "Adding $input [$ff] to make_input_list ...\n" ) if ($dbg5);
					push(@make_input_list, $input);
					$make_list{$input} = join(':', ($local,@rest));
				} else {
					prt( "Adding $input [$ff] to other_input_files ...\n" ) if ($dbg5);
					# We have a file that automake should cause to be
					# rebuilt, but shouldn't generate itself.
					push (@other_input_files, $_);
				}
			}
		}

		# Handle configuration headers.  A config header of `[$1]'
		# means we are actually scanning AM_CONFIG_HEADER from
		# aclocal.m4.
		if (/A([CM])_CONFIG_HEADER\s*\((.*)\)/ && $2 ne '[$1]') {
			am_conf_line_error($filename, $., "automake requires AM_CONFIG_HEADER, not AC_CONFIG_HEADER")
					if $1 eq 'C';
			$config_header_line = $.;
			my ($one_hdr);
			foreach $one_hdr (split (' ', $2)) {
				push (@config_fullnames, $one_hdr);
				if ($one_hdr =~ /^([^:]+):(.+)$/) {
					push (@config_names, $1);
					push (@config_headers, $2);
				} else {
					push (@config_names, $one_hdr);
					push (@config_headers, $one_hdr . '.in');
				}
			}
		}

		if (/$AM_CONDITIONAL_PATTERN/o) {
			prt( "$aclnum: [$acline]\n" ) if ($dbg2a);
			if ( defined $cfg_defines{$1} ) {
				# has been DEFINED in am2dsp?.cfg file, or previously defined
				prt( "Storing configure_cond key $1 ... value=2\n" ) if ($dbg2);
				$configure_cond{$1} = 2;
			} else {
				prt( "Storing configure_cond key $1 ... value=1\n" ) if ($dbg2);
				$configure_cond{$1} = 1;
			}
		}

		if (/$AM_INIT_AUTOMAKE/o) {
			$dsp_package = $1;
			$dsp_version = $2;
			prt( "Got AM_INIT_AUTOMAKE($dsp_package, $dsp_version) ...\n" );

			$dsp_package = do_macro_sub($dsp_package);
			$dsp_version = do_macro_sub($dsp_version);
			prt( "Set DSP package = $dsp_package, DSP version = $dsp_version ...\n" );
		}
    }

    close(CONFIGURE);
}


sub am_error {
    my ($msg) = shift;
    prt( $msg."\n" );
	close_log($outfile,1);
	exit($exit_status);
}

sub am_conf_line_error {
	my (@args) = @_;
	am_error(join(' ',@args));
}

sub am_line_error {
    my ($symbol, @args) = @_;
    prt( "am_line_error: sym=[$symbol] arg0=[$args[0]] ...\n" );
    if ($symbol && "$symbol" ne '-1') {
		my ($file) = "$active_am_file";
		if ($symbol =~ /^\d+$/) {
			# SYMBOL is a line number, so just add the colon.
			$file .= ':' . $symbol;
		} elsif (defined $content_lines{$symbol}) {
			# SYMBOL is a variable defined in Makefile.am, so add the
			# line number we saved from there.
			$file .= ':' . $content_lines{$symbol};
		} elsif (defined $configure_vars{$symbol}) {
			# SYMBOL is a variable defined in configure.ac, so add the
			# appropriate line number.
			$file = $configure_vars{$symbol};
		} else {
			# Couldn't find the line number.
		}
		###warn $file, ": ", join (' ', @args), "\n";
		prt( "$file: ". join (' ', @args). "\n" );
		$exit_status = 1;
      prt( "ERROR EXIT!\n" );
		close_log($outfile,1);
		exit($exit_status);
    } else {
		am_error (join(' ',@args));
	}
}


sub initialize_per_input {
    # These two variables are used when generating each Makefile.in.
    # They hold the Makefile.in until it is ready to be printed.
    $output_vars = '';
    $output_trailer = '';

    # This holds the contents of a Makefile.am, as parsed by
    # read_am_file.
    %contents = ();

    # This holds the names which are targets.  These also appear in
    # %contents.
    %targets = ();

    # For a variable or target which is defined conditionally, this
    # holds an array of the conditional values.  The array is composed
    # of pairs of condition strings (the variables which configure
    # will substitute) and values (the value of a target is
    # meaningless).  For an unconditional variable, this is empty.
    %conditional = ();

    # This holds the line numbers at which various elements of
    # %contents are defined.
    %content_lines = ();

    # This holds a 1 if a particular variable was examined.
    %content_seen = ();

    # This is the conditional stack.
    @conditional_stack = ();

    # This holds the set of included files.
    @include_stack = ();

    # This holds the "relative directory" of the current Makefile.in.
    # Eg for src/Makefile.in, this is "src".
    $relative_dir = '';

    # This maps the source extension of a suffix rule to its
    # corresponding output extension.
    %suffix_rules = ();

	# per AM file sources
	@am_c_files = ();
	@am_h_files = ();
	@am_o_files = ();
}

sub unique_proj {
	my ($prj) = shift;
	if (defined $other_projs{$prj}) {
		my $nprj = $prj;
		my $i = 0;
		while (defined $other_projs{$nprj}) {
			$i++;
			$nprj = "$prj$i";
		}
		return $nprj;
	}
	return $prj;
}

sub get_lib_depends($) {
   my ($deps) = shift;  # = ($contents{$ky});
   # like bvhtest_LDADD = libsgbvh.a -lsgstructure -lsgmath $(base_LIBS)
   # [openal_test1]=[$(top_builddir)/simgear/debug/libsgdebug.a $(openal_LIBS)]
   my @arr = split(' ',$deps);
   my $srcs = '';
   my ($fi,$fil,$dir);
   foreach $fi (@arr) {
      $fi = substr($fi,16) if ($fi =~ /^\$\(top_builddir\)\//);   # remove top_builddir...
      next if ($fi =~ /^\$\(\w+\)$/);  # for the moment IGNORE '$(base_LIBS)', '$(openal_LIBS)'...
      ($fil,$dir) = fileparse($fi);    # only want filename at end
      if ($fil =~ /^lib(\w+)\.a$/) {
         $fil = $1;  # chop off 'lib(.+).a'
      } elsif ($fil =~ /^-l/) {
         $fil = substr($fil,2); # remove '-l' at front
      } else {
         prtw("WARNING: get_lib_depends: CHECKOUT this item [$fi] [$fil]!!!\n");
         $fil = '';
      }
      if (length($fil)) {
         next if ($fil eq 'z');        # forget libz
         next if ($fil eq 'stdc++');   # and stdc
         $srcs .= "*" if (length($srcs)); # add separator
         $srcs .= $fil; # and this item
      }
   }
   return $srcs;  # return LIST
}

# process for OTHER THAN LIBRARIES
sub process_other_sources {
	my ($mf, $rd) = @_;
	my ($nam, $dir, $ext) = fileparse( $mf, qr/\.[^.]*/ );
   my $rel_dir = dirname($mf);
	# like 'noinst_PROGRAMS = calc_loc'
	# and then 'calc_loc_SOURCES = calc_loc.cxx'
	my ($key, $projs, $proj, $proj2, $ky, $fnd, $skey, $fil, $srcs, $fi, $dnmf, $dndir, $uproj);
   my ($typ, $proj3);
   my $bas_dir = ".\\";
	my @dir_cont = ();
	my @proj_arr = ();
	my @proj_files = ();
	$fnd = 0;
	$dnmf = 0;
   foreach $key (keys %contents) {
		$projs = $contents{$key};	# get PROJECT name
		#if (($key eq 'noinst_PROGRAMS')||($key eq 'bin_PROGRAMS')) {
		if ( $key =~ /(.+)_PROGRAMS/) {
         $typ = $1;  # usually 'noinst' or 'bin', but ...
			@proj_arr = split(' ', $projs);	# split in case multiple
			foreach $proj (@proj_arr) {	# process each project, getting the SOURCES
				$proj2 = $proj;
				$proj2 =~ s/-/_/g;  # change hyphen (-) to underscore (_)
				foreach $ky (keys %contents) {
					if (($ky =~ /($proj)_SOURCES/) || ($ky =~ /($proj2)_SOURCES/)){
						$skey = $ky;
						$fnd = 1;
						last;
					}
				}
				if ($fnd) {
					@proj_files = split(' ', $contents{$skey});
					#prt( "Project: [$proj] - sources [".$contents{$skey}."]\n" );
					$srcs = '';
					foreach $fil (@proj_files) {
						if ($fil =~ /^\$\(([^\)]*)\)$/) {
							# Found a variable.
							my $varname = $1;
							if (defined $contents{$varname}) {
								my @f2 = split(' ', $contents{$varname});
								foreach $fi (@f2) {
									$srcs .= '*' if (length($srcs));
									$srcs .= unix_2_dos($bas_dir . $rel_dir . "\\" . $fi);
								}
							} else {
								$srcs .= '*' if (length($srcs));
								$srcs .= unix_2_dos($bas_dir . $rel_dir . "\\" . $fil);
							}
						} else {
							$srcs .= '*' if (length($srcs));
							$srcs .= unix_2_dos($bas_dir . $rel_dir . "\\" . $fil);
						}
					}
					prt( "Project: [$proj] - sources [$srcs]\n" ) if ($dbg21);
					$uproj = unique_proj($proj);
					$other_projs{$uproj} = $srcs;	# keep this PROJECT, and SOURCES (both c and h)
					find_am_includes( $proj, $uproj, $rd );
   				foreach $ky (keys %contents) {
	   				if (($ky =~ /($proj)_LDADD/) || ($ky =~ /($proj2)_LDADD/)){
                     $srcs = get_lib_depends($contents{$ky});
                     if (length($srcs)) {
                        $other_deps{$uproj} = $srcs;
                        prt("[dbg29] Found dependencies [$uproj]=[$srcs].\n") if ($dbg29);
                     }
				   		last;
					   }
				   }

				} else {
               prtw("WARNING: Using a LOOSE search, since [$proj] nor [$proj2]_SOURCES NOT FOUNT!\n");
                    # so, just search for SOURCES - may be a MACRO, which did not expand!!!
                    foreach $ky (keys %contents) {
                        if ($ky =~ /(.+)_SOURCES/) {
                            $proj3 = $1;    # maybe this is the PROJECT name
                            $skey = $ky;
                            $fnd = 1;
                            last;
                        }
                    }
                    if ($fnd) {
                        @proj_files = split(' ', $contents{$skey});
                        #prt( "Project: [$proj] - sources [".$contents{$skey}."]\n" );
                        $srcs = '';
                        foreach $fil (@proj_files) {
                            if ($fil =~ /^\$\(([^\)]*)\)$/) {
                                # Found a variable.
                                my $varname = $1;
                                if (defined $contents{$varname}) {
                                    my @f2 = split(' ', $contents{$varname});
                                    foreach $fi (@f2) {
                                        $srcs .= '*' if (length($srcs));
                                        $srcs .= unix_2_dos($bas_dir . $rel_dir . "\\" . $fi);
                                    }
                                } else {
                                    $srcs .= '*' if (length($srcs));
                                    $srcs .= unix_2_dos($bas_dir . $rel_dir . "\\" . $fil);
                                }
                            } else {
                                $srcs .= '*' if (length($srcs));
                                $srcs .= unix_2_dos($bas_dir . $rel_dir . "\\" . $fil);
                            }
                        }
                        prt( "Project: [$proj] - sources [$srcs]\n" ) if ($dbg21);
                        $uproj = unique_proj($proj3);
                        $other_projs{$uproj} = $srcs;	# keep this PROJECT, and SOURCES (both c and h)
                        find_am_includes( $proj3, $uproj, $rd );
                    } else {
                        # if we can FIND a C source in the folder OF THE SAME NAME
                        # as this project
                        if ( !$dndir ) {
                            prtw("WARNING: Even LOOSE search FAILED for [$proj] and [$proj2] READING DIRECTORY!\n");
                            $fil = $rd . $rel_dir;
                            if (opendir( DIR, $fil)) {
                                @dir_cont = readdir(DIR);
                                close DIR;
                                prt( "Found ".scalar @dir_cont." items in [$fil]\n" ) if ($dbg23);
                            } else {
                                prtw( "WARNING: FAILED to OPEN DIRECTORY [$fil] ...\n" );
                            }
                            $dndir = 1;
                        }
                        $srcs = '';
                        $fnd = 0;
                        if ( @dir_cont ) {
                            # check contents of local directory
                            foreach $fi (@dir_cont) {
                                next if (($fi eq '.')||($fi eq '..'));
                                $fil = $rd . $rel_dir . "\\" . $fi;
                                if ( is_c_source($fi) && (-f $fil)) {
                                    if ($fi =~ /$proj\..+/) {
                                        $srcs = unix_2_dos($rel_dir . "\\" . $fi);
                                        $fnd = 1;
                                        last;
                                    }
                                }
                            }
                        }
                        if ($fnd) {
                            prt( "Project: [$proj] - sources [$srcs]\n" ) if ($dbg21 || $dbg23);
                            $uproj = unique_proj($proj);
                            $other_projs{$uproj} = $srcs;	# keep this PROJECT
                            $fnd = 0;
                            find_am_includes( $proj, $uproj, $rd );
                        } else {
                            prt( "Processing file [$mf] ...\n" ) if (!$dnmf);
                            if ($proj =~ /^\@.+\@$/) {
                                $proj = substr($proj,1,length($proj)-2);
                                my $msub = do_macro_sub2($proj);
                                prtw( "WARNING: Project: @[$proj]@ = [$msub] - NO SOURCES FOUND???\n" );
                            } else {
                                prtw( "WARNING: Project: [$proj] - NO SOURCES FOUND???\n" );
                            }
                            $dnmf = 1;
                        }
                    }
				}
			} # for each $proj in @proj_arr - that is EACH PROGRAM
		}  # if ( $key =~ /(.+)_PROGRAMS/)
	}  # for each key in the Makefile.am file - key = something
}

sub replace_top_srcdir {
	my ($nam,$rd) = @_;
	if ($nam =~ /\$\(([^\)]*)\)/ ) {
		my $mac = $1;
		prt( "Checking [$mac] ...\n" ) if ($dbg25);
		if (($mac eq 'top_srcdir')  ||
			($mac eq 'top_builddir') ) {
			$nam =~ s/\$\(${mac}\)/$rd/;
			$nam = unix_2_dos($nam);
			$nam =~ s/\\{2}/\\/g;
			prt( "Substituted [$nam] ...\n" ) if ($dbg25);
		}
	}
	return $nam;
}

sub find_am_includes {
	my ( $proj, $group, $rd ) = @_;
	### FIND INCLUDES
	#################
	# like INCLUDES = -I$(top_srcdir)/include
	my $incs = '';
	my @files = ();
	my $cnt = 0;
	my $nam = '';
	my $mac = '';
	my $ii = 0;
	my $jj = 0;
	my $k2 = '';
	foreach $k2 (keys %contents) {
		if ($k2 eq 'INCLUDES') {
			$incs = $contents{$k2};
			last;
		}
	}
	$incs = trim_all($incs);
	if (length($incs)) {
		prt( "Got INCLUDES [$incs] ...\n" ) if ($dbg25);
		@files = split(' ',$incs);
		$cnt = scalar @files;
		for ($ii = 0; $ii < $cnt; $ii++) {
			$nam = $files[$ii];
			$nam =~ s/^-I//;
			$nam = replace_top_srcdir($nam,$rd);
			$files[$ii] = $nam;
		}
		# remove any DUPLICATES
		for ($ii = 0; $ii < $cnt; $ii++) {
			$nam = $files[$ii];
			if (length($nam)) {
				for ($jj = 0; $jj < $cnt; $jj++) {
					if ($ii != $jj) {
						if ($nam eq $files[$jj]) {
							$files[$jj] = '';
						}
					}
				}
			}
		}
		$incs = join(' ', @files);
	}
	$am_includes{$group} = $incs;
	prt( "[dbg26] For $group, saved INCLUDES [$incs]\n" ) if ($dbg26);

	# STATICCPPFLAGS = -DCURL_STATICLIB
	# CPPFLAGS = -DCURL_NO_OLDIES $(STATICCPPFLAGS)
	$incs = '';
	foreach $k2 (keys %contents) {
		if ($k2 eq 'CPPFLAGS') {
			$incs = $contents{$k2};
			last;
		}
	}
	$incs = trim_all($incs);
	if (length($incs)) {
		prt( "Got CPPFLAGS [$incs] ...\n" ) if ($dbg25);
		@files = split(' ',$incs);
		$cnt = scalar @files;
		for ($ii = 0; $ii < $cnt; $ii++) {
			$nam = $files[$ii];
			$nam =~ s/^-D//;
            if ($nam =~ /\$\(([^\)]*)\)/ ) {
				$mac = $1;
				prt( "Checking [$mac] ...\n" ) if ($dbg25);
				if (defined $contents{$mac}) {
					my $mac2 = $contents{$mac};
					$nam =~ s/\$\(${mac}\)/$mac2/;
					$nam =~ s/^-D//;
					prt( "Substituted [$nam] ...\n" ) if ($dbg25);
				}
			}
			$files[$ii] = $nam;
		}
		$incs = join(' ', @files);
	}
	prt( "[dbg26] For $group, saved CPPFLAGS [$incs]\n" ) if ($dbg26);
	$am_cppflags{$group} = $incs;
	# LIBDIR = $(top_builddir)/lib
	# LDADD = $(LIBDIR)/libcurl.la
	$incs = '';
	foreach $k2 (keys %contents) {
		#if ($k2 eq 'LDADD') {
		if ($k2 =~ /LDADD/) {
			$incs = $contents{$k2};
			last;
		}
	}
	$incs = trim_all($incs);
	if (length($incs)) {
		prt( "Got LDADD [$incs] ...\n" ) if ($dbg25);
		@files = split(' ',$incs);
		$cnt = scalar @files;
		for ($ii = 0; $ii < $cnt; $ii++) {
			$nam = $files[$ii];
			$nam =~ s/^-I//;
            if ($nam =~ /\$\(([^\)]*)\)/ ) {
				$mac = $1;
				prt( "Checking [$mac] ...\n" ) if ($dbg25);
				if (defined $contents{$mac}) {
					my $mac2 = $contents{$mac};
					$mac2 = replace_top_srcdir($mac2,$rd);
					$nam =~ s/\$\(${mac}\)/$mac2/;
					$nam = unix_2_dos($nam);
					if ($nam =~ /lib(\w+)\.la/) {
						$nam =~ s/lib(\w+)\.la/Lib_${1}\.lib/;
					}
					prt( "Substituted [$nam] ...\n" ) if ($dbg25);
				}
			}
			$files[$ii] = $nam;
		}
		$incs = join(' ', @files);
	}
	prt( "[dbg26] For $group, saved LDADD [$incs]\n" ) if ($dbg26);
	$am_libadds{$group} = $incs;

}

sub process_am_file {
   my ($dsp_name,$makefile,$rd) = @_;
   my $base_dir = './';
   my $dsp_file = $rd . $dsp_name . '.dsp';
   prt( "process_am_file: using dsp_name=$dsp_file, and makefile=$makefile ...\n" ) if $dbg7;

   initialize_per_input();

   ###my $relative_dir = dirname($makefile);
   $relative_dir = dirname($makefile);
   my $dupe = 0;
   my @files = ();
   my $key = '';
   my $group = 'Lib_Special';
   my $file = '';	# full path to file
	my $fi = '';	# filename only
	my $ext = '';	# and its extension
	my $dir = '';	# directory it is in
	my $nam = '';	# file title
	my $proj = '';
	my $cnt = 0;
   my $fnd = 0;
   my $gotsubd = 0;
   my $gotinch = 0;
	# build AM file name
	my $file_am = $rd . $makefile;
	# 04/04/2008 READ 'configure.ac' if present in THIS DIRECTORY
	# ===========================================================
	my $file_ac = $full_fil_path . "configure.ac";
	if ( -f $file_ac ) {
		# we have an configure.ac file here
		scan_one_configure_file( $file_ac, $rd );
	}
	$file_am .= '.am' if !($file_am =~ /\.am$/i);
	$current_am_file = sub_common_folder($file_am,$rd);
	prt( "Processing AM file [$current_am_file]\n" ) if ($dbg24);
	push(@done_am,$file_am);
   read_main_am_file($file_am, $rd);

   # preview the 'contents' to find 'group'
   if ($dbg28 || $dbg19) {
        $cnt = scalar keys(%contents);
        prt( "CT: Got $cnt items in \%contents ... file $file_am ...\n" );
        $cnt = 0;
        foreach $key (keys %contents) {
            $file = trim_all($contents{$key});
            $cnt++;
            prt( "CT:$cnt: [$key] = [$file]\n" );
        }
   }

   ###############################################################
   ## process various contents of %contents
   ##############################################################
   # use 'noinst_LIBRARIES' macro
   foreach $key (keys %contents) {
        #if ($key eq 'noinst_LIBRARIES') {
        if ($key =~ /\s*([\w-]+)_LIBRARIES\s*/) {
            @files = split(' ', $contents{$key});
            foreach $file (@files) {
                if ($file =~ /^lib([\w-]+)\.a/) {
                    $group = 'lib' . $1;
                    prt( "Group set to [$group] ... file $file_am ...\n" ) if ($dbg8 || $dbg8g);
                    $fnd = 1;
                    last;
                }
            }
        } elsif ($key eq 'SUBDIRS') {
           $gotsubd++;
        } elsif ($key eq 'include_HEADERS') {
           $gotinch++;
        }
   }
   if (!$fnd) {
      # found NOT LIBRARY item
      # try for check_PROGRAMS or bin_PROGRAMS
      foreach $key (keys %contents) {
         if ($key =~ /\s*([\w-]+)_PROGRAMS\s*/) {
            $nam = $1;
            @files = split(' ', $contents{$key});
            foreach $file (@files) {
               if ($file =~ /^([\w-]+)$/) {
                  ###$group = $nam.'_'. $1;
                  $group = $1;
                  prt( "Group set to [$group] ... file $file_am ...\n" ) if ($dbg8 || $dbg8g);
                  $fnd = 1;
                  last;
               }
            }
         }
      }
   }
   # 10/02/2010 - NO warning IF it has a 'SUBDIRS' entry
   if (!$fnd && ($gotsubd == 0) && ($gotinch == 0)) {
   #if (!$fnd) {
      prtw( "WARNING: NO 'GROUP' FOUND in $file_am ...\n" );
   } else {

   }

    # now extract LIBRARY SOURCES from 'contents' hash
   foreach $key (sort keys %contents) {
       #if ($key eq "include_HEADERS") {
       #} # not found in Makefile.am files?
       #if ($key =~ /^lib(.*)_a_SOURCES/) {
       #if (($key =~ /^lib(.*)_a_SOURCES/)  ||
		 #	($key =~ /^lib(.*)_la_SOURCES/) ||
		 #	($key =~ /^libc(.*)_la_SOURCES/) ) {
       if (($key =~ /lib(.*)_a_SOURCES/)  ||
           ($key =~ /lib(.*)_la_SOURCES/) ||
           ($key =~ /libc(.*)_la_SOURCES/) ) {
          $proj = $1;		# keep Project RAW name
          $group = 'lib' . $proj;	# generate a GROUP
          @files = split(' ', $contents{$key});
          foreach (@files) {
             my $src_dir = $base_dir . $relative_dir . '/';
             $src_dir =~ s/\//\\/g; # fixup DOS path separators
             if (/^\$\(([^\)]*)\)$/) {
                # Found a variable.
                my $varname = $1;
                prt( "Expanding variable $varname ...\n" ) if ($dbg8);
                foreach (split(' ', $contents{$varname})) {
                   $file = $src_dir . $_;
                   $dupe = add_2_source_list( $file, $group );
                }
                prt( "Done expanding variable $varname ...\n" ) if ($dbg8);
             } else {
                $file = $src_dir . $_;
                $dupe = add_2_source_list( $file, $group );
             }
          }
          find_am_includes( $proj, $group, $rd );
       } elsif ($key eq "fgfs_SOURCES") {
          #elsif ($key =~ /(.*)_SOURCES/) {
          $group = 'main';
          @files = split(' ', $contents{$key});
          foreach $fi (@files) {
             my $sdir = $base_dir . $relative_dir . '/';
             $sdir =~ s/\//\\/g; # fixup DOS path separators
             $file = $sdir . $fi;
             $dupe = add_2_source_list( $file, $group );
          }
       } else {
          # key is NOT ( Lib(.*)_a_SOURCES fgfs_SOURCES )
          if (($key eq 'EXTRA_DIST')||($key eq 'fgjs_SOURCES')) {
             @files = split(' ', $contents{$key});
             foreach $fi (@files) {
                ($nam, $dir, $ext) = fileparse( $fi, qr/\.[^.]*/ );
                $file = $base_dir . $relative_dir . '/' . $fi;
                $file =~ s/\//\\/g; # use DOS path sep
                if (is_c_source_ext($ext)) {
                   push(@msvc_c_extra, $file);
                   prt( "Added [$file] to C EXTRA list\n" ) if $dbg9;
                } elsif (is_h_source_ext($ext)) {
                   push(@msvc_h_extra, $file);
                   prt( "Added [$file] to H EXTRA list\n" ) if $dbg9;
                } else {
                   push(@msvc_o_extra, $file);
                   prt( "Added [$file] to other EXTRA list\n" ) if $dbg9;
                }
             }
          } elsif ($key eq 'noinst_HEADERS') {
             # JSBSim has put the HEADERS in HERE,
             # FIX20061107
             # more recently SimGear put some 'source' file in here
             # which are now discarded
             @files = split(' ', $contents{$key});
             foreach $fi (@files) {
                ($nam, $dir, $ext) = fileparse( $fi, qr/\.[^.]*/ );
                if (is_h_source_ext($ext)) { # FIX20061107 - only IFF a header
                  $file = $base_dir . $relative_dir . '/' . $fi;
						$file =~ s/\//\\/g; # use DOS path sep
						$dupe = add_2_source_list( $file, $group );
                }
             }
          }
       }
   }
   
	# process for OTHER THAN LIBRARIES
	process_other_sources( $makefile . '.am', $rd );

	# per AM file sources
	my $ac_cnt = scalar @am_c_files;
	my $ah_cnt = scalar @am_h_files;
   my $of_cnt = scalar @am_o_files;
   ###prt( "End am file: $dsp_file - ac_cnt=$ac_cnt, ah_cnt=$ah_cnt, from $makefile.am ...\n" ) if ($dbg20);
   prt( "End_am_file: ac_cnt=$ac_cnt, ah_cnt=$ah_cnt, of_cnt=$of_cnt, from $makefile ...\n" ) if ($dbg20);

}


sub read_main_am_file {
    my ($amfile, $rd) = @_;
    read_am_file($amfile, $rd);
    my @topdir = ();
    foreach (split(/\//, $relative_dir)) {
        next if $_ eq '.' || $_ eq '';
        if ($_ eq '..') {
            pop @topdir;
        } else {
            push(@topdir, '..');
        }
    }
    @topdir = ('.') if ! @topdir;
    my $top_builddir = join('/', @topdir);
}

sub read_am_file {
    my ($am_file, $rd) = @_;
	$active_am_file = $am_file;
    ###open( AM_FILE, $am_file ) or mydie( "ERROR: Can't open $am_file: $!\n" );
	###if ($am_file =~ /^(\\|\/){1}/) {
	###	$am_file = substr($am_file,1);
	###}
    if (!open( AM_FILE, $am_file )) {
		prtw( "ERROR: Can't open $am_file: $! \n" );
		return;
	}
    prt( "\n$pack: reading $am_file (rel_dir=$relative_dir)\n" ) if ($dbg10 || $dbg16 || $dbg19);
	my @am_lines = <AM_FILE>;
	close AM_FILE;
	my $am_lncnt = scalar @am_lines;
	my ($am_name, $am_path) = fileparse($am_file);
    my $saw_bk = 0;
    my $was_rule = 0;
    my $spacing = '';
    my $comment = '';
    my $last_var_name = '';
    my $blank = 0;
    my $cond_true = 2; # undetermined ...
	my $msg = '';
	my $aml = '';
	my $aln = 0;
	my $ai = 0;
	my $cond_string = '';
	# process, line by line ... where $aln is current line
    for ($ai = 0; $ai < $am_lncnt; $ai++) {
		$_ = $am_lines[$ai];
		$aln = $ai + 1;
        if (/$IGNORE_PATTERN/o) {
            # Merely delete comments beginning with two hashes.
        } elsif (/$WHITE_PATTERN/o) {
            # Stick a single white line before the incoming macro or rule.
            $spacing = "\n";
            $blank = 1;
        } elsif (/$COMMENT_PATTERN/o) {
            # Stick comments before the incoming macro or rule.  Make
            # sure a blank line preceeds first block of comments.
            $spacing = "\n" unless $blank;
            $blank = 1;
            $comment .= $spacing . $_;
            $spacing = '';
        } else {
            last;
        }
    }

    $output_vars .= $comment . "\n";
    $comment = '';
    $spacing = "\n";
    my $source_suffix_pattern = '';

    my $is_ok_macro = 0;
	for ( ; $ai < $am_lncnt; $ai++) {
		$_ = $am_lines[$ai];
		$aln = $ai + 1;
		$aml = $am_lines[$ai];
        chomp $aml;
        $aml = trim_all($aml); # clean up line
        prt( "$aln [$aml]\n" ) if ($dbg10);
        $_ .= "\n" unless substr ($_, -1, 1) eq "\n";
        if (/$IGNORE_PATTERN/o) {
            # Merely delete comments beginning with two hashes.
        } elsif (/$WHITE_PATTERN/o) {
            # Stick a single white line before the incoming macro or rule.
            $spacing = "\n";
            ###am_line_error ($., "blank line following trailing backslash") if $saw_bk;
            am_line_error ($aln, "blank line following trailing backslash") if $saw_bk;
        } elsif (/$COMMENT_PATTERN/o) {
            # Stick comments before the incoming macro or rule.
            $comment .= $spacing . $_;
            $spacing = '';
            ###am_line_error ($., "comment following trailing backslash") if $saw_bk;
            am_line_error ($aln, "comment following trailing backslash") if $saw_bk;
        } elsif ($saw_bk) {
			# continuation of previous line(s) ...
            if ($was_rule) {
                $output_trailer .= join ('', @conditional_stack) . $_;
                $saw_bk = /\\$/;
            } else {
                $saw_bk = /\\$/;
                # Chop newline and backslash if this line is
                # continued.  ensure trailing whitespace exists.
                chop if $saw_bk;
                chop if $saw_bk;
				# decide if to be added to 'contents' ...
				my $add_it = 0; # assume NO
				my $disc_it = 0;	# also NO
                if ( defined $contents{$last_var_name} ) {
                    if ( @conditional_stack ) {
                        if ( $conditional_stack[$#conditional_stack] =~ /_TRUE\@$/ ) {
                            # we are in the if TRUE state
                            if ($cond_true == 1) {
								$add_it = 1;
							} else {
								$disc_it = 1;
							}
						} else {
							# we are in a FALSE state
                            if ($cond_true != 1) {
								$add_it = 1;
							} else {
								$disc_it = 1;
							}
						}
					} else { 
						###prt( "No conditional stack for this macro! lv=[$last_var_name] ".
						###	"add_it\n" ) if ($dbg11);
						$add_it = 1; # so add it
					}
				} else { # NO contents for this var
					prt( "NO contents for [$last_var_name]! ".
						"Technical this is an ERROR, but for now add_it...\n" ) if ($dbg11);
					$add_it = 1;
				}

				if ($add_it) {
					$contents{$last_var_name} .= ' ' unless $contents{$last_var_name} =~ /\s$/;
					$contents{$last_var_name} .= $_;
					if (@conditional_stack) {
						$conditional{$last_var_name} .= quote_cond_val ($_);
					}
					prt( "End $last_var_name = [".trim_line($contents{$last_var_name})."]...\n" ) if ( ! $saw_bk && $dbg12);
				} else {
					if ($dbg22 || !$disc_it) {
						# that is, if NOT a deliberate discard
						###push(@warnings, "WARNING: File [$am_file], line $., last var [$last_var_name]");
						push(@warnings, "WARNING: File [$am_file], line $aln, last var [$last_var_name]");
						push(@warnings, " Discarding the [$_] ... CHECK ME");
						if ( defined $contents{$last_var_name} ) {
							if ( @conditional_stack ) {
								push(@warnings, " stack = $conditional_stack[$#conditional_stack] cond_true=$cond_true");
							}
						}
						prtw( "WARNING: Discarding this [$_] ... CHECK ME!\n" );
					}
				}
            }
        } elsif (/$IF_PATTERN/o) {
            if ( defined $configure_cond{$1} ) {
                ###if ( defined $cfg_defines{$1} )
                if ($configure_cond{$1} == 2) {
                    prt( "Found if $1, and condition is TRUE (see cfg_defines) ...\n" ) if ($dbg13);
                    $cond_true = 1;
                } else {
                    prt( "Found if $1, but NOT in cfg_defines ...\n" ) if ($dbg13);
                    $cond_true = 0;
                }
            } else {
               # 2009-06-09 - change to a WARNING only
               ###am_line_error ($aln, "Line $aln: [$aml]\n[$1] does not appear in AM_CONDITIONAL!");
               prtw("WARNING: Line $aln: [$aml] [$1] does not appear in AM_CONDITIONAL!\n");
            }
            push (@conditional_stack, "\@" . $1 . "_TRUE\@");
        } elsif (/$ELSE_PATTERN/o) {
            if (! @conditional_stack) {
                ###am_line_error ($., "else without if");
                am_line_error ($aln, "else without if");
            } elsif ($conditional_stack[$#conditional_stack] =~ /_FALSE\@$/) {
                ###am_line_error ($., "else after else");
                am_line_error ($aln, "else after else");
            } else {
                $conditional_stack[$#conditional_stack] =~ s/_TRUE\@$/_FALSE\@/;
            }
        } elsif (/$ENDIF_PATTERN/o) {
            if (! @conditional_stack) {
                ###am_line_error ($., ": endif without if");
                am_line_error ($aln, ": endif without if");
            } else {
                pop @conditional_stack;
            }
            $cond_true = 2; # undetermined ...
        } elsif (/$RULE_PATTERN/o) {
            # Found a rule.
            $was_rule = 1;
            if (defined $contents{$1}
                && (@conditional_stack
                ? ! defined $conditional{$1}
                : defined $conditional{$1})) {
                am_line_error ($1, "$aln: $1 if RULE_PATTERN defined both conditionally and unconditionally");
            }
            # Value here doesn't matter; for targets we only note
            # existence.
            $contents{$1} = 1;
            $targets{$1} = 1;
            $cond_string = join ('', @conditional_stack);
            if (@conditional_stack) {
                if ($conditional{$1}) {
                    check_ambiguous_conditional ($1, $cond_string);
                    $conditional{$1} .= ' ';
                } else {
                    $conditional{$1} = '';
                }
                $conditional{$1} .= $cond_string . ' 1';
            }
            ###$content_lines{$1} = $.;
            $content_lines{$1} = $aln;
            $output_trailer .= $comment . $spacing . $cond_string . $_;
            $comment = $spacing = '';
            $saw_bk = /\\$/;

            # Check the rule for being a suffix rule. If so, store in
            # a hash.

            my $source_suffix;
            my $object_suffix;

            if (($source_suffix, $object_suffix) = ($1 =~ $SUFFIX_RULE_PATTERN)) {
              $suffix_rules{$source_suffix} = $object_suffix;
              prt( "Sources ending in .$source_suffix become .$object_suffix\n" ) if $dbg17;
              $source_suffix_pattern = "(" . join('|', keys %suffix_rules) . ")";
            }

            # FIXME: make sure both suffixes are in SUFFIXES? Or set
            # SUFFIXES from suffix_rules?
        } elsif (($is_ok_macro = /$MACRO_PATTERN/o) || /$BOGUS_MACRO_PATTERN/o) {
            prt( "line $aln: Found a macro definition. 1[$1] 2[$2] 3[$3] ...\n" ) if ($dbg14);
            $was_rule = 0;
            $last_var_name = $1;
            my $type = $2;
            my $value = $3;
            if (defined $contents{$1} &&
               (@conditional_stack ? !defined $conditional{$1} : defined $conditional{$1})) {
               prt( "POTENTIAL ERROR: Found a macro definition. 1[$last_var_name] 2[$type] 3[$value] ...\n" );
               if (@conditional_stack) {
                  prt( "Conditional stack: $conditional_stack[-1]" );
               } else {
                  prt( "NO Conditional stack: " );
               }
               if (defined $conditional{$last_var_name}) {
                  prt( " Value = $conditional{$last_var_name}" );
               } else {
                  prt( " No conditional" );
               }
               prt(" IGNORED\n");
               #am_line_error ($last_var_name, "$last_var_name $aln: if MACRO_PATTERN defined both conditionally and unconditionally");
            }
            if (($value ne '') && (substr($value, -1) eq "\\")) {
                $value = substr($value, 0, length($value) - 1);
            }
            #else {
            #    $value = $3;
            #}
            if ($type eq '+') {
                if (! defined $contents{$last_var_name}
                    && defined $configure_vars{$last_var_name}) {
                    $contents{$last_var_name} = '@' . $last_var_name . '@';
                }
                $contents{$last_var_name} .= ' ' . $value;
            } else {
                if ( defined $contents{$last_var_name} ) {
                    if ( @conditional_stack ) {
                        if ( $conditional_stack[$#conditional_stack] =~ /_TRUE\@$/ ) {
                            # we are in the if TRUE state
                            if ($cond_true == 1) {
                                if ($dbg15) {
                                    if ( $contents{$last_var_name} ne $value ) {
                                        ###    " replaced with [$value] (am_file=$am_file line=$.)".
                                        prt( "NB: [$last_var_name] = [$contents{$last_var_name}]" .
                                            " replaced with [$value] (am_file=$am_file line=$aln)".
											(/\\$/ ? "saw_bk" : "end") . "\n" );
                                    }
                                }
                                $contents{$last_var_name} = $value;
                                ###$content_lines{$last_var_name} = $.;
                                $content_lines{$last_var_name} = $aln;
                            }
                        } else {
                            # we are in the else FALSE state
                            if ($cond_true != 1) {
                                if ($dbg15) {
                                    if ( $contents{$last_var_name} ne $value ) {
                                        ###   " replaced with [$value] (am_file=$am_file line=$.)".
                                        prt( "NB: [$last_var_name] = [$contents{$last_var_name}]" .
                                            " replaced with [$value] (am_file=$am_file line=$aln)".
											(/\\$/ ? "saw_bk" : "end") . "\n" );
                                    }
                                }
                                $contents{$last_var_name} = $value;
                                ###$content_lines{$last_var_name} = $.;
                                $content_lines{$last_var_name} = $aln;
                            }
                        }
                    }
                } else {
                    ###    "(am_file=$am_file line=$.) ".
                    prt( "First setting [$last_var_name] = [$value] " .
                        "(am_file=$am_file line=$aln) ".
						(/\\$/ ? "saw_bk" : "end") . "\n" ) if ($dbg15);
                    $contents{$last_var_name} = $value;
                    # The first assignment to a macro sets the line
                    # number.  Ideally I suppose we would associate line
                    # numbers with random bits of text.
                    ###$content_lines{$last_var_name} = $.;
                    $content_lines{$last_var_name} = $aln;
                }
            }
            $cond_string = join ('', @conditional_stack);
            if (@conditional_stack) {
                my $found = 0;
                my $val;
                if ($conditional{$last_var_name}) {
                    if ($type eq '+') {
                        # If we're adding to the conditional, and it
                        # exists, then we might want to simply replace
                        # the old value with the new one.
                        my (@new_vals, @cond_vals);
                        @cond_vals = split (' ', $conditional{$last_var_name});
                        while (@cond_vals) {
                            my $vcond = shift (@cond_vals);
                            push (@new_vals, $vcond);
                            if (conditional_same ($vcond, $cond_string)) {
                                $found = 1;
                                $val = (unquote_cond_val (shift (@cond_vals)) . ' ' . $value);
                                push (@new_vals, quote_cond_val ($val));
                            } else {
                                push (@new_vals, shift (@cond_vals));
                            }
                        }
                        if ($found) {
                            $conditional{$last_var_name} = join (' ', @new_vals);
                        }
                    }

                    if (! $found) {
                        check_ambiguous_conditional ($last_var_name, $cond_string);
                        $conditional{$last_var_name} .= ' ';
                        $val = $value;
                    }
                } else {
                    $conditional{$last_var_name} = '';
                    $val = $contents{$last_var_name};
                }
                if (! $found) {
                    $conditional{$last_var_name} .= ($cond_string . ' ' . quote_cond_val ($val));
                }
            }
            # FIXME: this doesn't always work correctly; it will group
            # all comments for a given variable, no matter where
            # defined.
            $am_vars{$last_var_name} = $comment . $spacing;
            $def_type{$last_var_name} = ($type eq ':') ? ':' : '';
            push (@var_list, $last_var_name);
            $comment = $spacing = '';
            $saw_bk = /\\$/;

            # Error if bogus.
            ###am_line_error ($., "bad macro name \`$last_var_name'") if ! $is_ok_macro;
            am_line_error ($aln, "bad macro name \`$last_var_name'") if ! $is_ok_macro;
        } elsif (/$INCLUDE_PATTERN/o) {
			my $orgpath = $1;
			my $path = $orgpath;
			if ($path =~ s/^\$\(top_srcdir\)\///) {
				push (@include_stack, "\$\(top_srcdir\)/$path");
			} else {
				$path =~ s/\$\(srcdir\)\///;
				push (@include_stack, "\$\(srcdir\)/$path");
				$path = $relative_dir . "/" . $path;
			}
			if ($path =~ /^\//) {
				$path = substr($path,1);
			}
			if (! -f $path) {
				my $ff = $relative_dir . "/" . $path;
				if (-f $ff) {
					$path = $ff;
				} else {
					$ff = $rd . $path;
					if (-f $ff) {
						$path = $ff;
					} else {
						prtw( "WARNING: in [$am_file] - Include WILL FAIL! [$orgpath] ...\n" );
						prtw( "  Last try was $ff ...\n" );
					}
				}
			}
			read_am_file ($path, $rd);
			$active_am_file = $am_file;
        } else {
            # This isn't an error; it is probably a continued rule.
            # In fact, this is what we assume.
            $was_rule = 1;
            $output_trailer .= ($comment . $spacing
                    . join ('', @conditional_stack) . $_);
            $comment = $spacing = '';
            $saw_bk = /\\$/;
        }
        ###$_ = <AM_FILE>;
    }

    prt( "Done reading of $am_file\n" ) if ($dbg10 || $dbg16 || $dbg19);
    ###close(AM_FILE);
    $output_trailer .= $comment;

    am_error ("unterminated conditionals: " . join (' ', @conditional_stack)) if (@conditional_stack);
}


sub add_2_source_list {
    my ($fil, $grp) = @_;
	my ($ft, $dir, $fe) = fileparse( $fil, qr/\.[^.]*/ );
    my $src = ($grp . '|' . $fil . '|' . $ft);
    my $ret = 0;
    if ( is_c_source_ext($fe) ) {
        prt( "Add $src to C list\n" ) if $dbg18;
        push(@msvc_c_files, $src);
        foreach my $tt (@msvc_titles) {
            # just to deal with duplicate names
            if( $tt eq $ft ) {
                $ret = 1;
                last;
            }
        }
        push(@msvc_titles, $ft);
		push(@am_c_files, $src);
    } elsif ( is_h_source_ext($fe) ) {
        prt( "Add $src to H list\n" ) if $dbg18;
        push(@msvc_h_files, $src);
		push(@am_h_files, $src);
    } else {
        prt( "Add $src to OTHER list\n" ) if $dbg18;
        push(@msvc_o_files, $src);
		push(@am_o_files, $src);
    }
    return $ret;
}

# Quote a value in order to put it in $conditional.  We need to quote
# spaces, and we need to handle null strings, so that we can later
# retrieve values by splitting on space.
sub quote_cond_val
{
    my ($val) = @_;
    $val =~ s/ /\001/g;
    $val =~ s/\t/\003/g;
    $val = "\002" if $val eq '';
    return $val;
}

# this function seems MISSING - maybe never used!
sub unquote_cond_val {
    my ($val) = @_;
    $val =~ s/\001/ /g;
    $val =~ s/\003/\t/g;
    $val = '' if $val eq "\002";
    return $val;
}

# See if a conditional is true.  Both arguments are conditional
# strings.  This returns true if the first conditional is true when
# the second conditional is true.
sub conditional_true_when {
    my ($cond, $when) = @_;

    # Check the easy case first.
    if ($cond eq $when) {
		return 1;
    }

    # Check each component of $cond, which looks @COND1@@COND2@.
    foreach my $comp (split ('@', $cond)) {
		# The way we split will give null strings between each condition.
		next if ! $comp;
		if (index ($when, '@' . $comp . '@') == -1) {
			return 0;
		}
    }
    return 1;
}

# Check for an ambiguous conditional.  This is called when a variable
# or target is being defined conditionally.  If we already know about
# a definition that is true under the same conditions, then we have an
# ambiguity.
sub check_ambiguous_conditional {
    my ($var_name, $cond) = @_;
    my (@cond_vals) = split (' ', $conditional{$var_name});
    while (@cond_vals) {
		my ($vcond) = shift (@cond_vals);
		shift (@cond_vals);
		if (conditional_true_when ($vcond, $cond) || conditional_true_when ($cond, $vcond)) {
			prt( "$var_name multiply defined in condition\n" );
		}
    }
}

sub trim_line {
    my ($l) = shift;
    chomp $l;
    $l =~ s/\r$//; # and remove CR, if present
    $l =~ s/\t/ /g; # tab to space
    $l =~ s/\s\s/ /g while ($l =~ /\s\s/);	# double space to single
    for ($l) {	# trim leading and trailing spaces
        s/^\s+//;
        s/\s+$//;
    }
    return $l;
}

sub is_c_source {
	my ($f) = shift;
	if ( ($f =~ /\.c$/i) || ($f =~ /\.cpp$/i) || ($f =~ /\.cxx$/i) ||
		 ($f =~ /\.inl$/i) || ($f =~ /\.cc$/i) ) {
		return 1;
	}
	return 0;
}

sub is_h_special {
	my ($f) = shift;
	if (($f =~ /osg/i)||($f =~ /OpenThreads/i)||($f =~ /Producer/i)) {
		return 1;
	}
	return 0;
}

sub is_h_source {
	my ($f) = shift;
	if ( ($f =~ /\.h$/i) || ($f =~ /\.hpp$/i) || ($f =~ /\.hxx$/i) ) {
		return 1;
	}
	return 0;
}

sub is_dsw_file {
	my ($f) = shift;
	if ( ($f =~ /\.dsw$/i) || ($f =~ /\.dsp$/i) ) {
		return 1;
	}
	return 0;
}

sub is_sln_file {
	my ($f) = shift;
	if ( ($f =~ /\.sln$/i) || ($f =~ /\.vcproj$/i) ) {
		return 1;
	}
	return 0;
}

sub is_ch_source {
	my ($f) = shift;
	if (is_c_source($f) || is_h_source($f)) {
		return 1;
	}
	return 0;
}

sub is_my_type {
	my ($f) = shift;
	if (is_c_source($f)) {
		return $TYPE_C;
	} elsif (is_h_source($f)) {
		return $TYPE_H;
	} elsif (is_dsw_file($f)) {
		return $TYPE_DSW;
	} elsif (is_sln_file($f)) {
		return $TYPE_SLN;
	} elsif ($f =~ /^makefile\.am$/i) {
		return $TYPE_AM;
	} elsif ($f =~ /makefile/i) {
		return $TYPE_MAK;
	}
	return $TYPE_NONE;
}

sub mark_all_files {
	my ($f) = shift;
	my $lcf = lc($f);
	#                   0    1    2  3     4
	# push(@all_files, [$df, $ff, 0, $typ, $rd]) if ($typ);
	my $ac = scalar @all_files;
	for (my $i = 0; $i < $ac; $i++) {
		my $tf = lc($all_files[$i][1]);
		if ($tf eq $lcf) {
			my $ct = $all_files[$i][2];
			$ct++;
			$all_files[$i][2] = $ct;
			return 1;
		}
	}
	return 0;
}

sub show_all_sources {
	my ($msg,$rd) = @_;
	my $ac = scalar @all_files;
	# push(@all_files, [$df, $ff, 0, $typ, $rd]) if ($typ);
	my ($mc, $i, $file, $line, $omsg);
	$mc = 0;
	my @files = ();
	my ($nam, $dir, $ext);
	# get count of MISSING items
	for ($i = 0; $i < $ac; $i++) {
		if ($all_files[$i][3] == $TYPE_C) {
			if ($all_files[$i][2] == 0) {
				$mc++;
			}
		}
	}
	if ($mc) {
		prt( "\nSources found, but MISSED - $mc ...root = $rd\n" );
		for ($i = 0; $i < $ac; $i++) {
			if ($all_files[$i][3] == $TYPE_C) {
				if ($all_files[$i][2] == 0) {
					$file = $all_files[$i][1];
					$omsg = " ".sub_common_folder($file, $rd)." ";
					$omsg = get_main_msg($file,sub_common_folder($file,$rd),1,"") if ($check4main2); # check if a main()
					prt( "$omsg\n" );
					push(@files, $file) if ($recheck_am);
				}
			}
		}
		prt( "Above $mc Sources NOT INCLUDED in $msg ...\n\n" );
		if ($recheck_am) {
			my @makes = get_all_makes();
			$mc = 0;
			foreach my $mk (@makes) {
				if (open MK, "<$mk") {
					my @lines = <MK>;
					close MK;
					foreach $file (@files) {
						if ($check_full) {
							($nam, $dir) = fileparse($file);
							foreach $line (@lines) {
								chomp $line;
								if ($line =~ /$nam/) {
									prt( "Note: [$nam] is in [$mk] ... line[$line]\n" );
									$mc++;
									last;
								}
							}
						} else {
							($nam, $dir, $ext) = fileparse( $file, qr/\.[^.]*/ );
							foreach $line (@lines) {
								chomp $line;
								if ($line =~ /$nam/) {
									prt( "Note: [$nam] is in [$mk] ... line[$line]\n" );
									$mc++;
									last;
								}
							}
						}
					}
				}
			}
			if ($mc) {
				prt( "Above $mc references should be checked ...\n\n" );
			}
		}
	}
}

sub get_missed_makes {
	my ($rd) = shift;
	my $ac = scalar @all_files;
	my $i = 0;
	my @mam = ();
	for ($i = 0; $i < $ac; $i++) {
		if ($all_files[$i][3] == $TYPE_AM) {
			if ($all_files[$i][2] == 0) {
				my $file = $all_files[$i][1];	# get FULL (qualified) path name
				my ($nm, $path) = fileparse($file);	# split it to name,path
				#my $rp = unix_2_dos(get_relative_path( $path, $rd ).$nm);
				my $rp = unix_2_dos(get_relative_path_2( $path, $rd ).$nm);
				push( @mam, $rp );	# pass back the DOS relative makefile.am entry
			}
		}
	}
	return @mam;
}

sub get_all_makes {
	my $ac = scalar @all_files;
	my $i = 0;
	my @mam = ();
	for ($i = 0; $i < $ac; $i++) {
		if ($all_files[$i][3] == $TYPE_AM) {
			my $file = $all_files[$i][1];	# get FULL (qualified) path name
			###my ($nm, $path) = fileparse($file);	# split it to name,path
			push( @mam, $file );	# pass back makefile.am full path
		}
	}
	return @mam;
}

########################################################################
####### ONLY DSP STUFF

sub expand_here {
    local $_ = shift;
    s/\%cflags\%/$msvc_cflags/g;
    s/\%libs\%/$msvc_libs/g;
    s/\%dlibs\%/$msvc_dlibs/g;
    s/\%rlibs\%/$msvc_rlibs/g;
    return $_;
}

sub console_app_dsp_init {
    my ($name, $dsp_name) = @_;
    prt( "Creating console app type $dsp_name ...\n" );
	my $ren = rename_2_old_bak( $dsp_name );
	if ($ren) {
		prt( "Renamed original to ".(($ren == 1) ? "OLD" : "BAK")." ...\n" );
	}
    open(DSP, ">$dsp_name") || mydie( "ERROR: Can't create $dsp_name: $!\n" );

    print DSP expand_here(<<EOF);
# Microsoft Developer Studio Project File - Name="$name" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **

# TARGTYPE "Win32 (x86) Console Application" 0x0103

CFG=$name - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE 
!MESSAGE NMAKE /f "$name.mak".
!MESSAGE 
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE 
!MESSAGE NMAKE /f "$name.mak" CFG="$name - Win32 Debug"
!MESSAGE 
!MESSAGE Possible choices for configuration are:
!MESSAGE 
!MESSAGE "$name - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "$name - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE 

# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe

!IF  "\$(CFG)" == "$name - Win32 Release"

# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
# ADD CPP /nologo /W3 /GR /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FD /c %cflags%
# SUBTRACT CPP /YX
# ADD RSC /l 0xc09 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:console /machine:I386 %libs% %rlibs%

!ELSEIF  "\$(CFG)" == "$name - Win32 Debug"

# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD CPP /nologo /W3 /Gm /GR /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c %cflags%
# ADD RSC /l 0xc09 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD LINK32 kernel32.lib user32.lib winspool.lib comdlg32.lib gdi32.lib shell32.lib advapi32.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept %libs% %dlibs%

!ENDIF 

# Begin Target

# Name "$name - Win32 Release"
# Name "$name - Win32 Debug"
EOF

    close(DSP);
}

sub static_lib_dsp_init {
    my ($name, $dsp_name) = @_;
    prt( "Creating static library type $dsp_name ... \n" );
	my $ren = rename_2_old_bak( $dsp_name );
	if ($ren) {
		prt( "Renamed original to ".(($ren == 1) ? "OLD" : "BAK")." ...\n" );
	}
    open(DSP, ">$dsp_name") || mydie( "ERROR: Can't create $dsp_name: $!\n" );

    print DSP expand_here(<<EOF);
# Microsoft Developer Studio Project File - Name="$name" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **

# TARGTYPE "Win32 (x86) Static Library" 0x0104

CFG=$name - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE 
!MESSAGE NMAKE /f "$name.mak".
!MESSAGE 
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE 
!MESSAGE NMAKE /f "$name.mak" CFG="$name - Win32 Debug"
!MESSAGE 
!MESSAGE Possible choices for configuration are:
!MESSAGE 
!MESSAGE "$name - Win32 Release" (based on "Win32 (x86) Static Library")
!MESSAGE "$name - Win32 Debug" (based on "Win32 (x86) Static Library")
!MESSAGE 

# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
RSC=rc.exe

!IF  "\$(CFG)" == "$name - Win32 Release"

# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
# ADD CPP /nologo /W3 /GR /GX /O2 /D "NDEBUG" /D "WIN32" /D "_MBCS" /FD /c %cflags%
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo

!ELSEIF  "\$(CFG)" == "$name - Win32 Debug"

# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Target_Dir ""
# ADD CPP /nologo /W3 /Gm /GR /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_MBCS" /FD /GZ /c %cflags%
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo

!ENDIF 

# Begin Target

# Name "$name - Win32 Release"
# Name "$name - Win32 Debug"
# Begin Group "Source Files"

# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;cc"
EOF

    close(DSP);
}

sub end_dsp_file {
	my ($fil) = shift;
	if (open DSP, ">>$fil") {
      print DSP "# End Group\n";
      # just the HEADER files - SHOULD ACCUMULATE THEM FOR HERE
      print DSP "# Begin Group \"Header Files\"\n";
      print DSP "\n";
      print DSP "# PROP Default_Filter \"h;hpp;hxx;hm;inl\"\n";
      # 2009-06-09 Add HEADERS if nay found
      if (defined $am_headers{$last_project}) {
   		my $lsrcs = $am_headers{$last_project};
         my @arr = split(/\|/ , $lsrcs);	# Sources
         foreach my $s (@arr) {
            $s = fix_rel($s);
            print DSP "# Begin Source File\n";
            print DSP "\n";
            print DSP "SOURCE=$s\n";
            print DSP "# End Source File\n";
         }
      } else {
         print DSP "\n";
      }
      print DSP "# End Group\n";
      print DSP "# End Target\n";
      print DSP "# End Project\n";
      close(DSP);
      prt( "[dbg30] Ending last DSP - [$fil] ...\n" ) if ($dbg30);
	}
}

sub get_am_incs {
	my ($prj) = shift;
	my $incs = '';
	my $ics = '';
	my @pths = ();
	my $ic = '';
	if (defined $am_includes{$prj}) {
		$ics = $am_includes{$prj};
		@pths = split(' ',$ics);
		foreach $ic (@pths) {
			$incs .= " " if (length($incs));
			$incs .= "/I \"$ic\"";	# could go for RELATIVE DIRECTORY
		}
	}
	if (defined $am_cppflags{$prj}) {
		$ics = $am_cppflags{$prj};
		@pths = split(' ',$ics);
		foreach $ic (@pths) {
			$incs .= " " if (length($incs));
			$incs .= "/D \"$ic\"";
		}
	}

   # 2009-05-09 - ensure _CRT_SECURE_NO_DEPRECATE is added
   if ($add_CRT_SECURE_NO_DEPRECATE) {
      if ( !($incs =~ /_CRT_SECURE_NO_DEPRECATE/) ) {
			$incs .= " " if !($incs =~ /\s$/);
         $incs .= "/D \"_CRT_SECURE_NO_DEPRECATE\"";
      }
   }
   if ($add_CONFIG_H) {
      if ( !($incs =~ /HAVE_CONFIG_H/) ) {
			$incs .= " " if !($incs =~ /\s$/);
         $incs .= "/D \"HAVE_CONFIG_H\"";
      }
   }
   if ($add_NOMINMAX) {
      if ( !($incs =~ /NOMINMAX/) ) {
			$incs .= " " if !($incs =~ /\s$/);
         $incs .= "/D \"NOMINMAX\"";
      }
   }

	return $incs;
}

sub get_am_libs {
	my ($prj) = shift;
	my $incs = '';
	if (defined $am_libadds{$prj}) {
		$incs = $am_libadds{$prj};
	}
	return $incs;
}

# write DSP file
sub out_dsp_file {
   my ($prj, $fil, $src, $rd, $typ) = @_;
   my ($dsptype, $srcdir, $shtfil);
   if ($prj ne $last_project) {
      end_dsp_file($last_dsp_file) if (length($last_dsp_file));

		$last_project = $prj;
		$last_dsp_file = $fil;
		$msvc_cflags = get_am_incs( $prj );
		$msvc_libs = get_am_libs( $prj );
		if ($typ) {
         static_lib_dsp_init( $prj, $fil );
         $dsptype = 'static lib';
		} else {
			console_app_dsp_init( $prj, $fil );
         $dsptype = 'console app';
		}
      $shtfil = subract_target_dir($fil);
      prtd( "Started new DSP $shtfil, project $prj, type $dsptype ...\n" );
	}

	if (open OF, ">>$fil") {
		print OF "# Begin Source File\n";
		print OF "\n";
      $srcdir = $src;
      #if ($add_SOURCE_REL && length($src_relative)) {
      #   $srcdir = $src_relative;
      #   $srcdir .= "\\" if (! (($srcdir =~ /(\\|\/)$/) || ($src =~ /^(\\|\/)/)) );
      #   $srcdir .= "$src";
      #}
  		print OF "SOURCE=$srcdir\n";
		print OF "# End Source File\n";
		close OF;
	}
}

# RENAME A FILE TO .OLD, or .BAK
# 0 - do nothing if file does not exist.
# 1 - rename to .OLD if .OLD does NOT exist
# 2 - rename to .BAK, if .OLD already exists,
# 3 - deleting any previous .BAK ...
sub rename_2_old_bak {
	my ($fil) = shift;
	my $ret = 0;
	if ( -f $fil ) {
		my ($nm,$dir,$ext) = fileparse( $fil, qr/\.[^.]*/ );
		my $nmbo = $dir . $nm . '.old';
		$ret = 1;
		if ( -f $nmbo) {
			$ret = 2;
			$nmbo = $dir . $nm . '.bak';
			if ( -f $nmbo ) {
				$ret = 3;
				unlink $nmbo;
			}
		}
		rename $fil, $nmbo;
	}
	return $ret;
}

sub C_comment_starts {
    my ($txt) = shift;
    my $len = length($txt);
    my $ptxt = '';
    my $ttxt = '';
    my ($k, $ch, $pch, $k2, $nch);
    for ($k = 0; $k < $len; $k++) {
        $k2 = $k + 1;
        $ch = substr($txt,$k,1);
        $nch = (($k2 < $len) ? substr($txt,$k2,1) : '');
        if (($ch eq '/')&&($nch eq '*')) {
            $ttxt = substr($txt,($k2+1));
            return $k2, $ptxt, $ttxt;   # return offset, previous and begin comment
        }
        $pch = $ch;
        $ptxt .= $ch;
    }
    return 0, $ptxt, $ttxt;
}

sub inline_comment_starts {
    my ($txt) = shift;
    my $len = length($txt);
    my $ptxt = '';
    my ($k, $ch, $pch, $k2, $nch);
    for ($k = 0; $k < $len; $k++) {
        $k2 = $k + 1;
        $ch = substr($txt,$k,1);
        $nch = (($k2 < $len) ? substr($txt,$k2,1) : '');
        if (($ch eq '/')&&($nch eq '/')) {
            return $k2, $ptxt;   # return offset, previous
        }
        $pch = $ch;
        $ptxt .= $ch;
    }
    return 0, $ptxt;
}

sub C_comment_ends {
    my ($txt) = shift;
    my $len = length($txt);
    my $ttxt = '';
    my ($k, $ch, $pch, $k2, $nch);
    for ($k = 0; $k < $len; $k++) {
        $k2 = $k + 1;
        $ch = substr($txt,$k,1);
        $nch = (($k2 < $len) ? substr($txt,$k2,1) : '');
        if (($ch eq '*')&&($nch eq '/')) {
            $ttxt = substr($txt,($k2+1));
            return $k2, $ttxt;  # return trailing 
        }
        $pch = $ch;
    }
    return 0, $ttxt;
}

sub get_C_file_lines {
    my ($inf) = shift;
    my (@lines, $lncnt, $line, $tline);
    my ($isc, $ptxt, $i, $ttxt, $ise, $atxt, $i2, $ctxt);
    my $incomm = 0;
    my $lnnum = 0;
    my ($typ, $inmacro, $len, $ch, $j, $braces, $pbrac);
    my ($msg);
    my @nlines = ();
    $inmacro = 0;
    $braces = 0;
    $pbrac = 0;
    if (open INF, "<$inf") {
        @lines = <INF>;
        close INF;
        $lncnt = scalar @lines;
        prt( "Processing $lncnt lines, from $inf ...\n") if ($dbg_comm);
        for ($i = 0; $i < $lncnt; $i++) {
            $lnnum++;
            $line = $lines[$i];
            $tline = trim_all($line);

            if ( $incomm ) {
                ($ise,$atxt) = C_comment_ends($tline);
                if ($ise) {
                    $msg = "$lnnum: Comment ends";
                    $msg .= " [$atxt] follows" if length($atxt);
                    prt( "$msg\n" ) if ($dbg_comm);
                    $incomm = 0;
                    $tline = trim_all($atxt);
                }
            }
            if ( ! $incomm ) {
                $ctxt = $tline;
                ($isc,$ptxt,$ttxt) = C_comment_starts($tline);
                if ($isc) {
                    # deal with any $ptxt ..
                    ($ise,$atxt) = C_comment_ends($ttxt);
                    if ($ise) {
                        while ($isc && $ise) {
                            $ptxt = trim_all($ptxt);
                            $atxt = trim_all($atxt);
                            $ctxt = $ptxt;
                            $ctxt .= ' ' if (length($ctxt) && length($atxt) && ($atxt ne ';'));
                            $ctxt .= $atxt if length($atxt);
                            $ctxt = trim_all($ctxt);
                            $msg = "$lnnum: Comment starts/ends - ";
                            $msg .= "[$ptxt] before " if length($ptxt);
                            $msg .= "[$atxt] follows " if length($atxt);
                            prt( "$msg\n" ) if ($dbg_comm);
                            ($isc,$ptxt,$ttxt) = C_comment_starts($ctxt);
                            $atxt = '';
                            if ($isc) {
                                ($ise,$atxt) = C_comment_ends($ttxt);
                            }
                            $ptxt = trim_all($ptxt);
                            $atxt = trim_all($atxt);
                            $ctxt = $ptxt;
                            $ctxt .= ' ' if (length($ctxt) && length($atxt) && ($atxt ne ';'));
                            ###$ctxt .= ' ' if length($ctxt);
                            $ctxt .= $atxt if length($atxt);
                            $ctxt = trim_all($ctxt);
                        }
                    } else {
                        $ctxt = trim_all($ptxt);
                        $msg = "$lnnum: IN Comment";
                        $msg .= " after [$ptxt]" if length($ptxt);
                        prt( "$msg\n" ) if ($dbg_comm);
                        $incomm = 1;
                    }
                }
                $tline = $ctxt;
                if (! $incomm) {
                    ($isc,$ptxt) = inline_comment_starts($tline);
                    if ($isc) {
                        $ctxt = trim_all($ptxt);
                        $msg = "$lnnum: INLINE Comment";
                        $msg .= " after [$ptxt]" if length($ptxt);
                        prt( "$msg\n" ) if ($dbg_comm);
                    } else {
                        $ctxt = $tline;
                    }
                }
            }
            $tline = trim_all($ctxt);
            push(@nlines, $tline) if length($tline);
        }
    } else {
        prtw( "WARNING: Unable to OPEN [$inf]!\n" );
    }
    return @nlines;
}

sub get_include_list {
    my ($fil, $rarr) = @_;
    my @lines = ();
    if ( -f $fil ) {
        my @lines = get_C_file_lines($fil);
        my $lncnt = scalar @lines;
        my $inc = '';
        prt( "Processing $lncnt lines, from $fil ...\n") if ($dbg_incs);
        for (my $i = 0; $i < $lncnt; $i++) {
            my $line = $lines[$i];
            if ($line =~ /\s*#\s*include\s+(.+)/ ) {
                $inc = $1;
                push(@{$rarr},$inc);
                prt( "include: [$inc] ($line)\n" ) if ($dbg_incs);
            }
        }
    } else {
        prtw( "WARNING: Unable to find [$fil]!\n" );
    }
}

sub show_inc_hash {
    my ($key, $val, $cnt);
    $cnt = scalar keys(%inc_hash);
    prt( "\nShow of $cnt keys in inc_hash ...\n" );
    foreach $key (sort keys %inc_hash) {
        $val = $inc_hash{$key};
        prt( "$key = [$val]\n" );
    }
    prt("Done\n");
}

# eof - amsrcs02.pl
