commit 14dd080dbaff9e651ef4a8d4b1541753637bb7cd Author: Able Date: Sat Dec 3 12:15:03 2022 -0600 initial userland commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..52323c3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,262 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "basic_driver" +version = "0.1.0" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clparse" +version = "0.1.0" +dependencies = [ + "hashbrown 0.13.1", + "log", + "toml 0.5.9 (git+https://git.ablecorp.us/theoddgarlic/toml-rs)", +] + +[[package]] +name = "derelict_microarchitecture" +version = "0.1.0" + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "ground" +version = "0.1.0" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038" +dependencies = [ + "ahash 0.8.2", +] + +[[package]] +name = "libc" +version = "0.2.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "no_video" +version = "0.1.0" + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ron" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" +dependencies = [ + "base64", + "bitflags", + "serde", +] + +[[package]] +name = "serde" +version = "1.0.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tar" +version = "0.1.0" +dependencies = [ + "versioning", +] + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +dependencies = [ + "serde", +] + +[[package]] +name = "toml" +version = "0.5.9" +source = "git+https://git.ablecorp.us/theoddgarlic/toml-rs#34db433429f3ad38921d13ac9aba74c8a706f376" +dependencies = [ + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "trash_manifest" +version = "0.1.0" +dependencies = [ + "ron", + "serde", + "thiserror", + "toml 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "versioning" +version = "0.1.2" +dependencies = [ + "serde", +] + +[[package]] +name = "vgable" +version = "0.1.0" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..fb4d00b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,22 @@ +[workspace] + +members = [ + + "drivers/basic_driver", + "drivers/graphics/derelict_microarchitecture", + "drivers/graphics/ground", + "drivers/graphics/novideo", + "drivers/graphics/vgable", + + "libraries/clparse", + "libraries/tar", + "libraries/trash_manifest", + "libraries/versioning", + + # "programs/delete", + # "programs/list", + # "programs/shell", + # "programs/table", + # "programs/table_view", + # "programs/undelete", +] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..fea8d32 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 AbleCorp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/drivers/basic_driver/.cargo/config.toml b/drivers/basic_driver/.cargo/config.toml new file mode 100644 index 0000000..f4e8c00 --- /dev/null +++ b/drivers/basic_driver/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/drivers/basic_driver/Cargo.toml b/drivers/basic_driver/Cargo.toml new file mode 100644 index 0000000..1cb438f --- /dev/null +++ b/drivers/basic_driver/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "basic_driver" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/drivers/basic_driver/src/main.rs b/drivers/basic_driver/src/main.rs new file mode 100644 index 0000000..00a4608 --- /dev/null +++ b/drivers/basic_driver/src/main.rs @@ -0,0 +1,8 @@ +#![no_std] +#![no_main] + +#[no_mangle] +fn start() -> i32 { + // Simple driver + 1 +} diff --git a/drivers/graphics/derelict_microarchitecture/.cargo/config.toml b/drivers/graphics/derelict_microarchitecture/.cargo/config.toml new file mode 100644 index 0000000..f4e8c00 --- /dev/null +++ b/drivers/graphics/derelict_microarchitecture/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/drivers/graphics/derelict_microarchitecture/Cargo.toml b/drivers/graphics/derelict_microarchitecture/Cargo.toml new file mode 100644 index 0000000..164e1e5 --- /dev/null +++ b/drivers/graphics/derelict_microarchitecture/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "derelict_microarchitecture" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/drivers/graphics/derelict_microarchitecture/src/main.rs b/drivers/graphics/derelict_microarchitecture/src/main.rs new file mode 100644 index 0000000..00a4608 --- /dev/null +++ b/drivers/graphics/derelict_microarchitecture/src/main.rs @@ -0,0 +1,8 @@ +#![no_std] +#![no_main] + +#[no_mangle] +fn start() -> i32 { + // Simple driver + 1 +} diff --git a/drivers/graphics/ground/.cargo/config.toml b/drivers/graphics/ground/.cargo/config.toml new file mode 100644 index 0000000..f4e8c00 --- /dev/null +++ b/drivers/graphics/ground/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/drivers/graphics/ground/Cargo.toml b/drivers/graphics/ground/Cargo.toml new file mode 100644 index 0000000..83408d3 --- /dev/null +++ b/drivers/graphics/ground/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "ground" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/drivers/graphics/ground/src/main.rs b/drivers/graphics/ground/src/main.rs new file mode 100644 index 0000000..00a4608 --- /dev/null +++ b/drivers/graphics/ground/src/main.rs @@ -0,0 +1,8 @@ +#![no_std] +#![no_main] + +#[no_mangle] +fn start() -> i32 { + // Simple driver + 1 +} diff --git a/drivers/graphics/novideo/.cargo/config.toml b/drivers/graphics/novideo/.cargo/config.toml new file mode 100644 index 0000000..f4e8c00 --- /dev/null +++ b/drivers/graphics/novideo/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/drivers/graphics/novideo/Cargo.toml b/drivers/graphics/novideo/Cargo.toml new file mode 100644 index 0000000..6800b92 --- /dev/null +++ b/drivers/graphics/novideo/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "no_video" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/drivers/graphics/novideo/src/main.rs b/drivers/graphics/novideo/src/main.rs new file mode 100644 index 0000000..00a4608 --- /dev/null +++ b/drivers/graphics/novideo/src/main.rs @@ -0,0 +1,8 @@ +#![no_std] +#![no_main] + +#[no_mangle] +fn start() -> i32 { + // Simple driver + 1 +} diff --git a/drivers/graphics/vgable/.cargo/config.toml b/drivers/graphics/vgable/.cargo/config.toml new file mode 100644 index 0000000..f4e8c00 --- /dev/null +++ b/drivers/graphics/vgable/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/drivers/graphics/vgable/Cargo.toml b/drivers/graphics/vgable/Cargo.toml new file mode 100644 index 0000000..7441bbe --- /dev/null +++ b/drivers/graphics/vgable/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "vgable" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/drivers/graphics/vgable/src/main.rs b/drivers/graphics/vgable/src/main.rs new file mode 100644 index 0000000..00a4608 --- /dev/null +++ b/drivers/graphics/vgable/src/main.rs @@ -0,0 +1,8 @@ +#![no_std] +#![no_main] + +#[no_mangle] +fn start() -> i32 { + // Simple driver + 1 +} diff --git a/drivers/readme.md b/drivers/readme.md new file mode 100644 index 0000000..e69de29 diff --git a/libraries/clparse/Cargo.toml b/libraries/clparse/Cargo.toml new file mode 100644 index 0000000..caed2a1 --- /dev/null +++ b/libraries/clparse/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "clparse" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["std"] +std = [] + + +[dependencies] +log = "*" +hashbrown = "*" + + +[dependencies.toml] +git = "https://git.ablecorp.us:443/theoddgarlic/toml-rs" +# version = "0.5.8" +default-features = false + + +# [dependencies.thiserror] +# version = "1.0" +# default-features = false diff --git a/libraries/clparse/SPEC.md b/libraries/clparse/SPEC.md new file mode 100644 index 0000000..751145f --- /dev/null +++ b/libraries/clparse/SPEC.md @@ -0,0 +1,17 @@ +# CLParse +## Arguments +An argument must be passed in the form of `abc=xyz` + +arguments may ***NOT*** have implicit argument anywhere but the final spot and it optionally may have ***only*** one(1) +This is different that you may be used to + +For example; the `cat` program of unix-like machines can be called like + +`cat abc.c xyz.c` + +and the equivelent on ableOS would be + +`append src=abc.c dest=xyz.c` + +## Config +clparse also loads configs into toml values and passes them along diff --git a/libraries/clparse/assets/example.toml b/libraries/clparse/assets/example.toml new file mode 100644 index 0000000..58816bf --- /dev/null +++ b/libraries/clparse/assets/example.toml @@ -0,0 +1,3 @@ +[list] +size = true +last_modified = true diff --git a/libraries/clparse/assets/full_system_example.toml b/libraries/clparse/assets/full_system_example.toml new file mode 100644 index 0000000..dc08313 --- /dev/null +++ b/libraries/clparse/assets/full_system_example.toml @@ -0,0 +1,16 @@ +[kernel] +boot = true + +[system] +[system.bootloader] + +[shared.list] +size = true +last_modified = true + +[user.able.list] +size = false +last_modified = true + +[user.able.shell.alias] +ls = "list" diff --git a/libraries/clparse/src/lib.rs b/libraries/clparse/src/lib.rs new file mode 100644 index 0000000..5bde939 --- /dev/null +++ b/libraries/clparse/src/lib.rs @@ -0,0 +1,210 @@ +//! # clparse +//! simple command line parser for ableOS + +#![no_std] + +extern crate alloc; + +#[cfg(feature = "std")] +extern crate std; + +use hashbrown; +use hashbrown::HashMap; + +use alloc::string::String; +pub use toml; + +pub type Argmap = HashMap; + +#[derive(Debug, Clone)] +pub struct Arguments { + pub arguments: Argmap, +} + +impl Arguments { + // TODO: Maybe check if a key is truthy, like if it's value equals "true", "1", "on", etc. + // NOTE: The above is out of scope for this pr but will be considered -able + pub fn parse(command: String) -> Result { + // TODO: Parse one implicit arg. + // NOTE: The above is out of scope for this pr but will be considered -able + let mut cmd = Arguments { + arguments: HashMap::new(), + }; + + if !command.is_empty() { + let command_split = command.split(" "); + for kv_pair in command_split { + if kv_pair.contains("=") { + // println!("{kv_pair}"); + let mut kv_split = kv_pair.split("="); + + let key = kv_split.next().unwrap().to_string(); + // println!("{key}"); + let value = kv_split.next().unwrap().to_string(); + // println!("{value}"); + + cmd.arguments.insert(key, value); + } else { + return Err(CLparseErrors::MissingEquals); + } + } + } + Ok(cmd) + } + pub fn parse_from_string>( + string: S, + ) -> Result<(Value, Arguments), CLparseErrors> { + let config: toml::Value = Value::String("".to_string()); + + let args = Arguments::parse(string.into())?; + + return Ok((config, args)); + } + + #[cfg(feature = "std")] + pub fn parse_from_args() -> Result<(Value, Arguments), CLparseErrors> { + use std::{ + collections::{BTreeMap, HashMap}, + env, + fs::File, + io::{Read, Write}, + println, + }; + + use alloc::format; + + let args = env::args(); + let mut args_str = String::new(); + + let dir = env::current_dir().unwrap(); + let mut config: toml::Value = Value::String("".to_string()); + match env::current_exe() { + Ok(bin) => { + if let Some(exe_name) = bin.as_path().file_name() { + let exe_name = exe_name.to_str().unwrap(); + + // println!("{exe_name}"); + + let path = format!( + "/home/{}/configs/{}/config.toml", + env::var("USER").unwrap(), + exe_name + ); + let path = std::path::Path::new(&path); + if path.exists() { + let display = path.display(); + + let mut file = match File::open(&path) { + Err(why) => panic!("couldn't open {}: {}", display, why), + Ok(file) => file, + }; + + let mut s = String::new(); + match file.read_to_string(&mut s) { + Err(why) => panic!("couldn't read {}: {}", display, why), + Ok(_) => { + // print!("{} contains:\n{}", display, s) + } + } + + config = toml::from_str(&s).unwrap(); + } else { + let mut file = File::create(path); + + println!("Make a file in your home dir called \"configs\""); + + file.unwrap().write_all(b"Hello, world!").unwrap(); + } + } + } + Err(_) => todo!(), + } + + for (count, arg) in args.enumerate() { + if count == 0 || count == 1 { + // Ignore stdio + program name on unix + } else { + args_str.push_str(&arg); + args_str.push(' '); + } + } + // needed to remove the very last ` ` which is unneeded + args_str.pop(); + // println!("{args_str}"); + + let ret = Arguments::parse(args_str)?; + return Ok((config, ret)); + } + + pub fn is_truthy(&self, key: &str) -> bool { + match self.arguments.get(key) { + Some(val) => { + let val = val.to_lowercase(); + if val == "true" || val == "1" || val == "on" || val == "t" { + return true; + } + false + } + None => false, + } + } +} + +#[derive(Debug)] +pub enum CLparseErrors { + MalformendArgument, + MissingEquals, +} + +impl Display for CLparseErrors { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?}", self)?; + Ok(()) + } +} + +use core::fmt::Display; + +use crate::alloc::string::ToString; + +#[cfg(test)] +mod tests { + + use super::*; + #[test] + #[cfg(feature = "std")] + fn test_arg_parse() { + use std::println; + let argline = "abc=xyz".to_string(); + + match Arguments::parse(argline) { + Ok(ok) => { + println!("{ok:?}") + } + Err(err) => panic!("{err:?}"), + } + } + + #[test] + fn fail_test_arg_parse() { + let argline = "abcxyz".to_string(); + + match Arguments::parse(argline) { + Ok(ok) => panic!("{ok:?}"), + Err(_) => {} + } + } +} + +use toml::Value; + +#[test] +#[cfg(feature = "std")] +pub fn load_config() { + use std::println; + + let toml_str = include_str!("../assets/example.toml"); + + let decoded: Value = toml::from_str(toml_str).unwrap(); + println!("{}", decoded); +} diff --git a/libraries/tar/Cargo.toml b/libraries/tar/Cargo.toml new file mode 100644 index 0000000..0cb766b --- /dev/null +++ b/libraries/tar/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "tar" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +versioning = { path = "../versioning" } diff --git a/libraries/tar/readme.md b/libraries/tar/readme.md new file mode 100644 index 0000000..ff6c995 --- /dev/null +++ b/libraries/tar/readme.md @@ -0,0 +1,3 @@ +# Tar +instead of using the sortform name for tar we will be using full names +`mtime` becomes `modified_time` \ No newline at end of file diff --git a/libraries/tar/src/lib.rs b/libraries/tar/src/lib.rs new file mode 100644 index 0000000..a09a55e --- /dev/null +++ b/libraries/tar/src/lib.rs @@ -0,0 +1,55 @@ +#![no_std] +pub const VERSION: versioning::Version = versioning::Version { + major: 0, + minor: 1, + patch: 0, +}; + +pub struct Header { + pub filename: [char; 100], + pub filemode: [char; 8], + pub user_id: [char; 8], + pub group_id: [char; 8], + pub size: [char; 12], + pub modified_time: [char; 12], + pub checksum: [char; 8], + pub typeflag: char, +} + +impl From<[char; 512]> for Header { + fn from(item: [char; 512]) -> Self { + let mut filename: [char; 100] = ['\0'; 100]; + filename[..100].clone_from_slice(&item[0..100]); + + let mut filemode: [char; 8] = ['\0'; 8]; + filemode[..8].clone_from_slice(&item[100..108]); + + let mut user_id: [char; 8] = ['\0'; 8]; + user_id[..8].clone_from_slice(&item[108..116]); + + let mut group_id: [char; 8] = ['\0'; 8]; + group_id[..8].clone_from_slice(&item[116..124]); + + let mut size: [char; 12] = ['\0'; 12]; + size[..12].clone_from_slice(&item[124..136]); + + let mut modified_time: [char; 12] = ['\0'; 12]; + modified_time[..12].clone_from_slice(&item[136..148]); + + let mut checksum: [char; 8] = ['\0'; 8]; + checksum[..8].clone_from_slice(&item[148..154]); + + let typeflag: char = item[155]; + + Self { + filename, + filemode, + user_id, + group_id, + size, + modified_time, + checksum, + typeflag, + } + } +} diff --git a/libraries/trash_manifest/Cargo.toml b/libraries/trash_manifest/Cargo.toml new file mode 100644 index 0000000..473bcb9 --- /dev/null +++ b/libraries/trash_manifest/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "trash_manifest" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +toml = "0.5" +serde = { version = "1.0", features = ["derive"] } +thiserror = "1.0" +ron = "0.7" \ No newline at end of file diff --git a/libraries/trash_manifest/src/lib.rs b/libraries/trash_manifest/src/lib.rs new file mode 100644 index 0000000..cad02f6 --- /dev/null +++ b/libraries/trash_manifest/src/lib.rs @@ -0,0 +1,76 @@ +use std::fs::File; +use std::io::{Read, Seek, Write}; +use std::path::PathBuf; + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct TrashFile { + pub name: String, + pub origin: PathBuf, +} + +pub struct Manifest { + files: Vec, + manifest: File, +} + +impl Manifest { + /// Creates a new Manifest from a .manifest.ron file. + /// Expects File to be read and write. + pub fn new(mut manifest: File) -> Result { + let mut contents = String::new(); + manifest.read_to_string(&mut contents)?; + manifest.rewind()?; + + if contents.len() < 2 { + contents = "[]".to_string(); + } + let files: Vec = ron::from_str(&contents)?; + Ok(Self { files, manifest }) + } + + /// Get a certain file from the manifest by it's name. + pub fn get(&self, name: String) -> Option { + self.files + .iter() + .find(|file| file.name == *name) + .map(|file| file.clone()) + } + + /// Get a clone of the underlying Vec of TrashFile. + pub fn get_all(&self) -> Vec { + self.files.clone() + } + + /// Remove a file from the manifest by it's name. + /// Consumes self and returns self back in a result to prevent inconsistencies + /// between self and the actual manifest file in case of Err. + pub fn remove(mut self, name: String) -> Result { + self.files.retain(|file| file.name != name); + let contents = ron::to_string(&self.files)?; + self.manifest.set_len(0)?; + self.manifest.rewind()?; + write!(&mut self.manifest, "{}", contents)?; + Ok(self) + } + + /// Add a file to the manifest by it's name and path to origin. + /// Consumes self and returns self back in a result to prevent inconsistencies + /// between self and the actual manifest file in case of Err. + pub fn add(mut self, name: String, origin: PathBuf) -> Result { + self.files.push(TrashFile { name, origin }); + dbg!(&self.files); + let contents = ron::to_string(&self.files)?; + self.manifest.set_len(0)?; + self.manifest.rewind()?; + write!(&mut self.manifest, "{}", contents)?; + Ok(self) + } +} + +#[derive(thiserror::Error, Debug)] +pub enum TrashManifestError { + #[error("Could not read manifest file")] + IoError(#[from] std::io::Error), + #[error("Could not serialize or deserialize manifest file")] + SerializationError(#[from] ron::Error), +} diff --git a/libraries/versioning/Cargo.toml b/libraries/versioning/Cargo.toml new file mode 100644 index 0000000..109caab --- /dev/null +++ b/libraries/versioning/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "versioning" +version = "0.1.2" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +[dependencies.serde] +version = "1.0" +default-features = false +features = ["derive"] diff --git a/libraries/versioning/src/lib.rs b/libraries/versioning/src/lib.rs new file mode 100644 index 0000000..cc97292 --- /dev/null +++ b/libraries/versioning/src/lib.rs @@ -0,0 +1,45 @@ +// ! A unified versioning system for Rust. +#![no_std] +use core::{fmt::Display, prelude::rust_2021::derive}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct Version { + pub major: u8, + pub minor: u8, + pub patch: u8, +} + +impl Version { + pub fn new(major: u8, minor: u8, patch: u8) -> Self { + Self { + major, + minor, + patch, + } + } + + pub fn new_from_env(vstring: &'static str) -> Self { + let mut spl = vstring.split("."); + if spl.clone().count() > 3 { + panic!("Improper version string"); + } + // TODO: handle failing any of these + let major: u8 = spl.nth(0).unwrap().parse().unwrap(); + let minor: u8 = spl.nth(1).unwrap().parse().unwrap(); + let patch: u8 = spl.nth(2).unwrap().parse().unwrap(); + + Self { + major, + minor, + patch, + } + } +} + +impl Display for Version { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "v{}.{}.{}", self.major, self.minor, self.patch)?; + Ok(()) + } +} diff --git a/programs/delete/Cargo.toml b/programs/delete/Cargo.toml new file mode 100644 index 0000000..a194246 --- /dev/null +++ b/programs/delete/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "delete" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clparse = { path = "../clparse", features = ["std"] } +trash_manifest = { path = "../trash_manifest" } +anyhow = "1.0" +fs_extra = "1.2" diff --git a/programs/delete/README.md b/programs/delete/README.md new file mode 100644 index 0000000..1d5977f --- /dev/null +++ b/programs/delete/README.md @@ -0,0 +1,15 @@ +# delete + +Moves a file to your .trash directory and updates the .manifest file therein. + +## Usage + +Remove file: + +* `delete path=foobar.txt` +* `delete p=foobar.txt` + +Remove directory: + +* `delete path=foobar/ dir=true` +* `delete path=foobar/ d=t` diff --git a/programs/delete/assets/config.toml b/programs/delete/assets/config.toml new file mode 100644 index 0000000..f663e75 --- /dev/null +++ b/programs/delete/assets/config.toml @@ -0,0 +1,9 @@ + + +# // - Always permanently delete files, +bypass_trash = true +# // - Set .trash path. +trashpath = "/home/user/.trash" + +# // TODO: Configuration options in programs config file: +# // - Handle naming collisions by changing file name instead of erroring, diff --git a/programs/delete/src/main.rs b/programs/delete/src/main.rs new file mode 100644 index 0000000..ce4395e --- /dev/null +++ b/programs/delete/src/main.rs @@ -0,0 +1,66 @@ +use fs_extra::{dir, file}; + +use std::env; +use std::fs::OpenOptions; +use std::path::PathBuf; + +// TODO: Configuration options in programs config file: +// - Always permanently delete files, +// - Handle naming collisions by changing file name instead of erroring, +// - Set .trash path. + +// TODO: Option to permanently delete file. +// TODO: .trash path option. +fn main() -> anyhow::Result<()> { + let ret = clparse::Arguments::parse_from_args()?; + let args = ret.1; + + let trash_path = env::var("HOME")? + "/.trash/"; + let from = PathBuf::from({ + let p = args.arguments.get("p"); + let path = args.arguments.get("path"); + match (p, path) { + (None, None) => anyhow::bail!("No path specified"), + (None, Some(path)) => path, + (Some(path), None) => path, + (Some(_), Some(_)) => anyhow::bail!("Option p and path conflict"), + } + }) + .canonicalize()?; + let name = from + .file_name() + .map(|name| name.to_str()) + .flatten() + .ok_or(anyhow::anyhow!("Weird file name"))?; + let manifest = trash_manifest::Manifest::new( + OpenOptions::new() + .create(true) + .write(true) + .read(true) + .open(trash_path.clone() + ".manifest.ron")?, + )?; + let is_in_trash = if from.to_string_lossy() == trash_path.clone() + name { + true + } else { + false + }; + + match ( + is_in_trash, + args.arguments.get("dir").is_some() || args.arguments.get("d").is_some(), + ) { + (true, true) => fs_extra::dir::remove(from.clone())?, + (true, false) => fs_extra::file::remove(from.clone())?, + (false, true) => { + fs_extra::dir::move_dir(from.clone(), trash_path + name, &dir::CopyOptions::new())?; + } + (false, false) => { + fs_extra::file::move_file(from.clone(), trash_path + name, &file::CopyOptions::new())?; + } + } + if !is_in_trash { + manifest.add(name.to_string(), from.clone())?; + } + + Ok(()) +} diff --git a/programs/list/Cargo.toml b/programs/list/Cargo.toml new file mode 100644 index 0000000..df88960 --- /dev/null +++ b/programs/list/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "list" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clparse = { path = "../clparse" } +number_prefix = "*" +table = { path = "../table" } diff --git a/programs/list/src/main.rs b/programs/list/src/main.rs new file mode 100644 index 0000000..26b29df --- /dev/null +++ b/programs/list/src/main.rs @@ -0,0 +1,95 @@ +use std::{collections::HashMap, env, fs}; + +use number_prefix::NumberPrefix; +use table::{Row, Table}; + +fn main() -> std::io::Result<()> { + let ret = clparse::Arguments::parse_from_args().unwrap(); + let args = ret.1; + + let mut table = Table { + table: HashMap::new(), + }; + + let current_dir = env::current_dir()?; + + let size_shown = args.is_truthy("s"); + + let last_modified_shown = args.is_truthy("lm"); + + let row = Row(vec![], 20); + table.table.insert("File Name".to_string(), row); + + if size_shown { + let row = Row(vec![], 20); + table.table.insert("Size".to_string(), row); + } + + if last_modified_shown { + let row = Row(vec![], 20); + table.table.insert("Last Modified".to_string(), row); + } + + for entry in fs::read_dir(current_dir)? { + let entry = entry?; + let path = entry.path(); + + let metadata = fs::metadata(&path)?; + + let abc = table.table.get_mut(&"File Name".to_string()); + let mut path = format!( + "{}", + path.file_name() + .ok_or("No filename") + .unwrap() + .to_str() + .unwrap(), + ); + if metadata.is_dir() { + path.push('/') + } + + match abc { + Some(row) => row.0.push(path), + None => todo!(), + } + + if size_shown { + let size = metadata.len(); + + let abc = table.table.get_mut(&"Size".to_string()); + match abc { + Some(row) => row.0.push(maybe_bytes_suffix(size)), + None => todo!(), + } + } + if last_modified_shown { + let last_modified = metadata.modified()?.elapsed().unwrap().as_secs(); + + let abc = table.table.get_mut(&"Last Modified".to_string()); + match abc { + Some(row) => row.0.push(format!("{} seconds ago", last_modified)), + None => todo!(), + } + + // print!(" {} seconds ago |", last_modified); + } + + print!("\n"); + } + + println!("{}", table); + + Ok(()) +} + +fn maybe_bytes_suffix(amount: u64) -> String { + return match NumberPrefix::binary(amount as f64) { + NumberPrefix::Standalone(bytes) => { + format!("{}B", bytes) + } + NumberPrefix::Prefixed(prefix, n) => { + format!("{:.1} {}B", n, prefix.symbol()) + } + }; +} diff --git a/programs/shell/Cargo.toml b/programs/shell/Cargo.toml new file mode 100644 index 0000000..4227dfe --- /dev/null +++ b/programs/shell/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "shell" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clparse = { path = "../clparse" } diff --git a/programs/shell/assets/config.toml b/programs/shell/assets/config.toml new file mode 100644 index 0000000..1ceff6d --- /dev/null +++ b/programs/shell/assets/config.toml @@ -0,0 +1,2 @@ +[alias] +ls = "list" diff --git a/programs/shell/src/main.rs b/programs/shell/src/main.rs new file mode 100644 index 0000000..0b8f994 --- /dev/null +++ b/programs/shell/src/main.rs @@ -0,0 +1,57 @@ +use std::{ + env, + io::{stdin, stdout, Read, Write}, + path::Path, + process::{Child, Command, Stdio}, +}; + +const VERSION: &str = env!("CARGO_PKG_VERSION"); +fn main() { + let ret = clparse::Arguments::parse_from_args().unwrap(); + let config = ret.0; + + // println!("{}", config); + + loop { + // use the `>` character as the prompt + // need to explicitly flush this to ensure it prints before read_line + print!("{} ~> ", std::env::current_dir().unwrap().display()); + stdout().flush().unwrap(); + let mut input = String::new(); + stdin().read_line(&mut input).unwrap(); + print!("{}", input); + + let mut in_qoute = false; + let mut building_command = String::new(); + let mut command_chain: Vec = vec![]; + + for x in input.chars() { + if x == '"' { + in_qoute = !in_qoute; + } + + if x == '+' || x == '\n' { + if !in_qoute { + command_chain.push(building_command.trim().to_string()); + + building_command = String::new(); + } + } else { + building_command.push(x); + } + } + + for mut command in command_chain { + let abc = config.get("alias"); + for x in abc.unwrap().as_table().unwrap() { + if command == *x.0 { + command = (*x.1).to_string(); + command.remove(0); + command.pop(); + } + } + + println!("Command: {}", command); + } + } +} diff --git a/programs/table/Cargo.toml b/programs/table/Cargo.toml new file mode 100644 index 0000000..12933c6 --- /dev/null +++ b/programs/table/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "table" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/programs/table/README.md b/programs/table/README.md new file mode 100644 index 0000000..929cbf2 --- /dev/null +++ b/programs/table/README.md @@ -0,0 +1,19 @@ +# Table + +a table is a data structure similar to a 2d array with rows and coloumns +for example a list of files could be passed as a table to copy + +using the following command you can generate a table + +`list s=t e=t` which is shorthand for `list size=true executable=true` + +> To generate a similar table use [this website](https://www.tablesgenerator.com/text_tables) + +|file name|file size|executable| +|-------- |---------|----------| +|able.txt |54 |false | +|ex.wasm |34 |true | + +using the following command you can merge all files into one + +`list s=t e=t + append dest=all.txt` diff --git a/programs/table/src/lib.rs b/programs/table/src/lib.rs new file mode 100644 index 0000000..fefac5c --- /dev/null +++ b/programs/table/src/lib.rs @@ -0,0 +1,80 @@ +use core::fmt::Display; +use std::collections::HashMap; + +pub type Header = String; +#[derive(Debug)] +pub struct Row(pub Vec, pub usize); + +impl Display for Row { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // let header = &self.0; + let column = &self.0; + let longest_length = &self.1; + + // write!(f, "{}", header)?; + // for _ in 0..longest_length - header.len() { + // write!(f, " ")?; + // } + // write!(f, "|\n")?; + // for _ in 0..*longest_length { + // write!(f, "-")?; + // } + + // write!(f, "|\n")?; + for xys in column { + write!(f, "{}", xys)?; + for _ in 0..longest_length - xys.len() { + write!(f, " ")?; + } + write!(f, "|\n")?; + } + + Ok(()) + } +} + +pub struct Table { + pub table: HashMap, +} + +impl Display for Table { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (header, row) in &self.table { + write!(f, "{}", header)?; + for _ in 0..row.1 - header.len() { + write!(f, " ")?; + } + write!(f, "|\n")?; + for _ in 0..row.1 { + write!(f, "-")?; + } + + write!(f, "|\n")?; + + write!(f, "{}", row)?; + } + + Ok(()) + } +} + +#[test] +fn test_print() { + let tbl = Table { + table: HashMap::new(), + }; + /* tbl.table.append(&mut vec![Row( + "header".to_string(), + vec![ + "hi".to_string(), + "hey whats up there".to_string(), + "hey".to_string(), + "whats".to_string(), + "up".to_string(), + "there".to_string(), + ], + 18, + )]); + */ + println!("{}", tbl); +} diff --git a/programs/table_view/Cargo.toml b/programs/table_view/Cargo.toml new file mode 100644 index 0000000..f4e0a91 --- /dev/null +++ b/programs/table_view/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "table_view" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +table = { path = "../table" } diff --git a/programs/table_view/src/main.rs b/programs/table_view/src/main.rs new file mode 100644 index 0000000..f8a5b6b --- /dev/null +++ b/programs/table_view/src/main.rs @@ -0,0 +1,19 @@ +use std::collections::HashMap; + +use table::{Row, Table}; + +fn main() { + let mut tbl = Table { + table: HashMap::new(), + }; + tbl.table.insert( + "Example".to_string(), + Row( + // "header".to_string(), + vec!["hi".to_string(), "hey whats up there".to_string()], + 19, + ), + ); + + println!("{}", tbl); +} diff --git a/programs/undelete/Cargo.toml b/programs/undelete/Cargo.toml new file mode 100644 index 0000000..49b3898 --- /dev/null +++ b/programs/undelete/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "undelete" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clparse = { path = "../clparse" } +trash_manifest = { path = "../trash_manifest" } +anyhow = "1.0" +fs_extra = "1.2" \ No newline at end of file diff --git a/programs/undelete/README.md b/programs/undelete/README.md new file mode 100644 index 0000000..6068792 --- /dev/null +++ b/programs/undelete/README.md @@ -0,0 +1,20 @@ +# undelete + +Moves a file from your .trash directory to current directory then updates the .manifest file. + +## Usage + +List deleted files: + +* `undelete list=true` +* `undelete l=t` + +Undelete file or directory: + +* `undelete name=foobar.txt` +* `undelete n=foobar.txt` + +Move to original location instead: + +* `undelete name=foobar.txt origin=true` +* `undelete name=foobar.txt o=t` diff --git a/programs/undelete/src/main.rs b/programs/undelete/src/main.rs new file mode 100644 index 0000000..3f58988 --- /dev/null +++ b/programs/undelete/src/main.rs @@ -0,0 +1,55 @@ +use std::{env, fs::OpenOptions, path::PathBuf}; + +use fs_extra::{dir, file}; + +fn main() -> anyhow::Result<()> { + let ret = clparse::Arguments::parse_from_args()?; + let args = ret.1; + + let trash_path = env::var("HOME")? + "/.trash/"; + let manifest = trash_manifest::Manifest::new( + OpenOptions::new() + .create(true) + .write(true) + .read(true) + .open(trash_path.clone() + ".manifest.ron")?, + )?; + + if !args.is_truthy("list") || !args.is_truthy("l") { + let name = { + let n = args.arguments.get("n"); + let name = args.arguments.get("name"); + match (n, name) { + (None, None) => anyhow::bail!("No name or operation specified"), + (None, Some(name)) => name, + (Some(name), None) => name, + (Some(_), Some(_)) => anyhow::bail!("Option n and name conflict"), + } + }; + let file = manifest + .get(name.to_string()) + .ok_or(anyhow::anyhow!("File not found in trash manifest"))?; + let from = PathBuf::from(trash_path.clone() + &file.name); + let to = if args.is_truthy("origin") || args.is_truthy("o") { + file.origin + } else { + env::current_dir()? + }; + + if from.is_dir() { + fs_extra::dir::move_dir(from, &to, &dir::CopyOptions::new())?; + } else { + fs_extra::file::move_file(from, &to, &file::CopyOptions::new())?; + } + + manifest.remove(file.name.clone())?; + } else { + let mut output = String::new(); + for file in manifest.get_all() { + output.push_str(&(file.name + "\n")); + } + println!("{}", output); + } + + Ok(()) +}