Command Line Interface (CLI): your recommendations



Hello, All!

I'm implementing simple CLI (flat model, no tree-style menu etc.). Command
line looks like this: <command> <param1> <param2> ... <paramN> (where
N=1..4)
And idea is pretty simple:

1) get whole string of input line
2) preset table of strings matching <command>
3) preset table of function calls
4) scan <command> table for string, if match - call matching function (I
declare ALL callback functions as having the same format)

Here is code implementing part of this algorithm. The problem I came across
is if pressing 'Enter' continuously then sometimes output of PROMPT turnes
out clumsy, i.e. it's printed repeatedly in a line.

Before I went too far, I would like to ask your opinion about concept I'm
using and recommendations if possible:

#define _GNU_SOURCE

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define COUNT(a) (sizeof(a) / sizeof(a[0]))

const char *PROMPT = "CLI>";

enum errCodes { RC_SUCCESS = 0, RC_ERROR = -1 };

/* table of <commands> */
const char *commands[] = {
"show",
"version",
"help",
"port",
"exit"
};

/* callback function */
typedef int (*handler_ptr_t)(int, char**);

/* define command line: <command> <param1> <param2> ... <paramN> */
typedef struct cmdLineEntry_s {
#define MAX_NAME 20
char command[MAX_NAME];
#define MAX_ARGS 4
char params[MAX_ARGS];
unsigned int params_num;
handler_ptr_t cmd_handler;
} cmdLineEntry_t;

int cliShow(int argc, char *argv[])
{
puts("cliShow() stub");
return 0;
}

int cliVersion(int argc, char *argv[])
{
puts("cliVersion() stub");
return 0;
}

int cliHelp(int argc, char *argv[])
{
puts("cliHelp() stub");
return 0;
}

int cliPort(int argc, char *argv[])
{
puts("cliPort() stub");
return 0;
}

int cliExit(int argc, char *argv[])
{
puts("cliExit() stub");
exit(EXIT_SUCCESS);
}

/* define table functions pointers */
handler_ptr_t ftable[] = { cliShow, cliVersion, cliHelp, cliPort, cliExit };

/* parse command line and fill structure */
static int
cliParseCommandLine(char *buf, cmdLineEntry_t *cli)
{
const char delim[] = " \t\n";
unsigned int i;
char *token;

memset(cli, 0, sizeof *cli);

token = strtok(buf, delim);
for (i = 0; i < COUNT(commands); i++) {
if (!strcmp(token, commands[i])) {
strcpy(cli->command, token);
cli->cmd_handler = ftable[i];
i = 0;
for (token = strtok(NULL, delim); token != NULL; ) {
cli->params[i++] = token;
token = strtok(NULL, delim);
}
cli->params_num = i;
return 0;
}
}
return -1;
}

int main(void)
{
char buf[BUFSIZ] = {0};
cmdLineEntry_t cli = { {0}, {NULL}, 0, NULL };

while (1) {
printf("%s", PROMPT);
if ( fgets(buf, BUFSIZ, stdin) ) {
/* skip LF/CR/TAB/SP */
if (buf[0] == '\n' || buf[0] == ' ' || buf[0] == '\r' || buf[0]
== '\t')
continue;

/* parse stream */
if ( cliParseCommandLine(buf, &cli) < 0 ) {
printf("Error : invalid command!\n");
continue;
}
else {
cli.cmd_handler(cli.params_num, cli.params);
}
}
else continue; /* EOL */
} /* while */

return 0;
}

TIA!


With best regards, Roman Mashak. E-mail: mrv@xxxxxxxx


.



Relevant Pages