#!/perl -w
# NAME: dswlist.pl
# AIM: Given a MSVC6 DSW files, show the LIST of DSP files it references
# or given a FOLDER, search for ALL .DSW files, and show .DSP list.
# 01/06/2012 - Fixfor 'SUB' should be 'SUBTRACT'
# 29/05/2012 - Output a cmake list of sources found
# 20090913 - add command line parsing, and make it more conform to scan of vcproj
# substitution variables
# -NEW_PROJECT_NAME-"   = name of the project
# -NEW_OUTD_(REL|DBG)-  = PROP Output_Dir ????
# -NEW_INTER_(REL|DBG)- = PROP Intermediate_Dir ????
# ADD CPP with 
# -NEW_RT_(REL|DBG)-    = RUNTIME, like /MT /MD, /MTd, etc
# -NEW_INCS_(REL|DBG)-  = INCLUDE DIRECTORIES, like /I ".."
# -NEW_DEFS_(REL|DBG)-  = DEFINES, like /D "FGFS"
# ADD LINK32 (for console, app, DLL) with
# -NEW_LIBS_(REL|DBG)-  = Additional libraries for the link
# -NEW_OUT_(REL|DBG)-   = link output, like /out:"StaticRelease\libpng.lib
# -NEW_POST_(REL|DBG)-  = POST build - description and commands, TAB separated
# ADD LIB32 (for static library) with
# -NEW_OUT_(REL|DBG)-   =  OUTPUT static library
# 22/04/2007 - geoff mclane - http://geoffmclane.com/mperl/samples/index.htm
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
#use Cwd qw(chdir abs_path);
use Cwd;
my $os = $^O;
my $perl_dir = '/home/geoff/bin';
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_cmake.pl' or die "Unable to load 'lib_cmake.pl'! Check location and \@INC content.\n";
# log file stuff
our ($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);

# user variables
my $VERS = "0.0.3 2012/05/29";
#my $VERS = "0.0.2 2012/05/21";
#my $VERS = "0.0.1 22/09/2009";
my $load_log = 0;   # load log file at END
my $in_file = '';
my $verbosity = 0;
my $debug_on = 0;
my $def_file = 'def_file';
my $recursive = 1;
my $show_rel = 0;
my $fix_rela = 1;
my $show_srcs = 1;	# read the DSP, and show the SOURCES contained
my $curr_dbg =0;
my $spl_exclude_unix = 0;
my $output_cmake_sources = 1;
my $lib_type = 'OFF';
my $drop_dsp_rel = 1;

# debug items
my $dbg1 = 0;	# show Project line during collection
my $dbg2 = 0;	# show during collection
my $dbg3 = 0;	# show during folder collection
my $dbg4 = 0;	# show Processing 
my $dbg05 = 0;   # show fix_rel details...

my $base_name = ''; # base name of source DSP file
my $base_dir = "C:\\Projects\\hb\\a52dec\\vc++";    # base directory
my $def_input = $base_dir."\\a52dec.dsw";
##my $base_dir = "C:\\FG\\FGCOM";
##my $base_dir = "C:\\FG\\10\\freeglut"; #\\progs\\demos\\";
##my $base_dir = "C:\\Projects\\UltraVNC-102-Src\\UltraVNC";
#my $def_input = $base_dir."demos.dsw"; # adjust this to the file you want parsed
##my $def_input = $base_dir; #."\\freeglut.dsw"; # adjust this to the file you want parsed

# program global variables
my @files = ();
my @file_list = ();
my $pcnt = 0;
my $wmsg = '';
my $dswcnt = 0;
#-- get current directory
my $pwd = cwd();
my $targ_dir = '';
my $cmake_all = '';
my ($proj_title,$proj_dir,$proj_ext);
my $done_cmake_head = 0;

my $appt_console_stg  = 'Console Application';
my $appt_windows_stg  = 'Application';
my $appt_dynalib_stg  = 'Dynamic-Link Library';
my $appt_statlib_stg  = 'Static Library';
my $appt_utility_stg  = 'Utility';

my %master_hash = ();

### program variables
my @warnings = ();
my $cwd = cwd();
my $out_file = '';

sub VERB1() { return $verbosity >= 1; }
sub VERB2() { return $verbosity >= 2; }
sub VERB5() { return $verbosity >= 5; }
sub VERB9() { return $verbosity >= 9; }

sub show_warnings($) {
    my ($val) = @_;
    if (@warnings) {
        prt( "\nGot ".scalar @warnings." WARNINGS...\n" );
        foreach my $itm (@warnings) {
           prt("$itm\n");
        }
        prt("\n");
    } else {
        prt( "\nNo warnings issued.\n\n" ) if (VERB9());
    }
}

