//VS-Specific, ignore
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//Function signature definition
typedef int (*cmd_func)(int argc, char* argv[]);
//Some hacky defines
#define strcpy_s(dest, s, src) strcpy(dest, src);
#define memcpy_s(dest, destsize, source, sourcesize) memcpy(dest, source, sourcesize);
typedef struct {
char* name; //Command name; will be checked against
int parc; //param count: How many strings there are in parv
char** parv; //Names of the parameters (for whatever reason we have them here...)
int argc; //argument count: Value to be passed to func
char** argv; //argument vector: Value to be passed to func
cmd_func func; //Actual command function
} cmd_desc;
///<summary>
///Extended free: Frees only if p != NULL
///</summary>
///<returns>true if p was freed, false otherwise</returns>
bool exfree(void* p)
{
if (p != NULL) {
free(p);
return true;
}
return false;
}
///<summary>
///Clears a cmd_desc struct instance by setting all bytes to 0
///</summary>
///<param name="to_clear">pointer to allocated structure that shall be cleared</param>
void clear_cmd_desc(cmd_desc* to_clear)
{
exfree(to_clear->name);
for (int i = 0; i < to_clear->parc; i++) exfree(to_clear->parv[i]); //Free parameter names
for (int i = 0; i < to_clear->argc; i++) exfree(to_clear->argv[i]); //Free arguments
memset(to_clear, 0, sizeof(cmd_desc));
}
///<summary>
///Creates a valid cmd_desc structure with the values provided as parameters.
///</summary>
///<returns>A calloc-created pointer to a valid struct, or NULL if no valid struct could be generated</returns>
cmd_desc* create_cmd_desc(const char* name, int parc, const char** parv, cmd_func func)
{
if (parc < 0) return NULL;
cmd_desc* ptr = (cmd_desc*)calloc(1, sizeof(cmd_desc)); //calloc is very nice here: it automatically sets the memory to 0
if (ptr == NULL) return NULL;
ptr->name = (char*)malloc(strlen(name) + 1);
strcpy_s(ptr->name, strlen(name) + 1, name); //This is non-standard. If errors occur: Remove the 2nd param and change to strcpy().
//Some (illegal) shortcut.
if (parc > 0)
{
ptr->parc = parc;
ptr->parv = (char**)malloc(sizeof(char*)*parc);
//Loop over all arguments to copy, then allocate, then copy
for (int i = 0; i < parc; i++) {
ptr->parv[i] = (char*)malloc(strlen(parv[i]) + 1);
strcpy_s(ptr->parv[i], strlen(parv[i]) + 1, parv[i]); //Non-standard again
}
}
ptr->func = func;
return ptr;
}
///<summary>
///Pushes an input at the end of a cmd_desc structure
///</summary>
bool push_input(cmd_desc* d, const char* input)
{
char** tmp = (char**)realloc(d->argv, sizeof(char*) * (d->argc + 1)); //WARNING: This is really bad. If you fancy, try making this more efficient.
//One way of making the line above more efficient: Double the current size each time we would exceed the current memory limit. This would logarithmically decrease the need of new allocations.
if (tmp == NULL) return false;
else d->argv = tmp;
d->argv[d->argc] = (char*)malloc(strlen(input) + 1);
strcpy_s(d->argv[d->argc], strlen(input) + 1, input);
d->argc++;
return true;
}
///<summary>
///Clears all input from cmd_desc* d.
///</summary>
void clear_input(cmd_desc* d)
{
for (int i = 0; i < d->argc; i++) exfree(d->argv[i]);
exfree(d->argv);
d->argv = NULL;
d->argc = 0;
}
///<summary>
///Adds a cmd_desc to the global array
///</summary>
bool add_cmd_desc(cmd_desc** arr, size_t* index, size_t* arrsize, cmd_desc* desc)
{
//Logarithmic reallocation procedure
if (*index >= *arrsize)
{
cmd_desc* tmp = (cmd_desc*)realloc(*arr, sizeof(cmd_desc) * *arrsize * 2);
if (tmp == NULL) return false;
else {
*arrsize *= 2;
*arr = tmp;
}
}
//Set copy the contents of desc (*desc) to its location in the array
(*arr)[(*index)++] = *desc;
//Free desc as we have copied it already
free(desc);
return true;
}
///<summary>
///Converts all strings in argv to integers and adds them, returning the sum. It prints sum, average and median to stdout.
///</summary>
///<returns>Integer sum of all valid values in argv</returns>
int adder(int argc, char* argv[])
{
if (argc <= 0)
{
printf("Invalid parameters\n");
return 0;
}
int sum = 0;
for (int i = 0; i < argc; i++)
{
sum += atoi(argv[i]);
}
printf("Sum is %d, Average is %d and median is %d\n", sum, sum / argc, atoi(argv[argc / 2]));
return sum;
}
int main(int argc, char* argv[])
{
cmd_desc* cmd_desc_arr = NULL;
size_t arr_ind = 0;
size_t arr_size = 4;
cmd_desc_arr = (cmd_desc*)calloc(arr_size, sizeof(cmd_desc));
if (cmd_desc_arr == NULL) return -1;
size_t bufstep = 256;
size_t bufsize = 256;
char* buf = (char*)calloc(bufsize, sizeof(char)); //Input buffer
char* bufbase = buf;
if (buf == NULL)
{
free(cmd_desc_arr); //Free some sad things
return -1;
}
//Add our little example function. We shortcut the descriptions 'cause I have no clue what they are for anyways.
cmd_desc* d = create_cmd_desc("adder", 0, NULL, &adder);
if (d == NULL) printf("Uh. Something really bad happened. And the programmer was too lazy to clean up... Take cover.\n");
add_cmd_desc(&cmd_desc_arr, &arr_ind, &arr_size, d);
printf("This is a little command line interface (cli) demo.\nEnter 'quit' to quit.\n");
while (scanf("%255s", bufbase) > 0) //Scanf string, 255 characters at max.
{
if (strcmp(bufbase, "quit") == 0) break;
for (size_t i = 0; i < arr_ind; i++)
{
cmd_desc* d = &cmd_desc_arr[i];
if (d->name == NULL) continue;
if (strcmp(d->name, buf) == 0)
{
//Function found, now we can read data in
while (scanf("%255s", buf) > 0) //This format string could be created with ssprintf, but I'm too lazy now
{
if (buf[0] == '<') break;
//If not full yet / Broken mid-word, so we need to fix it...
if (buf[bufstep-2] != 0)
{
buf += bufstep-1;
}
if (bufbase[bufsize - 2] != 0) {
//No more space here, so we have to increase the size
char* tmp = (char*)realloc(bufbase, bufsize * 2);
if (tmp == NULL) {
printf("Failed to realloc.\n");
goto end;
}
int diff = (buf - bufbase);
bufbase = tmp;
//Some magic here: Reset the buf to keep its relative offset to bufbase
buf = buf + diff;
bufsize *= 2;
//Some safety measures
bufbase[bufsize - 2] = 0;
bufbase[bufsize - 1] = 0;
}
//Else we are done here and we can add the argument
push_input(d, bufbase);
}
//Call the functions
d->func(d->argc, d->argv);
//Clean up
clear_input(d);
//We could cleanup, i.e. reduce the size of the input buffer here. I won't.
break; //Break as there should only be one function per name
}
}
printf("Ladies and gentlemen, next round, your input please...\n");
}
end:
free(bufbase);
for (size_t i = 0; i < arr_size; i++)
{
clear_cmd_desc(&cmd_desc_arr[i]);
}
exfree(cmd_desc_arr);
getchar();
return 0;
}