/*+++++++++++++++++
  client-commands.c - common commands for interactive clients
  markus@mhoenicka.de 3-7-00
  $Id: client-commands.c,v 1.6.2.2 2005/07/29 21:15:34 mhoenicka Exp $

   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, see <http://www.gnu.org/licenses/>

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>

#include "refdb.h"
#include "connect.h"
#include "linklist.h"
#include "pref.h"
#include "client-commands.h"
#include "refdb-client.h"
#include "readln.h"
#include "strfncs.h"
#include "tokenize.h"

extern int n_verbose;
extern int n_done;
extern COMMAND commands[]; 
extern Prefs prefs[];

extern char readline_name[];
extern int n_oldlog_dest;
extern char oldlog_file[];
extern int n_log_dest;
extern FILE* fp_log_file;
extern char log_dest[];
extern char log_file[];
extern int n_log_level;

/* forward declarations of local functions */
static int is_valid_varname(char* varname);
static void list_variables(void);
static void reload_var(const char* varname);

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_verbose(): toggle verbose mode

  int com_verbose returns always 0

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_verbose (char *arg)
{
  if (strncmp(arg, "-h", 2) == 0) {
    printf("Toggles the verbose mode on and off. If on, the error messages tend to be somewhat more explicit.\nSyntax: verbose [-h]\nOptions: -h           prints this mini-help\n");
    return 0;
  }

  n_verbose = !n_verbose;
  if (n_verbose) {
    printf("Switched verbose mode ON\n");
  }
  else {
    printf("Switched verbose mode OFF\n");
  }
  return (0);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_quit(): The user wishes to quit using this program.

  int com_quit returns always 0

  char *arg currently not used

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_quit (char *arg)
{
  if (strncmp(arg, "-h", 2) == 0) {
    printf("Closes the session and exits.\nSyntax: quit [-h]\nOptions: -h           prints this mini-help\n");
    return 0;
  }

  n_done = 1;
  return (0);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_help(): Print out help for ARG, or for all of the commands if
              ARG is not present.

  void com_help returns always 0

  char *arg name of a command, or empty string. In the latter case
            help for all commands will be displayed

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_help (char *arg)
{
  register int i;
  int n_printed = 0;

  printf("This is a command overview. To get specific information about a command, run this command with the -h option.\n");

  for (i = 0; commands[i].name; i++)
    {
      if (!*arg || (strcmp (arg, commands[i].name) == 0))
        {
	  if (strlen(commands[i].name) >= 8) {
	    printf ("%s\t\t%s.\n", commands[i].name, commands[i].doc);
	  }
	  else {
	    printf ("%s\t\t\t%s.\n", commands[i].name, commands[i].doc);
	  }
          n_printed++;
        }
    }

  if (!n_printed)
    {
      printf ("No commands match `%s'.  Your choices are:\n", arg);

      for (i = 0; commands[i].name; i++)
        {
          /* Print in six columns. */
          if (n_printed == 6)
            {
              n_printed = 0;
              printf ("\n");
            }

          printf ("%s\t", commands[i].name);
          n_printed++;
        }

      if (n_printed)
        printf ("\n");
    }
  return (0);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_listdb(): list databases
 
  int com_listdb 0 if successful, 1 if error

  char *arg search pattern for the database names. This must be a
            valid SQL regular expression. If this string is empty,
            the search will return all existing databases

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_listdb (char* arg)
{
  return listvalue(arg, "listdb", "Lists the names of the available databases. If an argument is given as a SQL regular expression, only the matching databases are shown.\nSyntax: listdb [-h] [SQL-regexp]\nOptions: -h           prints this mini-help\n         All other arguments are interpreted as an SQL regular expression.\n", "could not connect", 1);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_liststyle(): list bibliography styles
 
  int com_liststyle 0 if successful, 1 if error

  char *arg search pattern for the bibliography names. This must be a
            valid SQL regular expression. If this string is empty,
            the search will return all existing databases

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_liststyle (char* arg)
{
  return listvalue(arg, "liststyle", "Lists the names of the available bibliography styles. If an argument is given as a Unix regular expression, only the matching styles are shown.\nSyntax: liststyle [-h] [unix-regexp]\nOptions: -h           prints this mini-help\n         All other arguments are interpreted as a Unix regular expression.\n", "could not connect", 1);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_setvalue(): lists or sets configuration variables

  int com_setvalue returns 0 if ok, 1 if failure

  char* arg ptr to string with the command arguments

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_setvalue (char* arg) {
  int inargc; /* number of tokens in the command */
  int inargcmax; /* maximum number of tokens */
  int error;
  int n_varindex;
  char **inargv; /* tokens of the command */
  char oldval[PREFS_BUF_LEN] = "";
  char *stripped_arg;

  if (!strcmp(arg, "-h")) {
    fprintf(stderr, "Set a new value for a configuration variable\nSyntax: set varname varvalue\n");
    return 0;
  }

  inargc = 0;
  inargcmax = 10;
  inargv = malloc((size_t)inargcmax*sizeof(char*));

  if (inargv == NULL) {
    fprintf(stderr, "out of memory\n");
    return 1;
  }

  error = string_tokenize(&inargc, &inargv, inargcmax, arg);
  if (error == 2) { /* no arguments, list all names and values */
    list_variables();
    free(inargv);
    return 0;
  }
  else if (error == 1) {
    fprintf(stderr, "out of memory\n");
    free(inargv);
    return 1;
  }

  if ((n_varindex = is_valid_varname(inargv[0])) == -1) {
    fprintf(stderr, "invalid variable name\n");
    free(inargv);
    return 1;
  }

  if (inargc == 1) { /* only variable name, show its current value */
    if (strcmp(prefs[n_varindex].varname, "passwd")) { /* don't reveal password */
      fprintf(stderr, "%s %s\n", prefs[n_varindex].varname, prefs[n_varindex].varvalue);
    }
    free(inargv);
    return 0;
  }
  else { /* at least two arguments, ignore excess arguments */
    if (strlen(inargv[1]) > PREFS_BUF_LEN) {
      fprintf(stderr, "value string too long\n");
      free(inargv);
      return 1;
    }

    stripped_arg = strip_quote(inargv[1]);
    strcpy(oldval, prefs[n_varindex].varvalue); /* save old value */
    strcpy(prefs[n_varindex].varvalue, stripped_arg); /* set new value */

    if (postprocess_var(prefs[n_varindex].varname)) {
      strcpy(prefs[n_varindex].varvalue, oldval); /* roll back */
      if (postprocess_var(prefs[n_varindex].varname)) {
	fprintf(stderr, "could not revert to old value\n");
	free(inargv);
	return 1;
      }
    }
    /* else: all fine */
  }
  
  reload_var(prefs[n_varindex].varname);

  if (strcmp(prefs[n_varindex].varname, "passwd")) { /* don't reveal password */
    fprintf(stderr, "new value of %s: %s\n", prefs[n_varindex].varname, prefs[n_varindex].varvalue);
  }

  free(inargv);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  is_valid_varname(): checks whether a variable exists 

  static int is_valid_varname returns the index in the Prefs[] array if the
                       variable name exists. Returns -1 if the
		       variable name does not exist.

  char* varname ptr to string containing the variable name

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int is_valid_varname(char* varname) {
  int i = 0;

  if (varname == NULL || !varname[0]) { /* make sure we have something to compare */
    return -1;
  }

  while (*(prefs[i].varname)) {
    if (!strcmp(varname, prefs[i].varname)) {
      return i;
    }
    i++;
  }
  return -1;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  list_variables(): displays a table of variable names and values

  static void list_variables returns nothing

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void list_variables(void) {
  int i = 0;

  /* could be beefed up a bit */
  while (*(prefs[i].varname)) {
    if (strcmp(prefs[i].varname, "passwd")) { /* don't reveal password */
      fprintf(stderr, "%s %s\n", prefs[i].varname, prefs[i].varvalue);
    }
    i++;
  }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  reload_var(): restarts things after variables were changed. The
                current implementation only needs to restart logging

  static void list_variables returns nothing

  const char* varname ptr to a string with the name of the changed variable

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void reload_var(const char* varname) {
  if (!strcmp(varname, "logfile") ||
      !strcmp(varname, "logdest")) { /* some log setting changed, restart */
    /* finish old logging */
    if (n_oldlog_dest == 2) {
      fclose(fp_log_file);
    }
    else if (n_oldlog_dest == 1) {
      closelog(); /* be nice */
    }

    /* set up logging */
    if (n_log_dest == 2) { /* use custom log file */
      if ((fp_log_file = fopen(log_file, "ab")) == NULL) {
	n_log_dest = 1; /* fall back to syslog */
	log_dest[0] = '1';
	log_dest[1] = '\0';
	openlog(readline_name, LOG_PID|LOG_ODELAY, LOG_USER);
	LOG_PRINT(LOG_WARNING, "could not open custom log file");
      }
    }
    else if (n_log_dest == 1) { /* use syslog */
      openlog(readline_name, LOG_PID|LOG_ODELAY, LOG_USER);
    }

    n_oldlog_dest = n_log_dest; /* create new backup copies */
  }
}

