Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 125 additions & 4 deletions core/IO.carp
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand All @@ -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)))
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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))

Comment on lines +247 to +252
Copy link
Copy Markdown
Member

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.

(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)]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The C side already returns a String. We should fix the function signature on the C side to avoid a double allocation.

(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)] ()))
Expand Down Expand Up @@ -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...."
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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)]
Expand Down Expand Up @@ -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)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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)")
Expand Down Expand Up @@ -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."
""
"```"
Expand Down
109 changes: 109 additions & 0 deletions core/carp_io.h
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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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);
}
Expand Down Expand Up @@ -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;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is using 0777 really desirable here?

#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);
}
Loading
Loading