#!/usr/bin/perl -w
# NAME: amsrcs04.pl
# AIM: To scan a GNU configure.ac, or configure.in input file, for output Makefiles, then
# successifly scan the Makefile.am (or .in) files, and create a DSP file foreach LIBRARY,
# or PROGRAMS found in this make file san, then finally a DSW file.
# =======================================================================================
# NOTE: This is NOT really a follow on from amsrcs03.pl ;=((
# This is a COMPLETE rewrite using lib_acscan.pl, and lib_amscan.pl
# =======================================================================================
# History: Growing upwards...
# 01/06/2012 - New option --ADD proj:srcs to force a source file be add to the project, a source 
#              that may not be found in the Makefile.am files.
# 19/05/2012 - try to fix cmake install for projects like PLIB that has to be installed in /include/plib
# 11/05/2012 - More 'fixes' for unix running
# 10/05/2012 - Make some path_d2u() fixes...
# 10/05/2012 - Some tidying and do work to run in linux
# 18/04/2012 - Begin write of CMakeLists.txt files ;=))
# 17/04/2012 - Due to a WARNING from MSVC10 with auto on change adding out:bin\foo.exe to a copy to bin (like createdsp03.pl ddoes)
# 16/04/2012 - Put the copy BAT in C:\MDOS if it exists
# 19/02/2012 - add -v[num]
# 14/10/2011 - Found a case where only 2 Makefile.am found in configure.ac scan, BUT there is a line
# [01] 39: [AC_CONFIG_SUBDIRS(autoconf-lib-link gettext-runtime gettext-tools)], and these SUBDIRS do 
# contain Makefile.am files... so add them to the list of AM to scan
# 03/08/2011 - FIX20110803 - Found macro in Makefile.am like E00 = # src1.c - This should yield NO source
# 21/04/2011 - FIX20110421 in lib_amscan.pl, to eliminate duplicate libs and paths.
# 27/03/2011 - FIX20110327 - -L<set> Allow the 'set' to include MULTIPLE libraries, for multiple project
#   2011-03-14 The -L <set> of library:path:[D|R|B][:project] could be EXTENDED to include
#   multiple name, of both the library and project parts, like
#   -L lib1;lib2:common_path:[D|R|B][:proj1;proj2;proj3
#   Likewise -x project could be EXTENDED to be -x project1;project2;project3
# 23/03/2011 - FIX20110323 - Some minor improvement, and allow -I MACRO:project per project, and fix
#   2011-03-14:FIXED:20110322: The -D macro[:value] got real messed up with say 
#   '-D DEF_DIR=C:\some\path' Now FIXED 20110322 with got_colon_split(), and colon_split() subs
# 06/03/2011 - FIX20110306 - Set project RUNTIME - either 'T'=static /MT, or 'D'=DLL (default) /MD
# 30/12/2010 - Release 4.24, with some minor things in libraries...
# 18/11/2010 - Fix librayr input -L lib:path:D[;proj] to accept 'c:\' as not a colon split
# 03/11/2010 - Try moving JOINED to FIRST
# 31/10/2010 - Several important additional options, and when a user option -L a-lib.lib:lib:[D|R|B],
#              where the a-lib.lib can be a semicolon separated list of libraries, but if no [;proj]
#              also try to add this to the DSW, if a local library dependency, to set the build order.
# 21/10/2010 - Added new functions to handle
#   defines on a project, by project basis, like
#   -D TESTNUM=1:signal1, -D TESTNUM=2:signal2, -D TESTNUM=3:signal3
#   AND set project type like SL to DLL, or CA to WA, but DLL NOT tested
# 28/09/2010 Conditionals in the Makefile.am are stored, BUT when it comes to choosing the
#   correct sustitution, was just the FIRST TRUE always CHOSEN.
#   Added the option like -
#   --cond item[:1|0] (-c) = Define a conditional to be applied during parsing.
#   Revert to previous, if no conditional defined by user, but write a temp/missed_cond_list.txt
#   for items NOT defined.
# 23/09/2010:19/09/2010 - Lots of updates, changes, additions
# 08/09/2010 geoff mclane http://geoffair.net/mperl
use strict;
use warnings;
use File::Basename;  # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] )
use File::Spec; # File::Spec->rel2abs($rel); # we are IN the SLN directory, get ABSOLUTE from RELATIVE
use Cwd;
my $os = $^O;
my $perl_dir = ((exists $ENV{'HOME'}) ? $ENV{'HOME'}.'/bin' : '/tmp');
my $PATH_SEP = '/';
my $temp_dir = '/tmp';
if ($os =~ /win/i) {
    $perl_dir = 'C:\GTools\perl';
    $temp_dir = $perl_dir;
    $PATH_SEP = "\\";
}
unshift(@INC, $perl_dir);
require 'lib_utils.pl' or die "Unable to load 'lib_utils.pl' Check paths in \@INC...\n";
require 'lib_acscan.pl' or die "Unable to load 'lib_acscan.pl'! Check location and \@INC content.\n";
require 'lib_amscan.pl' or die "Unable to load 'lib_amscan.pl'! Check location and \@INC content.\n";
require 'lib_dsphdrs.pl' or die "Unable to load 'lib_dsphdrs.pl'! Check location and \@INC content.\n";
require 'lib_vcscan.pl' or die "Unable to load 'lib_vcscan.pl'! Check location and \@INC content.\n";
require 'lib_params.pl' or die "Unable to load 'lib_params.pl'! Check location and \@INC content.\n";
require 'lib_srcscan.pl' or die "Unable to load 'lib_srcscan.pl'! Check location and \@INC content.\n";
require 'lib_cmake.pl' or die "Unable to load 'lib_cmake.pl'! Check location and \@INC content.\n";

# log file stuff
my ($LF);
my $pgmname = $0;
if ($pgmname =~ /(\\|\/)/) {
    my @tmpsp = split(/(\\|\/)/,$pgmname);
    $pgmname = $tmpsp[-1];
}
my $outfile = $temp_dir.$PATH_SEP."temp.$pgmname.txt";
open_log($outfile);

my $perl_temp_dir = $temp_dir;
my $my_version = "version 0.5.1 2012-05-10";
###my $my_version = "version 0.5.0 2012-04-19";

# user variables
my $load_log = 0;
my $no_conf_write = 0;
my $write_am_list = 1;
my $verbosity = 0;
my $show_per_file = 1;
my $dsp_out_dir = $perl_temp_dir;
my $do_joined_first = 0; # NO, this is a BAD idea, since the 'original' DSP would overwrite any joined
my $write_dsp_files = 0;
my $write_cmake_files = 0;
# my $usr_3rdparty_dir = '';

my $conf_file = $perl_temp_dir.$PATH_SEP."temp.$pgmname.conf";
my $amlistfile = $perl_temp_dir.$PATH_SEP."amlist.txt";
my $temp_copy_bat = $perl_temp_dir.$PATH_SEP."tempcopy.bat";
my $miss_mac_file = $perl_temp_dir.$PATH_SEP."missed_mscro_list.txt";
my $missing_list_file = $perl_temp_dir.$PATH_SEP."missed_defined_list.txt";
my $missed_source_list = $perl_temp_dir.$PATH_SEP."missed_source_list.txt";
my $missed_source_dsp = $perl_temp_dir.$PATH_SEP."missed_source_dsp.txt";
my $missed_cond_list = $perl_temp_dir.$PATH_SEP."missed_cond_list.txt";
# ==========================================================
# test projects
my $debug_on = 0;   # run with DEFAULT, if no other input...
my $def_file = 'C:\Projects\libxml2\configure.in';
my $def_targ = '';
#my $def_file = 'C:\Projects\giflib\configure.ac';
#my $def_file = 'C:\Projects\pcre\configure.ac';
#my $def_targ = 'C:\Projects\giflib\msvc';
#my $def_targ = '';
#my $def_file = 'C:\Projects\libsigc\libsigc++-2.2.8\configure.ac';
#my $def_targ = 'C:\Projects\libsigc\libsigc++-2.2.8\msvc';
#my $def_name = 'pcre';
my $def_name = '';
# ============================================================
my %g_default_condits = ( # default conditionals
    "USE_GLUT" => "TRUE",
    "ENABLE_JPEG_SERVER" => "FALSE",
    "ENABLE_SP_FDM" => "TRUE"
);

########################################
### SHARED RESOURCES, VALUES
### ========================
our $fix_relative_sources = 1;
our %g_user_subs = ();    # supplied by USER INPUT
our %g_user_condits = (); # conditionals supplied by the user
# Auto output does the following -
# For libaries
# Debug:  '/out:"lib\barD.lib"'
# Release:'/out:"lib\barD.lib"'
# for programs
# Debug:  '/out:"bin\fooD.exe"'
# Release:'/out:"bin\foo.exe"'
# This also 'adds' missing 'include' files
#Bit:   1: Use 'Debug\$proj_name', and 'Release\$proj_name' for intermediate and out directories
#Bit:   2: Set output to lib, or bin, and names to fooD.lib/foo.lib or barD.exe/bar.exe
#Bit:   4: Set program dependence per library output directories
#Bit:   8: Add 'msvc' to input file directory, if no target directory given
#Bit:  16: Add program library dependencies, if any, to DSW file output.
#Bit:  32: Add all necessary headers to the DSP file. That is scan the sources for #include "foo.h", etc.
#Bit:  64: Write a blank header group even there are no header files for that component.
#Bit: 128: Add defined item of HAVE_CONFIG_H to all DSP files.
#Bit: 256: Exclude projects in SUBDIRS protected by a DEFINITION macro, else include ALL.
#Bit: 512: Unconditionally add ANY libraries build, and NOT excluded to the last application
#Bit:1024: If NO users conditional, do sustitution, if at all possible, regardless of TRUE or FALSE
#Bit:2048: Add User -L dependent libraries to each application
#Bit: These can be given as an integer, or the form 2+8, etc. Note using -1 sets ALL bits on.
#Bit: Bit 32 really slows down the DSP creation, since it involves scanning every line of the sources.
my $auto_max_bit = 512;
our $auto_on_flag = -1; #Bit: ALL ON by default = ${$rparams}{'CURR_AUTO_ON_FLAG'}
sub get_curr_auto_flag() { return $auto_on_flag; }
#my ($g_in_name, $g_in_dir);
#my ($root_file, $root_folder);
#sub get_root_dir() { return $root_folder; }
our $exit_value = 0;
# But SOME Makefile.am will use specific 'paths' so the above can FAIL to find
# a file, so the following two 'try harder' options, will do a full 'root'
# directory SCAN, and search for the file of that name in the scanned files
our $try_harder = 1;
our $try_much_harder = 1;
# ==============================================================================
our $process_subdir = 0;
our $warn_on_plus = 0;
# ==============================================================================
# NOTE: Usually a Makefile.am contains SOURCE file names 'relative' to itself,
# which is usually without any path. This options ADDS the path to the
# Makefile.am, and then substracts the 'root' path, to get a SOURCE file
# relative to the 'root' configure.ac, which is what is needed if the DSP
# is to be placed in a $target_dir, and we want the file relative to that
our $add_rel_sources = 1;
our $target_dir = '';
# ==============================================================================
our $ignore_EXTRA_DIST = 0;
our $added_in_init = '';
our $supp_make_in = 0; # Support Makefile.in scanning
our $project_name = ''; # a name to override any ac scanned name of the project

my $dsp_files_skipped = 0;

### =============================
# offsets into REF_LIB_LIST array
our $RLO_MSG = 0;
our $RLO_PRJ = 1;
our $RLO_VAL = 2;
our $RLO_NAM = 3;
our $RLO_EXC = 4;
### =============================

my $inst_hdr_path = 'include';
my $inst_lib_path = 'lib';
my $inst_bin_path = 'bin';
sub get_inst_hdr_path() { return $inst_hdr_path; }
sub get_inst_lib_path() { return $inst_lib_path; }
sub get_inst_hdr_bin() { return $inst_bin_path; }

sub get_PATH_SEP() { return $PATH_SEP; }
sub get_pgmname() { return $pgmname; }
sub get_dsp_out_dir() { return $dsp_out_dir; }
sub get_write_cmake_files() { return $write_cmake_files; }
sub get_write_dsp_files() { return $write_dsp_files; }

sub fix_rpath_per_os($) {
    my $rff = shift;
    my $ff = ${$rff};
    $ff = ($os =~ /win/i) ? path_u2d($ff) : path_d2u($ff);
    ${$rff} = $ff;
}

# DEBUG - Show a REF HASH
sub show_ref_hash_inline($) {
    my $rh = shift;
    my ($key,$val,$typ);
    foreach $key (sort keys %{$rh}) {
        next if ($key =~ /^CURR_/);
        $val = ${$rh}{$key};
        $typ = ref($val);
        $typ = $val if (length($typ) == 0);
        prt("$key [$typ] ");
    }
}

sub show_ref_hash($$) {
    my ($rh,$flag) = @_;
    my ($key,$val,$typ,$cnt,$len);
    $cnt = scalar keys(%{$rh});
    prt("\nREF HASH has $cnt keys....\n");
    foreach $key (sort keys %{$rh}) {
        next if ($key =~ /^CURR_/);
        $val = ${$rh}{$key};
        $typ = ref($val);
        $len = length($typ);
        $typ = $val if ($len == 0);
        prt("$key [$typ] ");
        if ($flag & 1) {
            if ($len && ($typ eq 'HASH')) {
                prt("{");
                show_ref_hash_inline($val);
                prt("}");
            }
            prt("\n");
        }
    }
    prt("\n");
}

# Add to this (IN LOWER CASE FORM) as new libraries are found...
my @win_libs = qw( msimg32.lib imm32.lib winmm.lib ws2_32.lib shlwapi.lib );
sub is_win_library($) {
    my ($lib) = @_;
    my $lclib = lc($lib);
    my ($key);
    foreach $key (@win_libs) {
        return 1 if ($key eq $lclib);
    }
    return 0;
}

my %excluded_projects = ();
my %joined_projects = ();
my %proj_renaming = ();

# 01/06/2012 - New option --ADD proj:srcs to force a source file be add to the project, a source 
#              that may not be found in the Makefile.am files.
my %project_sources = ();   # user added sources -A proj:src1[;src2[;src3...]]
sub add_srcs_to_proj($$) {
    my ($proj,$srcs) = @_;
    my ($msg);
    if (defined $project_sources{$proj}) {
        $project_sources{$proj} .= ";$srcs";
        $msg = 'appended';
    } else {
        $project_sources{$proj} = "$srcs";
        $msg = 'added';
    }
    prt("Sources [$srcs] $msg to project [$proj] in \%project_sources.\n");
}
sub get_added_srcs_4_proj($) {
    my $proj = shift;
    if (defined $project_sources{$proj}) {
        return $project_sources{$proj};
    }
    return '';
}

# my $excluded_msg = '<all';
my $final_batch_msg = '';

sub set_exclude_project($$$) {
    my ($arg,$sarg,$verb) = @_;
    my @arr = split(/:/,$sarg);
    my $cnt = scalar @arr;
    my ($prj,$srcs);
    if ($cnt == 1) {
        $prj = $arr[0];
        $srcs = '<all>';
        $excluded_projects{$prj} = $srcs;
        prt("Set to eXclude ALL of project [$prj]\n") if ($verb);
    } elsif ($cnt == 2) {
        $prj = $arr[0];
        $srcs = $arr[1];
        @arr = split(/;/,$srcs);
        $cnt = scalar @arr;
        $excluded_projects{$prj} = $srcs;
        prt("Set to eXclude $cnt sources of project [$prj] [".join(";",@arr)."]\n") if ($verb);
    } else {
        if ($verb) {
            pgm_exit(2,"ERROR: Command [$arg $sarg] FAILED! Not of form prj[:srcs]...\n");
        } else {
            prtw("WARNING: INTERNAL: Command [$arg $sarg] FAILED! Not of form prj[:srcs]...\n");
        }
    }
}

sub get_project_new_name($) {
    my $old = shift;
    if (defined $proj_renaming{$old}) {
        return $proj_renaming{$old};
    }
    return $old;
}

sub is_project_src_excluded($$) {
    my ($proj,$rslist) = @_;
    if (defined $excluded_projects{$proj}) {
        ${$rslist} = $excluded_projects{$proj}; # for all = '<all>'
        return 1;
    }
    return 0;
}

sub is_project_all_excluded($) {
    my ($proj) = @_;
    my $list = '';
    if ( is_project_src_excluded($proj,\$list) && ($list eq '<all>')) {
        return 1;
    }
    return 0;
}


my $dbg_lib_path = 0;
my $in_input_file = 0;

my %by_proj_includes = ();  # -NEW_INCS- BY PROJECT
my $proj_incs = '/I "."';   # -NEW_INCS- GLOBAL

my $proj_rt = 'D';  # ie use /MD and /MDd

sub warn_unused_keys($$$) {
    my ($ruser,$rused,$msg) = @_;
    my ($key,$cnt);
    $cnt = 0;
    foreach $key (keys %{$ruser}) {
        if (!defined ${$rused}{$key}) {
            prtw("WARNING: UNUSED $msg key [$key]\n");
            $cnt++;
        }
    }
    return $cnt;
}

my %by_proj_types = ();
my %by_proj_typused = ();

# GET -T project:type[:source_list]
sub get_user_proj_type($$$) {
    my ($proj,$def,$rsrcs) = @_;
    ${$rsrcs} = ''; # ensure $addsrcs is cleared
    if (defined $by_proj_types{$proj}) {
        my (@arr,$cnt,$srcs,$msg);
        @arr = split(/:/,$by_proj_types{$proj});
        $cnt = scalar @arr;
        $def = $arr[0];
        $srcs = '';
        $srcs = $arr[1] if ($cnt == 2);
        ${$rsrcs} = $srcs;
        $msg = "[v9] get_user_proj_type [$proj] [$def] ($cnt)";
        $msg .= ", with srcs [$srcs]" if (length($srcs));
        prt("$msg\n") if (VERB9());
        $by_proj_typused{$proj} = 1;
    }
    return $def;
}

my $proj_defs = '/D "_CRT_SECURE_NO_WARNINGS"';
my %by_proj_defines = ();
my %by_proj_defused = ();

sub warn_missed_user_types() {
    my $msg = "Types";
    my $cnt = scalar keys(%by_proj_types);
    my $res = 0;
    if ($cnt) {
        prt("[v9] Checking for unused $msg ($cnt)\n") if (VERB9());
        $res = warn_unused_keys(\%by_proj_types,\%by_proj_typused,$msg);
    }
    return $res;
}
sub warn_missed_user_defined() {
    my $msg = "Defines";
    my $cnt = scalar keys(%by_proj_defines);
    my $res = 0;
    if ($cnt) {
        prt("[v9] Checking for unused $msg ($cnt)\n") if (VERB9());
        $res = warn_unused_keys(\%by_proj_defines,\%by_proj_defused,$msg);
    }
    return $res;
}


my %by_proj_libraries = ();
my %by_proj_libraryD = ();
my %by_proj_libraryR = ();
my %by_proj_libsused = ();
sub warn_missed_user_libs() {
    my $msg = "Libraries";
    my $cnt = scalar keys(%by_proj_libraries);
    my $res = 0;
    if ($cnt) {
        prt("[v9] Checking for unused $msg ($cnt)\n") if (VERB9());
        $res += warn_unused_keys(\%by_proj_libraries,\%by_proj_libsused,$msg);
    }
    $msg = "LibraryD";
    $cnt = scalar keys(%by_proj_libraryD);
    if ($cnt) {
        prt("[v9] Checking for unused $msg ($cnt)\n") if (VERB9());
        $res += warn_unused_keys(\%by_proj_libraryD,\%by_proj_libsused,$msg);
    }
    $msg = "LibraryR";
    $cnt = scalar keys(%by_proj_libraryR);
    if ($cnt) {
        prt("[v9] Checking for unused $msg ($cnt)\n") if (VERB9());
        $res += warn_unused_keys(\%by_proj_libraryR,\%by_proj_libsused,$msg);
    }
    return $res;
}

# NOTE: For user includes, usually also NEED 'libpath'
# like $proj_libD .= " /libpath:\"Debug\" foo.lib";
# like $proj_libR .= " /libpath:\"Release\" foo.lib";
# OR
# like $proj_lib .= " /libpath:\"lib\";
# like $proj_libD .= " fooD.lib";
# like $proj_libR .= " foo.lib";
# sub in line ADD LINK32 kernel32.lib ... -NEW_LIBS- /nologo ...
my $proj_libs = 'Winmm.lib ws2_32.lib';
my $proj_libD = '';
my $proj_libR = '';

# NOTE: This is for say '/out:"bin\foo.exe"' or '/out:"lib\bar.lib"'
my $proj_outputR = '';
my $proj_outputD = '';

# object output, and the default for other things if NOT specifically stated
my $proj_interR = '"Release"';
my $proj_interD = '"Debug"';

### program variables
my @warnings = ();
my $cwd = cwd();
my $conf_string = '';
my $only_dry_run = 0;
my $in_file = '';
my $curr_command = '';

#########################################
### DEBUG STUFF ###
# Debug items SHARED with LIBRARY - note use of 'our' - to adjust this list
# must also adjust the lib_acscan.pl library!!!
our $dbg_lac01 = 0; # prt( "[01] scan_..._file: Reading $file\n" ) if $dbg_lac01; and more
our $dbg_lac02 = 0; # show EACH line prt( "[02] $lnn: $cline... for each read line.
our $dbg_lac03 = 0; # prt( "[03] Variable [$key] = [$nval]\n" )
our $dbg_lac04 = 0; # prt( "[04] Split to $vlen components ...\n" )
our $dbg_lac05 = 0; # prt( "[05] Substitute [$key] = [$nval]\n" ) if ($v1 ne $v2))
our $dbg_lac06 = 0; # prt( "[06] $.: Should JOIN lines? - [$cline]\n" ) and more...
our $dbg_lac07 = 0; # prt( "[07] $.: Got AC_INIT = [$1]\n" ) and AC_DEFIN... etc
our $dbg_lac08 = 0; # prt( "[08] Got ac_output_line = $. [$rawline]\n" ) plus accumulation
our $dbg_lac09 = 0; # prt( "[01|09] Adding $input [$ff] to mk_inp_list ...\n" )
our $dbg_lac10 = 0; # prt( "[01|10] Adding $input [$ff] to other_input_files ...\n" )
our $dbg_lac11 = 0; # prt( "[11] Storing configure_cond key $1 ... value=2\n" ), and more
our $dbg_lac12 = 0; # prt( "[12] $.: 1=[$1] = 2=[$2] NOT USED [$cline]\n" )
our $dbg_lac13 = 0; # prt("[13] $lnn: Failed on MACRO [$blk], in file [$file]\n")
our $dbg_lac14 = 0; # show each MACRO split in FULL
our $dbg_lac15 = 0; # Show each AC MACRO accumulation...
our $dbg_lac16 = 0; # Show back slash accumulation...
our $dbg_lac17 = 0; # show all substitutions
our $dbg_lac18 = 0; # show setting or replacing each macro with value
our $dbg_lac19 = 0; # unused at present
our $dbg_lac20 = 0; # prt("[20] Directory SEARCH for [$test], found $fcnt...
my $dbg_lac_check = 20;

