#![no_std] #![no_main] use lancer_user::fs::{self, FsClient, READDIR_ENTRY_SIZE, ReadDirEntry}; use lancer_user::net; use lancer_user::syscall; use lancer_user::{print, println}; struct SortEntry { name: [u8; 48], name_len: u16, inode_type: u8, size: u64, } const EMPTY_ENTRY: SortEntry = SortEntry { name: [0; 48], name_len: 0, inode_type: 0, size: 0, }; fn type_char(t: u8) -> u8 { match t { 1 => b'd', 2 => b'l', _ => b'f', } } fn format_size(size: u64, buf: &mut [u8; 20]) -> usize { match size { 0 => { buf[0] = b'0'; 1 } _ => { let mut tmp = [0u8; 20]; fn fill(v: u64, tmp: &mut [u8; 20], pos: usize) -> usize { match v { 0 => pos, _ => { tmp[pos] = b'0' + (v % 10) as u8; fill(v / 10, tmp, pos + 1) } } } let digits = fill(size, &mut tmp, 0); (0..digits).for_each(|j| { buf[j] = tmp[digits - 1 - j]; }); digits } } } fn insertion_sort(entries: &mut [SortEntry], count: usize) { fn sink(entries: &mut [SortEntry], j: usize) { if j > 0 && name_cmp(&entries[j], &entries[j - 1]) == core::cmp::Ordering::Less { entries.swap(j, j - 1); sink(entries, j - 1); } } (1..count).for_each(|i| sink(entries, i)); } fn name_cmp(a: &SortEntry, b: &SortEntry) -> core::cmp::Ordering { let a_name = &a.name[..a.name_len as usize]; let b_name = &b.name[..b.name_len as usize]; let min_len = a_name.len().min(b_name.len()); (0..min_len) .find_map(|i| match a_name[i].cmp(&b_name[i]) { core::cmp::Ordering::Equal => None, ord => Some(ord), }) .unwrap_or_else(|| a_name.len().cmp(&b_name.len())) } const MAX_DISPLAY_ENTRIES: usize = 128; struct CollectState { total: usize, overflow: bool, } fn collect_entries( client: &mut FsClient, handle: u8, buf: &mut [u8], entries: &mut [SortEntry; MAX_DISPLAY_ENTRIES], state: &mut CollectState, cursor: u64, ) { match client.readdir(handle, cursor, buf) { Err(e) => { println!("readdir error: {}", e.name()); } Ok((0, _)) => {} Ok((count, next)) => { (0..count).for_each(|i| match state.total < MAX_DISPLAY_ENTRIES { true => { let off = i * READDIR_ENTRY_SIZE; if let Some(entry) = ReadDirEntry::from_bytes(&buf[off..]) { let nlen = (entry.name_len as usize).min(48); entries[state.total].name[..nlen].copy_from_slice(&entry.name[..nlen]); entries[state.total].name_len = nlen as u16; entries[state.total].inode_type = entry.inode_type; entries[state.total].size = entry.size; state.total += 1; } } false => { state.overflow = true; } }); collect_entries(client, handle, buf, entries, state, next) } } } fn list_dir(client: &mut FsClient, handle: u8) { let mut entries = [EMPTY_ENTRY; MAX_DISPLAY_ENTRIES]; let mut state = CollectState { total: 0, overflow: false, }; let mut buf = [0u8; READDIR_ENTRY_SIZE * 16]; collect_entries(client, handle, &mut buf, &mut entries, &mut state, 0); insertion_sort(&mut entries, state.total); (0..state.total).for_each(|i| { let e = &entries[i]; let tc = type_char(e.inode_type); print!("{}", tc as char); print!(" "); if let Ok(name) = core::str::from_utf8(&e.name[..e.name_len as usize]) { print!("{}", name); } let mut sbuf = [0u8; 20]; let slen = format_size(e.size, &mut sbuf); print!(" "); if let Ok(s) = core::str::from_utf8(&sbuf[..slen]) { print!("{}", s); } println!(); }); if state.overflow { println!("(listing truncated at {} entries)", MAX_DISPLAY_ENTRIES); } } #[unsafe(no_mangle)] pub extern "C" fn lancer_main() -> ! { let (rx, _tx) = match net::init() { Some(pair) => pair, None => { lancer_user::show!(net, error, "netsock init failed"); syscall::exit(); } }; let mut args_buf = [0u8; 256]; let args_len = net::recv_args(&rx, &mut args_buf); let args = &args_buf[..args_len]; let (_cmd, rest) = net::next_token(args); let (path_tok, _) = net::next_token(rest); let path: &[u8] = match path_tok.is_empty() { true => b"/", false => path_tok, }; let mut client = unsafe { fs::init() }; let list_rights = fs::FsRights::from_raw(fs::FsRights::LIST.raw() | fs::FsRights::TRAVERSE.raw()); let handle = match fs::open_path_with_rights(&mut client, 0, path, list_rights) { Ok(h) => h, Err(e) => { println!("ls: {}", e.name()); syscall::exit(); } }; list_dir(&mut client, handle); let _ = client.close(handle); syscall::exit() }