Skip to content
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

Implement remove, remove_all and mkdir #426

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ fn main() {
.flag("-diag-suppress=177") // variable was declared but never referenced
.flag("-diag-suppress=550") // variable was set but never used
.flag("-diag-suppress=20039") // a __host__ function redeclared with __device__, hence treated as a __host__ __device__ function
.flag("-diag-suppress=68") // integer conversion resulted in a change of sign
.flag("-diag-suppress=2464") // conversion from a string literal to "char *" is deprecated
.compile("hvm-cu");

println!("cargo:rustc-cfg=feature=\"cuda\"");
Expand Down
4 changes: 2 additions & 2 deletions src/hvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,7 @@ static inline Port enter(Net* net, Port var) {
}

// Atomically Links `A ~ B`.
static inline void link(Net* net, TM* tm, Port A, Port B) {
static inline void link_ports(Net* net, TM* tm, Port A, Port B) {
// Attempts to directionally point `A ~> B`
while (true) {
// If `A` is NODE: swap `A` and `B`, and continue
Expand Down Expand Up @@ -842,7 +842,7 @@ static inline void link(Net* net, TM* tm, Port A, Port B) {

// Links `A ~ B` (as a pair).
static inline void link_pair(Net* net, TM* tm, Pair AB) {
link(net, tm, get_fst(AB), get_snd(AB));
link_ports(net, tm, get_fst(AB), get_snd(AB));
}

// Interactions
Expand Down
120 changes: 120 additions & 0 deletions src/run.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include <dlfcn.h>
#include <errno.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include "hvm.c"

// Readback: λ-Encoded Ctr
Expand Down Expand Up @@ -716,6 +719,120 @@ Port io_dl_close(Net* net, Book* book, Port argm) {
return inject_ok(net, new_port(ERA, 0));
}

// Deletes a single file or an empty directory at the specified path.
// Returns Ok(None) if successful, or Err(reason) if an error occurs.
// This function attempts to remove both files and empty directories without
// first checking the type of the path.
// Returns: Result<*, IOError<i24>>
Port io_delete_file(Net* net, Book* book, Port argm) {
Str path = readback_str(net, book, argm);

int result = remove(path.buf);
free(path.buf);

if (result == 0) {
return inject_ok(net, new_port(ERA, 0));
} else {
return inject_io_err_inner(net, new_port(NUM, new_i24(errno)));
}
}

int delete_directory_recursive(const char* path) {
DIR *d = opendir(path);
size_t path_len = strlen(path);
int r = -1;

if (d) {
struct dirent *p;
r = 0;

while (!r && (p = readdir(d))) {
int r2 = -1;
char *buf;
size_t len;

if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) {
continue;
}

len = path_len + strlen(p->d_name) + 2;
buf = malloc(len);

if (buf) {
struct stat statbuf;
snprintf(buf, len, "%s/%s", path, p->d_name);

if (!stat(buf, &statbuf)) {
if (S_ISDIR(statbuf.st_mode)) {
r2 = delete_directory_recursive(buf);
} else {
r2 = remove(buf);
}
}

free(buf);
}

r = r2;
}

closedir(d);
}

if (!r) {
r = rmdir(path);
}

return r;
}

// Deletes a directory at the specified path. If recursive is True,
// it will delete the directory and all its contents.
// Returns Ok(None) if successful, or Err(reason) if an error occurs.
// Note: For non-recursive deletion of an empty directory,
// this function behaves the same as delete_file(path).
// Returns: Result<*, IOError<i24>>
Port io_delete_directory(Net* net, Book* book, Port argm) {
Tup tup = readback_tup(net, book, argm, 2);
if (2 != tup.elem_len) {
return inject_io_err_type(net);
}

Str path = readback_str(net, book, tup.elem_buf[0]);
u32 rec = get_u24(get_val(tup.elem_buf[1]));

int res;
if (rec) {
res = delete_directory_recursive(path.buf);
} else {
res = rmdir(path.buf);
}
free(path.buf);

if (0 == res) {
return inject_ok(net, new_port(ERA, 0));
} else {
return inject_io_err_inner(net, new_port(NUM, new_i24(errno)));
}
}

// Creates a new directory with the given path.
// Returns Ok(None) if sucessfull, or Err(reason) if an error occurs.
// Returns: Result<*, IOError<i24>>
Port io_mkdir(Net* net, Book* book, Port argm) {
Str name = readback_str(net, book, argm);

const mode_t mode = 0777;
int status = mkdir(name.buf, mode);
free(name.buf);

if (status) {
return inject_io_err_inner(net, new_port(NUM, new_i24(errno)));
} else {
return inject_ok(net, new_port(ERA, 0));
}
}

// Book Loader
// -----------

Expand All @@ -731,6 +848,9 @@ void book_init(Book* book) {
book->ffns_buf[book->ffns_len++] = (FFn){"DL_OPEN", io_dl_open};
book->ffns_buf[book->ffns_len++] = (FFn){"DL_CALL", io_dl_call};
book->ffns_buf[book->ffns_len++] = (FFn){"DL_CLOSE", io_dl_open};
book->ffns_buf[book->ffns_len++] = (FFn){"DELETE_FILE", io_delete_file};
book->ffns_buf[book->ffns_len++] = (FFn){"DELETE_DIRECTORY", io_delete_directory};
book->ffns_buf[book->ffns_len++] = (FFn){"MKDIR", io_mkdir};
}

// Monadic IO Evaluator
Expand Down
122 changes: 122 additions & 0 deletions src/run.cu
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#include <dlfcn.h>
#include <errno.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include "hvm.cu"

// Readback: λ-Encoded Ctr
Expand Down Expand Up @@ -834,6 +838,121 @@ Port io_dl_close(GNet* gnet, Book* book, Port argm) {
return gnet_inject_ok(gnet, new_port(ERA, 0));
}

// Deletes a single file or an empty directory at the specified path.
// Returns Ok(None) if successful, or Err(reason) if an error occurs.
// This function attempts to remove both files and empty directories without
// first checking the type of the path.
// Returns: Result<*, IOError<i24>>
Port io_delete_file(GNet* gnet, Port argm) {
Str s = gnet_readback_str(gnet, argm);

int result = remove(s.buf);
free(s.buf);

if (result == 0) {
return gnet_inject_ok(gnet, new_port(ERA, 0));
} else {
return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(errno)));
}
}

int delete_directory_recursive(const char* path) {
DIR* d = opendir(path);
size_t path_len = strlen(path);
int r = -1;

if (d) {
struct dirent *p;
r = 0;

while (!r && (p = readdir(d))) {
int r2 = -1;
char* buf;
size_t len;

if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..")) {
continue;
}

len = path_len + strlen(p->d_name) + 2;
buf = (char*) malloc(len);

if (buf) {
struct stat statbuf;
snprintf(buf, len, "%s/%s", path, p->d_name);

if (!stat(buf, &statbuf)) {
if (S_ISDIR(statbuf.st_mode)) {
r2 = delete_directory_recursive(buf);
} else {
r2 = remove(buf);
}
}

free(buf);
}

r = r2;
}

closedir(d);
}

if (!r) {
r = rmdir(path);
}

return r;
}

// Deletes a directory at the specified path. If recursive is True,
// it will delete the directory and all its contents.
// Returns Ok(None) if successful, or Err(reason) if an error occurs.
// Note: For non-recursive deletion of an empty directory,
// this function behaves the same as delete_file(path).
// Returns: Result<*, IOError<i24>>
Port io_delete_directory(GNet* gnet, Port argm) {
Tup tup = gnet_readback_tup(gnet, argm, 2);
if (tup.elem_len != 2) {
fprintf(stderr, "io_delete_directory: expected tuple\n");

return gnet_inject_io_err_type(gnet);
}

Str path = gnet_readback_str(gnet, tup.elem_buf[0]);
u32 rec = get_u24(get_val(tup.elem_buf[1]));
int res;
if (rec) {
res = delete_directory_recursive(path.buf);
} else {
res = rmdir(path.buf);
}
free(path.buf);

if (res == 0) {
return gnet_inject_ok(gnet, new_port(ERA, 0));
} else {
return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(errno)));
}
}

