$err";
+ }
+ 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 &CNFHTMLService;
+
+sub CNFHTMLService {
+ my ($cgi,$ptr) = (CGI -> new(),undef); $cgi->param('service', 'feeds');
+ my $cnf = CNFParser -> new (undef,{ DO_ENABLED => 1, HAS_EXTENSIONS=>1, ANONS_ARE_PUBLIC => 1, CGI=>$cgi });
+ $cnf->parse(undef,_getServiceScript($cgi));
+ $ptr = $cnf->data();
+ $ptr = $ptr->{'PAGE'};
+ say $$ptr if $ptr;
+ # open my $fh, ">dump_of_output_to_browser.html";
+ # print $fh $$ptr;
+ # close $fh;
+ return 0
+}
+
+sub _getServiceScript($cgi) {
+ my $service = $cgi->param('service'); $service = "undef" if not $service;
+ if($service eq 'feeds'){
+ return _CNF_Script_For_Feeds();
+ }
+ CNFHTMLServiceError->throw(error=>"UNKNOWN SERVICE -> $service", show_trace=>1)
+}
+
+sub _CNF_Script_For_Feeds {
+<<__CNF_IS_COOL__;
+<
+
+ RUN_FEEDS = yes
+ CONVERT_TO_CNF_NODES = yes
+ OUTPUT_TO_CONSOLE = false
+ OUTPUT_TO_MD = no
+ BENCHMARK = no
+ TZ=Australia/Sydney
+ OUTPUT_DIR = "./rss_output"
+
+
+ CONVERT_CNF_HTML = yes
+ CNF_TREE_STORE = true
+
+ package : RSSFeedsPlugin
+ subroutine : process
+ property : RSS_FEEDS
+
+>>
+// Following is a table having a list of details for available RSS feeds to process.
+|| The more rows have here the longer it takes to fetch them, what is it, once a day, week, month?
+<< RSS_FEEDS
+ID`Name`URL`Description~
+01`CPAN`http://search.cpan.org/uploads.rdf`CPAN modules news and agenda.~
+>>
+__CNF_IS_COOL__
+}
+
+
+1;
+
+=begin copyright
+Programed by : Will Budic
+EContactHash : 990MWWLWM8C2MI8K (https://github.com/wbudic/EContactHash.md)
+Source : https://github.com/wbudic/LifeLog
+ 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
\ No newline at end of file
diff --git a/htdocs/cgi-bin/index.cgi b/htdocs/cgi-bin/index.cgi
index d8c475d..812e4d1 100755
--- a/htdocs/cgi-bin/index.cgi
+++ b/htdocs/cgi-bin/index.cgi
@@ -1,6 +1,6 @@
#!/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,
+# 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.
@@ -15,15 +15,15 @@ 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
+# 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 "
+ ]#]
+ >Description>
+ >Item>
+ Description>
+ >Item>
+ Description>
+ >Item>
+ create a HTTP::Request from a curl command line
Changes for 0.52 - 2023-09-15
Switch test suite to Test2::V0
+
Suppress trivial Content-Length headers in ->as_curl
+
--disable is a boolean option
+
Add support for --json
+
Add support for --max-filesize The behaviour is slightly different from cURL. --max-filesize looks at the size of the body, not at the Content-Length header.
+
Add support for --interface (only IP addresses, not interface names)
+
Fix Mojolicious snippet generation with local address
+
+ ]#]
+ >Description>
+ >Item>
+ The C Perl-Powered Pre-Processor
Changes for 0.001_01 - 2023-09-15
Basic intermingling of perl variables in C code
+
Arbitrary perl in '##' directives
+
Magic indent adjustment after variable substitution
+
+ ]#]
+ >Description>
+ >Item>
+ VMware vCloud API
Changes for 2.404 - 2023-09-14
Added: POD test
+
Improved: 'use warnings' on all modules
+
+ ]#]
+ >Description>
+ >Item>
+ Blacklist prereqs using an Acme::CPANModules module
Changes for 0.002 - 2023-07-09
No functional changes.
+
Remove the usage of smartmatch.
+
+ ]#]
+ >Description>
+ >Item>
+ A Perl wrapper around OpenSSL's AES library
Changes for 0.07
Significant updates since 0.02
+
Detailed Changes
+
+ ]#]
+ >Description>
+ >Item>
+ Description>
+ >Item>
+ Execute external programs or internal code blocks as separate process.
Changes for 0.34 - 2023-09-14T19:55:18Z
Adapt to deprecation of spurt in upstream Mojolicious
+
Make git work in github workflow
+
Turn warnings "Sleeping inside locked section" into notes
+
Avoid warnings about using undefined value as file handle
tidied up documentation. gave the NameTab widget it's own pm file added -autoupdate option added -onlyselect option addid -rigid option added -unselecttabcall option fixed some minor bugs
+
+ ]#]
+ >Description>
+ >Item>
+ Provides generalized and structured configuration value access
Changes for 2.004
Switched to Perl6::Junction to avoid smartmatch warnings from Syntax::Keyword::Junction
require a recent enough ExtUtils::CppGuess and set the required C++ standard
+
allow the zxing-cpp package name for pkg-config, which seems to be what packagers used before upstream decided on "zxing.pc". https://github.com/tonycoz/imager-zxing/issues/1
+
+ ]#]
+ >Description>
+ >Item>
+ System Calls for File IO, User, Process, Signal, Socket
Changes for 0.491 - 2023-09-13
New Features
+
Incompatible Changes
+
+ ]#]
+ >Description>
+ >Item>
+ Log::Dispatch::Email subclass that sends mail using Email::Mailer
Changes for 1.13 - 2023-09-12T21:12:32-07:00
Require exact v1.23 (resolves issue #5)
+
+ ]#]
+ >Description>
+ >Item>
+ Original program path locations extension for exact
Changes for 1.05 - 2023-09-12T21:10:17-07:00
Remove redundant strict (since it's provided by exact)
+
New import signature change required by exact v1.23
Improve/fix import of packages into other packages (resolves issue #4)
+
+ ]#]
+ >Description>
+ >Item>
+ "Mail Merge" or just substitute tokens in ODF documents
Changes for 1.000
ODF::MailMerge::Engine->new positional args eliminated; now use proto_elt => $table # specify the object directly context => $context, proto_tag => "tagstring" # search for it Modifier :die ("Delete If Empty") replaces :delempty
+
+ ]#]
+ >Description>
+ >Item>
+ Description>
+ >Item>
+ Description>
+ >Item>
+ Fetch actual raw streamable URLs from various radio-station, video & podcast websites.
Changes for 2.19 - 2023-09-12
StreamFinder::Youtube - 1) Fix failure to fetch artist, icon, etc. sometimes on embedded IFRAME urls (slight site changes) and first episode from some channels. 2) Add -youtube-site argument to specify a different default youtube site (default https://www.youtube.com). 3) Add ability to parse youtube channel URLs containing an at-sign, ie.: https://www.youtube.com/@channelID.
+
StreamFinder::Subsplash - Restore as EXPERIMENTAL, as this site seems to now work again, at least for audio streams on some sites.
- deprecated complementary, will be removed at 2.0
+
+ ]#]
+ >Description>
+ >Item>
+ Find Smallest Set of Smallest Rings in graphs
Changes for 0.1.0 - 2022-12-15
Initial release.
+
+ ]#]
+ >Description>
+ >Item>
+ Pure perl API for Proxmox virtualisation
Changes for 0.38 - 2023-09-11
fix/use correct parameter name for user in tests thanks to MartijnLivaart
+
Feat/check new arguments thanks to MartijnLivaart
+
Fix/test access directory thanks to MartijnLivaart via GH#27
+
feat: check if debug parameter propagates from new() thanks to MartijnLivaart via GH#29
+
Pod corrections thanks to poptix via GH#31
+
+ ]#]
+ >Description>
+ >Item>
+ >Brew>
+>>
diff --git a/htdocs/cgi-bin/system/modules/CNFDateTime.pm b/htdocs/cgi-bin/system/modules/CNFDateTime.pm
new file mode 100644
index 0000000..966cb77
--- /dev/null
+++ b/htdocs/cgi-bin/system/modules/CNFDateTime.pm
@@ -0,0 +1,111 @@
+###
+# CNFDateTime objects provide conversions from script to high precision time function not inbuild into perl interpreter.
+# They are lightly initilized, compared to using DateTime directly, so this is not merely a wrapper around DateTime.
+#
+package CNFDateTime;
+use strict;
+use warnings;
+use DateTime;
+use DateTime::Format::DateParse;
+use Time::HiRes qw(time usleep);
+use feature 'signatures';
+
+use constant{
+ FORMAT => '%Y-%m-%d %H:%M:%S',
+ FORMAT_NANO => '%Y-%m-%d %H:%M:%S.%3N %Z',
+ FORMAT_SCHLONG => '%A, %d %B %Y %H:%M:%S %Z',
+ FORMAT_MEDIUM => '%d %b %Y %H:%M:%S',
+ DEFAULT_TIME_ZONE => 'UTC'
+};
+
+sub new {
+ my $class = shift;
+ my %settings;
+ if(ref($_[0]) eq ''){
+ %settings = @_;
+ }else{
+ %settings = %{$_[0]}
+ }
+ $settings{epoch} = time if !$settings{epoch};
+ $settings{TZ} = DEFAULT_TIME_ZONE if !$settings{TZ};
+ return bless \%settings, $class
+}
+
+sub datetime($self) {
+ return $self->{datetime} if exists $self->{datetime};
+ $self->{epoch} = time if not defined $self->{epoch};
+ my $dt = DateTime->from_epoch(epoch=>$self->{epoch},time_zone=>$self->{TZ});
+ $self->{datetime} = $dt;
+ return $dt
+}
+sub toTimestamp($self) {
+ return $self->{timestamp} if exists $self->{timestamp};
+ usleep(1_028_69);
+ $self->{timestamp} = $self->datetime() -> strftime(FORMAT_NANO)
+}
+sub toTimestampShort($self) {
+ return $self->{timestamp} if exists $self->{timestamp};
+ usleep(1_028_69);
+ $self->{timestamp} = $self->datetime() -> strftime(FORMAT)
+}
+sub toSchlong($self){
+ return $self->{long} if exists $self->{long};
+ $self->{long} = $self->datetime() -> strftime(FORMAT_SCHLONG)
+}
+sub _toCNFDate ($formated, $timezone) {
+ my $dt = DateTime::Format::DateParse->parse_datetime($formated, $timezone);
+ return new('CNFDateTime',{epoch => $dt->epoch, datetime=>$dt, TZ=>$timezone});
+}
+sub _listAvailableCountryCodes(){
+ require DateTime::TimeZone;
+ return DateTime::TimeZone->countries();
+}
+sub _listAvailableTZ($country){
+ require DateTime::TimeZone;
+ return length($country)==2?DateTime::TimeZone->names_in_country( $country ):DateTime::TimeZone->names_in_category( $country );
+}
+
+
+1;
+
+=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
+
+=begin history
+Implementing the DateTime module with local libraries, was always problematic at some stages
+as the Perl build or running environment changes.
+
+It is huge and in minimal form usually delivered with default basic or minimal Perl setups.
+It in full provides the most compressive list of world locales and timezones possibilities.
+This means language translations and many other, formats of date outputs based on the locale expected look.
+
+PerlCNF doesn't need it in reality as has its own fixed format to accept and produce.
+PerlCNF must also support world timezones.
+
+Hence it needs DateTime, and some of its modules to provide its timezone string and convert it back and forth.
+Other, DateTime::Format::DateParse, module itself is small, compared to the DateTime module.
+
+Without proper dev. tools and what to look for, it is very hard to figure out what is going on, that things fail.
+For example at the production site. But not on the development setup.
+
+2023-08-23
+
+On occasions DateTime in the past, since 5 eight years to this day, it would lib error crash the whole Perl running environment.
+Something veryhard to find and correct also to forsure test on various installations.
+For these and other reasons, the PerlCNF datetime format was avoided from being implemented or needed.
+
+However, CNFDateTime in its first inclination attempts again to encapsulate this long time due functionality of requirements.
+Came to life in the final of PerlCNF v.2.9, along with the new PerlCNF instruction DATE, of the release.
+
+TestManager has also now been updated to capture any weird and possible Perl underlying connectors to libraries,
+which are of no concern what so ever to the actual local code being tested.
+
+=cut history
\ No newline at end of file
diff --git a/htdocs/cgi-bin/system/modules/CNFNode.pm b/htdocs/cgi-bin/system/modules/CNFNode.pm
index 7136781..f1fc238 100644
--- a/htdocs/cgi-bin/system/modules/CNFNode.pm
+++ b/htdocs/cgi-bin/system/modules/CNFNode.pm
@@ -1,13 +1,6 @@
-#
+###
# Represents a tree node CNF object having children and a parent node if it is not the root.
-# Programed by : Will Budic
-# Notice - This source file is copied and usually placed in a local directory, outside of its 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.
-# Source of Origin : https://github.com/wbudic/PerlCNF.git
-# Documentation : Specifications_For_CNF_ReadMe.md
-# Open Source Code License -> https://choosealicense.com/licenses/isc/
-#
+###
package CNFNode;
use strict;
use warnings;
@@ -33,17 +26,37 @@ sub list {shift -> {'@@'}}
sub script {shift -> {'~'}}
sub priority {shift -> {'^'}}
sub evaluate {shift -> {'&'}}
+###
+# Obtains this nodes all public attributes.
+# What you usually only want.
+###
sub attributes {
my $self = shift;
- my @nodes;
+ my @attributes;
my $regex = PRIVATE_FIELDS();
- foreach(sort keys %$self){
- my $node = $self->{$_};
- if($_ !~ /$regex/){
- $nodes[@nodes] = [$_, $node]
+ foreach (sort keys %$self){;
+ if($_ !~ /^$regex/){
+ $attributes[@attributes] = [$_, $self->{$_}]
+ }
+ }
+ return @attributes;
+}
+###
+# Utility arrays any attributes by list requested.
+# $node-> array('Name','#') will return node 'Name' attribute and value if it has it, onderwise undef for either.
+###
+sub array {
+ my $self = shift;
+ my @attributes = @_;
+ my @arr;
+ foreach my $next(@attributes){
+ my $val = $self -> {$next};
+ if(ref($val) eq 'SCALAR'){
+ $val = $$val;
}
+ $arr[@arr] = $val#'['.$next.':'.$val.']';
}
- return @nodes;
+ return @arr;
}
sub nodes {
my $self = shift;
@@ -53,7 +66,23 @@ sub nodes {
}
return ();
}
-
+###
+# Add another CNFNode to this one, to become a its parent.
+# Returns $self so you can perl them, if you want..
+##
+sub add {
+ my ($self, $node, @nodes) = @_;
+ my $prev = $self->{'@$'};
+ if($prev) {
+ @nodes = @$prev;
+ }else{
+ @nodes = ();
+ }
+ $node->{'@'} = \$self;
+ $nodes[@nodes] = $node;
+ $self -> {'@$'} = \@nodes;
+ return $self;
+}
###
# Convenience method, returns string scalar value dereferenced (a copy) of the property value.
##
@@ -65,7 +94,7 @@ sub val {
if(!$ret && $self->{'@$'}){ #return from subproperties.
my $buf;
my @arr = @{$self->{'@$'}};
- foreach my $node(@arr){
+ foreach my $node(@arr){
$buf .= $node -> val() ."\n";
}
return $buf;
@@ -78,14 +107,14 @@ sub val {
my $meta = meta(SHELL());
sub _evaluate {
- my $value = shift;
+ my $value = shift;
if($value =~ s/($meta)//i){
$value =~ s/^`|`\s*$/""/g; #we strip as a possible monkey copy had now redundant meta in the value.
$value = '`'.$value.'`';
}
- ## no critic BuiltinFunctions::ProhibitStringyEval
+ ## no critic BuiltinFunctions::ProhibitStringyEval
my $ret = eval $value;
- ## use critic
+ ## use critic
if ($ret){
chomp $ret;
return $ret;
@@ -97,21 +126,29 @@ sub val {
#
+sub items(){
+ my $self = shift;
+ return $self -> {'@$'}
+}
+
###
# Search select nodes based on from a path statement.
-# It will always return an array for even a single subproperty.
-# The reason is several subproperties of the same name can be contained by the parent property.
+# It will always return an array for even a single subproperty with a passed path ending with (/*).
+# The reason is several subproperties of the same name can be contained as elements of this node.
# It will return an array of list values with (@@).
-# Or will return an array of its shallow list of child nodes with (@$).
+# Or will return an array of its shallow list of child nodes with (@$).
# Or will return an scalar value of an attribute or an property with (#).
# NOTICE - 20221222 Future versions might provide also for more complex path statements with regular expressions enabled.
###
sub find {
- my ($self, $path, $ret, $prev, $seekArray,$ref)=@_;
- foreach my $name(split(/\//, $path)){
- if(ref($self) eq "ARRAY"){
+ my ($self, $path, $ret, $prev, $seekArray,$ref)=@_; my @arr;
+ foreach my $name(split(/\//, $path)){
+ if( $name eq "*" && @arr){
+ return \@arr # The path instructs to return an array, which is set but return is set to single only found element.
+ }
+ elsif(ref($self) eq "ARRAY"){
if($name eq '#'){
- if(ref($ret) eq "ARRAY"){
+ if(ref($ret) eq "ARRAY"){
next
}else{
return $prev->val()
@@ -119,11 +156,10 @@ sub find {
}elsif($name =~ /\[(\d+)\]/){
$self = $ret = @$ret[$1];
next
-
}else{
- $ret = $prev->{'@$'};
+ $ret = $prev->{'@$'};
}
- }else{
+ }else{
if ($name eq '@@') {
$ret = $self->{'@@'}; $seekArray = 1;
next
@@ -138,7 +174,7 @@ sub find {
}
$ref = ref($ret);
if(!$seekArray && $ref eq 'ARRAY'){ # ret can be an array of parent same name elemenents.
- foreach my$n(@$ret) {
+ foreach my$n(@$ret) {
if ($n->node($name)){
$ret = $n; last
}
@@ -146,21 +182,24 @@ sub find {
}elsif($ref eq "CNFNode" && $seekArray){
$ret = $ret->{$name};
next
- }else{
- $ret = $self->{'@$'} if ! $seekArray; # This will initiate further search in subproperties names.
+ }else{
+ if (!$seekArray){
+ # This will initiate further search in subproperties names.
+ $ret = $self->{'@$'};
+ @arr = ();
+ }
}
}
$ref = ref($ret);
if($ret && $ref eq 'ARRAY'){
my $found = 0;
- my @arr;
- undef $prev;
+ undef $prev;
foreach my $ele(@$ret){
- if($seekArray && exists $ele->{'@$'}){
- foreach my$node(@{$ele->{'@$'}}){
+ if($seekArray && exists $ele->{'@$'}){
+ foreach my$node(@{$ele->{'@$'}}){
if ($node->{'_'} eq $name){
$arr[@arr] = $ele = $node;
- }
+ }
}
if(@arr>1){
$ret = \@arr;
@@ -199,10 +238,10 @@ sub find {
}
}
elsif($name && $ref eq "CNFNode"){
- $ret = $ret -> {$name}
- }
+ $ret = $ret -> {$name}
+ }
}
- return $ret;
+ return !$ret?\@arr:$ret;
}
###
# Similar to find, put simpler node by path routine.
@@ -216,13 +255,13 @@ sub node {
if($ret){
foreach(@$ret){
if ($_->{'_'} eq $path){
- return $_;
+ return $_;
}
}
}
return
}
- foreach my $name(split(/\//, $path)){
+ foreach my $name(split(/\//, $path)){
$ret = $self->{'@$'};
if($ret){
foreach(@$ret){
@@ -232,19 +271,18 @@ sub node {
}
}
}
- return $ret;
+ return $ret;
}
-
###
# Outreached subs list of collected node links found in a property.
my @linked_subs;
###
-# The parsing guts of the CNFNode, that from raw script, recursively creates and tree of nodes from it.
+# The parsing guts of the CNFNode, that from raw script, recursively creates a tree of nodes from it.
###
sub process {
- my ($self, $parser, $script)=@_;
+ my ($self, $parser, $script)=@_;
my ($sub, $val, $isArray,$isShortifeScript,$body) = (undef,0,0,0,"");
my ($tag,$sta,$end)=("","",""); my $meta_shortife = &meta_node_in_shortife;
my ($opening,$closing,$valing)=(0,0,0);
@@ -254,28 +292,28 @@ sub process {
$val = $self->{'#'};
if($val){
$val .= "\n$script";
- }else{
+ }else{
$val = $script;
}
}else{
my @lines = split(/\n/, $script);
foreach my $ln(@lines){
- $ln =~ s/^\s+|\s+$//g;
- if(length ($ln)){
- my $isShortife = ($ln =~ s/($meta_shortife)/""/sexi);
+ $ln =~ s/^\s+|\s+$//g;
+ if(length ($ln)){
+ my $isShortife = ($ln =~ s/($meta_shortife)/""/sexi);
if($ln =~ /^([<>\[\]])(.*)([<>\[\]])$/ && $1 eq $3){
$sta = $1;
$tag = $2;
- $end = $3;
- $isShortifeScript = 1 if $isShortife;
+ $end = $3;
+ $isShortifeScript = 1 if $isShortife;
my $isClosing = ($sta =~ /[>\]]/) ? 1 : 0;
if($tag =~ /^([#\*\@]+)[\[<](.*)[\]>]\/*[#\*\@]+$/){#<- The \/ can sneak in as included in closing tag.
if($1 eq '*'){
my $link = $2;
- my $rval = $self -> obtainLink($parser, $link);
- if($rval){
+ my $rval = $self -> obtainLink($parser, $link);
+ if($rval){
if($opening){
- $body .= qq($ln\n);
+ $body .= qq($ln\n);
}else{
#Is this a child node?
if(exists $self->{'@'}){
@@ -284,7 +322,7 @@ sub process {
if($prev) {
@nodes = @$prev;
}else{
- @nodes = ();
+ @nodes = ();
}
$nodes[@nodes] = CNFNode->new({'_'=>$link, '*'=>$rval,'@' => \$self});
$self->{'@$'} = \@nodes;
@@ -292,20 +330,20 @@ sub process {
else{
#Links scripted in main tree parent are copied main tree attributes.
$self->{$link} = $rval
- }
+ }
}
next
- }else{
+ }else{
if(!$opening){warn "Anon link $link not located with $ln for node ".$self->{'_'}};
}
}elsif($1 eq '@@'){
if($opening==$closing){
- $array[@array] = $2; $val="";
- next
+ $array[@array] = $2; $val="";
+ next
}
- }else{
- $val = $2;
- }
+ }else{
+ $val = $2;
+ }
}elsif($tag =~ /^(.*)[\[<]\/*(.*)[\]>](.*)$/ && $1 eq $3){
if($opening){
$body .= qq($ln\n)
@@ -317,21 +355,21 @@ sub process {
if($prev) {
@nodes = @$prev;
}else{
- @nodes = ();
- }
+ @nodes = ();
+ }
$nodes[@nodes] = $property;
$self->{'@$'} = \@nodes;
}
next
}elsif($isClosing){
$opening--;
- $closing++;
+ $closing++;
}else{
$opening++;
- $closing--;
+ $closing--;
}
- if(!$sub){
+ if(!$sub){
$isArray = $isArray? 0 : 1 if $tag =~ /@@/;
$sub = $tag; $body = "";
next
@@ -339,20 +377,20 @@ sub process {
if($opening==$closing){
if($tag eq '#'){
$body =~ s/\s$//;#cut only one last nl if any.
- if(!$val){
+ if(!$val){
$val = $body;
- }else{
+ }else{
$val .= $body
}
$valing = 0;
$tag ="" if $isClosing
}else{
my $property = CNFNode->new({'_'=>$sub, '@' => \$self});
- my $a = $isArray;
+ my $a = $isArray;
if($isShortifeScript){
- my ($sub,$prev,$cnt_nl,$bck_p);
+ my ($sub,$prev,$cnt_nl,$bck_p);
while ($body =~ / (.*)__+ ([\\\|]|\/*) | (.*)[:=](.*) | (.*)\n/gmx){
- my @sel = @{^CAPTURE};
+ my @sel = @{^CAPTURE};
if(defined $sel[0]){
if ($sel[1]){
my $t = substr $sel[1],0,1;
@@ -369,11 +407,12 @@ sub process {
$sub = $parent; next
}
}
- $sub = CNFNode->new({'_'=>$sel[0], '@' => $parent});
+ $t = $sel[0]; $t=~s/[\s_]*$//g;
+ $sub = CNFNode->new({'_' => $t, '@' => $parent});
my @elements = exists $parent -> {'@$'} ? $parent -> {'@$'} : ();
$elements[@elements] = $sub; $prev = $parent; $cnt_nl = 0;
$parent -> {'@$'} = \@elements;
- }
+ }
}
elsif (defined $sel[2] && defined $sel[3]){
my $attribute = $sel[2]; $attribute =~ s/^\s*|\s*$//g;
@@ -388,9 +427,9 @@ sub process {
elsif (defined $sel[4]){
if ($sel[4] eq ''){
if(++$cnt_nl>1){ #cancel collapse chain and at root of property that is shorted.
- ##$sub = $property ;
+ ##$sub = $property ;
$cnt_nl =0
- }
+ }
next
}elsif($sel[4] !~ /^\s*\#/ ){
my $parent = $sub ? $sub->parent() : $property;
@@ -402,7 +441,7 @@ sub process {
# $sub ="";
}
}
- }#while
+ }#while
$isShortifeScript = 0;
}else{
$property -> process($parser, $body);
@@ -410,7 +449,7 @@ sub process {
$isArray = $a;
if($tag eq '@@'){
$array[@array] = $property;
- if( not exists $property->{'#'} && $body ){
+ if( not exists $property->{'#'} && $body ){
$body =~ s/\n$//; $property->{'#'} = $body
}
}else{
@@ -419,30 +458,30 @@ sub process {
if($prev) {
@nodes = @$prev;
}else{
- @nodes = ();
+ @nodes = ();
}
$nodes[@nodes] = $property;
$self->{'@$'} = \@nodes;
}
undef $sub; $body = $val = "";
}
- next
+ next
}else{
# warn "Tag $sta$tag$sta failed closing -> $body"
- }
- }
+ }
+ }
}elsif($tag eq '#'){
$valing = 1;
}elsif($opening==0 && $isArray){
- $array[@array] = $ln;
- }elsif($opening==0 && $ln =~ /^([<\[])(.+)([<\[])(.*)([>\]])(.+)([>\]])$/ &&
+ $array[@array] = $ln;
+ }elsif($opening==0 && $ln =~ /^([<\[])(.+)([<\[])(.*)([>\]])(.+)([>\]])$/ &&
$1 eq $3 && $5 eq $7 ){ #<- tagged in line
if($2 eq '#') {
if($val){$val = "$val $4"}
- else{$val = $4}
+ else{$val = $4}
}elsif($2 eq '*'){
my $link = $4;
- my $rval = $self -> obtainLink($parser, $link);
+ my $rval = $self -> obtainLink($parser, $link);
if($rval){
#Is this a child node?
if(exists $self->{'@'}){
@@ -451,7 +490,7 @@ sub process {
if($prev) {
@nodes = @$prev;
}else{
- @nodes = ();
+ @nodes = ();
}
$nodes[@nodes] = CNFNode->new({'_'=>$link, '*'=>$rval, '@' => \$self});
$self->{'@$'} = \@nodes;
@@ -460,29 +499,29 @@ sub process {
#Links scripted in main tree parent are copied main tree attributes.
$self->{$link} = $rval
}
- }else{
+ }else{
warn "Anon link $link not located with '$ln' for node ".$self->{'_'} if !$opening;
}
}elsif($2 eq '@@'){
- $array[@array] = CNFNode->new({'_'=>$2, '#'=>$4, '@' => \$self});
+ $array[@array] = CNFNode->new({'_'=>$2, '#'=>$4, '@' => \$self});
}else{
- my $property = CNFNode->new({'_'=>$2, '#'=>$4, '@' => \$self});
+ my $property = CNFNode->new({'_'=>$2, '#'=>$4, '@' => \$self});
my @nodes;
my $prev = $self->{'@$'};
if($prev) {
@nodes = @$prev;
}else{
- @nodes = ();
+ @nodes = ();
}
$nodes[@nodes] = $property;
$self->{'@$'} = \@nodes;
}
- next
+ next
}elsif($val){
$val = $self->{'#'};
if($val){
$self->{'#'} = qq($val\n$ln\n);
- }else{
+ }else{
$self->{'#'} = qq($ln\n);
}
}
@@ -494,31 +533,31 @@ sub process {
my @attr = ($ln =~ m/([\s\w]*?)\s*[=:]\s*(.*)\s*/);
if(@attr>1){
my $n = $attr[0];
- my $v = $attr[1];
+ my $v = $attr[1];
if($v =~ /[<\[]\*[<\[](.*)[]>\]]\*[>\]]/){
- $v = $self-> obtainLink($parser, $1)
- } $v =~ m/^(['"]).*(['"])$/g;
- $v =~ s/^$1|$2$//g if($1 && $2 && $1 eq $2);
- $self->{$n} = $v;
+ $v = $self-> obtainLink($parser, $1)
+ } $v =~ m/^(['"]).*(['"])$/g;
+ $v =~ s/^$1|$2$//g if($1 && $2 && $1 eq $2);
+ $self->{$n} = $v;
next;
- }else{
+ }else{
$val = $ln if $val;
- }
+ }
}
# Very complex rule, allow #comment lines in buffer withing an node value tag, ie [#[..]#]
- $body .= qq($ln\n) #if !$tag && $ln!~/^\#/ || $tag eq '#'
+ $body .= qq($ln\n) #if !$tag && $ln!~/^\#/ || $tag eq '#'
}
elsif($tag eq '#'){
$body .= qq(\n)
}
- }
+ }
}
$self->{'@@'} = \@array if @array;
$self->{'#'} = \$val if $val;
## no critic BuiltinFunctions::ProhibitStringyEval
- no strict 'refs';
+ no strict 'refs';
while(@linked_subs){
- my $entry = pop (@linked_subs);
+ my $entry = pop (@linked_subs);
my $node = $entry->{node};
my $res = &{+$entry->{sub}}($node);
$entry->{node}->{'*'} = \$res;
@@ -530,16 +569,16 @@ sub obtainLink {
my ($self,$parser,$link, $ret) = @_;
## no critic BuiltinFunctions::ProhibitStringyEval
no strict 'refs';
- if($link =~/(.*)(\(\.\))$/){
+ if($link =~/(.*)(\(\.\))$/){
push @linked_subs, {node=>$self,link=>$link,sub=>$1};
return 1;
}elsif($link =~/(\w*)::\w+$/){
use Module::Loaded qw(is_loaded);
if(is_loaded($1)){
- $ret = \&{+$link}($self);
+ $ret = \&{+$link}($self);
}
- }
- $ret = $parser->obtainLink($link) if !$ret;
+ }
+ $ret = $parser->obtainLink($link) if !$ret;
return $ret;
}
@@ -548,14 +587,14 @@ sub obtainLink {
#
sub validate {
my $self = shift;
- my ($tag,$sta,$end,$lnc,$errors)=("","","",0,0);
+ my ($tag,$sta,$end,$lnc,$errors)=("","","",0,0);
my (@opening,@closing,@singels);
my ($open,$close) = (0,0);
- my @lines = defined $self -> script() ? split(/\n/, $self->script()) :();
+ my @lines = defined $self -> script() ? split(/\n/, $self->script()) :();
foreach my $ln(@lines){
$ln =~ s/^\s+|\s+$//g;
$lnc++;
- #print $ln, "<-","\n";
+ #print $ln, "<-","\n";
if(length ($ln)){
#print $ln, "\n";
if($ln =~ /^([<>\[\]])(.*)([<>\[\]])(.*)/ && $1 eq $3){
@@ -573,7 +612,7 @@ sub validate {
$close++;
push @closing, {T=>$tag, idx=>$close, L=>$lnc, N=>($open-$close+1),S=>$sta};
}
- else{
+ else{
push @opening, {T=>$tag, idx=>$open, L=>$lnc, N=>($open-$close),S=>$sta};
$open++;
}
@@ -586,14 +625,14 @@ sub validate {
my $c = pop @closing;
if(!$c){
$errors++;
- warn "Error unclosed tag-> [".$o->{T}.'[ @'.$o->{L}
+ warn "Error unclosed tag-> [".$o->{T}.'[ @'.$o->{L}
}
- }
+ }
}else{
my $errors = 0; my $error_tag; my $nesting;
my $cnt = $#opening;
- for my $i (0..$cnt){
- my $o = $opening[$i];
+ for my $i (0..$cnt){
+ my $o = $opening[$i];
my $c = $closing[$cnt - $i];
if($o->{T} ne $c->{T}){
print '['.$o->{T}."[ idx ".$o->{idx}." line ".$o->{L}.
@@ -604,21 +643,21 @@ sub validate {
if($o->{T} ne $c->{T}){
my $j = $cnt;
- for ($j = $cnt; $j>-1; $j--){ # TODO 2023-0117 - For now matching by tag name,
+ for ($j = $cnt; $j>-1; $j--){ # TODO 2023-0117 - For now matching by tag name,
$c = $closing[$j];# can't be bothered, to check if this will always be appropriate.
last if $c -> {T} eq $o->{T}
}
- print "\t search [".$o->{T}.'[ idx '.$o->{idx} .' line '.$o->{L}.
+ print "\t search [".$o->{T}.'[ idx '.$o->{idx} .' line '.$o->{L}.
' top found: ]'.$c->{T}."] idx ".$c->{idx}." line ".$c->{N}." loops: $j \n" if $self->{DEBUG};
}else{next}
if($o->{T} ne $c->{T} && $o->{N} ne $c->{N}){
- cluck "Error opening and clossing tags mismatch for ".
+ cluck "Error opening and clossing tags mismatch for ".
_brk($o).' ln: '.$o->{L}.' idx: '.$o->{idx}.
' wrongly matched with '._brk($c).' ln: '.$c->{L}.' idx: '.$c->{idx}."\n";
$errors++;
}
- }
+ }
}
return $errors;
}
@@ -631,39 +670,39 @@ sub validate {
# Compare one node with another if is equal in structure.
##
sub equals {
- my ($self, $node, $ref) = @_; $ref = ref($node);
+ my ($self, $node, $ref) = @_; $ref = ref($node);
if (ref($node) eq 'CNFNode'){
my @s = sort keys %$self;
- my @o = sort keys %$node;
+ my @o = sort keys %$node;
my $i=$#o;
foreach (0..$i){
my $n = $o[$i-$_];
if($n eq '~' || $n eq '^'){
- splice @o,$i-$_,1;
- }
+ splice @o,$i-$_,1;
+ }
}
$i=$#s;
foreach (0..$i){
my $n = $s[$i-$_];
if($n eq '~' || $n=~/^CNF_/ || $n=~/^DO_/){
- splice @s,$i-$_,1;
- }
+ splice @s,$i-$_,1;
+ }
}$i=0;
if(@s == @o){
foreach(@s) {
if($_ ne $o[$i++]){
return 0
}
- }
+ }
if($self -> {'@$'} && $node -> {'@$'}){
@s = sort keys @{$self -> {'@$'}};
- @o = sort keys @{$node -> {'@$'}};
+ @o = sort keys @{$node -> {'@$'}};
$i = 0;
foreach(@s) {
if($_ ne $o[$i++]){
return 0
}
- }
+ }
}
return 1;
}
@@ -671,4 +710,87 @@ sub equals {
return 0;
}
-1;
\ No newline at end of file
+sub toScript {
+ my($self,$nested,$script)= @_;
+ my($isParent,$tag,$tab); $nested=1 if!$nested; $tab =3*$nested; $tab = ' 'x$tab;
+ if(not exists $self->{'@'}){
+ $script .= "<<".$self->{_}."\n"; $isParent = 1;
+ }else{
+ $tag = $self->{_};
+ if($nested){
+ $script .= "$tab<$tag<\n"
+ }else{
+ $script .= "$tab\[$tag\[\n"
+ }
+ }
+ my @attr = $self -> attributes();
+ foreach (@attr){
+ if($nested){
+ if(@$_[0] ne '#' && @$_[0] ne '_'){
+ if(@$_[1]){
+ $script .= "$tab ".@$_[0].": ".@$_[1];
+ }else{
+ $script .= "$tab ".@$_[0]." ";
+ }
+ }
+ }else{
+ if(@$_[0] ne '#' && @$_[0] ne '_'){
+ if(@$_[1]){
+ $script .= "$tab ".@$_[0]."=\"".@$_[1]."\"";
+ }else{
+ $script .= "$tab ".@$_[0]." ";
+ }
+ }
+ }
+ $script .= "\n"
+ }
+ my $list = $self->{'@@'};
+ if($list){
+ foreach(@$list) {
+ $script .= "$tab <@@<$_>@@>\n"
+ }
+ }
+
+ my $nodes = $self->{'@$'};
+ if($nodes){
+ foreach my $nd (@$nodes) {
+ my $ref = ref($nd);
+ $nd = $$nd if ($ref eq 'REF');
+ if (ref($nd) eq 'CNFNode'){
+ $script .= toScript($nd, $nested+1);
+ }
+ }
+ }
+ my $val = $self->{'#'};
+ if($val){
+ if(ref($val) eq 'SCALAR'){
+ $val = $$val;
+ }
+ $val =~ s/\n/\n$tab /gs; $val =~ s/\s*$//;
+ $script .= $tab."[#\[\n$tab $val\n$tab]#]\n"
+ }
+
+ if ($isParent){
+ $script .= ">>\n"
+ }else{
+ if($nested){
+ $script .= "$tab>$tag>\n"
+ }else{
+ $script .= "$tab]$tag]\n"
+ }
+ }
+ return $script;
+}
+
+1;
+
+=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
\ No newline at end of file
diff --git a/htdocs/cgi-bin/system/modules/CNFParser.pm b/htdocs/cgi-bin/system/modules/CNFParser.pm
index 8a7c554..9d69dd4 100644
--- a/htdocs/cgi-bin/system/modules/CNFParser.pm
+++ b/htdocs/cgi-bin/system/modules/CNFParser.pm
@@ -1,31 +1,23 @@
+###
# Main Parser for the Configuration Network File Format.
-# Programed by : Will Budic
-# Notice - This source file is copied and usually placed in a local directory, outside of its 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.
-# Source of Origin : https://github.com/wbudic/PerlCNF.git
-# Documentation : Specifications_For_CNF_ReadMe.md
-# Open Source Code License -> https://choosealicense.com/licenses/isc/
-#
+##
package CNFParser;
use strict;use warnings;#use warnings::unused;
-use Exception::Class ('CNFParserException');
+use Exception::Class ('CNFParserException');
use Syntax::Keyword::Try;
use Hash::Util qw(lock_hash unlock_hash);
-use Time::HiRes qw(time);
-use DateTime;
require CNFMeta; CNFMeta::import();
require CNFNode;
+require CNFDateTime;
-
-# Do not remove the following no critic, no security or object issues possible.
+# Do not remove the following no critic, no security or object issues possible.
# We can use perls default behaviour on return.
##no critic qw(Subroutines::RequireFinalReturn)
##no critic Perl::Critic::Policy::ControlStructures::ProhibitMutatingListFunctions
-use constant VERSION => '2.9';
+use constant VERSION => '3.0';
our @files;
our %lists;
our %properties;
@@ -38,15 +30,15 @@ our %ANONS;
#private -> Instance fields:
my $anons;
my %includes;
- my %instructs;
+ my %instructs;
###
-# CNF Instruction tag covered reserved words.
-# You probably don't want to use these as your own possible instruction implementation.
+# CNF Instruction tag covered reserved words.
+# You can't use any of these as your own possible instruction implementation, unless in lower case.
###
-our %RESERVED_WORDS = map +($_, 1), qw{ CONST CONSTANT DATA VARIABLE VAR
- FILE TABLE TREE INDEX
- VIEW SQL MIGRATE DO LIB
+our %RESERVED_WORDS = map +($_, 1), qw{ CONST CONSTANT DATA DATE VARIABLE VAR
+ FILE TABLE TREE INDEX
+ VIEW SQL MIGRATE DO LIB PROCESSOR
PLUGIN MACRO %LOG INCLUDE INSTRUCTOR };
sub isReservedWord { my ($self, $word)=@_; return $word ? $RESERVED_WORDS{$word} : undef }
@@ -61,10 +53,10 @@ our $CONSTREQ = 0;
# Create a new CNFParser instance.
# $path - Path to some .cnf_file file, to parse, not compsuluory to add now? Make undef.
# $attrs - is reference to hash of constances and settings to dynamically employ.
-# $del_keys - is a reference to an array of constance attributes to dynamically remove.
-sub new { my ($class, $path, $attrs, $del_keys, $self) = @_;
+# $del_keys - is a reference to an array of constance attributes to dynamically remove.
+sub new { my ($class, $path, $attrs, $del_keys, $self) = @_;
if ($attrs){
- $self = \%$attrs;
+ $self = \%$attrs;
}else{
$self = {
DO_ENABLED => 0, # Enable/Disable DO instruction. Which could evaluated potentially be an doom execute destruction.
@@ -72,15 +64,16 @@ sub new { my ($class, $path, $attrs, $del_keys, $self) = @_;
ENABLE_WARNINGS => 1, # Disable this one, and you will stare into the void, about errors or operations skipped.
STRICT => 1, # Enable/Disable strict processing to FATAL on errors, this throws and halts parsing on errors.
HAS_EXTENSIONS => 0, # Enable/Disable extension of custom instructions. These is disabled by default and ingored.
- DEBUG => 0, # Not internally used by the parser, but possible a convience bypass setting for code using it.
- CNF_CONTENT => "", # Origin of the script, this wull be set by the parser, usually the path of a script file or is direct content.
- };
- }
+ DEBUG => 0, # Not internally used by the parser, but possible a convienince bypass setting for code using it.
+ CNF_CONTENT => "", # Origin of the script, this will be set by the parser, usually the path of a script file or is direct content.
+ RUN_PROCESSORS => 1, # When enabled post parse processors are run, are these outside of the scope of the parsers executions.
+ };
+ }
$CONSTREQ = $self->{CONSTANT_REQUIRED};
if (!$self->{ANONS_ARE_PUBLIC}){ #Not public, means are private to this object, that is, anons are not static.
$self->{ANONS_ARE_PUBLIC} = 0; #<- Caveat of Perl, if this is not set to zero, it can't be accessed legally in a protected hash.
$self->{__ANONS__} = {};
- }
+ }
if(exists $self->{'%LOG'}){
if(ref($self->{'%LOG'}) ne 'HASH'){
die '%LOG'. "passed attribute is not an hash reference."
@@ -88,9 +81,10 @@ sub new { my ($class, $path, $attrs, $del_keys, $self) = @_;
$properties{'%LOG'} = $self->{'%LOG'}
}
}
- $self->{STRICT} = 1 if not exists $self->{STRICT}; #make strict by default if missing.
+ $self->{STRICT} = 1 if not exists $self->{STRICT}; #make strict by default if missing.
$self->{ENABLE_WARNINGS} = 1 if not exists $self->{ENABLE_WARNINGS};
$self->{HAS_EXTENSIONS} = 0 if not exists $self->{HAS_EXTENSIONS};
+ $self->{RUN_PROCESSORS} = 1 if not exists $self->{RUN_PROCESSORS}; #Bby default enabled, disable during script dev.
$self->{CNF_VERSION} = VERSION;
$self->{__DATA__} = {};
bless $self, $class; $self->parse($path, undef, $del_keys) if($path);
@@ -98,34 +92,36 @@ sub new { my ($class, $path, $attrs, $del_keys, $self) = @_;
}
#
-sub import {
+sub import {
my $caller = caller; no strict "refs";
{
*{"${caller}::configDumpENV"} = \&dumpENV;
*{"${caller}::anon"} = \&anon;
- *{"${caller}::SQL"} = \&SQL;
+ *{"${caller}::SQL"} = \&SQL;
+ *{"${caller}::isCNFTrue"} = \&_isTrue;
+ *{"${caller}::now"} = \&now;
}
- return 1;
+ return 1;
}
our $meta_has_priority = meta_has_priority();
our $meta_priority = meta_priority();
our $meta_on_demand = meta_on_demand();
###
-# The metaverse is that further this can be expanded,
+# The metaverse is that further this can be expanded,
# to provide further dynamic meta processing of the property value of an anon.
# When the future becomes life in anonymity, unknown variables best describe the meta state.
##
package META_PROCESS {
sub constance{
- my($class, $set) = @_;
+ my($class, $set) = @_;
if(!$set){
$set = {anonymous=>'*'}
}
bless $set, $class
}
sub process{
- my($self, $property, $val) = @_;
+ my($self, $property, $val) = @_;
if($self->{anonymous} ne '*'){
return $self->{anonymous}($property,$val)
}
@@ -140,14 +136,20 @@ return <<__JSON
{"$property"="$val"}
__JSON
}
-
-
-
+###
+# Check a value if it is CNFPerl boolean true.
+# For isFalse just negate check with not, as undef is concidered false or 0.
+##
+sub _isTrue{
+ my $value = shift;
+ return 0 if(not $value);
+ return ($value =~ /1|true|yes|on/i)
+}
###
# Post parsing instructed special item objects. They have lower priority to Order of apperance and from CNFNodes.
##
package InstructedDataItem {
-
+
our $dataItemCounter = int(0);
sub new { my ($class, $ele, $ins, $val) = @_;
@@ -159,7 +161,7 @@ package InstructedDataItem {
ins => $ins,
val => $val,
'^' => $priority
- }, $class
+ }, $class
}
sub toString {
my $self = shift;
@@ -169,25 +171,26 @@ package InstructedDataItem {
#
###
-# PropertyValueStyle objects must have same rule of how an property body can be scripted for attributes.
+# PropertyValueStyle objects must have same rule of how a property body can be scripted for attributes.
##
-package PropertyValueStyle {
+package PropertyValueStyle {
sub new {
my ($class, $element, $script, $self) = @_;
$self = {} if not $self;
$self->{element}=$element;
if($script){
- my ($p,$v);
+ my ($p,$v);
foreach my $itm($script=~/\s*(\w*)\s*[:=]\s*(.*)\s*/gm){
if($itm){
if(!$p){
$p = $itm;
}else{
+ $itm =~ s/^\s*(['"])(.*)\g{1}$/$2/g if $itm;
$self->{$p}=$itm;
undef $p;
}
- }
+ }
}
}else{
warn "PropertyValue process what?"
@@ -197,7 +200,7 @@ package PropertyValueStyle {
sub setPlugin{
my ($self, $obj) = @_;
$self->{plugin} = $obj;
- }
+ }
sub result {
my ($self, $value) = @_;
$self->{value} = $value;
@@ -213,14 +216,14 @@ package PropertyValueStyle {
# i.e. ${CNFParser->new()->anon()}{'MyDynamicAnon'} = 'something';
# However a private config instance, will have its own anon's. And could be read only if it exist as a property, via this anon(NAME) method.
# This hasn't been yet fully specified in the PerlCNF specs.
-# i.e. ${CNFParser->new({ANONS_ARE_PUBLIC=>0})->anon('MyDynamicAnon') # <-- Will not be available.
+# i.e. ${CNFParser->new({ANONS_ARE_PUBLIC=>0})->anon('MyDynamicAnon') # <-- Will not be available.
##
sub anon { my ($self, $n, $args)=@_;
my $anechoic = \%ANONS;
if(ref($self) ne 'CNFParser'){
$n = $self;
- }elsif (not $self->{'ANONS_ARE_PUBLIC'}){
- $anechoic = $self->{'__ANONS__'};
+ }elsif (not $self->{'ANONS_ARE_PUBLIC'}){
+ $anechoic = $self->{'__ANONS__'};
}
if($n){
my $ret = %$anechoic{$n};
@@ -230,26 +233,26 @@ sub anon { my ($self, $n, $args)=@_;
if($ref eq 'META_PROCESS'){
my @arr = ($ret =~ m/(\$\$\$.+?\$\$\$)/gm);
foreach my $find(@arr) {# <- MACRO TAG translate. ->
- my $s= $find; $s =~ s/^\$\$\$|\$\$\$$//g;#
+ my $s= $find; $s =~ s/^\$\$\$|\$\$\$$//g;#
my $r = %$anechoic{$s};
if(!$r && exists $self->{$s}){#fallback to maybe constant property has been seek'd?
$r = $self->{$s};
}
if(!$r){
- warn "Unable to find property to translate macro expansion: $n -> $find\n"
+ warn "Unable to find property to translate macro expansion: $n -> $find\n"
unless $self and not $self->{ENABLE_WARNINGS}
}else{
- $ret =~ s/\Q$find\E/$r/g;
+ $ret =~ s/\Q$find\E/$r/g;
}
}
$ret = $args->process($n,$ret);
}elsif($ref eq 'HASHREF'){
- foreach my $key(keys %$args){
+ foreach my $key(keys %$args){
if($ret =~ m/\$\$\$$key\$\$\$/g){
my $val = %$args{$key};
$ret =~ s/\$\$\$$key\$\$\$/$val/g;
- }
+ }
}
}elsif($ref eq 'ARRAY'){ #we rather have argument passed as an proper array then a list with perl
my $cnt = 1;
@@ -260,11 +263,11 @@ sub anon { my ($self, $n, $args)=@_;
}else{
my $val = %$anechoic{$args};
$ret =~ s/\$\$\$$args\$\$\$/$val/g;
- warn "Scalar argument passed $args, did you mean array to pass? For property $n=$ret\n"
- unless $self and not $self->{ENABLE_WARNINGS}
+ warn "Scalar argument passed $args, did you mean array to pass? For property $n=$ret\n"
+ unless $self and not $self->{ENABLE_WARNINGS}
}
}
- my $ref = ref($ret);
+ my $ref = ref($ret);
return $$ret if $ref eq "REF";
return $ret->val() if $ref eq "CNFNode";
return $ret;
@@ -275,7 +278,7 @@ sub anon { my ($self, $n, $args)=@_;
###
# Validates and returns a constant named value as part of this configs instance.
# Returns undef if it doesn't exist, and exception if constance required is set;
-sub const { my ($self,$c)=@_;
+sub const { my ($self,$c)=@_;
if(exists $self->{$c}){
return $self->{$c}
}
@@ -285,32 +288,43 @@ sub const { my ($self,$c)=@_;
###
# Collections are global, Reason for this is that any number of subsequent files parsed,
-# might contain properties that overwrite previous existing ones.
-# Or require ones that don't includes, expecting thm to be there.
+# might contain properties that overwrite previous existing ones.
+# Or require ones that don't include, and expecting them to be there.
# This overwritting can be erronous, but also is not expected to be very common to happen.
# Following method, provides direct access to the properties, this method shouldn't be used in general.
sub collections {\%properties}
-# Collection now returns the contained type dereferenced.
+#@Deprecated use property subroutine instead.
+sub collection {
+return property(@_);
+}
+###
+# Collection now returns the contained type dereferenced and is concidered a property.
# Make sure you use the appropriate Perl type on the receiving end.
# Note, if properties contain any scalar key entry, it sure hasn't been set by this parser.
-sub collection { my($self, $name) = @_;
+#
+sub property { my($self, $name) = @_;
if(exists($properties{$name})){
my $ret = $properties{$name};
- if(ref($ret) eq 'ARRAY'){
+ my $ref = ref($ret);
+ if($ref eq 'ARRAY'){
return @{$ret}
- }else{
+ }elsif($ref eq 'PropertyValueStyle'){
+ return $ret;
+ }
+ else{
return %{$ret}
}
}
return %properties{$name}
}
-sub data {shift->{'__DATA__'}}
-sub listDelimit {
- my ($this, $d , $t)=@_;
+sub data {return shift->{'__DATA__'}}
+
+sub listDelimit {
+ my ($this, $d , $t)=@_;
my @p = @{$lists{$t}};
- if(@p&&$d){
+ if(@p&&$d){
my @ret = ();
foreach (@p){
my @s = split $d, $_;
@@ -319,13 +333,13 @@ sub listDelimit {
$lists{$t}=\@ret;
return @{$lists{$t}};
}
- return;
+ return;
}
sub lists {\%lists}
sub list {
- my $t=shift;if(@_ > 0){$t=shift;}
- my $an = $lists{$t};
- return @{$an} if defined $an;
+ my $t=shift;if(@_ > 0){$t=shift;}
+ my $an = $lists{$t};
+ return @{$an} if defined $an;
die "Error: List name '$t' not found!"
}
@@ -346,14 +360,16 @@ sub addENVList { my ($self, @vars) = @_;
}return;
}
-
-sub template { my ($self, $property, %macros) = @_;
+###
+# Perform a macro replacement on tagged strings in a property value.
+##
+sub template { my ($self, $property, %macros) = @_;
my $val = $self->anon($property);
- if($val){
+ if($val){
foreach my $m(keys %macros){
my $v = $macros{$m};
$m ="\\\$\\\$\\\$".$m."\\\$\\\$\\\$";
- $val =~ s/$m/$v/gs;
+ $val =~ s/$m/$v/gs;
}
my $prev;
foreach my $m(split(/\$\$\$/,$val)){
@@ -372,7 +388,7 @@ sub template { my ($self, $property, %macros) = @_;
}
}
return $val;
- }
+ }
}
#
@@ -383,20 +399,20 @@ sub doInstruction { my ($self,$e,$t,$v) = @_;
$t = "" if not defined $t;
if($t eq 'CONST' or $t eq 'CONSTANT'){#Single constant with mulit-line value;
- $v =~ s/^\s//;
- # Not allowed to overwrite constant. i.e. it could be DO_ENABLED which is restricted.
+ # It is NOT allowed to overwrite constant.
if (not $self->{$e}){
- $self->{$e} = $v if not $self->{$e};
+ $v =~ s/^\s//;
+ $self->{$e} = $v;
}else{
warn "Skipped constant detected assignment for '$e'.";
}
}
elsif($t eq 'VAR' or $t eq 'VARIABLE'){
- $v =~ s/^\s//;
- $anons->{$e} = $v;
+ $v =~ s/^\s//;
+ $anons->{$e} = $v;
}
elsif($t eq 'DATA'){
- $v=~ s/^\n//;
+ $v=~ s/^\n//;
foreach(split /~\n/,$v){
my @a;
$_ =~ s/\\`/\\f/g;#We escape to form feed the found 'escaped' backtick so can be used as text.
@@ -415,7 +431,7 @@ sub doInstruction { my ($self,$e,$t,$v) = @_;
}
push @a, $v;
}
- else{
+ else{
if($t =~ /^\#(.*)/) {#First is usually ID a number and also '#' signifies number.
$d = $1;#substr $d, 1;
$d=0 if !$d; #default to 0 if not specified.
@@ -425,19 +441,31 @@ sub doInstruction { my ($self,$e,$t,$v) = @_;
push @a, $d;
}
}
- }
+ }
my $existing = $self->{'__DATA__'}{$e};
if(defined $existing){
my @rows = @$existing;
- push @rows, [@a] if scalar @a >0;
+ push @rows, [@a] if scalar @a >0;
$self->{'__DATA__'}{$e} = \@rows
}else{
- my @rows; push @rows, [@a];
- $self->{'__DATA__'}{$e} = \@rows if scalar @a >0;
+ my @rows; push @rows, [@a];
+ $self->{'__DATA__'}{$e} = \@rows if scalar @a >0;
}
- }
-
+ }
+
+ }elsif($t eq 'DATE'){
+ if($v && $v !~ /now|today/i){
+ $v =~ s/^\s//;
+ if($self->{STRICT}&&$v!~/^\d\d\d\d-\d\d-\d\d/){
+ $self-> warn("Invalid date format: $v expecting -> YYYY-MM-DD at start as possibility of DD-MM-YYYY or MM-DD-YYYY is ambiguous.")
+ }
+ $v = CNFDateTime::_toCNFDate($v,$self->{'TZ'});
+
+ }else{
+ $v = CNFDateTime->new(TZ=>$self->{'TZ'});
+ }
+ $anons->{$e} = $v;
}elsif($t eq 'FILE'){#@TODO Test case this
my ($i,$path,$cnf_file) = (0,"",$self->{CNF_CONTENT});
$v=~s/\s+//g;
@@ -486,27 +514,27 @@ sub doInstruction { my ($self,$e,$t,$v) = @_;
push @a, $d
}
else{
- push @a, $d;
- }
- }
+ push @a, $d;
+ }
+ }
my $existing = $self->{'__DATA__'}{$e};
if(defined $existing){
my @rows = @$existing;
- push @rows, [@a] if scalar @a >0;
+ push @rows, [@a] if scalar @a >0;
$self->{'__DATA__'}{$e} = \@rows
}else{
- my @rows; push @rows, [@a];
- $self->{'__DATA__'}{$e} = \@rows if scalar @a >0;
+ my @rows; push @rows, [@a];
+ $self->{'__DATA__'}{$e} = \@rows if scalar @a >0;
}
- }
+ }
}
- }
- }
+ }
+ }
}elsif($t eq 'INCLUDE'){
$includes{$e} = {loaded=>0,path=>$e,v=>$v};
}elsif($t eq 'TREE'){
my $tree = 0;
- if (!$v){
+ if (!$v){
$v = $e;
$e = 'LAST_DO';
}
@@ -516,12 +544,12 @@ sub doInstruction { my ($self,$e,$t,$v) = @_;
if( $v =~ s/$meta_priority/""/sexi){
$priority = $2;
}
- $tree = CNFNode->new({'_'=>$e,'~'=>$v,'^'=>$priority});
+ $tree = CNFNode->new({'_'=>$e,'~'=>$v,'^'=>$priority});
$tree->{DEBUG} = 1 if $self->{DEBUG};
$instructs{$e} = $tree;
- }elsif($t eq 'TABLE'){ # This has now be late bound and send to the CNFSQL package. since v.2.6
- SQL()->createTable($e,$v) } # It is hardly been used. But in future itt might change.
- elsif($t eq 'INDEX'){ SQL()->createIndex($v)}
+ }elsif($t eq 'TABLE'){ # This has now be late bound and send to the CNFSQL package. since v.2.6
+ SQL()->createTable($e,$v) } # It is hardly been used. But in future this might change.
+ elsif($t eq 'INDEX'){ SQL()->createIndex($v)}
elsif($t eq 'VIEW'){ SQL()->createView($e,$v)}
elsif($t eq 'SQL'){ SQL($e,$v)}
elsif($t eq 'MIGRATE'){SQL()->migrate($e, $v)
@@ -529,7 +557,7 @@ sub doInstruction { my ($self,$e,$t,$v) = @_;
elsif($t eq 'DO'){
if($DO_ENABLED){
my $ret;
- if (!$v){
+ if (!$v){
$v = $e;
$e = 'LAST_DO';
}
@@ -538,14 +566,14 @@ sub doInstruction { my ($self,$e,$t,$v) = @_;
}
if( $v =~ s/($meta_priority)/""/sexi){
$priority = $2;
- }
+ }
if($v=~ s/($meta_on_demand)/""/ei){
$anons->{$e} = CNFNode -> new({'_'=>$e,'&'=>$v,'^'=>$priority});
return;
}
- ## no critic BuiltinFunctions::ProhibitStringyEval
+ ## no critic BuiltinFunctions::ProhibitStringyEval
$ret = eval $v if not $ret;
- ## use critic
+ ## use critic
if ($ret){
chomp $ret;
$anons->{$e} = $ret;
@@ -558,15 +586,15 @@ sub doInstruction { my ($self,$e,$t,$v) = @_;
}
}elsif($t eq 'LIB'){
if($DO_ENABLED){
- if (!$v){
- $v = $e;
- $e = 'LAST_LIB';
- }
+ if (!$v){
+ $v = $e;
+ $e = 'LAST_LIB';
+ }
try{
use Module::Load;
autoload $v;
$v =~ s/^(.*\/)*|(\..*)$//g;
- $anons->{$e} = $v;
+ $anons->{$e} = $v;
}catch{
$self->warn("Module DO_ENABLED library failed to load: $v\n");
$anons->{$e} = '<>';
@@ -576,35 +604,41 @@ sub doInstruction { my ($self,$e,$t,$v) = @_;
$anons->{$e} = '<>';
}
}
- elsif($t eq 'PLUGIN'){
+ elsif($t eq 'PLUGIN'){
if($DO_ENABLED){
- $instructs{$e} = InstructedDataItem -> new($e, 'PLUGIN', $v);
+ $instructs{$e} = InstructedDataItem -> new($e, 'PLUGIN', $v);
}else{
$self->warn("DO_ENABLED is set to false to process following plugin: $e\n")
- }
+ }
}
- elsif($t eq 'INSTRUCTOR'){
+ elsif($t eq 'PROCESSOR'){
+ if(not $self->registerProcessor($e, $v)){
+ CNFParserException->throw("PostParseProcessor Registration Failed for '<<$e<$t>$v>>'!\t");
+ }
+ }
+ elsif($t eq 'INSTRUCTOR'){
if(not $self->registerInstructor($e, $v) && $self->{STRICT}){
CNFParserException->throw("Instruction Registration Failed for '<<$e<$t>$v>>'!\t");
}
}
+ elsif($t eq 'MACRO'){
+ $instructs{$e}=$v;
+ }
elsif(exists $instructors{$t}){
if(not $instructors{$t}->instruct($e, $v) && $self->{STRICT}){
CNFParserException->throw("Instruction processing failed for '<<$e<$t>>'!\t");
}
}
- elsif($t eq 'MACRO'){
- $instructs{$e}=$v;
- }else{
- #Register application statement as either an anonymous one. Or since v.1.2 a listing type tag.
+ else{
+ #Register application statement as either an anonymous one. Or since v.1.2 a listing type tag.
if($e !~ /\$\$$/){ #<- It is not matching {name}$$ here.
if($self->{'HAS_EXTENSIONS'}){
$anons->{$e} = InstructedDataItem->new($e,$t,$v)
}else{
- $v = $t if not $v;
+ $v = $t if not $v;
if($e=~/^\$/){
$self->{$e} = $v if !$self->{$e}; # Not allowed to overwrite constant.
- }else{
+ }else{
$anons->{$e} = $v
}
}
@@ -616,8 +650,8 @@ sub doInstruction { my ($self,$e,$t,$v) = @_;
my $array = $lists{$e};
if(!$array){$array=();$lists{$e} = \@{$array};}
push @{$array}, $v;
- }
- }
+ }
+ }
}
###
@@ -626,11 +660,11 @@ sub doInstruction { my ($self,$e,$t,$v) = @_;
sub parse { my ($self, $cnf_file, $content, $del_keys) = @_;
my @tags;
- if($self->{'ANONS_ARE_PUBLIC'}){
+ if($self->{'ANONS_ARE_PUBLIC'}){
$anons = \%ANONS;
- }else{
+ }else{
$anons = $self->{'__ANONS__'};
- }
+ }
#private %includes; for now we keep on possible multiple calls to parse.
#private instructs on this parse call.
%instructs = ();
@@ -639,12 +673,12 @@ sub parse { my ($self, $cnf_file, $content, $del_keys) = @_;
unlock_hash(%$self);
if(not $content){
- open(my $fh, "<:perlio", $cnf_file ) or die "Can't open $cnf_file -> $!";
- read $fh, $content, -s $fh;
+ open(my $fh, "<:perlio", $cnf_file ) or die "Can't open $cnf_file -> $!";
+ read $fh, $content, -s $fh;
close $fh;
my @stat = stat($cnf_file);
- $self->{CNF_STAT} = \@stat;
- $self->{CNF_CONTENT} = $cnf_file;
+ $self->{CNF_STAT} = \@stat;
+ $self->{CNF_CONTENT} = $cnf_file;
}else{
my $type =Scalar::Util::reftype($content);
if($type && $type eq 'ARRAY'){
@@ -658,9 +692,9 @@ sub parse { my ($self, $cnf_file, $content, $del_keys) = @_;
my $spc = $content =~ /\n/ ? '(<{2,3}?)(<*.*?>*)(>{2,3})' : '(<{2,3}?)(<*.*?>*?)(>{2,3})$';
- @tags = ($content =~ m/$spc/gms);
+ @tags = ($content =~ m/$spc/gms);
- foreach my $tag (@tags){
+ foreach my $tag (@tags){
next if not $tag;
next if $tag =~ m/^(>+)|^(<<)/;
if($tag =~ m/^<(\w*)\s+(.*?)>*$/gs){ # Original fastest and early format: <<>>
@@ -668,29 +702,28 @@ sub parse { my ($self, $cnf_file, $content, $del_keys) = @_;
my $v = $2;
if(isReservedWord($self,$t)){
my $isVar = ($t eq 'VARIABLE' || $t eq 'VAR');
- if($t eq 'CONST' or $isVar){ #constant multiple properties.
- foreach my $line(split '\n', $v) {
- $line =~ s/^\s+|\s+$//; # strip unwanted spaces
+ if($t eq 'CONST' or $isVar){ #constant multiple properties.
+ foreach my $line(split '\n', $v) {
+ $line =~ s/^\s+|\s+$//; # strip unwanted spaces
$line =~ s/\s*>$//;
- $line =~ m/([\$\w]*)(\s*=\s*)(.*)/g;
+ $line =~ m/([\$\w]*)(\s*=\s*)(.*)/g;
my $name = $1;
- $line = $3;
+ $line = $3; $line =~ s/^\s*(['"])(.*)\g{1}$/$2/;#strip quotes
if(defined $name){
if($isVar){
- $line =~ s/^\s*["']|['"]\s*$//g;#strip qoutes
$anons ->{$name} = $line if $line
}else{
- if($line and not $self->{$name}){# Not allowed to overwrite constant.
- $line =~ s/^\s*["']|['"]\s*$//g;#strip qoutes
- $self->{$name} = $line;
- }else{
+ if($line and not $self->{$name}){# Not allowed to overwrite constant.
+
+ $self->{$name} = $line;
+ }else{
warn "Skipping and keeping previously set constance -> [$name] the new value ".
($line eq $self->{$name})?"matches it":"dosean't match -> $line."
- }
+ }
}
}
}
- }else{
+ }else{
doInstruction($self,$v,$t,undef);
}
}else{
@@ -701,7 +734,7 @@ sub parse { my ($self, $cnf_file, $content, $del_keys) = @_;
}else{
#vars are e-element,t-token or instruction,v- for value, vv -array of the lot.
my ($e,$t,$v,@vv);
-
+
# Check if very old format and don't parse the data for old code compatibility to (still) do it.
# This is interesting, as a newer format file is expected to use the DATA instruction and final data specified script rules.
if($CNF_VER eq 'CNF2.2' && $tag =~ m/(\w+)\s*(<\d+>\s)\s*(.*\n)/mg){#It is old DATA format annon
@@ -713,7 +746,7 @@ sub parse { my ($self, $cnf_file, $content, $del_keys) = @_;
}
# Before mauling into possible value types, let us go for the full expected tag specs first:
# <<{$sig}{name}<{INSTRUCTION}>{value\n...value\n}>>
- # Found in ->
+ # Found in ->
if($tag !~ /\n/ && $tag =~ /^([@%\$\.\/\w]+)\s*([ <>]+)(\w*>)(.*)/) {
$e = $1;
$t = $2;
@@ -725,7 +758,7 @@ sub parse { my ($self, $cnf_file, $content, $del_keys) = @_;
$t = $3;
$v = $5;
}
- }else{
+ }else{
#############################################################################
$tag =~ m/\s*([@%\$\.\/\w]+)\s* # The name.
([ <>\n]) # begin or close of instruction, where '\n' mark in script as instruction less.
@@ -735,39 +768,39 @@ sub parse { my ($self, $cnf_file, $content, $del_keys) = @_;
(>$)* # capture above value up to here from buffer, i.e. if comming from a >>> tag.
/gmxs; ###############################################################################
- $e =$1;
+ $e =$1;
if($e eq '@' or $2 eq '<' or ($2 eq '>' and !$4)){
- $t = $3;
+ $t = $3;
}else{
$t = $1;
- $e = $3
+ $e = $3
}
$v= $5;
$v =~ s/>$//m if defined($4) && $4 eq '<' or $6; #value has been crammed into an instruction?
-
+
}
if(!$v && !$RESERVED_WORDS{$t}){
- $v= $t;
- }
+ $v= $t;
+ }
$v =~ s/\\/>/g;# escaped brackets from v.2.8.
-
- #Do we have an autonumbered instructed list?
+
+ #Do we have an autonumbered instructed list?
#DATA best instructions are exempted and differently handled by existing to only one uniquely named property.
#So its name can't be autonumbered.
- if ($e =~ /(.*?)\$\$$/){
+ if ($e =~ /(.*?)\$\$$/){
$e = $1;
if($t && $t ne 'DATA'){
my $array = $lists{$e};
- if(!$array){$array=();$lists{$e} = \@{$array};}
+ if(!$array){$array=();$lists{$e} = \@{$array};}
push @{$array}, InstructedDataItem -> new($e, $t, $v);
next
- }
+ }
}elsif ($e eq '@'){#collection processing.
my $isArray = $t=~ m/^@/;
# if(!$v && $t =~ m/(.*)>(\s*.*\s*)/gms){
# $t = $1;
# $v = $2;
- # }
+ # }
my @lst = ($isArray?split(/[,\n]/, $v):split('\n', $v)); $_="";
my @props = map {
s/^\s+|\s+$//; # strip unwanted spaces
@@ -780,15 +813,15 @@ sub parse { my ($self, $cnf_file, $content, $del_keys) = @_;
$self->warn("ERROR collection is trying to use a reserved property name -> $t.");
next
}else{
- my @arr=();
- foreach (@props){
+ my @arr=();
+ foreach (@props){
push @arr, $_ if($_ && length($_)>0);
}
$properties{$t}=\@arr;
}
}else{
- my %hsh;
- my $macro = 0;
+ my %hsh;
+ my $macro = 0;
if(exists($properties{$t})){
if($self->isReservedWord($t)){
$self->warn("Skipped overwritting reserved property -> $t.");
@@ -797,24 +830,24 @@ sub parse { my ($self, $cnf_file, $content, $del_keys) = @_;
%hsh = %{$properties{$t}}
}
}else{
- %hsh =();
+ %hsh =();
}
- foreach my $p(@props){
+ foreach my $p(@props){
if($p && $p eq 'MACRO'){$macro=1}
- elsif( $p && length($p)>0 ){
+ elsif( $p && length($p)>0 ){
my @pair = ($p=~/\s*([-+_\w]*)\s*[=:]\s*(.*)/s);#split(/\s*=\s*/, $p);
- next if (@pair != 2 || $pair[0] =~ m/^[#\\\/]+/m);#skip, it is a comment or not '=' delimited line.
- my $name = $pair[0];
+ next if (@pair != 2 || $pair[0] =~ m/^[#\\\/]+/m);#skip, it is a comment or not '=' delimited line.
+ my $name = $pair[0];
my $value = $pair[1]; $value =~ s/^\s*["']|['"]$//g;#strip quotes
if($macro){
my @arr = ($value =~ m/(\$\$\$.+?\$\$\$)/gm);
- foreach my $find(@arr) {
+ foreach my $find(@arr) {
my $s = $find; $s =~ s/^\$\$\$|\$\$\$$//g;
- my $r = $anons->{$s};
+ my $r = $anons->{$s};
$r = $self->{$s} if !$r;
$r = $instructs{$s} if !$r;
CNFParserException->throw(error=>"Unable to find property for $t.$name -> $find\n",show_trace=>1) if !$r;
- $value =~ s/\Q$find\E/$r/g;
+ $value =~ s/\Q$find\E/$r/g;
}
}
$hsh{$name}=$value; $self->log("macro $t.$name->$value\n") if $self->{DEBUG}
@@ -837,17 +870,17 @@ sub parse { my ($self, $cnf_file, $content, $del_keys) = @_;
my $v = $struct;
my @arr = ($v =~ m/(\$\$\$.+?\$\$\$)/gm);
foreach my $find(@arr) {# <- MACRO TAG translate. ->
- my $s= $find; $s =~ s/^\$\$\$|\$\$\$$//g;#
+ my $s= $find; $s =~ s/^\$\$\$|\$\$\$$//g;#
my $r = %$anons{$s};
- $r = $self->{$s} if !$r;
+ $r = $self->{$s} if !$r;
if(!$r){
$self->warn("Unable to find property to translate macro expansion: $e -> $find\n");
}else{
- $v =~ s/\Q$find\E/$r/g;
+ $v =~ s/\Q$find\E/$r/g;
}
- }
+ }
$anons->{$e}=$v;
- }else{
+ }else{
$items[@items] = $struct;
}
}
@@ -856,70 +889,74 @@ sub parse { my ($self, $cnf_file, $content, $del_keys) = @_;
for my $idx(0..$#items) {
my $struct = $items[$idx];
- my $type = ref($struct);
+ my $type = ref($struct);
if($type eq 'CNFNode' && $struct-> priority() > 0){
$struct->validate() if $self->{ENABLE_WARNINGS};
- $anons ->{$struct->name()} = $struct->process($self, $struct->script());
+ $anons ->{$struct->name()} = $struct->process($self, $struct->script());
splice @items, $idx, 1
}
}
#Now only what is left instructed data items or plugins, and nodes that have assigned last priority, if any.
for my $idx(0..$#items) {
my $struct = $items[$idx];
- my $type = ref($struct);
- if($type eq 'CNFNode'){
- $struct->validate() if $self->{ENABLE_WARNINGS};
- $anons->{$struct->name()} = $struct->process($self, $struct->script());
- }elsif($type eq 'InstructedDataItem'){
+ my $type = ref($struct);
+ if($type eq 'CNFNode'){
+ $struct->validate() if $self->{ENABLE_WARNINGS};
+ $anons->{$struct->name()} = $struct->process($self, $struct->script());
+ }elsif($type eq 'InstructedDataItem'){
my $t = $struct->{ins};
- if($t eq 'PLUGIN'){
+ if($t eq 'PLUGIN'){
instructPlugin($self,$struct,$anons);
- }
+ }
}else{warn "What is -> $struct type:$type ?"}
}
- undef %instructs;
+ undef %instructs;
}
#Do scripted includes.
- my @inc = sort values %includes;
+ my @inc = sort values %includes;
$includes{$0} = {loaded=>1, path=>$self->{CNF_CONTENT}}; #<- to prevent circular includes.
foreach my $file(@inc){
if(!$file->{loaded} && $file->{path} ne $self->{CNF_CONTENT}){
if(open(my $fh, "<:perlio", $file->{path} )){
read $fh, $content, -s $fh;
- close $fh;
+ close $fh;
if($content){
$file->{loaded} = 1;
$self->parse(undef, $content)
}else{
$self->error("Include content is blank for -> ".$file->{path})
- }
+ }
}else{
CNFParserException->throw("Can't open ".$file->{path}." -> $!") if $self->{STRICT};
$file->{loaded} = 0;
$self->error("Script include not available -> ".$file->{path})
}
}
- }
- foreach my $k(@$del_keys){
+ }
+ foreach my $k(@$del_keys){
delete $self->{$k} if exists $self->{$k}
}
+ my $runProcessors = $self->{RUN_PROCESSORS} ? 1: 0;
lock_hash(%$self);#Make repository finally immutable.
+ runPostParseProcessors($self) if $runProcessors;
+ return $self
}
#
sub instructPlugin {
- my ($self, $struct, $anons) = @_;
- try{
+ my ($self, $struct, $anons) = @_;
+ try{
$properties{$struct->{'ele'}} = doPlugin($self, $struct, $anons);
$self->log("Plugin instructed ->". $struct->{'ele'});
- }catch($e){
+ }catch($e){
if($self->{STRICT}){
- CNFParserException->throw(error=>$e, show_trace=>1);
+ CNFParserException->throw(error=>$e);
}else{
- $self->trace("Error @ Plugin -> ". $struct->toString() ." Error-> $@")
+ $self->trace("Error @ Plugin -> ". $struct->toString() ." Error-> $@")
}
}
}
+#
###
# Register Instructor on tag and value for to be externally processed.
@@ -927,51 +964,123 @@ sub instructPlugin {
# $body - Contains attribute(s) linking to method(s) to be registered.
# @TODO Current Under development.
###
-sub registerInstructor {
- my ($self, $package, $body) = @_;
- $body =~ s/^\s*|\s*$//g;
- my ($obj, %args, $ins);
- foreach my $ln(split(/\n/,$body)){
- my @pair = $ln =~ /\s*(\w+)[:=](.*)\s*/;
- my $ins = $1; $ins = $ln if !$ins;
- my $mth = $2;
- if($ins =~ /[a-z]/){
- $args{$ins} = $mth;
- next
- }
- if(exists $instructors{$ins}){
- $self -> error("$package<$ins> <- Instruction has been previously registered by: ".ref(${$instructors{$ins}}));
+sub registerInstructor {
+ my ($self, $package, $body) = @_;
+ $body =~ s/^\s*|\s*$//g;
+ my ($obj, %args, $ins, $mth);
+ foreach my $ln(split(/\n/,$body)){
+ my @pair = $ln =~ /\s*(\w+)[:=](.*)\s*/;
+ $ins = $1; $ins = $ln if !$ins;
+ $mth = $2;
+ if($ins =~ /[a-z]/i){
+ $args{$ins} = $mth;
+ }
+ }
+ if(exists $instructors{$ins}){
+ $self -> error("$package<$ins> <- Instruction has been previously registered by: ".ref(${$instructors{$ins}}));
+ return;
+ }else{
+
+ foreach(values %instructors){
+ if(ref($$_) eq $package){
+ $obj = $_; last
+ }
+ }
+
+ if(!$obj){
+ ## no critic (RequireBarewordIncludes)
+ require $package.'.pm';
+ my $methods = Class::Inspector->methods($package, 'full', 'public');
+ my ($has_new,$has_instruct);
+ foreach(@$methods){
+ $has_new = 1 if $_ eq "$package\::new";
+ $has_instruct = 1 if $_ eq "$package\::instruct";
+ }
+ if(!$has_new){
+ $self -> log("ERR $package<$ins> -> new() method not found for package.");
return;
- }else{
- foreach(values %instructors){
- if(ref($$_) eq $package){
- $obj = $_; last
- }
- }
- if(!$obj){
- ## no critic (RequireBarewordIncludes)
- require $package.'.pm';
- my $methods = Class::Inspector->methods($package, 'full', 'public');
- my ($has_new,$has_instruct);
- foreach(@$methods){
- $has_new = 1 if $_ eq "$package\::new";
- $has_instruct = 1 if $_ eq "$package\::instruct";
- }
- if(!$has_new){
- $self -> log("ERR $package<$ins> -> new() method not found for package.");
- return;
- }
- if(!$has_instruct){
- $self -> log("ERR $package<$ins> -> instruct() required method not found for package.");
- return;
- }
- $obj = $package -> new(\%args);
- }
- $instructors{$ins} = \$obj;
- }
- }
- return \$obj;
+ }
+ if(!$has_instruct){
+ $self -> log("ERR $package<$ins> -> instruct() required method not found for package.");
+ return;
+ }
+ $obj = $package -> new(\%args);
+ }
+ $instructors{$ins} = \$obj
+ }
+ return \$obj;
}
+#
+
+###
+# Register PostParseProcessor for further externally processing.
+# $package - Is the anonymouse package name.
+# $body - Contains attribute(s) where function is the most required one.
+###
+sub registerProcessor {
+ my ($self, $package, $body) = @_;
+ $body =~ s/^\s*|\s*$//g if $body;
+ my ($obj, %args, $ins, $mth, $func);
+ foreach my $ln(split(/\n/,$body)){
+ my @pair = $ln =~ /\s*(\w+)[:=](.*)\s*/;
+ $ins = $1; $ins = $ln if !$ins;
+ $mth = $2;
+ if($ins =~ /^func\w*/){
+ $func = $mth
+ }
+ elsif($ins =~ /[a-z]/i){
+ $args{$ins} = $mth
+ }
+ }
+ $func = $ins if !$func;
+ if(!$func){
+ $self -> log("ERR <<$package<$body>> function attribute not found set.");
+ return;
+ }
+ ## no critic (RequireBarewordIncludes)
+ require $package.'.pm';
+ my $methods = Class::Inspector->methods($package, 'full', 'public');
+ my ($has_new,$has_func);
+ foreach(@$methods){
+ $has_new = 1 if $_ eq "$package\::new";
+ $has_func = 1 if $_ eq "$package\::$func";
+ }
+ if(!$has_new){
+ $self -> log("ERR In package $package -> new() method not found for package.");
+ return;
+ }
+ if(!$has_func){
+ $self -> log("ERR In package $package -> $func(\$parser) required method not found for package.");
+ return;
+ }
+ $obj = $package -> new(\%args);
+ $self->addPostParseProcessor($obj,$func);
+ return 1;
+}
+
+sub addPostParseProcessor {
+ my $self = shift;
+ my $processor = shift;
+ my $func = shift;
+ my @arr;
+ my $arf = $self->{POSTParseProcessors} if exists $self->{POSTParseProcessors};
+ @arr = @$arf if $arf;
+ $arr[@arr] = [$processor, $func];
+ $self->{POSTParseProcessors} = \@arr;
+}
+
+sub runPostParseProcessors {
+ my $self = shift;
+ my $arr = $self->{POSTParseProcessors} if exists $self->{POSTParseProcessors};
+ foreach(@$arr){
+ my @objdts =@$_;
+ my $prc = $objdts[0];
+ my $func = $objdts[1];
+ $prc -> $func($self);
+ }
+}
+
+#
###
# Setup and pass to pluging CNF functionality.
@@ -984,20 +1093,22 @@ sub doPlugin {
my $pck = $plugin->{package};
my $prp = $plugin->{property};
my $sub = $plugin->{subroutine};
- if($pck && $prp && $sub){
+ if($pck && $prp && $sub){
## no critic (RequireBarewordIncludes)
require "$pck.pm";
- my $obj;
- my $settings = $properties{'%Settings'};#Properties are global.
+ #Properties are global, all plugins share a %Settings property if specifed, otherwise the default will be set from here only.
+ my $settings = $properties{'%Settings'};
if($settings){
- $obj = $pck->new(\%$settings);
- }else{
- $obj = $pck->new();
- }
+ foreach(keys %$settings){
+ #We allow for now, the plugin have settings set by its property, do not overwrite if exists as set.
+ $plugin->{$_} = $settings->{$_} unless exists $plugin->{$_}
+ } ;
+ }
+ my $obj = $pck->new($plugin);
my $res = $obj-> $sub($self, $prp);
- if($res){
- $plugin->setPlugin($obj);
- return $plugin;
+ if($res){
+ $plugin->setPlugin($obj);
+ return $plugin;
}else{
die "Sorry, the PLUGIN feature has not been Implemented Yet!"
}
@@ -1016,13 +1127,13 @@ sub obtainLink {
my $meths;
## no critic BuiltinFunctions::ProhibitStringyEval
no strict 'refs';
- if($link =~/(\w*)::\w+$/){
+ if($link =~/(\w*)::\w+$/){
use Module::Loaded qw(is_loaded);
if(is_loaded($1)){
- $ret = \&{+$link}($self);
+ $ret = \&{+$link}($self);
}else{
eval require "$1.pm";
- $ret = &{+$link};
+ $ret = &{+$link};
if(!$ret){
$self->error( qq(Package constance link -> $link is not available (try to place in main:: package with -> 'use $1;')));
$ret = $link
@@ -1031,17 +1142,17 @@ sub obtainLink {
}else{
$ret = $self->anon($link);
$ret = $self-> {$link} if !$ret;
- }
+ }
return $ret;
}
###
# Writes out to a handle an CNF property or this parsers constance's as default property.
# i.e. new CNFParser()->writeOut(*STDOUT);
-sub writeOut { my ($self, $handle, $property) = @_;
+sub writeOut { my ($self, $handle, $property) = @_;
my $buffer;
if(!$property){
- my @keys = sort keys %$self;
+ my @keys = sort keys %$self;
$buffer = "<<\n";
if(ref $prp eq 'ARRAY') {
my @arr = sort keys @$prp; my $n=0;
- foreach (@arr){
+ foreach (@arr){
$buffer .= "\"$_\"";
if($arr[-1] ne $_){
if($n++>5){
@@ -1087,12 +1198,12 @@ sub writeOut { my ($self, $handle, $property) = @_;
$buffer .= ","
}
}
- }
+ }
}elsif(ref $prp eq 'HASH') {
my %hsh = %$prp;
my @keys = sort keys %hsh;
- foreach my $key(@keys){
- $buffer .= $key . "\t= \"". $hsh{$key} ."\"\n";
+ foreach my $key(@keys){
+ $buffer .= $key . "\t= \"". $hsh{$key} ."\"\n";
}
}
$buffer .= ">>\n";
@@ -1104,12 +1215,12 @@ sub writeOut { my ($self, $handle, $property) = @_;
$prp = $ANONS{$property};
$prp = $self->{$property} if !$prp;
if (!$prp){
- $buffer = "<Property not found!>>>\n"
+ $buffer = "<Property not found!>>>\n"
}else{
$buffer = "<<$property><$prp>>\n";
}
return $buffer if !$handle;
- print $handle $buffer;
+ print $handle $buffer;
return 0;
}
}
@@ -1131,11 +1242,15 @@ sub log {
my $self = shift;
my $message = shift;
my $type = shift; $type = "" if !$type;
+ my $isWarning = $type eq 'WARNG';
my $attach = join @_; $message .= $attach if $attach;
- my %log = $self -> collection('%LOG');
- my $time = DateTime->from_epoch( epoch => time )->strftime('%Y-%m-%d %H:%M:%S.%3N');
- $message = "$type $message" if 'WARNG';
- if($message =~ /^ERROR/ || $type eq 'WARNG'){
+ my %log = $self -> property('%LOG');
+ my $time = exists $self->{'TZ'} ? CNFDateTime -> new(TZ=>$self->{'TZ'}) -> toTimestamp() :
+ CNFDateTime -> new()-> toTimestamp();
+
+ $message = "$type $message" if $isWarning;
+
+ if($message =~ /^ERROR/ || $isWarning){
warn $time . " " .$message;
}
elsif(%log && $log{console}){
@@ -1145,37 +1260,38 @@ sub log {
my $logfile = $log{file};
my $tail_cnt = $log{tail};
if($log{tail} && $tail_cnt && int(`tail -n $tail_cnt $logfile | wc -l`)>$tail_cnt-1){
- use File::ReadBackwards;
+use File::ReadBackwards;
my $pos = do {
my $fh = File::ReadBackwards->new($logfile) or die $!;
$fh->readline() for 1..$tail_cnt;
$fh->tell()
- };
+ };
truncate($logfile, $pos) or die $!;
-
+
}
open (my $fh, ">>", $logfile) or die ("$!");
print $fh $time . " - " . $message ."\n";
close $fh;
}
+ return $time . " " .$message;
}
sub error {
my $self = shift;
- my $message = shift;
+ my $message = shift;
$self->log("ERROR $message");
}
use Carp qw(cluck); #what the? I know...
sub warn {
my $self = shift;
- my $message = shift;
+ my $message = shift;
if($self->{ENABLE_WARNINGS}){
- $self -> log($message,'WARNG');
+ $self -> log($message,'WARNG');
}
}
sub trace {
my $self = shift;
- my $message = shift;
- my %log = $self -> collection('%LOG');
+ my $message = shift;
+ my %log = $self -> property('%LOG');
if(%log){
$self -> log($message)
}else{
@@ -1183,6 +1299,8 @@ sub trace {
}
}
+sub now {return CNFDateTime->new(shift)}
+
sub dumpENV{
foreach (keys(%ENV)){print $_,"=", "\'".$ENV{$_}."\'", "\n"}
}
@@ -1197,23 +1315,57 @@ sub SQL {
}
our $JSON;
sub JSON {
- my $self = shift;
+ my $self = shift;
if(!$JSON){
- require CNFJSON;
- $JSON = CNFJSON-> new( {CNF_VERSION=>$self->{CNF_VERSION},
+ require CNFJSON;
+ $JSON = CNFJSON-> new({ CNF_VERSION=>$self->{CNF_VERSION},
CNF_CONTENT=>$self->{CNF_CONTENT},
- DO_ENABLED=>$self->{DO_ENABLED}
- } );
- }
+ DO_ENABLED =>$self->{DO_ENABLED}
+ });
+ }
return $JSON;
}
+###
+# CNFNodes are kept as anons by the TREE instruction, but these either could have been futher processed or
+# externaly assigned too as nodes to the parser.
+###
+our %NODES;
+sub addTree {
+ my ($self, $name, $node )= @_;
+ if($name && $node){
+ $NODES{$name} = $node;
+ }
+}
+### Utility way to obtain CNFNodes from a configuration.
+sub getTree {
+ my ($self, $name) = @_;
+ return $NODES{$name} if exists $NODES{$name};
+ my $ret = $self->anon($name);
+ if(ref($ret) eq 'CNFNode'){
+ return \$ret;
+ }
+ return;
+}
sub END {
undef %ANONS;
undef @files;
+undef %properties;
+undef %lists;
+undef %instructors;
}
1;
+=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
__END__
## Instructions & Reserved words
@@ -1223,9 +1375,10 @@ __END__
3. Current Reserved words list is.
- CONST - Concentrated list of constances, or individaly tagged name and its value.
- VARIABLE - Concentrated list of anons, or individaly tagged name and its value.
- - DATA - CNF scripted delimited data property, having uniform table data rows.
+ - DATA - CNF scripted delimited data property, having uniform table data rows.
+ - DATE - Translate PerlCNF date representation to DateTime object. Returns now() on empty property value.
- FILE - CNF scripted delimited data property is in a separate file.
- - %LOG - Log settings property, i.e. enabled=1, console=1.
+ - %LOG - Log settings property, i.e. enabled=>1, console=>1.
- TABLE - SQL related.
- TREE - Property is a CNFNode tree containing multiple debth nested children nodes.
- INCLUDE - Include properties from another file to this repository.
@@ -1233,6 +1386,7 @@ __END__
- INSTRUCT - Provides custom new anonymous instruction.
- VIEW - SQL related.
- PLUGIN - Provides property type extension for the PerlCNF repository.
+ - PROCESSOR- Registered processor to be called once all parsing is done and repository secured.
- SQL - SQL related.
- MIGRATE - SQL related.
- MACRO
diff --git a/htdocs/cgi-bin/system/modules/HTMLIndexProcessorPlugin.pm b/htdocs/cgi-bin/system/modules/HTMLIndexProcessorPlugin.pm
index 46fa171..7fdceab 100644
--- a/htdocs/cgi-bin/system/modules/HTMLIndexProcessorPlugin.pm
+++ b/htdocs/cgi-bin/system/modules/HTMLIndexProcessorPlugin.pm
@@ -7,8 +7,7 @@ use Syntax::Keyword::Try;
use Exception::Class ('HTMLIndexProcessorPluginException');
use feature qw(signatures);
use Scalar::Util qw(looks_like_number);
-use Date::Manip;
-
+use Clone qw(clone);
use CGI;
use CGI::Session '-ip_match';
@@ -16,49 +15,41 @@ use constant VERSION => '1.0';
our $TAB = ' 'x4;
-sub new ($class, $fields={Language=>'English',DateFormat=>'US'}){
-
- if(ref($fields) eq 'REF'){
- warn "Hash reference required as argument for fields!"
+sub new ($class, $plugin){
+ my $settings;
+ if($plugin){
+ $settings = clone $plugin; #clone otherwise will get hijacked with blessings.
}
- my $lang = $fields->{'Language'};
- my $frmt = $fields->{'DateFormat'};
- Date_Init("Language=$lang","DateFormat=$frmt");
-
- return bless $fields, $class
+ return bless $settings, $class
}
###
# Process config data to contain expected fields and data.
###
-sub convert ($self, $parser, $property) {
+sub convert ($self, $parser, $property) {
my ($buffer,$title, $link, $body_attrs, $body_on_load, $give_me);
my $cgi = CGI -> new();
- my $cgi_action = $cgi-> param('action');
- my $cgi_doc = $cgi-> param('doc');
+ my $cgi_action = $cgi-> param('action');
+ my $cgi_doc = $cgi-> param('doc');
my $tree = $parser-> anon($property);
- die "Tree property '$property' is not available!" if(!$tree or ref($tree) ne 'CNFNode');
-
+ die "Tree property '$property' is not available!" if(!$tree or ref($tree) ne 'CNFNode');
try{
-
-
- if (exists $parser->{'HTTP_HEADER'}){
+ if (exists $parser->{'HTTP_HEADER'}){
$buffer .= $parser-> {'HTTP_HEADER'};
- }else{
+ }else{
if(exists $parser -> collections()->{'%HTTP_HEADER'}){
my %http_hdr = $parser -> collection('%HTTP_HEADER');
$buffer = $cgi->header(%http_hdr);
}
}
-
- if ($cgi_action and $cgi_action eq 'load'){
- $buffer .= $cgi->start_html(); my
+ if ($cgi_action and $cgi_action eq 'load'){
+ $buffer .= $cgi->start_html(); my
$load = loadDocument($parser, $cgi_doc);
if($load){
- $buffer .= $$load if $load;
+ $buffer .= $$load if $load;
}else{
$buffer .= "Document is empty: $cgi_doc\n"
- }
+ }
}else{
$title = $tree -> {'Title'} if exists $tree->{'Title'};
$link = $tree -> {'HEADER'};
@@ -69,13 +60,13 @@ try{
if($link){
if(ref($link) eq 'CNFNode'){
my $arr = $link->find('CSS/@@');
- foreach (@$arr){
- push @hhshCSS, {-type => 'text/css', -src => $_->val()};
+ foreach (@$arr){
+ push @hhshCSS, {-type => 'text/css', -src => $_->val()};
}
$arr = $link->find('JS/@@');
- foreach (@$arr){
- push @hhshJS, {-type => 'text/javascript', -src => $_->val()};
- }
+ foreach (@$arr){
+ push @hhshJS, {-type => 'text/javascript', -src => $_->val()};
+ }
$arr = $link -> find('STYLE');
if(ref($arr) eq 'ARRAY'){
foreach (@$arr){
@@ -85,51 +76,51 @@ try{
}
$arr = $link -> find('SCRIPT');
if(ref($arr) eq 'ARRAY'){
- foreach (@$arr){
+ foreach (@$arr){
$give_me .= "\n\n"
}}else{
$give_me .= "\n\n"
}
- }
- delete $tree -> {'HEADER'};
- }
+ }
+ delete $tree -> {'HEADER'};
+ }
$buffer .= $cgi->start_html(
-title => $title,
-onload => $body_on_load,
- # -BGCOLOR => $colBG,
+ # -BGCOLOR => $colBG,
-style => \@hhshCSS,
-script => \@hhshJS,
-head=>$give_me,
$body_attrs
);
- foreach my $node($tree->nodes()){
+ foreach my $node($tree->nodes()){
$buffer .= build($parser, $node) if $node;
}
$buffer .= $cgi->end_html();
}
$parser->data()->{$property} = \$buffer;
}catch($e){
- HTMLIndexProcessorPluginException->throw(error=>$e ,show_trace=>1);
+ HTMLIndexProcessorPluginException->throw(error=>$e);
}
}
#
sub loadDocument($parser, $doc) {
my $slurp = do {
- open my $fh, '<:encoding(UTF-8)', $doc or HTMLIndexProcessorPluginException->throw("Document not avaliable: $doc");
+ open my $fh, '<:encoding(UTF-8)', $doc or HTMLIndexProcessorPluginException->throw(error=>"Document not avaliable -> \"$doc\" ", show_trace=>1);
local $/;
- <$fh>;
+ <$fh>;
};
if($doc =~/\.md$/){
- require MarkdownPlugin;
- my @r = @{MarkdownPlugin->new()->parse($slurp)};
+ require MarkdownPlugin;
+ my @r = @{MarkdownPlugin->new(undef)->parse($slurp)};
return $r[0];
}
- return \$slurp
+ return \$slurp
}
###
# Builds the html version out of a CNFNode.
-# CNFNode with specific tags here are converted also here,
+# CNFNode with specific tags here are converted also here,
# those that are out of the scope for normal standard HTML tags.
# i.e. HTML doesn't have row and cell tags. Neither has meta links syntax.
###
@@ -143,14 +134,14 @@ sub build {
$bf .= "\t"x$tabs."
\n"."\t"x$tabs."
";
foreach my $n($node->nodes()){
if($n->{'_'} ne '#'){
- my $b = build($parser, $n, $tabs+1);
+ my $b = build($parser, $n, $tabs+1);
$bf .= "$b\n" if $b;
}
}
if($node->{'#'}){
my $v = $node->val();
$v =~ s/\n\n+/\<\/br>\n/gs;
- $bf .= "\t
\n\t
\n".$v."
\n\t
\n";
+ $bf .= "\t
\n\t
\n".$v."
\n\t
\n";
}
$bf .= "\t
\t
\n"
}elsif( $name eq 'row' || $name eq 'cell' ){
@@ -161,13 +152,13 @@ sub build {
$bf .= "$b\n" if $b;
}
}
- $bf .= $node->val()."\n" if $node->{'#'};
+ $bf .= $node->val()."\n" if $node->{'#'};
$bf .= "\t"x$tabs.""
}elsif( $name eq 'img' ){
$bf .= "\t\t\n";
}elsif($name eq 'list_images'){
my $paths = $node->{'@@'};
- foreach my $ndp (@$paths){
+ foreach my $ndp (@$paths){
my $path = $ndp -> val();
my @ext = split(',',"jpg,jpeg,png,gif");
my $exp = " ".$path."/*.". join (" ".$path."/*.", @ext);
@@ -180,22 +171,22 @@ sub build {
$bf .= qq(\t
require a recent enough ExtUtils::CppGuess and set the required C++ standard
+
allow the zxing-cpp package name for pkg-config, which seems to be what packagers used before upstream decided on "zxing.pc". https://github.com/tonycoz/imager-zxing/issues/1
+
+ ]#]
+ >Description>
+ >Item>
+ System Calls for File IO, User, Process, Signal, Socket
Changes for 0.491 - 2023-09-13
New Features
+
Incompatible Changes
+
+ ]#]
+ >Description>
+ >Item>
+ Log::Dispatch::Email subclass that sends mail using Email::Mailer
Changes for 1.13 - 2023-09-12T21:12:32-07:00
Require exact v1.23 (resolves issue #5)
+
+ ]#]
+ >Description>
+ >Item>
+ Original program path locations extension for exact
Changes for 1.05 - 2023-09-12T21:10:17-07:00
Remove redundant strict (since it's provided by exact)
+
New import signature change required by exact v1.23
Improve/fix import of packages into other packages (resolves issue #4)
+
+ ]#]
+ >Description>
+ >Item>
+ "Mail Merge" or just substitute tokens in ODF documents
Changes for 1.000
ODF::MailMerge::Engine->new positional args eliminated; now use proto_elt => $table # specify the object directly context => $context, proto_tag => "tagstring" # search for it Modifier :die ("Delete If Empty") replaces :delempty
+
+ ]#]
+ >Description>
+ >Item>
+ Description>
+ >Item>
+ Description>
+ >Item>
+ Fetch actual raw streamable URLs from various radio-station, video & podcast websites.
Changes for 2.19 - 2023-09-12
StreamFinder::Youtube - 1) Fix failure to fetch artist, icon, etc. sometimes on embedded IFRAME urls (slight site changes) and first episode from some channels. 2) Add -youtube-site argument to specify a different default youtube site (default https://www.youtube.com). 3) Add ability to parse youtube channel URLs containing an at-sign, ie.: https://www.youtube.com/@channelID.
+
StreamFinder::Subsplash - Restore as EXPERIMENTAL, as this site seems to now work again, at least for audio streams on some sites.
Without DateTime, ParseTime::ISO8601 now accepts any zone. These are handled by setting $ENV{TZ} before the conversion and hoping for the best. The documentation warns that this is a shaky way to handle zones.
+
Make Warner->wail() stack dump if $Carp::Verbose true.
+
Add --almanac to pass(). This adds almanac data to appropriate passes. --ephemeris is more verbose, adding almanac data to all passes. This change involved refactoring event formatting to use sub-templates, rather than if-elsif-else chains.
+
Add 'none' as valid argument to FormatTime->round_time(). It is equivalent to specifying undef, i.e. no rounding.
+
+ ]#]
+ >Description>
+ >Item>
+ a generic connection to a hierarchical-structured data set
Changes for 1.51 - 2023-09-11T11:13:04Z
+ ]#]
+ >Description>
+ >Item>
+ Description>
+ >Item>
+ System Calls for File IO, User, Process, Signal, Socket
Changes for 0.490 - 2023-09-11
Bug Fix
+
+ ]#]
+ >Description>
+ >Item>
+ a generic connection to a hierarchical-structured data set
Changes for 1.50 - 2023-09-11T07:06:09Z
+ ]#]
+ >Description>
+ >Item>
+ A Perl Mail Authentication Milter
Changes for 3.20230911 - 2023-09-11T06:18:44+00:00
Core: Switch from deprecated method in Net::DNS In Net::DNS::Resolver, call the rdstring method rather than the deprecated rdstring method This change bumps the minimum version of Net::DNS required to 1.01
+
SPF: Add option to detect and optionally mitigate SPF upgrade problems.
+
Core: Add authentication_milter_log command with arex subcommand which can be used to process ARex JSON log format back into standard Authentication-Results: header lines
+
+ ]#]
+ >Description>
+ >Item>
+ System Calls for File IO, User, Process, Signal, Socket
Changes for 0.489 - 2023-09-11
New Features
+
Bug Fix
+
+ ]#]
+ >Description>
+ >Item>
+ System Calls for File IO, User, Process, Signal, Socket
Changes for 0.488 - 2023-09-11
Prerequirement Changes
+
Incompatible Changes
+
+ ]#]
+ >Description>
+ >Item>
+ "Mail Merge" or just substitute tokens in ODF documents
Changes for 0.003
(i.e. only if the row is being replicated). Preened docs.