diff --git a/CLI/CLI.c b/CLI/CLI.c index f7a674e..3daced7 100644 --- a/CLI/CLI.c +++ b/CLI/CLI.c @@ -3,13 +3,44 @@ #include #include #include +#include + +// #include #include "../FIFOBuff/FIFOBuffChar.h" +#ifdef HISTORY +#include "../History/history.h" +#endif CLI_charOutFn CLI_charOut; CMDList_t* CMDList; FIFOBuffChar_t* FIFO; -int i; +enum { + CLI_State_Default, + CLI_State_Esc, + CLI_State_ANSIVT100 +} CLI_State = CLI_State_Default; +#ifdef HISTORY +History_t* History; +#endif + +void CLI_charOut_save(char ch) +{ + if (CLI_charOut != NULL) + { + // create string of size one to be compatable with string print function + char c[2] = {ch, 0}; + (*CLI_charOut)(&c[0]); + } +} + +void CLI_stringOut(char* str) +{ + for (; *str != 0; str++) + { + CLI_charOut_save(*str); + } +} // initilize and register the lineout print function bool CLI_init(CLI_charOutFn lineOut, CMDList_t* cmdList) @@ -18,11 +49,13 @@ bool CLI_init(CLI_charOutFn lineOut, CMDList_t* cmdList) CMDList = cmdList; FIFO = FIFOBuffChar_create(); +#ifdef HISTORY + History = History_init(); +#endif if (CLI_charOut != NULL) { - (*CLI_charOut)(">"); - (*CLI_charOut)(" "); + CLI_stringOut((char*)"> "); } return true; @@ -34,6 +67,9 @@ bool CLI_deinit() CMDList = NULL; FIFOBuffChar_delete(FIFO); +#ifdef HISTORY + History_deinit(History); +#endif return true; } @@ -42,6 +78,7 @@ 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); @@ -53,109 +90,238 @@ char* fifoToString(FIFOBuffChar_t* fifo) return out; } +#ifdef HISTORY +void CLI_PrintHistory() +{ + char** historyList; + int ret = History_getFullHistory(History, &historyList); + if (ret >= 0) + { + for (int i=0; *(historyList + i) != NULL; i++) + { + printf("%03i: %s\n", i, *(historyList + i)); + } + } + else + { + printf("ERROR: get history returnd: %i\n", ret); + } + free(historyList); +} + +void historyPrevius() +{ + char* line; + int i; + if ( + (History_getCurrPos(History) == History_getSize(History)) + && (FIFOBuffChar_getSize(FIFO) > 0) + ) + { + // add current text to end of history + line = fifoToString(FIFO); + History_put(History, line); + for (i=0; *(line + i) != '\0'; i++) + { + CLI_stringOut((char*)"\x1b[D \x1b[D"); + } + History_getPrev(History, &line); + } + + // empty current line + while (FIFOBuffChar_getSize(FIFO) > 0) + { + CLI_stringOut((char*)"\x1b[D \x1b[D"); + FIFOBuffChar_pop(FIFO); + } + + // get previus command + int ret = History_getPrev(History, &line); + + // write line + if ((ret >= 0) && (line != NULL)) + { + for (i=0; *(line + i) != '\0'; i++) + { + CLI_charIn(*(line + i)); + } + } +} +void historyNext() +{ + char* line; + // empty current line + while (FIFOBuffChar_getSize(FIFO) > 0) + { + CLI_stringOut((char*)"\x1b[D \x1b[D"); + FIFOBuffChar_pop(FIFO); + } + + // get next command + int ret = History_getNext(History, &line); + + // write line + if ((ret >= 0) && (line != NULL)) + { + int i; + for (i=0; *(line + i) != '\0'; i++) + { + CLI_charIn(*(line + i)); + } + } +} +#endif + int tryExecute(FIFOBuffChar_t* fifo) { + int ret = 0; + CMD_t* cmd = NULL; char* line = fifoToString(fifo); - CMD_t* cmd = CMDList_get(CMDList, line); + if (*line == '\0') + { // empty line + ret = 1; + CLI_stringOut((char*)"> "); + } + else + { + cmd = CMDList_get(CMDList, line); + } if (cmd != NULL) { - (*(cmd->fn))(line); +#ifdef HISTORY + History_put(History, line); +#endif + int ret = (*(cmd->fn))(line); - if (CLI_charOut != NULL) + if (ret != INT_MIN) { - (*CLI_charOut)(">"); - (*CLI_charOut)(" "); + CLI_stringOut((char*)"> "); } - return true; + ret = 0; } - else + else if (ret == 0) { if (CLI_charOut != NULL) { char err[100]; sprintf(&err[0], "command not found: %s\n> ", line); - for (i=0; err[i] != 0; i++) - { - char c[2] = {err[i], 0}; - (*CLI_charOut)(&c[0]); - } + CLI_stringOut(&err[0]); } - return false; + ret = -1; + +#ifdef HISTORY + free(line); +#endif } +#ifndef HISTORY + free(line); +#endif + return ret; } // to recive a single caracter bool CLI_charIn(char c) { bool ok = true; - if ((c >= 'a') && (c <= 'z')) + char str[100]; + char C = c; + if ((C >= 'a') && (C <= 'z')) { - c &= (~0x20); // convert to uppercase + C &= (~0x20); // convert to uppercase } - if ( //TODO: update list of accepted characters - ((c >= 'A') && (c <= 'Z')) - || ((c >= '0') && (c <= '9')) - || (c == ' ') - || (c == '-') - || (c == '_') - ) + switch (CLI_State) { - FIFOBuffChar_put(FIFO, c); // save char in buffer - if (CLI_charOut != NULL) - { // echo to terminal - (*CLI_charOut)(&c); - } - } - else - { - char str[100]; - switch (c) - { - case '\n': - case '\r': - if (CLI_charOut != NULL) - { // echo to terminal - (*CLI_charOut)(&c); - } - FIFOBuffChar_t* fifo = FIFO; - FIFO = FIFOBuffChar_create(); - ok = tryExecute(fifo); - FIFOBuffChar_delete(fifo); - break; - - case 127: // backspace - if (FIFOBuffChar_pop(FIFO)) + case CLI_State_Default: + if ( //TODO: update list of accepted characters + ((C >= 'A') && (C <= 'Z')) + || ((c >= '0') && (c <= '9')) + || (c == ' ') + || (c == '-') + || (c == '_') + ) + { + FIFOBuffChar_put(FIFO, C); // save char in buffer + CLI_charOut_save(c); + } + else + { + switch (c) { - (*CLI_charOut)("\x1b"); - (*CLI_charOut)("["); - (*CLI_charOut)("D"); - (*CLI_charOut)(" "); - (*CLI_charOut)("\x1b"); - (*CLI_charOut)("["); - (*CLI_charOut)("D"); - } - break; + case '\n': + case '\r': + if (CLI_charOut != NULL) + { // echo to terminal + char ch[2] = {c, 0}; + (*CLI_charOut)(&ch[0]); + } + FIFOBuffChar_t* fifo = FIFO; + FIFO = FIFOBuffChar_create(); + ok = (tryExecute(fifo) == 0); + FIFOBuffChar_delete(fifo); + break; + + case 127: // backspace + if (FIFOBuffChar_pop(FIFO)) + { // pop something of the buffer + CLI_stringOut((char*)"\x1b[D \x1b[D"); // "" + } + break; - case 27: // escape (start for arrow keys) - sprintf(&str[0], "\ninvlid char: ESC - (%i)\n", c); - for (i=0; str[i] != 0; i++) - { - char ch[2] = {str[i], 0}; - (*CLI_charOut)(&ch[0]); - } - break; + case 27: // escape (start for arrow keys) + CLI_State = CLI_State_Esc; + break; - default: - sprintf(&str[0], "\ninvlid char: '%c' - (%i)\n", c, c); - for (i=0; str[i] != 0; i++) - { - char ch[2] = {str[i], 0}; - (*CLI_charOut)(&ch[0]); + default: + sprintf(&str[0], "\ninvlid char: '%c' - (%i)\n", c, c); + CLI_stringOut(&str[0]); + break; } - break; - } + } + break; + + case CLI_State_Esc: + if (c == '[') + { + CLI_State = CLI_State_ANSIVT100; + } + else + { + CLI_State = CLI_State_Default; + } + break; + + case CLI_State_ANSIVT100: + switch (c) + { +#ifdef HISTORY + case 'A': // arrow up + CLI_State = CLI_State_Default; + historyPrevius(); + break; + case 'B': // arrow down + CLI_State = CLI_State_Default; + historyNext(); + break; + case 'C': // arrow right + CLI_stringOut((char*)"(key: arrow right)"); + CLI_State = CLI_State_Default; + break; + case 'D': // arrow left + CLI_stringOut((char*)"(key: arrow left)"); + 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_State = CLI_State_Default; + } + } + break; + } return ok; diff --git a/CLI/CLI.h b/CLI/CLI.h index 32af73a..e09b5e9 100644 --- a/CLI/CLI.h +++ b/CLI/CLI.h @@ -11,6 +11,10 @@ typedef int (*CLI_charOutFn)(const char* line); bool CLI_init(CLI_charOutFn lineOut, CMDList_t* cmdList); bool CLI_deinit(); +#ifdef HISTORY +extern void CLI_PrintHistory(); +#endif + // to recive a single caracter bool CLI_charIn(char c); diff --git a/CMDList/CMDList.c b/CMDList/CMDList.c index 8c64ef0..d785fd3 100644 --- a/CMDList/CMDList.c +++ b/CMDList/CMDList.c @@ -5,7 +5,6 @@ #include #include -int i; // initilises a CMDList_t with all NULL pointers CMDList_t* CMDList_init() { @@ -19,20 +18,17 @@ CMDList_t* CMDList_init() int CMDList_deinit(CMDList_t *list) { CMDList_t** list_p = (CMDList_t**)list; - // printf("deinit %p\n", (void*)list); - // printf("deinit e %p\n", list->e); + int i; for (i = 0; i < 26; i++) { if (*(list_p + i) != NULL) { - // printf("deinit %i\n", i); CMDList_deinit(*(list_p + i)); } } - //TODO: fix "free(): invalid pointer" - free(list); + free(*list_p); return 0; } diff --git a/CMDList/CMDList.h b/CMDList/CMDList.h index e6d3bff..215ae41 100644 --- a/CMDList/CMDList.h +++ b/CMDList/CMDList.h @@ -3,7 +3,7 @@ typedef struct CMD_t { char* cmd; - void (*fn)(char* line); + int (*fn)(char* line); } CMD_t; typedef struct CMDList_s { diff --git a/CMakeLists.txt b/CMakeLists.txt index 720145f..ec5cf73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,13 @@ add_library(FIFOBuffChar FIFOBuff/FIFOBuffChar.c) add_library(CMDList CMDList/CMDList.c) add_library(CMDListPrint CMDList/printList.c) +# History +add_library(history History/History.c) + # CLI add_library(CLI CLI/CLI.c) target_link_libraries(CLI CMDList FIFOBuffChar) + +add_library(CLI_History CLI/CLI.c) +target_link_libraries(CLI_History CMDList FIFOBuffChar history) +target_compile_definitions(CLI_History PRIVATE HISTORY) diff --git a/FIFOBuff/FIFOBuffChar.c b/FIFOBuff/FIFOBuffChar.c index fe1fc0c..6801ad6 100644 --- a/FIFOBuff/FIFOBuffChar.c +++ b/FIFOBuff/FIFOBuffChar.c @@ -1,4 +1,4 @@ -#include "C:\Users\denbo\git\ems31_2023-2024_groep_09_ccs\ ems31_2023-2024_groep_09_submodules_2\FIFOBuff\FIFOBuffChar.h" +#include "FIFOBuffChar.h" #include #include diff --git a/History/History.c b/History/History.c new file mode 100644 index 0000000..74bdca0 --- /dev/null +++ b/History/History.c @@ -0,0 +1,204 @@ +#include "history.h" + +#include +#include +#include + +History_t* History_init() +{ + History_t* history = malloc(sizeof(History_t)); + + if (history != NULL) { + history->FirstEl_p = NULL; + history->LastEl_p = NULL; + history->CurrEl_p = NULL; + history->size = 0; + } + + return history; +} + + +int History_deinit(History_t* history) +{ + // Check if the buffer there is something in the buffer + if (history->FirstEl_p == NULL) + { + // Delete all elements in the buffer + History_element_t* el = history->FirstEl_p; + while (el != NULL) + { + History_element_t* nextEl = el->nextEl_p; + free(el); + el = nextEl; + } + } + + free(history); + return 0; +} + +int History_put(History_t *history, char* line) +{ + int retCode; + if ((history->LastEl_p == NULL) && (history->FirstEl_p == NULL)) + { + // buffer is empty. add first element + history->FirstEl_p = malloc(sizeof(History_element_t)); + if (history->FirstEl_p != NULL) + { + history->FirstEl_p->line = line; + history->FirstEl_p->prevEl_p = NULL; + history->FirstEl_p->nextEl_p = NULL; + + history->LastEl_p = history->FirstEl_p; + history->size = 1; + retCode = 0; + } + else + { // failt to allocate memory + retCode = -1; + } + } + else if ((history->LastEl_p != NULL) && (history->FirstEl_p != NULL)) + { + // add element to exsiting buffer + History_element_t* el = malloc(sizeof(History_element_t)); + if (el != NULL) + { + el->line = line; + el->prevEl_p = history->LastEl_p; + el->nextEl_p = NULL; + history->LastEl_p->nextEl_p = el; + history->LastEl_p = el; + history->size++; + retCode = 0; + } + else + { // failt to allocate memory + retCode = -2; + } + } + else + { + retCode = -3; + } + + if (retCode >= 0) + { + // reset current pointer + history->CurrEl_p = NULL; + } + + return retCode; +} + +int History_getPrev(History_t* history, char** line) +{ + int retCode; + if (history->CurrEl_p == NULL) + { + history->CurrEl_p = history->LastEl_p; + } + else if (history->CurrEl_p->prevEl_p != NULL) + { + history->CurrEl_p = history->CurrEl_p->prevEl_p; + } + else + { // no previus line + retCode = -2; + } + + if (history->CurrEl_p != NULL) + { + *line = history->CurrEl_p->line; + retCode = 0; + } + else + { // no items in history? + *line = NULL; + retCode = -1; + } + return 0; +} + +int History_getNext(History_t* history, char** line) +{ + int retCode; + if (history->CurrEl_p != NULL) + { + history->CurrEl_p = history->CurrEl_p->nextEl_p; + } + + if (history->CurrEl_p != NULL) + { + *line = history->CurrEl_p->line; + retCode = 0; + } + else + { + *line = NULL; + retCode = -1; + } + return 0; +} + +int History_getFullHistory(History_t* history, char*** list) +{ + int retCode = 0; + *list = malloc(sizeof(char**) * (History_getSize(history) + 1)); + History_element_t* el = history->FirstEl_p; + + for (int i = 0; (i < History_getSize(history)-1) && (retCode >= 0); i++) + { + *((*list) + i) = el->line; + if (el->nextEl_p == NULL) + { + retCode = -1; + } + else + { + el = (History_element_t*)el->nextEl_p; + } + } + + if (retCode >= 0) + { + // indicate end of list + *((*list) + History_getSize(history)) = NULL; + } + + return retCode; +} + +int History_getCurrPos(History_t* history) +{ + int retCode = 0; + + if (history->CurrEl_p != NULL) + { + History_element_t* el = history->FirstEl_p; + for (int i = 0; (el != history->CurrEl_p) && (retCode >= 0); i++) + { + if (el->nextEl_p == NULL) + { + retCode = 2; + } + else + { + el = (History_element_t*)el->nextEl_p; + } + } + } + else + { + retCode = History_getSize(history); + } + + return retCode; +} + +unsigned int History_getSize(History_t *history) +{ + return history->size; +} diff --git a/History/history.h b/History/history.h new file mode 100644 index 0000000..35bc020 --- /dev/null +++ b/History/history.h @@ -0,0 +1,40 @@ +#ifndef HISTORY_H +#define HISTORY_H + +#include + +// one element in the buffer +typedef struct History_element_s { + void* prevEl_p; + char* line; + void* nextEl_p; +} History_element_t; + +// defines all vars for the buffer to operate +typedef struct History_s { + History_element_t* FirstEl_p; + History_element_t* LastEl_p; + History_element_t* CurrEl_p; + unsigned char size; +} History_t; + +// create a fifo buffer and initilizes it with zeros +extern History_t* History_init(); + +// destroy a fifo buffer (free up its space) +extern int History_deinit(History_t* history); + +// put value i in buffer if there is still memory avaliable +extern int History_put(History_t *history, char* line); + +// get value from buffer and writes it to *line +extern int History_getPrev(History_t* history, char** line); +extern int History_getNext(History_t* history, char** line); + +extern int History_getCurrPos(History_t* history); + +extern int History_getFullHistory(History_t* history, char*** list); + +extern unsigned int History_getSize(History_t *history); + +#endif \ No newline at end of file