Module-Generic

 view release on metacpan or  search on metacpan

lib/Module/Generic.pm  view on Meta::CPAN

                    # We add the 8 bits compliant version only if any colour was provided, i.e.
                    # This is not just a style definition
                    CORE::push( @$params8, $styles->{lc($s)} ) if( scalar( @$params8 ) );
                }
            }
        }
        CORE::push( @$data, "\e[" . CORE::join( ';', @$params8 ) . "m" ) if( scalar( @$params8 ) );
        CORE::push( @$data, "\e[" . CORE::join( ';', @$params ) . "m" ) if( scalar( @$params ) );
        return( $data );
    };

    # NOTE: process_params()
    my $process_params = sub
    {
        my( $params ) = @_;
        return if( !defined( $params ) || !length( $params // '' ) );
        my $def = {};
        # Example: bold green underline on black
        if( $params =~ m{
            ^[[:blank:]]*
            (?:
                (?<style1>$style_re)
                [[:blank:]]+
            )?
            (?<fg_colour>$colour_re)
            (?:
                [[:blank:]]+
                (?<style2>$style_re)
            )?
            (?:
                [[:blank:]]+on[[:blank:]]+
                (?<bg_colour>$colour_re)
            )?
            [[:blank:]]*$
            }xi )
        {
            my $style = $+{style1} || $+{style2};
            my $fg = $+{fg_colour};
            my $bg = $+{bg_colour};
            $def = 
            {
                style     => $style,
                colour    => $fg,
                bg_colour => $bg,
            };
            $self->__message( 120, "\$process_params->(): Params regular expression matched -> ", sub{ $self->Module::Generic::dump( $def ) } );
        }
        else
        {
            $self->__message( 120, "\$process_params->(): Params regular expression failed to match. Trying to eval '$params' instead." );
            # Only allow characters that make sense for hash-like parameters.
            # This forbids variables, code blocks, Perl ops, backticks, etc.
            if( $params =~ /[^a-zA-Z0-9_,\=>\s\h[:blank:]'"\|\(\)\.\-]/ )
            {
                $self->__message( 120, "\$process_params->(): Illegal characters found inside eval." );
                return;
            }
            # illegal functions that have no business being here, and could pass through the previous check
            elsif( $params =~ /\b(?:
                    qx|system|open|exec|fork|require|use|eval|do|
                    package|sub|BEGIN|UNITCHECK|CHECK|INIT|END|
                    readpipe|sysopen|unlink|rename|chmod|chown|utime|truncate|mkdir|rmdir|opendir|readdir|closedir|glob
                )\b/i )
            {
                $self->__message( 120, "\$process_params->(): Illegal functions used inside eval." );
                return;
            }

            local $SIG{__WARN__} = sub{};
            local $SIG{__DIE__} = sub{};
            local $@;
            my @res = eval( $params );
            $self->__message( 120, "\$process_params->(): evaluating '$params' produced -> ", sub{ $self->Module::Generic::dump( \@res ) } );
            $def = { @res } if( scalar( @res ) && !( scalar( @res ) % 2 ) );
            if( $@ || ref( $def ) ne 'HASH' )
            {
                my $err = $@ || "Invalid styling \"${params}\"";
                $self->__message( 120, "\$process_params->(): Error evaluating '$params' -> $@" );
                $def = {};
            }
        }

        if( scalar( keys( %$def ) ) )
        {
            $self->__message( 120, "\$process_params->(): colour definition is: ", sub{ $self->Module::Generic::dump( $def ) } );
            my $ref = $colour_format->( $def );
            $self->__message( 120, "\$process_params->(): Returning -> ", sub{ $self->Module::Generic::dump( $ref ) } );
            return if( !$ref || !scalar( @$ref ) );
            return( $ref );
        }
        $self->__message( 120, "\$process_params->(): Returning nothing." );
        return;
    };

    # NOTE: parse()
    my $parse;
    $parse = sub
    {
        # $chunk is the text from position $pos until the end of the string, as provided by parent
        my $chunk   = shift( @_ );
        my $args    = shift( @_ ) || {};
        my $copy    = $chunk;
        my $out     = '';
        my $level   = $args->{level} // 1;
        my $counter = $args->{counter} // 0;
        return( $chunk ) if( $level > $max_depth );
        my( $open_d, $close_d );
        # If we are given specific open and close delimiter use them, otherwise, use our list of candidates.
        # This only happens the first time we hist the text to parse and search for those delimiters.
        # Afterward, as a rule, we always stick to the same delimiters used. The user cannot mix delimiters in the same string.
        # We create Regexp object on purpose, so we can differentiate from plain string later in our code.
        # The initial '$open' and '$close' are simple strings, not Regexp object, so we can differentiate them:
        # Regexp -> confirmed open and close delimiters
        # Non-Regexp -> prospective open and close delimiters
        # Once the open and close delimiters are confirmed they never change.
#         $open_d  = $args->{open}  ? ( ref( $args->{open} )  ? $args->{open}  : qr{(?<open_d>\Q$args->{open}\E)} ) : $open;
#         $close_d = $args->{close} ? ( ref( $args->{close} ) ? $args->{close} : qr{?<close_d>\Q$args->{close})} )  : $close;
        $open_d  = $args->{open}  ? $args->{open}  : $open;
        $close_d = $args->{close} ? $args->{close} : $close;
        $self->__message( 120, "\$parse->() [LEVEL ${level}]: Using \$open_d '$open_d', and \$close_d '$close_d' with closing counter '$counter'" );
        $self->__message( 120, "\$parse->() [LEVEL ${level}]: Processing string '", ( $chunk // 'undef' ), "'" );



( run in 0.915 second using v1.01-cache-2.11-cpan-39bf76dae61 )