=begin pod

=TITLE class IO::Path

=SUBTITLE File or directory path

=for code :skip-test
class IO::Path is Cool does IO { }

Built-in class for file and directory paths.

Conceptually, an C<IO::Path> object consists of a volume (think C<C:> on
windows), a directory, and a basename.

C<IO::Path> supports both purely textual operations, and operations that
access the file system.

The current working directory is available as an C<IO::Path> object in C<$*CWD>.

The behavior of C<IO::Path> is dependent on the operating system it runs on;
to get reproducible behavior across operating systems, you can use one of its
subclasses instead: L<IO::Spec::Unix>, L<IO::Spec::Win32>,
L<IO::Spec::Cygwin>, L<IO::Spec::QNX>.

The rest of this document silently assumes Unix semantics in its examples,
unless when stated otherwise.

=head1 Methods

=head2 method new

    multi method new(Str:D $path, IO::Spec :$SPEC = $*SPEC, Str() :$CWD = $*CWD)
    multi method new(
        :$basename!, :$dirname = '.', :$volume = ''
        IO::Spec :$SPEC = $*SPEC, Str() :$CWD = $*CWD
    )

Creates a new C<IO::Path> object from a path string (which is being parsed for
volume, directory name and basename), or from volume, directory name and
basename passed as named arguments.