#########################################
# DEBUG for lib_amscan.pl
our $dbg_s01 = 0; # show each file line, in form "[01] $i2: [$line]"
our $dbg_s02 = 0; # show extraction from hash, like "Listing $acnt keys in hash ..."
our $dbg_s03 = 0; # show "Find sources for $val LIBRARY keys ...\n" and MORE
our $dbg_s04 = 0; # show prt( "LIBRARY [$ky] has SOURCES [$val]
our $dbg_s05 = 0; # show prt( "$am ". ((-f $am) ? "ok" : "no find!")
our $dbg_s06 = 0; # show prt( "Opened cond_stack with [".$cond_stack[$#cond_stack]."] $fil
our $dbg_s07 = 0; # add new line before 'Processing $cnt lines..., as does 08 also...
our $dbg_s08 = 0; # show prt( "Processing $cnt lines from $fil ...
our $dbg_s09 = 0; # show prt( "Got $cnt subdirectories [$slist] ...
our $dbg_s10 = 0; # show prtw("WARNING:1: No substitution for [$ms] found in hash ...
our $dbg_s11 = 0; # show target: gathering of lines...
our $dbg_s12 = 0; # show setting key=value in hash, during am file scan
our $dbg_s13 = 0; # show initial substitution, during am file scan
our $dbg_s14 = 0; # similar to about, but only show NO sub FOUND
our $dbg_s15 = 0; # List each source, for each project...
our $dbg_s16 = 0; # Like [02] list ALL keys showing dispostion
our $dbg_s17 = 0; # Out CHECK ME - SHOULD THIS ITEMS BE INCLUDED for a prog,lib,src key, now skipped!
our $dbg_s18 = 0; # show change due to adding relative directory
our $dbg_s19 = 0; # show finds found by directory searching...
our $dbg_s20 = 0; # output list of sources and header from dir scan, NOT included in a project
our $dbg_s21 = 0; # prt("\n[21] Seeking 'all' or 'default' in $cnt keys...\n"), and MORE...
our $dbg_s22 = 0; # show '[22]CHANGED:1: key [gatomic_c] cond [FALSE] Was [gatomic-gcc.c], now [gatomic.c] FALSE, plus
our $dbg_s23 = 0; # debug the search for sub-directories to avoid
our $dbg_s24 = 0; # output a list of ALL projects in $rlibs and $rprogs
my $am_check_tot = 24;

sub VERB1() { return ($verbosity > 0); }
sub VERB2() { return ($verbosity > 1); }
sub VERB5() { return ($verbosity > 4); }
sub VERB9() { return ($verbosity > 8); }

my $project_dbg_write = 0;
my $dbg_cmake = 1;

sub set_all_dbg_on() {
    ac_set_all_dbg_on();
    am_set_all_dbg_on();
}
sub set_all_dbg_off() {
    ac_set_all_dbg_off();
    am_set_all_dbg_off();
}

sub set_max_debug_on() {
    $verbosity = 10;
    $project_dbg_write = -1;
    set_all_dbg_on();
    #ac_set_dbg_extra();
    prt("[debug_on] Have set MAXIMUM debug output...\n");
}

sub wait_key() {
    prt("Any key to continue...\n");
    my $char = <>;
}

# ***TBD*** this is NOW not called - see show_missing_subs2() instead
# but perhaps this is better, in that it als adds those added automatically
# but this is a 'conflict' between g_user_sub, and common_sub to be resolved
sub show_missing_subs($) {
    my ($rsnf) = @_;
    if ($dbg_s13 || $dbg_s14) {
        my @arr = keys %{$rsnf};
        my ($cnt,$txt,$key,$fil,$val,%hash,$msg);
        $txt = '';
        if (@arr) {
            $cnt = scalar @arr;
            prt("[13|14] There are at least $cnt missing substitutions.\n");
            $txt = "# [13|14] There are at least $cnt missing substitutions.\n";
            if ($show_per_file) {
                %hash = ();
                foreach $key (@arr) {
                    $fil = ${$rsnf}{$key};
                    push(@{$hash{$fil}},$key);
                }
                foreach $fil (keys %hash) {
                    $val = $hash{$fil};
                    $cnt = scalar @{$val};
                    $txt .= "\n# Missing $cnt from file [$fil]\n";
                    prt("Missing $cnt [");
                    $msg = '';
                    foreach $key (sort @{$val}) {
                        $txt .= "-m $key \"\"\n";
                        $msg .= ', ' if (length($msg));
                        $msg .= $key;
                    }
                    prt("$msg] from file [$fil]\n");
                }
            } else {
                foreach $key (sort @arr) {
                    $fil = ${$rsnf}{$key};
                    prt("Missing [$key], in [$fil]\n");
                    $txt .= "-m $key \"\"\n";
                }
            }
        } else {
            prt("[13|14] There are NO missing substitutions.\n");
        }
        @arr = split(/\s/,$added_in_init);
        $cnt = scalar @arr;
        if ($cnt) {
            prt("But note ADDED $cnt items, during init...");
            if (length($miss_mac_file) && (length($txt))) {
                $txt .= "\n# Note the following set of $cnt items were added during init...\n";
                %hash = ();
                init_common_subs2(\%hash,0);
                $cnt = 0;
                foreach $key (@arr) {
                    if (defined $hash{$key}) {
                        $val = $hash{$key};
                        if ( (length($val) == 0) || ($val =~ /^\s+$/) ) {
                            $val = '""';
                        }
                        $txt .= "-m $key $val\n";
                        $cnt++;
                    }
                }
                prt(" also now added to response file...");
            }
            prt("\n");
        }
        if (length($miss_mac_file) && (length($txt))) {
            write2file($txt,$miss_mac_file);
            prt("Written list for use as '-r $miss_mac_file' response file, after correction.\n");
        }
    }
}

# =============================================
sub show_warnings($) {
    my $val = shift;
    my $cnt = warn_missed_user_types();
    $cnt += warn_missed_user_defined();
    $cnt += warn_missed_user_libs();
   if (@warnings) {
      prt( "\nGot ".scalar @warnings." WARNINGS...\n" );
      foreach my $itm (@warnings) {
         prt("$itm\n");
      }
      prt("\n");
   } elsif ($cnt == 0) {
      prt("No warnings issued.\n");
   }
}

sub show_missing_conds() {
    my $rparams = get_ref_params();
    my $rmc = ${$rparams}{'REF_MISSED_CONDITIONS'};
    my $cnt = scalar keys(%{$rmc});
    if ($cnt) {
        if (VERB1()) {
            prt("[v1] List of $cnt missing conditionals...\n");
        } else {
            prt("Would list $cnt missing conditionals... if verbose >= 1\n");
        }
        my ($key,$val,$msg,@arr,$num,%dupes);
        $msg = "# List of $cnt missing conditionals...\n";
        $msg .= "# If used ONLY in TRUE or FALSE, then parameter should be correct.\n";
        $msg .= "# but if used TRUE and FALSE, then must select 0 or 1 only.\n";
        $cnt = 0;
        foreach $key (sort keys %{$rmc}) {
            $cnt++;
            $val = ${$rmc}{$key};
            @arr = split(/\|/,$val);
            %dupes = ();
            foreach $num (@arr) {
                $dupes{$num} = 1;
            }
            @arr = sort keys(%dupes);
            $val = '';
            foreach $num (@arr) {
                $val .= '|' if (length($val));
                $val .= $num;
            }
            $num = '0|1';
            if (scalar @arr == 1) {
                if ($val eq 'FALSE') {
                    $num = 0;
                } elsif ($val eq 'TRUE') {
                    $num = 1;
                } else {
                    $num .= '?';
                }
            } else {
                $msg .= "# TO BE SELECTED\n";
            }
            $msg .= "-c $key:$num\n";
            prt(" $cnt: -c $key:$num ($val)\n") if (VERB1());
        }
        if (length($missed_cond_list)) {
            write2file($msg,$missed_cond_list);
            prt("Written list of $cnt missing conditionals to [$missed_cond_list]\n");
        }
    } else {
        prt("No missing conditionals listed.\n");
    }
}


sub show_missing_subs2($) {
    my ($val) = shift;
    my $rparams = get_ref_params();
    my $rsnf = ${$rparams}{'CURR_SUBS_NOT_FOUND'};
    my @arr = keys %{$rsnf};
    my $cnt = scalar @arr;
    my $msg = '';
    #if ($dbg_lac13) {
    my ($key,$fil);
    if ($cnt) {
        $cnt = scalar @arr;
        prt("There are at least $cnt missing substitutions. ");
        if ($dbg_lac13) {
            prt("\n");
        } else {
            prt("Use '-d 113' to view list...\n");
        }
        foreach $key (sort @arr) {
            $fil = ${$rsnf}{$key};
            prt("Missing [$key], in [$fil]\n") if ($dbg_lac13);
            $msg .= "-m $key \"\"\n";
        }
        if (length($missing_list_file)) {
            write2file($msg,$missing_list_file);
            prt("List written to file [$missing_list_file] for review...\n");
        }
    } else {
        prt("[13] There are NO missing substitutions.\n") if ($val == 0);
    }
    #@arr = split $added_in_init;
    #$cnt = scalar @arr;
    #prt("But note added $cnt, [$added_in_init] in init...\n") if (length($added_in_init));
    #} elsif ($cnt && ($val == 0)) {
    #    prt("There are at least $cnt missing substitutions. Use '-d 13' to view.\n");
    #}
}

sub show_scanned_lines_and_time() {
    my $rparams = get_ref_params();
    my ($cnt,$tim,$fil);
    if (defined ${$rparams}{'TOTAL_LINE_COUNT'} && defined ${$rparams}{'CURR_BEGIN_TIME'}) {
        $cnt = ${$rparams}{'TOTAL_LINE_COUNT'};
        $tim = time();
        $fil = $tim - ${$rparams}{'CURR_BEGIN_TIME'};
        prt("Scanned a total of ".get_nn($cnt)." lines of source, in ".secs_HHMMSS($fil)." ...\n");
    }
}

sub pgm_exit($$) {
    my ($val,$msg) = @_;
    if ($msg =~ /TEMP EXIT/) {
        prt("$msg\n");
        close_log($outfile,$load_log);
        exit($val);
    }
    show_missing_conds() if ($val == 0);

    show_missing_subs2($val) if ($val != 2);

    if (length($conf_string) && ($val == 0) && !$no_conf_write) {
        write2file($conf_string,$conf_file);
        prt("Written configuration to [$conf_file]\n");
    }

    show_warnings($val) if ($val != 2);

    show_scanned_lines_and_time();

    if (length($msg)) {
        prt("Current CMD: [$curr_command]\n") if (length($curr_command));
        prt("\a\n") if ($msg =~ /^ERROR/);
        $msg =~ s/\n$//;
        $msg .= " time:".localtime(time())." ($val)\n";
        prt("$msg\n");
    } else {
        prt("Current CMD: [$curr_command] time [".localtime(time())."] ($val)\n");
    }
	prt("$final_batch_msg\n") if (length($final_batch_msg));
    close_log($outfile,$load_log);
    exit($val);
}


my %warnings_done = ();
my $last_warning = '';
sub prtw($) {
   my ($tx) = shift;
   $tx =~ s/\n$//;
   # avoid REPETATIVE warnings
   prt("$tx\n") if ($tx ne $last_warning);
   $last_warning = $tx;
   if (!defined $warnings_done{$tx}) {
       $warnings_done{$tx} = 1;
       push(@warnings,$tx);
   }
}


sub write_to_am_list($$) {
    my ($ff,$ok) = @_;
    if (length($amlistfile) && $write_am_list) {
        fix_rpath_per_os(\$ff);
        my $dff = $ff;
        my ($nm,$dir) = fileparse($ff);
        my $typ = 3;
        if ($ok) {
            my $lcid = lc(get_root_dir());  # g_in_dir
            my $lcfd = lc($dir);
            fix_rpath_per_os(\$lcid);
            fix_rpath_per_os(\$lcfd);
            if ($lcid eq $lcfd) {
                $typ = 1;
            } else {
                $typ = 2;
            }
        }
        if (-f $amlistfile) {
            if (open INF, "<$amlistfile") {
                my @lines = <INF>;
                close INF;
                my ($line,@arr,$cnt,$fff);
                my $lcinf = lc($dff);
                foreach $line (@lines) {
                    chomp $line;
                    @arr = split(/\s/,$line);
                    $cnt = scalar @arr;
                    if ($cnt == 2) {
                        $fff = lc($arr[1]);
                        if ($fff eq $lcinf) {
                            return;
                        }
                    }
                }
            }
            append2file("$typ $dff\n",$amlistfile);
        } else {
            write2file("$typ $dff\n",$amlistfile);
        }
    }
}

sub got_colon_split($) {
    my ($txt) = @_;
    my $len = length($txt);
    my ($i,$ch,$pc,$i2,$nc);
    $ch = '';
    for ($i = 0; $i < $len; $i++) {
        $pc = $ch;
        $ch = substr($txt,$i,1);
        if ($ch eq ':') {
            # skip over LETTER : SLASH
            $i2 = $i + 1;
            $nc = ($i2 < $len) ? substr($txt,$i2,1) : "";
            if (($nc =~ /(\\|\/)/) && ($pc =~ /\w+/)) {
                # next is slash - previous was letter - no split here
            } else {
                return 1;
            }
        }
    }
    return 0;
}

# take care with things like lib:C:\projs\lib - skip 'DRIVE_LETTER:\'
sub colon_split($) {
    my ($txt) = @_;
    my $len = length($txt);
    my @arr = ();
    my ($i,$ch,$tag,$pc,$i2,$nc);
    $ch = '';
    $tag = '';
    for ($i = 0; $i < $len; $i++) {
        $pc = $ch;
        $ch = substr($txt,$i,1);
        $i2 = $i + 1;
        $nc = ($i2 < $len) ? substr($txt,$i2,1) : "";
        if ($ch eq ':') {
            # skip over LETTER : SLASH
            if (($nc =~ /(\\|\/)/) && ($pc =~ /\w+/)) {
                # next is slash - previous was letter - no split here
            } else {
                #push(@arr,$tag) if (length($tag));
                push(@arr,$tag); # even BLANK tags get pushed
                $tag = ''; # kill current tag
                next;   # and go for NEXT
            }
        }
        $tag .= $ch;
    }
    push(@arr,$tag) if (length($tag));
    return @arr;
}

sub process_ac_file($) {
    my ($rparams) = @_;
    my $inf = ${$rparams}{'CURR_FILE'};
    return if (! -f $inf);
    #($g_in_name, $g_in_dir) = fileparse($inf);
    prt("Scanning [$inf] file... using lib_acscsn.pl:scan_configure_ac_file()\n");
    scan_configure_ac_file($rparams,0);
    mark_in_dir_scan($rparams,$inf);
    # ======================================================
    show_ac_hash($rparams);
    return $rparams;
}

#====================================================
# do NOT know WHY I have 2 subs that do the SAME thing
# sub sub_root_folder  AND sub_root_dir($)

sub sub_common_folder {
    my ($fil,$root) = @_;
    fix_rpath_per_os(\$fil);
    fix_rpath_per_os(\$root);
    my ($lfil,$lrot);
    if ($os =~ /win/i) {
        $lfil = lc($fil);
        $lrot = lc($root);
    } else {
        $lfil = $fil;
        $lrot = $root;
    }
    my $len1 = length($lfil);
    my $len2 = length($lrot);
    my ($i);
    for ($i = 0; (($i < $len1)&&($i < $len2)); $i++) {
        if (substr($lfil,$i,1) ne substr($lrot,$i,1)) {
            last;
        }
    }
    return substr($fil,$i);
}

sub sub_root_folder {
    my ($fil) = shift;
    my $rd = get_root_dir();
    return sub_common_folder($fil,$rd);
}

sub begins_with {
    my ($rt, $pt) = @_;
    my $ln = length($rt);
    my ($i);
    if (length($pt) >= $ln) {
        for ($i = 0; $i < $ln; $i++) {
            return 0 if (substr($rt,$i,1) ne substr($pt,$i,1));
        }
        return 1; # does indeed begin with...
    }
    return 0;
}

sub sub_first_from_second_if($$) {
    my ($d1,$d2) = @_;
    if (being_with($d1,$d2)) {
        $d2 = substr($d2, length($d1));
    }
    return $d2;
}

# VARIOUS FIXES FOR THE FILE NAME
# 1. ensure ALL DOS format
# 2. remove any simple dot relative, like '.\' from beginning
# 3. if given a FULL PATH name, remove C:\FG\20\FlightGear
# 4. if a relative name, remove FligthGear
# 5. if any removal, ensure any beginning '\' is removed
sub sub_root_dir($) {
    my ($ff) = shift;   # = $a_dir.$src
    fix_rpath_per_os(\$ff);
    my $rd = get_root_dir();
    fix_rpath_per_os(\$rd);
    if (begins_with($rd, $ff)) {
        $ff = substr($ff, length($rd));
    }
    return $ff;
}

sub set_anon_ref_hashes($) {
    my ($rparams) = @_;
    my %hash = ();
    my %targ = ();
    ${$rparams}{'CURR_REF_HASH'} = \%hash;
    ${$rparams}{'AM_FILE_TARGETS'} = \%targ;
}

sub process_one_am_file($);
# Process a Makefile.am file
sub process_one_am_file($) {
    my ($rparams) = @_;
    my $ramsdone = ${$rparams}{'REF_AMS_DONE'};
    my $fil = ${$rparams}{'AM_FILE'};
    $fil = fix_rel_path3($fil,'process_one_am_file');
    my $sfil = sub_root_folder($fil);

    return if (defined ${$ramsdone}{$fil});

    ${$ramsdone}{$fil} = 1; # make sure this file is marked DONE
    set_anon_ref_hashes($rparams);
    my $ramh = am_process_AM_file($rparams,0);
    # ${$refhash}{'CURR_AM_FILE'} = $fil;
    ${$ramsdone}{$fil} = $ramh; # for each file processed, keep the ref am hash
    mark_in_dir_scan($rparams,$fil);    # mark it as USED

    my ($p_tit,$p_dir,$p_ext) = fileparse( $fil, qr/\.[^.]*/ );
    my $do_subs = ${$rparams}{'PROCESS_SUBDIR'};
    my ($slist,@ar,$cnt);
    # NOTE WELL: BY default \$do_subs = \${\$rparams}{'PROCESS_SUBDIR'}; IS OFF
    if ($do_subs && (defined ${$ramh}{'SUBDIRS'})) {
        $slist = ${$ramh}{'SUBDIRS'};
        @ar = split(/\s+/,$slist);
        $cnt = scalar @ar;
        prt( "[09] Got $cnt subdirectories [$slist] ...from [$sfil]\n" ) if ($dbg_s09);
        foreach my $dir (@ar) {
            my $am = $p_dir.$dir.'\Makefile.am';
            fix_rpath_per_os(\$am);
            $am =~ s/\\\\/\\/g while ($am =~ /\\\\/);
            my $sam = sub_root_folder($am);
            if (-f $am) {
                prt( "[05] Processing AM file [$am], from [$fil] ...\n" ) if ($dbg_s05);
                ${$rparams}{'AM_FILE'} = $am;   # DOS form ensured
                process_one_am_file($rparams);
                ${$rparams}{'AM_FILE'} = $fil;
            } else {
                prtw( "[05] WARNING: AM [$am] NOT FOUND! in [$dir], from [$fil]!\n" ) if ($dbg_s05);
            }
        }
    } elsif ($do_subs) {
        prt( "[05] No SUBDIRS in AM file [$fil].\n" ) if ($dbg_s05);
    } elsif (defined ${$ramh}{'SUBDIRS'}) {
        my $slist = ${$ramh}{'SUBDIRS'};
        @ar = split(/\s+/,$slist);
        $cnt = scalar @ar;
        if ($cnt) {
            prt( "[05] PROCESS_SUBDIR off so $cnt SUBDIRS [$slist] AM file [$fil] NOT yet processed.\n" ) if ($dbg_s05);
        }
    }
    prt( "[05] END AM file [$fil].\n" ) if ($dbg_s05);
    return $ramh;
}

sub process_an_am_file($) {
    my ($rparams) = @_;

    my $rh = process_one_am_file($rparams);  # iteratively process the Makefile.am files
    #list_to_arrays($fil,\%g_programs,\%g_libraries,\%g_ams_done);
    #list_to_arrays($rparams);
    ##write_temp_dsp($dsp_outfile);
}

# [dbg_v40] STORE:1: In rcfgs (ra)[Release], [-NEW_OUTD-], [Release|Win32], & $dsp_sub_sub ] )
# [dbg_v40] STORE:2: In rcfgs (ra)[Debug], [-NEW_OUTD-], [Debug|Win32], & $dsp_sub_sub ] )
sub set_default_configs_2($) {
    my ($rh) = @_;
    #my $var1 = "-NEW_OUTD-";
    my $var1 = "";
    my $rcfgs = get_project_configs($rh);   # 'PROJECT_CFGS'
    my ($dsp_sub_sub,$confname,$conf);
    $dsp_sub_sub = get_default_sub3(0);
    $confname = 'Release';
    $conf = 'Release|WIN32';
    push(@{$rcfgs}, [ $confname, $var1, $conf, $dsp_sub_sub ]); # ONLY STORE OF 'PROJECT_CFGS'
    ${$rh}{'PROJECT_CCNT'}++;   # count of stored 'PROJECT_CFGS
    $dsp_sub_sub = get_default_sub3(1);
    $confname = 'Debug';
    $conf = 'Debug|WIN32';
    push(@{$rcfgs}, [ $confname, $var1, $conf, $dsp_sub_sub ]); # ONLY STORE OF 'PROJECT_CFGS'
    ${$rh}{'PROJECT_CCNT'}++;   # count of stored 'PROJECT_CFGS
}

# ###########################################################
# get DSP replacement values
# ==========================
sub get_user_rt($$) {
    my ($flag,$proj) = @_;
    my $urt = '';
    if ($proj_rt eq 'D') {
        if ($flag == 1) {
            $urt = '/MD';
        } else {
            $urt = '/MDd';
        }
    } else {
        if ($flag == 1) {
            $urt = '/MT';
        } else {
            $urt = '/MTd';
        }
    }
    prt("[v9] get_user_rt: flag=$flag, proj=$proj, return=[$urt]\n") if (VERB9());
    return $urt;
}

# GET -L dependent libraries option
## NOTE: Bit 1 == Release, else Debug
#my %by_proj_libraries = ();
#my %by_proj_libraryD = ();
#my %by_proj_libraryR = ();
#my %by_proj_libsused = ();
sub get_user_libs($$) {
    my ($flag,$proj) = @_;
    my $var1 = $proj_libs;
    my ($refh);
    if ((!defined $proj)||(length($proj) == 0)||($proj =~ /^\s+$/)) {
        pgm_exit(1,"ERROR: INTERNAL: get_user_libs call WITHOUT a PROJECT NAME!\n");
    }
    if ($flag & 1) {
        $refh = \%by_proj_libraryR;
        if (length($proj_libR)) {
            $var1 .= " " if (length($var1));
            $var1 .= $proj_libR;
        }
    } else {
        $refh = \%by_proj_libraryD;
        if (length($proj_libD)) {
            $var1 .= " " if (length($var1));
            $var1 .= $proj_libD;
        }
    }

    if (defined $by_proj_libraries{$proj}) {
        $var1 .= ' '.$by_proj_libraries{$proj};
    }

    if (defined ${$refh}{$proj}) {
        $var1 .= ' '.${$refh}{$proj};
    }
    $by_proj_libsused{$proj} = 1;
    prt("[v9] get_user_libs: flag=$flag, proj=$proj, return=[$var1]\n") if (VERB9());
    return $var1;
}

