How can I optionally use Win32::Console and its co

2019-09-18 08:26发布

问题:

To start with, I'm working Perl v5.8.4 and I DO NOT have the ability to upgrade Perl or install Term::ReadKey or IO::Prompt (or really anything outside of what is in Core), so please take that into consideration when answering/commenting.

I'm trying to write a completely self contained Perl script that (among other things) prompts for a password. It needs to be cross platform compatible between Windows, AIX, and Solaris. I don't want it to echo the password as it is typed. Here's what I have:

BEGIN {
    if ($^O eq 'MSWin32') {
        require Win32::Console; 
        Win32::Console->import();
    }
}
sub get_password {
    print "Enter password: ";
    my $pass = '';
    # Change terminal settings to not display password
    if ($os eq 'MSWin32') {
        my $stdin = new Win32::Console STD_INPUT_HANDLE;
        my $orig_mode = $stdin->Mode();
        $stdin->Mode(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | +ENABLE_MOUSE_INPUT);
        chomp($pass = <STDIN>);
        $stdin->Mode($orig_mode);
    }
    else {
        system('stty', '-echo');
        chomp($password = <STDIN>);
        system('stty', 'echo');
    }
    print "\n";
    return $pass;
}

This works perfectly fine on all platforms (assuming I don't use strict), however, the 4 constants used in the Win32 block throw errors against strict subs on Unix:

Bareword "STD_INPUT_HANDLE" not allowed while "strict subs" in use at script.pl line 488.
Bareword "ENABLE_LINE_INPUT" not allowed while "strict subs" in use at script.pl line 490.
Bareword "ENABLE_PROCESSED_INPUT" not allowed while "strict subs" in use at script.pl line 490.
Bareword "ENABLE_MOUSE_INPUT" not allowed while "strict subs" in use at script.pl line 490.

I can't for the life of me figure out how to make both Windows and Unix be happy with those 4 constants. If I try to define them in a Unix only block, the Windows compiler tells me that I'm redefining them.

Can I fix this? Or perhaps do it in another way? Thanks in advance for the help.

回答1:

You must use parens for subroutine calls to subroutines you haven't declared. So either add parens by changing

STD_INPUT_HANDLE
ENABLE_LINE_INPUT
...

to

STD_INPUT_HANDLE()
ENABLE_LINE_INPUT()
...

or declare the subroutines when they wouldn't be declared by Win32::Console by changing

    if ($^O eq 'MSWin32') {
        require Win32::Console; 
        Win32::Console->import();
    }

to

    if ($^O eq 'MSWin32') {
        require Win32::Console; 
        Win32::Console->import();
    } else {
        eval <<'__EOI__'.';1' or die $@;
            sub STD_INPUT_HANDLE  { die }
            sub ENABLE_LINE_INPUT { die }
            ...
__EOI__
    }

A cleaner approach would be to move the OS-specific code into separate modules.

BEGIN {
    my $mod = $^O eq 'MSWin32' ? 'My::IO::Win32' : 'My::IO::Default';
    eval "require $mod" or die $@;
    $mod->import(qw( get_password ));
}


回答2:

You can put the Unix and Win32 versions of get_password in two separate modules, say, My::Input::Unix and My::Input::Win32, respectively, and then require the appropriate one depending on platform. Therefore, the Win32 version would not even be compiled on a Unix machine, avoiding the undefined constants etc.