Last active
January 1, 2016 20:49
-
-
Save dagon666/8199158 to your computer and use it in GitHub Desktop.
cli offline
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "cli.h" | |
#include <string.h> | |
#include <stdio.h> | |
#define POSINC(__x) (((__x) < (CLI_CMD_BUFFER_SIZE - 1)) ? (__x + 1) : (__x)) | |
#define POSDEC(__x) ((__x) ? ((__x) - 1) : 0) | |
/* ================================================================================ */ | |
static void _cli_prompt(t_cli_ctx *a_ctx __attribute__((unused)), unsigned char nl); | |
static unsigned char _cli_count_arguments(t_cli_ctx *a_ctx); | |
static unsigned char _cli_interpret_cmd(t_cli_ctx *a_ctx); | |
static void _cli_reinterpret_cmd(t_cli_ctx *a_ctx, unsigned char a_response); | |
static void _cli_history_add(t_cli_ctx *a_ctx); | |
static void _cli_history_up(void* a_ctx); | |
static void _cli_history_down(void* a_ctx); | |
static void _cli_delete(void *a_data); | |
static void _cli_none(void *a_data __attribute__((unused))); | |
/* ================================================================================ */ | |
/** | |
* @brief global definition of multi-code commands | |
*/ | |
static t_multicode_cmd _g_mc_cmds[] = { | |
{ { 0x1b, 0x5b, 0x44 }, 3, _cli_delete }, | |
{ { 0x1b, 0x5b, 0x43 }, 3, _cli_none }, | |
{ { 0x1b, 0x5b, 0x41 }, 3, _cli_history_up }, | |
{ { 0x1b, 0x5b, 0x42 }, 3, _cli_history_down }, | |
// null | |
{ {0x00}, 0, 0x00} | |
}; | |
/* ================================================================================ */ | |
void cli_init(t_cli_ctx *a_ctx, t_cmd *a_cmds) { | |
memset(a_ctx, 0x00, sizeof(t_cli_ctx)); | |
a_ctx->cmds = a_cmds; | |
a_ctx->mcmds = _g_mc_cmds; | |
} | |
void cli_read(t_cli_ctx *a_ctx) { | |
unsigned char i = 0x00; | |
unsigned char res = E_CMD_OK; | |
// if no character available - then exit | |
if (!CLI_IO_INPUT(&i)) return; | |
/// multi-code matching | |
if (a_ctx->mc.pos && (a_ctx->mc.pos < MULTICODE_INPUT_MAX_LEN)) { | |
unsigned char mi = 0x00; | |
a_ctx->mc.buf[a_ctx->mc.pos++] = i; | |
while (a_ctx->mcmds[mi].fh) { | |
if (!memcmp(a_ctx->mcmds[mi].pattern, | |
a_ctx->mc.buf, | |
a_ctx->mcmds[mi].len)) { | |
a_ctx->mcmds[mi].fh((void *)a_ctx); | |
a_ctx->mc.pos = 0; | |
memset(a_ctx->mc.buf, 0x00, MULTICODE_INPUT_MAX_LEN); | |
break; | |
} | |
mi++; | |
} | |
return; | |
} | |
/// char by char matching | |
switch(i) { | |
case KEY_CODE_BACKSPACE: // backspace | |
case KEY_CODE_DELETE: // del | |
_cli_delete((void *)a_ctx); | |
break; | |
case KEY_CODE_ESCAPE: // special characters | |
if (a_ctx->mcmds) { | |
a_ctx->mc.pos = 1; | |
a_ctx->mc.buf[0x00] = i; | |
} | |
break; | |
case KEY_CODE_ENTER: // new line | |
a_ctx->cmd[POSINC(a_ctx->cpos)] = '\0'; | |
CLI_IO_OUTPUT("\r\n", 2); | |
res = _cli_interpret_cmd(a_ctx); | |
_cli_reinterpret_cmd(a_ctx, res); | |
a_ctx->cpos = 0; | |
memset(a_ctx->cmd, 0x00, CLI_CMD_BUFFER_SIZE); | |
_cli_prompt(a_ctx, 1); | |
break; | |
default: | |
/* echo */ | |
if (a_ctx->cpos < (CLI_CMD_BUFFER_SIZE - 1) && isprint(i)) { | |
a_ctx->cmd[a_ctx->cpos++] = i; | |
CLI_IO_OUTPUT(&i, 1); | |
} | |
break; | |
} | |
} | |
/* ================================================================================ */ | |
static void _cli_prompt(t_cli_ctx *a_ctx __attribute__((unused)), unsigned char nl) { | |
if (nl) CLI_IO_OUTPUT("\r\n", 2); | |
CLI_IO_OUTPUT("#> ", 3); | |
} | |
static unsigned char _cli_count_arguments(t_cli_ctx *a_ctx) { | |
char *cur = a_ctx->cmd; | |
unsigned char cnt = 0; | |
for(;;) { | |
while (*cur == ' ') cur++; | |
if (*cur == '\0') break; | |
cnt++; | |
while (*cur != '\0' && *cur != ' ') { | |
cur++; | |
} | |
} | |
return cnt; | |
} | |
static unsigned char _cli_interpret_cmd(t_cli_ctx *a_ctx) { | |
unsigned char i = 0; | |
unsigned char ret = E_CMD_OK; | |
if (!strlen(a_ctx->cmd)) { | |
return E_CMD_EMPTY; | |
} | |
if (strlen(a_ctx->cmd) < 2) { | |
return E_CMD_TOO_SHORT; | |
} | |
while (a_ctx->cmds[i].fh) { | |
if (!strncmp(a_ctx->cmds[i].cmd, a_ctx->cmd, strlen(a_ctx->cmds[i].cmd))) { | |
a_ctx->argc = _cli_count_arguments(a_ctx); | |
// call the handler | |
a_ctx->cmds[i].fh((void *)a_ctx); | |
break; | |
} | |
i++; | |
} | |
if (!a_ctx->cmds[i].fh) { | |
ret = E_CMD_NOT_FOUND; | |
} | |
return ret; | |
} | |
static void _cli_reinterpret_cmd(t_cli_ctx *a_ctx, unsigned char a_response) { | |
switch(a_response) { | |
case E_CMD_NOT_FOUND: { | |
char str[] = "\r\nCommand not found"; | |
CLI_IO_OUTPUT(str, strlen(str)); | |
_cli_history_add(a_ctx); | |
} | |
break; | |
case E_CMD_TOO_SHORT: { | |
char str[] = "\r\nCommand too short"; | |
CLI_IO_OUTPUT(str, strlen(str)); | |
} | |
break; | |
default: | |
_cli_history_add(a_ctx); | |
break; | |
} // switch | |
} | |
static void _cli_history_add(t_cli_ctx *a_ctx) { | |
if (a_ctx->hhead == CLI_CMD_HISTORY_LEN && | |
a_ctx->htail == CLI_CMD_HISTORY_LEN) { | |
a_ctx->hhead = 0; | |
a_ctx->htail = 0; | |
} | |
else { | |
a_ctx->hhead++; | |
a_ctx->hhead %= CLI_CMD_HISTORY_LEN; | |
a_ctx->hpos = a_ctx->hhead; | |
if (a_ctx->htail >= a_ctx->hhead) { | |
a_ctx->htail = (a_ctx->hhead + 1) % CLI_CMD_HISTORY_LEN; | |
} | |
} | |
memset(a_ctx->history[a_ctx->hhead], 0x00, CLI_CMD_BUFFER_SIZE); | |
strcpy(a_ctx->history[a_ctx->hhead], a_ctx->cmd); | |
} | |
static void _cli_history_up(void* a_ctx) { | |
t_cli_ctx *ctx = (t_cli_ctx *)a_ctx; | |
char prompt[12] = {0x00}; | |
if ((ctx->hhead != ctx->htail) && | |
strlen(ctx->history[ctx->hpos])) { | |
memset(ctx->cmd, 0x00, CLI_CMD_BUFFER_SIZE); | |
strcpy(ctx->cmd, ctx->history[ctx->hpos]); | |
ctx->cpos = strlen(ctx->cmd); | |
if (!ctx->hpos) | |
ctx->hpos = CLI_CMD_HISTORY_LEN - 1; | |
else | |
ctx->hpos--; | |
snprintf(prompt, sizeof(prompt), "\r\n(%d/%d)", | |
ctx->hpos, CLI_CMD_HISTORY_LEN); | |
CLI_IO_OUTPUT(prompt, strlen(prompt)); | |
CLI_IO_OUTPUT(ctx->cmd, strlen(ctx->cmd)); | |
} | |
} | |
static void _cli_history_down(void* a_ctx) { | |
t_cli_ctx *ctx = (t_cli_ctx *)a_ctx; | |
char prompt[12] = {0x00}; | |
if ((ctx->hhead != ctx->htail) && | |
strlen(ctx->history[ctx->hpos])) { | |
memset(ctx->cmd, 0x00, CLI_CMD_BUFFER_SIZE); | |
ctx->hpos++; | |
ctx->hpos %= CLI_CMD_HISTORY_LEN; | |
strcpy(ctx->cmd, ctx->history[ctx->hpos]); | |
ctx->cpos = strlen(ctx->cmd); | |
snprintf(prompt, sizeof(prompt), "\r\n(%d/%d)", | |
ctx->hpos, CLI_CMD_HISTORY_LEN); | |
CLI_IO_OUTPUT(prompt, strlen(prompt)); | |
CLI_IO_OUTPUT(ctx->cmd, strlen(ctx->cmd)); | |
} | |
} | |
static void _cli_delete(void *a_data) { | |
t_cli_ctx *ctx = (t_cli_ctx *)a_data; | |
if (ctx->cpos) { | |
ctx->cmd[ctx->cpos--] = '\0'; | |
CLI_IO_OUTPUT("\b \b", 3); | |
} | |
} | |
static void _cli_none(void *a_data) { | |
return; | |
} | |
/* ================================================================================ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef CLI_H_DK89LYHI | |
#define CLI_H_DK89LYHI | |
/** | |
* @brief IO input routine - change it accordingly to your implementation | |
*/ | |
#define CLI_IO_INPUT(__data) \ | |
linux_getc(__data) | |
/** | |
* @brief IO output routine - change it accordingly to your implementation | |
*/ | |
#define CLI_IO_OUTPUT(__data, __len) \ | |
linux_putc(__data, __len) | |
/** | |
* @brief key code for backspace | |
*/ | |
#define KEY_CODE_BACKSPACE 0x7f | |
/** | |
* @brief key code for delete key | |
*/ | |
#define KEY_CODE_DELETE 0x7e | |
/** | |
* @brief key code for enter key | |
*/ | |
#define KEY_CODE_ENTER 0x0a | |
/** | |
* @brief key code for escape key | |
*/ | |
#define KEY_CODE_ESCAPE 0x1b | |
/** | |
* @brief command line buffer size (defines the maximum command length) | |
*/ | |
#define CLI_CMD_BUFFER_SIZE 16 | |
/** | |
* @brief how many history items to maintain | |
*/ | |
#define CLI_CMD_HISTORY_LEN 8 | |
/** | |
* @brief how long a multi-code sequence can be | |
*/ | |
#define MULTICODE_INPUT_MAX_LEN 5 | |
/** | |
* @brief command interpretation result | |
*/ | |
typedef enum _t_cmd_status { | |
E_CMD_OK = 0, | |
E_CMD_NOT_FOUND, | |
E_CMD_TOO_SHORT, | |
E_CMD_EMPTY | |
} t_cmd_status; | |
/** | |
* @brief CLI command | |
*/ | |
typedef struct _t_cmd { | |
const char *cmd; | |
void (*fh)(void*); | |
} t_cmd; | |
/** | |
* @brief multicode input | |
*/ | |
typedef struct _t_multicode_input { | |
unsigned char buf[MULTICODE_INPUT_MAX_LEN]; | |
unsigned char pos; | |
} t_multicode_input; | |
/** | |
* @brief multicode command | |
*/ | |
typedef struct _t_multicode_cmd { | |
unsigned char pattern[MULTICODE_INPUT_MAX_LEN]; | |
unsigned char len; | |
void (*fh)(void*); | |
} t_multicode_cmd; | |
/** | |
* @brief CLI system context structure | |
*/ | |
typedef struct _t_cli_ctx { | |
// command set | |
t_cmd *cmds; | |
t_multicode_cmd *mcmds; | |
// cmd | |
char cmd[CLI_CMD_BUFFER_SIZE]; | |
unsigned char cpos; | |
unsigned char argc; | |
// history | |
char history[CLI_CMD_HISTORY_LEN][CLI_CMD_BUFFER_SIZE]; | |
unsigned char hpos; | |
unsigned char hhead, htail; | |
// multicode input | |
t_multicode_input mc; | |
} t_cli_ctx; | |
void cli_init(t_cli_ctx *a_ctx, t_cmd *a_cmds); | |
void cli_read(t_cli_ctx *a_ctx); | |
#endif /* end of include guard: CLI_H_DK89LYHI */ | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "linux_io.h" | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <termios.h> | |
void cm_off(void) { | |
struct termios t; | |
tcgetattr(STDIN_FILENO, &t); | |
t.c_lflag &= ~ICANON; | |
t.c_lflag &= ~ECHO; | |
// Apply the new settings | |
tcsetattr(STDIN_FILENO, TCSANOW, &t); | |
} | |
void cm_on(void) { | |
struct termios t; | |
tcgetattr(STDIN_FILENO, &t); | |
t.c_lflag |= ICANON; | |
t.c_lflag |= ECHO; | |
// Apply the new settings | |
tcsetattr(STDIN_FILENO, TCSANOW, &t); | |
} | |
unsigned char linux_getc(unsigned char *a_data) { | |
*a_data = getchar(); | |
return 1; | |
} | |
unsigned char linux_putc(unsigned char *data, unsigned char a_len) { | |
while (a_len--) { | |
fputc(*data++, stdout); | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef __LINUX_IO_H__ | |
#define __LINUX_IO_H__ | |
void cm_off(void); | |
void cm_on(void); | |
unsigned char linux_getc(unsigned char *a_data); | |
unsigned char linux_putc(unsigned char *data, unsigned char a_len); | |
#endif /* __LINUX_IO_H__ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "cli.h" | |
#include <stdio.h> | |
#include <string.h> | |
static void fh_hw(void *a_data); | |
static void fh_hi(void *a_data); | |
static void fh_argc(void *a_data); | |
static t_cmd g_cmds[] = { | |
{ "hw", fh_hw }, | |
{ "hi", fh_hi }, | |
{ "argc", fh_argc }, | |
// null | |
{ 0x00, 0x00 } | |
}; | |
static void fh_hw(void *a_data) { | |
CLI_IO_OUTPUT("hello_world", 11); | |
} | |
static void fh_hi(void *a_data) { | |
CLI_IO_OUTPUT("hi", 2); | |
} | |
static void fh_argc(void *a_data) { | |
char tmp[16] = { 0x00 }; | |
t_cli_ctx *ctx = (t_cli_ctx *)a_data; | |
sprintf(tmp, "argc: %d", ctx->argc); | |
CLI_IO_OUTPUT(tmp, strlen(tmp)); | |
} | |
int main(int argc, char const *argv[]) { | |
t_cli_ctx cli_ctx; | |
cli_init(&cli_ctx, g_cmds); | |
cm_off(); | |
while (1) { | |
cli_read(&cli_ctx); | |
} | |
cm_on(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment