File Coverage

blib/lib/Win32/Symlinks.pm
Criterion Covered Total %
statement 10 71 14.0
branch 1 32 3.1
condition 0 9 0.0
subroutine 4 6 66.6
pod 0 1 0.0
total 15 119 12.6


line stmt bran cond sub pod time code
1             package Win32::Symlinks;
2            
3 2     2   70248 use 5.006;
  2         13  
4 2     2   13 use strict;
  2         4  
  2         41  
5 2     2   12 use warnings;
  2         4  
  2         1639  
6            
7             =encoding latin1
8            
9             =head1 NAME
10            
11             Win32::Symlinks - A maintained, working implementation of Perl symlink built in features for Windows.
12            
13             =head1 VERSION
14            
15             Version 0.08
16            
17             =cut
18            
19             BEGIN {
20 2     2   10 our $VERSION = '0.08';
21 2 50       345 if ($^O eq 'MSWin32') {
22 0           require XSLoader;
23 0           require Win32::Shortcut;
24 0           require File::Spec;
25 0           require File::Basename;
26            
27 0           XSLoader::load();
28 0           _override_link_test();
29            
30             *CORE::GLOBAL::readlink = sub ($) {
31 0           my $path = shift;
32 0           undef $Win32::Symlinks::Type;
33 0           $path = File::Spec->catdir(File::Spec->splitdir($path));
34 0 0         if ($path =~ /\.lnk$/i) {
35 0           my $sc = Win32::Shortcut->new;
36 0           $sc->Load($path);
37 0           my $target = $sc->{Path};
38 0           $sc->Close;
39 0 0         if ($target) {
40 0           $Win32::Symlinks::Type = 'SHORTCUT';
41 0           return $target;
42             }
43             }
44 0   0       my $cmd = $ENV{COMSPEC} || 'cmd.exe';
45 0           my $directory = File::Basename::dirname($path);
46 0           my $item = File::Basename::basename($path);
47 0           my @r = `"$cmd" /c dir /A:l "$directory" 2>&1`;
48 0           for my $i (@r) {
49 0 0         if ($i =~ m[<(JUNCTION|SYMLINK|SYMLINKD)>\s+(.*?)\s+\Q[\E([^\]]+)\Q]\E]) {
50 0           my ($type, $name, $target) = ($1, $2, $3);
51 0           for my $i ($type, $name, $target) {
52 0           $i =~ s[(^\s+|\s+$)][]g;
53             }
54 0 0         if ($name eq $item) {
55 0           $Win32::Symlinks::Type = $type;
56 0           return $target;
57             }
58             }
59             }
60 0           return;
61 0           };
62            
63             *CORE::GLOBAL::symlink = sub ($$) {
64 0           my ($old, $new) = (shift, shift);
65 0 0         return unless defined $old;
66 0 0         return unless defined $new;
67 0           $old = File::Spec->catdir(File::Spec->splitdir($old));
68 0           $new = File::Spec->catdir(File::Spec->splitdir($new));
69 0           my $r;
70 0 0         if (-d $old) {
71 0           $r = `mklink /d "$new" "$old" 2>&1`;
72             } else {
73 0           $r = `mklink "$new" "$old" 2>&1`;
74             }
75 0 0         return 1 if $r =~ /\Q<<===>>\E/;
76 0           return 0;
77 0           };
78            
79             *CORE::GLOBAL::unlink = sub ($) {
80 0           my $path = shift;
81 0 0         return unless defined $path;
82 0           $path = File::Spec->catdir(File::Spec->splitdir($path));
83 0   0       my $cmd = $ENV{COMSPEC} || 'cmd.exe';
84 0 0 0       if (_test_d($path) and l($path)) {
    0          
85 0           my $r = `"$cmd" /c rmdir "$path" 2>&1`;
86 0 0         return $r ? 0 : 1;
87             } elsif (l($path)) {
88 0           my $r = `"$cmd" /c del /Q "$path" 2>&1`;
89 0 0         return $r ? 0 : 1;
90             }
91 0           return CORE::unlink($path);
92 0           };
93             }
94             }
95            
96             sub l ($) {
97 0 0   0 0   return 1 if defined readlink($_[0]);
98 0           return 0;
99             }
100            
101             # We need this because some versions of Perl (seen in 5.18) return true for -f dir_symlink
102             # and false for -d dir_symlink. This breaks the unlink override.
103             sub _test_d {
104 0     0     my $path = shift;
105 0   0       my $cmd = $ENV{COMSPEC} || 'cmd.exe';
106 0           my $r = `"$cmd" /c cd "$path" 2>&1`;
107 0           $r =~ s/(^\s+|\s+$)//g;
108 0 0         return $r ? 0 : 1;
109             }
110            
111            
112             =head1 SYNOPSIS
113            
114             This module enables, on Windows, symlink related Perl features that don't work by default on Windows.
115            
116             Specifically, it enables the functionality that you would see on *nix OSes, for C<-l $filename>, C, C and C.
117            
118             This features have never properly been ported to Windows by the Perl development team. They were initially unimplemented due to the
119             limitations that Windows used to have prior to NTFS (e.g. when Windows used Fat32 as main file system).
120            
121             That situation has been different for at least two decades now. Yet, Perl continues to keep these functions unimplemented on Windows.
122            
123             The aim of this module is to allow Perl code to use C<-l $filename>, C, C and C seamlessly between *nix
124             and Windows. Just by using the module, it will do its best effort to make these functions work exactly the same and as they are
125             expected to work.
126            
127             The module doesn't do anything if it is run on a *nix machine, it defaults to the built in functions. But, by being present in your
128             code, you'll ensure these functions don't break when being executed in a Windows based Perl distribution.
129            
130             Perhaps a little code snippet.
131            
132             use Win32::Symlinks;
133            
134             # That's it. Now symlink, readlink, unlink and -l will work correctly when
135             # executed under Windows.
136            
137             # Also, you don't need to call it everywhere. Calling it once is enough.
138            
139            
140            
141             =head1 EXPORT
142            
143             Only when running under Windows, the built in functions C, C and C,
144             as well as the file test C<-l>, are overriden.
145            
146             If at some point you really need to make sure you are calling the built in function,
147             you should explicitly use the CORE prefix (e.g. C).
148            
149             When running on any OS that is *not* Windows, it will default to the built in
150             Perl functions. This module doesn't do anything on non Windows platforms, which
151             makes it perfect if you are working on a non Windows machine but want to make
152             sure your symlink related functions will not break under Windows.
153            
154             =head1 AUTHOR
155            
156             Francisco Zarabozo, C<< >>
157            
158             =head1 BUGS
159            
160             I'm sure there are many that I haven't been able to trigger. If you find a bug,
161             please don't hesitate to report it. I promise I will make an effort to have it
162             resolved ASAP.
163            
164             Take into account that this implementation can only work on NTFS file systems.
165             Symlinks are not implemented in file systems like FAT32. Windows has been using
166             NTFS since Windows 2000, but it used FAT32 for Windows 95, 98, Millenium, etc.
167            
168             Also, external devices like USB sticks, SD cards, etc., are generally formatted
169             with FAT32 or ExFAT. So, if you try to use these functions over such devices,
170             it will fail, unless you format them with the NTFS file system first.
171            
172             Please report any bugs or feature requests to C, or through
173             the web interface at L. I will be notified, and then you'll
174             automatically be notified of progress on your bug as I make changes.
175            
176            
177            
178            
179             =head1 SUPPORT
180            
181             You can find documentation for this module with the perldoc command.
182            
183             perldoc Win32::Symlinks
184            
185            
186             You can also look for information at:
187            
188             =over 4
189            
190             =item * RT: CPAN's request tracker (report bugs here)
191            
192             L
193            
194             =item * AnnoCPAN: Annotated CPAN documentation
195            
196             L
197            
198             =item * CPAN Ratings
199            
200             L
201            
202             =item * Search CPAN
203            
204             L
205            
206             =back
207            
208            
209             =head1 ACKNOWLEDGEMENTS
210            
211             A small part of C code was taken from the Win32_Links project from Jlevens,
212             which has a GNU v2.0 license and can be found at
213             L.
214            
215            
216             =head1 LICENSE AND COPYRIGHT
217            
218             This software is copyright (c) 2021 by Francisco Zarabozo.
219            
220             This is free software; you can redistribute it and/or modify it under
221             the same terms as the Perl 5 programming language system itself.
222            
223            
224             =cut
225            
226             1; # End of Win32::Symlinks