Mailing List Archive

Making %ENV even more magical
I posted this on clpm but have received no responses, so I wonder if it
made it to the real world.

I tried my hand at a module (see below) to help with the problem on
DOS/NT in which the Perl programmer must know the exact case spelling
of environment variables but no one else has to, i.e. $ENV{'path'},
$ENV{'Path'}, and $ENV{'PATH'} are 3 different environment variables in
Perl, but are one and the same to DOS and NT. WinEnv.pm creates a hash
that maps all case spellings of variables to the same variable, but it's
resisted all attempts to make it actually remap %ENV and do away with
having to use a different hash name.

I suspect that the answer is "you can't do that" because of %ENV's
magical and/or predefined status, but I'd like a more authoritative
source than my suspicions. If it's possible, could some more
knowledgeable person please show me the way, even if it's "study such
and such". BTW, http pointers are quite worthless to me: my net access
is behind a firewall that doesn't pass http. :-( Thanks.

----- cut -----
package WinEnv;

=head1 NAME

WinEnv - Perl module that redefines the magic %ENV hash for DOS-ish
systems so that environment variable keyword case is ignored.

=head1 DESCRIPTION

Perl maintains environment variables in a pseudo-associative-array
named %ENV. In the DOS, Windows 95, and Windows NT environments, the
system ignores environment variable case. Perl, however, doesn't, thus
"path", "Path", and "PATH" are all the same variable to the system,
but to Perl they're 3 different variables. This module eliminates case
distinctions in environment variables for the calling package when it
uses the specified hash (%WinENV by default) in place of the magic %ENV
hash. For example:

use WinEnv;

creates %WinENV for which $WinENV{'path'} is identical to $WinENV{'PATH'}
and retains the magic of %ENV.

use WinEnv 'myenv';

creates %myenv instead of %WinENV.

=head1 AUTHOR

Dave Wolfe <david_wolfe@risc.sps.mot.com>

=cut

my %envmap;

foreach (keys %ENV) {
$envmap{uc $_} = $_;
}

sub import {
my ($callpack) = caller(0);
my $pack = shift;
my @vars = @_ ? @_ : ('WinENV');

foreach (@vars) {
tie %{"${callpack}::$_"}, WinEnv, $_;
}
}

sub TIEHASH {
bless \($_[1]);
}

sub FETCH {
my $var = $envmap{uc $_[1]} || $_[1];
$ENV{$var};
}

sub STORE {
my $Var = uc $_[1];
$envmap{$Var} = $_[1] unless defined $envmap{$Var};
$ENV{$envmap{$Var}} = $_[2];
}

sub DELETE {
delete $ENV{$envmap{uc $_[1]}};
delete $envmap{uc $_[1]};
}

sub EXISTS {
exists $ENV{$envmap{uc $_[1]}};
}

sub FIRSTKEY {
my ($x) = keys %ENV;
each %ENV;
}

sub NEXTKEY {
each %ENV;
}

1;
----- cut -----
--
Dave Wolfe *Not a spokesman for Motorola* (512) 891-3246
Motorola MMTG 6501 Wm. Cannon Dr. W. OE112 Austin TX 78735-8598
Re: Making %ENV even more magical [ In reply to ]
would this enhance or imperil portability?

\--tom
Re: Making %ENV even more magical [ In reply to ]
> I tried my hand at a module (see below) to help with the problem on
> DOS/NT in which the Perl programmer must know the exact case spelling
> of environment variables but no one else has to, i.e. $ENV{'path'},
> $ENV{'Path'}, and $ENV{'PATH'} are 3 different environment variables in
> Perl, but are one and the same to DOS and NT. WinEnv.pm creates a hash
> that maps all case spellings of variables to the same variable, but it's
> resisted all attempts to make it actually remap %ENV and do away with
> having to use a different hash name.
>
> I suspect that the answer is "you can't do that" because of %ENV's
> magical and/or predefined status, but I'd like a more authoritative
> source than my suspicions. If it's possible, could some more
> knowledgeable person please show me the way, even if it's "study such
> and such". BTW, http pointers are quite worthless to me: my net access
> is behind a firewall that doesn't pass http. :-( Thanks.

