![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
Simple Menu for user config at startup etc
userMenu.cpp
- Committer:
- skyscraper
- Date:
- 2020-03-29
- Revision:
- 0:694155408133
File content as of revision 0:694155408133:
// /// Simple commandline menu system. /// Originally from https://code.google.com/archive/p/ardu-imu/downloads #include <ctype.h> #include <string.h> #include <iostream> #include "userMenu.h" // statics char Menu::_inbuf[commandlineLenMax]; Menu::arg Menu::_argv[argsLenMax+ 1]; // constructor Menu::Menu(const char*prompt, const Menu::command *commands, uint8_t entries, preprompt ppfunc) : _prompt(prompt), _commands(commands), _entries(entries), _ppfunc(ppfunc) { } namespace { // could be a eeprom option // If terminal at PC end already echoes then don't echo here static constexpr bool serial_echo = false; } // run the menu void Menu::run(void) { for (;;) { if(_ppfunc != nullptr){ // run the included pre-prompt function if ( !_ppfunc()){ return; } } // Serial.print(_prompt); // Serial.print(']'); std::cout << _prompt << ']'; for (uint8_t len = 0,done = false; done == false;) { // int c = -1;// = Serial.read(); int c = std::cin.get(); switch(c) { case -1: //eof break; case '\r': // carriage return -> process command _inbuf[len] = '\0'; if ( serial_echo){ // Serial.write('\r'); // Serial.write('\n'); std::cout << '\r'; } std::cout << '\n'; done = true; break; case '\b': // backspace if (len > 0) { len--; if ( serial_echo){ // Serial.write('\b'); std::cout << '\b'; } // Serial.write(' '); // Serial.write('\b'); std::cout << " \b"; } break; default: // printable character if (isprint(c) && (len < (commandlineLenMax - 1))) { _inbuf[len++] = c; if(serial_echo){ // Serial.write((char)c); std::cout << static_cast<char>(c); } } break; } } // split the input line into tokens uint8_t argc = 0; char *s = nullptr; _argv[argc++].str = strtok_r(_inbuf, " ", &s); // XXX should an empty line by itself back out of the current menu? for ( ;argc <= argsLenMax; ++argc) { _argv[argc].str = strtok_r(nullptr, " ", &s); if (_argv[argc].str != nullptr){ _argv[argc].i = atol(_argv[argc].str); _argv[argc].f = atof(_argv[argc].str); // calls strtod, > 700B ! }else{ break; } } if (_argv[0].str == nullptr) { continue; } // 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 for(int i = argc; i <= argsLenMax;++i){ _argv[i].str = nullptr; _argv[i].i = 0; _argv[i].f = 0; } bool cmd_found = false; // look for a command matching the first word (note that it may be empty) int i = 0; for (; i < _entries; ++i) { if (!strcmp(_argv[0].str, _commands[i].command)) { int8_t ret = _call(i, argc); cmd_found=true; if (-2 == ret){ return; } break; } } // implicit commands if (i == _entries) { if (!strcmp(_argv[0].str, "?") || (!strcmp(_argv[0].str, "help"))) { _help(); cmd_found=true; } else if (!strcmp(_argv[0].str, "exit")) { return; } } if (cmd_found == false){ //Serial.println("Invalid command, type 'help'"); std::cout << "Invalid command, type 'help'\n" ; } } } // display the list of commands in response to the 'help' command void Menu::_help(void) { // Serial.println("Commands:"); std::cout << "Commands:\n"; for (int i = 0; i < _entries; i++){ std::cout << " " << _commands[i].command << '\n'; // Serial.print(" "); // Serial.print(_commands[i].command); // Serial.print('\n'); } } // run the n'th command in the menu int8_t Menu::_call(uint8_t n, uint8_t argc) { return _commands[n].func(argc, &_argv[0]); }