Diff for /libaitcli/src/aitcli.c between versions 1.1 and 1.5

version 1.1, 2010/04/16 13:20:29 version 1.5, 2012/07/22 22:37:08
Line 5 Line 5
 * $Author$  * $Author$
 * $Id$  * $Id$
 *  *
*************************************************************************/**************************************************************************
 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>
 
 Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
         by Michael Pounov <misho@elwix.org>.  All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
 1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
 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 "global.h"
   #include "cli.h"
   
   
 #pragma GCC visibility push(hidden)  #pragma GCC visibility push(hidden)
   
 cliCommands_t cli_stdCmds[] = {  
         { "test", cli_Cmd_Unsupported, "Test - Don`t use default command structure!", "test <cr>", cli_Comp_Filename },   
         { "-------", NULL, "---------------------", NULL, NULL },   
         { "help", cli_Cmd_Help, "Help screen", "help [command] <cr>", NULL },   
         { "exit", cli_Cmd_Exit, "Exit from console", "exit <cr>", NULL },   
         { NULL, NULL, NULL, NULL }  
 };  
   
 // ------------------------------------------------  // ------------------------------------------------
   
 int cli_Errno;  int cli_Errno;
Line 26  char cli_Error[STRSIZ]; Line 56  char cli_Error[STRSIZ];
   
 #pragma GCC visibility pop  #pragma GCC visibility pop
   
   
 // cli_GetErrno() Get error code of last operation  // cli_GetErrno() Get error code of last operation
inline int cli_GetErrno()inline int
 cli_GetErrno()
 {  {
         return cli_Errno;          return cli_Errno;
 }  }
   
 // io_GetError() Get error text of last operation  // io_GetError() Get error text of last operation
inline const char *cli_GetError()inline const char *
 cli_GetError()
 {  {
         return cli_Error;          return cli_Error;
 }  }
   
 // cli_SetErr() Set error to variables for internal use!!!  // cli_SetErr() Set error to variables for internal use!!!
inline void cli_SetErr(int eno, char *estr, ...)inline void
 cli_SetErr(int eno, char *estr, ...)
 {  {
         va_list lst;          va_list lst;
   
Line 53  inline void cli_SetErr(int eno, char *estr, ...) Line 85  inline void cli_SetErr(int eno, char *estr, ...)
   
 // ------------------------------------------------------------  // ------------------------------------------------------------
   
   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 = io_strdup(buf->line_buf);
           if (!str)
                   return RETCODE_ERR;
           else {
                   s = str;
                   io_TrimStr(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:
           io_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() Printf CLI features * cli_Printf() Send message to CLI session
 * @out = Output stream * @buffer = CLI buffer
 * @csFormat = Printf format string * @fmt = printf format string
  * @... = arguments defined in fmt
  * return: none   * return: none
 */  */