// Creates a new directory with the given path.
// Returns Ok(None) if sucessfull, or Err(reason) if an error occurs.
// Returns: Result<*, IOError<i24>>
Port io_mkdir(GNet* gnet, Port argm) {
Str name = gnet_readback_str(gnet, argm);

const mode_t mode = 0777;
int result = mkdir(name.buf, mode);
free(name.buf);

if (result) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we keep the var names the same? In the C runtime you called this status.

return gnet_inject_io_err_inner(gnet, new_port(NUM, new_i24(errno)));
} else {
return gnet_inject_ok(gnet, new_port(ERA, 0));
}
}

void book_init(Book* book) {
book->ffns_buf[book->ffns_len++] = (FFn){"READ", io_read};
book->ffns_buf[book->ffns_len++] = (FFn){"OPEN", io_open};
Expand All @@ -846,6 +965,9 @@ void book_init(Book* book) {
book->ffns_buf[book->ffns_len++] = (FFn){"DL_OPEN", io_dl_open};
book->ffns_buf[book->ffns_len++] = (FFn){"DL_CALL", io_dl_call};
book->ffns_buf[book->ffns_len++] = (FFn){"DL_CLOSE", io_dl_open};
book->ffns_buf[book->ffns_len++] = (FFn){"DELETE_FILE", io_delete_file};
book->ffns_buf[book->ffns_len++] = (FFn){"DELETE_DIRECTORY", io_delete_directory};
Copy link
Member

Choose a reason for hiding this comment

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

Following the convention of using the C/unix name for these functions, it would be better to call them "RM" and "RMDIR".

Also, if we're using "rmdir" we definitely should not also use "delete_directory", either go with one style or the other.

HVM in general prefers short names

book->ffns_buf[book->ffns_len++] = (FFn){"MKDIR", io_mkdir};

cudaMemcpyToSymbol(BOOK, book, sizeof(Book));
}
Expand Down
21 changes: 21 additions & 0 deletions tests/programs/io/create_directory.bend
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#{
Creates the batata directory and then deletes it.
#}

test-io = 1

IO/FS/mkdir path =
(call "MKDIR" path)

IO/FS/delete_directory path recursive =
(call "DELETE_DIRECTORY" (path, recursive))

False = 0

main =
let path = "./batata"
with IO {
ask * = (IO/FS/mkdir path)
ask s = (IO/FS/delete_directory path False)
(wrap s)
}
16 changes: 16 additions & 0 deletions tests/programs/io/delete_dir_file.bend
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#{
Calls the delete_directory function with a file path as argument.
#}

test-io = 1

IO/FS/delete_directory path recursive =
(call "DELETE_DIRECTORY" (path, recursive))

False = 0

main =
with IO {
ask s = (IO/FS/delete_directory "./delete_dir_file.bend" False)
(wrap s)
}
32 changes: 32 additions & 0 deletions tests/programs/io/delete_dir_recursive.bend
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#{
Creates the following tree structure and then deletes A and its children.
A
|-- a.txt
|-- AA
| `-- aa.txt
`-- AB
`-- ab.txt
#}

test-io = 1

IO/FS/mkdir path =
(call "MKDIR" path)

IO/FS/delete_directory path recursive =
(call "DELETE_DIRECTORY" (path, recursive))

True = 1

main =
with IO {
ask * = (IO/FS/mkdir "A")
ask * = (IO/FS/mkdir "A/AA")
ask * = (IO/FS/mkdir "A/AB")
ask * = (IO/FS/write_file "A/a.txt" (String/encode_utf8 "a"))
ask * = (IO/FS/write_file "A/AA/aa.txt" (String/encode_utf8 "aa"))
ask * = (IO/FS/write_file "A/AB/ab.txt" (String/encode_utf8 "ab"))

ask s = (IO/FS/delete_directory "A" True)
(wrap s)
}
Loading
Loading