Diff for /libaitcli/src/aitcli.c between versions 1.1.1.1.2.15 and 1.20

version 1.1.1.1.2.15, 2010/05/20 08:18:41 version 1.20, 2025/12/17 23:30:11
Line 5 Line 5
 * $Author$  * $Author$
 * $Id$  * $Id$
 *  *
*************************************************************************/**************************************************************************
#include "global.h"The ELWIX and AITNET software is distributed under the following
 terms:
   
   All of the documentation and software included in the ELWIX and AITNET
   Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
   
#pragma GCC visibility push(hidden)Copyright 2004 - 2025
         by Michael Pounov <misho@elwix.org>.  All rights reserved.
   
cliCommands_t cli_stdCmds[] = {Redistribution and use in source and binary forms, with or without
        { "test", cli_Cmd_Unsupported, "Test - Don`t use default command structure!", "test <cr>", cli_Comp_Filename }, modification, are permitted provided that the following conditions
        { "-------", NULL, "---------------------", NULL, NULL }, are met:
        { "help", cli_Cmd_Help, "Help screen", "help [command] <cr>", NULL }, 1. Redistributions of source code must retain the above copyright
        { "exit", cli_Cmd_Exit, "Exit from console", "exit <cr>", NULL },    notice, this list of conditions and the following disclaimer.
        { NULL, NULL, NULL, NULL }2. Redistributions in binary form must reproduce the above copyright
};   notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
 3. All advertising materials mentioning features or use of this software
    must display the following acknowledgement:
 This product includes software developed by Michael Pounov <misho@elwix.org>
 ELWIX - Embedded LightWeight unIX and its contributors.
 4. Neither the name of AITNET nor the names of its contributors
    may be used to endorse or promote products derived from this software
    without specific prior written permission.
   
// ------------------------------------------------THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 SUCH DAMAGE.
 */
 #include "global.h"
 #include "cli.h"
   
   
   #pragma GCC visibility push(hidden)
   
 int cli_Errno;  int cli_Errno;
 char cli_Error[STRSIZ];  char cli_Error[STRSIZ];
   
 char cli_pending_special_char;  
   
 #pragma GCC visibility pop  #pragma GCC visibility pop
   
   // cli_GetErrno() Get error code of last operation
   int
   cli_GetErrno()
   {
           return cli_Errno;
   }
   
static void cli_Null_Prep_Term(int meta)// cli_GetError() Get error text of last operation
 const char *
 cli_GetError()
 {  {
           return cli_Error;
 }  }
   
#include <syslog.h>// cli_SetErr() Set error to variables for internal use!!!
static int cli_Net_rl_GetCh(FILE *s)void
 cli_SetErr(int eno, char *estr, ...)
 {  {
        int ch = rl_getc(s);        va_list lst;
   
        if (!cli_pending_special_char && 0x1b == ch) {        cli_Errno = eno;
                cli_pending_special_char = ch;        memset(cli_Error, 0, sizeof cli_Error);
                return ch;        va_start(lst, estr);
         vsnprintf(cli_Error, sizeof cli_Error, estr, lst);
         va_end(lst);
 }
 
 // ------------------------------------------------------------
 
 static inline void
 clrscrEOL(linebuffer_t * __restrict buf)
 {
         register int i;
         int ign __attribute__((unused));
 
         if (buf && buf->line_prompt) {
                 ign = write(buf->line_out, K_CR, 1);
 
                 for (i = 0; i < buf->line_len; i++)
                         ign = write(buf->line_out, K_SPACE, 1);
         }          }
        if (cli_pending_special_char && 0x5b == ch) {}
                cli_pending_special_char = ch;
                return ch;static inline void
 printfEOL(linebuffer_t * __restrict buf, int len, int prompt)
 {
         int ign __attribute__((unused));
 
         if (buf) {
                 if (prompt && buf->line_prompt) {
                         ign = write(buf->line_out, K_CR, 1);
                         ign = write(buf->line_out, buf->line_prompt, buf->line_bol);
                 }
 
                 ign = write(buf->line_out, buf->line_buf, len == -1 ? 
                                 buf->line_eol - buf->line_bol : len);
         }          }
        if (0x5b == cli_pending_special_char) {}
                cli_pending_special_char = 0;
                return ch;static inline void
 printfCR(linebuffer_t * __restrict buf, int prompt)
 {
         int ign __attribute__((unused));
 
         if (buf && prompt && buf->line_prompt) {
                 ign = write(buf->line_out, K_CR, 1);
                 ign = write(buf->line_out, buf->line_prompt, buf->line_bol);
         }          }
   }
   
        syslog(LOG_CRIT, "+++++ getc=%0x\n", ch);static inline void
        cli_pending_special_char = 0;printfNL(linebuffer_t * __restrict buf, int prompt)
        fputc(ch, rl_outstream);{
        fflush(rl_outstream);        int ign __attribute__((unused));
        return ch;
         if (buf) {
                 ign = write(buf->line_out, K_ENTER, 1);
 
                 if (prompt)
                         if (prompt && buf->line_prompt)
                                 ign = write(buf->line_out, buf->line_prompt, buf->line_bol);
         }
 }  }
   
