PDA

View Full Version : Need assistance in modifying some perl code


Kabuthunk
05-10-2008, 02:48 AM
Hello all, glad I stumbled across this forum. Given the many different forums for coding help, I might actually be able to assist in some, and ask questions in others... such as this one :P

In either case... I've got some perl code that I need help modifying... seeing as I'm completely unfamiliar with perl. A friend set up the code for me (or downloaded it somewhere, I'm not sure), but I need it changed a bit.


Currently, I have a webpage that uses a server-side include to generate a random image. Not in the way I've seen like... every other random-image generator works, but entirely different. The coding inside the html file is:

<a href=" <!--#include virtual="/cgi-bin/rand.pl/randomimage.txt" --> " border="0" alt="">

Inside rand.pl is:

-----------------------------------

#!/usr/bin/perl
#
# Standard place to look for files - change this to suit your needs.
# NOTE: Trailing slash is important - leave it there!
$base = "";

# Get the file name and the type of random number generation.
$x = substr ( $ENV{'PATH_INFO'}, 1 );

@args = split ( /,/, $x );

$file = $args[0];
$type = $args[1];
$p = "$base$file";

$prevRand = -1;
&SeedRandom;

# Set to flush output
$|=1;

print "Content-type: text/html\n\n";
print "\n";

$n = 0;

&Fatal ( "Can't open main file $p" ) if ! -r $p;
open ( PAGE, $p );
while ( <PAGE> )
{
$line[$n] = $_;
$n++;
}
close ( PAGE );

$i = int(rand($n));

print $line[$i];

# --- Print a fatal error message.

sub Fatal
{
local ( $m ) = @_;
print "<p><strong>Server error!</strong>\n";
print "<p align=center>$m</p>\n";
die "Aborted";
}

sub SeedRandom
{
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime;

if ( $type eq "d" || $seed eq "d" )
{
$x = $year * 365 + $yday;
srand($x);
}
elsif ( $type eq "m" || $seed eq "m" )
{
$x = $year * 12 + $mon;
srand($x);
}
else
{
srand;
}
}


-------------------------------------

And finally, randomimage.txt is simply a text file, however each line is a different image, and a bit of html code. For example:

/p1/archives/z991.html"><img src="/planetzebeth/archives/z991.png
/p1/archives/z992.html"><img src="/planetzebeth/archives/z992.png
/k1/kabucam/index.html"><img src="/planetzebeth/kabucam/kabucam001.jpg


The perl code picks a random line from inside that file, and thus the random image generator allows the image to be clicked on, and take you to the specific page that it's located at.


My problem is that the randomimage.txt file is starting to get a little on the bulky side... as in well over a meg, and steadily climbing. Thus, I need to modify the code so that I can break up that .txt file into multiple files. I just don't want to piss off my hosting service with massive SSI calls here.

So could anyone tell me if the perl code can be modified to randomly pick from one of X different files (random1.txt, random2.txt, etc... something like that), followed by randomly selecting a line from that file like it currently does?

FishMonger
05-10-2008, 04:17 AM
First, let me say that a 1 or even 2 meg file is still considered tiny as far as Perl is concerned. I often work with files that are over 300meg and Perl can easily handle much larger files. I would just use the 1 file instead of breaking it up, unless you needed to split up the links into categories.

To answer your question, yes that script can be modified to randomly pick a file, but that script is very poorly written and I wouldn't recommend using it.

Here are a number of well written modules to choose from that could be used; 2 of which are specifically designed for this task, the other 2 just help to simplify half of the task. (note, only 1 of these would be needed)

File::RandomLine - Retrieve random lines from a file
http://search.cpan.org/~dagolden/File-RandomLine-0.19/lib/File/RandomLine.pm

File::Random - Perl module for random selecting of a file
http://search.cpan.org/~bigj/File-Random-0.17/Random.pm

Tie::File - Access the lines of a disk file via a Perl array
http://search.cpan.org/~mjd/Tie-File-0.96/lib/Tie/File.pm

File::Slurp - Efficient Reading/Writing of Complete Files
http://search.cpan.org/~drolsky/File-Slurp-9999.13/lib/File/Slurp.pm

Kabuthunk
05-10-2008, 04:43 AM
Well, at least I know it's possible. But yeah, splitting that randomimage.txt file apart would help a lot with organization as well.

Looking around those several links, they definitely have a lot less coding than what I'm currently using. Always a plus. Although, for the randomizing, it says it's based on the number of characters per line, so I might have to test that a bit to see if it's random enough, since the length of the lines may vary quite a bit. Although, if I can get things in seperate files, I can easily avoid the significant differences.

Although, with the "Random" perl module, it says it has multiple dependencies. Given I'm unfamiliar with this, would those be seperate files I need somewhere, or do I just kinda hope that the server space provider has them... somewhere?


Given I'd need to get a random line from a random file, would it be called from the html page with a command something like:

<!--#include virtual="/cgi-bin/randomline.pl/<!--#include virtual="/cgi-bin/random.pl/" -->" -->

Is it even possible to have nested #includes?

FishMonger
05-10-2008, 05:05 AM
I've never tried, but I highly doubt that you can nest SSI statements.

Before I show an example of selecting a random file, here's a cleaned up version of your script (which could still use a couple more adjustments/improvements).
#!/usr/bin/perl

use strict;
use warnings;
use CGI::Carp qw/fatalsToBrowser/;

# Standard place to look for files - change this to suit your needs.
# NOTE: Trailing slash is important - leave it there!
my $base = "";

# Get the file name and the type of random number generation.
my $file = $base . substr( $ENV{'PATH_INFO'}, 1 );

open my $PAGE, '<', $file or Fatal("Can't open main file $file");
chomp(my @lines = <$PAGE>);
close $PAGE;

print $line[int(rand($#lines))];


sub Fatal {
my $error_msg = shift;
print "<p><strong>Server error!</strong>\n",
"<p align=center>$error_msg</p>\nAborted";
exit;
}

Kabuthunk
05-10-2008, 06:11 AM
Awesome. Your updated code indeed works, and looks a helluva lot less messy than the old version. Although I gotta say, this one's using a lot of different commands. Can't say I've ever seen "chomp" in any language I know. Well... unless I name a variable or constant that :P.

And I'm likely mistaken, but going off of this code, I'd think the "$line[int(rand($#lines))];" could be modified to pick a number between 1 and 5 or whatever, and then insert it into the filename such as rand$file.txt, becoming rand1.txt, rand2.txt, etc. Again... this is purely speculation, and I have no clue if that's possible, or if that would just... y'know... crash.

And for the record, nested #includes officially do not work. At least in Firefox inside Linux. It looks like it does it once, and then just... leaves the second one in the source code when the webpage loads. So... no such luck just using the above code twice :P.

FishMonger
05-10-2008, 07:09 AM
Can't say I've ever seen "chomp" in any languagecomp removes the line terminator, which is \n by default, from each element of the @lines array.

perldoc -f chomp
http://perldoc.perl.org/functions/chomp.html

perldoc strict
http://perldoc.perl.org/strict.html

perldoc warnings
http://perldoc.perl.org/warnings.html

perldoc CGI::Carp
http://perldoc.perl.org/CGI/Carp.html

http://search.cpan.org/

And I'm likely mistaken, but going off of this code, I'd think the "$line[int(rand($#lines))];" could be modified to pick a number between 1 and 5 or whatever, and then insert it into the filename such as rand$file.txt, becoming rand1.txt, rand2.txt, etc.
You mean like this:
my $num = int(rand 5);
open my $PAGE, '<', "rand$num.txt" or Fatal("Can't open main file rand$num.txt");

The problem with that is as you add/remove files, you'd need to modify the script to accommodate the new number of files.

It's late and my 14 hour day is over. Tomorrow, I'll post a better solution.

Kabuthunk
05-10-2008, 03:43 PM
I've got no problems with modifying the perl code as needed. I imagine I'll start with say... 5 files (since that's the number I've been using as an example), and can't imagine that changing more than like... once a year tops.

Otherwise, I'm kinda lacking in time at the moment until later today, so I won't be able to give that random page part of the code a test right now... but if you're plotting a better solution, I guess that works out then :P

Kabuthunk
05-11-2008, 07:15 AM
Hell, wait... I didn't upload your updated version of the original code properly... only realized that now. It unfortunately doesn't work and gives me the error "[an error occurred while processing this directive]".

FishMonger
05-11-2008, 04:44 PM
There's a slight typo (missing an s) on one of the lines

change:
print $line[int(rand($#lines))];

to:
print $lines[int(rand($#lines))];

and make sure you upload it in ascii mode, not binary.

Kabuthunk
05-11-2008, 05:06 PM
Yep, it's uploading in ascii. Unfortunately, it's still giving me the same identical error :{

Could it perhaps be the way I'm calling it through the HTML file? Does this one need to be treated differently?

If it helps, the page is at http://www.zebeth.com/planetzebeth/random.html

awatson
05-12-2008, 07:22 PM
Could it be a permissions problem? You could try changing it to 777 and seeing if it'll run. Or could can you try running the script from the command line to see if returns any errors?

FishMonger
05-12-2008, 07:58 PM
The first question for me to ask is: Did it work with your original script?

If it did, then the problem is most likely incorrect permissions. It should be 755

If the original one didn't work, then it's most likely the way the script is being called.
I'd normally expect it to be called like this:<a href="<!--#include virtual="/cgi-bin/rand.pl?file=randomimage.txt" --> " border="0" alt="">

Then you'd use the CGI module to parse the url.use CGI qw/:standard/;
my $file = param('file');

However, since you're planing on splitting up the randomimage.txt file, it would be best to not pass the filename in the url.

Kabuthunk
05-14-2008, 02:07 PM
Well, ignoring the spam account that posted in here, the original code did indeed work. So when you mean to change it to 755, I'm not entirely sure what you mean, and is it possible to do from an FTP program (gFTP in this case... but I think that's linux-specific :P).

Going on the thought that you mean chmod 755, I don't seem to quite have that option. I have a pile of checkboxes (SUID, SGID, Sticky, and then sections of 'user', 'group', and 'other' have options of Read, Write, Execute), but no option to change a chmod number. Strange.

chaosprime
05-14-2008, 04:34 PM
In those terms, 755 means "user read+write+execute, group read+execute, other read+execute".

FishMonger
05-14-2008, 04:55 PM
Each digit represents the permission settings for 'user', 'group', and 'other' (in that order).

read = 4
write = 2
execute = 1

In this case, the seven means the 'owner' has full rights, 'group' and 'other' have read and execute rights but can't modify the file. Just place a 'check' in the corresponding boxes for each section.

The only other possible problem that I can think of would be the "use CGI::Carp" line. That is a core module so it should be available, but some hosts strip their Perl installations down to the bare minimum leaving out some of the core modules. If the permissions settings don't fix the issue, try commenting out that use statement.

Kabuthunk
05-14-2008, 07:58 PM
It looks like the file had those permission settings anyway (which is rather odd, since those persmissions were only on the server end, not on my hard drive's version). I tried commenting out that "use CGI" line, same thing... [an error occurred while processing this directive].

It'd be nice if the error it spat out was a bit more specific. As a last ditch hope, I've tried commenting out various other lines in hopes of getting ANY other error. Commenting out all 3 'use' commands at the top does nothing. Commenting out the entire 'fatal' section does nothing. I've tried commenting out everything except a single line of 'print "asdf"; and it still spits out the same error.

The only way I can get it to say anything else is if I comment out say... close $PAGE; or my $base = ""; or get rid of the word 'my', at which point it spits out big error about contact the site administrator to report error.

So yeah... at this point, I'm completely baffled as to what part of this new code it doesn't like.

FishMonger
05-14-2008, 08:31 PM
The only other thing I can think of is that your host is using an old version of perl that doesn't support lexical filehandles.

change this:open my $PAGE, '<', $file or Fatal("Can't open main file $file");
chomp(my @lines = <$PAGE>);
close $PAGE;

to this:
open PAGE, $file or Fatal("Can't open main file $file");
my @lines = <PAGE>;
chomp @lines;
close PAGE;

The web server error log will have more detailed info on what's wrong. Does your control panel have a section where it would provide the log entries? If not, you might want to contact your host to see if they will provide the info.

Kabuthunk
05-14-2008, 08:52 PM
Same error. However, I located the error log. The error it's constantly been having evidently is:

error: file is writable by others: (/home/zebethc/public_html/cgi-bin/rand1.pl)


Which makes NO sense whatsoever, since I've just checked again, and neither 'group' nor 'others' has checked off "write". But it looks like this might be permission based.

Strange, I wonder why this code needs different permissions than the old perl code though.


On the plus side, said error log helped me take care of a few other random errors on the site.

Currently trying to get a hold of server admin to find out why it thinks it's writable when it's chmod indicates it's not. They're offline at the moment. No clue if they will even have an answer for me eventually

chaosprime
05-14-2008, 09:46 PM
Any chance you could be checking the permissions on the wrong file -- rand.pl rather than rand1.pl?

That "file is writable by others" is actually an Apache-level error that's for your security in a shared environment. But yeah, either you're looking at the wrong file in your chmod interface or the chmod interface is misreporting the permissions.

Kabuthunk
05-14-2008, 10:41 PM
No, not going after the wrong file... neither one of them are writable. Hell, rand.pl isn't even executable (rand.pl being the 'old code' for the random quotes on my page, rand1.pl being the one we're working with). Hence, I'll poke around and see if there is any other way to chmod these files other than gFTP.

Kabuthunk
05-30-2008, 07:06 AM
Sorry about the ridiculously long delay here... the server admin hasn't gotten back to me. It's not really through a company or anything... it's just a guy who's hosting like... a few sites on a server he's got through yet another place. I'm getting it dirt cheap though :P


But otherwise, while we're waiting, is there any way to get the currently working code to do the pick-a-file, then pick-a-line thing?

chaosprime
05-30-2008, 02:08 PM
Well, I imagine you can try just backing up your rand.pl and replacing it with the code you're trying to set up, rather than making a new rand1.pl. But it would be really unpleasant if writing rand.pl screwed up its permissions too.

bazz
05-30-2008, 03:45 PM
Just to ship in in case it might help.

I have only ever had the error you showed, [an error occurred while processing this directive] when the include has been wrong.

Whenever the include has been OK and if the perl is wrong, it throws most often the 500 error. So I would look into how you are calling the script as a more likely reason for it not working. FishMongers scripts are as close to bullet-proof as you would need. :cool:

bazz

FishMonger
05-30-2008, 09:28 PM
On the 9th you stated that the script I provided worked, but on the 10th it didn't work. I can only assume that when it was working, you were testing it on you local machine and on the 10th, you uploaded it to the server and it stopped working.

Either:

The script was corrupted during the upload.
The file permissions are set incorrectly.
The file was not uploaded to the proper directory or does not have the correct filename.
If your original script is working, then I'd hesitate modifying it until we can learn more about the server setup and the cause of the failure of my updated version. If you modify the working script, you might "break it" in the same way as the now non working script, in which case, you'll be left without any working script.

Kabuthunk
06-03-2008, 06:32 AM
As I said on the 11th, "I didn't upload your updated version of the original code properly". It's relatively safe to assume that FishMonger's code is correct... it's the server that's being stupid. I discovered that when I thought it was working, it was still using the original code. It turns out the new code has never worked on this server,

The original code doesn't even have the same permissions as the new one needs... the original is set to chmod 744 (by default, it seems, as I've never changed that one from the get-go), while the new one is set to 755. I wouldn't worry about 'breaking' the original script, as that's backed up on a half-dozen different places anyway.

bazz
06-03-2008, 10:23 AM
when you try to view your perl script (the new one), are you looking at the html page which includes it or, are you looking (trying to), directly at it through www.yourdomain.com/cgi-bin/script.pl?

if looking through the web page, if it throws that error [can't follow that directive] when looking through the html page what does it throw up when looking at the script directly?

It is possible that your looking only through the html page and it could then be the ssi throwing the error rather than the script?

bazz

FishMonger
06-04-2008, 12:16 AM
when you try to view your perl script (the new one), are you looking at the html page which includes it or, are you looking (trying to), directly at it through www.yourdomain.com/cgi-bin/script.pl?
Since it doesn't output the html headers, the script I provided can not be called directly. It was meant to be called via the SSI call and shouldn't need to output the headers because the html page should have already done so. If the html page didn't output the headers, like it should have done, then doing so in the script will allow it to be called as a standalone script and "might" fix the SSI issue.