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}