File

Standard library for filesystem operations.

Functions in this module let you read from and write to the filesystem. Many functions accept a path, which can be relative to the current working directory (see cwd()) or absolute. The companion module Path can be used to operate on filesystem paths.

Filesystem operations have many error cases, so functions in this module can raise a variety of exceptions, the most common ones being Exists, DoesntExist, EndOfFile, PermissionDenied, and NotPermitted.

To import all names from this module, use:

import File (*)

With no import, you can still access anything with the prefix File., like File.read_path.

Index

NameTypeDescription

read_path(path)

String -> String

Reads and returns the contents of the file at path.

write_path(path, contents)

(String, String) -> ()

Writes contents to the file at path path.

cp(from, to)

(String, String) -> Int

Copies the file at path from to path to, overwriting to if it exists.

mv(from, to)

(String, String) -> ()

Moves the file at path from to path to, overwriting to if it exists.

rm(path)

String -> ()

Removes the file at path.

rmdir(path)

String -> ()

Removes the directory at path.

ls(path)

String -> [String]

Lists the files in the directory given by path.

glob(pat)

String -> [String]

Returns a list of existing paths that match the glob pattern pat.

mkdir(path)

String -> ()

Makes a new directory at path.

mkdir_p(path)

String -> ()

Makes a new directory at path, creating parent directories if need be.

exists?(path)

String -> Bool

