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

interactive.cc

/*
 * WallFire -- a comprehensive firewall administration tool.
 * 
 * Copyright (C) 2001 Hervé Eychenne <rv@wallfire.org>
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 */

using namespace std;

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> /* for read() */
#include <sys/select.h> /* for select() */
#include <errno.h>
#include <string.h>
#ifdef HAVE_CTYPE_H
#include <ctype.h> /* for isspace() */
#endif

#ifdef HAVE_LIBREADLINE
# include <readline/readline.h>
# include <readline/history.h>
#endif

#include "interactive.h"
#include "wflogs_filter.h"
#include "defs.h"

// #undef HAVE_LIBREADLINE /* uncomment this for test purpose */

// external vars RV@@7
extern bool interactive, realtime;
extern int verbose;
extern wflogs_filter* myfilter;

bool beep = false;

/* A structure which contains information on the commands this program
   can understand. */
typedef struct {
  char* name;                 /* User printable name of the function. */
  int (*func)(const char*);         /* Function to call to do the job. */
  char* doc;                  /* Documentation for this function. */
} COMMAND;

/* The names of functions that actually do the manipulation. */
static int com_realtime(const char*), com_beep(const char*),
  com_filter(const char*), com_help(const char*), com_quit(const char*),
  com_verbose(const char*);
static COMMAND commands[] = {
  { "help", com_help,   N_("Display this text") },
  { "?",    com_help,   N_("Synonym for `help'") },
  { "quit", com_quit,   N_("Quit") },
  { "exit", com_quit,   N_("Synonym for `quit'") },
  { "beep", com_beep,   N_("Set beep mode: [on|off|?]. Beep for every log entry displayed") },
  { "filter",     com_filter, N_("Set filter expression: [expression|unset]") },
  { "realtime",   com_realtime,     N_("Set realtime mode: [on|off|?]. Monitor new log entries.") },
  { "verbose",    com_verbose,      N_("Set verbosity level: [level]") },
  { NULL, NULL, NULL }
};
     

/* Strip whitespace from the start and end of STRING.  Return a pointer
   into STRING. */
static char*
stripwhite(char* string) {
  char *s, *t;
     
  for (s = string; isspace(*s); s++);
  if (*s == '\0')
    return s;
  
  t = s + strlen(s) - 1;
  while (t > s && isspace(*t))
    t--;
  *++t = '\0';
  
  return s;
}


/* Look up NAME as the name of a command, and return a pointer to that
   command.  Return a NULL pointer if NAME isn't a command name. */
static COMMAND*
find_command(const char* name) {
  if (name == NULL)
    return NULL;
#if 1 /* non strict */
  int i, len;
  COMMAND *command = NULL;
  
  len = strlen(name);
  for (i = 0; commands[i].name; i++)
    if (!strncmp(name, commands[i].name, len)) {
      if (command)
      return NULL; /* not unique */
      command = &commands[i];
    }

  return command;
#else /* strict */
  int i;
     
  for (i = 0; commands[i].name; i++)
    if (!strcmp(name, commands[i].name))
      return &commands[i];
     
  return NULL;
#endif
}


/* Execute a command line. */
static int
execute_line(char* line) {
  /* Isolate the command word. */
  for (; *line != '\0' && isspace(*line); line++);
  if (*line == '\0')
    return 0;
  char* word = line++;
  for (; *line != '\0' && !isspace(*line); line++);
  if (*line != '\0')
    *line++ = '\0';
     
  COMMAND* command = find_command(word);
  if (command == NULL) {
    printf(_("%s: no such command.\n"), word);
    return -1;
  }
     
  /* Get argument to command, if any. */
  for (; *line != '\0' && isspace(*line); line++);
  word = line;
     
  /* Call the function. */
  return (*(command->func))(word);
}


static void
commandline_parse(char* line) {
  char* s;

  if (line == NULL) { /* EOF */
    if ((s = strdup("exit")) == NULL)
      exit(1);
    cout << "quit\n";
  }
  else {
    //    fprintf(stderr, "commandline_parse : '%s'\n", line);
    s = stripwhite(line); /* remove leading and trailing whitespace */

    if (*s == '\0') { /* empty line */
      free(line);
      return;
    }
#ifdef HAVE_LIBREADLINE
    add_history(s);
#endif
  }

  execute_line(s);

  free(line);
}


#ifdef HAVE_LIBREADLINE

