I'm working on a Parse::RecDescent grammar to read a given human-readable set of rules and then spit out a file that is much easier for a computer to read.
One of the tokens is a list of "keywords"; about 26 different keywords. These may change over time, and may be referenced by multiple pieces of code. Consequently, I want to store the keyword-y things in a data file and load them in.
A feature of Parse::RecDescent is the ability to interpolate variables in regexes, and I would like to use it.
I wrote up some code as a proof of concept:
#arr = ("foo", "bar", "frank", "jim");
$data = <<SOMEDATA;
This is some data with the word foo in it
SOMEDATA
$arrstr = join("|", #arr);
if($data =~ /($arrstr)/)
{
print "Matched $1\n";
}
else
{
print "Failed to match\n";
}
This worked correctly.
When I moved to my main program to implement it, I wrote:
{
my $myerror = open(FILE, "data.txt") or die("Failed to open data");
my #data_arr = <FILE>;
close FILE;
my $dataarrstr = join("|", #data_arr);
}
#many rules having nothing to do with the data array are here...
event : /($dataarrstr)/
{ $return = $item[1]; }
|
And at this point, I received this error from P::RD: ERROR (line 18): Invalid event: Was expecting /($dataarrstr)/.
I don't know why. Does anyone have any ideas that would serve to help me out here?
edit:
This is not a scoping issue- I've tried that. I've also tried the m{...} syntax.
After perusing documentation and a very similar question over at http://perlmonks.org/?node_id=384098, I worked out this solution.
event :/\w+/
{
$return = ::is_valid_event($item[1]);
}
| <error>
Outside the grammar -
#This manages the problem of not being able to interpolate the variable
#in the grammar action
sub is_valid_event {
my $word = shift #_;
if($word =~ /$::data_str/)
{
return $word;
}
else
{
return undef;
}
}
Related
I recently migrated some Perl code from SunSolaris to a Linux(Ubuntu) box of 64 bit. After the migration Storable.pm is breaking with the following error:
Byte order is not compatible at /usr/lib/perl/5.18/Storable.pm, at /home/VD/Cache.pm line 347.
After some research on the internet I found that I need to use nfreeze instead of thaw, but now I receive the following error:
not a reference at /home/VD/Cache.pm line 347.
Any suggestions how to fix this?
sub get
{
my($self, $type, $param_ref) = #_;
#return 1 if(!$self->{'INI'}{'sf.system.cache.enabled'});
if($self->{'INI'}{'sf.system.cache.database.enabled'})
{
### DATABASE
my $param = $self->SF::Cache::convert_parameter($type, $param_ref);
if($self->SF::Cache::CACHE_TABLE_USERCONTENT && $$param{'type'} == 2)
{
### user-content
my $query = 'SELECT PARAM_CONTENT AS C, DATA AS D FROM sf_cache_usercontent WHERE SITE=? AND PARAM_USER=?';
my $bindvar = { 1=>$self->{'site'}, 2=>$$param{'user'} };
my $sth = $self->db_select($query, $bindvar);
#print SF::Util::debug_dumpquery($query, $bindvar);
return undef if($self->{'Error'});
my %usercontent;
undef(%usercontent);
while(my $hashref = $self->db_fetch($sth))
{
$usercontent{$$hashref{'C'}} = $$hashref{'D'};# ? 1 : 0;
}
return \%usercontent;
}
else
### ******************************************************************************************************
{
my $ret = $self->SF::Cache::get_database('DATA', $param);
return Storable::nfreeze($ret) if(defined $ret);
}
}
else
{
### FILESYSTEM
my $filename = $self->SF::Cache::filename($type, $param_ref);
if($filename && -e $filename)
{
if($self->{'INI'}{'sf.system.cache.lock.enabled'} && defined &lock_retrieve)
{
return lock_retrieve $filename;
}
else
{
return retrieve $filename;
}
}
else
{
$! = 0;
}
}
return undef;
}
Go back to your original system, thaw then nfreeze the file there to fix it.
perl -MStorable=nstore,retrieve -e'nstore(retrieve($ARGV[0]), $ARGV[1])' file fixed
So, "not a reference" means ... exactly what it says on the tin. Can you try printing the thingy with Data::Dumper from comments it's this line:
return Storable::nfreeze($ret) if(defined $ret)
So - what does:
print Dumper $ret;
produce? Is it a reference?
I'm not so sure though that you're right about needing nfreeze instead of thaw, because they both do different things. freeze packs a variable; thaw unpacks it. So nfreeze can replace freeze.
But the core purpose of doing this is to transfer your packed up scalar to another program on another architecture. Is this what you're doing?
If so, can I suggest instead considering transferring it as JSON or XML instead?
Having a code:
Line1
Line2
..
LineN
I want to automatically produce
Line1
JunkLines2-100
Line2
JunkLines102-200
Line3
etc.
where JunkLines are Perl junk code that does not change state of program but looks like continuation of legitimate program.
I don't want obfuscate my code with obfuscators that, say, rename variable names to something unreadable - this unreadability is a signal that code is obfuscated.
I don't understand why so many downvotes: this looks like an interesting problem.
I had some spare time and tried something on my own:
Garbage code is in a separate file with blocks separated by #---
Ofuscator is changing names of variables and functions in garbage code if this exists in original good code (I assumed you define variables with my and functions with sub)
Ofusctaor is randomly adding content from garbage file (looping if needed)
That looks like this
Good code (original.pl)
#!perl
use strict;
my $var1='hello';
my $var2='there';
sub f {
print "This is a function";
++$var1
}
print $var1;
print "\n$var2";
Garbage code (garbage_code.pl)
#!perl
#---
my $var2='__NEW';
my $var3="This is garbage";
#---
sub g {
$var3+="changed";
print "This shall be removed";
return $var3;
}
#---
sub f {
return g(shift).$var2;
}
#---
f("function name shall be changed");
Offuscator
#!perl
use strict;
use Data::Dumper;
#-Get Original 'good' code
open CODE,"<original.pl" or die $!;
my #code=<CODE>;
close CODE;
#-Get Garbage code
open GARBAGE,"<garbage_code.pl" or die $!;
my #garbage=split(/^#---/m, join("",<GARBAGE>));
shift #garbage; #Remove header
map { s/^.*?(\w.*?)\s*$/\1/s } #garbage; #Trail spaces and EOL at beginning and end
map { s/print .*?;//g } #garbage; #Remove print calls
close GARBAGE;
#-List variables and functions in good code
my %list_var;
my %list_func;
for my $line (#code) {
if ($line=~/my \s*[\$#%](\w+)/) { $list_var{$1}=undef; }
elsif ($line=~/sub \s*(\w+)/) { $list_func{$1}=undef; }
else { }
}
#-List variables and functions in garbage code
my #list_var_garbage;
my #list_func_garbage;
for my $line (#garbage) {
while ($line=~/my \s*[\$#%](\w+)/g) { push(#list_var_garbage,$1); }
while ($line=~/sub \s*(\w+)/g) { push(#list_func_garbage,$1); }
}
#-Replace names of variables and functions in garbage code if it exists in good code
#Get equivalent name
for my $type ('var', 'func') {
my $rh_list = ($type eq 'var' ? \%list_var : \%list_func);
my #list_names=(keys %$rh_list, ($type eq 'var' ? #list_var_garbage : #list_func_garbage));
for my $name (#list_names) {
#Get new name
my $new_name=$name;
#For names of good code OR new names in garbage code
if (!defined $rh_list->{$new_name}) {
while (exists $rh_list->{$new_name}) { $new_name.="1"; }
#Store in hash table
$rh_list->{$new_name}=undef; #to ensure uniqueness of replacements
$rh_list->{$name}=$new_name; #Replacement name in garbage code
} else {}
}
}
#Replace
map { s/(?:sub \s*|&)\K(\w+)/$list_func{$1}/g } #garbage;
map { s/(\w+)\(/$list_func{$1}(/g } #garbage;
map { s/[\$#%]\K(\w+)/$list_var{$1}/g } #garbage;
#-Function to get garbage content
my $i_garbage=0;
sub get_garbage {
return $garbage[ ($i_garbage++) % scalar(#garbage) ]."\n";
}
#-Copy garbage in good code
my #new_code;
for my $line (#code) {
#-Add the line
push(#new_code, $line);
#-Add garbage
#Blocks: add garbage at the end
if ($line=~/\{/ .. $line=~/\}/) {
if ($line=~/\}/) { push(#new_code, get_garbage()); }
#Other: randomly add garbage
} else {
if (int(rand(2))) { push(#new_code, get_garbage()); }
}
}
#Print file with new code
open NEW_CODE, ">new.pl" or die $!;
print NEW_CODE #new_code;
close NEW_CODE;
Result
#!perl
use strict;
my $var21='__NEW';
my $var3="This is garbage";
sub g {
$var3+="changed";
return $var3;
}
my $var1='hello';
sub f1 {
return g(shift).$var21;
}
my $var2='there';
f1("function name shall be changed");
sub f {
print "This is a function";
++$var1
}
my $var21='__NEW';
my $var3="This is garbage";
sub g {
$var3+="changed";
return $var3;
}
print $var1;
print "\n$var2";
sub f1 {
return g(shift).$var21;
}
f1("function name shall be changed");
It probably does not take into account all cases but this is definitely a good working prototype.
Cheers
I'm not sure if you're receiving negative votes from lack of information or what, but this is a relatively easy fix. Assuming you want to do it in Perl:
just create another perl file which reads in your file one line at a time,prints it out to a new file, and appends a line of random nonsense afterwords (in this case, from a large file which is assumed to contain a bunch of random lines of perl code "obfuscatedlines.txt")
A small example:
use strict;
use warnings;
my #randomlines;
open (INHANDLE, "<perlfile.pl");
open (OBHANDLE, "obfuscatedlines.txt");
open (OUTHANDLE, ">newfile.pl");
while(<OBHANDLE>) {push #randomlines, $_;}
my $i=0;
while(<INHANDLE>)
{
print OUTHANDLE $_;
print OUTHANDLE $randomlines[$i];
$i++;
}
I'm trying to use asp-perl to pre-process some files that have embedded perl, asp style (probably doesn't matter, but it's not html).
for example:
want this <%="yes"%>
not this <%print "no" %>
I would like it to yield:
want this yes
not this
and have the 'no' end up in a different file or stream.
Is there some flag/configuration to enable this? I tried looking in CGI, Apache::ASP,... and nothing's jumping out at me.
EDIT. After burning a bunch of time in the debugger, I've found that overriding these two subs gives me the result I want. a bit of a hack. I guess I only needed the first sub. The second is to avoid writing to a file.
sub Apache::ASP::InitPackageGlobals {
my $self = shift;
# unless ($self->{response_tied}) {
# # set printing to Response object
# $self->{response_tied} = 1;
# tie *RESPONSE, 'Apache::ASP::Response', $self->{Response};
# select(RESPONSE);
# }
# ---- init package objects ----
# unoptimized this because we should only call this function once
# and maybe twice if there is a defined Script_OnStart
for my $object (#Apache::ASP::Objects) {
for my $import_package (#{$self->{init_packages}}) {
my $init_var = $import_package.'::'.$object;
$$init_var = $self->{$object};
}
}
undef;
}
my $parse_results = "";
sub Apache::ASP::CGI::print {
shift;
$parse_results .= join("", map { ref($_) =~ /SCALAR/ ? $$_ : $_; } #_);
}
I'm a beginner and confused about what's happening inside this Perl subroutine.
I'm using only global variables to simplify things, but it's still not working.
I'm simply trying to print a file's read, write and executable attributes using the file test operators with IF statements.
Can anyone point out the problem for me?
Louie
sub getfileattributes {
if (-r $file) {
$attributes[0] = "readable";
} else { $attributes[0] = "not readable"; }
if (-w _) {
$attributes[1] = "writable";
} else { $attributes[1] = "not writable"; }
if (-x _) {
$attributes[2] = "executable";
} else { $attributes[2] = "not executable"; }
}
my #attributes;
my $file;
foreach $file (#ARGV) {
&getfileattributes;
printf "The file $file is %s, %s and %s\n", #attributes;
}
Using global variables is usually quite bad and points to a design error. In this case, the error seems to be that you don't know how to pass arguments to a sub.
Here is the pattern in Perl:
sub I_take_arguments {
# all my arguments are in #_ array
my ($firstarg, $secondarg, #rest) = #_;
say "1st argument: $firstarg";
say "2nd argument: " .($firstarg+1). " (incremented)";
say "The rest is: [#rest]";
}
Subs are invoked like
I_take_arguments(1, 2, "three", 4);
(Do not invoke them as &nameOfTheSub, this makes use of very special behaviour you don't usually want.)
This would print
1st argument: 1
2nd argument: 3
The rest is: [three 4]
Subroutines can return values, either with the return statement or as the value of the last statement that is executed. These subs are equivalent:
sub foo {return "return value"}
sub bar {"return value"}
I would write your getfileattributes as
sub getFileAttributes {
my ($name) = #_;
return
-r $name ? "readable" : "not readable",
-w $name ? "writable" : "not writable",
-x $name ? "executable" : "not executable";
}
What is happening here? I take an argument $name and then return a list of values. The return keyword could be omitted. The return takes a list of values and does not require parens, so I leave them out. The TEST ? TRUE-STATEMENT : FALSE-STATEMENT operator is known from other languages.
Then, in your loop, the sub would be invoked like
for my $filename (#ARGV) {
my ($r, $w, $x) = getFileAttributes($filename);
say "The file $filename is $r, $w and $x";
}
or
foreach my $file (#ARGV) {
my #attributes = getFileAttributes($file);
printf "The file $file is %s, %s and %s\n", #attributes;
}
Notes:
say is like print, but adds a newline at the end. To use it, you have to have a Perl > 5.10 and you should use 5.010 or whatever version or use feature qw(say).
always use strict; use warnings; unless you know better for sure.
Often, you can write programs without assigning to a variable twice (Single assignment form). This can make reasoning about control flow much easier. This is why global variables (but not global constants) are bad.
You are not actually using global varaibles. My scopes the variables them local to the main routine, so when you call the subroutine, $file and #attributes are scoped to the subroutine, not to the main routine.
Change my to our for $file and #attributes to make the variables global and available to the subroutine.
You can check this for yourself by using the -d argument for perl to run it in the debugger and check the values of the items.
This is driving me crazy, Perl is simply losing the value of a variable once I enter an if statement... and the weird this is, its only that variable, any other variable will not lose its value
open (MYFILE, "b");
my $haysack = "";
while (<MYFILE>)
{
$haysack = $haysack . "$_";
}
close (MYFILE);
open (MYFILE2, "ip_range");
my $needles = "";
while (<MYFILE2>)
{
$needles = $needles . "$_";
}
close (MYFILE2);
my $someOtherValue = "blabla";
while ($needles =~ m/(.*?)\n/g)
{
$needle = $1;
if ($haysack =~ m/$needle/ims)
{
print "FOUND : $needle\n";
print "$someOtherValue\n";
}
}
So the code succesfully enters the if statement, but once it does I get the following output:
FOUND:
blabla
can anyone help?
This really should be a comment, since it's not an answer, but comments don't allow code formatting, so:
Can you provide a complete, runnable, self-contained, minimal example which demonstrates the problem without extraneous moving parts, such as reading files? Something similar to the following:
#!/usr/bin/env perl
use strict;
use warnings;
my $haysack = "Foo
Bar
Baz
";
my $needles = "a
b
c
";
while ($needles =~ m/(.*?)\n/g) {
my $needle = $1;
if ($haysack =~ m/$needle/ims) {
print "FOUND : $needle\n";
}
}
...except that mine runs perfectly, producing the output
FOUND : a
FOUND : b
rather than failing. Note that, in the process of creating a minimal failing example, you are very likely to discover the solution to your problem...
As far as general troubleshooting advice, use strict and use warnings if you aren't already doing so. Check the value of $1 after doing the outer match to verify that $needle will be getting the value you expect it to.