#!/usr/bin/perl -w
# NAME: chkcmake.pl
# AIM: Given an input folder, or input file, follow the CMakeLists.txt files
# 26/04/2012 - If there exists a folder 'CMakeModules' load ALL from here - 
# It SEEMS these are AVAILABLE whether a specific 'INCLUDE(modulename)' or NOT is given!
# 22/04/2012 - Some improvements...
# 2012-01-29 - Run in Ubuntu and Windows
# 25/10/2011 geoff mclane http://geoffair.net/mperl
use strict;
use warnings;
use File::Basename;  # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] )
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_cmakeread.pl' or die "Unable to load 'lib_cmakeread.pl' Check paths in \@INC...\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.4 2012-04-26";  # load ALL 'CMakeModule' in advance, it it EXISTS
# $VERS = "0.0.3 2012-04-22";
# $VERS = "0.0.2 2012-01-29";
# $VERS = "0.0.1 2011-10-25";
my $inc_all_cmake = 1; # 26/04/2012 - If there exists a folder 'CMakeModules' load ALL from here...
# double UGH - maybe should load, on demand, ALL modules in install [share\cmake-2.8\Modules]!!!
my $load_installed_cmake = 0;   # load ALL 200-300 ...cmake files from the install

my $load_log = 0;
my $in_file = '';
my $verbosity = 0;
my $show_mess = 0;
my $show_installs = 0;
my $show_sets = 0;
my $show_options = 0;
my $show_lists = 0;
my $show_binaries = 0;
### my $show_if_tags = 0;
my $show_all = 0;

my $warn_set_overwrites = 0;
my $warn_macro_overwrites = 0;
my $warn_not_expaned = 0;
my $warn_unexpanded_fn = 0;
my $warn_substitution_for = 0;  # WARNING: Substitution for [
sub set_warn_all() { 
    $warn_set_overwrites = 1; $warn_macro_overwrites = 1; $warn_not_expaned = 1;
    $warn_unexpanded_fn = 1; $warn_substitution_for = 1;
}

### program variables
# main action
sub process_cmake_list($);  # ref to file lines setup, and these are process
sub process_in_file($$);    # process a cmake script file

my $total_file_lines = 0;
my $total_files = 0;

my @warnings = ();
my $warnings_avoided = 0;

my $cwd = cwd();

my $total_options = 0;
my $total_lines = 0;
my $total_bytes = 0;

my ($cmake_moddir); # = $d."CMakeModules"

my @cmake_actions = qw(CMAKE_MINIMUM_REQUIRED CMAKE_POLICY INCLUDE PROJECT 
 SET FILE STRING MESSAGE FIND_LIBRARY LIST FIND_PACKAGE
 EXECUTE_PROCESS OPTION GET_FILENAME_COMPONENT CHECK_INCLUDE_FILE
 CHECK_CXX_SOURCE_COMPILES INCLUDE_DIRECTORIES
 ADD_DEFINITIONS CHECK_FUNCTION_EXISTS CONFIGURE_FILE 
 ADD_SUBDIRECTORY INSTALL CONFIGURE_FILE
 ADD_CUSTOM_TARGET TARGET_LINK_LIBRARIES
 CHECK_INCLUDE_FILES CHECK_TYPE_SIZE ADD_LIBRARY 
 SET_TARGET_PROPERTIES ADD_EXECUTABLE GET_TARGET_PROPERTY ADD_CUSTOM_COMMAND
 ADD_DEPENDENCIES FLTK_WRAP_UI GET_PROPERTY MARK_AS_ADVANCED
 GET_CMAKE_PROPERTY SET_PROPERTY TRY_COMPILE FIND_PATH FIND_PROGRAM
 FIND_PACKAGE_MESSAGE TRY_RUN FIND_PACKAGE_HANDLE_STANDARD_ARGS UNSET CMAKE_DETERMINE_COMPILER_ID
 EXEC_PROGRAM _FIND_ECLIPSE_VERSION PROCESSORCOUNT GETDEFAULTWINDOWSPREFIXBASE BREAK SET_COMPILE_FLAGS_VAR
 SET_LINK_FLAGS_VAR PRINTTESTCOMPILERSTATUS CMAKE_DETERMINE_COMPILER_ABI CRT_VERSION MATH CHECK_VERSION
 CMAKE_DETERMINE_COMPILER_ID_VENDOR
 ENABLE_TESTING SITE_NAME GET_VS_VERSION_STRING BUILD_COMMAND DEFINE_PROPERTY _BOOST_CHECK_SPELLING
 _BOOST_COMPILER_DUMPVERSION _BOOST_PREPEND_LIST_WITH_THREADAPI _BOOST_SWAP_WITH_REALPATH
 _BOOST_MARK_COMPONENTS_FOUND LOAD_CACHE FIND_FILE
 _GTEST_APPEND_DEBUGS _GTEST_FIND_LIBRARY _GTK2_FIND_INCLUDE_DIR _GTK2_FIND_LIBRARY
 _GTK2_GET_VERSION _PROTOBUF_FIND_LIBRARIES _QT4_QUERY_QMAKE ERROR_MESSAGE
 FIND_IMAGEMAGICK_API FIND_IMAGEMAGICK_EXE GET_DIRECTORY_PROPERTY INTERROGATE_MPI_COMPILER
 IS_FILE_EXECUTABLE LINK_DIRECTORIES LINK_LIBRARIES OSG_FIND_LIBRARY OSG_FIND_PATH
 OSG_MARK_AS_ADVANCED PKG_CHECK_MODULES SANITYCHECKSDKANDDEPLOYTARGET SEPARATE_ARGUMENTS
 SET_DIRECTORY_PROPERTIES TRY_REGULAR_COMPILER
 MACRO ENDMACRO 
 FOREACH ENDFOREACH 
 FUNCTION ENDFUNCTION
 RETURN ENABLE_LANGUAGE
);

sub func_nul { };

my %cpack_actions = (
    'CPACK_SET_IF_NOT_SET' => \&func_nul,   # act [(CPACK_PACKAGE_NAME "${CMAKE_PROJECT_NAME}")] 
    'CPACK_CHECK_FILE_EXISTS' => \&func_nul,  # act [("${CPACK_PACKAGE_DESCRIPTION_FILE}" "package description")]
    'CPACK_OPTIONAL_APPEND' => \&func_nul,  # act [(CPACK_GENERATOR CPACK_BINARY_BUNDLE Bundle)]
    'CPACK_ENCODE_VARIABLES' => \&func_nul, # act [()]
    'CPACK_PARSE_ARGUMENTS' => \&func_nul,  # act [(CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME} "DISPLAY_NAME;DESCRIPTION;GROUP;DEPENDS;INSTALL_TYPES;ARCHIVE_FILE" "HIDDEN;REQUIRED;DISABLED;DOWNLOADED" ${ARGN} )]
    'CPACK_APPEND_STRING_VARIABLE_SET_COMMAND' => \&func_nul,   # act [( CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME}_DISPLAY_NAME CPACK_ADDCOMP_STR)]
    'CPACK_APPEND_VARIABLE_SET_COMMAND' => \&func_nul,  # act [( CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME}_GROUP CPACK_ADDCOMP_STR)]
    'CPACK_APPEND_OPTION_SET_COMMAND' => \&func_nul    # act [( CPACK_COMPONENT_${CPACK_ADDCOMP_UNAME}_HIDDEN CPACK_ADDCOMP_STR)]
);

my %unlisted_tags = ();
my %load_failed = ();
my %macros_not_found = ();
my %missing_list_action = ();
my %unexpanded_macros = ();
my %undefined_regex_match = ();
my %uncased_file_action = ();
my %uncased_string_action = ();
my %not_found_in_set_or_list = ();
my %action_is_uncased = ();
my %undefined_strip_item = ();
my %undefined_strip_item_macro = ();

# DEBUG
my $debug_on = 1;
my $def_file = 'C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\Platform\Windows-cl.cmake';
#my $def_file = 'C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\GNUInstallDirs.cmake';
#my $def_file = 'C:\FGCVS\flightgear\source';
# $def_file = 'C:\Projects\freeglut';
# $def_file = 'C:\FG\30\flightgear';

my $dbg_01 = 0;
my $dbg_02 = 0; # show prt("Action [$tag] $action\n") if ($dbg_02 || VERB9());
my $dbg_03 = 0;
my $dbg_04 = 0; # MACRO and macro expansion
my $dbg_05 = 0; # ADD_SUBDIRECTORY actions
my $dbg_06 = 0; # prt("[v9] Collected FOREACH MACRO($item1) args $argc [$args], from $bgn to $end\n[$macro]\n") if (VERB9() || $dbg_06);
my $dbg_07 = 0; # show save and restore stack
my $dbg_08 = 0; # prt("[v9] Loading 'INCLUDE' [$ff]\n");
my $add_searched_debug = 0;

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 || $warnings_avoided) {
        prt( "\nGot ".scalar @warnings." WARNINGS...\n" );
        prt("With $warnings_avoided duplicates or disabled avoided\n") if ($warnings_avoided);
        foreach my $itm (@warnings) {
           prt("$itm\n");
        }
        prt("\n");
    } else {
        ### prt( "\nNo warnings issued.\n\n" );
    }
}

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);
}

sub path_form($) {
    my $rp = shift;  # = \$ff
    if ($os =~ /win/i) {
        ${$rp} = path_u2d(${$rp});
    } else {
        ${$rp} = path_d2u(${$rp});
    }
}

sub clean_action($) {
    my $act = shift;
    $act =~ s/^\(//;
    $act =~ s/\)$//;
    return $act;
}

sub extract_var($) {
    my $var = shift;
    # ${FLIGHTGEAR_VERSION}
    # ${versionFile}
    if ($var =~ /^\$\{(.+)\}$/) {
        return $1;
    }
    return $var;
}

my @tmpifstk = ();
my $save_counter = 0;
my @save_stack = ();
sub save_ref_hash_stack($) {
    my $rh = shift;
    $save_stack[$save_counter][0] = ${$rh}{'PROJECT_SOURCE_DIR'};
    $save_stack[$save_counter][1] = ${$rh}{"BASE_FILE"};
    $save_stack[$save_counter][2] = ${$rh}{"BASE_LINES"};
    $save_stack[$save_counter][3] = ${$rh}{'CURR_IF_STACK'};
    $save_stack[$save_counter][4] = ${$rh}{'CURR_IN_IF'};   # zero IF counter
    $save_stack[$save_counter][5] = ${$rh}{"ACT_LCNT"};
    $save_stack[$save_counter][6] = ${$rh}{'ACT_I'};
    $save_stack[$save_counter][7] = ${$rh}{'CURR_LINE_SOURCE'}; # = "File [$inf]" or 'Macro $mnm';
    @tmpifstk = ();
    ${$rh}{'CURR_IF_STACK'} = \@tmpifstk;
    my $svif = $save_stack[$save_counter][1];
    prt("save_ref_hash_stack: [$svif]\n") if ($dbg_07);
    $save_counter++;
}
sub restore_ref_hash_stack($) {
    my $rh = shift;
    if ($save_counter) {
        $save_counter--;
    } else {
        pgm_exit(1,"restore_ref_hash_stack call out of turn!\n");
    }
    ${$rh}{'PROJECT_SOURCE_DIR'} = $save_stack[$save_counter][0];
    ${$rh}{"BASE_FILE"}          = $save_stack[$save_counter][1];
    ${$rh}{"BASE_LINES"}         = $save_stack[$save_counter][2];
    ${$rh}{'CURR_IF_STACK'}      = $save_stack[$save_counter][3];
    ${$rh}{'CURR_IN_IF'}         = $save_stack[$save_counter][4];
    ${$rh}{"ACT_LCNT"}           = $save_stack[$save_counter][5];
    ${$rh}{'ACT_I'}              = $save_stack[$save_counter][6];
    ${$rh}{'CURR_LINE_SOURCE'}   = $save_stack[$save_counter][7]; # = "File [$inf]" or 'Macro $mnm';
    my $svif = $save_stack[$save_counter][1];
    prt("restore_ref_hash_stack: [$svif]\n") if ($dbg_07);
}

sub fix_directory($) {
    my $rd = shift;
    if (!(${$rd} =~ /(\\|\/)$/)) {
        ${$rd} .= $PATH_SEP;
    }
}

sub get_cmake_install_paths_ra() {
    my $envstg = $ENV{"PATH"};
    my ($path,$up,@arr,$ver,$tmp,$cnt);
    my @paths = ();
    #prt("ENV(PATH) = $envstg\n");
    @arr = split(";",$envstg);
    $cnt = scalar @arr;
    #prt("Got $cnt paths to test...\n");
    foreach $path (@arr) {
        #prt("Testing [$path]\n");
        next if (length($path) == 0);
        if ($path =~ /CMake/i) {
            $path =~ s/(\\|\/)bin$//i;
            $ver = '';
            if (-d $path) {
                #$up = path_d2u($path);
                $up = path_u2d($path);
                prt("Adding path [$up]\n");
                @arr = split(/(\/|\\)+/,$up);
                foreach $tmp (@arr) {
                    if ($tmp =~ /cmake/i) {
                        $ver = $tmp;
                        last;
                    }
                }
                push(@paths,[$up,$ver]);
            } else {
                prtw("WARNING: Not a valid DIRECTORY! [$path]\n");
            }
        } else {
            # prt("Not 'CMake' related!\n");
        }
    }
    return \@paths;
}

sub scan_cmake_path($$);
sub scan_cmake_path($$) {
    my ($path,$rh) = @_;
    # my @installed_files = ();
    # ${$rh}{'CMAKE_INSTALLED_FILES'} = \@installed_files;
    my $rif = ${$rh}{'CMAKE_INSTALLED_FILES'};
    my @dirs = ();
    if (opendir(DIR,$path)) {
        my @files = readdir(DIR);
        closedir(DIR);
        my ($file,$ff);
        fix_directory(\$path);
        foreach $file (@files) {
            next if (($file eq '.')||($file eq '..'));
            $ff = $path.$file;
            if (-l $ff) {
                # ignore links
            } elsif (-d $ff) {
                push(@dirs,$ff);
            } elsif (-f $ff) {
                if ($file =~ /\.cmake$/i) {
                    #prt("adding file [$ff]\n");
                    push(@{$rif},$ff);
                }
            } else {
                prtw("WARNING: Unable to find file [$ff]!\n");
            }
        }
        foreach $path (@dirs) {
            scan_cmake_path($path,$rh);
        }
    } else {
        prtw("WARNING: Unable to open directory [$path]!\n");
    }
}


sub scan_cmake_install_paths($) {
    my $rh = shift;
    my $rval = 0;
    my $rp = get_cmake_install_paths_ra();
    my $cnt = scalar @{$rp};
    if ($cnt) {
        my ($i,$path,$ver);
        for ($i = 0; $i < $cnt; $i++) {
            $path = ${$rp}[$i][0];
            scan_cmake_path($path,$rh);
        }
    } else {
        prtw("WARNING: Found NO Cmake install paths!\n");
        $rval = 1;
    }
    return $rval;
}

my @list_operations = qw( LENGTH GET APPEND FIND INSERT REMOVE_ITEM REMOVE_AT REMOVE_DUPLICATES REVERSE SORT );
sub is_in_list_operations($) {
    my $act = shift;
    my ($itm);
    foreach $itm (@list_operations) {
        return 1 if ($act eq $itm);
    }
    return 0;
}
my @add_library_adjectives = qw ( SHARED STATIC MODULE UNKNOWN IMPORTED GLOBAL EXCLUDE_FROM_ALL );
sub is_add_library_adjective($) {
    my $act = shift;
    my ($itm);
    foreach $itm (@add_library_adjectives) {
        return 1 if ($act eq $itm);
    }
    return 0;
}
my @add_executable_adjectives = qw ( WIN32 MACOSX_BUNDLE EXCLUDE_FROM_ALL IMPORTED GLOBAL );
sub is_add_executable_adjective($) {
    my $act = shift;
    my ($itm);
    foreach $itm (@add_executable_adjectives) {
        return 1 if ($act eq $itm);
    }
    return 0;
}