inline int cli_Printf(FILE *out, const char *csFormat, ...)inline void
 cli_Printf(linebuffer_t * __restrict buffer, char *fmt, ...)
 {  {
         va_list lst;          va_list lst;
        int ret;        FILE *f;
   
        va_start(lst, csFormat);        if (fmt) {
                 f = fdopen(buffer->line_out, "a");
                 if (!f) {
                         LOGERR;
                         return;
                 }
   
        ret = vfprintf(out, csFormat, lst);                va_start(lst, fmt);
        if (-1 == ret)                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 = io_malloc(sizeof(struct tagCommand));
         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(&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);
                         io_free(cmd);
                         break;
                 }
 
         return ret;          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;
   }
   
   
 /*  /*
 * cliComp() Initialize completion CLI features * cli_addHistory() Add line to history
 * @cmdComplete = Completion function * @buffer = CLI buffer
 * @cmdEntry = Compentry function * @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 = io_malloc(sizeof(struct tagHistory)))) {
                 LOGERR;
                 return RETCODE_ERR;
         } else
                 memset(h, 0, sizeof(struct tagHistory));
 
         if (str) {
                 if (!*str) {
                         io_free(h);
                         return RETCODE_OK;
                 }
 
                 h->hist_len = strlcpy(h->hist_line, str, BUFSIZ);
         } else {
                 if (!*buffer->line_buf || buffer->line_len < 2) {
                         io_free(h);
                         return RETCODE_OK;
                 }
 
                 memcpy(h->hist_line, buffer->line_buf, (h->hist_len = buffer->line_len));
                 io_TrimStr(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(buf);
 
                 if (!(h = io_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   * return: none
 */  */
inline void cliComp(cli_Completion_t *cmdComplete, cli_CompEntry_t *cmdEntry)inline void
 cli_resetHistory(linebuffer_t * __restrict buffer)
 {  {
        // command completon        buffer->line_h = NULL;
        rl_attempted_completion_function = cmdComplete; 
        rl_completion_entry_function = cmdEntry; 
 }  }
   
   
 /*  /*
 * cliExec() Execute CLI main loop * cli_freeLine() Clear entire line
 * @cmdList = Commands list * @buffer = CLI buffer
 * @out = Output handle * return: RETCODE_ERR error, RETCODE_OK ok
 * @csPrompt = Prompt text 
 * return: -1 error, 0 = exit w/^+D, 1 done. 
 */  */
int cliExec(cliCommands_t *cmdList, FILE *out, const char *csPrompt)inline int
 cli_freeLine(linebuffer_t * __restrict buffer)
 {  {
        char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];        int code = RETCODE_ERR;
        int ret = 0;
         if (buffer) {
                 if (buffer->line_buf)
                         io_free(buffer->line_buf);
 
                 buffer->line_buf = io_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) {
                         io_free(buffer->line_prompt);
                         buffer->line_prompt = NULL;
                         buffer->line_bol = 0;
                 }
 
                 if (prompt) {
                         buffer->line_prompt = io_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);
                         io_free(c);
                 }
                 while ((h = TAILQ_FIRST(&buffer->line_history))) {
                         TAILQ_REMOVE(&buffer->line_history, h, hist_next);
                         io_free(h);
                 }
 
                 if (buffer->line_prompt)
                         io_free(buffer->line_prompt);
 
                 if (buffer->line_keys)
                         io_free(buffer->line_keys);
                 if (buffer->line_buf)
                         io_free(buffer->line_buf);
 
                 io_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;          register int i;
         cliCommands_t *cmd = NULL;  
   
        inline int inline_help()        /* init buffer */
        {        buffer = io_malloc(sizeof(linebuffer_t));
                cli_Cmd_Help(cmdList ? cmdList : cli_stdCmds, -1, out, NULL);        if (!buffer) {
                rl_on_new_line();                LOGERR;
                return 0;                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 = io_strdup(prompt);
                         if (!buffer->line_prompt) {
                                 LOGERR;
                                 io_free(buffer);
                                 return NULL;
                         } else
                                 buffer->line_eol = buffer->line_bol = strlen(buffer->line_prompt);
                 }
         }          }
           buffer->line_buf = io_malloc(BUFSIZ);
           if (!buffer->line_buf) {
                   LOGERR;
                   if (buffer->line_prompt)
                           io_free(buffer->line_prompt);
                   io_free(buffer);
                   return NULL;
           } else {
                   memset(buffer->line_buf, 0, BUFSIZ);
                   buffer->line_len = 1 + buffer->line_eol;
           }
           keys = io_calloc(MAX_BINDKEY + 1, sizeof(bindkey_t));
           if (!keys) {
                   LOGERR;
                   if (buffer->line_prompt)
                           io_free(buffer->line_prompt);
                   io_free(buffer->line_buf);
                   io_free(buffer);
                   return NULL;
           } else
                   memset(keys, 0, sizeof(bindkey_t) * (MAX_BINDKEY + 1));
   
        char **cli_stdCompletion(const char *text, int start, int end)        /* add helper functions */
        {        cli_addCommand(buffer, "exit", 0, cli_Cmd_Exit, "exit <cr>", "Exit from console");
                register int i;        cli_addCommand(buffer, "help", 0, cli_Cmd_Help, "help [command] <cr>", "Help screen");
                char **matches = NULL;        cli_addCommand(buffer, "-------", 0, NULL, "-------------------------", NULL);
   
                char *cmdCompGet(const char *text, int state)        /* fill key bindings */
                {        // ascii chars & ctrl+chars
                        int len = strlen(text);        for (i = 0; i < 256; i++) {
                 *keys[i].key_ch = (u_char) i;
                 keys[i].key_len = 1;
   
                        for (i = state; cmdList[i].cmd_name; i++) {                if (!i || i == *K_CTRL_D)
                                if (strncmp(cmdList[i].cmd_name, "---", 3) &&                         keys[i].key_func = bufEOF;
                                                !strncmp(cmdList[i].cmd_name, text, len))                if (i == *K_CTRL_M || i == *K_CTRL_J)
                                        return strdup(cmdList[i].cmd_name);                        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;
         }
   
                        return NULL;        // 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 io_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;
                 }                  }
   
                if (!start)                memset(buf, 0, sizeof buf);
                        matches = rl_completion_matches(text, cmdCompGet);                readLen = read(buffer->line_in, buf, BUFSIZ);
                else                if (readLen == -1) {
                        for (i = 0; cmdList[i].cmd_name; i++) {                        LOGERR;
                                if (!cmdList[i].cmd_comp)                        return str;
                                        continue;                }
                                if (!strncmp(rl_line_buffer, cmdList[i].cmd_name, strlen(cmdList[i].cmd_name)))                if (!readLen) {
                                        matches = rl_completion_matches(text, cmdList[i].cmd_comp);                        if (buffer->line_buf)
                                 str = io_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;
                         }                          }
   
                return matches;                if (code)
                         break;
         }          }
        char *cli_stdCompEntry(const char *ignore, int invoking_key)
        {        if (code != RETCODE_ERR && code != RETCODE_EOF && buffer->line_buf)
                return NULL;                str = io_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:
                         cli_telnet_SetCmd(Attr + 0, DO, TELOPT_TTYPE);
                         cli_telnet_SetCmd(Attr + 1, WILL, TELOPT_ECHO);
                         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);
                         cli_telnet_SetCmd(Attr + 4, DO, TELOPT_LINEMODE);
                         if ((ret = cli_telnetSend(sock, Attr, 5, NULL, 0, 0)) == -1)
                                 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 = cli_telnetRecv(sock, &a, &alen, buf, BUFSIZ)) < 0) {
                                                 if (a)
                                                         io_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)
                                                 io_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 = cli_telnetSend(sock, Attr, pty == s ? 0 : attrlen, 
                                                                         buf, ret, 0)) == -1)
                                                 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 --- */          /* --- main body of CLI --- */
           cliInitLine(buffer);
   
        rl_bind_key('?', inline_help);        if (cli_loadHistory(buffer, csHistFile) == RETCODE_ERR)
        if (!rl_attempted_completion_function)                 return RETCODE_ERR;
                cliComp(cli_stdCompletion, cli_stdCompEntry); 
   
         do {          do {
                line = readline(csPrompt);                line = cliReadLine(buffer);
                if (!line) {    // ^+d                if (!line) {
                        cli_Printf(out, "\n");                        printfNL(buffer, 0);
                         break;                          break;
                }                } else
                         cli_addHistory(buffer, NULL);
                 // clear whitespaces                  // clear whitespaces
                for (s = line; isspace(*s); s++);                for (s = line; isspace((int) *s); s++);
                 if (*s) {                  if (*s) {
                        for (t = s + strlen(s) - 1; t > s && isspace(*t); t--);                        for (t = s + strlen(s) - 1; t > s && isspace((int) *t); t--);
                         *++t = 0;                          *++t = 0;
                 }                  }
   
                 if (*s) {                  if (*s) {
                         add_history(s);  
   
                         memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);                          memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
                         for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t"));                           for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t")); 
                                         *app ? app++ : app);                                          *app ? app++ : app);
   
                         /*  
                         for (i = 0; i < MAX_PROMPT_ITEMS; i++)  
                                 cli_Printf(out, "i=%d %s\n", i, items[i]);  
                                 */  
   
                         // exec_cmd ...                          // exec_cmd ...
                        for (cmd = NULL, i = 0; cmdList[i].cmd_name; i++)                        i = 0;
                                if (*items[0] && !strncmp(cmdList[i].cmd_name, items[0], strlen(items[0]))) {                        SLIST_FOREACH(cmd, &buffer->line_cmds, cmd_next) {
                                        cmd = &cmdList[i];                                if (*items[0] && !strncmp(cmd->cmd_name, items[0], strlen(items[0])))
                                         break;                                          break;
                                }                                else
                                         i++;
                         }
 
                         if (!cmd) {                          if (!cmd) {
                                cli_Printf(out, "Command '%s' not found!\n", items[0]);                                cli_Printf(buffer, "\nCommand '%s' not found!\n", items[0]);
                                 ret = -1;                                  ret = -1;
                         } else                          } else
                                ret = cmd->cmd_func(cmdList, i, out, items);                                if (cmd->cmd_func) {
                                         cli_Printf(buffer, "\n");
                                         ret = cmd->cmd_func(buffer, i, items);
                                 } else {
                                         clrscrEOL(buffer);
                                         printfCR(buffer, 1);
                                 }
                 }                  }
   
                free(line);                cli_freeLine(buffer);
                 cli_resetHistory(buffer);
                 io_free(line);
         } while (ret < 1);          } while (ret < 1);
   
           cli_saveHistory(buffer, csHistFile, HISTORY_LINES);
         return ret;          return ret;
 }  }

Removed from v.1.1  
changed lines
  Added in v.1.5


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