blob: 6fa4a4b8880e9d0dbc5871c25be23cc5b4191820 [file] [log] [blame]
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -07001/*
2 * Copyright (c) 2008 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to 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 NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <app/console.h>
24#include <debug.h>
25#include <string.h>
26#include <ctype.h>
27#include <stdlib.h>
28
29static cmd_block *command_list = NULL;
30
31/* a linear array of statically defined command blocks,
32 defined in the linker script.
33 */
34extern cmd_block __commands_start;
35extern cmd_block __commands_end;
36
37static int cmd_help(int argc, const cmd_args *argv);
38static int cmd_test(int argc, const cmd_args *argv);
39
40STATIC_COMMAND_START
41 { "help", "this list", &cmd_help },
42 { "test", "test the command processor", &cmd_test },
43STATIC_COMMAND_END(help);
44
45int console_init(void)
46{
47 printf("console_init: entry\n");
48
49 /* add all the statically defined commands to the list */
50 cmd_block *block;
51 for (block = &__commands_start; block != &__commands_end; block++) {
52 console_register_commands(block);
53 }
54
55 return 0;
56}
57
58static const cmd *match_command(const char *command)
59{
60 cmd_block *block;
61 size_t i;
62
63 for (block = command_list; block != NULL; block = block->next) {
64 const cmd *curr_cmd = block->list;
65 for (i = 0; i < block->count; i++) {
66 if (strcmp(command, curr_cmd[i].cmd_str) == 0) {
67 return &curr_cmd[i];
68 }
69 }
70 }
71
72 return NULL;
73}
74
75static int read_line(char *buffer, int len)
76{
77 int pos = 0;
78 int escape_level = 0;
79
80 for (;;) {
81 char c;
82
83 /* loop until we get a char */
84 if (dgetc(&c) < 0)
85 continue;
86
87// printf("c = 0x%hhx\n", c);
88
89 if (escape_level == 0) {
90 switch (c) {
91 case '\r':
92 case '\n':
93 dputc(c);
94 goto done;
95
96 case 0x7f: // backspace or delete
97 case 0x8:
98 if (pos > 0) {
99 pos--;
100 dputs("\x1b[1D"); // move to the left one
101 dputc(' ');
102 dputs("\x1b[1D"); // move to the left one
103 }
104 break;
105
106 case 0x1b: // escape
107 escape_level++;
108 break;
109
110 default:
111 buffer[pos++] = c;
112 dputc(c);
113 }
114 } else if (escape_level == 1) {
115 // inside an escape, look for '['
116 if (c == '[') {
117 escape_level++;
118 } else {
119 // we didn't get it, abort
120 escape_level = 0;
121 }
122 } else { // escape_level > 1
123 switch (c) {
124 case 67: // right arrow
125 buffer[pos++] = ' ';
126 dputc(' ');
127 break;
128 case 68: // left arrow
129 if (pos > 0) {
130 pos--;
131 dputs("\x1b[1D"); // move to the left one
132 dputc(' ');
133 dputs("\x1b[1D"); // move to the left one
134 }
135 break;
136 case 65: // up arrow
137 case 66: // down arrow
138 // XXX do history here
139 break;
140 default:
141 break;
142 }
143 escape_level = 0;
144 }
145
146 /* end of line. */
147 if (pos == (len - 1)) {
148 dputs("\nerror: line too long\n");
149 pos = 0;
150 goto done;
151 }
152 }
153
154done:
155// printf("returning pos %d\n", pos);
156
157 buffer[pos] = 0;
158 return pos;
159}
160
161static int tokenize_command(char *buffer, cmd_args *args, int arg_count)
162{
163 int pos;
164 int arg;
165 bool finished;
166 enum {
167 INITIAL = 0,
168 IN_SPACE,
169 IN_TOKEN
170 } state;
171
172 pos = 0;
173 arg = 0;
174 state = INITIAL;
175 finished = false;
176
177 for (;;) {
178 char c = buffer[pos];
179
180 if (c == '\0')
181 finished = true;
182
183// printf("c 0x%hhx state %d arg %d pos %d\n", c, state, arg, pos);
184
185 switch (state) {
186 case INITIAL:
187 if (isspace(c)) {
188 state = IN_SPACE;
189 } else {
190 state = IN_TOKEN;
191 args[arg].str = &buffer[pos];
192 }
193 break;
194 case IN_TOKEN:
195 if (finished) {
196 arg++;
197 goto done;
198 }
199 if (isspace(c)) {
200 arg++;
201 buffer[pos] = 0;
202 /* are we out of tokens? */
203 if (arg == arg_count)
204 goto done;
205 state = IN_SPACE;
206 }
207 pos++;
208 break;
209 case IN_SPACE:
210 if (finished)
211 goto done;
212 if (!isspace(c)) {
213 state = IN_TOKEN;
214 args[arg].str = &buffer[pos];
215 }
216 pos++;
217 break;
218 }
219 }
220
221done:
222 return arg;
223}
224
225static void convert_args(int argc, cmd_args *argv)
226{
227 int i;
228
229 for (i = 0; i < argc; i++) {
230 argv[i].u = atoui(argv[i].str);
231 argv[i].i = atoi(argv[i].str);
232 }
233}
234
235static void console_loop(void)
236{
237 cmd_args args[16];
238 char buffer[256];
239
240 printf("entering main console loop\n");
241
242 for (;;) {
243 dputs("] ");
244
245 int len = read_line(buffer, sizeof(buffer));
246 if (len == 0)
247 continue;
248
249// printf("line = '%s'\n", buffer);
250
251 /* tokenize the line */
252 int argc = tokenize_command(buffer, args, 16);
253 if (argc < 0) {
254 printf("syntax error\n");
255 continue;
256 } else if (argc == 0) {
257 continue;
258 }
259
260// printf("after tokenize: argc %d\n", argc);
261// for (int i = 0; i < argc; i++)
262// printf("%d: '%s'\n", i, args[i].str);
263
264 /* convert the args */
265 convert_args(argc, args);
266
267 /* try to match the command */
268 const cmd *command = match_command(args[0].str);
269 if (!command) {
270 printf("command not found\n");
271 continue;
272 }
273
274 int result = command->cmd_callback(argc, args);
275
276 // XXX do something with the result
277 }
278}
279
280
281void console_start(void)
282{
283
284 console_loop();
285}
286
287int console_run_command(const char *string)
288{
289 const cmd *command;
290
291 ASSERT(string != NULL);
292
293 command = match_command(string);
294 if (!command)
295 return -1;
296
297 int result = command->cmd_callback(0, NULL);
298
299 return result;
300}
301
302void console_register_commands(cmd_block *block)
303{
304 ASSERT(block);
305 ASSERT(block->next == NULL);
306
307 block->next = command_list;
308 command_list = block;
309}
310
311static int cmd_help(int argc, const cmd_args *argv)
312{
313
314 printf("command list:\n");
315
316 cmd_block *block;
317 size_t i;
318
319 for (block = command_list; block != NULL; block = block->next) {
320 const cmd *curr_cmd = block->list;
321 for (i = 0; i < block->count; i++) {
322 printf("\t%-16s: %s\n", curr_cmd[i].cmd_str, curr_cmd[i].help_str ? curr_cmd[i].help_str : "");
323 }
324 }
325
326 return 0;
327}
328
329static int cmd_test(int argc, const cmd_args *argv)
330{
331 int i;
332
333 printf("argc %d, argv %p\n", argc, argv);
334 for (i = 0; i < argc; i++)
335 printf("\t%d: str '%s', i %d, u %#x\n", i, argv[i].str, argv[i].i, argv[i].u);
336
337 return 0;
338}
339