sub rationalise_lib_paths() {
    # need to CHECK the library paths, and
    # eliminate DUPLICATES
    my $rlist = get_user_libs(1,"<GLOBAL>");
    my $dlist = get_user_libs(2,"<GLOBAL>");
    my $rhr = space_split_to_rh($rlist);
    my $rhd = space_split_to_rh($dlist);
    my $plibs = '';
    my $plibr = '';
    my $plibd = '';
    my ($key);
    foreach $key (keys %{$rhr}) {
        next if ($key =~ /^\/libpath:/); # do path second
        if (defined ${$rhd}{$key}) {
            # this is COMMON
            $plibs .= ' ' if (length($plibs));
            $plibs .= $key;
        } else {
            # only for RELEASE
            $plibr .= ' ' if (length($plibr));
            $plibr .= $key;
        }
    }
    foreach $key (keys %{$rhr}) {
        next if ( !($key =~ /^\/libpath:/) ); # do paths now
        if (defined ${$rhd}{$key}) {
            # this is COMMON
            $plibs .= ' ' if (length($plibs));
            $plibs .= $key;
        } else {
            # only for RELEASE
            $plibr .= ' ' if (length($plibr));
            $plibr .= $key;
        }
    }
    foreach $key (keys %{$rhd}) {
        next if ($key =~ /^\/libpath:/); # do path second
        if (defined ${$rhr}{$key}) {
            # this is COMMON - already done
        } else {
            # only for DEBUG
            $plibd .= ' ' if (length($plibd));
            $plibd .= $key;
        }
    }
    foreach $key (keys %{$rhd}) {
        next if ( !($key =~ /^\/libpath:/) ); # do paths now
        if (defined ${$rhr}{$key}) {
            # this is COMMON - already done
        } else {
            # only for DEBUG
            $plibd .= ' ' if (length($plibd));
            $plibd .= $key;
        }
    }
    if (($plibs ne $proj_libs)||
        ($plibr ne $proj_libR)||
        ($plibd ne $proj_libD)) {
        prt("LIB CHANGE: [$plibs] ne [$proj_libs] and/or\n".
            "            [$plibr] ne [$proj_libR] and/or\n".
            "            [$plibd] ne [$proj_libD] and/or\n") if ($dbg_lib_path);
        $proj_libs = $plibs;
        $proj_libR = $plibr;
        $proj_libD = $plibd;
    }
}

# NAH, perhpas do NOT need to do this...
sub rationalise_proj_lib_path($) {
    my ($stg);
    my $rh = space_split_to_rh($stg);
    my ($key,$nstg);
    $nstg = '';
    foreach $key (keys %{$rh}) {
        $nstg .= ' ' if (length($nstg));
        $nstg .= $key;
    }
    return $nstg;
}

sub get_project_incs($) {
    my $proj = shift;
    my $pi = '';
    $pi = $by_proj_includes{$proj} if (defined $by_proj_includes{$proj});
    return $pi;
}

sub get_project_defs($) {
    my $proj = shift;
    my $pi = '';
    $pi = $by_proj_defines{$proj} if (defined $by_proj_defines{$proj});
    $by_proj_defused{$proj} = 1;
    return $pi;
}


# FIX20110323 - changed from 'get_user_defs:' to 'get_user_incs:'
# should ALSO allow DEFINES (-D) to also be in a BY PROJECT basis
# my %by_proj_includes = ();
sub get_user_incs($$) {
    my ($not_used,$proj) = @_;
    my $pi = get_project_incs($proj);
    $pi .= ' ' if (length($pi) && length($proj_incs));
    $pi .= $proj_incs;
    prt("[v9] get_user_incs: proj=$proj, return=[$pi]\n") if (VERB9());
    return $pi;
}

sub get_user_defs($$) {
    my ($not_used,$proj) = @_;
    my $pd = get_project_defs($proj);
    $pd .= ' ' if (length($pd) && length($proj_defs));
    $pd .= $proj_defs;
    prt("[v9] get_user_defs: proj=$proj, return=[$pd]\n") if (VERB9());
    return $pd;
}

# Auto output does the following -
# For libaries
# Debug:  '/out:"lib\barD.lib"'
# Release:'/out:"lib\barD.lib"'
# for programs
# Debug:  '/out:"bin\fooD.exe"'
# Release:'/out:"bin\foo.exe"'
# our $auto_on_flag = BITS = ${$rparams}{'CURR_AUTO_ON_FLAG'}
sub get_user_output($$) {
    my ($flag,$proj) = @_;
    my $line = '';
    if ($flag & 1) {
        if (length($proj_outputR)) {
            $line = $proj_outputR;
        }
    } elsif (length($proj_outputD)) {
        $line = $proj_outputD;
    }
    prt("[v9] get_user_defs: flag=$flag, proj=$proj, return=[$line]\n") if (VERB9());
    return $line;
}

sub get_user_inter($$) {
    my ($flag,$proj) = @_;
    my $line = '';
    if ( $flag & 1 ) {
        if (length($proj_interR)) {
            $line = $proj_interR;
        } else {
            $line = 'Release';
        }
    } else {
        if (length($proj_interD)) {
            $line = $proj_interD;
        } else {
            $line = 'Debug';
        }
    }
    return $line;
}

# setting defines
# FIX20110323 - All a global define like '-D PATH=C:\some\path
sub add_defined_item($) {
    my $txt = shift;
    my $msg = '';
    # if ($txt =~ /:/) {
    if (got_colon_split($txt)) {
        my (@a,$cnt,$pj);
        #@a = split(/:/,$txt);
        @a = colon_split($txt);
        $cnt = scalar @a;
        pgm_exit(1,"ERROR: Define [$txt] did NOT split in 2! Must be say -D TESTNUM=1:signal1...\n")
            if ($cnt != 2);
        $txt = '/D "'.$a[0].'"';
        $pj = $a[1];
        if (defined $by_proj_defines{$pj}) {
            $txt = " $txt";
            $by_proj_defines{$pj} .= $txt;
        } else {
            $by_proj_defines{$pj} .= $txt;
        }
        $msg = "only for project [$pj].";
    } else {
        $msg = "globally.";
        $txt = '/D "'.$txt.'"';
        $proj_defs .= ' ' if (length($proj_defs));
        $proj_defs .= $txt;
        $proj_defs = eliminate_dupes($proj_defs);
    }
    prt("Added [$txt] to compiler defines $msg.\n");
}

# SET a /I "include\path"
# FIX20110323 - allow on by project basis '-I SOME_VALUE:project_only'
# FIX20110803 - allow simple -I path1;path2;path3
sub add_include_item($) {
    my $cmd = shift;
    my $txt = $cmd;
    my (@a,$cnt,$pj);
    if (got_colon_split($txt)) {
        @a = colon_split($txt);
        $cnt = scalar @a;
        pgm_exit(1,"ERROR: Define [$txt] did NOT split in 2! Must be say -I C:\\some\\path:project\n")
            if ($cnt != 2);
        $txt = $a[0];
        fix_rpath_per_os(\$txt);
        $txt = '/I "'.$txt.'"';
        $pj = $a[1];
        if (defined $by_proj_includes{$pj}) {
            $txt = " $txt";
            $by_proj_includes{$pj} .= $txt; # -NEW_INCS- BY PROJECT
        } else {
            $by_proj_includes{$pj} = $txt;
        }
        prt("Added [$txt] to compiler includes for project [$pj].\n");
    } else {
        @a = split(/;+/,$txt);
        $txt = '';
        foreach $pj (@a) {
            if (length($pj)) {
                $txt .= " " if (length($txt));
                fix_rpath_per_os(\$pj);
                $txt .= '/I "'.$pj.'"';
            }
        }
        if (length($txt)) {
            $proj_incs .= ' ' if (length($proj_incs));
            $proj_incs .= $txt; # -NEW_INCS-
            $proj_incs = eliminate_dupes($proj_incs);
            prt("Added [$txt] to global compiler includes.\n");
        } else {
            pgm_exit(1,"ERROR: -I options FAILED to yield any /I paths! cmd = [$cmd]\n")
        }
    }
}

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

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


sub valid_rp_hash($) {
    my $rp = shift;
    #my $temp_copy_bat  = ${$rp}{'PROJECT_COPY_BAT'};
    my @arr = qw(PROJECT_NAME PROJECT_TYPE PROJECT_SOURCES PROJECT_TARGET PROJECT_DBG_WRITE PROJECT_FIX_REL
        PROJECT_AM_RHASH PROJECT_USER_LIBS PROJECT_USER_OUTS);
    my ($key);
    foreach $key (@arr) {
        if (!defined ${$rp}{$key}) {
            prt("ERROR: $key NOT defined in project hash\n");
            return 0;
        }
    }
    return 1;
}

sub fix_dir_string($) {
    my ($rdir) = @_;
    if (! ( ${$rdir} =~ /(\\|\/)$/) ) {
        ${$rdir} .= $PATH_SEP;
    }
}

sub is_same_file($$) {
    my ($f1,$f2) = @_;
    my $len = length($f1);
    return 0 if ($len != length($f2));
    my ($i,$c1,$c2);
    for ($i = 0; $i < $len; $i++) {
        $c1 = lc(substr($f1,$i,1));
        $c2 = lc(substr($f2,$i,1));
        return 0 if ($c1 ne $c2);
    }
    return 1;
}

my @missed_dir_cmp = ();
sub mark_in_dir_scan($$) {
    my ($rparams,$relfile) = @_;
    return if (! ${$rparams}{'CURR_DONE_SCAN'} );
    #my $dir = ${$rparams}{'CURR_FILE_DIR'};
    my $dir = ${$rparams}{'TARGET_DIR'};
    fix_dir_string(\$dir);
    my ($ff);
    if ($relfile =~ /^\w{1}:(\\|\/)/) {
        $ff = $relfile;
    } else {
        $ff = $dir.$relfile;
    }
    fix_rpath_per_os(\$ff);
    $ff = fix_rel_path($ff);
    my $rda = ${$rparams}{'CURR_DIR_SCAN'};
    my $cnt = scalar @{$rda};
    my ($i,$file);
    ### prt("Mark in dir scan [$relfile] [$ff] of $cnt files...\n");
    for ($i = 0; $i < $cnt; $i++) {
        $file = ${$rda}[$i][1];  # get FULL FILE
        if (is_same_file($ff,$file)) {
            #              0     1   2 3
            #push(@{$rda},[$file,$ff,0,0]);
            ${$rda}[$i][2] = 1;
            last;
        }
    }

    if ($i >= $cnt) {
        push(@missed_dir_cmp,$ff);
        ### prt("Missed in dir scan [$relfile] [$ff] in $i files...\n");
    }
}

sub file_in_array($$) {
    my ($file,$rxarr) = @_;
    my ($itm);
    my ($fil,$dir) = fileparse($file);
    foreach $itm (@{$rxarr}) {
        return 1 if ($itm eq $fil);
    }
    return 0;
}

sub prune_excluded_libs($$$) {
    my ($rparams,$proj_name,$cval) = @_;
    # if project itself is eXcluded, then keep ALL libs
    return $cval if ((length($cval) == 0)||($cval =~ /^\s+$/));
    return $cval if (is_project_all_excluded($proj_name));
    if ( defined ${$rparams}{'REF_JOINED_EXCLUDED'} ) {
        my $rxj = ${$rparams}{'REF_JOINED_EXCLUDED'};
        my $nval = ''; # else PRUNE list for EXLUDED items
        my @arr = space_split($cval);
        my ($key,$val,$lib,$add);
        my %hash = ();
        prt("[v9] Pruning library list for project [$proj_name] [$cval]\n") if (VERB9());
        foreach $key (keys %{$rxj}) {
            $val = ${$rxj}{$key};
            if ($val == 1) {
                $hash{$key} = 1;
                #$lib = $key.".lib";
                #$hash{$lib} = 1;
            }
        }
        foreach $key (@arr) {
            # RESTRICTION - libraries should be added WITHOUT a PATH
            $add = 1;
            if (is_win_library($key)) {
                $add = 1;
            } elsif ($key =~ /\.lib$/i) { # && !($key =~ /(\\|\/)/) )
                $lib = $key;
                $lib =~ s/\.lib$//i;
                $lib =~ s/D$//;
                $add = 0 if (defined $hash{$lib});
            }
            if ($add) {
                $nval .= ' ' if (length($nval));
                $nval .= $key;
            }
        }
        prt("[v9] Project libs adjusted to [$nval]\n") if (VERB9() && ($cval ne $nval));
        return $nval;
    }
    return $cval;
}

# need better guard against DUPLICATE source being added
sub create_proj_dsp($$$$) {
    my ($fil,$odir,$rparams,$rp) = @_;

    if ( ! valid_rp_hash($rp) ) {
        prtw("WARNING: INTERNAL ERROR: 'project' HAS INVALID HASH! Check script!\n");
        return;
    }
    if (!defined ${$rparams}{'ROOT_FOLDER'}) {
        pgm_exit(1,"ERROR: No ROOT_FOLDER in \$rparams! FIX ME!\n");
    }
    #if (!defined ${$rp}{'PROJECT_ROOT'}) {
    #    pgm_exit(1,"ERROR: No PROJECT_ROOT in \$rp! FIX ME!\n");
    #}
    my ($line,$key,$group,$filter,$ok,$rdir,$cfil,$cdir,$sfil,$tdsp,$msg);
    my ($name,$type,$i,$conf,$rh2,$flag,$cnt,$i2);
    my ($confname,$var1,$rcfgs,$tmp);
    my $proj_title = ${$rparams}{'PROJECT_NAME_MASTER'};
    # 13/05/2012 - ERROR if NO project title
    if ((length($proj_title) == 0)||($proj_title =~ /^\s+$/)||($proj_title =~ /\s/)) {
        pgm_exit(1,"ERROR: Have NO (or bad) project title [$proj_title]!\n".
            "That is 'PROJECT_NAME_MASTER' has NOT been given a value!\n".
            "Can ONLY abort!\n");
    }

    ### DEBUG ### show_ref_hash($rparams,1);
    ### DEBUG ### show_ref_hash($rp,1);
    ### DEBUG ### pgm_exit(1,"TEMP EXIT");
    my $auto_on = ${$rparams}{'CURR_AUTO_ON_FLAG'};
    my $proj_name = ${$rp}{'PROJECT_NAME'};
    my $proj_type = ${$rp}{'PROJECT_TYPE'};
    my $rinp_srcs = ${$rp}{'PROJECT_SOURCES'};
    my $proj_targ = ${$rp}{'PROJECT_TARGET'};
    my $dbg_write = ${$rp}{'PROJECT_DBG_WRITE'};
    #my $temp_copy_bat  = ${$rp}{'PROJECT_COPY_BAT'};
    my $fix_rel   = ${$rp}{'PROJECT_FIX_REL'};
    my $rhash     = ${$rp}{'PROJECT_AM_RHASH'}; # get AM file PROJECT hash
    my $rex       = ${$rparams}{'AM_EXCLUDED_DIRS'}; # get the EXCLUDED (if any!)
    my $amfil     = ${$rhash}{':_AM_FILE_SRC_:'};
    my $post_stg  = '';
    my $proj_root = ${$rparams}{'ROOT_FOLDER'};
    my $proj_am_file = '';
    if (defined ${$rhash}{':_AM_FILE_SRC_:'}) {
        $proj_am_file = ${$rhash}{':_AM_FILE_SRC_:'};
    }
    if (defined ${$rp}{'PROJECT_POST_EVENT'}) {
        $post_stg = ${$rp}{'PROJECT_POST_EVENT'};
    }
    my ($n,$d)    = fileparse($amfil);
    my $dir = sub_root_dir($d);
    $dir =~ s/\\$//;    # remove TAIL
    $msg = (defined ${$rex}{$dir}) ? "EXCLUDED" : "ok";
    my $xsrcs = '';
    my $isinx = is_project_src_excluded($proj_name,\$xsrcs);
    $xsrcs = '' if ($xsrcs eq '<all>');
    my ($xit);
    my @xarr = split(/;/,$xsrcs);

    my ($nm,$dr,$ex) = fileparse($fil, qr/\.[^.]*/ );

    my $hr = get_default_ref_hash($fil); # this it the VERSION 1 - create REF HASH - used to write _DSP3
    $name = $proj_name;
    $tdsp = $odir;
    $tdsp .= $PATH_SEP if (!($tdsp =~ /(\\|\/)$/));
    $tdsp .= $proj_name.".dsp";
    $key = 'CURR_AUTO_ON_FLAG';
    ${$hr}{$key} = $auto_on;
    $key = 'PROJECT_TITLE';
    ${$hr}{$key} = $proj_title; # pass on the PROJECT TITLE to each project $key = 'PROJECT_TITLE';
    $key = 'PROJECT_AM_FILE';
    ${$hr}{$key} = $proj_am_file; # pass on the PROJECT AM FILE to each project = $key = 'PROJECT_AM_FILE';
    $key = 'PROJECT_ROOT';
    ${$hr}{$key} = $proj_root;
    $key = 'PROJECT_NAME';
    ${$hr}{$key} = $proj_name;
    #$key = 'PROJECT_TYPE';
    $key = 'PROJECT_APTP';
    if ( get_app_type_4_short($proj_type, \$type) ) {
        ${$hr}{$key} = $type;
        prt("[v2] Set project type to [$type], from [$proj_type] $msg (dw=$dbg_write)\n") if (VERB2());
    } else {
        pgm_exit(1,"ERROR: INTERNAL: Unable to get project type from [$proj_type]! Only 'CA', 'SL', 'DLL', 'WA'!\n");
    }

    if ($isinx) {
        if (length($xsrcs)) {
            prt("[v9] project [$proj_type] eXcludes: [$xsrcs]\n") if (VERB9());
        } else {
            prt("[v9] project [$proj_type] eXclude WHOLE PROJECT!\n") if (VERB9());
        }
    } elsif (length($xsrcs)) {
        prtw("WARNING: INTERNAL: Have eXclude list [$xsrcs], but \$isinx not SET! ($isinx)\n");
    } else {
        prt("[v9] project [$proj_type] NOT in eXclude\n") if (VERB9());
    }

    ${$rp}{'PROJECT_DSP'}     = $tdsp;
    ${$rp}{'PROJECT_DSP_TMP'} = $fil;

    my @sources = ();
    my %dupe_srcs = ();
    foreach $line (@{$rinp_srcs}) {
        next if (defined $dupe_srcs{$line});
        $ok = 0;
        if ($fix_rel) {
            ($cfil,$cdir) = fileparse($line);
            # $rdir = get_relative_path($cdir,$odir);
            $rdir = get_rel_dos_path($cdir,$odir);
            $sfil = $rdir.$cfil;
            #prt("From [$line], to [$proj_targ], got rel [$sfil]\n");
            prt("[v5] for [$line],\n     to [$proj_targ], got\n") if (VERB5());
        } else {
            $sfil = $line;
        }
        prt("[v2] Relative source [$sfil]\n") if (VERB2());
        mark_in_dir_scan($rparams,$line);
        if (is_c_source_extended($line)) {
            # if (($line =~ /\.cxx$/i) || ($line =~ /\.c$/i) || ($line =~ /\.cpp$/i) || ($line =~ /\.cc$/i))
            $filter = get_def_src_filt();
            $group = get_def_src_grp();
            $ok = 1;
        } elsif (is_h_source_extended($line)) {
            # elsif ( ($line =~ /\.hxx$/i) || ($line =~ /\.h$/i) || ($line =~ /\.hpp$/i) )
            $filter = get_def_hdr_filt();
            $group = get_def_hdr_grp();
            $ok = 1;
        } elsif (is_resource_file($line)) {
            $filter = get_def_rcs_filt();
            $group = get_def_rcs_grp();
            $ok = 1;
        }

        if ($ok) {
            $xit = 0;
            # failed with ($xsrcs =~ /\b$sfil\b/)
            if ($isinx && length($xsrcs) && file_in_array($sfil,\@xarr)) { 
                $xit = 1;
                prt("[v2] Excluding from build source [$sfil]\n") if (VERB5());
            }
            #                0      1       2        3
            push(@sources, [ $sfil, $group, $filter, $xit, '' ]);   # ${$rph}{'PROJECT_SRCS'}
        } else {
            prtw("WARNING: CHECK Discarded SOURCE item [$line]\n");
		}
        $dupe_srcs{$line} = 1;
        $dupe_srcs{$sfil} = 1;
	}
    if (@sources) {
        $key = 'PROJECT_SRCS';
        ${$hr}{$key} = [@sources];
    } else {
        # pgm_exit(1,"ERROR: Project: [$proj_name] No sources!!!\n");
        prtw("WARNING: Project: [$proj_name] No sources!!! [$proj_am_file]\n");
        return;
    }

    set_default_configs_2($hr);     # set Release and Debug

    $key = 'PROJECT_CFGS';
    if (defined ${$hr}{$key}) {
        $rcfgs = ${$hr}{$key};
        $cnt = scalar @{$rcfgs};
        prt( "[v5] Project: [$proj_name], got $cnt CONFIGS...\n" ) if (VERB5());
        my $outdir = '';
        for ($i = 0; $i < $cnt; $i++) {
            $i2 = $i + 1;
            #                   0          1          2           3
            #                   Debug      -NEW_OUTD- Debug|WIN32
            # push(@{$rcfgs}, [ $confname, $var1,     $conf,      $dsp_sub_sub ]);
            $confname = ${$rcfgs}[$i][0];
            #$var1     = ${$rcfgs}[$i][1]; # has no meaning!!!
            $conf     = ${$rcfgs}[$i][2];
            $rh2      = ${$rcfgs}[$i][3];
            if (get_act_config_type($conf)) {   # test /Release/i and /Debug/i and others
                $flag = 2;
                $outdir = 'Debug';
            } else {
                $flag = 1;
                $outdir = 'Release'
            }
#            if (($conf =~ /Release/i)||($confname =~ /Release/i)) {
#                $flag = 1;
#            } elsif (($conf =~ /Debug/i)||($confname =~ /Debug/i)) {
#                $flag = 2;
#            } else {
#                pgm_exit(1,"ERROR: Can NOT set config type as 'Release' or 'Debug'! Got [$conf] [$confname]\n");
#            }

            prt("[v5] $i2: [$conf] [$confname] ($flag) [$proj_name]\n") if (VERB5());
            $key = '-NEW_OUTD-';
            if (defined ${$rh2}{$key}) {
                $outdir = ${$rh2}{$key};
            }
            foreach $key (keys %{$rh2}) {
                $line = ${$rh2}{$key};
                $var1 = $line;  # start the SAME
                $msg = '';
                if ($key eq '-NEW_OUT-') {  # of form '/out:"bin\foo.exe"'
                    $var1 = ${$rp}{'PROJECT_USER_OUTS'}->($flag,$line);;
                } elsif (($key eq '-NEW_OUTD-') || ($key eq '-NEW_INTER-')) {
                    # Intermediate or Output DIRECTORY
                    $var1 = get_user_inter($flag,$proj_name);
                    if ($auto_on & 1) {
                        if ($proj_name =~ /[\w\.-]+/) {
                            if ($var1 eq '"Debug"') {
                                $var1 = "\"Debug\\$proj_name\"";
                            } elsif ($var1 eq '"Release"') {
                                $var1 = "\"Release\\$proj_name\"";
                            }
                            $outdir = strip_quotes($var1);
                        } else {
                            prtw("WARNING: auto_on_flag=$auto_on, but prog name [$proj_name] NOT just [an,.,-]??? CHECK ME\n");
                        }
                    }
                } elsif ($key eq '-NEW_POST-') {
                    # -NEW_POST- = []
                    if (length($post_stg)) { # ($auto_on & 2) {
                        my @arr = split(";",$post_stg);
                        $tmp = scalar @arr;
                        if (length($line)) {
                            prtw("WARNING: post event [$line] being replaces! [$post_stg]\n");
                        }
                        pgm_exit(1,"ERROR: Post string [$post_stg] did not split 3 or 6! got $tmp!\n") if (($tmp != 3)&&($tmp != 6));
                        my $od = $arr[0];
                        my $srcname = $arr[1];
                        my $dstname = $srcname;
                        $dstname = $arr[2] if ($flag == 2);
                        ## Begin Special Build Tool
                        ## SOURCE="$(InputPath)"
                        #PostBuild_Desc=Copy Release Release\getopt\getopt.lib to lib\getopt.lib
                        #PostBuild_Cmds=if NOT EXIST lib md lib	copy Release\getopt\getopt.lib lib\getopt.lib
                        ## End Special Build Tool
                        $var1 = "# Begin Special Build Tool\n";
                        $var1 .= "# SOURCE=\"\$(InputPath)\"\n";
                        $var1 .= "PostBuild_Desc=Copy $outdir\\$srcname to $od\\$dstname\n";
                        $var1 .= "PostBuild_Cmds=if not exist $od\\. md $od\tcopy $outdir\\$srcname $od\\$dstname";
                        if ($tmp == 6) {
                            $od = $arr[3];
                            $srcname = $arr[4];
                            $dstname = $srcname;
                            $dstname = $arr[5] if ($flag == 2);
                            $var1 .= "\tif not exist $od\\. md $od\tcopy copy $outdir\\$srcname $od\\$dstname";
                        }
                        $var1 .= "\n";
                        $var1 .= "# End Special Build Tool\n";
                        $msg .= "Added post event [$var1]\n";
                    }
                } elsif ($key eq '-NEW_INCS-') {
                    $var1 = get_user_incs($flag,$proj_name); # -NEW_INCS- = []
                } elsif ($key eq '-NEW_LIBS-') {
                    $var1 = '';
                    #if (($proj_type ne 'SL') && ($proj_type ne 'DLL')) {
                    if ($proj_type ne 'SL') {
                        # $var1 = get_user_libs($flag,$line); # -NEW_LIBS- = [Winmm.lib ws2_32.lib]
                        # $var1 = ${$rparams}{'CURR_USER_LIBS'}->($flag,$line);
                        $var1 = ${$rp}{'PROJECT_USER_LIBS'}->($flag,$proj_name); # = get_user_libs
                        $var1 = prune_excluded_libs($rparams,$proj_name,$var1);
                    }
                } elsif ($key eq '-NEW_DEFS-') {
                    $var1 = get_user_defs($flag,$proj_name); # -NEW_DEFS- = [/D "_CRT_SECURE_NO_WARNINGS"]
                } elsif ($key eq '-NEW_RT-') {
                    $var1 = get_user_rt($flag,$proj_name); # -NEW_RT- = [/MT]
                } else {
                    prtw("WARNING: Key [$key] NOT in if table!?!?\n");
                }
                if ($line ne $var1) {
                    $msg = "changed to [$var1]";
                    ${$rh2}{$key} = $var1;
                }
                prt("[v9] $key = [$line] $msg\n") if (VERB9());
            }
        }
    } else {
        pgm_exit(1,"INTERNAL ERROR: Hash does NOT have key [$key]!\n");
    }

    if ($only_dry_run) {
        prt("Write of DSP to [$fil] AVOIDED, due to DRY RUN ON!\n") if ($dbg_write);
        return;
    }


    # prt("Writing DSP...\n");
    if ($write_dsp_files) {
        if ( write_hash_to_DSP3( $fil, $hr, $dbg_write ) ) {
            prt("Written DSP to [$fil]\n") if ($dbg_write);
        } else {
            prtw("WARNING: FAILED TO WRITE DSP FILE!\n");
        }
    } else {
        $dsp_files_skipped++;
    }

    if (do_hash_cmake_test($hr)) {
        prtw("WARNING: PROJECT $proj_name $type FAILED CMAKE HASH TEST!\n");
    } else {
        my $rcph = get_cmake_proj_hash_ref();
        push(@{$rcph},$hr);
    }
}

