Skip to content

Latest commit

 

History

History
1784 lines (1167 loc) · 49.4 KB

File metadata and controls

1784 lines (1167 loc) · 49.4 KB

TITLE

Synopsis 32: Setting Library - IO

VERSION

Created: 19 Feb 2009

Last Modified: 28 Oct 2014
Version: 27

Overview

This synopsis describes in depth the subroutines and methods that were described broadly in S16. Please note that any implementation is free to use multi-method dispatch on both subroutines as well as methods should this increase the performance or maintainability.

Functions

print()

sub print(*@text --> Bool) is export

Print the given text on $*OUT.

say()

sub say(*@text --> Bool) is export

Print the given text, followed by a new line "\n" on $*OUT. Before printing, call the .gist method on any non-Str objects.

note()

sub note(*@text --> Bool) is export

Print the given text, followed by a new line "\n" on $*ERR. Before printing, call the .gist method on any non-Str objects.

dd()

sub dd(@vars --> Bool) is export

Tiny Data Dumper. Takes the variables specified and notes them (on $*ERR) in an easy to read format, along with the name of the variable. So:

my $a = 42;
dd($a);   # notes "$a = 42"

prompt()

sub prompt($msg --> Bool) is export

Simple Prompter. Print message on $*OUT and obtains a single line of input from $*IN.

open()

sub open ($name as IO,
  # mode
    Bool :$r  = True,
    Bool :$w  = False,
    Bool :$rw = False,
    Bool :$a  = False,
  # encoding
    Bool :$bin = False,
    Str  :$enc = "Unicode",  # utf-8 unless otherwise
  # newlines
    Any  :$nl    = "EOL",
    Bool :$chomp = True,
    --> IO::Handle ) is export

A convenience function for opening normal files as text (by default) as specified by its (first) parameter. It returns an instantiated "IO::Handle" object. The following named parameters may also be specified:

:r

Open file for reading. Default is True.

:w

Open file for writing by creating an empty file with the given name. The original contents of an existing file with that name, will be lost. Default is False.

:rw

Open file for reading and writing with the given name. The original contents of an existing file with that name, will be lost. Default is False.

:a

Open file for appending, create one if it didn't exist yet. This may or may not inhibit overwriting the original contents when moving the file pointer. Default is False.

:bin

Open file in binary mode (byte mode). A file opened with :bin may still be processed line-by-line, but IO will be in terms of Buf rather than Str types. Default is False, implying text semantics.

:enc

Encoding to use if opened in text mode. Defaults to "Unicode", which implies figuring out which actual UTF is in use, either from a BOM or other heuristics. If heuristics are inconclusive, UTF-8 will be assumed. (No 8-bit encoding will ever be picked implicitly.)

:nl

The marker used to indicate the end of a line of text. Only used in text mode. Defaults to "EOL", which implies accepting any combination of "\n", "\r\n" or "\r" or any other Unicode character that has the Zl (Separator, Line) property.

:chomp

Whether or not to remove new line characters from text obtained with .lines and .get. Defaults to True.

dir()

sub dir($directory as Str = $*CWD,
    Mu       :$test = $*SPEC.curupdir,
    Bool     :$absolute = False,
    Bool     :$Str = False,
    IO::Path :$CWD = $*CWD,
    --> List ) is export

Returns a lazy list of (relative) paths in the $directory as IO::Path objects, by default from the directory pointed to by $*CWD. If dir() fails, it returns an X::IO::Dir failure. The following named parameters are optional:

:test

Expression against which to smart-match for inclusion in result list. By default excludes curdir (usually ".") and updir (usually "..") only.

:absolute

Boolean indicating to return absolute path names, rather than relative ones. False by default.

:Str

Boolean indicating to return Strings, rather than IO::Path objects. False by default.

:CWD

Only important if :absolute is specified with a True value. The directory to pre-pend to the relative file paths. Defaults to $*CWD.

slurp()

sub slurp ($what = $*ARGFILES,
    Bool :$bin = False,
    Str  :$enc = "Unicode",
    --> Str|Buf ) is export

Slurps the contents of the entire file into a Str (or Buf if :bin). Accepts :bin and :enc optional named parameters, with the same meaning as "open()". The routine will fail if the file does not exist, or is a directory.

spurt()

sub spurt ($where, $what,
    Str  :$enc        = $*ENC,
    Bool :append      = False,
    Bool :$createonly = False,
    --> Bool ) is export

Writes the indicated contents (2nd positional parameter) to the location indicated by the first positional parameter (which can either be a string, an IO::Path object, or an already opened IO::Handle object).

If a file needs to be opened for writing, it will also be closed. Returns True on success, or the appropriate Failure if something went wrong.

These named parameters are optional and only have meaning if the first positional parameter was not an IO::Handle:

:enc

The encoding with which the contents will be written. [conjectural]

:append

Boolean indicating whether to append to a (potentially) existing file. If the file did not exist yet, it will be created. Defaults to False.

:createonly

Boolean indicating whether to fail if the file already exists. Defaults to False.

mkdir()

sub mkdir($dir as IO, $mode = 0o777 --> Bool) is export

