--- /dev/null
+/*
+ * 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 => {
+ }
+ }
+ }
+}