A similar problem arose under VMS, and we chose to address it by mapping
%ENV keys to uppercase. I don't think you can appropriate %ENV easily,
because of its magicalness, but you can intercept retrievals from %ENV
in hv_fetch, and perform whatever mapping you'd like. It's a bit of
a hack, but it works. For an example, look at the code already
there in the #ifdef DYNAMIC_ENV_FETCH block; it's used by VMS to do
lookups on demand, since the "contents" of %ENV may change over the
life of a program.
Re: Making %ENV even more magical [ In reply to ]
[ Tom Christiansen writes: ]
>
> would this enhance or imperil portability?

My WinEnv.pm as written or making it have the same effect on %ENV
directly?

The latter would enhance portability, because Perl programs could then
use %ENV without caring about the nuances of the variables' case by just
using WinEnv.pm. Obviously it should be extended so that it would remap
%ENV only for DOS-ish systems and could then be used by all programs
desiring that level of portability.

Without having %ENV case-independent, NT Perl programs must use another
hash name, which makes them quite non-portable.

Although it would seem that knowing the exact case of environment
variables is necessary anyway, as it is for Unix, the fact that the
system API doesn't care means that other programs tend to be quite
whimsical about case, since, for them, it doesn't matter. As a result,
the obvious choice (at least to a kicking and screaming "convert"
from Unix :-) of $ENV{'PATH'} is undefined, since NT usually defines
$ENV{'Path'} instead (which is one and the same as far as NT is
concerned).

Does that explain the situation enough to address your concerns?

--
Dave Wolfe *Not a spokesman for Motorola* (512) 891-3246
Motorola MMTG 6501 Wm. Cannon Dr. W. OE112 Austin TX 78735-8598
Re: Making %ENV even more magical [ In reply to ]
On Tue, 21 Nov 1995 14:46:49 -0500 (EST), Charles Bailey <bailey@genetics.upenn.edu> wrote:
David Wolfe wrote:
>> I tried my hand at a module (see below) to help with the problem on
>> DOS/NT in which the Perl programmer must know the exact case spelling
>> of environment variables but no one else has to, i.e. $ENV{'path'},
>> $ENV{'Path'}, and $ENV{'PATH'} are 3 different environment variables in
>> Perl, but are one and the same to DOS and NT.

>A similar problem arose under VMS, and we chose to address it by mapping
>%ENV keys to uppercase. I don't think you can appropriate %ENV easily,
>because of its magicalness, but you can intercept retrievals from %ENV
>in hv_fetch, and perform whatever mapping you'd like. It's a bit of
>a hack, but it works. For an example, look at the code already
>there in the #ifdef DYNAMIC_ENV_FETCH block; it's used by VMS to do
>lookups on demand, since the "contents" of %ENV may change over the
>life of a program.

MacPerl also has case insensitive environment variables and also uses
DYNAMIC_ENV_FETCH. I'm not sure I understand your comment about mapping
%ENV keys to uppercase, though. Is this for writing only?

Matthias

-----
Matthias Neeracher <neeri@iis.ee.ethz.ch> http://err.ethz.ch/members/neeri.html
"I am not of Aryan extraction: that is Indo-Iranian; as far as I am aware
none of my ancestors spoke Hindustani, Persian, Gypsy, or any related
dialects." -- J.R.R. Tolkien
Re: Making %ENV even more magical [ In reply to ]
Tom Christiansen writes:
>
> would this enhance or imperil portability?
>
> \--tom
>

I see no problem with environment variables being case-sensitive under
OS/2. They are with pdksh as well, it is only the command processor
that converts names to uppercase. I think perl uses putenv and getenv,
so this behaviour shows that C supports case-sensitivity.

I do not think one should model perl after a broken system command
processor of DOS, OS/2 or NT.

Ilya
Re: Making %ENV even more magical [ In reply to ]
On Tue, 21 Nov 1995, David Wolfe wrote:

> package WinEnv;
>
> =head1 NAME
>
> WinEnv - Perl module that redefines the magic %ENV hash for DOS-ish
> systems so that environment variable keyword case is ignored.
> [...]
> use WinEnv;
>
> creates %WinENV for which $WinENV{'path'} is identical to $WinENV{'PATH'}
> and retains the magic of %ENV.
>
> use WinEnv 'myenv';
>
> creates %myenv instead of %WinENV.

