# Windows.pm: Windows Methods for RDA::Object::Rda package RDA::Local::Windows; # $Id: Windows.pm,v 1.24 2015/06/11 08:00:04 RDA Exp $ # ARCS: $Header: /home/cvs/cvs/RDA_8/src/scripting/lib/RDA/Local/Windows.pm,v 1.24 2015/06/11 08:00:04 RDA Exp $ # # Change History # 20150609 MSC Add the can_spawn method. =head1 NAME RDA::Local::Windows - Windows Methods for RDA::Object::Rda =head1 SYNOPSIS require RDA::Local::Windows; =head1 DESCRIPTION See L and L. This package overrides the implementation of these methods, not the semantics. The following methods are available: =cut use strict; BEGIN { use Exporter; use RDA::Text qw(get_string); } # Define the global public variables use vars qw($CAN_PRC $CAN_SYS $CAN_WIN $STRINGS $VERSION @EXPORT_OK @ISA); $VERSION = sprintf('%d.%02d', q$Revision: 1.24 $ =~ /(\d+)\.(\d+)/); @EXPORT_OK = qw($CAN_PRC $CAN_SYS $CAN_WIN); require RDA::Local::Unix; @ISA = qw(RDA::Local::Unix Exporter); # Define the spawn capabilities $CAN_PRC = 4; # Can Win32::Process $CAN_SYS = 1; # Can system 1,... $CAN_WIN = 2; # Can Win32 my $SPAWN = undef; # Define the global private constants # Define the global private variables # Report the package version sub Version { return $VERSION; } =head2 Sas_bat([$path])> This method adds a C<.bat> extension to the specified path. =cut sub as_bat { my ($slf, $pth) = @_; return defined($pth) ? "$pth.bat" : '.bat'; } =head2 Sas_cmd([$path])> This method adds a C<.cmd> extension to the specified path. =cut sub as_cmd { my ($slf, $pth) = @_; return defined($pth) ? "$pth.cmd" : '.cmd'; } =head2 Sas_exe([$path])> This method adds a C<.exe> extension to the specified path. =cut sub as_exe { my ($slf, $pth) = @_; return defined($pth) ? "$pth.exe" : '.exe'; } =head2 Sarg_dir([$dir...,]$dir)> This method performs a C and quotes the result. =cut sub arg_dir { my $slf = shift; return $slf->quote($slf->cat_dir(@_)); } =head2 Sarg_file([$dir...,]$file)> This method performs a C and quotes the result. =cut sub arg_file { my $slf = shift; return $slf->quote($slf->cat_file(@_)); } =head2 Sbasename($file[,@suf])> This method extracts the base name of the file specification and removes the suffix when it belongs to the suffix list. It matches each element of this list as a string against the end of the name. =cut sub basename { my ($slf, $fil, @suf) = @_; my ($bas, $dir, $suf); return unless defined($fil); # Parse the file $fil =~ s/([^:])[\\\/]*\z/$1/; ($dir, $bas, $suf) = $slf->parse_path($fil, @suf); # Adjust the base name return length($bas) ? $bas : defined($suf) && length($suf) ? $suf : $dir; } =head2 Scan_spawn> This method indicates whether it is possible to launch background processes. =cut sub can_spawn { my ($slf) = @_; # Check the capabilities on the first use unless (defined($SPAWN)) { $SPAWN = 0; if (ref($slf)) { return $SPAWN if $slf->{'B_NO_SPAWN'}; $SPAWN += $CAN_SYS unless $slf->{'B_TAINTED'}; } eval 'require Win32'; $SPAWN += $CAN_WIN unless $@; eval 'require Win32::Process'; $SPAWN += $CAN_PRC unless $@; } # Return the capabilities return $SPAWN; } =head2 Sclean_native($path)> This method performs a logical cleanup of a path. It removes successive backslashes and successive C<\.>. It attempts to further reduce the number of C<\..> present in the path. It returns a native path. =cut sub clean_native { return _clean_path(0, @_); } =head2 Sclean_path($path)> This method performs a logical cleanup of a path. It removes successive slashes and successive C<\.>. It attempts to further reduce the number of C<\..> present in the path. =cut sub clean_path { return _clean_path(1, @_); } sub _clean_path { my ($flg, $slf, $pth) = @_; my ($vol); # Assemble the fragments if (ref($pth) eq 'ARRAY') { foreach my $itm (@{$pth}) { $itm =~ s{^"(.*)"$}{$1}s; } $pth = join(q{/}, @{$pth}); } return unless $flg || defined($pth = $slf->is_path($pth)); # Extract the volume (multiple conditions required for old Perl versions) $pth =~ s{/}{\\}g; # x/x -> x\x if ($pth =~ s{^(\\\\[^\\]+)(\\|\z)}{\\}s) { $vol = $1; } elsif ($pth =~ s{^([a-z]:)}{}is) { $vol = uc($1); } else { $vol = q{}; } # Clean the path $pth =~ s{\\+}{\\}g; # x\\\\x -> x\x $pth =~ s{(\\\.)+(\\|\z)}{\\}g; # x\.\.\x -> x\x $pth =~ s{^(\.\\)+(.)}{$2}s; # .\x -> x $pth =~ s{^(\\\.\.)+(\\|\z)}{\\}s; # \..\.. -> \ $pth =~ s{(.)\\$}{$1}; # x\ -> x if ($pth =~ m{\\\.\.(\\|\z)}) { my ($val, @tbl); foreach my $itm (split(/\\/, $pth, -1)) { if ($itm eq q{..} && @tbl) { $itm = pop(@tbl); push(@tbl, $itm) if $itm eq q{}; push(@tbl, $itm, q{..}) if $itm eq q{..}; } else { push(@tbl, $itm); } } $pth = ((scalar @tbl) > 1) ? join(q{\\}, @tbl) : !defined($val = pop(@tbl)) ? q{.} : ($val eq q{}) ? q{\\} : $val; } return $vol.$pth; } =head2 Sdev_null> This method returns a string representation of the null device. =cut sub dev_null { return 'nul'; } =head2 Sdev_tty> This method returns a string representation of the terminal device. =cut sub dev_tty { return 'con'; } =head2 Sfind_path($path,$command[,$flag])> This method explores the path to find where a command is located. When the command is found, it returns a full path name. Otherwise, it returns an undefined variable. It only considers files or symbolic links in its search. Unless the flag is set, the file path is quoted as required by a command shell. =cut sub find_path { my ($slf, $pth, $cmd, $flg) = @_; my ($fil, $sep); if ($cmd && $pth) { unless (ref($pth) eq 'ARRAY') { $sep = $slf->get_separator; $pth = [split(/$sep/, $pth)]; } foreach my $dir (@{$pth}) { next unless opendir(DIR, $dir); foreach my $nam (readdir(DIR)) { next unless $nam =~ m/^(\Q$cmd\E(\.(bat|cmd|exe))?)$/i; $fil = $slf->cat_file($dir, $1); if (stat($fil) && (-f $fil || -l $fil)) { closedir(DIR); return $flg ? $fil : $slf->quote($fil) } } closedir(DIR); } } return; } =head2 Sget_separator> This method returns the character used as separator. =cut sub get_separator { return q{;}; } =head2 Shas_short> This method indicates whether the files and directories can have short names. =cut sub has_short { return 1; } =head2 Sis_absolute($path)> This method indicates whether the argument is an absolute path. =cut sub is_absolute { my ($slf, $pth) = @_; return defined($pth) ? scalar ($pth =~ m{^([a-z]:)?[\\/]}is) : 0; } =head2 Sis_root_dir($path)> This method indicates whether the path represents a root directory. It assumes that the provided path is already cleaned. =cut sub is_root_dir { my ($slf, $pth) = @_; return 0 unless defined($pth); $pth =~ s{^[a-z]:}{}is; return $pth eq q{\\}; } =head2 Sis_unix> This method returns a true value when the operating system belongs to the UNIX family. =cut sub is_unix { return 0; } =head2 Sis_windows> This method returns a true value when the operating system belongs to the Windows family. =cut sub is_windows { return 1; } =head2 Skill_child($pid)> This method kills a child process tree when possible. =cut sub kill_child { my ($slf, $pid) = @_; return unless defined($pid); # Do not kill the process tree on emulated forks unless ($pid < 0) ## no critic (Unless) { # On recent Perl versions, kill the process tree return kill(-9, $pid) unless $] < 5.008009; ## no critic (Number,Unless) # Try to kill the process tree with taskkill eval {`taskkill /F /T /pid $pid 2>NUL`}; } # Otherwise, kill the process return kill(9, $pid); } =head2 Slc_path($path)> This method converts a path to lowercase but the volume in uppercase. =cut sub lc_path { my ($slf, $str) = @_; return ($str =~ m/^([A-Za-z]:)(.*)$/) ? uc($1).lc($2) : lc($str); } =head2 Snative($path[,$flag])> This method converts the path to its Windows representation. When the flag is set, it converts the path to lowercase. =cut sub native { my ($slf, $pth, $flg) = @_; return unless defined($pth); ($pth) = $pth =~ m/^([^"]+)$/ or $pth =~ m/^"([^"]+)"$/ or get_string('BAD_PATH', $pth); $pth =~ s{/}{\\}g; $pth = lc($pth) if $flg; $pth =~ s{^([a-z]:)}{\U$1\Q}; return $pth; } =head2 Sparse_path($path[,@suf])> This method divides a file path into the directories, its file name, and optionally the file suffix. The directory part contains everything up to and including the last directory separator in the $path including the volume, when applicable. The remainder of the path is the file name. =cut sub parse_path { my ($slf, $pth, @suf) = @_; my ($bas, $dir, $ext, @ext); return () unless defined($pth); # Parse the file ($dir, $bas) = ($pth =~ /^((?:.*[:\\\/])?)(.*)/s); if (defined($dir)) { $dir =~ s/([^:])[\\\/]*\z/$1/; } else { $dir = q{.}; } # Extract the file suffix if (@suf) { $ext = q{}; foreach my $suf (@suf) { $ext = $1.$ext if defined($suf) && $bas =~ s/(\Q$suf\E)\z//is; } @ext = ($ext) unless $ext eq q{}; } # Return the parsing result return ($dir, $bas, @ext); } =head2 Squote($string[,$flag])> This method encodes a string to be considered as a single argument by a command shell. Unless the flag is set, variable substitution is disabled. =cut sub quote { my ($slf, $str, $flg) = @_; return unless defined($str); return q{""} unless length($str); die get_string('BAD_STRING', $str) unless $str =~ m/^([\001-\377]+)$/; $str = $1; return $str unless $str =~ m/[\000-\037\s\&\(\)\[\]\{\}\|\<\>\^\=\!\"\'\`\+\,\~\*\?\%]/; $str =~ s{"}{"^""}g; $str =~ s{\%}{"^\%"}g unless $flg; return q{"}.$str.q{"}; } =head2 Squote2($string[,$flag])> This method is similar to the C method but the result does not contain the leading and trailing quotation marks. =cut sub quote2 { my ($slf, $str, $flg) = @_; return unless defined($str); return q{} unless length($str); die get_string('BAD_STRING', $str) unless $str =~ m/^([\001-\377]+)$/; $str = $1; return $str unless $str =~ m/[\000-\037\s\&\(\)\[\]\{\}\|\<\>\^\=\!\"\'\`\+\,\~\*\?\%]/; $str =~ s{"}{"^""}g; $str =~ s{\%}{"^\%"}g unless $flg; return $str; } =head2 Sshort($path[,$flag])> This method converts the path to its native representation using only short names. When the flag is set, it converts the path to lowercase. =cut sub short { my ($slf, $pth, $flg) = @_; my ($res); return unless defined($pth); ($pth) = $pth =~ m/^([^"]+)$/ or $pth =~ m/^"([^"]+)"$/ or get_string('BAD_PATH', $pth); $pth =~ s{^([a-z]:)}{\U$1\Q}; $res = $pth; $res =~ s{[\/\\]}{\\\\}g; ($res) = `cmd /q /c "FOR %D IN (\042$res\042) DO ECHO %~sD"`; $res =~ m/^(.*?)[\n\r]*$/; $res = $flg ? lc_path($slf, $1) : $1; ## no critic (Capture) $slf->{'_sht'}->{$pth} = $res if ref($slf) eq 'RDA::Object::Rda' && $slf->is_absolute($pth); return $res; } =head2 Ssplit_dir($path)> This method returns the list of directories contained in the specified path. The first element will contain the volume information. It returns an empty list when the path is missing. =cut sub split_dir { my ($slf, $pth) = @_; my ($vol, @dir); if (defined($pth)) { ($vol, $pth) = $slf->split_volume($pth); @dir = split(/\\/, $pth, -1); $dir[0] = $vol.$dir[0]; } return @dir; } =head2 Ssplit_volume($path)> This method separates the volume from the other path information. It returns an empty list when the path is missing. =cut sub split_volume { my ($slf, $pth) = @_; my ($vol); return () unless defined($pth); $pth =~ s{/}{\\}g; $vol = ($pth =~ s{^(\\\\[^\\]+)\\?\z}{\\}s) ? $1 : ($pth =~ s{^(\\\\[^\\]+\\)}{}s) ? $1 : ($pth =~ s{^([a-z]:)}{}is) ? uc($1) : q{}; return ($vol, ($pth eq q{}) ? q{.} : $pth); } =head2 Sunquote($string)> This method removes the quoting characters from a string. =cut sub unquote { my ($slf, $str) = @_; my ($buf); if (defined($str)) { for ($buf = q{} ;;) ## no critic (For) { return $buf.$str unless $str =~ s/\A(.*?)(\"|\^.)//s; $buf .= $1; if ($2 eq q{"}) { return $buf.$str unless $str =~ s/\A(.*?)\"//s; $buf .= $1; } else { $buf .= substr($2, 1); } } } return; } # --- Auxiliary routines ------------------------------------------------------ # Get uname information sub sys_uname { my ($slf) = @_; my ($str, $sys); # Try to get it from perl eval { require POSIX; $sys = [POSIX::uname()]; ## no critic (Call) }; return $sys unless $@; # Try to emulate it $sys = [q{?}, q{?}, q{?}, q{?}, q{?}]; ($str) = `echo exit | cmd`; if ($str =~ m/^(.*) \[Version\s+(\d+\.\d+)\.([^]]*)\]/) { ($sys->[0], $sys->[2], $sys->[3]) = ($1, $2, $3); } elsif ($str =~ m/^Microsoft\(R\) Windows NT\(TM\)/i) { $sys->[0] = 'Microsoft Windows NT'; } $sys->[1] = $slf->get_node; $sys->[4] = $str if defined($str = $slf->get_env('PROCESSOR_ARCHITECTURE')); return $sys; } 1; __END__ =head1 SEE ALSO L, L, L, L =head1 COPYRIGHT NOTICE Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. =head1 TRADEMARK NOTICE Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners. =cut