common: Implement the "ls" shell command.
[gps-watch.git] / src / common / shell.rs
1 /*
2  * Copyright (c) 2020 Tilman Sauerbeck (tilman at code-monkey de)
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23
24 use buffer::Buffer;
25 use logger::Logger;
26 use yencode::Yencode;
27 use systick;
28
29 pub struct Shell<'a> {
30     tx_buf: &'a mut Buffer,
31     command_offset: usize,
32     command_buffer: [u8; 32],
33 }
34
35 extern {
36     fn usb_serial_read(c: *mut u8) -> bool;
37 }
38
39 struct ArgumentIter<'a> {
40     command: &'a [u8],
41     offset: usize,
42 }
43
44 impl<'a> Iterator for ArgumentIter<'a> {
45     type Item = &'a [u8];
46
47     fn next(&mut self) -> Option<&'a [u8]> {
48         let delimiter = b' ';
49         let mut start : Option<usize> = None;
50
51         // Find the start of the substring.
52         for o in self.offset..self.command.len() {
53             if self.command[o] != delimiter {
54                 start = Some(o);
55                 break
56             }
57         }
58
59         start.and_then(|start2| {
60             let mut end : Option<usize> = None;
61
62             // Find the end of the substring.
63             for o in start2..self.command.len() {
64                 if self.command[o] == delimiter {
65                     break
66                 }
67
68                 end = Some(o);
69             }
70
71             end.and_then(|end2| {
72                 let end3 = end2 + 1;
73
74                 self.offset = end3;
75
76                 Some(&self.command[start2..end3])
77             })
78         })
79     }
80 }
81
82 fn atoi32(s: &[u8]) -> Option<u32> {
83     let mut u = 0u32;
84
85     for o in 0..s.len() {
86         let c = s[o];
87         let zero = b'0' as u8;
88         let nine = b'9' as u8;
89
90         if c < zero || c > nine {
91             return None;
92         }
93
94         let d = (c - zero) as u32;
95
96         if let Some(u2) = u.checked_mul(10) {
97             if let Some(u3) = u2.checked_add(d) {
98                 u = u3;
99             } else {
100                 return None;
101             }
102         } else {
103             return None;
104         }
105     }
106
107     Some(u)
108 }
109
110 fn atoi16(s: &[u8]) -> Option<u16> {
111     atoi32(s).and_then(|u| {
112         if u <= u16::max_value() as u32 {
113             Some(u as u16)
114         } else {
115             None
116         }
117     })
118 }
119
120 fn read_char() -> Option<u8> {
121     unsafe {
122         let mut c = b'\0';
123
124         if usb_serial_read(&mut c) {
125             Some(c)
126         } else {
127             None
128         }
129     }
130 }
131
132 fn read_char_delay_ms(limit_ms: u32) -> Option<u8> {
133     let mut total_delay_ms = 0u32;
134
135     while total_delay_ms < limit_ms {
136         if let Some(c) = read_char() {
137             return Some(c);
138         }
139
140         let ms = 50;
141
142         systick::delay_ms(ms);
143         total_delay_ms += ms;
144     }
145
146     None
147 }
148
149 impl<'a> Shell<'a> {
150     pub fn new(tx_buf: &mut Buffer) -> Shell {
151         Shell {
152             tx_buf: tx_buf,
153             command_offset: 0,
154             command_buffer: [0; 32],
155         }
156     }
157
158     pub fn update(&mut self, logger: &mut Logger) {
159         while let Some(c) = read_char() {
160             if c != b'\n' {
161                 if self.command_offset != self.command_buffer.len() {
162                     self.command_buffer[self.command_offset] = c;
163                     self.command_offset += 1;
164                 }
165             } else {
166                 let command_length = self.command_offset;
167                 self.command_offset = 0;
168
169                 if command_length != self.command_buffer.len() {
170                     self.command_buffer[command_length] = b'\0';
171
172                     if command_length != 0 {
173                         self.dispatch(command_length, logger);
174                     }
175                 } else {
176                     self.command_buffer[self.command_offset] = b'\0';
177
178                     self.tx_buf.write(b"maximum command length exceeded.\n");
179                 }
180
181                 self.tx_buf.write(b"$ ");
182                 self.tx_buf.flush();
183             }
184         }
185     }
186
187     fn dispatch(&mut self, command_length: usize, logger: &mut Logger) {
188         let command : [u8; 32] = self.command_buffer;
189
190         let mut args_iter = ArgumentIter {
191             command: &command[0..command_length],
192             offset: 0,
193         };
194
195         match args_iter.next() {
196             Some(b"help") => {
197                 let usage = b"\
198 Supported commands:
199     help           Show this help message.
200     ls             List recordings.
201     get REC_ID     Retrieve recording.
202     clear_storage  Fully erase the flash's contents.
203     dump_storage   Dump the flash's contents.
204 ";
205
206                 self.tx_buf.write(usage);
207             },
208
209             Some(b"ls") => self.run_ls(logger),
210             Some(b"get") => self.run_get(logger, &mut args_iter),
211             Some(b"clear_storage") => self.run_clear_storage(logger),
212             Some(b"dump_storage") => self.run_dump_storage(logger),
213
214             Some(ref other) => {
215                 self.tx_buf.write(b"unknown_command: ");
216                 self.tx_buf.write(other);
217                 self.tx_buf.write(b"\n");
218             },
219
220             None => {
221             }
222         }
223     }
224
225     fn run_ls(&mut self, logger: &mut Logger) {
226         logger.list_recordings(self.tx_buf);
227     }
228
229     fn run_get(&mut self, logger: &mut Logger, args_iter: &mut ArgumentIter) {
230         if let Some(recording_id_s) = args_iter.next() {
231             if let Some(recording_id) = atoi16(recording_id_s) {
232                 if logger.has_recording(recording_id) {
233                     self.tx_buf.write(b"waiting for receiver to start...\n");
234                     self.tx_buf.flush();
235
236                     let have_receiver = read_char_delay_ms(5000).map(|c| {
237                         c == b'R'
238                     }).map_or_else(|| {
239                         false
240                     }, |_| {
241                         let result = logger.get_recording(recording_id,
242                                                           self.tx_buf);
243
244                         if result.is_err() {
245                             self.tx_buf.write(b"get: failed to retrieve recording\n");
246                         }
247
248                         true
249                     });
250
251                     if !have_receiver {
252                         self.tx_buf.write(b"no signal from receiver\n");
253                     }
254                 } else {
255                     self.tx_buf.write(b"get: no such recording\n");
256                 }
257             } else {
258                 self.tx_buf.write(b"get: invalid argument\n");
259             }
260         } else {
261             self.tx_buf.write(b"get: missing argument\n");
262         }
263
264         self.tx_buf.flush();
265     }
266
267     fn run_clear_storage(&self, logger: &mut Logger) {
268         logger.storage.clear();
269     }
270
271     fn run_dump_storage(&mut self, logger: &mut Logger) {
272         self.tx_buf.write(b"waiting for receiver to start...\n");
273         self.tx_buf.flush();
274
275         let have_receiver = read_char_delay_ms(5000).map(|c| {
276             c == b'R'
277         }).map_or_else(|| {
278             false
279         }, |_| {
280             let mut yenc = Yencode::new(&mut self.tx_buf);
281
282             yenc.start(b"gps-watch-storage.bin");
283
284             const CHUNK_SIZE: usize = 1024;
285             let num_chunks = logger.storage.size() / CHUNK_SIZE;
286
287             for i in 0..num_chunks {
288                 let mut buf = [0u8; CHUNK_SIZE];
289
290                 logger.storage.read(i * CHUNK_SIZE, &mut buf);
291
292                 yenc.data(&buf);
293             }
294
295             yenc.finish();
296
297             true
298         });
299
300         if !have_receiver {
301             self.tx_buf.write(b"no signal from receiver\n");
302         }
303
304         self.tx_buf.flush();
305     }
306 }