summaryrefslogtreecommitdiff
path: root/src/devices/file.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/file.rs')
-rw-r--r--src/devices/file.rs293
1 files changed, 230 insertions, 63 deletions
diff --git a/src/devices/file.rs b/src/devices/file.rs
index f442dd8..163c10d 100644
--- a/src/devices/file.rs
+++ b/src/devices/file.rs
@@ -1,90 +1,257 @@
-use std::io::{ErrorKind, Read, Seek, SeekFrom};
-use std::fs::{File, OpenOptions};
+mod buffered_file;
+mod circular_path_buffer;
+mod directory_entry;
+mod directory_listing;
+mod entry;
+mod operations;
+
+pub use buffered_file::*;
+pub use circular_path_buffer::*;
+pub use directory_entry::*;
+pub use directory_listing::*;
+pub use entry::*;
+use operations::*;
+
+use std::fs::{OpenOptions, metadata};
+use std::os::unix::ffi::OsStrExt;
+use std::path::{Component, Path, PathBuf};
+
+fn is_blank_path(path: &Path) -> bool {
+ path == PathBuf::new()
+}
pub struct FileDevice {
- pub name: Vec<u8>,
- pub rename: Vec<u8>,
+ /// The path to which the file device is confined. Files and directories
+ /// outside of this directory cannot be accessed.
+ pub base_path: PathBuf,
- pub file: Option<File>,
- pub operation_state: bool,
- pub pointer: u32,
- pub file_size: u32,
+ pub open_buffer: CircularPathBuffer,
+ pub move_buffer: CircularPathBuffer,
+ pub name_buffer: CircularPathBuffer,
+
+ pub entry: Option<(Entry, PathBuf)>,
+
+ pub move_success: bool,
+ pub new_pointer: u32,
+ pub new_length: u32,
}
impl FileDevice {
pub fn new() -> Self {
Self {
- name: Vec::new(),
- rename: Vec::new(),
-
- file: None,
- operation_state: false,
- pointer: 0,
- file_size: 0,
- }
- }
-
- pub fn push_name_byte(&mut self, byte: u8) {
- if byte != 0 { self.name.push(byte); return }
- // If char was null, attempt to open the file and read file size.
- let path: std::ffi::OsString = std::os::unix::ffi::OsStringExt::from_vec(
- std::mem::take(&mut self.name));
- let try_open = OpenOptions::new().read(true).write(true).open(path);
- if let Ok(file) = try_open {
- if let Ok(metadata) = file.metadata() {
- // Success.
- self.file = Some(file);
- match u32::try_from(metadata.len()) {
- Ok(len) => self.file_size = len,
- Err(_) => self.file_size = u32::MAX,
- }
- return;
+ base_path: PathBuf::from("/home/ben/Sandbox"),
+
+ open_buffer: CircularPathBuffer::new(),
+ move_buffer: CircularPathBuffer::new(),
+ name_buffer: CircularPathBuffer::new(),
+
+ entry: None,
+
+ move_success: false,
+ new_pointer: 0,
+ new_length: 0,
+ }
+ }
+
+ pub fn close_entry(&mut self) {
+ self.open_buffer.clear();
+ self.move_buffer.clear();
+ self.name_buffer.clear();
+ self.entry = None;
+ self.new_pointer = 0;
+ self.new_length = 0;
+ }
+
+ pub fn write_to_open_port(&mut self, byte: u8) {
+ if let Some(relative_path) = self.open_buffer.push_byte(byte) {
+ println!("Attempting to open path {relative_path:?}");
+ self.close_entry();
+ if !is_blank_path(&relative_path) {
+ if let Ok(path) = self.attach_base(&relative_path) {
+ println!("Attempting to open absolute path {path:?}");
+ let _ = self.open_entry(&path);
+ };
}
}
- // Failure.
- self.close();
}
- pub fn set_file_size(&mut self) {
- if let Some(file) = &self.file {
- if let Ok(()) = file.set_len(self.file_size.into()) {
- return;
+ pub fn set_name_pointer(&mut self, byte: u8) {
+ self.name_buffer.set_pointer(byte);
+ }
+
+ /// Open the entry at the given path.
+ pub fn open_entry(&mut self, path: &Path) -> Result<(), ()> {
+ macro_rules! raise_on_err {
+ ($res:expr) => {match $res {Ok(v)=>v, Err(_)=>return Err(())} } }
+
+ if !path.starts_with(&self.base_path) { return Err(()); }
+ let metadata = raise_on_err!(metadata(&path));
+ if metadata.is_file() {
+ println!("Opening file as readable/writable");
+ if let Ok(file) = OpenOptions::new().read(true).write(true).open(&path) {
+ self.close_entry();
+ let file_entry = Entry::File(BufferedFile::new(file));
+ self.entry = Some((file_entry, path.to_owned()));
+ let relative = remove_base(&path, &self.base_path).unwrap();
+ self.name_buffer.populate(relative.as_os_str().as_bytes());
+ println!("Success, opened file {path:?}");
+ return Ok(());
};
- }
- self.close();
+ } else if metadata.is_dir() {
+ println!("Opening directory");
+ if let Ok(listing) = DirectoryListing::from_path(&path, &self.base_path) {
+ let dir_entry = Entry::Directory(listing);
+ self.entry = Some((dir_entry, path.to_owned()));
+ let relative = remove_base(&path, &self.base_path).unwrap();
+ self.name_buffer.populate(relative.as_os_str().as_bytes());
+ println!("Success, opened directory {path:?}");
+ return Ok(());
+ };
+ };
+ return Err(());
}
- pub fn read(&mut self) -> u8 {
- if let Some(file) = &mut self.file {
- let mut buffer: [u8; 1] = [0; 1];
- match file.read_exact(&mut buffer) {
- Ok(()) => return buffer[0],
- Err(err) => match err.kind() {
- ErrorKind::UnexpectedEof => return 0,
- _ => unimplemented!("File read error: ErrorKind::{:?}", err.kind()),
+ pub fn write_to_move_port(&mut self, byte: u8) {
+ if let Some(dest) = self.move_buffer.push_byte(byte) {
+ if let Some((_, source)) = &self.entry {
+ if is_blank_path(&dest) {
+ self.move_success = delete_entry(&source);
+ } else if let Ok(destination) = self.attach_base(&dest) {
+ println!("Attempting to move entry: {destination:?}");
+ self.move_success = move_entry(&source, &destination);
+ }
+ } else {
+ if is_blank_path(&dest) {
+ self.move_success = false;
+ } else if let Ok(destination) = self.attach_base(&dest) {
+ println!("Attempting to create entry: {destination:?}");
+ self.move_success = create_file(&destination);
}
}
+ self.close_entry();
+ }
+ }
+
+ /// Attempt to open the parent directory of the current entry.
+ pub fn ascend_to_parent(&mut self) {
+ if let Some((_, path)) = &self.entry {
+ if let Some(parent_path) = path.parent() {
+ let path = parent_path.to_owned();
+ let _ = self.open_entry(&path);
+ }
}
- self.close();
- return 0;
}
- pub fn seek(&mut self) {
- if let Some(file) = &mut self.file {
- let point = SeekFrom::Start(self.pointer.into());
- if let Ok(pointer) = file.seek(point) {
- if let Ok(pointer_u32) = pointer.try_into() {
- self.pointer = pointer_u32;
- return;
+ /// Attempt to open the currently-selected child.
+ pub fn descend_to_child(&mut self) {
+ if let Some((Entry::Directory(listing), _)) = &self.entry {
+ if let Some(child_path) = listing.child_path() {
+ if let Ok(child_path) = self.attach_base(&child_path) {
+ let child_path = child_path.to_owned();
+ let _ = self.open_entry(&child_path);
}
};
}
- self.close();
}
- pub fn close(&mut self) {
- self.file = None;
- self.pointer = 0;
- self.file_size = 0;
+ /// Return true if the currently-open entry is a directory.
+ pub fn entry_type(&self) -> bool {
+ match self.entry {
+ Some((Entry::Directory(_), _)) => true,
+ _ => false,
+ }
+ }
+
+ /// Return true if the currently-selected child is a directory.
+ pub fn read_child_name(&mut self) -> u8 {
+ if let Some((Entry::Directory(listing), _)) = &mut self.entry {
+ listing.child_name().read_byte()
+ } else {
+ 0
+ }
+ }
+
+ pub fn set_child_name_pointer(&mut self, byte: u8) {
+ if let Some((Entry::Directory(listing), _)) = &mut self.entry {
+ listing.child_name().set_pointer(byte);
+ }
+ }
+
+ /// Return true if the currently-selected child is a directory.
+ pub fn child_type(&self) -> bool {
+ if let Some((Entry::Directory(listing), _)) = &self.entry {
+ match listing.child_type() {
+ Some(EntryType::Directory) => true,
+ Some(EntryType::File) => false,
+ None => false,
+ }
+ } else {
+ false
+ }
+ }
+
+ pub fn read_byte(&mut self) -> u8 {
+ match &mut self.entry {
+ Some((Entry::File(buffered_file), _)) => buffered_file.read_byte(),
+ _ => 0,
+ }
+ }
+
+ pub fn write_byte(&mut self, byte: u8) {
+ match &mut self.entry {
+ Some((Entry::File(buffered_file), _)) => buffered_file.write_byte(byte),
+ _ => (),
+ }
+ }
+
+ pub fn pointer(&mut self) -> u32 {
+ match &mut self.entry {
+ Some((Entry::File(buffered_file), _)) => buffered_file.pointer(),
+ Some((Entry::Directory(listing), _)) => listing.selected(),
+ _ => 0,
+ }
+ }
+
+ pub fn commit_pointer(&mut self) {
+ let pointer = std::mem::take(&mut self.new_pointer);
+ match &mut self.entry {
+ Some((Entry::File(buffered_file), _)) => buffered_file.set_pointer(pointer),
+ Some((Entry::Directory(listing), _)) => listing.set_selected(pointer),
+ _ => (),
+ }
+ }
+
+ pub fn length(&mut self) -> u32 {
+ match &mut self.entry {
+ Some((Entry::File(buffered_file), _)) => buffered_file.length(),
+ Some((Entry::Directory(listing), _)) => listing.length(),
+ _ => 0,
+ }
+ }
+
+ pub fn commit_length(&mut self) {
+ let length = std::mem::take(&mut self.new_length);
+ match &mut self.entry {
+ Some((Entry::File(buffered_file), _)) => buffered_file.set_length(length),
+ _ => (),
+ }
+ }
+
+ fn attach_base(&self, relative_path: &Path) -> Result<PathBuf, ()> {
+ let mut full_path = self.base_path.clone();
+ let mut has_root = false;
+ for component in relative_path.components() {
+ match component {
+ Component::Normal(s) => full_path.push(s),
+ Component::ParentDir => return Err(()),
+ Component::CurDir => continue,
+ Component::RootDir => has_root = true,
+ Component::Prefix(_) => continue,
+ }
+ };
+ match has_root {
+ true => Ok(full_path),
+ false => Err(())
+ }
}
}