Filename | /usr/share/perl5/Path/Tiny.pm |
Statements | Executed 1697 statements in 43.6ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
2 | 1 | 1 | 14.4ms | 14.4ms | CORE:read (opcode) | Path::Tiny::
35 | 5 | 2 | 9.17ms | 9.38ms | path | Path::Tiny::
1 | 1 | 1 | 4.26ms | 5.44ms | BEGIN@14 | Path::Tiny::
19 | 3 | 1 | 2.08ms | 3.69ms | filehandle | Path::Tiny::
1 | 1 | 1 | 1.99ms | 7.42ms | BEGIN@13 | Path::Tiny::
17 | 2 | 1 | 1.73ms | 1.73ms | CORE:close (opcode) | Path::Tiny::
16 | 1 | 1 | 1.24ms | 16.7ms | spew | Path::Tiny::
1 | 1 | 1 | 1.22ms | 1.32ms | BEGIN@29 | Path::Tiny::
17 | 2 | 1 | 1.22ms | 1.22ms | CORE:print (opcode) | Path::Tiny::
16 | 2 | 1 | 581µs | 18.1ms | spew_utf8 | Path::Tiny::
16 | 1 | 1 | 499µs | 499µs | CORE:sysopen (opcode) | Path::Tiny::
16 | 1 | 1 | 488µs | 510µs | CORE:rename (opcode) | Path::Tiny::
1 | 1 | 1 | 346µs | 748µs | _check_UU | Path::Tiny::
39 | 5 | 1 | 310µs | 310µs | _get_args | Path::Tiny::
16 | 1 | 1 | 283µs | 283µs | CORE:truncate (opcode) | Path::Tiny::
16 | 1 | 1 | 262µs | 355µs | _resolve_symlinks | Path::Tiny::
19 | 1 | 1 | 240µs | 240µs | CORE:flock (opcode) | Path::Tiny::
16 | 1 | 1 | 184µs | 695µs | move | Path::Tiny::
2 | 1 | 1 | 156µs | 14.9ms | slurp | Path::Tiny::
3 | 1 | 1 | 131µs | 131µs | CORE:open (opcode) | Path::Tiny::
51 | 2 | 1 | 119µs | 119µs | CORE:subst (opcode) | Path::Tiny::
35 | 1 | 1 | 105µs | 105µs | _is_root | Path::Tiny::
32 | 2 | 1 | 96µs | 96µs | __ANON__[:30] | Path::Tiny::
16 | 1 | 1 | 93µs | 93µs | CORE:ftlink (opcode) | Path::Tiny::
32 | 2 | 1 | 73µs | 73µs | CORE:binmode (opcode) | Path::Tiny::
2 | 2 | 1 | 55µs | 55µs | CORE:regcomp (opcode) | Path::Tiny::
1 | 1 | 1 | 36µs | 702µs | append | Path::Tiny::
1 | 1 | 1 | 32µs | 750µs | append_utf8 | Path::Tiny::
35 | 1 | 1 | 26µs | 26µs | CORE:match (opcode) | Path::Tiny::
2 | 1 | 1 | 16µs | 16µs | slurp_raw | Path::Tiny::
1 | 1 | 1 | 12µs | 142µs | BEGIN@19 | Path::Tiny::
1 | 1 | 1 | 12µs | 12µs | BEGIN@1 | RBM::
1 | 1 | 1 | 12µs | 23µs | BEGIN@12 | Path::Tiny::
1 | 1 | 1 | 12µs | 38µs | BEGIN@1415 | Path::Tiny::
1 | 1 | 1 | 11µs | 44µs | BEGIN@2133 | Path::Tiny::Error::
6 | 6 | 1 | 7µs | 7µs | CORE:qr (opcode) | Path::Tiny::
1 | 1 | 1 | 6µs | 15µs | BEGIN@11 | Path::Tiny::
1 | 1 | 1 | 6µs | 22µs | BEGIN@38 | Path::Tiny::
1 | 1 | 1 | 6µs | 33µs | BEGIN@132 | flock::
1 | 1 | 1 | 6µs | 7µs | BEGIN@2 | RBM::
1 | 1 | 1 | 5µs | 5µs | BEGIN@94 | Path::Tiny::
1 | 1 | 1 | 4µs | 16µs | BEGIN@3.1 | RBM::
2 | 1 | 1 | 3µs | 3µs | CORE:ftsize (opcode) | Path::Tiny::
0 | 0 | 0 | 0s | 0s | __ANON__[:2133] | Path::Tiny::Error::
0 | 0 | 0 | 0s | 0s | throw | Path::Tiny::Error::
0 | 0 | 0 | 0s | 0s | FREEZE | Path::Tiny::
0 | 0 | 0 | 0s | 0s | THAW | Path::Tiny::
0 | 0 | 0 | 0s | 0s | __ANON__[:1214] | Path::Tiny::
0 | 0 | 0 | 0s | 0s | __ANON__[:1425] | Path::Tiny::
0 | 0 | 0 | 0s | 0s | __ANON__[:1431] | Path::Tiny::
0 | 0 | 0 | 0s | 0s | __ANON__[:1437] | Path::Tiny::
0 | 0 | 0 | 0s | 0s | __ANON__[:1532] | Path::Tiny::
0 | 0 | 0 | 0s | 0s | __ANON__[:95] | Path::Tiny::
0 | 0 | 0 | 0s | 0s | _check_PU | Path::Tiny::
0 | 0 | 0 | 0s | 0s | _just_filepath | Path::Tiny::
0 | 0 | 0 | 0s | 0s | _non_empty | Path::Tiny::
0 | 0 | 0 | 0s | 0s | _parse_file_temp_args | Path::Tiny::
0 | 0 | 0 | 0s | 0s | _resolve_between | Path::Tiny::
0 | 0 | 0 | 0s | 0s | _splitpath | Path::Tiny::
0 | 0 | 0 | 0s | 0s | _symbolic_chmod | Path::Tiny::
0 | 0 | 0 | 0s | 0s | _throw | Path::Tiny::
0 | 0 | 0 | 0s | 0s | _win32_vol | Path::Tiny::
0 | 0 | 0 | 0s | 0s | absolute | Path::Tiny::
0 | 0 | 0 | 0s | 0s | append_raw | Path::Tiny::
0 | 0 | 0 | 0s | 0s | assert | Path::Tiny::
0 | 0 | 0 | 0s | 0s | basename | Path::Tiny::
0 | 0 | 0 | 0s | 0s | cached_temp | Path::Tiny::
0 | 0 | 0 | 0s | 0s | canonpath | Path::Tiny::
0 | 0 | 0 | 0s | 0s | child | Path::Tiny::
0 | 0 | 0 | 0s | 0s | children | Path::Tiny::
0 | 0 | 0 | 0s | 0s | chmod | Path::Tiny::
0 | 0 | 0 | 0s | 0s | copy | Path::Tiny::
0 | 0 | 0 | 0s | 0s | cwd | Path::Tiny::
0 | 0 | 0 | 0s | 0s | digest | Path::Tiny::
0 | 0 | 0 | 0s | 0s | dirname | Path::Tiny::
0 | 0 | 0 | 0s | 0s | edit | Path::Tiny::
0 | 0 | 0 | 0s | 0s | edit_lines | Path::Tiny::
0 | 0 | 0 | 0s | 0s | edit_lines_raw | Path::Tiny::
0 | 0 | 0 | 0s | 0s | edit_lines_utf8 | Path::Tiny::
0 | 0 | 0 | 0s | 0s | edit_raw | Path::Tiny::
0 | 0 | 0 | 0s | 0s | edit_utf8 | Path::Tiny::
0 | 0 | 0 | 0s | 0s | exists | Path::Tiny::
0 | 0 | 0 | 0s | 0s | is_absolute | Path::Tiny::
0 | 0 | 0 | 0s | 0s | is_dir | Path::Tiny::
0 | 0 | 0 | 0s | 0s | is_file | Path::Tiny::
0 | 0 | 0 | 0s | 0s | is_relative | Path::Tiny::
0 | 0 | 0 | 0s | 0s | is_rootdir | Path::Tiny::
0 | 0 | 0 | 0s | 0s | iterator | Path::Tiny::
0 | 0 | 0 | 0s | 0s | lines | Path::Tiny::
0 | 0 | 0 | 0s | 0s | lines_raw | Path::Tiny::
0 | 0 | 0 | 0s | 0s | lines_utf8 | Path::Tiny::
0 | 0 | 0 | 0s | 0s | lstat | Path::Tiny::
0 | 0 | 0 | 0s | 0s | mkpath | Path::Tiny::
0 | 0 | 0 | 0s | 0s | new | Path::Tiny::
0 | 0 | 0 | 0s | 0s | parent | Path::Tiny::
0 | 0 | 0 | 0s | 0s | realpath | Path::Tiny::
0 | 0 | 0 | 0s | 0s | relative | Path::Tiny::
0 | 0 | 0 | 0s | 0s | remove | Path::Tiny::
0 | 0 | 0 | 0s | 0s | remove_tree | Path::Tiny::
0 | 0 | 0 | 0s | 0s | rootdir | Path::Tiny::
0 | 0 | 0 | 0s | 0s | sibling | Path::Tiny::
0 | 0 | 0 | 0s | 0s | slurp_utf8 | Path::Tiny::
0 | 0 | 0 | 0s | 0s | spew_raw | Path::Tiny::
0 | 0 | 0 | 0s | 0s | stat | Path::Tiny::
0 | 0 | 0 | 0s | 0s | stringify | Path::Tiny::
0 | 0 | 0 | 0s | 0s | subsumes | Path::Tiny::
0 | 0 | 0 | 0s | 0s | tempdir | Path::Tiny::
0 | 0 | 0 | 0s | 0s | tempfile | Path::Tiny::
0 | 0 | 0 | 0s | 0s | touch | Path::Tiny::
0 | 0 | 0 | 0s | 0s | touchpath | Path::Tiny::
0 | 0 | 0 | 0s | 0s | visit | Path::Tiny::
0 | 0 | 0 | 0s | 0s | volume | Path::Tiny::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | 2 | 39µs | 1 | 12µs | # spent 12µs within RBM::BEGIN@1 which was called:
# once (12µs+0s) by RBM::BEGIN@5 at line 1 # spent 12µs making 1 call to RBM::BEGIN@1 |
2 | 2 | 16µs | 2 | 9µs | # spent 7µs (6+2) within RBM::BEGIN@2 which was called:
# once (6µs+2µs) by RBM::BEGIN@5 at line 2 # spent 7µs making 1 call to RBM::BEGIN@2
# spent 2µs making 1 call to strict::import |
3 | 2 | 36µs | 2 | 27µs | # spent 16µs (4+11) within RBM::BEGIN@3.1 which was called:
# once (4µs+11µs) by RBM::BEGIN@5 at line 3 # spent 16µs making 1 call to RBM::BEGIN@3.1
# spent 11µs making 1 call to warnings::import |
4 | |||||
5 | package Path::Tiny; | ||||
6 | # ABSTRACT: File path utility | ||||
7 | |||||
8 | 1 | 300ns | our $VERSION = '0.108'; | ||
9 | |||||
10 | # Dependencies | ||||
11 | 2 | 29µs | 2 | 24µs | # spent 15µs (6+9) within Path::Tiny::BEGIN@11 which was called:
# once (6µs+9µs) by RBM::BEGIN@5 at line 11 # spent 15µs making 1 call to Path::Tiny::BEGIN@11
# spent 9µs making 1 call to Config::import |
12 | 3 | 34µs | 3 | 34µs | # spent 23µs (12+11) within Path::Tiny::BEGIN@12 which was called:
# once (12µs+11µs) by RBM::BEGIN@5 at line 12 # spent 23µs making 1 call to Path::Tiny::BEGIN@12
# spent 6µs making 1 call to UNIVERSAL::VERSION
# spent 6µs making 1 call to Exporter::import |
13 | 3 | 180µs | 2 | 7.44ms | # spent 7.42ms (1.99+5.43) within Path::Tiny::BEGIN@13 which was called:
# once (1.99ms+5.43ms) by RBM::BEGIN@5 at line 13 # spent 7.42ms making 1 call to Path::Tiny::BEGIN@13
# spent 13µs making 1 call to UNIVERSAL::VERSION |
14 | 2 | 155µs | 1 | 5.44ms | # spent 5.44ms (4.26+1.17) within Path::Tiny::BEGIN@14 which was called:
# once (4.26ms+1.17ms) by RBM::BEGIN@5 at line 14 # spent 5.44ms making 1 call to Path::Tiny::BEGIN@14 |
15 | |||||
16 | 1 | 1µs | our @EXPORT = qw/path/; | ||
17 | 1 | 2µs | our @EXPORT_OK = qw/cwd rootdir tempfile tempdir/; | ||
18 | |||||
19 | # spent 142µs (12+129) within Path::Tiny::BEGIN@19 which was called:
# once (12µs+129µs) by RBM::BEGIN@5 at line 27 | ||||
20 | 1 | 10µs | 1 | 129µs | PATH => 0, # spent 129µs making 1 call to constant::import |
21 | CANON => 1, | ||||
22 | VOL => 2, | ||||
23 | DIR => 3, | ||||
24 | FILE => 4, | ||||
25 | TEMP => 5, | ||||
26 | IS_WIN32 => ( $^O eq 'MSWin32' ), | ||||
27 | 1 | 64µs | 1 | 142µs | }; # spent 142µs making 1 call to Path::Tiny::BEGIN@19 |
28 | |||||
29 | # spent 1.32ms (1.22+97µs) within Path::Tiny::BEGIN@29 which was called:
# once (1.22ms+97µs) by RBM::BEGIN@5 at line 33 | ||||
30 | 32 | 184µs | q{""} => sub { $_[0]->[PATH] }, | ||
31 | bool => sub () { 1 }, | ||||
32 | 1 | 6µs | 1 | 24µs | fallback => 1, # spent 24µs making 1 call to overload::import |
33 | 1 | 596µs | 1 | 1.32ms | ); # spent 1.32ms making 1 call to Path::Tiny::BEGIN@29 |
34 | |||||
35 | # FREEZE/THAW per Sereal/CBOR/Types::Serialiser protocol | ||||
36 | sub FREEZE { return $_[0]->[PATH] } | ||||
37 | sub THAW { return path( $_[2] ) } | ||||
38 | 4 | 369µs | 2 | 37µs | # spent 22µs (6+15) within Path::Tiny::BEGIN@38 which was called:
# once (6µs+15µs) by RBM::BEGIN@5 at line 38 # spent 22µs making 1 call to Path::Tiny::BEGIN@38
# spent 15µs making 1 call to warnings::unimport |
39 | |||||
40 | 1 | 100ns | my $HAS_UU; # has Unicode::UTF8; lazily populated | ||
41 | |||||
42 | # spent 748µs (346+402) within Path::Tiny::_check_UU which was called:
# once (346µs+402µs) by Path::Tiny::spew_utf8 at line 1875 | ||||
43 | 1 | 3µs | local $SIG{__DIE__}; # prevent outer handler from being called | ||
44 | 1 | 6µs | !!eval { | ||
45 | 1 | 203µs | require Unicode::UTF8; | ||
46 | 1 | 28µs | 1 | 15µs | Unicode::UTF8->VERSION(0.58); # spent 15µs making 1 call to UNIVERSAL::VERSION |
47 | 1 | 400ns | 1; | ||
48 | }; | ||||
49 | } | ||||
50 | |||||
51 | my $HAS_PU; # has PerlIO::utf8_strict; lazily populated | ||||
52 | |||||
53 | sub _check_PU { | ||||
54 | local $SIG{__DIE__}; # prevent outer handler from being called | ||||
55 | !!eval { | ||||
56 | # MUST preload Encode or $SIG{__DIE__} localization fails | ||||
57 | # on some Perl 5.8.8 (maybe other 5.8.*) compiled with -O2. | ||||
58 | require Encode; | ||||
59 | require PerlIO::utf8_strict; | ||||
60 | PerlIO::utf8_strict->VERSION(0.003); | ||||
61 | 1; | ||||
62 | }; | ||||
63 | } | ||||
64 | |||||
65 | 1 | 10µs | 1 | 2.54ms | my $HAS_FLOCK = $Config{d_flock} || $Config{d_fcntl_can_lock} || $Config{d_lockf}; # spent 2.54ms making 1 call to Config::FETCH |
66 | |||||
67 | # notions of "root" directories differ on Win32: \\server\dir\ or C:\ or \ | ||||
68 | 1 | 6µs | 1 | 2µs | my $SLASH = qr{[\\/]}; # spent 2µs making 1 call to Path::Tiny::CORE:qr |
69 | 1 | 3µs | 1 | 1µs | my $NOTSLASH = qr{[^\\/]}; # spent 1µs making 1 call to Path::Tiny::CORE:qr |
70 | 1 | 3µs | 1 | 800ns | my $DRV_VOL = qr{[a-z]:}i; # spent 800ns making 1 call to Path::Tiny::CORE:qr |
71 | 1 | 26µs | 2 | 20µs | my $UNC_VOL = qr{$SLASH $SLASH $NOTSLASH+ $SLASH $NOTSLASH+}x; # spent 20µs making 1 call to Path::Tiny::CORE:regcomp
# spent 500ns making 1 call to Path::Tiny::CORE:qr |
72 | 1 | 40µs | 2 | 35µs | my $WIN32_ROOT = qr{(?: $UNC_VOL $SLASH | $DRV_VOL $SLASH | $SLASH )}x; # spent 35µs making 1 call to Path::Tiny::CORE:regcomp
# spent 700ns making 1 call to Path::Tiny::CORE:qr |
73 | |||||
74 | sub _win32_vol { | ||||
75 | my ( $path, $drv ) = @_; | ||||
76 | require Cwd; | ||||
77 | my $dcwd = eval { Cwd::getdcwd($drv) }; # C: -> C:\some\cwd | ||||
78 | # getdcwd on non-existent drive returns empty string | ||||
79 | # so just use the original drive Z: -> Z: | ||||
80 | $dcwd = "$drv" unless defined $dcwd && length $dcwd; | ||||
81 | # normalize dwcd to end with a slash: might be C:\some\cwd or D:\ or Z: | ||||
82 | $dcwd =~ s{$SLASH?$}{/}; | ||||
83 | # make the path absolute with dcwd | ||||
84 | $path =~ s{^$DRV_VOL}{$dcwd}; | ||||
85 | return $path; | ||||
86 | } | ||||
87 | |||||
88 | # This is a string test for before we have the object; see is_rootdir for well-formed | ||||
89 | # object test | ||||
90 | # spent 105µs within Path::Tiny::_is_root which was called 35 times, avg 3µs/call:
# 35 times (105µs+0s) by Path::Tiny::path at line 254, avg 3µs/call | ||||
91 | 35 | 107µs | return IS_WIN32() ? ( $_[0] =~ /^$WIN32_ROOT$/ ) : ( $_[0] eq '/' ); | ||
92 | } | ||||
93 | |||||
94 | # spent 5µs within Path::Tiny::BEGIN@94 which was called:
# once (5µs+0s) by RBM::BEGIN@5 at line 96 | ||||
95 | 1 | 6µs | *_same = IS_WIN32() ? sub { lc( $_[0] ) eq lc( $_[1] ) } : sub { $_[0] eq $_[1] }; | ||
96 | 1 | 284µs | 1 | 5µs | } # spent 5µs making 1 call to Path::Tiny::BEGIN@94 |
97 | |||||
98 | # mode bits encoded for chmod in symbolic mode | ||||
99 | 1 | 2µs | my %MODEBITS = ( om => 0007, gm => 0070, um => 0700 ); ## no critic | ||
100 | 3 | 8µs | { my $m = 0; $MODEBITS{$_} = ( 1 << $m++ ) for qw/ox ow or gx gw gr ux uw ur/ }; | ||
101 | |||||
102 | sub _symbolic_chmod { | ||||
103 | my ( $mode, $symbolic ) = @_; | ||||
104 | for my $clause ( split /,\s*/, $symbolic ) { | ||||
105 | if ( $clause =~ m{\A([augo]+)([=+-])([rwx]+)\z} ) { | ||||
106 | my ( $who, $action, $perms ) = ( $1, $2, $3 ); | ||||
107 | $who =~ s/a/ugo/g; | ||||
108 | for my $w ( split //, $who ) { | ||||
109 | my $p = 0; | ||||
110 | $p |= $MODEBITS{"$w$_"} for split //, $perms; | ||||
111 | if ( $action eq '=' ) { | ||||
112 | $mode = ( $mode & ~$MODEBITS{"${w}m"} ) | $p; | ||||
113 | } | ||||
114 | else { | ||||
115 | $mode = $action eq "+" ? ( $mode | $p ) : ( $mode & ~$p ); | ||||
116 | } | ||||
117 | } | ||||
118 | } | ||||
119 | else { | ||||
120 | Carp::croak("Invalid mode clause '$clause' for chmod()"); | ||||
121 | } | ||||
122 | } | ||||
123 | return $mode; | ||||
124 | } | ||||
125 | |||||
126 | # flock doesn't work on NFS on BSD or on some filesystems like lustre. | ||||
127 | # Since program authors often can't control or detect that, we warn once | ||||
128 | # instead of being fatal if we can detect it and people who need it strict | ||||
129 | # can fatalize the 'flock' category | ||||
130 | |||||
131 | #<<< No perltidy | ||||
132 | 2 | 4.62ms | 2 | 60µs | # spent 33µs (6+27) within flock::BEGIN@132 which was called:
# once (6µs+27µs) by RBM::BEGIN@5 at line 132 # spent 33µs making 1 call to flock::BEGIN@132
# spent 27µs making 1 call to warnings::register::import |
133 | #>>> | ||||
134 | |||||
135 | 2 | 600ns | my $WARNED_NO_FLOCK = 0; | ||
136 | |||||
137 | sub _throw { | ||||
138 | my ( $self, $function, $file, $msg ) = @_; | ||||
139 | if ( $function =~ /^flock/ | ||||
140 | && $! =~ /operation not supported|function not implemented/i | ||||
141 | && !warnings::fatal_enabled('flock') ) | ||||
142 | { | ||||
143 | if ( !$WARNED_NO_FLOCK ) { | ||||
144 | warnings::warn( flock => "Flock not available: '$!': continuing in unsafe mode" ); | ||||
145 | $WARNED_NO_FLOCK++; | ||||
146 | } | ||||
147 | } | ||||
148 | else { | ||||
149 | $msg = $! unless defined $msg; | ||||
150 | Path::Tiny::Error->throw( $function, ( defined $file ? $file : $self->[PATH] ), | ||||
151 | $msg ); | ||||
152 | } | ||||
153 | return; | ||||
154 | } | ||||
155 | |||||
156 | # cheapo option validation | ||||
157 | # spent 310µs within Path::Tiny::_get_args which was called 39 times, avg 8µs/call:
# 19 times (101µs+0s) by Path::Tiny::filehandle at line 1027, avg 5µs/call
# 16 times (174µs+0s) by Path::Tiny::spew at line 1855, avg 11µs/call
# 2 times (21µs+0s) by Path::Tiny::slurp at line 1788, avg 10µs/call
# once (11µs+0s) by Path::Tiny::append_utf8 at line 565
# once (4µs+0s) by Path::Tiny::append at line 545 | ||||
158 | 39 | 37µs | my ( $raw, @valid ) = @_; | ||
159 | 39 | 28µs | if ( defined($raw) && ref($raw) ne 'HASH' ) { | ||
160 | my ( undef, undef, undef, $called_as ) = caller(1); | ||||
161 | $called_as =~ s{^.*::}{}; | ||||
162 | Carp::croak("Options for $called_as must be a hash reference"); | ||||
163 | } | ||||
164 | 39 | 23µs | my $cooked = {}; | ||
165 | 39 | 31µs | for my $k (@valid) { | ||
166 | 60 | 77µs | $cooked->{$k} = delete $raw->{$k} if exists $raw->{$k}; | ||
167 | } | ||||
168 | 39 | 36µs | if ( keys %$raw ) { | ||
169 | my ( undef, undef, undef, $called_as ) = caller(1); | ||||
170 | $called_as =~ s{^.*::}{}; | ||||
171 | Carp::croak( "Invalid option(s) for $called_as: " . join( ", ", keys %$raw ) ); | ||||
172 | } | ||||
173 | 39 | 220µs | return $cooked; | ||
174 | } | ||||
175 | |||||
176 | #--------------------------------------------------------------------------# | ||||
177 | # Constructors | ||||
178 | #--------------------------------------------------------------------------# | ||||
179 | |||||
180 | #pod =construct path | ||||
181 | #pod | ||||
182 | #pod $path = path("foo/bar"); | ||||
183 | #pod $path = path("/tmp", "file.txt"); # list | ||||
184 | #pod $path = path("."); # cwd | ||||
185 | #pod $path = path("~user/file.txt"); # tilde processing | ||||
186 | #pod | ||||
187 | #pod Constructs a C<Path::Tiny> object. It doesn't matter if you give a file or | ||||
188 | #pod directory path. It's still up to you to call directory-like methods only on | ||||
189 | #pod directories and file-like methods only on files. This function is exported | ||||
190 | #pod automatically by default. | ||||
191 | #pod | ||||
192 | #pod The first argument must be defined and have non-zero length or an exception | ||||
193 | #pod will be thrown. This prevents subtle, dangerous errors with code like | ||||
194 | #pod C<< path( maybe_undef() )->remove_tree >>. | ||||
195 | #pod | ||||
196 | #pod If the first component of the path is a tilde ('~') then the component will be | ||||
197 | #pod replaced with the output of C<glob('~')>. If the first component of the path | ||||
198 | #pod is a tilde followed by a user name then the component will be replaced with | ||||
199 | #pod output of C<glob('~username')>. Behaviour for non-existent users depends on | ||||
200 | #pod the output of C<glob> on the system. | ||||
201 | #pod | ||||
202 | #pod On Windows, if the path consists of a drive identifier without a path component | ||||
203 | #pod (C<C:> or C<D:>), it will be expanded to the absolute path of the current | ||||
204 | #pod directory on that volume using C<Cwd::getdcwd()>. | ||||
205 | #pod | ||||
206 | #pod If called with a single C<Path::Tiny> argument, the original is returned unless | ||||
207 | #pod the original is holding a temporary file or directory reference in which case a | ||||
208 | #pod stringified copy is made. | ||||
209 | #pod | ||||
210 | #pod $path = path("foo/bar"); | ||||
211 | #pod $temp = Path::Tiny->tempfile; | ||||
212 | #pod | ||||
213 | #pod $p2 = path($path); # like $p2 = $path | ||||
214 | #pod $t2 = path($temp); # like $t2 = path( "$temp" ) | ||||
215 | #pod | ||||
216 | #pod This optimizes copies without proliferating references unexpectedly if a copy is | ||||
217 | #pod made by code outside your control. | ||||
218 | #pod | ||||
219 | #pod Current API available since 0.017. | ||||
220 | #pod | ||||
221 | #pod =cut | ||||
222 | |||||
223 | # spent 9.38ms (9.17+211µs) within Path::Tiny::path which was called 35 times, avg 268µs/call:
# 16 times (8.45ms+39µs) by Path::Tiny::spew at line 1864, avg 531µs/call
# 15 times (549µs+99µs) by RBM::run_script at line 466 of /root/tor-browser-build/rbm/lib/RBM.pm, avg 43µs/call
# 2 times (78µs+48µs) by RBM::sha256file at line 619 of /root/tor-browser-build/rbm/lib/RBM.pm, avg 63µs/call
# once (51µs+14µs) by RBM::build_run at line 1071 of /root/tor-browser-build/rbm/lib/RBM.pm
# once (41µs+12µs) by RBM::build_run at line 1081 of /root/tor-browser-build/rbm/lib/RBM.pm | ||||
224 | 35 | 16µs | my $path = shift; | ||
225 | Carp::croak("Path::Tiny paths require defined, positive-length parts") | ||||
226 | 35 | 65µs | unless 1 + @_ == grep { defined && length } $path, @_; | ||
227 | |||||
228 | # non-temp Path::Tiny objects are effectively immutable and can be reused | ||||
229 | 35 | 25µs | if ( !@_ && ref($path) eq __PACKAGE__ && !$path->[TEMP] ) { | ||
230 | return $path; | ||||
231 | } | ||||
232 | |||||
233 | # stringify objects | ||||
234 | 35 | 17µs | $path = "$path"; | ||
235 | |||||
236 | # expand relative volume paths on windows; put trailing slash on UNC root | ||||
237 | if ( IS_WIN32() ) { | ||||
238 | $path = _win32_vol( $path, $1 ) if $path =~ m{^($DRV_VOL)(?:$NOTSLASH|$)}; | ||||
239 | $path .= "/" if $path =~ m{^$UNC_VOL$}; | ||||
240 | } | ||||
241 | |||||
242 | # concatenations stringifies objects, too | ||||
243 | 35 | 21µs | if (@_) { | ||
244 | $path .= ( _is_root($path) ? "" : "/" ) . join( "/", @_ ); | ||||
245 | } | ||||
246 | |||||
247 | # canonicalize, but with unix slashes and put back trailing volume slash | ||||
248 | 35 | 228µs | 35 | 48µs | my $cpath = $path = File::Spec->canonpath($path); # spent 48µs making 35 calls to File::Spec::Unix::canonpath, avg 1µs/call |
249 | $path =~ tr[\\][/] if IS_WIN32(); | ||||
250 | 35 | 13µs | $path = "/" if $path eq '/..'; # for old File::Spec | ||
251 | $path .= "/" if IS_WIN32() && $path =~ m{^$UNC_VOL$}; | ||||
252 | |||||
253 | # root paths must always have a trailing slash, but other paths must not | ||||
254 | 35 | 120µs | 35 | 105µs | if ( _is_root($path) ) { # spent 105µs making 35 calls to Path::Tiny::_is_root, avg 3µs/call |
255 | $path =~ s{/?$}{/}; | ||||
256 | } | ||||
257 | else { | ||||
258 | 35 | 8.40ms | 35 | 32µs | $path =~ s{/$}{}; # spent 32µs making 35 calls to Path::Tiny::CORE:subst, avg 917ns/call |
259 | } | ||||
260 | |||||
261 | # do any tilde expansions | ||||
262 | 35 | 136µs | 35 | 26µs | if ( $path =~ m{^(~[^/]*).*} ) { # spent 26µs making 35 calls to Path::Tiny::CORE:match, avg 746ns/call |
263 | require File::Glob; | ||||
264 | my ($homedir) = File::Glob::bsd_glob($1); | ||||
265 | $homedir =~ tr[\\][/] if IS_WIN32(); | ||||
266 | $path =~ s{^(~[^/]*)}{$homedir}; | ||||
267 | } | ||||
268 | |||||
269 | 35 | 301µs | bless [ $path, $cpath ], __PACKAGE__; | ||
270 | } | ||||
271 | |||||
272 | #pod =construct new | ||||
273 | #pod | ||||
274 | #pod $path = Path::Tiny->new("foo/bar"); | ||||
275 | #pod | ||||
276 | #pod This is just like C<path>, but with method call overhead. (Why would you | ||||
277 | #pod do that?) | ||||
278 | #pod | ||||
279 | #pod Current API available since 0.001. | ||||
280 | #pod | ||||
281 | #pod =cut | ||||
282 | |||||
283 | sub new { shift; path(@_) } | ||||
284 | |||||
285 | #pod =construct cwd | ||||
286 | #pod | ||||
287 | #pod $path = Path::Tiny->cwd; # path( Cwd::getcwd ) | ||||
288 | #pod $path = cwd; # optional export | ||||
289 | #pod | ||||
290 | #pod Gives you the absolute path to the current directory as a C<Path::Tiny> object. | ||||
291 | #pod This is slightly faster than C<< path(".")->absolute >>. | ||||
292 | #pod | ||||
293 | #pod C<cwd> may be exported on request and used as a function instead of as a | ||||
294 | #pod method. | ||||
295 | #pod | ||||
296 | #pod Current API available since 0.018. | ||||
297 | #pod | ||||
298 | #pod =cut | ||||
299 | |||||
300 | sub cwd { | ||||
301 | require Cwd; | ||||
302 | return path( Cwd::getcwd() ); | ||||
303 | } | ||||
304 | |||||
305 | #pod =construct rootdir | ||||
306 | #pod | ||||
307 | #pod $path = Path::Tiny->rootdir; # / | ||||
308 | #pod $path = rootdir; # optional export | ||||
309 | #pod | ||||
310 | #pod Gives you C<< File::Spec->rootdir >> as a C<Path::Tiny> object if you're too | ||||
311 | #pod picky for C<path("/")>. | ||||
312 | #pod | ||||
313 | #pod C<rootdir> may be exported on request and used as a function instead of as a | ||||
314 | #pod method. | ||||
315 | #pod | ||||
316 | #pod Current API available since 0.018. | ||||
317 | #pod | ||||
318 | #pod =cut | ||||
319 | |||||
320 | sub rootdir { path( File::Spec->rootdir ) } | ||||
321 | |||||
322 | #pod =construct tempfile, tempdir | ||||
323 | #pod | ||||
324 | #pod $temp = Path::Tiny->tempfile( @options ); | ||||
325 | #pod $temp = Path::Tiny->tempdir( @options ); | ||||
326 | #pod $temp = tempfile( @options ); # optional export | ||||
327 | #pod $temp = tempdir( @options ); # optional export | ||||
328 | #pod | ||||
329 | #pod C<tempfile> passes the options to C<< File::Temp->new >> and returns a C<Path::Tiny> | ||||
330 | #pod object with the file name. The C<TMPDIR> option is enabled by default. | ||||
331 | #pod | ||||
332 | #pod The resulting C<File::Temp> object is cached. When the C<Path::Tiny> object is | ||||
333 | #pod destroyed, the C<File::Temp> object will be as well. | ||||
334 | #pod | ||||
335 | #pod C<File::Temp> annoyingly requires you to specify a custom template in slightly | ||||
336 | #pod different ways depending on which function or method you call, but | ||||
337 | #pod C<Path::Tiny> lets you ignore that and can take either a leading template or a | ||||
338 | #pod C<TEMPLATE> option and does the right thing. | ||||
339 | #pod | ||||
340 | #pod $temp = Path::Tiny->tempfile( "customXXXXXXXX" ); # ok | ||||
341 | #pod $temp = Path::Tiny->tempfile( TEMPLATE => "customXXXXXXXX" ); # ok | ||||
342 | #pod | ||||
343 | #pod The tempfile path object will be normalized to have an absolute path, even if | ||||
344 | #pod created in a relative directory using C<DIR>. If you want it to have | ||||
345 | #pod the C<realpath> instead, pass a leading options hash like this: | ||||
346 | #pod | ||||
347 | #pod $real_temp = tempfile({realpath => 1}, @options); | ||||
348 | #pod | ||||
349 | #pod C<tempdir> is just like C<tempfile>, except it calls | ||||
350 | #pod C<< File::Temp->newdir >> instead. | ||||
351 | #pod | ||||
352 | #pod Both C<tempfile> and C<tempdir> may be exported on request and used as | ||||
353 | #pod functions instead of as methods. | ||||
354 | #pod | ||||
355 | #pod B<Note>: for tempfiles, the filehandles from File::Temp are closed and not | ||||
356 | #pod reused. This is not as secure as using File::Temp handles directly, but is | ||||
357 | #pod less prone to deadlocks or access problems on some platforms. Think of what | ||||
358 | #pod C<Path::Tiny> gives you to be just a temporary file B<name> that gets cleaned | ||||
359 | #pod up. | ||||
360 | #pod | ||||
361 | #pod B<Note 2>: if you don't want these cleaned up automatically when the object | ||||
362 | #pod is destroyed, File::Temp requires different options for directories and | ||||
363 | #pod files. Use C<< CLEANUP => 0 >> for directories and C<< UNLINK => 0 >> for | ||||
364 | #pod files. | ||||
365 | #pod | ||||
366 | #pod B<Note 3>: Don't lose the temporary object by chaining a method call instead | ||||
367 | #pod of storing it: | ||||
368 | #pod | ||||
369 | #pod my $lost = tempdir()->child("foo"); # tempdir cleaned up right away | ||||
370 | #pod | ||||
371 | #pod B<Note 4>: The cached object may be accessed with the L</cached_temp> method. | ||||
372 | #pod Keeping a reference to, or modifying the cached object may break the | ||||
373 | #pod behavior documented above and is not supported. Use at your own risk. | ||||
374 | #pod | ||||
375 | #pod Current API available since 0.097. | ||||
376 | #pod | ||||
377 | #pod =cut | ||||
378 | |||||
379 | sub tempfile { | ||||
380 | shift if @_ && $_[0] eq 'Path::Tiny'; # called as method | ||||
381 | my $opts = ( @_ && ref $_[0] eq 'HASH' ) ? shift @_ : {}; | ||||
382 | $opts = _get_args( $opts, qw/realpath/ ); | ||||
383 | |||||
384 | my ( $maybe_template, $args ) = _parse_file_temp_args(@_); | ||||
385 | # File::Temp->new demands TEMPLATE | ||||
386 | $args->{TEMPLATE} = $maybe_template->[0] if @$maybe_template; | ||||
387 | |||||
388 | require File::Temp; | ||||
389 | my $temp = File::Temp->new( TMPDIR => 1, %$args ); | ||||
390 | close $temp; | ||||
391 | my $self = $opts->{realpath} ? path($temp)->realpath : path($temp)->absolute; | ||||
392 | $self->[TEMP] = $temp; # keep object alive while we are | ||||
393 | return $self; | ||||
394 | } | ||||
395 | |||||
396 | sub tempdir { | ||||
397 | shift if @_ && $_[0] eq 'Path::Tiny'; # called as method | ||||
398 | my $opts = ( @_ && ref $_[0] eq 'HASH' ) ? shift @_ : {}; | ||||
399 | $opts = _get_args( $opts, qw/realpath/ ); | ||||
400 | |||||
401 | my ( $maybe_template, $args ) = _parse_file_temp_args(@_); | ||||
402 | |||||
403 | # File::Temp->newdir demands leading template | ||||
404 | require File::Temp; | ||||
405 | my $temp = File::Temp->newdir( @$maybe_template, TMPDIR => 1, %$args ); | ||||
406 | my $self = $opts->{realpath} ? path($temp)->realpath : path($temp)->absolute; | ||||
407 | $self->[TEMP] = $temp; # keep object alive while we are | ||||
408 | # Some ActiveState Perls for Windows break Cwd in ways that lead | ||||
409 | # File::Temp to get confused about what path to remove; this | ||||
410 | # monkey-patches the object with our own view of the absolute path | ||||
411 | $temp->{REALNAME} = $self->[CANON] if IS_WIN32; | ||||
412 | return $self; | ||||
413 | } | ||||
414 | |||||
415 | # normalize the various ways File::Temp does templates | ||||
416 | sub _parse_file_temp_args { | ||||
417 | my $leading_template = ( scalar(@_) % 2 == 1 ? shift(@_) : '' ); | ||||
418 | my %args = @_; | ||||
419 | %args = map { uc($_), $args{$_} } keys %args; | ||||
420 | my @template = ( | ||||
421 | exists $args{TEMPLATE} ? delete $args{TEMPLATE} | ||||
422 | : $leading_template ? $leading_template | ||||
423 | : () | ||||
424 | ); | ||||
425 | return ( \@template, \%args ); | ||||
426 | } | ||||
427 | |||||
428 | #--------------------------------------------------------------------------# | ||||
429 | # Private methods | ||||
430 | #--------------------------------------------------------------------------# | ||||
431 | |||||
432 | sub _splitpath { | ||||
433 | my ($self) = @_; | ||||
434 | @{$self}[ VOL, DIR, FILE ] = File::Spec->splitpath( $self->[PATH] ); | ||||
435 | } | ||||
436 | |||||
437 | # spent 355µs (262+93) within Path::Tiny::_resolve_symlinks which was called 16 times, avg 22µs/call:
# 16 times (262µs+93µs) by Path::Tiny::spew at line 1862, avg 22µs/call | ||||
438 | 16 | 13µs | my ($self) = @_; | ||
439 | 16 | 10µs | my $new = $self; | ||
440 | 16 | 15µs | my ( $count, %seen ) = 0; | ||
441 | 16 | 239µs | 16 | 93µs | while ( -l $new->[PATH] ) { # spent 93µs making 16 calls to Path::Tiny::CORE:ftlink, avg 6µs/call |
442 | if ( $seen{ $new->[PATH] }++ ) { | ||||
443 | $self->_throw( 'readlink', $self->[PATH], "symlink loop detected" ); | ||||
444 | } | ||||
445 | if ( ++$count > 100 ) { | ||||
446 | $self->_throw( 'readlink', $self->[PATH], "maximum symlink depth exceeded" ); | ||||
447 | } | ||||
448 | my $resolved = readlink $new->[PATH] or $new->_throw( 'readlink', $new->[PATH] ); | ||||
449 | $resolved = path($resolved); | ||||
450 | $new = $resolved->is_absolute ? $resolved : $new->sibling($resolved); | ||||
451 | } | ||||
452 | 16 | 73µs | return $new; | ||
453 | } | ||||
454 | |||||
455 | #--------------------------------------------------------------------------# | ||||
456 | # Public methods | ||||
457 | #--------------------------------------------------------------------------# | ||||
458 | |||||
459 | #pod =method absolute | ||||
460 | #pod | ||||
461 | #pod $abs = path("foo/bar")->absolute; | ||||
462 | #pod $abs = path("foo/bar")->absolute("/tmp"); | ||||
463 | #pod | ||||
464 | #pod Returns a new C<Path::Tiny> object with an absolute path (or itself if already | ||||
465 | #pod absolute). If no argument is given, the current directory is used as the | ||||
466 | #pod absolute base path. If an argument is given, it will be converted to an | ||||
467 | #pod absolute path (if it is not already) and used as the absolute base path. | ||||
468 | #pod | ||||
469 | #pod This will not resolve upward directories ("foo/../bar") unless C<canonpath> | ||||
470 | #pod in L<File::Spec> would normally do so on your platform. If you need them | ||||
471 | #pod resolved, you must call the more expensive C<realpath> method instead. | ||||
472 | #pod | ||||
473 | #pod On Windows, an absolute path without a volume component will have it added | ||||
474 | #pod based on the current drive. | ||||
475 | #pod | ||||
476 | #pod Current API available since 0.101. | ||||
477 | #pod | ||||
478 | #pod =cut | ||||
479 | |||||
480 | sub absolute { | ||||
481 | my ( $self, $base ) = @_; | ||||
482 | |||||
483 | # absolute paths handled differently by OS | ||||
484 | if (IS_WIN32) { | ||||
485 | return $self if length $self->volume; | ||||
486 | # add missing volume | ||||
487 | if ( $self->is_absolute ) { | ||||
488 | require Cwd; | ||||
489 | # use Win32::GetCwd not Cwd::getdcwd because we're sure | ||||
490 | # to have the former but not necessarily the latter | ||||
491 | my ($drv) = Win32::GetCwd() =~ /^($DRV_VOL | $UNC_VOL)/x; | ||||
492 | return path( $drv . $self->[PATH] ); | ||||
493 | } | ||||
494 | } | ||||
495 | else { | ||||
496 | return $self if $self->is_absolute; | ||||
497 | } | ||||
498 | |||||
499 | # no base means use current directory as base | ||||
500 | require Cwd; | ||||
501 | return path( Cwd::getcwd(), $_[0]->[PATH] ) unless defined $base; | ||||
502 | |||||
503 | # relative base should be made absolute; we check is_absolute rather | ||||
504 | # than unconditionally make base absolute so that "/foo" doesn't become | ||||
505 | # "C:/foo" on Windows. | ||||
506 | $base = path($base); | ||||
507 | return path( ( $base->is_absolute ? $base : $base->absolute ), $_[0]->[PATH] ); | ||||
508 | } | ||||
509 | |||||
510 | #pod =method append, append_raw, append_utf8 | ||||
511 | #pod | ||||
512 | #pod path("foo.txt")->append(@data); | ||||
513 | #pod path("foo.txt")->append(\@data); | ||||
514 | #pod path("foo.txt")->append({binmode => ":raw"}, @data); | ||||
515 | #pod path("foo.txt")->append_raw(@data); | ||||
516 | #pod path("foo.txt")->append_utf8(@data); | ||||
517 | #pod | ||||
518 | #pod Appends data to a file. The file is locked with C<flock> prior to writing | ||||
519 | #pod and closed afterwards. An optional hash reference may be used to pass | ||||
520 | #pod options. Valid options are: | ||||
521 | #pod | ||||
522 | #pod =for :list | ||||
523 | #pod * C<binmode>: passed to C<binmode()> on the handle used for writing. | ||||
524 | #pod * C<truncate>: truncates the file after locking and before appending | ||||
525 | #pod | ||||
526 | #pod The C<truncate> option is a way to replace the contents of a file | ||||
527 | #pod B<in place>, unlike L</spew> which writes to a temporary file and then | ||||
528 | #pod replaces the original (if it exists). | ||||
529 | #pod | ||||
530 | #pod C<append_raw> is like C<append> with a C<binmode> of C<:unix> for fast, | ||||
531 | #pod unbuffered, raw write. | ||||
532 | #pod | ||||
533 | #pod C<append_utf8> is like C<append> with a C<binmode> of | ||||
534 | #pod C<:unix:encoding(UTF-8)> (or L<PerlIO::utf8_strict>). If L<Unicode::UTF8> | ||||
535 | #pod 0.58+ is installed, a raw append will be done instead on the data encoded | ||||
536 | #pod with C<Unicode::UTF8>. | ||||
537 | #pod | ||||
538 | #pod Current API available since 0.060. | ||||
539 | #pod | ||||
540 | #pod =cut | ||||
541 | |||||
542 | # spent 702µs (36+666) within Path::Tiny::append which was called:
# once (36µs+666µs) by Path::Tiny::append_utf8 at line 568 | ||||
543 | 1 | 1µs | my ( $self, @data ) = @_; | ||
544 | 1 | 1µs | my $args = ( @data && ref $data[0] eq 'HASH' ) ? shift @data : {}; | ||
545 | 1 | 2µs | 1 | 4µs | $args = _get_args( $args, qw/binmode truncate/ ); # spent 4µs making 1 call to Path::Tiny::_get_args |
546 | 1 | 1µs | my $binmode = $args->{binmode}; | ||
547 | 1 | 900ns | $binmode = ( ( caller(0) )[10] || {} )->{'open>'} unless defined $binmode; | ||
548 | 1 | 900ns | my $mode = $args->{truncate} ? ">" : ">>"; | ||
549 | 1 | 6µs | 1 | 133µs | my $fh = $self->filehandle( { locked => 1 }, $mode, $binmode ); # spent 133µs making 1 call to Path::Tiny::filehandle |
550 | 1 | 527µs | 1 | 519µs | print {$fh} map { ref eq 'ARRAY' ? @$_ : $_ } @data; # spent 519µs making 1 call to Path::Tiny::CORE:print |
551 | 1 | 21µs | 1 | 10µs | close $fh or $self->_throw('close'); # spent 10µs making 1 call to Path::Tiny::CORE:close |
552 | } | ||||
553 | |||||
554 | sub append_raw { | ||||
555 | my ( $self, @data ) = @_; | ||||
556 | my $args = ( @data && ref $data[0] eq 'HASH' ) ? shift @data : {}; | ||||
557 | $args = _get_args( $args, qw/binmode truncate/ ); | ||||
558 | $args->{binmode} = ':unix'; | ||||
559 | append( $self, $args, @data ); | ||||
560 | } | ||||
561 | |||||
562 | # spent 750µs (32+717) within Path::Tiny::append_utf8 which was called:
# once (32µs+717µs) by RBM::build_run at line 1081 of /root/tor-browser-build/rbm/lib/RBM.pm | ||||
563 | 1 | 1µs | my ( $self, @data ) = @_; | ||
564 | 1 | 2µs | my $args = ( @data && ref $data[0] eq 'HASH' ) ? shift @data : {}; | ||
565 | 1 | 4µs | 1 | 11µs | $args = _get_args( $args, qw/binmode truncate/ ); # spent 11µs making 1 call to Path::Tiny::_get_args |
566 | 1 | 5µs | if ( defined($HAS_UU) ? $HAS_UU : ( $HAS_UU = _check_UU() ) ) { | ||
567 | 1 | 4µs | $args->{binmode} = ":unix"; | ||
568 | 2 | 18µs | 2 | 706µs | append( $self, $args, map { Unicode::UTF8::encode_utf8($_) } @data ); # spent 702µs making 1 call to Path::Tiny::append
# spent 4µs making 1 call to Unicode::UTF8::encode_utf8 |
569 | } | ||||
570 | elsif ( defined($HAS_PU) ? $HAS_PU : ( $HAS_PU = _check_PU() ) ) { | ||||
571 | $args->{binmode} = ":unix:utf8_strict"; | ||||
572 | append( $self, $args, @data ); | ||||
573 | } | ||||
574 | else { | ||||
575 | $args->{binmode} = ":unix:encoding(UTF-8)"; | ||||
576 | append( $self, $args, @data ); | ||||
577 | } | ||||
578 | } | ||||
579 | |||||
580 | #pod =method assert | ||||
581 | #pod | ||||
582 | #pod $path = path("foo.txt")->assert( sub { $_->exists } ); | ||||
583 | #pod | ||||
584 | #pod Returns the invocant after asserting that a code reference argument returns | ||||
585 | #pod true. When the assertion code reference runs, it will have the invocant | ||||
586 | #pod object in the C<$_> variable. If it returns false, an exception will be | ||||
587 | #pod thrown. The assertion code reference may also throw its own exception. | ||||
588 | #pod | ||||
589 | #pod If no assertion is provided, the invocant is returned without error. | ||||
590 | #pod | ||||
591 | #pod Current API available since 0.062. | ||||
592 | #pod | ||||
593 | #pod =cut | ||||
594 | |||||
595 | sub assert { | ||||
596 | my ( $self, $assertion ) = @_; | ||||
597 | return $self unless $assertion; | ||||
598 | if ( ref $assertion eq 'CODE' ) { | ||||
599 | local $_ = $self; | ||||
600 | $assertion->() | ||||
601 | or Path::Tiny::Error->throw( "assert", $self->[PATH], "failed assertion" ); | ||||
602 | } | ||||
603 | else { | ||||
604 | Carp::croak("argument to assert must be a code reference argument"); | ||||
605 | } | ||||
606 | return $self; | ||||
607 | } | ||||
608 | |||||
609 | #pod =method basename | ||||
610 | #pod | ||||
611 | #pod $name = path("foo/bar.txt")->basename; # bar.txt | ||||
612 | #pod $name = path("foo.txt")->basename('.txt'); # foo | ||||
613 | #pod $name = path("foo.txt")->basename(qr/.txt/); # foo | ||||
614 | #pod $name = path("foo.txt")->basename(@suffixes); | ||||
615 | #pod | ||||
616 | #pod Returns the file portion or last directory portion of a path. | ||||
617 | #pod | ||||
618 | #pod Given a list of suffixes as strings or regular expressions, any that match at | ||||
619 | #pod the end of the file portion or last directory portion will be removed before | ||||
620 | #pod the result is returned. | ||||
621 | #pod | ||||
622 | #pod Current API available since 0.054. | ||||
623 | #pod | ||||
624 | #pod =cut | ||||
625 | |||||
626 | sub basename { | ||||
627 | my ( $self, @suffixes ) = @_; | ||||
628 | $self->_splitpath unless defined $self->[FILE]; | ||||
629 | my $file = $self->[FILE]; | ||||
630 | for my $s (@suffixes) { | ||||
631 | my $re = ref($s) eq 'Regexp' ? qr/$s$/ : qr/\Q$s\E$/; | ||||
632 | last if $file =~ s/$re//; | ||||
633 | } | ||||
634 | return $file; | ||||
635 | } | ||||
636 | |||||
637 | #pod =method canonpath | ||||
638 | #pod | ||||
639 | #pod $canonical = path("foo/bar")->canonpath; # foo\bar on Windows | ||||
640 | #pod | ||||
641 | #pod Returns a string with the canonical format of the path name for | ||||
642 | #pod the platform. In particular, this means directory separators | ||||
643 | #pod will be C<\> on Windows. | ||||
644 | #pod | ||||
645 | #pod Current API available since 0.001. | ||||
646 | #pod | ||||
647 | #pod =cut | ||||
648 | |||||
649 | sub canonpath { $_[0]->[CANON] } | ||||
650 | |||||
651 | #pod =method cached_temp | ||||
652 | #pod | ||||
653 | #pod Returns the cached C<File::Temp> or C<File::Temp::Dir> object if the | ||||
654 | #pod C<Path::Tiny> object was created with C</tempfile> or C</tempdir>. | ||||
655 | #pod If there is no such object, this method throws. | ||||
656 | #pod | ||||
657 | #pod B<WARNING>: Keeping a reference to, or modifying the cached object may | ||||
658 | #pod break the behavior documented for temporary files and directories created | ||||
659 | #pod with C<Path::Tiny> and is not supported. Use at your own risk. | ||||
660 | #pod | ||||
661 | #pod Current API available since 0.101. | ||||
662 | #pod | ||||
663 | #pod =cut | ||||
664 | |||||
665 | sub cached_temp { | ||||
666 | my $self = shift; | ||||
667 | $self->_throw( "cached_temp", $self, "has no cached File::Temp object" ) | ||||
668 | unless defined $self->[TEMP]; | ||||
669 | return $self->[TEMP]; | ||||
670 | } | ||||
671 | |||||
672 | #pod =method child | ||||
673 | #pod | ||||
674 | #pod $file = path("/tmp")->child("foo.txt"); # "/tmp/foo.txt" | ||||
675 | #pod $file = path("/tmp")->child(@parts); | ||||
676 | #pod | ||||
677 | #pod Returns a new C<Path::Tiny> object relative to the original. Works | ||||
678 | #pod like C<catfile> or C<catdir> from File::Spec, but without caring about | ||||
679 | #pod file or directories. | ||||
680 | #pod | ||||
681 | #pod B<WARNING>: because the argument could contain C<..> or refer to symlinks, | ||||
682 | #pod there is no guarantee that the new path refers to an actual descendent of | ||||
683 | #pod the original. If this is important to you, transform parent and child with | ||||
684 | #pod L</realpath> and check them with L</subsumes>. | ||||
685 | #pod | ||||
686 | #pod Current API available since 0.001. | ||||
687 | #pod | ||||
688 | #pod =cut | ||||
689 | |||||
690 | sub child { | ||||
691 | my ( $self, @parts ) = @_; | ||||
692 | return path( $self->[PATH], @parts ); | ||||
693 | } | ||||
694 | |||||
695 | #pod =method children | ||||
696 | #pod | ||||
697 | #pod @paths = path("/tmp")->children; | ||||
698 | #pod @paths = path("/tmp")->children( qr/\.txt$/ ); | ||||
699 | #pod | ||||
700 | #pod Returns a list of C<Path::Tiny> objects for all files and directories | ||||
701 | #pod within a directory. Excludes "." and ".." automatically. | ||||
702 | #pod | ||||
703 | #pod If an optional C<qr//> argument is provided, it only returns objects for child | ||||
704 | #pod names that match the given regular expression. Only the base name is used | ||||
705 | #pod for matching: | ||||
706 | #pod | ||||
707 | #pod @paths = path("/tmp")->children( qr/^foo/ ); | ||||
708 | #pod # matches children like the glob foo* | ||||
709 | #pod | ||||
710 | #pod Current API available since 0.028. | ||||
711 | #pod | ||||
712 | #pod =cut | ||||
713 | |||||
714 | sub children { | ||||
715 | my ( $self, $filter ) = @_; | ||||
716 | my $dh; | ||||
717 | opendir $dh, $self->[PATH] or $self->_throw('opendir'); | ||||
718 | my @children = readdir $dh; | ||||
719 | closedir $dh or $self->_throw('closedir'); | ||||
720 | |||||
721 | if ( not defined $filter ) { | ||||
722 | @children = grep { $_ ne '.' && $_ ne '..' } @children; | ||||
723 | } | ||||
724 | elsif ( $filter && ref($filter) eq 'Regexp' ) { | ||||
725 | @children = grep { $_ ne '.' && $_ ne '..' && $_ =~ $filter } @children; | ||||
726 | } | ||||
727 | else { | ||||
728 | Carp::croak("Invalid argument '$filter' for children()"); | ||||
729 | } | ||||
730 | |||||
731 | return map { path( $self->[PATH], $_ ) } @children; | ||||
732 | } | ||||
733 | |||||
734 | #pod =method chmod | ||||
735 | #pod | ||||
736 | #pod path("foo.txt")->chmod(0777); | ||||
737 | #pod path("foo.txt")->chmod("0755"); | ||||
738 | #pod path("foo.txt")->chmod("go-w"); | ||||
739 | #pod path("foo.txt")->chmod("a=r,u+wx"); | ||||
740 | #pod | ||||
741 | #pod Sets file or directory permissions. The argument can be a numeric mode, a | ||||
742 | #pod octal string beginning with a "0" or a limited subset of the symbolic mode use | ||||
743 | #pod by F</bin/chmod>. | ||||
744 | #pod | ||||
745 | #pod The symbolic mode must be a comma-delimited list of mode clauses. Clauses must | ||||
746 | #pod match C<< qr/\A([augo]+)([=+-])([rwx]+)\z/ >>, which defines "who", "op" and | ||||
747 | #pod "perms" parameters for each clause. Unlike F</bin/chmod>, all three parameters | ||||
748 | #pod are required for each clause, multiple ops are not allowed and permissions | ||||
749 | #pod C<stugoX> are not supported. (See L<File::chmod> for more complex needs.) | ||||
750 | #pod | ||||
751 | #pod Current API available since 0.053. | ||||
752 | #pod | ||||
753 | #pod =cut | ||||
754 | |||||
755 | sub chmod { | ||||
756 | my ( $self, $new_mode ) = @_; | ||||
757 | |||||
758 | my $mode; | ||||
759 | if ( $new_mode =~ /\d/ ) { | ||||
760 | $mode = ( $new_mode =~ /^0/ ? oct($new_mode) : $new_mode ); | ||||
761 | } | ||||
762 | elsif ( $new_mode =~ /[=+-]/ ) { | ||||
763 | $mode = _symbolic_chmod( $self->stat->mode & 07777, $new_mode ); ## no critic | ||||
764 | } | ||||
765 | else { | ||||
766 | Carp::croak("Invalid mode argument '$new_mode' for chmod()"); | ||||
767 | } | ||||
768 | |||||
769 | CORE::chmod( $mode, $self->[PATH] ) or $self->_throw("chmod"); | ||||
770 | |||||
771 | return 1; | ||||
772 | } | ||||
773 | |||||
774 | #pod =method copy | ||||
775 | #pod | ||||
776 | #pod path("/tmp/foo.txt")->copy("/tmp/bar.txt"); | ||||
777 | #pod | ||||
778 | #pod Copies the current path to the given destination using L<File::Copy>'s | ||||
779 | #pod C<copy> function. Upon success, returns the C<Path::Tiny> object for the | ||||
780 | #pod newly copied file. | ||||
781 | #pod | ||||
782 | #pod Current API available since 0.070. | ||||
783 | #pod | ||||
784 | #pod =cut | ||||
785 | |||||
786 | # XXX do recursively for directories? | ||||
787 | sub copy { | ||||
788 | my ( $self, $dest ) = @_; | ||||
789 | require File::Copy; | ||||
790 | File::Copy::copy( $self->[PATH], $dest ) | ||||
791 | or Carp::croak("copy failed for $self to $dest: $!"); | ||||
792 | |||||
793 | return -d $dest ? path( $dest, $self->basename ) : path($dest); | ||||
794 | } | ||||
795 | |||||
796 | #pod =method digest | ||||
797 | #pod | ||||
798 | #pod $obj = path("/tmp/foo.txt")->digest; # SHA-256 | ||||
799 | #pod $obj = path("/tmp/foo.txt")->digest("MD5"); # user-selected | ||||
800 | #pod $obj = path("/tmp/foo.txt")->digest( { chunk_size => 1e6 }, "MD5" ); | ||||
801 | #pod | ||||
802 | #pod Returns a hexadecimal digest for a file. An optional hash reference of options may | ||||
803 | #pod be given. The only option is C<chunk_size>. If C<chunk_size> is given, that many | ||||
804 | #pod bytes will be read at a time. If not provided, the entire file will be slurped | ||||
805 | #pod into memory to compute the digest. | ||||
806 | #pod | ||||
807 | #pod Any subsequent arguments are passed to the constructor for L<Digest> to select | ||||
808 | #pod an algorithm. If no arguments are given, the default is SHA-256. | ||||
809 | #pod | ||||
810 | #pod Current API available since 0.056. | ||||
811 | #pod | ||||
812 | #pod =cut | ||||
813 | |||||
814 | sub digest { | ||||
815 | my ( $self, @opts ) = @_; | ||||
816 | my $args = ( @opts && ref $opts[0] eq 'HASH' ) ? shift @opts : {}; | ||||
817 | $args = _get_args( $args, qw/chunk_size/ ); | ||||
818 | unshift @opts, 'SHA-256' unless @opts; | ||||
819 | require Digest; | ||||
820 | my $digest = Digest->new(@opts); | ||||
821 | if ( $args->{chunk_size} ) { | ||||
822 | my $fh = $self->filehandle( { locked => 1 }, "<", ":unix" ); | ||||
823 | my $buf; | ||||
824 | $digest->add($buf) while read $fh, $buf, $args->{chunk_size}; | ||||
825 | } | ||||
826 | else { | ||||
827 | $digest->add( $self->slurp_raw ); | ||||
828 | } | ||||
829 | return $digest->hexdigest; | ||||
830 | } | ||||
831 | |||||
832 | #pod =method dirname (deprecated) | ||||
833 | #pod | ||||
834 | #pod $name = path("/tmp/foo.txt")->dirname; # "/tmp/" | ||||
835 | #pod | ||||
836 | #pod Returns the directory portion you would get from calling | ||||
837 | #pod C<< File::Spec->splitpath( $path->stringify ) >> or C<"."> for a path without a | ||||
838 | #pod parent directory portion. Because L<File::Spec> is inconsistent, the result | ||||
839 | #pod might or might not have a trailing slash. Because of this, this method is | ||||
840 | #pod B<deprecated>. | ||||
841 | #pod | ||||
842 | #pod A better, more consistently approach is likely C<< $path->parent->stringify >>, | ||||
843 | #pod which will not have a trailing slash except for a root directory. | ||||
844 | #pod | ||||
845 | #pod Deprecated in 0.056. | ||||
846 | #pod | ||||
847 | #pod =cut | ||||
848 | |||||
849 | sub dirname { | ||||
850 | my ($self) = @_; | ||||
851 | $self->_splitpath unless defined $self->[DIR]; | ||||
852 | return length $self->[DIR] ? $self->[DIR] : "."; | ||||
853 | } | ||||
854 | |||||
855 | #pod =method edit, edit_raw, edit_utf8 | ||||
856 | #pod | ||||
857 | #pod path("foo.txt")->edit( \&callback, $options ); | ||||
858 | #pod path("foo.txt")->edit_utf8( \&callback ); | ||||
859 | #pod path("foo.txt")->edit_raw( \&callback ); | ||||
860 | #pod | ||||
861 | #pod These are convenience methods that allow "editing" a file using a single | ||||
862 | #pod callback argument. They slurp the file using C<slurp>, place the contents | ||||
863 | #pod inside a localized C<$_> variable, call the callback function (without | ||||
864 | #pod arguments), and then write C<$_> (presumably mutated) back to the | ||||
865 | #pod file with C<spew>. | ||||
866 | #pod | ||||
867 | #pod An optional hash reference may be used to pass options. The only option is | ||||
868 | #pod C<binmode>, which is passed to C<slurp> and C<spew>. | ||||
869 | #pod | ||||
870 | #pod C<edit_utf8> and C<edit_raw> act like their respective C<slurp_*> and | ||||
871 | #pod C<spew_*> methods. | ||||
872 | #pod | ||||
873 | #pod Current API available since 0.077. | ||||
874 | #pod | ||||
875 | #pod =cut | ||||
876 | |||||
877 | sub edit { | ||||
878 | my $self = shift; | ||||
879 | my $cb = shift; | ||||
880 | my $args = _get_args( shift, qw/binmode/ ); | ||||
881 | Carp::croak("Callback for edit() must be a code reference") | ||||
882 | unless defined($cb) && ref($cb) eq 'CODE'; | ||||
883 | |||||
884 | local $_ = | ||||
885 | $self->slurp( exists( $args->{binmode} ) ? { binmode => $args->{binmode} } : () ); | ||||
886 | $cb->(); | ||||
887 | $self->spew( $args, $_ ); | ||||
888 | |||||
889 | return; | ||||
890 | } | ||||
891 | |||||
892 | # this is done long-hand to benefit from slurp_utf8 optimizations | ||||
893 | sub edit_utf8 { | ||||
894 | my ( $self, $cb ) = @_; | ||||
895 | Carp::croak("Callback for edit_utf8() must be a code reference") | ||||
896 | unless defined($cb) && ref($cb) eq 'CODE'; | ||||
897 | |||||
898 | local $_ = $self->slurp_utf8; | ||||
899 | $cb->(); | ||||
900 | $self->spew_utf8($_); | ||||
901 | |||||
902 | return; | ||||
903 | } | ||||
904 | |||||
905 | sub edit_raw { $_[2] = { binmode => ":unix" }; goto &edit } | ||||
906 | |||||
907 | #pod =method edit_lines, edit_lines_utf8, edit_lines_raw | ||||
908 | #pod | ||||
909 | #pod path("foo.txt")->edit_lines( \&callback, $options ); | ||||
910 | #pod path("foo.txt")->edit_lines_utf8( \&callback ); | ||||
911 | #pod path("foo.txt")->edit_lines_raw( \&callback ); | ||||
912 | #pod | ||||
913 | #pod These are convenience methods that allow "editing" a file's lines using a | ||||
914 | #pod single callback argument. They iterate over the file: for each line, the | ||||
915 | #pod line is put into a localized C<$_> variable, the callback function is | ||||
916 | #pod executed (without arguments) and then C<$_> is written to a temporary file. | ||||
917 | #pod When iteration is finished, the temporary file is atomically renamed over | ||||
918 | #pod the original. | ||||
919 | #pod | ||||
920 | #pod An optional hash reference may be used to pass options. The only option is | ||||
921 | #pod C<binmode>, which is passed to the method that open handles for reading and | ||||
922 | #pod writing. | ||||
923 | #pod | ||||
924 | #pod C<edit_lines_utf8> and C<edit_lines_raw> act like their respective | ||||
925 | #pod C<slurp_*> and C<spew_*> methods. | ||||
926 | #pod | ||||
927 | #pod Current API available since 0.077. | ||||
928 | #pod | ||||
929 | #pod =cut | ||||
930 | |||||
931 | sub edit_lines { | ||||
932 | my $self = shift; | ||||
933 | my $cb = shift; | ||||
934 | my $args = _get_args( shift, qw/binmode/ ); | ||||
935 | Carp::croak("Callback for edit_lines() must be a code reference") | ||||
936 | unless defined($cb) && ref($cb) eq 'CODE'; | ||||
937 | |||||
938 | my $binmode = $args->{binmode}; | ||||
939 | # get default binmode from caller's lexical scope (see "perldoc open") | ||||
940 | $binmode = ( ( caller(0) )[10] || {} )->{'open>'} unless defined $binmode; | ||||
941 | |||||
942 | # writing need to follow the link and create the tempfile in the same | ||||
943 | # dir for later atomic rename | ||||
944 | my $resolved_path = $self->_resolve_symlinks; | ||||
945 | my $temp = path( $resolved_path . $$ . int( rand( 2**31 ) ) ); | ||||
946 | |||||
947 | my $temp_fh = $temp->filehandle( { exclusive => 1, locked => 1 }, ">", $binmode ); | ||||
948 | my $in_fh = $self->filehandle( { locked => 1 }, '<', $binmode ); | ||||
949 | |||||
950 | local $_; | ||||
951 | while (<$in_fh>) { | ||||
952 | $cb->(); | ||||
953 | $temp_fh->print($_); | ||||
954 | } | ||||
955 | |||||
956 | close $temp_fh or $self->_throw( 'close', $temp ); | ||||
957 | close $in_fh or $self->_throw('close'); | ||||
958 | |||||
959 | return $temp->move($resolved_path); | ||||
960 | } | ||||
961 | |||||
962 | sub edit_lines_raw { $_[2] = { binmode => ":unix" }; goto &edit_lines } | ||||
963 | |||||
964 | sub edit_lines_utf8 { | ||||
965 | $_[2] = { binmode => ":raw:encoding(UTF-8)" }; | ||||
966 | goto &edit_lines; | ||||
967 | } | ||||
968 | |||||
969 | #pod =method exists, is_file, is_dir | ||||
970 | #pod | ||||
971 | #pod if ( path("/tmp")->exists ) { ... } # -e | ||||
972 | #pod if ( path("/tmp")->is_dir ) { ... } # -d | ||||
973 | #pod if ( path("/tmp")->is_file ) { ... } # -e && ! -d | ||||
974 | #pod | ||||
975 | #pod Implements file test operations, this means the file or directory actually has | ||||
976 | #pod to exist on the filesystem. Until then, it's just a path. | ||||
977 | #pod | ||||
978 | #pod B<Note>: C<is_file> is not C<-f> because C<-f> is not the opposite of C<-d>. | ||||
979 | #pod C<-f> means "plain file", excluding symlinks, devices, etc. that often can be | ||||
980 | #pod read just like files. | ||||
981 | #pod | ||||
982 | #pod Use C<-f> instead if you really mean to check for a plain file. | ||||
983 | #pod | ||||
984 | #pod Current API available since 0.053. | ||||
985 | #pod | ||||
986 | #pod =cut | ||||
987 | |||||
988 | sub exists { -e $_[0]->[PATH] } | ||||
989 | |||||
990 | sub is_file { -e $_[0]->[PATH] && !-d _ } | ||||
991 | |||||
992 | sub is_dir { -d $_[0]->[PATH] } | ||||
993 | |||||
994 | #pod =method filehandle | ||||
995 | #pod | ||||
996 | #pod $fh = path("/tmp/foo.txt")->filehandle($mode, $binmode); | ||||
997 | #pod $fh = path("/tmp/foo.txt")->filehandle({ locked => 1 }, $mode, $binmode); | ||||
998 | #pod $fh = path("/tmp/foo.txt")->filehandle({ exclusive => 1 }, $mode, $binmode); | ||||
999 | #pod | ||||
1000 | #pod Returns an open file handle. The C<$mode> argument must be a Perl-style | ||||
1001 | #pod read/write mode string ("<" ,">", ">>", etc.). If a C<$binmode> | ||||
1002 | #pod is given, it is set during the C<open> call. | ||||
1003 | #pod | ||||
1004 | #pod An optional hash reference may be used to pass options. | ||||
1005 | #pod | ||||
1006 | #pod The C<locked> option governs file locking; if true, handles opened for writing, | ||||
1007 | #pod appending or read-write are locked with C<LOCK_EX>; otherwise, they are | ||||
1008 | #pod locked with C<LOCK_SH>. When using C<locked>, ">" or "+>" modes will delay | ||||
1009 | #pod truncation until after the lock is acquired. | ||||
1010 | #pod | ||||
1011 | #pod The C<exclusive> option causes the open() call to fail if the file already | ||||
1012 | #pod exists. This corresponds to the O_EXCL flag to sysopen / open(2). | ||||
1013 | #pod C<exclusive> implies C<locked> and will set it for you if you forget it. | ||||
1014 | #pod | ||||
1015 | #pod See C<openr>, C<openw>, C<openrw>, and C<opena> for sugar. | ||||
1016 | #pod | ||||
1017 | #pod Current API available since 0.066. | ||||
1018 | #pod | ||||
1019 | #pod =cut | ||||
1020 | |||||
1021 | # Note: must put binmode on open line, not subsequent binmode() call, so things | ||||
1022 | # like ":unix" actually stop perlio/crlf from being added | ||||
1023 | |||||
1024 | # spent 3.69ms (2.08+1.61) within Path::Tiny::filehandle which was called 19 times, avg 194µs/call:
# 16 times (1.90ms+1.33ms) by Path::Tiny::spew at line 1865, avg 202µs/call
# 2 times (128µs+198µs) by Path::Tiny::slurp at line 1791, avg 163µs/call
# once (50µs+83µs) by Path::Tiny::append at line 549 | ||||
1025 | 19 | 21µs | my ( $self, @args ) = @_; | ||
1026 | 19 | 33µs | my $args = ( @args && ref $args[0] eq 'HASH' ) ? shift @args : {}; | ||
1027 | 19 | 36µs | 19 | 101µs | $args = _get_args( $args, qw/locked exclusive/ ); # spent 101µs making 19 calls to Path::Tiny::_get_args, avg 5µs/call |
1028 | 19 | 32µs | $args->{locked} = 1 if $args->{exclusive}; | ||
1029 | 19 | 23µs | my ( $opentype, $binmode ) = @args; | ||
1030 | |||||
1031 | 19 | 13µs | $opentype = "<" unless defined $opentype; | ||
1032 | Carp::croak("Invalid file mode '$opentype'") | ||||
1033 | 19 | 77µs | unless grep { $opentype eq $_ } qw/< +< > +> >> +>>/; | ||
1034 | |||||
1035 | 19 | 14µs | $binmode = ( ( caller(0) )[10] || {} )->{ 'open' . substr( $opentype, -1, 1 ) } | ||
1036 | unless defined $binmode; | ||||
1037 | 19 | 16µs | $binmode = "" unless defined $binmode; | ||
1038 | |||||
1039 | 19 | 11µs | my ( $fh, $lock, $trunc ); | ||
1040 | 19 | 50µs | if ( $HAS_FLOCK && $args->{locked} && !$ENV{PERL_PATH_TINY_NO_FLOCK} ) { | ||
1041 | 19 | 23µs | require Fcntl; | ||
1042 | # truncating file modes shouldn't truncate until lock acquired | ||||
1043 | 19 | 45µs | if ( grep { $opentype eq $_ } qw( > +> ) ) { | ||
1044 | # sysopen in write mode without truncation | ||||
1045 | 16 | 138µs | 16 | 26µs | my $flags = $opentype eq ">" ? Fcntl::O_WRONLY() : Fcntl::O_RDWR(); # spent 26µs making 16 calls to Fcntl::O_WRONLY, avg 2µs/call |
1046 | 16 | 128µs | 16 | 16µs | $flags |= Fcntl::O_CREAT(); # spent 16µs making 16 calls to Fcntl::O_CREAT, avg 994ns/call |
1047 | 16 | 88µs | 16 | 10µs | $flags |= Fcntl::O_EXCL() if $args->{exclusive}; # spent 10µs making 16 calls to Fcntl::O_EXCL, avg 656ns/call |
1048 | 16 | 690µs | 16 | 499µs | sysopen( $fh, $self->[PATH], $flags ) or $self->_throw("sysopen"); # spent 499µs making 16 calls to Path::Tiny::CORE:sysopen, avg 31µs/call |
1049 | |||||
1050 | # fix up the binmode since sysopen() can't specify layers like | ||||
1051 | # open() and binmode() can't start with just :unix like open() | ||||
1052 | 16 | 182µs | 16 | 87µs | if ( $binmode =~ s/^:unix// ) { # spent 87µs making 16 calls to Path::Tiny::CORE:subst, avg 5µs/call |
1053 | # eliminate pseudo-layers | ||||
1054 | 16 | 323µs | 16 | 50µs | binmode( $fh, ":raw" ) or $self->_throw("binmode (:raw)"); # spent 50µs making 16 calls to Path::Tiny::CORE:binmode, avg 3µs/call |
1055 | # strip off real layers until only :unix is left | ||||
1056 | 16 | 367µs | 48 | 144µs | while ( 1 < ( my $layers =()= PerlIO::get_layers( $fh, output => 1 ) ) ) { # spent 121µs making 32 calls to PerlIO::get_layers, avg 4µs/call
# spent 23µs making 16 calls to Path::Tiny::CORE:binmode, avg 1µs/call |
1057 | binmode( $fh, ":pop" ) or $self->_throw("binmode (:pop)"); | ||||
1058 | } | ||||
1059 | } | ||||
1060 | |||||
1061 | # apply any remaining binmode layers | ||||
1062 | 16 | 13µs | if ( length $binmode ) { | ||
1063 | binmode( $fh, $binmode ) or $self->_throw("binmode ($binmode)"); | ||||
1064 | } | ||||
1065 | |||||
1066 | # ask for lock and truncation | ||||
1067 | 16 | 132µs | 16 | 15µs | $lock = Fcntl::LOCK_EX(); # spent 15µs making 16 calls to Fcntl::LOCK_EX, avg 912ns/call |
1068 | 16 | 9µs | $trunc = 1; | ||
1069 | } | ||||
1070 | elsif ( $^O eq 'aix' && $opentype eq "<" ) { | ||||
1071 | # AIX can only lock write handles, so upgrade to RW and LOCK_EX if | ||||
1072 | # the file is writable; otherwise give up on locking. N.B. | ||||
1073 | # checking -w before open to determine the open mode is an | ||||
1074 | # unavoidable race condition | ||||
1075 | if ( -w $self->[PATH] ) { | ||||
1076 | $opentype = "+<"; | ||||
1077 | $lock = Fcntl::LOCK_EX(); | ||||
1078 | } | ||||
1079 | } | ||||
1080 | else { | ||||
1081 | 3 | 22µs | 3 | 6µs | $lock = $opentype eq "<" ? Fcntl::LOCK_SH() : Fcntl::LOCK_EX(); # spent 4µs making 2 calls to Fcntl::LOCK_SH, avg 2µs/call
# spent 2µs making 1 call to Fcntl::LOCK_EX |
1082 | } | ||||
1083 | } | ||||
1084 | |||||
1085 | 19 | 18µs | unless ($fh) { | ||
1086 | 3 | 3µs | my $mode = $opentype . $binmode; | ||
1087 | 3 | 195µs | 3 | 131µs | open $fh, $mode, $self->[PATH] or $self->_throw("open ($mode)"); # spent 131µs making 3 calls to Path::Tiny::CORE:open, avg 44µs/call |
1088 | } | ||||
1089 | |||||
1090 | 19 | 329µs | 19 | 240µs | do { flock( $fh, $lock ) or $self->_throw("flock ($lock)") } if $lock; # spent 240µs making 19 calls to Path::Tiny::CORE:flock, avg 13µs/call |
1091 | 19 | 364µs | 16 | 283µs | do { truncate( $fh, 0 ) or $self->_throw("truncate") } if $trunc; # spent 283µs making 16 calls to Path::Tiny::CORE:truncate, avg 18µs/call |
1092 | |||||
1093 | 19 | 128µs | return $fh; | ||
1094 | } | ||||
1095 | |||||
1096 | #pod =method is_absolute, is_relative | ||||
1097 | #pod | ||||
1098 | #pod if ( path("/tmp")->is_absolute ) { ... } | ||||
1099 | #pod if ( path("/tmp")->is_relative ) { ... } | ||||
1100 | #pod | ||||
1101 | #pod Booleans for whether the path appears absolute or relative. | ||||
1102 | #pod | ||||
1103 | #pod Current API available since 0.001. | ||||
1104 | #pod | ||||
1105 | #pod =cut | ||||
1106 | |||||
1107 | sub is_absolute { substr( $_[0]->dirname, 0, 1 ) eq '/' } | ||||
1108 | |||||
1109 | sub is_relative { substr( $_[0]->dirname, 0, 1 ) ne '/' } | ||||
1110 | |||||
1111 | #pod =method is_rootdir | ||||
1112 | #pod | ||||
1113 | #pod while ( ! $path->is_rootdir ) { | ||||
1114 | #pod $path = $path->parent; | ||||
1115 | #pod ... | ||||
1116 | #pod } | ||||
1117 | #pod | ||||
1118 | #pod Boolean for whether the path is the root directory of the volume. I.e. the | ||||
1119 | #pod C<dirname> is C<q[/]> and the C<basename> is C<q[]>. | ||||
1120 | #pod | ||||
1121 | #pod This works even on C<MSWin32> with drives and UNC volumes: | ||||
1122 | #pod | ||||
1123 | #pod path("C:/")->is_rootdir; # true | ||||
1124 | #pod path("//server/share/")->is_rootdir; #true | ||||
1125 | #pod | ||||
1126 | #pod Current API available since 0.038. | ||||
1127 | #pod | ||||
1128 | #pod =cut | ||||
1129 | |||||
1130 | sub is_rootdir { | ||||
1131 | my ($self) = @_; | ||||
1132 | $self->_splitpath unless defined $self->[DIR]; | ||||
1133 | return $self->[DIR] eq '/' && $self->[FILE] eq ''; | ||||
1134 | } | ||||
1135 | |||||
1136 | #pod =method iterator | ||||
1137 | #pod | ||||
1138 | #pod $iter = path("/tmp")->iterator( \%options ); | ||||
1139 | #pod | ||||
1140 | #pod Returns a code reference that walks a directory lazily. Each invocation | ||||
1141 | #pod returns a C<Path::Tiny> object or undef when the iterator is exhausted. | ||||
1142 | #pod | ||||
1143 | #pod $iter = path("/tmp")->iterator; | ||||
1144 | #pod while ( $path = $iter->() ) { | ||||
1145 | #pod ... | ||||
1146 | #pod } | ||||
1147 | #pod | ||||
1148 | #pod The current and parent directory entries ("." and "..") will not | ||||
1149 | #pod be included. | ||||
1150 | #pod | ||||
1151 | #pod If the C<recurse> option is true, the iterator will walk the directory | ||||
1152 | #pod recursively, breadth-first. If the C<follow_symlinks> option is also true, | ||||
1153 | #pod directory links will be followed recursively. There is no protection against | ||||
1154 | #pod loops when following links. If a directory is not readable, it will not be | ||||
1155 | #pod followed. | ||||
1156 | #pod | ||||
1157 | #pod The default is the same as: | ||||
1158 | #pod | ||||
1159 | #pod $iter = path("/tmp")->iterator( { | ||||
1160 | #pod recurse => 0, | ||||
1161 | #pod follow_symlinks => 0, | ||||
1162 | #pod } ); | ||||
1163 | #pod | ||||
1164 | #pod For a more powerful, recursive iterator with built-in loop avoidance, see | ||||
1165 | #pod L<Path::Iterator::Rule>. | ||||
1166 | #pod | ||||
1167 | #pod See also L</visit>. | ||||
1168 | #pod | ||||
1169 | #pod Current API available since 0.016. | ||||
1170 | #pod | ||||
1171 | #pod =cut | ||||
1172 | |||||
1173 | sub iterator { | ||||
1174 | my $self = shift; | ||||
1175 | my $args = _get_args( shift, qw/recurse follow_symlinks/ ); | ||||
1176 | my @dirs = $self; | ||||
1177 | my $current; | ||||
1178 | return sub { | ||||
1179 | my $next; | ||||
1180 | while (@dirs) { | ||||
1181 | if ( ref $dirs[0] eq 'Path::Tiny' ) { | ||||
1182 | if ( !-r $dirs[0] ) { | ||||
1183 | # Directory is missing or not readable, so skip it. There | ||||
1184 | # is still a race condition possible between the check and | ||||
1185 | # the opendir, but we can't easily differentiate between | ||||
1186 | # error cases that are OK to skip and those that we want | ||||
1187 | # to be exceptions, so we live with the race and let opendir | ||||
1188 | # be fatal. | ||||
1189 | shift @dirs and next; | ||||
1190 | } | ||||
1191 | $current = $dirs[0]; | ||||
1192 | my $dh; | ||||
1193 | opendir( $dh, $current->[PATH] ) | ||||
1194 | or $self->_throw( 'opendir', $current->[PATH] ); | ||||
1195 | $dirs[0] = $dh; | ||||
1196 | if ( -l $current->[PATH] && !$args->{follow_symlinks} ) { | ||||
1197 | # Symlink attack! It was a real dir, but is now a symlink! | ||||
1198 | # N.B. we check *after* opendir so the attacker has to win | ||||
1199 | # two races: replace dir with symlink before opendir and | ||||
1200 | # replace symlink with dir before -l check above | ||||
1201 | shift @dirs and next; | ||||
1202 | } | ||||
1203 | } | ||||
1204 | while ( defined( $next = readdir $dirs[0] ) ) { | ||||
1205 | next if $next eq '.' || $next eq '..'; | ||||
1206 | my $path = $current->child($next); | ||||
1207 | push @dirs, $path | ||||
1208 | if $args->{recurse} && -d $path && !( !$args->{follow_symlinks} && -l $path ); | ||||
1209 | return $path; | ||||
1210 | } | ||||
1211 | shift @dirs; | ||||
1212 | } | ||||
1213 | return; | ||||
1214 | }; | ||||
1215 | } | ||||
1216 | |||||
1217 | #pod =method lines, lines_raw, lines_utf8 | ||||
1218 | #pod | ||||
1219 | #pod @contents = path("/tmp/foo.txt")->lines; | ||||
1220 | #pod @contents = path("/tmp/foo.txt")->lines(\%options); | ||||
1221 | #pod @contents = path("/tmp/foo.txt")->lines_raw; | ||||
1222 | #pod @contents = path("/tmp/foo.txt")->lines_utf8; | ||||
1223 | #pod | ||||
1224 | #pod @contents = path("/tmp/foo.txt")->lines( { chomp => 1, count => 4 } ); | ||||
1225 | #pod | ||||
1226 | #pod Returns a list of lines from a file. Optionally takes a hash-reference of | ||||
1227 | #pod options. Valid options are C<binmode>, C<count> and C<chomp>. | ||||
1228 | #pod | ||||
1229 | #pod If C<binmode> is provided, it will be set on the handle prior to reading. | ||||
1230 | #pod | ||||
1231 | #pod If a positive C<count> is provided, that many lines will be returned from the | ||||
1232 | #pod start of the file. If a negative C<count> is provided, the entire file will be | ||||
1233 | #pod read, but only C<abs(count)> will be kept and returned. If C<abs(count)> | ||||
1234 | #pod exceeds the number of lines in the file, all lines will be returned. | ||||
1235 | #pod | ||||
1236 | #pod If C<chomp> is set, any end-of-line character sequences (C<CR>, C<CRLF>, or | ||||
1237 | #pod C<LF>) will be removed from the lines returned. | ||||
1238 | #pod | ||||
1239 | #pod Because the return is a list, C<lines> in scalar context will return the number | ||||
1240 | #pod of lines (and throw away the data). | ||||
1241 | #pod | ||||
1242 | #pod $number_of_lines = path("/tmp/foo.txt")->lines; | ||||
1243 | #pod | ||||
1244 | #pod C<lines_raw> is like C<lines> with a C<binmode> of C<:raw>. We use C<:raw> | ||||
1245 | #pod instead of C<:unix> so PerlIO buffering can manage reading by line. | ||||
1246 | #pod | ||||
1247 | #pod C<lines_utf8> is like C<lines> with a C<binmode> of C<:raw:encoding(UTF-8)> | ||||
1248 | #pod (or L<PerlIO::utf8_strict>). If L<Unicode::UTF8> 0.58+ is installed, a raw | ||||
1249 | #pod UTF-8 slurp will be done and then the lines will be split. This is | ||||
1250 | #pod actually faster than relying on C<:encoding(UTF-8)>, though a bit memory | ||||
1251 | #pod intensive. If memory use is a concern, consider C<openr_utf8> and | ||||
1252 | #pod iterating directly on the handle. | ||||
1253 | #pod | ||||
1254 | #pod Current API available since 0.065. | ||||
1255 | #pod | ||||
1256 | #pod =cut | ||||
1257 | |||||
1258 | sub lines { | ||||
1259 | my $self = shift; | ||||
1260 | my $args = _get_args( shift, qw/binmode chomp count/ ); | ||||
1261 | my $binmode = $args->{binmode}; | ||||
1262 | $binmode = ( ( caller(0) )[10] || {} )->{'open<'} unless defined $binmode; | ||||
1263 | my $fh = $self->filehandle( { locked => 1 }, "<", $binmode ); | ||||
1264 | my $chomp = $args->{chomp}; | ||||
1265 | # XXX more efficient to read @lines then chomp(@lines) vs map? | ||||
1266 | if ( $args->{count} ) { | ||||
1267 | my ( $counter, $mod, @result ) = ( 0, abs( $args->{count} ) ); | ||||
1268 | while ( my $line = <$fh> ) { | ||||
1269 | $line =~ s/(?:\x{0d}?\x{0a}|\x{0d})$// if $chomp; | ||||
1270 | $result[ $counter++ ] = $line; | ||||
1271 | # for positive count, terminate after right number of lines | ||||
1272 | last if $counter == $args->{count}; | ||||
1273 | # for negative count, eventually wrap around in the result array | ||||
1274 | $counter %= $mod; | ||||
1275 | } | ||||
1276 | # reorder results if full and wrapped somewhere in the middle | ||||
1277 | splice( @result, 0, 0, splice( @result, $counter ) ) | ||||
1278 | if @result == $mod && $counter % $mod; | ||||
1279 | return @result; | ||||
1280 | } | ||||
1281 | elsif ($chomp) { | ||||
1282 | return map { s/(?:\x{0d}?\x{0a}|\x{0d})$//; $_ } <$fh>; ## no critic | ||||
1283 | } | ||||
1284 | else { | ||||
1285 | return wantarray ? <$fh> : ( my $count =()= <$fh> ); | ||||
1286 | } | ||||
1287 | } | ||||
1288 | |||||
1289 | sub lines_raw { | ||||
1290 | my $self = shift; | ||||
1291 | my $args = _get_args( shift, qw/binmode chomp count/ ); | ||||
1292 | if ( $args->{chomp} && !$args->{count} ) { | ||||
1293 | return split /\n/, slurp_raw($self); ## no critic | ||||
1294 | } | ||||
1295 | else { | ||||
1296 | $args->{binmode} = ":raw"; | ||||
1297 | return lines( $self, $args ); | ||||
1298 | } | ||||
1299 | } | ||||
1300 | |||||
1301 | 1 | 4µs | 1 | 1µs | my $CRLF = qr/(?:\x{0d}?\x{0a}|\x{0d})/; # spent 1µs making 1 call to Path::Tiny::CORE:qr |
1302 | |||||
1303 | sub lines_utf8 { | ||||
1304 | my $self = shift; | ||||
1305 | my $args = _get_args( shift, qw/binmode chomp count/ ); | ||||
1306 | if ( ( defined($HAS_UU) ? $HAS_UU : ( $HAS_UU = _check_UU() ) ) | ||||
1307 | && $args->{chomp} | ||||
1308 | && !$args->{count} ) | ||||
1309 | { | ||||
1310 | my $slurp = slurp_utf8($self); | ||||
1311 | $slurp =~ s/$CRLF$//; # like chomp, but full CR?LF|CR | ||||
1312 | return split $CRLF, $slurp, -1; ## no critic | ||||
1313 | } | ||||
1314 | elsif ( defined($HAS_PU) ? $HAS_PU : ( $HAS_PU = _check_PU() ) ) { | ||||
1315 | $args->{binmode} = ":unix:utf8_strict"; | ||||
1316 | return lines( $self, $args ); | ||||
1317 | } | ||||
1318 | else { | ||||
1319 | $args->{binmode} = ":raw:encoding(UTF-8)"; | ||||
1320 | return lines( $self, $args ); | ||||
1321 | } | ||||
1322 | } | ||||
1323 | |||||
1324 | #pod =method mkpath | ||||
1325 | #pod | ||||
1326 | #pod path("foo/bar/baz")->mkpath; | ||||
1327 | #pod path("foo/bar/baz")->mkpath( \%options ); | ||||
1328 | #pod | ||||
1329 | #pod Like calling C<make_path> from L<File::Path>. An optional hash reference | ||||
1330 | #pod is passed through to C<make_path>. Errors will be trapped and an exception | ||||
1331 | #pod thrown. Returns the list of directories created or an empty list if | ||||
1332 | #pod the directories already exist, just like C<make_path>. | ||||
1333 | #pod | ||||
1334 | #pod Current API available since 0.001. | ||||
1335 | #pod | ||||
1336 | #pod =cut | ||||
1337 | |||||
1338 | sub mkpath { | ||||
1339 | my ( $self, $args ) = @_; | ||||
1340 | $args = {} unless ref $args eq 'HASH'; | ||||
1341 | my $err; | ||||
1342 | $args->{error} = \$err unless defined $args->{error}; | ||||
1343 | require File::Path; | ||||
1344 | my @dirs = File::Path::make_path( $self->[PATH], $args ); | ||||
1345 | if ( $err && @$err ) { | ||||
1346 | my ( $file, $message ) = %{ $err->[0] }; | ||||
1347 | Carp::croak("mkpath failed for $file: $message"); | ||||
1348 | } | ||||
1349 | return @dirs; | ||||
1350 | } | ||||
1351 | |||||
1352 | #pod =method move | ||||
1353 | #pod | ||||
1354 | #pod path("foo.txt")->move("bar.txt"); | ||||
1355 | #pod | ||||
1356 | #pod Move the current path to the given destination path using Perl's | ||||
1357 | #pod built-in L<rename|perlfunc/rename> function. Returns the result | ||||
1358 | #pod of the C<rename> function (except it throws an exception if it fails). | ||||
1359 | #pod | ||||
1360 | #pod Current API available since 0.001. | ||||
1361 | #pod | ||||
1362 | #pod =cut | ||||
1363 | |||||
1364 | # spent 695µs (184+510) within Path::Tiny::move which was called 16 times, avg 43µs/call:
# 16 times (184µs+510µs) by Path::Tiny::spew at line 1869, avg 43µs/call | ||||
1365 | 16 | 10µs | my ( $self, $dst ) = @_; | ||
1366 | |||||
1367 | 16 | 683µs | 32 | 533µs | return rename( $self->[PATH], $dst ) # spent 510µs making 16 calls to Path::Tiny::CORE:rename, avg 32µs/call
# spent 22µs making 16 calls to Path::Tiny::__ANON__[Path/Tiny.pm:30], avg 1µs/call |
1368 | || $self->_throw( 'rename', $self->[PATH] . "' -> '$dst" ); | ||||
1369 | } | ||||
1370 | |||||
1371 | #pod =method openr, openw, openrw, opena | ||||
1372 | #pod | ||||
1373 | #pod $fh = path("foo.txt")->openr($binmode); # read | ||||
1374 | #pod $fh = path("foo.txt")->openr_raw; | ||||
1375 | #pod $fh = path("foo.txt")->openr_utf8; | ||||
1376 | #pod | ||||
1377 | #pod $fh = path("foo.txt")->openw($binmode); # write | ||||
1378 | #pod $fh = path("foo.txt")->openw_raw; | ||||
1379 | #pod $fh = path("foo.txt")->openw_utf8; | ||||
1380 | #pod | ||||
1381 | #pod $fh = path("foo.txt")->opena($binmode); # append | ||||
1382 | #pod $fh = path("foo.txt")->opena_raw; | ||||
1383 | #pod $fh = path("foo.txt")->opena_utf8; | ||||
1384 | #pod | ||||
1385 | #pod $fh = path("foo.txt")->openrw($binmode); # read/write | ||||
1386 | #pod $fh = path("foo.txt")->openrw_raw; | ||||
1387 | #pod $fh = path("foo.txt")->openrw_utf8; | ||||
1388 | #pod | ||||
1389 | #pod Returns a file handle opened in the specified mode. The C<openr> style methods | ||||
1390 | #pod take a single C<binmode> argument. All of the C<open*> methods have | ||||
1391 | #pod C<open*_raw> and C<open*_utf8> equivalents that use C<:raw> and | ||||
1392 | #pod C<:raw:encoding(UTF-8)>, respectively. | ||||
1393 | #pod | ||||
1394 | #pod An optional hash reference may be used to pass options. The only option is | ||||
1395 | #pod C<locked>. If true, handles opened for writing, appending or read-write are | ||||
1396 | #pod locked with C<LOCK_EX>; otherwise, they are locked for C<LOCK_SH>. | ||||
1397 | #pod | ||||
1398 | #pod $fh = path("foo.txt")->openrw_utf8( { locked => 1 } ); | ||||
1399 | #pod | ||||
1400 | #pod See L</filehandle> for more on locking. | ||||
1401 | #pod | ||||
1402 | #pod Current API available since 0.011. | ||||
1403 | #pod | ||||
1404 | #pod =cut | ||||
1405 | |||||
1406 | # map method names to corresponding open mode | ||||
1407 | 1 | 3µs | my %opens = ( | ||
1408 | opena => ">>", | ||||
1409 | openr => "<", | ||||
1410 | openw => ">", | ||||
1411 | openrw => "+<" | ||||
1412 | ); | ||||
1413 | |||||
1414 | 1 | 6µs | while ( my ( $k, $v ) = each %opens ) { | ||
1415 | 2 | 2.57ms | 2 | 64µs | # spent 38µs (12+26) within Path::Tiny::BEGIN@1415 which was called:
# once (12µs+26µs) by RBM::BEGIN@5 at line 1415 # spent 38µs making 1 call to Path::Tiny::BEGIN@1415
# spent 26µs making 1 call to strict::unimport |
1416 | # must check for lexical IO mode hint | ||||
1417 | *{$k} = sub { | ||||
1418 | my ( $self, @args ) = @_; | ||||
1419 | my $args = ( @args && ref $args[0] eq 'HASH' ) ? shift @args : {}; | ||||
1420 | $args = _get_args( $args, qw/locked/ ); | ||||
1421 | my ($binmode) = @args; | ||||
1422 | $binmode = ( ( caller(0) )[10] || {} )->{ 'open' . substr( $v, -1, 1 ) } | ||||
1423 | unless defined $binmode; | ||||
1424 | $self->filehandle( $args, $v, $binmode ); | ||||
1425 | 4 | 11µs | }; | ||
1426 | *{ $k . "_raw" } = sub { | ||||
1427 | my ( $self, @args ) = @_; | ||||
1428 | my $args = ( @args && ref $args[0] eq 'HASH' ) ? shift @args : {}; | ||||
1429 | $args = _get_args( $args, qw/locked/ ); | ||||
1430 | $self->filehandle( $args, $v, ":raw" ); | ||||
1431 | 4 | 10µs | }; | ||
1432 | *{ $k . "_utf8" } = sub { | ||||
1433 | my ( $self, @args ) = @_; | ||||
1434 | my $args = ( @args && ref $args[0] eq 'HASH' ) ? shift @args : {}; | ||||
1435 | $args = _get_args( $args, qw/locked/ ); | ||||
1436 | $self->filehandle( $args, $v, ":raw:encoding(UTF-8)" ); | ||||
1437 | 4 | 10µs | }; | ||
1438 | } | ||||
1439 | |||||
1440 | #pod =method parent | ||||
1441 | #pod | ||||
1442 | #pod $parent = path("foo/bar/baz")->parent; # foo/bar | ||||
1443 | #pod $parent = path("foo/wibble.txt")->parent; # foo | ||||
1444 | #pod | ||||
1445 | #pod $parent = path("foo/bar/baz")->parent(2); # foo | ||||
1446 | #pod | ||||
1447 | #pod Returns a C<Path::Tiny> object corresponding to the parent directory of the | ||||
1448 | #pod original directory or file. An optional positive integer argument is the number | ||||
1449 | #pod of parent directories upwards to return. C<parent> by itself is equivalent to | ||||
1450 | #pod C<parent(1)>. | ||||
1451 | #pod | ||||
1452 | #pod Current API available since 0.014. | ||||
1453 | #pod | ||||
1454 | #pod =cut | ||||
1455 | |||||
1456 | # XXX this is ugly and coverage is incomplete. I think it's there for windows | ||||
1457 | # so need to check coverage there and compare | ||||
1458 | sub parent { | ||||
1459 | my ( $self, $level ) = @_; | ||||
1460 | $level = 1 unless defined $level && $level > 0; | ||||
1461 | $self->_splitpath unless defined $self->[FILE]; | ||||
1462 | my $parent; | ||||
1463 | if ( length $self->[FILE] ) { | ||||
1464 | if ( $self->[FILE] eq '.' || $self->[FILE] eq ".." ) { | ||||
1465 | $parent = path( $self->[PATH] . "/.." ); | ||||
1466 | } | ||||
1467 | else { | ||||
1468 | $parent = path( _non_empty( $self->[VOL] . $self->[DIR] ) ); | ||||
1469 | } | ||||
1470 | } | ||||
1471 | elsif ( length $self->[DIR] ) { | ||||
1472 | # because of symlinks, any internal updir requires us to | ||||
1473 | # just add more updirs at the end | ||||
1474 | if ( $self->[DIR] =~ m{(?:^\.\./|/\.\./|/\.\.$)} ) { | ||||
1475 | $parent = path( $self->[VOL] . $self->[DIR] . "/.." ); | ||||
1476 | } | ||||
1477 | else { | ||||
1478 | ( my $dir = $self->[DIR] ) =~ s{/[^\/]+/$}{/}; | ||||
1479 | $parent = path( $self->[VOL] . $dir ); | ||||
1480 | } | ||||
1481 | } | ||||
1482 | else { | ||||
1483 | $parent = path( _non_empty( $self->[VOL] ) ); | ||||
1484 | } | ||||
1485 | return $level == 1 ? $parent : $parent->parent( $level - 1 ); | ||||
1486 | } | ||||
1487 | |||||
1488 | sub _non_empty { | ||||
1489 | my ($string) = shift; | ||||
1490 | return ( ( defined($string) && length($string) ) ? $string : "." ); | ||||
1491 | } | ||||
1492 | |||||
1493 | #pod =method realpath | ||||
1494 | #pod | ||||
1495 | #pod $real = path("/baz/foo/../bar")->realpath; | ||||
1496 | #pod $real = path("foo/../bar")->realpath; | ||||
1497 | #pod | ||||
1498 | #pod Returns a new C<Path::Tiny> object with all symbolic links and upward directory | ||||
1499 | #pod parts resolved using L<Cwd>'s C<realpath>. Compared to C<absolute>, this is | ||||
1500 | #pod more expensive as it must actually consult the filesystem. | ||||
1501 | #pod | ||||
1502 | #pod If the parent path can't be resolved (e.g. if it includes directories that | ||||
1503 | #pod don't exist), an exception will be thrown: | ||||
1504 | #pod | ||||
1505 | #pod $real = path("doesnt_exist/foo")->realpath; # dies | ||||
1506 | #pod | ||||
1507 | #pod However, if the parent path exists and only the last component (e.g. filename) | ||||
1508 | #pod doesn't exist, the realpath will be the realpath of the parent plus the | ||||
1509 | #pod non-existent last component: | ||||
1510 | #pod | ||||
1511 | #pod $real = path("./aasdlfasdlf")->realpath; # works | ||||
1512 | #pod | ||||
1513 | #pod The underlying L<Cwd> module usually worked this way on Unix, but died on | ||||
1514 | #pod Windows (and some Unixes) if the full path didn't exist. As of version 0.064, | ||||
1515 | #pod it's safe to use anywhere. | ||||
1516 | #pod | ||||
1517 | #pod Current API available since 0.001. | ||||
1518 | #pod | ||||
1519 | #pod =cut | ||||
1520 | |||||
1521 | # Win32 and some Unixes need parent path resolved separately so realpath | ||||
1522 | # doesn't throw an error resolving non-existent basename | ||||
1523 | sub realpath { | ||||
1524 | my $self = shift; | ||||
1525 | $self = $self->_resolve_symlinks; | ||||
1526 | require Cwd; | ||||
1527 | $self->_splitpath if !defined $self->[FILE]; | ||||
1528 | my $check_parent = | ||||
1529 | length $self->[FILE] && $self->[FILE] ne '.' && $self->[FILE] ne '..'; | ||||
1530 | my $realpath = eval { | ||||
1531 | # pure-perl Cwd can carp | ||||
1532 | local $SIG{__WARN__} = sub { }; | ||||
1533 | Cwd::realpath( $check_parent ? $self->parent->[PATH] : $self->[PATH] ); | ||||
1534 | }; | ||||
1535 | # parent realpath must exist; not all Cwd::realpath will error if it doesn't | ||||
1536 | $self->_throw("resolving realpath") | ||||
1537 | unless defined $realpath && length $realpath && -e $realpath; | ||||
1538 | return ( $check_parent ? path( $realpath, $self->[FILE] ) : path($realpath) ); | ||||
1539 | } | ||||
1540 | |||||
1541 | #pod =method relative | ||||
1542 | #pod | ||||
1543 | #pod $rel = path("/tmp/foo/bar")->relative("/tmp"); # foo/bar | ||||
1544 | #pod | ||||
1545 | #pod Returns a C<Path::Tiny> object with a path relative to a new base path | ||||
1546 | #pod given as an argument. If no argument is given, the current directory will | ||||
1547 | #pod be used as the new base path. | ||||
1548 | #pod | ||||
1549 | #pod If either path is already relative, it will be made absolute based on the | ||||
1550 | #pod current directly before determining the new relative path. | ||||
1551 | #pod | ||||
1552 | #pod The algorithm is roughly as follows: | ||||
1553 | #pod | ||||
1554 | #pod =for :list | ||||
1555 | #pod * If the original and new base path are on different volumes, an exception | ||||
1556 | #pod will be thrown. | ||||
1557 | #pod * If the original and new base are identical, the relative path is C<".">. | ||||
1558 | #pod * If the new base subsumes the original, the relative path is the original | ||||
1559 | #pod path with the new base chopped off the front | ||||
1560 | #pod * If the new base does not subsume the original, a common prefix path is | ||||
1561 | #pod determined (possibly the root directory) and the relative path will | ||||
1562 | #pod consist of updirs (C<"..">) to reach the common prefix, followed by the | ||||
1563 | #pod original path less the common prefix. | ||||
1564 | #pod | ||||
1565 | #pod Unlike C<File::Spec::abs2rel>, in the last case above, the calculation based | ||||
1566 | #pod on a common prefix takes into account symlinks that could affect the updir | ||||
1567 | #pod process. Given an original path "/A/B" and a new base "/A/C", | ||||
1568 | #pod (where "A", "B" and "C" could each have multiple path components): | ||||
1569 | #pod | ||||
1570 | #pod =for :list | ||||
1571 | #pod * Symlinks in "A" don't change the result unless the last component of A is | ||||
1572 | #pod a symlink and the first component of "C" is an updir. | ||||
1573 | #pod * Symlinks in "B" don't change the result and will exist in the result as | ||||
1574 | #pod given. | ||||
1575 | #pod * Symlinks and updirs in "C" must be resolved to actual paths, taking into | ||||
1576 | #pod account the possibility that not all path components might exist on the | ||||
1577 | #pod filesystem. | ||||
1578 | #pod | ||||
1579 | #pod Current API available since 0.001. New algorithm (that accounts for | ||||
1580 | #pod symlinks) available since 0.079. | ||||
1581 | #pod | ||||
1582 | #pod =cut | ||||
1583 | |||||
1584 | sub relative { | ||||
1585 | my ( $self, $base ) = @_; | ||||
1586 | $base = path( defined $base && length $base ? $base : '.' ); | ||||
1587 | |||||
1588 | # relative paths must be converted to absolute first | ||||
1589 | $self = $self->absolute if $self->is_relative; | ||||
1590 | $base = $base->absolute if $base->is_relative; | ||||
1591 | |||||
1592 | # normalize volumes if they exist | ||||
1593 | $self = $self->absolute if !length $self->volume && length $base->volume; | ||||
1594 | $base = $base->absolute if length $self->volume && !length $base->volume; | ||||
1595 | |||||
1596 | # can't make paths relative across volumes | ||||
1597 | if ( !_same( $self->volume, $base->volume ) ) { | ||||
1598 | Carp::croak("relative() can't cross volumes: '$self' vs '$base'"); | ||||
1599 | } | ||||
1600 | |||||
1601 | # if same absolute path, relative is current directory | ||||
1602 | return path(".") if _same( $self->[PATH], $base->[PATH] ); | ||||
1603 | |||||
1604 | # if base is a prefix of self, chop prefix off self | ||||
1605 | if ( $base->subsumes($self) ) { | ||||
1606 | $base = "" if $base->is_rootdir; | ||||
1607 | my $relative = "$self"; | ||||
1608 | $relative =~ s{\A\Q$base/}{}; | ||||
1609 | return path($relative); | ||||
1610 | } | ||||
1611 | |||||
1612 | # base is not a prefix, so must find a common prefix (even if root) | ||||
1613 | my ( @common, @self_parts, @base_parts ); | ||||
1614 | @base_parts = split /\//, $base->_just_filepath; | ||||
1615 | |||||
1616 | # if self is rootdir, then common directory is root (shown as empty | ||||
1617 | # string for later joins); otherwise, must be computed from path parts. | ||||
1618 | if ( $self->is_rootdir ) { | ||||
1619 | @common = (""); | ||||
1620 | shift @base_parts; | ||||
1621 | } | ||||
1622 | else { | ||||
1623 | @self_parts = split /\//, $self->_just_filepath; | ||||
1624 | |||||
1625 | while ( @self_parts && @base_parts && _same( $self_parts[0], $base_parts[0] ) ) { | ||||
1626 | push @common, shift @base_parts; | ||||
1627 | shift @self_parts; | ||||
1628 | } | ||||
1629 | } | ||||
1630 | |||||
1631 | # if there are any symlinks from common to base, we have a problem, as | ||||
1632 | # you can't guarantee that updir from base reaches the common prefix; | ||||
1633 | # we must resolve symlinks and try again; likewise, any updirs are | ||||
1634 | # a problem as it throws off calculation of updirs needed to get from | ||||
1635 | # self's path to the common prefix. | ||||
1636 | if ( my $new_base = $self->_resolve_between( \@common, \@base_parts ) ) { | ||||
1637 | return $self->relative($new_base); | ||||
1638 | } | ||||
1639 | |||||
1640 | # otherwise, symlinks in common or from common to A don't matter as | ||||
1641 | # those don't involve updirs | ||||
1642 | my @new_path = ( ("..") x ( 0+ @base_parts ), @self_parts ); | ||||
1643 | return path(@new_path); | ||||
1644 | } | ||||
1645 | |||||
1646 | sub _just_filepath { | ||||
1647 | my $self = shift; | ||||
1648 | my $self_vol = $self->volume; | ||||
1649 | return "$self" if !length $self_vol; | ||||
1650 | |||||
1651 | ( my $self_path = "$self" ) =~ s{\A\Q$self_vol}{}; | ||||
1652 | |||||
1653 | return $self_path; | ||||
1654 | } | ||||
1655 | |||||
1656 | sub _resolve_between { | ||||
1657 | my ( $self, $common, $base ) = @_; | ||||
1658 | my $path = $self->volume . join( "/", @$common ); | ||||
1659 | my $changed = 0; | ||||
1660 | for my $p (@$base) { | ||||
1661 | $path .= "/$p"; | ||||
1662 | if ( $p eq '..' ) { | ||||
1663 | $changed = 1; | ||||
1664 | if ( -e $path ) { | ||||
1665 | $path = path($path)->realpath->[PATH]; | ||||
1666 | } | ||||
1667 | else { | ||||
1668 | $path =~ s{/[^/]+/..$}{/}; | ||||
1669 | } | ||||
1670 | } | ||||
1671 | if ( -l $path ) { | ||||
1672 | $changed = 1; | ||||
1673 | $path = path($path)->realpath->[PATH]; | ||||
1674 | } | ||||
1675 | } | ||||
1676 | return $changed ? path($path) : undef; | ||||
1677 | } | ||||
1678 | |||||
1679 | #pod =method remove | ||||
1680 | #pod | ||||
1681 | #pod path("foo.txt")->remove; | ||||
1682 | #pod | ||||
1683 | #pod This is just like C<unlink>, except for its error handling: if the path does | ||||
1684 | #pod not exist, it returns false; if deleting the file fails, it throws an | ||||
1685 | #pod exception. | ||||
1686 | #pod | ||||
1687 | #pod Current API available since 0.012. | ||||
1688 | #pod | ||||
1689 | #pod =cut | ||||
1690 | |||||
1691 | sub remove { | ||||
1692 | my $self = shift; | ||||
1693 | |||||
1694 | return 0 if !-e $self->[PATH] && !-l $self->[PATH]; | ||||
1695 | |||||
1696 | return unlink( $self->[PATH] ) || $self->_throw('unlink'); | ||||
1697 | } | ||||
1698 | |||||
1699 | #pod =method remove_tree | ||||
1700 | #pod | ||||
1701 | #pod # directory | ||||
1702 | #pod path("foo/bar/baz")->remove_tree; | ||||
1703 | #pod path("foo/bar/baz")->remove_tree( \%options ); | ||||
1704 | #pod path("foo/bar/baz")->remove_tree( { safe => 0 } ); # force remove | ||||
1705 | #pod | ||||
1706 | #pod Like calling C<remove_tree> from L<File::Path>, but defaults to C<safe> mode. | ||||
1707 | #pod An optional hash reference is passed through to C<remove_tree>. Errors will be | ||||
1708 | #pod trapped and an exception thrown. Returns the number of directories deleted, | ||||
1709 | #pod just like C<remove_tree>. | ||||
1710 | #pod | ||||
1711 | #pod If you want to remove a directory only if it is empty, use the built-in | ||||
1712 | #pod C<rmdir> function instead. | ||||
1713 | #pod | ||||
1714 | #pod rmdir path("foo/bar/baz/"); | ||||
1715 | #pod | ||||
1716 | #pod Current API available since 0.013. | ||||
1717 | #pod | ||||
1718 | #pod =cut | ||||
1719 | |||||
1720 | sub remove_tree { | ||||
1721 | my ( $self, $args ) = @_; | ||||
1722 | return 0 if !-e $self->[PATH] && !-l $self->[PATH]; | ||||
1723 | $args = {} unless ref $args eq 'HASH'; | ||||
1724 | my $err; | ||||
1725 | $args->{error} = \$err unless defined $args->{error}; | ||||
1726 | $args->{safe} = 1 unless defined $args->{safe}; | ||||
1727 | require File::Path; | ||||
1728 | my $count = File::Path::remove_tree( $self->[PATH], $args ); | ||||
1729 | |||||
1730 | if ( $err && @$err ) { | ||||
1731 | my ( $file, $message ) = %{ $err->[0] }; | ||||
1732 | Carp::croak("remove_tree failed for $file: $message"); | ||||
1733 | } | ||||
1734 | return $count; | ||||
1735 | } | ||||
1736 | |||||
1737 | #pod =method sibling | ||||
1738 | #pod | ||||
1739 | #pod $foo = path("/tmp/foo.txt"); | ||||
1740 | #pod $sib = $foo->sibling("bar.txt"); # /tmp/bar.txt | ||||
1741 | #pod $sib = $foo->sibling("baz", "bam.txt"); # /tmp/baz/bam.txt | ||||
1742 | #pod | ||||
1743 | #pod Returns a new C<Path::Tiny> object relative to the parent of the original. | ||||
1744 | #pod This is slightly more efficient than C<< $path->parent->child(...) >>. | ||||
1745 | #pod | ||||
1746 | #pod Current API available since 0.058. | ||||
1747 | #pod | ||||
1748 | #pod =cut | ||||
1749 | |||||
1750 | sub sibling { | ||||
1751 | my $self = shift; | ||||
1752 | return path( $self->parent->[PATH], @_ ); | ||||
1753 | } | ||||
1754 | |||||
1755 | #pod =method slurp, slurp_raw, slurp_utf8 | ||||
1756 | #pod | ||||
1757 | #pod $data = path("foo.txt")->slurp; | ||||
1758 | #pod $data = path("foo.txt")->slurp( {binmode => ":raw"} ); | ||||
1759 | #pod $data = path("foo.txt")->slurp_raw; | ||||
1760 | #pod $data = path("foo.txt")->slurp_utf8; | ||||
1761 | #pod | ||||
1762 | #pod Reads file contents into a scalar. Takes an optional hash reference which may | ||||
1763 | #pod be used to pass options. The only available option is C<binmode>, which is | ||||
1764 | #pod passed to C<binmode()> on the handle used for reading. | ||||
1765 | #pod | ||||
1766 | #pod C<slurp_raw> is like C<slurp> with a C<binmode> of C<:unix> for | ||||
1767 | #pod a fast, unbuffered, raw read. | ||||
1768 | #pod | ||||
1769 | #pod C<slurp_utf8> is like C<slurp> with a C<binmode> of | ||||
1770 | #pod C<:unix:encoding(UTF-8)> (or L<PerlIO::utf8_strict>). If L<Unicode::UTF8> | ||||
1771 | #pod 0.58+ is installed, a raw slurp will be done instead and the result decoded | ||||
1772 | #pod with C<Unicode::UTF8>. This is just as strict and is roughly an order of | ||||
1773 | #pod magnitude faster than using C<:encoding(UTF-8)>. | ||||
1774 | #pod | ||||
1775 | #pod B<Note>: C<slurp> and friends lock the filehandle before slurping. If | ||||
1776 | #pod you plan to slurp from a file created with L<File::Temp>, be sure to | ||||
1777 | #pod close other handles or open without locking to avoid a deadlock: | ||||
1778 | #pod | ||||
1779 | #pod my $tempfile = File::Temp->new(EXLOCK => 0); | ||||
1780 | #pod my $guts = path($tempfile)->slurp; | ||||
1781 | #pod | ||||
1782 | #pod Current API available since 0.004. | ||||
1783 | #pod | ||||
1784 | #pod =cut | ||||
1785 | |||||
1786 | # spent 14.9ms (156µs+14.7) within Path::Tiny::slurp which was called 2 times, avg 7.45ms/call:
# 2 times (156µs+14.7ms) by RBM::sha256file at line 1805, avg 7.45ms/call | ||||
1787 | 2 | 800ns | my $self = shift; | ||
1788 | 2 | 7µs | 2 | 21µs | my $args = _get_args( shift, qw/binmode/ ); # spent 21µs making 2 calls to Path::Tiny::_get_args, avg 10µs/call |
1789 | 2 | 2µs | my $binmode = $args->{binmode}; | ||
1790 | 2 | 1µs | $binmode = ( ( caller(0) )[10] || {} )->{'open<'} unless defined $binmode; | ||
1791 | 2 | 15µs | 2 | 325µs | my $fh = $self->filehandle( { locked => 1 }, "<", $binmode ); # spent 325µs making 2 calls to Path::Tiny::filehandle, avg 163µs/call |
1792 | 2 | 12µs | 2 | 3µs | if ( ( defined($binmode) ? $binmode : "" ) eq ":unix" # spent 3µs making 2 calls to Path::Tiny::CORE:ftsize, avg 2µs/call |
1793 | and my $size = -s $fh ) | ||||
1794 | { | ||||
1795 | 2 | 600ns | my $buf; | ||
1796 | 2 | 14.4ms | 2 | 14.4ms | read $fh, $buf, $size; # File::Slurp in a nutshell # spent 14.4ms making 2 calls to Path::Tiny::CORE:read, avg 7.19ms/call |
1797 | 2 | 89µs | return $buf; | ||
1798 | } | ||||
1799 | else { | ||||
1800 | local $/; | ||||
1801 | return scalar <$fh>; | ||||
1802 | } | ||||
1803 | } | ||||
1804 | |||||
1805 | 4 | 17µs | 2 | 14.9ms | # spent 16µs within Path::Tiny::slurp_raw which was called 2 times, avg 8µs/call:
# 2 times (16µs+0s) by RBM::sha256file at line 619 of /root/tor-browser-build/rbm/lib/RBM.pm, avg 8µs/call # spent 14.9ms making 2 calls to Path::Tiny::slurp, avg 7.45ms/call |
1806 | |||||
1807 | sub slurp_utf8 { | ||||
1808 | if ( defined($HAS_UU) ? $HAS_UU : ( $HAS_UU = _check_UU() ) ) { | ||||
1809 | return Unicode::UTF8::decode_utf8( slurp( $_[0], { binmode => ":unix" } ) ); | ||||
1810 | } | ||||
1811 | elsif ( defined($HAS_PU) ? $HAS_PU : ( $HAS_PU = _check_PU() ) ) { | ||||
1812 | $_[1] = { binmode => ":unix:utf8_strict" }; | ||||
1813 | goto &slurp; | ||||
1814 | } | ||||
1815 | else { | ||||
1816 | $_[1] = { binmode => ":raw:encoding(UTF-8)" }; | ||||
1817 | goto &slurp; | ||||
1818 | } | ||||
1819 | } | ||||
1820 | |||||
1821 | #pod =method spew, spew_raw, spew_utf8 | ||||
1822 | #pod | ||||
1823 | #pod path("foo.txt")->spew(@data); | ||||
1824 | #pod path("foo.txt")->spew(\@data); | ||||
1825 | #pod path("foo.txt")->spew({binmode => ":raw"}, @data); | ||||
1826 | #pod path("foo.txt")->spew_raw(@data); | ||||
1827 | #pod path("foo.txt")->spew_utf8(@data); | ||||
1828 | #pod | ||||
1829 | #pod Writes data to a file atomically. The file is written to a temporary file in | ||||
1830 | #pod the same directory, then renamed over the original. An optional hash reference | ||||
1831 | #pod may be used to pass options. The only option is C<binmode>, which is passed to | ||||
1832 | #pod C<binmode()> on the handle used for writing. | ||||
1833 | #pod | ||||
1834 | #pod C<spew_raw> is like C<spew> with a C<binmode> of C<:unix> for a fast, | ||||
1835 | #pod unbuffered, raw write. | ||||
1836 | #pod | ||||
1837 | #pod C<spew_utf8> is like C<spew> with a C<binmode> of C<:unix:encoding(UTF-8)> | ||||
1838 | #pod (or L<PerlIO::utf8_strict>). If L<Unicode::UTF8> 0.58+ is installed, a raw | ||||
1839 | #pod spew will be done instead on the data encoded with C<Unicode::UTF8>. | ||||
1840 | #pod | ||||
1841 | #pod B<NOTE>: because the file is written to a temporary file and then renamed, the | ||||
1842 | #pod new file will wind up with permissions based on your current umask. This is a | ||||
1843 | #pod feature to protect you from a race condition that would otherwise give | ||||
1844 | #pod different permissions than you might expect. If you really want to keep the | ||||
1845 | #pod original mode flags, use L</append> with the C<truncate> option. | ||||
1846 | #pod | ||||
1847 | #pod Current API available since 0.011. | ||||
1848 | #pod | ||||
1849 | #pod =cut | ||||
1850 | |||||
1851 | # XXX add "unsafe" option to disable flocking and atomic? Check benchmarks on append() first. | ||||
1852 | # spent 16.7ms (1.24+15.4) within Path::Tiny::spew which was called 16 times, avg 1.04ms/call:
# 16 times (1.24ms+15.4ms) by Path::Tiny::spew_utf8 at line 1880, avg 1.04ms/call | ||||
1853 | 16 | 17µs | my ( $self, @data ) = @_; | ||
1854 | 16 | 35µs | my $args = ( @data && ref $data[0] eq 'HASH' ) ? shift @data : {}; | ||
1855 | 16 | 57µs | 16 | 174µs | $args = _get_args( $args, qw/binmode/ ); # spent 174µs making 16 calls to Path::Tiny::_get_args, avg 11µs/call |
1856 | 16 | 20µs | my $binmode = $args->{binmode}; | ||
1857 | # get default binmode from caller's lexical scope (see "perldoc open") | ||||
1858 | 16 | 11µs | $binmode = ( ( caller(0) )[10] || {} )->{'open>'} unless defined $binmode; | ||
1859 | |||||
1860 | # spewing need to follow the link | ||||
1861 | # and create the tempfile in the same dir | ||||
1862 | 16 | 95µs | 16 | 355µs | my $resolved_path = $self->_resolve_symlinks; # spent 355µs making 16 calls to Path::Tiny::_resolve_symlinks, avg 22µs/call |
1863 | |||||
1864 | 16 | 182µs | 32 | 8.56ms | my $temp = path( $resolved_path . $$ . int( rand( 2**31 ) ) ); # spent 8.49ms making 16 calls to Path::Tiny::path, avg 531µs/call
# spent 74µs making 16 calls to Path::Tiny::__ANON__[Path/Tiny.pm:30], avg 5µs/call |
1865 | 16 | 119µs | 16 | 3.23ms | my $fh = $temp->filehandle( { exclusive => 1, locked => 1 }, ">", $binmode ); # spent 3.23ms making 16 calls to Path::Tiny::filehandle, avg 202µs/call |
1866 | 16 | 837µs | 16 | 702µs | print {$fh} map { ref eq 'ARRAY' ? @$_ : $_ } @data; # spent 702µs making 16 calls to Path::Tiny::CORE:print, avg 44µs/call |
1867 | 16 | 1.84ms | 16 | 1.72ms | close $fh or $self->_throw( 'close', $temp->[PATH] ); # spent 1.72ms making 16 calls to Path::Tiny::CORE:close, avg 107µs/call |
1868 | |||||
1869 | 16 | 240µs | 16 | 695µs | return $temp->move($resolved_path); # spent 695µs making 16 calls to Path::Tiny::move, avg 43µs/call |
1870 | } | ||||
1871 | |||||
1872 | sub spew_raw { splice @_, 1, 0, { binmode => ":unix" }; goto &spew } | ||||
1873 | |||||
1874 | # spent 18.1ms (581µs+17.5) within Path::Tiny::spew_utf8 which was called 16 times, avg 1.13ms/call:
# 15 times (540µs+16.8ms) by RBM::run_script at line 466 of /root/tor-browser-build/rbm/lib/RBM.pm, avg 1.16ms/call
# once (42µs+726µs) by RBM::build_run at line 1071 of /root/tor-browser-build/rbm/lib/RBM.pm | ||||
1875 | 16 | 115µs | 1 | 748µs | if ( defined($HAS_UU) ? $HAS_UU : ( $HAS_UU = _check_UU() ) ) { # spent 748µs making 1 call to Path::Tiny::_check_UU |
1876 | 16 | 9µs | my $self = shift; | ||
1877 | spew( | ||||
1878 | $self, | ||||
1879 | { binmode => ":unix" }, | ||||
1880 | 32 | 578µs | 32 | 16.8ms | map { Unicode::UTF8::encode_utf8($_) } map { ref eq 'ARRAY' ? @$_ : $_ } @_ # spent 16.7ms making 16 calls to Path::Tiny::spew, avg 1.04ms/call
# spent 111µs making 16 calls to Unicode::UTF8::encode_utf8, avg 7µs/call |
1881 | ); | ||||
1882 | } | ||||
1883 | elsif ( defined($HAS_PU) ? $HAS_PU : ( $HAS_PU = _check_PU() ) ) { | ||||
1884 | splice @_, 1, 0, { binmode => ":unix:utf8_strict" }; | ||||
1885 | goto &spew; | ||||
1886 | } | ||||
1887 | else { | ||||
1888 | splice @_, 1, 0, { binmode => ":unix:encoding(UTF-8)" }; | ||||
1889 | goto &spew; | ||||
1890 | } | ||||
1891 | } | ||||
1892 | |||||
1893 | #pod =method stat, lstat | ||||
1894 | #pod | ||||
1895 | #pod $stat = path("foo.txt")->stat; | ||||
1896 | #pod $stat = path("/some/symlink")->lstat; | ||||
1897 | #pod | ||||
1898 | #pod Like calling C<stat> or C<lstat> from L<File::stat>. | ||||
1899 | #pod | ||||
1900 | #pod Current API available since 0.001. | ||||
1901 | #pod | ||||
1902 | #pod =cut | ||||
1903 | |||||
1904 | # XXX break out individual stat() components as subs? | ||||
1905 | sub stat { | ||||
1906 | my $self = shift; | ||||
1907 | require File::stat; | ||||
1908 | return File::stat::stat( $self->[PATH] ) || $self->_throw('stat'); | ||||
1909 | } | ||||
1910 | |||||
1911 | sub lstat { | ||||
1912 | my $self = shift; | ||||
1913 | require File::stat; | ||||
1914 | return File::stat::lstat( $self->[PATH] ) || $self->_throw('lstat'); | ||||
1915 | } | ||||
1916 | |||||
1917 | #pod =method stringify | ||||
1918 | #pod | ||||
1919 | #pod $path = path("foo.txt"); | ||||
1920 | #pod say $path->stringify; # same as "$path" | ||||
1921 | #pod | ||||
1922 | #pod Returns a string representation of the path. Unlike C<canonpath>, this method | ||||
1923 | #pod returns the path standardized with Unix-style C</> directory separators. | ||||
1924 | #pod | ||||
1925 | #pod Current API available since 0.001. | ||||
1926 | #pod | ||||
1927 | #pod =cut | ||||
1928 | |||||
1929 | sub stringify { $_[0]->[PATH] } | ||||
1930 | |||||
1931 | #pod =method subsumes | ||||
1932 | #pod | ||||
1933 | #pod path("foo/bar")->subsumes("foo/bar/baz"); # true | ||||
1934 | #pod path("/foo/bar")->subsumes("/foo/baz"); # false | ||||
1935 | #pod | ||||
1936 | #pod Returns true if the first path is a prefix of the second path at a directory | ||||
1937 | #pod boundary. | ||||
1938 | #pod | ||||
1939 | #pod This B<does not> resolve parent directory entries (C<..>) or symlinks: | ||||
1940 | #pod | ||||
1941 | #pod path("foo/bar")->subsumes("foo/bar/../baz"); # true | ||||
1942 | #pod | ||||
1943 | #pod If such things are important to you, ensure that both paths are resolved to | ||||
1944 | #pod the filesystem with C<realpath>: | ||||
1945 | #pod | ||||
1946 | #pod my $p1 = path("foo/bar")->realpath; | ||||
1947 | #pod my $p2 = path("foo/bar/../baz")->realpath; | ||||
1948 | #pod if ( $p1->subsumes($p2) ) { ... } | ||||
1949 | #pod | ||||
1950 | #pod Current API available since 0.048. | ||||
1951 | #pod | ||||
1952 | #pod =cut | ||||
1953 | |||||
1954 | sub subsumes { | ||||
1955 | my $self = shift; | ||||
1956 | Carp::croak("subsumes() requires a defined, positive-length argument") | ||||
1957 | unless defined $_[0]; | ||||
1958 | my $other = path(shift); | ||||
1959 | |||||
1960 | # normalize absolute vs relative | ||||
1961 | if ( $self->is_absolute && !$other->is_absolute ) { | ||||
1962 | $other = $other->absolute; | ||||
1963 | } | ||||
1964 | elsif ( $other->is_absolute && !$self->is_absolute ) { | ||||
1965 | $self = $self->absolute; | ||||
1966 | } | ||||
1967 | |||||
1968 | # normalize volume vs non-volume; do this after absolute path | ||||
1969 | # adjustments above since that might add volumes already | ||||
1970 | if ( length $self->volume && !length $other->volume ) { | ||||
1971 | $other = $other->absolute; | ||||
1972 | } | ||||
1973 | elsif ( length $other->volume && !length $self->volume ) { | ||||
1974 | $self = $self->absolute; | ||||
1975 | } | ||||
1976 | |||||
1977 | if ( $self->[PATH] eq '.' ) { | ||||
1978 | return !!1; # cwd subsumes everything relative | ||||
1979 | } | ||||
1980 | elsif ( $self->is_rootdir ) { | ||||
1981 | # a root directory ("/", "c:/") already ends with a separator | ||||
1982 | return $other->[PATH] =~ m{^\Q$self->[PATH]\E}; | ||||
1983 | } | ||||
1984 | else { | ||||
1985 | # exact match or prefix breaking at a separator | ||||
1986 | return $other->[PATH] =~ m{^\Q$self->[PATH]\E(?:/|$)}; | ||||
1987 | } | ||||
1988 | } | ||||
1989 | |||||
1990 | #pod =method touch | ||||
1991 | #pod | ||||
1992 | #pod path("foo.txt")->touch; | ||||
1993 | #pod path("foo.txt")->touch($epoch_secs); | ||||
1994 | #pod | ||||
1995 | #pod Like the Unix C<touch> utility. Creates the file if it doesn't exist, or else | ||||
1996 | #pod changes the modification and access times to the current time. If the first | ||||
1997 | #pod argument is the epoch seconds then it will be used. | ||||
1998 | #pod | ||||
1999 | #pod Returns the path object so it can be easily chained with other methods: | ||||
2000 | #pod | ||||
2001 | #pod # won't die if foo.txt doesn't exist | ||||
2002 | #pod $content = path("foo.txt")->touch->slurp; | ||||
2003 | #pod | ||||
2004 | #pod Current API available since 0.015. | ||||
2005 | #pod | ||||
2006 | #pod =cut | ||||
2007 | |||||
2008 | sub touch { | ||||
2009 | my ( $self, $epoch ) = @_; | ||||
2010 | if ( !-e $self->[PATH] ) { | ||||
2011 | my $fh = $self->openw; | ||||
2012 | close $fh or $self->_throw('close'); | ||||
2013 | } | ||||
2014 | if ( defined $epoch ) { | ||||
2015 | utime $epoch, $epoch, $self->[PATH] | ||||
2016 | or $self->_throw("utime ($epoch)"); | ||||
2017 | } | ||||
2018 | else { | ||||
2019 | # literal undef prevents warnings :-( | ||||
2020 | utime undef, undef, $self->[PATH] | ||||
2021 | or $self->_throw("utime ()"); | ||||
2022 | } | ||||
2023 | return $self; | ||||
2024 | } | ||||
2025 | |||||
2026 | #pod =method touchpath | ||||
2027 | #pod | ||||
2028 | #pod path("bar/baz/foo.txt")->touchpath; | ||||
2029 | #pod | ||||
2030 | #pod Combines C<mkpath> and C<touch>. Creates the parent directory if it doesn't exist, | ||||
2031 | #pod before touching the file. Returns the path object like C<touch> does. | ||||
2032 | #pod | ||||
2033 | #pod Current API available since 0.022. | ||||
2034 | #pod | ||||
2035 | #pod =cut | ||||
2036 | |||||
2037 | sub touchpath { | ||||
2038 | my ($self) = @_; | ||||
2039 | my $parent = $self->parent; | ||||
2040 | $parent->mkpath unless $parent->exists; | ||||
2041 | $self->touch; | ||||
2042 | } | ||||
2043 | |||||
2044 | #pod =method visit | ||||
2045 | #pod | ||||
2046 | #pod path("/tmp")->visit( \&callback, \%options ); | ||||
2047 | #pod | ||||
2048 | #pod Executes a callback for each child of a directory. It returns a hash | ||||
2049 | #pod reference with any state accumulated during iteration. | ||||
2050 | #pod | ||||
2051 | #pod The options are the same as for L</iterator> (which it uses internally): | ||||
2052 | #pod C<recurse> and C<follow_symlinks>. Both default to false. | ||||
2053 | #pod | ||||
2054 | #pod The callback function will receive a C<Path::Tiny> object as the first argument | ||||
2055 | #pod and a hash reference to accumulate state as the second argument. For example: | ||||
2056 | #pod | ||||
2057 | #pod # collect files sizes | ||||
2058 | #pod my $sizes = path("/tmp")->visit( | ||||
2059 | #pod sub { | ||||
2060 | #pod my ($path, $state) = @_; | ||||
2061 | #pod return if $path->is_dir; | ||||
2062 | #pod $state->{$path} = -s $path; | ||||
2063 | #pod }, | ||||
2064 | #pod { recurse => 1 } | ||||
2065 | #pod ); | ||||
2066 | #pod | ||||
2067 | #pod For convenience, the C<Path::Tiny> object will also be locally aliased as the | ||||
2068 | #pod C<$_> global variable: | ||||
2069 | #pod | ||||
2070 | #pod # print paths matching /foo/ | ||||
2071 | #pod path("/tmp")->visit( sub { say if /foo/ }, { recurse => 1} ); | ||||
2072 | #pod | ||||
2073 | #pod If the callback returns a B<reference> to a false scalar value, iteration will | ||||
2074 | #pod terminate. This is not the same as "pruning" a directory search; this just | ||||
2075 | #pod stops all iteration and returns the state hash reference. | ||||
2076 | #pod | ||||
2077 | #pod # find up to 10 files larger than 100K | ||||
2078 | #pod my $files = path("/tmp")->visit( | ||||
2079 | #pod sub { | ||||
2080 | #pod my ($path, $state) = @_; | ||||
2081 | #pod $state->{$path}++ if -s $path > 102400 | ||||
2082 | #pod return \0 if keys %$state == 10; | ||||
2083 | #pod }, | ||||
2084 | #pod { recurse => 1 } | ||||
2085 | #pod ); | ||||
2086 | #pod | ||||
2087 | #pod If you want more flexible iteration, use a module like L<Path::Iterator::Rule>. | ||||
2088 | #pod | ||||
2089 | #pod Current API available since 0.062. | ||||
2090 | #pod | ||||
2091 | #pod =cut | ||||
2092 | |||||
2093 | sub visit { | ||||
2094 | my $self = shift; | ||||
2095 | my $cb = shift; | ||||
2096 | my $args = _get_args( shift, qw/recurse follow_symlinks/ ); | ||||
2097 | Carp::croak("Callback for visit() must be a code reference") | ||||
2098 | unless defined($cb) && ref($cb) eq 'CODE'; | ||||
2099 | my $next = $self->iterator($args); | ||||
2100 | my $state = {}; | ||||
2101 | while ( my $file = $next->() ) { | ||||
2102 | local $_ = $file; | ||||
2103 | my $r = $cb->( $file, $state ); | ||||
2104 | last if ref($r) eq 'SCALAR' && !$$r; | ||||
2105 | } | ||||
2106 | return $state; | ||||
2107 | } | ||||
2108 | |||||
2109 | #pod =method volume | ||||
2110 | #pod | ||||
2111 | #pod $vol = path("/tmp/foo.txt")->volume; # "" | ||||
2112 | #pod $vol = path("C:/tmp/foo.txt")->volume; # "C:" | ||||
2113 | #pod | ||||
2114 | #pod Returns the volume portion of the path. This is equivalent | ||||
2115 | #pod to what L<File::Spec> would give from C<splitpath> and thus | ||||
2116 | #pod usually is the empty string on Unix-like operating systems or the | ||||
2117 | #pod drive letter for an absolute path on C<MSWin32>. | ||||
2118 | #pod | ||||
2119 | #pod Current API available since 0.001. | ||||
2120 | #pod | ||||
2121 | #pod =cut | ||||
2122 | |||||
2123 | sub volume { | ||||
2124 | my ($self) = @_; | ||||
2125 | $self->_splitpath unless defined $self->[VOL]; | ||||
2126 | return $self->[VOL]; | ||||
2127 | } | ||||
2128 | |||||
2129 | package Path::Tiny::Error; | ||||
2130 | |||||
2131 | 1 | 1µs | our @CARP_NOT = qw/Path::Tiny/; | ||
2132 | |||||
2133 | 2 | 190µs | 2 | 78µs | # spent 44µs (11+33) within Path::Tiny::Error::BEGIN@2133 which was called:
# once (11µs+33µs) by RBM::BEGIN@5 at line 2133 # spent 44µs making 1 call to Path::Tiny::Error::BEGIN@2133
# spent 33µs making 1 call to overload::import |
2134 | |||||
2135 | sub throw { | ||||
2136 | my ( $class, $op, $file, $err ) = @_; | ||||
2137 | chomp( my $trace = Carp::shortmess ); | ||||
2138 | my $msg = "Error $op on '$file': $err$trace\n"; | ||||
2139 | die bless { op => $op, file => $file, err => $err, msg => $msg }, $class; | ||||
2140 | } | ||||
2141 | |||||
2142 | 1 | 25µs | 1; | ||
2143 | |||||
2144 | |||||
2145 | # vim: ts=4 sts=4 sw=4 et: | ||||
2146 | |||||
2147 | __END__ | ||||
sub Path::Tiny::CORE:binmode; # opcode | |||||
sub Path::Tiny::CORE:close; # opcode | |||||
# spent 240µs within Path::Tiny::CORE:flock which was called 19 times, avg 13µs/call:
# 19 times (240µs+0s) by Path::Tiny::filehandle at line 1090, avg 13µs/call | |||||
# spent 93µs within Path::Tiny::CORE:ftlink which was called 16 times, avg 6µs/call:
# 16 times (93µs+0s) by Path::Tiny::_resolve_symlinks at line 441, avg 6µs/call | |||||
# spent 3µs within Path::Tiny::CORE:ftsize which was called 2 times, avg 2µs/call:
# 2 times (3µs+0s) by Path::Tiny::slurp at line 1792, avg 2µs/call | |||||
# spent 26µs within Path::Tiny::CORE:match which was called 35 times, avg 746ns/call:
# 35 times (26µs+0s) by Path::Tiny::path at line 262, avg 746ns/call | |||||
# spent 131µs within Path::Tiny::CORE:open which was called 3 times, avg 44µs/call:
# 3 times (131µs+0s) by Path::Tiny::filehandle at line 1087, avg 44µs/call | |||||
sub Path::Tiny::CORE:print; # opcode | |||||
# spent 7µs within Path::Tiny::CORE:qr which was called 6 times, avg 1µs/call:
# once (2µs+0s) by RBM::BEGIN@5 at line 68
# once (1µs+0s) by RBM::BEGIN@5 at line 69
# once (1µs+0s) by RBM::BEGIN@5 at line 1301
# once (800ns+0s) by RBM::BEGIN@5 at line 70
# once (700ns+0s) by RBM::BEGIN@5 at line 72
# once (500ns+0s) by RBM::BEGIN@5 at line 71 | |||||
# spent 14.4ms within Path::Tiny::CORE:read which was called 2 times, avg 7.19ms/call:
# 2 times (14.4ms+0s) by Path::Tiny::slurp at line 1796, avg 7.19ms/call | |||||
sub Path::Tiny::CORE:regcomp; # opcode | |||||
# spent 510µs (488+22) within Path::Tiny::CORE:rename which was called 16 times, avg 32µs/call:
# 16 times (488µs+22µs) by Path::Tiny::move at line 1367, avg 32µs/call | |||||
sub Path::Tiny::CORE:subst; # opcode | |||||
# spent 499µs within Path::Tiny::CORE:sysopen which was called 16 times, avg 31µs/call:
# 16 times (499µs+0s) by Path::Tiny::filehandle at line 1048, avg 31µs/call | |||||
# spent 283µs within Path::Tiny::CORE:truncate which was called 16 times, avg 18µs/call:
# 16 times (283µs+0s) by Path::Tiny::filehandle at line 1091, avg 18µs/call |