Logo Search packages:      
Sourcecode: netams version File versions  Download package

libcli.c

#define _GNU_SOURCE
#include "config.h"
#include "lib.h"
#include "libcli.h"
// vim:sw=8 ts=8

#define MAX_COMPLETIONS 128

enum cli_states
{
      STATE_LOGIN,
      STATE_PASSWORD,
      STATE_NORMAL,
      STATE_ENABLE_PASSWORD,
      STATE_ENABLE,
};

struct unp
{
      char *username;
      char *password;
      struct unp *next;
};

struct cli_filter_cmds
{
      char *cmd;
      char *help;
};

//int cli_run_command(struct cli_def *cli, char *command);
int cli_match_filter_init(struct cli_def *cli, int argc, char **argv, struct cli_filter *filt);
int cli_range_filter_init(struct cli_def *cli, int argc, char **argv, struct cli_filter *filt);
int cli_count_filter_init(struct cli_def *cli, int argc, char **argv, struct cli_filter *filt);
int cli_match_filter(struct cli_def *cli, char *string, void *data);
int cli_range_filter(struct cli_def *cli, char *string, void *data);
int cli_count_filter(struct cli_def *cli, char *string, void *data);

static struct cli_filter_cmds filter_cmds[] =
{
      { "begin",   "Begin with lines that match" },
      { "between", "Between lines that match" },
      { "count",   "Count of lines"   },
      { "exclude", "Exclude lines that match" },
      { "include", "Include lines that match" },
      { "grep",    "Include lines that match regex (options: -v, -i, -e)" },
      { "egrep",   "Include lines that match extended regex" },
      { NULL, NULL}
};

char *cli_command_name(struct cli_def *cli, struct cli_command *command)
{
      char *name = cli->commandname;
      char *o;

      if (name) free(name);
      name = calloc(1,1);

      while (command)
      {
            o = name;
            asprintf(&name, "%s %s", command->command, o);
            command = command->parent;
            free(o);
      }
      cli->commandname = name;
      return name;
}

void cli_set_auth_callback(struct cli_def *cli, int (*auth_callback)(char *, char *))
{
      cli->auth_callback = auth_callback;
}

void cli_set_enable_callback(struct cli_def *cli, int (*enable_callback)(char *))
{
      cli->enable_callback = enable_callback;
}

void cli_allow_user(struct cli_def *cli, const char *username, const char *password)
{
      struct unp *u, *n;
      n = malloc(sizeof(struct unp));
      n->username = strdup(username);
      n->password = strdup(password);
      n->next = NULL;

      if (!cli->users)
            cli->users = n;
      else
      {
            for (u = cli->users; u && u->next; u = u->next);
            if (u) u->next = n;
      }
}

void cli_allow_enable(struct cli_def *cli, const char *password)
{
      if (cli->enable_password)
            free(cli->enable_password);

      cli->enable_password = strdup(password);
}

void cli_deny_user(struct cli_def *cli, const char *username)
{
      struct unp *u, *p = NULL;
      if (!cli->users) return;
      for (u = cli->users; u; u = u->next)
      {
            if (strcmp(username, u->username) == 0)
            {
                  if (p)
                        p->next = u->next;
                  else
                        cli->users = u->next;
                  free(u->username);
                  free(u->password);
                  free(u);
                  break;
            }
            p = u;
      }
}

void cli_set_banner(struct cli_def *cli, const char *banner)
{
      if (cli->banner)
      {
            free(cli->banner);
            cli->banner = 0;
      }

      if (banner && *banner)
            cli->banner = strdup(banner);
}

void cli_set_hostname(struct cli_def *cli, const char *hostname)
{
      if (cli->hostname)
      {
            free(cli->hostname);
            cli->hostname = 0;
      }

      if (hostname && *hostname)
            cli->hostname = strdup(hostname);
}

void cli_set_promptchar(struct cli_def *cli, const char *promptchar)
{
      if (cli->promptchar) free(cli->promptchar);
      cli->promptchar = strdup(promptchar);
}

int cli_set_privilege(struct cli_def *cli, int privilege)
{
      int old = cli->privilege;
      cli->privilege = privilege;

      if (privilege != old)
            cli_set_promptchar(cli, privilege == PRIVILEGE_PRIVILEGED ? "# " : "> ");

      return old;
}

void cli_set_modestring(struct cli_def *cli, const char *modestring)
{
      if (cli->modestring)
      {
            free(cli->modestring);
            cli->modestring = NULL;
      }
      if (modestring) cli->modestring = strdup(modestring);
}

int cli_set_configmode(struct cli_def *cli, int mode, const char *config_desc)
{
      int old = cli->mode;
      cli->mode = mode;

      if (mode != old)
      {
            if (!cli->mode)
            {
                  // Not config mode
                  cli_set_modestring(cli, NULL);
            }
            else if (config_desc && *config_desc)
            {
                  char string[64];
                  snprintf(string, sizeof(string), "(config-%s)", config_desc);
                  cli_set_modestring(cli, string);
            }
            else
            {
                  cli_set_modestring(cli, "(config)");
            }
      }

      return old;
}

int cli_build_shortest(struct cli_def *cli, struct cli_command *commands)
{
      struct cli_command *c, *p;

      for (c = commands; c; c = c->next)
      {
            for (c->unique_len = 1; c->unique_len <= strlen(c->command); c->unique_len++)
            {
                  for (p = commands; p; p = p->next)
                  {
                        if (c == p) continue;
                        if ((c->mode == p->mode || c->mode == MODE_ANY || p->mode == MODE_ANY) &&
                              c->privilege == p->privilege &&
                              strncmp(p->command, c->command, c->unique_len) == 0)
                                    break; //foundmatch
                              
                  }
                  if(!p) break; //check foundmatch
            }
            if (c->children) cli_build_shortest(cli, c->children);
      }
      return CLI_OK;
}

struct cli_command *cli_register_command(struct cli_def *cli, struct cli_command *parent, const char *command, int (*callback)(struct cli_def *cli, const char *, char **, int), int privilege, int mode, const char *help)
{
      struct cli_command *c, *p;
      struct cli_command **cmd;

      if (!command) return NULL;
      
      if (parent)
            cmd = &parent->children;
      else
            cmd = &cli->commands;
                  
      for (p = (*cmd); p && p->next ; p = p->next)
      {
            if (mode == p->mode &&
                  privilege == p->privilege &&
                  strcasecmp(p->command, command) == 0)
            {
                  //this command already registered
                        return p;
            }
      }
      
      //new command
      if (!(c = calloc(sizeof(struct cli_command), 1))) return NULL;