sub pgm_exit($$) {
    my ($val,$msg) = @_;
    if (length($msg)) {
        $msg .= "\n" if (!($msg =~ /\n$/));
        prt($msg);
    }
    show_warnings($val);
    close_log($outfile,$load_log);
    exit($val);
}


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

# "Win32 (x86) Dynamic-Link Library" 0x0102
# TARGTYPE "Win32 (x86) Application" 0x0101
sub get_app_type_stg_local($) {
    my ($stg) = shift;
    if ($stg =~ /Static\s+Library/) {
        return $appt_statlib_stg;
    } elsif ($stg =~ /Console\s+Application/) {
        return $appt_console_stg;
    } elsif ($stg =~ /Dynamic-Link\s+Library/) {
        return $appt_dynalib_stg;
    } elsif ($stg =~ /Application/) {
        return $appt_windows_stg;
    } elsif ($stg =~ /External\s+Target/) {
        return $appt_utility_stg; # FIX20120521 = 'Utility';
    }
    return "Unresolved [$stg] FIXME in $pgmname!!!";
}

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

# split_space - space_split - 
# like split(/\s/,$txt), but honour double inverted commas
# also accept and split '"something"/>', but ONLY if in the tail
sub space_split2($) {
	my ($txt) = shift;
	my $len = length($txt);
	my ($k, $ch, $tag, $incomm, $k2, $nch);
	my @arr = ();
	$tag = '';
	$incomm = 0;
	for ($k = 0; $k < $len; $k++) {
		$ch = substr($txt,$k,1);
        $k2 = $k + 1;
        $nch = ($k2 < $len) ? substr($txt,$k2,1) : "";
		if ($incomm) {
			$incomm = 0 if ($ch eq '"');
			$tag .= $ch;
		} elsif ($ch =~ /\s/) { # any spacey char
         push(@arr, $tag) if (length($tag));
			$tag = '';
		} elsif (($ch =~ /\//)&&($nch eq '>')) { # 04/10/2008, but only if before '>' 24/09/2008 add this as well
			push(@arr, $tag) if (length($tag));
			$tag = $ch; # restart tag with this character
		} else {
			$tag .= $ch;
			$incomm = 1 if ($ch eq '"');
		}
	}
	push(@arr, $tag) if (length($tag));
	return \@arr;
}

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

sub get_cmake_header() {
    my $cmake_root = "\n# root CMakeLists.txt, generated by $pgmname, on ";
    $cmake_root .= lu_get_YYYYMMDD_hhmmss(time())."\n\n";
    $cmake_root .= "cmake_minimum_required( VERSION 2.8.8 )\n\n";
    $cmake_root .= "project( $proj_title )\n\n";
    $cmake_root .= "# The version number.\n";
    # TODO ***TBD*** This VERSION stuff MUST BE FIXED ONE DAY
    # =======================================================
    $cmake_root .= "set( ${proj_title}_MAJOR 3 )\n";
    $cmake_root .= "set( ${proj_title}_MINOR 0 )\n";
    $cmake_root .= "set( ${proj_title}_POINT 0 )\n\n";
    $cmake_root .= add_options_block($lib_type);
    $cmake_root .= get_definitions_block();
    return $cmake_root;

}

sub show_hash_results2($$) {
    my ($dbg, $rh) = @_;
    my ($key, $val, $arr, $itm, $icnt, $i, $msg, $len, $src, $grp, $nm, $dir, $ext);
    my ($iia, $wmsg, $tmp, $ics, $mlen, $slen);
    my ($captyp,$cname,$dsp,$rf,$gotsdsp,$dsp_name,$dsp_dir,$ff,$ffp);
    my ($rel_path,$sn,$sd);
    my %srcs = ();
    my @fsrcs = ();
    my @results = ();
    $gotsdsp = 0;
    $key = '-NEW_PROJECT_NAME-';
    if (defined ${$rh}{$key}) {
       $cname = ${$rh}{$key};
    } else {
       $cname = "Unknown - [$key] NOT SET!"; 
    }
    $msg = " name: $cname";
    $msg .= " [key=$key]" if ($dbg & 8);
    $key = 'SOURCE_FILE';
    if (defined ${$rh}{$key}) {
       $dsp = ${$rh}{$key};     # path to DSP file, with source
       $rf = remove_base_path($dsp, $base_dir);
       $gotsdsp = 1;
       ($dsp_name,$dsp_dir) = fileparse($dsp);
    } else {
       $dsp = "Unknown - [$key] NOT SET!"; 
       $rf = $dsp;
    }

    #$msg .= " DSP [$dsp]";
    $msg .= " DSP [$dsp_name] in [$dsp_dir]";
    prt("$msg\n");
    $key = 'APP_TYPE';
    if (defined ${$rh}{$key}) {
       $captyp = ${$rh}{$key};
    } else {
       $captyp = "Unknown - [$key] NOT SET!"; 
    }
    $msg = " type: $captyp";
    $msg .= " [key=$key]" if ($dbg & 8);
    prt( "$msg\n" );
    if (length($targ_dir) && $gotsdsp) {
        $rel_path = get_relative_path4($targ_dir,$dsp_dir);
        prt("Got relative path [$rel_path]\n");
    } else {
        $gotsdsp = 0;
    }

    # some rough cmake stuff
    my $var1 = "${cname}_SOURCES";
    my $var2 = "${cname}_HEADERS";
    my $cmake = '';
    my @arr = ();
    my $tot_srcs = 0;
    my $tot_ok = 0;
    my $tot_hdrs = 0;
    my $tot_hok = 0;
    my $indent = 3;
    my $def_max_line1 = 70;
    my $def_max_line2 = 85;
    my $max_line = $def_max_line1;
    my $src_cnt = 0;
    my $hdr_cnt = 0;
    if ($output_cmake_sources) {
        if (defined ${$rh}{'C_SOURCES'}) {
            $val = ${$rh}{'C_SOURCES'};
            $icnt = scalar @{$val};
            @arr = ();
            $tot_srcs += $icnt;
            $src_cnt = $icnt;
            if ($icnt) {
                for ($i = 0; $i < $icnt; $i++) {
                    # $src = path_d2u(${$val}[$i][0]);
                    $src = ${$val}[$i][0];
                    next if ($spl_exclude_unix && ($src =~ /(\\|\/)unix(\\|\/)/));
                    if ($gotsdsp) {
                        $ff = $dsp_dir.$src;
                        $ffp = fix_rel_path3($ff,'show_hash_results2');
                        ($sn,$sd) = fileparse($ffp);
                        $rel_path = get_relative_path4($sd,$targ_dir);
                        $src = path_d2u($rel_path.$sn);
                    }
                    push(@arr,$src);
                }
            }
            $icnt = scalar @arr;
            $tot_ok = $icnt;
            if ($icnt) {
                ###$cmake .= "\n";
                $cmake .= "set( $var1 ";
                $msg = '';
                $max_line = $def_max_line1;
                foreach $src (sort @arr) {
                    # TODO ***TBD*** need to get the actual source path correct, but for the moment
                    # assume it is from a DSP in msvc, so drop the leading '..\'
                    $src =~ s/^\.\.(\\|\/)// if ($drop_dsp_rel);
                    $msg .= "$src ";
                    if (length($msg) > $max_line) {
                        $cmake .= "$msg\n";
                        $msg = " " x $indent;
                        $max_line = $def_max_line2;
                    }
                }
                if (length($msg) > $indent) {
                    $cmake .= $msg;
                } else {
                    $cmake =~ s/\n$//;  # remove trailing new line
                }
                $cmake .= ")\n\n";
            }
        }
        if (defined ${$rh}{'H_SOURCES'}) {
            $val = ${$rh}{'H_SOURCES'};
            $icnt = scalar @{$val};
            $tot_hdrs = $icnt;
            $hdr_cnt = $icnt;
            @arr = ();
            if ($icnt) {
                for ($i = 0; $i < $icnt; $i++) {
                    $src = path_d2u(${$val}[$i][0]);
                    next if ($spl_exclude_unix && ($src =~ /(\\|\/)unix(\\|\/)/));
                    if ($gotsdsp) {
                        $ff = $dsp_dir.$src;
                        $ffp = fix_rel_path3($ff,'show_hash_results2');
                        ($sn,$sd) = fileparse($ffp);
                        $rel_path = get_relative_path4($sd,$targ_dir);
                        $src = path_d2u($rel_path.$sn);
                    }
                    push(@arr,$src);
                }
            }
            $icnt = scalar @arr;
            $tot_hok = $icnt;
            if ($icnt) {
                $cmake .= "set( $var2 ";
                $msg = '';
                $max_line = $def_max_line1;
                foreach $src (sort @arr) {
                    # TODO ***TBD*** need to get the actual source path correct, but for the moment
                    # assume it is from a DSP in msvc, so drop the leading '..\'
                    $src =~ s/^\.\.(\\|\/)// if ($drop_dsp_rel);
                    $msg .= "$src ";
                    if (length($msg) > $max_line) {
                        $cmake .= "$msg\n";
                        $msg = " " x $indent;
                        $max_line = $def_max_line2;
                    }
                }
                if (length($msg) > $indent) {
                    $cmake .= $msg;
                } else {
                    $cmake =~ s/\n$//;  # remove trailing new line
                }
                $cmake .= ")\n\n";
            }
        }
        if (length($cmake)) {
            prt("\n# Adding $tot_ok of $tot_srcs sources for $cname ($tot_hok of $tot_hdrs hdrs)\n");
            prt($cmake);
            # my $appt_console_stg  = 'Console Application';
            # my $appt_windows_stg  = 'Application';
            # my $appt_dynalib_stg  = 'Dynamic-Link Library';
            # my $appt_statlib_stg  = 'Static Library';
            $cmake_all = get_cmake_header() if (!$done_cmake_head);
            $done_cmake_head = 1;
            if (($captyp eq $appt_console_stg) || ($captyp eq $appt_windows_stg)) {
                $msg = "\nadd_executable( $cname ";
                $msg .= "WIN32 " if ($captyp eq $appt_windows_stg);
                $msg .= "\${$var1} " if ($src_cnt);
                $msg .= "\${$var2} " if ($hdr_cnt);
                $msg .= ")\n";
                $cmake .= "$msg";
                $cmake .= "set_target_properties( $cname PROPERTIES DEBUG_POSTFIX d )\n";
                $cmake .= "install(TARGETS $cname DESTINATION bin )\n";
                $cmake .= "install(FILES \${$var2} DESTINATION include )\n" if ($hdr_cnt);
                $cmake .= "# May need to add library dependence\n";
                $cmake .= "# target_link_libraries( $cname \${some_lib_LIST} )\n";

            } elsif (($captyp eq $appt_dynalib_stg) || ($captyp eq $appt_statlib_stg)) {
                $msg = "\nadd_library( $cname \${LIB_TYPE} ";
                $msg .= "\${$var1} " if ($src_cnt);
                $msg .= "\${$var2} " if ($hdr_cnt);
                $msg .= ")\n";
                $cmake .= "$msg\n";
                # # add the install targets
                # ==============================================
                # DEAL WITH LIBRARY INSTALL
                # ==============================================
                # install (TARGETS Hello DESTINATION lib)
                # install (FILES ${HELLO_HDRS} DESTINATION include)
                # $msg = "install(TARGETS $pname DESTINATION $ilibp)\n";
                $msg = "\n# deal with install \n";
                $msg .= "install( TARGETS $cname\n";
                $msg .= "         RUNTIME DESTINATION bin\n";
                $msg .= "         LIBRARY DESTINATION lib\n";
                $msg .= "         ARCHIVE DESTINATION lib )\n";
                $cmake .= $msg;
                $cmake .= "install(FILES \${$var2} DESTINATION include )\n" if ($hdr_cnt);
                $cmake .= "# If a SHARED library, may need to add library dependence\n";
                $cmake .= "# target_link_libraries( $cname \${some_lib_LIST} )\n";
            }
            $cmake_all .= "# Project $cname, type $captyp\n";
            $cmake_all .= "# Adding $tot_ok of $tot_srcs sources ($tot_hok of $tot_hdrs hdrs)\n";
            $cmake_all .= "$cmake\n";
        } else {
            prt("UGH! NO sources for $cname\n");
        }
    }
    # ========================================

    push(@results, [$cname, $captyp, 0, 0]);
    foreach $key (sort keys(%{$rh}) ) {
        $val = $$rh{$key};
        $len = length($val);
        if (($key eq 'C_SOURCES')||($key eq 'H_SOURCES')) {
            # SHOW of sources and headers in DSP/VCPROJ file
            $icnt = scalar @{$val};
            $msg = "$key count $icnt sources ...";
            prt( "$msg ($icnt)\n" );
            $msg = '';
            if ($key eq 'C_SOURCES') {
                $results[0][3] = $icnt;
            } elsif ($key eq 'H_SOURCES') {
                $results[0][4] = $icnt;
            }
            # push(@vc_c_sources,[$adddefs, $fname, $flist]);
            $mlen = 0;
            # get MINIMUM length
            for ($i = 0; $i < $icnt; $i++) {
                $src = $$val[$i][0];
                $slen = length($src);
                $mlen = $slen if ($slen > $mlen);
            }
            for ($i = 0; $i < $icnt; $i++) {
                $src = $$val[$i][0];
                ($nm,$dir,$ext) = fileparse( $src, qr/\.[^.]*/ );
                $nm = lc($nm);
                $iia = defined $srcs{$nm}; 
                $ics = is_c_source_local($src);
                if ($iia) {
                    if ($ics) {
                        $wmsg = "WARNING: Duplicate [$src]! ";
                        prtw( "$wmsg\n" );
                    }
                } else {
                    $srcs{$nm} = 1 if ($ics);
                    push(@fsrcs,$src);
                }
                $grp = $$val[$i][1];
                $msg .= "\n" if length($msg);
                $tmp = $src;
                $tmp .= ' ' while (length($tmp) < $mlen);
                $msg .= $tmp;
                $msg .= " [$grp]";
                ###$msg .= ' '.$$val[$i][2];
            }
            prt( "$msg\n" ) if ($dbg & 4);
        } elsif (($key eq 'APP_TYPE')||($key eq '-NEW_PROJECT_NAME-')) {
            # now shown at the beginning
            # prt( "Application Type [$val]\n" );
        } elsif ($key eq 'SOURCE_FILE') {
            # extracted and shown
        } else {
            # SHOW of other things extracted from the PROJECT file (vcproj or dsp)
            $arr = space_split2($val);
            $icnt = scalar @{$arr};
            $msg = "$key = [";
            $msg .= "$val]";
            $msg .= " $icnt items...";
            prt( "$msg\n" ) if !($dbg & 1); # will show ALL if dbg & 1
            $msg = '';
            if ($dbg & 1) {
                prt( "[dbg1]: Show of $icnt items ...\n" );
                for ($i = 0; $i < $icnt; $i++) {
                    $itm = ${$arr}[$i];
                    if ($itm =~ /^\/(D|I)/) {
                        # /I or /D
                        $i++;
                        if ($i < $icnt) {
                            $src = ${$arr}[$i];
                        } else {
                            $src = 'OUT OF ITEMS - CHECK ME!';
                        }
                        prt( "$itm $src\n" );
                    } else {
                        # other ...
                        prt( "$itm\n" );
                    }
                }
            }
        }
    }
    return \@results;
}


sub show_master_hash($) {
    my ($mhr) = @_;
    my ($key,$hr,$cnt,$ra);
    $cnt = scalar keys(%{$mhr});
    prt( "Show of $cnt items...\n" );
    my %h = ();
    foreach $key (keys %{$mhr}) {
        prt( "Project [$key]\n" );
        $hr = ${$mhr}{$key};    # extract hash for project
        $ra = show_hash_results2($curr_dbg,$hr);    # ret a results hash
        $h{$key} = $ra;
    }
    return \%h;
}


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

sub process_directory { ## $in_folder
	my ($inf, $lev) = @_;
	my $rcnt = 0;
	my ($DH);
	if ( !opendir($DH, $inf) ) {
		prt( "ERROR: Unable to OPEN FOLDER [$inf] ... $! ... \n" );
		return $rcnt;
	}
	my @files = readdir($DH);
	closedir $DH;
	my $fcnt = scalar @files;
	prt( "Have $fcnt to process from $inf ...\n" ) if ($dbg3);
	foreach my $file (@files) {
		if (($file eq '.') || ($file eq '..')) {
			next;
		}
		my $ff = $inf . "\\" . $file;
		if (-d $ff) {
			if ($recursive) {
				###if (in_excl_list($file)) {
				###	push(@folders, sub_main($ff));
				###}
				$rcnt += process_directory( $ff, $lev + 1 );
			}
		} else {
			# is a FILE
			if ( is_my_file($file) ) {
				push(@file_list, $ff);
				$rcnt++;
			}
		}
	}
	return $rcnt;
}

sub is_my_file {
	my ($f) = shift;
	my ($nm,$dir,$ext) = fileparse( $f, qr/\.[^.]*/ );
	if (lc($ext) eq '.dsw') {
		return 1;
	}
	return 0;
}

# ENSURE '/' is used throughout string.
sub dos_to_unix {
	my ($du) = shift;
	$du =~ s/\\/\//g;
	return $du;
}

sub remove_base_dir($) {
    my $ff = shift;
    my $len1 = length($ff);
    my $bd = $base_dir;
    my $len2 = length($bd);
    if ($len1 > $len2) {
        return substr($ff,$len2);
    } else {
        return $ff;
    }
}

sub scan_dsp_lines($$) {
    my ($ra,$dsp) = @_;
	##my @dsplines = @_;
	##my $lncnt = scalar @dsplines;
	my $lncnt = scalar @{$ra};
	my @dspsrcs = ();
    my $projname = '';
    my $projtype = '';
    my $group = '';
    my $filter = '';
    my ($tmp, $key, $line, $tline, $lnn, $len, $i, $ff);
    my %dsp_hash = ();
    my $hr = \%dsp_hash;
    my $mhr = \%master_hash;
    my @c_sources = ();
    my @h_sources = ();
    my @o_sources = (); # others
    my ($dsp_name,$dsp_dir) = fileparse($dsp);
	###prt( "File contains $lncnt lines ...\n" );
    # push(@c_sources,[$src, $group, $filter, 0]);
    $lnn = 0;
    for ($i = 0; $i < $lncnt; $i++) {
	    #$line = $dsplines[$i];
	    $line = ${$ra}[$i];
		chomp $line;
        $lnn++;
        $tline = trim_all($line);
        $len = length($tline);
        next if ($len == 0);
        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_local($tmp);
            prt( "Project Type [$projtype]\n" );
            $key = 'APP_TYPE';
            ${$hr}{$key} = $projtype;
	    } elsif ( $line =~ /^#\s+Begin\s+Group\s+(.*)/ ) {
            $group = strip_quotes2($1);
			prt( "Begin Group  [$group]\n" ) if (VERB9());
        } elsif ( $line =~ /^#\s+PROP\s+Default_Filter\s+"(.*)".*$/ ) {
            $filter = $1;
            prt( "Filter       [$filter]\n" ) if (VERB9());
		} 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($line);
			$line =~ s/^\.\///; # remove any leading './';
            $ff = remove_base_dir($dsp_dir.$line);
            $ff = path_d2u($ff);
			if (($line =~ /\.cxx$/i) || ($line =~ /\.c$/i) || ($line =~ /\.cpp$/i)) {
				#push(@dspsrcs, $line);
                #push(@c_sources, [$line, $group, $filter]);
				push(@dspsrcs, $ff);
                push(@c_sources, [$ff, $group, $filter]);
			} elsif ( ($line =~ /\.hxx$/i) || ($line =~ /\.h$/i) || ($line =~ /\.hpp$/i) ) {
				#push(@dspsrcs, $line);
                #push(@h_sources, [$line, $group, $filter]);
				push(@dspsrcs, $ff);
                push(@h_sources, [$ff, $group, $filter]);
            } else {
                if ( $line =~ /^\$\(/ ) {
                    # need maco expansion
                    prt("$lnn: CHECK 'MACRO' SOURCE [$line]\n"); # if (VERB9());
                } else {
                    #push(@o_sources, [$line, $group, $filter]);
                    push(@o_sources, [$ff, $group, $filter]);
                    prt("$lnn: CHECK 'other' SOURCE [$line]\n") if (VERB9());
				}
			}
        } elsif ($line =~ /^!MESSAGE/) {
            # ignore MESSAGE lines
        } elsif ($line =~ /^!IF\s+/) {
        } elsif ($line =~ /^!ELSEIF\s+/) {
        } elsif ($line =~ /^!ENDIF/) {
        } elsif ($line =~ /^\#/) {
            $line =~ s/^\#\s+//;
            # hash line
            if ($line =~ /^Microsoft Developer Studio Generated Build File, Format Version 6.00/) {
                # ignore
            } elsif ($line =~ /^\*\*\s+DO\s+NOT\s+EDIT\s+\*\*/) {
                # ignore
            } elsif ($line =~ /^Begin Project/) {
                # ignore
            } elsif ($line =~ /^PROP\s+/) {
                # deal with various PROPERTIES
            } elsif ($line =~ /^ADD\s+/) {
            } elsif ($line =~ /^SUB\s+/) {
            } elsif ($line =~ /^SUBTRACT\s+/) { # FIX20120601
            } elsif ($line =~ /^Name\s+/) {
            } elsif ($line =~ /^Begin\s+/) {
                # # Begin Custom Build - Creating apr.h from apr.hw
                # InputPath=.\include\apr.hw
                # ".\include\apr.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
                # 	type .\include\apr.hw > .\include\apr.h
                # # End Custom Build
                if ($line =~ /Begin\s+Custom\s+Build/) {
                    # stay and EAT until END
                    $i++;
                    for (; $i < $lncnt; $i++) {
                        #$line = $dsplines[$i];
                        $line = ${$ra}[$i];
		                chomp $line;
                        $lnn++;
                        $tline = trim_all($line);
                        $len = length($tline);
                        next if ($len == 0);
                        last if ($line =~ /^\#\s+End\s+Custom\s+Build/);
                    }
                } elsif ($line =~ /^Begin\s+Special\s+Build\s+Tool/) {
                    # TargetPath=Release\libapr-2.dll
                    # SOURCE="$(InputPath)"
                    # PostBuild_Desc=Embed .manifest
                    # PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
                    # # End Special Build Tool
                    # stay and EAT until END
                    $i++;
                    for (; $i < $lncnt; $i++) {
                        #$line = $dsplines[$i];
                        $line = ${$ra}[$i];
		                chomp $line;
                        $lnn++;
                        $tline = trim_all($line);
                        $len = length($tline);
                        next if ($len == 0);
                        last if ($line =~ /^\#\s+End\s+Special\s+Build/);
                    }
                }
            } elsif ($line =~ /^End\s+/) {
            } else {
                prt("CHECK UNdecoded HASH line [$line]\n");
            }
        } elsif ($line =~ /^\w+=/) {
            # macro
		} else {
            prt("CHECK UNdecoded line [$line]\n");
        }
	}
    $key = 'C_SOURCES';
    ${$hr}{$key} = [@c_sources];
    $key = 'H_SOURCES';
    ${$hr}{$key} = [@h_sources];
    $key = 'SOURCE_FILE';
    ${$hr}{$key} = $dsp;
    $key = $projname;
    ${$mhr}{$key} = $hr;
	$lncnt = scalar @dspsrcs;
	prt( "File contains $lncnt SOURCES ...\n" );
	return \@dspsrcs;
}

sub remove_base_path($$) {
    my ($ln, $bs) = @_;
    my $len1 = length($ln);
    my $len2 = length($bs);
    if ($len1 < $len2) {
        return $ln;
    }
    my ($i,$c1,$c2);
    for ($i = 0; $i < $len2; $i++) {
        $c1 = lc(substr($ln,$i,1));
        $c2 = lc(substr($bs,$i,1));
        if ($c1 ne $c2) {
            return $ln;
        }
    }
    return substr($ln,$len2);
}

sub show_files {
	my (@fils) = @_;
	my $cnt = 0;
    my ($line,$ff);
	foreach $line (@fils) {
		$cnt++;
        $ff = File::Spec->rel2abs($line);
		###my $rp = substr($line, length($base_dir));
		my $rp = remove_base_path($line, $base_dir);
		if ($show_rel) {
			if ($fix_rela) {
				$rp = fix_rel($rp);
			}
			prt( "$cnt: [$rp]\n" );
		} else {
			#prt( "$cnt: [$line]\n" );
			prt( "$cnt: [$ff]\n" );
		}
		if ($show_srcs) {
			# read the DSP, and enumerate the SOURCES
			if (open(INF, "<$line")) {
				my @lns = <INF>;
				close INF;
				my $lncnt = scalar @lns;
				prt( "$line contains $lncnt lines to process ...\n" ) if (VERB9());
				my $srcs = scan_dsp_lines(\@lns,$ff);
                if (VERB5()) {
                    foreach my $src (@{$srcs}) {
                        prt( "   $src\n" );
                    }
                }
			} else {
				prt( "WARNING: Failed to open [$line] ...\n" );
			}
		}
	}
}

sub process_dsw {
	my ($fl) = shift;
	my @fls = load_in_file( $fl );
	prt( "\nFrom $fl got ".scalar @fls." DSP files...\n" );
	show_files( @fls );
}

sub load_in_file {
	my ($inf) = shift;
	my @infs = ();
    my ($cnt,$nm,$dir,$proj,@arr,$dsp,$ok,$line);
	###prt( "Processing $inf ...\n" );
	if ( !open INF, "<$inf" ) {
		$wmsg = "WARNING: Can not OPEN [$inf] ... $! ...";
		prt( "$wmsg\n" );
		push(@warnings, $wmsg);
		return @infs;
	}
	my @lines = <INF>;
	close INF;
	$cnt = scalar @lines;
	($nm,$dir) = fileparse($inf);
	prt( "\nProcessing $cnt lines from [$nm] in [$dir] ...\n" ) if ($dbg4);
	$cnt = 0;
	foreach $line (@lines) {
		$line = trim_all($line);
		###if ($line =~ /Project:\s+\"{1}(.+)\"{1}/) {
		if ($line =~ /Project:\s+(.+)\s+-\s+Package\s+Owner=/) {
			$cnt++;
			prt( "$cnt Project [$1] ...\n" ) if ($dbg1);
			$proj = $1;
			$proj =~ s/\"//g;
			@arr = split(/=/, $proj);
			if (scalar @arr >= 2) {
				$pcnt++;
				$dsp = $dir . $arr[1];
				$ok = 'NOT FOUND';
				if ( -f $dsp) {
					$ok = 'ok';
				}
				prt( "$pcnt name=[".$arr[0]."], file=[".$arr[1]."] ...$ok \n" ) if ($dbg2);
				push(@infs, $dsp);
			}
		}
	}
	$cnt = scalar @infs;
	prt( "Got $cnt files from $inf ...\n" ) if ($dbg4);
	return @infs;
}

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

sub fix_rel {
	my ($path) = shift;
	# my @a = split(/\\/, $path);
    $path = unix_2_dos($path);
	my @a = split(/\\/, $path);
	my $npath = '';
	my $max = scalar @a;
	my @na = ();
    my ($i,$p,$pt);
    prt( "[dbg05] fix_rel:[$path], split to $max parts...\n" ) if ($dbg05);
	for ($i = 0; $i < $max; $i++) {
		$p = $a[$i];
		if ($p eq '.') {
			# ignore this
		} elsif ($p eq '..') {
			if (@na) {
				pop @na;	# discard previous
			} else {
				prt( "WARNING: Got relative .. without previous!!!\n" );
			}
		} else {
            prt( "[dbg05] adding [$p]\n" ) if ($dbg05);
			push(@na,$p);
		}
	}
	foreach $pt (@na) {
		$npath .= "\\" if length($npath);
		$npath .= $pt;
	}
    prt( "[dbg05] returning [$npath]\n" ) if ($dbg05);
	return $npath;
}

sub die_if_no_file {
	my ($fil) = shift;
	if ((length($fil) == 0) || !( -f $fil )) {
		if (length($fil)) {
			mydie( "ERROR: Can NOT locate [$fil] ... $! ...\n" );
		} else {
			mydie( "ERROR: Must give a DSW input file ...\n" );
		}
	}
}

sub process_build_file($) {
    my $file = shift;
    if ($file =~ /\.dsp/i) {
    	my @fl = ();
        push(@fl,$file);
    	show_files(@fl);
    } else {
        process_dsw($file); # assume it is a DSW
    }
}

sub process_in_file($) {
    my ($inf) = @_;
    my $ok = 0;
    if ( -f $inf ) {
        my $ff = File::Spec->rel2abs($inf);
        ($base_name,$base_dir) = fileparse($ff);
        process_build_file($inf);
        $ok = 1;
#    } elsif ( -d $inf ) {
#        process_directory( $inf, 0 );
#        $dswcnt = scalar @file_list;
#        prt( "Found $dswcnt DSW files to process ...\n" );
#        foreach $line (@file_list) {
#            process_dsw( $line );
#        }
#        $ok = 1;
    } else {
        prtw("WARNING: [$in_file] is NOT a file!\n");
    }
    if ($ok) {
        prt( "\nShow of MASTER HASH\n" );
        my $rr = show_master_hash(\%master_hash);
        my ($nm,$tp,$ln,$min,$ct);
        $min = 0;
        foreach my $k (keys %{$rr}) {
            my $v = ${$rr}{$k};
            $nm = ${$v}[0][0];
            $tp = ${$v}[0][1];
            $ln = length($nm);
            $min = $ln if ($ln > $min);
        }
        $ct = 0;
        foreach my $k (keys %{$rr}) {
            my $v = ${$rr}{$k};
            $nm = ${$v}[0][0];
            $tp = ${$v}[0][1];
            $nm .= ' ' while (length($nm) < $min);
            $ct++;
            # The $k is the same as the name
            prt( "$ct: Project: $nm Type: $tp\n" );
        }
    }
}

### MAIN ###
parse_args(@ARGV);
process_in_file($in_file);
if (length($cmake_all)) {
    if (length($out_file)) {
        $cmake_all .= "\n# end of generated root CMakeLists.txt file for $proj_title, by $pgmname\n\n";
        write2file($cmake_all,$out_file);
        prt("Bare cmake written to [$out_file]\n");
    } else {
        prt("No output file so bare cmake not written. User -o file-name\n");
    }
}
pgm_exit(0,"");

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

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

sub parse_args {
    my (@av) = @_;
    my ($arg,$sarg);
    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();
                pgm_exit(0,"Help exit(0)");
            } elsif ($sarg =~ /^v/) {
                if ($sarg =~ /^v.*(\d+)$/) {
                    $verbosity = $1;
                } else {
                    while ($sarg =~ /^v/) {
                        $verbosity++;
                        $sarg = substr($sarg,1);
                    }
                }
                prt("Verbosity = $verbosity\n") if (VERB1());
            } elsif ($sarg =~ /^l/) {
                $load_log = 1;
                prt("Set to load log at end.\n") if (VERB1());
            } elsif ($sarg =~ /^o/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $out_file = $sarg;
                prt("Set out file to [$out_file].\n") if (VERB1());
            } elsif ($sarg =~ /^t/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $targ_dir = File::Spec->rel2abs($sarg);
                prt("Set target directory to [$targ_dir].\n") if (VERB1());
            } else {
                pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n");
            }
        } else {
            $in_file = File::Spec->rel2abs($arg);
            ($proj_title,$proj_dir,$proj_ext) = fileparse($in_file , qr/\.[^.]*/ );
            prt("Set input to [$in_file]\n") if (VERB1());
        }
        shift @av;
    }

    if ((length($in_file) ==  0) && $debug_on) {
        $in_file = $def_file;
        prt("Set DEFAULT input to [$in_file]\n");
    }
    if (length($in_file) ==  0) {
        pgm_exit(1,"ERROR: No input files found in command!\n");
    }
    if (! -f $in_file) {
        pgm_exit(1,"ERROR: Unable to find in file [$in_file]! Check name, location...\n");
    }
}

sub give_help {
    prt("$pgmname: version $VERS\n");
    prt("Usage: $pgmname [options] in-file\n");
    prt("Options:\n");
    prt(" --help  (-h or -?) = This help, and exit 0.\n");
    prt(" --verb[n]     (-v) = Bump [or set] verbosity. def=$verbosity\n");
    prt(" --load        (-l) = Load LOG at end. ($outfile)\n");
    prt(" --targ dir    (-t) = Target directory for CMakeLists.txt. Implies fix source el to this.\n");
    prt(" --out <file>  (-o) = Write output to this file.\n");
}

# eof - dswlist.pl
