summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-01 13:51:28 +0200
committersalmonik <salmonik@tuta.io>2026-08-01 13:51:28 +0200
commit7d8c0ab9e1279e1aeb48ad6968533b4b07efad95 (patch)
tree7d2cfd93ef7d35368280f5a412e63f292916d874
parentshell: keyword redirection + post-filter flags (ADR 0002) (diff)
downloadrs-dos-7d8c0ab9e1279e1aeb48ad6968533b4b07efad95.tar.gz
rs-dos-7d8c0ab9e1279e1aeb48ad6968533b4b07efad95.zip
shell: cat <path> TIL <marker> sugar + precise ordering errors
- `cat/hexdump <path> TIL <marker>` is now sugar for `USE <path> TIL <marker>`: the paths are the input source, truncated at the first marker line across the combined stream. Path args + USE stays a syntax error (two input sources), now with a message that says so. - The parser's `trailing` flag conflated filters with the output keyword, so `cat f TO out TIL m` blamed the filters. USE/TIL now distinguish "before the filters" from "before the output keyword", and filters after the output keyword (`TO out -h 2`) are now rejected instead of silently accepted. - cat_file_until/hexdump_file_until return a tri-state (Done/Marker/ Aborted) so multi-path TIL truncation stops at the first marker. Verified in QEMU (10 checks): truncation, combined-stream marker, filters after the sugar, heredoc round trip, all four ordering-error messages, USE-with-paths ambiguity, fsck clean.
Diffstat (limited to '')
-rw-r--r--CONTEXT.md41
-rw-r--r--shell/src/lib.rs133
2 files changed, 143 insertions, 31 deletions
diff --git a/CONTEXT.md b/CONTEXT.md
index 7514a6f..c6bd5f9 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -375,6 +375,12 @@ Resolved this session:
375 All other commands reject input keywords with a syntax error — a 375 All other commands reject input keywords with a syntax error — a
376 command declares whether it takes input. `USE`/`TIL` are general 376 command declares whether it takes input. `USE`/`TIL` are general
377 building blocks; more consumers will come later. 377 building blocks; more consumers will come later.
378- **`cat <path> TIL <marker>` is sugar for `cat USE <path> TIL
379 <marker>`** (refined this session): the paths are the input source,
380 truncated at the first marker line across the combined stream —
381 `cat a b TIL m` reads `a` then `b` and stops at the first `m` line.
382 The single-source reading is unambiguous, so path args + `TIL` is
383 valid; path args + `USE` stays a syntax error (two input sources).
378- **Keywords: uppercase-only, aliases valid.** `WRITE`==`WRITING`, 384- **Keywords: uppercase-only, aliases valid.** `WRITE`==`WRITING`,
379 `TIL`==`UNTIL`, `USE`==`USING`; lowercase forms are ordinary text, so 385 `TIL`==`UNTIL`, `USE`==`USING`; lowercase forms are ordinary text, so
380 `echo use the force` can never redirect. Keywords are reserved 386 `echo use the force` can never redirect. Keywords are reserved
@@ -404,8 +410,9 @@ Assumed edge semantics (flag if wrong):
404 is silent, as `echo >` behaved before. 410 is silent, as `echo >` behaved before.
405- `-p` pauses terminal output only; with a file sink it is a no-op. 411- `-p` pauses terminal output only; with a file sink it is a no-op.
406- `-h N` / `-t N` require N ≥ 1; otherwise syntax error. 412- `-h N` / `-t N` require N ≥ 1; otherwise syntax error.
407- `cat p USE s` (path args *and* an input keyword) is a syntax error — 413- `cat p USE s` (path args *and* an explicit source keyword) is a
408 the input source is ambiguous. `cat USE s` and path-less `cat TIL m` 414 syntax error — the input source is ambiguous. `cat USE s`, path-less
415 `cat TIL m`, and `cat <path> TIL m` (sugar for `USE <path> TIL m`)
409 are the valid input forms. 416 are the valid input forms.
410- ADR 0002 (`docs/adr/0002-keyword-redirection-and-post-filters.md`) 417- ADR 0002 (`docs/adr/0002-keyword-redirection-and-post-filters.md`)
411 captures the keyword-redirection + post-filter decision; the ADR is 418 captures the keyword-redirection + post-filter decision; the ADR is
@@ -455,10 +462,16 @@ Everything above is now implemented in `shell/src/lib.rs`:
455 truncates and writes from offset 0, `TO` appends at current size. 462 truncates and writes from offset 0, `TO` appends at current size.
456 Diagnostics (errors, usage) always go to the terminal, never into 463 Diagnostics (errors, usage) always go to the terminal, never into
457 the pipeline or a file. 464 the pipeline or a file.
458- **Input consumers** — `cat`/`hexdump`, path-less only: no keyword = 465- **Input consumers** — `cat`/`hexdump`: no keyword = keyboard until
459 keyboard until ^D, `USE f` = file, `TIL m` = keyboard heredoc, 466 ^D, `USE f` = file, `TIL m` = keyboard heredoc, `USE f TIL m` =
460 `USE f TIL m` = file truncated at the exact marker line (marker and 467 file truncated at the exact marker line (marker and everything
461 everything after discarded). Other commands reject input keywords. 468 after discarded), and `cat <path> TIL m` / `hexdump <path> TIL m` =
469 the paths as source, truncated at the first marker across the
470 combined stream. Other commands reject input keywords.
471- **Ordering enforcement** — `USE`/`TIL` must precede the filters and
472 the output keyword; filters must precede the output keyword;
473 distinct messages for each violation ("must come before the
474 filters" / "must come before the output keyword").
462- **^D** — EOF everywhere, at the prompt it is a no-op (fresh 475- **^D** — EOF everywhere, at the prompt it is a no-op (fresh
463 prompt); ^C keeps line-cancel / stream-abort; `poweroff`/`exit` 476 prompt); ^C keeps line-cancel / stream-abort; `poweroff`/`exit`
464 are the only shutdown verbs. 477 are the only shutdown verbs.
@@ -474,9 +487,25 @@ Bugs found by QEMU verification and fixed:
4742. `cat USE f TIL m` leaked everything after the marker — the 4872. `cat USE f TIL m` leaked everything after the marker — the
475 final-partial-line emit ran even after the marker break; fixed 488 final-partial-line emit ran even after the marker break; fixed
476 with a `stopped` flag in `cat_file_until`/`hexdump_file_until`. 489 with a `stopped` flag in `cat_file_until`/`hexdump_file_until`.
4903. `cat file TO out TIL m` blamed the filters — the `trailing` flag
491 was set by both filters and the output keyword, so the "must come
492 before the filters" error fired for the output keyword too; the
493 parser now distinguishes them (`TO dst TIL m` → "TIL must come
494 before the output keyword"). Also: filters after the output
495 keyword (`TO dst -h 2`) were silently accepted — now a syntax
496 error.
4974. `cat file TIL m` was rejected as ambiguous — path args + `TIL` is
498 now sugar for `USE <path> TIL <marker>` (single source, truncated
499 at the marker); path args + `USE` remains a syntax error.
477 500
478Verified: 26 grammar checks pass on a fresh disk *and* on a reused 501Verified: 26 grammar checks pass on a fresh disk *and* on a reused
479one (rewriting existing files), `make test-disk` round trip is 502one (rewriting existing files), `make test-disk` round trip is
480fsck-clean, the `-p` pager pauses at screenfuls and resumes on 503fsck-clean, the `-p` pager pauses at screenfuls and resumes on
481keypress, `-h` stops `/dev/mem` at exactly N lines, and ^C flushes a 504keypress, `-h` stops `/dev/mem` at exactly N lines, and ^C flushes a
482`-t` window on an infinite stream. 505`-t` window on an infinite stream.
506
507Follow-up verification (this session): 10 checks pass in QEMU for the
508`cat <path> TIL <marker>` sugar — truncation at the marker, combined
509multi-path stream truncated at the first marker, filters after the
510sugar (`-h 1`), the heredoc round trip, all four ordering-error
511messages, and the `USE`-with-paths ambiguity message; fsck clean.
diff --git a/shell/src/lib.rs b/shell/src/lib.rs
index bc43faf..cf46cb0 100644
--- a/shell/src/lib.rs
+++ b/shell/src/lib.rs
@@ -52,7 +52,7 @@ pub fn run() -> ! {
52 // Read one line from keyboard (tty canonical mode) 52 // Read one line from keyboard (tty canonical mode)
53 match tty::read_line() { 53 match tty::read_line() {
54 LineInput::Interrupted => continue, // ^C — fresh prompt 54 LineInput::Interrupted => continue, // ^C — fresh prompt
55 LineInput::Eof => continue, // ^D on an empty line — fresh prompt (no poweroff) 55 LineInput::Eof => continue, // ^D on an empty line — fresh prompt (no poweroff)
56 LineInput::Line(line) => { 56 LineInput::Line(line) => {
57 let trimmed = line.trim(); 57 let trimmed = line.trim();
58 if trimmed.is_empty() { 58 if trimmed.is_empty() {
@@ -132,7 +132,10 @@ fn tokenize(s: &str) -> Result<Vec<Token>, &'static str> {
132} 132}
133 133
134fn is_keyword(s: &str) -> bool { 134fn is_keyword(s: &str) -> bool {
135 matches!(s, "WRITING" | "WRITE" | "TO" | "USE" | "USING" | "TIL" | "UNTIL") 135 matches!(
136 s,
137 "WRITING" | "WRITE" | "TO" | "USE" | "USING" | "TIL" | "UNTIL"
138 )
136} 139}
137 140
138/// How the command's input is fed. Only `cat`/`hexdump`, and only when 141/// How the command's input is fed. Only `cat`/`hexdump`, and only when
@@ -189,7 +192,7 @@ fn parse(s: &str) -> Result<Parsed, String> {
189 192
190 let mut i = 1; 193 let mut i = 1;
191 let mut args: Vec<String> = Vec::new(); 194 let mut args: Vec<String> = Vec::new();
192 195
193 let mut filters: Vec<Filter> = Vec::new(); 196 let mut filters: Vec<Filter> = Vec::new();
194 let mut output: Option<Sink> = None; 197 let mut output: Option<Sink> = None;
195 let mut trailing = false; // a filter flag or output keyword has been seen 198 let mut trailing = false; // a filter flag or output keyword has been seen
@@ -204,6 +207,9 @@ fn parse(s: &str) -> Result<Parsed, String> {
204 if !t.literal { 207 if !t.literal {
205 match text { 208 match text {
206 "USE" | "USING" => { 209 "USE" | "USING" => {
210 if output.is_some() {
211 return Err("USE must come before the output keyword".into());
212 }
207 if trailing { 213 if trailing {
208 return Err("USE must come before the filters".into()); 214 return Err("USE must come before the filters".into());
209 } 215 }
@@ -220,6 +226,9 @@ fn parse(s: &str) -> Result<Parsed, String> {
220 continue; 226 continue;
221 } 227 }
222 "TIL" | "UNTIL" => { 228 "TIL" | "UNTIL" => {
229 if output.is_some() {
230 return Err("TIL must come before the output keyword".into());
231 }
223 if trailing { 232 if trailing {
224 return Err("TIL must come before the filters".into()); 233 return Err("TIL must come before the filters".into());
225 } 234 }
@@ -255,6 +264,9 @@ fn parse(s: &str) -> Result<Parsed, String> {
255 continue; 264 continue;
256 } 265 }
257 "-h" | "-t" => { 266 "-h" | "-t" => {
267 if output.is_some() {
268 return Err("filters must come before the output keyword".into());
269 }
258 trailing = true; 270 trailing = true;
259 i += 1; 271 i += 1;
260 let n = toks.get(i).ok_or("flag needs a count")?; 272 let n = toks.get(i).ok_or("flag needs a count")?;
@@ -271,6 +283,9 @@ fn parse(s: &str) -> Result<Parsed, String> {
271 continue; 283 continue;
272 } 284 }
273 "-g" => { 285 "-g" => {
286 if output.is_some() {
287 return Err("filters must come before the output keyword".into());
288 }
274 trailing = true; 289 trailing = true;
275 i += 1; 290 i += 1;
276 let pat = toks.get(i).ok_or("-g needs a pattern")?; 291 let pat = toks.get(i).ok_or("-g needs a pattern")?;
@@ -279,6 +294,9 @@ fn parse(s: &str) -> Result<Parsed, String> {
279 continue; 294 continue;
280 } 295 }
281 "-p" => { 296 "-p" => {
297 if output.is_some() {
298 return Err("filters must come before the output keyword".into());
299 }
282 trailing = true; 300 trailing = true;
283 filters.push(Filter::Pause); 301 filters.push(Filter::Pause);
284 i += 1; 302 i += 1;
@@ -324,7 +342,10 @@ enum StageState {
324 /// Rolling window of the last N lines. 342 /// Rolling window of the last N lines.
325 Tail(VecDeque<String>), 343 Tail(VecDeque<String>),
326 Grep, 344 Grep,
327 Pause { shown: usize, screen: usize }, 345 Pause {
346 shown: usize,
347 screen: usize,
348 },
328} 349}
329 350
330/// Where the filtered line stream ends up. 351/// Where the filtered line stream ends up.
@@ -473,10 +494,9 @@ impl Pipeline {
473 unreachable!("stage state does not match filter") 494 unreachable!("stage state does not match filter")
474 }; 495 };
475 for l in cur.drain(..) { 496 for l in cur.drain(..) {
476 if *shown > 0 && *shown % *screen == 0 497 if *shown > 0 && *shown % *screen == 0 && !pager_wait() {
477 && !pager_wait() { 498 return false;
478 return false; 499 }
479 }
480 *shown += 1; 500 *shown += 1;
481 out.push(l); 501 out.push(l);
482 } 502 }
@@ -529,7 +549,10 @@ impl Pipeline {
529 if let OutSink::File { h: None, .. } = &self.sink { 549 if let OutSink::File { h: None, .. } = &self.sink {
530 self.open_sink(); 550 self.open_sink();
531 } 551 }
532 if let OutSink::File { h: Some(h), pos, .. } = &mut self.sink { 552 if let OutSink::File {
553 h: Some(h), pos, ..
554 } = &mut self.sink
555 {
533 let mut bytes = l.as_bytes().to_vec(); 556 let mut bytes = l.as_bytes().to_vec();
534 bytes.push(b'\n'); 557 bytes.push(b'\n');
535 if let Ok(n) = rsnix::fs::write(*h, &bytes, *pos) { 558 if let Ok(n) = rsnix::fs::write(*h, &bytes, *pos) {
@@ -544,7 +567,9 @@ impl Pipeline {
544 /// the file's current size. 567 /// the file's current size.
545 fn open_sink(&mut self) { 568 fn open_sink(&mut self) {
546 let (path, overwrite) = match &self.sink { 569 let (path, overwrite) = match &self.sink {
547 OutSink::File { path, overwrite, .. } => (path.clone(), *overwrite), 570 OutSink::File {
571 path, overwrite, ..
572 } => (path.clone(), *overwrite),
548 _ => return, 573 _ => return,
549 }; 574 };
550 match rsnix::fs::ensure_file(&path) { 575 match rsnix::fs::ensure_file(&path) {
@@ -616,9 +641,16 @@ fn execute(s: &str) {
616 term_err(&alloc::format!("{}: does not accept input", cmd)); 641 term_err(&alloc::format!("{}: does not accept input", cmd));
617 return; 642 return;
618 } 643 }
619 if !parsed.args.is_empty() { 644 // `cat <path> TIL <marker>` is sugar for `cat USE <path> TIL
645 // <marker>`: the paths are the input source, truncated at the
646 // marker. An explicit source keyword (USE / USE..TIL) alongside
647 // path arguments is genuinely two input sources.
648 if !parsed.args.is_empty() && !matches!(parsed.input, Input::Til(_)) {
620 term_err(&alloc::format!( 649 term_err(&alloc::format!(
621 "{}: input keyword with path arguments is ambiguous", 650 "{}: input source keyword with path arguments is ambiguous — \
651the paths are the input; drop the keyword, or to truncate them at a marker \
652write `{} <path> TIL <marker>`",
653 cmd,
622 cmd 654 cmd
623 )); 655 ));
624 return; 656 return;
@@ -680,8 +712,10 @@ fn cmd_help(_p: &Parsed) {
680redirection keywords (uppercase, after args): 712redirection keywords (uppercase, after args):
681 USE/USING <src> read command input from a file (cat/hexdump only) 713 USE/USING <src> read command input from a file (cat/hexdump only)
682 TIL/UNTIL <marker> read input until an exact marker line (heredoc) 714 TIL/UNTIL <marker> read input until an exact marker line (heredoc)
715 cat <path> TIL <marker> reads the path to the marker
683 WRITING/WRITE <dst> write output to a file (overwrite) 716 WRITING/WRITE <dst> write output to a file (overwrite)
684 TO <dst> append output to a file 717 TO <dst> append output to a file
718 order: USE/TIL first, then the -flags, then WRITING/TO last
685 a file sink replaces the screen; > >> < << are gone (> is plain text) 719 a file sink replaces the screen; > >> < << are gone (> is plain text)
686 720
687post-filter flags (trailing, applied left to right): 721post-filter flags (trailing, applied left to right):
@@ -758,6 +792,14 @@ fn cmd_ls(p: &Parsed) {
758// cat — file and stream consumers 792// cat — file and stream consumers
759// --------------------------------------------------------------------------- 793// ---------------------------------------------------------------------------
760 794
795/// How a file read ended: read to EOF, stopped at the TIL marker, or
796/// aborted (^C / head kill) — the caller should stop processing paths.
797enum ReadResult {
798 Done,
799 Marker,
800 Aborted,
801}
802
761fn cmd_cat(p: &Parsed) { 803fn cmd_cat(p: &Parsed) {
762 let mut pipe = Pipeline::new(&p.filters, &p.output); 804 let mut pipe = Pipeline::new(&p.filters, &p.output);
763 match &p.input { 805 match &p.input {
@@ -778,7 +820,18 @@ fn cmd_cat(p: &Parsed) {
778 cat_file(&mut pipe, path); 820 cat_file(&mut pipe, path);
779 } 821 }
780 Input::Til(marker) => { 822 Input::Til(marker) => {
781 cat_stdin_until(&mut pipe, marker); 823 if p.args.is_empty() {
824 cat_stdin_until(&mut pipe, marker);
825 } else {
826 // `cat <path> TIL <marker>` — the paths are the input
827 // source, truncated at the first marker line.
828 for path in &p.args {
829 match cat_file_until(&mut pipe, path, marker) {
830 ReadResult::Done => {}
831 ReadResult::Marker | ReadResult::Aborted => break,
832 }
833 }
834 }
782 } 835 }
783 Input::UseTil(path, marker) => { 836 Input::UseTil(path, marker) => {
784 cat_file_until(&mut pipe, path, marker); 837 cat_file_until(&mut pipe, path, marker);
@@ -796,7 +849,7 @@ fn cat_stdin(pipe: &mut Pipeline) -> bool {
796 } 849 }
797 } 850 }
798 LineInput::Interrupted => return false, // ^C aborts the stream 851 LineInput::Interrupted => return false, // ^C aborts the stream
799 LineInput::Eof => return true, // ^D = EOF 852 LineInput::Eof => return true, // ^D = EOF
800 } 853 }
801 } 854 }
802} 855}
@@ -855,12 +908,12 @@ fn cat_file(pipe: &mut Pipeline, path: &str) -> bool {
855 true 908 true
856} 909}
857 910
858fn cat_file_until(pipe: &mut Pipeline, path: &str, marker: &str) -> bool { 911fn cat_file_until(pipe: &mut Pipeline, path: &str, marker: &str) -> ReadResult {
859 let h = match rsnix::fs::resolve(path) { 912 let h = match rsnix::fs::resolve(path) {
860 Some(h) => h, 913 Some(h) => h,
861 None => { 914 None => {
862 term_err(&alloc::format!("cat: {}: no such file", path)); 915 term_err(&alloc::format!("cat: {}: no such file", path));
863 return true; 916 return ReadResult::Done;
864 } 917 }
865 }; 918 };
866 let mut buf = [0u8; 512]; 919 let mut buf = [0u8; 512];
@@ -869,7 +922,7 @@ fn cat_file_until(pipe: &mut Pipeline, path: &str, marker: &str) -> bool {
869 let mut stopped = false; 922 let mut stopped = false;
870 'outer: loop { 923 'outer: loop {
871 if tty::interrupt_pressed() { 924 if tty::interrupt_pressed() {
872 return false; 925 return ReadResult::Aborted;
873 } 926 }
874 match rsnix::fs::read(h, &mut buf, pos) { 927 match rsnix::fs::read(h, &mut buf, pos) {
875 Ok(0) | Err(_) => break, 928 Ok(0) | Err(_) => break,
@@ -885,16 +938,20 @@ fn cat_file_until(pipe: &mut Pipeline, path: &str, marker: &str) -> bool {
885 break 'outer; 938 break 'outer;
886 } 939 }
887 if !pipe.line(line) { 940 if !pipe.line(line) {
888 return false; 941 return ReadResult::Aborted;
889 } 942 }
890 } 943 }
891 } 944 }
892 } 945 }
893 } 946 }
894 if !stopped && !pending.is_empty() && pending != marker && !pipe.line(&pending) { 947 if !stopped && !pending.is_empty() && pending != marker && !pipe.line(&pending) {
895 return false; 948 return ReadResult::Aborted;
949 }
950 if stopped {
951 ReadResult::Marker
952 } else {
953 ReadResult::Done
896 } 954 }
897 true
898} 955}
899 956
900// --------------------------------------------------------------------------- 957// ---------------------------------------------------------------------------
@@ -998,7 +1055,18 @@ fn cmd_hexdump(p: &Parsed) {
998 hexdump_file(&mut pipe, path); 1055 hexdump_file(&mut pipe, path);
999 } 1056 }
1000 Input::Til(marker) => { 1057 Input::Til(marker) => {
1001 hexdump_stdin_until(&mut pipe, marker); 1058 if p.args.is_empty() {
1059 hexdump_stdin_until(&mut pipe, marker);
1060 } else {
1061 // `hexdump <path> TIL <marker>` — the paths are the input
1062 // source, truncated at the first marker line.
1063 for path in &p.args {
1064 match hexdump_file_until(&mut pipe, path, marker) {
1065 ReadResult::Done => {}
1066 ReadResult::Marker | ReadResult::Aborted => break,
1067 }
1068 }
1069 }
1002 } 1070 }
1003 Input::UseTil(path, marker) => { 1071 Input::UseTil(path, marker) => {
1004 hexdump_file_until(&mut pipe, path, marker); 1072 hexdump_file_until(&mut pipe, path, marker);
@@ -1101,12 +1169,12 @@ fn hexdump_stdin_until(pipe: &mut Pipeline, marker: &str) {
1101 ctx.flush(); 1169 ctx.flush();
1102} 1170}
1103 1171
1104fn hexdump_file_until(pipe: &mut Pipeline, path: &str, marker: &str) { 1172fn hexdump_file_until(pipe: &mut Pipeline, path: &str, marker: &str) -> ReadResult {
1105 let h = match rsnix::fs::resolve(path) { 1173 let h = match rsnix::fs::resolve(path) {
1106 Some(h) => h, 1174 Some(h) => h,
1107 None => { 1175 None => {
1108 term_err(&alloc::format!("hexdump: {}: no such file", path)); 1176 term_err(&alloc::format!("hexdump: {}: no such file", path));
1109 return; 1177 return ReadResult::Done;
1110 } 1178 }
1111 }; 1179 };
1112 let mut ctx = HexCtx { 1180 let mut ctx = HexCtx {
@@ -1149,6 +1217,13 @@ fn hexdump_file_until(pipe: &mut Pipeline, path: &str, marker: &str) {
1149 ctx.feed(pending.as_bytes()); 1217 ctx.feed(pending.as_bytes());
1150 } 1218 }
1151 ctx.flush(); 1219 ctx.flush();
1220 if !ctx.alive {
1221 ReadResult::Aborted
1222 } else if stopped {
1223 ReadResult::Marker
1224 } else {
1225 ReadResult::Done
1226 }
1152} 1227}
1153 1228
1154// --------------------------------------------------------------------------- 1229// ---------------------------------------------------------------------------
@@ -1242,7 +1317,11 @@ fn cmd_mem(p: &Parsed) {
1242 let ps = rsnix::memory::page_stats(); 1317 let ps = rsnix::memory::page_stats();
1243 1318
1244 let mut pipe = Pipeline::new(&p.filters, &p.output); 1319 let mut pipe = Pipeline::new(&p.filters, &p.output);
1245 let _ = pipe.line(&alloc::format!("memory map (key=0x{:x}, {} entries)", key, map.len())); 1320 let _ = pipe.line(&alloc::format!(
1321 "memory map (key=0x{:x}, {} entries)",
1322 key,
1323 map.len()
1324 ));
1246 let _ = pipe.line(&alloc::format!( 1325 let _ = pipe.line(&alloc::format!(
1247 " usable {:4} MB Conventional", 1326 " usable {:4} MB Conventional",
1248 conventional_kb / 1024 1327 conventional_kb / 1024
@@ -1304,7 +1383,11 @@ fn cmd_mem(p: &Parsed) {
1304fn cmd_mmap(p: &Parsed) { 1383fn cmd_mmap(p: &Parsed) {
1305 let (map, key) = rsnix::memory::get_map(); 1384 let (map, key) = rsnix::memory::get_map();
1306 let mut pipe = Pipeline::new(&p.filters, &p.output); 1385 let mut pipe = Pipeline::new(&p.filters, &p.output);
1307 let _ = pipe.line(&alloc::format!("memory map (key=0x{:x}, {} entries):", key, map.len())); 1386 let _ = pipe.line(&alloc::format!(
1387 "memory map (key=0x{:x}, {} entries):",
1388 key,
1389 map.len()
1390 ));
1308 let _ = pipe.line(" # type start pages size"); 1391 let _ = pipe.line(" # type start pages size");
1309 for (i, d) in map.iter().enumerate() { 1392 for (i, d) in map.iter().enumerate() {
1310 let l = alloc::format!( 1393 let l = alloc::format!(