      c->callback = callback;
      c->next = NULL;
      c->command = strdup(command);
      c->parent = parent;
      c->privilege = privilege;
      c->mode = mode;
      if (help) c->help = strdup(help);
      
      if (p)
            p->next = c;
      else
            (*cmd) = c;       

      cli_build_shortest(cli, (parent) ? parent : cli->commands);
      return c;
}

void cli_free_command(struct cli_command *cmd) {
      struct cli_command *c,*p;

      for(c = cmd->children; c;) {
            p = c->next;
            cli_free_command(c);
            c=p;
      }
      
      free(cmd);
}

int cli_unregister_command(struct cli_def *cli, const char *command)
{
      struct cli_command *c, *p = NULL;

      if (!command) return -1;
      if (!cli->commands) return CLI_OK;

      for (c = cli->commands; c; c = c->next)
      {
            if (strcmp(c->command, command) == 0)
            {
                  if (p)
                        p->next = c->next;
                  else
                        cli->commands = c->next;
                  
                  cli_free_command(c);
                  return CLI_OK;
            }
            p = c;
      }

      return CLI_OK;
}

int cli_show_help(struct cli_def *cli, struct cli_command *c)
{
      struct cli_command *p;
      for (p = c; p; p = p->next)
      {
            if (p->command && p->callback && cli->privilege >= p->privilege &&
                        (p->mode == cli->mode || p->mode == MODE_ANY))
            {
                  cli_error(cli, "  %-20s %s", cli_command_name(cli, p), p->help ? : "");
            }
            if (p->children)
            {
                  cli_show_help(cli, p->children);
            }
      }
      return CLI_OK;
}

int cli_int_enable(struct cli_def *cli, const char * command, char *argv[], int argc)
{

      if (cli->privilege == PRIVILEGE_PRIVILEGED)
            return CLI_OK;

      if (!cli->enable_password && !cli->enable_callback)
      {
            // No password required, set privilege immediately
            cli_set_privilege(cli, PRIVILEGE_PRIVILEGED);
            cli_set_configmode(cli, MODE_EXEC, NULL);
      }
      else
      {
            // Require password entry
            cli->state = STATE_ENABLE_PASSWORD;
      }
      return CLI_OK;
}

int cli_int_disable(struct cli_def *cli, const char * command, char *argv[], int argc)
{
      cli_set_privilege(cli, PRIVILEGE_UNPRIVILEGED);
      cli_set_configmode(cli, MODE_EXEC, NULL);
      return CLI_OK;
}

int cli_int_help(struct cli_def *cli, const char *command, char *argv[], int argc)
{
      cli_error(cli, "\nCommands available:");
      cli_show_help(cli, cli->commands);
      return CLI_OK;
}

int cli_int_history(struct cli_def *cli, const char *command, char *argv[], int argc)
{
      int i;

      cli_error(cli, "\nCommand history:");
      for (i = 0; i < MAX_HISTORY; i++)
      {
            if (cli->history[i])
                  cli_error(cli, "%3d. %s", i, cli->history[i]);
      }
      return CLI_OK;
}

int cli_int_quit(struct cli_def *cli, const char *command, char *argv[], int argc)
{
      cli_set_privilege(cli, PRIVILEGE_UNPRIVILEGED);
      cli_set_configmode(cli, MODE_EXEC, NULL);
      return CLI_QUIT;
}

int cli_int_end(struct cli_def *cli, const char *command, char *argv[], int argc)
{
      cli_set_configmode(cli, MODE_EXEC, NULL);
      cli->service = NULL;
      return CLI_OK;
}

int cli_int_exit(struct cli_def *cli, const char *command, char *argv[], int argc)
{
      if (cli->mode == MODE_EXEC)
      {
            return cli_int_quit(cli, command, argv, argc);
      }
      
      if (cli->mode > MODE_CONFIG)
            cli_set_configmode(cli, MODE_CONFIG, NULL);
      else
            cli_set_configmode(cli, MODE_EXEC, NULL);

      cli->service = NULL;

      return CLI_OK;
}

int cli_int_configure_terminal(struct cli_def *cli, const char *command, char *argv[], int argc)
{
      cli_set_configmode(cli, MODE_CONFIG, NULL);
      return CLI_OK;
}

struct cli_def *cli_init()
{
      struct cli_def *cli;
      struct cli_command *c;

      if (!(cli = calloc(sizeof(struct cli_def), 1)))
            return NULL;

      cli->buf_size = 1024;
      cli->buffer = calloc(cli->buf_size, 1);
      
