aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.md2
-rw-r--r--api/api.md4
-rw-r--r--api/paths.md4
-rw-r--r--api/paths.yaml7
-rw-r--r--man/man3/tomo-Path.create_directory.37
-rw-r--r--src/environment.c3
-rw-r--r--src/modules.c4
-rw-r--r--src/stdlib/paths.c12
-rw-r--r--src/stdlib/paths.h2
9 files changed, 35 insertions, 10 deletions
diff --git a/CHANGES.md b/CHANGES.md
index f850d535..f54e4d05 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -22,6 +22,8 @@
- Added `Path.lines()`.
- Added `Text.find(text, target, start=1)`.
- Added `at_cleanup()` to register cleanup functions.
+- Added `recursive` argument to `Path.create_directory()` to create parent
+ directories if needed.
- Deprecated:
- Sets are no longer a separate type with separate methods.
- Instead of sets, use tables with a value type of `{KeyType:Empty}`.
diff --git a/api/api.md b/api/api.md
index 5c9dc9c4..ec6ffb49 100644
--- a/api/api.md
+++ b/api/api.md
@@ -2777,15 +2777,17 @@ assert (./directory).children(include_hidden=yes) == [".git", "foo.txt"]
## Path.create_directory
```tomo
-Path.create_directory : func(path: Path, permissions = Int32(0o755) -> Void)
+Path.create_directory : func(path: Path, permissions = Int32(0o755), recursive = yes -> Void)
```
Creates a new directory at the specified path with the given permissions. If any of the parent directories do not exist, they will be created as needed.
+
Argument | Type | Description | Default
---------|------|-------------|---------
path | `Path` | The path of the directory to create. | -
permissions | `` | The permissions to set on the new directory. | `Int32(0o755)`
+recursive | `` | If set to `yes`, then recursively create any parent directories if they don't exist, otherwise fail if the parent directory does not exist. When set to `yes`, this function behaves like `mkdir -p`. | `yes`
**Return:** Nothing.
diff --git a/api/paths.md b/api/paths.md
index 07f0560b..4beabdc2 100644
--- a/api/paths.md
+++ b/api/paths.md
@@ -253,15 +253,17 @@ assert (./directory).children(include_hidden=yes) == [".git", "foo.txt"]
## Path.create_directory
```tomo
-Path.create_directory : func(path: Path, permissions = Int32(0o755) -> Void)
+Path.create_directory : func(path: Path, permissions = Int32(0o755), recursive = yes -> Void)
```
Creates a new directory at the specified path with the given permissions. If any of the parent directories do not exist, they will be created as needed.
+
Argument | Type | Description | Default
---------|------|-------------|---------
path | `Path` | The path of the directory to create. | -
permissions | `` | The permissions to set on the new directory. | `Int32(0o755)`
+recursive | `` | If set to `yes`, then recursively create any parent directories if they don't exist, otherwise fail if the parent directory does not exist. When set to `yes`, this function behaves like `mkdir -p`. | `yes`
**Return:** Nothing.
diff --git a/api/paths.yaml b/api/paths.yaml
index 8fbd18dc..65d63671 100644
--- a/api/paths.yaml
+++ b/api/paths.yaml
@@ -258,6 +258,7 @@ Path.create_directory:
description: >
Creates a new directory at the specified path with the given permissions. If
any of the parent directories do not exist, they will be created as needed.
+ note: >
return:
type: 'Void'
description: >
@@ -271,6 +272,12 @@ Path.create_directory:
default: 'Int32(0o755)'
description: >
The permissions to set on the new directory.
+ recursive:
+ default: 'yes'
+ description: >
+ If set to `yes`, then recursively create any parent directories if they
+ don't exist, otherwise fail if the parent directory does not exist. When
+ set to `yes`, this function behaves like `mkdir -p`.
example: |
(./new_directory).create_directory()
diff --git a/man/man3/tomo-Path.create_directory.3 b/man/man3/tomo-Path.create_directory.3
index adfe7e97..db50d468 100644
--- a/man/man3/tomo-Path.create_directory.3
+++ b/man/man3/tomo-Path.create_directory.3
@@ -2,14 +2,14 @@
.\" Copyright (c) 2025 Bruce Hill
.\" All rights reserved.
.\"
-.TH Path.create_directory 3 2025-05-17 "Tomo man-pages"
+.TH Path.create_directory 3 2025-11-27 "Tomo man-pages"
.SH NAME
Path.create_directory \- make a directory
.SH LIBRARY
Tomo Standard Library
.SH SYNOPSIS
.nf
-.BI Path.create_directory\ :\ func(path:\ Path,\ permissions\ =\ Int32(0o755)\ ->\ Void)
+.BI Path.create_directory\ :\ func(path:\ Path,\ permissions\ =\ Int32(0o755),\ recursive\ =\ yes\ ->\ Void)
.fi
.SH DESCRIPTION
Creates a new directory at the specified path with the given permissions. If any of the parent directories do not exist, they will be created as needed.
@@ -24,10 +24,13 @@ l l l l.
Name Type Description Default
path Path The path of the directory to create. -
permissions The permissions to set on the new directory. Int32(0o755)
+recursive If set to `yes`, then recursively create any parent directories if they don't exist, otherwise fail if the parent directory does not exist. When set to `yes`, this function behaves like `mkdir -p`. yes
.TE
.SH RETURN
Nothing.
+.SH NOTES
+
.SH EXAMPLES
.EX
(./new_directory).create_directory()
diff --git a/src/environment.c b/src/environment.c
index 6075ce49..88a15bb5 100644
--- a/src/environment.c
+++ b/src/environment.c
@@ -310,7 +310,8 @@ env_t *global_env(bool source_mapping) {
{"child", "Path$child", "func(path:Path, child:Text -> Path)"}, //
{"children", "Path$children", "func(path:Path, include_hidden=no -> [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), recursive=yes)"}, //
{"current_dir", "Path$current_dir", "func(->Path)"}, //
{"exists", "Path$exists", "func(path:Path -> Bool)"}, //
{"expand_home", "Path$expand_home", "func(path:Path -> Path)"}, //
diff --git a/src/modules.c b/src/modules.c
index 23b8a0a0..df6bade3 100644
--- a/src/modules.c
+++ b/src/modules.c
@@ -153,10 +153,10 @@ bool try_install_module(module_info_t mod, bool ask_confirmation) {
const char *extension = p + 1;
Path_t tmpdir = Path$unique_directory(Path("/tmp/tomo-module-XXXXXX"));
tmpdir = Path$child(tmpdir, Text$from_str(mod.name));
- Path$create_directory(tmpdir, 0755);
+ Path$create_directory(tmpdir, 0755, true);
xsystem("curl ", mod.url, " -o ", tmpdir);
- Path$create_directory(dest, 0755);
+ Path$create_directory(dest, 0755, true);
if (streq(extension, ".zip")) xsystem("unzip ", tmpdir, "/", filename, " -d ", dest);
else if (streq(extension, ".tar.gz") || streq(extension, ".tar"))
xsystem("tar xf ", tmpdir, "/", filename, " -C ", dest);
diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c
index 22effad7..6c99cf0b 100644
--- a/src/stdlib/paths.c
+++ b/src/stdlib/paths.c
@@ -468,11 +468,19 @@ void Path$remove(Path_t path, bool ignore_missing) {
}
public
-void Path$create_directory(Path_t path, int permissions) {
+void Path$create_directory(Path_t path, int permissions, bool recursive) {
+retry:
path = Path$expand_home(path);
const char *c_path = Path$as_c_string(path);
int status = mkdir(c_path, (mode_t)permissions);
- if (status != 0 && errno != EEXIST) fail("Could not create directory: ", c_path, " (", strerror(errno), ")");
+ if (status != 0) {
+ if (recursive && errno == ENOENT) {
+ Path$create_directory(Path$parent(path), permissions, recursive);
+ goto retry;
+ } else if (errno != EEXIST) {
+ fail("Could not create directory: ", c_path, " (", strerror(errno), ")");
+ }
+ }
}
static List_t _filtered_children(Path_t path, bool include_hidden, mode_t filter) {
diff --git a/src/stdlib/paths.h b/src/stdlib/paths.h
index 3b1f3ce6..677631b2 100644
--- a/src/stdlib/paths.h
+++ b/src/stdlib/paths.h
@@ -41,7 +41,7 @@ void Path$set_owner(Path_t path, OptionalText_t owner, OptionalText_t group, boo
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$create_directory(Path_t path, int permissions);
+void Path$create_directory(Path_t path, int permissions, bool recursive);
List_t Path$children(Path_t path, bool include_hidden);
List_t Path$files(Path_t path, bool include_hidden);
List_t Path$subdirectories(Path_t path, bool include_hidden);