# 1: $(MACRO)
# 2: ${MACRO}
# 3: @MACRO@
# 4: $MACRO
sub get_macro_type_and_key($$) {
    my ($txt,$rkey) = @_;
    my $typ = 0;
    my $key = '';
    if ($txt =~ /^\$\((\w+)\)$/) {
        $key = $1;
        $typ = 1;   # $(MACRO)
    } elsif ($txt =~ /^\$\{(\w+)\}$/) {
        $key = $1;
        $typ = 2;   # ${MACRO}
    } elsif ($txt =~ /^\@(\w+)\@$/) {
        $key = $1;
        $typ = 3;   # @MACRO@
    } elsif ($txt =~ /^\$(\w+)$/) {
        $key = $1;
        $typ = 4;   # $MACRO
    }
    ${$rkey} = $key;
    return $typ;
}

sub get_macro_new_value($$$$$) {
    my ($key,$rparams,$rhash,$rnval,$all) = @_;
    my $rus  = ${$rparams}{'CURR_USER_SUBS'};
    my $rcomsubs = ${$rparams}{'CURR_COMMON_SUBS'};
    my $rglobhash = ${$rparams}{'REF_GLOBAL_HASH'};
    my $nval = '';
    my $fnd = 0;
    my ($tmp,@arr,$k,$v,$cnt);
    my ($ckey,$copt,$trfa,$usrtf,$msg);

    if (defined ${$rus}{$key}) {
        $nval = ${$rus}{$key};
        $fnd = 4;
    } elsif (defined ${$rcomsubs}{$key}) {
        $nval = ${$rcomsubs}{$key};
        $fnd = 3;
    } elsif (defined ${$rglobhash}{$key}) {
        $nval = ${$rglobhash}{$key};
        $fnd = 5;
    } elsif (defined ${$rhash}{$key}) {
        $nval = ${$rhash}{$key};
        $fnd = 1;
    } else {
        my $res = key_in_cond_TRUE_or_FALSE($key,$rhash,\$tmp);
        if ($res) {
            my $rdef_conds = ${$rparams}{'REF_DEF_CONDITIONS'};
            my $rmis_conds = ${$rparams}{'REF_MISSED_CONDITIONS'};
            $cnt = scalar @{$tmp};
            prt("[23] Found condition key(s) $cnt\n") if ($dbg_s23);
            foreach $k (@{$tmp}) {
                $v = ${$rhash}{$k};
                #next if ( !(length($v) && ((length($nval) == 0) || ($v ne $nval))) );
                if ($k =~ /^(\w+)\s+if\s+(\w+)\@_(TRUE|FALSE)\@$/) {
                    $ckey = $1;
                    $copt = $2;
                    $trfa = $3;
                    $msg = "Per key [$k], got value [$v] opt [$copt] trfa [$trfa] ($all)";
                    if (defined ${$rdef_conds}{$copt}) {
                        $usrtf = ${$rdef_conds}{$copt};
                        $msg .= " usr [$usrtf]";
                        if ($usrtf eq $trfa) {
                            if ( length($v) && ((length($nval) == 0) || ($v ne $nval))) {
                                $nval .= ' ' if (length($nval));
                                $nval .= $v;
                                $msg .= " added";
                            } else {
                                if (length($v)) {
                                    $msg .= " NOT added";
                                } else {
                                    $msg .= " NULL";
                                }
                            }
                        } else {
                            if ($all) {
                                if ( length($v) && ((length($nval) == 0) || ($v ne $nval))) {
                                    $nval .= ' ' if (length($nval));
                                    $nval .= $v;
                                    $msg .= " ADDED";
                                } else {
                                    if (length($v)) {
                                        $msg .= " NOT ADDED";
                                    } else {
                                        $msg .= " null";
                                    }
                                }
                            } else {
                                if (length($v)) {
                                    $msg .= "\n Discarded [$v] because NOT per user [$usrtf]";
                                } else {
                                    $msg .= ' Null';
                                }
                            }
                        }
                    } else {
                        $msg .= " NUD";
                        if ($all) {
                            if ( length($v) && ((length($nval) == 0) || ($v ne $nval))) {
                                $nval .= ' ' if (length($nval));
                                $nval .= $v;
                                $msg .= " Added";
                            } else {
                                 $msg .= " not added";
                            }
                        } else {
                            prtw("WARNING:[1] Discarded value [$v], since [$copt] NOT user defined! TF [$trfa]\n");
                            if (defined ${$rmis_conds}{$copt}) {
                                $v = ${$rmis_conds}{$copt};
                                if ( !($v =~ /$trfa/) ) {
                                    ${$rmis_conds}{$copt} .= "|$trfa";
                                }
                            } else {
                                ${$rmis_conds}{$copt} = $trfa;
                            }
                            $msg .= " DISCARDED";
                        }
                    }
                    prt("[23] $msg\n") if ($dbg_s23);
                } else {
                    pgm_exit(1,"ERROR INTERNAL: Condition key [$k] DOES NOT CONFORM!\n");
                }
            }
            $fnd = 2;
        }
    }
    ${$rnval} = $nval;
    return $fnd;
}

sub do_all_substitutions($$$$) {
    my ($line,$rparams,$rhash,$all) = @_;
    if (($line =~ /\$/)||($line =~ /\@\w+\@/)) {
        my ($itm,$typ,$key,$fnd,$nval,$cnt);
        my $oline = $line;
        my @arr = am_macro_split($line,0);  # get only MACROS
        $cnt = scalar @arr;
        prt("[23] Processing substitutions in [$line] split $cnt...\n") if ($dbg_s23);
        foreach $itm (@arr) {
            $key = '';
            $typ = get_macro_type_and_key($itm,\$key);
            prt("[23] Got macro type $typ, for [$itm] key [$key]\n") if ($dbg_s23);
            next if ($typ == 0);
            $nval = 'What!';
            $fnd = get_macro_new_value($key,$rparams,$rhash,\$nval,$all);
            prt("[23] For [$key], got $fnd, nval = [$nval]\n") if ($dbg_s23);
            if ($typ == 1) {
                # 1: $(MACRO)
                $line =~ s/\$\($key\)/$nval/;
            } elsif ($typ == 2) {
                # 2: ${MACRO}
                $line =~ s/\$\{$key\}/$nval/;
            } elsif ($typ == 3) {
                # 3: @MACRO@
                $line =~ s/\@$key\@/$nval/;
            } elsif ($typ == 4) {
                # 4: $MACRO
                $line =~ s/\$$key/$nval/;
            } else {
                pgm_exit(2,"ERROR INTERNAL: get macro type and key returned BAD type [$typ]! Should be 1 to 4!! [$itm] [$key] [$nval]\n");
            }
        }
        if ($oline eq $line) {
            pgm_exit(2,"Problem: MACRO substitution FAILED\n");
        } else {
            prt("[23] Line IN  [$oline]\n") if ($dbg_s23);
            prt("[23] Line OUT [$line]\n") if ($dbg_s23);
        }
    }
    return $line;
}

sub sanitise_subdir_value($) {
    my $val = shift;
    my @arr = split(/\s+/,$val);
    my %h = ();    # clear the HASH
    my ($v);
    foreach $v (@arr) {
        if (length($v) && ($v ne '.')) {
            $h{$v} = 1;
        }
    }
    @arr = keys %h;
    $val = '';
    foreach $v (@arr) {
        $val .= ' ' if (length($val));
        $val .= $v;
    }
    return $val;
}

sub hoh_process_value($$$$$$$) {
    my ($val,$dir,$sdir,$rsd,$all,$rparams,$key) = @_;
    my @arr = split(/\s+/,$val);
    my ($v,$msg,$sdcnt);
    my ($ckey,$copt,$trfa,$usrtf);
    my ($ff,$f);
    $sdcnt = 0;
    if ($key =~ /^(\w+)\s+if\s+(\w+)\@_(TRUE|FALSE)\@$/) {
        $ckey = $1;
        $copt = $2;
        $trfa = $3;
        my $rdef_conds = ${$rparams}{'REF_DEF_CONDITIONS'};
        my $rmis_conds = ${$rparams}{'REF_MISSED_CONDITIONS'};
        foreach $v (@arr) {
            $ff = $dir.$v;
            $f = $sdir.$v;
            $msg = " Subdir [$f]";
            if (-d $ff) {
                $msg .= " ok";
            } else {
                $msg .= " NOT FOUND"
            }
            if (! defined ${$rsd}{$f}) {
                if (defined ${$rdef_conds}{$copt}) {
                    $usrtf = ${$rdef_conds}{$copt};
                    $msg .= " usr [$usrtf]";
                    if ($usrtf eq $trfa) {
                        ${$rsd}{$f} = $ff;
                        $msg .= " added";
                    } else {
                        if ($all) {
                            ${$rsd}{$f} = $ff;
                            $msg .= " ADDED";
                        } else {
                            $msg .= " NOT ADDED";
                            $msg .= "\n Discarded [$v] because NOT per user [$usrtf]";
                        }
                    }
                } else {
                    # NOT USER DEFINED
                    $msg .= " NUD";
                    if ($all) {
                         ${$rsd}{$f} = $ff;
                         $msg .= " Added";
                    } else {
                        $msg .= " DISCARDED";
                        prtw("WARNING:2: Discarded value [$v], since [$copt] NOT user defined! TF [$trfa]\n");
                        if (defined ${$rmis_conds}{$copt}) {
                            $v = ${$rmis_conds}{$copt};
                            if ( !($v =~ /$trfa/) ) {
                                ${$rmis_conds}{$copt} .= "|$trfa";
                                $msg .= "+";
                            }
                        } else {
                            ${$rmis_conds}{$copt} = $trfa;
                        }
                    }
                }
            } else {
                # aleady defined in the subdir list
                $msg .= " repeat";
            }
            prt("[23] $msg\n") if ($dbg_s23);
        } # for each subdir
    } else {
        # NOT a TRUE FALSE key, so add em all
        foreach $v (@arr) {
            $ff = $dir.$v;
            $f = $sdir.$v;
            $msg = " subdir [$f]";
            if (! defined ${$rsd}{$f}) {
                # get a final (rel) SUBDIR list
                ${$rsd}{$f} = $ff;
                $msg .= " added";
                if (-d $ff) {
                    $msg .= " ok";
                } else {
                    $msg .= " NOT FOUND"
                }
            } else {
                $msg .= " repeat";
            }
            $sdcnt++;
            prt("[23] $sdcnt: $msg\n") if ($dbg_s23);
        }
    }
}

sub process_hoh_all($$) {
    my ($rparams,$all) = @_;
    my $rprojh = ${$rparams}{'REF_PROJECTS_HASH'};
    my $rhohsubs = ${$rparams}{'REF_HOH_SUBS'};
    my $ramsdone = ${$rparams}{'REF_AMS_DONE'};
    my $amcnt = scalar keys(%{$ramsdone});
    my ($file,$href,$val,$sfil,$key);
    my ($proj,$rp,$rhash,$amfil,$fnd,$cnt,@arr,$v);
    my ($tit,$dir,$sdir);
    my %done = ();
    my %missed = ();
    $cnt = 0;
    my %subdirs = ();
    my $rsd = \%subdirs;
    prt("\n[23] ") if ($dbg_s23);
    prt("Display of HOH SUBS... total $amcnt AM files... ($all)\n") if ($dbg_s23 || $all);
    foreach $proj (keys %{$rprojh}) {
        $rp = ${$rprojh}{$proj};
        $rhash = ${$rp}{'PROJECT_AM_RHASH'}; # add to PROJECT hash
        $amfil = ${$rhash}{':_AM_FILE_SRC_:'};
        next if (defined $done{$amfil});
        next if (defined $missed{$amfil});
        $fnd = 0;
        foreach $file (keys %{$rhohsubs}) {
            if ($file eq $amfil) {
                $fnd = 1;
                last;
            }
        }
        if ($fnd) {
            $cnt++;
            $sfil = sub_root_dir($amfil);
            $href = ${$rhohsubs}{$amfil};
            ($tit,$dir) = fileparse($amfil);
            ($tit,$sdir) = fileparse($sfil);
            $sdir = '' if ($sdir =~ /^\.(\\|\/)$/);
            prt("[23] $cnt: FILE: [$sfil] [$sdir] [$amfil]\n") if ($dbg_s23);
            foreach $key (keys %{$href}) {
                $val = ${$href}{$key};
                prt(" [23] $cnt: key [$key] [$val]\n") if ($dbg_s23);
                $val = sanitise_subdir_value($val);
                $val = trim_all(do_all_substitutions($val,$rparams,$rhash,$all)) if (length($val));
                if (length($val)) {
                    prt(" [23] After SUBS [$key] = [$val] ($all)\n") if ($dbg_s23);
                    hoh_process_value($val,$dir,$sdir,$rsd,$all,$rparams,$key);
                } else {
                    prt(" [23] [$key] = <BLANK>\n") if ($dbg_s23);
                }
                # ${$href}{$key} = $val;
            }
            $done{$amfil} = 1;
        } else {
            $missed{$amfil} = 1;
        }
    }
    prt("[23] Done $cnt AM files, with projects...\n") if ($dbg_s23);
    $fnd = 0;
    foreach $file (keys %{$rhohsubs}) {
        next if (defined $done{$file});
        $fnd++;
    }
    if ($fnd) {
        prt("[23] Got $fnd AM files, without projects...\n") if ($dbg_s23);
        foreach $amfil (keys %{$rhohsubs}) {
            next if (defined $done{$amfil});
            $cnt++;
            $sfil = sub_root_dir($amfil);
            $href = ${$rhohsubs}{$amfil};
            ($tit,$dir) = fileparse($amfil);
            ($tit,$sdir) = fileparse($sfil);
            $sdir = '' if ($sdir =~ /^\.(\\|\/)$/);
            prt("[23] $cnt: FILE: [$sfil] [$sdir] [$amfil]\n") if ($dbg_s23);
            foreach $key (keys %{$href}) {
                $val = ${$href}{$key};
                $val = sanitise_subdir_value($val);
                $val = trim_all(do_all_substitutions($val,$rparams,$rhash,$all)) if (length($val));
                if (length($val)) {
                    prt(" [23] After SUBS2 [$key] = [$val]\n") if ($dbg_s23);
                    hoh_process_value($val,$dir,$sdir,$rsd,$all,$rparams,$key);
                } else {
                    prt(" [23] [$key] = <BLANK>\n") if ($dbg_s23);
                }
                # ${$href}{$key} = $val;
            }
            $done{$amfil} = 1;
        }
        prt("[23] Done total $cnt AM files, with and without projects...\n") if ($dbg_s23);
    }

    $fnd = scalar keys(%missed);
    if ($fnd) {
        prt("[23] Problem: missed $fnd files...\n") if ($dbg_s23);
        foreach $file (keys %missed) {
            $cnt++;
            prt(" [23] $cnt: [$file]\n") if ($dbg_s23);
        }
    }
    $fnd = scalar keys(%{$rsd});
    prt("[23] END HOH - All $cnt files processed... ($amcnt) ($all)... $fnd subdirs...\n") if ($dbg_s23);
    return $rsd;
}

sub process_hoh_subs($) {
    my ($rparams) = @_;
    my $auto_on = ${$rparams}{'CURR_AUTO_ON_FLAG'};
    my ($key,$cnt);
    $cnt = 0;
    my %excluded = ();
    my $rex = \%excluded;
    if ($auto_on & 256) {
        my $rsdall = process_hoh_all($rparams,1);
        my $rsddef = process_hoh_all($rparams,0);
        $key = scalar keys(%{$rsdall});
        $cnt = scalar keys(%{$rsddef});
        prt("[23] Got $key subdirs for all, and $cnt for reduced...\n") if ($dbg_s23);
        $cnt = 0;
        foreach $key (keys %{$rsdall}) {
            if (! defined ${$rsddef}{$key}) {
                $cnt++;
            }
        }
        if ($cnt) {
            prt("Got $cnt sub-directories EXCLUDED...\n");
            $cnt = 0;
            foreach $key (keys %{$rsdall}) {
                if (! defined ${$rsddef}{$key}) {
                    $cnt++;
                    prt("[23]  $cnt: [$key]\n") if ($dbg_s23);
                    ${$rex}{$key} = 1;
                }
            }
        } else {
            prt("AH! Got NO subdirectories EXCLUDED...\n");
        }
    } else {
        prt("EXCLUDE BIT:256 is OFF. No projects excluded.\n");
    }

    ${$rparams}{'AM_EXCLUDED_DIRS'} = $rex; # store the EXCLUDED (if any!)
    return $rex;
}

sub get_EXCLUDE_msg($$$) {
    my ($rparams,$rp,$rex) = @_;
    my $proj_name =  ${$rp}{'PROJECT_NAME'};
    #prt("Checking $proj_name...\n") if ($proj_name =~ /libpangocairo/);
    return "EXCLUDED (by -x)" if (is_project_all_excluded($proj_name));
    my $rhash = ${$rp}{'PROJECT_AM_RHASH'}; # get AM file PROJECT hash
    my $amfil = ${$rhash}{':_AM_FILE_SRC_:'};
    my ($n,$d) = fileparse($amfil);
    my $dir = sub_root_dir($d);
    $dir =~ s/\\$//;    # remove TAIL
    my $msg = "ok";
    # $rex = ${$rparams}{'AM_EXCLUDED_DIRS'}; # store the EXCLUDED (if any!)
    if (defined ${$rex}{$dir}) {
        $msg = "EXCLUDED (by dir)";
    }
    #prt("Not all excluded $proj_name...\n") if ($proj_name =~ /libpangocairo/);
    if ( defined ${$rparams}{'REF_JOINED_EXCLUDED'} ) {
        my $rxj = ${$rparams}{'REF_JOINED_EXCLUDED'};
        if ( defined ${$rxj}{$proj_name} ) {
            if ( ${$rxj}{$proj_name} == 1 ) {
                $msg = "EXCLUDED (by -j)";
                #prt("Returning $msg\n") if ($proj_name =~ /libpangocairo/);
            #} else {
            #    prt("WHY VALUE ".${$rxj}{$proj_name}."?\n") if ($proj_name =~ /libpangocairo/);
            }
        #} else {
        #    my @arr = keys(%{$rxj});
        #    prt("WHY NOT DEFINED??? keys [".join(', ',@arr)."]\n") if ($proj_name =~ /libpangocairo/);
        }
    }
    return $msg;
}

