File Coverage

blib/lib/Lchmod.pm
Criterion Covered Total %
statement 49 50 98.0
branch 17 20 85.0
condition n/a
subroutine 11 11 100.0
pod 1 1 100.0
total 78 82 95.1


line stmt bran cond sub pod time code
1             package Lchmod;
2              
3 2     2   20575 use strict;
  2         3  
  2         73  
4 2     2   9 use warnings;
  2         3  
  2         55  
5 2     2   1117 use FFI::Me;
  2         19335  
  2         13  
6              
7             $Lchmod::VERSION = '0.02';
8              
9             my $LCHMOD_AVAILABLE = 0;
10 16 50   16   972 sub LCHMOD_AVAILABLE { return 1 if $LCHMOD_AVAILABLE; return; }
  0         0  
11              
12             sub import {
13 5     5   571 shift;
14 5         10 my $caller = caller;
15              
16 5 100       15 if (@_) {
17              
18             # TODO ?: warn if @_ contains someting other than lchmod or LCHMOD_AVAILABLE
19              
20 2     2   204 no strict 'refs'; ## no critic
  2         4  
  2         254  
21 3 100       14 *{ $caller . "::lchmod" } = \&lchmod if grep m/^lchmod$/, @_;
  2         12  
22 3 100       49 *{ $caller . "::LCHMOD_AVAILABLE" } = \&LCHMOD_AVAILABLE if grep m/^LCHMOD_AVAILABLE$/, @_;
  2         1323  
23             }
24             else {
25 2     2   12 no strict 'refs'; ## no critic
  2         3  
  2         891  
26 2         3 *{ $caller . "::lchmod" } = \&lchmod;
  2         54  
27             }
28             }
29              
30             {
31             local $@;
32             eval { # 1st: try current process
33             ffi _sys_lchmod => (
34             rv => ffi::int,
35             arg => [ ffi::str, ffi::int ],
36             sym => 'lchmod',
37             );
38             };
39             $LCHMOD_AVAILABLE = 1 if !$@;
40             }
41              
42             # ? needed ?
43             # if (!$LCHMOD_AVAILABLE) {
44             # local $@;
45             # eval { # 2nd: try explicit libc
46             # ffi _sys_lchmod => (
47             # lib => $^O eq 'darwin' ? 'libc.dylib' : 'libc.so', # or whatever
48             # rv => ffi::int,
49             # arg => [ffi::str,ffi::int],
50             # sym => 'lchmod',
51             # );
52             # };
53             # $LCHMOD_AVAILABLE = 1 if !$@;
54             # }
55              
56             sub lchmod {
57 14     14 1 68035 my ( $mode, @files ) = @_;
58              
59 14 100       49 if ( !LCHMOD_AVAILABLE() ) {
60 1         6 $! = _get_errno_func_not_impl(); # ENOSYS
61 1         4 return undef; ## no critic
62             }
63              
64 13         20 my $count = 0;
65 13         66 my $normalized_mode = sprintf( "%04o", $mode & 07777 ); ## no critic
66              
67 13         30 for my $path (@files) {
68 17 100       388 if ( -l $path ) {
69 6         38 _sys_lchmod( $path, oct($normalized_mode) );
70             }
71             else {
72 11         312 chmod( oct($normalized_mode), $path );
73             }
74              
75 17         345 my $current_mode = ( lstat($path) )[2];
76 17 100       55 next if !$current_mode;
77              
78 15         62 $current_mode = sprintf( "%04o", $current_mode & 07777 ); ## no critic
79 15 100       63 $count++ if $current_mode eq $normalized_mode;
80             }
81              
82 13         91 return $count;
83             }
84              
85             sub _get_errno_func_not_impl {
86              
87             # we don't want to load POSIX but if its there we want to use it
88             # CONSTANTs are weird when not defined so we have to:
89              
90 1     1   3 local $^W = 0;
91 2     2   14 no warnings;
  2         2  
  2         107  
92 2     2   12 no strict; ## no critic
  2         3  
  2         209  
93 1         2 my $posix = POSIX::ENOSYS;
94             return
95 1 50       8 $posix ne 'POSIX::ENOSYS' ? POSIX::ENOSYS
    50          
96             : $^O =~ /linux/i ? 38
97             : 78;
98             }
99              
100             1;
101              
102             __END__