Creates the directory as indicated by the positional parameter. Returns True on success or an appropriate Failure.

rmdir()

sub rmdir($dir as IO --> Bool) is export

Removes the (empty) directory as indicated by the positional parameter. Returns True on success or an appropriate Failure.

chdir()

sub chdir($dir as IO, $CWD = $*CWD,
    :$test = <d r>
    --> Bool) is export

Changes the current working directory to the given directory, for the scope in which $*CWD is active (if no second positional parameter is given) or for the scope of the indicated localized $*CWD. A typical use case:

{
    chdir("foo", my $*CWD);
    # working directory changed to "foo"
}
# restored to what it was

Returns True if successful, or an appropriate Failure, e.g if the directory does not exist, or is not a directory, or is not readable.

Please note that this directory has no connection with whatever the operating system thinks is the current working directory. The value of $*CWD just will always be prepended to any relative paths in any file operation in Perl 6.

Also note that you can use chdir to set similar dynamic variables, like $*TMPDIR and $*HOME this way:

chdir("bar", my $*TMPDIR);   # set $*TMPDIR in this scope
chdir("bar", my $*HOME);     # set $*HOME in this scope

copy()

sub copy ($source as IO, $dest as IO,
    :$createonly = False,
    --> Bool ) is export

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

rename()

sub rename ($source as IO, $dest as IO,
    :$createonly = False,
    --> Bool ) is export

Moves a file, as indicated by the first positional parameter, by renaming it to the destination specified. If :createonly is set to True, the rename fails if a file already exists in the destination. Returns True upon success, or an appropriate Failure if the operation could not be completed.

Please use "move()" if a file could not be moved by renaming (usually because the destination is on a different physical storage device).

move()

sub move ($source as IO, $dest as IO,
    :$createonly = False,
    --> Bool ) is export

Moves a file, as indicated by the first positional parameter, by copying its contents to the destination specified, and then removing the file at the original location. If :createonly is set to True, the move fails if a file already exists in the destination. Returns True upon success, or an appropriate Failure if the operation could not be completed.

Please use "rename()" if a file can be moved by renaming (which is usually possible if the destination is on the same different physical storage device). Alternately, the move() function is free to try the rename() first, and if that (silently) fails, do it the hard way.

sub unlink(*@files --> @removed) is export

Delete all specified ordinary files, links, or symbolic links. Returns the names of the files that were successfully deleted.

chmod()

sub chmod($permission, *@files --> @changed) is export

Changes the permissions of a list of files. The first element of the list must be the numerical mode, which should probably be an octal number, and which definitely should not be a string of octal digits: 0o644 is okay, 0644 is not. Returns the names of the files that were successfully changed.

$count = chmod 0o755, 'foo', 'bar';
chmod 0o755, @executables;
$mode =  '0644'; chmod $mode, 'foo';  # BAD!!! sets mode to --w----r-T
$mode = '0o644'; chmod $mode, 'foo';  # this is better
$mode =  0o644 ; chmod $mode, 'foo';  # this is best
sub link($target, $source --> Bool) is export

Create a hard link between the target from the given source path. Returns True if successful, or an appropriate Failure.

sub symlink($target, $source --> Bool) is export

Create a symbolic link between the target from the given source path. Returns True if successful, or an appropriate Failure.

IO Types

IO

role IO { };

The base role only tags that this is an IO object for more generic purposes. It doesn't specify any methods or attributes.

IO::Spec

This class is a collection of methods dealing with file specifications (commonly known as file names, though it can include the entire directory path). Most of the methods allow access to lower-level operations on file path strings.

These operations are significantly different on some operating systems, so the actual work is being done by subclasses such as IO::Spec::Unix, IO::Spec::Win32 and IO::Spec::Cygwin.

The correct IO::Spec class for your system, is available in the $*SPEC dynamic variable. So typically, you would call methods on that:

my $cleanpath = $*SPEC.canonpath("a/.//b/")  # gives "a/b"

This set of modules was inspired by Perl 5's File::Spec. An implementation may choose to inherit from IO::Spec, or any of its subclasses, if that helps in avoiding code duplication.

The select method is the only method provided by IO::Spec itself.

.select

method select(IO::Spec:U: $name = $*DISTRO.name as Str --> IO::Spec:U)

The .select method takes an optional argument: a string indicating the type of system for which to perform file specification operations. By default, it takes $*DISTRO.name.

At startup, $*SPEC is initialized to IO::Spec.select.

IO::Spec subclasses

The following methods should be provided by the IO::Spec subclasses, or may be inherited from another class. They will never check anything with an actual file system. In alphabetical order:

.abs2rel

method abs2rel($path as Str, $base = $*CWD --> Str)

Takes a path and an optional base path (default $*CWD) and returns a relative path from the base path to the destination path. If the base path is relative, then it will first be transformed to an absolute path with ".rel2abs", relative to $*CWD.

On systems with the concept of volume, if $path and $base appear to be on two different volumes, it will not attempt to resolve the two paths, and will instead simply return $path.

On systems that have a grammar that indicates filenames, this ignores the $base filename as well. Otherwise all path components are assumed to be directories.

