mirror of https://github.com/vinc/moros.git
Refactor everything to stay below 80 chars per line (#567)
* Refactor everything to stay below 80 chars per line * Refactor hash command * Refactor code changed by cargo fmt * Refactor code * Realign syscall numbers * Update readme
This commit is contained in:
parent
a9eb6f9c21
commit
aada07a3ef
50
README.md
50
README.md
|
@ -2,7 +2,7 @@
|
|||
|
||||
![screenshot](doc/images/moros.png)
|
||||
|
||||
MOROS is a hobby operating system written in Rust by [Vincent Ollivier](https://vinc.cc).
|
||||
MOROS is a hobby operating system written in Rust by [Vincent Ollivier][0].
|
||||
|
||||
It targets computers with a x86-64 architecture and a BIOS, so mostly from 2005
|
||||
to 2020, but it also runs well on most emulators (Bochs, QEMU, and VirtualBox).
|
||||
|
@ -11,28 +11,27 @@ This project started from the [seventh post][1] of the second edition of
|
|||
[Writing an OS in Rust][2] by Philipp Oppermann and by reading the
|
||||
[OSDev wiki][3] along with many open source kernels.
|
||||
|
||||
[![GitHub Actions](https://img.shields.io/github/actions/workflow/status/vinc/moros/rust.yml)](https://github.com/vinc/moros)
|
||||
[![Crates.io](https://img.shields.io/crates/v/moros.svg)](https://crates.io/crates/moros)
|
||||
|
||||
[![GitHub Actions][s1]](https://github.com/vinc/moros)
|
||||
[![Crates.io][s2]](https://crates.io/crates/moros)
|
||||
|
||||
## Features
|
||||
|
||||
- External bootloader (using [bootloader](https://github.com/rust-osdev/bootloader))
|
||||
- x86 CPU support (using [x86_64](https://crates.io/crates/x86_64))
|
||||
- Hardware interrupts (using [pic8259](https://crates.io/crates/pic8259))
|
||||
- PS/2 Keyboard with customizable layout (using [pc-keyboard](https://crates.io/crates/pc-keyboard))
|
||||
- External bootloader (using [bootloader][4])
|
||||
- x86 CPU support (using [x86_64][5])
|
||||
- Hardware interrupts (using [pic8259][6])
|
||||
- PS/2 Keyboard with customizable layout (using [pc-keyboard][7])
|
||||
- VGA Text mode with customizable font and color palette
|
||||
- Serial output (using [uart_16550](https://crates.io/crates/uart_16550))
|
||||
- Serial output (using [uart_16550][8])
|
||||
- Paging
|
||||
- Heap allocation (using [linked_list_allocator](https://crates.io/crates/linked_list_allocator))
|
||||
- ACPI shutdown (using [acpi](https://crates.io/crates/acpi) and [aml](https://crates.io/crates/aml))
|
||||
- Heap allocation (using [linked_list_allocator][9])
|
||||
- ACPI shutdown (using [acpi][10]) and [aml][11])
|
||||
- RTC clock
|
||||
- PCI devices
|
||||
- ATA PIO mode
|
||||
- Random number generator (using [rand_hc](https://crates.io/crates/rand_hc))
|
||||
- Random number generator (using [rand_hc][12])
|
||||
- RTL8139 network card
|
||||
- AMD PCNET network card
|
||||
- DHCP/IP/TCP/UDP/DNS/HTTP network protocols (using [smoltcp](https://crates.io/crates/smoltcp))
|
||||
- DHCP/IP/TCP/UDP/DNS/HTTP network protocols (using [smoltcp][13])
|
||||
- Basic [filesystem](doc/filesystem.md)
|
||||
- Basic [shell](doc/shell.md)
|
||||
- Basic [text editor](doc/editor.md)
|
||||
|
@ -41,16 +40,14 @@ This project started from the [seventh post][1] of the second edition of
|
|||
- Some file and [network](doc/network.md) commands
|
||||
- Some [games](doc/games.md)
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is available [here](doc/index.md)
|
||||
|
||||
|
||||
## Setup
|
||||
|
||||
You will need `git`, `gcc`, `make`, `curl`, `qemu-img`, and
|
||||
`qemu-system-x86_64` on the host system.
|
||||
You will need `git`, `gcc`, `make`, `curl`, `qemu-img`,
|
||||
and `qemu-system-x86_64` on the host system.
|
||||
|
||||
Clone the repo:
|
||||
|
||||
|
@ -63,7 +60,6 @@ Install the required tools with `make setup` or the following commands:
|
|||
$ rustup show
|
||||
$ cargo install bootimage
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Build the image to `disk.img`:
|
||||
|
@ -78,7 +74,7 @@ Run natively on a x86 computer by copying the bootloader and the kernel to a
|
|||
hard drive or USB stick (but there is currently no USB driver so the filesystem
|
||||
will not be available in that case):
|
||||
|
||||
$ sudo dd if=target/x86_64-moros/release/bootimage-moros.bin of=/dev/sdx && sync
|
||||
$ sudo dd if=target/x86_64-moros/release/bootimage-moros.bin of=/dev/sdx
|
||||
|
||||
MOROS will open a console in diskless mode after boot if no filesystem is
|
||||
detected. The following command will setup the filesystem on a hard drive,
|
||||
|
@ -90,18 +86,30 @@ allowing you to exit the diskless mode and log in as a normal user:
|
|||
your OS, and `install` or `disk format` inside MOROS if you don't use an
|
||||
emulator.**
|
||||
|
||||
|
||||
## Tests
|
||||
|
||||
Run the test suite in QEMU:
|
||||
|
||||
$ make test
|
||||
|
||||
|
||||
## License
|
||||
|
||||
MOROS is released under MIT.
|
||||
|
||||
[0]: https://vinc.cc
|
||||
[1]: https://github.com/phil-opp/blog_os/tree/post-07
|
||||
[2]: https://os.phil-opp.com
|
||||
[3]: https://wiki.osdev.org
|
||||
[4]: https://github.com/rust-osdev/bootloader
|
||||
[5]: https://crates.io/crates/x86_64
|
||||
[6]: https://crates.io/crates/pic8259
|
||||
[7]: https://crates.io/crates/pc-keyboard
|
||||
[8]: https://crates.io/crates/uart_16550
|
||||
[9]: https://crates.io/crates/linked_list_allocator
|
||||
[10]: https://crates.io/crates/acpi
|
||||
[11]: https://crates.io/crates/aml
|
||||
[12]: https://crates.io/crates/rand_hc
|
||||
[13]: https://crates.io/crates/smoltcp
|
||||
|
||||
[s1]: https://img.shields.io/github/actions/workflow/status/vinc/moros/rust.yml
|
||||
[s2]: https://img.shields.io/crates/v/moros.svg
|
||||
|
|
|
@ -4,7 +4,7 @@ use core::alloc::{GlobalAlloc, Layout};
|
|||
|
||||
pub struct UserspaceAllocator;
|
||||
|
||||
unsafe impl GlobalAlloc for UserspaceAllocator{
|
||||
unsafe impl GlobalAlloc for UserspaceAllocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
syscall::alloc(layout.size(), layout.align())
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::sys;
|
||||
use core::fmt;
|
||||
|
||||
pub use crate::sys::console::{ETX_KEY, EOT_KEY};
|
||||
pub use crate::sys::console::{EOT_KEY, ETX_KEY};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Style {
|
||||
|
@ -11,23 +11,38 @@ pub struct Style {
|
|||
|
||||
impl Style {
|
||||
pub fn reset() -> Self {
|
||||
Self { foreground: None, background: None }
|
||||
Self {
|
||||
foreground: None,
|
||||
background: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn foreground(name: &str) -> Self {
|
||||
Self { foreground: color_to_fg(name), background: None }
|
||||
Self {
|
||||
foreground: color_to_fg(name),
|
||||
background: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_foreground(self, name: &str) -> Self {
|
||||
Self { foreground: color_to_fg(name), background: self.background }
|
||||
Self {
|
||||
foreground: color_to_fg(name),
|
||||
background: self.background,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn background(name: &str) -> Self {
|
||||
Self { foreground: None, background: color_to_bg(name) }
|
||||
Self {
|
||||
foreground: None,
|
||||
background: color_to_bg(name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_background(self, name: &str) -> Self {
|
||||
Self { foreground: self.foreground, background: color_to_bg(name) }
|
||||
Self {
|
||||
foreground: self.foreground,
|
||||
background: color_to_bg(name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color(name: &str) -> Self {
|
||||
|
|
|
@ -37,7 +37,9 @@ pub fn from_bytes(buf: &[u8]) -> Result<Font, ()> {
|
|||
fn parse_psf_font() {
|
||||
assert!(from_bytes(include_bytes!("../../dsk/ini/boot.sh")).is_err());
|
||||
|
||||
let font = from_bytes(include_bytes!("../../dsk/ini/fonts/zap-light-8x16.psf")).unwrap();
|
||||
let font = from_bytes(
|
||||
include_bytes!("../../dsk/ini/fonts/zap-light-8x16.psf")
|
||||
).unwrap();
|
||||
assert_eq!(font.height, 16);
|
||||
assert_eq!(font.size, 256);
|
||||
assert_eq!(font.data.len(), 256 * 16);
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
use crate::api::syscall;
|
||||
use crate::sys;
|
||||
use crate::sys::fs::OpenFlag;
|
||||
use crate::api::syscall;
|
||||
|
||||
use alloc::format;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub use crate::sys::fs::{FileInfo, DeviceType};
|
||||
pub use crate::sys::fs::{DeviceType, FileInfo};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum IO { Read, Write }
|
||||
pub enum IO {
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
|
||||
pub trait FileIO {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()>;
|
||||
|
@ -106,7 +109,11 @@ pub fn create_device(path: &str, kind: DeviceType) -> Option<usize> {
|
|||
|
||||
pub fn read(path: &str, buf: &mut [u8]) -> Result<usize, ()> {
|
||||
if let Some(info) = syscall::info(path) {
|
||||
let res = if info.is_device() { open_device(path) } else { open_file(path) };
|
||||
let res = if info.is_device() {
|
||||
open_device(path)
|
||||
} else {
|
||||
open_file(path)
|
||||
};
|
||||
if let Some(handle) = res {
|
||||
if let Some(bytes) = syscall::read(handle, buf) {
|
||||
syscall::close(handle);
|
||||
|
@ -173,11 +180,11 @@ pub fn append(path: &str, buf: &[u8]) -> Result<usize, ()> {
|
|||
Err(())
|
||||
}
|
||||
|
||||
pub fn reopen(path: &str, handle: usize, append_mode: bool) -> Result<usize, ()> {
|
||||
pub fn reopen(path: &str, handle: usize, append: bool) -> Result<usize, ()> {
|
||||
let res = if let Some(info) = syscall::info(path) {
|
||||
if info.is_device() {
|
||||
open_device(path)
|
||||
} else if append_mode {
|
||||
} else if append {
|
||||
append_file(path)
|
||||
} else {
|
||||
open_file(path)
|
||||
|
@ -218,7 +225,7 @@ pub fn read_dir(path: &str) -> Result<Vec<FileInfo>, ()> {
|
|||
|
||||
#[test_case]
|
||||
fn test_file() {
|
||||
use crate::sys::fs::{mount_mem, format_mem, dismount};
|
||||
use crate::sys::fs::{dismount, format_mem, mount_mem};
|
||||
mount_mem();
|
||||
format_mem();
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::api::syscall;
|
||||
|
||||
use alloc::vec;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec;
|
||||
|
||||
pub struct Stdin;
|
||||
pub struct Stdout;
|
||||
|
@ -17,7 +17,8 @@ impl Stdin {
|
|||
if let Some(bytes) = syscall::read(0, &mut buf) {
|
||||
if bytes > 0 {
|
||||
buf.resize(bytes, 0);
|
||||
return Some(String::from_utf8_lossy(&buf).to_string().remove(0));
|
||||
let s = String::from_utf8_lossy(&buf).to_string().remove(0);
|
||||
return Some(s);
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
|
@ -5,16 +5,18 @@ macro_rules! entry_point {
|
|||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
$crate::api::syscall::write(1, b"An exception occured!\n");
|
||||
$crate::api::syscall::exit($crate::api::process::ExitCode::ExecError);
|
||||
let code = $crate::api::process::ExitCode::ExecError;
|
||||
$crate::api::syscall::exit(code);
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[export_name = "_start"]
|
||||
pub unsafe extern "sysv64" fn __impl_start(args_ptr: u64, args_len: usize) {
|
||||
let args = core::slice::from_raw_parts(args_ptr as *const _, args_len);
|
||||
pub unsafe extern "sysv64" fn __impl_start(ptr: u64, len: usize) {
|
||||
let args = core::slice::from_raw_parts(ptr as *const _, len);
|
||||
let f: fn(&[&str]) = $path;
|
||||
f(args);
|
||||
$crate::api::syscall::exit($crate::api::process::ExitCode::Success);
|
||||
let code = $crate::api::process::ExitCode::Success;
|
||||
$crate::api::syscall::exit(code);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -62,7 +64,9 @@ macro_rules! error {
|
|||
($($arg:tt)*) => ({
|
||||
let csi_color = $crate::api::console::Style::color("LightRed");
|
||||
let csi_reset = $crate::api::console::Style::reset();
|
||||
eprintln!("{}Error:{} {}", csi_color, csi_reset, format_args!($($arg)*));
|
||||
eprintln!(
|
||||
"{}Error:{} {}", csi_color, csi_reset, format_args!($($arg)*)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -71,7 +75,9 @@ macro_rules! warning {
|
|||
($($arg:tt)*) => ({
|
||||
let csi_color = $crate::api::console::Style::color("Yellow");
|
||||
let csi_reset = $crate::api::console::Style::reset();
|
||||
eprintln!("{}Warning:{} {}", csi_color, csi_reset, format_args!($($arg)*));
|
||||
eprintln!(
|
||||
"{}Warning:{} {}", csi_color, csi_reset, format_args!($($arg)*)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@ use core::convert::From;
|
|||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum ExitCode {
|
||||
Success = 0,
|
||||
Failure = 1,
|
||||
UsageError = 64,
|
||||
DataError = 65,
|
||||
Success = 0,
|
||||
Failure = 1,
|
||||
UsageError = 64,
|
||||
DataError = 65,
|
||||
OpenError = 128,
|
||||
ReadError = 129,
|
||||
ExecError = 130,
|
||||
|
@ -19,15 +19,15 @@ pub enum ExitCode {
|
|||
impl From<usize> for ExitCode {
|
||||
fn from(code: usize) -> Self {
|
||||
match code {
|
||||
0 => ExitCode::Success,
|
||||
64 => ExitCode::UsageError,
|
||||
65 => ExitCode::DataError,
|
||||
0 => ExitCode::Success,
|
||||
64 => ExitCode::UsageError,
|
||||
65 => ExitCode::DataError,
|
||||
128 => ExitCode::OpenError,
|
||||
129 => ExitCode::ReadError,
|
||||
130 => ExitCode::ExecError,
|
||||
200 => ExitCode::PageFaultError,
|
||||
255 => ExitCode::ShellExit,
|
||||
_ => ExitCode::Failure,
|
||||
_ => ExitCode::Failure,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use crate::api::{console, fs, io};
|
|||
use alloc::boxed::Box;
|
||||
use alloc::string::{String, ToString};
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp;
|
||||
use vte::{Params, Parser, Perform};
|
||||
|
||||
pub struct Prompt {
|
||||
|
@ -36,20 +35,20 @@ impl Prompt {
|
|||
self.update_completion();
|
||||
println!();
|
||||
return Some(String::new());
|
||||
},
|
||||
}
|
||||
console::EOT_KEY => { // End of Transmission (^D)
|
||||
self.update_completion();
|
||||
println!();
|
||||
return None;
|
||||
},
|
||||
}
|
||||
'\n' => { // New Line
|
||||
self.update_completion();
|
||||
self.update_history();
|
||||
println!();
|
||||
return Some(self.line.iter().collect());
|
||||
},
|
||||
}
|
||||
c => {
|
||||
for b in c.to_string().as_bytes() {
|
||||
for b in c.to_string().as_bytes() {
|
||||
parser.advance(self, *b);
|
||||
}
|
||||
}
|
||||
|
@ -91,16 +90,16 @@ impl Prompt {
|
|||
} else {
|
||||
(bs, 0)
|
||||
}
|
||||
},
|
||||
}
|
||||
None => {
|
||||
let line: String = self.line.iter().collect();
|
||||
self.completion.entries = (self.completion.completer)(&line);
|
||||
if !self.completion.entries.is_empty() {
|
||||
(0, 0)
|
||||
} else {
|
||||
return
|
||||
return;
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
let erase = "\x08".repeat(bs);
|
||||
let complete = &self.completion.entries[pos];
|
||||
|
@ -123,16 +122,16 @@ impl Prompt {
|
|||
} else {
|
||||
(bs, pos - 1)
|
||||
}
|
||||
},
|
||||
}
|
||||
None => {
|
||||
let line: String = self.line.iter().collect();
|
||||
self.completion.entries = (self.completion.completer)(&line);
|
||||
if !self.completion.entries.is_empty() {
|
||||
(0, 0)
|
||||
} else {
|
||||
return
|
||||
return;
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
let erase = "\x08".repeat(bs);
|
||||
let complete = &self.completion.entries[pos];
|
||||
|
@ -147,8 +146,12 @@ impl Prompt {
|
|||
return;
|
||||
}
|
||||
let (bs, i) = match self.history.pos {
|
||||
Some(i) => (self.history.entries[i].chars().count(), cmp::max(i, 1) - 1),
|
||||
None => (self.line.len(), n - 1),
|
||||
Some(i) => {
|
||||
(self.history.entries[i].chars().count(), i.saturating_sub(1))
|
||||
}
|
||||
None => {
|
||||
(self.line.len(), n - 1)
|
||||
}
|
||||
};
|
||||
let line = &self.history.entries[i];
|
||||
let blank = ' '.to_string().repeat((self.offset + bs) - self.cursor);
|
||||
|
@ -245,7 +248,7 @@ impl Perform for Prompt {
|
|||
match c {
|
||||
'\x08' => self.handle_backspace_key(),
|
||||
'\t' => self.handle_tab_key(),
|
||||
_ => {},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,7 +259,7 @@ impl Perform for Prompt {
|
|||
}
|
||||
}
|
||||
|
||||
fn csi_dispatch(&mut self, params: &Params, _intermediates: &[u8], _ignore: bool, c: char) {
|
||||
fn csi_dispatch(&mut self, params: &Params, _: &[u8], _: bool, c: char) {
|
||||
match c {
|
||||
'A' => self.handle_up_key(),
|
||||
'B' => self.handle_down_key(),
|
||||
|
@ -269,8 +272,8 @@ impl Perform for Prompt {
|
|||
self.handle_delete_key();
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::api::syscall;
|
||||
use crate::api::fs;
|
||||
use crate::api::syscall;
|
||||
|
||||
pub fn get_u64() -> u64 {
|
||||
let mut buf = [0; 8];
|
||||
|
|
193
src/api/regex.rs
193
src/api/regex.rs
|
@ -21,7 +21,7 @@ impl From<char> for MetaChar {
|
|||
fn from(c: char) -> Self {
|
||||
match c {
|
||||
'.' => MetaChar::Any,
|
||||
_ => MetaChar::Literal(c),
|
||||
_ => MetaChar::Literal(c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ impl MetaCharExt for MetaChar {
|
|||
'D' => MetaChar::NonNumeric,
|
||||
'S' => MetaChar::NonWhitespace,
|
||||
'W' => MetaChar::NonAlphanumeric,
|
||||
_ => MetaChar::Literal(c),
|
||||
_ => MetaChar::Literal(c),
|
||||
}
|
||||
}
|
||||
fn contains(&self, c: char) -> bool {
|
||||
|
@ -82,7 +82,12 @@ impl Regex {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_match(re: &[char], text: &[char], start: &mut usize, end: &mut usize) -> bool {
|
||||
fn is_match(
|
||||
re: &[char],
|
||||
text: &[char],
|
||||
start: &mut usize,
|
||||
end: &mut usize,
|
||||
) -> bool {
|
||||
if re.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
@ -105,7 +110,11 @@ fn is_match(re: &[char], text: &[char], start: &mut usize, end: &mut usize) -> b
|
|||
}
|
||||
}
|
||||
|
||||
fn is_match_here(re: &[char], text: &[char], end: &mut usize) -> bool {
|
||||
fn is_match_here(
|
||||
re: &[char],
|
||||
text: &[char],
|
||||
end: &mut usize,
|
||||
) -> bool {
|
||||
if re.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
@ -136,19 +145,44 @@ fn is_match_here(re: &[char], text: &[char], end: &mut usize) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
fn is_match_star(lazy: bool, mc: MetaChar, re: &[char], text: &[char], end: &mut usize) -> bool {
|
||||
fn is_match_star(
|
||||
lazy: bool,
|
||||
mc: MetaChar,
|
||||
re: &[char],
|
||||
text: &[char],
|
||||
end: &mut usize,
|
||||
) -> bool {
|
||||
is_match_char(lazy, mc, re, text, .., end)
|
||||
}
|
||||
|
||||
fn is_match_plus(lazy: bool, mc: MetaChar, re: &[char], text: &[char], end: &mut usize) -> bool {
|
||||
fn is_match_plus(
|
||||
lazy: bool,
|
||||
mc: MetaChar,
|
||||
re: &[char],
|
||||
text: &[char],
|
||||
end: &mut usize,
|
||||
) -> bool {
|
||||
is_match_char(lazy, mc, re, text, 1.., end)
|
||||
}
|
||||
|
||||
fn is_match_ques(lazy: bool, mc: MetaChar, re: &[char], text: &[char], end: &mut usize) -> bool {
|
||||
fn is_match_ques(
|
||||
lazy: bool,
|
||||
mc: MetaChar,
|
||||
re: &[char],
|
||||
text: &[char],
|
||||
end: &mut usize,
|
||||
) -> bool {
|
||||
is_match_char(lazy, mc, re, text, ..2, end)
|
||||
}
|
||||
|
||||
fn is_match_char<T: RangeBounds<usize>>(lazy: bool, mc: MetaChar, re: &[char], text: &[char], range: T, end: &mut usize) -> bool {
|
||||
fn is_match_char<T: RangeBounds<usize>>(
|
||||
lazy: bool,
|
||||
mc: MetaChar,
|
||||
re: &[char],
|
||||
text: &[char],
|
||||
range: T,
|
||||
end: &mut usize,
|
||||
) -> bool {
|
||||
let mut i = 0;
|
||||
let n = text.len();
|
||||
|
||||
|
@ -183,82 +217,81 @@ fn is_match_char<T: RangeBounds<usize>>(lazy: bool, mc: MetaChar, re: &[char], t
|
|||
#[test_case]
|
||||
fn test_regex() {
|
||||
let tests = [
|
||||
("", "aaa", true),
|
||||
("", "", true),
|
||||
("aaa", "aaa", true),
|
||||
("aaa", "bbb", false),
|
||||
("a.a", "aaa", true),
|
||||
("a.a", "aba", true),
|
||||
("a.a", "abb", false),
|
||||
|
||||
("a*", "aaa", true),
|
||||
("a*b", "aab", true),
|
||||
("a*b*", "aabb", true),
|
||||
("a*b*", "bb", true),
|
||||
("a.*", "abb", true),
|
||||
(".*", "aaa", true),
|
||||
("a.*", "a", true),
|
||||
|
||||
("a.+", "ab", true),
|
||||
("a.+", "abb", true),
|
||||
("a.+", "a", false),
|
||||
("a.+b", "ab", false),
|
||||
("a.+b", "abb", true),
|
||||
(".+", "abb", true),
|
||||
(".+", "b", true),
|
||||
|
||||
("a?b", "abb", true),
|
||||
("a?b", "bb", true),
|
||||
("a?b", "aabb", true),
|
||||
|
||||
("^a.*a$", "aaa", true),
|
||||
("^#.*", "#aaa", true),
|
||||
("^#.*", "a#aaa", false),
|
||||
(".*;$", "aaa;", true),
|
||||
(".*;$", "aaa;a", false),
|
||||
("^.*$", "aaa", true),
|
||||
|
||||
("a.b", "abb", true),
|
||||
("a.b", "a.b", true),
|
||||
("a\\.b", "abb", false),
|
||||
("a\\.b", "a.b", true),
|
||||
("a\\\\.b", "abb", false),
|
||||
("a\\\\.b", "a.b", false),
|
||||
("a\\\\.b", "a\\bb", true),
|
||||
("a\\\\.b", "a\\.b", true),
|
||||
("a\\\\\\.b", "a\\bb", false),
|
||||
("a\\\\\\.b", "a\\.b", true),
|
||||
("a\\\\\\.b", "a\\\\bb", false),
|
||||
("a\\\\\\.b", "a\\\\.b", false),
|
||||
("a\\\\\\\\.b", "a\\bb", false),
|
||||
("a\\\\\\\\.b", "a\\.b", false),
|
||||
("", "aaa", true),
|
||||
("", "", true),
|
||||
("aaa", "aaa", true),
|
||||
("aaa", "bbb", false),
|
||||
("a.a", "aaa", true),
|
||||
("a.a", "aba", true),
|
||||
("a.a", "abb", false),
|
||||
("a*", "aaa", true),
|
||||
("a*b", "aab", true),
|
||||
("a*b*", "aabb", true),
|
||||
("a*b*", "bb", true),
|
||||
("a.*", "abb", true),
|
||||
(".*", "aaa", true),
|
||||
("a.*", "a", true),
|
||||
("a.+", "ab", true),
|
||||
("a.+", "abb", true),
|
||||
("a.+", "a", false),
|
||||
("a.+b", "ab", false),
|
||||
("a.+b", "abb", true),
|
||||
(".+", "abb", true),
|
||||
(".+", "b", true),
|
||||
("a?b", "abb", true),
|
||||
("a?b", "bb", true),
|
||||
("a?b", "aabb", true),
|
||||
("^a.*a$", "aaa", true),
|
||||
("^#.*", "#aaa", true),
|
||||
("^#.*", "a#aaa", false),
|
||||
(".*;$", "aaa;", true),
|
||||
(".*;$", "aaa;a", false),
|
||||
("^.*$", "aaa", true),
|
||||
("a.b", "abb", true),
|
||||
("a.b", "a.b", true),
|
||||
("a\\.b", "abb", false),
|
||||
("a\\.b", "a.b", true),
|
||||
("a\\\\.b", "abb", false),
|
||||
("a\\\\.b", "a.b", false),
|
||||
("a\\\\.b", "a\\bb", true),
|
||||
("a\\\\.b", "a\\.b", true),
|
||||
("a\\\\\\.b", "a\\bb", false),
|
||||
("a\\\\\\.b", "a\\.b", true),
|
||||
("a\\\\\\.b", "a\\\\bb", false),
|
||||
("a\\\\\\.b", "a\\\\.b", false),
|
||||
("a\\\\\\\\.b", "a\\bb", false),
|
||||
("a\\\\\\\\.b", "a\\.b", false),
|
||||
("a\\\\\\\\.b", "a\\\\bb", true),
|
||||
("a\\\\\\\\.b", "a\\\\.b", true),
|
||||
|
||||
("a\\wb", "aéb", true),
|
||||
("a\\wb", "awb", true),
|
||||
("a\\wb", "abb", true),
|
||||
("a\\wb", "a1b", true),
|
||||
("a\\wb", "a.b", false),
|
||||
("a\\Wb", "aWb", false),
|
||||
("a\\Wb", "abb", false),
|
||||
("a\\Wb", "a1b", false),
|
||||
("a\\Wb", "a.b", true),
|
||||
("a\\db", "abb", false),
|
||||
("a\\db", "a1b", true),
|
||||
("a\\Db", "abb", true),
|
||||
("a\\Db", "a1b", false),
|
||||
("a\\sb", "abb", false),
|
||||
("a\\sb", "a b", true),
|
||||
("a\\Sb", "abb", true),
|
||||
("a\\Sb", "a b", false),
|
||||
|
||||
("a\\.*d", "a..d", true),
|
||||
("a\\.*d", "a.cd", false),
|
||||
("a\\w*d", "abcd", true),
|
||||
("a\\wb", "aéb", true),
|
||||
("a\\wb", "awb", true),
|
||||
("a\\wb", "abb", true),
|
||||
("a\\wb", "a1b", true),
|
||||
("a\\wb", "a.b", false),
|
||||
("a\\Wb", "aWb", false),
|
||||
("a\\Wb", "abb", false),
|
||||
("a\\Wb", "a1b", false),
|
||||
("a\\Wb", "a.b", true),
|
||||
("a\\db", "abb", false),
|
||||
("a\\db", "a1b", true),
|
||||
("a\\Db", "abb", true),
|
||||
("a\\Db", "a1b", false),
|
||||
("a\\sb", "abb", false),
|
||||
("a\\sb", "a b", true),
|
||||
("a\\Sb", "abb", true),
|
||||
("a\\Sb", "a b", false),
|
||||
("a\\.*d", "a..d", true),
|
||||
("a\\.*d", "a.cd", false),
|
||||
("a\\w*d", "abcd", true),
|
||||
];
|
||||
for (re, text, is_match) in tests {
|
||||
assert!(Regex::new(re).is_match(text) == is_match, "Regex::new(\"{}\").is_match(\"{}\") == {}", re, text, is_match);
|
||||
assert!(
|
||||
Regex::new(re).is_match(text) == is_match,
|
||||
"Regex::new(\"{}\").is_match(\"{}\") == {}",
|
||||
re,
|
||||
text,
|
||||
is_match
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(Regex::new(".*").find("abcd"), Some((0, 4)));
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::api::fs::IO;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::syscall;
|
||||
use crate::sys::syscall::number::*;
|
||||
use crate::sys::fs::FileInfo;
|
||||
use crate::sys::syscall::number::*;
|
||||
use crate::syscall;
|
||||
|
||||
use smoltcp::wire::IpAddress;
|
||||
use smoltcp::wire::Ipv4Address;
|
||||
|
@ -90,7 +90,9 @@ pub fn spawn(path: &str, args: &[&str]) -> Result<(), ExitCode> {
|
|||
let args_ptr = args.as_ptr() as usize;
|
||||
let path_len = path.len();
|
||||
let args_len = args.len();
|
||||
let res = unsafe { syscall!(SPAWN, path_ptr, path_len, args_ptr, args_len) };
|
||||
let res = unsafe {
|
||||
syscall!(SPAWN, path_ptr, path_len, args_ptr, args_len)
|
||||
};
|
||||
if res == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -156,9 +158,7 @@ pub fn accept(handle: usize) -> Result<IpAddress, ()> {
|
|||
}
|
||||
|
||||
pub fn alloc(size: usize, align: usize) -> *mut u8 {
|
||||
unsafe {
|
||||
syscall!(ALLOC, size, align) as *mut u8
|
||||
}
|
||||
unsafe { syscall!(ALLOC, size, align) as *mut u8 }
|
||||
}
|
||||
|
||||
pub fn free(ptr: *mut u8, size: usize, align: usize) {
|
||||
|
@ -169,7 +169,7 @@ pub fn free(ptr: *mut u8, size: usize, align: usize) {
|
|||
|
||||
#[test_case]
|
||||
fn test_file() {
|
||||
use crate::sys::fs::{mount_mem, format_mem, dismount, OpenFlag};
|
||||
use crate::sys::fs::{dismount, format_mem, mount_mem, OpenFlag};
|
||||
use alloc::vec;
|
||||
mount_mem();
|
||||
format_mem();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::sys;
|
||||
use crate::api::clock;
|
||||
use crate::sys;
|
||||
|
||||
use time::{OffsetDateTime, Duration, UtcOffset};
|
||||
use time::{Duration, OffsetDateTime, UtcOffset};
|
||||
|
||||
pub fn now() -> OffsetDateTime {
|
||||
now_utc().to_offset(offset())
|
||||
|
@ -9,7 +9,9 @@ pub fn now() -> OffsetDateTime {
|
|||
|
||||
pub fn now_utc() -> OffsetDateTime {
|
||||
let s = clock::realtime(); // Since Unix Epoch
|
||||
let ns = Duration::nanoseconds(libm::floor(1e9 * (s - libm::floor(s))) as i64);
|
||||
let ns = Duration::nanoseconds(
|
||||
libm::floor(1e9 * (s - libm::floor(s))) as i64
|
||||
);
|
||||
OffsetDateTime::from_unix_timestamp(s as i64) + ns
|
||||
}
|
||||
|
||||
|
|
|
@ -3,22 +3,22 @@
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum Color {
|
||||
Black = 0,
|
||||
Blue = 1,
|
||||
Green = 2,
|
||||
Cyan = 3,
|
||||
Red = 4,
|
||||
Magenta = 5,
|
||||
Brown = 6,
|
||||
LightGray = 7,
|
||||
DarkGray = 8,
|
||||
LightBlue = 9,
|
||||
LightGreen = 10,
|
||||
LightCyan = 11,
|
||||
LightRed = 12,
|
||||
Pink = 13,
|
||||
Yellow = 14,
|
||||
White = 15,
|
||||
Black = 0x0,
|
||||
Blue = 0x1,
|
||||
Green = 0x2,
|
||||
Cyan = 0x3,
|
||||
Red = 0x4,
|
||||
Magenta = 0x5,
|
||||
Brown = 0x6,
|
||||
LightGray = 0x7,
|
||||
DarkGray = 0x8,
|
||||
LightBlue = 0x9,
|
||||
LightGreen = 0xA,
|
||||
LightCyan = 0xB,
|
||||
LightRed = 0xC,
|
||||
Pink = 0xD,
|
||||
Yellow = 0xE,
|
||||
White = 0xF,
|
||||
}
|
||||
|
||||
const COLORS: [Color; 16] = [
|
||||
|
@ -66,7 +66,7 @@ pub fn from_ansi(code: u8) -> Color {
|
|||
95 => Color::Pink,
|
||||
96 => Color::LightCyan,
|
||||
97 => Color::White,
|
||||
_ => Color::Black, // Error
|
||||
_ => Color::Black, // TODO: Error
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use core::convert::TryInto;
|
|||
|
||||
// TODO: Move this to kernel after removing the `vga set palette` command
|
||||
pub struct Palette {
|
||||
pub colors: [(u8, u8, u8); 16]
|
||||
pub colors: [(u8, u8, u8); 16],
|
||||
}
|
||||
|
||||
impl Palette {
|
||||
|
@ -26,7 +26,7 @@ impl Palette {
|
|||
(0xFF, 0x00, 0xFF), // Pink (Light Magenta)
|
||||
(0xFF, 0xFF, 0x00), // Yellow (Light Yellow)
|
||||
(0xFF, 0xFF, 0xFF), // White
|
||||
]
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,5 +7,5 @@ use moros::entry_point;
|
|||
entry_point!(main);
|
||||
|
||||
fn main(_args: &[&str]) {
|
||||
syscall::write(1, b"\x1b[2J\x1b[1;1H"); // Clear screen and move cursor to top
|
||||
syscall::write(1, b"\x1b[2J\x1b[1;1H"); // Clear screen and move to top
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ extern crate alloc;
|
|||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
use moros::api::io;
|
||||
use moros::api::syscall;
|
||||
use moros::api::process;
|
||||
use moros::api::syscall;
|
||||
use moros::entry_point;
|
||||
|
||||
entry_point!(main);
|
||||
|
|
|
@ -8,10 +8,13 @@ entry_point!(main);
|
|||
|
||||
fn main(_args: &[&str]) {
|
||||
syscall::write(1, b"\x1b[93m"); // Yellow
|
||||
syscall::write(1, b"MOROS has reached its fate, the system is now halting.");
|
||||
syscall::write(1, b"MOROS has reached its fate, ");
|
||||
syscall::write(1, b"the system is now halting.");
|
||||
syscall::write(1, b"\x1b[0m"); // Reset
|
||||
syscall::write(1, b"\n");
|
||||
syscall::sleep(0.5);
|
||||
syscall::halt();
|
||||
loop { syscall::sleep(1.0) }
|
||||
loop {
|
||||
syscall::sleep(1.0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,17 +11,17 @@ entry_point!(main);
|
|||
|
||||
fn main(args: &[&str]) {
|
||||
if args.len() > 1 {
|
||||
syscall::write(1, args[1].as_bytes()); // FIXME: this is needed
|
||||
syscall::write(1, args[1].as_bytes()); // FIXME: this is needed
|
||||
syscall::write(1, "\n".as_bytes());
|
||||
|
||||
let mut hello = "Hello, ".to_string();
|
||||
hello.push_str(args[1]); // FIXME: for that to work
|
||||
hello.push_str(args[1]); // FIXME: for that to work
|
||||
hello.push_str("!\n");
|
||||
syscall::write(1, hello.as_bytes());
|
||||
|
||||
if args.len() > 2 {
|
||||
let mut hello = "Hello, ".to_string();
|
||||
hello.push_str(args[2]); // FIXME: not working
|
||||
hello.push_str(args[2]); // FIXME: not working
|
||||
hello.push_str("!\n");
|
||||
syscall::write(1, hello.as_bytes());
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use moros::entry_point;
|
||||
use moros::api::syscall;
|
||||
use moros::entry_point;
|
||||
|
||||
entry_point!(main);
|
||||
|
||||
|
|
|
@ -8,9 +8,14 @@ entry_point!(main);
|
|||
|
||||
fn main(_args: &[&str]) {
|
||||
syscall::write(1, b"\x1b[93m"); // Yellow
|
||||
syscall::write(1, b"MOROS has reached its fate, the system is now rebooting.\n");
|
||||
syscall::write(
|
||||
1,
|
||||
b"MOROS has reached its fate, the system is now rebooting.\n",
|
||||
);
|
||||
syscall::write(1, b"\x1b[0m"); // Reset
|
||||
syscall::sleep(0.5);
|
||||
syscall::reboot();
|
||||
loop { syscall::sleep(1.0) }
|
||||
loop {
|
||||
syscall::sleep(1.0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use moros::api::syscall;
|
||||
use moros::api::process::ExitCode;
|
||||
use moros::api::syscall;
|
||||
use moros::entry_point;
|
||||
|
||||
entry_point!(main);
|
||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -30,7 +30,9 @@ pub fn init(boot_info: &'static BootInfo) {
|
|||
sys::keyboard::init();
|
||||
sys::time::init();
|
||||
|
||||
log!("MOROS v{}\n", option_env!("MOROS_VERSION").unwrap_or(env!("CARGO_PKG_VERSION")));
|
||||
let v = option_env!("MOROS_VERSION").unwrap_or(env!("CARGO_PKG_VERSION"));
|
||||
log!("MOROS v{}\n", v);
|
||||
|
||||
sys::mem::init(boot_info);
|
||||
sys::cpu::init();
|
||||
sys::pci::init(); // Require MEM
|
||||
|
@ -44,7 +46,12 @@ pub fn init(boot_info: &'static BootInfo) {
|
|||
fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
|
||||
let csi_color = api::console::Style::color("LightRed");
|
||||
let csi_reset = api::console::Style::reset();
|
||||
printk!("{}Error:{} Could not allocate {} bytes\n", csi_color, csi_reset, layout.size());
|
||||
printk!(
|
||||
"{}Error:{} Could not allocate {} bytes\n",
|
||||
csi_color,
|
||||
csi_reset,
|
||||
layout.size()
|
||||
);
|
||||
hlt_loop();
|
||||
}
|
||||
|
||||
|
@ -71,7 +78,6 @@ pub fn test_runner(tests: &[&dyn Testable]) {
|
|||
exit_qemu(QemuExitCode::Success);
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum QemuExitCode {
|
||||
|
|
|
@ -5,7 +5,7 @@ extern crate alloc;
|
|||
|
||||
use bootloader::{entry_point, BootInfo};
|
||||
use core::panic::PanicInfo;
|
||||
use moros::{sys, usr, print, println, debug, hlt_loop};
|
||||
use moros::{debug, hlt_loop, print, println, sys, usr};
|
||||
|
||||
entry_point!(main);
|
||||
|
||||
|
|
120
src/sys/acpi.rs
120
src/sys/acpi.rs
|
@ -1,15 +1,15 @@
|
|||
use crate::sys;
|
||||
|
||||
use acpi::{AcpiHandler, PhysicalMapping, AcpiTables};
|
||||
use acpi::{AcpiHandler, AcpiTables, PhysicalMapping};
|
||||
use alloc::boxed::Box;
|
||||
use aml::value::AmlValue;
|
||||
use aml::{AmlContext, AmlName, DebugVerbosity, Handler};
|
||||
use core::ptr::NonNull;
|
||||
use x86_64::PhysAddr;
|
||||
use x86_64::instructions::port::Port;
|
||||
use x86_64::PhysAddr;
|
||||
|
||||
fn read_addr<T>(physical_address: usize) -> T where T: Copy {
|
||||
let virtual_address = sys::mem::phys_to_virt(PhysAddr::new(physical_address as u64));
|
||||
fn read_addr<T>(addr: usize) -> T where T: Copy {
|
||||
let virtual_address = sys::mem::phys_to_virt(PhysAddr::new(addr as u64));
|
||||
unsafe { *virtual_address.as_ptr::<T>() }
|
||||
}
|
||||
|
||||
|
@ -19,27 +19,29 @@ pub fn shutdown() {
|
|||
let slp_len = 1 << 13;
|
||||
|
||||
log!("ACPI Shutdown\n");
|
||||
let mut aml = AmlContext::new(Box::new(MorosAmlHandler), DebugVerbosity::None);
|
||||
let handler = Box::new(MorosAmlHandler);
|
||||
let mut aml = AmlContext::new(handler, DebugVerbosity::None);
|
||||
let res = unsafe { AcpiTables::search_for_rsdp_bios(MorosAcpiHandler) };
|
||||
match res {
|
||||
Ok(acpi) => {
|
||||
if let Ok(fadt) = acpi.find_table::<acpi::fadt::Fadt>() {
|
||||
if let Ok(block) = fadt.pm1a_control_block() {
|
||||
pm1a_control_block = block.address as u32;
|
||||
//debug!("ACPI Found PM1a Control Block: {:#X}", pm1a_control_block);
|
||||
}
|
||||
}
|
||||
if let Ok(dsdt) = &acpi.dsdt() {
|
||||
let address = sys::mem::phys_to_virt(PhysAddr::new(dsdt.address as u64));
|
||||
//debug!("ACPI Found DSDT at {:#X} {:#X}", dsdt.address, address);
|
||||
let table = unsafe { core::slice::from_raw_parts(address.as_ptr(), dsdt.length as usize) };
|
||||
let phys_addr = PhysAddr::new(dsdt.address as u64);
|
||||
let virt_addr = sys::mem::phys_to_virt(phys_addr);
|
||||
let ptr = virt_addr.as_ptr();
|
||||
let table = unsafe {
|
||||
core::slice::from_raw_parts(ptr , dsdt.length as usize)
|
||||
};
|
||||
if aml.parse_table(table).is_ok() {
|
||||
let name = AmlName::from_str("\\_S5").unwrap();
|
||||
if let Ok(AmlValue::Package(s5)) = aml.namespace.get_by_path(&name) {
|
||||
//debug!("ACPI Found \\_S5 in DSDT");
|
||||
let res = aml.namespace.get_by_path(&name);
|
||||
if let Ok(AmlValue::Package(s5)) = res {
|
||||
if let AmlValue::Integer(value) = s5[0] {
|
||||
slp_typa = value as u16;
|
||||
//debug!("ACPI Found SLP_TYPa in \\_S5: {}", slp_typa);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -67,10 +69,15 @@ pub fn shutdown() {
|
|||
pub struct MorosAcpiHandler;
|
||||
|
||||
impl AcpiHandler for MorosAcpiHandler {
|
||||
unsafe fn map_physical_region<T>(&self, physical_address: usize, size: usize) -> PhysicalMapping<Self, T> {
|
||||
let virtual_address = sys::mem::phys_to_virt(PhysAddr::new(physical_address as u64));
|
||||
//debug!("ACPI mapping phys {:#X} -> virt {:#X}", physical_address, virtual_address);
|
||||
PhysicalMapping::new(physical_address, NonNull::new(virtual_address.as_mut_ptr()).unwrap(), size, size, Self)
|
||||
unsafe fn map_physical_region<T>(
|
||||
&self,
|
||||
addr: usize,
|
||||
size: usize,
|
||||
) -> PhysicalMapping<Self, T> {
|
||||
let phys_addr = PhysAddr::new(addr as u64);
|
||||
let virt_addr = sys::mem::phys_to_virt(phys_addr);
|
||||
let ptr = NonNull::new(virt_addr.as_mut_ptr()).unwrap();
|
||||
PhysicalMapping::new(addr, ptr, size, size, Self)
|
||||
}
|
||||
|
||||
fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {}
|
||||
|
@ -79,24 +86,65 @@ impl AcpiHandler for MorosAcpiHandler {
|
|||
struct MorosAmlHandler;
|
||||
|
||||
impl Handler for MorosAmlHandler {
|
||||
fn read_u8(&self, address: usize) -> u8 { read_addr::<u8>(address) }
|
||||
fn read_u16(&self, address: usize) -> u16 { read_addr::<u16>(address) }
|
||||
fn read_u32(&self, address: usize) -> u32 { read_addr::<u32>(address) }
|
||||
fn read_u64(&self, address: usize) -> u64 { read_addr::<u64>(address) }
|
||||
fn write_u8(&mut self, _address: usize, _value: u8) { unimplemented!() }
|
||||
fn write_u16(&mut self, _address: usize, _value: u16) { unimplemented!() }
|
||||
fn write_u32(&mut self, _address: usize, _value: u32) { unimplemented!() }
|
||||
fn write_u64(&mut self, _address: usize, _value: u64) { unimplemented!() }
|
||||
fn read_io_u8(&self, _port: u16) -> u8 { unimplemented!() }
|
||||
fn read_io_u16(&self, _port: u16) -> u16 { unimplemented!() }
|
||||
fn read_io_u32(&self, _port: u16) -> u32 { unimplemented!() }
|
||||
fn write_io_u8(&self, _port: u16, _value: u8) { unimplemented!() }
|
||||
fn write_io_u16(&self, _port: u16, _value: u16) { unimplemented!() }
|
||||
fn write_io_u32(&self, _port: u16, _value: u32) { unimplemented!() }
|
||||
fn read_pci_u8(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u8 { unimplemented!() }
|
||||
fn read_pci_u16(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u16 { unimplemented!() }
|
||||
fn read_pci_u32(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u32 { unimplemented!() }
|
||||
fn write_pci_u8(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16, _value: u8) { unimplemented!() }
|
||||
fn write_pci_u16(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16, _value: u16) { unimplemented!() }
|
||||
fn write_pci_u32(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16, _value: u32) { unimplemented!() }
|
||||
fn read_u8(&self, address: usize) -> u8 {
|
||||
read_addr::<u8>(address)
|
||||
}
|
||||
fn read_u16(&self, address: usize) -> u16 {
|
||||
read_addr::<u16>(address)
|
||||
}
|
||||
fn read_u32(&self, address: usize) -> u32 {
|
||||
read_addr::<u32>(address)
|
||||
}
|
||||
fn read_u64(&self, address: usize) -> u64 {
|
||||
read_addr::<u64>(address)
|
||||
}
|
||||
|
||||
fn write_u8(&mut self, _: usize, _: u8) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn write_u16(&mut self, _: usize, _: u16) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn write_u32(&mut self, _: usize, _: u32) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn write_u64(&mut self, _: usize, _: u64) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn read_io_u8(&self, _: u16) -> u8 {
|
||||
unimplemented!()
|
||||
}
|
||||
fn read_io_u16(&self, _: u16) -> u16 {
|
||||
unimplemented!()
|
||||
}
|
||||
fn read_io_u32(&self, _: u16) -> u32 {
|
||||
unimplemented!()
|
||||
}
|
||||
fn write_io_u8(&self, _: u16, _: u8) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn write_io_u16(&self, _: u16, _: u16) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn write_io_u32(&self, _: u16, _: u32) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn read_pci_u8(&self, _: u16, _: u8, _: u8, _: u8, _: u16) -> u8 {
|
||||
unimplemented!()
|
||||
}
|
||||
fn read_pci_u16(&self, _: u16, _: u8, _: u8, _: u8, _: u16) -> u16 {
|
||||
unimplemented!()
|
||||
}
|
||||
fn read_pci_u32(&self, _: u16, _: u8, _: u8, _: u8, _: u16) -> u32 {
|
||||
unimplemented!()
|
||||
}
|
||||
fn write_pci_u8(&self, _: u16, _: u8, _: u8, _: u8, _: u16, _: u8) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn write_pci_u16(&self, _: u16, _: u8, _: u8, _: u8, _: u16, _: u16) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn write_pci_u32(&self, _: u16, _: u8, _: u8, _: u8, _: u16, _: u32) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,10 @@ use core::cmp;
|
|||
use core::ops::{Index, IndexMut};
|
||||
use linked_list_allocator::LockedHeap;
|
||||
use spin::Mutex;
|
||||
use x86_64::structures::paging::OffsetPageTable;
|
||||
use x86_64::structures::paging::mapper::MapToError;
|
||||
use x86_64::structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB};
|
||||
use x86_64::structures::paging::{
|
||||
mapper::MapToError, page::PageRangeInclusive,
|
||||
FrameAllocator, Mapper, OffsetPageTable, Page, PageTableFlags, Size4KiB,
|
||||
};
|
||||
use x86_64::VirtAddr;
|
||||
|
||||
#[cfg_attr(not(feature = "userspace"), global_allocator)]
|
||||
|
@ -19,15 +20,16 @@ static ALLOCATOR: LockedHeap = LockedHeap::empty();
|
|||
pub const HEAP_START: u64 = 0x4444_4444_0000;
|
||||
|
||||
fn max_memory() -> u64 {
|
||||
option_env!("MOROS_MEMORY").unwrap_or("32").parse::<u64>().unwrap() << 20 // MB
|
||||
// Default to 32 MB
|
||||
option_env!("MOROS_MEMORY").unwrap_or("32").parse::<u64>().unwrap() << 20
|
||||
}
|
||||
|
||||
pub fn init_heap() -> Result<(), MapToError<Size4KiB>> {
|
||||
let mapper = sys::mem::mapper();
|
||||
let mut frame_allocator = sys::mem::frame_allocator();
|
||||
|
||||
// Use half of the memory for the heap caped to 16MB by default because the
|
||||
// allocator is slow.
|
||||
// Use half of the memory for the heap caped to 16 MB by default
|
||||
// because the allocator is slow.
|
||||
let heap_size = cmp::min(sys::mem::memory_size(), max_memory()) / 2;
|
||||
let heap_start = VirtAddr::new(HEAP_START);
|
||||
sys::process::init_process_addr(HEAP_START + heap_size);
|
||||
|
@ -40,8 +42,10 @@ pub fn init_heap() -> Result<(), MapToError<Size4KiB>> {
|
|||
};
|
||||
|
||||
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
|
||||
|
||||
for page in pages {
|
||||
let frame = frame_allocator.allocate_frame().ok_or(MapToError::FrameAllocationFailed)?;
|
||||
let err = MapToError::FrameAllocationFailed;
|
||||
let frame = frame_allocator.allocate_frame().ok_or(err)?;
|
||||
unsafe {
|
||||
mapper.map_to(page, frame, flags, &mut frame_allocator)?.flush();
|
||||
}
|
||||
|
@ -54,47 +58,54 @@ pub fn init_heap() -> Result<(), MapToError<Size4KiB>> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn alloc_pages(mapper: &mut OffsetPageTable, addr: u64, size: usize) -> Result<(), ()> {
|
||||
//debug!("Alloc pages (addr={:#X}, size={})", addr, size);
|
||||
pub fn alloc_pages(
|
||||
mapper: &mut OffsetPageTable, addr: u64, size: usize
|
||||
) -> Result<(), ()> {
|
||||
let size = size.saturating_sub(1) as u64;
|
||||
let mut frame_allocator = sys::mem::frame_allocator();
|
||||
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE;
|
||||
|
||||
let pages = {
|
||||
let start_page = Page::containing_address(VirtAddr::new(addr));
|
||||
let end_page = Page::containing_address(VirtAddr::new(addr + (size as u64) - 1));
|
||||
let end_page = Page::containing_address(VirtAddr::new(addr + size));
|
||||
Page::range_inclusive(start_page, end_page)
|
||||
};
|
||||
|
||||
let flags = PageTableFlags::PRESENT
|
||||
| PageTableFlags::WRITABLE
|
||||
| PageTableFlags::USER_ACCESSIBLE;
|
||||
|
||||
for page in pages {
|
||||
//debug!("Alloc page {:?}", page);
|
||||
if let Some(frame) = frame_allocator.allocate_frame() {
|
||||
//debug!("Alloc frame {:?}", frame);
|
||||
unsafe {
|
||||
if let Ok(mapping) = mapper.map_to(page, frame, flags, &mut frame_allocator) {
|
||||
//debug!("Mapped {:?} to {:?}", page, frame);
|
||||
mapping.flush();
|
||||
} else {
|
||||
debug!("Could not map {:?} to {:?}", page, frame);
|
||||
return Err(());
|
||||
}
|
||||
let res = unsafe {
|
||||
mapper.map_to(page, frame, flags, &mut frame_allocator)
|
||||
};
|
||||
if let Ok(mapping) = res {
|
||||
mapping.flush();
|
||||
} else {
|
||||
debug!("Could not map {:?} to {:?}", page, frame);
|
||||
return Err(());
|
||||
}
|
||||
} else {
|
||||
debug!("Could not allocate frame for {:?}", page);
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
use x86_64::structures::paging::page::PageRangeInclusive;
|
||||
|
||||
// TODO: Replace `free` by `dealloc`
|
||||
pub fn free_pages(mapper: &mut OffsetPageTable, addr: u64, size: usize) {
|
||||
let size = size.saturating_sub(1) as u64;
|
||||
|
||||
let pages: PageRangeInclusive<Size4KiB> = {
|
||||
let start_page = Page::containing_address(VirtAddr::new(addr));
|
||||
let end_page = Page::containing_address(VirtAddr::new(addr + (size as u64) - 1));
|
||||
let end_page = Page::containing_address(VirtAddr::new(addr + size));
|
||||
Page::range_inclusive(start_page, end_page)
|
||||
};
|
||||
|
||||
for page in pages {
|
||||
if let Ok((_frame, mapping)) = mapper.unmap(page) {
|
||||
if let Ok((_, mapping)) = mapper.unmap(page) {
|
||||
mapping.flush();
|
||||
} else {
|
||||
//debug!("Could not unmap {:?}", page);
|
||||
|
@ -117,7 +128,9 @@ impl PhysBuf {
|
|||
let buffer_len = vec.len() - 1;
|
||||
let memory_len = phys_addr(&vec[buffer_len]) - phys_addr(&vec[0]);
|
||||
if buffer_len == memory_len as usize {
|
||||
Self { buf: Arc::new(Mutex::new(vec)) }
|
||||
Self {
|
||||
buf: Arc::new(Mutex::new(vec)),
|
||||
}
|
||||
} else {
|
||||
Self::from(vec.clone()) // Clone vec and try again
|
||||
}
|
||||
|
@ -162,7 +175,9 @@ impl core::ops::Deref for PhysBuf {
|
|||
impl core::ops::DerefMut for PhysBuf {
|
||||
fn deref_mut(&mut self) -> &mut [u8] {
|
||||
let mut vec = self.buf.lock();
|
||||
unsafe { alloc::slice::from_raw_parts_mut(vec.as_mut_ptr(), vec.len()) }
|
||||
unsafe {
|
||||
alloc::slice::from_raw_parts_mut(vec.as_mut_ptr(), vec.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,15 +9,17 @@ use lazy_static::lazy_static;
|
|||
use spin::Mutex;
|
||||
use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly};
|
||||
|
||||
// See "Information Technology - AT Attachment with Packet Interface Extension (ATA/ATAPI-4)" (1998)
|
||||
// Information Technology
|
||||
// AT Attachment with Packet Interface Extension (ATA/ATAPI-4)
|
||||
// (1998)
|
||||
|
||||
pub const BLOCK_SIZE: usize = 512;
|
||||
|
||||
#[repr(u16)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Command {
|
||||
Read = 0x20,
|
||||
Write = 0x30,
|
||||
Read = 0x20,
|
||||
Write = 0x30,
|
||||
Identify = 0xEC,
|
||||
}
|
||||
|
||||
|
@ -67,8 +69,8 @@ pub struct Bus {
|
|||
impl Bus {
|
||||
pub fn new(id: u8, io_base: u16, ctrl_base: u16, irq: u8) -> Self {
|
||||
Self {
|
||||
id, irq,
|
||||
|
||||
id,
|
||||
irq,
|
||||
data_register: Port::new(io_base + 0),
|
||||
error_register: PortReadOnly::new(io_base + 1),
|
||||
features_register: PortWriteOnly::new(io_base + 1),
|
||||
|
@ -79,7 +81,6 @@ impl Bus {
|
|||
drive_register: Port::new(io_base + 6),
|
||||
status_register: PortReadOnly::new(io_base + 7),
|
||||
command_register: PortWriteOnly::new(io_base + 7),
|
||||
|
||||
alternate_status_register: PortReadOnly::new(ctrl_base + 0),
|
||||
control_register: PortWriteOnly::new(ctrl_base + 0),
|
||||
drive_blockess_register: PortReadOnly::new(ctrl_base + 1),
|
||||
|
@ -129,7 +130,10 @@ impl Bus {
|
|||
let start = sys::clock::uptime();
|
||||
while self.status().get_bit(bit as usize) != val {
|
||||
if sys::clock::uptime() - start > 1.0 {
|
||||
debug!("ATA hanged while polling {:?} bit in status register", bit);
|
||||
debug!(
|
||||
"ATA hanged while polling {:?} bit in status register",
|
||||
bit
|
||||
);
|
||||
self.debug();
|
||||
return Err(());
|
||||
}
|
||||
|
@ -153,7 +157,11 @@ impl Bus {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn write_command_params(&mut self, drive: u8, block: u32) -> Result<(), ()> {
|
||||
fn write_command_params(
|
||||
&mut self,
|
||||
drive: u8,
|
||||
block: u32
|
||||
) -> Result<(), ()> {
|
||||
let lba = true;
|
||||
let mut bytes = block.to_le_bytes();
|
||||
bytes[3].set_bit(4, drive > 0);
|
||||
|
@ -194,7 +202,12 @@ impl Bus {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&mut self, drive: u8, block: u32, buf: &mut [u8]) -> Result<(), ()> {
|
||||
fn read(
|
||||
&mut self,
|
||||
drive: u8,
|
||||
block: u32,
|
||||
buf: &mut [u8]
|
||||
) -> Result<(), ()> {
|
||||
debug_assert!(buf.len() == BLOCK_SIZE);
|
||||
self.setup_pio(drive, block)?;
|
||||
self.write_command(Command::Read)?;
|
||||
|
@ -242,7 +255,9 @@ impl Bus {
|
|||
}
|
||||
}
|
||||
match (self.lba1(), self.lba2()) {
|
||||
(0x00, 0x00) => Ok(IdentifyResponse::Ata([(); 256].map(|_| { self.read_data() }))),
|
||||
(0x00, 0x00) => {
|
||||
Ok(IdentifyResponse::Ata([(); 256].map(|_| self.read_data())))
|
||||
}
|
||||
(0x14, 0xEB) => Ok(IdentifyResponse::Atapi),
|
||||
(0x3C, 0xC3) => Ok(IdentifyResponse::Sata),
|
||||
(_, _) => Err(()),
|
||||
|
@ -253,17 +268,23 @@ impl Bus {
|
|||
fn reset(&mut self) {
|
||||
unsafe {
|
||||
self.control_register.write(4); // Set SRST bit
|
||||
self.wait(5); // Wait at least 5 ns
|
||||
self.wait(5); // Wait at least 5 ns
|
||||
self.control_register.write(0); // Then clear it
|
||||
self.wait(2000); // Wait at least 2 ms
|
||||
self.wait(2000); // Wait at least 2 ms
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn debug(&mut self) {
|
||||
unsafe {
|
||||
debug!("ATA status register: 0b{:08b} <BSY|DRDY|#|#|DRQ|#|#|ERR>", self.alternate_status_register.read());
|
||||
debug!("ATA error register: 0b{:08b} <#|#|#|#|#|ABRT|#|#>", self.error_register.read());
|
||||
debug!(
|
||||
"ATA status register: 0b{:08b} <BSY|DRDY|#|#|DRQ|#|#|ERR>",
|
||||
self.alternate_status_register.read()
|
||||
);
|
||||
debug!(
|
||||
"ATA error register: 0b{:08b} <#|#|#|#|#|ABRT|#|#>",
|
||||
self.error_register.read()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -296,12 +317,22 @@ pub struct Drive {
|
|||
impl Drive {
|
||||
pub fn open(bus: u8, dsk: u8) -> Option<Self> {
|
||||
let mut buses = BUSES.lock();
|
||||
if let Ok(IdentifyResponse::Ata(res)) = buses[bus as usize].identify_drive(dsk) {
|
||||
let res = buses[bus as usize].identify_drive(dsk);
|
||||
if let Ok(IdentifyResponse::Ata(res)) = res {
|
||||
let buf = res.map(u16::to_be_bytes).concat();
|
||||
let serial = String::from_utf8_lossy(&buf[20..40]).trim().into();
|
||||
let model = String::from_utf8_lossy(&buf[54..94]).trim().into();
|
||||
let blocks = u32::from_be_bytes(buf[120..124].try_into().unwrap()).rotate_left(16);
|
||||
Some(Self { bus, dsk, model, serial, blocks })
|
||||
|
||||
let blocks = u32::from_be_bytes(buf[120..124].try_into().unwrap()).
|
||||
rotate_left(16);
|
||||
|
||||
Some(Self {
|
||||
bus,
|
||||
dsk,
|
||||
model,
|
||||
serial,
|
||||
blocks,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -3,9 +3,11 @@ use crate::api::fs::{FileIO, IO};
|
|||
use crate::sys;
|
||||
use crate::sys::cmos::CMOS;
|
||||
|
||||
use time::{OffsetDateTime, Duration};
|
||||
use time::{Duration, OffsetDateTime};
|
||||
|
||||
const DAYS_BEFORE_MONTH: [u64; 13] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
|
||||
const DAYS_BEFORE_MONTH: [u64; 13] = [
|
||||
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Uptime;
|
||||
|
@ -36,8 +38,7 @@ impl FileIO for Uptime {
|
|||
unimplemented!();
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
}
|
||||
fn close(&mut self) {}
|
||||
|
||||
fn poll(&mut self, event: IO) -> bool {
|
||||
match event {
|
||||
|
@ -81,8 +82,7 @@ impl FileIO for Realtime {
|
|||
unimplemented!();
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
}
|
||||
fn close(&mut self) {}
|
||||
|
||||
fn poll(&mut self, event: IO) -> bool {
|
||||
match event {
|
||||
|
@ -96,23 +96,23 @@ impl FileIO for Realtime {
|
|||
pub fn realtime() -> f64 {
|
||||
let rtc = CMOS::new().rtc(); // Assuming GMT
|
||||
|
||||
let timestamp = 86400 * days_before_year(rtc.year as u64)
|
||||
+ 86400 * days_before_month(rtc.year as u64, rtc.month as u64)
|
||||
+ 86400 * (rtc.day - 1) as u64
|
||||
+ 3600 * rtc.hour as u64
|
||||
+ 60 * rtc.minute as u64
|
||||
+ rtc.second as u64;
|
||||
let ts = 86400 * days_before_year(rtc.year as u64)
|
||||
+ 86400 * days_before_month(rtc.year as u64, rtc.month as u64)
|
||||
+ 86400 * (rtc.day - 1) as u64
|
||||
+ 3600 * rtc.hour as u64
|
||||
+ 60 * rtc.minute as u64
|
||||
+ rtc.second as u64;
|
||||
|
||||
let fract = sys::time::time_between_ticks()
|
||||
* (sys::time::ticks() - sys::time::last_rtc_update()) as f64;
|
||||
|
||||
(timestamp as f64) + fract
|
||||
(ts as f64) + fract
|
||||
}
|
||||
|
||||
fn days_before_year(year: u64) -> u64 {
|
||||
(1970..year).fold(0, |days, y| {
|
||||
(1970..year).fold(0, |days, y|
|
||||
days + if is_leap_year(y) { 366 } else { 365 }
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
fn days_before_month(year: u64, month: u64) -> u64 {
|
||||
|
@ -134,7 +134,9 @@ fn is_leap_year(year: u64) -> bool {
|
|||
|
||||
pub fn init() {
|
||||
let s = realtime();
|
||||
let ns = Duration::nanoseconds(libm::floor(1e9 * (s - libm::floor(s))) as i64);
|
||||
let ns = Duration::nanoseconds(
|
||||
libm::floor(1e9 * (s - libm::floor(s))) as i64
|
||||
);
|
||||
let dt = OffsetDateTime::from_unix_timestamp(s as i64) + ns;
|
||||
let rtc = dt.format(DATE_TIME_ZONE);
|
||||
log!("RTC {}\n", rtc);
|
||||
|
|
|
@ -57,8 +57,10 @@ impl RTC {
|
|||
impl FileIO for RTC {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()> {
|
||||
self.sync();
|
||||
let date = Date::try_from_ymd(self.year.into(), self.month, self.day).map_err(|_| ())?;
|
||||
let date_time = date.try_with_hms(self.hour, self.minute, self.second).map_err(|_| ())?;
|
||||
let date = Date::try_from_ymd(self.year.into(), self.month, self.day).
|
||||
map_err(|_| ())?;
|
||||
let date_time = date.try_with_hms(self.hour, self.minute, self.second).
|
||||
map_err(|_| ())?;
|
||||
let out = date_time.format(DATE_TIME);
|
||||
buf.copy_from_slice(out.as_bytes());
|
||||
Ok(out.len())
|
||||
|
@ -84,8 +86,7 @@ impl FileIO for RTC {
|
|||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
}
|
||||
fn close(&mut self) {}
|
||||
|
||||
fn poll(&mut self, event: IO) -> bool {
|
||||
match event {
|
||||
|
@ -134,16 +135,19 @@ impl CMOS {
|
|||
|
||||
let b = self.read_register(Register::B);
|
||||
|
||||
if b & 0x04 == 0 { // BCD Mode
|
||||
// BCD Mode
|
||||
if b & 0x04 == 0 {
|
||||
rtc.second = (rtc.second & 0x0F) + ((rtc.second / 16) * 10);
|
||||
rtc.minute = (rtc.minute & 0x0F) + ((rtc.minute / 16) * 10);
|
||||
rtc.hour = ((rtc.hour & 0x0F) + (((rtc.hour & 0x70) / 16) * 10)) | (rtc.hour & 0x80);
|
||||
rtc.hour = ((rtc.hour & 0x0F) + (((rtc.hour & 0x70) / 16) * 10))
|
||||
| (rtc.hour & 0x80);
|
||||
rtc.day = (rtc.day & 0x0F) + ((rtc.day / 16) * 10);
|
||||
rtc.month = (rtc.month & 0x0F) + ((rtc.month / 16) * 10);
|
||||
rtc.year = (rtc.year & 0x0F) + ((rtc.year / 16) * 10);
|
||||
}
|
||||
|
||||
if (b & 0x02 == 0) && (rtc.hour & 0x80 == 0) { // 12 hour format
|
||||
// 12 hour format
|
||||
if (b & 0x02 == 0) && (rtc.hour & 0x80 == 0) {
|
||||
rtc.hour = ((rtc.hour & 0x7F) + 12) % 24;
|
||||
}
|
||||
|
||||
|
@ -165,7 +169,8 @@ impl CMOS {
|
|||
|
||||
let b = self.read_register(Register::B);
|
||||
|
||||
if b & 0x02 == 0 { // 12 hour format
|
||||
// 12 hour format
|
||||
if b & 0x02 == 0 {
|
||||
if hour == 0 {
|
||||
hour = 24;
|
||||
}
|
||||
|
@ -175,7 +180,8 @@ impl CMOS {
|
|||
}
|
||||
}
|
||||
|
||||
if b & 0x04 == 0 { // BCD Mode
|
||||
// BCD Mode
|
||||
if b & 0x04 == 0 {
|
||||
second = 16 * (second / 10) + (second % 10);
|
||||
minute = 16 * (minute / 10) + (minute % 10);
|
||||
hour = 16 * (hour / 10) + (hour % 10);
|
||||
|
|
|
@ -11,6 +11,11 @@ pub static STDIN: Mutex<String> = Mutex::new(String::new());
|
|||
pub static ECHO: AtomicBool = AtomicBool::new(true);
|
||||
pub static RAW: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub const BS_KEY: char = '\x08'; // Backspace
|
||||
pub const EOT_KEY: char = '\x04'; // End of Transmission
|
||||
pub const ESC_KEY: char = '\x1B'; // Escape
|
||||
pub const ETX_KEY: char = '\x03'; // End of Text
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Console;
|
||||
|
||||
|
@ -44,8 +49,7 @@ impl FileIO for Console {
|
|||
Ok(n)
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
}
|
||||
fn close(&mut self) {}
|
||||
|
||||
fn poll(&mut self, event: IO) -> bool {
|
||||
match event {
|
||||
|
@ -99,11 +103,6 @@ pub fn is_raw_enabled() -> bool {
|
|||
RAW.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub const ETX_KEY: char = '\x03'; // End of Text
|
||||
pub const EOT_KEY: char = '\x04'; // End of Transmission
|
||||
pub const BS_KEY: char = '\x08'; // Backspace
|
||||
pub const ESC_KEY: char = '\x1B'; // Escape
|
||||
|
||||
pub fn key_handle(key: char) {
|
||||
let mut stdin = STDIN.lock();
|
||||
|
||||
|
@ -113,41 +112,45 @@ pub fn key_handle(key: char) {
|
|||
if is_echo_enabled() {
|
||||
let n = match c {
|
||||
ETX_KEY | EOT_KEY | ESC_KEY => 2,
|
||||
_ => if (c as u32) < 0xFF { 1 } else { c.len_utf8() },
|
||||
_ => {
|
||||
if (c as u32) < 0xFF {
|
||||
1
|
||||
} else {
|
||||
c.len_utf8()
|
||||
}
|
||||
}
|
||||
};
|
||||
print_fmt(format_args!("{}", BS_KEY.to_string().repeat(n)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let key = if (key as u32) < 0xFF { (key as u8) as char } else { key };
|
||||
let key = if (key as u32) < 0xFF {
|
||||
(key as u8) as char
|
||||
} else {
|
||||
key
|
||||
};
|
||||
stdin.push(key);
|
||||
if is_echo_enabled() {
|
||||
match key {
|
||||
ETX_KEY => print_fmt(format_args!("^C")),
|
||||
EOT_KEY => print_fmt(format_args!("^D")),
|
||||
ESC_KEY => print_fmt(format_args!("^[")),
|
||||
_ => print_fmt(format_args!("{}", key)),
|
||||
_ => print_fmt(format_args!("{}", key)),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end_of_text() -> bool {
|
||||
interrupts::without_interrupts(|| {
|
||||
STDIN.lock().contains(ETX_KEY)
|
||||
})
|
||||
interrupts::without_interrupts(|| STDIN.lock().contains(ETX_KEY))
|
||||
}
|
||||
|
||||
pub fn end_of_transmission() -> bool {
|
||||
interrupts::without_interrupts(|| {
|
||||
STDIN.lock().contains(EOT_KEY)
|
||||
})
|
||||
interrupts::without_interrupts(|| STDIN.lock().contains(EOT_KEY))
|
||||
}
|
||||
|
||||
pub fn drain() {
|
||||
interrupts::without_interrupts(|| {
|
||||
STDIN.lock().clear();
|
||||
})
|
||||
interrupts::without_interrupts(|| STDIN.lock().clear())
|
||||
}
|
||||
|
||||
pub fn read_char() -> char {
|
||||
|
@ -182,9 +185,7 @@ pub fn read_line() -> String {
|
|||
stdin.clear();
|
||||
Some(line)
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
});
|
||||
if let Some(line) = res {
|
||||
|
|
|
@ -11,8 +11,8 @@ pub fn init() {
|
|||
log!("CPU {}\n", processor_brand_string.as_str().trim());
|
||||
}
|
||||
|
||||
if let Some(processor_frequency_info) = cpuid.get_processor_frequency_info() {
|
||||
let processor_base_frequency = processor_frequency_info.processor_base_frequency();
|
||||
log!("CPU {} MHz\n", processor_base_frequency);
|
||||
if let Some(info) = cpuid.get_processor_frequency_info() {
|
||||
let frequency = info.processor_base_frequency();
|
||||
log!("CPU {} MHz\n", frequency);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use super::bitmap_block::BitmapBlock;
|
||||
use super::block_device::BlockDeviceIO;
|
||||
use super::block_device::BLOCK_DEVICE;
|
||||
|
||||
use core::convert::TryInto;
|
||||
|
||||
|
@ -22,9 +23,7 @@ impl Block {
|
|||
|
||||
pub fn alloc() -> Option<Self> {
|
||||
match BitmapBlock::next_free_addr() {
|
||||
None => {
|
||||
None
|
||||
}
|
||||
None => None,
|
||||
Some(addr) => {
|
||||
BitmapBlock::alloc(addr);
|
||||
|
||||
|
@ -42,7 +41,7 @@ impl Block {
|
|||
|
||||
pub fn read(addr: u32) -> Self {
|
||||
let mut buf = [0; super::BLOCK_SIZE];
|
||||
if let Some(ref mut block_device) = *super::block_device::BLOCK_DEVICE.lock() {
|
||||
if let Some(ref mut block_device) = *BLOCK_DEVICE.lock() {
|
||||
if block_device.read(addr, &mut buf).is_err() {
|
||||
debug!("MFS: could not read block {:#X}", addr);
|
||||
}
|
||||
|
@ -51,7 +50,7 @@ impl Block {
|
|||
}
|
||||
|
||||
pub fn write(&self) {
|
||||
if let Some(ref mut block_device) = *super::block_device::BLOCK_DEVICE.lock() {
|
||||
if let Some(ref mut block_device) = *BLOCK_DEVICE.lock() {
|
||||
if block_device.write(self.addr, &self.buf).is_err() {
|
||||
debug!("MFS: could not write block {:#X}", self.addr);
|
||||
}
|
||||
|
@ -78,12 +77,14 @@ impl Block {
|
|||
}
|
||||
|
||||
pub struct LinkedBlock {
|
||||
block: Block
|
||||
block: Block,
|
||||
}
|
||||
|
||||
impl LinkedBlock {
|
||||
pub fn new(addr: u32) -> Self {
|
||||
Self { block: Block::new(addr) }
|
||||
Self {
|
||||
block: Block::new(addr),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alloc() -> Option<Self> {
|
||||
|
@ -91,7 +92,9 @@ impl LinkedBlock {
|
|||
}
|
||||
|
||||
pub fn read(addr: u32) -> Self {
|
||||
Self { block: Block::read(addr) }
|
||||
Self {
|
||||
block: Block::read(addr),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&self) {
|
||||
|
|
|
@ -61,12 +61,6 @@ impl MemBlockDevice {
|
|||
let dev = vec![[0; super::BLOCK_SIZE]; len];
|
||||
Self { dev }
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn len(&self) -> usize {
|
||||
self.dev.len()
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
impl BlockDeviceIO for MemBlockDevice {
|
||||
|
@ -112,7 +106,7 @@ const ATA_CACHE_SIZE: usize = 1024;
|
|||
#[derive(Clone)]
|
||||
pub struct AtaBlockDevice {
|
||||
cache: [Option<(u32, Vec<u8>)>; ATA_CACHE_SIZE],
|
||||
dev: sys::ata::Drive
|
||||
dev: sys::ata::Drive,
|
||||
}
|
||||
|
||||
impl AtaBlockDevice {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use super::{dirname, filename, realpath, FileIO, IO};
|
||||
use super::block::LinkedBlock;
|
||||
use super::dir::Dir;
|
||||
use super::file::File;
|
||||
use super::block::LinkedBlock;
|
||||
use super::{dirname, filename, realpath, FileIO, IO};
|
||||
|
||||
use crate::sys::clock::{Realtime, Uptime};
|
||||
use crate::sys::cmos::RTC;
|
||||
use crate::sys::console::Console;
|
||||
use crate::sys::random::Random;
|
||||
use crate::sys::clock::{Uptime, Realtime};
|
||||
use crate::sys::net::socket::tcp::TcpSocket;
|
||||
use crate::sys::net::socket::udp::UdpSocket;
|
||||
use crate::sys::random::Random;
|
||||
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
@ -37,7 +37,7 @@ impl DeviceType {
|
|||
DeviceType::Console => Console::size(),
|
||||
DeviceType::TcpSocket => TcpSocket::size(),
|
||||
DeviceType::UdpSocket => UdpSocket::size(),
|
||||
_ => 1,
|
||||
_ => 1,
|
||||
};
|
||||
let mut res = vec![0; len];
|
||||
res[0] = self as u8;
|
||||
|
@ -61,15 +61,33 @@ pub enum Device {
|
|||
impl From<u8> for Device {
|
||||
fn from(i: u8) -> Self {
|
||||
match i {
|
||||
i if i == DeviceType::Null as u8 => Device::Null,
|
||||
i if i == DeviceType::File as u8 => Device::File(File::new()),
|
||||
i if i == DeviceType::Console as u8 => Device::Console(Console::new()),
|
||||
i if i == DeviceType::Random as u8 => Device::Random(Random::new()),
|
||||
i if i == DeviceType::Uptime as u8 => Device::Uptime(Uptime::new()),
|
||||
i if i == DeviceType::Realtime as u8 => Device::Realtime(Realtime::new()),
|
||||
i if i == DeviceType::RTC as u8 => Device::RTC(RTC::new()),
|
||||
i if i == DeviceType::TcpSocket as u8 => Device::TcpSocket(TcpSocket::new()),
|
||||
i if i == DeviceType::UdpSocket as u8 => Device::UdpSocket(UdpSocket::new()),
|
||||
i if i == DeviceType::Null as u8 => {
|
||||
Device::Null
|
||||
}
|
||||
i if i == DeviceType::File as u8 => {
|
||||
Device::File(File::new())
|
||||
}
|
||||
i if i == DeviceType::Console as u8 => {
|
||||
Device::Console(Console::new())
|
||||
}
|
||||
i if i == DeviceType::Random as u8 => {
|
||||
Device::Random(Random::new())
|
||||
}
|
||||
i if i == DeviceType::Uptime as u8 => {
|
||||
Device::Uptime(Uptime::new())
|
||||
}
|
||||
i if i == DeviceType::Realtime as u8 => {
|
||||
Device::Realtime(Realtime::new())
|
||||
}
|
||||
i if i == DeviceType::RTC as u8 => {
|
||||
Device::RTC(RTC::new())
|
||||
}
|
||||
i if i == DeviceType::TcpSocket as u8 => {
|
||||
Device::TcpSocket(TcpSocket::new())
|
||||
}
|
||||
i if i == DeviceType::UdpSocket as u8 => {
|
||||
Device::UdpSocket(UdpSocket::new())
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +100,7 @@ impl Device {
|
|||
let filename = filename(&pathname);
|
||||
if let Some(mut dir) = Dir::open(dirname) {
|
||||
if let Some(dir_entry) = dir.create_device(filename) {
|
||||
return Some(Device::File(dir_entry.into()))
|
||||
return Some(Device::File(dir_entry.into()));
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -138,7 +156,7 @@ impl FileIO for Device {
|
|||
|
||||
fn close(&mut self) {
|
||||
match self {
|
||||
Device::Null => {},
|
||||
Device::Null => {}
|
||||
Device::File(io) => io.close(),
|
||||
Device::Console(io) => io.close(),
|
||||
Device::Random(io) => io.close(),
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use super::{dirname, filename, realpath, FileIO, IO};
|
||||
use super::super_block::SuperBlock;
|
||||
use super::bitmap_block::BitmapBlock;
|
||||
use super::block::LinkedBlock;
|
||||
use super::dir_entry::DirEntry;
|
||||
use super::read_dir::ReadDir;
|
||||
use super::bitmap_block::BitmapBlock;
|
||||
use super::super_block::SuperBlock;
|
||||
use super::FileType;
|
||||
use super::block::LinkedBlock;
|
||||
use super::{dirname, filename, realpath, FileIO, IO};
|
||||
use crate::sys;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
@ -22,7 +22,13 @@ pub struct Dir {
|
|||
|
||||
impl From<DirEntry> for Dir {
|
||||
fn from(entry: DirEntry) -> Self {
|
||||
Self { parent: Some(Box::new(entry.dir())), name: entry.name(), addr: entry.addr(), size: entry.size(), entry_index: 0 }
|
||||
Self {
|
||||
parent: Some(Box::new(entry.dir())),
|
||||
name: entry.name(),
|
||||
addr: entry.addr(),
|
||||
size: entry.size(),
|
||||
entry_index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +36,13 @@ impl Dir {
|
|||
pub fn root() -> Self {
|
||||
let name = String::new();
|
||||
let addr = SuperBlock::read().data_area();
|
||||
let mut root = Self { parent: None, name, addr, size: 0, entry_index: 0 };
|
||||
let mut root = Self {
|
||||
parent: None,
|
||||
name,
|
||||
addr,
|
||||
size: 0,
|
||||
entry_index: 0,
|
||||
};
|
||||
root.update_size();
|
||||
root
|
||||
}
|
||||
|
@ -71,10 +83,8 @@ impl Dir {
|
|||
} else {
|
||||
return None;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return None
|
||||
},
|
||||
}
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
Some(dir)
|
||||
|
@ -110,7 +120,8 @@ impl Dir {
|
|||
let mut entries = self.entries();
|
||||
while entries.next().is_some() {}
|
||||
|
||||
// Allocate a new block for the dir if no space left for adding the new entry
|
||||
// Allocate a new block for the dir if no space left for adding
|
||||
// the new entry.
|
||||
let space_left = entries.block.data().len() - entries.block_offset();
|
||||
let entry_len = DirEntry::empty_len() + name.len();
|
||||
if entry_len > space_left {
|
||||
|
@ -119,7 +130,7 @@ impl Dir {
|
|||
Some(new_block) => {
|
||||
entries.block = new_block;
|
||||
entries.block_offset = 0;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,7 +155,14 @@ impl Dir {
|
|||
entries.block.write();
|
||||
self.update_size();
|
||||
|
||||
Some(DirEntry::new(self.clone(), kind, entry_addr, entry_size, entry_time, &entry_name))
|
||||
Some(DirEntry::new(
|
||||
self.clone(),
|
||||
kind,
|
||||
entry_addr,
|
||||
entry_size,
|
||||
entry_time,
|
||||
&entry_name,
|
||||
))
|
||||
}
|
||||
|
||||
// Deleting an entry is done by setting the entry address to 0
|
||||
|
@ -244,8 +262,7 @@ impl FileIO for Dir {
|
|||
Err(())
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
}
|
||||
fn close(&mut self) {}
|
||||
|
||||
fn poll(&mut self, event: IO) -> bool {
|
||||
match event {
|
||||
|
@ -255,7 +272,8 @@ impl FileIO for Dir {
|
|||
}
|
||||
}
|
||||
|
||||
// Truncate to the given number of bytes at most while respecting char boundaries
|
||||
// Truncate to the given number of bytes at most
|
||||
// while respecting char boundaries.
|
||||
fn truncate(s: &str, max: usize) -> String {
|
||||
s.char_indices().take_while(|(i, _)| *i <= max).map(|(_, c)| c).collect()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::{dirname, filename, realpath, FileType};
|
||||
use super::dir::Dir;
|
||||
use super::{dirname, filename, realpath, FileType};
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
|
@ -26,9 +26,23 @@ impl DirEntry {
|
|||
None
|
||||
}
|
||||
|
||||
pub fn new(dir: Dir, kind: FileType, addr: u32, size: u32, time: u64, name: &str) -> Self {
|
||||
pub fn new(
|
||||
dir: Dir,
|
||||
kind: FileType,
|
||||
addr: u32,
|
||||
size: u32,
|
||||
time: u64,
|
||||
name: &str
|
||||
) -> Self {
|
||||
let name = String::from(name);
|
||||
Self { dir, kind, addr, size, time, name }
|
||||
Self {
|
||||
dir,
|
||||
kind,
|
||||
addr,
|
||||
size,
|
||||
time,
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty_len() -> usize {
|
||||
|
@ -80,7 +94,12 @@ impl DirEntry {
|
|||
}
|
||||
|
||||
pub fn info(&self) -> FileInfo {
|
||||
FileInfo { kind: self.kind, name: self.name(), size: self.size(), time: self.time }
|
||||
FileInfo {
|
||||
kind: self.kind,
|
||||
name: self.name(),
|
||||
size: self.size(),
|
||||
time: self.time,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,7 +113,12 @@ pub struct FileInfo {
|
|||
|
||||
impl FileInfo {
|
||||
pub fn new() -> Self {
|
||||
Self { kind: FileType::File, name: String::new(), size: 0, time: 0 }
|
||||
Self {
|
||||
kind: FileType::File,
|
||||
name: String::new(),
|
||||
size: 0,
|
||||
time: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root() -> Self {
|
||||
|
@ -102,7 +126,12 @@ impl FileInfo {
|
|||
let name = String::new();
|
||||
let size = Dir::root().size() as u32;
|
||||
let time = 0;
|
||||
Self { kind, name, size, time }
|
||||
Self {
|
||||
kind,
|
||||
name,
|
||||
size,
|
||||
time,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u32 {
|
||||
|
@ -147,11 +176,12 @@ impl FileInfo {
|
|||
}
|
||||
}
|
||||
|
||||
use core::convert::TryInto;
|
||||
use core::convert::From;
|
||||
use core::convert::TryInto;
|
||||
impl From<&[u8]> for FileInfo {
|
||||
fn from(buf: &[u8]) -> Self {
|
||||
let kind = match buf[0] { // TODO: Add FileType::from(u8)
|
||||
let kind = match buf[0] {
|
||||
// TODO: Add FileType::from(u8)
|
||||
0 => FileType::Dir,
|
||||
1 => FileType::File,
|
||||
2 => FileType::Device,
|
||||
|
@ -161,6 +191,11 @@ impl From<&[u8]> for FileInfo {
|
|||
let time = u64::from_be_bytes(buf[5..13].try_into().unwrap());
|
||||
let i = 14 + buf[13] as usize;
|
||||
let name = String::from_utf8_lossy(&buf[14..i]).into();
|
||||
Self { kind, name, size, time }
|
||||
Self {
|
||||
kind,
|
||||
name,
|
||||
size,
|
||||
time,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::{dirname, filename, realpath, FileIO, IO};
|
||||
use super::dir::Dir;
|
||||
use super::block::LinkedBlock;
|
||||
use super::dir::Dir;
|
||||
use super::dir_entry::DirEntry;
|
||||
use super::{dirname, filename, realpath, FileIO, IO};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::{String, ToString};
|
||||
|
@ -42,7 +42,7 @@ impl File {
|
|||
name: String::new(),
|
||||
addr: 0,
|
||||
size: 0,
|
||||
offset:0,
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,13 +87,13 @@ impl File {
|
|||
SeekFrom::End(i) => i + self.size as i32,
|
||||
};
|
||||
if offset < 0 || offset > self.size as i32 { // TODO: offset > size?
|
||||
return Err(())
|
||||
return Err(());
|
||||
}
|
||||
self.offset = offset as u32;
|
||||
|
||||
Ok(self.offset)
|
||||
}
|
||||
// TODO: add `read_to_end(&self, buf: &mut Vec<u8>) -> Result<u32>`
|
||||
// TODO: Add `read_to_end(&self, buf: &mut Vec<u8>) -> Result<u32>`
|
||||
|
||||
// TODO: `return Result<String>`
|
||||
pub fn read_to_string(&mut self) -> String {
|
||||
|
@ -200,8 +200,7 @@ impl FileIO for File {
|
|||
Ok(bytes)
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
}
|
||||
fn close(&mut self) {}
|
||||
|
||||
fn poll(&mut self, event: IO) -> bool {
|
||||
match event {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
mod block;
|
||||
mod bitmap_block;
|
||||
mod block;
|
||||
mod block_device;
|
||||
mod device;
|
||||
mod dir;
|
||||
|
@ -10,14 +10,16 @@ mod super_block;
|
|||
|
||||
use crate::sys;
|
||||
|
||||
pub use crate::api::fs::{dirname, filename, realpath, FileIO, IO};
|
||||
pub use crate::sys::ata::BLOCK_SIZE;
|
||||
pub use bitmap_block::BITMAP_SIZE;
|
||||
pub use block_device::{
|
||||
dismount, format_ata, format_mem, is_mounted, mount_ata, mount_mem
|
||||
};
|
||||
pub use device::{Device, DeviceType};
|
||||
pub use dir::Dir;
|
||||
pub use dir_entry::FileInfo;
|
||||
pub use file::{File, SeekFrom};
|
||||
pub use block_device::{format_ata, format_mem, is_mounted, mount_ata, mount_mem, dismount};
|
||||
pub use crate::api::fs::{dirname, filename, realpath, FileIO, IO};
|
||||
pub use crate::sys::ata::BLOCK_SIZE;
|
||||
|
||||
use dir_entry::DirEntry;
|
||||
use super_block::SuperBlock;
|
||||
|
@ -149,10 +151,8 @@ pub fn canonicalize(path: &str) -> Result<String, ()> {
|
|||
} else {
|
||||
Ok(path.to_string())
|
||||
}
|
||||
},
|
||||
None => {
|
||||
Ok(path.to_string())
|
||||
}
|
||||
None => Ok(path.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::dir_entry::DirEntry;
|
||||
use super::block::LinkedBlock;
|
||||
use super::dir::Dir;
|
||||
use super::dir_entry::DirEntry;
|
||||
use super::FileType;
|
||||
|
||||
use alloc::string::String;
|
||||
|
@ -87,7 +87,7 @@ impl Iterator for ReadDir {
|
|||
_ => {
|
||||
self.block_offset = offset; // Rewind the cursor
|
||||
break;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
let entry_addr = self.read_u32();
|
||||
|
@ -109,7 +109,14 @@ impl Iterator for ReadDir {
|
|||
}
|
||||
|
||||
let dir = self.dir.clone();
|
||||
return Some(DirEntry::new(dir, entry_kind, entry_addr, entry_size, entry_time, &entry_name));
|
||||
return Some(DirEntry::new(
|
||||
dir,
|
||||
entry_kind,
|
||||
entry_addr,
|
||||
entry_size,
|
||||
entry_time,
|
||||
&entry_name,
|
||||
));
|
||||
}
|
||||
|
||||
match self.block.next() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::sys;
|
||||
use crate::KERNEL_SIZE;
|
||||
use super::block::Block;
|
||||
use super::block_device::BlockDeviceIO;
|
||||
use crate::sys;
|
||||
use crate::KERNEL_SIZE;
|
||||
use core::convert::TryInto;
|
||||
|
||||
const SUPERBLOCK_ADDR: u32 = (KERNEL_SIZE / super::BLOCK_SIZE) as u32;
|
||||
|
@ -9,7 +9,7 @@ const SIGNATURE: &[u8; 8] = b"MOROS FS";
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct SuperBlock {
|
||||
signature: &'static[u8; 8],
|
||||
signature: &'static [u8; 8],
|
||||
version: u8,
|
||||
block_size: u32,
|
||||
pub block_count: u32,
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
use lazy_static::lazy_static;
|
||||
use x86_64::VirtAddr;
|
||||
use x86_64::instructions::segmentation::{CS, DS, Segment};
|
||||
use x86_64::instructions::segmentation::{Segment, CS, DS};
|
||||
use x86_64::instructions::tables::load_tss;
|
||||
use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector};
|
||||
use x86_64::structures::gdt::{
|
||||
Descriptor, GlobalDescriptorTable, SegmentSelector
|
||||
};
|
||||
use x86_64::structures::tss::TaskStateSegment;
|
||||
use x86_64::VirtAddr;
|
||||
|
||||
const STACK_SIZE: usize = 1024 * 8;
|
||||
pub const DOUBLE_FAULT_IST_INDEX: u16 = 0;
|
||||
pub const PAGE_FAULT_IST_INDEX: u16 = 1;
|
||||
pub const GENERAL_PROTECTION_FAULT_IST_INDEX: u16 = 2;
|
||||
pub const DOUBLE_FAULT_IST: u16 = 0;
|
||||
pub const PAGE_FAULT_IST: u16 = 1;
|
||||
pub const GENERAL_PROTECTION_FAULT_IST: u16 = 2;
|
||||
|
||||
lazy_static! {
|
||||
static ref TSS: TaskStateSegment = {
|
||||
|
@ -17,15 +19,15 @@ lazy_static! {
|
|||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
VirtAddr::from_ptr(unsafe { &STACK }) + STACK_SIZE
|
||||
};
|
||||
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = {
|
||||
tss.interrupt_stack_table[DOUBLE_FAULT_IST as usize] = {
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
VirtAddr::from_ptr(unsafe { &STACK }) + STACK_SIZE
|
||||
};
|
||||
tss.interrupt_stack_table[PAGE_FAULT_IST_INDEX as usize] = {
|
||||
tss.interrupt_stack_table[PAGE_FAULT_IST as usize] = {
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
VirtAddr::from_ptr(unsafe { &STACK }) + STACK_SIZE
|
||||
};
|
||||
tss.interrupt_stack_table[GENERAL_PROTECTION_FAULT_IST_INDEX as usize] = {
|
||||
tss.interrupt_stack_table[GENERAL_PROTECTION_FAULT_IST as usize] = {
|
||||
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
|
||||
VirtAddr::from_ptr(unsafe { &STACK }) + STACK_SIZE
|
||||
};
|
||||
|
@ -43,7 +45,16 @@ lazy_static! {
|
|||
let user_code = gdt.add_entry(Descriptor::user_code_segment());
|
||||
let user_data = gdt.add_entry(Descriptor::user_data_segment());
|
||||
|
||||
(gdt, Selectors { tss, code, data, user_code, user_data })
|
||||
(
|
||||
gdt,
|
||||
Selectors {
|
||||
tss,
|
||||
code,
|
||||
data,
|
||||
user_code,
|
||||
user_data,
|
||||
},
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{api, sys, hlt_loop};
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::sys::process::Registers;
|
||||
use crate::{api, hlt_loop, sys};
|
||||
|
||||
use core::arch::asm;
|
||||
use lazy_static::lazy_static;
|
||||
|
@ -8,7 +8,10 @@ use spin::Mutex;
|
|||
use x86_64::instructions::interrupts;
|
||||
use x86_64::instructions::port::Port;
|
||||
use x86_64::registers::control::Cr2;
|
||||
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, InterruptStackFrameValue, PageFaultErrorCode};
|
||||
use x86_64::structures::idt::{
|
||||
InterruptDescriptorTable, InterruptStackFrame, InterruptStackFrameValue,
|
||||
PageFaultErrorCode,
|
||||
};
|
||||
use x86_64::structures::paging::OffsetPageTable;
|
||||
use x86_64::VirtAddr;
|
||||
|
||||
|
@ -24,10 +27,12 @@ fn interrupt_index(irq: u8) -> u8 {
|
|||
sys::pic::PIC_1_OFFSET + irq
|
||||
}
|
||||
|
||||
fn default_irq_handler() {}
|
||||
fn default_handler() {}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref IRQ_HANDLERS: Mutex<[fn(); 16]> = Mutex::new([default_irq_handler; 16]);
|
||||
static ref IRQ_HANDLERS: Mutex<[fn(); 16]> = {
|
||||
Mutex::new([default_handler; 16])
|
||||
};
|
||||
|
||||
static ref IDT: InterruptDescriptorTable = {
|
||||
let mut idt = InterruptDescriptorTable::new();
|
||||
|
@ -37,16 +42,17 @@ lazy_static! {
|
|||
unsafe {
|
||||
idt.double_fault.
|
||||
set_handler_fn(double_fault_handler).
|
||||
set_stack_index(sys::gdt::DOUBLE_FAULT_IST_INDEX);
|
||||
set_stack_index(sys::gdt::DOUBLE_FAULT_IST);
|
||||
idt.page_fault.
|
||||
set_handler_fn(page_fault_handler).
|
||||
set_stack_index(sys::gdt::PAGE_FAULT_IST_INDEX);
|
||||
set_stack_index(sys::gdt::PAGE_FAULT_IST);
|
||||
idt.general_protection_fault.
|
||||
set_handler_fn(general_protection_fault_handler).
|
||||
set_stack_index(sys::gdt::GENERAL_PROTECTION_FAULT_IST_INDEX);
|
||||
set_stack_index(sys::gdt::GENERAL_PROTECTION_FAULT_IST);
|
||||
|
||||
let f = wrapped_syscall_handler as *mut fn();
|
||||
idt[0x80].
|
||||
set_handler_fn(core::mem::transmute(wrapped_syscall_handler as *mut fn())).
|
||||
//set_stack_index(sys::gdt::GENERAL_PROTECTION_FAULT_IST_INDEX).
|
||||
set_handler_fn(core::mem::transmute(f)).
|
||||
set_privilege_level(x86_64::PrivilegeLevel::Ring3);
|
||||
}
|
||||
idt[interrupt_index(0) as usize].set_handler_fn(irq0_handler);
|
||||
|
@ -71,10 +77,14 @@ lazy_static! {
|
|||
|
||||
macro_rules! irq_handler {
|
||||
($handler:ident, $irq:expr) => {
|
||||
pub extern "x86-interrupt" fn $handler(_stack_frame: InterruptStackFrame) {
|
||||
pub extern "x86-interrupt" fn $handler(_: InterruptStackFrame) {
|
||||
let handlers = IRQ_HANDLERS.lock();
|
||||
handlers[$irq]();
|
||||
unsafe { sys::pic::PICS.lock().notify_end_of_interrupt(interrupt_index($irq)); }
|
||||
unsafe {
|
||||
sys::pic::PICS.lock().notify_end_of_interrupt(
|
||||
interrupt_index($irq)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -102,25 +112,36 @@ extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
|
|||
panic!();
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn double_fault_handler(stack_frame: InterruptStackFrame, error_code: u64) -> ! {
|
||||
extern "x86-interrupt" fn double_fault_handler(
|
||||
stack_frame: InterruptStackFrame,
|
||||
error_code: u64,
|
||||
) -> ! {
|
||||
debug!("EXCEPTION: DOUBLE FAULT");
|
||||
debug!("Stack Frame: {:#?}", stack_frame);
|
||||
debug!("Error: {:?}", error_code);
|
||||
panic!();
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn page_fault_handler(_stack_frame: InterruptStackFrame, error_code: PageFaultErrorCode) {
|
||||
extern "x86-interrupt" fn page_fault_handler(
|
||||
_stack_frame: InterruptStackFrame,
|
||||
error_code: PageFaultErrorCode,
|
||||
) {
|
||||
//debug!("EXCEPTION: PAGE FAULT ({:?})", error_code);
|
||||
let addr = Cr2::read().as_u64();
|
||||
|
||||
let page_table = unsafe { sys::process::page_table() };
|
||||
let phys_mem_offset = unsafe { sys::mem::PHYS_MEM_OFFSET.unwrap() };
|
||||
let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset)) };
|
||||
let mut mapper = unsafe {
|
||||
OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset))
|
||||
};
|
||||
|
||||
if sys::allocator::alloc_pages(&mut mapper, addr, 1).is_err() {
|
||||
let csi_color = api::console::Style::color("LightRed");
|
||||
let csi_reset = api::console::Style::reset();
|
||||
printk!("{}Error:{} Could not allocate address {:#X}\n", csi_color, csi_reset, addr);
|
||||
printk!(
|
||||
"{}Error:{} Could not allocate address {:#X}\n",
|
||||
csi_color, csi_reset, addr
|
||||
);
|
||||
if error_code.contains(PageFaultErrorCode::USER_MODE) {
|
||||
api::syscall::exit(ExitCode::PageFaultError);
|
||||
} else {
|
||||
|
@ -129,21 +150,30 @@ extern "x86-interrupt" fn page_fault_handler(_stack_frame: InterruptStackFrame,
|
|||
}
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn general_protection_fault_handler(stack_frame: InterruptStackFrame, error_code: u64) {
|
||||
extern "x86-interrupt" fn general_protection_fault_handler(
|
||||
stack_frame: InterruptStackFrame,
|
||||
error_code: u64,
|
||||
) {
|
||||
debug!("EXCEPTION: GENERAL PROTECTION FAULT");
|
||||
debug!("Stack Frame: {:#?}", stack_frame);
|
||||
debug!("Error: {:?}", error_code);
|
||||
panic!();
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn stack_segment_fault_handler(stack_frame: InterruptStackFrame, error_code: u64) {
|
||||
extern "x86-interrupt" fn stack_segment_fault_handler(
|
||||
stack_frame: InterruptStackFrame,
|
||||
error_code: u64,
|
||||
) {
|
||||
debug!("EXCEPTION: STACK SEGMENT FAULT");
|
||||
debug!("Stack Frame: {:#?}", stack_frame);
|
||||
debug!("Error: {:?}", error_code);
|
||||
panic!();
|
||||
}
|
||||
|
||||
extern "x86-interrupt" fn segment_not_present_handler(stack_frame: InterruptStackFrame, error_code: u64) {
|
||||
extern "x86-interrupt" fn segment_not_present_handler(
|
||||
stack_frame: InterruptStackFrame,
|
||||
error_code: u64,
|
||||
) {
|
||||
debug!("EXCEPTION: SEGMENT NOT PRESENT");
|
||||
debug!("Stack Frame: {:#?}", stack_frame);
|
||||
debug!("Error: {:?}", error_code);
|
||||
|
@ -151,7 +181,7 @@ extern "x86-interrupt" fn segment_not_present_handler(stack_frame: InterruptStac
|
|||
}
|
||||
|
||||
// Naked function wrapper saving all scratch registers to the stack
|
||||
// See: https://os.phil-opp.com/returning-from-exceptions/#a-naked-wrapper-function
|
||||
// See: https://os.phil-opp.com/returning-from-exceptions/
|
||||
macro_rules! wrap {
|
||||
($fn: ident => $w:ident) => {
|
||||
#[naked]
|
||||
|
@ -192,26 +222,36 @@ wrap!(syscall_handler => wrapped_syscall_handler);
|
|||
// NOTE: We can't use "x86-interrupt" for syscall_handler because we need to
|
||||
// return a result in the RAX register and it will be overwritten when the
|
||||
// context of the caller is restored.
|
||||
extern "sysv64" fn syscall_handler(stack_frame: &mut InterruptStackFrame, regs: &mut Registers) {
|
||||
extern "sysv64" fn syscall_handler(
|
||||
stack_frame: &mut InterruptStackFrame,
|
||||
regs: &mut Registers
|
||||
) {
|
||||
let n = regs.rax;
|
||||
|
||||
// The registers order follow the System V ABI convention
|
||||
let n = regs.rax;
|
||||
let arg1 = regs.rdi;
|
||||
let arg2 = regs.rsi;
|
||||
let arg3 = regs.rdx;
|
||||
let arg4 = regs.r8;
|
||||
|
||||
if n == sys::syscall::number::SPAWN { // Backup CPU context
|
||||
// Backup CPU context before spawning a process
|
||||
if n == sys::syscall::number::SPAWN {
|
||||
sys::process::set_stack_frame(**stack_frame);
|
||||
sys::process::set_registers(*regs);
|
||||
}
|
||||
|
||||
let res = sys::syscall::dispatcher(n, arg1, arg2, arg3, arg4);
|
||||
|
||||
if n == sys::syscall::number::EXIT { // Restore CPU context
|
||||
// Restore CPU context before exiting a process
|
||||
if n == sys::syscall::number::EXIT {
|
||||
let sf = sys::process::stack_frame();
|
||||
unsafe {
|
||||
// FIXME: the following line should replace the next ones
|
||||
//stack_frame.as_mut().write(sf);
|
||||
core::ptr::write_volatile(stack_frame.as_mut().extract_inner() as *mut InterruptStackFrameValue, sf); // FIXME
|
||||
let inner = stack_frame.as_mut().extract_inner();
|
||||
let ptr = inner as *mut InterruptStackFrameValue;
|
||||
core::ptr::write_volatile(ptr, sf);
|
||||
|
||||
core::ptr::write_volatile(regs, sys::process::registers());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use crate::sys;
|
||||
use crate::api::syscall;
|
||||
use crate::sys;
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use pc_keyboard::{layouts, DecodedKey, Error, HandleControl, KeyState, KeyCode, KeyEvent, Keyboard, ScancodeSet1};
|
||||
use pc_keyboard::{
|
||||
layouts, DecodedKey, Error, HandleControl, KeyCode, KeyEvent, KeyState,
|
||||
Keyboard, ScancodeSet1,
|
||||
};
|
||||
use spin::Mutex;
|
||||
use x86_64::instructions::port::Port;
|
||||
|
||||
|
@ -21,25 +24,31 @@ pub enum KeyboardLayout {
|
|||
impl KeyboardLayout {
|
||||
fn add_byte(&mut self, scancode: u8) -> Result<Option<KeyEvent>, Error> {
|
||||
match self {
|
||||
KeyboardLayout::Azerty(keyboard) => keyboard.add_byte(scancode),
|
||||
KeyboardLayout::Dvorak(keyboard) => keyboard.add_byte(scancode),
|
||||
KeyboardLayout::Qwerty(keyboard) => keyboard.add_byte(scancode),
|
||||
KeyboardLayout::Azerty(kb) => kb.add_byte(scancode),
|
||||
KeyboardLayout::Dvorak(kb) => kb.add_byte(scancode),
|
||||
KeyboardLayout::Qwerty(kb) => kb.add_byte(scancode),
|
||||
}
|
||||
}
|
||||
|
||||
fn process_keyevent(&mut self, event: KeyEvent) -> Option<DecodedKey> {
|
||||
match self {
|
||||
KeyboardLayout::Azerty(keyboard) => keyboard.process_keyevent(event),
|
||||
KeyboardLayout::Dvorak(keyboard) => keyboard.process_keyevent(event),
|
||||
KeyboardLayout::Qwerty(keyboard) => keyboard.process_keyevent(event),
|
||||
KeyboardLayout::Azerty(kb) => kb.process_keyevent(event),
|
||||
KeyboardLayout::Dvorak(kb) => kb.process_keyevent(event),
|
||||
KeyboardLayout::Qwerty(kb) => kb.process_keyevent(event),
|
||||
}
|
||||
}
|
||||
|
||||
fn from(name: &str) -> Option<Self> {
|
||||
match name {
|
||||
"azerty" => Some(KeyboardLayout::Azerty(Keyboard::new(HandleControl::MapLettersToUnicode))),
|
||||
"dvorak" => Some(KeyboardLayout::Dvorak(Keyboard::new(HandleControl::MapLettersToUnicode))),
|
||||
"qwerty" => Some(KeyboardLayout::Qwerty(Keyboard::new(HandleControl::MapLettersToUnicode))),
|
||||
"azerty" => Some(KeyboardLayout::Azerty(Keyboard::new(
|
||||
HandleControl::MapLettersToUnicode,
|
||||
))),
|
||||
"dvorak" => Some(KeyboardLayout::Dvorak(Keyboard::new(
|
||||
HandleControl::MapLettersToUnicode,
|
||||
))),
|
||||
"qwerty" => Some(KeyboardLayout::Qwerty(Keyboard::new(
|
||||
HandleControl::MapLettersToUnicode,
|
||||
))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -82,9 +91,15 @@ fn interrupt_handler() {
|
|||
if let Ok(Some(event)) = keyboard.add_byte(scancode) {
|
||||
let ord = Ordering::Relaxed;
|
||||
match event.code {
|
||||
KeyCode::AltLeft | KeyCode::AltRight => ALT.store(event.state == KeyState::Down, ord),
|
||||
KeyCode::ShiftLeft | KeyCode::ShiftRight => SHIFT.store(event.state == KeyState::Down, ord),
|
||||
KeyCode::ControlLeft | KeyCode::ControlRight => CTRL.store(event.state == KeyState::Down, ord),
|
||||
KeyCode::AltLeft | KeyCode::AltRight => {
|
||||
ALT.store(event.state == KeyState::Down, ord)
|
||||
}
|
||||
KeyCode::ShiftLeft | KeyCode::ShiftRight => {
|
||||
SHIFT.store(event.state == KeyState::Down, ord)
|
||||
}
|
||||
KeyCode::ControlLeft | KeyCode::ControlRight => {
|
||||
CTRL.store(event.state == KeyState::Down, ord)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let is_alt = ALT.load(ord);
|
||||
|
@ -92,16 +107,24 @@ fn interrupt_handler() {
|
|||
let is_shift = SHIFT.load(ord);
|
||||
if let Some(key) = keyboard.process_keyevent(event) {
|
||||
match key {
|
||||
DecodedKey::Unicode('\u{7f}') if is_alt && is_ctrl => syscall::reboot(), // Ctrl-Alt-Del
|
||||
DecodedKey::RawKey(KeyCode::PageUp) => send_csi("5~"),
|
||||
DecodedKey::RawKey(KeyCode::PageDown) => send_csi("6~"),
|
||||
DecodedKey::RawKey(KeyCode::ArrowUp) => send_csi("A"),
|
||||
DecodedKey::RawKey(KeyCode::ArrowDown) => send_csi("B"),
|
||||
// Ctrl-Alt-Del
|
||||
DecodedKey::Unicode('\u{7f}') if is_alt && is_ctrl => {
|
||||
syscall::reboot()
|
||||
}
|
||||
|
||||
DecodedKey::RawKey(KeyCode::PageUp) => send_csi("5~"),
|
||||
DecodedKey::RawKey(KeyCode::PageDown) => send_csi("6~"),
|
||||
DecodedKey::RawKey(KeyCode::ArrowUp) => send_csi("A"),
|
||||
DecodedKey::RawKey(KeyCode::ArrowDown) => send_csi("B"),
|
||||
DecodedKey::RawKey(KeyCode::ArrowRight) => send_csi("C"),
|
||||
DecodedKey::RawKey(KeyCode::ArrowLeft) => send_csi("D"),
|
||||
DecodedKey::Unicode('\t') if is_shift => send_csi("Z"), // Convert Shift-Tab into Backtab
|
||||
DecodedKey::Unicode(c) => send_key(c),
|
||||
_ => {},
|
||||
DecodedKey::RawKey(KeyCode::ArrowLeft) => send_csi("D"),
|
||||
|
||||
// Convert Shift-Tab into Backtab
|
||||
DecodedKey::Unicode('\t') if is_shift => send_csi("Z"),
|
||||
|
||||
DecodedKey::Unicode(c) => send_key(c),
|
||||
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@ use bootloader::bootinfo::{BootInfo, MemoryMap, MemoryRegionType};
|
|||
use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
|
||||
use x86_64::instructions::interrupts;
|
||||
use x86_64::registers::control::Cr3;
|
||||
use x86_64::structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB, Translate};
|
||||
use x86_64::structures::paging::{
|
||||
FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB, Translate,
|
||||
};
|
||||
use x86_64::{PhysAddr, VirtAddr};
|
||||
|
||||
pub static mut PHYS_MEM_OFFSET: Option<u64> = None;
|
||||
|
@ -21,7 +23,12 @@ pub fn init(boot_info: &'static BootInfo) {
|
|||
let start_addr = region.range.start_addr();
|
||||
let end_addr = region.range.end_addr();
|
||||
memory_size += end_addr - start_addr;
|
||||
log!("MEM [{:#016X}-{:#016X}] {:?}\n", start_addr, end_addr - 1, region.region_type);
|
||||
log!(
|
||||
"MEM [{:#016X}-{:#016X}] {:?}\n",
|
||||
start_addr,
|
||||
end_addr - 1,
|
||||
region.region_type
|
||||
);
|
||||
}
|
||||
log!("MEM {} KB\n", memory_size >> 10);
|
||||
MEMORY_SIZE.store(memory_size, Ordering::Relaxed);
|
||||
|
@ -30,7 +37,12 @@ pub fn init(boot_info: &'static BootInfo) {
|
|||
|
||||
unsafe { PHYS_MEM_OFFSET.replace(phys_mem_offset) };
|
||||
unsafe { MEMORY_MAP.replace(&boot_info.memory_map) };
|
||||
unsafe { MAPPER.replace(OffsetPageTable::new(active_page_table(), VirtAddr::new(phys_mem_offset))) };
|
||||
unsafe {
|
||||
MAPPER.replace(OffsetPageTable::new(
|
||||
active_page_table(),
|
||||
VirtAddr::new(phys_mem_offset),
|
||||
))
|
||||
};
|
||||
|
||||
sys::allocator::init_heap().expect("heap initialization failed");
|
||||
});
|
||||
|
@ -69,7 +81,7 @@ pub unsafe fn create_page_table(frame: PhysFrame) -> &'static mut PageTable {
|
|||
}
|
||||
|
||||
pub struct BootInfoFrameAllocator {
|
||||
memory_map: &'static MemoryMap
|
||||
memory_map: &'static MemoryMap,
|
||||
}
|
||||
|
||||
impl BootInfoFrameAllocator {
|
||||
|
@ -79,20 +91,26 @@ impl BootInfoFrameAllocator {
|
|||
|
||||
fn usable_frames(&self) -> impl Iterator<Item = PhysFrame> {
|
||||
let regions = self.memory_map.iter();
|
||||
let usable_regions = regions.filter(|r| r.region_type == MemoryRegionType::Usable);
|
||||
let addr_ranges = usable_regions.map(|r| r.range.start_addr()..r.range.end_addr());
|
||||
let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096));
|
||||
frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr)))
|
||||
let usable_regions = regions.filter(|r|
|
||||
r.region_type == MemoryRegionType::Usable
|
||||
);
|
||||
let addr_ranges = usable_regions.map(|r|
|
||||
r.range.start_addr()..r.range.end_addr()
|
||||
);
|
||||
let frame_addresses = addr_ranges.flat_map(|r|
|
||||
r.step_by(4096)
|
||||
);
|
||||
frame_addresses.map(|addr|
|
||||
PhysFrame::containing_address(PhysAddr::new(addr))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
|
||||
fn allocate_frame(&mut self) -> Option<PhysFrame> {
|
||||
let next = ALLOCATED_FRAMES.fetch_add(1, Ordering::SeqCst);
|
||||
//debug!("Allocate frame {} / {}", next, self.usable_frames().count());
|
||||
|
||||
// FIXME: creating an iterator for each allocation is very slow if
|
||||
// the heap is larger than a few megabytes.
|
||||
// FIXME: When the heap is larger than a few megabytes,
|
||||
// creating an iterator for each allocation become very slow.
|
||||
self.usable_frames().nth(next)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,9 @@ macro_rules! log {
|
|||
let uptime = $crate::sys::clock::uptime();
|
||||
let csi_color = $crate::api::console::Style::color("LightGreen");
|
||||
let csi_reset = $crate::api::console::Style::reset();
|
||||
$crate::sys::console::print_fmt(format_args!("{}[{:.6}]{} ", csi_color, uptime, csi_reset));
|
||||
$crate::sys::console::print_fmt(
|
||||
format_args!("{}[{:.6}]{} ", csi_color, uptime, csi_reset)
|
||||
);
|
||||
$crate::sys::console::print_fmt(format_args!($($arg)*));
|
||||
// TODO: Add newline
|
||||
}
|
||||
|
|
|
@ -93,7 +93,10 @@ impl<'a> smoltcp::phy::Device for EthernetDevice {
|
|||
caps
|
||||
}
|
||||
|
||||
fn receive(&mut self, _instant: smoltcp::time::Instant) -> Option<(Self::RxToken<'a>, Self::TxToken<'a>)> {
|
||||
fn receive(
|
||||
&mut self,
|
||||
_instant: smoltcp::time::Instant,
|
||||
) -> Option<(Self::RxToken<'a>, Self::TxToken<'a>)> {
|
||||
if let Some(buffer) = self.receive_packet() {
|
||||
if self.config().is_debug_enabled() {
|
||||
debug!("NET Packet Received");
|
||||
|
@ -101,15 +104,22 @@ impl<'a> smoltcp::phy::Device for EthernetDevice {
|
|||
}
|
||||
self.stats().rx_add(buffer.len() as u64);
|
||||
let rx = RxToken { buffer };
|
||||
let tx = TxToken { device: self.clone() };
|
||||
let tx = TxToken {
|
||||
device: self.clone(),
|
||||
};
|
||||
Some((rx, tx))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn transmit(&mut self, _instant: smoltcp::time::Instant) -> Option<Self::TxToken<'a>> {
|
||||
let tx = TxToken { device: self.clone() };
|
||||
fn transmit(
|
||||
&mut self,
|
||||
_instant: smoltcp::time::Instant
|
||||
) -> Option<Self::TxToken<'a>> {
|
||||
let tx = TxToken {
|
||||
device: self.clone(),
|
||||
};
|
||||
Some(tx)
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +130,10 @@ pub struct RxToken {
|
|||
}
|
||||
|
||||
impl smoltcp::phy::RxToken for RxToken {
|
||||
fn consume<R, F>(mut self, f: F) -> R where F: FnOnce(&mut [u8]) -> R {
|
||||
fn consume<R, F>(mut self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
f(&mut self.buffer)
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +143,10 @@ pub struct TxToken {
|
|||
device: EthernetDevice,
|
||||
}
|
||||
impl smoltcp::phy::TxToken for TxToken {
|
||||
fn consume<R, F>(mut self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R {
|
||||
fn consume<R, F>(mut self, len: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> R,
|
||||
{
|
||||
let config = self.device.config();
|
||||
let buf = self.device.next_tx_buffer(len);
|
||||
if config.is_debug_enabled() {
|
||||
|
@ -244,9 +260,15 @@ pub fn init() {
|
|||
}
|
||||
};
|
||||
if let Some(io_base) = find_pci_io_base(0x10EC, 0x8139) {
|
||||
add(EthernetDevice::RTL8139(nic::rtl8139::Device::new(io_base)), "RTL8139");
|
||||
add(
|
||||
EthernetDevice::RTL8139(nic::rtl8139::Device::new(io_base)),
|
||||
"RTL8139",
|
||||
);
|
||||
}
|
||||
if let Some(io_base) = find_pci_io_base(0x1022, 0x2000) {
|
||||
add(EthernetDevice::PCNET(nic::pcnet::Device::new(io_base)), "PCNET");
|
||||
add(
|
||||
EthernetDevice::PCNET(nic::pcnet::Device::new(io_base)),
|
||||
"PCNET",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
pub mod rtl8139;
|
||||
pub mod pcnet;
|
||||
pub mod rtl8139;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::sys;
|
||||
use crate::sys::allocator::PhysBuf;
|
||||
use crate::sys::net::{EthernetDeviceIO, Config, Stats};
|
||||
use crate::sys::net::{Config, EthernetDeviceIO, Stats};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
|
@ -26,14 +26,14 @@ const CSR0_IDON: usize = 8;
|
|||
//const CSR0_BABL: usize = 14;
|
||||
//const CSR0_ERR: usize = 0;
|
||||
|
||||
const DE_ENP: usize = 0;
|
||||
const DE_STP: usize = 1;
|
||||
const DE_ENP: usize = 0;
|
||||
const DE_STP: usize = 1;
|
||||
//const DE_BUFF: usize = 2;
|
||||
//const DE_CRC: usize = 3;
|
||||
//const DE_OFLO: usize = 4;
|
||||
//const DE_FRAM: usize = 5;
|
||||
//const DE_ERR: usize = 6;
|
||||
const DE_OWN: usize = 7;
|
||||
const DE_OWN: usize = 7;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Ports {
|
||||
|
@ -218,7 +218,9 @@ impl Device {
|
|||
let addr = init_struct.addr();
|
||||
self.ports.write_csr_32(1, addr.get_bits(0..16) as u32);
|
||||
self.ports.write_csr_32(2, addr.get_bits(16..32) as u32);
|
||||
assert!(self.ports.read_csr_32(0) == 0b000000100); // STOP
|
||||
|
||||
// STOP
|
||||
debug_assert!(self.ports.read_csr_32(0) == 0b000000100);
|
||||
|
||||
// self.ports.write_csr_32(4, 1 << 11); // Pad short ethernet packets
|
||||
|
||||
|
@ -229,15 +231,23 @@ impl Device {
|
|||
while !self.ports.read_csr_32(0).get_bit(CSR0_IDON) {
|
||||
sys::time::halt();
|
||||
}
|
||||
assert!(self.ports.read_csr_32(0) == 0b110000001); // IDON + INTR + INIT
|
||||
|
||||
// IDON + INTR + INIT
|
||||
debug_assert!(self.ports.read_csr_32(0) == 0b110000001);
|
||||
|
||||
// Start the card
|
||||
self.ports.write_csr_32(0, 1 << CSR0_STRT);
|
||||
assert!(self.ports.read_csr_32(0) == 0b110110011); // IDON + INTR + RXON + TXON + STRT + INIT
|
||||
|
||||
// IDON + INTR + RXON + TXON + STRT + INIT
|
||||
debug_assert!(self.ports.read_csr_32(0) == 0b110110011);
|
||||
}
|
||||
|
||||
fn init_descriptor_entry(&mut self, i: usize, is_rx: bool) {
|
||||
let des = if is_rx { &mut self.rx_des } else { &mut self.tx_des };
|
||||
let des = if is_rx {
|
||||
&mut self.rx_des
|
||||
} else {
|
||||
&mut self.tx_des
|
||||
};
|
||||
|
||||
// Set buffer address
|
||||
let addr = if is_rx {
|
||||
|
@ -277,47 +287,22 @@ impl EthernetDeviceIO for Device {
|
|||
let rmd1 = self.rx_des[rx_id * DE_LEN + 7];
|
||||
let end_of_packet = rmd1.get_bit(DE_ENP);
|
||||
|
||||
/*
|
||||
let start_of_packet = rmd1.get_bit(DE_STP);
|
||||
let error = rmd1.get_bit(DE_ERR);
|
||||
let buffer_error = rmd1.get_bit(DE_BUFF);
|
||||
let overflow_error = rmd1.get_bit(DE_OFLO) && !rmd1.get_bit(DE_ENP);
|
||||
let crc_error = rmd1.get_bit(DE_CRC) && !rmd1.get_bit(DE_OFLO) && rmd1.get_bit(DE_ENP);
|
||||
let framing_error = rmd1.get_bit(DE_FRAM) && !rmd1.get_bit(DE_OFLO) && rmd1.get_bit(DE_ENP);
|
||||
|
||||
if self.config.is_debug_enabled() {
|
||||
printk!("Flags: ");
|
||||
if start_of_packet {
|
||||
printk!("start_of_packet ");
|
||||
}
|
||||
if end_of_packet {
|
||||
printk!("end_of_packet ");
|
||||
}
|
||||
if error {
|
||||
if overflow_error {
|
||||
printk!("overflow_error ");
|
||||
}
|
||||
if framing_error {
|
||||
printk!("framing_error ");
|
||||
}
|
||||
if crc_error {
|
||||
printk!("crc_error ");
|
||||
}
|
||||
}
|
||||
printk!("\n");
|
||||
}
|
||||
*/
|
||||
|
||||
// Read packet size
|
||||
let packet_size = u16::from_le_bytes([
|
||||
self.rx_des[rx_id * DE_LEN + 8],
|
||||
self.rx_des[rx_id * DE_LEN + 9]
|
||||
self.rx_des[rx_id * DE_LEN + 9],
|
||||
]) as usize;
|
||||
|
||||
let n = if end_of_packet { packet_size } else { self.rx_buffers[rx_id].len() };
|
||||
let n = if end_of_packet {
|
||||
packet_size
|
||||
} else {
|
||||
self.rx_buffers[rx_id].len()
|
||||
};
|
||||
packet.extend(&self.rx_buffers[rx_id][0..n]);
|
||||
|
||||
self.rx_des[rx_id * DE_LEN + 7].set_bit(DE_OWN, true); // Give back ownership
|
||||
// Give back ownership
|
||||
self.rx_des[rx_id * DE_LEN + 7].set_bit(DE_OWN, true);
|
||||
|
||||
rx_id = (rx_id + 1) % RX_BUFFERS_COUNT;
|
||||
self.rx_id.store(rx_id, Ordering::Relaxed);
|
||||
|
||||
|
@ -336,14 +321,18 @@ impl EthernetDeviceIO for Device {
|
|||
fn transmit_packet(&mut self, len: usize) {
|
||||
let tx_id = self.tx_id.load(Ordering::SeqCst);
|
||||
|
||||
self.tx_des[tx_id * DE_LEN + 7].set_bit(DE_STP, true); // Set start of packet
|
||||
self.tx_des[tx_id * DE_LEN + 7].set_bit(DE_ENP, true); // Set end of packet
|
||||
// Set start and end of packet
|
||||
self.tx_des[tx_id * DE_LEN + 7].set_bit(DE_STP, true);
|
||||
self.tx_des[tx_id * DE_LEN + 7].set_bit(DE_ENP, true);
|
||||
|
||||
// Set buffer byte count (0..12 BCNT + 12..16 ONES)
|
||||
let bcnt = (0xF000 | (0x0FFF & (1 + !(len as u16)))).to_le_bytes();
|
||||
self.tx_des[tx_id * DE_LEN + 4] = bcnt[0];
|
||||
self.tx_des[tx_id * DE_LEN + 5] = bcnt[1];
|
||||
|
||||
// Give back ownership to the card
|
||||
self.tx_des[tx_id * DE_LEN + 7].set_bit(DE_OWN, true);
|
||||
|
||||
self.tx_id.store((tx_id + 1) % TX_BUFFERS_COUNT, Ordering::Relaxed);
|
||||
|
||||
if !is_buffer_owner(&self.tx_des, tx_id) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::sys::allocator::PhysBuf;
|
||||
use crate::sys::net::{EthernetDeviceIO, Config, Stats};
|
||||
use crate::sys::net::{Config, EthernetDeviceIO, Stats};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
|
@ -24,9 +24,9 @@ const TX_BUFFER_LEN: usize = 2048;
|
|||
const TX_BUFFERS_COUNT: usize = 4;
|
||||
const ROK: u16 = 0x01;
|
||||
|
||||
const CR_RST: u8 = 1 << 4; // Reset
|
||||
const CR_RE: u8 = 1 << 3; // Receiver Enable
|
||||
const CR_TE: u8 = 1 << 2; // Transmitter Enable
|
||||
const CR_RST: u8 = 1 << 4; // Reset
|
||||
const CR_RE: u8 = 1 << 3; // Receiver Enable
|
||||
const CR_TE: u8 = 1 << 2; // Transmitter Enable
|
||||
const CR_BUFE: u8 = 1 << 0; // Buffer Empty
|
||||
|
||||
// Rx Buffer Length
|
||||
|
@ -38,10 +38,10 @@ const RCR_RBLEN: u32 = (RX_BUFFER_IDX << 11) as u32;
|
|||
// of the buffer. So the buffer must have an additionnal 1500 bytes.
|
||||
const RCR_WRAP: u32 = 1 << 7;
|
||||
|
||||
const RCR_AB: u32 = 1 << 3; // Accept Broadcast packets
|
||||
const RCR_AM: u32 = 1 << 2; // Accept Multicast packets
|
||||
const RCR_APM: u32 = 1 << 1; // Accept Physical Match packets
|
||||
const RCR_AAP: u32 = 1 << 0; // Accept All Packets
|
||||
const RCR_AB: u32 = 1 << 3; // Accept Broadcast packets
|
||||
const RCR_AM: u32 = 1 << 2; // Accept Multicast packets
|
||||
const RCR_APM: u32 = 1 << 1; // Accept Physical Match packets
|
||||
const RCR_AAP: u32 = 1 << 0; // Accept All Packets
|
||||
|
||||
// Interframe Gap Time
|
||||
const TCR_IFG: u32 = 3 << 24;
|
||||
|
@ -68,23 +68,46 @@ const TCR_MXDMA2: u32 = 1 << 10;
|
|||
//const OWC: u32 = 1 << 29; // Out of Window Collision
|
||||
//const CDH: u32 = 1 << 28; // CD Heart Beat
|
||||
const TOK: u32 = 1 << 15; // Transmit OK
|
||||
//const TUN: u32 = 1 << 14; // Transmit FIFO Underrun
|
||||
//const TUN: u32 = 1 << 14; // Transmit FIFO Underrun
|
||||
const OWN: u32 = 1 << 13; // DMA operation completed
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Ports {
|
||||
pub mac: [Port<u8>; 6], // ID Registers (IDR0 ... IDR5)
|
||||
pub tx_cmds: [Port<u32>; TX_BUFFERS_COUNT], // Transmit Status of Descriptors (TSD0 .. TSD3)
|
||||
pub tx_addrs: [Port<u32>; TX_BUFFERS_COUNT], // Transmit Start Address of Descriptor0 (TSAD0 .. TSAD3)
|
||||
pub config1: Port<u8>, // Configuration Register 1 (CONFIG1)
|
||||
pub rx_addr: Port<u32>, // Receive (Rx) Buffer Start Address (RBSTART)
|
||||
pub capr: Port<u16>, // Current Address of Packet Read (CAPR)
|
||||
pub cba: Port<u16>, // Current Buffer Address (CBA)
|
||||
pub cmd: Port<u8>, // Command Register (CR)
|
||||
pub imr: Port<u16>, // Interrupt Mask Register (IMR)
|
||||
pub isr: Port<u16>, // Interrupt Status Register (ISR)
|
||||
pub tx_config: Port<u32>, // Transmit (Tx) Configuration Register (TCR)
|
||||
pub rx_config: Port<u32>, // Receive (Rx) Configuration Register (RCR)
|
||||
// ID Registers (IDR0 ... IDR5)
|
||||
pub mac: [Port<u8>; 6],
|
||||
|
||||
// Transmit Status of Descriptors (TSD0 .. TSD3)
|
||||
pub tx_cmds: [Port<u32>; TX_BUFFERS_COUNT],
|
||||
|
||||
// Transmit Start Address of Descriptor0 (TSAD0 .. TSAD3)
|
||||
pub tx_addrs: [Port<u32>; TX_BUFFERS_COUNT],
|
||||
|
||||
// Configuration Register 1 (CONFIG1)
|
||||
pub config1: Port<u8>,
|
||||
|
||||
// Receive (Rx) Buffer Start Address (RBSTART)
|
||||
pub rx_addr: Port<u32>,
|
||||
|
||||
// Current Address of Packet Read (CAPR)
|
||||
pub capr: Port<u16>,
|
||||
|
||||
// Current Buffer Address (CBA)
|
||||
pub cba: Port<u16>,
|
||||
|
||||
// Command Register (CR)
|
||||
pub cmd: Port<u8>,
|
||||
|
||||
// Interrupt Mask Register (IMR)
|
||||
pub imr: Port<u16>,
|
||||
|
||||
// Interrupt Status Register (ISR)
|
||||
pub isr: Port<u16>,
|
||||
|
||||
// Transmit (Tx) Configuration Register (TCR)
|
||||
pub tx_config: Port<u32>,
|
||||
|
||||
// Receive (Rx) Configuration Register (RCR)
|
||||
pub rx_config: Port<u32>,
|
||||
}
|
||||
|
||||
impl Ports {
|
||||
|
@ -159,7 +182,9 @@ impl Device {
|
|||
rx_buffer: PhysBuf::new(RX_BUFFER_LEN + RX_BUFFER_PAD + MTU),
|
||||
|
||||
rx_offset: 0,
|
||||
tx_buffers: [(); TX_BUFFERS_COUNT].map(|_| PhysBuf::new(TX_BUFFER_LEN)),
|
||||
tx_buffers: [(); TX_BUFFERS_COUNT].map(|_|
|
||||
PhysBuf::new(TX_BUFFER_LEN)
|
||||
),
|
||||
|
||||
// Before a transmission begin the id is incremented,
|
||||
// so the first transimission will start at 0.
|
||||
|
@ -206,10 +231,12 @@ impl Device {
|
|||
}
|
||||
|
||||
// Configure receive buffer (RCR)
|
||||
unsafe { self.ports.rx_config.write(RCR_RBLEN | RCR_WRAP | RCR_AB | RCR_AM | RCR_APM | RCR_AAP) }
|
||||
let flags = RCR_RBLEN | RCR_WRAP | RCR_AB | RCR_AM | RCR_APM | RCR_AAP;
|
||||
unsafe { self.ports.rx_config.write(flags) }
|
||||
|
||||
// Configure transmit buffer (TCR)
|
||||
unsafe { self.ports.tx_config.write(TCR_IFG | TCR_MXDMA1 | TCR_MXDMA2); }
|
||||
let flags = TCR_IFG | TCR_MXDMA1 | TCR_MXDMA2;
|
||||
unsafe { self.ports.tx_config.write(flags) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,27 +260,31 @@ impl EthernetDeviceIO for Device {
|
|||
return None;
|
||||
}
|
||||
|
||||
//let isr = unsafe { self.ports.isr.read() };
|
||||
let cba = unsafe { self.ports.cba.read() };
|
||||
|
||||
// CAPR starts at 65520 and with the pad it overflows to 0
|
||||
let capr = unsafe { self.ports.capr.read() };
|
||||
let offset = ((capr as usize) + RX_BUFFER_PAD) % (1 << 16);
|
||||
let header = u16::from_le_bytes(self.rx_buffer[(offset + 0)..(offset + 2)].try_into().unwrap());
|
||||
|
||||
let header = u16::from_le_bytes(
|
||||
self.rx_buffer[(offset + 0)..(offset + 2)].try_into().unwrap(),
|
||||
);
|
||||
|
||||
if header & ROK != ROK {
|
||||
unsafe { self.ports.capr.write((((cba as usize) % RX_BUFFER_LEN) - RX_BUFFER_PAD) as u16) };
|
||||
let capr = ((cba as usize) % RX_BUFFER_LEN) - RX_BUFFER_PAD;
|
||||
unsafe { self.ports.capr.write(capr as u16) }
|
||||
return None;
|
||||
}
|
||||
|
||||
let n = u16::from_le_bytes(self.rx_buffer[(offset + 2)..(offset + 4)].try_into().unwrap()) as usize;
|
||||
//let crc = u32::from_le_bytes(self.rx_buffer[(offset + n)..(offset + n + 4)].try_into().unwrap());
|
||||
let n = u16::from_le_bytes(
|
||||
self.rx_buffer[(offset + 2)..(offset + 4)].try_into().unwrap()
|
||||
) as usize;
|
||||
|
||||
// Update buffer read pointer
|
||||
self.rx_offset = (offset + n + 4 + 3) & !3;
|
||||
unsafe {
|
||||
self.ports.capr.write(((self.rx_offset % RX_BUFFER_LEN) - RX_BUFFER_PAD) as u16);
|
||||
}
|
||||
let capr = (self.rx_offset % RX_BUFFER_LEN) - RX_BUFFER_PAD;
|
||||
unsafe { self.ports.capr.write(capr as u16) }
|
||||
|
||||
//unsafe { self.ports.isr.write(0x1); }
|
||||
Some(self.rx_buffer[(offset + 4)..(offset + n)].to_vec())
|
||||
}
|
||||
|
||||
|
@ -296,7 +327,8 @@ pub fn interrupt_handler() {
|
|||
printk!("RTL8139 interrupt!\n");
|
||||
if let Some(mut guard) = sys::net::IFACE.try_lock() {
|
||||
if let Some(ref mut iface) = *guard {
|
||||
unsafe { iface.device_mut().ports.isr.write(0xFFFF) } // Clear the interrupt
|
||||
// Clear the interrupt
|
||||
unsafe { iface.device_mut().ports.isr.write(0xFFFF) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@ use smoltcp::time::Duration;
|
|||
use spin::Mutex;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref SOCKETS: Mutex<SocketSet<'static>> = Mutex::new(SocketSet::new(vec![]));
|
||||
pub static ref SOCKETS: Mutex<SocketSet<'static>> = {
|
||||
Mutex::new(SocketSet::new(vec![]))
|
||||
};
|
||||
}
|
||||
|
||||
fn random_port() -> u16 {
|
||||
|
|
|
@ -78,23 +78,18 @@ impl TcpSocket {
|
|||
}
|
||||
connecting = true;
|
||||
}
|
||||
tcp::State::SynSent => {
|
||||
}
|
||||
tcp::State::SynSent => {}
|
||||
tcp::State::Established => {
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
// Did something get sent before the connection closed?
|
||||
return if socket.can_recv() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
};
|
||||
return if socket.can_recv() { Ok(()) } else { Err(()) };
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(duration);
|
||||
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(d);
|
||||
}
|
||||
sys::time::halt();
|
||||
}
|
||||
|
@ -112,8 +107,8 @@ impl TcpSocket {
|
|||
return Err(());
|
||||
}
|
||||
|
||||
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(duration);
|
||||
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(d);
|
||||
}
|
||||
sys::time::halt();
|
||||
Ok(())
|
||||
|
@ -133,8 +128,8 @@ impl TcpSocket {
|
|||
return Ok(endpoint.addr);
|
||||
}
|
||||
|
||||
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(duration);
|
||||
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(d);
|
||||
}
|
||||
sys::time::halt();
|
||||
}
|
||||
|
@ -158,7 +153,8 @@ impl FileIO for TcpSocket {
|
|||
iface.poll(sys::net::time(), device, &mut sockets);
|
||||
let socket = sockets.get_mut::<tcp::Socket>(self.handle);
|
||||
|
||||
if buf.len() == 1 { // 1 byte status read
|
||||
if buf.len() == 1 {
|
||||
// 1 byte status read
|
||||
buf[0] = tcp_socket_status(socket);
|
||||
return Ok(1);
|
||||
}
|
||||
|
@ -170,8 +166,8 @@ impl FileIO for TcpSocket {
|
|||
if !socket.may_recv() {
|
||||
break;
|
||||
}
|
||||
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(duration);
|
||||
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(d);
|
||||
}
|
||||
sys::time::halt();
|
||||
}
|
||||
|
@ -204,8 +200,8 @@ impl FileIO for TcpSocket {
|
|||
sent = true; // Break after next poll
|
||||
}
|
||||
|
||||
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(duration);
|
||||
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(d);
|
||||
}
|
||||
sys::time::halt();
|
||||
}
|
||||
|
@ -229,8 +225,8 @@ impl FileIO for TcpSocket {
|
|||
socket.close();
|
||||
closed = true;
|
||||
|
||||
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(duration);
|
||||
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(d);
|
||||
}
|
||||
sys::time::halt();
|
||||
}
|
||||
|
|
|
@ -42,13 +42,20 @@ impl UdpSocket {
|
|||
|
||||
pub fn new() -> Self {
|
||||
let mut sockets = SOCKETS.lock();
|
||||
let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1024]);
|
||||
let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1024]);
|
||||
let udp_rx_buffer = udp::PacketBuffer::new(
|
||||
vec![udp::PacketMetadata::EMPTY], vec![0; 1024]
|
||||
);
|
||||
let udp_tx_buffer = udp::PacketBuffer::new(
|
||||
vec![udp::PacketMetadata::EMPTY], vec![0; 1024]
|
||||
);
|
||||
let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer);
|
||||
let handle = sockets.add(udp_socket);
|
||||
let remote_endpoint = None;
|
||||
|
||||
Self { handle, remote_endpoint }
|
||||
Self {
|
||||
handle,
|
||||
remote_endpoint,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect(&mut self, addr: IpAddress, port: u16) -> Result<(), ()> {
|
||||
|
@ -62,15 +69,15 @@ impl UdpSocket {
|
|||
let mut sockets = SOCKETS.lock();
|
||||
iface.poll(sys::net::time(), device, &mut sockets);
|
||||
let socket = sockets.get_mut::<udp::Socket>(self.handle);
|
||||
|
||||
|
||||
if !socket.is_open() {
|
||||
let local_endpoint = IpListenEndpoint::from(random_port());
|
||||
socket.bind(local_endpoint).unwrap();
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(duration);
|
||||
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(d);
|
||||
}
|
||||
sys::time::halt();
|
||||
}
|
||||
|
@ -102,7 +109,8 @@ impl FileIO for UdpSocket {
|
|||
iface.poll(sys::net::time(), device, &mut sockets);
|
||||
let socket = sockets.get_mut::<udp::Socket>(self.handle);
|
||||
|
||||
if buf.len() == 1 { // 1 byte status read
|
||||
if buf.len() == 1 {
|
||||
// 1 byte status read
|
||||
buf[0] = udp_socket_status(socket);
|
||||
return Ok(1);
|
||||
}
|
||||
|
@ -111,8 +119,8 @@ impl FileIO for UdpSocket {
|
|||
(bytes, _) = socket.recv_slice(buf).map_err(|_| ())?;
|
||||
break;
|
||||
}
|
||||
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(duration);
|
||||
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(d);
|
||||
}
|
||||
sys::time::halt();
|
||||
}
|
||||
|
@ -139,8 +147,8 @@ impl FileIO for UdpSocket {
|
|||
break;
|
||||
}
|
||||
if socket.can_send() {
|
||||
if let Some(remote_endpoint) = self.remote_endpoint {
|
||||
if socket.send_slice(buf.as_ref(), remote_endpoint).is_err() {
|
||||
if let Some(endpoint) = self.remote_endpoint {
|
||||
if socket.send_slice(buf.as_ref(), endpoint).is_err() {
|
||||
return Err(());
|
||||
}
|
||||
} else {
|
||||
|
@ -149,8 +157,8 @@ impl FileIO for UdpSocket {
|
|||
sent = true; // Break after next poll
|
||||
}
|
||||
|
||||
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(duration);
|
||||
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(d);
|
||||
}
|
||||
sys::time::halt();
|
||||
}
|
||||
|
@ -174,8 +182,8 @@ impl FileIO for UdpSocket {
|
|||
socket.close();
|
||||
closed = true;
|
||||
|
||||
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(duration);
|
||||
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
|
||||
wait(d);
|
||||
}
|
||||
sys::time::halt();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use bit_field::BitField;
|
||||
use lazy_static::lazy_static;
|
||||
|
@ -47,7 +48,9 @@ impl DeviceConfig {
|
|||
let mut base_addresses: [u32; 6] = [0; 6];
|
||||
for i in 0..6 {
|
||||
let offset = 0x10 + ((i as u8) << 2);
|
||||
let mut register = ConfigRegister::new(bus, device, function, offset);
|
||||
let mut register = ConfigRegister::new(
|
||||
bus, device, function, offset
|
||||
);
|
||||
base_addresses[i] = register.read();
|
||||
}
|
||||
|
||||
|
@ -72,7 +75,9 @@ impl DeviceConfig {
|
|||
}
|
||||
|
||||
pub fn enable_bus_mastering(&mut self) {
|
||||
let mut register = ConfigRegister::new(self.bus, self.device, self.function, 0x04);
|
||||
let mut register = ConfigRegister::new(
|
||||
self.bus, self.device, self.function, 0x04
|
||||
);
|
||||
let mut data = register.read();
|
||||
data.set_bit(2, true);
|
||||
register.write(data);
|
||||
|
@ -80,7 +85,7 @@ impl DeviceConfig {
|
|||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref PCI_DEVICES: Mutex<Vec<DeviceConfig>> = Mutex::new(Vec::new());
|
||||
pub static ref PCI_DEVICES: Mutex<Vec<DeviceConfig>> = Mutex::new(vec![]);
|
||||
}
|
||||
|
||||
pub fn find_device(vendor_id: u16, device_id: u16) -> Option<DeviceConfig> {
|
||||
|
@ -122,7 +127,14 @@ fn check_device(bus: u8, device: u8) {
|
|||
fn add_device(bus: u8, device: u8, function: u8) {
|
||||
let config = DeviceConfig::new(bus, device, function);
|
||||
PCI_DEVICES.lock().push(config);
|
||||
log!("PCI {:04X}:{:02X}:{:02X} [{:04X}:{:04X}]\n", bus, device, function, config.vendor_id, config.device_id);
|
||||
log!(
|
||||
"PCI {:04X}:{:02X}:{:02X} [{:04X}:{:04X}]\n",
|
||||
bus,
|
||||
device,
|
||||
function,
|
||||
config.vendor_id,
|
||||
config.device_id
|
||||
);
|
||||
}
|
||||
|
||||
fn get_vendor_id(bus: u8, device: u8, function: u8) -> u16 {
|
||||
|
@ -151,10 +163,11 @@ impl ConfigRegister {
|
|||
Self {
|
||||
data_port: Port::new(0xCFC),
|
||||
addr_port: Port::new(0xCF8),
|
||||
addr: 0x8000_0000 | ((bus as u32) << 16)
|
||||
| ((device as u32) << 11)
|
||||
| ((function as u32) << 8)
|
||||
| ((offset as u32) & 0xFC),
|
||||
addr: 0x8000_0000
|
||||
| ((bus as u32) << 16)
|
||||
| ((device as u32) << 11)
|
||||
| ((function as u32) << 8)
|
||||
| ((offset as u32) & 0xFC),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,22 +195,29 @@ pub fn init() {
|
|||
for dev in devs.iter() {
|
||||
// NOTE: There's not yet an AHCI driver for SATA disks so we must
|
||||
// switch to IDE legacy mode for the ATA driver.
|
||||
if dev.class == 0x01 && dev.subclass == 0x01 { // IDE Controller
|
||||
let mut register = ConfigRegister::new(dev.bus, dev.device, dev.function, 0x08);
|
||||
if dev.class == 0x01 && dev.subclass == 0x01 {
|
||||
// IDE Controller
|
||||
let mut register = ConfigRegister::new(
|
||||
dev.bus, dev.device, dev.function, 0x08
|
||||
);
|
||||
let mut data = register.read();
|
||||
let prog_offset = 8; // The programing interface start at bit 8
|
||||
|
||||
// Switching primary channel to compatibility mode
|
||||
if dev.prog.get_bit(0) { // PCI native mode
|
||||
if dev.prog.get_bit(1) { // Modifiable
|
||||
if dev.prog.get_bit(0) {
|
||||
// PCI native mode
|
||||
if dev.prog.get_bit(1) {
|
||||
// Modifiable
|
||||
data.set_bit(prog_offset, false);
|
||||
register.write(data);
|
||||
}
|
||||
}
|
||||
|
||||
// Switching secondary channel to compatibility mode
|
||||
if dev.prog.get_bit(2) { // PCI native mode
|
||||
if dev.prog.get_bit(3) { // Modifiable
|
||||
if dev.prog.get_bit(2) {
|
||||
// PCI native mode
|
||||
if dev.prog.get_bit(3) {
|
||||
// Modifiable
|
||||
data.set_bit(prog_offset + 2, false);
|
||||
register.write(data);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,9 @@ use spin::Mutex;
|
|||
pub const PIC_1_OFFSET: u8 = 32;
|
||||
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
|
||||
|
||||
pub static PICS: Mutex<ChainedPics> = Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });
|
||||
pub static PICS: Mutex<ChainedPics> = Mutex::new(unsafe {
|
||||
ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET)
|
||||
});
|
||||
|
||||
pub fn init() {
|
||||
unsafe {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::api::process::ExitCode;
|
||||
use crate::sys::fs::{Resource, Device};
|
||||
use crate::sys::console::Console;
|
||||
use crate::sys::fs::{Device, Resource};
|
||||
use crate::sys;
|
||||
use crate::sys::gdt::GDT;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::collections::btree_map::BTreeMap;
|
||||
|
@ -9,24 +11,53 @@ use alloc::sync::Arc;
|
|||
use alloc::vec::Vec;
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
use core::arch::asm;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
|
||||
use lazy_static::lazy_static;
|
||||
use linked_list_allocator::LockedHeap;
|
||||
use object::{Object, ObjectSegment};
|
||||
use spin::RwLock;
|
||||
use x86_64::registers::control::Cr3;
|
||||
use x86_64::structures::idt::InterruptStackFrameValue;
|
||||
use x86_64::structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame};
|
||||
use x86_64::structures::paging::{
|
||||
FrameAllocator, OffsetPageTable, PageTable, PhysFrame
|
||||
};
|
||||
use x86_64::VirtAddr;
|
||||
|
||||
const ELF_MAGIC: [u8; 4] = [0x7F, b'E', b'L', b'F'];
|
||||
const BIN_MAGIC: [u8; 4] = [0x7F, b'B', b'I', b'N'];
|
||||
|
||||
const MAX_HANDLES: usize = 64;
|
||||
const MAX_PROCS: usize = 4; // TODO: Update this when more than one process can run at once
|
||||
const MAX_PROCS: usize = 4; // TODO: Increase this
|
||||
const MAX_PROC_SIZE: usize = 10 << 20; // 10 MB
|
||||
|
||||
static CODE_ADDR: AtomicU64 = AtomicU64::new(0);
|
||||
pub static PID: AtomicUsize = AtomicUsize::new(0);
|
||||
pub static MAX_PID: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
lazy_static! {
|
||||
pub static ref PROCESS_TABLE: RwLock<[Box<Process>; MAX_PROCS]> = RwLock::new([(); MAX_PROCS].map(|_| Box::new(Process::new())));
|
||||
pub static ref PROCESS_TABLE: RwLock<[Box<Process>; MAX_PROCS]> = {
|
||||
RwLock::new([(); MAX_PROCS].map(|_| Box::new(Process::new())))
|
||||
};
|
||||
}
|
||||
|
||||
// Called during kernel heap initialization
|
||||
pub fn init_process_addr(addr: u64) {
|
||||
sys::process::CODE_ADDR.store(addr, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[repr(align(8), C)]
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct Registers {
|
||||
// Saved scratch registers
|
||||
pub r11: usize,
|
||||
pub r10: usize,
|
||||
pub r9: usize,
|
||||
pub r8: usize,
|
||||
pub rdi: usize,
|
||||
pub rsi: usize,
|
||||
pub rdx: usize,
|
||||
pub rcx: usize,
|
||||
pub rax: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -42,11 +73,17 @@ impl ProcessData {
|
|||
let env = BTreeMap::new();
|
||||
let dir = dir.to_string();
|
||||
let user = user.map(String::from);
|
||||
|
||||
let mut handles = [(); MAX_HANDLES].map(|_| None);
|
||||
handles[0] = Some(Box::new(Resource::Device(Device::Console(Console::new())))); // stdin
|
||||
handles[1] = Some(Box::new(Resource::Device(Device::Console(Console::new())))); // stdout
|
||||
handles[2] = Some(Box::new(Resource::Device(Device::Console(Console::new())))); // stderr
|
||||
handles[3] = Some(Box::new(Resource::Device(Device::Null))); // stdnull
|
||||
let stdin = Resource::Device(Device::Console(Console::new()));
|
||||
let stdout = Resource::Device(Device::Console(Console::new()));
|
||||
let stderr = Resource::Device(Device::Console(Console::new()));
|
||||
let stdnull = Resource::Device(Device::Null);
|
||||
handles[0] = Some(Box::new(stdin));
|
||||
handles[1] = Some(Box::new(stdout));
|
||||
handles[2] = Some(Box::new(stderr));
|
||||
handles[3] = Some(Box::new(stdnull));
|
||||
|
||||
Self { env, dir, user, handles }
|
||||
}
|
||||
}
|
||||
|
@ -189,9 +226,15 @@ pub fn exit() {
|
|||
let table = PROCESS_TABLE.read();
|
||||
let proc = &table[id()];
|
||||
|
||||
let page_table = unsafe { sys::mem::create_page_table(proc.page_table_frame) };
|
||||
let phys_mem_offset = unsafe { sys::mem::PHYS_MEM_OFFSET.unwrap() };
|
||||
let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset)) };
|
||||
let page_table = unsafe {
|
||||
sys::mem::create_page_table(proc.page_table_frame)
|
||||
};
|
||||
let phys_mem_offset = unsafe {
|
||||
sys::mem::PHYS_MEM_OFFSET.unwrap()
|
||||
};
|
||||
let mut mapper = unsafe {
|
||||
OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset))
|
||||
};
|
||||
sys::allocator::free_pages(&mut mapper, proc.code_addr, MAX_PROC_SIZE);
|
||||
|
||||
MAX_PID.fetch_sub(1, Ordering::SeqCst);
|
||||
|
@ -235,42 +278,6 @@ pub unsafe fn free(ptr: *mut u8, _layout: Layout) {
|
|||
}
|
||||
}
|
||||
|
||||
/************************
|
||||
* Userspace experiment *
|
||||
************************/
|
||||
|
||||
// See https://nfil.dev/kernel/rust/coding/rust-kernel-to-userspace-and-back/
|
||||
// And https://github.com/WartaPoirier-corp/ananos/blob/dev/docs/notes/context-switch.md
|
||||
|
||||
use crate::sys;
|
||||
use crate::sys::gdt::GDT;
|
||||
use core::sync::atomic::AtomicU64;
|
||||
use x86_64::VirtAddr;
|
||||
|
||||
static CODE_ADDR: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
// Called during kernel heap initialization
|
||||
pub fn init_process_addr(addr: u64) {
|
||||
sys::process::CODE_ADDR.store(addr, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[repr(align(8), C)]
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct Registers { // Saved scratch registers
|
||||
pub r11: usize,
|
||||
pub r10: usize,
|
||||
pub r9: usize,
|
||||
pub r8: usize,
|
||||
pub rdi: usize,
|
||||
pub rsi: usize,
|
||||
pub rdx: usize,
|
||||
pub rcx: usize,
|
||||
pub rax: usize,
|
||||
}
|
||||
|
||||
const ELF_MAGIC: [u8; 4] = [0x7F, b'E', b'L', b'F'];
|
||||
const BIN_MAGIC: [u8; 4] = [0x7F, b'B', b'I', b'N'];
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Process {
|
||||
id: usize,
|
||||
|
@ -308,7 +315,11 @@ impl Process {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn spawn(bin: &[u8], args_ptr: usize, args_len: usize) -> Result<(), ExitCode> {
|
||||
pub fn spawn(
|
||||
bin: &[u8],
|
||||
args_ptr: usize,
|
||||
args_len: usize
|
||||
) -> Result<(), ExitCode> {
|
||||
if let Ok(id) = Self::create(bin) {
|
||||
let proc = {
|
||||
let table = PROCESS_TABLE.read();
|
||||
|
@ -326,44 +337,59 @@ impl Process {
|
|||
return Err(());
|
||||
}
|
||||
|
||||
let page_table_frame = sys::mem::frame_allocator().allocate_frame().expect("frame allocation failed");
|
||||
let page_table = unsafe { sys::mem::create_page_table(page_table_frame) };
|
||||
let kernel_page_table = unsafe { sys::mem::active_page_table() };
|
||||
let page_table_frame = sys::mem::frame_allocator().allocate_frame().
|
||||
expect("frame allocation failed");
|
||||
|
||||
let page_table = unsafe {
|
||||
sys::mem::create_page_table(page_table_frame)
|
||||
};
|
||||
|
||||
let kernel_page_table = unsafe {
|
||||
sys::mem::active_page_table()
|
||||
};
|
||||
|
||||
// FIXME: for now we just copy everything
|
||||
for (user_page, kernel_page) in page_table.iter_mut().zip(kernel_page_table.iter()) {
|
||||
let pages = page_table.iter_mut().zip(kernel_page_table.iter());
|
||||
for (user_page, kernel_page) in pages {
|
||||
*user_page = kernel_page.clone();
|
||||
}
|
||||
|
||||
let phys_mem_offset = unsafe { sys::mem::PHYS_MEM_OFFSET.unwrap() };
|
||||
let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset)) };
|
||||
let mut mapper = unsafe {
|
||||
OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset))
|
||||
};
|
||||
|
||||
let proc_size = MAX_PROC_SIZE as u64;
|
||||
let code_addr = CODE_ADDR.fetch_add(proc_size, Ordering::SeqCst);
|
||||
let stack_addr = code_addr + proc_size - 4096;
|
||||
//debug!("code_addr: {:#X}", code_addr);
|
||||
//debug!("stack_addr: {:#X}", stack_addr);
|
||||
|
||||
let mut entry_point_addr = 0;
|
||||
let code_ptr = code_addr as *mut u8;
|
||||
let code_size = bin.len();
|
||||
sys::allocator::alloc_pages(&mut mapper, code_addr, code_size).expect("proc mem alloc");
|
||||
|
||||
sys::allocator::alloc_pages(&mut mapper, code_addr, code_size).
|
||||
expect("proc mem alloc");
|
||||
|
||||
if bin[0..4] == ELF_MAGIC { // ELF binary
|
||||
if let Ok(obj) = object::File::parse(bin) {
|
||||
entry_point_addr = obj.entry();
|
||||
sys::allocator::alloc_pages(&mut mapper, code_addr + entry_point_addr, code_size).expect("proc mem alloc");
|
||||
|
||||
let addr = code_addr + entry_point_addr;
|
||||
sys::allocator::alloc_pages(&mut mapper, addr, code_size).
|
||||
expect("proc mem alloc");
|
||||
|
||||
for segment in obj.segments() {
|
||||
let addr = segment.address() as usize;
|
||||
if let Ok(data) = segment.data() {
|
||||
for (i, b) in data.iter().enumerate() {
|
||||
//debug!("code: {:#X}", unsafe { code_ptr.add(addr + i) as usize });
|
||||
unsafe { core::ptr::write(code_ptr.add(addr + i), *b) };
|
||||
unsafe {
|
||||
core::ptr::write(code_ptr.add(addr + i), *b)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if bin[0..4] == BIN_MAGIC { // Flat binary
|
||||
//sys::allocator::alloc_pages(&mut mapper, code_addr, code_size).expect("proc mem alloc");
|
||||
for (i, b) in bin.iter().skip(4).enumerate() {
|
||||
unsafe { core::ptr::write(code_ptr.add(i), *b) };
|
||||
}
|
||||
|
@ -385,7 +411,16 @@ impl Process {
|
|||
let id = MAX_PID.fetch_add(1, Ordering::SeqCst);
|
||||
let parent_id = parent.id;
|
||||
let proc = Process {
|
||||
id, parent_id, code_addr, stack_addr, entry_point_addr, page_table_frame, data, stack_frame, registers, allocator
|
||||
id,
|
||||
parent_id,
|
||||
code_addr,
|
||||
stack_addr,
|
||||
entry_point_addr,
|
||||
page_table_frame,
|
||||
data,
|
||||
stack_frame,
|
||||
registers,
|
||||
allocator,
|
||||
};
|
||||
|
||||
let mut process_table = PROCESS_TABLE.write();
|
||||
|
@ -398,14 +433,19 @@ impl Process {
|
|||
fn exec(&self, args_ptr: usize, args_len: usize) {
|
||||
let page_table = unsafe { sys::process::page_table() };
|
||||
let phys_mem_offset = unsafe { sys::mem::PHYS_MEM_OFFSET.unwrap() };
|
||||
let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset)) };
|
||||
let mut mapper = unsafe {
|
||||
OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset))
|
||||
};
|
||||
|
||||
let heap_addr = self.code_addr + (self.stack_addr - self.code_addr) / 2;
|
||||
//debug!("user-args: {:#016X}", heap_addr);
|
||||
sys::allocator::alloc_pages(&mut mapper, heap_addr, 1).expect("proc heap alloc");
|
||||
|
||||
sys::allocator::alloc_pages(&mut mapper, heap_addr, 1).
|
||||
expect("proc heap alloc");
|
||||
|
||||
let args_ptr = ptr_from_addr(args_ptr as u64) as usize;
|
||||
let args: &[&str] = unsafe { core::slice::from_raw_parts(args_ptr as *const &str, args_len) };
|
||||
let args: &[&str] = unsafe {
|
||||
core::slice::from_raw_parts(args_ptr as *const &str, args_len)
|
||||
};
|
||||
let mut addr = heap_addr;
|
||||
let vec: Vec<&str> = args.iter().map(|arg| {
|
||||
let ptr = addr as *mut u8;
|
||||
|
@ -428,9 +468,10 @@ impl Process {
|
|||
let args_ptr = args.as_ptr() as u64;
|
||||
|
||||
let heap_addr = addr;
|
||||
let heap_size = (self.stack_addr - heap_addr) / 2;
|
||||
//debug!("user-heap: {:#016X}..{:#016X}", heap_addr, heap_addr + heap_size);
|
||||
unsafe { self.allocator.lock().init(heap_addr as *mut u8, heap_size as usize) };
|
||||
let heap_size = ((self.stack_addr - heap_addr) / 2) as usize;
|
||||
unsafe {
|
||||
self.allocator.lock().init(heap_addr as *mut u8, heap_size);
|
||||
}
|
||||
|
||||
set_id(self.id); // Change PID
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::sys;
|
||||
use crate::api::fs::{FileIO, IO};
|
||||
use crate::sys;
|
||||
|
||||
use rand::{RngCore, SeedableRng};
|
||||
use rand_hc::Hc128Rng;
|
||||
|
@ -27,8 +27,7 @@ impl FileIO for Random {
|
|||
unimplemented!();
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
}
|
||||
fn close(&mut self) {}
|
||||
|
||||
fn poll(&mut self, event: IO) -> bool {
|
||||
match event {
|
||||
|
|
|
@ -14,13 +14,13 @@ lazy_static! {
|
|||
}
|
||||
|
||||
pub struct Serial {
|
||||
port: SerialPort
|
||||
port: SerialPort,
|
||||
}
|
||||
|
||||
impl Serial {
|
||||
fn new(addr: u16) -> Self {
|
||||
Self {
|
||||
port: unsafe { SerialPort::new(addr) }
|
||||
port: unsafe { SerialPort::new(addr) },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ impl Perform for Serial {
|
|||
_ => return,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
'l' => { // Disable
|
||||
for param in params.iter() {
|
||||
match param[0] {
|
||||
|
@ -67,17 +67,17 @@ impl Perform for Serial {
|
|||
_ => return,
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn print_fmt(args: fmt::Arguments) {
|
||||
interrupts::without_interrupts(|| {
|
||||
SERIAL.lock().write_fmt(args).expect("Could not print to serial");
|
||||
})
|
||||
interrupts::without_interrupts(||
|
||||
SERIAL.lock().write_fmt(args).expect("Could not print to serial")
|
||||
)
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
|
|
|
@ -3,21 +3,28 @@ pub mod service;
|
|||
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::sys;
|
||||
use crate::sys::fs::{FileInfo, IO};
|
||||
use crate::sys::fs::FileInfo;
|
||||
|
||||
use core::arch::asm;
|
||||
use smoltcp::wire::IpAddress;
|
||||
use smoltcp::wire::Ipv4Address;
|
||||
|
||||
/*
|
||||
* Dispatching system calls
|
||||
*/
|
||||
fn utf8_from_raw_parts(ptr: *mut u8, len: usize) -> &'static str {
|
||||
unsafe {
|
||||
let slice = core::slice::from_raw_parts(ptr, len);
|
||||
core::str::from_utf8_unchecked(slice)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> usize {
|
||||
pub fn dispatcher(
|
||||
n: usize,
|
||||
arg1: usize,
|
||||
arg2: usize,
|
||||
arg3: usize,
|
||||
arg4: usize
|
||||
) -> usize {
|
||||
match n {
|
||||
number::EXIT => {
|
||||
service::exit(ExitCode::from(arg1)) as usize
|
||||
}
|
||||
number::EXIT => service::exit(ExitCode::from(arg1)) as usize,
|
||||
number::SLEEP => {
|
||||
service::sleep(f64::from_bits(arg1 as u64));
|
||||
0
|
||||
|
@ -25,35 +32,39 @@ pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize)
|
|||
number::DELETE => {
|
||||
let ptr = sys::process::ptr_from_addr(arg1 as u64);
|
||||
let len = arg2;
|
||||
let path = unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr, len)) };
|
||||
let path = utf8_from_raw_parts(ptr, len);
|
||||
service::delete(path) as usize
|
||||
}
|
||||
number::INFO => {
|
||||
let ptr = sys::process::ptr_from_addr(arg1 as u64);
|
||||
let len = arg2;
|
||||
let path = unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr, len)) };
|
||||
let path = utf8_from_raw_parts(ptr, len);
|
||||
let info = unsafe { &mut *(arg3 as *mut FileInfo) };
|
||||
service::info(path, info) as usize
|
||||
}
|
||||
number::OPEN => {
|
||||
let ptr = sys::process::ptr_from_addr(arg1 as u64);
|
||||
let len = arg2;
|
||||
let path = utf8_from_raw_parts(ptr, len);
|
||||
let flags = arg3;
|
||||
let path = unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr, len)) };
|
||||
service::open(path, flags) as usize
|
||||
}
|
||||
number::READ => {
|
||||
let handle = arg1;
|
||||
let ptr = sys::process::ptr_from_addr(arg2 as u64);
|
||||
let len = arg3;
|
||||
let buf = unsafe { core::slice::from_raw_parts_mut(ptr, len) };
|
||||
let buf = unsafe {
|
||||
core::slice::from_raw_parts_mut(ptr, len)
|
||||
};
|
||||
service::read(handle, buf) as usize
|
||||
}
|
||||
number::WRITE => {
|
||||
let handle = arg1;
|
||||
let ptr = sys::process::ptr_from_addr(arg2 as u64);
|
||||
let len = arg3;
|
||||
let buf = unsafe { core::slice::from_raw_parts_mut(ptr, len) }; // TODO: Remove mut
|
||||
let buf = unsafe {
|
||||
core::slice::from_raw_parts_mut(ptr, len) // TODO: Remove mut
|
||||
};
|
||||
service::write(handle, buf) as usize
|
||||
}
|
||||
number::CLOSE => {
|
||||
|
@ -69,7 +80,7 @@ pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize)
|
|||
number::SPAWN => {
|
||||
let path_ptr = sys::process::ptr_from_addr(arg1 as u64);
|
||||
let path_len = arg2;
|
||||
let path = unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(path_ptr, path_len)) };
|
||||
let path = utf8_from_raw_parts(path_ptr, path_len);
|
||||
let args_ptr = arg3;
|
||||
let args_len = arg4;
|
||||
service::spawn(path, args_ptr, args_len) as usize
|
||||
|
@ -79,16 +90,16 @@ pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize)
|
|||
service::stop(code)
|
||||
}
|
||||
number::POLL => {
|
||||
let ptr = sys::process::ptr_from_addr(arg1 as u64) as *const (usize, IO);
|
||||
let ptr = sys::process::ptr_from_addr(arg1 as u64) as *const _;
|
||||
let len = arg2;
|
||||
let list = unsafe { core::slice::from_raw_parts(ptr, len) };
|
||||
service::poll(list) as usize
|
||||
}
|
||||
number::CONNECT => {
|
||||
let handle = arg1;
|
||||
let buf_ptr = sys::process::ptr_from_addr(arg2 as u64);
|
||||
let buf_len = arg3;
|
||||
let buf = unsafe { core::slice::from_raw_parts(buf_ptr, buf_len) };
|
||||
let ptr = sys::process::ptr_from_addr(arg2 as u64);
|
||||
let len = arg3;
|
||||
let buf = unsafe { core::slice::from_raw_parts(ptr, len) };
|
||||
let addr = IpAddress::from(Ipv4Address::from_bytes(buf));
|
||||
let port = arg4 as u16;
|
||||
service::connect(handle, addr, port) as usize
|
||||
|
@ -100,11 +111,11 @@ pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize)
|
|||
}
|
||||
number::ACCEPT => {
|
||||
let handle = arg1;
|
||||
let buf_ptr = sys::process::ptr_from_addr(arg2 as u64);
|
||||
let buf_len = arg3;
|
||||
let buf = unsafe { core::slice::from_raw_parts_mut(buf_ptr, buf_len) };
|
||||
let ptr = sys::process::ptr_from_addr(arg2 as u64);
|
||||
let len = arg3;
|
||||
let buf = unsafe { core::slice::from_raw_parts_mut(ptr, len) };
|
||||
if let Ok(addr) = service::accept(handle) {
|
||||
buf[0..buf_len].clone_from_slice(addr.as_bytes());
|
||||
buf[0..len].clone_from_slice(addr.as_bytes());
|
||||
0
|
||||
} else {
|
||||
-1 as isize as usize
|
||||
|
@ -128,10 +139,6 @@ pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sending system calls
|
||||
*/
|
||||
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn syscall0(n: usize) -> usize {
|
||||
let res: usize;
|
||||
|
@ -165,7 +172,12 @@ pub unsafe fn syscall2(n: usize, arg1: usize, arg2: usize) -> usize {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn syscall3(n: usize, arg1: usize, arg2: usize, arg3: usize) -> usize {
|
||||
pub unsafe fn syscall3(
|
||||
n: usize,
|
||||
arg1: usize,
|
||||
arg2: usize,
|
||||
arg3: usize
|
||||
) -> usize {
|
||||
let res: usize;
|
||||
asm!(
|
||||
"int 0x80", in("rax") n,
|
||||
|
@ -176,7 +188,13 @@ pub unsafe fn syscall3(n: usize, arg1: usize, arg2: usize, arg3: usize) -> usize
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn syscall4(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> usize {
|
||||
pub unsafe fn syscall4(
|
||||
n: usize,
|
||||
arg1: usize,
|
||||
arg2: usize,
|
||||
arg3: usize,
|
||||
arg4: usize
|
||||
) -> usize {
|
||||
let res: usize;
|
||||
asm!(
|
||||
"int 0x80", in("rax") n,
|
||||
|
@ -188,19 +206,30 @@ pub unsafe fn syscall4(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: us
|
|||
|
||||
#[macro_export]
|
||||
macro_rules! syscall {
|
||||
($n:expr) => (
|
||||
$crate::sys::syscall::syscall0(
|
||||
$n as usize));
|
||||
($n:expr, $a1:expr) => (
|
||||
$crate::sys::syscall::syscall1(
|
||||
$n as usize, $a1 as usize));
|
||||
($n:expr, $a1:expr, $a2:expr) => (
|
||||
$crate::sys::syscall::syscall2(
|
||||
$n as usize, $a1 as usize, $a2 as usize));
|
||||
($n:expr, $a1:expr, $a2:expr, $a3:expr) => (
|
||||
($n:expr) => {
|
||||
$crate::sys::syscall::syscall0($n as usize)
|
||||
};
|
||||
($n:expr, $a1:expr) => {
|
||||
$crate::sys::syscall::syscall1($n as usize, $a1 as usize)
|
||||
};
|
||||
($n:expr, $a1:expr, $a2:expr) => {
|
||||
$crate::sys::syscall::syscall2($n as usize, $a1 as usize, $a2 as usize)
|
||||
};
|
||||
($n:expr, $a1:expr, $a2:expr, $a3:expr) => {
|
||||
$crate::sys::syscall::syscall3(
|
||||
$n as usize, $a1 as usize, $a2 as usize, $a3 as usize));
|
||||
($n:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => (
|
||||
$n as usize,
|
||||
$a1 as usize,
|
||||
$a2 as usize,
|
||||
$a3 as usize,
|
||||
)
|
||||
};
|
||||
($n:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
|
||||
$crate::sys::syscall::syscall4(
|
||||
$n as usize, $a1 as usize, $a2 as usize, $a3 as usize, $a4 as usize));
|
||||
$n as usize,
|
||||
$a1 as usize,
|
||||
$a2 as usize,
|
||||
$a3 as usize,
|
||||
$a4 as usize,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
pub const EXIT: usize = 0x1;
|
||||
pub const SPAWN: usize = 0x2;
|
||||
pub const READ: usize = 0x3;
|
||||
pub const WRITE: usize = 0x4;
|
||||
pub const OPEN: usize = 0x5;
|
||||
pub const CLOSE: usize = 0x6;
|
||||
pub const INFO: usize = 0x7;
|
||||
pub const DUP: usize = 0x8;
|
||||
pub const DELETE: usize = 0x9;
|
||||
pub const STOP: usize = 0xA;
|
||||
pub const SLEEP: usize = 0xB;
|
||||
pub const POLL: usize = 0xC;
|
||||
pub const CONNECT: usize = 0xD;
|
||||
pub const LISTEN: usize = 0xE;
|
||||
pub const ACCEPT: usize = 0xF;
|
||||
pub const ALLOC: usize = 0x10;
|
||||
pub const FREE: usize = 0x11;
|
||||
pub const EXIT: usize = 0x1;
|
||||
pub const SPAWN: usize = 0x2;
|
||||
pub const READ: usize = 0x3;
|
||||
pub const WRITE: usize = 0x4;
|
||||
pub const OPEN: usize = 0x5;
|
||||
pub const CLOSE: usize = 0x6;
|
||||
pub const INFO: usize = 0x7;
|
||||
pub const DUP: usize = 0x8;
|
||||
pub const DELETE: usize = 0x9;
|
||||
pub const STOP: usize = 0xA;
|
||||
pub const SLEEP: usize = 0xB;
|
||||
pub const POLL: usize = 0xC;
|
||||
pub const CONNECT: usize = 0xD;
|
||||
pub const LISTEN: usize = 0xE;
|
||||
pub const ACCEPT: usize = 0xF;
|
||||
pub const ALLOC: usize = 0x10;
|
||||
pub const FREE: usize = 0x11;
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
use crate::sys;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::fs::{FileIO, IO};
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::sys;
|
||||
use crate::sys::fs::Device;
|
||||
use crate::sys::fs::FileInfo;
|
||||
use crate::sys::fs::Resource;
|
||||
use crate::sys::fs::Device;
|
||||
use crate::sys::process::Process;
|
||||
|
||||
use alloc::vec;
|
||||
use core::alloc::Layout;
|
||||
use core::arch::asm;
|
||||
use smoltcp::wire::IpAddress;
|
||||
|
||||
pub fn exit(code: ExitCode) -> ExitCode {
|
||||
//debug!("syscall::exit(code={})", code as usize);
|
||||
sys::process::exit();
|
||||
code
|
||||
}
|
||||
|
@ -90,7 +90,6 @@ pub fn close(handle: usize) {
|
|||
}
|
||||
|
||||
pub fn spawn(path: &str, args_ptr: usize, args_len: usize) -> ExitCode {
|
||||
//debug!("syscall::spawn(path={}, args_ptr={:#X}, args_len={})", path, args_ptr, args_len);
|
||||
let path = match sys::fs::canonicalize(path) {
|
||||
Ok(path) => path,
|
||||
Err(_) => return ExitCode::OpenError,
|
||||
|
@ -116,10 +115,7 @@ pub fn stop(code: usize) -> usize {
|
|||
match code {
|
||||
0xCAFE => { // Reboot
|
||||
unsafe {
|
||||
asm!(
|
||||
"xor rax, rax",
|
||||
"mov cr3, rax"
|
||||
);
|
||||
asm!("xor rax, rax", "mov cr3, rax");
|
||||
}
|
||||
}
|
||||
0xDEAD => { // Halt
|
||||
|
@ -147,8 +143,12 @@ pub fn poll(list: &[(usize, IO)]) -> isize {
|
|||
pub fn connect(handle: usize, addr: IpAddress, port: u16) -> isize {
|
||||
if let Some(mut file) = sys::process::handle(handle) {
|
||||
let res = match *file {
|
||||
Resource::Device(Device::TcpSocket(ref mut dev)) => dev.connect(addr, port),
|
||||
Resource::Device(Device::UdpSocket(ref mut dev)) => dev.connect(addr, port),
|
||||
Resource::Device(Device::TcpSocket(ref mut dev)) => {
|
||||
dev.connect(addr, port)
|
||||
}
|
||||
Resource::Device(Device::UdpSocket(ref mut dev)) => {
|
||||
dev.connect(addr, port)
|
||||
}
|
||||
_ => Err(()),
|
||||
};
|
||||
if res.is_ok() {
|
||||
|
@ -184,13 +184,9 @@ pub fn accept(handle: usize) -> Result<IpAddress, ()> {
|
|||
Err(())
|
||||
}
|
||||
|
||||
use core::alloc::Layout;
|
||||
|
||||
pub fn alloc(size: usize, align: usize) -> *mut u8 {
|
||||
if let Ok(layout) = Layout::from_size_align(size, align) {
|
||||
let ptr = unsafe { sys::process::alloc(layout) };
|
||||
//debug!("syscall::alloc(size={}, align={}) -> ptr={:?}", size, align, ptr);
|
||||
ptr
|
||||
unsafe { sys::process::alloc(layout) }
|
||||
} else {
|
||||
core::ptr::null_mut()
|
||||
}
|
||||
|
@ -198,7 +194,6 @@ pub fn alloc(size: usize, align: usize) -> *mut u8 {
|
|||
|
||||
pub fn free(ptr: *mut u8, size: usize, align: usize) {
|
||||
if let Ok(layout) = Layout::from_size_align(size, align) {
|
||||
//debug!("syscall::free(ptr={:?}, size={}, align={})", ptr, size, align);
|
||||
unsafe { sys::process::free(ptr, layout) };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::sys;
|
||||
use crate::sys::cmos::CMOS;
|
||||
use core::hint::spin_loop;
|
||||
use core::sync::atomic::{AtomicUsize, AtomicU64, Ordering};
|
||||
use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
|
||||
use x86_64::instructions::interrupts;
|
||||
use x86_64::instructions::port::Port;
|
||||
|
||||
|
|
127
src/sys/vga.rs
127
src/sys/vga.rs
|
@ -1,6 +1,6 @@
|
|||
use crate::api::font::Font;
|
||||
use crate::api::vga::{Color, Palette};
|
||||
use crate::api::vga::color;
|
||||
use crate::api::vga::{Color, Palette};
|
||||
use crate::sys;
|
||||
|
||||
use alloc::string::String;
|
||||
|
@ -14,18 +14,15 @@ use vte::{Params, Parser, Perform};
|
|||
use x86_64::instructions::interrupts;
|
||||
use x86_64::instructions::port::Port;
|
||||
|
||||
// See https://web.stanford.edu/class/cs140/projects/pintos/specs/freevga/vga/vga.htm
|
||||
// And https://01.org/sites/default/files/documentation/snb_ihd_os_vol3_part1_0.pdf
|
||||
|
||||
const ATTR_ADDR_DATA_REG: u16 = 0x3C0;
|
||||
const ATTR_DATA_READ_REG: u16 = 0x3C1;
|
||||
const SEQUENCER_ADDR_REG: u16 = 0x3C4;
|
||||
const ATTR_ADDR_DATA_REG: u16 = 0x3C0;
|
||||
const ATTR_DATA_READ_REG: u16 = 0x3C1;
|
||||
const SEQUENCER_ADDR_REG: u16 = 0x3C4;
|
||||
const DAC_ADDR_WRITE_MODE_REG: u16 = 0x3C8;
|
||||
const DAC_DATA_REG: u16 = 0x3C9;
|
||||
const GRAPHICS_ADDR_REG: u16 = 0x3CE;
|
||||
const CRTC_ADDR_REG: u16 = 0x3D4;
|
||||
const CRTC_DATA_REG: u16 = 0x3D5;
|
||||
const INPUT_STATUS_REG: u16 = 0x3DA;
|
||||
const DAC_DATA_REG: u16 = 0x3C9;
|
||||
const GRAPHICS_ADDR_REG: u16 = 0x3CE;
|
||||
const CRTC_ADDR_REG: u16 = 0x3D4;
|
||||
const CRTC_DATA_REG: u16 = 0x3D5;
|
||||
const INPUT_STATUS_REG: u16 = 0x3DA;
|
||||
|
||||
const FG: Color = Color::LightGray;
|
||||
const BG: Color = Color::Black;
|
||||
|
@ -139,12 +136,14 @@ impl Writer {
|
|||
|
||||
fn write_byte(&mut self, byte: u8) {
|
||||
match byte {
|
||||
0x0A => { // Newline
|
||||
0x0A => {
|
||||
// Newline
|
||||
self.new_line();
|
||||
},
|
||||
}
|
||||
0x0D => { // Carriage Return
|
||||
},
|
||||
0x08 => { // Backspace
|
||||
}
|
||||
0x08 => {
|
||||
// Backspace
|
||||
if self.writer[0] > 0 {
|
||||
self.writer[0] -= 1;
|
||||
let c = ScreenChar {
|
||||
|
@ -153,11 +152,10 @@ impl Writer {
|
|||
};
|
||||
let x = self.writer[0];
|
||||
let y = self.writer[1];
|
||||
unsafe {
|
||||
core::ptr::write_volatile(&mut self.buffer.chars[y][x], c);
|
||||
}
|
||||
let ptr = &mut self.buffer.chars[y][x];
|
||||
unsafe { core::ptr::write_volatile(ptr, c); }
|
||||
}
|
||||
},
|
||||
}
|
||||
byte => {
|
||||
if self.writer[0] >= BUFFER_WIDTH {
|
||||
self.new_line();
|
||||
|
@ -165,12 +163,18 @@ impl Writer {
|
|||
|
||||
let x = self.writer[0];
|
||||
let y = self.writer[1];
|
||||
let ascii_code = if is_printable(byte) { byte } else { UNPRINTABLE };
|
||||
let ascii_code = if is_printable(byte) {
|
||||
byte
|
||||
} else {
|
||||
UNPRINTABLE
|
||||
};
|
||||
let color_code = self.color_code;
|
||||
let c = ScreenChar { ascii_code, color_code };
|
||||
unsafe {
|
||||
core::ptr::write_volatile(&mut self.buffer.chars[y][x], c);
|
||||
}
|
||||
let c = ScreenChar {
|
||||
ascii_code,
|
||||
color_code,
|
||||
};
|
||||
let ptr = &mut self.buffer.chars[y][x];
|
||||
unsafe { core::ptr::write_volatile(ptr, c); }
|
||||
self.writer[0] += 1;
|
||||
}
|
||||
}
|
||||
|
@ -232,7 +236,8 @@ impl Writer {
|
|||
for j in 0..font.height as usize {
|
||||
let vga_offset = j + i * 32 as usize;
|
||||
let fnt_offset = j + i * font.height as usize;
|
||||
buffer.add(vga_offset).write_volatile(font.data[fnt_offset]);
|
||||
let ptr = buffer.add(vga_offset);
|
||||
ptr.write_volatile(font.data[fnt_offset]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -286,18 +291,18 @@ impl Perform for Writer {
|
|||
0 => {
|
||||
fg = FG;
|
||||
bg = BG;
|
||||
},
|
||||
}
|
||||
30..=37 | 90..=97 => {
|
||||
fg = color::from_ansi(param[0] as u8);
|
||||
},
|
||||
}
|
||||
40..=47 | 100..=107 => {
|
||||
bg = color::from_ansi((param[0] as u8) - 10);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
self.set_color(fg, bg);
|
||||
},
|
||||
}
|
||||
'A' => { // Cursor Up
|
||||
let mut n = 1;
|
||||
for param in params.iter() {
|
||||
|
@ -305,23 +310,25 @@ impl Perform for Writer {
|
|||
}
|
||||
self.writer[1] = self.writer[1].saturating_sub(n);
|
||||
self.cursor[1] = self.cursor[1].saturating_sub(n);
|
||||
},
|
||||
}
|
||||
'B' => { // Cursor Down
|
||||
let mut n = 1;
|
||||
for param in params.iter() {
|
||||
n = param[0] as usize;
|
||||
}
|
||||
self.writer[1] = cmp::min(self.writer[1] + n, BUFFER_HEIGHT - 1);
|
||||
self.cursor[1] = cmp::min(self.cursor[1] + n, BUFFER_HEIGHT - 1);
|
||||
},
|
||||
let height = BUFFER_HEIGHT - 1;
|
||||
self.writer[1] = cmp::min(self.writer[1] + n, height);
|
||||
self.cursor[1] = cmp::min(self.cursor[1] + n, height);
|
||||
}
|
||||
'C' => { // Cursor Forward
|
||||
let mut n = 1;
|
||||
for param in params.iter() {
|
||||
n = param[0] as usize;
|
||||
}
|
||||
self.writer[0] = cmp::min(self.writer[0] + n, BUFFER_WIDTH - 1);
|
||||
self.cursor[0] = cmp::min(self.cursor[0] + n, BUFFER_WIDTH - 1);
|
||||
},
|
||||
let width = BUFFER_WIDTH - 1;
|
||||
self.writer[0] = cmp::min(self.writer[0] + n, width);
|
||||
self.cursor[0] = cmp::min(self.cursor[0] + n, width);
|
||||
}
|
||||
'D' => { // Cursor Backward
|
||||
let mut n = 1;
|
||||
for param in params.iter() {
|
||||
|
@ -329,7 +336,7 @@ impl Perform for Writer {
|
|||
}
|
||||
self.writer[0] = self.writer[0].saturating_sub(n);
|
||||
self.cursor[0] = self.cursor[0].saturating_sub(n);
|
||||
},
|
||||
}
|
||||
'G' => { // Cursor Horizontal Absolute
|
||||
let (_, y) = self.cursor_position();
|
||||
let mut x = 1;
|
||||
|
@ -341,7 +348,7 @@ impl Perform for Writer {
|
|||
}
|
||||
self.set_writer_position(x - 1, y);
|
||||
self.set_cursor_position(x - 1, y);
|
||||
},
|
||||
}
|
||||
'H' => { // Move cursor
|
||||
let mut x = 1;
|
||||
let mut y = 1;
|
||||
|
@ -357,20 +364,20 @@ impl Perform for Writer {
|
|||
}
|
||||
self.set_writer_position(x - 1, y - 1);
|
||||
self.set_cursor_position(x - 1, y - 1);
|
||||
},
|
||||
}
|
||||
'J' => { // Erase in Display
|
||||
let mut n = 0;
|
||||
for param in params.iter() {
|
||||
n = param[0] as usize;
|
||||
}
|
||||
match n {
|
||||
// TODO: 0 and 1, from cursor to begining or to end of screen
|
||||
// TODO: 0 and 1, cursor to begining or to end of screen
|
||||
2 => self.clear_screen(),
|
||||
_ => return,
|
||||
}
|
||||
self.set_writer_position(0, 0);
|
||||
self.set_cursor_position(0, 0);
|
||||
},
|
||||
}
|
||||
'K' => { // Erase in Line
|
||||
let (x, y) = self.cursor_position();
|
||||
let mut n = 0;
|
||||
|
@ -385,7 +392,7 @@ impl Perform for Writer {
|
|||
}
|
||||
self.set_writer_position(x, y);
|
||||
self.set_cursor_position(x, y);
|
||||
},
|
||||
}
|
||||
'h' => { // Enable
|
||||
for param in params.iter() {
|
||||
match param[0] {
|
||||
|
@ -394,7 +401,7 @@ impl Perform for Writer {
|
|||
_ => return,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
'l' => { // Disable
|
||||
for param in params.iter() {
|
||||
match param[0] {
|
||||
|
@ -403,8 +410,8 @@ impl Perform for Writer {
|
|||
_ => return,
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -445,9 +452,9 @@ impl fmt::Write for Writer {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub fn print_fmt(args: fmt::Arguments) {
|
||||
interrupts::without_interrupts(|| {
|
||||
WRITER.lock().write_fmt(args).expect("Could not print to VGA");
|
||||
});
|
||||
interrupts::without_interrupts(||
|
||||
WRITER.lock().write_fmt(args).expect("Could not print to VGA")
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cols() -> usize {
|
||||
|
@ -459,15 +466,15 @@ pub fn rows() -> usize {
|
|||
}
|
||||
|
||||
pub fn color() -> (Color, Color) {
|
||||
interrupts::without_interrupts(|| {
|
||||
interrupts::without_interrupts(||
|
||||
WRITER.lock().color()
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
pub fn set_color(foreground: Color, background: Color) {
|
||||
interrupts::without_interrupts(|| {
|
||||
interrupts::without_interrupts(||
|
||||
WRITER.lock().set_color(foreground, background)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// ASCII Printable
|
||||
|
@ -480,18 +487,18 @@ pub fn is_printable(c: u8) -> bool {
|
|||
}
|
||||
|
||||
pub fn set_font(font: &Font) {
|
||||
interrupts::without_interrupts(|| {
|
||||
WRITER.lock().set_font(font);
|
||||
})
|
||||
interrupts::without_interrupts(||
|
||||
WRITER.lock().set_font(font)
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: Remove this
|
||||
pub fn set_palette(palette: Palette) {
|
||||
interrupts::without_interrupts(|| {
|
||||
interrupts::without_interrupts(||
|
||||
for (i, (r, g, b)) in palette.colors.iter().enumerate() {
|
||||
WRITER.lock().set_palette(i, *r, *g, *b);
|
||||
WRITER.lock().set_palette(i, *r, *g, *b)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// 0x00 -> top
|
||||
|
|
|
@ -17,7 +17,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
pub fn encode(s: &[u8]) -> Vec<u8> {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
buf.resize(s.len() * 4 / 3 + 4, 0); // Resize to base64 + padding
|
||||
let bytes_written = base64::encode_config_slice(s, base64::STANDARD_NO_PAD, &mut buf);
|
||||
let bytes_written = base64::encode_config_slice(
|
||||
s, base64::STANDARD_NO_PAD, &mut buf
|
||||
);
|
||||
buf.resize(bytes_written, 0); // Resize back to actual size
|
||||
buf
|
||||
}
|
||||
|
@ -25,7 +27,9 @@ pub fn encode(s: &[u8]) -> Vec<u8> {
|
|||
pub fn decode(s: &[u8]) -> Vec<u8> {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
buf.resize(s.len(), 0);
|
||||
let bytes_written = base64::decode_config_slice(s, base64::STANDARD_NO_PAD, &mut buf).unwrap();
|
||||
let bytes_written = base64::decode_config_slice(
|
||||
s, base64::STANDARD_NO_PAD, &mut buf
|
||||
).unwrap();
|
||||
buf.resize(bytes_written, 0);
|
||||
buf
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{api, sys};
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::console::Style;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::{api, sys};
|
||||
|
||||
use x86_64::instructions::port::Port;
|
||||
|
||||
|
@ -55,7 +55,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
error!("Missing freq");
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
},
|
||||
}
|
||||
"-l" | "--len" => {
|
||||
if i + 1 < n {
|
||||
if let Ok(value) = args[i + 1].parse() {
|
||||
|
@ -69,8 +69,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
error!("Missing len");
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
@ -83,10 +83,19 @@ fn help() -> Result<(), ExitCode> {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} beep {}<options>{1}", csi_title, csi_reset, csi_option);
|
||||
println!(
|
||||
"{}Usage:{} beep {}<options>{1}",
|
||||
csi_title, csi_reset, csi_option
|
||||
);
|
||||
println!();
|
||||
println!("{}Options:{}", csi_title, csi_reset);
|
||||
println!(" {0}-f{1}, {0}--freq <hertz>{1} Tone frequency", csi_option, csi_reset);
|
||||
println!(" {0}-l{1}, {0}--len <milliseconds>{1} Tone length", csi_option, csi_reset);
|
||||
println!(
|
||||
" {0}-f{1}, {0}--freq <hertz>{1} Tone frequency",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
println!(
|
||||
" {0}-l{1}, {0}--len <milliseconds>{1} Tone length",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::api::console::Style;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::prompt::Prompt;
|
||||
use crate::api::console::Style;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::format;
|
||||
|
@ -9,9 +9,9 @@ use alloc::vec::Vec;
|
|||
|
||||
use nom::branch::alt;
|
||||
use nom::character::complete::{char, space0};
|
||||
use nom::number::complete::double;
|
||||
use nom::combinator::map;
|
||||
use nom::multi::many0;
|
||||
use nom::number::complete::double;
|
||||
use nom::sequence::{delimited, tuple};
|
||||
use nom::IResult;
|
||||
|
||||
|
@ -34,13 +34,18 @@ pub enum Exp {
|
|||
|
||||
fn parse(input: &str) -> IResult<&str, Exp> {
|
||||
let (input, num1) = parse_term(input)?;
|
||||
let (input, exps) = many0(tuple((alt((char('+'), char('-'))), parse_term)))(input)?;
|
||||
let (input, exps) = many0(
|
||||
tuple((alt((char('+'), char('-'))), parse_term))
|
||||
)(input)?;
|
||||
Ok((input, parse_exp(num1, exps)))
|
||||
}
|
||||
|
||||
fn parse_term(input: &str) -> IResult<&str, Exp> {
|
||||
let (input, num1) = parse_factor(input)?;
|
||||
let (input, exps) = many0(tuple((alt((char('%'), char('/'), char('*'))), parse_factor)))(input)?;
|
||||
let (input, exps) = many0(tuple((
|
||||
alt((char('%'), char('/'), char('*'))),
|
||||
parse_factor,
|
||||
)))(input)?;
|
||||
Ok((input, parse_exp(num1, exps)))
|
||||
}
|
||||
|
||||
|
@ -99,10 +104,8 @@ fn parse_eval(line: &str) -> Result<f64, String> {
|
|||
} else {
|
||||
Err(format!("Could not parse '{}'", line))
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
Err(format!("Could not parse '{}'", line))
|
||||
},
|
||||
}
|
||||
Err(_) => Err(format!("Could not parse '{}'", line)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,7 +148,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
for &arg in args {
|
||||
match arg {
|
||||
"-h" | "--help" => return help(),
|
||||
_ => {},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if args.len() == 1 {
|
||||
|
@ -168,7 +171,10 @@ pub fn help() -> Result<(), ExitCode> {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} calc {}[<exp>]{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} calc {}[<exp>]{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -180,28 +186,28 @@ fn test_calc() {
|
|||
};
|
||||
}
|
||||
|
||||
assert_eq!(eval!("1"), "1");
|
||||
assert_eq!(eval!("1.5"), "1.5");
|
||||
assert_eq!(eval!("1"), "1");
|
||||
assert_eq!(eval!("1.5"), "1.5");
|
||||
|
||||
assert_eq!(eval!("+1"), "1");
|
||||
assert_eq!(eval!("-1"), "-1");
|
||||
assert_eq!(eval!("+1"), "1");
|
||||
assert_eq!(eval!("-1"), "-1");
|
||||
|
||||
assert_eq!(eval!("1 + 2"), "3");
|
||||
assert_eq!(eval!("1 + 2 + 3"), "6");
|
||||
assert_eq!(eval!("1 + 2.5"), "3.5");
|
||||
assert_eq!(eval!("1 + 2.5"), "3.5");
|
||||
assert_eq!(eval!("2 - 1"), "1");
|
||||
assert_eq!(eval!("1 - 2"), "-1");
|
||||
assert_eq!(eval!("2 * 3"), "6");
|
||||
assert_eq!(eval!("2 * 3.5"), "7");
|
||||
assert_eq!(eval!("6 / 2"), "3");
|
||||
assert_eq!(eval!("6 / 4"), "1.5");
|
||||
assert_eq!(eval!("2 ^ 4"), "16");
|
||||
assert_eq!(eval!("3 % 2"), "1");
|
||||
assert_eq!(eval!("1 + 2"), "3");
|
||||
assert_eq!(eval!("1 + 2 + 3"), "6");
|
||||
assert_eq!(eval!("1 + 2.5"), "3.5");
|
||||
assert_eq!(eval!("1 + 2.5"), "3.5");
|
||||
assert_eq!(eval!("2 - 1"), "1");
|
||||
assert_eq!(eval!("1 - 2"), "-1");
|
||||
assert_eq!(eval!("2 * 3"), "6");
|
||||
assert_eq!(eval!("2 * 3.5"), "7");
|
||||
assert_eq!(eval!("6 / 2"), "3");
|
||||
assert_eq!(eval!("6 / 4"), "1.5");
|
||||
assert_eq!(eval!("2 ^ 4"), "16");
|
||||
assert_eq!(eval!("3 % 2"), "1");
|
||||
|
||||
assert_eq!(eval!("2 * 3 + 4"), "10");
|
||||
assert_eq!(eval!("2 * (3 + 4)"), "14");
|
||||
assert_eq!(eval!("2 ^ 4 + 1"), "17");
|
||||
assert_eq!(eval!("1 + 2 ^ 4"), "17");
|
||||
assert_eq!(eval!("2 * 3 + 4"), "10");
|
||||
assert_eq!(eval!("2 * (3 + 4)"), "14");
|
||||
assert_eq!(eval!("2 ^ 4 + 1"), "17");
|
||||
assert_eq!(eval!("1 + 2 ^ 4"), "17");
|
||||
assert_eq!(eval!("1 + 3 * 2 ^ 4 * 2 + 3"), "100");
|
||||
}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
use crate::{api, sys};
|
||||
use crate::api::console::Style;
|
||||
use crate::api::fs;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::prompt::Prompt;
|
||||
use crate::{api, sys};
|
||||
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use littlewing::color::*;
|
||||
use littlewing::chess::*;
|
||||
use littlewing::fen::FEN;
|
||||
use lazy_static::lazy_static;
|
||||
use littlewing::chess::*;
|
||||
use littlewing::color::*;
|
||||
use littlewing::fen::FEN;
|
||||
use spin::Mutex;
|
||||
|
||||
lazy_static! {
|
||||
|
@ -20,7 +20,9 @@ lazy_static! {
|
|||
}
|
||||
|
||||
const FEN: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
const COMMANDS: [&str; 8] = ["quit", "help", "init", "time", "move", "undo", "show", "perf"];
|
||||
const COMMANDS: [&str; 8] = [
|
||||
"quit", "help", "init", "time", "move", "undo", "show", "perf",
|
||||
];
|
||||
|
||||
fn update_autocomplete(prompt: &mut Prompt, game: &mut Game) {
|
||||
*MOVES.lock() = game.get_moves().into_iter().map(|m| m.to_lan()).collect();
|
||||
|
@ -29,13 +31,15 @@ fn update_autocomplete(prompt: &mut Prompt, game: &mut Game) {
|
|||
let mut entries = Vec::new();
|
||||
let args: Vec<&str> = line.split(' ').collect();
|
||||
let i = args.len() - 1;
|
||||
if i == 0 { // Autocomplete command
|
||||
if i == 0 {
|
||||
// Autocomplete command
|
||||
for &cmd in &COMMANDS {
|
||||
if let Some(entry) = cmd.strip_prefix(args[i]) {
|
||||
entries.push(entry.into());
|
||||
}
|
||||
}
|
||||
} else if i == 1 && (args[0] == "move" || args[0] == "m") { // Autocomplete moves
|
||||
} else if i == 1 && (args[0] == "move" || args[0] == "m") {
|
||||
// Autocomplete moves
|
||||
for m in &*MOVES.lock() {
|
||||
if let Some(entry) = m.strip_prefix(args[1]) {
|
||||
entries.push(entry.into());
|
||||
|
@ -78,9 +82,10 @@ impl Chess {
|
|||
let history_file = "~/.chess-history";
|
||||
prompt.history.load(history_file);
|
||||
|
||||
self.game.show_coordinates = true;
|
||||
self.game.clock = Clock::new(40, 5 * 60 * 1000); // 40 moves in 5 minutes
|
||||
// 40 moves in 5 minutes
|
||||
self.game.clock = Clock::new(40, 5 * 60 * 1000);
|
||||
self.game.clock.system_time = Arc::new(system_time);
|
||||
self.game.show_coordinates = true;
|
||||
let size = 1 << 20; // 1 MB
|
||||
self.game.tt_resize(size);
|
||||
self.game.load_fen(FEN).unwrap();
|
||||
|
@ -99,7 +104,7 @@ impl Chess {
|
|||
"u" | "undo" => self.cmd_undo(args),
|
||||
"l" | "load" => self.cmd_load(args),
|
||||
"s" | "save" => self.cmd_save(args),
|
||||
"perf" => self.cmd_perf(args),
|
||||
"perf" => self.cmd_perf(args),
|
||||
cmd => {
|
||||
if cmd.is_empty() {
|
||||
println!();
|
||||
|
@ -117,21 +122,28 @@ impl Chess {
|
|||
fn cmd_help(&mut self, _args: Vec<&str>) {
|
||||
println!("{}Commands:{}", self.csi_notif, self.csi_reset);
|
||||
let cmds = [
|
||||
("q", "uit", "Exit this program\n"),
|
||||
("h", "elp", "Display this screen\n"),
|
||||
("i", "nit", "Initialize a new game\n"),
|
||||
("t", "ime <moves> <time>", "Set clock to <moves> in <time> (in seconds)\n"),
|
||||
("p", "lay [<side>]", "Play <side> on the board\n"),
|
||||
("m", "ove <move>", "Play <move> on the board\n"),
|
||||
("u", "ndo", "Undo the last move\n"),
|
||||
("l", "oad <file>", "Load game from <file>\n"),
|
||||
("s", "ave <file>", "Save game to <file>\n"),
|
||||
("", "perf [<depth>] ", "Count the nodes at each depth\n"),
|
||||
("q", "uit", "Exit this program\n"),
|
||||
("h", "elp", "Display this screen\n"),
|
||||
("i", "nit", "Initialize a new game\n"),
|
||||
(
|
||||
"t",
|
||||
"ime <moves> <time>",
|
||||
"Set clock to <moves> in <time> (in seconds)\n",
|
||||
),
|
||||
("p", "lay [<side>]", "Play <side> on the board\n"),
|
||||
("m", "ove <move>", "Play <move> on the board\n"),
|
||||
("u", "ndo", "Undo the last move\n"),
|
||||
("l", "oad <file>", "Load game from <file>\n"),
|
||||
("s", "ave <file>", "Save game to <file>\n"),
|
||||
("", "perf [<depth>] ", "Count the nodes at each depth\n"),
|
||||
];
|
||||
for (alias, command, usage) in &cmds {
|
||||
let csi_col1 = Style::color("LightGreen");
|
||||
let csi_col2 = Style::color("LightCyan");
|
||||
print!(" {}{}{}{:20}{}{}", csi_col1, alias, csi_col2, command, self.csi_reset, usage);
|
||||
print!(
|
||||
" {}{}{}{:20}{}{}",
|
||||
csi_col1, alias, csi_col2, command, self.csi_reset, usage
|
||||
);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
@ -152,9 +164,15 @@ impl Chess {
|
|||
if let Ok(contents) = fs::read_to_string(path) {
|
||||
if self.game.load_fen(&contents).is_ok() {
|
||||
self.side = self.game.side() ^ 1;
|
||||
let color = if self.game.side() == WHITE { "white" } else { "black" };
|
||||
let color = if self.game.side() == WHITE {
|
||||
"white"
|
||||
} else {
|
||||
"black"
|
||||
};
|
||||
println!();
|
||||
println!("{}<{} play {}", self.csi_color, self.csi_reset, color);
|
||||
println!(
|
||||
"{}<{} play {}", self.csi_color, self.csi_reset, color
|
||||
);
|
||||
println!();
|
||||
println!("{}", self.game);
|
||||
} else {
|
||||
|
@ -171,7 +189,8 @@ impl Chess {
|
|||
return;
|
||||
}
|
||||
let path = args[1];
|
||||
if fs::write(path, format!("{}\n", self.game.to_fen()).as_bytes()).is_ok() {
|
||||
let contents = format!("{}\n", self.game.to_fen());
|
||||
if fs::write(path, contents.as_bytes()).is_ok() {
|
||||
println!();
|
||||
} else {
|
||||
error!("Could not write to '{}'\n", path);
|
||||
|
@ -183,12 +202,12 @@ impl Chess {
|
|||
1 => {
|
||||
error!("No <moves> and <time> given\n");
|
||||
return;
|
||||
},
|
||||
}
|
||||
2 => {
|
||||
error!("No <time> given\n");
|
||||
return;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Ok(moves) = args[1].parse::<u16>() {
|
||||
if let Ok(time) = args[2].parse::<f64>() {
|
||||
|
@ -286,9 +305,13 @@ impl Chess {
|
|||
loop {
|
||||
let started_at = (self.game.clock.system_time)();
|
||||
let n = self.game.perft(depth);
|
||||
let s = (((self.game.clock.system_time)() - started_at) as f64) / 1000.0;
|
||||
let t = (self.game.clock.system_time)();
|
||||
let s = ((t - started_at) as f64) / 1000.0;
|
||||
let nps = (n as f64) / s;
|
||||
println!("{}{}:{} {}{} ({:.2} s, {:.2e} nps)", csi_depth, depth, csi_count, n, csi_reset, s, nps);
|
||||
println!(
|
||||
"{}{}:{} {}{} ({:.2} s, {:.2e} nps)",
|
||||
csi_depth, depth, csi_count, n, csi_reset, s, nps
|
||||
);
|
||||
|
||||
if args.len() > 1 || sys::console::end_of_text() {
|
||||
break;
|
||||
|
@ -301,11 +324,15 @@ impl Chess {
|
|||
|
||||
fn play(&mut self) {
|
||||
let time = (self.game.clock.allocated_time() as f64) / 1000.0;
|
||||
print!("{}<{} wait {:.2} seconds{}", self.csi_color, self.csi_notif, time, self.csi_reset);
|
||||
print!(
|
||||
"{}<{} wait {:.2} seconds{}",
|
||||
self.csi_color, self.csi_notif, time, self.csi_reset
|
||||
);
|
||||
let r = self.game.search(1..99);
|
||||
print!("\x1b[2K\x1b[1G");
|
||||
if let Some(m) = r {
|
||||
println!("{}<{} move {}", self.csi_color, self.csi_reset, m.to_lan());
|
||||
let s = m.to_lan();
|
||||
println!("{}<{} move {}", self.csi_color, self.csi_reset, s);
|
||||
println!();
|
||||
self.game.make_move(m);
|
||||
self.game.history.push(m);
|
||||
|
@ -345,7 +372,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
for &arg in args {
|
||||
match arg {
|
||||
"-h" | "--help" => return help(),
|
||||
_ => {},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let mut chess = Chess::new();
|
||||
|
@ -357,6 +384,9 @@ pub fn help() -> Result<(), ExitCode> {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} chess {}{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} chess {}{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
help();
|
||||
return Ok(());
|
||||
}
|
||||
_ => continue
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,5 +38,8 @@ fn help() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} copy {}<src> <dst>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} copy {}<src> <dst>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,11 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
if args.len() > 2 {
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
let format = if args.len() > 1 { args[1] } else { DATE_TIME_ZONE };
|
||||
let format = if args.len() > 1 {
|
||||
args[1]
|
||||
} else {
|
||||
DATE_TIME_ZONE
|
||||
};
|
||||
if format == "-h" || format == "--help" {
|
||||
return help();
|
||||
}
|
||||
|
@ -28,6 +32,9 @@ fn help() -> Result<(), ExitCode> {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} date {}[<format>]{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} date {}[<format>]{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -15,15 +15,15 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
help();
|
||||
return Ok(());
|
||||
}
|
||||
_ => continue
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
for arg in &args[1..] {
|
||||
let mut pathname = *arg;
|
||||
|
||||
// The commands `delete /usr/alice/` and `delete /usr/alice` are equivalent,
|
||||
// but `delete /` should not be modified.
|
||||
// The commands `delete /usr/alice/` and `delete /usr/alice`
|
||||
// are equivalent, but `delete /` should not be modified.
|
||||
if pathname.len() > 1 {
|
||||
pathname = pathname.trim_end_matches('/');
|
||||
}
|
||||
|
@ -52,7 +52,10 @@ fn help() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} delete {}<path>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} delete {}<path>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
println!();
|
||||
println!("{}Paths:{}", csi_title, csi_reset);
|
||||
println!(" {0}<dir>/{1} Delete directory", csi_option, csi_reset);
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
use crate::{sys, usr, debug};
|
||||
use crate::api::clock;
|
||||
use crate::api::console::Style;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::syscall;
|
||||
use crate::debug;
|
||||
use crate::sys::console;
|
||||
use crate::sys::net;
|
||||
use crate::usr::shell;
|
||||
|
||||
use alloc::format;
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use smoltcp::iface::SocketSet;
|
||||
use smoltcp::socket::dhcpv4;
|
||||
use smoltcp::time::Instant;
|
||||
|
@ -24,7 +27,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some((ref mut iface, ref mut device)) = *sys::net::NET.lock() {
|
||||
if let Some((ref mut iface, ref mut device)) = *net::NET.lock() {
|
||||
let dhcp_socket = dhcpv4::Socket::new();
|
||||
let mut sockets = SocketSet::new(vec![]);
|
||||
let dhcp_handle = sockets.add(dhcp_socket);
|
||||
|
@ -38,30 +41,32 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
error!("Timeout reached");
|
||||
return Err(ExitCode::Failure);
|
||||
}
|
||||
if sys::console::end_of_text() || sys::console::end_of_transmission() {
|
||||
if console::end_of_text() || console::end_of_transmission() {
|
||||
eprintln!();
|
||||
return Err(ExitCode::Failure);
|
||||
}
|
||||
|
||||
let time = Instant::from_micros((clock::realtime() * 1000000.0) as i64);
|
||||
let ms = (clock::realtime() * 1000000.0) as i64;
|
||||
let time = Instant::from_micros(ms);
|
||||
iface.poll(time, device, &mut sockets);
|
||||
let event = sockets.get_mut::<dhcpv4::Socket>(dhcp_handle).poll();
|
||||
|
||||
match event {
|
||||
None => {}
|
||||
Some(dhcpv4::Event::Configured(config)) => {
|
||||
dhcp_config = Some((config.address, config.router, config.dns_servers));
|
||||
dhcp_config = Some(
|
||||
(config.address, config.router, config.dns_servers)
|
||||
);
|
||||
if verbose {
|
||||
debug!("DHCP Offer received");
|
||||
}
|
||||
break;
|
||||
}
|
||||
Some(dhcpv4::Event::Deconfigured) => {
|
||||
}
|
||||
Some(dhcpv4::Event::Deconfigured) => {}
|
||||
}
|
||||
|
||||
if let Some(wait_duration) = iface.poll_delay(time, &sockets) {
|
||||
syscall::sleep((wait_duration.total_micros() as f64) / 1000000.0);
|
||||
if let Some(d) = iface.poll_delay(time, &sockets) {
|
||||
syscall::sleep((d.total_micros() as f64) / 1000000.0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -70,21 +75,21 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
|
||||
if let Some((address, router, dns_servers)) = dhcp_config {
|
||||
usr::shell::exec(&format!("net config ip {}", address)).ok();
|
||||
usr::shell::exec("net config ip").ok();
|
||||
shell::exec(&format!("net config ip {}", address)).ok();
|
||||
shell::exec("net config ip").ok();
|
||||
|
||||
if let Some(router) = router {
|
||||
usr::shell::exec(&format!("net config gw {}", router)).ok();
|
||||
shell::exec(&format!("net config gw {}", router)).ok();
|
||||
} else {
|
||||
usr::shell::exec("net config gw 0.0.0.0").ok();
|
||||
shell::exec("net config gw 0.0.0.0").ok();
|
||||
}
|
||||
usr::shell::exec("net config gw").ok();
|
||||
shell::exec("net config gw").ok();
|
||||
|
||||
let dns: Vec<_> = dns_servers.iter().map(|s| s.to_string()).collect();
|
||||
if !dns.is_empty() {
|
||||
usr::shell::exec(&format!("net config dns {}", dns.join(","))).ok();
|
||||
shell::exec(&format!("net config dns {}", dns.join(","))).ok();
|
||||
}
|
||||
usr::shell::exec("net config dns").ok();
|
||||
shell::exec("net config dns").ok();
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -96,9 +101,15 @@ fn help() -> Result<(), ExitCode> {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} dhcp {}<options>{1}", csi_title, csi_reset, csi_option);
|
||||
println!(
|
||||
"{}Usage:{} dhcp {}<options>{1}",
|
||||
csi_title, csi_reset, csi_option
|
||||
);
|
||||
println!();
|
||||
println!("{}Options:{}", csi_title, csi_reset);
|
||||
println!(" {0}-v{1}, {0}--verbose{1} Increase verbosity", csi_option, csi_reset);
|
||||
println!(
|
||||
" {0}-v{1}, {0}--verbose{1} Increase verbosity",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -4,27 +4,20 @@ use crate::api::process::ExitCode;
|
|||
use crate::api::unit::SizeUnit;
|
||||
use crate::sys;
|
||||
use crate::sys::ata::Drive;
|
||||
use crate::sys::console;
|
||||
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
match *args.get(1).unwrap_or(&"") {
|
||||
"f" | "format" if args.len() == 3 => {
|
||||
format(args[2])
|
||||
}
|
||||
"e" | "erase" if args.len() == 3 => {
|
||||
erase(args[2])
|
||||
}
|
||||
"u" | "usage" => {
|
||||
usage(&args[2..])
|
||||
}
|
||||
"l" | "list" => {
|
||||
list()
|
||||
}
|
||||
"f" | "format" if args.len() == 3 => format(args[2]),
|
||||
"e" | "erase" if args.len() == 3 => erase(args[2]),
|
||||
"u" | "usage" => usage(&args[2..]),
|
||||
"l" | "list" => list(),
|
||||
"-h" | "--help" => {
|
||||
help();
|
||||
Ok(())
|
||||
|
@ -62,6 +55,10 @@ fn format(pathname: &str) -> Result<(), ExitCode> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_canceled() -> bool {
|
||||
console::end_of_text() || console::end_of_transmission()
|
||||
}
|
||||
|
||||
fn erase(pathname: &str) -> Result<(), ExitCode> {
|
||||
match parse_disk_path(pathname) {
|
||||
Ok((bus, dsk)) => {
|
||||
|
@ -74,7 +71,7 @@ fn erase(pathname: &str) -> Result<(), ExitCode> {
|
|||
let buf = vec![0; drive.block_size() as usize];
|
||||
print!("\x1b[?25l"); // Disable cursor
|
||||
for i in 0..n {
|
||||
if sys::console::end_of_text() || sys::console::end_of_transmission() {
|
||||
if is_canceled() {
|
||||
println!();
|
||||
print!("\x1b[?25h"); // Enable cursor
|
||||
return Err(ExitCode::Failure);
|
||||
|
@ -128,14 +125,32 @@ fn usage(args: &[&str]) -> Result<(), ExitCode> {
|
|||
let size = sys::fs::disk_size();
|
||||
let used = sys::fs::disk_used();
|
||||
let free = size - used;
|
||||
let width = [size, used, free].iter().fold(0, |acc, num| {
|
||||
let width = [size, used, free].iter().fold(0, |acc, num|
|
||||
core::cmp::max(acc, unit.format(*num).len())
|
||||
});
|
||||
);
|
||||
let color = Style::color("LightCyan");
|
||||
let reset = Style::reset();
|
||||
println!("{}size:{} {:>width$}", color, reset, unit.format(size), width = width);
|
||||
println!("{}used:{} {:>width$}", color, reset, unit.format(used), width = width);
|
||||
println!("{}free:{} {:>width$}", color, reset, unit.format(free), width = width);
|
||||
println!(
|
||||
"{}size:{} {:>width$}",
|
||||
color,
|
||||
reset,
|
||||
unit.format(size),
|
||||
width = width
|
||||
);
|
||||
println!(
|
||||
"{}used:{} {:>width$}",
|
||||
color,
|
||||
reset,
|
||||
unit.format(used),
|
||||
width = width
|
||||
);
|
||||
println!(
|
||||
"{}free:{} {:>width$}",
|
||||
color,
|
||||
reset,
|
||||
unit.format(free),
|
||||
width = width
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -143,21 +158,36 @@ fn help_usage() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} disk usage {}<options>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} disk usage {}<options>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
println!();
|
||||
println!("{}Options:{}", csi_title, csi_reset);
|
||||
println!(" {0}-b{1}, {0}--binary-size{1} Use binary size", csi_option, csi_reset);
|
||||
println!(
|
||||
" {0}-b{1}, {0}--binary-size{1} Use binary size",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
||||
fn help() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} disk {}<command>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} disk {}<command>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
println!();
|
||||
println!("{}Commands:{}", csi_title, csi_reset);
|
||||
println!(" {}list{} List detected disks", csi_option, csi_reset);
|
||||
println!(" {}usage{} List disk usage", csi_option, csi_reset);
|
||||
println!(
|
||||
" {}list{} List detected disks",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
println!(
|
||||
" {}usage{} List disk usage",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
println!(" {}format <path>{} Format disk", csi_option, csi_reset);
|
||||
println!(" {}erase <path>{} Erase disk", csi_option, csi_reset);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::sys;
|
||||
use crate::api::{console, fs, io};
|
||||
use crate::api::console::Style;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::{console, fs, io};
|
||||
use crate::sys;
|
||||
|
||||
use alloc::format;
|
||||
use alloc::string::{String, ToString};
|
||||
|
@ -56,7 +56,7 @@ impl Editor {
|
|||
for line in contents.split('\n') {
|
||||
lines.push(line.into());
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(_) => {
|
||||
lines.push(String::new());
|
||||
}
|
||||
|
@ -64,7 +64,15 @@ impl Editor {
|
|||
|
||||
let pathname = pathname.into();
|
||||
|
||||
Self { pathname, clipboard, lines, cursor, offset, highlighted, config }
|
||||
Self {
|
||||
pathname,
|
||||
clipboard,
|
||||
lines,
|
||||
cursor,
|
||||
offset,
|
||||
highlighted,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save(&mut self) -> Result<(), ExitCode> {
|
||||
|
@ -89,11 +97,15 @@ impl Editor {
|
|||
}
|
||||
|
||||
fn print_status(&mut self, status: &str, background: &str) {
|
||||
// Move cursor to the bottom of the screen
|
||||
print!("\x1b[{};1H", self.rows() + 1);
|
||||
|
||||
let color = Style::color("Black").with_background(background);
|
||||
let reset = Style::reset();
|
||||
print!("\x1b[{};1H", self.rows() + 1); // Move cursor to the bottom of the screen
|
||||
print!("{}{:cols$}{}", color, status, reset, cols = self.cols());
|
||||
print!("\x1b[{};{}H", self.cursor.y + 1, self.cursor.x + 1); // Move cursor back
|
||||
|
||||
// Move cursor back
|
||||
print!("\x1b[{};{}H", self.cursor.y + 1, self.cursor.x + 1);
|
||||
}
|
||||
|
||||
fn print_editing_status(&mut self) {
|
||||
|
@ -128,9 +140,14 @@ impl Editor {
|
|||
|
||||
fn render_line(&self, y: usize) -> String {
|
||||
// Render line into a row of the screen, or an empty row when past eof
|
||||
let line = if y < self.lines.len() { &self.lines[y] } else { "" };
|
||||
let line = if y < self.lines.len() {
|
||||
&self.lines[y]
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let mut row: Vec<char> = format!("{:cols$}", line, cols = self.offset.x).chars().collect();
|
||||
let s = format!("{:cols$}", line, cols = self.offset.x);
|
||||
let mut row: Vec<char> = s.chars().collect();
|
||||
let n = self.offset.x + self.cols();
|
||||
let after = if row.len() > n {
|
||||
row.truncate(n - 1);
|
||||
|
@ -144,9 +161,9 @@ impl Editor {
|
|||
|
||||
fn render_char(&self, c: char) -> Option<String> {
|
||||
match c {
|
||||
'\t' => Some(" ".repeat(self.config.tab_size)),
|
||||
'\t' => Some(" ".repeat(self.config.tab_size)),
|
||||
c if console::is_printable(c) => Some(c.to_string()),
|
||||
_ => None,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,13 +177,16 @@ impl Editor {
|
|||
if cursor == closing {
|
||||
for (y, line) in self.lines.iter().enumerate() {
|
||||
for (x, c) in line.chars().enumerate() {
|
||||
if oy + cy == y && ox + cx == x { // Cursor position
|
||||
if oy + cy == y && ox + cx == x {
|
||||
// Cursor position
|
||||
if let Some((x, y)) = stack.pop() {
|
||||
self.highlighted.push((cx, cy, closing));
|
||||
let is_col = ox <= x && x < ox + self.cols();
|
||||
let is_row = oy <= y && y < oy + self.rows();
|
||||
if is_col && is_row {
|
||||
self.highlighted.push((x - ox, y - oy, opening));
|
||||
self.highlighted.push(
|
||||
(x - ox, y - oy, opening)
|
||||
);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
@ -186,7 +206,8 @@ impl Editor {
|
|||
if cursor == opening {
|
||||
for (y, line) in self.lines.iter().enumerate().skip(oy + cy) {
|
||||
for (x, c) in line.chars().enumerate() {
|
||||
if y == oy + cy && x <= ox + cx { // Skip chars before cursor
|
||||
if y == oy + cy && x <= ox + cx {
|
||||
// Skip chars before cursor
|
||||
continue;
|
||||
}
|
||||
if c == opening {
|
||||
|
@ -198,7 +219,9 @@ impl Editor {
|
|||
let is_col = ox <= x && x < ox + self.cols();
|
||||
let is_row = oy <= y && y < oy + self.rows();
|
||||
if is_col && is_row {
|
||||
self.highlighted.push((x - ox, y - oy, closing));
|
||||
self.highlighted.push(
|
||||
(x - ox, y - oy, closing)
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -262,7 +285,7 @@ impl Editor {
|
|||
}
|
||||
|
||||
pub fn run(&mut self) -> Result<(), ExitCode> {
|
||||
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move cursor to top
|
||||
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move to top
|
||||
self.print_screen();
|
||||
self.print_editing_status();
|
||||
self.print_highlighted();
|
||||
|
@ -281,37 +304,38 @@ impl Editor {
|
|||
'\x1B' => { // ESC
|
||||
escape = true;
|
||||
continue;
|
||||
},
|
||||
}
|
||||
'[' if escape => {
|
||||
csi = true;
|
||||
csi_params.clear();
|
||||
continue;
|
||||
},
|
||||
}
|
||||
'\0' => {
|
||||
continue;
|
||||
}
|
||||
'\x11' | '\x03' => { // Ctrl Q or Ctrl C
|
||||
// TODO: Warn if modifications have not been saved
|
||||
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move cursor to top
|
||||
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move to top
|
||||
print!("\x1b[?25h"); // Enable cursor
|
||||
break;
|
||||
},
|
||||
}
|
||||
'\x17' => { // Ctrl W
|
||||
self.save().ok();
|
||||
print!("\x1b[?25h"); // Enable cursor
|
||||
continue;
|
||||
},
|
||||
}
|
||||
'\x18' => { // Ctrl X
|
||||
let res = self.save();
|
||||
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move cursor to top
|
||||
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move to top
|
||||
print!("\x1b[?25h"); // Enable cursor
|
||||
return res;
|
||||
},
|
||||
}
|
||||
'\n' => { // Newline
|
||||
let y = self.offset.y + self.cursor.y;
|
||||
let old_line = self.lines[y].clone();
|
||||
let mut row: Vec<char> = old_line.chars().collect();
|
||||
let new_line = row.split_off(self.offset.x + self.cursor.x).into_iter().collect();
|
||||
let new_line = row.
|
||||
split_off(self.offset.x + self.cursor.x).
|
||||
into_iter().collect();
|
||||
self.lines[y] = row.into_iter().collect();
|
||||
self.lines.insert(y + 1, new_line);
|
||||
if self.cursor.y == self.rows() - 1 {
|
||||
|
@ -322,21 +346,22 @@ impl Editor {
|
|||
self.cursor.x = 0;
|
||||
self.offset.x = 0;
|
||||
self.print_screen();
|
||||
},
|
||||
}
|
||||
'~' if csi && csi_params == "5" => { // Page Up
|
||||
let scroll = self.rows() - 1; // Keep one previous line on screen
|
||||
let scroll = self.rows() - 1; // Keep one line on screen
|
||||
self.offset.y -= cmp::min(scroll, self.offset.y);
|
||||
self.print_screen();
|
||||
},
|
||||
}
|
||||
'~' if csi && csi_params == "6" => { // Page Down
|
||||
let scroll = self.rows() - 1; // Keep one previous line on screen
|
||||
let remaining = cmp::max(self.lines.len(), 1) - self.offset.y - 1;
|
||||
let scroll = self.rows() - 1; // Keep one line on screen
|
||||
let n = cmp::max(self.lines.len(), 1);
|
||||
let remaining = n - self.offset.y - 1;
|
||||
self.offset.y += cmp::min(scroll, remaining);
|
||||
if self.cursor.y + scroll > remaining {
|
||||
self.cursor.y = 0;
|
||||
}
|
||||
self.print_screen();
|
||||
},
|
||||
}
|
||||
'A' if csi => { // Arrow Up
|
||||
if self.cursor.y > 0 {
|
||||
self.cursor.y -= 1
|
||||
|
@ -345,11 +370,12 @@ impl Editor {
|
|||
}
|
||||
self.align_cursor();
|
||||
self.print_screen();
|
||||
},
|
||||
}
|
||||
'B' if csi => { // Arrow Down
|
||||
let is_eof = self.offset.y + self.cursor.y == self.lines.len() - 1;
|
||||
let n = self.lines.len() - 1;
|
||||
let is_eof = n == (self.offset.y + self.cursor.y);
|
||||
let is_bottom = self.cursor.y == self.rows() - 1;
|
||||
if self.cursor.y < cmp::min(self.rows(), self.lines.len() - 1) {
|
||||
if self.cursor.y < cmp::min(self.rows(), n) {
|
||||
if is_bottom || is_eof {
|
||||
if !is_eof {
|
||||
self.offset.y += 1;
|
||||
|
@ -360,14 +386,16 @@ impl Editor {
|
|||
self.align_cursor();
|
||||
self.print_screen();
|
||||
}
|
||||
},
|
||||
}
|
||||
'C' if csi => { // Arrow Right
|
||||
let line = &self.lines[self.offset.y + self.cursor.y];
|
||||
if line.is_empty() || self.cursor.x + self.offset.x >= line.chars().count() {
|
||||
let x = self.cursor.x + self.offset.x;
|
||||
let n = line.chars().count();
|
||||
if line.is_empty() || x >= n {
|
||||
print!("\x1b[?25h"); // Enable cursor
|
||||
escape = false;
|
||||
csi = false;
|
||||
continue
|
||||
continue;
|
||||
} else if self.cursor.x == self.cols() - 1 {
|
||||
self.offset.x += self.cols();
|
||||
self.cursor.x -= self.cols() - 1;
|
||||
|
@ -375,7 +403,7 @@ impl Editor {
|
|||
} else {
|
||||
self.cursor.x += 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
'D' if csi => { // Arrow Left
|
||||
if self.cursor.x + self.offset.x == 0 {
|
||||
print!("\x1b[?25h"); // Enable cursor
|
||||
|
@ -390,36 +418,37 @@ impl Editor {
|
|||
} else {
|
||||
self.cursor.x -= 1;
|
||||
}
|
||||
},
|
||||
}
|
||||
'Z' if csi => { // Backtab (Shift + Tab)
|
||||
// Do nothing
|
||||
},
|
||||
// Do nothing
|
||||
}
|
||||
'\x14' => { // Ctrl T -> Go to top of file
|
||||
self.cursor.x = 0;
|
||||
self.cursor.y = 0;
|
||||
self.offset.x = 0;
|
||||
self.offset.y = 0;
|
||||
self.print_screen();
|
||||
},
|
||||
}
|
||||
'\x02' => { // Ctrl B -> Go to bottom of file
|
||||
self.cursor.x = 0;
|
||||
self.cursor.y = cmp::min(self.rows(), self.lines.len()) - 1;
|
||||
self.offset.x = 0;
|
||||
self.offset.y = self.lines.len() - 1 - self.cursor.y;
|
||||
self.print_screen();
|
||||
},
|
||||
}
|
||||
'\x01' => { // Ctrl A -> Go to beginning of line
|
||||
self.cursor.x = 0;
|
||||
self.offset.x = 0;
|
||||
self.print_screen();
|
||||
},
|
||||
}
|
||||
'\x05' => { // Ctrl E -> Go to end of line
|
||||
let n = self.lines[self.offset.y + self.cursor.y].chars().count();
|
||||
let line = &self.lines[self.offset.y + self.cursor.y];
|
||||
let n = line.chars().count();
|
||||
let w = self.cols();
|
||||
self.cursor.x = n % w;
|
||||
self.offset.x = w * (n / w);
|
||||
self.print_screen();
|
||||
},
|
||||
}
|
||||
'\x04' => { // Ctrl D -> Delete (cut) line
|
||||
let i = self.offset.y + self.cursor.y;
|
||||
self.clipboard.push(self.lines.remove(i));
|
||||
|
@ -427,8 +456,8 @@ impl Editor {
|
|||
self.lines.push(String::new());
|
||||
}
|
||||
|
||||
// Move cursor up to the previous line
|
||||
if i >= self.lines.len() {
|
||||
// Move cursor up to the previous line
|
||||
if self.cursor.y > 0 {
|
||||
self.cursor.y -= 1;
|
||||
} else if self.offset.y > 0 {
|
||||
|
@ -439,11 +468,11 @@ impl Editor {
|
|||
self.offset.x = 0;
|
||||
|
||||
self.print_screen();
|
||||
},
|
||||
}
|
||||
'\x19' => { // Ctrl Y -> Yank (copy) line
|
||||
let i = self.offset.y + self.cursor.y;
|
||||
self.clipboard.push(self.lines[i].clone());
|
||||
},
|
||||
}
|
||||
'\x10' => { // Ctrl P -> Put (paste) line
|
||||
let i = self.offset.y + self.cursor.y;
|
||||
if let Some(line) = self.clipboard.pop() {
|
||||
|
@ -452,11 +481,12 @@ impl Editor {
|
|||
self.cursor.x = 0;
|
||||
self.offset.x = 0;
|
||||
self.print_screen();
|
||||
},
|
||||
}
|
||||
'\x08' => { // Backspace
|
||||
let y = self.offset.y + self.cursor.y;
|
||||
if self.offset.x + self.cursor.x > 0 { // Remove char from line
|
||||
let mut row: Vec<char> = self.lines[y].chars().collect();
|
||||
if self.offset.x + self.cursor.x > 0 {
|
||||
// Remove char from line
|
||||
let mut row: Vec<_> = self.lines[y].chars().collect();
|
||||
row.remove(self.offset.x + self.cursor.x - 1);
|
||||
self.lines[y] = row.into_iter().collect();
|
||||
|
||||
|
@ -469,7 +499,8 @@ impl Editor {
|
|||
let line = self.render_line(y);
|
||||
print!("\x1b[2K\x1b[1G{}", line);
|
||||
}
|
||||
} else { // Remove newline from previous line
|
||||
} else {
|
||||
// Remove newline from previous line
|
||||
if self.cursor.y == 0 && self.offset.y == 0 {
|
||||
print!("\x1b[?25h"); // Enable cursor
|
||||
escape = false;
|
||||
|
@ -496,30 +527,33 @@ impl Editor {
|
|||
|
||||
self.print_screen();
|
||||
}
|
||||
},
|
||||
'\x7f' => { // Delete
|
||||
}
|
||||
'\x7f' => {
|
||||
// Delete
|
||||
let y = self.offset.y + self.cursor.y;
|
||||
let n = self.lines[y].chars().count();
|
||||
if self.offset.x + self.cursor.x >= n { // Remove newline from line
|
||||
if self.offset.x + self.cursor.x >= n {
|
||||
// Remove newline from line
|
||||
if y + 1 < self.lines.len() {
|
||||
let line = self.lines.remove(y + 1);
|
||||
self.lines[y].push_str(&line);
|
||||
self.print_screen();
|
||||
}
|
||||
} else { // Remove char from line
|
||||
} else {
|
||||
// Remove char from line
|
||||
self.lines[y].remove(self.offset.x + self.cursor.x);
|
||||
let line = self.render_line(y);
|
||||
print!("\x1b[2K\x1b[1G{}", line);
|
||||
}
|
||||
},
|
||||
}
|
||||
c if csi => {
|
||||
csi_params.push(c);
|
||||
continue;
|
||||
},
|
||||
}
|
||||
c => {
|
||||
if let Some(s) = self.render_char(c) {
|
||||
let y = self.offset.y + self.cursor.y;
|
||||
let mut row: Vec<char> = self.lines[y].chars().collect();
|
||||
let mut row: Vec<_> = self.lines[y].chars().collect();
|
||||
for c in s.chars() {
|
||||
row.insert(self.offset.x + self.cursor.x, c);
|
||||
self.cursor.x += 1;
|
||||
|
@ -530,11 +564,11 @@ impl Editor {
|
|||
self.cursor.x -= self.cols();
|
||||
self.print_screen();
|
||||
} else {
|
||||
let line = self.render_line(self.offset.y + self.cursor.y);
|
||||
let line = self.render_line(y);
|
||||
print!("\x1b[2K\x1b[1G{}", line);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
self.print_editing_status();
|
||||
self.print_highlighted();
|
||||
|
@ -565,5 +599,8 @@ fn help() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} edit {}<file>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} edit {}<file>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,10 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
let size = section.size();
|
||||
let align = section.align();
|
||||
println!();
|
||||
println!("{}{}{} (addr: {:#X}, size: {}, align: {})", color, name, reset, addr, size, align);
|
||||
println!(
|
||||
"{}{}{} (addr: {:#X}, size: {}, align: {})",
|
||||
color, name, reset, addr, size, align
|
||||
);
|
||||
if let Ok(data) = section.data() {
|
||||
usr::hex::print_hex_at(data, addr);
|
||||
}
|
||||
|
@ -52,5 +55,8 @@ fn help() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} elf {}<binary>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} elf {}<binary>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,12 +10,14 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
help();
|
||||
return Ok(());
|
||||
}
|
||||
_ => continue
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
match n {
|
||||
1 => {
|
||||
let width = sys::process::envs().keys().map(|k| k.len()).max().unwrap_or(0);
|
||||
let width = sys::process::envs().keys().map(|k|
|
||||
k.len()
|
||||
).max().unwrap_or(0);
|
||||
for (key, val) in sys::process::envs() {
|
||||
println!("{:width$} \"{}\"", key, val, width = width);
|
||||
}
|
||||
|
@ -46,5 +48,8 @@ fn help() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} env {}[<key> [<value>]]{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} env {}[<key> [<value>]]{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::sys;
|
||||
use crate::api::console::Style;
|
||||
use crate::api::fs;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::regex::Regex;
|
||||
use crate::api::console::Style;
|
||||
use crate::sys;
|
||||
|
||||
use alloc::format;
|
||||
use alloc::string::{String, ToString};
|
||||
|
@ -68,7 +68,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
|
||||
if name.is_some() { // TODO
|
||||
if name.is_some() {
|
||||
// TODO
|
||||
error!("`--name` is not implemented");
|
||||
return Err(ExitCode::Failure);
|
||||
}
|
||||
|
@ -103,7 +104,11 @@ fn print_matching_lines(path: &str, pattern: &str, state: &mut PrintingState) {
|
|||
}
|
||||
}
|
||||
|
||||
fn print_matching_lines_in_file(path: &str, pattern: &str, state: &mut PrintingState) {
|
||||
fn print_matching_lines_in_file(
|
||||
path: &str,
|
||||
pattern: &str,
|
||||
state: &mut PrintingState
|
||||
) {
|
||||
let name_color = Style::color("Yellow");
|
||||
let line_color = Style::color("LightCyan");
|
||||
let match_color = Style::color("LightRed");
|
||||
|
@ -119,9 +124,9 @@ fn print_matching_lines_in_file(path: &str, pattern: &str, state: &mut PrintingS
|
|||
while let Some((a, b)) = re.find(&String::from_iter(&line[j..])) {
|
||||
let m = j + a;
|
||||
let n = j + b;
|
||||
let before = String::from_iter(&line[j..m]);
|
||||
let b = String::from_iter(&line[j..m]);
|
||||
let matched = String::from_iter(&line[m..n]);
|
||||
l = format!("{}{}{}{}{}", l, before, match_color, matched, reset);
|
||||
l = format!("{}{}{}{}{}", l, b, match_color, matched, reset);
|
||||
j = n;
|
||||
if m == n || n >= line.len() {
|
||||
// Some patterns like "" or ".*?" would never move the
|
||||
|
@ -148,7 +153,14 @@ fn print_matching_lines_in_file(path: &str, pattern: &str, state: &mut PrintingS
|
|||
}
|
||||
let width = matches[matches.len() - 1].0.to_string().len();
|
||||
for (i, line) in matches {
|
||||
println!("{}{:>width$}:{} {}", line_color, i, reset, line, width = width);
|
||||
println!(
|
||||
"{}{:>width$}:{} {}",
|
||||
line_color,
|
||||
i,
|
||||
reset,
|
||||
line,
|
||||
width = width
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -158,9 +170,20 @@ fn usage() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} find {}<options> <path>{1}", csi_title, csi_reset, csi_option);
|
||||
println!(
|
||||
"{}Usage:{} find {}<options> <path>{1}",
|
||||
csi_title, csi_reset, csi_option
|
||||
);
|
||||
println!();
|
||||
println!("{}Options:{}", csi_title, csi_reset);
|
||||
println!(" {0}-n{1}, {0}--name \"<pattern>\"{1} Find file name matching {0}<pattern>{1}", csi_option, csi_reset);
|
||||
println!(" {0}-l{1}, {0}--line \"<pattern>\"{1} Find lines matching {0}<pattern>{1}", csi_option, csi_reset);
|
||||
println!(
|
||||
" {0}-n{1}, {0}--name \"<pattern>\"{1} \
|
||||
Find file name matching {0}<pattern>{1}",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
println!(
|
||||
" {0}-l{1}, {0}--line \"<pattern>\"{1} \
|
||||
Find lines matching {0}<pattern>{1}",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
|
|
@ -36,9 +36,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
|
||||
paths.sort();
|
||||
for path in paths {
|
||||
if let Err(code) = print_hash(path, full) {
|
||||
return Err(code);
|
||||
}
|
||||
print_hash(path, full)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -76,8 +74,14 @@ fn help() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} hash {}<file>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} hash {}<file>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
println!();
|
||||
println!("{}Options:{}", csi_title, csi_reset);
|
||||
println!(" {0}-f{1}, {0}--full{1} Show full hash", csi_option, csi_reset);
|
||||
println!(
|
||||
" {0}-f{1}, {0}--full{1} Show full hash",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
|
244
src/usr/help.rs
244
src/usr/help.rs
|
@ -14,10 +14,13 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
|
||||
fn help_command(cmd: &str) -> Result<(), ExitCode> {
|
||||
match cmd {
|
||||
"-h" | "--help" => { help(); Ok(()) },
|
||||
"-h" | "--help" => {
|
||||
help();
|
||||
Ok(())
|
||||
}
|
||||
"date" => help_date(),
|
||||
"edit" => help_edit(),
|
||||
_ => help_unknown(cmd),
|
||||
_ => help_unknown(cmd),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +33,10 @@ fn print_usage(alias: &str, command: &str, usage: &str) {
|
|||
let csi_col1 = Style::color("LightGreen");
|
||||
let csi_col2 = Style::color("LightCyan");
|
||||
let csi_reset = Style::reset();
|
||||
println!(" {}{}{}{:21}{}{}", csi_col1, alias, csi_col2, command, csi_reset, usage);
|
||||
println!(
|
||||
" {}{}{}{:21}{}{}",
|
||||
csi_col1, alias, csi_col2, command, csi_reset, usage
|
||||
);
|
||||
}
|
||||
|
||||
fn help_summary() -> Result<(), ExitCode> {
|
||||
|
@ -38,22 +44,30 @@ fn help_summary() -> Result<(), ExitCode> {
|
|||
let csi_reset = Style::reset();
|
||||
|
||||
println!("{}Usage:{}", csi_color, csi_reset);
|
||||
print_usage("", "<dir>", " Change directory");
|
||||
print_usage("", "<cmd>", " Execute command");
|
||||
print_usage("", "<dir>", " Change directory");
|
||||
print_usage("", "<cmd>", " Execute command");
|
||||
println!();
|
||||
|
||||
println!("{}Commands:{}", csi_color, csi_reset);
|
||||
print_usage("c", "opy <file> <file>", "Copy file from source to destination");
|
||||
print_usage("d", "elete <file>", "Delete file or empty directory");
|
||||
print_usage("e", "dit <file>", "Edit existing or new file");
|
||||
print_usage("f", "ind <str> <path>", "Find pattern in path");
|
||||
print_usage("h", "elp <cmd>", "Display help about a command");
|
||||
print_usage("l", "ist <dir>", "List entries in directory");
|
||||
print_usage("m", "ove <file> <file>", "Move file from source to destination");
|
||||
print_usage("p", "rint <str>", "Print string to screen");
|
||||
print_usage("q", "uit", "Quit the console");
|
||||
print_usage("r", "ead <file>", "Read file to screen");
|
||||
print_usage("w", "rite <file>", "Write file or directory");
|
||||
print_usage(
|
||||
"c",
|
||||
"opy <file> <file>",
|
||||
"Copy file from source to destination",
|
||||
);
|
||||
print_usage("d", "elete <file>", "Delete file or empty directory");
|
||||
print_usage("e", "dit <file>", "Edit existing or new file");
|
||||
print_usage("f", "ind <str> <path>", "Find pattern in path");
|
||||
print_usage("h", "elp <cmd>", "Display help about a command");
|
||||
print_usage("l", "ist <dir>", "List entries in directory");
|
||||
print_usage(
|
||||
"m",
|
||||
"ove <file> <file>",
|
||||
"Move file from source to destination",
|
||||
);
|
||||
print_usage("p", "rint <str>", "Print string to screen");
|
||||
print_usage("q", "uit", "Quit the console");
|
||||
print_usage("r", "ead <file>", "Read file to screen");
|
||||
print_usage("w", "rite <file>", "Write file or directory");
|
||||
println!();
|
||||
|
||||
println!("{}Credits:{}", csi_color, csi_reset);
|
||||
|
@ -64,7 +78,8 @@ fn help_summary() -> Result<(), ExitCode> {
|
|||
fn help_edit() -> Result<(), ExitCode> {
|
||||
let csi_color = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("MOROS text editor is a very simple editor inspired by Pico, Nano, and Micro.");
|
||||
println!(
|
||||
"MOROS text editor is a very simple editor inspired by Pico.");
|
||||
println!();
|
||||
println!("{}Commands:{}", csi_color, csi_reset);
|
||||
let commands = [
|
||||
|
@ -90,42 +105,166 @@ fn help_edit() -> Result<(), ExitCode> {
|
|||
fn help_date() -> Result<(), ExitCode> {
|
||||
let csi_color = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("The date command's formatting behavior is based on strftime from C.");
|
||||
println!("The date command's formatting behavior is based on strftime.");
|
||||
println!();
|
||||
println!("{}Specifiers:{}", csi_color, csi_reset);
|
||||
let specifiers = [
|
||||
("%a", "Abbreviated weekday name", "Thu"),
|
||||
("%A", "Full weekday name", "Thursday"),
|
||||
("%b", "Abbreviated month name", "Aug"),
|
||||
("%B", "Full month name", "August"),
|
||||
("%c", "Date and time representation, equivalent to %a %b %-d %-H:%M:%S %-Y", "Thu Aug 23 14:55:02 2001"),
|
||||
("%C", "Year divided by 100 and truncated to integer (00-99)", "20"),
|
||||
("%d", "Day of the month, zero-padded (01-31)", "23"),
|
||||
("%D", "Short MM/DD/YY date, equivalent to %-m/%d/%y", "8/23/01"),
|
||||
("%F", "Short YYYY-MM-DD date, equivalent to %-Y-%m-%d", "2001-08-23"),
|
||||
("%g", "Week-based year, last two digits (00-99)", "01"),
|
||||
("%G", "Week-based year", "2001"),
|
||||
("%H", "Hour in 24h format (00-23)", "14"),
|
||||
("%I", "Hour in 12h format (01-12)", "02"),
|
||||
("%j", "Day of the year (001-366)", "235"),
|
||||
("%m", "Month as a decimal number (01-12)", "08"),
|
||||
("%M", "Minute (00-59)", "55"),
|
||||
("%N", "Subsecond nanoseconds. Always 9 digits", "012345678"),
|
||||
("%p", "am or pm designation", "pm"),
|
||||
("%P", "AM or PM designation", "PM"),
|
||||
("%r", "12-hour clock time, equivalent to %-I:%M:%S %p", "2:55:02 pm"),
|
||||
("%R", "24-hour HH:MM time, equivalent to %-H:%M", "14:55"),
|
||||
("%S", "Second (00-59)", "02"),
|
||||
("%T", "24-hour clock time with seconds, equivalent to %-H:%M:%S", "14:55:02"),
|
||||
("%u", "ISO 8601 weekday as number with Monday as 1 (1-7)", "4"),
|
||||
("%U", "Week number with the first Sunday as the start of week one (00-53)", "33"),
|
||||
("%V", "ISO 8601 week number (01-53)", "34"),
|
||||
("%w", "Weekday as a decimal number with Sunday as 0 (0-6)", "4"),
|
||||
("%W", "Week number with the first Monday as the start of week one (00-53)", "34"),
|
||||
("%y", "Year, last two digits (00-99)", "01"),
|
||||
("%Y", "Full year, including + if ≥10,000", "2001"),
|
||||
("%z", "ISO 8601 offset from UTC in timezone (+HHMM)", "+0100"),
|
||||
("%%", "Literal %", "%"),
|
||||
(
|
||||
"%a",
|
||||
"Abbreviated weekday name",
|
||||
"Thu",
|
||||
),
|
||||
(
|
||||
"%A",
|
||||
"Full weekday name",
|
||||
"Thursday",
|
||||
),
|
||||
(
|
||||
"%b",
|
||||
"Abbreviated month name",
|
||||
"Aug",
|
||||
),
|
||||
(
|
||||
"%B",
|
||||
"Full month name",
|
||||
"August",
|
||||
),
|
||||
(
|
||||
"%c",
|
||||
"Date and time, equivalent to %a %b %-d %-H:%M:%S %-Y",
|
||||
"Thu Aug 23 14:55:02 2001",
|
||||
),
|
||||
(
|
||||
"%C",
|
||||
"Year divided by 100 and truncated to integer (00-99)",
|
||||
"20",
|
||||
),
|
||||
(
|
||||
"%d",
|
||||
"Day of the month, zero-padded (01-31)",
|
||||
"23",
|
||||
),
|
||||
(
|
||||
"%D",
|
||||
"Short MM/DD/YY date, equivalent to %-m/%d/%y",
|
||||
"8/23/01",
|
||||
),
|
||||
(
|
||||
"%F",
|
||||
"Short YYYY-MM-DD date, equivalent to %-Y-%m-%d",
|
||||
"2001-08-23",
|
||||
),
|
||||
(
|
||||
"%g",
|
||||
"Week-based year, last two digits (00-99)",
|
||||
"01",
|
||||
),
|
||||
(
|
||||
"%G",
|
||||
"Week-based year",
|
||||
"2001",
|
||||
),
|
||||
(
|
||||
"%H",
|
||||
"Hour in 24h format (00-23)",
|
||||
"14",
|
||||
),
|
||||
(
|
||||
"%I",
|
||||
"Hour in 12h format (01-12)",
|
||||
"02",
|
||||
),
|
||||
(
|
||||
"%j",
|
||||
"Day of the year (001-366)",
|
||||
"235",
|
||||
),
|
||||
(
|
||||
"%m",
|
||||
"Month as a decimal number (01-12)",
|
||||
"08",
|
||||
),
|
||||
(
|
||||
"%M",
|
||||
"Minute (00-59)",
|
||||
"55",
|
||||
),
|
||||
(
|
||||
"%N",
|
||||
"Subsecond nanoseconds. Always 9 digits",
|
||||
"012345678",
|
||||
),
|
||||
(
|
||||
"%p",
|
||||
"am or pm designation", "pm"),
|
||||
(
|
||||
"%P",
|
||||
"AM or PM designation", "PM"),
|
||||
(
|
||||
"%r",
|
||||
"12-hour clock time, equivalent to %-I:%M:%S %p",
|
||||
"2:55:02 pm",
|
||||
),
|
||||
(
|
||||
"%R",
|
||||
"24-hour HH:MM time, equivalent to %-H:%M",
|
||||
"14:55"
|
||||
),
|
||||
(
|
||||
"%S",
|
||||
"Second (00-59)",
|
||||
"02"
|
||||
),
|
||||
(
|
||||
"%T",
|
||||
"24-hour clock time with seconds, equivalent to %-H:%M:%S",
|
||||
"14:55:02",
|
||||
),
|
||||
(
|
||||
"%u",
|
||||
"ISO 8601 weekday as number with Monday as 1 (1-7)",
|
||||
"4",
|
||||
),
|
||||
(
|
||||
"%U",
|
||||
"Week number with Sunday as first day of the week (00-53)",
|
||||
"33",
|
||||
),
|
||||
(
|
||||
"%V",
|
||||
"ISO 8601 week number (01-53)",
|
||||
"34",
|
||||
),
|
||||
(
|
||||
"%w",
|
||||
"Weekday as a decimal number with Sunday as 0 (0-6)",
|
||||
"4",
|
||||
),
|
||||
(
|
||||
"%W",
|
||||
"Week number with Monday as first day of the week (00-53)",
|
||||
"34",
|
||||
),
|
||||
(
|
||||
"%y",
|
||||
"Year, last two digits (00-99)",
|
||||
"01",
|
||||
),
|
||||
(
|
||||
"%Y",
|
||||
"Full year, including + if ≥10,000",
|
||||
"2001",
|
||||
),
|
||||
(
|
||||
"%z",
|
||||
"ISO 8601 offset from UTC in timezone (+HHMM)",
|
||||
"+0100",
|
||||
),
|
||||
(
|
||||
"%%",
|
||||
"Literal %",
|
||||
"%"
|
||||
),
|
||||
];
|
||||
for (specifier, usage, _exemple) in &specifiers {
|
||||
let csi_color = Style::color("LightCyan");
|
||||
|
@ -139,5 +278,8 @@ fn help() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} help {}[<command>]{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} help {}[<command>]{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::api::fs;
|
||||
use crate::api::console::Style;
|
||||
use crate::api::fs;
|
||||
use crate::api::process::ExitCode;
|
||||
|
||||
use alloc::format;
|
||||
|
@ -17,7 +17,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
return Ok(());
|
||||
}
|
||||
let pathname = args[1];
|
||||
if let Ok(buf) = fs::read_to_bytes(pathname) { // TODO: read chunks
|
||||
if let Ok(buf) = fs::read_to_bytes(pathname) {
|
||||
// TODO: read chunks
|
||||
print_hex(&buf);
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -61,5 +62,8 @@ fn help() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} hex {}<file>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} hex {}<file>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::usr;
|
||||
use crate::api::console::Style;
|
||||
use crate::sys::fs::OpenFlag;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::random;
|
||||
use crate::api::syscall;
|
||||
use crate::sys::fs::OpenFlag;
|
||||
use crate::usr;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use bit_field::BitField;
|
||||
|
@ -143,11 +143,11 @@ pub fn resolve(name: &str) -> Result<IpAddress, ResponseCode> {
|
|||
if let Some(handle) = syscall::open(socket_path, flags) {
|
||||
if syscall::connect(handle, addr, port).is_err() {
|
||||
syscall::close(handle);
|
||||
return Err(ResponseCode::NetworkError)
|
||||
return Err(ResponseCode::NetworkError);
|
||||
}
|
||||
if syscall::write(handle, &query.datagram).is_none() {
|
||||
syscall::close(handle);
|
||||
return Err(ResponseCode::NetworkError)
|
||||
return Err(ResponseCode::NetworkError);
|
||||
}
|
||||
loop {
|
||||
let mut data = vec![0; buf_len];
|
||||
|
@ -174,10 +174,8 @@ pub fn resolve(name: &str) -> Result<IpAddress, ResponseCode> {
|
|||
Ok(IpAddress::from(ipv4))
|
||||
}
|
||||
}
|
||||
code => {
|
||||
Err(code)
|
||||
}
|
||||
}
|
||||
code => Err(code),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
@ -211,5 +209,8 @@ fn help() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} host {}<domain>{1}", csi_title, csi_reset, csi_option);
|
||||
println!(
|
||||
"{}Usage:{} host {}<domain>{1}",
|
||||
csi_title, csi_reset, csi_option
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use crate::{sys, usr};
|
||||
use crate::api::console::Style;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::syscall;
|
||||
use crate::sys::console;
|
||||
use crate::sys::fs::OpenFlag;
|
||||
use crate::usr;
|
||||
|
||||
use alloc::format;
|
||||
use alloc::string::{String, ToString};
|
||||
|
@ -17,7 +18,10 @@ struct URL {
|
|||
pub path: String,
|
||||
}
|
||||
|
||||
enum ResponseState { Headers, Body }
|
||||
enum ResponseState {
|
||||
Headers,
|
||||
Body,
|
||||
}
|
||||
|
||||
impl URL {
|
||||
pub fn parse(url: &str) -> Option<Self> {
|
||||
|
@ -75,7 +79,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
error!("Invalid option '{}'", args[i]);
|
||||
return Err(ExitCode::UsageError);
|
||||
} else if host.is_empty() {
|
||||
host = args[i].trim_start_matches("http://").trim_start_matches("https://");
|
||||
host = args[i].
|
||||
trim_start_matches("http://").
|
||||
trim_start_matches("https://");
|
||||
} else if path.is_empty() {
|
||||
path = args[i];
|
||||
} else {
|
||||
|
@ -103,9 +109,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
let port = url.port;
|
||||
let addr = if url.host.ends_with(char::is_numeric) {
|
||||
match IpAddress::from_str(&url.host) {
|
||||
Ok(ip_addr) => {
|
||||
ip_addr
|
||||
}
|
||||
Ok(ip_addr) => ip_addr,
|
||||
Err(_) => {
|
||||
error!("Invalid address format");
|
||||
return Err(ExitCode::UsageError);
|
||||
|
@ -113,9 +117,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
} else {
|
||||
match usr::host::resolve(&url.host) {
|
||||
Ok(ip_addr) => {
|
||||
ip_addr
|
||||
}
|
||||
Ok(ip_addr) => ip_addr,
|
||||
Err(e) => {
|
||||
error!("Could not resolve host: {:?}", e);
|
||||
return Err(ExitCode::Failure);
|
||||
|
@ -157,7 +159,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
|
||||
let mut response_state = ResponseState::Headers;
|
||||
loop {
|
||||
if sys::console::end_of_text() || sys::console::end_of_transmission() {
|
||||
if console::end_of_text() || console::end_of_transmission() {
|
||||
eprintln!();
|
||||
syscall::close(handle);
|
||||
return Err(ExitCode::Failure);
|
||||
|
@ -179,7 +181,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
j += 1;
|
||||
}
|
||||
let line = String::from_utf8_lossy(&data[i..j]); // TODO: check i == j
|
||||
// TODO: check i == j
|
||||
let line = String::from_utf8_lossy(&data[i..j]);
|
||||
if is_verbose {
|
||||
if i == 0 {
|
||||
print!("{}", csi_verbose);
|
||||
|
@ -195,8 +198,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
i = j + 1;
|
||||
}
|
||||
ResponseState::Body => {
|
||||
// NOTE: The buffer may not be convertible to a UTF-8 string
|
||||
// so we write it to STDOUT directly instead of using print.
|
||||
// NOTE: The buffer may not be convertible to a
|
||||
// UTF-8 string so we write it to STDOUT directly
|
||||
// instead of using print.
|
||||
syscall::write(1, &data[i..n]);
|
||||
break;
|
||||
}
|
||||
|
@ -219,10 +223,19 @@ fn help() -> Result<(), ExitCode> {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} http {}<options> <url>{1}", csi_title, csi_reset, csi_option);
|
||||
println!(
|
||||
"{}Usage:{} http {}<options> <url>{1}",
|
||||
csi_title, csi_reset, csi_option
|
||||
);
|
||||
println!();
|
||||
println!("{}Options:{}", csi_title, csi_reset);
|
||||
println!(" {0}-v{1}, {0}--verbose{1} Increase verbosity", csi_option, csi_reset);
|
||||
println!(" {0}-t{1}, {0}--timeout <seconds>{1} Request timeout", csi_option, csi_reset);
|
||||
println!(
|
||||
" {0}-v{1}, {0}--verbose{1} Increase verbosity",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
println!(
|
||||
" {0}-t{1}, {0}--timeout <seconds>{1} Request timeout",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
320
src/usr/httpd.rs
320
src/usr/httpd.rs
|
@ -1,4 +1,3 @@
|
|||
use crate::sys;
|
||||
use crate::api::clock;
|
||||
use crate::api::clock::DATE_TIME_ZONE;
|
||||
use crate::api::console::Style;
|
||||
|
@ -6,6 +5,8 @@ use crate::api::fs;
|
|||
use crate::api::process::ExitCode;
|
||||
use crate::api::syscall;
|
||||
use crate::api::time;
|
||||
use crate::sys;
|
||||
use crate::sys::console;
|
||||
|
||||
use alloc::collections::btree_map::BTreeMap;
|
||||
use alloc::collections::vec_deque::VecDeque;
|
||||
|
@ -15,9 +16,9 @@ use alloc::vec;
|
|||
use alloc::vec::Vec;
|
||||
use core::fmt;
|
||||
use smoltcp::iface::SocketSet;
|
||||
use smoltcp::phy::Device;
|
||||
use smoltcp::socket::tcp;
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::phy::Device;
|
||||
use smoltcp::wire::IpAddress;
|
||||
|
||||
const MAX_CONNECTIONS: usize = 32;
|
||||
|
@ -49,20 +50,26 @@ impl Request {
|
|||
let mut req = Request::new(addr);
|
||||
let mut is_header = true;
|
||||
for (i, line) in msg.lines().enumerate() {
|
||||
if i == 0 { // Request line
|
||||
if i == 0 {
|
||||
// Request line
|
||||
let fields: Vec<_> = line.split(' ').collect();
|
||||
if fields.len() >= 2 {
|
||||
req.verb = fields[0].to_string();
|
||||
req.path = fields[1].to_string();
|
||||
}
|
||||
} else if is_header { // Message header
|
||||
} else if is_header {
|
||||
// Message header
|
||||
if let Some((key, val)) = line.split_once(':') {
|
||||
req.headers.insert(key.trim().to_string(), val.trim().to_string());
|
||||
let k = key.trim().to_string();
|
||||
let v = val.trim().to_string();
|
||||
req.headers.insert(k, v);
|
||||
} else if line.is_empty() {
|
||||
is_header = false;
|
||||
}
|
||||
} else if !is_header { // Message body
|
||||
req.body.extend_from_slice(format!("{}\n", line).as_bytes());
|
||||
} else if !is_header {
|
||||
// Message body
|
||||
let s = format!("{}\n", line);
|
||||
req.body.extend_from_slice(s.as_bytes());
|
||||
}
|
||||
}
|
||||
Some(req)
|
||||
|
@ -82,13 +89,20 @@ struct Response {
|
|||
size: usize,
|
||||
body: Vec<u8>,
|
||||
headers: BTreeMap<String, String>,
|
||||
real_path: String,
|
||||
}
|
||||
|
||||
impl Response {
|
||||
pub fn new(req: Request) -> Self {
|
||||
let mut headers = BTreeMap::new();
|
||||
headers.insert("Date".to_string(), time::now_utc().format("%a, %d %b %Y %H:%M:%S GMT"));
|
||||
headers.insert("Server".to_string(), format!("MOROS/{}", env!("CARGO_PKG_VERSION")));
|
||||
headers.insert(
|
||||
"Date".to_string(),
|
||||
time::now_utc().format("%a, %d %b %Y %H:%M:%S GMT"),
|
||||
);
|
||||
headers.insert(
|
||||
"Server".to_string(),
|
||||
format!("MOROS/{}", env!("CARGO_PKG_VERSION")),
|
||||
);
|
||||
Self {
|
||||
req,
|
||||
buf: Vec::new(),
|
||||
|
@ -98,30 +112,44 @@ impl Response {
|
|||
size: 0,
|
||||
body: Vec::new(),
|
||||
headers,
|
||||
real_path: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end(&mut self) {
|
||||
self.size = self.body.len();
|
||||
self.headers.insert("Content-Length".to_string(), self.size.to_string());
|
||||
self.headers.insert("Connection".to_string(), if self.is_persistent() {
|
||||
"keep-alive".to_string()
|
||||
} else {
|
||||
"close".to_string()
|
||||
});
|
||||
self.headers.insert("Content-Type".to_string(), if self.mime.starts_with("text/") {
|
||||
format!("{}; charset=utf-8", self.mime)
|
||||
} else {
|
||||
format!("{}", self.mime)
|
||||
});
|
||||
self.headers.insert(
|
||||
"Content-Length".to_string(),
|
||||
self.size.to_string()
|
||||
);
|
||||
self.headers.insert(
|
||||
"Connection".to_string(),
|
||||
if self.is_persistent() {
|
||||
"keep-alive".to_string()
|
||||
} else {
|
||||
"close".to_string()
|
||||
}
|
||||
);
|
||||
self.headers.insert(
|
||||
"Content-Type".to_string(),
|
||||
if self.mime.starts_with("text/") {
|
||||
format!("{}; charset=utf-8", self.mime)
|
||||
} else {
|
||||
format!("{}", self.mime)
|
||||
}
|
||||
);
|
||||
self.write();
|
||||
}
|
||||
|
||||
fn write(&mut self) {
|
||||
self.buf.clear();
|
||||
self.buf.extend_from_slice(format!("{}\r\n", self.status()).as_bytes());
|
||||
self.buf.extend_from_slice(
|
||||
format!("{}\r\n", self.status()).as_bytes()
|
||||
);
|
||||
for (key, val) in &self.headers {
|
||||
self.buf.extend_from_slice(format!("{}: {}\r\n", key, val).as_bytes());
|
||||
self.buf.extend_from_slice(
|
||||
format!("{}: {}\r\n", key, val).as_bytes()
|
||||
);
|
||||
}
|
||||
self.buf.extend_from_slice(b"\r\n");
|
||||
self.buf.extend_from_slice(&self.body);
|
||||
|
@ -135,7 +163,7 @@ impl Response {
|
|||
403 => "Forbidden",
|
||||
404 => "Not Found",
|
||||
500 => "Internal Server Error",
|
||||
_ => "Unknown Error",
|
||||
_ => "Unknown Error",
|
||||
};
|
||||
format!("HTTP/1.1 {} {}", self.code, msg)
|
||||
}
|
||||
|
@ -157,15 +185,119 @@ impl fmt::Display for Response {
|
|||
let csi_pink = Style::color("Pink");
|
||||
let csi_reset = Style::reset();
|
||||
write!(
|
||||
f, "{}{} - -{} [{}] {}\"{} {}\"{} {} {}",
|
||||
csi_cyan, self.req.addr,
|
||||
csi_pink, self.time,
|
||||
csi_blue, self.req.verb, self.req.path,
|
||||
csi_reset, self.code, self.size
|
||||
f,
|
||||
"{}{} - -{} [{}] {}\"{} {}\"{} {} {}",
|
||||
csi_cyan,
|
||||
self.req.addr,
|
||||
csi_pink,
|
||||
self.time,
|
||||
csi_blue,
|
||||
self.req.verb,
|
||||
self.req.path,
|
||||
csi_reset,
|
||||
self.code,
|
||||
self.size
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn get(req: &Request, res: &mut Response) {
|
||||
if fs::is_dir(&res.real_path) && !req.path.ends_with('/') {
|
||||
res.code = 301;
|
||||
res.mime = "text/html".to_string();
|
||||
res.headers.insert(
|
||||
"Location".to_string(),
|
||||
format!("{}/", req.path),
|
||||
);
|
||||
res.body.extend_from_slice(b"<h1>Moved Permanently</h1>\r\n");
|
||||
} else {
|
||||
let mut not_found = true;
|
||||
for autocomplete in
|
||||
&["", "/index.html", "/index.htm", "/index.txt"]
|
||||
{
|
||||
let real_path = format!("{}{}", res.real_path, autocomplete);
|
||||
if fs::is_dir(&real_path) {
|
||||
continue;
|
||||
}
|
||||
if let Ok(buf) = fs::read_to_bytes(&real_path) {
|
||||
res.code = 200;
|
||||
res.mime = content_type(&res.real_path);
|
||||
let tmp;
|
||||
res.body.extend_from_slice(
|
||||
if res.mime.starts_with("text/") {
|
||||
tmp = String::from_utf8_lossy(&buf).to_string().
|
||||
replace("\n", "\r\n");
|
||||
tmp.as_bytes()
|
||||
} else {
|
||||
&buf
|
||||
},
|
||||
);
|
||||
not_found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if not_found {
|
||||
if let Ok(mut files) = fs::read_dir(&res.real_path) {
|
||||
res.code = 200;
|
||||
res.mime = "text/html".to_string();
|
||||
res.body.extend_from_slice(
|
||||
format!("<h1>Index of {}</h1>\r\n", req.path).as_bytes()
|
||||
);
|
||||
files.sort_by_key(|f| f.name());
|
||||
for file in files {
|
||||
let path = format!("{}{}", req.path, file.name());
|
||||
let link = format!(
|
||||
"<li><a href=\"{}\">{}</a></li>\n",
|
||||
path,
|
||||
file.name()
|
||||
);
|
||||
res.body.extend_from_slice(link.as_bytes());
|
||||
}
|
||||
} else {
|
||||
res.code = 404;
|
||||
res.mime = "text/html".to_string();
|
||||
res.body.extend_from_slice(b"<h1>Not Found</h1>\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn put(req: &Request, res: &mut Response) {
|
||||
if res.real_path.ends_with('/') {
|
||||
// Write directory
|
||||
let real_path = res.real_path.trim_end_matches('/');
|
||||
if fs::exists(real_path) {
|
||||
res.code = 403;
|
||||
} else if let Some(handle) = fs::create_dir(real_path) {
|
||||
syscall::close(handle);
|
||||
res.code = 200;
|
||||
} else {
|
||||
res.code = 500;
|
||||
}
|
||||
} else {
|
||||
// Write file
|
||||
if fs::write(&res.real_path, &req.body).is_ok() {
|
||||
res.code = 200;
|
||||
} else {
|
||||
res.code = 500;
|
||||
}
|
||||
}
|
||||
res.mime = "text/plain".to_string();
|
||||
}
|
||||
|
||||
fn delete(_req: &Request, res: &mut Response) {
|
||||
if fs::exists(&res.real_path) {
|
||||
if fs::delete(&res.real_path).is_ok() {
|
||||
res.code = 200;
|
||||
} else {
|
||||
res.code = 500;
|
||||
}
|
||||
} else {
|
||||
res.code = 404;
|
||||
}
|
||||
res.mime = "text/plain".to_string();
|
||||
}
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
let csi_color = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
|
@ -223,15 +355,19 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
connections.push((tcp_handle, send_queue, keep_alive));
|
||||
}
|
||||
|
||||
println!("{}HTTP Server listening on 0.0.0.0:{}{}", csi_color, port, csi_reset);
|
||||
println!(
|
||||
"{}HTTP Server listening on 0.0.0.0:{}{}",
|
||||
csi_color, port, csi_reset
|
||||
);
|
||||
|
||||
loop {
|
||||
if sys::console::end_of_text() || sys::console::end_of_transmission() {
|
||||
if console::end_of_text() || console::end_of_transmission() {
|
||||
println!();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let time = Instant::from_micros((clock::realtime() * 1000000.0) as i64);
|
||||
let ms = (clock::realtime() * 1000000.0) as i64;
|
||||
let time = Instant::from_micros(ms);
|
||||
iface.poll(time, device, &mut sockets);
|
||||
|
||||
for (tcp_handle, send_queue, keep_alive) in &mut connections {
|
||||
|
@ -245,100 +381,37 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
None => continue,
|
||||
};
|
||||
if socket.may_recv() {
|
||||
let res = socket.recv(|buffer| {
|
||||
if let Some(req) = Request::from(endpoint.addr, buffer) {
|
||||
let res = socket.recv(|buf| {
|
||||
if let Some(req) = Request::from(endpoint.addr, buf) {
|
||||
let mut res = Response::new(req.clone());
|
||||
let sep = if req.path == "/" { "" } else { "/" };
|
||||
let real_path = format!("{}{}{}", dir, sep, req.path.strip_suffix('/').unwrap_or(&req.path)).replace("//", "/");
|
||||
res.real_path = format!(
|
||||
"{}{}{}",
|
||||
dir,
|
||||
sep,
|
||||
req.path.strip_suffix('/').unwrap_or(&req.path)
|
||||
).replace("//", "/");
|
||||
|
||||
match req.verb.as_str() {
|
||||
"GET" => {
|
||||
if fs::is_dir(&real_path) && !req.path.ends_with('/') {
|
||||
res.code = 301;
|
||||
res.mime = "text/html".to_string();
|
||||
res.headers.insert("Location".to_string(), format!("{}/", req.path));
|
||||
res.body.extend_from_slice(b"<h1>Moved Permanently</h1>\r\n");
|
||||
} else {
|
||||
let mut not_found = true;
|
||||
for autocomplete in &["", "/index.html", "/index.htm", "/index.txt"] {
|
||||
let real_path = format!("{}{}", real_path, autocomplete);
|
||||
if fs::is_dir(&real_path) {
|
||||
continue;
|
||||
}
|
||||
if let Ok(buf) = fs::read_to_bytes(&real_path) {
|
||||
res.code = 200;
|
||||
res.mime = content_type(&real_path);
|
||||
let tmp;
|
||||
res.body.extend_from_slice(if res.mime.starts_with("text/") {
|
||||
tmp = String::from_utf8_lossy(&buf).to_string().replace("\n", "\r\n");
|
||||
tmp.as_bytes()
|
||||
} else {
|
||||
&buf
|
||||
});
|
||||
not_found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if not_found {
|
||||
if let Ok(mut files) = fs::read_dir(&real_path) {
|
||||
res.code = 200;
|
||||
res.mime = "text/html".to_string();
|
||||
res.body.extend_from_slice(format!("<h1>Index of {}</h1>\r\n", req.path).as_bytes());
|
||||
files.sort_by_key(|f| f.name());
|
||||
for file in files {
|
||||
let path = format!("{}{}", req.path, file.name());
|
||||
let link = format!("<li><a href=\"{}\">{}</a></li>\n", path, file.name());
|
||||
res.body.extend_from_slice(link.as_bytes());
|
||||
}
|
||||
} else {
|
||||
res.code = 404;
|
||||
res.mime = "text/html".to_string();
|
||||
res.body.extend_from_slice(b"<h1>Not Found</h1>\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
get(&req, &mut res)
|
||||
}
|
||||
"PUT" if !read_only => {
|
||||
if real_path.ends_with('/') { // Write directory
|
||||
let real_path = real_path.trim_end_matches('/');
|
||||
if fs::exists(real_path) {
|
||||
res.code = 403;
|
||||
} else if let Some(handle) = fs::create_dir(real_path) {
|
||||
syscall::close(handle);
|
||||
res.code = 200;
|
||||
} else {
|
||||
res.code = 500;
|
||||
}
|
||||
} else { // Write file
|
||||
if fs::write(&real_path, &req.body).is_ok() {
|
||||
res.code = 200;
|
||||
} else {
|
||||
res.code = 500;
|
||||
}
|
||||
}
|
||||
res.mime = "text/plain".to_string();
|
||||
},
|
||||
put(&req, &mut res)
|
||||
}
|
||||
"DELETE" if !read_only => {
|
||||
if fs::exists(&real_path) {
|
||||
if fs::delete(&real_path).is_ok() {
|
||||
res.code = 200;
|
||||
} else {
|
||||
res.code = 500;
|
||||
}
|
||||
} else {
|
||||
res.code = 404;
|
||||
}
|
||||
res.mime = "text/plain".to_string();
|
||||
},
|
||||
delete(&req, &mut res)
|
||||
}
|
||||
_ => {
|
||||
let s = b"<h1>Bad Request</h1>\r\n";
|
||||
res.body.extend_from_slice(s);
|
||||
res.code = 400;
|
||||
res.mime = "text/html".to_string();
|
||||
res.body.extend_from_slice(b"<h1>Bad Request</h1>\r\n");
|
||||
},
|
||||
}
|
||||
}
|
||||
res.end();
|
||||
println!("{}", res);
|
||||
(buffer.len(), Some(res))
|
||||
(buf.len(), Some(res))
|
||||
} else {
|
||||
(0, None)
|
||||
}
|
||||
|
@ -351,7 +424,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
if socket.can_send() {
|
||||
if let Some(chunk) = send_queue.pop_front() {
|
||||
let sent = socket.send_slice(&chunk).expect("Could not send chunk");
|
||||
let sent = socket.send_slice(&chunk).
|
||||
expect("Could not send chunk");
|
||||
debug_assert!(sent == chunk.len());
|
||||
}
|
||||
}
|
||||
|
@ -398,10 +472,22 @@ fn usage() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} httpd {}<options>{1}", csi_title, csi_reset, csi_option);
|
||||
println!(
|
||||
"{}Usage:{} httpd {}<options>{1}",
|
||||
csi_title, csi_reset, csi_option
|
||||
);
|
||||
println!();
|
||||
println!("{}Options:{}", csi_title, csi_reset);
|
||||
println!(" {0}-d{1}, {0}--dir <path>{1} Set directory to {0}<path>{1}", csi_option, csi_reset);
|
||||
println!(" {0}-p{1}, {0}--port <number>{1} Listen to port {0}<number>{1}", csi_option, csi_reset);
|
||||
println!(" {0}-r{1}, {0}--read-only{1} Set read-only mode", csi_option, csi_reset);
|
||||
println!(
|
||||
" {0}-d{1}, {0}--dir <path>{1} Set directory to {0}<path>{1}",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
println!(
|
||||
" {0}-p{1}, {0}--port <number>{1} Listen to port {0}<number>{1}",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
println!(
|
||||
" {0}-r{1}, {0}--read-only{1} Set read-only mode",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::{api, sys, usr};
|
||||
use crate::api::console::Style;
|
||||
use crate::api::fs;
|
||||
use crate::api::fs::DeviceType;
|
||||
use crate::api::io;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::syscall;
|
||||
use crate::{api, sys, usr};
|
||||
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
|
@ -25,7 +25,11 @@ pub fn copy_files(verbose: bool) {
|
|||
copy_file("/bin/halt", include_bytes!("../../dsk/bin/halt"), verbose);
|
||||
//copy_file("/bin/hello", include_bytes!("../../dsk/bin/hello"), verbose);
|
||||
copy_file("/bin/print", include_bytes!("../../dsk/bin/print"), verbose);
|
||||
copy_file("/bin/reboot", include_bytes!("../../dsk/bin/reboot"), verbose);
|
||||
copy_file(
|
||||
"/bin/reboot",
|
||||
include_bytes!("../../dsk/bin/reboot"),
|
||||
verbose,
|
||||
);
|
||||
copy_file("/bin/sleep", include_bytes!("../../dsk/bin/sleep"), verbose);
|
||||
|
||||
create_dir("/dev/clk", verbose); // Clock
|
||||
|
@ -39,74 +43,245 @@ pub fn copy_files(verbose: bool) {
|
|||
create_dev("/dev/net/tcp", DeviceType::TcpSocket, verbose);
|
||||
create_dev("/dev/net/udp", DeviceType::UdpSocket, verbose);
|
||||
|
||||
copy_file("/ini/banner.txt", include_bytes!("../../dsk/ini/banner.txt"), verbose);
|
||||
copy_file("/ini/boot.sh", include_bytes!("../../dsk/ini/boot.sh"), verbose);
|
||||
copy_file("/ini/lisp.lsp", include_bytes!("../../dsk/ini/lisp.lsp"), verbose);
|
||||
copy_file("/ini/shell.sh", include_bytes!("../../dsk/ini/shell.sh"), verbose);
|
||||
copy_file("/ini/version.txt", include_bytes!("../../dsk/ini/version.txt"), verbose);
|
||||
copy_file(
|
||||
"/ini/banner.txt",
|
||||
include_bytes!("../../dsk/ini/banner.txt"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/ini/boot.sh",
|
||||
include_bytes!("../../dsk/ini/boot.sh"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/ini/lisp.lsp",
|
||||
include_bytes!("../../dsk/ini/lisp.lsp"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/ini/shell.sh",
|
||||
include_bytes!("../../dsk/ini/shell.sh"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/ini/version.txt",
|
||||
include_bytes!("../../dsk/ini/version.txt"),
|
||||
verbose,
|
||||
);
|
||||
|
||||
create_dir("/ini/palettes", verbose);
|
||||
copy_file("/ini/palettes/gruvbox-dark.sh", include_bytes!("../../dsk/ini/palettes/gruvbox-dark.sh"), verbose);
|
||||
copy_file("/ini/palettes/gruvbox-light.sh", include_bytes!("../../dsk/ini/palettes/gruvbox-light.sh"), verbose);
|
||||
copy_file(
|
||||
"/ini/palettes/gruvbox-dark.sh",
|
||||
include_bytes!("../../dsk/ini/palettes/gruvbox-dark.sh"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/ini/palettes/gruvbox-light.sh",
|
||||
include_bytes!("../../dsk/ini/palettes/gruvbox-light.sh"),
|
||||
verbose,
|
||||
);
|
||||
|
||||
create_dir("/ini/fonts", verbose);
|
||||
//copy_file("/ini/fonts/lat15-terminus-8x16.psf", include_bytes!("../../dsk/ini/fonts/lat15-terminus-8x16.psf"), verbose);
|
||||
copy_file("/ini/fonts/zap-light-8x16.psf", include_bytes!("../../dsk/ini/fonts/zap-light-8x16.psf"), verbose);
|
||||
copy_file("/ini/fonts/zap-vga-8x16.psf", include_bytes!("../../dsk/ini/fonts/zap-vga-8x16.psf"), verbose);
|
||||
/*
|
||||
copy_file(
|
||||
"/ini/fonts/lat15-terminus-8x16.psf",
|
||||
include_bytes!("../../dsk/ini/fonts/lat15-terminus-8x16.psf"),
|
||||
verbose
|
||||
);
|
||||
*/
|
||||
copy_file(
|
||||
"/ini/fonts/zap-light-8x16.psf",
|
||||
include_bytes!("../../dsk/ini/fonts/zap-light-8x16.psf"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/ini/fonts/zap-vga-8x16.psf",
|
||||
include_bytes!("../../dsk/ini/fonts/zap-vga-8x16.psf"),
|
||||
verbose,
|
||||
);
|
||||
|
||||
create_dir("/lib/lisp", verbose);
|
||||
copy_file("/lib/lisp/alias.lsp", include_bytes!("../../dsk/lib/lisp/alias.lsp"), verbose);
|
||||
copy_file("/lib/lisp/core.lsp", include_bytes!("../../dsk/lib/lisp/core.lsp"), verbose);
|
||||
copy_file("/lib/lisp/file.lsp", include_bytes!("../../dsk/lib/lisp/file.lsp"), verbose);
|
||||
//copy_file("/lib/lisp/legacy.lsp", include_bytes!("../../dsk/lib/lisp/legacy.lsp"), verbose);
|
||||
copy_file(
|
||||
"/lib/lisp/alias.lsp",
|
||||
include_bytes!("../../dsk/lib/lisp/alias.lsp"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/lib/lisp/core.lsp",
|
||||
include_bytes!("../../dsk/lib/lisp/core.lsp"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/lib/lisp/file.lsp",
|
||||
include_bytes!("../../dsk/lib/lisp/file.lsp"),
|
||||
verbose,
|
||||
);
|
||||
/*
|
||||
copy_file(
|
||||
"/lib/lisp/legacy.lsp",
|
||||
include_bytes!("../../dsk/lib/lisp/legacy.lsp"),
|
||||
verbose
|
||||
);
|
||||
*/
|
||||
|
||||
copy_file("/tmp/alice.txt", include_bytes!("../../dsk/tmp/alice.txt"), verbose);
|
||||
copy_file("/tmp/machines.txt", include_bytes!("../../dsk/tmp/machines.txt"), verbose);
|
||||
copy_file(
|
||||
"/tmp/alice.txt",
|
||||
include_bytes!("../../dsk/tmp/alice.txt"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/machines.txt",
|
||||
include_bytes!("../../dsk/tmp/machines.txt"),
|
||||
verbose,
|
||||
);
|
||||
|
||||
create_dir("/tmp/lisp", verbose);
|
||||
copy_file("/tmp/lisp/colors.lsp", include_bytes!("../../dsk/tmp/lisp/colors.lsp"), verbose);
|
||||
copy_file("/tmp/lisp/doc.lsp", include_bytes!("../../dsk/tmp/lisp/doc.lsp"), verbose);
|
||||
copy_file("/tmp/lisp/factorial.lsp", include_bytes!("../../dsk/tmp/lisp/factorial.lsp"), verbose);
|
||||
//copy_file("/tmp/lisp/fetch.lsp", include_bytes!("../../dsk/tmp/lisp/fetch.lsp"), verbose);
|
||||
copy_file("/tmp/lisp/fibonacci.lsp", include_bytes!("../../dsk/tmp/lisp/fibonacci.lsp"), verbose);
|
||||
copy_file("/tmp/lisp/geotime.lsp", include_bytes!("../../dsk/tmp/lisp/geotime.lsp"), verbose);
|
||||
copy_file("/tmp/lisp/ntp.lsp", include_bytes!("../../dsk/tmp/lisp/ntp.lsp"), verbose);
|
||||
copy_file("/tmp/lisp/pi.lsp", include_bytes!("../../dsk/tmp/lisp/pi.lsp"), verbose);
|
||||
copy_file("/tmp/lisp/sum.lsp", include_bytes!("../../dsk/tmp/lisp/sum.lsp"), verbose);
|
||||
copy_file(
|
||||
"/tmp/lisp/colors.lsp",
|
||||
include_bytes!("../../dsk/tmp/lisp/colors.lsp"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/lisp/doc.lsp",
|
||||
include_bytes!("../../dsk/tmp/lisp/doc.lsp"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/lisp/factorial.lsp",
|
||||
include_bytes!("../../dsk/tmp/lisp/factorial.lsp"),
|
||||
verbose,
|
||||
);
|
||||
/*
|
||||
copy_file(
|
||||
"/tmp/lisp/fetch.lsp",
|
||||
include_bytes!("../../dsk/tmp/lisp/fetch.lsp"),
|
||||
verbose
|
||||
);
|
||||
*/
|
||||
copy_file(
|
||||
"/tmp/lisp/fibonacci.lsp",
|
||||
include_bytes!("../../dsk/tmp/lisp/fibonacci.lsp"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/lisp/geotime.lsp",
|
||||
include_bytes!("../../dsk/tmp/lisp/geotime.lsp"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/lisp/ntp.lsp",
|
||||
include_bytes!("../../dsk/tmp/lisp/ntp.lsp"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/lisp/pi.lsp",
|
||||
include_bytes!("../../dsk/tmp/lisp/pi.lsp"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/lisp/sum.lsp",
|
||||
include_bytes!("../../dsk/tmp/lisp/sum.lsp"),
|
||||
verbose,
|
||||
);
|
||||
|
||||
create_dir("/tmp/life", verbose);
|
||||
copy_file("/tmp/life/centinal.cells", include_bytes!("../../dsk/tmp/life/centinal.cells"), verbose);
|
||||
copy_file("/tmp/life/flower-of-eden.cells", include_bytes!("../../dsk/tmp/life/flower-of-eden.cells"), verbose);
|
||||
copy_file("/tmp/life/garden-of-eden.cells", include_bytes!("../../dsk/tmp/life/garden-of-eden.cells"), verbose);
|
||||
copy_file("/tmp/life/glider-gun.cells", include_bytes!("../../dsk/tmp/life/glider-gun.cells"), verbose);
|
||||
copy_file("/tmp/life/pentadecathlon.cells", include_bytes!("../../dsk/tmp/life/pentadecathlon.cells"), verbose);
|
||||
copy_file("/tmp/life/queen-bee-shuttle.cells", include_bytes!("../../dsk/tmp/life/queen-bee-shuttle.cells"), verbose);
|
||||
copy_file("/tmp/life/ship-in-a-bottle.cells", include_bytes!("../../dsk/tmp/life/ship-in-a-bottle.cells"), verbose);
|
||||
copy_file("/tmp/life/thunderbird.cells", include_bytes!("../../dsk/tmp/life/thunderbird.cells"), verbose);
|
||||
copy_file("/tmp/life/wing.cells", include_bytes!("../../dsk/tmp/life/wing.cells"), verbose);
|
||||
copy_file(
|
||||
"/tmp/life/centinal.cells",
|
||||
include_bytes!("../../dsk/tmp/life/centinal.cells"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/life/flower-of-eden.cells",
|
||||
include_bytes!("../../dsk/tmp/life/flower-of-eden.cells"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/life/garden-of-eden.cells",
|
||||
include_bytes!("../../dsk/tmp/life/garden-of-eden.cells"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/life/glider-gun.cells",
|
||||
include_bytes!("../../dsk/tmp/life/glider-gun.cells"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/life/pentadecathlon.cells",
|
||||
include_bytes!("../../dsk/tmp/life/pentadecathlon.cells"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/life/queen-bee-shuttle.cells",
|
||||
include_bytes!("../../dsk/tmp/life/queen-bee-shuttle.cells"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/life/ship-in-a-bottle.cells",
|
||||
include_bytes!("../../dsk/tmp/life/ship-in-a-bottle.cells"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/life/thunderbird.cells",
|
||||
include_bytes!("../../dsk/tmp/life/thunderbird.cells"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/life/wing.cells",
|
||||
include_bytes!("../../dsk/tmp/life/wing.cells"),
|
||||
verbose,
|
||||
);
|
||||
|
||||
create_dir("/tmp/beep", verbose);
|
||||
copy_file("/tmp/beep/tetris.sh", include_bytes!("../../dsk/tmp/beep/tetris.sh"), verbose);
|
||||
copy_file("/tmp/beep/starwars.sh", include_bytes!("../../dsk/tmp/beep/starwars.sh"), verbose);
|
||||
copy_file("/tmp/beep/mario.sh", include_bytes!("../../dsk/tmp/beep/mario.sh"), verbose);
|
||||
copy_file(
|
||||
"/tmp/beep/tetris.sh",
|
||||
include_bytes!("../../dsk/tmp/beep/tetris.sh"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/beep/starwars.sh",
|
||||
include_bytes!("../../dsk/tmp/beep/starwars.sh"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/tmp/beep/mario.sh",
|
||||
include_bytes!("../../dsk/tmp/beep/mario.sh"),
|
||||
verbose,
|
||||
);
|
||||
|
||||
create_dir("/var/www", verbose);
|
||||
copy_file("/var/www/index.html", include_bytes!("../../dsk/var/www/index.html"), verbose);
|
||||
copy_file("/var/www/moros.css", include_bytes!("../../dsk/var/www/moros.css"), verbose);
|
||||
copy_file("/var/www/moros.png", include_bytes!("../../dsk/var/www/moros.png"), verbose);
|
||||
copy_file(
|
||||
"/var/www/index.html",
|
||||
include_bytes!("../../dsk/var/www/index.html"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/var/www/moros.css",
|
||||
include_bytes!("../../dsk/var/www/moros.css"),
|
||||
verbose,
|
||||
);
|
||||
copy_file(
|
||||
"/var/www/moros.png",
|
||||
include_bytes!("../../dsk/var/www/moros.png"),
|
||||
verbose,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
let csi_color = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Welcome to MOROS v{} installation program!{}", csi_color, env!("CARGO_PKG_VERSION"), csi_reset);
|
||||
println!(
|
||||
"{}Welcome to MOROS v{} installation program!{}",
|
||||
csi_color,
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
csi_reset
|
||||
);
|
||||
println!();
|
||||
|
||||
let mut has_confirmed = false;
|
||||
for &arg in args {
|
||||
match arg {
|
||||
"-y" | "--yes" => has_confirmed = true,
|
||||
_ => continue
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
if !has_confirmed {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::sys;
|
||||
use crate::api::console::Style;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::sys;
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
if args.len() == 1 {
|
||||
|
@ -37,8 +37,14 @@ fn help() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} keyboard {}<command>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} keyboard {}<command>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
println!();
|
||||
println!("{}Commands:{}", csi_title, csi_reset);
|
||||
println!(" {0}set <layout>{1} Set keyboard layout", csi_option, csi_reset);
|
||||
println!(
|
||||
" {0}set <layout>{1} Set keyboard layout",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::api::fs;
|
|||
use crate::api::process::ExitCode;
|
||||
use crate::api::random;
|
||||
use crate::sys;
|
||||
use crate::sys::console;
|
||||
|
||||
use alloc::collections::BTreeSet;
|
||||
use alloc::format;
|
||||
|
@ -43,7 +44,7 @@ impl Game {
|
|||
let cell = (x as i64, y as i64);
|
||||
match c {
|
||||
' ' | '.' | '0' => self.grid.remove(&cell),
|
||||
_ => self.grid.insert(cell),
|
||||
_ => self.grid.insert(cell),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -55,8 +56,8 @@ impl Game {
|
|||
if self.seed_interval > 0 && self.step % self.seed_interval == 0 {
|
||||
self.seed();
|
||||
}
|
||||
if sys::console::end_of_text() || (self.is_game_over() && self.quiet) {
|
||||
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move cursor to top
|
||||
if console::end_of_text() || (self.is_game_over() && self.quiet) {
|
||||
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move to top
|
||||
return;
|
||||
}
|
||||
print!("{}", self);
|
||||
|
@ -74,7 +75,10 @@ impl Game {
|
|||
for x in 0..self.cols {
|
||||
for y in 0..self.rows {
|
||||
let cell = (x, y);
|
||||
match neighboors(&cell).iter().fold(0, |s, c| s + self.grid.contains(c) as u8) {
|
||||
let n = neighboors(&cell).iter().fold(0, |s, c|
|
||||
s + self.grid.contains(c) as u8
|
||||
);
|
||||
match n {
|
||||
2 => continue,
|
||||
3 => cells_to_insert.push(cell),
|
||||
_ => cells_to_remove.push(cell),
|
||||
|
@ -118,15 +122,20 @@ impl Game {
|
|||
let reset = Style::reset();
|
||||
let stats = format!("GEN: {:04} | POP: {:04}", gen, pop);
|
||||
let size = (self.cols as usize) - stats.len();
|
||||
format!("\n{}{:n$}{}{}", color, title, stats, reset, n=size)
|
||||
format!("\n{}{:n$}{}{}", color, title, stats, reset, n = size)
|
||||
}
|
||||
}
|
||||
|
||||
fn neighboors(&(x, y): &(i64, i64)) -> Vec<(i64, i64)> {
|
||||
vec![
|
||||
(x - 1, y - 1), (x, y - 1), (x + 1, y - 1),
|
||||
(x - 1, y), (x + 1, y),
|
||||
(x - 1, y + 1), (x, y + 1), (x + 1, y + 1),
|
||||
(x - 1, y - 1),
|
||||
(x, y - 1),
|
||||
(x + 1, y - 1),
|
||||
(x - 1, y),
|
||||
(x + 1, y),
|
||||
(x - 1, y + 1),
|
||||
(x, y + 1),
|
||||
(x + 1, y + 1),
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -135,7 +144,11 @@ impl fmt::Display for Game {
|
|||
let mut out = String::new();
|
||||
for y in 0..self.rows {
|
||||
for x in 0..self.cols {
|
||||
out.push(if self.grid.contains(&(x, y)) { '#' } else { ' ' });
|
||||
out.push(if self.grid.contains(&(x, y)) {
|
||||
'#'
|
||||
} else {
|
||||
' '
|
||||
});
|
||||
}
|
||||
if y < self.rows - 1 {
|
||||
out.push('\n');
|
||||
|
@ -168,7 +181,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
"-p" | "--population" => {
|
||||
if i + 1 < n {
|
||||
game.seed_population = args[i + 1].parse().unwrap_or(game.seed_population);
|
||||
game.seed_population = args[i + 1].parse().
|
||||
unwrap_or(game.seed_population);
|
||||
i += 1;
|
||||
} else {
|
||||
error!("Missing --population <num>");
|
||||
|
@ -177,7 +191,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
"-i" | "--interval" => {
|
||||
if i + 1 < n {
|
||||
game.seed_interval = args[i + 1].parse().unwrap_or(game.seed_interval);
|
||||
game.seed_interval = args[i + 1].parse().
|
||||
unwrap_or(game.seed_interval);
|
||||
i += 1;
|
||||
} else {
|
||||
error!("Missing --interval <num>");
|
||||
|
@ -229,11 +244,30 @@ fn usage() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} life {}<options> [<path>]{1}", csi_title, csi_reset, csi_option);
|
||||
println!(
|
||||
"{}Usage:{} life {}<options> [<path>]{1}",
|
||||
csi_title, csi_reset, csi_option
|
||||
);
|
||||
println!();
|
||||
println!("{}Options:{}", csi_title, csi_reset);
|
||||
println!(" {0}-p{1}, {0}--population <num>{1} Set the seed population to {0}<num>{1}", csi_option, csi_reset);
|
||||
println!(" {0}-i{1}, {0}--interval <num>{1} Set the seed interval to {0}<num>{1}", csi_option, csi_reset);
|
||||
println!(" {0}-s{1}, {0}--speed <num>{1} Set the simulation speed to {0}<num>{1}", csi_option, csi_reset);
|
||||
println!(" {0}-q{1}, {0}--quiet{1} Enable quiet mode", csi_option, csi_reset);
|
||||
println!(
|
||||
" {0}-p{1}, {0}--population <num>{1} \
|
||||
Set the seed population to {0}<num>{1}",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
println!(
|
||||
" {0}-i{1}, {0}--interval <num>{1} \
|
||||
Set the seed interval to {0}<num>{1}",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
println!(
|
||||
" {0}-s{1}, {0}--speed <num>{1} \
|
||||
Set the simulation speed to {0}<num>{1}",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
println!(
|
||||
" {0}-q{1}, {0}--quiet{1} \
|
||||
Enable quiet mode",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::FUNCTIONS;
|
||||
use super::primitive;
|
||||
use super::eval::BUILT_INS;
|
||||
use super::eval::eval_args;
|
||||
use super::eval::BUILT_INS;
|
||||
use super::primitive;
|
||||
use super::FUNCTIONS;
|
||||
use super::{Err, Exp, Number};
|
||||
use crate::{could_not, expected};
|
||||
|
||||
|
@ -23,69 +23,230 @@ pub struct Env {
|
|||
pub fn default_env() -> Rc<RefCell<Env>> {
|
||||
let mut data: BTreeMap<String, Exp> = BTreeMap::new();
|
||||
|
||||
data.insert("pi".to_string(), Exp::Num(Number::from(PI)));
|
||||
data.insert("=".to_string(), Exp::Primitive(primitive::lisp_eq));
|
||||
data.insert(">".to_string(), Exp::Primitive(primitive::lisp_gt));
|
||||
data.insert(">=".to_string(), Exp::Primitive(primitive::lisp_gte));
|
||||
data.insert("<".to_string(), Exp::Primitive(primitive::lisp_lt));
|
||||
data.insert("<=".to_string(), Exp::Primitive(primitive::lisp_lte));
|
||||
data.insert("*".to_string(), Exp::Primitive(primitive::lisp_mul));
|
||||
data.insert("+".to_string(), Exp::Primitive(primitive::lisp_add));
|
||||
data.insert("-".to_string(), Exp::Primitive(primitive::lisp_sub));
|
||||
data.insert("/".to_string(), Exp::Primitive(primitive::lisp_div));
|
||||
data.insert("^".to_string(), Exp::Primitive(primitive::lisp_exp));
|
||||
data.insert("<<".to_string(), Exp::Primitive(primitive::lisp_shl));
|
||||
data.insert(">>".to_string(), Exp::Primitive(primitive::lisp_shr));
|
||||
data.insert("rem".to_string(), Exp::Primitive(primitive::lisp_rem));
|
||||
data.insert("cos".to_string(), Exp::Primitive(primitive::lisp_cos));
|
||||
data.insert("acos".to_string(), Exp::Primitive(primitive::lisp_acos));
|
||||
data.insert("asin".to_string(), Exp::Primitive(primitive::lisp_asin));
|
||||
data.insert("atan".to_string(), Exp::Primitive(primitive::lisp_atan));
|
||||
data.insert("sin".to_string(), Exp::Primitive(primitive::lisp_sin));
|
||||
data.insert("tan".to_string(), Exp::Primitive(primitive::lisp_tan));
|
||||
data.insert("trunc".to_string(), Exp::Primitive(primitive::lisp_trunc));
|
||||
data.insert("shell".to_string(), Exp::Primitive(primitive::lisp_shell));
|
||||
data.insert("string".to_string(), Exp::Primitive(primitive::lisp_string));
|
||||
data.insert("string->binary".to_string(), Exp::Primitive(primitive::lisp_string_binary));
|
||||
data.insert("binary->string".to_string(), Exp::Primitive(primitive::lisp_binary_string));
|
||||
data.insert("binary->number".to_string(), Exp::Primitive(primitive::lisp_binary_number));
|
||||
data.insert("number->binary".to_string(), Exp::Primitive(primitive::lisp_number_binary));
|
||||
data.insert("number->string".to_string(), Exp::Primitive(primitive::lisp_number_string));
|
||||
data.insert("string->number".to_string(), Exp::Primitive(primitive::lisp_string_number));
|
||||
data.insert("type".to_string(), Exp::Primitive(primitive::lisp_type));
|
||||
data.insert("parse".to_string(), Exp::Primitive(primitive::lisp_parse));
|
||||
data.insert("list".to_string(), Exp::Primitive(primitive::lisp_list));
|
||||
data.insert("sort".to_string(), Exp::Primitive(primitive::lisp_sort));
|
||||
data.insert("unique".to_string(), Exp::Primitive(primitive::lisp_unique));
|
||||
data.insert("contains?".to_string(), Exp::Primitive(primitive::lisp_contains));
|
||||
data.insert("slice".to_string(), Exp::Primitive(primitive::lisp_slice));
|
||||
data.insert("chunks".to_string(), Exp::Primitive(primitive::lisp_chunks));
|
||||
data.insert("length".to_string(), Exp::Primitive(primitive::lisp_length));
|
||||
data.insert("concat".to_string(), Exp::Primitive(primitive::lisp_concat));
|
||||
|
||||
data.insert("number/type".to_string(), Exp::Primitive(primitive::lisp_number_type));
|
||||
data.insert("regex/find".to_string(), Exp::Primitive(primitive::lisp_regex_find));
|
||||
data.insert("string/split".to_string(), Exp::Primitive(primitive::lisp_string_split));
|
||||
data.insert("string/trim".to_string(), Exp::Primitive(primitive::lisp_string_trim));
|
||||
|
||||
data.insert("file/size".to_string(), Exp::Primitive(primitive::lisp_file_size));
|
||||
data.insert("file/open".to_string(), Exp::Primitive(primitive::lisp_file_open));
|
||||
data.insert("file/read".to_string(), Exp::Primitive(primitive::lisp_file_read));
|
||||
data.insert("file/write".to_string(), Exp::Primitive(primitive::lisp_file_write));
|
||||
data.insert("file/close".to_string(), Exp::Primitive(primitive::lisp_file_close));
|
||||
|
||||
data.insert("socket/connect".to_string(), Exp::Primitive(primitive::lisp_socket_connect));
|
||||
data.insert("socket/listen".to_string(), Exp::Primitive(primitive::lisp_socket_listen));
|
||||
data.insert("socket/accept".to_string(), Exp::Primitive(primitive::lisp_socket_accept));
|
||||
|
||||
data.insert("host".to_string(), Exp::Primitive(primitive::lisp_host));
|
||||
|
||||
data.insert("dict".to_string(), Exp::Primitive(primitive::lisp_dict));
|
||||
data.insert("get".to_string(), Exp::Primitive(primitive::lisp_get));
|
||||
data.insert("put".to_string(), Exp::Primitive(primitive::lisp_put));
|
||||
data.insert(
|
||||
"pi".to_string(),
|
||||
Exp::Num(Number::from(PI)),
|
||||
);
|
||||
data.insert(
|
||||
"=".to_string(),
|
||||
Exp::Primitive(primitive::lisp_eq),
|
||||
);
|
||||
data.insert(
|
||||
">".to_string(),
|
||||
Exp::Primitive(primitive::lisp_gt),
|
||||
);
|
||||
data.insert(
|
||||
">=".to_string(),
|
||||
Exp::Primitive(primitive::lisp_gte),
|
||||
);
|
||||
data.insert(
|
||||
"<".to_string(),
|
||||
Exp::Primitive(primitive::lisp_lt),
|
||||
);
|
||||
data.insert(
|
||||
"<=".to_string(),
|
||||
Exp::Primitive(primitive::lisp_lte),
|
||||
);
|
||||
data.insert(
|
||||
"*".to_string(),
|
||||
Exp::Primitive(primitive::lisp_mul),
|
||||
);
|
||||
data.insert(
|
||||
"+".to_string(),
|
||||
Exp::Primitive(primitive::lisp_add),
|
||||
);
|
||||
data.insert(
|
||||
"-".to_string(),
|
||||
Exp::Primitive(primitive::lisp_sub),
|
||||
);
|
||||
data.insert(
|
||||
"/".to_string(),
|
||||
Exp::Primitive(primitive::lisp_div),
|
||||
);
|
||||
data.insert(
|
||||
"^".to_string(),
|
||||
Exp::Primitive(primitive::lisp_exp),
|
||||
);
|
||||
data.insert(
|
||||
"<<".to_string(),
|
||||
Exp::Primitive(primitive::lisp_shl),
|
||||
);
|
||||
data.insert(
|
||||
">>".to_string(),
|
||||
Exp::Primitive(primitive::lisp_shr),
|
||||
);
|
||||
data.insert(
|
||||
"rem".to_string(),
|
||||
Exp::Primitive(primitive::lisp_rem),
|
||||
);
|
||||
data.insert(
|
||||
"cos".to_string(),
|
||||
Exp::Primitive(primitive::lisp_cos),
|
||||
);
|
||||
data.insert(
|
||||
"acos".to_string(),
|
||||
Exp::Primitive(primitive::lisp_acos),
|
||||
);
|
||||
data.insert(
|
||||
"asin".to_string(),
|
||||
Exp::Primitive(primitive::lisp_asin),
|
||||
);
|
||||
data.insert(
|
||||
"atan".to_string(),
|
||||
Exp::Primitive(primitive::lisp_atan),
|
||||
);
|
||||
data.insert(
|
||||
"sin".to_string(),
|
||||
Exp::Primitive(primitive::lisp_sin),
|
||||
);
|
||||
data.insert(
|
||||
"tan".to_string(),
|
||||
Exp::Primitive(primitive::lisp_tan),
|
||||
);
|
||||
data.insert(
|
||||
"trunc".to_string(),
|
||||
Exp::Primitive(primitive::lisp_trunc),
|
||||
);
|
||||
data.insert(
|
||||
"shell".to_string(),
|
||||
Exp::Primitive(primitive::lisp_shell),
|
||||
);
|
||||
data.insert(
|
||||
"string".to_string(),
|
||||
Exp::Primitive(primitive::lisp_string),
|
||||
);
|
||||
data.insert(
|
||||
"string->binary".to_string(),
|
||||
Exp::Primitive(primitive::lisp_string_binary),
|
||||
);
|
||||
data.insert(
|
||||
"binary->string".to_string(),
|
||||
Exp::Primitive(primitive::lisp_binary_string),
|
||||
);
|
||||
data.insert(
|
||||
"binary->number".to_string(),
|
||||
Exp::Primitive(primitive::lisp_binary_number),
|
||||
);
|
||||
data.insert(
|
||||
"number->binary".to_string(),
|
||||
Exp::Primitive(primitive::lisp_number_binary),
|
||||
);
|
||||
data.insert(
|
||||
"number->string".to_string(),
|
||||
Exp::Primitive(primitive::lisp_number_string),
|
||||
);
|
||||
data.insert(
|
||||
"string->number".to_string(),
|
||||
Exp::Primitive(primitive::lisp_string_number),
|
||||
);
|
||||
data.insert(
|
||||
"type".to_string(),
|
||||
Exp::Primitive(primitive::lisp_type),
|
||||
);
|
||||
data.insert(
|
||||
"parse".to_string(),
|
||||
Exp::Primitive(primitive::lisp_parse),
|
||||
);
|
||||
data.insert(
|
||||
"list".to_string(),
|
||||
Exp::Primitive(primitive::lisp_list),
|
||||
);
|
||||
data.insert(
|
||||
"sort".to_string(),
|
||||
Exp::Primitive(primitive::lisp_sort),
|
||||
);
|
||||
data.insert(
|
||||
"unique".to_string(),
|
||||
Exp::Primitive(primitive::lisp_unique),
|
||||
);
|
||||
data.insert(
|
||||
"contains?".to_string(),
|
||||
Exp::Primitive(primitive::lisp_contains),
|
||||
);
|
||||
data.insert(
|
||||
"slice".to_string(),
|
||||
Exp::Primitive(primitive::lisp_slice),
|
||||
);
|
||||
data.insert(
|
||||
"chunks".to_string(),
|
||||
Exp::Primitive(primitive::lisp_chunks),
|
||||
);
|
||||
data.insert(
|
||||
"length".to_string(),
|
||||
Exp::Primitive(primitive::lisp_length),
|
||||
);
|
||||
data.insert(
|
||||
"concat".to_string(),
|
||||
Exp::Primitive(primitive::lisp_concat),
|
||||
);
|
||||
data.insert(
|
||||
"number/type".to_string(),
|
||||
Exp::Primitive(primitive::lisp_number_type),
|
||||
);
|
||||
data.insert(
|
||||
"regex/find".to_string(),
|
||||
Exp::Primitive(primitive::lisp_regex_find),
|
||||
);
|
||||
data.insert(
|
||||
"string/split".to_string(),
|
||||
Exp::Primitive(primitive::lisp_string_split),
|
||||
);
|
||||
data.insert(
|
||||
"string/trim".to_string(),
|
||||
Exp::Primitive(primitive::lisp_string_trim),
|
||||
);
|
||||
data.insert(
|
||||
"file/size".to_string(),
|
||||
Exp::Primitive(primitive::lisp_file_size),
|
||||
);
|
||||
data.insert(
|
||||
"file/open".to_string(),
|
||||
Exp::Primitive(primitive::lisp_file_open),
|
||||
);
|
||||
data.insert(
|
||||
"file/read".to_string(),
|
||||
Exp::Primitive(primitive::lisp_file_read),
|
||||
);
|
||||
data.insert(
|
||||
"file/write".to_string(),
|
||||
Exp::Primitive(primitive::lisp_file_write),
|
||||
);
|
||||
data.insert(
|
||||
"file/close".to_string(),
|
||||
Exp::Primitive(primitive::lisp_file_close),
|
||||
);
|
||||
data.insert(
|
||||
"socket/connect".to_string(),
|
||||
Exp::Primitive(primitive::lisp_socket_connect),
|
||||
);
|
||||
data.insert(
|
||||
"socket/listen".to_string(),
|
||||
Exp::Primitive(primitive::lisp_socket_listen),
|
||||
);
|
||||
data.insert(
|
||||
"socket/accept".to_string(),
|
||||
Exp::Primitive(primitive::lisp_socket_accept),
|
||||
);
|
||||
data.insert(
|
||||
"host".to_string(),
|
||||
Exp::Primitive(primitive::lisp_host),
|
||||
);
|
||||
data.insert(
|
||||
"dict".to_string(),
|
||||
Exp::Primitive(primitive::lisp_dict),
|
||||
);
|
||||
data.insert(
|
||||
"get".to_string(),
|
||||
Exp::Primitive(primitive::lisp_get),
|
||||
);
|
||||
data.insert(
|
||||
"put".to_string(),
|
||||
Exp::Primitive(primitive::lisp_put),
|
||||
);
|
||||
|
||||
// Setup autocompletion
|
||||
*FUNCTIONS.lock() = data.keys().cloned().chain(BUILT_INS.map(String::from)).collect();
|
||||
*FUNCTIONS.lock() = data.keys().cloned().
|
||||
chain(BUILT_INS.map(String::from)).collect();
|
||||
|
||||
Rc::new(RefCell::new(Env { data, outer: None }))
|
||||
}
|
||||
|
@ -103,34 +264,42 @@ pub fn env_get(key: &str, env: &Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
|||
let env = env.borrow_mut();
|
||||
match env.data.get(key) {
|
||||
Some(exp) => Ok(exp.clone()),
|
||||
None => {
|
||||
match &env.outer {
|
||||
Some(outer_env) => env_get(key, outer_env),
|
||||
None => could_not!("find symbol '{}'", key),
|
||||
}
|
||||
}
|
||||
None => match &env.outer {
|
||||
Some(outer_env) => env_get(key, outer_env),
|
||||
None => could_not!("find symbol '{}'", key),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn env_set(key: &str, val: Exp, env: &Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
pub fn env_set(
|
||||
key: &str,
|
||||
val: Exp,
|
||||
env: &Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
let mut env = env.borrow_mut();
|
||||
match env.data.get(key) {
|
||||
Some(_) => {
|
||||
env.data.insert(key.to_string(), val.clone());
|
||||
Ok(val)
|
||||
}
|
||||
None => {
|
||||
match &env.outer {
|
||||
Some(outer_env) => env_set(key, val, outer_env),
|
||||
None => could_not!("find symbol '{}'", key),
|
||||
}
|
||||
}
|
||||
None => match &env.outer {
|
||||
Some(outer_env) => env_set(key, val, outer_env),
|
||||
None => could_not!("find symbol '{}'", key),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
enum InnerEnv { Function, Macro }
|
||||
enum InnerEnv {
|
||||
Function,
|
||||
Macro,
|
||||
}
|
||||
|
||||
fn inner_env(kind: InnerEnv, params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<Env>>) -> Result<Rc<RefCell<Env>>, Err> {
|
||||
fn inner_env(
|
||||
kind: InnerEnv,
|
||||
params: &Exp,
|
||||
args: &[Exp],
|
||||
outer: &mut Rc<RefCell<Env>>,
|
||||
) -> Result<Rc<RefCell<Env>>, Err> {
|
||||
let mut args = match kind {
|
||||
InnerEnv::Function => eval_args(args, outer)?,
|
||||
InnerEnv::Macro => args.to_vec(),
|
||||
|
@ -177,13 +346,24 @@ fn inner_env(kind: InnerEnv, params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<
|
|||
}
|
||||
_ => return expected!("params to be a list"),
|
||||
}
|
||||
Ok(Rc::new(RefCell::new(Env { data, outer: Some(Rc::new(RefCell::new(outer.borrow_mut().clone()))) })))
|
||||
Ok(Rc::new(RefCell::new(Env {
|
||||
data,
|
||||
outer: Some(Rc::new(RefCell::new(outer.borrow_mut().clone()))),
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn function_env(params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<Env>>) -> Result<Rc<RefCell<Env>>, Err> {
|
||||
pub fn function_env(
|
||||
params: &Exp,
|
||||
args: &[Exp],
|
||||
outer: &mut Rc<RefCell<Env>>,
|
||||
) -> Result<Rc<RefCell<Env>>, Err> {
|
||||
inner_env(InnerEnv::Function, params, args, outer)
|
||||
}
|
||||
|
||||
pub fn macro_env(params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<Env>>) -> Result<Rc<RefCell<Env>>, Err> {
|
||||
pub fn macro_env(
|
||||
params: &Exp,
|
||||
args: &[Exp],
|
||||
outer: &mut Rc<RefCell<Env>>,
|
||||
) -> Result<Rc<RefCell<Env>>, Err> {
|
||||
inner_env(InnerEnv::Macro, params, args, outer)
|
||||
}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
use super::{Err, Exp, Env, Function, parse_eval};
|
||||
use super::env::{env_keys, env_get, env_set, function_env};
|
||||
use super::env::{env_get, env_keys, env_set, function_env};
|
||||
use super::expand::expand;
|
||||
use crate::could_not;
|
||||
use super::string;
|
||||
use super::{parse_eval, Env, Err, Exp, Function};
|
||||
use crate::could_not;
|
||||
|
||||
use crate::{ensure_length_eq, ensure_length_gt, expected};
|
||||
use crate::api::fs;
|
||||
use crate::{ensure_length_eq, ensure_length_gt, expected};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::format;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::cell::RefCell;
|
||||
|
||||
fn eval_quote_args(args: &[Exp]) -> Result<Exp, Err> {
|
||||
|
@ -20,55 +20,73 @@ fn eval_quote_args(args: &[Exp]) -> Result<Exp, Err> {
|
|||
Ok(args[0].clone())
|
||||
}
|
||||
|
||||
fn eval_atom_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
fn eval_atom_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
match eval(&args[0], env)? {
|
||||
Exp::List(_) => Ok(Exp::Bool(false)),
|
||||
_ => Ok(Exp::Bool(true)),
|
||||
_ => Ok(Exp::Bool(true)),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_equal_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
fn eval_equal_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
let a = eval(&args[0], env)?;
|
||||
let b = eval(&args[1], env)?;
|
||||
Ok(Exp::Bool(a == b))
|
||||
}
|
||||
|
||||
fn eval_head_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
fn eval_head_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
match eval(&args[0], env)? {
|
||||
Exp::List(list) => {
|
||||
ensure_length_gt!(list, 0);
|
||||
Ok(list[0].clone())
|
||||
},
|
||||
}
|
||||
_ => expected!("first argument to be a list"),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_tail_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
fn eval_tail_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
match eval(&args[0], env)? {
|
||||
Exp::List(list) => {
|
||||
ensure_length_gt!(list, 0);
|
||||
Ok(Exp::List(list[1..].to_vec()))
|
||||
},
|
||||
}
|
||||
_ => expected!("first argument to be a list"),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_cons_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
fn eval_cons_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
match eval(&args[1], env)? {
|
||||
Exp::List(mut list) => {
|
||||
list.insert(0, eval(&args[0], env)?);
|
||||
Ok(Exp::List(list))
|
||||
},
|
||||
}
|
||||
_ => expected!("first argument to be a list"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eval_variable_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
pub fn eval_variable_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
match &args[0] {
|
||||
Exp::Sym(name) => {
|
||||
|
@ -76,28 +94,37 @@ pub fn eval_variable_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Ex
|
|||
env.borrow_mut().data.insert(name.clone(), exp);
|
||||
Ok(Exp::Sym(name.clone()))
|
||||
}
|
||||
_ => expected!("first argument to be a symbol")
|
||||
_ => expected!("first argument to be a symbol"),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_set_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
fn eval_set_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
match &args[0] {
|
||||
Exp::Sym(name) => {
|
||||
let exp = eval(&args[1], env)?;
|
||||
Ok(env_set(name, exp, env)?)
|
||||
}
|
||||
_ => expected!("first argument to be a symbol")
|
||||
_ => expected!("first argument to be a symbol"),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_env_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
fn eval_env_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 0);
|
||||
let keys = env_keys(env)?.iter().map(|k| Exp::Sym(k.clone())).collect();
|
||||
Ok(Exp::List(keys))
|
||||
}
|
||||
|
||||
fn eval_while_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
fn eval_while_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
ensure_length_gt!(args, 1);
|
||||
let cond = &args[0];
|
||||
let mut res = Exp::List(vec![]);
|
||||
|
@ -109,7 +136,10 @@ fn eval_while_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
|
|||
Ok(res)
|
||||
}
|
||||
|
||||
fn eval_apply_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
fn eval_apply_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
ensure_length_gt!(args, 1);
|
||||
let mut args = args.to_vec();
|
||||
match eval(&args.pop().unwrap(), env) {
|
||||
|
@ -119,13 +149,19 @@ fn eval_apply_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
|
|||
eval(&Exp::List(args.to_vec()), env)
|
||||
}
|
||||
|
||||
fn eval_eval_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
fn eval_eval_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let exp = eval(&args[0], env)?;
|
||||
eval(&exp, env)
|
||||
}
|
||||
|
||||
fn eval_do_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
fn eval_do_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
let mut res = Ok(Exp::List(vec![]));
|
||||
for arg in args {
|
||||
res = Ok(eval(arg, env)?);
|
||||
|
@ -133,10 +169,14 @@ fn eval_do_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
|||
res
|
||||
}
|
||||
|
||||
fn eval_load_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
fn eval_load_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let path = string(&eval(&args[0], env)?)?;
|
||||
let mut input = fs::read_to_string(&path).or(could_not!("find file '{}'", path))?;
|
||||
let mut input = fs::read_to_string(&path).
|
||||
or(could_not!("find file '{}'", path))?;
|
||||
loop {
|
||||
let (rest, _) = parse_eval(&input, env)?;
|
||||
if rest.is_empty() {
|
||||
|
@ -147,7 +187,10 @@ fn eval_load_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
|
|||
Ok(Exp::Bool(true))
|
||||
}
|
||||
|
||||
fn eval_doc_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
fn eval_doc_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
match eval(&args[0], env)? {
|
||||
Exp::Primitive(_) => Ok(Exp::Str("".to_string())),
|
||||
|
@ -157,23 +200,40 @@ fn eval_doc_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn eval_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Vec<Exp>, Err> {
|
||||
pub fn eval_args(
|
||||
args: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Vec<Exp>, Err> {
|
||||
args.iter().map(|x| eval(x, env)).collect()
|
||||
}
|
||||
|
||||
pub const BUILT_INS: [&str; 26] = [
|
||||
"quote", "quasiquote", "unquote", "unquote-splicing",
|
||||
"atom?", "equal?", "head", "tail", "cons",
|
||||
"if", "cond", "while",
|
||||
"variable", "function", "macro",
|
||||
"define-function", "define",
|
||||
"quote",
|
||||
"quasiquote",
|
||||
"unquote",
|
||||
"unquote-splicing",
|
||||
"atom?",
|
||||
"equal?",
|
||||
"head",
|
||||
"tail",
|
||||
"cons",
|
||||
"if",
|
||||
"cond",
|
||||
"while",
|
||||
"variable",
|
||||
"function",
|
||||
"macro",
|
||||
"define-function",
|
||||
"define",
|
||||
"define-macro",
|
||||
"set",
|
||||
"apply", "eval", "expand",
|
||||
"apply",
|
||||
"eval",
|
||||
"expand",
|
||||
"do",
|
||||
"load",
|
||||
"doc",
|
||||
"env"
|
||||
"env",
|
||||
];
|
||||
|
||||
pub fn eval(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
|
@ -191,62 +251,104 @@ pub fn eval(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
|||
ensure_length_gt!(list, 0);
|
||||
let args = &list[1..];
|
||||
match &list[0] {
|
||||
Exp::Sym(s) if s == "quote" => return eval_quote_args(args),
|
||||
Exp::Sym(s) if s == "atom?" => return eval_atom_args(args, env),
|
||||
Exp::Sym(s) if s == "equal?" => return eval_equal_args(args, env),
|
||||
Exp::Sym(s) if s == "head" => return eval_head_args(args, env),
|
||||
Exp::Sym(s) if s == "tail" => return eval_tail_args(args, env),
|
||||
Exp::Sym(s) if s == "cons" => return eval_cons_args(args, env),
|
||||
Exp::Sym(s) if s == "set" => return eval_set_args(args, env),
|
||||
Exp::Sym(s) if s == "while" => return eval_while_args(args, env),
|
||||
Exp::Sym(s) if s == "apply" => return eval_apply_args(args, env),
|
||||
Exp::Sym(s) if s == "eval" => return eval_eval_args(args, env),
|
||||
Exp::Sym(s) if s == "do" => return eval_do_args(args, env),
|
||||
Exp::Sym(s) if s == "load" => return eval_load_args(args, env),
|
||||
Exp::Sym(s) if s == "doc" => return eval_doc_args(args, env),
|
||||
Exp::Sym(s) if s == "variable" => return eval_variable_args(args, env),
|
||||
Exp::Sym(s) if s == "env" => return eval_env_args(args, env),
|
||||
Exp::Sym(s) if s == "expand" => {
|
||||
Exp::Sym(s) if s == "quote" => {
|
||||
return eval_quote_args(args);
|
||||
}
|
||||
Exp::Sym(s) if s == "atom?" => {
|
||||
return eval_atom_args(args, env);
|
||||
}
|
||||
Exp::Sym(s) if s == "equal?" => {
|
||||
return eval_equal_args(args, env);
|
||||
}
|
||||
Exp::Sym(s) if s == "head" => {
|
||||
return eval_head_args(args, env);
|
||||
}
|
||||
Exp::Sym(s) if s == "tail" => {
|
||||
return eval_tail_args(args, env);
|
||||
}
|
||||
Exp::Sym(s) if s == "cons" => {
|
||||
return eval_cons_args(args, env);
|
||||
}
|
||||
Exp::Sym(s) if s == "set" => {
|
||||
return eval_set_args(args, env);
|
||||
}
|
||||
Exp::Sym(s) if s == "while" => {
|
||||
return eval_while_args(args, env);
|
||||
}
|
||||
Exp::Sym(s) if s == "apply" => {
|
||||
return eval_apply_args(args, env);
|
||||
}
|
||||
Exp::Sym(s) if s == "eval" => {
|
||||
return eval_eval_args(args, env);
|
||||
}
|
||||
Exp::Sym(s) if s == "do" => {
|
||||
return eval_do_args(args, env);
|
||||
}
|
||||
Exp::Sym(s) if s == "load" => {
|
||||
return eval_load_args(args, env);
|
||||
}
|
||||
Exp::Sym(s) if s == "doc" => {
|
||||
return eval_doc_args(args, env);
|
||||
}
|
||||
Exp::Sym(s) if s == "variable" => {
|
||||
return eval_variable_args(args, env);
|
||||
}
|
||||
Exp::Sym(s) if s == "env" => {
|
||||
return eval_env_args(args, env);
|
||||
}
|
||||
Exp::Sym(s) if s == "expand" => {
|
||||
ensure_length_eq!(args, 1);
|
||||
return expand(&args[0], env);
|
||||
}
|
||||
Exp::Sym(s) if s == "if" => {
|
||||
ensure_length_gt!(args, 1);
|
||||
if eval(&args[0], env)?.is_truthy() { // consequent
|
||||
if eval(&args[0], env)?.is_truthy() { // Consequent
|
||||
exp_tmp = args[1].clone();
|
||||
} else if args.len() > 2 { // alternate
|
||||
} else if args.len() > 2 { // Alternate
|
||||
exp_tmp = args[2].clone();
|
||||
} else { // '()
|
||||
exp_tmp = Exp::List(vec![Exp::Sym("quote".to_string()), Exp::List(vec![])]);
|
||||
exp_tmp = Exp::List(vec![
|
||||
Exp::Sym("quote".to_string()),
|
||||
Exp::List(vec![]),
|
||||
]);
|
||||
}
|
||||
exp = &exp_tmp;
|
||||
}
|
||||
Exp::Sym(s) if s == "function" || s == "macro" => {
|
||||
let (params, body, doc) = match args.len() {
|
||||
2 => (args[0].clone(), args[1].clone(), None),
|
||||
3 => (args[0].clone(), args[2].clone(), Some(string(&args[1])?)),
|
||||
2 => {
|
||||
(args[0].clone(), args[1].clone(), None)
|
||||
}
|
||||
3 => {
|
||||
let doc = Some(string(&args[1])?);
|
||||
(args[0].clone(), args[2].clone(), doc)
|
||||
}
|
||||
_ => return expected!("3 or 4 arguments"),
|
||||
};
|
||||
let f = Box::new(Function { params, body, doc });
|
||||
let exp = if s == "function" { Exp::Function(f) } else { Exp::Macro(f) };
|
||||
let exp = if s == "function" {
|
||||
Exp::Function(f)
|
||||
} else {
|
||||
Exp::Macro(f)
|
||||
};
|
||||
return Ok(exp);
|
||||
}
|
||||
_ => {
|
||||
match eval(&list[0], env)? {
|
||||
Exp::Function(f) => {
|
||||
env_tmp = function_env(&f.params, args, env)?;
|
||||
exp_tmp = f.body;
|
||||
env = &mut env_tmp;
|
||||
exp = &exp_tmp;
|
||||
},
|
||||
Exp::Primitive(f) => {
|
||||
return f(&eval_args(args, env)?)
|
||||
},
|
||||
_ => return expected!("first argument to be a function"),
|
||||
_ => match eval(&list[0], env)? {
|
||||
Exp::Function(f) => {
|
||||
env_tmp = function_env(&f.params, args, env)?;
|
||||
exp_tmp = f.body;
|
||||
env = &mut env_tmp;
|
||||
exp = &exp_tmp;
|
||||
}
|
||||
}
|
||||
Exp::Primitive(f) => {
|
||||
return f(&eval_args(args, env)?);
|
||||
}
|
||||
_ => {
|
||||
return expected!("first argument to be a function");
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => return Err(Err::Reason("Unexpected argument".to_string())),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,45 +1,50 @@
|
|||
use super::{Err, Exp, Env};
|
||||
use super::env::{env_get, macro_env};
|
||||
use super::eval::eval;
|
||||
use super::{Env, Err, Exp};
|
||||
|
||||
use crate::{ensure_length_eq, ensure_length_gt, ensure_list, ensure_string, expected};
|
||||
use crate::{
|
||||
ensure_length_eq, ensure_length_gt, ensure_list, ensure_string, expected
|
||||
};
|
||||
|
||||
use alloc::format;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::cell::RefCell;
|
||||
|
||||
fn is_sym(e: &Exp, name: &str) -> bool {
|
||||
*e == Exp::Sym(name.to_string())
|
||||
}
|
||||
|
||||
pub fn expand_quasiquote(exp: &Exp) -> Result<Exp, Err> {
|
||||
match exp {
|
||||
Exp::List(list) if list.len() > 0 => {
|
||||
match &list[0] {
|
||||
Exp::Sym(s) if s == "unquote" => {
|
||||
Ok(list[1].clone())
|
||||
}
|
||||
Exp::List(l) if l.len() == 2 && l[0] == Exp::Sym("unquote-splice".to_string()) => {
|
||||
Ok(Exp::List(vec![
|
||||
Exp::Sym("concat".to_string()),
|
||||
l[1].clone(),
|
||||
expand_quasiquote(&Exp::List(list[1..].to_vec()))?
|
||||
]))
|
||||
}
|
||||
_ => {
|
||||
Ok(Exp::List(vec![
|
||||
Exp::Sym("cons".to_string()),
|
||||
expand_quasiquote(&list[0])?,
|
||||
expand_quasiquote(&Exp::List(list[1..].to_vec()))?,
|
||||
]))
|
||||
}
|
||||
Exp::List(list) if list.len() > 0 => match &list[0] {
|
||||
Exp::Sym(s) if s == "unquote" => Ok(list[1].clone()),
|
||||
Exp::List(l) if l.len() == 2 && is_sym(&l[0], "unquote-splice") => {
|
||||
Ok(Exp::List(vec![
|
||||
Exp::Sym("concat".to_string()),
|
||||
l[1].clone(),
|
||||
expand_quasiquote(&Exp::List(list[1..].to_vec()))?,
|
||||
]))
|
||||
}
|
||||
}
|
||||
_ => Ok(Exp::List(vec![
|
||||
Exp::Sym("cons".to_string()),
|
||||
expand_quasiquote(&list[0])?,
|
||||
expand_quasiquote(&Exp::List(list[1..].to_vec()))?,
|
||||
])),
|
||||
},
|
||||
_ => Ok(Exp::List(vec![Exp::Sym("quote".to_string()), exp.clone()])),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_list(list: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
||||
let expanded: Result<Vec<Exp>, Err> = list.iter().map(|item| expand(item, env)).collect();
|
||||
pub fn expand_list(
|
||||
list: &[Exp],
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<Exp, Err> {
|
||||
let expanded: Result<Vec<Exp>, Err> = list.iter().map(|item|
|
||||
expand(item, env)
|
||||
).collect();
|
||||
Ok(Exp::List(expanded?))
|
||||
}
|
||||
|
||||
|
@ -74,16 +79,22 @@ pub fn expand(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
|||
let name = args[0].clone();
|
||||
let args = Exp::List(args[1..].to_vec());
|
||||
let body = expand(body, env)?;
|
||||
let mut function = vec![Exp::Sym("function".to_string()), args, body];
|
||||
let mut function = vec![
|
||||
Exp::Sym("function".to_string()),
|
||||
args,
|
||||
body,
|
||||
];
|
||||
if list.len() == 4 {
|
||||
function.insert(2, list[2].clone());
|
||||
}
|
||||
Ok(Exp::List(vec![
|
||||
Exp::Sym("variable".to_string()), name, Exp::List(function)
|
||||
Exp::Sym("variable".to_string()),
|
||||
name,
|
||||
Exp::List(function),
|
||||
]))
|
||||
}
|
||||
Exp::Sym(_) => expand_list(list, env),
|
||||
_ => expected!("first argument to be a symbol or a list")
|
||||
_ => expected!("first argument to be a symbol or a list"),
|
||||
}
|
||||
}
|
||||
Exp::Sym(s) if s == "define-macro" => {
|
||||
|
@ -95,13 +106,17 @@ pub fn expand(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
|||
let args = Exp::List(args[1..].to_vec());
|
||||
let body = expand(&list[2], env)?;
|
||||
Ok(Exp::List(vec![
|
||||
Exp::Sym("variable".to_string()), name, Exp::List(vec![
|
||||
Exp::Sym("macro".to_string()), args, body
|
||||
])
|
||||
Exp::Sym("variable".to_string()),
|
||||
name,
|
||||
Exp::List(vec![
|
||||
Exp::Sym("macro".to_string()),
|
||||
args,
|
||||
body
|
||||
]),
|
||||
]))
|
||||
}
|
||||
(Exp::Sym(_), _) => expand_list(list, env),
|
||||
_ => expected!("first argument to be a symbol or a list")
|
||||
_ => expected!("first argument to be a symbol or a list"),
|
||||
}
|
||||
}
|
||||
Exp::Sym(s) if s == "cond" => {
|
||||
|
@ -110,7 +125,11 @@ pub fn expand(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
|
|||
ensure_length_eq!(args, 2);
|
||||
let test_exp = expand(&args[0], env)?;
|
||||
let then_exp = expand(&args[1], env)?;
|
||||
let mut res = vec![Exp::Sym("if".to_string()), test_exp, then_exp];
|
||||
let mut res = vec![
|
||||
Exp::Sym("if".to_string()),
|
||||
test_exp,
|
||||
then_exp,
|
||||
];
|
||||
if list.len() > 2 {
|
||||
let mut acc = vec![Exp::Sym("cond".to_string())];
|
||||
acc.extend_from_slice(&list[2..]);
|
||||
|
|
|
@ -5,34 +5,36 @@ mod number;
|
|||
mod parse;
|
||||
mod primitive;
|
||||
|
||||
pub use number::Number;
|
||||
pub use env::Env;
|
||||
pub use number::Number;
|
||||
|
||||
use env::default_env;
|
||||
use eval::{eval, eval_variable_args};
|
||||
use expand::expand;
|
||||
use parse::parse;
|
||||
|
||||
use crate::api::fs;
|
||||
use crate::api::console::Style;
|
||||
use crate::api::fs;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::prompt::Prompt;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::collections::btree_map::BTreeMap;
|
||||
use alloc::format;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::string::String;
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::collections::btree_map::BTreeMap;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::cell::RefCell;
|
||||
use core::convert::TryInto;
|
||||
use core::cmp;
|
||||
use core::convert::TryInto;
|
||||
use core::fmt;
|
||||
use lazy_static::lazy_static;
|
||||
use spin::Mutex;
|
||||
|
||||
// MOROS Lisp is a lisp-1 like Scheme and Clojure
|
||||
//
|
||||
// Eval & Env adapted from Risp
|
||||
// Copyright 2019 Stepan Parunashvili
|
||||
// https://github.com/stopachka/risp
|
||||
|
@ -40,11 +42,16 @@ use spin::Mutex;
|
|||
// Parser rewritten from scratch using Nom
|
||||
// https://github.com/geal/nom
|
||||
//
|
||||
// See "Recursive Functions of Symic Expressions and Their Computation by Machine" by John McCarthy (1960)
|
||||
// And "The Roots of Lisp" by Paul Graham (2002)
|
||||
// References:
|
||||
//
|
||||
// MOROS Lisp is a lisp-1 like Scheme and Clojure
|
||||
// See "Technical Issues of Separation in Function Cells and Value Cells" by Richard P. Gabriel (1982)
|
||||
// "Recursive Functions of Symic Expressions and Their Computation by Machine"
|
||||
// by John McCarthy (1960)
|
||||
//
|
||||
// "The Roots of Lisp"
|
||||
// by Paul Graham (2002)
|
||||
//
|
||||
// "Technical Issues of Separation in Function Cells and Value Cells"
|
||||
// by Richard P. Gabriel (1982)
|
||||
|
||||
// Types
|
||||
|
||||
|
@ -75,13 +82,13 @@ impl PartialEq for Exp {
|
|||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Exp::Function(a), Exp::Function(b)) => a == b,
|
||||
(Exp::Macro(a), Exp::Macro(b)) => a == b,
|
||||
(Exp::List(a), Exp::List(b)) => a == b,
|
||||
(Exp::Dict(a), Exp::Dict(b)) => a == b,
|
||||
(Exp::Bool(a), Exp::Bool(b)) => a == b,
|
||||
(Exp::Num(a), Exp::Num(b)) => a == b,
|
||||
(Exp::Str(a), Exp::Str(b)) => a == b,
|
||||
(Exp::Sym(a), Exp::Sym(b)) => a == b,
|
||||
(Exp::Macro(a), Exp::Macro(b)) => a == b,
|
||||
(Exp::List(a), Exp::List(b)) => a == b,
|
||||
(Exp::Dict(a), Exp::Dict(b)) => a == b,
|
||||
(Exp::Bool(a), Exp::Bool(b)) => a == b,
|
||||
(Exp::Num(a), Exp::Num(b)) => a == b,
|
||||
(Exp::Str(a), Exp::Str(b)) => a == b,
|
||||
(Exp::Sym(a), Exp::Sym(b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -91,13 +98,13 @@ impl PartialOrd for Exp {
|
|||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
match (self, other) {
|
||||
(Exp::Function(a), Exp::Function(b)) => a.partial_cmp(b),
|
||||
(Exp::Macro(a), Exp::Macro(b)) => a.partial_cmp(b),
|
||||
(Exp::List(a), Exp::List(b)) => a.partial_cmp(b),
|
||||
(Exp::Dict(a), Exp::Dict(b)) => a.partial_cmp(b),
|
||||
(Exp::Bool(a), Exp::Bool(b)) => a.partial_cmp(b),
|
||||
(Exp::Num(a), Exp::Num(b)) => a.partial_cmp(b),
|
||||
(Exp::Str(a), Exp::Str(b)) => a.partial_cmp(b),
|
||||
(Exp::Sym(a), Exp::Sym(b)) => a.partial_cmp(b),
|
||||
(Exp::Macro(a), Exp::Macro(b)) => a.partial_cmp(b),
|
||||
(Exp::List(a), Exp::List(b)) => a.partial_cmp(b),
|
||||
(Exp::Dict(a), Exp::Dict(b)) => a.partial_cmp(b),
|
||||
(Exp::Bool(a), Exp::Bool(b)) => a.partial_cmp(b),
|
||||
(Exp::Num(a), Exp::Num(b)) => a.partial_cmp(b),
|
||||
(Exp::Str(a), Exp::Str(b)) => a.partial_cmp(b),
|
||||
(Exp::Sym(a), Exp::Sym(b)) => a.partial_cmp(b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -107,20 +114,25 @@ impl fmt::Display for Exp {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let out = match self {
|
||||
Exp::Primitive(_) => format!("(function args)"),
|
||||
Exp::Function(f) => format!("(function {})", f.params),
|
||||
Exp::Macro(m) => format!("(macro {})", m.params),
|
||||
Exp::Bool(a) => a.to_string(),
|
||||
Exp::Num(n) => n.to_string(),
|
||||
Exp::Sym(s) => s.clone(),
|
||||
Exp::Str(s) => format!("{:?}", s).replace("\\u{8}", "\\b").replace("\\u{1b}", "\\e"),
|
||||
Exp::List(list) => {
|
||||
let xs: Vec<String> = list.iter().map(|x| x.to_string()).collect();
|
||||
Exp::Function(f) => format!("(function {})", f.params),
|
||||
Exp::Macro(m) => format!("(macro {})", m.params),
|
||||
Exp::Bool(a) => a.to_string(),
|
||||
Exp::Num(n) => n.to_string(),
|
||||
Exp::Sym(s) => s.clone(),
|
||||
Exp::Str(s) => {
|
||||
format!("{:?}", s).
|
||||
replace("\\u{8}", "\\b").replace("\\u{1b}", "\\e")
|
||||
}
|
||||
Exp::List(list) => {
|
||||
let xs: Vec<_> = list.iter().map(|x| x.to_string()).collect();
|
||||
format!("({})", xs.join(" "))
|
||||
},
|
||||
Exp::Dict(dict) => {
|
||||
let xs: Vec<String> = dict.iter().map(|(k, v)| format!("{} {}", k, v)).collect();
|
||||
}
|
||||
Exp::Dict(dict) => {
|
||||
let xs: Vec<_> = dict.iter().map(|(k, v)|
|
||||
format!("{} {}", k, v)
|
||||
).collect();
|
||||
format!("(dict {})", xs.join(" "))
|
||||
},
|
||||
}
|
||||
};
|
||||
write!(f, "{}", out)
|
||||
}
|
||||
|
@ -166,7 +178,7 @@ macro_rules! ensure_length_gt {
|
|||
macro_rules! ensure_string {
|
||||
($exp:expr) => {
|
||||
match $exp {
|
||||
Exp::Str(_) => {},
|
||||
Exp::Str(_) => {}
|
||||
_ => return expected!("a string"),
|
||||
}
|
||||
};
|
||||
|
@ -176,7 +188,7 @@ macro_rules! ensure_string {
|
|||
macro_rules! ensure_list {
|
||||
($exp:expr) => {
|
||||
match $exp {
|
||||
Exp::List(_) => {},
|
||||
Exp::List(_) => {}
|
||||
_ => return expected!("a list"),
|
||||
}
|
||||
};
|
||||
|
@ -237,7 +249,10 @@ pub fn byte(exp: &Exp) -> Result<u8, Err> {
|
|||
|
||||
// REPL
|
||||
|
||||
fn parse_eval(input: &str, env: &mut Rc<RefCell<Env>>) -> Result<(String, Exp), Err> {
|
||||
fn parse_eval(
|
||||
input: &str,
|
||||
env: &mut Rc<RefCell<Env>>
|
||||
) -> Result<(String, Exp), Err> {
|
||||
let (rest, exp) = parse(input)?;
|
||||
let exp = expand(&exp, env)?;
|
||||
let exp = eval(&exp, env)?;
|
||||
|
@ -349,7 +364,10 @@ fn help() -> Result<(), ExitCode> {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} lisp {}[<file> [<args>]]{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} lisp {}[<file> [<args>]]{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -431,7 +449,10 @@ fn test_lisp() {
|
|||
|
||||
// cons
|
||||
assert_eq!(eval!("(cons (quote 1) (quote (2 3)))"), "(1 2 3)");
|
||||
assert_eq!(eval!("(cons (quote 1) (cons (quote 2) (cons (quote 3) (quote ()))))"), "(1 2 3)");
|
||||
assert_eq!(
|
||||
eval!("(cons (quote 1) (cons (quote 2) (cons (quote 3) (quote ()))))"),
|
||||
"(1 2 3)"
|
||||
);
|
||||
|
||||
// cond
|
||||
assert_eq!(eval!("(cond ((< 2 4) 1))"), "1");
|
||||
|
@ -452,14 +473,18 @@ fn test_lisp() {
|
|||
assert_eq!(eval!("(if \"\" 1 2)"), "1");
|
||||
|
||||
// while
|
||||
assert_eq!(eval!("(do (variable i 0) (while (< i 5) (set i (+ i 1))) i)"), "5");
|
||||
assert_eq!(
|
||||
eval!("(do (variable i 0) (while (< i 5) (set i (+ i 1))) i)"),
|
||||
"5"
|
||||
);
|
||||
|
||||
// variable
|
||||
eval!("(variable a 2)");
|
||||
assert_eq!(eval!("(+ a 1)"), "3");
|
||||
eval!("(variable add-one (function (b) (+ b 1)))");
|
||||
assert_eq!(eval!("(add-one 2)"), "3");
|
||||
eval!("(variable fibonacci (function (n) (if (< n 2) n (+ (fibonacci (- n 1)) (fibonacci (- n 2))))))");
|
||||
eval!("(variable fibonacci (function (n) \
|
||||
(if (< n 2) n (+ (fibonacci (- n 1)) (fibonacci (- n 2))))))");
|
||||
assert_eq!(eval!("(fibonacci 6)"), "8");
|
||||
|
||||
// function
|
||||
|
@ -525,7 +550,10 @@ fn test_lisp() {
|
|||
|
||||
// number
|
||||
assert_eq!(eval!("(binary->number (number->binary 42) \"int\")"), "42");
|
||||
assert_eq!(eval!("(binary->number (number->binary 42.0) \"float\")"), "42.0");
|
||||
assert_eq!(
|
||||
eval!("(binary->number (number->binary 42.0) \"float\")"),
|
||||
"42.0"
|
||||
);
|
||||
|
||||
// string
|
||||
assert_eq!(eval!("(parse \"9.75\")"), "9.75");
|
||||
|
@ -535,7 +563,10 @@ fn test_lisp() {
|
|||
assert_eq!(eval!("(equal? \"foo\" \"foo\")"), "true");
|
||||
assert_eq!(eval!("(equal? \"foo\" \"bar\")"), "false");
|
||||
assert_eq!(eval!("(string/trim \"abc\n\")"), "\"abc\"");
|
||||
assert_eq!(eval!("(string/split \"a\nb\nc\" \"\n\")"), "(\"a\" \"b\" \"c\")");
|
||||
assert_eq!(
|
||||
eval!("(string/split \"a\nb\nc\" \"\n\")"),
|
||||
"(\"a\" \"b\" \"c\")"
|
||||
);
|
||||
|
||||
// apply
|
||||
assert_eq!(eval!("(apply + '(1 2 3))"), "6");
|
||||
|
@ -559,24 +590,66 @@ fn test_lisp() {
|
|||
assert_eq!(eval!("(list 1 2 (+ 1 2))"), "(1 2 3)");
|
||||
|
||||
// bigint
|
||||
assert_eq!(eval!("9223372036854775807"), "9223372036854775807"); // -> int
|
||||
assert_eq!(eval!("9223372036854775808"), "9223372036854775808"); // -> bigint
|
||||
assert_eq!(eval!("0x7fffffffffffffff"), "9223372036854775807"); // -> int
|
||||
assert_eq!(eval!("0x8000000000000000"), "9223372036854775808"); // -> bigint
|
||||
assert_eq!(eval!("0x800000000000000f"), "9223372036854775823"); // -> bigint
|
||||
assert_eq!(eval!("(+ 9223372036854775807 0)"), "9223372036854775807"); // -> int
|
||||
assert_eq!(eval!("(- 9223372036854775808 1)"), "9223372036854775807"); // -> bigint
|
||||
assert_eq!(eval!("(+ 9223372036854775807 1)"), "9223372036854775808"); // -> bigint
|
||||
assert_eq!(eval!("(+ 9223372036854775807 1.0)"), "9223372036854776000.0"); // -> float
|
||||
assert_eq!(eval!("(+ 9223372036854775807 10)"), "9223372036854775817"); // -> bigint
|
||||
assert_eq!(eval!("(* 9223372036854775807 10)"), "92233720368547758070"); // -> bigint
|
||||
assert_eq!(
|
||||
eval!("9223372036854775807"),
|
||||
"9223372036854775807" // -> int
|
||||
);
|
||||
assert_eq!(
|
||||
eval!("9223372036854775808"),
|
||||
"9223372036854775808" // -> bigint
|
||||
);
|
||||
assert_eq!(
|
||||
eval!("0x7fffffffffffffff"),
|
||||
"9223372036854775807" // -> int
|
||||
);
|
||||
assert_eq!(
|
||||
eval!("0x8000000000000000"),
|
||||
"9223372036854775808" // -> bigint
|
||||
);
|
||||
assert_eq!(
|
||||
eval!("0x800000000000000f"),
|
||||
"9223372036854775823" // -> bigint
|
||||
);
|
||||
assert_eq!(
|
||||
eval!("(+ 9223372036854775807 0)"),
|
||||
"9223372036854775807" // -> int
|
||||
);
|
||||
assert_eq!(
|
||||
eval!("(- 9223372036854775808 1)"),
|
||||
"9223372036854775807" // -> bigint
|
||||
);
|
||||
assert_eq!(
|
||||
eval!("(+ 9223372036854775807 1)"),
|
||||
"9223372036854775808" // -> bigint
|
||||
);
|
||||
assert_eq!(
|
||||
eval!("(+ 9223372036854775807 1.0)"),
|
||||
"9223372036854776000.0" // -> float
|
||||
);
|
||||
assert_eq!(
|
||||
eval!("(+ 9223372036854775807 10)"),
|
||||
"9223372036854775817" // -> bigint
|
||||
);
|
||||
assert_eq!(
|
||||
eval!("(* 9223372036854775807 10)"),
|
||||
"92233720368547758070" // -> bigint
|
||||
);
|
||||
|
||||
assert_eq!(eval!("(^ 2 16)"), "65536"); // -> int
|
||||
assert_eq!(eval!("(^ 2 128)"), "340282366920938463463374607431768211456"); // -> bigint
|
||||
assert_eq!(eval!("(^ 2.0 128)"), "340282366920938500000000000000000000000.0"); // -> float
|
||||
assert_eq!(
|
||||
eval!("(^ 2 16)"),
|
||||
"65536" // -> int
|
||||
);
|
||||
assert_eq!(
|
||||
eval!("(^ 2 128)"),
|
||||
"340282366920938463463374607431768211456" // -> bigint
|
||||
);
|
||||
assert_eq!(
|
||||
eval!("(^ 2.0 128)"),
|
||||
"340282366920938500000000000000000000000.0" // -> float
|
||||
);
|
||||
|
||||
assert_eq!(eval!("(number/type 9223372036854775807)"), "\"int\"");
|
||||
assert_eq!(eval!("(number/type 9223372036854775808)"), "\"bigint\"");
|
||||
assert_eq!(eval!("(number/type 9223372036854775807)"), "\"int\"");
|
||||
assert_eq!(eval!("(number/type 9223372036854775808)"), "\"bigint\"");
|
||||
assert_eq!(eval!("(number/type 9223372036854776000.0)"), "\"float\"");
|
||||
|
||||
// quasiquote
|
||||
|
@ -614,7 +687,10 @@ fn test_lisp() {
|
|||
assert_eq!(eval!("(list 1 2 3)"), "(1 2 3)");
|
||||
|
||||
// dict
|
||||
assert_eq!(eval!("(dict \"a\" 1 \"b\" 2 \"c\" 3)"), "(dict \"a\" 1 \"b\" 2 \"c\" 3)");
|
||||
assert_eq!(
|
||||
eval!("(dict \"a\" 1 \"b\" 2 \"c\" 3)"),
|
||||
"(dict \"a\" 1 \"b\" 2 \"c\" 3)"
|
||||
);
|
||||
|
||||
// get
|
||||
assert_eq!(eval!("(get \"Hello\" 0)"), "\"H\"");
|
||||
|
@ -625,7 +701,10 @@ fn test_lisp() {
|
|||
assert_eq!(eval!("(get (dict \"a\" 1 \"b\" 2 \"c\" 3) \"d\")"), "()");
|
||||
|
||||
// put
|
||||
assert_eq!(eval!("(put (dict \"a\" 1 \"b\" 2) \"c\" 3)"), "(dict \"a\" 1 \"b\" 2 \"c\" 3)");
|
||||
assert_eq!(
|
||||
eval!("(put (dict \"a\" 1 \"b\" 2) \"c\" 3)"),
|
||||
"(dict \"a\" 1 \"b\" 2 \"c\" 3)"
|
||||
);
|
||||
assert_eq!(eval!("(put (list 1 3) 1 2)"), "(1 2 3)");
|
||||
assert_eq!(eval!("(put \"Heo\" 2 \"ll\")"), "\"Hello\"");
|
||||
}
|
||||
|
|
|
@ -6,13 +6,13 @@ use alloc::vec::Vec;
|
|||
use core::convert::TryFrom;
|
||||
use core::fmt;
|
||||
use core::num::ParseIntError;
|
||||
use core::ops::{Neg, Add, Div, Mul, Sub, Rem, Shl, Shr};
|
||||
use core::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub};
|
||||
use core::str::FromStr;
|
||||
use num_bigint::BigInt;
|
||||
use num_bigint::ParseBigIntError;
|
||||
use num_traits::cast::ToPrimitive;
|
||||
use num_traits::Num;
|
||||
use num_traits::Zero;
|
||||
use num_traits::cast::ToPrimitive;
|
||||
|
||||
#[derive(Clone, PartialEq, PartialOrd)]
|
||||
pub enum Number {
|
||||
|
@ -26,16 +26,22 @@ macro_rules! trigonometric_method {
|
|||
pub fn $op(&self) -> Number {
|
||||
Number::Float(libm::$op(self.into()))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! arithmetic_method {
|
||||
($op:ident, $checked_op:ident) => {
|
||||
pub fn $op(self, other: Number) -> Number {
|
||||
match (self, other) {
|
||||
(Number::BigInt(a), Number::BigInt(b)) => Number::BigInt(a.$op(b)),
|
||||
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.$op(b)),
|
||||
(Number::Int(a), Number::BigInt(b)) => Number::BigInt(a.$op(b)),
|
||||
(Number::BigInt(a), Number::BigInt(b)) => {
|
||||
Number::BigInt(a.$op(b))
|
||||
}
|
||||
(Number::BigInt(a), Number::Int(b)) => {
|
||||
Number::BigInt(a.$op(b))
|
||||
}
|
||||
(Number::Int(a), Number::BigInt(b)) => {
|
||||
Number::BigInt(a.$op(b))
|
||||
}
|
||||
(Number::Int(a), Number::Int(b)) => {
|
||||
if let Some(r) = a.$checked_op(b) {
|
||||
Number::Int(r)
|
||||
|
@ -43,13 +49,21 @@ macro_rules! arithmetic_method {
|
|||
Number::BigInt(BigInt::from(a).$op(BigInt::from(b)))
|
||||
}
|
||||
}
|
||||
(Number::Int(a), Number::Float(b)) => Number::Float((a as f64).$op(b)),
|
||||
(Number::Float(a), Number::Int(b)) => Number::Float(a.$op(b as f64)),
|
||||
(Number::Float(a), Number::Float(b)) => Number::Float(a.$op(b)),
|
||||
_ => Number::Float(f64::NAN), // TODO
|
||||
(Number::Int(a), Number::Float(b)) => {
|
||||
Number::Float((a as f64).$op(b))
|
||||
}
|
||||
(Number::Float(a), Number::Int(b)) => {
|
||||
Number::Float(a.$op(b as f64))
|
||||
}
|
||||
(Number::Float(a), Number::Float(b)) => {
|
||||
Number::Float(a.$op(b))
|
||||
}
|
||||
_ => {
|
||||
Number::Float(f64::NAN) // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Number {
|
||||
|
@ -68,9 +82,15 @@ impl Number {
|
|||
// NOTE: Rem use `libm::fmod` for `f64` instead of `rem`
|
||||
pub fn rem(self, other: Number) -> Number {
|
||||
match (self, other) {
|
||||
(Number::BigInt(a), Number::BigInt(b)) => Number::BigInt(a.rem(b)),
|
||||
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.rem(b)),
|
||||
(Number::Int(a), Number::BigInt(b)) => Number::BigInt(a.rem(b)),
|
||||
(Number::BigInt(a), Number::BigInt(b)) => {
|
||||
Number::BigInt(a.rem(b))
|
||||
}
|
||||
(Number::BigInt(a), Number::Int(b)) => {
|
||||
Number::BigInt(a.rem(b))
|
||||
}
|
||||
(Number::Int(a), Number::BigInt(b)) => {
|
||||
Number::BigInt(a.rem(b))
|
||||
}
|
||||
(Number::Int(a), Number::Int(b)) => {
|
||||
if let Some(r) = a.checked_rem(b) {
|
||||
Number::Int(r)
|
||||
|
@ -78,10 +98,18 @@ impl Number {
|
|||
Number::BigInt(BigInt::from(a).rem(BigInt::from(b)))
|
||||
}
|
||||
}
|
||||
(Number::Int(a), Number::Float(b)) => Number::Float(libm::fmod(a as f64, b)),
|
||||
(Number::Float(a), Number::Int(b)) => Number::Float(libm::fmod(a, b as f64)),
|
||||
(Number::Float(a), Number::Float(b)) => Number::Float(libm::fmod(a, b)),
|
||||
_ => Number::Float(f64::NAN), // TODO
|
||||
(Number::Int(a), Number::Float(b)) => {
|
||||
Number::Float(libm::fmod(a as f64, b))
|
||||
}
|
||||
(Number::Float(a), Number::Int(b)) => {
|
||||
Number::Float(libm::fmod(a, b as f64))
|
||||
}
|
||||
(Number::Float(a), Number::Float(b)) => {
|
||||
Number::Float(libm::fmod(a, b))
|
||||
}
|
||||
_ => {
|
||||
Number::Float(f64::NAN) // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,9 +117,15 @@ impl Number {
|
|||
let bmax = BigInt::from(u32::MAX);
|
||||
let imax = u32::MAX as i64;
|
||||
match (self, other) {
|
||||
(_, Number::BigInt(b)) if *b > bmax => Number::Float(f64::INFINITY),
|
||||
(_, Number::Int(b)) if *b > imax => Number::Float(f64::INFINITY),
|
||||
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.pow(*b as u32)),
|
||||
(_, Number::BigInt(b)) if *b > bmax => {
|
||||
Number::Float(f64::INFINITY)
|
||||
}
|
||||
(_, Number::Int(b)) if *b > imax => {
|
||||
Number::Float(f64::INFINITY)
|
||||
}
|
||||
(Number::BigInt(a), Number::Int(b)) => {
|
||||
Number::BigInt(a.pow(*b as u32))
|
||||
}
|
||||
(Number::Int(a), Number::Int(b)) => {
|
||||
if let Some(r) = a.checked_pow(*b as u32) {
|
||||
Number::Int(r)
|
||||
|
@ -99,16 +133,26 @@ impl Number {
|
|||
Number::BigInt(BigInt::from(*a)).pow(other)
|
||||
}
|
||||
}
|
||||
(Number::Int(a), Number::Float(b)) => Number::Float(libm::pow(*a as f64, *b)),
|
||||
(Number::Float(a), Number::Int(b)) => Number::Float(libm::pow(*a, *b as f64)),
|
||||
(Number::Float(a), Number::Float(b)) => Number::Float(libm::pow(*a, *b)),
|
||||
_ => Number::Float(f64::NAN), // TODO
|
||||
(Number::Int(a), Number::Float(b)) => {
|
||||
Number::Float(libm::pow(*a as f64, *b))
|
||||
}
|
||||
(Number::Float(a), Number::Int(b)) => {
|
||||
Number::Float(libm::pow(*a, *b as f64))
|
||||
}
|
||||
(Number::Float(a), Number::Float(b)) => {
|
||||
Number::Float(libm::pow(*a, *b))
|
||||
}
|
||||
_ => {
|
||||
Number::Float(f64::NAN) // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn neg(self) -> Number {
|
||||
match self {
|
||||
Number::BigInt(a) => Number::BigInt(-a),
|
||||
Number::BigInt(a) => {
|
||||
Number::BigInt(-a)
|
||||
}
|
||||
Number::Int(a) => {
|
||||
if let Some(r) = a.checked_neg() {
|
||||
Number::Int(r)
|
||||
|
@ -116,7 +160,9 @@ impl Number {
|
|||
Number::BigInt(-BigInt::from(a))
|
||||
}
|
||||
}
|
||||
Number::Float(a) => Number::Float(-a),
|
||||
Number::Float(a) => {
|
||||
Number::Float(-a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,7 +176,9 @@ impl Number {
|
|||
|
||||
pub fn shl(self, other: Number) -> Number {
|
||||
match (self, other) {
|
||||
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.shl(b)),
|
||||
(Number::BigInt(a), Number::Int(b)) => {
|
||||
Number::BigInt(a.shl(b))
|
||||
}
|
||||
(Number::Int(a), Number::Int(b)) => {
|
||||
if let Some(r) = a.checked_shl(b as u32) {
|
||||
Number::Int(r)
|
||||
|
@ -138,13 +186,17 @@ impl Number {
|
|||
Number::BigInt(BigInt::from(a).shl(b))
|
||||
}
|
||||
}
|
||||
_ => Number::Float(f64::NAN), // TODO
|
||||
_ => {
|
||||
Number::Float(f64::NAN) // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shr(self, other: Number) -> Number {
|
||||
match (self, other) {
|
||||
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.shr(b)),
|
||||
(Number::BigInt(a), Number::Int(b)) => {
|
||||
Number::BigInt(a.shr(b))
|
||||
}
|
||||
(Number::Int(a), Number::Int(b)) => {
|
||||
if let Some(r) = a.checked_shr(b as u32) {
|
||||
Number::Int(r)
|
||||
|
@ -152,7 +204,9 @@ impl Number {
|
|||
Number::BigInt(BigInt::from(a).shr(b))
|
||||
}
|
||||
}
|
||||
_ => Number::Float(f64::NAN), // TODO
|
||||
_ => {
|
||||
Number::Float(f64::NAN) // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,7 +242,7 @@ macro_rules! operator {
|
|||
self.$op(other)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
operator!(Add, add);
|
||||
|
@ -285,8 +339,8 @@ impl From<usize> for Number {
|
|||
impl From<&Number> for f64 {
|
||||
fn from(num: &Number) -> f64 {
|
||||
match num {
|
||||
Number::Float(n) => *n,
|
||||
Number::Int(n) => *n as f64,
|
||||
Number::Float(n) => *n,
|
||||
Number::Int(n) => *n as f64,
|
||||
Number::BigInt(n) => n.to_f64().unwrap_or(f64::NAN),
|
||||
}
|
||||
}
|
||||
|
@ -298,15 +352,17 @@ macro_rules! try_from_number {
|
|||
type Error = Err;
|
||||
|
||||
fn try_from(num: Number) -> Result<Self, Self::Error> {
|
||||
let err = Err::Reason(format!("Expected an integer between 0 and {}", $int::MAX));
|
||||
let err = Err::Reason(
|
||||
format!("Expected an integer between 0 and {}", $int::MAX)
|
||||
);
|
||||
match num {
|
||||
Number::Float(n) => $int::try_from(n as i64).or(Err(err)),
|
||||
Number::Int(n) => $int::try_from(n).or(Err(err)),
|
||||
Number::Float(n) => $int::try_from(n as i64).or(Err(err)),
|
||||
Number::Int(n) => $int::try_from(n).or(Err(err)),
|
||||
Number::BigInt(n) => n.$to_int().ok_or(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
try_from_number!(usize, to_usize);
|
||||
|
@ -315,13 +371,15 @@ try_from_number!(u8, to_u8);
|
|||
|
||||
impl fmt::Display for Number {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
//write!(f, "{}", self), // FIXME: alloc error
|
||||
// FIXME: alloc error
|
||||
// write!(f, "{}", self)
|
||||
match self {
|
||||
Number::Int(n) => {
|
||||
write!(f, "{}", n)
|
||||
}
|
||||
Number::BigInt(n) => {
|
||||
//write!(f, "{}", n), // FIXME: rust-lld: error: undefined symbol: fmod
|
||||
// FIXME: rust-lld: error: undefined symbol: fmod
|
||||
// write!(f, "{}", n)
|
||||
let mut v = Vec::new();
|
||||
let mut n = n.clone();
|
||||
if n < BigInt::from(0) {
|
||||
|
|
|
@ -5,8 +5,6 @@ use alloc::string::String;
|
|||
use alloc::string::ToString;
|
||||
use alloc::vec;
|
||||
|
||||
use nom::Err::Error;
|
||||
use nom::IResult;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::escaped_transform;
|
||||
use nom::bytes::complete::is_not;
|
||||
|
@ -14,84 +12,69 @@ use nom::bytes::complete::tag;
|
|||
use nom::bytes::complete::take_while1;
|
||||
use nom::character::complete::char;
|
||||
use nom::character::complete::multispace0;
|
||||
use nom::character::complete::one_of;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::opt;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::value;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many1;
|
||||
use nom::sequence::delimited;
|
||||
use nom::sequence::preceded;
|
||||
use nom::sequence::tuple;
|
||||
use nom::character::complete::one_of;
|
||||
use nom::multi::many1;
|
||||
use nom::sequence::terminated;
|
||||
use nom::sequence::tuple;
|
||||
use nom::Err::Error;
|
||||
use nom::IResult;
|
||||
|
||||
// https://docs.rs/nom/latest/nom/recipes/index.html#hexadecimal
|
||||
fn hexadecimal(input: &str) -> IResult<&str, &str> {
|
||||
preceded(
|
||||
tag("0x"),
|
||||
recognize(
|
||||
many1(
|
||||
terminated(one_of("0123456789abcdefABCDEF"), many0(char('_')))
|
||||
)
|
||||
)
|
||||
recognize(many1(terminated(
|
||||
one_of("0123456789abcdefABCDEF"),
|
||||
many0(char('_')),
|
||||
))),
|
||||
)(input)
|
||||
}
|
||||
|
||||
// https://docs.rs/nom/latest/nom/recipes/index.html#decimal
|
||||
fn decimal(input: &str) -> IResult<&str, &str> {
|
||||
recognize(
|
||||
many1(
|
||||
terminated(one_of("0123456789"), many0(char('_')))
|
||||
)
|
||||
)(input)
|
||||
recognize(many1(terminated(one_of("0123456789"), many0(char('_')))))(input)
|
||||
}
|
||||
|
||||
// https://docs.rs/nom/latest/nom/recipes/index.html#binary
|
||||
fn binary(input: &str) -> IResult<&str, &str> {
|
||||
preceded(
|
||||
tag("0b"),
|
||||
recognize(
|
||||
many1(
|
||||
terminated(one_of("01"), many0(char('_')))
|
||||
)
|
||||
)
|
||||
recognize(many1(terminated(one_of("01"), many0(char('_'))))),
|
||||
)(input)
|
||||
}
|
||||
|
||||
// https://docs.rs/nom/latest/nom/recipes/index.html#floating-point-numbers
|
||||
fn float(input: &str) -> IResult<&str, &str> {
|
||||
alt((
|
||||
recognize( // .42
|
||||
recognize(
|
||||
// .42
|
||||
tuple((
|
||||
char('.'),
|
||||
decimal,
|
||||
opt(tuple((
|
||||
one_of("eE"),
|
||||
opt(one_of("+-")),
|
||||
decimal
|
||||
)))
|
||||
))
|
||||
opt(tuple((one_of("eE"), opt(one_of("+-")), decimal))),
|
||||
)),
|
||||
),
|
||||
recognize( // 42e42 and 42.42e42
|
||||
recognize(
|
||||
// 42e42 and 42.42e42
|
||||
tuple((
|
||||
decimal,
|
||||
opt(preceded(
|
||||
char('.'),
|
||||
decimal,
|
||||
)),
|
||||
opt(preceded(char('.'), decimal)),
|
||||
one_of("eE"),
|
||||
opt(one_of("+-")),
|
||||
decimal
|
||||
))
|
||||
),
|
||||
recognize( // 42. and 42.42
|
||||
tuple((
|
||||
decimal,
|
||||
char('.'),
|
||||
opt(decimal)
|
||||
))
|
||||
)
|
||||
)),
|
||||
),
|
||||
recognize(
|
||||
// 42. and 42.42
|
||||
tuple((decimal, char('.'), opt(decimal))),
|
||||
),
|
||||
))(input)
|
||||
}
|
||||
|
||||
|
@ -101,15 +84,22 @@ fn is_symbol_letter(c: char) -> bool {
|
|||
}
|
||||
|
||||
fn parse_str(input: &str) -> IResult<&str, Exp> {
|
||||
let escaped = map(opt(escaped_transform(is_not("\\\""), '\\', alt((
|
||||
value("\\", tag("\\")),
|
||||
value("\"", tag("\"")),
|
||||
value("\n", tag("n")),
|
||||
value("\r", tag("r")),
|
||||
value("\t", tag("t")),
|
||||
value("\x08", tag("b")),
|
||||
value("\x1B", tag("e")),
|
||||
)))), |inner| inner.unwrap_or("".to_string()));
|
||||
let escaped = map(
|
||||
opt(escaped_transform(
|
||||
is_not("\\\""),
|
||||
'\\',
|
||||
alt((
|
||||
value("\\", tag("\\")),
|
||||
value("\"", tag("\"")),
|
||||
value("\n", tag("n")),
|
||||
value("\r", tag("r")),
|
||||
value("\t", tag("t")),
|
||||
value("\x08", tag("b")),
|
||||
value("\x1B", tag("e")),
|
||||
)),
|
||||
)),
|
||||
|inner| inner.unwrap_or("".to_string()),
|
||||
);
|
||||
let (input, s) = delimited(char('"'), escaped, char('"'))(input)?;
|
||||
Ok((input, Exp::Str(s)))
|
||||
}
|
||||
|
@ -122,7 +112,7 @@ fn parse_sym(input: &str) -> IResult<&str, Exp> {
|
|||
fn parse_num(input: &str) -> IResult<&str, Exp> {
|
||||
let (input, num) = recognize(tuple((
|
||||
opt(alt((char('+'), char('-')))),
|
||||
alt((float, hexadecimal, binary, decimal))
|
||||
alt((float, hexadecimal, binary, decimal)),
|
||||
)))(input)?;
|
||||
Ok((input, Exp::Num(Number::from(num))))
|
||||
}
|
||||
|
@ -133,7 +123,9 @@ fn parse_bool(input: &str) -> IResult<&str, Exp> {
|
|||
}
|
||||
|
||||
fn parse_list(input: &str) -> IResult<&str, Exp> {
|
||||
let (input, list) = delimited(char('('), many0(parse_exp), char(')'))(input)?;
|
||||
let (input, list) = delimited(
|
||||
char('('), many0(parse_exp), char(')')
|
||||
)(input)?;
|
||||
Ok((input, Exp::List(list)))
|
||||
}
|
||||
|
||||
|
@ -173,17 +165,33 @@ fn parse_comment(input: &str) -> IResult<&str, &str> {
|
|||
|
||||
fn parse_exp(input: &str) -> IResult<&str, Exp> {
|
||||
let (input, _) = opt(many0(parse_comment))(input)?;
|
||||
delimited(multispace0, alt((
|
||||
parse_num, parse_bool, parse_str, parse_list, parse_quote, parse_quasiquote, parse_unquote_splice, parse_unquote, parse_splice, parse_sym
|
||||
)), alt((parse_comment, multispace0)))(input)
|
||||
delimited(
|
||||
multispace0,
|
||||
alt((
|
||||
parse_num,
|
||||
parse_bool,
|
||||
parse_str,
|
||||
parse_list,
|
||||
parse_quote,
|
||||
parse_quasiquote,
|
||||
parse_unquote_splice,
|
||||
parse_unquote,
|
||||
parse_splice,
|
||||
parse_sym,
|
||||
)),
|
||||
alt((parse_comment, multispace0)),
|
||||
)(input)
|
||||
}
|
||||
|
||||
pub fn parse(input: &str)-> Result<(String, Exp), Err> {
|
||||
pub fn parse(input: &str) -> Result<(String, Exp), Err> {
|
||||
match parse_exp(input) {
|
||||
Ok((input, exp)) => Ok((input.to_string(), exp)),
|
||||
Err(Error(err)) => {
|
||||
if err.input.is_empty() {
|
||||
Ok(("".to_string(), Exp::List(vec![Exp::Sym("quote".to_string()), Exp::List(vec![])])))
|
||||
Ok(("".to_string(), Exp::List(vec![
|
||||
Exp::Sym("quote".to_string()),
|
||||
Exp::List(vec![])
|
||||
])))
|
||||
} else {
|
||||
let line = err.input.lines().next().unwrap();
|
||||
could_not!("parse '{}'", line)
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
use super::parse::parse;
|
||||
use super::{Err, Exp, Number};
|
||||
use super::{float, number, string};
|
||||
use super::{bytes, numbers, strings};
|
||||
use super::{float, number, string};
|
||||
use super::{Err, Exp, Number};
|
||||
|
||||
use crate::{ensure_length_eq, ensure_length_gt, expected, could_not};
|
||||
use crate::api::regex::Regex;
|
||||
use crate::api::syscall;
|
||||
use crate::sys::fs::OpenFlag;
|
||||
use crate::usr::shell;
|
||||
use crate::usr::host;
|
||||
use crate::usr::shell;
|
||||
use crate::{could_not, ensure_length_eq, ensure_length_gt, expected};
|
||||
|
||||
use alloc::collections::btree_map::BTreeMap;
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp::Ordering::Equal;
|
||||
use core::convert::TryFrom;
|
||||
use core::convert::TryInto;
|
||||
|
@ -24,32 +24,46 @@ use num_bigint::BigInt;
|
|||
use smoltcp::wire::IpAddress;
|
||||
|
||||
pub fn lisp_eq(args: &[Exp]) -> Result<Exp, Err> {
|
||||
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] == nums[1])))
|
||||
Ok(Exp::Bool(
|
||||
numbers(args)?.windows(2).all(|nums| nums[0] == nums[1])
|
||||
))
|
||||
}
|
||||
|
||||
pub fn lisp_gt(args: &[Exp]) -> Result<Exp, Err> {
|
||||
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] > nums[1])))
|
||||
Ok(Exp::Bool(
|
||||
numbers(args)?.windows(2).all(|nums| nums[0] > nums[1])
|
||||
))
|
||||
}
|
||||
|
||||
pub fn lisp_gte(args: &[Exp]) -> Result<Exp, Err> {
|
||||
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] >= nums[1])))
|
||||
Ok(Exp::Bool(
|
||||
numbers(args)?.windows(2).all(|nums| nums[0] >= nums[1])
|
||||
))
|
||||
}
|
||||
|
||||
pub fn lisp_lt(args: &[Exp]) -> Result<Exp, Err> {
|
||||
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] < nums[1])))
|
||||
Ok(Exp::Bool(
|
||||
numbers(args)?.windows(2).all(|nums| nums[0] < nums[1])
|
||||
))
|
||||
}
|
||||
|
||||
pub fn lisp_lte(args: &[Exp]) -> Result<Exp, Err> {
|
||||
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] <= nums[1])))
|
||||
Ok(Exp::Bool(
|
||||
numbers(args)?.windows(2).all(|nums| nums[0] <= nums[1])
|
||||
))
|
||||
}
|
||||
|
||||
pub fn lisp_mul(args: &[Exp]) -> Result<Exp, Err> {
|
||||
let res = numbers(args)?.iter().fold(Number::Int(1), |acc, a| acc * a.clone());
|
||||
let res = numbers(args)?.iter().fold(Number::Int(1), |acc, a|
|
||||
acc * a.clone()
|
||||
);
|
||||
Ok(Exp::Num(res))
|
||||
}
|
||||
|
||||
pub fn lisp_add(args: &[Exp]) -> Result<Exp, Err> {
|
||||
let res = numbers(args)?.iter().fold(Number::Int(0), |acc, a| acc + a.clone());
|
||||
let res = numbers(args)?.iter().fold(Number::Int(0), |acc, a|
|
||||
acc + a.clone()
|
||||
);
|
||||
Ok(Exp::Num(res))
|
||||
}
|
||||
|
||||
|
@ -60,7 +74,9 @@ pub fn lisp_sub(args: &[Exp]) -> Result<Exp, Err> {
|
|||
if args.len() == 1 {
|
||||
Ok(Exp::Num(-head))
|
||||
} else {
|
||||
let res = args[1..].iter().fold(Number::Int(0), |acc, a| acc + a.clone());
|
||||
let res = args[1..].iter().fold(Number::Int(0), |acc, a|
|
||||
acc + a.clone()
|
||||
);
|
||||
Ok(Exp::Num(head - res))
|
||||
}
|
||||
}
|
||||
|
@ -180,7 +196,9 @@ pub fn lisp_string_binary(args: &[Exp]) -> Result<Exp, Err> {
|
|||
ensure_length_eq!(args, 1);
|
||||
let s = string(&args[0])?;
|
||||
let buf = s.as_bytes();
|
||||
Ok(Exp::List(buf.iter().map(|b| Exp::Num(Number::from(*b))).collect()))
|
||||
Ok(Exp::List(
|
||||
buf.iter().map(|b| Exp::Num(Number::from(*b))).collect()
|
||||
))
|
||||
}
|
||||
|
||||
pub fn lisp_binary_string(args: &[Exp]) -> Result<Exp, Err> {
|
||||
|
@ -188,33 +206,46 @@ pub fn lisp_binary_string(args: &[Exp]) -> Result<Exp, Err> {
|
|||
match &args[0] {
|
||||
Exp::List(list) => {
|
||||
let buf = bytes(list)?;
|
||||
let s = String::from_utf8(buf).or(expected!("a valid UTF-8 string"))?;
|
||||
let s = String::from_utf8(buf).
|
||||
or(expected!("a valid UTF-8 string"))?;
|
||||
Ok(Exp::Str(s))
|
||||
}
|
||||
_ => expected!("argument to be a list")
|
||||
_ => expected!("argument to be a list"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lisp_binary_number(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 2);
|
||||
match (&args[0], &args[1]) { // TODO: default type to "int" and make it optional
|
||||
match (&args[0], &args[1]) {
|
||||
// TODO: default type to "int" and make it optional
|
||||
(Exp::List(list), Exp::Str(kind)) => {
|
||||
let buf = bytes(list)?;
|
||||
ensure_length_eq!(buf, 8);
|
||||
match kind.as_str() { // TODO: bigint
|
||||
"int" => Ok(Exp::Num(Number::Int(i64::from_be_bytes(buf[0..8].try_into().unwrap())))),
|
||||
"float" => Ok(Exp::Num(Number::Float(f64::from_be_bytes(buf[0..8].try_into().unwrap())))),
|
||||
match kind.as_str() {
|
||||
// TODO: bigint
|
||||
"int" => Ok(Exp::Num(Number::Int(i64::from_be_bytes(
|
||||
buf[0..8].try_into().unwrap()
|
||||
)))),
|
||||
"float" => Ok(Exp::Num(Number::Float(f64::from_be_bytes(
|
||||
buf[0..8].try_into().unwrap()
|
||||
)))),
|
||||
_ => expected!("valid number type"),
|
||||
}
|
||||
}
|
||||
_ => expected!("arguments to be the type of number and a list of bytes")
|
||||
_ => {
|
||||
expected!("arguments to be the type of number and a list of bytes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lisp_number_binary(args: &[Exp]) -> Result<Exp, Err> {
|
||||
ensure_length_eq!(args, 1);
|
||||
let n = number(&args[0])?;
|
||||
Ok(Exp::List(n.to_be_bytes().iter().map(|b| Exp::Num(Number::from(*b))).collect()))
|
||||
Ok(Exp::List(
|
||||
n.to_be_bytes().iter().map(|b|
|
||||
Exp::Num(Number::from(*b))
|
||||
).collect()
|
||||
))
|
||||
}
|
||||
|
||||
pub fn lisp_number_string(args: &[Exp]) -> Result<Exp, Err> {
|
||||
|
@ -226,7 +257,7 @@ pub fn lisp_number_string(args: &[Exp]) -> Result<Exp, Err> {
|
|||
}
|
||||
r
|
||||
}
|
||||
_ => 10
|
||||
_ => 10,
|
||||
};
|
||||
let s = match number(&args[0])? {
|
||||
Number::Int(n) if args.len() == 2 => {
|
||||
|
@ -254,14 +285,14 @@ pub fn lisp_type(args: &[Exp]) -> Result<Exp, Err> {
|
|||
ensure_length_eq!(args, 1);
|
||||
let exp = match args[0] {
|
||||
Exp::Primitive(_) => "function",
|
||||
Exp::Function(_) => "function",
|
||||
Exp::Macro(_) => "macro",
|
||||
Exp::List(_) => "list",
|
||||
Exp::Dict(_) => "dict",
|
||||
Exp::Bool(_) => "boolean",
|
||||
Exp::Str(_) => "string",
|
||||
Exp::Sym(_) => "symbol",
|
||||
Exp::Num(_) => "number",
|
||||
Exp::Function(_) => "function",
|
||||
Exp::Macro(_) => "macro",
|
||||
Exp::List(_) => "list",
|
||||
Exp::Dict(_) => "dict",
|
||||
Exp::Bool(_) => "boolean",
|
||||
Exp::Str(_) => "string",
|
||||
Exp::Sym(_) => "symbol",
|
||||
Exp::Num(_) => "number",
|
||||
};
|
||||
Ok(Exp::Str(exp.to_string()))
|
||||
}
|
||||
|
@ -310,8 +341,14 @@ pub fn lisp_contains(args: &[Exp]) -> Result<Exp, Err> {
|
|||
|
||||
pub fn lisp_slice(args: &[Exp]) -> Result<Exp, Err> {
|
||||
let (a, b) = match args.len() {
|
||||
2 => (usize::try_from(number(&args[1])?)?, 1),
|
||||
3 => (usize::try_from(number(&args[1])?)?, usize::try_from(number(&args[2])?)?),
|
||||
2 => (
|
||||
usize::try_from(number(&args[1])?)?,
|
||||
1,
|
||||
),
|
||||
3 => (
|
||||
usize::try_from(number(&args[1])?)?,
|
||||
usize::try_from(number(&args[2])?)?,
|
||||
),
|
||||
_ => return expected!("2 or 3 arguments"),
|
||||
};
|
||||
match &args[0] {
|
||||
|
@ -323,7 +360,7 @@ pub fn lisp_slice(args: &[Exp]) -> Result<Exp, Err> {
|
|||
let s: String = s.chars().skip(a).take(b).collect();
|
||||
Ok(Exp::Str(s))
|
||||
}
|
||||
_ => expected!("first argument to be a list or a string")
|
||||
_ => expected!("first argument to be a list or a string"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,9 +369,11 @@ pub fn lisp_chunks(args: &[Exp]) -> Result<Exp, Err> {
|
|||
match (&args[0], &args[1]) {
|
||||
(Exp::List(list), Exp::Num(num)) => {
|
||||
let n = usize::try_from(num.clone())?;
|
||||
Ok(Exp::List(list.chunks(n).map(|a| Exp::List(a.to_vec())).collect()))
|
||||
Ok(Exp::List(
|
||||
list.chunks(n).map(|a| Exp::List(a.to_vec())).collect()
|
||||
))
|
||||
}
|
||||
_ => expected!("a list and a number")
|
||||
_ => expected!("a list and a number"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,7 +382,7 @@ pub fn lisp_length(args: &[Exp]) -> Result<Exp, Err> {
|
|||
match &args[0] {
|
||||
Exp::List(list) => Ok(Exp::Num(Number::from(list.len()))),
|
||||
Exp::Str(string) => Ok(Exp::Num(Number::from(string.chars().count()))),
|
||||
_ => expected!("a list or a string")
|
||||
_ => expected!("a list or a string"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,7 +393,7 @@ pub fn lisp_concat(args: &[Exp]) -> Result<Exp, Err> {
|
|||
if let Exp::List(list) = arg {
|
||||
res.extend_from_slice(list);
|
||||
} else {
|
||||
return expected!("a list")
|
||||
return expected!("a list");
|
||||
}
|
||||
}
|
||||
Ok(Exp::List(res))
|
||||
|
@ -368,7 +407,7 @@ pub fn lisp_number_type(args: &[Exp]) -> Result<Exp, Err> {
|
|||
Exp::Num(Number::Int(_)) => Ok(Exp::Str("int".to_string())),
|
||||
Exp::Num(Number::BigInt(_)) => Ok(Exp::Str("bigint".to_string())),
|
||||
Exp::Num(Number::Float(_)) => Ok(Exp::Str("float".to_string())),
|
||||
_ => expected!("argument to be a number")
|
||||
_ => expected!("argument to be a number"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -378,12 +417,12 @@ pub fn lisp_regex_find(args: &[Exp]) -> Result<Exp, Err> {
|
|||
ensure_length_eq!(args, 2);
|
||||
match (&args[0], &args[1]) {
|
||||
(Exp::Str(regex), Exp::Str(s)) => {
|
||||
let res = Regex::new(regex).find(s).map(|(a, b)| {
|
||||
let res = Regex::new(regex).find(s).map(|(a, b)|
|
||||
vec![Exp::Num(Number::from(a)), Exp::Num(Number::from(b))]
|
||||
}).unwrap_or(vec![]);
|
||||
).unwrap_or(vec![]);
|
||||
Ok(Exp::List(res))
|
||||
}
|
||||
_ => expected!("arguments to be a regex and a string")
|
||||
_ => expected!("arguments to be a regex and a string"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,13 +434,17 @@ pub fn lisp_string_split(args: &[Exp]) -> Result<Exp, Err> {
|
|||
(Exp::Str(string), Exp::Str(pattern)) => {
|
||||
let list = if pattern.is_empty() {
|
||||
// NOTE: "abc".split("") => ["", "b", "c", ""]
|
||||
string.chars().map(|s| Exp::Str(s.to_string())).collect()
|
||||
string.chars().map(|s|
|
||||
Exp::Str(s.to_string())
|
||||
).collect()
|
||||
} else {
|
||||
string.split(pattern).map(|s| Exp::Str(s.to_string())).collect()
|
||||
string.split(pattern).map(|s|
|
||||
Exp::Str(s.to_string())
|
||||
).collect()
|
||||
};
|
||||
Ok(Exp::List(list))
|
||||
}
|
||||
_ => expected!("a string and a pattern")
|
||||
_ => expected!("a string and a pattern"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -434,14 +477,14 @@ pub fn lisp_file_open(args: &[Exp]) -> Result<Exp, Err> {
|
|||
"a" => OpenFlag::Append as usize,
|
||||
"r" => OpenFlag::Read as usize,
|
||||
"w" => OpenFlag::Write as usize,
|
||||
_ => return expected!("valid mode"),
|
||||
_ => return expected!("valid mode"),
|
||||
};
|
||||
flags |= match syscall::info(&path) {
|
||||
Some(info) if info.is_device() => OpenFlag::Device as usize,
|
||||
Some(info) if info.is_dir() => OpenFlag::Dir as usize,
|
||||
None if &mode == "r" => return could_not!("open file"),
|
||||
None => OpenFlag::Create as usize,
|
||||
_ => 0
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
match syscall::open(&path, flags) {
|
||||
|
@ -466,7 +509,9 @@ pub fn lisp_file_read(args: &[Exp]) -> Result<Exp, Err> {
|
|||
match syscall::read(handle, &mut buf) {
|
||||
Some(n) => {
|
||||
buf.resize(n, 0);
|
||||
Ok(Exp::List(buf.iter().map(|b| Exp::Num(Number::from(*b))).collect()))
|
||||
Ok(Exp::List(
|
||||
buf.iter().map(|b| Exp::Num(Number::from(*b))).collect()
|
||||
))
|
||||
}
|
||||
None => could_not!("read file"),
|
||||
}
|
||||
|
@ -484,7 +529,7 @@ pub fn lisp_file_write(args: &[Exp]) -> Result<Exp, Err> {
|
|||
None => could_not!("write file"),
|
||||
}
|
||||
}
|
||||
_ => expected!("second argument to be a list")
|
||||
_ => expected!("second argument to be a list"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -547,9 +592,9 @@ pub fn lisp_get(args: &[Exp]) -> Result<Exp, Err> {
|
|||
Exp::Dict(dict) => {
|
||||
let key = format!("{}", args[1]);
|
||||
if let Some(val) = dict.get(&key) {
|
||||
Ok(val.clone())
|
||||
Ok(val.clone())
|
||||
} else {
|
||||
Ok(Exp::List(vec![]))
|
||||
Ok(Exp::List(vec![]))
|
||||
}
|
||||
}
|
||||
Exp::List(l) => {
|
||||
|
@ -568,7 +613,7 @@ pub fn lisp_get(args: &[Exp]) -> Result<Exp, Err> {
|
|||
Ok(Exp::Str("".to_string()))
|
||||
}
|
||||
}
|
||||
_ => expected!("first argument to be a dict, a list, or a string")
|
||||
_ => expected!("first argument to be a dict, a list, or a string"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -597,7 +642,7 @@ pub fn lisp_put(args: &[Exp]) -> Result<Exp, Err> {
|
|||
let s: String = s.into_iter().collect();
|
||||
Ok(Exp::Str(s))
|
||||
}
|
||||
_ => expected!("first argument to be a dict, a list, or a string")
|
||||
_ => expected!("first argument to be a dict, a list, or a string"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -605,11 +650,7 @@ pub fn lisp_host(args: &[Exp]) -> Result<Exp, Err> {
|
|||
ensure_length_eq!(args, 1);
|
||||
let hostname = string(&args[0])?;
|
||||
match host::resolve(&hostname) {
|
||||
Ok(addr) => {
|
||||
Ok(Exp::Str(format!("{}", addr)))
|
||||
}
|
||||
Err(_) => {
|
||||
Ok(Exp::List(vec![]))
|
||||
}
|
||||
Ok(addr) => Ok(Exp::Str(format!("{}", addr))),
|
||||
Err(_) => Ok(Exp::List(vec![])),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use crate::sys;
|
||||
use crate::api::clock::DATE_TIME;
|
||||
use crate::api::console::Style;
|
||||
use crate::api::time;
|
||||
use crate::api::fs;
|
||||
use crate::api::fs::FileInfo;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::syscall;
|
||||
use crate::api::time;
|
||||
use crate::api::unit::SizeUnit;
|
||||
use crate::sys;
|
||||
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
|
@ -21,7 +21,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
for i in 1..n {
|
||||
match args[i] {
|
||||
"-h" | "--help" => return help(),
|
||||
"-a" | "--all" => hide_dot_files = false,
|
||||
"-a" | "--all" => hide_dot_files = false,
|
||||
"-n" | "--name" => sort = "name",
|
||||
"-s" | "--size" => sort = "size",
|
||||
"-t" | "--time" => sort = "time",
|
||||
|
@ -40,9 +40,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
if let Some(info) = syscall::info(path) {
|
||||
if info.is_dir() {
|
||||
if let Ok(entries) = fs::read_dir(path) {
|
||||
let mut files: Vec<_> = entries.iter().filter(|entry| {
|
||||
let mut files: Vec<_> = entries.iter().filter(|entry|
|
||||
!(entry.name().starts_with('.') && hide_dot_files)
|
||||
}).collect();
|
||||
).collect();
|
||||
|
||||
match sort {
|
||||
"name" => files.sort_by_key(|f| f.name()),
|
||||
|
@ -91,19 +91,42 @@ fn print_file(file: &FileInfo, width: usize, unit: SizeUnit) {
|
|||
} else {
|
||||
csi_reset
|
||||
};
|
||||
println!("{:>width$} {} {}{}{}", size, time, color, file.name(), csi_reset, width = width);
|
||||
println!(
|
||||
"{:>width$} {} {}{}{}",
|
||||
size,
|
||||
time,
|
||||
color,
|
||||
file.name(),
|
||||
csi_reset,
|
||||
width = width
|
||||
);
|
||||
}
|
||||
|
||||
fn help() -> Result<(), ExitCode> {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} list {}<options> [<dir>]{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} list {}<options> [<dir>]{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
println!();
|
||||
println!("{}Options:{}", csi_title, csi_reset);
|
||||
println!(" {0}-a{1}, {0}--all{1} Show dot files", csi_option, csi_reset);
|
||||
println!(" {0}-n{1}, {0}--name{1} Sort by name", csi_option, csi_reset);
|
||||
println!(" {0}-s{1}, {0}--size{1} Sort by size", csi_option, csi_reset);
|
||||
println!(" {0}-t{1}, {0}--time{1} Sort by time", csi_option, csi_reset);
|
||||
println!(
|
||||
" {0}-a{1}, {0}--all{1} Show dot files",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
println!(
|
||||
" {0}-n{1}, {0}--name{1} Sort by name",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
println!(
|
||||
" {0}-s{1}, {0}--size{1} Sort by size",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
println!(
|
||||
" {0}-t{1}, {0}--time{1} Sort by time",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
use crate::sys;
|
||||
use crate::api::console::Style;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::unit::SizeUnit;
|
||||
use crate::sys;
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
match *args.get(1).unwrap_or(&"") {
|
||||
"u" | "usage" => {
|
||||
usage(&args[2..])
|
||||
}
|
||||
"u" | "usage" => usage(&args[2..]),
|
||||
"f" | "format" => {
|
||||
sys::fs::mount_mem();
|
||||
sys::fs::format_mem();
|
||||
|
@ -49,14 +47,32 @@ fn usage(args: &[&str]) -> Result<(), ExitCode> {
|
|||
let size = sys::allocator::memory_size();
|
||||
let used = sys::allocator::memory_used();
|
||||
let free = size - used;
|
||||
let width = [size, used, free].iter().fold(0, |acc, num| {
|
||||
let width = [size, used, free].iter().fold(0, |acc, num|
|
||||
core::cmp::max(acc, unit.format(*num).len())
|
||||
});
|
||||
);
|
||||
let color = Style::color("LightCyan");
|
||||
let reset = Style::reset();
|
||||
println!("{}size:{} {:>width$}", color, reset, unit.format(size), width = width);
|
||||
println!("{}used:{} {:>width$}", color, reset, unit.format(used), width = width);
|
||||
println!("{}free:{} {:>width$}", color, reset, unit.format(free), width = width);
|
||||
println!(
|
||||
"{}size:{} {:>width$}",
|
||||
color,
|
||||
reset,
|
||||
unit.format(size),
|
||||
width = width
|
||||
);
|
||||
println!(
|
||||
"{}used:{} {:>width$}",
|
||||
color,
|
||||
reset,
|
||||
unit.format(used),
|
||||
width = width
|
||||
);
|
||||
println!(
|
||||
"{}free:{} {:>width$}",
|
||||
color,
|
||||
reset,
|
||||
unit.format(free),
|
||||
width = width
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -64,17 +80,26 @@ fn help_usage() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} memory usage {}<options>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} memory usage {}<options>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
println!();
|
||||
println!("{}Options:{}", csi_title, csi_reset);
|
||||
println!(" {0}-b{1}, {0}--binary-size{1} Use binary size", csi_option, csi_reset);
|
||||
println!(
|
||||
" {0}-b{1}, {0}--binary-size{1} Use binary size",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
||||
fn help() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} memory {}<command>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} memory {}<command>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
println!();
|
||||
println!("{}Commands:{}", csi_title, csi_reset);
|
||||
println!(" {}usage{} List memory usage", csi_option, csi_reset);
|
||||
|
|
|
@ -23,11 +23,11 @@ pub mod life;
|
|||
pub mod lisp;
|
||||
pub mod list;
|
||||
pub mod memory;
|
||||
pub mod r#move;
|
||||
pub mod net;
|
||||
pub mod pci;
|
||||
pub mod pi;
|
||||
pub mod pow;
|
||||
pub mod r#move;
|
||||
pub mod read;
|
||||
pub mod shell;
|
||||
pub mod socket;
|
||||
|
|
|
@ -14,7 +14,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
help();
|
||||
return Ok(());
|
||||
}
|
||||
_ => continue
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,5 +29,8 @@ fn help() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} move {}<src> <dst>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} move {}<src> <dst>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
|
250
src/usr/net.rs
250
src/usr/net.rs
|
@ -1,22 +1,24 @@
|
|||
use crate::sys;
|
||||
use alloc::format;
|
||||
use crate::api::clock;
|
||||
use crate::api::console::Style;
|
||||
use crate::api::fs;
|
||||
use crate::api::syscall;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::syscall;
|
||||
use crate::sys;
|
||||
use crate::sys::console;
|
||||
use crate::sys::net;
|
||||
use crate::sys::net::EthernetDeviceIO;
|
||||
use alloc::format;
|
||||
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::string::ToString;
|
||||
use alloc::string::String;
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec;
|
||||
use core::str::FromStr;
|
||||
use smoltcp::wire::{EthernetFrame, PrettyPrinter, IpCidr, Ipv4Address};
|
||||
use smoltcp::iface::SocketSet;
|
||||
use smoltcp::phy::Device;
|
||||
use smoltcp::socket::tcp;
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::phy::Device;
|
||||
use smoltcp::iface::SocketSet;
|
||||
use smoltcp::wire::{EthernetFrame, IpCidr, Ipv4Address, PrettyPrinter};
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
match *args.get(1).unwrap_or(&"") {
|
||||
|
@ -40,49 +42,10 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
}
|
||||
"s" | "stat" => {
|
||||
if let Some((_, ref mut device)) = *sys::net::NET.lock() {
|
||||
let stats = device.stats();
|
||||
let csi_color = Style::color("LightCyan");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}rx:{} {} packets ({} bytes)", csi_color, csi_reset, stats.rx_packets_count(), stats.rx_bytes_count());
|
||||
println!("{}tx:{} {} packets ({} bytes)", csi_color, csi_reset, stats.tx_packets_count(), stats.tx_bytes_count());
|
||||
} else {
|
||||
error!("Network error");
|
||||
}
|
||||
stat();
|
||||
}
|
||||
"m" | "monitor" => {
|
||||
if let Some((ref mut iface, ref mut device)) = *sys::net::NET.lock() {
|
||||
device.config().enable_debug();
|
||||
|
||||
let mtu = device.capabilities().max_transmission_unit;
|
||||
let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; mtu]);
|
||||
let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; mtu]);
|
||||
let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer);
|
||||
let mut sockets = SocketSet::new(vec![]);
|
||||
let tcp_handle = sockets.add(tcp_socket);
|
||||
|
||||
loop {
|
||||
if sys::console::end_of_text() || sys::console::end_of_transmission() {
|
||||
println!();
|
||||
return Ok(());
|
||||
}
|
||||
syscall::sleep(0.1);
|
||||
|
||||
let time = Instant::from_micros((clock::realtime() * 1000000.0) as i64);
|
||||
iface.poll(time, device, &mut sockets);
|
||||
let socket = sockets.get_mut::<tcp::Socket>(tcp_handle);
|
||||
if socket.may_recv() {
|
||||
socket.recv(|buffer| {
|
||||
let recvd_len = buffer.len();
|
||||
let data = buffer.to_owned();
|
||||
debug!("{}", PrettyPrinter::<EthernetFrame<&[u8]>>::new("", &buffer));
|
||||
(recvd_len, data)
|
||||
}).unwrap();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("Network error");
|
||||
}
|
||||
monitor();
|
||||
}
|
||||
_ => {
|
||||
error!("Invalid command");
|
||||
|
@ -96,19 +59,28 @@ fn help() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} net {}<command>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} net {}<command>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
println!();
|
||||
println!("{}Commands:{}", csi_title, csi_reset);
|
||||
println!(" {}config{} Configure network", csi_option, csi_reset);
|
||||
println!(" {}monitor{} Monitor network", csi_option, csi_reset);
|
||||
println!(" {}stat{} Display network status", csi_option, csi_reset);
|
||||
println!(
|
||||
" {}stat{} Display network status",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
}
|
||||
|
||||
fn help_config() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} net config {}<attribute> <value>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} net config {}<attribute> <value>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
println!();
|
||||
println!("{}Attributes:{}", csi_title, csi_reset);
|
||||
println!(" {}mac{} MAC Address", csi_option, csi_reset);
|
||||
|
@ -122,59 +94,77 @@ fn print_config(attribute: &str) {
|
|||
let csi_reset = Style::reset();
|
||||
if let Some(value) = get_config(attribute) {
|
||||
let width = 4 - attribute.len();
|
||||
println!("{}{}:{}{:width$}{}", csi_color, attribute, csi_reset, "", value, width = width);
|
||||
println!(
|
||||
"{}{}:{}{:width$}{}",
|
||||
csi_color,
|
||||
attribute,
|
||||
csi_reset,
|
||||
"",
|
||||
value,
|
||||
width = width
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const DNS_FILE: &str = "/ini/dns";
|
||||
|
||||
fn dns_config() -> Option<String> {
|
||||
if let Ok(value) = fs::read_to_string(DNS_FILE) {
|
||||
let servers = value.trim();
|
||||
if servers.split(',').all(|s| Ipv4Address::from_str(s).is_ok()) {
|
||||
Some(servers.to_string())
|
||||
} else {
|
||||
error!("Could not parse '{}'", servers);
|
||||
None
|
||||
}
|
||||
} else {
|
||||
error!("Could not read '{}'", DNS_FILE);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn gw_config() -> Option<String> {
|
||||
let mut res = None;
|
||||
if let Some((ref mut iface, _)) = *sys::net::NET.lock() {
|
||||
iface.routes_mut().update(|storage| {
|
||||
if let Some(route) = storage.iter().next() {
|
||||
res = Some(route.via_router.to_string());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
error!("Network error");
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn ip_config() -> Option<String> {
|
||||
if let Some((ref mut iface, _)) = *sys::net::NET.lock() {
|
||||
if let Some(ip_cidr) = iface.ip_addrs().iter().next() {
|
||||
return Some(format!(
|
||||
"{}/{}", ip_cidr.address(), ip_cidr.prefix_len()
|
||||
));
|
||||
}
|
||||
} else {
|
||||
error!("Network error");
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn mac_config() -> Option<String> {
|
||||
if let Some((ref mut iface, _)) = *sys::net::NET.lock() {
|
||||
return Some(iface.hardware_addr().to_string());
|
||||
} else {
|
||||
error!("Network error");
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_config(attribute: &str) -> Option<String> {
|
||||
match attribute {
|
||||
"mac" => {
|
||||
if let Some((ref mut iface, _)) = *sys::net::NET.lock() {
|
||||
return Some(iface.hardware_addr().to_string());
|
||||
} else {
|
||||
error!("Network error");
|
||||
}
|
||||
None
|
||||
}
|
||||
"ip" => {
|
||||
if let Some((ref mut iface, _)) = *sys::net::NET.lock() {
|
||||
if let Some(ip_cidr) = iface.ip_addrs().iter().next() {
|
||||
return Some(format!("{}/{}", ip_cidr.address(), ip_cidr.prefix_len()));
|
||||
}
|
||||
} else {
|
||||
error!("Network error");
|
||||
}
|
||||
None
|
||||
}
|
||||
"gw" => {
|
||||
let mut res = None;
|
||||
if let Some((ref mut iface, _)) = *sys::net::NET.lock() {
|
||||
iface.routes_mut().update(|storage| {
|
||||
if let Some(route) = storage.iter().next() {
|
||||
res = Some(route.via_router.to_string());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
error!("Network error");
|
||||
}
|
||||
res
|
||||
}
|
||||
"dns" => {
|
||||
if let Ok(value) = fs::read_to_string(DNS_FILE) {
|
||||
let servers = value.trim();
|
||||
if servers.split(',').all(|s| Ipv4Address::from_str(s).is_ok()) {
|
||||
Some(servers.to_string())
|
||||
} else {
|
||||
error!("Could not parse '{}'", servers);
|
||||
None
|
||||
}
|
||||
} else {
|
||||
error!("Could not read '{}'", DNS_FILE);
|
||||
None
|
||||
}
|
||||
}
|
||||
"dns" => dns_config(),
|
||||
"gw" => gw_config(),
|
||||
"ip" => ip_config(),
|
||||
"mac" => mac_config(),
|
||||
_ => {
|
||||
error!("Invalid config attribute");
|
||||
None
|
||||
|
@ -225,7 +215,8 @@ pub fn set_config(attribute: &str, value: &str) {
|
|||
"dns" => {
|
||||
let servers = value.trim();
|
||||
if servers.split(',').all(|s| Ipv4Address::from_str(s).is_ok()) {
|
||||
if fs::write(DNS_FILE, format!("{}\n", servers).as_bytes()).is_err() {
|
||||
let s = format!("{}\n", servers);
|
||||
if fs::write(DNS_FILE, s.as_bytes()).is_err() {
|
||||
error!("Could not write to '{}'", DNS_FILE);
|
||||
}
|
||||
} else {
|
||||
|
@ -237,3 +228,66 @@ pub fn set_config(attribute: &str, value: &str) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stat() {
|
||||
if let Some((_, ref mut device)) = *sys::net::NET.lock() {
|
||||
let stats = device.stats();
|
||||
let csi_color = Style::color("LightCyan");
|
||||
let csi_reset = Style::reset();
|
||||
println!(
|
||||
"{}rx:{} {} packets ({} bytes)",
|
||||
csi_color,
|
||||
csi_reset,
|
||||
stats.rx_packets_count(),
|
||||
stats.rx_bytes_count()
|
||||
);
|
||||
println!(
|
||||
"{}tx:{} {} packets ({} bytes)",
|
||||
csi_color,
|
||||
csi_reset,
|
||||
stats.tx_packets_count(),
|
||||
stats.tx_bytes_count()
|
||||
);
|
||||
} else {
|
||||
error!("Network error");
|
||||
}
|
||||
}
|
||||
|
||||
fn monitor() {
|
||||
if let Some((ref mut iface, ref mut device)) = *net::NET.lock() {
|
||||
device.config().enable_debug();
|
||||
|
||||
let mtu = device.capabilities().max_transmission_unit;
|
||||
let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; mtu]);
|
||||
let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; mtu]);
|
||||
let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer);
|
||||
let mut sockets = SocketSet::new(vec![]);
|
||||
let tcp_handle = sockets.add(tcp_socket);
|
||||
|
||||
loop {
|
||||
if console::end_of_text() || console::end_of_transmission() {
|
||||
println!();
|
||||
return;
|
||||
}
|
||||
syscall::sleep(0.1);
|
||||
|
||||
let ms = (clock::realtime() * 1000000.0) as i64;
|
||||
let time = Instant::from_micros(ms);
|
||||
iface.poll(time, device, &mut sockets);
|
||||
let socket = sockets.get_mut::<tcp::Socket>(tcp_handle);
|
||||
if socket.may_recv() {
|
||||
socket.recv(|buffer| {
|
||||
let recvd_len = buffer.len();
|
||||
let data = buffer.to_owned();
|
||||
let pp = PrettyPrinter::<EthernetFrame<&[u8]>>::new(
|
||||
"", &buffer
|
||||
);
|
||||
debug!("{}", pp);
|
||||
(recvd_len, data)
|
||||
}).unwrap();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("Network error");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,10 +11,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
"list" => {
|
||||
let verbose = args.contains(&"-v") || args.contains(&"--verbose");
|
||||
list(verbose)
|
||||
},
|
||||
_ => {
|
||||
help()
|
||||
}
|
||||
_ => help(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,17 +21,38 @@ fn list(verbose: bool) -> Result<(), ExitCode> {
|
|||
let color2 = Style::color("LightBlue");
|
||||
let reset = Style::reset();
|
||||
if verbose {
|
||||
println!("{}+-------------------->{} bus num{}", color1, color2, reset);
|
||||
println!("{}| +--------------->{} device num{}", color1, color2, reset);
|
||||
println!("{}| | +------------>{} function num{}", color1, color2, reset);
|
||||
println!("{}| | | +-------->{} vendor id{}", color1, color2, reset);
|
||||
println!("{}| | | | +--->{} device id{}", color1, color2, reset);
|
||||
println!(
|
||||
"{}+-------------------->{} bus num{}",
|
||||
color1, color2, reset
|
||||
);
|
||||
println!(
|
||||
"{}| +--------------->{} device num{}",
|
||||
color1, color2, reset
|
||||
);
|
||||
println!(
|
||||
"{}| | +------------>{} function num{}",
|
||||
color1, color2, reset
|
||||
);
|
||||
println!(
|
||||
"{}| | | +-------->{} vendor id{}",
|
||||
color1, color2, reset
|
||||
);
|
||||
println!(
|
||||
"{}| | | | +--->{} device id{}",
|
||||
color1, color2, reset
|
||||
);
|
||||
println!("{}| | | | |{}", color1, reset);
|
||||
}
|
||||
for d in sys::pci::list() {
|
||||
print!("{:04X}:{:02X}:{:02X} [{:04X}:{:04X}]", d.bus, d.device, d.function, d.vendor_id, d.device_id);
|
||||
print!(
|
||||
"{:04X}:{:02X}:{:02X} [{:04X}:{:04X}]",
|
||||
d.bus, d.device, d.function, d.vendor_id, d.device_id
|
||||
);
|
||||
if verbose {
|
||||
println!(" {}rev={:#04X} class={:#04X},{:#04X} prog={:#04X}{}", color2, d.rev, d.class, d.subclass, d.prog, reset);
|
||||
println!(
|
||||
" {}rev={:#04X} class={:#04X},{:#04X} prog={:#04X}{}",
|
||||
color2, d.rev, d.class, d.subclass, d.prog, reset
|
||||
);
|
||||
} else {
|
||||
println!();
|
||||
}
|
||||
|
@ -45,12 +64,21 @@ fn help() -> Result<(), ExitCode> {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} pci {}<command> <options>{1}", csi_title, csi_reset, csi_option);
|
||||
println!(
|
||||
"{}Usage:{} pci {}<command> <options>{1}",
|
||||
csi_title, csi_reset, csi_option
|
||||
);
|
||||
println!();
|
||||
println!("{}Commands:{}", csi_title, csi_reset);
|
||||
println!(" {}list{} List PCI devices", csi_option, csi_reset);
|
||||
println!(
|
||||
" {}list{} List PCI devices",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
println!();
|
||||
println!("{}Options:{}", csi_title, csi_reset);
|
||||
println!(" {0}-v{1}, {0}--verbose{1} Increase verbosity", csi_option, csi_reset);
|
||||
println!(
|
||||
" {0}-v{1}, {0}--verbose{1} Increase verbosity",
|
||||
csi_option, csi_reset
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{usr, sys};
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::{sys, usr};
|
||||
|
||||
use alloc::format;
|
||||
use num_bigint::BigInt;
|
||||
|
@ -33,7 +33,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
match digits {
|
||||
Some(0) => break,
|
||||
Some(i) => digits = Some(i - 1),
|
||||
None => {},
|
||||
None => {}
|
||||
}
|
||||
|
||||
let nr = (&r - &n * &t) * 10;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::api::console::Style;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::{io, random, console};
|
||||
use crate::api::{console, io, random};
|
||||
|
||||
use core::fmt;
|
||||
use alloc::format;
|
||||
use alloc::string::ToString;
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt;
|
||||
use vte::{Params, Parser, Perform};
|
||||
|
||||
struct Game {
|
||||
|
@ -37,7 +37,7 @@ impl Game {
|
|||
match c {
|
||||
'q' | console::ETX_KEY | console::EOT_KEY => {
|
||||
return;
|
||||
},
|
||||
}
|
||||
c => {
|
||||
for b in c.to_string().as_bytes() {
|
||||
parser.advance(self, *b);
|
||||
|
@ -131,18 +131,18 @@ impl fmt::Display for Game {
|
|||
write!(f, " |")?;
|
||||
} else {
|
||||
let color = match v {
|
||||
2 => Style::color("LightGray"),
|
||||
4 => Style::color("LightBlue"),
|
||||
8 => Style::color("LightCyan"),
|
||||
16 => Style::color("LightGreen"),
|
||||
32 => Style::color("Yellow"),
|
||||
64 => Style::color("LightRed"),
|
||||
128 => Style::color("Pink"),
|
||||
256 => Style::color("Magenta"),
|
||||
512 => Style::color("Pink"),
|
||||
2 => Style::color("LightGray"),
|
||||
4 => Style::color("LightBlue"),
|
||||
8 => Style::color("LightCyan"),
|
||||
16 => Style::color("LightGreen"),
|
||||
32 => Style::color("Yellow"),
|
||||
64 => Style::color("LightRed"),
|
||||
128 => Style::color("Pink"),
|
||||
256 => Style::color("Magenta"),
|
||||
512 => Style::color("Pink"),
|
||||
1024 => Style::color("Red"),
|
||||
2048 => Style::color("Brown"),
|
||||
_ => Style::color("White"),
|
||||
_ => Style::color("White"),
|
||||
};
|
||||
write!(f, " {}{:^5}{}|", color, v, reset)?;
|
||||
}
|
||||
|
@ -154,13 +154,13 @@ impl fmt::Display for Game {
|
|||
}
|
||||
|
||||
impl Perform for Game {
|
||||
fn csi_dispatch(&mut self, _params: &Params, _intermediates: &[u8], _ignore: bool, c: char) {
|
||||
fn csi_dispatch(&mut self, _: &Params, _: &[u8], _: bool, c: char) {
|
||||
match c {
|
||||
'A' => self.handle_up_key(),
|
||||
'B' => self.handle_down_key(),
|
||||
'C' => self.handle_forward_key(),
|
||||
'D' => self.handle_backward_key(),
|
||||
_ => {},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::{api, sys, usr};
|
||||
use crate::api::console;
|
||||
use crate::api::console::Style;
|
||||
use crate::api::fs;
|
||||
use crate::api::syscall;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::syscall;
|
||||
use crate::sys::console;
|
||||
use crate::{api, usr};
|
||||
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::format;
|
||||
|
@ -39,7 +39,10 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
// > read /net/tcp/time.nist.gov:13
|
||||
let parts: Vec<_> = path.split('/').collect();
|
||||
if parts.len() < 4 {
|
||||
println!("{}Usage:{} read {}/net/<proto>/<host>[:<port>]/<path>{1}", csi_title, csi_reset, csi_option);
|
||||
println!(
|
||||
"{}Usage:{} read {}/net/<proto>/<host>[:<port>]/<path>{1}",
|
||||
csi_title, csi_reset, csi_option
|
||||
);
|
||||
Err(ExitCode::Failure)
|
||||
} else {
|
||||
let host = parts[3];
|
||||
|
@ -87,18 +90,18 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
let is_float_device = info.size() == 8;
|
||||
let is_eof_device = info.size() > 8;
|
||||
loop {
|
||||
if sys::console::end_of_text() || sys::console::end_of_transmission() {
|
||||
if console::end_of_text() || console::end_of_transmission() {
|
||||
println!();
|
||||
return Ok(());
|
||||
}
|
||||
if let Ok(bytes) = fs::read_to_bytes(path) {
|
||||
if is_char_device && bytes.len() == 1 {
|
||||
match bytes[0] as char {
|
||||
console::ETX_KEY => {
|
||||
api::console::ETX_KEY => {
|
||||
println!("^C");
|
||||
return Ok(());
|
||||
}
|
||||
console::EOT_KEY => {
|
||||
api::console::EOT_KEY => {
|
||||
println!("^D");
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -106,7 +109,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
}
|
||||
if is_float_device && bytes.len() == 8 {
|
||||
println!("{:.6}", f64::from_be_bytes(bytes[0..8].try_into().unwrap()));
|
||||
let f = f64::from_be_bytes(bytes[0..8].try_into().
|
||||
unwrap());
|
||||
println!("{:.6}", f);
|
||||
return Ok(());
|
||||
}
|
||||
for b in bytes {
|
||||
|
@ -135,7 +140,10 @@ fn help() {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} read {}<path>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!(
|
||||
"{}Usage:{} read {}<path>{}",
|
||||
csi_title, csi_reset, csi_option, csi_reset
|
||||
);
|
||||
println!();
|
||||
println!("{}Paths:{}", csi_title, csi_reset);
|
||||
println!(" {0}<dir>/{1} Read directory", csi_option, csi_reset);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue