[ Tech stuff | NT | Perl on NT ]

In which it is revealed that IIS appears to have a rather broken idea of what the current working director (cwd) actually is

Note that this problem has gone away with IIS4. Please don't take that as an endorsement of IIS4 - Rich

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                           #
##################################################