common: Add the shell module.
authorTilman Sauerbeck <tilman@code-monkey.de>
Wed, 8 Jan 2020 10:09:36 +0000 (11:09 +0100)
committerTilman Sauerbeck <tilman@code-monkey.de>
Wed, 8 Jan 2020 11:07:33 +0000 (12:07 +0100)
It implements no useful functionality yet.

SConscript.libcommon
src/common/lib.rs
src/common/shell.rs [new file with mode: 0644]

index 5f9ffe246b6dd4a328adb6dc5e0e8c791037a9dd..a7c5aff85e7814295b8f5da3eb3c8d9077782860 100644 (file)
@@ -24,6 +24,7 @@ source_files_rs = [
     'src/common/time.rs',
     'src/common/storage.rs',
     'src/common/mx25l.rs',
+    'src/common/shell.rs',
 ]
 
 source_files_c = [
index 72b261a3e14f2717e76f66aee0d921b22a6f0c4e..dd33c6809466543026e26a0b645dc77e30d9f7be 100644 (file)
@@ -46,3 +46,4 @@ pub mod fmt;
 pub mod time;
 pub mod storage;
 pub mod mx25l;
+pub mod shell;
diff --git a/src/common/shell.rs b/src/common/shell.rs
new file mode 100644 (file)
index 0000000..09c7907
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2020 Tilman Sauerbeck (tilman at code-monkey de)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+use buffer::Buffer;
+
+pub struct Shell<'a> {
+    tx_buf: &'a mut Buffer,
+    command_offset: usize,
+    command_buffer: [u8; 32],
+}
+
+extern {
+    fn usb_serial_read(c: *mut u8) -> bool;
+}
+
+struct ArgumentIter<'a> {
+    command: &'a [u8],
+    offset: usize,
+}
+
+impl<'a> Iterator for ArgumentIter<'a> {
+    type Item = &'a [u8];
+
+    fn next(&mut self) -> Option<&'a [u8]> {
+        let delimiter = b' ';
+        let mut start : Option<usize> = None;
+
+        // Find the start of the substring.
+        for o in self.offset..self.command.len() {
+            if self.command[o] != delimiter {
+                start = Some(o);
+                break
+            }
+        }
+
+        start.and_then(|start2| {
+            let mut end : Option<usize> = None;
+
+            // Find the end of the substring.
+            for o in start2..self.command.len() {
+                if self.command[o] == delimiter {
+                    break
+                }
+
+                end = Some(o);
+            }
+
+            end.and_then(|end2| {
+                let end3 = end2 + 1;
+
+                self.offset = end3;
+
+                Some(&self.command[start2..end3])
+            })
+        })
+    }
+}
+
+fn read_char() -> Option<u8> {
+    unsafe {
+        let mut c = b'\0';
+
+        if usb_serial_read(&mut c) {
+            Some(c)
+        } else {
+            None
+        }
+    }
+}
+
+impl<'a> Shell<'a> {
+    pub fn new(tx_buf: &mut Buffer) -> Shell {
+        Shell {
+            tx_buf: tx_buf,
+            command_offset: 0,
+            command_buffer: [0; 32],
+        }
+    }
+
+    pub fn update(&mut self) {
+        while let Some(c) = read_char() {
+            if c != b'\n' {
+                if self.command_offset != self.command_buffer.len() {
+                    self.command_buffer[self.command_offset] = c;
+                    self.command_offset += 1;
+                }
+            } else {
+                let command_length = self.command_offset;
+                self.command_offset = 0;
+
+                if command_length != self.command_buffer.len() {
+                    self.command_buffer[command_length] = b'\0';
+
+                    if command_length != 0 {
+                        self.dispatch(command_length);
+                    }
+                } else {
+                    self.command_buffer[self.command_offset] = b'\0';
+
+                    self.tx_buf.write(b"maximum command length exceeded.\n");
+                }
+
+                self.tx_buf.write(b"$ ");
+                self.tx_buf.flush();
+            }
+        }
+    }
+
+    fn dispatch(&mut self, command_length: usize) {
+        let command : [u8; 32] = self.command_buffer;
+
+        let mut args_iter = ArgumentIter {
+            command: &command[0..command_length],
+            offset: 0,
+        };
+
+        match args_iter.next() {
+            Some(b"help") => {
+                let usage = b"\
+Supported commands:
+    help           Show this help message.
+";
+
+                self.tx_buf.write(usage);
+            },
+
+            Some(ref other) => {
+                self.tx_buf.write(b"unknown_command: ");
+                self.tx_buf.write(other);
+                self.tx_buf.write(b"\n");
+            },
+
+            None => {
+            }
+        }
+    }
+}