/*+++++++++++++++++
  refdba - the refdb administration console application
  markus@mhoenicka.de 2-10-00
  $Id: refdba.c,v 1.45.2.9 2005/11/05 23:50:35 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/>

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

/* ToDo: implement a fixdb command to ensure the integrity of the database ?? */

/* general includes */
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/fcntl.h>

#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>

#include <unistd.h> 
#include <limits.h>
#include <syslog.h>
#include "getopt.h"

/* our own stuff */
#include "refdb.h"  /* common stuff for all refdb applications*/
#include "connect.h"
#include "linklist.h" /* linked list support */
#include "pref.h" /* for init file, depends on linklist.h */
#include "strfncs.h" /* for is functions */
#include "refdba.h" /* stuff specific to this file */
#include "readln.h"  /* readline-related  stuff */
#include "page.h" /* pager functions */
#include "refdb-client.h" /* stuff common to all clients */
#include "client-commands.h" /* common interactive commands */
#include "connect.h" /* modified read/write socket functions */
#include "tokenize.h" /* tokenizes command-lines */
#include "readris.h" /* read RIS datasets */
#include "passwd.h" /* securely obtain passwords */
#include "enigma.h"

/* this is the easiest way to add portability to Cygwin */
/* O_BINARY is defined in fcntl.h on Win32/Cygwin */
#ifndef O_BINARY
#define O_BINARY 0
#endif

/*+ the commands array contains the user commands, the functions called, and
  short explanatory messages +*/
COMMAND commands[] = {
  { "help", com_help, "Display this text" },
  { "?", com_help, "Synonym for `help'" },
  { "quit", com_quit, "Quit using refdba" },
  { "addstyle", com_addstyle, "Add or update a bibliography style" },
  { "adduser", com_adduser, "Add users" },
  { "addword", com_addword, "Add reserved journal words" },
  { "confserv", com_confserv, "Configure the application server" },
  { "createdb", com_createdb, "Create a new database" },
  { "deletedb", com_deletedb, "Delete a database" },
  { "deletestyle", com_deletestyle, "Delete a bibliography style" },
  { "deleteuser", com_deleteuser, "Remove users" },
  { "deleteword", com_deleteword, "Remove reserved journal words" },
  { "getstyle", com_getstyle, "Retrieve a bibliography style" },
  { "scankw", com_scankw, "Run keyword scan" },
  { "listdb", com_listdb, "List databases" },
  { "liststyle", com_liststyle, "List bibliography styles" },
  { "listuser", com_listuser, "Get a list of users" },
  { "listword", com_listword, "Get a list of reserved journal words" },
  { "set", com_setvalue, "Set new value of config variable" },
  { "verbose", com_verbose, "Toggle verbose mode" },
  { "viewstat", com_viewstat, "View statistics" },
  { (char *)NULL, (Function *)NULL, (char *)NULL }
};

/* Globals */

/*+ When non-zero, this global means the user is done using this program. +*/
int n_done;

/*+ this array will hold the user preferences +*/
Prefs prefs[13] = {
  {"serverip", ""},
  {"port", ""},
  {"verbose", ""},
  {"pager", ""},
  {"username", ""},
  {"passwd", ""},
  {"timeout", ""},
  {"logfile", ""},
  {"logdest", ""},
  {"loglevel", ""},
  {"refdblib", ""},
  {"no_encrypt", ""},
  {"", ""}
};

/* these are the configurable variables with the compile-time defaults */
char server_ip[PREFS_BUF_LEN] = "127.0.0.1"; /*+ default IP address of refdbd +*/
char port_address[PREFS_BUF_LEN] = "9734"; /*+ default port address of refdbd +*/
char the_pager[PREFS_BUF_LEN] = "stdout"; /*+ default "pager" (stdout) +*/
char username[PREFS_BUF_LEN] = ""; /*+ default username (emtpy) +*/
char passwd[PREFS_BUF_LEN] = "*"; /*+ default password (ask user) +*/
char refdb_timeout[PREFS_BUF_LEN] = "180"; /*+ 180 seconds default timeout +*/
char verbose[PREFS_BUF_LEN] = "f"; /*+ 1 = verbose output, 0 = terse output +*/
char log_file[PREFS_BUF_LEN] = "/var/log/refdba.log"; /*+ default log file +*/
char log_dest[PREFS_BUF_LEN] = "2"; /*+ default log destination (0 = stderr, 1 = syslog, 2 = log_file +*/
char log_level[PREFS_BUF_LEN] = "6"; /*+ default level up to which messages are logged (0 through 7). -1 means no logging +*/
char refdblib[PREFS_BUF_LEN] = ""; /* path to shareable files */
char confdir[_POSIX_PATH_MAX+1] = ""; /* path to the config files */
char no_encrypt[PREFS_BUF_LEN] = ""; /* do not encrypt passwords if 't' */
int main_argc = 0; /* save argc for commands in batch mode */
char **main_argv = NULL; /* save argv for commands in batch mode */

#ifdef READLINE41
char* rl_readline_name; /* name used for readline history */
#else
const char* rl_readline_name; /* name used for readline history */
#endif /* READLINE41 */

char readline_name[] = "refdba";
CPPFunction* rl_attempted_completion_function; /* ptr to completer */
int n_refdb_timeout;
int n_verbose = 0; /*+ do we want logorrhoeic output? +*/

int n_broken_pipe; /*+ 1 indicates that we attempted to write to a broken pipe +*/
int n_abort_connect; /*+ 1 indicates that we want to abort a connection +*/
int n_log_dest = 1; /* destination of log output */
int n_oldlog_dest = 1; /* previous destination of log output */
int n_log_level = 0; /* level of log information that will be printed */
int n_cgi = 0; /* if 1, we run as a cgi app (currently dummy) */
int n_batchmode = 0; /* 1 if we're running in batch mode */
int n_read_stdin = 0; /* if 1, data try to squeeze in at stdin */

FILE* fp_log_file = NULL; /* ptr to log file struct */

extern const char cs_term[];

/* prototypes of local functions */
static int adduser(int n_remove, char* arg);
static int addword (int n_remove, char* arg);