#if 0// ------------------------------------------------------------
static void cli_Line_Handler(char *line)
 static int
 bufCHAR(int idx, void * __restrict cli_buffer)
 {  {
        int len;        linebuffer_t *buf = cli_buffer;
        static char cli_Buffer[BUFSIZ];        int pos;
        static int cli_BufferLen, cli_BufferPos;        int ign __attribute__((unused));
   
        if (!line) {     // EOF        if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
                fwrite("\x4", 1, 1, rl_outstream);        // ctrl+D                return RETCODE_ERR;
                goto end;
        } else        pos = buf->line_eol - buf->line_bol;
                len = strlen(line);
        if (BUFSIZ - 2 < len)        if (buf->line_mode == LINEMODE_INS)
                cli_BufferPos = cli_BufferLen = 0;                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;
 
         if (buf->line_prompt)
                 ign = write(buf->line_out, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len);
 
         if (buf->line_mode == LINEMODE_INS) {
                 ign = 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 cli_buffer)
 {
         if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
                 return RETCODE_ERR;
 
         printfCR(cli_buffer, 1);
         return RETCODE_EOL;
 }
 
 static int
 bufEOF(int idx, void * __restrict cli_buffer)
 {
         int ret;
 
         /*
         if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
                 return RETCODE_ERR;
         */
 
         printfNL(cli_buffer, 0);
         ret = cli_Cmd_End(cli_buffer, idx, NULL);
         printfCR(cli_buffer, (ret != RETCODE_EOF));
 
         return ret;
 }
 
 static int
 bufUP(int idx, void * __restrict cli_buffer)
 {
         linebuffer_t *buf = cli_buffer;
         int pos;
 
         if (!cli_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 cli_buffer)
 {
         linebuffer_t *buf = cli_buffer;
         int pos;
 
         if (!cli_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 cli_buffer)
 {
         if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
                 return RETCODE_ERR;
 
         clrscrEOL(cli_buffer);
         cli_freeLine(cli_buffer);
 
         printfCR(cli_buffer, 1);
         return RETCODE_OK;
 }
 
 static int
 bufBS(int idx, void * __restrict cli_buffer)
 {
         linebuffer_t *buf = cli_buffer;
 
         if (!cli_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 cli_buffer)
 {
         linebuffer_t *buf = cli_buffer;
 
         if (!cli_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 cli_buffer)
 {
         linebuffer_t *buf = cli_buffer;
 
         if (!cli_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 cli_buffer)
 {
         linebuffer_t *buf = cli_buffer;
 
         if (!cli_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 cli_buffer)
 {
         linebuffer_t *buf = cli_buffer;
 
         if (!cli_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 cli_buffer)
 {
         linebuffer_t *buf = cli_buffer;
 
         if (!cli_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 cli_buffer)
 {
         linebuffer_t *buf = cli_buffer;
 
         if (!cli_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 cli_buffer)
 {
         linebuffer_t *buf = cli_buffer;
 
         if (!cli_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 cli_buffer)
 {
         linebuffer_t *buf = cli_buffer;
         char *str, *s, **app, *items[MAX_PROMPT_ITEMS], szLine[STRSIZ];
         register int i, j;
         struct tagCommand *cmd, *c;
         int pos, ret = RETCODE_OK;
         int ign __attribute__((unused));
 
         if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
                 return RETCODE_ERR;
 
         str = e_strdup(buf->line_buf);
         if (!str)
                 return RETCODE_ERR;
         else {          else {
                if (BUFSIZ - 2 < len + cli_BufferLen) {                s = str;
                        if (BUFSIZ - 2 >= cli_BufferLen - cli_BufferPos + len) {                str_Trim(s);
                                cli_BufferLen -= cli_BufferPos;        }
                                memmove(cli_Buffer, cli_Buffer + cli_BufferPos, cli_BufferLen); 
                        } else 
                                cli_BufferLen = 0; 
   
                        cli_BufferPos = 0;        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 & (1 << 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 & (1 << 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);
                                   }
                   }
           }
   
                memcpy(cli_Buffer + cli_BufferLen, line, len);        /* completion show actions ... */
                cli_BufferLen += len;        if (j > 1 && c) {
                cli_Buffer[cli_BufferLen++] = '\r';                printfNL(buf, 0);
                cli_Buffer[cli_BufferLen++] = '\n';                ign = 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);
   
        fwrite(cli_Buffer, 1, cli_BufferLen, rl_outstream);                pos = buf->line_eol - buf->line_bol;
        if (!cli_pending_special_char) {
                fwrite("\r", 1, 1, rl_outstream);                buf->line_len += c->cmd_len + 1;
                if (*line)                buf->line_eol += c->cmd_len + 1;
                        add_history(line);
                 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);
         }          }
   
        free(line);endcomp:
end:        e_free(str);
        rl_callback_handler_remove();        return ret;
        if (cli_pending_special_char) {}
                fwrite(&cli_pending_special_char, 1, 1, rl_outstream);
                cli_pending_special_char = 0;static int
 bufHelp(int idx, void * __restrict cli_buffer)
 {
         linebuffer_t *buf = cli_buffer;
 
         if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
                 return RETCODE_ERR;
 
         cli_Cmd_Help(buf, buf->line_level, NULL);
 
         printfEOL(buf, buf->line_len - 1, 1);
         printfEOL(buf, -1, 1);
         return RETCODE_OK;
 }
 
 static int
 bufEndNode(int idx, void * __restrict cli_buffer)
 {
         linebuffer_t *buf = cli_buffer;
         char szPrompt[STRSIZ + 16] = {[0 ... STRSIZ + 15] = 0};
 
         if (!cli_buffer || idx < 0 || idx > MAX_BINDKEY)
                 return RETCODE_ERR;
 
         if (buf->line_level > 0) {
                 printfNL(cli_buffer, 0);
                 buf->line_level--;
                 snprintf(szPrompt, sizeof szPrompt, "%s{%d}> ", buf->line_porigin, buf->line_level);
                 cli_setPrompt(buf, szPrompt);
                 cli_Printf(buf, "Enter to config level %d\n", buf->line_level);
         }          }
        fflush(rl_outstream);
         return bufCLR(idx, cli_buffer);
 }  }
 #endif  
   
   
// cli_GetErrno() Get error code of last operation/*
inline int cli_GetErrno() * cli_Printf() - Send message to CLI session
  *
  * @cli_buffer = CLI buffer
  * @fmt = printf format string
  * @... = arguments defined in fmt
  * return: none
 */
 void
 cli_Printf(linebuffer_t * __restrict cli_buffer, char *fmt, ...)
 {  {
        return cli_Errno;        va_list lst;
         FILE *f;
 
         if (fmt) {
                 f = fdopen(cli_buffer->line_out, "a");
                 if (!f) {
                         LOGERR;
                         return;
                 }
 
                 va_start(lst, fmt);
                 vfprintf(f, fmt, lst);
                 va_end(lst);
 
                 fclose(f);
         } else
                 cli_SetErr(EINVAL, "Invalid input parameters ...");
 }  }
   
// io_GetError() Get error text of last operation/*
inline const char *cli_GetError() * cli_PrintHelp() - Print help screen
  *
  * @cli_buffer = CLI buffer
  * return: none
 */
 void
 cli_PrintHelp(linebuffer_t * __restrict cli_buffer)
 {  {
        return cli_Error;        if (cli_buffer) {
                 bufHelp(0, cli_buffer);
                 clrscrEOL(cli_buffer);
         } else
                 cli_SetErr(EINVAL, "Invalid input parameters ...");
 }  }
   
// cli_SetErr() Set error to variables for internal use!!!
inline void cli_SetErr(int eno, char *estr, ...)/*
  * cli_BindKey() - Bind function to key
  *
  * @key = key structure
  * @cli_buffer = CLI buffer
  * return: RETCODE_ERR error, RETCODE_OK ok, >0 bind at position
 */
 int
 cli_BindKey(bindkey_t * __restrict key, linebuffer_t * __restrict cli_buffer)
 {  {
        va_list lst;        register int i;
   
        cli_Errno = eno;        if (!key || !cli_buffer) {
        memset(cli_Error, 0, STRSIZ);                cli_SetErr(EINVAL, "Invalid input parameters ...");
        va_start(lst, estr);                return RETCODE_ERR;
        vsnprintf(cli_Error, STRSIZ, estr, lst);        }
        va_end(lst);
         for (i = 0; i < MAX_BINDKEY; i++)
                 if (key->key_len == cli_buffer->line_keys[i].key_len && 
                                 !memcmp(key->key_ch, cli_buffer->line_keys[i].key_ch, 
                                         key->key_len)) {
                         cli_buffer->line_keys[i].key_func = key->key_func;
                         return i;
                 }
 
         return RETCODE_OK;
 }  }
   
 // ------------------------------------------------------------  
   
 /*  /*
 * cli_Printf() Printf CLI features * cli_addCommand() - Add command to CLI session
 * @out = Output stream *
 * @csFormat = Printf format string * @cli_buffer = CLI buffer
 * return: -1 error, != -1 printed chars * @csCmd = Command name
  * @cliLevel = Level in CLI, -1 view from all levels, 0 hidden, >0 mask levels
  * @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
 */  */
inline int cli_Printf(FILE *out, const char *csFormat, ...)int
 cli_addCommand(linebuffer_t * __restrict cli_buffer, const char *csCmd, 
                 int cliLevel, cmd_func_t funcCmd, 
                 const char *csInfo, const char *csHelp)
 {  {
        va_list lst;        struct tagCommand *cmd;
        int ret; 
   
        va_start(lst, csFormat);        if (!cli_buffer || !csCmd) {
                 cli_SetErr(EINVAL, "Invalid input parameters ...");
                 return RETCODE_ERR;
         }
   
        ret = vfprintf(out, csFormat, lst);        cmd = e_malloc(sizeof(struct tagCommand));
        if (-1 == ret)        if (!cmd) {
                 LOGERR;                  LOGERR;
                   return RETCODE_ERR;
           } else
                   memset(cmd, 0, sizeof(struct tagCommand));
   
        va_end(lst);        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(&cli_buffer->line_cmds, cmd, cmd_next);
         return RETCODE_OK;
 }
 
 /*
  * cli_delCommand() - Delete command from CLI session
  *
  * @cli_buffer = CLI buffer
  * @csCmd = Command name
  * @cliLevel = Level in CLI, -1 view from all levels, 0 hidden, >0 mask levels
  * return: RETCODE_ERR error, RETCODE_OK ok
 */
 int
 cli_delCommand(linebuffer_t * __restrict cli_buffer, const char *csCmd, int cliLevel)
 {
         struct tagCommand *cmd;
         int ret = RETCODE_OK;
 
         if (!cli_buffer || !csCmd) {
                 cli_SetErr(EINVAL, "Invalid input parameters ...");
                 return RETCODE_ERR;
         }
 
         SLIST_FOREACH(cmd, &cli_buffer->line_cmds, cmd_next) 
                 if (cmd->cmd_level == cliLevel && !strcmp(cmd->cmd_name, csCmd)) {
                         ret = 1;
                         SLIST_REMOVE(&cli_buffer->line_cmds, cmd, tagCommand, cmd_next);
                         e_free(cmd);
                         break;
                 }
 
         return ret;          return ret;
 }  }
   
   /*
    * cli_updCommand() - Update command in CLI session
    *
    * @cli_buffer = CLI buffer
    * @csCmd = Command name
    * @cliLevel = Level in CLI, -1 view from all levels, 0 hidden, >0 mask levels
    * @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 cli_buffer, const char *csCmd, 
                   int cliLevel, cmd_func_t funcCmd, 
                   const char *csInfo, const char *csHelp)
   {
           struct tagCommand *cmd;
           int ret = RETCODE_OK;
   
           if (!cli_buffer || !csCmd) {
                   cli_SetErr(EINVAL, "Invalid input parameters ...");
                   return RETCODE_ERR;
           }
   
           SLIST_FOREACH(cmd, &cli_buffer->line_cmds, cmd_next)
                   if ((!cmd->cmd_level || cmd->cmd_level == cliLevel) && 
                                   !strcmp(cmd->cmd_name, csCmd)) {
                           if (!cmd->cmd_level)
                                   cmd->cmd_level = cliLevel;
                           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;
   }
   
   
 /*  /*
 * cliComp() Initialize completion CLI features * cli_addHistory() - Add line to history
 * @cmdComplete = Completion function *
 * @cmdEntry = Compentry function * @cli_buffer = CLI buffer
 * return: none * @str = Add custom text or if NULL use readed line from CLI buffer
  * return: RETCODE_ERR error, RETCODE_OK ok
 */  */
inline void cliComp(cli_Completion_t *cmdComplete, cli_CompEntry_t *cmdEntry)int
 cli_addHistory(linebuffer_t * __restrict cli_buffer, const char * __restrict str)
 {  {
        // command completon        struct tagHistory *h;
        rl_attempted_completion_function = cmdComplete;
        rl_completion_entry_function = cmdEntry;        if (!cli_buffer) {
                 cli_SetErr(EINVAL, "Invalid input parameters ...");
                 return RETCODE_ERR;
         }
 
         if (!(h = e_malloc(sizeof(struct tagHistory)))) {
                 LOGERR;
                 return RETCODE_ERR;
         } else
                 memset(h, 0, sizeof(struct tagHistory));
 
         if (str) {
                 if (!*str) {
                         e_free(h);
                         return RETCODE_OK;
                 }
 
                 h->hist_len = strlcpy(h->hist_line, str, BUFSIZ);
         } else {
                 if (!*cli_buffer->line_buf || cli_buffer->line_len < 2) {
                         e_free(h);
                         return RETCODE_OK;
                 }
 
                 memcpy(h->hist_line, cli_buffer->line_buf, (h->hist_len = cli_buffer->line_len));
                 str_Trim(h->hist_line);
                 h->hist_len = strlen(h->hist_line);
         }
 
         TAILQ_INSERT_HEAD(&cli_buffer->line_history, h, hist_next);
         return h->hist_len;
 }  }
   
 /*  /*
 * cliTTY() Initialize I/O TTY CLI features * cli_saveHistory() - Save history to file
 * @term = terminal name *
 * @inp = input handle * @cli_buffer = CLI buffer
 * @out = output handle * @histfile = History filename, if NULL will be use default name
 * @win = window size * @lines = Maximum history lines to save
 * return: -1 error, != -1 ok * return: RETCODE_ERR error, RETCODE_OK ok
 */  */
inline int cliTTY(const char *term, FILE *inp, FILE *out, struct winsize *win)int
 cli_saveHistory(linebuffer_t * __restrict cli_buffer, const char *histfile, int lines)
 {  {
        if (term)        FILE *f;
                rl_terminal_name = term;        mode_t mode;
         char szFName[MAXPATHLEN];
         struct tagHistory *h;
   
        if (inp)        if (!cli_buffer) {
                rl_instream = inp;                cli_SetErr(EINVAL, "Invalid input parameters ...");
        if (out)                return RETCODE_ERR;
                rl_outstream = out;        }
         if (!histfile)
                 strlcpy(szFName, HISTORY_FILE, MAXPATHLEN);
         else
                 strlcpy(szFName, histfile, MAXPATHLEN);
   
        if (win)        mode = umask(0177);
               if (ioctl(!rl_outstream ? STDOUT_FILENO : fileno(rl_outstream), TIOCSWINSZ, win) == -1) {        f = fopen(szFName, "w");
                       LOGERR;        if (!f) {
                       return -1;                LOGERR;
               }                return RETCODE_ERR;
         }
   
        return 0;        TAILQ_FOREACH(h, &cli_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_ReadHistory() Read CLI History from file * cli_loadHistory() - Load history from file
 * @csFile = history file name, if NULL default history name is ".aitcli.history" *
 * return: -1 error; != -1 readed ok * @cli_buffer = CLI buffer
  * @histfile = History filename, if NULL will be use default name
  * return: RETCODE_ERR error, RETCODE_OK ok
 */  */
inline int cli_ReadHistory(const char *csFile)int
 cli_loadHistory(linebuffer_t * __restrict cli_buffer, const char *histfile)
 {  {
        return read_history(!csFile ? ".aitcli.history" : csFile);        FILE *f;
         char szFName[MAXPATHLEN], buf[BUFSIZ];
         struct tagHistory *h;
 
         if (!cli_buffer) {
                 cli_SetErr(EINVAL, "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
                         str_Trim(buf);
 
                 if (!(h = e_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(&cli_buffer->line_history, h, hist_next);
         }
 
         fclose(f);
 
         return RETCODE_OK;
 }  }
   
 /*  /*
 * cli_WriteHistory() Write CLI History to file * cli_resetHistory() - Reset history search in CLI session
 * @csFile = history file name, if NULL default history name is ".aitcli.history" *
 * @lineNum = save number of history entry lines, if -1 all lines saved without limit * @cli_buffer = CLI buffer
 * return: -1 error; != -1 readed ok * return: none
 */  */
inline int cli_WriteHistory(const char *csFile, int lineNum)void
 cli_resetHistory(linebuffer_t * __restrict cli_buffer)
 {  {
        int ret;        cli_buffer->line_h = NULL;
        const char *psFile = !csFile ? ".aitcli.history" : csFile;}
   
         ret = write_history(psFile);  
         if (-1 != ret && -1 != lineNum)  
                 history_truncate_file(psFile, lineNum);  
   
        return ret;/*
  * cli_freeLine() - Clear entire line
  *
  * @cli_buffer = CLI buffer
  * return: RETCODE_ERR error, RETCODE_OK ok
 */
 int
 cli_freeLine(linebuffer_t * __restrict cli_buffer)
 {
         int code = RETCODE_ERR;
 
         if (cli_buffer) {
                 if (cli_buffer->line_buf)
                         e_free(cli_buffer->line_buf);
 
                 cli_buffer->line_buf = e_malloc(BUFSIZ);
                 if (cli_buffer->line_buf) {
                         memset(cli_buffer->line_buf, 0, BUFSIZ);
                         cli_buffer->line_eol = cli_buffer->line_bol;
                         cli_buffer->line_len = 1 + cli_buffer->line_eol;
 
                         code = RETCODE_OK;
                 } else
                         LOGERR;
         } else
                 cli_SetErr(EINVAL, "Invalid input parameters ...");
 
         return code;
 }  }
   
 /*  /*
 * cliNetInit() Initialize Readline if CLI bind to socket * cli_setPrompt() - Set new prompt for CLI session
 * @csProg = program name *
 * @pty = Master pty * @cli_buffer = CLI buffer
 * @term = stdin termios * @prompt = new text for prompt or if NULL disable prompt
  * return: none   * return: none
 */  */
void cliNetInit(const char *csProg, int pty, struct termios *term)void
 cli_setPrompt(linebuffer_t * __restrict cli_buffer, const char *prompt)
 {  {
        struct termios t;        if (cli_buffer) {
        int on = 1;                if (cli_buffer->line_prompt) {
                         e_free(cli_buffer->line_prompt);
                         cli_buffer->line_prompt = NULL;
                         cli_buffer->line_bol = 0;
                 }
   
        memset(&t, 0, sizeof t);                if (prompt) {
        if (term)                        cli_buffer->line_prompt = e_strdup(prompt);
                t = *term;                        if (cli_buffer->line_prompt) {
        else {                                cli_buffer->line_bol = strlen(cli_buffer->line_prompt);
                t.c_lflag = TTYDEF_LFLAG;                                cli_buffer->line_eol = cli_buffer->line_bol;
                t.c_iflag = TTYDEF_IFLAG;                                cli_buffer->line_len = 1 + cli_buffer->line_eol;
                t.c_oflag = TTYDEF_OFLAG;                        } else
                t.c_cflag = TTYDEF_CFLAG;                                LOGERR;
                cfsetspeed(&t, B9600);                }
         } else
                 cli_SetErr(EINVAL, "Invalid input parameters ...");
 }
 
 
 /*
  * cliEnd() - Clear data, Free resources and close CLI session
  *
  * @cli_buffer = CLI buffer
  * return: RETCODE_ERR error, RETCODE_OK ok
 */
 void
 cliEnd(linebuffer_t * __restrict cli_buffer)
 {
         struct tagHistory *h;
         struct tagCommand *c;
 
         if (cli_buffer) {
                 while ((c = SLIST_FIRST(&cli_buffer->line_cmds))) {
                         SLIST_REMOVE_HEAD(&cli_buffer->line_cmds, cmd_next);
                         e_free(c);
                 }
                 while ((h = TAILQ_FIRST(&cli_buffer->line_history))) {
                         TAILQ_REMOVE(&cli_buffer->line_history, h, hist_next);
                         e_free(h);
                 }
 
                 if (cli_buffer->line_prompt)
                         e_free(cli_buffer->line_prompt);
 
                 if (cli_buffer->line_keys)
                         e_free(cli_buffer->line_keys);
                 if (cli_buffer->line_buf)
                         e_free(cli_buffer->line_buf);
 
                 e_free(cli_buffer);
                 cli_buffer = NULL;
         } else
                 cli_SetErr(EINVAL, "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 *cli_buffer;
         bindkey_t *keys;
         register int i;
         char szPrompt[STRSIZ + 16] = {[0 ... STRSIZ + 15] = 0};
 
         /* init buffer */
         cli_buffer = e_malloc(sizeof(linebuffer_t));
         if (!cli_buffer) {
                 LOGERR;
                 return NULL;
         } else {
                 memset(cli_buffer, 0, sizeof(linebuffer_t));
 
                 cli_buffer->line_in = fin;
                 cli_buffer->line_out = fout;
 
                 TAILQ_INIT(&cli_buffer->line_history);
                 SLIST_INIT(&cli_buffer->line_cmds);
 
                 if (prompt) {
                         strlcpy(cli_buffer->line_porigin, prompt, sizeof cli_buffer->line_porigin);
                         snprintf(szPrompt, sizeof szPrompt, "%s{%d}> ", cli_buffer->line_porigin, cli_buffer->line_level);
                         cli_buffer->line_prompt = e_strdup(szPrompt);
                         if (!cli_buffer->line_prompt) {
                                 LOGERR;
                                 e_free(cli_buffer);
                                 return NULL;
                         } else
                                 cli_buffer->line_eol = cli_buffer->line_bol = 
                                         strlen(cli_buffer->line_prompt);
                 } else
                         cli_buffer->line_mode = LINEMODE_OVER;
         }          }
           cli_buffer->line_buf = e_malloc(BUFSIZ);
           if (!cli_buffer->line_buf) {
                   LOGERR;
                   if (cli_buffer->line_prompt)
                           e_free(cli_buffer->line_prompt);
                   e_free(cli_buffer);
                   return NULL;
           } else {
                   memset(cli_buffer->line_buf, 0, BUFSIZ);
                   cli_buffer->line_len = 1 + cli_buffer->line_eol;
           }
           keys = e_calloc(MAX_BINDKEY + 1, sizeof(bindkey_t));
           if (!keys) {
                   LOGERR;
                   if (cli_buffer->line_prompt)
                           e_free(cli_buffer->line_prompt);
                   e_free(cli_buffer->line_buf);
                   e_free(cli_buffer);
                   return NULL;
           } else
                   memset(keys, 0, sizeof(bindkey_t) * (MAX_BINDKEY + 1));
   
        t.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT);        /* add helper functions */
//      t.c_iflag &= ~(ICRNL | BRKINT | INPCK | ISTRIP | IXON);        cli_addCommand(cli_buffer, "exit", 1, cli_Cmd_Exit, "exit <cr>", "Exit from console");
        t.c_iflag &= ~ICRNL;        cli_addCommand(cli_buffer, "help", -1, cli_Cmd_Help, "help [command] <cr>", "Help screen");
         cli_addCommand(cli_buffer, "-------", -1, NULL, "-------------------------", NULL);
         cli_addCommand(cli_buffer, "where", -1, cli_Cmd_WhereAmI, "where <cr>", 
                         "Query current level of console");
         cli_addCommand(cli_buffer, "top", -1, cli_Cmd_Top, "top <cr>", "Top level of console");
         cli_addCommand(cli_buffer, "end", -1, cli_Cmd_End, "end <cr>", "End level of console");
         cli_addCommand(cli_buffer, "config", -1, cli_Cmd_Config, "config <cr>", 
                         "Config next level of console");
         cli_addCommand(cli_buffer, "-------", -1, 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 (cli_buffer->line_prompt && (i == *K_CTRL_H || i == *K_BACKSPACE))
                         keys[i].key_func = bufBS;
                 if (i == *K_CTRL_C)
                         keys[i].key_func = bufCLR;
                 if (cli_buffer->line_prompt && i == *K_CTRL_A)
                         keys[i].key_func = bufBEGIN;
                 if (cli_buffer->line_prompt && i == *K_CTRL_E)
                         keys[i].key_func = bufEND;
                 if (cli_buffer->line_prompt && i == *K_TAB)
                         keys[i].key_func = bufComp;
                 if (i == *K_CTRL_Z)
                         keys[i].key_func = bufEndNode;
                 if (i >= *K_SPACE && i < *K_BACKSPACE)
                         keys[i].key_func = bufCHAR;
                 if (i > *K_BACKSPACE && i < 0xff)
                         keys[i].key_func = bufCHAR;
                 if (cli_buffer->line_prompt && 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;
         if (cli_buffer->line_prompt)
                 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;
         if (cli_buffer->line_prompt)
                 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;
         if (cli_buffer->line_prompt)
                 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;
         if (cli_buffer->line_prompt)
                 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;
         if (cli_buffer->line_prompt)
                 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;
         if (cli_buffer->line_prompt)
                 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;
         if (cli_buffer->line_prompt)
                 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;
         if (cli_buffer->line_prompt)
                 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;
         if (cli_buffer->line_prompt)
                 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++;
 
         cli_buffer->line_keys = keys;
         return cli_buffer;
 }
 
 /*
  * cliSetLine() - Set CLI input line terminal
  *
  * @cli_buffer = CLI buffer
  * @old = Old terminal settings
  * return: -1 error or 0 ok
 */
 int
 cliSetLine(linebuffer_t * __restrict cli_buffer, struct termios * __restrict old)
 {
         struct termios t;
 
         memset(&t, 0, sizeof t);
         tcgetattr(cli_buffer->line_in, &t);
         if (old)
                 memcpy(old, &t, sizeof(struct termios));
         t.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | 
                         ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT);
         t.c_iflag |= IGNBRK;          t.c_iflag |= IGNBRK;
         t.c_cc[VMIN] = 1;          t.c_cc[VMIN] = 1;
         t.c_cc[VTIME] = 0;          t.c_cc[VTIME] = 0;
        tcsetattr(pty, TCSANOW, &t);        return tcsetattr(cli_buffer->line_in, TCSANOW, &t);
 }
   
        ioctl(pty, TIOCPKT, &on);/*
  * cliResetLine() - Reset CLI input line terminal
  *
  * @cli_buffer = CLI buffer
  * @old = Original terminal settings
  * return: -1 error or 0 ok
 */
 int
 cliResetLine(linebuffer_t * __restrict cli_buffer, struct termios * __restrict orig)
 {
         return tcsetattr(cli_buffer->line_in, TCSANOW, orig);
 }
   
        rl_readline_name = csProg;/*
        rl_variable_bind("editing-mode", "emacs"); * cliReadLine() - Read line from opened CLI session
  *
  * @cli_buffer = CLI buffer
  * @timeout = Session timeout (-1 infinit)
  * return: NULL if error or !=NULL readed line, must be e_free after use!
 */
 char *
 cliReadLine(linebuffer_t * __restrict cli_buffer, int timeout)
 {
         int code, readLen, ret;
         register int i;
         struct pollfd fds;
         char buf[BUFSIZ], *str = NULL;
   
        rl_instream = fdopen(pty, "r");        if (!cli_buffer) {
        rl_outstream = NULL;                cli_SetErr(EINVAL, "Invalid input parameters ...");
                 return NULL;
         } else if (timeout > 0)
                 timeout *= 1000;        /* convert from sec to ms */
 
         memset(&fds, 0, sizeof fds);
         fds.fd = cli_buffer->line_in;
         fds.events = POLLIN;
 
         printfCR(cli_buffer, 1);
         while (42) {
                 if ((ret = poll(&fds, 1, timeout)) < 1) {
                         if (!ret) {
                                 cli_buffer->line_kill = 1;
                                 if (str) {
                                         e_free(str);
                                         str = NULL;
                                 }
                         } else
                                 LOGERR;
                         return str;
                 }
 
                 memset(buf, 0, sizeof buf);
                 readLen = read(cli_buffer->line_in, buf, BUFSIZ);
                 if (readLen < 1) {
                         if (readLen)
                                 LOGERR;
                         return NULL;
                 }
 
 recheck:
                 for (code = RETCODE_OK, i = MAX_BINDKEY - 1; i > -1; i--)
                         if (readLen >= cli_buffer->line_keys[i].key_len && 
                                         !memcmp(cli_buffer->line_keys[i].key_ch, buf, 
                                                 cli_buffer->line_keys[i].key_len)) {
                                 readLen -= cli_buffer->line_keys[i].key_len;
                                 if (readLen)
                                         memmove(buf, buf + cli_buffer->line_keys[i].key_len, readLen);
                                 else
                                         memset(buf, 0, cli_buffer->line_keys[i].key_len);
 
                                 if (cli_buffer->line_keys[i].key_func)
                                         if ((code = cli_buffer->line_keys[i].key_func(i, cli_buffer)))
                                                 readLen = 0;
 
                                 if (readLen)
                                         goto recheck;
                                 else
                                         break;
                         }
 
                 if (code)
                         break;
         }
 
         if (code != RETCODE_ERR && code != RETCODE_EOF && cli_buffer->line_buf)
                 str = e_strdup(cli_buffer->line_buf);
         return str;
 }  }
   
   
 /*  /*
 * cliNetExec() Execute net CLI main loop * cliNetLoop() - CLI network main loop binded to socket
 * @cmdList = Commands list *
 * @csPrompt = Prompt text * @cli_buffer = CLI buffer
  * @csHistFile = History file name
  * @sock = client socket   * @sock = client socket
 * @term = stdin termios * @timeout = Session timeout (-1 infinit)
 * @win = window size of tty * return: RETCODE_ERR error, RETCODE_OK ok
 * return: -1 error, 0 = exit w/^+D, 1 done. 
 */  */
int cliNetExec(cliCommands_t *cmdList, const char *csPrompt, int sock, struct termios *term, struct winsize *win)int
 cliNetLoop(linebuffer_t * __restrict cli_buffer, const char *csHistFile, 
                 int sock, int timeout)
 {  {
        int pty, ret = 0, r, s, alen, attrlen, flg;        u_char buf[BUFSIZ];
         int pid, stat, pty, s, alen, flg, attrlen = 0, ret = 0;
         fd_set fds;          fd_set fds;
         struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 };          struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 };
         u_char buf[BUFSIZ];  
         struct telnetAttrs *a, Attr[10];          struct telnetAttrs *a, Attr[10];
   
        switch (forkpty(&pty, NULL, term, win)) {        switch ((pid = forkpty(&pty, NULL, NULL, NULL))) {
                 case -1:                  case -1:
                         LOGERR;                          LOGERR;
                         return -1;                          return -1;
                 case 0:                  case 0:
                        close(sock);                        if (!cli_buffer) {
                                 cli_SetErr(EINVAL, "Invalid input parameters ...");
                                 return -1;
                         } else
                                 close(sock);
   
//                      rl_prep_term_function = cli_Null_Prep_Term;                        ret = cliLoop(cli_buffer, csHistFile, timeout) < 0 ? 1 : 0;
                        rl_getc_function = cli_Net_rl_GetCh;                        cliEnd(cli_buffer);
   
                         cliNetInit(getprogname(), STDIN_FILENO, term);  
                         ret = cliExec(cmdList, csPrompt) < 0 ? 1 : 0;  
                         /* spawn Shell mode */  
                         /*  
                         execl("/bin/tcsh", "tcsh", NULL);  
                         */  
                         _exit(ret);                          _exit(ret);
                 default:                  default:
                        rl_prep_term_function = cli_Null_Prep_Term;                        cli_telnet_SetCmd(Attr + 0, DO, TELOPT_TTYPE);
                        cli_telnet_SetCmd(Attr + 1, WILL, TELOPT_ECHO);
                        cliNetInit(getprogname(), pty, term);                        cli_telnet_Set_SubOpt(Attr + 2, TELOPT_LFLOW, LFLOW_OFF, NULL, 0);
                        cli_telnet_Set_SubOpt(Attr + 3, TELOPT_LFLOW, LFLOW_RESTART_XON, NULL, 0);
                        /* spawn Shell mode */                        cli_telnet_SetCmd(Attr + 4, DO, TELOPT_LINEMODE);
                        /*                        if ((ret = cli_telnetSend(sock, Attr, 5, NULL, 0, 0)) == -1)
                        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); 
                        */ 
                        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;                                  return -1;
                        } else                        else
                                 flg = 0;                                  flg = 0;
   
                         while (42) {                          while (42) {
                                   if (waitpid(pid, &stat, WNOHANG))
                                           break;
   
                                 FD_ZERO(&fds);                                  FD_ZERO(&fds);
                                 FD_SET(sock, &fds);                                  FD_SET(sock, &fds);
                                 FD_SET(pty, &fds);                                  FD_SET(pty, &fds);
Line 334  int cliNetExec(cliCommands_t *cmdList, const char *csP Line 1439  int cliNetExec(cliCommands_t *cmdList, const char *csP
                                         break;                                          break;
                                 }                                  }
   
                                 r = FD_ISSET(sock, &fds) ? sock : pty;  
                                 s = FD_ISSET(sock, &fds) ? pty : sock;                                  s = FD_ISSET(sock, &fds) ? pty : sock;
   
                                if ((ret = telnetRecv(r, &a, &alen, buf, BUFSIZ)) < 0) {                                if (FD_ISSET(sock, &fds)) {
                                         memset(buf, 0, BUFSIZ);
                                         if ((ret = cli_telnetRecv(sock, &a, &alen, buf, BUFSIZ)) < 0) {
                                                 if (a)
                                                         e_free(a);
 
                                                 if (-2 == ret)
                                                         continue;
                                                 // EOF
                                                 if (-3 == ret)
                                                         shutdown(sock, SHUT_RD);
                                                 break;
                                         }
                                         attrlen = 0;
                                         if (1 == flg && alen) {
                                                 cli_telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_SGA);
                                                 cli_telnet_SetCmd(&Attr[attrlen++], DO, TELOPT_ECHO);
                                         }
                                         if (2 == flg && alen) {
                                                 cli_telnet_SetCmd(&Attr[attrlen++], WILL, TELOPT_ECHO);
                                                 cli_telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW, 
                                                                 LFLOW_OFF, NULL, 0);
                                                 cli_telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW, 
                                                                 LFLOW_RESTART_XON, NULL, 0);
                                                 cli_telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_LINEMODE);
                                         }
                                         if (a)                                          if (a)
                                                free(a);                                                e_free(a);
   
                                        if (-2 == ret)                                        if ((ret = write(pty, buf, ret)) == -1) {
                                                continue;                                                LOGERR;
                                        // EOF                                                break;
                                        if (-3 == ret) 
                                                shutdown(r, 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 = telnetSend(s, Attr, pty == s ? 0 : attrlen, buf, ret, 0)) == -1) {                                if (FD_ISSET(pty, &fds)) {
                                        cli_Errno = telnet_GetErrno();                                        memset(buf, 0, BUFSIZ);
                                        strlcpy(cli_Error, telnet_GetError(), STRSIZ);                                        if ((ret = read(pty, buf, BUFSIZ)) < 1) {
                                        break;                                                if (ret)
                                } else                                                        LOGERR;
                                        flg++;                                                break;
                                         }
 
                                         if ((ret = cli_telnetSend(sock, Attr, pty == s ? 0 : attrlen, 
                                                                         buf, ret, 0)) == -1)
                                                 break;
                                         else
                                                 flg++;
                                 }
                         }                          }
   
                         close(pty);                          close(pty);
Line 383  int cliNetExec(cliCommands_t *cmdList, const char *csP Line 1499  int cliNetExec(cliCommands_t *cmdList, const char *csP
 }  }
   
 /*  /*
 * cliExec() Execute CLI main loop * cliRun() - CLI run command line
 * @cmdList = Commands list *
 * @csPrompt = Prompt text * @cli_buffer = CLI buffer
 * return: -1 error, 0 = exit w/^+D, 1 done. * @psInput = Input command line
  * @prompt = Display prompt after command
  * return: RETCODE_ERR error, RETCODE_OK ok
 */  */
int cliExec(cliCommands_t *cmdList, const char *csPrompt)int
 cliRun(linebuffer_t * __restrict cli_buffer, char *psInput, int prompt)
 {  {
         char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];          char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];
        int ret = 0;        int ret = RETCODE_OK;
        register int i;        struct tagCommand *cmd;
        cliCommands_t *cmd = NULL; 
        FILE *out; 
   
        inline int inline_help()        if (!psInput)
        {                return RETCODE_ERR;
                cli_Cmd_Help(cmdList ? cmdList : cli_stdCmds, -1, out, NULL);        else
                rl_on_new_line();                line = psInput;
                return 0;
         // clear whitespaces
         for (s = line; isspace((int) *s); s++);
         if (*s) {
                 for (t = s + strlen(s) - 1; t > s && isspace((int) *t); t--);
                 *++t = 0;
         }          }
   
        char **cli_stdCompletion(const char *text, int start, int end)        if (*s) {
        {                memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
                register int i;                for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && 
                char **matches = NULL;                                (*app = strsep(&s, " \t")); *app ? app++ : app);
   
                char *cmdCompGet(const char *text, int state)                // exec_cmd ...
                {                SLIST_FOREACH(cmd, &cli_buffer->line_cmds, cmd_next) {
                        int len = strlen(text);                        if (!(cmd->cmd_level & (1 << cli_buffer->line_level)))
                                continue;
                        for (i = state; cmdList[i].cmd_name; i++) {                        if (*items[0] && !strncmp(cmd->cmd_name, items[0], strlen(items[0])))
                                if (strncmp(cmdList[i].cmd_name, "---", 3) &&                                 break;
                                                !strncmp(cmdList[i].cmd_name, text, len)) 
                                        return strdup(cmdList[i].cmd_name); 
                        } 
 
                        return NULL; 
                 }                  }
   
                if (!start)                if (!cmd) {
                        matches = rl_completion_matches(text, cmdCompGet);                        cli_Printf(cli_buffer, "%sCommand '%s' not found!\n", 
                else                                        cli_buffer->line_prompt ? "\n" : "", items[0]);
                        for (i = 0; cmdList[i].cmd_name; i++) {                        ret = RETCODE_ERR;
                                if (!cmdList[i].cmd_comp)                } else
                                        continue;                        if (cmd->cmd_func) {
                                if (!strncmp(rl_line_buffer, cmdList[i].cmd_name, strlen(cmdList[i].cmd_name)))                                if (prompt && cli_buffer->line_prompt)
                                        matches = rl_completion_matches(text, cmdList[i].cmd_comp);                                        cli_Printf(cli_buffer, "\n");
                                 ret = cmd->cmd_func(cli_buffer, 
                                                 cli_buffer->line_level, items);
                         } else if (prompt) {
                                 clrscrEOL(cli_buffer);
                                 printfCR(cli_buffer, 1);
                         }                          }
   
                 return matches;  
         }          }
         char *cli_stdCompEntry(const char *ignore, int invoking_key)  
         {  
                 return NULL;  
         }  
   
        /* --- main body of CLI --- */        return ret;
 }
   
        out = rl_outstream;/*
        if (!out) * cliLoop() - CLI main loop
                out = stdout; *
  * @cli_buffer = CLI buffer
  * @csHistFile = History file name
  * @timeout = Session timeout (-1 infinit)
  * return: RETCODE_ERR error, RETCODE_OK ok
 */
 int
 cliLoop(linebuffer_t * __restrict cli_buffer, const char *csHistFile, int timeout)
 {
         char *line;
         int ret = RETCODE_OK;
         struct termios t;
   
        rl_bind_key('?', inline_help);        /* --- main body of CLI --- */
        if (!rl_attempted_completion_function)         cliSetLine(cli_buffer, &t);
                cliComp(cli_stdCompletion, cli_stdCompEntry); 
   
           if (cli_loadHistory(cli_buffer, csHistFile) == RETCODE_ERR)
                   return RETCODE_ERR;
   
         do {          do {
                line = readline(csPrompt);                line = cliReadLine(cli_buffer, timeout);
                if (!line) {    // ^+d                if (!line) {
                        cli_Printf(out, "\n");                        printfNL(cli_buffer, 0);
                         break;                          break;
                }                } else
                // clear whitespaces                        cli_addHistory(cli_buffer, NULL);
                for (s = line; isspace(*s); s++); 
                if (*s) { 
                        for (t = s + strlen(s) - 1; t > s && isspace(*t); t--); 
                        *++t = 0; 
                } 
   
                if (*s) {                ret = cliRun(cli_buffer, line, 42);
                        add_history(s); 
   
                        memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);                cli_freeLine(cli_buffer);
                        for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t"));                 cli_resetHistory(cli_buffer);
                                        *app ? app++ : app);                e_free(line);
         } while (!cli_buffer->line_kill);
   
                        /*        cli_saveHistory(cli_buffer, csHistFile, HISTORY_LINES);
                        for (i = 0; i < MAX_PROMPT_ITEMS; i++) 
                                cli_Printf(out, "i=%d %s\n", i, items[i]); 
                                */ 
   
                        // exec_cmd ...        /* --- restore tty --- */
                        for (cmd = NULL, i = 0; cmdList[i].cmd_name; i++)        cliResetLine(cli_buffer, &t);
                                if (*items[0] && !strncmp(cmdList[i].cmd_name, items[0], strlen(items[0]))) { 
                                        cmd = &cmdList[i]; 
                                        break; 
                                } 
                        if (!cmd) { 
                                cli_Printf(out, "Command '%s' not found!\n", items[0]); 
                                ret = -1; 
                        } else 
                                ret = cmd->cmd_func(cmdList, i, out, items); 
                } 
 
                free(line); 
        } while (ret < 1); 
   
         return ret;          return ret;
 }  }

Removed from v.1.1.1.1.2.15  
changed lines
  Added in v.1.20


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>