sub string_to_directives_ra($) {
    my ($txt) = shift;
    my $len = length($txt);
    my ($i,$ch,$inquot,$pc,$pc2);
    $inquot = 0;
    my @braces = ();
    my @lines = (); # accumulate lines
    my $ntxt = '';  # accumulate a line
    $ch = '';
    $pc = '';
    for ($i = 0; $i < $len; $i++) {
        $pc2 = $pc;
        $pc = $ch;
        $ch = substr($txt,$i,1);
        if ($inquot) {
            if ($ch =~ /\s/) {
                $ntxt .= ' ' if ($ntxt =~ /\S$/g);
            } else {
                $ntxt .= $ch;
                $inquot = 0 if ( ($ch eq '"') && ( ($pc ne "\\") || ( ($pc eq "\\")&&($pc2 eq "\\") ) ) );
            }
        } elsif ($ch eq '"') {
            $ntxt .= $ch;
            $inquot = 1; # if ($pc ne "\\");
        } elsif ($ch eq '(') {
            $ntxt .= $ch;
            push(@braces,$ch);
        } elsif ($ch eq ')') {
            $ntxt .= $ch;
            if (@braces) {
                pop @braces;
            }
            if (!@braces) {
                push(@lines,$ntxt) if (length($ntxt));
                $ntxt = ""; # start again
            }
        } elsif ($ch eq "\n") {
            # no add of this, but a space if length and not a trailing space
            $ntxt .= ' ' if (length($ntxt) && ($ntxt =~ /\S$/g));
        } elsif ($ch eq '#') {
            # eat comments
            for (; $i < $len; $i++) {
                $ch = substr($txt,$i,1);
                last if ($ch eq "\n");
            }
        } else {
            if ($ch =~ /\s/) {
                $ntxt .= ' ' if ($ntxt =~ /\S$/g);
            } else {
                $ntxt .= $ch;
            }
        }
    }
    $ntxt =~ s/\s$//g;
    push(@lines,$ntxt) if (length($ntxt));
    return \@lines;
}

sub not_same_ref_array($$) {
    my ($ra1,$ra2) = @_;    # $pia,\@list)) {
    my $ac1 = scalar @{$ra1};
    my $ac2 = scalar @{$ra2};
    return 1 if ($ac1 != $ac2);
    my ($i);
    my @arr1 = sort @{$ra1};
    my @arr2 = sort @{$ra2};
    for ($i = 0; $i < $ac1; $i++) {
        return 1 if ($arr1[$i] ne $arr2[$i]);
    }
    return 0;
}