I'd suggest calling it "%ENVi", to match "stricmp", and also as a cute (if
pointless) pun. But I'm not sure whether a forced-uppercase mechanism is
really what's needed. As others have pointed out, hacking the source code
to fix this isn't _that_ bad an idea.

--
Kenneth Albanowski (kjahds@kjahds.com, CIS: 70705,126)
Re: Making %ENV even more magical [ In reply to ]
[ Ilya Zakharevich writes: ]
>
> I see no problem with environment variables being case-sensitive under
> OS/2. They are with pdksh as well, it is only the command processor
> that converts names to uppercase. I think perl uses putenv and getenv,
> so this behaviour shows that C supports case-sensitivity.
>
> I do not think one should model perl after a broken system command
> processor of DOS, OS/2 or NT.

While I have the utmost respect for you, Ilya, I disagree with your
assumptions and therefore with your conclusion. It goes much deeper
than the command processors. The getenv and putenv in the C library do
*not* support case-sensitivity, at least not on NT. A test program will
retrieve the same value for 'PATH', 'path', and 'pATH' (the spelling
displayed by the command processor and the Control Panel System applet
was 'Path'). The problem lies in the fact that Perl's %ENV supports
case-sensitivity and the system doesn't, i.e. Perl shows all 3 of the
previous example spellings as undefined. A further example:

DB<1> p $ENV{logname}

DB<2> system 'echo %logname%'
dwolfe

Maybe you're being confused by *setting* a given spelling of an
environment variable. The case-independent spelling of that variable is
set in the external environment, but it's still case-dependent in %ENV.
E.g.:

DB<1> p defined $ENV{zz} ? "defined" : "undefined"
undefined
DB<2> p defined $ENV{ZZ} ? "defined" : "undefined"
undefined
DB<3> system 'echo %zz%'
%zz%

DB<4> system 'echo %ZZ%'
%ZZ%

DB<5> $ENV{zz}='zot'

DB<6> !1
p defined $ENV{zz} ? "defined" : "undefined"
defined
DB<2> !2
p defined $ENV{ZZ} ? "defined" : "undefined"
undefined
DB<3> !3
system 'echo %zz%'
zot

DB<4> !4
system 'echo %ZZ%'
zot

In other words, defining 'zz' in %ENV only defines 'zz' in %ENV, but
defines both 'zz' and 'ZZ' (as well as 'Zz' and 'zZ') externally.
Therein lies the problem: the system doesn't see any difference between
case spellings but %ENV *does*. It's not a matter of modeling Perl after
a broken system command processor, but rather the reality of working
with a brosystem design choice. ;-)

--
Dave Wolfe *Not a spokesman for Motorola* (512) 891-3246
Motorola MMTG 6501 Wm. Cannon Dr. W. OE112 Austin TX 78735-8598
Re: Making %ENV even more magical [ In reply to ]
Matthias Neeracher writes:
: MacPerl also has case insensitive environment variables and also uses
: DYNAMIC_ENV_FETCH. I'm not sure I understand your comment about mapping
: %ENV keys to uppercase, though. Is this for writing only?

The system services which handle logical name creation and translation
under VMS are case sensitive, but the command-line interpreter and file
system are not, in general. Specifically, unless one explicitly
quotes an argument to a command, it is upcased. As a result, it is
convention under VMS that logical names are specified in uppercase.
In order to preserve the "feel" of this convention, keys of %ENV
are upcased in Perl before being passed to the appropriate system
service. The obvious drawback is that one cannot specify a mixed-
case logical name. Since this is really a rare occasion, we plan
to address that in a VMS-specific extension for logical name
handling, which is necessary in any case because the flat
namespace provided by %ENV covers only a portion of the
capacity of VMS logical names.

