#!/usr/bin/perl -w
# NAME: log2cmake.pl
# AIM: Take a unix build log, and attempt to create a CMakeLists.txt from it, hopefully...
# 23/12/2013 - Some more tweaks using unix-bldlog.txt for gmp library
# 25/10/2013 - If given a current base, use a full directory scan to search for source files
# 02/10/2013 - Add -b base_directory used in log file
# 27/08/2013 - Lots more work with different styles build log files, and fill out the cmake script
# 20/08/2013 - gcc lines can be a compile or a link
# 13/03/2013 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";
# 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.1 2013-03-13";
my $VERS = "0.0.2 2013-08-27";
my $load_log = 0;
my $in_file = '';
my $verbosity = 0;
my $out_file = '';
my $base_dir = '';
my $src_dir = '';
my $proj_name = '';
my $tmp_out = $temp_dir.$PATH_SEP."tempCMakeLists.txt";
my $show_ignored = 0;
my $show_finding = 0;
my $min_source = 2; # if 2 or less, no new line
my $skip_test_block = 1;    # skip begin '`' until next

# ### DEBUG ###
my $db_shw_mhdrs = 0;
my $debug_on = 0;
my $def_file = 'C:\FG\18\ELFkickers\build\ELFkickers\bldlog-u.txt';
my $def_base = '/home/geoff/projects/ELFkickers';
my $def_src = "C:\\FG\\18\\ELFkickers";
#my $def_file = 'C:\FG\18\gettext-0.18.1.1\build\bldlog-u.txt';
#my $def_file = 'C:\FG\17\wget-1.14\bldlog-1.txt';
#my $def_base = '/home/geoff/projects/gettext-0.18.1.1';
#my $def_src = "C:\\FG\\18\\gettext-0.18.1.1";

### program variables
my @warnings = ();
my $cwd = cwd();
my $curr_rel_dir = '';
my $curr_abs_dir = '';
my $chk_src_file = 0;
my %entered_dirs = ();

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

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

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


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

# ==============================================
# 10/04/2012 - begin to process files with a BOM
# LOAD without a BOM
my $strip_bom = 1;
my $curr_file_bom = '';

my @BOM_list = (
    [ "UTF-8",       3, [0xEF,0xBB,0xBF     ] ], # 239 187 191   
    [ "UTF-16 (BE)", 2, [0xFE,0xFF          ] ], # 254 255 
    [ "UTF-16 (LE)", 2, [0xFF,0xFE          ] ], # 255 254
    [ "UTF-32 (BE)", 4, [0x00,0x00,0xFE,0xFF] ], # 0 0 254 255
    [ "UTF-32 (LE)", 4, [0xFF,0xFE,0x00,0x00] ], # 255 254 0 0
    [ "UTF-7a"     , 4, [0x2B,0x2F,0x76,0x38] ], # 2B 2F 76 39  2B 2F 76 2B  2B 2F 76 2F
    [ "UTF-7b"     , 4, [0x2B,0x2F,0x76,0x39] ], # 2B 2F 76 39  2B 2F 76 2B  2B 2F 76 2F
    [ "UTF-7c"     , 4, [0x2B,0x2F,0x76,0x2B] ], # 2B 2F 76 39  2B 2F 76 2B  2B 2F 76 2F
    [ "UTF-7d"     , 4, [0x2B,0x2F,0x76,0x2F] ], # 2B 2F 76 39  2B 2F 76 2B  2B 2F 76 2F
    [ "UTF-1"      , 3, [0xF7,0x64,0x4C     ] ], # 247 100 76 
    [ "UTF-EBCDIC" , 4, [0xDD,0x73,0x66,0x73] ], # 221 115 102 115
    [ "SCSU"       , 3, [0x0E,0xFE,0xFF     ] ], # 14 254 255
    [ "BOCU-1"     , 3, [0xFB,0xEE,0x28     ] ], # 251 238 40
    [ "GB-18030"   , 4, [0x84,0x31,0x95,0x33] ]  # 132 49 149 51
);

sub line_has_bom($$) {
    my ($line,$rname) = @_;
    my $max = scalar @BOM_list;
    my $len = length($line);
    my ($i,$j,$name,$cnt,$ra,$ch,$val);
    for ($i = 0; $i < $max; $i++) {
        $name = $BOM_list[$i][0]; # name
        $cnt  = $BOM_list[$i][1]; # length
        $ra   = $BOM_list[$i][2]; # ref array of values
        if ($len > $cnt) {  # make sure line length GT BOM
            for ($j = 0; $j < $cnt; $j++) {
                $ch = substr($line,$j,1);   # extract CHAR
                $val = ord($ch);            # get VALUE
                last if ($val != ${$ra}[$j]); # compare
            }
            if ($j == $cnt) {   # if ALL values found
                ${$rname} = $name;  # give back 'name'
                return $cnt;    # and return count
            }
        }
    }
    return 0;   # no BOM found
}

sub remove_utf_bom($$) {
    my ($ff,$ra) = @_;
    my $line = ${$ra}[0];  # get first line
    my $name = '';
    my $len = line_has_bom($line,\$name);
    if ($len) {
        $curr_file_bom = substr($line,0,$len);
        $line = substr($line,$len); # truncate line
        ${$ra}[0] = $line;  # and return minus BOM
        my ($nm,$dr) = fileparse($ff); # just show name
        prt("[v9] NOTE: File [$nm] is $name encoding. BOM($len) removed.\n") if (VERB9());
    }
}

sub load_file_lines($$) {
    my ($ff,$ra) = @_;
    my $lncnt = 0;
    $curr_file_bom = '';
    if (open INF, "<$ff") {
        @{$ra} = <INF>;
        close INF;
        $lncnt = scalar @{$ra};
        remove_utf_bom($ff,$ra) if ($strip_bom);
    } else {
        prtw("WARNING: Unable to open [$ff]!\n");
    }
    return $lncnt;
}



sub get_show_stg($) {
    my $txt = shift;
    my $len = length($txt);
    if ($len < 103) {
        return $txt;
    }
    my $ntxt = substr($txt,0,50);
    $ntxt .= "...";
    $ntxt .= substr($txt,length($txt)-50);
    return $ntxt;
}

my @curr_srcs = ();

sub strip_src_dir($) {
    my $dir = shift;
    my $len = length($src_dir);
    return $dir if ($len == 0);
    return $dir if (length($dir) < $len);
    my ($i);
    for ($i = 0; $i < $len; $i++) {
        if (substr($dir,$i,1) ne substr($src_dir,$i,1)) {
            prtw("WARNING: Failed match at $i on $len [$dir] [$src_dir]\n");
            return $dir;
        }
    }
    $dir = substr($dir,$len);
    $dir =~ s/^(\\|\/)//;
    return $dir;
}

my $curr_lnn = 0;
my $curr_tline = '';