/* **************************************************************** */
/*                  Interface to Readline Completion                */
/* **************************************************************** */

static char* null_command_generator(const char*, int);
static char* command_generator(const char*, int);
static char* option_generator(const char*, int);
static char** wflogs_completion(const char*, int, int);

/* Tell the GNU Readline library how to complete.  We want to try to complete
   on command names if this is the first word in the line, or on filenames
   if not. */
static void
initialize_readline() {
  /* Allow conditional parsing of the ~/.inputrc file. */
  rl_readline_name = "Wflogs";

  /* Tell the completer to return nothing by default (otherwise,
     rl_filename_completion_function() would be used). */
  rl_completion_entry_function = null_command_generator;

  /* Tell the completer that we want a crack first. */
  rl_attempted_completion_function = wflogs_completion;
}

/* Attempt to complete on the contents of TEXT.  START and END bound the
   region of rl_line_buffer that contains the word to complete.  TEXT is
   the word to complete.  We can use the entire contents of rl_line_buffer
   in case we want to do some simple parsing.  Return the array of matches,
   or NULL if there aren't any. */
static char**
wflogs_completion(const char* text, int start, int end) {
  int ti;
  char** matches = NULL; /* completion (none) by default */

  /* Determine if this could be a command word.  It is if it appears at
     the start of the line (ignoring preceding whitespace). */
  // fprintf(stderr, "|%s| %i %i\n", text, start, end);
  for (ti = start - 1; ti > -1 && isspace(rl_line_buffer[ti]); ti--);

  if (ti < 0)
    matches = rl_completion_matches(text, command_generator); /* command */
  else {
    /* complete only a single option RV@@8
       for (ti--; ti > -1 && !isspace(rl_line_buffer[ti]); ti--);
       for (ti--; ti > -1 && isspace(rl_line_buffer[ti]); ti--);

       if (ti < 0)
    */
    matches = rl_completion_matches(text, option_generator); /* option */
  }
  return matches; /* if NULL, no completion */
}

/* Return no match. */
static char*
null_command_generator(const char* text, int state) {
  return NULL;
}

/* Generator function for command completion.  STATE lets us know whether
   to start from scratch; without any state (i.e. STATE == 0), then we
   start at the top of the list. */
static char*
command_generator(const char* text, int state) {
  static int list_index, len;
  char* name;

  /* If this is a new word to complete, initialize now.  This includes
     saving the length of TEXT for efficiency, and initializing the index
     variable to 0. */
  if (!state) {
    list_index = 0;
    len = strlen(text);
  }

  /* Return the next name which partially matches from the command list. */
  while ((name = commands[list_index].name)) {
    list_index++;
    if (!strncmp(name, text, len))
      return strdup(name);
  }
  return NULL;  /* no names matched */
}

/* Generator function for option completion.  STATE lets us know whether
   to start from scratch; without any state (i.e. STATE == 0), then we
   start at the top of the list. */
static char*
option_generator(const char* text, int state) {
  static int list_index, len;

  /* If this is a new word to complete, initialize now.  This includes
     saving the length of TEXT for efficiency, and initializing the index
     variable to 0. */
  if (!state) {
    list_index = 0;
    len = strlen(text);
    // printf("\n|state: %s|\n", text); /* RV@@7 */
    return NULL;
    return ""; /* return to adequate option, here RV@@8 */
  }
  return NULL;  /* no name matched */
}

#else /* now, if we don't have readline */

static char*
commandline_get() {
  char buf[1024];
  int i;

  i = read(0, buf, sizeof(buf));
  if (i == -1) {
    perror(_("Read error"));
    return NULL;
  }
  if (i == 0)
    return NULL;

  if (buf[i - 1] == '\n')
    buf[i - 1] = '\0'; /* strip \n */

  return strdup(buf);
}

#endif

static void
change_mode(const char* modename, int* modevar, const char* arg) {
  if (arg == NULL || *arg == '\0')
    *modevar = 1 - (*modevar != 0);
  else if (!strcmp(arg, "on"))
    *modevar = 1;
  else if (!strcmp(arg, "off"))
    *modevar = 0;
  else if (!strcmp(arg, "?"))
    printf(_("Current value: "));
  else {
    puts(_("Invalid argument"));
    return;
  }

  if (*modevar)
    printf(_("%s mode on\n"), modename);
  else
    printf(_("%s mode off\n"), modename);
}