One other consequence of this approach is that the same value
may be associated with distinct keys of %ENV (e.g. $ENV{'FOO'},
$ENV{'Foo'} and $ENV{'foo'} will all be instantiated on demand
by (essentially) sys$trnlnm('FOO'). Since the mapping occurs
at the level of translation, however, not in hv_fetch(), and
since the values are instantiated in demand, only the ones
acutally used in the Perl program are cached in %ENV.
This means that iteration over %ENV will only return keys
which have actually been used -- if one looked up $ENV{FOO},
then C<keys %ENV} would contain 'FOO', but not 'foo', 'Foo',
etc. Again, I don't see this as a real problem, especially
since iteration over %ENV is broken by DYNAMIC_ENV_FETCH
at a more basic level -- one only sees the elements one
has used. Eventually, I think this can be fixed by supplying
the appropriate magic routines for the %ENV hash.

Regards,
Charles Bailey bailey@genetics.upenn.edu
Re: Making %ENV even more magical [ In reply to ]
David Wolfe writes:
>
> [ Ilya Zakharevich writes: ]
> >
> > I see no problem with environment variables being case-sensitive under
> > OS/2. They are with pdksh as well, it is only the command processor
> > that converts names to uppercase. I think perl uses putenv and getenv,
> > so this behaviour shows that C supports case-sensitivity.
> >
> > I do not think one should model perl after a broken system command
> > processor of DOS, OS/2 or NT.
>
> While I have the utmost respect for you, Ilya, I disagree with your
> assumptions and therefore with your conclusion.

You did not need to flatter me, I already reconsidered my position the
moment I saw that $ENV{Path} is needed on NT. Btw, what you wrote only
supports my position:

> It goes much deeper
> than the command processors. The getenv and putenv in the C library do
> *not* support case-sensitivity, at least not on NT. A test program will
> retrieve the same value for 'PATH', 'path', and 'pATH' (the spelling
> displayed by the command processor and the Control Panel System applet
> was 'Path').

I'm not sure your assessment of the situation is correct. Whatever you
do below uses command processor. Try this or a C program of your
choice:

Enter h or `h h' for help.

main::(-e:1): 0
DB<1> $ENV{zzz} = TTTT

DB<2> x $ENV{zzz}
0 TTTT
DB<3> x $ENV{ZZZ}
0 undef
DB<4> system 'perl', '-le', 'print $ENV{zzz}'
TTTT

DB<5> system 'perl', '-le', 'print $ENV{ZZZ}'


My reading is that command processor uppercases anything in %...% or
set ...=blah. CRT does not. Well, I did it in OS/2, but I see no other
way to explain what putenv() _may_ do anywhere.

Anyway, what you write contradict the fact one needs $ENV{Path} on
NT. What gives?

Ilya
Re: Making %ENV even more magical [ In reply to ]
[ Ilya Zakharevich writes: ]
>
> You did not need to flatter me, [...]

I just thought my statement might be taken as offensive and wanted to
set it in the context of reasoned disagreement so that it wouldn't be
interpreted as inflammatory.

> Btw, what you wrote only supports my position:
>
> > It goes much deeper
> > than the command processors. The getenv and putenv in the C library do
> > *not* support case-sensitivity, at least not on NT. A test program will
^^^^^^^^^^^^^^
> > retrieve the same value for 'PATH', 'path', and 'pATH' (the spelling
> > displayed by the command processor and the Control Panel System applet
> > was 'Path').

My C test program didn't involve any command processor.

> I'm not sure your assessment of the situation is correct. Whatever you
> do below uses command processor. Try this or a C program of your
> choice:

Already done, with the behavior noted initially.

[ Perl example on OS/2 deleted ]
> My reading is that command processor uppercases anything in %...% or
> set ...=blah. CRT does not. Well, I did it in OS/2, but I see no other
> way to explain what putenv() _may_ do anywhere.
>
> Anyway, what you write contradict the fact one needs $ENV{Path} on
> NT. What gives?

It does appear my Perl example was flawed and I can't get to an NT
system at the moment to try your example on NT, but my C program
contradicts your OS/2 results, so I suspect that NT (and DOS?) have
little in common with OS/2 in this regard.

In any event, I'm told the whole issue is moot since the next release of
NT Perl will do case smashing on %ENV keys.

--
Dave Wolfe *Not a spokesman for Motorola* (512) 891-3246
Motorola MMTG 6501 Wm. Cannon Dr. W. OE112 Austin TX 78735-8598