sub process_action($) {
    my $rh = shift;
    my ($tag,$action,$lnn,$tline,$val);
    my ($item1,$item2,$item3);
    my $bfile = ${$rh}{"BASE_FILE"};
    $tag =  ${$rh}{"ACT_TAG"};
    $action = ${$rh}{"ACT_ACT"};
    $lnn = ${$rh}{"ACT_LNN"};
    $tline = ${$rh}{"ACT_LINE"};
    my $rdefs = ${$rh}{'CMAKE_DEFINES'}; # = \%defines;
    my $rlines = ${$rh}{"BASE_LINES"};

    my ($msg,$act,@arr,$act2,$file,$var,$act3,$ff);
    my ($nm,$dr);
    my ($i,$i2,$cnt,$tmp,$ok,$ra,$key);
    prt("[v9] Action [$tag] $action\n") if ($dbg_02 || VERB9());
    $act = clean_action($action);
    @arr = space_split($act);
    $cnt = scalar @arr;

    if ($tag eq "CMAKE_MINIMUM_REQUIRED") {
    } elsif ($tag eq 'CMAKE_POLICY') {
    } elsif ($tag eq 'ADD_CUSTOM_TARGET') {
    } elsif ($tag eq 'ADD_CUSTOM_COMMAND') {
    } elsif ($tag eq 'ADD_DEPENDENCIES') {
    } elsif ($tag eq 'ADD_DEFINITIONS') {
    } elsif ($tag eq 'ADD_SUBDIRECTORY') {
        # This should be processed for a CMakeLists.txt file here
        #     my @subdirs = ();
        #    $hash{'CMAKE_SUBDIRS'} = \@subdirs;
        $msg = '';
        my $rsubs = ${$rh}{'CMAKE_SUBDIRS'};
        my ($bname,$bdir) = fileparse($bfile);
        for ($i = 0; $i < $cnt; $i++) {
            $item1 = $arr[$i];
            $tmp = $bdir.$item1.$PATH_SEP."CMakeLists.txt";
            if (-f $tmp) {
                push(@{$rsubs},[$item1,$tmp]);
                $msg = ", file [$tmp]";
            } else {
                $msg = "WARNING: Can NOT locate [$tmp]! base [$bfile]";
                prtw("$msg\n");
            }
        }
        prt("[v9] ADD_SUBDIRECTORY $i $action $msg\n") if ($dbg_05 || VERB9());
    } elsif ($tag eq 'ADD_LIBRARY') {
        # add_library(&lt;name&gt; &lt;SHARED|STATIC|MODULE|UNKNOWN&gt; IMPORTED [GLOBAL])
        # add_library(&lt;name&gt; [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
        # my %libs_sources = ();
        # $hash{'CMAKE_LIBS'} = \%libs_sources;
        my $rlibs = ${$rh}{'CMAKE_LIBS'};
        $item1 = $arr[0];   # get LIBRARY name
        if ($cnt >= 2) {
            my @lsrcs = ();
            for ($i = 1; $i < $cnt; $i++) {
                $item2 = $arr[$i];   # get adjective or first source
                next if (is_add_library_adjective($item2));
                push(@lsrcs,$item2);
            }
            if (defined ${$rlibs}{$item1}) {
                prtw("WARNING: add_library entry IS DUPLICATED\n");
            } else {
                ${$rlibs}{$item1} = \@lsrcs;
            }
        } else {
            prtw("WARNING: BAD add_library entry [$tag] $action\n");
        }
    } elsif ($tag eq 'ADD_EXECUTABLE') {
        # add_executable(&lt;name&gt; [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
        # add_executable(&lt;name&gt; IMPORTED [GLOBAL])
        # my %exes_sources = ();
        # $hash{'CMAKE_EXES'} = \%exes_sources;
        my $rexes = ${$rh}{'CMAKE_EXES'};
        $item1 = $arr[0];   # get LIBRARY name
        if ($cnt >= 2) {
            my @esrcs = ();
            for ($i = 1; $i < $cnt; $i++) {
                $item2 = $arr[$i];   # get adjective or first source
                next if (is_add_executable_adjective($item2));
                push(@esrcs,$item2);
            }
            if (defined ${$rexes}{$item1}) {
                prtw("WARNING: add_executable entry IS DUPLICATED\n");
            } else {
                ${$rexes}{$item1} = \@esrcs;
            }
        } else {
            prtw("WARNING: BAD add_executable entry [$tag] $action\n");
        }

    } elsif ($tag eq "INCLUDE") {
        # This maybe a FILE to be loaded like include(FlightGearComponent) = <root>\CMakeModules\FlightGearComponent.cmake file
        # or it can be a 'standard' CMakeMoudles file from the installation 
        # See Directory of C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\Platform
        # =====================================================================================================================
        $item1 = $arr[0];   # get INCLUDE base name
        $item2 = ${$rh}{'PRIMARY_CMAKE_DIR'};
        $ff = $item2;
        $ff .= "CMakeModules".$PATH_SEP;
        $ff .= "$item1.cmake";
        $ok = 0;
        my @tries = ();
        if (-f $ff) {
            $ok = 1;
        } else {
            push(@tries,$ff);
            my $envstg = $ENV{"PATH"};
            my @parr = split(/;+/,$envstg);
            foreach $tmp (@parr) {
                next if (!($tmp =~ /cmake/i));
                $tmp =~ s/(\\|\/)bin/\\/;
                $ff = $tmp."share\\cmake-2.8\\Modules\\$item1.cmake";
                if (-f $ff) {
                    $ok = 1;
                    last;
                }
                push(@tries,$ff);
                $ff = $tmp."Modules\\$item1.cmake";
                if (-f $ff) {
                    $ok = 1;
                    last;
                }
                push(@tries,$ff);
                $ff = $tmp."share\\cmake-2.8\\Modules\\Platform\\$item1.cmake";
                if (-f $ff) {
                    $ok = 1;
                    last;
                }
                push(@tries,$ff);
            }
        }
        if ($ok) {
            my $svverb = $verbosity;
            $verbosity = 9 if ($dbg_08);
            prt("[v9] Loading 'INCLUDE' [$ff]\n") if (VERB9());
            # SAVE CERTAIN VARIABLES FROM CHANGE
            save_ref_hash_stack($rh);
            process_in_file($ff,$rh);
            # RESTORE CERTAIN VARIABLES BACK TO FORMER
            restore_ref_hash_stack($rh);
            # pgm_exit(1,"TEMP EXIT");
            $verbosity = $svverb;
        } elsif (!defined $load_failed{$item1}) {
            $load_failed{$item1} = 1;
            if ($item1 =~ /\$\{(\w+)\}/) {
                $val = $1;
                if (!defined $unexpanded_macros{$val}) {
                    $unexpanded_macros{$val} = 1;
                    if ($warn_not_expaned) {
                        prtw("WARNING: Unable to load INCLUDE [$item1] due to MACRO [$val] NOT expanded\n");
                    } else {
                        $warnings_avoided++;
                    }
                }
            } else {
                prtw("WARNING: Unable to locate INCLUDE [$item1] TRIED \n".join("\n",@tries)."!\n");
            }
        }

    } elsif ($tag eq 'PROJECT') {
        # CMAKE_PROJECT_NAME
        $val = strip_quotes($arr[0]);
        ${$rh}{'PROJ_NAME'} = $val;
        prt("$lnn: $tag = $val\n");
    } elsif ($tag eq 'SET') {
        # should be a VARIABLE item1 [item2 [item3...]]]
        my $rsmh = ${$rh}{'CMAKE_SETMACROS'};
        # my @arr = sort keys(%{$rsmh});
        $val = $arr[0]; # get the variable
        my @list = ();
        for ($i = 1; $i < $cnt; $i++) {
            push(@list,$arr[$i]);
        }
        # $set_macros{$val} = \@list;
        if (defined ${$rsmh}{$val}) {
            if ($warn_set_overwrites) {
                if ((${$rh}{'CURR_IN_IF'} == 0) && !($val =~ /^CMAKE_/i)) {
                    my $pia = ${$rsmh}{$val};
                    if (not_same_ref_array($pia,\@list)) {
                        $msg = "WARNING: SET variable [$val] being overwritten with NEW list! No IF \nIn file [$bfile]($lnn)]\n";
                        $msg .= "prev [".join(";",sort @{$pia})."]\n";
                        $msg .= "curr [".join(";",sort @list)."]\n";
                        prtw($msg);
                    }
                } else {
                    $warnings_avoided++;
                }
            } else {
                $warnings_avoided++;
            }
        }
        ${$rsmh}{$val} = \@list;
    } elsif ($tag eq 'FILE') {
        # file(READ version versionFile)
        $act2 = $arr[0];
        if ($act2 eq 'READ') {
            $file = strip_quotes($arr[1]);
            $val  = strip_quotes($arr[2]);
            ${$rh}{"FILE_VAR"} = $val;
            if (! -f $file) {
                ($nm,$dr) = fileparse($bfile);
                $file = $dr.$file
            }
            if (-f $file) {
                if (open FIL,"<$file") {
                    my @lines = <FIL>;
                    close FIL;
                    $cnt = scalar @lines;
                    for ($i = 0; $i < $cnt; $i++) {
                        $tmp = trim_all($lines[$i]);
                        $lines[$i] = $tmp;
                    }
                    ${$rh}{$val} = join(" ",@lines);
                    prt("Loaded FILE(READ [".${$rh}{$val}."]) into $val\n") if (VERB9());
                } else {
                    prtw("WARNING: Unable to open FILE [$file] $tline [$bfile]$lnn\n");
                }
            } else {
                if ($file =~ /\$\{(\w+)\}/) {
                    $val = $1;
                    if ($warn_unexpanded_fn) {
                        prtw("WARNING: Unable to find FILE [$file] due unexpanded [$val] [$bfile]$lnn\n");
                    } else {
                        $warnings_avoided++;
                    }
                } else {
                    prtw("WARNING: Unable to find FILE [$file] $tline [$bfile]$lnn\n");
                }
            }
        } elsif ($act2 eq 'WRITE') {
            # ***TBD*** TODO
        } elsif ($act2 eq 'APPEND') {
            # ***TBD*** TODO
        } elsif ($act2 eq 'GLOB_RECURSE') {
            # ***TBD*** TODO
            # file((GLOB_RECURSE _JAVA_GLOBBED_FILES "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/*.class") [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\UseJavaClassFilelist.cmake]34
        } elsif ($act2 eq 'RELATIVE_PATH') {
            # ***TBD*** TODO
            # file((RELATIVE_PATH _JAVA_CLASS_FILE ${CMAKE_JAVA_CLASS_OUTPUT_PATH} ${_JAVA_GLOBBED_FILES}) [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\UseJavaClassFilelist.cmake]1
        } elsif ($act2 eq 'MAKE_DIRECTORY') {
            # ***TBD*** TODO
            # FILE((MAKE_DIRECTORY ${CPACK_TEMPORARY_DIRECTORY}/debian) [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CPackDeb.cmake]182
        } elsif ($act2 eq 'REMOVE_RECURSE') {
            # ***TBD*** TODO
            # FILE((REMOVE_RECURSE "${CPACK_TEMPORARY_DIRECTORY}/debian") [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CPackDeb.cmake]211
        } elsif ($act2 eq 'REMOVE') {
            # ***TBD*** TODO
            # FILE((REMOVE ${CMAKE_PLATFORM_ROOT_BIN}/CMakeCPlatform.cmake ) [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeTestCCompiler.cmake]50
        } elsif ($act2 eq 'GLOB') {
            # ***TBD*** TODO
            # FILE((GLOB _CMAKE_OSX_SDKS "${OSX_DEVELOPER_ROOT}/SDKs/*") [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\Platform\Darwin.cmake]63
        } elsif ($act2 eq 'STRINGS') {
            # ***TBD*** TODO
            # file((STRINGS "${CMAKE_CURRENT_LIST_FILE}" linesLIMIT_COUNT ${_ep_documentation_line_count}REGEX "^# ( \\[[A-Z0-9_]+ [^]]*\\] +#.*$|[A-Za-z0-9_]+\\()") [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\ExternalProject.cmake]163
        } elsif ($act2 eq 'TO_CMAKE_PATH') {
            # ***TBD*** TODO
            # file((TO_CMAKE_PATH ${BOOST_ROOT} BOOST_ROOT) [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindBoost.cmake]602
        } elsif ($act2 eq 'COPY') {
            # ***TBD*** TODO
            # file((COPY ${FortranCInterface_SOURCE_DIR}/DESTINATION ${CMAKE_BINARY_DIR}/CMakeFiles/FortranCInterfaceMinGW) [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FortranCInterface.cmake]123
        } else {
            if (!defined $uncased_file_action{$act2}) {
                $uncased_file_action{$act2} = 1;
                prtw("WARNING: Uncased FILE action [$act2] $tline [$bfile]$lnn\n");
            } else {
                $warnings_avoided++;
            }
        }
    } elsif ($tag eq 'STRING') {
        #        0     1     2                                3                     4
        #        act2  act3  $item1                           $item2                $item3
        # string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CPACK_PACKAGE_VERSION ${FLIGHTGEAR_VERSION} )
        $act2 = $arr[0];
        if ($act2 eq 'STRIP') {
            # string(STRIP ${versionFile} FLIGHTGEAR_VERSION) 
            $item1 = extract_var($arr[1]);
            $item2 = $arr[2];
            if (defined ${$rh}{$item1}) {
                ${$rh}{$item2} = trim_all(${$rh}{$item1});
            } else {
                if ($item1 =~ /^\"*\$\{(\w+)\}\"*$/) {
                    $tmp = $1;
                    if (!defined $undefined_strip_item_macro{$tmp}) {
                        $undefined_strip_item_macro{$tmp} = 1;
                        prtw("WARNING: Undefined STRIP item macro [$tmp] [$item1] $tline [$bfile]$lnn\n");
                    } else {
                        $warnings_avoided++;
                    }
                } elsif (!defined $undefined_strip_item{$item1}) {
                    $undefined_strip_item{$item1} = 1;
                    prtw("WARNING: Undefined STRIP item [$item1] $tline [$bfile]$lnn\n");
                } else {
                    $warnings_avoided++;
                }
            }
        } elsif ($act2 eq 'REGEX') {
            $act3 = $arr[1];
            if ($act3 eq 'MATCH') {
                $item1 = strip_quotes($arr[2]);
                $item1 =~ s/\\\\/\\/g;
                $item2 = $arr[3];
                $item3 = extract_var($arr[4]);
                if (defined ${$rh}{$item3}) {
                    $val = ${$rh}{$item3};
                    if ($val =~ m/($item1)/) {
                        ${$rdefs}{$item2} = $1;
                        for ($i = 1; ; $i++) {
                            $i2 = $i + 1;
                            if (eval "defined \$$i2") {
                                $var = "CMAKE_MATCH_$i";
                                ${$rdefs}{$var} = eval "\$$i2";
                                prt("\$$i2 = ".${$rdefs}{$var}." in $var\n");
                            } else {
                                last;
                            }
                        }
                        prt("Got $item2 = ".${$rdefs}{$item2}."\n");
                    } else {
                        prtw("REGEX MATCH FAILED! on if ($val =~ /($item1)/)! [$bfile]$lnn\n");
                    }
                } else {
                    # WARNING: Undefined REGEX MATCH item [IN] string((REGEX MATCH "%[A-Za-z\(\)]*" F_PREFIX IN) [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CPackRPM.cmake]2
                    # WARNING: Undefined REGEX MATCH item [LISTS] string((REGEX MATCH "%[A-Za-z\(\)]*" F_PREFIX LISTS) [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CPackRPM.cmake]2   
                    # WARNING: Undefined REGEX MATCH item [CPACK_RPM_USER_FILELIST_INTERNAL] string((REGEX MATCH "%[A-Za-z\(\)]*" F_PREFIX CPACK_RPM_USER_FILELIST_INTERNAL) [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CPackRPM.cmake]2
                    if (!defined $undefined_regex_match{$item3}) {
                        $undefined_regex_match{$item3} = 1;
                        prtw("WARNING: Undefined REGEX MATCH item [$item3] $tline [$bfile]$lnn\n");
                    } else {
                        $warnings_avoided++;
                    }
                }
            } elsif ($act3 eq 'REPLACE') {
                # ***TBD*** TODO
            } elsif ($act3 eq 'MATCHALL') {
                # ***TBD*** TODO
                # WARNING: Uncase STRING REGEX [MATCHALL] action - STRING((REGEX MATCHALL "-I[^;]+"wxWidgets_INCLUDE_DIRS "${wxWidgets_CXX_FLAGS}") [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindwxWidgets.cmake]752
            } else {
                prtw("WARNING: Uncase STRING REGEX [$act3] action - $tline [$bfile]$lnn\n");
            }
        } elsif ($act2 eq 'TOUPPER') {
            # ***TBD*** TODO
        } elsif ($act2 eq 'REPLACE') {
            # ***TBD*** TODO
        } elsif ($act2 eq 'LENGTH') {
            # ***TBD*** TODO
            # STRING((LENGTH AUTOREQ _PACKAGE_HEADER_STRLENGTH) [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CPackRPM.cmake]24
        } elsif ($act2 eq 'SUBSTRING') {
            # ***TBD*** TODO
            # STRING((SUBSTRING AUTOREQ 1 ${_PACKAGE_HEADER_STRLENGTH} _PACKAGE_HEADER_TAIL) [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CPackRPM.cmake]26
        } elsif ($act2 eq 'TOLOWER') {
            # ***TBD*** TODO
            # STRING((TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_DEBIAN_PACKAGE_NAME) [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CPackDeb.cmake]241
        } elsif ($act2 eq 'COMPARE') {
            # ***TBD*** TODO
            # STRING((COMPARE NOTEQUAL "${QT_QMAKE_EXECUTABLE_LAST}" "${QT_QMAKE_EXECUTABLE}" QT_QMAKE_CHANGED) [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindQt4.cmake]488
        } else {
            if (!defined $uncased_string_action{$act2}) {
                $uncased_string_action{$act2} = 1;
                prtw("WARNING: Uncased STRING action [$act2] $tline [$bfile]$lnn\n");
            } else {
                $warnings_avoided++;
            }
        }
    } elsif ($tag eq 'MESSAGE') {
        prt("$lnn: $tag $arr[0] [".strip_quotes($arr[1])."]\n") if ($show_mess || $dbg_03);
    } elsif ($tag eq 'FIND_LIBRARY') {
    } elsif ($tag eq 'LIST') {
        # $hash{'CMAKE_LISTS'} = \%list_items;
        my $rli = ${$rh}{'CMAKE_LISTS'};
        $item1 = $arr[0];
        $ok = is_in_list_operations($item1);
        if ($ok && ($cnt >= 2)) {
            $item2 = $arr[1];
            my @larr = ();
            my $lcnt = 0;
            for ($i = 2; $i < $cnt; $i++) {
                push(@larr, $arr[$i]);
                $lcnt++;
            }
            if (defined ${$rli}{$item2}) {
                # already have this LIST
                prt("LIST [$item1] op [$item2] already EXISTS, $lcnt new items\n") if (VERB9());
            } else {
                # is a NEW list
                ${$rli}{$item2} = [];
                prt("LIST [$item1] op [$item2] is NEW\, $lcnt new items\n") if (VERB9());
            }
            $ra = ${$rli}{$item2};
            if ($item1 eq 'APPEND') {
                push(@{$ra},@larr);
            } elsif ($item1 eq 'LENGTH') {
                # ***TBD*** TODO
            } elsif ($item1 eq 'FIND') {
                # ***TBD*** TODO
            } elsif ($item1 eq 'GET') {
                # ***TBD*** TODO
            } elsif ($item1 eq 'REMOVE_ITEM') {
                # WARNING: LIST [REMOVE_ITEM] op [depends_CUDA_NVCC_FLAGS] operation NOT handled yet TODO! 
                # ***TBD*** TODO
            } elsif ($item1 eq 'REMOVE_DUPLICATES') {
                # ***TBD*** TODO
                # WARNING: LIST [REMOVE_DUPLICATES] op [dependency_list] operation NOT handled yet TODO! [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindCUDA\make2cmake.cmake]86
            } elsif ($item1 eq 'SORT') {
                # ***TBD*** TODO
                # WARNING: LIST [SORT] op [dependency_list] operation NOT handled yet TODO! [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindCUDA\make2cmake.cmake]87
            } elsif ($item1 eq 'REMOVE_AT') {
                # ***TBD*** TODO
                # WARNING: LIST [REMOVE_AT] op [MPI_EXTRA_LIBRARY_WORK] operation NOT handled yet TODO! [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindMPI.cmake]604
            } else {
                # WARNING: LIST [GET] op [pieces] operation NOT handled yet TODO! 
                if (!defined $missing_list_action{$item1}) {
                    $missing_list_action{$item1} = 1;
                    prtw("WARNING: LIST [$item1] op [$item2] operation NOT handled yet TODO! [$bfile]$lnn\n");
                } else {
                    $warnings_avoided++;
                }
            }
        } else {
            if ($ok) {
                prtw("WARNING: LIST operation [$item1] has insufficient elements! [$bfile]$lnn\n");
            } else {
                prtw("WARNING: LIST operation [$item1] UNKNOWN [$bfile]$lnn\n");
            }
        }
    } elsif ($tag eq 'FIND_PACKAGE') {
    } elsif ($tag eq 'EXECUTE_PROCESS') {
    } elsif ($tag eq 'OPTION') {
        # option(SP_FDMS "Set to ON to build FlightGear with special-purpose FDMs" OFF)
        $item1 = $arr[0];
        $item2 = $arr[1];
        $item3 = $arr[2];
        ${$rdefs}{$item1} = (($item3 eq 'OFF') ? 0 : ($item3 eq 'ON') ? 1 : 0);
        ${$rdefs}{"MESSAGE-$item1"} = strip_quotes($item2);
        # see process_cmake_OPTION_lines($rh) for setting 'CMAKE_OPTIONS' array
    } elsif ($tag eq 'GET_FILENAME_COMPONENT') {
    } elsif ($tag eq 'CHECK_INCLUDE_FILE') {
    } elsif ($tag eq 'CHECK_CXX_SOURCE_COMPILES') {
    } elsif ($tag eq 'INCLUDE_DIRECTORIES') {
    } elsif ($tag eq 'CHECK_FUNCTION_EXISTS') {
    } elsif ($tag eq 'CONFIGURE_FILE') {
    } elsif ($tag eq 'INSTALL') {
        #  my @install_actions = ();
        # ${$rh}{'CMAKE_INSTALLS'} = \@install_actions;
        my $ria = ${$rh}{'CMAKE_INSTALLS'};
        # push(@install_actions,[$lnn,$act]);
        push(@{$ria},[$lnn,$act]);
    } elsif ($tag eq 'CONFIGURE_FILE') {
    } elsif ($tag eq 'CHECK_INCLUDE_FILES') {
    } elsif ($tag eq 'CHECK_TYPE_SIZE') {
    } elsif ($tag eq 'GET_TARGET_PROPERTY') {
    } elsif ($tag eq 'SET_TARGET_PROPERTIES') {
    } elsif ($tag eq 'TARGET_LINK_LIBRARIES') {
    } elsif ($tag eq 'MACRO') {
        # must collect until ENDMACRO
        $item1 = $arr[0];   # get MACRO name
        my $lncnt = ${$rh}{"ACT_LCNT"};
        $lnn = ${$rh}{"ACT_LNN"};
        my $macro = '';
        my $args = '';
        my $argc = 0;
        for ($i = 1; $i < $cnt; $i++) {
            $args .= ' ' if (length($args));
            $args .= $arr[$i];
            $argc++;
        }
        my $bgn = $lnn;
        my $line = '';
        for (;$lnn < $lncnt ; $lnn++) {
            $line = ${$rlines}[$lnn];
            chomp $line;
            last if ($line =~ /^\s*ENDMACRO/i);
            $macro .= "$line\n";
        }
        $macro =~ s/\s$//g;
        prt("[v9] MACRO name $item1, args $argc [$args]\n") if ($dbg_04 || VERB9());
        my $rma = ${$rh}{'CMAKE_MACROS'};
        # $len = length($macro);
        if (defined ${$rma}{$item1}) {
            if ( ${$rh}{'CURR_IN_IF'} == 0 ) {   # NOT in any IF
                if ($warn_macro_overwrites) {
                    prtw("WARNING: Macro [$item1] ALREADY EXIST! Being overwritten!\n");
                } else {
                    $warnings_avoided++;
                }
            }
        }
        #my $rdia = string_to_directives_ra($macro);
        #$macro = join("\n",@{$rdia});
        #                  0     1
        ${$rma}{$item1} = [$args,$macro];

        ${$rh}{'ACT_I'} = $lnn; # move forward
        my $end = $lnn + 1;
        for ($lnn = 1; $lnn < $cnt; $lnn++) {
            $item1 .= ' ';
            $item1 .= $arr[$lnn];
        }
        prt("[v9] Collected MACRO($item1) from $bgn to $end, stored in 'CMAKE_MACROS'\n[$macro]\n") if (VERB9());
    } elsif ($tag eq 'FUNCTION') {
        # must eat until ENDFUNCTION
        # FindPackageMessage # FIND_PACKAGE_MESSAGE(<name> "message for user" "find result details")
        # my %functions = ();
        # ${$rh}{'CMAKE_FUNCTIONS'} = \%functions;
        $item1 = $arr[0];   # get FUNCTION name
        my $lncnt = ${$rh}{"ACT_LCNT"};
        $lnn = ${$rh}{"ACT_LNN"};
        my $line = '';
        my $function = '';
        for (;$lnn < $lncnt ; $lnn++) {
            $line = ${$rlines}[$lnn];
            chomp $line;
            $line = trim_all($line);
            last if ($line =~ /^ENDFUNCTION/i);
            next if (length($line) == 0);
            $function .= "$line\n";
        }
        my $rfh = ${$rh}{'CMAKE_FUNCTIONS'};
        ${$rfh}{$item1} = $function;
        # move the file lines FORWARD
        # ============================================
        ${$rh}{'ACT_I'}         = $lnn; # move forward
        # ============================================
    } elsif ($tag eq 'FOREACH') {
        # must eat until ENDFOREACH
        # BUT WATCH OUT FOR NESTED FOREACH LIKE
        # foreach (lang C CXX Fortran)
        #  foreach (id GNU Intel PGI XL)
        #    if (NOT CMAKE_${lang}_COMPILER_ID OR "${CMAKE_${lang}_COMPILER_ID}" STREQUAL "${id}")
        #      list(APPEND _MPI_${lang}_COMPILER_NAMES ${_MPI_${id}_${lang}_COMPILER_NAMES})
        #    endif()
        #    unset(_MPI_${id}_${lang}_COMPILER_NAMES)    # clean up the namespace here
        #  endforeach()
        # endforeach()
        $item1 = $arr[0];   # get MACRO name
        my $lncnt = ${$rh}{"ACT_LCNT"};
        $lnn = ${$rh}{"ACT_LNN"};
        my $macro = '';
        my $args = '';
        my $argc = 0;
        for ($i = 1; $i < $cnt; $i++) {
            $tmp = $arr[$i];    # extract the ARGUMENT item
            if ($tmp =~ /^\${(\w+)\}$/) {
                # got a macro to EXPAND
                # - check in 'SET' lists
                $val = $1;
                $ok = 0;
                my $rsmh = ${$rh}{'CMAKE_SETMACROS'};
                my @slarr = keys(%{$rsmh});
                foreach $key (@slarr) {
                    if ($key eq $val) {
                        $tmp = ${$rsmh}{$key};
                        $ok = 1;
                        last;
                    }
                }
                if (!$ok) {
                    # - check in 'LIST' lists
                    my $rli = ${$rh}{'CMAKE_LISTS'};
                    # $ok = is_in_list_operations($item1);
                    @slarr = keys(%{$rli});
                    foreach $key (@slarr) {
                        if ($key eq $val) {
                            $tmp = ${$rli}{$key};
                            $ok = 1;
                            last;
                        }
                    }
                }
                if (!$ok) {
                    if (!defined $not_found_in_set_or_list{$val}) {
                        $not_found_in_set_or_list{$val} = 1;
                         if ($warn_substitution_for) {
                            prtw("WARNING: Substitution for [$tmp] [$val] NOT FOUND IN 'SET' or 'LIST'! act [$action] [$bfile]$lnn\n");
                         } else {
                            $warnings_avoided++;
                         }
                    } else {
                        $warnings_avoided++;
                    }
                }
            }
            $args .= ' ' if (length($args));
            $args .= $tmp;
            $argc++;
        }
        my $bgn = $lnn;
        my $line = '';
        for (;$lnn < $lncnt ; $lnn++) {
            $line = ${$rlines}[$lnn];
            chomp $line;
            $line = trim_all($line);
            last if ($line =~ /^ENDFOREACH/i);
            next if (length($line) == 0);
            $macro .= "$line\n";
        }
        $macro =~ s/\s$//g;
        my $end = $lnn + 1;

        prt("[v9] Collected FOREACH MACRO($item1) args $argc [$args], from $bgn to $end\n[$macro]\n") if (VERB9() || $dbg_06);
        my $rdia = string_to_directives_ra($macro);
        prt("[v9] Trimmed actions [".join("\n",@{$rdia})."]\n") if (VERB9() || $dbg_06);
        # [v9] Collected FOREACH MACRO(mylibfolder) args 24 [Airports Aircraft ATC ATCDCL Radio ... ] from 31 to 34
        # Trimmed actions [add_subdirectory(${mylibfolder})]
        # in sub process_cmake_list($)
        # SAVE CERTAIN VARIABLES FROM CHANGE
        save_ref_hash_stack($rh);
        $msg = '';
        for ($i = 1; $i < $cnt; $i++) {
            $args = $arr[$i];
            $macro = join("\n",@{$rdia});
            my $orgtxt = $macro;
            $macro =~ s/\$\{$item1\}/$args/g;
            prt("[v9] After substitution [$macro]\n") if (VERB9() || $dbg_06);
            if (($orgtxt eq $macro)&&(length($msg)==0)) {
                $msg = "WARNING: Substitution of [$item1] to [$args] FAILED! ";
                $msg .= "File [$bfile]($bgn)";
            }
            my @lines = split("\n",$macro); # get back into an array
            ${$rh}{"BASE_LINES"} = \@lines;
            ${$rh}{'CURR_LINE_SOURCE'} = "Macro $item1 subs";
            process_cmake_list($rh);
        }
        prt("$msg\n") if (length($msg));
        prt("[v9] Completed $argc MACRO($item1) substitutions...\n") if (VERB9() || $dbg_06); 
        # RESTORE CERTAIN VARIABLES BACK TO FORMER
        restore_ref_hash_stack($rh);
        # move the file lines FORWARD
        # ============================================
        ${$rh}{'ACT_I'}         = $lnn; # move forward
        # ============================================
    } elsif ($tag eq 'FLTK_WRAP_UI') {
    } elsif ($tag eq 'GET_PROPERTY') {
    } elsif ($tag eq 'MARK_AS_ADVANCED') {
    } elsif ($tag eq 'GET_CMAKE_PROPERTY') {
    } elsif ($tag eq 'SET_PROPERTY') {
        # act [(GLOBAL APPEND PROPERTY FG_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/${sources}")] in [C:\FGCVS\flightgear\source\src\Airports\CMakeLists.txt]
    } elsif ($tag eq 'TRY_COMPILE') {
        # act [(${VAR} ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.cxx COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS} "${CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES}" "${CHECK_CXX_SOURCE_COMPILES_ADD_INCLUDES}" OUTPUT_VARIABLE OUTPUT)] in [C:\FGCVS\flightgear\source\src\Airports\CMakeLists.txt]39
    } elsif ($tag eq 'FIND_PATH') {
        # act [(ALUT_INCLUDE_DIR alut.h HINTS $ENV{ALUTDIR} PATH_SUFFIXES include/AL include/ALUT include PATHS ~/Library/Frameworks /Library/Frameworks /usr/local /usr /opt )]
    } elsif ($tag eq 'FIND_PROGRAM') {
        # act [(GIT_EXECUTABLE NAMES ${git_names} DOC "git command line client" )]
    } elsif ($tag eq 'RETURN') {
        # ***TBD** TODO
    } elsif ($tag eq 'FIND_PACKAGE_MESSAGE') {
        # ***TBD** TODO
        # act [(X11 "Found X11: ${X11_X11_LIB}" "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]")] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindX11.cmake]408 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindX11.cmake] 
    } elsif ($tag eq 'TRY_RUN') {
        # ***TBD** TODO
        # act [(THREADS_PTHREAD_ARG THREADS_HAVE_PTHREAD_ARG ${CMAKE_BINARY_DIR} ${CMAKE_ROOT}/Modules/CheckForPthreads.c CMAKE_FLAGS -DLINK_LIBRARIES:STRING=-pthread COMPILE_OUTPUT_VARIABLE OUTPUT)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindThreads.cmake]84 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindThreads.cmake] 
    } elsif ($tag eq 'FIND_PACKAGE_HANDLE_STANDARD_ARGS') {
        # ***TBD** TODO
        # act [(Threads DEFAULT_MSG Threads_FOUND)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindThreads.cmake]157 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindThreads.cmake] 
    } elsif ($tag eq 'UNSET') {
        # ***TBD** TODO
        # act [(CMAKE_C_ABI_FILES)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeCInformation.cmake]75 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeCInformation.cmake] 
    } elsif ($tag eq 'CMAKE_DETERMINE_COMPILER_ID') {
        # ***TBD** TODO
        # act [(C CFLAGS CMakeCCompilerId.c)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeDetermineCCompiler.cmake]130 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeDetermineCCompiler.cmake] 
    } elsif ($tag eq 'EXEC_PROGRAM') {
        # ***TBD** TODO
        # act [(${CMAKE_Fortran_COMPILER} ARGS ${CMAKE_Fortran_COMPILER_ID_FLAGS_LIST} -E "\"${CMAKE_ROOT}/Modules/CMakeTestGNU.c\"" OUTPUT_VARIABLE CMAKE_COMPILER_OUTPUT RETURN_VALUE CMAKE_COMPILER_RETURN)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeDetermineFortranCompiler.cmake]183 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeDetermineFortranCompiler.cmake] 
    } elsif ($tag eq '_FIND_ECLIPSE_VERSION') {
        # ***TBD** TODO
        # act [()] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeFindEclipseCDT4.cmake]55 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeFindEclipseCDT4.cmake] 
    } elsif ($tag eq 'PROCESSORCOUNT') {
        # ***TBD** TODO
        # act [(_CMAKE_ECLIPSE_PROCESSOR_COUNT)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeFindEclipseCDT4.cmake]61 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeFindEclipseCDT4.cmake] 
    } elsif ($tag eq 'GETDEFAULTWINDOWSPREFIXBASE') {
        # ***TBD** TODO
        # act [(CMAKE_GENERIC_PROGRAM_FILES)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeGenericSystem.cmake]162 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeGenericSystem.cmake] 
    } elsif ($tag eq 'BREAK') {
        # ***TBD** TODO
        # act [()] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeFindPackageMode.cmake]3 src [Macro file subs] 
    } elsif ($tag eq 'SET_COMPILE_FLAGS_VAR') {
        # ***TBD** TODO
        # act [(${NAME})] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeFindPackageMode.cmake]178 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeFindPackageMode.cmake] 
    } elsif ($tag eq 'SET_LINK_FLAGS_VAR') {
        # ***TBD** TODO
        # act [(${NAME})] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeFindPackageMode.cmake]180 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeFindPackageMode.cmake] 
    } elsif ($tag eq 'PRINTTESTCOMPILERSTATUS') {
        # ***TBD** TODO
        # act [("C" "")] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeTestCCompiler.cmake]23 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeTestCCompiler.cmake] 
    } elsif ($tag eq 'CMAKE_DETERMINE_COMPILER_ABI') {
        # ***TBD** TODO
        # act [(C ${CMAKE_ROOT}/Modules/CMakeCCompilerABI.c)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeTestCCompiler.cmake]71 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeTestCCompiler.cmake] 
    } elsif ($tag eq 'CRT_VERSION') {
        # ***TBD** TODO
        # act [("${manifest_files}" manifest_version_list)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeVerifyManifest.cmake]1 src [Macro f subs] 
    } elsif ($tag eq 'MATH') {
        # ***TBD** TODO
        # act [(EXPR _PACKAGE_HEADER_STRLENGTH "${_PACKAGE_HEADER_STRLENGTH} - 1")] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CPackRPM.cmake]25 src [Macro _RPM_SPEC_HEADER subs] 
    } elsif ($tag eq 'CHECK_VERSION') {
        # ***TBD** TODO
        # act [(${exe_files} "${manifest_version_list}")] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeVerifyManifest.cmake]1 src [Macro f subs] 
    } elsif ($tag eq 'CMAKE_DETERMINE_COMPILER_ID_VENDOR') {
        # ***TBD** TODO
        # act [(ASM${ASM_DIALECT})] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeDetermineASMCompiler.cmake]119 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CMakeDetermineASMCompiler.cmake] 
    } elsif ($tag eq 'ENABLE_TESTING') {
        # ***TBD** TODO
        # act [()] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CTest.cmake]97 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CTest.cmake] 
    } elsif ($tag eq 'SITE_NAME') {
        # ***TBD** TODO
        # act [(SITE)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CTest.cmake]196 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CTest.cmake] 
    } elsif ($tag eq 'GET_VS_VERSION_STRING') {
        # ***TBD** TODO
        # act [("${CMAKE_GENERATOR}" DART_CXX_NAME)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CTest.cmake]228 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CTest.cmake] 
    } elsif ($tag eq 'BUILD_COMMAND') {
        # ***TBD** TODO
        # act [(MAKECOMMAND_DEFAULT_VALUE CONFIGURATION "\${CTEST_CONFIGURATION_TYPE}")] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CTest.cmake]235 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CTest.cmake] 
    } elsif ($tag eq 'DEFINE_PROPERTY') {
        # ***TBD** TODO
        # act [(GLOBAL PROPERTY CTEST_TARGETS_ADDED BRIEF_DOCS "Internal property used by CTestTargets module." FULL_DOCS "Set by the CTestTargets module to track addition of testing targets." )] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CTestTargets.cmake]57 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\CTestTargets.cmake] 
    } elsif ($tag eq '_BOOST_CHECK_SPELLING') {
        # ***TBD** TODO
        # act [(Boost_ROOT)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindBoost.cmake]572 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindBoost.cmake] 
    } elsif ($tag eq '_BOOST_COMPILER_DUMPVERSION') {
        # ***TBD** TODO
        # act [(_boost_COMPILER_VERSION)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindBoost.cmake]775 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindBoost.cmake] 
    } elsif ($tag eq '_BOOST_PREPEND_LIST_WITH_THREADAPI') {
        # ***TBD** TODO
        # act [(_boost_RELEASE_NAMES ${_boost_RELEASE_NAMES})] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindBoost.cmake]13 src [Macro COMPONENT subs] 
    } elsif ($tag eq '_BOOST_SWAP_WITH_REALPATH') {
        # ***TBD** TODO
        # WARNING: tag [_BOOST_SWAP_WITH_REALPATH] NOT LISTED! act [(Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE "${_boost_docstring_release}")] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindBoost.cmake]32 src [Macro COMPONENT subs] 
    } elsif ($tag eq '_BOOST_MARK_COMPONENTS_FOUND') {
        # ***TBD** TODO
        # act [(OFF)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindBoost.cmake]1064 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindBoost.cmake] 
    } elsif ($tag eq 'LOAD_CACHE') {
        # ***TBD** TODO
        # act [(${CABLE_BUILD_DIR} EXCLUDE BUILD_SHARED_LIBS LIBRARY_OUTPUT_PATH EXECUTABLE_OUTPUT_PATH MAKECOMMAND CMAKE_INSTALL_PREFIX INCLUDE_INTERNALS CABLE_LIBRARY_PATH CABLE_EXECUTABLE_PATH)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindCABLE.cmake]40 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindCABLE.cmake] 
    } elsif ($tag eq 'FIND_FILE') {
        # ***TBD** TODO
        # act [(CURSES_HAVE_CURSES_H curses.h )] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindCurses.cmake]75 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindCurses.cmake] 
    } elsif ($tag eq '_GTEST_APPEND_DEBUGS') {
        # ***TBD** TODO
        # act [(GTEST_LIBRARIES GTEST_LIBRARY)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindGTest.cmake]154 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindGTest.cmake] 
    } elsif ($tag eq '_GTEST_FIND_LIBRARY') {
        # ***TBD** TODO
        # act [(GTEST_LIBRARY gtest-md gtest)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindGTest.cmake]138 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindGTest.cmake] 
    } elsif ($tag eq '_GTK2_FIND_INCLUDE_DIR') {
        # ***TBD** TODO
        # act [(GTK2_GTK_INCLUDE_DIR gtk/gtk.h)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindGTK2.cmake]375 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindGTK2.cmake] 
    } elsif ($tag eq '_GTK2_FIND_LIBRARY') {
        # ***TBD** TODO
        # act [(GTK2_GLIB_LIBRARY glib false true)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindGTK2.cmake]4 src [Macro _GTK2_component subs] 
    } elsif ($tag eq '_GTK2_GET_VERSION') {
        # ***TBD** TODO
        # act [(GTK2_MAJOR_VERSION GTK2_MINOR_VERSION GTK2_PATCH_VERSION ${GTK2_GTK_INCLUDE_DIR}/gtk/gtkversion.h)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindGTK2.cmake]380 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindGTK2.cmake] 
    } elsif ($tag eq '_PROTOBUF_FIND_LIBRARIES') {
        # ***TBD** TODO
        # act [(PROTOBUF protobuf)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindProtobuf.cmake]167 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindProtobuf.cmake] 
    } elsif ($tag eq '_QT4_QUERY_QMAKE') {
        # ***TBD** TODO
        # act [(QT_VERSION QTVERSION)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindQt4.cmake]493 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindQt4.cmake] 
    } elsif ($tag eq 'ERROR_MESSAGE') {
        # ***TBD** TODO
        # act [( "Invalid Qt version string given: \"${QT_MIN_VERSION}\", expected e.g. \"3.1.5\"")] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindQt3.cmake]205 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindQt3.cmake] 
    } elsif ($tag eq 'FIND_IMAGEMAGICK_API') {
        # ***TBD** TODO
        # act [(Magick++ Magick++.h Magick++ CORE_RL_Magick++_ )] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindImageMagick.cmake]2 src [Macro component subs] 
    } elsif ($tag eq 'FIND_IMAGEMAGICK_EXE') {
        # ***TBD** TODO
        # act [(${ImageMagick_FIND_COMPONENTS})] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindImageMagick.cmake]9 src [Macro component subs] 
    } elsif ($tag eq 'GET_DIRECTORY_PROPERTY') {
        # ***TBD** TODO
        # act [(incl INCLUDE_DIRECTORIES)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\SystemInformation.cmake]20 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\SystemInformation.cmake] 
    } elsif ($tag eq 'INTERROGATE_MPI_COMPILER') {
        # ***TBD** TODO
        # act [(C ${try_libs})] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindMPI.cmake]13 src [Macro lang subs] 
    } elsif ($tag eq 'IS_FILE_EXECUTABLE') {
        # ***TBD** TODO
        # act [(MPI_C_COMPILER MPI_COMPILER_IS_EXECUTABLE)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindMPI.cmake]3 src [Macro lang subs] 
    } elsif ($tag eq 'LINK_DIRECTORIES') {
        # ***TBD** TODO
        # act [(${VTK_LIBRARY_DIRS})] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\UseVTK40.cmake]29 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\UseVTK40.cmake] 
    } elsif ($tag eq 'LINK_LIBRARIES') {
        # ***TBD** TODO
        # act [(${WXWINDOWS_LIBRARIES})] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\Use_wxWindows.cmake]53 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\Use_wxWindows.cmake] 
    } elsif ($tag eq 'OSG_FIND_LIBRARY') {
        # ***TBD** TODO
        # act [(OSG osg)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\Findosg.cmake]51 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\Findosg.cmake] 
    } elsif ($tag eq 'OSG_FIND_PATH') {
        # ***TBD** TODO
        # act [(OSG osg/Version)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindOpenSceneGraph.cmake]88 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindOpenSceneGraph.cmake] 
    } elsif ($tag eq 'OSG_MARK_AS_ADVANCED') {
        # ***TBD** TODO
        # act [(OSG)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindOpenSceneGraph.cmake]90 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindOpenSceneGraph.cmake] 
    } elsif ($tag eq 'PKG_CHECK_MODULES') {
        # ***TBD** TODO
        # act [(PC_GNUTLS gnutls)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindGnuTLS.cmake]39 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindGnuTLS.cmake] 
    } elsif ($tag eq 'SANITYCHECKSDKANDDEPLOYTARGET') {
        # ***TBD** TODO
        # act [("${CMAKE_OSX_SYSROOT}" "${CMAKE_OSX_DEPLOYMENT_TARGET}")] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\Platform\Darwin.cmake]147 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\Platform\Darwin.cmake] 
    } elsif ($tag eq 'SEPARATE_ARGUMENTS') {
        # ***TBD** TODO
        # act [(wxWidgets_CXX_FLAGS)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindwxWidgets.cmake]735 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindwxWidgets.cmake] 
    } elsif ($tag eq 'SET_DIRECTORY_PROPERTIES') {
        # ***TBD** TODO
        # act [(PROPERTIES INCLUDE_DIRECTORIES "${DumpInformation_BINARY_DIR};${DumpInformation_SOURCE_DIR}")] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\SystemInformation.cmake]21 src [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\SystemInformation.cmake] 
    } elsif ($tag eq 'TRY_REGULAR_COMPILER') {
        # ***TBD** TODO
        # act [(C regular_compiler_worked)] in [C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules\FindMPI.cmake]17 src [Macro lang subs] 
    } elsif ($tag eq 'ENABLE_LANGUAGE') {
        # ***TBD** TODO
    } else {
        if (!defined $action_is_uncased{$tag}) {
            $action_is_uncased{$tag} = 1;
            prtw("WARNING: Action NOT CASED! [$tag] act [$action] [$bfile]$lnn\n");
        } else {
           $warnings_avoided++;
        }
    }
}

