Solved

call a modules INIT

Posted on 2004-08-25
10
241 Views
Last Modified: 2010-03-05
This is a spinoff of thread:

The Question:

how can i call a modules INIT-section explicitly from another module or main or eval()?

example:

eval
qq{
      use Win32::API;
      Win32::API->Import( "winmm.dll", "long timeGetTime()" );
};

throws a warning because Win32::API::INIT is not executed within an eval-statement.

so how can i make INIT run without having to change the Win32::API´s code?



0
Comment
Question by:holli
  • 5
  • 3
  • 2
10 Comments
 
LVL 6

Author Comment

by:holli
Comment Utility
0
 
LVL 3

Accepted Solution

by:
terageek earned 500 total points
Comment Utility
I don't know if this will work, and it isn't too direct or pretty, but here is a shot...

#Search @INC for Win32::API
foreach my $path (@INC) {
    if (-e $path/Win32/API.pm) {
      # Open the .pm file and grab the INIT block
        my $init_block = "";
        open PM "<$path/Win32/API.pm");
        while ($line = <PM>) {
           #Start grabbing the INIT block
            if ($line =~ /INIT/) {
                $init_block = $line;
            } elsif ($init_block ne "") {
                $init_block .= $line;
            }
            if ($init_block =~ /{/) {
               #Count the number of "{" and "}"
                my $open = scalar @{[$init_block =~ /\{/g]};
                my $closed = scalar @{[$init_block =~ /\}/g]};
               #Eval the block if the block is closed
                if ($open == $closed) {
                  # Get rid of "INIT" and make sure this
                  # is evaluated in the right package context
                    $init_block =~ s/^.*?INIT/package Win32::API;/;
                    eval $init_block;
                  # Might need to set package back here???
                    $init_block = "";
                }
            }
        }
        last;
    }
}

The above code makes some assumptions about proper formatting of the pm code.  You may also need to put yourself back into your own package.
0
 
LVL 3

Expert Comment

by:terageek
Comment Utility
Change

if ($line =~ /INIT/) {

to

# Parse out comments
$line =~ s/(^|[^\$])#.*/$1/;
if ($line =~ /\bINIT\b/) {


That should skip cases where "INIT" is in a comment, and it will avoid issues with other words containing "INIT", like "$INITIALVALUE".
0
 
LVL 6

Author Comment

by:holli
Comment Utility
i did a little investigation.

package t;

sub INIT
{
     print "INIT";
}

sub import
{
   my ($pkg, @args) = @_;
   print "package $pkg loaded, args ", join(':',@args), "\n";
   @foo::args = @args;
 
   my $init = $t::{INIT};
   my $foo  = $t::{foo};
   $init = \&$init;
   $foo = \&$foo;
   print "* $init *";
   print "* $foo *";
   &foo();
   &$init();
   print "end";
}

sub foo
{
     print "FOO";    
}

1;

______________


#t.pl
use lib qw (c:/ );
use t qw (x y);
print "main";


outcome:
package t loaded, args x:y
Undefined subroutine &t::INIT called at c://t.pm line 21.
BEGIN failed--compilation aborted at c:\t.pl line 2.
* CODE(0x186a230) ** CODE(0x186a170) *FOO


Why?
"CODE(0x186a230)" looks like a code-reference. why can´t i call it?
0
 
LVL 1

Expert Comment

by:boazgOLD
Comment Utility
the main problem with this specific itin, it seems, is that id reads from the <DATA> stream. before you eval the code,(1) read until __DATA__, and (2) replace <DATA> with <PM>, or whatever you want to call the file handle. another few bugs needed fixing... use q(), not qq{} (both curley brackets and qq caused problems). there was a spot in the INIT that used # not in comment context... had to fix that

also, the init in this case is in API/type.pm not in API.pm

result (based on code above)
      foreach $path (@INC) {
    if (-e "$path/Win32/API/type.pm") {
      # Open the .pm file and grab the INIT block
        my $init_block = "";
        open PM, "<$path/Win32/API/type.pm";
        while ($line = <PM>) {
           #Start grabbing the INIT block
               if ($line !~ /\/[^\/]*#[^\/]*\//) {
               $line =~ s/(^|[^\$])#.*/$1/;}
           if ($line =~ /\bINIT\b/) {
                $init_block = $line;
            } elsif ($init_block ne "") {
                $init_block .= $line;
            }
            if ($init_block =~ /{/) {
               #Count the number of "{" and "}"
                my $open = scalar @{[$init_block =~ /\{/g]};
                my $closed = scalar @{[$init_block =~ /\}/g]};
               #Eval the block if the block is closed
                if ($open == $closed) {
                  # Get rid of "INIT" and make sure this
                  # is evaluated in the right package context
                    $init_block =~ s/^.*?INIT/package Win32::API::Type;/;
                     #set correct file handle
               *Win32::API::Type::DATA=*main::PM;
                      #read until _DATA_
                                $readline=<PM>;
                      while($readline !~ /__DATA__\n/){$readline=<PM>;};
                       ######back to old code#####
                    eval $init_block;
                              print $@."\n";
                  # Might need to set package back here???
                    $init_block = "";
                              last;
                }
            }
        }
    }
}


hope that helps
0
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 
LVL 6

Author Comment

by:holli
Comment Utility
that is ugly.

what do think about this? change:

INIT { #old code

to

INIT { &SINIT; }

sub SINIT { #old code

then you can call Module::SINIT();

quite elegant, isn´t it. or are there hidden penalties.
i am currently working on a module that automates that step by using a code-filter (Filter::Simple).
0
 
LVL 6

Author Comment

by:holli
Comment Utility
i have this now:

Module Eval::INIT:

package Eval::INIT;

my $filter;

use Filter::Simple sub
{
      return if $filter++;
      
      my $i=0;
      while ( $_ =~ s/\bINIT(?!\x00)([\s\n]+)/\1INIT\x00 { \&SINIT$i; } \nsub SINIT$i\1/ms ) {$i++;};       
      s/\x00//msg;
};

1;

Module t.pm

package t;

use Eval::INIT;

INIT
{
      print "INIT\n";
}

1;

and t.pl

eval "use t; &t::SINIT0();";
print "main";

output:
INIT
main

what do you think?

0
 
LVL 1

Expert Comment

by:boazgOLD
Comment Utility
beutiful! and it works for your problem... you do have to change the module code, but only by adding one line...

well, it would seem you solved you own problem!
0
 
LVL 6

Author Comment

by:holli
Comment Utility
it would be nice if coud "inject" the use Evaled::Filter into the module, like

eval "use Evaled::InjectFilter qw (Win32::API::Type)";

from main::.

do you think this could make it to cpan?
what to do with the points?
0
 
LVL 1

Expert Comment

by:boazgOLD
Comment Utility
the inject may be a problem... the INIT code has to run in Type.pm (cause of the <DATA>) so i'm not shure...
to put it on CPAN you will have to properly package it and stuff. you can try, if you like...
as for the points... terageek wrote a very tough piece of code, he deserves points.
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
perl match and sort unique result 2 122
perl script help 12 101
Strange perl issue 6 122
Perl Scripting from a shell script with if and for 4 82
In the distant past (last year) I hacked together a little toy that would allow a couple of Manager types to query, preview, and extract data from a number of MongoDB instances, to their tool of choice: Excel (http://dilbert.com/strips/comic/2007-08…
Checking the Alert Log in AWS RDS Oracle can be a pain through their user interface.  I made a script to download the Alert Log, look for errors, and email me the trace files.  In this article I'll describe what I did and share my script.
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now