When running Perl applications under IIS, I found that they do not seem to know what the CWD (Current Working Directory) is, and appear to think that it is c:\Perl, or wherever you have Perl installed. After digging a while, I found that it is the web server's responsibility to set this environment variable, and Perl reads it from the environment. Thus, we can safely say that it is Microsoft's fault.
In looking for a lookaround, the best thing that I came up with was, of course, installing Apache. But, failing that, you can add the following code to the top of your Perl CGI programs:
BEGIN{($_=$0)=~s![\\/][^\\/]+$!!;push@INC,$_}
Of course, when I posted that code to a mailing list, people found that it worked, but was entirely illegible, and they really wanted to know what it did. Here's the complete text from a note that I posted to the HWG-Servers mailing list:
I wrote the following ghastly code ... > > BEGIN{($_=$0)=~s![\\/][^\\/]+$!!;push@INC,$_} > > ($here=$0)=~s![\\/][^\\/]+$!!; And Steve very reasonably asked ... > ...I was wondering if you would mind taking the time to > decompose these two little gems for me. (Regex's are definitely the > most powerful and yet confusing programming constructs I've run across, > with the possible exception of APL.) OK, first ... BEGIN{($_=$0)=~s![\\/][^\\/]+$!!;push@INC,$_} BEGIN{} is a piece of code that will be executed before anything else in the script. In general, Perl executes stuff in the order that it appears in the code - being very imprecise there. There are some exceptions to this, like BEGIN blocks and "use" statements, and some other stuff. BEGIN ensures that the usual ordering is ignored and this statement gets done before other stuff. This is important in this case, because any "use" statements are likely to fail if this code is not executed first, as you will see in a minute. ($_ = $0) That little bit there takes the value of $0 and puts it in the variable $_ $0 is the name of the script that is currently running. More accurately, it is the first agrument that was presented to the perl interpreter, which is always the name of the script, in the CGI environment. $_ is the "default" variable, which is a pretty powerful thing all by itself, but is probably a topic for another time. I stuck it in another variable, since modifying $0 can have some wierd results, and, potentially, you may actually want to know what $0 was, so I really don't want to modify it. OK, now the ugly part. ($_=$0)=~s![\\/][^\\/]+$!! You already know what ($_=$0) means. =~ means, simply put, perform the following regular expression on. OK, so it means more than that, and terms like "bind to" are used, but take that's what it really means. Now, the s/// operator is pretty cool (this may seem like a side track - hang on). Syntax like s/foo/bar/ replaces the sub-string "foo" with the sub-string "bar" in a string. Occasionally, however, your s/// statement might contain references to the character "/", as in our case. When this happens, you have to "escape" the /, so that the s/// operator does not get confused between the /'s that occur in the operator, and the ones that are actually part of your string. (Escaping involves putting a \ in front of the offending character.) Another convenient way around this is that you can use other characters instead of the /, such as !, so that the s/// operator becomes the s!!! operator. This is what I have done in my code. In my s/// operator, we have the follwing: s![\\/][^\\/]+$!! Think of it in two parts - the "search" part, and the "replace" part. Consult the s/foo/bar/ example above for a more concrete example. In this example, the "search" part is [\\/][^//\]+$ and the replace part is "" - that is, it is empty. Let's do the easy part first. If we match the "search" part (whatever it ends up meaning) we'll replace it with "" (nothing). That is, we'll just delete that part. That's not too bad. OK, now the "search" part, one piece at a time. [\\/][^//\]+$ [] is a character class, and that means that I want to match any of the characters that appear in there. In my character class are two characters. \ and / Remember what I said about escaping characters? Well, every time you have a \ character, you have to escape it, or Perl thinky that you are using it to escape something else. So, [\\/] means I want to match one character that is either \ or /. The second character class is [^\\/]. The ^ means not, and I am trying to match a character that is NOT \ or /. The + means more than one. The $ means "occurring at the end of the string. OK, so, to translate that into something that makes more sense, I am starting with the name of the script that is running, which is something like c:\foo\bar\baz\script.pl And I am matching everything at the END that starts with a \ (or a /, for systems that use that slash istead of the other one) followed by one or more non-slash characters, and removing it. In this case, that would leave is with c:\foo\bar\baz , which is the "current directory", as desired. Note that this will also work with systems that use the / slash, since the regular expression does not care which slash I am using. Finally, "push @INC, $_" takes that variable that I just created, and stuffs it onto @INC, which is the array containing Perl's search path. If, after this point, I try to "use" or "require" a file, it is going to look in the currentl directory. Actually, on looking back, it makes more sense to unshift $INC, $_ since that will make it look in the current directory _first_ rather than last. But then, the line is 3 characters longer! The second statement above, upon careful examination, is the same line of code, except that I put the value into the variable $here, and just keep it around for reference. Hopefully, that was reasonably clear. Regular Expressions are the most powerful thing that is in Perl. It allows you to do a page and a half of explanation in one line. If you really want to know more about this, you need to get Mastering Regular Expressions from O'Reilly & Assoc, by Jeff Friedl Wonderful book. -- ################################################## # Rich Bowen # # Web Services Engineer - DataBeam Corporation # # rbowen@databeam.com # ##################################################