1use std::collections::HashMap;
2use std::fs::{File, OpenOptions};
3use std::io::{BufRead, BufReader, Write};
4
5enum FileHandle {
7 Read(BufReader<File>),
8 Write(File),
9}
10
11pub struct IoContext {
17 handles: HashMap<i32, FileHandle>,
18 next_fd: i32,
19}
20
21impl Default for IoContext {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl IoContext {
28 pub fn new() -> Self {
30 Self {
31 handles: HashMap::new(),
32 next_fd: 3,
33 }
34 }
35
36 pub fn fopen(&mut self, path: &str, mode: &str) -> i32 {
39 let handle = match mode {
40 "r" => File::open(path).map(|f| FileHandle::Read(BufReader::new(f))),
41 "w" => File::create(path).map(FileHandle::Write),
42 "a" => OpenOptions::new()
43 .append(true)
44 .create(true)
45 .open(path)
46 .map(FileHandle::Write),
47 "r+" => OpenOptions::new()
48 .read(true)
49 .write(true)
50 .open(path)
51 .map(FileHandle::Write),
52 _ => return -1,
53 };
54 match handle {
55 Ok(h) => {
56 let fd = self.next_fd;
57 self.handles.insert(fd, h);
58 self.next_fd += 1;
59 fd
60 }
61 Err(_) => -1,
62 }
63 }
64
65 pub fn fclose(&mut self, fd: i32) -> i32 {
67 if self.handles.remove(&fd).is_some() {
68 0
69 } else {
70 -1
71 }
72 }
73
74 pub fn fclose_all(&mut self) {
76 self.handles.clear();
77 }
78
79 pub fn fgetl(&mut self, fd: i32) -> Option<String> {
82 match self.handles.get_mut(&fd)? {
83 FileHandle::Read(reader) => {
84 let mut line = String::new();
85 match reader.read_line(&mut line) {
86 Ok(0) => None,
87 Ok(_) => {
88 if line.ends_with('\n') {
89 line.pop();
90 }
91 if line.ends_with('\r') {
92 line.pop();
93 }
94 Some(line)
95 }
96 Err(_) => None,
97 }
98 }
99 FileHandle::Write(_) => None,
100 }
101 }
102
103 pub fn fgets(&mut self, fd: i32) -> Option<String> {
106 match self.handles.get_mut(&fd)? {
107 FileHandle::Read(reader) => {
108 let mut line = String::new();
109 match reader.read_line(&mut line) {
110 Ok(0) => None,
111 Ok(_) => Some(line),
112 Err(_) => None,
113 }
114 }
115 FileHandle::Write(_) => None,
116 }
117 }
118
119 pub fn write_to_fd(&mut self, fd: i32, s: &str) -> Result<(), String> {
122 match fd {
123 1 => {
124 print!("{s}");
125 std::io::stdout().flush().ok();
126 Ok(())
127 }
128 2 => {
129 eprint!("{s}");
130 std::io::stderr().flush().ok();
131 Ok(())
132 }
133 _ => match self.handles.get_mut(&fd) {
134 Some(FileHandle::Write(f)) => f
135 .write_all(s.as_bytes())
136 .map_err(|e| format!("fprintf: write error: {e}")),
137 Some(FileHandle::Read(_)) => {
138 Err(format!("fprintf: fd {fd} is not open for writing"))
139 }
140 None => Err(format!("fprintf: invalid file descriptor {fd}")),
141 },
142 }
143 }
144}