Returns true if path exists (regardless of whether it's a file, directory, device, etc.

stat(path)

String -> Stat

Returns metadata about the file at path.

lstat(path)

String -> Stat

Returns metadata about the file at path, but doesn't resolve symbolic links.

open(path, mode)

(String, OpenMode) -> Handle

Opens the file at path in the specified mode.

close(handle)

Handle -> ()

Closes the file handle handle that was opened previously by a call to open().

read(handle, amount)

(Handle, ReadAmount) -> String

Reads characters from the file given by handle in the amount amount.

write(handle, contents)

(Handle, String) -> ()

Writes contents to the file given by handle.

seek(handle, pos)

(Handle, Int) -> ()

Changes the file position of handle to pos, where 0 is the beginning of the file, and n is the n bytes after the beginning.

seek_end(handle)

Handle -> Int

Changes the file position of handle to the last position.

tell(handle)

Handle -> Int

Returns the file position of handle, where 0 is the beginning of the file and n is the n bytes after the beginning.

truncate(handle)

Handle -> ()

Truncates the file given by handle, removing all data from the current file position up to the end of the file.

cwd()

() -> String

Returns the current working directory, which starts as the directory in which par is initially run.

cd(path)

String -> ()

Changes the current working directory to path.

sys_temp_dir

String

The system's temporary directory.

make_temp_dir()

() -> String

Makes a new temporary directory within sys_temp_dir, returning its path.

write_temp(contents)

String -> String

Writes contents to a temporary file located within sys_temp_dir, returning its path.

ln(existing, new)

(String, String) -> ()

Creates a hard link at path new that mirrors the file at path existing.

ln_s(existing, new)

(String, String) -> ()

Creates a symbolic link at path new that points to path existing.

readlink(path)

String -> String

Resolves the symbolic link at path, returning the target path to which it refers to.

chmod(path, mode)

(String, Mode) -> ()

Changes the access controls for the file at the given path to mode.

chown(path, uid)

(String, Int) -> ()

Changes the owner of path to the user given by ID uid.

chgrp(path, gid)

(String, Int) -> ()

Changes the group of path to the group given by ID gid.

struct Stat

Stat

Contains metadata about a file.

enum FileType

FileType

Specifies the type of a file.

struct Mode

Mode

Contains access controls for a file.

enum Permissions

Permissions

Specifies permissions when accessing a file.

enum OpenMode

OpenMode

Specifies what mode to open a file in.

enum Handle

Handle

Represents an open file handle returned by open().

enum ReadAmount

ReadAmount

Specifies the amount to read from a file.

Functions

read_path : String -> String
read_path(path)

Reads and returns the contents of the file at path.

assert read_path(write_temp("foo")) == "foo"
write_path : (String, String) -> ()
write_path(path, contents)

Writes contents to the file at path path.

let path = Path.join(make_temp_dir(), "new")
write_path(path, "foo")
assert read_path(path) == "foo"
cp : (String, String) -> Int
cp(from, to)

Copies the file at path from to path to, overwriting to if it exists. Doesn't work with directories.

let path = write_temp("foo")
let new_path = Path.join(make_temp_dir(), "new")

cp(path, new_path)
assert read_path(new_path) == "foo"
assert read_path(path) == "foo"
mv : (String, String) -> ()
mv(from, to)

Moves the file at path from to path to, overwriting to if it exists. Doesn't work with directories.

let path = write_temp("foo")
let new_path = Path.join(make_temp_dir(), "new")

mv(path, new_path)
assert read_path(new_path) == "foo"
assert !exists?(path)
rm : String -> ()
rm(path)

Removes the file at path. Doesn't work with directories; use rmdir.

let path = write_temp("")
rm(path)
assert !exists?(path)
rmdir : String -> ()
rmdir(path)

Removes the directory at path. The directory must be empty to be removable.

let path = make_temp_dir()
rmdir(path)
assert !exists?(path)
ls : String -> [String]
ls(path)

Lists the files in the directory given by path. Returns a list of paths, where each path starts with path and is followed by a directory separator and the filename.

let path = make_temp_dir()
write_path(Path.join(path, "foo"), "")
write_path(Path.join(path, "bar"), "")
mkdir(Path.join(path, "baz"))
assert to_set(ls(path)) == #["foo", "bar", "baz"]
glob : String -> [String]
glob(pat)

Returns a list of existing paths that match the glob pattern pat. pat may contain the following special sequences:

  • ? — Matches any one character.
  • * — Matches zero or more characters, excluding the directory separator.
  • ** — Matches zero or more characters, including the directory separator.
  • {foo,bar,...} — Matches either foo or bar or etc.
  • [a,b,f-z,...] — Matches either the single character a or the single character b or the range of characters f through z or etc.

All characters that aren't ?, *, {, }, [, or ] match themselves, case-sensitive.

let path = make_temp_dir()
let foo_path = Path.join(path, "foo")
let bar_path = Path.join(path, "bar")
let baz_path = Path.join(path, "baz-foo")

write_path(foo_path, "")
write_path(bar_path, "")
mkdir(baz_path)

assert to_set(glob(Path.join(path, "*"))) ==
  #[foo_path, bar_path, baz_path]
assert to_set(glob(Path.join(path, "*foo"))) ==
  #[foo_path, baz_path]
assert to_set(glob(Path.join(path, "{foo,bar}"))) ==
  #[foo_path, bar_path]
mkdir : String -> ()
mkdir(path)

Makes a new directory at path. All parent directories must exist; to create parent directories as well, use mkdir_p().

let path = Path.join(make_temp_dir(), "new")
mkdir(path)
assert stat(path).type == Directory
mkdir_p : String -> ()
mkdir_p(path)

Makes a new directory at path, creating parent directories if need be.

let path = Path.join_all([make_temp_dir(), "new", "sub"])
mkdir_p(path)
assert stat(path).type == Directory
exists? : String -> Bool
exists?(path)

Returns true if path exists (regardless of whether it's a file, directory, device, etc.). If path is a symbolic link, returns true if the target path it refers to exists.

assert exists?(write_temp(""))
assert exists?(make_temp_dir())
assert !exists?(Path.join(make_temp_dir(), "new"))
assert !exists?("asdf")
stat : String -> Stat
stat(path)

Returns metadata about the file at path. If path is a symbolic link, resolves it and returns info about the target path it refers to. See the Stat struct for details on the result.

let contents = "hello"
let path = write_temp(contents)
let mode = Mode {
  owner = AllowRead
  group = AllowRead
  other = AllowRead
}
chmod(path, mode)

let meta = stat(path)
assert meta.size == length(contents)
assert meta.type == Regular
assert meta.mode == mode
lstat : String -> Stat
lstat(path)

Returns metadata about the file at path, but doesn't resolve symbolic links. If path is a symbolic link, returns info about the link itself, not the target path the link refers to. See the Stat struct for details on the result.

let path = write_temp("foo")
let new_path = Path.join(make_temp_dir(), "new")
ln_s(path, new_path)

let meta = lstat(new_path)
assert meta.type == Symlink
open : (String, OpenMode) -> Handle
open(path, mode)

Opens the file at path in the specified mode. See OpenMode for a description of the different modes. Returns a file handle that can be passed to read(), write(), close(), etc. In all modes except Append, the initial file position is 0, meaning reads/ writes will occur at the beginning of the file. In the Append mode, writes occur at the end of the file. position() can be used to modify the position to read/write at.

let h = open(write_temp("hey\nthere"), Read)
ensure close(h) after
  assert read(h, All) == "hey\nthere"
close : Handle -> ()
close(handle)

Closes the file handle handle that was opened previously by a call to open(). After this function returns, [handle] may no longer be used for any operations. If it is used, a ClosedHandle exception will be raised.

let path = write_temp("")
let h = open(path, Write)

ensure close(h) after
  write(h, "hello")
assert read_path(path) == "hello"
read : (Handle, ReadAmount) -> String
read(handle, amount)

Reads characters from the file given by handle in the amount amount. See the ReadAmount struct for details on how to specify an amount.

If you try to read from the end of a file, raises EndOfFile.

let h = open(write_temp("hey\nthere"), Read)
ensure close(h) after
  assert read(h, Line) == "hey\n"
  assert read(h, Chars(2)) == "th"
  assert read(h, All) == "ere"
write : (Handle, String) -> ()
write(handle, contents)

Writes contents to the file given by handle.

let path = write_temp("hey\nthere")
let h = open(path, Write)

// File should be truncated.
assert read_path(path) == ""

ensure close(h) after
  write(h, "hello\n")
  write(h, "foo")
  write(h, "end")
assert read_path(path) == "hello\nfooend"
seek : (Handle, Int) -> ()
seek(handle, pos)

Changes the file position of handle to pos, where 0 is the beginning of the file, and n is the n bytes after the beginning. The next read or write will occur at position pos.

let path = write_temp("hey\nthere")
let h = open(path, ReadWrite)

ensure close(h) after
  seek(h, 4)
  write(h, "where")
assert read_path(path) == "hey\nwhere"
seek_end : Handle -> Int
seek_end(handle)

Changes the file position of handle to the last position. This is useful for writing at the end of the file or getting the size of the file with tell().

let contents = "hey\nthere"
let path = write_temp(contents)
let h = open(path, ReadWrite)

ensure close(h) after
  let end = seek_end(h)
  assert end == length(contents)
  assert tell(h) == end
  write(h, "where")
assert read_path(path) == "hey\ntherewhere"
tell : Handle -> Int
tell(handle)

Returns the file position of handle, where 0 is the beginning of the file and n is the n bytes after the beginning.

let h = open(write_temp("hey\nthere"), ReadWrite)
ensure close(h) after
  assert tell(h) == 0
  assert read(h, Line) == "hey\n"
  assert tell(h) == 4

  write(h, "foo")
  assert tell(h) == 7

  seek(h, 2)
  assert tell(h) == 2
  assert read(h, All) == "y\nfoore"
truncate : Handle -> ()
truncate(handle)

Truncates the file given by handle, removing all data from the current file position up to the end of the file.

let path = write_temp("hey\nthere")
let h = open(path, ReadWrite)

ensure close(h) after
  seek(h, 4)
  truncate(h)
assert read_path(path) == "hey\n"
cwd : () -> String
cwd()

Returns the current working directory, which starts as the directory in which par is initially run.

// Prints the current working directory.
print(cwd())
cd : String -> ()
cd(path)

Changes the current working directory to path.

// Make foo the new working directory.
cd("foo")
sys_temp_dir : String
sys_temp_dir

The system's temporary directory. Use make_temp_dir() to make a new temporary directory within this directory, and write_temp() to write a temporary file into this directory.

// Write to temporary file foo in system temp directory.
let path = Path.join(sys_temp_dir, "foo")
write_path(path, "bar")
make_temp_dir : () -> String
make_temp_dir()

Makes a new temporary directory within sys_temp_dir, returning its path. See write_temp() if you want to write to a temporary file.

let path = make_temp_dir()
assert Path.dir(path) == sys_temp_dir
assert ls(path) == []
write_temp : String -> String
write_temp(contents)

Writes contents to a temporary file located within sys_temp_dir, returning its path. See make_temp_dir() if you want to create a new temporary directory.

let path = write_temp("foo")
assert Path.dir(path) == sys_temp_dir
assert read_path(path) == "foo"
ln : (String, String) -> ()
ln(existing, new)

Creates a hard link at path new that mirrors the file at path existing. A hard link is an alias that refers to the same underlying file (i.e. inode in linux). If existing is deleted or moved, new will still exist with its contents. If existing or new is modified, the other will also be modified, as the same underlying file is changed. On many systems, hard links to directories are not allowed.

let path = write_temp("foo")
let new_path = Path.join(make_temp_dir(), "new")

ln(path, new_path)
assert read_path(new_path) == "foo"

// Hard link should be maintained even when original is deleted.
rm(path)
assert read_path(new_path) == "foo"
ln_s : (String, String) -> ()
ln_s(existing, new)

Creates a symbolic link at path new that points to path existing. A symbolic link is a special file whose contents are a filesystem path; in this way, it's just a pointer to existing. If existing is deleted or moved, new will refer to a non-existent path. If existing is modified, new will still point to existing, and hence you can see the changes. Symbolic links can refer to directories.

let path = write_temp("foo")
let new_path = Path.join(make_temp_dir(), "new")

ln_s(path, new_path)
assert lstat(new_path).type == Symlink
assert stat(new_path).type == Regular
assert read_path(new_path) == "foo"

Resolves the symbolic link at path, returning the target path to which it refers to.

let path = write_temp("")
let new_path = Path.join(make_temp_dir(), "new")
ln_s(path, new_path)
assert readlink(new_path) == path
chmod : (String, Mode) -> ()
chmod(path, mode)

Changes the access controls for the file at the given path to mode. See the Mode struct for details on what can be modified.

On Windows, only the owner permissions can be changed. For regular files, the supported values are AllowRead and AllowReadWrite. For directories, only AllowReadWrite is supported. If you specify an unsupported value, raises InvalidWindowsPermissions.

let path = write_temp("")
let new_mode = Mode {
  owner = AllowReadWrite
  group = AllowExec
  other = AllowNone
}
chmod(path, new_mode)

if OS.windows? then
  assert stat(path).mode.owner == AllowReadWrite
else
  assert stat(path).mode == new_mode
chown : (String, Int) -> ()
chown(path, uid)

Changes the owner of path to the user given by ID uid. This can only be run by a super-user on UNIX systems. On Windows, raises NotPermitted.

// Change owner of "/foo" to the user with ID 0 (generally root).
chown("/foo", 0)
chgrp : (String, Int) -> ()
chgrp(path, gid)

Changes the group of path to the group given by ID gid. This can only be run by the owner of the file or a super-user on UNIX systems. On Windows, raises NotPermitted.

// Change group of "foo/bar" to the group with ID 30.
chgrp("foo/bar", 30)

Types

struct Stat

Contains metadata about a file. Returned by stat().

  • size — The file's size.
  • type — The file type (file, driectory, symlink, etc.); see FileType.
  • atime — The last Unix time the file was accessed, represented as the number of seconds since the epoch.
  • mtime — The last Unix time the file was modified, represented as the number of seconds since the epoch.
  • mode — The mode of the file, containing permissions; see Mode.
  • uid — The user ID of the file's owner.
  • gid — The group ID of the file's group.
struct Stat {
  size : Int
  type : FileType
  atime : Int
  mtime : Int
  mode : Mode
  uid : Int
  gid : Int
}
enum FileType

Specifies the type of a file. Returned by stat() as part of the Stat struct.

  • Regular — A regular file.
  • Directory — A directory.
  • Symlink — A symbolic link.
  • Device — A device (e.g. /dev/null is the null device).
  • Other — Some other file (e.g. named pipe, unix socket, etc.).
enum FileType {
  Regular
  Directory
  Symlink
  Device
  Other
}
struct Mode

Contains access controls for a file. Returned by stat() as part of the Stat struct, and can be changed via chmod(). owner, group, and other are the permissions granted to the respective groups. See Permissions for values that owner, group, and other can take on.

On Windows, only owner permissions are relevant, and the only possible values are AllowRead and AllowReadWrite. The values for group and other will always be the same as owner.

struct Mode {
  owner : Permissions
  group : Permissions
  other : Permissions
}
enum Permissions

Specifies permissions when accessing a file.

  • AllowNone — No operations are allowed.
  • AllowRead — Reading is permitted.
  • AllowWrite — Writing is permitted.
  • AllowExec — Executing is permitted.
  • AllowReadWrite — Reading and writing are permitted.
  • AllowWriteExec — Writing and executing are permitted.
  • AllowAll — Reading, writing, and executing are permitted.
enum Permissions {
  AllowNone
  AllowRead
  AllowWrite
  AllowExec
  AllowReadWrite
  AllowReadExec
  AllowWriteExec
  AllowAll
}
enum OpenMode

Specifies what mode to open a file in. Passed to open().

  • Read — Open a file for reading.
  • Write — Open a file for writing. The file is truncated (i.e. all current contents are erased). If the file doesn't exist, it is created.
  • Append — Open a file for appending. If the file doesn't exist, it is created. All writes occur at the end of the file.
  • ReadWrite — Open a file for reading and writing. If the file doesn't exist, it is created.
  • Exclusive — Create and open a file for writing. If the file already exists, raises Exists.
enum OpenMode {
  Read
  Write
  Append
  ReadWrite
  Exclusive
}
enum Handle

Represents an open file handle returned by open().

enum ReadAmount

Specifies the amount to read from a file.

  • Chars(n) — Read n characters.
  • Line — Read one line.
  • All — Read all remaining contents.
enum ReadAmount {
  Chars(Int)
  Line
  All
}

Exceptions

exception Exists

The file already exists. Raised if, for instance, mkdir() is called with an existing path.

exception DoesntExist

No such file or directory exists. Raised if, for instance, you stat() a non-existent path.

exception EndOfFile

Raised by read() if you attempt to read at the end of a file.

exception PermissionDenied

There aren't sufficient permissions to access a file.

exception NotPermitted

Either the operation doesn't make sense, like using rm() on a directory, or you need root permissions, like when using chown().

exception ClosedHandle

The file handle has been closed already.

exception BadHandle

The file handle is either invalid or doesn't support this operation. Raised if, for instance, read() is called on a file that is opened in Write mode.

exception TempUnavailable

A resource is temporarily unavailable; try again.

exception Busy

A resource is busy and can't be used.

exception DiskQuotaExceeded

The disk quota has been exceeded.

exception BadAddress

Bad address in a system call argument.

exception TooLarge

The file is too large.

exception Interrupted

A system call was interrupted.

exception InvalidArgument

An invalid file was given as an argument; for instance, a non-symlink was passed to readlink().

exception IOError

Generic IO error. On Windows, this is raised in place of NotDirectory.

exception IllegalDirOperation

There was an illegal operation on a directory. Raised if, for instance, cp() is called on a directory.

exception TooManyOpen

There are too many open files.

There are too many links.

exception SymlinkLoop

There are too many levels of symbolic links to traverse.

exception NameTooLong

A file's name is too long.

exception TableOverflow

The file table has overflowed.

exception NoDevice

No such device exists.

exception OutOfMemory

There isn't enough memory.

exception NoSpace

No space is left on the device.

exception NotBlockDevice

A block device is required.

exception NotDirectory

A directory is required. Raised if, for instance, you try to ls() or rmdir() a regular file.

exception NotSupported

An operation is not supported.

exception NoDeviceOrAddress

No such device or address.

exception BrokenPipe

A pipe was broken. Occurs when you're trying to write to a pipe, but nothing is reading from that pipe.

exception ReadOnlyFS

The filesystem is read-only.

exception InvalidSeek

A seek operation was performed on an invalid file type.

exception NoProcess

No such process.

exception StaleHandle

The file handle is stale.

You're trying to perform an operation across two separate filesystems.

exception SystemLimit

A system limit was reached.

exception InvalidWindowsPermissions(Permissions)

For regular files on Windows, AllowRead and AllowReadWrite are the only supported permissions. For directories on Windows, only AllowReadWrite is supported.

exception UnknownError(String)

An unknown error, where the argument is the error code.