-
Notifications
You must be signed in to change notification settings - Fork 182
feat(core): add more IO extensions (exists?, mkdir, rmdir, cwd) #1518
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -97,7 +97,7 @@ | |
| (register fread- (Fn [a Int Int (Ptr FILE)] Int) "fread") | ||
|
|
||
| (doc fread | ||
| "Reads a given numebr of bytes from a file into C-String." | ||
| "Reads a given number of bytes from a file into C-String." | ||
| "Wraps `fread` from the C standard library." | ||
| "Consider using [`read-file`](#read-file) or" | ||
| "[`unsafe-read-file`](#unsafe-read-file) instead.") | ||
|
|
@@ -115,7 +115,7 @@ | |
|
|
||
| (doc fflush! | ||
| "Flushes buffered data via [`fflush`](#fflush)" | ||
| ", but discards the result, making this function appropraite for use as" | ||
| ", but discards the result, making this function appropriate for use as" | ||
| "a side effect in `do` forms.") | ||
| (defn fflush! [file] | ||
| (ignore (fflush file))) | ||
|
|
@@ -153,6 +153,24 @@ | |
| (defn unlink! [file-name] | ||
| (ignore (unlink file-name))) | ||
|
|
||
| (private rename-) | ||
| (hidden rename-) | ||
| (register rename- (Fn [(Ptr CChar) (Ptr CChar)] Int) "rename") | ||
|
|
||
| (doc rename | ||
| "Renames a file or directory from `old-name` to `new-name`." | ||
| "Returns an integer indicating success (0) or failure (-1)." | ||
| "Wraps `rename` from the C standard library.") | ||
| (defn rename [old-name new-name] | ||
| (rename- (String.cstr old-name) (String.cstr new-name))) | ||
|
|
||
| (doc rename! | ||
| "Renames a file via [`rename`](#rename), but discards the result," | ||
| "making this function appropriate for use as a side effect in" | ||
| "`do` forms.") | ||
| (defn rename! [old-name new-name] | ||
| (ignore (rename old-name new-name))) | ||
|
|
||
| (doc fseek | ||
| "Sets the position indicator of a [FILE](#file) based on a given" | ||
| "reference position and offset. The position indicator will be set to an" | ||
|
|
@@ -204,8 +222,85 @@ | |
| "given [FILE](#file)." | ||
| "Wraps `ferror` from the C standard library.") | ||
| (register ferror (Fn [(Ptr FILE)] Bool) "ferror") | ||
|
|
||
| (doc list-dir | ||
| "Returns an array containing the names of the entries in the directory" | ||
| "designated by `path`, skipping `.` and `..`.") | ||
| (register list-dir (Fn [&String] (Array String)) "IO_Raw_list_MINUS_dir") | ||
|
|
||
| (doc mkdir | ||
| "Creates a directory at the given path." | ||
| "Returns an integer indicating success (0) or failure (-1)." | ||
| "Wraps `mkdir` from the C standard library.") | ||
| (register mkdir (Fn [&String] Int) "IO_mkdir") | ||
|
|
||
| (doc rmdir | ||
| "Removes a directory at the given path." | ||
| "Returns an integer indicating success (0) or failure (-1)." | ||
| "Wraps `rmdir` from the C standard library.") | ||
| (register rmdir (Fn [&String] Int) "IO_rmdir") | ||
|
|
||
| (register get-cwd (Fn [] (Ptr CChar)) "IO_Raw_get_MINUS_cwd") | ||
| (register set-cwd (Fn [&String] Int) "IO_Raw_set_MINUS_cwd") | ||
| ) | ||
|
|
||
| (doc file-exists? "Checks if a file exists at the given path.") | ||
| (register file-exists? (Fn [&String] Bool)) | ||
|
|
||
| (doc dir-exists? "Checks if a directory exists at the given path.") | ||
| (register dir-exists? (Fn [&String] Bool)) | ||
|
|
||
| (doc get-cwd | ||
| "Returns the current working directory." | ||
| "" | ||
| "Returns a (Result String String) indicating success or failure.") | ||
| (defn get-cwd [] | ||
| (let [res (IO.Raw.get-cwd)] | ||
| (if (null? res) | ||
| (Result.Error (fmt "Failed to get current working directory: %s" &(System.error-text))) | ||
| (let [s (String.from-cstr res)] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The C side already returns a |
||
| (do | ||
| (Pointer.free res) | ||
| (Result.Success s)))))) | ||
|
|
||
| (doc set-cwd | ||
| "Sets the current working directory to `path`." | ||
| "" | ||
| "Returns a (Result Bool String) indicating success or failure.") | ||
| (defn set-cwd [path] | ||
| (if (Int.= (IO.Raw.set-cwd path) 0) | ||
| (Result.Success true) | ||
| (Result.Error (fmt "Failed to set current working directory to '%s': %s" path &(System.error-text))))) | ||
|
|
||
| (doc create-dir | ||
| "Creates a directory at the given path." | ||
| "" | ||
| "Returns a (Result Bool String) indicating success or failure.") | ||
| (defn create-dir [path] | ||
| (if (Int.= (IO.Raw.mkdir path) 0) | ||
| (Result.Success true) | ||
| (Result.Error (fmt "Failed to create directory '%s': %s" path &(System.error-text))))) | ||
|
|
||
| (doc remove-dir | ||
| "Removes a directory at the given path." | ||
| "" | ||
| "Returns a (Result Bool String) indicating success or failure.") | ||
| (defn remove-dir [path] | ||
| (if (Int.= (IO.Raw.rmdir path) 0) | ||
| (Result.Success true) | ||
| (Result.Error (fmt "Failed to remove directory '%s': %s" path &(System.error-text))))) | ||
|
|
||
| (doc list-dir | ||
| "Returns an array containing the names of the entries in the directory" | ||
| "designated by `path`, skipping `.` and `..`." | ||
| "" | ||
| "Returns a (Result (Array String) String) indicating success or failure.") | ||
| (defn list-dir [path] | ||
| (let [res (IO.Raw.list-dir path)] | ||
| (if (Int.= (Array.length &res) -1) | ||
| (Result.Error (fmt "Failed to list directory '%s': %s" path &(System.error-text))) | ||
| (Result.Success res)))) | ||
|
|
||
| (doc println | ||
| "Prints a String ref to [`stdout`](#stdout), appends a newline.") | ||
| (register println (Fn [(Ref String)] ())) | ||
|
|
@@ -238,7 +333,7 @@ | |
|
|
||
| (doc open-file | ||
| "Opens a [FILE](#file) with the given name using a designated mode" | ||
| "(e.g. [r]ead, [w]rite, [a]ppend), [rb] read binary...)." | ||
| "(e.g. [r]ead, [w]rite, [a]ppend), [rb] read binary...." | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We open a paren here, so we should also close it. |
||
| "See `fopen` from the C standard library for a description of valid mode parameters.") | ||
| (defn open-file [filename mode] | ||
| (let [ptr (IO.Raw.fopen filename mode)] | ||
|
|
@@ -329,6 +424,32 @@ | |
| (Result.Success true) | ||
| (Result.Error (fmt "only %d of %d bytes were written" bytes-written bytes2write))))))) | ||
|
|
||
| (doc move-file | ||
| "Moves or renames a file or directory from `old-name` to `new-name`." | ||
| "" | ||
| "Returns a (Result Bool String) indicating success or failure.") | ||
| (defn move-file [old-name new-name] | ||
| (if (Int.= (IO.Raw.rename old-name new-name) 0) | ||
| (Result.Success true) | ||
| (Result.Error (fmt "Failed to move '%s' to '%s': %s" old-name new-name &(System.error-text))))) | ||
|
|
||
| (doc remove-file | ||
| "Deletes a file from the filesystem." | ||
| "" | ||
| "Returns a (Result Bool String) indicating success or failure.") | ||
| (defn remove-file [filename] | ||
| (if (Int.= (IO.Raw.unlink filename) 0) | ||
| (Result.Success true) | ||
| (Result.Error (fmt "Failed to remove file '%s': %s" filename &(System.error-text))))) | ||
|
|
||
| (doc copy-file | ||
| "Copies a file from `src` to `dest`. Overwrites `dest` if it already exists." | ||
| "" | ||
| "Returns a (Result Bool String) indicating success or failure.") | ||
| (defn copy-file [src dest] | ||
| (Result.and-then (read-file src) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This reads the complete file into memory. We should probably do a chunked read-write. |
||
| &(fn [content] (write-file &content dest)))) | ||
|
|
||
| (private getenv-) | ||
| (hidden getenv-) | ||
| (doc getenv- "gets the value of an environment variable (thin wrapper for the C standard library)") | ||
|
|
@@ -357,7 +478,7 @@ | |
| `(IO.println %(build-str* forms))) | ||
|
|
||
| (doc print* | ||
| "Prints any number of values to [`stdout`](#stdout), using thier" | ||
| "Prints any number of values to [`stdout`](#stdout), using their" | ||
| "`str` implementations." | ||
| "" | ||
| "```" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,16 @@ | ||
| #include <sys/stat.h> | ||
| #ifdef _WIN32 | ||
| #include <direct.h> | ||
| #define getcwd _getcwd | ||
| #define chdir _chdir | ||
| #define stat _stat | ||
| #define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) | ||
| #define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) | ||
| #else | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like Windows is not happy with this change quite yet. |
||
| #include <dirent.h> | ||
| #include <unistd.h> | ||
| #endif | ||
|
|
||
| void IO_println(String* s) { | ||
| puts(*s); | ||
| } | ||
|
|
@@ -53,3 +66,99 @@ String IO_unsafe_MINUS_read_MINUS_file(const String* filename) { | |
|
|
||
| return buffer; | ||
| } | ||
|
|
||
| bool IO_file_MINUS_exists_QMARK_(const String* path) { | ||
| struct stat st; | ||
| if (stat(*path, &st) == 0) { | ||
| return S_ISREG(st.st_mode); | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| bool IO_dir_MINUS_exists_QMARK_(const String* path) { | ||
| struct stat st; | ||
| if (stat(*path, &st) == 0) { | ||
| return S_ISDIR(st.st_mode); | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| #ifdef _WIN32 | ||
| Array IO_Raw_list_MINUS_dir(const String* path) { | ||
| Array result = {0, 0, NULL}; | ||
| WIN32_FIND_DATA fd; | ||
| char search_path[MAX_PATH]; | ||
| snprintf(search_path, MAX_PATH, "%s\\*", *path); | ||
| HANDLE hFind = FindFirstFile(search_path, &fd); | ||
| if (hFind != INVALID_HANDLE_VALUE) { | ||
| do { | ||
| if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0) { | ||
| continue; | ||
| } | ||
| String name = CARP_MALLOC(strlen(fd.cFileName) + 1); | ||
| strcpy(name, fd.cFileName); | ||
| result.len++; | ||
| result.data = CARP_REALLOC(result.data, sizeof(String) * result.len); | ||
| ((String*)result.data)[result.len - 1] = name; | ||
| } while (FindNextFile(hFind, &fd)); | ||
| FindClose(hFind); | ||
| } else { | ||
| result.len = -1; | ||
| } | ||
| return result; | ||
| } | ||
| #else | ||
| Array IO_Raw_list_MINUS_dir(const String* path) { | ||
| DIR *d; | ||
| struct dirent *dir; | ||
| d = opendir(*path); | ||
| Array result = {0, 0, NULL}; | ||
| if (d) { | ||
| while ((dir = readdir(d)) != NULL) { | ||
| if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) { | ||
| continue; | ||
| } | ||
| String name = CARP_MALLOC(strlen(dir->d_name) + 1); | ||
| strcpy(name, dir->d_name); | ||
| result.len++; | ||
| result.data = CARP_REALLOC(result.data, sizeof(String) * result.len); | ||
| ((String*)result.data)[result.len - 1] = name; | ||
| } | ||
| closedir(d); | ||
| } else { | ||
| result.len = -1; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don’t think returning an array of length -1 is safe or sensible here. We should find a different error condition (also for all the functions on top of this one). |
||
| } | ||
| return result; | ||
| } | ||
| #endif | ||
|
|
||
| int IO_mkdir(const String* path) { | ||
| #ifdef _WIN32 | ||
| return _mkdir(*path); | ||
| #else | ||
| return mkdir(*path, 0777); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is using |
||
| #endif | ||
| } | ||
|
|
||
| int IO_rmdir(const String* path) { | ||
| #ifdef _WIN32 | ||
| return _rmdir(*path); | ||
| #else | ||
| return rmdir(*path); | ||
| #endif | ||
| } | ||
|
|
||
| String IO_Raw_get_MINUS_cwd() { | ||
| char* buffer = getcwd(NULL, 0); | ||
| if (buffer) { | ||
| String result = CARP_MALLOC(strlen(buffer) + 1); | ||
| strcpy(result, buffer); | ||
| free(buffer); | ||
| return result; | ||
| } | ||
| return NULL; | ||
| } | ||
|
|
||
| int IO_Raw_set_MINUS_cwd(const String* path) { | ||
| return chdir(*path); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those should probably also be in
IO.Raw.