sub full_opts_line($) {
    my ($line) = shift;
    my $len = length($line);
    my $inquot = 0;
    my $inbrac = 0;
    my ($i,$ch,$qc,$pc,$pc2);
    $ch = '';
    $pc = '';
    for ($i = 0; $i < $len; $i++) {
        $pc2 = $pc;
        $pc = $ch;
        $ch = substr($line,$i,1);
        if ($inbrac) {
            if ($inquot) {
                # $inquot = 0 if (($ch eq $qc)&&($pc ne "\\"));
                $inquot = 0 if ( ($ch eq $qc) && ( ($pc ne "\\") || ( ($pc eq "\\")&&($pc2 eq "\\") ) ) );
            } elsif ($ch eq '"') { # &&($pc ne "\\")) {
                $inquot = 1;
                $qc = $ch;
            } elsif ($ch eq ')') {
                $inbrac--;
                if ($inbrac == 0) {
                    return 1;
                }
            }
        } else {
            if ($inquot) {
                #$inquot = 0 if (($ch eq $qc)&&($pc ne "\\"));
                $inquot = 0 if ( ($ch eq $qc) && ( ($pc ne "\\") || ( ($pc eq "\\")&&($pc2 eq "\\") ) ) );
            } elsif ($ch eq '"') { # &&($pc ne "\\")) {
                $inquot = 1;
                $qc = $ch;
            } elsif ($ch eq '(') {
                $inbrac++;
            }
        }
    }
    return 0;
}

