Cross-platform Perl/CGI tips and tricks

Friday Dec 4th 1998 by Dave Edis
Share:

Let's talk about writing cross-platform Perl/CGI programs. Cross-platform means that a program will work just as well on a Windows server as it will on a Unix server and vice versa. It also means that if a program is moved from one type of server to another, it can run without any modifications.

December 4, 1998
Cross-platform Perl/CGI tips and tricks

by Dave Edis

Let's talk about writing cross-platform Perl/CGI programs. Cross-platform means that a program will work just as well on a Windows server as it will on a Unix server and vice versa. It also means that if a program is moved from one type of server to another, it can run without any modifications.

Not only does this make your code more portable and easier to set up, it also saves you time when you are developing a program on one server and installing it on another, or when your client decides to switch servers or Web hosting providers.

Figuring out what the cgi directory is

One of the biggest hassles in setting up a new program is figuring out the paths to data files, templates, and other required files. These can vary widely from server to server, and if you only have FTP access to the server on which you're installing your program, it can be very difficult to determine the full filepath.

Even when you know the filepath, you will need to modify it each time your program is installed on a different server. If you are constantly jumping from a development server to a staging server to a live server, this can be a lot of work.

The trick? In Perl the

$0
variable contains the full filepath of the script currently being executed. By using a simple regular expression, we can chop the script name off the end and get the name of the current directory the script is in (
$cgidir
). Then we can access all our required files using relative filepaths. (i.e.,
"$cgidir/../data/database.db"
).

### Find current directory path
if    ($0=~m#^(.*)\\#){ $cgidir = "$1"; }  # win/dos
elsif ($0=~m#^(.*)/# ){ $cgidir = "$1"; }  # Unix
else  {`pwd` =~ /(.*)/; $cgidir = "$1"; }  # Unix

The first line finds the cgi directory on a Windows server, which uses back slashes to separate the filepath. The second line finds the cgi directory on a Unix server, which uses forward slashes. Finally, we've found that on a very few Unix servers

$0
isn't defined with the full filepath. In that case, we can get the current directory by using the Unix "pwd" command.

By using

$cgidir
, you'll never have to worry about server filepaths again.

How to "die" with something better than "Server Error 500"

Anyone who develops Web applications is all too familiar with that "Server Error 500" screen, the one that appears every time you have even the slightest error in your code. When something goes wrong in your program and Perl "dies," it exits the program currently running and makes an entry in the error log telling you what the problem was.

In many cases, it can be difficult to locate and view the error log to find out just what went wrong, and on some servers it's not possible at all (due to file permissions). As it turns out, there's a quick and easy way to replace that error screen with a helpful error message of your own design. By setting the signal handler for die, we can "catch" that die signal and tell the program to do something else before it dies. The following code should go near the top of our program so it runs before anything else.

$SIG{__DIE__} = \&Error_Msg;

sub Error_Msg {
  $msg = "@_";
  print "Content-type: text/html\n\n";
  print "The following error occurred : $msg\n";
  exit;
}

You can customize your error message even more by specifying the "die" message and using

$!
to tell you what went wrong. You can even have it print up a fancy HTML screen so it doesn't look so scary to the user. Also, by default, die will tell you the name of the program in which the error occurred and on which line, but you can prevent that (if you want) by putting a nextline character ("\n") at the end of your "die" message.

open(FILE,"<$cgidir/myfile.dat") ||
[break inserted here by editor to fit page; omit when copying!]

 die("Read Myfile : Can't open file $cgidir/myfile.dat : $!"); 

Now when your programs crash, die will tell you exactly what happened and why.

How to handle end-of-line characters and binary files

Windows and Unix handle end-of-line characters differently. Unix, for example, puts a single nextline character ("\n") at the end of each line in a text file to signify that that line is over and a new one has started. Windows does the same thing but uses two characters, a return and a nextline character ("\r\n").

This means that anywhere those characters are being read in, your program should recognize them as either "\r\n" or "\n". This could be while reading in data files, parsing text, reading form input, or so forth. If you're counting on one and you get the other, your program may not function the way you want it to. An easy way to account for this is to simply replace "\r\n" with "\n" like this:

$string =~ s/\r\n/\n/gs; 


Another issue is that Windows treats binary files and text files differently, so if you're trying to read or write a binary file, you'll get corrupted data, unless you specify that the file is to be read or written in binary mode.

### Read Binary file on Windows
open(FILE,"<$cgidir/image.gif");
binmode(FILE);       # Specify Binary mode for file
binmode(STDOUT);     # Specify Binary mode for output
print "Content-type: image/gif\n\n";
while () { print; }
close(FILE);

Unix doesn't pay attention to "binmode," so it won't cause problems by having it in there, it will just make Windows systems recognize the file as binary and read/write it correctly.

Perl replacements for common system commands

I've seen many CGI programs that would work beautifully on any server except for one simple thing. Where the programmer needed to copy, rename, or erase a file, instead of doing it in Perl he just used an operating system specific shell command (e.g.,
system("cp $file1 $file2");
). Not only does this make the program only work on Unix, it also poses a potential security issue when you pass unchecked variables to the shell.

Here are some 100 percent Perl-based alternatives to common system commands that will work on any operating system.

### ERASE
unlink("$cgidir/file.dat"); 

### RENAME
rename("$cgidir/oldfile.dat","$cgidir/newfile.dat"); 

### COPY
open(FILE1,"<$cgidir/file1.dat");
open(FILE2,">$cgidir/file2.dat");
binmode(FILE1);
binmode(FILE2);
while () { print FILE2; }
close(FILE1);
close(FILE2);

### CHMOD
chmod(0777,"$cgidir/file.dat"); 

### MAKE DIRECTORY
mkdir("$cgidir/newdir",0777); 

### REMOVE DIRECTORY
rmdir("$cgidir/newdir"); 

### CURRENT DATE/TIME
$date = scalar localtime(time);
print $date;  

By using a few simple tricks and tips like these, you'll not only be saving time but writing better programs too! If you have any tricks and tips of your own you would like to share, drop me a line.

Dave Edis is president of Edis Digital, a new media solutions firm in Vancouver, Canada. Edis Digital develops Web-based publishing tools and applications for companies around the world.


Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved