/*************************************************************************
* (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
* by Michael Pounov <misho@openbsd-bg.org>
*
* $Author: misho $
* $Id: aitcli.c,v 1.2.2.21 2010/12/07 16:33:46 misho Exp $
*
*************************************************************************/
#include "global.h"
#include "cli.h"
#pragma GCC visibility push(hidden)
// ------------------------------------------------
int cli_Errno;
char cli_Error[STRSIZ];
#pragma GCC visibility pop
// cli_GetErrno() Get error code of last operation
inline int
cli_GetErrno()
{
return cli_Errno;
}
// io_GetError() Get error text of last operation
inline const char *
cli_GetError()
{
return cli_Error;
}
// cli_SetErr() Set error to variables for internal use!!!
inline void
cli_SetErr(int eno, char *estr, ...)
{
va_list lst;
cli_Errno = eno;
memset(cli_Error, 0, STRSIZ);
va_start(lst, estr);
vsnprintf(cli_Error, STRSIZ, estr, lst);
va_end(lst);
}
// ------------------------------------------------------------
static inline void
clrscrEOL(linebuffer_t * __restrict buf)
{
register int i;
if (buf) {
write(buf->line_out, K_CR, 1);
for (i = 0; i < buf->line_len; i++)
write(buf->line_out, K_SPACE, 1);
}
}
static inline void
printfEOL(linebuffer_t * __restrict buf, int len, int prompt)
{
if (buf) {
write(buf->line_out, K_CR, 1);
if (prompt && buf->line_prompt)
write(buf->line_out, buf->line_prompt, buf->line_bol);
write(buf->line_out, buf->line_buf, len == -1 ? buf->line_eol - buf->line_bol: len);
}
}
static inline void
printfCR(linebuffer_t * __restrict buf, int prompt)
{
if (buf) {
write(buf->line_out, K_CR, 1);
if (prompt)
if (prompt && buf->line_prompt)
write(buf->line_out, buf->line_prompt, buf->line_bol);
}
}
static inline void
printfNL(linebuffer_t * __restrict buf, int prompt)
{
if (buf) {
write(buf->line_out, K_ENTER, 1);
if (prompt)
if (prompt && buf->line_prompt)
write(buf->line_out, buf->line_prompt, buf->line_bol);
}
}
// ------------------------------------------------------------
static int
bufCHAR(int idx, void * __restrict buffer)
{
linebuffer_t *buf = buffer;
int pos;
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
pos = buf->line_eol - buf->line_bol;
if (buf->line_mode == LINEMODE_INS)
memmove(buf->line_buf + pos + buf->line_keys[idx].key_len, buf->line_buf + pos,
buf->line_len - buf->line_eol);
if (buf->line_mode == LINEMODE_INS || buf->line_eol == buf->line_len - 1)
buf->line_len += buf->line_keys[idx].key_len;
buf->line_eol += buf->line_keys[idx].key_len;
memcpy(buf->line_buf + pos, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len);
buf->line_buf[buf->line_len - 1] = 0;
write(buf->line_out, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len);
if (buf->line_mode == LINEMODE_INS) {
write(buf->line_out, (const u_char*) buf->line_buf + pos + buf->line_keys[idx].key_len,
buf->line_len - buf->line_eol);
printfEOL(buf, -1, 1);
}
return RETCODE_OK;
}
static int
bufEOL(int idx, void * __restrict buffer)
{
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
printfCR(buffer, 1);
return RETCODE_EOL;
}
static int
bufEOF(int idx, void * __restrict buffer)
{
/*
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
*/
return RETCODE_EOF;
}
static int
bufUP(int idx, void * __restrict buffer)
{
linebuffer_t *buf = buffer;
int pos;
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
if (!buf->line_h)
buf->line_h = TAILQ_FIRST(&buf->line_history);
else
buf->line_h = TAILQ_NEXT(buf->line_h, hist_next);
if (!buf->line_h)
return RETCODE_OK;
clrscrEOL(buf);
cli_freeLine(buf);
pos = buf->line_eol - buf->line_bol;
buf->line_len += buf->line_h->hist_len;
buf->line_eol += buf->line_h->hist_len;
memcpy(buf->line_buf + pos, buf->line_h->hist_line, buf->line_h->hist_len);
buf->line_buf[buf->line_len - 1] = 0;
printfEOL(buf, -1, 1);
return RETCODE_OK;
}
static int
bufDOWN(int idx, void * __restrict buffer)
{
linebuffer_t *buf = buffer;
int pos;
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
if (!buf->line_h)
buf->line_h = TAILQ_LAST(&buf->line_history, tqHistoryHead);
else
buf->line_h = TAILQ_PREV(buf->line_h, tqHistoryHead, hist_next);
if (!buf->line_h)
return RETCODE_OK;
clrscrEOL(buf);
cli_freeLine(buf);
pos = buf->line_eol - buf->line_bol;
buf->line_len += buf->line_h->hist_len;
buf->line_eol += buf->line_h->hist_len;
memcpy(buf->line_buf + pos, buf->line_h->hist_line, buf->line_h->hist_len);
buf->line_buf[buf->line_len - 1] = 0;
printfEOL(buf, -1, 1);
return RETCODE_OK;
}
static int
bufCLR(int idx, void * __restrict buffer)
{
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
clrscrEOL(buffer);
cli_freeLine(buffer);
printfCR(buffer, 1);
return RETCODE_OK;
}
static int
bufBS(int idx, void * __restrict buffer)
{
linebuffer_t *buf = buffer;
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
if (buf->line_bol < buf->line_eol) {
clrscrEOL(buf);
buf->line_eol--;
buf->line_len--;
memmove(buf->line_buf + buf->line_eol - buf->line_bol,
buf->line_buf + buf->line_eol - buf->line_bol + 1,
buf->line_len - buf->line_eol);
buf->line_buf[buf->line_len - 1] = 0;
printfEOL(buf, buf->line_len - 1, 1);
printfEOL(buf, -1, 1);
}
return RETCODE_OK;
}
static int
bufBTAB(int idx, void * __restrict buffer)
{
linebuffer_t *buf = buffer;
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
if (buf->line_bol < buf->line_eol) {
clrscrEOL(buf);
buf->line_len = buf->line_eol - buf->line_bol + 1;
buf->line_buf[buf->line_len - 1] = 0;
printfEOL(buf, -1, 1);
}
return RETCODE_OK;
}
static int
bufMODE(int idx, void * __restrict buffer)
{
linebuffer_t *buf = buffer;
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
buf->line_mode = !buf->line_mode ? LINEMODE_OVER : LINEMODE_INS;
return RETCODE_OK;
}
static int
bufBEGIN(int idx, void * __restrict buffer)
{
linebuffer_t *buf = buffer;
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
buf->line_eol = buf->line_bol;
printfCR(buf, 1);
return RETCODE_OK;
}
static int
bufEND(int idx, void * __restrict buffer)
{
linebuffer_t *buf = buffer;
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
buf->line_eol = buf->line_len - 1;
printfEOL(buf, -1, 1);
return RETCODE_OK;
}
static int
bufLEFT(int idx, void * __restrict buffer)
{
linebuffer_t *buf = buffer;
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
if (buf->line_bol < buf->line_eol)
printfEOL(buf, --buf->line_eol - buf->line_bol, 1);
return RETCODE_OK;
}
static int
bufRIGHT(int idx, void * __restrict buffer)
{
linebuffer_t *buf = buffer;
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
if (buf->line_eol < buf->line_len - 1)
printfEOL(buf, ++buf->line_eol - buf->line_bol, 1);
return RETCODE_OK;
}
static int
bufDEL(int idx, void * __restrict buffer)
{
linebuffer_t *buf = buffer;
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
clrscrEOL(buf);
buf->line_len--;
memmove(buf->line_buf + buf->line_eol - buf->line_bol,
buf->line_buf + buf->line_eol - buf->line_bol + 1,
buf->line_len - buf->line_eol);
buf->line_buf[buf->line_len - 1] = 0;
printfEOL(buf, buf->line_len - 1, 1);
printfEOL(buf, -1, 1);
return RETCODE_OK;
}
static int
bufComp(int idx, void * __restrict buffer)
{
linebuffer_t *buf = buffer;
char *str, *s, **app, *items[MAX_PROMPT_ITEMS], szLine[STRSIZ];
register int i, j;
struct tagCommand *cmd, *c;
int pos, ret = RETCODE_OK;
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
str = strdup(buf->line_buf);
if (!str)
return RETCODE_ERR;
else {
s = str;
io_TrimStr((u_char*) s);
}
i = j = 0;
c = NULL;
memset(szLine, 0, STRSIZ);
if (*s) {
memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
for (app = items, i = 0; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t"));
*app ? i++ : i, *app ? app++ : app);
if (i) {
SLIST_FOREACH(cmd, &buf->line_cmds, cmd_next) {
if (cmd->cmd_level == buf->line_level &&
!strncmp(cmd->cmd_name, items[0], strlen(items[0]))) {
if (strncmp(cmd->cmd_name, CLI_CMD_SEP, strlen(CLI_CMD_SEP))) {
j++;
c = cmd;
strlcat(szLine, " ", STRSIZ);
strlcat(szLine, cmd->cmd_name, STRSIZ);
}
}
}
if (i > 1 && c) {
/* we are on argument of command and has complition info */
j++; // always must be j > 1 ;) for arguments
strlcpy(szLine, c->cmd_info, STRSIZ);
}
} else {
/* we have valid char but i == 0, this case is illegal */
ret = RETCODE_ERR;
goto endcomp;
}
} else {
/* we on 0 position of prompt, show commands for this level */
SLIST_FOREACH(cmd, &buf->line_cmds, cmd_next) {
if (cmd->cmd_level == buf->line_level)
if (strncmp(cmd->cmd_name, CLI_CMD_SEP, strlen(CLI_CMD_SEP))) {
j++;
c = cmd;
strlcat(szLine, " ", STRSIZ);
strlcat(szLine, cmd->cmd_name, STRSIZ);
}
}
}
/* completion show actions ... */
if (j > 1 && c) {
printfNL(buf, 0);
write(buf->line_out, szLine, strlen(szLine));
printfNL(buf, 1);
printfEOL(buf, buf->line_len - 1, 1);
printfEOL(buf, -1, 1);
}
if (j == 1 && c) {
clrscrEOL(buf);
cli_freeLine(buf);
pos = buf->line_eol - buf->line_bol;
buf->line_len += c->cmd_len + 1;
buf->line_eol += c->cmd_len + 1;
memcpy(buf->line_buf + pos, c->cmd_name, c->cmd_len);
buf->line_buf[pos + c->cmd_len] = (u_char) *K_SPACE;
buf->line_buf[buf->line_len - 1] = 0;
printfEOL(buf, -1, 1);
}
endcomp:
free(str);
return ret;
}
static int
bufHelp(int idx, void * __restrict buffer)
{
linebuffer_t *buf = buffer;
if (!buffer || idx < 0 || idx > MAX_BINDKEY)
return RETCODE_ERR;
cli_Cmd_Help(buf, -1, NULL);
printfEOL(buf, buf->line_len - 1, 1);
printfEOL(buf, -1, 1);
return RETCODE_OK;
}
// ---------------------------------------------------------------
/*
* cli_Printf() Send message to CLI session
* @buffer = CLI buffer
* @fmt = printf format string
* @... = arguments defined in fmt
* return: none
*/
inline void
cli_Printf(linebuffer_t * __restrict buffer, char *fmt, ...)
{
va_list lst;
FILE *f;
if (fmt) {
f = fdopen(buffer->line_out, "a");
if (!f) {
LOGERR;
return;
}
va_start(lst, fmt);
vfprintf(f, fmt, lst);
va_end(lst);
} else
cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
}
/*
* cli_PrintHelp() Print help screen
* @buffer = CLI buffer
* return: none
*/
inline void
cli_PrintHelp(linebuffer_t * __restrict buffer)
{
if (buffer) {
bufHelp(0, buffer);
clrscrEOL(buffer);
} else
cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
}
/*
* cli_BindKey() Bind function to key
* @key = key structure
* @buffer = CLI buffer
* return: RETCODE_ERR error, RETCODE_OK ok, >0 bind at position
*/
int
cli_BindKey(bindkey_t * __restrict key, linebuffer_t * __restrict buffer)
{
register int i;
if (!key || !buffer) {
cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
return RETCODE_ERR;
}
for (i = 0; i < MAX_BINDKEY; i++)
if (key->key_len == buffer->line_keys[i].key_len &&
!memcmp(key->key_ch, buffer->line_keys[i].key_ch, key->key_len)) {
buffer->line_keys[i].key_func = key->key_func;
return i;
}
return RETCODE_OK;
}
/*
* cli_addCommand() Add command to CLI session
* @buffer = CLI buffer
* @csCmd = Command name
* @cliLevel = Level in CLI, -1 unprivi(view from all), 0 main config, 1 sub config ...
* @funcCmd = Callback function when user call command
* @csInfo = Inline information for command
* @csHelp = Help line when call help
* return: RETCODE_ERR error, RETCODE_OK ok
*/
int
cli_addCommand(linebuffer_t * __restrict buffer, const char *csCmd, int cliLevel, cmd_func_t funcCmd,
const char *csInfo, const char *csHelp)
{
struct tagCommand *cmd;
if (!buffer || !csCmd) {
cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
return RETCODE_ERR;
}
cmd = malloc(sizeof(struct tagCommand));
if (!cmd) {
LOGERR;
return RETCODE_ERR;
} else
memset(cmd, 0, sizeof(struct tagCommand));
cmd->cmd_level = cliLevel;
cmd->cmd_func = funcCmd;
cmd->cmd_len = strlcpy(cmd->cmd_name, csCmd, STRSIZ);
if (csInfo)
strlcpy(cmd->cmd_info, csInfo, STRSIZ);
if (csHelp)
strlcpy(cmd->cmd_help, csHelp, STRSIZ);
SLIST_INSERT_HEAD(&buffer->line_cmds, cmd, cmd_next);
return RETCODE_OK;
}
/*
* cli_delCommand() Delete command from CLI session
* @buffer = CLI buffer
* @csCmd = Command name
* @cliLevel = Level in CLI, -1 unprivi(view from all), 0 main config, 1 sub config ...
* return: RETCODE_ERR error, RETCODE_OK ok
*/
int
cli_delCommand(linebuffer_t * __restrict buffer, const char *csCmd, int cliLevel)
{
struct tagCommand *cmd;
int ret = RETCODE_OK;
if (!buffer || !csCmd) {
cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
return RETCODE_ERR;
}
SLIST_FOREACH(cmd, &buffer->line_cmds, cmd_next)
if (cmd->cmd_level == cliLevel && !strcmp(cmd->cmd_name, csCmd)) {
ret = 1;
SLIST_REMOVE(&buffer->line_cmds, cmd, tagCommand, cmd_next);
free(cmd);
break;
}
return ret;
}
/*
* cli_updCommand() Update command in CLI session
* @buffer = CLI buffer
* @csCmd = Command name
* @cliLevel = Level in CLI, -1 unprivi(view from all), 0 main config, 1 sub config ...
* @funcCmd = Callback function when user call command
* @csInfo = Inline information for command
* @csHelp = Help line when call help
* return: RETCODE_ERR error, RETCODE_OK ok
*/
int
cli_updCommand(linebuffer_t * __restrict buffer, const char *csCmd, int cliLevel, cmd_func_t funcCmd,
const char *csInfo, const char *csHelp)
{
struct tagCommand *cmd;
int ret = RETCODE_OK;
if (!buffer || !csCmd) {
cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
return RETCODE_ERR;
}
SLIST_FOREACH(cmd, &buffer->line_cmds, cmd_next)
if (cmd->cmd_level == cliLevel && !strcmp(cmd->cmd_name, csCmd)) {
ret = 1;
if (funcCmd)
cmd->cmd_func = funcCmd;
if (csInfo)
strlcpy(cmd->cmd_info, csInfo, STRSIZ);
if (csHelp)
strlcpy(cmd->cmd_help, csHelp, STRSIZ);
break;
}
return ret;
}
/*
* cli_addHistory() Add line to history
* @buffer = CLI buffer
* @str = Add custom text or if NULL use readed line from CLI buffer
* return: RETCODE_ERR error, RETCODE_OK ok
*/
int
cli_addHistory(linebuffer_t * __restrict buffer, const char * __restrict str)
{
struct tagHistory *h;
if (!buffer) {
cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
return RETCODE_ERR;
}
if (!(h = malloc(sizeof(struct tagHistory)))) {
LOGERR;
return RETCODE_ERR;
} else
memset(h, 0, sizeof(struct tagHistory));
if (str) {
if (!*str) {
free(h);
return RETCODE_OK;
}
h->hist_len = strlcpy(h->hist_line, str, BUFSIZ);
} else {
if (!*buffer->line_buf || buffer->line_len < 2) {
free(h);
return RETCODE_OK;
}
memcpy(h->hist_line, buffer->line_buf, (h->hist_len = buffer->line_len));
io_TrimStr((u_char*) h->hist_line);
h->hist_len = strlen(h->hist_line);
}
TAILQ_INSERT_HEAD(&buffer->line_history, h, hist_next);
return h->hist_len;
}
/*
* cli_saveHistory() Save history to file
* @buffer = CLI buffer
* @histfile = History filename, if NULL will be use default name
* @lines = Maximum history lines to save
* return: RETCODE_ERR error, RETCODE_OK ok
*/
int
cli_saveHistory(linebuffer_t * __restrict buffer, const char *histfile, int lines)
{
FILE *f;
mode_t mode;
char szFName[MAXPATHLEN];
struct tagHistory *h;
if (!buffer) {
cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
return RETCODE_ERR;
}
if (!histfile)
strlcpy(szFName, HISTORY_FILE, MAXPATHLEN);
else
strlcpy(szFName, histfile, MAXPATHLEN);
mode = umask(0177);
f = fopen(szFName, "w");
if (!f) {
LOGERR;
return RETCODE_ERR;
}
TAILQ_FOREACH(h, &buffer->line_history, hist_next) {
fprintf(f, "%s\n", h->hist_line);
if (lines)
lines--;
else
break;
}
fclose(f);
umask(mode);
return RETCODE_OK;
}
/*
* cli_loadHistory() Load history from file
* @buffer = CLI buffer
* @histfile = History filename, if NULL will be use default name
* return: RETCODE_ERR error, RETCODE_OK ok
*/
int
cli_loadHistory(linebuffer_t * __restrict buffer, const char *histfile)
{
FILE *f;
char szFName[MAXPATHLEN], buf[BUFSIZ];
struct tagHistory *h;
if (!buffer) {
cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
return RETCODE_ERR;
}
if (!histfile)
strlcpy(szFName, HISTORY_FILE, MAXPATHLEN);
else
strlcpy(szFName, histfile, MAXPATHLEN);
f = fopen(szFName, "r");
if (!f)
return RETCODE_OK;
while (fgets(buf, BUFSIZ, f)) {
if (!*buf || *buf == '#')
continue;
else
io_TrimStr((u_char*) buf);
if (!(h = malloc(sizeof(struct tagHistory)))) {
LOGERR;
fclose(f);
return RETCODE_ERR;
} else
memset(h, 0, sizeof(struct tagHistory));
h->hist_len = strlcpy(h->hist_line, buf, BUFSIZ);
TAILQ_INSERT_TAIL(&buffer->line_history, h, hist_next);
}
fclose(f);
return RETCODE_OK;
}
/*
* cli_resetHistory() Reset history search in CLI session
* @buffer = CLI buffer
* return: none
*/
inline void
cli_resetHistory(linebuffer_t * __restrict buffer)
{
buffer->line_h = NULL;
}
/*
* cli_freeLine() Clear entire line
* @buffer = CLI buffer
* return: RETCODE_ERR error, RETCODE_OK ok
*/
inline int
cli_freeLine(linebuffer_t * __restrict buffer)
{
int code = RETCODE_ERR;
if (buffer) {
if (buffer->line_buf)
free(buffer->line_buf);
buffer->line_buf = malloc(BUFSIZ);
if (buffer->line_buf) {
memset(buffer->line_buf, 0, BUFSIZ);
buffer->line_eol = buffer->line_bol;
buffer->line_len = 1 + buffer->line_eol;
code = RETCODE_OK;
} else
LOGERR;
} else
cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
return code;
}
/*
* cli_setPrompt() Set new prompt for CLI session
* @buffer = CLI buffer
* @prompt = new text for prompt or if NULL disable prompt
* return: none
*/
inline void
cli_setPrompt(linebuffer_t * __restrict buffer, const char *prompt)
{
if (buffer) {
if (buffer->line_prompt) {
free(buffer->line_prompt);
buffer->line_prompt = NULL;
buffer->line_bol = 0;
}
if (prompt) {
buffer->line_prompt = strdup(prompt);
if (buffer->line_prompt) {
buffer->line_bol = strlen(buffer->line_prompt);
buffer->line_eol = buffer->line_bol;
buffer->line_len = 1 + buffer->line_eol;
} else
LOGERR;
}
} else
cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
}
/*
* cliEnd() Clear data, Free resources and close CLI session
* @buffer = CLI buffer
* return: RETCODE_ERR error, RETCODE_OK ok
*/
void
cliEnd(linebuffer_t * __restrict buffer)
{
struct tagHistory *h;
struct tagCommand *c;
if (buffer) {
while ((c = SLIST_FIRST(&buffer->line_cmds))) {
SLIST_REMOVE_HEAD(&buffer->line_cmds, cmd_next);
free(c);
}
while ((h = TAILQ_FIRST(&buffer->line_history))) {
TAILQ_REMOVE(&buffer->line_history, h, hist_next);
free(h);
}
if (buffer->line_prompt)
free(buffer->line_prompt);
if (buffer->line_keys)
free(buffer->line_keys);
if (buffer->line_buf)
free(buffer->line_buf);
free(buffer);
buffer = NULL;
} else
cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
}
/*
* cliInit() Start CLI session, allocate memory for resources and bind keys
* @fin = Input device handle
* @fout = Output device handle
* @prompt = text for prompt, if NULL disable prompt
* return: NULL if error or !=NULL CLI buffer
*/
linebuffer_t *
cliInit(int fin, int fout, const char *prompt)
{
linebuffer_t *buffer;
bindkey_t *keys;
register int i;
/* init buffer */
buffer = malloc(sizeof(linebuffer_t));
if (!buffer) {
LOGERR;
return NULL;
} else {
memset(buffer, 0, sizeof(linebuffer_t));
buffer->line_in = fin;
buffer->line_out = fout;
TAILQ_INIT(&buffer->line_history);
SLIST_INIT(&buffer->line_cmds);
if (prompt) {
buffer->line_prompt = strdup(prompt);
if (!buffer->line_prompt) {
LOGERR;
free(buffer);
return NULL;
} else
buffer->line_eol = buffer->line_bol = strlen(buffer->line_prompt);
}
}
buffer->line_buf = malloc(BUFSIZ);
if (!buffer->line_buf) {
LOGERR;
if (buffer->line_prompt)
free(buffer->line_prompt);
free(buffer);
return NULL;
} else {
memset(buffer->line_buf, 0, BUFSIZ);
buffer->line_len = 1 + buffer->line_eol;
}
keys = calloc(MAX_BINDKEY + 1, sizeof(bindkey_t));
if (!keys) {
LOGERR;
if (buffer->line_prompt)
free(buffer->line_prompt);
free(buffer->line_buf);
free(buffer);
return NULL;
} else
memset(keys, 0, sizeof(bindkey_t) * (MAX_BINDKEY + 1));
/* add helper functions */
cli_addCommand(buffer, "exit", 0, cli_Cmd_Exit, "exit <cr>", "Exit from console");
cli_addCommand(buffer, "help", 0, cli_Cmd_Help, "help [command] <cr>", "Help screen");
cli_addCommand(buffer, "-------", 0, NULL, "-------------------------", NULL);
/* fill key bindings */
// ascii chars & ctrl+chars
for (i = 0; i < 256; i++) {
*keys[i].key_ch = (u_char) i;
keys[i].key_len = 1;
if (!i || i == *K_CTRL_D)
keys[i].key_func = bufEOF;
if (i == *K_CTRL_M || i == *K_CTRL_J)
keys[i].key_func = bufEOL;
if (i == *K_CTRL_H || i == *K_BACKSPACE)
keys[i].key_func = bufBS;
if (i == *K_CTRL_C)
keys[i].key_func = bufCLR;
if (i == *K_CTRL_A)
keys[i].key_func = bufBEGIN;
if (i == *K_CTRL_E)
keys[i].key_func = bufEND;
if (i == *K_TAB)
keys[i].key_func = bufComp;
if (i >= *K_SPACE && i < *K_BACKSPACE)
keys[i].key_func = bufCHAR;
if (i > *K_BACKSPACE && i < 0xff)
keys[i].key_func = bufCHAR;
if (i == '?')
keys[i].key_func = bufHelp;
}
// alt+chars
for (i = 256; i < 512; i++) {
keys[i].key_ch[0] = 0x1b;
keys[i].key_ch[1] = (u_char) i - 256;
keys[i].key_len = 2;
}
// 3 bytes
keys[i].key_len = sizeof K_F1 - 1;
memcpy(keys[i].key_ch, K_F1, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_F2 - 1;
memcpy(keys[i].key_ch, K_F2, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_F3 - 1;
memcpy(keys[i].key_ch, K_F3, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_F4 - 1;
memcpy(keys[i].key_ch, K_F4, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_SH_F1 - 1;
memcpy(keys[i].key_ch, K_CTRL_SH_F1, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_SH_F2 - 1;
memcpy(keys[i].key_ch, K_CTRL_SH_F2, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_SH_F3 - 1;
memcpy(keys[i].key_ch, K_CTRL_SH_F3, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_SH_F4 - 1;
memcpy(keys[i].key_ch, K_CTRL_SH_F4, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_SH_F5 - 1;
memcpy(keys[i].key_ch, K_CTRL_SH_F5, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_SH_F6 - 1;
memcpy(keys[i].key_ch, K_CTRL_SH_F6, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_SH_F7 - 1;
memcpy(keys[i].key_ch, K_CTRL_SH_F7, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_SH_F8 - 1;
memcpy(keys[i].key_ch, K_CTRL_SH_F8, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_SH_F9 - 1;
memcpy(keys[i].key_ch, K_CTRL_SH_F9, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_SH_F10 - 1;
memcpy(keys[i].key_ch, K_CTRL_SH_F10, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_SH_F11 - 1;
memcpy(keys[i].key_ch, K_CTRL_SH_F11, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_SH_F12 - 1;
memcpy(keys[i].key_ch, K_CTRL_SH_F12, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_F1 - 1;
memcpy(keys[i].key_ch, K_CTRL_F1, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_F2 - 1;
memcpy(keys[i].key_ch, K_CTRL_F2, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_F3 - 1;
memcpy(keys[i].key_ch, K_CTRL_F3, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_F4 - 1;
memcpy(keys[i].key_ch, K_CTRL_F4, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_F5 - 1;
memcpy(keys[i].key_ch, K_CTRL_F5, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_F6 - 1;
memcpy(keys[i].key_ch, K_CTRL_F6, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_F7 - 1;
memcpy(keys[i].key_ch, K_CTRL_F7, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_F8 - 1;
memcpy(keys[i].key_ch, K_CTRL_F8, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_F9 - 1;
memcpy(keys[i].key_ch, K_CTRL_F9, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_F10 - 1;
memcpy(keys[i].key_ch, K_CTRL_F10, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_F11 - 1;
memcpy(keys[i].key_ch, K_CTRL_F11, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_CTRL_F12 - 1;
memcpy(keys[i].key_ch, K_CTRL_F12, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_HOME - 1;
keys[i].key_func = bufBEGIN;
memcpy(keys[i].key_ch, K_HOME, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_END - 1;
keys[i].key_func = bufEND;
memcpy(keys[i].key_ch, K_END, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_UP - 1;
keys[i].key_func = bufUP;
memcpy(keys[i].key_ch, K_UP, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_DOWN - 1;
keys[i].key_func = bufDOWN;
memcpy(keys[i].key_ch, K_DOWN, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_RIGHT - 1;
keys[i].key_func = bufRIGHT;
memcpy(keys[i].key_ch, K_RIGHT, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_LEFT - 1;
keys[i].key_func = bufLEFT;
memcpy(keys[i].key_ch, K_LEFT, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_BTAB - 1;
keys[i].key_func = bufBTAB;
memcpy(keys[i].key_ch, K_BTAB, keys[i].key_len);
i++;
// 4 bytes
keys[i].key_len = sizeof K_INS - 1;
keys[i].key_func = bufMODE;
memcpy(keys[i].key_ch, K_INS, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_DEL - 1;
keys[i].key_func = bufDEL;
memcpy(keys[i].key_ch, K_DEL, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_PGUP - 1;
memcpy(keys[i].key_ch, K_PGUP, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_PGDN - 1;
memcpy(keys[i].key_ch, K_PGDN, keys[i].key_len);
i++;
// 5 bytes
keys[i].key_len = sizeof K_F5 - 1;
memcpy(keys[i].key_ch, K_F5, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_F6 - 1;
memcpy(keys[i].key_ch, K_F6, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_F7 - 1;
memcpy(keys[i].key_ch, K_F7, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_F8 - 1;
memcpy(keys[i].key_ch, K_F8, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_F9 - 1;
memcpy(keys[i].key_ch, K_F9, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_F10 - 1;
memcpy(keys[i].key_ch, K_F10, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_F11 - 1;
memcpy(keys[i].key_ch, K_F11, keys[i].key_len);
i++;
keys[i].key_len = sizeof K_F12 - 1;
memcpy(keys[i].key_ch, K_F12, keys[i].key_len);
i++;
buffer->line_keys = keys;
return buffer;
}
/*
* cliInitLine() Init CLI input line terminal
* @buffer = CLI buffer
* return: none
*/
int
cliInitLine(linebuffer_t * __restrict buffer)
{
struct termios t;
memset(&t, 0, sizeof t);
tcgetattr(buffer->line_in, &t);
t.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT);
t.c_iflag |= IGNBRK;
t.c_cc[VMIN] = 1;
t.c_cc[VTIME] = 0;
return tcsetattr(buffer->line_in, TCSANOW, &t);
}
/*
* cliReadLine() Read line from opened CLI session
* @buffer = CLI buffer
* return: NULL if error or !=NULL readed line, must be free after use!
*/
char *
cliReadLine(linebuffer_t * __restrict buffer)
{
int code, readLen;
register int i;
struct pollfd fds;
char buf[BUFSIZ], *str = NULL;
if (!buffer) {
cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
return NULL;
}
memset(&fds, 0, sizeof fds);
fds.fd = buffer->line_in;
fds.events = POLLIN;
printfCR(buffer, 1);
while (42) {
if (poll(&fds, 1, -1) < 1) {
LOGERR;
return str;
}
memset(buf, 0, sizeof buf);
readLen = read(buffer->line_in, buf, BUFSIZ);
if (readLen == -1) {
LOGERR;
return str;
}
if (!readLen) {
if (buffer->line_buf)
str = strdup(buffer->line_buf);
else
cli_SetErr(EPIPE, "Error:: unknown state ...");
return str;
}
recheck:
for (code = RETCODE_OK, i = MAX_BINDKEY - 1; i > -1; i--)
if (readLen >= buffer->line_keys[i].key_len &&
!memcmp(buffer->line_keys[i].key_ch, buf,
buffer->line_keys[i].key_len)) {
readLen -= buffer->line_keys[i].key_len;
if (readLen)
memmove(buf, buf + buffer->line_keys[i].key_len, readLen);
else
memset(buf, 0, buffer->line_keys[i].key_len);
if (buffer->line_keys[i].key_func)
if ((code = buffer->line_keys[i].key_func(i, buffer)))
readLen = 0;
if (readLen)
goto recheck;
else
break;
}
if (code)
break;
}
if (code != RETCODE_ERR && code != RETCODE_EOF && buffer->line_buf)
str = strdup(buffer->line_buf);
return str;
}
/*
* cliNetLoop() CLI network main loop binded to socket
* @buffer = CLI buffer
* @csHistFile = History file name
* @sock = client socket
* return: RETCODE_ERR error, RETCODE_OK ok
*/
int
cliNetLoop(linebuffer_t * __restrict buffer, const char *csHistFile, int sock)
{
u_char buf[BUFSIZ];
int pid, stat, pty, r, s, alen, flg, attrlen = 0, ret = 0;
fd_set fds;
struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 };
struct telnetAttrs *a, Attr[10];
switch ((pid = forkpty(&pty, NULL, NULL, NULL))) {
case -1:
LOGERR;
return -1;
case 0:
if (!buffer) {
cli_SetErr(EINVAL, "Error:: invalid input parameters ...");
return -1;
} else
close(sock);
ret = cliLoop(buffer, csHistFile) < 0 ? 1 : 0;
cliEnd(buffer);
exit(ret);
default:
telnet_SetCmd(Attr + 0, DO, TELOPT_TTYPE);
telnet_SetCmd(Attr + 1, WILL, TELOPT_ECHO);
telnet_Set_SubOpt(Attr + 2, TELOPT_LFLOW, LFLOW_OFF, NULL, 0);
telnet_Set_SubOpt(Attr + 3, TELOPT_LFLOW, LFLOW_RESTART_XON, NULL, 0);
telnet_SetCmd(Attr + 4, DO, TELOPT_LINEMODE);
if ((ret = telnetSend(sock, Attr, 5, NULL, 0, 0)) == -1) {
cli_Errno = telnet_GetErrno();
strlcpy(cli_Error, telnet_GetError(), STRSIZ);
return -1;
} else
flg = 0;
while (42) {
if (waitpid(pid, &stat, WNOHANG))
break;
FD_ZERO(&fds);
FD_SET(sock, &fds);
FD_SET(pty, &fds);
if ((ret = select(FD_SETSIZE, &fds, NULL, NULL, &tv)) < 1) {
if (!ret)
cli_SetErr(ETIMEDOUT, "Client session timeout ...");
break;
}
r = FD_ISSET(sock, &fds) ? sock : pty;
s = FD_ISSET(sock, &fds) ? pty : sock;
if (FD_ISSET(sock, &fds)) {
memset(buf, 0, BUFSIZ);
if ((ret = telnetRecv(sock, &a, &alen, buf, BUFSIZ)) < 0) {
if (a)
free(a);
if (-2 == ret)
continue;
// EOF
if (-3 == ret)
shutdown(sock, SHUT_RD);
else {
cli_Errno = telnet_GetErrno();
strlcpy(cli_Error, telnet_GetError(), STRSIZ);
}
break;
}
attrlen = 0;
if (1 == flg && alen) {
telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_SGA);
telnet_SetCmd(&Attr[attrlen++], DO, TELOPT_ECHO);
}
if (2 == flg && alen) {
telnet_SetCmd(&Attr[attrlen++], WILL, TELOPT_ECHO);
telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW,
LFLOW_OFF, NULL, 0);
telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW,
LFLOW_RESTART_XON, NULL, 0);
telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_LINEMODE);
}
if (a)
free(a);
if ((ret = write(pty, buf, ret)) == -1) {
LOGERR;
break;
}
}
if (FD_ISSET(pty, &fds)) {
memset(buf, 0, BUFSIZ);
if ((ret = read(pty, buf, BUFSIZ)) == -1) {
LOGERR;
break;
}
if ((ret = telnetSend(sock, Attr, pty == s ? 0 : attrlen, buf, ret, 0)) == -1) {
cli_Errno = telnet_GetErrno();
strlcpy(cli_Error, telnet_GetError(), STRSIZ);
break;
} else
flg++;
}
}
close(pty);
}
return ret;
}
/*
* cliLoop() CLI main loop
* @buffer = CLI buffer
* @csHistFile = History file name
* return: RETCODE_ERR error, RETCODE_OK ok
*/
int
cliLoop(linebuffer_t * __restrict buffer, const char *csHistFile)
{
char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];
register int i;
int ret = RETCODE_OK;
struct tagCommand *cmd;
/* --- main body of CLI --- */
cliInitLine(buffer);
if (cli_loadHistory(buffer, csHistFile) == RETCODE_ERR)
return RETCODE_ERR;
do {
line = cliReadLine(buffer);
if (!line) {
printfNL(buffer, 0);
break;
} else
cli_addHistory(buffer, NULL);
// clear whitespaces
for (s = line; isspace(*s); s++);
if (*s) {
for (t = s + strlen(s) - 1; t > s && isspace(*t); t--);
*++t = 0;
}
if (*s) {
memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t"));
*app ? app++ : app);
// exec_cmd ...
i = 0;
SLIST_FOREACH(cmd, &buffer->line_cmds, cmd_next) {
if (*items[0] && !strncmp(cmd->cmd_name, items[0], strlen(items[0])))
break;
else
i++;
}
if (!cmd) {
cli_Printf(buffer, "\nCommand '%s' not found!\n", items[0]);
ret = -1;
} else
if (cmd->cmd_func) {
cli_Printf(buffer, "\n");
ret = cmd->cmd_func(buffer, i, items);
} else {
clrscrEOL(buffer);
printfCR(buffer, 1);
}
}
cli_freeLine(buffer);
cli_resetHistory(buffer);
free(line);
} while (ret < 1);
cli_saveHistory(buffer, csHistFile, HISTORY_LINES);
return ret;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>