2010-09-24 02:50:02 -03:00
|
|
|
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*-
|
|
|
|
|
|
|
|
//
|
|
|
|
// Simple commandline menu system.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include <FastSerial.h>
|
2010-11-27 19:27:08 -04:00
|
|
|
#include <AP_Common.h>
|
2010-09-24 02:50:02 -03:00
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <avr/pgmspace.h>
|
|
|
|
|
|
|
|
#include "include/menu.h"
|
|
|
|
|
|
|
|
// statics
|
|
|
|
char Menu::_inbuf[MENU_COMMANDLINE_MAX];
|
|
|
|
Menu::arg Menu::_argv[MENU_ARGS_MAX + 1];
|
|
|
|
|
|
|
|
// constructor
|
2010-09-24 04:31:59 -03:00
|
|
|
Menu::Menu(const prog_char *prompt, const Menu::command *commands, uint8_t entries, preprompt ppfunc) :
|
2010-09-24 02:50:02 -03:00
|
|
|
_prompt(prompt),
|
|
|
|
_commands(commands),
|
2010-09-24 04:31:59 -03:00
|
|
|
_entries(entries),
|
|
|
|
_ppfunc(ppfunc)
|
2010-09-24 02:50:02 -03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// run the menu
|
|
|
|
void
|
|
|
|
Menu::run(void)
|
|
|
|
{
|
|
|
|
uint8_t len, i, ret;
|
|
|
|
uint8_t argc;
|
|
|
|
int c;
|
2010-09-25 15:13:08 -03:00
|
|
|
char *s;
|
2010-09-24 02:50:02 -03:00
|
|
|
|
|
|
|
// loop performing commands
|
|
|
|
for (;;) {
|
2010-09-24 03:18:59 -03:00
|
|
|
|
2010-09-24 04:31:59 -03:00
|
|
|
// run the pre-prompt function, if one is defined
|
|
|
|
if ((NULL != _ppfunc) && !_ppfunc())
|
|
|
|
return;
|
|
|
|
|
2010-09-24 02:50:02 -03:00
|
|
|
// loop reading characters from the input
|
|
|
|
len = 0;
|
|
|
|
Serial.printf("%S] ", _prompt);
|
|
|
|
for (;;) {
|
|
|
|
c = Serial.read();
|
|
|
|
if (-1 == c)
|
|
|
|
continue;
|
|
|
|
// carriage return -> process command
|
|
|
|
if ('\r' == c) {
|
|
|
|
_inbuf[len] = '\0';
|
|
|
|
Serial.write('\r');
|
|
|
|
Serial.write('\n');
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// backspace
|
|
|
|
if ('\b' == c) {
|
|
|
|
if (len > 0) {
|
|
|
|
len--;
|
|
|
|
Serial.write('\b');
|
|
|
|
Serial.write(' ');
|
|
|
|
Serial.write('\b');
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// printable character
|
|
|
|
if (isprint(c) && (len < (MENU_COMMANDLINE_MAX - 1))) {
|
|
|
|
_inbuf[len++] = c;
|
|
|
|
Serial.write((char)c);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// split the input line into tokens
|
|
|
|
argc = 0;
|
2010-09-25 15:13:08 -03:00
|
|
|
_argv[argc++].str = strtok_r(_inbuf, " ", &s);
|
2010-09-25 15:02:41 -03:00
|
|
|
// XXX should an empty line by itself back out of the current menu?
|
2010-09-24 02:50:02 -03:00
|
|
|
while (argc <= MENU_ARGS_MAX) {
|
2010-09-25 15:13:08 -03:00
|
|
|
_argv[argc].str = strtok_r(NULL, " ", &s);
|
2010-09-24 02:50:02 -03:00
|
|
|
if ('\0' == _argv[argc].str)
|
|
|
|
break;
|
|
|
|
_argv[argc].i = atol(_argv[argc].str);
|
|
|
|
_argv[argc].f = atof(_argv[argc].str); // calls strtod, > 700B !
|
|
|
|
argc++;
|
|
|
|
}
|
2010-11-01 00:40:54 -03:00
|
|
|
|
|
|
|
// populate arguments that have not been specified with "" and 0
|
|
|
|
// this is safer than NULL in the case where commands may look
|
|
|
|
// without testing argc
|
2010-11-08 21:58:00 -04:00
|
|
|
i = argc;
|
|
|
|
while (i <= MENU_ARGS_MAX) {
|
|
|
|
_argv[i].str = "";
|
|
|
|
_argv[i].i = 0;
|
|
|
|
_argv[i].f = 0;
|
|
|
|
i++;
|
2010-11-01 00:40:54 -03:00
|
|
|
}
|
2010-09-24 02:50:02 -03:00
|
|
|
|
|
|
|
// look for a command matching the first word (note that it may be empty)
|
|
|
|
for (i = 0; i < _entries; i++) {
|
2010-09-25 15:02:41 -03:00
|
|
|
if (!strcasecmp_P(_argv[0].str, _commands[i].command)) {
|
2010-09-24 03:18:59 -03:00
|
|
|
ret = _call(i, argc);
|
2010-09-24 02:50:02 -03:00
|
|
|
if (-2 == ret)
|
|
|
|
return;
|
2010-09-24 04:31:59 -03:00
|
|
|
break;
|
2010-09-24 02:50:02 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-24 04:31:59 -03:00
|
|
|
// implicit commands
|
|
|
|
if (i == _entries) {
|
2010-09-25 15:02:41 -03:00
|
|
|
if (!strcmp(_argv[0].str, "?") || (!strcasecmp_P(_argv[0].str, PSTR("help")))) {
|
2010-09-24 04:31:59 -03:00
|
|
|
_help();
|
2010-09-25 15:02:41 -03:00
|
|
|
} else if (!strcasecmp_P(_argv[0].str, PSTR("exit"))) {
|
2010-09-24 04:31:59 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2010-09-24 02:50:02 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// display the list of commands in response to the 'help' command
|
|
|
|
void
|
|
|
|
Menu::_help(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
Serial.println("Commands:");
|
|
|
|
for (i = 0; i < _entries; i++)
|
|
|
|
Serial.printf(" %S\n", _commands[i].command);
|
|
|
|
}
|
2010-09-24 03:18:59 -03:00
|
|
|
|
|
|
|
// run the n'th command in the menu
|
2010-09-25 15:02:41 -03:00
|
|
|
int8_t
|
2010-09-24 03:18:59 -03:00
|
|
|
Menu::_call(uint8_t n, uint8_t argc)
|
|
|
|
{
|
|
|
|
func fn;
|
|
|
|
|
|
|
|
fn = (func)pgm_read_word(&_commands[n].func);
|
|
|
|
return(fn(argc, &_argv[0]));
|
|
|
|
}
|