2024-07-19 23:49:15 +02:00

309 lines
5.7 KiB
C

#include "CLI.h"
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
void CLI_charOut_save(CLI_t* cli, char ch)
{
if (cli->CLI_charOut != NULL)
{
// create string of size one to be compatable with string print function
char c[2] = {ch, 0};
(*(cli->CLI_charOut))(&c[0]);
}
}
void CLI_stringOut(CLI_t* cli, char* str)
{
for (; *str != 0; str++)
{
CLI_charOut_save(cli, *str);
}
}
// initilize and register the lineout print function
CLI_t CLI_init(CLI_charOutFn lineOut, CMDList_t* cmdList)
{
CLI_t cli;
cli.CLI_charOut = lineOut;
cli.CMDList = cmdList;
cli.CLI_State = CLI_State_Default;
cli.FIFO = FIFOBuffChar_create();
#ifdef HISTORY
cli.History = History_init();
#endif
if (cli.CLI_charOut != NULL)
{
CLI_stringOut(&cli, (char*)"> ");
}
return cli;
}
bool CLI_deinit(CLI_t* cli)
{
cli->CLI_charOut = NULL;
cli->CMDList = NULL;
FIFOBuffChar_delete(cli->FIFO);
#ifdef HISTORY
History_deinit(cli->History, true);
#endif
return true;
}
char* fifoToString(FIFOBuffChar_t* fifo)
{
char* out = malloc(fifo->size + 1);
char* write_p = out;
int i;
for (i = fifo->size; i > 0; i--)
{
FIFOBuffChar_get(fifo, write_p);
write_p++;
}
*write_p = 0;
return out;
}
#ifdef HISTORY
void CLI_PrintHistory(CLI_t* cli)
{
char** historyList;
int ret = History_getFullHistory(cli->History, &historyList);
int i;
char str[150];
if (ret >= 0)
{
for (i=0; *(historyList + i) != NULL; i++)
{
snprintf(&str[0], 150, "%03i: %s\n", i, *(historyList + i));
CLI_stringOut(cli, &str[0]);
}
}
else
{
snprintf(&str[0], 150, "ERROR: get history returnd: %i\n", ret);
CLI_stringOut(cli, &str[0]);
}
free(historyList);
}
void historyPrevius(CLI_t* cli)
{
char* line;
int i;
if (
(History_getCurrPos(cli->History) == History_getSize(cli->History))
&& (FIFOBuffChar_getSize(cli->FIFO) > 0)
)
{
// add current text to end of history
line = fifoToString(cli->FIFO);
History_put(cli->History, line);
for (i=0; *(line + i) != '\0'; i++)
{
CLI_stringOut(cli, (char*)"\x1b[D \x1b[D");
}
History_getPrev(cli->History, &line);
}
// empty current line
while (FIFOBuffChar_getSize(cli->FIFO) > 0)
{
CLI_stringOut(cli, (char*)"\x1b[D \x1b[D");
FIFOBuffChar_pop(cli->FIFO);
}
// get previus command
int ret = History_getPrev(cli->History, &line);
// write line
if ((ret >= 0) && (line != NULL))
{
int i;
for (i=0; *(line + i) != '\0'; i++)
{
CLI_charIn(cli, *(line + i));
}
}
}
void historyNext(CLI_t* cli)
{
char* line;
// empty current line
while (FIFOBuffChar_getSize(cli->FIFO) > 0)
{
CLI_stringOut(cli, (char*)"\x1b[D \x1b[D");
FIFOBuffChar_pop(cli->FIFO);
}
// get next command
int ret = History_getNext(cli->History, &line);
// write line
if ((ret >= 0) && (line != NULL))
{
int i;
for (i=0; *(line + i) != '\0'; i++)
{
CLI_charIn(cli, *(line + i));
}
}
}
#endif
int tryExecute(CLI_t* cli, FIFOBuffChar_t* fifo)
{
int ret = 0;
CMD_t* cmd = NULL;
char* line = fifoToString(fifo);
if (*line == '\0')
{ // empty line
ret = 1;
CLI_stringOut(cli, (char*)"> ");
}
else
{
cmd = CMDList_get(cli->CMDList, line);
}
if (cmd != NULL)
{
#ifdef HISTORY
History_put(cli->History, line);
#endif
int ret = (*(cmd->fn))(line, cli);
if (ret != INT_MIN)
{
CLI_stringOut(cli, (char*)"> ");
}
ret = 0;
}
else if (ret == 0)
{
char err[100];
sprintf(&err[0], "command not found: %s\n> ", line);
CLI_stringOut(cli, &err[0]);
ret = -1;
#ifdef HISTORY
free(line);
#endif
}
#ifndef HISTORY
free(line);
#endif
return ret;
}
// to recive a single caracter
bool CLI_charIn(CLI_t* cli, char c)
{
bool ok = true;
char str[100];
char C = c;
if ((C >= 'a') && (C <= 'z'))
{
C &= (~0x20); // convert to uppercase
}
switch (cli->CLI_State)
{
case CLI_State_Default:
if ((C >= ' ') && (C <= '~')) // see ascii table
{
FIFOBuffChar_put(cli->FIFO, C); // save char in buffer
CLI_charOut_save(cli, c);
}
else
{
switch (c)
{
case '\n':
case '\r':
// echo to terminal
CLI_charOut_save(cli, c);
// copy buffer and create new buffer
FIFOBuffChar_t* fifo = cli->FIFO;
cli->FIFO = FIFOBuffChar_create();
// execute command if valid
ok = (tryExecute(cli, fifo) == 0);
// delete old buffer
FIFOBuffChar_delete(fifo);
break;
case 8:
case 127: // delete (backspace)
if (FIFOBuffChar_pop(cli->FIFO))
{ // pop something of the buffer
CLI_stringOut(cli, (char*)"\x1b[D \x1b[D"); // "<left arrow><space><left arrow>"
}
break;
case 27: // escape (start for arrow keys)
cli->CLI_State = CLI_State_Esc;
break;
default:
sprintf(&str[0], "\ninvlid char: '%c' - (%i)\n", c, c);
CLI_stringOut(cli, &str[0]);
break;
}
}
break;
case CLI_State_Esc:
if (c == '[')
{
cli->CLI_State = CLI_State_ANSIVT100;
}
else
{
cli->CLI_State = CLI_State_Default;
}
break;
case CLI_State_ANSIVT100:
switch (c)
{
#ifdef HISTORY
case 'A': // arrow up
cli->CLI_State = CLI_State_Default;
historyPrevius(cli);
break;
case 'B': // arrow down
cli->CLI_State = CLI_State_Default;
historyNext(cli);
break;
case 'C': // arrow right
CLI_stringOut(cli, (char*)"(key: arrow right)");
cli->CLI_State = CLI_State_Default;
break;
case 'D': // arrow left
CLI_stringOut(cli, (char*)"(key: arrow left)");
cli->CLI_State = CLI_State_Default;
break;
#endif
default:
// only to back on on alpha char. seems to be the with all '\x1d[' commands
if ((C >= 'A') && (C <= 'Z'))
{
cli->CLI_State = CLI_State_Default;
}
}
break;
}
return ok;
}