sub get_excluded_by_joined($) {
    my ($rparams) = @_;
    my $rph = ${$rparams}{'REF_PROJECTS_HASH'};
    my $pcnt = scalar keys(%{$rph});
    my $rjp = ${$rparams}{'REF_JOINED_PROJS'}; # = \%joined_projects;
    my ($proj_name,$bad,$self,@arr,$cnt,$type,$list,$proj_type,$srcs,$fnd1,$key);
    my ($rp1,$prj,$fnd2,$rp,$ky2,$val,$msg);
    my $fndlist = '';
    my %hexj = ();
    $list = '';
    my $hr = \%hexj;
    $cnt = scalar keys(%{$rjp});
    prt("[v9] get_excluded_by_joined: Got $cnt keys in rjp, $pcnt in rph...\n") if (VERB9());
    return $hr if ($cnt == 0);
    foreach $proj_name (keys %{$rjp}) {
        $bad = 0;
        $self = 0;
        $val = ${$rjp}{$proj_name};
        # prt("Get joined, thus excluded for [$proj_name], [$val]...\n");
        @arr = space_split($val);
        $cnt = scalar @arr;
        if ($cnt != 2) {
            prtw("ERROR: INTERNAL: Joining [$proj_name] of [$val] did not split correctly! ($cnt)\n");
            next;
        }
        $type = $arr[0];
        $list = $arr[1];
        @arr = split(/:/,$type);
        $cnt = scalar @arr;
        if (($cnt < 1)||($cnt > 2)) {
            prtw("ERROR: INTERNAL: Joining [$proj_name] of [$type] did not split correctly! ($cnt)\n");
            next;
        }
        $proj_type = $arr[0];
        $srcs = ($cnt == 2) ? $arr[1] : ''; # potential semicolon sep list, or nothing
        if ( !get_app_type_4_short($proj_type, \$msg) ) {
            prtw("ERROR: INTERNAL: Joining [$proj_name] of [$proj_type] did resolve!\n");
            next;
        }
        $fnd1 = 0;
        foreach $key (keys %{$rph}) {
            $rp1 = ${$rph}{$key};
            $prj = ${$rp1}{'PROJECT_NAME'};
            if ($prj eq $proj_name) {
                $fnd1 = 1;
                last;
            }
        }
        @arr = split(/;/,$list);
        my @parr = ();
        # prt("Scan rph for [$list] ".scalar @arr." ...($fnd1)\n");
        foreach $ky2 (@arr) {
            $fnd2 = 0;
            foreach $key (keys %{$rph}) {
                $rp = ${$rph}{$key};
                $prj = ${$rp}{'PROJECT_NAME'};
                if ($ky2 eq $prj) {
                    $fnd2 = 1;
                    if ($prj eq $proj_name) {
                        $self++;    # already have this project hash
                    } else {
                        push(@parr,$rp);    # keep this for later
                    }
                    last;
                }
            }
            if (!$fnd2) {
                prtw("ERROR: WARNING: Joining [$proj_name], NOT FOUND [$ky2] in rph!\n");
                $bad = 1;
                last;
            }
        }
        if ( $self && (! $fnd1 || ($self != 1)) ) {
            prtw("ERROR: INTERNAL: Joining [$proj_name] of [$proj_type] found and not found!!! ($self vs $fnd1)\n");
            $bad = 1;
        }
        if (!@parr) {
            prtw("ERROR: INTERNAL: Joining [$proj_name] of [$proj_type] not found join projects!!!\n");
            $bad = 1;
        }
        next if ($bad);

        ${$hr}{$proj_name} = 2;
        $fndlist .= ' ' if (length($fndlist));
        $fndlist .= $proj_name;
        foreach $rp (@parr) {
            $prj = ${$rp}{'PROJECT_NAME'};
            ${$hr}{$prj} = 1;
            $fndlist .= ";$prj";
        }
    }
    $msg = "";
    $cnt = 0;
    foreach $key (keys %{$hr}) {
        $val = ${$hr}{$key};
        $msg .= ", " if (length($msg));
        $msg .= "$key:$val";
        $cnt++;
    }
    prt("[v9] Excluded by JOIN: $fndlist. k:v [$msg]\n") if (length($fndlist) && VERB9());
    return $hr;  # hash - keys is list of EXCLUDED by join if value = 1, joined name if 2
}

# got destination project name - $proj,$type,$srcs or none,$list
# $joined_projects{$proj} = "$type:$srcs $list";
sub process_joined_projects($) {
    my ($rparams) = @_;
    my $auto_on = ${$rparams}{'CURR_AUTO_ON_FLAG'};
    my $rjp = ${$rparams}{'REF_JOINED_PROJS'}; # = \%joined_projects;
    my $rph = ${$rparams}{'REF_PROJECTS_HASH'};
    my $rex = ${$rparams}{'AM_EXCLUDED_DIRS'}; # store the EXCLUDED (if any!)
    my $rlib_dupes = ${$rparams}{'REF_LIB_DUPES'}; # = \%lib_dupes;
    my ($prj,$proj_name,$rp1,$key,$val,$fnd1,$bad,$cnt,$type,$list);
    my ($srcs,$msg,$rp,$ky2,@arr,$fnd2,@parr,$self);
    my ($val2,$isexcl,$rhash,$src,$rinp_srcs,$rsrc);
    my ($proj_type,$tmp);
    $bad = 0;   # issue a WARNING - FAILED IN JOIN
    foreach $proj_name (keys %{$rjp}) {
        $bad = 0;
        $self = 0;
        $val = ${$rjp}{$proj_name};
        prt("\nJoining [$proj_name], [$val]... (dw=$project_dbg_write)\n");
        @arr = space_split($val);
        $cnt = scalar @arr;
        if ($cnt != 2) {
            prtw("ERROR: INTERNAL: Joining [$proj_name] of [$val] did not split correctly! ($cnt)\n");
            next;
        }
        $type = $arr[0];
        $list = $arr[1];
        @arr = split(/:/,$type);
        $cnt = scalar @arr;
        if (($cnt < 1)||($cnt > 2)) {
            prtw("ERROR: INTERNAL: Joining [$proj_name] of [$type] did not split correctly! ($cnt)\n");
            next;
        }
        $proj_type = $arr[0];
        $srcs = ($cnt == 2) ? $arr[1] : ''; # potential semicolon sep list, or nothing
        if ( !get_app_type_4_short($proj_type, \$msg) ) {
            prtw("ERROR: INTERNAL: Joining [$proj_name] of [$proj_type] did resolve!\n");
            next;
        }
        $fnd1 = 0;
        foreach $key (keys %{$rph}) {
            $rp1 = ${$rph}{$key};
            $prj = ${$rp1}{'PROJECT_NAME'};
            if ($prj eq $proj_name) {
                $fnd1 = 1;
                last;
            }
        }
        @arr = split(/;/,$list);
        @parr = ();
        foreach $ky2 (@arr) {
            $fnd2 = 0;
            foreach $key (keys %{$rph}) {
                $rp = ${$rph}{$key};
                $prj = ${$rp}{'PROJECT_NAME'};
                if ($ky2 eq $prj) {
                    $fnd2 = 1;
                    if ($prj eq $proj_name) {
                        $self++;    # already have this project hash
                    } else {
                        push(@parr,$rp);    # keep this for later
                    }
                    last;
                }
            }
            if (!$fnd2) {
                prtw("ERROR: WARNING: Joining [$proj_name], finding [$ky2] NOT FOUND!\n");
                $bad = 1;
                last;
            }
        }
        if ( $self && (! $fnd1 || ($self != 1)) ) {
            prtw("ERROR: INTERNAL: Joining [$proj_name] of [$proj_type] found and not found!!! ($self vs $fnd1)\n");
            $bad = 1;
        }
        if (!@parr) {
            prtw("ERROR: INTERNAL: Joining [$proj_name] of [$proj_type] not found join projects!!!\n");
            $bad = 1;
        }
        next if ($bad);
        if (!$fnd1) {
            if (defined ${$rph}{$proj_name}) {
                $rp1 = ${$rph}{$proj_name};
                $prj = ${$rp1}{'PROJECT_NAME'};
                ${$rp1}{'PROJECT_NAME'} = $proj_name;
                prtw("WARNING: Join [$proj_name], CONFLICT with existing [$prj] already in HASH\n".
                    "may or may not be a PROBLEM?\n");
            } else {
                # Must setup a NEW project reference, to pass inspection, like
                # my @arr = qw(PROJECT_NAME PROJECT_TYPE PROJECT_SOURCES PROJECT_TARGET PROJECT_DBG_WRITE 
                # PROJECT_FIX_REL PROJECT_AM_RHASH PROJECT_USER_LIBS PROJECT_USER_OUTS );
                my %p = ();
                my %h = ();
                $rp1 = \%p;
                ${$rph}{$proj_name} = $rp1;
                $rp = $parr[0];
                ${$rp1}{'PROJECT_USER_LIBS'} = ${$rparams}{'CURR_USER_LIBS'};  # get_user_libs
                ${$rp1}{'PROJECT_USER_OUTS'} = ${$rparams}{'CURR_USER_OUTS'};  # normal fetch for 'out:puts'
                ${$rp1}{'PROJECT_SOURCES'} = [ ];
                ${$rp1}{'PROJECT_TARGET'} = ${$rp}{'PROJECT_TARGET'};
                ${$rp1}{'PROJECT_DBG_WRITE'} = ${$rp}{'PROJECT_DBG_WRITE'};
                ${$rp1}{'PROJECT_FIX_REL'} = ${$rp}{'PROJECT_FIX_REL'};
                ${$rp1}{'PROJECT_AM_RHASH'} = \%h;
                my $addsrcs = '';
                # GET -T project:type[:source_list]
                $tmp = get_user_proj_type($proj_name,$proj_type,\$addsrcs);  # get any additional user sources
                prtw("WARNING: -J type [$proj_type] NOT EQUAL -T type [$tmp]! Choosing -J type.\n")
                    if ($tmp ne $proj_type);
                if (length($addsrcs)) {
                    $srcs .= ';' if (length($srcs) && !($srcs =~ /;$/));
                    $srcs .= $addsrcs;
                    prt("[15] Added sources from -T option [$addsrcs]\n") if ($dbg_s15);
                }
                # need to 'fix' CURR_USER_OUTS...
                if ($auto_on & 2) { # = $auto_on_flag
                    set_new_user_outs($rp1,$proj_name,$proj_type);
                    $msg = "Bit: 2: key [$key], pn [$proj_name] [$proj_type] - fetch R:'";
                    $msg .= ${$rp1}{'PROJECT_USER_OUTS'}->(1,$proj_name);
                    $msg .= "' D:'";
                    $msg .= ${$rp1}{'PROJECT_USER_OUTS'}->(2,$proj_name);
                    $msg .= "'";
                    prt("[15] $msg\n") if ($dbg_s15);
                    if (($proj_type eq 'SL')||($proj_type eq 'DLL')) {
                        if (! defined ${$rlib_dupes}{$proj_name}) {
                            ${$rlib_dupes}{$proj_name} = 1;
                            my %h = ();
                            $h{$key} = $proj_name;
                            ${$rparams}{'CURR_LIB_SUBS'} = [] if (!defined ${$rparams}{'CURR_LIB_SUBS'});
                            $tmp = ${$rparams}{'CURR_LIB_SUBS'};
                            push(@{$tmp}, \%h);
                        }
                    }
                } else {
                    prt("[15] Bit: 2: auto output is OFF\n") if ($dbg_s15);
                }

            }
        }

        $rinp_srcs = ${$rp1}{'PROJECT_SOURCES'};
        foreach $src (@{$rinp_srcs}) {
            if (length($src)) {
                $srcs .= ';' if (length($srcs));
                $srcs .= $src;
            }
        }
        $rhash = ${$rp1}{'PROJECT_AM_RHASH'};
        ${$rhash}{':_AM_FILE_SRC_:'} = "User INPUT";

        ${$rp1}{'PROJECT_TYPE'} = $proj_type;
        ${$rp1}{'PROJECT_NAME'} = $proj_name;

        # phew - got project hash, for join name $proj, or not if NEW ($self set if same name
        # found each of the projects to be JOINED to $proj, in @parr...
        # NOW JOIN THEM
        $val2 = $dsp_out_dir.$PATH_SEP."temp.$proj_name.dsp";
        $msg = get_EXCLUDE_msg($rparams,$rp1,$rex);
        $isexcl = ($msg =~ /EXCLUDE/) ? 1 : 0;
        ${$rp1}{'PROJECT_TARGET'} = $target_dir;
        ${$rp1}{'PROJECT_DBG_WRITE'} = $project_dbg_write;
        ${$rp1}{'PROJECT_FIX_REL'} = 0;  # source array already FIXED, relative to $target_dir
        foreach $rp (@parr) {
            $rsrc = ${$rp}{'PROJECT_SOURCES'};
            $prj = ${$rp}{'PROJECT_NAME'};
            set_exclude_project("-j",$prj,0);   # exclude from DSW
            foreach $src (@{$rsrc}) {
                if (length($src)) {
                    $srcs .= ';' if (length($srcs));
                    $srcs .= $src;
                }
            }
            # should ALSO check PROJECT_USER_LIBS and maybe fix 
        }
        my @src_arr = ();
        push(@src_arr, split(/;/,$srcs));
        ${$rp1}{'PROJECT_SOURCES'} = \@src_arr;
        prt("Create JOINED DSP for project [$proj_name]...$msg\nCombined sources [$srcs]\n");

        create_proj_dsp($val2,$target_dir,$rparams,$rp1);

        prt("Done JOINED DSP for [$proj_name]\n");

        # prtw("WARNING: JOIN CODE YET TO BE FULLY CHECKED!\n");
    }
}

sub get_proj_libs_rel($) {
    my $rp = shift;
    pgm_exit(1,"ERROR: Project ref hash NOT VALID!\n") if (!defined ${$rp}{'PROJECT_NAME'} || !defined ${$rp}{'PROJECT_USER_LIBS'});
    my $pnm = ${$rp}{'PROJECT_NAME'};
    my $libs = ${$rp}{'PROJECT_USER_LIBS'}->(1,$pnm);  # def=${$rparams}{'CURR_USER_LIBS'}=\&get_user_libs, but may be altered! 1=rel else dbg
    return $libs; # return string like "/libpath:\"Debug\" fooD.lib ..."
}

sub get_proj_libs_dbg($) {
    my $rp = shift;
    pgm_exit(1,"ERROR: Project ref hash NOT VALID!\n") if (!defined ${$rp}{'PROJECT_NAME'} || !defined ${$rp}{'PROJECT_USER_LIBS'});
    my $pnm = ${$rp}{'PROJECT_NAME'};
    my $libs = ${$rp}{'PROJECT_USER_LIBS'}->(2,$pnm);  # def=${$rparams}{'CURR_USER_LIBS'}=\&get_user_libs, but may be altered! 1=rel else dbg
    return $libs; # return string like "/libpath:\"Debug\" fooD.lib ..."
}

sub get_proj_user_libs($$$) {
    my ($rp,$rll,$rdeps) = @_;
    my $pnm = ${$rp}{'PROJECT_NAME'};
    # get a list like # like $proj_libD .= " /libpath:\"Debug\" fooD.lib";
    my $tmp = ${$rp}{'PROJECT_USER_LIBS'}->(1,$pnm); # def=${$rparams}{'CURR_USER_LIBS'}=\&get_user_libs, but may be altered! 1=rel else dbg
    $tmp .= ' ' if (length($tmp));
    $tmp   .= ${$rp}{'PROJECT_USER_LIBS'}->(2,$pnm);
    prt("EXTRA:get_proj_user_libs: for [$pnm], got list [$tmp]\n");
    my @arr = space_split($tmp);
    my %liblist = ();
    my $retlibs = '';
    my ($itm);
    foreach $itm (@arr) {
        next if ($itm =~ /^\/libpath/);
        $itm =~ s/\.lib$//;
        $itm =~ s/D$//;
        if (defined ${$rll}{$itm}) {
            if (!defined ${$rdeps}{$itm}) {
                if (!defined $liblist{$itm}) {
                    $liblist{$itm} = 1;
                    $retlibs .= ' ' if (length($retlibs));
                    $retlibs .= $itm;
                }
            }
        }
    }
    return $retlibs;
}

# if ( is_project_all_excluded($nm2) )
sub is_dependency_excluded($$$$) {
    my ($rparams,$pnm,$isexcl,$nm2) = @_;
    # if the prog itself is being eXclude, then ADD ALL dependencies
    #prt("Checking add $nm2, to $pnm...\n") if ($nm2 =~ /libpangocairo/);
    return 0 if ($isexcl);
    # else, if the depedencey is ALL eXclude, then do NOT add this
    return 1 if (is_project_all_excluded($nm2));
    # else, check if dependency is now eXcluded, by virtue is joined to another
    #prt("Dep $nm2 not all excluded...\n") if ($nm2 =~ /libpangocairo/);
    if ( defined ${$rparams}{'REF_JOINED_EXCLUDED'} ) {
        my $rxj = ${$rparams}{'REF_JOINED_EXCLUDED'};
        if ( defined ${$rxj}{$nm2} ) {
            if ( ${$rxj}{$nm2} == 1 ) {
                #prt("Dep not added to $pnm...\n") if ($nm2 =~ /libpangocairo/);
                return 1;
            #} else {
            #    prt("Dep added to $pnm, due ".${$rxj}{$nm2}." value\n") if ($nm2 =~ /libpangocairo/);
            }
        #} else {
        #    prt("Dep $nm2 not DEFINED?...\n") if ($nm2 =~ /libpangocairo/);
        }
    }
    return 0;   # ok to ADD this dependency to MAIN DSW
}

