import "./core.zc" import "./mem.zc" import "./result.zc" import "./string.zc" import "./vec.zc" import "sys/fs.zc" def Z_SEEK_SET = 0; def Z_SEEK_END = 2; def Z_F_OK = 0; extern fn _z_fs_mkdir(path: const char*) -> c_int; extern fn _z_fs_get_metadata(path: const char*, size: u64*, is_dir: c_int*, is_file: c_int*) -> c_int; extern fn _z_fs_read_entry(dir: void*, out_name: char*, buf_size: c_int, is_dir: c_int*) -> c_int; extern fn _z_fs_opendir(name: const char*) -> void*; extern fn _z_fs_closedir(dir: void*) -> c_int; struct File { handle: void*; } struct Metadata { size: u64; is_dir: bool; is_file: bool; } struct DirEntry { name: String; is_dir: bool; } impl File { /// Opens a file with the given mode. fn open(path: char*, mode: char*) -> Result { let h = fopen(path, mode); if (h == NULL) { return Result::Err("Failed to open file"); } return Result::Ok(File { handle: h }); } /// Closes the file handle. fn close(self) { if (self.handle) { fclose((FILE*)self.handle); self.handle = NULL; } } /// Reads the whole file into a String. fn read_to_string(self) -> Result { if (self.handle == NULL) { return Result::Err("File not open"); } fseek((FILE*)self.handle, 0, Z_SEEK_END); let size = ftell((FILE*)self.handle); fseek((FILE*)self.handle, 0, Z_SEEK_SET); let buffer: char* = alloc_n((usize)size + 1); if (buffer == NULL) { return Result::Err("Out of memory"); } let read = fread(buffer, 1, size, (FILE*)self.handle); buffer[read] = 0; let s = String::new(buffer); dealloc(buffer); let res = Result::Ok(s); s.forget(); let ret = res; res.forget(); return ret; } /// Reads the whole file into a String. fn read_all(path: char*) -> Result { let res = File::open(path, "rb"); if (res.is_err()) { return Result::Err(res.err); } let f: File = res.unwrap(); let s_res = f.read_to_string(); f.close(); let ret = s_res; s_res.forget(); return ret; } /// Writes a C string to the file. fn write_string(self, content: char*) -> Result { if (self.handle == NULL) { return Result::Err("File not open"); } let len = strlen(content); let written = fwrite(content, 1, len, (FILE*)self.handle); if (written != len) { return Result::Err("Write incomplete"); } return Result::Ok(true); } /// Reads all lines into a Vec of Strings. fn read_lines(path: char*) -> Result< Vec > { let res = File::read_all(path); if (res.is_err()) { return Result< Vec >::Err(res.err); } let content = res.unwrap(); let lines = content.split('\n'); // Remove trailing \r from lines if on windows or CRLF for (let i: usize = 0; i < lines.len; i = i + 1) { let line = lines.get_ref(i); let len = line.length(); if (len > 0) { let last_char = line.vec.get(len - 1); if (last_char == '\r') { let trimmed = line.substring(0, len - 1); line.destroy(); lines.set(i, trimmed); } } } // Remove the final blank line if ending in a delimiter if (lines.len > 0) { let last_idx = lines.len - 1; let last_line = lines.get_ref(last_idx); if (last_line.length() == 0) { let empty = lines.pop(); // Safe because it's the element itself empty.destroy(); } } content.destroy(); let ret_res = Result< Vec >::Ok(lines); let ret = ret_res; return ret; } /// Writes multiple lines to the file. fn write_lines(path: char*, lines: Vec*) -> Result { let res = File::open(path, "wb"); if (res.is_err()) { return Result::Err(res.err); } let f = res.unwrap(); for (let i: usize = 0; i < lines.len; i = i + 1) { let line = lines.get_ref(i); f.write_string(line.c_str()); f.write_string("\n"); } f.close(); return Result::Ok(true); } /// Returns whether the path exists. fn exists(path: char*) -> bool { let zero: c_int = 0; return access(path, Z_F_OK) == zero; } /// Returns the file metadata, or `None`. fn metadata(path: char*) -> Result { let size: uint64_t; let is_d: c_int; let is_f: c_int; let res = _z_fs_get_metadata(path, &size, &is_d, &is_f); let non_zero: c_int = 0; if (res != non_zero) { return Result::Err("Failed to get metadata"); } return Result::Ok(Metadata { size: (u64)size, is_dir: is_d != non_zero, is_file: is_f != non_zero }); } /// Creates a directory. fn create_dir(path: char*) -> Result { let status = _z_fs_mkdir(path); let zero: c_int = 0; if (status != zero) { return Result::Err("Failed to create directory"); } return Result::Ok(true); } /// Removes a file. fn remove_file(path: char*) -> Result { let status = unlink(path); let zero: c_int = 0; if (status != zero) { return Result::Err("Failed to remove file"); } return Result::Ok(true); } /// Removes a directory. fn remove_dir(path: char*) -> Result { let status = rmdir(path); let zero: c_int = 0; if (status != zero) { return Result::Err("Failed to remove directory"); } return Result::Ok(true); } /// Lists a directory as Vec of DirEntry. fn read_dir(path: char*) -> Result< Vec > { let dir = _z_fs_opendir(path); if (dir == NULL) { return Result< Vec >::Err("Failed to open directory"); } let entries = Vec::new(); let name_buf: char* = alloc_n(256); if (name_buf == NULL) { _z_fs_closedir(dir); return Result< Vec >::Err("Out of memory"); } let is_d: c_int = 0; let is_d_zero: c_int = 0; while (_z_fs_read_entry(dir, name_buf, 256, &is_d)) { let zero_cmp: c_int = 0; if (strcmp(name_buf, ".") == zero_cmp || strcmp(name_buf, "..") == zero_cmp) { continue; } let s = String::new(name_buf); let ent = DirEntry { name: s, is_dir: is_d != is_d_zero }; // Transfer ownership: String -> DirEntry s.forget(); entries.push(ent); // Transfer ownership: DirEntry -> Vec ent.name.forget(); } dealloc(name_buf); _z_fs_closedir(dir); let res = Result< Vec >::Ok(entries); let ret = res; return ret; } /// Returns the current working directory. fn current_dir() -> Result { let buf: char* = alloc_n(1024); if (buf == NULL) return Result::Err("Out of memory"); if (getcwd(buf, 1024) == NULL) { dealloc(buf); return Result::Err("Failed to get current directory"); } let s = String::new(buf); dealloc(buf); let res = Result::Ok(s); s.forget(); let ret = res; res.forget(); return ret; } } impl Drop for File { fn drop(self) { self.close(); } }