devela/sys/os/linux/fns/read.rs
1// devela::sys::os::linux::fns::read
2//
3//! read related functions
4//
5
6use super::{linux_print, linux_sys_exit, linux_sys_read, LINUX_FILENO as FILENO};
7use core::str;
8
9/// Gets a single byte from *stdin*.
10///
11/// This function makes use of the [`linux_sys_read`] syscall to read a byte.
12///
13/// # Error Handling
14/// If the read fails, it prints an error message and exits with status code 11.
15pub fn linux_get_byte() -> u8 {
16 let mut c = 0;
17 loop {
18 let n = unsafe { linux_sys_read(FILENO::STDIN, &mut c as *mut u8, 1) };
19 if n < 0 {
20 linux_print("read failed");
21 unsafe { linux_sys_exit(11) };
22 }
23 if n == 1 {
24 break;
25 }
26 }
27 c
28}
29
30/// Pauses execution until receiving from *stdin* any `char` in the `list`.
31///
32/// # Error Handling
33/// If the read fails, it prints an error message and exits with status code 11.
34pub fn linux_pause_until_char(list: &[char]) {
35 loop {
36 if list.contains(&linux_get_dirty_char()) {
37 break;
38 }
39 }
40}
41
42/// Gets a single `char` from *stdin*,
43/// or `None` if the bytes are not valid utf-8.
44///
45/// # Error Handling
46/// If the read fails, it prints an error message and exits with status code 11.
47pub fn linux_get_char() -> Option<char> {
48 let bytes = linux_get_utf8_bytes()?;
49 let s = unsafe { str::from_utf8_unchecked(&bytes) };
50 Some(s.chars().next().unwrap())
51}
52
53/// Gets either a single `char` from *stdin*, or the replacement character.
54///
55/// If the bytes received doesn't form a valid unicode scalar then the
56/// [replacement character (�)][char::REPLACEMENT_CHARACTER] will be returned.
57///
58/// # Error Handling
59/// If the read fails, it prints an error message and exits with status code 11.
60pub fn linux_get_dirty_char() -> char {
61 match linux_get_utf8_bytes() {
62 Some(bytes) => {
63 let s = unsafe { str::from_utf8_unchecked(&bytes) };
64 s.chars().next().unwrap()
65 }
66 None => char::REPLACEMENT_CHARACTER,
67 }
68}
69
70/// Gets a utf-8 encoded byte sequence from *stdin* representing a `char`.
71///
72/// Returns `None` if the bytes does not form a valid unicode scalar.
73///
74/// # Error Handling
75/// If the read fails, it prints an error message and exits with status code 11.
76pub fn linux_get_utf8_bytes() -> Option<[u8; 4]> {
77 let mut bytes = [0u8; 4];
78 let len;
79
80 // Read the first byte to determine the length of the character.
81 bytes[0] = linux_get_byte();
82 if bytes[0] & 0x80 == 0 {
83 // This is an ASCII character, so we can return it immediately.
84 return Some([bytes[0], 0, 0, 0]);
85 } else if bytes[0] & 0xE0 == 0xC0 {
86 len = 2;
87 } else if bytes[0] & 0xF0 == 0xE0 {
88 len = 3;
89 } else if bytes[0] & 0xF8 == 0xF0 {
90 len = 4;
91 } else {
92 // Not a valid first byte of a UTF-8 character.
93 return None;
94 }
95
96 // Read the remaining bytes of the character.
97 for i in 1..len {
98 bytes[i as usize] = linux_get_byte();
99 if bytes[i as usize] & 0xC0 != 0x80 {
100 // Not a valid continuation byte.
101 return None;
102 }
103 }
104
105 Some(bytes)
106}
107
108/// Prompts the user for a string from *stdin*, backed by a `buffer`.
109///
110/// # Examples
111/// ```no_run
112/// use devela::linux_prompt;
113///
114/// let mut name_buffer = [0_u8; 32];
115/// let name: &str = linux_prompt::<32>("Enter your name: ", &mut name_buffer);
116/// ```
117///
118/// # Error Handling
119/// If the write fails, it prints an error message and exits with status code 10.
120/// If the read fails, it prints an error message and exits with status code 11.
121pub fn linux_prompt<'input, const CAP: usize>(
122 text: &str,
123 buffer: &'input mut [u8; CAP],
124) -> &'input str {
125 linux_print(text);
126 linux_get_line(buffer)
127}
128
129/// Gets a string from *stdin* backed by a `buffer`, until a newline.
130///
131/// # Examples
132/// ```no_run
133/// use devela::linux_get_line;
134///
135/// let mut buf = [0_u8; 32];
136/// let name: &str = linux_get_line::<32>(&mut buf);
137/// ```
138///
139/// # Error handling
140/// If the read fails, it prints an error message and exits with status code 11.
141pub fn linux_get_line<const CAP: usize>(buffer: &mut [u8; CAP]) -> &str {
142 linux_get_str(buffer, '\n')
143}
144
145/// Gets a string from *stdin* backed by a `buffer`,
146/// until the `stop` char is received.
147///
148/// # Examples
149/// ```no_run
150/// use devela::linux_get_str;
151///
152/// let mut buf = [0_u8; 32];
153/// let name: &str = linux_get_str::<32>(&mut buf, '\n');
154/// ```
155pub fn linux_get_str<const CAP: usize>(buffer: &mut [u8; CAP], stop: char) -> &str {
156 let mut index = 0;
157 loop {
158 if let Some(c) = linux_get_char() {
159 let mut c_buf = [0; 4];
160 let c_str = c.encode_utf8(&mut c_buf);
161
162 if c == stop {
163 break;
164 } else if index + c_str.len() <= CAP {
165 linux_print(c_str);
166
167 for &b in c_str.as_bytes() {
168 buffer[index] = b;
169 index += 1;
170 }
171 }
172 }
173 }
174
175 unsafe { str::from_utf8_unchecked(&buffer[..index]) }
176}