diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-09-09 02:43:15 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-09-09 02:43:15 -0400 |
| commit | a306f945571c8027badd2ca673304991f628900c (patch) | |
| tree | 328f8c57bec5e0b71d7c589213badb3b4d03b39c | |
| parent | 1fbe2cb5dd1aa4b20411ee0c3b00310677373a55 (diff) | |
Add mktemp functionality
| -rw-r--r-- | builtins/path.c | 59 | ||||
| -rw-r--r-- | builtins/path.h | 5 | ||||
| -rw-r--r-- | compile.c | 3 | ||||
| -rw-r--r-- | environment.c | 2 | ||||
| -rw-r--r-- | test/paths.tm | 26 |
5 files changed, 91 insertions, 4 deletions
diff --git a/builtins/path.c b/builtins/path.c index d5305e30..fa15519a 100644 --- a/builtins/path.c +++ b/builtins/path.c @@ -28,6 +28,18 @@ PUREFUNC public Path_t Path$escape_text(Text_t text) return (Path_t)text; } +PUREFUNC public Path_t Path$concat(Path_t a, Path_t b) +{ + Path_t path = Text$concat(a, b); + while (Text$has(path, Pattern("/../"))) + path = Text$replace(path, Pattern("{!/}/../"), Text(""), Text(""), false); + + while (Text$has(path, Pattern("/./"))) + path = Text$replace(path, Pattern("/./"), Text("/"), Text(""), false); + + return path; +} + public Text_t Path$resolved(Path_t path, Path_t relative_to) { while (Text$has(path, Pattern("/../"))) @@ -129,13 +141,13 @@ static void _write(Path_t path, Text_t text, int mode, int permissions) const char *path_str = Text$as_c_string(path); int fd = open(path_str, mode, permissions); if (fd == -1) - fail("Could not write to file: %s\n%s", strerror(errno)); + fail("Could not write to file: %s\n%s", path_str, strerror(errno)); const char *str = Text$as_c_string(text); size_t len = strlen(str); ssize_t written = write(fd, str, len); if (written != (ssize_t)len) - fail("Could not write to file: %s\n%s", strerror(errno)); + fail("Could not write to file: %s\n%s", path_str, strerror(errno)); } public void Path$write(Path_t path, Text_t text, int permissions) @@ -161,7 +173,6 @@ public Text_t Path$read(Path_t path) fail("Could not read file: %k (%s)", &path, strerror(errno)); if ((sb.st_mode & S_IFMT) == S_IFREG) { // Use memory mapping if it's a real file: - printf("USING MMAP\n"); const char *mem = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); char *gc_mem = GC_MALLOC_ATOMIC((size_t)sb.st_size+1); memcpy(gc_mem, mem, (size_t)sb.st_size); @@ -270,6 +281,48 @@ public Array_t Path$subdirectories(Path_t path, bool include_hidden) return _filtered_children(path, include_hidden, S_IFDIR); } +public Path_t Path$unique_directory(Path_t path) +{ + if (Text$matches(path, Pattern("~/{..}"))) + path = Paths(Text$format("%s", getenv("HOME")), Text$slice(path, I(2), I(-1))); + const char *path_str = Text$as_c_string(path); + size_t len = strlen(path_str); + if (len >= PATH_MAX) fail("Path is too long: %s", path_str); + char buf[PATH_MAX] = {}; + strcpy(buf, path_str); + if (buf[len-1] == '/') + buf[--len] = '\0'; + char *created = mkdtemp(buf); + if (!created) fail("Failed to create temporary directory: %s (%s)", path_str, strerror(errno)); + return Text$format("%s/", created); +} + +public Text_t Path$write_unique(Path_t path, Text_t text) +{ + if (Text$matches(path, Pattern("~/{..}"))) + path = Paths(Text$format("%s", getenv("HOME")), Text$slice(path, I(2), I(-1))); + const char *path_str = Text$as_c_string(path); + size_t len = strlen(path_str); + if (len >= PATH_MAX) fail("Path is too long: %s", path_str); + char buf[PATH_MAX] = {}; + strcpy(buf, path_str); + + int64_t suffixlen = 0; + (void)Text$find(path, Pattern("{0+!X}{end}"), I(1), &suffixlen); + if (suffixlen < 0) suffixlen = 0; + + int fd = mkstemps(buf, suffixlen); + if (fd == -1) + fail("Could not write to unique file: %s\n%s", buf, strerror(errno)); + + const char *str = Text$as_c_string(text); + size_t write_len = strlen(str); + ssize_t written = write(fd, str, write_len); + if (written != (ssize_t)write_len) + fail("Could not write to file: %s\n%s", buf, strerror(errno)); + return Text$format("%s", buf); +} + public const TypeInfo Path$info = { .size=sizeof(Path_t), .align=__alignof__(Path_t), diff --git a/builtins/path.h b/builtins/path.h index ea1e99ae..0826f00d 100644 --- a/builtins/path.h +++ b/builtins/path.h @@ -13,7 +13,8 @@ #define Path(text) ((Path_t)Text(text)) #define Paths(...) ((Path_t)Texts(__VA_ARGS__)) -Path_t Path$escape_text(Text_t text); +PUREFUNC Path_t Path$concat(Path_t a, Path_t b); +PUREFUNC Path_t Path$escape_text(Text_t text); Path_t Path$resolved(Path_t path, Path_t relative_to); Path_t Path$relative(Path_t path, Path_t relative_to); bool Path$exists(Path_t path); @@ -30,6 +31,8 @@ void Path$create_directory(Path_t path, int permissions); Array_t Path$children(Path_t path, bool include_hidden); Array_t Path$files(Path_t path, bool include_hidden); Array_t Path$subdirectories(Path_t path, bool include_hidden); +Path_t Path$unique_directory(Path_t path); +Text_t Path$write_unique(Path_t path, Text_t text); extern const TypeInfo Path$info; @@ -1856,6 +1856,9 @@ CORD compile(env_t *env, ast_t *ast) case BINOP_CONCAT: { switch (operand_t->tag) { case TextType: { + const char *lang = Match(operand_t, TextType)->lang; + if (streq(lang, "Path")) + return CORD_all("Path$concat(", lhs, ", ", rhs, ")"); return CORD_all("Text$concat(", lhs, ", ", rhs, ")"); } case ArrayType: { diff --git a/environment.c b/environment.c index 0f5e1826..25b0cb79 100644 --- a/environment.c +++ b/environment.c @@ -264,7 +264,9 @@ env_t *new_compilation_unit(CORD *libname) {"remove", "Path$remove", "func(path:Path, ignore_missing=no)"}, {"resolved", "Path$resolved", "func(path:Path, relative_to=(./))->Path"}, {"subdirectories", "Path$children", "func(path:Path, include_hidden=no)->[Path]"}, + {"unique_directory", "Path$unique_directory", "func(path:Path)->Path"}, {"write", "Path$write", "func(path:Path, text:Text, permissions=0o644_i32)"}, + {"write_unique", "Path$write_unique", "func(path:Path, text:Text)->Path"}, )}, {"Shell", Type(TextType, .lang="Shell", .env=namespace_env(env, "Shell")), "Shell_t", "Shell$info", TypedArray(ns_entry_t, {"escape_text", "Shell$escape_text", "func(text:Text)->Shell"}, diff --git a/test/paths.tm b/test/paths.tm new file mode 100644 index 00000000..bc85c31a --- /dev/null +++ b/test/paths.tm @@ -0,0 +1,26 @@ +# Tests for file paths +func main(): + >> (/):exists() + = yes + >> (~/):exists() + = yes + + >> tmpdir := (/tmp/tomo-test-path-XXXXXX):unique_directory() + >> (/tmp):subdirectories():has(tmpdir) + = yes + + >> tmpfile := (tmpdir++(./one.txt)) + >> tmpfile:write("Hello world") + >> tmpfile:append("!") + >> tmpfile:read() + = "Hello world!" + >> tmpdir:files():has(tmpfile) + = yes + + >> tmpfile:remove() + + >> tmpdir:files():has(tmpfile) + = no + + >> tmpdir:remove() + |