The path's operation will be performed using C<:$SPEC> semantics (defaults to
current L«C<$*SPEC>|/language/variables#Dynamic_variables») and will use
C<:$CWD> as the directory the path is relative to (defaults to
L«C<$*CWD>|/language/variables#Dynamic_variables»).

=head2 attribute CWD

    IO::Path.new("foo", :CWD</home/camelia>)
        .IO.CWD.say; # OUTPUT: «/home/camelia␤»

Read-only. Contains implicit or explicit value of C<:$CWD> argument to C<.new>.

=head2 attribute SPEC

    IO::Path.new("foo", :SPEC(IO::Spec::Unix.new))
        .IO.SPEC.^name.say; # OUTPUT: «IO::Spec::Unix␤»

Read-only. Contains implicit or explicit value of C<:$SPEC> argument to C<.new>.

=head2 method ACCEPTS

    multi method ACCEPTS(IO::Path:D: Cool:D $other --> Bool:D)

Coerces the argument to C<IO::Path>, if necessary. Returns C<True> if
L«C<.absolute>|/routine/absolute» method on both paths returns the same string.
B<NOTE:> it's possible for two paths that superficially point to the same
resource to NOT smartmatch as C<True>, if they were constructed differently and
were never fully resolved:

    say "foo/../bar".IO ~~ "bar".IO # False

The reason is the two paths above may point to different resources when fully
resolved (e.g. if C<foo> is a symlink). Resolve the paths before smartmatching
to check they point to same resource:

    say "foo/../bar".IO.resolve(:completely) ~~ "bar".IO.resolve(:completely) # True

=head2 method basename

    method basename(IO::Path:D:)

Returns the basename part of the path object, which is the name of the
filesystem object itself that is referenced by the path.

    "docs/README.pod".IO.basename.say; # OUTPUT: «README.pod␤»
    "/tmp/".IO.basename.say;           # OUTPUT: «tmp␤»

Note that in L«C<IO::Spec::Win32>|/type/IO::Spec::Win32» semantics, the
C<basename> of a Windows share is C<\>, not the name of the share itself:

    IO::Path::Win32.new('//server/share').basename.say; # OUTPUT: «\␤»

=head2 method concat-with

    method concat-with(IO::Path:D: Str() $what --> IO::Path:D)

Concatenates a path fragment to the invocant and returns the resultant
C<IO::Path>.

    "foo/bar".IO.concat-with("meow")    .resolve.relative.say; # OUTPUT: «foo/bar/meow␤»
    "foo/bar".IO.concat-with("/meow")   .resolve.relative.say; # OUTPUT: «foo/bar/meow␤»
    "foo/bar".IO.concat-with("meow.txt").resolve.relative.say; # OUTPUT: «foo/bar/meow.txt␤»
    "foo/bar".IO.concat-with("../meow") .resolve.relative.say; # OUTPUT: «foo/meow␤»
    "foo/bar".IO.concat-with("../../")  .resolve.relative.say; # OUTPUT: «.␤»

=head2 method extension

Defined as:

    multi extension(IO::Path:D:                                         --> Str:D)
    multi extension(IO::Path:D:               Int :$parts               --> Str:D)
    multi extension(IO::Path:D:             Range :$parts               --> Str:D)
    multi extension(IO::Path:D: Str $subst,   Int :$parts, Str :$joiner --> IO::Path:D)
    multi extension(IO::Path:D: Str $subst, Range :$parts, Str :$joiner --> IO::Path:D)

Returns the extension consisting of C<$parts> parts (defaults to C<1>),
where a "part" is defined
as a dot followed by possibly-empty string up to the end of the string, or
previous part. That is C<"foo.tar.gz"> has an extension of two parts: first part
is C<"gz"> and second part is C<"tar"> and calling
C<"foo.tar.gz".IO.extension: :2parts> gives C<"tar.gz">. If an extension with
the specified number of C<$parts> is not found, returns an empty string.

C<$parts> can be a L«C<Range>|/type/Range», specifying the minimum number of
parts and maximum number of parts the extension should have. The routine will
attempt to much the most parts it can. If C<$parts> range's endpoints that are
smaller than C<0> they'll be treated as C<0>; implementations may treat
endpoints larger than C<2⁶³-1> as C<2⁶³-1>. Ranges with C<NaN> or
L«C<Str>|/type/Str» endpoints will cause an exception to be thrown.

If C<$subst> is provided, the extension will be instead replaced with C<$subst>
and a new C<IO::Path> object will be returned. It will be joined to the file's
name with C<$joiner>, which defaults to an empty string when C<$subst> is
an empty string and to C<"."> when C<$subst> is not empty. B<Note:> if as
the result of replacement the L«C<basename>|/routine/basename» of the path
ends up being empty, it will be assumed to be C<.> (a single dot).

    # Getting an extension:
    say "foo.tar.gz".IO.extension;               # OUTPUT: «gz␤»
    say "foo.tar.gz".IO.extension: :2parts;      # OUTPUT: «tar.gz␤»
    say "foo.tar.gz".IO.extension: :parts(^5);   # OUTPUT: «tar.gz␤»
    say "foo.tar.gz".IO.extension: :parts(0..1); # OUTPUT: «gz␤»

    # Replacing an extension
    say "foo.tar.gz".IO.extension: '';                # OUTPUT: «"foo.tar".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP';             # OUTPUT: «"foo.tar.ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :0parts;    # OUTPUT: «"foo.tar.gz.ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :2parts;    # OUTPUT: «"foo.ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :parts(^5); # OUTPUT: «"foo.ZIP".IO␤»

    # Replacing an extension using non-standard joiner:
    say "foo.tar.gz".IO.extension: '',    :joiner<_>;  # OUTPUT: «"foo.tar_".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :joiner<_>;  # OUTPUT: «"foo.tar_ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :joiner<_>,
                                           :2parts;     # OUTPUT: «"foo_ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :joiner<_>,
                                           :parts(^5);  # OUTPUT: «"foo_ZIP".IO␤»

    # EDGE CASES:

    # There is no 5-part extension, so returned value is an empty string
    say "foo.tar.gz".IO.extension: :5parts; # OUTPUT: «␤»

    # There is no 5-part extension, so we replaced nothing:
    say "foo.tar.gz".IO.extension: 'ZIP', :5parts; # OUTPUT: «"foo.tar.gz".IO␤»

    # Replacing a 0-part extension is just appending:
    say "foo.tar.gz".IO.extension: 'ZIP', :0parts; # OUTPUT: «"foo.tar.gz.ZIP".IO␤»

    # Replace 1-part of the extension, using '.' joiner
    say "...".IO.extension: 'tar'; # OUTPUT: «"....tar".IO␤»

    # Replace 1-part of the extension, using empty string joiner
    say "...".IO.extension: 'tar', :joiner(''); # OUTPUT: «"...tar".IO␤»

    # Remove 1-part extension; results in empty basename, so result is ".".IO
    say ".".IO.extension: ''; # OUTPUT: «".".IO␤»

=head2 method dirname

    method dirname(IO::Path:D:)

Returns the directory name portion of the path object. That is, it returns
the path excluding the volume and the base name.

    say IO::Path.new("/home/camelia/myfile.p6").dirname;    # OUTPUT: «/home/camelia␤»
    say IO::Path.new("/home/camelia").dirname;              # OUTPUT: «/home␤»
    say IO::Path.new("/home").dirname;                      # OUTPUT: «/␤»


=head2 method volume

    method volume(IO::Path:D:)

Returns the volume portion of the path object. On Unix system, this is always
the empty string.

    say IO::Path::Win32.new("C:\\Windows\\registry.ini").volume;    # OUTPUT: «C:␤»

=head2 method parts

    method parts(IO::Path:D: --> Hash)

Returns a hash with the keys C<dirname>, C<path> and C<volume>, and as
values the return values of the methods with the same names.

    say IO::Path.new("/etc/passwd").parts.perl;
    # OUTPUT: «{:basename("passwd"), :directory("/etc"), :dirname("/etc"), :volume("")}␤»

=head2 method path

    method path(IO::Path:D: --> Str)

Returns the string the object was constructed from or the value of
C<$SPEC.join($volume,$dirname,$basename)> if multi-part version of C<.new>
was used. B<NOTE:> this does not include the C<$.CWD>; see
L«C<IO::Path.absolute>|/routine/absolute»
and L«C<IO::Path.relative>|/routine/relative» for
stringification options that include C<$.CWD>.

=head2 method Str

    method Str(IO::Path:D: --> Str)

Alias for L«C<IO::Path.path>|/routine/path».

=head2 method open

    method open(IO::Path:D: *%opts)

Opens the path as a file; the named options control the mode, and are the
same as the L<open> function accepts.

=head2 method watch

    method watch(IO::Path:D: --> Supply)

Watches the path for modifications. Only implemented in Rakudo with the
MoarVM backend at the moment.

=head2 method is-absolute

    method is-absolute(IO::Path:D: --> Bool)

Returns C<True> if the path is an absolute path, and C<False> otherwise.

=head2 method is-relative

    method is-relative(IO::Path:D: --> Bool)

Returns C<True> if the path is a relative path, and C<False> otherwise.

=head2 method absolute

    multi method absolute(IO::Path:D: --> Str)
    multi method absolute(IO::Path:D: $base --> Str)

Returns a new C<Str> object that is an absolute path. If the invocant
is not already an absolute path, it is first made absolute using C<$base>
as base, if it is provided, or the C<.CWD> attribute the object was
created with if it is not.

=head2 method relative

    method relative(IO::Path:D: $base = $*CWD --> Str)

Returns a new C<Str> object with the path relative to the C<$base>. If C<$base>
is not provided, C<$*CWD> is used in its place. If the invocant is not
an absolute path, it's first made to be absolute using the C<.CWD>
attribute the object was created with, and then is made relative to C<$base>.

=head2 method parent

    method parent(IO::Path:D: --> IO::Path)

Removes the last portion of the path and returns the result as a new C<IO::Path>.

    my $io = IO::Path.new( "/etc/passwd" );
    say $io.parent;                          # OUTPUT: «"/etc".IO␤»

=head2 method child

    method child(IO::Path:D: $childname --> IO::Path)

Appends C<$childname> to the end of the path, adding path separators where
needed and returns the result as a new C<IO::Path>.

    my $io = IO::Path.new( "/bin" );
    say $io.child('netstat');                # OUTPUT: «"/bin/netstat".IO␤»

=head2 method resolve

    method resolve(IO::Path:D: --> IO::Path)

Returns a new C<IO::Path> object with all symbolic links and references to the
parent directory (C<..>) resolved.  This means that the filesystem is examined
for each directory in the path, and any symlinks found are followed.

    # bar is a symlink pointing to "/baz"
    my $io = "foo/./bar/..".IO.resolve;      # now "/" (the parent of "/baz")

NOTE: Currently (May 2016) this method doesn't work correctly on all platforms,
e.g. Windows, since it assumes POSIX semantics.

=head2 routine dir

    sub    dir(Cool $path = '.', Mu :$test = none('.', '..'))
    method dir(IO::Path:D: Mu :$test = none('.', '..'))

Returns the contents of a directory as a lazy list of C<IO::Path> objects
representing relative paths, filtered by L<smart-matching|
/language/operators#infix_~~> their names against the C<:test> parameter.

Examples:

    # To iterate over the contents of the current directory:
    for dir() -> $file {
        say $file;
    }

    # As before, but include even '.' and '..' which are filtered out by
    # the default :test matcher:
    for dir(test => *) -> $file {
        say $file;
    }

    # To get the names of all .jpg and .jpeg files in ~/Downloads:
=for code :skip-test
my @jpegs = "%*ENV<HOME>/Downloads".IO.dir(test => /:i '.' jpe?g $/)».Str;

=comment TODO: Turn the following into a FAQ entry, and link to it from here.

An example program that lists all files and directories recursively:

    sub MAIN($dir = '.') {
        my @todo = $dir.IO;
        while @todo {
            for @todo.pop.dir -> $path {
                say $path.Str;
                @todo.push: $path if $path.d;
            }
        }
    }

A lazy way to find the first three files ending in ".p6" recursively starting from the current directory:

=for code :skip-test
my @stack = '.'.IO;
my $perl-files = gather while @stack {
    with @stack.pop {
        when :d { @stack.append: .dir }
        .take when .extension.lc eq 'p6'
    }
}
.put for $perl-files[^3];
=for code :skip-test

=head2 method e

    method e(--> Bool)

Returns C<Bool::True> if the invocant is a valid path that exists.

=head2 method d

    method d(--> Bool)

Returns C<Bool::True> if the invocant is a path and the directory exists.

=head2 method f

    method f(--> Bool)

Returns C<Bool::True> if the invocant is a path and the file exists.

=head2 method s

    method s(--> Int)

If the path is valid and points to a regular file the size of that file
in bytes is returned. If the path is valid but pointing to something which
is not a file, e.g. a directory, a L<X::IO::NotAFile> exception is thrown.
An exception is also thrown if the path is invalid, in that case the exception
type is L<X::IO::DoesNotExist>.

=head2 method l

    method l(--> Bool)

Returns C<Bool::True> if the invocant is a path and a symlink.

=head2 method r

    method r(--> Bool)

Returns C<Bool::True> if the invocant is a path and accessible.

=head2 method w

    method w(--> Bool)

Returns C<Bool::True> if the invocant is a path and writable.

=head2 method rw

    method rw(--> Bool)

Returns C<Bool::True> if the invocant is a path, readable and writable.

=head2 method x

    method x(--> Bool)

Returns C<Bool::True> if the invocant is a path and executable.

=head2 method rwx

    method rwx(--> Bool)

Returns C<Bool::True> if the invocant is a path, executable, readable and writable.

=head2 method z

    method z(--> Bool)

Returns C<Bool::True> if the invocant is a path and the size is 0.

=head2 routine slurp

    multi method slurp(IO::Path:D: :$bin, :$enc)

Return the file content for this path, like L<slurp|/routine/slurp>.

=head2 method spurt

    method spurt(IO::Path:D: $data, :$enc, :$append, :$createonly)

Opens the file path for writing, and writes all of the C<$data> into it.
Will L«C<fail>|/routine/fail» if it cannot succeed for any reason.
The C<$data> can be any L«C<Cool>|/type/Cool» type or any L«C<Blob>|/type/Blob»
type. Arguments are as follows:

=item C<:$enc> — character encoding of the data. Takes same values as C<:$enc>
in L«C<IO::Handle.open>|/routine/open». Defaults to C<utf8>. Ignored if C<$data>
is a L«C<Blob>|/type/Blob».

=item C<:$append> — open the file in C<append> mode, preserving existing
contents, and appending data to the end of the file.

=item C<:$createonly> — L«C<fail>|/routine/fail» if the file already exists.

=head2 routine chdir

    multi method chdir(IO::Path:U: $path, :$test = 'r' --> IO::Path)
    multi method chdir(IO::Path:D: Str() $path is copy, :$test = 'r' --> IO::Path)

=head2 routine mkdir

    multi sub mkdir(Int:D $mode, *@dirnames --> List)
    multi sub mkdir($path, $mode = 0o777 --> Bool)
    method    mkdir(IO::Path:D: $mode = 0o777 --> Bool)

Creates one or more directories with the L<permissions|
https://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation>
specified as an integer (though still subject to L<umask|
https://en.wikipedia.org/wiki/Umask>).

Creates parent directories, as needed. That is, C<mkdir "foo/bar"> will
create C<foo> if it does not exist.

Throws an exception of type L<X::IO::Mkdir> if the
directory cannot be created, except for the C<*@dirnames> form, which
returns the names of the directories that were successfully created.

=head2 routine rmdir

    sub    rmdir(IO $dir --> Bool)
    method rmdir(IO::Path:D: --> Bool)

Remove the given directory if it is empty.

Returns C<True> on success.  Throws an exception of type
L<X::IO::Rmdir> if the directory cannot be removed (e.g.
the directory is not empty, or the path is not a directory).

Since this only works on an empty directory, to remove a directory and its
contents you will have to do something more complex.

    # When we have a directory first recurse, then remove it
    multi sub rm-all(IO::Path $path where :d) {
        .&rm-all for $path.dir;
        rmdir($path)
    }

    # Otherwise just remove the thing directly
    multi sub rm-all(IO::Path $path) { $path.unlink }

See also L<rmtree in File::Directory::Tree|https://github.com/labster/p6-file-directory-tree>.

=head2 routine chmod

    sub    chmod($mode, *@filenames --> List)
    method chmod(IO::Path:D: $mode --> Bool)

Changes the POSIX permissions of a file (or in the subroutine form, any number
of files) to C<$mode>.

The subroutine form returns the names of the files for which setting the new
mode was successful. The method form returns True on success, and otherwise
L<fails|/routine/fail> with L<X::IO::Chmod>.

The mode is expected as an integer following the L<standard numeric notation|
https://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation>, and is
best written as an octal number:

=for code :skip-test
chmod 0o755, "myfile1", "myfile2"; # make two files executable by the owner
"myfile".IO.chmod(0o444);          # make a file read-only
$*CWD.chmod(0o544);                # make the working directory read-only

Make sure you I<don't> accidentally pass the intended octal digits as a decimal
number (or string containing a decimal number):

=for code :skip-test
chmod '0444', 'myfile';            # BAD!!! (interpreted as mode 0o674)

=head2 routine rename

    method rename(IO::Path:D: $to, :$createonly --> Bool)
    sub    rename($from, $to, :$createonly --> Bool)

Renames a file. Both C<$from> (the file to be renamed) and C<$to> (the
destination) can take arbitrary paths. If C<:createonly> is set to C<True>,
the rename fails if a file already exists in the destination.  Returns
C<True> upon success, or L<fails|/routine/fail> with L<X::IO::Rename> if the
operation could not be completed.

Please use L<move|/routine/move> if a file could not be moved by renaming
(usually because the destination is on a different physical storage device).

=head2 routine copy

    method copy(IO::Path:D: $to, :$createonly)
    sub    copy($from, $to, :$createonly)

Copies a file, as indicated by C<$from> or the invocant, to the
destination specified.  If C<:createonly> is set to C<True>, copy fails if a file
already exists in the destination.  Returns C<True> upon success, or an
appropriate C<Failure> if the operation could not be completed.

=head2 routine move

    method move(IO::Path:D: $to, :$createonly)
    sub    move($from, $to, :$createonly)

Moves a file. Both C<$from> (the file to be moved) and C<$to> (the
destination) can take arbitrary paths. If C<:createonly> is set to C<True>,
the move fails if a file already exists in the destination.  Returns C<True>
upon success, or L<fails|/routine/fail> with L<X::IO::Move> if the operation
could not be completed.

Please use L<rename|/routine/rename> if a file can be moved by renaming
(which is usually possible if the destination is on the same different
physical storage device).

=head2 routine symlink

    method symlink(IO::Path:D: Str $name --> Bool)
    sub    symlink(Str $target, Str $name --> Bool)

Create a new symbolic link named as $target (or the name of the invocant in
the method form) to the existing file named C<$name>.

Both forms will return C<True> on success or L<fail|/routine/fail> with
L<X::IO::Symlink> if the symbolic link could not be created.

=head2 routine link

    method link(IO::Path:D: Str $name --> Bool)
    sub    link(Str $target, Str $name --> Bool)

Create a new link named as $target (or the name of the invocant in the
method form) to the existing file named C<$name>.

Both forms will return C<True> on success or L<fail|/routine/fail> with
L<X::IO::Link> if the link operation could not be performed.

=head2 routine unlink

    method unlink(IO::Path:D: --> Bool)
    sub    unlink(*@filenames --> List)

Delete all specified ordinary files, links, or symbolic links.

The subroutine form returns the names of the files that were successfully
deleted. The method form returns C<True> on success, or
L<fails|/routine/fail> with L<X::IO::Unlink> if the operation could not be
completed.

=head2 method IO

    method IO(IO::Path:D: --> IO::Path)

Returns the invocant.

=head2 method SPEC

    method SPEC(IO::Path:D: --> IO::Spec)

Returns the L<IO::Spec|/type/IO::Spec> object that was (implicitly) specified at object
creation time.

    my $io = IO::Path.new("/bin/bash");
    say $io.SPEC;                            # OUTPUT: «(Unix)␤»
    say $io.SPEC.dir-sep;                    # OUTPUT: «/␤»

=head1 File test operators

X<|:e> X<|:d> X<|:f> X<|:l> X<|:r> X<|:w> X<|:x> X<|:s> X<|:z> X<|:rw> X<|:rwx>

=begin table
    op       meaning
    ==       ======
    :e       Exists
    :d       Directory
    :f       File
    :l       Symbolic link
    :r       Readable
    :w       Writable
    :x       Executable
    :s       Size
    :z       Zero size
    :rw      Readable and writable
    :rwx     Readable, writable, and executable

=end table

Usage:

If you have a string - a path to something in the filesystem:

    if "path/to/directory".IO ~~ :e & :d {
        say 'path exists and points to a directory';
    }

    my $file = "path/to/file";
    if $file.IO ~~ :e {
        say 'file exists';
    }

Instead of the colonpair syntax, you can use method calls too:

    if 'path/to/file'.IO.e {
        say 'file exists';
    }

If you already have an IO object in C<$file>, either by creating one yourself,
or by getting it from another subroutine, such as C<dir>,
you can write this:

    my $path = "path/to/file".IO;
    given $path {
        when :f { say 'path is a file' }
        when :d { say 'path is a directory' }
    }

=head1 File timestamp retrieval

There are also 3 methods for fetching the 3 timestamps of a file (inode),
on Operating Systems where these are available:

=head2 method modified

Return an L<Instant> object representing the timestamp when the file was
last modified.

=for code :skip-test
say "path/to/file".IO.modified;               #  e.g. Instant:1424089165

To obtain a human-readable form of the timestamp, call C<DateTime> method
on the returned C<Instant>, to convert it into a L<DateTime> object:

=for code :skip-test
say "path/to/file".IO.modified.DateTime;      # e.g. 2015-02-16T12:18:50Z

=head2 method accessed

Return an L<Instant> object representing the timestamp when the file was
last accessed. B<Note:> depending on how the filesystem was mounted, the
last accessed time may not update on I<each access> to the file, but only
on the first access after modifications.

=for code :skip-test
say "path/to/file".IO.accessed;               # e.g. Instant:1424353577
say "path/to/file".IO.accessed.DateTime;      # e.g. 2015-02-19T13:45:42Z

=head2 method changed

Return an L<Instant> object representing the timestamp when the inode was
last changed.

=for code :skip-test
"path/to/file".IO.changed;                    # e.g. Instant:1424089165

To obtain a human-readable form of the timestamp, use a L<DateTime> object:

=for code :skip-test
say DateTime.new("path/to/file".IO.changed);  # e.g. 2015-02-16T12:18:50Z

or more readably:

=for code :skip-test
my $change_instant = "path/to/file".IO.changed;
my $change_time = DateTime.new($change_instant);
say $change_time;                             # e.g. 2015-02-16T12:18:50Z

=head1 File permissions retrieval

=head2 method mode

Return an L<IntStr> object representing the POSIX permissions of a file.  The
Str part of the result is the octal representation of the file permission, like
the form accepted by the chmod(1) utility.

=for code :skip-test
say ~"path/to/file".IO.mode;        # e.g. '0644'
say +"path/to/file".IO.mode;        # e.g. 420, where sprintf('%04o', 420) eq '0644'

The result of this can be used in the other methods that take a mode as an
argument.

=for code :skip-test
"path/to/file1".IO.chmod("path/to/file2".IO.mode);  # will change the
                                                    # permissions of file1
                                                    # to be the same as file2

=end pod

# vim: expandtab shiftwidth=4 ft=perl6