sub strip_comp_line($$) {
    my ($txt,$rh) = @_;
    my @arr = space_split($txt);
    my @narr = ();
    my $cnt = scalar @arr;
    my ($i,$tmp);
    for ($i = 0; $i < $cnt; $i++) {
        $txt = $arr[$i];
        next if ($txt =~ /^-std=/);
        next if ($txt =~ /^-D/);
        next if ($txt =~ /^-I/);
        next if ($txt =~ /^-fPIC/);
        next if ($txt =~ /^\d*>/);
        next if ($txt =~ /^-O/);
        next if ($txt =~ /^-g/);
        next if ($txt =~ /^-fvis/);
        if ($txt eq '||') {
            last;
        }

        if ( ($txt =~ /^`test/) && (($i + 2) < $cnt) ) {
            # could be `test -f 'gettext.c'
            $tmp = $arr[$i+2];
            $tmp =~ s/^\'//;
            $tmp =~ s/\'$//;
            if ( is_c_source($tmp) ) {
                $txt = $tmp;
                $i += 2;
            }
            #prt(join(" ",@arr)."\n");
            #pgm_exit(1, "Got [$txt] [$tmp]\n");
        }

        push(@narr,$txt);
        next if ($txt =~ /^-/);

        if ( is_c_source($txt) ) {
            if ($chk_src_file) {
                my $ff = $txt;
                my $ok = 'NOT FOUND';
                ### prt("CHECK SOURCE [$txt]\n");
                if (check_for_source($txt,\@curr_srcs,$curr_lnn,$curr_tline,\$ff,"strip_comp")) {
                    $ok = 'ok';
                } else {
                    $ff = $txt;
                }
                #my $ff = fix_rel_path($curr_abs_dir.$PATH_SEP.$txt);
                #if (-f $ff) {
                #    $ok = 'ok';
                #    $ff = strip_src_dir($ff);
                #    push(@curr_srcs,$ff);
                #}
                #prt("SRC: $ff $ok\n");
                ${$rh}{$ff} = $ok;
            } else {
                ${$rh}{$txt} = 1;
            }
        }
    }
    $txt = join(" ",@narr);
    return $txt;
}

sub strip_link_line($) {
    my $txt = shift;
    my @arr = space_split($txt);
    my @narr = ();
    foreach $txt (@arr) {
        next if ($txt =~ /^-std=/);
        next if ($txt =~ /^-D/);
        next if ($txt =~ /^-g$/);
        next if ($txt =~ /^-O/);
        next if ($txt eq '-lc');
        next if ($txt =~ /^-Wl,/);
        next if ($txt =~ /^-shared/);
        next if ($txt =~ /^-L/);
        next if ($txt eq '-nostdlib');
        push(@narr,$txt);
    }
    $txt = join(" ",@narr);
    return $txt;
}

sub strip_base_dir($) {
    my $dir = shift;
    my $len = length($base_dir);
    return $dir if ($len == 0);
    return $dir if (length($dir) < $len);
    my ($i);
    for ($i = 0; $i < $len; $i++) {
        if (substr($dir,$i,1) ne substr($base_dir,$i,1)) {
            prtw("WARNING: Failed match at $i on $len [$dir] [$base_dir]\n");
            return $dir;
        }
    }
    $dir = substr($dir,$len);
    return $dir;
}

# if 'gcc -Wall ...  -c -o dynamic.o dynamic.c' is COMPILE
# if 'gcc ebfc.o brainfuck.o libelfparts.a -o ebfc' is LINK
sub is_compile_line($) {
    my $line = shift;
    return 1 if (($line =~ /\s+-c\s+/)&&($line =~ /\s+-o\s+/));
    # but sometimes there is NO output -o!!! like
    # gcc -c -g -O2 -W -Wall -std=c99 -pedantic -DHAVE_CONFIG_H -I. -I.. rdfdump.c] FIX ME
    return 1 if (($line =~ /\s+-c\s+/)&&($line =~ /\s+-std=/));
    return 0;
}
sub is_link_line($) {
    my $line = shift;
    return 0 if ($line =~ /\s+-c\s+/);
    return 1 if ($line =~ /\s+-o\s+/);
    return 0;
}

my @cfile_exts = qw( .c .cxx .cpp .cc );
my @hfile_exts = qw( .h .hxx .hpp );
my %done_txt = ();

my $done_scan = 0;
my $dirs_found = 0;
my $files_found = 0;
my %dir_scan = ();
sub scan_directory($$);
sub scan_directory($$) {
    my ($dir,$lev) = @_;
    if (! opendir(DIR,$dir) ) {
        prtw("WARNING: Failed to open directory $dir!\n");
        return; # nothing to do but return
    }
    my @files = readdir(DIR);
    closedir(DIR);
    my ($file,$ff,$ra);
    ut_fix_directory(\$dir);
    my @dirs = ();
    $dirs_found++;
    foreach $file (@files) {
        next if ($file eq '.');
        next if ($file eq '..');
        $ff = $dir.$file;
        if (-d $ff) {
            push(@dirs,$ff);
        } elsif (-f $ff) {
            # keep everything, or just likely sources - no EVERYTHING
            # keep as hash or array - choose array, since can push full and file name
            # but a hash on file name with an array of locations seems best
            $dir_scan{$file} = [] if (!defined $dir_scan{$file});
            $ra = $dir_scan{$file};
            push(@{$ra},$dir);   # this file was found in this array of directories
            $files_found++;
        } else {
            pgm_exit(1,"ERROR: WHAT IS THIS [$ff] if not file or folder!!! FIX ME!!!\n");
        }
    }
    foreach $ff (@dirs) {
        scan_directory($ff,($lev+1));
    }
    if ($lev == 0) {
        prt("In scan of $dirs_found Directories, found $files_found files...\n");
    }
}


sub scan_base() {
    $done_scan = 1;
    scan_directory($src_dir,0);
}


sub find_this_file($$) {
    my ($fil,$rff) = @_;
    scan_base() if (!$done_scan);
    my ($ra,$hint,$ff);
    $hint = ${$rff};
    if (defined $dir_scan{$fil}) {
        $ra = $dir_scan{$fil};
        # TODO: for now take the first even if more than one
        $ff = ${$ra}[0];    # get the directory
        $ff .= $fil;        # add the file
        if (-f $ff) {
            ${$rff} = $ff;
            return 1;
        } else {
            pgm_exit(1,"ERROR: A previously found file now NOT FOUND\nFile [$ff] FIX ME\n");
        }
    }
    return 0;
}

my %shown_failed_find = ();
sub check_for_source($$$$$$) {
    my ($txt_in,$ra,$lnn,$tline,$retff,$caller) = @_;
    my $ff = fix_rel_path($curr_abs_dir.$PATH_SEP.$txt_in);
    my $ok = 'NOT FOUND';
    my ($tmp,$fil,$rff,@tried);
    my ($n,$d,$e);
    my ($name,$drv,$ext) = fileparse($txt_in, qr/\.[^.]*/);
    if (-f $ff) {
        $ok = 'ok';
        $rff = strip_src_dir($ff);
        push(@{$ra},$rff);
        ${$retff} = $rff;  # return source file found
        prt("$lnn:1: SRC: $rff $ok ($ff) [$txt_in]\n") if (VERB5());
        ($n,$d,$e) = fileparse($ff, qr/\.[^.]*/);
        foreach $tmp (@hfile_exts) {
            #$fil = $d.$n.$tmp;
            $fil = $drv.$n.$tmp;
            $ff = fix_rel_path($curr_abs_dir.$PATH_SEP.$fil);
            $ok = 'NOT FOUND';
            push(@tried,$ff);
            if (-f $ff) {
                $ok = 'ok';
                $rff = strip_src_dir($ff);
                push(@{$ra},$rff);
                prt("$lnn:1: HDR: $rff $ok ($ff)\n") if (VERB5());
                return 2;
            }
        }
        if (($db_shw_mhdrs || VERB9()) && !(defined $done_txt{$txt_in})) {
            prt("$lnn:1: NO HEADER for [$txt_in]! Tried\n");
            prt(join("\n",@tried)."\n");
            $done_txt{$txt_in} = 1;
        }
        return 1;
    }
    # =========================================================
    # maybe it was a object source, with the .o or .lo stripped
    foreach $tmp (@cfile_exts) {
        $fil = $txt_in.$tmp;
        $ff = fix_rel_path($curr_abs_dir.$PATH_SEP.$fil);
        $ok = 'NOT FOUND';
        if (-f $ff) {
            $ok = 'ok';
            $rff = strip_src_dir($ff);
            push(@{$ra},$rff);
            ${$retff} = $rff;
            prt("$lnn:2: SRC: $rff $ok ($ff) [$txt_in]\n") if (VERB5());
            ($n,$d,$e) = fileparse($ff, qr/\.[^.]*/);
            @tried = ();
            foreach $tmp (@hfile_exts) {
                ###$fil = $d.$n.$tmp;
                $fil = $drv.$n.$tmp;
                $ff = fix_rel_path($curr_abs_dir.$PATH_SEP.$fil);
                push(@tried,$ff);
                $ok = 'NOT FOUND';
                if (-f $ff) {
                    $ok = 'ok';
                    $rff = strip_src_dir($ff);
                    push(@{$ra},$rff);
                    prt("$lnn:2: HDR: $rff $ok ($ff)\n") if (VERB5());
                    return 2;
                }
            }
            if (($db_shw_mhdrs || VERB9()) && !(defined $done_txt{$txt_in})) {
                prt("$lnn:2: NO HEADER for [$txt_in]! Tried\n");
                prt(join("\n",@tried)."\n");
            }
            return 1;
        }
    }
    # ok, maybe $txt is the relative path, and all that is needed is add the source
    # =============================================================================
    my $dir = $src_dir; # given by user
    ut_fix_directory(\$dir);
    $fil = $dir.$txt_in;
    if (-f $fil) {
        $rff = strip_src_dir($fil);
        push(@{$ra},$rff);
        ${$retff} = $rff;
        prt("$lnn:3: SRC: $rff $ok ($fil) [$txt_in]\n") if (VERB5());
        ($n,$d,$e) = fileparse($fil, qr/\.[^.]*/);
        foreach $tmp (@hfile_exts) {
            $ff = $d.$n.$tmp;
            if (-f $ff) {
                $rff = strip_src_dir($ff);
                push(@{$ra},$rff);
                prt("$lnn:3: HDR: $rff $ok ($ff)\n") if (VERB5());
                return 2;
            }
        }
        return 1;
    }
    foreach $tmp (@cfile_exts) {
        $ff = $fil.$tmp;
        $ok = 'NOT FOUND';
        if (-f $ff) {
            $ok = 'ok';
            $rff = strip_src_dir($ff);
            push(@{$ra},$rff);
            ${$retff} = $rff;
            prt("$lnn:2: SRC: $rff $ok ($ff) [$txt_in]\n") if (VERB5());
            @tried = ();
            foreach $tmp (@hfile_exts) {
                $ff = $fil.$tmp;
                push(@tried,$ff);
                $ok = 'NOT FOUND';
                if (-f $ff) {
                    $ok = 'ok';
                    $rff = strip_src_dir($ff);
                    push(@{$ra},$rff);
                    prt("$lnn:2: HDR: $rff $ok ($ff)\n") if (VERB5());
                    return 2;
                }
            }
            if (($db_shw_mhdrs || VERB9()) && !(defined $done_txt{$txt_in})) {
                prt("$lnn:2: NO HEADER for [$txt_in]! Tried\n");
                prt(join("\n",@tried)."\n");
            }
            return 1;
        }
    }
    # 25/10/2013 - ok, all find methods FAILED, so scan the directory if givin a -source <dir>
    # ========================================================================================
    if (length($src_dir)) {
        prt("Searching for [$txt_in] in scan...\n") if (VERB9());
        if (find_this_file($txt_in,\$ff)) {
            # ok, found it in a base directory scan
            $ok = 'ok';
            $rff = strip_src_dir($ff);
            push(@{$ra},$rff);
            ${$retff} = $rff;
            prt("$lnn:4: SRC: $rff $ok ($ff)\n") if (VERB5());
            # now try to find a matching header file
            ($n,$d,$e) = fileparse($ff, qr/\.[^.]*/);
            foreach $tmp (@hfile_exts) {
                $ff = $d.$n.$tmp;
                if (-f $ff) {
                    $rff = strip_src_dir($ff);
                    push(@{$ra},$rff);
                    prt("$lnn:4: HDR: $rff $ok ($ff)\n") if (VERB5());
                    return 2;
                }
            }
            return 1;
        }
        foreach $tmp (@cfile_exts) {
            my $tmp2 = $txt_in.$tmp;
            prt("Searching for [$tmp2] in scan...\n") if (VERB9() || $show_finding);
            if (find_this_file($tmp2,\$ff)) {
                # ok, found it in a base directory scan
                $ok = 'ok';
                $rff = strip_src_dir($ff);
                push(@{$ra},$rff);
                ${$retff} = $rff;
                prt("$lnn:4: SRC: $rff $ok ($ff)\n") if (VERB5() || $show_finding);
                # now try to find a matching header file
                ($n,$d,$e) = fileparse($ff, qr/\.[^.]*/);
                foreach $tmp (@hfile_exts) {
                    $ff = $d.$n.$tmp;
                    if (-f $ff) {
                        $rff = strip_src_dir($ff);
                        push(@{$ra},$rff);
                        prt("$lnn:4: HDR: $rff $ok ($ff)\n") if (VERB5() || $show_finding);
                        return 2;
                    }
                }
                return 1;
            }
        }
        if ($name.$ext ne $txt_in) {
            foreach $tmp (@cfile_exts) {
                my $tmp2 = $name.$tmp;
                prt("Searching for [$tmp2] in scan...\n") if (VERB9() || $show_finding);
                if (find_this_file($tmp2,\$ff)) {
                    # ok, found it in a base directory scan
                    $ok = 'ok';
                    $rff = strip_src_dir($ff);
                    push(@{$ra},$rff);
                    ${$retff} = $rff;
                    prt("$lnn:4: SRC: $rff $ok ($ff)\n") if (VERB5() || $show_finding);
                    # now try to find a matching header file
                    ($n,$d,$e) = fileparse($ff, qr/\.[^.]*/);
                    foreach $tmp (@hfile_exts) {
                        $ff = $d.$n.$tmp;
                        if (-f $ff) {
                            $rff = strip_src_dir($ff);
                            push(@{$ra},$rff);
                            prt("$lnn:4: HDR: $rff $ok ($ff)\n") if (VERB5() || $show_finding);
                            return 2;
                        }
                    }
                    return 1;
                }
            }
        }
    }
    # ========================================================================================

    ############################################################
    ### NOT FOUND - WHY???????
    if (defined $shown_failed_find{$txt_in}) {
        return 0;
    }
    $shown_failed_find{$txt_in} = 1;
    $ff = fix_rel_path($curr_abs_dir.$PATH_SEP.$txt_in);
    my $msg = "$lnn: $tline";
    $msg .= "\nFull: $curr_tline" if ($tline ne $curr_tline);
    prtw("WARNING: $lnn: FAILED TO FIND SOURCE [$txt_in] [$ff] [$fil]\n$msg\ncaller=$caller\n");
    ###pgm_exit(1,"TEMP EXIT");
    return 0;
}

my %heads = ();
my %funcs = ();
my %types = ();

sub process_checking($$) {
    my ($lnn,$inc) = @_;
    my ($itm,$msg,@arr,$cnt);
    @arr = split(/\s+/,$inc);
    $cnt = scalar @arr;
    if ($inc =~ /^for\s+(\S+)\.\.\./) {
        $itm = trim_all($1);
        $inc = trim_all(substr($inc,(4 + length($itm) + 3)));
        if (($inc =~ /yes/)||($inc =~ /no/)) {
            if ($itm =~ /\.h/) {
                $heads{$itm} = 1;
                $msg = 'H';
            } elsif ($itm =~ /_t$/) {
                $types{$itm} = 1;
                $msg = 'T';
            } else {
                $funcs{$itm} = 1;
                $msg = 'F';
            }
            prt("$lnn: FOR $msg $itm RES Y/N\n") if (VERB9());
        } else {
            prt("$lnn: FOR $itm RES $inc\n") if (VERB2());
        }
    } elsif ( ($arr[0] =~ /\.h/ ) && ($cnt == 3) &&
        (($arr[1] eq 'usability...')||($arr[1] eq 'presence...')) &&
        (($arr[2] eq 'yes')||($arr[2] eq 'no')) ) {
        $heads{$arr[0]} = 1;
    } else {
        prt("$lnn: $inc\n") if (VERB2());
    }
}

sub show_hdrs() {
    my ($cnt,$key,@arrh,$var,$minh,$len,$def,@defs,@arrf,$minf);
    @arrh = sort keys(%heads);
    @arrf = sort keys(%funcs);
    $minh = 0;
    foreach $key (@arrh) {
        $len = length($key);
        $minh = $len if ($len > $minh);
    }
    $minf = 0;
    foreach $key (@arrf) {
        $len = length($key);
        $minf = $len if ($len > $minf);
    }

    # headers
    $cnt = scalar @arrh;
    prt("\n# $cnt checks for headers\n");
    @defs = ();
    foreach $key (@arrh) {
        $var = uc($key);
        $var =~ s/\./_/g;
        $var =~ s/\//_/g;
        $def = "#cmakedefine HAVE_$var 1";
        push(@defs,$def);
        $key .= ' ' while (length($key) < $minh);
        prt(" check_include_file($key HAVE_$var)\n");
    }

    prt("\n".join("\n",@defs)."\n");

    # functions
    $cnt = scalar @arrf;
    prt("\n# $cnt checks for functions\n");
    @defs = ();
    foreach $key (@arrf) {
        $var = uc($key);
        $var =~ s/\./_/g;
        $var =~ s/\//_/g;
        $def = "#cmakedefine HAVE_$var 1";
        push(@defs,$def);
        $key .= ' ' while (length($key) < $minf);
        prt(" check_function_exists($key HAVE_$var)\n");
    }

    prt("\n".join("\n",@defs)."\n");

}

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


sub process_in_file($) {
    my ($inf) = @_;
    #if (! open INF, "<$inf") {
    #    pgm_exit(1,"ERROR: Unable to open file [$inf]\n"); 
    #}
    #my @lines = <INF>;
    #close INF;
    #my $lncnt = scalar @lines;
    my @lines = ();
    my $lncnt = load_file_lines($inf,\@lines);
    prt("Processing $lncnt lines, from [$inf]...\n");
    my ($line,$inc,$lnn,$i,$tline,$len,$i2,$tmp,$dir,$ldir);
    my (@arr,$j,$alen,$lib,$ind,$blnn,$elnn,$oline);
    my ($off1,$off2,$sline);
    $lnn = 0;
    $dir = '';
    $ldir = '';
    my %hash = ();
    my $islink = 0;
    my $iscomp = 0;
    my %sources = ();
    my @dir_stack = ();
    # if we have BOTH a base directory and a current source directory, then we can check the source
    my $lbd = length($base_dir);
    my $lsd = length($src_dir);
    #$chk_src_file = ($lbd && $lsd) ? 1 : 0;
    $chk_src_file = ($lsd > 0) ? 1 : 0;
    for ($i = 0; $i < $lncnt; $i++) {
        $line = $lines[$i];
        chomp $line;
        $lnn++;
        $curr_lnn = $lnn;
        $i2 = $i + 1;
        $tline = trim_all($line);
        $len = length($tline);
        next if ($len == 0);
        $blnn = $lnn;
        while (($tline =~ /\\$/)&&($i2 < $lncnt)) {
            $tline =~ s/\\$//; # remove
            $tline = trim_all($tline);
            $i++;
            $tmp = $lines[$i];
            chomp $tmp;
            $lnn++;
            $i2 = $i + 1;
            $tline .= ' ' if (length($tline));
            $tline .= trim_all($tmp);
        }
        $elnn = $lnn;
        $len = length($tline);
        #$ind = index($tline," && mv ");
        #if ($ind > 0) {
        #    $tline = substr($tline,0,length($tline)-$ind);
        #    $len = length($tline);
        #}
        # skip configure lines, but extract 'cmake' script from 'checking ...' lines
        if ($line =~ /^checking\s+/) {
            $line = substr($line, 9);
            process_checking($blnn,$line);
            next;
        }
        next if ($line =~ /^configure:\s/);
        next if ($line =~ /^config.status:\s+/);
        next if ($line =~ /^===\s+configuring/);
        next if ($line =~ /^\(cached\)\s+checking\s+/);

        next if ($line =~ /^\[master\s+/);
        next if ($line =~ /^WARNING:\s+/);
        next if ($line =~ /^if\s+test\s+/);
        $oline = $tline;
        $curr_tline = $tline;
        $islink = 0;
        $iscomp = 0;
        # but this can be a horrible line like -
        # cd tools/dev && /bin/bash /home/geoff/projects/subversion/subversion-1.8.1/libtool ...
        # gcc  -g -O2   -pthread   -rpath /usr/local/lib  -o fsfs-reorg  fsfs-reorg.lo 
        # ../../subversion/libsvn_delta/libsvn_delta-1.la ../../subversion/libsvn_subr/libsvn_subr-1.la -lapr-1 
        # which is a link line, or 
        # if false ; then /bin/bash /home/geoff/projects/subversion/subversion-1.8.1/libtool --tag=CC 
        # --silent --mode=compile gcc -std=c90 -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE  -g -O2 -pthread 
        # -I./subversion/include -I./subversion -I/usr/include/apr-1.0 -I/usr/include/apr-1.0 
        # -o subversion/mod_dav_svn/activity.lo -c subversion/mod_dav_svn/activity.c ; 
        # else echo "fake" > subversion/mod_dav_svn/activity.lo ; fi
        # which is a compile of a source line, so make is SIMPLE
        if ($tline =~ /^if\s+(\S+)\s*;\s*then\s+/) {
            $tline =~ s/^if\s+(\S+)\s*;\s*then\s+//;
        }
        $off1 = index($tline," gcc ");
        $off2 = index($tline," -o ");
        if (($off1 > 0) && ($off2 > $off1)) {
            $sline = substr($tline,0,$off1);    # get head of line
            if ($sline =~ /^cd\s+(\S+)\s+&/) {
                $curr_rel_dir = $1;
                ut_fix_rpath_per_os(\$curr_rel_dir);
                $curr_abs_dir = fix_rel_path($src_dir.$PATH_SEP.$curr_rel_dir) if (length($src_dir));
                ut_fix_rpath_per_os(\$curr_abs_dir);
                prt("$lnn: Enter abs $curr_abs_dir\n") if (VERB5());
                ###pgm_exit(1,"TEMP EXIT: Entered [$curr_rel_dir] abs [$curr_abs_dir]\n");
            }
            $tline = substr($tline,$off1 + 1);
            $off1 = index($tline,';');
            if ($off1 > 0) {
                $tline = trim_all(substr($tline,0,$off1));
                ###pgm_exit(1,"Is LINK or COMPILE [$tline]");
            }
        }
        if ($tline =~ /^libtool:\s+link:\s+/) {
            $tline =~ s/^libtool:\s+link:\s+//;
            if (($tline =~ /^\s*\(\s*cd\s+/)&&($tline =~ /\s+rm\s+-f\s+/)) {
                next;
            }
            # libtool: link: gcc .libs/gettext-po.o .libs/str-list.o 
            # -Wl,--whole-archive ./.libs/libgnu.a -Wl,--no-whole-archive  -lc 
            # -Wl,-soname -Wl,libgettextpo.so.0 -o .libs/libgettextpo.so.0.5.1
            $tline = strip_link_line($tline);
            $islink = 1;
        } elsif ($tline =~ /^libtool:\s+compile:\s+/) {
            $tline =~ s/^libtool:\s+compile:\s+//;
            $tline = strip_comp_line($tline, \%sources);
            $iscomp = 1;
        } elsif ($tline =~ /^gcc\s+/) {
            # 20/08/2013 - gcc lines can be a compile or a link
            # if 'gcc -Wall ...  -c -o dynamic.o dynamic.c' is COMPILE
            # if 'gcc ebfc.o brainfuck.o libelfparts.a -o ebfc' is LINK
            if (is_compile_line($tline)) {
                $tline = strip_comp_line($tline, \%sources);
                $iscomp = 1;    # already check this - if ($tline =~ /\s+-c\s+/);
            } elsif (is_link_line($tline)) {
                $tline = strip_link_line($tline);
                $islink = 1;
            } else {
                prtw("WARNING: $blnn: Unknown 'gcc' line [$tline] FIX ME\n");
            }
        } elsif ($tline =~ /^ar /) {
            $tline = strip_link_line($tline);
            $islink = 1;
        }

        next if ($tline =~ /^mv\s+/);
        next if ($tline =~ /^rm\s+/);
        next if ($tline =~ /^\s*\/bin\//);
        next if ($tline =~ /^\/usr\//);
        next if ($tline =~ /^Making\s+/);
        next if ($tline =~ /^ranlib /);
        next if ($tline =~ /^sed\s+/);
        ###next if ($tline =~ /^echo /);
        if ($tline =~ /^(-|\w)+\.\w+:/) {
            next;
        }

        if ($tline =~ /^make/) {
            if ($tline =~ / Entering directory `(.+)'/) { # \'(.+)\'/) {
                $tmp = $1;
                if ($tmp ne $dir) {
                    $ldir = $dir;
                    $dir = $tmp;
                    $curr_rel_dir = strip_base_dir($dir);
                    if ($chk_src_file) {
                        push(@dir_stack,$curr_abs_dir) if (length($curr_abs_dir));
                        $curr_abs_dir = fix_rel_path($src_dir.$PATH_SEP.$curr_rel_dir);
                        ut_fix_rpath_per_os(\$curr_abs_dir);
                        prt("$lnn: Enter abs $curr_abs_dir\n") if (VERB5());
                        $entered_dirs{$curr_abs_dir} = 1;
                    } else {
                        prt("$lnn: Enter rel $curr_rel_dir\n") if (VERB5());
                    }
                }
                @curr_srcs = ();  # clear compiled list on directory change
            } elsif ($tline =~ / Leaving directory `(.+)'/) { # \'(.+)\'/) {
                $tmp = $1;
                # leaving a directory apparently pops back to previous entered
                if (@dir_stack) {
                    $curr_abs_dir = $dir_stack[-1];
                    pop @dir_stack;
                    prt("$lnn: popped abs $curr_abs_dir\n") if (VERB5());
                }
                if ($ldir ne $dir) {
                    prt("$lnn: Exit  $dir to $ldir\n") if (VERB9());
                    $dir = $ldir;
                }
            }
            next;
        }

        if ($islink) {
            ###@arr = split(/\s+/,$tline);
            @arr = my_space_split($tline);
            $alen = scalar @arr;
            my @a = ();
            $lib = 'NOT FOUND';
            my ($n,$d,$typ,$ra,$ccnt);
            $typ = "UNKNOWN";
            my %h = ();
            my @narr = ();
            # processing a LINK line, like 
            # libtool: link: gcc -std=gnu99 -g -O2 -o .libs/msgfilter 
            # msgfilter-msgfilter.o msgfilter-filter-sr-latin.o  
            # ./.libs/libgettextsrc.so /home/geoff/projects/gettext-0.18.1.1/gettext-tools/gnulib-lib/.libs/libgettextlib.so 
            # /usr/lib/x86_64-linux-gnu/libxml2.so -lc
            # 1 - preprocess, looking for -o output
            my $fnd = 0;
            my $isar = 0;
            my $isln = 0;   # create link, or copy usually
            for ($j = 0; $j < $alen; $j++) {
                $tmp = $arr[$j];
                if (($tmp eq '-o')&&(($j + 1) < $alen)) {
                    $j++;
                    $tmp = $arr[$j];
                    ($lib,$d) = fileparse($tmp);
                    if (!($lib =~ /\./)) {
                        # no extension s- sure sign it is an
                        $typ = 'EXE';
                    } elsif ($lib =~ /\.so(\s|\.)*/) {
                        $typ = 'DLL';
                        $lib =~ s/\.so.*$//;
                    } else {
                        #$typ = 'EXE';
                        $typ = 'LIB';
                    }
                    $fnd = 1;
                    last;
                } elsif ($tmp eq 'ar') {
                    $isar = 1;
                } elsif ($tmp eq 'ln') {
                    $isln = 1;
                    last;
                }
            }
            if ($isln) {
                prt("$lnn: Skipping create link [$tline]\n");
                next;
            }
            my $isgcc = 0;
            my $isshared = 0;
            my $hasobjs = 0;
            my $objcnt = 0;
            if (!$fnd && !$isar) {
                # line like 
                # libtool: link: gcc -std=gnu99 -shared -fPIC -DPIC .libs/assert.o .libs/compat.o 
                # .libs/errno.o .libs/extract-dbl.o .libs/invalid.o .libs/memory.o .libs/mp_bpl.o 
                # ...lots more object files...
                # mpf/.libs/mul_ui.o mpf/.libs/div.o mpf/.libs/div_ui.o mpf/.libs/cmp.o 
                # mpf/.libs/cmp_d.o mpf/.libs/cmp_ui.o mpf/.libs/cmp_si.o mpf
                for ($j = 0; $j < $alen; $j++) {
                    $tmp = $arr[$j];
                    if ($tmp eq 'libtool:') {
                    } elsif ($tmp eq 'link:') {
                    } elsif ($tmp eq 'gcc') {
                        $isgcc = 1;
                    } elsif ($tmp =~ /^-/) {
                        if ($tmp eq '-shared') {
                            $isshared++;
                            $typ = 'DLL';
                        } elsif ($tmp eq '-fPIC') {
                            $isshared++;
                            $typ = 'DLL';
                        }
                    } else {
                        ($n,$d) = fileparse($tmp);
                        if ($n =~ /\.o$/) {
                            $objcnt++;
                            if (check_for_source($tmp,\@a,$lnn,$tline,\$tmp,"new_serv")) {
                                $hasobjs++;
                            }
                        } else {
                            $lib = $tmp;
                        }
                    }
                }
                if ($isgcc && $objcnt && $isshared && length($lib)) {
                    prt("$lnn: Got $objcnt objects, $hasobjs sources, name [$lib], shared $typ\n");
                    $fnd = 1;
                } else {
                    prtw("WARNING: $lnn: New services did NOT work!\n");
                    # exit(1);
                }
            }
            if (!$fnd) {
                if (!$isar) {
                    ###prtw("WARNING: No -o found! CHECK LINE\n$oline\n");
                    pgm_exit(1,"WARNING: NOT ar and no -o found! CHECK LINE\n$oline\nFIX ME\n");
                }
                # ar line like
                # libtool: link: ar cru .libs/libgnuintl.a bindtextdom.o dcgettext.o dgettext.o 
                # gettext.o finddomain.o hash-string.o loadmsgcat.o localealias.o textdomain.o 
                # l10nflist.o explodename.o dcigettext.o dcngettext.o dngettext.o ngettext.o 
                # plural.o plural-exp.o localcharset.o threadlib.o lock.o relocatable.o 
                # langprefs.o localename.o log.o printf.o setlocale.o version.o osdep.o 
                # intl-compat.o
                # NOTE; 
                for ($j = 0; $j < $alen; $j++) {
                    $tmp = $arr[$j];
                    if ($tmp =~ /\.a$/) {
                        ($lib,$d) = fileparse($tmp);
                        $lib =~ s/\.a//; # the ARCHIVE LIBRARY NAME
                        $typ = "LIB";
                        $fnd = 1;
                        last;
                    } elsif ($tmp =~ /\.la$/) {
                        ($lib,$d) = fileparse($tmp);
                        $lib =~ s/\.la//; # the ARCHIVE LIBRARY NAME
                        $typ = "LIB";
                        $fnd = 1;
                        last;
                    }
                }
                if (!$fnd) {
                    pgm_exit(1,"WARNING: Is ar but no .a ouput found! CHECK LINE\n$oline\nFIX ME\n");
                }
            }
            if (!$isgcc) {
                for ($j = 0; $j < $alen; $j++) {
                    $tmp = $arr[$j];
                    if ($tmp =~ /\.a$/) {
                        if ($typ eq 'UNKNONW') {
                            ($lib,$d) = fileparse($tmp);
                            $lib =~ s/\.a//; # the LIBRARY
                            $typ = "LIB";
                        } else {
                            $h{deps} = [] if (!defined $h{deps});
                            $ra = $h{deps};
                            push(@{$ra},$tmp);
                        }
                    } elsif ($tmp =~ /\.o$/) {
                        $tmp =~ s/\.o$//;
                        if ($chk_src_file) {
                            check_for_source($tmp,\@a,$lnn,$tline,\$tmp,"notgcc.o");
                        } else {
                            push(@a,$tmp);
                        }
                    } elsif ($tmp =~ /\.lo$/) {
                        $tmp =~ s/\.lo$//;
                        if ($chk_src_file) {
                            check_for_source($tmp,\@a,$lnn,$tline,\$tmp,"notgcc.lo");
                        } else {
                            push(@a,$tmp);
                        }
                    } elsif (($tmp eq '-o')&&(($j + 1) < $alen)) {
                        $j++;
                        # aleady done
                    } elsif ($tmp =~ /\.so$/) {
                        ($tmp,$d) = fileparse($tmp);
                        ###$tmp =~ s/\.so$//;
                        $h{deps} = [] if (!defined $h{deps});
                        $ra = $h{deps};
                        push(@{$ra},$tmp);
                    } elsif ($tmp =~ /\.a$/) {
                        ($tmp,$d) = fileparse($tmp);
                        ###$tmp =~ s/\.so$//;
                        $h{deps} = [] if (!defined $h{deps});
                        $ra = $h{deps};
                        push(@{$ra},$tmp);
                    } elsif (is_c_source($tmp)) {
                        if ($chk_src_file) {
                            check_for_source($tmp,\@a,$lnn,$tline,\$tmp,"notgcc.isc");
                        } else {
                            push(@a,$tmp);
                        }
                    }
                }
            }
            # 25/10/2013 - but ar can be used to append sources
            # 20130805 - NEVER overwrite an existing entry
            my $unam = $lib;
            if (! $isar) {
                $d = 0;
                while (defined $hash{$unam}) {
                    $d++;
                    $unam = $lib.$d;
                }
            }
            $alen = scalar @a;
            $ccnt = scalar @curr_srcs;
            prt("$lnn: $unam srcs $ccnt objs $alen\n") if (VERB9());
            if (($ccnt == 0) && $islink && $alen) {
                # could be a line like, where the ONLY source indication is '...o'
                # and library deppendencies can be ...a or makebe ...so
                # gcc ebfc.o brainfuck.o libelfparts.a -o ebfc
                @curr_srcs = @a;
                $ccnt = scalar @curr_srcs;
            }
            my @a2 = ();
            if ($ccnt) {
                my %dh = ();
                foreach $tmp (@curr_srcs) {
                    if (!defined $dh{$tmp}) {
                        $dh{$tmp} = 1;
                        push(@a2,$tmp);
                    }
                }
                # MAYBE add from object as well, but no DUPES
                foreach $tmp (@a) {
                    if (!defined $dh{$tmp}) {
                        $dh{$tmp} = 1;
                        push(@a2,$tmp);
                    }
                }

                ###$h{asrcs} = \@a2;
                $ccnt = scalar @a2;
            }
            if ($lib eq 'NOT FOUND') {
                prtw("WARNING: $lnn: $typ $unam with $alen ($ccnt) sources!\n");
            } elsif ($typ eq 'UNKNOWN') {
                prtw("WARNING: $lnn: $typ $unam with $alen ($ccnt) sources!\n");
            } elsif ($ccnt == 0) {
                prtw("WARNING: $lnn: $typ $unam with $alen items but NO SOURCES!\n");
            } else {
                prt("$lnn: $typ $unam with $alen ($ccnt) sources.\n");
                if ($isar && defined $hash{$lib}) {
                    my $refh = $hash{$lib}; # get the previous
                    my $refa = ${$refh}{srcs};
                    push(@{$refa},@a); # just ADD these sources
                    my $refa2 = ${$refh}{asrcs};
                    push(@{$refa2},@a2); # just ADD these sources
                } else {
                    $h{type} = $typ;
                    $h{srcs} = \@a;
                    $h{line} = $lnn;
                    $h{name} = $unam;
                    $h{asrcs} = \@a2;
                    $hash{$unam} = \%h;
                }
            }
            @curr_srcs = (); # NO must clear compiled list whether linking library or exe
            ###next;
        }
        $tmp = get_show_stg($tline);
        $tmp = "link: $tmp" if ($islink);
        $tmp = "comp: $tmp" if ($iscomp);
        prt("$i2: $tmp\n") if (VERB5() || $show_ignored);
    }
    $tmp = scalar keys(%sources);
    prt("Collected a total of $tmp sources...\n");
    return \%hash;
}

my %headers = ();  # headers are only ever attached to one build object

sub get_includes_block() {
    my $txt = <<EOF;
#include (CheckFunctionExists)
#include (CheckCSourceCompiles)
#include (CheckCXXSourceCompiles)
#include (CheckIncludeFile)
#include (CheckLibraryExists)    # added 20130803 for MACRO CONCAT below
#include (TestBigEndian)         # added 20130810 to add an 'endian' define

EOF
    return $txt;
}

sub get_cmake_header($) {
    my $title = shift;
    my $cmake = "# ============================================================\n";
    $cmake .= "# $src_dir\\"."CMakeLists.txt, generated by $pgmname, $VERS, on ";
    $cmake .= lu_get_YYYYMMDD_hhmmss(time())."\n";
    $cmake .= "# from build log $in_file\n";
    $cmake .= "cmake_minimum_required( VERSION 2.6.4 )\n\n";
    $cmake .= get_includes_block();
    $cmake .= "project($title)\n\n";
    return $cmake;
}

sub add_options_block($) {
    my $type = shift;
    my $txt = <<EOF;
# Allow developers to select if Dynamic or static libraries are built.
set( LIB_TYPE STATIC )  # set default static
option( BUILD_SHARED_LIB    "Set ON to build Shared Libraries"      $type )
option( BUILD_TEST_PROGRAMS "Set ON to build the utility programs"  OFF )

# read 'version' file into a variable (stripping any newlines or spaces)
#file(READ version versionFile)
#if (NOT versionFile)
#    message(FATAL_ERROR "Unable to determine version. version file is missing.")
#endif()
#string(STRIP "\${versionFile}" MY_VERSION)
#add_definitions( -DVERSION="\${MY_VERSION}" )

EOF
    return $txt;
}

sub get_definitions_block() {
    my $txt = <<EOF;
if(CMAKE_COMPILER_IS_GNUCXX)
    set( WARNING_FLAGS -Wall )
endif(CMAKE_COMPILER_IS_GNUCXX)

if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 
   set( WARNING_FLAGS "-Wall -Wno-overloaded-virtual" )
endif() 

if(WIN32)
    if(MSVC)
        # turn off various warnings
        set(WARNING_FLAGS "\${WARNING_FLAGS} /wd4996")
        # foreach(warning 4244 4251 4267 4275 4290 4786 4305)
        #     set(WARNING_FLAGS "\${WARNING_FLAGS} /wd\${warning}")
        # endforeach(warning)

        set( MSVC_FLAGS "-DNOMINMAX -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS -D__CRT_NONSTDC_NO_WARNINGS" )
        # if (\${MSVC_VERSION} EQUAL 1600)
        #    set( MSVC_LD_FLAGS "/FORCE:MULTIPLE" )
        # endif (\${MSVC_VERSION} EQUAL 1600)
    endif(MSVC)
    # set( NOMINMAX 1 )
    list(APPEND extra_LIBS Winmm.lib)
endif(WIN32)

set( CMAKE_C_FLAGS "\${CMAKE_C_FLAGS} \${WARNING_FLAGS} \${MSVC_FLAGS} -D_REENTRANT" )
set( CMAKE_CXX_FLAGS "\${CMAKE_CXX_FLAGS} \${WARNING_FLAGS} \${MSVC_FLAGS} -D_REENTRANT" )
set( CMAKE_EXE_LINKER_FLAGS "\${CMAKE_EXE_LINKER_FLAGS} \${MSVC_LD_FLAGS}" )

# example header checks
# check_include_file(inttypes.h     HAVE_INTTYPES_H)
# check_include_file(limits.h       HAVE_LIMITS_H)
# check_include_file(stdbool.h      HAVE_STDBOOL_H)
# check_include_file(strings.h      HAVE_STRINGS_H)
# check_include_file(unistd.h       HAVE_UNISTD_H)
# example function checks
# check_function_exists(_snprintf   HAVE__SNPRINTF)
# check_function_exists(_vsnprintf  HAVE__VSNPRINTF)
# check_function_exists(vsnprintf   HAVE_VSNPRINTF)

# add these definition to config.h.in
#cmakedefine HAVE_INTTYPES_H 1
#cmakedefine HAVE_LIMITS_H 1
#cmakedefine HAVE_STDBOOL_H 1
#cmakedefine HAVE_STRINGS_H 1
#cmakedefine HAVE_UNISTD_H 1
#cmakedefine HAVE__SNPRINTF 1
#cmakedefine HAVE__VSNPRINTF 1
#cmakedefine HAVE_VSNPRINTF 1

# then put information in a config.h file
#configure_file( "\${CMAKE_SOURCE_DIR}/config.h.in" "\${CMAKE_BINARY_DIR}/config.h" )
# make config.h findable
#include_directories( \${CMAKE_BINARY_DIR} )
# and usable
#add_definitions( -DHAVE_CONFIG_H )

# to distinguish between debug and release lib
if (WIN32)
set( CMAKE_DEBUG_POSTFIX "d" )
endif ()

if(BUILD_SHARED_LIB)
   set(LIB_TYPE SHARED)
   message(STATUS "*** Building DLL library \${LIB_TYPE}")
else(BUILD_SHARED_LIB)
   message(STATUS "*** Building static library \${LIB_TYPE}")
endif(BUILD_SHARED_LIB)

EOF
    return $txt;
}


sub ref_hash_2_cmake($) {
    my $rh = shift;
    my @keys = keys %{$rh};
    my $cnt = scalar @keys;
    my ($key,$rh2,@keys2,$cnt2,$name,$type,$rsa,$scnt,$lnn,$src);
    my ($hcnt);
    my $tsrcs = 0;
    my @libs = ();
    my @exes = ();
    my @dlls = ();
    my @missed = ();
    my @hdrs = ();
    my $cmake = '';
    my $indent1 = '    ';
    my $indent2 = $indent1.'    ';
    # sort into type
    foreach $key (@keys) {
        $rh2 = ${$rh}{$key};
        $type = 'UNKNOWN';
        if (defined ${$rh2}{type}) {
           $type = ${$rh2}{type};
        } 
        $scnt = 0;
        if (defined ${$rh2}{asrcs}) {
           $rsa = ${$rh2}{asrcs};
           $scnt = scalar @{$rsa};
        }
        if ($scnt == 0) {
            push(@missed,$key);
            next;
        }
        
        if ($type eq 'EXE') {
            push(@exes,$key);
        } elsif ($type eq 'LIB') {
            push(@libs,$key);
        } elsif ($type eq 'DLL') {
            push(@dlls,$key);
        } else {
            push(@missed,$key);
        }

    }

    $cnt = scalar @libs;
    if ($cnt) {
        prt("List of $cnt libraries...\n");
        $cmake .= "# List of $cnt LIBRARIES\n";
    }
    # =====================================================
    # TODO: If lots of sources, with relative long paths use
    # TODO: set(dir to/the/long/path); Then each source ${dir}/$src
    foreach $key (sort @libs) {
        $rh2 = ${$rh}{$key};
        $name = "NOT FOUND";
        if (defined ${$rh2}{name}) {
           $name = ${$rh2}{name};
        }
        $type = 'UNKNOWN';
        if (defined ${$rh2}{type}) {
           $type = ${$rh2}{type};
        }
        $scnt = 0;
        if (defined ${$rh2}{asrcs}) {
           $rsa = ${$rh2}{asrcs};
           $scnt = scalar @{$rsa};
        }
        $lnn = "unk";
        if (defined ${$rh2}{line}) {
           $lnn = ${$rh2}{line};
        }

       #prt("$key with $cnt2 items... name $name, type $type, srcs $scnt");
       prt("$lnn: $type $key ($name) srcs $scnt");
       ###prt(join(" ",@keys2));
       prt("\n");
       $tsrcs += $scnt;

        if ($scnt) {
            $name =~ s/\..*$//; # remove trailing extent, if any
            $cmake .= "set(name $name)\n";
            $cmake .= "set(\${name}_SRCS\n";
            @hdrs = ();
            foreach $src (@{$rsa}) {
                $src = path_d2u($src);
                if (is_h_source($src)) {
                    if (!defined $headers{$src}) {
                        $headers{$src} = 1;
                        push(@hdrs,$src);
                    }
                } else {
                    $cmake .= $indent1."$src\n";
                }
            }
            $cmake .= $indent1.")\n";
            if (@hdrs) {
                $cmake .= "set(\${name}_HDRS\n";
                foreach $src (@hdrs) {
                    $cmake .= $indent1."$src\n";
                }
                $cmake .= $indent1.")\n";
                $cmake .= "add_library(\${name} \${LIB_TYPE} \${\${name}_SRCS} \${\${name}_HDRS})\n";
            } else {
                $cmake .= "add_library(\${name} \${LIB_TYPE} \${\${name}_SRCS})\n";
            }
            $cmake .= "list(APPEND add_LIBS \${name})\n";
            $cmake .= "\n";
        }
    }
    $cnt = scalar @dlls;
    prt("List of $cnt shared libraries...\n") if ($cnt);
    foreach $key (sort @dlls) {
        $rh2 = ${$rh}{$key};
        $name = "NOT FOUND";
        if (defined ${$rh2}{name}) {
           $name = ${$rh2}{name};
        }
        $type = 'UNKNOWN';
        if (defined ${$rh2}{type}) {
           $type = ${$rh2}{type};
        }
        $scnt = 0;
        if (defined ${$rh2}{asrcs}) {
           $rsa = ${$rh2}{asrcs};
           $scnt = scalar @{$rsa};
        }
        $lnn = "unk";
        if (defined ${$rh2}{line}) {
           $lnn = ${$rh2}{line};
        }

       #prt("$key with $cnt2 items... name $name, type $type, srcs $scnt");
       prt("$lnn: $type $key ($name) srcs $scnt");
       ###prt(join(" ",@keys2));
       prt("\n");
       $tsrcs += $scnt;
        if ($scnt) {
            @hdrs = ();
            $cmake .= "set(name $name)\n";
            $cmake .= "set(\${name}_SRCS\n";
            foreach $src (@{$rsa}) {
                $src = path_d2u($src);
                if (is_h_source($src)) {
                    if (!defined $headers{$src}) {
                        $headers{$src} = 1;
                        push(@hdrs,$src);
                    }
                } else {
                    $cmake .= $indent1."$src\n";
                }
            }
            $cmake .= $indent1.")\n";
            if (@hdrs) {
                $cmake .= "set(\${name}_HDRS\n";
                foreach $src (@hdrs) {
                    $cmake .= $indent1."$src\n";
                }
                $cmake .= "   )\n";
                $cmake .= "add_library(\${name} \${LIB_TYPE} \${\${name}_SRCS} \${\${name}_HDRS})\n";
            } else {
                $cmake .= "add_library(\${name} \${LIB_TYPE} \${\${name}_SRCS})\n";
            }
            $cmake .= "add_library(\${name} \${LIB_TYPE} \${\${name}_SRCS})\n";
            $cmake .= "list(APPEND add_LIBS \${name})\n";
            $cmake .= "\n";
        }
    }
    $cnt = scalar @exes;
    $cmake .= "#####################################################\n";
    # ===================================================================
    # TODO: If the $cnt is say 2-3 or more use a macro to add executables
    # Some project, like the recent openssl has 36 'test' apps to build
    # ===================================================================
    if ($cnt) {
        prt("List of $cnt executables...\n");
        $cmake .= "# adding $cnt executables\n";
    }
    $cmake .= "if (BUILD_TEST_PROGRAMS)\n";
    foreach $key (sort @exes) {
        $rh2 = ${$rh}{$key};
        $name = "NOT FOUND";
        if (defined ${$rh2}{name}) {
           $name = ${$rh2}{name};
        }
        $type = 'UNKNOWN';
        if (defined ${$rh2}{type}) {
           $type = ${$rh2}{type};
        }
        $scnt = 0;
        if (defined ${$rh2}{asrcs}) {
           $rsa = ${$rh2}{asrcs};
           $scnt = scalar @{$rsa};
        }
        $lnn = "unk";
        if (defined ${$rh2}{line}) {
           $lnn = ${$rh2}{line};
        }

       #prt("$key with $cnt2 items... name $name, type $type, srcs $scnt");
       prt("$lnn: $type $key ($name) srcs $scnt");
       ###prt(join(" ",@keys2));
       prt("\n");
       $tsrcs += $scnt;
        if ($scnt) {
            @hdrs = ();
            $cmake .= $indent1."set(name $name)\n";
            $cmake .= $indent1."set(\${name}_SRCS";
            $cmake .= "\n" if ($scnt > $min_source);
            foreach $src (@{$rsa}) {
                $src = path_d2u($src);
                if (is_h_source($src)) {
                    if (!defined $headers{$src}) {
                        $headers{$src} = 1;
                        push(@hdrs,$src);
                    }
                } else {
                    if ($scnt > $min_source) {
                        $cmake .= $indent2."$src\n";
                    } else {
                        $cmake .= " $src"; # stay inline for small counts
                    }
                }
            }
            if ($scnt > $min_source) {
                $cmake .= $indent2.")\n";
            } else {
                $cmake .= " )\n"; # keep it compact
            }
            $hcnt = scalar @hdrs;
            if ($hcnt) {
                $cmake .= $indent1."set(\${name}_HDRS";
                $cmake .= "\n" if ($hcnt > $min_source);
                foreach $src (@hdrs) {
                    if ($hcnt > $min_source) {
                        $cmake .= $indent2."$src\n";
                    } else {
                        $cmake .= " $src"; # keep compact
                    }
                }
                if ($hcnt > $min_source) {
                    $cmake .= $indent2.")\n";
                } else {
                    $cmake .= " )\n"; # compact for small sources
                }
                $cmake .= $indent1."add_executable(\${name} \${\${name}_SRCS} \${\${name}_HDRS})\n";
            } else {
                $cmake .= $indent1."add_executable(\${name} \${\${name}_SRCS})\n";
            }
            $cmake .= $indent1."# set_target_properties(\${name} PROPERTIES COMPILE_FLAGS \"-DMONOLITH\")\n";
            $cmake .= $indent1."target_link_libraries(\${name} \${add_LIBS} \${extra_LIBS})\n";
            $cmake .= $indent1."if (WIN32)\n";
            $cmake .= $indent2."set_target_properties(\${name} PROPERTIES DEBUG_POSTFIX d)\n";
            $cmake .= $indent1."endif ()\n";
            $cmake .= $indent1."list(APPEND add_EXES \${name})\n";
            $cmake .= "\n";
        }
    }
    $cmake .= "endif ()\n";

    $cnt = scalar @missed;
    prt("List of $cnt MISSED - CHECK THESE...\n") if ($cnt);
    foreach $key (@missed) {
        $rh2 = ${$rh}{$key};
        $name = "NOT FOUND";
        if (defined ${$rh2}{name}) {
           $name = ${$rh2}{name};
        }
        $type = 'UNKNOWN';
        if (defined ${$rh2}{type}) {
           $type = ${$rh2}{type};
        }
        $scnt = 0;
        if (defined ${$rh2}{asrcs}) {
           $rsa = ${$rh2}{asrcs};
           $scnt = scalar @{$rsa};
        }
        $lnn = "unk";
        if (defined ${$rh2}{line}) {
           $lnn = ${$rh2}{line};
        }

       #prt("$key with $cnt2 items... name $name, type $type, srcs $scnt");
       prt("$lnn: $type $key ($name) srcs $scnt");
       ###prt(join(" ",@keys2));
       prt("\n");
       $tsrcs += $scnt;
    }
    #prt("Total source $tsrcs\n");

    my $nam = length($proj_name) ? $proj_name : "FIX_PROJECT_NAME";
    my $txt = get_cmake_header($nam);
    $txt .= add_options_block('OFF');
    $txt .= get_definitions_block();
    $txt .= $cmake;
    $txt .= "\n# eof\n";
    my $file = $tmp_out;
    $file = $out_file if (length($out_file));
    rename_2_old_bak($file); # NEVER overwrite
    write2file($txt,$file);
    prt("CMake script written to $file\n");
}

#########################################
### MAIN ###
parse_args(@ARGV);
my $ref_hash = process_in_file($in_file);
show_hdrs();
ref_hash_2_cmake($ref_hash);

pgm_exit(0,"");
########################################

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

sub parse_args {
    my (@av) = @_;
    my ($arg,$sarg);
    while (@av) {
        $arg = $av[0];
        if ($arg =~ /^-/) {
            $sarg = substr($arg,1);
            $sarg = substr($sarg,1) while ($sarg =~ /^-/);
            if (($sarg =~ /^h/i)||($sarg eq '?')) {
                give_help();
                pgm_exit(0,"Help exit(0)");
            } elsif ($sarg =~ /^v/) {
                if ($sarg =~ /^v.*(\d+)$/) {
                    $verbosity = $1;
                } else {
                    while ($sarg =~ /^v/) {
                        $verbosity++;
                        $sarg = substr($sarg,1);
                    }
                }
                prt("Verbosity = $verbosity\n") if (VERB1());
            } elsif ($sarg =~ /^l/) {
                if ($sarg =~ /^ll/) {
                    $load_log = 2;
                } else {
                    $load_log = 1;
                }
                prt("Set to load log at end. ($load_log)\n") if (VERB1());
            } elsif ($sarg =~ /^n/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $proj_name = $sarg;
                prt("Set project name to [$proj_name].\n") if (VERB1());
            } elsif ($sarg =~ /^o/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $out_file = $sarg;
                prt("Set out file to [$out_file].\n") if (VERB1());
            } elsif ($sarg =~ /^s/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $src_dir = $sarg;
                prt("Set src directory to [$src_dir].\n") if (VERB1());
                pgm_exit(1,"ERROR: Unable to locate dir [$src_dir]\n") if (! -d $src_dir);
            } elsif ($sarg =~ /^b/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $base_dir = $sarg;
                prt("Set base directory to [$src_dir].\n") if (VERB1());
            } elsif ($sarg =~ /^i/) {
                $show_ignored = 1;
                prt("Set to output ignored lines. ($show_ignored)\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 ($debug_on) {
        prtw("WARNING: DEBUG is ON\n");
        if (length($in_file) ==  0) {
            $in_file = $def_file;
            prt("Set DEFAULT input to [$in_file]\n");
            ###$load_log = 1;
            $base_dir = $def_base;
            $src_dir = $def_src;
        }
    }
    if (length($in_file) ==  0) {
        pgm_exit(1,"ERROR: No input files found in command!\n");
    }
    if (! -f $in_file) {
        pgm_exit(1,"ERROR: Unable to find in file [$in_file]! Check name, location...\n");
    }
}

sub give_help {
    prt("$pgmname: version $VERS\n");
    prt("Usage: $pgmname [options] in-file\n");
    prt("Options:\n");
    prt(" --help  (-h or -?) = This help, and exit 0.\n");
    prt(" --verb[n]     (-v) = Bump [or set] verbosity. def=$verbosity\n");
    prt(" --load        (-l) = Load LOG at end. ($outfile)\n");
    prt(" --out <file>  (-o) = Write output to this file.\n");
    prt(" --base <dir>  (-b) = Base directory to be stripped from make enter directory.\n");
    prt(" --name <name> (-n) = Set the project name.\n");
    prt(" --src <dir>   (-s) = Current source base directory.\n");
    prt(" --ignored     (-i) = Show ALL ignored lines. (def=$show_ignored)\n");
    prt("\n");
    prt("Note: to be able to verify the source before adding it to the CMakeLists.txt\n");
    prt("      need to be given the base directory to strip from a make entered directory,\n");
    prt("      making it releative, AND a current source directory to search for the source,\n");
    prt("      to establish a new absolute path to the source.\n");
}

# eof - template.pl
