Nothing to see here, move along
1#![no_std]
2#![no_main]
3
4use lancer_user::fs::{self, FsClient, READDIR_ENTRY_SIZE, ReadDirEntry};
5use lancer_user::net;
6use lancer_user::syscall;
7use lancer_user::{print, println};
8
9struct SortEntry {
10 name: [u8; 48],
11 name_len: u16,
12 inode_type: u8,
13 size: u64,
14}
15
16const EMPTY_ENTRY: SortEntry = SortEntry {
17 name: [0; 48],
18 name_len: 0,
19 inode_type: 0,
20 size: 0,
21};
22
23fn type_char(t: u8) -> u8 {
24 match t {
25 1 => b'd',
26 2 => b'l',
27 _ => b'f',
28 }
29}
30
31fn format_size(size: u64, buf: &mut [u8; 20]) -> usize {
32 match size {
33 0 => {
34 buf[0] = b'0';
35 1
36 }
37 _ => {
38 let mut tmp = [0u8; 20];
39 fn fill(v: u64, tmp: &mut [u8; 20], pos: usize) -> usize {
40 match v {
41 0 => pos,
42 _ => {
43 tmp[pos] = b'0' + (v % 10) as u8;
44 fill(v / 10, tmp, pos + 1)
45 }
46 }
47 }
48 let digits = fill(size, &mut tmp, 0);
49 (0..digits).for_each(|j| {
50 buf[j] = tmp[digits - 1 - j];
51 });
52 digits
53 }
54 }
55}
56
57fn insertion_sort(entries: &mut [SortEntry], count: usize) {
58 fn sink(entries: &mut [SortEntry], j: usize) {
59 if j > 0 && name_cmp(&entries[j], &entries[j - 1]) == core::cmp::Ordering::Less {
60 entries.swap(j, j - 1);
61 sink(entries, j - 1);
62 }
63 }
64 (1..count).for_each(|i| sink(entries, i));
65}
66
67fn name_cmp(a: &SortEntry, b: &SortEntry) -> core::cmp::Ordering {
68 let a_name = &a.name[..a.name_len as usize];
69 let b_name = &b.name[..b.name_len as usize];
70 let min_len = a_name.len().min(b_name.len());
71 (0..min_len)
72 .find_map(|i| match a_name[i].cmp(&b_name[i]) {
73 core::cmp::Ordering::Equal => None,
74 ord => Some(ord),
75 })
76 .unwrap_or_else(|| a_name.len().cmp(&b_name.len()))
77}
78
79const MAX_DISPLAY_ENTRIES: usize = 128;
80
81struct CollectState {
82 total: usize,
83 overflow: bool,
84}
85
86fn collect_entries(
87 client: &mut FsClient,
88 handle: u8,
89 buf: &mut [u8],
90 entries: &mut [SortEntry; MAX_DISPLAY_ENTRIES],
91 state: &mut CollectState,
92 cursor: u64,
93) {
94 match client.readdir(handle, cursor, buf) {
95 Err(e) => {
96 println!("readdir error: {}", e.name());
97 }
98 Ok((0, _)) => {}
99 Ok((count, next)) => {
100 (0..count).for_each(|i| match state.total < MAX_DISPLAY_ENTRIES {
101 true => {
102 let off = i * READDIR_ENTRY_SIZE;
103 if let Some(entry) = ReadDirEntry::from_bytes(&buf[off..]) {
104 let nlen = (entry.name_len as usize).min(48);
105 entries[state.total].name[..nlen].copy_from_slice(&entry.name[..nlen]);
106 entries[state.total].name_len = nlen as u16;
107 entries[state.total].inode_type = entry.inode_type;
108 entries[state.total].size = entry.size;
109 state.total += 1;
110 }
111 }
112 false => {
113 state.overflow = true;
114 }
115 });
116 collect_entries(client, handle, buf, entries, state, next)
117 }
118 }
119}
120
121fn list_dir(client: &mut FsClient, handle: u8) {
122 let mut entries = [EMPTY_ENTRY; MAX_DISPLAY_ENTRIES];
123 let mut state = CollectState {
124 total: 0,
125 overflow: false,
126 };
127 let mut buf = [0u8; READDIR_ENTRY_SIZE * 16];
128
129 collect_entries(client, handle, &mut buf, &mut entries, &mut state, 0);
130
131 insertion_sort(&mut entries, state.total);
132
133 (0..state.total).for_each(|i| {
134 let e = &entries[i];
135 let tc = type_char(e.inode_type);
136 print!("{}", tc as char);
137 print!(" ");
138 if let Ok(name) = core::str::from_utf8(&e.name[..e.name_len as usize]) {
139 print!("{}", name);
140 }
141 let mut sbuf = [0u8; 20];
142 let slen = format_size(e.size, &mut sbuf);
143 print!(" ");
144 if let Ok(s) = core::str::from_utf8(&sbuf[..slen]) {
145 print!("{}", s);
146 }
147 println!();
148 });
149
150 if state.overflow {
151 println!("(listing truncated at {} entries)", MAX_DISPLAY_ENTRIES);
152 }
153}
154
155#[unsafe(no_mangle)]
156pub extern "C" fn lancer_main() -> ! {
157 let (rx, _tx) = match net::init() {
158 Some(pair) => pair,
159 None => {
160 lancer_user::show!(net, error, "netsock init failed");
161 syscall::exit();
162 }
163 };
164
165 let mut args_buf = [0u8; 256];
166 let args_len = net::recv_args(&rx, &mut args_buf);
167 let args = &args_buf[..args_len];
168
169 let (_cmd, rest) = net::next_token(args);
170 let (path_tok, _) = net::next_token(rest);
171 let path: &[u8] = match path_tok.is_empty() {
172 true => b"/",
173 false => path_tok,
174 };
175
176 let mut client = unsafe { fs::init() };
177
178 let list_rights =
179 fs::FsRights::from_raw(fs::FsRights::LIST.raw() | fs::FsRights::TRAVERSE.raw());
180 let handle = match fs::open_path_with_rights(&mut client, 0, path, list_rights) {
181 Ok(h) => h,
182 Err(e) => {
183 println!("ls: {}", e.name());
184 syscall::exit();
185 }
186 };
187
188 list_dir(&mut client, handle);
189 let _ = client.close(handle);
190
191 syscall::exit()
192}