literm is a small terminal for the mbed This library allows mbed users to easily create a terminal for their mbed and simplify developpment. Users can simply add their own commands by using the LITERM_CMD macro. Each command is described by a name a description and a fallback which can get literm context and commands arguments. Please refers to the documentation, examples and don't hesitate to submit bugs, advices or request.
Revision 0:ae9f1dd56e92, committed 2013-10-08
- Comitter:
- garfunkheul
- Date:
- Tue Oct 08 20:14:17 2013 +0000
- Commit message:
- this is the first commit, introducing literm. Literm is a small terminal for the mbed which supports: simple command adding, auto help, small history, ctrl+c and ctrl+d.
Changed in this revision
literm.cpp | Show annotated file Show diff for this revision Revisions of this file |
literm.h | Show annotated file Show diff for this revision Revisions of this file |
diff -r 000000000000 -r ae9f1dd56e92 literm.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/literm.cpp Tue Oct 08 20:14:17 2013 +0000 @@ -0,0 +1,176 @@ +#include "literm.h" +#include "mbed.h" + +void literm::redraw(const char *line) +{ + out->printf("\x1B[1K"); /* Clear line */ + out->printf("\x1B[140D"); /* Move cursor to Left */ + out->printf("%s%s", prompt, line); +} + +int do_help(literm *term, char *args) +{ + struct literm_cmd *pcmd = term->get_cmds(); + Serial *out = term->get_out(); + + while (pcmd->prev != NULL) { + out->printf("\r\n%s - %s", pcmd->name, pcmd->desc); + pcmd = pcmd->prev; + } + + return 0; +} + +literm::literm(Serial *_out) +{ + out = _out; + + /* Register default config */ + sprintf(prompt, "mbed > "); + sprintf(welcome, "\r\n\r\nHello from literm library"); + + /* Register Help command which is the first one */ + sprintf(help_cmd.name, "help"); + help_cmd.prev = NULL; + help_cmd.func = do_help; + cmd_list = &help_cmd; +} + +void literm::loop() +{ + int hist_begin = -1; /* -1 = empty hist */ + int hist_end = 0; + int hist_cur; + int hist_full = 0; + char hist[HIST_SIZE][MAX_CMDLINE_SIZE]; + + char cmdline[MAX_CMDLINE_SIZE]; + int max_cmdline, i; + struct literm_cmd *pcmd; + + out->printf(welcome); + + while (1) { +begin: + pcmd = cmd_list; + max_cmdline = 0; + hist_cur = -1; /* -1 = no hist used */ + + out->printf("\r\n%s",prompt); + do { + if (out->readable()) { + cmdline[max_cmdline] = out->getc(); + + if (cmdline[max_cmdline] == 3) + goto begin; + if (cmdline[max_cmdline] == 4) + goto exit; + if (cmdline[max_cmdline] == 8 || cmdline[max_cmdline] == 127) { + if (max_cmdline > 0) { + max_cmdline--; + cmdline[max_cmdline] = '\0'; + redraw(cmdline); + } + } else if (cmdline[max_cmdline] == 27) { + out->getc(); /* trash useless value */ + char tmp = out->getc(); + if (tmp == 65) { /* up */ + if (hist_cur != hist_end && hist_begin != -1) { + if (hist_cur == -1) + hist_cur = hist_begin; + else + hist_cur--; + if (hist_cur < 0) + hist_cur = HIST_SIZE - 1; + redraw(hist[hist_cur]); + } + } else if (tmp == 66) { /* down */ + if (hist_cur != hist_begin) { + hist_cur++; + if(hist_cur >= HIST_SIZE) + hist_cur = 0; + redraw(hist[hist_cur]); + } + } + } else + out->putc(cmdline[max_cmdline++]); + } + } while (cmdline[max_cmdline - 1] != 13); + + /* use a command from history */ + if (hist_cur != -1) { + strncpy(cmdline, hist[hist_cur], MAX_CMDLINE_SIZE - 1); + max_cmdline = strlen(cmdline); + if (hist_cur == hist_begin) + goto proceed; + + /* use a new command */ + } else { + /* deal with empty lines */ + if (max_cmdline < 2 && hist_cur == -1) + goto begin; + + cmdline[max_cmdline - 1] = '\0'; + } + + /* Update History */ + hist_begin++; + if (hist_begin >= HIST_SIZE) { + hist_begin = 0; + hist_full = 1; + } + if (hist_full) + hist_end++; + if (hist_end >= HIST_SIZE) + hist_end = 0; + strncpy(hist[hist_begin], cmdline, MAX_CMDLINE_SIZE - 1); + +proceed: + for (i = 0; i < max_cmdline; ++i) { + if (cmdline[i] == ' ') { + cmdline[i] = '\0'; + break; + } + if (cmdline[i] == '\0') { + cmdline[i + 1] = '\0'; + break; + } + } + + while (strcmp(pcmd->name, cmdline) && pcmd != NULL) + pcmd = pcmd->prev; + + if (pcmd != NULL) + (*pcmd->func)(this, cmdline + i + 1); + else + out->printf("\r\n%s not found", cmdline); + } +exit: + out->printf("\r\nExiting small cli\r\n"); +} + +literm_cmd* literm::get_cmds() +{ + return cmd_list; +} + +Serial* literm::get_out() +{ + return out; +} + +void literm::set_hostname(char *name) +{ + snprintf(prompt, sizeof(prompt) - 1, "%s > ", name); +} + +void literm::set_welcome(char *message) +{ + snprintf(welcome, sizeof(welcome) - 1, "\r\n\r\n%s", message); +} + +void literm::add_cmd(literm_cmd *_cmd) +{ + _cmd->prev = cmd_list; + cmd_list = _cmd; +}
diff -r 000000000000 -r ae9f1dd56e92 literm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/literm.h Tue Oct 08 20:14:17 2013 +0000 @@ -0,0 +1,134 @@ +#ifndef _LITERM_H_ +#define _LITERM_H_ + +#include "mbed.h" + +#define HIST_SIZE 5 +#define MAX_CMDLINE_SIZE 128 + +class literm; + +struct literm_cmd { + char name[8]; + char desc[64]; + int (*func)(literm *, char *); + + literm_cmd *prev; +}; + +#define LITERM_CMD(_name, _desc) \ + struct literm_cmd _name##_cmd = { \ + .name = #_name, \ + .desc = _desc, \ + .func = do_##_name, }; + +/** Small terminal class. + * This class intends to provide a small terminal for the mbed. It supports the following features: + * - Add command based on a name, a description and a callback + * - Auto help command based on name and description declarations. + * - Ctrl-C to clear written command. + * - Delete last char with backspace (if VT100 supported). + * - Exit the CLI with ctrl-D. + * - Small History (with up and down arrows) + * + * Example: + * @code + * #include "mbed.h" + * #include "literm.h" + * + * int do_get(literm *term, char * args) + * { + * Serial *out = term->get_out(); + * + * out->printf("\r\nGetting!"); + * + * return 0; + * } + * + * int do_set(literm *term, char * args) + * { + * Serial *out = term->get_out(); + * + * out->printf("\r\nSetting!"); + * + * return 0; + * } + * + * LITERM_CMD(get, "getting something"); + * LITERM_CMD(set, "setting something"); + * + * int main() + * { + * Serial pc(USBTX, USBRX); + * literm term(&pc); + * + * pc.baud(115200); + * pc.format(8, Serial::None, 1); + * + * term.set_hostname("mbed cli 1"); + * term.set_welcome("Bonjour et bienvenue!"); + * + * term.add_cmd(&get_cmd); + * term.add_cmd(&set_cmd); + * term.loop(); + * + * term.set_hostname("mbed cli 2"); + * term.loop(); + * } + * @endcode + */ +class literm +{ + + public: + + /** Create a literm instance. + * @param _out serial output used to print and received + */ + literm(Serial * _out); + + /** Versatile loop which start and manage the cli. + */ + void loop(); + + /** Set the hostname used by the cli. + * + * @param name hostname to use, must be < 12 chars + */ + void set_hostname(char *name); + + /** Set the welcome message used by the cli. + * + * @param message welcome message to use, must be < 126 chars + */ + void set_welcome(char *message); + + /** Add a command to the smal command line. + * + * @param _cmd pointer to a command to add. Commands are created with LITERM_CMD macro + */ + void add_cmd(literm_cmd *_cmd); + + /** Return a pointer to the command stack. + * + * @returns stack command pointer + */ + literm_cmd* get_cmds(); + + /** Return a pointer to the Serial used as input/output for cli. + * + * @returns serial input/ouput + */ + Serial * get_out(); + + private: + Serial *out; + char prompt[16]; + char welcome[128]; + + void redraw(const char* line); + literm_cmd *cmd_list; + literm_cmd help_cmd; +}; + +#endif