/* declaration of the svn version function */
const char* svn_version(void);

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  The one and only main function
  
  int main returns 0 if successful (this is always the case in interactive
           mode), 1 if error
  
  int argc number of arguments

  char** argv ptr to array of strings with the command line arguments
  
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int main (int argc, char** argv)
{
  char the_opt[5];
  char the_cmd[32] = "";
  char *line, *s;
  char *the_command;
  char *new_the_command;
  size_t the_command_len = 512;
  int retval = 0;
  int n_opt, i, j;
  int n_readinit = 1; /* if 1, read config file. If 0, skip config file */

  struct sigaction act, oldact, intact, oldintact;

  if ((the_command = malloc(the_command_len)) == NULL) {
    fprintf(stderr, "out of memory\n");
    exit (1);
  }
  the_command[0] = '\0';

  /* Allow conditional parsing of the ~/.inputrc file. */
  rl_readline_name = readline_name;

  /* initialize signal handler */
  n_broken_pipe = 0;
  n_abort_connect = 0;

  act.sa_handler = pipehandler;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;

  intact.sa_handler = inthandler;
  sigemptyset(&intact.sa_mask);
  intact.sa_flags = 0;

  if (sigaction(SIGPIPE, &act, &oldact) != 0 ||
      sigaction(SIGINT, &intact, &oldintact) != 0) {
    fprintf(stderr, "initializing signal handlers failed\n");
    exit(1);
  }

  /* initialize the array of preference values */
  prefs[0].varvalue = server_ip;
  prefs[1].varvalue = port_address;
  prefs[2].varvalue = verbose;
  prefs[3].varvalue = the_pager;
  prefs[4].varvalue = username;
  prefs[5].varvalue = passwd;
  prefs[6].varvalue = refdb_timeout;
  prefs[7].varvalue = log_file;
  prefs[8].varvalue = log_dest;
  prefs[9].varvalue = log_level;
  prefs[10].varvalue = refdblib;
  prefs[11].varvalue = no_encrypt;

  /* a slimy hack to detect options before we run getopt */
  j = 2;
  for (i = 0; i < argc; i++) {
    if (argv[i][0] == '-' && argv[i][1] == 'q') {
      n_readinit = 0;
      j--;
      if (!j) {
	break;
      }
    }
    if (argv[i][0] == '-' && argv[i][1] == 'y') {
      strncpy(confdir, argv[i+1], _POSIX_PATH_MAX);
      confdir[_POSIX_PATH_MAX] = '\0';
      j--;
      if (!j) {
	break;
      }
    }
  }

  if (n_readinit) {
    /* read config file settings */
    read_prefs(prefs, "refdbarc", 0);
  }

  /* read command line settings. These may override the config file settings */
  while ((n_opt = getopt(argc, argv, "c:d:C:e:E:f:hH:i:l:L:o:O:p:qrRT:u:vVw:W:xy:")) != -1) {
    switch (n_opt) {
    case 'c':
      strncpy(the_pager, optarg, PREFS_BUF_LEN);
      the_pager[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'C':
      strncpy(the_cmd, optarg, 31);
      the_cmd[31] = '\0';
      if (!find_command(optarg, commands)) {
	fprintf (stderr, "%s: No such command for refdb.\n", optarg);
	exit (1);
      }
      n_batchmode = 1;
      break;
    case 'e':
      strncpy(log_dest, optarg, PREFS_BUF_LEN);
      log_dest[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'h':
      if (!n_batchmode) {
	fprintf(stderr, "Usage: refdba [-c pager] [-C command] [-e logdest] [-h] [-i IP_address] [-l loglevel] [-L logfile] [-p port] [-q] [-T time] [-u name] [-v] [-V] [-w password] [-x] [-y confdir]\nOptions: -c command to run a pager\n         -C run command in batch mode\n         -e log destination (0=stderr;1=syslog;2=custom file)\n         -h prints this help\n         -i set server IP address to address\n         -l log level (0<=level<=7)\n         -L full path to custom log file\n         -p set server port to port\n         -q ignore init-file\n         -T set timeout to time seconds\n         -u set username\n         -v show version information\n         -V switch to verbose mode\n         -w password (use '*' to be asked interactively)\n         -x do not encrypt passwords\n         -y look for configuration files in confdir\n");
	exit (0);
      }
      else {
	if ((new_the_command = mstrcat(the_command, " -h ", &the_command_len, 0)) == NULL) {
	  fprintf(stderr, "out of memory\n");
	  exit (1);
	}
	else {
	  the_command = new_the_command;
	}
      }
      break;
    case 'i':
      strncpy(server_ip, optarg, PREFS_BUF_LEN);
      server_ip[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'l':
      strncpy(log_level, optarg, PREFS_BUF_LEN);
      log_level[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'L':
      strncpy(log_file, optarg, PREFS_BUF_LEN);
      log_file[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'p':
      strncpy(port_address, optarg, PREFS_BUF_LEN);
      port_address[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'q':
      n_readinit = 0;
      break;
    case 'T':
      strncpy(refdb_timeout, optarg, PREFS_BUF_LEN);
      refdb_timeout[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'u':
      strncpy(username, optarg, USERNAME_LENGTH);
      username[USERNAME_LENGTH] = '\0'; /* terminate if optarg string was too long */
      break;
    case 'v':
      printf("refdba %s built from svn revision %s markus@mhoenicka.de\nYou may redistribute and modify this software under the terms of the GNU General Public License.\n", VERSION, svn_version());
      exit (0);
      break;
    case 'V':
      verbose[0] = 't'; /* poor man's strcpy */
      verbose[1] = '\0';
      break;
    case 'w':
      strncpy(passwd, optarg, PREFS_BUF_LEN);
      passwd[PREFS_BUF_LEN-1] = '\0';
      break;
    case 'x':
      strcpy(no_encrypt, "t");
      break;
    case 'y':
      /* do nothing, this option is used before getopt runs */
      break;

      /* now all the options that the commands handle themselves */

    case 'd': /* fall through, we assemble the command string */
    case 'E':
    case 'f':
    case 'H':
    case 'o':
    case 'O':
    case 'R':
    case 'W':
    case 'r':
      sprintf(the_opt, " -%c ", n_opt);
      if ((new_the_command = mstrcat(the_command, the_opt, &the_command_len, 0)) == NULL) {
	fprintf(stderr, "out of memory\n");
	exit (1);
      }
      else {
	the_command = new_the_command;
      }
      if (optarg && *optarg) {
	if ((new_the_command = mstrcat(the_command, optarg, &the_command_len, 0)) == NULL) {
	  fprintf(stderr, "out of memory\n");
	  exit (1);
	}
	else {
	  the_command = new_the_command;
	}
      }
      break;
    case ':':
      fprintf(stderr, "Usage: refdba [-c pager] [-C command] [-e logdest] [-h] [-i IP_address] [-l loglevel] [-L logfile] [-p port] [-q] [-T time] [-u name] [-v] [-V] [-w password] [-x] [-y confdir]\nOptions: -c command to run a pager\n         -C run command in batch mode\n         -e log destination (0=stderr;1=syslog;2=custom file)\n         -h prints this help\n         -i set server IP address to address\n         -l log level (0<=level<=7)\n         -L full path to custom log file\n         -p set server port to port\n         -q ignore init-file\n         -T set timeout to time seconds\n         -u set username\n         -v show version information\n         -V switch to verbose mode\n         -w password (use '*' to be asked interactively)\n         -x do not encrypt passwords\n         -y look for configuration files in confdir\n");
      exit (1);
      break;
    case '?':
      fprintf(stderr, "unknown option %c: use refdba -h to display usage\n", optopt);
      break;
    }
  }

  /* a smart but simple hack to hide the password in the ps ax output */
  for (i = 0; i < argc; i++) {
    if (argv[i][0] == '-' && argv[i][1] == 'w') {
      j = 0;
      while (argv[i+1][j]) {
	argv[i+1][j] = 'x';
	j++;
      }
      break;
    }
  }

  /* translate some command line settings into numeric values */
  postprocess_var("timeout");
  postprocess_var("loglevel");
  postprocess_var("logdest");
  n_oldlog_dest = n_log_dest; /* keep a backup copy if changed by set */
  postprocess_var("verbose");

  /* 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("refdba", 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("refdba", LOG_PID|LOG_ODELAY, LOG_USER);
  }

  /* check values */
  if (postprocess_var("serverip")) {
    LOG_PRINT(LOG_CRIT, "incorrect IP address or hostname");
    exit (1);
  }

  if (postprocess_var("port")) {
    LOG_PRINT(LOG_CRIT, "incorrect port");
    exit (1);
  }

  /* see whether we have a username */
  if (postprocess_var("username")) {
    fprintf(stderr, "incorrect username\n");
    LOG_PRINT(LOG_CRIT, "incorrect username");
    exit (1);
  }

  /* see whether we need a password */
  postprocess_var("passwd");

/*      printf("username: %s; password: %s<<\n", username, passwd); */

  if (n_batchmode) { /* we're running in batch mode */
    main_argv = argv;
    main_argc = argc;

    s = stripwhite (the_cmd, 0, 0);

    if (*s) {
      char* batchstring;
      /* s contains only the command name. For a full command line, add
	 the remainder of the argv string array */
      if ((batchstring = build_batchcommand(argc, argv, optind, s, the_command)) == NULL) {
	LOG_PRINT(LOG_WARNING, "out of memory");
	retval = 1;
      }
      else {
	LOG_PRINT(LOG_DEBUG, batchstring);
	retval = execute_line(batchstring, commands);
	free(batchstring);
      }
    }
  }
  else { /* we're running in interactive mode */
    if (n_verbose) {
      fprintf(stderr, "refdba %s markus@mhoenicka.de\nYou may redistribute and modify this software under the terms of the GNU General Public License.\nType '?' for a command overview\n", VERSION);
    }

    initialize_readline ();	/* Bind our completer. */

    /* Loop reading and executing lines until the user quits. */
    for ( ; n_done == 0; ) {
      line = readline ("refdba: ");

      if (!line)
	break;
      
      /* Remove leading and trailing whitespace from the line.
	 Then, if there is anything left, add it to the history list
	 and execute it. */
      s = stripwhite (line, 0, 0);

      if (*s) {
	LOG_PRINT(LOG_DEBUG, s);
	add_history (s);
	retval = execute_line (s, commands);
	n_abort_connect = 0; /* reset just in case Ctrl-C was not used
				to interrupt a stalled connection, but
				rather randomly */
      }

      free (line);
    } /* for */
  } /* if/else */

  exit (retval);
}

/* **************************************************************** */
/*                                                                  */
/*                   refdba Commands                                */
/*                                                                  */
/* **************************************************************** */


/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_createdb(): create a new database

  int com_createdb  0 if successful, 1 on error

  char *arg the name of the new database. This must be a name which
            is a valid filename on the operating system that the
            database server runs on. 

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_createdb (char* arg)
{
  char outbuffer[COMMAND_INBUF_LEN] = ""; /* holds the command for the server */
  char inbuffer[COMMAND_INBUF_LEN] = "";
  struct simplelistvals slvals;
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";

  slvals.outbuffer = outbuffer;

  strcpy(slvals.outbuffer, "createdb ");
  slvals.n_file_open = 0;
  slvals.n_file_append = 0;
  slvals.n_pipe = 0;
  slvals.outfile = NULL;
  slvals.outpipe = NULL;

  if (strncmp(arg, "-h", 2) == 0) {
    printf("Creates new databases\nSyntax: createdb [-E encoding] [-h] {name} [name1...]\nOptions: -E           set character encoding\n         -h           prints this mini-help\nAll other arguments are interpreted as database names\n");
    return 0;
  }

  if (connect_to_server(&slvals.n_sockfd, server_ip, port_address) != 0) {
    return 1;
  }

  strcpy(scrambled_passwd, passwd);

  if (init_dialog(slvals.n_sockfd, scrambled_passwd, inbuffer)) {
    close(slvals.n_sockfd);
    return 1;
  }

  /* arg will contain the encoding (-E <encoding>) if specified */
  strcat(slvals.outbuffer, arg);
  strcat(slvals.outbuffer, " -u ");
  strcat(slvals.outbuffer, username);
  if (strlen(passwd) > 0) {
    strcat(slvals.outbuffer, " -w ");
    strcat(slvals.outbuffer, scrambled_passwd);
  }

  getsimplelist(&slvals, 1);

  close(slvals.n_sockfd);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_deletedb(): delete a database

  int com_deletedb 0 if successful, 1 if error 

  char *arg the name of the database which is to be deleted

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_deletedb (char* arg)
{
  char outbuffer[COMMAND_INBUF_LEN] = ""; /* holds the command for the server */
  char inbuffer[COMMAND_INBUF_LEN] = "";
  struct simplelistvals slvals;
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";

  slvals.outbuffer = outbuffer;

  strcpy(slvals.outbuffer, "deletedb ");
  slvals.n_file_open = 0;
  slvals.n_file_append = 0;
  slvals.n_pipe = 0;
  slvals.outfile = NULL;
  slvals.outpipe = NULL;

  if (strncmp(arg, "-h", 2) == 0) {
    printf("Deletes databases\nSyntax: deletedb [-h] {name} [name1...]\nOptions: -h           prints this mini-help\nAll other arguments are interpreted as database names\n");
    return 0;
  }

  if (connect_to_server(&slvals.n_sockfd, server_ip, port_address) != 0) {
    return 1;
  }

  strcpy(scrambled_passwd, passwd);

  if (init_dialog(slvals.n_sockfd, scrambled_passwd, inbuffer)) {
    close(slvals.n_sockfd);
    return 1;
  }

  strcat(slvals.outbuffer, arg);
  strcat(slvals.outbuffer, " -u ");
  strcat(slvals.outbuffer, username);
  if (strlen(passwd) > 0) {
    strcat(slvals.outbuffer, " -w ");
    strcat(slvals.outbuffer, scrambled_passwd);
  }

  getsimplelist(&slvals, 1);

  close(slvals.n_sockfd);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_confserv(): configure application server

  int com_confserv 0 if successful, 1 if error

  char *arg command argument. Currently supported by refdbd are stop,
            ping, serverip value, timeout value, logfile value,
            logdest value, loglevel value, cleanvar

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_confserv (char* arg)
{
  char outbuffer[COMMAND_INBUF_LEN] = ""; /* holds the command for the server */
  char inbuffer[COMMAND_INBUF_LEN] = "";
  struct simplelistvals slvals;
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";

  slvals.outbuffer = outbuffer;

  strcpy(slvals.outbuffer, "confserv ");
  slvals.n_file_open = 0;
  slvals.n_file_append = 0;
  slvals.n_pipe = 0;
  slvals.outfile = NULL;
  slvals.outpipe = NULL;

  if (strncmp(arg, "-h", 2) == 0) {
    printf("Reconfigures the application server (current session only)\nSyntax: confserv [-h] {command}\nOptions: -h           prints this mini-help\nSupported commands are:\n  stop            stops the application server\n  ping            tests whether the application server is alive\n  serverip value  set the serverip variable to value\n  timeout value   set the timeout variable to value\n  logfile value   set the logfile variable to value\n  logdest value   set the logdest variable to value\n  loglevel value  set the loglevel variable to value\n");
    return 0;
  }

  if (connect_to_server(&slvals.n_sockfd, server_ip, port_address) != 0) {
    return 1;
  }

  strcpy(scrambled_passwd, passwd);

  if (init_dialog(slvals.n_sockfd, scrambled_passwd, inbuffer)) {
    close(slvals.n_sockfd);
    return 1;
  }

  strcat(slvals.outbuffer, arg);
  strcat(slvals.outbuffer, " -u ");
  strcat(slvals.outbuffer, username);
  if (strlen(passwd) > 0) {
    strcat(slvals.outbuffer, " -w ");
    strcat(slvals.outbuffer, scrambled_passwd);
  }

  getsimplelist(&slvals, 1);

  close(slvals.n_sockfd);

  /* due to the odd implementation of the confserv command on the server side we have to send a second command to really let the server stop (the command is handled by a child and queued to the parent, so any changes take effect only just before the *next* command is accepted. this is ok for all commands except for stop which users expect to be immediate) */
  if (strncmp(arg, "stop", 4) == 0 && strncmp(slvals.inbuffer, "708", 3) == 0) { /* the second check is necessary to see
                                    whether confserv stop was successfully
				    submitted. We must not send strange
				    commands now if we don't have the access
				    rights to really stop the server */
    if (connect_to_server(&slvals.n_sockfd, server_ip, port_address) != 0) {
      return 1;
    }

    strcpy(scrambled_passwd, passwd);

    if (init_dialog(slvals.n_sockfd, scrambled_passwd, inbuffer)) { /* this will kill */
      close(slvals.n_sockfd);
      return 1;
    }
    close(slvals.n_sockfd);
  }
  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_viewstat(): view statistics

  int com_viewstat 0 if successful, 1 if error

  char *arg command argument, currently ignored by refdbd

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_viewstat (char* arg)
{
  int retval;
  char outbuffer[COMMAND_INBUF_LEN] = ""; /* holds the command for the server */
  char inbuffer[COMMAND_INBUF_LEN] = "";
  struct simplelistvals slvals;
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";

  slvals.outbuffer = outbuffer;

  strcpy(slvals.outbuffer, "viewstat ");
  slvals.n_file_open = 0;
  slvals.n_file_append = 0;
  slvals.n_pipe = 0;
  slvals.outfile = NULL;
  slvals.outpipe = NULL;

  if (strncmp(arg, "-h", 2) == 0) {
    printf("Shows some statistical and version information\nSyntax: viewstat [-h]\nOptions: -h           prints this mini-help\n");
    return 0;
  }

  if (connect_to_server(&slvals.n_sockfd, server_ip, port_address) != 0) {
    return 1;
  }

  strcpy(scrambled_passwd, passwd);

  if (init_dialog(slvals.n_sockfd, scrambled_passwd, inbuffer)) {
    close(slvals.n_sockfd);
    return 1;
  }

  /*  strcat(outbuffer, arg); */
  strcat(slvals.outbuffer, " -u ");
  strcat(slvals.outbuffer, username);
  if (strlen(passwd) > 0) {
    strcat(slvals.outbuffer, " -w ");
    strcat(slvals.outbuffer, scrambled_passwd);
  }

  retval = getsimplelist(&slvals, 1);

  close(slvals.n_sockfd);

  return retval;
}

/* Return non-zero if ARG is a valid argument for CALLER, else print
   an error message and return zero. */
/* int valid_argument (char *caller, char *arg) { */
/*   if (!arg || !*arg) */
/*     { */
/*       fprintf (stderr, "%s: Argument required.\n", caller); */
/*       return 0; */
/*     } */

/*   return 1; */
/* } */

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_adduser(): add or remove user

  int com_adduser 0 if successful, 1 if error 

  char *arg specifies the host, the database, and the usernames

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_adduser (char* arg) {
  return adduser(0, arg);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_deleteuser(): remove user

  int com_deleteuser 0 if successful, 1 if error 

  char *arg specifies the host, the database, and the usernames

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_deleteuser (char* arg) {
  return adduser(1, arg);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  adduser(): add or remove user

  static int adduser 0 if successful, 1 if error 

  int n_remove if 1, delete users, if 0, add users

  char *arg specifies the host, the database, and the usernames

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int adduser (int n_remove, char* arg)
{
  int n_sockfd; /* file descriptor of the socket */
  char *outbuffer;
  char *new_outbuffer;
  char inbuffer[COMMAND_INBUF_LEN] = "";
  char srv_inbuffer[COMMAND_INBUF_LEN] = "";
/*    char infile[_POSIX_PATH_MAX]; */
  char* infile;
  char database_name[DBNAME_LENGTH+1] = ""; 
  char user_host[HOSTNAME_LENGTH+1] = "";
  char newuser_passwd[PASSWD_LENGTH*3+1] = "";
  char newuser_scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  char **inargv; /* tokens of the argument */
  char *newarg;
/*    char *read_result; */
  char *send_buffer;
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  size_t outbuf_len = COMMAND_INBUF_LEN;
  int numbyte;
  int n_read_done = 0;
  int inargc = 0; /* number of tokens of the argument */
  int inargcmax; /* maximum number of tokens */
  int result;
  int i;
  int infile_fd;
  int n_opt;
  int n_readonly = 0;
  int n_read_file = 0;
  int n_cmdlinerror = 0;
  int n_just_help = 0;
  int n_curr_trailing_z = 0;
  int n_last_trailing_z = 0;
  int n_server_status;
  size_t byte_written = 0;
  FILE *pagerfp;
  struct lilimem sentinel;

  sentinel.ptr_mem = NULL;
  sentinel.ptr_next = NULL;
  sentinel.varname[0] = '\0';

  outbuffer = malloc(outbuf_len);
  if (outbuffer == NULL) {
    delete_all_lilimem(&sentinel);
    return 1;
  }
  
  if (insert_lilimem(&sentinel, (void**)&outbuffer, "outbuffer")) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  strcpy(outbuffer, "adduser ");

/*   if (n_batchmode) { */ /* in batchmode, the command line is already tokenized */
/*     inargc = main_argc; */
/*     inargv = main_argv; */
/*   } */
/*   else { */
    /* parse the argument. first we cut the argument
       into pieces with strtok, then we use getopt to interpret */

    /* get a buffer to hold the tokens. Start with 10 tokens,
       increase in steps of 10 as needed */
    inargc = 0;
    inargcmax = 10;
    inargv = malloc((size_t)inargcmax*sizeof(char*));
    if (inargv == NULL) {
      return 1;
    }

    if (insert_lilimem(&sentinel, (void**)&inargv, "inargv")) {
      return 1;
    }

    /* the following is a temporary hack to allow cmdln_tokenize to work */
    newarg = malloc((size_t)(strlen(arg)+9));
    if (newarg == NULL) {
      delete_all_lilimem(&sentinel);
      return 1;
    }

    if (insert_lilimem(&sentinel, (void**)&newarg, "newarg")) {
      delete_all_lilimem(&sentinel);
      return 1;
    }

    strcpy(newarg, "adduser ");
    strcat(newarg, arg);

    result = cmdln_tokenize(&inargc, &inargv, inargcmax, newarg);


    if (result == 1 || result == 2) { /* memory error */
      delete_all_lilimem(&sentinel);
      return 1;
    }
/*   } */ /* if/else */

  /* now we have the tokens nicely arranged in inargc */
/*    for (i = 0; i < inargc; i++) { */
/*      printf("inargv[%d]: %s\n", i, inargv[i]); */
/*    } */

  /* get options */
  optind = 0;

  while ((n_opt = getopt(inargc, inargv, "c:C:d:e:f:hH:i:l:L:o:O:p:qRT:u:vVw:W:")) != -1) {
    switch(n_opt) {
    case 'd':
/*        printf("-f %s\n", optarg); */
      strncpy(database_name, optarg, DBNAME_LENGTH);
      database_name[DBNAME_LENGTH] = '\0';  /* terminate in case string got truncated */
      break;
    case 'f':
      if (!strcmp(optarg, "stdin")) {
	n_read_stdin = 1;
      }
      else {
	infile = canonicalize_path(optarg);
	if (insert_lilimem(&sentinel, (void**)&infile, "infile")) {
	  delete_all_lilimem(&sentinel);
	  return 1;
	}
	n_read_file = 1;
      }
      break;
    case 'h':
      if (n_remove) {
	printf("Revokes access rights to a database for the given users\nSyntax: deleteuser {-d database} [-h] {-H hostname} {user|-f infile}\nOptions: -d database  specify the database\n         -f infile    Read the names of the users from file infile\n         -h           prints this mini-help\n         -H hostname  specify the hostname of the server that runs refdbd through which the users will connect\n         All other arguments are interpreted as usernames.\n");
      }
      else {
	printf("Grants access rights to a database for the given users\nSyntax: adduser {-d database} [-h] {-H hostname} [-R] [-W password] {user|-f infile}\nOptions: -d database  specify the database\n         -f infile    Read the names of the users from file infile\n         -h           prints this mini-help\n         -H hostname  specify the hostname of the server that runs refdbd through which the users will connect\n         -R grant read-only access (default: read/write)\n         -W new password for given user\n         All other arguments are interpreted as usernames.\n");
      }
      n_just_help++;
      break;
    case 'H':
/*       printf("-H %s\n", optarg); */
      strncpy(user_host, optarg, HOSTNAME_LENGTH);
      user_host[HOSTNAME_LENGTH] = '\0';  /* terminate in case string got truncated */
      break;
    case 'R':
      n_readonly++;
      break;
    case 'W':
/*        printf("-N %s\n", optarg); */
      strncpy(newuser_passwd, optarg, PASSWD_LENGTH);
      newuser_passwd[PASSWD_LENGTH] = '\0';  /* terminate in case string got truncated */
      break;
    case 'c': /* fall through, has been taken care of in main() */
    case 'C':
    case 'e':
    case 'i':
    case 'l':
    case 'L':
    case 'o':
    case 'O':
    case 'p':
    case 'q':
    case 'T':
    case 'u':
    case 'v':
    case 'V':
    case 'w':
      break;
    case ':':
      fprintf(stderr, "missing option\n");
      n_cmdlinerror = 1;
      break;
    case '?':
      fprintf(stderr, "unknown option\n");
      n_cmdlinerror = 1;
      break;
    }
  }
	    

  /* get arguments */
/*   for (i = 0; i < inargc; i++) { */
/*     printf("argument %s\n", inargv[i]); */
/*   } */
  
  if (n_cmdlinerror || n_just_help) {
    delete_all_lilimem(&sentinel);
    return (n_just_help) ? 0:1;
  }
  else if (!*database_name && !n_remove) {
    fprintf(stderr, "can't add users without database information\n");
    delete_all_lilimem(&sentinel);
    return 1;
  }
  else if (!*database_name) {
    fprintf(stderr, "can't change user status without database information\n");
    delete_all_lilimem(&sentinel);
    return 1;
  }
  /* don't complain about missing host as some database servers do
     not support changing host-based info this way. refdbd will
     use localhost if no host is specified */

  if (connect_to_server(&n_sockfd, server_ip, port_address) != 0) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  strcpy(scrambled_passwd, passwd);

  if (init_dialog(n_sockfd, scrambled_passwd, srv_inbuffer)) {
    close(n_sockfd);
    delete_all_lilimem(&sentinel);
    return 1;
  }


  /* encrypt newuser passwd if we have one */
  if (newuser_passwd[0]
      && *no_encrypt != 't') {
    if (enigma_encrypt(newuser_passwd, newuser_scrambled_passwd, srv_inbuffer)) {
      fprintf(stderr, "password encryption error\n");
      send_status(n_sockfd, 112, TERM_NO);
      return 1;
    }
    numberize_passwd(newuser_passwd, newuser_scrambled_passwd);
  }

  /* assemble command */
  strcat(outbuffer, " -u ");
  strcat(outbuffer, username);
  if (*passwd) {
    strcat(outbuffer, " -w ");
    strcat(outbuffer, scrambled_passwd);
  }
  strcat(outbuffer, " -d ");
  strcat(outbuffer, database_name);

  if (n_remove) {
    strcat(outbuffer, " -r ");
  }

  if (n_readonly) {
    strcat(outbuffer, " -B ");
  }

  if (*user_host) {
    strcat(outbuffer, " -H ");
    strcat(outbuffer, user_host);
  }

  if (*newuser_passwd) {
    strcat(outbuffer, " -W ");
    strcat(outbuffer, newuser_passwd);
  }

  /* now send the command to the application server */
/*   fprintf(stderr, "%s\n", outbuffer); */
  send_status(n_sockfd, 0, TERM_NO);
  numbyte = tiwrite(n_sockfd, outbuffer, TERM_YES);
  if (numbyte == -1) {
    delete_all_lilimem(&sentinel);
    fprintf(stderr, "%s\n", get_status_msg(110));
    close(n_sockfd);
    return 1;
  }

  if ((n_server_status = read_status(n_sockfd))) {
    delete_all_lilimem(&sentinel);
    fprintf(stderr, "%s\n", get_status_msg(n_server_status));
    close(n_sockfd);
    return 1;
  }

  outbuffer[0] = '\0';

  /* open data source for reading if necessary */
  if (n_read_file || n_read_stdin || optind == inargc) {
    if (n_read_file) {
      infile_fd = open(infile, O_RDONLY|O_BINARY);
    }
    else {
      infile_fd = fileno(stdin);
      n_read_stdin = 1; /* not set if optind == inargc */
    }
    if (infile_fd == -1) {
      send_status(n_sockfd, 112, TERM_NO);
      delete_all_lilimem(&sentinel);
      close(n_sockfd);
      return 1;
    }

    if ((new_outbuffer = read_tokens(infile_fd, outbuffer, &outbuf_len)) == NULL) {
      send_status(n_sockfd, 112, TERM_NO);
      delete_all_lilimem(&sentinel);
      close(n_sockfd);
      if (!n_read_stdin) {
	close(infile_fd);
      }
      return 1;
    }
    else {
      outbuffer = new_outbuffer;
    }
    if (!n_read_stdin) {
      close(infile_fd);
    }
    delete_lilimem(&sentinel, "infile");
  }

/*    printf("%s\n", outbuffer); */

  for (i = optind; i < inargc; i++) {
/*      printf("%s\n", inargv[i]); */
    if ((new_outbuffer = mstrcat(outbuffer, inargv[i], &outbuf_len, 0)) == NULL) {
      send_status(n_sockfd, 112, TERM_NO);
      delete_all_lilimem(&sentinel);
      close(n_sockfd);
      return 1;
    }
    else {
      outbuffer = new_outbuffer;
    }
    if ((new_outbuffer = mstrcat(outbuffer, " ", &outbuf_len, 0)) == NULL) {
      send_status(n_sockfd, 112, TERM_NO);
      delete_all_lilimem(&sentinel);
      close(n_sockfd);
      return 1;
    }
    else {
      outbuffer = new_outbuffer;
    }
  }
/*   printf("%s\n", outbuffer); */
  delete_lilimem(&sentinel, "inargv");
  delete_lilimem(&sentinel, "newarg");

  /* now send the string with the usernames to the application server */
  send_status(n_sockfd, 0, TERM_NO);
  send_buffer = outbuffer;
  while (outbuf_len > COMMAND_INBUF_LEN) {
    numbyte = iwrite(n_sockfd, send_buffer, COMMAND_INBUF_LEN);
    if (numbyte == -1) {
      delete_all_lilimem(&sentinel);
      fprintf(stderr, "could not write to refdbd. Stop\n");
      close(n_sockfd);
      return (1);
    }
    send_buffer += numbyte;
    outbuf_len -= numbyte;
  }

  numbyte = tiwrite(n_sockfd, send_buffer, TERM_YES);
  if (numbyte == -1) {
    delete_all_lilimem(&sentinel);
    fprintf(stderr, "could not write to refdbd. Stop\n");
    close(n_sockfd);
    return (1);
  }

  /* read data */
  if ((n_server_status = read_status(n_sockfd))) {
    delete_all_lilimem(&sentinel);
    fprintf(stderr, "%s\n", get_status_msg(n_server_status));
    close(n_sockfd);
    return 1;
  }
  /* openpager and open_outfile are guaranteed to return a valid file/pipe - 
     and be it stdout */
  pagerfp = openpager(the_pager);

  do {
    numbyte = tread(n_sockfd, inbuffer, OUTBUF_LEN);
    if (numbyte == -1) {
      fprintf(stderr, "could not read from refdbd. Stop\n");
      delete_all_lilimem(&sentinel);
      close(n_sockfd);
      closepager(pagerfp);
      n_broken_pipe = 0;
      return (1);
    }

    n_curr_trailing_z = get_trailz(inbuffer, numbyte);

    if (numbyte >= TERM_LEN) {
      if (n_curr_trailing_z >= TERM_LEN) {
	n_read_done++;
	/* send back confirmation to the server */
	send_status(n_sockfd, 0, TERM_NO);
      }
    }
    else if (n_curr_trailing_z == numbyte
	     && n_curr_trailing_z + n_last_trailing_z >= TERM_LEN) {
      n_read_done++;
      /* send back confirmation to the server */
      send_status(n_sockfd, 0, TERM_NO);
    }
    else if (n_curr_trailing_z == numbyte) {
      /* terminator is still incomplete */
      n_last_trailing_z += n_curr_trailing_z;
      continue;
    }

    /* write numbyte chars to output, unless this is the last chunk: we do not
       want to write the terminating \0 */
    if (!n_broken_pipe) {
      if (n_last_trailing_z) {
	byte_written += fwrite(cs_term, sizeof(char), n_last_trailing_z, pagerfp);
      }
      byte_written += fwrite(inbuffer, sizeof(char), numbyte-n_curr_trailing_z, pagerfp);
    }
    if (!n_read_done) {
      n_last_trailing_z = n_curr_trailing_z;
    }
    /*  printf("%s", inbuffer); */
  } while (!n_read_done);

  /* read summary */
  if ((n_server_status = read_status(n_sockfd))) {
    delete_all_lilimem(&sentinel);
    fprintf(stderr, "%s\n", get_status_msg(n_server_status));
    close(n_sockfd);
    return 1;
  }

  /* send back confirmation to the server */
  send_status(n_sockfd, 0, TERM_NO);

  numbyte = tread(n_sockfd, inbuffer, OUTBUF_LEN);
  if (numbyte == -1) {
    fprintf(stderr, "could not read from refdbd. Stop\n");
    delete_all_lilimem(&sentinel);
    close(n_sockfd);
    n_broken_pipe = 0;
    return 1;
  }

  closepager(pagerfp);

  fprintf(stderr, "%s", inbuffer);

  n_broken_pipe = 0;
  delete_all_lilimem(&sentinel);
  close(n_sockfd);
  return (0);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_listuser(): list all users that have some entries in their
                  personal interest lists

  int com_listuser 0 if successful, 1 if error 

  char *arg specifies the regexp to look for

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_listuser (char* arg)
{
  /* this command won't work without specifying a database to query */
  if (!strstr(arg, "-d ")) {
    fprintf(stderr, "The listuser command requires a '-d database' argument.\n");
    return 1;
  }

  return listvalue(arg, "listuser", "Lists the names of the refdb users of a given database.\nSyntax: listuser [-h] -d database <unix-regexp>\nOptions: -h           prints this mini-help\n         -d           database\n         All other arguments are interpreted as a Unix regular expression.\n", "could not connect", 1);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_scankw(): run a manual keyword scan

  int com_scankw 0 if successful, 1 if error 

  char *arg specifies a command argument 

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_scankw (char* arg) {
  return listvalue(arg, "scankw", "Runs a manual keyword scan.\nSyntax: scankw [-h] -d database\nOptions: -h           prints this mini-help\n         -d           database\n", "could not connect", 1);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_addword(): add reserved journal word

  int com_addword 0 if successful, 1 if error 

  char *arg specifies the arguments

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_addword (char* arg) {
  return addword(0, arg);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_deleteword(): remove reserved journal word

  int com_deleteword 0 if successful, 1 if error 

  char *arg specifies the arguments

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_deleteword (char* arg) {
  return addword(1, arg);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  addword(): add or remove reserved journal word

  static int addword 0 if successful, 1 if error 

  int n_remove if 1, words will be removed, if 0, will be added

  char *arg specifies the arguments

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int addword (int n_remove, char* arg) {
  int n_sockfd; /* file descriptor of the socket */
  char *outbuffer;
  char *new_outbuffer;
  char inbuffer[COMMAND_INBUF_LEN] = "";
  char srv_inbuffer[COMMAND_INBUF_LEN] = "";
  char* infile;
  char **inargv; /* tokens of the argument */
  char *newarg;
  char scrambled_passwd[PASSWD_LENGTH*3+1] = "";
  size_t outbuf_len = COMMAND_INBUF_LEN;
  int numbyte;
  int n_read_done = 0;
  int inargc = 0; /* number of tokens of the argument */
  int inargcmax; /* maximum number of tokens */
  int result;
  int i;
  int infile_fd;
  int n_opt;
  int n_read_file = 0;
  int n_cmdlinerror = 0;
  int n_just_help = 0;
  int n_curr_trailing_z = 0;
  int n_last_trailing_z = 0;
  int n_server_status;
  size_t byte_written = 0;
  FILE *pagerfp;
  struct lilimem sentinel;

  sentinel.ptr_mem = NULL;
  sentinel.ptr_next = NULL;
  sentinel.varname[0] = '\0';

  outbuffer = malloc(outbuf_len);
  if (outbuffer == NULL) {
    delete_all_lilimem(&sentinel);
    return 1;
  }
  
  if (insert_lilimem(&sentinel, (void**)&outbuffer, "outbuffer")) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  strcpy(outbuffer, "addword ");

/*   if (n_batchmode) { */ /* in batchmode, the command line is already tokenized */
/*     inargc = main_argc; */
/*     inargv = main_argv; */
/*   } */
/*   else { */
    /* parse the argument. first we cut the argument
       into pieces with strtok, then we use getopt to interpret */

    /* get a buffer to hold the tokens. Start with 10 tokens,
       increase in steps of 10 as needed */
    inargc = 0;
    inargcmax = 10;
    inargv = malloc((size_t)inargcmax*sizeof(char*));
    if (inargv == NULL) {
      return 1;
    }

    if (insert_lilimem(&sentinel, (void**)&inargv, "inargv")) {
      return 1;
    }

    /* the following is a temporary hack to allow cmdln_tokenize to work */
    newarg = malloc((size_t)(strlen(arg)+9));
    if (newarg == NULL) {
      delete_all_lilimem(&sentinel);
      return 1;
    }

    if (insert_lilimem(&sentinel, (void**)&newarg, "newarg")) {
      delete_all_lilimem(&sentinel);
      return 1;
    }

    strcpy(newarg, "addword ");
    strcat(newarg, arg);

    result = cmdln_tokenize(&inargc, &inargv, inargcmax, newarg);


    if (result == 1 || result == 2) { /* memory error */
      delete_all_lilimem(&sentinel);
      return 1;
    }
/*   } */ /* if/else */

  /* now we have the tokens nicely arranged in inargc */
/*    for (i = 0; i < inargc; i++) { */
/*      printf("inargv[%d]: %s\n", i, inargv[i]); */
/*    } */

  /* get options */
  optind = 0;

  while ((n_opt = getopt(inargc, inargv, "c:C:e:f:hi:l:L:o:O:p:qT:u:vVw:")) != -1) {
    switch(n_opt) {
    case 'f':
      if (!strcmp(optarg, "stdin")) {
	n_read_stdin = 1;
      }
      else {
	infile = canonicalize_path(optarg);
	if (insert_lilimem(&sentinel, (void**)&infile, "infile")) {
	  delete_all_lilimem(&sentinel);
	  return 1;
	}
	n_read_file = 1;
      }
      break;
    case 'h':
      if (n_remove) {
	printf("Removes reserved journal words\nSyntax: deleteword [-h] {word|-f infile}\nOptions: -f infile    Read the words from infile\n         -h           prints this mini-help\n         All other arguments are interpreted as words.\n");
      }
      else {
	printf("Adds reserved journal words\nSyntax: addword [-h] {word|-f infile}\nOptions: -f infile    Read the words from infile\n         -h           prints this mini-help\n         All other arguments are interpreted as words.\n");
      }
      n_just_help++;
      break;
    case 'c': /* fall through, has been taken care of in main() */
    case 'C':
    case 'e':
    case 'i':
    case 'l':
    case 'L':
    case 'o':
    case 'O':
    case 'p':
    case 'q':
    case 'T':
    case 'u':
    case 'v':
    case 'V':
    case 'w':
      break;
    case ':':
      fprintf(stderr, "missing option\n");
      n_cmdlinerror = 1;
      break;
    case '?':
      fprintf(stderr, "unknown option\n");
      n_cmdlinerror = 1;
      break;
    }
  }
	    

  /* get arguments */
/*    for (i = optind; i < inargc; i++) { */
/*      printf("argument %s\n", inargv[i]); */
/*    } */
  
  if (n_cmdlinerror || n_just_help) {
    delete_all_lilimem(&sentinel);
    return (n_just_help) ? 0:1;
  }

  if (connect_to_server(&n_sockfd, server_ip, port_address) != 0) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  strcpy(scrambled_passwd, passwd);

  if (init_dialog(n_sockfd, scrambled_passwd, srv_inbuffer)) {
    close(n_sockfd);
    delete_all_lilimem(&sentinel);
    return 1;
  }


  /* assemble command */
  strcat(outbuffer, " -u ");
  strcat(outbuffer, username);
  if (*passwd) {
    strcat(outbuffer, " -w ");
    strcat(outbuffer, scrambled_passwd);
  }
  if (n_remove) {
    strcat(outbuffer, " -r ");
  }

  /* now send the command to the application server */
  send_status(n_sockfd, 0, TERM_NO);
  numbyte = tiwrite(n_sockfd, outbuffer, TERM_YES);
  if (numbyte == -1) {
    delete_all_lilimem(&sentinel);
    fprintf(stderr, "could not write to refdbd\n");
    close(n_sockfd);
    return 1;
  }

  if ((n_server_status = read_status(n_sockfd))) {
    delete_all_lilimem(&sentinel);
    fprintf(stderr, "%s\n", get_status_msg(n_server_status));
    close(n_sockfd);
    return 1;
  }

  outbuffer[0] = '\0';

  /* open data source for reading if necessary */
  if (n_read_file || n_read_stdin || optind == inargc) {
    if (n_read_file) {
      infile_fd = open(infile, O_RDONLY|O_BINARY);
    }
    else {
      infile_fd = fileno(stdin);
      n_read_stdin = 1; /* not set if optind == inargc */
    }
    if (infile_fd == -1) {
      send_status(n_sockfd, 112, TERM_NO);
      delete_all_lilimem(&sentinel);
      close(n_sockfd);
      return 1;
    }

    if ((new_outbuffer = read_tokens(infile_fd, outbuffer, &outbuf_len)) == NULL) {
      send_status(n_sockfd, 112, TERM_NO);
      delete_all_lilimem(&sentinel);
      close(n_sockfd);
      if (!n_read_stdin) {
	close(infile_fd);
      }
      return 1;
    }
    else {
      outbuffer = new_outbuffer;
    }
    if (!n_read_stdin) {
      close(infile_fd);
    }
    delete_lilimem(&sentinel, "infile");
  }

/*    printf("%s\n", outbuffer); */

  for (i = optind; i < inargc; i++) {
/*      printf("%s\n", inargv[i]); */
    if ((new_outbuffer = mstrcat(outbuffer, inargv[i], &outbuf_len, 0)) == NULL) {
      send_status(n_sockfd, 112, TERM_NO);
      delete_all_lilimem(&sentinel);
      close(n_sockfd);
      return 1;
    }
    else {
      outbuffer = new_outbuffer;
    }
    if ((new_outbuffer = mstrcat(outbuffer, " ", &outbuf_len, 0)) == NULL) {
      send_status(n_sockfd, 112, TERM_NO);
      delete_all_lilimem(&sentinel);
      close(n_sockfd);
      return 1;
    }
    else {
      outbuffer = new_outbuffer;
    }
  }
/*    printf("%s\n", outbuffer); */
  delete_lilimem(&sentinel, "inargv");
  delete_lilimem(&sentinel, "newarg");

  /* now send the string with the words to the application server */
  send_status(n_sockfd, 0, TERM_NO);
  numbyte = tiwrite(n_sockfd, outbuffer, TERM_YES);
  if (numbyte == -1) {
    delete_all_lilimem(&sentinel);
    fprintf(stderr, "%s", get_status_msg(110));
    close(n_sockfd);
    return 1;
  }

  if ((n_server_status = read_status(n_sockfd))) {
    delete_all_lilimem(&sentinel);
    fprintf(stderr, "%s\n", get_status_msg(n_server_status));
    close(n_sockfd);
    return 1;
  }

  /* openpager and open_outfile are guaranteed to return a valid file/pipe - 
     and be it stdout */
  pagerfp = openpager(the_pager);

  do {
    numbyte = tread(n_sockfd, inbuffer, OUTBUF_LEN);
    if (numbyte == -1) {
      fprintf(stderr, "could not read from refdbd. Stop\n");
      delete_all_lilimem(&sentinel);
      close(n_sockfd);
      closepager(pagerfp);
      n_broken_pipe = 0;
      return (1);
    }

    n_curr_trailing_z = get_trailz(inbuffer, numbyte);

    if (numbyte >= TERM_LEN) {
      if (n_curr_trailing_z >= TERM_LEN) {
	n_read_done++;
	/* send back confirmation to the server */
	send_status(n_sockfd, 0, TERM_NO);
      }
    }
    else if (n_curr_trailing_z == numbyte
	     && n_curr_trailing_z + n_last_trailing_z >= TERM_LEN) {
      n_read_done++;
      /* send back confirmation to the server */
      send_status(n_sockfd, 0, TERM_NO);
    }
    else if (n_curr_trailing_z == numbyte) {
      /* terminator is still incomplete */
      n_last_trailing_z += n_curr_trailing_z;
      continue;
    }

    /* write numbyte chars to output, unless this is the last chunk: we do not
       want to write the terminating \0 */
    if (!n_broken_pipe) {
      if (n_last_trailing_z) {
	byte_written += fwrite(cs_term, sizeof(char), n_last_trailing_z, pagerfp);
      }
      byte_written += fwrite(inbuffer, sizeof(char), numbyte-n_curr_trailing_z, pagerfp);
    }
    /*  printf("%s", inbuffer); */
  } while (!n_read_done);

  n_broken_pipe = 0;
  delete_all_lilimem(&sentinel);

  if ((n_server_status = read_status(n_sockfd))) {
    delete_all_lilimem(&sentinel);
    fprintf(stderr, "%s\n", get_status_msg(n_server_status));
    close(n_sockfd);
    return 1;
  }

  numbyte = tread(n_sockfd, inbuffer, OUTBUF_LEN);
  if (numbyte == -1) {
    fprintf(stderr, "%s", get_status_msg(109));
    delete_all_lilimem(&sentinel);
    close(n_sockfd);
    n_broken_pipe = 0;
    return 1;
  }

  /* send back confirmation to the server */
  send_status(n_sockfd, 0, TERM_NO);

  closepager(pagerfp);

  fprintf(stderr, "%s", inbuffer);

  close(n_sockfd);
  return (0);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_listword(): list reserved journal words

  int com_listword 0 if successful, 1 if error 

  char *arg specifies the regexp to look for

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_listword (char* arg)
{
  return listvalue(arg, "listword", "Lists reserved journal words (i.e. words which are no abbreviations). If an argument is given as a Unix regular expression, only the matching words are shown.\nSyntax: listword [-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_addstyle(): add or remove bibliography style

  int com_addstyle 0 if successful, 1 if error 

  char *arg specifies the styles to add

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_addstyle (char* arg)
{
  int n_sockfd; /* file descriptor of the socket */
  char *outbuffer;
  char *outpipe;
  char **inargv; /* tokens of the argument */
  char *newarg;
  char inbuffer[COMMAND_INBUF_LEN];
  char srv_inbuffer[COMMAND_INBUF_LEN] = "";
  char outfile[_POSIX_PATH_MAX];
  char scrambled_passwd[PASSWD_LENGTH*3+1];
  size_t outbuf_len = COMMAND_INBUF_LEN;
  int numbyte;
  int inargc = 0; /* number of tokens of the argument */
  int inargcmax; /* maximum number of tokens */
  int result;
  int k;
  int n_opt;
  int n_file_open = 0;
  int n_file_append = 0;
  int n_pipe = 0; /* indicates -c switch */
  int n_cmdlinerror = 0;
  int n_just_help = 0;
  int n_style_file_done = 0;
  int n_xml_result;
  int n_have_dataset = 0;
  int cs_status;
  int n_set_count = 0;
  int numcycles; /* number of cycles reading input */
  size_t byte_written = 0;
  FILE *pagerfp;
  FILE *infp = NULL;
  struct lilimem sentinel;

  sentinel.ptr_mem = NULL;
  sentinel.ptr_next = NULL;
  sentinel.varname[0] = '\0';


  outbuffer = malloc(outbuf_len);
  if (outbuffer == NULL) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (insert_lilimem(&sentinel, (void**)&outbuffer, NULL)) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  strcpy(outbuffer, "addstyle ");
    

  /* parse the argument. first we cut the argument
     into pieces with strtok, then we use getopt to interpret */

  /* get a buffer to hold the tokens. Start with 10 tokens,
     increase in steps of 10 as needed */
  inargc = 0;
  inargcmax = 10;
  inargv = malloc((size_t)inargcmax*sizeof(char*));
  if (inargv == NULL) {
    return 1;
  }
  
  if (insert_lilimem(&sentinel, (void**)&inargv, NULL)) {
    return 1;
  }
  
  /* the following is a temporary hack to allow cmdln_tokenize to work */
  newarg = malloc((size_t)(strlen(arg)+11));
  if (newarg == NULL) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (insert_lilimem(&sentinel, (void**)&newarg, NULL)) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  strcpy(newarg, "addstyle ");
  strcat(newarg, arg);
  
  result = cmdln_tokenize(&inargc, &inargv, inargcmax, newarg);


  if (result == 1 || result == 2) { /* memory error */
    delete_all_lilimem(&sentinel);
    return 1;
  }

  /* now we have the tokens nicely arranged in inargc */
/*    for (i = 0; i < inargc; i++) { */
/*      printf("inargv[%d]: %s\n", i, inargv[i]); */
/*    } */

  /* get options */
  optind = 0;

  while ((n_opt = getopt(inargc, inargv, "c:C:d:e:f:hH:i:l:L:o:O:p:qrT:u:vVw:")) != -1) {
    switch(n_opt) {
    case 'c':
      /*        printf("-c %s\n", optarg); */
      outpipe = malloc(strlen(optarg)+1);
      if (outpipe == NULL) {
	delete_all_lilimem(&sentinel);
	return 0;
      }
      strcpy(outpipe, optarg);
      if (insert_lilimem(&sentinel, (void**)&outpipe, NULL)) {
	delete_all_lilimem(&sentinel);
	return 1;
      }
      n_pipe = 1;
      break;
    case 'f':
      /*        printf("-f %s\n", optarg); */
      if (!strcmp(optarg, "stdin")) {
	n_read_stdin = 1;
      }
      break;
    case 'h':
	printf("Adds bibliography styles\nSyntax: addstyle [-h] {file} [file1...]\nOptions: -h           prints this mini-help\n         All other arguments are interpreted as filenames with style specifications.\n");
      n_just_help++;
      break;
    case 'o':
      /*        printf("-o %s\n", optarg); */
      strncpy(outfile, optarg, _POSIX_PATH_MAX - 1);
      outfile[_POSIX_PATH_MAX-1] = '\0';  /* terminate in case string got truncated */
      n_file_open = 1;
      break;
    case 'O':
      /*        printf("-O %s\n", optarg); */
      strncpy(outfile, optarg, _POSIX_PATH_MAX - 1);
      outfile[_POSIX_PATH_MAX-1] = '\0';  /* terminate in case string got truncated */
      n_file_append = 1;
      break;
    case 'C':
    case 'd':
    case 'e':
    case 'H':
    case 'i':
    case 'l':
    case 'L':
    case 'p':
    case 'q':
    case 'r':
    case 'T':
    case 'u':
    case 'v':
    case 'V':
    case 'w':
      break;
    case ':':
      fprintf(stderr, "missing option\n");
      n_cmdlinerror = 1;
      break;
    case '?':
      fprintf(stderr, "unknown option\n");
      n_cmdlinerror = 1;
      break;
    }
  }
	    

  /* get arguments */
/*    for (i = optind; i < inargc; i++) { */
/*      printf("argument %s\n", inargv[i]); */
/*    } */
  
  if (n_cmdlinerror || n_just_help) {
    delete_all_lilimem(&sentinel);
    return (n_just_help) ? 0:1;
  }
  if (connect_to_server(&n_sockfd, server_ip, port_address) != 0) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  strcpy(scrambled_passwd, passwd);

  if (init_dialog(n_sockfd, scrambled_passwd, srv_inbuffer)) {
    close(n_sockfd);
    delete_all_lilimem(&sentinel);
    return 1;
  }

  /* assemble command */
  strcat(outbuffer, " -u ");
  strcat(outbuffer, username);
  if (strlen(passwd) > 0) {
    strcat(outbuffer, " -w ");
    strcat(outbuffer, scrambled_passwd);
  }

  /* now send the command to the application server */
/*    printf("%s\n", outbuffer); */
  send_status(n_sockfd, 0, TERM_NO);

  numbyte = tiwrite(n_sockfd, outbuffer, TERM_YES);
  if (numbyte == -1) {
    delete_all_lilimem(&sentinel);
    fprintf(stderr, "could not write to refdbd. Stop\n");
    close(n_sockfd);
    return 1;
  }

  /* check server status */
  if ((cs_status = read_status(n_sockfd))) {
    fprintf(stderr, "%s\n", get_status_msg(cs_status));
    return 1;
  }
  
  /* openpager is guaranteed to return a valid file/pipe - 
     and be it stdout */
  if (n_file_open) {
    pagerfp = open_outfile(outfile, 0);
  }
  else if (n_file_append) {
    pagerfp = open_outfile(outfile, 1);
  }
  else if (n_pipe) {
    pagerfp = openpager(outpipe);
  }
  else {
    pagerfp = openpager(the_pager);
  }

  if (n_read_stdin || optind == inargc) {
    infp = stdin;
    numcycles = 1;
    n_read_stdin = 1; /* not set if optind == inargc */
  }
  else {
    numcycles = inargc-optind;
  }
  /* reuse result */
  result = 0;

  for (k = 0; k < numcycles; k++) {
    n_set_count++; /* starts at 1 */
    n_style_file_done = 0;
    /* try to open our target file */
    if (!n_read_stdin) {
      /* try to open our target file */
      infp = fopen(inargv[optind+k], "rb");
/*            printf("%s<<\n", inargv[optind+k]);  */
      if (infp == NULL) {
	continue;
      }
    }

    /* runs phases 1 through 5 of transfer protocol */
    n_xml_result = send_xml_data(infp, pagerfp, stderr, n_sockfd, &byte_written);

    if (!n_read_stdin) {
      fclose(infp);
    }

    if (n_xml_result != 0
	&& n_xml_result != 4) {
      if (n_file_open || n_file_append) {
	close_outfile(pagerfp);
      }
      else {
	closepager(pagerfp);
      }
      delete_all_lilimem(&sentinel);
      close(n_sockfd);
      n_broken_pipe = 0;

      result = 1;
      goto cleanup;
    }

    /* ------------------------------------------------------------ */
    /* PHASE 5 */
    /*  signal server that this file is done */
    if (k == numcycles-1) {
      /* this file is done, and it was the last one */
      send_status(n_sockfd, 402, TERM_NO);
    }
    else {
      /* this file is done, but there's more to come */
      send_status(n_sockfd, 404, TERM_NO);
    }

    n_have_dataset++;

    /* check server status, should be "chunk added successfully" */
    if ((cs_status = read_status(n_sockfd)) != 403) {
      if (cs_status == 400) {
	size_t byte_written_to_inbuffer;
	int num_trailz;
	int n_read_done = 0;
	/* retrieve server-generated error message */
	do {
	  numbyte = tread(n_sockfd, inbuffer, OUTBUF_LEN);
	  /*   	printf("phase4 server reply:%s<<\n", inbuffer); */
	  if (numbyte == -1) {
	    /* timeout while reading */
	    fprintf(stderr, "%s", get_status_msg(109));
	    result = 1;
	    goto cleanup;
	  }

	  /* we rely on the fact that the server reply is no more than
	     OUTBUF_LEN in length */
	  if ((num_trailz = get_trailz(inbuffer, numbyte)) >= TERM_LEN) { /* if transmission ends */
	    n_read_done++;
	  }
	  /* write numbyte chars to output, unless this is the last chunk:
	     we do not want to write the terminating \0 */
	  if (!n_broken_pipe) {
	    byte_written_to_inbuffer = fwrite(inbuffer, sizeof(char), numbyte-num_trailz, pagerfp);
	  }
	  /* 	printf("%s", inbuffer); */
	} while (!n_read_done);

	result = 1;
/* 	goto cleanup; */
      }
      else {
	fprintf(stderr, "%s\n", get_status_msg(cs_status));
	result = 1;
	goto cleanup;
      }
    } /* end if cs_status */

    /* reuse inbuffer */
/*     sprintf(inbuffer, "405:%d\n", n_set_count); */
/*     fwrite(inbuffer, sizeof(char), strlen(inbuffer), pagerfp); */
  } /* end for */

  /* we must run this code if none of the files could be opened */
  if (!n_have_dataset) {
    send_status(n_sockfd, 401, TERM_NO);
    result = 1;
    fprintf(stderr, "no input data\n");
    goto cleanup;
  }

  /* ------------------------------------------------------------ */
  /* PHASE 6 */
/*   fprintf(stderr, "try to read return message\n"); */
  numbyte = tread(n_sockfd, inbuffer, COMMAND_INBUF_LEN);
/*    printf("phase6 server reply:%s<<\n", inbuffer); */
    
  if (numbyte == -1) {
    fprintf(stderr, "could not read from refdbd\n");
    if (n_file_open || n_file_append) {
      close_outfile(pagerfp);
    }
    else {
      closepager(pagerfp);
    }
    result = 1;
    goto cleanup;
  }
    
  if (!*inbuffer) {
    fprintf(stderr, "server error. Stop.\n");
    if (n_file_open || n_file_append) {
      close_outfile(pagerfp);
    }
    else {
      closepager(pagerfp);
    }
    result = 1;
    goto cleanup;
  }
  else {
    size_t byte_written_to_inbuffer;

    byte_written_to_inbuffer = fwrite(inbuffer, sizeof(char), strlen(inbuffer), pagerfp);
      /* leave out the following message for the time being. With the current implementation of the XML handling code any whitespace after a <citation> element would count as another dataset */
/*        sprintf(inbuffer, "%d dataset(s) sent.\n", n_chunk_count); */
/*        fwrite(inbuffer, sizeof(char), strlen(inbuffer), pagerfp); */
  }

  /* retrieve summary */
  send_status(n_sockfd, 0, TERM_NO);

  if ((cs_status = read_status(n_sockfd)) == 0) {
    numbyte = tread(n_sockfd, inbuffer, COMMAND_INBUF_LEN);
  }

  /* send back confirmation to the server */
  send_status(n_sockfd, 0, TERM_NO);

 cleanup:
  /* reset */
  n_broken_pipe = 0;

  if (n_file_open || n_file_append) {
    close_outfile(pagerfp);
    fprintf(stderr, "%d byte written to %s\n", byte_written, outfile);
  }
  else {
    closepager(pagerfp);
  }

  fprintf(stderr, "%s\n", inbuffer);

  delete_all_lilimem(&sentinel);
  close(n_sockfd);
  return (0);
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_deletestyle(): delete a bibliography style

  int com_deletestyle 0 if successful, 1 if error 

  char *arg the name of the bibliography style which is to be deleted

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_deletestyle (char* arg)
{
  char outbuffer[COMMAND_INBUF_LEN]; /* holds the command for the server */
  char inbuffer[COMMAND_INBUF_LEN] = "";
  struct simplelistvals slvals;
  char scrambled_passwd[PASSWD_LENGTH*3+1];

  slvals.outbuffer = outbuffer;
  strcpy(slvals.outbuffer, "deletestyle ");
  slvals.n_file_open = 0;
  slvals.n_file_append = 0;
  slvals.n_pipe = 0;
  slvals.outfile = NULL;
  slvals.outpipe = NULL;

  if (strncmp(arg, "-h", 2) == 0) {
    printf("Deletes bibliography styles\nSyntax: deletestyle [-h] {unix-regexp}\nOptions: -h           prints this mini-help\nThis command will delete all bibliography styles whose names match the regular expression\n");
    return 0;
  }

  if (connect_to_server(&slvals.n_sockfd, server_ip, port_address) != 0) {
    return 1;
  }

  strcpy(scrambled_passwd, passwd);

  if (init_dialog(slvals.n_sockfd, scrambled_passwd, inbuffer)) {
    close(slvals.n_sockfd);
    return 1;
  }

  strcat(slvals.outbuffer, arg);
  strcat(slvals.outbuffer, " -u ");
  strcat(slvals.outbuffer, username);
  if (strlen(passwd) > 0) {
    strcat(slvals.outbuffer, " -w ");
    strcat(slvals.outbuffer, scrambled_passwd);
  }

  getsimplelist(&slvals, 1);

  close(slvals.n_sockfd);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  com_getstyle(): retrieves a bibliography style

  int com_getstyle 0 if successful, 1 if error 

  char *arg the name of the bibliography style which is to be deleted

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int com_getstyle (char* arg)
{
  int inargc = 0;
  int inargcmax = 0;
  int result;
  int n_opt;
  int i;
  int n_just_help = 0;
  int n_cmdlinerror = 0;
  char outbuffer[COMMAND_INBUF_LEN]; /* holds the command for the server */
  char inbuffer[COMMAND_INBUF_LEN] = "";
  char scrambled_passwd[PASSWD_LENGTH*3+1];
  char *newarg;
  char **inargv; /* tokens of the argument */
  struct simplelistvals slvals;
  struct lilimem sentinel;

  slvals.outbuffer = outbuffer;
  strcpy(slvals.outbuffer, "getstyle ");
  slvals.n_file_open = 0;
  slvals.n_file_append = 0;
  slvals.n_pipe = 0;
  slvals.outfile = NULL;
  slvals.outpipe = NULL;

  sentinel.ptr_mem = NULL;
  sentinel.ptr_next = NULL;
  sentinel.varname[0] = '\0';

  /* parse the argument. first we cut the argument
     into pieces with strtok, then we use getopt to interpret */
  
  /* get a buffer to hold the tokens. Start with 10 tokens,
     increase in steps of 10 as needed */
  inargc = 0;
  inargcmax = 10;
  inargv = malloc((size_t)inargcmax*sizeof(char*));
  if (inargv == NULL) {
    return 1;
  }

  if (insert_lilimem(&sentinel, (void**)&inargv, NULL)) {
    return 1;
  }

  /* the following is a temporary hack to allow cmdln_tokenize to work */
  newarg = malloc((size_t)(strlen(arg)+11));
  if (newarg == NULL) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (insert_lilimem(&sentinel, (void**)&newarg, NULL)) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  strcpy(newarg, "getstyle ");
  strcat(newarg, arg);

  result = cmdln_tokenize(&inargc, &inargv, inargcmax, newarg);


  if (result == 1 || result == 2) { /* memory error */
    delete_all_lilimem(&sentinel);
    return 1;
  }

  /* now we have the tokens nicely arranged in inargc */
/*    for (i = 0; i < inargc; i++) { */
/*      printf("inargv[%d]: %s\n", i, inargv[i]); */
/*    } */

  /* get options */
  optind = 0;

  while ((n_opt = getopt(inargc, inargv, "c:C:d:e:f:hH:i:l:L:o:O:p:qrT:u:vVw:")) != -1) {
    switch(n_opt) {
    case 'c':
      /*        printf("-c %s\n", optarg); */
      slvals.outpipe = malloc(strlen(optarg)+1);
      if (slvals.outpipe == NULL) {
	delete_all_lilimem(&sentinel);
	return 0;
      }
      strcpy(slvals.outpipe, optarg);
      if (insert_lilimem(&sentinel, (void**)&slvals.outpipe, NULL)) {
	delete_all_lilimem(&sentinel);
	return 1;
      }
      slvals.n_pipe = 1;
      break;
    case 'h':
    printf("Retrieves a bibliography style\nSyntax: getstyle [-c command] [-h] [-o file] [-O file] name [name1...]\nOptions: -c command   pipe the output through command\n         -h           prints this mini-help\n         -o outfile   save the output in outfile (overwrite)\n         -O outfile   append the output to outfile\nThis command will retrieve the bibliography style(s) with the given name(s)\n");
      n_just_help++;
      break;
    case 'o':
      /*        printf("-o %s\n", optarg); */
      slvals.outfile = canonicalize_path(optarg);
      if (insert_lilimem(&sentinel, (void**)&(slvals.outfile), NULL)) {
	delete_all_lilimem(&sentinel);
	return 1;
      }
      slvals.n_file_open = 1;
      break;
    case 'O':
      /*        printf("-O %s\n", optarg); */
      slvals.outfile = canonicalize_path(optarg);
      if (insert_lilimem(&sentinel, (void**)&(slvals.outfile), NULL)) {
	delete_all_lilimem(&sentinel);
	return 1;
      }
      slvals.n_file_append = 1;
      break;
    case 'C':
    case 'd':
    case 'e':
    case 'f':
    case 'H':
    case 'i':
    case 'l':
    case 'L':
    case 'p':
    case 'q':
    case 'r':
    case 'T':
    case 'u':
    case 'v':
    case 'V':
    case 'w':
      break;
    case ':':
      fprintf(stderr, "missing option\n");
      n_cmdlinerror = 1;
      break;
    case '?':
      fprintf(stderr, "unknown option\n");
      n_cmdlinerror = 1;
      break;
    }
  }
	    

  /* get arguments */
/*    for (i = optind; i < inargc; i++) { */
/*      printf("argument %s\n", inargv[i]); */
/*    } */
  
  if (n_cmdlinerror || n_just_help) {
    delete_all_lilimem(&sentinel);
    return (n_just_help) ? 0:1;
  }

  if (connect_to_server(&slvals.n_sockfd, server_ip, port_address) != 0) {
    delete_all_lilimem(&sentinel);
    return 1;
  }

  strcpy(scrambled_passwd, passwd);

  if (init_dialog(slvals.n_sockfd, scrambled_passwd, inbuffer)) {
    close(slvals.n_sockfd);
    delete_all_lilimem(&sentinel);
    return 1;
  }

  if (optind < inargc) {
    strcat(slvals.outbuffer, "\"");
    /*      printf("%d\n", optind); */
    for (i = optind; i < inargc; i++) {
      strcat(slvals.outbuffer, inargv[i]);
      strcat(slvals.outbuffer, "\" \"");
    }
    strcpy(&slvals.outbuffer[strlen(slvals.outbuffer)-1], ""); /* remove trailing quotation mark */
  }

  strcat(slvals.outbuffer, " -u ");
  strcat(slvals.outbuffer, username);
  if (strlen(passwd) > 0) {
    strcat(slvals.outbuffer, " -w ");
    strcat(slvals.outbuffer, scrambled_passwd);
  }

  getsimplelist(&slvals, 1);

  close(slvals.n_sockfd);
  delete_all_lilimem(&sentinel);

  return 0;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  postprocess_var(): checks and converts as necessary config variables

  int postprocess_var returns 0 if ok, 1 if error

  const char* varname name of the variable to check

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int postprocess_var(const char* varname) {
  int result;

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

  /* individual treatment for each variable */
  if (!strcmp(varname, "logfile")) {
    /* nothing to do */
    return 0;
  }
  else if (!strcmp(varname, "logdest")) {
    n_log_dest = num_logdest(log_dest);
    return 0;
  }
  else if (!strcmp(varname, "loglevel")) {
    n_log_level = num_loglevel(log_level);
    return 0;
  }
  else if (!strcmp(varname, "pager")) {
    /* nothing to do */
    return 0;
  }
  else if (!strcmp(varname, "passwd")) {
    if (strcmp(passwd, "*") == 0) {
      ask_for_passwd(passwd);
    }
    return 0;
  }
  else if (!strcmp(varname, "port")) {
    if (!is_port(port_address)) {
      fprintf(stderr, "%s is no valid port\n", port_address);
      if (n_verbose) {
	fprintf(stderr, "Port addresses below 1024 are reserved for system use. refdb should use a port higher than 1024. The server and all clients must use the same port.\n");
      }
      return 1;
    }
    return 0;
  }
  else if (!strcmp(varname, "serverip")) {
    result = check_ip(server_ip); /* reuse i */
    if (result > 0) {
      if (result==1) {
	fprintf(stderr, "\'%s\' cannot be resolved as a hostname\n", server_ip);
      }
      else if (result==2) {
	fprintf(stderr, "\'%s\' does not appear to be an IP host\n", server_ip);
      }
      else if (result==3) {
	fprintf(stderr, "\'%s\' does not appear to have a valid IP address\n", server_ip);
      }
      else if (result==4) {
	fprintf(stderr, "\'%s' has more than one network interface. Please specify one IP address\n", server_ip);
      }
      return 1;
    }
    else if (result==-1) {
      /* silently change 'localhost' to '127.0.0.1' */
      strcpy(server_ip, "127.0.0.1");
    }
    return 0;
  }
  else if (!strcmp(varname, "timeout")) {
    n_refdb_timeout = atoi(refdb_timeout);
    return 0;
  }
  else if (!strcmp(varname, "username")) {
    if (!username[0] && getlogin()) {
      strcpy(username, getlogin()); /* although not recommended, the login name
				     is a good guess */
      return 0;
    }
    else if (!username[0]) {
      return 1;
    }
    return 0;
  }
  else if (!strcmp(varname, "verbose")) {
    n_verbose = (verbose[0] == 't') ? 1:0;
    return 0;
  }
  return 1; /* should never happen */
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  log_print(): writes a log message

  void log_print

  int priority the priority level of the log message as in syslog.h

  char* string a string containing the message to be logged

  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void log_print(int priority, const char* string) {
  /* we must have this fn in the file with main() because FILE* 
     cannot be declared extern (??) */
  time_t the_time;
  char timestring[256] = "";

  if (n_log_dest == 0) { /* output on stderr */
    fprintf(stderr, "%s\n", string);
  }
  else if (n_log_dest == 1) { /* output via syslog */
    syslog(priority, "%s", string);
  }
  else { /* output in user-defined logfile */
    time(&the_time);
    strftime(timestring, 256, "%a %b %d %H:%M:%S %Y", gmtime(&the_time));
    fprintf(fp_log_file, "%d:pid=%d:%s:%s\n", priority, getpid(), timestring, string);
  }
}