      cli_register_command(cli, NULL, "help", cli_int_help, PRIVILEGE_UNPRIVILEGED, MODE_ANY, "Show available commands");
      cli_register_command(cli, NULL, "quit", cli_int_quit, PRIVILEGE_UNPRIVILEGED, MODE_ANY, "Disconnect");
      cli_register_command(cli, NULL, "exit", cli_int_exit, PRIVILEGE_UNPRIVILEGED, MODE_ANY, "Exit from current mode");
      cli_register_command(cli, NULL, "end", cli_int_end, PRIVILEGE_PRIVILEGED, MODE_ANY, "Exit to exec mode");
      cli_register_command(cli, NULL, "history", cli_int_history, PRIVILEGE_UNPRIVILEGED, MODE_ANY, "Show a list of previously run commands");
      cli_register_command(cli, NULL, "enable", cli_int_enable, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Turn on privileged commands");
      cli_register_command(cli, NULL, "disable", cli_int_disable, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Turn off privileged commands");

      c = cli_register_command(cli, NULL, "configure", NULL, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Enter configuration mode");
      cli_register_command(cli, c, "terminal", cli_int_configure_terminal, PRIVILEGE_PRIVILEGED, MODE_EXEC, "Configure from the terminal");

      cli->privilege = cli->mode = -1;
      cli_set_privilege(cli, PRIVILEGE_UNPRIVILEGED);
      cli_set_configmode(cli, MODE_EXEC, NULL);
      return cli;
}

void cli_unregister_all(struct cli_def *cli, struct cli_command *command)
{
      struct cli_command *c, *p = NULL;

      if (!command) command = cli->commands;
      if (!command) return;

      for (c = command; c; )
      {
            p = c->next;

            // Unregister all child commands
            if (c->children)
                  cli_unregister_all(cli, c->children);

            free(c);

            c = p;
      }
}

int cli_done(struct cli_def *cli)
{
      struct unp *u = cli->users, *n;
      
      cli_free_history(cli);

      // Free all users
      while (u)
      {
            if (u->username) free(u->username);
            if (u->password) free(u->password);
            n = u->next;
            free(u);
            u = n;
      }

      // Free all commands
      cli_unregister_all(cli, NULL);
      
      if (cli->commandname) free(cli->commandname);
      if (cli->modestring) free(cli->modestring);
      if (cli->banner) free(cli->banner);
      if (cli->promptchar) free(cli->promptchar);
      if (cli->hostname) free(cli->hostname);
      
      free(cli->buffer);

      free(cli);
      return CLI_OK;
}

int cli_add_history(struct cli_def *cli, const char *cmd)
{
      int i;
      for (i = 0; i < MAX_HISTORY; i++)
      {
            if (!cli->history[i])
            {
                  if(i == 0 || strcasecmp(cli->history[i-1], cmd))
                        cli->history[i] = strdup(cmd);
                  return CLI_OK;
            }
      }
      // No space found, drop one off the beginning of the list
      free(cli->history[0]);
      for (i = 0; i < MAX_HISTORY-1; i++)
            cli->history[i] = cli->history[i+1];
      cli->history[MAX_HISTORY - 1] = strdup(cmd);
      return CLI_OK;
}

void cli_free_history(struct cli_def *cli)
{
      int i;
      for (i = 0; i < MAX_HISTORY; i++)
      {
            if(cli->history[i])
            {
                  free(cli->history[i]);
                  cli->history[i]=NULL;
            }
      }
}

int cli_parse_line(const char *line, char *words[], int max_words)
{
      int nwords = 0;
      const char *p = line;
      const char *word_start = line;
      int inquote = 0;

      while (nwords < max_words - 1)
      {
            if (!*p || *p == inquote || (word_start && !inquote && (isspace(*p) || *p == '|')))
            {
                  if (word_start)
                  {
                        int len = p - word_start;

                        memcpy(words[nwords] = malloc(len + 1), word_start, len);
                        words[nwords++][len] = 0;
                  }

                  if (!*p)
                        break;

                  if (inquote)
                        p++; // skip over trailing quote

                  inquote = 0;
                  word_start = 0;
            }
            else if (*p == '"' || *p == '\'')
            {
                  inquote = *p++;
                  word_start = p;
            }
            else
            {
                  if (!word_start)
                  {
                        if (*p == '|')
                        {
                              words[nwords++] = strdup("|");
                        }
                        else if (!isspace(*p))
                              word_start = p;
                  }

                  p++;
            }
      }

      return nwords;
}

int cli_find_command(struct cli_def *cli, struct cli_command *commands, int num_words, char *words[], int start_word, int filters[])
{
      struct cli_command *c, *again=NULL;
      int c_words = num_words;

      if (filters[0])
            c_words = filters[0];

      // Deal with ? for help
      if (!words[start_word]) { return CLI_ERROR; }
      if (words[start_word][strlen(words[start_word])-1] == '?')
      {
            int l = strlen(words[start_word])-1;
      
            if ( commands->parent && commands->parent->callback)
                  cli_error(cli, "%-20s %s", cli_command_name(cli, commands->parent),  commands->parent->help ? : "");

            for (c = commands; c; c = c->next)
            {
                  if (strncasecmp(c->command, words[start_word], l) == 0
                     && (c->callback || c->children)
                     && cli->privilege >= c->privilege
                     && (c->mode == cli->mode || c->mode == MODE_ANY))
                        cli_error(cli, "  %-20s %s", c->command, c->help ? : "");
            }

            return CLI_OK;
      }

      for (c = commands; c; c = c->next)
      {
            if (cli->privilege < c->privilege)
                  continue;

            if (strncasecmp(c->command, words[start_word], c->unique_len))
                  continue;
                  
            if (strncasecmp(c->command, words[start_word], strlen(words[start_word])))
                  continue;
AGAIN:
            if (c->mode == cli->mode || c->mode == MODE_ANY)
            {
                  int rc = CLI_OK;
                  int f;
                  struct cli_filter **filt = &cli->filters;

                  // Found a word!
                  if (!c->children)
                  {
                        // Last word
                        if (!c->callback)
                        {
                              cli_error(cli, "No callback for \"%s\"", cli_command_name(cli, c));
                              return CLI_ERROR;
                        }
                        if (words[num_words-1][strlen(words[num_words-1])-1] == '?')
                        {
                              cli_error(cli, "%-20s %s", cli_command_name(cli, c), c->help ? : "");
                              return CLI_OK;
                        }
                  }
                  else
                  {
                        if (start_word == c_words - 1)
                        {
                              if(c->callback)
                                    goto CORRECT_CHECKS;
                              
                              cli_error(cli, "Incomplete command");
                              return CLI_ERROR;
                        }
                        rc = cli_find_command(cli, c->children, num_words, words, start_word + 1, filters);
                        if(rc == CLI_ERROR_ARG)
                        {
                              if(c->callback)
                              {
                                    rc = CLI_OK;
                                    goto CORRECT_CHECKS;
                              }
                              else 
                                    cli_error(cli, "Invalid %s \"%s\"",
                                          commands->parent ? "argument" : "command", words[start_word]);
                        }
                        return rc; 
                  }

                  if (!c->callback)
                  {
                        cli_error(cli, "Internal server error processing \"%s\"", cli_command_name(cli, c));
                        return CLI_ERROR;
                  }

CORRECT_CHECKS:
                  for (f = 0; rc == CLI_OK && filters[f]; f++)
                  {
                        int n = num_words;
                        char **argv;
                        int argc;
                        int len;

                        if (filters[f+1])
                              n = filters[f+1];

                        if (filters[f] == n - 1)
                        {
                              cli_error(cli, "Missing filter");
                              return CLI_ERROR;
                        }

                        argv = words + filters[f] + 1;
                        argc = n - (filters[f] + 1);
                        len = strlen(argv[0]);
                        if (argv[argc-1][strlen(argv[argc-1])-1] == '?')
                        {
                              if (argc == 1)
                              {
                                    int i;

                                    for(i=0; filter_cmds[i].cmd; i++)
                                          cli_error(cli, "  %-20s %s",
                                                filter_cmds[i].cmd, filter_cmds[i].help );
                              }
                              else
                              {
                                    if (argv[0][0] != 'c') // count
                                          cli_error(cli, "  WORD");

                                    if (argc > 2 || argv[0][0] == 'c') // count
                                          cli_error(cli, "  <cr>");
                              }

                              return CLI_OK;
                        }

                        if (argv[0][0] == 'b' && len < 3) // [beg]in, [bet]ween
                        {
                              cli_error(cli, "Ambiguous filter \"%s\" (begin, between)", argv[0]);
                              return CLI_ERROR;
                        }

                        if (argv[0][0] == 'e' && len < 2) // [eg]rep, [ex]clude
                        {
                              cli_error(cli, "Ambiguous filter \"%s\" (egrep, exclude)", argv[0]);
                              return CLI_ERROR;
                        }

                        *filt = calloc(sizeof(struct cli_filter), 1);

                        if (!strncmp("include", argv[0], len) ||
                              !strncmp("exclude", argv[0], len) ||
                              !strncmp("grep", argv[0], len) ||
                              !strncmp("egrep", argv[0], len))
                              rc = cli_match_filter_init(cli, argc, argv, *filt);
                        else if (!strncmp("begin", argv[0], len) ||
                               !strncmp("between", argv[0], len))
                              rc = cli_range_filter_init(cli, argc, argv, *filt);
                        else if (!strncmp("count", argv[0], len))
                              rc = cli_count_filter_init(cli, argc, argv, *filt);
                        else
                        {
                              cli_error(cli, "Invalid filter \"%s\"", argv[0]);
                              rc = CLI_ERROR;
                        }

                        if (rc == CLI_OK)
                        {
                              filt = &(*filt)->next;
                        }
                        else
                        {
                              free(*filt);
                              *filt = 0;
                        }
                  }

                  if (rc == CLI_OK)
                        rc = c->callback(cli, cli_command_name(cli, c), words , c_words);
                  
                  while (cli->filters)
                  {
                        struct cli_filter *filt = cli->filters;

                        // call one last time to clean up
                        filt->filter(cli, NULL, filt->data);
                        cli->filters = filt->next;
                        free(filt);
                  }

                  return rc;
            }
            else if(cli->mode > MODE_CONFIG && c->mode == MODE_CONFIG)
            {
                  //command matched but from another mode,
                  // remeber it if we fail to find correct command
                  again = c;

            }
      }

      // drop out of config submode if we have matched command on MODE_CONFIG
      if (again)
      {     
            c=again;
            cli_set_configmode(cli, MODE_CONFIG, NULL);
            goto AGAIN;
      }

      if(start_word == 0) 
            cli_error(cli, "Invalid %s \"%s\"", commands->parent ? "argument" : "command", words[start_word]);
      return CLI_ERROR_ARG;
}

int cli_run_command(struct cli_def *cli, const char *command)
{
      int num_words, r, i, f;
      char *words[128] = {0};
      int filters[128] = {0};

      if (!command) return CLI_ERROR;
      while (isspace(*command))
            command++;

      if (!*command) return CLI_OK;

      num_words = cli_parse_line(command, words, sizeof(words)/sizeof(words[0]));
      for (i = f = 0; i < num_words && f < sizeof(filters)/sizeof(filters[0])-1; i++)
      {
            if (words[i][0] == '|')
                  filters[f++] = i;
      }

      filters[f] = 0;

      if (num_words) 
            r = cli_find_command(cli, cli->commands, num_words, words, 0, filters);
      else
            r = CLI_ERROR;

      for (i = 0; i < num_words; i++)
            free(words[i]);

      if (r == CLI_QUIT)
            return r;

      return CLI_OK;
}

int cli_get_completions(struct cli_def *cli, const char *command, const char **completions)
{
      struct cli_command *c;
      int num_words, i, k=0;
      char *words[MAX_COMPLETIONS] = {0};
      int filter = 0;
      
      if (!command) return 0;
      while (isspace(*command))
            command++;
      
      if (!*command) return 0;

      num_words = cli_parse_line(command, words, sizeof(words)/sizeof(words[0]));
      for (i = 0; i < num_words; i++)
      {
            if (words[i][0] == '|')
                  filter = i;
      }
      
      if (!num_words) return 0;

      int all=0;
      int len;
      
      len = strlen(words[num_words-1]);
      
      if(filter) // complete filters
      {
            if (num_words - filter > 2) return 0;    //filter already completed
            if (num_words - filter == 1) all = 1;

            for (i=0; filter_cmds[i].cmd && k <  MAX_COMPLETIONS; i++)
                  if (all || ( len != strlen(filter_cmds[i].cmd)
                      && !strncmp(filter_cmds[i].cmd, words[num_words-1], len)))
                        completions[k++]=filter_cmds[i].cmd;
             
             return k;
      }

      if ( command[strlen(command)-1] == ' ')
      {
            all=1;
            num_words++;
      }
      
      for (c = cli->commands, i=0; c && i < num_words-1; c = c->next)
      {
            if ((cli->privilege >= c->privilege) &&
                  (c->mode == cli->mode || c->mode == MODE_ANY) &&
                  (strncasecmp(c->command, words[i], c->unique_len) == 0))
            {
                  c = c->children;
                  if(!c) return 0;
                  i++;
            }
      }

      for (;c && k < MAX_COMPLETIONS; c = c->next)
      {
            if ((cli->privilege >= c->privilege) &&
                  (c->mode == cli->mode || c->mode == MODE_ANY) &&
                  (all || (strncasecmp(c->command, words[i], len) == 0)))
                        completions[k++] = c->command;
      }     
      return k;
}

void cli_clear_line(int sockfd, char *cmd, int l, int cursor)
{
      int i;
      if (cursor < l) for (i = 0; i < (l - cursor); i++) write(sockfd, " ", 1);
      for (i = 0; i < l; i++) cmd[i] = '\b';
      for (; i < l * 2; i++) cmd[i] = ' ';
      for (; i < l * 3; i++) cmd[i] = '\b';
      write(sockfd, cmd, i);
      memset(cmd, 0, i);
      l = cursor = 0;
}

void cli_reprompt(struct cli_def *cli)
{
      if (!cli) return;
      cli->showprompt = 1;
}

void cli_regular(struct cli_def *cli, int (*callback)(struct cli_def *cli))
{
      if (!cli) return;
      cli->regular_callback = callback;
}

#define DES_PREFIX "{crypt}"  /* to distinguish b/w clear text and DES crypted */
#define MD5_PREFIX "$1$"

static int pass_matches(char *pass, char *try)
{
      int des;
      if ((des = !strncasecmp(pass, DES_PREFIX, sizeof(DES_PREFIX)-1)))
            pass += sizeof(DES_PREFIX)-1;

      if (des || !strncmp(pass, MD5_PREFIX, sizeof(MD5_PREFIX)-1))
            try = crypt(try, pass);

      return !strcmp(pass, try);
}

#define CTRL(c) (c - '@')

static int show_prompt(struct cli_def *cli, int sockfd)
{
      int len = 0;

      if (cli->hostname)
            len += write(sockfd, cli->hostname, strlen(cli->hostname));

      if (cli->modestring)
            len += write(sockfd, cli->modestring, strlen(cli->modestring));

      return len + write(sockfd, cli->promptchar, strlen(cli->promptchar));
}

int cli_loop(struct cli_def *cli, int sockfd)
{
      int n;
      unsigned char c;
      char *cmd = NULL, *oldcmd = NULL;
      int l, oldl = 0, is_telnet_option = 0, skip = 0, esc = 0;
      int cursor = 0, insertmode = 1;
      char *username = NULL, *password = NULL;
      char *negotiate =
            "\xFF\xFB\x03"
            "\xFF\xFB\x01"
            "\xFF\xFD\x03"
            "\xFF\xFD\x01";

      cli->state = STATE_LOGIN;

      memset(cli->history, 0, MAX_HISTORY);
      write(sockfd, negotiate, strlen(negotiate));

      if ((cmd = malloc(4096)) == NULL)
            return CLI_ERROR;

      if (!(cli->client = fdopen(sockfd, "w+")))
            return CLI_ERROR;

      setbuf(cli->client, NULL);
      if (cli->banner)
            cli_error(cli, "%s", cli->banner);

      #ifdef USE_CLI_ENABLE
            // Start off in unprivileged mode
            cli_set_privilege(cli, PRIVILEGE_UNPRIVILEGED);
      #else
            cli_set_privilege(cli, PRIVILEGE_PRIVILEGED);
      #endif
      cli_set_configmode(cli, MODE_EXEC, NULL);

      // No auth required?
      if (!cli->users && !cli->auth_callback) cli->state = STATE_NORMAL;

      while (1)
      {
            signed int in_history = 0;
            int lastchar = 0;
            struct timeval tm;

            cli->showprompt = 1;

            if (oldcmd)
            {
                  l = cursor = oldl;
                  oldcmd[l] = 0;
                  cli->showprompt = 1;
                  oldcmd = NULL;
                  oldl = 0;
            }
            else
            {
                  memset(cmd, 0, 4096);
                  l = 0;
                  cursor = 0;
            }

            tm.tv_sec = 1;
            tm.tv_usec = 0;

            while (1)
            {
                  int sr;
                  fd_set r;
                  if (cli->showprompt)
                  {
                        if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
                              write(sockfd, "\r\n", 2);

                        switch (cli->state)
                        {
                              case STATE_LOGIN:
                                    write(sockfd, "Username: ", strlen("Username: "));
                                    break;
                              case STATE_PASSWORD:
                                    write(sockfd, "Password: ", strlen("Password: "));
                                    break;
                              case STATE_NORMAL:
                              case STATE_ENABLE:
                                    show_prompt(cli, sockfd);
                                    write(sockfd, cmd, l);
                                    if (cursor < l)
                                    {
                                          int n = l - cursor;
                                          while (n--)
                                                write(sockfd, "\b", 1);
                                    }
                                    break;
                              case STATE_ENABLE_PASSWORD:
                                    write(sockfd, "Password: ", strlen("Password: "));
                                    break;

                        }
                        cli->showprompt = 0;
                  }

                  FD_ZERO(&r);
                  FD_SET(sockfd, &r);

                  if ((sr = select(sockfd + 1, &r, NULL, NULL, &tm)) < 0)
                  {
                        // Select Error
                        if (errno == EINTR) continue;
                        perror("read");
                        l = -1;
                        break;
                  }
                  if (sr == 0)
                  {
                        // Timeout every second
                        if (cli->regular_callback && cli->regular_callback(cli) != CLI_OK)
                              break;
                        tm.tv_sec = 1;
                        tm.tv_usec = 0;
                        continue;
                  }
                  if ((n = read(sockfd, &c, 1)) < 0)
                  {
                        if (errno == EINTR) continue;
                        perror("read");
                        l = -1;
                        break;
                  }
                  if (n == 0) { l = -1; break; }
                  if (skip) { skip--; continue; }

                  if (c == 255 && !is_telnet_option)
                  {
                        is_telnet_option++;
                        continue;
                  }
                  if (is_telnet_option)
                  {
                        if (c >= 251 && c <= 254)
                        {
                              is_telnet_option = c;
                              continue;
                        }
                        if (c != 255)
                        {
                              is_telnet_option = 0;
                              continue;
                        }
                        is_telnet_option = 0;
                  }

                  // Handle ANSI arrows
                  if (esc)
                  {
                        if (esc == '[')
                        {
                              // Remap to readline control codes
                              switch (c)
                              {
                                    case 'A':
                                          // Up
                                          c = CTRL('P');
                                          break;
                                    case 'B':
                                          // Down
                                          c = CTRL('N');
                                          break;
                                    case 'C':
                                          // Right
                                          c = CTRL('F');
                                          break;
                                    case 'D':
                                          // Left
                                          c = CTRL('B');
                                          break;
                                    default:
                                          c = 0;
                              }
                              esc = 0;
                        }
                        else
                        {
                              esc = (c == '[') ? c : 0;
                              continue;
                        }
                  }

                  if (c == 0) continue;
                  if (c == '\n') continue;
                  if (c == '\r') {
                        if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
                              write(sockfd, "\r\n", 2);
                        break;
                  }

                  if (c == 27) { esc = 1; continue; }
                  if (c == CTRL('C')) { write(sockfd, "\a", 1); continue; }

                  // Back word, backspace/delete
                  if (c == CTRL('W') || c == CTRL('H') || c == 0x7f)
                  {
                        int back = 0;

                        // Word
                        if (c == CTRL('W'))
                        {
                              int nc = cursor;

                              if (l == 0 || cursor == 0) continue;

                              while (nc && cmd[nc - 1] == ' ')
                              {
                                    nc--;
                                          back++;
                              }

                              while (nc && cmd[nc - 1] != ' ')
                              {
                                    nc--;
                                          back++;
                              }
                        }
                        // Char
                        else
                        {
                              if (l == 0 || cursor == 0)
                              {
                                    write(sockfd, "\a", 1);
                                    continue;
                              }

                              back = 1;
                        }

                        if (back)
                        {
                              while (back--)
                              {
                                    if (l == cursor)
                                    {
                                          cmd[--cursor] = 0;
                                          if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
                                                write(sockfd, "\b \b", 3);
                                    }
                                    else
                                    {
                                          int i;
                                          cursor--;
                                          if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
                                          {
                                                for (i = cursor; i <= l; i++) cmd[i] = cmd[i+1];
                                                write(sockfd, "\b", 1);
                                                write(sockfd, cmd + cursor, strlen(cmd + cursor));
                                                write(sockfd, " ", 1);
                                                for (i = 0; i <= strlen(cmd + cursor); i++)
                                                      write(sockfd, "\b", 1);
                                          }
                                    }
                                    l--;
                              }

                              continue;
                        }
                  }

                  // Redraw
                  if (c == CTRL('L'))
                  {
                        int i;
                        int cursorback = l - cursor;

                        if (cli->state == STATE_PASSWORD || cli->state == STATE_ENABLE_PASSWORD)
                              continue;

                        write(sockfd, "\r\n", 2);
                        show_prompt(cli, sockfd);
                        write(sockfd, cmd, l);
                        for (i = 0; i < cursorback; i++) write(sockfd, "\b", 1);
                        continue;
                  }

                  // Clear line
                  if (c == CTRL('U'))
                  {
                        if (cli->state == STATE_PASSWORD || cli->state == STATE_ENABLE_PASSWORD)
                        {
                              memset(cmd, 0, l);
                        }
                        else
                        {
                              cli_clear_line(sockfd, cmd, l, cursor);
                        }
                        l = 0; cursor = 0;
                        continue;
                  }

                  // Kill to EOL
                  if (c == CTRL('K'))
                  {
                        if (cursor == l)
                              continue;

                        if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
                        {
                              int c;
                              for (c = cursor; c < l; c++) write(sockfd, " ", 1);
                              for (c = cursor; c < l; c++) write(sockfd, "\b", 1);
                        }

                        memset(cmd + cursor, 0, l - cursor);
                        l = cursor;
                        continue;
                  }

                  // EOT
                  if (c == CTRL('D'))
                  {
                        if (cli->state == STATE_PASSWORD || cli->state == STATE_ENABLE_PASSWORD)
                              break;

                        if (l)
                              continue;

                        strcpy(cmd, "quit");
                        l = cursor = strlen(cmd);
                        write(sockfd, "quit\r\n", l + 2);
                        break;
                  }

                  // disable
                  if (c == CTRL('Z'))
                  {
                        if (cli->mode != MODE_EXEC)
                        {
                              cli_clear_line(sockfd, cmd, l, cursor);
                              cli_set_configmode(cli, MODE_EXEC, NULL);
                              cli->showprompt = 1;
                        }
                        continue;
                  }

                  // Tab completion
                  if (c == CTRL('I'))
                  {
                        const char *completions[MAX_COMPLETIONS];
                        int num_completions = 0;
                        int common_len = 0;

                        if (cli->state == STATE_PASSWORD || cli->state == STATE_ENABLE_PASSWORD)
                              continue;

                        if (cursor != l) continue;

                        if (l > 0)
                              num_completions = cli_get_completions(cli, cmd, completions);

                        if (num_completions == 0)
                        {
                              write(sockfd, "\a", 1);
                              continue;
                        }
                        
                        for (; l > 0; l--, cursor--)
                        {
                              if (cmd[l-1] == ' ' || cmd[l-1] == '|')
                              {
                                    break;
                              }
                              write(sockfd, "\b", 1);
                        }
                        
                        if (num_completions == 1)
                        {
                              // Single completion
                              strcpy((cmd + l), completions[0]);
                              l+= strlen(completions[0]);
                              cmd[l++] = ' ';
                              cursor = l;
                              write(sockfd, completions[0], strlen(completions[0]));
                              write(sockfd, " ", 1);
                              continue;
                        }
                        
                        //find out common part
                        int i;
                        common_len = strlen(completions[0]);
                        for (i=1; common_len && i < num_completions; i++)
                        {
                              while(strncasecmp(completions[0], completions[i], common_len))
                                    common_len--;
                        }
                        
                        if (common_len > 0)
                        {
                              //non empty intersection
                              strncpy((cmd + l), completions[0], common_len);
                              l+= common_len;
                              cursor = l;
                              write(sockfd, completions[0], common_len);
                        }
                        
                        if (lastchar == CTRL('I'))
                        {
                              // double tab
                              int i;
                              write(sockfd, "\r\n", 2);
                              for (i = 0; i < num_completions; i++)
                              {
                                    write(sockfd, completions[i], strlen(completions[i]));
                                    if (i % 4 == 3)
                                          write(sockfd, "\r\n", 2);
                                    else
                                          write(sockfd, "         ", 1);
                              }
                              if (i % 4 != 3) write(sockfd, "\r\n", 2);
                              cli->showprompt = 1;
                        }
                        else
                        {
                              // More than one completion
                              lastchar = c;
                              write(sockfd, "\a", 1);
                        }
                        continue;
                  }

                  // History
                  if (c == CTRL('P') || c == CTRL('N'))
                  {
                        int history_found = 0;

                        if (cli->state == STATE_PASSWORD || cli->state == STATE_ENABLE_PASSWORD)
                              continue;

                        if (c == CTRL('P')) // Up
                        {
                              in_history--;
                              if (in_history < 0)
                              {
                                    for (in_history = MAX_HISTORY-1; in_history >= 0; in_history--)
                                    {
                                          if (cli->history[in_history]) { history_found = 1; break; }
                                    }
                              }
                              else
                              {
                                    if (cli->history[in_history]) history_found = 1;
                              }
                        }
                        else // Down
                        {
                              in_history++;
                              if (in_history >= MAX_HISTORY || !cli->history[in_history])
                              {
                                    int i = 0;
                                    for (i = 0; i < MAX_HISTORY; i++)
                                          if (cli->history[i]) { in_history = i; history_found = 1; break; }
                              }
                              else
                              {
                                    if (cli->history[in_history]) history_found = 1;
                              }
                        }
                        if (history_found && cli->history[in_history])
                        {
                              // Show history item
                              cli_clear_line(sockfd, cmd, l, cursor);
                              memset(cmd, 0, 4096);
                              strncpy(cmd, cli->history[in_history], 4095);
                              l = cursor = strlen(cmd);
                              write(sockfd, cmd, l);
                        }

                        continue;
                  }

                  // Left/right cursor motion
                  if (c == CTRL('B') || c == CTRL('F'))
                  {
                        if (c == CTRL('B')) // Left
                        {
                              if (cursor)
                              {
                                    if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
                                          write(sockfd, "\b", 1);

                                    cursor--;
                              }
                        }
                        else // Right
                        {
                              if (cursor < l)
                              {
                                    if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
                                          write(sockfd, &cmd[cursor], 1);

                                    cursor++;
                              }
                        }

                        continue;
                  }

                  // Start of line
                  if (c == CTRL('A'))
                  {
                        if (cursor)
                        {
                              if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
                              {
                                    write(sockfd, "\r", 1);
                                    show_prompt(cli, sockfd);
                              }
                              cursor = 0;
                        }

                        continue;
                  }

                  // End of line
                  if (c == CTRL('E'))
                  {
                        if (cursor < l)
                        {
                              if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
                                    write(sockfd, &cmd[cursor], l - cursor);

                              cursor = l;
                        }

                        continue;
                  }

                  // Normal character typed
                  if (cursor == l)
                  {
                        // End of text
                        cmd[cursor] = c;
                        if (l < 4095)
                        {
                              l++;
                              cursor++;
                        }
                        else
                        {
                              write(sockfd, "\a", 1);
                              continue;
                        }
                  }
                  else
                  {
                        // Middle of text
                        if (insertmode)
                        {
                              int i;
                              // Move everything one character to the right
                              if (l >= 4094) l--;
                              for (i = l; i >= cursor; i--)
                                    cmd[i + 1] = cmd[i];
                              // Write what we've just added
                              cmd[cursor] = c;

                              write(sockfd, &cmd[cursor], l - cursor + 1);
                              for (i = 0; i < (l - cursor + 1); i++)
                                    write(sockfd, "\b", 1);
                              l++;
                        }
                        else
                        {
                              cmd[cursor] = c;
                        }
                        cursor++;
                  }

                  if (cli->state != STATE_PASSWORD && cli->state != STATE_ENABLE_PASSWORD)
                  {
                        // ?
                        if (c == 63 && cursor == l)
                        {
                              write(sockfd, "\r\n", 2);
                              oldcmd = cmd;
                              oldl = cursor = l - 1;
                              break;
                        }
                        write(sockfd, &c, 1);
                  }

                  oldcmd = NULL;
                  oldl = 0;
                  lastchar = c;
            }

            if (l < 0) break;

            if (strcasecmp(cmd, "quit") == 0) break;
            if (cli->state == STATE_LOGIN)
            {
                  if (l == 0) continue;

                  // Require login
                  if (username) free(username);
                  username = strdup(cmd);
                  cli->state = STATE_PASSWORD;
                  cli->showprompt = 1;
            }
            else if (cli->state == STATE_PASSWORD)
            {
                  // Require password
                  int allowed = 0;

                  if (password) free(password);
                  password = strdup(cmd);
                  if (cli->auth_callback)
                  {
                        if (cli->auth_callback(username, password))
                        {
                              allowed++;
                        }
                  }

                  if (!allowed)
                  {
                        struct unp *u;
                        for (u = cli->users; u; u = u->next)
                        {
                              if (!strcmp(u->username, username) && pass_matches(u->password, password))
                              {
                                    allowed++;
                                    break;
                              }
                        }
                  }

                  if (allowed)
                  {
                        cli_error(cli, "");
                        cli->state = STATE_NORMAL;
                  }
                  else
                  {
                        cli_error(cli, "\n\nAccess denied");
                        free(username); username = NULL;
                        free(password); password = NULL;
                        cli->state = STATE_LOGIN;
                  }
                  cli->showprompt = 1;
            }
            else if (cli->state == STATE_ENABLE_PASSWORD)
            {
                  int allowed = 0;
                  if (cli->enable_password)
                  {
                        // Check stored static enable password
                        if (pass_matches(cli->enable_password, cmd))
                              allowed++;
                  }
                  if (!allowed && cli->enable_callback)
                  {
                        // Check callback
                        if (cli->enable_callback(cmd))
                        {
                              allowed++;
                        }
                  }
                  if (allowed)
                  {
                        cli->state = STATE_ENABLE;
                        cli_set_privilege(cli, PRIVILEGE_PRIVILEGED);
                  }
                  else
                  {
                        cli_error(cli, "\n\nAccess denied");
                        cli->state = STATE_NORMAL;
                  }
            }
            else
            {
                  if (l == 0) continue;
                  if (cmd[l - 1] != '?' && strcasecmp(cmd, "history") != 0) cli_add_history(cli, cmd);
                  if (cli_run_command(cli, cmd) == CLI_QUIT)
                        break;
            }
      }

      {
            cli_free_history(cli);
            
            if (username) free(username);
            if (password) free(password);
            if (cmd) free(cmd);
      }

      fclose(cli->client);
      cli->client = NULL;
      return CLI_OK;
}

int cli_file(struct cli_def *cli, FILE *fh, int privilege, int mode)
{
      int oldpriv = cli_set_privilege(cli, privilege);
      int oldmode = cli_set_configmode(cli, mode, NULL);
      char buf[4096];

      while (1)
      {
            char *p;
            char *cmd;
            char *end;
            u_char bracket1 = 0; // ' count
            u_char bracket2 = 0; // " count

            if (fgets(buf, sizeof(buf), fh) <= 0)
                  break; // End of file

            if ((p = strpbrk(buf, "\r\n")))
                  *p = 0;

            for (p = buf; *p; p++)
            {
                switch (*p)
                {
                  case '\'':
                        bracket1 = 1 - bracket1;
                      break;

                  case '"':
                      bracket2 = 1 - bracket2;
                      break;

                  case '#':
                      if (!bracket1 && !bracket2) {   //no active brackets -> this is comment
                        *p = 0;
                        goto OUT;
                      }
                      break;
                }
            }
OUT:
            cmd = buf;
            while (isspace(*cmd))
                  cmd++;

            if (!*cmd)
                  continue;

            for (p = end = cmd; *p; p++)
                  if (!isspace(*p))
                        end = p;

            *++end = 0;
            if (strcasecmp(cmd, "quit") == 0)
                  break;

            if (cli_run_command(cli, cmd) == CLI_QUIT)
                  break;
      }

      cli_set_privilege(cli, oldpriv);
      cli_set_configmode(cli, oldmode, NULL /* didn't save desc */);

      return CLI_OK;
}

static void _print(struct cli_def *cli, int print_mode, const char *format, va_list ap)
{
      static char *buffer;
      static int size, len;
      char *p;
      int n;
      
      if (!cli) return; // sanity check
      
      buffer = cli->buffer;
      size = cli->buf_size;
      len = strlen(buffer);
      
      while ((n = vsnprintf(buffer+len, size-len, format, ap)) >= size-len)
      {
            if (!(buffer = realloc(buffer, size += 1024)))
                  return;
            
            cli->buffer = buffer;
            cli->buf_size = size;
      }

      if (n < 0) // vaprintf failed
            return;
      
      p = buffer;
      do {
            char *next = strchr(p, '\n');
            struct cli_filter *f = (print_mode&PRINT_FILTERED) ? cli->filters : 0;
            int print = 1;

            if (next)
                  *next++ = 0;
            else if (print_mode&PRINT_BUFFERED)
                  break;

            while (print && f)
            {
                  print = (f->filter(cli, p, f->data) == CLI_OK);
                  f = f->next;
            }
            if (print)
            {
                  if (cli->print_callback)
                        cli->print_callback(cli, p);
                  else if (cli->client)
                        fprintf(cli->client, "%s\r\n", p);
            }

            p = next;
      } while (p);

      if(p && *p) {
            if(p != buffer)
                  bcopy(p, buffer, strlen(p));
      } else 
            *buffer=0;
}

void cli_bufprint(struct cli_def *cli, const char *format, ...)
{
      va_list ap;
      
      va_start(ap, format);
      _print(cli, PRINT_BUFFERED|PRINT_FILTERED, format, ap);
      va_end(ap);
}

void cli_vabufprint(struct cli_def *cli, const char *format, va_list ap) {
      _print(cli, PRINT_BUFFERED, format, ap);
}

void cli_print(struct cli_def *cli, const char *format, ...)
{
      va_list ap;

      va_start(ap, format);
      _print(cli, PRINT_FILTERED, format, ap);
      va_end(ap);
}

void cli_error(struct cli_def *cli, const char *format, ...)
{
      va_list ap;

      va_start(ap, format);
      _print(cli, PRINT_PLAIN, format, ap);
      va_end(ap);
}

static char *join_words(int argc, char **argv)
{
      char *p;
      int len = 0;
      int i;

      for (i = 0; i < argc; i++)
      {
            if (i)
                  len += 1;

            len += strlen(argv[i]);
      }

      p = malloc(len + 1);
      p[0] = 0;

      for (i = 0; i < argc; i++)
      {
            if (i)
                  strcat(p, " ");

            strcat(p, argv[i]);
      }

      return p;
}

struct cli_match_filter_state {
      int flags;
#define MATCH_REGEX           1
#define MATCH_INVERT          2
      union {
            char *string;
            regex_t re;
      } match;
};

int cli_match_filter_init(struct cli_def *cli, int argc, char **argv, struct cli_filter *filt)
{
      struct cli_match_filter_state *state;
      int rflags;
      int i;
      char *p;

      if (argc < 2)
      {
            if (cli->client)
                  fprintf(cli->client, "Match filter requires an argument\r\n");

            return CLI_ERROR;
      }

      filt->filter = cli_match_filter;
      filt->data = state = calloc(sizeof(struct cli_match_filter_state), 1);

      if (argv[0][0] == 'i' || // include/exclude
            (argv[0][0] == 'e' && argv[0][1] == 'x'))
      {
            if (argv[0][0] == 'e')
                  state->flags = MATCH_INVERT;

            state->match.string = join_words(argc-1, argv+1);
            return CLI_OK;
      }

      state->flags = MATCH_REGEX;

      // grep/egrep
      rflags = REG_NOSUB;
      if (argv[0][0] == 'e') // egrep
            rflags |= REG_EXTENDED;

      i = 1;
      while (i < argc - 1 && argv[i][0] == '-' && argv[i][1])
      {
            int last = 0;
            p = &argv[i][1];

            if (strspn(p, "vie") != strlen(p))
                  break;

            while (*p)
            {
                  switch (*p++)
                  {
                  case 'v':
                        state->flags |= MATCH_INVERT;
                        break;

                  case 'i':
                        rflags |= REG_ICASE;
                        break;

                  case 'e':
                        last++;
                        break;
                  }
            }

            i++;
            if (last)
                  break;
      }

      p = join_words(argc-i, argv+i);
      if ((i = regcomp(&state->match.re, p, rflags)))
      {
            if (cli->client)
                  fprintf(cli->client, "Invalid pattern \"%s\"\r\n", p);

            free(p);
            return CLI_ERROR;
      }

      free(p);
      return CLI_OK;
}

int cli_match_filter(struct cli_def *cli, char *string, void *data)
{
      struct cli_match_filter_state *state = data;
      int r = CLI_ERROR;

      if (!string) // clean up
      {
            if (state->flags & MATCH_REGEX)
                  regfree(&state->match.re);
            else
                  free(state->match.string);

            free(state);
            return CLI_OK;
      }

      if (state->flags & MATCH_REGEX)
      {
            if (!regexec(&state->match.re, string, 0, NULL, 0))
                  r = CLI_OK;
      }
      else
      {
            if (strstr(string, state->match.string))
                  r = CLI_OK;
      }

      if (state->flags & MATCH_INVERT)
      {
            if (r == CLI_OK)
                  r = CLI_ERROR;
            else
                  r = CLI_OK;
      }

      return r;
}

struct cli_range_filter_state {
      int matched;
      char *from;
      char *to;
};

int cli_range_filter_init(struct cli_def *cli, int argc, char **argv, struct cli_filter *filt)
{
      struct cli_range_filter_state *state;
      char *from = 0;
      char *to = 0;

      if (!strncmp(argv[0], "bet", 3)) // between
      {
            if (argc < 3)
            {
                  if (cli->client)
                        fprintf(cli->client, "Between filter requires 2 arguments\r\n");

                  return CLI_ERROR;
            }

            from = strdup(argv[1]);
            to = join_words(argc-2, argv+2);
      }
      else // begin
      {
            if (argc < 2)
            {
                  if (cli->client)
                        fprintf(cli->client, "Begin filter requires an argument\r\n");

                  return CLI_ERROR;
            }

            from = join_words(argc-1, argv+1);
      }

      filt->filter = cli_range_filter;
      filt->data = state = calloc(sizeof(struct cli_range_filter_state), 1);

      state->from = from;
      state->to = to;

      return CLI_OK;
}

int cli_range_filter(struct cli_def *cli, char *string, void *data)
{
      struct cli_range_filter_state *state = data;
      int r = CLI_ERROR;

      if (!string) // clean up
      {
            free(state->from);
            free(state->to);
            free(state);
            return CLI_OK;
      }

      if (!state->matched)
            state->matched = !!strstr(string, state->from);

      if (state->matched)
      {
            r = CLI_OK;
            if (state->to && strstr(string, state->to))
                  state->matched = 0;
      }

      return r;
}

int cli_count_filter_init(struct cli_def *cli, int argc, char **argv, struct cli_filter *filt)
{
      if (argc > 1)
      {
            if (cli->client)
                  fprintf(cli->client, "Count filter does not take arguments\r\n");

            return CLI_ERROR;
      }

      filt->filter = cli_count_filter;
      filt->data = calloc(sizeof(int), 1);

      return CLI_OK;
}

int cli_count_filter(struct cli_def *cli, char *string, void *data)
{
      int *count = data;

      if (!string) // clean up
      {
            // print count
            if (cli->client)
                  fprintf(cli->client, "%d\r\n", *count);

            free(count);
            return CLI_OK;
      }

      while (isspace(*string))
            string++;

      if (*string)
            (*count)++;  // only count non-blank lines

      return CLI_ERROR; // no output
}

void cli_print_callback(struct cli_def *cli, void (*callback)(struct cli_def *, char *))
{
      cli->print_callback = callback;
}

Generated by  Doxygen 1.6.0   Back to index