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