sub split_opts_line($) {
    my ($line) = shift;
    my $len = length($line);
    my $inquot = 0;
    my $inbrac = 0;
    my ($i,$ch,$qc,$command,$hadsp,$pc,$pc2);
    my @arr = ();   # got nothing so far
    $command = '';  # start the collection
    $hadsp = 0;     # had no space yet
    for ($i = 0; $i < $len; $i++) {
        $ch = substr($line,$i,1);
        if ($ch eq '(') {
            last;   # found first '('
        } elsif ( !($ch =~ /\s/) ) {
            $command .= $ch;
        } else {    # have a SPACE
            if (length($command)) {
                # already had some non-space - trailing space
                # should check if the next sig char is the '('
            }
            # else have not yet started command,
            # so ignore this beginning space
        }
    }
    if (($ch ne '(')||(length($command)==0)) {
        prtw("WARNING: Option line did not conform! [$line]\n");
        return \@arr;
    }
    push(@arr,$command);    # push first item 'OPTION' or 'option'
    $command = '';
    # collect space separated items, skipping spaces in quoted strings
    $ch = '';
    $pc = '';
    for (; $i < $len; $i++) {
        $pc2 = $pc;
        $pc = $ch;
        $ch = substr($line,$i,1);
        if ($inbrac) {
            if ($inquot) {
                $command .= $ch;
                # $inquot = 0 if (($ch eq $qc)&&($pc ne "\\"));
                $inquot = 0 if ( ($ch eq $qc) && ( ($pc ne "\\") || ( ($pc eq "\\")&&($pc2 eq "\\") ) ) );
            } else {
                if ($ch =~ /\s/) {
                    push(@arr,$command) if (length($command));
                    $command = '';
                } else {    # not a space
                    if ($ch eq ')') {
                        $inbrac--;
                        if ($inbrac == 0) {
                            last;
                        }
                    } else { # not end bracket
                        $command .= $ch;
                        if ($ch eq '"') { # &&($pc ne "\\")) {
                            $inquot = 1;
                            $qc = $ch;
                        }
                    }
                }
            }
        } else {
            if ($inquot) {
                # $inquot = 0 if (($ch eq $qc)&&($pc ne "\\"));
                $inquot = 0 if ( ($ch eq $qc) && ( ($pc ne "\\") || ( ($pc eq "\\")&&($pc2 eq "\\") ) ) );
            } elsif ($ch eq '"') { # &&($pc ne "\\")) {
                $inquot = 1;
                $qc = $ch;
            } elsif ($ch eq '(') {
                $inbrac++;
            }
        }
    }
    push(@arr,$command) if (length($command));
    return \@arr;
}

