--- /dev/null
+#!/usr/bin/env perl
+# A delegated CNFParser processed rendering of the Document Index Web page, a Model-View-Controller Pattern approuch.
+# The index.cnf script contains the structure and page skeleton,
+# all configuration as well as the HTMLIndexProcessorPlugin converting the CNF to final HTML.
+# It is very convienient, as both style and script for the page is separated and developed in the index.cnf.
+# Which then can be moved to a respective include file over there.
+# This controller binds and provides to the parser to do its magic thing.
+#
+use v5.30;
+use strict;
+use warnings;
+use Exception::Class ('LifeLogException');
+use Syntax::Keyword::Try;
+##
+# We use dynamic perl compilations. The following ONLY HERE required to carp to browser on
+# system requirments or/and unexpected perl compiler errors.
+##
+use CGI::Carp qw(fatalsToBrowser set_message);
+
+BEGIN {
+ sub handle_errors {
+ my $err = shift;
+ say "<html><body><h2>Server Error</h2><pre>Error: $err</pre></body></html>";
+ }
+ set_message(\&handle_errors);
+}
+
+use lib "system/modules";
+require CNFParser;
+require CNFNode;
+
+our $GLOB_HTML_SERVE = "'{}/*.cgi' '{}/*.htm' '{}/*.html' '{}/*.md' '{}/*.txt'";
+our $script_path = $0; $script_path =~ s/\w+.cgi$//;
+
+exit &HTMLPageBuilderFromCNF;
+
+sub HTMLPageBuilderFromCNF {
+ my $html = obtainDirListingHTML('docs');
+ my $cnf = CNFParser -> new (
+ $script_path."index.cnf",{
+ DO_ENABLED => 1, HAS_EXTENSIONS=>1,
+ ANONS_ARE_PUBLIC => 1,
+ PAGE_HEAD => "<h1 id=\"index_head\">Index Page of Docs Directory</h1>",
+ PAGE_CONTENT => $html,
+ PAGE_FOOT => "<!--Not Defined-->"
+ }
+ );
+ my $ptr = $cnf->data();
+ $ptr = $ptr->{'PAGE'};
+ say $$ptr if $ptr;
+ return 0
+}
+
+sub obtainDirListingHTML {
+ my ($dir, $ret) = (shift,"");
+ my $html = listFiles($dir,$script_path,"");
+ if($html){
+ $ret .="<ul><b>$dir →</b>\n";
+ $ret .= $html;
+ opendir (my $handle, $script_path.$dir) or die "Couldn't open directory, $!";
+ while (my $node = readdir $handle) {
+ my $file_full_path = "$script_path$dir/$node";
+ if($node !~ /^\./ && -d $file_full_path){
+ $html = obtainDirListingHTML($dir.'/'.$node);
+ $ret .= $html if $html
+ }
+ }
+ closedir $handle;
+ $ret .= "</ul>";
+ }
+ return $ret;
+}
+
+sub listFiles ($){
+ my ($dir, $script_path, $ret) = @_;
+ my $path = $script_path.$dir;
+ my $spec = $GLOB_HTML_SERVE; $spec =~ s/{}/$path/gp;
+ my @files = glob ($spec);
+ @files = sort {
+ ( $a=~m/\w+[_-]*/ eq $b=~m/\w+[_-]*/ && length $a > length $b) ||
+ $a <=> $b
+ } @files;
+ foreach my $file(@files){
+ ($file =~ m/(\w+\.\w*)$/g);
+ my $name = $1;
+ if($file =~ /\.md$/){
+ my @title = getDocTitle($file);
+ $ret .= qq(\t\t\t<li><a href="$dir/$title[0]">$title[1]</a> ‐ $name</li>\n);
+ }else{
+ $ret .= qq(\t\t\t<li><a href="$dir/$name">$name</a></li>\n);
+ }
+ }
+
+ return $ret;
+}
+
+sub getDocTitle($){
+ my ($file,$ret) = shift;
+ open(my $fh, '<', $file) or LifeLogException->throw("Can't open $file: $!");
+ while (my $line = <$fh>) {
+ if($line =~ /^#+\s*(.*)/){
+ $ret = $1;
+ last;
+ }
+ }
+ close $fh;
+ ($file =~ m/(\w+\.\w*)$/g);
+ return ($1,$ret)
+}
+1;
+=begin copyright
+Programed by : Will Budić
+EContactHash : 990MWWLWM8C2MI8K (https://github.com/wbudic/EContactHash.md)
+Source : https://github.com/wbudic/LifeLog
+Open Source Code License -> https://github.com/wbudic/PerlCNF/blob/master/ISC_License.md
+=cut copyright
--- /dev/null
+!CNF3.3
+
+<<@<%WEBAPP_SETTINGS>
+ $LOG_PATH = data
+ //TODO We are reading only the css property, old way is the following hash, preserved as reminder.
+ $THEME = css => wsrc/main.css, colBG => #c8fff8, colSHDW => #9baec8
+>>
+
+
+<<@<%HTTP_HEADER>
+-charset = "UTF8"
+-expires = "+5s"
+>>
+
+<<CURRENT_THEME<PREPROCESS>
+ package : PreProcessorObtainThemeSettings
+ subroutine : process
+ property : THEME
+>>
+
+<<HEADER<TREE> _HAS_PROCESSING_PRIORITY_
+
+[JS[
+ [@@[wsrc/main.js]@@]
+ [@@[wsrc/feeds.js]@@]
+ [@@[wsrc/jquery.js]@@]
+ [@@[wsrc/jquery-ui.js]@@]
+]JS]
+[CSS[
+ [@@[wsrc/jquery-ui.css]@@]
+ [@@[wsrc/jquery-ui.theme.css]@@]
+ [@@[wsrc/jquery-ui.theme.css]@@]
+ [@@[wsrc/main.css]@@]
+ [@@[wsrc/effects.css]@@]
+ [@@[wsrc/feeds.css]@@]
+]CSS]
+
+<STYLE<
+[#[
+ #container{
+ border: 2px solid #00000017;
+ width: 78%;
+ margin: 0 auto;
+ padding: 0px;
+ }
+
+ #header {
+ border: 1px solid gray;
+ background: rgba(0, 223, 246, 0.13);
+ margin:5px;
+ }
+
+ #content {
+ border: 1px solid gray;
+ border-top:0;
+ text-align: left !important;
+ vertical-align: middle;
+ /*margin:5px;*/
+ background: rgba(0, 223, 246, 0.13);
+ }
+ #tabspanels > div {
+ border: none;
+ text-align:left !important;
+ padding: 0;
+ border-width: 0;
+ }
+ .no-top-border {
+ border-top: none;
+ }
+ .textual {
+ width: 40%;
+ }
+ .textual p::first-letter {
+ color: blueviolet;
+ initial-letter: 3 2;
+ padding-right: 5pt;
+ }
+ #content ul {
+ background: transparent;
+ color:black;
+ margin: 0;
+ padding:5px;
+ }
+ #content li {
+ padding: 0px;
+ margin-left:30px;
+ }
+
+ #content li a:link {
+ font-weight: normal;
+ color:rgb(26, 96, 111);
+ }
+
+ #content li:hover {
+ color: #ff4d21;
+ font-weight: bolder;
+ background: rgba(255,255,255,0.2);
+ }
+
+ #content li a:visited {
+ color: rgb(150, 8, 8);
+ font-weight: bold;
+ }
+
+
+ #footer {
+ border: 1px solid gray;
+ background: rgba(128,128,128,0.2);
+ margin:5px;
+ }
+
+
+ .md_doc {
+ background: white;
+ border: 1px solid gray;
+ border-top: 0;
+ padding: 10px; margin: 5px;
+ text-align: left;
+ overflow-y:scroll;
+ color:black;
+ margin:0 auto;
+ }
+
+ .md_doc ul{
+ font-size: large;
+ }
+
+ .md_doc p{
+ margin: 0 auto;
+ padding: 5px;
+ text-align: left;
+ font-weight: normal;
+ }
+
+ .md_doc blockquote {
+ margin-top: 0;
+ margin-bottom: 16px;
+ background:#b2f8ef;
+ border-left: 3px solid #94cde7;
+ border-top: 2px solid #94cde7;
+ border-right: 2px solid #94cde7;
+ }
+ .md_doc blockquote > :last-child{
+ border-bottom: 2px solid #94cde7;
+ }
+
+ .div_img{
+ height:450px;
+ }
+
+ .md_img{
+ height:80%;
+ }
+
+ code, pre{
+ font-family: 'Droid Sans Mono', 'monospace', monospace;
+ }
+
+ .pre {
+ border:1px solid black;
+ background: rgba(255,255,255,0.2);
+ padding:15px;
+ text-align: left;
+ }
+ .sh {
+ background: black;
+ color: lightgreen;
+ padding: 15px;
+ width: auto;
+ border-radius: .32em;
+ border: 2px solid lightgreen;
+ margin: inherit;
+ margin-right: 30px;
+ }
+
+ div .html {
+ border:1px solid lightgray;
+ background: rgba(255,255,255,0.2);
+ padding:10px;
+ font-family:monospace;
+ text-align: left;
+ }
+
+ div .cnf {
+ border:1px solid lightgray;
+ background: rgba(255,255,255,0.2);
+ padding:10px;
+ font-family:monospace;
+ text-align: left;
+ padding-bottom: 10px;
+ margin-right: 2px;
+ margin-top: 15px;
+ }
+
+ dt{
+ margin-right: 2px;
+ }
+
+ .cnf h1{
+ text-align: left;
+ padding-left: 15px;
+ margin-top: -20px;
+ height: 20px;
+ line-height: 20px;
+ font-size: 15px;
+ }
+
+ .cnf h1 span{
+ background-color: white;
+ border:1px solid lightgray;
+ color:lightgray;
+ font-size:small;
+ padding: 3px;
+ padding-left: 5px;
+ padding-right: 5px;
+ }
+
+
+ div .perl {
+ border:1px solid lightgray;
+ background: rgba(149, 215, 172, 0.2);
+ padding-left:15px;
+ font-family:monospace;
+ text-align: left;
+ padding-bottom: 20px;
+ margin-right: 2px;
+ margin-top: 15px;
+ }
+
+ .mermaid{
+ border:1px solid lightgray;
+ background: transparent;
+ padding-left:15px;
+ text-align: left;
+ padding-bottom: 20px;
+ margin-right: 2px;
+ margin-top: 15px;
+ }
+
+ .perl h1{
+ text-align: left;
+ padding-left: 15px;
+ margin-top: -10px;
+ height: 20px;
+ line-height: 20px;
+ font-size: 15px;
+ }
+
+ .perl h1 span{
+ background: rgba(170, 227, 191, 0.75);
+ border:1px solid lightgray;
+ color:black;
+ font-size:small;
+ padding: 3px;
+ padding-left: 5px;
+ padding-right: 5px;
+ }
+
+ .span_status {
+ position: absolute;
+ /* top: 80px;
+ left:420px;*/
+ border: 2px solid #94cde7;
+ padding: 5px;
+ text-align: center;
+ background: #ccffff;
+ text-decoration-style: wavy;
+ filter: drop-shadow( 10px 8px 5px #3e6f70);
+ z-index:10;
+ }
+
+ div#tabs {
+ background:transparent;
+ border:0;
+ padding: 0;
+ }
+
+ div#tabs ul {
+ background:transparent;
+ }
+
+ div#tabs li {
+ font-style: normal;
+ font-weight: bolder;
+ padding:1px;
+ border-top-left-radius:15px;
+ border-top-right-radius:15px;
+ border-bottom: 0;
+ margin-top: 0;
+
+ }
+
+
+ div#tabs .ui-tabs-anchor {
+ padding: .01em 1em;
+ color:black;
+ }
+ div#tabs ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header, .ui-state-active, a.ui-button:active, .ui-button:active, .ui-button.ui-state-active:hover {
+ border: 1px solid #050506;
+ background: transparent;
+ font-style: normal;
+ font-weight: normal;
+ margin-top: 0px;
+ border-bottom: 0;
+ }
+
+ div#tabspanels .ui-state-active a {
+ color: #472f80;
+ text-decoration-line: underline;
+ }
+
+ #menu_page .menu_head a {
+ background: rgba(255,255,255,.6)
+ }
+ .span-content{
+ border: 1px solid black;
+ text-align: left;
+ background-color: #e6ffff;
+ vertical-align: top;
+ }
+
+]#]
+>STYLE>
+
+<STYLE<
+ <*<MarkdownPlugin::CSS>*>
+>STYLE>
+
+<SCRIPT<
+[#[
+var PREV_DIR_ELE = null;
+function onIndexBodyLoad(){
+ console.log("Initiated page.");
+ $("#status").html("Index page is ready!").show();
+ $("#status").fadeOut(2000);
+
+ selectIDAnchors();
+ $("#content a").prop("visitied",false);
+ onBodyLoadGeneric();
+
+ $( function() {
+ $( "#tabs" ).tabs();
+ });
+ $( "#tabs" ).show();
+}
+
+function loadDocResult(content){
+ $('#doc_display').html(content);
+ $("#status").fadeOut(2000);
+ $(document).scrollTop($("#content_pane").offset().top);
+ if(window.mermaid){
+ window.mermaid.run();
+ }
+ selectIDAnchors();
+ $('#tab_display').click();
+}
+
+function selectIDAnchors(){
+ $("#content a").click(
+ function(e){
+ e.preventDefault();
+ $("#status").prependTo( $("#content_pane").parentElement );
+ $("#status").html("Loading: " + e.target.href).show();
+ $("#status").prependTo(e.target.parentElement);
+ if(PREV_DIR_ELE){
+ PREV_DIR_ELE.parentElement.setAttribute("style","");
+ }
+ e.target.parentElement.setAttribute("style","color: rgb(136, 58, 200); background-color: rgba(244, 241, 241, 0.386); font-size: large; padding-left:1em;border-radius:15px; text-align:center;");
+ PREV_DIR_ELE = e.target;
+ $.post('index.cgi', {action:'load', doc:e.target.getAttribute('href')}, loadDocResult).fail(
+ function(response) {$('#doc_display').html("Service Error: "+response.status,response.responseText)}
+ );
+ }
+ );
+}
+function gotoTabDirListing(){
+ $('#tab_dir_listing').click();
+}
+function gotoTabDisplay(){
+ $('#tab_display').click();
+}
+]#]
+>SCRIPT>
+
+
+
+>>HEADER>TREE>
+
+
+
+###
+# We in plugin mainly access this PAGE property, <*<HEADER>*> is linked in for clarity,
+# and/or if want to change from keeping the original \<\<HEADER<TREE>...\>\> above.
+#
+<<PAGE<TREE> __PROCESS_LAST__
+
+ <*<HEADER>*>
+
+ Title: Index Page
+ OnLoad : onIndexBodyLoad()
+
+<div<
+ id:menu_page
+ <#<
+ <span class="menu_head">
+ <a id="to_bottom" href="#bottom" title="Go to bottom of page.">
+ <span class="ui-icon ui-icon-arrowthick-1-s" style="float:none;"></span></a>
+ <span class="menu_title"> Page </span>
+ <a id="to_top" href="#top" title="Go to top of page.">
+ <span class="ui-icon ui-icon-arrowthick-1-n" style="float:none;"></span></a>
+ </span>
+ <hr>
+ <a class="ui-button ui-corner-all ui-widget" href="app">App</a><br><br>
+ <a class="ui-button ui-corner-all ui-widget" href="index.cgi">Index</a><br><br>
+ <a class="ui-button ui-corner-all ui-widget" href="#htop" onclick="gotoTabDirListing()">< Listing ></a><br><br>
+ <a class="ui-button ui-corner-all ui-widget" href="#htop" onclick="gotoTabDisplay()">< Display ></a>
+ <hr>
+ <a class="ui-button ui-corner-all ui-widget" href="main.cgi">Life Log</a><hr>
+ <a class="ui-button ui-corner-all ui-widget" onclick="return fetchFeeds()">RSS Feeds</a>
+>#>
+ >div>
+<div<
+id:index-content
+class:content
+style:"height:100vh"
+ <div<
+ id:container
+ <div<
+ id:header
+ <*<PAGE_HEAD>*>
+ <a<
+ name: top
+ >a>
+ >div>
+ <div<
+ id:content_pane
+ <span<
+ id:status
+ class:span_status
+ <#<Page getting ready...>#>
+ >span>
+
+ <div<
+ id="tabs"
+ style="height:auto;display:none;"
+ <ul<
+ <#<
+ <li><a id="tab_dir_listing" href="#listing" selected>Directory Listing</a></li>
+ <li><a id="tab_display" href="#display">Display</a></li>
+ >#>
+ >ul>
+ <div<
+ id="tabspanels"
+ class:no-top-border
+ <div<
+ id="listing"
+ class="no-top-border"
+ <div<
+ id="content"
+ <*<PAGE_CONTENT>*>
+ >div>
+ >div>
+ <div<
+ id="display"
+ class="ui-state-active no-top-border league-mono-thin-condensed-tnum"
+ <div<
+ id:doc_display
+ class:md_doc
+ <*<EMPTY_TAB_INFO>*>
+ >div>
+ >div>
+ <script<
+ type="module"
+ <#<
+ import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
+ mermaid.initialize({
+ securityLevel: 'loose',
+ });
+ window.mermaid = mermaid;
+ >#>
+ >script>
+ >div>
+ >div>
+
+
+ <div<
+ id:doc_display_bottom
+ class:md_doc
+ <*<INFO_MD>*>
+ >div>
+ <a<
+ name="feed_top"
+ >a>
+ <a<
+ id: rss_anchor
+ >a>
+ <div<
+ id="feeds"
+ class="rz"
+ style ="margin: 5px; visibility:hidden"
+ [#[ RSS Here ]#]
+ >div>
+ >div>
+ <div<
+ id:footer
+ <*<PAGE_FOOT>*>
+ <span<⌈>span>
+ <a<
+ href:#top
+ title:Goto top of page.
+ onClick: gotoTabDirListing()
+ <#<Show Dir Listing>#>
+ >a>
+
+ <span<⌋ ⌈>span>
+ <a<
+ id:code
+ href:#top
+ title:Go to top of page.
+ <#<To Top Of Page>#>
+ >a>
+
+
+ <span<⌋>span>
+ <a<
+ name: bottom
+ >a>
+ >div>
+ >div>
+ <!<Page brought to you by HTMLIndexProcessorPlugin, from the PerlCNF project.>!>
+>div>
+>>
+
+<<EMPTY_TAB_INFO<ESCAPED>
+### Tab is currently empty!
+\>Please, select a link from the [directory listing](#top|onclick="gotoTabDirListing()") above.**
+\<pre class="mermaid"\>graph TD;
+CNF<-->Application
+Application-->Database
+Application-->WebService
+WebService<-->CNF
+WebService<-->Database
+Database <--> CNF
+\</pre\>
+>>
+
+<<INFO_MD<ESCAPED> _HAS_PROCESSING_PRIORITY_
+
+### INFO
+\> This Page is the Documentation listing coming with the [LifeLog](https://github.com/wbudic/LifeLog) Application.
+\>
+\>[Open Source License](https://choosealicense.com/licenses/isc/)
+
+<h3>CHANGE3</h3>
+<center>Page brought to you by HTMLIndexProcessorPlugin v.<*<HTMLIndexProcessorPlugin::VERSION>*> in Moon Stage (beta).</center>
+
+>>
+
+<<CNF_TO_HTML<PLUGIN> __PROCESS_LAST__
+ package : HTMLIndexProcessorPlugin
+ subroutine : convert
+ property : PAGE
+>>
+
+<<INFO_MD_TO_HTML<PLUGIN> _HAS_PROCESSING_PRIORITY_
+ package : MarkdownPlugin
+ subroutine : convert
+ property : INFO_MD
+>>
+<<INFO_MD_TO_HTM2L<PLUGIN> _HAS_PROCESSING_PRIORITY_
+ package : MarkdownPlugin
+ subroutine : convert
+ property : EMPTY_TAB_INFO
+>>
+<<1>>
--- /dev/null
+#!/usr/bin/env perl
+##
+# Module installer for projects.
+# Run this script from any Project directory containing perl modules or scripts.
+##
+use warnings; use strict;
+###
+# Prerequisites for this script itself. Run first:
+# cpan Term::ReadKey;
+# cpan Term::ANSIColor;
+## no critic (ProhibitStringyEval)
+use Term::ReadKey;
+use Term::ANSIColor qw(:constants);
+
+use constant PERL_FILES_GLOB => "*local/*.pl local/*.pm system/modules/*.pm tests/*.pm tests/*.pl *.pl *.pm *.cgi";
+
+my $project = `pwd`."/".$0; $project =~ s/\/.*.pl$//g; $project =~ s/\s$//g;
+my @user_glob;
+our $PERL_VERSION = $^V->{'original'}; my $ERR = 0; my $key;
+
+print WHITE "\n *** Project Perl Module Installer coded by ",BRIGHT_RED, "https://github.com/wbudic", WHITE,"***", qq(
+ \nRunning scan on project path:$project
+ \nYou have Perl on $^O [$^X] version: $PERL_VERSION\n
+);
+print BLUE "<<@<\@INC<\n# Your default module package paths:\n", YELLOW;
+local $. = 0; foreach(@INC){
+ print $.++.".: $_\n";
+}
+print BLUE ">>\n", RESET;
+if($> > 0){
+ print "You are NOT installing system wide, which is required for webservers CGI.\nAre you sure about this?\n"
+}else{
+ print "You are INSTALLING modules SYSTEM WIDE, are you sure about this?\n"
+}
+if(@ARGV==0){
+ print qq(\nThis program will try to figure out now all the modules
+ required for this project, and install them if missing.
+ This can take some time.
+ );
+ print RED "Do you want to proceed (press either the 'Y'es or 'N'o key)?", RESET;
+
+do{
+ ReadMode('cbreak');
+ $key = ReadKey(0); print "\n";
+ ReadMode('normal');
+ exit 1 if(uc $key eq 'N');
+ $key = "[ENTER]" if $key =~ /\n/;
+ print "You have pressed the '$key' key, that is nice, but why?\nOnly the CTRL+C/Y/N keys do something normal." if uc $key ne 'Y';
+ }while(uc $key ne 'Y');
+}
+else{
+ foreach(@ARGV){
+ if(-d $_){
+ $_ =~ s/\s$//g;
+ print "\nGlobing for perl files in $project/$_";
+ my @located = glob("$_/*.pl $_/*.pm");
+ print " ... found ".@located." files.";
+ push @user_glob, @located;
+
+ }else{
+ warn "Argument: $_ is not a local directory."
+ }
+ }
+}
+
+my @locals=();
+print "\nGlobing for perl modules in project $project";
+my @perl_files = glob(PERL_FILES_GLOB);
+print " ... found ".@perl_files." files.\n";
+push @perl_files, @user_glob;
+my %modules; my %localPackages;
+foreach my $file(@perl_files){
+ next if $0 =~ /$file$/;
+ if($file =~ /(\w*)\.pm$/){
+ $localPackages{$1}=$file;
+ }
+ print "\nExamining:$file\n";
+ my $res = `perl -ne '/\\s*(^use\\s([a-zA-Z:]*))\\W/ and print "\$2;"' $file`;
+ my @list = split(/;+/,$res);
+ foreach(@list){
+ if($_=~ /^\w\d\.\d+.*/){
+ print "\tA specified 'use $_' found in ->$file\n";
+ if($PERL_VERSION ne $_){
+ $_ =~s/^v//g;
+ my @fv = split(/\./, $_);
+ $PERL_VERSION =~s/^v//g;
+ my @pv = split(/\./, $PERL_VERSION);
+ push @fv, 0 if @fv < 3;
+ for my$i(0..$#fv){
+ if( $pv[$i] < $fv[$i] ){
+ $ERR++; print "\n\t\033[31mERROR -> Perl required version has been found not matching.\033[0m\n";
+ last
+ }
+ }
+ }
+ }
+ }
+ foreach(@list){
+ $_ =~ s/^\s*|\s*use\s*//g;
+ $_ =~ s/[\'\"].*[\'\"]$//g;
+ next if !$_ or $_ =~ /^[a-z]|\d*\.\d*$|^\W/;
+ $_ =~ s/\(\)|\(.*\)|qw\(.*\)//g;
+ $modules{$_}=$file if $_;
+ print "$_\n";
+ }
+ if($file=~/\.pm$/){# it is presumed local package module.
+ $locals[@locals] = `perl -ne '/\\s*(^package\\s(\\w+))/ and print "\$2" and exit' $file`;
+ }
+}
+
+print WHITE "\nList of Modules required for thie project:\n";
+my @missing=();
+foreach my $mod (sort keys %modules){
+ my $missing;
+ eval "use $mod";
+ if ($@){
+ $missing[@missing] = $mod;
+ print MAGENTA "\t$mod \t in ", $modules{$mod}," is suspicious?\n";
+ }else{
+ print BLUE "\t$mod\n"
+ }
+}foreach(@missing){
+ if(exists $localPackages{$_}){
+ delete $modules{$_}
+ }else{
+ print BRIGHT_RED $_, MAGENTA, " is missing!\n"
+ }
+}
+my %skip_candidates;
+my $missing_count = @missing;
+if($missing_count>0){
+ foreach my $candidate(@missing){
+ foreach(@locals){
+ if($_ eq $candidate && not exists $skip_candidates{$_}){
+ $missing_count--;
+ $skip_candidates{$_} = 1;
+ print GREEN, "Found the missing $candidate module in locals.\n"
+ }
+ }
+ }
+}
+my $perls = `whereis perl`;
+print GREEN, "Following is all of ",$perls;
+print YELLOW, "Reminder -> Make sure you switched to the right brew release.\n" if $perls =~ /perlbrew/;
+print RESET, "Number of local modules:",scalar(@locals),"\n";
+print RESET, "Number of external modules:",scalar(keys %modules),"\n";
+print RESET, "Number of cpan modules about to be tried to install:",$missing_count,"\n";
+
+print GREEN, qq(
+Do you still want to continue to compile/test/install or check further modules?
+Only the first run is the depest and can take a long time, i.e. if you have to install over 5 modules.
+At other times this will only check further your current status.
+
+Now (press either the 'Y'es or 'N'o key) please?), RESET;
+do{
+ReadMode('cbreak');
+$key = ReadKey(0); print "\n";
+ReadMode('normal');
+ exit 1 if(uc $key eq 'N');
+ $key = "[ENTER]" if $key =~ /\n/;
+ print "You have pressed the '$key' key, that is nice, but why?\nOnly the CTRL+C/Y/N keys do something normal.\n" if uc $key ne 'Y';
+}while(uc $key ne 'Y');
+
+my ($mcnt,$mins) = (0,0);
+my @kangaroos = sort keys %skip_candidates;
+
+##
+# Some modules if found to be needed to be forcefeed. can be hardcoded here my friends, why not?
+##
+foreach ((
+ 'LWP::Simple',
+ 'LWP::Protocol::https',
+ 'XML::LibXML::SAX'
+)){
+ $modules{$_}=1; print "Forcefeed: $_\n"
+}
+
+MODULES_LOOP:
+foreach my $mod (sort keys %modules){
+
+ foreach(@kangaroos){
+ if($_ eq $mod){
+ next MODULES_LOOP
+ }
+ }
+ $mcnt++;
+ ## no critic (ProhibitStringyEval)
+ eval "use $mod";
+ if ($@) {
+ system(qq(perl -MCPAN -e 'install $mod'));
+ if ($? == -1) {
+ print "failed to install: $mod\n";
+ }else{
+ my $v = eval "\$$mod\::VERSION";
+ $v = $v ? "(v$v)" : "";
+ print "Installed module $mod $v!\n";
+ $mins++
+ }
+ }else{
+ $mod =~ s/\s*$//;
+ my $v = eval "\$$mod\::VERSION";
+ $v = $v ? "(v$v)" : "";
+ print "Skipping module $mod $v, already installed!\n";
+ }
+}
+print "\nProject $project\nRequires $mcnt modules.\nInstalled New: $mins\n";
+print "WARNING! - This project requires in ($ERR) parts code that might not be compatible yet with your installed/running version of perl (v$PERL_VERSION).\n"
+if $ERR;
+
+
+=begin copyright
+Programed by : Will Budic
+EContactHash : 990MWWLWM8C2MI8K (https://github.com/wbudic/EContactHash.md)
+Source : https://github.com/wbudic/PerlCNF.git
+Documentation : Specifications_For_CNF_ReadMe.md
+ This source file is copied and usually placed in a local directory, outside of its repository project.
+ So it could not be the actual or current version, can vary or has been modiefied for what ever purpose in another project.
+ Please leave source of origin in this file for future references.
+Open Source Code License -> https://github.com/wbudic/PerlCNF/blob/master/ISC_License.md
+=cut copyright