static void
change_mode_int(const char* modename, int* modevar, const char* arg) {
  if (arg == NULL || *arg == '\0' || !strcmp(arg, "?"))
    printf(_("Current value: "));
  else {
    char* p;
    int num = strtol(arg, &p, 0);
    if (*p != '\0') {
      puts(_("Invalid argument"));
      return;
    }
    *modevar = num;
  }
  printf(_("%s set to %i\n"), modename, *modevar);
}

static void
change_mode_bool(const char* modename, bool* modevar, const char* arg) {
  /* we have to do this as calling change_mode with a cast doesn't work */
  int tmp = *modevar;
  change_mode(modename, &tmp, arg);
  *modevar = tmp;
}

#if 0 // RV@@6

/* Return non-zero if ARG is a valid argument for CALLER, else print
   an error message and return zero. */
static int
valid_argument(const char* caller, const char* arg) {
  if (arg == NULL || *arg == '\0') {
    printf(_("%s: argument required.\n"), caller);
    return 0;
  }
  return 1;
}

#endif

/* Print out help for ARG, or for all of the commands if ARG is
   not present. */
static int
com_help(const char* arg) {
  int i;
  int printed = 0;
     
  for (i = 0; commands[i].name; i++) {
    if (*arg == '\0' || !strcmp(arg, commands[i].name)) {
      printf("%-16s%s.\n", commands[i].name, _(commands[i].doc));
      printed++;
    }
  }
     
  if (!printed) {
    printf(_("No command matches `%s'.  Possibilities are:\n"), arg);
     
    for (i = 0; commands[i].name; i++) {
      /* Print in six columns. */
      if (printed == 6) {
      printed = 0;
      putchar('\n');
      }
     
      printf("%s\t", commands[i].name);
      printed++;
    }
     
    if (printed)
      putchar('\n');
  }
  return 0;
}

/* The user wishes to quit this program. */
static int
com_quit(const char* arg) {
  exit(0);
}

static int
com_beep(const char* arg) {
  change_mode_bool("beep", &beep, arg);
  return 0;
}

static int
com_filter(const char* arg) {
  if (arg == NULL || *arg == '\0' || !strcmp(arg, "?")) {
    printf(_("Filter expression: "));
    if (myfilter != NULL)
      myfilter->print(cout) << endl;
    else
      puts(_("none"));
    return 0;
  }
  wflogs_filter* newmyfilter = NULL;
  if (strcmp(arg, "unset")) {
    newmyfilter = new wflogs_filter();
    if (newmyfilter == NULL || newmyfilter->set(arg) == false) {
      puts(_("Wrong filter expression."));
      if (newmyfilter != NULL)
      delete newmyfilter;
      return 1;
    }
  }
  /* Now replace the old expression by the new (the new may be null). */
  if (myfilter != NULL)
    delete myfilter;
  myfilter = newmyfilter;

  if (verbose) {
    printf(_("Filter expression: "));
    if (myfilter != NULL)
      myfilter->print(cout) << endl;
    else
      puts(_("none"));
  }

  return 0;
}

static int
com_realtime(const char* arg) {
  change_mode_bool("realtime", &realtime, arg);
  /* don't forget to init realtime mode here (if not initialised yet) RV@@8 */
  return 0;
}

static int
com_verbose(const char* arg) {
  /* only global verbose is changed, not output_module::verbose RV@@9 */
  change_mode_int("verbose", &verbose, arg);
  return 0;
}


wf_interactive::wf_interactive(const string& myprompt) {
  cout << '\n' << _("Type `help' for a list of available commands.") << '\n';
#ifdef HAVE_LIBREADLINE
  initialize_readline();
  rl_callback_handler_install(myprompt.c_str(), commandline_parse);
#endif
  prompt = myprompt;
}

void
wf_interactive::display_prompt() const {
#ifndef HAVE_LIBREADLINE
  cout << prompt << flush;
#endif
  /* display is automatic if handled by readline */
}

void
wf_interactive::display_update() const {
#ifdef HAVE_LIBREADLINE
  rl_forced_update_display();
#else
  display_prompt();
#endif
}

void
wf_interactive::callback() const {
#ifdef HAVE_LIBREADLINE
  rl_callback_read_char(); /* every char */
#else
  commandline_parse(commandline_get()); /* every line */
  display_prompt();
#endif
}

Generated by  Doxygen 1.6.0   Back to index