sub process_cmake_OPTION_lines($) {
    my $rh = shift;
    my ($rlines,$in);
    $in = ${$rh}{"BASE_FILE"};
    $rlines = ${$rh}{"BASE_LINES"};
    ###my @options = ();
    ###${$rh}{'CMAKE_OPTIONS'} = \@options;
    my $rops = ${$rh}{'CMAKE_OPTIONS'};

    my ($cnt,$lnn,$i,$i2,$line,$tline,$tmp);
    my ($ra,$len,$option,$message,$default);
    my $mino = 0;
    my $minm = 0;
    $cnt = scalar @{$rlines};
    $in =~ s/^\.(\\|\/)//;
    $lnn = sprintf("%5d", $cnt);
    $in .= ' ' while (length($in) < 8+1+3);
    prt("[v9] Got $lnn lines, from [$in] to process...\n") if (VERB9());
    for ($i = 0; $i < $cnt; $i++) {
        $total_lines++;
        $i2 = $i + 1;
        $line = ${$rlines}[$i];
        $total_bytes += length($line);
        chomp $line;
        $tline = trim_all($line);
        if ($tline =~ /^option\s*\(/i) { # seek OPTION(...) line
            while (($i2 < $cnt)&&(!full_opts_line($tline))) {
                $total_lines++;
                $i++;
                $i2 = $i + 1;
                $tmp = ${$rlines}[$i];
                $total_bytes += length($tmp);
                $tline .= ' ';
                $tline = trim_all($tmp);
            }
            prt("[v9]:$i2: [$tline]\n") if (VERB9());
            $ra = split_opts_line($tline);
            $len = scalar @{$ra};
            if ($len >= 2) {
                $tmp = ${$ra}[0];
                $option = ${$ra}[1];
                $message = 'NO MESSAGE';
                if ($len > 2) {
                    $message = ${$ra}[2];
                }
                $default = 'OFF';
                if ($len > 3) {
                    $default = ${$ra}[3];
                }
                ###push(@options, [$option, $message, $default]);
                push(@{$rops}, [$option, $message, $default]);
                if (VERB9()) {
                    prt("[v9] $option $message $default\n");
                    #foreach $tmp (@{$ra}) {
                    #    prt("$tmp ");
                    #}
                    #prt("\n");
                }
            } else {
                prtw("$pgmname:WARNING: Line $i2 did not SPLIT correctly! [$tline] file $in\n");
            }
        }
    }
    # ${$rh}{"OPTIONS"} = \@options;
}

#sub add_if_tags($$) {
#    my ($rh,$txt) = @_;
#    my $riftags = ${$rh}{'CMAKE_IFTAGS'};
#    my $bfile = ${$rh}{"BASE_FILE"};
#    my $lnn = ${$rh}{"ACT_LNN"};
#    #${$riftags}{$txt} = 1;
#    my $len = length($txt);
#    my ($i,$ch,$tag,%mods,$ra);
#    $tag = '';
#    %mods = ();
#    # if(${CMAKE_VERSION} VERSION_GREATER 2.8.4)
#    if ($txt =~ /\W*\$\{CMAKE_VERSION\}/) {
#        $tag = 'CMAKE_VERSION';
#        ${$riftags}{$tag} = [] if (!defined ${$riftags}{$tag});
#        $ra = ${$riftags}{$tag};
#        push(@{$ra},[{%mods},$bfile,$lnn]);
#        return;
#    } elsif ($txt =~ /\W*\$\{MSVC_VERSION\}/) {
#        #     if (${MSVC_VERSION} EQUAL 1600)
#        $tag = 'MSVC_VERSION';
#        ${$riftags}{$tag} = [] if (!defined ${$riftags}{$tag});
#        $ra = ${$riftags}{$tag};
#        push(@{$ra},[{%mods},$bfile,$lnn]);
#        return;
#    } elsif ($txt =~ /\$ENV\{(\w+)\}/) {
#        # if(NOT "$ENV{BUILD_ID}" STREQUAL "")
#        $tag = "ENV(".$1.")";
#        ${$riftags}{$tag} = [] if (!defined ${$riftags}{$tag});
#        $ra = ${$riftags}{$tag};
#        push(@{$ra},[{%mods},$bfile,$lnn]);
#        return;
#    }
#    for ($i = 0; $i < $len; $i++) {
#        $ch = substr($txt,$i,1);
#        if ($ch =~ /\w/) {
#            $tag .= $ch;
#        } elsif (length($tag)) {
#            if (($tag =~ /^AND$/i)||($tag =~ /^OR$/i)||($tag =~ /^NOT$/i)||($tag =~ /^STREQUAL$/i)) {
#                # forget these
#                $mods{$tag} = 1;
#            } else {
#                ${$riftags}{$tag} = [] if (!defined ${$riftags}{$tag});
#                $ra = ${$riftags}{$tag};
#                push(@{$ra},[{%mods},$bfile,$lnn]);
#                %mods = ();
#            }
#            $tag = '';
#        }
#    }
#    #${$riftags}{$tag} = [@mods];
#    if (length($tag)) {
#        ${$riftags}{$tag} = [] if (!defined ${$riftags}{$tag});
#        $ra = ${$riftags}{$tag};
#        push(@{$ra},[{%mods},$bfile,$lnn]);
#    }
#}

sub process_macro($) {
    my $rh = shift;
    my $bfile  = ${$rh}{"BASE_FILE"};
    my $csrc   = ${$rh}{'CURR_LINE_SOURCE'};
    my $rma = ${$rh}{'CMAKE_MACROS'};
    my $tag = ${$rh}{"ACT_TAG"};
    my $action = ${$rh}{"ACT_ACT"};
    my $lnn = ${$rh}{"ACT_LNN"};
    my $tline = ${$rh}{"ACT_LINE"};
    my $i = ${$rh}{"ACT_I"};
    my $lctag = lc($tag);
    my ($rm);
    $tag = ${$rh}{'CURR_FOUND_MACRO'};
    if (defined ${$rma}{$tag}) {
        $rm = ${$rma}{$tag};
    } elsif (defined ${$rma}{$lctag}) {
        $tag = $lctag;
        $rm = ${$rma}{$tag};
    } else {
        if (!defined $macros_not_found{$tag} && !defined $macros_not_found{$lctag}) {
            $macros_not_found{$tag} = 1;
            $macros_not_found{$lctag} = 1;
            prtw("WARNING: Macro matching [$tag] and [$lctag] NOT FOUND! in 'CMAKE_MACROS [$bfile]$lnn\n");
        }
        return;
    }
    ### my $rm = ${$rma}{$tag};
    my $msubs = ${$rm}[0];
    my @sarr = space_split($msubs);
    my %subs = ();
    my ($tmp,$scnt,$msg);
    $scnt = 0;
    foreach $tmp (@sarr) {
        $subs{$tmp} = 1;
        $scnt++;
    }
    my $macro = ${$rm}[1];
    my $act = clean_action($action);
    my @arr = space_split($act);
    my $cnt = scalar @arr;
    my $name = $arr[0];
    my $ARGN = '';
    for ($i = 0; $i < $cnt; $i++) {
        if ($i < $scnt) {
            $tmp = $sarr[$i];
            $subs{$tmp} = $arr[$i];
            $macro =~ s/\$\{$tmp\}/$arr[$i]/g;
        } else {
            $ARGN .= ' ' if (length($ARGN));
            $ARGN .= $arr[$i];
        }
    }
    $macro =~ s/\$\{ARGN\}/$ARGN/g if (length($ARGN));
    $msg = '';
    foreach $tmp (@sarr) {
        $msg .= ' ' if (length($msg));
        $msg .= "$tmp=";
        $msg .= $subs{$tmp};
    }
    # prt("[v9] Macro $tag, ms [$msubs] - $cnt [$act]\n") if ($dbg_04 || VERB9());
    if ($dbg_04 || VERB9()) {
        prt("[v9] Macro $tag, ms [$msubs] - $cnt $msg ARGN=$ARGN\n");
        prt("[v9] Macro TEXT [$macro]\n");
    }
}

my $max_out_line = 95;
sub max_out_line($) {
    my $line = shift;
    my $len = length($line);
    if ($len > $max_out_line) {
        $line = substr($line,0,45)."...".substr($line,$len-45);
    }
    return $line;
}


sub process_cmake_list($) {
    my $rh = shift;
    my $bfile  = ${$rh}{"BASE_FILE"};
    my $rlines = ${$rh}{"BASE_LINES"};
    my $cursrc = ${$rh}{'CURR_LINE_SOURCE'};
    $bfile .= ":$cursrc" if ($bfile ne $cursrc);

    my $lcnt = scalar @{$rlines};
    prt("[v9] Got $lcnt lines, from $bfile\n") if (VERB9());
    my ($i,$lnn,$line,$trline,$len,$ch,$j,$tag,$test,$fnd,@braces,$action,$inquot,$pc,$pc2,$bq);
    my ($rma,$key,$msg,$tmp,$tail,$brcnt);
    #my @ifstack = ();
    #my %iftags = ();
    #my @if_stack = ();
    #${$rh}{'CURR_IF_STACK'} = \@if_stack;
    my $rifstk = ${$rh}{'CURR_IF_STACK'};
    @{$rifstk} = ();    # clear stack
    ${$rh}{'CURR_IN_IF'} = 0;   # zero IF counter
    my $riftags = ${$rh}{'CMAKE_IFTAGS'};
    ${$rh}{"ACT_LCNT"}  = $lcnt;
    $ch = '';
    $pc = '';
    for ($i = 0; $i < $lcnt; $i++) {
        $lnn = $i + 1;
        $line = ${$rlines}[$i]; # extract a LINE
        chomp $line;
        $trline = trim_all($line);
        $len = length($trline);
        next if ($len == 0);    # skip if trimmed line is blank
        next if ($trline =~ /^\#/);  # skip if a comment line
        $tag = '';
        $line = ''; # start FULL line accumulation
        # get FIRST item on the line at the tag (directive)
        $ch = '';
        $pc = '';
        for ($j = 0; $j < $len; $j++) {
            $pc2 = $pc;
            $pc = $ch;
            $ch = substr($trline,$j,1);
            $line .= $ch;
            last if ($ch =~ /\W/);
            $tag .= $ch;
        }
        $tag = uc($tag);    # see if the directive is in the list of directives
        # continue eating spaces
        for (; $j < $len; $j++) {
            $pc2 = $pc;
            $pc = $ch;
            $ch = substr($trline,$j,1);
            last if ($ch =~ /\S/);
        }
        # this should be the BEGINNING of a directive (... until ...) end
        if ($ch eq '(') {
            $line .= $ch;   # add to LINE
            @braces = ();
            push(@braces,$ch);
            $brcnt = scalar @braces;
            $j++;
            $action = $ch;  # start the action
            $inquot = 0;
            # continue on the line - skip over quote "..."
            $pc = '';
            $tail = '';
            for (; $j < $len; $j++) {
                $pc2 = $pc;
                $pc = $ch;
                $ch = substr($trline,$j,1);
                if ($brcnt == 0) {
                    last if ($ch eq '#');
                    if ($ch =~ /\s/) {
                        $tail .= $ch if (length($tail));
                    } else {
                        $tail .= $ch;
                    }
                    next;
                }
                $action .= $ch; # add character to 'action', within braces
                $line .= $ch;
                if ($inquot) {
                    # $inquot = 0 if (($ch eq '"')&&($pc ne "\\"));
                    $inquot = 0 if ( ($ch eq '"') && ( ($pc ne "\\") || ( ($pc eq "\\")&&($pc2 eq "\\") ) ) );
                } else {
                    if ($ch eq '(') {
                        push(@braces,$ch);
                        $brcnt = scalar @braces;
                    } elsif ($ch eq ')') {
                        if (@braces) {
                            pop @braces;    # pop this brace
                        } else {
                            $msg = "WARNING: Encountered ')' with none on stack! $line [$bfile]$lnn";
                            prtw("$msg\n");
                        }
                        $brcnt = scalar @braces;
                    } elsif ($ch eq '"') { # &&($pc ne "\\")) {
                        $inquot = 1;
                        $bq = $j;
                    }
                }
            }
            # done the line
            # 20120427 - BUT FOUND LINE WRAP WITH QUOTES LIKE
            #   FILE(WRITE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testFortranCompiler.f "
            #        PROGRAM TESTFortran
            #        PRINT *, 'Hello'
            #        END
            # ")
            #if ($inquot) {
                #$tmp = max_out_line($trline);
                #$msg = "Ended line in QUOTES! [";
                #$len = length($msg);
                #$msg .= "$tmp] [$bfile]$lnn";
                #$msg .= " src [$csrc]" if ($bfile ne $csrc);
                #if ($bq < 80) {
                    #$msg .= "\n";
                    #$msg .= ($len + $bq) x ' ';
                    #$msg .= "<- BEGIN HERE";
                #}
                #prtw("WARNING: $msg\n");
            #}
            #$inquot = 0;    # 20120425 - NEW BEHAVIOUR - end of line == end of quotes
            $brcnt = scalar @braces;
            while ($brcnt) {
                # but are in OPEN braces, so continue to NEXT
                $i++;
                $action .= ' ';
                if ($i < $lcnt) {
                    $lnn = $i + 1;
                    $trline = ${$rlines}[$i]; # get NEXT line
                    chomp $trline;
                    $trline = trim_all($trline);
                    $len = length($trline);
                    # process this NEXT line, again skipping quotes
                    $ch = '';
                    $pc = '';
                    for ($j = 0; $j < $len; $j++) {
                        $pc2 = $pc;
                        $pc = $ch;
                        $ch = substr($trline,$j,1);
                        $action .= $ch;
                        $line .= $ch;
                        if ($inquot) {
                            $inquot = 0 if ( ($ch eq '"') && ( ($pc ne "\\") || ( ($pc eq "\\")&&($pc2 eq "\\") ) ) );
                            # $inquot = 0 if (($ch eq '"')&&($pc ne "\\"));
                        } else {
                            if ($ch eq '(') {
                                push(@braces,$ch);
                                $brcnt = scalar @braces;
                            } elsif ($ch eq ')') {
                                if (@braces) {
                                    pop @braces;
                                } else {
                                    $msg = "WARNING: Encountered ')' with none on stack! $line [$bfile]$lnn";
                                    prtw("$msg\n");
                                }
                                $brcnt = scalar @braces;
                            } elsif ($ch eq '"') { # &&($pc ne "\\")) {
                                $inquot = 1;
                            }
                        }
                    }
                } else {
                    $tmp = max_out_line($line);
                    $msg = "WARNING: Ran out of lines in a brace! [$tmp] [$bfile]$lnn";
                    prtw("$msg\n");
                    last;
                }
            }

            if ($inquot) {
                $tmp = max_out_line($line);
                $msg = "WARNING: Ended lines in QUOTES! [$tmp] [$bfile]$lnn";
                prtw("$msg\n");
            }
            if (length($tail)) {
                $tmp = max_out_line($line);
                $msg = "WARNING: Got out-of-brace [$tail] WHAT IS THIS? [$tmp] [$bfile]$lnn";
                prtw("$msg\n");
            }
        } elsif ($ch eq '@') {
            # example - ALSO NOTE TRAILING comment '# list'
            # set(CUDA_NVCC_FLAGS @CUDA_NVCC_FLAGS@ ;; @CUDA_WRAP_OPTION_NVCC_FLAGS@) # list
            # @CUDA_NVCC_FLAGS_CONFIG@
            # set(nvcc_flags @nvcc_flags@) # list
            $line .= $ch;   # add to LINE
            for (; $j < $len; $j++) {
                $pc2 = $pc;
                $pc = $ch;
                $ch = substr($trline,$j,1);
                $line .= $ch;
                last if ($ch eq '@');
            }
            # ***TBD*** What to do with THIS??????
            next;
        } else {
            $msg = "WARNING: First char NOT '(' nor '\@'! [$line] [$bfile]$lnn";
            prtw("$msg\n");
            next;
        }

        ${$rh}{"ACT_TAG"}  = $tag;
        ${$rh}{"ACT_ACT"}  = $action;
        ${$rh}{"ACT_LNN"}  = $lnn;
        ${$rh}{"ACT_LINE"} = $line;
        ${$rh}{"ACT_I"}    = $i;

        if ($line =~ /^if\s*\((.*)\)/i) {
            # FOUND AN IF
            ${$rh}{'CURR_IN_IF'}++;   # bump IF counter
            $tag = $1;
            # my $riftags = ${$rh}{'CMAKE_IFTAGS'} = \%if_tags;
            prt("$lnn: IF($tag)\n") if ($dbg_01 || VERB9());
            #push(@ifstack,[$tag,$lnn]);
            push(@{$rifstk},[$tag,$lnn]);
            if (length($tag)) {
                # $iftags{$tag} = 1;
                ### add_if_tags($rh,$tag);
                # ${$riftags}{$tag} = 1;
            } else {
                prtw("WARNING: $lnn: Got an IF with NO tag! [$line]! [$bfile]$lnn\n");
            }
        } elsif ($line =~ /^else\s*\((.*)\)/i) {
            # FOUND AN ELSE
            $tag = $1;
            prt("$lnn: ELSE($tag)\n") if ($dbg_01 || VERB9());
        } elsif ($line =~ /^elseif\s*\((.*)\)/i) {
            # FOUND ELSEIF
            $tag = $1;
            prt("$lnn: ELSEIF($tag)\n") if ($dbg_01 || VERB9());
        } elsif ($line =~ /^endif\s*\((.*)\)/i) {
            # FOUND ENDIF
            ${$rh}{'CURR_IN_IF'}--;   # decrement IF counter
            $tag = $1;
            prt("$lnn: ENDIF($tag)\n") if ($dbg_01) || VERB9();
            if (@{$rifstk}) {
                pop @{$rifstk};
            }
        } else {
            # NOT IF, ELSE, ELSEIF, ENDIF
            $fnd = 0;
            foreach $test (@cmake_actions) {
                if ($test eq $tag) {
                    $fnd = 1;
                    last;
                }
            }
            if (defined $cpack_actions{$tag}) {
                $cpack_actions{$tag}->($rh);
            } elsif ($fnd) {
                #prt("Action [$tag] $action\n");
                process_action($rh);
                $i = ${$rh}{"ACT_I"};   # potentially updated line number
            } else {
                $rma = ${$rh}{'CMAKE_MACROS'};
                foreach $key (keys %{$rma}) {
                    if ($key eq $tag) {
                        $fnd = 1;
                        ${$rh}{'CURR_FOUND_MACRO'} = $key;
                        last;
                    }
                    if (lc($key) eq lc($tag)) {
                        $fnd = 1;
                        ${$rh}{'CURR_FOUND_MACRO'} = $key;
                        last;
                    }
                }
                if ($fnd) {
                    # found action is a MACRO - see if I can EXPAND the macro using the arguments
                    process_macro($rh);
                } else {
                    if (!defined $unlisted_tags{$tag}) {
                        $unlisted_tags{$tag} = 1;
                        $msg = '';
                        if ($add_searched_debug) {
                            $msg = "\nSearched: ";
                            my (@tarr,$tcnt);
                            @tarr = keys(%cpack_actions);
                            $tcnt = scalar @tarr;
                            $msg .= "\ncpack_actions: $tcnt ";
                            foreach $tmp (@tarr) {
                                $msg .= "$tmp ";
                            }
                            $tcnt = scalar @cmake_actions; 
                            $msg .= "\ncmake_actions: $tcnt ";
                            foreach $tmp (@cmake_actions) {
                                $msg .= "$tmp ";
                            }
                            @tarr = keys(%cpack_actions);
                            $tcnt = scalar @tarr;
                            $msg .= "\nCMAKE_MACROS: $tcnt ";
                            foreach $key (@tarr) {
                                $msg .= "$key ";
                            }
                        }
                        prtw("WARNING: tag NOT LISTED! [$tag] act [$action] in [$bfile]$lnn $msg\n");
                    } else {
                        $unlisted_tags{$tag}++;
                    }
                }
            }
        }
    }
    if (@{$rifstk}) {
        prtw("WARNING: Still entries in IF stack! [$bfile]$lnn\n");
    }
    process_cmake_OPTION_lines($rh);
}

    ### DO MACRO SUBS ###
sub do_macro_subs($) {
    my $rh = shift;
    my $rma = ${$rh}{'CMAKE_MACROS'};
    my @arr = sort keys(%{$rma});
    #my @arr = sort keys(%macros);
    my ($mnm,$args,$macs,$rm);
    foreach $mnm (@arr) {
        # $rm = $macros{$mnm};    # ${$rma}{$mnm};
        $rm = ${$rma}{$mnm};
        $args = ${$rm}[0];
        $macs = ${$rm}[1];
        my @lines = split("\n", $macs);    # ${$rm}{$mnm});
        ${$rh}{"BASE_LINES"} = \@lines;
        ${$rh}{'CURR_LINE_SOURCE'} = "Macro $mnm";
        process_cmake_list($rh);
    }
}

sub do_subdirectories($) {
    my $rh = shift;
    # ADD_SUBDIRECTORY
    # my %subdirs = ();
    # ${$rh}{'CMAKE_SUBDIRS'} = \%subdirs;
    #my %subs_done = ();
    # ${$rh}{'CMAKE_SUBSDONE'} = \%subs_done;
    my $rsh = ${$rh}{'CMAKE_SUBDIRS'};
    my $rsd = ${$rh}{'CMAKE_SUBSDONE'};
    my @arr = keys(%{$rsh});
    my $max = scalar @arr;
    my ($i,$sd,$ff,$key,$rsa);
    for ($i = 0; $i < $max; $i++) {
        $key = $arr[$i];
        $rsa = ${$rsh}{$key};
        #                 0      1
        # push(@{$rsubs},[$item1,$ff]);
        $sd = ${$rsa}[0];
        $ff = ${$rsa}[1];
        if (!defined ${$rsd}{$ff}) {
            ${$rsd}{$ff} = 1;
            process_in_file($ff,$rh);
        }
    }
}

sub process_in_file($$) {
    my ($inf,$rh) = @_;
    my $rfh = ${$rh}{'CMAKE_FILES_LOADED'};
    if (defined ${$rfh}{$inf}) {
        return $rh;
    }
    ${$rfh}{$inf} = 1;  # set as DONE
    if (! open INF, "<$inf") {
        pgm_exit(1,"ERROR: Unable to open file [$inf]\n"); 
    }
    my @lines = <INF>;
    close INF;
    my $lncnt = scalar @lines;
    prt("Processing $lncnt lines, from [$inf]...\n") if (VERB2());
    $total_file_lines += $lncnt;
    $total_files++;
    my ($name,$dir) = fileparse($inf);
    ${$rh}{'PROJECT_SOURCE_DIR'} = $dir;
    ${$rh}{"BASE_FILE"} = $inf;
    ${$rh}{'CURR_LINE_SOURCE'} = $inf;
    ${$rh}{"BASE_LINES"} = \@lines;
    my $rfa = ${$rh}{'CMAKE_FILES'};
    push(@{$rfa},$inf);

    process_cmake_list($rh);

    # should this be on a per FILE basis, or at the END
    # do_macro_subs($rh);

    do_subdirectories($rh);

    return $rh;
}

sub process_in_dir($$) {
    my ($dir,$rh) = @_;
    if (! opendir(DIR, $dir) ) {
        pgm_exit(1,"ERROR: Unable to open directory [$dir]\n");
    }
    my @files = readdir(DIR);
    closedir(DIR);
    my ($bfile);
    $dir .= $PATH_SEP if ( !($dir =~ /(\\|\/)$/) );
    $bfile = $dir."CMakeLists.txt";
    if (-f $bfile) {
        ${$rh}{'PRIMARY_CMAKE_FILE'} = $bfile;
        ${$rh}{'PRIMARY_CMAKE_DIR'}  = $dir;
        return process_in_file($bfile,$rh);
    } else {
        prtw("WARNING: Directory [$dir] does NOT contain a 'CMakeLists.txt' file!\n");
    }
    return $rh;
}

sub show_cmake_options($) {
    my $rh = shift;
    my ($in,$roptions);
    $in = ${$rh}{'PRIMARY_CMAKE_FILE'};
    $roptions = ${$rh}{"CMAKE_OPTIONS"};
    my ($i,$ra,$len,$option,$message,$default,$cnt);
    my $mino = 0;
    my $minm = 0;
    $cnt = scalar @{$roptions};
    $total_options += $cnt;
    $mino = 0;
    $minm = 0;
    if ($cnt) {
        prt("\nOPTION: Found $cnt 'OPTION' items in [$in]\n");
        for ($i = 0; $i < $cnt; $i++) {
            #                 0        1         2
            # push(@options, [$option, $message, $default]);
            $option = ${$roptions}[$i][0];
            $message = ${$roptions}[$i][1];
            $default = ${$roptions}[$i][2];
            $len = length($option);
            $mino = $len if ($len > $mino);
            $len = length($message);
            $minm = $len if ($len > $minm);
        }
        for ($i = 0; $i < $cnt; $i++) {
            #                 0        1         2
            # push(@options, [$option, $message, $default]);
            $option = ${$roptions}[$i][0];
            $message = ${$roptions}[$i][1];
            $default = ${$roptions}[$i][2];
            $option .= ' ' while (length($option) < $mino);
            $message .= ' ' while (length($message) < $minm);
            prt("$option $message $default\n");
        }
    } else {
        prt("[v9] Found NO options!\n") if (VERB9());
    }
}

sub show_install_actions($) {
    my $rh = shift;
    my $in = ${$rh}{'PRIMARY_CMAKE_FILE'};
    #my $cnt = scalar @install_actions;
    #  my @install_actions = ();
    # ${$rh}{'CMAKE_INSTALLS'} = \@install_actions;
    my $ria = ${$rh}{'CMAKE_INSTALLS'};
    # push(@install_actions,[$lnn,$act]);
    #push(@{$ria},[$lnn,$act]);
    my $cnt = scalar @{$ria};
    #                        0    1
    # push(@install_actions,[$lnn,$act]);
    prt("\nINSTALLL: Found $cnt 'INSTALL' actions in [$in]\n");
    my ($act,$lnn,$ra,$i);
    for ($i = 0; $i < $cnt; $i++) {
        # $ra = $install_actions[$i];
        $ra = ${$ria}[$i];
        $lnn = ${$ra}[0];
        $act = ${$ra}[1];
        prt("$lnn $act\n");
    }
}

sub show_set_lists($) {
    my $rh = shift;
    my $in = ${$rh}{'PRIMARY_CMAKE_FILE'};
    ## my %set_macros = ();
    ## ${$rh}{'CMAKE_SETMACROS'} = \%set_macros;
    ## my @arr = sort keys(%set_macros);
    my $rsmh = ${$rh}{'CMAKE_SETMACROS'};
    my @arr = sort keys(%{$rsmh});
    my $cnt = scalar @arr;
    prt("\nSET: Found $cnt 'SET' lists in [$in]\n");
    my ($var,$ra);
    $cnt = 0;
    foreach $var (@arr) {
        $ra = ${$rsmh}{$var};
        $cnt++;
        prt("$cnt: $var = [".join(" ",@{$ra})."]\n");
    }
}

sub show_list_sets($) {
    my $rh = shift;
    my $in = ${$rh}{'PRIMARY_CMAKE_FILE'};
    my $rli = ${$rh}{'CMAKE_LISTS'};
    # $ok = is_in_list_operations($item1);
    my @arr = sort keys(%{$rli});
    my $cnt = scalar @arr;
    prt("\nLIST: Found $cnt 'LIST' lists in [$in]\n");
    my ($key,$ra,$icnt);
    $cnt = 0;
    foreach $key (@arr) {
        $ra = ${$rli}{$key};
        $cnt++;
        $icnt = scalar @{$ra};
        prt("$cnt:$icnt: $key = [".join(" ",@{$ra})."]\n");
    }
}

sub show_binary_items($) {
    my $rh = shift;
    my $in = ${$rh}{'PRIMARY_CMAKE_FILE'};
    my $rlibs = ${$rh}{'CMAKE_LIBS'};
    my $rexes = ${$rh}{'CMAKE_EXES'};
    my @earr = sort keys(%{$rexes});
    my @larr = sort keys(%{$rlibs});
    my $ecnt = scalar @earr;
    my $lcnt = scalar @larr;
    prt("\nADD_[BIN]: Found $ecnt 'EXECUTABLE' $lcnt 'LIBRARY' in [$in]\n");
    my ($key,$ra,$icnt);
    prt(" EXECUTABLE: $ecnt\n") if ($ecnt);
    $ecnt = 0;
    foreach $key (@earr) {
        $ra = ${$rexes}{$key};
        $ecnt++;
        $icnt = scalar @{$ra};
        prt("$ecnt:$icnt: $key = [".join(" ",@{$ra})."]\n");
    }
    prt(" LIBRARY: $lcnt\n") if ($lcnt);
    $lcnt = 0;
    foreach $key (@larr) {
        $ra = ${$rlibs}{$key};
        $lcnt++;
        $icnt = scalar @{$ra};
        prt("$lcnt:$icnt: $key = [".join(" ",@{$ra})."]\n");
    }
}

#sub show_if_tags($) {
#    my $rh = shift;
#    my $riftags = ${$rh}{'CMAKE_IFTAGS'};
#    my @arr = keys(%{$riftags});
#    my $itcnt = scalar @arr;
#    prt("\nGot $itcnt IF tags...\n");
#    $itcnt = 0;
#    my ($tag,$ra,$ff,$lnn,$ra2,$msg,$min,$len);
#    $min = 0;
#    foreach $tag (sort @arr) {
#        $len = length($tag);
#        $min = $len if ($len > $min);
#    }
#    # $ra = ${$riftags}{$tag};
#    # push(@{$ra},[{%mods},$bfile,$lnn]);
#    foreach $tag (sort @arr) {
#        $itcnt++;
#        $ra = ${$riftags}{$tag};
#        $msg = '';
#        foreach $ra2 (@{$ra}) {
#            $ff = ${$ra2}[1];
#            $lnn = ${$ra2}[2];
#            $msg .= ' ' if (length($msg));
#            $msg .= "$ff($lnn)";
#        }
#
#        $tag .= ' ' while (length($tag) < $min);
#        prt("$itcnt: $tag $msg\n");
#    }
#}

sub show_files_done($) {
    my $rh = shift;
    my $rfa = ${$rh}{'CMAKE_FILES'};
    my ($inf,$cnt);
    #push(@{$rfa},$inf);
    $cnt = scalar @{$rfa};
    prt("\nProcessed $cnt files...\n");
    $cnt = 0;
    foreach $inf (@{$rfa}) {
        $cnt++;
        prt("$cnt: $inf\n");
    }
}

sub load_cmake_mod_dir($$) {
    my ($dir,$rh) = @_; # $cmake_moddir,$$rel_hash);
    if (! opendir(DIR, $dir) ) {
        pgm_exit(1,"ERROR: Unable to open directory [$dir]\n");
    }
    my @files = readdir(DIR);
    closedir(DIR);
    my ($file,$ff);
    $dir .= $PATH_SEP if (!($dir =~ /(\\|\/)$/));
    my $svverb = $verbosity;
    $verbosity = 9 if ($dbg_08);
    foreach $file (@files) {
        next if (($file eq ".")||($file eq ".."));
        next if (!($file =~ /\.cmake$/));
        $ff = $dir.$file;
        path_form(\$ff);
        if (-f $ff) {
            process_in_file($ff,$rh);
        } else {
            pgm_exit(1,"ERROR: Stupid IMPOSSIBILITY! [$ff] DOES NOT EXIST!\n");
        }
    }
    $verbosity = $svverb;
}

sub load_installed_cmake($) {
    my $rh = shift;
    scan_cmake_install_paths($rh);
    my $rif = ${$rh}{'CMAKE_INSTALLED_FILES'};
    my $cnt = scalar @{$rif};
    prt("Got $cnt files to load...\n");
    my ($ff);
    foreach $ff (@{$rif}) {
        process_in_file($ff,$rh);
    }
}

#########################################
### MAIN ###
parse_args(@ARGV);
### prt( "$pgmname: in [$cwd]: Hello, World...\n" );
my $ref_hash = get_cmake_ref_hash();

if ($load_installed_cmake) {
    load_installed_cmake($ref_hash);
    pgm_exit(1,"TEMP EXIST");
}

if (-f $in_file) {
    my ($n,$d) = fileparse($in_file);
    $cmake_moddir = $d."CMakeModules";
    if ((-d $cmake_moddir) && $inc_all_cmake) {
        # load CMakeModule directory
        load_cmake_mod_dir($cmake_moddir,$$ref_hash);
    } elsif ($inc_all_cmake) {
        prtw("WARNING: No 'CMakeModules' directory EXISTS for this file! [$cmake_moddir]\n");
    }
    ${$ref_hash}{'PRIMARY_CMAKE_DIR'}  = $d;
    ${$ref_hash}{'PRIMARY_CMAKE_FILE'} = $in_file;
    process_in_file($in_file,$ref_hash);
    #$ref_hash = process_in_file($in_file);
} elsif (-d $in_file) {
    my $d = $in_file;
    fix_directory(\$d);
    $cmake_moddir = $d."CMakeModules";
    if ($inc_all_cmake) {
        if (-d $cmake_moddir) {
            # load CMakeModule directory
            load_cmake_mod_dir($cmake_moddir,$ref_hash);
        } else {
            prtw("WARNING: No [$cmake_moddir] EXISTS!\n");
        }
    }
    process_in_dir($in_file,$ref_hash);
    #$ref_hash = process_in_dir($in_file);
}

# should this be on a per FILE basis, or at the END
do_macro_subs($ref_hash);
prt("Processed totol $total_file_lines lines from $total_files CMakeLists.txt files...\n");

show_cmake_options($ref_hash) if ($show_options || $show_all);
show_install_actions($ref_hash) if ($show_installs || $show_all);
show_set_lists($ref_hash) if ($show_sets || $show_all);
show_list_sets($ref_hash) if ($show_lists || $show_all);
show_binary_items($ref_hash) if ($show_binaries || $show_all);
### show_if_tags($ref_hash) if ($show_if_tags || $show_all);
show_files_done($ref_hash) if (VERB1());

prt("Processed totol $total_file_lines lines from $total_files CMakeLists.txt files...\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 =~ /^b/) {
                $show_binaries = 1;
                prt("Set show 'ADD_EXECUTABLE' and 'ADD_LIBRARY' and sources.\n") if (VERB1());
            } elsif ($sarg =~ /^i/) {
                $show_installs = 1;
                prt("Set show 'INSTALL' actions.\n") if (VERB1());
            #} elsif ($sarg =~ /^I/) {
            #    $show_if_tags = 1;
            #    prt("Set show 'IF' tags.\n") if (VERB1());
            } elsif ($sarg =~ /^s/) {
                $show_sets = 1;
                prt("Set show 'SET' lists.\n") if (VERB1());
            } 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/) {
                $show_lists = 1;
                prt("Set show 'LIST' sets.\n") if (VERB1());
            } elsif ($sarg =~ /^l/) {
                $load_log = 1;
                prt("Set to load log at end.\n") if (VERB1());
            } elsif ($sarg =~ /^o/) {
                $show_options = 1;
                prt("Set to show 'OPTION' items.\n") if (VERB1());
            } else {
                pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n");
            }
        } else {
            $in_file = $arg;
            prt("Set input to [$in_file]\n") if (VERB1());
        }
        shift @av;
    }

    if ((length($in_file) ==  0) && $debug_on) {
        $in_file = $def_file;
        $load_log = 2;
        #$show_binaries = 1;
        #$show_lists = 1;
        #$show_sets = 1;
        set_warn_all();
    }
    if (length($in_file) ==  0) {
        pgm_exit(1,"ERROR: No input files found in command!\n");
    }
    if ((! -d $in_file) && (! -f $in_file)) {
        pgm_exit(1,"ERROR: Unable to find in directory or file [$in_file]! Check name, location...\n");
    }
}

