Skip to content

Instantly share code, notes, and snippets.

@dagon666
Last active January 1, 2016 20:49
Show Gist options
  • Save dagon666/8199158 to your computer and use it in GitHub Desktop.
Save dagon666/8199158 to your computer and use it in GitHub Desktop.
cli offline
#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;
}
/* ================================================================================ */
#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 */
#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);
}
}
#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__ */
#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