aboutsummaryrefslogtreecommitdiff
path: root/src/stdlib/paths.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/stdlib/paths.c')
-rw-r--r--src/stdlib/paths.c87
1 files changed, 79 insertions, 8 deletions
diff --git a/src/stdlib/paths.c b/src/stdlib/paths.c
index ed8383fd..e3028cce 100644
--- a/src/stdlib/paths.c
+++ b/src/stdlib/paths.c
@@ -324,6 +324,77 @@ Result_t Path$append_bytes(Path_t path, List_t bytes, int permissions) {
return _write(path, bytes, O_WRONLY | O_APPEND | O_CREAT, permissions);
}
+typedef struct {
+ const char *path_str;
+ int fd;
+ int mode;
+ int permissions;
+} writer_data_t;
+
+static Result_t _write_bytes_to_fd(List_t bytes, bool close_file, void *userdata) {
+ writer_data_t *data = userdata;
+ if (bytes.length > 0) {
+ int fd = open(data->path_str, data->mode, data->permissions);
+ if (fd == -1) {
+ if (errno == EMFILE || errno == ENFILE) {
+ // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that
+ // will be closed by GC finalizers.
+ GC_gcollect();
+ fd = open(data->path_str, data->mode, data->permissions);
+ }
+ if (fd == -1) return FailureResult("Could not write to file: ", data->path_str, " (", strerror(errno), ")");
+ }
+ data->fd = fd;
+
+ if (bytes.stride != 1) List$compact(&bytes, 1);
+ ssize_t written = write(data->fd, bytes.data, (size_t)bytes.length);
+ if (written != (ssize_t)bytes.length)
+ return FailureResult("Could not write to file: ", data->path_str, " (", strerror(errno), ")");
+ }
+ // After first successful write, all writes are appends
+ data->mode = (O_WRONLY | O_CREAT | O_APPEND);
+
+ if (close_file && data->fd != -1) {
+ if (close(data->fd) == -1)
+ return FailureResult("Failed to close file: ", data->path_str, " (", strerror(errno), ")");
+ data->fd = -1;
+ }
+ return SuccessResult;
+}
+
+static Result_t _write_text_to_fd(Text_t text, bool close_file, void *userdata) {
+ return _write_bytes_to_fd(Text$utf8(text), close_file, userdata);
+}
+
+static void _writer_cleanup(writer_data_t *data) {
+ if (data && data->fd != -1) {
+ close(data->fd);
+ data->fd = -1;
+ }
+}
+
+public
+Closure_t Path$byte_writer(Path_t path, bool append, int permissions) {
+ path = Path$expand_home(path);
+ const char *path_str = Path$as_c_string(path);
+ int mode = append ? (O_WRONLY | O_CREAT | O_APPEND) : (O_WRONLY | O_CREAT | O_TRUNC);
+ writer_data_t *userdata =
+ new (writer_data_t, .fd = -1, .path_str = path_str, .mode = mode, .permissions = permissions);
+ GC_register_finalizer(userdata, (void *)_writer_cleanup, NULL, NULL, NULL);
+ return (Closure_t){.fn = _write_bytes_to_fd, .userdata = userdata};
+}
+
+public
+Closure_t Path$writer(Path_t path, bool append, int permissions) {
+ path = Path$expand_home(path);
+ const char *path_str = Path$as_c_string(path);
+ int mode = append ? (O_WRONLY | O_CREAT | O_APPEND) : (O_WRONLY | O_CREAT | O_TRUNC);
+ writer_data_t *userdata =
+ new (writer_data_t, .fd = -1, .path_str = path_str, .mode = mode, .permissions = permissions);
+ GC_register_finalizer(userdata, (void *)_writer_cleanup, NULL, NULL, NULL);
+ return (Closure_t){.fn = _write_text_to_fd, .userdata = userdata};
+}
+
public
OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) {
path = Path$expand_home(path);
@@ -331,8 +402,8 @@ OptionalList_t Path$read_bytes(Path_t path, OptionalInt_t count) {
int fd = open(path_str, O_RDONLY);
if (fd == -1) {
if (errno == EMFILE || errno == ENFILE) {
- // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will
- // be closed by GC finalizers.
+ // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that
+ // will be closed by GC finalizers.
GC_gcollect();
fd = open(path_str, O_RDONLY);
}
@@ -619,7 +690,7 @@ bool Path$has_extension(Path_t path, Text_t extension) {
if (extension.length == 0)
return !Text$has(Text$from(last, I(2)), Text(".")) || Text$equal_values(last, Text(".."));
- if (!Text$starts_with(extension, Text("."), NULL)) extension = Texts(Text("."), extension);
+ if (!Text$starts_with(extension, Text("."), NULL)) extension = Text$concat(Text("."), extension);
return Text$ends_with(Text$from(last, I(2)), extension, NULL);
}
@@ -705,8 +776,8 @@ OptionalClosure_t Path$by_line(Path_t path) {
FILE *f = fopen(path_str, "r");
if (f == NULL) {
if (errno == EMFILE || errno == ENFILE) {
- // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will
- // be closed by GC finalizers.
+ // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that
+ // will be closed by GC finalizers.
GC_gcollect();
f = fopen(path_str, "r");
}
@@ -726,8 +797,8 @@ OptionalList_t Path$lines(Path_t path) {
FILE *f = fopen(path_str, "r");
if (f == NULL) {
if (errno == EMFILE || errno == ENFILE) {
- // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that will
- // be closed by GC finalizers.
+ // If we hit file handle limits, run GC collection to try to clean up any lingering file handles that
+ // will be closed by GC finalizers.
GC_gcollect();
f = fopen(path_str, "r");
}
@@ -808,7 +879,7 @@ Text_t Path$as_text(const void *obj, bool color, const TypeInfo_t *type) {
&& (path->components.length == 0 || !Text$equal_values(*(Text_t *)(path->components.data), Text(".."))))
text = Text$concat(path->components.length > 0 ? Text("./") : Text("."), text);
- if (color) text = Texts(Text("\033[32;1m"), text, Text("\033[m"));
+ if (color) text = Text$concat(Text("\033[32;1m"), text, Text("\033[m"));
return text;
}