#!/usr/bin/perl -w
# #######################################################################################
# 06/04/2012 - Try to avoid uninitialised $vcconf variable
# 20100128 - add_copy_bat: Due to xmlrpc having ++ in name, cloak each in inverted commas
# 28/10/2011 - Fix like tempcopydsp.bat to COPY the DSW
# 14/10/2010 - Really should add BLANK header groups, even when NO source, or headers. ie default to 1
# 10/09/2010 - changed name to lib_dsphdrs.pl
# module: fgdsphdrs03.pl - Version 3 - see fgdsphdrs.pl and 01 for versions 1 and 2
# header information, when building DSP files
# 23/04/2011 - # FIX20110423 - Unrecognized escape \l passed through in regex
# 17/03/2011 - # FIX20110317 - copy $conf to $tconf, and eliminate the project NAME
# 10/01/2011 - Well, if NO GROUP, add SOURCE within on Begin Source ... End Source block, at end...
# 31/05/2010 - do not add a GROUP if the group string is null
# 2010/05/05 - assume RELEASE when a DLL type
# 2010/05/01 - added 'strict' and 'warnings', and did some fixes
# 25/04/2010 - fix write_proj_DSW3, when outputing the 'copydsp' bat file, but still NOT correct is
# -dsp=<out_dir> is given, and -fix_rel, since the dsw will have the old location, and likewise 'copydsp' ***TBD***
# 2010/04/23 - get_proj_begin() - remove '.\' in front of file, and added cwd() if output dsw is '.\'
# 2010/03/08 - No warning when say ReadMe.txt is added 'outside' source groups.
# 2010/03/01 - if vcproj contains IgnoreDefaultLibraryNames="MSVCRT", add /nodefaultlib:"MSVCRT" to DSP
#   Add this to the -NEW_OUT- substitution parameter - that is to the # ADD LINK32 line.
#   Can be DIFFERENT per configuration.
# ALSO removed odbc32.lib and odbccp32.lib from the default dsp LINK32
# 2009/10/18 - incompatible with PREVIOUS versions. Begin separation of CONFIGURATION
#   so MULTIPLE configuration can be supported
#
# 20090819 - start to split header generation so additional options can be done
#
# 20090816 - Added ($dbg & 2) output of 'C_SOURCES' during check for duplicates
# NOTE: The $hash{'C_SOURCES'} = [ @c_sources ] is of the form push(@c_sources, [ $file, $group, $filter ]); Similar for 'H_SOURCES'...
# The 'default' group and filter are available through -
# my $sgrp = get_def_src_grp();    # "Source Files"; my $sflt = get_def_src_filt();
# my $hgrp = get_def_hdr_grp();    # "Header Files"; my $hflt = get_def_hdr_filt();
# The DEFAULT hash, sub get_default_sub { } returns a HASH with all the '-NEW_*' items set.
# To this MUST be added -
# $hash{'APP_TYPE'} = $app_statlib_stg, or one of the other strings setting APPLICATION TYPE
# and of course the above $hash{'C_SOURCES'} and 'H_SOURCES' before using sub write_hash_to_DSP2 {...}
#
# 10/11/2008 - Added /GR to compiler for each. Without this MSVC7 defaults to /GR-, which
# disables Runt-Time Type Info, which causes a BIG problem with OSG
#
# APP_TYPE
# $app_console_stg  = 'Console Application'
# $app_windows_stg  = 'Application'
# $app_dynalib_stg  = 'Dynamic-Link Library'
# $app_statlib_stg  = 'Static Library'

# $app_utility_stg  = 'Utility'              = *TBD*
#
# substitution variables
# -NEW_PROJECT_NAME-    = name of the project   = MUST EXIST
# -NEW_OUTD-            = PROP Output_Dir ????
# -NEW_INTER-           = PROP Intermediate_Dir ????
# ADD CPP with 
# -NEW_RT-              = RUNTIME, like /MT /MD, /MTd, etc
# -NEW_INCS-            = INCLUDE DIRECTORIES, like /I ".."
# -NEW_DEFS-            = DEFINES, like /D "FGFS"
# ADD LINK32 (for console, app, DLL) with
# -NEW_LIBS-            = Additional libraries for the link
# -NEW_OUT-             = link output, like /out:"StaticRelease\libpng.lib
# -NEW_POST-            = POST build - description and commands, TAB separated
# ADD LIB32 (for static library) with
# -NEW_OUT-             =  OUTPUT static library
# 10/08/2010 - Note config can be divided into Debug, Release, thus
# it is possible to have say R=foo.lib D=food_d.lib ...
# #######################################################################################
use strict;
use warnings;

use Cwd;

# DEBUG ONLY - should be OFF
my $dbg_props = 0;
my $g_write_dbg = 0;    # given through write_hash_to_DSP3 or write_proj_DSW3
my $only_win32_conf = 1;    # limitation to ONLY handle WIN32 configs
my $add_blank_header_group = 1;
sub set_only_win32_conf($) { $only_win32_conf = shift; }

sub set_dbg_props { $dbg_props = 1; }
sub set_blank_header_group($) { $add_blank_header_group = shift }
sub get_fetch_function($$$$);

my $act_proj_dsphdrs = '';  # set on get

# like zlib = ConfigurationType="4", alut = ConfigurationType="2", fg = ConfigurationType="1"
#  // This is an internal type to Visual Studio, it seems that:
#  // 4 == static library
#  // 2 == dll
#  // 1 == executable
#  // 10 == utility
my $app_console_stg  = 'Console Application';
my $app_windows_stg  = 'Application';
my $app_dynalib_stg  = 'Dynamic-Link Library';
my $app_statlib_stg  = 'Static Library';
my $app_utility_stg  = 'Utility';

my @dsp_sub_set = qw(-NEW_RT- -NEW_DEFS- -NEW_LIBS- -NEW_POST- -NEW_INCS- -NEW_OUT- -NEW_OUTD- -NEW_INTER-);

# 2009/10/18 - return a REFERENCE to the NEW default
sub get_default_sub3($) {
    my ($conf) = @_;
    my %def_sub = (
    "-NEW_RT-"       => "/MT",
    "-NEW_DEFS-"     => "/D \"_CRT_SECURE_NO_WARNINGS\"",
    "-NEW_LIBS-"     => "Winmm.lib ws2_32.lib",
    "-NEW_POST-"     => "",
    "-NEW_INCS-"     => "",
    "-NEW_OUT-"      => "",
    "-NEW_OUTD-"     => "\"Release\"",
    "-NEW_INTER-"    => "\"Release\""
    );

    my $rds = \%def_sub;
    if ($conf == 1) {
        ${$rds}{"-NEW_RT-"}    = '/MTd';
        ${$rds}{"-NEW_OUTD-"}  = "\"Debug\"",
        ${$rds}{"-NEW_INTER-"} = '"Debug"';
    }
    return $rds;
}

sub is_valid_dsp_sub_sub($) {
    my $rds = shift;
    my $max = scalar @dsp_sub_set;
    my ($i,$itm);
    for ($i = 0; $i < $max; $i++) {
        $itm = $dsp_sub_set[$i];
        last if (!defined ${$rds}{$itm});
    }
    return 0 if ($i == $max);
    return 1;
}

sub get_default_sub_ref($) {
    my ($add) = @_;
    my %def_sub = (
    "APP_TYPE"           => "Static Library",
    "-NEW_PROJECT_NAME-" => "fgfs",
    "PROJECT_FLAGS"      => [ 0, 0 ]
    );

    if ($add) {
        $def_sub{'config-001-Debug'}   = get_default_sub3(1);
        $def_sub{'config-002-Release'} = get_default_sub3(0);
    }

    return \%def_sub;
}

my $def_source_filter = 'cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;for;f90';
my $def_header_filter = "h;hpp;hxx;hm;inl;fi;fd";
sub get_default_source_filter() { return $def_source_filter; }
sub get_default_header_filter() { return $def_header_filter; }

sub get_app_type_hash_ref_short() {
    my %short_2_app_type = (
        'CA' => $app_console_stg,
        'WA' => $app_windows_stg,
        'DLL' => $app_dynalib_stg,
        'SL' => $app_statlib_stg
        );
    return \%short_2_app_type;
}

sub get_app_type_4_short($$) {
    my ($st, $rs) = @_;
    my $ras = get_app_type_hash_ref_short();
    if (defined ${$ras}{$st}) {
        ${$rs} = ${$ras}{$st};
        return 1;
    }
    return 0;
}

# "Win32 (x86) Dynamic-Link Library" 0x0102
sub get_app_type_stg($) {
    my ($stg) = shift;
    if ($stg =~ /Static\s+Library/) {
        return $app_statlib_stg;
    } elsif ($stg =~ /Console\s+Application/) {
        return $app_console_stg;
    } elsif ($stg =~ /Dynamic-Link\s+Library/) {
        return $app_dynalib_stg;
    }
    return "Unresolved [$stg] FIXME in lib_dsphdrs.pl!!!";
}

sub strip_quotes_02($) {
	my ($ln) = shift;
	if ($ln =~ /^".*"$/) {
		$ln = substr($ln,1,length($ln)-2);
	}
	return $ln;
}

sub dos_2_unix_02($) {
	my ($du) = shift;
	$du =~ s/\\/\//g;
	return $du;
}