# ================================================================
# Now add 'missed' headers
# ================================================================
sub write_project_DSP_DSW_files($) {
    my ($rparams) = @_;
    my $rph = ${$rparams}{'REF_PROJECTS_HASH'};
    my $cnt = scalar keys(%{$rph});
    my ($key,$rp,$val,$val2,$dsp,$msg,$n,$d);
    $key = 'PROJECT_NAME';  # should have been set by AC_INIT(...) MACRO
    my $proj_title = 'tempprojname';
    if (defined ${$rparams}{$key}) {
        $proj_title = ${$rparams}{$key};
    }
    my $auto_on = ${$rparams}{'CURR_AUTO_ON_FLAG'};
    my $rex = process_hoh_subs($rparams);   # get EXCLUDED sub-directories
    my ($proj_type,$xcnt,$pnm,$tmp,$nm2,@arr);
    prt("\nCreating DSP files for $cnt projects [$proj_title]...\n");
    $xcnt = 0;
    #my %lib_list = ();  # list of LIBRARIES written
    my $rlib_lists = ${$rparams}{'REF_LIB_LISTS'};  # make list of LIBRARIES written
    my $sl_cnt = 0;
    my $ca_cnt = 0;
    my $dll_cnt = 0;
    my $wa_cnt = 0;
    my $ot_cnt = 0;
    my ($tot_libs,$tot_apps);
    my $lab = '';
    my ($rdtxt,$isexcl,$lkey);
    # ***TBD*** this should be MOVED to lib_params
    ${$rparams}{'REF_JOINED_PROJS'} = \%joined_projects;
    ${$rparams}{'REF_JOINED_EXCLUDED'} = get_excluded_by_joined($rparams);
    $ot_cnt = scalar keys(%{$rph});
    prt("[v9] Scanning, and typing $ot_cnt keys in rph...\n") if (VERB9());
    foreach $key (keys %{$rph}) {
        $rp = ${$rph}{$key};
        $val = ${$rp}{'PROJECT_NAME'};
        $msg = get_EXCLUDE_msg($rparams,$rp,$rex);
        $isexcl = ($msg =~ /EXCLUDE/) ? 1 : 0;
        $val2 = '<FAILED>';
        $proj_type = ${$rp}{'PROJECT_TYPE'};
        get_app_type_4_short($proj_type, \$val2);
        $dsp = ${$rp}{'PROJECT_USER_OUTS'}->(1,"");
        prt("[v9] key [$key] name [$val] [$val2] [$dsp] $msg\n") if (VERB9());
        $xcnt++ if ($isexcl);
        #my $RLO_MSG = 0;
        #my $RLO_PRJ = 1;
        #my $RLO_VAL = 2;
        #my $RLO_NAM = 3;
        #my $RLO_EXC = 4;
        if ($proj_type eq 'SL') {
            #                        0     1    2  3     4
            ${$rlib_lists}{$val} = [ $msg, $rp, 0, $val, $isexcl ]; # keep library LIST written, and whether ok or EXCLUDED
            $sl_cnt++;
        } elsif ($proj_type eq 'CA') {
            $ca_cnt++;
        } elsif ($proj_type eq 'DLL') {
            ${$rlib_lists}{$val} = [ $msg, $rp, 0, $val, $isexcl ]; # keep library LIST written, and whether ok or EXCLUDED
            $dll_cnt++;
        } elsif ($proj_type eq 'WA') {
            $wa_cnt++;
        } else {
            prtw("WARNING: Binary [$val] NOT 'SL', 'CA', 'DLL', 'WA'! It is [$proj_type]?\n") if ($ot_cnt == 0);
            $ot_cnt++;
        }
    }
    $tot_libs = $sl_cnt + $dll_cnt;
    $tot_apps = $ca_cnt + $wa_cnt;
    if ($do_joined_first) {
        # create DSP for JOINED, if any
        # =============================
        process_joined_projects($rparams);
        # =============================
    }

    prt("Doing DSP for SL=$sl_cnt, DLL=$dll_cnt, CA=$ca_cnt, WA=$wa_cnt.\n");
    prt("NOTE: $xcnt projects are EXCLUDED from the DSW.\n") if ($xcnt);
    # create DSP for LIBRARIES
    foreach $key (keys %{$rph}) {
        $rp = ${$rph}{$key};
        $proj_type = ${$rp}{'PROJECT_TYPE'};
        next if ( !(($proj_type eq 'SL')||($proj_type eq 'DLL')) );
        $val = ${$rp}{'PROJECT_NAME'};
        $val2 = $dsp_out_dir.$PATH_SEP."temp.$val.dsp";
        $msg = get_EXCLUDE_msg($rparams,$rp,$rex);
        $isexcl = ($msg =~ /EXCLUDE/) ? 1 : 0;
        prt("\nCreate DSP for project [$val]...$msg\n") if ($project_dbg_write);
        ${$rp}{'PROJECT_TARGET'} = $target_dir;
        ${$rp}{'PROJECT_DBG_WRITE'} = $project_dbg_write;
        ${$rp}{'PROJECT_FIX_REL'} = 0;  # source array already FIXED, relative to $target_dir
        create_proj_dsp($val2,$target_dir,$rparams,$rp);
        $dsp = ${$rp}{'PROJECT_DSP'};
    }
    # create DSP for APPS
    foreach $key (keys %{$rph}) {
        $rp = ${$rph}{$key};
        $proj_type = ${$rp}{'PROJECT_TYPE'};
        next if (($proj_type eq 'SL')||($proj_type eq 'DLL'));
        $val = ${$rp}{'PROJECT_NAME'};
        $val2 = $dsp_out_dir.$PATH_SEP."temp.$val.dsp";
        $msg = get_EXCLUDE_msg($rparams,$rp,$rex);
        $isexcl = ($msg =~ /EXCLUDE/) ? 1 : 0;
        prt("\nCreate DSP for project [$val]...$msg\n") if ($project_dbg_write);
        ${$rp}{'PROJECT_TARGET'} = $target_dir;
        ${$rp}{'PROJECT_DBG_WRITE'} = $project_dbg_write;
        ${$rp}{'PROJECT_FIX_REL'} = 0;  # source array already FIXED, relative to $target_dir
        create_proj_dsp($val2,$target_dir,$rparams,$rp);
        $dsp = ${$rp}{'PROJECT_DSP'};
    }

    if (!$do_joined_first) {
        # create DSP for JOINED, if any
        # =============================
        process_joined_projects($rparams);
        # =============================
    }

    # done DSP, and joined DSP, now DSW
    $rph = ${$rparams}{'REF_PROJECTS_HASH'};
    my $dswreal = "$proj_title.dsw";
    my $dswfull = $target_dir.$dswreal;
    my $dswtemp = $dsp_out_dir.$PATH_SEP."temp.$dswreal";
    my $dswtexc = $dsp_out_dir.$PATH_SEP."temp.excluded.$dswreal";
    my $dswexcl = $target_dir."excluded.$dswreal";
    my $cbtemp = $temp_copy_bat; # = $dsp_out_dir."tempcopy.bat";

    $cnt = scalar keys(%{$rph});
    if ($cnt) {
        if ($only_dry_run) {
            prt("\nSummary of $cnt project DSP files that would be written, if NOT DRY RUN!\n");
        } elsif (VERB1()) {
            prt("\n[v1] Summary of $cnt project DSP files written...\n");
        }
    } else {
        $msg = "\nEEK! It looks like NO project DSP files written!\n";
        prt($msg);
		$final_batch_msg .= $msg;
    }

    # ===============================================
    # Ouput the DSW file
    my $dswtxt = get_dsw_head();
    my $dswtxt2 = get_dsw_head();

    my $cbtxt = "\@echo Copy of $cnt DSP, plus the DSW to target directory\n";
    $cbtxt .= "\@echo Target is [$target_dir]\n";
    $cbtxt .= "\@if EXIST $target_dir".". goto GOTTARG\n";
    $cbtxt .= "\@echo ERROR: Appears TARGET directory does NOT EXIST!\n";
    $cbtxt .= "\@goto END\n";
    $cbtxt .= ":GOTTARG\n";
    $cbtxt .= "\@if \"%1x\" == \"x\" goto DNCMD\n";
    $cbtxt .= "\@if \"%1x\" == \"NOPAUSEx\" goto DNCMD\n";
    $cbtxt .= "\@echo ERROR: Only input allowed is 'NOPAUSE'\n";
    $cbtxt .= "\@goto END\n";
    $cbtxt .= ":DNCMD\n";
    $cbtxt .= "\@if \"\%TEMPNP\%x\" == \"yesx\" (\n";
    $cbtxt .= "\@echo Due to TEMPNP, will unconditionally OVERWRITE existing files, if any.\n";
    $cbtxt .= ") else (\n";
    $cbtxt .= "\@echo Setting TEMPNP=yes in the environment will skip more pauses, if file exists.\n";
    $cbtxt .= ")\n";
    $cbtxt .= "\@if \"\%1x\" == \"NOPAUSEx\" goto DNP1\n";
    $cbtxt .= "\@echo *** CONTINUE? ***\n";
    $cbtxt .= "\@pause\n";
    $cbtxt .= ":DNP1\n";
    $cbtxt .= "\n";

    # need to add copying EXCLUDE, if any
    $cnt = 0;
    my %depends = ();
    my %dupe_in_dsw = ();
    # Do LIBRARIES first
    foreach $key (keys %{$rph}) {
        $rp = ${$rph}{$key};
        $proj_type = ${$rp}{'PROJECT_TYPE'};
        next if ( !(($proj_type eq 'SL')||($proj_type eq 'DLL')) );
        $pnm = ${$rp}{'PROJECT_NAME'};
        next if (defined $dupe_in_dsw{$pnm});
        $msg = get_EXCLUDE_msg($rparams,$rp,$rex);
        $isexcl = ($msg =~ /EXCLUDE/) ? 1 : 0;
        $dupe_in_dsw{$pnm} = "$key:$proj_type:$msg:$isexcl";
        $dsp = "$pnm.dsp";
        $val = ${$rp}{'PROJECT_DSP'};
        ($n,$d) = fileparse($val);
        $val2 = ${$rp}{'PROJECT_DSP_TMP'};
        $val = '"'.$val.'"' if ($val =~ /[\s\+]+/);
        $val2 = '"'.$val2.'"' if ($val2 =~ /[\s\+]+/);
        $cnt++;
        $lab = "DOCOPY$cnt";
        $cbtxt .= "\@if NOT EXIST $val goto $lab\n";
        $cbtxt .= "\@if \"\%TEMPNP\%x\" == \"yesx\" goto $lab\n";
        $cbtxt .= "\@echo Warning: File [$val] ALREADY EXISTS! *** OVERWRITE? ***\n";
        $cbtxt .= "\@pause\n";
        $cbtxt .= ":$lab\n";
        $cbtxt .= "\@echo Copying $n...\n";
        $cbtxt .= "\@copy $val2 $val >nul\n";
        # ====================================================================
        $rdtxt = \$dswtxt;  # default to MAIN DSW for $proj_title
        $rdtxt = \$dswtxt2 if ($isexcl); # if eXcluded, switch to excluded DSW
        # ====================================================================
        ${$rdtxt} .= get_proj_begin( $pnm, $dsp );
        %depends = ();
        # INSERT PROJECT DEPENDENCIES ***
        if (defined ${$rp}{'PROJECT_DEPENDS'}) {
            if ($auto_on & 16) {
                my $rpda = ${$rp}{'PROJECT_DEPENDS'};
                $msg .= ' with deps [';
                # CHECK if this is NOT EXCLUDED
                foreach $nm2 (@{$rpda}) {
                    $msg .= " $nm2";
                    if (defined ${$rlib_lists}{$nm2}) {
                        $tmp = ${$rlib_lists}{$nm2};
                        if (${$tmp}[$RLO_MSG] =~ /EXCLUDE/) {
                            prtw("WARNING: Dependent name [$nm2] is EXCLUDED!\n");
                        }
                        ${$tmp}[$RLO_VAL] = 1;
                        ${$rlib_lists}{$nm2} = $tmp;
                    } else {
                        prtw("WARNING: This name [$nm2] NOT in LIBRARY LIST!\n");
                    }
                    # now, joined, done just before this, can ADD to excluded, so...
                    # if ( is_project_all_excluded($nm2) )
                    if ( is_dependency_excluded($rparams,$pnm,$isexcl,$nm2) ) {
                        $msg .= " EXCLUDED";
                    } elsif (!defined $depends{$nm2}) {
                        ${$rdtxt} .= "    Begin Project Dependency\n";
                        ${$rdtxt} .= "    Project_Dep_Name $nm2\n";
                        ${$rdtxt} .= "    End Project Dependency\n";
                        $depends{$nm2} = 1;
                    }
                }
                $msg .= ']';
            } else {
                $msg .= " add depend NOT enabled (16)";
            }
        } else {
            # ***TBD*** FIX20101029 - But should ALWAYS add -L lib_name:[D|R|B] with NO specific PROJECT
            # if ($proj_type ne 'SL')
            $msg .= ' NO depends';
            if (($proj_type ne 'SL') && ($auto_on & 16) && ($auto_on & 2048)) {
                $tmp = get_proj_user_libs($rp,$rlib_lists,\%depends);
                if (length($tmp)) {
                    @arr = split(/ /,$tmp);
                    $tmp = scalar @arr;
                    $msg .= " Bit:2048 + $tmp";
                    foreach $nm2 (@arr) {
                        if (!defined $depends{$nm2}) {
                            if ( is_dependency_excluded($rparams,$pnm,$isexcl,$nm2) ) {
                                $msg .= " +$nm2:EXLCUDED";
                            } else {
                                ${$rdtxt} .= "    Begin Project Dependency\n";
                                ${$rdtxt} .= "    Project_Dep_Name $nm2\n";
                                ${$rdtxt} .= "    End Project Dependency\n";
                                $depends{$nm2} = 1;
                                $msg .= " +$nm2";
                            }
                        }
                    }
                }
            }
        }
        ${$rdtxt} .= get_proj_end();
        prt("Project [$key], for [".sub_root_dir(strip_quotes($val))."]\n") if (VERB1());
        prt("[v9] Written [$val2] $msg\n") if (VERB9());
    }

    # Do APPLICATIONS second
    my $dcnt = 0;
    foreach $key (keys %{$rph}) {
        $rp = ${$rph}{$key};
        $proj_type = ${$rp}{'PROJECT_TYPE'};
        next if (($proj_type eq 'SL')||($proj_type eq 'DLL'));
        $pnm = ${$rp}{'PROJECT_NAME'};
        next if (defined $dupe_in_dsw{$pnm});
        $msg = get_EXCLUDE_msg($rparams,$rp,$rex);
        $isexcl = ($msg =~ /EXCLUDE/) ? 1 : 0;
        $dupe_in_dsw{$pnm} = "$key:$proj_type:$msg:$isexcl";
        $dsp = "$pnm.dsp";
        $val = ${$rp}{'PROJECT_DSP'};
        ($n,$d) = fileparse($val);
        $val2 = ${$rp}{'PROJECT_DSP_TMP'};
        $val = '"'.$val.'"' if ($val =~ /[\s\+]+/);
        $val2 = '"'.$val2.'"' if ($val2 =~ /[\s\+]+/);
        $dcnt++;
        $cnt++;
        $lab = "DOCOPY$cnt";
        $cbtxt .= "\@if NOT EXIST $val goto $lab\n";
        $cbtxt .= "\@if \"\%TEMPNP\%x\" == \"yesx\" goto $lab\n";
        $cbtxt .= "\@echo Warning: File [$val] ALREADY EXISTS! *** OVERWRITE? ***\n";
        $cbtxt .= "\@pause\n";
        $cbtxt .= ":$lab\n";
        $cbtxt .= "\@echo Copying $n...\n";
        $cbtxt .= "\@copy $val2 $val >nul\n";
        # ====================================================================
        $rdtxt = \$dswtxt;  # default to MAIN DSW for $proj_title
        $rdtxt = \$dswtxt2 if ($isexcl); # if eXcluded, switch to excluded DSW
        # ====================================================================
        ${$rdtxt} .= get_proj_begin( $pnm, $dsp );
        # INSERT PROJECT DEPENDENCIES ***
        %depends = ();
        if (defined ${$rp}{'PROJECT_DEPENDS'}) {
            if ($auto_on & 16) {
                my $rpda = ${$rp}{'PROJECT_DEPENDS'};
                $msg .= ' with deps [';
                # CHECK if this is NOT EXCLUDED
                foreach $nm2 (@{$rpda}) {
                    $msg .= " $nm2";
                    if (defined ${$rlib_lists}{$nm2}) {
                        $tmp = ${$rlib_lists}{$nm2};
                        if (${$tmp}[$RLO_MSG] =~ /EXCLUDE/) {
                            prtw("WARNING: Dependent name [$nm2] is EXCLUDED!\n");
                        }
                        ${$tmp}[$RLO_VAL] = 1;
                        ${$rlib_lists}{$nm2} = $tmp;
                    } else {
                        prtw("WARNING: This name [$nm2] NOT in LIBRARY LIST!\n");
                    }
                    # now, joined, done just before this, can ADD to excluded, so...
                    # if ( is_project_all_excluded($nm2) )
                    if ( is_dependency_excluded($rparams,$pnm,$isexcl,$nm2) ) {
                        $msg .= " EXCLUDED";
                    } elsif (!defined $depends{$nm2}) {
                        ${$rdtxt} .= "    Begin Project Dependency\n";
                        ${$rdtxt} .= "    Project_Dep_Name $nm2\n";
                        ${$rdtxt} .= "    End Project Dependency\n";
                        $depends{$nm2} = 1;
                    }
                }
                $msg .= ']';
                if (($dcnt + 1) == $tot_apps) {
                    # this is the LAST application
                    if ($auto_on & 512) {
                        # check the LIBRARY DEPENDANCE
                        $dcnt = 0;
                        foreach $lkey (keys %{$rlib_lists}) {
                            $tmp = ${$rlib_lists}{$lkey};
                            if (${$tmp}[2] == 0) {
                                $msg = ${$tmp}[0];
                                if ($msg =~ /ok/) {
                                    $nm2 = ${$tmp}[$RLO_NAM];
                                    # now, joined, done just before this, can ADD to excluded, so...
                                    # if ( is_project_all_excluded($nm2) )
                                    if ( is_dependency_excluded($rparams,$pnm,$isexcl,$nm2) ) {
                                        $msg .= " EXCLUDED";
                                    } elsif (!defined $depends{$nm2}) {
                                        ${$rdtxt} .= "    Begin Project Dependency\n";
                                        ${$rdtxt} .= "    Project_Dep_Name $nm2\n";
                                        ${$rdtxt} .= "    End Project Dependency\n";
                                        $depends{$nm2} = 1;
                                    }
                                    ${$tmp}[$RLO_VAL] = 1;
                                }
                            }
                            ${$rlib_lists}{$lkey} = $tmp;
                        }
                    }
                }
            } else {
                $msg .= " add depend NOT enabled (16)";
            }
        } else {
            # ***TBD*** FIX20101029 - But should ALWAYS add -L lib_name:[D|R|B] with NO specific PROJECT
            # if ( is_project_all_excluded($nm2) ) {
            # if ($proj_type ne 'SL')
            $msg .= ' NO depends';
            if (($proj_type ne 'SL') && ($auto_on & 16) && ($auto_on & 2048)) {
                $tmp = get_proj_user_libs($rp,$rlib_lists,\%depends);
                if (length($tmp)) {
                    @arr = split(/ /,$tmp);
                    $tmp = scalar @arr;
                    $msg .= " Bit:2048 + $tmp";
                    foreach $nm2 (@arr) {
                        if (!defined $depends{$nm2}) {
                            if ( is_dependency_excluded($rparams,$pnm,$isexcl,$nm2) ) {
                                $msg .= " +$nm2:EXLCUDED";
                            } else {
                                ${$rdtxt} .= "    Begin Project Dependency\n";
                                ${$rdtxt} .= "    Project_Dep_Name $nm2\n";
                                ${$rdtxt} .= "    End Project Dependency\n";
                                $depends{$nm2} = 1;
                                $msg .= " $nm2";
                                if (defined ${$rlib_lists}{$nm2}) {
                                    $tmp = ${$rlib_lists}{$nm2};
                                    ${$tmp}[$RLO_VAL] = 1;
                                }
                            }
                        }
                    }
                }
            }
        }
        ${$rdtxt} .= get_proj_end();
        prt("Project [$key], for [".sub_root_dir(strip_quotes($val))."]\n written [$val2] $msg\n") if (VERB1());
    }

    if ($cnt) {
        # check the LIBRARY DEPENDANCE
        $dcnt = 0;
        foreach $lkey (keys %{$rlib_lists}) {
            $tmp = ${$rlib_lists}{$lkey};
            if (${$tmp}[$RLO_VAL] == 0) {
                $msg = ${$tmp}[$RLO_MSG];
                if ($msg =~ /ok/) {
                    prtw("WARNING: No dependence on library [$lkey] created!\n");
                    $dcnt++;
                } else {
                    prt("No dependence on library [$lkey] created, but $msg!\n");
                }
            }
        }
        if ($dcnt) {
            prtw("WARNING: Need to add a project to use the $dcnt library dependency! ***TBD**\n");
        }

        $dswtxt .= get_dsw_tail();
        $dswtxt2 .= get_dsw_tail();

        $cnt++;
        $lab = "DOCOPY$cnt";
        $val = $dswfull;
        ($n,$d) = fileparse($val);
        $val2 = $dswtemp;
        $val = '"'.$val.'"' if ($val =~ /[\s\+]+/);
        $val2 = '"'.$val2.'"' if ($val2 =~ /[\s\+]+/);
        prt("\nFor project [$proj_title] DSW [".sub_root_dir(strip_quotes($val))."]\n") if (VERB1());
        $cbtxt .= "\@if NOT EXIST $val goto $lab\n";
        $cbtxt .= "\@if \"\%TEMPNP\%x\" == \"yesx\" goto $lab\n";
        $cbtxt .= "\@echo Warning: File [$val] ALREADY EXISTS! *** OVERWRITE? ***\n";
        $cbtxt .= "\@pause\n";
        $cbtxt .= ":$lab\n";
        $cbtxt .= "\@echo Copying $n...\n";
        $cbtxt .= "\@copy $val2 $val >nul\n";

        $cnt++;
        $lab = "DOCOPY$cnt";
        $val = $dswexcl;
        $val2 = $dswtexc;
        ($n,$d) = fileparse($val);
        $val = '"'.$val.'"' if ($val =~ /[\s\+]+/);
        $val2 = '"'.$val2.'"' if ($val2 =~ /[\s\+]+/);
        prt("For project [$proj_title] DSW [".sub_root_dir(strip_quotes($val))."]\n") if (VERB1() && $xcnt);
        $cbtxt .= "\@if NOT EXIST $val goto $lab\n";
        $cbtxt .= "\@if \"\%TEMPNP\%x\" == \"yesx\" goto $lab\n";
        $cbtxt .= "\@echo Warning: File [$val] ALREADY EXISTS! *** OVERWRITE? ***\n";
        $cbtxt .= "\@pause\n";
        $cbtxt .= ":$lab\n";
        $cbtxt .= "\@echo Copying $n...\n";
        $cbtxt .= "\@copy $val2 $val >nul\n";

        # =====================================================================
        # finish OFF copy batch file
        $cbtxt .= "\@echo All $cnt copied, done...\n";
        $cbtxt .= "\@echo Change to [$target_dir], and run MSVC... Good luck ;=))\n";
        $cbtxt .= ":END\n";
        if ($only_dry_run) {
            prt(" would write [$dswtemp]...if NOT DRY RUN\n") if (VERB1());
        } elsif ($write_dsp_files) {
            # primary DSW file
            rename_2_old_bak_plus($dswtemp);
            write2file($dswtxt,$dswtemp);
            prt("[v9] Written [$dswtemp]...\n") if (VERB9());

            # EXCLUDED DSW file
            rename_2_old_bak_plus($dswtexc);
            write2file($dswtxt2,$dswtexc);
            prt("[v9] Written [$dswtexc]...\n") if (VERB9() && $xcnt);

            write2file($cbtxt,$cbtemp);
			$final_batch_msg .= "Written BATCH file [$cbtemp], to COPY the $cnt 'temp' DSW/DSP files\n to TARGET [$target_dir]... ";
            #prt("Written BATCH file [$cbtemp], to COPY the $cnt 'temp' DSW/DSP files\n to TARGET [$target_dir]... ");
            if (-d $target_dir) {
                $final_batch_msg .= "ok";
            } else {
                $final_batch_msg .= "WARNING: DOES NOT EXIST!";
            }
			prt("$final_batch_msg\n");
        }
    } else {
        $msg = "No project DSP files written, nor a DSW! Sorry...\n";
		prt($msg);
		$final_batch_msg .= $msg;
    }
    if ($dsp_files_skipped) {
        prt("Writing of $dsp_files_skipped dsp/dsw files is skipped. Use -dsp or -dsw to enabled write.\n");
    }
}

sub process_am_list($) {
    my ($rparams) = @_;
    my ($key,$val,$cnt,$ky2,$val2,$len,$min,$ff,$ok);
    my $inf = ${$rparams}{'CURR_FILE'};
    my $rh =  ${$rparams}{'CURR_HASH'};
    my $supp_make_in = ${$rparams}{'SUPP_MAKE_IN'};
    my ($in_name, $in_dir) = fileparse($inf);
    $key = 'A_MAKE_INPUT_LIST';
    if (defined ${$rh}{$key}) {
        $val = ${$rh}{$key};
        $cnt = scalar @{$val};
        prt("\nprocess_am_list: $key with $cnt items in array...");
        prt(" NOTE: NO ITEMS TO PROCESS!") if ($cnt == 0); # 25/12/2010 - really show NO 'am' files
        prt("\n");
        # try to get the ROOT AM FILE
        foreach $ky2 (@{$val}) {
            $ff = $in_dir.$ky2.".am";
            fix_rpath_per_os(\$ff);
            if (-f $ff) {
                if (!($ky2 =~ /(\\|\/)/)) {
                    ${$rparams}{'ROOT_AM_FILE'} = $ff;
                    last;
                }
            } elsif ($supp_make_in) {
                $ff = $in_dir.$ky2.".in";
                fix_rpath_per_os(\$ff);
                if (-f $ff) {
                    if (!($ky2 =~ /(\\|\/)/)) {
                        ${$rparams}{'ROOT_AM_FILE'} = $ff;
                        last;
                    }
                }
            }
        }
        foreach $ky2 (@{$val}) {
            $ff = $in_dir.$ky2.".am";
            fix_rpath_per_os(\$ff);
            if (-f $ff) {
                $ok = ".am ok";
                ${$rparams}{'AM_FILE'} = $ff;
            } elsif ($supp_make_in) {
                $ff = $in_dir.$ky2.".in";
                fix_rpath_per_os(\$ff);
                if (-f $ff) {
                    $ok = ".in ok";
                    ${$rparams}{'AM_FILE'} = $ff;
                } else {
                    $ok = "'am' or 'in' NOT FOUND [$ff]";
                }
            } else {
                $ok = "'am' NOT FOUND [$ff]";
            }
            if ($ok =~ /ok/) {
                process_an_am_file($rparams);
            } else {
                prtw("WARNING: Missing AM File: [$ky2] $ok\n");
            }
        }
    }
    if (defined ${$rparams}{'ROOT_AM_FILE'}) {
       ${$rparams}{'AM_FILE'} = ${$rparams}{'ROOT_AM_FILE'};
    }
}

sub get_perl_temp_dir() {
    if (! -d $perl_temp_dir) {
        mkdir $perl_temp_dir;
        if (! -d $perl_temp_dir) {
            pgm_exit(1,"ERROR: Unable to create directory [$perl_temp_dir]\nMaybe there is already a file of that name, or...\n");
        }
    }
}

sub do_sanity_check() {
    pgm_exit(1,"ERROR: Problem with lib_acscan DEBUG!\n Count ".ac_get_dbg_range()." versus dbg_lac_check=$dbg_lac_check\n")
        if (ac_get_dbg_range() != $dbg_lac_check);
    pgm_exit(1,"ERROR: Problem with lib_amscan DEBUG!\nCount ".am_get_dbg_range()." versus am_check_tot=$am_check_tot\n")
        if (am_get_dbg_range() != $am_check_tot );
    get_perl_temp_dir();
}

