Update file path API and docs

This commit is contained in:
Bruce Hill 2025-03-16 16:11:43 -04:00
parent 88ab24c133
commit b0b2504a94
4 changed files with 372 additions and 31 deletions

View File

@ -37,33 +37,68 @@ intended. Paths can be created from text with slashes using
## Path Methods ## Path Methods
- [`func accessed(path:Path, follow_symlinks=yes -> Moment?)`](#accessed)
- [`func append(path: Path, text: Text, permissions: Int32 = 0o644[32] -> Void)`](#append) - [`func append(path: Path, text: Text, permissions: Int32 = 0o644[32] -> Void)`](#append)
- [`func append_bytes(path: Path, bytes: [Byte], permissions: Int32 = 0o644[32] -> Void)`](#append_bytes) - [`func append_bytes(path: Path, bytes: [Byte], permissions: Int32 = 0o644[32] -> Void)`](#append_bytes)
- [`func base_name(path: Path -> Text)`](#base_name) - [`func base_name(path: Path -> Text)`](#base_name)
- [`func by_line(path: Path -> func(->Text?)?)`](#by_line) - [`func by_line(path: Path -> func(->Text?)?)`](#by_line)
- [`func can_execute(path:Path -> Bool)`](#can_execute)
- [`func can_read(path:Path -> Bool)`](#can_read)
- [`func can_write(path:Path -> Bool)`](#can_write)
- [`func changed(path:Path, follow_symlinks=yes -> Moment?)`](#changed)
- [`func child(path: Path, child:Text -> Path)`](#child)
- [`func children(path: Path, include_hidden=no -> [Path])`](#children) - [`func children(path: Path, include_hidden=no -> [Path])`](#children)
- [`func create_directory(path: Path, permissions=0o755[32] -> Void)`](#create_directory) - [`func create_directory(path: Path, permissions=0o755[32] -> Void)`](#create_directory)
- [`func exists(path: Path -> Bool)`](#exists) - [`func exists(path: Path -> Bool)`](#exists)
- [`func extension(path: Path, full=yes -> Text)`](#extension) - [`func extension(path: Path, full=yes -> Text)`](#extension)
- [`func files(path: Path, include_hidden=no -> [Path])`](#files) - [`func files(path: Path, include_hidden=no -> [Path])`](#files)
- [`func from_components(components:[Text] -> Path)`](#from_components)
- [`func glob(path: Path -> [Path])`](#glob) - [`func glob(path: Path -> [Path])`](#glob)
- [`func group(path: Path, follow_symlinks=yes -> Text?)`](#group)
- [`func is_directory(path: Path, follow_symlinks=yes -> Bool)`](#is_directory) - [`func is_directory(path: Path, follow_symlinks=yes -> Bool)`](#is_directory)
- [`func is_file(path: Path, follow_symlinks=yes -> Bool)`](#is_file) - [`func is_file(path: Path, follow_symlinks=yes -> Bool)`](#is_file)
- [`func is_socket(path: Path, follow_symlinks=yes -> Bool)`](#is_socket) - [`func is_socket(path: Path, follow_symlinks=yes -> Bool)`](#is_socket)
- [`func is_symlink(path: Path -> Bool)`](#is_symlink) - [`func is_symlink(path: Path -> Bool)`](#is_symlink)
- [`func modified(path:Path, follow_symlinks=yes -> Moment?)`](#modified)
- [`func owner(path: Path, follow_symlinks=yes -> Text?)`](#owner)
- [`func parent(path: Path -> Path)`](#parent) - [`func parent(path: Path -> Path)`](#parent)
- [`func read(path: Path -> Text?)`](#read) - [`func read(path: Path -> Text?)`](#read)
- [`func read_bytes(path: Path -> [Byte]?)`](#read_bytes) - [`func read_bytes(path: Path -> [Byte]?)`](#read_bytes)
- [`func relative(path: Path, relative_to=(./) -> Path)`](#relative) - [`func relative_to(path: Path, relative_to=(./) -> Path)`](#relative_to)
- [`func remove(path: Path, ignore_missing=no -> Void)`](#remove) - [`func remove(path: Path, ignore_missing=no -> Void)`](#remove)
- [`func resolved(path: Path, relative_to=(./) -> Path)`](#resolved) - [`func resolved(path: Path, relative_to=(./) -> Path)`](#resolved)
- [`func set_owner(path:Path, owner=none:Text, group=none:Text, follow_symlinks=yes)`](#set_owner)
- [`func subdirectories(path: Path, include_hidden=no -> [Path])`](#subdirectories) - [`func subdirectories(path: Path, include_hidden=no -> [Path])`](#subdirectories)
- [`func unique_directory(path: Path -> Path)`](#unique_directory) - [`func unique_directory(path: Path -> Path)`](#unique_directory)
- [`func write(path: Path, text: Text, permissions=0o644[32] -> Void)`](#write) - [`func write(path: Path, text: Text, permissions=0o644[32] -> Void)`](#write)
- [`func write(path: Path, bytes: [Byte], permissions=0o644[32] -> Void)`](#write_bytes) - [`func write_bytes(path: Path, bytes: [Byte], permissions=0o644[32] -> Void)`](#write_bytes)
- [`func write_unique(path: Path, text: Text -> Path)`](#write_unique) - [`func write_unique(path: Path, text: Text -> Path)`](#write_unique)
- [`func write_unique_bytes(path: Path, bytes: [Byte] -> Path)`](#write_unique_bytes) - [`func write_unique_bytes(path: Path, bytes: [Byte] -> Path)`](#write_unique_bytes)
### `accessed`
Gets the file access time of a file.
```tomo
func accessed(path:Path, follow_symlinks: Bool = yes -> Moment?)
```
- `path`: The path of the file whose access time you want.
- `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:**
A [Moment](moments.md) representing when the file or directory was last
accessed, or `none` if no such file or directory exists.
**Example:**
```tomo
>> (./file.txt):accessed()
= Sun 16 Mar 2025 03:43:53 PM EDT EDT : Moment?
>> (./not-a-file):accessed()
= none : Moment?
```
---
### `append` ### `append`
Appends the given text to the file at the specified path, creating the file if Appends the given text to the file at the specified path, creating the file if
it doesn't already exist. Failure to write will result in a runtime error. it doesn't already exist. Failure to write will result in a runtime error.
@ -74,7 +109,7 @@ func append(path: Path, text: Text, permissions: Int32 = 0o644[32] -> Void)
- `path`: The path of the file to append to. - `path`: The path of the file to append to.
- `text`: The text to append to the file. - `text`: The text to append to the file.
- `permissions` (optional): The permissions to set on the file if it is being created (default is `0o644`). - `permissions`: The permissions to set on the file if it is being created (default is `0o644`).
**Returns:** **Returns:**
Nothing. Nothing.
@ -96,7 +131,7 @@ func append_bytes(path: Path, bytes: [Byte], permissions: Int32 = 0o644[32] -> V
- `path`: The path of the file to append to. - `path`: The path of the file to append to.
- `bytes`: The bytes to append to the file. - `bytes`: The bytes to append to the file.
- `permissions` (optional): The permissions to set on the file if it is being created (default is `0o644`). - `permissions`: The permissions to set on the file if it is being created (default is `0o644`).
**Returns:** **Returns:**
Nothing. Nothing.
@ -158,6 +193,127 @@ for line in (/dev/stdin):by_line()!:
--- ---
### `can_execute`
Returns whether or not a file can be executed by the current user/group.
```tomo
func can_execute(path: Path -> Bool)
```
- `path`: The path of the file to check.
**Returns:**
`yes` if the file or directory exists and the current user has execute permissions, otherwise `no`.
**Example:**
```tomo
>> (/bin/sh):can_execute()
= yes
>> (/usr/include/stdlib.h):can_execute()
= no
>> (/non/existant/file):can_execute()
= no
```
---
### `can_read`
Returns whether or not a file can be read by the current user/group.
```tomo
func can_read(path: Path -> Bool)
```
- `path`: The path of the file to check.
**Returns:**
`yes` if the file or directory exists and the current user has read permissions, otherwise `no`.
**Example:**
```tomo
>> (/usr/include/stdlib.h):can_read()
= yes
>> (/etc/shadow):can_read()
= no
>> (/non/existant/file):can_read()
= no
```
---
### `can_write`
Returns whether or not a file can be written by the current user/group.
```tomo
func can_write(path: Path -> Bool)
```
- `path`: The path of the file to check.
**Returns:**
`yes` if the file or directory exists and the current user has write permissions, otherwise `no`.
**Example:**
```tomo
>> (/tmp):can_write()
= yes
>> (/etc/passwd):can_write()
= no
>> (/non/existant/file):can_write()
= no
```
---
### `changed`
Gets the file change time of a file.
**Note:** this is the
["ctime"](https://en.wikipedia.org/wiki/Stat_(system_call)#ctime) of a file,
which is _not_ the file creation time.
```tomo
func changed(path:Path, follow_symlinks: Bool = yes -> Moment?)
```
- `path`: The path of the file whose change time you want.
- `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:**
A [Moment](moments.md) representing when the file or directory was last
changed, or `none` if no such file or directory exists.
**Example:**
```tomo
>> (./file.txt):changed()
= Sun 16 Mar 2025 03:43:53 PM EDT EDT : Moment?
>> (./not-a-file):changed()
= none : Moment?
```
---
### `child`
Return a path that is a child of another path.
```tomo
func child(path: Path, child: Text -> [Path])
```
- `path`: The path of a directory.
- `child`: The name of a child file or directory.
**Returns:**
A new path representing the child.
**Example:**
```tomo
>> (./directory):child("file.txt")
= (./directory/file.txt)
```
---
### `children` ### `children`
Returns a list of children (files and directories) within the directory at the specified path. Optionally includes hidden files. Returns a list of children (files and directories) within the directory at the specified path. Optionally includes hidden files.
@ -166,7 +322,7 @@ func children(path: Path, include_hidden=no -> [Path])
``` ```
- `path`: The path of the directory. - `path`: The path of the directory.
- `include_hidden` (optional): Whether to include hidden files, which start with a `.` (default is `no`). - `include_hidden`: Whether to include hidden files, which start with a `.` (default is `no`).
**Returns:** **Returns:**
A list of paths for the children. A list of paths for the children.
@ -188,7 +344,7 @@ func create_directory(path: Path, permissions=0o755[32] -> Void)
``` ```
- `path`: The path of the directory to create. - `path`: The path of the directory to create.
- `permissions` (optional): The permissions to set on the new directory (default is `0o644`). - `permissions`: The permissions to set on the new directory (default is `0o644`).
**Returns:** **Returns:**
Nothing. Nothing.
@ -224,11 +380,11 @@ func exists(path: Path -> Bool)
Returns the file extension of the file at the specified path. Optionally returns the full extension. Returns the file extension of the file at the specified path. Optionally returns the full extension.
```tomo ```tomo
func extension(path: Path, full=yes -> Text) func extension(path: Path, full:Bool = yes -> Text)
``` ```
- `path`: The path of the file. - `path`: The path of the file.
- `full` (optional): Whether to return everything after the first `.` in the - `full`: Whether to return everything after the first `.` in the
base name, or only the last part of the extension (default is `yes`). base name, or only the last part of the extension (default is `yes`).
**Returns:** **Returns:**
@ -253,11 +409,11 @@ no file extension.
Returns a list of files within the directory at the specified path. Optionally includes hidden files. Returns a list of files within the directory at the specified path. Optionally includes hidden files.
```tomo ```tomo
func files(path: Path, include_hidden=no -> [Path]) func files(path: Path, include_hidden: Bool = no -> [Path])
``` ```
- `path`: The path of the directory. - `path`: The path of the directory.
- `include_hidden` (optional): Whether to include hidden files (default is `no`). - `include_hidden`: Whether to include hidden files (default is `no`).
**Returns:** **Returns:**
A list of file paths. A list of file paths.
@ -270,6 +426,30 @@ A list of file paths.
--- ---
### `from_components`
Returns a path built from an array of path components.
```tomo
func from_components(components: [Text] -> Path)
```
- `components`: An array of path components.
**Returns:**
A path representing the given components.
**Example:**
```tomo
>> Path.from_components(["/", "usr", "include"])
= /usr/include
>> Path.from_components(["foo.txt"])
= ./foo.txt
>> Path.from_components(["~", ".local"])
= ~/.local
```
---
### `glob` ### `glob`
Perform a globbing operation and return an array of matching paths. Some glob Perform a globbing operation and return an array of matching paths. Some glob
specific details: specific details:
@ -312,6 +492,29 @@ A list of file paths that match the glob.
--- ---
### `group`
Get the owning group of a file or directory.
```tomo
func group(path: Path, follow_symlinks: Bool = yes -> Text?)
```
- `path`: The path whose owning group to get.
- `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:**
The name of the group which owns the file or directory, or `none` if the path does not exist.
**Example:**
```tomo
>> (/bin):group()
= "root"
>> (/non/existent/file):group()
= none:Text
```
---
### `is_directory` ### `is_directory`
Checks if the path represents a directory. Optionally follows symbolic links. Checks if the path represents a directory. Optionally follows symbolic links.
@ -320,7 +523,7 @@ func is_directory(path: Path, follow_symlinks=yes -> Bool)
``` ```
- `path`: The path to check. - `path`: The path to check.
- `follow_symlinks` (optional): Whether to follow symbolic links (default is `yes`). - `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:** **Returns:**
`True` if the path is a directory, `False` otherwise. `True` if the path is a directory, `False` otherwise.
@ -344,7 +547,7 @@ func is_file(path: Path, follow_symlinks=yes -> Bool)
``` ```
- `path`: The path to check. - `path`: The path to check.
- `follow_symlinks` (optional): Whether to follow symbolic links (default is `yes`). - `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:** **Returns:**
`True` if the path is a file, `False` otherwise. `True` if the path is a file, `False` otherwise.
@ -368,7 +571,7 @@ func is_socket(path: Path, follow_symlinks=yes -> Bool)
``` ```
- `path`: The path to check. - `path`: The path to check.
- `follow_symlinks` (optional): Whether to follow symbolic links (default is `yes`). - `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:** **Returns:**
`True` if the path is a socket, `False` otherwise. `True` if the path is a socket, `False` otherwise.
@ -401,6 +604,53 @@ func is_symlink(path: Path -> Bool)
--- ---
### `modified`
Gets the file modification time of a file.
```tomo
func modified(path:Path, follow_symlinks: Bool = yes -> Moment?)
```
- `path`: The path of the file whose modification time you want.
- `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:**
A [Moment](moments.md) representing when the file or directory was last
modified, or `none` if no such file or directory exists.
**Example:**
```tomo
>> (./file.txt):modified()
= Sun 16 Mar 2025 03:43:53 PM EDT EDT : Moment?
>> (./not-a-file):modified()
= none : Moment?
```
---
### `owner`
Get the owning user of a file or directory.
```tomo
func owner(path: Path, follow_symlinks: Bool = yes -> Text?)
```
- `path`: The path whose owner to get.
- `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:**
The name of the user who owns the file or directory, or `none` if the path does not exist.
**Example:**
```tomo
>> (/bin):owner()
= "root"
>> (/non/existent/file):owner()
= none:Text
```
---
### `parent` ### `parent`
Returns the parent directory of the file or directory at the specified path. Returns the parent directory of the file or directory at the specified path.
@ -471,15 +721,15 @@ returned.
--- ---
### `relative` ### `relative_to`
Returns the path relative to a given base path. By default, the base path is the current directory. Returns the path relative to a given base path. By default, the base path is the current directory.
```tomo ```tomo
func relative(path: Path, relative_to=(./) -> Path) func relative_to(path: Path, relative_to=(./) -> Path)
``` ```
- `path`: The path to convert. - `path`: The path to convert.
- `relative_to` (optional): The base path for the relative path (default is `./`). - `relative_to`: The base path for the relative path (default is `./`).
**Returns:** **Returns:**
The relative path. The relative path.
@ -500,7 +750,7 @@ func remove(path: Path, ignore_missing=no -> Void)
``` ```
- `path`: The path to remove. - `path`: The path to remove.
- `ignore_missing` (optional): Whether to ignore errors if the file or directory does not exist (default is `no`). - `ignore_missing`: Whether to ignore errors if the file or directory does not exist (default is `no`).
**Returns:** **Returns:**
Nothing. Nothing.
@ -520,7 +770,7 @@ func resolved(path: Path, relative_to=(./) -> Path)
``` ```
- `path`: The path to resolve. - `path`: The path to resolve.
- `relative_to` (optional): The base path for resolution (default is `./`). - `relative_to`: The base path for resolution (default is `./`).
**Returns:** **Returns:**
The resolved absolute path. The resolved absolute path.
@ -536,6 +786,28 @@ The resolved absolute path.
--- ---
### `set_owner`
Set the owning user and/or group for a path.
```tomo
func set_owner(path:Path, owner: Text? = none:Text, group: Text? = none:Text, follow_symlinks: Bool = yes)
```
- `path`: The path to change the permissions for.
- `owner`: If non-none, the new user to assign to be the owner of the file (default: `none`).
- `group`: If non-none, the new group to assign to be the owner of the file (default: `none`).
- `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:**
Nothing. If a path does not exist, a failure will be raised.
**Example:**
```tomo
(./file.txt):set_owner(owner="root", group="wheel")
```
---
### `subdirectories` ### `subdirectories`
Returns a list of subdirectories within the directory at the specified path. Optionally includes hidden subdirectories. Returns a list of subdirectories within the directory at the specified path. Optionally includes hidden subdirectories.
@ -544,7 +816,7 @@ func subdirectories(path: Path, include_hidden=no -> [Path])
``` ```
- `path`: The path of the directory. - `path`: The path of the directory.
- `include_hidden` (optional): Whether to include hidden subdirectories (default is `no`). - `include_hidden`: Whether to include hidden subdirectories (default is `no`).
**Returns:** **Returns:**
A list of subdirectory paths. A list of subdirectory paths.
@ -595,7 +867,7 @@ func write(path: Path, text: Text, permissions=0o644[32] -> Void)
- `path`: The path of the file to write to. - `path`: The path of the file to write to.
- `text`: The text to write to the file. - `text`: The text to write to the file.
- `permissions` (optional): The permissions to set on the file if it is created (default is `0o644`). - `permissions`: The permissions to set on the file if it is created (default is `0o644`).
**Returns:** **Returns:**
Nothing. Nothing.
@ -618,7 +890,7 @@ func write(path: Path, bytes: [Byte], permissions=0o644[32] -> Void)
- `path`: The path of the file to write to. - `path`: The path of the file to write to.
- `bytes`: An array of bytes to write to the file. - `bytes`: An array of bytes to write to the file.
- `permissions` (optional): The permissions to set on the file if it is created (default is `0o644`). - `permissions`: The permissions to set on the file if it is created (default is `0o644`).
**Returns:** **Returns:**
Nothing. Nothing.

View File

@ -334,45 +334,45 @@ env_t *new_compilation_unit(CORD libname)
{"year", "Moment$year", "func(moment:Moment,timezone=none:Text -> Int)"}, {"year", "Moment$year", "func(moment:Moment,timezone=none:Text -> Int)"},
)}, )},
{"Path", PATH_TYPE, "Path_t", "Path$info", TypedArray(ns_entry_t, {"Path", PATH_TYPE, "Path_t", "Path$info", TypedArray(ns_entry_t,
{"accessed", "Path$accessed", "func(path:Path, follow_symlinks=yes -> Moment?)"},
{"append", "Path$append", "func(path:Path, text:Text, permissions=Int32(0o644))"}, {"append", "Path$append", "func(path:Path, text:Text, permissions=Int32(0o644))"},
{"append_bytes", "Path$append_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"}, {"append_bytes", "Path$append_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"},
{"base_name", "Path$base_name", "func(path:Path -> Text)"}, {"base_name", "Path$base_name", "func(path:Path -> Text)"},
{"by_line", "Path$by_line", "func(path:Path -> func(->Text?)?)"}, {"by_line", "Path$by_line", "func(path:Path -> func(->Text?)?)"},
{"can_execute", "Path$can_execute", "func(path:Path -> Bool)"},
{"can_read", "Path$can_read", "func(path:Path -> Bool)"},
{"can_write", "Path$can_write", "func(path:Path -> Bool)"},
{"changed", "Path$changed", "func(path:Path, follow_symlinks=yes -> Moment?)"},
{"child", "Path$with_component", "func(path:Path, child:Text -> Path)"}, {"child", "Path$with_component", "func(path:Path, child:Text -> Path)"},
{"children", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, {"children", "Path$children", "func(path:Path, include_hidden=no -> [Path])"},
{"components", "Path$components", "func(path:Path -> [Text])"},
{"concatenated_with", "Path$concat", "func(a,b:Path -> Path)"}, {"concatenated_with", "Path$concat", "func(a,b:Path -> Path)"},
{"create_directory", "Path$create_directory", "func(path:Path, permissions=Int32(0o755))"}, {"create_directory", "Path$create_directory", "func(path:Path, permissions=Int32(0o755))"},
{"escape_int", "Int$value_as_text", "func(i:Int -> Path)"},
{"escape_path", "Path$escape_path", "func(path:Path -> Path)"},
{"escape_text", "Path$escape_text", "func(text:Text -> Path)"},
{"exists", "Path$exists", "func(path:Path -> Bool)"}, {"exists", "Path$exists", "func(path:Path -> Bool)"},
{"extension", "Path$extension", "func(path:Path, full=yes -> Text)"}, {"extension", "Path$extension", "func(path:Path, full=yes -> Text)"},
{"files", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, {"files", "Path$children", "func(path:Path, include_hidden=no -> [Path])"},
{"from_components", "Path$from_components", "func(components:[Text] -> Path)"}, {"from_components", "Path$from_components", "func(components:[Text] -> Path)"},
{"glob", "Path$glob", "func(path:Path -> [Path])"}, {"glob", "Path$glob", "func(path:Path -> [Path])"},
{"group", "Path$group", "func(path:Path, follow_symlinks=yes -> Text?)"},
{"is_directory", "Path$is_directory", "func(path:Path, follow_symlinks=yes -> Bool)"}, {"is_directory", "Path$is_directory", "func(path:Path, follow_symlinks=yes -> Bool)"},
{"is_file", "Path$is_file", "func(path:Path, follow_symlinks=yes -> Bool)"}, {"is_file", "Path$is_file", "func(path:Path, follow_symlinks=yes -> Bool)"},
{"is_pipe", "Path$is_pipe", "func(path:Path, follow_symlinks=yes -> Bool)"}, {"is_pipe", "Path$is_pipe", "func(path:Path, follow_symlinks=yes -> Bool)"},
{"is_socket", "Path$is_socket", "func(path:Path, follow_symlinks=yes -> Bool)"}, {"is_socket", "Path$is_socket", "func(path:Path, follow_symlinks=yes -> Bool)"},
{"is_symlink", "Path$is_symlink", "func(path:Path -> Bool)"}, {"is_symlink", "Path$is_symlink", "func(path:Path -> Bool)"},
{"modified", "Path$modified", "func(path:Path, follow_symlinks=yes -> Moment?)"},
{"owner", "Path$owner", "func(path:Path, follow_symlinks=yes -> Text?)"},
{"parent", "Path$parent", "func(path:Path -> Path)"}, {"parent", "Path$parent", "func(path:Path -> Path)"},
{"read", "Path$read", "func(path:Path -> Text?)"}, {"read", "Path$read", "func(path:Path -> Text?)"},
{"read_bytes", "Path$read_bytes", "func(path:Path, limit=none:Int -> [Byte]?)"}, {"read_bytes", "Path$read_bytes", "func(path:Path, limit=none:Int -> [Byte]?)"},
{"relative", "Path$relative", "func(path:Path, relative_to=(./) -> Path)"},
{"relative_to", "Path$relative_to", "func(path:Path, relative_to:Path -> Path)"}, {"relative_to", "Path$relative_to", "func(path:Path, relative_to:Path -> Path)"},
{"remove", "Path$remove", "func(path:Path, ignore_missing=no)"}, {"remove", "Path$remove", "func(path:Path, ignore_missing=no)"},
{"resolved", "Path$resolved", "func(path:Path, relative_to=(./) -> Path)"}, {"resolved", "Path$resolved", "func(path:Path, relative_to=(./) -> Path)"},
{"set_owner", "Path$set_owner", "func(path:Path, owner=none:Text, group=none:Text, follow_symlinks=yes)"},
{"subdirectories", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, {"subdirectories", "Path$children", "func(path:Path, include_hidden=no -> [Path])"},
{"unique_directory", "Path$unique_directory", "func(path:Path -> Path)"}, {"unique_directory", "Path$unique_directory", "func(path:Path -> Path)"},
{"write", "Path$write", "func(path:Path, text:Text, permissions=Int32(0o644))"}, {"write", "Path$write", "func(path:Path, text:Text, permissions=Int32(0o644))"},
{"write_bytes", "Path$write_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"}, {"write_bytes", "Path$write_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"},
{"write_unique", "Path$write_unique", "func(path:Path, text:Text -> Path)"}, {"write_unique", "Path$write_unique", "func(path:Path, text:Text -> Path)"},
{"write_unique_bytes", "Path$write_unique_bytes", "func(path:Path, bytes:[Byte] -> Path)"}, {"write_unique_bytes", "Path$write_unique_bytes", "func(path:Path, bytes:[Byte] -> Path)"},
{"modified", "Path$modified", "func(path:Path, follow_symlinks=yes -> Moment?)"},
{"accessed", "Path$accessed", "func(path:Path, follow_symlinks=yes -> Moment?)"},
{"changed", "Path$changed", "func(path:Path, follow_symlinks=yes -> Moment?)"},
)}, )},
// RNG must come after Path so we can read bytes from /dev/urandom // RNG must come after Path so we can read bytes from /dev/urandom
{"RNG", RNG_TYPE, "RNG_t", "RNG", TypedArray(ns_entry_t, {"RNG", RNG_TYPE, "RNG_t", "RNG", TypedArray(ns_entry_t,

View File

@ -4,6 +4,8 @@
#include <fcntl.h> #include <fcntl.h>
#include <gc.h> #include <gc.h>
#include <glob.h> #include <glob.h>
#include <grp.h>
#include <pwd.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
@ -215,6 +217,27 @@ public bool Path$is_symlink(Path_t path)
return (sb.st_mode & S_IFMT) == S_IFLNK; return (sb.st_mode & S_IFMT) == S_IFLNK;
} }
public bool Path$can_read(Path_t path)
{
path = Path$_expand_home(path);
const char *path_str = Path$as_c_string(path);
return (euidaccess(path_str, R_OK) == 0);
}
public bool Path$can_write(Path_t path)
{
path = Path$_expand_home(path);
const char *path_str = Path$as_c_string(path);
return (euidaccess(path_str, W_OK) == 0);
}
public bool Path$can_execute(Path_t path)
{
path = Path$_expand_home(path);
const char *path_str = Path$as_c_string(path);
return (euidaccess(path_str, X_OK) == 0);
}
public OptionalMoment_t Path$modified(Path_t path, bool follow_symlinks) public OptionalMoment_t Path$modified(Path_t path, bool follow_symlinks)
{ {
struct stat sb; struct stat sb;
@ -340,6 +363,45 @@ public OptionalText_t Path$read(Path_t path)
return Text$from_bytes(bytes); return Text$from_bytes(bytes);
} }
public OptionalText_t Path$owner(Path_t path, bool follow_symlinks)
{
struct stat sb;
int status = path_stat(path, follow_symlinks, &sb);
if (status != 0) return NONE_TEXT;
struct passwd *pw = getpwuid(sb.st_uid);
return pw ? Text$from_str(pw->pw_name) : NONE_TEXT;
}
public OptionalText_t Path$group(Path_t path, bool follow_symlinks)
{
struct stat sb;
int status = path_stat(path, follow_symlinks, &sb);
if (status != 0) return NONE_TEXT;
struct group *gr = getgrgid(sb.st_uid);
return gr ? Text$from_str(gr->gr_name) : NONE_TEXT;
}
public void Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t group, bool follow_symlinks)
{
uid_t owner_id = (uid_t)-1;
if (owner.length >= 0) {
struct passwd *pwd = getpwnam(Text$as_c_string(owner));
if (pwd == NULL) fail("Not a valid user: %k", &owner);
owner_id = pwd->pw_uid;
}
gid_t group_id = (gid_t)-1;
if (group.length >= 0) {
struct group *grp = getgrnam(Text$as_c_string(group));
if (grp == NULL) fail("Not a valid group: %k", &group);
group_id = grp->gr_gid;
}
const char *path_str = Path$as_c_string(path);
int result = follow_symlinks ? chown(path_str, owner_id, group_id) : lchown(path_str, owner_id, group_id);
if (result < 0)
fail("Could not set owner!");
}
public void Path$remove(Path_t path, bool ignore_missing) public void Path$remove(Path_t path, bool ignore_missing)
{ {
path = Path$_expand_home(path); path = Path$_expand_home(path);
@ -515,6 +577,7 @@ public Path_t Path$with_component(Path_t path, Text_t component)
}; };
ARRAY_INCREF(result.components); ARRAY_INCREF(result.components);
Array$insert(&result.components, &component, I(0), sizeof(Text_t)); Array$insert(&result.components, &component, I(0), sizeof(Text_t));
clean_components(&result.components);
return result; return result;
} }
@ -690,7 +753,7 @@ public Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type)
text = Text$concat(path->components.length > 0 ? Text("~/") : Text("~"), text); text = Text$concat(path->components.length > 0 ? Text("~/") : Text("~"), text);
else if (path->type == PATH_ABSOLUTE) else if (path->type == PATH_ABSOLUTE)
text = Text$concat(Text("/"), text); text = Text$concat(Text("/"), text);
else if (path->type == PATH_RELATIVE && path->components.length > 0 && !Text$equal_values(*(Text_t*)(path->components.data), Text(".."))) else if (path->type == PATH_RELATIVE && (path->components.length == 0 || !Text$equal_values(*(Text_t*)(path->components.data), Text(".."))))
text = Text$concat(path->components.length > 0 ? Text("./") : Text("."), text); text = Text$concat(path->components.length > 0 ? Text("./") : Text("."), text);
if (color) if (color)

View File

@ -24,6 +24,9 @@ bool Path$is_directory(Path_t path, bool follow_symlinks);
bool Path$is_pipe(Path_t path, bool follow_symlinks); bool Path$is_pipe(Path_t path, bool follow_symlinks);
bool Path$is_socket(Path_t path, bool follow_symlinks); bool Path$is_socket(Path_t path, bool follow_symlinks);
bool Path$is_symlink(Path_t path); bool Path$is_symlink(Path_t path);
bool Path$can_read(Path_t path);
bool Path$can_write(Path_t path);
bool Path$can_execute(Path_t path);
OptionalMoment_t Path$modified(Path_t path, bool follow_symlinks); OptionalMoment_t Path$modified(Path_t path, bool follow_symlinks);
OptionalMoment_t Path$accessed(Path_t path, bool follow_symlinks); OptionalMoment_t Path$accessed(Path_t path, bool follow_symlinks);
OptionalMoment_t Path$changed(Path_t path, bool follow_symlinks); OptionalMoment_t Path$changed(Path_t path, bool follow_symlinks);
@ -33,6 +36,9 @@ void Path$append(Path_t path, Text_t text, int permissions);
void Path$append_bytes(Path_t path, Array_t bytes, int permissions); void Path$append_bytes(Path_t path, Array_t bytes, int permissions);
OptionalText_t Path$read(Path_t path); OptionalText_t Path$read(Path_t path);
OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t limit); OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t limit);
void Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t group, bool follow_symlinks);
OptionalText_t Path$owner(Path_t path, bool follow_symlinks);
OptionalText_t Path$group(Path_t path, bool follow_symlinks);
void Path$remove(Path_t path, bool ignore_missing); void Path$remove(Path_t path, bool ignore_missing);
void Path$create_directory(Path_t path, int permissions); void Path$create_directory(Path_t path, int permissions);
Array_t Path$children(Path_t path, bool include_hidden); Array_t Path$children(Path_t path, bool include_hidden);