| 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 | Path::Tiny::CORE:read (opcode) |
| 35 | 5 | 2 | 9.17ms | 9.38ms | Path::Tiny::path |
| 1 | 1 | 1 | 4.26ms | 5.44ms | Path::Tiny::BEGIN@14 |
| 19 | 3 | 1 | 2.08ms | 3.69ms | Path::Tiny::filehandle |
| 1 | 1 | 1 | 1.99ms | 7.42ms | Path::Tiny::BEGIN@13 |
| 17 | 2 | 1 | 1.73ms | 1.73ms | Path::Tiny::CORE:close (opcode) |
| 16 | 1 | 1 | 1.24ms | 16.7ms | Path::Tiny::spew |
| 1 | 1 | 1 | 1.22ms | 1.32ms | Path::Tiny::BEGIN@29 |
| 17 | 2 | 1 | 1.22ms | 1.22ms | Path::Tiny::CORE:print (opcode) |
| 16 | 2 | 1 | 581µs | 18.1ms | Path::Tiny::spew_utf8 |
| 16 | 1 | 1 | 499µs | 499µs | Path::Tiny::CORE:sysopen (opcode) |
| 16 | 1 | 1 | 488µs | 510µs | Path::Tiny::CORE:rename (opcode) |
| 1 | 1 | 1 | 346µs | 748µs | Path::Tiny::_check_UU |
| 39 | 5 | 1 | 310µs | 310µs | Path::Tiny::_get_args |
| 16 | 1 | 1 | 283µs | 283µs | Path::Tiny::CORE:truncate (opcode) |
| 16 | 1 | 1 | 262µs | 355µs | Path::Tiny::_resolve_symlinks |
| 19 | 1 | 1 | 240µs | 240µs | Path::Tiny::CORE:flock (opcode) |
| 16 | 1 | 1 | 184µs | 695µs | Path::Tiny::move |
| 2 | 1 | 1 | 156µs | 14.9ms | Path::Tiny::slurp |
| 3 | 1 | 1 | 131µs | 131µs | Path::Tiny::CORE:open (opcode) |
| 51 | 2 | 1 | 119µs | 119µs | Path::Tiny::CORE:subst (opcode) |
| 35 | 1 | 1 | 105µs | 105µs | Path::Tiny::_is_root |
| 32 | 2 | 1 | 96µs | 96µs | Path::Tiny::__ANON__[:30] |
| 16 | 1 | 1 | 93µs | 93µs | Path::Tiny::CORE:ftlink (opcode) |
| 32 | 2 | 1 | 73µs | 73µs | Path::Tiny::CORE:binmode (opcode) |
| 2 | 2 | 1 | 55µs | 55µs | Path::Tiny::CORE:regcomp (opcode) |
| 1 | 1 | 1 | 36µs | 702µs | Path::Tiny::append |
| 1 | 1 | 1 | 32µs | 750µs | Path::Tiny::append_utf8 |
| 35 | 1 | 1 | 26µs | 26µs | Path::Tiny::CORE:match (opcode) |
| 2 | 1 | 1 | 16µs | 16µs | Path::Tiny::slurp_raw |
| 1 | 1 | 1 | 12µs | 142µs | Path::Tiny::BEGIN@19 |
| 1 | 1 | 1 | 12µs | 12µs | RBM::BEGIN@1 |
| 1 | 1 | 1 | 12µs | 23µs | Path::Tiny::BEGIN@12 |
| 1 | 1 | 1 | 12µs | 38µs | Path::Tiny::BEGIN@1415 |
| 1 | 1 | 1 | 11µs | 44µs | Path::Tiny::Error::BEGIN@2133 |
| 6 | 6 | 1 | 7µs | 7µs | Path::Tiny::CORE:qr (opcode) |
| 1 | 1 | 1 | 6µs | 15µs | Path::Tiny::BEGIN@11 |
| 1 | 1 | 1 | 6µs | 22µs | Path::Tiny::BEGIN@38 |
| 1 | 1 | 1 | 6µs | 33µs | flock::BEGIN@132 |
| 1 | 1 | 1 | 6µs | 7µs | RBM::BEGIN@2 |
| 1 | 1 | 1 | 5µs | 5µs | Path::Tiny::BEGIN@94 |
| 1 | 1 | 1 | 4µs | 16µs | RBM::BEGIN@3.1 |
| 2 | 1 | 1 | 3µs | 3µs | Path::Tiny::CORE:ftsize (opcode) |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::Error::__ANON__[:2133] |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::Error::throw |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::FREEZE |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::THAW |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::__ANON__[:1214] |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::__ANON__[:1425] |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::__ANON__[:1431] |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::__ANON__[:1437] |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::__ANON__[:1532] |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::__ANON__[:95] |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::_check_PU |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::_just_filepath |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::_non_empty |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::_parse_file_temp_args |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::_resolve_between |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::_splitpath |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::_symbolic_chmod |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::_throw |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::_win32_vol |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::absolute |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::append_raw |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::assert |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::basename |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::cached_temp |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::canonpath |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::child |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::children |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::chmod |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::copy |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::cwd |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::digest |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::dirname |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::edit |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::edit_lines |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::edit_lines_raw |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::edit_lines_utf8 |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::edit_raw |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::edit_utf8 |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::exists |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::is_absolute |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::is_dir |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::is_file |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::is_relative |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::is_rootdir |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::iterator |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::lines |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::lines_raw |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::lines_utf8 |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::lstat |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::mkpath |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::new |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::parent |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::realpath |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::relative |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::remove |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::remove_tree |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::rootdir |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::sibling |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::slurp_utf8 |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::spew_raw |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::stat |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::stringify |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::subsumes |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::tempdir |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::tempfile |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::touch |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::touchpath |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::visit |
| 0 | 0 | 0 | 0s | 0s | Path::Tiny::volume |
| 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 |