If $path is relative, it is first converted to absolute form using ".rel2abs". This means that it is taken to be relative to $*CWD.

.canonpath

method canonpath($path as Str --> Str)

Perform a logical cleanup of a path and returns that. Note that this does *not* collapse x/../y sections into y. This is by design. If /foo on your system is a symlink to /bar/baz, then /foo/../quux is actually /bar/quux, not /quux as a naive ../-removal would give you. If you want to do this kind of processing, you probably want "IO::Path"'s ".resolve" method to actually traverse the filesystem cleaning up paths like this.

.catdir

method catdir(*@dir as Array[Str] --> Str)

Concatenate two or more directory names to form a complete path ending with a directory. Removes any trailing slashes from the resulting string, unless the result is the ".rootdir".

.catpath

method catpath($volume, $dir, $file --> Str)

Takes volume, directory and file portions and returns an entire path string. Under Unix, $volume is ignored, and directory and file are concatenated. On other OSes, $volume is significant. Directory separators like slashes are inserted if need be.

.contents

.curdir

method curdir(--> Str)

Returns a string representation of the current directory (Usually ".").

curupdir

Returns a test as to whether a given path is identical to the current directory (as indicated by ".curdir") or the parent directory (as indicated by ".updir". This is usually none(<. ..>). It is the default for the :test parameter to /dir() and "IO::Path"'s ".contents" method. It can also be used to extend dir() through its :test named parameter:

dir "my/directory", test => all($*SPEC.curupdir, /^ '.' /);

This example would return all files beginning with a period that are not "." or ".." directories.

.devnull

method devnull(--> Str)

Returns a string representation of the null device (e.g. "/dev/null" on Unix-like systems).

.extension

method extension($path as Str --> Str)

Returns the extension (if any) of the given path.

.is-absolute

method is-absolute($path as Str --> Bool)

Takes as its argument a path, and returns True if it is an absolute path, False otherwise. For IO::Spec::Win32, it returns 1 if it's an absolute path without a volume, and 2 if it's absolute with a volume.

.join

method join(:$volume, $dir, $file --> Str)

A close relative of ".catpath", this method takes volume, directory and basename portions and returns an entire path string. If a directory is ".", it is removed from the (relative) path output, because this function inverts the functionality of dirname and basename.

Directory separators are inserted if necessary. Under Unix, $volume is ignored, and only directory and basename are concatenated. On other OSes, $volume is significant.

This method is the inverse of ".split"; the results can be passed to it to get the volume, dirname, and basename portions back.

.PATH

method PATH($PATH = %*ENV<PATH> --> List[Str])

Convert a string formatted like a system's PATH specification, and returns it as a list of strings. Takes %*ENV<PATH> by default.

.rel2abs

method rel2abs($path as Str, $base = $*CWD as Str --> Str)

Converts a relative path to an absolute path, using an optional base directory. If the base directory is not specified, $*CWD will be assumed.

If $base is relative, then it is first converted to absolute form, relative to $*CWD.

On systems with the concept of volume, if $path and $base appear to be on two different volumes, t will not attempt to resolve the two paths, and will instead simply return $path.

On systems that have a grammar that indicates filenames (like VMS), this ignores the $base specification as well. Otherwise all path components are assumed to be directories.

If $path is absolute, it is cleaned up and returned using ".canonpath".

.rootdir

method rootdir(--> Str)

Returns a string representation of the root directory (usually "/").

.split

method split($path as Str --> Hash[Str])

A close relative of ".splitdir", this function also splits a path into volume, directory, and basename portions. Unlike ".splitdir", split returns paths compatible with dirname and basename and returns it arguments as a hash of volume, directory, and basename.

This means that trailing slashes will be eliminated from the directory and basename components, in Win32 and Unix-like environments. The basename component will always contain the last part of the path, even if it is a directory, '.', or '..'. If a relative path's directory portion would otherwise be empty, the directory is set to whatever curdir is.

On systems with no concept of volume, returns '' (the empty string) for volume. The results can be passed to ".join" to get back a path equivalent to (but not necessarily identical to) the original path. If you want to keep all of the characters involved, use ".splitdir" instead.

.splitdir

method splitdir($directories as Str --> List[Str])

The opposite of ".catdir". $directories must be only the directory portion of the path on systems that have the concept of a volume or that have path syntax that differentiates files from directories.

Unlike just splitting the directories on the separator, empty directory names ('') can be returned, because these are significant on some OSes.

.splitpath

method splitpath( $path, $nofile = False )

Splits a path in to volume, directory, and filename portions and returns these as a List. On systems with no concept of volume, returns '' for volume.

my ($volume,$directories,$file) = $*SPEC.splitpath( $path );
my ($volume,$directories,$file) = $*SPEC.splitpath( $path, $no_file );

For systems with no syntax differentiating filenames from directories, assumes that the last file is a path unless $no_file is True or a trailing separator or /. or /.. is present. On Unix, this means that $no_file true makes this return ( '', $path, '' ).

The directory portion may or may not be returned with a trailing '/'.

The results can be passed to ".catpath" to get back a path equivalent to (but not necessarily identical to) the original path.

.tmpdir

method tmpdir(--> IO::Path)

Returns an "IO::Path" representation of the first writable directory from an implicit list of possible temporary directories. Returns the current directory if no writable temporary directories are found. The list of directories checked depends on the platform.

.updir

method updir(--> Str)

Returns a string representation of the parent directory (usually "..").

Comparison of .splitpath and .split

OS      Path       splitpath               split
Unix    /a/b/c     ("", "/a/b/", "c")      ("", "/a/b", "c")
Unix    /a/b//c/   ("", "/a/b//c/", "")    ("", "/a/b", "c")
Unix    /a/b/.     ("", "/a/b/.", "")      ("", "/a/b", ".")
Win32   C:\a\b\    ("C:", "\\a\\b\\", "")  ("C:", "\\a", "b")
VMS     A:[b.c]    ("A:", "[b.c]", "")     ("A:", "[b]", "[c]")

* The VMS section is still speculative, and not yet supported.

Comparison of .catpath and .join

OS     Components            catpath        join
Unix   ("", "/a/b", "c")     /a/b/c         /a/b/c
Unix   ("", ".", "foo")      ./foo          foo
Unix   ("", "/", "/")        //             /
Win32  ("C:", "\a", "b")     C:\a\b         C:\a\b
VMS    ("A:", "[b]", "[c]")  A:[b][c]       A:[b.c]

* The VMS section is still speculative, and not yet supported.

IO::Path

class IO::Path is Cool { }

Holds a path of a file or directory. The path is generally divided into three parts, the volume, dirname and base name.

On Windows, the volume is a drive letter like C:, or a UNC network volume like \\share\. On UNIX-based systems, the volume part is empty.

The basename is name of the file or directory that the IO::Path object represents, and the directory is the part of the path leading up to the basename.

path              volume         dirname    basename
/usr/bin/gvim                    /usr/bin   gvim
/usr/bin/                        /usr       bin
foo/bar.txt                      foo        bar.txt
C:\temp\f.txt     C:             \temp      f.txt
\\server\share\a  \\server\share \          a

By default, IO::Path uses the IO::Spec setting as found in $*SPEC when the object is created. If you want to work paths as if you were using another OS, you can specify another IO::Spec subclass with the optional :SPEC named parameter.

There are several ways of creating an IO::Path. The easiest way is to use .IO coercer:

my $io = "foo/bar".IO;

Of course, you can always call the .new method as well:

my $io = IO::Path.new( $full-path );
my $io = IO::Path.new( :$volume, :$dirname, :$basename);

Whenever a new IO::Path is created, an internal absolute and cleaned version of the specified path is stored, using the implicitly or explicitly specified values for $*SPEC and $*CWD:

my $io = IO::Path.new( "foo", :SPEC<win32>, :CWD</usr/local/src> );

would create an IO::Path object with IO::Spec::Win32 semantics, with an absolute path of /usr/local/src/foo. Yes, that would be strange, but it is possible. A shorter way would be:

my $io = "foo".IO( :SPEC<win32>, :CWD</usr/local/src> );

The (implicit) value of :CWD is only used for creating the absolute path at instantiation time. The (implicit) value of :SPEC is actually saved in the object to be able to perform path operations with the correct semantics at a later time.

File test methods

The following (single letter) methods can be used on the IO::Path object:

M  Test performed                              Returns
=  ==============                              =======
r  Path is readable by effective uid/gid.      Bool
w  Path is writable by effective uid/gid.      Bool
x  Path is executable by effective uid/gid.    Bool
o  Path is owned by effective uid.             Bool

R  Path is readable by real uid/gid.           Bool
W  Path is writable by real uid/gid.           Bool
X  Path is executable by real uid/gid.         Bool
O  Path is owned by real uid.                  Bool

e  Path exists.                                Bool
s  Size of the path in bytes.                  Int
z  Path has zero size (an empty file).         Bool

f  Path is a plain file.                       Bool
d  Path is a directory.                        Bool
l  Path is a symbolic link.                    Bool
L  Actual path of symbolic link (readlink)     Str
p  Path is a named pipe (FIFO)                 Bool
S  Path is a socket.                           Bool
b  Path is a block special file.               Bool
c  Path is a character special file.           Bool

u  Path has setuid bit set.                    Bool
g  Path has setgid bit set.                    Bool
k  Path has sticky bit set.                    Bool

To allow for easy chaining of file tests, there is an .all method that can be fed the tests to be tried as a List of strings. The value returned will be the first non-True value, or the final True value.

say "rwx" if $io.all: <r w x>;

if $io.all(<f r w x s>) -> $size {
    say "plain file with rwx of $size bytes";
}

For convenience, you can also specify the negated letter for the opposite test:

if $io.all(<!d r w x s>) -> $size {
    say "not a directory with rwx of $size bytes";
}

Other methods are listed here in alphabetical order:

.absolute

method absolute($base as Str --> Str)

The absolute path of the path, optionally from the relative base.

.accessed

method accessed(--> Instant)

Returns the Instant when the file was last accessed, or Failure if this could not be determined.

.basename

method basename(--> Str)

Returns the base name part of the path -- that is, the last portion. Functions equivalently to the basename shell program on Unix-like systems.

.changed

method changed(--> Instant)

Returns the Instant when the metadata of the file was last changed, or Failure if this could not be determined.

.chdir

method chdir(:$CWD = $*CWD --> Bool)

Like "chdir()", but with ".absolute" as the first parameter.

.child

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

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

.chmod

method chmod($permissions --> Bool)

Like "chmod()", but with ".absolute" as the second parameter.

.copy

method copy($dest, :$createonly --> Bool)

Like "copy()", but with ".absolute" as the first parameter.

.dir

method dir(:$test, :$absolute, :$CWD --> List[Str])

Like "dir()", but with ".absolute" as the first parameter.

.dirname

method dirname(-->Str)

Returns the directory part of the path, not including the last item. Functions equivalently to the dirname shell program on Unix-like systems.

.extension

method extension(--> Str)

Returns the extension of the path, if any.

.IO

method IO(--> IO::Path)

Returns itself.

.is-absolute

method is-absolute(--> Bool)

Always returns True since internally the path is always stored as an absolute path.

.is-relative

method is-relative(--> Bool)

Always returns False since internally the path is always stored as an absolute path.

.lines

method lines( --> List[Str] )

Returns a (lazy) list of lines of which the file consists, or a Failure if something went wrong.

.mkdir

method mkdir($mode = 0o777 --> Bool)

Like "mkdir()", but with ".absolute" as the first parameter.

.modified

method modified(--> Instant)

Returns the Instant when the contents of the file were last modified, or Failure if this could not be determined.

.move

method move($dest as IO, :$createonly --> Bool)

Like "move()", but with ".absolute" as the first parameter.

.open

method open(... --> IO::Handle)

Like "open()", but with ".absolute" as the first parameter.

.parent

method parent(--> IO::Path)

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

.pred

method pred(--> IO::Path)

Create previous logical path and return the result as a new IO::Path or returns Failure if that is not possible.

.relative

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

Transforms the path into an relative form, and returns the result as a new IO::Path. If $base is supplied, transforms it relative to that base directory, otherwise the $*CWDis used. Paths that are already relative are returned unchanged.

.rename

method rename($dest as IO, :$createonly --> Bool)

Like "rename()", but with ".absolute" as the first parameter.

.resolve

method resolve(--> IO::Path)

Returns a new IO::Path object with all symbolic links and references to the parent directory (..) are physically 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")

.rmdir

method rmdir(--> Bool)

Removes (deletes) the directory represented by the IO::Path. Returns True if successful, or a Failure of some kind if not. Typically fails if the path is not a directory or the directory is not empty.

.slurp

method slurp(:$bin, :$enc  --> Str|Buf)

Like "slurp()", but with ".absolute" as the first parameter.

.SPEC

method SPEC(--> IO::Spec)

Returns the "IO::Spec" object that was (implicitely) specified at object creation time.

.spurt

method spurt(:$enc, :$append, :$createonly, :$bin  --> Str|Buf)

Like "spurt()", but with ".absolute" as the first parameter.

.succ

method succ(--> IO::Path)

Create next logical path and return the result as a new IO::Path.

.unlink

method unlink(--> Bool)

Like "unlink()", but with ".absolute" as the first parameter. Returns True on success or an appropriate Failure.

.volume

method volume(-->Str)

Returns the volume part of the path. On Unix-like OSes or systems without a concept of volume in the path, returns the empty string.

.words

method words( :$nw = "WS" --> List[Str] )

Returns a (lazy) list of words of which the file consists, or a Failure if something went wrong. Also takes the following optional named parameters:

:nw

The delimiter between what are to be considered words. By default assumes "WS", which indicates any whitespace character.

Subclasses

The IO::Path class may have IO::Spec specific subclasses. But basically, these would only implicitely specify the IO::Class to be specified for the .new method:

class IO::Path::Win32 {
    method new(|c) { IO::Path.new(|c, :SPEC(IO::Spec::Win32) }
}

IO::Handle

class IO::Handle does IO { ... }

A handle of a file, pipe or anything else that supports reading or writing like a file.

The IO::Handle object is usually not directly instantiated, but with "open()" or "IO::Path"'s".open". Nonetheless, you can create an IO::Handle object with just a path:

my $handle = IO::Handle.new($filename as Str);
my $handle = IO::Handle.new($filename as Str, :SPEC(*$SPEC));
my $handle = IO::Handle.new($filename as Str, :SPEC(*$SPEC), :CWD($*CWD));

This does not interact with anything at all and will appear as if the file has been .closed. From then on, the .path method will return the IO::Path object that was created

The .open method does interact with the file system:

$handle.open;  # same as $handle = $filename.IO.open

It has the same optional named parameters as "open()" and either returns itself (for historical reasons), or a Failure with additional information.

Methods handled by .path

The filename specified with .new is internally stored as an "IO::Path" object, obtainable with the .path method. The following methods are handled by .path and work exactly the same:

absolute       the absolute, canonical path
accessed       last access time (if available)
basename       the basename of the path
changed        last (metadata) changed time
chmod          change attributes of path
dirname        the directory part of the absolute path
extension      the extension of the file
is-absolute    is the (original) path absolute
is-relative    is the (original) path relative
modified       last modified time
relative       the relative path against CWD
SPEC           the :SPEC at instantiation time
volume         the volume of the path (if any)

The following methods also work the same as with IO::Path, but it may be less logical to use these on an IO::Handle object as these return new IO::Path objects.

child          append basename to path
IO             same as .path
parent         remove last portion of path
pred           previous logical path
resolve        follow symlinks to the real path
succ           next logical path

These IO::Path methods seem to only make sense if the IO::Handle object is closed. But there may be some uses for this, but it seems more like extra rope for shooting yourself in the foot.

copy           create a copy of file
mkdir          create directory
move           move (rename) to other storage
rename         rename (move) to other name
rmdir          remove directory if empty directory
unlink         remove file

[Conjecture: perhaps the above methods should fail on IO::Handle]

Contrary to the IO::Path methods with the same name, these methods operate only from the current file position. If the file was just opened, it's identical as with the IO::Path version. But if you have done anything to the handle that moved the file pointer, you will get a different result.

lines          contents of file as lines
slurp          obtain the contents of the file
spurt          write / append contents to file
words          contents of file as words

The other methods of IO::Handle are:

.close

method close(--> Bool)

Closes the handle and returns True, or a Failure if something went wrong.

.encoding

method encoding(--> Str)

method encoding($encoding --> Str)

Without arguments, simply returns the current encoding used on the handle. If supplied with a string identifying a valid encoding, change the handle to read with that encoding from then on. Options include binary, utf8, and other text encodings. An invalid encoding causes the method to return a Failure.

.eof

method eof(--> Bool)

Returns True if the handle is exhausted, False otherwise.

.fileno

method fileno(--> int)

Returns the file descriptor, which is always a native integer, conforming to C89.

.flush

method flush(--> Bool)

Attempts to flush any buffered data, returns True if successful, an appropriate Failure otherwise.

.get

method get(--> Str)

Reads the next line and returns it. Uses the (implicit) specification of :nl with "open" to determine where a line ends. Returns a Str type object if no more lines to be read.

.getc

method getc(Int $chars = 1 --> Str)

Tries to read $chars characters and return them concatenated as a string. Returns a Str type object if no more lines to be read.

.ins

method ins(--> Int)

Returns the number of lines that have been read with ".get" or ".lines".

.opened

method opened(--> Bool)

Return whether the file is opened.

.p

method p(--> Bool)

Returns whether the handle is opened to a pipe.

.print

method print (*@text --> Bool)

Stringifies each element, concatenates those strings, and writes the result to the file. Returns True if successful, a Failure otherwise.

.read

method read(Int $bytes --> Buf)

Reads and returns $bytes bytes from the handle, or as many as are possible.

.say

method say (*@text --> Bool)

This is identical to ".print" except that it stringifies its arguments by calling .gist on them and auto-appends a newline after the final argument.

.seek

method seek(Int $position, MoveMethod $whence --> Bool)

Move the file pointer to $position. The meaning of this position is always in "bytes", so you better know what you're doing in a text-file.

The $whence value should be a MoveMethod value, which is one of:

name        value
=========== =====
FromStart     0
FromCurrent   1
FromEnd       2

These numerical values will also be accepted. Returns True on success, or a Failure if something went wrong (e.g. when using $*IN on a terminal input).

.t

method t(--> Bool)

Returns True if the handle is opened to a tty, aka there might actually be a person watching.

.tell

method tell(--> Int)

Returns the position of the file pointer in "bytes".

.write

method write(Buf $buf --> Int)

Tries to write $buf to the file. The actual number of bytes written is returned, or a Failure if something went wrong.

    This is "raw" write. $buf contains plain bytes. If you want to write a Str, you should .encode it first, or use ".print".

Here Be Dragons

Everything below this point hasn't been reviewed properly

IO::Socket

role IO::Socket {
    has %.options;
    has Bool $.Listener;
    ...
}

Accessing the %.options would on Unix be done with getsockopt(2)/setsockopt(2).

The $.Listener attribute indicates whether the socket will be a listening socket when opened, rather than indicating whether it is currently listening.

new
method new(
    :$Listener, # initializes $.Listener
)

The initial value of the $.Listener attribute is defined according to the following rules:

* If $Listener is passed to .new(), then that value is used
* If neither a local address nor a remote address are passed in, throw an exception
* If no remote address is passed, then $.Listener is set to SOMAXCONN
* If no local address is used, then $Listener is set to 0
* If both local and remote addresses are used, throw an exception that asks people to
  specify $Listener
open
method open()

If $.Listener is true, does a bind(2) and a listen(2), otherwise does a connect(2).

It's end-user use case is intended for the case where NoOpen is passed to .new(). .new() itself will presumably also call it.

close
method close()

Implements the close() function from IO::Closeable by doing a shutdown on the connection (see below) with @how set to ('Readable', 'Writeable').

shutdown
method shutdown(Str @how)

Does a shutdown(2) on the connection. See also IO::Readable.isReadable and IO::Writeable.isWriteable.

$how can contain 1 or more of the strings 'Readable' and 'Writeable'.

accept
method accept( --> IO::Socket)
method read(Int $bytes --> Buf)

Reads and returns $bytes bytes from the handle

method write(Buf $buf --> Int)

Implements the IO::Writeable interface by doing a send(2).

IO::Socket::INET

class IO::Socket::INET does IO::Socket {
    has Str $.proto = 'TCP';
    has Str $.host;
    has Int $.port;
    has Str $.localhost;
    has Int $.localport;
    ...
}
new
multi method new(:$host!, :$port, *%attributes) { ... }
multi method new(:$localhost!, :$localport, :$listen! *%attributes) { ... }

Creates a new socket and opens it.

Conjectural Stuff

Everything below this point should be considered as mere ideas for future evolution, not as things that a compiler write should implement unquestioningly.

IO::ACL

This is a basic abstraction; for better control, use the operating-system specific interfaces, over which this is a thin veneer.

class IO::ACL {
    has Str $.type; # "User", "Group", "Everyone", ???
    has Str $.id; # username or groupname; unused for $type eq "Everyone"
    has %.permissions;
            # Unsupported values may (or may not) throw
            # UnsupportedPermission when set or read
    has Path $.owningObject;
    ...
}

The permissions used in %permissions are:

Readable

Should be supported by all filesystems as an item to read from the hash for the group "Everyone".

Writeable

Should be supported by all filesystems as an item to read from the hash for the group "Everyone".

Executable

Supported on most Unix systems, anyway. Windows should be able to guess when this is read, and throw an exception if written to.

Default

An ACL of User,fred,Default sets the user "fred" to be the owner of the file. This can be done with groups too. Works on Unix, at least.

The $.owningObject attribute of ACL shows what the ACL is set on. On a Windows system, this can be a parent directory, as permissions are inherited.

IO::Pipe

class IO::Pipe does IO::Streamable does IO::Readable does IO::Writable {
    ...
}

Will need to set IO::Readable.isReadable and IO::Writable.isWriteable depending on opening method.

close()

If the file handle came from a piped open, close will additionally return Failure (aliased to $!) if one of the other system calls involved fails, or if the program exits with non-zero status. The exception object will contain any pertinent information. Closing a pipe also waits for the process executing on the pipe to complete, in case you want to look at the output of the pipe afterwards, and implicitly puts the exit status value into the Failure object if necessary.

IO::Pipe.to
method to(Str $command, *%opts --> Bool)
method to(Str *@command, *%opts --> Bool)

Opens a one-way pipe writing to $command. IO redirection for stderr is specified with :err(IO) or :err<Str>. Other IO redirection is done with feed operators. XXX how to specify "2>&1"?

IO::Pipe.from
method from(Str $command, *%opts --> Bool)
method from(Str *@command, *%opts --> Bool)

Opens a one-way pipe reading from $command. IO redirection for stderr is specified with :err(IO) or :err<Str>. Other IO redirection is done with feed operators. XXX how to specify "2>&1"?

IO::Pipe.pair
method pair(--> List of IO::Pipe)

A wrapper for pipe(2), returns a pair of IO objects representing the reader and writer ends of the pipe.

($r, $w) = IO::Pipe.pair;

OS-specific classes

Unix

Path::Unix

chown
multi chown ($uid = -1, $gid = -1, *@files --> Int)

Changes the owner (and group) of a list of files. The first two elements of the list must be the numeric uid and gid, in that order. A value of -1 in either position is interpreted by most systems to leave that value unchanged. Returns the number of files successfully changed.

$count = chown $uid, $gid, 'foo', 'bar';
chown $uid, $gid, @filenames;

On systems that support fchown, you might pass file handles among the files. On systems that don't support fchown, passing file handles produces a fatal error at run time.

Here's an example that looks up nonnumeric uids in the passwd file:

$user = prompt "User: ";
$pattern = prompt "Files: ";

($login,$pass,$uid,$gid) = getpwnam($user)
    or die "$user not in passwd file";

@ary = glob($pattern);      # expand filenames
chown $uid, $gid, @ary;

On most systems, you are not allowed to change the ownership of the file unless you're the superuser, although you should be able to change the group to any of your secondary groups. On insecure systems, these restrictions may be relaxed, but this is not a portable assumption. On POSIX systems, you can detect this condition this way:

use POSIX qw(sysconf _PC_CHOWN_RESTRICTED);
$can-chown-giveaway = not sysconf(_PC_CHOWN_RESTRICTED);
stat
IO.stat
$node.stat(Bool :$link); # :link does an lstat instead

Returns a stat buffer. If the lstat succeeds, the stat buffer evaluates to true, and additional file tests may be performed on the value. If the stat fails, all subsequent tests on the stat buffer also evaluate to false.

IO::Socket::Unix

role IO::Socket::Unix does IO::Socket {
    has Str $.RemoteAddr, # Remote Address
    has Str $.LocalAddr,  # Local Address
}
new
method new(
    Str  :$RemoteAddr,
    Str  :$LocalAddr,

    Bool :$Listener,   # Passed to IO::Socket.new()

    Bool :$Blocking,   # Passed to IO::Streamable.new()
    Bool :$NoOpen,     # Passed to IO::Streamable.new()

    --> IO::Socket::Unix
) {...}
pair
method pair(Int $domain, Int $type, Int $protocol --> List of IO)

A wrapper for socketpair(2), returns a pair of IO objects representing the reader and writer ends of the socket.

use IO::Socket;
($r, $w) = IO::Socket::Unix.pair(AF_UNIX, SOCK_STREAM, PF_UNSPEC);

IO::POSIX

Indicates that this object can perform standard posix IO operations. It implies IO::Readable and IO::Writeable.

method dup( --> IO)
has Bool $.blocking is rw
method flock(:$r,:$w --> Bool)
method funlock( --> Bool)
...

Unfilled

IO.ioctl

Available only as a handle method.

alarm
prompt
multi prompt (Str $prompt --> Str)

Should there be an IO::Interactive role?

Str.readpipe
sysopen
IO.sysseek
umask

Removed functions

IO.eof

Gone, see eoi IO::Seekable.

IO.fileno

See IO::Handle.

/(get|set)(host|net|proto|serv|sock).*/

Should be implemented by an external library.

lstat

Use stat with the :link option.

IO.name

Changed to .path, but we haven't gotten around to specifying this on all of them.

The .name method returns the name of the file/socket/uri the handle was opened with, if known. Returns Nil otherwise. There is no corresponding name() function.

pipe

Gone, see Pipe.pair

select(both)

Gone. (Note: for sub-second sleep, just use sleep with a fractional argument.)

IO.shutdown()

Gone, see IO::Socket.close(), $IO::Readable.isReadable, and $IO::Writeable.isWriteable

socketpair

Gone, see Socket.pair

IO.sysread

Gone, see IO::Readable.read().

IO.syswrite

Gone, see IO::Writeable.read().

utime

Gone, see Path.times.

IO::Buffered

Indicates that this object performs buffering. The management of the buffer is completely implementation specific.

method autoflush( --> Bool) is rw

Forces this object to keep its buffers empty

If set to nonzero, forces a flush right away and after every write or print on the currently selected output channel. Default is 0 (regardless of whether the channel is really buffered by the system or not; $OUT_FH.autoflush tells you only whether you've asked Perl explicitly to flush after each write). $*OUT will typically be line buffered if output is to the terminal and block buffered otherwise. Setting this variable is useful primarily when you are outputting to a pipe or socket, such as when you are running a Perl program under rsh and want to see the output as it's happening. This has no effect on input buffering.

IO::Streamable

This role represents objects that depend on some external resource, which means that data might not be available at request.

role IO::Streamable does IO {...}
new()
method new(
    Bool :$NoOpen,
    Bool :$Blocking,
    --> IO::Streamable
) {...}

Unless the NoOpen option is passed, an open will be done on the IO object when it is created.

If blocking is passed in, .blocking() is called (see below).

method blocking( --> Bool) is rw

This allows the user to control whether this object should do a blocking wait or immediately return in the case of not having data available.

uri
method uri(Str $uri --> IO::Streamable) {...}

This should be callable on the class, and act like a kind of "new()" function. When given a URI, it returns an IO::Streamable of the appropriate type, and throws an error when an inappropriate type is passed in. For example, calling IO::File.uri('http://....') will throw an error (but will suggest using just uri('http://...') instead).

IO::Encoded

This is a generic role for encoded data streams.

method encoding( --> Str) is rw
method locale( --> Str) is rw

Encoding and locale are required for sane conversions.

IO::Readable::Encoded

This role provides encoded access to a readable data stream, implies IO::Encoded. Might imply IO::Buffered, but that's not a requirement.

uri
method uri(Str $uri --> IO::Streamable);
sub uri(Str $uri --> IO::Streamable);

Returns an appropriate IO::Streamable descendant, with the type depending on the uri passed in. Here are some example mappings:

URI type IO type
======== =======
file:    IO::Path
ftp:     IO::Socket::INET (data channel)
http:    IO::Socket::INET

These can naturally be overridden or added to by other modules.

%*PROTOCOLS dynamic variable

For each protocol, stores a type name that should be instantiated by calling the uri constructor on that type, and passing in the appropriate uri.

AUTHORS

The authors of the related Perl 5 docs
Rod Adams <rod@rodadams.net>
Larry Wall <larry@wall.org>
Aaron Sherman <ajs@ajs.com>
Mark Stosberg <mark@summersault.com>
Carl Mäsak <cmasak@gmail.com>
Moritz Lenz <moritz@faui2k3.org>
Tim Nelson <wayland@wayland.id.au>
Daniel Ruoso <daniel@ruoso.com>
Lyle Hopkins <webmaster@cosmicperl.com>
Brent Laabs <bslaabs@gmail.com>
Tobias Leich <email@froggs.de>
Elizabeth Mattijsen <liz@wenzperl.nl>