sub set_out_file_names($) {
    my $dir = shift;
    $dir .= $PATH_SEP if (!($dir =~ /(\\|\/)$/));
    $conf_file = $dir."temp.$pgmname.conf";
    $amlistfile = $dir."amlist.txt";
    $temp_copy_bat = $dir."tempcopy.bat";
    $miss_mac_file = $dir."missed_mscro_list.txt";
    $missing_list_file = $dir."missed_defined_list.txt";
    $missed_source_list = $dir."missed_source_list.txt";
    $missed_source_dsp = $dir."missed_source_dsp.txt";
    $missed_cond_list = $dir."missed_cond_list.txt";
}

sub module_intialisation($) {
    my ($fil) = @_;
    set_out_file_names($dsp_out_dir);
    if (-d "C:\\MDOS") {
        $temp_copy_bat = "C:\\MDOS\\tempcopy.bat";
    }
    #my $auto_on = ${$rparams}{'CURR_AUTO_ON_FLAG'};
    set_blank_header_group(0) if ( !($auto_on_flag & 64) );
    # my $proj_defs = '/D "_CRT_SECURE_NO_WARNINGS"';
    add_defined_item('HAVE_CONFIG_H') if ($auto_on_flag & 128);
    my $rparams = init_common_subs($fil); # note: sets ROOT_FOLDER - where a CMakeLists.txt could be written
    ${$rparams}{'CURR_MISSED_SOURCES'} = $missed_source_list;
    ${$rparams}{'CURR_MISSED_SRCDSP'} = $missed_source_dsp;
    return $rparams;
}

#########################################
### MAIN ###
# check the DEBUG stuff is ok
do_sanity_check();

parse_args(@ARGV);

my $rph = module_intialisation($in_file); # establish $root_folder, and init %g_common_subs

process_ac_file($rph);  # mainly to get substitutions, and get the Makefile AM file output list

# 13/05/2012 - MAKE SURE WE HAVE A MASTER PROJECT NAME
# ====================================================
if (length($project_name) == 0) { # = NO user name, ${$rparams}{'PROJECT_NAME_MASTER'} = $project_name;
    if (length(${$rph}{'PROJECT_NAME'})) {
        ${$rph}{'PROJECT_NAME_MASTER'} = ${$rph}{'PROJECT_NAME'}; # = $ac_prog; # overall PROJECT NAME
    } else {
        pgm_exit(1,"ERROR: No main project name! Use -n <name> to set one!\n");
    }
}
# ====================================================

process_am_list($rph);  # process EACH Makefile.AM individually

# 25/12/2010 - check for existance of the 'Makefile.am' to scan
#if (defined ${$rph}{'AM_FILE'}) { # my $in_fil = ${$rparams}{'AM_FILE'};
    am_list_to_arrays($rph);    # move the AM information into 'project' HASHES
#} else {
#    # 25/12/2010 - indication of FAILURE to find anything to scan...
#    pgm_exit(3,"ERROR: No 'Makefile' am/in/or ? to scan...");
#}

write_project_DSP_DSW_files($rph); # write the 'project' HASHES to DSP, and finally a DSW

write_project_cmake_files($rph);

am_out_dir_scan_info($rph);

if ($exit_value) {
    pgm_exit($exit_value,"Exit, with error $exit_value");
} else {
    pgm_exit($exit_value,"Normal exit(0)");
}

########################################
### COMMAND LINE PARSING ###


sub local_strip_both_quotes($) {
    my $txt = shift;
    if ($txt =~ /^'(.+)'$/) {
        return $1;
    }
    if ($txt =~ /^"(.+)"$/) {
        return $1;
    }
    return '' if ($txt eq '""');
    return '' if ($txt eq "''");
    #prt("Stripping [$txt] FAILED\n");
    return $txt;
}

sub load_input_file($$) {
    my ($arg,$file) = @_;
    if (open INF, "<$file") {
        my @lines = <INF>;
        close INF;
        my @carr = ();
        my ($line,@arr,$tmp,$i);
        my $lncnt = scalar @lines;
        for ($i = 0; $i < $lncnt; $i++) {
            $line = $lines[$i];
            $line = trim_all($line);
            next if (length($line) == 0);
            next if ($line =~ /^#/);
            while (($line =~ /\\$/)&&(($i+1) < $lncnt)) {
                $i++;
                $line =~ s/\\$//;
                $line .= trim_all($lines[$i]);
            }
            @arr = split(/\s/,$line);
            foreach $tmp (@arr) {
                $tmp = local_strip_both_quotes($tmp);
                push(@carr,$tmp);
            }
        }
        $in_input_file++;
        parse_args(@carr);
        $in_input_file--;
    } else {
        pgm_exit(1,"ERROR: Unable to 'open' file [$file]!\n")
    }
}

sub need_arg {
    my ($arg,@av) = @_;
    pgm_exit(1,"ERROR: [$arg] must have following argument!\n") if (!@av);
}

sub add_2_commands($) {
    my ($ra) = @_;  # = \@av
    my ($itm);
    foreach $itm (@{$ra}) {
        $curr_command .= ' ' if (length($curr_command));
        $curr_command .= $itm;
    }
}

sub show_auto_help() {
    my $file = $0;
    my ($line,$max,$tmp,$cnt,$tmp2);
    my $auto_on = $auto_on_flag;
    if (open INF, "<$file") {
        my @lines = <INF>;
        close INF;
        prt("Bit list, with some 'notes', indicating what each BIT does.\n");
        $cnt = 0;
        foreach $line (@lines) {
            chomp $line;
            if ($line =~ /\#Bit:/) {
                prt("$line\n");
                $cnt++;
            }
        }
        prt("ERROR: Not able to find auto on flag lines!\n") if ($cnt == 0);
    } else {
        prt("Unable to open file [$file], no auto help NOT available!\n");
    }
    $tmp = 1;
    $tmp2 = '';
    while ($tmp) {
        if ($auto_on & $tmp) {
            $tmp2 .= ' ' if (length($tmp2));
            $tmp2 .= "$tmp";
        }
        $tmp = $tmp << 1;
        last if ($tmp > $auto_max_bit);
    }

    prt("Current auto on = $auto_on. Bits: [$tmp2]\n");
    prt("The flag can be SET by '-a on' to enable all, and '-a off' to disable all.\n");
    prt("Or set to a specific value, '-a 64', '-a 255', or groups, '-a 2+4+8+64'.\n");
    prt("\n");
    prt("ALSO bits can be MODIFIED using the command...\n");
    prt(" --auto-mod [-]v (-am)= Add bit if positive, remove bit if negative.\n");
    prt("For example '-am -512' would remove this bit from the value.\n");
    prt("\n");
    pgm_exit(0,"End AUTO help ($cnt)\n");
}

sub set_auto_flag($$) {
    my ($arg,$sarg) = @_;
    my $ok = -1;
    my $num = 0;
    my $sa = substr($arg,1) if ($arg =~ /^-/);
    $sa = substr($sa,1) while ($sa =~ /^-/);
    my $caf = $auto_on_flag;
    if ($sarg =~ /^\d+\+\d+/) {
        my @arr = split(/\+/,$sarg);
        $num = 0;
        foreach (@arr) {
            $num += $_;
        }
        $sarg = $num;
    }

    if ($sarg =~ /^-(\d+)$/) {
        $num = $1;
        $num *= -1;
        $ok = 2;
    } elsif ($sarg =~ /^(\d+)$/) {
        $num = $1;
        if ($num > 0) {
            $ok = 1;
        } else {
            $ok = 0;
        }
    } else {
        if (($sa eq 'am')||($sa eq 'auto-mod')) {
            pgm_exit(1,"ERROR: Unknown command '$arg $sarg'! Can NOT be '-am' or '--auto-mod' with text 'on', 'off', or integer. (or -1 for all)\n");
        } else {
            if ($sarg =~ /^on$/i) {
                $ok = 1;
                $num = -1;
            } elsif ($sarg =~ /^off$/i) {
                $ok = 0;
                $num = 0;
            } elsif ($sarg =~ /^help$/i) {
                show_auto_help();
            } else {
                pgm_exit(1,"ERROR: Unknown command '$arg $sarg'! Can only be 'on', 'off', or integer. (or -1 for all)\n");
            }
        }
    }
    if (($sa eq 'am')||($sa eq 'auto-mod')) {
        # ONLY MODIFY THE CURRENT FLAG
        if ($ok == 2) {
            $auto_on_flag &= ~(-$num);
        } else {
            $auto_on_flag |= $num;
        }
        $ok = 'MOD';
    } else {
        if ($ok) {
            $ok = 'ON';
            $auto_on_flag = $num;
        } else {
            $auto_on_flag = $num;
            $ok = 'OFF';
        }
    }
    prt("Set auto on flag [$sa] $ok - From [$caf] to [$auto_on_flag] ");
    $ok = 1;
    my $msg = '';
    $num = 0;
    while($ok) {
        if ($auto_on_flag & $ok) {
            $msg .= "+" if (length($msg));
            $msg .= "$ok";
            $num += $ok;
        }
        $ok = $ok << 1;
        last if ($ok > $auto_max_bit);
    }
    prt("Value [$msg] ($num)\n");
}

# SET -L dependent libraries option
# like $proj_lib .= " /libpath:\"lib\";
# like $proj_libD .= " fooD.lib";
# like $proj_libR .= " foo.lib";
# sub in line ADD LINK32 kernel32.lib ... -NEW_LIBS- /nologo ...
#my $proj_libs = 'Winmm.lib ws2_32.lib';
#my $proj_libD = '';
#my $proj_libR = '';
# FIX20110327 - -L<set> Allow the 'set' to include MULTIPLE libraries, for multiple project
sub add_user_libs($$) {
    my ($arg,$libs) = @_;
    # FIX20101118 - Watch out for static.lib:C:\path\to\lib:D[:project]
    #my @arr = split(":",$libs);
    my @arr = colon_split($libs);
    my $cnt = scalar @arr;
    my ($lib,$path,$dest,$rtarg,$stg,$typ,$proj);
    if (VERB9()) {
        prt("[v9] add_user_libs: Got array of $cnt ");
        foreach $proj (@arr) {
            prt("[$proj]");
        }
        prt("\n");
    }
    if ($cnt == 3) {    # no project - set GLOBAL
        $lib = strip_quotes($arr[0]);
        $path = strip_quotes($arr[1]);
        $dest = $arr[2];
        if (($dest eq 'D')||($dest eq 'd')) {
            $rtarg = \$proj_libD;
            $typ = 'Debug';
        } elsif (($dest eq 'R')||($dest eq 'r')) {
            $rtarg = \$proj_libR;
            $typ = 'Release';
        } elsif (($dest eq 'B')||($dest eq 'b')) {
            $rtarg = \$proj_libs;
            $typ = 'Both';
        } else {
            pgm_exit(1,"ERROR: For '$arg $libs', the 3rd item MUST be 'D', 'R', or 'B', NOT '$dest'!\n");
        }
        $stg = '';
        @arr = split(/;+/,$lib); # FIX20110327
        if (VERB9()) {
            $cnt = scalar @arr;
            prt("[v9]3: Got library split $cnt ");
            foreach $lib (@arr) {
                $lib = strip_quotes(trim_ends($lib));
                $stg .= "[$lib] ";
            }
            prt("$stg\n");
        }
        $stg = '';
        foreach $lib (@arr) {
            $lib = strip_quotes(trim_ends($lib));
            if (length($lib)) {
                $stg .= ' ' if (length($stg));
                if ($lib =~ /\s/) {
                    $stg .= "\"$lib\"";
                } else {
                    $stg .= "$lib";
                }
            }
        }
        if (length($path)) {
            $stg .= " /libpath:\"$path\"";
        }
        if (length(${$rtarg})) {
            ${$rtarg} .= ' ';
        }
        ${$rtarg} .= $stg;
        prt("Added '$stg' to $typ program library dependence.\n");
        rationalise_lib_paths();
    } elsif ($cnt == 4) {
        # given a specific PROJECT to add this specific LIBRARY to...
        # FIX20110327 - allow MULTIPLE libraries
        #my %by_proj_libraries = ();
        #my %by_proj_libraryD = ();
        #my %by_proj_libraryR = ();
        $lib = strip_quotes($arr[0]);
        $path = strip_quotes($arr[1]);
        $dest = strip_quotes($arr[2]);
        $proj = strip_quotes($arr[3]);
        pgm_exit(1,"ERROR: For '$arg $libs', the 4th item MUST be a 'project' name! Not blank!\n")
            if ((length($proj) == 0) || ($proj =~ /^\s+$/));
        if ($dest eq 'D') {
            $rtarg = \%by_proj_libraryD;
            $typ = 'Debug';
        } elsif ($dest eq 'R') {
            $rtarg = \%by_proj_libraryR;
            $typ = 'Release';
        } elsif ($dest eq 'B') {
            $rtarg = \%by_proj_libraries;
            $typ = 'Both';
        } else {
            pgm_exit(1,"ERROR: For '$arg $libs', the 3rd item MUST be 'D', 'R', or 'B', NOT '$dest'!\n");
        }
        @arr = split(/;/,$lib); # FIX20110327
        $stg = '';
        if (VERB9()) {
            $cnt = scalar @arr;
            prt("[v9]4: Got library split $cnt ");
            foreach $lib (@arr) {
                $lib = strip_quotes(trim_ends($lib));
                $stg .= "[$lib] ";
            }
            prt("$stg\n");
        }
        $stg = '';
        foreach $lib (@arr) {
            $lib = strip_quotes(trim_ends($lib));
            if (length($lib)) {
                $stg .= ' ' if (length($stg));
                if ($lib =~ /\s/) {
                    $stg .= "\"$lib\"";
                } else {
                    $stg .= "$lib";
                }
            }
        }
        if (length($path)) {
            $stg .= " /libpath:\"$path\"";
        }
        @arr = split(/;+/,$proj); # FIX20110327
        foreach $proj (@arr) {
            $proj = strip_quotes(trim_ends($proj));
            if (length($proj)) {
                if (defined ${$rtarg}{$proj}) {
                    ${$rtarg}{$proj} .= ' ' if (length(${$rtarg}{$proj}));
                    ${$rtarg}{$proj} .= $stg;
                } else {
                    ${$rtarg}{$proj} = $stg;
                }
                prt("For project [$proj], added '$stg' to $typ library dependence\n");
                $stg = ${$rtarg}{$proj};
                prt("[v9] Got full [$stg]\n") if (VERB9());
            }
        }
    } else {
        pgm_exit(1,"ERROR: For '$arg $libs', the 'set' did NOT split into 3 or 4 items!\n".
            " The LIB <set> consists of <name:path:(D|R|B)[:project]>. The 'path' can be blank, absolute, or relative\n".
            "  to the 'target' directory of the program DSP. D=Debug, R=Release, B=Both. It will be\n".
            "  added to ALL if no 'project', else ONLY to that project.\n");
    }
}

sub set_cond_item($$) {
    my ($arg,$sarg) = @_;
    my @arr = split(":",$sarg);
    my $item = $arr[0];
    my $val = 'TRUE';
    my ($tmp);
    if (scalar @arr > 1) {
        $tmp = $arr[1];
        if ( ($tmp =~ /^\d+$/) && (($tmp == 1)||($tmp == 0)) ) {
            $val = 'FALSE' if ($tmp == 0);
        } else {
            pgm_exit(2,"ERROR: Argument can only be $item + :1 or :0, not [$tmp]\n");
        }
    }
    if (defined $g_user_condits{$item}) {
        prt("Resetting conditional [$item] to [$val]\n");
    } else {
        prt("Setting conditional [$item] to [$val]\n");
    }
    $g_user_condits{$item} = $val; # stored as ${$rparams}{'REF_DEF_CONDITIONS'}
}

# SET -T project:type[:source_list]
sub set_project_type($$) {
    my ($arg,$sarg) = @_;
    my @arr = split(/:/,$sarg);
    my $cnt = scalar @arr;
    my $bad = 0;
    my $msg = '';
    my ($proj,$type,$srcs,@ts);
    if (($cnt >= 2)&&($cnt <= 3)) {
        $proj = $arr[0];
        $type = $arr[1];
        $srcs = ($cnt == 3) ? $arr[2] : '';
        if ( get_app_type_4_short($type, \$msg) ) {
            $by_proj_types{$proj} = $type;
            if (length($srcs)) {
                $by_proj_types{$proj} .= ':'.$srcs;
                $msg .= " + srcs [$srcs]";
            }
        } else {
            $msg = "FAILED conversion on type [$type] to string! Only 'CA', 'WA', 'SL', 'DLL' allowed!";
            $bad = 1;
        }
    } else {
        $bad = 1;
        $msg = "Did not split to 2 (or 3) on ':' char! Must be 'name:type[:src_list]'";
    }
    if ($bad) {
        pgm_exit(2,"ERROR: Command [$arg $sarg] FAILED! $msg\n");
    } else {
        prt("Set project [$proj], to type [$type] $msg\n");
    }
}

sub add_joined_projects($$) {
    my ($arg,$sarg) = @_;
    my (@arr,$bad,$cnt,$proj,$list,$type,$msg,$srcs,$emsg);
    @arr = space_split($sarg);
    $bad = 1;
    $cnt = scalar @arr;
    $emsg = "Splitting [$sarg] per space to 2 ($cnt)";
    goto FAILED if ($cnt != 2);
    $proj = $arr[0];
    $list = $arr[1];
    @arr = split(/:/,$proj);
    $cnt = scalar @arr;
    $emsg = "Splitting [$proj] per colon to 2 or 3 ($cnt)";
    goto FAILED if (($cnt < 2)||($cnt > 3));
    $proj = $arr[0];
    $type = $arr[1];
    $srcs = ($cnt == 3) ? $arr[2] : '';
    $emsg = "Converting [$type] to string";
    goto FAILED if ( !get_app_type_4_short($type, \$msg) );
    # got destination project name - $proj,$type,$srcs or none,$list
    $joined_projects{$proj} = "$type:$srcs $list";
    @arr = split(/;/,$list);
    $cnt = scalar @arr;
    $msg = "Join [$list]($cnt), to project [$proj], type [$type] ($msg)";
    $msg .= ", plus srcs [$srcs]" if (length($srcs));
    $bad = 0;
FAILED:
    if($bad) {
        pgm_exit(2,"ERROR: Command [$arg $sarg] FAILED! $emsg\n Not of form '-j prj:typ[:srcs] prj1;prj2;...'\n");
    }
    prt("$msg\n");
}

sub set_rename_proj($$) {
    my ($arg,$sarg) = @_;
    my @arr = split(":",$sarg);
    if (scalar @arr != 2) {
        pgm_exit(2,"ERROR: Argument $arg $sarg did NOT split into 2 on ':'!\n");
    }
    my $old = $arr[0];
    my $new = $arr[1];
    if (length($old) && length($new) && ($old ne $new)) {
        $proj_renaming{$old} = $new;
        prt("Set to RENAME [$old] to [$new]\n");
    } else {
        pgm_exit(2,"ERROR: In argument [$arg $sarg]!\n");
    }
}

###my $inst_hdr_path = 'include';
###my $inst_lib_path = 'lib';
###my $inst_bin_path = 'bin';

sub set_cmake_options($) {
    my $sarg = shift;
    $write_cmake_files = 1;
    my @arr = split(":",$sarg);
    my $cnt = scalar @arr;
    my ($i,$opt,@arr2,$msg);
    $msg = '';
    for ($i = 1; $i < $cnt; $i++) {
        $opt = $arr[$i];
        $msg = "Option: " if (length($msg) == 0);
        if (($opt eq '?')||($opt =~ /help/i)) {
            show_cmake_help();
            pgm_exit(0,"Cmake HELP exit(0)\n");
        } elsif ($opt =~ /^it;.+/) {
            $opt =~ s/^it;//;
            $inst_hdr_path = $opt;
            $msg .= "Inst. hdrs to [$opt] ";
        } elsif ($opt =~ /^lt;.+/) {
            $opt =~ s/^lt;//;
            $inst_lib_path = $opt;
            $msg .= "Inst. libs to [$opt] ";
        } elsif ($opt =~ /^bt;.+/) {
            $opt =~ s/^bt;//;
            $inst_bin_path = $opt;
            $msg .= "Inst. bins to [$opt] ";
        } else {
            pgm_exit(1,"ERROR: Unknown cmake option [$opt] (1)\n");
        }
    }
    prt("Set to write CMake files $msg.\n");
}


sub parse_args {
    my (@av) = @_;
    my ($arg,$sarg,$tmp,$rngac,$rngam,@arr);
    add_2_commands(\@av);
    while (@av) {
        $arg = $av[0];
        if ($arg =~ /^-/) {
            $sarg = substr($arg,1);
            $sarg = substr($sarg,1) while ($sarg =~ /^-/);
            if (($sarg =~ /^h/i)||($sarg eq '?')) {
                give_help();
                $conf_string = "";
                pgm_exit(0,"Help exit(0)");
#            } elsif ($sarg =~ /^3/) {
#                need_arg(@av);
#                shift @av;
#                $sarg = $av[0];
#                $usr_3rdparty_dir = $sarg;
#                $conf_string .= "$arg $sarg\n";
#                if (! -d $usr_3rdparty_dir) {
#                    pgm_exit(1,"ERROR: 3rdparty directory given [$usr_3rdparty_dir] IS NOT VALID!\n");
#                }
            # OUTPUTS
            } elsif (($sarg =~ /^dsp/i)||($sarg =~ /^dsw/i)) {
                $write_dsp_files = 1;
                prt("Set to write DSP/DSW files.\n");
            } elsif ($sarg =~ /^cmake/i) {
                set_cmake_options($sarg);
            } elsif ($sarg =~ /^a/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                set_auto_flag($arg,$sarg);
                $conf_string .= "$arg $sarg\n";
            } elsif ($sarg =~ /^A/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                @arr = split(":",$sarg);
                $tmp = scalar @arr;
                if ($tmp == 2) {
                    add_srcs_to_proj($arr[0],$arr[1]);

                } else {
                    pgm_exit(1,"ERROR: [$sarg] did NOT split in 2 on ':'!\n".
                        "Option $arg must be followed by proj:srcs, where srcs can be\n".
                        "a semi-colon list of sources to add to the 'proj'\n");
                }
                $conf_string .= "$arg $sarg\n";
            } elsif ($sarg =~ /^c/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                set_cond_item($arg,$sarg);
                $conf_string .= "$arg $sarg\n";
            } elsif ($sarg =~ /^D/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                add_defined_item($sarg);
                $conf_string .= "$arg $sarg\n";
            } elsif ($sarg =~ /^d/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $conf_string .= "$arg $sarg\n";
                if ($sarg =~ /^(\d+)$/) {
                    $tmp = $1;
                    $rngac = ac_get_dbg_range();
                    $rngam = am_get_dbg_range();
                    if (($tmp > 200) && (($tmp - 200) <= $rngam)) {
                        if (am_set_dbg_var($tmp - 200)) {
                            prt("Set AM lib Debug $tmp ON!\n");
                        } else {
                            pgm_exit(1,"ERROR: FAILED to set AM lib Debug $tmp ON!\n");
                        }
                    } elsif (($tmp > 100) && (($tmp - 100) <= $rngac)) {
                        if (ac_set_dbg_var($tmp - 100)) {
                            prt("Set AC lib Debug $tmp ON!\n");
                        } else {
                            pgm_exit(1,"ERROR: FAILED to set AC lib Debug $tmp ON!\n");
                        }
                    } else {
                        $rngac += 100;
                        $rngam += 200;
                        pgm_exit(1,"ERROR: Invalid argument [$arg $sarg]! Is numerical, but not range 101 to $rngac, nor 202 to $rngam!\n");
                    }
                } elsif ($sarg =~ /^-(\d+)$/) {
                    $tmp = $1;
                    $rngac = ac_get_dbg_range();
                    $rngam = am_get_dbg_range();
                    if (($tmp > 200) && (($tmp - 200) <= $rngam)) {
                        if (am_clear_dbg_var($tmp - 200)) {
                            prt("Clear AM lib Debug $tmp OFF!\n");
                        } else {
                            pgm_exit(1,"ERROR: FAILED to clear AM lib Debug $tmp OFF!\n");
                        }
                    } elsif (($tmp > 100) && (($tmp - 100) <= $rngac)) {
                        if (ac_clear_dbg_var($tmp - 100)) {
                            prt("Clear AC lib Debug $tmp OFF!\n");
                        } else {
                            pgm_exit(1,"ERROR: FAILED to clear AC lib Debug $tmp OFF!\n");
                        }
                    } else {
                        $rngac += 100;
                        $rngam += 200;
                        pgm_exit(1,"ERROR: Invalid argument [$arg $sarg]! Is numerical, but not range 101 to $rngac, nor 202 to $rngam!\n");
                    }
                } else {
                    if ($sarg =~ /^help$/i) {
                        show_dbg_help();
                        $conf_string = "";
                        pgm_exit(0,"DEBUG Help exit(0)\n");
                    } elsif ($sarg =~ /^all$/i) {
                        prt("Setting ALL debug ON!\n");
                        set_all_dbg_on();
                    } elsif ($sarg =~ /^none$/i) {
                        prt("Setting ALL debug OFF!\n");
                        set_all_dbg_off();
                    } elsif ($sarg =~ /^dry-run$/i) {
                        prt("Setting DRY RUN ONLY!\n");
                        $only_dry_run = 1;
                    } elsif ($sarg =~ /^extra$/i) {
                        prt("Setting ALL debug ON, plus EXTRA!\n");
                        set_max_debug_on();
                    } else {
                        pgm_exit(1,"ERROR: Invalid argument [$arg $sarg]! Not numerical, nor 'all', 'none', or 'help' !\n");
                    }
                }
            } elsif ($sarg =~ /^I/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                add_include_item($sarg);
                $conf_string .= "$arg $sarg\n";
            } elsif ($sarg =~ /^j/i) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                need_arg(@av);
                shift @av;
                $sarg .= " $av[0]";
                add_joined_projects($arg,$sarg);
                $conf_string .= "$arg $sarg\n";
            } elsif ($sarg =~ /^l/) {
                if (($sarg =~ /^ll/)||($sarg =~ /^l2/)) {
                    $load_log = 2;
                } else {
                    $load_log = 1;
                }
                $conf_string .= "$arg\n";
            } elsif ($sarg =~ /^L/) {   # -LIB set = name:path:(R|D|B)
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                add_user_libs($arg,$sarg);
                $conf_string .= "$arg $sarg\n";
            } elsif ($sarg =~ /^M/) {
                # store a macro
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                need_arg(@av);
                shift @av;
                $tmp = $av[0];
                $g_user_subs{$sarg} = $tmp; # supplied by USER INPUT
                prt("Set MACRO [$sarg] = [$tmp] in common subs...\n");
                $tmp = '""' if ((length($tmp) == 0)||($tmp =~ /^\s+$/));
                $conf_string .= "$arg $sarg $tmp\n";
            } elsif ($sarg =~ /^m/) {
                # mv/rename a project old:new
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                set_rename_proj($arg,$sarg);
            } elsif ($sarg =~ /^n/i) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $project_name = $sarg;
                prt("Set default over-all project name to [$project_name]\n");
                $conf_string .= "$arg $sarg\n";
            } elsif ($sarg =~ /^o/i) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                fix_rpath_per_os(\$sarg);
                $dsp_out_dir = File::Spec->rel2abs($sarg);
                prt("Set DSP out directory to [$dsp_out_dir]\n");
                $conf_string .= "$arg $sarg\n";
                if (! -d $dsp_out_dir) {
                    pgm_exit(1,"ERROR: The OUTPUT directory given, $dsp_out_dir does NOT EXIST!\n".
                        "Give only an output directory that already EXISTS.\n");
                }
 #           } elsif ($sarg =~ /^p/) {
 #               if (-f $conf_file) {
 #                   prt("Loading previous commands from [$conf_file]\n");
 #                   load_input_file($arg,$conf_file);
 #                   $no_conf_write = 1;
 #               } else {
 #                   pgm_exit(1,"ERROR: No previous config file [$conf_file] to load!\n");
 #               }
            } elsif ($sarg =~ /^r/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                prt("Loading from response file [$sarg]...\n");
                load_input_file($arg,$sarg);
            } elsif ($sarg =~ /^R/) {
                # FIX20110306 - Set project RUNTIME - either 'T'=static, or 'D'=DLL (default)
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                if (($sarg eq 'T')||($sarg eq 'D')) {
                    $proj_rt = $sarg;
                    prt("Set RUNTIME to [$sarg]... ");
                    if ($proj_rt eq 'D') {
                        prt("i.e. /MD release, /MDd debug DLL libs\n");
                    } else {
                        prt("i.e. /MT release, /MTd debug static libs\n");
                    }
                } else {
                    pgm_exit(1,"ERROR: Runtime can ONLY be 'T', for static, or 'D' for DLL! Not [$sarg]!\n");
                }
            } elsif ($sarg =~ /^s/) {
                $supp_make_in = 1;
                prt("Added support for Makefile.in.\n");
                $conf_string .= "$arg\n";
            } elsif ($sarg =~ /^t/) {  # target directory for DSP file(s)
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $target_dir = File::Spec->rel2abs($sarg);
                $fix_relative_sources = 1;
                prt("Set to TARGET folder to [$target_dir].\n");
                $conf_string .= "$arg $sarg\n";
            } elsif ($sarg =~ /^T/) {  # set TYPE SL->DLL, or CA->WA for a project
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                set_project_type($arg,$sarg);
                $conf_string .= "$arg $sarg\n";
            } elsif ($sarg =~ /^v/i) {
                if ($sarg =~ /^v.*(\d+)$/) {
                    $verbosity = $1;
                } else {
                    while ($sarg =~ /^v/i) {
                        $verbosity++;
                        $sarg = substr($sarg,1);
                    }
                }
                prt("Set verbosity to [$verbosity]\n");
                $conf_string .= "$arg\n";
            } elsif ($sarg =~ /^w/i) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $project_dbg_write = $sarg;
                prt("Set write debug to $project_dbg_write\n");
                $conf_string .= "$arg $sarg\n";
            } elsif ($sarg =~ /^x/i) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                set_exclude_project($arg,$sarg,1);
                $conf_string .= "$arg $sarg\n";
            } else {
                pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n");
            }
        } else {
            fix_rpath_per_os(\$arg);
            $in_file = File::Spec->rel2abs($arg);
            if (-f $in_file) {
                $conf_string .= "$arg\n";
                prt("Set input to [$in_file]\n");
            } else {
                pgm_exit(1,"ERROR: Unable to locate file [$in_file]! Check name, location...\n");
            }
        }
        shift @av;
    }

    if ($in_input_file) {
        return;
    }

    if ($in_input_file == 0) {
        if ((length($in_file) ==  0) && $debug_on) {
            my @carr = ();
            push(@carr,"-d"); push(@carr,"extra");
            #push(@carr,"-d"); push(@carr,"dry-run");

            push(@carr,"$def_file");
            # for libxm2
            push(@carr,"-s"); # $supp_make_in = 1; # Support Makefile.in scanning
            push(@carr,"-I"); push(@carr,"..\\include");
            push(@carr,"-D"); push(@carr,"LIBXML_STATIC");
            push(@carr,"-m"); push(@carr,'THREAD_W32'); push(@carr,'Win32');
            push(@carr,"-d"); push(@carr,113);

            # for libgif
            #push(@carr,"-I"); push(@carr,"..\\lib");
            #push(@carr,"-I"); push(@carr,"..\\windows");

            # for sigc++
            # $g_user_subs{'SIGCXX_API_VERSION'} = 2; # supplied by USER INPUT

            # for pcre
            #push(@carr,"-s"); # $supp_make_in = 1; # Support Makefile.in scanning
            #push(@carr,"-D"); push(@carr,"PCRE_STATIC");

            #push(@carr,"-l\n"); # $load_log = 1;
            if ((length($target_dir) ==  0) && length($def_targ)) {
                push(@carr,"-t"); push(@carr,"$def_targ"); # $target_dir = $def_targ;
            }
            if (length($def_name) && (length($project_name) == 0)) {    # if $debug_on
                push(@carr,"-n"); push(@carr,$def_name);
            }
            $in_input_file++;
            parse_args(@carr);
            $in_input_file--;
        }

        if (length($in_file) ==  0) {
            pgm_exit(2,"ERROR: No 'configure.ac' input file found in command!\n");
        }
        if (! -f $in_file) {
            pgm_exit(2,"ERROR: Can NOT find file [$in_file]! Check name, location!\n");
        }
    }
    if (! -d $dsp_out_dir) {
        pgm_exit(2,"ERROR: Directory [$dsp_out_dir] does NOT exist!\n Maybe it needs to be corrected, or created...\n");
    }
    #wait_key();
    #if ($in_input_file == 0) {
    #    pgm_exit(1, "Check results...\n");
    #}
}