sub get_LIST_info() {
    my $txt = <<EOF;
from : http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:list
list: List operations.
  list(LENGTH &gt;list&lt; &gt;output variable&lt;)
  list(GET &gt;list&lt; &gt;element index&lt; [&gt;element index&lt; ...] &gt;output variable&lt;)  
  list(APPEND &gt;list&lt; &gt;element&lt; [&gt;element&lt; ...])  
  list(FIND &gt;list&lt; &gt;value&lt; &gt;output variable&lt;)  
  list(INSERT &gt;list&lt; &gt;element_index&lt; &gt;element&lt; [&gt;element&lt; ...])  
  list(REMOVE_ITEM &gt;list&lt; &gt;value&lt; [&gt;value&lt; ...])  
  list(REMOVE_AT &gt;list&lt; &gt;index&lt; [&gt;index&lt; ...])  
  list(REMOVE_DUPLICATES &gt;list&lt;)  
  list(REVERSE &gt;list&lt;)  
  list(SORT &gt;list&lt;)

LENGTH will return a given list's length.
GET will return list of elements specified by indices from the list.
APPEND will append elements to the list.
FIND will return the index of the element specified in the list or -1 if it wasn't found.
INSERT will insert elements to the list to the specified location.
REMOVE_AT and REMOVE_ITEM will remove items from the list. The difference is that 
REMOVE_ITEM will remove the given items, while REMOVE_AT will remove the items at the given indices.
REMOVE_DUPLICATES will remove duplicated items in the list.
REVERSE reverses the contents of the list in-place.
SORT sorts the list in-place alphabetically.
 
The list subcommands APPEND, INSERT, REMOVE_AT, REMOVE_ITEM, REMOVE_DUPLICATES, REVERSE and SORT may create 
 new values for the list within the current CMake variable scope. Similar to the SET command, the LIST 
 command creates new variable values in the current scope, even if the list itself is actually defined in 
 a parent scope. To propagate the results of these operations upwards, use SET with PARENT_SCOPE, SET 
 with CACHE INTERNAL, or some other means of value propagation.
 
NOTES: A list in cmake is a ; separated group of strings. To create a list the set command can be used. 
 For example, set(var a b c d e) creates a list with a;b;c;d;e, and set(var "a b c d e") creates a string or 
 a list with one item in it.
 
When specifying index values, if &gt;element index&lt; is 0 or greater, it is indexed from the beginning 
 of the list, with 0 representing the first list element. If &gt;element index&lt; is -1 or lesser, it 
 is indexed from the end of the list, with -1 representing the last list element. Be careful when 
 counting with negative indices: they do not start from 0. -0 is equivalent to 0, the first list element.

EOF
    return $txt;
}

sub get_SET_info() {
    my $txt = <<EOF;
from : http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set
set: Set a CMAKE variable to a given value.
  set(&lt;variable&gt; &lt;value&gt; [[CACHE &lt;type&gt; &lt;docstring&gt; [FORCE]] | PARENT_SCOPE])
Within CMake sets &lt;variable&gt; to the value &lt;value&gt;. &lt;value&gt; is expanded before &lt;variable&gt; 
is set to it. If CACHE is present, then the &lt;variable&gt; is put in the cache. &lt;type&gt; and &lt;docstring&gt; 
are then required. &lt;type&gt; is used by the CMake GUI to choose a widget with which the user sets a value. 

The value for &lt;type&gt; may be one of
   FILEPATH = File chooser dialog.  PATH = Directory chooser dialog.  STRING = Arbitrary string. BOOL  Boolean ON/OFF checkbox.
   INTERNAL = No GUI entry (used for persistent variables).
If &lt;type&gt; is INTERNAL, then the &lt;value&gt; is always written into the cache, replacing any 
values existing in the cache. If it is not a cache variable, then this always writes into the current makefile. 
The FORCE option will overwrite the cache value removing any changes by the user.
 
If PARENT_SCOPE is present, the variable will be set in the scope above the current scope. 
Each new directory or function creates a new scope. This command will set the value of a variable into 
the parent directory or calling function (whichever is applicable to the case at hand).
 
If &lt;value&gt; is not specified then the variable is removed instead of set. See also: the unset() command.

   set(&lt;variable&gt; &lt;value1&gt; ... &lt;valueN&gt;)
In this case &lt;variable&gt; is set to a semicolon separated list of values.
 
&lt;variable&gt; can be an environment variable such as:
   set( ENV{PATH} /home/martink )
in which case the environment variable will be set.

EOF
    return $txt;
}