sub scan_DSP_lines {
	my ($df,$mhr,$rdlns) = @_;
	my $lncnt = scalar @{$rdlns};
	my @dspsrcs = ();
    my $projname = '';
    my $projtype = '';
    my $group = '';
    my $filter = '';
    my ($tmp, $key);
    my ($line); # 2010/05/01 
    my %dsp_hash = ();
    my $hr = \%dsp_hash;
    my @c_sources = ();
    my @h_sources = ();
    prt( "Scanning $lncnt lines, from [$df]...\n" );
	###prt( "File contains $lncnt lines ...\n" );
    # push(@c_sources,[$src, $group, $filter, 0]); # 'PROJECT_SRCS'
	foreach $line (@{$rdlns}) {
		chomp $line;
        if ($line =~ /^#\s+Microsoft\s+Developer\s+Studio\s+.+Name="(\w+)".+$/) {
            $projname = $1;
            prt( "Project Name [$projname]\n" );
            # -NEW_PROJECT_NAME-"   = name of the project
            $key = '-NEW_PROJECT_NAME-';
            ${$hr}{$key} = $projname;
        } elsif ($line =~ /^#\s+TARGTYPE\s+(.*)/) {
            $tmp = $1;
			#prt( "# TARGTYPE $1\n" );
            $projtype = get_app_type_stg($tmp);
            prt( "Project Type [$projtype]\n" );
            $key = 'APP_TYPE';
            ${$hr}{$key} = $projtype;
	    } elsif ( $line =~ /^#\s+Begin\s+Group\s+(.*)/ ) {
            $group = strip_quotes_02($1);
			prt( "Begin Group  [$group]\n" );
        } elsif ( $line =~ /^#\s+PROP\s+Default_Filter\s+"(.*)".*$/ ) {
            $filter = $1;
            prt( "Filter       [$filter]\n" );
		} elsif ( $line =~ /^SOURCE=/ ) {
			$line =~ s/^SOURCE=//o;
			while ($line =~ /\W$/) { # ending in NON-alphanumic
				####prt( "Discarding [".substr($line,-1,1)."]!\n" );
				$line = substr($line,0,length($line)-1);
			}
			##while (( substr($line,-1,1) eq ' ' )||( substr($line,-1,1) eq "\t")||
			##	( substr($line,-1,1) eq "\r")||( substr($line,-1,1) eq "\n")) {
			##	$line = substr($line,0,length($line)-1);
			##}
			$line =~ s/^\"//; # remove leading inverted commas
			$line =~ s/\"$//; # remove trailing inverted commas
			$line = dos_to_unix_02($line);
			$line =~ s/^\.\///;
			if (($line =~ /\.cxx$/i) || ($line =~ /\.c$/i) || ($line =~ /\.cpp$/i) || ($line =~ /\.cc$/i)) {
				push(@dspsrcs, $line);
                push(@c_sources, [$line, $group, $filter]); # 'PROJECT_SRCS'
			} elsif ( ($line =~ /\.hxx$/i) || ($line =~ /\.h$/i) || ($line =~ /\.hpp$/i) ) {
				push(@dspsrcs, $line);
                push(@h_sources, [$line, $group, $filter]); # 'PROJECT_SRCS'
            } else {
                if ( !($line =~ /^\$\(/) ) {
                    prt( "CHECK Discarded [$line]\n" );
				}
			}
		}
	}
    $key = 'C_SOURCES';
    ${$hr}{$key} = [@c_sources];
    $key = 'H_SOURCES';
    ${$hr}{$key} = [@h_sources];
    $key = $projname;
    ${$mhr}{$key} = $hr;
	$lncnt = scalar @dspsrcs;
	prt( "File [$df] contains $lncnt SOURCES ...\n" );
	return \@dspsrcs;
}


sub process_DSP_file($$) {
    my ($df,$rmh) = @_;
    if (open(INF, "<$df")) {
        my @lns = <INF>;
        close INF;
        my $lncnt = scalar @lns;
        my $rdsrcs = scan_DSP_lines($df,$rmh,\@lns);
    } else {
        prtw("WARNING:scan_DSP_file: Unable to OPEN [$df]!\n");
    }
}

# building a DSP configuration block #!IF ..(a).. #!ELSEIF ..(b).. #!ELSE ..(c).. #!ENDIF
sub get_list_of_subs($) {
    my ($txt) = @_;
    my $len = length($txt);
    my ($i,$tag,$cc);
    my @arr = ();
    my %h = ();
    $tag = '';  # start NO TAG
    for ($i = 0; $i < $len; $i++) {
        $cc = substr($txt,$i,1);
        if ($cc =~ /(\w|-)/) {
            $tag .= $cc;    # accumulate TAG - add all '-' or \w (which INCLUDES the '_' char)
        } else {
            # NOT '-' or \w (which INCLUDES the '_' char)
            if (length($tag) && ($tag =~ /^-NEW_/)) {   # if the TAG begins properly
                #push(@arr,$tag);
                $h{$tag} = 1;   # store the substitution tag, like -NEW_LIBS-, 
            }
            $tag = '';  # clear tag and seek more
        }
    }
    if (length($tag) && ($tag =~ /^-NEW_/)) {   # if the TAG begins properly
        #push(@arr,$tag);
        $h{$tag} = 1;   # store the substitution tag, like -NEW_LIBS-, 
    }
    @arr = sort keys(%h);   # but just want an ARRAY list
    $len = scalar @arr;     # we MUST collect tags
    if ($len == 0) {
        prt( "ERROR: Collected NO tags! From string of $len chars -\n" );
        prt( "$txt\n" );
        pgm_exit(1,"ERROR: EXIT lib_dsphdrs:get_list_of_subs: NO TAGS FOUND in above! Seeking '-NEW_...'!");
    } else {
        #prt(" $len sub-tags " );
    }
    return \@arr;   # return array reference
}

################################################################################
# ============================================================================ #
sub get_project_name($$) {
    my ($rh,$rn) = @_;
    my $key = '-NEW_PROJECT_NAME-';
    my ($nm);
    $key = 'PROJECT_NAME' if (!defined ${$rh}{$key});
    if (defined ${$rh}{$key}) {
        $nm = ${$rh}{$key};
        if (length($nm)) {
            ${$rn} = $nm;
            return 1;
        } else {
            prtw("WARNING: Project name string BLANK!\n");
        }
    }
    return 0;
}

sub get_common_dsp_head($$) {
    my ($rh,$name) = @_;
    my $key = '-NEW_PROJECT_NAME-';
	my $dsp_head = <<EOF;
# Microsoft Developer Studio Project File - Name="-NEW_PROJECT_NAME-" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **

EOF
    $dsp_head =~ s/$key/$name/g;
    return $dsp_head;
}

sub get_targ_type_ref() {
    my %type_2_target_type_hash = (
        $app_console_stg  => '# TARGTYPE "Win32 (x86) Console Application" 0x0103',
        $app_windows_stg  => '# TARGTYPE "Win32 (x86) Application" 0x0101',
        $app_dynalib_stg  => '# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102',
        $app_statlib_stg  => '# TARGTYPE "Win32 (x86) Static Library" 0x0104'
    );
    return \%type_2_target_type_hash;
}

# based on list
sub get_based_on_ref() {
    my %type_2_based_on_hash = (
        $app_console_stg  => '(based on "Win32 (x86) Console Application")',
        $app_windows_stg  => '(based on "Win32 (x86) Application")',
        $app_dynalib_stg  => '(based on "Win32 (x86) Dynamic-Link Library")',
        $app_statlib_stg  => '(based on "Win32 (x86) Static Library")'
    );
    return \%type_2_based_on_hash;
}

sub get_APP_TYPE_string($) {
    my ($rh) = @_;
    my $key = 'APP_TYPE';
    $key = 'PROJECT_APTP' if (!defined ${$rh}{$key});
    return $key;
}
sub get_project_type($$) {
    my ($rh,$rs) = @_;
    my $key = get_APP_TYPE_string($rh); # 'APP_TYPE' or 'PROJECT_APTP'
    if (defined ${$rh}{$key}) {
        ${$rs} = ${$rh}{$key};
        return 1;
    }
    return 0;
}

sub is_dll_project($) {
    my ($rh) = @_;
    my ($type);
    if (get_project_type($rh,\$type) ) {
        if ($type eq $app_dynalib_stg) {
            return 1;
        }
    }
    return 0;
}

sub is_app_project($) {
    my ($rh) = @_;
    my ($type);
    if (get_project_type($rh,\$type) ) {
        if ($type eq $app_windows_stg) {
            return 1;
        }
    }
    return 0;
}


sub get_based_on_stg($$) {
    my ($rh,$rs) = @_;
    my $typ = '';
    if (get_project_type($rh,\$typ)) {
        my $rtth = get_based_on_ref();
        if (defined ${$rtth}{$typ}) {
            ${$rs} = ${$rtth}{$typ};
            return 1;
        }
    }
    return 0;
}

sub add_targ_type($$) {
    my ($rh,$rs) = @_;
    my $key = get_APP_TYPE_string($rh); # 'APP_TYPE' or 'PROJECT_APTP'
    my $rtth = get_targ_type_ref();
    if (defined ${$rh}{$key}) {
        my $typ = ${$rh}{$key};
        if (defined ${$rtth}{$typ}) {
            ${$rs} .= ${$rtth}{$typ}."\n\n";
            return 1;
        }
    }
    return 0;
}

sub add_def_config3($$) {
    my ($rh,$rs) = @_;
    my $name = '';
    return 0 if ( !get_project_name($rh,\$name) && length($name) ); 
    my ($key,$rcfgs,$cnt,$conf,$confname);
    my ($i);    # 2010/05/01 
    # my $rcfgs = ${$rh}{'PROJECT_CFGS'};
    $key = 'PROJECT_CFGS';
    if (defined ${$rh}{$key}) {
        $rcfgs = ${$rh}{$key};
        $cnt = scalar @{$rcfgs};
        if ($cnt) {
            #                   0          1          2           3
            #                   Debug      -NEW_OUTD- Debug|WIN32
            # push(@{$rcfgs}, [ $confname, $var1,     $conf,      $dsp_sub_sub ]);
            # search for 'Debug', and make it the Default
            for ($i = 0; $i < $cnt; $i++) {
                $confname = ${$rcfgs}[$i][0];
                if ($confname eq 'Debug') {
                    $conf = "$name - Win32 $confname";
                    ${$rh}{'CURR_CONF'} = $confname;
                    ${$rh}{'CURR_CON1'} = ${$rcfgs}[$i][2];
                    ${$rs} .= "CFG=$conf\n";
                    return 1;
                }
            }
            # could not find pure 'Debug', so use FIRST in array
            $confname = ${$rcfgs}[0][0];
            $conf = "$name - Win32 $confname";
            ${$rh}{'CURR_CONF'} = $confname;
            ${$rh}{'CURR_CON1'} = ${$rcfgs}[$i][2];
            ${$rs} .= "CFG=$conf\n";
           return 1;
        }
    }
    return 0;
}

sub add_def_config($$) {
    my ($rh,$rs) = @_;
    my ($key);
    my $name = '';
    return 0 if ( !get_project_name($rh,\$name) && length($name) ); 
    # first search for 'Debug', and make it the DEFAULT
    foreach $key (keys %{$rh}) {
        if ($key =~ /^config-\d+/) {
            # this is the FIRST config item - make it the DEFAULT
            # like $def_sub{'config-001-Debug'} = get_default_sub3();
            my @arr = split('-',$key);
            my $cnt = scalar @arr;
            if ( $cnt >= 3) {
                my $conf = $arr[2];
                my $i = 3;
                for (; $i < $cnt; $i++) {
                    $conf .= '-';
                    $conf .= $arr[$i];
                }
                if ($conf eq 'Debug') {
                    ${$rs} .= "CFG=$name - Win32 $conf\n";
                    return 1;
                }
            }
        }
    }
    # No pure 'Debug' found, so take the FIRST
    foreach $key (sort keys %{$rh}) {
        if ($key =~ /^config-\d+/) {
            # this is the FIRST config item - make it the DEFAULT
            # like $def_sub{'config-001-Debug'} = get_default_sub3();
            my @arr = split('-',$key);
            my $cnt = scalar @arr;
            if ( $cnt >= 3) {
                my $conf = $arr[2];
                my $i = 3;
                for (; $i < $cnt; $i++) {
                    $conf .= '-';
                    $conf .= $arr[$i];
                }
                ${$rs} .= "CFG=$name - Win32 $conf\n";
                return 1;
            }
        }
    }
    return 0;
}

# Get the configs into a tidy array
my $prev_proj_name = '';
sub get_configs_array3($$$) {
    my ($rh,$rs,$name) = @_;
    my @confarr = ();
    my ($key,$rcfgs,$cfgcnt,$confname,$hr2,$conf,$test,@offs,$vcconf,@arr);
    my ($i,$ncnt);    # 2010/05/01 
    my $prt_out = 1; # ($prev_proj_name eq $act_proj_dsphdrs) ? 0 : 1;
    # my $rcfgs = ${$rh}{'PROJECT_CFGS'};
    my $dbg = $g_write_dbg;
    my ($capname,$key2);
    $cfgcnt = 0;
    $key = 'CURR_DBGF';
    $dbg = ${$rh}{$key} if (defined ${$rh}{$key});
    if ($dbg & 128) { $prt_out = 1; } else { $prt_out = 0; }
    $key = '-NEW_PROJECT_NAME-';
    if (defined ${$rh}{$key}) {
        $capname = ${$rh}{$key};
    } else {
        $key2 = 'PROJECT_NAME';
        if (defined ${$rh}{$key2}) {
            $capname = ${$rh}{$key2};
            # $hvers = 1;
        } else {
            prtw( "WARNING:lib dsphdrs:get_configs_array3: No project NAME in hash... key=[$key]or[$key2]\n" );
            # $isok = 0;
            $capname = "NOT AVAILABLE!";
        }
    }
    $key = 'PROJECT_CFGS';
    @offs = ();
    if (defined ${$rh}{$key}) {
        $rcfgs = ${$rh}{$key};
        $cfgcnt = scalar @{$rcfgs};
        prt("lib dsphdrs:get_configs_array3: proj [$capname] key [$key] - Got $cfgcnt CONFIGS...\n") if ($prt_out);
        $test = 0;
        push(@offs,-99);    # setup min of 2 offsets
        push(@offs,-99);    # to be fixed when found, IF found
        for ($i = 0; $i < $cfgcnt; $i++) {
            $confname = ${$rcfgs}[$i][0];
            $vcconf   = ${$rcfgs}[$i][2];
            if (!$vcconf) {
                #pgm_exit(1,"ERROR:get_configs_array3: $capname ref hash passed $key \$vcconf is UNDEFINED!\n") if (!$vcconf);
                prtw("WARNING:get_configs_array3:$i: proj [$capname] ref hash passed $key \$vcconf is UNDEFINED!\n");
                next;
            }
            # SPECIAL FIX = ONLY INCLUDE WIN32 #
            next if ($only_win32_conf && !($vcconf =~ /WIN32/i));
            @arr = split(/\|/, $vcconf);
            if (($confname eq 'Release') && !($test & 1) && ($vcconf =~ /Win32/i)) {
                $offs[0] = $i;
                $test |= 1;
            } elsif (($confname eq 'Debug') && !($test & 2) && ($vcconf =~ /Win32/i)) {
                $offs[1] = $i;
                $test |= 2;
            } else {
                push(@offs,$i);
            }
        }
        if ($test == 3) {
            $ncnt = scalar @offs;
            if ($ncnt != $cfgcnt) {
                prt("lib dsphdrs:get_configs_array3: Reduced to $ncnt of $cfgcnt CONFIGS\n") if ($prt_out);
            }
            # process in ORDER 'Release, Debug, Others'
            #                   0          1          2           3
            #                   Debug      -NEW_OUTD- Debug|WIN32
            # push(@{$rcfgs}, [ $confname, $var1,     $conf,      $dsp_sub_sub ]);
            foreach $i (@offs) {
                $confname = ${$rcfgs}[$i][0];
                $vcconf   = ${$rcfgs}[$i][2];
                $hr2      = ${$rcfgs}[$i][3];
                @arr = split(/\|/, $vcconf);
                # build the NAME string
                if ($arr[1] =~ /64/) {
                    $arr[1] = "Win64";
                } else {
                    $arr[1] = "Win32";
                }
                $conf = "\"$name - $arr[1] $confname\"";
                #               0      1           2
                push(@confarr,[ $conf, '<no key>', $hr2 ] );
                prt("lib dsphdrs:get_configs_array3:1: Stored [$conf]\n") if ($prt_out);
                if (length(${$rs}) == 0) {
                    ${$rs} = $conf;     # set the FIRST, as DEFAULT
                    ${$rh}{'CURR_CONF'} = $confname;
                    ${$rh}{'CURR_CON1'} = ${$rcfgs}[$i][2];
                }
            }
        } else {
            # process in array ORDER
            #                   0          1          2           3
            #                   Debug      -NEW_OUTD- Debug|WIN32
            # push(@{$rcfgs}, [ $confname, $var1,     $conf,      $dsp_sub_sub ]);
            for ($i = 0; $i < $cfgcnt; $i++) {
                $confname = ${$rcfgs}[$i][0];
                $vcconf   = ${$rcfgs}[$i][2];
                $hr2      = ${$rcfgs}[$i][3];
                next if ($only_win32_conf && !($vcconf =~ /WIN32/i));
                # build the NAME string
                $conf = "\"$name - Win32 $confname\"";
                #               0      1           2
                push(@confarr,[ $conf, '<no key>', $hr2 ] );
                prt("lib dsphdrs:get_configs_array3:2: Stored [$conf]\n") if ($prt_out);
                if (length(${$rs}) == 0) {
                    ${$rs} = $conf;     # set the FIRST, as DEFAULT
                    ${$rh}{'CURR_CONF'} = $confname;
                    ${$rh}{'CURR_CON1'} = ${$rcfgs}[$i][2];
                }
            }
        }
    }
    # ========================
    $key = 'CURR_CARR';
    ${$rh}{$key} = [@confarr];
    # ========================
    $prev_proj_name = $act_proj_dsphdrs;
    my $nccnt = scalar @confarr;
    prt("lib dsphdrs:get_configs_array3: proj [$capname] begin $cfgcnt, returning ref array of $nccnt items\n") if ($prt_out);
    return \@confarr;
}

sub get_configs_array($$$) {
    my ($rh,$rs,$name) = @_;
    my @confarr = ();
    my %hash = ();
    my ($key,@arr,$conf,$cnt,$test,@ckeys,$keylist);
    my ($i);    # 2010/05/01 
    ${$rs} = '';
    # "-NEW_PROJECT_NAME- - Win32 Release"
    # Try to enforce order
    @ckeys = ();
    $test = 0;
    push(@ckeys,'<nk>');
    push(@ckeys,'<nk>');
    foreach $key (keys %{$rh}) {
        if ($key =~ /^config-\d+/) {
            @arr = split('-',$key);
            $conf = '';
            $cnt = scalar @arr;
            if ( $cnt >= 3) {
                $conf .= $arr[2];
                $i = 3;
                for (; $i < $cnt; $i++) {
                    $conf .= '-';
                    $conf .= $arr[$i];
                }
            }
            if ($conf eq 'Release') {
                $ckeys[0] = $key;
                $test |= 1;
            } elsif ($conf eq 'Debug') {
                $ckeys[1] = $key;
                $test |= 2;
            } else {
                push(@ckeys,$key);
            }
        }
    }
    if ($test == 3) {
        #prt( "We can ORDER the KEYS\n" );
        foreach $key (@ckeys) {
            my $rh2 = ${$rh}{$key};
            # like $def_sub{'config-001-Debug'} = get_default_sub3();
            @arr = split('-',$key);
            $conf = "\"$name - Win32 ";
            $cnt = scalar @arr;
            if ( $cnt >= 3) {
                $conf .= $arr[2];
                $i = 3;
                for (; $i < $cnt; $i++) {
                    $conf .= '-';
                    $conf .= $arr[$i];
                }
                $conf .= '"';
                ${$rs} = $conf if (length(${$rs}) == 0);
                if (defined $hash{$conf}) {
                    prtw("WARNING: DUPLICATED config [$conf]!\n");
                    @arr = ();
                    return @arr;
                }
                $hash{$conf} = 1;
                ### prt("CONFIG: [$conf], key=[$key]\n");
                push(@confarr,[ $conf, $key, $rh2 ] );
            } else {
                prtw("WARNING: split of [$key] did not give 3 or more items!\n");
                @arr = ();
                return @arr;
            }
        }
    } else {
        prtw( "Order of keys FAILED! Using pure key sort order - 'config-???-Debug', etc\n" );
        foreach $key (sort keys %{$rh}) {
            if ($key =~ /^config-\d+/) {
                my $rh2 = ${$rh}{$key};
                # like $def_sub{'config-001-Debug'} = get_default_sub3();
                @arr = split('-',$key);
                $conf = "\"$name - Win32 ";
                $cnt = scalar @arr;
                if ( $cnt >= 3) {
                    $conf .= $arr[2];
                    $i = 3;
                    for (; $i < $cnt; $i++) {
                        $conf .= '-';
                        $conf .= $arr[$i];
                    }
                    $conf .= '"';
                    ${$rs} = $conf if (length(${$rs}) == 0);
                    if (defined $hash{$conf}) {
                        prtw("WARNING: DUPLICATED config [$conf]!\n");
                        @arr = ();
                        return @arr;
                    }
                    $hash{$conf} = 1;
                    ### prt("CONFIG: [$conf], key=[$key]\n");
                    push(@confarr,[ $conf, $key, $rh2 ] );
                } else {
                    prtw("WARNING: split of [$key] did not give 3 or more items!\n");
                    @arr = ();
                    return @arr;
                }
            }
        }
    }
    ### prt( "Return ref to array of ".scalar @confarr." configs...\n" );
    $key = 'CURR_CARR';
    ${$rh}{$key} = [@confarr];
    return \@confarr;
}

sub get_hash_version($) {
    my ($rh) = @_;
    my $key = 'PROJECT_VERS';
    if (defined ${$rh}{$key}) {
        return ${$rh}{$key};
    }
    return 0;
}

sub add_message_section($$) {
    my ($rh,$rs) = @_;
    my ($name, $conf, $i, $cnt, @arr, $basedon, $msg, $key);
    my ($hvers, $conf1, $rconfarr, $func);
    $hvers = get_hash_version($rh);
    if ($hvers) {
        $func = \&get_configs_array3;   # ($$$);
        $msg = "Version $hvers using get_configs_array3 function";
    } else {
        $func = \&get_configs_array;    # ($$$);
        $msg = "Version $hvers using get_configs_array function";
    }
    if ( !get_project_name($rh,\$name) ) {
        return 0;
    }
    if ( !get_based_on_stg($rh, \$basedon) ) {
        return 0;
    }
    $conf1 = '';
    ####prt("lib_dsphdrs:add_message_section: Calling function: $name $msg\n");
    $rconfarr = $func->($rh, \$conf1, $name);
    $cnt = scalar @{$rconfarr};
    if ($cnt < 2) {
        prtw("WARNING:$name: $msg returned count [$cnt] configs!\n" );
        return 0;
    }
    $msg  = "!MESSAGE This is not a valid makefile. To build this project using NMAKE,\n";
    $msg .= "!MESSAGE use the Export Makefile command and run\n";
    $msg .= "!MESSAGE \n";
    $msg .= "!MESSAGE NMAKE /f \"".$name.".mak\".\n";
    $msg .= "!MESSAGE \n";
    $msg .= "!MESSAGE You can specify a configuration when running NMAKE\n";
    $msg .= "!MESSAGE by defining the macro CFG on the command line. For example:\n";
    $msg .= "!MESSAGE \n";
    $msg .= "!MESSAGE NMAKE /f \"".$name.".mak\" CFG=".$conf1."\n";
    $msg .= "!MESSAGE \n";
    $msg .= "!MESSAGE Possible choices for configuration are:\n";
    $msg .= "!MESSAGE \n";
    for ($i = 0; $i < $cnt; $i++) {
        $conf = ${$rconfarr}[$i][0];
        $msg .= "!MESSAGE $conf $basedon\n";
    }
    $msg .= "!MESSAGE \n";
    $msg .= "\n";
    ${$rs} .= $msg;
    return 1;
}

sub get_compile_block($$) {
    my ($rh,$rs) = @_;

    return 0;
}

sub get_project_flag($) {
    my ($rh) = @_;
    my $key = "PROJECT_FLAGS";
    my $flag = 0;
    if (defined ${$rh}{$key}) {
        $flag = ${$rh}{$key}[0];
    }
    return $flag;
}

sub show_rh2($$$$) {
    my ($conf,$rh2,$stg,$rsa) = @_;
    prt("For CONFIG [$conf], got...\n");
    my ($key,$val);
    foreach $key (keys %{$rh2}) {
        $val = ${$rh2}{$key};
        prt("$key = [$val]\n");
    }
    prt("And for the \@ras array, got...\n");
    foreach $key (@{$rsa}) {
        if (defined ${$rh2}{$key}) {
            $val = ${$rh2}{$key};
            prt("$key = [$val]\n");
        } else {
            prt("$key = NOT DEFINED!\n");
        }
    }
}

# NOTE: CMake uses 'MinSizeRel|Win32' and 'RelWithDebInfo|Win32'
# ==============================================================
sub get_act_config_type($) {      # test /Release/i and /Debug/i and others
    my $val = shift;
    my $cfg = 0;
    if ($val =~ /Release/i) {
        $cfg = 0;
    } elsif ($val =~ /Debug/i) {
        $cfg = 1;
    } elsif ($val =~ /Rel/i) {
        $cfg = 0;
    } elsif ($val =~ /DLL/i) {
        $cfg = 0;
    } elsif ($val =~ /template/i) {
        # NOT sure WHAT THIS IS
        $cfg = 0;
    } else {
        prtw("WARNING: Could NOT set config type from [$val] FIX ME! Def to Rel(0)\n");
        $cfg = 0;
    }
    return $cfg;
}


sub get_dsp_head_stg($$$) {
    my ($rh,$rs,$dbg) = @_;
    my $name = '';
    my $type = '';
    my ($i,$conf,$key,$rh2,$hvers,$func,$func2,$msg);
    my $modstg = 'fgdpshdrs03:get_dsp_head_stg;';
    my ($nt,@tmparr);
    $hvers = get_hash_version($rh);
    if ($hvers) {
        $func = \&get_configs_array3; # ($$$);
        $func2 = \&add_def_config3;
        $msg = "vers=$hvers use f3";
    } else {
        $func = \&get_configs_array; # ($$$);
        $func2 = \&add_def_config;
        $msg = "vers=$hvers use f";
    }

    my $pflag = get_project_flag($rh);
    $name = '';
    if ( !get_project_name($rh, \$name) || (length(trim_all($name)) == 0)) {
        prtw("ERROR:$modstg Unable to get project name!\n");
        return 0;
    }
    if ( !get_project_type($rh, \$type) ) {
        prtw("ERROR:$modstg Unable to get project type!\n");
        return 0;
    }
    my $isdllapp = (is_dll_project($rh) | is_app_project($rh));
    my $conf1 = '';
    #my $rconfarr = get_configs_array($rh, \$conf1, $name);
    prt("lib_dsphdrs:get_dsp_head_stg: proj [$name] calling func $msg\n") if ($dbg);
    my $rconfarr = $func->($rh, \$conf1, $name);
    my $cnt = scalar @{$rconfarr};
    if ($cnt < 2) {
        prtw("WARNING:$modstg get_configs_array returned [$cnt] configs!\n" );
        return 0;
    }
    my $flag = 0;
    my ($stg,$rsa,$ky,$min,$len,$tconf);
    # FIX20110317 - eliminate the project name from the $conf string, BEFORE testing for 'Release' & 'Debug'
    for ($i = 0; $i < $cnt; $i++) {
        $conf = ${$rconfarr}[$i][0];
        $tconf = $conf; # FIX20110317 - copy, and
        $tconf =~ s/$name//; # eliminate the NAME
        if ($hvers == 0) {  # first version hash
            $key  = ${$rconfarr}[$i][1];
            if ( !defined ${$rh}{$key}) {
                prtw("WARNING:$modstg Unable to get ref HASH for key [$key] [$conf]!\n");
                return 0;
            }
        }
        if (get_act_config_type($tconf)) {    # test /Release/i and /Debug/i and others
            $flag |= 2;
        } else {
            $flag |= 1;
        }
#        if ($tconf =~ /Release/i) {
#            $flag |= 1;
#        } elsif ($tconf =~ /Debug/i) {
#            $flag |= 2;
#        } elsif ($conf =~ /Rel/i) {
#            $flag |= 1; # 2012/04/11 - CMake uses 'MinSizeRel|Win32' and 'RelWithDebInfo|Win32' - see get_act_config_type above
#        } elsif ($tconf =~ /DLL/) {
#            $flag |= 1; # 2010/05/05 - assume RELEASE when a DLL type
#        } elsif ($tconf =~ /Template/i) {
#            $flag |= 1; # 2012/04/16 - assume RELEASE when a MSVC10 'template' - whatever that is...
#        } else {
#            prtw("WARNING:$modstg Can NOT put [$conf] [$tconf] in Debug or Release category [$key]!\n");
#            return 0;
#        }
    }
    if ($flag != 3) {
       prtw("WARNING:$modstg Do NOT have at least 1 Debug and 1 Release category!\n");
       return 0;
    }
    # ===========================================================
    # commence string
    prt("lib_dsphdrs:get_dsp_head_stg: proj [$name] calling get_common_dsp_head $msg\n") if ($dbg);
    my $dsp_stg = get_common_dsp_head($rh,$name);
    if ( !add_targ_type($rh, \$dsp_stg) ) {
        prtw("WARNING:$modstg Unable to get target type string! 'PROJECT_APTP' NOT SET!\n");
        return 0;
    }
    # if ( !add_def_config($rh, \$dsp_stg) ) {
    prt("lib_dsphdrs:get_dsp_head_stg: proj [$name] calling func2 $msg\n") if ($dbg);
    if ( ! $func2->($rh, \$dsp_stg) ) {
        prtw("WARNING:$modstg Unable to get default config string!\n");
        return 0;
    }
    if ( !add_message_section($rh, \$dsp_stg) ) {
        prtw("WARNING:$modstg Unable to get MESSAGE section!\n");
        return 0;
    }
    $dsp_stg .= "# Begin Project\n";
    $dsp_stg .= "# PROP AllowPerConfigDependencies 0\n";
    $dsp_stg .= "# PROP Scc_ProjName \"\"\n";
    $dsp_stg .= "# PROP Scc_LocalPath \"\"\n";
    $dsp_stg .= "CPP=cl.exe\n";
    $dsp_stg .= "MTL=midl.exe\n" if ($isdllapp);
    $dsp_stg .= "RSC=rc.exe\n";
    $dsp_stg .= "\n";
    $func = 0;

    # begin the CONFIGS - process substitution of things like say -NEW_LIBS-, on a PER config basis
    #prt("Pre-display of $cnt configs...\n");
    #for ($i = 0; $i < $cnt; $i++) {
        #$conf = ${$rconfarr}[$i][0];    # This is the FULL string 'name - Win32 Debug'
        #$rh2 = ${$rconfarr}[$i][2];
        #if ( get_fetch_function($type,$conf,\$func,$dbg) ) {
            #$stg = $func->();
            #$rsa = get_list_of_subs($stg);  # collect list of '-NEW_...' tags
            #show_rh2($conf,$rh2,$stg,$rsa);
        #}
    #}

    $flag = 0;
    for ($i = 0; $i < $cnt; $i++) {
        $conf = ${$rconfarr}[$i][0];    # This is the FULL string 'name - Win32 Debug'
        $rh2 = ${$rconfarr}[$i][2];
        $tconf = $conf; # FIX20110317 - copy, and
        $tconf =~ s/$name//; # eliminate the NAME
        if (get_act_config_type($tconf)) {     # test /Release/i and /Debug/i and others
            $flag = 2;
        } else {
            $flag = 1;
        }
#        if ($tconf =~ /Release/i) {
#            $flag = 1;
#        } elsif ($tconf =~ /Debug/i) {
#            $flag = 2;
#        } elsif ($tconf =~ /DLL/) {
#            $flag = 1; # 2010/05/05 - assume RELEASE when a DLL type
#        }
        #if ($hvers) {
        #    $rh2 = ${$rconfarr}[$i][2];
        #} else {
        #    $key  = ${$rconfarr}[$i][1];
        #    if (defined ${$rh}{$key}) {
        #        $rh2 = ${$rh}{$key};
        #    } else {
        #        prtw("WARNING:$modstg Unable to get ref HASH for key [$key]!\n");
        #        return 0;
        #    }
        #}
        if ($i == 0) {
            # !IF  "\$(CFG)" == "-NEW_PROJECT_NAME- - Win32 Release"
            $dsp_stg .= "!IF  \"\$(CFG)\" == $conf\n\n";
        } else {
            #!ELSEIF  "\$(CFG)" == "-NEW_PROJECT_NAME- - Win32 Debug"
            $dsp_stg .= "!ELSEIF  \"\$(CFG)\" == $conf\n\n";
        }
        # if ( get_fetch_function($type,$conf,\$func,$dbg) ) {
        if ( get_fetch_function($type,$tconf,\$func,$dbg) ) {
            $stg = $func->();
            $rsa  = get_list_of_subs($stg);  # collect list of '-NEW_...' tags
            # DO THE SUSTITUTIONS, per configuration
            # ======================================
            $msg = "Doing substitutions for [$conf] [$tconf] [$type]";
            $min = length($msg);
            prt("$msg\t[dbg & 8])\n" ) if ($dbg & 8);
            foreach $ky (@{$rsa}) {
                if (defined ${$rh2}{$ky}) {
                    $nt = ${$rh2}{$ky}; # extract the substitution value
                    $stg =~ s/$ky/$nt/gm;   # DO IT
                    $ky .= ' ' while (length($ky) < 12); # -NEW_INTER- length
                    $msg = "Subbed $ky with [$nt]";
                    $msg .= ' ' while (length($msg) < $min);
                    prt("$msg\t[dbg & 8]\n" ) if ($dbg & 8);
                } else {
                    prtw( "WARNING:$modstg Substitution for [$ky] is NOT in extracted reference!\n" );
                    @tmparr = keys(%{$rh2});
                    prt( "Got " );
                    foreach $nt (@tmparr) {
                        prt("[$nt]");
                    }
                    prt("\n");
                    return 0;
                }
            }
            # ==================================
            $dsp_stg .= $stg;   # add now substituted string to DSP output string
        } else {
            prtw("WARNING:$modstg Unable to get fetch function [$key][$conf][$type]!\n");
            return 0;
        }
    }
    $dsp_stg .= "!ENDIF \n";
    $dsp_stg .= "\n";
    $dsp_stg .= "# Begin Target\n";
    $dsp_stg .= "\n";
    # Name "-NEW_PROJECT_NAME- - Win32 Release"
    # Name "-NEW_PROJECT_NAME- - Win32 Debug"
    for ($i = 0; $i < $cnt; $i++) {
        $conf = ${$rconfarr}[$i][0]; # This is the FULL string, with quotes, like '"name - Win32 Debug"'
        $dsp_stg .= "# Name - $conf\n";
    }
    ${$rs} = $dsp_stg;
	return 1;
}

sub get_def_lib_list() {
    my $ll = 'kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib';
    return $ll;
}

# Windows Console Application
# ===========================
sub get_dsp_console_rel {
	my $dsp_head = <<EOF;
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir "."
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir -NEW_OUTD-
# PROP Intermediate_Dir -NEW_INTER-
# PROP Ignore_Export_Lib 0
# PROP Target_Dir "."
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
# ADD CPP /nologo -NEW_RT- /W3 /GR /GX /O2 -NEW_INCS- /D "WIN32" /D "NDEBUG" /D "_CONSOLE" -NEW_DEFS- /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x809 /d "NDEBUG"
# ADD RSC /l 0x809 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib -NEW_LIBS- /nologo /subsystem:console /machine:I386 -NEW_OUT-
-NEW_POST-
EOF
    return $dsp_head;
}

sub get_dsp_console_dbg {
	my $dsp_head = <<EOF;
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir "."
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir -NEW_OUTD-
# PROP Intermediate_Dir -NEW_INTER-
# PROP Ignore_Export_Lib 0
# PROP Target_Dir "."
# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
# ADD CPP /nologo -NEW_RT- /W3 /Gm /GR /GX /ZI /Od -NEW_INCS- /D "WIN32" /D "_DEBUG" /D "_CONSOLE" -NEW_DEFS- /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x809 /d "_DEBUG"
# ADD RSC /l 0x809 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:console /debug /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib -NEW_LIBS- /nologo /subsystem:console /debug /machine:I386 -NEW_OUT-
-NEW_POST-
EOF
	return $dsp_head;
}

# Windows Application
# ===========================
sub get_dsp_app_rel {
	my $dsp_head = <<EOF;
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir -NEW_OUTD-
# PROP Intermediate_Dir -NEW_INTER-
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /c
# ADD CPP /nologo /W3 -NEW_RT- /GR /GX /O2 -NEW_INCS- /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" -NEW_DEFS- /FD /c
# SUBTRACT CPP /YX /Yc /Yu
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x809 /d "NDEBUG"
# ADD RSC /l 0x809 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib -NEW_LIBS- /nologo /subsystem:windows /machine:I386 -NEW_OUT-
-NEW_POST-
EOF
    return $dsp_head;
}

sub get_dsp_app_dbg {
	my $dsp_head = <<EOF;
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir -NEW_OUTD-
# PROP Intermediate_Dir -NEW_INTER-
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
# ADD CPP /nologo /W3 -NEW_RT- /Gm /GR /GX /ZI /Od -NEW_INCS- /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" -NEW_DEFS- /FD /GZ /c
# SUBTRACT CPP /YX /Yc /Yu
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x809 /d "_DEBUG"
# ADD RSC /l 0x809 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib -NEW_LIBS- /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept -NEW_OUT-
-NEW_POST-
EOF
	return $dsp_head;
}

# Windows Static Library
# ===========================
sub get_dsp_slib_rel {
	my $dsp_head = <<EOF;
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir -NEW_OUTD-
# PROP Intermediate_Dir -NEW_INTER-
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /W3 /GR /GX /O2 -NEW_RT- -NEW_INCS- /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" -NEW_DEFS- /FD /c
# SUBTRACT CPP /YX /Yc /Yu
# ADD BASE RSC /l 0x809 /d "NDEBUG"
# ADD RSC /l 0x809 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo -NEW_OUT-
-NEW_POST-
EOF
    return $dsp_head;
}

sub get_dsp_slib_dbg {
	my $dsp_head = <<EOF;
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir -NEW_OUTD-
# PROP Intermediate_Dir -NEW_INTER-
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
# ADD CPP /nologo /W3 /Gm /GR /GX /ZI /Od -NEW_RT- -NEW_INCS- /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" -NEW_DEFS- /FD /GZ /c
# SUBTRACT CPP /YX /Yc /Yu
# ADD BASE RSC /l 0x809 /d "_DEBUG"
# ADD RSC /l 0x809 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
# ADD LIB32 /nologo -NEW_OUT-
-NEW_POST-
EOF
	return $dsp_head;
}

# Windows Dynamic Library
# ===========================
sub get_dsp_dynalib_rel {
	my $dsp_head = <<EOF;
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir -NEW_OUTD-
# PROP Intermediate_Dir -NEW_INTER-
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Yu"stdafx.h" /FD /c
# ADD CPP /nologo -NEW_RT- /W3 /GR /GX /O2 -NEW_INCS- /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" -NEW_DEFS- /FD /c
# SUBTRACT CPP /YX /Yc /Yu
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x809 /d "NDEBUG"
# ADD RSC /l 0x809 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /dll /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib -NEW_LIBS- /nologo /dll /machine:I386 -NEW_OUT-
-NEW_POST-
EOF
    return $dsp_head;
}

sub get_dsp_dynalib_dbg {
	my $dsp_head = <<EOF;
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir -NEW_OUTD-
# PROP Intermediate_Dir -NEW_INTER-
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /Yu"stdafx.h" /FD /GZ  /c
# ADD CPP /nologo -NEW_RT- /W3 /Gm /GR /GX /ZI /Od -NEW_INCS- /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" -NEW_DEFS- /FD /GZ  /c
# SUBTRACT CPP /YX /Yc /Yu
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x809 /d "_DEBUG"
# ADD RSC /l 0x809 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib -NEW_LIBS- /nologo /dll /debug /machine:I386 /pdbtype:sept -NEW_OUT-
-NEW_POST-
EOF
    return $dsp_head;
}

# BUG: 2011-03-17 - UGH for a project like
# Returning function 'get_dsp_slib_dbg' for ["libsgdebug_a - Win32 Release"] of [Static Library]...[dbg & 16]
# need to eliminate the project NAME from the config string
sub get_fetch_function($$$$) {
    my ($type,$conf,$rfunc,$dbg) = @_;
    my $dbrel = get_act_config_type($conf);   # test /Release/i and /Debug/i and others
    my $msg = '';

#    if ($conf =~ /Debug/i) {
#        $dbrel = 1;
#    } elsif ($conf =~ /Release/i) {
#        $dbrel = 0;
#    } elsif ($conf =~ /Rel/i) {
#        $dbrel = 0; # 2012/04/11 - CMake uses 'MinSizeRel|Win32' and 'RelWithDebInfo|Win32' - see get_act_config_type()
#    } elsif ($conf =~ /DLL/) {
#        $dbrel = 0; # 2010/05/05 - assume DLL is 'Release type
#    } elsif ($conf =~ /Template/i) {
#        $dbrel = 0; # 2012/04/16 - assume 'Template' is 'Release type - MSVC10 addition
#    } else {
#        prtw("WARNING: [$conf] does NOT contain 'Debug' or 'Release' [$type]\n");
#        return 0;
#    }
    my %type_2_functions = (
        $app_console_stg => [ ['get_dsp_console_rel', \&get_dsp_console_rel], ['get_dsp_console_dbg', \&get_dsp_console_dbg] ],
        $app_windows_stg => [ ['get_dsp_app_rel'    , \&get_dsp_app_rel    ], ['get_dsp_app_dbg'    , \&get_dsp_app_dbg    ] ],
        $app_dynalib_stg => [ ['get_dsp_dynalib_rel', \&get_dsp_dynalib_rel], ['get_dsp_dynalib_dbg', \&get_dsp_dynalib_dbg] ],
        $app_statlib_stg => [ ['get_dsp_slib_rel'   , \&get_dsp_slib_rel   ], ['get_dsp_slib_dbg'   , \&get_dsp_slib_dbg   ] ]
        );

    if (defined $type_2_functions{$type}) {
        my $ra = $type_2_functions{$type};
        $msg = ${$ra}[$dbrel][0];
        ${$rfunc} = ${$ra}[$dbrel][1];
    } else {
        prtw("WARNING: type [$type] string NOT matched! [$conf]\n");
        return 0;
    }

    prt( "Returning function '$msg' for [$conf] of [$type]...[dbg & 16]\n" ) if ($dbg & 16);
    return 1;
}

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


sub get_dsp_tail {
	my $dsp_tail = <<EOF;
# End Target
# End Project
EOF

	return $dsp_tail;
}

sub get_dsw_head {
	### default DSW
	my $raw_dswh = <<EOF;
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!

EOF
	return $raw_dswh;
}


sub get_dsw_tail {
	my $raw_dswt = <<EOF;
###############################################################################

Global:

Package=<5>
{{{
}}}

Package=<3>
{{{
}}}

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

EOF
	return $raw_dswt;
}

sub get_dsp_header_text {
    my ($rm, $v) = @_;
    my $iok = 0;
    prtw( "WARNING: get_dsp_header_text: DEPRECIATED FUNCTION USED!\n" );
    return $iok;
}

my $def_runtime_lts = "MD";
my $def_runtime_stg = "Multithreaded DLL";
my $def_runtime_val = 2;
#    0                  1     2                         3  4
my @fg_runtimes_array = (
    ["RuntimeLibrary", "MT",  "Multithreaded",           0, 0],
    ["RuntimeLibrary", "MTd", "Multithreaded Debug",     1, 0],
    ["RuntimeLibrary", $def_runtime_lts, $def_runtime_stg, $def_runtime_val, 0],
    ["RuntimeLibrary", "MDd", "Multithreaded DLL Debug", 3, 0],
    ["RuntimeLibrary", "ML",  "Single Thread",           4, 0],
    ["RuntimeLibrary", "MLd", "Single Thread Debug",     5, 0]
);

sub fg_get_runtime_val_2_lts {
	my ($rtn) = shift;
    my $len = scalar @fg_runtimes_array;
    for (my $i = 0; $i < $len; $i++) {
        my $itm = $fg_runtimes_array[$i][3];
        if ($itm == $rtn) {
            return "/" . $fg_runtimes_array[$i][1];
        }
    }
    prtw( "WARNING: $rtn not found in runtimes array! Using default '$def_runtime_lts'!!\n" );
	return "/$def_runtime_lts";
}

sub get_app_conf_type {
    my ($ct) = shift;
    # per <Configuration ... ConfigurationType="4" ...
    my %vc_cts = (
        1  => $app_windows_stg,
        2  => $app_dynalib_stg,
        4  => $app_statlib_stg,
        10 => $app_utility_stg );
    my $at = '';
    if (defined $vc_cts{$ct}) {
        $at = $vc_cts{$ct};
    } else {
        prtw( "WARNING: Failed to FIND '$ct' in v8_conftypes!\n" );
        mydie( "ABORTING, in disgust ;=))\n" );
    }
    return $at;
}

sub get_app_conf_type2($$) {
    my ($ct,$rat) = @_;
    my $iret = 1;
    # per <Configuration ... ConfigurationType="4" ...
    my %vc_cts = (
        1  => $app_windows_stg,
        2  => $app_dynalib_stg,
        4  => $app_statlib_stg,
        10 => $app_utility_stg );
    if (defined $vc_cts{$ct}) {
        ${$rat} = $vc_cts{$ct};
        $iret = 0;
    }
    return $iret;   
}


# $apptype = adjust_app_type_per_subsystem( $apptype, $adddeps );
sub adjust_app_type_per_subsystem {
    my ($capt,$ss,$dbg) = @_;
    # and if 1 ($app_windows), then per SubSystem
    # <Tool Name="VCLinkerTool" ... SubSystem="2"
    # SubSystem="1" = :console or "2" = :window
    my %vc_ss = (
        1 => $app_console_stg,
        2 => $app_windows_stg );
    if (defined $vc_ss{$ss}) {
        my $napt = $vc_ss{$ss};
        if ($capt eq $napt) {
            prt("Got APPTYPE: NO CHANGE [$capt] equals SubSystem ($ss) [$napt] ...\n" ) if ($dbg);
        } else {
            if ($capt eq $app_dynalib_stg) {
                if ($napt eq $app_windows_stg) {
                    # these are NOT incompatible, so NO CHANGE
                    prt("Got APPTYPE: NO CHANGE [$capt] EQUIVALENT to $ss ($napt) ...\n" ) if ($dbg);
                } else {
                    prtw("WARNING: NO CHANGE but [$capt] NOT EQUIVALENT to $ss ($napt) ...\n" );
                }
            } else {
                prt("Set APPTYPE: from [$capt], to [$napt] ($ss).\n" ) if ($dbg);
                $capt = $napt;
            }
        }
    }
    return $capt;
}

# function: get_balance_of_line_bump_offset
# parameters passed:
# $rt = reference to counter
# $tx = balance of file string
sub get_balance_of_line_bump_offset {
    my ($rt,$tx) = @_;
    my $ln = length($tx);
    my $ba = '';
    for (my $k = 0; $k < $ln; $k++) {
        my $ch = substr($tx,$k,1);
        if ($ch eq "\n") {
            $$rt++;
            return $ba;
        }
        $ba .= $ch;
        $$rt++;
    }
    return $ba;
}

sub get_if_props {
    my ($txt) = shift;
    my @lns = split("\n",$txt);
    my $len = scalar @lns;
    my ($t, $ln, $iln, $idir, $tmp);
    my $props = '';
    my $propint = '#\\s+PROP\\s+Intermediate_Dir\\s+"(.+)".*';
    for ($t = 0; $t < $len; $t++) {
        $ln = $lns[$t];
        chomp $ln;
        if ($ln =~ /\!MESSAGE\s+/) {
           # ignore this
        } elsif ($ln =~ /\!IF\s+/) {
            $iln = $ln;
            prtw("WARNING:$t: NOT FIRST FOUND [$ln]\n") if length($props);
            $props = "\n$ln\n\n";
            prt( "LINE $t: [$ln]\n" ) if ($dbg_props);
            $t++;
            for ( ; $t < $len; $t++) {
                $ln = $lns[$t];
                chomp $ln;
                ##if ($ln =~ /#\s+PROP\s+Intermediate_Dir\s+"(.+)".*/) {
                if ($ln =~ /$propint/) {
                    $tmp = $1;  # FIX20110423
                    $idir = $tmp;
                    $idir .= "\\Dupes";
                    $ln =~ s/$tmp/$idir/;
                    $props .= $ln."\n\n";
                    $t++;
                    prt( "ID $t = [$ln]\n" ) if ($dbg_props);
                    last;
                }
            }
        } elsif ($ln =~ /\!ELSEIF\s+/) {
            $props .= "$ln\n\n";
            prt( "LINE $t: [$ln]\n" ) if ($dbg_props);
            $t++;
            for ( ; $t < $len; $t++) {
                $ln = $lns[$t];
                chomp $ln;
                ###if ($ln =~ /#\s+PROP\s+Intermediate_Dir\s+"(.+)".*/) {
                if ($ln =~ /$propint/) {
                    $tmp = $1;  # FIX20110423
                    $idir = $tmp;
                    $idir .= "\\Dupes";
                    $ln =~ s/$tmp/$idir/;
                    $props .= $ln."\n\n";
                    $t++;
                    prt( "ID $t = [$ln]\n" ) if ($dbg_props);
                    last;
                }
            }
        } elsif ($ln =~ /\!ENDIF\s+/) {
            $props .= "$ln\n\n";
            prt( "LINE $t: [$ln]\n" ) if ($dbg_props);
        }
    }
    return $props;
}

sub add_dotrel_if_none {
    my ($fl) = shift;
    # FIX20120311 - return as is if drive + colon + (\\|\/)
    return $fl if ($fl =~ /^\w{1}:(\\|\/)/);
    $fl = ".\\".$fl if ( !($fl =~ /^\./) );
    return $fl;
}

# 2009/10/25 - special provision for RC files - not really duplicates
# 20090910 - this version expects SOURCE file array to be in form 
#           0       1      2      3 +++
#push(@cs, [$sfile, $sgrp, $sflt, $hm] );
# if $hm is 1, then '# PROP Exclude_From_Build 1' is added after source
# DEBUG
# prt( "Processing project [$capname], $captyp, to $of\n" ) if ($dbg & 1);
# prt( "CS: [$fil]\n" ) if ($dbg & 2);
# prt( "HS: [$fil]\n" ) if ($dbg & 2);
# prt("Note: There appears to be NO header files...\n") if ($dbg & 4);
# prt( "Doing substitutions for [$conf] [$type]\n" ) if ($dbg & 8);
# prt( "Subbed $ky with [$nt]\n" ) if ($dbg & 8);
# prt( "Returning function '$msg' for [$conf] of [$type]...\n" ) if ($dbg & 16);
# prt( "[dbg & 32] Got $cnt groups of headers [" ); and prt( "[dbg & 32] Got $cnt groups of sources [" );
sub write_hash_to_DSP3 {
    my ($of, $rh, $dbg) = @_; # say ('tempvcscan.dsp', \%h, 0);
    $g_write_dbg = $dbg;
    my ($val, $msg, $key, $cnt, $srcs, $captyp, $capname);
    my $isok = 1;   # assume it IS ok
    my ($fnam, $ftyp, $i, $fil, $cnt2);
    my ($dupes, $nm, $dr, $ext, @dups, $props);
    my ($hm, $hm_msg, $tmp, $tmp2); # added 20090910
    my ($rsa, $adde,$i2);  # 2009/10/19 - check have SUBSTITUTES for ALL SUBS
    my ($key2, $hvers);
    my %dsrc = ();
    my $def_src_group = 'Source Files'; # added 20090915
    my $def_hdr_group = 'Header Files'; # added 20090915
    my $def_oth_group = 'Other Files'; # added 20090915
    my $src_group = '';
    my $hdr_group = '';
    my $oth_group = '';
    my %src_groups = ();
    my %hdr_groups = ();
    my $flag = 0;
    my @tarr = ();
    my $outsrccnt = 0;
    my $modstg = 'lib dsphdrs:write_hash_to_DSP3';
    my $grplen = 0;
    $tmp2 = '';  # clear this 20100116
    prt("write_hash_to_DSP3() has DEBUG value [$dbg]\n") if ($dbg != 0);
    $key = "CURR_DBGF";
    ${$rh}{$key} = $dbg if (!defined ${$rh}{$key}); # store current DEBUG flag
    $hvers = get_hash_version($rh);
    prt("Hash version: [$hvers] (dbg & 0x8000)\n") if ($dbg & 0x8000);
    $key = 'PROJECT_CFGS';
    my ($rcfgs,$confname,$vcconf);
    if (defined ${$rh}{$key}) {
        $rcfgs = ${$rh}{$key};
        $cnt = scalar @{$rcfgs};
        prt("lib dsphdrs:write_hash_to_DSP3: CONIFG count $cnt\n") if ($dbg);
        for ($i = 0; $i < $cnt; $i++) {
            $confname = ${$rcfgs}[$i][0];
            $vcconf   = ${$rcfgs}[$i][2];
            if (!$vcconf) {
                prtw( "WARNING:$modstg: config reference UNDEFINED! key=[$key]\n" );
                $isok = 0;
                last;
            }
        }
    }
    $key = "PROJECT_FLAGS";
    if (defined ${$rh}{$key}) {
        $flag = ${$rh}{$key}[0];
        $dbg |= ${$rh}{$key}[1];
    }
    $msg = '';
    $key = '-NEW_PROJECT_NAME-';
    if (defined ${$rh}{$key}) {
        $capname = ${$rh}{$key};
    } else {
        $key2 = 'PROJECT_NAME';
        if (defined ${$rh}{$key2}) {
            $capname = ${$rh}{$key2};
            $hvers = 1;
        } else {
            prtw( "WARNING:$modstg: No project NAME in hash... key=[$key]or[$key2]\n" );
            $isok = 0;
        }
    }

    $act_proj_dsphdrs = $capname;

    if ($isok) {
        $key = get_APP_TYPE_string($rh);    # $hvers ? 'PROJECT_TYPE' : 'APP_TYPE';
        if (defined ${$rh}{$key}) {
            $captyp = ${$rh}{$key};
        } else {
            prtw( "WARNING:$modstg: No application type in hash ... key=[$key]\n" );
            $isok = 0;
        }
    }

    if ($isok) {
        prt( "\nProcessing project [$capname], $captyp... [dbg & 1]\n" ) if ($dbg & 1);
        if ($hvers) {
            $key = 'PROJECT_SRCS';
            if (defined ${$rh}{$key}) {
                $srcs = ${$rh}{$key}; # = [@vc_c_sources] or [@dsp_sources];
                $cnt = scalar @{$srcs};
                if ($cnt == 0) {
                    prtw( "WARNING:$modstg: No source files in hash ... key=[$key]\n" );
                    $isok = 0 if (!$add_blank_header_group);
                } else {
                    #                 0      1       2        3
                    #push(@sources, [ $sfil, $group, $filter, $xit, '' ]);
                    my $rsa = ${$srcs}[0];
                    $cnt = scalar @{$rsa};
                    if ($cnt < 4) {
                        prtw( "WARNING:$modstg:1: (ref) Array of a source NOT enough depth (have $cnt, should be 4, or more ...\n" );
                        $isok = 0;
                    }
                }
            } else {
                prtw( "WARNING:$modstg: No SOURCES KEY in hash ... key=[$key]\n" );
                $isok = 0;
            }
        } else {
            $key = 'C_SOURCES';
            if (defined ${$rh}{$key}) {
                $srcs = ${$rh}{$key}; # = [@vc_c_sources];
                $cnt = scalar @{$srcs};
                if ($cnt == 0) {
                    prtw( "WARNING:$modstg: No source files in hash ... key=[$key]\n" );
                    $isok = 0 if (!$add_blank_header_group);
                } else {
                    my $rsa = ${$srcs}[0];
                    $cnt = scalar @{$rsa};
                    if ($cnt < 4) {
                        prtw( "WARNING:$modstg:0: (ref) Array of a source NOT enough depth (have $cnt, should be 4, or more ...\n" );
                        $isok = 0;
                    }
                }
                $key = 'H_SOURCES';
                $cnt2 = 0;
                if (defined ${$rh}{$key}) {
                    $srcs = ${$rh}{$key}; # = [@vc_h_sources];
                    $cnt2 = scalar @{$srcs};
                }
                prt( "with $cnt source, $cnt2 headers files, to output $of [dbg & 1]\n" ) if (($dbg & 1) && $isok);
            } else {
                prtw( "WARNING:$modstg: No SOURCES KEY in hash ... key=[$key]\n" );
                $isok = 0;
            }
        }
    }
    if ($isok) {
        $isok = get_dsp_head_stg($rh, \$msg, $dbg);   # get DSP header text, with substitutions DONE
    }
    ############################################
    ### ABORT IF NOT OK ###
    #######################
    if (! $isok) {
        # 2009/09/22 - since already generated a WARNING on the problem, this should only be ADVICE
        prt( "ADVICE:$modstg: No DSP written... for reason shown...\n" );
        return 0;
    }
    ############################################

    ###############################################
    # scan for DUPLICATE - need SPECIAL treatment
    ###############################################
    $key = ($hvers ? 'PROJECT_SRCS' : 'C_SOURCES');
    prt( "[&64] SCAN THE SOURCES (for dupes) key [$key]\n" ) if ($dbg & 64);
    if (defined ${$rh}{$key}) {
        $srcs = ${$rh}{$key}; # = [@vc_c_sources];
        $cnt = scalar @{$srcs};
    } else {
        if ($add_blank_header_group) {
            prt( "NOTE:$modstg: No SOURCES in hash ... key=[$key]\n" );
        } else {
            prtw( "WARNING:$modstg: No SOURCES in hash ... key=[$key]\n" );
            $isok = 0;
            return 0;
        }
    }
    $dupes = 0;
    @dups = ();     # init duplicates
    %dsrc = ();
    $tmp = '';
    prt( "[&64] NO sources to check\n" ) if (($dbg & 64) && ($cnt == 0));
    for ($i = 0; $i < $cnt; $i++) {
        $i2 = $i + 1;
        # storage done in scanvc.pl - see
        # push(@{$src_ref}, [ $last_src, $fname, $flist,  0,    '' ]); # and PUSH onto SOURCE stack
        #    #                0          1       2        3
        #    push(@sources, [ $sfil,     $group, $filter, $xit, '' ]);
        $fil  = ${$srcs}[$i][0];  # extract C SOURCE file
        $fnam = ${$srcs}[$i][1];  # extract 'folder' (group) name
        $ftyp = ${$srcs}[$i][2];  # its FILTER STRING
        $hm   = ${$srcs}[$i][3];  # and if it has 'main'
        $adde = ${$srcs}[$i][4];  # and any POST stuff
        if (length($fnam) == 0) { # 20110110 - CHANGE BEHAVIOUR - If no 'group', list seperately, after groups
    		prt( "CS:$i2: [$fil] NO GROUP [&2]\n" ) if ($dbg & 2);
            next;  # NOTE: source EXCLUDED from %src_groups{$fnam} hash
        }
		prt( "CS:$i2: [$fil] group:[$fnam] [&2]\n" ) if ($dbg & 2);
        ($nm,$dr,$ext) = fileparse( $fil, qr/\.[^.]*/ );
        $nm = lc($nm);
        # if (is_c_source($fil)) 2009/10/21 - changed to 'extended'
        if (is_c_source_extended($fil)) {
            if (!defined $src_groups{$fnam}) {
                $src_groups{$fnam} = $ftyp;
            }
            if (defined $dsrc{$nm}) {
                if (lc($ext) ne '.rc') {
                    $dupes++;
                    push(@dups,$fil);
                    prt("Check DUPLICATE [$nm] [$fil] vs [$dsrc{$nm}] [&64]\n") if ($dbg & 64);
                }
            } else {
                $dsrc{$nm} = $fil;
            }
            if (length($tmp) == 0) {
                $tmp = $fnam;
            } elsif (length($src_group) == 0) {
                if (($flag & 1)&&( $tmp ne $fnam )) {
                    $src_group = $def_src_group;
                    prt("NOTE: Due to multiple source GROUPS, like [$tmp] and [$fnam], using DEFAULT [$src_group]!\n");
                }
            }
        } else {
            if ($hvers) {
                if ( is_h_source_extended($fil) || is_resource_file($fil) ) {
                    if (!defined $hdr_groups{$fnam}) {
                        $hdr_groups{$fnam} = $ftyp;
                    }
                    if (length($tmp2) == 0) {
                        $tmp2 = $fnam;
                    } elsif (length($hdr_group) == 0) {
                        if (($flag & 1)&&($tmp2 ne $fnam)) {
                            $hdr_group = $def_hdr_group;
                            prt("NOTE: Due to multiple header GROUPS, like [$tmp2] and [$fnam], using DEFAULT [$hdr_group]!\n");
                        }
                    }
                } elsif (is_config_file_like($fil)) {
                    # this is special items like config.h-msvc, simgear-config.h.mc5, etc... IGNORE
                } elsif (is_text_ext_file($fil)) {
                    # text file (*.txt)... IGNORE
                } else {
                    prtw( "WARNING: File [$fil] in H_SOURCES key, BUT does NOT pass is_h_source()! group $fnam. CHECK IT OUT![1]\n" );
                }
            } else {
                prtw( "WARNING: File [$fil] in C_SOURCES key, BUT does NOT pass is_c_source_extended()! group $fnam. CHECK IT OUT!!!\n" );
            }
        }
    }

    @tarr = keys(%src_groups);
    $cnt = scalar @tarr;
    if ($dbg & 32) {
        if ($cnt) {
            prt( "[&32] Got $cnt groups of sources [" );
            foreach $tmp2 (@tarr) { prt( "$tmp2 " ); }
            prt("]\n");
        } else {
            prt( "[&32] Got NO groups of sources!\n" );
        }
    }

    # scan through HEADER sources
    prt( "[&64] SCAN THE HEADERS (setting \%hdr_groups, per GROUP)\n" ) if ($dbg & 64);
    $key = 'H_SOURCES';
    $cnt = 0;
    if (defined ${$rh}{$key}) {
        $srcs = ${$rh}{$key}; # = [@vc_c_sources];
        $cnt = scalar @{$srcs};
        $tmp2 = '';
        for ($i = 0; $i < $cnt; $i++) {
            $i2 = $i + 1;
            $fil  = ${$srcs}[$i][0];  # extract C SOURCE file
            $fnam = ${$srcs}[$i][1];  # extract 'folder' (group) name
            $ftyp = ${$srcs}[$i][2];  # its FILTER STRING
            $hm   = ${$srcs}[$i][3];  # and if it has 'main'
            if (length($fnam) == 0) { # 20110110 - CHANGE BEHAVIOUR - If no 'group', list seperately, after groups
                prt( "HS:$i2: [$fil] NO GROUP [dbg & 2]\n" ) if ($dbg & 2);
                next; # NOTE: source EXCLUDED from %hdr_groups{$fnam} hash
            }
            prt( "HS:$i2: [$fil] group:[$fnam] [dbg & 2]\n" ) if ($dbg & 2);
            ($nm,$dr,$ext) = fileparse( $fil, qr/\.[^.]*/ );
            $nm = lc($nm);
            #if (is_h_source($fil)) # 2009/10/21 - change to 'extended'
            #if (is_h_source_extended($fil)) # 2009/10/25 - include RESOURCE files
            if ( is_h_source_extended($fil) || is_resource_file($fil) ) {
                if (!defined $hdr_groups{$fnam}) {
                    $hdr_groups{$fnam} = $ftyp;
                }
                if (length($tmp2) == 0) {
                    $tmp2 = $fnam;
                } elsif (length($hdr_group) == 0) {
                    if (($flag & 1)&&($tmp2 ne $fnam)) {
                        $hdr_group = $def_hdr_group;
                        prt("NOTE: Due to multiple header GROUPS, like [$tmp2] and [$fnam], using DEFAULT [$hdr_group]!\n");
                    }
                }
            } elsif (is_config_file_like($fil)) {
                # this is special items like config.h-msvc, simgear-config.h.mc5, etc... IGNORE
            } elsif (is_text_ext_file($fil)) {
                    # text file (*.txt)... IGNORE
            } else {
                prtw( "WARNING: File [$fil] in H_SOURCES key, BUT does NOT pass is_h_source_extended()! group $fnam. CHECK IT OUT![2]\n" );
            }
        }
    } else {
        prt("[&64] There are NO HEADERS!\n") if ($dbg & 64);
    }
    @tarr = keys(%hdr_groups);
    $cnt = scalar @tarr;
    if ($cnt && ($dbg & 32)) {
        prt( "[&32] Got $cnt groups of headers [" );
        foreach $tmp2 (@tarr) { prt( "$tmp2 " ); }
        prt("]\n");
    }

    $props = '';
    if (@dups) {
        # we have one or more duplicates
        $props = get_if_props($msg);
        # prt("NOTE: Adding PROP, due to duplicates!\n[$props] CHECHME!\n");
        # prt("NOTE: Adding PROP, due to duplicates! CHECKME!\n");
        prt("[&8000]: Due dups, added PROPS [$props] CHECKME!\n") if ($dbg & 0x8000);
    }

    prt( "[&64] OUTPUT THE SOURCES and HEADERS to [$of]\n" ) if ($dbg & 64);
    if ($flag & 1) {
        # =====================
        # First the C/C++ files
        # =====================
        prt( "Output reduced to TWO groups ONLY [dbg & 32]\n" ) if ($dbg & 32);
        $key = 'C_SOURCES';
        $srcs = ${$rh}{$key}; # = [@vc_c_sources];
        $cnt = scalar @{$srcs};
        for ($i = 0; $i < $cnt; $i++) {
            $fnam = $$srcs[$i][1];   # extract FIRST 'folder' (group) name
            # 20110110 - CHANGE BEHAVIOUR - If no 'group', list seperately, after groups
            last if (length($fnam));
        }
        $grplen = length($fnam);
        $grplen = 0 if ($fnam =~ /^\s$/);
        $grplen = 0 if ($fnam eq '<none>');
        $fnam = $src_group if (length($src_group)); # 20090915 - override with 'Source Files', if MULTIPLE groups
        $ftyp = $$srcs[0][2];   # its FILTER STRING = CAN BE BLANK!!!
        $hm   = $$srcs[0][3];   # and if it has 'main'
        if ($cnt) {
            # 31/05/2010 - do not add a GROUP if the group string is null
            # 10/01/2011 - these are now ouput AFTER all the 'groups'
            if ($grplen) {
                $msg .= '# Begin Group "'.$fnam."\"\n";
                $msg .= "\n";
                $msg .= '# PROP Default_Filter "'.$ftyp."\"\n";
            }
            for ($i = 0; $i < $cnt; $i++) {
                $fil = $$srcs[$i][0];
                $hm  = $$srcs[$i][3]; # and if it has 'main'
                $nm = lc($nm);
                $msg .= "# Begin Source File\n";
                $msg .= "\n";
                ###$msg .= "SOURCE=$fil\n";
                $msg .= "SOURCE=".add_dotrel_if_none($fil)."\n";
                $outsrccnt++ if (length($fil));
                if ( length($props) && is_in_array($fil,@dups) ) {
                    prt( "Added DUPE props for $fil ...\n" ) if ($dbg_props);
                    $msg .= $props;
                }
                $msg .= "# PROP Exclude_From_Build 1\n" if $hm;
                $msg .= ${$srcs}[$i][4];
                $msg .= "# End Source File\n";
            }
            if ($grplen) {
                $msg .= "# End Group\n";
            }
        } else {
            $fnam = 'Source Files';
            $ftyp = get_default_source_filter(); # 'cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;for;f90';
            if ($add_blank_header_group) {
                $msg .= '# Begin Group "'.$fnam."\"\n";
                $msg .= "\n";
                $msg .= '# PROP Default_Filter "'.$ftyp."\"\n";
                $msg .= "# End Group\n";
                prt( "[&4] Added BLANK Group [$fnam], Filter [$ftyp]\n" ) if ($dbg & 4);
            } else {
                prt( "[&4] No BLANK source Group [$fnam], Filter [$ftyp]\n" ) if ($dbg & 4);
            }
        }

        # ==================
        # OUTPUT THE HEADERS
        # ==================
        $key = 'H_SOURCES';
        $cnt = 0;
        if (defined ${$rh}{$key}) {
            $srcs = ${$rh}{$key}; # = [@vc_h_sources];
            $cnt = scalar @{$srcs};
            for ($i = 0; $i < $cnt; $i++) {
                $fnam = ${$srcs}[$i][1];
                last if (length($fnam));
            }
            $fnam = $hdr_group if (length($hdr_group)); # 20090915 - override with 'Header Files', if MULTIPLE groups
            $ftyp = $$srcs[$i][2];
        }
        if ($cnt) {
            if ($fnam ne '<none>') {
                $msg .= '# Begin Group "'.$fnam."\"\n";
                $msg .= "\n";
                $msg .= '# PROP Default_Filter "'.$ftyp."\"\n";
            }
            for ($i = 0; $i < $cnt; $i++) {
                $fil = $$srcs[$i][0];
                $hm  = $$srcs[$i][3];
                $msg .= "# Begin Source File\n";
                $msg .= "\n";
                $msg .= "SOURCE=".add_dotrel_if_none($fil)."\n";
                $outsrccnt++ if (length($fil));
                # $msg .= "# PROP Exclude_From_Build 1\n" if $hm;
                $msg .= "# End Source File\n";
            }
            $msg .= "# End Group\n" if ($fnam ne '<none>');
        } else {
            # my $def_hdr_group = 'Header Files'; # added 20090915
            # my $def_hdr_filt = "h;hpp;hxx;hm;inl;fi;fd";
            prt("[&4] Note:1: There appears to be NO header files...\n") if ($dbg & 4);
            if ($add_blank_header_group) {
                $fnam = 'Header Files';
                $ftyp = get_default_header_filter(); # "h;hpp;hxx;hm;inl;fi;fd";
                $msg .= '# Begin Group "'.$fnam."\"\n";
                $msg .= "\n";
                $msg .= '# PROP Default_Filter "'.$ftyp."\"\n";
                $msg .= "# End Group\n";
            }
        }
    } else {
        my $sgrpcnt = scalar keys(%src_groups);
        my $hgrpcnt = scalar keys(%hdr_groups);
        prt( "[&32] Output SOURCES based on GROUP sg=$sgrpcnt, hg=$hgrpcnt\n" ) if ($dbg & 32);
        $grplen = 0;
        $cnt = 0;
        # output SOURCE files GROUP
        foreach $fnam (keys %src_groups) {
            $ftyp = $src_groups{$fnam};
            $key = ($hvers ? 'PROJECT_SRCS' : 'C_SOURCES');
            prt( "[&4] Group [$fnam], Filter [$ftyp], fnam [$fnam] ky [$key]\n" ) if ($dbg & 4);;
            $grplen = 0;
            if ( length($fnam) && ($fnam ne '<none>')) {
                $msg .= '# Begin Group "'.$fnam."\"\n";
                $msg .= "\n";
                $msg .= '# PROP Default_Filter "'.$ftyp."\"\n";
                $grplen = 1;
            }
            if (defined ${$rh}{$key}) {
                $srcs = ${$rh}{$key}; # = [@vc_c_sources];
                $cnt = scalar @{$srcs};
                for ($i = 0; $i < $cnt; $i++) {
                    $fil = ${$srcs}[$i][0];
                    $hm  = ${$srcs}[$i][3];
                    if ($fnam eq ${$srcs}[$i][1]) {
                        $msg .= "# Begin Source File\n";
                        $msg .= "\n";
                        $msg .= "SOURCE=".add_dotrel_if_none($fil)."\n";
                        $outsrccnt++ if (length($fil));
                        if ( length($props) && is_in_array($fil,@dups) ) {
                            prt( "Added DUPE props for $fil ...\n" ) if ($dbg_props);
                            $msg .= $props;
                        }
                        $msg .= "# PROP Exclude_From_Build 1\n" if $hm;
                        $msg .= ${$srcs}[$i][4];
                        $msg .= "# End Source File\n";
                    }
                }
            }

            $key = 'H_SOURCES';
            if (defined ${$rh}{$key}) {
                $srcs = ${$rh}{$key}; # = [@vc_h_sources];
                $cnt = scalar @{$srcs};
                if ($cnt) {
                    for ($i = 0; $i < $cnt; $i++) {
                        $fil = ${$srcs}[$i][0];
                        $hm  = ${$srcs}[$i][3];
                        if ($fnam eq ${$srcs}[$i][1]) {
                            $msg .= "# Begin Source File\n";
                            $msg .= "\n";
                            $msg .= "SOURCE=".add_dotrel_if_none($fil)."\n";
                            $outsrccnt++ if (length($fil));
                            $msg .= "# PROP Exclude_From_Build 1\n" if $hm;
                            $msg .= "# End Source File\n";
                        }
                    }
                } else {
                    prt("[&4] Note:2: There appears to be NO header files...\n") if ($dbg & 4);
                }
            } else {
                prt("[&4] Note:3: There appears to be NO header files...\n") if ($dbg & 4);
            }
            $msg .= "# End Group\n" if (($fnam ne '<none>') && $grplen);
        }
        if ($sgrpcnt == 0) {
            $fnam = 'Source Files';
            $ftyp = get_default_source_filter(); # 'cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;for;f90';
            if ($add_blank_header_group) {
                $msg .= '# Begin Group "'.$fnam."\"\n";
                $msg .= "\n";
                $msg .= '# PROP Default_Filter "'.$ftyp."\"\n";
                $msg .= "# End Group\n";
                prt( "[&4] Added BLANK Group [$fnam], Filter [$ftyp]\n" ) if ($dbg & 4);
                $sgrpcnt++; # not really, but want header group as well
            } else {
                prt( "[&4] No BLANK source Group [$fnam], Filter [$ftyp]\n" ) if ($dbg & 4);
            }
        }

        # output each HEADER file per group
        foreach $fnam (keys %hdr_groups) {
            $ftyp = $hdr_groups{$fnam};
            prt( "[&4] Group [$fnam], Filter [$ftyp]\n" ) if ($dbg & 4);
            $grplen = 0;
            if (length($fnam) && ($fnam ne '<none>')) {
                $msg .= '# Begin Group "'.$fnam."\"\n";
                $msg .= "\n";
                $msg .= '# PROP Default_Filter "'.$ftyp."\"\n";
                $grplen = 1;
            }
            $key = ($hvers ? 'PROJECT_SRCS' : 'C_SOURCES');
            if (defined ${$rh}{$key}) {
                $srcs = ${$rh}{$key}; # = [@vc_c_sources];
                $cnt = scalar @{$srcs};
                for ($i = 0; $i < $cnt; $i++) {
                    $fil = ${$srcs}[$i][0];
                    $hm  = ${$srcs}[$i][3];
                    if ($fnam eq ${$srcs}[$i][1]) {
                        $msg .= "# Begin Source File\n";
                        $msg .= "\n";
                        $msg .= "SOURCE=".add_dotrel_if_none($fil)."\n";
                        $outsrccnt++ if (length($fil));
                        if ( length($props) && is_in_array($fil,@dups) ) {
                            prt( "Added DUPE props for $fil ...\n" ) if ($dbg_props);
                            $msg .= $props;
                        }
                        $msg .= "# PROP Exclude_From_Build 1\n" if $hm;
                        $msg .= ${$srcs}[$i][4];
                        $msg .= "# End Source File\n";
                    }
                }
            }
            $key = 'H_SOURCES';
            if (defined ${$rh}{$key}) {
                $srcs = ${$rh}{$key}; # = [@vc_h_sources];
                $cnt = scalar @{$srcs};
                for ($i = 0; $i < $cnt; $i++) {
                    $fil = ${$srcs}[$i][0];
                    $hm  = ${$srcs}[$i][3];
                    if ($fnam eq ${$srcs}[$i][1]) {
                        $msg .= "# Begin Source File\n";
                        $msg .= "\n";
                        $msg .= "SOURCE=".add_dotrel_if_none($fil)."\n";
                        $outsrccnt++ if (length($fil));
                        $msg .= "# PROP Exclude_From_Build 1\n" if $hm;
                        $msg .= "# End Source File\n";
                    }
                }
            }
            $msg .= "# End Group\n" if (($fnam ne '<none>') && $grplen);
        }
        if ($sgrpcnt && !$hgrpcnt && $add_blank_header_group) {
            $fnam = 'Header Files';
            $ftyp = get_default_header_filter(); # "h;hpp;hxx;hm;inl;fi;fd";
            $msg .= '# Begin Group "'.$fnam."\"\n";
            $msg .= "\n";
            $msg .= '# PROP Default_Filter "'.$ftyp."\"\n";
            $msg .= "# End Group\n";
            prt( "[&4] Added BLANK Group [$fnam], Filter [$ftyp]\n" ) if ($dbg & 4);
            $fnam = '<none>';
        }
        $cnt = scalar @{$srcs};
        for ($i = 0; $i < $cnt; $i++) {
            $fnam = $$srcs[$i][1];   # extract FIRST 'folder' (group) name,
            if (length($fnam) == 0) {
                # output a SOURCE with NO GROUP
                $fil = ${$srcs}[$i][0];
                $msg .= "# Begin Source File\n";
                $msg .= "\n";
                $msg .= "SOURCE=".add_dotrel_if_none($fil)."\n";
                $outsrccnt++ if (length($fil));
                $msg .= "# End Source File\n";
            }
        }
    }

    if (($outsrccnt == 0) && (!$add_blank_header_group)) {
        $key = 'PROJECT_FILE';
        $fnam = (defined ${$rh}{$key}) ? ${$rh}{$key} : 'UNKNOWN';
        prtw( "ERROR:$modstg NO SOURCES in FILE [$fnam]!!! Aborting...\n" );
        return 0;
    }    
	$msg .= get_dsp_tail();

    rename_2_old_bak_plus($of);

    write2file($msg,$of);

    # prt( "Written [$of] file ...\n" ); # up to caller to ADVISE, if desired...

    return 1;

}

sub get_proj_begin {
	my ($prj, $fil) = @_;
	my $ret = <<EOF;
###############################################################################

Project: "$prj"="$fil" - Package Owner=<4>

Package=<5>
{{{
}}}

Package=<4>
{{{
EOF
	return $ret;
}

sub get_proj_end {
	my $ret = <<EOF;
}}}

EOF
	return $ret;
}

sub get_proj_depends3($$) {
	my ($prj, $rd) = @_;
	my ($pdeps, @arr, $dpn);
    my $msg = '';
	if (defined $$rd{$prj}) {
		$pdeps = $$rd{$prj};
		if (length($pdeps)) {
			@arr = split( /\|/, $pdeps );
			foreach $dpn (@arr) {
				$msg .= "    Begin Project Dependency\n";
				$msg .= "    Project_Dep_Name $dpn\n";
				$msg .= "    End Project Dependency\n";
			}
		}
	} else {
		prtw( "WARNING: Project [$prj] NOT defined in sln_depends!!!\n" );
	}
    return $msg;
}

sub get_bat_head() {
    my $bh = <<EOF;
\@echo Copy temporary DSP files, to final destination
\@echo CONTINUE? Ctrl+C to abort...
\@pause
\@echo Have you BACKED UP the current DSP files?
\@pause
EOF
    return $bh;
}

# 2010/03/01 - use an appended '.old', or '.bak', like the new rename_2_old_bak_plus
sub add_bat_copy($$$) {
    my ($s,$d,$i) = @_;
    my ($nm,$dir,$ext) = fileparse($d,qr/\.[^.]*/);
    my $dc = "DOCPY$i";
    my $uo = "USEOLD$i";
    my $ub = "USEBAK$i";
    my $old = $nm.$ext.".old";
    my $bak = $nm.$ext.".bak";
    my $fbak = $dir.$bak;
    my $fold = $dir.$old;
    # 25/04/2010 - ensure all DOS paths
    $s = path_u2d($s);
    $d = path_u2d($d);
    $old = path_u2d($old);
    $bak = path_u2d($bak);
    $fold = path_u2d($fold);
    $fbak = path_u2d($fbak);
    # 20100128 - Due to xmlrpc having ++ in name, cloak each in inverted commas
    $s = add_quotes($s);
    $d = add_quotes($d);
    $old = add_quotes($old);
    $bak = add_quotes($bak);
    $fold = add_quotes($fold);
    $fbak = add_quotes($fbak);
    prt("Copy src=[$s], to dest=[$d]\n") if ($g_write_dbg & 1);
    my $t = "\@if NOT EXIST $d goto $dc\n";
    $t .= "\@if NOT EXIST $fold goto $uo\n";
    $t .= "\@if NOT EXIST $fbak goto $ub\n";
    $t .= "del $fbak\n";
    $t .= ":$ub\n";
    $t .= "ren $d $bak\n";
    $t .= "\@goto $dc\n";
    $t .= ":$uo\n";
    $t .= "ren $d $old\n";
    $t .= ":$dc\n";
    $t .= "copy $s $d\n";
    return $t;
}

# ===============================================
# OUTPUT DSW FILE
# passed an output DSW file, and
# a multilayered solution hash reference
# = out file
# = solution hash
# = batch file name
# = debug flag, for DSW write
# 28/10/2011 - Fix like tempcopydsp.bat to COPY the DSW
# ===============================================
sub write_proj_DSW3($$$$) {
    my ($of,$slnhr,$bat,$g_write_dbg) = @_;
    my ($i,$pcnt,$prj,$key,$fdsp,$odsp,$btxt);
    my ($projs,$ref_deps,$results,$msg,$slnf);
    my ($nm,$dir,$ext,$destdsw,$dirs);
    my ($fil); # 2010/05/01 
    $key = 'PROJECT_FILE';
    if (!defined ${$slnhr}{$key}) {
        prtw("WARNING: No key [$key] in SOLUTION HASH! So NO DSW written\n" );
        return 1;
    }

    $slnf = ${$slnhr}{$key};
    ($nm,$dirs,$ext) = fileparse( $slnf, qr/\.[^.]*/ );
    ($msg,$dir,$ext) = fileparse( $of, qr/\.[^.]*/ );
    $dir = cwd() if ($dir =~ /^\.(\\|\/)$/);   # fill out directory, if local
    $dir .= "\\" if (!($dir =~ /(\\|\/)$/)); # 25/04/2010 - add trailing sep, if none
    $destdsw = $dirs.$nm.".dsw";
    prt("From sln=[$slnf],\n got DSW=[$destdsw]\n"); 
    
    $key = 'PROJECT_LIST';
    if (!defined ${$slnhr}{$key}) {
        prtw("WARNING: No key [$key] in SOLUTION HASH! So NO DSW written\n" );
        return 1;
    }
    #                       0     1     2
    # push(@project_list, [ $tmp, $dsp, $out ]);
    # $k = 'PROJECT_LIST';
    # ${$rsh}{$k} = [@project_list];  # keep project list, including output DSP file, if written
    $projs = ${$slnhr}{$key};

	$pcnt = scalar @{$projs};   # process projects, one by one
    if ($pcnt == 0) {
        prtw("WARNING: No COUNT in key [$key] in SOLUTION HASH! So NO DSW written!\n");
        return 1;
    }

    $key = 'DEPENDS';
    # $hash{'DEPENDS'} = { %sln_depends  };
    if (!defined ${$slnhr}{$key}) {
        prtw("WARNING: No key [$key] in SOLUTION HASH! So NO DSW written\n" );
        return 1;
    }
    $ref_deps = ${$slnhr}{$key};

    $key = 'RESULTS';
    if (! defined ${$slnhr}{$key}) {
        prt("ERROR: No key [$key]! Has ");
        foreach my $k (keys %{$slnhr}) {
            prt("$k ");
        }
        prt("CHECK ME\n");
        prtw("WARNING: No key [$key] in SOLUTION HASH! So NO DSW written!\n");
        return 1;
    }

    # push(@results, [$k, $nm, $captyp, $relpf, $fdspf]); # stored in 'RESULTS'
    $results = ${$slnhr}{$key};

    # Appear to have all the information
    # ##################################

	# process the projects
    $btxt = "";
	$msg = get_dsw_head();
	for ($i = 0; $i < $pcnt; $i++) {
        # for batch file
        $odsp = ${$projs}[$i][2];   # get OUTPUT (temp) DSP file
        prt("[dbg & 1] write_proj_DSW3: 'temp'     DSP file [$odsp]\n") if ($g_write_dbg & 1);
        $fdsp = ${$results}[$i][4]; # destination DSP file absolute
        prt("[dbg & 2] write_proj_DSW3: 'absolute' DSP file [$fdsp]\n") if ($g_write_dbg & 2);
        $btxt .= add_bat_copy( $odsp, $fdsp, $i );
        # ==============
		$prj = ${$projs}[$i][0];
		#$fil = ${$projs}[$i][1];
        #                 0   1    2        3       4  - see vcproj05.pl
        # push(@results, [$k, $nm, $captyp, $relpf, $fdspf]); # stored in 'RESULTS'
        $fil = ${$results}[$i][3];  # destination DSP file relative
		$msg .= get_proj_begin( $prj, $fil );
        # ========================
		# add any DEPENDENCIES NOW
		# fg_add_proj_depends( $DSW, $prj, $ref_deps );
        $msg .= get_proj_depends3( $prj, $ref_deps );
		$msg .= get_proj_end();
	}

	$msg .= get_dsw_tail();

    rename_2_old_bak_plus($of);

    write2file($msg,$of);

    prt( "Written [$of] file ...\n" );
    if ($pcnt && length($bat)) {
        # write an update batch file
        $btxt = get_bat_head().$btxt;
        #                   tempDSW realDSW   count
        $btxt .= add_bat_copy( $of, $destdsw, $i );
        $btxt .= "\@echo All done\n";
        write2file($btxt,$bat);
        prt( "Written [$bat] file ...\n" );
        #prt( "$btxt\n" );
    }
    return 0;   # success

}

sub get_simple_DSW_txt($$) {
    my ($prj,$fil) = @_;
	my $msg = get_dsw_head();
	#for ($i = 0; $i < $pcnt; $i++) {
		#$prj = ${$projs}[$i][0];
        #$fil = ${$results}[$i][3];  # destination DSP file relative
	$msg .= get_proj_begin( $prj, $fil );
        #$msg .= get_proj_depends3( $prj, $ref_deps );
	$msg .= get_proj_end();
	#}
	$msg .= get_dsw_tail();
    #write2file($msg,$of);
    return $msg;
}


1;
# eof - lib_dsphdrs.pl