# =============================================================================================
# TO BE DONE - ***TBD*** Other items thought of, but not yet implemented
#
# - Add DEFINE differently for Debug and Release, GLOBALLY or per PROJECT
#
# - Adding headers - ok this is good, but most headers get attached to the LIBRARIES,
#   since they are processed FIRST. There should be a better scheme for this. But can not
#   immediately see how to handle it, yet.
#   And in one instance in Glib-1.24.2 I saw one header added twice - I though I had BLOCK this!
#   Need to check later projects, before/while adding headers...
#
#   AND, since I now know all the headers included by a file, the script SHOULD make sure
#   the additional include directories is updated to find this/these headers.
#
# - noinst_PROGRAMS, check_PROGRAMS versus bin_PROGRAMS
#   Maybe the DSP for no-install prograsm could be still written, BUT not included in
#   the main DSW file. Maybe a 2nd DSW file could be written for these 'check' and 'no-install'
#   programs. 2010-10-26 - Add a -x (eXclude) option, which maybe help with this.
#
# - There are times when combining some static libraries could be good, but care should be taken with
#   this, because this separation is important in most cases. Maybe
#   --join a:b:c d  (-j) = Join projects named 'a', 'b', and 'c', and make a project 'd'
#
# - config.h - When it is noted that the source uses a 'config.h', maybe generate a common file
#   in the target directory, and make sure it is added to the most important library/programs, if pos.
#
# - missing source - certain Makefile.am will have a $host switch to INCLUDE say MAC or Windows
#   sources in say a library, but these will not always get picked up and put in the library.
#   Conversely, sometimes say MAC items will get picked up when they should not. Need a way to
#   (a) Force the INCLUSION of certain source to a project, and
#   (b) Force the EXCLUSION of certain sources from a project.
#   These sources are presently only written to the $missed_source_list file,
#   but this seems now well handled, at least partially, by the new --cond item:[0|1] option
#
#   Be able to ADD use dependencies to the DSW file, especially for additional libraries that
#   may NOT be in a LDADD entries - like they are only for WIN32
#   Maybe --PROJ <dep) (-P) = Project dependence added to DSW file.
#
#   An idea to force ALL libraries to be compiled first would be to add a 'dummy' application,
#   and add dependence of each library to it... like...
#sub write_dummy_prog {
#    my $prog = <<EOF;
#// aaa_msvc_dummy.c - only to force library dependence
##include <stdlib.h>
#int main(int argc, char **argv) { return 0; }
#EOF
#    my $name = $target_dir.'aaa_msvc_dummy.c';
#    write2file($prog,$name);
#    return 1;
#}
#   Or MAYBE add this dependence to one of the test/example built exes
#
#   Scanning of m4 files - maybe re-enable this
#   from : http://stackoverflow.com/questions/1970926/whats-the-point-of-aclocal
#   However, the latest documentation from both sets of tools suggests that the 
#   entire aclocal/acinclude paradigm is now obsolete, in favor of a newer method 
#   of specifying a directory containing m4 macro files. The current recommendation 
#   is that you create a directory in your project directory called simply m4 
#   (acinclude seems more appropriate to this author), and add macros in the form 
#   of individual .m4 files to this directory. All files in this directory will be 
#   gathered into aclocal.m4 before Autoconf processes your configure.ac file. 
#   Ultimately, aclocal will be replaced by functionality in Autoconf itself
#   Mentions : http://www.lrde.epita.fr/~adl/autotools.html
#
#   2011-03-14 - Perhaps REMOVE the '_a', example PLIB Makefile.am for pui
#   lib_LIBRARIES = libplibpu.a
#   libplibpu_a_SOURCES = ...
#   This correctly generate a 'libplibpu_a' project, but maybe it could be just 'plibpu'
#   It is noted in exe project that inlcude this library it will just use -lplibpu to name this library
#
#   2011-03-14 The comparison of the full directory scan outputs 'files' NOT used anywhere,
#   but perhaps this could be reduced by any EXTRA_DIST = some.dsp files. The EXTRA_DIST does
#   mean the file is NOT used elsewhere thus should not show up as 'un-referenced'...
#
#   21/04/2011 - Add say an -A alias flag, somewhat like the -L option
#   -A alias:lib:path:[RDB] example
#   -A sgio:libsgio_a.lib:path:R, then when the item -
#   project_LDADD = -lsgio, then the library/path libsgio_a.lib and <path> are added for Release build
#   That is tell the script what to expect in a LDADD Makefile.am statement, and supply the
#   appropriate library, and path to add to the project.
#
# =============================================================================================

sub show_dbg_ranges() {
    my ($max,$tmp);
    $max = ac_get_dbg_range();
    prt(" --dbg <num>     (-d) = Set DEBUG flag of this value.\n");
    prt("                        For AC lib_acscan, numbers in range 101 to 1$max\n");
    $max = am_get_dbg_range();
    prt("                        For AM lib_amscan, numbers in range 201 to 2$max\n");
    $tmp = ac_get_dbg_stg();
    prt("   Presently $tmp are ON in lib_acscan.\n") if (length($tmp));
    $tmp = am_get_dbg_stg();
    prt("   Presently $tmp are ON in lib_amscan.\n") if (length($tmp));
    prt("                        Text setting are 'all', 'none', 'extra', and 'help'.\n");
}

sub show_dbg_help {
    my $file = $0;
    my ($line,$max,$tmp,$cnt,$tmp2);
    show_dbg_ranges();
    if (open INF, "<$file") {
        my @lines = <INF>;
        close INF;
        prt(" Detailed list, with some 'notes' indicating what each does.\n");
        $cnt = 0;
        my @ac_dbg = ();
        my @am_dbg = ();
        foreach $line (@lines) {
            $line = trim_all($line);
            if ($line =~ /^our\s+\$dbg_lac(\d+)\s*=\s*\d+\s*;\s*#(.+)$/) {
                $tmp = $1;
                $tmp2 = $2;
                push(@ac_dbg,"1$tmp: $tmp2");
                $cnt++;
            } elsif ($line =~ /^our\s+\$dbg_s(\d+)\s*=\s*\d+\s*;\s*#(.+)$/) {
                $tmp = $1;
                $tmp2 = $2;
                push(@am_dbg,"2$tmp: $tmp2");
                $cnt++;
            }
        }
        if ($cnt) {
            if (@ac_dbg) {
                $cnt = scalar @ac_dbg;
                prt("For DEBUG of AC file scan... $cnt\n");
                foreach $tmp (@ac_dbg) {
                    prt(" $tmp\n");
                }
            } else {
                prt("PROBLEM: NO lib_acscan ADDITIONAL debug HELP!\n");
            }
            if (@am_dbg) {
                $cnt = scalar @am_dbg;
                prt("For DEBUG of AM file scans... $cnt\n");
                foreach $tmp (@am_dbg) {
                    prt(" $tmp\n");
                }
            } else {
                prt("PROBLEM: NO lib_amscan ADDITIONAL debug HELP!\n");
            }
            prt("NOTE: Adding DEBUG output only serves to DEBUG what the script 'saw', and 'did'!\n");
        } else {
            prt("\nPROBLEM: Found no \$dbg?? vars in file [$file], so NO DEBUG ADDITIONAL HELP!\n");
        }
    } else {
        prt("ERROR: Unable to open file [$file], so NO ADDITIONAL DEBUG HELP!\n");
    }
}


sub give_help {
    my ($tmp);
    prt("$pgmname: $my_version\n");
    prt("Usage: $pgmname [options] in-file\n");
    prt(" --help    (-h or -?) = This help, and exit 0.\n");
    prt("Purpose:\n");
    prt(" Scan the input file as a configure.ac file, then scan the Makefile.am entries indicated by the\n");
    prt("  AC_OUTPUT macro, and output CMake and/or MSVC DSW/DSP build files, if enabled, with the contents found.\n");
    prt("NOTES:\n");
    prt(" Auto output does the following, and more: For libraries: D:'/out:\"lib\\barD.lib\"'; R:\n");
    prt("  '/out:\"lib\\barD.lib\"'. For programs: D:'/out:\"bin\\fooD.exe\"'; R:'/out:\"bin\\foo.exe\"'\n");
    prt("  Use '-a help' for fuller list of this 'auto' feature BIT effects.\n");
    prt(" Do NOT add the /D or /I to the macro defined or included. This will be added internally,\n");
    prt("  and note the upper case for -D, -I and -L.\n");
    prt(" Setting a macro is 3 entries, like '-m item value', thus if set a blank is required, use\n");
    prt("  '-m item \"\"'. Macros substitutions found missing are written to the file\n");
    prt("  [$miss_mac_file] for review, and then use\n");
    prt("  as a '-r <file>' response input file.\n");
    prt(" The debug switch is strictly for that. It adds no functionality, just a noisier output,\n");
    prt("  and thus allow a DEBUG view of what the script 'saw', and 'did' to potentially\n");
    prt("  'fix' the script.\n");
    prt(" Unless there are two commands using the same letter, normally case, and after the first\n");
    prt("  letter are ignored.\n");
    prt(" The LIB <set> consists of <name:path:(D|R|B)[:project]>. The 'name' can be a single name, or\n");
    prt("  a semicolon separated list of library names. The 'path' can be blank, absolute, or\n");
    prt("  relative to the 'target' directory of the program DSP. D=Debug, R=Release, B=Both. If no optional\n");
    prt("  project name is given, then the library (or libs) are added to all, except Static Libraries (SL).\n");
    prt("  RESTRICTION - libraries should be added WITHOUT a PATH. The 'path' should only be given as 2nd value.\n");
    prt("Options:\n");
#    prt(" --3rdparty <dir> (-3) = Set a 3rdpartty included and lib directory for CMake find_library()\n");
    prt(" --ADD proj:srcs  (-A) = Add source[;src2[;...]] to a project, perhaps NOT found in present Makefile.am scan.\n");
    prt(" --auto on|off|v  (-a) = Automate certain preferred outputs. '-a help' for MORE. (def=$auto_on_flag)\n");
    prt(" --cond item      (-c) = Set a CONDITIONAL item. Can add [:1|0]. Default is 1 (TRUE).\n");
    show_dbg_ranges();
    prt(" --DEF mac[:nam]  (-D) = Add a global defined macro, OR to a specific ':project', if given.\n");
    prt(" --INC <path>     (-I) = Add an include macro, like '/I \"path\"', to the DSP compile.\n");
    prt("                        Add :project to add an include on a per project basis.\n");
    prt(" --join nam list (-j) = Join the semicolon separated 'list', to project of type, like 'nam:type'.\n");
    prt("                        Can be 'a:t a;b;c', or 'd:t a;b;c'. Type is per -T below, and\n");
    prt("                        can have semicolon separated sources following another colon.\n");
    prt(" --LIB <set>     (-L) = Add a global dependant libraries, to programs being built,\n");
    prt("                        OR to just to a specific project, if given. See NOTES:\n");
    prt(" --load-log      (-l) = Load log file at end. (def=".($load_log ? "On" : "Off").")\n");
    prt(" --MAC item val  (-M) = Store a MACRO, item=value, for substitution. (use '-d 13' to list missing).\n");
    prt(" --mv old:new    (-m) = Move/rename a project from am name old to new.\n");
    prt(" --name <name>   (-n) = Overrride any name in the AC_INIT/AM_INIT_AUTOMAKE.\n");
    prt(" --out <dir>     (-o) = Give existing DSP output directory, relative or absolute.\n");
    prt("                        Will NOT be created. (def=$dsp_out_dir).\n");
#    prt(" --previous      (-p) = Load previous commands from [$conf_file]\n") if (-f $conf_file);
    prt(" --resp <file>   (-r) = Commands from a reponse/input file.\n");
    prt(" --RT  [D|T]     (-R) = Set global RUNTIME. D=/MD|/MDd T=/MT|/MTd (def=$proj_rt).\n"); # FIX20110306 - Set project RUNTIME - either 'T'=static, or 'D'=DLL (default)
    prt(" --supp_in       (-s) = Support Makefile.in, if NO Makefile.am found. Does not always work! (def=$supp_make_in)\n");
    prt(" --TYPE nm:type  (-T) = Set other than default types for project name : types CA|WA|SL|DLL, and\n");
    prt("                        the type can be followed with additional 'sources'. name:type:src1;src2;...\n");
    prt(" --targ <dir>    (-t) = Establish a target directory for the DSW/DSP files, if enabled.\n");
    prt(" --verbosity     (-v) = Bump verbosity of this module. Default [$verbosity] to 9 (-vvvvvvvvv)\n");
    prt(" --wr_dbg <num>  (-w) = Add debug during the DSP write output. Bit values. -1 for all.\n");
    prt(" --xclude <set>  (-x) = Exclude a 'project', or sources, from a project. A 'set' consists of\n");
    prt("                        a project name, optionally followed, after a colon,\n");
    prt("                        by a semi-colon separated list of sources. eg proj:src1;src2;...' to exclude\n");
    prt("Output: Both default to OFF.\n");
    prt(" -dsp or -dsw         = Output MSVC build files. Initially written to -out <dir>, with a batch file to copy to -targ <dir>.\n");
    prt(" -cmake[:op[:op...]]  = Output CMake CMakeLists.txt file, to -o <dir>, with bat to copy to source.\n");
    show_cmake_help();
}

sub show_cmake_help {
    prt(" Can be followed by a list of colon spearated special cmake options.\n");
    prt(" it;targ   = Set special 'install' headers option, like it;include/plib for PLIB.\n");
    prt(" lt;targ   = Set special 'install' libraries option, like lt;sbin for a system utility.\n");
    prt(" bt;targ   = Set special 'install' binary option, perhaps like bt;bin/tg.\n");
    prt(" Other otions to follow as they ar identified.\n");
   
}


# eof - amsrcs04.pl