sub get_ADD_LIBRARY_info() {
    my $txt = <<EOF;
add_library: Add a library to the project using the specified source files.
  add_library(&lt;name&gt; [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)

Adds a library target called &lt;name&gt; to be built from the source files listed in 
the command invocation. The &lt;name&gt; corresponds to the logical target name and must 
be globally unique within a project. The actual file name of the library built is 
constructed based on conventions of the native platform (such as lib&lt;name&gt;.a or &lt;name&gt;.lib).
 
STATIC, SHARED, or MODULE may be given to specify the type of library to be created. STATIC libraries 
are archives of object files for use when linking other targets. SHARED libraries are linked 
dynamically and loaded at runtime. MODULE libraries are plugins that are not linked into other 
targets but may be loaded dynamically at runtime using dlopen-like functionality. If no type 
is given explicitly the type is STATIC or SHARED based on whether the current value of the 
variable BUILD_SHARED_LIBS is true.
 
By default the library file will be created in the build tree directory corresponding to the 
source tree directory in which the command was invoked. See documentation of the 
ARCHIVE_OUTPUT_DIRECTORY, LIBRARY_OUTPUT_DIRECTORY, and RUNTIME_OUTPUT_DIRECTORY target 
properties to change this location. See documentation of the OUTPUT_NAME target property 
to change the &lt;name&gt; part of the final file name. 

If EXCLUDE_FROM_ALL is given the corresponding property will be set on the created target. 
See documentation of the EXCLUDE_FROM_ALL target property for details.
 
The add_library command can also create IMPORTED library targets using this signature:

   add_library(&lt;name&gt; &lt;SHARED|STATIC|MODULE|UNKNOWN&gt; IMPORTED [GLOBAL])
An IMPORTED library target references a library file located outside the project. No rules 
are generated to build it. The target name has scope in the directory in which it is created 
and below, but the GLOBAL option extends visibility. It may be referenced like any target built 
within the project. IMPORTED libraries are useful for convenient reference from commands like 
target_link_libraries. Details about the imported library are specified by setting properties 
whose names begin in "IMPORTED_". The most important such property is IMPORTED_LOCATION 
(and its per-configuration version IMPORTED_LOCATION_&lt;CONFIG&gt;) which specifies the 
location of the main library file on disk. See documentation of the IMPORTED_* properties 
for more information.
 
The signature
   add_library(&lt;name&gt; OBJECT &lt;src&gt;...)
creates a special "object library" target. An object library compiles source files but does 
not archive or link their object files into a library. Instead other targets created by 
add_library or add_executable may reference the objects using an expression of the 
form $&lt;TARGET_OBJECTS:objlib&gt; as a source, where "objlib" is the object library 
name. For example:
   add_library(... $&lt;TARGET_OBJECTS:objlib&gt; ...)  add_executable(... $&lt;TARGET_OBJECTS:objlib&gt; ...)
will include objlib's object files in a library and an executable along with those compiled from 
their own sources. Object libraries may contain only sources (and headers) that compile to 
object files. They may contain custom commands generating such sources, but not PRE_BUILD, 
PRE_LINK, or POST_BUILD commands. Object libraries cannot be imported, exported, 
installed, or linked.

EOF
    return $txt;
}

sub get_ADD_EXECUTABLE_info() {
    my $txt = <<EOF;
add_executable: Add an executable to the project using the specified source files.
  add_executable(&lt;name&gt; [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
Adds an executable target called &lt;name&gt; to be built from the source files listed 
in the command invocation. The &lt;name&gt; corresponds to the logical target name and must 
be globally unique within a project. The actual file name of the executable built is constructed 
based on conventions of the native platform (such as &lt;name&gt;.exe or just &lt;name&gt;). 

By default the executable file will be created in the build tree directory corresponding to 
the source tree directory in which the command was invoked. See documentation of the 
RUNTIME_OUTPUT_DIRECTORY target property to change this location. See documentation of the 
OUTPUT_NAME target property to change the &lt;name&gt; part of the final file name. 

If WIN32 is given the property WIN32_EXECUTABLE will be set on the target created. See 
documentation of that target property for details.
 
If MACOSX_BUNDLE is given the corresponding property will be set on the created target. 
See documentation of the MACOSX_BUNDLE target property for details.
 
If EXCLUDE_FROM_ALL is given the corresponding property will be set on the created target. 
See documentation of the EXCLUDE_FROM_ALL target property for details.
 
The add_executable command can also create IMPORTED executable targets using this signature:
   add_executable(&lt;name&gt; IMPORTED [GLOBAL])
An IMPORTED executable target references an executable file located outside the project. 
No rules are generated to build it. The target name has scope in the directory in which 
it is created and below, but the GLOBAL option extends visibility. It may be referenced 
like any target built within the project. IMPORTED executables are useful for convenient 
reference from commands like add_custom_command. Details about the imported executable 
are specified by setting properties whose names begin in "IMPORTED_". The most important 
such property is IMPORTED_LOCATION (and its per-configuration version 
IMPORTED_LOCATION_&lt;CONFIG&gt;) which specifies the location of the main executable 
file on disk. See documentation of the IMPORTED_* properties for more information.

EOF
    return $txt;
}

sub get_MACRO_info() {
    my $txt = <<EOF;
from : http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:macro
macro: Start recording a macro for later invocation as a command.
  macro(&lt;name&gt; [arg1 [arg2 [arg3 ...]]])
    COMMAND1(ARGS ...)
    COMMAND2(ARGS ...)
    ...
  endmacro(&lt;name&gt;)

Define a macro named &lt;name&gt; that takes arguments named arg1 arg2 arg3 (...). Commands 
listed after macro, but before the matching endmacro, are not invoked until the macro is 
invoked. When it is invoked, the commands recorded in the macro are first modified by 
replacing formal parameters (\${arg1}) with the arguments passed, and then invoked as 
normal commands. In addition to referencing the formal parameters you can reference 
the values \${ARGC} which will be set to the number of arguments passed into the 
function as well as \${ARGV0} \${ARGV1} \${ARGV2} ... which will have the actual values 
of the arguments passed in. This facilitates creating macros with optional arguments. 

Additionally \${ARGV} holds the list of all arguments given to the macro and \${ARGN} holds 
the list of argument past the last expected argument. Note that the parameters to a macro 
and values such as ARGN are not variables in the usual CMake sense. They are string 
replacements much like the c preprocessor would do with a macro. If you want true CMake 
variables you should look at the function command.
 
See the cmake_policy() command documentation for the behavior of policies inside macros.

EOF
    return $txt;
}

sub get_FOREACH_info() {
    my $txt = <<EOF;

foreach: Evaluate a group of commands for each value in a list.
  foreach(loop_var arg1 arg2 ...)    COMMAND1(ARGS ...)    COMMAND2(ARGS ...)    ...  endforeach(loop_var)
All commands between foreach and the matching endforeach are recorded without being invoked. 
Once the endforeach is evaluated, the recorded list of commands is invoked once for each argument 
listed in the original foreach command. Before each iteration of the loop "\${loop_var}" will be set as a 
variable with the current value in the list.

   foreach(loop_var RANGE total)  foreach(loop_var RANGE start stop [step])
Foreach can also iterate over a generated range of numbers. There are three types of this iteration:
 
* When specifying single number, the range will have elements 0 to "total".
* When specifying two numbers, the range will have elements from the first number to the second number.
* The third optional number is the increment used to iterate from the first number to the second number.
   foreach(loop_var IN [LISTS [list1 [...]]] [ITEMS [item1 [...]]])
Iterates over a precise list of items. The LISTS option names list-valued variables to be traversed, 
including empty elements (an empty string is a zero-length list). The ITEMS option ends argument 
parsing and includes all arguments following it in the iteration.

EOF
    return $txt;
}

sub get_CHECK_CXX_SOURCE_RUNS_info() {
    my $txt = <<EOF;
from : http://www.cmake.org/cmake/help/cmake2.4docs.html
Check_CXX_Source_Runs: macro which checks if the source code compiles

CHECK_CXX_SOURCE_RUNS(SOURCE VAR)
  SOURCE - source code to try to compile  
  VAR    - variable to store size if the type exists.

The following variables may be set before calling this macro to modify the way the check is run:
  CMAKE_REQUIRED_FLAGS = string of compile command line flags  
  CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar)  
  CMAKE_REQUIRED_INCLUDES = list of include directories  
  CMAKE_REQUIRED_LIBRARIES = list of libraries to link

EOF
    return $txt;
}

sub get_FILE_info() {
    my $txt = <<EOF;
from : http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:file
file: File manipulation command.
  file(WRITE filename "message to write"... )
  file(APPEND filename "message to write"... )
  file(READ filename variable [LIMIT numBytes] [OFFSET offset] [HEX])
  file(<MD5|SHA1|SHA224|SHA256|SHA384|SHA512> filename variable)
  file(STRINGS filename variable [LIMIT_COUNT num] [LIMIT_INPUT numBytes] [LIMIT_OUTPUT numBytes]
    [LENGTH_MINIMUM numBytes] [LENGTH_MAXIMUM numBytes] [NEWLINE_CONSUME] [REGEX regex] [NO_HEX_CONVERSION])
  file(GLOB variable [RELATIVE path] [globbing expressions]...)  
  file(GLOB_RECURSE variable [RELATIVE path] [FOLLOW_SYMLINKS] [globbing expressions]...)
  file(RENAME <oldname> <newname>)  
  file(REMOVE [file1 ...])  
  file(REMOVE_RECURSE [file1 ...])  
  file(MAKE_DIRECTORY [directory1 directory2 ...])  
  file(RELATIVE_PATH variable directory file)  
  file(TO_CMAKE_PATH path result)  
  file(TO_NATIVE_PATH path result)  
  file(DOWNLOAD url file [INACTIVITY_TIMEOUT timeout] [TIMEOUT timeout] [STATUS status] [LOG log] [SHOW_PROGRESS]
   [EXPECTED_MD5 sum])
  file(UPLOAD filename url [INACTIVITY_TIMEOUT timeout] [TIMEOUT timeout] [STATUS status] [LOG log] [SHOW_PROGRESS])

WRITE will write a message into a file called 'filename'. It overwrites the file if it already exists, 
and creates the file if it does not exist.
 
APPEND will write a message into a file same as WRITE, except it will append it to the end of the file
 
READ will read the content of a file and store it into the variable. It will start at the given offset 
and read up to numBytes. If the argument HEX is given, the binary data will be converted to hexadecimal 
representation and this will be stored in the variable.
 
MD5, SHA1, SHA224, SHA256, SHA384, and SHA512 will compute a cryptographic hash of the content of a file.
 
STRINGS will parse a list of ASCII strings from a file and store it in a variable. Binary data in the 
file are ignored. Carriage return (CR) characters are ignored. It works also for Intel Hex and Motorola 
S-record files, which are automatically converted to binary format when reading them. Disable this 
using NO_HEX_CONVERSION.
 
LIMIT_COUNT sets the maximum number of strings to return. LIMIT_INPUT sets the maximum number of bytes 
to read from the input file. LIMIT_OUTPUT sets the maximum number of bytes to store in the output variable. 
LENGTH_MINIMUM sets the minimum length of a string to return. Shorter strings are ignored. 
LENGTH_MAXIMUM sets the maximum length of a string to return. Longer strings are split into strings no 
longer than the maximum length. NEWLINE_CONSUME allows newlines to be included in strings instead of 
terminating them.
 
REGEX specifies a regular expression that a string must match to be returned. 

Typical usage 
  file(STRINGS myfile.txt myfile)
stores a list in the variable "myfile" in which each item is a line from the input file.
 
GLOB will generate a list of all files that match the globbing expressions and store it 
into the variable. Globbing expressions are similar to regular expressions, but much simpler. 
If RELATIVE flag is specified for an expression, the results will be returned as a relative path 
to the given path. (We do not recommend using GLOB to collect a list of source files from your 
source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated 
build system cannot know when to ask CMake to regenerate.)
 
Examples of globbing expressions include:
    *.cxx      - match all files with extension cxx   
    *.vt?      - match all files with extension vta,...,vtz   
    f[3-5].txt - match files f3.txt, f4.txt, f5.txt

GLOB_RECURSE will generate a list similar to the regular GLOB, except it will traverse all the 
subdirectories of the matched directory and match the files. Subdirectories that are symlinks are 
only traversed if FOLLOW_SYMLINKS is given or cmake policy CMP0009 is not set to NEW. See 
cmake --help-policy CMP0009 for more information.
 
Examples of recursive globbing include:
    /dir/*.py  - match all python files in /dir and subdirectories

MAKE_DIRECTORY will create the given directories, also if their parent directories don't exist yet
 
RENAME moves a file or directory within a filesystem, replacing the destination atomically.
 
REMOVE will remove the given files, also in subdirectories
 
REMOVE_RECURSE will remove the given files and directories, also non-empty directories
 
RELATIVE_PATH will determine relative path from directory to the given file.
 
TO_CMAKE_PATH will convert path into a cmake style path with unix /. The input can be a single path 
or a system path like "$ENV{PATH}". Note the double quotes around the ENV call TO_CMAKE_PATH only 
takes one argument. This command will also convert the native list delimiters for a list of paths 
like the PATH environment variable.
 
TO_NATIVE_PATH works just like TO_CMAKE_PATH, but will convert from a cmake style path into the 
native path style \ for windows and / for UNIX.
 
DOWNLOAD will download the given URL to the given file. If LOG var is specified a log of the 
download will be put in var. If STATUS var is specified the status of the operation will be 
put in var. The status is returned in a list of length 2. The first element is the numeric 
return value for the operation, and the second element is a string value for the error. 
A 0 numeric error means no error in the operation. If TIMEOUT time is specified, the operation 
will timeout after time seconds, time should be specified as an integer. The INACTIVITY_TIMEOUT 
specifies an integer number of seconds of inactivity after which the operation should terminate. 
If EXPECTED_MD5 sum is specified, the operation will verify that the downloaded file's actual md5 
sum matches the expected value. If it does not match, the operation fails with an error. If 
SHOW_PROGRESS is specified, progress information will be printed as status messages until the 
operation is complete.
 
UPLOAD will upload the given file to the given URL. If LOG var is specified a log of the upload 
will be put in var. If STATUS var is specified the status of the operation will be put in var. The 
status is returned in a list of length 2. The first element is the numeric return value for the 
operation, and the second element is a string value for the error. A 0 numeric error means no error 
in the operation. If TIMEOUT time is specified, the operation will timeout after time seconds, 
time should be specified as an integer. The INACTIVITY_TIMEOUT specifies an integer number of 
seconds of inactivity after which the operation should terminate. If SHOW_PROGRESS is specified, 
progress information will be printed as status messages until the operation is complete.
 
The file() command also provides COPY and INSTALL signatures:
   file(<COPY|INSTALL> files... DESTINATION <dir> [FILE_PERMISSIONS permissions...] 
     [DIRECTORY_PERMISSIONS permissions...] [NO_SOURCE_PERMISSIONS] [USE_SOURCE_PERMISSIONS]
     [FILES_MATCHING] [[PATTERN <pattern> | REGEX <regex>] [EXCLUDE] [PERMISSIONS permissions...]] [...])
The COPY signature copies files, directories, and symlinks to a destination folder. Relative input paths 
are evaluated with respect to the current source directory, and a relative destination is evaluated with 
respect to the current build directory. Copying preserves input file timestamps, and optimizes out a file 
if it exists at the destination with the same timestamp. Copying preserves input permissions unless explicit 
permissions or NO_SOURCE_PERMISSIONS are given (default is USE_SOURCE_PERMISSIONS). See the install(DIRECTORY) 
command for documentation of permissions, PATTERN, REGEX, and EXCLUDE options. 

The INSTALL signature differs slightly from COPY: it prints status messages, and NO_SOURCE_PERMISSIONS is 
default. Installation scripts generated by the install() command use this signature (with some undocumented 
options for internal use).

EOF
    return $txt
}

sub get_IF_info() {
    my $txt = <<EOF;
from : http://www.cmake.org/cmake/help/v2.8.8/cmake.html#command:if

if: Conditionally execute a group of commands.
  if(expression)    # then section.
   COMMAND1(ARGS ...)
   COMMAND2(ARGS ...)
   ...
elseif(expression2)    # elseif section.
   COMMAND1(ARGS ...)
   COMMAND2(ARGS ...)
   ...
else(expression)    # else section.
   COMMAND1(ARGS ...)
   COMMAND2(ARGS ...)
   ...
endif(expression)

Evaluates the given expression. If the result is true, the commands in the THEN section are 
invoked. Otherwise, the commands in the else section are invoked. The elseif and else sections 
are optional. You may have multiple elseif clauses. Note that the expression in the else and 
endif clause is optional. 

Long expressions can be used and there is a traditional order of precedence. Parenthetical 
expressions are evaluated first followed by unary operators such as EXISTS, COMMAND, and DEFINED. 

Then any EQUAL, LESS, GREATER, STRLESS, STRGREATER, STREQUAL, MATCHES will be evaluated. 

Then NOT operators and finally AND, OR operators will be evaluated.

Possible expressions are:
   if(<constant>)
True if the constant is 1, ON, YES, TRUE, Y, or a non-zero number. False if the constant 
is 0, OFF, NO, FALSE, N, IGNORE, "", or ends in the suffix '-NOTFOUND'. Named boolean constants 
are case-insensitive. If the argument is not one of these constants, it is treated as a variable:

   if(<variable>)
True if the variable is defined to a value that is not a false constant. False otherwise. 

  if(NOT <expression>)
True if the expression is not true.

   if(<expr1> AND <expr2>)
True if both expressions would be considered true individually.

   if(<expr1> OR <expr2>)
True if either expression would be considered true individually.

   if(COMMAND command-name)
True if the given name is a command, macro or function that can be invoked.

   if(POLICY policy-id)
True if the given name is an existing policy (of the form CMP<NNNN>).

   if(TARGET target-name)
True if the given name is an existing target, built or imported.

   if(EXISTS file-name)
   if(EXISTS directory-name)
True if the named file or directory exists. Behavior is well-defined only for full paths.

   if(file1 IS_NEWER_THAN file2)
True if file1 is newer than file2 or if one of the two files doesn't exist. Behavior 
is well-defined only for full paths.

   if(IS_DIRECTORY directory-name)
True if the given name is a directory. Behavior is well-defined only for full paths.

   if(IS_SYMLINK file-name)
True if the given name is a symbolic link. Behavior is well-defined only for full paths.

   if(IS_ABSOLUTE path)
True if the given path is an absolute path.

   if(<variable|string> MATCHES regex)
True if the given string or variable's value matches the given regular expression.

   if(<variable|string> LESS <variable|string>)
   if(<variable|string> GREATER <variable|string>)  
   if(<variable|string> EQUAL <variable|string>)
True if the given string or variable's value is a valid number and the inequality or equality is true.

   if(<variable|string> STRLESS <variable|string>)
   if(<variable|string> STRGREATER <variable|string>)
   if(<variable|string> STREQUAL <variable|string>)
True if the given string or variable's value is lexicographically less (or greater, or equal) 
than the string or variable on the right.

   if(<variable|string> VERSION_LESS <variable|string>)
   if(<variable|string> VERSION_EQUAL <variable|string>)
   if(<variable|string> VERSION_GREATER <variable|string>)
Component-wise integer version number comparison (version format is major[.minor[.patch[.tweak]]]).

   if(DEFINED <variable>)
True if the given variable is defined. It does not matter if the variable is true or false 
just if it has been set.

   if((expression) AND (expression OR (expression)))
The expressions inside the parenthesis are evaluated first and then the remaining expression 
is evaluated as in the previous examples. Where there are nested parenthesis the innermost are 
evaluated as part of evaluating the expression that contains them.

The if command was written very early in CMake's history, predating the \${} variable evaluation 
syntax, and for convenience evaluates variables named by its arguments as shown in the above 
signatures. Note that normal variable evaluation with \${} applies before the if command even 
receives the arguments. Therefore code like

   set(var1 OFF)
   set(var2 "var1")
   if(\${var2})
appears to the if command as

   if(var1)
and is evaluated according to the if(<variable>) case documented above. The result 
is OFF which is false. However, if we remove the \${} from the example then the command sees

   if(var2)
which is true because var2 is defined to "var1" which is not a false constant.
 
Automatic evaluation applies in the other cases whenever the above-documented signature 
accepts <variable|string>:
 
1) The left hand argument to MATCHES is first checked to see if it is a defined variable, 
if so the variable's value is used, otherwise the original value is used. 

2) If the left hand argument to MATCHES is missing it returns false without error 

3) Both left and right hand arguments to LESS GREATER EQUAL are independently tested to 
see if they are defined variables, if so their defined values are used otherwise the original 
value is used. 

4) Both left and right hand arguments to STRLESS STREQUAL STRGREATER are independently tested 
to see if they are defined variables, if so their defined values are used otherwise the 
original value is used. 

5) Both left and right hand argumemnts to VERSION_LESS VERSION_EQUAL VERSION_GREATER are 
independently tested to see if they are defined variables, if so their defined values are 
used otherwise the original value is used. 

6) The right hand argument to NOT is tested to see if it is a boolean constant, if so the 
value is used, otherwise it is assumed to be a variable and it is dereferenced. 

7) The left and right hand arguments to AND OR are independently tested to see if they 
are boolean constants, if so they are used as such, otherwise they are assumed to be 
variables and are dereferenced. 

EOF
    return $txt;
}



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(" --bin         (-b) = Show binary items and sources.\n");
    prt(" --load        (-l) = Set to load log at end.\n");
    prt(" --List        (-L) = Show 'LIST' sets.\n");
    prt(" --install     (-i) = Show 'INSTALL' actions.\n");
    ###prt(" --IF          (-I) = Show 'IF' tags.\n");
    prt(" --options     (-o) = Show 'OPTION' list.\n");
    prt(" --set         (-s) = Show 'SET' lists.\n");
    prt(" --verb[n]     (-v) = Bump [or set] verbosity. def=$verbosity\n");
}

# eof - chkcmake.pl
