Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1702 | - | 1 | // -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*- |
2 | |||
3 | // |
||
4 | // Simple commandline menu system. |
||
5 | // |
||
6 | |||
7 | #include <FastSerial.h> |
||
8 | #include <AP_Common.h> |
||
9 | |||
10 | #include <ctype.h> |
||
11 | #include <string.h> |
||
12 | #include <avr/pgmspace.h> |
||
13 | |||
14 | #include "include/menu.h" |
||
15 | |||
16 | // statics |
||
17 | char Menu::_inbuf[MENU_COMMANDLINE_MAX]; |
||
18 | Menu::arg Menu::_argv[MENU_ARGS_MAX + 1]; |
||
19 | |||
20 | // constructor |
||
21 | Menu::Menu(const prog_char *prompt, const Menu::command *commands, uint8_t entries, preprompt ppfunc) : |
||
22 | _prompt(prompt), |
||
23 | _commands(commands), |
||
24 | _entries(entries), |
||
25 | _ppfunc(ppfunc) |
||
26 | { |
||
27 | } |
||
28 | |||
29 | // run the menu |
||
30 | void |
||
31 | Menu::run(void) |
||
32 | { |
||
33 | int8_t ret; |
||
34 | uint8_t len, i; |
||
35 | uint8_t argc; |
||
36 | int c; |
||
37 | char *s; |
||
38 | |||
39 | // loop performing commands |
||
40 | for (;;) { |
||
41 | |||
42 | // run the pre-prompt function, if one is defined |
||
43 | if ((NULL != _ppfunc) && !_ppfunc()) |
||
44 | return; |
||
45 | |||
46 | // loop reading characters from the input |
||
47 | len = 0; |
||
48 | Serial.printf("%S] ", FPSTR(_prompt)); |
||
49 | for (;;) { |
||
50 | c = Serial.read(); |
||
51 | if (-1 == c) |
||
52 | continue; |
||
53 | // carriage return -> process command |
||
54 | if ('\r' == c) { |
||
55 | _inbuf[len] = '\0'; |
||
56 | Serial.write('\r'); |
||
57 | Serial.write('\n'); |
||
58 | break; |
||
59 | } |
||
60 | // backspace |
||
61 | if ('\b' == c) { |
||
62 | if (len > 0) { |
||
63 | len--; |
||
64 | Serial.write('\b'); |
||
65 | Serial.write(' '); |
||
66 | Serial.write('\b'); |
||
67 | continue; |
||
68 | } |
||
69 | } |
||
70 | // printable character |
||
71 | if (isprint(c) && (len < (MENU_COMMANDLINE_MAX - 1))) { |
||
72 | _inbuf[len++] = c; |
||
73 | Serial.write((char)c); |
||
74 | continue; |
||
75 | } |
||
76 | } |
||
77 | |||
78 | // split the input line into tokens |
||
79 | argc = 0; |
||
80 | _argv[argc++].str = strtok_r(_inbuf, " ", &s); |
||
81 | // XXX should an empty line by itself back out of the current menu? |
||
82 | while (argc <= MENU_ARGS_MAX) { |
||
83 | _argv[argc].str = strtok_r(NULL, " ", &s); |
||
84 | if ('\0' == _argv[argc].str) |
||
85 | break; |
||
86 | _argv[argc].i = atol(_argv[argc].str); |
||
87 | _argv[argc].f = atof(_argv[argc].str); // calls strtod, > 700B ! |
||
88 | argc++; |
||
89 | } |
||
90 | |||
91 | if (_argv[0].str == NULL) { |
||
92 | continue; |
||
93 | } |
||
94 | |||
95 | // populate arguments that have not been specified with "" and 0 |
||
96 | // this is safer than NULL in the case where commands may look |
||
97 | // without testing argc |
||
98 | i = argc; |
||
99 | while (i <= MENU_ARGS_MAX) { |
||
100 | _argv[i].str = ""; |
||
101 | _argv[i].i = 0; |
||
102 | _argv[i].f = 0; |
||
103 | i++; |
||
104 | } |
||
105 | |||
106 | bool cmd_found = false; |
||
107 | // look for a command matching the first word (note that it may be empty) |
||
108 | for (i = 0; i < _entries; i++) { |
||
109 | if (!strcasecmp_P(_argv[0].str, _commands[i].command)) { |
||
110 | ret = _call(i, argc); |
||
111 | cmd_found=true; |
||
112 | if (-2 == ret) |
||
113 | return; |
||
114 | break; |
||
115 | } |
||
116 | } |
||
117 | |||
118 | // implicit commands |
||
119 | if (i == _entries) { |
||
120 | if (!strcmp(_argv[0].str, "?") || (!strcasecmp_P(_argv[0].str, PSTR("help")))) { |
||
121 | _help(); |
||
122 | cmd_found=true; |
||
123 | } else if (!strcasecmp_P(_argv[0].str, PSTR("exit"))) { |
||
124 | return; |
||
125 | } |
||
126 | } |
||
127 | |||
128 | if (cmd_found==false) |
||
129 | { |
||
130 | Serial.println("Invalid command, type 'help'"); |
||
131 | } |
||
132 | |||
133 | } |
||
134 | } |
||
135 | |||
136 | // display the list of commands in response to the 'help' command |
||
137 | void |
||
138 | Menu::_help(void) |
||
139 | { |
||
140 | int i; |
||
141 | |||
142 | Serial.println("Commands:"); |
||
143 | for (i = 0; i < _entries; i++) |
||
144 | Serial.printf(" %S\n", FPSTR(_commands[i].command)); |
||
145 | } |
||
146 | |||
147 | // run the n'th command in the menu |
||
148 | int8_t |
||
149 | Menu::_call(uint8_t n, uint8_t argc) |
||
150 | { |
||
151 | func fn; |
||
152 | |||
153 | fn = (func)pgm_read_pointer(&_commands[n].func); |
||
154 | return(fn(argc, &_argv[0])); |
||
155 | } |